diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index 8a56423796..da34bd0dc6 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -1,4 +1,4 @@ -import {RichText} from '@atproto/api' +import {RichText} from '@bsky.app/sdk/richtext' import {i18n} from '@lingui/core' import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 6df56e5f78..6267907f16 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -1813,5 +1813,16 @@ "typescript/no-unsafe-member-access": { "count": 3 } + }, + "webpack.config.js": { + "import/no-nodejs-modules": { + "count": 1 + }, + "typescript/no-unsafe-call": { + "count": 11 + }, + "typescript/no-unsafe-member-access": { + "count": 30 + } } } \ No newline at end of file diff --git a/package.json b/package.json index f58b0adcb7..281cb0d8b0 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,6 @@ "prettier": "prettier --check ." }, "dependencies": { - "@atproto/api": "0.20.34", "@atproto/common-web": "0.5.6", "@atproto/lex": "0.2.2", "@atproto/lex-client": "0.2.4", diff --git a/src/features/inviteFriends/urls.ts b/src/features/inviteFriends/urls.ts index 7a22b8faf4..d75d5fa133 100644 --- a/src/features/inviteFriends/urls.ts +++ b/src/features/inviteFriends/urls.ts @@ -7,7 +7,7 @@ * label simply drops the `https://` scheme for readability. * * Kept as a dependency-free leaf module (no #/lib/strings/url-helpers import) - * so its unit tests stay fast and isolated from the heavy @atproto/api graph. + * so its unit tests stay fast and isolated from the heavy SDK graph. */ function stripLeadingAt(handle: string): string { diff --git a/src/lib/api/__tests__/computeCid.test.ts b/src/lib/api/__tests__/computeCid.test.ts index 4082c6b553..a2cd6967f3 100644 --- a/src/lib/api/__tests__/computeCid.test.ts +++ b/src/lib/api/__tests__/computeCid.test.ts @@ -7,22 +7,20 @@ jest.unmock('multiformats/cid') jest.unmock('multiformats/hashes/hasher') -import {BlobRef} from '@atproto/api' import {CID} from 'multiformats/cid' import {computeCid} from '#/lib/api/computeCid' import {type app, type com} from '#/lexicons' /* - * Golden-CID regression test for the composer post pipeline (design section F). + * Golden-CID regression test for the composer post pipeline. * * `computeCid` hashes a post record in the client so a thread's later posts can * reference earlier posts by CID before the server assigns them. The hash is * byte-sensitive: any drift in how records (especially blobs) are serialized to * DAG-CBOR silently produces the wrong CID and breaks reply chains with NO type - * error. These golden values were captured from the PRE-migration `computeCid` - * (the `instanceof BlobRef` path) and MUST remain byte-identical after the guard - * is changed to the structural `isBlobRef` shape check. + * error. These golden values MUST remain byte-identical - they gate the + * structural `isBlobRef` shape check. * * The blob CID below is a fixed, deterministic CIDv1/raw/sha256 used purely as a * stable fixture - it is not derived from any real upload. @@ -30,10 +28,7 @@ import {type app, type com} from '#/lexicons' const BLOB_CID = 'bafkreieq5jui4j25lacwomsqgjeswwl3y5zcdrresptwgmfylxo2depppq' /** - * Build a post record with an image embed whose blob is the given value. Used to - * prove that a `@atproto/api` `BlobRef` class instance (the shape the not-yet - * -migrated video path still yields) and a plain-JSON lex blob (the shape lex - * `uploadBlob` returns) hash to the SAME CID. + * Build a post record with an image embed whose blob is the given value. */ function postWithImageBlob(blob: unknown): app.bsky.feed.post.Main { return { @@ -65,22 +60,14 @@ describe('computeCid', () => { ) }) - it('case 2: record whose embed carries a BlobRef class instance', async () => { - const blob = new BlobRef(CID.parse(BLOB_CID), 'image/jpeg', 12345) - expect(await computeCid(postWithImageBlob(blob))).toBe( - 'bafyreiem7g6vja66nebr7he4fshfnlyndyldbvle2n265oixscmepjcbii', - ) - }) - - it('case 2b: a plain-JSON blob object hashes identically to the class instance', async () => { + it('case 2: record whose embed carries a plain-JSON lex blob', async () => { /* - * This is the post-migration shape: lex `uploadBlob` returns a plain object - * `{$type: 'blob', ref, mimeType, size}` (with `ref` a parsed CID), not a - * `BlobRef` class instance. The structural `isBlobRef` guard must treat it - * exactly like the class instance so the CID is unchanged. Under the - * pre-change `instanceof` code this case already matches because the plain - * object walks through `prepareForHashing` unchanged and DAG-CBOR encodes - * its CID `ref` the same way `.ipld()` does. + * The blob shape lex `uploadBlob` returns: a plain object + * `{$type: 'blob', ref, mimeType, size}` with `ref` a parsed CID. The + * structural `isBlobRef` guard passes it through `prepareForHashing` + * untouched and DAG-CBOR encodes its CID `ref` as a CID link. The golden + * CID below is the byte-identical value the pre-migration `BlobRef` class + * instance produced via `.ipld()`. */ const blob = { $type: 'blob' as const, diff --git a/src/lib/api/computeCid.ts b/src/lib/api/computeCid.ts index 894f7a8c03..fdfb344e58 100644 --- a/src/lib/api/computeCid.ts +++ b/src/lib/api/computeCid.ts @@ -46,10 +46,9 @@ export async function computeCid( } /** - * True for a plain-JSON lexicon blob, the shape lex `uploadBlob` now returns - * (`{$type: 'blob', ref, mimeType, size}` with `ref` a parsed CID). Replaces - * the old `instanceof BlobRef` check, since lex blobs are plain objects, not - * class instances (design section F). + * True for a plain-JSON lexicon blob, the shape lex `uploadBlob` returns + * (`{$type: 'blob', ref, mimeType, size}` with `ref` a parsed CID). Lex blobs + * are plain objects, not class instances. */ function isBlobRef(v: unknown): boolean { if (v == null || typeof v !== 'object') return false @@ -57,24 +56,6 @@ function isBlobRef(v: unknown): boolean { return o.$type === 'blob' && 'ref' in o && 'mimeType' in o } -/** - * True for a legacy `@atproto/api` `BlobRef` class instance. During the - * migration the video embed path still yields these (its blob comes from the - * not-yet-migrated `app.bsky.video.getJobStatus` bridge call), so we must keep - * handling them here even though the composer's own uploads are now plain lex - * blobs. A class instance is duck-typed by its `ipld()` method plus the - * `ref`/`mimeType` fields; it has NO `$type` and a non-plain prototype, so it - * would otherwise slip past both `isBlobRef` and `isPlainObject` and be encoded - * wrong - silently breaking video reply-chain CIDs. - */ -function isBlobRefInstance( - v: unknown, -): v is {ipld: () => unknown; ref: unknown; mimeType: unknown} { - if (v == null || typeof v !== 'object') return false - const o = v as Record - return typeof o.ipld === 'function' && 'ref' in o && 'mimeType' in o -} - // Returns a transformed version of the object for use in DAG-CBOR. // eslint-disable-next-line @typescript-eslint/no-explicit-any function prepareForHashing(v: any): any { @@ -87,16 +68,6 @@ function prepareForHashing(v: any): any { return v } - /* - * A legacy `BlobRef` class instance must be converted via `ipld()` to the - * plain `{$type, ref, mimeType, size}` object; encoding the instance directly - * would emit its internal `original` field and omit `$type`, producing the - * wrong CID. `ipld()` returns exactly what `isBlobRef` accepts above. - */ - if (isBlobRefInstance(v)) { - return v.ipld() - } - // Walk through arrays if (Array.isArray(v)) { let pure = true diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 1e50b1c635..305dbe1e5a 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -402,10 +402,9 @@ async function resolveMedia( return { $type: 'app.bsky.embed.video', /* - * The video blob is a legacy `@atproto/api` BlobRef from the not-yet - * -migrated video pipeline (getJobStatus, in composer state/video). Its - * structural shape matches the lexicon blob field; the CID hasher handles - * both class instances and plain lex blobs (see computeCid). + * The video blob is a plain lex blob from the video pipeline + * (getJobStatus, in composer state/video). Its structural shape matches + * the lexicon blob field and the CID hasher (see computeCid). */ video: videoDraft.pendingPublish.blobRef, alt: videoDraft.altText || undefined, diff --git a/src/lib/strings/__tests__/errors.test.ts b/src/lib/strings/__tests__/errors.test.ts index aa9538032f..d8cff26985 100644 --- a/src/lib/strings/__tests__/errors.test.ts +++ b/src/lib/strings/__tests__/errors.test.ts @@ -1,4 +1,3 @@ -import {XRPCError} from '@atproto/api' import { type Procedure, type Query, @@ -15,21 +14,9 @@ import { import {isErrorMaybeAppPasswordPermissions, isNetworkError} from '../errors' /** - * Old-world fixture: `@atproto/api` XRPCError. `.headers` is a plain record, - * `.error` is the lexicon code string, `.status` a numeric ResponseType enum. - */ -function oldError( - status: number, - error: string, - headers?: Record, -) { - return new XRPCError(status, error, undefined, headers) -} - -/** - * New-world fixture: lex `XrpcResponseError`. Built from a WHATWG `Response` - * (so `.headers` is a `Headers` object and `.status` is the numeric HTTP - * status) plus a JSON error payload carrying `.error`. + * Lex `XrpcResponseError` fixture. Built from a WHATWG `Response` (so + * `.headers` is a `Headers` object and `.status` is the numeric HTTP status) + * plus a JSON error payload carrying `.error`. */ function lexError( status: number, @@ -45,8 +32,7 @@ function lexError( } describe('isXrpcError', () => { - it('matches both old XRPCError and lex XrpcResponseError', () => { - expect(isXrpcError(oldError(400, 'TokenInvalid'))).toBe(true) + it('matches a lex XrpcResponseError', () => { expect(isXrpcError(lexError(400, 'TokenInvalid'))).toBe(true) }) @@ -58,8 +44,7 @@ describe('isXrpcError', () => { }) describe('getErrorStatus', () => { - it('reads status from both worlds', () => { - expect(typeof getErrorStatus(oldError(400, 'TokenInvalid'))).toBe('number') + it('reads status from a lex XrpcResponseError', () => { expect(getErrorStatus(lexError(429, 'RateLimitExceeded'))).toBe(429) }) @@ -69,8 +54,7 @@ describe('getErrorStatus', () => { }) describe('getErrorName', () => { - it('reads the lexicon error code from both worlds', () => { - expect(getErrorName(oldError(400, 'TokenInvalid'))).toBe('TokenInvalid') + it('reads the lexicon error code', () => { expect(getErrorName(lexError(400, 'TokenInvalid'))).toBe('TokenInvalid') }) @@ -80,12 +64,6 @@ describe('getErrorName', () => { }) describe('getErrorHeader', () => { - it('reads a header from the old record shape (case-insensitive)', () => { - const e = oldError(429, 'RateLimitExceeded', {'ratelimit-reset': '123'}) - expect(getErrorHeader(e, 'ratelimit-reset')).toBe('123') - expect(getErrorHeader(e, 'RateLimit-Reset')).toBe('123') - }) - it('reads a header from the lex Headers object', () => { const e = lexError(429, 'RateLimitExceeded', {'ratelimit-reset': '123'}) expect(getErrorHeader(e, 'ratelimit-reset')).toBe('123') @@ -99,10 +77,7 @@ describe('getErrorHeader', () => { }) describe('isErrorMaybeAppPasswordPermissions', () => { - it('matches a TokenInvalid error from both worlds', () => { - expect( - isErrorMaybeAppPasswordPermissions(oldError(400, 'TokenInvalid')), - ).toBe(true) + it('matches a TokenInvalid error', () => { expect( isErrorMaybeAppPasswordPermissions(lexError(400, 'TokenInvalid')), ).toBe(true) @@ -118,9 +93,6 @@ describe('isErrorMaybeAppPasswordPermissions', () => { }) it('does not match unrelated XRPC errors', () => { - expect( - isErrorMaybeAppPasswordPermissions(oldError(400, 'InvalidRequest')), - ).toBe(false) expect( isErrorMaybeAppPasswordPermissions(lexError(400, 'InvalidRequest')), ).toBe(false) diff --git a/src/lib/strings/errors.ts b/src/lib/strings/errors.ts index c737be01b3..7923fd7545 100644 --- a/src/lib/strings/errors.ts +++ b/src/lib/strings/errors.ts @@ -1,6 +1,6 @@ import {t} from '@lingui/core/macro' -import {getErrorName, getErrorStatus, isXrpcError} from '#/lib/xrpc-error' +import {getErrorName, isXrpcError} from '#/lib/xrpc-error' export function cleanError(e: unknown): string { if (!e) { @@ -87,13 +87,11 @@ export function isCancelledError(e: unknown) { return str.includes('cancel') } -// TODO Replace this with error.shouldRetry() when available. -dsb const RETRYABLE_ERRORS = [408, 425, 429, 500, 502, 503, 504, 522, 524] export function isRetryableHttpStatus(status: number) { return RETRYABLE_ERRORS.includes(status) } export function shouldRetryError(e: unknown) { - const status = getErrorStatus(e) - return status !== undefined && isRetryableHttpStatus(status) + return isXrpcError(e) && e.shouldRetry() } diff --git a/src/lib/xrpc-error.ts b/src/lib/xrpc-error.ts index bc23fda70a..dd9e3ab9d3 100644 --- a/src/lib/xrpc-error.ts +++ b/src/lib/xrpc-error.ts @@ -1,44 +1,34 @@ -import {XRPCError} from '@atproto/api' import {XrpcError, XrpcResponseError} from '@atproto/lex-client' /** - * True for an XRPC error from either the old bridge agent (`@atproto/api` - * `XRPCError`) or a lex `Client` (`@atproto/lex-client` `XrpcError`, the - * abstract base of `XrpcResponseError`/`XrpcInvalidResponseError`/ - * `XrpcInternalError`). During the migration both worlds can throw, so matchers - * must accept both. + * True for an XRPC error from a lex `Client` (`@atproto/lex-client` `XrpcError`, + * the abstract base of `XrpcResponseError`/`XrpcInvalidResponseError`/ + * `XrpcInternalError`). */ -export function isXrpcError(e: unknown): e is XRPCError | XrpcError { - return e instanceof XRPCError || e instanceof XrpcError +export function isXrpcError(e: unknown): e is XrpcError { + return e instanceof XrpcError } /** - * HTTP status, or undefined if not an XRPC error / no response. Only lex + * HTTP status, or undefined if not an XRPC error / no response. Only * `XrpcResponseError` (a server response) carries a status; the internal/fetch * lex errors do not. */ export function getErrorStatus(e: unknown): number | undefined { - if (e instanceof XRPCError) return e.status - if (e instanceof XrpcResponseError) return e.status - return undefined + return e instanceof XrpcResponseError ? e.status : undefined } -/** The lexicon error code (`err.error`), from either world. */ +/** The lexicon error code (`err.error`). */ export function getErrorName(e: unknown): string | undefined { - if (isXrpcError(e)) return (e as {error?: string}).error - return undefined + return isXrpcError(e) ? (e as {error?: string}).error : undefined } /** - * Read a response header off an XRPC error, normalizing the shape change: - * old XRPCError.headers is a plain record; lex XrpcResponseError.headers is a - * WHATWG Headers object. + * Read a response header off an XRPC error. Only `XrpcResponseError` carries a + * response; its `.headers` is a WHATWG `Headers` object. */ export function getErrorHeader(e: unknown, name: string): string | undefined { - if (e instanceof XrpcResponseError) return e.headers.get(name) ?? undefined - if (e instanceof XRPCError) { - const h = (e as {headers?: Record}).headers - return h?.[name.toLowerCase()] - } - return undefined + return e instanceof XrpcResponseError + ? (e.headers.get(name) ?? undefined) + : undefined } diff --git a/src/screens/StarterPack/Wizard/StepProfiles.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx index 5d21328700..45e52fa14b 100644 --- a/src/screens/StarterPack/Wizard/StepProfiles.tsx +++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx @@ -16,7 +16,6 @@ import {ScreenTransition} from '#/components/ScreenTransition' import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardListCard' import {Text} from '#/components/Typography' import {IS_NATIVE} from '#/env' -import {type app} from '#/lexicons' import type * as bsky from '#/types/bsky' function keyExtractor(item: bsky.profile.AnyProfileView) { diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index 0c34f2c1d2..5427d72a3e 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -66,8 +66,8 @@ function toSystemMessageView( * The guard disjunction is kept inline in the `if` (rather than a separate * boolean) so TS narrows `ev` to the union of system-message log types, all * of which carry a `message: SystemMessageView`. The generated lexicon types - * are stricter than the old `@atproto/api` ones (no `[k: string]: unknown` - * index signature), so a separate boolean would not narrow the access. + * are strict (no `[k: string]: unknown` index signature), so a separate + * boolean would not narrow the access. */ if ( bsky.isType(chat.bsky.convo.defs.logAddMember, ev) || diff --git a/src/state/queries/explore-feed-previews.tsx b/src/state/queries/explore-feed-previews.tsx index d1cc369473..4d1a36cfa3 100644 --- a/src/state/queries/explore-feed-previews.tsx +++ b/src/state/queries/explore-feed-previews.tsx @@ -205,7 +205,7 @@ export function useFeedPreviews( if (item.isFallbackMarker) continue const moderations = item.items.map(item => - // @atproto/api and yields lex-typed slice items. + // the tuner yields lex-typed slice items. moderatePost(item.post, moderationOpts!), ) @@ -232,7 +232,7 @@ export function useFeedPreviews( const feedPostSliceItem: FeedPostSliceItem = { _reactKey: `${item._reactKey}-${i}-${subItem.post.uri}`, uri: subItem.post.uri, - // off @atproto/api and yields lex-typed slice items. + // the tuner yields lex-typed slice items. post: subItem.post, record: subItem.record, moderation: moderations[i], diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index 0edb16faf2..d687ed7767 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -288,7 +288,7 @@ export function usePostFeedQuery( .map(slice => { const moderations = slice.items.map(item => moderatePost( - // off @atproto/api and yields lex-typed slice items. + // the tuner yields lex-typed slice items. item.post, moderationOpts!, ), @@ -341,7 +341,7 @@ export function usePostFeedQuery( const feedPostSliceItem: FeedPostSliceItem = { _reactKey: `${slice._reactKey}-${i}-${item.post.uri}`, uri: item.post.uri, - // off @atproto/api and yields lex-typed slice items. + // the tuner yields lex-typed slice items. post: item.post, record: item.record, moderation: moderations[i], diff --git a/src/state/queries/search-posts-params.ts b/src/state/queries/search-posts-params.ts index 33950edae1..78b549ce23 100644 --- a/src/state/queries/search-posts-params.ts +++ b/src/state/queries/search-posts-params.ts @@ -289,8 +289,8 @@ export function buildSearchPostsV2Filters( } /** - * Consistent with timestamp parsing in @atproto/api. Only the date is used; the - * time is appended here since the lexicon expects a datetime value. + * Consistent with atproto timestamp parsing. Only the date is used; the time + * is appended here since the lexicon expects a datetime value. */ const parseTimestamp = (value: string | undefined): string | undefined => { if (!value) return undefined diff --git a/src/types/bsky/__tests__/index.test.ts b/src/types/bsky/__tests__/index.test.ts index f108ebc98a..44f0bd4aed 100644 --- a/src/types/bsky/__tests__/index.test.ts +++ b/src/types/bsky/__tests__/index.test.ts @@ -1,5 +1,3 @@ -import {AppBskyFeedPost} from '@atproto/api' - import {app} from '#/lexicons' import * as bsky from '#/types/bsky' @@ -44,8 +42,8 @@ describe('types/bsky new-world helpers (#/lexicons)', () => { }) it('accepts an invalid body that has the right $type (dangerous semantics)', () => { - // Mirrors the old `dangerousIsType`: only the `$type` is checked, so a - // structurally invalid record still passes. + // Only the `$type` is checked, so a structurally invalid record still + // passes. expect(bsky.isType(app.bsky.feed.post, invalidPost)).toBe(true) }) @@ -55,8 +53,7 @@ describe('types/bsky new-world helpers (#/lexicons)', () => { }) it('returns false (does not throw) for null and undefined', () => { - // Mirrors the old `dangerousIsType`/`is$typed` behavior - call sites - // pass e.g. `post.record` which may be undefined. + // Call sites pass e.g. `post.record` which may be undefined. expect(bsky.isType(app.bsky.feed.post, null)).toBe(false) expect(bsky.isType(app.bsky.feed.post, undefined)).toBe(false) expect(bsky.isType(app.bsky.feed.post, 'string')).toBe(false) @@ -122,42 +119,3 @@ describe('types/bsky new-world helpers (#/lexicons)', () => { }) }) }) - -describe('types/bsky legacy helpers (@atproto/api) still work', () => { - it('dangerousIsType accepts the right $type without validating the body', () => { - expect( - bsky.dangerousIsType( - validPost, - AppBskyFeedPost.isRecord, - ), - ).toBe(true) - // Right $type, invalid body - still passes the fast guard. - expect( - bsky.dangerousIsType( - invalidPost, - AppBskyFeedPost.isRecord, - ), - ).toBe(true) - expect( - bsky.dangerousIsType( - wrongType, - AppBskyFeedPost.isRecord, - ), - ).toBe(false) - }) - - it('validate fully validates the body', () => { - expect( - bsky.validate( - validPost, - AppBskyFeedPost.validateRecord, - ), - ).toBe(true) - expect( - bsky.validate( - invalidPost, - AppBskyFeedPost.validateRecord, - ), - ).toBe(false) - }) -}) diff --git a/src/types/bsky/index.ts b/src/types/bsky/index.ts index e5b292e3a2..278c1c1079 100644 --- a/src/types/bsky/index.ts +++ b/src/types/bsky/index.ts @@ -10,86 +10,21 @@ import { type ValidateOptions, type ValidationResult, } from '@atproto/lex' -import {type ValidationResult as LegacyValidationResult} from '@atproto/lexicon' export * as post from '#/types/bsky/post' export * as profile from '#/types/bsky/profile' export * as starterPack from '#/types/bsky/starterPack' -/** - * Unsafe cast from an old `@atproto/api` view/record type to its `#/lexicons` - * equivalent. Only for mixed-world boundaries during the migration where a - * producer has not yet flipped. Structurally the shapes are identical modulo - * branded string types; this asserts the brand the compiler cannot prove. - * Every use is a migration debt marker - grep `toLex` at Phase 4 cleanup. - */ -export function toLex(v: unknown): T { - return v as T -} - -/** - * Fast type checking without full schema validation, for use with data we - * trust, or for non-critical path use cases. Why? Our SDK's `is*` identity - * utils do not assert the type of the entire object, only the `$type` string. - * - * For full validation of the object schema, use the `validate` export from - * this file. - * - * Usage: - * ```ts - * import * as bsky from '#/types/bsky' - * - * if (bsky.dangerousIsType(item, AppBskyFeedPost.isRecord)) { - * // `item` has type `$Typed` here - * } - * ``` - */ -export function dangerousIsType( - record: unknown, - identity: (v: V) => v is V & {$type: NonNullable}, -): record is R { - return identity(record) -} - -/** - * Fully validates the object schema, which has a performance cost. - * - * For faster checks with data we trust, like that from our app view, use the - * `dangerousIsType` export from this same file. - * - * Usage: - * ```ts - * import * as bsky from '#/types/bsky' - * - * if (bsky.validate(item, AppBskyFeedPost.validateRecord)) { - * // `item` has type `$Typed` here - * } - * ``` - */ -export function validate( - record: unknown, - validator: (v: unknown) => LegacyValidationResult, -): record is R { - return validator(record).success -} - /* - * New-world helpers (below) operate on the generated lexicon schema objects - * from '#/lexicons', replacing the `@atproto/api`-based helpers above. - * - * The old helpers take a record plus a standalone `is*`/`validate*` function - * (e.g. `AppBskyFeedPost.isRecord`). The new codegen instead attaches the - * validation surface directly to each schema, so these helpers take the schema - * object itself: + * These helpers operate on the generated lexicon schema objects from + * '#/lexicons'. The codegen attaches the validation surface directly to each + * schema, so these helpers take the schema object itself: * * ```ts * import {app} from '#/lexicons' * import * as bsky from '#/types/bsky' * - * // old: bsky.dangerousIsType(v, AppBskyFeedPost.isRecord) * bsky.isType(app.bsky.feed.post, v) - * - * // old: bsky.validate(v, AppBskyFeedPost.validateRecord) * bsky.matches(app.bsky.feed.post, v) * ``` * @@ -113,9 +48,8 @@ type TypedSchema = RecordSchema | TypedObjectSchema * string; it does NOT assert the rest of the object matches the schema. An * invalid record with the right `$type` will pass. * - * This is the '#/lexicons' equivalent of {@link dangerousIsType}. For full - * validation of the object schema, use {@link matches}, {@link parse}, or - * {@link safeParse} from this same file. + * For full validation of the object schema, use {@link matches}, {@link parse}, + * or {@link safeParse} from this same file. * * Usage: * ```ts @@ -135,10 +69,9 @@ export function isType( * Deliberately NOT delegating to the schema's `isTypeOf`: the generated * `TypedObjectSchema.isTypeOf` treats a MISSING `$type` as a match * (maybe-typed semantics), which would let any plain object satisfy any - * def-schema check and break `$type`-discriminated unions. The old - * `dangerousIsType` required a present, matching `$type`, and so do we. - * The nullish/object guard also mirrors the old `is$typed` behavior of - * returning false (not throwing) for null/undefined input. + * def-schema check and break `$type`-discriminated unions. We require a + * present, matching `$type`. The nullish/object guard also returns false + * (not throws) for null/undefined input. */ return ( value != null && @@ -151,9 +84,8 @@ export function isType( * Fully validates the object against the schema (strict, no coercion), which * has a performance cost, and narrows the value on success. * - * This is the '#/lexicons' equivalent of {@link validate}. For faster checks - * with data we trust, like that from our app view, use {@link isType} from this - * same file. + * For faster checks with data we trust, like that from our app view, use + * {@link isType} from this same file. * * Usage: * ```ts diff --git a/src/types/bsky/starterPack.ts b/src/types/bsky/starterPack.ts index ed080864ab..21cb4634df 100644 --- a/src/types/bsky/starterPack.ts +++ b/src/types/bsky/starterPack.ts @@ -1,11 +1,10 @@ import {app} from '#/lexicons' /* - * The generated `$type`-only guards. The old `@atproto/api` - * `AppBskyGraphDefs.isStarterPackView*` helpers matched on a present, - * matching `$type`; we reproduce that here against the `#/lexicons` schema's - * `$type` string rather than delegating to the schema's `isTypeOf` (which - * treats a missing `$type` as a match). + * The generated `$type`-only guards. These match on a present, matching + * `$type` against the `#/lexicons` schema's `$type` string rather than + * delegating to the schema's `isTypeOf` (which treats a missing `$type` as a + * match). */ export function isBasicView( v: unknown, diff --git a/src/view/com/composer/drafts/state/schema.ts b/src/view/com/composer/drafts/state/schema.ts index 123fc1d715..7a4f4c44f7 100644 --- a/src/view/com/composer/drafts/state/schema.ts +++ b/src/view/com/composer/drafts/state/schema.ts @@ -1,7 +1,7 @@ import {type app} from '#/lexicons' /** * Types for draft display and local media tracking. - * Server draft types come from @atproto/api. + * Server draft types come from the atproto lexicons. */ /** diff --git a/webpack.config.js b/webpack.config.js index 889a2cb83f..dace4e8fe5 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -45,7 +45,7 @@ function patchSourceMapFilter(rules, pathPattern) { module.exports = async function (env, argv) { env.babel = { - dangerouslyAddModulePathsToTranspile: ['@bsky.app/expo', '@atproto/api'], + dangerouslyAddModulePathsToTranspile: ['@bsky.app/expo'], } let config = await createExpoWebpackConfigAsync(env, argv) /*