@atproto/bsky 0.0.76 → 0.0.77

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 (219) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/api/app/bsky/feed/getActorLikes.js +2 -2
  3. package/dist/api/app/bsky/feed/getActorLikes.js.map +1 -1
  4. package/dist/api/app/bsky/feed/getLikes.js +6 -6
  5. package/dist/api/app/bsky/feed/getLikes.js.map +1 -1
  6. package/dist/api/app/bsky/feed/getPosts.js +4 -4
  7. package/dist/api/app/bsky/feed/getPosts.js.map +1 -1
  8. package/dist/api/app/bsky/feed/getQuotes.d.ts +4 -0
  9. package/dist/api/app/bsky/feed/getQuotes.d.ts.map +1 -0
  10. package/dist/api/app/bsky/feed/getQuotes.js +67 -0
  11. package/dist/api/app/bsky/feed/getQuotes.js.map +1 -0
  12. package/dist/api/app/bsky/feed/getRepostedBy.js +6 -6
  13. package/dist/api/app/bsky/feed/getRepostedBy.js.map +1 -1
  14. package/dist/api/app/bsky/feed/searchPosts.js +4 -4
  15. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  16. package/dist/api/app/bsky/graph/getFollowers.js +8 -8
  17. package/dist/api/app/bsky/graph/getFollowers.js.map +1 -1
  18. package/dist/api/app/bsky/graph/getList.js +3 -3
  19. package/dist/api/app/bsky/graph/getList.js.map +1 -1
  20. package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
  21. package/dist/api/app/bsky/notification/listNotifications.js +29 -8
  22. package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
  23. package/dist/api/index.d.ts.map +1 -1
  24. package/dist/api/index.js +2 -0
  25. package/dist/api/index.js.map +1 -1
  26. package/dist/data-plane/server/db/database-schema.d.ts +4 -2
  27. package/dist/data-plane/server/db/database-schema.d.ts.map +1 -1
  28. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.d.ts +4 -0
  29. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.d.ts.map +1 -0
  30. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.js +15 -0
  31. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.js.map +1 -0
  32. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.d.ts +4 -0
  33. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.d.ts.map +1 -0
  34. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.js +30 -0
  35. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.js.map +1 -0
  36. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.d.ts +4 -0
  37. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.d.ts.map +1 -0
  38. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.js +20 -0
  39. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.js.map +1 -0
  40. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.d.ts +4 -0
  41. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.d.ts.map +1 -0
  42. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.js +28 -0
  43. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.js.map +1 -0
  44. package/dist/data-plane/server/db/migrations/index.d.ts +4 -0
  45. package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
  46. package/dist/data-plane/server/db/migrations/index.js +5 -1
  47. package/dist/data-plane/server/db/migrations/index.js.map +1 -1
  48. package/dist/data-plane/server/db/tables/post-agg.d.ts +1 -0
  49. package/dist/data-plane/server/db/tables/post-agg.d.ts.map +1 -1
  50. package/dist/data-plane/server/db/tables/post-gate.d.ts +14 -0
  51. package/dist/data-plane/server/db/tables/post-gate.d.ts.map +1 -0
  52. package/dist/data-plane/server/db/tables/post-gate.js +4 -0
  53. package/dist/data-plane/server/db/tables/post-gate.js.map +1 -0
  54. package/dist/data-plane/server/db/tables/post.d.ts +3 -0
  55. package/dist/data-plane/server/db/tables/post.d.ts.map +1 -1
  56. package/dist/data-plane/server/db/tables/quote.d.ts +16 -0
  57. package/dist/data-plane/server/db/tables/quote.d.ts.map +1 -0
  58. package/dist/data-plane/server/db/tables/quote.js +4 -0
  59. package/dist/data-plane/server/db/tables/quote.js.map +1 -0
  60. package/dist/data-plane/server/indexing/index.d.ts +2 -0
  61. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  62. package/dist/data-plane/server/indexing/index.js +6 -0
  63. package/dist/data-plane/server/indexing/index.js.map +1 -1
  64. package/dist/data-plane/server/indexing/plugins/post-gate.d.ts +10 -0
  65. package/dist/data-plane/server/indexing/plugins/post-gate.d.ts.map +1 -0
  66. package/dist/data-plane/server/indexing/plugins/post-gate.js +101 -0
  67. package/dist/data-plane/server/indexing/plugins/post-gate.js.map +1 -0
  68. package/dist/data-plane/server/indexing/plugins/post.d.ts +2 -0
  69. package/dist/data-plane/server/indexing/plugins/post.d.ts.map +1 -1
  70. package/dist/data-plane/server/indexing/plugins/post.js +122 -15
  71. package/dist/data-plane/server/indexing/plugins/post.js.map +1 -1
  72. package/dist/data-plane/server/indexing/plugins/thread-gate.d.ts.map +1 -1
  73. package/dist/data-plane/server/indexing/plugins/thread-gate.js +12 -0
  74. package/dist/data-plane/server/indexing/plugins/thread-gate.js.map +1 -1
  75. package/dist/data-plane/server/routes/index.d.ts.map +1 -1
  76. package/dist/data-plane/server/routes/index.js +2 -0
  77. package/dist/data-plane/server/routes/index.js.map +1 -1
  78. package/dist/data-plane/server/routes/interactions.d.ts.map +1 -1
  79. package/dist/data-plane/server/routes/interactions.js +2 -1
  80. package/dist/data-plane/server/routes/interactions.js.map +1 -1
  81. package/dist/data-plane/server/routes/quotes.d.ts +6 -0
  82. package/dist/data-plane/server/routes/quotes.d.ts.map +1 -0
  83. package/dist/data-plane/server/routes/quotes.js +27 -0
  84. package/dist/data-plane/server/routes/quotes.js.map +1 -0
  85. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  86. package/dist/data-plane/server/routes/records.js +11 -1
  87. package/dist/data-plane/server/routes/records.js.map +1 -1
  88. package/dist/data-plane/server/util.d.ts +6 -7
  89. package/dist/data-plane/server/util.d.ts.map +1 -1
  90. package/dist/data-plane/server/util.js +1 -9
  91. package/dist/data-plane/server/util.js.map +1 -1
  92. package/dist/hydration/feed.d.ts +10 -0
  93. package/dist/hydration/feed.d.ts.map +1 -1
  94. package/dist/hydration/feed.js +31 -7
  95. package/dist/hydration/feed.js.map +1 -1
  96. package/dist/hydration/hydrator.d.ts +4 -2
  97. package/dist/hydration/hydrator.d.ts.map +1 -1
  98. package/dist/hydration/hydrator.js +89 -34
  99. package/dist/hydration/hydrator.js.map +1 -1
  100. package/dist/hydration/util.d.ts +0 -1
  101. package/dist/hydration/util.d.ts.map +1 -1
  102. package/dist/hydration/util.js +1 -5
  103. package/dist/hydration/util.js.map +1 -1
  104. package/dist/lexicon/index.d.ts +2 -0
  105. package/dist/lexicon/index.d.ts.map +1 -1
  106. package/dist/lexicon/index.js +4 -0
  107. package/dist/lexicon/index.js.map +1 -1
  108. package/dist/lexicon/lexicons.d.ts +141 -0
  109. package/dist/lexicon/lexicons.d.ts.map +1 -1
  110. package/dist/lexicon/lexicons.js +142 -0
  111. package/dist/lexicon/lexicons.js.map +1 -1
  112. package/dist/lexicon/types/app/bsky/embed/record.d.ts +8 -1
  113. package/dist/lexicon/types/app/bsky/embed/record.d.ts.map +1 -1
  114. package/dist/lexicon/types/app/bsky/embed/record.js +11 -1
  115. package/dist/lexicon/types/app/bsky/embed/record.js.map +1 -1
  116. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +2 -0
  117. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  118. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  119. package/dist/lexicon/types/app/bsky/feed/getQuotes.d.ts +44 -0
  120. package/dist/lexicon/types/app/bsky/feed/getQuotes.d.ts.map +1 -0
  121. package/dist/lexicon/types/app/bsky/feed/getQuotes.js +3 -0
  122. package/dist/lexicon/types/app/bsky/feed/getQuotes.js.map +1 -0
  123. package/dist/lexicon/types/app/bsky/feed/postgate.d.ts +25 -0
  124. package/dist/lexicon/types/app/bsky/feed/postgate.d.ts.map +1 -0
  125. package/dist/lexicon/types/app/bsky/feed/postgate.js +27 -0
  126. package/dist/lexicon/types/app/bsky/feed/postgate.js.map +1 -0
  127. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts +2 -0
  128. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts.map +1 -1
  129. package/dist/lexicon/types/app/bsky/feed/threadgate.js.map +1 -1
  130. package/dist/proto/bsky_connect.d.ts +21 -1
  131. package/dist/proto/bsky_connect.d.ts.map +1 -1
  132. package/dist/proto/bsky_connect.js +20 -0
  133. package/dist/proto/bsky_connect.js.map +1 -1
  134. package/dist/proto/bsky_pb.d.ts +96 -0
  135. package/dist/proto/bsky_pb.d.ts.map +1 -1
  136. package/dist/proto/bsky_pb.js +306 -4
  137. package/dist/proto/bsky_pb.js.map +1 -1
  138. package/dist/util/uris.d.ts +12 -0
  139. package/dist/util/uris.d.ts.map +1 -0
  140. package/dist/util/uris.js +34 -0
  141. package/dist/util/uris.js.map +1 -0
  142. package/dist/views/index.d.ts +8 -2
  143. package/dist/views/index.d.ts.map +1 -1
  144. package/dist/views/index.js +83 -39
  145. package/dist/views/index.js.map +1 -1
  146. package/dist/views/types.d.ts +1 -1
  147. package/dist/views/types.d.ts.map +1 -1
  148. package/dist/views/types.js.map +1 -1
  149. package/dist/views/util.d.ts +11 -1
  150. package/dist/views/util.d.ts.map +1 -1
  151. package/dist/views/util.js +19 -8
  152. package/dist/views/util.js.map +1 -1
  153. package/package.json +4 -4
  154. package/proto/bsky.proto +33 -0
  155. package/src/api/app/bsky/feed/getActorLikes.ts +1 -1
  156. package/src/api/app/bsky/feed/getLikes.ts +1 -1
  157. package/src/api/app/bsky/feed/getPosts.ts +1 -1
  158. package/src/api/app/bsky/feed/getQuotes.ts +105 -0
  159. package/src/api/app/bsky/feed/getRepostedBy.ts +1 -1
  160. package/src/api/app/bsky/feed/searchPosts.ts +1 -1
  161. package/src/api/app/bsky/graph/getFollowers.ts +1 -1
  162. package/src/api/app/bsky/graph/getList.ts +1 -1
  163. package/src/api/app/bsky/notification/listNotifications.ts +32 -6
  164. package/src/api/index.ts +2 -0
  165. package/src/data-plane/server/db/database-schema.ts +7 -3
  166. package/src/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.ts +12 -0
  167. package/src/data-plane/server/db/migrations/20240723T220703655Z-quotes.ts +28 -0
  168. package/src/data-plane/server/db/migrations/20240801T193939827Z-post-gate.ts +17 -0
  169. package/src/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.ts +25 -0
  170. package/src/data-plane/server/db/migrations/index.ts +4 -0
  171. package/src/data-plane/server/db/tables/post-agg.ts +1 -0
  172. package/src/data-plane/server/db/tables/post-gate.ts +12 -0
  173. package/src/data-plane/server/db/tables/post.ts +3 -0
  174. package/src/data-plane/server/db/tables/quote.ts +15 -0
  175. package/src/data-plane/server/indexing/index.ts +7 -0
  176. package/src/data-plane/server/indexing/plugins/post-gate.ts +104 -0
  177. package/src/data-plane/server/indexing/plugins/post.ts +151 -16
  178. package/src/data-plane/server/indexing/plugins/thread-gate.ts +12 -0
  179. package/src/data-plane/server/routes/index.ts +2 -0
  180. package/src/data-plane/server/routes/interactions.ts +2 -1
  181. package/src/data-plane/server/routes/quotes.ts +32 -0
  182. package/src/data-plane/server/routes/records.ts +11 -1
  183. package/src/data-plane/server/util.ts +0 -8
  184. package/src/hydration/feed.ts +58 -12
  185. package/src/hydration/hydrator.ts +94 -22
  186. package/src/hydration/util.ts +0 -4
  187. package/src/lexicon/index.ts +12 -0
  188. package/src/lexicon/lexicons.ts +145 -0
  189. package/src/lexicon/types/app/bsky/embed/record.ts +19 -0
  190. package/src/lexicon/types/app/bsky/feed/defs.ts +2 -0
  191. package/src/lexicon/types/app/bsky/feed/getQuotes.ts +54 -0
  192. package/src/lexicon/types/app/bsky/feed/postgate.ts +47 -0
  193. package/src/lexicon/types/app/bsky/feed/threadgate.ts +2 -0
  194. package/src/proto/bsky_connect.ts +24 -0
  195. package/src/proto/bsky_pb.ts +289 -0
  196. package/src/util/uris.ts +31 -0
  197. package/src/views/index.ts +90 -35
  198. package/src/views/types.ts +1 -0
  199. package/src/views/util.ts +37 -7
  200. package/tests/__snapshots__/feed-generation.test.ts.snap +37 -0
  201. package/tests/data-plane/__snapshots__/indexing.test.ts.snap +18 -0
  202. package/tests/data-plane/indexing.test.ts +1 -0
  203. package/tests/postgates.test.ts +186 -0
  204. package/tests/seed/feed-hidden-replies.ts +62 -0
  205. package/tests/seed/postgates.ts +56 -0
  206. package/tests/views/__snapshots__/author-feed.test.ts.snap +56 -0
  207. package/tests/views/__snapshots__/block-lists.test.ts.snap +6 -0
  208. package/tests/views/__snapshots__/blocks.test.ts.snap +10 -0
  209. package/tests/views/__snapshots__/list-feed.test.ts.snap +22 -0
  210. package/tests/views/__snapshots__/mute-lists.test.ts.snap +8 -0
  211. package/tests/views/__snapshots__/mutes.test.ts.snap +6 -0
  212. package/tests/views/__snapshots__/posts.test.ts.snap +12 -0
  213. package/tests/views/__snapshots__/quotes.test.ts.snap +399 -0
  214. package/tests/views/__snapshots__/thread.test.ts.snap +50 -0
  215. package/tests/views/__snapshots__/timeline.test.ts.snap +170 -0
  216. package/tests/views/author-feed.test.ts +3 -9
  217. package/tests/views/feed-hidden-replies.test.ts +246 -0
  218. package/tests/views/feed-view-post.test.ts +501 -0
  219. package/tests/views/quotes.test.ts +105 -0
@@ -6,7 +6,7 @@ import { Notification } from '../proto/bsky_pb'
6
6
  import { ids } from '../lexicon/lexicons'
7
7
  import { isMain as isEmbedRecord } from '../lexicon/types/app/bsky/embed/record'
8
8
  import { isMain as isEmbedRecordWithMedia } from '../lexicon/types/app/bsky/embed/recordWithMedia'
9
- import { isListRule } from '../lexicon/types/app/bsky/feed/threadgate'
9
+ import { isListRule as isThreadgateListRule } from '../lexicon/types/app/bsky/feed/threadgate'
10
10
  import { hydrationLogger } from '../logger'
11
11
  import {
12
12
  ActorHydrator,
@@ -38,12 +38,12 @@ import {
38
38
  HydrationMap,
39
39
  RecordInfo,
40
40
  ItemRef,
41
- didFromUri,
42
41
  urisByCollection,
43
42
  mergeMaps,
44
43
  mergeNestedMaps,
45
44
  mergeManyMaps,
46
45
  } from './util'
46
+ import { uriToDid as didFromUri } from '../util/uris'
47
47
  import {
48
48
  FeedGenAggs,
49
49
  FeedGens,
@@ -56,6 +56,7 @@ import {
56
56
  PostAggs,
57
57
  PostViewerStates,
58
58
  Threadgates,
59
+ Postgates,
59
60
  FeedItem,
60
61
  } from './feed'
61
62
  import { ParsedLabelers } from '../util'
@@ -91,6 +92,7 @@ export type HydrationState = {
91
92
  follows?: Follows
92
93
  followBlocks?: FollowBlocks
93
94
  threadgates?: Threadgates
95
+ postgates?: Postgates
94
96
  lists?: Lists
95
97
  listAggs?: ListAggs
96
98
  listViewers?: ListViewerStates
@@ -109,9 +111,13 @@ export type HydrationState = {
109
111
  bidirectionalBlocks?: BidirectionalBlocks
110
112
  }
111
113
 
112
- export type PostBlock = { embed: boolean; reply: boolean }
114
+ export type PostBlock = { embed: boolean; parent: boolean; root: boolean }
113
115
  export type PostBlocks = HydrationMap<PostBlock>
114
- type PostBlockPairs = { embed?: RelationshipPair; reply?: RelationshipPair }
116
+ type PostBlockPairs = {
117
+ embed?: RelationshipPair
118
+ parent?: RelationshipPair
119
+ root?: RelationshipPair
120
+ }
115
121
 
116
122
  export type FollowBlock = boolean
117
123
  export type FollowBlocks = HydrationMap<FollowBlock>
@@ -337,27 +343,47 @@ export class Hydrator {
337
343
  const urisLayer1 = nestedRecordUrisFromPosts(postsLayer0)
338
344
  const additionalRootUris = rootUrisFromPosts(postsLayer0) // supports computing threadgates
339
345
  const urisLayer1ByCollection = urisByCollection(urisLayer1)
340
- const postUrisLayer1 = urisLayer1ByCollection.get(ids.AppBskyFeedPost) ?? []
346
+ const embedPostUrisLayer1 =
347
+ urisLayer1ByCollection.get(ids.AppBskyFeedPost) ?? []
341
348
  const postsLayer1 = await this.feed.getPosts(
342
- [...postUrisLayer1, ...additionalRootUris],
349
+ [...embedPostUrisLayer1, ...additionalRootUris],
343
350
  ctx.includeTakedowns,
344
351
  )
345
352
  // second level embeds, ignoring any additional root uris we mixed-in to the previous layer
346
- const urisLayer2 = nestedRecordUrisFromPosts(postsLayer1, postUrisLayer1)
353
+ const urisLayer2 = nestedRecordUrisFromPosts(
354
+ postsLayer1,
355
+ embedPostUrisLayer1,
356
+ )
347
357
  const urisLayer2ByCollection = urisByCollection(urisLayer2)
348
- const postUrisLayer2 = urisLayer2ByCollection.get(ids.AppBskyFeedPost) ?? []
358
+ const embedPostUrisLayer2 =
359
+ urisLayer2ByCollection.get(ids.AppBskyFeedPost) ?? []
349
360
  const threadRootUris = new Set<string>()
350
361
  for (const [uri, post] of postsLayer0) {
351
362
  if (post) {
352
363
  threadRootUris.add(rootUriFromPost(post) ?? uri)
353
364
  }
354
365
  }
366
+ const postUrisWithThreadgates = new Set<string>()
367
+ for (const uri of threadRootUris) {
368
+ const post = postsLayer0.get(uri)
369
+ /*
370
+ * Checking `post.hasThreadGate` is an optimization, which tells us that
371
+ * this post has a threadgate record associated with it. `hydratePosts`
372
+ * always hydrates root posts via `additionalRootUris`, so we try to
373
+ * check the optimization flag were possible. If the post is unavailable
374
+ * for whatever reason, we fall back to requesting threadgate records
375
+ * that may not exist.
376
+ */
377
+ if (!post || post.hasThreadGate) {
378
+ postUrisWithThreadgates.add(uri)
379
+ }
380
+ }
355
381
  const [postsLayer2, threadgates] = await Promise.all([
356
- this.feed.getPosts(postUrisLayer2, ctx.includeTakedowns),
357
- this.feed.getThreadgatesForPosts([...threadRootUris.values()]),
382
+ this.feed.getPosts(embedPostUrisLayer2, ctx.includeTakedowns),
383
+ this.feed.getThreadgatesForPosts([...postUrisWithThreadgates.values()]),
358
384
  ])
359
385
  // collect list/feedgen embeds, lists in threadgates, post record hydration
360
- const gateListUris = getListUrisFromGates(threadgates)
386
+ const threadgateListUris = getListUrisFromThreadgates(threadgates)
361
387
  const nestedListUris = [
362
388
  ...(urisLayer1ByCollection.get(ids.AppBskyGraphList) ?? []),
363
389
  ...(urisLayer2ByCollection.get(ids.AppBskyGraphList) ?? []),
@@ -369,7 +395,7 @@ export class Hydrator {
369
395
  const nestedLabelerDids = [
370
396
  ...(urisLayer1ByCollection.get(ids.AppBskyLabelerService) ?? []),
371
397
  ...(urisLayer2ByCollection.get(ids.AppBskyLabelerService) ?? []),
372
- ].map((uri) => new AtUri(uri).hostname)
398
+ ].map(didFromUri)
373
399
  const nestedStarterPackUris = [
374
400
  ...(urisLayer1ByCollection.get(ids.AppBskyGraphStarterpack) ?? []),
375
401
  ...(urisLayer2ByCollection.get(ids.AppBskyGraphStarterpack) ?? []),
@@ -379,13 +405,19 @@ export class Hydrator {
379
405
  const allPostUris = [...posts.keys()]
380
406
  const allRefs = [
381
407
  ...refs,
382
- ...postUrisLayer1.map(uriToRef), // supports aggregates on embed #viewRecords
383
- ...postUrisLayer2.map(uriToRef),
408
+ ...embedPostUrisLayer1.map(uriToRef), // supports aggregates on embed #viewRecords
409
+ ...embedPostUrisLayer2.map(uriToRef),
384
410
  ]
385
411
  const threadRefs = allRefs.map((ref) => ({
386
412
  ...ref,
387
413
  threadRoot: posts.get(ref.uri)?.record.reply?.root.uri ?? ref.uri,
388
414
  }))
415
+ const postUrisWithPostgates = new Set<string>()
416
+ for (const [uri, post] of posts) {
417
+ if (post && post.hasPostGate) {
418
+ postUrisWithPostgates.add(uri)
419
+ }
420
+ }
389
421
 
390
422
  const [
391
423
  postAggs,
@@ -397,6 +429,7 @@ export class Hydrator {
397
429
  feedGenState,
398
430
  labelerState,
399
431
  starterPackState,
432
+ postgates,
400
433
  ] = await Promise.all([
401
434
  this.feed.getPostAggregates(allRefs),
402
435
  ctx.viewer
@@ -405,10 +438,11 @@ export class Hydrator {
405
438
  this.label.getLabelsForSubjects(allPostUris, ctx.labelers),
406
439
  this.hydratePostBlocks(posts),
407
440
  this.hydrateProfiles(allPostUris.map(didFromUri), ctx),
408
- this.hydrateLists([...nestedListUris, ...gateListUris], ctx),
441
+ this.hydrateLists([...nestedListUris, ...threadgateListUris], ctx),
409
442
  this.hydrateFeedGens(nestedFeedGenUris, ctx),
410
443
  this.hydrateLabelers(nestedLabelerDids, ctx),
411
444
  this.hydrateStarterPacksBasic(nestedStarterPackUris, ctx),
445
+ this.feed.getPostgatesForPosts([...postUrisWithPostgates.values()]),
412
446
  ])
413
447
  if (!ctx.includeTakedowns) {
414
448
  actionTakedownLabels(allPostUris, posts, labels)
@@ -427,6 +461,7 @@ export class Hydrator {
427
461
  postBlocks,
428
462
  labels,
429
463
  threadgates,
464
+ postgates,
430
465
  ctx,
431
466
  },
432
467
  )
@@ -445,10 +480,17 @@ export class Hydrator {
445
480
  // 3p block for replies
446
481
  const parentUri = post.reply?.parent.uri
447
482
  const parentDid = parentUri && didFromUri(parentUri)
448
- if (parentDid) {
483
+ if (parentDid && parentDid !== creator) {
449
484
  const pair: RelationshipPair = [creator, parentDid]
450
485
  relationships.push(pair)
451
- postBlockPairs.reply = pair
486
+ postBlockPairs.parent = pair
487
+ }
488
+ const rootUri = post.reply?.root.uri
489
+ const rootDid = rootUri && didFromUri(rootUri)
490
+ if (rootDid && rootDid !== creator) {
491
+ const pair: RelationshipPair = [creator, rootDid]
492
+ relationships.push(pair)
493
+ postBlockPairs.root = pair
452
494
  }
453
495
  // 3p block for record embeds
454
496
  for (const embedUri of nestedRecordUris(post)) {
@@ -457,12 +499,13 @@ export class Hydrator {
457
499
  postBlockPairs.embed = pair
458
500
  }
459
501
  }
460
- // replace embed/reply pairs with block state
502
+ // replace embed/parent/root pairs with block state
461
503
  const blocks = await this.graph.getBidirectionalBlocks(relationships)
462
- for (const [uri, { embed, reply }] of postBlocksPairs) {
504
+ for (const [uri, { embed, parent, root }] of postBlocksPairs) {
463
505
  postBlocks.set(uri, {
464
506
  embed: !!embed && blocks.isBlocked(...embed),
465
- reply: !!reply && blocks.isBlocked(...reply),
507
+ parent: !!parent && blocks.isBlocked(...parent),
508
+ root: !!root && blocks.isBlocked(...root),
466
509
  })
467
510
  }
468
511
  return postBlocks
@@ -743,6 +786,21 @@ export class Hydrator {
743
786
  this.label.getLabelsForSubjects(uris, ctx.labelers),
744
787
  this.hydrateProfiles(uris.map(didFromUri), ctx),
745
788
  ])
789
+ const viewerRootPostUris = new Set<string>()
790
+ for (const notif of notifs) {
791
+ if (notif.reason === 'reply') {
792
+ const post = posts.get(notif.uri)
793
+ if (post) {
794
+ const rootUri = post.record.reply?.root.uri
795
+ if (rootUri && didFromUri(rootUri) === ctx.viewer) {
796
+ viewerRootPostUris.add(rootUri)
797
+ }
798
+ }
799
+ }
800
+ }
801
+ const threadgates = await this.feed.getThreadgatesForPosts([
802
+ ...viewerRootPostUris.values(),
803
+ ])
746
804
  actionTakedownLabels(postUris, posts, labels)
747
805
  return mergeStates(profileState, {
748
806
  posts,
@@ -750,6 +808,7 @@ export class Hydrator {
750
808
  reposts,
751
809
  follows,
752
810
  labels,
811
+ threadgates,
753
812
  ctx,
754
813
  })
755
814
  }
@@ -881,6 +940,18 @@ export class Hydrator {
881
940
  (await this.feed.getFeedGens([uri], includeTakedowns)).get(uri) ??
882
941
  undefined
883
942
  )
943
+ } else if (collection === ids.AppBskyFeedThreadgate) {
944
+ return (
945
+ (await this.feed.getThreadgateRecords([uri], includeTakedowns)).get(
946
+ uri,
947
+ ) ?? undefined
948
+ )
949
+ } else if (collection === ids.AppBskyFeedPostgate) {
950
+ return (
951
+ (await this.feed.getPostgateRecords([uri], includeTakedowns)).get(
952
+ uri,
953
+ ) ?? undefined
954
+ )
884
955
  } else if (collection === ids.AppBskyLabelerService) {
885
956
  if (parsed.rkey !== 'self') return
886
957
  const did = parsed.hostname
@@ -1039,10 +1110,10 @@ const nestedRecordUris = (post: Post['record']): string[] => {
1039
1110
  return uris
1040
1111
  }
1041
1112
 
1042
- const getListUrisFromGates = (gates: Threadgates) => {
1113
+ const getListUrisFromThreadgates = (gates: Threadgates) => {
1043
1114
  const uris: string[] = []
1044
1115
  for (const gate of gates.values()) {
1045
- const listRules = gate?.record.allow?.filter(isListRule) ?? []
1116
+ const listRules = gate?.record.allow?.filter(isThreadgateListRule) ?? []
1046
1117
  for (const rule of listRules) {
1047
1118
  uris.push(rule.list)
1048
1119
  }
@@ -1073,6 +1144,7 @@ export const mergeStates = (
1073
1144
  follows: mergeMaps(stateA.follows, stateB.follows),
1074
1145
  followBlocks: mergeMaps(stateA.followBlocks, stateB.followBlocks),
1075
1146
  threadgates: mergeMaps(stateA.threadgates, stateB.threadgates),
1147
+ postgates: mergeMaps(stateA.postgates, stateB.postgates),
1076
1148
  lists: mergeMaps(stateA.lists, stateB.lists),
1077
1149
  listAggs: mergeMaps(stateA.listAggs, stateB.listAggs),
1078
1150
  listViewers: mergeMaps(stateA.listViewers, stateB.listViewers),
@@ -116,10 +116,6 @@ export const parseCid = (cidStr: string | undefined): CID | undefined => {
116
116
  }
117
117
  }
118
118
 
119
- export const didFromUri = (uri: string) => {
120
- return new AtUri(uri).hostname
121
- }
122
-
123
119
  export const urisByCollection = (uris: string[]): Map<string, string[]> => {
124
120
  const result = new Map<string, string[]>()
125
121
  for (const uri of uris) {
@@ -102,6 +102,7 @@ import * as AppBskyFeedGetLikes from './types/app/bsky/feed/getLikes'
102
102
  import * as AppBskyFeedGetListFeed from './types/app/bsky/feed/getListFeed'
103
103
  import * as AppBskyFeedGetPostThread from './types/app/bsky/feed/getPostThread'
104
104
  import * as AppBskyFeedGetPosts from './types/app/bsky/feed/getPosts'
105
+ import * as AppBskyFeedGetQuotes from './types/app/bsky/feed/getQuotes'
105
106
  import * as AppBskyFeedGetRepostedBy from './types/app/bsky/feed/getRepostedBy'
106
107
  import * as AppBskyFeedGetSuggestedFeeds from './types/app/bsky/feed/getSuggestedFeeds'
107
108
  import * as AppBskyFeedGetTimeline from './types/app/bsky/feed/getTimeline'
@@ -1385,6 +1386,17 @@ export class AppBskyFeedNS {
1385
1386
  return this._server.xrpc.method(nsid, cfg)
1386
1387
  }
1387
1388
 
1389
+ getQuotes<AV extends AuthVerifier>(
1390
+ cfg: ConfigOf<
1391
+ AV,
1392
+ AppBskyFeedGetQuotes.Handler<ExtractAuth<AV>>,
1393
+ AppBskyFeedGetQuotes.HandlerReqCtx<ExtractAuth<AV>>
1394
+ >,
1395
+ ) {
1396
+ const nsid = 'app.bsky.feed.getQuotes' // @ts-ignore
1397
+ return this._server.xrpc.method(nsid, cfg)
1398
+ }
1399
+
1388
1400
  getRepostedBy<AV extends AuthVerifier>(
1389
1401
  cfg: ConfigOf<
1390
1402
  AV,
@@ -4989,6 +4989,7 @@ export const schemaDict = {
4989
4989
  'lex:app.bsky.embed.record#viewRecord',
4990
4990
  'lex:app.bsky.embed.record#viewNotFound',
4991
4991
  'lex:app.bsky.embed.record#viewBlocked',
4992
+ 'lex:app.bsky.embed.record#viewDetached',
4992
4993
  'lex:app.bsky.feed.defs#generatorView',
4993
4994
  'lex:app.bsky.graph.defs#listView',
4994
4995
  'lex:app.bsky.labeler.defs#labelerView',
@@ -5083,6 +5084,20 @@ export const schemaDict = {
5083
5084
  },
5084
5085
  },
5085
5086
  },
5087
+ viewDetached: {
5088
+ type: 'object',
5089
+ required: ['uri', 'detached'],
5090
+ properties: {
5091
+ uri: {
5092
+ type: 'string',
5093
+ format: 'at-uri',
5094
+ },
5095
+ detached: {
5096
+ type: 'boolean',
5097
+ const: true,
5098
+ },
5099
+ },
5100
+ },
5086
5101
  },
5087
5102
  },
5088
5103
  AppBskyEmbedRecordWithMedia: {
@@ -5165,6 +5180,9 @@ export const schemaDict = {
5165
5180
  likeCount: {
5166
5181
  type: 'integer',
5167
5182
  },
5183
+ quoteCount: {
5184
+ type: 'integer',
5185
+ },
5168
5186
  indexedAt: {
5169
5187
  type: 'string',
5170
5188
  format: 'datetime',
@@ -5205,6 +5223,9 @@ export const schemaDict = {
5205
5223
  replyDisabled: {
5206
5224
  type: 'boolean',
5207
5225
  },
5226
+ embeddingDisabled: {
5227
+ type: 'boolean',
5228
+ },
5208
5229
  },
5209
5230
  },
5210
5231
  feedViewPost: {
@@ -6280,6 +6301,69 @@ export const schemaDict = {
6280
6301
  },
6281
6302
  },
6282
6303
  },
6304
+ AppBskyFeedGetQuotes: {
6305
+ lexicon: 1,
6306
+ id: 'app.bsky.feed.getQuotes',
6307
+ defs: {
6308
+ main: {
6309
+ type: 'query',
6310
+ description: 'Get a list of quotes for a given post.',
6311
+ parameters: {
6312
+ type: 'params',
6313
+ required: ['uri'],
6314
+ properties: {
6315
+ uri: {
6316
+ type: 'string',
6317
+ format: 'at-uri',
6318
+ description: 'Reference (AT-URI) of post record',
6319
+ },
6320
+ cid: {
6321
+ type: 'string',
6322
+ format: 'cid',
6323
+ description:
6324
+ 'If supplied, filters to quotes of specific version (by CID) of the post record.',
6325
+ },
6326
+ limit: {
6327
+ type: 'integer',
6328
+ minimum: 1,
6329
+ maximum: 100,
6330
+ default: 50,
6331
+ },
6332
+ cursor: {
6333
+ type: 'string',
6334
+ },
6335
+ },
6336
+ },
6337
+ output: {
6338
+ encoding: 'application/json',
6339
+ schema: {
6340
+ type: 'object',
6341
+ required: ['uri', 'posts'],
6342
+ properties: {
6343
+ uri: {
6344
+ type: 'string',
6345
+ format: 'at-uri',
6346
+ },
6347
+ cid: {
6348
+ type: 'string',
6349
+ format: 'cid',
6350
+ },
6351
+ cursor: {
6352
+ type: 'string',
6353
+ },
6354
+ posts: {
6355
+ type: 'array',
6356
+ items: {
6357
+ type: 'ref',
6358
+ ref: 'lex:app.bsky.feed.defs#postView',
6359
+ },
6360
+ },
6361
+ },
6362
+ },
6363
+ },
6364
+ },
6365
+ },
6366
+ },
6283
6367
  AppBskyFeedGetRepostedBy: {
6284
6368
  lexicon: 1,
6285
6369
  id: 'app.bsky.feed.getRepostedBy',
@@ -6596,6 +6680,56 @@ export const schemaDict = {
6596
6680
  },
6597
6681
  },
6598
6682
  },
6683
+ AppBskyFeedPostgate: {
6684
+ lexicon: 1,
6685
+ id: 'app.bsky.feed.postgate',
6686
+ defs: {
6687
+ main: {
6688
+ type: 'record',
6689
+ key: 'tid',
6690
+ description:
6691
+ 'Record defining interaction rules for a post. The record key (rkey) of the postgate record must match the record key of the post, and that record must be in the same repository.',
6692
+ record: {
6693
+ type: 'object',
6694
+ required: ['post', 'createdAt'],
6695
+ properties: {
6696
+ createdAt: {
6697
+ type: 'string',
6698
+ format: 'datetime',
6699
+ },
6700
+ post: {
6701
+ type: 'string',
6702
+ format: 'at-uri',
6703
+ description: 'Reference (AT-URI) to the post record.',
6704
+ },
6705
+ detachedEmbeddingUris: {
6706
+ type: 'array',
6707
+ maxLength: 50,
6708
+ items: {
6709
+ type: 'string',
6710
+ format: 'at-uri',
6711
+ },
6712
+ description:
6713
+ 'List of AT-URIs embedding this post that the author has detached from.',
6714
+ },
6715
+ embeddingRules: {
6716
+ type: 'array',
6717
+ maxLength: 5,
6718
+ items: {
6719
+ type: 'union',
6720
+ refs: ['lex:app.bsky.feed.postgate#disableRule'],
6721
+ },
6722
+ },
6723
+ },
6724
+ },
6725
+ },
6726
+ disableRule: {
6727
+ type: 'object',
6728
+ description: 'Disables embedding of this post.',
6729
+ properties: {},
6730
+ },
6731
+ },
6732
+ },
6599
6733
  AppBskyFeedRepost: {
6600
6734
  lexicon: 1,
6601
6735
  id: 'app.bsky.feed.repost',
@@ -6807,6 +6941,15 @@ export const schemaDict = {
6807
6941
  type: 'string',
6808
6942
  format: 'datetime',
6809
6943
  },
6944
+ hiddenReplies: {
6945
+ type: 'array',
6946
+ maxLength: 50,
6947
+ items: {
6948
+ type: 'string',
6949
+ format: 'at-uri',
6950
+ },
6951
+ description: 'List of hidden reply URIs.',
6952
+ },
6810
6953
  },
6811
6954
  },
6812
6955
  },
@@ -10074,11 +10217,13 @@ export const ids = {
10074
10217
  AppBskyFeedGetListFeed: 'app.bsky.feed.getListFeed',
10075
10218
  AppBskyFeedGetPostThread: 'app.bsky.feed.getPostThread',
10076
10219
  AppBskyFeedGetPosts: 'app.bsky.feed.getPosts',
10220
+ AppBskyFeedGetQuotes: 'app.bsky.feed.getQuotes',
10077
10221
  AppBskyFeedGetRepostedBy: 'app.bsky.feed.getRepostedBy',
10078
10222
  AppBskyFeedGetSuggestedFeeds: 'app.bsky.feed.getSuggestedFeeds',
10079
10223
  AppBskyFeedGetTimeline: 'app.bsky.feed.getTimeline',
10080
10224
  AppBskyFeedLike: 'app.bsky.feed.like',
10081
10225
  AppBskyFeedPost: 'app.bsky.feed.post',
10226
+ AppBskyFeedPostgate: 'app.bsky.feed.postgate',
10082
10227
  AppBskyFeedRepost: 'app.bsky.feed.repost',
10083
10228
  AppBskyFeedSearchPosts: 'app.bsky.feed.searchPosts',
10084
10229
  AppBskyFeedSendInteractions: 'app.bsky.feed.sendInteractions',
@@ -38,6 +38,7 @@ export interface View {
38
38
  | ViewRecord
39
39
  | ViewNotFound
40
40
  | ViewBlocked
41
+ | ViewDetached
41
42
  | AppBskyFeedDefs.GeneratorView
42
43
  | AppBskyGraphDefs.ListView
43
44
  | AppBskyLabelerDefs.LabelerView
@@ -125,3 +126,21 @@ export function isViewBlocked(v: unknown): v is ViewBlocked {
125
126
  export function validateViewBlocked(v: unknown): ValidationResult {
126
127
  return lexicons.validate('app.bsky.embed.record#viewBlocked', v)
127
128
  }
129
+
130
+ export interface ViewDetached {
131
+ uri: string
132
+ detached: true
133
+ [k: string]: unknown
134
+ }
135
+
136
+ export function isViewDetached(v: unknown): v is ViewDetached {
137
+ return (
138
+ isObj(v) &&
139
+ hasProp(v, '$type') &&
140
+ v.$type === 'app.bsky.embed.record#viewDetached'
141
+ )
142
+ }
143
+
144
+ export function validateViewDetached(v: unknown): ValidationResult {
145
+ return lexicons.validate('app.bsky.embed.record#viewDetached', v)
146
+ }
@@ -28,6 +28,7 @@ export interface PostView {
28
28
  replyCount?: number
29
29
  repostCount?: number
30
30
  likeCount?: number
31
+ quoteCount?: number
31
32
  indexedAt: string
32
33
  viewer?: ViewerState
33
34
  labels?: ComAtprotoLabelDefs.Label[]
@@ -51,6 +52,7 @@ export interface ViewerState {
51
52
  like?: string
52
53
  threadMuted?: boolean
53
54
  replyDisabled?: boolean
55
+ embeddingDisabled?: boolean
54
56
  [k: string]: unknown
55
57
  }
56
58
 
@@ -0,0 +1,54 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as AppBskyFeedDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ /** Reference (AT-URI) of post record */
14
+ uri: string
15
+ /** If supplied, filters to quotes of specific version (by CID) of the post record. */
16
+ cid?: string
17
+ limit: number
18
+ cursor?: string
19
+ }
20
+
21
+ export type InputSchema = undefined
22
+
23
+ export interface OutputSchema {
24
+ uri: string
25
+ cid?: string
26
+ cursor?: string
27
+ posts: AppBskyFeedDefs.PostView[]
28
+ [k: string]: unknown
29
+ }
30
+
31
+ export type HandlerInput = undefined
32
+
33
+ export interface HandlerSuccess {
34
+ encoding: 'application/json'
35
+ body: OutputSchema
36
+ headers?: { [key: string]: string }
37
+ }
38
+
39
+ export interface HandlerError {
40
+ status: number
41
+ message?: string
42
+ }
43
+
44
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
45
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
46
+ auth: HA
47
+ params: QueryParams
48
+ input: HandlerInput
49
+ req: express.Request
50
+ res: express.Response
51
+ }
52
+ export type Handler<HA extends HandlerAuth = never> = (
53
+ ctx: HandlerReqCtx<HA>,
54
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,47 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
5
+ import { lexicons } from '../../../../lexicons'
6
+ import { isObj, hasProp } from '../../../../util'
7
+ import { CID } from 'multiformats/cid'
8
+
9
+ export interface Record {
10
+ createdAt: string
11
+ /** Reference (AT-URI) to the post record. */
12
+ post: string
13
+ /** List of AT-URIs embedding this post that the author has detached from. */
14
+ detachedEmbeddingUris?: string[]
15
+ embeddingRules?: (DisableRule | { $type: string; [k: string]: unknown })[]
16
+ [k: string]: unknown
17
+ }
18
+
19
+ export function isRecord(v: unknown): v is Record {
20
+ return (
21
+ isObj(v) &&
22
+ hasProp(v, '$type') &&
23
+ (v.$type === 'app.bsky.feed.postgate#main' ||
24
+ v.$type === 'app.bsky.feed.postgate')
25
+ )
26
+ }
27
+
28
+ export function validateRecord(v: unknown): ValidationResult {
29
+ return lexicons.validate('app.bsky.feed.postgate#main', v)
30
+ }
31
+
32
+ /** Disables embedding of this post. */
33
+ export interface DisableRule {
34
+ [k: string]: unknown
35
+ }
36
+
37
+ export function isDisableRule(v: unknown): v is DisableRule {
38
+ return (
39
+ isObj(v) &&
40
+ hasProp(v, '$type') &&
41
+ v.$type === 'app.bsky.feed.postgate#disableRule'
42
+ )
43
+ }
44
+
45
+ export function validateDisableRule(v: unknown): ValidationResult {
46
+ return lexicons.validate('app.bsky.feed.postgate#disableRule', v)
47
+ }
@@ -16,6 +16,8 @@ export interface Record {
16
16
  | { $type: string; [k: string]: unknown }
17
17
  )[]
18
18
  createdAt: string
19
+ /** List of hidden reply URIs. */
20
+ hiddenReplies?: string[]
19
21
  [k: string]: unknown
20
22
  }
21
23