@atproto/bsky 0.4.0 → 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 (264) hide show
  1. package/CHANGELOG.md +28 -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 +29 -19
  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/index.d.ts.map +1 -1
  212. package/dist/api/index.js +2 -0
  213. package/dist/api/index.js.map +1 -1
  214. package/dist/api/internal/bsky/actor/getProfiles.d.ts.map +1 -1
  215. package/dist/api/internal/bsky/actor/getProfiles.js +1 -0
  216. package/dist/api/internal/bsky/actor/getProfiles.js.map +1 -1
  217. package/dist/config.d.ts +0 -4
  218. package/dist/config.d.ts.map +1 -1
  219. package/dist/config.js +0 -10
  220. package/dist/config.js.map +1 -1
  221. package/dist/context.d.ts +0 -2
  222. package/dist/context.d.ts.map +1 -1
  223. package/dist/context.js +0 -3
  224. package/dist/context.js.map +1 -1
  225. package/dist/feature-gates/gates.d.ts +0 -4
  226. package/dist/feature-gates/gates.d.ts.map +1 -1
  227. package/dist/feature-gates/gates.js +0 -4
  228. package/dist/feature-gates/gates.js.map +1 -1
  229. package/dist/hydration/external.d.ts +8 -2
  230. package/dist/hydration/external.d.ts.map +1 -1
  231. package/dist/hydration/external.js +2 -0
  232. package/dist/hydration/external.js.map +1 -1
  233. package/dist/hydration/hydrator.d.ts +6 -2
  234. package/dist/hydration/hydrator.d.ts.map +1 -1
  235. package/dist/hydration/hydrator.js +96 -29
  236. package/dist/hydration/hydrator.js.map +1 -1
  237. package/dist/index.d.ts.map +1 -1
  238. package/dist/index.js +4 -19
  239. package/dist/index.js.map +1 -1
  240. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts +29 -0
  241. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts.map +1 -1
  242. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js +2 -1
  243. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js.map +1 -1
  244. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.d.ts +3 -0
  245. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.d.ts.map +1 -0
  246. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.d.ts +85 -0
  247. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.d.ts.map +1 -0
  248. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.js +29 -0
  249. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.js.map +1 -0
  250. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.js +6 -0
  251. package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.js.map +1 -0
  252. package/dist/lexicons/app/bsky/unspecced.d.ts +1 -0
  253. package/dist/lexicons/app/bsky/unspecced.d.ts.map +1 -1
  254. package/dist/lexicons/app/bsky/unspecced.js +1 -0
  255. package/dist/lexicons/app/bsky/unspecced.js.map +1 -1
  256. package/dist/telemetry/events.d.ts +5 -4
  257. package/dist/telemetry/events.d.ts.map +1 -1
  258. package/dist/telemetry/events.js +13 -2
  259. package/dist/telemetry/events.js.map +1 -1
  260. package/dist/views/external.d.ts +5 -0
  261. package/dist/views/external.d.ts.map +1 -1
  262. package/dist/views/external.js +38 -11
  263. package/dist/views/external.js.map +1 -1
  264. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"unspecced.js","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/unspecced.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,IAAI,MAAM,qBAAqB,CAAC;AAC5C,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,0CAA0C,MAAM,2DAA2D,CAAC;AACxH,OAAO,KAAK,uBAAuB,MAAM,wCAAwC,CAAC;AAClF,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,kCAAkC,MAAM,mDAAmD,CAAC;AACxG,OAAO,KAAK,0CAA0C,MAAM,2DAA2D,CAAC;AACxH,OAAO,KAAK,mCAAmC,MAAM,oDAAoD,CAAC;AAC1G,OAAO,KAAK,wBAAwB,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,eAAe,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,yBAAyB,MAAM,0CAA0C,CAAC;AACtF,OAAO,KAAK,2BAA2B,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,wBAAwB,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,gCAAgC,MAAM,iDAAiD,CAAC;AACpG,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,4BAA4B,MAAM,6CAA6C,CAAC;AAC5F,OAAO,KAAK,oCAAoC,MAAM,qDAAqD,CAAC;AAC5G,OAAO,KAAK,2BAA2B,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,mCAAmC,MAAM,oDAAoD,CAAC;AAC1G,OAAO,KAAK,2BAA2B,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,mCAAmC,MAAM,oDAAoD,CAAC;AAC1G,OAAO,KAAK,yBAAyB,MAAM,0CAA0C,CAAC;AACtF,OAAO,KAAK,sBAAsB,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,gBAAgB,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,mBAAmB,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,0BAA0B,MAAM,2CAA2C,CAAC","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as defs from \"./unspecced/defs.js\";\nexport * as getAgeAssuranceState from \"./unspecced/getAgeAssuranceState.js\";\nexport * as getAtmosphereExploreArticleRecommendations from \"./unspecced/getAtmosphereExploreArticleRecommendations.js\";\nexport * as getAtmosphereExploreTab from \"./unspecced/getAtmosphereExploreTab.js\";\nexport * as getConfig from \"./unspecced/getConfig.js\";\nexport * as getOnboardingSuggestedStarterPacks from \"./unspecced/getOnboardingSuggestedStarterPacks.js\";\nexport * as getOnboardingSuggestedStarterPacksSkeleton from \"./unspecced/getOnboardingSuggestedStarterPacksSkeleton.js\";\nexport * as getOnboardingSuggestedUsersSkeleton from \"./unspecced/getOnboardingSuggestedUsersSkeleton.js\";\nexport * as getPopularFeedGenerators from \"./unspecced/getPopularFeedGenerators.js\";\nexport * as getPostThreadOtherV2 from \"./unspecced/getPostThreadOtherV2.js\";\nexport * as getPostThreadV2 from \"./unspecced/getPostThreadV2.js\";\nexport * as getSuggestedFeeds from \"./unspecced/getSuggestedFeeds.js\";\nexport * as getSuggestedFeedsSkeleton from \"./unspecced/getSuggestedFeedsSkeleton.js\";\nexport * as getSuggestedOnboardingUsers from \"./unspecced/getSuggestedOnboardingUsers.js\";\nexport * as getSuggestedStarterPacks from \"./unspecced/getSuggestedStarterPacks.js\";\nexport * as getSuggestedStarterPacksSkeleton from \"./unspecced/getSuggestedStarterPacksSkeleton.js\";\nexport * as getSuggestedUsers from \"./unspecced/getSuggestedUsers.js\";\nexport * as getSuggestedUsersForDiscover from \"./unspecced/getSuggestedUsersForDiscover.js\";\nexport * as getSuggestedUsersForDiscoverSkeleton from \"./unspecced/getSuggestedUsersForDiscoverSkeleton.js\";\nexport * as getSuggestedUsersForExplore from \"./unspecced/getSuggestedUsersForExplore.js\";\nexport * as getSuggestedUsersForExploreSkeleton from \"./unspecced/getSuggestedUsersForExploreSkeleton.js\";\nexport * as getSuggestedUsersForSeeMore from \"./unspecced/getSuggestedUsersForSeeMore.js\";\nexport * as getSuggestedUsersForSeeMoreSkeleton from \"./unspecced/getSuggestedUsersForSeeMoreSkeleton.js\";\nexport * as getSuggestedUsersSkeleton from \"./unspecced/getSuggestedUsersSkeleton.js\";\nexport * as getSuggestionsSkeleton from \"./unspecced/getSuggestionsSkeleton.js\";\nexport * as getTaggedSuggestions from \"./unspecced/getTaggedSuggestions.js\";\nexport * as getTrendingTopics from \"./unspecced/getTrendingTopics.js\";\nexport * as getTrends from \"./unspecced/getTrends.js\";\nexport * as getTrendsSkeleton from \"./unspecced/getTrendsSkeleton.js\";\nexport * as initAgeAssurance from \"./unspecced/initAgeAssurance.js\";\nexport * as searchActorsSkeleton from \"./unspecced/searchActorsSkeleton.js\";\nexport * as searchPostsSkeleton from \"./unspecced/searchPostsSkeleton.js\";\nexport * as searchStarterPacksSkeleton from \"./unspecced/searchStarterPacksSkeleton.js\";\n"]}
1
+ {"version":3,"file":"unspecced.js","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/unspecced.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,IAAI,MAAM,qBAAqB,CAAC;AAC5C,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,0CAA0C,MAAM,2DAA2D,CAAC;AACxH,OAAO,KAAK,uBAAuB,MAAM,wCAAwC,CAAC;AAClF,OAAO,KAAK,qBAAqB,MAAM,sCAAsC,CAAC;AAC9E,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,kCAAkC,MAAM,mDAAmD,CAAC;AACxG,OAAO,KAAK,0CAA0C,MAAM,2DAA2D,CAAC;AACxH,OAAO,KAAK,mCAAmC,MAAM,oDAAoD,CAAC;AAC1G,OAAO,KAAK,wBAAwB,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,eAAe,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,yBAAyB,MAAM,0CAA0C,CAAC;AACtF,OAAO,KAAK,2BAA2B,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,wBAAwB,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,gCAAgC,MAAM,iDAAiD,CAAC;AACpG,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,4BAA4B,MAAM,6CAA6C,CAAC;AAC5F,OAAO,KAAK,oCAAoC,MAAM,qDAAqD,CAAC;AAC5G,OAAO,KAAK,2BAA2B,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,mCAAmC,MAAM,oDAAoD,CAAC;AAC1G,OAAO,KAAK,2BAA2B,MAAM,4CAA4C,CAAC;AAC1F,OAAO,KAAK,mCAAmC,MAAM,oDAAoD,CAAC;AAC1G,OAAO,KAAK,yBAAyB,MAAM,0CAA0C,CAAC;AACtF,OAAO,KAAK,sBAAsB,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,SAAS,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,iBAAiB,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,gBAAgB,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,oBAAoB,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,mBAAmB,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,0BAA0B,MAAM,2CAA2C,CAAC","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as defs from \"./unspecced/defs.js\";\nexport * as getAgeAssuranceState from \"./unspecced/getAgeAssuranceState.js\";\nexport * as getAtmosphereExploreArticleRecommendations from \"./unspecced/getAtmosphereExploreArticleRecommendations.js\";\nexport * as getAtmosphereExploreTab from \"./unspecced/getAtmosphereExploreTab.js\";\nexport * as getAtmosphereTimeline from \"./unspecced/getAtmosphereTimeline.js\";\nexport * as getConfig from \"./unspecced/getConfig.js\";\nexport * as getOnboardingSuggestedStarterPacks from \"./unspecced/getOnboardingSuggestedStarterPacks.js\";\nexport * as getOnboardingSuggestedStarterPacksSkeleton from \"./unspecced/getOnboardingSuggestedStarterPacksSkeleton.js\";\nexport * as getOnboardingSuggestedUsersSkeleton from \"./unspecced/getOnboardingSuggestedUsersSkeleton.js\";\nexport * as getPopularFeedGenerators from \"./unspecced/getPopularFeedGenerators.js\";\nexport * as getPostThreadOtherV2 from \"./unspecced/getPostThreadOtherV2.js\";\nexport * as getPostThreadV2 from \"./unspecced/getPostThreadV2.js\";\nexport * as getSuggestedFeeds from \"./unspecced/getSuggestedFeeds.js\";\nexport * as getSuggestedFeedsSkeleton from \"./unspecced/getSuggestedFeedsSkeleton.js\";\nexport * as getSuggestedOnboardingUsers from \"./unspecced/getSuggestedOnboardingUsers.js\";\nexport * as getSuggestedStarterPacks from \"./unspecced/getSuggestedStarterPacks.js\";\nexport * as getSuggestedStarterPacksSkeleton from \"./unspecced/getSuggestedStarterPacksSkeleton.js\";\nexport * as getSuggestedUsers from \"./unspecced/getSuggestedUsers.js\";\nexport * as getSuggestedUsersForDiscover from \"./unspecced/getSuggestedUsersForDiscover.js\";\nexport * as getSuggestedUsersForDiscoverSkeleton from \"./unspecced/getSuggestedUsersForDiscoverSkeleton.js\";\nexport * as getSuggestedUsersForExplore from \"./unspecced/getSuggestedUsersForExplore.js\";\nexport * as getSuggestedUsersForExploreSkeleton from \"./unspecced/getSuggestedUsersForExploreSkeleton.js\";\nexport * as getSuggestedUsersForSeeMore from \"./unspecced/getSuggestedUsersForSeeMore.js\";\nexport * as getSuggestedUsersForSeeMoreSkeleton from \"./unspecced/getSuggestedUsersForSeeMoreSkeleton.js\";\nexport * as getSuggestedUsersSkeleton from \"./unspecced/getSuggestedUsersSkeleton.js\";\nexport * as getSuggestionsSkeleton from \"./unspecced/getSuggestionsSkeleton.js\";\nexport * as getTaggedSuggestions from \"./unspecced/getTaggedSuggestions.js\";\nexport * as getTrendingTopics from \"./unspecced/getTrendingTopics.js\";\nexport * as getTrends from \"./unspecced/getTrends.js\";\nexport * as getTrendsSkeleton from \"./unspecced/getTrendsSkeleton.js\";\nexport * as initAgeAssurance from \"./unspecced/initAgeAssurance.js\";\nexport * as searchActorsSkeleton from \"./unspecced/searchActorsSkeleton.js\";\nexport * as searchPostsSkeleton from \"./unspecced/searchPostsSkeleton.js\";\nexport * as searchStarterPacksSkeleton from \"./unspecced/searchStarterPacksSkeleton.js\";\n"]}
@@ -1,8 +1,8 @@
1
+ import type { NsidString } from '@atproto/lex';
1
2
  import type { NotificationsV2Algorithm } from '../config.js';
2
- import type { app } from '../lexicons/index.js';
3
3
  export type HydrationSource = 'known_likers' | 'known_followers' | 'activity_subscriptions';
4
4
  /** XRPC method that owns a hydration; set on `HydrateCtx` from the method's `$lxm`. */
5
- export type HydrationXrpcEndpoint = app.bsky.embed.getEmbedExternalView.$lxm | app.bsky.unspecced.getAtmosphereExploreTab.$lxm;
5
+ export type HydrationXrpcEndpoint = NsidString;
6
6
  /** Reported XRPC endpoint of an external hydration traversal. */
7
7
  export type ExternalHydrationXrpcEndpoint = HydrationXrpcEndpoint | 'unknown';
8
8
  /** Entry point that owns an external hydration traversal. */
@@ -90,11 +90,12 @@ declare class EventReporter {
90
90
  algorithm: NotificationsV2Algorithm;
91
91
  durationMs: number;
92
92
  }): void;
93
- /** Reports dataplane fetches and raw-item counts once per completed page. */
94
- groupedNotifications({ itemsFetched, itemsReturned, dataplaneFetches, }: {
93
+ /** Reports dataplane fetches, raw-item counts, and hydration time once per completed page. */
94
+ groupedNotifications({ itemsFetched, itemsReturned, dataplaneFetches, hydrationDurationMs, }: {
95
95
  itemsFetched: number;
96
96
  itemsReturned: number;
97
97
  dataplaneFetches: number;
98
+ hydrationDurationMs: number;
98
99
  }): void;
99
100
  }
100
101
  export declare const events: EventReporter;
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAO/C,MAAM,MAAM,eAAe,GACzB,cAAc,GAAG,iBAAiB,GAAG,wBAAwB,CAAA;AAE/D,uFAAuF;AACvF,MAAM,MAAM,qBAAqB,GAC7B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,GACxC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,IAAI,CAAA;AAEnD,iEAAiE;AACjE,MAAM,MAAM,6BAA6B,GAAG,qBAAqB,GAAG,SAAS,CAAA;AAE7E,6DAA6D;AAC7D,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,MAAM,GAAG,cAAc,CAAA;AACpE;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAClC,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE/C,oEAAoE;AACpE,KAAK,2BAA2B,GAAG;IACjC,YAAY,EAAE,6BAA6B,CAAA;IAC3C,IAAI,EAAE,qBAAqB,CAAA;IAC3B,OAAO,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAA;IACrB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,oBAAoB,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAM,aAAa;;IAmJjB,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAAE,MAAM,EAAE,eAAe,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,QAQzE;IAED;;;;;OAKG;IACH,0BAA0B,CAAC,EACzB,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,UAAU,EACV,aAAa,EACb,OAAO,EACP,OAAO,EACP,KAAK,EACL,aAAa,EACb,YAAY,EACZ,oBAAoB,GACrB,EAAE,qBAAqB,GACtB,2BAA2B,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,QA0CrD;IAED,oFAAoF;IACpF,oBAAoB,CAAC,EACnB,SAAS,EACT,UAAU,GACX,EAAE;QACD,SAAS,EAAE,wBAAwB,CAAA;QACnC,UAAU,EAAE,MAAM,CAAA;KACnB,QAMA;IAED,6EAA6E;IAC7E,oBAAoB,CAAC,EACnB,YAAY,EACZ,aAAa,EACb,gBAAgB,GACjB,EAAE;QACD,YAAY,EAAE,MAAM,CAAA;QACpB,aAAa,EAAE,MAAM,CAAA;QACrB,gBAAgB,EAAE,MAAM,CAAA;KACzB,QAQA;CACF;AAED,eAAO,MAAM,MAAM,EAAE,aAAmC,CAAA"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAO5D,MAAM,MAAM,eAAe,GACzB,cAAc,GAAG,iBAAiB,GAAG,wBAAwB,CAAA;AAE/D,uFAAuF;AACvF,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAA;AAE9C,iEAAiE;AACjE,MAAM,MAAM,6BAA6B,GAAG,qBAAqB,GAAG,SAAS,CAAA;AAE7E,6DAA6D;AAC7D,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,MAAM,GAAG,cAAc,CAAA;AACpE;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAClC,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE/C,oEAAoE;AACpE,KAAK,2BAA2B,GAAG;IACjC,YAAY,EAAE,6BAA6B,CAAA;IAC3C,IAAI,EAAE,qBAAqB,CAAA;IAC3B,OAAO,EAAE,wBAAwB,CAAA;CAClC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAA;IACrB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,oBAAoB,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAM,aAAa;;IAiKjB,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;QAAE,MAAM,EAAE,eAAe,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,QAQzE;IAED;;;;;OAKG;IACH,0BAA0B,CAAC,EACzB,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,UAAU,EACV,aAAa,EACb,OAAO,EACP,OAAO,EACP,KAAK,EACL,aAAa,EACb,YAAY,EACZ,oBAAoB,GACrB,EAAE,qBAAqB,GACtB,2BAA2B,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,QA0CrD;IAED,oFAAoF;IACpF,oBAAoB,CAAC,EACnB,SAAS,EACT,UAAU,GACX,EAAE;QACD,SAAS,EAAE,wBAAwB,CAAA;QACnC,UAAU,EAAE,MAAM,CAAA;KACnB,QAMA;IAED,8FAA8F;IAC9F,oBAAoB,CAAC,EACnB,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,mBAAmB,GACpB,EAAE;QACD,YAAY,EAAE,MAAM,CAAA;QACpB,aAAa,EAAE,MAAM,CAAA;QACrB,gBAAgB,EAAE,MAAM,CAAA;QACxB,mBAAmB,EAAE,MAAM,CAAA;KAC5B,QASA;CACF;AAED,eAAO,MAAM,MAAM,EAAE,aAAmC,CAAA"}
@@ -68,6 +68,16 @@ class EventReporter {
68
68
  explicitBucketBoundaries: [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100],
69
69
  },
70
70
  });
71
+ #notificationHydrationDuration = meter.createHistogram('notifications.grouped.hydration.duration', {
72
+ description: 'Wall time of the grouped notifications hydration phase',
73
+ unit: 'ms',
74
+ valueType: ValueType.DOUBLE,
75
+ advice: {
76
+ explicitBucketBoundaries: [
77
+ 1, 2.5, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000,
78
+ ],
79
+ },
80
+ });
71
81
  #groupedNotificationsDataplaneFetches = meter.createHistogram('notifications.grouped.dataplane_fetches', {
72
82
  description: 'Dataplane notification fetches per grouped notifications response',
73
83
  unit: '{fetch}',
@@ -186,12 +196,13 @@ class EventReporter {
186
196
  diag.error('Failed to record notification_grouping:', err);
187
197
  }
188
198
  }
189
- /** Reports dataplane fetches and raw-item counts once per completed page. */
190
- groupedNotifications({ itemsFetched, itemsReturned, dataplaneFetches, }) {
199
+ /** Reports dataplane fetches, raw-item counts, and hydration time once per completed page. */
200
+ groupedNotifications({ itemsFetched, itemsReturned, dataplaneFetches, hydrationDurationMs, }) {
191
201
  try {
192
202
  this.#groupedNotificationsItemsFetched.record(itemsFetched);
193
203
  this.#groupedNotificationsItemsReturned.record(itemsReturned);
194
204
  this.#groupedNotificationsDataplaneFetches.record(dataplaneFetches);
205
+ this.#notificationHydrationDuration.record(hydrationDurationMs);
195
206
  }
196
207
  catch (err) {
197
208
  diag.error('Failed to record grouped_notifications:', err);
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,cAAc,EACd,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,OAAO,GACR,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAExD,MAAM,KAAK,GAAU,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAA;AACtD,MAAM,MAAM,GAAW,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;AA8DtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,aAAa;IACjB,uBAAuB,GAAG,KAAK,CAAC,aAAa,CAG1C,kBAAkB,EAAE;QACrB,WAAW,EACT,mEAAmE;QACrE,SAAS,EAAE,SAAS,CAAC,GAAG;KACzB,CAAC,CAAA;IAEF,yBAAyB,GACvB,KAAK,CAAC,eAAe,CACnB,mCAAmC,EACnC;QACE,WAAW,EACT,wGAAwG;QAC1G,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,SAAS,CAAC,GAAG;QACxB,MAAM,EAAE;YACN,wBAAwB,EAAE;gBACxB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI;aAC5C;SACF;KACF,CACF,CAAA;IAEH,yBAAyB,GACvB,KAAK,CAAC,eAAe,CACnB,4BAA4B,EAC5B;QACE,WAAW,EACT,oFAAoF;QACtF,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,SAAS,CAAC,GAAG;QACxB,sEAAsE;QACtE,MAAM,EAAE,EAAE,wBAAwB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE;KAClE,CACF,CAAA;IAEH,0BAA0B,GACxB,KAAK,CAAC,eAAe,CACnB,6BAA6B,EAC7B;QACE,WAAW,EACT,mFAAmF;QACrF,IAAI,EAAE,IAAI;QACV,SAAS,EAAE,SAAS,CAAC,MAAM;QAC3B,MAAM,EAAE;YACN,wBAAwB,EAAE;gBACxB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK;aACtD;SACF;KACF,CACF,CAAA;IAEH,yBAAyB,GAAG,KAAK,CAAC,aAAa,CAI5C,4BAA4B,EAAE;QAC/B,WAAW,EACT,0GAA0G;QAC5G,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,SAAS,CAAC,GAAG;KACzB,CAAC,CAAA;IAEF,6BAA6B,GAAG,KAAK,CAAC,eAAe,CAElD,gCAAgC,EAAE;QACnC,WAAW,EACT,iEAAiE;QACnE,IAAI,EAAE,IAAI;QACV,SAAS,EAAE,SAAS,CAAC,MAAM;QAC3B,MAAM,EAAE;YACN,wBAAwB,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC;SACvE;KACF,CAAC,CAAA;IAEF,qCAAqC,GAAG,KAAK,CAAC,eAAe,CAC3D,yCAAyC,EACzC;QACE,WAAW,EACT,mEAAmE;QACrE,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,SAAS,CAAC,GAAG;QACxB,MAAM,EAAE;YACN,wBAAwB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;SAC7D;KACF,CACF,CAAA;IAED,iCAAiC,GAAG,KAAK,CAAC,eAAe,CACvD,qCAAqC,EACrC;QACE,WAAW,EACT,yFAAyF;QAC3F,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,SAAS,CAAC,GAAG;QACxB,MAAM,EAAE;YACN,wBAAwB,EAAE;gBACxB,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;aACzD;SACF;KACF,CACF,CAAA;IAED,kCAAkC,GAAG,KAAK,CAAC,eAAe,CACxD,sCAAsC,EACtC;QACE,WAAW,EACT,sFAAsF;QACxF,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,SAAS,CAAC,GAAG;QACxB,MAAM,EAAE;YACN,wBAAwB,EAAE;gBACxB,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;aACzD;SACF;KACF,CACF,CAAA;IAED;;;;;OAKG;IACH,IAAI,CACF,SAAiB,EACjB,UAAiE;QAEjE,IAAI,CAAC;YACH,uDAAuD;YACvD,MAAM,CAAC,IAAI,CAAC;gBACV,SAAS;gBACT,cAAc,EAAE,cAAc,CAAC,IAAI;gBACnC,UAAU;aACX,CAAC,CAAA;YAEF,mDAAmD;YACnD,wDAAwD;YACxD,YAAY,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,CAAC,CAAA;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,CAAC,uBAAuB,SAAS,GAAG,EAAE,GAAG,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAED,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,EAA6C;QACxE,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC;YAC9B,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC;gBACnB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,OAAO,CAAA;QACb,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;QACjD,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACzD,CAAC;IAED;;;;;OAKG;IACH,0BAA0B,CAAC,EACzB,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,UAAU,EACV,aAAa,EACb,OAAO,EACP,OAAO,EACP,KAAK,EACL,aAAa,EACb,YAAY,EACZ,oBAAoB,GAEgC;QACpD,IAAI,CAAC,IAAI,CAAC,8BAA8B,EAAE;YACxC,YAAY;YACZ,IAAI;YACJ,OAAO;YACP,UAAU;YACV,aAAa;YACb,OAAO;YACP,OAAO;YACP,KAAK;YACL,aAAa;YACb,YAAY;YACZ,oBAAoB;SACrB,CAAC,CAAA;QACF,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;YAClD,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;YAChE,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;YAC1D,IAAI,CAAC,0BAA0B,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;YAC9D,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,aAAa,EAAE;gBAChD,YAAY;gBACZ,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,UAAU;aACnB,CAAC,CAAA;YACF,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,KAAK,GAAG,aAAa,EAAE;gBACxD,YAAY;gBACZ,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,YAAY;aACrB,CAAC,CAAA;YACF,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,oBAAoB,EAAE;gBACvD,YAAY;gBACZ,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,UAAU;aACnB,CAAC,CAAA;YACF,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,YAAY,GAAG,oBAAoB,EAAE;gBACtE,YAAY;gBACZ,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,YAAY;aACrB,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,CAAC,gDAAgD,EAAE,GAAG,CAAC,CAAA;QACnE,CAAC;IACH,CAAC;IAED,oFAAoF;IACpF,oBAAoB,CAAC,EACnB,SAAS,EACT,UAAU,GAIX;QACC,IAAI,CAAC;YACH,IAAI,CAAC,6BAA6B,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,CAAA;QACtE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAA;QAC5D,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,oBAAoB,CAAC,EACnB,YAAY,EACZ,aAAa,EACb,gBAAgB,GAKjB;QACC,IAAI,CAAC;YACH,IAAI,CAAC,iCAAiC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;YAC3D,IAAI,CAAC,kCAAkC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;YAC7D,IAAI,CAAC,qCAAqC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAA;QACrE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAA;QAC5D,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,MAAM,GAAkB,IAAI,aAAa,EAAE,CAAA","sourcesContent":["import {\n type Logger,\n type Meter,\n SeverityNumber,\n ValueType,\n diag,\n logs,\n metrics,\n} from '@opentelemetry/api'\nimport type { NotificationsV2Algorithm } from '../config.js'\nimport type { app } from '../lexicons/index.js'\nimport { eventsLogger } from '../logger.js'\nimport { isAbortError, isTimeoutError } from './util.js'\n\nconst meter: Meter = metrics.getMeter('@atproto/bsky')\nconst logger: Logger = logs.getLogger('@atproto/bsky')\n\nexport type HydrationSource =\n 'known_likers' | 'known_followers' | 'activity_subscriptions'\n\n/** XRPC method that owns a hydration; set on `HydrateCtx` from the method's `$lxm`. */\nexport type HydrationXrpcEndpoint =\n | app.bsky.embed.getEmbedExternalView.$lxm\n | app.bsky.unspecced.getAtmosphereExploreTab.$lxm\n\n/** Reported XRPC endpoint of an external hydration traversal. */\nexport type ExternalHydrationXrpcEndpoint = HydrationXrpcEndpoint | 'unknown'\n\n/** Entry point that owns an external hydration traversal. */\nexport type ExternalHydrationRoot = 'uris' | 'refs' | 'dependencies'\n/**\n * `partial` is a returned traversal that recovered from a best-effort\n * dependency failure; `capped` takes precedence over it.\n */\nexport type ExternalHydrationOutcome =\n 'completed' | 'capped' | 'partial' | 'failed'\n\n/** Bounded metric attributes of an external hydration traversal. */\ntype ExternalHydrationAttributes = {\n xrpcEndpoint: ExternalHydrationXrpcEndpoint\n root: ExternalHydrationRoot\n outcome: ExternalHydrationOutcome\n}\n\n/**\n * Generic record lookup work of one external hydration traversal, across the\n * root and all nested dependency batches. Excludes profile/label hydration\n * and backlink sample/count RPCs.\n */\nexport type ExternalHydrationWork = {\n /**\n * Accepted latest/exact lookup identities scheduled for fetching, after\n * filtering duplicates and seen keys. Unavailable results and lookups in a\n * failed batch count; lookups skipped at the pass cap do not.\n */\n recordLookups: number\n /** Nonempty batches that scheduled generic fetches; not a raw RPC count. */\n batches: number\n /** Deepest fetched batch, where the root batch is pass 1; 0 if none. */\n maxPass: number\n /**\n * Refs inspected by the traversal's first dependency discovery. A traversal\n * whose root batch failed has none.\n */\n roots: number\n /** Roots available after takedown and owner moderation. */\n rootsResolved: number\n /**\n * Refs inspected by later dependency discoveries: records returned by\n * successful nested batches. Lookups lost to a failed batch or the pass cap\n * are excluded.\n */\n dependencies: number\n /** Dependencies available after takedown and owner moderation. */\n dependenciesResolved: number\n}\n\n/**\n * Central hub for reporting AppView events. Each method reports a\n * single event to every relevant sink in one call:\n *\n * 1. a pino logger → stdout/stderr (docker logs)\n * 2. an OTEL counter → aggregated metric (low-cardinality dimensions only)\n * 3. the OTEL Logs SDK → structured, trace-correlated log record (full detail)\n *\n * This keeps the three concerns in sync: there is exactly one call site per\n * event, and the counter / log / attribute definitions live together here\n * rather than being duplicated and drifting across handlers.\n *\n * @note High-cardinality attributes (e.g. `did`, `uri`) belong in the pino and\n * OTEL log records but must stay OUT of the counters, whose attributes must\n * remain low-cardinality to avoid a metric-series explosion.\n *\n * @note The OTEL counters and log records are no-ops unless the corresponding\n * OTEL exporter is configured, so calling these methods is always safe and\n * essentially free when telemetry is disabled.\n */\nclass EventReporter {\n #hydrationFailedCounter = meter.createCounter<{\n source: HydrationSource\n reason: 'abort' | 'timeout' | 'error'\n }>('hydration.failed', {\n description:\n 'Number of fail-open hydration steps that did not produce a result',\n valueType: ValueType.INT,\n })\n\n #externalHydrationLookups =\n meter.createHistogram<ExternalHydrationAttributes>(\n 'hydration.external.record_lookups',\n {\n description:\n 'Generic external record lookups scheduled per hydration traversal, including nested dependency batches',\n unit: '{lookup}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [\n 0, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000,\n ],\n },\n },\n )\n\n #externalHydrationBatches =\n meter.createHistogram<ExternalHydrationAttributes>(\n 'hydration.external.batches',\n {\n description:\n 'Generic external record fetch batches per hydration traversal, one per nested pass',\n unit: '{batch}',\n valueType: ValueType.INT,\n // @NOTE Traversals are capped at `MAX_EXTERNAL_HYDRATION_PASSES` (8).\n advice: { explicitBucketBoundaries: [0, 1, 2, 3, 4, 5, 6, 7, 8] },\n },\n )\n\n #externalHydrationDuration =\n meter.createHistogram<ExternalHydrationAttributes>(\n 'hydration.external.duration',\n {\n description:\n 'Wall time of an external hydration traversal, including nested dependency batches',\n unit: 'ms',\n valueType: ValueType.DOUBLE,\n advice: {\n explicitBucketBoundaries: [\n 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000,\n ],\n },\n },\n )\n\n #externalHydrationRecords = meter.createCounter<{\n xrpcEndpoint: ExternalHydrationXrpcEndpoint\n kind: 'root' | 'dependency'\n status: 'resolved' | 'unresolved'\n }>('hydration.external.records', {\n description:\n 'Root and dependency records inspected by external hydration traversals, by availability after moderation',\n unit: '{record}',\n valueType: ValueType.INT,\n })\n\n #notificationGroupingDuration = meter.createHistogram<{\n algorithm: NotificationsV2Algorithm\n }>('notifications.grouped.duration', {\n description:\n 'Compute time of each notification grouping algorithm invocation',\n unit: 'ms',\n valueType: ValueType.DOUBLE,\n advice: {\n explicitBucketBoundaries: [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100],\n },\n })\n\n #groupedNotificationsDataplaneFetches = meter.createHistogram(\n 'notifications.grouped.dataplane_fetches',\n {\n description:\n 'Dataplane notification fetches per grouped notifications response',\n unit: '{fetch}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n },\n },\n )\n\n #groupedNotificationsItemsFetched = meter.createHistogram(\n 'notifications.grouped.items_fetched',\n {\n description:\n 'Raw notifications fetched across all dataplane reads per grouped notifications response',\n unit: '{notification}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [\n 50, 100, 150, 200, 300, 400, 500, 1000, 1500, 2000, 3000,\n ],\n },\n },\n )\n\n #groupedNotificationsItemsReturned = meter.createHistogram(\n 'notifications.grouped.items_returned',\n {\n description:\n 'Raw notifications represented by the final groups per grouped notifications response',\n unit: '{notification}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [\n 50, 100, 150, 200, 300, 400, 500, 1000, 1500, 2000, 3000,\n ],\n },\n },\n )\n\n /**\n * Fans a single event out to both the pino logger (stdout/stderr) and the\n * OTEL Logs SDK (structured record).\n *\n * @note this method should never throw\n */\n #log(\n eventName: string,\n attributes: Record<string, string | number | boolean | undefined>,\n ) {\n try {\n // Emits an OTEL log for the event (goes to collector).\n logger.emit({\n eventName,\n severityNumber: SeverityNumber.INFO,\n attributes,\n })\n\n // Emits a Pino log for the event (goes to stdout).\n // NOTE: We'll probably migrate towards only using OTEL.\n eventsLogger.info({ eventName, ...attributes })\n } catch (err) {\n diag.error(`Failed to log event ${eventName}:`, err)\n }\n }\n\n hydrationFailed({ source, err }: { source: HydrationSource; err: unknown }) {\n const reason = isAbortError(err)\n ? 'abort'\n : isTimeoutError(err)\n ? 'timeout'\n : 'error'\n this.#log('hydration_failed', { source, reason })\n this.#hydrationFailedCounter.add(1, { source, reason })\n }\n\n /**\n * Reports the totals of one top-level external hydration traversal. Numeric\n * totals are observations and log fields, never metric attributes.\n *\n * @note this method should never throw\n */\n externalHydrationTraversal({\n xrpcEndpoint,\n root,\n outcome,\n durationMs,\n recordLookups,\n batches,\n maxPass,\n roots,\n rootsResolved,\n dependencies,\n dependenciesResolved,\n }: ExternalHydrationWork &\n ExternalHydrationAttributes & { durationMs: number }) {\n this.#log('external_hydration_traversal', {\n xrpcEndpoint,\n root,\n outcome,\n durationMs,\n recordLookups,\n batches,\n maxPass,\n roots,\n rootsResolved,\n dependencies,\n dependenciesResolved,\n })\n try {\n const attributes = { xrpcEndpoint, root, outcome }\n this.#externalHydrationLookups.record(recordLookups, attributes)\n this.#externalHydrationBatches.record(batches, attributes)\n this.#externalHydrationDuration.record(durationMs, attributes)\n this.#externalHydrationRecords.add(rootsResolved, {\n xrpcEndpoint,\n kind: 'root',\n status: 'resolved',\n })\n this.#externalHydrationRecords.add(roots - rootsResolved, {\n xrpcEndpoint,\n kind: 'root',\n status: 'unresolved',\n })\n this.#externalHydrationRecords.add(dependenciesResolved, {\n xrpcEndpoint,\n kind: 'dependency',\n status: 'resolved',\n })\n this.#externalHydrationRecords.add(dependencies - dependenciesResolved, {\n xrpcEndpoint,\n kind: 'dependency',\n status: 'unresolved',\n })\n } catch (err) {\n diag.error('Failed to record external_hydration_traversal:', err)\n }\n }\n\n /** Reports compute time once per grouping invocation, excluding dataplane reads. */\n notificationGrouping({\n algorithm,\n durationMs,\n }: {\n algorithm: NotificationsV2Algorithm\n durationMs: number\n }) {\n try {\n this.#notificationGroupingDuration.record(durationMs, { algorithm })\n } catch (err) {\n diag.error('Failed to record notification_grouping:', err)\n }\n }\n\n /** Reports dataplane fetches and raw-item counts once per completed page. */\n groupedNotifications({\n itemsFetched,\n itemsReturned,\n dataplaneFetches,\n }: {\n itemsFetched: number\n itemsReturned: number\n dataplaneFetches: number\n }) {\n try {\n this.#groupedNotificationsItemsFetched.record(itemsFetched)\n this.#groupedNotificationsItemsReturned.record(itemsReturned)\n this.#groupedNotificationsDataplaneFetches.record(dataplaneFetches)\n } catch (err) {\n diag.error('Failed to record grouped_notifications:', err)\n }\n }\n}\n\nexport const events: EventReporter = new EventReporter()\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Logger,\n type Meter,\n SeverityNumber,\n ValueType,\n diag,\n logs,\n metrics,\n} from '@opentelemetry/api'\nimport type { NsidString } from '@atproto/lex'\nimport type { NotificationsV2Algorithm } from '../config.js'\nimport { eventsLogger } from '../logger.js'\nimport { isAbortError, isTimeoutError } from './util.js'\n\nconst meter: Meter = metrics.getMeter('@atproto/bsky')\nconst logger: Logger = logs.getLogger('@atproto/bsky')\n\nexport type HydrationSource =\n 'known_likers' | 'known_followers' | 'activity_subscriptions'\n\n/** XRPC method that owns a hydration; set on `HydrateCtx` from the method's `$lxm`. */\nexport type HydrationXrpcEndpoint = NsidString\n\n/** Reported XRPC endpoint of an external hydration traversal. */\nexport type ExternalHydrationXrpcEndpoint = HydrationXrpcEndpoint | 'unknown'\n\n/** Entry point that owns an external hydration traversal. */\nexport type ExternalHydrationRoot = 'uris' | 'refs' | 'dependencies'\n/**\n * `partial` is a returned traversal that recovered from a best-effort\n * dependency failure; `capped` takes precedence over it.\n */\nexport type ExternalHydrationOutcome =\n 'completed' | 'capped' | 'partial' | 'failed'\n\n/** Bounded metric attributes of an external hydration traversal. */\ntype ExternalHydrationAttributes = {\n xrpcEndpoint: ExternalHydrationXrpcEndpoint\n root: ExternalHydrationRoot\n outcome: ExternalHydrationOutcome\n}\n\n/**\n * Generic record lookup work of one external hydration traversal, across the\n * root and all nested dependency batches. Excludes profile/label hydration\n * and backlink sample/count RPCs.\n */\nexport type ExternalHydrationWork = {\n /**\n * Accepted latest/exact lookup identities scheduled for fetching, after\n * filtering duplicates and seen keys. Unavailable results and lookups in a\n * failed batch count; lookups skipped at the pass cap do not.\n */\n recordLookups: number\n /** Nonempty batches that scheduled generic fetches; not a raw RPC count. */\n batches: number\n /** Deepest fetched batch, where the root batch is pass 1; 0 if none. */\n maxPass: number\n /**\n * Refs inspected by the traversal's first dependency discovery. A traversal\n * whose root batch failed has none.\n */\n roots: number\n /** Roots available after takedown and owner moderation. */\n rootsResolved: number\n /**\n * Refs inspected by later dependency discoveries: records returned by\n * successful nested batches. Lookups lost to a failed batch or the pass cap\n * are excluded.\n */\n dependencies: number\n /** Dependencies available after takedown and owner moderation. */\n dependenciesResolved: number\n}\n\n/**\n * Central hub for reporting AppView events. Each method reports a\n * single event to every relevant sink in one call:\n *\n * 1. a pino logger → stdout/stderr (docker logs)\n * 2. an OTEL counter → aggregated metric (low-cardinality dimensions only)\n * 3. the OTEL Logs SDK → structured, trace-correlated log record (full detail)\n *\n * This keeps the three concerns in sync: there is exactly one call site per\n * event, and the counter / log / attribute definitions live together here\n * rather than being duplicated and drifting across handlers.\n *\n * @note High-cardinality attributes (e.g. `did`, `uri`) belong in the pino and\n * OTEL log records but must stay OUT of the counters, whose attributes must\n * remain low-cardinality to avoid a metric-series explosion.\n *\n * @note The OTEL counters and log records are no-ops unless the corresponding\n * OTEL exporter is configured, so calling these methods is always safe and\n * essentially free when telemetry is disabled.\n */\nclass EventReporter {\n #hydrationFailedCounter = meter.createCounter<{\n source: HydrationSource\n reason: 'abort' | 'timeout' | 'error'\n }>('hydration.failed', {\n description:\n 'Number of fail-open hydration steps that did not produce a result',\n valueType: ValueType.INT,\n })\n\n #externalHydrationLookups =\n meter.createHistogram<ExternalHydrationAttributes>(\n 'hydration.external.record_lookups',\n {\n description:\n 'Generic external record lookups scheduled per hydration traversal, including nested dependency batches',\n unit: '{lookup}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [\n 0, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000,\n ],\n },\n },\n )\n\n #externalHydrationBatches =\n meter.createHistogram<ExternalHydrationAttributes>(\n 'hydration.external.batches',\n {\n description:\n 'Generic external record fetch batches per hydration traversal, one per nested pass',\n unit: '{batch}',\n valueType: ValueType.INT,\n // @NOTE Traversals are capped at `MAX_EXTERNAL_HYDRATION_PASSES` (8).\n advice: { explicitBucketBoundaries: [0, 1, 2, 3, 4, 5, 6, 7, 8] },\n },\n )\n\n #externalHydrationDuration =\n meter.createHistogram<ExternalHydrationAttributes>(\n 'hydration.external.duration',\n {\n description:\n 'Wall time of an external hydration traversal, including nested dependency batches',\n unit: 'ms',\n valueType: ValueType.DOUBLE,\n advice: {\n explicitBucketBoundaries: [\n 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000,\n ],\n },\n },\n )\n\n #externalHydrationRecords = meter.createCounter<{\n xrpcEndpoint: ExternalHydrationXrpcEndpoint\n kind: 'root' | 'dependency'\n status: 'resolved' | 'unresolved'\n }>('hydration.external.records', {\n description:\n 'Root and dependency records inspected by external hydration traversals, by availability after moderation',\n unit: '{record}',\n valueType: ValueType.INT,\n })\n\n #notificationGroupingDuration = meter.createHistogram<{\n algorithm: NotificationsV2Algorithm\n }>('notifications.grouped.duration', {\n description:\n 'Compute time of each notification grouping algorithm invocation',\n unit: 'ms',\n valueType: ValueType.DOUBLE,\n advice: {\n explicitBucketBoundaries: [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100],\n },\n })\n\n #notificationHydrationDuration = meter.createHistogram(\n 'notifications.grouped.hydration.duration',\n {\n description: 'Wall time of the grouped notifications hydration phase',\n unit: 'ms',\n valueType: ValueType.DOUBLE,\n advice: {\n explicitBucketBoundaries: [\n 1, 2.5, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000,\n ],\n },\n },\n )\n\n #groupedNotificationsDataplaneFetches = meter.createHistogram(\n 'notifications.grouped.dataplane_fetches',\n {\n description:\n 'Dataplane notification fetches per grouped notifications response',\n unit: '{fetch}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n },\n },\n )\n\n #groupedNotificationsItemsFetched = meter.createHistogram(\n 'notifications.grouped.items_fetched',\n {\n description:\n 'Raw notifications fetched across all dataplane reads per grouped notifications response',\n unit: '{notification}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [\n 50, 100, 150, 200, 300, 400, 500, 1000, 1500, 2000, 3000,\n ],\n },\n },\n )\n\n #groupedNotificationsItemsReturned = meter.createHistogram(\n 'notifications.grouped.items_returned',\n {\n description:\n 'Raw notifications represented by the final groups per grouped notifications response',\n unit: '{notification}',\n valueType: ValueType.INT,\n advice: {\n explicitBucketBoundaries: [\n 50, 100, 150, 200, 300, 400, 500, 1000, 1500, 2000, 3000,\n ],\n },\n },\n )\n\n /**\n * Fans a single event out to both the pino logger (stdout/stderr) and the\n * OTEL Logs SDK (structured record).\n *\n * @note this method should never throw\n */\n #log(\n eventName: string,\n attributes: Record<string, string | number | boolean | undefined>,\n ) {\n try {\n // Emits an OTEL log for the event (goes to collector).\n logger.emit({\n eventName,\n severityNumber: SeverityNumber.INFO,\n attributes,\n })\n\n // Emits a Pino log for the event (goes to stdout).\n // NOTE: We'll probably migrate towards only using OTEL.\n eventsLogger.info({ eventName, ...attributes })\n } catch (err) {\n diag.error(`Failed to log event ${eventName}:`, err)\n }\n }\n\n hydrationFailed({ source, err }: { source: HydrationSource; err: unknown }) {\n const reason = isAbortError(err)\n ? 'abort'\n : isTimeoutError(err)\n ? 'timeout'\n : 'error'\n this.#log('hydration_failed', { source, reason })\n this.#hydrationFailedCounter.add(1, { source, reason })\n }\n\n /**\n * Reports the totals of one top-level external hydration traversal. Numeric\n * totals are observations and log fields, never metric attributes.\n *\n * @note this method should never throw\n */\n externalHydrationTraversal({\n xrpcEndpoint,\n root,\n outcome,\n durationMs,\n recordLookups,\n batches,\n maxPass,\n roots,\n rootsResolved,\n dependencies,\n dependenciesResolved,\n }: ExternalHydrationWork &\n ExternalHydrationAttributes & { durationMs: number }) {\n this.#log('external_hydration_traversal', {\n xrpcEndpoint,\n root,\n outcome,\n durationMs,\n recordLookups,\n batches,\n maxPass,\n roots,\n rootsResolved,\n dependencies,\n dependenciesResolved,\n })\n try {\n const attributes = { xrpcEndpoint, root, outcome }\n this.#externalHydrationLookups.record(recordLookups, attributes)\n this.#externalHydrationBatches.record(batches, attributes)\n this.#externalHydrationDuration.record(durationMs, attributes)\n this.#externalHydrationRecords.add(rootsResolved, {\n xrpcEndpoint,\n kind: 'root',\n status: 'resolved',\n })\n this.#externalHydrationRecords.add(roots - rootsResolved, {\n xrpcEndpoint,\n kind: 'root',\n status: 'unresolved',\n })\n this.#externalHydrationRecords.add(dependenciesResolved, {\n xrpcEndpoint,\n kind: 'dependency',\n status: 'resolved',\n })\n this.#externalHydrationRecords.add(dependencies - dependenciesResolved, {\n xrpcEndpoint,\n kind: 'dependency',\n status: 'unresolved',\n })\n } catch (err) {\n diag.error('Failed to record external_hydration_traversal:', err)\n }\n }\n\n /** Reports compute time once per grouping invocation, excluding dataplane reads. */\n notificationGrouping({\n algorithm,\n durationMs,\n }: {\n algorithm: NotificationsV2Algorithm\n durationMs: number\n }) {\n try {\n this.#notificationGroupingDuration.record(durationMs, { algorithm })\n } catch (err) {\n diag.error('Failed to record notification_grouping:', err)\n }\n }\n\n /** Reports dataplane fetches, raw-item counts, and hydration time once per completed page. */\n groupedNotifications({\n itemsFetched,\n itemsReturned,\n dataplaneFetches,\n hydrationDurationMs,\n }: {\n itemsFetched: number\n itemsReturned: number\n dataplaneFetches: number\n hydrationDurationMs: number\n }) {\n try {\n this.#groupedNotificationsItemsFetched.record(itemsFetched)\n this.#groupedNotificationsItemsReturned.record(itemsReturned)\n this.#groupedNotificationsDataplaneFetches.record(dataplaneFetches)\n this.#notificationHydrationDuration.record(hydrationDurationMs)\n } catch (err) {\n diag.error('Failed to record grouped_notifications:', err)\n }\n }\n}\n\nexport const events: EventReporter = new EventReporter()\n"]}
@@ -1,8 +1,13 @@
1
1
  import { type $Typed, type AtUriString } from '@atproto/lex';
2
+ import type { ExternalRecord } from '../hydration/external.js';
2
3
  import type { HydrationState } from '../hydration/hydrator.js';
3
4
  import type { Views } from './index.js';
4
5
  import type { ExternalRecordView } from './types.js';
5
6
  export declare const LIVESTREAM_HEARTBEAT_WINDOW_MS: number;
6
7
  /** Build a modality view from validated, available records in hydration state. */
7
8
  export declare function externalRecordView(views: Views, uri: AtUriString, state: HydrationState, now: number): $Typed<ExternalRecordView> | undefined;
9
+ /** Resolve renderable view dependencies without constructing presentation fields. */
10
+ export declare function externalRecordViewRefs(views: Views, uri: AtUriString, state: HydrationState): AtUriString[] | undefined;
11
+ /** Apply shared block, review, and takedown checks to a hydrated record. */
12
+ export declare function availableRecord(views: Views, uri: AtUriString, state: HydrationState, info?: ExternalRecord | null | undefined): ExternalRecord | undefined;
8
13
  //# sourceMappingURL=external.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/views/external.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,WAAW,EAOjB,MAAM,cAAc,CAAA;AAGrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAK9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAA2B,MAAM,YAAY,CAAA;AAE7E,eAAO,MAAM,8BAA8B,QAAa,CAAA;AAExD,kFAAkF;AAClF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAwIxC"}
1
+ {"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/views/external.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,WAAW,EAOjB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAK9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAA2B,MAAM,YAAY,CAAA;AAE7E,eAAO,MAAM,8BAA8B,QAAa,CAAA;AAExD,kFAAkF;AAClF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAExC;AAED,qFAAqF;AACrF,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,cAAc,GACpB,WAAW,EAAE,GAAG,SAAS,CAE3B;AA2ND,4EAA4E;AAC5E,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,cAAc,EACrB,IAAI,GAAE,cAAc,GAAG,IAAI,GAAG,SAA2C,GACxE,cAAc,GAAG,SAAS,CAmB5B"}
@@ -8,6 +8,13 @@ import { uriToDid } from '../util/uris.js';
8
8
  export const LIVESTREAM_HEARTBEAT_WINDOW_MS = 2 * MINUTE;
9
9
  /** Build a modality view from validated, available records in hydration state. */
10
10
  export function externalRecordView(views, uri, state, now) {
11
+ return externalRecordViewOrRefs(views, uri, state, now, false);
12
+ }
13
+ /** Resolve renderable view dependencies without constructing presentation fields. */
14
+ export function externalRecordViewRefs(views, uri, state) {
15
+ return externalRecordViewOrRefs(views, uri, state, 0, true);
16
+ }
17
+ function externalRecordViewOrRefs(views, uri, state, now, refsOnly) {
11
18
  const info = availableRecord(views, uri, state);
12
19
  if (!info)
13
20
  return;
@@ -17,10 +24,13 @@ export function externalRecordView(views, uri, state, now) {
17
24
  let base = record.site;
18
25
  const refs = [uri];
19
26
  if (isAtUriString(record.site)) {
20
- publisher = publicationView(views, record.site, state);
21
- if (!publisher)
27
+ const publication = publicationRecord(views, record.site, state);
28
+ if (!publication)
22
29
  return;
23
- base = publisher.uri;
30
+ base = publication.url;
31
+ if (!refsOnly) {
32
+ publisher = publicationView(views, record.site, state, publication);
33
+ }
24
34
  refs.push(record.site);
25
35
  }
26
36
  const url = httpUri(record.path
@@ -28,6 +38,8 @@ export function externalRecordView(views, uri, state, now) {
28
38
  : base);
29
39
  if (!url || !record.title)
30
40
  return;
41
+ if (refsOnly)
42
+ return refs;
31
43
  return app.bsky.embed.external.viewArticle.$build({
32
44
  ...commonFields(views, refs, state),
33
45
  uri: url,
@@ -47,6 +59,9 @@ export function externalRecordView(views, uri, state, now) {
47
59
  });
48
60
  }
49
61
  if (site.standard.publication.$matches(record)) {
62
+ if (refsOnly) {
63
+ return publicationRecord(views, uri, state) ? [uri] : undefined;
64
+ }
50
65
  return publicationView(views, uri, state);
51
66
  }
52
67
  if (social.grain.gallery.$matches(record)) {
@@ -73,6 +88,8 @@ export function externalRecordView(views, uri, state, now) {
73
88
  continue;
74
89
  seenPhotos.add(photoUri);
75
90
  refs.push(link.uri, photoUri);
91
+ if (refsOnly)
92
+ continue;
76
93
  items.push(app.bsky.embed.external.viewGalleryImage.$build({
77
94
  thumbnail: views.imgUriBuilder.getPresetUri('feed_thumbnail', uriToDid(photoUri), getBlobCidString(photo.photo)),
78
95
  fullsize: views.imgUriBuilder.getPresetUri('feed_fullsize', uriToDid(photoUri), getBlobCidString(photo.photo)),
@@ -85,6 +102,8 @@ export function externalRecordView(views, uri, state, now) {
85
102
  : undefined,
86
103
  }));
87
104
  }
105
+ if (refsOnly)
106
+ return refs;
88
107
  return app.bsky.embed.external.viewGallery.$build({
89
108
  ...commonFields(views, refs, state),
90
109
  uri: buildExternalRecordUrl(uri) ?? uri,
@@ -97,6 +116,8 @@ export function externalRecordView(views, uri, state, now) {
97
116
  });
98
117
  }
99
118
  if (place.stream.livestream.$matches(record)) {
119
+ if (refsOnly)
120
+ return [uri];
100
121
  const lastSeen = record.lastSeenAt ? Date.parse(record.lastSeenAt) : NaN;
101
122
  // @NOTE The window applies on both sides of now to tolerate clock skew, so
102
123
  // a future heartbeat counts as live for at most twice the window.
@@ -116,13 +137,10 @@ export function externalRecordView(views, uri, state, now) {
116
137
  });
117
138
  }
118
139
  }
119
- function publicationView(views, uri, state) {
120
- const record = availableRecord(views, uri, state)?.record;
121
- if (!site.standard.publication.$matches(record))
122
- return;
123
- const url = httpUri(record.url);
124
- if (!url || !record.name)
140
+ function publicationView(views, uri, state, publication = publicationRecord(views, uri, state)) {
141
+ if (!publication)
125
142
  return;
143
+ const { record, url } = publication;
126
144
  return app.bsky.embed.external.viewArticlePublication.$build({
127
145
  ...commonFields(views, [uri], state),
128
146
  uri: url,
@@ -143,8 +161,17 @@ function publicationView(views, uri, state) {
143
161
  subscribers: backlinkProfiles(views, uri, state, 'subscription'),
144
162
  });
145
163
  }
146
- function availableRecord(views, uri, state) {
147
- const info = state.externalRecords?.get(uri);
164
+ function publicationRecord(views, uri, state) {
165
+ const record = availableRecord(views, uri, state)?.record;
166
+ if (!site.standard.publication.$matches(record))
167
+ return;
168
+ const url = httpUri(record.url);
169
+ if (!url || !record.name)
170
+ return;
171
+ return { record, url };
172
+ }
173
+ /** Apply shared block, review, and takedown checks to a hydrated record. */
174
+ export function availableRecord(views, uri, state, info = state.externalRecords?.get(uri)) {
148
175
  if (!info)
149
176
  return;
150
177
  const parsed = new AtUri(uri);
@@ -1 +1 @@
1
- {"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/views/external.ts"],"names":[],"mappings":"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","sourcesContent":["import { MINUTE } from '@atproto/common'\nimport {\n type $Typed,\n type AtUriString,\n type DidString,\n type NsidString,\n type UriString,\n getBlobCidString,\n isAtUriString,\n isDidString,\n} from '@atproto/lex'\nimport { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'\nimport type { ExternalRecord } from '../hydration/external.js'\nimport type { HydrationState } from '../hydration/hydrator.js'\nimport { app, com, place, site, social } from '../lexicons/index.js'\nimport { buildExternalRecordUrl } from '../util/external/record-url.js'\nimport { estimateReadingTimeMinutes } from '../util/external/standard-site.js'\nimport { uriToDid } from '../util/uris.js'\nimport type { Views } from './index.js'\nimport type { ExternalRecordView, Label, ProfileViewBasic } from './types.js'\n\nexport const LIVESTREAM_HEARTBEAT_WINDOW_MS = 2 * MINUTE\n\n/** Build a modality view from validated, available records in hydration state. */\nexport function externalRecordView(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n now: number,\n): $Typed<ExternalRecordView> | undefined {\n const info = availableRecord(views, uri, state)\n if (!info) return\n const { record } = info\n\n if (site.standard.document.$matches(record)) {\n let publisher:\n $Typed<app.bsky.embed.external.ViewArticlePublication> | undefined\n let base = record.site\n const refs = [uri]\n if (isAtUriString(record.site)) {\n publisher = publicationView(views, record.site, state)\n if (!publisher) return\n base = publisher.uri\n refs.push(record.site)\n }\n const url = httpUri(\n record.path\n ? `${base.replace(/\\/$/, '')}/${record.path.replace(/^\\//, '')}`\n : base,\n )\n if (!url || !record.title) return\n return app.bsky.embed.external.viewArticle.$build({\n ...commonFields(views, refs, state),\n uri: url,\n title: record.title,\n description: record.description ?? '',\n createdAt: record.publishedAt,\n updatedAt: record.updatedAt,\n image: record.coverImage\n ? views.imgUriBuilder.getPresetUri(\n 'feed_thumbnail',\n uriToDid(uri),\n getBlobCidString(record.coverImage),\n )\n : undefined,\n publisher,\n readingTime: record.textContent\n ? estimateReadingTimeMinutes(record.textContent)\n : undefined,\n likeCount: backlinkCount(uri, site.standard.graph.recommend.$type, state),\n likers: backlinkProfiles(views, uri, state, 'recommend'),\n })\n }\n\n if (site.standard.publication.$matches(record)) {\n return publicationView(views, uri, state)\n }\n\n if (social.grain.gallery.$matches(record)) {\n const links = (state.externalRecordBacklinks?.get(uri) ?? []).flatMap(\n (linkUri) => {\n const link = availableRecord(views, linkUri, state)?.record\n if (\n !social.grain.gallery.item.$matches(link) ||\n link.gallery !== uri ||\n uriToDid(linkUri) !== uriToDid(uri)\n ) {\n return []\n }\n return [{ uri: linkUri, record: link }]\n },\n )\n links.sort(\n (a, b) =>\n (a.record.position ?? 0) - (b.record.position ?? 0) ||\n compareStrings(a.uri, b.uri),\n )\n\n const refs = [uri]\n const seenPhotos = new Set<AtUriString>()\n const items: $Typed<app.bsky.embed.external.ViewGalleryImage>[] = []\n for (const link of links) {\n const photoUri = link.record.item\n if (seenPhotos.has(photoUri)) continue\n const photo = availableRecord(views, photoUri, state)?.record\n if (!social.grain.photo.$matches(photo)) continue\n seenPhotos.add(photoUri)\n refs.push(link.uri, photoUri)\n items.push(\n app.bsky.embed.external.viewGalleryImage.$build({\n thumbnail: views.imgUriBuilder.getPresetUri(\n 'feed_thumbnail',\n uriToDid(photoUri),\n getBlobCidString(photo.photo),\n ),\n fullsize: views.imgUriBuilder.getPresetUri(\n 'feed_fullsize',\n uriToDid(photoUri),\n getBlobCidString(photo.photo),\n ),\n alt: photo.alt,\n aspectRatio: photo.aspectRatio\n ? {\n width: photo.aspectRatio.width,\n height: photo.aspectRatio.height,\n }\n : undefined,\n }),\n )\n }\n return app.bsky.embed.external.viewGallery.$build({\n ...commonFields(views, refs, state),\n uri: buildExternalRecordUrl(uri) ?? uri,\n title: record.title,\n description: record.description ?? '',\n createdAt: record.createdAt,\n items,\n likeCount: backlinkCount(uri, social.grain.favorite.$type, state),\n likers: backlinkProfiles(views, uri, state, 'favorite'),\n })\n }\n\n if (place.stream.livestream.$matches(record)) {\n const lastSeen = record.lastSeenAt ? Date.parse(record.lastSeenAt) : NaN\n // @NOTE The window applies on both sides of now to tolerate clock skew, so\n // a future heartbeat counts as live for at most twice the window.\n const fresh = Math.abs(now - lastSeen) < LIVESTREAM_HEARTBEAT_WINDOW_MS\n return app.bsky.embed.external.viewLivestream.$build({\n ...commonFields(views, [uri], state),\n uri: httpUri(record.canonicalUrl) ?? httpUri(record.url) ?? uri,\n title: record.title,\n description: '',\n createdAt: record.createdAt,\n image: record.thumb\n ? views.imgUriBuilder.getPresetUri(\n 'feed_thumbnail',\n uriToDid(uri),\n getBlobCidString(record.thumb),\n )\n : undefined,\n active: !record.endedAt && fresh,\n startedAt: record.createdAt,\n endedAt: record.endedAt,\n })\n }\n}\n\nfunction publicationView(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n): $Typed<app.bsky.embed.external.ViewArticlePublication> | undefined {\n const record = availableRecord(views, uri, state)?.record\n if (!site.standard.publication.$matches(record)) return\n const url = httpUri(record.url)\n if (!url || !record.name) return\n return app.bsky.embed.external.viewArticlePublication.$build({\n ...commonFields(views, [uri], state),\n uri: url,\n title: record.name,\n description: record.description ?? '',\n logo: record.icon\n ? views.imgUriBuilder.getPresetUri(\n 'avatar',\n uriToDid(uri),\n getBlobCidString(record.icon),\n )\n : undefined,\n theme: record.basicTheme\n ? app.bsky.embed.external.viewArticlePublicationTheme.$build({\n background: hexColor(record.basicTheme.background),\n foreground: hexColor(record.basicTheme.foreground),\n accent: hexColor(record.basicTheme.accent),\n accentForeground: hexColor(record.basicTheme.accentForeground),\n })\n : undefined,\n subscriptionCount: backlinkCount(\n uri,\n site.standard.graph.subscription.$type,\n state,\n ),\n subscribers: backlinkProfiles(views, uri, state, 'subscription'),\n })\n}\n\nfunction availableRecord(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n): ExternalRecord | undefined {\n const info = state.externalRecords?.get(uri)\n if (!info) return\n const parsed = new AtUri(uri)\n if (!isDidString(parsed.host) || parsed.collection !== info.record.$type)\n return\n // @NOTE Actor hydration nulls unavailable accounts (missing and tombstoned\n // ones even with includeTakedowns). Absent entries just weren't hydrated.\n if (state.actors?.get(parsed.host) === null) return\n if (views.viewerBlockExists(parsed.host, state)) return\n if (!views.viewerSeesNeedsReview({ uri }, state)) return\n if (\n !state.ctx?.includeTakedowns &&\n (info.takedownRef ||\n state.labels?.get(uri)?.isTakendown ||\n views.actorIsNoHosted(parsed.host, state))\n ) {\n return\n }\n return info\n}\n\nfunction commonFields(\n views: Views,\n uris: AtUriString[],\n state: HydrationState,\n) {\n const refs = [...new Set(uris)]\n const dids = [...new Set(refs.map(uriToDid))]\n return {\n associatedRefs: refs.map((uri) =>\n com.atproto.repo.strongRef.$build({\n uri,\n cid: state.externalRecords!.get(uri)!.cid,\n }),\n ),\n associatedProfiles: dids.flatMap((did) => {\n const profile = views.profileBasic(did, state)\n return profile ? [profile] : []\n }),\n labels: refs.flatMap((uri) =>\n recordLabels(uri, state.externalRecords!.get(uri)!, state),\n ),\n }\n}\n\nfunction recordLabels(\n uri: AtUriString,\n info: ExternalRecord,\n state: HydrationState,\n): Label[] {\n const labels = state.labels?.getBySubject(uri) ?? []\n const selfLabels = info.record.labels\n if (!com.atproto.label.defs.selfLabels.$matches(selfLabels)) return labels\n const createdAt = info.record.createdAt ?? info.record.publishedAt\n const cts =\n typeof createdAt === 'string'\n ? normalizeDatetimeAlways(createdAt)\n : '1970-01-01T00:00:00.000Z'\n return [\n ...labels,\n ...selfLabels.values.map(({ val }) => ({\n src: uriToDid(uri),\n uri,\n cid: info.cid,\n val,\n cts,\n })),\n ]\n}\n\nfunction backlinkCount(\n uri: AtUriString,\n collection: NsidString,\n state: HydrationState,\n): number | undefined {\n const counts = state.externalRecordBacklinkCounts?.get(uri)\n if (!counts) return\n const count = counts[collection] ?? 0\n return Number.isSafeInteger(count) && count >= 0 ? count : undefined\n}\n\nfunction backlinkProfiles(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n kind: 'recommend' | 'subscription' | 'favorite',\n): ProfileViewBasic[] | undefined {\n const backlinks = state.externalRecordBacklinks?.get(uri)\n if (!backlinks) return\n const dids = new Set<DidString>()\n for (const backlink of backlinks) {\n const record = availableRecord(views, backlink, state)?.record\n const matches =\n kind === 'recommend'\n ? site.standard.graph.recommend.$matches(record) &&\n record.document === uri\n : kind === 'subscription'\n ? site.standard.graph.subscription.$matches(record) &&\n record.publication === uri\n : social.grain.favorite.$matches(record) && record.subject === uri\n if (matches) dids.add(uriToDid(backlink))\n }\n const profiles: ProfileViewBasic[] = []\n for (const did of [...dids].sort(compareStrings)) {\n const profile = views.profileBasic(did, state)\n if (profile) profiles.push(profile)\n if (profiles.length === 3) break\n }\n return profiles\n}\n\nfunction hexColor(\n color: site.standard.theme.basic.Main['background'],\n): string | undefined {\n if (site.standard.theme.color.rgb.$matches(color)) {\n return `#${[color.r, color.g, color.b].map(hexByte).join('')}`\n }\n}\n\nfunction hexByte(value: number): string {\n return value.toString(16).padStart(2, '0')\n}\n\nfunction httpUri(value: string | undefined): UriString | undefined {\n if (!value) return\n try {\n const url = new URL(value)\n if (url.protocol === 'http:' || url.protocol === 'https:')\n return url.href as UriString\n } catch {\n return\n }\n}\n\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\n}\n"]}
1
+ {"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/views/external.ts"],"names":[],"mappings":"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","sourcesContent":["import { MINUTE } from '@atproto/common'\nimport {\n type $Typed,\n type AtUriString,\n type DidString,\n type NsidString,\n type UriString,\n getBlobCidString,\n isAtUriString,\n isDidString,\n} from '@atproto/lex'\nimport { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'\nimport type { ExternalRecord } from '../hydration/external.js'\nimport type { HydrationState } from '../hydration/hydrator.js'\nimport { app, com, place, site, social } from '../lexicons/index.js'\nimport { buildExternalRecordUrl } from '../util/external/record-url.js'\nimport { estimateReadingTimeMinutes } from '../util/external/standard-site.js'\nimport { uriToDid } from '../util/uris.js'\nimport type { Views } from './index.js'\nimport type { ExternalRecordView, Label, ProfileViewBasic } from './types.js'\n\nexport const LIVESTREAM_HEARTBEAT_WINDOW_MS = 2 * MINUTE\n\n/** Build a modality view from validated, available records in hydration state. */\nexport function externalRecordView(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n now: number,\n): $Typed<ExternalRecordView> | undefined {\n return externalRecordViewOrRefs(views, uri, state, now, false)\n}\n\n/** Resolve renderable view dependencies without constructing presentation fields. */\nexport function externalRecordViewRefs(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n): AtUriString[] | undefined {\n return externalRecordViewOrRefs(views, uri, state, 0, true)\n}\n\nfunction externalRecordViewOrRefs(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n now: number,\n refsOnly: false,\n): $Typed<ExternalRecordView> | undefined\nfunction externalRecordViewOrRefs(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n now: number,\n refsOnly: true,\n): AtUriString[] | undefined\nfunction externalRecordViewOrRefs(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n now: number,\n refsOnly: boolean,\n): $Typed<ExternalRecordView> | AtUriString[] | undefined {\n const info = availableRecord(views, uri, state)\n if (!info) return\n const { record } = info\n\n if (site.standard.document.$matches(record)) {\n let publisher:\n $Typed<app.bsky.embed.external.ViewArticlePublication> | undefined\n let base = record.site\n const refs = [uri]\n if (isAtUriString(record.site)) {\n const publication = publicationRecord(views, record.site, state)\n if (!publication) return\n base = publication.url\n if (!refsOnly) {\n publisher = publicationView(views, record.site, state, publication)\n }\n refs.push(record.site)\n }\n const url = httpUri(\n record.path\n ? `${base.replace(/\\/$/, '')}/${record.path.replace(/^\\//, '')}`\n : base,\n )\n if (!url || !record.title) return\n if (refsOnly) return refs\n return app.bsky.embed.external.viewArticle.$build({\n ...commonFields(views, refs, state),\n uri: url,\n title: record.title,\n description: record.description ?? '',\n createdAt: record.publishedAt,\n updatedAt: record.updatedAt,\n image: record.coverImage\n ? views.imgUriBuilder.getPresetUri(\n 'feed_thumbnail',\n uriToDid(uri),\n getBlobCidString(record.coverImage),\n )\n : undefined,\n publisher,\n readingTime: record.textContent\n ? estimateReadingTimeMinutes(record.textContent)\n : undefined,\n likeCount: backlinkCount(uri, site.standard.graph.recommend.$type, state),\n likers: backlinkProfiles(views, uri, state, 'recommend'),\n })\n }\n\n if (site.standard.publication.$matches(record)) {\n if (refsOnly) {\n return publicationRecord(views, uri, state) ? [uri] : undefined\n }\n return publicationView(views, uri, state)\n }\n\n if (social.grain.gallery.$matches(record)) {\n const links = (state.externalRecordBacklinks?.get(uri) ?? []).flatMap(\n (linkUri) => {\n const link = availableRecord(views, linkUri, state)?.record\n if (\n !social.grain.gallery.item.$matches(link) ||\n link.gallery !== uri ||\n uriToDid(linkUri) !== uriToDid(uri)\n ) {\n return []\n }\n return [{ uri: linkUri, record: link }]\n },\n )\n links.sort(\n (a, b) =>\n (a.record.position ?? 0) - (b.record.position ?? 0) ||\n compareStrings(a.uri, b.uri),\n )\n\n const refs = [uri]\n const seenPhotos = new Set<AtUriString>()\n const items: $Typed<app.bsky.embed.external.ViewGalleryImage>[] = []\n for (const link of links) {\n const photoUri = link.record.item\n if (seenPhotos.has(photoUri)) continue\n const photo = availableRecord(views, photoUri, state)?.record\n if (!social.grain.photo.$matches(photo)) continue\n seenPhotos.add(photoUri)\n refs.push(link.uri, photoUri)\n if (refsOnly) continue\n items.push(\n app.bsky.embed.external.viewGalleryImage.$build({\n thumbnail: views.imgUriBuilder.getPresetUri(\n 'feed_thumbnail',\n uriToDid(photoUri),\n getBlobCidString(photo.photo),\n ),\n fullsize: views.imgUriBuilder.getPresetUri(\n 'feed_fullsize',\n uriToDid(photoUri),\n getBlobCidString(photo.photo),\n ),\n alt: photo.alt,\n aspectRatio: photo.aspectRatio\n ? {\n width: photo.aspectRatio.width,\n height: photo.aspectRatio.height,\n }\n : undefined,\n }),\n )\n }\n if (refsOnly) return refs\n return app.bsky.embed.external.viewGallery.$build({\n ...commonFields(views, refs, state),\n uri: buildExternalRecordUrl(uri) ?? uri,\n title: record.title,\n description: record.description ?? '',\n createdAt: record.createdAt,\n items,\n likeCount: backlinkCount(uri, social.grain.favorite.$type, state),\n likers: backlinkProfiles(views, uri, state, 'favorite'),\n })\n }\n\n if (place.stream.livestream.$matches(record)) {\n if (refsOnly) return [uri]\n const lastSeen = record.lastSeenAt ? Date.parse(record.lastSeenAt) : NaN\n // @NOTE The window applies on both sides of now to tolerate clock skew, so\n // a future heartbeat counts as live for at most twice the window.\n const fresh = Math.abs(now - lastSeen) < LIVESTREAM_HEARTBEAT_WINDOW_MS\n return app.bsky.embed.external.viewLivestream.$build({\n ...commonFields(views, [uri], state),\n uri: httpUri(record.canonicalUrl) ?? httpUri(record.url) ?? uri,\n title: record.title,\n description: '',\n createdAt: record.createdAt,\n image: record.thumb\n ? views.imgUriBuilder.getPresetUri(\n 'feed_thumbnail',\n uriToDid(uri),\n getBlobCidString(record.thumb),\n )\n : undefined,\n active: !record.endedAt && fresh,\n startedAt: record.createdAt,\n endedAt: record.endedAt,\n })\n }\n}\n\nfunction publicationView(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n publication = publicationRecord(views, uri, state),\n): $Typed<app.bsky.embed.external.ViewArticlePublication> | undefined {\n if (!publication) return\n const { record, url } = publication\n return app.bsky.embed.external.viewArticlePublication.$build({\n ...commonFields(views, [uri], state),\n uri: url,\n title: record.name,\n description: record.description ?? '',\n logo: record.icon\n ? views.imgUriBuilder.getPresetUri(\n 'avatar',\n uriToDid(uri),\n getBlobCidString(record.icon),\n )\n : undefined,\n theme: record.basicTheme\n ? app.bsky.embed.external.viewArticlePublicationTheme.$build({\n background: hexColor(record.basicTheme.background),\n foreground: hexColor(record.basicTheme.foreground),\n accent: hexColor(record.basicTheme.accent),\n accentForeground: hexColor(record.basicTheme.accentForeground),\n })\n : undefined,\n subscriptionCount: backlinkCount(\n uri,\n site.standard.graph.subscription.$type,\n state,\n ),\n subscribers: backlinkProfiles(views, uri, state, 'subscription'),\n })\n}\n\nfunction publicationRecord(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n) {\n const record = availableRecord(views, uri, state)?.record\n if (!site.standard.publication.$matches(record)) return\n const url = httpUri(record.url)\n if (!url || !record.name) return\n return { record, url }\n}\n\n/** Apply shared block, review, and takedown checks to a hydrated record. */\nexport function availableRecord(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n info: ExternalRecord | null | undefined = state.externalRecords?.get(uri),\n): ExternalRecord | undefined {\n if (!info) return\n const parsed = new AtUri(uri)\n if (!isDidString(parsed.host) || parsed.collection !== info.record.$type)\n return\n // @NOTE Actor hydration nulls unavailable accounts (missing and tombstoned\n // ones even with includeTakedowns). Absent entries just weren't hydrated.\n if (state.actors?.get(parsed.host) === null) return\n if (views.viewerBlockExists(parsed.host, state)) return\n if (!views.viewerSeesNeedsReview({ uri }, state)) return\n if (\n !state.ctx?.includeTakedowns &&\n (info.takedownRef ||\n state.labels?.get(uri)?.isTakendown ||\n views.actorIsNoHosted(parsed.host, state))\n ) {\n return\n }\n return info\n}\n\nfunction commonFields(\n views: Views,\n uris: AtUriString[],\n state: HydrationState,\n) {\n const refs = [...new Set(uris)]\n const dids = [...new Set(refs.map(uriToDid))]\n return {\n associatedRefs: refs.map((uri) =>\n com.atproto.repo.strongRef.$build({\n uri,\n cid: state.externalRecords!.get(uri)!.cid,\n }),\n ),\n associatedProfiles: dids.flatMap((did) => {\n const profile = views.profileBasic(did, state)\n return profile ? [profile] : []\n }),\n labels: refs.flatMap((uri) =>\n recordLabels(uri, state.externalRecords!.get(uri)!, state),\n ),\n }\n}\n\nfunction recordLabels(\n uri: AtUriString,\n info: ExternalRecord,\n state: HydrationState,\n): Label[] {\n const labels = state.labels?.getBySubject(uri) ?? []\n const selfLabels = info.record.labels\n if (!com.atproto.label.defs.selfLabels.$matches(selfLabels)) return labels\n const createdAt = info.record.createdAt ?? info.record.publishedAt\n const cts =\n typeof createdAt === 'string'\n ? normalizeDatetimeAlways(createdAt)\n : '1970-01-01T00:00:00.000Z'\n return [\n ...labels,\n ...selfLabels.values.map(({ val }) => ({\n src: uriToDid(uri),\n uri,\n cid: info.cid,\n val,\n cts,\n })),\n ]\n}\n\nfunction backlinkCount(\n uri: AtUriString,\n collection: NsidString,\n state: HydrationState,\n): number | undefined {\n const counts = state.externalRecordBacklinkCounts?.get(uri)\n if (!counts) return\n const count = counts[collection] ?? 0\n return Number.isSafeInteger(count) && count >= 0 ? count : undefined\n}\n\nfunction backlinkProfiles(\n views: Views,\n uri: AtUriString,\n state: HydrationState,\n kind: 'recommend' | 'subscription' | 'favorite',\n): ProfileViewBasic[] | undefined {\n const backlinks = state.externalRecordBacklinks?.get(uri)\n if (!backlinks) return\n const dids = new Set<DidString>()\n for (const backlink of backlinks) {\n const record = availableRecord(views, backlink, state)?.record\n const matches =\n kind === 'recommend'\n ? site.standard.graph.recommend.$matches(record) &&\n record.document === uri\n : kind === 'subscription'\n ? site.standard.graph.subscription.$matches(record) &&\n record.publication === uri\n : social.grain.favorite.$matches(record) && record.subject === uri\n if (matches) dids.add(uriToDid(backlink))\n }\n const profiles: ProfileViewBasic[] = []\n for (const did of [...dids].sort(compareStrings)) {\n const profile = views.profileBasic(did, state)\n if (profile) profiles.push(profile)\n if (profiles.length === 3) break\n }\n return profiles\n}\n\nfunction hexColor(\n color: site.standard.theme.basic.Main['background'],\n): string | undefined {\n if (site.standard.theme.color.rgb.$matches(color)) {\n return `#${[color.r, color.g, color.b].map(hexByte).join('')}`\n }\n}\n\nfunction hexByte(value: number): string {\n return value.toString(16).padStart(2, '0')\n}\n\nfunction httpUri(value: string | undefined): UriString | undefined {\n if (!value) return\n try {\n const url = new URL(value)\n if (url.protocol === 'http:' || url.protocol === 'https:')\n return url.href as UriString\n } catch {\n return\n }\n}\n\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -71,18 +71,18 @@
71
71
  "undici_v8": "npm:undici@^8.12.2",
72
72
  "zod": "3.23.8",
73
73
  "@atproto-labs/fetch-node": "^0.4.3",
74
- "@atproto-labs/opentelemetry-node": "^0.5.0",
75
74
  "@atproto-labs/xrpc-utils": "^0.1.28",
76
- "@atproto/api": "^0.29.6",
75
+ "@atproto/api": "^0.30.0",
77
76
  "@atproto/common": "^0.8.7",
78
- "@atproto/crypto": "^0.5.7",
79
77
  "@atproto/did": "^0.5.6",
78
+ "@atproto/crypto": "^0.5.7",
80
79
  "@atproto/identity": "^0.5.20",
80
+ "@atproto-labs/opentelemetry-node": "^0.5.0",
81
81
  "@atproto/lex": "^0.4.4",
82
82
  "@atproto/repo": "^0.11.2",
83
83
  "@atproto/sync": "^0.4.20",
84
- "@atproto/syntax": "^0.7.7",
85
- "@atproto/xrpc-server": "^0.13.7"
84
+ "@atproto/xrpc-server": "^0.13.7",
85
+ "@atproto/syntax": "^0.7.7"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@bufbuild/buf": "^1.73.0",