@atproto/bsky 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/api/app/bsky/actor/getProfile.js +1 -1
  3. package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
  4. package/dist/api/app/bsky/actor/getProfiles.js +1 -1
  5. package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
  6. package/dist/api/app/bsky/actor/getSuggestions.js +2 -2
  7. package/dist/api/app/bsky/actor/getSuggestions.js.map +1 -1
  8. package/dist/api/app/bsky/embed/getEmbedExternalView.d.ts.map +1 -1
  9. package/dist/api/app/bsky/embed/getEmbedExternalView.js +132 -53
  10. package/dist/api/app/bsky/embed/getEmbedExternalView.js.map +1 -1
  11. package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
  12. package/dist/api/app/bsky/feed/getAuthorFeed.js +4 -2
  13. package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
  14. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +2 -2
  15. package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
  16. package/dist/api/app/bsky/notification/constants.d.ts +4 -1
  17. package/dist/api/app/bsky/notification/constants.d.ts.map +1 -1
  18. package/dist/api/app/bsky/notification/constants.js +3 -0
  19. package/dist/api/app/bsky/notification/constants.js.map +1 -1
  20. package/dist/api/app/bsky/notification/getGroupedNotifications.js +19 -7
  21. package/dist/api/app/bsky/notification/getGroupedNotifications.js.map +1 -1
  22. package/dist/api/app/bsky/notification/grouping/spotlight.d.ts +1 -1
  23. package/dist/api/app/bsky/notification/grouping/spotlight.d.ts.map +1 -1
  24. package/dist/api/app/bsky/notification/grouping/spotlight.js +114 -80
  25. package/dist/api/app/bsky/notification/grouping/spotlight.js.map +1 -1
  26. package/dist/api/app/bsky/notification/listActivitySubscriptions.js +3 -2
  27. package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -1
  28. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts +4 -0
  29. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -0
  30. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js +142 -0
  31. package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -0
  32. package/dist/api/index.d.ts.map +1 -1
  33. package/dist/api/index.js +2 -0
  34. package/dist/api/index.js.map +1 -1
  35. package/dist/api/internal/bsky/actor/getProfiles.js +4 -1
  36. package/dist/api/internal/bsky/actor/getProfiles.js.map +1 -1
  37. package/dist/config.d.ts +4 -0
  38. package/dist/config.d.ts.map +1 -1
  39. package/dist/config.js +12 -0
  40. package/dist/config.js.map +1 -1
  41. package/dist/data-plane/server/indexing/index.d.ts +3 -1
  42. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  43. package/dist/data-plane/server/indexing/index.js +2 -0
  44. package/dist/data-plane/server/indexing/index.js.map +1 -1
  45. package/dist/data-plane/server/indexing/plugins/profile-link.d.ts +8 -0
  46. package/dist/data-plane/server/indexing/plugins/profile-link.d.ts.map +1 -0
  47. package/dist/data-plane/server/indexing/plugins/profile-link.js +30 -0
  48. package/dist/data-plane/server/indexing/plugins/profile-link.js.map +1 -0
  49. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  50. package/dist/data-plane/server/routes/records.js +1 -0
  51. package/dist/data-plane/server/routes/records.js.map +1 -1
  52. package/dist/hydration/actor.d.ts +4 -1
  53. package/dist/hydration/actor.d.ts.map +1 -1
  54. package/dist/hydration/actor.js +11 -0
  55. package/dist/hydration/actor.js.map +1 -1
  56. package/dist/hydration/external.d.ts +74 -70
  57. package/dist/hydration/external.d.ts.map +1 -1
  58. package/dist/hydration/external.js +79 -133
  59. package/dist/hydration/external.js.map +1 -1
  60. package/dist/hydration/hydrator.d.ts +57 -14
  61. package/dist/hydration/hydrator.d.ts.map +1 -1
  62. package/dist/hydration/hydrator.js +417 -105
  63. package/dist/hydration/hydrator.js.map +1 -1
  64. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +24 -0
  65. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
  66. package/dist/lexicons/app/bsky/actor/defs.defs.js +13 -0
  67. package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
  68. package/dist/lexicons/app/bsky/actor/link.d.ts +3 -0
  69. package/dist/lexicons/app/bsky/actor/link.d.ts.map +1 -0
  70. package/dist/lexicons/app/bsky/actor/link.defs.d.ts +105 -0
  71. package/dist/lexicons/app/bsky/actor/link.defs.d.ts.map +1 -0
  72. package/dist/lexicons/app/bsky/actor/link.defs.js +34 -0
  73. package/dist/lexicons/app/bsky/actor/link.defs.js.map +1 -0
  74. package/dist/lexicons/app/bsky/actor/link.js +6 -0
  75. package/dist/lexicons/app/bsky/actor/link.js.map +1 -0
  76. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts +14 -0
  77. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
  78. package/dist/lexicons/app/bsky/actor/profile.defs.js +3 -0
  79. package/dist/lexicons/app/bsky/actor/profile.defs.js.map +1 -1
  80. package/dist/lexicons/app/bsky/actor.d.ts +1 -0
  81. package/dist/lexicons/app/bsky/actor.d.ts.map +1 -1
  82. package/dist/lexicons/app/bsky/actor.js +1 -0
  83. package/dist/lexicons/app/bsky/actor.js.map +1 -1
  84. package/dist/lexicons/app/bsky/authFullApp.defs.d.ts.map +1 -1
  85. package/dist/lexicons/app/bsky/authFullApp.defs.js +1 -0
  86. package/dist/lexicons/app/bsky/authFullApp.defs.js.map +1 -1
  87. package/dist/lexicons/app/bsky/authManageProfile.defs.d.ts.map +1 -1
  88. package/dist/lexicons/app/bsky/authManageProfile.defs.js +1 -0
  89. package/dist/lexicons/app/bsky/authManageProfile.defs.js.map +1 -1
  90. package/dist/lexicons/app/bsky/embed/external.defs.d.ts +130 -0
  91. package/dist/lexicons/app/bsky/embed/external.defs.d.ts.map +1 -1
  92. package/dist/lexicons/app/bsky/embed/external.defs.js +135 -0
  93. package/dist/lexicons/app/bsky/embed/external.defs.js.map +1 -1
  94. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.d.ts +15 -3
  95. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.d.ts.map +1 -1
  96. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.js +18 -3
  97. package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.js.map +1 -1
  98. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.d.ts +62 -3
  99. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.d.ts.map +1 -1
  100. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.js +53 -2
  101. package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.js.map +1 -1
  102. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.d.ts +3 -0
  103. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -0
  104. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts +103 -0
  105. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts.map +1 -0
  106. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js +95 -0
  107. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js.map +1 -0
  108. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.js +6 -0
  109. package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -0
  110. package/dist/lexicons/app/bsky/unspecced.d.ts +1 -0
  111. package/dist/lexicons/app/bsky/unspecced.d.ts.map +1 -1
  112. package/dist/lexicons/app/bsky/unspecced.js +1 -0
  113. package/dist/lexicons/app/bsky/unspecced.js.map +1 -1
  114. package/dist/lexicons/index.d.ts +2 -0
  115. package/dist/lexicons/index.d.ts.map +1 -1
  116. package/dist/lexicons/index.js +2 -0
  117. package/dist/lexicons/index.js.map +1 -1
  118. package/dist/lexicons/place/stream/badge/defs.d.ts +2 -0
  119. package/dist/lexicons/place/stream/badge/defs.d.ts.map +1 -0
  120. package/dist/lexicons/place/stream/badge/defs.defs.d.ts +131 -0
  121. package/dist/lexicons/place/stream/badge/defs.defs.d.ts.map +1 -0
  122. package/dist/lexicons/place/stream/badge/defs.defs.js +73 -0
  123. package/dist/lexicons/place/stream/badge/defs.defs.js.map +1 -0
  124. package/dist/lexicons/place/stream/badge/defs.js +5 -0
  125. package/dist/lexicons/place/stream/badge/defs.js.map +1 -0
  126. package/dist/lexicons/place/stream/badge.d.ts +2 -0
  127. package/dist/lexicons/place/stream/badge.d.ts.map +1 -0
  128. package/dist/lexicons/place/stream/badge.js +5 -0
  129. package/dist/lexicons/place/stream/badge.js.map +1 -0
  130. package/dist/lexicons/place/stream/chat/defs.d.ts +2 -0
  131. package/dist/lexicons/place/stream/chat/defs.d.ts.map +1 -0
  132. package/dist/lexicons/place/stream/chat/defs.defs.d.ts +44 -0
  133. package/dist/lexicons/place/stream/chat/defs.defs.d.ts.map +1 -0
  134. package/dist/lexicons/place/stream/chat/defs.defs.js +41 -0
  135. package/dist/lexicons/place/stream/chat/defs.defs.js.map +1 -0
  136. package/dist/lexicons/place/stream/chat/defs.js +5 -0
  137. package/dist/lexicons/place/stream/chat/defs.js.map +1 -0
  138. package/dist/lexicons/place/stream/chat/pinnedRecord.d.ts +3 -0
  139. package/dist/lexicons/place/stream/chat/pinnedRecord.d.ts.map +1 -0
  140. package/dist/lexicons/place/stream/chat/pinnedRecord.defs.d.ts +108 -0
  141. package/dist/lexicons/place/stream/chat/pinnedRecord.defs.d.ts.map +1 -0
  142. package/dist/lexicons/place/stream/chat/pinnedRecord.defs.js +31 -0
  143. package/dist/lexicons/place/stream/chat/pinnedRecord.defs.js.map +1 -0
  144. package/dist/lexicons/place/stream/chat/pinnedRecord.js +6 -0
  145. package/dist/lexicons/place/stream/chat/pinnedRecord.js.map +1 -0
  146. package/dist/lexicons/place/stream/chat/profile.d.ts +3 -0
  147. package/dist/lexicons/place/stream/chat/profile.d.ts.map +1 -0
  148. package/dist/lexicons/place/stream/chat/profile.defs.d.ts +143 -0
  149. package/dist/lexicons/place/stream/chat/profile.defs.d.ts.map +1 -0
  150. package/dist/lexicons/place/stream/chat/profile.defs.js +62 -0
  151. package/dist/lexicons/place/stream/chat/profile.defs.js.map +1 -0
  152. package/dist/lexicons/place/stream/chat/profile.js +6 -0
  153. package/dist/lexicons/place/stream/chat/profile.js.map +1 -0
  154. package/dist/lexicons/place/stream/chat.d.ts +4 -0
  155. package/dist/lexicons/place/stream/chat.d.ts.map +1 -0
  156. package/dist/lexicons/place/stream/chat.js +7 -0
  157. package/dist/lexicons/place/stream/chat.js.map +1 -0
  158. package/dist/lexicons/place/stream/defs.d.ts +2 -0
  159. package/dist/lexicons/place/stream/defs.d.ts.map +1 -0
  160. package/dist/lexicons/place/stream/defs.defs.d.ts +54 -0
  161. package/dist/lexicons/place/stream/defs.defs.d.ts.map +1 -0
  162. package/dist/lexicons/place/stream/defs.defs.js +40 -0
  163. package/dist/lexicons/place/stream/defs.defs.js.map +1 -0
  164. package/dist/lexicons/place/stream/defs.js +5 -0
  165. package/dist/lexicons/place/stream/defs.js.map +1 -0
  166. package/dist/lexicons/place/stream/livestream.d.ts +3 -0
  167. package/dist/lexicons/place/stream/livestream.d.ts.map +1 -0
  168. package/dist/lexicons/place/stream/livestream.defs.d.ts +312 -0
  169. package/dist/lexicons/place/stream/livestream.defs.d.ts.map +1 -0
  170. package/dist/lexicons/place/stream/livestream.defs.js +105 -0
  171. package/dist/lexicons/place/stream/livestream.defs.js.map +1 -0
  172. package/dist/lexicons/place/stream/livestream.js +6 -0
  173. package/dist/lexicons/place/stream/livestream.js.map +1 -0
  174. package/dist/lexicons/place/stream.d.ts +5 -0
  175. package/dist/lexicons/place/stream.d.ts.map +1 -0
  176. package/dist/lexicons/place/stream.js +8 -0
  177. package/dist/lexicons/place/stream.js.map +1 -0
  178. package/dist/lexicons/place.d.ts +2 -0
  179. package/dist/lexicons/place.d.ts.map +1 -0
  180. package/dist/lexicons/place.js +5 -0
  181. package/dist/lexicons/place.js.map +1 -0
  182. package/dist/lexicons/site/standard/document.defs.d.ts +127 -127
  183. package/dist/lexicons/site/standard/document.defs.d.ts.map +1 -1
  184. package/dist/lexicons/site/standard/document.defs.js +28 -28
  185. package/dist/lexicons/site/standard/document.defs.js.map +1 -1
  186. package/dist/lexicons/site/standard/graph/recommend.defs.d.ts +11 -11
  187. package/dist/lexicons/site/standard/graph/recommend.defs.d.ts.map +1 -1
  188. package/dist/lexicons/site/standard/graph/recommend.defs.js +1 -1
  189. package/dist/lexicons/site/standard/graph/recommend.defs.js.map +1 -1
  190. package/dist/lexicons/site/standard/publication.defs.d.ts +52 -52
  191. package/dist/lexicons/site/standard/publication.defs.d.ts.map +1 -1
  192. package/dist/lexicons/site/standard/publication.defs.js +6 -6
  193. package/dist/lexicons/site/standard/publication.defs.js.map +1 -1
  194. package/dist/lexicons/site/standard/theme/basic.defs.d.ts +14 -14
  195. package/dist/lexicons/site/standard/theme/basic.defs.d.ts.map +1 -1
  196. package/dist/lexicons/site/standard/theme/basic.defs.js +1 -1
  197. package/dist/lexicons/site/standard/theme/basic.defs.js.map +1 -1
  198. package/dist/lexicons/social/grain/defs.d.ts +2 -0
  199. package/dist/lexicons/social/grain/defs.d.ts.map +1 -0
  200. package/dist/lexicons/social/grain/defs.defs.d.ts +15 -0
  201. package/dist/lexicons/social/grain/defs.defs.d.ts.map +1 -0
  202. package/dist/lexicons/social/grain/defs.defs.js +14 -0
  203. package/dist/lexicons/social/grain/defs.defs.js.map +1 -0
  204. package/dist/lexicons/social/grain/defs.js +5 -0
  205. package/dist/lexicons/social/grain/defs.js.map +1 -0
  206. package/dist/lexicons/social/grain/favorite.d.ts +3 -0
  207. package/dist/lexicons/social/grain/favorite.d.ts.map +1 -0
  208. package/dist/lexicons/social/grain/favorite.defs.d.ts +72 -0
  209. package/dist/lexicons/social/grain/favorite.defs.d.ts.map +1 -0
  210. package/dist/lexicons/social/grain/favorite.defs.js +26 -0
  211. package/dist/lexicons/social/grain/favorite.defs.js.map +1 -0
  212. package/dist/lexicons/social/grain/favorite.js +6 -0
  213. package/dist/lexicons/social/grain/favorite.js.map +1 -0
  214. package/dist/lexicons/social/grain/gallery/item.d.ts +3 -0
  215. package/dist/lexicons/social/grain/gallery/item.d.ts.map +1 -0
  216. package/dist/lexicons/social/grain/gallery/item.defs.d.ts +94 -0
  217. package/dist/lexicons/social/grain/gallery/item.defs.d.ts.map +1 -0
  218. package/dist/lexicons/social/grain/gallery/item.defs.js +29 -0
  219. package/dist/lexicons/social/grain/gallery/item.defs.js.map +1 -0
  220. package/dist/lexicons/social/grain/gallery/item.js +6 -0
  221. package/dist/lexicons/social/grain/gallery/item.js.map +1 -0
  222. package/dist/lexicons/social/grain/gallery.d.ts +4 -0
  223. package/dist/lexicons/social/grain/gallery.d.ts.map +1 -0
  224. package/dist/lexicons/social/grain/gallery.defs.d.ts +98 -0
  225. package/dist/lexicons/social/grain/gallery.defs.d.ts.map +1 -0
  226. package/dist/lexicons/social/grain/gallery.defs.js +33 -0
  227. package/dist/lexicons/social/grain/gallery.defs.js.map +1 -0
  228. package/dist/lexicons/social/grain/gallery.js +7 -0
  229. package/dist/lexicons/social/grain/gallery.js.map +1 -0
  230. package/dist/lexicons/social/grain/photo.d.ts +3 -0
  231. package/dist/lexicons/social/grain/photo.d.ts.map +1 -0
  232. package/dist/lexicons/social/grain/photo.defs.d.ts +98 -0
  233. package/dist/lexicons/social/grain/photo.defs.d.ts.map +1 -0
  234. package/dist/lexicons/social/grain/photo.defs.js +31 -0
  235. package/dist/lexicons/social/grain/photo.defs.js.map +1 -0
  236. package/dist/lexicons/social/grain/photo.js +6 -0
  237. package/dist/lexicons/social/grain/photo.js.map +1 -0
  238. package/dist/lexicons/social/grain.d.ts +5 -0
  239. package/dist/lexicons/social/grain.d.ts.map +1 -0
  240. package/dist/lexicons/social/grain.js +8 -0
  241. package/dist/lexicons/social/grain.js.map +1 -0
  242. package/dist/lexicons/social.d.ts +2 -0
  243. package/dist/lexicons/social.d.ts.map +1 -0
  244. package/dist/lexicons/social.js +5 -0
  245. package/dist/lexicons/social.js.map +1 -0
  246. package/dist/proto/bsky_connect.d.ts +10 -1
  247. package/dist/proto/bsky_connect.d.ts.map +1 -1
  248. package/dist/proto/bsky_connect.js +10 -1
  249. package/dist/proto/bsky_connect.js.map +1 -1
  250. package/dist/proto/bsky_pb.d.ts +34 -0
  251. package/dist/proto/bsky_pb.d.ts.map +1 -1
  252. package/dist/proto/bsky_pb.js +60 -0
  253. package/dist/proto/bsky_pb.js.map +1 -1
  254. package/dist/telemetry/events.d.ts +30 -0
  255. package/dist/telemetry/events.d.ts.map +1 -1
  256. package/dist/telemetry/events.js +37 -0
  257. package/dist/telemetry/events.js.map +1 -1
  258. package/dist/util/external/index.d.ts +8 -0
  259. package/dist/util/external/index.d.ts.map +1 -0
  260. package/dist/util/external/index.js +50 -0
  261. package/dist/util/external/index.js.map +1 -0
  262. package/dist/util/{standard-site.d.ts → external/standard-site.d.ts} +30 -4
  263. package/dist/util/external/standard-site.d.ts.map +1 -0
  264. package/dist/util/{standard-site.js → external/standard-site.js} +73 -4
  265. package/dist/util/external/standard-site.js.map +1 -0
  266. package/dist/views/external.d.ts +8 -0
  267. package/dist/views/external.d.ts.map +1 -0
  268. package/dist/views/external.js +260 -0
  269. package/dist/views/external.js.map +1 -0
  270. package/dist/views/index.d.ts +12 -6
  271. package/dist/views/index.d.ts.map +1 -1
  272. package/dist/views/index.js +89 -42
  273. package/dist/views/index.js.map +1 -1
  274. package/dist/views/profile-link-rules.d.ts +10 -0
  275. package/dist/views/profile-link-rules.d.ts.map +1 -0
  276. package/dist/views/profile-link-rules.js +48 -0
  277. package/dist/views/profile-link-rules.js.map +1 -0
  278. package/dist/views/types.d.ts +3 -0
  279. package/dist/views/types.d.ts.map +1 -1
  280. package/dist/views/types.js.map +1 -1
  281. package/package.json +4 -4
  282. package/dist/util/standard-site.d.ts.map +0 -1
  283. package/dist/util/standard-site.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # @atproto/bsky
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#5615](https://github.com/bluesky-social/atproto/pull/5615) [`a6f5a3c`](https://github.com/bluesky-social/atproto/commit/a6f5a3c7f5e294e96971810d61f23b8f5998a5e2) Thanks [@vineyardbovines](https://github.com/vineyardbovines)! - Hydrate `app.bsky.actor.link` records into `links` on detailed profile views, leaving out taken-down links and links to URL shorteners.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#5631](https://github.com/bluesky-social/atproto/pull/5631) [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Return basic starter-pack views in grouped notifications, avoiding feed and list-member hydration.
12
+
13
+ - [#5636](https://github.com/bluesky-social/atproto/pull/5636) [`5e0e11d`](https://github.com/bluesky-social/atproto/commit/5e0e11db6d42758e4fb763bcbfb6d2affdf7b2e9) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add the optional `BSKY_EXTERNAL_RECORDS_SAFE_DOMAINS` comma-separated setting, exposed as `ServerConfig.externalRecordsSafeDomains`, which defaults to an empty list.
14
+
15
+ - [#5638](https://github.com/bluesky-social/atproto/pull/5638) [`ba08e05`](https://github.com/bluesky-social/atproto/commit/ba08e05d86f7cdb71ea11d2b620ac2d7eec6b532) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Make external view dependency hydration best effort.
16
+
17
+ - [#5631](https://github.com/bluesky-social/atproto/pull/5631) [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Limit known-follower hydration in grouped notifications to the first ten actors per follow group.
18
+
19
+ - [#5635](https://github.com/bluesky-social/atproto/pull/5635) [`cffb239`](https://github.com/bluesky-social/atproto/commit/cffb239f483be851f3eb0e09243d4739f5bc9cd0) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Replace custom hydration in `getAtmosphereExploreTab` with generic external-view hydration, using the first URI in each worker group as its canonical root and discovering dependencies automatically.
20
+
21
+ - [#5636](https://github.com/bluesky-social/atproto/pull/5636) [`5e0e11d`](https://github.com/bluesky-social/atproto/commit/5e0e11db6d42758e4fb763bcbfb6d2affdf7b2e9) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Organize external hydration helpers and their specific types below the hydrator class without changing behavior.
22
+
23
+ - [#5614](https://github.com/bluesky-social/atproto/pull/5614) [`976ebfb`](https://github.com/bluesky-social/atproto/commit/976ebfb1f1c0feea00d12e3eedbcaca2a690a030) Thanks [@foysalit](https://github.com/foysalit)! - Allow the trusted moderation service to bypass actor and list blocks when fetching author feeds.
24
+
25
+ - [#5635](https://github.com/bluesky-social/atproto/pull/5635) [`cffb239`](https://github.com/bluesky-social/atproto/commit/cffb239f483be851f3eb0e09243d4739f5bc9cd0) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Refactor external record hydration into a shared, bounded pipeline for latest-URI and exact-ref lookups, including dependency discovery, moderation, deduplication, and traversal metrics for Standard Site, Grain, and Streamplace records.
26
+
27
+ - [#5636](https://github.com/bluesky-social/atproto/pull/5636) [`5e0e11d`](https://github.com/bluesky-social/atproto/commit/5e0e11db6d42758e4fb763bcbfb6d2affdf7b2e9) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add standalone Standard.site record selection/validation and safe-domain URL matching utilities for external embeds.
28
+
29
+ - [#5638](https://github.com/bluesky-social/atproto/pull/5638) [`ba08e05`](https://github.com/bluesky-social/atproto/commit/ba08e05d86f7cdb71ea11d2b620ac2d7eec6b532) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Remove legacy Standard.site external-view hydration.
30
+
31
+ - [#5636](https://github.com/bluesky-social/atproto/pull/5636) [`5e0e11d`](https://github.com/bluesky-social/atproto/commit/5e0e11db6d42758e4fb763bcbfb6d2affdf7b2e9) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Make `app.bsky.embed.getEmbedExternalView` resolve a single canonical record (from `canonicalUri`, or normalized from the legacy `uris` input, which is now optional), validate it against the requested URL before building views, return generic `data` alongside the Standard.site-only legacy `view`, and report records that fail validation in `unverifiedAssociatedRefs`/`unverifiedAssociatedRecords`.
32
+
33
+ - [#5633](https://github.com/bluesky-social/atproto/pull/5633) [`dbb0bb5`](https://github.com/bluesky-social/atproto/commit/dbb0bb54a6228021b4169a82a3e8dbe38b42fd4c) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Extend grouped notification spotlighting to reposts and via-repost interactions, with up to three spotlights per page.
34
+
35
+ - [#5631](https://github.com/bluesky-social/atproto/pull/5631) [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Run profile hydration through a shared worker and let callers request activity-subscription state alongside profile data.
36
+
37
+ - [#5638](https://github.com/bluesky-social/atproto/pull/5638) [`ba08e05`](https://github.com/bluesky-social/atproto/commit/ba08e05d86f7cdb71ea11d2b620ac2d7eec6b532) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Delete unused legacy Standard.site URI hydration helpers.
38
+
39
+ - [#5631](https://github.com/bluesky-social/atproto/pull/5631) [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Hydrate profiles and subscription settings in listActivitySubscriptions without fetching unused known followers, profile counts, or joined-via starter packs.
40
+
41
+ - [#5636](https://github.com/bluesky-social/atproto/pull/5636) [`5e0e11d`](https://github.com/bluesky-social/atproto/commit/5e0e11db6d42758e4fb763bcbfb6d2affdf7b2e9) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Keep persisted `getEmbedExternalView` associations slim: always return the canonical record, plus only an opted-in set of dependency collections (a Standard.site document's publication, a Grain gallery's items and photos). Recommendation, subscription and favorite records still back rendered previews but are no longer returned for persistence.
42
+
43
+ - [#5631](https://github.com/bluesky-social/atproto/pull/5631) [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d) Thanks [@rafaeleyng](https://github.com/rafaeleyng)! - Return basic profiles with known followers in grouped notification related views, avoiding detailed-profile and activity-subscription hydration.
44
+ - Updated dependencies [[`d70b673`](https://github.com/bluesky-social/atproto/commit/d70b673be6bce3b6f4d7478254e359913a101c18), [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d), [`5e0e11d`](https://github.com/bluesky-social/atproto/commit/5e0e11db6d42758e4fb763bcbfb6d2affdf7b2e9), [`dbb0bb5`](https://github.com/bluesky-social/atproto/commit/dbb0bb54a6228021b4169a82a3e8dbe38b42fd4c), [`647cb41`](https://github.com/bluesky-social/atproto/commit/647cb412576f1ba92c63aa00c71743cb400bdf8d)]:
45
+ - @atproto/api@0.25.0
46
+
47
+ ## 0.1.7
48
+
49
+ ### Patch Changes
50
+
51
+ - [#5617](https://github.com/bluesky-social/atproto/pull/5617) [`94e3838`](https://github.com/bluesky-social/atproto/commit/94e3838ea3bb1785a1989931a815b62b2057e5db) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add app.bsky.unspecced.getAtmosphereExploreTab with curated CMS content, canonical external modality views, and BSKY_ATMOSPHERE_EXPLORE_TAB_SERVICE configuration. Accept optional langs, countryCode, and regionCode parameters, forwarding countryCode and regionCode to the CMS.
52
+
53
+ - [#5599](https://github.com/bluesky-social/atproto/pull/5599) [`648f7c9`](https://github.com/bluesky-social/atproto/commit/648f7c9ebff13a51341977abd7fe2f5b603a2783) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add canonical Atmosphere modality view schemas.
54
+
55
+ - [#5607](https://github.com/bluesky-social/atproto/pull/5607) [`6831a2b`](https://github.com/bluesky-social/atproto/commit/6831a2bc6041039bef670b37da624ccb164b6231) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Determine livestream view activity from a two-minute `lastSeenAt` heartbeat window.
56
+
57
+ - [#5607](https://github.com/bluesky-social/atproto/pull/5607) [`6831a2b`](https://github.com/bluesky-social/atproto/commit/6831a2bc6041039bef670b37da624ccb164b6231) Thanks [@estrattonbailey](https://github.com/estrattonbailey)! - Add article, publication, gallery, and livestream record modality view builders.
58
+ - Updated dependencies [[`94e3838`](https://github.com/bluesky-social/atproto/commit/94e3838ea3bb1785a1989931a815b62b2057e5db), [`648f7c9`](https://github.com/bluesky-social/atproto/commit/648f7c9ebff13a51341977abd7fe2f5b603a2783)]:
59
+ - @atproto/api@0.24.3
60
+
3
61
  ## 0.1.6
4
62
 
5
63
  ### Patch Changes
@@ -40,7 +40,7 @@ const hydration = async (input) => {
40
40
  const { ctx, params, skeleton } = input;
41
41
  return ctx.hydrator.hydrateProfilesDetailed([skeleton.did], params.hydrateCtx.copy({
42
42
  overrideIncludeTakedownsForActor: true,
43
- }));
43
+ }), { knownFollowersDids: [skeleton.did], activitySubscriptions: true });
44
44
  };
45
45
  const presentation = (input) => {
46
46
  const { ctx, params, skeleton, hydration } = input;
@@ -1 +1 @@
1
- {"version":3,"file":"getProfile.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/actor/getProfile.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DidString } from '@atproto/syntax'\nimport { InvalidRequestError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline, noRules } from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getProfile = createPipeline(skeleton, hydration, noRules, presentation)\n server.add(app.bsky.actor.getProfile, {\n auth: ctx.authVerifier.optionalStandardOrRole,\n handler: async ({ auth, params, req }) => {\n const { viewer, includeTakedowns, skipViewerBlocks } =\n ctx.authVerifier.parseCreds(auth)\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n includeTakedowns,\n skipViewerBlocks,\n })\n\n const result = await getProfile({ ...params, hydrateCtx }, ctx)\n\n const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({\n repoRev,\n labelers: hydrateCtx.labelers,\n }),\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<SkeletonState> => {\n const { ctx, params } = input\n const [did] = await ctx.hydrator.actor.getDids([params.actor])\n if (!did) {\n throw new InvalidRequestError('Profile not found')\n }\n return { did }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(\n [skeleton.did],\n params.hydrateCtx.copy({\n overrideIncludeTakedownsForActor: true,\n }),\n )\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n hydration: HydrationState\n}) => {\n const { ctx, params, skeleton, hydration } = input\n const profile = ctx.views.profileDetailed(skeleton.did, hydration)\n if (!profile) {\n throw new InvalidRequestError('Profile not found')\n } else if (!params.hydrateCtx.includeTakedowns) {\n if (ctx.views.actorIsTakendown(skeleton.did, hydration)) {\n throw new InvalidRequestError(\n 'Account has been suspended',\n 'AccountTakedown',\n )\n } else if (ctx.views.actorIsDeactivated(skeleton.did, hydration)) {\n throw new InvalidRequestError(\n 'Account is deactivated',\n 'AccountDeactivated',\n )\n }\n }\n return profile\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.actor.getProfile.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype SkeletonState = { did: DidString }\n"]}
1
+ {"version":3,"file":"getProfile.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/actor/getProfile.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DidString } from '@atproto/syntax'\nimport { InvalidRequestError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline, noRules } from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getProfile = createPipeline(skeleton, hydration, noRules, presentation)\n server.add(app.bsky.actor.getProfile, {\n auth: ctx.authVerifier.optionalStandardOrRole,\n handler: async ({ auth, params, req }) => {\n const { viewer, includeTakedowns, skipViewerBlocks } =\n ctx.authVerifier.parseCreds(auth)\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n includeTakedowns,\n skipViewerBlocks,\n })\n\n const result = await getProfile({ ...params, hydrateCtx }, ctx)\n\n const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({\n repoRev,\n labelers: hydrateCtx.labelers,\n }),\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<SkeletonState> => {\n const { ctx, params } = input\n const [did] = await ctx.hydrator.actor.getDids([params.actor])\n if (!did) {\n throw new InvalidRequestError('Profile not found')\n }\n return { did }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(\n [skeleton.did],\n params.hydrateCtx.copy({\n overrideIncludeTakedownsForActor: true,\n }),\n { knownFollowersDids: [skeleton.did], activitySubscriptions: true },\n )\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n hydration: HydrationState\n}) => {\n const { ctx, params, skeleton, hydration } = input\n const profile = ctx.views.profileDetailed(skeleton.did, hydration)\n if (!profile) {\n throw new InvalidRequestError('Profile not found')\n } else if (!params.hydrateCtx.includeTakedowns) {\n if (ctx.views.actorIsTakendown(skeleton.did, hydration)) {\n throw new InvalidRequestError(\n 'Account has been suspended',\n 'AccountTakedown',\n )\n } else if (ctx.views.actorIsDeactivated(skeleton.did, hydration)) {\n throw new InvalidRequestError(\n 'Account is deactivated',\n 'AccountDeactivated',\n )\n }\n }\n return profile\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.actor.getProfile.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype SkeletonState = { did: DidString }\n"]}
@@ -47,7 +47,7 @@ const skeleton = async (input) => {
47
47
  };
48
48
  const hydration = async (input) => {
49
49
  const { ctx, params, skeleton } = input;
50
- return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx);
50
+ return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx, { knownFollowersDids: skeleton.dids, activitySubscriptions: true });
51
51
  };
52
52
  const presentation = (input) => {
53
53
  const { ctx, skeleton, hydration } = input;
@@ -1 +1 @@
1
- {"version":3,"file":"getProfiles.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/actor/getProfiles.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline, noRules } from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getProfile = createPipeline(skeleton, hydration, noRules, presentation)\n server.add(app.bsky.actor.getProfiles, {\n auth: ctx.authVerifier.standardOptionalParameterized({\n lxmCheck: (method) => {\n if (!method) return false\n return (\n method === app.bsky.actor.getProfiles.$lxm ||\n method.startsWith('chat.bsky.')\n )\n },\n }),\n opts: {\n // @TODO remove after grace period has passed, behavior is non-standard.\n // temporarily added for compat w/ previous version of xrpc-server to avoid breakage of a few specified parties.\n paramsParseLoose: true,\n },\n handler: async ({ auth, params, req }) => {\n const { viewer, includeTakedowns, skipViewerBlocks } =\n ctx.authVerifier.parseCreds(auth)\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers,\n includeTakedowns,\n skipViewerBlocks,\n })\n\n const result = await getProfile({ ...params, hydrateCtx }, ctx)\n\n const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({\n repoRev,\n labelers: hydrateCtx.labelers,\n }),\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<SkeletonState> => {\n const { ctx, params } = input\n const dids = await ctx.hydrator.actor.getDidsDefined(params.actors)\n return { dids }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx)\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n const profiles = mapDefined(skeleton.dids, (did) =>\n ctx.views.profileDetailed(did, hydration),\n )\n return { profiles }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.actor.getProfiles.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype SkeletonState = { dids: DidString[] }\n"]}
1
+ {"version":3,"file":"getProfiles.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/actor/getProfiles.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { DidString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline, noRules } from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getProfile = createPipeline(skeleton, hydration, noRules, presentation)\n server.add(app.bsky.actor.getProfiles, {\n auth: ctx.authVerifier.standardOptionalParameterized({\n lxmCheck: (method) => {\n if (!method) return false\n return (\n method === app.bsky.actor.getProfiles.$lxm ||\n method.startsWith('chat.bsky.')\n )\n },\n }),\n opts: {\n // @TODO remove after grace period has passed, behavior is non-standard.\n // temporarily added for compat w/ previous version of xrpc-server to avoid breakage of a few specified parties.\n paramsParseLoose: true,\n },\n handler: async ({ auth, params, req }) => {\n const { viewer, includeTakedowns, skipViewerBlocks } =\n ctx.authVerifier.parseCreds(auth)\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers,\n includeTakedowns,\n skipViewerBlocks,\n })\n\n const result = await getProfile({ ...params, hydrateCtx }, ctx)\n\n const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({\n repoRev,\n labelers: hydrateCtx.labelers,\n }),\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<SkeletonState> => {\n const { ctx, params } = input\n const dids = await ctx.hydrator.actor.getDidsDefined(params.actors)\n return { dids }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(\n skeleton.dids,\n params.hydrateCtx,\n { knownFollowersDids: skeleton.dids, activitySubscriptions: true },\n )\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: SkeletonState\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n const profiles = mapDefined(skeleton.dids, (did) =>\n ctx.views.profileDetailed(did, hydration),\n )\n return { profiles }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.actor.getProfiles.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype SkeletonState = { dids: DidString[] }\n"]}
@@ -86,7 +86,7 @@ const skeleton = async (input) => {
86
86
  };
87
87
  const hydration = async (input) => {
88
88
  const { ctx, params, skeleton } = input;
89
- return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx);
89
+ return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx, { knownFollowersDids: skeleton.dids, activitySubscriptions: true });
90
90
  };
91
91
  const noBlocksOrMutes = (input) => {
92
92
  const { ctx, skeleton, hydration } = input;
@@ -96,7 +96,7 @@ const noBlocksOrMutes = (input) => {
96
96
  };
97
97
  const presentation = (input) => {
98
98
  const { ctx, skeleton, hydration } = input;
99
- const actors = mapDefined(skeleton.dids, (did) => ctx.views.profileKnownFollowers(did, hydration));
99
+ const actors = mapDefined(skeleton.dids, (did) => ctx.views.profile(did, hydration));
100
100
  return {
101
101
  actors,
102
102
  cursor: skeleton.cursor,
@@ -1 +1 @@
1
- {"version":3,"file":"getSuggestions.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/actor/getSuggestions.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined, noUndefinedVals } from '@atproto/common'\nimport { type Client, type DidString, isDidString } from '@atproto/lex'\nimport type { Headers as HeadersMap, Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type { DataPlaneClient } from '../../../../data-plane/index.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { parseString } from '../../../../hydration/util.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline } from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\nimport { fillPage, resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getSuggestions = createPipeline(\n skeleton,\n hydration,\n noBlocksOrMutes,\n presentation,\n )\n server.add(app.bsky.actor.getSuggestions, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ params, auth, req, signal }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers,\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 { resHeaders: resultHeaders, ...result } = await fillPage({\n cursor: params.cursor,\n limit: params.limit,\n fetch: ({ cursor, limit }) =>\n getSuggestions(\n { ...params, cursor, limit, hydrateCtx, headers, signal },\n ctx,\n ),\n items: (r) => r.actors,\n })\n const suggestionsResHeaders = noUndefinedVals({\n 'content-language': resultHeaders?.get('content-language'),\n })\n return {\n encoding: 'application/json',\n body: result,\n headers: {\n ...suggestionsResHeaders,\n ...resHeaders({ labelers: hydrateCtx.labelers }),\n },\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<Skeleton> => {\n const { ctx, params } = input\n const viewer = params.hydrateCtx.viewer\n const useIris = params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.SuggestedUsersIrisEnable,\n )\n const client = useIris ? ctx.irisClient : ctx.suggestionsClient\n\n if (viewer && client) {\n const res = await client.xrpc(app.bsky.unspecced.getSuggestionsSkeleton, {\n headers: params.headers,\n signal: params.signal,\n params: {\n relativeToDid: viewer,\n viewer: viewer ?? undefined,\n limit: params.limit,\n cursor: params.cursor,\n },\n })\n return {\n dids: res.body.actors.map((a) => a.did),\n cursor: res.body.cursor,\n recId: res.body.recId,\n recIdStr: res.body.recIdStr,\n resHeaders: res.headers,\n }\n } else {\n // @NOTE for appview swap moving to rkey-based cursors which are somewhat permissive, should not hard-break pagination\n const suggestions = await ctx.dataplane.getFollowSuggestions({\n actorDid: viewer ?? undefined,\n cursor: params.cursor,\n limit: params.limit,\n })\n // @NOTE filtering to avoid type casting\n let dids = suggestions.dids.filter(isDidString)\n if (viewer !== null) {\n const follows = await ctx.dataplane.getActorFollowsActors({\n actorDid: viewer,\n targetDids: dids,\n })\n dids = dids.filter((did, i) => !follows.uris[i] && did !== viewer)\n }\n return { dids, cursor: parseString(suggestions.cursor) }\n }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx)\n}\n\nconst noBlocksOrMutes = (input: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n skeleton.dids = skeleton.dids.filter(\n (did) =>\n !ctx.views.viewerBlockExists(did, hydration) &&\n !ctx.views.viewerMuteExists(did, hydration),\n )\n return skeleton\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n const actors = mapDefined(skeleton.dids, (did) =>\n ctx.views.profileKnownFollowers(did, hydration),\n )\n return {\n actors,\n cursor: skeleton.cursor,\n recId: skeleton.recId,\n recIdStr: skeleton.recIdStr,\n resHeaders: skeleton.resHeaders,\n }\n}\n\ntype Context = {\n suggestionsClient: Client | undefined\n irisClient: Client | undefined\n dataplane: DataPlaneClient\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.actor.getSuggestions.$Params & {\n hydrateCtx: HydrateCtx\n headers: HeadersMap\n signal: AbortSignal\n}\n\ntype Skeleton = {\n dids: DidString[]\n cursor?: string\n recId?: number\n recIdStr?: string\n resHeaders?: Headers\n}\n"]}
1
+ {"version":3,"file":"getSuggestions.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/actor/getSuggestions.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined, noUndefinedVals } from '@atproto/common'\nimport { type Client, type DidString, isDidString } from '@atproto/lex'\nimport type { Headers as HeadersMap, Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type { DataPlaneClient } from '../../../../data-plane/index.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { parseString } from '../../../../hydration/util.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline } from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\nimport { fillPage, resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getSuggestions = createPipeline(\n skeleton,\n hydration,\n noBlocksOrMutes,\n presentation,\n )\n server.add(app.bsky.actor.getSuggestions, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ params, auth, req, signal }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers,\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 { resHeaders: resultHeaders, ...result } = await fillPage({\n cursor: params.cursor,\n limit: params.limit,\n fetch: ({ cursor, limit }) =>\n getSuggestions(\n { ...params, cursor, limit, hydrateCtx, headers, signal },\n ctx,\n ),\n items: (r) => r.actors,\n })\n const suggestionsResHeaders = noUndefinedVals({\n 'content-language': resultHeaders?.get('content-language'),\n })\n return {\n encoding: 'application/json',\n body: result,\n headers: {\n ...suggestionsResHeaders,\n ...resHeaders({ labelers: hydrateCtx.labelers }),\n },\n }\n },\n })\n}\n\nconst skeleton = async (input: {\n ctx: Context\n params: Params\n}): Promise<Skeleton> => {\n const { ctx, params } = input\n const viewer = params.hydrateCtx.viewer\n const useIris = params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.SuggestedUsersIrisEnable,\n )\n const client = useIris ? ctx.irisClient : ctx.suggestionsClient\n\n if (viewer && client) {\n const res = await client.xrpc(app.bsky.unspecced.getSuggestionsSkeleton, {\n headers: params.headers,\n signal: params.signal,\n params: {\n relativeToDid: viewer,\n viewer: viewer ?? undefined,\n limit: params.limit,\n cursor: params.cursor,\n },\n })\n return {\n dids: res.body.actors.map((a) => a.did),\n cursor: res.body.cursor,\n recId: res.body.recId,\n recIdStr: res.body.recIdStr,\n resHeaders: res.headers,\n }\n } else {\n // @NOTE for appview swap moving to rkey-based cursors which are somewhat permissive, should not hard-break pagination\n const suggestions = await ctx.dataplane.getFollowSuggestions({\n actorDid: viewer ?? undefined,\n cursor: params.cursor,\n limit: params.limit,\n })\n // @NOTE filtering to avoid type casting\n let dids = suggestions.dids.filter(isDidString)\n if (viewer !== null) {\n const follows = await ctx.dataplane.getActorFollowsActors({\n actorDid: viewer,\n targetDids: dids,\n })\n dids = dids.filter((did, i) => !follows.uris[i] && did !== viewer)\n }\n return { dids, cursor: parseString(suggestions.cursor) }\n }\n}\n\nconst hydration = async (input: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n}) => {\n const { ctx, params, skeleton } = input\n return ctx.hydrator.hydrateProfilesDetailed(\n skeleton.dids,\n params.hydrateCtx,\n { knownFollowersDids: skeleton.dids, activitySubscriptions: true },\n )\n}\n\nconst noBlocksOrMutes = (input: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n skeleton.dids = skeleton.dids.filter(\n (did) =>\n !ctx.views.viewerBlockExists(did, hydration) &&\n !ctx.views.viewerMuteExists(did, hydration),\n )\n return skeleton\n}\n\nconst presentation = (input: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = input\n const actors = mapDefined(skeleton.dids, (did) =>\n ctx.views.profile(did, hydration),\n )\n return {\n actors,\n cursor: skeleton.cursor,\n recId: skeleton.recId,\n recIdStr: skeleton.recIdStr,\n resHeaders: skeleton.resHeaders,\n }\n}\n\ntype Context = {\n suggestionsClient: Client | undefined\n irisClient: Client | undefined\n dataplane: DataPlaneClient\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.actor.getSuggestions.$Params & {\n hydrateCtx: HydrateCtx\n headers: HeadersMap\n signal: AbortSignal\n}\n\ntype Skeleton = {\n dids: DidString[]\n cursor?: string\n recId?: number\n recIdStr?: string\n resHeaders?: Headers\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getEmbedExternalView.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAmBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqBvD"}
1
+ {"version":3,"file":"getEmbedExternalView.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAuBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqBvD"}
@@ -1,6 +1,9 @@
1
- import { getSiteStandardRecordsFromHydrationMapsByDocumentUri, } from '../../../../hydration/external.js';
2
- import { app, com } from '../../../../lexicons/index.js';
1
+ import { isDidString, } from '@atproto/lex';
2
+ import { parseAtUriString } from '@atproto/syntax';
3
+ import { app, com, site, social } from '../../../../lexicons/index.js';
3
4
  import { createPipeline, noRules, } from '../../../../pipeline.js';
5
+ import { isExternalRecordUrlSafe } from '../../../../util/external/index.js';
6
+ import { getStandardSiteRecordsForUri, validateStandardSiteRecordsForUrl, } from '../../../../util/external/standard-site.js';
4
7
  import { resHeaders } from '../../../util.js';
5
8
  export default function (server, ctx) {
6
9
  const getEmbedExternalView = createPipeline(skeleton, hydration, noRules, presentation);
@@ -20,67 +23,143 @@ export default function (server, ctx) {
20
23
  });
21
24
  }
22
25
  const skeleton = async (inputs) => {
23
- return { uris: inputs.params.uris };
26
+ return { canonicalUri: normalizeCanonicalUri(inputs.params) };
24
27
  };
25
28
  const hydration = async (inputs) => {
26
29
  const { ctx, params, skeleton } = inputs;
27
- return ctx.hydrator.hydrateEmbedExternalViewFromUris(skeleton.uris, params.hydrateCtx);
30
+ if (!skeleton.canonicalUri)
31
+ return { ctx: params.hydrateCtx };
32
+ return ctx.hydrator.hydrateEmbedExternalViewFromUris([skeleton.canonicalUri], params.hydrateCtx);
28
33
  };
29
34
  const presentation = (inputs) => {
30
- const { hydration } = inputs;
31
- const documents = hydration.siteStandardDocuments;
32
- const publications = hydration.siteStandardPublications;
33
- // Dispatch by record type. Today site.standard is the only kind we know
34
- // how to render; future record types get their own branch.
35
- if ((documents && documents.size > 0) ||
36
- (publications && publications.size > 0)) {
37
- return standardSitePresentation(inputs, documents, publications);
35
+ const { ctx, params, skeleton, hydration } = inputs;
36
+ const { canonicalUri } = skeleton;
37
+ if (!canonicalUri)
38
+ return {};
39
+ if (!hydration.externalRecords?.get(canonicalUri))
40
+ return {};
41
+ const { associatedRefs, associatedRecords } = collectAssociations(canonicalUri, hydration.externalRecords);
42
+ // Validate before building any view: trust must not depend on whether the
43
+ // records happen to be renderable.
44
+ const standardSite = isStandardSiteUri(canonicalUri);
45
+ const standardSiteRecords = standardSite
46
+ ? getStandardSiteRecordsForUri(canonicalUri, hydration.externalRecords)
47
+ : undefined;
48
+ const verified = standardSiteRecords
49
+ ? validateStandardSiteRecordsForUrl(standardSiteRecords.document, standardSiteRecords.publication, params.url)
50
+ : isExternalRecordUrlSafe(params.url, ctx.cfg.externalRecordsSafeDomains);
51
+ if (!verified) {
52
+ return {
53
+ unverifiedAssociatedRefs: associatedRefs,
54
+ unverifiedAssociatedRecords: associatedRecords,
55
+ };
56
+ }
57
+ const data = ctx.views.externalRecordView(canonicalUri, hydration);
58
+ let view;
59
+ if (standardSiteRecords) {
60
+ const standardSiteView = ctx.views.externalEmbedFromStandardSiteRecords({
61
+ document: standardSiteRecords.document,
62
+ publication: standardSiteRecords.publication,
63
+ state: hydration,
64
+ assumedUrl: params.url,
65
+ });
66
+ if (standardSiteView) {
67
+ view = app.bsky.embed.external.view.$build({
68
+ external: {
69
+ ...standardSiteView,
70
+ uri: params.url,
71
+ associatedRefs: associatedRefs,
72
+ },
73
+ });
74
+ }
75
+ }
76
+ return {
77
+ view,
78
+ data,
79
+ associatedRefs: associatedRefs,
80
+ associatedRecords: associatedRecords,
81
+ };
82
+ };
83
+ /** A complete DID-based record URI: record key present, no fragment. */
84
+ const parseRecordUri = (value) => {
85
+ const parsed = parseAtUriString(value);
86
+ if (parsed.success &&
87
+ isDidString(parsed.value.authority) &&
88
+ parsed.value.collection &&
89
+ parsed.value.rkey &&
90
+ !parsed.value.hash) {
91
+ return { uri: value, collection: parsed.value.collection };
92
+ }
93
+ return undefined;
94
+ };
95
+ const isStandardSiteUri = (uri) => {
96
+ const collection = parseRecordUri(uri)?.collection;
97
+ return (collection === site.standard.document.$type ||
98
+ collection === site.standard.publication.$type);
99
+ };
100
+ /**
101
+ * Reduce the request to one record URI. An explicit `canonicalUri` wins and
102
+ * is never retried with `uris`. Otherwise the legacy `uris` input selects the
103
+ * first Standard.site document, or else the first publication, regardless of
104
+ * array order. No I/O: selection depends only on the supplied URIs.
105
+ */
106
+ const normalizeCanonicalUri = (params) => {
107
+ if (params.canonicalUri !== undefined) {
108
+ return parseRecordUri(params.canonicalUri)?.uri;
38
109
  }
39
- return {};
110
+ const candidates = (params.uris ?? []).flatMap((value) => {
111
+ const parsed = parseRecordUri(value);
112
+ return parsed ? [parsed] : [];
113
+ });
114
+ const preferred = candidates.find((c) => c.collection === site.standard.document.$type) ??
115
+ candidates.find((c) => c.collection === site.standard.publication.$type);
116
+ return preferred?.uri;
40
117
  };
41
- const standardSitePresentation = (inputs, documents, publications) => {
42
- const { ctx, params, hydration } = inputs;
43
- // Pick the first hydrated doc, then pair it with the publication its
44
- // `site` field points at (if any). Docs the dataplane auto-resolved a
45
- // publication for end up matched; unrelated publications fall away.
46
- const { document, publication } = getSiteStandardRecordsFromHydrationMapsByDocumentUri(documents, publications);
47
- // Emit response refs/records only for the records we actually selected.
48
- // Anything else (e.g. extra publications the dataplane returned) is
49
- // intentionally excluded so the strongRefs Cardy writes onto the post
50
- // match the view we built. Profiles are emitted in the same order as
51
- // refs (one per slot) so consumers can match by index.
118
+ /**
119
+ * Persistence policy, not render-time hydration: the canonical record (when
120
+ * available, whatever its collection) plus only the additional collections
121
+ * opted in here, so records baked into a post stay slim. Hydration still holds
122
+ * the full state (e.g. recommendation, subscription and favorite records that
123
+ * back the rendered `likers`/`subscribers` previews); those are deliberately
124
+ * not persisted. An absent entry means no additional collections. Add a
125
+ * collection here only when a post must carry it.
126
+ */
127
+ const PERSISTED_DEPENDENCY_COLLECTIONS = new Map([
128
+ [site.standard.document.$type, [site.standard.publication.$type]],
129
+ [
130
+ social.grain.gallery.$type,
131
+ [social.grain.gallery.item.$type, social.grain.photo.$type],
132
+ ],
133
+ ]);
134
+ /**
135
+ * The canonical record, then the hydrated dependencies whose URI collection is
136
+ * opted in by `PERSISTED_DEPENDENCY_COLLECTIONS` for the canonical collection.
137
+ * Refs and associatedRecords are index-aligned, with CIDs taken from
138
+ * hydration; unavailable records are excluded.
139
+ */
140
+ const collectAssociations = (canonicalUri, records) => {
52
141
  const associatedRefs = [];
53
142
  const associatedRecords = [];
54
- for (const slot of [document, publication]) {
55
- if (!slot)
143
+ const canonicalCollection = parseRecordUri(canonicalUri)?.collection;
144
+ const persisted = (canonicalCollection &&
145
+ PERSISTED_DEPENDENCY_COLLECTIONS.get(canonicalCollection)) ||
146
+ [];
147
+ const uris = [
148
+ canonicalUri,
149
+ ...[...records.keys()].filter((uri) => uri !== canonicalUri),
150
+ ];
151
+ for (const uri of uris) {
152
+ const info = records.get(uri);
153
+ if (!info)
56
154
  continue;
57
- associatedRefs.push(com.atproto.repo.strongRef.$build({
58
- uri: slot.ref.uri,
59
- cid: slot.info.cid,
60
- }));
61
- associatedRecords.push(slot.info.record);
155
+ if (uri !== canonicalUri) {
156
+ const collection = parseRecordUri(uri)?.collection;
157
+ if (!collection || !persisted.includes(collection))
158
+ continue;
159
+ }
160
+ associatedRefs.push(com.atproto.repo.strongRef.$build({ uri, cid: info.cid }));
161
+ associatedRecords.push(info.record);
62
162
  }
63
- if (!associatedRefs.length)
64
- return {};
65
- const overlay = ctx.views.externalEmbedFromStandardSiteRecords({
66
- document,
67
- publication,
68
- state: hydration,
69
- assumedUrl: params.url,
70
- });
71
- // The view builder rejected the records (validation failed, or the pair
72
- // didn't produce the title viewExternal requires). Return nothing — Cardy
73
- // falls back to its own card render and doesn't write strongRefs to the
74
- // post.
75
- if (!overlay)
76
- return {};
77
- const view = app.bsky.embed.external.view.$build({
78
- external: {
79
- ...overlay,
80
- uri: params.url,
81
- associatedRefs,
82
- },
83
- });
84
- return { view, associatedRefs, associatedRecords };
163
+ return { associatedRefs, associatedRecords };
85
164
  };
86
165
  //# sourceMappingURL=getEmbedExternalView.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getEmbedExternalView.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AtUriString, LexMap } from '@atproto/lex'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport {\n type SiteStandardDocuments,\n type SiteStandardPublications,\n getSiteStandardRecordsFromHydrationMapsByDocumentUri,\n} from '../../../../hydration/external.js'\nimport type { HydrateCtx, Hydrator } from '../../../../hydration/hydrator.js'\nimport { app, com } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport type { Views } from '../../../../views/index.js'\nimport type { ExternalEmbedView, StrongRef } from '../../../../views/types.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getEmbedExternalView = createPipeline(\n skeleton,\n hydration,\n noRules,\n presentation,\n )\n server.add(app.bsky.embed.getEmbedExternalView, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ params, auth, req }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })\n const result = await getEmbedExternalView({ ...params, hydrateCtx }, ctx)\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n\nconst skeleton = async (\n inputs: SkeletonFnInput<Context, Params>,\n): Promise<Skeleton> => {\n return { uris: inputs.params.uris as AtUriString[] }\n}\n\nconst hydration = async (\n inputs: HydrationFnInput<Context, Params, Skeleton>,\n) => {\n const { ctx, params, skeleton } = inputs\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(\n skeleton.uris,\n params.hydrateCtx,\n )\n}\n\nconst presentation = (\n inputs: PresentationFnInput<Context, Params, Skeleton>,\n): Output => {\n const { hydration } = inputs\n const documents = hydration.siteStandardDocuments\n const publications = hydration.siteStandardPublications\n // Dispatch by record type. Today site.standard is the only kind we know\n // how to render; future record types get their own branch.\n if (\n (documents && documents.size > 0) ||\n (publications && publications.size > 0)\n ) {\n return standardSitePresentation(inputs, documents, publications)\n }\n return {}\n}\n\nconst standardSitePresentation = (\n inputs: PresentationFnInput<Context, Params, Skeleton>,\n documents: SiteStandardDocuments | undefined,\n publications: SiteStandardPublications | undefined,\n): Output => {\n const { ctx, params, hydration } = inputs\n\n // Pick the first hydrated doc, then pair it with the publication its\n // `site` field points at (if any). Docs the dataplane auto-resolved a\n // publication for end up matched; unrelated publications fall away.\n const { document, publication } =\n getSiteStandardRecordsFromHydrationMapsByDocumentUri(\n documents,\n publications,\n )\n\n // Emit response refs/records only for the records we actually selected.\n // Anything else (e.g. extra publications the dataplane returned) is\n // intentionally excluded so the strongRefs Cardy writes onto the post\n // match the view we built. Profiles are emitted in the same order as\n // refs (one per slot) so consumers can match by index.\n const associatedRefs: StrongRef[] = []\n const associatedRecords: LexMap[] = []\n for (const slot of [document, publication]) {\n if (!slot) continue\n associatedRefs.push(\n com.atproto.repo.strongRef.$build({\n uri: slot.ref.uri,\n cid: slot.info.cid,\n }),\n )\n associatedRecords.push(slot.info.record as LexMap)\n }\n\n if (!associatedRefs.length) return {}\n\n const overlay = ctx.views.externalEmbedFromStandardSiteRecords({\n document,\n publication,\n state: hydration,\n assumedUrl: params.url,\n })\n // The view builder rejected the records (validation failed, or the pair\n // didn't produce the title viewExternal requires). Return nothing — Cardy\n // falls back to its own card render and doesn't write strongRefs to the\n // post.\n if (!overlay) return {}\n\n const view = app.bsky.embed.external.view.$build({\n external: {\n ...overlay,\n uri: params.url,\n associatedRefs,\n },\n })\n\n return { view, associatedRefs, associatedRecords }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n}\n\ntype Params = app.bsky.embed.getEmbedExternalView.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype Skeleton = {\n uris: AtUriString[]\n}\n\ntype Output = {\n view?: ExternalEmbedView\n associatedRefs?: StrongRef[]\n associatedRecords?: LexMap[]\n}\n"]}
1
+ {"version":3,"file":"getEmbedExternalView.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/embed/getEmbedExternalView.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AtUriString,\n type LexMap,\n type TypedLexMap,\n isDidString,\n} from '@atproto/lex'\nimport { parseAtUriString } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { ServerConfig } from '../../../../config.js'\nimport type { AppContext } from '../../../../context.js'\nimport type { ExternalRecords } from '../../../../hydration/external.js'\nimport type {\n HydrateCtx,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app, com, site, social } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { isExternalRecordUrlSafe } from '../../../../util/external/index.js'\nimport {\n getStandardSiteRecordsForUri,\n validateStandardSiteRecordsForUrl,\n} from '../../../../util/external/standard-site.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getEmbedExternalView = createPipeline(\n skeleton,\n hydration,\n noRules,\n presentation,\n )\n server.add(app.bsky.embed.getEmbedExternalView, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ params, auth, req }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })\n const result = await getEmbedExternalView({ ...params, hydrateCtx }, ctx)\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n\nconst skeleton = async (\n inputs: SkeletonFnInput<Context, Params>,\n): Promise<Skeleton> => {\n return { canonicalUri: normalizeCanonicalUri(inputs.params) }\n}\n\nconst hydration = async (\n inputs: HydrationFnInput<Context, Params, Skeleton>,\n): Promise<HydrationState> => {\n const { ctx, params, skeleton } = inputs\n if (!skeleton.canonicalUri) return { ctx: params.hydrateCtx }\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(\n [skeleton.canonicalUri],\n params.hydrateCtx,\n )\n}\n\nconst presentation = (\n inputs: PresentationFnInput<Context, Params, Skeleton>,\n): Output => {\n const { ctx, params, skeleton, hydration } = inputs\n const { canonicalUri } = skeleton\n if (!canonicalUri) return {}\n\n if (!hydration.externalRecords?.get(canonicalUri)) return {}\n\n const { associatedRefs, associatedRecords } = collectAssociations(\n canonicalUri,\n hydration.externalRecords,\n )\n\n // Validate before building any view: trust must not depend on whether the\n // records happen to be renderable.\n const standardSite = isStandardSiteUri(canonicalUri)\n const standardSiteRecords = standardSite\n ? getStandardSiteRecordsForUri(canonicalUri, hydration.externalRecords)\n : undefined\n const verified = standardSiteRecords\n ? validateStandardSiteRecordsForUrl(\n standardSiteRecords.document,\n standardSiteRecords.publication,\n params.url,\n )\n : isExternalRecordUrlSafe(params.url, ctx.cfg.externalRecordsSafeDomains)\n\n if (!verified) {\n return {\n unverifiedAssociatedRefs: associatedRefs,\n unverifiedAssociatedRecords: associatedRecords,\n }\n }\n\n const data = ctx.views.externalRecordView(canonicalUri, hydration)\n\n let view: Output['view']\n if (standardSiteRecords) {\n const standardSiteView = ctx.views.externalEmbedFromStandardSiteRecords({\n document: standardSiteRecords.document,\n publication: standardSiteRecords.publication,\n state: hydration,\n assumedUrl: params.url,\n })\n if (standardSiteView) {\n view = app.bsky.embed.external.view.$build({\n external: {\n ...standardSiteView,\n uri: params.url,\n associatedRefs: associatedRefs,\n },\n })\n }\n }\n\n return {\n view,\n data,\n associatedRefs: associatedRefs,\n associatedRecords: associatedRecords,\n }\n}\n\ntype RecordUri = { uri: AtUriString; collection: string }\n\n/** A complete DID-based record URI: record key present, no fragment. */\nconst parseRecordUri = (value: string): RecordUri | undefined => {\n const parsed = parseAtUriString(value)\n if (\n parsed.success &&\n isDidString(parsed.value.authority) &&\n parsed.value.collection &&\n parsed.value.rkey &&\n !parsed.value.hash\n ) {\n return { uri: value as AtUriString, collection: parsed.value.collection }\n }\n return undefined\n}\n\nconst isStandardSiteUri = (uri: AtUriString): boolean => {\n const collection = parseRecordUri(uri)?.collection\n return (\n collection === site.standard.document.$type ||\n collection === site.standard.publication.$type\n )\n}\n\n/**\n * Reduce the request to one record URI. An explicit `canonicalUri` wins and\n * is never retried with `uris`. Otherwise the legacy `uris` input selects the\n * first Standard.site document, or else the first publication, regardless of\n * array order. No I/O: selection depends only on the supplied URIs.\n */\nconst normalizeCanonicalUri = (params: Params): AtUriString | undefined => {\n if (params.canonicalUri !== undefined) {\n return parseRecordUri(params.canonicalUri)?.uri\n }\n const candidates = (params.uris ?? []).flatMap((value) => {\n const parsed = parseRecordUri(value)\n return parsed ? [parsed] : []\n })\n const preferred =\n candidates.find((c) => c.collection === site.standard.document.$type) ??\n candidates.find((c) => c.collection === site.standard.publication.$type)\n return preferred?.uri\n}\n\n/**\n * Persistence policy, not render-time hydration: the canonical record (when\n * available, whatever its collection) plus only the additional collections\n * opted in here, so records baked into a post stay slim. Hydration still holds\n * the full state (e.g. recommendation, subscription and favorite records that\n * back the rendered `likers`/`subscribers` previews); those are deliberately\n * not persisted. An absent entry means no additional collections. Add a\n * collection here only when a post must carry it.\n */\nconst PERSISTED_DEPENDENCY_COLLECTIONS: ReadonlyMap<string, readonly string[]> =\n new Map<string, readonly string[]>([\n [site.standard.document.$type, [site.standard.publication.$type]],\n [\n social.grain.gallery.$type,\n [social.grain.gallery.item.$type, social.grain.photo.$type],\n ],\n ])\n\n/**\n * The canonical record, then the hydrated dependencies whose URI collection is\n * opted in by `PERSISTED_DEPENDENCY_COLLECTIONS` for the canonical collection.\n * Refs and associatedRecords are index-aligned, with CIDs taken from\n * hydration; unavailable records are excluded.\n */\nconst collectAssociations = (\n canonicalUri: AtUriString,\n records: ExternalRecords,\n): { associatedRefs: StrongRef[]; associatedRecords: LexMap[] } => {\n const associatedRefs: StrongRef[] = []\n const associatedRecords: LexMap[] = []\n const canonicalCollection = parseRecordUri(canonicalUri)?.collection\n const persisted =\n (canonicalCollection &&\n PERSISTED_DEPENDENCY_COLLECTIONS.get(canonicalCollection)) ||\n []\n const uris = [\n canonicalUri,\n ...[...records.keys()].filter((uri) => uri !== canonicalUri),\n ]\n for (const uri of uris) {\n const info = records.get(uri)\n if (!info) continue\n if (uri !== canonicalUri) {\n const collection = parseRecordUri(uri)?.collection\n if (!collection || !persisted.includes(collection)) continue\n }\n associatedRefs.push(\n com.atproto.repo.strongRef.$build({ uri, cid: info.cid }),\n )\n associatedRecords.push(info.record as TypedLexMap)\n }\n return { associatedRefs, associatedRecords }\n}\n\ntype StrongRef = com.atproto.repo.strongRef.Main\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n cfg: Pick<ServerConfig, 'externalRecordsSafeDomains'>\n}\n\ntype Params = app.bsky.embed.getEmbedExternalView.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype Skeleton = {\n canonicalUri?: AtUriString\n}\n\ntype Output = app.bsky.embed.getEmbedExternalView.$OutputBody\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"getAuthorFeed.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/feed/getAuthorFeed.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACtE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAA;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAQ,MAAM,+BAA+B,CAAA;AACnE,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,QAAQ,EAEd,MAAM,mCAAmC,CAAA;AAE1C,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAInD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAGvD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA2CvD;AAUD,eAAO,MAAM,QAAQ,WAAkB;IACrC,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,KAAG,OAAO,CAAC,QAAQ,CA2DnB,CAAA;AAqFD,KAAK,OAAO,GAAG;IACb,QAAQ,EAAE,QAAQ,CAAA;IAClB,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,eAAe,CAAA;CAC3B,CAAA;AAED,KAAK,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,GAAG;IAClD,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA"}
1
+ {"version":3,"file":"getAuthorFeed.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/feed/getAuthorFeed.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AACtE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAA;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAQ,MAAM,+BAA+B,CAAA;AACnE,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,QAAQ,EAEd,MAAM,mCAAmC,CAAA;AAE1C,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAInD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAGvD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA2CvD;AAUD,eAAO,MAAM,QAAQ,WAAkB;IACrC,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,KAAG,OAAO,CAAC,QAAQ,CA2DnB,CAAA;AAuFD,KAAK,OAAO,GAAG;IACb,QAAQ,EAAE,QAAQ,CAAA;IAClB,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,eAAe,CAAA;CAC3B,CAAA;AAED,KAAK,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,GAAG;IAClD,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA"}
@@ -113,11 +113,13 @@ const hydration = async (inputs) => {
113
113
  const noBlocksOrMutedReposts = (inputs) => {
114
114
  const { ctx, skeleton, hydration } = inputs;
115
115
  const relationship = hydration.profileViewers?.get(skeleton.actor.did);
116
- if (relationship &&
116
+ if (!hydration.ctx?.skipViewerBlocks &&
117
+ relationship &&
117
118
  (relationship.blocking || ctx.views.blockingByList(relationship, hydration))) {
118
119
  throw new InvalidRequestError(`Requester has blocked actor: ${skeleton.actor.did}`, 'BlockedActor');
119
120
  }
120
- if (relationship &&
121
+ if (!hydration.ctx?.skipViewerBlocks &&
122
+ relationship &&
121
123
  (relationship.blockedBy || ctx.views.blockedByList(relationship, hydration))) {
122
124
  throw new InvalidRequestError(`Requester is blocked by actor: ${skeleton.actor.did}`, 'BlockedByActor');
123
125
  }
@@ -1 +1 @@
1
- {"version":3,"file":"getAuthorFeed.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/feed/getAuthorFeed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,OAAO,EAAE,mBAAmB,EAAe,MAAM,sBAAsB,CAAA;AAKvE,OAAO,EAIL,WAAW,GACZ,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAEnE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEzE,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,aAAa,GAAG,cAAc,CAClC,QAAQ,EACR,SAAS,EACT,sBAAsB,EACtB,YAAY,CACb,CAAA;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;QACtC,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,sBAAsB;QAC7C,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;YACvC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAClD,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,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,gBAAgB;gBAChB,gBAAgB;gBAChB,QAAQ,EAAE,GAAG,CAAC,kBAAkB,CAAC,KAAK,CACpC,GAAG,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CACpE;aACF,CAAC,CAAA;YAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;gBAC5B,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,aAAa,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,GAAG,CAAC;gBAC9D,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI;aACrB,CAAC,CAAA;YAEF,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;YAE/D,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,UAAU,CAAC;oBAClB,OAAO;oBACP,QAAQ,EAAE,UAAU,CAAC,QAAQ;iBAC9B,CAAC;aACH,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,mBAAmB,GAAG;IAC1B,kBAAkB,EAAE,SAAS,EAAE,2CAA2C;IAC1E,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;IAC3C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;IAC3C,wBAAwB,EAAE,QAAQ,CAAC,wBAAwB;IAC3D,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;CAC5C,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAAE,MAG9B,EAAqB,EAAE;IACtB,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC9D,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,CAAC,CAAA;IACpD,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,EAAE;QACvD,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,gBAAgB;QACpD,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,kBAAkB;KACvD,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,CAAC,CAAA;IACpD,CAAC;IACD,IAAI,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IACpD,CAAC;IAED,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IAC5D,MAAM,kBAAkB,GAAG,CAAC,MAAM,CAAC,MAAM,CAAA;IACzC,MAAM,sBAAsB,GAC1B,kBAAkB;QAClB,MAAM,CAAC,WAAW;QAClB,UAAU;QACV,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG,CAAA;IAExC,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC;QAC5C,QAAQ,EAAE,GAAG;QACb,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC;KAC7C,CAAC,CAAA;IAEF,IAAI,KAAK,GAAe,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/C,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAkB,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,EAAE;QAClE,MAAM,EAAE,IAAI,CAAC,MAAM;YACjB,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,MAAqB,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS,EAAE;YACvE,CAAC,CAAC,SAAS;KACd,CAAC,CAAC,CAAA;IAEH,IAAI,sBAAsB,IAAI,UAAU,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG;YACjB,IAAI,EAAE;gBACJ,GAAG,EAAE,UAAU,CAAC,GAAG;gBACnB,GAAG,EAAE,UAAU,CAAC,GAAG;aACpB;YACD,YAAY,EAAE,IAAI;SACnB,CAAA;QAED,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACrE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC3B,CAAC;IAED,OAAO;QACL,KAAK;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK;QACL,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;KAChC,CAAA;AACH,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,KAAK,EAAE,MAIxB,EAA2B,EAAE;IAC5B,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAA;IACxC,MAAM,CAAC,aAAa,EAAE,kBAAkB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5D,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE;YAC/D,WAAW,EACT,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM;gBAC1B,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAClC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CACtD;SACJ,CAAC;QACF,GAAG,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KAC5E,CAAC,CAAA;IACF,OAAO,WAAW,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAA;AACvD,CAAC,CAAA;AAED,MAAM,sBAAsB,GAAG,CAAC,MAI/B,EAAY,EAAE;IACb,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,CAAA;IAC3C,MAAM,YAAY,GAAG,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACtE,IACE,YAAY;QACZ,CAAC,YAAY,CAAC,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,EAC5E,CAAC;QACD,MAAM,IAAI,mBAAmB,CAC3B,gCAAgC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EACpD,cAAc,CACf,CAAA;IACH,CAAC;IACD,IACE,YAAY;QACZ,CAAC,YAAY,CAAC,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,EAC5E,CAAC;QACD,MAAM,IAAI,mBAAmB,CAC3B,kCAAkC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EACtD,gBAAgB,CACjB,CAAA;IACH,CAAC;IAED,MAAM,mBAAmB,GAAG,CAAC,IAAc,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAC7D,OAAO,CACL,CAAC,GAAG,CAAC,aAAa;YAClB,CAAC,GAAG,CAAC,iBAAiB;YACtB,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,0BAA0B;SACrE,CAAA;IACH,CAAC,CAAA;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,0BAA0B,EAAE,CAAC;QACnD,4DAA4D;QAC5D,+DAA+D;QAC/D,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;QACnE,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YAC9C,OAAO,CACL,mBAAmB,CAAC,IAAI,CAAC;gBACzB,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,IAAI,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CACnE,CAAA;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAC7D,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,YAAY,GAAG,CAAC,MAIrB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,CAAA;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAC/C,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,CACxC,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;AAC1C,CAAC,CAAA;AAmBD,MAAM,iBAAiB;IAIrB,YACE,KAAiB,EACT,SAAyB;QAAzB,cAAS,GAAT,SAAS,CAAgB;QALnC,aAAQ,GAAG,IAAI,GAAG,EAAe,CAAA;QACjC,UAAK,GAAG,IAAI,GAAG,EAAwB,CAAA;QAMrC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YAClC,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,EAAE,CAAC,GAAgB,EAAE,IAAI,GAAG,IAAI,GAAG,EAAe;QAChD,yDAAyD;QACzD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAA;QACrC,CAAC;QACD,iBAAiB;QACjB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YAC1B,OAAO,KAAK,CAAA;QACd,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACf,CAAC;QACD,2BAA2B;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAA;IACf,CAAC;IAEO,GAAG,CAAC,GAAgB,EAAE,IAAsB;QAClD,6CAA6C;QAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAA;QACd,CAAC;QACD,+CAA+C;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAA;QACd,CAAC;QACD,yDAAyD;QACzD,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,oEAAoE;QACpE,OAAO,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;IACjC,CAAC;CACF;AAED,SAAS,YAAY,CAAC,IAAU;IAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI,CAAA;AAC9C,CAAC","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { AtUriString } from '@atproto/lex'\nimport { InvalidRequestError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type { DataPlaneClient } from '../../../../data-plane/index.js'\nimport type { Actor } from '../../../../hydration/actor.js'\nimport type { FeedItem, Post } from '../../../../hydration/feed.js'\nimport {\n type HydrateCtx,\n type HydrationState,\n type Hydrator,\n mergeStates,\n} from '../../../../hydration/hydrator.js'\nimport { parseString } from '../../../../hydration/util.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline } from '../../../../pipeline.js'\nimport { FeedType } from '../../../../proto/bsky_pb.js'\nimport { safePinnedPost, uriToDid } from '../../../../util/uris.js'\nimport type { Views } from '../../../../views/index.js'\nimport { clearlyBadCursor, fillPage, resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getAuthorFeed = createPipeline(\n skeleton,\n hydration,\n noBlocksOrMutedReposts,\n presentation,\n )\n server.add(app.bsky.feed.getAuthorFeed, {\n auth: ctx.authVerifier.optionalStandardOrRole,\n handler: async ({ params, auth, req }) => {\n const { viewer, includeTakedowns, skipViewerBlocks } =\n ctx.authVerifier.parseCreds(auth)\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n includeTakedowns,\n skipViewerBlocks,\n features: ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n ),\n })\n\n const result = await fillPage({\n cursor: params.cursor,\n limit: params.limit,\n fetch: ({ cursor, limit }) =>\n getAuthorFeed({ ...params, cursor, limit, hydrateCtx }, ctx),\n items: (r) => r.feed,\n })\n\n const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({\n repoRev,\n labelers: hydrateCtx.labelers,\n }),\n }\n },\n })\n}\n\nconst FILTER_TO_FEED_TYPE = {\n posts_with_replies: undefined, // default: all posts, replies, and reposts\n posts_no_replies: FeedType.POSTS_NO_REPLIES,\n posts_with_media: FeedType.POSTS_WITH_MEDIA,\n posts_and_author_threads: FeedType.POSTS_AND_AUTHOR_THREADS,\n posts_with_video: FeedType.POSTS_WITH_VIDEO,\n}\n\nexport const skeleton = async (inputs: {\n ctx: Context\n params: Params\n}): Promise<Skeleton> => {\n const { ctx, params } = inputs\n const [did] = await ctx.hydrator.actor.getDids([params.actor])\n if (!did) {\n throw new InvalidRequestError('Profile not found')\n }\n const actors = await ctx.hydrator.actor.getActors([did], {\n includeTakedowns: params.hydrateCtx.includeTakedowns,\n skipCacheForDids: params.hydrateCtx.skipCacheForViewer,\n })\n const actor = actors.get(did)\n if (!actor) {\n throw new InvalidRequestError('Profile not found')\n }\n if (clearlyBadCursor(params.cursor)) {\n return { actor, filter: params.filter, items: [] }\n }\n\n const pinnedPost = safePinnedPost(actor.profile?.pinnedPost)\n const isFirstPageRequest = !params.cursor\n const shouldInsertPinnedPost =\n isFirstPageRequest &&\n params.includePins &&\n pinnedPost &&\n uriToDid(pinnedPost.uri) === actor.did\n\n const res = await ctx.dataplane.getAuthorFeed({\n actorDid: did,\n limit: params.limit,\n cursor: params.cursor,\n feedType: FILTER_TO_FEED_TYPE[params.filter],\n })\n\n let items: FeedItem[] = res.items.map((item) => ({\n post: { uri: item.uri as AtUriString, cid: item.cid || undefined },\n repost: item.repost\n ? { uri: item.repost as AtUriString, cid: item.repostCid || undefined }\n : undefined,\n }))\n\n if (shouldInsertPinnedPost && pinnedPost) {\n const pinnedItem = {\n post: {\n uri: pinnedPost.uri,\n cid: pinnedPost.cid,\n },\n authorPinned: true,\n }\n\n items = items.filter((item) => item.post.uri !== pinnedItem.post.uri)\n items.unshift(pinnedItem)\n }\n\n return {\n actor,\n filter: params.filter,\n items,\n cursor: parseString(res.cursor),\n }\n}\n\nconst hydration = async (inputs: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n}): Promise<HydrationState> => {\n const { ctx, params, skeleton } = inputs\n const [feedPostState, profileViewerState] = await Promise.all([\n ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx, {\n knownLikers:\n !!params.hydrateCtx.viewer &&\n params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.KnownLikersFeedEnable,\n ),\n }),\n ctx.hydrator.hydrateProfileViewers([skeleton.actor.did], params.hydrateCtx),\n ])\n return mergeStates(feedPostState, profileViewerState)\n}\n\nconst noBlocksOrMutedReposts = (inputs: {\n ctx: Context\n skeleton: Skeleton\n hydration: HydrationState\n}): Skeleton => {\n const { ctx, skeleton, hydration } = inputs\n const relationship = hydration.profileViewers?.get(skeleton.actor.did)\n if (\n relationship &&\n (relationship.blocking || ctx.views.blockingByList(relationship, hydration))\n ) {\n throw new InvalidRequestError(\n `Requester has blocked actor: ${skeleton.actor.did}`,\n 'BlockedActor',\n )\n }\n if (\n relationship &&\n (relationship.blockedBy || ctx.views.blockedByList(relationship, hydration))\n ) {\n throw new InvalidRequestError(\n `Requester is blocked by actor: ${skeleton.actor.did}`,\n 'BlockedByActor',\n )\n }\n\n const checkBlocksAndMutes = (item: FeedItem) => {\n const bam = ctx.views.feedItemBlocksAndMutes(item, hydration)\n return (\n !bam.authorBlocked &&\n !bam.originatorBlocked &&\n (!bam.authorMuted || bam.originatorMuted) // repost of muted content\n )\n }\n\n if (skeleton.filter === 'posts_and_author_threads') {\n // ensure replies are only included if the feed contains all\n // replies up to the thread root (i.e. a complete self-thread.)\n const selfThread = new SelfThreadTracker(skeleton.items, hydration)\n skeleton.items = skeleton.items.filter((item) => {\n return (\n checkBlocksAndMutes(item) &&\n (item.repost || item.authorPinned || selfThread.ok(item.post.uri))\n )\n })\n } else {\n skeleton.items = skeleton.items.filter(checkBlocksAndMutes)\n }\n\n return skeleton\n}\n\nconst presentation = (inputs: {\n ctx: Context\n skeleton: Skeleton\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = inputs\n const feed = mapDefined(skeleton.items, (item) =>\n ctx.views.feedViewPost(item, hydration),\n )\n return { feed, cursor: skeleton.cursor }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n dataplane: DataPlaneClient\n}\n\ntype Params = app.bsky.feed.getAuthorFeed.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype Skeleton = {\n actor: Actor\n items: FeedItem[]\n filter: app.bsky.feed.getAuthorFeed.$Params['filter']\n cursor?: string\n}\n\nclass SelfThreadTracker {\n feedUris = new Set<AtUriString>()\n cache = new Map<AtUriString, boolean>()\n\n constructor(\n items: FeedItem[],\n private hydration: HydrationState,\n ) {\n items.forEach((item) => {\n if (!item.repost) {\n this.feedUris.add(item.post.uri)\n }\n })\n }\n\n ok(uri: AtUriString, loop = new Set<AtUriString>()) {\n // if we've already checked this uri, pull from the cache\n if (this.cache.has(uri)) {\n return this.cache.get(uri) ?? false\n }\n // loop detection\n if (loop.has(uri)) {\n this.cache.set(uri, false)\n return false\n } else {\n loop.add(uri)\n }\n // cache through the result\n const result = this._ok(uri, loop)\n this.cache.set(uri, result)\n return result\n }\n\n private _ok(uri: AtUriString, loop: Set<AtUriString>): boolean {\n // must be in the feed to be in a self-thread\n if (!this.feedUris.has(uri)) {\n return false\n }\n // must be hydratable to be part of self-thread\n const post = this.hydration.posts?.get(uri)\n if (!post) {\n return false\n }\n // root posts (no parent) are trivial case of self-thread\n const parentUri = getParentUri(post)\n if (parentUri === null) {\n return true\n }\n // recurse w/ cache: this post is in a self-thread if its parent is.\n return this.ok(parentUri, loop)\n }\n}\n\nfunction getParentUri(post: Post) {\n return post.record.reply?.parent.uri ?? null\n}\n"]}
1
+ {"version":3,"file":"getAuthorFeed.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/feed/getAuthorFeed.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined } from '@atproto/common'\nimport type { AtUriString } from '@atproto/lex'\nimport { InvalidRequestError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type { DataPlaneClient } from '../../../../data-plane/index.js'\nimport type { Actor } from '../../../../hydration/actor.js'\nimport type { FeedItem, Post } from '../../../../hydration/feed.js'\nimport {\n type HydrateCtx,\n type HydrationState,\n type Hydrator,\n mergeStates,\n} from '../../../../hydration/hydrator.js'\nimport { parseString } from '../../../../hydration/util.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { createPipeline } from '../../../../pipeline.js'\nimport { FeedType } from '../../../../proto/bsky_pb.js'\nimport { safePinnedPost, uriToDid } from '../../../../util/uris.js'\nimport type { Views } from '../../../../views/index.js'\nimport { clearlyBadCursor, fillPage, resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getAuthorFeed = createPipeline(\n skeleton,\n hydration,\n noBlocksOrMutedReposts,\n presentation,\n )\n server.add(app.bsky.feed.getAuthorFeed, {\n auth: ctx.authVerifier.optionalStandardOrRole,\n handler: async ({ params, auth, req }) => {\n const { viewer, includeTakedowns, skipViewerBlocks } =\n ctx.authVerifier.parseCreds(auth)\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n includeTakedowns,\n skipViewerBlocks,\n features: ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n ),\n })\n\n const result = await fillPage({\n cursor: params.cursor,\n limit: params.limit,\n fetch: ({ cursor, limit }) =>\n getAuthorFeed({ ...params, cursor, limit, hydrateCtx }, ctx),\n items: (r) => r.feed,\n })\n\n const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)\n\n return {\n encoding: 'application/json',\n body: result,\n headers: resHeaders({\n repoRev,\n labelers: hydrateCtx.labelers,\n }),\n }\n },\n })\n}\n\nconst FILTER_TO_FEED_TYPE = {\n posts_with_replies: undefined, // default: all posts, replies, and reposts\n posts_no_replies: FeedType.POSTS_NO_REPLIES,\n posts_with_media: FeedType.POSTS_WITH_MEDIA,\n posts_and_author_threads: FeedType.POSTS_AND_AUTHOR_THREADS,\n posts_with_video: FeedType.POSTS_WITH_VIDEO,\n}\n\nexport const skeleton = async (inputs: {\n ctx: Context\n params: Params\n}): Promise<Skeleton> => {\n const { ctx, params } = inputs\n const [did] = await ctx.hydrator.actor.getDids([params.actor])\n if (!did) {\n throw new InvalidRequestError('Profile not found')\n }\n const actors = await ctx.hydrator.actor.getActors([did], {\n includeTakedowns: params.hydrateCtx.includeTakedowns,\n skipCacheForDids: params.hydrateCtx.skipCacheForViewer,\n })\n const actor = actors.get(did)\n if (!actor) {\n throw new InvalidRequestError('Profile not found')\n }\n if (clearlyBadCursor(params.cursor)) {\n return { actor, filter: params.filter, items: [] }\n }\n\n const pinnedPost = safePinnedPost(actor.profile?.pinnedPost)\n const isFirstPageRequest = !params.cursor\n const shouldInsertPinnedPost =\n isFirstPageRequest &&\n params.includePins &&\n pinnedPost &&\n uriToDid(pinnedPost.uri) === actor.did\n\n const res = await ctx.dataplane.getAuthorFeed({\n actorDid: did,\n limit: params.limit,\n cursor: params.cursor,\n feedType: FILTER_TO_FEED_TYPE[params.filter],\n })\n\n let items: FeedItem[] = res.items.map((item) => ({\n post: { uri: item.uri as AtUriString, cid: item.cid || undefined },\n repost: item.repost\n ? { uri: item.repost as AtUriString, cid: item.repostCid || undefined }\n : undefined,\n }))\n\n if (shouldInsertPinnedPost && pinnedPost) {\n const pinnedItem = {\n post: {\n uri: pinnedPost.uri,\n cid: pinnedPost.cid,\n },\n authorPinned: true,\n }\n\n items = items.filter((item) => item.post.uri !== pinnedItem.post.uri)\n items.unshift(pinnedItem)\n }\n\n return {\n actor,\n filter: params.filter,\n items,\n cursor: parseString(res.cursor),\n }\n}\n\nconst hydration = async (inputs: {\n ctx: Context\n params: Params\n skeleton: Skeleton\n}): Promise<HydrationState> => {\n const { ctx, params, skeleton } = inputs\n const [feedPostState, profileViewerState] = await Promise.all([\n ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx, {\n knownLikers:\n !!params.hydrateCtx.viewer &&\n params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.KnownLikersFeedEnable,\n ),\n }),\n ctx.hydrator.hydrateProfileViewers([skeleton.actor.did], params.hydrateCtx),\n ])\n return mergeStates(feedPostState, profileViewerState)\n}\n\nconst noBlocksOrMutedReposts = (inputs: {\n ctx: Context\n skeleton: Skeleton\n hydration: HydrationState\n}): Skeleton => {\n const { ctx, skeleton, hydration } = inputs\n const relationship = hydration.profileViewers?.get(skeleton.actor.did)\n if (\n !hydration.ctx?.skipViewerBlocks &&\n relationship &&\n (relationship.blocking || ctx.views.blockingByList(relationship, hydration))\n ) {\n throw new InvalidRequestError(\n `Requester has blocked actor: ${skeleton.actor.did}`,\n 'BlockedActor',\n )\n }\n if (\n !hydration.ctx?.skipViewerBlocks &&\n relationship &&\n (relationship.blockedBy || ctx.views.blockedByList(relationship, hydration))\n ) {\n throw new InvalidRequestError(\n `Requester is blocked by actor: ${skeleton.actor.did}`,\n 'BlockedByActor',\n )\n }\n\n const checkBlocksAndMutes = (item: FeedItem) => {\n const bam = ctx.views.feedItemBlocksAndMutes(item, hydration)\n return (\n !bam.authorBlocked &&\n !bam.originatorBlocked &&\n (!bam.authorMuted || bam.originatorMuted) // repost of muted content\n )\n }\n\n if (skeleton.filter === 'posts_and_author_threads') {\n // ensure replies are only included if the feed contains all\n // replies up to the thread root (i.e. a complete self-thread.)\n const selfThread = new SelfThreadTracker(skeleton.items, hydration)\n skeleton.items = skeleton.items.filter((item) => {\n return (\n checkBlocksAndMutes(item) &&\n (item.repost || item.authorPinned || selfThread.ok(item.post.uri))\n )\n })\n } else {\n skeleton.items = skeleton.items.filter(checkBlocksAndMutes)\n }\n\n return skeleton\n}\n\nconst presentation = (inputs: {\n ctx: Context\n skeleton: Skeleton\n hydration: HydrationState\n}) => {\n const { ctx, skeleton, hydration } = inputs\n const feed = mapDefined(skeleton.items, (item) =>\n ctx.views.feedViewPost(item, hydration),\n )\n return { feed, cursor: skeleton.cursor }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n dataplane: DataPlaneClient\n}\n\ntype Params = app.bsky.feed.getAuthorFeed.$Params & {\n hydrateCtx: HydrateCtx\n}\n\ntype Skeleton = {\n actor: Actor\n items: FeedItem[]\n filter: app.bsky.feed.getAuthorFeed.$Params['filter']\n cursor?: string\n}\n\nclass SelfThreadTracker {\n feedUris = new Set<AtUriString>()\n cache = new Map<AtUriString, boolean>()\n\n constructor(\n items: FeedItem[],\n private hydration: HydrationState,\n ) {\n items.forEach((item) => {\n if (!item.repost) {\n this.feedUris.add(item.post.uri)\n }\n })\n }\n\n ok(uri: AtUriString, loop = new Set<AtUriString>()) {\n // if we've already checked this uri, pull from the cache\n if (this.cache.has(uri)) {\n return this.cache.get(uri) ?? false\n }\n // loop detection\n if (loop.has(uri)) {\n this.cache.set(uri, false)\n return false\n } else {\n loop.add(uri)\n }\n // cache through the result\n const result = this._ok(uri, loop)\n this.cache.set(uri, result)\n return result\n }\n\n private _ok(uri: AtUriString, loop: Set<AtUriString>): boolean {\n // must be in the feed to be in a self-thread\n if (!this.feedUris.has(uri)) {\n return false\n }\n // must be hydratable to be part of self-thread\n const post = this.hydration.posts?.get(uri)\n if (!post) {\n return false\n }\n // root posts (no parent) are trivial case of self-thread\n const parentUri = getParentUri(post)\n if (parentUri === null) {\n return true\n }\n // recurse w/ cache: this post is in a self-thread if its parent is.\n return this.ok(parentUri, loop)\n }\n}\n\nfunction getParentUri(post: Post) {\n return post.record.reply?.parent.uri ?? null\n}\n"]}