@atproto/bsky 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/api/app/bsky/embed/getEmbedExternalView.d.ts.map +1 -1
  3. package/dist/api/app/bsky/embed/getEmbedExternalView.js +5 -1
  4. package/dist/api/app/bsky/embed/getEmbedExternalView.js.map +1 -1
  5. package/dist/api/app/bsky/notification/grouping/grouping.d.ts +4 -1
  6. package/dist/api/app/bsky/notification/grouping/grouping.d.ts.map +1 -1
  7. package/dist/api/app/bsky/notification/grouping/grouping.js +10 -1
  8. package/dist/api/app/bsky/notification/grouping/grouping.js.map +1 -1
  9. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -1
  10. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js +8 -3
  11. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -1
  12. package/dist/feature-gates/gates.d.ts +1 -0
  13. package/dist/feature-gates/gates.d.ts.map +1 -1
  14. package/dist/feature-gates/gates.js +1 -0
  15. package/dist/feature-gates/gates.js.map +1 -1
  16. package/dist/hydration/external.d.ts +31 -8
  17. package/dist/hydration/external.d.ts.map +1 -1
  18. package/dist/hydration/external.js +59 -22
  19. package/dist/hydration/external.js.map +1 -1
  20. package/dist/hydration/hydrator.d.ts +4 -0
  21. package/dist/hydration/hydrator.d.ts.map +1 -1
  22. package/dist/hydration/hydrator.js +36 -14
  23. package/dist/hydration/hydrator.js.map +1 -1
  24. package/dist/hydration/util.d.ts +1 -2
  25. package/dist/hydration/util.d.ts.map +1 -1
  26. package/dist/hydration/util.js +4 -5
  27. package/dist/hydration/util.js.map +1 -1
  28. package/dist/lexicons/social/grain/photo.defs.d.ts +11 -11
  29. package/dist/lexicons/social/grain/photo.defs.d.ts.map +1 -1
  30. package/dist/lexicons/social/grain/photo.defs.js +1 -1
  31. package/dist/lexicons/social/grain/photo.defs.js.map +1 -1
  32. package/dist/lexicons/tools/ozone/safelink/addRule.defs.d.ts +7 -1
  33. package/dist/lexicons/tools/ozone/safelink/addRule.defs.d.ts.map +1 -1
  34. package/dist/lexicons/tools/ozone/safelink/addRule.defs.js +2 -2
  35. package/dist/lexicons/tools/ozone/safelink/addRule.defs.js.map +1 -1
  36. package/dist/lexicons/tools/ozone/safelink/defs.defs.d.ts +10 -2
  37. package/dist/lexicons/tools/ozone/safelink/defs.defs.d.ts.map +1 -1
  38. package/dist/lexicons/tools/ozone/safelink/defs.defs.js +3 -3
  39. package/dist/lexicons/tools/ozone/safelink/defs.defs.js.map +1 -1
  40. package/dist/lexicons/tools/ozone/safelink/removeRule.defs.d.ts +6 -0
  41. package/dist/lexicons/tools/ozone/safelink/removeRule.defs.d.ts.map +1 -1
  42. package/dist/lexicons/tools/ozone/safelink/removeRule.defs.js +1 -1
  43. package/dist/lexicons/tools/ozone/safelink/removeRule.defs.js.map +1 -1
  44. package/dist/lexicons/tools/ozone/safelink/updateRule.defs.d.ts +7 -1
  45. package/dist/lexicons/tools/ozone/safelink/updateRule.defs.d.ts.map +1 -1
  46. package/dist/lexicons/tools/ozone/safelink/updateRule.defs.js +2 -2
  47. package/dist/lexicons/tools/ozone/safelink/updateRule.defs.js.map +1 -1
  48. package/dist/telemetry/events.d.ts +33 -4
  49. package/dist/telemetry/events.d.ts.map +1 -1
  50. package/dist/telemetry/events.js +49 -4
  51. package/dist/telemetry/events.js.map +1 -1
  52. package/dist/views/index.d.ts.map +1 -1
  53. package/dist/views/index.js +3 -6
  54. package/dist/views/index.js.map +1 -1
  55. package/package.json +8 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atproto/bsky
2
2
 
3
+ ## 0.3.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5688](https://github.com/bluesky-social/atproto/pull/5688) [`8f324e2`](https://github.com/bluesky-social/atproto/commit/8f324e2293f438bd436c4324efb43237dfcbc4fe) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Use saved, validated starter-pack attribution in follow notifications.
8
+
9
+ - [#5686](https://github.com/bluesky-social/atproto/pull/5686) [`58f14d6`](https://github.com/bluesky-social/atproto/commit/58f14d60273b1ccb7f187b882624c9e2045aed59) Thanks [@gcwill70](https://github.com/gcwill70)! - Add source-aware SafeLink rule synchronization and return rule sources in the API.
10
+
11
+ - [#5679](https://github.com/bluesky-social/atproto/pull/5679) [`3380a6c`](https://github.com/bluesky-social/atproto/commit/3380a6c2f80b127de8ac13e7202760c9532a1398) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Gate the Atmosphere Explore tab endpoint and return 403 when disabled.
12
+
13
+ - [#5680](https://github.com/bluesky-social/atproto/pull/5680) [`9f66a7e`](https://github.com/bluesky-social/atproto/commit/9f66a7e54b030700c03eda0bdbf35ffe756f4cd8) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add a `social.grain.photo` compatibility override that no longer requires `alt`, so the AppView renders Grain gallery photos that omit it. Ozone resolves the override; the PDS keeps validating writes against the published lexicon.
14
+ - Updated dependencies [[`58f14d6`](https://github.com/bluesky-social/atproto/commit/58f14d60273b1ccb7f187b882624c9e2045aed59)]:
15
+ - @atproto/api@0.28.1
16
+
17
+ ## 0.3.1
18
+
19
+ ### Patch Changes
20
+
21
+ - [#5677](https://github.com/bluesky-social/atproto/pull/5677) [`9354e5d`](https://github.com/bluesky-social/atproto/commit/9354e5d9b12ebf1add5ebccf36596031992abc1a) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Report external view hydration XRPC endpoint, duration, and root/dependency resolution.
22
+
23
+ - [#5677](https://github.com/bluesky-social/atproto/pull/5677) [`9354e5d`](https://github.com/bluesky-social/atproto/commit/9354e5d9b12ebf1add5ebccf36596031992abc1a) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Fetch generic external records in requests of at most 100 entries so large dependency passes, such as Atmosphere Explore galleries, no longer exceed the dataplane limit, and report external hydration traversals that recovered from dependency failures with a `partial` outcome.
24
+
3
25
  ## 0.3.0
4
26
 
5
27
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"getEmbedExternalView.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAuBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqBvD"}
1
+ {"version":3,"file":"getEmbedExternalView.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAuBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAyBvD"}
@@ -12,7 +12,11 @@ export default function (server, ctx) {
12
12
  handler: async ({ params, auth, req }) => {
13
13
  const viewer = auth.credentials.iss;
14
14
  const labelers = ctx.reqLabelers(req);
15
- const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer });
15
+ const hydrateCtx = await ctx.hydrator.createContext({
16
+ labelers,
17
+ viewer,
18
+ xrpcEndpoint: app.bsky.embed.getEmbedExternalView.$lxm,
19
+ });
16
20
  const result = await getEmbedExternalView({ ...params, hydrateCtx }, ctx);
17
21
  return {
18
22
  encoding: 'application/json',
@@ -1 +1 @@
1
- {"version":3,"file":"getEmbedExternalView.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AtUriString,\n type LexMap,\n type TypedLexMap,\n isDidString,\n} from '@atproto/lex'\nimport { parseAtUriString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { ServerConfig } from '../../../../config.js'\nimport type { AppContext } from '../../../../context.js'\nimport type { ExternalRecords } from '../../../../hydration/external.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app, com, site, social } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { isExternalRecordUrlSafe } from '../../../../util/external/index.js'\nimport {\n getStandardSiteRecordsForUri,\n validateStandardSiteRecordsForUrl,\n} from '../../../../util/external/standard-site.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getEmbedExternalView = createPipeline(\n skeleton,\n hydration,\n noRules,\n presentation,\n )\n server.add(app.bsky.embed.getEmbedExternalView, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ params, auth, req }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })\n const result = await getEmbedExternalView({ ...params, hydrateCtx }, ctx)\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n\nconst skeleton = async (\n inputs: SkeletonFnInput<Context, Params>,\n): Promise<Skeleton> => {\n return { canonicalUri: normalizeCanonicalUri(inputs.params) }\n}\n\nconst hydration = async (\n inputs: HydrationFnInput<Context, Params, Skeleton>,\n): Promise<HydrationState> => {\n const { ctx, params, skeleton } = inputs\n if (!skeleton.canonicalUri) return { ctx: params.hydrateCtx }\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(\n [skeleton.canonicalUri],\n params.hydrateCtx,\n )\n}\n\nconst presentation = (\n inputs: PresentationFnInput<Context, Params, Skeleton>,\n): Output => {\n const { ctx, params, skeleton, hydration } = inputs\n const { canonicalUri } = skeleton\n if (!canonicalUri) return {}\n\n if (!hydration.externalRecords?.get(canonicalUri)) return {}\n\n const { associatedRefs, associatedRecords } = collectAssociations(\n canonicalUri,\n hydration.externalRecords,\n )\n\n // Validate before building any view: trust must not depend on whether the\n // records happen to be renderable.\n const standardSite = isStandardSiteUri(canonicalUri)\n const standardSiteRecords = standardSite\n ? getStandardSiteRecordsForUri(canonicalUri, hydration.externalRecords)\n : undefined\n const verified = standardSiteRecords\n ? validateStandardSiteRecordsForUrl(\n standardSiteRecords.document,\n standardSiteRecords.publication,\n params.url,\n )\n : isExternalRecordUrlSafe(params.url, ctx.cfg.externalRecordsSafeDomains)\n\n if (!verified) {\n return {\n unverifiedAssociatedRefs: associatedRefs,\n unverifiedAssociatedRecords: associatedRecords,\n }\n }\n\n const data = ctx.views.externalRecordView(canonicalUri, hydration)\n\n let view: Output['view']\n if (standardSiteRecords) {\n const standardSiteView = ctx.views.externalEmbedFromStandardSiteRecords({\n document: standardSiteRecords.document,\n publication: standardSiteRecords.publication,\n state: hydration,\n assumedUrl: params.url,\n })\n if (standardSiteView) {\n view = app.bsky.embed.external.view.$build({\n external: {\n ...standardSiteView,\n uri: params.url,\n associatedRefs: associatedRefs,\n },\n })\n }\n }\n\n return {\n view,\n data,\n associatedRefs: associatedRefs,\n associatedRecords: associatedRecords,\n }\n}\n\ntype RecordUri = { uri: AtUriString; collection: string }\n\n/** A complete DID-based record URI: record key present, no fragment. */\nconst parseRecordUri = (value: string): RecordUri | undefined => {\n const parsed = parseAtUriString(value)\n if (\n parsed.success &&\n isDidString(parsed.value.authority) &&\n parsed.value.collection &&\n parsed.value.rkey &&\n !parsed.value.hash\n ) {\n return { uri: value as AtUriString, collection: parsed.value.collection }\n }\n return undefined\n}\n\nconst isStandardSiteUri = (uri: AtUriString): boolean => {\n const collection = parseRecordUri(uri)?.collection\n return (\n collection === site.standard.document.$type ||\n collection === site.standard.publication.$type\n )\n}\n\n/**\n * Reduce the request to one record URI. An explicit `canonicalUri` wins and\n * is never retried with `uris`. Otherwise the legacy `uris` input selects the\n * first Standard.site document, or else the first publication, regardless of\n * array order. No I/O: selection depends only on the supplied URIs.\n */\nconst normalizeCanonicalUri = (params: Params): AtUriString | undefined => {\n if (params.canonicalUri !== undefined) {\n return parseRecordUri(params.canonicalUri)?.uri\n }\n const candidates = (params.uris ?? []).flatMap((value) => {\n const parsed = parseRecordUri(value)\n return parsed ? [parsed] : []\n })\n const preferred =\n candidates.find((c) => c.collection === site.standard.document.$type) ??\n candidates.find((c) => c.collection === site.standard.publication.$type)\n return preferred?.uri\n}\n\n/**\n * Persistence policy, not render-time hydration: the canonical record (when\n * available, whatever its collection) plus only the additional collections\n * opted in here, so records baked into a post stay slim. Hydration still holds\n * the full state (e.g. recommendation, subscription and favorite records that\n * back the rendered `likers`/`subscribers` previews); those are deliberately\n * not persisted. An absent entry means no additional collections. Add a\n * collection here only when a post must carry it.\n */\nconst PERSISTED_DEPENDENCY_COLLECTIONS: ReadonlyMap<string, readonly string[]> =\n new Map<string, readonly string[]>([\n [site.standard.document.$type, [site.standard.publication.$type]],\n [\n social.grain.gallery.$type,\n [social.grain.gallery.item.$type, social.grain.photo.$type],\n ],\n ])\n\n/**\n * The canonical record, then the hydrated dependencies whose URI collection is\n * opted in by `PERSISTED_DEPENDENCY_COLLECTIONS` for the canonical collection.\n * Refs and associatedRecords are index-aligned, with CIDs taken from\n * hydration; unavailable records are excluded.\n */\nconst collectAssociations = (\n canonicalUri: AtUriString,\n records: ExternalRecords,\n): { associatedRefs: StrongRef[]; associatedRecords: LexMap[] } => {\n const associatedRefs: StrongRef[] = []\n const associatedRecords: LexMap[] = []\n const canonicalCollection = parseRecordUri(canonicalUri)?.collection\n const persisted =\n (canonicalCollection &&\n PERSISTED_DEPENDENCY_COLLECTIONS.get(canonicalCollection)) ||\n []\n const uris = [\n canonicalUri,\n ...[...records.keys()].filter((uri) => uri !== canonicalUri),\n ]\n for (const uri of uris) {\n const info = records.get(uri)\n if (!info) continue\n if (uri !== canonicalUri) {\n const collection = parseRecordUri(uri)?.collection\n if (!collection || !persisted.includes(collection)) continue\n }\n associatedRefs.push(\n com.atproto.repo.strongRef.$build({ uri, cid: info.cid }),\n )\n associatedRecords.push(info.record as TypedLexMap)\n }\n return { associatedRefs, associatedRecords }\n}\n\ntype StrongRef = com.atproto.repo.strongRef.Main\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n cfg: Pick<ServerConfig, 'externalRecordsSafeDomains'>\n}\n\ntype Params = app.bsky.embed.getEmbedExternalView.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype Skeleton = {\n canonicalUri?: AtUriString\n}\n\ntype Output = app.bsky.embed.getEmbedExternalView.$OutputBody\n"]}
1
+ {"version":3,"file":"getEmbedExternalView.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AtUriString,\n type LexMap,\n type TypedLexMap,\n isDidString,\n} from '@atproto/lex'\nimport { parseAtUriString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { ServerConfig } from '../../../../config.js'\nimport type { AppContext } from '../../../../context.js'\nimport type { ExternalRecords } from '../../../../hydration/external.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app, com, site, social } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { isExternalRecordUrlSafe } from '../../../../util/external/index.js'\nimport {\n getStandardSiteRecordsForUri,\n validateStandardSiteRecordsForUrl,\n} from '../../../../util/external/standard-site.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getEmbedExternalView = createPipeline(\n skeleton,\n hydration,\n noRules,\n presentation,\n )\n server.add(app.bsky.embed.getEmbedExternalView, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ params, auth, req }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n xrpcEndpoint: app.bsky.embed.getEmbedExternalView.$lxm,\n })\n const result = await getEmbedExternalView({ ...params, hydrateCtx }, ctx)\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n\nconst skeleton = async (\n inputs: SkeletonFnInput<Context, Params>,\n): Promise<Skeleton> => {\n return { canonicalUri: normalizeCanonicalUri(inputs.params) }\n}\n\nconst hydration = async (\n inputs: HydrationFnInput<Context, Params, Skeleton>,\n): Promise<HydrationState> => {\n const { ctx, params, skeleton } = inputs\n if (!skeleton.canonicalUri) return { ctx: params.hydrateCtx }\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(\n [skeleton.canonicalUri],\n params.hydrateCtx,\n )\n}\n\nconst presentation = (\n inputs: PresentationFnInput<Context, Params, Skeleton>,\n): Output => {\n const { ctx, params, skeleton, hydration } = inputs\n const { canonicalUri } = skeleton\n if (!canonicalUri) return {}\n\n if (!hydration.externalRecords?.get(canonicalUri)) return {}\n\n const { associatedRefs, associatedRecords } = collectAssociations(\n canonicalUri,\n hydration.externalRecords,\n )\n\n // Validate before building any view: trust must not depend on whether the\n // records happen to be renderable.\n const standardSite = isStandardSiteUri(canonicalUri)\n const standardSiteRecords = standardSite\n ? getStandardSiteRecordsForUri(canonicalUri, hydration.externalRecords)\n : undefined\n const verified = standardSiteRecords\n ? validateStandardSiteRecordsForUrl(\n standardSiteRecords.document,\n standardSiteRecords.publication,\n params.url,\n )\n : isExternalRecordUrlSafe(params.url, ctx.cfg.externalRecordsSafeDomains)\n\n if (!verified) {\n return {\n unverifiedAssociatedRefs: associatedRefs,\n unverifiedAssociatedRecords: associatedRecords,\n }\n }\n\n const data = ctx.views.externalRecordView(canonicalUri, hydration)\n\n let view: Output['view']\n if (standardSiteRecords) {\n const standardSiteView = ctx.views.externalEmbedFromStandardSiteRecords({\n document: standardSiteRecords.document,\n publication: standardSiteRecords.publication,\n state: hydration,\n assumedUrl: params.url,\n })\n if (standardSiteView) {\n view = app.bsky.embed.external.view.$build({\n external: {\n ...standardSiteView,\n uri: params.url,\n associatedRefs: associatedRefs,\n },\n })\n }\n }\n\n return {\n view,\n data,\n associatedRefs: associatedRefs,\n associatedRecords: associatedRecords,\n }\n}\n\ntype RecordUri = { uri: AtUriString; collection: string }\n\n/** A complete DID-based record URI: record key present, no fragment. */\nconst parseRecordUri = (value: string): RecordUri | undefined => {\n const parsed = parseAtUriString(value)\n if (\n parsed.success &&\n isDidString(parsed.value.authority) &&\n parsed.value.collection &&\n parsed.value.rkey &&\n !parsed.value.hash\n ) {\n return { uri: value as AtUriString, collection: parsed.value.collection }\n }\n return undefined\n}\n\nconst isStandardSiteUri = (uri: AtUriString): boolean => {\n const collection = parseRecordUri(uri)?.collection\n return (\n collection === site.standard.document.$type ||\n collection === site.standard.publication.$type\n )\n}\n\n/**\n * Reduce the request to one record URI. An explicit `canonicalUri` wins and\n * is never retried with `uris`. Otherwise the legacy `uris` input selects the\n * first Standard.site document, or else the first publication, regardless of\n * array order. No I/O: selection depends only on the supplied URIs.\n */\nconst normalizeCanonicalUri = (params: Params): AtUriString | undefined => {\n if (params.canonicalUri !== undefined) {\n return parseRecordUri(params.canonicalUri)?.uri\n }\n const candidates = (params.uris ?? []).flatMap((value) => {\n const parsed = parseRecordUri(value)\n return parsed ? [parsed] : []\n })\n const preferred =\n candidates.find((c) => c.collection === site.standard.document.$type) ??\n candidates.find((c) => c.collection === site.standard.publication.$type)\n return preferred?.uri\n}\n\n/**\n * Persistence policy, not render-time hydration: the canonical record (when\n * available, whatever its collection) plus only the additional collections\n * opted in here, so records baked into a post stay slim. Hydration still holds\n * the full state (e.g. recommendation, subscription and favorite records that\n * back the rendered `likers`/`subscribers` previews); those are deliberately\n * not persisted. An absent entry means no additional collections. Add a\n * collection here only when a post must carry it.\n */\nconst PERSISTED_DEPENDENCY_COLLECTIONS: ReadonlyMap<string, readonly string[]> =\n new Map<string, readonly string[]>([\n [site.standard.document.$type, [site.standard.publication.$type]],\n [\n social.grain.gallery.$type,\n [social.grain.gallery.item.$type, social.grain.photo.$type],\n ],\n ])\n\n/**\n * The canonical record, then the hydrated dependencies whose URI collection is\n * opted in by `PERSISTED_DEPENDENCY_COLLECTIONS` for the canonical collection.\n * Refs and associatedRecords are index-aligned, with CIDs taken from\n * hydration; unavailable records are excluded.\n */\nconst collectAssociations = (\n canonicalUri: AtUriString,\n records: ExternalRecords,\n): { associatedRefs: StrongRef[]; associatedRecords: LexMap[] } => {\n const associatedRefs: StrongRef[] = []\n const associatedRecords: LexMap[] = []\n const canonicalCollection = parseRecordUri(canonicalUri)?.collection\n const persisted =\n (canonicalCollection &&\n PERSISTED_DEPENDENCY_COLLECTIONS.get(canonicalCollection)) ||\n []\n const uris = [\n canonicalUri,\n ...[...records.keys()].filter((uri) => uri !== canonicalUri),\n ]\n for (const uri of uris) {\n const info = records.get(uri)\n if (!info) continue\n if (uri !== canonicalUri) {\n const collection = parseRecordUri(uri)?.collection\n if (!collection || !persisted.includes(collection)) continue\n }\n associatedRefs.push(\n com.atproto.repo.strongRef.$build({ uri, cid: info.cid }),\n )\n associatedRecords.push(info.record as TypedLexMap)\n }\n return { associatedRefs, associatedRecords }\n}\n\ntype StrongRef = com.atproto.repo.strongRef.Main\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n cfg: Pick<ServerConfig, 'externalRecordsSafeDomains'>\n}\n\ntype Params = app.bsky.embed.getEmbedExternalView.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype Skeleton = {\n canonicalUri?: AtUriString\n}\n\ntype Output = app.bsky.embed.getEmbedExternalView.$OutputBody\n"]}
@@ -11,7 +11,10 @@ export type RawNotification = {
11
11
  reason: DataplaneNotificationReasonWithSubject;
12
12
  reasonSubject: AtUriString;
13
13
  } | {
14
- reason: Exclude<DataplaneNotificationReason, DataplaneNotificationReasonWithSubject>;
14
+ reason: typeof NOTIFICATION_REASON.FOLLOW | typeof NOTIFICATION_REASON.FOLLOW_BACK;
15
+ reasonSubject: AtUriString | undefined;
16
+ } | {
17
+ reason: Exclude<DataplaneNotificationReason, DataplaneNotificationReasonWithSubject | typeof NOTIFICATION_REASON.FOLLOW | typeof NOTIFICATION_REASON.FOLLOW_BACK>;
15
18
  reasonSubject: undefined;
16
19
  });
17
20
  export type NotificationItem = {
@@ -1 +1 @@
1
- {"version":3,"file":"grouping.d.ts","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/grouping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC1E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AACxE,OAAO,EACL,KAAK,YAAY,EACjB,gBAAgB,EACjB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,mBAAmB,EACpB,MAAM,iBAAiB,CAAA;AAMxB,KAAK,sCAAsC,GACvC,OAAO,mBAAmB,CAAC,IAAI,GAC/B,OAAO,mBAAmB,CAAC,MAAM,GACjC,OAAO,mBAAmB,CAAC,eAAe,GAC1C,OAAO,mBAAmB,CAAC,iBAAiB,GAC5C,OAAO,mBAAmB,CAAC,KAAK,GAChC,OAAO,mBAAmB,CAAC,KAAK,GAChC,OAAO,mBAAmB,CAAC,kBAAkB,CAAA;AAKjD,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,SAAS,CAAA;IACvB,GAAG,EAAE,WAAW,CAAA;IAChB,SAAS,EAAE,cAAc,CAAA;CAC1B,GAAG,CACA;IACE,MAAM,EAAE,sCAAsC,CAAA;IAC9C,aAAa,EAAE,WAAW,CAAA;CAC3B,GACD;IACE,MAAM,EAAE,OAAO,CACb,2BAA2B,EAC3B,sCAAsC,CACvC,CAAA;IACD,aAAa,EAAE,SAAS,CAAA;CACzB,CACJ,CAAA;AAID,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,eAAe,CAAA;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAID,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,yBAAyB,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,SAAS,EAAE,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,cAAc,CAAA;IACzB,cAAc,EAAE,cAAc,CAAA;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,gBAAgB,EAAE,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,YAAY,GACzB,eAAe,GAAG,SAAS,CAsC7B;AAED,eAAO,MAAM,WAAW,UACf,gBAAgB,EAAE,SAClB,MAAM,OACR,MAAM,UACH,MAAM,GAAG,SAAS,aACf,wBAAwB,8BAElC,cAQF,CAAA"}
1
+ {"version":3,"file":"grouping.d.ts","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/grouping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC1E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AACxE,OAAO,EACL,KAAK,YAAY,EACjB,gBAAgB,EACjB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,mBAAmB,EACpB,MAAM,iBAAiB,CAAA;AAMxB,KAAK,sCAAsC,GACvC,OAAO,mBAAmB,CAAC,IAAI,GAC/B,OAAO,mBAAmB,CAAC,MAAM,GACjC,OAAO,mBAAmB,CAAC,eAAe,GAC1C,OAAO,mBAAmB,CAAC,iBAAiB,GAC5C,OAAO,mBAAmB,CAAC,KAAK,GAChC,OAAO,mBAAmB,CAAC,KAAK,GAChC,OAAO,mBAAmB,CAAC,kBAAkB,CAAA;AAKjD,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,SAAS,CAAA;IACvB,GAAG,EAAE,WAAW,CAAA;IAChB,SAAS,EAAE,cAAc,CAAA;CAC1B,GAAG,CACA;IAEE,MAAM,EAAE,sCAAsC,CAAA;IAC9C,aAAa,EAAE,WAAW,CAAA;CAC3B,GACD;IAEE,MAAM,EACF,OAAO,mBAAmB,CAAC,MAAM,GACjC,OAAO,mBAAmB,CAAC,WAAW,CAAA;IAC1C,aAAa,EAAE,WAAW,GAAG,SAAS,CAAA;CACvC,GACD;IAEE,MAAM,EAAE,OAAO,CACb,2BAA2B,EACzB,sCAAsC,GACtC,OAAO,mBAAmB,CAAC,MAAM,GACjC,OAAO,mBAAmB,CAAC,WAAW,CACzC,CAAA;IACD,aAAa,EAAE,SAAS,CAAA;CACzB,CACJ,CAAA;AAID,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,eAAe,CAAA;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAID,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,yBAAyB,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,SAAS,EAAE,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,cAAc,CAAA;IACzB,cAAc,EAAE,cAAc,CAAA;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,gBAAgB,EAAE,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,YAAY,GACzB,eAAe,GAAG,SAAS,CA+C7B;AAED,eAAO,MAAM,WAAW,UACf,gBAAgB,EAAE,SAClB,MAAM,OACR,MAAM,UACH,MAAM,GAAG,SAAS,aACf,wBAAwB,8BAElC,cAQF,CAAA"}
@@ -24,9 +24,18 @@ export function parseRawNotification(notification) {
24
24
  reasonSubject: reasonSubject,
25
25
  indexedAt,
26
26
  };
27
- case NOTIFICATION_REASON.CONTACT_MATCH:
28
27
  case NOTIFICATION_REASON.FOLLOW:
29
28
  case NOTIFICATION_REASON.FOLLOW_BACK:
29
+ return {
30
+ recipientDid: recipientDid,
31
+ uri: uri,
32
+ reason,
33
+ reasonSubject: reasonSubject
34
+ ? reasonSubject
35
+ : undefined,
36
+ indexedAt,
37
+ };
38
+ case NOTIFICATION_REASON.CONTACT_MATCH:
30
39
  case NOTIFICATION_REASON.MENTION:
31
40
  case NOTIFICATION_REASON.SUBSCRIBED_POST:
32
41
  case NOTIFICATION_REASON.UNVERIFIED:
@@ -1 +1 @@
1
- {"version":3,"file":"grouping.js","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/grouping.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,gBAAgB,GACjB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAGL,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AA8D3D,MAAM,UAAU,oBAAoB,CAClC,YAA0B;IAE1B,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,YAAY,CAAA;IAC5E,IAAI,CAAC,YAAY,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS;QAAE,OAAM;IAC/C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,EAA8B,CAAA;IAEhE,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,mBAAmB,CAAC,IAAI,CAAC;QAC9B,KAAK,mBAAmB,CAAC,MAAM,CAAC;QAChC,KAAK,mBAAmB,CAAC,eAAe,CAAC;QACzC,KAAK,mBAAmB,CAAC,iBAAiB,CAAC;QAC3C,KAAK,mBAAmB,CAAC,KAAK,CAAC;QAC/B,KAAK,mBAAmB,CAAC,KAAK,CAAC;QAC/B,KAAK,mBAAmB,CAAC,kBAAkB;YACzC,IAAI,CAAC,aAAa;gBAAE,OAAM;YAC1B,OAAO;gBACL,YAAY,EAAE,YAAyB;gBACvC,GAAG,EAAE,GAAkB;gBACvB,MAAM;gBACN,aAAa,EAAE,aAA4B;gBAC3C,SAAS;aACV,CAAA;QACH,KAAK,mBAAmB,CAAC,aAAa,CAAC;QACvC,KAAK,mBAAmB,CAAC,MAAM,CAAC;QAChC,KAAK,mBAAmB,CAAC,WAAW,CAAC;QACrC,KAAK,mBAAmB,CAAC,OAAO,CAAC;QACjC,KAAK,mBAAmB,CAAC,eAAe,CAAC;QACzC,KAAK,mBAAmB,CAAC,UAAU,CAAC;QACpC,KAAK,mBAAmB,CAAC,QAAQ;YAC/B,OAAO;gBACL,YAAY,EAAE,YAAyB;gBACvC,GAAG,EAAE,GAAkB;gBACvB,MAAM;gBACN,aAAa,EAAE,SAAS;gBACxB,SAAS;aACV,CAAA;QACH;YACE,OAAM;IACV,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAAyB,EACzB,KAAa,EACb,GAAW,EACX,MAA0B,EAC1B,SAAmC,EACnC,IAAI,GAAG,gBAAgB,CAAC,GAAG,EACX,EAAE;IAClB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,uBAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;QACjE,KAAK,aAAa,CAAC;QACnB;YACE,OAAO,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;IAClE,CAAC;AACH,CAAC,CAAA","sourcesContent":["import type { AtUriString, DatetimeString, DidString } from '@atproto/lex'\nimport type { NotificationsV2Algorithm } from '../../../../../config.js'\nimport {\n type Notification,\n NotificationFeed,\n} from '../../../../../proto/bsky_pb.js'\nimport {\n type AppviewNotificationReason,\n type DataplaneNotificationReason,\n NOTIFICATION_REASON,\n} from '../constants.js'\nimport { buildAlgoGravityGroups } from './algoGravity.js'\nimport { buildAlgoLookbackGroups } from './algoLookback.js'\n\n// Notification reasons that require reasonSubject to be non-empty.\n// It should be the AT-URI of a record belonging to the notification recipient\ntype DataplaneNotificationReasonWithSubject =\n | typeof NOTIFICATION_REASON.LIKE\n | typeof NOTIFICATION_REASON.REPOST\n | typeof NOTIFICATION_REASON.LIKE_VIA_REPOST\n | typeof NOTIFICATION_REASON.REPOST_VIA_REPOST\n | typeof NOTIFICATION_REASON.REPLY\n | typeof NOTIFICATION_REASON.QUOTE\n | typeof NOTIFICATION_REASON.STARTERPACK_JOINED\n\n// A dataplane notification that has passed our checks.\n// We just don't use the underlying proto type because\n// it has optional fields that we validate and ensure are present with this type.\nexport type RawNotification = {\n recipientDid: DidString\n uri: AtUriString\n indexedAt: DatetimeString\n} & (\n | {\n reason: DataplaneNotificationReasonWithSubject\n reasonSubject: AtUriString\n }\n | {\n reason: Exclude<\n DataplaneNotificationReason,\n DataplaneNotificationReasonWithSubject\n >\n reasonSubject: undefined\n }\n)\n\n// NotificationItem contains the raw notification plus computed data from it,\n// that will be used to calculate its grouping.\nexport type NotificationItem = {\n raw: RawNotification\n id: string\n groupingKey: string\n actorDid: DidString\n}\n\n// NotificationGroup is the grouping algorithm’s result: the notification items it grouped together,\n// plus their count and timestamps, before Views builds the lexicon response.\nexport type NotificationGroup = {\n id: string\n kind: AppviewNotificationReason\n groupingKey: string\n actorDids: DidString[]\n itemCount: number\n indexedAt: DatetimeString\n firstIndexedAt: DatetimeString\n isRead: boolean\n items: NotificationItem[]\n}\n\nexport type GroupingResult = {\n groups: NotificationGroup[]\n cursor?: string\n}\n\nexport function parseRawNotification(\n notification: Notification,\n): RawNotification | undefined {\n const { recipientDid, uri, reason, reasonSubject, timestamp } = notification\n if (!recipientDid || !uri || !timestamp) return\n const indexedAt = timestamp.toJson() as string as DatetimeString\n\n switch (reason) {\n case NOTIFICATION_REASON.LIKE:\n case NOTIFICATION_REASON.REPOST:\n case NOTIFICATION_REASON.LIKE_VIA_REPOST:\n case NOTIFICATION_REASON.REPOST_VIA_REPOST:\n case NOTIFICATION_REASON.REPLY:\n case NOTIFICATION_REASON.QUOTE:\n case NOTIFICATION_REASON.STARTERPACK_JOINED:\n if (!reasonSubject) return\n return {\n recipientDid: recipientDid as DidString,\n uri: uri as AtUriString,\n reason,\n reasonSubject: reasonSubject as AtUriString,\n indexedAt,\n }\n case NOTIFICATION_REASON.CONTACT_MATCH:\n case NOTIFICATION_REASON.FOLLOW:\n case NOTIFICATION_REASON.FOLLOW_BACK:\n case NOTIFICATION_REASON.MENTION:\n case NOTIFICATION_REASON.SUBSCRIBED_POST:\n case NOTIFICATION_REASON.UNVERIFIED:\n case NOTIFICATION_REASON.VERIFIED:\n return {\n recipientDid: recipientDid as DidString,\n uri: uri as AtUriString,\n reason,\n reasonSubject: undefined,\n indexedAt,\n }\n default:\n return\n }\n}\n\nexport const buildGroups = (\n items: NotificationItem[],\n limit: number,\n now: number,\n seenAt: number | undefined,\n algorithm: NotificationsV2Algorithm,\n feed = NotificationFeed.ALL,\n): GroupingResult => {\n switch (algorithm) {\n case 'algoLookback':\n return buildAlgoLookbackGroups(items, limit, now, seenAt, feed)\n case 'algoGravity':\n default:\n return buildAlgoGravityGroups(items, limit, now, seenAt, feed)\n }\n}\n"]}
1
+ {"version":3,"file":"grouping.js","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/grouping.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,gBAAgB,GACjB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAGL,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAyE3D,MAAM,UAAU,oBAAoB,CAClC,YAA0B;IAE1B,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,YAAY,CAAA;IAC5E,IAAI,CAAC,YAAY,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS;QAAE,OAAM;IAC/C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,EAA8B,CAAA;IAEhE,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,mBAAmB,CAAC,IAAI,CAAC;QAC9B,KAAK,mBAAmB,CAAC,MAAM,CAAC;QAChC,KAAK,mBAAmB,CAAC,eAAe,CAAC;QACzC,KAAK,mBAAmB,CAAC,iBAAiB,CAAC;QAC3C,KAAK,mBAAmB,CAAC,KAAK,CAAC;QAC/B,KAAK,mBAAmB,CAAC,KAAK,CAAC;QAC/B,KAAK,mBAAmB,CAAC,kBAAkB;YACzC,IAAI,CAAC,aAAa;gBAAE,OAAM;YAC1B,OAAO;gBACL,YAAY,EAAE,YAAyB;gBACvC,GAAG,EAAE,GAAkB;gBACvB,MAAM;gBACN,aAAa,EAAE,aAA4B;gBAC3C,SAAS;aACV,CAAA;QACH,KAAK,mBAAmB,CAAC,MAAM,CAAC;QAChC,KAAK,mBAAmB,CAAC,WAAW;YAClC,OAAO;gBACL,YAAY,EAAE,YAAyB;gBACvC,GAAG,EAAE,GAAkB;gBACvB,MAAM;gBACN,aAAa,EAAE,aAAa;oBAC1B,CAAC,CAAE,aAA6B;oBAChC,CAAC,CAAC,SAAS;gBACb,SAAS;aACV,CAAA;QACH,KAAK,mBAAmB,CAAC,aAAa,CAAC;QACvC,KAAK,mBAAmB,CAAC,OAAO,CAAC;QACjC,KAAK,mBAAmB,CAAC,eAAe,CAAC;QACzC,KAAK,mBAAmB,CAAC,UAAU,CAAC;QACpC,KAAK,mBAAmB,CAAC,QAAQ;YAC/B,OAAO;gBACL,YAAY,EAAE,YAAyB;gBACvC,GAAG,EAAE,GAAkB;gBACvB,MAAM;gBACN,aAAa,EAAE,SAAS;gBACxB,SAAS;aACV,CAAA;QACH;YACE,OAAM;IACV,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAAyB,EACzB,KAAa,EACb,GAAW,EACX,MAA0B,EAC1B,SAAmC,EACnC,IAAI,GAAG,gBAAgB,CAAC,GAAG,EACX,EAAE;IAClB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,uBAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;QACjE,KAAK,aAAa,CAAC;QACnB;YACE,OAAO,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;IAClE,CAAC;AACH,CAAC,CAAA","sourcesContent":["import type { AtUriString, DatetimeString, DidString } from '@atproto/lex'\nimport type { NotificationsV2Algorithm } from '../../../../../config.js'\nimport {\n type Notification,\n NotificationFeed,\n} from '../../../../../proto/bsky_pb.js'\nimport {\n type AppviewNotificationReason,\n type DataplaneNotificationReason,\n NOTIFICATION_REASON,\n} from '../constants.js'\nimport { buildAlgoGravityGroups } from './algoGravity.js'\nimport { buildAlgoLookbackGroups } from './algoLookback.js'\n\n// Notification reasons that require reasonSubject to be non-empty.\n// It should be the AT-URI of a record belonging to the notification recipient\ntype DataplaneNotificationReasonWithSubject =\n | typeof NOTIFICATION_REASON.LIKE\n | typeof NOTIFICATION_REASON.REPOST\n | typeof NOTIFICATION_REASON.LIKE_VIA_REPOST\n | typeof NOTIFICATION_REASON.REPOST_VIA_REPOST\n | typeof NOTIFICATION_REASON.REPLY\n | typeof NOTIFICATION_REASON.QUOTE\n | typeof NOTIFICATION_REASON.STARTERPACK_JOINED\n\n// A dataplane notification that has passed our checks.\n// We just don't use the underlying proto type because\n// it has optional fields that we validate and ensure are present with this type.\nexport type RawNotification = {\n recipientDid: DidString\n uri: AtUriString\n indexedAt: DatetimeString\n} & (\n | {\n // Cases that always have a reason subject.\n reason: DataplaneNotificationReasonWithSubject\n reasonSubject: AtUriString\n }\n | {\n // Cases that have optional reason subject.\n reason:\n | typeof NOTIFICATION_REASON.FOLLOW\n | typeof NOTIFICATION_REASON.FOLLOW_BACK\n reasonSubject: AtUriString | undefined\n }\n | {\n // Cases that never have a reason subject.\n reason: Exclude<\n DataplaneNotificationReason,\n | DataplaneNotificationReasonWithSubject\n | typeof NOTIFICATION_REASON.FOLLOW\n | typeof NOTIFICATION_REASON.FOLLOW_BACK\n >\n reasonSubject: undefined\n }\n)\n\n// NotificationItem contains the raw notification plus computed data from it,\n// that will be used to calculate its grouping.\nexport type NotificationItem = {\n raw: RawNotification\n id: string\n groupingKey: string\n actorDid: DidString\n}\n\n// NotificationGroup is the grouping algorithm’s result: the notification items it grouped together,\n// plus their count and timestamps, before Views builds the lexicon response.\nexport type NotificationGroup = {\n id: string\n kind: AppviewNotificationReason\n groupingKey: string\n actorDids: DidString[]\n itemCount: number\n indexedAt: DatetimeString\n firstIndexedAt: DatetimeString\n isRead: boolean\n items: NotificationItem[]\n}\n\nexport type GroupingResult = {\n groups: NotificationGroup[]\n cursor?: string\n}\n\nexport function parseRawNotification(\n notification: Notification,\n): RawNotification | undefined {\n const { recipientDid, uri, reason, reasonSubject, timestamp } = notification\n if (!recipientDid || !uri || !timestamp) return\n const indexedAt = timestamp.toJson() as string as DatetimeString\n\n switch (reason) {\n case NOTIFICATION_REASON.LIKE:\n case NOTIFICATION_REASON.REPOST:\n case NOTIFICATION_REASON.LIKE_VIA_REPOST:\n case NOTIFICATION_REASON.REPOST_VIA_REPOST:\n case NOTIFICATION_REASON.REPLY:\n case NOTIFICATION_REASON.QUOTE:\n case NOTIFICATION_REASON.STARTERPACK_JOINED:\n if (!reasonSubject) return\n return {\n recipientDid: recipientDid as DidString,\n uri: uri as AtUriString,\n reason,\n reasonSubject: reasonSubject as AtUriString,\n indexedAt,\n }\n case NOTIFICATION_REASON.FOLLOW:\n case NOTIFICATION_REASON.FOLLOW_BACK:\n return {\n recipientDid: recipientDid as DidString,\n uri: uri as AtUriString,\n reason,\n reasonSubject: reasonSubject\n ? (reasonSubject as AtUriString)\n : undefined,\n indexedAt,\n }\n case NOTIFICATION_REASON.CONTACT_MATCH:\n case NOTIFICATION_REASON.MENTION:\n case NOTIFICATION_REASON.SUBSCRIBED_POST:\n case NOTIFICATION_REASON.UNVERIFIED:\n case NOTIFICATION_REASON.VERIFIED:\n return {\n recipientDid: recipientDid as DidString,\n uri: uri as AtUriString,\n reason,\n reasonSubject: undefined,\n indexedAt,\n }\n default:\n return\n }\n}\n\nexport const buildGroups = (\n items: NotificationItem[],\n limit: number,\n now: number,\n seenAt: number | undefined,\n algorithm: NotificationsV2Algorithm,\n feed = NotificationFeed.ALL,\n): GroupingResult => {\n switch (algorithm) {\n case 'algoLookback':\n return buildAlgoLookbackGroups(items, limit, now, seenAt, feed)\n case 'algoGravity':\n default:\n return buildAlgoGravityGroups(items, limit, now, seenAt, feed)\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getAtmosphereExploreTab.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AA8CxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA0DvD"}
1
+ {"version":3,"file":"getAtmosphereExploreTab.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AA8CxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA+DvD"}
@@ -1,7 +1,7 @@
1
1
  import { mapDefined, noUndefinedVals } from '@atproto/common';
2
2
  import { l } from '@atproto/lex';
3
3
  import { AtUri } from '@atproto/syntax';
4
- import { MethodNotImplementedError, UpstreamFailureError, UpstreamTimeoutError, } from '@atproto/xrpc-server';
4
+ import { ForbiddenError, MethodNotImplementedError, UpstreamFailureError, UpstreamTimeoutError, } from '@atproto/xrpc-server';
5
5
  import { safeFetchWrap } from '@atproto-labs/fetch-node';
6
6
  import { app, site } from '../../../../lexicons/index.js';
7
7
  import { httpLogger } from '../../../../logger.js';
@@ -39,6 +39,11 @@ export default function (server, ctx) {
39
39
  server.add(app.bsky.unspecced.getAtmosphereExploreTab, {
40
40
  auth: ctx.authVerifier.standardOptional,
41
41
  handler: async ({ auth, params, req, signal }) => {
42
+ const viewer = auth.credentials.iss;
43
+ const features = ctx.featureGatesClient.scope(ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }));
44
+ if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {
45
+ throw new ForbiddenError('Atmosphere Explore is not enabled');
46
+ }
42
47
  const service = ctx.cfg.atmosphereExploreTabService;
43
48
  if (!service)
44
49
  throw new MethodNotImplementedError('Atmosphere Explore service not configured');
@@ -50,11 +55,11 @@ export default function (server, ctx) {
50
55
  catch {
51
56
  throw new UpstreamFailureError('Unable to fetch Atmosphere Explore configuration');
52
57
  }
53
- const viewer = auth.credentials.iss;
54
58
  const hydrateCtx = await ctx.hydrator.createContext({
55
59
  viewer,
56
60
  labelers: ctx.reqLabelers(req),
57
- features: ctx.featureGatesClient.scope(ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req })),
61
+ features,
62
+ xrpcEndpoint: app.bsky.unspecced.getAtmosphereExploreTab.$lxm,
58
63
  });
59
64
  const useIris = hydrateCtx.features.checkGate(hydrateCtx.features.Gate.AtmosphereExploreArticlesIrisEnable);
60
65
  // Use 501 instead of 500 as these are not considered retry-able by clients
@@ -1 +1 @@
1
- {"version":3,"file":"getAtmosphereExploreTab.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingHttpHeaders } from 'node:http'\nimport { mapDefined, noUndefinedVals } from '@atproto/common'\nimport { type AtUriString, type Client, l } from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport {\n MethodNotImplementedError,\n type Server,\n UpstreamFailureError,\n UpstreamTimeoutError,\n} from '@atproto/xrpc-server'\nimport { safeFetchWrap } from '@atproto-labs/fetch-node'\nimport type { AppContext } from '../../../../context.js'\nimport type { HydrateCtx, Hydrator } from '../../../../hydration/hydrator.js'\nimport { app, site } from '../../../../lexicons/index.js'\nimport { httpLogger } from '../../../../logger.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nconst MAX_CONFIG_BYTES = 512 * 1024\nconst MAX_LIST_ITEMS = 10\n\nconst groupSchema = l.object({\n canonicalUri: l.optional(l.unknown()),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n tags: l.optional(l.array(l.string({ maxLength: 128 }), { maxLength: 100 })),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\nconst bannerSchema = l.object({\n status: l.string({ maxLength: 128 }),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\n// Ingestion is best-effort per item: each section is an unvalidated value, and\n// every entry is validated on its own so one bad entry never fails the rest.\nconst configSchema = l.object({\n announcementBanners: l.optional(l.unknown()),\n articles: l.optional(l.unknown()),\n publications: l.optional(l.unknown()),\n photos: l.optional(l.unknown()),\n livestreams: l.optional(l.unknown()),\n apps: l.optional(l.unknown()),\n})\nconst MAX_INPUT_ENTRIES = 100\n\nexport default function (server: Server, ctx: AppContext) {\n const getExplore = createPipeline(skeleton, hydration, noRules, presentation)\n const fetchExploreTabSkeleton = createFetchExploreTabSkeleton()\n server.add(app.bsky.unspecced.getAtmosphereExploreTab, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const service = ctx.cfg.atmosphereExploreTabService\n if (!service)\n throw new MethodNotImplementedError(\n 'Atmosphere Explore service not configured',\n )\n let workerUrl: URL\n try {\n // Location filtering happens locally, so fetch the full config as-is.\n workerUrl = new URL(service)\n } catch {\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n const viewer = auth.credentials.iss\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers: ctx.reqLabelers(req),\n features: ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n ),\n })\n const useIris = hydrateCtx.features.checkGate(\n hydrateCtx.features.Gate.AtmosphereExploreArticlesIrisEnable,\n )\n // Use 501 instead of 500 as these are not considered retry-able by clients\n if (useIris && !ctx.irisClient)\n throw new MethodNotImplementedError('Iris agent not available')\n const result = await getExplore(\n {\n ...params,\n hydrateCtx,\n workerUrl,\n requestHeaders: req.headers,\n headers: noUndefinedVals({\n 'accept-language': params.langs?.length\n ? params.langs.join(',')\n : req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n }),\n signal,\n },\n {\n hydrator: ctx.hydrator,\n views: ctx.views,\n irisClient: useIris ? ctx.irisClient : undefined,\n fetchExploreTabSkeleton,\n },\n )\n return { encoding: 'application/json', body: result }\n },\n })\n}\n\nasync function skeleton({\n ctx,\n params,\n}: SkeletonFnInput<Context, Params>): Promise<SkeletonState> {\n const [config, recommendations] = await Promise.all([\n ctx.fetchExploreTabSkeleton(params.workerUrl, params.signal),\n ctx.irisClient\n ? ctx.irisClient\n .call(\n app.bsky.unspecced.getAtmosphereExploreArticleRecommendations,\n {\n limit: MAX_LIST_ITEMS,\n viewer: params.hydrateCtx.viewer ?? undefined,\n },\n {\n headers: params.headers,\n signal: params.signal,\n validateResponse: true,\n },\n )\n // @NOTE Request and response-validation failures use curated articles.\n .catch((err) => {\n if (params.signal.aborted) throw err\n httpLogger.warn(\n { err },\n 'Unable to fetch Atmosphere Explore article recommendations',\n )\n return undefined\n })\n : undefined,\n ])\n const now = Date.now()\n const announcementBanner = selectBanner(\n config.announcementBanners,\n params,\n now,\n )\n const select = createSelector(params, params.requestHeaders, now)\n return {\n now,\n announcementBanner,\n articles: orderGroups(\n recommendations && recommendations.articles.length > 0\n ? mapDefined(\n recommendations.articles.slice(0, MAX_LIST_ITEMS),\n ({ uri }) => {\n const root = recordUri(uri)\n return root &&\n new AtUri(root).collection === site.standard.document.$type\n ? { featured: false, uri: root }\n : undefined\n },\n )\n : completeGroups(select(config.articles)),\n ),\n articleRecIdStr: recommendations?.recIdStr,\n publications: orderGroups(completeGroups(select(config.publications))),\n photos: orderGroups(completeGroups(select(config.photos))),\n livestreams: orderGroups(completeGroups(select(config.livestreams))),\n // Apps have no hydration root or featured flag: keep worker order.\n apps: config.apps.slice(0, MAX_LIST_ITEMS),\n }\n}\n\nasync function hydration({\n ctx,\n params,\n skeleton,\n}: HydrationFnInput<Context, Params, SkeletonState>) {\n const uris = [\n ...new Set(\n [\n ...skeleton.articles,\n ...skeleton.publications,\n ...skeleton.photos,\n ...skeleton.livestreams,\n ].map(({ uri }) => uri),\n ),\n ]\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx)\n}\n\nfunction presentation({\n ctx,\n skeleton,\n hydration: state,\n}: PresentationFnInput<\n Context,\n Params,\n SkeletonState\n>): app.bsky.unspecced.getAtmosphereExploreTab.$OutputBody {\n const { now, articles, publications, photos, livestreams } = skeleton\n return {\n announcementBanner: skeleton.announcementBanner,\n articles: {\n items: mapDefined(articles, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n recIdStr: skeleton.articleRecIdStr,\n },\n publications: {\n items: mapDefined(publications, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n },\n photos: {\n items: mapDefined(photos, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewGallery.$isTypeOf(view) &&\n view.items.length\n ? { featured, view }\n : undefined\n }),\n },\n livestreams: {\n items: mapDefined(livestreams, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewLivestream.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n },\n apps: skeleton.apps,\n }\n}\n\nfunction createFetchExploreTabSkeleton() {\n // @NOTE The wrapper caps decoded response bytes, including compressed bodies.\n const fetchConfig = safeFetchWrap({\n timeout: 5000,\n responseMaxSize: MAX_CONFIG_BYTES,\n })\n\n return async function fetchExploreTabSkeleton(\n url: URL,\n signal: AbortSignal,\n ): Promise<ExploreConfig> {\n try {\n const response = await fetchConfig(url, {\n headers: { accept: 'application/json' },\n redirect: 'error',\n signal,\n })\n if (!response.ok) {\n await response.body?.cancel()\n throw new UpstreamFailureError(\n `Atmosphere Explore service returned HTTP ${response.status}`,\n )\n }\n return normalizeConfig(await response.json())\n } catch (err) {\n if (err instanceof UpstreamFailureError) throw err\n if (\n err instanceof Error &&\n (err.name === 'TimeoutError' || err.name === 'AbortError')\n ) {\n throw new UpstreamTimeoutError('Atmosphere Explore request timed out')\n }\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n }\n}\n\ntype Group = l.Infer<typeof groupSchema>\ntype AppCard = app.bsky.unspecced.getAtmosphereExploreTab.AppCard\ntype ValidBanner = {\n banner: l.Infer<typeof bannerSchema>\n presentation: AnnouncementBanner\n}\ntype ExploreConfig = {\n announcementBanners: ValidBanner[]\n articles: Group[]\n publications: Group[]\n photos: Group[]\n livestreams: Group[]\n apps: AppCard[]\n}\n\n/**\n * Keeps only the well-formed items of a successfully decoded worker body. Only\n * the first `MAX_INPUT_ENTRIES` entries of each section are examined, and\n * malformed entries are skipped whole, never coerced or repaired.\n */\nfunction normalizeConfig(body: unknown): ExploreConfig {\n const raw = configSchema.safeParse(body)\n const config = raw.success ? raw.value : {}\n const entries = (value: unknown): unknown[] =>\n Array.isArray(value) ? value.slice(0, MAX_INPUT_ENTRIES) : []\n const valid = <T>(schema: l.Schema<T>, value: unknown): T[] =>\n entries(value).flatMap((entry) => {\n const result = schema.safeParse(entry)\n return result.success ? [result.value] : []\n })\n return {\n announcementBanners: entries(config.announcementBanners).flatMap(\n (entry) => {\n const banner = bannerSchema.safeParse(entry)\n const presentation =\n app.bsky.unspecced.getAtmosphereExploreTab.announcementBanner.$safeValidate(\n entry,\n )\n return banner.success && presentation.success\n ? [{ banner: banner.value, presentation: presentation.value }]\n : []\n },\n ),\n articles: valid(groupSchema, config.articles),\n publications: valid(groupSchema, config.publications),\n photos: valid(groupSchema, config.photos),\n livestreams: valid(groupSchema, config.livestreams),\n apps: valid(\n app.bsky.unspecced.getAtmosphereExploreTab.appCard,\n config.apps,\n ),\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n irisClient: Client | undefined\n fetchExploreTabSkeleton: ReturnType<typeof createFetchExploreTabSkeleton>\n}\n\ntype Params = app.bsky.unspecced.getAtmosphereExploreTab.$Params & {\n hydrateCtx: HydrateCtx\n workerUrl: URL\n /** Recommendation headers for Iris only. */\n headers: Record<string, string>\n requestHeaders: IncomingHttpHeaders\n signal: AbortSignal\n}\n\ntype RecordGroup = { featured: boolean; uri: AtUriString }\n\ntype SkeletonState = {\n now: number\n announcementBanner?: app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n articles: RecordGroup[]\n articleRecIdStr?: string\n publications: RecordGroup[]\n photos: RecordGroup[]\n livestreams: RecordGroup[]\n apps: app.bsky.unspecced.getAtmosphereExploreTab.AppCard[]\n}\n\n/**\n * Randomizes a selected list (Fisher-Yates), then places featured items ahead\n * of ordinary ones. Runs before hydration, so presentation keeps this order.\n */\nfunction orderGroups(groups: RecordGroup[]): RecordGroup[] {\n const shuffled = [...groups]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n return [\n ...shuffled.filter(({ featured }) => featured),\n ...shuffled.filter(({ featured }) => !featured),\n ].slice(0, MAX_LIST_ITEMS)\n}\n\nfunction completeGroups(groups: SelectedGroup[] = []): RecordGroup[] {\n return mapDefined(groups, ({ featured, canonicalUri }) => {\n const uri = recordUri(canonicalUri)\n if (!uri) return\n return { featured, uri }\n })\n}\n\ntype AnnouncementBanner =\n app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n\n/**\n * Returns the single (already validated) banner to show. Eligible (enabled, in window) banners targeted at a matching location\n * win, first in worker order; otherwise the unrestricted eligible banner with\n * the furthest-out `endsAt` (absent means no expiration; ties keep worker order).\n */\nfunction selectBanner(\n banners: ValidBanner[],\n params: { countryCode?: string; regionCode?: string },\n now: number,\n): AnnouncementBanner | undefined {\n let matched: AnnouncementBanner | undefined\n let fallback: AnnouncementBanner | undefined\n let fallbackEnd = -Infinity\n for (const { banner, presentation } of banners) {\n const { status, startsAt, endsAt, locations } = banner\n if (status !== 'enabled') continue\n if (startsAt != null && Date.parse(startsAt) > now) continue\n const end = endsAt == null ? Infinity : Date.parse(endsAt)\n if (end <= now) continue\n if (locations?.length) {\n if (!matched && matchesLocation(locations, params)) {\n matched = presentation\n }\n } else if (end > fallbackEnd) {\n fallback = presentation\n fallbackEnd = end\n }\n }\n const selected = matched ?? fallback\n if (!selected) return\n const { id, title, description, url, image, overlayColor, textColor } =\n selected\n return { id, title, description, url, image, overlayColor, textColor }\n}\n\ntype Selectable = {\n tags?: string[]\n locations?: string[]\n startsAt?: string | null\n endsAt?: string | null\n}\ntype SelectedGroup = l.Infer<typeof groupSchema> & { featured: boolean }\n\n/**\n * Builds a per-list selector that drops location-ineligible entries, orders\n * the rest by priority bucket (location, requested topic, `general`, untagged;\n * stable within a bucket), moves entries with an active schedule window ahead\n * of the rest (stable, so bucket order holds within each side), and keeps the\n * first `MAX_LIST_ITEMS`. A worker-supplied `featured` flag is ignored.\n */\nfunction createSelector(\n params: { countryCode?: string; regionCode?: string },\n headers: IncomingHttpHeaders,\n now: number,\n) {\n const topics = parseTopics(headers['x-atproto-bsky-topics'])\n return (groups: l.Infer<typeof groupSchema>[] = []): SelectedGroup[] => {\n const buckets: l.Infer<typeof groupSchema>[][] = [[], [], [], []]\n for (const group of groups) {\n const bucket = selectionBucket(group, params, topics)\n if (bucket !== undefined) buckets[bucket].push(group)\n }\n const featured: SelectedGroup[] = []\n const ordinary: SelectedGroup[] = []\n for (const group of buckets.flat()) {\n const active = isFeatured(group, now)\n ;(active ? featured : ordinary).push({ ...group, featured: active })\n }\n return [...featured, ...ordinary].slice(0, MAX_LIST_ITEMS)\n }\n}\n\n/** Unlike banners, content needs both bounds to define a featured window. */\nfunction isFeatured({ startsAt, endsAt }: Selectable, now: number): boolean {\n return (\n startsAt != null &&\n endsAt != null &&\n Date.parse(startsAt) <= now &&\n now < Date.parse(endsAt)\n )\n}\n\nfunction selectionBucket(\n { tags, locations }: Selectable,\n params: { countryCode?: string; regionCode?: string },\n topics: Set<string>,\n): 0 | 1 | 2 | 3 | undefined {\n if (locations?.length) {\n if (!matchesLocation(locations, params)) return\n return 0\n }\n if (tags?.some((tag) => topics.has(tag))) return 1\n if (tags?.includes('general')) return 2\n if (!tags?.length) return 3\n}\n\nfunction parseTopics(header: string | string[] | undefined): Set<string> {\n const topics = new Set<string>()\n const value = Array.isArray(header) ? header.join(',') : header\n for (const token of value?.split(',') ?? []) {\n const topic = token.trim()\n if (topic) topics.add(topic)\n }\n return topics\n}\n\n/**\n * True only when a nonempty `locations` list matches the request. Entries are\n * a country (`JP`) or country and region (`US-WI`); a region alone never\n * matches. A full `regionCode` (`US-WI`) is honored only when its country\n * agrees with `countryCode`.\n */\nfunction matchesLocation(\n locations: string[] | undefined,\n { countryCode, regionCode }: { countryCode?: string; regionCode?: string },\n): boolean {\n const country = countryCode?.toUpperCase()\n if (!locations?.length || !country) return false\n let region = regionCode?.toUpperCase() || undefined\n if (region?.includes('-')) {\n const [regionCountry, subdivision] = region.split('-')\n region = regionCountry === country ? subdivision : undefined\n }\n return locations.some((location) => {\n const [locationCountry, locationRegion, ...rest] = location\n .toUpperCase()\n .split('-')\n if (rest.length || locationCountry !== country) return false\n return (\n locationRegion === undefined || (!!region && locationRegion === region)\n )\n })\n}\n\nfunction recordUri(value: unknown): AtUriString | undefined {\n if (!l.isAtUriString(value)) return\n const uri = new AtUri(value)\n return uri.collection && uri.rkey && l.isDidString(uri.hostname)\n ? value\n : undefined\n}\n"]}
1
+ {"version":3,"file":"getAtmosphereExploreTab.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingHttpHeaders } from 'node:http'\nimport { mapDefined, noUndefinedVals } from '@atproto/common'\nimport { type AtUriString, type Client, l } from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport {\n ForbiddenError,\n MethodNotImplementedError,\n type Server,\n UpstreamFailureError,\n UpstreamTimeoutError,\n} from '@atproto/xrpc-server'\nimport { safeFetchWrap } from '@atproto-labs/fetch-node'\nimport type { AppContext } from '../../../../context.js'\nimport type { HydrateCtx, Hydrator } from '../../../../hydration/hydrator.js'\nimport { app, site } from '../../../../lexicons/index.js'\nimport { httpLogger } from '../../../../logger.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nconst MAX_CONFIG_BYTES = 512 * 1024\nconst MAX_LIST_ITEMS = 10\n\nconst groupSchema = l.object({\n canonicalUri: l.optional(l.unknown()),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n tags: l.optional(l.array(l.string({ maxLength: 128 }), { maxLength: 100 })),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\nconst bannerSchema = l.object({\n status: l.string({ maxLength: 128 }),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\n// Ingestion is best-effort per item: each section is an unvalidated value, and\n// every entry is validated on its own so one bad entry never fails the rest.\nconst configSchema = l.object({\n announcementBanners: l.optional(l.unknown()),\n articles: l.optional(l.unknown()),\n publications: l.optional(l.unknown()),\n photos: l.optional(l.unknown()),\n livestreams: l.optional(l.unknown()),\n apps: l.optional(l.unknown()),\n})\nconst MAX_INPUT_ENTRIES = 100\n\nexport default function (server: Server, ctx: AppContext) {\n const getExplore = createPipeline(skeleton, hydration, noRules, presentation)\n const fetchExploreTabSkeleton = createFetchExploreTabSkeleton()\n server.add(app.bsky.unspecced.getAtmosphereExploreTab, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const features = ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n )\n if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {\n throw new ForbiddenError('Atmosphere Explore is not enabled')\n }\n const service = ctx.cfg.atmosphereExploreTabService\n if (!service)\n throw new MethodNotImplementedError(\n 'Atmosphere Explore service not configured',\n )\n let workerUrl: URL\n try {\n // Location filtering happens locally, so fetch the full config as-is.\n workerUrl = new URL(service)\n } catch {\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers: ctx.reqLabelers(req),\n features,\n xrpcEndpoint: app.bsky.unspecced.getAtmosphereExploreTab.$lxm,\n })\n const useIris = hydrateCtx.features.checkGate(\n hydrateCtx.features.Gate.AtmosphereExploreArticlesIrisEnable,\n )\n // Use 501 instead of 500 as these are not considered retry-able by clients\n if (useIris && !ctx.irisClient)\n throw new MethodNotImplementedError('Iris agent not available')\n const result = await getExplore(\n {\n ...params,\n hydrateCtx,\n workerUrl,\n requestHeaders: req.headers,\n headers: noUndefinedVals({\n 'accept-language': params.langs?.length\n ? params.langs.join(',')\n : req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n }),\n signal,\n },\n {\n hydrator: ctx.hydrator,\n views: ctx.views,\n irisClient: useIris ? ctx.irisClient : undefined,\n fetchExploreTabSkeleton,\n },\n )\n return { encoding: 'application/json', body: result }\n },\n })\n}\n\nasync function skeleton({\n ctx,\n params,\n}: SkeletonFnInput<Context, Params>): Promise<SkeletonState> {\n const [config, recommendations] = await Promise.all([\n ctx.fetchExploreTabSkeleton(params.workerUrl, params.signal),\n ctx.irisClient\n ? ctx.irisClient\n .call(\n app.bsky.unspecced.getAtmosphereExploreArticleRecommendations,\n {\n limit: MAX_LIST_ITEMS,\n viewer: params.hydrateCtx.viewer ?? undefined,\n },\n {\n headers: params.headers,\n signal: params.signal,\n validateResponse: true,\n },\n )\n // @NOTE Request and response-validation failures use curated articles.\n .catch((err) => {\n if (params.signal.aborted) throw err\n httpLogger.warn(\n { err },\n 'Unable to fetch Atmosphere Explore article recommendations',\n )\n return undefined\n })\n : undefined,\n ])\n const now = Date.now()\n const announcementBanner = selectBanner(\n config.announcementBanners,\n params,\n now,\n )\n const select = createSelector(params, params.requestHeaders, now)\n return {\n now,\n announcementBanner,\n articles: orderGroups(\n recommendations && recommendations.articles.length > 0\n ? mapDefined(\n recommendations.articles.slice(0, MAX_LIST_ITEMS),\n ({ uri }) => {\n const root = recordUri(uri)\n return root &&\n new AtUri(root).collection === site.standard.document.$type\n ? { featured: false, uri: root }\n : undefined\n },\n )\n : completeGroups(select(config.articles)),\n ),\n articleRecIdStr: recommendations?.recIdStr,\n publications: orderGroups(completeGroups(select(config.publications))),\n photos: orderGroups(completeGroups(select(config.photos))),\n livestreams: orderGroups(completeGroups(select(config.livestreams))),\n // Apps have no hydration root or featured flag: keep worker order.\n apps: config.apps.slice(0, MAX_LIST_ITEMS),\n }\n}\n\nasync function hydration({\n ctx,\n params,\n skeleton,\n}: HydrationFnInput<Context, Params, SkeletonState>) {\n const uris = [\n ...new Set(\n [\n ...skeleton.articles,\n ...skeleton.publications,\n ...skeleton.photos,\n ...skeleton.livestreams,\n ].map(({ uri }) => uri),\n ),\n ]\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx)\n}\n\nfunction presentation({\n ctx,\n skeleton,\n hydration: state,\n}: PresentationFnInput<\n Context,\n Params,\n SkeletonState\n>): app.bsky.unspecced.getAtmosphereExploreTab.$OutputBody {\n const { now, articles, publications, photos, livestreams } = skeleton\n return {\n announcementBanner: skeleton.announcementBanner,\n articles: {\n items: mapDefined(articles, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n recIdStr: skeleton.articleRecIdStr,\n },\n publications: {\n items: mapDefined(publications, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n },\n photos: {\n items: mapDefined(photos, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewGallery.$isTypeOf(view) &&\n view.items.length\n ? { featured, view }\n : undefined\n }),\n },\n livestreams: {\n items: mapDefined(livestreams, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewLivestream.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n },\n apps: skeleton.apps,\n }\n}\n\nfunction createFetchExploreTabSkeleton() {\n // @NOTE The wrapper caps decoded response bytes, including compressed bodies.\n const fetchConfig = safeFetchWrap({\n timeout: 5000,\n responseMaxSize: MAX_CONFIG_BYTES,\n })\n\n return async function fetchExploreTabSkeleton(\n url: URL,\n signal: AbortSignal,\n ): Promise<ExploreConfig> {\n try {\n const response = await fetchConfig(url, {\n headers: { accept: 'application/json' },\n redirect: 'error',\n signal,\n })\n if (!response.ok) {\n await response.body?.cancel()\n throw new UpstreamFailureError(\n `Atmosphere Explore service returned HTTP ${response.status}`,\n )\n }\n return normalizeConfig(await response.json())\n } catch (err) {\n if (err instanceof UpstreamFailureError) throw err\n if (\n err instanceof Error &&\n (err.name === 'TimeoutError' || err.name === 'AbortError')\n ) {\n throw new UpstreamTimeoutError('Atmosphere Explore request timed out')\n }\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n }\n}\n\ntype Group = l.Infer<typeof groupSchema>\ntype AppCard = app.bsky.unspecced.getAtmosphereExploreTab.AppCard\ntype ValidBanner = {\n banner: l.Infer<typeof bannerSchema>\n presentation: AnnouncementBanner\n}\ntype ExploreConfig = {\n announcementBanners: ValidBanner[]\n articles: Group[]\n publications: Group[]\n photos: Group[]\n livestreams: Group[]\n apps: AppCard[]\n}\n\n/**\n * Keeps only the well-formed items of a successfully decoded worker body. Only\n * the first `MAX_INPUT_ENTRIES` entries of each section are examined, and\n * malformed entries are skipped whole, never coerced or repaired.\n */\nfunction normalizeConfig(body: unknown): ExploreConfig {\n const raw = configSchema.safeParse(body)\n const config = raw.success ? raw.value : {}\n const entries = (value: unknown): unknown[] =>\n Array.isArray(value) ? value.slice(0, MAX_INPUT_ENTRIES) : []\n const valid = <T>(schema: l.Schema<T>, value: unknown): T[] =>\n entries(value).flatMap((entry) => {\n const result = schema.safeParse(entry)\n return result.success ? [result.value] : []\n })\n return {\n announcementBanners: entries(config.announcementBanners).flatMap(\n (entry) => {\n const banner = bannerSchema.safeParse(entry)\n const presentation =\n app.bsky.unspecced.getAtmosphereExploreTab.announcementBanner.$safeValidate(\n entry,\n )\n return banner.success && presentation.success\n ? [{ banner: banner.value, presentation: presentation.value }]\n : []\n },\n ),\n articles: valid(groupSchema, config.articles),\n publications: valid(groupSchema, config.publications),\n photos: valid(groupSchema, config.photos),\n livestreams: valid(groupSchema, config.livestreams),\n apps: valid(\n app.bsky.unspecced.getAtmosphereExploreTab.appCard,\n config.apps,\n ),\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n irisClient: Client | undefined\n fetchExploreTabSkeleton: ReturnType<typeof createFetchExploreTabSkeleton>\n}\n\ntype Params = app.bsky.unspecced.getAtmosphereExploreTab.$Params & {\n hydrateCtx: HydrateCtx\n workerUrl: URL\n /** Recommendation headers for Iris only. */\n headers: Record<string, string>\n requestHeaders: IncomingHttpHeaders\n signal: AbortSignal\n}\n\ntype RecordGroup = { featured: boolean; uri: AtUriString }\n\ntype SkeletonState = {\n now: number\n announcementBanner?: app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n articles: RecordGroup[]\n articleRecIdStr?: string\n publications: RecordGroup[]\n photos: RecordGroup[]\n livestreams: RecordGroup[]\n apps: app.bsky.unspecced.getAtmosphereExploreTab.AppCard[]\n}\n\n/**\n * Randomizes a selected list (Fisher-Yates), then places featured items ahead\n * of ordinary ones. Runs before hydration, so presentation keeps this order.\n */\nfunction orderGroups(groups: RecordGroup[]): RecordGroup[] {\n const shuffled = [...groups]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n return [\n ...shuffled.filter(({ featured }) => featured),\n ...shuffled.filter(({ featured }) => !featured),\n ].slice(0, MAX_LIST_ITEMS)\n}\n\nfunction completeGroups(groups: SelectedGroup[] = []): RecordGroup[] {\n return mapDefined(groups, ({ featured, canonicalUri }) => {\n const uri = recordUri(canonicalUri)\n if (!uri) return\n return { featured, uri }\n })\n}\n\ntype AnnouncementBanner =\n app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n\n/**\n * Returns the single (already validated) banner to show. Eligible (enabled, in window) banners targeted at a matching location\n * win, first in worker order; otherwise the unrestricted eligible banner with\n * the furthest-out `endsAt` (absent means no expiration; ties keep worker order).\n */\nfunction selectBanner(\n banners: ValidBanner[],\n params: { countryCode?: string; regionCode?: string },\n now: number,\n): AnnouncementBanner | undefined {\n let matched: AnnouncementBanner | undefined\n let fallback: AnnouncementBanner | undefined\n let fallbackEnd = -Infinity\n for (const { banner, presentation } of banners) {\n const { status, startsAt, endsAt, locations } = banner\n if (status !== 'enabled') continue\n if (startsAt != null && Date.parse(startsAt) > now) continue\n const end = endsAt == null ? Infinity : Date.parse(endsAt)\n if (end <= now) continue\n if (locations?.length) {\n if (!matched && matchesLocation(locations, params)) {\n matched = presentation\n }\n } else if (end > fallbackEnd) {\n fallback = presentation\n fallbackEnd = end\n }\n }\n const selected = matched ?? fallback\n if (!selected) return\n const { id, title, description, url, image, overlayColor, textColor } =\n selected\n return { id, title, description, url, image, overlayColor, textColor }\n}\n\ntype Selectable = {\n tags?: string[]\n locations?: string[]\n startsAt?: string | null\n endsAt?: string | null\n}\ntype SelectedGroup = l.Infer<typeof groupSchema> & { featured: boolean }\n\n/**\n * Builds a per-list selector that drops location-ineligible entries, orders\n * the rest by priority bucket (location, requested topic, `general`, untagged;\n * stable within a bucket), moves entries with an active schedule window ahead\n * of the rest (stable, so bucket order holds within each side), and keeps the\n * first `MAX_LIST_ITEMS`. A worker-supplied `featured` flag is ignored.\n */\nfunction createSelector(\n params: { countryCode?: string; regionCode?: string },\n headers: IncomingHttpHeaders,\n now: number,\n) {\n const topics = parseTopics(headers['x-atproto-bsky-topics'])\n return (groups: l.Infer<typeof groupSchema>[] = []): SelectedGroup[] => {\n const buckets: l.Infer<typeof groupSchema>[][] = [[], [], [], []]\n for (const group of groups) {\n const bucket = selectionBucket(group, params, topics)\n if (bucket !== undefined) buckets[bucket].push(group)\n }\n const featured: SelectedGroup[] = []\n const ordinary: SelectedGroup[] = []\n for (const group of buckets.flat()) {\n const active = isFeatured(group, now)\n ;(active ? featured : ordinary).push({ ...group, featured: active })\n }\n return [...featured, ...ordinary].slice(0, MAX_LIST_ITEMS)\n }\n}\n\n/** Unlike banners, content needs both bounds to define a featured window. */\nfunction isFeatured({ startsAt, endsAt }: Selectable, now: number): boolean {\n return (\n startsAt != null &&\n endsAt != null &&\n Date.parse(startsAt) <= now &&\n now < Date.parse(endsAt)\n )\n}\n\nfunction selectionBucket(\n { tags, locations }: Selectable,\n params: { countryCode?: string; regionCode?: string },\n topics: Set<string>,\n): 0 | 1 | 2 | 3 | undefined {\n if (locations?.length) {\n if (!matchesLocation(locations, params)) return\n return 0\n }\n if (tags?.some((tag) => topics.has(tag))) return 1\n if (tags?.includes('general')) return 2\n if (!tags?.length) return 3\n}\n\nfunction parseTopics(header: string | string[] | undefined): Set<string> {\n const topics = new Set<string>()\n const value = Array.isArray(header) ? header.join(',') : header\n for (const token of value?.split(',') ?? []) {\n const topic = token.trim()\n if (topic) topics.add(topic)\n }\n return topics\n}\n\n/**\n * True only when a nonempty `locations` list matches the request. Entries are\n * a country (`JP`) or country and region (`US-WI`); a region alone never\n * matches. A full `regionCode` (`US-WI`) is honored only when its country\n * agrees with `countryCode`.\n */\nfunction matchesLocation(\n locations: string[] | undefined,\n { countryCode, regionCode }: { countryCode?: string; regionCode?: string },\n): boolean {\n const country = countryCode?.toUpperCase()\n if (!locations?.length || !country) return false\n let region = regionCode?.toUpperCase() || undefined\n if (region?.includes('-')) {\n const [regionCountry, subdivision] = region.split('-')\n region = regionCountry === country ? subdivision : undefined\n }\n return locations.some((location) => {\n const [locationCountry, locationRegion, ...rest] = location\n .toUpperCase()\n .split('-')\n if (rest.length || locationCountry !== country) return false\n return (\n locationRegion === undefined || (!!region && locationRegion === region)\n )\n })\n}\n\nfunction recordUri(value: unknown): AtUriString | undefined {\n if (!l.isAtUriString(value)) return\n const uri = new AtUri(value)\n return uri.collection && uri.rkey && l.isDidString(uri.hostname)\n ? value\n : undefined\n}\n"]}
@@ -25,6 +25,7 @@ export declare enum Gate {
25
25
  SuggestedFeedsV2Enable = "suggested_feeds_v2:enable",
26
26
  SuggestedStarterPacksV2Enable = "suggested_starter_packs_v2:enable",
27
27
  SuggestedStarterPacksOnboardingV2Enable = "suggested_starter_packs_onboarding_v2:enable",
28
+ AtmosphereScreenEndpointsEnable = "atmosphere_screen_endpoints:enable",
28
29
  AtmosphereExploreArticlesIrisEnable = "atmosphere_explore:articles:iris:enable",
29
30
  AATest = "aa-test-appview"
30
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/feature-gates/gates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,oBAAY,IAAI;IACd,4BAA4B,0CAA0C;IACtE,+BAA+B,wCAAwC;IACvE,oCAAoC,6CAA6C;IACjF,gCAAgC,wCAAwC;IACxE,8BAA8B,uCAAuC;IACrE,+BAA+B,wCAAwC;IACvE,8BAA8B,wCAAwC;IACtE,wBAAwB,gCAAgC;IACxD,cAAc,qBAAqB;IACnC,QAAQ,qBAAqB;IAC7B,yBAAyB,kCAAkC;IAC3D,qBAAqB,6BAA6B;IAClD,qBAAqB,6BAA6B;IAClD,sBAAsB,+BAA+B;IACrD,2BAA2B,qCAAqC;IAChE,wBAAwB,gCAAgC;IACxD,uBAAuB,gCAAgC;IACvD,qBAAqB,6BAA6B;IAClD,sBAAsB,8BAA8B;IACpD,6BAA6B,sCAAsC;IACnE,uCAAuC,iDAAiD;IACxF,mCAAmC,4CAA4C;IAG/E,MAAM,oBAAoB;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,GAAG,CAAC,IAAI,CAAe,CAAA"}
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/feature-gates/gates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,oBAAY,IAAI;IACd,4BAA4B,0CAA0C;IACtE,+BAA+B,wCAAwC;IACvE,oCAAoC,6CAA6C;IACjF,gCAAgC,wCAAwC;IACxE,8BAA8B,uCAAuC;IACrE,+BAA+B,wCAAwC;IACvE,8BAA8B,wCAAwC;IACtE,wBAAwB,gCAAgC;IACxD,cAAc,qBAAqB;IACnC,QAAQ,qBAAqB;IAC7B,yBAAyB,kCAAkC;IAC3D,qBAAqB,6BAA6B;IAClD,qBAAqB,6BAA6B;IAClD,sBAAsB,+BAA+B;IACrD,2BAA2B,qCAAqC;IAChE,wBAAwB,gCAAgC;IACxD,uBAAuB,gCAAgC;IACvD,qBAAqB,6BAA6B;IAClD,sBAAsB,8BAA8B;IACpD,6BAA6B,sCAAsC;IACnE,uCAAuC,iDAAiD;IACxF,+BAA+B,uCAAuC;IACtE,mCAAmC,4CAA4C;IAG/E,MAAM,oBAAoB;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,GAAG,CAAC,IAAI,CAAe,CAAA"}
@@ -26,6 +26,7 @@ export var Gate;
26
26
  Gate["SuggestedFeedsV2Enable"] = "suggested_feeds_v2:enable";
27
27
  Gate["SuggestedStarterPacksV2Enable"] = "suggested_starter_packs_v2:enable";
28
28
  Gate["SuggestedStarterPacksOnboardingV2Enable"] = "suggested_starter_packs_onboarding_v2:enable";
29
+ Gate["AtmosphereScreenEndpointsEnable"] = "atmosphere_screen_endpoints:enable";
29
30
  Gate["AtmosphereExploreArticlesIrisEnable"] = "atmosphere_explore:articles:iris:enable";
30
31
  // temp
31
32
  Gate["AATest"] = "aa-test-appview";
@@ -1 +1 @@
1
- {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/feature-gates/gates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAN,IAAY,IA0BX;AA1BD,WAAY,IAAI;IACd,8EAAsE,CAAA;IACtE,+EAAuE,CAAA;IACvE,yFAAiF,CAAA;IACjF,gFAAwE,CAAA;IACxE,6EAAqE,CAAA;IACrE,+EAAuE,CAAA;IACvE,8EAAsE,CAAA;IACtE,gEAAwD,CAAA;IACxD,2CAAmC,CAAA;IACnC,qCAA6B,CAAA;IAC7B,mEAA2D,CAAA;IAC3D,0DAAkD,CAAA;IAClD,0DAAkD,CAAA;IAClD,6DAAqD,CAAA;IACrD,wEAAgE,CAAA;IAChE,gEAAwD,CAAA;IACxD,+DAAuD,CAAA;IACvD,0DAAkD,CAAA;IAClD,4DAAoD,CAAA;IACpD,2EAAmE,CAAA;IACnE,gGAAwF,CAAA;IACxF,uFAA+E,CAAA;IAE/E,OAAO;IACP,kCAA0B,CAAA;AAC5B,CAAC,EA1BW,IAAI,KAAJ,IAAI,QA0Bf;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAc,IAAI,GAAG,CAAC,EAAE,CAAC,CAAA","sourcesContent":["/**\n * Enum of all gates in the system. This should be the single source of truth\n * for all gates, and should be used in all places where gates are checked or\n * defined.\n */\nexport enum Gate {\n SuggestedUsersDiscoverEnable = 'suggested_users:discover_agent:enable',\n SuggestedUsersSocialProofEnable = 'suggested_users:social_proof:enable',\n ThreadsReplyRankingExplorationEnable = 'threads:reply_ranking_exploration:enable',\n SearchFilteringExplorationEnable = 'search:filtering_exploration:enable',\n SuggestedUsersForExploreEnable = 'suggested_users:for_explore:enable',\n SuggestedUsersForDiscoverEnable = 'suggested_users:for_discover:enable',\n SuggestedUsersForSeeMoreEnable = 'suggested_users:for_see_more:enable',\n SuggestedUsersIrisEnable = 'suggested_users:iris:enable',\n SearchV2Enable = 'search:v2:enable',\n IrisFeed = 'iris:feed:enable',\n IrisFeedWithFriendsEnable = 'iris:feed:with-friends:enable',\n IrisFeedThevidsEnable = 'iris:feed:thevids:enable',\n IrisFeedMutualsEnable = 'iris:feed:mutuals:enable',\n IrisFeedBskyTeamEnable = 'iris:feed:bsky-team:enable',\n IrisFeedBestOfFollowsEnable = 'iris:feed:best-of-follows:enable',\n IrisFeedFollowpicsEnable = 'iris:feed:followpics:enable',\n IrisFeedLoggedOutEnable = 'iris:feed:logged_out:enable',\n KnownLikersFeedEnable = 'known_likers:feed:enable',\n SuggestedFeedsV2Enable = 'suggested_feeds_v2:enable',\n SuggestedStarterPacksV2Enable = 'suggested_starter_packs_v2:enable',\n SuggestedStarterPacksOnboardingV2Enable = 'suggested_starter_packs_onboarding_v2:enable',\n AtmosphereExploreArticlesIrisEnable = 'atmosphere_explore:articles:iris:enable',\n\n // temp\n AATest = 'aa-test-appview',\n}\n\n/**\n * Set of gates that should be ignored when tracking gate evaluations for\n * analytics purposes. This is useful for gates that are not user-facing or are\n * overly noisy.\n */\nexport const IGNORE_METRICS_FOR_GATES: Set<Gate> = new Set([])\n"]}
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/feature-gates/gates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAN,IAAY,IA2BX;AA3BD,WAAY,IAAI;IACd,8EAAsE,CAAA;IACtE,+EAAuE,CAAA;IACvE,yFAAiF,CAAA;IACjF,gFAAwE,CAAA;IACxE,6EAAqE,CAAA;IACrE,+EAAuE,CAAA;IACvE,8EAAsE,CAAA;IACtE,gEAAwD,CAAA;IACxD,2CAAmC,CAAA;IACnC,qCAA6B,CAAA;IAC7B,mEAA2D,CAAA;IAC3D,0DAAkD,CAAA;IAClD,0DAAkD,CAAA;IAClD,6DAAqD,CAAA;IACrD,wEAAgE,CAAA;IAChE,gEAAwD,CAAA;IACxD,+DAAuD,CAAA;IACvD,0DAAkD,CAAA;IAClD,4DAAoD,CAAA;IACpD,2EAAmE,CAAA;IACnE,gGAAwF,CAAA;IACxF,8EAAsE,CAAA;IACtE,uFAA+E,CAAA;IAE/E,OAAO;IACP,kCAA0B,CAAA;AAC5B,CAAC,EA3BW,IAAI,KAAJ,IAAI,QA2Bf;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAc,IAAI,GAAG,CAAC,EAAE,CAAC,CAAA","sourcesContent":["/**\n * Enum of all gates in the system. This should be the single source of truth\n * for all gates, and should be used in all places where gates are checked or\n * defined.\n */\nexport enum Gate {\n SuggestedUsersDiscoverEnable = 'suggested_users:discover_agent:enable',\n SuggestedUsersSocialProofEnable = 'suggested_users:social_proof:enable',\n ThreadsReplyRankingExplorationEnable = 'threads:reply_ranking_exploration:enable',\n SearchFilteringExplorationEnable = 'search:filtering_exploration:enable',\n SuggestedUsersForExploreEnable = 'suggested_users:for_explore:enable',\n SuggestedUsersForDiscoverEnable = 'suggested_users:for_discover:enable',\n SuggestedUsersForSeeMoreEnable = 'suggested_users:for_see_more:enable',\n SuggestedUsersIrisEnable = 'suggested_users:iris:enable',\n SearchV2Enable = 'search:v2:enable',\n IrisFeed = 'iris:feed:enable',\n IrisFeedWithFriendsEnable = 'iris:feed:with-friends:enable',\n IrisFeedThevidsEnable = 'iris:feed:thevids:enable',\n IrisFeedMutualsEnable = 'iris:feed:mutuals:enable',\n IrisFeedBskyTeamEnable = 'iris:feed:bsky-team:enable',\n IrisFeedBestOfFollowsEnable = 'iris:feed:best-of-follows:enable',\n IrisFeedFollowpicsEnable = 'iris:feed:followpics:enable',\n IrisFeedLoggedOutEnable = 'iris:feed:logged_out:enable',\n KnownLikersFeedEnable = 'known_likers:feed:enable',\n SuggestedFeedsV2Enable = 'suggested_feeds_v2:enable',\n SuggestedStarterPacksV2Enable = 'suggested_starter_packs_v2:enable',\n SuggestedStarterPacksOnboardingV2Enable = 'suggested_starter_packs_onboarding_v2:enable',\n AtmosphereScreenEndpointsEnable = 'atmosphere_screen_endpoints:enable',\n AtmosphereExploreArticlesIrisEnable = 'atmosphere_explore:articles:iris:enable',\n\n // temp\n AATest = 'aa-test-appview',\n}\n\n/**\n * Set of gates that should be ignored when tracking gate evaluations for\n * analytics purposes. This is useful for gates that are not user-facing or are\n * overly noisy.\n */\nexport const IGNORE_METRICS_FOR_GATES: Set<Gate> = new Set([])\n"]}
@@ -1,6 +1,6 @@
1
1
  import { type AtUriString, type DidString, type NsidString, type TypedLexMap } from '@atproto/lex';
2
2
  import type { DataPlaneClient } from '../data-plane/client/index.js';
3
- import { type ExternalHydrationRoot, type ExternalHydrationWork } from '../telemetry/events.js';
3
+ import { type ExternalHydrationRoot, type ExternalHydrationWork, type HydrationXrpcEndpoint } from '../telemetry/events.js';
4
4
  import type { SiteStandardDocumentRecord, SiteStandardPublicationRecord } from '../views/types.js';
5
5
  import type { Actors } from './actor.js';
6
6
  import type { Labels } from './label.js';
@@ -60,10 +60,23 @@ export declare class ExternalHydrator {
60
60
  static readonly MAX_BACKLINK_FANOUT = 8;
61
61
  /** Hard cap on generic batches per traversal, counting the root as pass 1. */
62
62
  static readonly MAX_EXTERNAL_HYDRATION_PASSES = 8;
63
+ /**
64
+ * Entries per generic record RPC. The dataplane rejects larger URI lookups;
65
+ * exact-ref lookups use the same bound defensively.
66
+ */
67
+ static readonly MAX_RECORDS_PER_REQUEST = 100;
63
68
  constructor(dataplane: DataPlaneClient);
64
- /** Fetch exact record versions; unavailable records are represented by null. */
69
+ /**
70
+ * Fetch exact record versions; unavailable records are represented by null.
71
+ * Large inputs are fetched in sequential bounded requests; any failed request
72
+ * rejects the whole call.
73
+ */
65
74
  getRecordsByRef(refs: Required<ItemRef>[], includeTakedowns?: boolean): Promise<ExternalRecordsByRef>;
66
- /** Fetch the latest indexed versions without collection-specific validation. */
75
+ /**
76
+ * Fetch the latest indexed versions without collection-specific validation.
77
+ * Large inputs are fetched in sequential bounded requests; any failed request
78
+ * rejects the whole call.
79
+ */
67
80
  getRecordsByURI(uris: AtUriString[], includeTakedowns?: boolean): Promise<ExternalRecords>;
68
81
  /** Fetch a page of activity refs for subsequent generic record hydration. */
69
82
  getAtmosphereTimeline(viewerDid: DidString, opts?: {
@@ -115,18 +128,28 @@ export declare function actionExternalRecordTakedowns({ externalRecords, externa
115
128
  }, includeTakedowns?: boolean): void;
116
129
  /**
117
130
  * Generic lookup work of one external hydration traversal, shared by reference
118
- * through its nested passes. `capped` records hitting the pass limit.
131
+ * through its nested passes. `capped` records hitting the pass limit; `partial`
132
+ * records a recovered best-effort dependency failure.
119
133
  */
120
134
  export type ExternalHydrationTracker = ExternalHydrationWork & {
121
135
  capped: boolean;
136
+ partial: boolean;
137
+ /**
138
+ * Internal: set by the traversal's first dependency discovery, whose refs
139
+ * are its roots.
140
+ */
141
+ rootsCounted: boolean;
122
142
  };
123
143
  /**
124
144
  * Run `fn` with the caller's tracker, or as the owner of a new traversal when
125
- * there is none. Only the owner reports, once, including on failure. Owners
126
- * that completed without scheduling a batch did no work and are not reported;
127
- * caps and failures always are.
145
+ * there is none. Only the owner reports, once, including on failure, with the
146
+ * context's XRPC endpoint (`'unknown'` if unset) and the traversal's wall time. A
147
+ * returned traversal is `capped` if it hit the pass limit, else `partial` if
148
+ * it recovered from a dependency failure. Owners that completed without
149
+ * scheduling a batch did no work and are not reported; other outcomes always
150
+ * are.
128
151
  */
129
- export declare function trackExternalHydration<T>(root: ExternalHydrationRoot, hydrationMetrics: ExternalHydrationTracker | undefined, fn: (hydrationMetrics: ExternalHydrationTracker) => Promise<T>): Promise<T>;
152
+ export declare function trackExternalHydration<T>(root: ExternalHydrationRoot, xrpcEndpoint: HydrationXrpcEndpoint | undefined, hydrationMetrics: ExternalHydrationTracker | undefined, fn: (hydrationMetrics: ExternalHydrationTracker) => Promise<T>): Promise<T>;
130
153
  export type AssociatedSiteStandardRecord<T> = {
131
154
  ref: {
132
155
  uri: AtUriString;
@@ -1 +1 @@
1
- {"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/hydration/external.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/hydration/external.ts"],"names":[],"mappings":"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"}