@atproto/bsky 0.0.76 → 0.0.78

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 (226) hide show
  1. package/CHANGELOG.md +20 -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/getListFeed.d.ts.map +1 -1
  7. package/dist/api/app/bsky/feed/getListFeed.js +19 -3
  8. package/dist/api/app/bsky/feed/getListFeed.js.map +1 -1
  9. package/dist/api/app/bsky/feed/getPosts.js +4 -4
  10. package/dist/api/app/bsky/feed/getPosts.js.map +1 -1
  11. package/dist/api/app/bsky/feed/getQuotes.d.ts +4 -0
  12. package/dist/api/app/bsky/feed/getQuotes.d.ts.map +1 -0
  13. package/dist/api/app/bsky/feed/getQuotes.js +67 -0
  14. package/dist/api/app/bsky/feed/getQuotes.js.map +1 -0
  15. package/dist/api/app/bsky/feed/getRepostedBy.js +6 -6
  16. package/dist/api/app/bsky/feed/getRepostedBy.js.map +1 -1
  17. package/dist/api/app/bsky/feed/searchPosts.js +4 -4
  18. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  19. package/dist/api/app/bsky/graph/getFollowers.js +8 -8
  20. package/dist/api/app/bsky/graph/getFollowers.js.map +1 -1
  21. package/dist/api/app/bsky/graph/getList.js +7 -7
  22. package/dist/api/app/bsky/graph/getList.js.map +1 -1
  23. package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
  24. package/dist/api/app/bsky/notification/listNotifications.js +29 -8
  25. package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
  26. package/dist/api/index.d.ts.map +1 -1
  27. package/dist/api/index.js +2 -0
  28. package/dist/api/index.js.map +1 -1
  29. package/dist/data-plane/server/db/database-schema.d.ts +4 -2
  30. package/dist/data-plane/server/db/database-schema.d.ts.map +1 -1
  31. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.d.ts +4 -0
  32. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.d.ts.map +1 -0
  33. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.js +15 -0
  34. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.js.map +1 -0
  35. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.d.ts +4 -0
  36. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.d.ts.map +1 -0
  37. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.js +30 -0
  38. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.js.map +1 -0
  39. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.d.ts +4 -0
  40. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.d.ts.map +1 -0
  41. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.js +20 -0
  42. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.js.map +1 -0
  43. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.d.ts +4 -0
  44. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.d.ts.map +1 -0
  45. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.js +28 -0
  46. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.js.map +1 -0
  47. package/dist/data-plane/server/db/migrations/index.d.ts +4 -0
  48. package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
  49. package/dist/data-plane/server/db/migrations/index.js +5 -1
  50. package/dist/data-plane/server/db/migrations/index.js.map +1 -1
  51. package/dist/data-plane/server/db/tables/post-agg.d.ts +1 -0
  52. package/dist/data-plane/server/db/tables/post-agg.d.ts.map +1 -1
  53. package/dist/data-plane/server/db/tables/post-gate.d.ts +14 -0
  54. package/dist/data-plane/server/db/tables/post-gate.d.ts.map +1 -0
  55. package/dist/data-plane/server/db/tables/post-gate.js +4 -0
  56. package/dist/data-plane/server/db/tables/post-gate.js.map +1 -0
  57. package/dist/data-plane/server/db/tables/post.d.ts +3 -0
  58. package/dist/data-plane/server/db/tables/post.d.ts.map +1 -1
  59. package/dist/data-plane/server/db/tables/quote.d.ts +16 -0
  60. package/dist/data-plane/server/db/tables/quote.d.ts.map +1 -0
  61. package/dist/data-plane/server/db/tables/quote.js +4 -0
  62. package/dist/data-plane/server/db/tables/quote.js.map +1 -0
  63. package/dist/data-plane/server/indexing/index.d.ts +2 -0
  64. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  65. package/dist/data-plane/server/indexing/index.js +6 -0
  66. package/dist/data-plane/server/indexing/index.js.map +1 -1
  67. package/dist/data-plane/server/indexing/plugins/post-gate.d.ts +10 -0
  68. package/dist/data-plane/server/indexing/plugins/post-gate.d.ts.map +1 -0
  69. package/dist/data-plane/server/indexing/plugins/post-gate.js +101 -0
  70. package/dist/data-plane/server/indexing/plugins/post-gate.js.map +1 -0
  71. package/dist/data-plane/server/indexing/plugins/post.d.ts +2 -0
  72. package/dist/data-plane/server/indexing/plugins/post.d.ts.map +1 -1
  73. package/dist/data-plane/server/indexing/plugins/post.js +122 -15
  74. package/dist/data-plane/server/indexing/plugins/post.js.map +1 -1
  75. package/dist/data-plane/server/indexing/plugins/thread-gate.d.ts.map +1 -1
  76. package/dist/data-plane/server/indexing/plugins/thread-gate.js +12 -0
  77. package/dist/data-plane/server/indexing/plugins/thread-gate.js.map +1 -1
  78. package/dist/data-plane/server/routes/index.d.ts.map +1 -1
  79. package/dist/data-plane/server/routes/index.js +2 -0
  80. package/dist/data-plane/server/routes/index.js.map +1 -1
  81. package/dist/data-plane/server/routes/interactions.d.ts.map +1 -1
  82. package/dist/data-plane/server/routes/interactions.js +2 -1
  83. package/dist/data-plane/server/routes/interactions.js.map +1 -1
  84. package/dist/data-plane/server/routes/quotes.d.ts +6 -0
  85. package/dist/data-plane/server/routes/quotes.d.ts.map +1 -0
  86. package/dist/data-plane/server/routes/quotes.js +27 -0
  87. package/dist/data-plane/server/routes/quotes.js.map +1 -0
  88. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  89. package/dist/data-plane/server/routes/records.js +11 -1
  90. package/dist/data-plane/server/routes/records.js.map +1 -1
  91. package/dist/data-plane/server/util.d.ts +6 -7
  92. package/dist/data-plane/server/util.d.ts.map +1 -1
  93. package/dist/data-plane/server/util.js +1 -9
  94. package/dist/data-plane/server/util.js.map +1 -1
  95. package/dist/hydration/feed.d.ts +10 -0
  96. package/dist/hydration/feed.d.ts.map +1 -1
  97. package/dist/hydration/feed.js +31 -7
  98. package/dist/hydration/feed.js.map +1 -1
  99. package/dist/hydration/hydrator.d.ts +4 -2
  100. package/dist/hydration/hydrator.d.ts.map +1 -1
  101. package/dist/hydration/hydrator.js +89 -34
  102. package/dist/hydration/hydrator.js.map +1 -1
  103. package/dist/hydration/util.d.ts +0 -1
  104. package/dist/hydration/util.d.ts.map +1 -1
  105. package/dist/hydration/util.js +1 -5
  106. package/dist/hydration/util.js.map +1 -1
  107. package/dist/lexicon/index.d.ts +2 -0
  108. package/dist/lexicon/index.d.ts.map +1 -1
  109. package/dist/lexicon/index.js +4 -0
  110. package/dist/lexicon/index.js.map +1 -1
  111. package/dist/lexicon/lexicons.d.ts +144 -0
  112. package/dist/lexicon/lexicons.d.ts.map +1 -1
  113. package/dist/lexicon/lexicons.js +146 -1
  114. package/dist/lexicon/lexicons.js.map +1 -1
  115. package/dist/lexicon/types/app/bsky/embed/record.d.ts +9 -1
  116. package/dist/lexicon/types/app/bsky/embed/record.d.ts.map +1 -1
  117. package/dist/lexicon/types/app/bsky/embed/record.js +11 -1
  118. package/dist/lexicon/types/app/bsky/embed/record.js.map +1 -1
  119. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +2 -0
  120. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  121. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  122. package/dist/lexicon/types/app/bsky/feed/getQuotes.d.ts +44 -0
  123. package/dist/lexicon/types/app/bsky/feed/getQuotes.d.ts.map +1 -0
  124. package/dist/lexicon/types/app/bsky/feed/getQuotes.js +3 -0
  125. package/dist/lexicon/types/app/bsky/feed/getQuotes.js.map +1 -0
  126. package/dist/lexicon/types/app/bsky/feed/postgate.d.ts +25 -0
  127. package/dist/lexicon/types/app/bsky/feed/postgate.d.ts.map +1 -0
  128. package/dist/lexicon/types/app/bsky/feed/postgate.js +27 -0
  129. package/dist/lexicon/types/app/bsky/feed/postgate.js.map +1 -0
  130. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts +2 -0
  131. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts.map +1 -1
  132. package/dist/lexicon/types/app/bsky/feed/threadgate.js.map +1 -1
  133. package/dist/proto/bsky_connect.d.ts +30 -1
  134. package/dist/proto/bsky_connect.d.ts.map +1 -1
  135. package/dist/proto/bsky_connect.js +29 -0
  136. package/dist/proto/bsky_connect.js.map +1 -1
  137. package/dist/proto/bsky_pb.d.ts +135 -1
  138. package/dist/proto/bsky_pb.d.ts.map +1 -1
  139. package/dist/proto/bsky_pb.js +425 -5
  140. package/dist/proto/bsky_pb.js.map +1 -1
  141. package/dist/util/uris.d.ts +12 -0
  142. package/dist/util/uris.d.ts.map +1 -0
  143. package/dist/util/uris.js +34 -0
  144. package/dist/util/uris.js.map +1 -0
  145. package/dist/views/index.d.ts +8 -2
  146. package/dist/views/index.d.ts.map +1 -1
  147. package/dist/views/index.js +84 -39
  148. package/dist/views/index.js.map +1 -1
  149. package/dist/views/types.d.ts +1 -1
  150. package/dist/views/types.d.ts.map +1 -1
  151. package/dist/views/types.js.map +1 -1
  152. package/dist/views/util.d.ts +11 -1
  153. package/dist/views/util.d.ts.map +1 -1
  154. package/dist/views/util.js +19 -8
  155. package/dist/views/util.js.map +1 -1
  156. package/package.json +4 -4
  157. package/proto/bsky.proto +42 -2
  158. package/src/api/app/bsky/feed/getActorLikes.ts +1 -1
  159. package/src/api/app/bsky/feed/getLikes.ts +1 -1
  160. package/src/api/app/bsky/feed/getListFeed.ts +30 -3
  161. package/src/api/app/bsky/feed/getPosts.ts +1 -1
  162. package/src/api/app/bsky/feed/getQuotes.ts +108 -0
  163. package/src/api/app/bsky/feed/getRepostedBy.ts +1 -1
  164. package/src/api/app/bsky/feed/searchPosts.ts +1 -1
  165. package/src/api/app/bsky/graph/getFollowers.ts +1 -1
  166. package/src/api/app/bsky/graph/getList.ts +5 -5
  167. package/src/api/app/bsky/notification/listNotifications.ts +32 -6
  168. package/src/api/index.ts +2 -0
  169. package/src/data-plane/server/db/database-schema.ts +7 -3
  170. package/src/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.ts +12 -0
  171. package/src/data-plane/server/db/migrations/20240723T220703655Z-quotes.ts +28 -0
  172. package/src/data-plane/server/db/migrations/20240801T193939827Z-post-gate.ts +17 -0
  173. package/src/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.ts +25 -0
  174. package/src/data-plane/server/db/migrations/index.ts +4 -0
  175. package/src/data-plane/server/db/tables/post-agg.ts +1 -0
  176. package/src/data-plane/server/db/tables/post-gate.ts +12 -0
  177. package/src/data-plane/server/db/tables/post.ts +3 -0
  178. package/src/data-plane/server/db/tables/quote.ts +15 -0
  179. package/src/data-plane/server/indexing/index.ts +7 -0
  180. package/src/data-plane/server/indexing/plugins/post-gate.ts +104 -0
  181. package/src/data-plane/server/indexing/plugins/post.ts +151 -16
  182. package/src/data-plane/server/indexing/plugins/thread-gate.ts +12 -0
  183. package/src/data-plane/server/routes/index.ts +2 -0
  184. package/src/data-plane/server/routes/interactions.ts +2 -1
  185. package/src/data-plane/server/routes/quotes.ts +32 -0
  186. package/src/data-plane/server/routes/records.ts +11 -1
  187. package/src/data-plane/server/util.ts +0 -8
  188. package/src/hydration/feed.ts +58 -12
  189. package/src/hydration/hydrator.ts +94 -22
  190. package/src/hydration/util.ts +0 -4
  191. package/src/lexicon/index.ts +12 -0
  192. package/src/lexicon/lexicons.ts +149 -1
  193. package/src/lexicon/types/app/bsky/embed/record.ts +20 -0
  194. package/src/lexicon/types/app/bsky/feed/defs.ts +2 -0
  195. package/src/lexicon/types/app/bsky/feed/getQuotes.ts +54 -0
  196. package/src/lexicon/types/app/bsky/feed/postgate.ts +47 -0
  197. package/src/lexicon/types/app/bsky/feed/threadgate.ts +2 -0
  198. package/src/proto/bsky_connect.ts +35 -0
  199. package/src/proto/bsky_pb.ts +424 -1
  200. package/src/util/uris.ts +31 -0
  201. package/src/views/index.ts +91 -35
  202. package/src/views/types.ts +1 -0
  203. package/src/views/util.ts +37 -7
  204. package/tests/__snapshots__/feed-generation.test.ts.snap +42 -0
  205. package/tests/data-plane/__snapshots__/indexing.test.ts.snap +20 -0
  206. package/tests/data-plane/indexing.test.ts +1 -0
  207. package/tests/postgates.test.ts +186 -0
  208. package/tests/seed/feed-hidden-replies.ts +62 -0
  209. package/tests/seed/postgates.ts +56 -0
  210. package/tests/views/__snapshots__/author-feed.test.ts.snap +65 -0
  211. package/tests/views/__snapshots__/block-lists.test.ts.snap +7 -0
  212. package/tests/views/__snapshots__/blocks.test.ts.snap +11 -0
  213. package/tests/views/__snapshots__/list-feed.test.ts.snap +24 -0
  214. package/tests/views/__snapshots__/lists.test.ts.snap +185 -1
  215. package/tests/views/__snapshots__/mute-lists.test.ts.snap +8 -0
  216. package/tests/views/__snapshots__/mutes.test.ts.snap +6 -0
  217. package/tests/views/__snapshots__/posts.test.ts.snap +15 -0
  218. package/tests/views/__snapshots__/quotes.test.ts.snap +402 -0
  219. package/tests/views/__snapshots__/thread.test.ts.snap +50 -0
  220. package/tests/views/__snapshots__/timeline.test.ts.snap +191 -0
  221. package/tests/views/author-feed.test.ts +3 -9
  222. package/tests/views/feed-hidden-replies.test.ts +246 -0
  223. package/tests/views/feed-view-post.test.ts +501 -0
  224. package/tests/views/list-feed.test.ts +12 -0
  225. package/tests/views/lists.test.ts +83 -18
  226. package/tests/views/quotes.test.ts +105 -0
@@ -4,6 +4,7 @@ import { Record as LikeRecord } from '../lexicon/types/app/bsky/feed/like'
4
4
  import { Record as RepostRecord } from '../lexicon/types/app/bsky/feed/repost'
5
5
  import { Record as FeedGenRecord } from '../lexicon/types/app/bsky/feed/generator'
6
6
  import { Record as ThreadgateRecord } from '../lexicon/types/app/bsky/feed/threadgate'
7
+ import { Record as PostgateRecord } from '../lexicon/types/app/bsky/feed/postgate'
7
8
  import {
8
9
  HydrationMap,
9
10
  ItemRef,
@@ -12,11 +13,15 @@ import {
12
13
  parseString,
13
14
  split,
14
15
  } from './util'
15
- import { AtUri } from '@atproto/syntax'
16
- import { ids } from '../lexicon/lexicons'
17
16
  import { dedupeStrs } from '@atproto/common'
17
+ import { postUriToThreadgateUri, postUriToPostgateUri } from '../util/uris'
18
18
 
19
- export type Post = RecordInfo<PostRecord> & { violatesThreadGate: boolean }
19
+ export type Post = RecordInfo<PostRecord> & {
20
+ violatesThreadGate: boolean
21
+ violatesEmbeddingRules: boolean
22
+ hasThreadGate: boolean
23
+ hasPostGate: boolean
24
+ }
20
25
  export type Posts = HydrationMap<Post>
21
26
 
22
27
  export type PostViewerState = {
@@ -31,6 +36,7 @@ export type PostAgg = {
31
36
  likes: number
32
37
  replies: number
33
38
  reposts: number
39
+ quotes: number
34
40
  }
35
41
 
36
42
  export type PostAggs = HydrationMap<PostAgg>
@@ -58,6 +64,8 @@ export type FeedGenViewerStates = HydrationMap<FeedGenViewerState>
58
64
 
59
65
  export type Threadgate = RecordInfo<ThreadgateRecord>
60
66
  export type Threadgates = HydrationMap<Threadgate>
67
+ export type Postgate = RecordInfo<PostgateRecord>
68
+ export type Postgates = HydrationMap<Postgate>
61
69
 
62
70
  export type ThreadRef = ItemRef & { threadRoot: string }
63
71
 
@@ -83,7 +91,21 @@ export class FeedHydrator {
83
91
  return need.reduce((acc, uri, i) => {
84
92
  const record = parseRecord<PostRecord>(res.records[i], includeTakedowns)
85
93
  const violatesThreadGate = res.meta[i].violatesThreadGate
86
- return acc.set(uri, record ? { ...record, violatesThreadGate } : null)
94
+ const violatesEmbeddingRules = res.meta[i].violatesEmbeddingRules
95
+ const hasThreadGate = res.meta[i].hasThreadGate
96
+ const hasPostGate = res.meta[i].hasPostGate
97
+ return acc.set(
98
+ uri,
99
+ record
100
+ ? {
101
+ ...record,
102
+ violatesThreadGate,
103
+ violatesEmbeddingRules,
104
+ hasThreadGate,
105
+ hasPostGate,
106
+ }
107
+ : null,
108
+ )
87
109
  }, base)
88
110
  }
89
111
 
@@ -135,6 +157,7 @@ export class FeedHydrator {
135
157
  likes: counts.likes[i] ?? 0,
136
158
  reposts: counts.reposts[i] ?? 0,
137
159
  replies: counts.replies[i] ?? 0,
160
+ quotes: counts.quotes[i] ?? 0,
138
161
  })
139
162
  }, new HydrationMap<PostAgg>())
140
163
  }
@@ -185,14 +208,14 @@ export class FeedHydrator {
185
208
  includeTakedowns = false,
186
209
  ): Promise<Threadgates> {
187
210
  if (!postUris.length) return new HydrationMap<Threadgate>()
188
- const uris = postUris.map((uri) => {
189
- const parsed = new AtUri(uri)
190
- return AtUri.make(
191
- parsed.hostname,
192
- ids.AppBskyFeedThreadgate,
193
- parsed.rkey,
194
- ).toString()
195
- })
211
+ const uris = postUris.map(postUriToThreadgateUri)
212
+ return this.getThreadgateRecords(uris, includeTakedowns)
213
+ }
214
+
215
+ async getThreadgateRecords(
216
+ uris: string[],
217
+ includeTakedowns = false,
218
+ ): Promise<Threadgates> {
196
219
  const res = await this.dataplane.getThreadGateRecords({ uris })
197
220
  return uris.reduce((acc, uri, i) => {
198
221
  const record = parseRecord<ThreadgateRecord>(
@@ -203,6 +226,29 @@ export class FeedHydrator {
203
226
  }, new HydrationMap<Threadgate>())
204
227
  }
205
228
 
229
+ async getPostgatesForPosts(
230
+ postUris: string[],
231
+ includeTakedowns = false,
232
+ ): Promise<Postgates> {
233
+ if (!postUris.length) return new HydrationMap<Postgate>()
234
+ const uris = postUris.map(postUriToPostgateUri)
235
+ return this.getPostgateRecords(uris, includeTakedowns)
236
+ }
237
+
238
+ async getPostgateRecords(
239
+ uris: string[],
240
+ includeTakedowns = false,
241
+ ): Promise<Postgates> {
242
+ const res = await this.dataplane.getPostgateRecords({ uris })
243
+ return uris.reduce((acc, uri, i) => {
244
+ const record = parseRecord<PostgateRecord>(
245
+ res.records[i],
246
+ includeTakedowns,
247
+ )
248
+ return acc.set(uri, record ?? null)
249
+ }, new HydrationMap<Postgate>())
250
+ }
251
+
206
252
  async getLikes(uris: string[], includeTakedowns = false): Promise<Likes> {
207
253
  if (!uris.length) return new HydrationMap<Like>()
208
254
  const res = await this.dataplane.getLikeRecords({ uris })
@@ -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',
@@ -5033,6 +5034,9 @@ export const schemaDict = {
5033
5034
  likeCount: {
5034
5035
  type: 'integer',
5035
5036
  },
5037
+ quoteCount: {
5038
+ type: 'integer',
5039
+ },
5036
5040
  embeds: {
5037
5041
  type: 'array',
5038
5042
  items: {
@@ -5083,6 +5087,20 @@ export const schemaDict = {
5083
5087
  },
5084
5088
  },
5085
5089
  },
5090
+ viewDetached: {
5091
+ type: 'object',
5092
+ required: ['uri', 'detached'],
5093
+ properties: {
5094
+ uri: {
5095
+ type: 'string',
5096
+ format: 'at-uri',
5097
+ },
5098
+ detached: {
5099
+ type: 'boolean',
5100
+ const: true,
5101
+ },
5102
+ },
5103
+ },
5086
5104
  },
5087
5105
  },
5088
5106
  AppBskyEmbedRecordWithMedia: {
@@ -5165,6 +5183,9 @@ export const schemaDict = {
5165
5183
  likeCount: {
5166
5184
  type: 'integer',
5167
5185
  },
5186
+ quoteCount: {
5187
+ type: 'integer',
5188
+ },
5168
5189
  indexedAt: {
5169
5190
  type: 'string',
5170
5191
  format: 'datetime',
@@ -5205,6 +5226,9 @@ export const schemaDict = {
5205
5226
  replyDisabled: {
5206
5227
  type: 'boolean',
5207
5228
  },
5229
+ embeddingDisabled: {
5230
+ type: 'boolean',
5231
+ },
5208
5232
  },
5209
5233
  },
5210
5234
  feedViewPost: {
@@ -5728,7 +5752,7 @@ export const schemaDict = {
5728
5752
  main: {
5729
5753
  type: 'query',
5730
5754
  description:
5731
- 'Get a list of posts liked by an actor. Does not require auth.',
5755
+ 'Get a list of posts liked by an actor. Requires auth, actor must be the requesting account.',
5732
5756
  parameters: {
5733
5757
  type: 'params',
5734
5758
  required: ['actor'],
@@ -6280,6 +6304,69 @@ export const schemaDict = {
6280
6304
  },
6281
6305
  },
6282
6306
  },
6307
+ AppBskyFeedGetQuotes: {
6308
+ lexicon: 1,
6309
+ id: 'app.bsky.feed.getQuotes',
6310
+ defs: {
6311
+ main: {
6312
+ type: 'query',
6313
+ description: 'Get a list of quotes for a given post.',
6314
+ parameters: {
6315
+ type: 'params',
6316
+ required: ['uri'],
6317
+ properties: {
6318
+ uri: {
6319
+ type: 'string',
6320
+ format: 'at-uri',
6321
+ description: 'Reference (AT-URI) of post record',
6322
+ },
6323
+ cid: {
6324
+ type: 'string',
6325
+ format: 'cid',
6326
+ description:
6327
+ 'If supplied, filters to quotes of specific version (by CID) of the post record.',
6328
+ },
6329
+ limit: {
6330
+ type: 'integer',
6331
+ minimum: 1,
6332
+ maximum: 100,
6333
+ default: 50,
6334
+ },
6335
+ cursor: {
6336
+ type: 'string',
6337
+ },
6338
+ },
6339
+ },
6340
+ output: {
6341
+ encoding: 'application/json',
6342
+ schema: {
6343
+ type: 'object',
6344
+ required: ['uri', 'posts'],
6345
+ properties: {
6346
+ uri: {
6347
+ type: 'string',
6348
+ format: 'at-uri',
6349
+ },
6350
+ cid: {
6351
+ type: 'string',
6352
+ format: 'cid',
6353
+ },
6354
+ cursor: {
6355
+ type: 'string',
6356
+ },
6357
+ posts: {
6358
+ type: 'array',
6359
+ items: {
6360
+ type: 'ref',
6361
+ ref: 'lex:app.bsky.feed.defs#postView',
6362
+ },
6363
+ },
6364
+ },
6365
+ },
6366
+ },
6367
+ },
6368
+ },
6369
+ },
6283
6370
  AppBskyFeedGetRepostedBy: {
6284
6371
  lexicon: 1,
6285
6372
  id: 'app.bsky.feed.getRepostedBy',
@@ -6596,6 +6683,56 @@ export const schemaDict = {
6596
6683
  },
6597
6684
  },
6598
6685
  },
6686
+ AppBskyFeedPostgate: {
6687
+ lexicon: 1,
6688
+ id: 'app.bsky.feed.postgate',
6689
+ defs: {
6690
+ main: {
6691
+ type: 'record',
6692
+ key: 'tid',
6693
+ description:
6694
+ '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.',
6695
+ record: {
6696
+ type: 'object',
6697
+ required: ['post', 'createdAt'],
6698
+ properties: {
6699
+ createdAt: {
6700
+ type: 'string',
6701
+ format: 'datetime',
6702
+ },
6703
+ post: {
6704
+ type: 'string',
6705
+ format: 'at-uri',
6706
+ description: 'Reference (AT-URI) to the post record.',
6707
+ },
6708
+ detachedEmbeddingUris: {
6709
+ type: 'array',
6710
+ maxLength: 50,
6711
+ items: {
6712
+ type: 'string',
6713
+ format: 'at-uri',
6714
+ },
6715
+ description:
6716
+ 'List of AT-URIs embedding this post that the author has detached from.',
6717
+ },
6718
+ embeddingRules: {
6719
+ type: 'array',
6720
+ maxLength: 5,
6721
+ items: {
6722
+ type: 'union',
6723
+ refs: ['lex:app.bsky.feed.postgate#disableRule'],
6724
+ },
6725
+ },
6726
+ },
6727
+ },
6728
+ },
6729
+ disableRule: {
6730
+ type: 'object',
6731
+ description: 'Disables embedding of this post.',
6732
+ properties: {},
6733
+ },
6734
+ },
6735
+ },
6599
6736
  AppBskyFeedRepost: {
6600
6737
  lexicon: 1,
6601
6738
  id: 'app.bsky.feed.repost',
@@ -6807,6 +6944,15 @@ export const schemaDict = {
6807
6944
  type: 'string',
6808
6945
  format: 'datetime',
6809
6946
  },
6947
+ hiddenReplies: {
6948
+ type: 'array',
6949
+ maxLength: 50,
6950
+ items: {
6951
+ type: 'string',
6952
+ format: 'at-uri',
6953
+ },
6954
+ description: 'List of hidden reply URIs.',
6955
+ },
6810
6956
  },
6811
6957
  },
6812
6958
  },
@@ -10074,11 +10220,13 @@ export const ids = {
10074
10220
  AppBskyFeedGetListFeed: 'app.bsky.feed.getListFeed',
10075
10221
  AppBskyFeedGetPostThread: 'app.bsky.feed.getPostThread',
10076
10222
  AppBskyFeedGetPosts: 'app.bsky.feed.getPosts',
10223
+ AppBskyFeedGetQuotes: 'app.bsky.feed.getQuotes',
10077
10224
  AppBskyFeedGetRepostedBy: 'app.bsky.feed.getRepostedBy',
10078
10225
  AppBskyFeedGetSuggestedFeeds: 'app.bsky.feed.getSuggestedFeeds',
10079
10226
  AppBskyFeedGetTimeline: 'app.bsky.feed.getTimeline',
10080
10227
  AppBskyFeedLike: 'app.bsky.feed.like',
10081
10228
  AppBskyFeedPost: 'app.bsky.feed.post',
10229
+ AppBskyFeedPostgate: 'app.bsky.feed.postgate',
10082
10230
  AppBskyFeedRepost: 'app.bsky.feed.repost',
10083
10231
  AppBskyFeedSearchPosts: 'app.bsky.feed.searchPosts',
10084
10232
  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
@@ -66,6 +67,7 @@ export interface ViewRecord {
66
67
  replyCount?: number
67
68
  repostCount?: number
68
69
  likeCount?: number
70
+ quoteCount?: number
69
71
  embeds?: (
70
72
  | AppBskyEmbedImages.View
71
73
  | AppBskyEmbedExternal.View
@@ -125,3 +127,21 @@ export function isViewBlocked(v: unknown): v is ViewBlocked {
125
127
  export function validateViewBlocked(v: unknown): ValidationResult {
126
128
  return lexicons.validate('app.bsky.embed.record#viewBlocked', v)
127
129
  }
130
+
131
+ export interface ViewDetached {
132
+ uri: string
133
+ detached: true
134
+ [k: string]: unknown
135
+ }
136
+
137
+ export function isViewDetached(v: unknown): v is ViewDetached {
138
+ return (
139
+ isObj(v) &&
140
+ hasProp(v, '$type') &&
141
+ v.$type === 'app.bsky.embed.record#viewDetached'
142
+ )
143
+ }
144
+
145
+ export function validateViewDetached(v: unknown): ValidationResult {
146
+ return lexicons.validate('app.bsky.embed.record#viewDetached', v)
147
+ }