@atproto/bsky 0.1.7 → 0.2.0

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 (129) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/api/app/bsky/actor/getProfile.js +1 -1
  3. package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
  4. package/dist/api/app/bsky/actor/getProfiles.js +1 -1
  5. package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
  6. package/dist/api/app/bsky/actor/getSuggestions.js +2 -2
  7. package/dist/api/app/bsky/actor/getSuggestions.js.map +1 -1
  8. package/dist/api/app/bsky/embed/getEmbedExternalView.d.ts.map +1 -1
  9. package/dist/api/app/bsky/embed/getEmbedExternalView.js +132 -53
  10. package/dist/api/app/bsky/embed/getEmbedExternalView.js.map +1 -1
  11. package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
  12. package/dist/api/app/bsky/feed/getAuthorFeed.js +4 -2
  13. package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
  14. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +2 -2
  15. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
  16. package/dist/api/app/bsky/notification/constants.d.ts +4 -1
  17. package/dist/api/app/bsky/notification/constants.d.ts.map +1 -1
  18. package/dist/api/app/bsky/notification/constants.js +3 -0
  19. package/dist/api/app/bsky/notification/constants.js.map +1 -1
  20. package/dist/api/app/bsky/notification/getGroupedNotifications.js +19 -7
  21. package/dist/api/app/bsky/notification/getGroupedNotifications.js.map +1 -1
  22. package/dist/api/app/bsky/notification/grouping/spotlight.d.ts +1 -1
  23. package/dist/api/app/bsky/notification/grouping/spotlight.d.ts.map +1 -1
  24. package/dist/api/app/bsky/notification/grouping/spotlight.js +114 -80
  25. package/dist/api/app/bsky/notification/grouping/spotlight.js.map +1 -1
  26. package/dist/api/app/bsky/notification/listActivitySubscriptions.js +3 -2
  27. package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -1
  28. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -1
  29. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js +16 -148
  30. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -1
  31. package/dist/api/internal/bsky/actor/getProfiles.js +4 -1
  32. package/dist/api/internal/bsky/actor/getProfiles.js.map +1 -1
  33. package/dist/config.d.ts +2 -0
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/config.js +7 -0
  36. package/dist/config.js.map +1 -1
  37. package/dist/data-plane/server/indexing/index.d.ts +3 -1
  38. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  39. package/dist/data-plane/server/indexing/index.js +2 -0
  40. package/dist/data-plane/server/indexing/index.js.map +1 -1
  41. package/dist/data-plane/server/indexing/plugins/profile-link.d.ts +8 -0
  42. package/dist/data-plane/server/indexing/plugins/profile-link.d.ts.map +1 -0
  43. package/dist/data-plane/server/indexing/plugins/profile-link.js +30 -0
  44. package/dist/data-plane/server/indexing/plugins/profile-link.js.map +1 -0
  45. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  46. package/dist/data-plane/server/routes/records.js +1 -0
  47. package/dist/data-plane/server/routes/records.js.map +1 -1
  48. package/dist/hydration/actor.d.ts +4 -1
  49. package/dist/hydration/actor.d.ts.map +1 -1
  50. package/dist/hydration/actor.js +11 -0
  51. package/dist/hydration/actor.js.map +1 -1
  52. package/dist/hydration/external.d.ts +67 -64
  53. package/dist/hydration/external.d.ts.map +1 -1
  54. package/dist/hydration/external.js +79 -133
  55. package/dist/hydration/external.js.map +1 -1
  56. package/dist/hydration/hydrator.d.ts +53 -14
  57. package/dist/hydration/hydrator.d.ts.map +1 -1
  58. package/dist/hydration/hydrator.js +414 -105
  59. package/dist/hydration/hydrator.js.map +1 -1
  60. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +24 -0
  61. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
  62. package/dist/lexicons/app/bsky/actor/defs.defs.js +13 -0
  63. package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
  64. package/dist/lexicons/app/bsky/actor/link.d.ts +3 -0
  65. package/dist/lexicons/app/bsky/actor/link.d.ts.map +1 -0
  66. package/dist/lexicons/app/bsky/actor/link.defs.d.ts +105 -0
  67. package/dist/lexicons/app/bsky/actor/link.defs.d.ts.map +1 -0
  68. package/dist/lexicons/app/bsky/actor/link.defs.js +34 -0
  69. package/dist/lexicons/app/bsky/actor/link.defs.js.map +1 -0
  70. package/dist/lexicons/app/bsky/actor/link.js +6 -0
  71. package/dist/lexicons/app/bsky/actor/link.js.map +1 -0
  72. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts +14 -0
  73. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
  74. package/dist/lexicons/app/bsky/actor/profile.defs.js +3 -0
  75. package/dist/lexicons/app/bsky/actor/profile.defs.js.map +1 -1
  76. package/dist/lexicons/app/bsky/actor.d.ts +1 -0
  77. package/dist/lexicons/app/bsky/actor.d.ts.map +1 -1
  78. package/dist/lexicons/app/bsky/actor.js +1 -0
  79. package/dist/lexicons/app/bsky/actor.js.map +1 -1
  80. package/dist/lexicons/app/bsky/authFullApp.defs.d.ts.map +1 -1
  81. package/dist/lexicons/app/bsky/authFullApp.defs.js +1 -0
  82. package/dist/lexicons/app/bsky/authFullApp.defs.js.map +1 -1
  83. package/dist/lexicons/app/bsky/authManageProfile.defs.d.ts.map +1 -1
  84. package/dist/lexicons/app/bsky/authManageProfile.defs.js +1 -0
  85. package/dist/lexicons/app/bsky/authManageProfile.defs.js.map +1 -1
  86. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.d.ts +13 -3
  87. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.d.ts.map +1 -1
  88. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.js +11 -3
  89. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.js.map +1 -1
  90. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.d.ts +62 -3
  91. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.d.ts.map +1 -1
  92. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.js +53 -2
  93. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.js.map +1 -1
  94. package/dist/proto/bsky_connect.d.ts +10 -1
  95. package/dist/proto/bsky_connect.d.ts.map +1 -1
  96. package/dist/proto/bsky_connect.js +10 -1
  97. package/dist/proto/bsky_connect.js.map +1 -1
  98. package/dist/proto/bsky_pb.d.ts +34 -0
  99. package/dist/proto/bsky_pb.d.ts.map +1 -1
  100. package/dist/proto/bsky_pb.js +60 -0
  101. package/dist/proto/bsky_pb.js.map +1 -1
  102. package/dist/telemetry/events.d.ts +30 -0
  103. package/dist/telemetry/events.d.ts.map +1 -1
  104. package/dist/telemetry/events.js +37 -0
  105. package/dist/telemetry/events.js.map +1 -1
  106. package/dist/util/external/index.d.ts +8 -0
  107. package/dist/util/external/index.d.ts.map +1 -0
  108. package/dist/util/external/index.js +50 -0
  109. package/dist/util/external/index.js.map +1 -0
  110. package/dist/util/{standard-site.d.ts → external/standard-site.d.ts} +30 -4
  111. package/dist/util/external/standard-site.d.ts.map +1 -0
  112. package/dist/util/{standard-site.js → external/standard-site.js} +73 -4
  113. package/dist/util/external/standard-site.js.map +1 -0
  114. package/dist/views/external.js +1 -1
  115. package/dist/views/external.js.map +1 -1
  116. package/dist/views/index.d.ts +10 -6
  117. package/dist/views/index.d.ts.map +1 -1
  118. package/dist/views/index.js +84 -42
  119. package/dist/views/index.js.map +1 -1
  120. package/dist/views/profile-link-rules.d.ts +10 -0
  121. package/dist/views/profile-link-rules.d.ts.map +1 -0
  122. package/dist/views/profile-link-rules.js +48 -0
  123. package/dist/views/profile-link-rules.js.map +1 -0
  124. package/dist/views/types.d.ts +2 -0
  125. package/dist/views/types.d.ts.map +1 -1
  126. package/dist/views/types.js.map +1 -1
  127. package/package.json +4 -4
  128. package/dist/util/standard-site.d.ts.map +0 -1
  129. package/dist/util/standard-site.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"spotlight.js","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/spotlight.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport { app } from '../../../../../lexicons/index.js'\nimport {\n APPVIEW_NOTIFICATION_REASON,\n NOTIFICATION_REASON,\n} from '../constants.js'\nimport type {\n GroupingResult,\n NotificationGroup,\n NotificationItem,\n} from './grouping.js'\nimport {\n MAX_GROUP_SIZE,\n compareNotificationGroupsNewestFirst,\n isNotificationRead,\n} from './shared.js'\n\nconst MULTI_POST_LIKE_RANGES = [\n { maxItems: 200, requiredPosts: 4 },\n { maxItems: 500, requiredPosts: 8 },\n]\n\ntype SpotlightCandidate = Pick<\n NotificationGroup,\n 'kind' | 'groupingKey' | 'items'\n> & {\n isEligibleAfterTrimming: (\n spotlightItems: NotificationItem[],\n retainedItems: NotificationItem[],\n ) => boolean\n}\n\nexport const buildSpotlight = (\n items: NotificationItem[],\n groups: NotificationGroup[],\n cursor: string | undefined,\n limit: number,\n seenAt: number | undefined,\n): GroupingResult => {\n const noSpotlightPage = { groups, cursor }\n const itemCount = groups.reduce(\n (count, group) => count + group.items.length,\n 0,\n )\n\n // Grouping consumes a continuous raw prefix, even when groups interleave.\n // items contains all fetched notifications, but grouping may stop before consuming them all;\n // itemCount counts notifications included in the ordinary groups, and pageItems contains the notifications on the page.\n const pageItems = items.slice(0, itemCount)\n const spotlight = selectMultiPostLikeSpotlight(pageItems)\n if (!spotlight) return noSpotlightPage\n\n const selectedItems = new Set(spotlight.items)\n const positions = new Map(pageItems.map((item, index) => [item, index]))\n const proposedGroups = mapDefined(\n [\n ...groups.map((group) => ({\n // Filtering may remove notifications from this group; build a new group with only the remaining items and their updated metadata.\n kind: group.kind,\n groupingKey: group.groupingKey,\n items: group.items.filter((item) => !selectedItems.has(item)),\n })),\n spotlight,\n ],\n (group): NotificationGroup | undefined => {\n const newest = group.items[0]\n const oldest = group.items.at(-1)\n if (!newest || !oldest) return\n\n // Filtering may remove notifications from this group; build a new group with only the remaining items and their updated metadata.\n return {\n kind: group.kind,\n groupingKey: group.groupingKey,\n id: newest.id,\n items: group.items,\n actorDids: group.items.map((item) => item.actorDid),\n itemCount: group.items.length,\n indexedAt: newest.raw.indexedAt,\n firstIndexedAt: oldest.raw.indexedAt,\n isRead: isNotificationRead(newest.raw.indexedAt, seenAt),\n }\n },\n ).sort(compareNotificationGroupsNewestFirst)\n\n let retainedItems = pageItems\n const extraGroup = proposedGroups[limit]\n if (extraGroup) {\n const cutoff = positions.get(extraGroup.items[0]!)!\n retainedItems = pageItems.slice(0, cutoff)\n cursor = pageItems[cutoff - 1]!.raw.indexedAt\n // Trimming every group at one raw position preserves the page prefix.\n for (const group of proposedGroups) {\n group.items = group.items.filter((item) => positions.get(item)! < cutoff)\n group.actorDids = group.items.map((item) => item.actorDid)\n group.itemCount = group.items.length\n const oldest = group.items.at(-1)\n if (oldest) group.firstIndexedAt = oldest.raw.indexedAt\n }\n }\n\n const spotlightItems = retainedItems.filter((item) => selectedItems.has(item))\n if (!spotlight.isEligibleAfterTrimming(spotlightItems, retainedItems)) {\n return noSpotlightPage\n }\n\n return {\n cursor,\n groups: proposedGroups.filter((group) => group.items.length > 0),\n }\n}\n\nconst selectMultiPostLikeSpotlight = (\n items: NotificationItem[],\n): SpotlightCandidate | undefined => {\n const range = MULTI_POST_LIKE_RANGES.find(\n ({ maxItems }) => items.length <= maxItems,\n )\n if (!range) return\n\n // Eligibility uses the ordinary page's volume, including after trimming.\n const { requiredPosts } = range\n const likesByTopLiker = getLikeNotificationsByTopLiker(items)\n if (likesByTopLiker.length < requiredPosts) return\n const actorDid = likesByTopLiker[0]!.actorDid\n return {\n kind: APPVIEW_NOTIFICATION_REASON.MULTI_POST_LIKE,\n groupingKey: actorDid,\n items: likesByTopLiker.slice(0, MAX_GROUP_SIZE),\n isEligibleAfterTrimming: (spotlightItems, retainedItems) =>\n // Check if after trimming it still satisfies the requirements.\n spotlightItems.length >= requiredPosts &&\n getLikeNotificationsByTopLiker(retainedItems)[0]?.actorDid === actorDid,\n }\n}\n\nconst getLikeNotificationsByTopLiker = (\n items: NotificationItem[],\n): NotificationItem[] => {\n const likesByActor = new Map<DidString, NotificationItem[]>()\n for (const item of items) {\n if (\n item.raw.reason !== NOTIFICATION_REASON.LIKE ||\n new AtUri(item.raw.reasonSubject).collection !== app.bsky.feed.post.$type\n ) {\n continue\n }\n const likes = likesByActor.get(item.actorDid)\n if (likes) likes.push(item)\n else likesByActor.set(item.actorDid, [item])\n }\n\n // Each actor likes a post once; insertion order breaks ties by newest like.\n let top: NotificationItem[] = []\n for (const likes of likesByActor.values()) {\n if (likes.length > top.length) top = likes\n }\n return top\n}\n"]}
1
+ {"version":3,"file":"spotlight.js","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/spotlight.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport { app } from '../../../../../lexicons/index.js'\nimport {\n APPVIEW_NOTIFICATION_REASON,\n NOTIFICATION_REASON,\n} from '../constants.js'\nimport type {\n GroupingResult,\n NotificationGroup,\n NotificationItem,\n} from './grouping.js'\nimport {\n MAX_GROUP_SIZE,\n compareNotificationGroupsNewestFirst,\n isNotificationRead,\n} from './shared.js'\n\nconst MAX_SPOTLIGHTS = 3\nconst MULTI_POST_RANGES = [\n { maxItems: 200, requiredPosts: 4 },\n { maxItems: 500, requiredPosts: 8 },\n]\nconst SPOTLIGHT_KINDS = {\n [NOTIFICATION_REASON.LIKE]: APPVIEW_NOTIFICATION_REASON.MULTI_POST_LIKE,\n [NOTIFICATION_REASON.REPOST]: APPVIEW_NOTIFICATION_REASON.MULTI_POST_REPOST,\n [NOTIFICATION_REASON.LIKE_VIA_REPOST]:\n APPVIEW_NOTIFICATION_REASON.MULTI_POST_LIKE_VIA_REPOST,\n [NOTIFICATION_REASON.REPOST_VIA_REPOST]:\n APPVIEW_NOTIFICATION_REASON.MULTI_POST_REPOST_VIA_REPOST,\n} as const\ntype SpotlightReason = keyof typeof SPOTLIGHT_KINDS\nconst SPOTLIGHT_REASONS = Object.keys(SPOTLIGHT_KINDS) as SpotlightReason[]\n\ntype SpotlightCandidate = Pick<\n NotificationGroup,\n 'kind' | 'groupingKey' | 'items'\n> & {\n isEligibleAfterTrimming: (\n spotlightItems: NotificationItem[],\n retainedItems: NotificationItem[],\n ) => boolean\n}\n\nexport function buildSpotlight(\n items: NotificationItem[],\n groups: NotificationGroup[],\n cursor: string | undefined,\n limit: number,\n seenAt: number | undefined,\n): GroupingResult {\n // @NOTE Try one spotlight at a time, keeping the last page where all spotlights pass validation.\n let lastGoodPage: GroupingResult = { groups, cursor }\n const itemCount = groups.reduce(\n (count, group) => count + group.items.length,\n 0,\n )\n // @NOTE Grouping consumes a continuous raw prefix, even when groups interleave.\n // items contains all fetched notifications, but grouping may stop before consuming them all;\n // itemCount counts notifications included in the ordinary groups, and pageItems contains the notifications on the page.\n const pageItems = items.slice(0, itemCount)\n const positions = new Map(pageItems.map((item, index) => [item, index]))\n // @NOTE Rank each reason's top actor by full count, then recency, before capping spotlight size.\n const candidates = mapDefined(SPOTLIGHT_REASONS, (reason) =>\n selectSpotlight(pageItems, reason),\n )\n .sort(\n (left, right) =>\n right.items.length - left.items.length ||\n positions.get(left.items[0]!)! - positions.get(right.items[0]!)!,\n )\n .map((candidate) => ({\n ...candidate,\n items: candidate.items.slice(0, MAX_GROUP_SIZE),\n }))\n\n let lastGoodItems = pageItems\n const acceptedSpotlights: SpotlightCandidate[] = []\n for (const candidate of candidates) {\n if (acceptedSpotlights.length === MAX_SPOTLIGHTS) break\n // @NOTE 1. Tentatively add this spotlight using only notifications retained on the last accepted page.\n const candidateItems = candidate.items.filter(\n (item) => positions.get(item)! < lastGoodItems.length,\n )\n const selectedItems = new Set(candidateItems)\n const proposedGroups = mapDefined(\n [\n ...lastGoodPage.groups.map((group) => ({\n // @NOTE Filtering may remove notifications from this group; build a new group with only the remaining items and their updated metadata.\n kind: group.kind,\n groupingKey: group.groupingKey,\n items: group.items.filter((item) => !selectedItems.has(item)),\n })),\n { ...candidate, items: candidateItems },\n ],\n (group): NotificationGroup | undefined => {\n const newest = group.items[0]\n const oldest = group.items.at(-1)\n if (!newest || !oldest) return\n\n // @NOTE Filtering may remove notifications from this group; build a new group with only the remaining items and their updated metadata.\n return {\n kind: group.kind,\n groupingKey: group.groupingKey,\n id: newest.id,\n items: group.items,\n actorDids: group.items.map((item) => item.actorDid),\n itemCount: group.items.length,\n indexedAt: newest.raw.indexedAt,\n firstIndexedAt: oldest.raw.indexedAt,\n isRead: isNotificationRead(newest.raw.indexedAt, seenAt),\n }\n },\n ).sort(compareNotificationGroupsNewestFirst)\n\n // @NOTE 2. Trim the tentative page to the group limit without changing the saved state.\n let retainedItems = lastGoodItems\n let nextCursor = lastGoodPage.cursor\n const extraGroup = proposedGroups[limit]\n if (extraGroup) {\n const cutoff = positions.get(extraGroup.items[0]!)!\n retainedItems = lastGoodItems.slice(0, cutoff)\n nextCursor = pageItems[cutoff - 1]!.raw.indexedAt\n // @NOTE Trimming every group at one raw position preserves the page prefix.\n for (const group of proposedGroups) {\n group.items = group.items.filter(\n (item) => positions.get(item)! < cutoff,\n )\n group.actorDids = group.items.map((item) => item.actorDid)\n group.itemCount = group.items.length\n const oldest = group.items.at(-1)\n if (oldest) group.firstIndexedAt = oldest.raw.indexedAt\n }\n }\n\n const retained = new Set(retainedItems)\n // @NOTE 3. Reject the attempt if any spotlight fails, preserving the previously accepted page and cursor.\n const allEligible = [...acceptedSpotlights, candidate].every((spotlight) =>\n spotlight.isEligibleAfterTrimming(\n spotlight.items.filter((item) => retained.has(item)),\n retainedItems,\n ),\n )\n if (!allEligible) continue\n\n // @NOTE 4. Save the valid page, cursor, and remaining notifications for the next attempt.\n lastGoodPage = {\n cursor: nextCursor,\n groups: proposedGroups.filter((group) => group.items.length > 0),\n }\n lastGoodItems = retainedItems\n acceptedSpotlights.push(candidate)\n }\n\n return lastGoodPage\n}\n\nfunction selectSpotlight(\n items: NotificationItem[],\n reason: SpotlightReason,\n): SpotlightCandidate | undefined {\n const range = MULTI_POST_RANGES.find(\n ({ maxItems }) => items.length <= maxItems,\n )\n if (!range) return\n\n // @NOTE Eligibility uses the ordinary page's volume, including after trimming.\n const { requiredPosts } = range\n const topItems = getNotificationsByTopActor(items, reason)\n if (topItems.length < requiredPosts) return\n const actorDid = topItems[0]!.actorDid\n return {\n kind: SPOTLIGHT_KINDS[reason],\n groupingKey: actorDid,\n items: topItems,\n isEligibleAfterTrimming: (spotlightItems, retainedItems) =>\n // @NOTE Check if after trimming it still satisfies the requirements.\n spotlightItems.length >= requiredPosts &&\n getNotificationsByTopActor(retainedItems, reason)[0]?.actorDid ===\n actorDid,\n }\n}\n\nfunction getNotificationsByTopActor(\n items: NotificationItem[],\n reason: SpotlightReason,\n): NotificationItem[] {\n const collection =\n reason === NOTIFICATION_REASON.LIKE_VIA_REPOST ||\n reason === NOTIFICATION_REASON.REPOST_VIA_REPOST\n ? app.bsky.feed.repost.$type\n : app.bsky.feed.post.$type\n const itemsByActor = new Map<DidString, NotificationItem[]>()\n for (const item of items) {\n if (\n item.raw.reason !== reason ||\n new AtUri(item.raw.reasonSubject).collection !== collection\n ) {\n continue\n }\n const actorItems = itemsByActor.get(item.actorDid)\n if (actorItems) actorItems.push(item)\n else itemsByActor.set(item.actorDid, [item])\n }\n\n // @NOTE Each actor likes or reposts a post once per reason; insertion order breaks ties by newest notification.\n let top: NotificationItem[] = []\n for (const actorItems of itemsByActor.values()) {\n if (actorItems.length > top.length) top = actorItems\n }\n return top\n}\n"]}
@@ -47,8 +47,9 @@ const skeleton = async (input) => {
47
47
  const hydration = async (input) => {
48
48
  const { ctx, params, skeleton } = input;
49
49
  const { dids } = skeleton;
50
- const state = await ctx.hydrator.hydrateProfilesDetailed(dids, params.hydrateCtx);
51
- return state;
50
+ return ctx.hydrator.hydrateProfiles(dids, params.hydrateCtx, {
51
+ activitySubscriptions: true,
52
+ });
52
53
  };
53
54
  const noBlocks = (input) => {
54
55
  const { skeleton, hydration, ctx } = input;
@@ -1 +1 @@
1
- {"version":3,"file":"listActivitySubscriptions.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/listActivitySubscriptions.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtxWithViewer,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type RulesFnInput,\n type SkeletonFnInput,\n createPipeline,\n} from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport { clearlyBadCursor, fillPage, resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const listActivitySubscriptions = createPipeline(\n skeleton,\n hydration,\n noBlocks,\n presentation,\n )\n server.add(app.bsky.notification.listActivitySubscriptions, {\n auth: ctx.authVerifier.standard,\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 })\n\n const result = await fillPage({\n cursor: params.cursor,\n limit: params.limit,\n fetch: ({ cursor, limit }) =>\n listActivitySubscriptions(\n { ...params, cursor, limit, hydrateCtx },\n ctx,\n ),\n items: (r) => r.subscriptions,\n })\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n\nconst skeleton = async (\n input: SkeletonFnInput<Context, Params>,\n): Promise<SkeletonState> => {\n const { params, ctx } = input\n const actorDid = params.hydrateCtx.viewer\n if (clearlyBadCursor(params.cursor)) {\n return { actorDid, dids: [] }\n }\n const { dids, cursor } =\n await ctx.hydrator.dataplane.getActivitySubscriptionDids({\n actorDid: params.hydrateCtx.viewer,\n limit: params.limit,\n cursor: params.cursor,\n })\n return {\n actorDid,\n dids: dids as DidString[],\n cursor,\n }\n}\n\nconst hydration = async (\n input: HydrationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, params, skeleton } = input\n const { dids } = skeleton\n const state = await ctx.hydrator.hydrateProfilesDetailed(\n dids,\n params.hydrateCtx,\n )\n return state\n}\n\nconst noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {\n const { skeleton, hydration, ctx } = input\n skeleton.dids = skeleton.dids.filter(\n (did) => !ctx.views.viewerBlockExists(did, hydration),\n )\n return skeleton\n}\n\nconst presentation = (\n input: PresentationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, hydration, skeleton } = input\n const { dids, cursor } = skeleton\n const subscriptions = mapDefined(dids, (did) => {\n return ctx.views.profile(did, hydration)\n })\n return { subscriptions, cursor }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.notification.listActivitySubscriptions.$Params & {\n hydrateCtx: HydrateCtxWithViewer\n}\n\ntype SkeletonState = {\n actorDid: DidString\n dids: DidString[]\n cursor?: string\n}\n"]}
1
+ {"version":3,"file":"listActivitySubscriptions.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/listActivitySubscriptions.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtxWithViewer,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type RulesFnInput,\n type SkeletonFnInput,\n createPipeline,\n} from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport { clearlyBadCursor, fillPage, resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const listActivitySubscriptions = createPipeline(\n skeleton,\n hydration,\n noBlocks,\n presentation,\n )\n server.add(app.bsky.notification.listActivitySubscriptions, {\n auth: ctx.authVerifier.standard,\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 })\n\n const result = await fillPage({\n cursor: params.cursor,\n limit: params.limit,\n fetch: ({ cursor, limit }) =>\n listActivitySubscriptions(\n { ...params, cursor, limit, hydrateCtx },\n ctx,\n ),\n items: (r) => r.subscriptions,\n })\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n\nconst skeleton = async (\n input: SkeletonFnInput<Context, Params>,\n): Promise<SkeletonState> => {\n const { params, ctx } = input\n const actorDid = params.hydrateCtx.viewer\n if (clearlyBadCursor(params.cursor)) {\n return { actorDid, dids: [] }\n }\n const { dids, cursor } =\n await ctx.hydrator.dataplane.getActivitySubscriptionDids({\n actorDid: params.hydrateCtx.viewer,\n limit: params.limit,\n cursor: params.cursor,\n })\n return {\n actorDid,\n dids: dids as DidString[],\n cursor,\n }\n}\n\nconst hydration = async (\n input: HydrationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, params, skeleton } = input\n const { dids } = skeleton\n return ctx.hydrator.hydrateProfiles(dids, params.hydrateCtx, {\n activitySubscriptions: true,\n })\n}\n\nconst noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {\n const { skeleton, hydration, ctx } = input\n skeleton.dids = skeleton.dids.filter(\n (did) => !ctx.views.viewerBlockExists(did, hydration),\n )\n return skeleton\n}\n\nconst presentation = (\n input: PresentationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, hydration, skeleton } = input\n const { dids, cursor } = skeleton\n const subscriptions = mapDefined(dids, (did) => {\n return ctx.views.profile(did, hydration)\n })\n return { subscriptions, cursor }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.notification.listActivitySubscriptions.$Params & {\n hydrateCtx: HydrateCtxWithViewer\n}\n\ntype SkeletonState = {\n actorDid: DidString\n dids: DidString[]\n cursor?: string\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getAtmosphereExploreTab.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAwCxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA8IvD"}
1
+ {"version":3,"file":"getAtmosphereExploreTab.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AA0BxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAyHvD"}
@@ -1,16 +1,11 @@
1
- import { chunkArray, mapDefined } from '@atproto/common';
1
+ import { mapDefined } from '@atproto/common';
2
2
  import { l } from '@atproto/lex';
3
3
  import { AtUri } from '@atproto/syntax';
4
4
  import { MethodNotImplementedError, UpstreamFailureError, UpstreamTimeoutError, } from '@atproto/xrpc-server';
5
5
  import { safeFetchWrap } from '@atproto-labs/fetch-node';
6
- import { mergeManyStates, } from '../../../../hydration/hydrator.js';
7
- import { HydrationMap } from '../../../../hydration/util.js';
8
- import { app, site, social } from '../../../../lexicons/index.js';
9
- import { uriToDid } from '../../../../util/uris.js';
6
+ import { app } from '../../../../lexicons/index.js';
10
7
  const MAX_CONFIG_BYTES = 512 * 1024;
11
8
  const MAX_ATMOSPHERE_RECORDS = 500;
12
- const MAX_GALLERY_ITEMS = 10;
13
- const MAX_PREVIEW_ACTORS = 3;
14
9
  const groupSchema = l.object({
15
10
  featured: l.boolean(),
16
11
  uris: l.array(l.unknown(), { maxLength: 100 }),
@@ -82,61 +77,42 @@ export default function (server, ctx) {
82
77
  }
83
78
  const articles = completeGroups(config.articles);
84
79
  const publications = completeGroups(config.publications);
85
- const photos = completeGroups(config.photos?.map((group) => ({
86
- ...group,
87
- // @NOTE Gallery membership comes from backlinks, not loose CMS photo URIs.
88
- uris: mapDefined(group.uris, (input) => {
89
- const uri = recordUri(input);
90
- return uri &&
91
- new AtUri(uri).collection === social.grain.gallery.$type
92
- ? uri
93
- : undefined;
94
- }),
95
- })));
80
+ const photos = completeGroups(config.photos);
96
81
  const livestreams = completeGroups(config.livestreams);
97
82
  const uris = [
98
- ...new Set([...articles, ...publications, ...photos, ...livestreams].flatMap(({ uris }) => uris)),
83
+ ...new Set([...articles, ...publications, ...photos, ...livestreams].map(({ uri }) => uri)),
99
84
  ];
100
85
  const hydrateCtx = await ctx.hydrator.createContext({
101
86
  viewer: auth.credentials.iss,
102
87
  labelers: ctx.reqLabelers(req),
103
88
  });
104
- const state = await hydrateAtmosphere(uris, hydrateCtx, ctx.hydrator);
89
+ const state = await ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, hydrateCtx);
105
90
  const now = Date.now();
106
91
  const result = {
107
92
  announcementBanner: config.announcementBanner,
108
- articles: mapDefined(articles, ({ featured, uris }) => {
109
- const uri = articleUri(uris, state);
110
- const view = uri
111
- ? ctx.views.externalRecordView(uri, state, now)
112
- : undefined;
93
+ articles: mapDefined(articles, ({ featured, uri }) => {
94
+ const view = ctx.views.externalRecordView(uri, state, now);
113
95
  return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)
114
96
  ? { featured, view }
115
97
  : undefined;
116
98
  }),
117
- publications: mapDefined(publications, ({ featured, uris }) => {
118
- const view = uris.length === 1
119
- ? ctx.views.externalRecordView(uris[0], state, now)
120
- : undefined;
99
+ publications: mapDefined(publications, ({ featured, uri }) => {
100
+ const view = ctx.views.externalRecordView(uri, state, now);
121
101
  return view &&
122
102
  app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)
123
103
  ? { featured, view }
124
104
  : undefined;
125
105
  }),
126
- photos: mapDefined(photos, ({ featured, uris }) => {
127
- const view = uris.length === 1
128
- ? ctx.views.externalRecordView(uris[0], state, now)
129
- : undefined;
106
+ photos: mapDefined(photos, ({ featured, uri }) => {
107
+ const view = ctx.views.externalRecordView(uri, state, now);
130
108
  return view &&
131
109
  app.bsky.embed.external.viewGallery.$isTypeOf(view) &&
132
110
  view.items.length
133
111
  ? { featured, view }
134
112
  : undefined;
135
113
  }),
136
- livestreams: mapDefined(livestreams, ({ featured, uris }) => {
137
- const view = uris.length === 1
138
- ? ctx.views.externalRecordView(uris[0], state, now)
139
- : undefined;
114
+ livestreams: mapDefined(livestreams, ({ featured, uri }) => {
115
+ const view = ctx.views.externalRecordView(uri, state, now);
140
116
  return view && app.bsky.embed.external.viewLivestream.$isTypeOf(view)
141
117
  ? { featured, view }
142
118
  : undefined;
@@ -149,120 +125,12 @@ export default function (server, ctx) {
149
125
  }
150
126
  function completeGroups(groups = []) {
151
127
  return mapDefined(groups, ({ featured, uris }) => {
152
- const valid = mapDefined(uris, recordUri);
153
- if (!valid.length || valid.length !== uris.length)
128
+ const uri = recordUri(uris[0]);
129
+ if (!uri)
154
130
  return;
155
- return { featured, uris: valid };
131
+ return { featured, uri };
156
132
  });
157
133
  }
158
- async function hydrateAtmosphere(uris, ctx, hydrator) {
159
- const records = await hydrateRecords(uris, hydrator);
160
- const available = uris.filter((uri) => records.get(uri));
161
- const counts = await hydrator.external.getAtmosphereBacklinkCounts(available);
162
- const backlinks = new HydrationMap();
163
- // @NOTE Bound fan-out as well as each backlink page; never scan a whole collection.
164
- for (const batch of chunkArray(available, 8)) {
165
- await Promise.all(batch.map(async (uri) => {
166
- const collection = new AtUri(uri).collection;
167
- const sources = [];
168
- if (collection === site.standard.document.$type) {
169
- sources.push({
170
- collection: site.standard.graph.recommend.$type,
171
- limit: MAX_PREVIEW_ACTORS,
172
- });
173
- }
174
- else if (collection === site.standard.publication.$type) {
175
- sources.push({
176
- collection: site.standard.graph.subscription.$type,
177
- limit: MAX_PREVIEW_ACTORS,
178
- });
179
- }
180
- else if (collection === social.grain.gallery.$type) {
181
- sources.push({
182
- collection: social.grain.favorite.$type,
183
- limit: MAX_PREVIEW_ACTORS,
184
- }, {
185
- collection: social.grain.gallery.item.$type,
186
- limit: MAX_GALLERY_ITEMS,
187
- });
188
- }
189
- const links = [];
190
- for (const { collection, limit } of sources) {
191
- const page = await hydrator.external.getAtmosphereBacklinks(uri, collection, { limit });
192
- links.push(...mapDefined(page.backlinks.slice(0, limit), ({ uri: input }) => {
193
- const uri = recordUri(input);
194
- return uri && new AtUri(uri).collection === collection
195
- ? uri
196
- : undefined;
197
- }));
198
- }
199
- backlinks.set(uri, [...new Set(links)]);
200
- }));
201
- }
202
- records.merge(await hydrateRecords([...backlinks.values()].flatMap((uris) => uris ?? []), hydrator));
203
- const photoUris = [];
204
- for (const [galleryUri, links] of backlinks) {
205
- if (new AtUri(galleryUri).collection !== social.grain.gallery.$type)
206
- continue;
207
- for (const uri of links ?? []) {
208
- const item = social.grain.gallery.item.$ifMatches(records.get(uri)?.record);
209
- if (!item ||
210
- item.gallery !== galleryUri ||
211
- uriToDid(uri) !== uriToDid(galleryUri))
212
- continue;
213
- const photoUri = recordUri(item.item);
214
- if (photoUri &&
215
- new AtUri(photoUri).collection === social.grain.photo.$type)
216
- photoUris.push(photoUri);
217
- }
218
- }
219
- records.merge(await hydrateRecords(photoUris, hydrator));
220
- const allUris = [...records.keys()];
221
- const dids = [...new Set(allUris.map(uriToDid))];
222
- const states = [];
223
- for (const batch of chunkArray(dids, 200)) {
224
- states.push(await hydrator.hydrateProfilesBasic(batch, ctx));
225
- }
226
- for (const batch of chunkArray(allUris, MAX_ATMOSPHERE_RECORDS)) {
227
- states.push({
228
- labels: await hydrator.label.getLabelsForSubjects(batch, ctx.labelers),
229
- });
230
- }
231
- return {
232
- ...mergeManyStates(...states),
233
- ctx,
234
- externalRecords: records,
235
- externalRecordBacklinks: backlinks,
236
- externalRecordBacklinkCounts: counts,
237
- };
238
- }
239
- async function hydrateRecords(uris, hydrator) {
240
- const records = new HydrationMap();
241
- for (const batch of chunkArray([...new Set(uris)], MAX_ATMOSPHERE_RECORDS)) {
242
- records.merge(await hydrator.external.getRecordsByURI(batch));
243
- }
244
- return records;
245
- }
246
- // @NOTE A publication hydrated for another CMS group must not complete this article.
247
- function articleUri(uris, state) {
248
- const documents = uris.filter((uri) => new AtUri(uri).collection === site.standard.document.$type);
249
- const publications = uris.filter((uri) => new AtUri(uri).collection === site.standard.publication.$type);
250
- if (documents.length !== 1 ||
251
- publications.length > 1 ||
252
- documents.length + publications.length !== uris.length)
253
- return;
254
- const uri = documents[0];
255
- const document = site.standard.document.$ifMatches(state.externalRecords?.get(uri)?.record);
256
- if (!document)
257
- return;
258
- if (document.site.startsWith('at://')) {
259
- if (document.site !== publications[0])
260
- return;
261
- }
262
- else if (publications.length)
263
- return;
264
- return uri;
265
- }
266
134
  function recordUri(value) {
267
135
  if (!l.isAtUriString(value))
268
136
  return;
@@ -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 { chunkArray, mapDefined } from '@atproto/common'\nimport { type AtUriString, type NsidString, 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 {\n ExternalRecordBacklinks,\n ExternalRecords,\n} from '../../../../hydration/external.js'\nimport {\n type HydrateCtx,\n type HydrationState,\n type Hydrator,\n mergeManyStates,\n} from '../../../../hydration/hydrator.js'\nimport { HydrationMap } from '../../../../hydration/util.js'\nimport { app, site, social } from '../../../../lexicons/index.js'\nimport { uriToDid } from '../../../../util/uris.js'\n\nconst MAX_CONFIG_BYTES = 512 * 1024\nconst MAX_ATMOSPHERE_RECORDS = 500\nconst MAX_GALLERY_ITEMS = 10\nconst MAX_PREVIEW_ACTORS = 3\n\nconst groupSchema = l.object({\n featured: l.boolean(),\n uris: l.array(l.unknown(), { maxLength: 100 }),\n})\nconst configSchema = l.object({\n announcementBanner: l.optional(\n app.bsky.unspecced.getAtmosphereExploreTab.announcementBanner,\n ),\n articles: l.optional(l.array(groupSchema, { maxLength: 100 })),\n publications: l.optional(l.array(groupSchema, { maxLength: 100 })),\n photos: l.optional(l.array(groupSchema, { maxLength: 100 })),\n livestreams: l.optional(l.array(groupSchema, { maxLength: 100 })),\n apps: l.optional(\n l.array(app.bsky.unspecced.getAtmosphereExploreTab.appCard, {\n maxLength: 100,\n }),\n ),\n})\ntype RecordGroup = { featured: boolean; uris: AtUriString[] }\n\nexport default function (server: Server, ctx: AppContext) {\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 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 body: unknown\n try {\n const url = new URL(service)\n if (params.countryCode !== undefined) {\n url.searchParams.set('countryCode', params.countryCode)\n }\n if (params.regionCode !== undefined) {\n url.searchParams.set('regionCode', params.regionCode)\n }\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 body = 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 const parsed = configSchema.safeParse(body)\n if (!parsed.success)\n throw new UpstreamFailureError(\n 'Invalid Atmosphere Explore configuration',\n )\n const config = parsed.value\n const groups = [\n ...(config.articles ?? []),\n ...(config.publications ?? []),\n ...(config.photos ?? []),\n ...(config.livestreams ?? []),\n ]\n if (\n groups.reduce((count, group) => count + group.uris.length, 0) >\n MAX_ATMOSPHERE_RECORDS\n ) {\n throw new UpstreamFailureError(\n 'Atmosphere Explore configuration exceeds its record limit',\n )\n }\n const articles = completeGroups(config.articles)\n const publications = completeGroups(config.publications)\n const photos = completeGroups(\n config.photos?.map((group) => ({\n ...group,\n // @NOTE Gallery membership comes from backlinks, not loose CMS photo URIs.\n uris: mapDefined(group.uris, (input) => {\n const uri = recordUri(input)\n return uri &&\n new AtUri(uri).collection === social.grain.gallery.$type\n ? uri\n : undefined\n }),\n })),\n )\n const livestreams = completeGroups(config.livestreams)\n const uris = [\n ...new Set(\n [...articles, ...publications, ...photos, ...livestreams].flatMap(\n ({ uris }) => uris,\n ),\n ),\n ]\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer: auth.credentials.iss,\n labelers: ctx.reqLabelers(req),\n })\n const state = await hydrateAtmosphere(uris, hydrateCtx, ctx.hydrator)\n const now = Date.now()\n const result: app.bsky.unspecced.getAtmosphereExploreTab.$OutputBody = {\n announcementBanner: config.announcementBanner,\n articles: mapDefined(articles, ({ featured, uris }) => {\n const uri = articleUri(uris, state)\n const view = uri\n ? ctx.views.externalRecordView(uri, state, now)\n : undefined\n return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n publications: mapDefined(publications, ({ featured, uris }) => {\n const view =\n uris.length === 1\n ? ctx.views.externalRecordView(uris[0], state, now)\n : undefined\n return view &&\n app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n photos: mapDefined(photos, ({ featured, uris }) => {\n const view =\n uris.length === 1\n ? ctx.views.externalRecordView(uris[0], state, now)\n : undefined\n return view &&\n app.bsky.embed.external.viewGallery.$isTypeOf(view) &&\n view.items.length\n ? { featured, view }\n : undefined\n }),\n livestreams: mapDefined(livestreams, ({ featured, uris }) => {\n const view =\n uris.length === 1\n ? ctx.views.externalRecordView(uris[0], state, now)\n : undefined\n return view && app.bsky.embed.external.viewLivestream.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n apps: config.apps ?? [],\n }\n return { encoding: 'application/json', body: result }\n },\n })\n}\n\nfunction completeGroups(\n groups: l.Infer<typeof groupSchema>[] = [],\n): RecordGroup[] {\n return mapDefined(groups, ({ featured, uris }) => {\n const valid = mapDefined(uris, recordUri)\n if (!valid.length || valid.length !== uris.length) return\n return { featured, uris: valid }\n })\n}\n\nasync function hydrateAtmosphere(\n uris: AtUriString[],\n ctx: HydrateCtx,\n hydrator: Hydrator,\n): Promise<HydrationState> {\n const records = await hydrateRecords(uris, hydrator)\n const available = uris.filter((uri) => records.get(uri))\n const counts = await hydrator.external.getAtmosphereBacklinkCounts(available)\n const backlinks: ExternalRecordBacklinks = new HydrationMap()\n\n // @NOTE Bound fan-out as well as each backlink page; never scan a whole collection.\n for (const batch of chunkArray(available, 8)) {\n await Promise.all(\n batch.map(async (uri) => {\n const collection = new AtUri(uri).collection\n const sources: { collection: NsidString; limit: number }[] = []\n if (collection === site.standard.document.$type) {\n sources.push({\n collection: site.standard.graph.recommend.$type,\n limit: MAX_PREVIEW_ACTORS,\n })\n } else if (collection === site.standard.publication.$type) {\n sources.push({\n collection: site.standard.graph.subscription.$type,\n limit: MAX_PREVIEW_ACTORS,\n })\n } else if (collection === social.grain.gallery.$type) {\n sources.push(\n {\n collection: social.grain.favorite.$type,\n limit: MAX_PREVIEW_ACTORS,\n },\n {\n collection: social.grain.gallery.item.$type,\n limit: MAX_GALLERY_ITEMS,\n },\n )\n }\n const links: AtUriString[] = []\n for (const { collection, limit } of sources) {\n const page = await hydrator.external.getAtmosphereBacklinks(\n uri,\n collection,\n { limit },\n )\n links.push(\n ...mapDefined(page.backlinks.slice(0, limit), ({ uri: input }) => {\n const uri = recordUri(input)\n return uri && new AtUri(uri).collection === collection\n ? uri\n : undefined\n }),\n )\n }\n backlinks.set(uri, [...new Set(links)])\n }),\n )\n }\n records.merge(\n await hydrateRecords(\n [...backlinks.values()].flatMap((uris) => uris ?? []),\n hydrator,\n ),\n )\n\n const photoUris: AtUriString[] = []\n for (const [galleryUri, links] of backlinks) {\n if (new AtUri(galleryUri).collection !== social.grain.gallery.$type)\n continue\n for (const uri of links ?? []) {\n const item = social.grain.gallery.item.$ifMatches(\n records.get(uri)?.record,\n )\n if (\n !item ||\n item.gallery !== galleryUri ||\n uriToDid(uri) !== uriToDid(galleryUri)\n )\n continue\n const photoUri = recordUri(item.item)\n if (\n photoUri &&\n new AtUri(photoUri).collection === social.grain.photo.$type\n )\n photoUris.push(photoUri)\n }\n }\n records.merge(await hydrateRecords(photoUris, hydrator))\n\n const allUris = [...records.keys()]\n const dids = [...new Set(allUris.map(uriToDid))]\n const states: HydrationState[] = []\n for (const batch of chunkArray(dids, 200)) {\n states.push(await hydrator.hydrateProfilesBasic(batch, ctx))\n }\n for (const batch of chunkArray(allUris, MAX_ATMOSPHERE_RECORDS)) {\n states.push({\n labels: await hydrator.label.getLabelsForSubjects(batch, ctx.labelers),\n })\n }\n return {\n ...mergeManyStates(...states),\n ctx,\n externalRecords: records,\n externalRecordBacklinks: backlinks,\n externalRecordBacklinkCounts: counts,\n }\n}\n\nasync function hydrateRecords(\n uris: AtUriString[],\n hydrator: Hydrator,\n): Promise<ExternalRecords> {\n const records: ExternalRecords = new HydrationMap()\n for (const batch of chunkArray([...new Set(uris)], MAX_ATMOSPHERE_RECORDS)) {\n records.merge(await hydrator.external.getRecordsByURI(batch))\n }\n return records\n}\n\n// @NOTE A publication hydrated for another CMS group must not complete this article.\nfunction articleUri(\n uris: AtUriString[],\n state: HydrationState,\n): AtUriString | undefined {\n const documents = uris.filter(\n (uri) => new AtUri(uri).collection === site.standard.document.$type,\n )\n const publications = uris.filter(\n (uri) => new AtUri(uri).collection === site.standard.publication.$type,\n )\n if (\n documents.length !== 1 ||\n publications.length > 1 ||\n documents.length + publications.length !== uris.length\n )\n return\n const uri = documents[0]\n const document = site.standard.document.$ifMatches(\n state.externalRecords?.get(uri)?.record,\n )\n if (!document) return\n if (document.site.startsWith('at://')) {\n if (document.site !== publications[0]) return\n } else if (publications.length) return\n return uri\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":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAoB,CAAC,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,EACL,yBAAyB,EAEzB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAExD,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,gBAAgB,GAAG,GAAG,GAAG,IAAI,CAAA;AACnC,MAAM,sBAAsB,GAAG,GAAG,CAAA;AAElC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACrB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;CAC/C,CAAC,CAAA;AACF,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,kBAAkB,EAAE,CAAC,CAAC,QAAQ,CAC5B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,kBAAkB,CAC9D;IACD,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9D,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAClE,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5D,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACjE,IAAI,EAAE,CAAC,CAAC,QAAQ,CACd,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,OAAO,EAAE;QAC1D,SAAS,EAAE,GAAG;KACf,CAAC,CACH;CACF,CAAC,CAAA;AAGF,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,8EAA8E;IAC9E,MAAM,WAAW,GAAG,aAAa,CAAC;QAChC,OAAO,EAAE,IAAI;QACb,eAAe,EAAE,gBAAgB;KAClC,CAAC,CAAA;IACF,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,uBAAuB,EAAE;QACrD,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,gBAAgB;QACvC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;YAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,2BAA2B,CAAA;YACnD,IAAI,CAAC,OAAO;gBACV,MAAM,IAAI,yBAAyB,CACjC,2CAA2C,CAC5C,CAAA;YACH,IAAI,IAAa,CAAA;YACjB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;gBAC5B,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;oBACrC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;gBACzD,CAAC;gBACD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBACpC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;gBACvD,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE;oBACtC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;oBACvC,QAAQ,EAAE,OAAO;oBACjB,MAAM;iBACP,CAAC,CAAA;gBACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;oBAC7B,MAAM,IAAI,oBAAoB,CAC5B,4CAA4C,QAAQ,CAAC,MAAM,EAAE,CAC9D,CAAA;gBACH,CAAC;gBACD,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,oBAAoB;oBAAE,MAAM,GAAG,CAAA;gBAClD,IACE,GAAG,YAAY,KAAK;oBACpB,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,EAC1D,CAAC;oBACD,MAAM,IAAI,oBAAoB,CAAC,sCAAsC,CAAC,CAAA;gBACxE,CAAC;gBACD,MAAM,IAAI,oBAAoB,CAC5B,kDAAkD,CACnD,CAAA;YACH,CAAC;YACD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAC3C,IAAI,CAAC,MAAM,CAAC,OAAO;gBACjB,MAAM,IAAI,oBAAoB,CAC5B,0CAA0C,CAC3C,CAAA;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAA;YAC3B,MAAM,MAAM,GAAG;gBACb,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAC1B,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC;gBAC9B,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;gBACxB,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;aAC9B,CAAA;YACD,IACE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC7D,sBAAsB,EACtB,CAAC;gBACD,MAAM,IAAI,oBAAoB,CAC5B,2DAA2D,CAC5D,CAAA;YACH,CAAC;YACD,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YAChD,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;YACxD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC5C,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;YACtD,MAAM,IAAI,GAAG;gBACX,GAAG,IAAI,GAAG,CACR,CAAC,GAAG,QAAQ,EAAE,GAAG,YAAY,EAAE,GAAG,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,GAAG,CAC3D,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CACjB,CACF;aACF,CAAA;YACD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG;gBAC5B,QAAQ,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC;aAC/B,CAAC,CAAA;YACF,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,gCAAgC,CAC/D,IAAI,EACJ,UAAU,CACX,CAAA;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,MAAM,MAAM,GAA2D;gBACrE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE;oBACnD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;oBAC1D,OAAO,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC;wBAChE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;wBACpB,CAAC,CAAC,SAAS,CAAA;gBACf,CAAC,CAAC;gBACF,YAAY,EAAE,UAAU,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE;oBAC3D,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;oBAC1D,OAAO,IAAI;wBACT,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,IAAI,CAAC;wBAC9D,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;wBACpB,CAAC,CAAC,SAAS,CAAA;gBACf,CAAC,CAAC;gBACF,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE;oBAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;oBAC1D,OAAO,IAAI;wBACT,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC;wBACnD,IAAI,CAAC,KAAK,CAAC,MAAM;wBACjB,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;wBACpB,CAAC,CAAC,SAAS,CAAA;gBACf,CAAC,CAAC;gBACF,WAAW,EAAE,UAAU,CAAC,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE;oBACzD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAA;oBAC1D,OAAO,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC;wBACnE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;wBACpB,CAAC,CAAC,SAAS,CAAA;gBACf,CAAC,CAAC;gBACF,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;aACxB,CAAA;YACD,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QACvD,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,MAAM,GAAkC,EAAE;IAE1C,OAAO,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QAC/C,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9B,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAA;IAC1B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAM;IACnC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,CAAA;IAC5B,OAAO,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC9D,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,SAAS,CAAA;AACf,CAAC","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport { type AtUriString, 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 { app } from '../../../../lexicons/index.js'\n\nconst MAX_CONFIG_BYTES = 512 * 1024\nconst MAX_ATMOSPHERE_RECORDS = 500\n\nconst groupSchema = l.object({\n featured: l.boolean(),\n uris: l.array(l.unknown(), { maxLength: 100 }),\n})\nconst configSchema = l.object({\n announcementBanner: l.optional(\n app.bsky.unspecced.getAtmosphereExploreTab.announcementBanner,\n ),\n articles: l.optional(l.array(groupSchema, { maxLength: 100 })),\n publications: l.optional(l.array(groupSchema, { maxLength: 100 })),\n photos: l.optional(l.array(groupSchema, { maxLength: 100 })),\n livestreams: l.optional(l.array(groupSchema, { maxLength: 100 })),\n apps: l.optional(\n l.array(app.bsky.unspecced.getAtmosphereExploreTab.appCard, {\n maxLength: 100,\n }),\n ),\n})\ntype RecordGroup = { featured: boolean; uri: AtUriString }\n\nexport default function (server: Server, ctx: AppContext) {\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 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 body: unknown\n try {\n const url = new URL(service)\n if (params.countryCode !== undefined) {\n url.searchParams.set('countryCode', params.countryCode)\n }\n if (params.regionCode !== undefined) {\n url.searchParams.set('regionCode', params.regionCode)\n }\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 body = 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 const parsed = configSchema.safeParse(body)\n if (!parsed.success)\n throw new UpstreamFailureError(\n 'Invalid Atmosphere Explore configuration',\n )\n const config = parsed.value\n const groups = [\n ...(config.articles ?? []),\n ...(config.publications ?? []),\n ...(config.photos ?? []),\n ...(config.livestreams ?? []),\n ]\n if (\n groups.reduce((count, group) => count + group.uris.length, 0) >\n MAX_ATMOSPHERE_RECORDS\n ) {\n throw new UpstreamFailureError(\n 'Atmosphere Explore configuration exceeds its record limit',\n )\n }\n const articles = completeGroups(config.articles)\n const publications = completeGroups(config.publications)\n const photos = completeGroups(config.photos)\n const livestreams = completeGroups(config.livestreams)\n const uris = [\n ...new Set(\n [...articles, ...publications, ...photos, ...livestreams].map(\n ({ uri }) => uri,\n ),\n ),\n ]\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer: auth.credentials.iss,\n labelers: ctx.reqLabelers(req),\n })\n const state = await ctx.hydrator.hydrateEmbedExternalViewFromUris(\n uris,\n hydrateCtx,\n )\n const now = Date.now()\n const result: app.bsky.unspecced.getAtmosphereExploreTab.$OutputBody = {\n announcementBanner: config.announcementBanner,\n articles: 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 publications: 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 photos: 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 livestreams: 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 apps: config.apps ?? [],\n }\n return { encoding: 'application/json', body: result }\n },\n })\n}\n\nfunction completeGroups(\n groups: l.Infer<typeof groupSchema>[] = [],\n): RecordGroup[] {\n return mapDefined(groups, ({ featured, uris }) => {\n const uri = recordUri(uris[0])\n if (!uri) return\n return { featured, uri }\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"]}
@@ -30,7 +30,10 @@ const skeleton = async (input) => {
30
30
  };
31
31
  const hydration = async (input) => {
32
32
  const { ctx, params, skeleton } = input;
33
- return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx, { knownFollowersDids: skeleton.socialProofDids });
33
+ return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx, {
34
+ knownFollowersDids: skeleton.socialProofDids,
35
+ activitySubscriptions: true,
36
+ });
34
37
  };
35
38
  const presentation = (input) => {
36
39
  const { ctx, skeleton, hydration } = input;
@@ -1 +1 @@
1
- {"version":3,"file":"getProfiles.js","sourceRoot":"","sources":["../../../../../src/api/internal/bsky/actor/getProfiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAS5C,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAGjE,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;IAC9E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;QAC1C,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI;QAC3B,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;YACrC,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,IAAI;gBAC7B,QAAQ;gBACR,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;aAC1C,CAAC,CAAA;YAEF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,CAAC,CAAA;YAEhE,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,MAAM;aACb,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,KAAK,EAAE,KAGvB,EAA0B,EAAE;IAC3B,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;IACxB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;IAC5B,gEAAgE;IAChE,MAAM,eAAe,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAChB,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAA;AAClC,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,KAAK,EAAE,KAIxB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IACvC,OAAO,GAAG,CAAC,QAAQ,CAAC,uBAAuB,CACzC,QAAQ,CAAC,IAAI,EACb,MAAM,CAAC,UAAU,EACjB,EAAE,kBAAkB,EAAE,QAAQ,CAAC,eAAe,EAAE,CACjD,CAAA;AACH,CAAC,CAAA;AAED,MAAM,YAAY,GAAG,CAAC,KAKrB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CACjD,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAC1C,CAAA;IACD,OAAO,EAAE,QAAQ,EAAE,CAAA;AACrB,CAAC,CAAA","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { internal } from '../../../../lexicons/index.js'\nimport { createPipeline, noRules } from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getProfiles = createPipeline(skeleton, hydration, noRules, presentation)\n server.add(internal.bsky.actor.getProfiles, {\n auth: ctx.authVerifier.role,\n handler: async ({ params, req }) => {\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer: params.viewer ?? null,\n labelers,\n includeTakedowns: params.includeTakedowns,\n })\n\n const result = await getProfiles({ ...params, hydrateCtx }, ctx)\n\n return {\n encoding: 'application/json',\n body: result,\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<SkeletonState> => {\n const { params } = input\n const dids = params.dids\n const didSet = new Set(dids)\n // social proof is only hydrated for dids present in both inputs\n const socialProofDids = (params.socialProof ?? []).filter((did) =>\n didSet.has(did),\n )\n return { dids, socialProofDids }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(\n skeleton.dids,\n params.hydrateCtx,\n { knownFollowersDids: skeleton.socialProofDids },\n )\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n const profiles = mapDefined(skeleton.dids, (did) =>\n ctx.views.profileDetailed(did, hydration),\n )\n return { profiles }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = internal.bsky.actor.getProfiles.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype SkeletonState = {\n dids: DidString[]\n socialProofDids: DidString[]\n}\n"]}
1
+ {"version":3,"file":"getProfiles.js","sourceRoot":"","sources":["../../../../../src/api/internal/bsky/actor/getProfiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAS5C,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAGjE,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;IAC9E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;QAC1C,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI;QAC3B,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;YACrC,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,IAAI;gBAC7B,QAAQ;gBACR,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;aAC1C,CAAC,CAAA;YAEF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,CAAC,CAAA;YAEhE,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,MAAM;aACb,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,KAAK,EAAE,KAGvB,EAA0B,EAAE;IAC3B,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;IACxB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;IAC5B,gEAAgE;IAChE,MAAM,eAAe,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAChB,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAA;AAClC,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,KAAK,EAAE,KAIxB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IACvC,OAAO,GAAG,CAAC,QAAQ,CAAC,uBAAuB,CACzC,QAAQ,CAAC,IAAI,EACb,MAAM,CAAC,UAAU,EACjB;QACE,kBAAkB,EAAE,QAAQ,CAAC,eAAe;QAC5C,qBAAqB,EAAE,IAAI;KAC5B,CACF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,YAAY,GAAG,CAAC,KAKrB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CACjD,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAC1C,CAAA;IACD,OAAO,EAAE,QAAQ,EAAE,CAAA;AACrB,CAAC,CAAA","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { internal } from '../../../../lexicons/index.js'\nimport { createPipeline, noRules } from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getProfiles = createPipeline(skeleton, hydration, noRules, presentation)\n server.add(internal.bsky.actor.getProfiles, {\n auth: ctx.authVerifier.role,\n handler: async ({ params, req }) => {\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer: params.viewer ?? null,\n labelers,\n includeTakedowns: params.includeTakedowns,\n })\n\n const result = await getProfiles({ ...params, hydrateCtx }, ctx)\n\n return {\n encoding: 'application/json',\n body: result,\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<SkeletonState> => {\n const { params } = input\n const dids = params.dids\n const didSet = new Set(dids)\n // social proof is only hydrated for dids present in both inputs\n const socialProofDids = (params.socialProof ?? []).filter((did) =>\n didSet.has(did),\n )\n return { dids, socialProofDids }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(\n skeleton.dids,\n params.hydrateCtx,\n {\n knownFollowersDids: skeleton.socialProofDids,\n activitySubscriptions: true,\n },\n )\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n const profiles = mapDefined(skeleton.dids, (did) =>\n ctx.views.profileDetailed(did, hydration),\n )\n return { profiles }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = internal.bsky.actor.getProfiles.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype SkeletonState = {\n dids: DidString[]\n socialProofDids: DidString[]\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -54,6 +54,7 @@ export interface ServerConfigValues {
54
54
  rolodexApiKey?: string;
55
55
  rolodexHttpVersion?: '1.1' | '2';
56
56
  rolodexIgnoreBadTls?: boolean;
57
+ externalRecordsSafeDomains?: string[];
57
58
  searchUrl?: string;
58
59
  searchTagsHide: Set<string>;
59
60
  searchTagsHideAll: Set<string>;
@@ -202,6 +203,7 @@ export declare class ServerConfig {
202
203
  get debugFieldAllowedDids(): Set<string>;
203
204
  get draftsLimit(): number;
204
205
  get searchV2OverrideHeader(): string | undefined;
206
+ get externalRecordsSafeDomains(): string[];
205
207
  }
206
208
  export {};
207
209
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -96,6 +96,9 @@ export class ServerConfig {
96
96
  const rolodexHttpVersion = process.env.BSKY_ROLODEX_HTTP_VERSION || '2';
97
97
  const rolodexIgnoreBadTls = process.env.BSKY_ROLODEX_IGNORE_BAD_TLS === 'true';
98
98
  assert(rolodexHttpVersion === '1.1' || rolodexHttpVersion === '2');
99
+ const externalRecordsSafeDomains = envList(process.env.BSKY_EXTERNAL_RECORDS_SAFE_DOMAINS)
100
+ .map((value) => value.trim())
101
+ .filter(Boolean);
99
102
  const blobRateLimitBypassKey = process.env.BSKY_BLOB_RATE_LIMIT_BYPASS_KEY || undefined;
100
103
  // single domain would be e.g. "mypds.com", subdomains are supported with a leading dot e.g. ".mypds.com"
101
104
  const blobRateLimitBypassHostname = process.env.BSKY_BLOB_RATE_LIMIT_BYPASS_HOSTNAME || undefined;
@@ -245,6 +248,7 @@ export class ServerConfig {
245
248
  rolodexApiKey,
246
249
  rolodexHttpVersion,
247
250
  rolodexIgnoreBadTls,
251
+ externalRecordsSafeDomains,
248
252
  blobRateLimitBypassKey,
249
253
  blobRateLimitBypassHostname,
250
254
  adminPasswords,
@@ -533,6 +537,9 @@ export class ServerConfig {
533
537
  get searchV2OverrideHeader() {
534
538
  return this.cfg.searchV2OverrideHeader;
535
539
  }
540
+ get externalRecordsSafeDomains() {
541
+ return this.cfg.externalRecordsSafeDomains ?? [];
542
+ }
536
543
  }
537
544
  function envList(str) {
538
545
  if (str === undefined || str.length === 0)