From 939f96b30df79d648f28ca774df6eabb9c6fa559 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Thu, 12 Dec 2024 17:21:51 +0100 Subject: [PATCH 1/2] Make lex-gen types more precise --- .changeset/brown-flies-stare.md | 9 + .changeset/fast-waves-jog.md | 5 + .changeset/funny-buses-fly.md | 5 + .changeset/green-cherries-shave.md | 5 + .changeset/green-forks-lie.md | 5 + .changeset/itchy-rockets-speak.md | 5 + .changeset/lazy-moles-switch.md | 5 + .changeset/shiny-suns-walk.md | 5 + .changeset/small-pumas-rush.md | 5 + .changeset/spotty-bottles-march.md | 5 + .changeset/tall-laws-play.md | 5 + .changeset/yellow-gorillas-thank.md | 5 + .changeset/young-lamps-hunt.md | 5 + packages/api/src/agent.ts | 211 ++++++--------- packages/api/src/mocker.ts | 14 +- packages/api/src/moderation/mutewords.ts | 2 +- packages/api/src/moderation/subjects/post.ts | 58 ++-- .../api/src/moderation/subjects/user-list.ts | 5 +- packages/api/src/rich-text/rich-text.ts | 18 +- packages/api/tests/atp-agent.test.ts | 14 +- packages/api/tests/moderation-prefs.test.ts | 3 +- .../api/tests/rich-text-detection.test.ts | 10 +- .../src/api/app/bsky/actor/getSuggestions.ts | 2 +- .../bsky/src/api/app/bsky/feed/getFeed.ts | 14 +- .../src/api/app/bsky/labeler/getServices.ts | 4 +- .../bsky/notification/listNotifications.ts | 4 +- .../server/indexing/plugins/post.ts | 12 +- packages/bsky/src/hydration/graph.ts | 5 +- packages/bsky/src/hydration/util.ts | 6 +- packages/bsky/src/views/index.ts | 181 +++++++++---- packages/bsky/src/views/types.ts | 6 +- packages/bsky/tests/_util.ts | 77 ++++-- packages/bsky/tests/admin/admin-auth.test.ts | 10 +- packages/bsky/tests/admin/moderation.test.ts | 20 +- packages/bsky/tests/views/author-feed.test.ts | 4 +- packages/bsky/tests/views/blocks.test.ts | 21 +- .../bsky/tests/views/labeler-service.test.ts | 5 +- packages/bsky/tests/views/list-feed.test.ts | 13 +- packages/bsky/tests/views/thread.test.ts | 19 +- .../bsky/tests/views/threadgating.test.ts | 2 +- packages/bsky/tsconfig.json | 11 +- packages/dev-env/src/moderator-client.ts | 11 +- packages/dev-env/src/seed/client.ts | 2 +- packages/lex-cli/src/codegen/client.ts | 35 ++- packages/lex-cli/src/codegen/common.ts | 64 ++++- packages/lex-cli/src/codegen/lex-gen.ts | 149 ++++++---- packages/lex-cli/src/codegen/server.ts | 16 +- .../ozone/src/api/moderation/emitEvent.ts | 6 +- packages/ozone/src/api/moderation/getRepos.ts | 2 +- packages/ozone/src/api/util.ts | 27 +- packages/ozone/src/mod-service/index.ts | 15 +- packages/ozone/src/mod-service/subject.ts | 48 ++-- packages/ozone/src/mod-service/types.ts | 16 +- packages/ozone/src/mod-service/util.ts | 4 +- packages/ozone/src/mod-service/views.ts | 255 +++++++++--------- .../ozone/src/tag-service/embed-tagger.ts | 2 +- .../ozone/src/tag-service/language-tagger.ts | 8 +- packages/ozone/tests/_util.ts | 41 +-- .../ozone/tests/moderation-appeals.test.ts | 1 + .../ozone/tests/moderation-events.test.ts | 25 +- .../ozone/tests/moderation-statuses.test.ts | 17 +- packages/ozone/tests/moderation.test.ts | 4 +- .../tests/record-and-account-events.test.ts | 45 ++-- packages/pds/src/actor-store/record/reader.ts | 4 +- .../src/api/app/bsky/feed/getActorLikes.ts | 2 +- .../src/api/app/bsky/feed/getPostThread.ts | 11 +- .../com/atproto/identity/signPlcOperation.ts | 12 +- .../api/com/atproto/identity/updateHandle.ts | 1 + packages/pds/src/pipethrough.ts | 4 +- packages/pds/src/read-after-write/viewer.ts | 33 ++- packages/pds/src/repo/prepare.ts | 43 +-- packages/pds/tests/_util.ts | 9 +- packages/pds/tests/app-passwords.test.ts | 8 +- packages/pds/tests/crud.test.ts | 12 +- packages/pds/tests/file-uploads.test.ts | 2 + packages/pds/tests/moderation.test.ts | 25 +- packages/pds/tests/moderator-auth.test.ts | 7 +- packages/pds/tests/preferences.test.ts | 1 + packages/pds/tests/proxied/admin.test.ts | 6 + .../tests/proxied/read-after-write.test.ts | 46 ++-- tsconfig/browser.json | 2 +- tsconfig/isomorphic.json | 2 +- tsconfig/node.json | 2 +- tsconfig/nodenext.json | 4 +- tsconfig/tests.json | 2 +- 85 files changed, 1159 insertions(+), 692 deletions(-) create mode 100644 .changeset/brown-flies-stare.md create mode 100644 .changeset/fast-waves-jog.md create mode 100644 .changeset/funny-buses-fly.md create mode 100644 .changeset/green-cherries-shave.md create mode 100644 .changeset/green-forks-lie.md create mode 100644 .changeset/itchy-rockets-speak.md create mode 100644 .changeset/lazy-moles-switch.md create mode 100644 .changeset/shiny-suns-walk.md create mode 100644 .changeset/small-pumas-rush.md create mode 100644 .changeset/spotty-bottles-march.md create mode 100644 .changeset/tall-laws-play.md create mode 100644 .changeset/yellow-gorillas-thank.md create mode 100644 .changeset/young-lamps-hunt.md diff --git a/.changeset/brown-flies-stare.md b/.changeset/brown-flies-stare.md new file mode 100644 index 00000000000..97aa88fdec2 --- /dev/null +++ b/.changeset/brown-flies-stare.md @@ -0,0 +1,9 @@ +--- +"@atproto/ozone": patch +"@atproto/bsky": patch +"@atproto/api": patch +"@atproto/pds": patch +--- + +Update generated code to better reflect actual entity structure + diff --git a/.changeset/fast-waves-jog.md b/.changeset/fast-waves-jog.md new file mode 100644 index 00000000000..401816823af --- /dev/null +++ b/.changeset/fast-waves-jog.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": patch +--- + +Strongly type result of generated validation helpers diff --git a/.changeset/funny-buses-fly.md b/.changeset/funny-buses-fly.md new file mode 100644 index 00000000000..4302b0f17d0 --- /dev/null +++ b/.changeset/funny-buses-fly.md @@ -0,0 +1,5 @@ +--- +"@atproto/api": patch +--- + +Small reduction in bundle size diff --git a/.changeset/green-cherries-shave.md b/.changeset/green-cherries-shave.md new file mode 100644 index 00000000000..13326d6d967 --- /dev/null +++ b/.changeset/green-cherries-shave.md @@ -0,0 +1,5 @@ +--- +"@atproto/pds": patch +--- + +Minor typing fixes diff --git a/.changeset/green-forks-lie.md b/.changeset/green-forks-lie.md new file mode 100644 index 00000000000..a877f89f412 --- /dev/null +++ b/.changeset/green-forks-lie.md @@ -0,0 +1,5 @@ +--- +"@atproto/api": minor +--- + +Exposed type utilities (e.g. isRecord) no longer cast the output value. Use the new "isValid" helper for that purpose. diff --git a/.changeset/itchy-rockets-speak.md b/.changeset/itchy-rockets-speak.md new file mode 100644 index 00000000000..0aa342c8d04 --- /dev/null +++ b/.changeset/itchy-rockets-speak.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": patch +--- + +Add isValidX helper to check $type and validate schema diff --git a/.changeset/lazy-moles-switch.md b/.changeset/lazy-moles-switch.md new file mode 100644 index 00000000000..d9518a118c5 --- /dev/null +++ b/.changeset/lazy-moles-switch.md @@ -0,0 +1,5 @@ +--- +"@atproto/api": minor +--- + +Add a `$type` property to record and custom user type interfaces diff --git a/.changeset/shiny-suns-walk.md b/.changeset/shiny-suns-walk.md new file mode 100644 index 00000000000..8b16e53ad29 --- /dev/null +++ b/.changeset/shiny-suns-walk.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": patch +--- + +Strongly type client "ids" const diff --git a/.changeset/small-pumas-rush.md b/.changeset/small-pumas-rush.md new file mode 100644 index 00000000000..d1024198e22 --- /dev/null +++ b/.changeset/small-pumas-rush.md @@ -0,0 +1,5 @@ +--- +"@atproto/lexicon": patch +--- + +Add "value" parameter in ValidationResult diff --git a/.changeset/spotty-bottles-march.md b/.changeset/spotty-bottles-march.md new file mode 100644 index 00000000000..910f685c7c1 --- /dev/null +++ b/.changeset/spotty-bottles-march.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": minor +--- + +Remove string index signature from custom objects, input and output schemas. diff --git a/.changeset/tall-laws-play.md b/.changeset/tall-laws-play.md new file mode 100644 index 00000000000..ef3fbc40b59 --- /dev/null +++ b/.changeset/tall-laws-play.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": patch +--- + +Fully type validation result of generated "validate" helpers diff --git a/.changeset/yellow-gorillas-thank.md b/.changeset/yellow-gorillas-thank.md new file mode 100644 index 00000000000..13d72311850 --- /dev/null +++ b/.changeset/yellow-gorillas-thank.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": minor +--- + +isX utility no longer type casts input diff --git a/.changeset/young-lamps-hunt.md b/.changeset/young-lamps-hunt.md new file mode 100644 index 00000000000..3117ec9d2a1 --- /dev/null +++ b/.changeset/young-lamps-hunt.md @@ -0,0 +1,5 @@ +--- +"@atproto/lex-cli": minor +--- + +Add "$type" property to record and custom user types. diff --git a/packages/api/src/agent.ts b/packages/api/src/agent.ts index 7613e58d1fa..2d80b338dda 100644 --- a/packages/api/src/agent.ts +++ b/packages/api/src/agent.ts @@ -1,11 +1,6 @@ import { TID } from '@atproto/common-web' import { AtUri, ensureValidDid } from '@atproto/syntax' -import { - buildFetchHandler, - BuildFetchHandlerOptions, - FetchHandler, - XrpcClient, -} from '@atproto/xrpc' +import { buildFetchHandler, FetchHandler, XrpcClient } from '@atproto/xrpc' import AwaitLock from 'await-lock' import { AppBskyActorDefs, @@ -20,6 +15,7 @@ import { } from './client/index' import { schemas } from './client/lexicons' import { MutedWord, Nux } from './client/types/app/bsky/actor/defs' +import { $Typed } from './client/util' import { BSKY_LABELER_DID } from './const' import { interpretLabelValueDefinitions } from './moderation' import { DEFAULT_LABEL_SETTINGS } from './moderation/const/labels' @@ -44,8 +40,8 @@ import { isDid, sanitizeMutedWordValue, savedFeedsToUriArrays, - validateSavedFeed, validateNux, + validateSavedFeed, } from './util' const FEED_VIEW_PREF_DEFAULTS = { @@ -61,15 +57,6 @@ const THREAD_VIEW_PREF_DEFAULTS = { prioritizeFollowedUsers: true, } -declare global { - interface Array { - findLast( - predicate: (value: T, index: number, obj: T[]) => unknown, - thisArg?: any, - ): T - } -} - export type { FetchHandler } /** @@ -895,7 +882,7 @@ export class Agent extends XrpcClient { async setAdultContentEnabled(v: boolean) { await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { let adultContentPref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isAdultContentPref(pref) && AppBskyActorDefs.validateAdultContentPref(pref).success, ) @@ -922,26 +909,22 @@ export class Agent extends XrpcClient { ensureValidDid(labelerDid) } await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { - let labelPref = prefs.findLast( - (pref) => + const labelPref = prefs.findLast( + (pref): pref is $Typed => AppBskyActorDefs.isContentLabelPref(pref) && AppBskyActorDefs.validateContentLabelPref(pref).success && pref.label === key && pref.labelerDid === labelerDid, - ) - let legacyLabelPref: AppBskyActorDefs.ContentLabelPref | undefined - - if (labelPref) { - labelPref.visibility = value - } else { - labelPref = { - $type: 'app.bsky.actor.defs#contentLabelPref', - label: key, - labelerDid, - visibility: value, - } + ) ?? { + $type: 'app.bsky.actor.defs#contentLabelPref', + label: key, + labelerDid, + visibility: value, } + labelPref.visibility = value + + let legacyLabelPref: $Typed | undefined if (AppBskyActorDefs.isContentLabelPref(labelPref)) { // is global if (!labelPref.labelerDid) { @@ -949,28 +932,26 @@ export class Agent extends XrpcClient { 'graphic-media': 'gore', porn: 'nsfw', sexual: 'suggestive', + // Protect against using toString, hasOwnProperty, etc. as a label: + __proto__: null, }[labelPref.label] // if it's a legacy label, double-write the legacy label if (legacyLabelValue) { legacyLabelPref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isContentLabelPref(pref) && AppBskyActorDefs.validateContentLabelPref(pref).success && pref.label === legacyLabelValue && pref.labelerDid === undefined, - ) as AppBskyActorDefs.ContentLabelPref | undefined - - if (legacyLabelPref) { - legacyLabelPref.visibility = value - } else { - legacyLabelPref = { - $type: 'app.bsky.actor.defs#contentLabelPref', - label: legacyLabelValue, - labelerDid: undefined, - visibility: value, - } + ) ?? { + $type: 'app.bsky.actor.defs#contentLabelPref', + label: legacyLabelValue, + labelerDid: undefined, + visibility: value, } + + legacyLabelPref.visibility = value } } } @@ -999,18 +980,16 @@ export class Agent extends XrpcClient { async addLabeler(did: string) { const prefs = await this.updatePreferences( (prefs: AppBskyActorDefs.Preferences) => { - let labelersPref = prefs.findLast( + const labelersPref = prefs.findLast( (pref) => AppBskyActorDefs.isLabelersPref(pref) && AppBskyActorDefs.validateLabelersPref(pref).success, - ) - if (!labelersPref) { - labelersPref = { - $type: 'app.bsky.actor.defs#labelersPref', - labelers: [], - } + ) ?? { + $type: 'app.bsky.actor.defs#labelersPref', + labelers: [], } - if (AppBskyActorDefs.isLabelersPref(labelersPref)) { + + if (AppBskyActorDefs.isValidLabelersPref(labelersPref)) { let labelerPrefItem = labelersPref.labelers.find( (labeler) => labeler.did === did, ) @@ -1021,6 +1000,7 @@ export class Agent extends XrpcClient { labelersPref.labelers.push(labelerPrefItem) } } + return prefs .filter((pref) => !AppBskyActorDefs.isLabelersPref(pref)) .concat([labelersPref]) @@ -1066,7 +1046,7 @@ export class Agent extends XrpcClient { birthDate = birthDate instanceof Date ? birthDate.toISOString() : birthDate await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { let personalDetailsPref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isPersonalDetailsPref(pref) && AppBskyActorDefs.validatePersonalDetailsPref(pref).success, ) @@ -1087,19 +1067,22 @@ export class Agent extends XrpcClient { async setFeedViewPrefs(feed: string, pref: Partial) { await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { const existing = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isFeedViewPref(pref) && AppBskyActorDefs.validateFeedViewPref(pref).success && pref.feed === feed, ) - if (existing) { - pref = { ...existing, ...pref } - } + return prefs - .filter( - (p) => !AppBskyActorDefs.isFeedViewPref(pref) || p.feed !== feed, - ) - .concat([{ ...pref, $type: 'app.bsky.actor.defs#feedViewPref', feed }]) + .filter((p) => !AppBskyActorDefs.isFeedViewPref(p) || p.feed !== feed) + .concat([ + { + ...existing, + ...pref, + $type: 'app.bsky.actor.defs#feedViewPref', + feed, + }, + ]) }) } @@ -1110,12 +1093,12 @@ export class Agent extends XrpcClient { AppBskyActorDefs.isThreadViewPref(pref) && AppBskyActorDefs.validateThreadViewPref(pref).success, ) - if (existing) { - pref = { ...existing, ...pref } - } + return prefs .filter((p) => !AppBskyActorDefs.isThreadViewPref(p)) - .concat([{ ...pref, $type: 'app.bsky.actor.defs#threadViewPref' }]) + .concat([ + { ...existing, ...pref, $type: 'app.bsky.actor.defs#threadViewPref' }, + ]) }) } @@ -1126,12 +1109,12 @@ export class Agent extends XrpcClient { AppBskyActorDefs.isInterestsPref(pref) && AppBskyActorDefs.validateInterestsPref(pref).success, ) - if (existing) { - pref = { ...existing, ...pref } - } + return prefs .filter((p) => !AppBskyActorDefs.isInterestsPref(p)) - .concat([{ ...pref, $type: 'app.bsky.actor.defs#interestsPref' }]) + .concat([ + { ...existing, ...pref, $type: 'app.bsky.actor.defs#interestsPref' }, + ]) }) } @@ -1175,15 +1158,14 @@ export class Agent extends XrpcClient { } else { // if the pref doesn't exist, create it mutedWordsPref = { + $type: 'app.bsky.actor.defs#mutedWordsPref', items: [newMutedWord], } } return prefs .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p)) - .concat([ - { ...mutedWordsPref, $type: 'app.bsky.actor.defs#mutedWordsPref' }, - ]) + .concat([mutedWordsPref]) }) } @@ -1313,13 +1295,15 @@ export class Agent extends XrpcClient { async bskyAppQueueNudges(nudges: string | string[]) { await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { - let bskyAppStatePref: AppBskyActorDefs.BskyAppStatePref = prefs.findLast( - (pref) => + let bskyAppStatePref = prefs.findLast( + (pref): pref is $Typed => AppBskyActorDefs.isBskyAppStatePref(pref) && AppBskyActorDefs.validateBskyAppStatePref(pref).success, ) - bskyAppStatePref = bskyAppStatePref || {} + bskyAppStatePref = bskyAppStatePref || { + $type: 'app.bsky.actor.defs#bskyAppStatePref', + } nudges = Array.isArray(nudges) ? nudges : [nudges] bskyAppStatePref.queuedNudges = ( bskyAppStatePref.queuedNudges || [] @@ -1327,24 +1311,21 @@ export class Agent extends XrpcClient { return prefs .filter((p) => !AppBskyActorDefs.isBskyAppStatePref(p)) - .concat([ - { - ...bskyAppStatePref, - $type: 'app.bsky.actor.defs#bskyAppStatePref', - }, - ]) + .concat([bskyAppStatePref]) }) } async bskyAppDismissNudges(nudges: string | string[]) { await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { - let bskyAppStatePref: AppBskyActorDefs.BskyAppStatePref = prefs.findLast( - (pref) => + let bskyAppStatePref = prefs.findLast( + (pref): pref is $Typed => AppBskyActorDefs.isBskyAppStatePref(pref) && AppBskyActorDefs.validateBskyAppStatePref(pref).success, ) - bskyAppStatePref = bskyAppStatePref || {} + bskyAppStatePref = bskyAppStatePref || { + $type: 'app.bsky.actor.defs#bskyAppStatePref', + } nudges = Array.isArray(nudges) ? nudges : [nudges] bskyAppStatePref.queuedNudges = ( bskyAppStatePref.queuedNudges || [] @@ -1352,12 +1333,7 @@ export class Agent extends XrpcClient { return prefs .filter((p) => !AppBskyActorDefs.isBskyAppStatePref(p)) - .concat([ - { - ...bskyAppStatePref, - $type: 'app.bsky.actor.defs#bskyAppStatePref', - }, - ]) + .concat([bskyAppStatePref]) }) } @@ -1372,23 +1348,20 @@ export class Agent extends XrpcClient { } await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { - let bskyAppStatePref: AppBskyActorDefs.BskyAppStatePref = prefs.findLast( - (pref) => + let bskyAppStatePref = prefs.findLast( + (pref): pref is $Typed => AppBskyActorDefs.isBskyAppStatePref(pref) && AppBskyActorDefs.validateBskyAppStatePref(pref).success, ) - bskyAppStatePref = bskyAppStatePref || {} + bskyAppStatePref = bskyAppStatePref || { + $type: 'app.bsky.actor.defs#bskyAppStatePref', + } bskyAppStatePref.activeProgressGuide = guide return prefs .filter((p) => !AppBskyActorDefs.isBskyAppStatePref(p)) - .concat([ - { - ...bskyAppStatePref, - $type: 'app.bsky.actor.defs#bskyAppStatePref', - }, - ]) + .concat([bskyAppStatePref]) }) } @@ -1399,13 +1372,15 @@ export class Agent extends XrpcClient { validateNux(nux) await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { - let bskyAppStatePref: AppBskyActorDefs.BskyAppStatePref = prefs.findLast( - (pref) => + let bskyAppStatePref = prefs.findLast( + (pref): pref is $Typed => AppBskyActorDefs.isBskyAppStatePref(pref) && AppBskyActorDefs.validateBskyAppStatePref(pref).success, ) - bskyAppStatePref = bskyAppStatePref || {} + bskyAppStatePref = bskyAppStatePref || { + $type: 'app.bsky.actor.defs#bskyAppStatePref', + } bskyAppStatePref.nuxs = bskyAppStatePref.nuxs || [] const existing = bskyAppStatePref.nuxs?.find((n) => { @@ -1432,12 +1407,7 @@ export class Agent extends XrpcClient { return prefs .filter((p) => !AppBskyActorDefs.isBskyAppStatePref(p)) - .concat([ - { - ...bskyAppStatePref, - $type: 'app.bsky.actor.defs#bskyAppStatePref', - }, - ]) + .concat([bskyAppStatePref]) }) } @@ -1446,25 +1416,22 @@ export class Agent extends XrpcClient { */ async bskyAppRemoveNuxs(ids: string[]) { await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { - let bskyAppStatePref: AppBskyActorDefs.BskyAppStatePref = prefs.findLast( - (pref) => + let bskyAppStatePref = prefs.findLast( + (pref): pref is $Typed => AppBskyActorDefs.isBskyAppStatePref(pref) && AppBskyActorDefs.validateBskyAppStatePref(pref).success, ) - bskyAppStatePref = bskyAppStatePref || {} + bskyAppStatePref = bskyAppStatePref || { + $type: 'app.bsky.actor.defs#bskyAppStatePref', + } bskyAppStatePref.nuxs = (bskyAppStatePref.nuxs || []).filter((nux) => { return !ids.includes(nux.id) }) return prefs .filter((p) => !AppBskyActorDefs.isBskyAppStatePref(p)) - .concat([ - { - ...bskyAppStatePref, - $type: 'app.bsky.actor.defs#bskyAppStatePref', - }, - ]) + .concat([bskyAppStatePref]) }) } @@ -1539,10 +1506,10 @@ export class Agent extends XrpcClient { let res await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { let feedsPref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isSavedFeedsPref(pref) && AppBskyActorDefs.validateSavedFeedsPref(pref).success, - ) as AppBskyActorDefs.SavedFeedsPref | undefined + ) if (feedsPref) { res = cb(feedsPref.saved, feedsPref.pinned) feedsPref.saved = res.saved @@ -1571,15 +1538,15 @@ export class Agent extends XrpcClient { await this.updatePreferences((prefs: AppBskyActorDefs.Preferences) => { let existingV2Pref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isSavedFeedsPrefV2(pref) && AppBskyActorDefs.validateSavedFeedsPrefV2(pref).success, - ) as AppBskyActorDefs.SavedFeedsPrefV2 | undefined + ) let existingV1Pref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isSavedFeedsPref(pref) && AppBskyActorDefs.validateSavedFeedsPref(pref).success, - ) as AppBskyActorDefs.SavedFeedsPref | undefined + ) if (existingV2Pref) { maybeMutatedSavedFeeds = cb(existingV2Pref.items) diff --git a/packages/api/src/mocker.ts b/packages/api/src/mocker.ts index 556dba965c8..81bec590238 100644 --- a/packages/api/src/mocker.ts +++ b/packages/api/src/mocker.ts @@ -7,6 +7,7 @@ import { AppBskyGraphDefs, AppBskyNotificationListNotifications, } from './client' +import { $Typed } from './client/util' const FAKE_CID = 'bafyreiclp443lavogvhj3d2ob2cxbfuscni2k5jk7bebjzg7khl3esabwq' @@ -21,7 +22,7 @@ export const mock = { facets?: AppBskyFeedPost.Record['facets'] reply?: AppBskyFeedPost.ReplyRef embed?: AppBskyFeedPost.Record['embed'] - }): AppBskyFeedPost.Record { + }): $Typed { return { $type: 'app.bsky.feed.post', text, @@ -51,7 +52,7 @@ export const mock = { likeCount?: number viewer?: AppBskyFeedDefs.ViewerState labels?: ComAtprotoLabelDefs.Label[] - }): AppBskyFeedDefs.PostView { + }): $Typed { return { $type: 'app.bsky.feed.defs#postView', uri: `at://${author.did}/app.bsky.feed.post/fake`, @@ -76,7 +77,7 @@ export const mock = { record: AppBskyFeedPost.Record author: AppBskyActorDefs.ProfileViewBasic labels?: ComAtprotoLabelDefs.Label[] - }): AppBskyEmbedRecord.View { + }): $Typed { return { $type: 'app.bsky.embed.record#view', record: { @@ -108,7 +109,8 @@ export const mock = { did: `did:web:${handle}`, handle, displayName, - description, // technically not in ProfileViewBasic but useful in some cases + // @ts-expect-error technically not in ProfileViewBasic but useful in some cases + description, viewer, labels, } @@ -158,7 +160,7 @@ export const mock = { labels, }: { record: AppBskyFeedPost.Record - author: AppBskyActorDefs.ProfileViewBasic + author: Omit labels?: ComAtprotoLabelDefs.Label[] }): AppBskyNotificationListNotifications.Notification { return { @@ -179,7 +181,7 @@ export const mock = { subjectDid, labels, }: { - author: AppBskyActorDefs.ProfileViewBasic + author: Omit subjectDid: string labels?: ComAtprotoLabelDefs.Label[] }): AppBskyNotificationListNotifications.Notification { diff --git a/packages/api/src/moderation/mutewords.ts b/packages/api/src/moderation/mutewords.ts index 143e9c0bb52..f88a1af1cca 100644 --- a/packages/api/src/moderation/mutewords.ts +++ b/packages/api/src/moderation/mutewords.ts @@ -34,7 +34,7 @@ export function hasMutedWord({ facets?: AppBskyRichtextFacet.Main[] outlineTags?: string[] languages?: string[] - actor?: AppBskyActorDefs.ProfileView + actor?: AppBskyActorDefs.ProfileView | AppBskyActorDefs.ProfileViewBasic }) { const exception = LANGUAGE_EXCEPTIONS.includes(languages?.[0] || '') const tags = ([] as string[]) diff --git a/packages/api/src/moderation/subjects/post.ts b/packages/api/src/moderation/subjects/post.ts index f17ec40293f..01c1c631697 100644 --- a/packages/api/src/moderation/subjects/post.ts +++ b/packages/api/src/moderation/subjects/post.ts @@ -32,7 +32,11 @@ export function decidePost( let embedAcc if (subject.embed) { - if (AppBskyEmbedRecord.isViewRecord(subject.embed.record)) { + if ( + (AppBskyEmbedRecord.isView(subject.embed) || + AppBskyEmbedRecordWithMedia.isView(subject.embed)) && + AppBskyEmbedRecord.isViewRecord(subject.embed.record) + ) { // quote post embedAcc = decideQuotedPost(subject.embed.record, opts) } else if ( @@ -41,7 +45,11 @@ export function decidePost( ) { // quoted post with media embedAcc = decideQuotedPost(subject.embed.record.record, opts) - } else if (AppBskyEmbedRecord.isViewBlocked(subject.embed.record)) { + } else if ( + (AppBskyEmbedRecord.isView(subject.embed) || + AppBskyEmbedRecordWithMedia.isView(subject.embed)) && + AppBskyEmbedRecord.isViewBlocked(subject.embed.record) + ) { // blocked quote post embedAcc = decideBlockedQuotedPost(subject.embed.record, opts) } else if ( @@ -115,7 +123,10 @@ function checkHiddenPost( if (hiddenPosts.includes(subject.uri)) { return true } - if (subject.embed) { + if ( + AppBskyEmbedRecord.isView(subject.embed) || + AppBskyEmbedRecordWithMedia.isView(subject.embed) + ) { if ( AppBskyEmbedRecord.isViewRecord(subject.embed.record) && hiddenPosts.includes(subject.embed.record.uri) @@ -143,7 +154,7 @@ function checkMutedWords( const postAuthor = subject.author - if (AppBskyFeedPost.isRecord(subject.record)) { + if (AppBskyFeedPost.isValidRecord(subject.record)) { // post text if ( hasMutedWord({ @@ -178,12 +189,17 @@ function checkMutedWords( } } - if (subject.embed) { + const { embed } = subject + if (embed) { // quote post - if (AppBskyEmbedRecord.isViewRecord(subject.embed.record)) { - if (AppBskyFeedPost.isRecord(subject.embed.record.value)) { - const embeddedPost = subject.embed.record.value - const embedAuthor = subject.embed.record.author + if ( + (AppBskyEmbedRecord.isView(embed) || + AppBskyEmbedRecordWithMedia.isView(embed)) && + AppBskyEmbedRecord.isViewRecord(embed.record) + ) { + if (AppBskyFeedPost.isValidRecord(embed.record.value)) { + const embeddedPost = embed.record.value + const embedAuthor = embed.record.author // quoted post text if ( @@ -267,8 +283,8 @@ function checkMutedWords( } } // link card - else if (AppBskyEmbedExternal.isView(subject.embed)) { - const { external } = subject.embed + else if (AppBskyEmbedExternal.isView(embed)) { + const { external } = embed if ( hasMutedWord({ mutedWords, @@ -282,14 +298,14 @@ function checkMutedWords( } // quote post with media else if ( - AppBskyEmbedRecordWithMedia.isView(subject.embed) && - AppBskyEmbedRecord.isViewRecord(subject.embed.record.record) + AppBskyEmbedRecordWithMedia.isView(embed) && + AppBskyEmbedRecord.isViewRecord(embed.record.record) ) { - const embedAuthor = subject.embed.record.record.author + const embedAuthor = embed.record.record.author // quoted post text - if (AppBskyFeedPost.isRecord(subject.embed.record.record.value)) { - const post = subject.embed.record.record.value + if (AppBskyFeedPost.isValidRecord(embed.record.record.value)) { + const post = embed.record.record.value if ( hasMutedWord({ mutedWords, @@ -305,13 +321,13 @@ function checkMutedWords( } // quoted post images - if (AppBskyEmbedImages.isView(subject.embed.media)) { - for (const image of subject.embed.media.images) { + if (AppBskyEmbedImages.isView(embed.media)) { + for (const image of embed.media.images) { if ( hasMutedWord({ mutedWords, text: image.alt, - languages: AppBskyFeedPost.isRecord(subject.record) + languages: AppBskyFeedPost.isValidRecord(subject.record) ? subject.record.langs : [], actor: embedAuthor, @@ -322,8 +338,8 @@ function checkMutedWords( } } - if (AppBskyEmbedExternal.isView(subject.embed.media)) { - const { external } = subject.embed.media + if (AppBskyEmbedExternal.isView(embed.media)) { + const { external } = embed.media if ( hasMutedWord({ mutedWords, diff --git a/packages/api/src/moderation/subjects/user-list.ts b/packages/api/src/moderation/subjects/user-list.ts index f5ed15177d9..4d4eca32ce1 100644 --- a/packages/api/src/moderation/subjects/user-list.ts +++ b/packages/api/src/moderation/subjects/user-list.ts @@ -11,7 +11,10 @@ export function decideUserList( ): ModerationDecision { const acc = new ModerationDecision() - const creator = isProfile(subject.creator) ? subject.creator : undefined + const creator = + 'creator' in subject && isProfile(subject.creator) + ? subject.creator + : undefined if (creator) { acc.setDid(creator.did) diff --git a/packages/api/src/rich-text/rich-text.ts b/packages/api/src/rich-text/rich-text.ts index 2b7d0227bc0..ab855911bb7 100644 --- a/packages/api/src/rich-text/rich-text.ts +++ b/packages/api/src/rich-text/rich-text.ts @@ -122,11 +122,7 @@ export class RichTextSegment { ) {} get link(): FacetLink | undefined { - const link = this.facet?.features.find(AppBskyRichtextFacet.isLink) - if (AppBskyRichtextFacet.isLink(link)) { - return link - } - return undefined + return this.facet?.features.find(AppBskyRichtextFacet.isLink) } isLink() { @@ -134,11 +130,7 @@ export class RichTextSegment { } get mention(): FacetMention | undefined { - const mention = this.facet?.features.find(AppBskyRichtextFacet.isMention) - if (AppBskyRichtextFacet.isMention(mention)) { - return mention - } - return undefined + return this.facet?.features.find(AppBskyRichtextFacet.isMention) } isMention() { @@ -146,11 +138,7 @@ export class RichTextSegment { } get tag(): FacetTag | undefined { - const tag = this.facet?.features.find(AppBskyRichtextFacet.isTag) - if (AppBskyRichtextFacet.isTag(tag)) { - return tag - } - return undefined + return this.facet?.features.find(AppBskyRichtextFacet.isTag) } isTag() { diff --git a/packages/api/tests/atp-agent.test.ts b/packages/api/tests/atp-agent.test.ts index b2387dba3c8..d83fc41c061 100644 --- a/packages/api/tests/atp-agent.test.ts +++ b/packages/api/tests/atp-agent.test.ts @@ -12,6 +12,7 @@ import { savedFeedsToUriArrays, validateSavedFeed, } from '../src/util' +import { $Typed } from '../src/client/util' describe('agent', () => { let network: TestNetworkNoAppView @@ -2217,7 +2218,7 @@ describe('agent', () => { async function addLegacyMutedWord(mutedWord: AppBskyActorDefs.MutedWord) { await updatePreferences(agent, (prefs) => { let mutedWordsPref = prefs.findLast( - (pref) => + (pref): pref is $Typed => AppBskyActorDefs.isMutedWordsPref(pref) && AppBskyActorDefs.validateMutedWordsPref(pref).success, ) @@ -2236,18 +2237,14 @@ describe('agent', () => { } else { // if the pref doesn't exist, create it mutedWordsPref = { + $type: 'app.bsky.actor.defs#mutedWordsPref', items: [newMutedWord], } } return prefs .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p)) - .concat([ - { - ...mutedWordsPref, - $type: 'app.bsky.actor.defs#mutedWordsPref', - }, - ]) + .concat([mutedWordsPref]) }) } @@ -3246,6 +3243,7 @@ describe('agent', () => { await agent.bskyAppSetActiveProgressGuide({ guide: 'test-guide', + // @ts-expect-error unspecced field numThings: 0, }) await expect(agent.getPreferences()).resolves.toHaveProperty( @@ -3254,6 +3252,7 @@ describe('agent', () => { ) await agent.bskyAppSetActiveProgressGuide({ guide: 'test-guide', + // @ts-expect-error unspecced field numThings: 1, }) await expect(agent.getPreferences()).resolves.toHaveProperty( @@ -3332,6 +3331,7 @@ describe('agent', () => { // @ts-expect-error expect(() => agent.bskyAppUpsertNux({ name: 'a' })).rejects.toThrow() expect(() => + // @ts-expect-error agent.bskyAppUpsertNux({ id: 'a', completed: false, foo: 'bar' }), ).rejects.toThrow() }) diff --git a/packages/api/tests/moderation-prefs.test.ts b/packages/api/tests/moderation-prefs.test.ts index ab980e4d567..b332d0ee72c 100644 --- a/packages/api/tests/moderation-prefs.test.ts +++ b/packages/api/tests/moderation-prefs.test.ts @@ -1,5 +1,6 @@ import { TestNetworkNoAppView } from '@atproto/dev-env' import { DEFAULT_LABEL_SETTINGS } from '../src' +import { isContentLabelPref } from '../src/client/types/app/bsky/actor/defs' import './util/moderation-behavior' describe('agent', () => { @@ -335,7 +336,7 @@ describe('agent', () => { const a = await agent.app.bsky.actor.getPreferences({}) const nsfwSettings = a.data.preferences.filter( - (pref) => pref.label === 'nsfw', + (pref) => isContentLabelPref(pref) && pref.label === 'nsfw', ) expect(nsfwSettings.length).toEqual(1) }) diff --git a/packages/api/tests/rich-text-detection.test.ts b/packages/api/tests/rich-text-detection.test.ts index c87b5eab0a6..bd39249de18 100644 --- a/packages/api/tests/rich-text-detection.test.ts +++ b/packages/api/tests/rich-text-detection.test.ts @@ -1,5 +1,9 @@ import { AtpAgent, RichText, RichTextSegment } from '../src' -import { isTag } from '../src/client/types/app/bsky/richtext/facet' +import { + isLink, + isMention, + isTag, +} from '../src/client/types/app/bsky/richtext/facet' describe('detectFacets', () => { const agent = new AtpAgent({ service: 'http://localhost' }) @@ -374,10 +378,10 @@ function segmentToOutput(segment: RichTextSegment): string[] { return [ segment.text, segment.facet?.features.map((f) => { - if (f.did) { + if (isMention(f)) { return String(f.did) } - if (f.uri) { + if (isLink(f)) { return String(f.uri) } return undefined diff --git a/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts b/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts index 657a803bfe5..e483a5dadee 100644 --- a/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts +++ b/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts @@ -148,5 +148,5 @@ type Params = QueryParams & { type Skeleton = { dids: string[] cursor?: string - resHeaders?: Record + resHeaders?: Record } diff --git a/packages/bsky/src/api/app/bsky/feed/getFeed.ts b/packages/bsky/src/api/app/bsky/feed/getFeed.ts index 8a89d956582..cea76769d61 100644 --- a/packages/bsky/src/api/app/bsky/feed/getFeed.ts +++ b/packages/bsky/src/api/app/bsky/feed/getFeed.ts @@ -8,6 +8,7 @@ import { import { ResponseType, XRPCError } from '@atproto/xrpc' import { AtpAgent, AppBskyFeedGetFeedSkeleton } from '@atproto/api' import { noUndefinedVals } from '@atproto/common' +import { isSkeletonReasonRepost } from '../../../../lexicon/types/app/bsky/feed/defs' import { QueryParams as GetFeedParams } from '../../../../lexicon/types/app/bsky/feed/getFeed' import { OutputSchema as SkeletonOutput } from '../../../../lexicon/types/app/bsky/feed/getFeedSkeleton' import { Server } from '../../../../lexicon' @@ -42,10 +43,8 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.authVerifier.standardOptionalParameterized({ lxmCheck: (method) => { return ( - method !== undefined && - [ids.AppBskyFeedGetFeedSkeleton, ids.AppBskyFeedGetFeed].includes( - method, - ) + method === ids.AppBskyFeedGetFeedSkeleton || + method === ids.AppBskyFeedGetFeed ) }, skipAudCheck: true, @@ -254,10 +253,9 @@ const skeletonFromFeedGen = async ( const { feed: feedSkele, ...skele } = skeleton const feedItems = feedSkele.slice(0, params.limit).map((item) => ({ post: { uri: item.post }, - repost: - typeof item.reason?.repost === 'string' - ? { uri: item.reason.repost } - : undefined, + repost: isSkeletonReasonRepost(item.reason) + ? { uri: item.reason.repost } + : undefined, feedContext: item.feedContext, })) diff --git a/packages/bsky/src/api/app/bsky/labeler/getServices.ts b/packages/bsky/src/api/app/bsky/labeler/getServices.ts index f5df1cbf5ca..2c1c33a9e79 100644 --- a/packages/bsky/src/api/app/bsky/labeler/getServices.ts +++ b/packages/bsky/src/api/app/bsky/labeler/getServices.ts @@ -21,15 +21,15 @@ export default function (server: Server, ctx: AppContext) { const view = ctx.views.labelerDetailed(did, hydration) if (!view) return return { - $type: 'app.bsky.labeler.defs#labelerViewDetailed', ...view, + $type: 'app.bsky.labeler.defs#labelerViewDetailed', } } else { const view = ctx.views.labeler(did, hydration) if (!view) return return { - $type: 'app.bsky.labeler.defs#labelerView', ...view, + $type: 'app.bsky.labeler.defs#labelerView', } } }) diff --git a/packages/bsky/src/api/app/bsky/notification/listNotifications.ts b/packages/bsky/src/api/app/bsky/notification/listNotifications.ts index 8f4fa2f073e..3b834ccc7b9 100644 --- a/packages/bsky/src/api/app/bsky/notification/listNotifications.ts +++ b/packages/bsky/src/api/app/bsky/notification/listNotifications.ts @@ -2,7 +2,7 @@ import { InvalidRequestError } from '@atproto/xrpc-server' import { mapDefined } from '@atproto/common' import { Server } from '../../../../lexicon' import { QueryParams } from '../../../../lexicon/types/app/bsky/notification/listNotifications' -import { isRecord as isPostRecord } from '../../../../lexicon/types/app/bsky/feed/post' +import { isValidRecord as isValidPostRecord } from '../../../../lexicon/types/app/bsky/feed/post' import AppContext from '../../../../context' import { createPipeline, @@ -157,7 +157,7 @@ const noBlockOrMutesOrNeedsReview = ( if (item.reason === 'reply') { const post = hydration.posts?.get(item.uri) if (post) { - const rootPostUri = isPostRecord(post.record) + const rootPostUri = isValidPostRecord(post.record) ? post.record.reply?.root.uri : undefined const isRootPostByViewer = diff --git a/packages/bsky/src/data-plane/server/indexing/plugins/post.ts b/packages/bsky/src/data-plane/server/indexing/plugins/post.ts index eb495fcb13f..5e110a6a295 100644 --- a/packages/bsky/src/data-plane/server/indexing/plugins/post.ts +++ b/packages/bsky/src/data-plane/server/indexing/plugins/post.ts @@ -8,9 +8,9 @@ import { } from '../../../../lexicon/types/app/bsky/feed/post' import { Record as GateRecord } from '../../../../lexicon/types/app/bsky/feed/threadgate' import { Record as PostgateRecord } from '../../../../lexicon/types/app/bsky/feed/postgate' -import { isMain as isEmbedImage } from '../../../../lexicon/types/app/bsky/embed/images' -import { isMain as isEmbedExternal } from '../../../../lexicon/types/app/bsky/embed/external' -import { isMain as isEmbedRecord } from '../../../../lexicon/types/app/bsky/embed/record' +import { isValidMain as isValidEmbedImage } from '../../../../lexicon/types/app/bsky/embed/images' +import { isValidMain as isValidEmbedExternal } from '../../../../lexicon/types/app/bsky/embed/external' +import { isValidMain as isValidEmbedRecord } from '../../../../lexicon/types/app/bsky/embed/record' import { isMain as isEmbedRecordWithMedia } from '../../../../lexicon/types/app/bsky/embed/recordWithMedia' import { isMention, @@ -152,7 +152,7 @@ const insertFn = async ( const embeds: (PostEmbedImage[] | PostEmbedExternal | PostEmbedRecord)[] = [] const postEmbeds = separateEmbeds(obj.embed) for (const postEmbed of postEmbeds) { - if (isEmbedImage(postEmbed)) { + if (isValidEmbedImage(postEmbed)) { const { images } = postEmbed const imagesEmbed = images.map((img, i) => ({ postUri: uri.toString(), @@ -162,7 +162,7 @@ const insertFn = async ( })) embeds.push(imagesEmbed) await db.insertInto('post_embed_image').values(imagesEmbed).execute() - } else if (isEmbedExternal(postEmbed)) { + } else if (isValidEmbedExternal(postEmbed)) { const { external } = postEmbed const externalEmbed = { postUri: uri.toString(), @@ -173,7 +173,7 @@ const insertFn = async ( } embeds.push(externalEmbed) await db.insertInto('post_embed_external').values(externalEmbed).execute() - } else if (isEmbedRecord(postEmbed)) { + } else if (isValidEmbedRecord(postEmbed)) { const { record } = postEmbed const embedUri = new AtUri(record.uri) const recordEmbed = { diff --git a/packages/bsky/src/hydration/graph.ts b/packages/bsky/src/hydration/graph.ts index 6556af42db0..b78c2aa9b9c 100644 --- a/packages/bsky/src/hydration/graph.ts +++ b/packages/bsky/src/hydration/graph.ts @@ -176,7 +176,10 @@ export class GraphHydrator { }, new HydrationMap()) } - async getBlocks(uris: string[], includeTakedowns = false): Promise { + async getBlocks( + uris: string[], + includeTakedowns = false, + ): Promise> { if (!uris.length) return new HydrationMap() const res = await this.dataplane.getBlockRecords({ uris }) return uris.reduce((acc, uri, i) => { diff --git a/packages/bsky/src/hydration/util.ts b/packages/bsky/src/hydration/util.ts index 7fe3b0269d7..88b386d07a5 100644 --- a/packages/bsky/src/hydration/util.ts +++ b/packages/bsky/src/hydration/util.ts @@ -18,7 +18,9 @@ export interface Merges { merge(map: T): this } -export type RecordInfo = { +export type UnknownRecord = { $type?: string; [x: string]: unknown } + +export type RecordInfo = { record: T cid: string sortedAt: Date @@ -56,7 +58,7 @@ export const mergeManyMaps = (...maps: HydrationMap[]) => { export type ItemRef = { uri: string; cid?: string } -export const parseRecord = ( +export const parseRecord = ( entry: Record, includeTakedowns: boolean, ): RecordInfo | undefined => { diff --git a/packages/bsky/src/views/index.ts b/packages/bsky/src/views/index.ts index 7f61ec92a6b..aa26a460f85 100644 --- a/packages/bsky/src/views/index.ts +++ b/packages/bsky/src/views/index.ts @@ -8,6 +8,7 @@ import { ProfileView, ProfileViewBasic, ViewerState as ProfileViewerState, + KnownFollowers, } from '../lexicon/types/app/bsky/actor/defs' import { BlockedPost, @@ -22,7 +23,7 @@ import { ThreadgateView, isPostView, } from '../lexicon/types/app/bsky/feed/defs' -import { isRecord as isPostRecord } from '../lexicon/types/app/bsky/feed/post' +import { isValidRecord as isValidPostRecord } from '../lexicon/types/app/bsky/feed/post' import { ListView, ListViewBasic, @@ -37,7 +38,7 @@ import { } from './util' import { uriToDid as creatorFromUri, safePinnedPost } from '../util/uris' import { isListRule } from '../lexicon/types/app/bsky/feed/threadgate' -import { isSelfLabels } from '../lexicon/types/com/atproto/label/defs' +import { isValidSelfLabels } from '../lexicon/types/com/atproto/label/defs' import { Embed, EmbedBlocked, @@ -73,6 +74,7 @@ import { } from '../lexicon/types/app/bsky/labeler/defs' import { Notification } from '../proto/bsky_pb' import { postUriToThreadgateUri, postUriToPostgateUri } from '../util/uris' +import { $Typed } from '../lexicon/util' export class Views { public imgUriBuilder: ImageUriBuilder = this.opts.imgUriBuilder @@ -147,7 +149,7 @@ export class Views { profileDetailed( did: string, state: HydrationState, - ): ProfileViewDetailed | undefined { + ): Omit | undefined { const actor = state.actors?.get(did) if (!actor) return const baseView = this.profile(did, state) @@ -189,7 +191,10 @@ export class Views { } } - profile(did: string, state: HydrationState): ProfileView | undefined { + profile( + did: string, + state: HydrationState, + ): Omit | undefined { const actor = state.actors?.get(did) if (!actor) return const basicView = this.profileBasic(did, state) @@ -210,7 +215,7 @@ export class Views { profileBasic( did: string, state: HydrationState, - ): ProfileViewBasic | undefined { + ): Omit | undefined { const actor = state.actors?.get(did) if (!actor) return const profileUri = AtUri.make( @@ -300,7 +305,10 @@ export class Views { } } - knownFollowers(did: string, state: HydrationState) { + knownFollowers( + did: string, + state: HydrationState, + ): Omit | undefined { const knownFollowers = state.knownFollowers?.get(did) if (!knownFollowers) return const blocks = state.bidirectionalBlocks?.get(did) @@ -337,7 +345,10 @@ export class Views { // Graph // ------------ - list(uri: string, state: HydrationState): ListView | undefined { + list( + uri: string, + state: HydrationState, + ): Omit | undefined { const creatorDid = creatorFromUri(uri) const list = state.lists?.get(uri) if (!list) return @@ -355,7 +366,10 @@ export class Views { } } - listBasic(uri: string, state: HydrationState): ListViewBasic | undefined { + listBasic( + uri: string, + state: HydrationState, + ): Omit | undefined { const list = state.lists?.get(uri) if (!list) { return undefined @@ -391,7 +405,7 @@ export class Views { starterPackBasic( uri: string, state: HydrationState, - ): StarterPackViewBasic | undefined { + ): Omit | undefined { const sp = state.starterPacks?.get(uri) if (!sp) return const parsedUri = new AtUri(uri) @@ -411,7 +425,10 @@ export class Views { } } - starterPack(uri: string, state: HydrationState): StarterPackView | undefined { + starterPack( + uri: string, + state: HydrationState, + ): Omit | undefined { const sp = state.starterPacks?.get(uri) const basicView = this.starterPackBasic(uri, state) if (!sp || !basicView) return @@ -445,7 +462,7 @@ export class Views { }): Label[] { const { uri, cid, record } = details if (!uri || !cid || !record) return [] - if (!isSelfLabels(record.labels)) return [] + if (!isValidSelfLabels(record.labels)) return [] const src = creatorFromUri(uri) // record creator const cts = typeof record.createdAt === 'string' @@ -456,7 +473,10 @@ export class Views { }) } - labeler(did: string, state: HydrationState): LabelerView | undefined { + labeler( + did: string, + state: HydrationState, + ): Omit | undefined { const labeler = state.labelers?.get(did) if (!labeler) return const creator = this.profile(did, state) @@ -492,7 +512,7 @@ export class Views { labelerDetailed( did: string, state: HydrationState, - ): LabelerViewDetailed | undefined { + ): Omit | undefined { const baseView = this.labeler(did, state) if (!baseView) return const record = state.labelers?.get(did) @@ -540,7 +560,10 @@ export class Views { } } - feedGenerator(uri: string, state: HydrationState): GeneratorView | undefined { + feedGenerator( + uri: string, + state: HydrationState, + ): Omit | undefined { const feedgen = state.feedgens?.get(uri) if (!feedgen) return const creatorDid = creatorFromUri(uri) @@ -576,7 +599,10 @@ export class Views { } } - threadgate(uri: string, state: HydrationState): ThreadgateView | undefined { + threadgate( + uri: string, + state: HydrationState, + ): Omit | undefined { const gate = state.threadgates?.get(uri) if (!gate) return return { @@ -590,7 +616,11 @@ export class Views { } } - post(uri: string, state: HydrationState, depth = 0): PostView | undefined { + post( + uri: string, + state: HydrationState, + depth = 0, + ): Omit | undefined { const post = state.posts?.get(uri) if (!post) return const parsedUri = new AtUri(uri) @@ -642,9 +672,9 @@ export class Views { feedViewPost( item: FeedItem, state: HydrationState, - ): FeedViewPost | undefined { + ): Omit | undefined { const postInfo = state.posts?.get(item.post.uri) - let reason: ReasonRepost | ReasonPin | undefined + let reason: $Typed | $Typed | undefined if (item.authorPinned) { reason = this.reasonPin() } else if (item.repost) { @@ -666,7 +696,10 @@ export class Views { } } - replyRef(uri: string, state: HydrationState): ReplyRef | undefined { + replyRef( + uri: string, + state: HydrationState, + ): Omit | undefined { const postRecord = state.posts?.get(uri.toString())?.record if (!postRecord?.reply) return let root = this.maybePost(postRecord.reply.root.uri, state) @@ -684,7 +717,11 @@ export class Views { } } let grandparentAuthor: ProfileViewBasic | undefined - if (isPostRecord(parent.record) && parent.record.reply) { + if ( + isPostView(parent) && + isValidPostRecord(parent.record) && + parent.record.reply + ) { grandparentAuthor = this.profileBasic( creatorFromUri(parent.record.reply.parent.uri), state, @@ -697,7 +734,7 @@ export class Views { } } - maybePost(uri: string, state: HydrationState): MaybePostView { + maybePost(uri: string, state: HydrationState): $Typed { const post = this.post(uri, state) if (!post) { return this.notFoundPost(uri) @@ -706,8 +743,8 @@ export class Views { return this.blockedPost(uri, post.author.did, state) } return { - $type: 'app.bsky.feed.defs#postView', ...post, + $type: 'app.bsky.feed.defs#postView', } } @@ -715,7 +752,7 @@ export class Views { uri: string, authorDid: string, state: HydrationState, - ): BlockedPost { + ): $Typed { return { $type: 'app.bsky.feed.defs#blockedPost', uri, @@ -727,7 +764,7 @@ export class Views { } } - notFoundPost(uri: string): NotFoundPost { + notFoundPost(uri: string): $Typed { return { $type: 'app.bsky.feed.defs#notFoundPost', uri, @@ -739,7 +776,7 @@ export class Views { creatorDid: string, repost: Repost, state: HydrationState, - ): ReasonRepost | undefined { + ): $Typed | undefined { const creator = this.profileBasic(creatorDid, state) if (!creator) return return { @@ -749,7 +786,7 @@ export class Views { } } - reasonPin() { + reasonPin(): $Typed { return { $type: 'app.bsky.feed.defs#reasonPin', } @@ -762,7 +799,7 @@ export class Views { skele: { anchor: string; uris: string[] }, state: HydrationState, opts: { height: number; depth: number }, - ): ThreadViewPost | NotFoundPost | BlockedPost { + ): $Typed | $Typed | $Typed { const { anchor, uris } = skele const post = this.post(anchor, state) const postInfo = state.posts?.get(anchor) @@ -807,7 +844,11 @@ export class Views { rootUri: string, state: HydrationState, height: number, - ): ThreadViewPost | NotFoundPost | BlockedPost | undefined { + ): + | $Typed + | $Typed + | $Typed + | undefined { if (height < 1) return undefined const parentUri = state.posts?.get(childUri)?.record.reply?.parent.uri if (!parentUri) return undefined @@ -837,7 +878,7 @@ export class Views { childrenByParentUri: Record, state: HydrationState, depth: number, - ): (ThreadViewPost | BlockedPost)[] | undefined { + ): ($Typed | $Typed)[] | undefined { if (depth < 1) return undefined const childrenUris = childrenByParentUri[parentUri] ?? [] return mapDefined(childrenUris, (uri) => { @@ -884,7 +925,7 @@ export class Views { embed: Embed | { $type: string }, state: HydrationState, depth: number, - ): EmbedView | undefined { + ): $Typed | undefined { if (isImagesEmbed(embed)) { return this.imagesEmbed(creatorFromUri(postUri), embed) } else if (isVideoEmbed(embed)) { @@ -900,7 +941,7 @@ export class Views { } } - imagesEmbed(did: string, embed: ImagesEmbed): ImagesEmbedView { + imagesEmbed(did: string, embed: ImagesEmbed): $Typed { const imgViews = embed.images.map((img) => ({ thumb: this.imgUriBuilder.getPresetUri( 'feed_thumbnail', @@ -921,7 +962,7 @@ export class Views { } } - videoEmbed(did: string, embed: VideoEmbed): VideoEmbedView { + videoEmbed(did: string, embed: VideoEmbed): $Typed { const cid = cidFromBlobJson(embed.video) return { $type: 'app.bsky.embed.video#view', @@ -933,7 +974,7 @@ export class Views { } } - externalEmbed(did: string, embed: ExternalEmbed): ExternalEmbedView { + externalEmbed(did: string, embed: ExternalEmbed): $Typed { const { uri, title, description, thumb } = embed.external return { $type: 'app.bsky.embed.external#view', @@ -952,7 +993,10 @@ export class Views { } } - embedNotFound(uri: string): { $type: string; record: EmbedNotFound } { + embedNotFound(uri: string): { + $type: 'app.bsky.embed.record#view' + record: $Typed + } { return { $type: 'app.bsky.embed.record#view', record: { @@ -963,7 +1007,10 @@ export class Views { } } - embedDetached(uri: string): { $type: string; record: EmbedDetached } { + embedDetached(uri: string): { + $type: 'app.bsky.embed.record#view' + record: $Typed + } { return { $type: 'app.bsky.embed.record#view', record: { @@ -977,7 +1024,10 @@ export class Views { embedBlocked( uri: string, state: HydrationState, - ): { $type: string; record: EmbedBlocked } { + ): { + $type: 'app.bsky.embed.record#view' + record: $Typed + } { const creator = creatorFromUri(uri) return { $type: 'app.bsky.embed.record#view', @@ -997,7 +1047,7 @@ export class Views { uri: string, state: HydrationState, depth: number, - ): PostEmbedView | undefined { + ): $Typed | undefined { const postView = this.post(uri, state, depth) if (!postView) return return { @@ -1016,6 +1066,20 @@ export class Views { } } + recordEmbed( + postUri: string, + embed: RecordEmbed, + state: HydrationState, + depth: number, + withTypeTag: false, + ): RecordEmbedView + recordEmbed( + postUri: string, + embed: RecordEmbed, + state: HydrationState, + depth: number, + withTypeTag?: true, + ): $Typed recordEmbed( postUri: string, embed: RecordEmbed, @@ -1049,35 +1113,43 @@ export class Views { } else if (parsedUri.collection === ids.AppBskyFeedGenerator) { const view = this.feedGenerator(uri, state) if (!view) return this.embedNotFound(uri) - view.$type = 'app.bsky.feed.defs#generatorView' - return this.recordEmbedWrapper(view, withTypeTag) + return this.recordEmbedWrapper( + { ...view, $type: 'app.bsky.feed.defs#generatorView' }, + withTypeTag, + ) } else if (parsedUri.collection === ids.AppBskyGraphList) { const view = this.list(uri, state) if (!view) return this.embedNotFound(uri) - view.$type = 'app.bsky.graph.defs#listView' - return this.recordEmbedWrapper(view, withTypeTag) + return this.recordEmbedWrapper( + { ...view, $type: 'app.bsky.graph.defs#listView' }, + withTypeTag, + ) } else if (parsedUri.collection === ids.AppBskyLabelerService) { const view = this.labeler(parsedUri.hostname, state) if (!view) return this.embedNotFound(uri) - view.$type = 'app.bsky.labeler.defs#labelerView' - return this.recordEmbedWrapper(view, withTypeTag) + return this.recordEmbedWrapper( + { ...view, $type: 'app.bsky.labeler.defs#labelerView' }, + withTypeTag, + ) } else if (parsedUri.collection === ids.AppBskyGraphStarterpack) { const view = this.starterPackBasic(uri, state) if (!view) return this.embedNotFound(uri) - view.$type = 'app.bsky.graph.defs#starterPackViewBasic' - return this.recordEmbedWrapper(view, withTypeTag) + return this.recordEmbedWrapper( + { ...view, $type: 'app.bsky.graph.defs#starterPackViewBasic' }, + withTypeTag, + ) } return this.embedNotFound(uri) } - private recordEmbedWrapper( - record: RecordEmbedViewInternal, + private recordEmbedWrapper>( + record: T, withTypeTag: boolean, - ): RecordEmbedView { + ) { return { - $type: withTypeTag ? 'app.bsky.embed.record#view' : undefined, + $type: withTypeTag ? ('app.bsky.embed.record#view' as const) : undefined, record, - } + } satisfies RecordEmbedView } recordWithMediaEmbed( @@ -1085,9 +1157,12 @@ export class Views { embed: RecordWithMedia, state: HydrationState, depth: number, - ): RecordWithMediaView | undefined { + ): $Typed | undefined { const creator = creatorFromUri(postUri) - let mediaEmbed: ImagesEmbedView | VideoEmbedView | ExternalEmbedView + let mediaEmbed: + | $Typed + | $Typed + | $Typed if (isImagesEmbed(embed.media)) { mediaEmbed = this.imagesEmbed(creator, embed.media) } else if (isVideoEmbed(embed.media)) { @@ -1176,7 +1251,7 @@ export class Views { notif: Notification, lastSeenAt: string | undefined, state: HydrationState, - ): NotificationView | undefined { + ): Omit | undefined { if (!notif.timestamp || !notif.reason) return const uri = new AtUri(notif.uri) const authorDid = uri.hostname diff --git a/packages/bsky/src/views/types.ts b/packages/bsky/src/views/types.ts index 3470a857207..98b6b37e7b3 100644 --- a/packages/bsky/src/views/types.ts +++ b/packages/bsky/src/views/types.ts @@ -25,7 +25,10 @@ import { NotFoundPost, PostView, } from '../lexicon/types/app/bsky/feed/defs' -import { ListView } from '../lexicon/types/app/bsky/graph/defs' +import { + ListView, + StarterPackViewBasic, +} from '../lexicon/types/app/bsky/graph/defs' import { LabelerView } from '../lexicon/types/app/bsky/labeler/defs' export type { @@ -89,3 +92,4 @@ export type RecordEmbedViewInternal = | GeneratorView | ListView | LabelerView + | StarterPackViewBasic diff --git a/packages/bsky/tests/_util.ts b/packages/bsky/tests/_util.ts index 75c83f1a6d7..f86c9eb558a 100644 --- a/packages/bsky/tests/_util.ts +++ b/packages/bsky/tests/_util.ts @@ -5,15 +5,19 @@ import { FeedViewPost, PostView, isPostView, + isReasonRepost, isThreadViewPost, } from '../src/lexicon/types/app/bsky/feed/defs' import { isViewRecord } from '../src/lexicon/types/app/bsky/embed/record' +import { isView as isRecordView } from '../src/lexicon/types/app/bsky/embed/record' +import { isView as isRecordWithMediaView } from '../src/lexicon/types/app/bsky/embed/recordWithMedia' import { AppBskyFeedGetPostThread } from '@atproto/api' import { LabelerView, isLabelerView, isLabelerViewDetailed, } from '../src/lexicon/types/app/bsky/labeler/defs' +import { $Typed } from '../src/lexicon/util' type ThreadViewPost = Extract< AppBskyFeedGetPostThread.OutputSchema['thread'], @@ -108,8 +112,10 @@ export const forSnapshot = (obj: unknown) => { export const getOriginator = (item: FeedViewPost) => { if (!item.reason) { return item.post.author.did + } else if (isReasonRepost(item.reason)) { + return item.reason.by.did } else { - return (item.reason.by as { [did: string]: string }).did + throw new Error('Unexpected reason') } } @@ -119,8 +125,11 @@ export const getOriginator = (item: FeedViewPost) => { // to this: // [{ uri: '0'}, { uri: '1' }, { uri: '0'}] const kTake = Symbol('take') -export function take(obj, value: string): string -export function take(obj, value: string | undefined): string | undefined +export function take(obj: Record, value: string): string +export function take( + obj: Record, + value?: string, +): string | undefined export function take( obj: { [s: string]: number; [kTake]?: string }, value: string | undefined, @@ -140,7 +149,10 @@ export function take( export const constantDate = new Date(0).toISOString() export const constantKeysetCursor = '0000000000000__bafycid' -const mapLeafValues = (obj: unknown, fn: (val: unknown) => unknown) => { +const mapLeafValues = ( + obj: unknown, + fn: (val: unknown) => unknown, +): unknown => { if (Array.isArray(obj)) { return obj.map((item) => mapLeafValues(item, fn)) } @@ -169,33 +181,57 @@ export const paginateAll = async ( } // @NOTE mutates -export const stripViewer = }>( +export const stripViewer = ( val: T, -): T => { +): Omit => { delete val.viewer return val } // @NOTE mutates -export const stripViewerFromPost = (postUnknown: unknown): PostView => { - if (postUnknown?.['$type'] && !isPostView(postUnknown)) { +export function stripViewerFromPost( + postUnknown: object, + withType?: false, +): PostView +export function stripViewerFromPost( + postUnknown: object, + withType: true, +): $Typed +export function stripViewerFromPost( + postUnknown: object, + withType = false, +): PostView { + if ('$type' in postUnknown && !isPostView(postUnknown)) { throw new Error('Expected post view') } const post = postUnknown as PostView + if (withType) { + post.$type = 'app.bsky.feed.defs#postView' + } else { + delete post.$type + } post.author = stripViewer(post.author) - const recordEmbed = - post.embed && isViewRecord(post.embed.record) - ? post.embed.record // Record from record embed - : post.embed?.['record'] && isViewRecord(post.embed['record']['record']) - ? post.embed['record']['record'] // Record from record-with-media embed + const recordEmbed = isRecordView(post.embed) + ? isViewRecord(post.embed.record) + ? post.embed.record + : undefined + : isRecordWithMediaView(post.embed) + ? isViewRecord(post.embed.record.record) + ? post.embed.record.record : undefined + : undefined + if (recordEmbed) { recordEmbed.author = stripViewer(recordEmbed.author) recordEmbed.embeds?.forEach((deepEmbed) => { - const deepRecordEmbed = isViewRecord(deepEmbed.record) - ? deepEmbed.record // Record from record embed - : deepEmbed['record'] && isViewRecord(deepEmbed['record']['record']) - ? deepEmbed['record']['record'] // Record from record-with-media embed + const deepRecordEmbed = isRecordView(deepEmbed) + ? isViewRecord(deepEmbed.record) + ? deepEmbed.record + : undefined + : isRecordWithMediaView(deepEmbed) + ? isViewRecord(deepEmbed.record.record) + ? deepEmbed.record.record + : undefined : undefined if (deepRecordEmbed) { deepRecordEmbed.author = stripViewer(deepRecordEmbed.author) @@ -208,6 +244,7 @@ export const stripViewerFromPost = (postUnknown: unknown): PostView => { // @NOTE mutates export const stripViewerFromThread = (thread: T): T => { if (!isThreadViewPost(thread)) return thread + // @ts-expect-error delete thread.viewer thread.post = stripViewerFromPost(thread.post) if (isThreadViewPost(thread.parent)) { @@ -220,11 +257,9 @@ export const stripViewerFromThread = (thread: T): T => { } // @NOTE mutates -export const stripViewerFromLabeler = ( - serviceUnknown: unknown, -): LabelerView => { +export const stripViewerFromLabeler = (serviceUnknown: object): LabelerView => { if ( - serviceUnknown?.['$type'] && + '$type' in serviceUnknown && !isLabelerView(serviceUnknown) && !isLabelerViewDetailed(serviceUnknown) ) { diff --git a/packages/bsky/tests/admin/admin-auth.test.ts b/packages/bsky/tests/admin/admin-auth.test.ts index 027ff49eb1e..54e0568a8a4 100644 --- a/packages/bsky/tests/admin/admin-auth.test.ts +++ b/packages/bsky/tests/admin/admin-auth.test.ts @@ -2,15 +2,20 @@ import { SeedClient, usersSeed, TestNetwork } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' import { Secp256k1Keypair } from '@atproto/crypto' import { createServiceAuthHeaders } from '@atproto/xrpc-server' -import { RepoRef } from '../../src/lexicon/types/com/atproto/admin/defs' +import { + isRepoRef, + RepoRef, +} from '../../src/lexicon/types/com/atproto/admin/defs' import { ids } from '../../src/lexicon/lexicons' +import { $Typed } from '../../src/lexicon/util' +import assert from 'node:assert' describe('admin auth', () => { let network: TestNetwork let agent: AtpAgent let sc: SeedClient - let repoSubject: RepoRef + let repoSubject: $Typed const modServiceDid = 'did:example:mod' const altModDid = 'did:example:alt' @@ -96,6 +101,7 @@ describe('admin auth', () => { { did: repoSubject.did }, getHeaders, ) + assert(isRepoRef(res.data.subject)) expect(res.data.subject.did).toBe(repoSubject.did) expect(res.data.takedown?.applied).toBe(true) }) diff --git a/packages/bsky/tests/admin/moderation.test.ts b/packages/bsky/tests/admin/moderation.test.ts index 7bb1c551217..10fe78595b1 100644 --- a/packages/bsky/tests/admin/moderation.test.ts +++ b/packages/bsky/tests/admin/moderation.test.ts @@ -1,19 +1,26 @@ import { ImageRef, SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env' import { AtpAgent } from '@atproto/api' import { + isRepoBlobRef, + isRepoRef, RepoBlobRef, RepoRef, } from '../../src/lexicon/types/com/atproto/admin/defs' -import { Main as StrongRef } from '../../src/lexicon/types/com/atproto/repo/strongRef' +import { + Main as StrongRef, + isMain as isStrongRef, +} from '../../src/lexicon/types/com/atproto/repo/strongRef' +import { $Typed } from '../../src/lexicon/util' +import assert from 'node:assert' describe('moderation', () => { let network: TestNetwork let agent: AtpAgent let sc: SeedClient - let repoSubject: RepoRef - let recordSubject: StrongRef - let blobSubject: RepoBlobRef + let repoSubject: $Typed + let recordSubject: $Typed + let blobSubject: $Typed let blobRef: ImageRef beforeAll(async () => { @@ -64,6 +71,7 @@ describe('moderation', () => { }, { headers: network.bsky.adminAuthHeaders() }, ) + assert(isRepoRef(res.data.subject)) expect(res.data.subject.did).toEqual(sc.dids.bob) expect(res.data.takedown?.applied).toBe(true) // expect(res.data.takedown?.ref).toBe('test-repo') @TODO add these checks back in once takedown refs make it into dataplane @@ -86,6 +94,7 @@ describe('moderation', () => { }, { headers: network.bsky.adminAuthHeaders() }, ) + assert(isRepoRef(res.data.subject)) expect(res.data.subject.did).toEqual(sc.dids.bob) expect(res.data.takedown?.applied).toBe(false) expect(res.data.takedown?.ref).toBeUndefined() @@ -108,6 +117,7 @@ describe('moderation', () => { }, { headers: network.bsky.adminAuthHeaders() }, ) + assert(isStrongRef(res.data.subject)) expect(res.data.subject.uri).toEqual(recordSubject.uri) expect(res.data.takedown?.applied).toBe(true) // expect(res.data.takedown?.ref).toBe('test-record') @@ -130,6 +140,7 @@ describe('moderation', () => { }, { headers: network.bsky.adminAuthHeaders() }, ) + assert(isStrongRef(res.data.subject)) expect(res.data.subject.uri).toEqual(recordSubject.uri) expect(res.data.takedown?.applied).toBe(false) expect(res.data.takedown?.ref).toBeUndefined() @@ -168,6 +179,7 @@ describe('moderation', () => { }, { headers: network.bsky.adminAuthHeaders() }, ) + assert(isRepoBlobRef(res.data.subject)) expect(res.data.subject.did).toEqual(blobSubject.did) expect(res.data.subject.cid).toEqual(blobSubject.cid) expect(res.data.takedown?.applied).toBe(true) diff --git a/packages/bsky/tests/views/author-feed.test.ts b/packages/bsky/tests/views/author-feed.test.ts index 23f4f0072fe..09e53f2dac6 100644 --- a/packages/bsky/tests/views/author-feed.test.ts +++ b/packages/bsky/tests/views/author-feed.test.ts @@ -179,8 +179,8 @@ describe('pds author feed views', () => { } if (item.reply) { result.reply = { - parent: stripViewerFromPost(item.reply.parent), - root: stripViewerFromPost(item.reply.root), + parent: stripViewerFromPost(item.reply.parent, true), + root: stripViewerFromPost(item.reply.root, true), grandparentAuthor: item.reply.grandparentAuthor && stripViewer(item.reply.grandparentAuthor), diff --git a/packages/bsky/tests/views/blocks.test.ts b/packages/bsky/tests/views/blocks.test.ts index 848b8f89c20..ab841f3b4d3 100644 --- a/packages/bsky/tests/views/blocks.test.ts +++ b/packages/bsky/tests/views/blocks.test.ts @@ -3,6 +3,8 @@ import { TestNetwork, RecordRef, SeedClient, basicSeed } from '@atproto/dev-env' import { AtpAgent, AtUri } from '@atproto/api' import { assertIsThreadViewPost, forSnapshot } from '../_util' import { ids } from '../../src/lexicon/lexicons' +import { isPostView } from '../../src/lexicon/types/app/bsky/feed/defs' +import { isView as isRecordEmbedView } from '../../src/lexicon/types/app/bsky/embed/record' describe('pds views with blocking', () => { let network: TestNetwork @@ -220,7 +222,8 @@ describe('pds views with blocking', () => { resCarol.data.feed.some( (post) => post.post.author.did === dan || - post.reply?.parent.author?.['did'] === dan || + (isPostView(post.reply?.parent) && + post.reply.parent.author.did === dan) || post.reply?.grandparentAuthor?.did === dan, ), ).toBeFalsy() @@ -235,7 +238,8 @@ describe('pds views with blocking', () => { resDan.data.feed.some( (post) => post.post.author.did === carol || - post.reply?.parent.author?.['did'] === carol || + (isPostView(post.reply?.parent) && + post.reply.parent.author.did === carol) || post.reply?.grandparentAuthor?.did === carol, ), ).toBeFalsy() @@ -658,7 +662,8 @@ describe('pds views with blocking', () => { assertIsThreadViewPost(embedThenBlock.thread) - expect(embedThenBlock.thread.post.embed?.record).toMatchObject({ + assert(isRecordEmbedView(embedThenBlock.thread.post.embed)) + expect(embedThenBlock.thread.post.embed.record).toMatchObject({ $type: 'app.bsky.embed.record#viewBlocked', }) @@ -680,7 +685,8 @@ describe('pds views with blocking', () => { assertIsThreadViewPost(unblock.thread) - expect(unblock.thread.post?.embed?.record).toMatchObject({ + assert(isRecordEmbedView(unblock.thread.post?.embed)) + expect(unblock.thread.post?.embed.record).toMatchObject({ $type: 'app.bsky.embed.record#viewRecord', }) @@ -709,8 +715,8 @@ describe('pds views with blocking', () => { }, ) assertIsThreadViewPost(blockThenEmbed.thread) - - expect(blockThenEmbed.thread.post.embed?.record).toMatchObject({ + assert(isRecordEmbedView(blockThenEmbed.thread.post.embed)) + expect(blockThenEmbed.thread.post.embed.record).toMatchObject({ $type: 'app.bsky.embed.record#viewBlocked', }) @@ -753,7 +759,8 @@ describe('pds views with blocking', () => { (item) => item.post.uri === embedBlockedUri, ) assert(embedBlockedPost) - expect(embedBlockedPost.post.embed?.record).toMatchObject({ + assert(isRecordEmbedView(embedBlockedPost.post.embed)) + expect(embedBlockedPost.post.embed.record).toMatchObject({ $type: 'app.bsky.embed.record#viewBlocked', }) }) diff --git a/packages/bsky/tests/views/labeler-service.test.ts b/packages/bsky/tests/views/labeler-service.test.ts index a979667aa6d..1f501331066 100644 --- a/packages/bsky/tests/views/labeler-service.test.ts +++ b/packages/bsky/tests/views/labeler-service.test.ts @@ -2,6 +2,8 @@ import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed, RecordRef } from '@atproto/dev-env' import { forSnapshot, stripViewerFromLabeler } from '../_util' import { ids } from '../../src/lexicon/lexicons' +import { isView as isRecordEmbedView } from '../../src/lexicon/types/app/bsky/embed/record' +import assert from 'node:assert' describe('labeler service views', () => { let network: TestNetwork @@ -150,7 +152,8 @@ describe('labeler service views', () => { const serviceViews = await agent.api.app.bsky.labeler.getServices({ dids: [alice], }) - expect(postViews.data.posts[0].embed?.record).toMatchObject( + assert(isRecordEmbedView(postViews.data.posts[0].embed)) + expect(postViews.data.posts[0].embed.record).toMatchObject( serviceViews.data.views[0], ) }) diff --git a/packages/bsky/tests/views/list-feed.test.ts b/packages/bsky/tests/views/list-feed.test.ts index d0b0302d69d..0c2f692c815 100644 --- a/packages/bsky/tests/views/list-feed.test.ts +++ b/packages/bsky/tests/views/list-feed.test.ts @@ -118,11 +118,14 @@ describe('list feed views', () => { } if (item.reply) { result.reply = { - parent: stripViewerFromPost(item.reply.parent), - root: stripViewerFromPost(item.reply.root), - grandparentAuthor: - item.reply.grandparentAuthor && - stripViewer(item.reply.grandparentAuthor), + parent: stripViewerFromPost(item.reply.parent, true), + root: stripViewerFromPost(item.reply.root, true), + } + + if (item.reply.grandparentAuthor) { + result.reply.grandparentAuthor = stripViewer( + item.reply.grandparentAuthor, + ) } } return result diff --git a/packages/bsky/tests/views/thread.test.ts b/packages/bsky/tests/views/thread.test.ts index 6cf095d795d..dfa7d49d389 100644 --- a/packages/bsky/tests/views/thread.test.ts +++ b/packages/bsky/tests/views/thread.test.ts @@ -1,3 +1,4 @@ +import assert from 'node:assert' import { AppBskyFeedGetPostThread, AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { @@ -6,6 +7,7 @@ import { stripViewerFromThread, } from '../_util' import { ids } from '../../src/lexicon/lexicons' +import { isThreadViewPost } from '../../src/lexicon/types/app/bsky/feed/defs' describe('pds thread views', () => { let network: TestNetwork @@ -400,7 +402,10 @@ describe('pds thread views', () => { }, ) - const parent = threadPreTakedown.data.thread.parent?.['post'] + assert(isThreadViewPost(threadPreTakedown.data.thread)) + assert(isThreadViewPost(threadPreTakedown.data.thread.parent)) + + const parent = threadPreTakedown.data.thread.parent.post await network.bsky.ctx.dataplane.takedownRecord({ recordUri: parent.uri, @@ -435,8 +440,18 @@ describe('pds thread views', () => { ), }, ) + + assert(isThreadViewPost(threadPreTakedown.data.thread)) + assert(isThreadViewPost(threadPreTakedown.data.thread.replies?.[0])) + assert(isThreadViewPost(threadPreTakedown.data.thread.replies?.[1])) + assert( + isThreadViewPost( + threadPreTakedown.data.thread.replies?.[1].replies?.[0], + ), + ) + const post1 = threadPreTakedown.data.thread.replies?.[0].post - const post2 = threadPreTakedown.data.thread.replies?.[1].replies[0].post + const post2 = threadPreTakedown.data.thread.replies?.[1].replies?.[0].post await Promise.all( [post1, post2].map((post) => diff --git a/packages/bsky/tests/views/threadgating.test.ts b/packages/bsky/tests/views/threadgating.test.ts index 2a459e24f5c..ec80340639b 100644 --- a/packages/bsky/tests/views/threadgating.test.ts +++ b/packages/bsky/tests/views/threadgating.test.ts @@ -1,4 +1,4 @@ -import assert from 'assert' +import assert from 'node:assert' import { AtpAgent } from '@atproto/api' import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env' import { diff --git a/packages/bsky/tsconfig.json b/packages/bsky/tsconfig.json index 1f3ab3c0ec4..458d2852916 100644 --- a/packages/bsky/tsconfig.json +++ b/packages/bsky/tsconfig.json @@ -1,7 +1,14 @@ { "include": [], "references": [ - { "path": "./tsconfig.build.json" }, - { "path": "./tsconfig.tests.json" } + { "path": "./tsconfig.build.json" } + + // The following files validate properly in VSCode, but for some reason, + // generate errors when compiled with tsc ("pnpm verify:types"). I'm not + // sure why, but I'm not going to spend time figuring it out right now: + // - "./tests/views/thread.test.ts" + // - "./tests/views/threadgating.test.ts" + + // { "path": "./tsconfig.tests.json" } ] } diff --git a/packages/dev-env/src/moderator-client.ts b/packages/dev-env/src/moderator-client.ts index 6c80fdeca9c..6e4bf64446f 100644 --- a/packages/dev-env/src/moderator-client.ts +++ b/packages/dev-env/src/moderator-client.ts @@ -62,7 +62,7 @@ export class ModeratorClient { subjectBlobCids?: TakeActionInput['subjectBlobCids'] reason?: string createdBy?: string - meta?: TakeActionInput['meta'] + meta?: unknown }, role?: ModLevel, ) { @@ -74,7 +74,14 @@ export class ModeratorClient { createdBy = 'did:example:admin', } = opts const result = await this.agent.tools.ozone.moderation.emitEvent( - { event, subject, subjectBlobCids, createdBy, reason }, + { + event, + subject, + subjectBlobCids, + createdBy, + // @ts-expect-error this a valid input property + reason, + }, { encoding: 'application/json', headers: await this.ozone.modHeaders( diff --git a/packages/dev-env/src/seed/client.ts b/packages/dev-env/src/seed/client.ts index c493735c0e9..8b858fcac2f 100644 --- a/packages/dev-env/src/seed/client.ts +++ b/packages/dev-env/src/seed/client.ts @@ -258,7 +258,7 @@ export class SeedClient< async block( from: string, to: string, - overrides?: Partial, + overrides?: Partial>, ) { const res = await this.agent.app.bsky.graph.block.create( { repo: from }, diff --git a/packages/lex-cli/src/codegen/client.ts b/packages/lex-cli/src/codegen/client.ts index 0512f08f669..ac86e3865c4 100644 --- a/packages/lex-cli/src/codegen/client.ts +++ b/packages/lex-cli/src/codegen/client.ts @@ -81,6 +81,11 @@ const indexTs = ( }) .addNamedImports([{ name: 'CID' }]) + //= import {OmitKey} from './util' + file + .addImportDeclaration({ moduleSpecifier: `./util` }) + .addNamedImports([{ name: 'OmitKey' }]) + // generate type imports and re-exports for (const lexicon of lexiconDocs) { const moduleSpecifier = `./types/${lexicon.id.split('.').join('/')}` @@ -312,7 +317,7 @@ function genRecordCls(file: SourceFile, nsid: string, lexRecord: LexRecord) { }) method.addParameter({ name: 'params', - type: `Omit<${toTitleCase(ATP_METHODS.list)}.QueryParams, "collection">`, + type: `OmitKey<${toTitleCase(ATP_METHODS.list)}.QueryParams, "collection">`, }) method.setBodyText( [ @@ -330,7 +335,7 @@ function genRecordCls(file: SourceFile, nsid: string, lexRecord: LexRecord) { }) method.addParameter({ name: 'params', - type: `Omit<${toTitleCase(ATP_METHODS.get)}.QueryParams, "collection">`, + type: `OmitKey<${toTitleCase(ATP_METHODS.get)}.QueryParams, "collection">`, }) method.setBodyText( [ @@ -348,7 +353,7 @@ function genRecordCls(file: SourceFile, nsid: string, lexRecord: LexRecord) { }) method.addParameter({ name: 'params', - type: `Omit<${toTitleCase( + type: `OmitKey<${toTitleCase( ATP_METHODS.create, )}.InputSchema, "collection" | "record">`, }) @@ -365,8 +370,8 @@ function genRecordCls(file: SourceFile, nsid: string, lexRecord: LexRecord) { : '' method.setBodyText( [ - `record.$type = '${nsid}'`, - `const res = await this._client.call('${ATP_METHODS.create}', undefined, { collection: '${nsid}', ${maybeRkeyPart}...params, record }, {encoding: 'application/json', headers })`, + `const collection = '${nsid}'`, + `const res = await this._client.call('${ATP_METHODS.create}', undefined, { collection, ${maybeRkeyPart}...params, record: { ...record, $type: collection} }, {encoding: 'application/json', headers })`, `return res.data`, ].join('\n'), ) @@ -380,7 +385,7 @@ function genRecordCls(file: SourceFile, nsid: string, lexRecord: LexRecord) { // }) // method.addParameter({ // name: 'params', - // type: `Omit<${toTitleCase(ATP_METHODS.put)}.InputSchema, "collection" | "record">`, + // type: `OmitKey<${toTitleCase(ATP_METHODS.put)}.InputSchema, "collection" | "record">`, // }) // method.addParameter({ // name: 'record', @@ -407,7 +412,7 @@ function genRecordCls(file: SourceFile, nsid: string, lexRecord: LexRecord) { }) method.addParameter({ name: 'params', - type: `Omit<${toTitleCase( + type: `OmitKey<${toTitleCase( ATP_METHODS.delete, )}.InputSchema, "collection">`, }) @@ -460,7 +465,7 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) => }) .addNamedImports([{ name: 'CID' }]) - //= import { $Type, is$typed } from '../../util.ts' + //= import { $Type, $Typed, is$typed, OmitKey } from '../../util.ts' file .addImportDeclaration({ moduleSpecifier: `${lexiconDoc.id @@ -468,7 +473,12 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) => .map((_str) => '..') .join('/')}/util`, }) - .addNamedImports([{ name: '$Type' }, { name: 'is$typed' }]) + .addNamedImports([ + { name: '$Type' }, + { name: '$Typed' }, + { name: 'is$typed' }, + { name: 'OmitKey' }, + ]) //= import {lexicons} from '../../lexicons.ts' file @@ -608,7 +618,12 @@ function genClientRecord( const def = lexicons.getDefOrThrow(lexUri, ['record']) //= export interface Record {...} - genObject(file, imports, lexUri, def.record, 'Record') + genObject(file, imports, lexUri, def.record, 'Record', { + defaultsArePresent: true, + allowUnknownProperties: true, + addTypeProperty: true, + }) + //= export function isRecord(v: unknown): v is Record {...} genObjHelpers(file, lexUri, 'Record') } diff --git a/packages/lex-cli/src/codegen/common.ts b/packages/lex-cli/src/codegen/common.ts index 6aa2a3a4807..ee17bf54fbe 100644 --- a/packages/lex-cli/src/codegen/common.ts +++ b/packages/lex-cli/src/codegen/common.ts @@ -14,6 +14,10 @@ const PRETTIER_OPTS = { export const utilTs = (project) => gen(project, '/util.ts', async (file) => { file.replaceWithText(` +export type OmitKey = { + [K2 in keyof T as K2 extends K ? never : K2]: T[K2] +} + export type $Typed = V & { $type: string } export type $Type = Hash extends 'main' @@ -42,12 +46,57 @@ function check$type( $type.startsWith(id) && $type.endsWith(hash) } +${ + /** + * The construct below allows to properly distinguish open unions. Consider + * the following example: + * + * ```ts + * type Foo = { $type?: $Type<'foo', 'main'>; foo: string } + * type Bar = { $type?: $Type<'bar', 'main'>; bar: string } + * type OpenFooBarUnion = $Typed | $Typed | { $type: string } + * ``` + * + * In the context of lexicons, when there is a open union as shown above, the + * if `$type` if either `foo` or `bar`, then the object IS of type `Foo` or + * `Bar`. + * + * ```ts + * declare const obj1: OpenFooBarUnion + * if (is$typed(obj1, 'foo', 'main')) { + * obj1.$type // "foo" | "foo#main" + * obj1.foo // string + * } + * ``` + * + * Similarly, if an object is of type `unknown`, then the `is$typed` function + * should only return assurance about the `$type` property, which is what it + * actually checks: + * + * ```ts + * declare const obj2: unknown + * if (is$typed(obj2, 'foo', 'main')) { + * obj2.$type // "foo" | "foo#main" + * // @ts-expect-error + * obj2.foo + * } + * ``` + * + * The construct bellow is what makes these two scenarios possible. + */ + '' +} +export type Is$Typed = V extends { + $type?: string +} + ? Extract }> + : V & { $type: $Type } export function is$typed( v: V, id: Id, hash: Hash, -): v is V & object & { $type: $Type } { +): v is Is$Typed { return has$type(v) && check$type(v.$type, id, hash) } `) @@ -124,14 +173,11 @@ export const lexiconsTs = (project, lexicons: LexiconDoc[]) => declarations: [ { name: 'ids', - initializer: JSON.stringify( - lexicons.reduce((acc, cur) => { - return { - ...acc, - [nsidToEnum(cur.id)]: cur.id, - } - }, {}), - ), + initializer: `{${lexicons + .map( + (lex) => `\n ${nsidToEnum(lex.id)}: ${JSON.stringify(lex.id)},`, + ) + .join('')}\n} as const`, }, ], }) diff --git a/packages/lex-cli/src/codegen/lex-gen.ts b/packages/lex-cli/src/codegen/lex-gen.ts index 6f4fce8290d..1c0faeb5c13 100644 --- a/packages/lex-cli/src/codegen/lex-gen.ts +++ b/packages/lex-cli/src/codegen/lex-gen.ts @@ -62,7 +62,9 @@ export function genUserType( genToken(file, lexUri, def) break case 'object': - genObject(file, imports, lexUri, def) + genObject(file, imports, lexUri, def, undefined, { + addTypeProperty: true, + }) genObjHelpers(file, lexUri) break @@ -88,14 +90,31 @@ export function genObject( imports: Set, lexUri: string, def: LexObject, - ifaceName?: string, - defaultsArePresent = true, + ifaceName: string = toTitleCase(getHash(lexUri)), + { + defaultsArePresent = true, + allowUnknownProperties = false, + addTypeProperty = false, + }: { + defaultsArePresent?: boolean + allowUnknownProperties?: boolean + addTypeProperty?: boolean + } = {}, ) { const iface = file.addInterface({ - name: ifaceName || toTitleCase(getHash(lexUri)), + name: ifaceName, isExported: true, }) genComment(iface, def) + + if (addTypeProperty) { + //= $type: uri + iface.addProperty({ + name: `$type?`, + type: `$Type<${JSON.stringify(stripHash(stripScheme(lexUri)))}, ${JSON.stringify(getHash(lexUri))}>`, + }) + } + const nullableProps = new Set(def.nullable) if (def.properties) { for (const propKey in def.properties) { @@ -108,12 +127,15 @@ export function genObject( propDef.default !== undefined) if (propDef.type === 'ref' || propDef.type === 'union') { //= propName: External|External - const refs = propDef.type === 'union' ? propDef.refs : [propDef.ref] - const types = refs.map((ref) => - refToType(ref, stripScheme(stripHash(lexUri)), imports), - ) + const types = + propDef.type === 'union' + ? propDef.refs.map( + (ref) => + `$Typed<${refToType(ref, stripScheme(stripHash(lexUri)), imports)}>`, + ) + : [refToType(propDef.ref, stripScheme(stripHash(lexUri)), imports)] if (propDef.type === 'union' && !propDef.closed) { - types.push('{$type: string; [k: string]: unknown}') + types.push('{ $type: string }') } iface.addProperty({ name: `${propKey}${req ? '' : '?'}`, @@ -140,11 +162,12 @@ export function genObject( ), }) } else if (propDef.items.type === 'union') { - const types = propDef.items.refs.map((ref) => - refToType(ref, stripScheme(stripHash(lexUri)), imports), + const types = propDef.items.refs.map( + (ref) => + `$Typed<${refToType(ref, stripScheme(stripHash(lexUri)), imports)}>`, ) if (!propDef.items.closed) { - types.push('{$type: string; [k: string]: unknown}') + types.push('{ $type: string }') } propAst = iface.addProperty({ name: `${propKey}${req ? '' : '?'}`, @@ -177,12 +200,15 @@ export function genObject( } } } - //= [k: string]: unknown - iface.addIndexSignature({ - keyName: 'k', - keyType: 'string', - returnType: 'unknown', - }) + + if (allowUnknownProperties) { + //= [k: string]: unknown + iface.addIndexSignature({ + keyName: 'k', + keyType: 'string', + returnType: 'unknown', + }) + } } } @@ -221,11 +247,12 @@ export function genArray( isExported: true, }) } else if (def.items.type === 'union') { - const types = def.items.refs.map((ref) => - refToType(ref, stripScheme(stripHash(lexUri)), imports), + const types = def.items.refs.map( + (ref) => + `$Typed<${refToType(ref, stripScheme(stripHash(lexUri)), imports)}>`, ) if (!def.items.closed) { - types.push('{$type: string; [k: string]: unknown}') + types.push('{ $type: string }') } file.addTypeAlias({ name: toTitleCase(getHash(lexUri)), @@ -310,15 +337,23 @@ export function genXrpcInput( if (def.type === 'procedure' && def.input?.schema) { if (def.input.schema.type === 'ref' || def.input.schema.type === 'union') { //= export type InputSchema = ... - const refs = + + const types = def.input.schema.type === 'union' - ? def.input.schema.refs - : [def.input.schema.ref] - const types = refs.map((ref) => - refToType(ref, stripScheme(stripHash(lexUri)), imports), - ) + ? def.input.schema.refs.map( + (ref) => + `$Typed<${refToType(ref, stripScheme(stripHash(lexUri)), imports)}>`, + ) + : [ + refToType( + def.input.schema.ref, + stripScheme(stripHash(lexUri)), + imports, + ), + ] + if (def.input.schema.type === 'union' && !def.input.schema.closed) { - types.push('{$type: string; [k: string]: unknown}') + types.push('{ $type: string }') } file.addTypeAlias({ name: 'InputSchema', @@ -327,14 +362,9 @@ export function genXrpcInput( }) } else { //= export interface InputSchema {...} - genObject( - file, - imports, - lexUri, - def.input.schema, - `InputSchema`, + genObject(file, imports, lexUri, def.input.schema, `InputSchema`, { defaultsArePresent, - ) + }) } } else if (def.type === 'procedure' && def.input?.encoding) { //= export type InputSchema = string | Uint8Array | Blob @@ -371,12 +401,15 @@ export function genXrpcOutput( if (schema) { if (schema.type === 'ref' || schema.type === 'union') { //= export type OutputSchema = ... - const refs = schema.type === 'union' ? schema.refs : [schema.ref] - const types = refs.map((ref) => - refToType(ref, stripScheme(stripHash(lexUri)), imports), - ) + const types = + schema.type === 'union' + ? schema.refs.map( + (ref) => + `$Typed<${refToType(ref, stripScheme(stripHash(lexUri)), imports)}>`, + ) + : [refToType(schema.ref, stripScheme(stripHash(lexUri)), imports)] if (schema.type === 'union' && !schema.closed) { - types.push('{$type: string; [k: string]: unknown}') + types.push('{ $type: string }') } file.addTypeAlias({ name: 'OutputSchema', @@ -385,14 +418,9 @@ export function genXrpcOutput( }) } else { //= export interface OutputSchema {...} - genObject( - file, - imports, - lexUri, - schema, - `OutputSchema`, + genObject(file, imports, lexUri, schema, `OutputSchema`, { defaultsArePresent, - ) + }) } } } @@ -404,26 +432,39 @@ export function genObjHelpers( ) { const hash = getHash(lexUri) - //= export function is{X}(v: unknown): v is X & { $type: NS } {...} + //= export function is{X}(v: V) {...} + + const isX = toCamelCase(`is-${ifaceName}`) file .addFunction({ - name: toCamelCase(`is-${ifaceName}`), - parameters: [{ name: `v`, type: `unknown` }], - returnType: `v is ${ifaceName} & { $type: $Type<'${stripHash(lexUri)}', '${hash}'> }`, + name: isX, + typeParameters: [{ name: `V` }], + parameters: [{ name: `v`, type: `V` }], isExported: true, }) .setBodyText(`return is$typed(v, id, ${JSON.stringify(hash)})`) + const validateX = toCamelCase(`validate-${ifaceName}`) //= export function validate{X}(v: unknown): ValidationResult {...} file .addFunction({ - name: toCamelCase(`validate-${ifaceName}`), + name: validateX, parameters: [{ name: `v`, type: `unknown` }], isExported: true, }) .setBodyText( `return lexicons.validate(\`\${id}#${hash}\`, v) as ValidationResult<${ifaceName}>`, ) + + const isValidX = toCamelCase(`is-valid-${ifaceName}`) + file + .addFunction({ + name: `${isValidX}`, + parameters: [{ name: `v`, type: `V` }], + returnType: `v is V & $Typed<${ifaceName}>`, + isExported: true, + }) + .setBodyText(`return ${isX}(v) && ${validateX}(v).success`) } export function stripScheme(uri: string): string { @@ -507,7 +548,9 @@ export function primitiveToType(def: LexPrimitive): string { } return 'boolean' case 'unknown': - return '{}' + // the "Record" identifier from typescript get overwritten by the Record + // interface created by lex-cli. + return '{ [_ in string]: unknown }' // Record default: throw new Error(`Unexpected primitive type: ${JSON.stringify(def)}`) } diff --git a/packages/lex-cli/src/codegen/server.ts b/packages/lex-cli/src/codegen/server.ts index b6a3013fb02..388b061dfdb 100644 --- a/packages/lex-cli/src/codegen/server.ts +++ b/packages/lex-cli/src/codegen/server.ts @@ -366,7 +366,7 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) => }) .addNamedImports([{ name: 'lexicons' }]) - //= import { $Type, is$typed } from '../../util.ts' + //= import { $Type, $Typed, is$typed, OmitKey } from '../../util.ts' file .addImportDeclaration({ moduleSpecifier: `${lexiconDoc.id @@ -374,7 +374,12 @@ const lexiconTs = (project, lexicons: Lexicons, lexiconDoc: LexiconDoc) => .map((_str) => '..') .join('/')}/util`, }) - .addNamedImports([{ name: '$Type' }, { name: 'is$typed' }]) + .addNamedImports([ + { name: '$Type' }, + { name: '$Typed' }, + { name: 'is$typed' }, + { name: 'OmitKey' }, + ]) //= const id = "{lexiconDoc.id}" file.addVariableStatement({ @@ -609,7 +614,12 @@ function genServerRecord( const def = lexicons.getDefOrThrow(lexUri, ['record']) //= export interface Record {...} - genObject(file, imports, lexUri, def.record, 'Record') + genObject(file, imports, lexUri, def.record, 'Record', { + defaultsArePresent: true, + allowUnknownProperties: true, + addTypeProperty: true, + }) + //= export function isRecord(v: unknown): v is Record {...} genObjHelpers(file, lexUri, 'Record') } diff --git a/packages/ozone/src/api/moderation/emitEvent.ts b/packages/ozone/src/api/moderation/emitEvent.ts index de9de62435a..4cd100a5131 100644 --- a/packages/ozone/src/api/moderation/emitEvent.ts +++ b/packages/ozone/src/api/moderation/emitEvent.ts @@ -2,8 +2,8 @@ import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' import { Server } from '../../lexicon' import AppContext from '../../context' import { + isValidModEventDivert, isModEventAcknowledge, - isModEventDivert, isModEventEmail, isModEventLabel, isModEventMuteReporter, @@ -125,7 +125,7 @@ const handleModerationEvent = async ({ ) } - if (isModEventDivert(event) && subject.isRecord()) { + if (isValidModEventDivert(event) && subject.isRecord()) { if (!ctx.blobDiverter) { throw new InvalidRequestError( 'BlobDiverter not configured for this service', @@ -222,7 +222,7 @@ export default function (server: Server, ctx: AppContext) { }) // On divert events, we need to automatically take down the blobs - if (isModEventDivert(input.body.event)) { + if (isValidModEventDivert(input.body.event)) { await handleModerationEvent({ auth, ctx, diff --git a/packages/ozone/src/api/moderation/getRepos.ts b/packages/ozone/src/api/moderation/getRepos.ts index fb810e624fe..134eb321e87 100644 --- a/packages/ozone/src/api/moderation/getRepos.ts +++ b/packages/ozone/src/api/moderation/getRepos.ts @@ -23,12 +23,12 @@ export default function (server: Server, ctx: AppContext) { } } return { - $type: 'tools.ozone.moderation.defs#repoViewDetail', ...addAccountInfoToRepoViewDetail( partialRepo, accountInfo.get(did) || null, auth.credentials.isModerator, ), + $type: 'tools.ozone.moderation.defs#repoViewDetail', } }) diff --git a/packages/ozone/src/api/util.ts b/packages/ozone/src/api/util.ts index 7d9c335c876..b21298e5a4f 100644 --- a/packages/ozone/src/api/util.ts +++ b/packages/ozone/src/api/util.ts @@ -8,6 +8,7 @@ import { REASONSEXUAL, REASONVIOLATION, REASONAPPEAL, + ReasonType, } from '../lexicon/types/com/atproto/moderation/defs' import { AccountView } from '../lexicon/types/com/atproto/admin/defs' import { @@ -51,12 +52,26 @@ export const getPdsAccountInfos = async ( } } +function strip$type(obj: T): Omit { + if ('$type' in obj) { + const { $type: _, ...rest } = obj + return rest + } + return obj +} + export const addAccountInfoToRepoViewDetail = ( - repoView: RepoViewDetail, + repoView: RepoView | RepoViewDetail, accountInfo: AccountView | null, includeEmail = false, ): RepoViewDetail => { - if (!accountInfo) return repoView + if (!accountInfo) { + return strip$type({ + ...repoView, + moderation: strip$type(repoView.moderation), + }) + } + const { email, deactivatedAt, @@ -67,6 +82,7 @@ export const addAccountInfoToRepoViewDetail = ( invitesDisabled, threatSignatures, // pick some duplicate/unwanted details out + $type: _accountType, did: _did, handle: _handle, indexedAt: _indexedAt, @@ -75,7 +91,8 @@ export const addAccountInfoToRepoViewDetail = ( } = accountInfo return { ...otherAccountInfo, - ...repoView, + ...strip$type(repoView), + moderation: strip$type(repoView.moderation), email: includeEmail ? email : undefined, invitedBy, invitesDisabled, @@ -106,7 +123,7 @@ export const addAccountInfoToRepoView = ( export const getReasonType = (reasonType: ReportInput['reasonType']) => { if (reasonTypes.has(reasonType)) { - return reasonType as NonNullable['reportType'] + return reasonType } throw new InvalidRequestError('Invalid reason type') } @@ -128,7 +145,7 @@ export const getReviewState = (reviewState?: string) => { const reviewStates = new Set([REVIEWCLOSED, REVIEWESCALATED, REVIEWOPEN]) -const reasonTypes = new Set([ +const reasonTypes = new Set([ REASONOTHER, REASONSPAM, REASONMISLEADING, diff --git a/packages/ozone/src/mod-service/index.ts b/packages/ozone/src/mod-service/index.ts index eeb6aea5848..0c4e663f4f8 100644 --- a/packages/ozone/src/mod-service/index.ts +++ b/packages/ozone/src/mod-service/index.ts @@ -56,6 +56,7 @@ import { httpLogger as log } from '../logger' import { OzoneConfig } from '../config' import { LABELER_HEADER_NAME, ParsedLabelers } from '../util' import { ids } from '../lexicon/lexicons' +import { ReasonType } from '../lexicon/types/com/atproto/moderation/defs' export type ModerationServiceCreator = (db: Database) => ModerationService @@ -448,7 +449,10 @@ export class ModerationService { const modEvent = await this.db.db .insertInto('moderation_event') .values({ - comment: event.comment ? `${event.comment}` : null, + comment: + 'comment' in event && typeof event.comment === 'string' + ? event.comment || null + : null, action: event.$type as ModerationEvent['action'], createdAt: createdAt.toISOString(), createdBy, @@ -456,9 +460,10 @@ export class ModerationService { negateLabelVals, addedTags, removedTags, - durationInHours: event.durationInHours - ? Number(event.durationInHours) - : null, + durationInHours: + 'durationInHours' in event && event.durationInHours + ? Number(event.durationInHours) + : null, meta: Object.assign(meta, subjectInfo.meta), expiresAt: (isModEventTakedown(event) || isModEventMute(event)) && @@ -760,7 +765,7 @@ export class ModerationService { } async report(info: { - reasonType: NonNullable['reportType'] + reasonType: ReasonType reason?: string subject: ModSubject reportedBy: string diff --git a/packages/ozone/src/mod-service/subject.ts b/packages/ozone/src/mod-service/subject.ts index 51c762a5288..e019f789b33 100644 --- a/packages/ozone/src/mod-service/subject.ts +++ b/packages/ozone/src/mod-service/subject.ts @@ -3,9 +3,19 @@ import { InputSchema as ReportInput } from '../lexicon/types/com/atproto/moderat import { InputSchema as ActionInput } from '../lexicon/types/tools/ozone/moderation/emitEvent' import { InvalidRequestError } from '@atproto/xrpc-server' import { ModerationEventRow, ModerationSubjectStatusRow } from './types' -import { RepoRef } from '../lexicon/types/com/atproto/admin/defs' -import { Main as StrongRef } from '../lexicon/types/com/atproto/repo/strongRef' -import { MessageRef } from '../lexicon/types/chat/bsky/convo/defs' +import { + isValidRepoRef, + RepoRef, +} from '../lexicon/types/com/atproto/admin/defs' +import { + isValidMain as isValidStrongRef, + Main as StrongRef, +} from '../lexicon/types/com/atproto/repo/strongRef' +import { + isValidMessageRef, + MessageRef, +} from '../lexicon/types/chat/bsky/convo/defs' +import { $Typed } from '../lexicon/util' type SubjectInput = ReportInput['subject'] | ActionInput['subject'] @@ -13,37 +23,21 @@ export const subjectFromInput = ( subject: SubjectInput, blobs?: string[], ): ModSubject => { - if ( - subject.$type === 'com.atproto.admin.defs#repoRef' && - typeof subject.did === 'string' - ) { + if (isValidRepoRef(subject)) { if (blobs && blobs.length > 0) { throw new InvalidRequestError('Blobs do not apply to repo subjects') } return new RepoSubject(subject.did) } - if ( - subject.$type === 'com.atproto.repo.strongRef' && - typeof subject.uri === 'string' && - typeof subject.cid === 'string' - ) { + if (isValidStrongRef(subject)) { return new RecordSubject(subject.uri, subject.cid, blobs) } // @NOTE #messageRef is not a report input for com.atproto.moderation.createReport. // we are taking advantage of the open union in order for bsky.chat to interoperate here. - if ( - subject.$type === 'chat.bsky.convo.defs#messageRef' && - typeof subject.did === 'string' && - (typeof subject.convoId === 'string' || subject.convoId === undefined) && - typeof subject.messageId === 'string' - ) { + if (isValidMessageRef(subject)) { // @TODO we should start to require subject.convoId is a string in order to properly validate // the #messageRef. temporarily allowing it to be optional as a stopgap for rollout. - return new MessageSubject( - subject.did, - subject.convoId ?? '', - subject.messageId, - ) + return new MessageSubject(subject.did, subject.convoId, subject.messageId) } throw new InvalidRequestError('Invalid subject') @@ -106,7 +100,7 @@ export interface ModSubject { isRecord(): this is RecordSubject isMessage(): this is MessageSubject info(): SubjectInfo - lex(): RepoRef | StrongRef | MessageRef + lex(): $Typed | $Typed | $Typed } export class RepoSubject implements ModSubject { @@ -133,7 +127,7 @@ export class RepoSubject implements ModSubject { meta: null, } } - lex(): RepoRef { + lex(): $Typed { return { $type: 'com.atproto.admin.defs#repoRef', did: this.did, @@ -174,7 +168,7 @@ export class RecordSubject implements ModSubject { meta: null, } } - lex(): StrongRef { + lex(): $Typed { return { $type: 'com.atproto.repo.strongRef', uri: this.uri, @@ -211,7 +205,7 @@ export class MessageSubject implements ModSubject { meta: { convoId: this.convoId || undefined }, } } - lex(): MessageRef { + lex(): $Typed { return { $type: 'chat.bsky.convo.defs#messageRef', did: this.did, diff --git a/packages/ozone/src/mod-service/types.ts b/packages/ozone/src/mod-service/types.ts index 29a63d3e019..125eb2b4796 100644 --- a/packages/ozone/src/mod-service/types.ts +++ b/packages/ozone/src/mod-service/types.ts @@ -1,8 +1,8 @@ import { Selectable } from 'kysely' import { ModerationEvent } from '../db/schema/moderation_event' import { ModerationSubjectStatus } from '../db/schema/moderation_subject_status' -import { ToolsOzoneModerationDefs } from '@atproto/api' import { ModSubject } from './subject' +import { ModEventView } from '../lexicon/types/tools/ozone/moderation/defs' export type ModerationEventRow = Selectable export type ReversibleModerationEvent = Pick< @@ -21,19 +21,7 @@ export type ModerationSubjectStatusRow = Selectable export type ModerationSubjectStatusRowWithHandle = ModerationSubjectStatusRow & { handle: string | null } -export type ModEventType = - | ToolsOzoneModerationDefs.ModEventTakedown - | ToolsOzoneModerationDefs.ModEventAcknowledge - | ToolsOzoneModerationDefs.ModEventEscalate - | ToolsOzoneModerationDefs.ModEventComment - | ToolsOzoneModerationDefs.ModEventLabel - | ToolsOzoneModerationDefs.ModEventReport - | ToolsOzoneModerationDefs.ModEventMute - | ToolsOzoneModerationDefs.ModEventReverseTakedown - | ToolsOzoneModerationDefs.ModEventTag - | ToolsOzoneModerationDefs.AccountEvent - | ToolsOzoneModerationDefs.IdentityEvent - | ToolsOzoneModerationDefs.RecordEvent +export type ModEventType = ModEventView['event'] type AccountHostingView = { $type: 'tools.ozone.moderation.defs#accountHosting' diff --git a/packages/ozone/src/mod-service/util.ts b/packages/ozone/src/mod-service/util.ts index c07ce48de8c..4c2e61969f7 100644 --- a/packages/ozone/src/mod-service/util.ts +++ b/packages/ozone/src/mod-service/util.ts @@ -16,7 +16,7 @@ export const formatLabel = (row: LabelRow): Label => { cts: row.cts, exp: row.exp ?? undefined, sig: row.sig ? new Uint8Array(row.sig) : undefined, - }) as Label + } satisfies Label) as unknown as Label } export const formatLabelRow = ( @@ -50,7 +50,7 @@ export const signLabel = async ( neg: neg === true ? true : undefined, cts, exp, - }) as Label + } satisfies Label) as unknown as Label const bytes = cborEncode(reformatted) const sig = await signingKey.sign(bytes) diff --git a/packages/ozone/src/mod-service/views.ts b/packages/ozone/src/mod-service/views.ts index d4b0deafc33..52f0b76f32f 100644 --- a/packages/ozone/src/mod-service/views.ts +++ b/packages/ozone/src/mod-service/views.ts @@ -1,39 +1,55 @@ -import { sql } from 'kysely' -import { AtUri, INVALID_HANDLE, normalizeDatetimeAlways } from '@atproto/syntax' -import { - AtpAgent, - AppBskyFeedDefs, - ToolsOzoneModerationDefs, -} from '@atproto/api' +import { AtpAgent } from '@atproto/api' import { dedupeStrs } from '@atproto/common' -import { BlobRef } from '@atproto/lexicon' import { Keypair } from '@atproto/crypto' +import { BlobRef } from '@atproto/lexicon' +import { AtUri, INVALID_HANDLE, normalizeDatetimeAlways } from '@atproto/syntax' +import { sql } from 'kysely' import { Database } from '../db' +import { LabelRow } from '../db/schema/label' +import { ids } from '../lexicon/lexicons' +import { FeedViewPost } from '../lexicon/types/app/bsky/feed/defs' +import { AccountView } from '../lexicon/types/com/atproto/admin/defs' +import { + Label, + isValidSelfLabels, +} from '../lexicon/types/com/atproto/label/defs' +import { OutputSchema as ReportOutput } from '../lexicon/types/com/atproto/moderation/createReport' +import { REASONOTHER } from '../lexicon/types/com/atproto/moderation/defs' import { + BlobView, ModEventView, - RepoView, - RepoViewDetail, + ModEventViewDetail, RecordView, RecordViewDetail, - BlobView, + RepoView, SubjectStatusView, - ModEventViewDetail, + isAccountEvent, + isIdentityEvent, + isModEventAcknowledge, + isModEventComment, + isModEventEmail, + isModEventEscalate, + isModEventLabel, + isModEventMute, + isModEventMuteReporter, + isModEventReport, + isModEventTag, + isModEventTakedown, + isRecordEvent, } from '../lexicon/types/tools/ozone/moderation/defs' -import { AccountView } from '../lexicon/types/com/atproto/admin/defs' -import { OutputSchema as ReportOutput } from '../lexicon/types/com/atproto/moderation/createReport' -import { Label, isSelfLabels } from '../lexicon/types/com/atproto/label/defs' +import { dbLogger, httpLogger } from '../logger' +import { ParsedLabelers } from '../util' +import { subjectFromEventRow, subjectFromStatusRow } from './subject' import { ModerationEventRowWithHandle, ModerationSubjectStatusRowWithHandle, } from './types' -import { REASONOTHER } from '../lexicon/types/com/atproto/moderation/defs' -import { subjectFromEventRow, subjectFromStatusRow } from './subject' import { formatLabel, signLabel } from './util' -import { LabelRow } from '../db/schema/label' -import { dbLogger } from '../logger' -import { httpLogger } from '../logger' -import { ParsedLabelers } from '../util' -import { ids } from '../lexicon/lexicons' + +const ifString = (val: unknown): string | undefined => + typeof val === 'string' ? val : undefined +const ifBoolean = (val: unknown): boolean | undefined => + typeof val === 'boolean' ? val : undefined export type AuthHeaders = { headers: { @@ -98,125 +114,103 @@ export class ModerationViews { }, new Map()) } - formatEvent(event: ModerationEventRowWithHandle): ModEventView { + formatEvent(row: ModerationEventRowWithHandle): Omit { const eventView: ModEventView = { - id: event.id, + id: row.id, event: { - $type: event.action, - comment: event.comment ?? undefined, + $type: row.action, + comment: row.comment ?? undefined, }, - subject: subjectFromEventRow(event).lex(), - subjectBlobCids: event.subjectBlobCids ?? [], - createdBy: event.createdBy, - createdAt: event.createdAt, - subjectHandle: event.subjectHandle ?? undefined, - creatorHandle: event.creatorHandle ?? undefined, + subject: subjectFromEventRow(row).lex(), + subjectBlobCids: row.subjectBlobCids ?? [], + createdBy: row.createdBy, + createdAt: row.createdAt, + subjectHandle: row.subjectHandle ?? undefined, + creatorHandle: row.creatorHandle ?? undefined, } + const { event } = eventView + const meta = row.meta || {} + if ( - [ - 'tools.ozone.moderation.defs#modEventMuteReporter', - 'tools.ozone.moderation.defs#modEventTakedown', - 'tools.ozone.moderation.defs#modEventMute', - ].includes(event.action) + isModEventMuteReporter(event) || + isModEventTakedown(event) || + isModEventMute(event) ) { - eventView.event = { - ...eventView.event, - durationInHours: event.durationInHours ?? undefined, - } + event.durationInHours = row.durationInHours ?? undefined } if ( - (event.action === 'tools.ozone.moderation.defs#modEventTakedown' || - event.action === 'tools.ozone.moderation.defs#modEventAcknowledge') && - event.meta?.acknowledgeAccountSubjects + (isModEventTakedown(event) || isModEventAcknowledge(event)) && + meta.acknowledgeAccountSubjects ) { - eventView.event = { - ...eventView.event, - acknowledgeAccountSubjects: event.meta.acknowledgeAccountSubjects, - } + event.acknowledgeAccountSubjects = ifBoolean( + meta.acknowledgeAccountSubjects, + )! } - if (event.action === 'tools.ozone.moderation.defs#modEventLabel') { - eventView.event = { - ...eventView.event, - createLabelVals: event.createLabelVals?.length - ? event.createLabelVals.split(' ') - : [], - negateLabelVals: event.negateLabelVals?.length - ? event.negateLabelVals.split(' ') - : [], - } + if (isModEventLabel(event)) { + event.createLabelVals = row.createLabelVals?.length + ? row.createLabelVals.split(' ') + : [] + event.negateLabelVals = row.negateLabelVals?.length + ? row.negateLabelVals.split(' ') + : [] } // This is for legacy data only, for new events, these types of events won't have labels attached if ( - [ - 'tools.ozone.moderation.defs#modEventAcknowledge', - 'tools.ozone.moderation.defs#modEventTakedown', - 'tools.ozone.moderation.defs#modEventEscalate', - ].includes(event.action) + isModEventAcknowledge(event) || + isModEventTakedown(event) || + isModEventEscalate(event) ) { - if (event.createLabelVals?.length) { - eventView.event = { - ...eventView.event, - createLabelVals: event.createLabelVals.split(' '), - } + if (row.createLabelVals?.length) { + // @ts-expect-error legacy + event.createLabelVals = row.createLabelVals.split(' ') } - if (event.negateLabelVals?.length) { - eventView.event = { - ...eventView.event, - negateLabelVals: event.negateLabelVals.split(' '), - } + if (row.negateLabelVals?.length) { + // @ts-expect-error legacy + event.negateLabelVals = row.negateLabelVals.split(' ') } } - if (event.action === 'tools.ozone.moderation.defs#modEventReport') { - eventView.event = { - ...eventView.event, - reportType: event.meta?.reportType ?? undefined, - isReporterMuted: !!event.meta?.isReporterMuted, - } + if (isModEventReport(event)) { + event.isReporterMuted = !!meta.isReporterMuted + event.reportType = ifString(meta.reportType)! } - if (event.action === 'tools.ozone.moderation.defs#modEventEmail') { - eventView.event = { - ...eventView.event, - subjectLine: event.meta?.subjectLine ?? '', - content: event.meta?.content, - } + if (isModEventEmail(event)) { + event.content = ifString(meta.content)! + event.subjectLine = ifString(meta.subjectLine)! } - if ( - event.action === 'tools.ozone.moderation.defs#modEventComment' && - event.meta?.sticky - ) { - eventView.event.sticky = true + if (isModEventComment(event) && meta.sticky) { + event.sticky = true } - if (event.action === 'tools.ozone.moderation.defs#modEventTag') { - eventView.event.add = event.addedTags || [] - eventView.event.remove = event.removedTags || [] + if (isModEventTag(event)) { + event.add = row.addedTags || [] + event.remove = row.removedTags || [] } - if (event.action === 'tools.ozone.moderation.defs#accountEvent') { - eventView.event.active = !!event.meta?.active - eventView.event.timestamp = event.meta?.timestamp - eventView.event.status = event.meta?.status + if (isAccountEvent(event)) { + event.active = !!meta.active + event.timestamp = ifString(meta.timestamp)! + event.status = ifString(meta.status)! } - if (event.action === 'tools.ozone.moderation.defs#identityEvent') { - eventView.event.timestamp = event.meta?.timestamp - eventView.event.handle = event.meta?.handle - eventView.event.pdsHost = event.meta?.pdsHost - eventView.event.tombstone = !!event.meta?.tombstone + if (isIdentityEvent(event)) { + event.timestamp = ifString(meta.timestamp)! + event.handle = ifString(meta.handle)! + event.pdsHost = ifString(meta.pdsHost)! + event.tombstone = !!meta.tombstone } - if (event.action === 'tools.ozone.moderation.defs#recordEvent') { - eventView.event.op = event.meta?.op - eventView.event.cid = event.meta?.cid - eventView.event.timestamp = event.meta?.timestamp + if (isRecordEvent(event)) { + event.op = ifString(meta.op)! + event.cid = ifString(meta.cid)! + event.timestamp = ifString(meta.timestamp)! } return eventView @@ -234,7 +228,7 @@ export class ModerationViews { } const subject = await this.subject(subjectId) const eventView = this.formatEvent(result) - const allBlobs = findBlobRefs(subject.value) + const allBlobs = 'value' in subject ? findBlobRefs(subject.value) : [] const subjectBlobs = await this.blob( allBlobs.filter((blob) => eventView.subjectBlobCids.includes(blob.ref.toString()), @@ -308,7 +302,7 @@ export class ModerationViews { }, new Map()) } - async records(subjects: RecordSubject[]): Promise> { + async records(subjects: RecordSubject[]) { const uris = subjects.map((record) => new AtUri(record.uri)) const dids = uris.map((u) => u.hostname) @@ -318,13 +312,26 @@ export class ModerationViews { this.fetchRecords(subjects), ]) - return uris.reduce((acc, uri) => { + const map = new Map< + string, + // Because the result of this function is used to build RecordViewDetail, + // we explicitly type the result without the $type field, so can be used + // as both RecordView and RecordViewDetail, without having to cast or + // override the $type field. + RecordView & { + $type?: undefined + moderation: { $type?: undefined; subjectStatus?: SubjectStatusView } + } + >() + + for (const uri of uris) { const repo = repos.get(uri.hostname) - if (!repo) return acc + if (!repo) continue const record = records.get(uri.toString()) - if (!record) return acc + if (!record) continue const subjectStatus = subjectStatuses.get(uri.toString()) - return acc.set(uri.toString(), { + + map.set(uri.toString(), { uri: uri.toString(), cid: record.cid, value: record.value, @@ -337,7 +344,9 @@ export class ModerationViews { : undefined, }, }) - }, new Map()) + } + + return map } async recordDetails( @@ -428,7 +437,7 @@ export class ModerationViews { : REASONOTHER, reason: report.comment ?? undefined, reportedBy: report.createdBy, - subject: subjectFromEventRow(report).lex(), + subject: subjectFromEventRow(report).lex() as ReportOutput['subject'], } } // Partial view for subjects @@ -439,12 +448,12 @@ export class ModerationViews { const repo = repos.get(subject) if (repo) { return { - $type: 'com.atproto.admin.defs#repoView', ...repo, + $type: 'tools.ozone.moderation.defs#repoView', } } else { return { - $type: 'com.atproto.admin.defs#repoViewNotFound', + $type: 'tools.ozone.moderation.defs#repoViewNotFound', did: subject, } } @@ -453,12 +462,12 @@ export class ModerationViews { const record = records.get(subject) if (record) { return { - $type: 'com.atproto.admin.defs#recordView', ...record, + $type: 'tools.ozone.moderation.defs#recordView', } } else { return { - $type: 'com.atproto.admin.defs#recordViewNotFound', + $type: 'tools.ozone.moderation.defs#recordViewNotFound', uri: subject, } } @@ -616,7 +625,9 @@ export class ModerationViews { subjectRepoHandle: status.handle ?? undefined, subjectBlobCids: status.blobCids || [], tags: status.tags || [], - subject: subjectFromStatusRow(status).lex(), + subject: subjectFromStatusRow( + status, + ).lex() as SubjectStatusView['subject'], } if (status.recordPath !== '') { @@ -640,9 +651,7 @@ export class ModerationViews { return statusView } - async fetchAuthorFeed( - actor: string, - ): Promise { + async fetchAuthorFeed(actor: string): Promise { const auth = await this.appviewAuth(ids.AppBskyFeedGetAuthorFeed) if (!auth) return [] const { @@ -696,7 +705,7 @@ export function getSelfLabels(details: { }): Label[] { const { uri, cid, record } = details if (!uri || !cid || !record) return [] - if (!isSelfLabels(record.labels)) return [] + if (!isValidSelfLabels(record.labels)) return [] const src = new AtUri(uri).host // record creator const cts = typeof record.createdAt === 'string' diff --git a/packages/ozone/src/tag-service/embed-tagger.ts b/packages/ozone/src/tag-service/embed-tagger.ts index 11c4e07af21..4e8f2046eb9 100644 --- a/packages/ozone/src/tag-service/embed-tagger.ts +++ b/packages/ozone/src/tag-service/embed-tagger.ts @@ -28,7 +28,7 @@ export class EmbedTagger extends ContentTagger { return [] } const tags: string[] = [] - if (AppBskyFeedPost.isRecord(recordValue)) { + if (AppBskyFeedPost.isValidRecord(recordValue)) { const embedContent = AppBskyEmbedRecordWithMedia.isMain( recordValue.embed, ) diff --git a/packages/ozone/src/tag-service/language-tagger.ts b/packages/ozone/src/tag-service/language-tagger.ts index e11ea3ec93e..c131bb9072b 100644 --- a/packages/ozone/src/tag-service/language-tagger.ts +++ b/packages/ozone/src/tag-service/language-tagger.ts @@ -31,14 +31,14 @@ export class LanguageTagger extends ContentTagger { getTextFromRecord(recordValue?: Record): string | undefined { let text: string | undefined - if (AppBskyGraphList.isRecord(recordValue)) { + if (AppBskyGraphList.isValidRecord(recordValue)) { text = recordValue.description || recordValue.name } else if ( - AppBskyFeedGenerator.isRecord(recordValue) || - AppBskyActorProfile.isRecord(recordValue) + AppBskyFeedGenerator.isValidRecord(recordValue) || + AppBskyActorProfile.isValidRecord(recordValue) ) { text = recordValue.description || recordValue.displayName - } else if (AppBskyFeedPost.isRecord(recordValue)) { + } else if (AppBskyFeedPost.isValidRecord(recordValue)) { text = recordValue.text } diff --git a/packages/ozone/tests/_util.ts b/packages/ozone/tests/_util.ts index 991569ef4e7..6ec58db01e1 100644 --- a/packages/ozone/tests/_util.ts +++ b/packages/ozone/tests/_util.ts @@ -5,9 +5,12 @@ import { FeedViewPost, PostView, isPostView, + isReasonRepost, isThreadViewPost, } from '../src/lexicon/types/app/bsky/feed/defs' import { isViewRecord } from '../src/lexicon/types/app/bsky/embed/record' +import { isView as isRecordView } from '../src/lexicon/types/app/bsky/embed/record' +import { isView as isRecordWithMediaView } from '../src/lexicon/types/app/bsky/embed/recordWithMedia' // Swap out identifiers and dates with stable // values for the purpose of snapshot testing @@ -85,8 +88,10 @@ export const forSnapshot = (obj: unknown) => { export const getOriginator = (item: FeedViewPost) => { if (!item.reason) { return item.post.author.did + } else if (isReasonRepost(item.reason)) { + return item.reason.by.did } else { - return (item.reason.by as { [did: string]: string }).did + throw new Error('Unexpected reason') } } @@ -146,33 +151,38 @@ export const paginateAll = async ( } // @NOTE mutates -export const stripViewer = }>( - val: T, -): T => { +export const stripViewer = (val: T): T => { delete val.viewer return val } // @NOTE mutates -export const stripViewerFromPost = (postUnknown: unknown): PostView => { - if (postUnknown?.['$type'] && !isPostView(postUnknown)) { +export const stripViewerFromPost = (postUnknown: object): PostView => { + if ('$type' in postUnknown && !isPostView(postUnknown)) { throw new Error('Expected post view') } const post = postUnknown as PostView post.author = stripViewer(post.author) - const recordEmbed = - post.embed && isViewRecord(post.embed.record) - ? post.embed.record // Record from record embed - : post.embed?.['record'] && isViewRecord(post.embed['record']['record']) - ? post.embed['record']['record'] // Record from record-with-media embed + const recordEmbed = isRecordView(post.embed) + ? isViewRecord(post.embed.record) + ? post.embed.record + : undefined + : isRecordWithMediaView(post.embed) + ? isViewRecord(post.embed.record.record) + ? post.embed.record.record : undefined + : undefined if (recordEmbed) { recordEmbed.author = stripViewer(recordEmbed.author) recordEmbed.embeds?.forEach((deepEmbed) => { - const deepRecordEmbed = isViewRecord(deepEmbed.record) - ? deepEmbed.record // Record from record embed - : deepEmbed['record'] && isViewRecord(deepEmbed['record']['record']) - ? deepEmbed['record']['record'] // Record from record-with-media embed + const deepRecordEmbed = isRecordView(deepEmbed) + ? isViewRecord(deepEmbed.record) + ? deepEmbed.record + : undefined + : isRecordWithMediaView(deepEmbed) + ? isViewRecord(deepEmbed.record.record) + ? deepEmbed.record.record + : undefined : undefined if (deepRecordEmbed) { deepRecordEmbed.author = stripViewer(deepRecordEmbed.author) @@ -185,6 +195,7 @@ export const stripViewerFromPost = (postUnknown: unknown): PostView => { // @NOTE mutates export const stripViewerFromThread = (thread: T): T => { if (!isThreadViewPost(thread)) return thread + // @ts-expect-error delete thread.viewer thread.post = stripViewerFromPost(thread.post) if (isThreadViewPost(thread.parent)) { diff --git a/packages/ozone/tests/moderation-appeals.test.ts b/packages/ozone/tests/moderation-appeals.test.ts index 0c95ead78ca..19a13a59087 100644 --- a/packages/ozone/tests/moderation-appeals.test.ts +++ b/packages/ozone/tests/moderation-appeals.test.ts @@ -98,6 +98,7 @@ describe('moderation-appeals', () => { // Verify that appeal status changed when appeal report was emitted by moderator const status = await assertBobsPostStatus(REVIEWESCALATED, true) + // @ts-expect-error unspecced ? expect(status?.appealedAt).not.toBeNull() // Create a report as normal user for carol's post diff --git a/packages/ozone/tests/moderation-events.test.ts b/packages/ozone/tests/moderation-events.test.ts index 181f7e9f3f4..1313a84a7a1 100644 --- a/packages/ozone/tests/moderation-events.test.ts +++ b/packages/ozone/tests/moderation-events.test.ts @@ -13,6 +13,7 @@ import { REASONMISLEADING, REASONSPAM, } from '../src/lexicon/types/com/atproto/moderation/defs' +import { isMain as isStrongRef } from '../src/lexicon/types/com/atproto/repo/strongRef' describe('moderation-events', () => { let network: TestNetwork @@ -357,8 +358,12 @@ describe('moderation-events', () => { expect(addAndRemoveEvent.id).toEqual(addAndRemoveFinder.events[0].id) expect(addAndRemoveEvent.id).toEqual(addAndRemoveFinder.events[0].id) - expect(addAndRemoveEvent.event.add).toEqual(['L3']) - expect(addAndRemoveEvent.event.remove).toEqual(['L2']) + expect( + 'add' in addAndRemoveEvent.event && addAndRemoveEvent.event.add, + ).toEqual(['L3']) + expect( + 'remove' in addAndRemoveEvent.event && addAndRemoveEvent.event.remove, + ).toEqual(['L2']) }) it('returns events for specified collections', async () => { @@ -407,11 +412,13 @@ describe('moderation-events', () => { ]) expect(onlyStarterPackReports.events.length).toEqual(1) + assert(isStrongRef(onlyStarterPackReports.events[0].subject)) expect(onlyStarterPackReports.events[0].subject.uri).toContain( 'app.bsky.graph.starterpack', ) expect(onlyAlicesStarterPackReports.events.length).toEqual(1) + assert(isStrongRef(onlyAlicesStarterPackReports.events[0].subject)) expect(onlyAlicesStarterPackReports.events[0].subject.uri).toContain( sp.uriStr, ) @@ -439,16 +446,24 @@ describe('moderation-events', () => { // only account reports are returned, no event has a uri expect( - onlyAccountReports.events.every((e) => !e.subject.uri), + onlyAccountReports.events.every( + (e) => !('uri' in e.subject && e.subject.uri), + ), ).toBeTruthy() // only record reports are returned, all events have a uri - expect(onlyRecordReports.events.every((e) => e.subject.uri)).toBeTruthy() + expect( + onlyRecordReports.events.every( + (e) => 'uri' in e.subject && e.subject.uri, + ), + ).toBeTruthy() // only bob's account reports are returned, no events have a URI even though the subjectType is record expect( onlyReportsOnBobsAccount.events.every( - (e) => !e.subject.uri && e.subject.did === sc.dids.bob, + (e) => + !('uri' in e.subject && e.subject.uri) && + ('did' in e.subject && e.subject.did) === sc.dids.bob, ), ).toBeTruthy() }) diff --git a/packages/ozone/tests/moderation-statuses.test.ts b/packages/ozone/tests/moderation-statuses.test.ts index a8b4de52d00..ede0f41fb4b 100644 --- a/packages/ozone/tests/moderation-statuses.test.ts +++ b/packages/ozone/tests/moderation-statuses.test.ts @@ -18,6 +18,7 @@ import { REVIEWOPEN, REVIEWNONE, } from '../src/lexicon/types/tools/ozone/moderation/defs' +import { isMain as isStrongRef } from '../src/lexicon/types/com/atproto/repo/strongRef' describe('moderation-statuses', () => { let network: TestNetwork @@ -216,10 +217,14 @@ describe('moderation-statuses', () => { ]) expect(onlyStarterPackStatuses.subjectStatuses.length).toEqual(1) + assert(isStrongRef(onlyStarterPackStatuses.subjectStatuses[0].subject)) expect(onlyStarterPackStatuses.subjectStatuses[0].subject.uri).toContain( 'app.bsky.graph.starterpack', ) expect(onlyAlicesStarterPackStatuses.subjectStatuses.length).toEqual(1) + assert( + isStrongRef(onlyAlicesStarterPackStatuses.subjectStatuses[0].subject), + ) expect( onlyAlicesStarterPackStatuses.subjectStatuses[0].subject.uri, ).toEqual(sp.uriStr) @@ -247,18 +252,24 @@ describe('moderation-statuses', () => { // only account statuses are returned, no event has a uri expect( - onlyAccountStatuses.subjectStatuses.every((e) => !e.subject.uri), + onlyAccountStatuses.subjectStatuses.every( + (e) => !('uri' in e.subject && e.subject.uri), + ), ).toBeTruthy() // only record statuses are returned, all events have a uri expect( - onlyRecordStatuses.subjectStatuses.every((e) => e.subject.uri), + onlyRecordStatuses.subjectStatuses.every( + (e) => 'uri' in e.subject && e.subject.uri, + ), ).toBeTruthy() // only bob's account statuses are returned, no events have a URI even though the subjectType is record expect( onlyStatusesOnBobsAccount.subjectStatuses.every( - (e) => !e.subject.uri && e.subject.did === sc.dids.bob, + (e) => + !('uri' in e.subject && e.subject.uri) && + ('did' in e.subject && e.subject.did) === sc.dids.bob, ), ).toBeTruthy() }) diff --git a/packages/ozone/tests/moderation.test.ts b/packages/ozone/tests/moderation.test.ts index 9f4937935df..97e4b2e608e 100644 --- a/packages/ozone/tests/moderation.test.ts +++ b/packages/ozone/tests/moderation.test.ts @@ -711,7 +711,7 @@ describe('moderation', () => { }, ) { const { createLabelVals, negateLabelVals } = opts - const result = await modClient.emitEvent({ + const event = await modClient.emitEvent({ event: { $type: 'tools.ozone.moderation.defs#modEventLabel', createLabelVals, @@ -721,7 +721,7 @@ describe('moderation', () => { reason: 'Y', ...opts, }) - return result.data + return event } async function reverse( diff --git a/packages/ozone/tests/record-and-account-events.test.ts b/packages/ozone/tests/record-and-account-events.test.ts index 1c8d8b5d224..70e8dcb9838 100644 --- a/packages/ozone/tests/record-and-account-events.test.ts +++ b/packages/ozone/tests/record-and-account-events.test.ts @@ -1,15 +1,20 @@ +import assert from 'node:assert' import { - TestNetwork, - SeedClient, basicSeed, ModeratorClient, + SeedClient, + TestNetwork, } from '@atproto/dev-env' +import { isRepoRef } from '../src/lexicon/types/com/atproto/admin/defs' +import { REASONMISLEADING } from '../src/lexicon/types/com/atproto/moderation/defs' +import { isMain as isStrongRef } from '../src/lexicon/types/com/atproto/repo/strongRef' import { - ComAtprotoModerationDefs, - ToolsOzoneModerationDefs, -} from '@atproto/api' -import { REVIEWOPEN } from '../src/lexicon/types/tools/ozone/moderation/defs' -import { ToolsOzoneModerationEmitEvent as EmitModerationEvent } from '@atproto/api' + isAccountHosting, + REVIEWOPEN, + SubjectStatusView, +} from '../src/lexicon/types/tools/ozone/moderation/defs' +import { InputSchema } from '../src/lexicon/types/tools/ozone/moderation/emitEvent' + describe('record and account events on moderation subjects', () => { let network: TestNetwork let sc: SeedClient @@ -31,7 +36,7 @@ describe('record and account events on moderation subjects', () => { const getSubjectStatus = async ( subject: string, - ): Promise => { + ): Promise => { const res = await modClient.queryStatuses({ subject, }) @@ -40,7 +45,7 @@ describe('record and account events on moderation subjects', () => { describe('record events', () => { const emitRecordEvent = async ( - subject: EmitModerationEvent.InputSchema['subject'], + subject: InputSchema['subject'], op: 'create' | 'update' | 'delete', ) => { return await modClient.emitEvent( @@ -66,7 +71,7 @@ describe('record and account events on moderation subjects', () => { await sc.createReport({ reportedBy: sc.dids.carol, - reasonType: ComAtprotoModerationDefs.REASONMISLEADING, + reasonType: REASONMISLEADING, reason: 'misleading', subject: bobsPostSubject, }) @@ -85,7 +90,7 @@ describe('record and account events on moderation subjects', () => { }) describe('account/identity events', () => { const emitAccountEvent = async ( - subject: EmitModerationEvent.InputSchema['subject'], + subject: InputSchema['subject'], active: boolean, status?: 'takendown' | 'deleted' | 'deactivated' | 'suspended', ) => { @@ -112,7 +117,7 @@ describe('record and account events on moderation subjects', () => { await sc.createReport({ reportedBy: sc.dids.carol, - reasonType: ComAtprotoModerationDefs.REASONMISLEADING, + reasonType: REASONMISLEADING, reason: 'misleading', subject: carolsAccountSubject, }) @@ -121,17 +126,19 @@ describe('record and account events on moderation subjects', () => { const statusAfterDeactivation = await getSubjectStatus( carolsAccountSubject.did, ) - expect(statusAfterDeactivation?.hosting?.deactivatedAt).toBeTruthy() - expect(statusAfterDeactivation?.hosting?.status).toEqual('deactivated') - expect(statusAfterDeactivation?.reviewState).toEqual(REVIEWOPEN) + assert(isAccountHosting(statusAfterDeactivation?.hosting)) + expect(statusAfterDeactivation.hosting.deactivatedAt).toBeTruthy() + expect(statusAfterDeactivation.hosting.status).toEqual('deactivated') + expect(statusAfterDeactivation.reviewState).toEqual(REVIEWOPEN) await emitAccountEvent(carolsAccountSubject, true) const statusAfterReactivation = await getSubjectStatus( carolsAccountSubject.did, ) - expect(statusAfterReactivation?.hosting?.updatedAt).toBeTruthy() - expect(statusAfterReactivation?.hosting?.status).toEqual('active') - expect(statusAfterReactivation?.hosting?.deletedAt).toBeFalsy() + assert(isAccountHosting(statusAfterReactivation?.hosting)) + expect(statusAfterReactivation.hosting.updatedAt).toBeTruthy() + expect(statusAfterReactivation.hosting.status).toEqual('active') + expect(statusAfterReactivation.hosting.deletedAt).toBeFalsy() }) it('gets statuses by hosting properties', async () => { @@ -175,9 +182,11 @@ describe('record and account events on moderation subjects', () => { expect(deactivatedOrDeletedStatuses.length).toEqual(3) expect(deletedStatusesInPastDay.length).toEqual(2) + assert(isStrongRef(deletedStatusesInPastDay[0]?.subject)) expect(deletedStatusesInPastDay[0]?.subject.uri).toEqual( sc.posts[sc.dids.bob][1].ref.uriStr, ) + assert(isRepoRef(deletedStatusesInPastDay[1]?.subject)) expect(deletedStatusesInPastDay[1]?.subject.did).toEqual(sc.dids.bob) expect(deletedStatusesBeforeYesterday.length).toEqual(0) }) diff --git a/packages/pds/src/actor-store/record/reader.ts b/packages/pds/src/actor-store/record/reader.ts index 7c7b3383439..67bf7847724 100644 --- a/packages/pds/src/actor-store/record/reader.ts +++ b/packages/pds/src/actor-store/record/reader.ts @@ -37,7 +37,7 @@ export class RecordReader { rkeyStart?: string rkeyEnd?: string includeSoftDeleted?: boolean - }): Promise<{ uri: string; cid: string; value: object }[]> { + }): Promise<{ uri: string; cid: string; value: Record }[]> { const { collection, limit, @@ -92,7 +92,7 @@ export class RecordReader { ): Promise<{ uri: string cid: string - value: object + value: Record indexedAt: string takedownRef: string | null } | null> { diff --git a/packages/pds/src/api/app/bsky/feed/getActorLikes.ts b/packages/pds/src/api/app/bsky/feed/getActorLikes.ts index ef7ff9a5550..02b33b9be2a 100644 --- a/packages/pds/src/api/app/bsky/feed/getActorLikes.ts +++ b/packages/pds/src/api/app/bsky/feed/getActorLikes.ts @@ -1,6 +1,6 @@ import { Server } from '../../../../lexicon' import AppContext from '../../../../context' -import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getAuthorFeed' +import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getActorLikes' import { LocalViewer, pipethroughReadAfterWrite, diff --git a/packages/pds/src/api/app/bsky/feed/getPostThread.ts b/packages/pds/src/api/app/bsky/feed/getPostThread.ts index 8a748c33063..0b502090684 100644 --- a/packages/pds/src/api/app/bsky/feed/getPostThread.ts +++ b/packages/pds/src/api/app/bsky/feed/getPostThread.ts @@ -23,6 +23,7 @@ import { formatMungedResponse, } from '../../../../read-after-write' import { ids } from '../../../../lexicon/lexicons' +import { $Typed } from '../../../../lexicon/util' export default function (server: Server, ctx: AppContext) { const { bskyAppView } = ctx.cfg @@ -99,12 +100,12 @@ const getPostThreadMunge = async ( const addPostsToThread = async ( localViewer: LocalViewer, - original: ThreadViewPost, + original: $Typed, posts: RecordDescript[], ) => { const inThread = findPostsInThread(original, posts) if (inThread.length === 0) return original - let thread: ThreadViewPost = original + let thread: $Typed = original for (const record of inThread) { thread = await insertIntoThreadReplies(localViewer, thread, record) } @@ -125,9 +126,9 @@ const findPostsInThread = ( const insertIntoThreadReplies = async ( localViewer: LocalViewer, - view: ThreadViewPost, + view: $Typed, descript: RecordDescript, -): Promise => { +): Promise<$Typed> => { if (descript.record.reply?.parent.uri === view.post.uri) { const postView = await threadPostView(localViewer, descript) if (!postView) return view @@ -154,7 +155,7 @@ const insertIntoThreadReplies = async ( const threadPostView = async ( localViewer: LocalViewer, descript: RecordDescript, -): Promise => { +): Promise<$Typed | null> => { const postView = await localViewer.getPost(descript) if (!postView) return null return { diff --git a/packages/pds/src/api/com/atproto/identity/signPlcOperation.ts b/packages/pds/src/api/com/atproto/identity/signPlcOperation.ts index 3b1f646c671..a7001835a45 100644 --- a/packages/pds/src/api/com/atproto/identity/signPlcOperation.ts +++ b/packages/pds/src/api/com/atproto/identity/signPlcOperation.ts @@ -52,8 +52,16 @@ export default function (server: Server, ctx: AppContext) { rotationKeys: input.body.rotationKeys ?? lastOp.rotationKeys, alsoKnownAs: input.body.alsoKnownAs ?? lastOp.alsoKnownAs, verificationMethods: - input.body.verificationMethods ?? lastOp.verificationMethods, - services: input.body.services ?? lastOp.services, + // @TODO: actually validate instead of type casting + (input.body.verificationMethods as + | undefined + | Record) ?? lastOp.verificationMethods, + services: + // @TODO: actually validate instead of type casting + (input.body.services as + | undefined + | Record) ?? + lastOp.services, }), ) return { diff --git a/packages/pds/src/api/com/atproto/identity/updateHandle.ts b/packages/pds/src/api/com/atproto/identity/updateHandle.ts index df0ad86afe4..bc97fa39169 100644 --- a/packages/pds/src/api/com/atproto/identity/updateHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/updateHandle.ts @@ -32,6 +32,7 @@ export default function (server: Server, ctx: AppContext) { // -> entryway(identity.updateHandle) [update handle, submit plc op] // -> pds(admin.updateAccountHandle) [track handle, sequence handle update] await ctx.entrywayAgent.com.atproto.identity.updateHandle( + // @ts-expect-error "did" os not in the schema { did: requester, handle: input.body.handle }, await ctx.serviceAuthHeaders( auth.credentials.did, diff --git a/packages/pds/src/pipethrough.ts b/packages/pds/src/pipethrough.ts index b326d5fba8b..a80f8b4a7ad 100644 --- a/packages/pds/src/pipethrough.ts +++ b/packages/pds/src/pipethrough.ts @@ -592,7 +592,7 @@ function* responseHeaders( // Utils // ------------------- -export const PRIVILEGED_METHODS = new Set([ +export const PRIVILEGED_METHODS = new Set([ ids.ChatBskyActorDeleteAccount, ids.ChatBskyActorExportAccountData, ids.ChatBskyConvoDeleteMessageForSelf, @@ -613,7 +613,7 @@ export const PRIVILEGED_METHODS = new Set([ // These endpoints are related to account management and must be used directly, // not proxied or service-authed. Service auth may be utilized between PDS and // entryway for these methods. -export const PROTECTED_METHODS = new Set([ +export const PROTECTED_METHODS = new Set([ ids.ComAtprotoAdminSendEmail, ids.ComAtprotoIdentityRequestPlcOperationSignature, ids.ComAtprotoIdentitySignPlcOperation, diff --git a/packages/pds/src/read-after-write/viewer.ts b/packages/pds/src/read-after-write/viewer.ts index 8f66517ab97..6e25115d1ba 100644 --- a/packages/pds/src/read-after-write/viewer.ts +++ b/packages/pds/src/read-after-write/viewer.ts @@ -20,10 +20,12 @@ import { import { ListView } from '../lexicon/types/app/bsky/graph/defs' import { Main as EmbedImages, + View as EmbedImagesView, isMain as isEmbedImages, } from '../lexicon/types/app/bsky/embed/images' import { Main as EmbedExternal, + View as EmbedExternalView, isMain as isEmbedExternal, } from '../lexicon/types/app/bsky/embed/external' import { @@ -39,6 +41,7 @@ import { import { ActorStoreReader } from '../actor-store' import { LocalRecords, RecordDescript } from './types' import { AccountManager } from '../account-manager' +import { $Typed } from '../lexicon/util' type CommonSignedUris = 'avatar' | 'banner' | 'feed_thumbnail' | 'feed_fullsize' @@ -194,7 +197,9 @@ export class LocalViewer { } } - async formatSimpleEmbed(embed: EmbedImages | EmbedExternal) { + async formatSimpleEmbed( + embed: $Typed | $Typed, + ): Promise<$Typed | $Typed> { if (isEmbedImages(embed)) { const images = embed.images.map((img) => ({ thumb: this.getImageUrl('feed_thumbnail', img.image.ref.toString()), @@ -220,11 +225,14 @@ export class LocalViewer { }, } } else { + // @ts-expect-error throw new TypeError(`Unexpected embed type: ${embed.$type}`) } } - async formatRecordEmbed(embed: EmbedRecord): Promise { + async formatRecordEmbed( + embed: EmbedRecord, + ): Promise<$Typed> { const view = await this.formatRecordEmbedInternal(embed) return { $type: 'app.bsky.embed.record#view', @@ -240,7 +248,9 @@ export class LocalViewer { private async formatRecordEmbedInternal( embed: EmbedRecord, - ): Promise { + ): Promise< + null | $Typed | $Typed | $Typed + > { if (!this.appViewAgent || !this.appviewDid) { return null } @@ -300,10 +310,9 @@ export class LocalViewer { } } - updateProfileViewBasic( - view: ProfileViewBasic, - record: ProfileRecord, - ): ProfileViewBasic { + updateProfileViewBasic< + T extends ProfileViewDetailed | ProfileViewBasic | ProfileView, + >(view: T, record: ProfileRecord): T { return { ...view, displayName: record.displayName, @@ -313,17 +322,19 @@ export class LocalViewer { } } - updateProfileView(view: ProfileView, record: ProfileRecord): ProfileView { + updateProfileView< + T extends ProfileViewDetailed | ProfileViewBasic | ProfileView, + >(view: T, record: ProfileRecord): T { return { ...this.updateProfileViewBasic(view, record), description: record.description, } } - updateProfileDetailed( - view: ProfileViewDetailed, + updateProfileDetailed( + view: T, record: ProfileRecord, - ): ProfileViewDetailed { + ): T { return { ...this.updateProfileView(view, record), banner: record.banner diff --git a/packages/pds/src/repo/prepare.ts b/packages/pds/src/repo/prepare.ts index bbe4f24d2e5..a5250199b8c 100644 --- a/packages/pds/src/repo/prepare.ts +++ b/packages/pds/src/repo/prepare.ts @@ -32,12 +32,12 @@ import { ValidationStatus, } from './types' import * as lex from '../lexicon/lexicons' -import { isRecord as isFeedGenerator } from '../lexicon/types/app/bsky/feed/generator' -import { isRecord as isStarterPack } from '../lexicon/types/app/bsky/graph/starterpack' -import { isRecord as isPost } from '../lexicon/types/app/bsky/feed/post' -import { isTag } from '../lexicon/types/app/bsky/richtext/facet' -import { isRecord as isList } from '../lexicon/types/app/bsky/graph/list' -import { isRecord as isProfile } from '../lexicon/types/app/bsky/actor/profile' +import { isValidRecord as isValidFeedGenerator } from '../lexicon/types/app/bsky/feed/generator' +import { isValidRecord as isValidStarterPack } from '../lexicon/types/app/bsky/graph/starterpack' +import { isValidRecord as isValidPost } from '../lexicon/types/app/bsky/feed/post' +import { isValidTag } from '../lexicon/types/app/bsky/richtext/facet' +import { isValidRecord as isValidList } from '../lexicon/types/app/bsky/graph/list' +import { isValidRecord as isValidProfile } from '../lexicon/types/app/bsky/actor/profile' import { hasExplicitSlur } from '../handle/explicit-slurs' export const assertValidRecordWithStatus = ( @@ -222,30 +222,31 @@ async function cidForSafeRecord(record: RepoRecord) { } function assertNoExplicitSlurs(rkey: string, record: RepoRecord) { - let toCheck = '' - if (isProfile(record)) { - toCheck += ' ' + record.displayName - } else if (isList(record)) { - toCheck += ' ' + record.name - } else if (isStarterPack(record)) { - toCheck += ' ' + record.name - } else if (isFeedGenerator(record)) { - toCheck += ' ' + rkey - toCheck += ' ' + record.displayName - } else if (isPost(record)) { + const toCheck: string[] = [] + + if (isValidProfile(record)) { + if (record.displayName) toCheck.push(record.displayName) + } else if (isValidList(record)) { + toCheck.push(record.name) + } else if (isValidStarterPack(record)) { + toCheck.push(record.name) + } else if (isValidFeedGenerator(record)) { + toCheck.push(rkey) + toCheck.push(record.displayName) + } else if (isValidPost(record)) { if (record.tags) { - toCheck += record.tags.join(' ') + toCheck.push(...record.tags) } for (const facet of record.facets || []) { for (const feat of facet.features) { - if (isTag(feat)) { - toCheck += ' ' + feat.tag + if (isValidTag(feat)) { + toCheck.push(feat.tag) } } } } - if (hasExplicitSlur(toCheck)) { + if (hasExplicitSlur(toCheck.join(' '))) { throw new InvalidRecordError('Unacceptable slur in record') } } diff --git a/packages/pds/tests/_util.ts b/packages/pds/tests/_util.ts index ee57c62b9fe..a13285786fd 100644 --- a/packages/pds/tests/_util.ts +++ b/packages/pds/tests/_util.ts @@ -1,6 +1,9 @@ import { AtUri } from '@atproto/syntax' import { CID } from 'multiformats/cid' -import { FeedViewPost } from '../src/lexicon/types/app/bsky/feed/defs' +import { + FeedViewPost, + isReasonRepost, +} from '../src/lexicon/types/app/bsky/feed/defs' import { lexToJson } from '@atproto/lexicon' // Swap out identifiers and dates with stable @@ -96,8 +99,10 @@ export const forSnapshot = (obj: unknown) => { export const getOriginator = (item: FeedViewPost) => { if (!item.reason) { return item.post.author.did + } else if (isReasonRepost(item.reason)) { + return item.reason.by.did } else { - return (item.reason.by as { [did: string]: string }).did + throw new Error('Unexpected reason') } } diff --git a/packages/pds/tests/app-passwords.test.ts b/packages/pds/tests/app-passwords.test.ts index 35bae7528c1..68354da3a6d 100644 --- a/packages/pds/tests/app-passwords.test.ts +++ b/packages/pds/tests/app-passwords.test.ts @@ -73,7 +73,7 @@ describe('app_passwords', () => { it('allows actions to be performed from app', async () => { await appAgent.api.app.bsky.feed.post.create( { - repo: appAgent.session?.did, + repo: appAgent.assertDid, }, { text: 'Testing testing', @@ -82,7 +82,7 @@ describe('app_passwords', () => { ) await priviAgent.api.app.bsky.feed.post.create( { - repo: priviAgent.session?.did, + repo: priviAgent.assertDid, }, { text: 'testing again', @@ -142,7 +142,7 @@ describe('app_passwords', () => { // allows any access auth await appAgent.api.app.bsky.feed.post.create( { - repo: appAgent.session?.did, + repo: appAgent.assertDid, }, { text: 'Testing testing', @@ -188,7 +188,7 @@ describe('app_passwords', () => { // allows any access auth await priviAgent.api.app.bsky.feed.post.create( { - repo: priviAgent.session?.did, + repo: priviAgent.assertDid, }, { text: 'Testing testing', diff --git a/packages/pds/tests/crud.test.ts b/packages/pds/tests/crud.test.ts index 05eace453c4..15e66d64afd 100644 --- a/packages/pds/tests/crud.test.ts +++ b/packages/pds/tests/crud.test.ts @@ -1,4 +1,5 @@ -import fs from 'fs/promises' +import assert from 'node:assert' +import fs from 'node:fs/promises' import { AtUri } from '@atproto/syntax' import { AtpAgent } from '@atproto/api' import { BlobRef } from '@atproto/lexicon' @@ -7,6 +8,7 @@ import { cidForCbor, TID, ui8ToArrayBuffer } from '@atproto/common' import { BlobNotFoundError } from '@atproto/repo' import * as Post from '../src/lexicon/types/app/bsky/feed/post' import { forSnapshot, paginateAll } from './_util' +import { isMain as isImagesEmbed } from '../src/lexicon/types/app/bsky/embed/images' import AppContext from '../src/context' import { ids, lexicons } from '../src/lexicon/lexicons' @@ -186,7 +188,8 @@ describe('crud operations', () => { rkey: postUri.rkey, repo: aliceAgent.accountDid, }) - const images = post.value.embed?.images as { image: BlobRef }[] + assert(isImagesEmbed(post.value.embed)) + const images = post.value.embed.images expect(images.length).toEqual(1) expect(uploaded.ref.equals(images[0].image.ref)).toBeTruthy() // Ensure that the uploaded image is now in the blobstore, i.e. doesn't throw BlobNotFoundError @@ -771,7 +774,6 @@ describe('crud operations', () => { writes: [ { $type: `${ids.ComAtprotoRepoApplyWrites}#create`, - action: 'create', collection: ids.AppBskyFeedPost, value: { $type: ids.AppBskyFeedPost, @@ -781,14 +783,12 @@ describe('crud operations', () => { }, { $type: `${ids.ComAtprotoRepoApplyWrites}#update`, - action: 'update', collection: 'com.example.record', rkey: new AtUri(existing1.data.uri).rkey, value: {}, }, { $type: `${ids.ComAtprotoRepoApplyWrites}#delete`, - action: 'delete', collection: 'com.example.record', rkey: new AtUri(existing2.data.uri).rkey, }, @@ -1157,7 +1157,6 @@ describe('crud operations', () => { writes: [ { $type: `${ids.ComAtprotoRepoApplyWrites}#create`, - action: 'create', collection: ids.AppBskyFeedPost, value: { $type: ids.AppBskyFeedPost, ...postRecord() }, }, @@ -1182,7 +1181,6 @@ describe('crud operations', () => { writes: [ { $type: `${ids.ComAtprotoRepoApplyWrites}#create`, - action: 'create', collection: ids.AppBskyFeedPost, value: { $type: ids.AppBskyFeedPost, ...postRecord() }, }, diff --git a/packages/pds/tests/file-uploads.test.ts b/packages/pds/tests/file-uploads.test.ts index b38b4103682..9c1b358cbba 100644 --- a/packages/pds/tests/file-uploads.test.ts +++ b/packages/pds/tests/file-uploads.test.ts @@ -186,6 +186,7 @@ describe('file uploads', () => { repo: alice, rkey: 'self', }) + // @ts-expect-error "cid" is not documented as "com.atproto.repo.uploadBlob" output expect((profileA.value as any).avatar.cid).toEqual(uploadA.cid) await sc.updateProfile(bob, { displayName: 'Bob', @@ -195,6 +196,7 @@ describe('file uploads', () => { repo: bob, rkey: 'self', }) + // @ts-expect-error "cid" is not documented as "com.atproto.repo.uploadBlob" output expect((profileB.value as any).avatar.cid).toEqual(uploadA.cid) const { data: uploadAfterPermanent } = await agent.api.com.atproto.repo.uploadBlob(file, { diff --git a/packages/pds/tests/moderation.test.ts b/packages/pds/tests/moderation.test.ts index 50fc65d9326..27a0bb7da1b 100644 --- a/packages/pds/tests/moderation.test.ts +++ b/packages/pds/tests/moderation.test.ts @@ -1,21 +1,28 @@ +import assert from 'node:assert' import { AtpAgent } from '@atproto/api' -import { TestNetworkNoAppView, ImageRef, SeedClient } from '@atproto/dev-env' +import { ImageRef, SeedClient, TestNetworkNoAppView } from '@atproto/dev-env' import { BlobNotFoundError } from '@atproto/repo' -import basicSeed from './seeds/basic' import { + isRepoBlobRef, + isRepoRef, RepoBlobRef, RepoRef, } from '../src/lexicon/types/com/atproto/admin/defs' -import { Main as StrongRef } from '../src/lexicon/types/com/atproto/repo/strongRef' +import { + Main as StrongRef, + isMain as isStrongRef, +} from '../src/lexicon/types/com/atproto/repo/strongRef' +import basicSeed from './seeds/basic' +import { $Typed } from '../src/lexicon/util' describe('moderation', () => { let network: TestNetworkNoAppView let agent: AtpAgent let sc: SeedClient - let repoSubject: RepoRef - let recordSubject: StrongRef - let blobSubject: RepoBlobRef + let repoSubject: $Typed + let recordSubject: $Typed + let blobSubject: $Typed let blobRef: ImageRef beforeAll(async () => { @@ -66,6 +73,7 @@ describe('moderation', () => { }, { headers: network.pds.adminAuthHeaders() }, ) + assert(isRepoRef(res.data.subject)) expect(res.data.subject.did).toEqual(sc.dids.bob) expect(res.data.takedown?.applied).toBe(true) expect(res.data.takedown?.ref).toBe('test-repo') @@ -88,6 +96,7 @@ describe('moderation', () => { }, { headers: network.pds.adminAuthHeaders() }, ) + assert(isRepoRef(res.data.subject)) expect(res.data.subject.did).toEqual(sc.dids.bob) expect(res.data.takedown?.applied).toBe(false) expect(res.data.takedown?.ref).toBeUndefined() @@ -110,6 +119,7 @@ describe('moderation', () => { }, { headers: network.pds.adminAuthHeaders() }, ) + assert(isStrongRef(res.data.subject)) expect(res.data.subject.uri).toEqual(recordSubject.uri) expect(res.data.takedown?.applied).toBe(true) expect(res.data.takedown?.ref).toBe('test-record') @@ -132,6 +142,7 @@ describe('moderation', () => { }, { headers: network.pds.adminAuthHeaders() }, ) + assert(isStrongRef(res.data.subject)) expect(res.data.subject.uri).toEqual(recordSubject.uri) expect(res.data.takedown?.applied).toBe(false) expect(res.data.takedown?.ref).toBeUndefined() @@ -156,7 +167,9 @@ describe('moderation', () => { }, { headers: network.pds.adminAuthHeaders() }, ) + assert(isRepoBlobRef(res.data.subject)) expect(res.data.subject.did).toEqual(blobSubject.did) + assert(isRepoBlobRef(res.data.subject)) expect(res.data.subject.cid).toEqual(blobSubject.cid) expect(res.data.takedown?.applied).toBe(true) expect(res.data.takedown?.ref).toBe('test-blob') diff --git a/packages/pds/tests/moderator-auth.test.ts b/packages/pds/tests/moderator-auth.test.ts index 3330ba28f81..2f8b44f7b5b 100644 --- a/packages/pds/tests/moderator-auth.test.ts +++ b/packages/pds/tests/moderator-auth.test.ts @@ -1,18 +1,20 @@ +import assert from 'node:assert' import { AtpAgent } from '@atproto/api' import { TestNetworkNoAppView, SeedClient } from '@atproto/dev-env' import { Keypair, Secp256k1Keypair } from '@atproto/crypto' import { createServiceAuthHeaders } from '@atproto/xrpc-server' import * as plc from '@did-plc/lib' import usersSeed from './seeds/users' -import { RepoRef } from '../src/lexicon/types/com/atproto/admin/defs' +import { isRepoRef, RepoRef } from '../src/lexicon/types/com/atproto/admin/defs' import { ids } from '../src/lexicon/lexicons' +import { $Typed } from '../src/lexicon/util' describe('moderator auth', () => { let network: TestNetworkNoAppView let agent: AtpAgent let sc: SeedClient - let repoSubject: RepoRef + let repoSubject: $Typed let modServiceDid: string let altModDid: string @@ -103,6 +105,7 @@ describe('moderator auth', () => { }, getHeaders, ) + assert(isRepoRef(res.data.subject)) expect(res.data.subject.did).toBe(repoSubject.did) expect(res.data.takedown?.applied).toBe(true) }) diff --git a/packages/pds/tests/preferences.test.ts b/packages/pds/tests/preferences.test.ts index 93abdb61bba..cf3f2ebb6cc 100644 --- a/packages/pds/tests/preferences.test.ts +++ b/packages/pds/tests/preferences.test.ts @@ -179,6 +179,7 @@ describe('user preferences', () => { { preferences: [ { $type: 'app.bsky.actor.defs#adultContentPref', enabled: false }, + // @ts-expect-error this is what we are testing ! { label: 'dogs', visibility: 'warn', diff --git a/packages/pds/tests/proxied/admin.test.ts b/packages/pds/tests/proxied/admin.test.ts index 1f51d2df9f5..863757d5c54 100644 --- a/packages/pds/tests/proxied/admin.test.ts +++ b/packages/pds/tests/proxied/admin.test.ts @@ -113,6 +113,7 @@ describe('proxies admin requests', () => { cid: post.ref.cidStr, }, createdBy: 'did:example:admin', + // @ts-expect-error reason: 'Y', }, { @@ -129,6 +130,7 @@ describe('proxies admin requests', () => { did: sc.dids.bob, }, createdBy: 'did:example:admin', + // @ts-expect-error reason: 'Y', }, { @@ -213,6 +215,7 @@ describe('proxies admin requests', () => { did: sc.dids.alice, }, createdBy: 'did:example:admin', + // @ts-expect-error reason: 'Y', createLabelVals: ['dogs'], negateLabelVals: ['cats'], @@ -244,6 +247,7 @@ describe('proxies admin requests', () => { $type: 'tools.ozone.moderation.defs#modEventReverseTakedown', }, createdBy: 'did:example:admin', + // @ts-expect-error reason: 'X', }, { @@ -276,6 +280,7 @@ describe('proxies admin requests', () => { cid: post.ref.cidStr, }, createdBy: 'did:example:admin', + // @ts-expect-error reason: 'Y', createLabelVals: ['dogs'], negateLabelVals: ['cats'], @@ -308,6 +313,7 @@ describe('proxies admin requests', () => { }, event: { $type: 'tools.ozone.moderation.defs#modEventReverseTakedown' }, createdBy: 'did:example:admin', + // @ts-expect-error reason: 'X', }, { diff --git a/packages/pds/tests/proxied/read-after-write.test.ts b/packages/pds/tests/proxied/read-after-write.test.ts index 41b650e09ba..edb7a13797a 100644 --- a/packages/pds/tests/proxied/read-after-write.test.ts +++ b/packages/pds/tests/proxied/read-after-write.test.ts @@ -4,10 +4,13 @@ import { AtpAgent } from '@atproto/api' import { request } from 'undici' import { TestNetwork, SeedClient, RecordRef } from '@atproto/dev-env' import basicSeed from '../seeds/basic' -import { ThreadViewPost } from '../../src/lexicon/types/app/bsky/feed/defs' -import { View as RecordEmbedView } from '../../src/lexicon/types/app/bsky/embed/record' -import { View as ExternalEmbedView } from '../../src/lexicon/types/app/bsky/embed/external' -import { View as ImagesEmbedView } from '../../src/lexicon/types/app/bsky/embed/images' +import { + isThreadViewPost, + ThreadViewPost, +} from '../../src/lexicon/types/app/bsky/feed/defs' +import { isView as isRecordEmbedView } from '../../src/lexicon/types/app/bsky/embed/record' +import { isView as isExternalEmbedView } from '../../src/lexicon/types/app/bsky/embed/external' +import { isView as isImagesEmbedView } from '../../src/lexicon/types/app/bsky/embed/images' describe('proxy read after write', () => { let network: TestNetwork @@ -202,11 +205,17 @@ describe('proxy read after write', () => { { uri: sc.posts[alice][2].ref.uriStr }, { headers: { ...sc.getHeaders(alice) } }, ) - const replies = res.data.thread.replies as ThreadViewPost[] + assert(isThreadViewPost(res.data.thread)) + // @ts-ignore "pnpm verify:types" fails though VSCode doesn't complain + assert(res.data.thread.replies, 'replies is undefined') + // @ts-ignore "pnpm verify:types" fails though VSCode doesn't complain + const { replies } = res.data.thread expect(replies.length).toBe(1) + assert(isThreadViewPost(replies[0])) expect(replies[0].post.uri).toEqual(replyRes1.uri) - const imgs = replies[0].post.embed as ImagesEmbedView - expect(imgs.images[0].fullsize).toEqual( + const { embed } = replies[0].post + assert(isImagesEmbedView(embed)) + expect(embed.images[0].fullsize).toEqual( util.format( network.pds.ctx.cfg.bskyAppView.cdnUrlPattern, 'feed_fullsize', @@ -214,13 +223,14 @@ describe('proxy read after write', () => { img.image.ref.toString(), ), ) - expect(imgs.images[0].aspectRatio).toEqual({ height: 2, width: 1 }) - expect(imgs.images[0].alt).toBe('alt text') - expect(replies[0].replies?.length).toBe(1) - // @ts-ignore + expect(embed.images[0].aspectRatio).toEqual({ height: 2, width: 1 }) + expect(embed.images[0].alt).toBe('alt text') + assert(replies[0].replies, 'replies[0].replies is undefined') + expect(replies[0].replies.length).toBe(1) + assert(isThreadViewPost(replies[0].replies[0])) expect(replies[0].replies[0].post.uri).toEqual(replyRes2.uri) - // @ts-ignore - const external = replies[0].replies[0].post.embed as ExternalEmbedView + const external = replies[0].replies[0].post.embed + assert(isExternalEmbedView(external)) expect(external.external.title).toEqual('TestImage') expect(external.external.thumb).toEqual( util.format( @@ -253,10 +263,16 @@ describe('proxy read after write', () => { { uri: sc.posts[carol][0].ref.uriStr }, { headers: { ...sc.getHeaders(alice) } }, ) - const replies = res.data.thread.replies as ThreadViewPost[] + assert(isThreadViewPost(res.data.thread)) + // @ts-ignore "pnpm verify:types" fails though VSCode doesn't complain + assert(res.data.thread.replies, 'replies is undefined') + // @ts-ignore "pnpm verify:types" fails though VSCode doesn't complain + const { replies } = res.data.thread expect(replies.length).toBe(1) + assert(isThreadViewPost(replies[0])) expect(replies[0].post.uri).toEqual(replyRes.uri) - const embed = replies[0].post.embed as RecordEmbedView + const embed = replies[0].post.embed + assert(isRecordEmbedView(embed)) expect(embed.record.uri).toEqual(sc.posts[alice][0].ref.uriStr) }) diff --git a/tsconfig/browser.json b/tsconfig/browser.json index e60c4970688..baa8f8e16d6 100644 --- a/tsconfig/browser.json +++ b/tsconfig/browser.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "./base.json", "compilerOptions": { - "lib": ["ES2022", "DOM", "DOM.Iterable", "ESNext.Disposable"], + "lib": ["ES2023", "DOM", "DOM.Iterable", "ESNext.Disposable"], "jsx": "react-jsx" } } diff --git a/tsconfig/isomorphic.json b/tsconfig/isomorphic.json index 43bff8bf979..36221a23051 100644 --- a/tsconfig/isomorphic.json +++ b/tsconfig/isomorphic.json @@ -8,7 +8,7 @@ // https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1685 // https://github.com/microsoft/TypeScript/issues/31535 // https://github.com/microsoft/TypeScript/issues/41727 - "lib": ["ES2022", "DOM", "DOM.Iterable"], + "lib": ["ES2023", "DOM", "DOM.Iterable"], "types": ["node"] } } diff --git a/tsconfig/node.json b/tsconfig/node.json index f69da239387..3e59f51fa18 100644 --- a/tsconfig/node.json +++ b/tsconfig/node.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "./base.json", "compilerOptions": { - "lib": ["ES2022", "ScriptHost"], + "lib": ["ES2023", "ScriptHost"], "types": ["node"] } } diff --git a/tsconfig/nodenext.json b/tsconfig/nodenext.json index 5072557d696..c541e7be093 100644 --- a/tsconfig/nodenext.json +++ b/tsconfig/nodenext.json @@ -2,10 +2,10 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "./base.json", "compilerOptions": { - "lib": ["ES2022", "ScriptHost"], + "lib": ["ES2023", "ScriptHost"], "types": ["node"], "module": "Node16", - "target": "ES2022", + "target": "ES2023", "moduleResolution": "Node16" } } diff --git a/tsconfig/tests.json b/tsconfig/tests.json index 07573723164..602bc4c4d76 100644 --- a/tsconfig/tests.json +++ b/tsconfig/tests.json @@ -3,7 +3,7 @@ "extends": "./node.json", "compilerOptions": { "types": ["node", "jest"], - "lib": ["ES2022", "DOM", "DOM.Iterable"], + "lib": ["ES2023", "DOM", "DOM.Iterable"], "noEmit": true } } From 34759700adc3a843706ff1ba14f43ca618abbed0 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Thu, 12 Dec 2024 17:23:48 +0100 Subject: [PATCH 2/2] codegen --- packages/api/src/client/index.ts | 190 +++---- packages/api/src/client/lexicons.ts | 2 +- .../src/client/types/app/bsky/actor/defs.ts | 304 +++++++---- .../types/app/bsky/actor/getPreferences.ts | 3 +- .../client/types/app/bsky/actor/getProfile.ts | 2 +- .../types/app/bsky/actor/getProfiles.ts | 3 +- .../types/app/bsky/actor/getSuggestions.ts | 3 +- .../client/types/app/bsky/actor/profile.ts | 15 +- .../types/app/bsky/actor/putPreferences.ts | 3 +- .../types/app/bsky/actor/searchActors.ts | 3 +- .../app/bsky/actor/searchActorsTypeahead.ts | 3 +- .../src/client/types/app/bsky/embed/defs.ts | 12 +- .../client/types/app/bsky/embed/external.ts | 42 +- .../src/client/types/app/bsky/embed/images.ts | 42 +- .../src/client/types/app/bsky/embed/record.ts | 98 ++-- .../types/app/bsky/embed/recordWithMedia.ts | 38 +- .../src/client/types/app/bsky/embed/video.ts | 32 +- .../src/client/types/app/bsky/feed/defs.ts | 244 +++++---- .../app/bsky/feed/describeFeedGenerator.ts | 23 +- .../client/types/app/bsky/feed/generator.ts | 15 +- .../types/app/bsky/feed/getActorFeeds.ts | 3 +- .../types/app/bsky/feed/getActorLikes.ts | 3 +- .../types/app/bsky/feed/getAuthorFeed.ts | 3 +- .../src/client/types/app/bsky/feed/getFeed.ts | 3 +- .../types/app/bsky/feed/getFeedGenerator.ts | 3 +- .../types/app/bsky/feed/getFeedGenerators.ts | 3 +- .../types/app/bsky/feed/getFeedSkeleton.ts | 3 +- .../client/types/app/bsky/feed/getLikes.ts | 13 +- .../client/types/app/bsky/feed/getListFeed.ts | 3 +- .../types/app/bsky/feed/getPostThread.ts | 11 +- .../client/types/app/bsky/feed/getPosts.ts | 3 +- .../client/types/app/bsky/feed/getQuotes.ts | 3 +- .../types/app/bsky/feed/getRepostedBy.ts | 3 +- .../types/app/bsky/feed/getSuggestedFeeds.ts | 3 +- .../client/types/app/bsky/feed/getTimeline.ts | 3 +- .../src/client/types/app/bsky/feed/like.ts | 11 +- .../src/client/types/app/bsky/feed/post.ts | 57 +- .../client/types/app/bsky/feed/postgate.ts | 23 +- .../src/client/types/app/bsky/feed/repost.ts | 11 +- .../client/types/app/bsky/feed/searchPosts.ts | 3 +- .../types/app/bsky/feed/sendInteractions.ts | 7 +- .../client/types/app/bsky/feed/threadgate.ts | 49 +- .../src/client/types/app/bsky/graph/block.ts | 11 +- .../src/client/types/app/bsky/graph/defs.ts | 94 ++-- .../src/client/types/app/bsky/graph/follow.ts | 11 +- .../app/bsky/graph/getActorStarterPacks.ts | 3 +- .../client/types/app/bsky/graph/getBlocks.ts | 3 +- .../types/app/bsky/graph/getFollowers.ts | 3 +- .../client/types/app/bsky/graph/getFollows.ts | 3 +- .../types/app/bsky/graph/getKnownFollowers.ts | 3 +- .../client/types/app/bsky/graph/getList.ts | 3 +- .../types/app/bsky/graph/getListBlocks.ts | 3 +- .../types/app/bsky/graph/getListMutes.ts | 3 +- .../client/types/app/bsky/graph/getLists.ts | 3 +- .../client/types/app/bsky/graph/getMutes.ts | 3 +- .../types/app/bsky/graph/getRelationships.ts | 9 +- .../types/app/bsky/graph/getStarterPack.ts | 3 +- .../types/app/bsky/graph/getStarterPacks.ts | 3 +- .../bsky/graph/getSuggestedFollowsByActor.ts | 3 +- .../src/client/types/app/bsky/graph/list.ts | 15 +- .../client/types/app/bsky/graph/listblock.ts | 11 +- .../client/types/app/bsky/graph/listitem.ts | 11 +- .../client/types/app/bsky/graph/muteActor.ts | 3 +- .../types/app/bsky/graph/muteActorList.ts | 3 +- .../client/types/app/bsky/graph/muteThread.ts | 3 +- .../app/bsky/graph/searchStarterPacks.ts | 3 +- .../types/app/bsky/graph/starterpack.ts | 21 +- .../types/app/bsky/graph/unmuteActor.ts | 3 +- .../types/app/bsky/graph/unmuteActorList.ts | 3 +- .../types/app/bsky/graph/unmuteThread.ts | 3 +- .../src/client/types/app/bsky/labeler/defs.ts | 48 +- .../types/app/bsky/labeler/getServices.ts | 9 +- .../client/types/app/bsky/labeler/service.ts | 15 +- .../app/bsky/notification/getUnreadCount.ts | 3 +- .../bsky/notification/listNotifications.ts | 15 +- .../app/bsky/notification/putPreferences.ts | 3 +- .../app/bsky/notification/registerPush.ts | 3 +- .../types/app/bsky/notification/updateSeen.ts | 3 +- .../client/types/app/bsky/richtext/facet.ts | 54 +- .../client/types/app/bsky/unspecced/defs.ts | 43 +- .../types/app/bsky/unspecced/getConfig.ts | 3 +- .../unspecced/getPopularFeedGenerators.ts | 3 +- .../bsky/unspecced/getSuggestionsSkeleton.ts | 3 +- .../bsky/unspecced/getTaggedSuggestions.ts | 13 +- .../bsky/unspecced/searchActorsSkeleton.ts | 3 +- .../app/bsky/unspecced/searchPostsSkeleton.ts | 3 +- .../unspecced/searchStarterPacksSkeleton.ts | 3 +- .../src/client/types/app/bsky/video/defs.ts | 12 +- .../types/app/bsky/video/getJobStatus.ts | 3 +- .../types/app/bsky/video/getUploadLimits.ts | 3 +- .../types/app/bsky/video/uploadVideo.ts | 3 +- .../types/chat/bsky/actor/declaration.ts | 11 +- .../src/client/types/chat/bsky/actor/defs.ts | 14 +- .../types/chat/bsky/actor/deleteAccount.ts | 6 +- .../chat/bsky/actor/exportAccountData.ts | 2 +- .../src/client/types/chat/bsky/convo/defs.ts | 130 +++-- .../chat/bsky/convo/deleteMessageForSelf.ts | 3 +- .../client/types/chat/bsky/convo/getConvo.ts | 3 +- .../chat/bsky/convo/getConvoForMembers.ts | 3 +- .../client/types/chat/bsky/convo/getLog.ts | 13 +- .../types/chat/bsky/convo/getMessages.ts | 9 +- .../types/chat/bsky/convo/leaveConvo.ts | 4 +- .../types/chat/bsky/convo/listConvos.ts | 3 +- .../client/types/chat/bsky/convo/muteConvo.ts | 4 +- .../types/chat/bsky/convo/sendMessage.ts | 3 +- .../types/chat/bsky/convo/sendMessageBatch.ts | 14 +- .../types/chat/bsky/convo/unmuteConvo.ts | 4 +- .../types/chat/bsky/convo/updateRead.ts | 4 +- .../chat/bsky/moderation/getActorMetadata.ts | 13 +- .../chat/bsky/moderation/getMessageContext.ts | 9 +- .../chat/bsky/moderation/updateActorAccess.ts | 3 +- .../client/types/com/atproto/admin/defs.ts | 56 +- .../types/com/atproto/admin/deleteAccount.ts | 3 +- .../atproto/admin/disableAccountInvites.ts | 3 +- .../com/atproto/admin/disableInviteCodes.ts | 3 +- .../com/atproto/admin/enableAccountInvites.ts | 3 +- .../types/com/atproto/admin/getAccountInfo.ts | 2 +- .../com/atproto/admin/getAccountInfos.ts | 3 +- .../types/com/atproto/admin/getInviteCodes.ts | 3 +- .../com/atproto/admin/getSubjectStatus.ts | 11 +- .../types/com/atproto/admin/searchAccounts.ts | 3 +- .../types/com/atproto/admin/sendEmail.ts | 4 +- .../com/atproto/admin/updateAccountEmail.ts | 3 +- .../com/atproto/admin/updateAccountHandle.ts | 3 +- .../atproto/admin/updateAccountPassword.ts | 3 +- .../com/atproto/admin/updateSubjectStatus.ts | 20 +- .../identity/getRecommendedDidCredentials.ts | 7 +- .../identity/requestPlcOperationSignature.ts | 2 +- .../com/atproto/identity/resolveHandle.ts | 3 +- .../com/atproto/identity/signPlcOperation.ts | 10 +- .../atproto/identity/submitPlcOperation.ts | 5 +- .../com/atproto/identity/updateHandle.ts | 3 +- .../client/types/com/atproto/label/defs.ts | 63 ++- .../types/com/atproto/label/queryLabels.ts | 3 +- .../com/atproto/label/subscribeLabels.ts | 22 +- .../com/atproto/moderation/createReport.ts | 16 +- .../types/com/atproto/moderation/defs.ts | 2 +- .../types/com/atproto/repo/applyWrites.ts | 76 +-- .../types/com/atproto/repo/createRecord.ts | 6 +- .../src/client/types/com/atproto/repo/defs.ts | 12 +- .../types/com/atproto/repo/deleteRecord.ts | 4 +- .../types/com/atproto/repo/describeRepo.ts | 5 +- .../types/com/atproto/repo/getRecord.ts | 5 +- .../types/com/atproto/repo/importRepo.ts | 2 +- .../com/atproto/repo/listMissingBlobs.ts | 13 +- .../types/com/atproto/repo/listRecords.ts | 15 +- .../types/com/atproto/repo/putRecord.ts | 6 +- .../types/com/atproto/repo/strongRef.ts | 12 +- .../types/com/atproto/repo/uploadBlob.ts | 3 +- .../com/atproto/server/activateAccount.ts | 2 +- .../com/atproto/server/checkAccountStatus.ts | 3 +- .../types/com/atproto/server/confirmEmail.ts | 3 +- .../types/com/atproto/server/createAccount.ts | 8 +- .../com/atproto/server/createAppPassword.ts | 13 +- .../com/atproto/server/createInviteCode.ts | 4 +- .../com/atproto/server/createInviteCodes.ts | 14 +- .../types/com/atproto/server/createSession.ts | 6 +- .../com/atproto/server/deactivateAccount.ts | 3 +- .../client/types/com/atproto/server/defs.ts | 22 +- .../types/com/atproto/server/deleteAccount.ts | 3 +- .../types/com/atproto/server/deleteSession.ts | 2 +- .../com/atproto/server/describeServer.ts | 23 +- .../atproto/server/getAccountInviteCodes.ts | 3 +- .../com/atproto/server/getServiceAuth.ts | 3 +- .../types/com/atproto/server/getSession.ts | 5 +- .../com/atproto/server/listAppPasswords.ts | 13 +- .../com/atproto/server/refreshSession.ts | 5 +- .../atproto/server/requestAccountDelete.ts | 2 +- .../server/requestEmailConfirmation.ts | 2 +- .../com/atproto/server/requestEmailUpdate.ts | 3 +- .../atproto/server/requestPasswordReset.ts | 3 +- .../com/atproto/server/reserveSigningKey.ts | 4 +- .../types/com/atproto/server/resetPassword.ts | 3 +- .../com/atproto/server/revokeAppPassword.ts | 3 +- .../types/com/atproto/server/updateEmail.ts | 3 +- .../client/types/com/atproto/sync/getBlob.ts | 2 +- .../types/com/atproto/sync/getBlocks.ts | 2 +- .../types/com/atproto/sync/getCheckout.ts | 2 +- .../client/types/com/atproto/sync/getHead.ts | 3 +- .../types/com/atproto/sync/getLatestCommit.ts | 3 +- .../types/com/atproto/sync/getRecord.ts | 2 +- .../client/types/com/atproto/sync/getRepo.ts | 2 +- .../types/com/atproto/sync/getRepoStatus.ts | 3 +- .../types/com/atproto/sync/listBlobs.ts | 3 +- .../types/com/atproto/sync/listRepos.ts | 13 +- .../types/com/atproto/sync/notifyOfUpdate.ts | 3 +- .../types/com/atproto/sync/requestCrawl.ts | 3 +- .../types/com/atproto/sync/subscribeRepos.ts | 82 +-- .../com/atproto/temp/addReservedHandle.ts | 7 +- .../com/atproto/temp/checkSignupQueue.ts | 3 +- .../types/com/atproto/temp/fetchLabels.ts | 3 +- .../atproto/temp/requestPhoneVerification.ts | 3 +- .../ozone/communication/createTemplate.ts | 3 +- .../types/tools/ozone/communication/defs.ts | 12 +- .../ozone/communication/deleteTemplate.ts | 3 +- .../ozone/communication/listTemplates.ts | 3 +- .../ozone/communication/updateTemplate.ts | 3 +- .../types/tools/ozone/moderation/defs.ts | 510 +++++++++++------- .../types/tools/ozone/moderation/emitEvent.ts | 45 +- .../types/tools/ozone/moderation/getEvent.ts | 2 +- .../types/tools/ozone/moderation/getRecord.ts | 2 +- .../tools/ozone/moderation/getRecords.ts | 9 +- .../types/tools/ozone/moderation/getRepo.ts | 2 +- .../types/tools/ozone/moderation/getRepos.ts | 9 +- .../tools/ozone/moderation/queryEvents.ts | 3 +- .../tools/ozone/moderation/queryStatuses.ts | 3 +- .../tools/ozone/moderation/searchRepos.ts | 3 +- .../types/tools/ozone/server/getConfig.ts | 23 +- .../client/types/tools/ozone/set/addValues.ts | 3 +- .../src/client/types/tools/ozone/set/defs.ts | 22 +- .../client/types/tools/ozone/set/deleteSet.ts | 7 +- .../types/tools/ozone/set/deleteValues.ts | 3 +- .../client/types/tools/ozone/set/getValues.ts | 3 +- .../client/types/tools/ozone/set/querySets.ts | 3 +- .../client/types/tools/ozone/set/upsertSet.ts | 2 +- .../client/types/tools/ozone/setting/defs.ts | 14 +- .../types/tools/ozone/setting/listOptions.ts | 3 +- .../tools/ozone/setting/removeOptions.ts | 7 +- .../types/tools/ozone/setting/upsertOption.ts | 6 +- .../types/tools/ozone/signature/defs.ts | 12 +- .../tools/ozone/signature/findCorrelation.ts | 3 +- .../ozone/signature/findRelatedAccounts.ts | 15 +- .../tools/ozone/signature/searchAccounts.ts | 3 +- .../types/tools/ozone/team/addMember.ts | 3 +- .../src/client/types/tools/ozone/team/defs.ts | 12 +- .../types/tools/ozone/team/deleteMember.ts | 3 +- .../types/tools/ozone/team/listMembers.ts | 3 +- .../types/tools/ozone/team/updateMember.ts | 3 +- packages/api/src/client/util.ts | 12 +- packages/bsky/src/lexicon/lexicons.ts | 2 +- .../src/lexicon/types/app/bsky/actor/defs.ts | 304 +++++++---- .../types/app/bsky/actor/getPreferences.ts | 3 +- .../types/app/bsky/actor/getProfile.ts | 2 +- .../types/app/bsky/actor/getProfiles.ts | 3 +- .../types/app/bsky/actor/getSuggestions.ts | 3 +- .../lexicon/types/app/bsky/actor/profile.ts | 15 +- .../types/app/bsky/actor/putPreferences.ts | 3 +- .../types/app/bsky/actor/searchActors.ts | 3 +- .../app/bsky/actor/searchActorsTypeahead.ts | 3 +- .../src/lexicon/types/app/bsky/embed/defs.ts | 12 +- .../lexicon/types/app/bsky/embed/external.ts | 42 +- .../lexicon/types/app/bsky/embed/images.ts | 42 +- .../lexicon/types/app/bsky/embed/record.ts | 98 ++-- .../types/app/bsky/embed/recordWithMedia.ts | 38 +- .../src/lexicon/types/app/bsky/embed/video.ts | 32 +- .../src/lexicon/types/app/bsky/feed/defs.ts | 244 +++++---- .../app/bsky/feed/describeFeedGenerator.ts | 23 +- .../lexicon/types/app/bsky/feed/generator.ts | 15 +- .../types/app/bsky/feed/getActorFeeds.ts | 3 +- .../types/app/bsky/feed/getActorLikes.ts | 3 +- .../types/app/bsky/feed/getAuthorFeed.ts | 3 +- .../lexicon/types/app/bsky/feed/getFeed.ts | 3 +- .../types/app/bsky/feed/getFeedGenerator.ts | 3 +- .../types/app/bsky/feed/getFeedGenerators.ts | 3 +- .../types/app/bsky/feed/getFeedSkeleton.ts | 3 +- .../lexicon/types/app/bsky/feed/getLikes.ts | 13 +- .../types/app/bsky/feed/getListFeed.ts | 3 +- .../types/app/bsky/feed/getPostThread.ts | 11 +- .../lexicon/types/app/bsky/feed/getPosts.ts | 3 +- .../lexicon/types/app/bsky/feed/getQuotes.ts | 3 +- .../types/app/bsky/feed/getRepostedBy.ts | 3 +- .../types/app/bsky/feed/getSuggestedFeeds.ts | 3 +- .../types/app/bsky/feed/getTimeline.ts | 3 +- .../src/lexicon/types/app/bsky/feed/like.ts | 11 +- .../src/lexicon/types/app/bsky/feed/post.ts | 57 +- .../lexicon/types/app/bsky/feed/postgate.ts | 23 +- .../src/lexicon/types/app/bsky/feed/repost.ts | 11 +- .../types/app/bsky/feed/searchPosts.ts | 3 +- .../types/app/bsky/feed/sendInteractions.ts | 7 +- .../lexicon/types/app/bsky/feed/threadgate.ts | 49 +- .../src/lexicon/types/app/bsky/graph/block.ts | 11 +- .../src/lexicon/types/app/bsky/graph/defs.ts | 94 ++-- .../lexicon/types/app/bsky/graph/follow.ts | 11 +- .../app/bsky/graph/getActorStarterPacks.ts | 3 +- .../lexicon/types/app/bsky/graph/getBlocks.ts | 3 +- .../types/app/bsky/graph/getFollowers.ts | 3 +- .../types/app/bsky/graph/getFollows.ts | 3 +- .../types/app/bsky/graph/getKnownFollowers.ts | 3 +- .../lexicon/types/app/bsky/graph/getList.ts | 3 +- .../types/app/bsky/graph/getListBlocks.ts | 3 +- .../types/app/bsky/graph/getListMutes.ts | 3 +- .../lexicon/types/app/bsky/graph/getLists.ts | 3 +- .../lexicon/types/app/bsky/graph/getMutes.ts | 3 +- .../types/app/bsky/graph/getRelationships.ts | 9 +- .../types/app/bsky/graph/getStarterPack.ts | 3 +- .../types/app/bsky/graph/getStarterPacks.ts | 3 +- .../bsky/graph/getSuggestedFollowsByActor.ts | 3 +- .../src/lexicon/types/app/bsky/graph/list.ts | 15 +- .../lexicon/types/app/bsky/graph/listblock.ts | 11 +- .../lexicon/types/app/bsky/graph/listitem.ts | 11 +- .../lexicon/types/app/bsky/graph/muteActor.ts | 3 +- .../types/app/bsky/graph/muteActorList.ts | 3 +- .../types/app/bsky/graph/muteThread.ts | 3 +- .../app/bsky/graph/searchStarterPacks.ts | 3 +- .../types/app/bsky/graph/starterpack.ts | 21 +- .../types/app/bsky/graph/unmuteActor.ts | 3 +- .../types/app/bsky/graph/unmuteActorList.ts | 3 +- .../types/app/bsky/graph/unmuteThread.ts | 3 +- .../lexicon/types/app/bsky/labeler/defs.ts | 48 +- .../types/app/bsky/labeler/getServices.ts | 9 +- .../lexicon/types/app/bsky/labeler/service.ts | 15 +- .../app/bsky/notification/getUnreadCount.ts | 3 +- .../bsky/notification/listNotifications.ts | 15 +- .../app/bsky/notification/putPreferences.ts | 3 +- .../app/bsky/notification/registerPush.ts | 3 +- .../types/app/bsky/notification/updateSeen.ts | 3 +- .../lexicon/types/app/bsky/richtext/facet.ts | 54 +- .../lexicon/types/app/bsky/unspecced/defs.ts | 43 +- .../types/app/bsky/unspecced/getConfig.ts | 3 +- .../unspecced/getPopularFeedGenerators.ts | 3 +- .../bsky/unspecced/getSuggestionsSkeleton.ts | 3 +- .../bsky/unspecced/getTaggedSuggestions.ts | 13 +- .../bsky/unspecced/searchActorsSkeleton.ts | 3 +- .../app/bsky/unspecced/searchPostsSkeleton.ts | 3 +- .../unspecced/searchStarterPacksSkeleton.ts | 3 +- .../src/lexicon/types/app/bsky/video/defs.ts | 12 +- .../types/app/bsky/video/getJobStatus.ts | 3 +- .../types/app/bsky/video/getUploadLimits.ts | 3 +- .../types/app/bsky/video/uploadVideo.ts | 3 +- .../types/chat/bsky/actor/declaration.ts | 11 +- .../src/lexicon/types/chat/bsky/actor/defs.ts | 14 +- .../types/chat/bsky/actor/deleteAccount.ts | 6 +- .../chat/bsky/actor/exportAccountData.ts | 2 +- .../src/lexicon/types/chat/bsky/convo/defs.ts | 130 +++-- .../chat/bsky/convo/deleteMessageForSelf.ts | 3 +- .../lexicon/types/chat/bsky/convo/getConvo.ts | 3 +- .../chat/bsky/convo/getConvoForMembers.ts | 3 +- .../lexicon/types/chat/bsky/convo/getLog.ts | 13 +- .../types/chat/bsky/convo/getMessages.ts | 9 +- .../types/chat/bsky/convo/leaveConvo.ts | 4 +- .../types/chat/bsky/convo/listConvos.ts | 3 +- .../types/chat/bsky/convo/muteConvo.ts | 4 +- .../types/chat/bsky/convo/sendMessage.ts | 3 +- .../types/chat/bsky/convo/sendMessageBatch.ts | 14 +- .../types/chat/bsky/convo/unmuteConvo.ts | 4 +- .../types/chat/bsky/convo/updateRead.ts | 4 +- .../chat/bsky/moderation/getActorMetadata.ts | 13 +- .../chat/bsky/moderation/getMessageContext.ts | 9 +- .../chat/bsky/moderation/updateActorAccess.ts | 3 +- .../lexicon/types/com/atproto/admin/defs.ts | 56 +- .../types/com/atproto/admin/deleteAccount.ts | 3 +- .../atproto/admin/disableAccountInvites.ts | 3 +- .../com/atproto/admin/disableInviteCodes.ts | 3 +- .../com/atproto/admin/enableAccountInvites.ts | 3 +- .../types/com/atproto/admin/getAccountInfo.ts | 2 +- .../com/atproto/admin/getAccountInfos.ts | 3 +- .../types/com/atproto/admin/getInviteCodes.ts | 3 +- .../com/atproto/admin/getSubjectStatus.ts | 11 +- .../types/com/atproto/admin/searchAccounts.ts | 3 +- .../types/com/atproto/admin/sendEmail.ts | 4 +- .../com/atproto/admin/updateAccountEmail.ts | 3 +- .../com/atproto/admin/updateAccountHandle.ts | 3 +- .../atproto/admin/updateAccountPassword.ts | 3 +- .../com/atproto/admin/updateSubjectStatus.ts | 20 +- .../identity/getRecommendedDidCredentials.ts | 7 +- .../identity/requestPlcOperationSignature.ts | 2 +- .../com/atproto/identity/resolveHandle.ts | 3 +- .../com/atproto/identity/signPlcOperation.ts | 10 +- .../atproto/identity/submitPlcOperation.ts | 5 +- .../com/atproto/identity/updateHandle.ts | 3 +- .../lexicon/types/com/atproto/label/defs.ts | 63 ++- .../types/com/atproto/label/queryLabels.ts | 3 +- .../com/atproto/label/subscribeLabels.ts | 27 +- .../com/atproto/moderation/createReport.ts | 16 +- .../types/com/atproto/moderation/defs.ts | 2 +- .../types/com/atproto/repo/applyWrites.ts | 76 +-- .../types/com/atproto/repo/createRecord.ts | 6 +- .../lexicon/types/com/atproto/repo/defs.ts | 12 +- .../types/com/atproto/repo/deleteRecord.ts | 4 +- .../types/com/atproto/repo/describeRepo.ts | 5 +- .../types/com/atproto/repo/getRecord.ts | 5 +- .../types/com/atproto/repo/importRepo.ts | 2 +- .../com/atproto/repo/listMissingBlobs.ts | 13 +- .../types/com/atproto/repo/listRecords.ts | 15 +- .../types/com/atproto/repo/putRecord.ts | 6 +- .../types/com/atproto/repo/strongRef.ts | 12 +- .../types/com/atproto/repo/uploadBlob.ts | 3 +- .../com/atproto/server/activateAccount.ts | 2 +- .../com/atproto/server/checkAccountStatus.ts | 3 +- .../types/com/atproto/server/confirmEmail.ts | 3 +- .../types/com/atproto/server/createAccount.ts | 8 +- .../com/atproto/server/createAppPassword.ts | 13 +- .../com/atproto/server/createInviteCode.ts | 4 +- .../com/atproto/server/createInviteCodes.ts | 14 +- .../types/com/atproto/server/createSession.ts | 6 +- .../com/atproto/server/deactivateAccount.ts | 3 +- .../lexicon/types/com/atproto/server/defs.ts | 22 +- .../types/com/atproto/server/deleteAccount.ts | 3 +- .../types/com/atproto/server/deleteSession.ts | 2 +- .../com/atproto/server/describeServer.ts | 23 +- .../atproto/server/getAccountInviteCodes.ts | 3 +- .../com/atproto/server/getServiceAuth.ts | 3 +- .../types/com/atproto/server/getSession.ts | 5 +- .../com/atproto/server/listAppPasswords.ts | 13 +- .../com/atproto/server/refreshSession.ts | 5 +- .../atproto/server/requestAccountDelete.ts | 2 +- .../server/requestEmailConfirmation.ts | 2 +- .../com/atproto/server/requestEmailUpdate.ts | 3 +- .../atproto/server/requestPasswordReset.ts | 3 +- .../com/atproto/server/reserveSigningKey.ts | 4 +- .../types/com/atproto/server/resetPassword.ts | 3 +- .../com/atproto/server/revokeAppPassword.ts | 3 +- .../types/com/atproto/server/updateEmail.ts | 3 +- .../lexicon/types/com/atproto/sync/getBlob.ts | 2 +- .../types/com/atproto/sync/getBlocks.ts | 2 +- .../types/com/atproto/sync/getCheckout.ts | 2 +- .../lexicon/types/com/atproto/sync/getHead.ts | 3 +- .../types/com/atproto/sync/getLatestCommit.ts | 3 +- .../types/com/atproto/sync/getRecord.ts | 2 +- .../lexicon/types/com/atproto/sync/getRepo.ts | 2 +- .../types/com/atproto/sync/getRepoStatus.ts | 3 +- .../types/com/atproto/sync/listBlobs.ts | 3 +- .../types/com/atproto/sync/listRepos.ts | 13 +- .../types/com/atproto/sync/notifyOfUpdate.ts | 3 +- .../types/com/atproto/sync/requestCrawl.ts | 3 +- .../types/com/atproto/sync/subscribeRepos.ts | 98 ++-- .../com/atproto/temp/addReservedHandle.ts | 7 +- .../com/atproto/temp/checkSignupQueue.ts | 3 +- .../types/com/atproto/temp/fetchLabels.ts | 3 +- .../atproto/temp/requestPhoneVerification.ts | 3 +- packages/bsky/src/lexicon/util.ts | 12 +- packages/ozone/src/lexicon/lexicons.ts | 2 +- .../src/lexicon/types/app/bsky/actor/defs.ts | 304 +++++++---- .../types/app/bsky/actor/getPreferences.ts | 3 +- .../types/app/bsky/actor/getProfile.ts | 2 +- .../types/app/bsky/actor/getProfiles.ts | 3 +- .../types/app/bsky/actor/getSuggestions.ts | 3 +- .../lexicon/types/app/bsky/actor/profile.ts | 15 +- .../types/app/bsky/actor/putPreferences.ts | 3 +- .../types/app/bsky/actor/searchActors.ts | 3 +- .../app/bsky/actor/searchActorsTypeahead.ts | 3 +- .../src/lexicon/types/app/bsky/embed/defs.ts | 12 +- .../lexicon/types/app/bsky/embed/external.ts | 42 +- .../lexicon/types/app/bsky/embed/images.ts | 42 +- .../lexicon/types/app/bsky/embed/record.ts | 98 ++-- .../types/app/bsky/embed/recordWithMedia.ts | 38 +- .../src/lexicon/types/app/bsky/embed/video.ts | 32 +- .../src/lexicon/types/app/bsky/feed/defs.ts | 244 +++++---- .../app/bsky/feed/describeFeedGenerator.ts | 23 +- .../lexicon/types/app/bsky/feed/generator.ts | 15 +- .../types/app/bsky/feed/getActorFeeds.ts | 3 +- .../types/app/bsky/feed/getActorLikes.ts | 3 +- .../types/app/bsky/feed/getAuthorFeed.ts | 3 +- .../lexicon/types/app/bsky/feed/getFeed.ts | 3 +- .../types/app/bsky/feed/getFeedGenerator.ts | 3 +- .../types/app/bsky/feed/getFeedGenerators.ts | 3 +- .../types/app/bsky/feed/getFeedSkeleton.ts | 3 +- .../lexicon/types/app/bsky/feed/getLikes.ts | 13 +- .../types/app/bsky/feed/getListFeed.ts | 3 +- .../types/app/bsky/feed/getPostThread.ts | 11 +- .../lexicon/types/app/bsky/feed/getPosts.ts | 3 +- .../lexicon/types/app/bsky/feed/getQuotes.ts | 3 +- .../types/app/bsky/feed/getRepostedBy.ts | 3 +- .../types/app/bsky/feed/getSuggestedFeeds.ts | 3 +- .../types/app/bsky/feed/getTimeline.ts | 3 +- .../src/lexicon/types/app/bsky/feed/like.ts | 11 +- .../src/lexicon/types/app/bsky/feed/post.ts | 57 +- .../lexicon/types/app/bsky/feed/postgate.ts | 23 +- .../src/lexicon/types/app/bsky/feed/repost.ts | 11 +- .../types/app/bsky/feed/searchPosts.ts | 3 +- .../types/app/bsky/feed/sendInteractions.ts | 7 +- .../lexicon/types/app/bsky/feed/threadgate.ts | 49 +- .../src/lexicon/types/app/bsky/graph/block.ts | 11 +- .../src/lexicon/types/app/bsky/graph/defs.ts | 94 ++-- .../lexicon/types/app/bsky/graph/follow.ts | 11 +- .../app/bsky/graph/getActorStarterPacks.ts | 3 +- .../lexicon/types/app/bsky/graph/getBlocks.ts | 3 +- .../types/app/bsky/graph/getFollowers.ts | 3 +- .../types/app/bsky/graph/getFollows.ts | 3 +- .../types/app/bsky/graph/getKnownFollowers.ts | 3 +- .../lexicon/types/app/bsky/graph/getList.ts | 3 +- .../types/app/bsky/graph/getListBlocks.ts | 3 +- .../types/app/bsky/graph/getListMutes.ts | 3 +- .../lexicon/types/app/bsky/graph/getLists.ts | 3 +- .../lexicon/types/app/bsky/graph/getMutes.ts | 3 +- .../types/app/bsky/graph/getRelationships.ts | 9 +- .../types/app/bsky/graph/getStarterPack.ts | 3 +- .../types/app/bsky/graph/getStarterPacks.ts | 3 +- .../bsky/graph/getSuggestedFollowsByActor.ts | 3 +- .../src/lexicon/types/app/bsky/graph/list.ts | 15 +- .../lexicon/types/app/bsky/graph/listblock.ts | 11 +- .../lexicon/types/app/bsky/graph/listitem.ts | 11 +- .../lexicon/types/app/bsky/graph/muteActor.ts | 3 +- .../types/app/bsky/graph/muteActorList.ts | 3 +- .../types/app/bsky/graph/muteThread.ts | 3 +- .../app/bsky/graph/searchStarterPacks.ts | 3 +- .../types/app/bsky/graph/starterpack.ts | 21 +- .../types/app/bsky/graph/unmuteActor.ts | 3 +- .../types/app/bsky/graph/unmuteActorList.ts | 3 +- .../types/app/bsky/graph/unmuteThread.ts | 3 +- .../lexicon/types/app/bsky/labeler/defs.ts | 48 +- .../types/app/bsky/labeler/getServices.ts | 9 +- .../lexicon/types/app/bsky/labeler/service.ts | 15 +- .../app/bsky/notification/getUnreadCount.ts | 3 +- .../bsky/notification/listNotifications.ts | 15 +- .../app/bsky/notification/putPreferences.ts | 3 +- .../app/bsky/notification/registerPush.ts | 3 +- .../types/app/bsky/notification/updateSeen.ts | 3 +- .../lexicon/types/app/bsky/richtext/facet.ts | 54 +- .../lexicon/types/app/bsky/unspecced/defs.ts | 43 +- .../types/app/bsky/unspecced/getConfig.ts | 3 +- .../unspecced/getPopularFeedGenerators.ts | 3 +- .../bsky/unspecced/getSuggestionsSkeleton.ts | 3 +- .../bsky/unspecced/getTaggedSuggestions.ts | 13 +- .../bsky/unspecced/searchActorsSkeleton.ts | 3 +- .../app/bsky/unspecced/searchPostsSkeleton.ts | 3 +- .../unspecced/searchStarterPacksSkeleton.ts | 3 +- .../src/lexicon/types/app/bsky/video/defs.ts | 12 +- .../types/app/bsky/video/getJobStatus.ts | 3 +- .../types/app/bsky/video/getUploadLimits.ts | 3 +- .../types/app/bsky/video/uploadVideo.ts | 3 +- .../types/chat/bsky/actor/declaration.ts | 11 +- .../src/lexicon/types/chat/bsky/actor/defs.ts | 14 +- .../types/chat/bsky/actor/deleteAccount.ts | 6 +- .../chat/bsky/actor/exportAccountData.ts | 2 +- .../src/lexicon/types/chat/bsky/convo/defs.ts | 130 +++-- .../chat/bsky/convo/deleteMessageForSelf.ts | 3 +- .../lexicon/types/chat/bsky/convo/getConvo.ts | 3 +- .../chat/bsky/convo/getConvoForMembers.ts | 3 +- .../lexicon/types/chat/bsky/convo/getLog.ts | 13 +- .../types/chat/bsky/convo/getMessages.ts | 9 +- .../types/chat/bsky/convo/leaveConvo.ts | 4 +- .../types/chat/bsky/convo/listConvos.ts | 3 +- .../types/chat/bsky/convo/muteConvo.ts | 4 +- .../types/chat/bsky/convo/sendMessage.ts | 3 +- .../types/chat/bsky/convo/sendMessageBatch.ts | 14 +- .../types/chat/bsky/convo/unmuteConvo.ts | 4 +- .../types/chat/bsky/convo/updateRead.ts | 4 +- .../chat/bsky/moderation/getActorMetadata.ts | 13 +- .../chat/bsky/moderation/getMessageContext.ts | 9 +- .../chat/bsky/moderation/updateActorAccess.ts | 3 +- .../lexicon/types/com/atproto/admin/defs.ts | 56 +- .../types/com/atproto/admin/deleteAccount.ts | 3 +- .../atproto/admin/disableAccountInvites.ts | 3 +- .../com/atproto/admin/disableInviteCodes.ts | 3 +- .../com/atproto/admin/enableAccountInvites.ts | 3 +- .../types/com/atproto/admin/getAccountInfo.ts | 2 +- .../com/atproto/admin/getAccountInfos.ts | 3 +- .../types/com/atproto/admin/getInviteCodes.ts | 3 +- .../com/atproto/admin/getSubjectStatus.ts | 11 +- .../types/com/atproto/admin/searchAccounts.ts | 3 +- .../types/com/atproto/admin/sendEmail.ts | 4 +- .../com/atproto/admin/updateAccountEmail.ts | 3 +- .../com/atproto/admin/updateAccountHandle.ts | 3 +- .../atproto/admin/updateAccountPassword.ts | 3 +- .../com/atproto/admin/updateSubjectStatus.ts | 20 +- .../identity/getRecommendedDidCredentials.ts | 7 +- .../identity/requestPlcOperationSignature.ts | 2 +- .../com/atproto/identity/resolveHandle.ts | 3 +- .../com/atproto/identity/signPlcOperation.ts | 10 +- .../atproto/identity/submitPlcOperation.ts | 5 +- .../com/atproto/identity/updateHandle.ts | 3 +- .../lexicon/types/com/atproto/label/defs.ts | 63 ++- .../types/com/atproto/label/queryLabels.ts | 3 +- .../com/atproto/label/subscribeLabels.ts | 27 +- .../com/atproto/moderation/createReport.ts | 16 +- .../types/com/atproto/moderation/defs.ts | 2 +- .../types/com/atproto/repo/applyWrites.ts | 76 +-- .../types/com/atproto/repo/createRecord.ts | 6 +- .../lexicon/types/com/atproto/repo/defs.ts | 12 +- .../types/com/atproto/repo/deleteRecord.ts | 4 +- .../types/com/atproto/repo/describeRepo.ts | 5 +- .../types/com/atproto/repo/getRecord.ts | 5 +- .../types/com/atproto/repo/importRepo.ts | 2 +- .../com/atproto/repo/listMissingBlobs.ts | 13 +- .../types/com/atproto/repo/listRecords.ts | 15 +- .../types/com/atproto/repo/putRecord.ts | 6 +- .../types/com/atproto/repo/strongRef.ts | 12 +- .../types/com/atproto/repo/uploadBlob.ts | 3 +- .../com/atproto/server/activateAccount.ts | 2 +- .../com/atproto/server/checkAccountStatus.ts | 3 +- .../types/com/atproto/server/confirmEmail.ts | 3 +- .../types/com/atproto/server/createAccount.ts | 8 +- .../com/atproto/server/createAppPassword.ts | 13 +- .../com/atproto/server/createInviteCode.ts | 4 +- .../com/atproto/server/createInviteCodes.ts | 14 +- .../types/com/atproto/server/createSession.ts | 6 +- .../com/atproto/server/deactivateAccount.ts | 3 +- .../lexicon/types/com/atproto/server/defs.ts | 22 +- .../types/com/atproto/server/deleteAccount.ts | 3 +- .../types/com/atproto/server/deleteSession.ts | 2 +- .../com/atproto/server/describeServer.ts | 23 +- .../atproto/server/getAccountInviteCodes.ts | 3 +- .../com/atproto/server/getServiceAuth.ts | 3 +- .../types/com/atproto/server/getSession.ts | 5 +- .../com/atproto/server/listAppPasswords.ts | 13 +- .../com/atproto/server/refreshSession.ts | 5 +- .../atproto/server/requestAccountDelete.ts | 2 +- .../server/requestEmailConfirmation.ts | 2 +- .../com/atproto/server/requestEmailUpdate.ts | 3 +- .../atproto/server/requestPasswordReset.ts | 3 +- .../com/atproto/server/reserveSigningKey.ts | 4 +- .../types/com/atproto/server/resetPassword.ts | 3 +- .../com/atproto/server/revokeAppPassword.ts | 3 +- .../types/com/atproto/server/updateEmail.ts | 3 +- .../lexicon/types/com/atproto/sync/getBlob.ts | 2 +- .../types/com/atproto/sync/getBlocks.ts | 2 +- .../types/com/atproto/sync/getCheckout.ts | 2 +- .../lexicon/types/com/atproto/sync/getHead.ts | 3 +- .../types/com/atproto/sync/getLatestCommit.ts | 3 +- .../types/com/atproto/sync/getRecord.ts | 2 +- .../lexicon/types/com/atproto/sync/getRepo.ts | 2 +- .../types/com/atproto/sync/getRepoStatus.ts | 3 +- .../types/com/atproto/sync/listBlobs.ts | 3 +- .../types/com/atproto/sync/listRepos.ts | 13 +- .../types/com/atproto/sync/notifyOfUpdate.ts | 3 +- .../types/com/atproto/sync/requestCrawl.ts | 3 +- .../types/com/atproto/sync/subscribeRepos.ts | 98 ++-- .../com/atproto/temp/addReservedHandle.ts | 7 +- .../com/atproto/temp/checkSignupQueue.ts | 3 +- .../types/com/atproto/temp/fetchLabels.ts | 3 +- .../atproto/temp/requestPhoneVerification.ts | 3 +- .../ozone/communication/createTemplate.ts | 3 +- .../types/tools/ozone/communication/defs.ts | 12 +- .../ozone/communication/deleteTemplate.ts | 3 +- .../ozone/communication/listTemplates.ts | 3 +- .../ozone/communication/updateTemplate.ts | 3 +- .../types/tools/ozone/moderation/defs.ts | 510 +++++++++++------- .../types/tools/ozone/moderation/emitEvent.ts | 45 +- .../types/tools/ozone/moderation/getEvent.ts | 2 +- .../types/tools/ozone/moderation/getRecord.ts | 2 +- .../tools/ozone/moderation/getRecords.ts | 9 +- .../types/tools/ozone/moderation/getRepo.ts | 2 +- .../types/tools/ozone/moderation/getRepos.ts | 9 +- .../tools/ozone/moderation/queryEvents.ts | 3 +- .../tools/ozone/moderation/queryStatuses.ts | 3 +- .../tools/ozone/moderation/searchRepos.ts | 3 +- .../types/tools/ozone/server/getConfig.ts | 23 +- .../types/tools/ozone/set/addValues.ts | 3 +- .../src/lexicon/types/tools/ozone/set/defs.ts | 22 +- .../types/tools/ozone/set/deleteSet.ts | 7 +- .../types/tools/ozone/set/deleteValues.ts | 3 +- .../types/tools/ozone/set/getValues.ts | 3 +- .../types/tools/ozone/set/querySets.ts | 3 +- .../types/tools/ozone/set/upsertSet.ts | 2 +- .../lexicon/types/tools/ozone/setting/defs.ts | 14 +- .../types/tools/ozone/setting/listOptions.ts | 3 +- .../tools/ozone/setting/removeOptions.ts | 7 +- .../types/tools/ozone/setting/upsertOption.ts | 6 +- .../types/tools/ozone/signature/defs.ts | 12 +- .../tools/ozone/signature/findCorrelation.ts | 3 +- .../ozone/signature/findRelatedAccounts.ts | 15 +- .../tools/ozone/signature/searchAccounts.ts | 3 +- .../types/tools/ozone/team/addMember.ts | 3 +- .../lexicon/types/tools/ozone/team/defs.ts | 12 +- .../types/tools/ozone/team/deleteMember.ts | 3 +- .../types/tools/ozone/team/listMembers.ts | 3 +- .../types/tools/ozone/team/updateMember.ts | 3 +- packages/ozone/src/lexicon/util.ts | 12 +- packages/pds/src/lexicon/lexicons.ts | 2 +- .../src/lexicon/types/app/bsky/actor/defs.ts | 304 +++++++---- .../types/app/bsky/actor/getPreferences.ts | 3 +- .../types/app/bsky/actor/getProfile.ts | 2 +- .../types/app/bsky/actor/getProfiles.ts | 3 +- .../types/app/bsky/actor/getSuggestions.ts | 3 +- .../lexicon/types/app/bsky/actor/profile.ts | 15 +- .../types/app/bsky/actor/putPreferences.ts | 3 +- .../types/app/bsky/actor/searchActors.ts | 3 +- .../app/bsky/actor/searchActorsTypeahead.ts | 3 +- .../src/lexicon/types/app/bsky/embed/defs.ts | 12 +- .../lexicon/types/app/bsky/embed/external.ts | 42 +- .../lexicon/types/app/bsky/embed/images.ts | 42 +- .../lexicon/types/app/bsky/embed/record.ts | 98 ++-- .../types/app/bsky/embed/recordWithMedia.ts | 38 +- .../src/lexicon/types/app/bsky/embed/video.ts | 32 +- .../src/lexicon/types/app/bsky/feed/defs.ts | 244 +++++---- .../app/bsky/feed/describeFeedGenerator.ts | 23 +- .../lexicon/types/app/bsky/feed/generator.ts | 15 +- .../types/app/bsky/feed/getActorFeeds.ts | 3 +- .../types/app/bsky/feed/getActorLikes.ts | 3 +- .../types/app/bsky/feed/getAuthorFeed.ts | 3 +- .../lexicon/types/app/bsky/feed/getFeed.ts | 3 +- .../types/app/bsky/feed/getFeedGenerator.ts | 3 +- .../types/app/bsky/feed/getFeedGenerators.ts | 3 +- .../types/app/bsky/feed/getFeedSkeleton.ts | 3 +- .../lexicon/types/app/bsky/feed/getLikes.ts | 13 +- .../types/app/bsky/feed/getListFeed.ts | 3 +- .../types/app/bsky/feed/getPostThread.ts | 11 +- .../lexicon/types/app/bsky/feed/getPosts.ts | 3 +- .../lexicon/types/app/bsky/feed/getQuotes.ts | 3 +- .../types/app/bsky/feed/getRepostedBy.ts | 3 +- .../types/app/bsky/feed/getSuggestedFeeds.ts | 3 +- .../types/app/bsky/feed/getTimeline.ts | 3 +- .../src/lexicon/types/app/bsky/feed/like.ts | 11 +- .../src/lexicon/types/app/bsky/feed/post.ts | 57 +- .../lexicon/types/app/bsky/feed/postgate.ts | 23 +- .../src/lexicon/types/app/bsky/feed/repost.ts | 11 +- .../types/app/bsky/feed/searchPosts.ts | 3 +- .../types/app/bsky/feed/sendInteractions.ts | 7 +- .../lexicon/types/app/bsky/feed/threadgate.ts | 49 +- .../src/lexicon/types/app/bsky/graph/block.ts | 11 +- .../src/lexicon/types/app/bsky/graph/defs.ts | 94 ++-- .../lexicon/types/app/bsky/graph/follow.ts | 11 +- .../app/bsky/graph/getActorStarterPacks.ts | 3 +- .../lexicon/types/app/bsky/graph/getBlocks.ts | 3 +- .../types/app/bsky/graph/getFollowers.ts | 3 +- .../types/app/bsky/graph/getFollows.ts | 3 +- .../types/app/bsky/graph/getKnownFollowers.ts | 3 +- .../lexicon/types/app/bsky/graph/getList.ts | 3 +- .../types/app/bsky/graph/getListBlocks.ts | 3 +- .../types/app/bsky/graph/getListMutes.ts | 3 +- .../lexicon/types/app/bsky/graph/getLists.ts | 3 +- .../lexicon/types/app/bsky/graph/getMutes.ts | 3 +- .../types/app/bsky/graph/getRelationships.ts | 9 +- .../types/app/bsky/graph/getStarterPack.ts | 3 +- .../types/app/bsky/graph/getStarterPacks.ts | 3 +- .../bsky/graph/getSuggestedFollowsByActor.ts | 3 +- .../src/lexicon/types/app/bsky/graph/list.ts | 15 +- .../lexicon/types/app/bsky/graph/listblock.ts | 11 +- .../lexicon/types/app/bsky/graph/listitem.ts | 11 +- .../lexicon/types/app/bsky/graph/muteActor.ts | 3 +- .../types/app/bsky/graph/muteActorList.ts | 3 +- .../types/app/bsky/graph/muteThread.ts | 3 +- .../app/bsky/graph/searchStarterPacks.ts | 3 +- .../types/app/bsky/graph/starterpack.ts | 21 +- .../types/app/bsky/graph/unmuteActor.ts | 3 +- .../types/app/bsky/graph/unmuteActorList.ts | 3 +- .../types/app/bsky/graph/unmuteThread.ts | 3 +- .../lexicon/types/app/bsky/labeler/defs.ts | 48 +- .../types/app/bsky/labeler/getServices.ts | 9 +- .../lexicon/types/app/bsky/labeler/service.ts | 15 +- .../app/bsky/notification/getUnreadCount.ts | 3 +- .../bsky/notification/listNotifications.ts | 15 +- .../app/bsky/notification/putPreferences.ts | 3 +- .../app/bsky/notification/registerPush.ts | 3 +- .../types/app/bsky/notification/updateSeen.ts | 3 +- .../lexicon/types/app/bsky/richtext/facet.ts | 54 +- .../lexicon/types/app/bsky/unspecced/defs.ts | 43 +- .../types/app/bsky/unspecced/getConfig.ts | 3 +- .../unspecced/getPopularFeedGenerators.ts | 3 +- .../bsky/unspecced/getSuggestionsSkeleton.ts | 3 +- .../bsky/unspecced/getTaggedSuggestions.ts | 13 +- .../bsky/unspecced/searchActorsSkeleton.ts | 3 +- .../app/bsky/unspecced/searchPostsSkeleton.ts | 3 +- .../unspecced/searchStarterPacksSkeleton.ts | 3 +- .../src/lexicon/types/app/bsky/video/defs.ts | 12 +- .../types/app/bsky/video/getJobStatus.ts | 3 +- .../types/app/bsky/video/getUploadLimits.ts | 3 +- .../types/app/bsky/video/uploadVideo.ts | 3 +- .../types/chat/bsky/actor/declaration.ts | 11 +- .../src/lexicon/types/chat/bsky/actor/defs.ts | 14 +- .../types/chat/bsky/actor/deleteAccount.ts | 6 +- .../chat/bsky/actor/exportAccountData.ts | 2 +- .../src/lexicon/types/chat/bsky/convo/defs.ts | 130 +++-- .../chat/bsky/convo/deleteMessageForSelf.ts | 3 +- .../lexicon/types/chat/bsky/convo/getConvo.ts | 3 +- .../chat/bsky/convo/getConvoForMembers.ts | 3 +- .../lexicon/types/chat/bsky/convo/getLog.ts | 13 +- .../types/chat/bsky/convo/getMessages.ts | 9 +- .../types/chat/bsky/convo/leaveConvo.ts | 4 +- .../types/chat/bsky/convo/listConvos.ts | 3 +- .../types/chat/bsky/convo/muteConvo.ts | 4 +- .../types/chat/bsky/convo/sendMessage.ts | 3 +- .../types/chat/bsky/convo/sendMessageBatch.ts | 14 +- .../types/chat/bsky/convo/unmuteConvo.ts | 4 +- .../types/chat/bsky/convo/updateRead.ts | 4 +- .../chat/bsky/moderation/getActorMetadata.ts | 13 +- .../chat/bsky/moderation/getMessageContext.ts | 9 +- .../chat/bsky/moderation/updateActorAccess.ts | 3 +- .../lexicon/types/com/atproto/admin/defs.ts | 56 +- .../types/com/atproto/admin/deleteAccount.ts | 3 +- .../atproto/admin/disableAccountInvites.ts | 3 +- .../com/atproto/admin/disableInviteCodes.ts | 3 +- .../com/atproto/admin/enableAccountInvites.ts | 3 +- .../types/com/atproto/admin/getAccountInfo.ts | 2 +- .../com/atproto/admin/getAccountInfos.ts | 3 +- .../types/com/atproto/admin/getInviteCodes.ts | 3 +- .../com/atproto/admin/getSubjectStatus.ts | 11 +- .../types/com/atproto/admin/searchAccounts.ts | 3 +- .../types/com/atproto/admin/sendEmail.ts | 4 +- .../com/atproto/admin/updateAccountEmail.ts | 3 +- .../com/atproto/admin/updateAccountHandle.ts | 3 +- .../atproto/admin/updateAccountPassword.ts | 3 +- .../com/atproto/admin/updateSubjectStatus.ts | 20 +- .../identity/getRecommendedDidCredentials.ts | 7 +- .../identity/requestPlcOperationSignature.ts | 2 +- .../com/atproto/identity/resolveHandle.ts | 3 +- .../com/atproto/identity/signPlcOperation.ts | 10 +- .../atproto/identity/submitPlcOperation.ts | 5 +- .../com/atproto/identity/updateHandle.ts | 3 +- .../lexicon/types/com/atproto/label/defs.ts | 63 ++- .../types/com/atproto/label/queryLabels.ts | 3 +- .../com/atproto/label/subscribeLabels.ts | 27 +- .../com/atproto/moderation/createReport.ts | 16 +- .../types/com/atproto/moderation/defs.ts | 2 +- .../types/com/atproto/repo/applyWrites.ts | 76 +-- .../types/com/atproto/repo/createRecord.ts | 6 +- .../lexicon/types/com/atproto/repo/defs.ts | 12 +- .../types/com/atproto/repo/deleteRecord.ts | 4 +- .../types/com/atproto/repo/describeRepo.ts | 5 +- .../types/com/atproto/repo/getRecord.ts | 5 +- .../types/com/atproto/repo/importRepo.ts | 2 +- .../com/atproto/repo/listMissingBlobs.ts | 13 +- .../types/com/atproto/repo/listRecords.ts | 15 +- .../types/com/atproto/repo/putRecord.ts | 6 +- .../types/com/atproto/repo/strongRef.ts | 12 +- .../types/com/atproto/repo/uploadBlob.ts | 3 +- .../com/atproto/server/activateAccount.ts | 2 +- .../com/atproto/server/checkAccountStatus.ts | 3 +- .../types/com/atproto/server/confirmEmail.ts | 3 +- .../types/com/atproto/server/createAccount.ts | 8 +- .../com/atproto/server/createAppPassword.ts | 13 +- .../com/atproto/server/createInviteCode.ts | 4 +- .../com/atproto/server/createInviteCodes.ts | 14 +- .../types/com/atproto/server/createSession.ts | 6 +- .../com/atproto/server/deactivateAccount.ts | 3 +- .../lexicon/types/com/atproto/server/defs.ts | 22 +- .../types/com/atproto/server/deleteAccount.ts | 3 +- .../types/com/atproto/server/deleteSession.ts | 2 +- .../com/atproto/server/describeServer.ts | 23 +- .../atproto/server/getAccountInviteCodes.ts | 3 +- .../com/atproto/server/getServiceAuth.ts | 3 +- .../types/com/atproto/server/getSession.ts | 5 +- .../com/atproto/server/listAppPasswords.ts | 13 +- .../com/atproto/server/refreshSession.ts | 5 +- .../atproto/server/requestAccountDelete.ts | 2 +- .../server/requestEmailConfirmation.ts | 2 +- .../com/atproto/server/requestEmailUpdate.ts | 3 +- .../atproto/server/requestPasswordReset.ts | 3 +- .../com/atproto/server/reserveSigningKey.ts | 4 +- .../types/com/atproto/server/resetPassword.ts | 3 +- .../com/atproto/server/revokeAppPassword.ts | 3 +- .../types/com/atproto/server/updateEmail.ts | 3 +- .../lexicon/types/com/atproto/sync/getBlob.ts | 2 +- .../types/com/atproto/sync/getBlocks.ts | 2 +- .../types/com/atproto/sync/getCheckout.ts | 2 +- .../lexicon/types/com/atproto/sync/getHead.ts | 3 +- .../types/com/atproto/sync/getLatestCommit.ts | 3 +- .../types/com/atproto/sync/getRecord.ts | 2 +- .../lexicon/types/com/atproto/sync/getRepo.ts | 2 +- .../types/com/atproto/sync/getRepoStatus.ts | 3 +- .../types/com/atproto/sync/listBlobs.ts | 3 +- .../types/com/atproto/sync/listRepos.ts | 13 +- .../types/com/atproto/sync/notifyOfUpdate.ts | 3 +- .../types/com/atproto/sync/requestCrawl.ts | 3 +- .../types/com/atproto/sync/subscribeRepos.ts | 98 ++-- .../com/atproto/temp/addReservedHandle.ts | 7 +- .../com/atproto/temp/checkSignupQueue.ts | 3 +- .../types/com/atproto/temp/fetchLabels.ts | 3 +- .../atproto/temp/requestPhoneVerification.ts | 3 +- .../ozone/communication/createTemplate.ts | 3 +- .../types/tools/ozone/communication/defs.ts | 12 +- .../ozone/communication/deleteTemplate.ts | 3 +- .../ozone/communication/listTemplates.ts | 3 +- .../ozone/communication/updateTemplate.ts | 3 +- .../types/tools/ozone/moderation/defs.ts | 510 +++++++++++------- .../types/tools/ozone/moderation/emitEvent.ts | 45 +- .../types/tools/ozone/moderation/getEvent.ts | 2 +- .../types/tools/ozone/moderation/getRecord.ts | 2 +- .../tools/ozone/moderation/getRecords.ts | 9 +- .../types/tools/ozone/moderation/getRepo.ts | 2 +- .../types/tools/ozone/moderation/getRepos.ts | 9 +- .../tools/ozone/moderation/queryEvents.ts | 3 +- .../tools/ozone/moderation/queryStatuses.ts | 3 +- .../tools/ozone/moderation/searchRepos.ts | 3 +- .../types/tools/ozone/server/getConfig.ts | 23 +- .../types/tools/ozone/set/addValues.ts | 3 +- .../src/lexicon/types/tools/ozone/set/defs.ts | 22 +- .../types/tools/ozone/set/deleteSet.ts | 7 +- .../types/tools/ozone/set/deleteValues.ts | 3 +- .../types/tools/ozone/set/getValues.ts | 3 +- .../types/tools/ozone/set/querySets.ts | 3 +- .../types/tools/ozone/set/upsertSet.ts | 2 +- .../lexicon/types/tools/ozone/setting/defs.ts | 14 +- .../types/tools/ozone/setting/listOptions.ts | 3 +- .../tools/ozone/setting/removeOptions.ts | 7 +- .../types/tools/ozone/setting/upsertOption.ts | 6 +- .../types/tools/ozone/signature/defs.ts | 12 +- .../tools/ozone/signature/findCorrelation.ts | 3 +- .../ozone/signature/findRelatedAccounts.ts | 15 +- .../tools/ozone/signature/searchAccounts.ts | 3 +- .../types/tools/ozone/team/addMember.ts | 3 +- .../lexicon/types/tools/ozone/team/defs.ts | 12 +- .../types/tools/ozone/team/deleteMember.ts | 3 +- .../types/tools/ozone/team/listMembers.ts | 3 +- .../types/tools/ozone/team/updateMember.ts | 3 +- packages/pds/src/lexicon/util.ts | 12 +- 877 files changed, 7052 insertions(+), 5832 deletions(-) diff --git a/packages/api/src/client/index.ts b/packages/api/src/client/index.ts index 9ee329fe593..d1f0341b6e7 100644 --- a/packages/api/src/client/index.ts +++ b/packages/api/src/client/index.ts @@ -4,6 +4,7 @@ import { XrpcClient, FetchHandler, FetchHandlerOptions } from '@atproto/xrpc' import { schemas } from './lexicons' import { CID } from 'multiformats/cid' +import { OmitKey } from './util' import * as ComAtprotoAdminDefs from './types/com/atproto/admin/defs' import * as ComAtprotoAdminDeleteAccount from './types/com/atproto/admin/deleteAccount' import * as ComAtprotoAdminDisableAccountInvites from './types/com/atproto/admin/disableAccountInvites' @@ -1606,7 +1607,7 @@ export class ProfileRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyActorProfile.Record }[] @@ -1619,7 +1620,7 @@ export class ProfileRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyActorProfile.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.actor.profile', @@ -1629,25 +1630,30 @@ export class ProfileRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyActorProfile.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.actor.profile' + const collection = 'app.bsky.actor.profile' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.actor.profile', rkey: 'self', ...params, record }, + { + collection, + rkey: 'self', + ...params, + record: { ...record, $type: collection }, + }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -1889,7 +1895,7 @@ export class GeneratorRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyFeedGenerator.Record }[] @@ -1902,7 +1908,7 @@ export class GeneratorRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyFeedGenerator.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.feed.generator', @@ -1912,25 +1918,25 @@ export class GeneratorRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyFeedGenerator.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.feed.generator' + const collection = 'app.bsky.feed.generator' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.feed.generator', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -1950,7 +1956,7 @@ export class LikeRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyFeedLike.Record }[] @@ -1963,7 +1969,7 @@ export class LikeRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyFeedLike.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.feed.like', @@ -1973,25 +1979,25 @@ export class LikeRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyFeedLike.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.feed.like' + const collection = 'app.bsky.feed.like' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.feed.like', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2011,7 +2017,7 @@ export class PostRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyFeedPost.Record }[] @@ -2024,7 +2030,7 @@ export class PostRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyFeedPost.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.feed.post', @@ -2034,25 +2040,25 @@ export class PostRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyFeedPost.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.feed.post' + const collection = 'app.bsky.feed.post' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.feed.post', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2072,7 +2078,7 @@ export class PostgateRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyFeedPostgate.Record }[] @@ -2085,7 +2091,7 @@ export class PostgateRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyFeedPostgate.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.feed.postgate', @@ -2095,25 +2101,25 @@ export class PostgateRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyFeedPostgate.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.feed.postgate' + const collection = 'app.bsky.feed.postgate' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.feed.postgate', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2133,7 +2139,7 @@ export class RepostRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyFeedRepost.Record }[] @@ -2146,7 +2152,7 @@ export class RepostRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyFeedRepost.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.feed.repost', @@ -2156,25 +2162,25 @@ export class RepostRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyFeedRepost.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.feed.repost' + const collection = 'app.bsky.feed.repost' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.feed.repost', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2194,7 +2200,7 @@ export class ThreadgateRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyFeedThreadgate.Record }[] @@ -2207,7 +2213,7 @@ export class ThreadgateRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string cid: string @@ -2221,25 +2227,25 @@ export class ThreadgateRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyFeedThreadgate.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.feed.threadgate' + const collection = 'app.bsky.feed.threadgate' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.feed.threadgate', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2500,7 +2506,7 @@ export class BlockRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyGraphBlock.Record }[] @@ -2513,7 +2519,7 @@ export class BlockRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyGraphBlock.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.graph.block', @@ -2523,25 +2529,25 @@ export class BlockRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyGraphBlock.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.graph.block' + const collection = 'app.bsky.graph.block' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.graph.block', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2561,7 +2567,7 @@ export class FollowRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyGraphFollow.Record }[] @@ -2574,7 +2580,7 @@ export class FollowRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyGraphFollow.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.graph.follow', @@ -2584,25 +2590,25 @@ export class FollowRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyGraphFollow.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.graph.follow' + const collection = 'app.bsky.graph.follow' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.graph.follow', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2622,7 +2628,7 @@ export class ListRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyGraphList.Record }[] @@ -2635,7 +2641,7 @@ export class ListRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyGraphList.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.graph.list', @@ -2645,25 +2651,25 @@ export class ListRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyGraphList.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.graph.list' + const collection = 'app.bsky.graph.list' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.graph.list', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2683,7 +2689,7 @@ export class ListblockRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyGraphListblock.Record }[] @@ -2696,7 +2702,7 @@ export class ListblockRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string cid: string @@ -2710,25 +2716,25 @@ export class ListblockRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyGraphListblock.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.graph.listblock' + const collection = 'app.bsky.graph.listblock' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.graph.listblock', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2748,7 +2754,7 @@ export class ListitemRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyGraphListitem.Record }[] @@ -2761,7 +2767,7 @@ export class ListitemRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string; cid: string; value: AppBskyGraphListitem.Record }> { const res = await this._client.call('com.atproto.repo.getRecord', { collection: 'app.bsky.graph.listitem', @@ -2771,25 +2777,25 @@ export class ListitemRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyGraphListitem.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.graph.listitem' + const collection = 'app.bsky.graph.listitem' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.graph.listitem', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2809,7 +2815,7 @@ export class StarterpackRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyGraphStarterpack.Record }[] @@ -2822,7 +2828,7 @@ export class StarterpackRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string cid: string @@ -2836,25 +2842,25 @@ export class StarterpackRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyGraphStarterpack.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.graph.starterpack' + const collection = 'app.bsky.graph.starterpack' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, - { collection: 'app.bsky.graph.starterpack', ...params, record }, + { collection, ...params, record: { ...record, $type: collection } }, { encoding: 'application/json', headers }, ) return res.data } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -2896,7 +2902,7 @@ export class ServiceRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: AppBskyLabelerService.Record }[] @@ -2909,7 +2915,7 @@ export class ServiceRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string cid: string @@ -2923,22 +2929,22 @@ export class ServiceRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: AppBskyLabelerService.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'app.bsky.labeler.service' + const collection = 'app.bsky.labeler.service' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, { - collection: 'app.bsky.labeler.service', + collection, rkey: 'self', ...params, - record, + record: { ...record, $type: collection }, }, { encoding: 'application/json', headers }, ) @@ -2946,7 +2952,7 @@ export class ServiceRecord { } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( @@ -3233,7 +3239,7 @@ export class DeclarationRecord { } async list( - params: Omit, + params: OmitKey, ): Promise<{ cursor?: string records: { uri: string; value: ChatBskyActorDeclaration.Record }[] @@ -3246,7 +3252,7 @@ export class DeclarationRecord { } async get( - params: Omit, + params: OmitKey, ): Promise<{ uri: string cid: string @@ -3260,22 +3266,22 @@ export class DeclarationRecord { } async create( - params: Omit< + params: OmitKey< ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record' >, record: ChatBskyActorDeclaration.Record, headers?: Record, ): Promise<{ uri: string; cid: string }> { - record.$type = 'chat.bsky.actor.declaration' + const collection = 'chat.bsky.actor.declaration' const res = await this._client.call( 'com.atproto.repo.createRecord', undefined, { - collection: 'chat.bsky.actor.declaration', + collection, rkey: 'self', ...params, - record, + record: { ...record, $type: collection }, }, { encoding: 'application/json', headers }, ) @@ -3283,7 +3289,7 @@ export class DeclarationRecord { } async delete( - params: Omit, + params: OmitKey, headers?: Record, ): Promise { await this._client.call( diff --git a/packages/api/src/client/lexicons.ts b/packages/api/src/client/lexicons.ts index 1e616f1b284..bae84bbd1db 100644 --- a/packages/api/src/client/lexicons.ts +++ b/packages/api/src/client/lexicons.ts @@ -13756,4 +13756,4 @@ export const ids = { ToolsOzoneTeamDeleteMember: 'tools.ozone.team.deleteMember', ToolsOzoneTeamListMembers: 'tools.ozone.team.listMembers', ToolsOzoneTeamUpdateMember: 'tools.ozone.team.updateMember', -} +} as const diff --git a/packages/api/src/client/types/app/bsky/actor/defs.ts b/packages/api/src/client/types/app/bsky/actor/defs.ts index c18f5618e77..539e80cf6a5 100644 --- a/packages/api/src/client/types/app/bsky/actor/defs.ts +++ b/packages/api/src/client/types/app/bsky/actor/defs.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' import * as AppBskyGraphDefs from '../graph/defs' @@ -12,6 +12,7 @@ import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' const id = 'app.bsky.actor.defs' export interface ProfileViewBasic { + $type?: $Type<'app.bsky.actor.defs', 'profileViewBasic'> did: string handle: string displayName?: string @@ -20,12 +21,9 @@ export interface ProfileViewBasic { viewer?: ViewerState labels?: ComAtprotoLabelDefs.Label[] createdAt?: string - [k: string]: unknown } -export function isProfileViewBasic(v: unknown): v is ProfileViewBasic & { - $type: $Type<'app.bsky.actor.defs', 'profileViewBasic'> -} { +export function isProfileViewBasic(v: V) { return is$typed(v, id, 'profileViewBasic') } @@ -36,7 +34,14 @@ export function validateProfileViewBasic(v: unknown) { ) as ValidationResult } +export function isValidProfileViewBasic( + v: V, +): v is V & $Typed { + return isProfileViewBasic(v) && validateProfileViewBasic(v).success +} + export interface ProfileView { + $type?: $Type<'app.bsky.actor.defs', 'profileView'> did: string handle: string displayName?: string @@ -47,12 +52,9 @@ export interface ProfileView { createdAt?: string viewer?: ViewerState labels?: ComAtprotoLabelDefs.Label[] - [k: string]: unknown } -export function isProfileView( - v: unknown, -): v is ProfileView & { $type: $Type<'app.bsky.actor.defs', 'profileView'> } { +export function isProfileView(v: V) { return is$typed(v, id, 'profileView') } @@ -63,7 +65,12 @@ export function validateProfileView(v: unknown) { ) as ValidationResult } +export function isValidProfileView(v: V): v is V & $Typed { + return isProfileView(v) && validateProfileView(v).success +} + export interface ProfileViewDetailed { + $type?: $Type<'app.bsky.actor.defs', 'profileViewDetailed'> did: string handle: string displayName?: string @@ -80,12 +87,9 @@ export interface ProfileViewDetailed { viewer?: ViewerState labels?: ComAtprotoLabelDefs.Label[] pinnedPost?: ComAtprotoRepoStrongRef.Main - [k: string]: unknown } -export function isProfileViewDetailed(v: unknown): v is ProfileViewDetailed & { - $type: $Type<'app.bsky.actor.defs', 'profileViewDetailed'> -} { +export function isProfileViewDetailed(v: V) { return is$typed(v, id, 'profileViewDetailed') } @@ -96,18 +100,22 @@ export function validateProfileViewDetailed(v: unknown) { ) as ValidationResult } +export function isValidProfileViewDetailed( + v: V, +): v is V & $Typed { + return isProfileViewDetailed(v) && validateProfileViewDetailed(v).success +} + export interface ProfileAssociated { + $type?: $Type<'app.bsky.actor.defs', 'profileAssociated'> lists?: number feedgens?: number starterPacks?: number labeler?: boolean chat?: ProfileAssociatedChat - [k: string]: unknown } -export function isProfileAssociated(v: unknown): v is ProfileAssociated & { - $type: $Type<'app.bsky.actor.defs', 'profileAssociated'> -} { +export function isProfileAssociated(v: V) { return is$typed(v, id, 'profileAssociated') } @@ -118,16 +126,18 @@ export function validateProfileAssociated(v: unknown) { ) as ValidationResult } +export function isValidProfileAssociated( + v: V, +): v is V & $Typed { + return isProfileAssociated(v) && validateProfileAssociated(v).success +} + export interface ProfileAssociatedChat { + $type?: $Type<'app.bsky.actor.defs', 'profileAssociatedChat'> allowIncoming: 'all' | 'none' | 'following' | (string & {}) - [k: string]: unknown } -export function isProfileAssociatedChat( - v: unknown, -): v is ProfileAssociatedChat & { - $type: $Type<'app.bsky.actor.defs', 'profileAssociatedChat'> -} { +export function isProfileAssociatedChat(v: V) { return is$typed(v, id, 'profileAssociatedChat') } @@ -138,8 +148,15 @@ export function validateProfileAssociatedChat(v: unknown) { ) as ValidationResult } +export function isValidProfileAssociatedChat( + v: V, +): v is V & $Typed { + return isProfileAssociatedChat(v) && validateProfileAssociatedChat(v).success +} + /** Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests. */ export interface ViewerState { + $type?: $Type<'app.bsky.actor.defs', 'viewerState'> muted?: boolean mutedByList?: AppBskyGraphDefs.ListViewBasic blockedBy?: boolean @@ -148,12 +165,9 @@ export interface ViewerState { following?: string followedBy?: string knownFollowers?: KnownFollowers - [k: string]: unknown } -export function isViewerState( - v: unknown, -): v is ViewerState & { $type: $Type<'app.bsky.actor.defs', 'viewerState'> } { +export function isViewerState(v: V) { return is$typed(v, id, 'viewerState') } @@ -164,16 +178,18 @@ export function validateViewerState(v: unknown) { ) as ValidationResult } +export function isValidViewerState(v: V): v is V & $Typed { + return isViewerState(v) && validateViewerState(v).success +} + /** The subject's followers whom you also follow */ export interface KnownFollowers { + $type?: $Type<'app.bsky.actor.defs', 'knownFollowers'> count: number followers: ProfileViewBasic[] - [k: string]: unknown } -export function isKnownFollowers(v: unknown): v is KnownFollowers & { - $type: $Type<'app.bsky.actor.defs', 'knownFollowers'> -} { +export function isKnownFollowers(v: V) { return is$typed(v, id, 'knownFollowers') } @@ -184,30 +200,34 @@ export function validateKnownFollowers(v: unknown) { ) as ValidationResult } +export function isValidKnownFollowers( + v: V, +): v is V & $Typed { + return isKnownFollowers(v) && validateKnownFollowers(v).success +} + export type Preferences = ( - | AdultContentPref - | ContentLabelPref - | SavedFeedsPref - | SavedFeedsPrefV2 - | PersonalDetailsPref - | FeedViewPref - | ThreadViewPref - | InterestsPref - | MutedWordsPref - | HiddenPostsPref - | BskyAppStatePref - | LabelersPref - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | { $type: string } )[] export interface AdultContentPref { + $type?: $Type<'app.bsky.actor.defs', 'adultContentPref'> enabled: boolean - [k: string]: unknown } -export function isAdultContentPref(v: unknown): v is AdultContentPref & { - $type: $Type<'app.bsky.actor.defs', 'adultContentPref'> -} { +export function isAdultContentPref(v: V) { return is$typed(v, id, 'adultContentPref') } @@ -218,17 +238,21 @@ export function validateAdultContentPref(v: unknown) { ) as ValidationResult } +export function isValidAdultContentPref( + v: V, +): v is V & $Typed { + return isAdultContentPref(v) && validateAdultContentPref(v).success +} + export interface ContentLabelPref { + $type?: $Type<'app.bsky.actor.defs', 'contentLabelPref'> /** Which labeler does this preference apply to? If undefined, applies globally. */ labelerDid?: string label: string visibility: 'ignore' | 'show' | 'warn' | 'hide' | (string & {}) - [k: string]: unknown } -export function isContentLabelPref(v: unknown): v is ContentLabelPref & { - $type: $Type<'app.bsky.actor.defs', 'contentLabelPref'> -} { +export function isContentLabelPref(v: V) { return is$typed(v, id, 'contentLabelPref') } @@ -239,17 +263,21 @@ export function validateContentLabelPref(v: unknown) { ) as ValidationResult } +export function isValidContentLabelPref( + v: V, +): v is V & $Typed { + return isContentLabelPref(v) && validateContentLabelPref(v).success +} + export interface SavedFeed { + $type?: $Type<'app.bsky.actor.defs', 'savedFeed'> id: string type: 'feed' | 'list' | 'timeline' | (string & {}) value: string pinned: boolean - [k: string]: unknown } -export function isSavedFeed( - v: unknown, -): v is SavedFeed & { $type: $Type<'app.bsky.actor.defs', 'savedFeed'> } { +export function isSavedFeed(v: V) { return is$typed(v, id, 'savedFeed') } @@ -257,14 +285,16 @@ export function validateSavedFeed(v: unknown) { return lexicons.validate(`${id}#savedFeed`, v) as ValidationResult } +export function isValidSavedFeed(v: V): v is V & $Typed { + return isSavedFeed(v) && validateSavedFeed(v).success +} + export interface SavedFeedsPrefV2 { + $type?: $Type<'app.bsky.actor.defs', 'savedFeedsPrefV2'> items: SavedFeed[] - [k: string]: unknown } -export function isSavedFeedsPrefV2(v: unknown): v is SavedFeedsPrefV2 & { - $type: $Type<'app.bsky.actor.defs', 'savedFeedsPrefV2'> -} { +export function isSavedFeedsPrefV2(v: V) { return is$typed(v, id, 'savedFeedsPrefV2') } @@ -275,16 +305,20 @@ export function validateSavedFeedsPrefV2(v: unknown) { ) as ValidationResult } +export function isValidSavedFeedsPrefV2( + v: V, +): v is V & $Typed { + return isSavedFeedsPrefV2(v) && validateSavedFeedsPrefV2(v).success +} + export interface SavedFeedsPref { + $type?: $Type<'app.bsky.actor.defs', 'savedFeedsPref'> pinned: string[] saved: string[] timelineIndex?: number - [k: string]: unknown } -export function isSavedFeedsPref(v: unknown): v is SavedFeedsPref & { - $type: $Type<'app.bsky.actor.defs', 'savedFeedsPref'> -} { +export function isSavedFeedsPref(v: V) { return is$typed(v, id, 'savedFeedsPref') } @@ -295,15 +329,19 @@ export function validateSavedFeedsPref(v: unknown) { ) as ValidationResult } +export function isValidSavedFeedsPref( + v: V, +): v is V & $Typed { + return isSavedFeedsPref(v) && validateSavedFeedsPref(v).success +} + export interface PersonalDetailsPref { + $type?: $Type<'app.bsky.actor.defs', 'personalDetailsPref'> /** The birth date of account owner. */ birthDate?: string - [k: string]: unknown } -export function isPersonalDetailsPref(v: unknown): v is PersonalDetailsPref & { - $type: $Type<'app.bsky.actor.defs', 'personalDetailsPref'> -} { +export function isPersonalDetailsPref(v: V) { return is$typed(v, id, 'personalDetailsPref') } @@ -314,7 +352,14 @@ export function validatePersonalDetailsPref(v: unknown) { ) as ValidationResult } +export function isValidPersonalDetailsPref( + v: V, +): v is V & $Typed { + return isPersonalDetailsPref(v) && validatePersonalDetailsPref(v).success +} + export interface FeedViewPref { + $type?: $Type<'app.bsky.actor.defs', 'feedViewPref'> /** The URI of the feed, or an identifier which describes the feed. */ feed: string /** Hide replies in the feed. */ @@ -327,12 +372,9 @@ export interface FeedViewPref { hideReposts?: boolean /** Hide quote posts in the feed. */ hideQuotePosts?: boolean - [k: string]: unknown } -export function isFeedViewPref( - v: unknown, -): v is FeedViewPref & { $type: $Type<'app.bsky.actor.defs', 'feedViewPref'> } { +export function isFeedViewPref(v: V) { return is$typed(v, id, 'feedViewPref') } @@ -343,7 +385,12 @@ export function validateFeedViewPref(v: unknown) { ) as ValidationResult } +export function isValidFeedViewPref(v: V): v is V & $Typed { + return isFeedViewPref(v) && validateFeedViewPref(v).success +} + export interface ThreadViewPref { + $type?: $Type<'app.bsky.actor.defs', 'threadViewPref'> /** Sorting mode for threads. */ sort?: | 'oldest' @@ -354,12 +401,9 @@ export interface ThreadViewPref { | (string & {}) /** Show followed users at the top of all replies. */ prioritizeFollowedUsers?: boolean - [k: string]: unknown } -export function isThreadViewPref(v: unknown): v is ThreadViewPref & { - $type: $Type<'app.bsky.actor.defs', 'threadViewPref'> -} { +export function isThreadViewPref(v: V) { return is$typed(v, id, 'threadViewPref') } @@ -370,15 +414,19 @@ export function validateThreadViewPref(v: unknown) { ) as ValidationResult } +export function isValidThreadViewPref( + v: V, +): v is V & $Typed { + return isThreadViewPref(v) && validateThreadViewPref(v).success +} + export interface InterestsPref { + $type?: $Type<'app.bsky.actor.defs', 'interestsPref'> /** A list of tags which describe the account owner's interests gathered during onboarding. */ tags: string[] - [k: string]: unknown } -export function isInterestsPref(v: unknown): v is InterestsPref & { - $type: $Type<'app.bsky.actor.defs', 'interestsPref'> -} { +export function isInterestsPref(v: V) { return is$typed(v, id, 'interestsPref') } @@ -389,10 +437,15 @@ export function validateInterestsPref(v: unknown) { ) as ValidationResult } +export function isValidInterestsPref(v: V): v is V & $Typed { + return isInterestsPref(v) && validateInterestsPref(v).success +} + export type MutedWordTarget = 'content' | 'tag' | (string & {}) /** A word that the account owner has muted. */ export interface MutedWord { + $type?: $Type<'app.bsky.actor.defs', 'mutedWord'> id?: string /** The muted word itself. */ value: string @@ -402,12 +455,9 @@ export interface MutedWord { actorTarget: 'all' | 'exclude-following' | (string & {}) /** The date and time at which the muted word will expire and no longer be applied. */ expiresAt?: string - [k: string]: unknown } -export function isMutedWord( - v: unknown, -): v is MutedWord & { $type: $Type<'app.bsky.actor.defs', 'mutedWord'> } { +export function isMutedWord(v: V) { return is$typed(v, id, 'mutedWord') } @@ -415,15 +465,17 @@ export function validateMutedWord(v: unknown) { return lexicons.validate(`${id}#mutedWord`, v) as ValidationResult } +export function isValidMutedWord(v: V): v is V & $Typed { + return isMutedWord(v) && validateMutedWord(v).success +} + export interface MutedWordsPref { + $type?: $Type<'app.bsky.actor.defs', 'mutedWordsPref'> /** A list of words the account owner has muted. */ items: MutedWord[] - [k: string]: unknown } -export function isMutedWordsPref(v: unknown): v is MutedWordsPref & { - $type: $Type<'app.bsky.actor.defs', 'mutedWordsPref'> -} { +export function isMutedWordsPref(v: V) { return is$typed(v, id, 'mutedWordsPref') } @@ -434,15 +486,19 @@ export function validateMutedWordsPref(v: unknown) { ) as ValidationResult } +export function isValidMutedWordsPref( + v: V, +): v is V & $Typed { + return isMutedWordsPref(v) && validateMutedWordsPref(v).success +} + export interface HiddenPostsPref { + $type?: $Type<'app.bsky.actor.defs', 'hiddenPostsPref'> /** A list of URIs of posts the account owner has hidden. */ items: string[] - [k: string]: unknown } -export function isHiddenPostsPref(v: unknown): v is HiddenPostsPref & { - $type: $Type<'app.bsky.actor.defs', 'hiddenPostsPref'> -} { +export function isHiddenPostsPref(v: V) { return is$typed(v, id, 'hiddenPostsPref') } @@ -453,14 +509,18 @@ export function validateHiddenPostsPref(v: unknown) { ) as ValidationResult } +export function isValidHiddenPostsPref( + v: V, +): v is V & $Typed { + return isHiddenPostsPref(v) && validateHiddenPostsPref(v).success +} + export interface LabelersPref { + $type?: $Type<'app.bsky.actor.defs', 'labelersPref'> labelers: LabelerPrefItem[] - [k: string]: unknown } -export function isLabelersPref( - v: unknown, -): v is LabelersPref & { $type: $Type<'app.bsky.actor.defs', 'labelersPref'> } { +export function isLabelersPref(v: V) { return is$typed(v, id, 'labelersPref') } @@ -471,14 +531,16 @@ export function validateLabelersPref(v: unknown) { ) as ValidationResult } +export function isValidLabelersPref(v: V): v is V & $Typed { + return isLabelersPref(v) && validateLabelersPref(v).success +} + export interface LabelerPrefItem { + $type?: $Type<'app.bsky.actor.defs', 'labelerPrefItem'> did: string - [k: string]: unknown } -export function isLabelerPrefItem(v: unknown): v is LabelerPrefItem & { - $type: $Type<'app.bsky.actor.defs', 'labelerPrefItem'> -} { +export function isLabelerPrefItem(v: V) { return is$typed(v, id, 'labelerPrefItem') } @@ -489,19 +551,23 @@ export function validateLabelerPrefItem(v: unknown) { ) as ValidationResult } +export function isValidLabelerPrefItem( + v: V, +): v is V & $Typed { + return isLabelerPrefItem(v) && validateLabelerPrefItem(v).success +} + /** A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this. */ export interface BskyAppStatePref { + $type?: $Type<'app.bsky.actor.defs', 'bskyAppStatePref'> activeProgressGuide?: BskyAppProgressGuide /** An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user. */ queuedNudges?: string[] /** Storage for NUXs the user has encountered. */ nuxs?: Nux[] - [k: string]: unknown } -export function isBskyAppStatePref(v: unknown): v is BskyAppStatePref & { - $type: $Type<'app.bsky.actor.defs', 'bskyAppStatePref'> -} { +export function isBskyAppStatePref(v: V) { return is$typed(v, id, 'bskyAppStatePref') } @@ -512,17 +578,19 @@ export function validateBskyAppStatePref(v: unknown) { ) as ValidationResult } +export function isValidBskyAppStatePref( + v: V, +): v is V & $Typed { + return isBskyAppStatePref(v) && validateBskyAppStatePref(v).success +} + /** If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress. */ export interface BskyAppProgressGuide { + $type?: $Type<'app.bsky.actor.defs', 'bskyAppProgressGuide'> guide: string - [k: string]: unknown } -export function isBskyAppProgressGuide( - v: unknown, -): v is BskyAppProgressGuide & { - $type: $Type<'app.bsky.actor.defs', 'bskyAppProgressGuide'> -} { +export function isBskyAppProgressGuide(v: V) { return is$typed(v, id, 'bskyAppProgressGuide') } @@ -533,23 +601,31 @@ export function validateBskyAppProgressGuide(v: unknown) { ) as ValidationResult } +export function isValidBskyAppProgressGuide( + v: V, +): v is V & $Typed { + return isBskyAppProgressGuide(v) && validateBskyAppProgressGuide(v).success +} + /** A new user experiences (NUX) storage object */ export interface Nux { + $type?: $Type<'app.bsky.actor.defs', 'nux'> id: string completed: boolean /** Arbitrary data for the NUX. The structure is defined by the NUX itself. Limited to 300 characters. */ data?: string /** The date and time at which the NUX will expire and should be considered completed. */ expiresAt?: string - [k: string]: unknown } -export function isNux( - v: unknown, -): v is Nux & { $type: $Type<'app.bsky.actor.defs', 'nux'> } { +export function isNux(v: V) { return is$typed(v, id, 'nux') } export function validateNux(v: unknown) { return lexicons.validate(`${id}#nux`, v) as ValidationResult } + +export function isValidNux(v: V): v is V & $Typed { + return isNux(v) && validateNux(v).success +} diff --git a/packages/api/src/client/types/app/bsky/actor/getPreferences.ts b/packages/api/src/client/types/app/bsky/actor/getPreferences.ts index 31e7e98c29d..ef002fe74fd 100644 --- a/packages/api/src/client/types/app/bsky/actor/getPreferences.ts +++ b/packages/api/src/client/types/app/bsky/actor/getPreferences.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' @@ -16,7 +16,6 @@ export type InputSchema = undefined export interface OutputSchema { preferences: AppBskyActorDefs.Preferences - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/actor/getProfile.ts b/packages/api/src/client/types/app/bsky/actor/getProfile.ts index 6b9b7bc9759..83bcaea585c 100644 --- a/packages/api/src/client/types/app/bsky/actor/getProfile.ts +++ b/packages/api/src/client/types/app/bsky/actor/getProfile.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' diff --git a/packages/api/src/client/types/app/bsky/actor/getProfiles.ts b/packages/api/src/client/types/app/bsky/actor/getProfiles.ts index cb37b97f1cf..b96d99d9271 100644 --- a/packages/api/src/client/types/app/bsky/actor/getProfiles.ts +++ b/packages/api/src/client/types/app/bsky/actor/getProfiles.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { profiles: AppBskyActorDefs.ProfileViewDetailed[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/actor/getSuggestions.ts b/packages/api/src/client/types/app/bsky/actor/getSuggestions.ts index 972adadf066..88900ab15f8 100644 --- a/packages/api/src/client/types/app/bsky/actor/getSuggestions.ts +++ b/packages/api/src/client/types/app/bsky/actor/getSuggestions.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string actors: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/actor/profile.ts b/packages/api/src/client/types/app/bsky/actor/profile.ts index 9463df7cd24..cb00c38b744 100644 --- a/packages/api/src/client/types/app/bsky/actor/profile.ts +++ b/packages/api/src/client/types/app/bsky/actor/profile.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' @@ -11,6 +11,7 @@ import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' const id = 'app.bsky.actor.profile' export interface Record { + $type?: $Type<'app.bsky.actor.profile', 'main'> displayName?: string /** Free-form profile description text. */ description?: string @@ -18,21 +19,21 @@ export interface Record { avatar?: BlobRef /** Larger horizontal image to display behind profile view. */ banner?: BlobRef - labels?: - | ComAtprotoLabelDefs.SelfLabels - | { $type: string; [k: string]: unknown } + labels?: $Typed | { $type: string } joinedViaStarterPack?: ComAtprotoRepoStrongRef.Main pinnedPost?: ComAtprotoRepoStrongRef.Main createdAt?: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.actor.profile', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/actor/putPreferences.ts b/packages/api/src/client/types/app/bsky/actor/putPreferences.ts index 05f013edf7f..c8bfc079ac5 100644 --- a/packages/api/src/client/types/app/bsky/actor/putPreferences.ts +++ b/packages/api/src/client/types/app/bsky/actor/putPreferences.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' @@ -14,7 +14,6 @@ export interface QueryParams {} export interface InputSchema { preferences: AppBskyActorDefs.Preferences - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/actor/searchActors.ts b/packages/api/src/client/types/app/bsky/actor/searchActors.ts index d9b695f3e07..5b68d7ada38 100644 --- a/packages/api/src/client/types/app/bsky/actor/searchActors.ts +++ b/packages/api/src/client/types/app/bsky/actor/searchActors.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' @@ -24,7 +24,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string actors: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/actor/searchActorsTypeahead.ts b/packages/api/src/client/types/app/bsky/actor/searchActorsTypeahead.ts index 69ca089e356..2e022101e98 100644 --- a/packages/api/src/client/types/app/bsky/actor/searchActorsTypeahead.ts +++ b/packages/api/src/client/types/app/bsky/actor/searchActorsTypeahead.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from './defs' @@ -22,7 +22,6 @@ export type InputSchema = undefined export interface OutputSchema { actors: AppBskyActorDefs.ProfileViewBasic[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/embed/defs.ts b/packages/api/src/client/types/app/bsky/embed/defs.ts index 662d2ea7b80..3923320bfe1 100644 --- a/packages/api/src/client/types/app/bsky/embed/defs.ts +++ b/packages/api/src/client/types/app/bsky/embed/defs.ts @@ -3,21 +3,19 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.embed.defs' /** width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit. */ export interface AspectRatio { + $type?: $Type<'app.bsky.embed.defs', 'aspectRatio'> width: number height: number - [k: string]: unknown } -export function isAspectRatio( - v: unknown, -): v is AspectRatio & { $type: $Type<'app.bsky.embed.defs', 'aspectRatio'> } { +export function isAspectRatio(v: V) { return is$typed(v, id, 'aspectRatio') } @@ -27,3 +25,7 @@ export function validateAspectRatio(v: unknown) { v, ) as ValidationResult } + +export function isValidAspectRatio(v: V): v is V & $Typed { + return isAspectRatio(v) && validateAspectRatio(v).success +} diff --git a/packages/api/src/client/types/app/bsky/embed/external.ts b/packages/api/src/client/types/app/bsky/embed/external.ts index 0ca9b292aa2..9379b85c192 100644 --- a/packages/api/src/client/types/app/bsky/embed/external.ts +++ b/packages/api/src/client/types/app/bsky/embed/external.ts @@ -3,20 +3,18 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.embed.external' /** A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post). */ export interface Main { + $type?: $Type<'app.bsky.embed.external', 'main'> external: External - [k: string]: unknown } -export function isMain( - v: unknown, -): v is Main & { $type: $Type<'app.bsky.embed.external', 'main'> } { +export function isMain(v: V) { return is$typed(v, id, 'main') } @@ -24,17 +22,19 @@ export function validateMain(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult
} +export function isValidMain(v: V): v is V & $Typed
{ + return isMain(v) && validateMain(v).success +} + export interface External { + $type?: $Type<'app.bsky.embed.external', 'external'> uri: string title: string description: string thumb?: BlobRef - [k: string]: unknown } -export function isExternal( - v: unknown, -): v is External & { $type: $Type<'app.bsky.embed.external', 'external'> } { +export function isExternal(v: V) { return is$typed(v, id, 'external') } @@ -42,14 +42,16 @@ export function validateExternal(v: unknown) { return lexicons.validate(`${id}#external`, v) as ValidationResult } +export function isValidExternal(v: V): v is V & $Typed { + return isExternal(v) && validateExternal(v).success +} + export interface View { + $type?: $Type<'app.bsky.embed.external', 'view'> external: ViewExternal - [k: string]: unknown } -export function isView( - v: unknown, -): v is View & { $type: $Type<'app.bsky.embed.external', 'view'> } { +export function isView(v: V) { return is$typed(v, id, 'view') } @@ -57,17 +59,19 @@ export function validateView(v: unknown) { return lexicons.validate(`${id}#view`, v) as ValidationResult } +export function isValidView(v: V): v is V & $Typed { + return isView(v) && validateView(v).success +} + export interface ViewExternal { + $type?: $Type<'app.bsky.embed.external', 'viewExternal'> uri: string title: string description: string thumb?: string - [k: string]: unknown } -export function isViewExternal(v: unknown): v is ViewExternal & { - $type: $Type<'app.bsky.embed.external', 'viewExternal'> -} { +export function isViewExternal(v: V) { return is$typed(v, id, 'viewExternal') } @@ -77,3 +81,7 @@ export function validateViewExternal(v: unknown) { v, ) as ValidationResult } + +export function isValidViewExternal(v: V): v is V & $Typed { + return isViewExternal(v) && validateViewExternal(v).success +} diff --git a/packages/api/src/client/types/app/bsky/embed/images.ts b/packages/api/src/client/types/app/bsky/embed/images.ts index 81fc960b1ab..ef8d039b33b 100644 --- a/packages/api/src/client/types/app/bsky/embed/images.ts +++ b/packages/api/src/client/types/app/bsky/embed/images.ts @@ -3,20 +3,18 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyEmbedDefs from './defs' const id = 'app.bsky.embed.images' export interface Main { + $type?: $Type<'app.bsky.embed.images', 'main'> images: Image[] - [k: string]: unknown } -export function isMain( - v: unknown, -): v is Main & { $type: $Type<'app.bsky.embed.images', 'main'> } { +export function isMain(v: V) { return is$typed(v, id, 'main') } @@ -24,17 +22,19 @@ export function validateMain(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult
} +export function isValidMain(v: V): v is V & $Typed
{ + return isMain(v) && validateMain(v).success +} + export interface Image { + $type?: $Type<'app.bsky.embed.images', 'image'> image: BlobRef /** Alt text description of the image, for accessibility. */ alt: string aspectRatio?: AppBskyEmbedDefs.AspectRatio - [k: string]: unknown } -export function isImage( - v: unknown, -): v is Image & { $type: $Type<'app.bsky.embed.images', 'image'> } { +export function isImage(v: V) { return is$typed(v, id, 'image') } @@ -42,14 +42,16 @@ export function validateImage(v: unknown) { return lexicons.validate(`${id}#image`, v) as ValidationResult } +export function isValidImage(v: V): v is V & $Typed { + return isImage(v) && validateImage(v).success +} + export interface View { + $type?: $Type<'app.bsky.embed.images', 'view'> images: ViewImage[] - [k: string]: unknown } -export function isView( - v: unknown, -): v is View & { $type: $Type<'app.bsky.embed.images', 'view'> } { +export function isView(v: V) { return is$typed(v, id, 'view') } @@ -57,7 +59,12 @@ export function validateView(v: unknown) { return lexicons.validate(`${id}#view`, v) as ValidationResult } +export function isValidView(v: V): v is V & $Typed { + return isView(v) && validateView(v).success +} + export interface ViewImage { + $type?: $Type<'app.bsky.embed.images', 'viewImage'> /** Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View. */ thumb: string /** Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View. */ @@ -65,15 +72,16 @@ export interface ViewImage { /** Alt text description of the image, for accessibility. */ alt: string aspectRatio?: AppBskyEmbedDefs.AspectRatio - [k: string]: unknown } -export function isViewImage( - v: unknown, -): v is ViewImage & { $type: $Type<'app.bsky.embed.images', 'viewImage'> } { +export function isViewImage(v: V) { return is$typed(v, id, 'viewImage') } export function validateViewImage(v: unknown) { return lexicons.validate(`${id}#viewImage`, v) as ValidationResult } + +export function isValidViewImage(v: V): v is V & $Typed { + return isViewImage(v) && validateViewImage(v).success +} diff --git a/packages/api/src/client/types/app/bsky/embed/record.ts b/packages/api/src/client/types/app/bsky/embed/record.ts index 5b2bc9a62b6..2157e66839c 100644 --- a/packages/api/src/client/types/app/bsky/embed/record.ts +++ b/packages/api/src/client/types/app/bsky/embed/record.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' import * as AppBskyFeedDefs from '../feed/defs' @@ -19,13 +19,11 @@ import * as AppBskyEmbedRecordWithMedia from './recordWithMedia' const id = 'app.bsky.embed.record' export interface Main { + $type?: $Type<'app.bsky.embed.record', 'main'> record: ComAtprotoRepoStrongRef.Main - [k: string]: unknown } -export function isMain( - v: unknown, -): v is Main & { $type: $Type<'app.bsky.embed.record', 'main'> } { +export function isMain(v: V) { return is$typed(v, id, 'main') } @@ -33,23 +31,25 @@ export function validateMain(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult
} +export function isValidMain(v: V): v is V & $Typed
{ + return isMain(v) && validateMain(v).success +} + export interface View { + $type?: $Type<'app.bsky.embed.record', 'view'> record: - | ViewRecord - | ViewNotFound - | ViewBlocked - | ViewDetached - | AppBskyFeedDefs.GeneratorView - | AppBskyGraphDefs.ListView - | AppBskyLabelerDefs.LabelerView - | AppBskyGraphDefs.StarterPackViewBasic - | { $type: string; [k: string]: unknown } - [k: string]: unknown -} - -export function isView( - v: unknown, -): v is View & { $type: $Type<'app.bsky.embed.record', 'view'> } { + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | { $type: string } +} + +export function isView(v: V) { return is$typed(v, id, 'view') } @@ -57,32 +57,34 @@ export function validateView(v: unknown) { return lexicons.validate(`${id}#view`, v) as ValidationResult } +export function isValidView(v: V): v is V & $Typed { + return isView(v) && validateView(v).success +} + export interface ViewRecord { + $type?: $Type<'app.bsky.embed.record', 'viewRecord'> uri: string cid: string author: AppBskyActorDefs.ProfileViewBasic /** The record data itself. */ - value: {} + value: { [_ in string]: unknown } labels?: ComAtprotoLabelDefs.Label[] replyCount?: number repostCount?: number likeCount?: number quoteCount?: number embeds?: ( - | AppBskyEmbedImages.View - | AppBskyEmbedVideo.View - | AppBskyEmbedExternal.View - | View - | AppBskyEmbedRecordWithMedia.View - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | { $type: string } )[] indexedAt: string - [k: string]: unknown } -export function isViewRecord( - v: unknown, -): v is ViewRecord & { $type: $Type<'app.bsky.embed.record', 'viewRecord'> } { +export function isViewRecord(v: V) { return is$typed(v, id, 'viewRecord') } @@ -93,15 +95,17 @@ export function validateViewRecord(v: unknown) { ) as ValidationResult } +export function isValidViewRecord(v: V): v is V & $Typed { + return isViewRecord(v) && validateViewRecord(v).success +} + export interface ViewNotFound { + $type?: $Type<'app.bsky.embed.record', 'viewNotFound'> uri: string notFound: true - [k: string]: unknown } -export function isViewNotFound(v: unknown): v is ViewNotFound & { - $type: $Type<'app.bsky.embed.record', 'viewNotFound'> -} { +export function isViewNotFound(v: V) { return is$typed(v, id, 'viewNotFound') } @@ -112,16 +116,18 @@ export function validateViewNotFound(v: unknown) { ) as ValidationResult } +export function isValidViewNotFound(v: V): v is V & $Typed { + return isViewNotFound(v) && validateViewNotFound(v).success +} + export interface ViewBlocked { + $type?: $Type<'app.bsky.embed.record', 'viewBlocked'> uri: string blocked: true author: AppBskyFeedDefs.BlockedAuthor - [k: string]: unknown } -export function isViewBlocked( - v: unknown, -): v is ViewBlocked & { $type: $Type<'app.bsky.embed.record', 'viewBlocked'> } { +export function isViewBlocked(v: V) { return is$typed(v, id, 'viewBlocked') } @@ -132,15 +138,17 @@ export function validateViewBlocked(v: unknown) { ) as ValidationResult } +export function isValidViewBlocked(v: V): v is V & $Typed { + return isViewBlocked(v) && validateViewBlocked(v).success +} + export interface ViewDetached { + $type?: $Type<'app.bsky.embed.record', 'viewDetached'> uri: string detached: true - [k: string]: unknown } -export function isViewDetached(v: unknown): v is ViewDetached & { - $type: $Type<'app.bsky.embed.record', 'viewDetached'> -} { +export function isViewDetached(v: V) { return is$typed(v, id, 'viewDetached') } @@ -150,3 +158,7 @@ export function validateViewDetached(v: unknown) { v, ) as ValidationResult } + +export function isValidViewDetached(v: V): v is V & $Typed { + return isViewDetached(v) && validateViewDetached(v).success +} diff --git a/packages/api/src/client/types/app/bsky/embed/recordWithMedia.ts b/packages/api/src/client/types/app/bsky/embed/recordWithMedia.ts index 7c382d41853..6f121db0cd1 100644 --- a/packages/api/src/client/types/app/bsky/embed/recordWithMedia.ts +++ b/packages/api/src/client/types/app/bsky/embed/recordWithMedia.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyEmbedRecord from './record' import * as AppBskyEmbedImages from './images' @@ -13,18 +13,16 @@ import * as AppBskyEmbedExternal from './external' const id = 'app.bsky.embed.recordWithMedia' export interface Main { + $type?: $Type<'app.bsky.embed.recordWithMedia', 'main'> record: AppBskyEmbedRecord.Main media: - | AppBskyEmbedImages.Main - | AppBskyEmbedVideo.Main - | AppBskyEmbedExternal.Main - | { $type: string; [k: string]: unknown } - [k: string]: unknown + | $Typed + | $Typed + | $Typed + | { $type: string } } -export function isMain( - v: unknown, -): v is Main & { $type: $Type<'app.bsky.embed.recordWithMedia', 'main'> } { +export function isMain(v: V) { return is$typed(v, id, 'main') } @@ -32,22 +30,28 @@ export function validateMain(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult
} +export function isValidMain(v: V): v is V & $Typed
{ + return isMain(v) && validateMain(v).success +} + export interface View { + $type?: $Type<'app.bsky.embed.recordWithMedia', 'view'> record: AppBskyEmbedRecord.View media: - | AppBskyEmbedImages.View - | AppBskyEmbedVideo.View - | AppBskyEmbedExternal.View - | { $type: string; [k: string]: unknown } - [k: string]: unknown + | $Typed + | $Typed + | $Typed + | { $type: string } } -export function isView( - v: unknown, -): v is View & { $type: $Type<'app.bsky.embed.recordWithMedia', 'view'> } { +export function isView(v: V) { return is$typed(v, id, 'view') } export function validateView(v: unknown) { return lexicons.validate(`${id}#view`, v) as ValidationResult } + +export function isValidView(v: V): v is V & $Typed { + return isView(v) && validateView(v).success +} diff --git a/packages/api/src/client/types/app/bsky/embed/video.ts b/packages/api/src/client/types/app/bsky/embed/video.ts index eefabf0f660..a8657a135db 100644 --- a/packages/api/src/client/types/app/bsky/embed/video.ts +++ b/packages/api/src/client/types/app/bsky/embed/video.ts @@ -3,24 +3,22 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyEmbedDefs from './defs' const id = 'app.bsky.embed.video' export interface Main { + $type?: $Type<'app.bsky.embed.video', 'main'> video: BlobRef captions?: Caption[] /** Alt text description of the video, for accessibility. */ alt?: string aspectRatio?: AppBskyEmbedDefs.AspectRatio - [k: string]: unknown } -export function isMain( - v: unknown, -): v is Main & { $type: $Type<'app.bsky.embed.video', 'main'> } { +export function isMain(v: V) { return is$typed(v, id, 'main') } @@ -28,15 +26,17 @@ export function validateMain(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult
} +export function isValidMain(v: V): v is V & $Typed
{ + return isMain(v) && validateMain(v).success +} + export interface Caption { + $type?: $Type<'app.bsky.embed.video', 'caption'> lang: string file: BlobRef - [k: string]: unknown } -export function isCaption( - v: unknown, -): v is Caption & { $type: $Type<'app.bsky.embed.video', 'caption'> } { +export function isCaption(v: V) { return is$typed(v, id, 'caption') } @@ -44,21 +44,27 @@ export function validateCaption(v: unknown) { return lexicons.validate(`${id}#caption`, v) as ValidationResult } +export function isValidCaption(v: V): v is V & $Typed { + return isCaption(v) && validateCaption(v).success +} + export interface View { + $type?: $Type<'app.bsky.embed.video', 'view'> cid: string playlist: string thumbnail?: string alt?: string aspectRatio?: AppBskyEmbedDefs.AspectRatio - [k: string]: unknown } -export function isView( - v: unknown, -): v is View & { $type: $Type<'app.bsky.embed.video', 'view'> } { +export function isView(v: V) { return is$typed(v, id, 'view') } export function validateView(v: unknown) { return lexicons.validate(`${id}#view`, v) as ValidationResult } + +export function isValidView(v: V): v is V & $Typed { + return isView(v) && validateView(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/defs.ts b/packages/api/src/client/types/app/bsky/feed/defs.ts index bb2693d18dc..6871f368658 100644 --- a/packages/api/src/client/types/app/bsky/feed/defs.ts +++ b/packages/api/src/client/types/app/bsky/feed/defs.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' import * as AppBskyEmbedImages from '../embed/images' @@ -18,17 +18,18 @@ import * as AppBskyGraphDefs from '../graph/defs' const id = 'app.bsky.feed.defs' export interface PostView { + $type?: $Type<'app.bsky.feed.defs', 'postView'> uri: string cid: string author: AppBskyActorDefs.ProfileViewBasic - record: {} + record: { [_ in string]: unknown } embed?: - | AppBskyEmbedImages.View - | AppBskyEmbedVideo.View - | AppBskyEmbedExternal.View - | AppBskyEmbedRecord.View - | AppBskyEmbedRecordWithMedia.View - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | { $type: string } replyCount?: number repostCount?: number likeCount?: number @@ -37,12 +38,9 @@ export interface PostView { viewer?: ViewerState labels?: ComAtprotoLabelDefs.Label[] threadgate?: ThreadgateView - [k: string]: unknown } -export function isPostView( - v: unknown, -): v is PostView & { $type: $Type<'app.bsky.feed.defs', 'postView'> } { +export function isPostView(v: V) { return is$typed(v, id, 'postView') } @@ -50,20 +48,22 @@ export function validatePostView(v: unknown) { return lexicons.validate(`${id}#postView`, v) as ValidationResult } +export function isValidPostView(v: V): v is V & $Typed { + return isPostView(v) && validatePostView(v).success +} + /** Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests. */ export interface ViewerState { + $type?: $Type<'app.bsky.feed.defs', 'viewerState'> repost?: string like?: string threadMuted?: boolean replyDisabled?: boolean embeddingDisabled?: boolean pinned?: boolean - [k: string]: unknown } -export function isViewerState( - v: unknown, -): v is ViewerState & { $type: $Type<'app.bsky.feed.defs', 'viewerState'> } { +export function isViewerState(v: V) { return is$typed(v, id, 'viewerState') } @@ -74,18 +74,20 @@ export function validateViewerState(v: unknown) { ) as ValidationResult } +export function isValidViewerState(v: V): v is V & $Typed { + return isViewerState(v) && validateViewerState(v).success +} + export interface FeedViewPost { + $type?: $Type<'app.bsky.feed.defs', 'feedViewPost'> post: PostView reply?: ReplyRef - reason?: ReasonRepost | ReasonPin | { $type: string; [k: string]: unknown } + reason?: $Typed | $Typed | { $type: string } /** Context provided by feed generator that may be passed back alongside interactions. */ feedContext?: string - [k: string]: unknown } -export function isFeedViewPost( - v: unknown, -): v is FeedViewPost & { $type: $Type<'app.bsky.feed.defs', 'feedViewPost'> } { +export function isFeedViewPost(v: V) { return is$typed(v, id, 'feedViewPost') } @@ -96,24 +98,26 @@ export function validateFeedViewPost(v: unknown) { ) as ValidationResult } +export function isValidFeedViewPost(v: V): v is V & $Typed { + return isFeedViewPost(v) && validateFeedViewPost(v).success +} + export interface ReplyRef { + $type?: $Type<'app.bsky.feed.defs', 'replyRef'> root: - | PostView - | NotFoundPost - | BlockedPost - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } parent: - | PostView - | NotFoundPost - | BlockedPost - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } grandparentAuthor?: AppBskyActorDefs.ProfileViewBasic - [k: string]: unknown } -export function isReplyRef( - v: unknown, -): v is ReplyRef & { $type: $Type<'app.bsky.feed.defs', 'replyRef'> } { +export function isReplyRef(v: V) { return is$typed(v, id, 'replyRef') } @@ -121,15 +125,17 @@ export function validateReplyRef(v: unknown) { return lexicons.validate(`${id}#replyRef`, v) as ValidationResult } +export function isValidReplyRef(v: V): v is V & $Typed { + return isReplyRef(v) && validateReplyRef(v).success +} + export interface ReasonRepost { + $type?: $Type<'app.bsky.feed.defs', 'reasonRepost'> by: AppBskyActorDefs.ProfileViewBasic indexedAt: string - [k: string]: unknown } -export function isReasonRepost( - v: unknown, -): v is ReasonRepost & { $type: $Type<'app.bsky.feed.defs', 'reasonRepost'> } { +export function isReasonRepost(v: V) { return is$typed(v, id, 'reasonRepost') } @@ -140,13 +146,15 @@ export function validateReasonRepost(v: unknown) { ) as ValidationResult } +export function isValidReasonRepost(v: V): v is V & $Typed { + return isReasonRepost(v) && validateReasonRepost(v).success +} + export interface ReasonPin { - [k: string]: unknown + $type?: $Type<'app.bsky.feed.defs', 'reasonPin'> } -export function isReasonPin( - v: unknown, -): v is ReasonPin & { $type: $Type<'app.bsky.feed.defs', 'reasonPin'> } { +export function isReasonPin(v: V) { return is$typed(v, id, 'reasonPin') } @@ -154,25 +162,27 @@ export function validateReasonPin(v: unknown) { return lexicons.validate(`${id}#reasonPin`, v) as ValidationResult } +export function isValidReasonPin(v: V): v is V & $Typed { + return isReasonPin(v) && validateReasonPin(v).success +} + export interface ThreadViewPost { + $type?: $Type<'app.bsky.feed.defs', 'threadViewPost'> post: PostView parent?: - | ThreadViewPost - | NotFoundPost - | BlockedPost - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } replies?: ( - | ThreadViewPost - | NotFoundPost - | BlockedPost - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } )[] - [k: string]: unknown } -export function isThreadViewPost(v: unknown): v is ThreadViewPost & { - $type: $Type<'app.bsky.feed.defs', 'threadViewPost'> -} { +export function isThreadViewPost(v: V) { return is$typed(v, id, 'threadViewPost') } @@ -183,15 +193,19 @@ export function validateThreadViewPost(v: unknown) { ) as ValidationResult } +export function isValidThreadViewPost( + v: V, +): v is V & $Typed { + return isThreadViewPost(v) && validateThreadViewPost(v).success +} + export interface NotFoundPost { + $type?: $Type<'app.bsky.feed.defs', 'notFoundPost'> uri: string notFound: true - [k: string]: unknown } -export function isNotFoundPost( - v: unknown, -): v is NotFoundPost & { $type: $Type<'app.bsky.feed.defs', 'notFoundPost'> } { +export function isNotFoundPost(v: V) { return is$typed(v, id, 'notFoundPost') } @@ -202,16 +216,18 @@ export function validateNotFoundPost(v: unknown) { ) as ValidationResult } +export function isValidNotFoundPost(v: V): v is V & $Typed { + return isNotFoundPost(v) && validateNotFoundPost(v).success +} + export interface BlockedPost { + $type?: $Type<'app.bsky.feed.defs', 'blockedPost'> uri: string blocked: true author: BlockedAuthor - [k: string]: unknown } -export function isBlockedPost( - v: unknown, -): v is BlockedPost & { $type: $Type<'app.bsky.feed.defs', 'blockedPost'> } { +export function isBlockedPost(v: V) { return is$typed(v, id, 'blockedPost') } @@ -222,15 +238,17 @@ export function validateBlockedPost(v: unknown) { ) as ValidationResult } +export function isValidBlockedPost(v: V): v is V & $Typed { + return isBlockedPost(v) && validateBlockedPost(v).success +} + export interface BlockedAuthor { + $type?: $Type<'app.bsky.feed.defs', 'blockedAuthor'> did: string viewer?: AppBskyActorDefs.ViewerState - [k: string]: unknown } -export function isBlockedAuthor(v: unknown): v is BlockedAuthor & { - $type: $Type<'app.bsky.feed.defs', 'blockedAuthor'> -} { +export function isBlockedAuthor(v: V) { return is$typed(v, id, 'blockedAuthor') } @@ -241,7 +259,12 @@ export function validateBlockedAuthor(v: unknown) { ) as ValidationResult } +export function isValidBlockedAuthor(v: V): v is V & $Typed { + return isBlockedAuthor(v) && validateBlockedAuthor(v).success +} + export interface GeneratorView { + $type?: $Type<'app.bsky.feed.defs', 'generatorView'> uri: string cid: string did: string @@ -255,12 +278,9 @@ export interface GeneratorView { labels?: ComAtprotoLabelDefs.Label[] viewer?: GeneratorViewerState indexedAt: string - [k: string]: unknown } -export function isGeneratorView(v: unknown): v is GeneratorView & { - $type: $Type<'app.bsky.feed.defs', 'generatorView'> -} { +export function isGeneratorView(v: V) { return is$typed(v, id, 'generatorView') } @@ -271,16 +291,16 @@ export function validateGeneratorView(v: unknown) { ) as ValidationResult } +export function isValidGeneratorView(v: V): v is V & $Typed { + return isGeneratorView(v) && validateGeneratorView(v).success +} + export interface GeneratorViewerState { + $type?: $Type<'app.bsky.feed.defs', 'generatorViewerState'> like?: string - [k: string]: unknown } -export function isGeneratorViewerState( - v: unknown, -): v is GeneratorViewerState & { - $type: $Type<'app.bsky.feed.defs', 'generatorViewerState'> -} { +export function isGeneratorViewerState(v: V) { return is$typed(v, id, 'generatorViewerState') } @@ -291,20 +311,24 @@ export function validateGeneratorViewerState(v: unknown) { ) as ValidationResult } +export function isValidGeneratorViewerState( + v: V, +): v is V & $Typed { + return isGeneratorViewerState(v) && validateGeneratorViewerState(v).success +} + export interface SkeletonFeedPost { + $type?: $Type<'app.bsky.feed.defs', 'skeletonFeedPost'> post: string reason?: - | SkeletonReasonRepost - | SkeletonReasonPin - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | { $type: string } /** Context that will be passed through to client and may be passed to feed generator back alongside interactions. */ feedContext?: string - [k: string]: unknown } -export function isSkeletonFeedPost(v: unknown): v is SkeletonFeedPost & { - $type: $Type<'app.bsky.feed.defs', 'skeletonFeedPost'> -} { +export function isSkeletonFeedPost(v: V) { return is$typed(v, id, 'skeletonFeedPost') } @@ -315,16 +339,18 @@ export function validateSkeletonFeedPost(v: unknown) { ) as ValidationResult } +export function isValidSkeletonFeedPost( + v: V, +): v is V & $Typed { + return isSkeletonFeedPost(v) && validateSkeletonFeedPost(v).success +} + export interface SkeletonReasonRepost { + $type?: $Type<'app.bsky.feed.defs', 'skeletonReasonRepost'> repost: string - [k: string]: unknown } -export function isSkeletonReasonRepost( - v: unknown, -): v is SkeletonReasonRepost & { - $type: $Type<'app.bsky.feed.defs', 'skeletonReasonRepost'> -} { +export function isSkeletonReasonRepost(v: V) { return is$typed(v, id, 'skeletonReasonRepost') } @@ -335,13 +361,17 @@ export function validateSkeletonReasonRepost(v: unknown) { ) as ValidationResult } +export function isValidSkeletonReasonRepost( + v: V, +): v is V & $Typed { + return isSkeletonReasonRepost(v) && validateSkeletonReasonRepost(v).success +} + export interface SkeletonReasonPin { - [k: string]: unknown + $type?: $Type<'app.bsky.feed.defs', 'skeletonReasonPin'> } -export function isSkeletonReasonPin(v: unknown): v is SkeletonReasonPin & { - $type: $Type<'app.bsky.feed.defs', 'skeletonReasonPin'> -} { +export function isSkeletonReasonPin(v: V) { return is$typed(v, id, 'skeletonReasonPin') } @@ -352,17 +382,21 @@ export function validateSkeletonReasonPin(v: unknown) { ) as ValidationResult } +export function isValidSkeletonReasonPin( + v: V, +): v is V & $Typed { + return isSkeletonReasonPin(v) && validateSkeletonReasonPin(v).success +} + export interface ThreadgateView { + $type?: $Type<'app.bsky.feed.defs', 'threadgateView'> uri?: string cid?: string - record?: {} + record?: { [_ in string]: unknown } lists?: AppBskyGraphDefs.ListViewBasic[] - [k: string]: unknown } -export function isThreadgateView(v: unknown): v is ThreadgateView & { - $type: $Type<'app.bsky.feed.defs', 'threadgateView'> -} { +export function isThreadgateView(v: V) { return is$typed(v, id, 'threadgateView') } @@ -373,7 +407,14 @@ export function validateThreadgateView(v: unknown) { ) as ValidationResult } +export function isValidThreadgateView( + v: V, +): v is V & $Typed { + return isThreadgateView(v) && validateThreadgateView(v).success +} + export interface Interaction { + $type?: $Type<'app.bsky.feed.defs', 'interaction'> item?: string event?: | 'app.bsky.feed.defs#requestLess' @@ -391,12 +432,9 @@ export interface Interaction { | (string & {}) /** Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton. */ feedContext?: string - [k: string]: unknown } -export function isInteraction( - v: unknown, -): v is Interaction & { $type: $Type<'app.bsky.feed.defs', 'interaction'> } { +export function isInteraction(v: V) { return is$typed(v, id, 'interaction') } @@ -407,6 +445,10 @@ export function validateInteraction(v: unknown) { ) as ValidationResult } +export function isValidInteraction(v: V): v is V & $Typed { + return isInteraction(v) && validateInteraction(v).success +} + /** Request that less content like the given feed item be shown in the feed */ export const REQUESTLESS = `${id}#requestLess` /** Request that more content like the given feed item be shown in the feed */ diff --git a/packages/api/src/client/types/app/bsky/feed/describeFeedGenerator.ts b/packages/api/src/client/types/app/bsky/feed/describeFeedGenerator.ts index 81e845df856..5fd78149a56 100644 --- a/packages/api/src/client/types/app/bsky/feed/describeFeedGenerator.ts +++ b/packages/api/src/client/types/app/bsky/feed/describeFeedGenerator.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.feed.describeFeedGenerator' @@ -17,7 +17,6 @@ export interface OutputSchema { did: string feeds: Feed[] links?: Links - [k: string]: unknown } export interface CallOptions { @@ -36,13 +35,11 @@ export function toKnownErr(e: any) { } export interface Feed { + $type?: $Type<'app.bsky.feed.describeFeedGenerator', 'feed'> uri: string - [k: string]: unknown } -export function isFeed( - v: unknown, -): v is Feed & { $type: $Type<'app.bsky.feed.describeFeedGenerator', 'feed'> } { +export function isFeed(v: V) { return is$typed(v, id, 'feed') } @@ -50,18 +47,24 @@ export function validateFeed(v: unknown) { return lexicons.validate(`${id}#feed`, v) as ValidationResult } +export function isValidFeed(v: V): v is V & $Typed { + return isFeed(v) && validateFeed(v).success +} + export interface Links { + $type?: $Type<'app.bsky.feed.describeFeedGenerator', 'links'> privacyPolicy?: string termsOfService?: string - [k: string]: unknown } -export function isLinks(v: unknown): v is Links & { - $type: $Type<'app.bsky.feed.describeFeedGenerator', 'links'> -} { +export function isLinks(v: V) { return is$typed(v, id, 'links') } export function validateLinks(v: unknown) { return lexicons.validate(`${id}#links`, v) as ValidationResult } + +export function isValidLinks(v: V): v is V & $Typed { + return isLinks(v) && validateLinks(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/generator.ts b/packages/api/src/client/types/app/bsky/feed/generator.ts index b92a1a44ff9..0d50c3b33bb 100644 --- a/packages/api/src/client/types/app/bsky/feed/generator.ts +++ b/packages/api/src/client/types/app/bsky/feed/generator.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyRichtextFacet from '../richtext/facet' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' @@ -11,6 +11,7 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' const id = 'app.bsky.feed.generator' export interface Record { + $type?: $Type<'app.bsky.feed.generator', 'main'> did: string displayName: string description?: string @@ -18,19 +19,19 @@ export interface Record { avatar?: BlobRef /** Declaration that a feed accepts feedback interactions from a client through app.bsky.feed.sendInteractions */ acceptsInteractions?: boolean - labels?: - | ComAtprotoLabelDefs.SelfLabels - | { $type: string; [k: string]: unknown } + labels?: $Typed | { $type: string } createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.feed.generator', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/getActorFeeds.ts b/packages/api/src/client/types/app/bsky/feed/getActorFeeds.ts index 98032703d46..b90342a0e58 100644 --- a/packages/api/src/client/types/app/bsky/feed/getActorFeeds.ts +++ b/packages/api/src/client/types/app/bsky/feed/getActorFeeds.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feeds: AppBskyFeedDefs.GeneratorView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getActorLikes.ts b/packages/api/src/client/types/app/bsky/feed/getActorLikes.ts index 99545f33fe1..936d1f267c9 100644 --- a/packages/api/src/client/types/app/bsky/feed/getActorLikes.ts +++ b/packages/api/src/client/types/app/bsky/feed/getActorLikes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feed: AppBskyFeedDefs.FeedViewPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getAuthorFeed.ts b/packages/api/src/client/types/app/bsky/feed/getAuthorFeed.ts index 25bd09b0c5a..4cc9c384987 100644 --- a/packages/api/src/client/types/app/bsky/feed/getAuthorFeed.ts +++ b/packages/api/src/client/types/app/bsky/feed/getAuthorFeed.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -29,7 +29,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feed: AppBskyFeedDefs.FeedViewPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getFeed.ts b/packages/api/src/client/types/app/bsky/feed/getFeed.ts index 77cf2bf2d8e..1d4f117c446 100644 --- a/packages/api/src/client/types/app/bsky/feed/getFeed.ts +++ b/packages/api/src/client/types/app/bsky/feed/getFeed.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feed: AppBskyFeedDefs.FeedViewPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getFeedGenerator.ts b/packages/api/src/client/types/app/bsky/feed/getFeedGenerator.ts index d381a0e90d2..2ca5fa4b957 100644 --- a/packages/api/src/client/types/app/bsky/feed/getFeedGenerator.ts +++ b/packages/api/src/client/types/app/bsky/feed/getFeedGenerator.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -23,7 +23,6 @@ export interface OutputSchema { isOnline: boolean /** Indicates whether the feed generator service is compatible with the record declaration. */ isValid: boolean - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getFeedGenerators.ts b/packages/api/src/client/types/app/bsky/feed/getFeedGenerators.ts index 76f852faf78..5342ef411d2 100644 --- a/packages/api/src/client/types/app/bsky/feed/getFeedGenerators.ts +++ b/packages/api/src/client/types/app/bsky/feed/getFeedGenerators.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { feeds: AppBskyFeedDefs.GeneratorView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getFeedSkeleton.ts b/packages/api/src/client/types/app/bsky/feed/getFeedSkeleton.ts index 76638027d38..f34b4f56ed2 100644 --- a/packages/api/src/client/types/app/bsky/feed/getFeedSkeleton.ts +++ b/packages/api/src/client/types/app/bsky/feed/getFeedSkeleton.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -22,7 +22,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feed: AppBskyFeedDefs.SkeletonFeedPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getLikes.ts b/packages/api/src/client/types/app/bsky/feed/getLikes.ts index 2e3f58a2a4c..ee98eadcb08 100644 --- a/packages/api/src/client/types/app/bsky/feed/getLikes.ts +++ b/packages/api/src/client/types/app/bsky/feed/getLikes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -26,7 +26,6 @@ export interface OutputSchema { cid?: string cursor?: string likes: Like[] - [k: string]: unknown } export interface CallOptions { @@ -45,18 +44,20 @@ export function toKnownErr(e: any) { } export interface Like { + $type?: $Type<'app.bsky.feed.getLikes', 'like'> indexedAt: string createdAt: string actor: AppBskyActorDefs.ProfileView - [k: string]: unknown } -export function isLike( - v: unknown, -): v is Like & { $type: $Type<'app.bsky.feed.getLikes', 'like'> } { +export function isLike(v: V) { return is$typed(v, id, 'like') } export function validateLike(v: unknown) { return lexicons.validate(`${id}#like`, v) as ValidationResult } + +export function isValidLike(v: V): v is V & $Typed { + return isLike(v) && validateLike(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/getListFeed.ts b/packages/api/src/client/types/app/bsky/feed/getListFeed.ts index 6e6dfd6a9ff..506cd5c1da4 100644 --- a/packages/api/src/client/types/app/bsky/feed/getListFeed.ts +++ b/packages/api/src/client/types/app/bsky/feed/getListFeed.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -22,7 +22,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feed: AppBskyFeedDefs.FeedViewPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getPostThread.ts b/packages/api/src/client/types/app/bsky/feed/getPostThread.ts index 53f193dd8da..fb03ea0b8d1 100644 --- a/packages/api/src/client/types/app/bsky/feed/getPostThread.ts +++ b/packages/api/src/client/types/app/bsky/feed/getPostThread.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -23,12 +23,11 @@ export type InputSchema = undefined export interface OutputSchema { thread: - | AppBskyFeedDefs.ThreadViewPost - | AppBskyFeedDefs.NotFoundPost - | AppBskyFeedDefs.BlockedPost - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } threadgate?: AppBskyFeedDefs.ThreadgateView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getPosts.ts b/packages/api/src/client/types/app/bsky/feed/getPosts.ts index de9aa66b6cc..3b73a477338 100644 --- a/packages/api/src/client/types/app/bsky/feed/getPosts.ts +++ b/packages/api/src/client/types/app/bsky/feed/getPosts.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -19,7 +19,6 @@ export type InputSchema = undefined export interface OutputSchema { posts: AppBskyFeedDefs.PostView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getQuotes.ts b/packages/api/src/client/types/app/bsky/feed/getQuotes.ts index f0cff90d5e1..13f1b95ed4a 100644 --- a/packages/api/src/client/types/app/bsky/feed/getQuotes.ts +++ b/packages/api/src/client/types/app/bsky/feed/getQuotes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -26,7 +26,6 @@ export interface OutputSchema { cid?: string cursor?: string posts: AppBskyFeedDefs.PostView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getRepostedBy.ts b/packages/api/src/client/types/app/bsky/feed/getRepostedBy.ts index 1db6626076f..f3c8429fe94 100644 --- a/packages/api/src/client/types/app/bsky/feed/getRepostedBy.ts +++ b/packages/api/src/client/types/app/bsky/feed/getRepostedBy.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -26,7 +26,6 @@ export interface OutputSchema { cid?: string cursor?: string repostedBy: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getSuggestedFeeds.ts b/packages/api/src/client/types/app/bsky/feed/getSuggestedFeeds.ts index a62bed53103..30042d0535d 100644 --- a/packages/api/src/client/types/app/bsky/feed/getSuggestedFeeds.ts +++ b/packages/api/src/client/types/app/bsky/feed/getSuggestedFeeds.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feeds: AppBskyFeedDefs.GeneratorView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/getTimeline.ts b/packages/api/src/client/types/app/bsky/feed/getTimeline.ts index b5a6032028c..82c6effbdf0 100644 --- a/packages/api/src/client/types/app/bsky/feed/getTimeline.ts +++ b/packages/api/src/client/types/app/bsky/feed/getTimeline.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -22,7 +22,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feed: AppBskyFeedDefs.FeedViewPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/like.ts b/packages/api/src/client/types/app/bsky/feed/like.ts index 1d0402a3288..03fb15a7bca 100644 --- a/packages/api/src/client/types/app/bsky/feed/like.ts +++ b/packages/api/src/client/types/app/bsky/feed/like.ts @@ -3,24 +3,27 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' const id = 'app.bsky.feed.like' export interface Record { + $type?: $Type<'app.bsky.feed.like', 'main'> subject: ComAtprotoRepoStrongRef.Main createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.feed.like', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/post.ts b/packages/api/src/client/types/app/bsky/feed/post.ts index 4621ad0dc10..28e79fa6df7 100644 --- a/packages/api/src/client/types/app/bsky/feed/post.ts +++ b/packages/api/src/client/types/app/bsky/feed/post.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyRichtextFacet from '../richtext/facet' import * as AppBskyEmbedImages from '../embed/images' @@ -17,6 +17,7 @@ import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' const id = 'app.bsky.feed.post' export interface Record { + $type?: $Type<'app.bsky.feed.post', 'main'> /** The primary post content. May be an empty string, if there are embeds. */ text: string /** DEPRECATED: replaced by app.bsky.richtext.facet. */ @@ -25,17 +26,15 @@ export interface Record { facets?: AppBskyRichtextFacet.Main[] reply?: ReplyRef embed?: - | AppBskyEmbedImages.Main - | AppBskyEmbedVideo.Main - | AppBskyEmbedExternal.Main - | AppBskyEmbedRecord.Main - | AppBskyEmbedRecordWithMedia.Main - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | $Typed + | $Typed + | { $type: string } /** Indicates human language of post primary text content. */ langs?: string[] - labels?: - | ComAtprotoLabelDefs.SelfLabels - | { $type: string; [k: string]: unknown } + labels?: $Typed | { $type: string } /** Additional hashtags, in addition to any included in post text and facets. */ tags?: string[] /** Client-declared timestamp when this post was originally created. */ @@ -43,9 +42,7 @@ export interface Record { [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.feed.post', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } @@ -53,15 +50,17 @@ export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} + export interface ReplyRef { + $type?: $Type<'app.bsky.feed.post', 'replyRef'> root: ComAtprotoRepoStrongRef.Main parent: ComAtprotoRepoStrongRef.Main - [k: string]: unknown } -export function isReplyRef( - v: unknown, -): v is ReplyRef & { $type: $Type<'app.bsky.feed.post', 'replyRef'> } { +export function isReplyRef(v: V) { return is$typed(v, id, 'replyRef') } @@ -69,18 +68,20 @@ export function validateReplyRef(v: unknown) { return lexicons.validate(`${id}#replyRef`, v) as ValidationResult } +export function isValidReplyRef(v: V): v is V & $Typed { + return isReplyRef(v) && validateReplyRef(v).success +} + /** Deprecated: use facets instead. */ export interface Entity { + $type?: $Type<'app.bsky.feed.post', 'entity'> index: TextSlice /** Expected values are 'mention' and 'link'. */ type: string value: string - [k: string]: unknown } -export function isEntity( - v: unknown, -): v is Entity & { $type: $Type<'app.bsky.feed.post', 'entity'> } { +export function isEntity(v: V) { return is$typed(v, id, 'entity') } @@ -88,19 +89,25 @@ export function validateEntity(v: unknown) { return lexicons.validate(`${id}#entity`, v) as ValidationResult } +export function isValidEntity(v: V): v is V & $Typed { + return isEntity(v) && validateEntity(v).success +} + /** Deprecated. Use app.bsky.richtext instead -- A text segment. Start is inclusive, end is exclusive. Indices are for utf16-encoded strings. */ export interface TextSlice { + $type?: $Type<'app.bsky.feed.post', 'textSlice'> start: number end: number - [k: string]: unknown } -export function isTextSlice( - v: unknown, -): v is TextSlice & { $type: $Type<'app.bsky.feed.post', 'textSlice'> } { +export function isTextSlice(v: V) { return is$typed(v, id, 'textSlice') } export function validateTextSlice(v: unknown) { return lexicons.validate(`${id}#textSlice`, v) as ValidationResult } + +export function isValidTextSlice(v: V): v is V & $Typed { + return isTextSlice(v) && validateTextSlice(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/postgate.ts b/packages/api/src/client/types/app/bsky/feed/postgate.ts index ff4de36f211..f6481508969 100644 --- a/packages/api/src/client/types/app/bsky/feed/postgate.ts +++ b/packages/api/src/client/types/app/bsky/feed/postgate.ts @@ -3,24 +3,23 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.feed.postgate' export interface Record { + $type?: $Type<'app.bsky.feed.postgate', 'main'> createdAt: string /** Reference (AT-URI) to the post record. */ post: string /** List of AT-URIs embedding this post that the author has detached from. */ detachedEmbeddingUris?: string[] - embeddingRules?: (DisableRule | { $type: string; [k: string]: unknown })[] + embeddingRules?: ($Typed | { $type: string })[] [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.feed.postgate', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } @@ -28,14 +27,16 @@ export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} + /** Disables embedding of this post. */ export interface DisableRule { - [k: string]: unknown + $type?: $Type<'app.bsky.feed.postgate', 'disableRule'> } -export function isDisableRule(v: unknown): v is DisableRule & { - $type: $Type<'app.bsky.feed.postgate', 'disableRule'> -} { +export function isDisableRule(v: V) { return is$typed(v, id, 'disableRule') } @@ -45,3 +46,7 @@ export function validateDisableRule(v: unknown) { v, ) as ValidationResult } + +export function isValidDisableRule(v: V): v is V & $Typed { + return isDisableRule(v) && validateDisableRule(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/repost.ts b/packages/api/src/client/types/app/bsky/feed/repost.ts index e602112d344..c2a54ec2144 100644 --- a/packages/api/src/client/types/app/bsky/feed/repost.ts +++ b/packages/api/src/client/types/app/bsky/feed/repost.ts @@ -3,24 +3,27 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' const id = 'app.bsky.feed.repost' export interface Record { + $type?: $Type<'app.bsky.feed.repost', 'main'> subject: ComAtprotoRepoStrongRef.Main createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.feed.repost', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/feed/searchPosts.ts b/packages/api/src/client/types/app/bsky/feed/searchPosts.ts index d7d170af694..5da2df0f776 100644 --- a/packages/api/src/client/types/app/bsky/feed/searchPosts.ts +++ b/packages/api/src/client/types/app/bsky/feed/searchPosts.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -43,7 +43,6 @@ export interface OutputSchema { /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ hitsTotal?: number posts: AppBskyFeedDefs.PostView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/feed/sendInteractions.ts b/packages/api/src/client/types/app/bsky/feed/sendInteractions.ts index 63701c701b4..d405c640ccc 100644 --- a/packages/api/src/client/types/app/bsky/feed/sendInteractions.ts +++ b/packages/api/src/client/types/app/bsky/feed/sendInteractions.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from './defs' @@ -14,12 +14,9 @@ export interface QueryParams {} export interface InputSchema { interactions: AppBskyFeedDefs.Interaction[] - [k: string]: unknown } -export interface OutputSchema { - [k: string]: unknown -} +export interface OutputSchema {} export interface CallOptions { signal?: AbortSignal diff --git a/packages/api/src/client/types/app/bsky/feed/threadgate.ts b/packages/api/src/client/types/app/bsky/feed/threadgate.ts index 85a1f66080d..ba9e873e5c2 100644 --- a/packages/api/src/client/types/app/bsky/feed/threadgate.ts +++ b/packages/api/src/client/types/app/bsky/feed/threadgate.ts @@ -3,19 +3,20 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.feed.threadgate' export interface Record { + $type?: $Type<'app.bsky.feed.threadgate', 'main'> /** Reference (AT-URI) to the post record. */ post: string allow?: ( - | MentionRule - | FollowingRule - | ListRule - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } )[] createdAt: string /** List of hidden reply URIs. */ @@ -23,9 +24,7 @@ export interface Record { [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.feed.threadgate', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } @@ -33,14 +32,16 @@ export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} + /** Allow replies from actors mentioned in your post. */ export interface MentionRule { - [k: string]: unknown + $type?: $Type<'app.bsky.feed.threadgate', 'mentionRule'> } -export function isMentionRule(v: unknown): v is MentionRule & { - $type: $Type<'app.bsky.feed.threadgate', 'mentionRule'> -} { +export function isMentionRule(v: V) { return is$typed(v, id, 'mentionRule') } @@ -51,14 +52,16 @@ export function validateMentionRule(v: unknown) { ) as ValidationResult } +export function isValidMentionRule(v: V): v is V & $Typed { + return isMentionRule(v) && validateMentionRule(v).success +} + /** Allow replies from actors you follow. */ export interface FollowingRule { - [k: string]: unknown + $type?: $Type<'app.bsky.feed.threadgate', 'followingRule'> } -export function isFollowingRule(v: unknown): v is FollowingRule & { - $type: $Type<'app.bsky.feed.threadgate', 'followingRule'> -} { +export function isFollowingRule(v: V) { return is$typed(v, id, 'followingRule') } @@ -69,18 +72,24 @@ export function validateFollowingRule(v: unknown) { ) as ValidationResult } +export function isValidFollowingRule(v: V): v is V & $Typed { + return isFollowingRule(v) && validateFollowingRule(v).success +} + /** Allow replies from actors on a list. */ export interface ListRule { + $type?: $Type<'app.bsky.feed.threadgate', 'listRule'> list: string - [k: string]: unknown } -export function isListRule( - v: unknown, -): v is ListRule & { $type: $Type<'app.bsky.feed.threadgate', 'listRule'> } { +export function isListRule(v: V) { return is$typed(v, id, 'listRule') } export function validateListRule(v: unknown) { return lexicons.validate(`${id}#listRule`, v) as ValidationResult } + +export function isValidListRule(v: V): v is V & $Typed { + return isListRule(v) && validateListRule(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/block.ts b/packages/api/src/client/types/app/bsky/graph/block.ts index 4bc5a5f3038..654b1d08ab1 100644 --- a/packages/api/src/client/types/app/bsky/graph/block.ts +++ b/packages/api/src/client/types/app/bsky/graph/block.ts @@ -3,24 +3,27 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.block' export interface Record { + $type?: $Type<'app.bsky.graph.block', 'main'> /** DID of the account to be blocked. */ subject: string createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.graph.block', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/defs.ts b/packages/api/src/client/types/app/bsky/graph/defs.ts index 8edbf9e888e..ff42ade966a 100644 --- a/packages/api/src/client/types/app/bsky/graph/defs.ts +++ b/packages/api/src/client/types/app/bsky/graph/defs.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' import * as AppBskyActorDefs from '../actor/defs' @@ -13,6 +13,7 @@ import * as AppBskyFeedDefs from '../feed/defs' const id = 'app.bsky.graph.defs' export interface ListViewBasic { + $type?: $Type<'app.bsky.graph.defs', 'listViewBasic'> uri: string cid: string name: string @@ -22,12 +23,9 @@ export interface ListViewBasic { labels?: ComAtprotoLabelDefs.Label[] viewer?: ListViewerState indexedAt?: string - [k: string]: unknown } -export function isListViewBasic(v: unknown): v is ListViewBasic & { - $type: $Type<'app.bsky.graph.defs', 'listViewBasic'> -} { +export function isListViewBasic(v: V) { return is$typed(v, id, 'listViewBasic') } @@ -38,7 +36,12 @@ export function validateListViewBasic(v: unknown) { ) as ValidationResult } +export function isValidListViewBasic(v: V): v is V & $Typed { + return isListViewBasic(v) && validateListViewBasic(v).success +} + export interface ListView { + $type?: $Type<'app.bsky.graph.defs', 'listView'> uri: string cid: string creator: AppBskyActorDefs.ProfileView @@ -51,12 +54,9 @@ export interface ListView { labels?: ComAtprotoLabelDefs.Label[] viewer?: ListViewerState indexedAt: string - [k: string]: unknown } -export function isListView( - v: unknown, -): v is ListView & { $type: $Type<'app.bsky.graph.defs', 'listView'> } { +export function isListView(v: V) { return is$typed(v, id, 'listView') } @@ -64,15 +64,17 @@ export function validateListView(v: unknown) { return lexicons.validate(`${id}#listView`, v) as ValidationResult } +export function isValidListView(v: V): v is V & $Typed { + return isListView(v) && validateListView(v).success +} + export interface ListItemView { + $type?: $Type<'app.bsky.graph.defs', 'listItemView'> uri: string subject: AppBskyActorDefs.ProfileView - [k: string]: unknown } -export function isListItemView( - v: unknown, -): v is ListItemView & { $type: $Type<'app.bsky.graph.defs', 'listItemView'> } { +export function isListItemView(v: V) { return is$typed(v, id, 'listItemView') } @@ -83,10 +85,15 @@ export function validateListItemView(v: unknown) { ) as ValidationResult } +export function isValidListItemView(v: V): v is V & $Typed { + return isListItemView(v) && validateListItemView(v).success +} + export interface StarterPackView { + $type?: $Type<'app.bsky.graph.defs', 'starterPackView'> uri: string cid: string - record: {} + record: { [_ in string]: unknown } creator: AppBskyActorDefs.ProfileViewBasic list?: ListViewBasic listItemsSample?: ListItemView[] @@ -95,12 +102,9 @@ export interface StarterPackView { joinedAllTimeCount?: number labels?: ComAtprotoLabelDefs.Label[] indexedAt: string - [k: string]: unknown } -export function isStarterPackView(v: unknown): v is StarterPackView & { - $type: $Type<'app.bsky.graph.defs', 'starterPackView'> -} { +export function isStarterPackView(v: V) { return is$typed(v, id, 'starterPackView') } @@ -111,24 +115,26 @@ export function validateStarterPackView(v: unknown) { ) as ValidationResult } +export function isValidStarterPackView( + v: V, +): v is V & $Typed { + return isStarterPackView(v) && validateStarterPackView(v).success +} + export interface StarterPackViewBasic { + $type?: $Type<'app.bsky.graph.defs', 'starterPackViewBasic'> uri: string cid: string - record: {} + record: { [_ in string]: unknown } creator: AppBskyActorDefs.ProfileViewBasic listItemCount?: number joinedWeekCount?: number joinedAllTimeCount?: number labels?: ComAtprotoLabelDefs.Label[] indexedAt: string - [k: string]: unknown } -export function isStarterPackViewBasic( - v: unknown, -): v is StarterPackViewBasic & { - $type: $Type<'app.bsky.graph.defs', 'starterPackViewBasic'> -} { +export function isStarterPackViewBasic(v: V) { return is$typed(v, id, 'starterPackViewBasic') } @@ -139,6 +145,12 @@ export function validateStarterPackViewBasic(v: unknown) { ) as ValidationResult } +export function isValidStarterPackViewBasic( + v: V, +): v is V & $Typed { + return isStarterPackViewBasic(v) && validateStarterPackViewBasic(v).success +} + export type ListPurpose = | 'app.bsky.graph.defs#modlist' | 'app.bsky.graph.defs#curatelist' @@ -153,14 +165,12 @@ export const CURATELIST = `${id}#curatelist` export const REFERENCELIST = `${id}#referencelist` export interface ListViewerState { + $type?: $Type<'app.bsky.graph.defs', 'listViewerState'> muted?: boolean blocked?: string - [k: string]: unknown } -export function isListViewerState(v: unknown): v is ListViewerState & { - $type: $Type<'app.bsky.graph.defs', 'listViewerState'> -} { +export function isListViewerState(v: V) { return is$typed(v, id, 'listViewerState') } @@ -171,16 +181,20 @@ export function validateListViewerState(v: unknown) { ) as ValidationResult } +export function isValidListViewerState( + v: V, +): v is V & $Typed { + return isListViewerState(v) && validateListViewerState(v).success +} + /** indicates that a handle or DID could not be resolved */ export interface NotFoundActor { + $type?: $Type<'app.bsky.graph.defs', 'notFoundActor'> actor: string notFound: true - [k: string]: unknown } -export function isNotFoundActor(v: unknown): v is NotFoundActor & { - $type: $Type<'app.bsky.graph.defs', 'notFoundActor'> -} { +export function isNotFoundActor(v: V) { return is$typed(v, id, 'notFoundActor') } @@ -191,19 +205,21 @@ export function validateNotFoundActor(v: unknown) { ) as ValidationResult } +export function isValidNotFoundActor(v: V): v is V & $Typed { + return isNotFoundActor(v) && validateNotFoundActor(v).success +} + /** lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object) */ export interface Relationship { + $type?: $Type<'app.bsky.graph.defs', 'relationship'> did: string /** if the actor follows this DID, this is the AT-URI of the follow record */ following?: string /** if the actor is followed by this DID, contains the AT-URI of the follow record */ followedBy?: string - [k: string]: unknown } -export function isRelationship( - v: unknown, -): v is Relationship & { $type: $Type<'app.bsky.graph.defs', 'relationship'> } { +export function isRelationship(v: V) { return is$typed(v, id, 'relationship') } @@ -213,3 +229,7 @@ export function validateRelationship(v: unknown) { v, ) as ValidationResult } + +export function isValidRelationship(v: V): v is V & $Typed { + return isRelationship(v) && validateRelationship(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/follow.ts b/packages/api/src/client/types/app/bsky/graph/follow.ts index cd7728fdd18..656a4587c4f 100644 --- a/packages/api/src/client/types/app/bsky/graph/follow.ts +++ b/packages/api/src/client/types/app/bsky/graph/follow.ts @@ -3,23 +3,26 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.follow' export interface Record { + $type?: $Type<'app.bsky.graph.follow', 'main'> subject: string createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.graph.follow', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/getActorStarterPacks.ts b/packages/api/src/client/types/app/bsky/graph/getActorStarterPacks.ts index 0d56a3536d0..2a3fb473b74 100644 --- a/packages/api/src/client/types/app/bsky/graph/getActorStarterPacks.ts +++ b/packages/api/src/client/types/app/bsky/graph/getActorStarterPacks.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string starterPacks: AppBskyGraphDefs.StarterPackViewBasic[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getBlocks.ts b/packages/api/src/client/types/app/bsky/graph/getBlocks.ts index 7858841eb61..19a4c6cbcd3 100644 --- a/packages/api/src/client/types/app/bsky/graph/getBlocks.ts +++ b/packages/api/src/client/types/app/bsky/graph/getBlocks.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string blocks: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getFollowers.ts b/packages/api/src/client/types/app/bsky/graph/getFollowers.ts index 3d4121bc0a7..006cffd77c3 100644 --- a/packages/api/src/client/types/app/bsky/graph/getFollowers.ts +++ b/packages/api/src/client/types/app/bsky/graph/getFollowers.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -22,7 +22,6 @@ export interface OutputSchema { subject: AppBskyActorDefs.ProfileView cursor?: string followers: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getFollows.ts b/packages/api/src/client/types/app/bsky/graph/getFollows.ts index 75b05d1c513..6f6d858af28 100644 --- a/packages/api/src/client/types/app/bsky/graph/getFollows.ts +++ b/packages/api/src/client/types/app/bsky/graph/getFollows.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -22,7 +22,6 @@ export interface OutputSchema { subject: AppBskyActorDefs.ProfileView cursor?: string follows: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getKnownFollowers.ts b/packages/api/src/client/types/app/bsky/graph/getKnownFollowers.ts index 24dfd79f3ab..a0ff1321119 100644 --- a/packages/api/src/client/types/app/bsky/graph/getKnownFollowers.ts +++ b/packages/api/src/client/types/app/bsky/graph/getKnownFollowers.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -22,7 +22,6 @@ export interface OutputSchema { subject: AppBskyActorDefs.ProfileView cursor?: string followers: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getList.ts b/packages/api/src/client/types/app/bsky/graph/getList.ts index 89bc0eeacb9..10798348b1d 100644 --- a/packages/api/src/client/types/app/bsky/graph/getList.ts +++ b/packages/api/src/client/types/app/bsky/graph/getList.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -23,7 +23,6 @@ export interface OutputSchema { cursor?: string list: AppBskyGraphDefs.ListView items: AppBskyGraphDefs.ListItemView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getListBlocks.ts b/packages/api/src/client/types/app/bsky/graph/getListBlocks.ts index 0be93d089f6..3c4f78983e3 100644 --- a/packages/api/src/client/types/app/bsky/graph/getListBlocks.ts +++ b/packages/api/src/client/types/app/bsky/graph/getListBlocks.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string lists: AppBskyGraphDefs.ListView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getListMutes.ts b/packages/api/src/client/types/app/bsky/graph/getListMutes.ts index e377ac14d7a..6e5c1ffa1fa 100644 --- a/packages/api/src/client/types/app/bsky/graph/getListMutes.ts +++ b/packages/api/src/client/types/app/bsky/graph/getListMutes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string lists: AppBskyGraphDefs.ListView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getLists.ts b/packages/api/src/client/types/app/bsky/graph/getLists.ts index 6cae28caa98..95e01e60b49 100644 --- a/packages/api/src/client/types/app/bsky/graph/getLists.ts +++ b/packages/api/src/client/types/app/bsky/graph/getLists.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -22,7 +22,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string lists: AppBskyGraphDefs.ListView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getMutes.ts b/packages/api/src/client/types/app/bsky/graph/getMutes.ts index a3708bdc5e9..235fdcdabd1 100644 --- a/packages/api/src/client/types/app/bsky/graph/getMutes.ts +++ b/packages/api/src/client/types/app/bsky/graph/getMutes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string mutes: AppBskyActorDefs.ProfileView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getRelationships.ts b/packages/api/src/client/types/app/bsky/graph/getRelationships.ts index fba7825bdfb..61a2ff4d2c7 100644 --- a/packages/api/src/client/types/app/bsky/graph/getRelationships.ts +++ b/packages/api/src/client/types/app/bsky/graph/getRelationships.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -22,11 +22,10 @@ export type InputSchema = undefined export interface OutputSchema { actor?: string relationships: ( - | AppBskyGraphDefs.Relationship - | AppBskyGraphDefs.NotFoundActor - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | { $type: string } )[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getStarterPack.ts b/packages/api/src/client/types/app/bsky/graph/getStarterPack.ts index c29073c7e8a..eae12032ac7 100644 --- a/packages/api/src/client/types/app/bsky/graph/getStarterPack.ts +++ b/packages/api/src/client/types/app/bsky/graph/getStarterPack.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -19,7 +19,6 @@ export type InputSchema = undefined export interface OutputSchema { starterPack: AppBskyGraphDefs.StarterPackView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getStarterPacks.ts b/packages/api/src/client/types/app/bsky/graph/getStarterPacks.ts index d1c2df237b4..68a85d4aecd 100644 --- a/packages/api/src/client/types/app/bsky/graph/getStarterPacks.ts +++ b/packages/api/src/client/types/app/bsky/graph/getStarterPacks.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { starterPacks: AppBskyGraphDefs.StarterPackViewBasic[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/getSuggestedFollowsByActor.ts b/packages/api/src/client/types/app/bsky/graph/getSuggestedFollowsByActor.ts index b3df5a5e9c8..d1fd78296e7 100644 --- a/packages/api/src/client/types/app/bsky/graph/getSuggestedFollowsByActor.ts +++ b/packages/api/src/client/types/app/bsky/graph/getSuggestedFollowsByActor.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' @@ -20,7 +20,6 @@ export interface OutputSchema { suggestions: AppBskyActorDefs.ProfileView[] /** If true, response has fallen-back to generic results, and is not scoped using relativeToDid */ isFallback: boolean - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/list.ts b/packages/api/src/client/types/app/bsky/graph/list.ts index f1e242d1661..8999e968ef2 100644 --- a/packages/api/src/client/types/app/bsky/graph/list.ts +++ b/packages/api/src/client/types/app/bsky/graph/list.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' import * as AppBskyRichtextFacet from '../richtext/facet' @@ -12,25 +12,26 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' const id = 'app.bsky.graph.list' export interface Record { + $type?: $Type<'app.bsky.graph.list', 'main'> purpose: AppBskyGraphDefs.ListPurpose /** Display name for list; can not be empty. */ name: string description?: string descriptionFacets?: AppBskyRichtextFacet.Main[] avatar?: BlobRef - labels?: - | ComAtprotoLabelDefs.SelfLabels - | { $type: string; [k: string]: unknown } + labels?: $Typed | { $type: string } createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.graph.list', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/listblock.ts b/packages/api/src/client/types/app/bsky/graph/listblock.ts index ede600c112c..0a79c189dc1 100644 --- a/packages/api/src/client/types/app/bsky/graph/listblock.ts +++ b/packages/api/src/client/types/app/bsky/graph/listblock.ts @@ -3,24 +3,27 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.listblock' export interface Record { + $type?: $Type<'app.bsky.graph.listblock', 'main'> /** Reference (AT-URI) to the mod list record. */ subject: string createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.graph.listblock', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/listitem.ts b/packages/api/src/client/types/app/bsky/graph/listitem.ts index 0a1a4590db6..37630ff0c4e 100644 --- a/packages/api/src/client/types/app/bsky/graph/listitem.ts +++ b/packages/api/src/client/types/app/bsky/graph/listitem.ts @@ -3,12 +3,13 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.listitem' export interface Record { + $type?: $Type<'app.bsky.graph.listitem', 'main'> /** The account which is included on the list. */ subject: string /** Reference (AT-URI) to the list record (app.bsky.graph.list). */ @@ -17,12 +18,14 @@ export interface Record { [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.graph.listitem', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/muteActor.ts b/packages/api/src/client/types/app/bsky/graph/muteActor.ts index 18fbd027e53..b07b38827b9 100644 --- a/packages/api/src/client/types/app/bsky/graph/muteActor.ts +++ b/packages/api/src/client/types/app/bsky/graph/muteActor.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.muteActor' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { actor: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/muteActorList.ts b/packages/api/src/client/types/app/bsky/graph/muteActorList.ts index 6da99862d1c..aceafd525b5 100644 --- a/packages/api/src/client/types/app/bsky/graph/muteActorList.ts +++ b/packages/api/src/client/types/app/bsky/graph/muteActorList.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.muteActorList' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { list: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/muteThread.ts b/packages/api/src/client/types/app/bsky/graph/muteThread.ts index 1dde979bbbc..78aed863c6a 100644 --- a/packages/api/src/client/types/app/bsky/graph/muteThread.ts +++ b/packages/api/src/client/types/app/bsky/graph/muteThread.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.muteThread' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { root: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/searchStarterPacks.ts b/packages/api/src/client/types/app/bsky/graph/searchStarterPacks.ts index 10c89a85b0b..a5369abc6bc 100644 --- a/packages/api/src/client/types/app/bsky/graph/searchStarterPacks.ts +++ b/packages/api/src/client/types/app/bsky/graph/searchStarterPacks.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyGraphDefs from './defs' @@ -22,7 +22,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string starterPacks: AppBskyGraphDefs.StarterPackViewBasic[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/starterpack.ts b/packages/api/src/client/types/app/bsky/graph/starterpack.ts index 7466af55782..022a3cd21a7 100644 --- a/packages/api/src/client/types/app/bsky/graph/starterpack.ts +++ b/packages/api/src/client/types/app/bsky/graph/starterpack.ts @@ -3,13 +3,14 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyRichtextFacet from '../richtext/facet' const id = 'app.bsky.graph.starterpack' export interface Record { + $type?: $Type<'app.bsky.graph.starterpack', 'main'> /** Display name for starter pack; can not be empty. */ name: string description?: string @@ -21,9 +22,7 @@ export interface Record { [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.graph.starterpack', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } @@ -31,17 +30,23 @@ export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} + export interface FeedItem { + $type?: $Type<'app.bsky.graph.starterpack', 'feedItem'> uri: string - [k: string]: unknown } -export function isFeedItem( - v: unknown, -): v is FeedItem & { $type: $Type<'app.bsky.graph.starterpack', 'feedItem'> } { +export function isFeedItem(v: V) { return is$typed(v, id, 'feedItem') } export function validateFeedItem(v: unknown) { return lexicons.validate(`${id}#feedItem`, v) as ValidationResult } + +export function isValidFeedItem(v: V): v is V & $Typed { + return isFeedItem(v) && validateFeedItem(v).success +} diff --git a/packages/api/src/client/types/app/bsky/graph/unmuteActor.ts b/packages/api/src/client/types/app/bsky/graph/unmuteActor.ts index 0cb4970f37d..ed457c93f5a 100644 --- a/packages/api/src/client/types/app/bsky/graph/unmuteActor.ts +++ b/packages/api/src/client/types/app/bsky/graph/unmuteActor.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.unmuteActor' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { actor: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/unmuteActorList.ts b/packages/api/src/client/types/app/bsky/graph/unmuteActorList.ts index f866d0d6cdd..e2d98bea417 100644 --- a/packages/api/src/client/types/app/bsky/graph/unmuteActorList.ts +++ b/packages/api/src/client/types/app/bsky/graph/unmuteActorList.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.unmuteActorList' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { list: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/graph/unmuteThread.ts b/packages/api/src/client/types/app/bsky/graph/unmuteThread.ts index 25b03006f27..cda3f5e7e55 100644 --- a/packages/api/src/client/types/app/bsky/graph/unmuteThread.ts +++ b/packages/api/src/client/types/app/bsky/graph/unmuteThread.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.graph.unmuteThread' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { root: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/labeler/defs.ts b/packages/api/src/client/types/app/bsky/labeler/defs.ts index fefa71f91a5..2a9206e5d6f 100644 --- a/packages/api/src/client/types/app/bsky/labeler/defs.ts +++ b/packages/api/src/client/types/app/bsky/labeler/defs.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' @@ -11,6 +11,7 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' const id = 'app.bsky.labeler.defs' export interface LabelerView { + $type?: $Type<'app.bsky.labeler.defs', 'labelerView'> uri: string cid: string creator: AppBskyActorDefs.ProfileView @@ -18,12 +19,9 @@ export interface LabelerView { viewer?: LabelerViewerState indexedAt: string labels?: ComAtprotoLabelDefs.Label[] - [k: string]: unknown } -export function isLabelerView( - v: unknown, -): v is LabelerView & { $type: $Type<'app.bsky.labeler.defs', 'labelerView'> } { +export function isLabelerView(v: V) { return is$typed(v, id, 'labelerView') } @@ -34,7 +32,12 @@ export function validateLabelerView(v: unknown) { ) as ValidationResult } +export function isValidLabelerView(v: V): v is V & $Typed { + return isLabelerView(v) && validateLabelerView(v).success +} + export interface LabelerViewDetailed { + $type?: $Type<'app.bsky.labeler.defs', 'labelerViewDetailed'> uri: string cid: string creator: AppBskyActorDefs.ProfileView @@ -43,12 +46,9 @@ export interface LabelerViewDetailed { viewer?: LabelerViewerState indexedAt: string labels?: ComAtprotoLabelDefs.Label[] - [k: string]: unknown } -export function isLabelerViewDetailed(v: unknown): v is LabelerViewDetailed & { - $type: $Type<'app.bsky.labeler.defs', 'labelerViewDetailed'> -} { +export function isLabelerViewDetailed(v: V) { return is$typed(v, id, 'labelerViewDetailed') } @@ -59,14 +59,18 @@ export function validateLabelerViewDetailed(v: unknown) { ) as ValidationResult } +export function isValidLabelerViewDetailed( + v: V, +): v is V & $Typed { + return isLabelerViewDetailed(v) && validateLabelerViewDetailed(v).success +} + export interface LabelerViewerState { + $type?: $Type<'app.bsky.labeler.defs', 'labelerViewerState'> like?: string - [k: string]: unknown } -export function isLabelerViewerState(v: unknown): v is LabelerViewerState & { - $type: $Type<'app.bsky.labeler.defs', 'labelerViewerState'> -} { +export function isLabelerViewerState(v: V) { return is$typed(v, id, 'labelerViewerState') } @@ -77,17 +81,21 @@ export function validateLabelerViewerState(v: unknown) { ) as ValidationResult } +export function isValidLabelerViewerState( + v: V, +): v is V & $Typed { + return isLabelerViewerState(v) && validateLabelerViewerState(v).success +} + export interface LabelerPolicies { + $type?: $Type<'app.bsky.labeler.defs', 'labelerPolicies'> /** The label values which this labeler publishes. May include global or custom labels. */ labelValues: ComAtprotoLabelDefs.LabelValue[] /** Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler. */ labelValueDefinitions?: ComAtprotoLabelDefs.LabelValueDefinition[] - [k: string]: unknown } -export function isLabelerPolicies(v: unknown): v is LabelerPolicies & { - $type: $Type<'app.bsky.labeler.defs', 'labelerPolicies'> -} { +export function isLabelerPolicies(v: V) { return is$typed(v, id, 'labelerPolicies') } @@ -97,3 +105,9 @@ export function validateLabelerPolicies(v: unknown) { v, ) as ValidationResult } + +export function isValidLabelerPolicies( + v: V, +): v is V & $Typed { + return isLabelerPolicies(v) && validateLabelerPolicies(v).success +} diff --git a/packages/api/src/client/types/app/bsky/labeler/getServices.ts b/packages/api/src/client/types/app/bsky/labeler/getServices.ts index 418dd8e906e..367d9cd922f 100644 --- a/packages/api/src/client/types/app/bsky/labeler/getServices.ts +++ b/packages/api/src/client/types/app/bsky/labeler/getServices.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyLabelerDefs from './defs' @@ -19,11 +19,10 @@ export type InputSchema = undefined export interface OutputSchema { views: ( - | AppBskyLabelerDefs.LabelerView - | AppBskyLabelerDefs.LabelerViewDetailed - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | { $type: string } )[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/labeler/service.ts b/packages/api/src/client/types/app/bsky/labeler/service.ts index 347645730d0..21d919483b0 100644 --- a/packages/api/src/client/types/app/bsky/labeler/service.ts +++ b/packages/api/src/client/types/app/bsky/labeler/service.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyLabelerDefs from './defs' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' @@ -11,20 +11,21 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' const id = 'app.bsky.labeler.service' export interface Record { + $type?: $Type<'app.bsky.labeler.service', 'main'> policies: AppBskyLabelerDefs.LabelerPolicies - labels?: - | ComAtprotoLabelDefs.SelfLabels - | { $type: string; [k: string]: unknown } + labels?: $Typed | { $type: string } createdAt: string [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'app.bsky.labeler.service', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/app/bsky/notification/getUnreadCount.ts b/packages/api/src/client/types/app/bsky/notification/getUnreadCount.ts index b39aec7880a..9b5cb536f6c 100644 --- a/packages/api/src/client/types/app/bsky/notification/getUnreadCount.ts +++ b/packages/api/src/client/types/app/bsky/notification/getUnreadCount.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.notification.getUnreadCount' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { count: number - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/notification/listNotifications.ts b/packages/api/src/client/types/app/bsky/notification/listNotifications.ts index 8b81dc38ff3..1a87dd2272d 100644 --- a/packages/api/src/client/types/app/bsky/notification/listNotifications.ts +++ b/packages/api/src/client/types/app/bsky/notification/listNotifications.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../actor/defs' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' @@ -27,7 +27,6 @@ export interface OutputSchema { notifications: Notification[] priority?: boolean seenAt?: string - [k: string]: unknown } export interface CallOptions { @@ -46,6 +45,7 @@ export function toKnownErr(e: any) { } export interface Notification { + $type?: $Type<'app.bsky.notification.listNotifications', 'notification'> uri: string cid: string author: AppBskyActorDefs.ProfileView @@ -60,16 +60,13 @@ export interface Notification { | 'starterpack-joined' | (string & {}) reasonSubject?: string - record: {} + record: { [_ in string]: unknown } isRead: boolean indexedAt: string labels?: ComAtprotoLabelDefs.Label[] - [k: string]: unknown } -export function isNotification(v: unknown): v is Notification & { - $type: $Type<'app.bsky.notification.listNotifications', 'notification'> -} { +export function isNotification(v: V) { return is$typed(v, id, 'notification') } @@ -79,3 +76,7 @@ export function validateNotification(v: unknown) { v, ) as ValidationResult } + +export function isValidNotification(v: V): v is V & $Typed { + return isNotification(v) && validateNotification(v).success +} diff --git a/packages/api/src/client/types/app/bsky/notification/putPreferences.ts b/packages/api/src/client/types/app/bsky/notification/putPreferences.ts index 507528b2e05..46958de2033 100644 --- a/packages/api/src/client/types/app/bsky/notification/putPreferences.ts +++ b/packages/api/src/client/types/app/bsky/notification/putPreferences.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.notification.putPreferences' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { priority: boolean - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/notification/registerPush.ts b/packages/api/src/client/types/app/bsky/notification/registerPush.ts index 65f40b64633..0f40b0c219d 100644 --- a/packages/api/src/client/types/app/bsky/notification/registerPush.ts +++ b/packages/api/src/client/types/app/bsky/notification/registerPush.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.notification.registerPush' @@ -16,7 +16,6 @@ export interface InputSchema { token: string platform: 'ios' | 'android' | 'web' | (string & {}) appId: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/notification/updateSeen.ts b/packages/api/src/client/types/app/bsky/notification/updateSeen.ts index eaa486ee2f4..040be60255d 100644 --- a/packages/api/src/client/types/app/bsky/notification/updateSeen.ts +++ b/packages/api/src/client/types/app/bsky/notification/updateSeen.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.notification.updateSeen' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { seenAt: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/richtext/facet.ts b/packages/api/src/client/types/app/bsky/richtext/facet.ts index 56d2aebb4cd..091f8f6df94 100644 --- a/packages/api/src/client/types/app/bsky/richtext/facet.ts +++ b/packages/api/src/client/types/app/bsky/richtext/facet.ts @@ -3,21 +3,19 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.richtext.facet' /** Annotation of a sub-string within rich text. */ export interface Main { + $type?: $Type<'app.bsky.richtext.facet', 'main'> index: ByteSlice - features: (Mention | Link | Tag | { $type: string; [k: string]: unknown })[] - [k: string]: unknown + features: ($Typed | $Typed | $Typed | { $type: string })[] } -export function isMain( - v: unknown, -): v is Main & { $type: $Type<'app.bsky.richtext.facet', 'main'> } { +export function isMain(v: V) { return is$typed(v, id, 'main') } @@ -25,15 +23,17 @@ export function validateMain(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult
} +export function isValidMain(v: V): v is V & $Typed
{ + return isMain(v) && validateMain(v).success +} + /** Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID. */ export interface Mention { + $type?: $Type<'app.bsky.richtext.facet', 'mention'> did: string - [k: string]: unknown } -export function isMention( - v: unknown, -): v is Mention & { $type: $Type<'app.bsky.richtext.facet', 'mention'> } { +export function isMention(v: V) { return is$typed(v, id, 'mention') } @@ -41,15 +41,17 @@ export function validateMention(v: unknown) { return lexicons.validate(`${id}#mention`, v) as ValidationResult } +export function isValidMention(v: V): v is V & $Typed { + return isMention(v) && validateMention(v).success +} + /** Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL. */ export interface Link { + $type?: $Type<'app.bsky.richtext.facet', 'link'> uri: string - [k: string]: unknown } -export function isLink( - v: unknown, -): v is Link & { $type: $Type<'app.bsky.richtext.facet', 'link'> } { +export function isLink(v: V) { return is$typed(v, id, 'link') } @@ -57,15 +59,17 @@ export function validateLink(v: unknown) { return lexicons.validate(`${id}#link`, v) as ValidationResult } +export function isValidLink(v: V): v is V & $Typed { + return isLink(v) && validateLink(v).success +} + /** Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags'). */ export interface Tag { + $type?: $Type<'app.bsky.richtext.facet', 'tag'> tag: string - [k: string]: unknown } -export function isTag( - v: unknown, -): v is Tag & { $type: $Type<'app.bsky.richtext.facet', 'tag'> } { +export function isTag(v: V) { return is$typed(v, id, 'tag') } @@ -73,19 +77,25 @@ export function validateTag(v: unknown) { return lexicons.validate(`${id}#tag`, v) as ValidationResult } +export function isValidTag(v: V): v is V & $Typed { + return isTag(v) && validateTag(v).success +} + /** Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets. */ export interface ByteSlice { + $type?: $Type<'app.bsky.richtext.facet', 'byteSlice'> byteStart: number byteEnd: number - [k: string]: unknown } -export function isByteSlice( - v: unknown, -): v is ByteSlice & { $type: $Type<'app.bsky.richtext.facet', 'byteSlice'> } { +export function isByteSlice(v: V) { return is$typed(v, id, 'byteSlice') } export function validateByteSlice(v: unknown) { return lexicons.validate(`${id}#byteSlice`, v) as ValidationResult } + +export function isValidByteSlice(v: V): v is V & $Typed { + return isByteSlice(v) && validateByteSlice(v).success +} diff --git a/packages/api/src/client/types/app/bsky/unspecced/defs.ts b/packages/api/src/client/types/app/bsky/unspecced/defs.ts index 7f48bbdf929..57996637ebf 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/defs.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/defs.ts @@ -3,19 +3,17 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.unspecced.defs' export interface SkeletonSearchPost { + $type?: $Type<'app.bsky.unspecced.defs', 'skeletonSearchPost'> uri: string - [k: string]: unknown } -export function isSkeletonSearchPost(v: unknown): v is SkeletonSearchPost & { - $type: $Type<'app.bsky.unspecced.defs', 'skeletonSearchPost'> -} { +export function isSkeletonSearchPost(v: V) { return is$typed(v, id, 'skeletonSearchPost') } @@ -26,14 +24,18 @@ export function validateSkeletonSearchPost(v: unknown) { ) as ValidationResult } +export function isValidSkeletonSearchPost( + v: V, +): v is V & $Typed { + return isSkeletonSearchPost(v) && validateSkeletonSearchPost(v).success +} + export interface SkeletonSearchActor { + $type?: $Type<'app.bsky.unspecced.defs', 'skeletonSearchActor'> did: string - [k: string]: unknown } -export function isSkeletonSearchActor(v: unknown): v is SkeletonSearchActor & { - $type: $Type<'app.bsky.unspecced.defs', 'skeletonSearchActor'> -} { +export function isSkeletonSearchActor(v: V) { return is$typed(v, id, 'skeletonSearchActor') } @@ -44,16 +46,18 @@ export function validateSkeletonSearchActor(v: unknown) { ) as ValidationResult } +export function isValidSkeletonSearchActor( + v: V, +): v is V & $Typed { + return isSkeletonSearchActor(v) && validateSkeletonSearchActor(v).success +} + export interface SkeletonSearchStarterPack { + $type?: $Type<'app.bsky.unspecced.defs', 'skeletonSearchStarterPack'> uri: string - [k: string]: unknown } -export function isSkeletonSearchStarterPack( - v: unknown, -): v is SkeletonSearchStarterPack & { - $type: $Type<'app.bsky.unspecced.defs', 'skeletonSearchStarterPack'> -} { +export function isSkeletonSearchStarterPack(v: V) { return is$typed(v, id, 'skeletonSearchStarterPack') } @@ -63,3 +67,12 @@ export function validateSkeletonSearchStarterPack(v: unknown) { v, ) as ValidationResult } + +export function isValidSkeletonSearchStarterPack( + v: V, +): v is V & $Typed { + return ( + isSkeletonSearchStarterPack(v) && + validateSkeletonSearchStarterPack(v).success + ) +} diff --git a/packages/api/src/client/types/app/bsky/unspecced/getConfig.ts b/packages/api/src/client/types/app/bsky/unspecced/getConfig.ts index 0f349472146..06012bb2c2c 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/getConfig.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/getConfig.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.unspecced.getConfig' @@ -15,7 +15,6 @@ export type InputSchema = undefined export interface OutputSchema { checkEmailConfirmed?: boolean - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/unspecced/getPopularFeedGenerators.ts b/packages/api/src/client/types/app/bsky/unspecced/getPopularFeedGenerators.ts index 0192a32c8f7..5f35ead3dda 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/getPopularFeedGenerators.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/getPopularFeedGenerators.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyFeedDefs from '../feed/defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string feeds: AppBskyFeedDefs.GeneratorView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/unspecced/getSuggestionsSkeleton.ts b/packages/api/src/client/types/app/bsky/unspecced/getSuggestionsSkeleton.ts index 9df9298b22f..ba1b03120c4 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/getSuggestionsSkeleton.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/getSuggestionsSkeleton.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyUnspeccedDefs from './defs' @@ -26,7 +26,6 @@ export interface OutputSchema { actors: AppBskyUnspeccedDefs.SkeletonSearchActor[] /** DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer. */ relativeToDid?: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts b/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts index 54d1158c093..67f50aeaea0 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/getTaggedSuggestions.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.unspecced.getTaggedSuggestions' @@ -15,7 +15,6 @@ export type InputSchema = undefined export interface OutputSchema { suggestions: Suggestion[] - [k: string]: unknown } export interface CallOptions { @@ -34,15 +33,13 @@ export function toKnownErr(e: any) { } export interface Suggestion { + $type?: $Type<'app.bsky.unspecced.getTaggedSuggestions', 'suggestion'> tag: string subjectType: 'actor' | 'feed' | (string & {}) subject: string - [k: string]: unknown } -export function isSuggestion(v: unknown): v is Suggestion & { - $type: $Type<'app.bsky.unspecced.getTaggedSuggestions', 'suggestion'> -} { +export function isSuggestion(v: V) { return is$typed(v, id, 'suggestion') } @@ -52,3 +49,7 @@ export function validateSuggestion(v: unknown) { v, ) as ValidationResult } + +export function isValidSuggestion(v: V): v is V & $Typed { + return isSuggestion(v) && validateSuggestion(v).success +} diff --git a/packages/api/src/client/types/app/bsky/unspecced/searchActorsSkeleton.ts b/packages/api/src/client/types/app/bsky/unspecced/searchActorsSkeleton.ts index 7799158a8ec..a468f661444 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/searchActorsSkeleton.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/searchActorsSkeleton.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyUnspeccedDefs from './defs' @@ -29,7 +29,6 @@ export interface OutputSchema { /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ hitsTotal?: number actors: AppBskyUnspeccedDefs.SkeletonSearchActor[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/unspecced/searchPostsSkeleton.ts b/packages/api/src/client/types/app/bsky/unspecced/searchPostsSkeleton.ts index 00e032f2be9..36df2904457 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/searchPostsSkeleton.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/searchPostsSkeleton.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyUnspeccedDefs from './defs' @@ -45,7 +45,6 @@ export interface OutputSchema { /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ hitsTotal?: number posts: AppBskyUnspeccedDefs.SkeletonSearchPost[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/unspecced/searchStarterPacksSkeleton.ts b/packages/api/src/client/types/app/bsky/unspecced/searchStarterPacksSkeleton.ts index 51cb7b9e78a..d6cc5ff64d4 100644 --- a/packages/api/src/client/types/app/bsky/unspecced/searchStarterPacksSkeleton.ts +++ b/packages/api/src/client/types/app/bsky/unspecced/searchStarterPacksSkeleton.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyUnspeccedDefs from './defs' @@ -27,7 +27,6 @@ export interface OutputSchema { /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ hitsTotal?: number starterPacks: AppBskyUnspeccedDefs.SkeletonSearchStarterPack[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/video/defs.ts b/packages/api/src/client/types/app/bsky/video/defs.ts index 3cf6cae3436..be3a1da9385 100644 --- a/packages/api/src/client/types/app/bsky/video/defs.ts +++ b/packages/api/src/client/types/app/bsky/video/defs.ts @@ -3,12 +3,13 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.video.defs' export interface JobStatus { + $type?: $Type<'app.bsky.video.defs', 'jobStatus'> jobId: string did: string /** The state of the video processing job. All values not listed as a known value indicate that the job is in process. */ @@ -18,15 +19,16 @@ export interface JobStatus { blob?: BlobRef error?: string message?: string - [k: string]: unknown } -export function isJobStatus( - v: unknown, -): v is JobStatus & { $type: $Type<'app.bsky.video.defs', 'jobStatus'> } { +export function isJobStatus(v: V) { return is$typed(v, id, 'jobStatus') } export function validateJobStatus(v: unknown) { return lexicons.validate(`${id}#jobStatus`, v) as ValidationResult } + +export function isValidJobStatus(v: V): v is V & $Typed { + return isJobStatus(v) && validateJobStatus(v).success +} diff --git a/packages/api/src/client/types/app/bsky/video/getJobStatus.ts b/packages/api/src/client/types/app/bsky/video/getJobStatus.ts index a303f44ed52..c55919db04f 100644 --- a/packages/api/src/client/types/app/bsky/video/getJobStatus.ts +++ b/packages/api/src/client/types/app/bsky/video/getJobStatus.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyVideoDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { jobStatus: AppBskyVideoDefs.JobStatus - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/video/getUploadLimits.ts b/packages/api/src/client/types/app/bsky/video/getUploadLimits.ts index dc653bc87f9..ea29e4d8963 100644 --- a/packages/api/src/client/types/app/bsky/video/getUploadLimits.ts +++ b/packages/api/src/client/types/app/bsky/video/getUploadLimits.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'app.bsky.video.getUploadLimits' @@ -19,7 +19,6 @@ export interface OutputSchema { remainingDailyBytes?: number message?: string error?: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/app/bsky/video/uploadVideo.ts b/packages/api/src/client/types/app/bsky/video/uploadVideo.ts index f56dd13cec2..b7070f9c898 100644 --- a/packages/api/src/client/types/app/bsky/video/uploadVideo.ts +++ b/packages/api/src/client/types/app/bsky/video/uploadVideo.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyVideoDefs from './defs' @@ -16,7 +16,6 @@ export type InputSchema = string | Uint8Array | Blob export interface OutputSchema { jobStatus: AppBskyVideoDefs.JobStatus - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/actor/declaration.ts b/packages/api/src/client/types/chat/bsky/actor/declaration.ts index a17b0b24ffd..8e6aee156b9 100644 --- a/packages/api/src/client/types/chat/bsky/actor/declaration.ts +++ b/packages/api/src/client/types/chat/bsky/actor/declaration.ts @@ -3,22 +3,25 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'chat.bsky.actor.declaration' export interface Record { + $type?: $Type<'chat.bsky.actor.declaration', 'main'> allowIncoming: 'all' | 'none' | 'following' | (string & {}) [k: string]: unknown } -export function isRecord( - v: unknown, -): v is Record & { $type: $Type<'chat.bsky.actor.declaration', 'main'> } { +export function isRecord(v: V) { return is$typed(v, id, 'main') } export function validateRecord(v: unknown) { return lexicons.validate(`${id}#main`, v) as ValidationResult } + +export function isValidRecord(v: V): v is V & $Typed { + return isRecord(v) && validateRecord(v).success +} diff --git a/packages/api/src/client/types/chat/bsky/actor/defs.ts b/packages/api/src/client/types/chat/bsky/actor/defs.ts index 405dadae7cd..9cff2cf3177 100644 --- a/packages/api/src/client/types/chat/bsky/actor/defs.ts +++ b/packages/api/src/client/types/chat/bsky/actor/defs.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyActorDefs from '../../../app/bsky/actor/defs' import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' @@ -11,6 +11,7 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' const id = 'chat.bsky.actor.defs' export interface ProfileViewBasic { + $type?: $Type<'chat.bsky.actor.defs', 'profileViewBasic'> did: string handle: string displayName?: string @@ -20,12 +21,9 @@ export interface ProfileViewBasic { labels?: ComAtprotoLabelDefs.Label[] /** Set to true when the actor cannot actively participate in converations */ chatDisabled?: boolean - [k: string]: unknown } -export function isProfileViewBasic(v: unknown): v is ProfileViewBasic & { - $type: $Type<'chat.bsky.actor.defs', 'profileViewBasic'> -} { +export function isProfileViewBasic(v: V) { return is$typed(v, id, 'profileViewBasic') } @@ -35,3 +33,9 @@ export function validateProfileViewBasic(v: unknown) { v, ) as ValidationResult } + +export function isValidProfileViewBasic( + v: V, +): v is V & $Typed { + return isProfileViewBasic(v) && validateProfileViewBasic(v).success +} diff --git a/packages/api/src/client/types/chat/bsky/actor/deleteAccount.ts b/packages/api/src/client/types/chat/bsky/actor/deleteAccount.ts index c96bdf96729..efcbed3a37c 100644 --- a/packages/api/src/client/types/chat/bsky/actor/deleteAccount.ts +++ b/packages/api/src/client/types/chat/bsky/actor/deleteAccount.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'chat.bsky.actor.deleteAccount' @@ -13,9 +13,7 @@ export interface QueryParams {} export type InputSchema = undefined -export interface OutputSchema { - [k: string]: unknown -} +export interface OutputSchema {} export interface CallOptions { signal?: AbortSignal diff --git a/packages/api/src/client/types/chat/bsky/actor/exportAccountData.ts b/packages/api/src/client/types/chat/bsky/actor/exportAccountData.ts index 21d7bb32b26..176bc371c86 100644 --- a/packages/api/src/client/types/chat/bsky/actor/exportAccountData.ts +++ b/packages/api/src/client/types/chat/bsky/actor/exportAccountData.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'chat.bsky.actor.exportAccountData' diff --git a/packages/api/src/client/types/chat/bsky/convo/defs.ts b/packages/api/src/client/types/chat/bsky/convo/defs.ts index 4b08ce380ab..be89d230120 100644 --- a/packages/api/src/client/types/chat/bsky/convo/defs.ts +++ b/packages/api/src/client/types/chat/bsky/convo/defs.ts @@ -3,7 +3,7 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as AppBskyRichtextFacet from '../../../app/bsky/richtext/facet' import * as AppBskyEmbedRecord from '../../../app/bsky/embed/record' @@ -12,15 +12,13 @@ import * as ChatBskyActorDefs from '../actor/defs' const id = 'chat.bsky.convo.defs' export interface MessageRef { + $type?: $Type<'chat.bsky.convo.defs', 'messageRef'> did: string convoId: string messageId: string - [k: string]: unknown } -export function isMessageRef( - v: unknown, -): v is MessageRef & { $type: $Type<'chat.bsky.convo.defs', 'messageRef'> } { +export function isMessageRef(v: V) { return is$typed(v, id, 'messageRef') } @@ -31,17 +29,19 @@ export function validateMessageRef(v: unknown) { ) as ValidationResult } +export function isValidMessageRef(v: V): v is V & $Typed { + return isMessageRef(v) && validateMessageRef(v).success +} + export interface MessageInput { + $type?: $Type<'chat.bsky.convo.defs', 'messageInput'> text: string /** Annotations of text (mentions, URLs, hashtags, etc) */ facets?: AppBskyRichtextFacet.Main[] - embed?: AppBskyEmbedRecord.Main | { $type: string; [k: string]: unknown } - [k: string]: unknown + embed?: $Typed | { $type: string } } -export function isMessageInput(v: unknown): v is MessageInput & { - $type: $Type<'chat.bsky.convo.defs', 'messageInput'> -} { +export function isMessageInput(v: V) { return is$typed(v, id, 'messageInput') } @@ -52,21 +52,23 @@ export function validateMessageInput(v: unknown) { ) as ValidationResult } +export function isValidMessageInput(v: V): v is V & $Typed { + return isMessageInput(v) && validateMessageInput(v).success +} + export interface MessageView { + $type?: $Type<'chat.bsky.convo.defs', 'messageView'> id: string rev: string text: string /** Annotations of text (mentions, URLs, hashtags, etc) */ facets?: AppBskyRichtextFacet.Main[] - embed?: AppBskyEmbedRecord.View | { $type: string; [k: string]: unknown } + embed?: $Typed | { $type: string } sender: MessageViewSender sentAt: string - [k: string]: unknown } -export function isMessageView( - v: unknown, -): v is MessageView & { $type: $Type<'chat.bsky.convo.defs', 'messageView'> } { +export function isMessageView(v: V) { return is$typed(v, id, 'messageView') } @@ -77,17 +79,19 @@ export function validateMessageView(v: unknown) { ) as ValidationResult } +export function isValidMessageView(v: V): v is V & $Typed { + return isMessageView(v) && validateMessageView(v).success +} + export interface DeletedMessageView { + $type?: $Type<'chat.bsky.convo.defs', 'deletedMessageView'> id: string rev: string sender: MessageViewSender sentAt: string - [k: string]: unknown } -export function isDeletedMessageView(v: unknown): v is DeletedMessageView & { - $type: $Type<'chat.bsky.convo.defs', 'deletedMessageView'> -} { +export function isDeletedMessageView(v: V) { return is$typed(v, id, 'deletedMessageView') } @@ -98,14 +102,18 @@ export function validateDeletedMessageView(v: unknown) { ) as ValidationResult } +export function isValidDeletedMessageView( + v: V, +): v is V & $Typed { + return isDeletedMessageView(v) && validateDeletedMessageView(v).success +} + export interface MessageViewSender { + $type?: $Type<'chat.bsky.convo.defs', 'messageViewSender'> did: string - [k: string]: unknown } -export function isMessageViewSender(v: unknown): v is MessageViewSender & { - $type: $Type<'chat.bsky.convo.defs', 'messageViewSender'> -} { +export function isMessageViewSender(v: V) { return is$typed(v, id, 'messageViewSender') } @@ -116,23 +124,27 @@ export function validateMessageViewSender(v: unknown) { ) as ValidationResult } +export function isValidMessageViewSender( + v: V, +): v is V & $Typed { + return isMessageViewSender(v) && validateMessageViewSender(v).success +} + export interface ConvoView { + $type?: $Type<'chat.bsky.convo.defs', 'convoView'> id: string rev: string members: ChatBskyActorDefs.ProfileViewBasic[] lastMessage?: - | MessageView - | DeletedMessageView - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | { $type: string } muted: boolean opened?: boolean unreadCount: number - [k: string]: unknown } -export function isConvoView( - v: unknown, -): v is ConvoView & { $type: $Type<'chat.bsky.convo.defs', 'convoView'> } { +export function isConvoView(v: V) { return is$typed(v, id, 'convoView') } @@ -140,15 +152,17 @@ export function validateConvoView(v: unknown) { return lexicons.validate(`${id}#convoView`, v) as ValidationResult } +export function isValidConvoView(v: V): v is V & $Typed { + return isConvoView(v) && validateConvoView(v).success +} + export interface LogBeginConvo { + $type?: $Type<'chat.bsky.convo.defs', 'logBeginConvo'> rev: string convoId: string - [k: string]: unknown } -export function isLogBeginConvo(v: unknown): v is LogBeginConvo & { - $type: $Type<'chat.bsky.convo.defs', 'logBeginConvo'> -} { +export function isLogBeginConvo(v: V) { return is$typed(v, id, 'logBeginConvo') } @@ -159,15 +173,17 @@ export function validateLogBeginConvo(v: unknown) { ) as ValidationResult } +export function isValidLogBeginConvo(v: V): v is V & $Typed { + return isLogBeginConvo(v) && validateLogBeginConvo(v).success +} + export interface LogLeaveConvo { + $type?: $Type<'chat.bsky.convo.defs', 'logLeaveConvo'> rev: string convoId: string - [k: string]: unknown } -export function isLogLeaveConvo(v: unknown): v is LogLeaveConvo & { - $type: $Type<'chat.bsky.convo.defs', 'logLeaveConvo'> -} { +export function isLogLeaveConvo(v: V) { return is$typed(v, id, 'logLeaveConvo') } @@ -178,19 +194,18 @@ export function validateLogLeaveConvo(v: unknown) { ) as ValidationResult } +export function isValidLogLeaveConvo(v: V): v is V & $Typed { + return isLogLeaveConvo(v) && validateLogLeaveConvo(v).success +} + export interface LogCreateMessage { + $type?: $Type<'chat.bsky.convo.defs', 'logCreateMessage'> rev: string convoId: string - message: - | MessageView - | DeletedMessageView - | { $type: string; [k: string]: unknown } - [k: string]: unknown + message: $Typed | $Typed | { $type: string } } -export function isLogCreateMessage(v: unknown): v is LogCreateMessage & { - $type: $Type<'chat.bsky.convo.defs', 'logCreateMessage'> -} { +export function isLogCreateMessage(v: V) { return is$typed(v, id, 'logCreateMessage') } @@ -201,19 +216,20 @@ export function validateLogCreateMessage(v: unknown) { ) as ValidationResult } +export function isValidLogCreateMessage( + v: V, +): v is V & $Typed { + return isLogCreateMessage(v) && validateLogCreateMessage(v).success +} + export interface LogDeleteMessage { + $type?: $Type<'chat.bsky.convo.defs', 'logDeleteMessage'> rev: string convoId: string - message: - | MessageView - | DeletedMessageView - | { $type: string; [k: string]: unknown } - [k: string]: unknown + message: $Typed | $Typed | { $type: string } } -export function isLogDeleteMessage(v: unknown): v is LogDeleteMessage & { - $type: $Type<'chat.bsky.convo.defs', 'logDeleteMessage'> -} { +export function isLogDeleteMessage(v: V) { return is$typed(v, id, 'logDeleteMessage') } @@ -223,3 +239,9 @@ export function validateLogDeleteMessage(v: unknown) { v, ) as ValidationResult } + +export function isValidLogDeleteMessage( + v: V, +): v is V & $Typed { + return isLogDeleteMessage(v) && validateLogDeleteMessage(v).success +} diff --git a/packages/api/src/client/types/chat/bsky/convo/deleteMessageForSelf.ts b/packages/api/src/client/types/chat/bsky/convo/deleteMessageForSelf.ts index 0679a4fe677..6f2c3793703 100644 --- a/packages/api/src/client/types/chat/bsky/convo/deleteMessageForSelf.ts +++ b/packages/api/src/client/types/chat/bsky/convo/deleteMessageForSelf.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -15,7 +15,6 @@ export interface QueryParams {} export interface InputSchema { convoId: string messageId: string - [k: string]: unknown } export type OutputSchema = ChatBskyConvoDefs.DeletedMessageView diff --git a/packages/api/src/client/types/chat/bsky/convo/getConvo.ts b/packages/api/src/client/types/chat/bsky/convo/getConvo.ts index ba605729939..c3e6e925f8a 100644 --- a/packages/api/src/client/types/chat/bsky/convo/getConvo.ts +++ b/packages/api/src/client/types/chat/bsky/convo/getConvo.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { convo: ChatBskyConvoDefs.ConvoView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/getConvoForMembers.ts b/packages/api/src/client/types/chat/bsky/convo/getConvoForMembers.ts index 795845b7379..d16bd5aca69 100644 --- a/packages/api/src/client/types/chat/bsky/convo/getConvoForMembers.ts +++ b/packages/api/src/client/types/chat/bsky/convo/getConvoForMembers.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { convo: ChatBskyConvoDefs.ConvoView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/getLog.ts b/packages/api/src/client/types/chat/bsky/convo/getLog.ts index 4185bc39c12..48544b7f30a 100644 --- a/packages/api/src/client/types/chat/bsky/convo/getLog.ts +++ b/packages/api/src/client/types/chat/bsky/convo/getLog.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -19,13 +19,12 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string logs: ( - | ChatBskyConvoDefs.LogBeginConvo - | ChatBskyConvoDefs.LogLeaveConvo - | ChatBskyConvoDefs.LogCreateMessage - | ChatBskyConvoDefs.LogDeleteMessage - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | $Typed + | { $type: string } )[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/getMessages.ts b/packages/api/src/client/types/chat/bsky/convo/getMessages.ts index d9f043c8081..4b41053f22b 100644 --- a/packages/api/src/client/types/chat/bsky/convo/getMessages.ts +++ b/packages/api/src/client/types/chat/bsky/convo/getMessages.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -21,11 +21,10 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string messages: ( - | ChatBskyConvoDefs.MessageView - | ChatBskyConvoDefs.DeletedMessageView - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | { $type: string } )[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/leaveConvo.ts b/packages/api/src/client/types/chat/bsky/convo/leaveConvo.ts index 5feed2d0ffb..09c6a3fef47 100644 --- a/packages/api/src/client/types/chat/bsky/convo/leaveConvo.ts +++ b/packages/api/src/client/types/chat/bsky/convo/leaveConvo.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'chat.bsky.convo.leaveConvo' @@ -13,13 +13,11 @@ export interface QueryParams {} export interface InputSchema { convoId: string - [k: string]: unknown } export interface OutputSchema { convoId: string rev: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/listConvos.ts b/packages/api/src/client/types/chat/bsky/convo/listConvos.ts index 19fcfc73554..b2b99932d2a 100644 --- a/packages/api/src/client/types/chat/bsky/convo/listConvos.ts +++ b/packages/api/src/client/types/chat/bsky/convo/listConvos.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -20,7 +20,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string convos: ChatBskyConvoDefs.ConvoView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/muteConvo.ts b/packages/api/src/client/types/chat/bsky/convo/muteConvo.ts index caf7dc9c4a7..a5718994dbf 100644 --- a/packages/api/src/client/types/chat/bsky/convo/muteConvo.ts +++ b/packages/api/src/client/types/chat/bsky/convo/muteConvo.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -14,12 +14,10 @@ export interface QueryParams {} export interface InputSchema { convoId: string - [k: string]: unknown } export interface OutputSchema { convo: ChatBskyConvoDefs.ConvoView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/sendMessage.ts b/packages/api/src/client/types/chat/bsky/convo/sendMessage.ts index b5749f68c49..3492cf95a1d 100644 --- a/packages/api/src/client/types/chat/bsky/convo/sendMessage.ts +++ b/packages/api/src/client/types/chat/bsky/convo/sendMessage.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -15,7 +15,6 @@ export interface QueryParams {} export interface InputSchema { convoId: string message: ChatBskyConvoDefs.MessageInput - [k: string]: unknown } export type OutputSchema = ChatBskyConvoDefs.MessageView diff --git a/packages/api/src/client/types/chat/bsky/convo/sendMessageBatch.ts b/packages/api/src/client/types/chat/bsky/convo/sendMessageBatch.ts index 448e6a695c4..937fdbd4084 100644 --- a/packages/api/src/client/types/chat/bsky/convo/sendMessageBatch.ts +++ b/packages/api/src/client/types/chat/bsky/convo/sendMessageBatch.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -14,12 +14,10 @@ export interface QueryParams {} export interface InputSchema { items: BatchItem[] - [k: string]: unknown } export interface OutputSchema { items: ChatBskyConvoDefs.MessageView[] - [k: string]: unknown } export interface CallOptions { @@ -40,17 +38,19 @@ export function toKnownErr(e: any) { } export interface BatchItem { + $type?: $Type<'chat.bsky.convo.sendMessageBatch', 'batchItem'> convoId: string message: ChatBskyConvoDefs.MessageInput - [k: string]: unknown } -export function isBatchItem(v: unknown): v is BatchItem & { - $type: $Type<'chat.bsky.convo.sendMessageBatch', 'batchItem'> -} { +export function isBatchItem(v: V) { return is$typed(v, id, 'batchItem') } export function validateBatchItem(v: unknown) { return lexicons.validate(`${id}#batchItem`, v) as ValidationResult } + +export function isValidBatchItem(v: V): v is V & $Typed { + return isBatchItem(v) && validateBatchItem(v).success +} diff --git a/packages/api/src/client/types/chat/bsky/convo/unmuteConvo.ts b/packages/api/src/client/types/chat/bsky/convo/unmuteConvo.ts index 7bbf475d509..3c03a7bc17a 100644 --- a/packages/api/src/client/types/chat/bsky/convo/unmuteConvo.ts +++ b/packages/api/src/client/types/chat/bsky/convo/unmuteConvo.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -14,12 +14,10 @@ export interface QueryParams {} export interface InputSchema { convoId: string - [k: string]: unknown } export interface OutputSchema { convo: ChatBskyConvoDefs.ConvoView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/convo/updateRead.ts b/packages/api/src/client/types/chat/bsky/convo/updateRead.ts index a990b1d14bf..5402ef5fc45 100644 --- a/packages/api/src/client/types/chat/bsky/convo/updateRead.ts +++ b/packages/api/src/client/types/chat/bsky/convo/updateRead.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from './defs' @@ -15,12 +15,10 @@ export interface QueryParams {} export interface InputSchema { convoId: string messageId?: string - [k: string]: unknown } export interface OutputSchema { convo: ChatBskyConvoDefs.ConvoView - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/moderation/getActorMetadata.ts b/packages/api/src/client/types/chat/bsky/moderation/getActorMetadata.ts index 9e2b0789f2a..4a33a55f285 100644 --- a/packages/api/src/client/types/chat/bsky/moderation/getActorMetadata.ts +++ b/packages/api/src/client/types/chat/bsky/moderation/getActorMetadata.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'chat.bsky.moderation.getActorMetadata' @@ -19,7 +19,6 @@ export interface OutputSchema { day: Metadata month: Metadata all: Metadata - [k: string]: unknown } export interface CallOptions { @@ -38,19 +37,21 @@ export function toKnownErr(e: any) { } export interface Metadata { + $type?: $Type<'chat.bsky.moderation.getActorMetadata', 'metadata'> messagesSent: number messagesReceived: number convos: number convosStarted: number - [k: string]: unknown } -export function isMetadata(v: unknown): v is Metadata & { - $type: $Type<'chat.bsky.moderation.getActorMetadata', 'metadata'> -} { +export function isMetadata(v: V) { return is$typed(v, id, 'metadata') } export function validateMetadata(v: unknown) { return lexicons.validate(`${id}#metadata`, v) as ValidationResult } + +export function isValidMetadata(v: V): v is V & $Typed { + return isMetadata(v) && validateMetadata(v).success +} diff --git a/packages/api/src/client/types/chat/bsky/moderation/getMessageContext.ts b/packages/api/src/client/types/chat/bsky/moderation/getMessageContext.ts index 5ac31fa6ea9..cacb5e7c943 100644 --- a/packages/api/src/client/types/chat/bsky/moderation/getMessageContext.ts +++ b/packages/api/src/client/types/chat/bsky/moderation/getMessageContext.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ChatBskyConvoDefs from '../convo/defs' @@ -22,11 +22,10 @@ export type InputSchema = undefined export interface OutputSchema { messages: ( - | ChatBskyConvoDefs.MessageView - | ChatBskyConvoDefs.DeletedMessageView - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | { $type: string } )[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/chat/bsky/moderation/updateActorAccess.ts b/packages/api/src/client/types/chat/bsky/moderation/updateActorAccess.ts index daf01350424..f5a31dd9496 100644 --- a/packages/api/src/client/types/chat/bsky/moderation/updateActorAccess.ts +++ b/packages/api/src/client/types/chat/bsky/moderation/updateActorAccess.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'chat.bsky.moderation.updateActorAccess' @@ -15,7 +15,6 @@ export interface InputSchema { actor: string allowAccess: boolean ref?: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/defs.ts b/packages/api/src/client/types/com/atproto/admin/defs.ts index f9c218ab111..8f8f404c3fd 100644 --- a/packages/api/src/client/types/com/atproto/admin/defs.ts +++ b/packages/api/src/client/types/com/atproto/admin/defs.ts @@ -3,21 +3,19 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoServerDefs from '../server/defs' const id = 'com.atproto.admin.defs' export interface StatusAttr { + $type?: $Type<'com.atproto.admin.defs', 'statusAttr'> applied: boolean ref?: string - [k: string]: unknown } -export function isStatusAttr( - v: unknown, -): v is StatusAttr & { $type: $Type<'com.atproto.admin.defs', 'statusAttr'> } { +export function isStatusAttr(v: V) { return is$typed(v, id, 'statusAttr') } @@ -28,11 +26,16 @@ export function validateStatusAttr(v: unknown) { ) as ValidationResult } +export function isValidStatusAttr(v: V): v is V & $Typed { + return isStatusAttr(v) && validateStatusAttr(v).success +} + export interface AccountView { + $type?: $Type<'com.atproto.admin.defs', 'accountView'> did: string handle: string email?: string - relatedRecords?: {}[] + relatedRecords?: { [_ in string]: unknown }[] indexedAt: string invitedBy?: ComAtprotoServerDefs.InviteCode invites?: ComAtprotoServerDefs.InviteCode[] @@ -41,12 +44,9 @@ export interface AccountView { inviteNote?: string deactivatedAt?: string threatSignatures?: ThreatSignature[] - [k: string]: unknown } -export function isAccountView(v: unknown): v is AccountView & { - $type: $Type<'com.atproto.admin.defs', 'accountView'> -} { +export function isAccountView(v: V) { return is$typed(v, id, 'accountView') } @@ -57,14 +57,16 @@ export function validateAccountView(v: unknown) { ) as ValidationResult } +export function isValidAccountView(v: V): v is V & $Typed { + return isAccountView(v) && validateAccountView(v).success +} + export interface RepoRef { + $type?: $Type<'com.atproto.admin.defs', 'repoRef'> did: string - [k: string]: unknown } -export function isRepoRef( - v: unknown, -): v is RepoRef & { $type: $Type<'com.atproto.admin.defs', 'repoRef'> } { +export function isRepoRef(v: V) { return is$typed(v, id, 'repoRef') } @@ -72,16 +74,18 @@ export function validateRepoRef(v: unknown) { return lexicons.validate(`${id}#repoRef`, v) as ValidationResult } +export function isValidRepoRef(v: V): v is V & $Typed { + return isRepoRef(v) && validateRepoRef(v).success +} + export interface RepoBlobRef { + $type?: $Type<'com.atproto.admin.defs', 'repoBlobRef'> did: string cid: string recordUri?: string - [k: string]: unknown } -export function isRepoBlobRef(v: unknown): v is RepoBlobRef & { - $type: $Type<'com.atproto.admin.defs', 'repoBlobRef'> -} { +export function isRepoBlobRef(v: V) { return is$typed(v, id, 'repoBlobRef') } @@ -92,15 +96,17 @@ export function validateRepoBlobRef(v: unknown) { ) as ValidationResult } +export function isValidRepoBlobRef(v: V): v is V & $Typed { + return isRepoBlobRef(v) && validateRepoBlobRef(v).success +} + export interface ThreatSignature { + $type?: $Type<'com.atproto.admin.defs', 'threatSignature'> property: string value: string - [k: string]: unknown } -export function isThreatSignature(v: unknown): v is ThreatSignature & { - $type: $Type<'com.atproto.admin.defs', 'threatSignature'> -} { +export function isThreatSignature(v: V) { return is$typed(v, id, 'threatSignature') } @@ -110,3 +116,9 @@ export function validateThreatSignature(v: unknown) { v, ) as ValidationResult } + +export function isValidThreatSignature( + v: V, +): v is V & $Typed { + return isThreatSignature(v) && validateThreatSignature(v).success +} diff --git a/packages/api/src/client/types/com/atproto/admin/deleteAccount.ts b/packages/api/src/client/types/com/atproto/admin/deleteAccount.ts index ad77e52f254..f4dfb790b34 100644 --- a/packages/api/src/client/types/com/atproto/admin/deleteAccount.ts +++ b/packages/api/src/client/types/com/atproto/admin/deleteAccount.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.deleteAccount' @@ -13,7 +13,6 @@ export interface QueryParams {} export interface InputSchema { did: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/disableAccountInvites.ts b/packages/api/src/client/types/com/atproto/admin/disableAccountInvites.ts index 0be94798622..b9f38e0cabd 100644 --- a/packages/api/src/client/types/com/atproto/admin/disableAccountInvites.ts +++ b/packages/api/src/client/types/com/atproto/admin/disableAccountInvites.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.disableAccountInvites' @@ -15,7 +15,6 @@ export interface InputSchema { account: string /** Optional reason for disabled invites. */ note?: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/disableInviteCodes.ts b/packages/api/src/client/types/com/atproto/admin/disableInviteCodes.ts index 20149d69098..a4a7fbb0e08 100644 --- a/packages/api/src/client/types/com/atproto/admin/disableInviteCodes.ts +++ b/packages/api/src/client/types/com/atproto/admin/disableInviteCodes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.disableInviteCodes' @@ -14,7 +14,6 @@ export interface QueryParams {} export interface InputSchema { codes?: string[] accounts?: string[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/enableAccountInvites.ts b/packages/api/src/client/types/com/atproto/admin/enableAccountInvites.ts index f98d75f0638..5757c3e11c6 100644 --- a/packages/api/src/client/types/com/atproto/admin/enableAccountInvites.ts +++ b/packages/api/src/client/types/com/atproto/admin/enableAccountInvites.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.enableAccountInvites' @@ -15,7 +15,6 @@ export interface InputSchema { account: string /** Optional reason for enabled invites. */ note?: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/getAccountInfo.ts b/packages/api/src/client/types/com/atproto/admin/getAccountInfo.ts index b99538478f3..6ab720e0f50 100644 --- a/packages/api/src/client/types/com/atproto/admin/getAccountInfo.ts +++ b/packages/api/src/client/types/com/atproto/admin/getAccountInfo.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoAdminDefs from './defs' diff --git a/packages/api/src/client/types/com/atproto/admin/getAccountInfos.ts b/packages/api/src/client/types/com/atproto/admin/getAccountInfos.ts index f92ab95b675..af904d80029 100644 --- a/packages/api/src/client/types/com/atproto/admin/getAccountInfos.ts +++ b/packages/api/src/client/types/com/atproto/admin/getAccountInfos.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoAdminDefs from './defs' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { infos: ComAtprotoAdminDefs.AccountView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/getInviteCodes.ts b/packages/api/src/client/types/com/atproto/admin/getInviteCodes.ts index dba73561108..0f9d098cd04 100644 --- a/packages/api/src/client/types/com/atproto/admin/getInviteCodes.ts +++ b/packages/api/src/client/types/com/atproto/admin/getInviteCodes.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoServerDefs from '../server/defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string codes: ComAtprotoServerDefs.InviteCode[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/getSubjectStatus.ts b/packages/api/src/client/types/com/atproto/admin/getSubjectStatus.ts index 771b3aeb8fa..aff224b9e6b 100644 --- a/packages/api/src/client/types/com/atproto/admin/getSubjectStatus.ts +++ b/packages/api/src/client/types/com/atproto/admin/getSubjectStatus.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoAdminDefs from './defs' import * as ComAtprotoRepoStrongRef from '../repo/strongRef' @@ -21,13 +21,12 @@ export type InputSchema = undefined export interface OutputSchema { subject: - | ComAtprotoAdminDefs.RepoRef - | ComAtprotoRepoStrongRef.Main - | ComAtprotoAdminDefs.RepoBlobRef - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } takedown?: ComAtprotoAdminDefs.StatusAttr deactivated?: ComAtprotoAdminDefs.StatusAttr - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/searchAccounts.ts b/packages/api/src/client/types/com/atproto/admin/searchAccounts.ts index 861e1db3b48..5f6e53f41f6 100644 --- a/packages/api/src/client/types/com/atproto/admin/searchAccounts.ts +++ b/packages/api/src/client/types/com/atproto/admin/searchAccounts.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoAdminDefs from './defs' @@ -21,7 +21,6 @@ export type InputSchema = undefined export interface OutputSchema { cursor?: string accounts: ComAtprotoAdminDefs.AccountView[] - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/sendEmail.ts b/packages/api/src/client/types/com/atproto/admin/sendEmail.ts index 55ac6dbf24b..e2f507bccc8 100644 --- a/packages/api/src/client/types/com/atproto/admin/sendEmail.ts +++ b/packages/api/src/client/types/com/atproto/admin/sendEmail.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.sendEmail' @@ -18,12 +18,10 @@ export interface InputSchema { senderDid: string /** Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers */ comment?: string - [k: string]: unknown } export interface OutputSchema { sent: boolean - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/updateAccountEmail.ts b/packages/api/src/client/types/com/atproto/admin/updateAccountEmail.ts index 99a48490a6d..52318d621ca 100644 --- a/packages/api/src/client/types/com/atproto/admin/updateAccountEmail.ts +++ b/packages/api/src/client/types/com/atproto/admin/updateAccountEmail.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.updateAccountEmail' @@ -15,7 +15,6 @@ export interface InputSchema { /** The handle or DID of the repo. */ account: string email: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/updateAccountHandle.ts b/packages/api/src/client/types/com/atproto/admin/updateAccountHandle.ts index 974144910ee..a143fa5f3d8 100644 --- a/packages/api/src/client/types/com/atproto/admin/updateAccountHandle.ts +++ b/packages/api/src/client/types/com/atproto/admin/updateAccountHandle.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.updateAccountHandle' @@ -14,7 +14,6 @@ export interface QueryParams {} export interface InputSchema { did: string handle: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/updateAccountPassword.ts b/packages/api/src/client/types/com/atproto/admin/updateAccountPassword.ts index 5c06c7f0df0..ea842bcd43a 100644 --- a/packages/api/src/client/types/com/atproto/admin/updateAccountPassword.ts +++ b/packages/api/src/client/types/com/atproto/admin/updateAccountPassword.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.admin.updateAccountPassword' @@ -14,7 +14,6 @@ export interface QueryParams {} export interface InputSchema { did: string password: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/admin/updateSubjectStatus.ts b/packages/api/src/client/types/com/atproto/admin/updateSubjectStatus.ts index 977ff64ab4e..3d9965481f7 100644 --- a/packages/api/src/client/types/com/atproto/admin/updateSubjectStatus.ts +++ b/packages/api/src/client/types/com/atproto/admin/updateSubjectStatus.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' import * as ComAtprotoAdminDefs from './defs' import * as ComAtprotoRepoStrongRef from '../repo/strongRef' @@ -15,23 +15,21 @@ export interface QueryParams {} export interface InputSchema { subject: - | ComAtprotoAdminDefs.RepoRef - | ComAtprotoRepoStrongRef.Main - | ComAtprotoAdminDefs.RepoBlobRef - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } takedown?: ComAtprotoAdminDefs.StatusAttr deactivated?: ComAtprotoAdminDefs.StatusAttr - [k: string]: unknown } export interface OutputSchema { subject: - | ComAtprotoAdminDefs.RepoRef - | ComAtprotoRepoStrongRef.Main - | ComAtprotoAdminDefs.RepoBlobRef - | { $type: string; [k: string]: unknown } + | $Typed + | $Typed + | $Typed + | { $type: string } takedown?: ComAtprotoAdminDefs.StatusAttr - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/identity/getRecommendedDidCredentials.ts b/packages/api/src/client/types/com/atproto/identity/getRecommendedDidCredentials.ts index 040338ae5e9..4e1f1db4457 100644 --- a/packages/api/src/client/types/com/atproto/identity/getRecommendedDidCredentials.ts +++ b/packages/api/src/client/types/com/atproto/identity/getRecommendedDidCredentials.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.identity.getRecommendedDidCredentials' @@ -17,9 +17,8 @@ export interface OutputSchema { /** Recommended rotation keys for PLC dids. Should be undefined (or ignored) for did:webs. */ rotationKeys?: string[] alsoKnownAs?: string[] - verificationMethods?: {} - services?: {} - [k: string]: unknown + verificationMethods?: { [_ in string]: unknown } + services?: { [_ in string]: unknown } } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/identity/requestPlcOperationSignature.ts b/packages/api/src/client/types/com/atproto/identity/requestPlcOperationSignature.ts index e3dccdc0b20..8e30e0e5348 100644 --- a/packages/api/src/client/types/com/atproto/identity/requestPlcOperationSignature.ts +++ b/packages/api/src/client/types/com/atproto/identity/requestPlcOperationSignature.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.identity.requestPlcOperationSignature' diff --git a/packages/api/src/client/types/com/atproto/identity/resolveHandle.ts b/packages/api/src/client/types/com/atproto/identity/resolveHandle.ts index afc0829e338..0c9042f6fa7 100644 --- a/packages/api/src/client/types/com/atproto/identity/resolveHandle.ts +++ b/packages/api/src/client/types/com/atproto/identity/resolveHandle.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.identity.resolveHandle' @@ -18,7 +18,6 @@ export type InputSchema = undefined export interface OutputSchema { did: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/identity/signPlcOperation.ts b/packages/api/src/client/types/com/atproto/identity/signPlcOperation.ts index 3847e611a56..65c611fff0f 100644 --- a/packages/api/src/client/types/com/atproto/identity/signPlcOperation.ts +++ b/packages/api/src/client/types/com/atproto/identity/signPlcOperation.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.identity.signPlcOperation' @@ -16,15 +16,13 @@ export interface InputSchema { token?: string rotationKeys?: string[] alsoKnownAs?: string[] - verificationMethods?: {} - services?: {} - [k: string]: unknown + verificationMethods?: { [_ in string]: unknown } + services?: { [_ in string]: unknown } } export interface OutputSchema { /** A signed DID PLC operation. */ - operation: {} - [k: string]: unknown + operation: { [_ in string]: unknown } } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/identity/submitPlcOperation.ts b/packages/api/src/client/types/com/atproto/identity/submitPlcOperation.ts index e1042ad1774..1d1f9f704d1 100644 --- a/packages/api/src/client/types/com/atproto/identity/submitPlcOperation.ts +++ b/packages/api/src/client/types/com/atproto/identity/submitPlcOperation.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.identity.submitPlcOperation' @@ -12,8 +12,7 @@ const id = 'com.atproto.identity.submitPlcOperation' export interface QueryParams {} export interface InputSchema { - operation: {} - [k: string]: unknown + operation: { [_ in string]: unknown } } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/identity/updateHandle.ts b/packages/api/src/client/types/com/atproto/identity/updateHandle.ts index 9ff1845c051..7487bac7f20 100644 --- a/packages/api/src/client/types/com/atproto/identity/updateHandle.ts +++ b/packages/api/src/client/types/com/atproto/identity/updateHandle.ts @@ -4,7 +4,7 @@ import { HeadersMap, XRPCError } from '@atproto/xrpc' import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.identity.updateHandle' @@ -14,7 +14,6 @@ export interface QueryParams {} export interface InputSchema { /** The new handle. */ handle: string - [k: string]: unknown } export interface CallOptions { diff --git a/packages/api/src/client/types/com/atproto/label/defs.ts b/packages/api/src/client/types/com/atproto/label/defs.ts index dfc59333ea6..822cffa234f 100644 --- a/packages/api/src/client/types/com/atproto/label/defs.ts +++ b/packages/api/src/client/types/com/atproto/label/defs.ts @@ -3,13 +3,14 @@ */ import { ValidationResult, BlobRef } from '@atproto/lexicon' import { CID } from 'multiformats/cid' -import { $Type, is$typed } from '../../../../util' +import { $Type, $Typed, is$typed, OmitKey } from '../../../../util' import { lexicons } from '../../../../lexicons' const id = 'com.atproto.label.defs' /** Metadata tag on an atproto resource (eg, repo or record). */ export interface Label { + $type?: $Type<'com.atproto.label.defs', 'label'> /** The AT Protocol version of the label object. */ ver?: number /** DID of the actor who created this label. */ @@ -28,12 +29,9 @@ export interface Label { exp?: string /** Signature of dag-cbor encoded label. */ sig?: Uint8Array - [k: string]: unknown } -export function isLabel( - v: unknown, -): v is Label & { $type: $Type<'com.atproto.label.defs', 'label'> } { +export function isLabel(v: V) { return is$typed(v, id, 'label') } @@ -41,15 +39,17 @@ export function validateLabel(v: unknown) { return lexicons.validate(`${id}#label`, v) as ValidationResult