@atproto/bsky 0.3.13 → 0.5.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 (288) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/api/app/bsky/actor/getProfile.d.ts.map +1 -1
  3. package/dist/api/app/bsky/actor/getProfile.js +1 -0
  4. package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
  5. package/dist/api/app/bsky/actor/getProfiles.d.ts.map +1 -1
  6. package/dist/api/app/bsky/actor/getProfiles.js +1 -0
  7. package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
  8. package/dist/api/app/bsky/actor/getSuggestions.d.ts.map +1 -1
  9. package/dist/api/app/bsky/actor/getSuggestions.js +1 -0
  10. package/dist/api/app/bsky/actor/getSuggestions.js.map +1 -1
  11. package/dist/api/app/bsky/actor/searchActors.d.ts.map +1 -1
  12. package/dist/api/app/bsky/actor/searchActors.js +1 -0
  13. package/dist/api/app/bsky/actor/searchActors.js.map +1 -1
  14. package/dist/api/app/bsky/actor/searchActorsTypeahead.d.ts.map +1 -1
  15. package/dist/api/app/bsky/actor/searchActorsTypeahead.js +1 -0
  16. package/dist/api/app/bsky/actor/searchActorsTypeahead.js.map +1 -1
  17. package/dist/api/app/bsky/bookmark/getBookmarks.d.ts.map +1 -1
  18. package/dist/api/app/bsky/bookmark/getBookmarks.js +1 -0
  19. package/dist/api/app/bsky/bookmark/getBookmarks.js.map +1 -1
  20. package/dist/api/app/bsky/contact/getMatches.d.ts.map +1 -1
  21. package/dist/api/app/bsky/contact/getMatches.js +1 -0
  22. package/dist/api/app/bsky/contact/getMatches.js.map +1 -1
  23. package/dist/api/app/bsky/contact/importContacts.d.ts.map +1 -1
  24. package/dist/api/app/bsky/contact/importContacts.js +1 -0
  25. package/dist/api/app/bsky/contact/importContacts.js.map +1 -1
  26. package/dist/api/app/bsky/feed/getActorFeeds.d.ts.map +1 -1
  27. package/dist/api/app/bsky/feed/getActorFeeds.js +5 -1
  28. package/dist/api/app/bsky/feed/getActorFeeds.js.map +1 -1
  29. package/dist/api/app/bsky/feed/getActorLikes.d.ts.map +1 -1
  30. package/dist/api/app/bsky/feed/getActorLikes.js +1 -0
  31. package/dist/api/app/bsky/feed/getActorLikes.js.map +1 -1
  32. package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
  33. package/dist/api/app/bsky/feed/getAuthorFeed.js +1 -0
  34. package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
  35. package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
  36. package/dist/api/app/bsky/feed/getFeed.js +1 -0
  37. package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
  38. package/dist/api/app/bsky/feed/getFeedGenerator.d.ts.map +1 -1
  39. package/dist/api/app/bsky/feed/getFeedGenerator.js +5 -1
  40. package/dist/api/app/bsky/feed/getFeedGenerator.js.map +1 -1
  41. package/dist/api/app/bsky/feed/getFeedGenerators.d.ts.map +1 -1
  42. package/dist/api/app/bsky/feed/getFeedGenerators.js +5 -1
  43. package/dist/api/app/bsky/feed/getFeedGenerators.js.map +1 -1
  44. package/dist/api/app/bsky/feed/getLikes.d.ts.map +1 -1
  45. package/dist/api/app/bsky/feed/getLikes.js +1 -0
  46. package/dist/api/app/bsky/feed/getLikes.js.map +1 -1
  47. package/dist/api/app/bsky/feed/getListFeed.d.ts.map +1 -1
  48. package/dist/api/app/bsky/feed/getListFeed.js +1 -0
  49. package/dist/api/app/bsky/feed/getListFeed.js.map +1 -1
  50. package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
  51. package/dist/api/app/bsky/feed/getPostThread.js +1 -0
  52. package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
  53. package/dist/api/app/bsky/feed/getPosts.d.ts.map +1 -1
  54. package/dist/api/app/bsky/feed/getPosts.js +5 -1
  55. package/dist/api/app/bsky/feed/getPosts.js.map +1 -1
  56. package/dist/api/app/bsky/feed/getQuotes.d.ts.map +1 -1
  57. package/dist/api/app/bsky/feed/getQuotes.js +1 -0
  58. package/dist/api/app/bsky/feed/getQuotes.js.map +1 -1
  59. package/dist/api/app/bsky/feed/getRepostedBy.d.ts.map +1 -1
  60. package/dist/api/app/bsky/feed/getRepostedBy.js +1 -0
  61. package/dist/api/app/bsky/feed/getRepostedBy.js.map +1 -1
  62. package/dist/api/app/bsky/feed/getSuggestedFeeds.d.ts.map +1 -1
  63. package/dist/api/app/bsky/feed/getSuggestedFeeds.js +5 -1
  64. package/dist/api/app/bsky/feed/getSuggestedFeeds.js.map +1 -1
  65. package/dist/api/app/bsky/feed/getTimeline.d.ts.map +1 -1
  66. package/dist/api/app/bsky/feed/getTimeline.js +1 -0
  67. package/dist/api/app/bsky/feed/getTimeline.js.map +1 -1
  68. package/dist/api/app/bsky/feed/searchPosts.d.ts.map +1 -1
  69. package/dist/api/app/bsky/feed/searchPosts.js +1 -0
  70. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  71. package/dist/api/app/bsky/feed/searchPostsV2.d.ts.map +1 -1
  72. package/dist/api/app/bsky/feed/searchPostsV2.js +1 -0
  73. package/dist/api/app/bsky/feed/searchPostsV2.js.map +1 -1
  74. package/dist/api/app/bsky/graph/getActorStarterPacks.d.ts.map +1 -1
  75. package/dist/api/app/bsky/graph/getActorStarterPacks.js +1 -0
  76. package/dist/api/app/bsky/graph/getActorStarterPacks.js.map +1 -1
  77. package/dist/api/app/bsky/graph/getBlocks.d.ts.map +1 -1
  78. package/dist/api/app/bsky/graph/getBlocks.js +5 -1
  79. package/dist/api/app/bsky/graph/getBlocks.js.map +1 -1
  80. package/dist/api/app/bsky/graph/getFollowers.d.ts.map +1 -1
  81. package/dist/api/app/bsky/graph/getFollowers.js +1 -0
  82. package/dist/api/app/bsky/graph/getFollowers.js.map +1 -1
  83. package/dist/api/app/bsky/graph/getFollows.d.ts.map +1 -1
  84. package/dist/api/app/bsky/graph/getFollows.js +1 -0
  85. package/dist/api/app/bsky/graph/getFollows.js.map +1 -1
  86. package/dist/api/app/bsky/graph/getKnownFollowers.d.ts.map +1 -1
  87. package/dist/api/app/bsky/graph/getKnownFollowers.js +1 -0
  88. package/dist/api/app/bsky/graph/getKnownFollowers.js.map +1 -1
  89. package/dist/api/app/bsky/graph/getList.d.ts.map +1 -1
  90. package/dist/api/app/bsky/graph/getList.js +1 -0
  91. package/dist/api/app/bsky/graph/getList.js.map +1 -1
  92. package/dist/api/app/bsky/graph/getListBlocks.d.ts.map +1 -1
  93. package/dist/api/app/bsky/graph/getListBlocks.js +5 -1
  94. package/dist/api/app/bsky/graph/getListBlocks.js.map +1 -1
  95. package/dist/api/app/bsky/graph/getListMutes.d.ts.map +1 -1
  96. package/dist/api/app/bsky/graph/getListMutes.js +5 -1
  97. package/dist/api/app/bsky/graph/getListMutes.js.map +1 -1
  98. package/dist/api/app/bsky/graph/getLists.d.ts.map +1 -1
  99. package/dist/api/app/bsky/graph/getLists.js +1 -0
  100. package/dist/api/app/bsky/graph/getLists.js.map +1 -1
  101. package/dist/api/app/bsky/graph/getListsWithMembership.d.ts.map +1 -1
  102. package/dist/api/app/bsky/graph/getListsWithMembership.js +1 -0
  103. package/dist/api/app/bsky/graph/getListsWithMembership.js.map +1 -1
  104. package/dist/api/app/bsky/graph/getMutes.d.ts.map +1 -1
  105. package/dist/api/app/bsky/graph/getMutes.js +5 -1
  106. package/dist/api/app/bsky/graph/getMutes.js.map +1 -1
  107. package/dist/api/app/bsky/graph/getStarterPack.d.ts.map +1 -1
  108. package/dist/api/app/bsky/graph/getStarterPack.js +1 -0
  109. package/dist/api/app/bsky/graph/getStarterPack.js.map +1 -1
  110. package/dist/api/app/bsky/graph/getStarterPacks.d.ts.map +1 -1
  111. package/dist/api/app/bsky/graph/getStarterPacks.js +1 -0
  112. package/dist/api/app/bsky/graph/getStarterPacks.js.map +1 -1
  113. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.d.ts.map +1 -1
  114. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js +1 -0
  115. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js.map +1 -1
  116. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
  117. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +1 -0
  118. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
  119. package/dist/api/app/bsky/graph/searchStarterPacks.d.ts.map +1 -1
  120. package/dist/api/app/bsky/graph/searchStarterPacks.js +1 -0
  121. package/dist/api/app/bsky/graph/searchStarterPacks.js.map +1 -1
  122. package/dist/api/app/bsky/graph/searchStarterPacksV2.d.ts.map +1 -1
  123. package/dist/api/app/bsky/graph/searchStarterPacksV2.js +1 -0
  124. package/dist/api/app/bsky/graph/searchStarterPacksV2.js.map +1 -1
  125. package/dist/api/app/bsky/labeler/getServices.d.ts.map +1 -1
  126. package/dist/api/app/bsky/labeler/getServices.js +1 -0
  127. package/dist/api/app/bsky/labeler/getServices.js.map +1 -1
  128. package/dist/api/app/bsky/notification/getGroupedNotifications.d.ts.map +1 -1
  129. package/dist/api/app/bsky/notification/getGroupedNotifications.js +41 -20
  130. package/dist/api/app/bsky/notification/getGroupedNotifications.js.map +1 -1
  131. package/dist/api/app/bsky/notification/grouping/algoGravity.d.ts +4 -3
  132. package/dist/api/app/bsky/notification/grouping/algoGravity.d.ts.map +1 -1
  133. package/dist/api/app/bsky/notification/grouping/algoGravity.js +53 -42
  134. package/dist/api/app/bsky/notification/grouping/algoGravity.js.map +1 -1
  135. package/dist/api/app/bsky/notification/grouping/algoLookback.d.ts +4 -3
  136. package/dist/api/app/bsky/notification/grouping/algoLookback.d.ts.map +1 -1
  137. package/dist/api/app/bsky/notification/grouping/algoLookback.js +56 -42
  138. package/dist/api/app/bsky/notification/grouping/algoLookback.js.map +1 -1
  139. package/dist/api/app/bsky/notification/grouping/grouping.d.ts +12 -2
  140. package/dist/api/app/bsky/notification/grouping/grouping.d.ts.map +1 -1
  141. package/dist/api/app/bsky/notification/grouping/grouping.js +20 -6
  142. package/dist/api/app/bsky/notification/grouping/grouping.js.map +1 -1
  143. package/dist/api/app/bsky/notification/grouping/spotlight.d.ts +2 -2
  144. package/dist/api/app/bsky/notification/grouping/spotlight.d.ts.map +1 -1
  145. package/dist/api/app/bsky/notification/grouping/spotlight.js +35 -22
  146. package/dist/api/app/bsky/notification/grouping/spotlight.js.map +1 -1
  147. package/dist/api/app/bsky/notification/listActivitySubscriptions.d.ts.map +1 -1
  148. package/dist/api/app/bsky/notification/listActivitySubscriptions.js +1 -0
  149. package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -1
  150. package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
  151. package/dist/api/app/bsky/notification/listNotifications.js +5 -1
  152. package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
  153. package/dist/api/app/bsky/unspecced/atmosphere-grouping.d.ts +16 -0
  154. package/dist/api/app/bsky/unspecced/atmosphere-grouping.d.ts.map +1 -0
  155. package/dist/api/app/bsky/unspecced/atmosphere-grouping.js +124 -0
  156. package/dist/api/app/bsky/unspecced/atmosphere-grouping.js.map +1 -0
  157. package/dist/api/app/bsky/unspecced/atmosphere-hydration.d.ts +32 -0
  158. package/dist/api/app/bsky/unspecced/atmosphere-hydration.d.ts.map +1 -0
  159. package/dist/api/app/bsky/unspecced/atmosphere-hydration.js +201 -0
  160. package/dist/api/app/bsky/unspecced/atmosphere-hydration.js.map +1 -0
  161. package/dist/api/app/bsky/unspecced/atmosphere-pagination.d.ts +6 -0
  162. package/dist/api/app/bsky/unspecced/atmosphere-pagination.d.ts.map +1 -0
  163. package/dist/api/app/bsky/unspecced/atmosphere-pagination.js +164 -0
  164. package/dist/api/app/bsky/unspecced/atmosphere-pagination.js.map +1 -0
  165. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -1
  166. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js +120 -55
  167. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -1
  168. package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.d.ts +4 -0
  169. package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.d.ts.map +1 -0
  170. package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.js +30 -0
  171. package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.js.map +1 -0
  172. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts.map +1 -1
  173. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js +4 -7
  174. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js.map +1 -1
  175. package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.d.ts.map +1 -1
  176. package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.js +1 -0
  177. package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.js.map +1 -1
  178. package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.d.ts.map +1 -1
  179. package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js +1 -0
  180. package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js.map +1 -1
  181. package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -1
  182. package/dist/api/app/bsky/unspecced/getPostThreadV2.js +1 -0
  183. package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
  184. package/dist/api/app/bsky/unspecced/getSuggestedFeeds.d.ts.map +1 -1
  185. package/dist/api/app/bsky/unspecced/getSuggestedFeeds.js +4 -7
  186. package/dist/api/app/bsky/unspecced/getSuggestedFeeds.js.map +1 -1
  187. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts.map +1 -1
  188. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js +1 -0
  189. package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js.map +1 -1
  190. package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.d.ts.map +1 -1
  191. package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.js +4 -7
  192. package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.js.map +1 -1
  193. package/dist/api/app/bsky/unspecced/getSuggestedUsers.d.ts.map +1 -1
  194. package/dist/api/app/bsky/unspecced/getSuggestedUsers.js +2 -19
  195. package/dist/api/app/bsky/unspecced/getSuggestedUsers.js.map +1 -1
  196. package/dist/api/app/bsky/unspecced/getSuggestedUsersForDiscover.d.ts.map +1 -1
  197. package/dist/api/app/bsky/unspecced/getSuggestedUsersForDiscover.js +1 -0
  198. package/dist/api/app/bsky/unspecced/getSuggestedUsersForDiscover.js.map +1 -1
  199. package/dist/api/app/bsky/unspecced/getSuggestedUsersForExplore.d.ts.map +1 -1
  200. package/dist/api/app/bsky/unspecced/getSuggestedUsersForExplore.js +1 -0
  201. package/dist/api/app/bsky/unspecced/getSuggestedUsersForExplore.js.map +1 -1
  202. package/dist/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.d.ts.map +1 -1
  203. package/dist/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.js +1 -0
  204. package/dist/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.js.map +1 -1
  205. package/dist/api/app/bsky/unspecced/getTrendingTopics.d.ts.map +1 -1
  206. package/dist/api/app/bsky/unspecced/getTrendingTopics.js +6 -2
  207. package/dist/api/app/bsky/unspecced/getTrendingTopics.js.map +1 -1
  208. package/dist/api/app/bsky/unspecced/getTrends.d.ts.map +1 -1
  209. package/dist/api/app/bsky/unspecced/getTrends.js +6 -2
  210. package/dist/api/app/bsky/unspecced/getTrends.js.map +1 -1
  211. package/dist/api/blob-resolver.d.ts.map +1 -1
  212. package/dist/api/blob-resolver.js +9 -8
  213. package/dist/api/blob-resolver.js.map +1 -1
  214. package/dist/api/index.d.ts.map +1 -1
  215. package/dist/api/index.js +2 -0
  216. package/dist/api/index.js.map +1 -1
  217. package/dist/api/internal/bsky/actor/getProfiles.d.ts.map +1 -1
  218. package/dist/api/internal/bsky/actor/getProfiles.js +1 -0
  219. package/dist/api/internal/bsky/actor/getProfiles.js.map +1 -1
  220. package/dist/config.d.ts +2 -6
  221. package/dist/config.d.ts.map +1 -1
  222. package/dist/config.js +7 -16
  223. package/dist/config.js.map +1 -1
  224. package/dist/context.d.ts +0 -2
  225. package/dist/context.d.ts.map +1 -1
  226. package/dist/context.js +0 -3
  227. package/dist/context.js.map +1 -1
  228. package/dist/feature-gates/gates.d.ts +0 -4
  229. package/dist/feature-gates/gates.d.ts.map +1 -1
  230. package/dist/feature-gates/gates.js +0 -4
  231. package/dist/feature-gates/gates.js.map +1 -1
  232. package/dist/hydration/external.d.ts +58 -45
  233. package/dist/hydration/external.d.ts.map +1 -1
  234. package/dist/hydration/external.js +134 -114
  235. package/dist/hydration/external.js.map +1 -1
  236. package/dist/hydration/hydrator.d.ts +15 -9
  237. package/dist/hydration/hydrator.d.ts.map +1 -1
  238. package/dist/hydration/hydrator.js +170 -210
  239. package/dist/hydration/hydrator.js.map +1 -1
  240. package/dist/image/server.d.ts.map +1 -1
  241. package/dist/image/server.js +12 -1
  242. package/dist/image/server.js.map +1 -1
  243. package/dist/index.d.ts.map +1 -1
  244. package/dist/index.js +4 -19
  245. package/dist/index.js.map +1 -1
  246. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts +29 -0
  247. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts.map +1 -1
  248. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js +2 -1
  249. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js.map +1 -1
  250. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.d.ts +3 -0
  251. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.d.ts.map +1 -0
  252. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.d.ts +85 -0
  253. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.d.ts.map +1 -0
  254. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.js +29 -0
  255. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.js.map +1 -0
  256. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.js +6 -0
  257. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.js.map +1 -0
  258. package/dist/lexicons/app/bsky/unspecced.d.ts +1 -0
  259. package/dist/lexicons/app/bsky/unspecced.d.ts.map +1 -1
  260. package/dist/lexicons/app/bsky/unspecced.js +1 -0
  261. package/dist/lexicons/app/bsky/unspecced.js.map +1 -1
  262. package/dist/lexicons/tools/ozone/moderation/emitEvent.defs.d.ts +2 -2
  263. package/dist/lexicons/tools/ozone/moderation/emitEvent.defs.js.map +1 -1
  264. package/dist/lexicons/tools/ozone/report/closeReports.defs.d.ts +1 -1
  265. package/dist/lexicons/tools/ozone/report/closeReports.defs.d.ts.map +1 -1
  266. package/dist/lexicons/tools/ozone/report/closeReports.defs.js +1 -1
  267. package/dist/lexicons/tools/ozone/report/closeReports.defs.js.map +1 -1
  268. package/dist/telemetry/events.d.ts +14 -6
  269. package/dist/telemetry/events.d.ts.map +1 -1
  270. package/dist/telemetry/events.js +40 -5
  271. package/dist/telemetry/events.js.map +1 -1
  272. package/dist/util/external/standard-site.d.ts +2 -3
  273. package/dist/util/external/standard-site.d.ts.map +1 -1
  274. package/dist/util/external/standard-site.js +2 -3
  275. package/dist/util/external/standard-site.js.map +1 -1
  276. package/dist/util/uris.d.ts +5 -2
  277. package/dist/util/uris.d.ts.map +1 -1
  278. package/dist/util/uris.js +14 -1
  279. package/dist/util/uris.js.map +1 -1
  280. package/dist/views/external.d.ts +5 -0
  281. package/dist/views/external.d.ts.map +1 -1
  282. package/dist/views/external.js +38 -11
  283. package/dist/views/external.js.map +1 -1
  284. package/dist/views/index.d.ts +2 -2
  285. package/dist/views/index.d.ts.map +1 -1
  286. package/dist/views/index.js +17 -11
  287. package/dist/views/index.js.map +1 -1
  288. package/package.json +13 -14
@@ -3,19 +3,29 @@ import { l } from '@atproto/lex';
3
3
  import { AtUri } from '@atproto/syntax';
4
4
  import { ForbiddenError, MethodNotImplementedError, UpstreamFailureError, UpstreamTimeoutError, } from '@atproto/xrpc-server';
5
5
  import { safeFetchWrap } from '@atproto-labs/fetch-node';
6
+ import { mergeStates, } from '../../../../hydration/hydrator.js';
6
7
  import { app, site } from '../../../../lexicons/index.js';
7
8
  import { httpLogger } from '../../../../logger.js';
8
9
  import { createPipeline, noRules, } from '../../../../pipeline.js';
9
10
  import { getAtprotoPassthroughHeaders } from '../../../../util/headers.js';
10
11
  const MAX_CONFIG_BYTES = 512 * 1024;
11
12
  const MAX_LIST_ITEMS = 10;
12
- const groupSchema = l.object({
13
+ const groupFields = {
13
14
  canonicalUri: l.optional(l.unknown()),
14
- startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),
15
- endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),
16
15
  tags: l.optional(l.array(l.string({ maxLength: 128 }), { maxLength: 100 })),
17
16
  locations: l.optional(l.array(l.string({ maxLength: 16 }), { maxLength: 100 })),
17
+ };
18
+ const groupSchema = l.object(groupFields);
19
+ // Only livestreams are scheduled; other groups ignore any schedule fields.
20
+ const optionalDatetime = l.optional(l.nullable(l.string({ format: 'datetime' })));
21
+ const livestreamSchema = l.object({
22
+ ...groupFields,
23
+ previewAt: optionalDatetime,
24
+ startsAt: optionalDatetime,
25
+ endsAt: optionalDatetime,
18
26
  });
27
+ // `did` hydrates the app's profile; `null` and absent both mean no profile.
28
+ const appDidSchema = l.optional(l.nullable(l.string({ format: 'did' })));
19
29
  const bannerSchema = l.object({
20
30
  status: l.string({ maxLength: 128 }),
21
31
  startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),
@@ -111,7 +121,7 @@ async function skeleton({ ctx, params, }) {
111
121
  ]);
112
122
  const now = Date.now();
113
123
  const announcementBanner = selectBanner(config.announcementBanners, params, now);
114
- const select = createSelector(params, params.requestHeaders, now);
124
+ const select = createSelector(params, params.requestHeaders);
115
125
  return {
116
126
  now,
117
127
  announcementBanner,
@@ -120,15 +130,20 @@ async function skeleton({ ctx, params, }) {
120
130
  const root = recordUri(uri);
121
131
  return root &&
122
132
  new AtUri(root).collection === site.standard.document.$type
123
- ? { featured: false, uri: root }
133
+ ? { uri: root }
124
134
  : undefined;
125
135
  })
126
136
  : completeGroups(select(config.articles))),
127
137
  articleRecIdStr: recommendations?.recIdStr,
128
138
  publications: orderGroups(completeGroups(select(config.publications))),
129
139
  photos: orderGroups(completeGroups(select(config.photos))),
130
- livestreams: orderGroups(completeGroups(select(config.livestreams))),
131
- // Apps have no hydration root or featured flag: keep worker order.
140
+ // Expired and not-yet-previewed livestreams are dropped before selection,
141
+ // so they never consume the cap.
142
+ livestreams: orderGroups(mapDefined(select(config.livestreams.filter((entry) => isPreviewable(entry, now))), ({ canonicalUri, startsAt }) => {
143
+ const uri = recordUri(canonicalUri);
144
+ return uri ? { uri, startsAt } : undefined;
145
+ })),
146
+ // Apps are not record-backed: keep worker order.
132
147
  apps: config.apps.slice(0, MAX_LIST_ITEMS),
133
148
  };
134
149
  }
@@ -141,49 +156,74 @@ async function hydration({ ctx, params, skeleton, }) {
141
156
  ...skeleton.livestreams,
142
157
  ].map(({ uri }) => uri)),
143
158
  ];
144
- return ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx);
159
+ const dids = [...new Set(mapDefined(skeleton.apps, ({ did }) => did))];
160
+ const [externalState, appProfileState] = await Promise.all([
161
+ ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx),
162
+ dids.length
163
+ ? ctx.hydrator.hydrateProfilesDetailed(dids, params.hydrateCtx)
164
+ : {},
165
+ ]);
166
+ // Detailed profile state goes second so it wins any overlapping actor state.
167
+ return mergeStates(externalState, appProfileState);
145
168
  }
146
169
  function presentation({ ctx, skeleton, hydration: state, }) {
147
- const { now, articles, publications, photos, livestreams } = skeleton;
170
+ const { now, articles, publications, photos, livestreams, apps } = skeleton;
148
171
  return {
149
172
  announcementBanner: skeleton.announcementBanner,
150
173
  articles: {
151
- items: mapDefined(articles, ({ featured, uri }) => {
174
+ items: mapDefined(articles, ({ uri }) => {
152
175
  const view = ctx.views.externalRecordView(uri, state, now);
153
176
  return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)
154
- ? { featured, view }
177
+ ? { featured: false, view }
155
178
  : undefined;
156
179
  }),
157
180
  recIdStr: skeleton.articleRecIdStr,
158
181
  },
159
182
  publications: {
160
- items: mapDefined(publications, ({ featured, uri }) => {
183
+ items: mapDefined(publications, ({ uri }) => {
161
184
  const view = ctx.views.externalRecordView(uri, state, now);
162
185
  return view &&
163
186
  app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)
164
- ? { featured, view }
187
+ ? { featured: false, view }
165
188
  : undefined;
166
189
  }),
167
190
  },
168
191
  photos: {
169
- items: mapDefined(photos, ({ featured, uri }) => {
192
+ items: mapDefined(photos, ({ uri }) => {
170
193
  const view = ctx.views.externalRecordView(uri, state, now);
171
194
  return view &&
172
195
  app.bsky.embed.external.viewGallery.$isTypeOf(view) &&
173
196
  view.items.length
174
- ? { featured, view }
197
+ ? { featured: false, view }
175
198
  : undefined;
176
199
  }),
177
200
  },
178
201
  livestreams: {
179
- items: mapDefined(livestreams, ({ featured, uri }) => {
202
+ items: mapDefined(livestreams, ({ uri, startsAt }) => {
180
203
  const view = ctx.views.externalRecordView(uri, state, now);
181
- return view && app.bsky.embed.external.viewLivestream.$isTypeOf(view)
182
- ? { featured, view }
183
- : undefined;
204
+ if (!view || !app.bsky.embed.external.viewLivestream.$isTypeOf(view))
205
+ return;
206
+ // Previewed streams are never live; copy rather than mutate the view.
207
+ return startsAt != null && Date.parse(startsAt) > now
208
+ ? { featured: false, view: { ...view, active: false } }
209
+ : { featured: false, view };
184
210
  }),
185
211
  },
186
- apps: skeleton.apps,
212
+ apps: apps.map(({ card, did }) => {
213
+ const profile = did ? ctx.views.profileDetailed(did, state) : undefined;
214
+ return {
215
+ id: card.id,
216
+ title: card.title,
217
+ description: card.description,
218
+ logo: profile?.avatar ?? card.logo,
219
+ backgroundImage: profile?.banner ?? card.backgroundImage,
220
+ overlayColor: card.overlayColor,
221
+ textColor: card.textColor,
222
+ url: card.url,
223
+ category: card.category,
224
+ profile,
225
+ };
226
+ }),
187
227
  };
188
228
  }
189
229
  function createFetchExploreTabSkeleton() {
@@ -240,13 +280,44 @@ function normalizeConfig(body) {
240
280
  articles: valid(groupSchema, config.articles),
241
281
  publications: valid(groupSchema, config.publications),
242
282
  photos: valid(groupSchema, config.photos),
243
- livestreams: valid(groupSchema, config.livestreams),
244
- apps: valid(app.bsky.unspecced.getAtmosphereExploreTab.appCard, config.apps),
283
+ livestreams: valid(livestreamSchema, config.livestreams),
284
+ apps: entries(config.apps).flatMap((entry) => {
285
+ const parsed = parseApp(entry);
286
+ return parsed ? [parsed] : [];
287
+ }),
245
288
  };
246
289
  }
290
+ const APP_CARD_FIELDS = [
291
+ 'id',
292
+ 'title',
293
+ 'description',
294
+ 'logo',
295
+ 'backgroundImage',
296
+ 'overlayColor',
297
+ 'textColor',
298
+ 'url',
299
+ 'category',
300
+ ];
301
+ /**
302
+ * Validates an app entry. Only the public card fields are copied (so a
303
+ * supplied `profile` or `profileUrl` is never read) and only `did` is kept
304
+ * beyond them. A present but malformed `did` drops the app.
305
+ */
306
+ function parseApp(entry) {
307
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
308
+ return;
309
+ const raw = entry;
310
+ const did = appDidSchema.safeParse(raw.did);
311
+ if (!did.success)
312
+ return;
313
+ const card = app.bsky.unspecced.getAtmosphereExploreTab.appCard.safeParse(noUndefinedVals(Object.fromEntries(APP_CARD_FIELDS.map((key) => [key, raw[key]]))));
314
+ if (!card.success)
315
+ return;
316
+ return { card: card.value, did: did.value ?? undefined };
317
+ }
247
318
  /**
248
- * Randomizes a selected list (Fisher-Yates), then places featured items ahead
249
- * of ordinary ones. Runs before hydration, so presentation keeps this order.
319
+ * Randomizes a selected list (Fisher-Yates) and caps it. Runs before hydration,
320
+ * so presentation keeps this order.
250
321
  */
251
322
  function orderGroups(groups) {
252
323
  const shuffled = [...groups];
@@ -254,19 +325,25 @@ function orderGroups(groups) {
254
325
  const j = Math.floor(Math.random() * (i + 1));
255
326
  [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
256
327
  }
257
- return [
258
- ...shuffled.filter(({ featured }) => featured),
259
- ...shuffled.filter(({ featured }) => !featured),
260
- ].slice(0, MAX_LIST_ITEMS);
328
+ return shuffled.slice(0, MAX_LIST_ITEMS);
261
329
  }
262
330
  function completeGroups(groups = []) {
263
- return mapDefined(groups, ({ featured, canonicalUri }) => {
331
+ return mapDefined(groups, ({ canonicalUri }) => {
264
332
  const uri = recordUri(canonicalUri);
265
- if (!uri)
266
- return;
267
- return { featured, uri };
333
+ return uri ? { uri } : undefined;
268
334
  });
269
335
  }
336
+ /**
337
+ * Editorial window for a livestream: shown from `previewAt` (falling back to
338
+ * `startsAt`) inclusive until `endsAt` exclusive. Absent or null bounds are
339
+ * open. This is independent of the view's heartbeat-based `active` flag.
340
+ */
341
+ function isPreviewable({ previewAt, startsAt, endsAt }, now) {
342
+ const start = previewAt ?? startsAt;
343
+ if (start != null && Date.parse(start) > now)
344
+ return false;
345
+ return endsAt == null || now < Date.parse(endsAt);
346
+ }
270
347
  /**
271
348
  * Returns the single (already validated) banner to show. Eligible (enabled, in window) banners targeted at a matching location
272
349
  * win, first in worker order; otherwise the unrestricted eligible banner with
@@ -304,11 +381,9 @@ function selectBanner(banners, params, now) {
304
381
  /**
305
382
  * Builds a per-list selector that drops location-ineligible entries, orders
306
383
  * the rest by priority bucket (location, requested topic, `general`, untagged;
307
- * stable within a bucket), moves entries with an active schedule window ahead
308
- * of the rest (stable, so bucket order holds within each side), and keeps the
309
- * first `MAX_LIST_ITEMS`. A worker-supplied `featured` flag is ignored.
384
+ * stable within a bucket), and keeps the first `MAX_LIST_ITEMS`.
310
385
  */
311
- function createSelector(params, headers, now) {
386
+ function createSelector(params, headers) {
312
387
  const topics = parseTopics(headers['x-atproto-bsky-topics']);
313
388
  return (groups = []) => {
314
389
  const buckets = [[], [], [], []];
@@ -317,22 +392,9 @@ function createSelector(params, headers, now) {
317
392
  if (bucket !== undefined)
318
393
  buckets[bucket].push(group);
319
394
  }
320
- const featured = [];
321
- const ordinary = [];
322
- for (const group of buckets.flat()) {
323
- const active = isFeatured(group, now);
324
- (active ? featured : ordinary).push({ ...group, featured: active });
325
- }
326
- return [...featured, ...ordinary].slice(0, MAX_LIST_ITEMS);
395
+ return buckets.flat().slice(0, MAX_LIST_ITEMS);
327
396
  };
328
397
  }
329
- /** Unlike banners, content needs both bounds to define a featured window. */
330
- function isFeatured({ startsAt, endsAt }, now) {
331
- return (startsAt != null &&
332
- endsAt != null &&
333
- Date.parse(startsAt) <= now &&
334
- now < Date.parse(endsAt));
335
- }
336
398
  function selectionBucket({ tags, locations }, params, topics) {
337
399
  if (locations?.length) {
338
400
  if (!matchesLocation(locations, params))
@@ -348,11 +410,14 @@ function selectionBucket({ tags, locations }, params, topics) {
348
410
  }
349
411
  function parseTopics(header) {
350
412
  const topics = new Set();
351
- const value = Array.isArray(header) ? header.join(',') : header;
352
- for (const token of value?.split(',') ?? []) {
353
- const topic = token.trim();
354
- if (topic)
355
- topics.add(topic);
413
+ for (const value of Array.isArray(header) ? header : [header ?? '']) {
414
+ // @NOTE Clients may append `;<updatedAt>` metadata to the tag list. Ignore it
415
+ // for matching; the raw header is still forwarded to Iris.
416
+ for (const token of value.split(';', 1)[0].split(',')) {
417
+ const topic = token.trim();
418
+ if (topic)
419
+ topics.add(topic);
420
+ }
356
421
  }
357
422
  return topics;
358
423
  }
@@ -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 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"]}
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, type DidString, 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 {\n type HydrateCtx,\n type Hydrator,\n mergeStates,\n} 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 groupFields = {\n canonicalUri: l.optional(l.unknown()),\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 groupSchema = l.object(groupFields)\n// Only livestreams are scheduled; other groups ignore any schedule fields.\nconst optionalDatetime = l.optional(\n l.nullable(l.string({ format: 'datetime' })),\n)\nconst livestreamSchema = l.object({\n ...groupFields,\n previewAt: optionalDatetime,\n startsAt: optionalDatetime,\n endsAt: optionalDatetime,\n})\n// `did` hydrates the app's profile; `null` and absent both mean no profile.\nconst appDidSchema = l.optional(l.nullable(l.string({ format: 'did' })))\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)\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 ? { 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 // Expired and not-yet-previewed livestreams are dropped before selection,\n // so they never consume the cap.\n livestreams: orderGroups(\n mapDefined(\n select(config.livestreams.filter((entry) => isPreviewable(entry, now))),\n ({ canonicalUri, startsAt }) => {\n const uri = recordUri(canonicalUri)\n return uri ? { uri, startsAt } : undefined\n },\n ),\n ),\n // Apps are not record-backed: 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 const dids = [...new Set(mapDefined(skeleton.apps, ({ did }) => did))]\n const [externalState, appProfileState] = await Promise.all([\n ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx),\n dids.length\n ? ctx.hydrator.hydrateProfilesDetailed(dids, params.hydrateCtx)\n : {},\n ])\n // Detailed profile state goes second so it wins any overlapping actor state.\n return mergeStates(externalState, appProfileState)\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, apps } = skeleton\n return {\n announcementBanner: skeleton.announcementBanner,\n articles: {\n items: mapDefined(articles, ({ uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)\n ? { featured: false, view }\n : undefined\n }),\n recIdStr: skeleton.articleRecIdStr,\n },\n publications: {\n items: mapDefined(publications, ({ uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)\n ? { featured: false, view }\n : undefined\n }),\n },\n photos: {\n items: mapDefined(photos, ({ 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: false, view }\n : undefined\n }),\n },\n livestreams: {\n items: mapDefined(livestreams, ({ uri, startsAt }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n if (!view || !app.bsky.embed.external.viewLivestream.$isTypeOf(view))\n return\n // Previewed streams are never live; copy rather than mutate the view.\n return startsAt != null && Date.parse(startsAt) > now\n ? { featured: false, view: { ...view, active: false } }\n : { featured: false, view }\n }),\n },\n apps: apps.map(({ card, did }) => {\n const profile = did ? ctx.views.profileDetailed(did, state) : undefined\n return {\n id: card.id,\n title: card.title,\n description: card.description,\n logo: profile?.avatar ?? card.logo,\n backgroundImage: profile?.banner ?? card.backgroundImage,\n overlayColor: card.overlayColor,\n textColor: card.textColor,\n url: card.url,\n category: card.category,\n profile,\n }\n }),\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 LivestreamGroup = l.Infer<typeof livestreamSchema>\ntype AppCard = app.bsky.unspecced.getAtmosphereExploreTab.AppCard\n// A validated public card plus the internal DID used to hydrate its profile.\ntype AppEntry = { card: AppCard; did?: DidString }\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: LivestreamGroup[]\n apps: AppEntry[]\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(livestreamSchema, config.livestreams),\n apps: entries(config.apps).flatMap((entry) => {\n const parsed = parseApp(entry)\n return parsed ? [parsed] : []\n }),\n }\n}\n\nconst APP_CARD_FIELDS = [\n 'id',\n 'title',\n 'description',\n 'logo',\n 'backgroundImage',\n 'overlayColor',\n 'textColor',\n 'url',\n 'category',\n] as const\n\n/**\n * Validates an app entry. Only the public card fields are copied (so a\n * supplied `profile` or `profileUrl` is never read) and only `did` is kept\n * beyond them. A present but malformed `did` drops the app.\n */\nfunction parseApp(entry: unknown): AppEntry | undefined {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry))\n return\n const raw = entry as Record<string, unknown>\n const did = appDidSchema.safeParse(raw.did)\n if (!did.success) return\n const card = app.bsky.unspecced.getAtmosphereExploreTab.appCard.safeParse(\n noUndefinedVals(\n Object.fromEntries(APP_CARD_FIELDS.map((key) => [key, raw[key]])),\n ),\n )\n if (!card.success) return\n return { card: card.value, did: did.value ?? undefined }\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 = { uri: AtUriString }\ntype LivestreamRecordGroup = RecordGroup & { startsAt?: string | null }\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: LivestreamRecordGroup[]\n apps: AppEntry[]\n}\n\n/**\n * Randomizes a selected list (Fisher-Yates) and caps it. Runs before hydration,\n * so presentation keeps this order.\n */\nfunction orderGroups<T extends RecordGroup>(groups: T[]): T[] {\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 shuffled.slice(0, MAX_LIST_ITEMS)\n}\n\nfunction completeGroups(groups: Group[] = []): RecordGroup[] {\n return mapDefined(groups, ({ canonicalUri }) => {\n const uri = recordUri(canonicalUri)\n return uri ? { uri } : undefined\n })\n}\n\n/**\n * Editorial window for a livestream: shown from `previewAt` (falling back to\n * `startsAt`) inclusive until `endsAt` exclusive. Absent or null bounds are\n * open. This is independent of the view's heartbeat-based `active` flag.\n */\nfunction isPreviewable(\n { previewAt, startsAt, endsAt }: LivestreamGroup,\n now: number,\n): boolean {\n const start = previewAt ?? startsAt\n if (start != null && Date.parse(start) > now) return false\n return endsAt == null || now < Date.parse(endsAt)\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}\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), and keeps the first `MAX_LIST_ITEMS`.\n */\nfunction createSelector(\n params: { countryCode?: string; regionCode?: string },\n headers: IncomingHttpHeaders,\n) {\n const topics = parseTopics(headers['x-atproto-bsky-topics'])\n return <T extends Group>(groups: T[] = []): T[] => {\n const buckets: T[][] = [[], [], [], []]\n for (const group of groups) {\n const bucket = selectionBucket(group, params, topics)\n if (bucket !== undefined) buckets[bucket].push(group)\n }\n return buckets.flat().slice(0, MAX_LIST_ITEMS)\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 for (const value of Array.isArray(header) ? header : [header ?? '']) {\n // @NOTE Clients may append `;<updatedAt>` metadata to the tag list. Ignore it\n // for matching; the raw header is still forwarded to Iris.\n for (const token of value.split(';', 1)[0].split(',')) {\n const topic = token.trim()\n if (topic) topics.add(topic)\n }\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"]}
@@ -0,0 +1,4 @@
1
+ import { type Server } from '@atproto/xrpc-server';
2
+ import type { AppContext } from '../../../../context.js';
3
+ export default function (server: Server, ctx: AppContext): void;
4
+ //# sourceMappingURL=getAtmosphereTimeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getAtmosphereTimeline.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereTimeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAKxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA+BvD"}
@@ -0,0 +1,30 @@
1
+ import { ForbiddenError } from '@atproto/xrpc-server';
2
+ import { app } from '../../../../lexicons/index.js';
3
+ import { resHeaders } from '../../../util.js';
4
+ import { loadAtmosphereTimeline } from './atmosphere-pagination.js';
5
+ export default function (server, ctx) {
6
+ server.add(app.bsky.unspecced.getAtmosphereTimeline, {
7
+ auth: ctx.authVerifier.standard,
8
+ handler: async ({ auth, params, req }) => {
9
+ const now = Date.now();
10
+ const viewer = auth.credentials.iss;
11
+ const features = ctx.featureGatesClient.scope(ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }));
12
+ if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {
13
+ throw new ForbiddenError('Atmosphere timeline is not enabled');
14
+ }
15
+ const hydrateCtx = await ctx.hydrator.createContext({
16
+ viewer,
17
+ features,
18
+ labelers: ctx.reqLabelers(req),
19
+ xrpcEndpoint: app.bsky.unspecced.getAtmosphereTimeline.$lxm,
20
+ });
21
+ const body = await loadAtmosphereTimeline(ctx, hydrateCtx, params.cursor, now);
22
+ return {
23
+ encoding: 'application/json',
24
+ body,
25
+ headers: resHeaders({ labelers: hydrateCtx.labelers }),
26
+ };
27
+ },
28
+ });
29
+ }
30
+ //# sourceMappingURL=getAtmosphereTimeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getAtmosphereTimeline.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereTimeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAe,MAAM,sBAAsB,CAAA;AAElE,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAEnE,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE;QACnD,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,QAAQ;QAC/B,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAC3C,GAAG,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CACpE,CAAA;YACD,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,+BAA+B,CAAC,EAAE,CAAC;gBACvE,MAAM,IAAI,cAAc,CAAC,oCAAoC,CAAC,CAAA;YAChE,CAAC;YACD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,MAAM;gBACN,QAAQ;gBACR,QAAQ,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC;gBAC9B,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,IAAI;aAC5D,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,MAAM,sBAAsB,CACvC,GAAG,EACH,UAAU,EACV,MAAM,CAAC,MAAM,EACb,GAAG,CACJ,CAAA;YACD,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI;gBACJ,OAAO,EAAE,UAAU,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;aACvD,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { ForbiddenError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { resHeaders } from '../../../util.js'\nimport { loadAtmosphereTimeline } from './atmosphere-pagination.js'\n\nexport default function (server: Server, ctx: AppContext) {\n server.add(app.bsky.unspecced.getAtmosphereTimeline, {\n auth: ctx.authVerifier.standard,\n handler: async ({ auth, params, req }) => {\n const now = Date.now()\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 timeline is not enabled')\n }\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n features,\n labelers: ctx.reqLabelers(req),\n xrpcEndpoint: app.bsky.unspecced.getAtmosphereTimeline.$lxm,\n })\n const body = await loadAtmosphereTimeline(\n ctx,\n hydrateCtx,\n params.cursor,\n now,\n )\n return {\n encoding: 'application/json',\n body,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getOnboardingSuggestedStarterPacks.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6B,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAiBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAsCvD"}
1
+ {"version":3,"file":"getOnboardingSuggestedStarterPacks.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6B,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAiBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqCvD"}
@@ -13,9 +13,9 @@ export default function (server, ctx) {
13
13
  const viewer = auth.credentials.iss;
14
14
  const labelers = ctx.reqLabelers(req);
15
15
  const hydrateCtx = await ctx.hydrator.createContext({
16
+ xrpcEndpoint: app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$lxm,
16
17
  labelers,
17
18
  viewer,
18
- features: ctx.featureGatesClient.scope(ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req })),
19
19
  });
20
20
  const headers = noUndefinedVals({
21
21
  'accept-language': req.headers['accept-language'],
@@ -36,14 +36,11 @@ export default function (server, ctx) {
36
36
  }
37
37
  const skeleton = async (input) => {
38
38
  const { params, ctx } = input;
39
- const useIris = params.hydrateCtx.features.checkGate(params.hydrateCtx.features.Gate.SuggestedStarterPacksOnboardingV2Enable);
40
- const client = useIris ? ctx.irisClient : ctx.topicsClient;
41
- if (!client) {
39
+ if (!ctx.irisClient) {
42
40
  // Use 501 instead of 500 as these are not considered retry-able by clients
43
- const agent = useIris ? 'Iris' : 'Topics';
44
- throw new MethodNotImplementedError(`${agent} agent not available`);
41
+ throw new MethodNotImplementedError('Iris agent not available');
45
42
  }
46
- const skeleton = await client.call(app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton, { limit: params.limit, viewer: params.hydrateCtx.viewer ?? undefined }, { headers: params.headers, signal: params.signal });
43
+ const skeleton = await ctx.irisClient.call(app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton, { limit: params.limit, viewer: params.hydrateCtx.viewer ?? undefined }, { headers: params.headers, signal: params.signal });
47
44
  // @TODO Make sure upstream always provides this
48
45
  skeleton.starterPacks ??= [];
49
46
  return skeleton;
@@ -1 +1 @@
1
- {"version":3,"file":"getOnboardingSuggestedStarterPacks.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE7D,OAAO,EAAE,KAAK,EAAoC,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAe,MAAM,sBAAsB,CAAA;AAE7E,OAAO,EAGL,eAAe,GAChB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACnD,OAAO,EAKL,cAAc,GACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAA;AAG1E,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,kCAAkC,GAAG,cAAc,CACvD,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAY,CACb,CAAA;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,kCAAkC,EAAE;QAChE,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,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;YACrC,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,QAAQ;gBACR,MAAM;gBACN,QAAQ,EAAE,GAAG,CAAC,kBAAkB,CAAC,KAAK,CACpC,GAAG,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CACpE;aACF,CAAC,CAAA;YACF,MAAM,OAAO,GAAG,eAAe,CAAC;gBAC9B,iBAAiB,EAAE,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC;gBACjD,GAAG,4BAA4B,CAAC,GAAG,CAAC;aACrC,CAAC,CAAA;YACF,MAAM,MAAM,GAAG,MAAM,kCAAkC,CACrD;gBACE,GAAG,MAAM;gBACT,UAAU;gBACV,OAAO;gBACP,MAAM;aACP,EACD,GAAG,CACJ,CAAA;YACD,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,MAAM;aACb,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,QAAQ,GAA+C,KAAK,EAChE,KAAK,EACmB,EAAE;IAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;IAE7B,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAClD,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,uCAAuC,CACxE,CAAA;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAA;IAE1D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,2EAA2E;QAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAA;QACzC,MAAM,IAAI,yBAAyB,CAAC,GAAG,KAAK,sBAAsB,CAAC,CAAA;IACrE,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAChC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,0CAA0C,EAC7D,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,SAAS,EAAE,EACtE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CACnD,CAAA;IAED,gDAAgD;IAChD,QAAQ,CAAC,YAAY,KAAK,EAAE,CAAA;IAE5B,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,SAAS,GAAgD,KAAK,EAClE,KAAK,EACL,EAAE;IACF,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAEvC,MAAM,KAAK,GAAgC,IAAI,GAAG,EAAE,CAAA;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAA;IACvC,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,6BAA6B,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAA;IACzE,CAAC;IACD,MAAM,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACjE,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC;QAC1E,GAAG,CAAC,QAAQ,CAAC,0BAA0B,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC;KAClE,CAAC,CAAA;IAEF,OAAO,eAAe,CAAC,iBAAiB,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAA;AACpE,CAAC,CAAA;AAED,MAAM,QAAQ,GAA4C,CAAC,KAAK,EAAE,EAAE;IAClE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAA;IACvC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzD,MAAM,gBAAgB,GAAkB;QACtC,GAAG,QAAQ;QACX,YAAY,EAAE,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YACjD,IAAI,CAAC;gBACH,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC,CAAC;KACH,CAAA;IAED,OAAO,gBAAgB,CAAA;AACzB,CAAC,CAAA;AAED,MAAM,YAAY,GAKd,CAAC,KAAK,EAAE,EAAE;IACZ,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAE1C,OAAO;QACL,YAAY,EAAE,UAAU,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CACtD,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CACtC;QACD,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAA;AACH,CAAC,CAAA;AAkBD,SAAS,6BAA6B,CACpC,YAA4B;IAE5B,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,CAAA;IAE5B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAa,CAAA;IAEjC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC","sourcesContent":["import { mapDefined, noUndefinedVals } from '@atproto/common'\nimport type { Client } from '@atproto/lex'\nimport { AtUri, type AtUriString, type DidString } from '@atproto/syntax'\nimport { MethodNotImplementedError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport {\n type HydrateCtx,\n type Hydrator,\n mergeManyStates,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFn,\n type PresentationFn,\n type RulesFn,\n type SkeletonFn,\n createPipeline,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getOnboardingSuggestedStarterPacks = createPipeline(\n skeleton,\n hydration,\n noBlocks,\n presentation,\n )\n server.add(app.bsky.unspecced.getOnboardingSuggestedStarterPacks, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n features: ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n ),\n })\n const headers = noUndefinedVals({\n 'accept-language': req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n })\n const result = await getOnboardingSuggestedStarterPacks(\n {\n ...params,\n hydrateCtx,\n headers,\n signal,\n },\n ctx,\n )\n return {\n encoding: 'application/json',\n body: result,\n }\n },\n })\n}\n\nconst skeleton: SkeletonFn<Context, Params, SkeletonState> = async (\n input,\n): Promise<SkeletonState> => {\n const { params, ctx } = input\n\n const useIris = params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.SuggestedStarterPacksOnboardingV2Enable,\n )\n const client = useIris ? ctx.irisClient : ctx.topicsClient\n\n if (!client) {\n // Use 501 instead of 500 as these are not considered retry-able by clients\n const agent = useIris ? 'Iris' : 'Topics'\n throw new MethodNotImplementedError(`${agent} agent not available`)\n }\n\n const skeleton = await client.call(\n app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton,\n { limit: params.limit, viewer: params.hydrateCtx.viewer ?? undefined },\n { headers: params.headers, signal: params.signal },\n )\n\n // @TODO Make sure upstream always provides this\n skeleton.starterPacks ??= []\n\n return skeleton\n}\n\nconst hydration: HydrationFn<Context, Params, SkeletonState> = async (\n input,\n) => {\n const { ctx, params, skeleton } = input\n\n const pairs: Map<DidString, DidString[]> = new Map()\n const viewer = params.hydrateCtx.viewer\n if (viewer) {\n pairs.set(viewer, getUniqueDidsFromStarterPacks(skeleton.starterPacks))\n }\n const [starterPacksState, bidirectionalBlocks] = await Promise.all([\n ctx.hydrator.hydrateStarterPacks(skeleton.starterPacks, params.hydrateCtx),\n ctx.hydrator.hydrateBidirectionalBlocks(pairs, params.hydrateCtx),\n ])\n\n return mergeManyStates(starterPacksState, { bidirectionalBlocks })\n}\n\nconst noBlocks: RulesFn<Context, Params, SkeletonState> = (input) => {\n const { skeleton, params, hydration } = input\n const viewer = params.hydrateCtx.viewer\n if (!viewer) {\n return skeleton\n }\n\n const blocks = hydration.bidirectionalBlocks?.get(viewer)\n const filteredSkeleton: SkeletonState = {\n ...skeleton,\n starterPacks: skeleton.starterPacks.filter((uri) => {\n try {\n return !blocks?.get(new AtUri(uri).did)\n } catch {\n return false\n }\n }),\n }\n\n return filteredSkeleton\n}\n\nconst presentation: PresentationFn<\n Context,\n Params,\n SkeletonState,\n app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$OutputBody\n> = (input) => {\n const { ctx, skeleton, hydration } = input\n\n return {\n starterPacks: mapDefined(skeleton.starterPacks, (uri) =>\n ctx.views.starterPack(uri, hydration),\n ),\n recIdStr: skeleton.recIdStr,\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n topicsClient: Client | undefined\n irisClient: Client | undefined\n}\n\ntype Params = app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$Params & {\n hydrateCtx: HydrateCtx\n headers: Record<string, string>\n signal: AbortSignal\n}\n\ntype SkeletonState =\n app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton.$OutputBody\n\nfunction getUniqueDidsFromStarterPacks(\n starterPacks?: AtUriString[],\n): DidString[] {\n if (!starterPacks) return []\n\n const dids = new Set<DidString>()\n\n for (const uri of starterPacks) {\n try {\n dids.add(new AtUri(uri).did)\n } catch {\n continue\n }\n }\n\n return Array.from(dids)\n}\n"]}
1
+ {"version":3,"file":"getOnboardingSuggestedStarterPacks.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined, noUndefinedVals } from '@atproto/common'\nimport type { Client } from '@atproto/lex'\nimport { AtUri, type AtUriString, type DidString } from '@atproto/syntax'\nimport { MethodNotImplementedError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport {\n type HydrateCtx,\n type Hydrator,\n mergeManyStates,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFn,\n type PresentationFn,\n type RulesFn,\n type SkeletonFn,\n createPipeline,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getOnboardingSuggestedStarterPacks = createPipeline(\n skeleton,\n hydration,\n noBlocks,\n presentation,\n )\n server.add(app.bsky.unspecced.getOnboardingSuggestedStarterPacks, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n xrpcEndpoint:\n app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$lxm,\n labelers,\n viewer,\n })\n const headers = noUndefinedVals({\n 'accept-language': req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n })\n const result = await getOnboardingSuggestedStarterPacks(\n {\n ...params,\n hydrateCtx,\n headers,\n signal,\n },\n ctx,\n )\n return {\n encoding: 'application/json',\n body: result,\n }\n },\n })\n}\n\nconst skeleton: SkeletonFn<Context, Params, SkeletonState> = async (\n input,\n): Promise<SkeletonState> => {\n const { params, ctx } = input\n\n if (!ctx.irisClient) {\n // Use 501 instead of 500 as these are not considered retry-able by clients\n throw new MethodNotImplementedError('Iris agent not available')\n }\n\n const skeleton = await ctx.irisClient.call(\n app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton,\n { limit: params.limit, viewer: params.hydrateCtx.viewer ?? undefined },\n { headers: params.headers, signal: params.signal },\n )\n\n // @TODO Make sure upstream always provides this\n skeleton.starterPacks ??= []\n\n return skeleton\n}\n\nconst hydration: HydrationFn<Context, Params, SkeletonState> = async (\n input,\n) => {\n const { ctx, params, skeleton } = input\n\n const pairs: Map<DidString, DidString[]> = new Map()\n const viewer = params.hydrateCtx.viewer\n if (viewer) {\n pairs.set(viewer, getUniqueDidsFromStarterPacks(skeleton.starterPacks))\n }\n const [starterPacksState, bidirectionalBlocks] = await Promise.all([\n ctx.hydrator.hydrateStarterPacks(skeleton.starterPacks, params.hydrateCtx),\n ctx.hydrator.hydrateBidirectionalBlocks(pairs, params.hydrateCtx),\n ])\n\n return mergeManyStates(starterPacksState, { bidirectionalBlocks })\n}\n\nconst noBlocks: RulesFn<Context, Params, SkeletonState> = (input) => {\n const { skeleton, params, hydration } = input\n const viewer = params.hydrateCtx.viewer\n if (!viewer) {\n return skeleton\n }\n\n const blocks = hydration.bidirectionalBlocks?.get(viewer)\n const filteredSkeleton: SkeletonState = {\n ...skeleton,\n starterPacks: skeleton.starterPacks.filter((uri) => {\n try {\n return !blocks?.get(new AtUri(uri).did)\n } catch {\n return false\n }\n }),\n }\n\n return filteredSkeleton\n}\n\nconst presentation: PresentationFn<\n Context,\n Params,\n SkeletonState,\n app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$OutputBody\n> = (input) => {\n const { ctx, skeleton, hydration } = input\n\n return {\n starterPacks: mapDefined(skeleton.starterPacks, (uri) =>\n ctx.views.starterPack(uri, hydration),\n ),\n recIdStr: skeleton.recIdStr,\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n irisClient: Client | undefined\n}\n\ntype Params = app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$Params & {\n hydrateCtx: HydrateCtx\n headers: Record<string, string>\n signal: AbortSignal\n}\n\ntype SkeletonState =\n app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton.$OutputBody\n\nfunction getUniqueDidsFromStarterPacks(\n starterPacks?: AtUriString[],\n): DidString[] {\n if (!starterPacks) return []\n\n const dids = new Set<DidString>()\n\n for (const uri of starterPacks) {\n try {\n dids.add(new AtUri(uri).did)\n } catch {\n continue\n }\n }\n\n return Array.from(dids)\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getPopularFeedGenerators.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getPopularFeedGenerators.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAcxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqFvD"}
1
+ {"version":3,"file":"getPopularFeedGenerators.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getPopularFeedGenerators.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAcxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAsFvD"}
@@ -17,6 +17,7 @@ export default function (server, ctx) {
17
17
  req,
18
18
  }));
19
19
  const hydrateCtx = await ctx.hydrator.createContext({
20
+ xrpcEndpoint: app.bsky.unspecced.getPopularFeedGenerators.$lxm,
20
21
  viewer,
21
22
  labelers,
22
23
  features,