Something went wrong. Try again.
Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations. pdsmoover.com
pds atproto migrations moo cow
Something went wrong. Try again.
2.7 kB · 71 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172import * as Sentry from '@sentry/sveltekit'import { XrpcResponseError } from '@atproto/lex'import { BlobRateLimitError, isAuthFactorError } from '@pds-moover/moover'
/** * XRPC error codes that describe something the user did or has to do, not something broken: a * wrong password, a 2FA code to enter, a mistyped invite code or PLC token, a handle somebody * else holds. The page shows these as its own message and the user fixes them; Sentry would only * see the same handful of codes over and over. */const USER_FACING_XRPC_ERRORS = new Set([ 'AuthFactorTokenRequired', 'AuthenticationRequired', 'InvalidToken', 'ExpiredToken', 'InvalidInviteCode', 'HandleNotAvailable', 'InvalidHandle', 'InvalidPassword', 'InvalidEmail', 'UnsupportedDomain', 'RateLimitExceeded',])
/** * True for an error the UI already expects and handles on its own, so it should not be sent to * Sentry: a 2FA prompt, a user choosing to stop at a rate limit, a request the page cancelled, or * one of the user-facing XRPC codes above. */export function isExpectedError(error: unknown): boolean { if (isAuthFactorError(error)) return true if (error instanceof BlobRateLimitError) return true if (error instanceof XrpcResponseError) { if (error.status === 429) return true if (USER_FACING_XRPC_ERRORS.has(error.error)) return true } // A fetch the page itself cancelled (superseded typeahead, navigation away). if (error instanceof Error && error.name === 'AbortError') return true return false}
export interface ReportOptions { /** 'warning' for best-effort work the page recovers from on its own; 'error' otherwise. */ level?: 'error' | 'warning' /** Extra, non-identifying context for the event. Never handles, DIDs or emails. */ extra?: Record<string, unknown>}
/** * Sends a caught error to Sentry, unless it is one the UI expects (see {@link isExpectedError}). * `op` names what was being attempted (e.g. `migration.run`) and becomes the `op` tag, so events * group by step rather than by whichever message the PDS happened to answer with. Does not log: * callers keep their own console output. A no-op while the DSN is unset (local dev, e2e). * * @returns true when the error was reported, false when it was filtered as expected. */export function reportError( error: unknown, op: string, { level = 'error', extra }: ReportOptions = {},): boolean { if (isExpectedError(error)) return false
const context: Record<string, unknown> = { ...extra } if (error instanceof XrpcResponseError) { context.xrpc_error = error.error context.xrpc_status = error.status } Sentry.captureException(error, { level, tags: { op }, extra: context }) return true}