@atproto/bsky 0.0.151 → 0.0.152

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 (60) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +4 -0
  3. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -0
  4. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.js +76 -0
  5. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -0
  6. package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts +4 -0
  7. package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -0
  8. package/dist/api/app/bsky/unspecced/getPostThreadV2.js +86 -0
  9. package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -0
  10. package/dist/api/index.d.ts.map +1 -1
  11. package/dist/api/index.js +4 -0
  12. package/dist/api/index.js.map +1 -1
  13. package/dist/config.d.ts +2 -0
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/config.js +7 -0
  16. package/dist/config.js.map +1 -1
  17. package/dist/hydration/feed.d.ts.map +1 -1
  18. package/dist/hydration/feed.js.map +1 -1
  19. package/dist/lexicon/index.d.ts +6 -2
  20. package/dist/lexicon/index.d.ts.map +1 -1
  21. package/dist/lexicon/index.js +12 -4
  22. package/dist/lexicon/index.js.map +1 -1
  23. package/dist/lexicon/lexicons.d.ts +498 -82
  24. package/dist/lexicon/lexicons.d.ts.map +1 -1
  25. package/dist/lexicon/lexicons.js +259 -42
  26. package/dist/lexicon/lexicons.js.map +1 -1
  27. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +61 -0
  28. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -0
  29. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js +25 -0
  30. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -0
  31. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts +92 -0
  32. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -0
  33. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js +52 -0
  34. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js.map +1 -0
  35. package/dist/views/index.d.ts +35 -0
  36. package/dist/views/index.d.ts.map +1 -1
  37. package/dist/views/index.js +463 -0
  38. package/dist/views/index.js.map +1 -1
  39. package/dist/views/threads-v2.d.ts +62 -0
  40. package/dist/views/threads-v2.d.ts.map +1 -0
  41. package/dist/views/threads-v2.js +180 -0
  42. package/dist/views/threads-v2.js.map +1 -0
  43. package/package.json +4 -4
  44. package/src/api/app/bsky/unspecced/getPostThreadHiddenV2.ts +116 -0
  45. package/src/api/app/bsky/unspecced/getPostThreadV2.ts +130 -0
  46. package/src/api/index.ts +4 -0
  47. package/src/config.ts +9 -0
  48. package/src/hydration/feed.ts +1 -0
  49. package/src/lexicon/index.ts +33 -9
  50. package/src/lexicon/lexicons.ts +278 -43
  51. package/src/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.ts +93 -0
  52. package/src/lexicon/types/app/bsky/unspecced/getPostThreadV2.ts +160 -0
  53. package/src/views/index.ts +742 -0
  54. package/src/views/threads-v2.ts +351 -0
  55. package/tests/seed/thread-v2.ts +775 -0
  56. package/tests/seed/util.ts +52 -0
  57. package/tests/views/__snapshots__/thread-v2.test.ts.snap +1091 -0
  58. package/tests/views/thread-v2.test.ts +2009 -0
  59. package/tsconfig.build.tsbuildinfo +1 -1
  60. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -0,0 +1,775 @@
1
+ import { AppBskyFeedPost } from '@atproto/api'
2
+ import {
3
+ RecordRef,
4
+ SeedClient,
5
+ TestNetwork,
6
+ TestNetworkNoAppView,
7
+ } from '@atproto/dev-env'
8
+ import { User, createUsers } from './util'
9
+
10
+ type ReplyFn = (
11
+ replyAuthor: User,
12
+ overridesOrCb?: Partial<AppBskyFeedPost.Record> | ReplyCb,
13
+ maybeReplyCb?: ReplyCb,
14
+ ) => Promise<void>
15
+
16
+ type ReplyCb = (r: ReplyFn) => Promise<void>
17
+
18
+ const rootReplyFnBuilder = <T extends TestNetworkNoAppView>(
19
+ sc: SeedClient<T>,
20
+ root: RecordRef,
21
+ parent: RecordRef,
22
+ prevBreadcrumbs: string,
23
+ posts: Record<
24
+ string,
25
+ | Awaited<ReturnType<SeedClient['post']>>
26
+ | Awaited<ReturnType<SeedClient['reply']>>
27
+ >,
28
+ ): ReplyFn => {
29
+ let index = 0
30
+ return async (
31
+ replyAuthor: User,
32
+ overridesOrCb?: Partial<AppBskyFeedPost.Record> | ReplyCb,
33
+ maybeReplyCb?: ReplyCb,
34
+ ) => {
35
+ let overrides: Partial<AppBskyFeedPost.Record> | undefined
36
+ let replyCb: ReplyCb | undefined
37
+ if (overridesOrCb && typeof overridesOrCb === 'function') {
38
+ replyCb = overridesOrCb
39
+ } else {
40
+ overrides = overridesOrCb
41
+ replyCb = maybeReplyCb
42
+ }
43
+
44
+ const breadcrumbs = prevBreadcrumbs
45
+ ? `${prevBreadcrumbs}.${index++}`
46
+ : `${index++}`
47
+ const text = breadcrumbs
48
+ const reply = await sc.reply(
49
+ replyAuthor.did,
50
+ root,
51
+ parent,
52
+ text,
53
+ undefined,
54
+ undefined,
55
+ overrides,
56
+ )
57
+ posts[breadcrumbs] = reply
58
+ // Await for this post to be processed before replying to it.
59
+ replyCb && (await sc.network.processAll())
60
+ await replyCb?.(rootReplyFnBuilder(sc, root, reply.ref, breadcrumbs, posts))
61
+ }
62
+ }
63
+
64
+ const createThread = async <T extends TestNetworkNoAppView>(
65
+ sc: SeedClient<T>,
66
+ rootAuthor: User,
67
+ overridesOrCb?: Partial<AppBskyFeedPost.Record> | ReplyCb,
68
+ maybeReplyCb?: ReplyCb,
69
+ ) => {
70
+ let overrides: Partial<AppBskyFeedPost.Record> | undefined
71
+ let replyCb: ReplyCb | undefined
72
+ if (overridesOrCb && typeof overridesOrCb === 'function') {
73
+ replyCb = overridesOrCb
74
+ } else {
75
+ overrides = overridesOrCb
76
+ replyCb = maybeReplyCb
77
+ }
78
+
79
+ const replies: Record<string, Awaited<ReturnType<SeedClient['reply']>>> = {}
80
+ const breadcrumbs = ''
81
+ const text = 'root'
82
+ const root = await sc.post(
83
+ rootAuthor.did,
84
+ text,
85
+ undefined,
86
+ undefined,
87
+ undefined,
88
+ overrides,
89
+ )
90
+ // Await for this post to be processed before replying to it.
91
+ replyCb && (await sc.network.processAll())
92
+ await replyCb?.(
93
+ rootReplyFnBuilder(sc, root.ref, root.ref, breadcrumbs, replies),
94
+ )
95
+ return { root, replies }
96
+ }
97
+
98
+ export async function simple(
99
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
100
+ prefix = 'simple',
101
+ ) {
102
+ const users = await createUsers(sc, prefix, [
103
+ 'op',
104
+ 'alice',
105
+ 'bob',
106
+ 'carol',
107
+ ] as const)
108
+ const { op, alice, bob, carol } = users
109
+
110
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
111
+ await r(op, async (r) => {
112
+ await r(op)
113
+ })
114
+ await r(alice)
115
+ await r(bob, async (r) => {
116
+ await r(alice)
117
+ })
118
+ await r(carol)
119
+ })
120
+
121
+ return {
122
+ seedClient: sc,
123
+ users,
124
+ root,
125
+ r,
126
+ }
127
+ }
128
+
129
+ export async function long(sc: SeedClient<TestNetwork | TestNetworkNoAppView>) {
130
+ const users = await createUsers(sc, 'long', [
131
+ 'op',
132
+ 'alice',
133
+ 'bob',
134
+ 'carol',
135
+ 'dan',
136
+ ] as const)
137
+ const { op, alice, bob, carol, dan } = users
138
+
139
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
140
+ await r(op, async (r) => {
141
+ await r(op, async (r) => {
142
+ await r(op, async (r) => {
143
+ await r(op, async (r) => {
144
+ await r(op)
145
+ })
146
+ })
147
+ await r(op)
148
+ })
149
+ })
150
+
151
+ await r(alice)
152
+ await r(bob)
153
+ await r(carol)
154
+
155
+ await r(op, async (r) => {
156
+ await r(op, async (r) => {
157
+ await r(alice, async (r) => {
158
+ await r(op, async (r) => {
159
+ await r(op)
160
+ })
161
+ })
162
+ })
163
+ })
164
+
165
+ await r(alice)
166
+ await r(bob)
167
+ await r(carol)
168
+ })
169
+
170
+ await sc.like(op.did, r['5'].ref)
171
+ await sc.like(bob.did, r['5'].ref)
172
+ await sc.like(carol.did, r['5'].ref)
173
+ await sc.like(dan.did, r['5'].ref)
174
+
175
+ await sc.like(op.did, r['6'].ref)
176
+ await sc.like(alice.did, r['6'].ref)
177
+ await sc.like(carol.did, r['6'].ref)
178
+
179
+ await sc.like(op.did, r['7'].ref)
180
+ await sc.like(bob.did, r['7'].ref)
181
+
182
+ return {
183
+ seedClient: sc,
184
+ users,
185
+ root,
186
+ r,
187
+ }
188
+ }
189
+
190
+ export async function deep(sc: SeedClient<TestNetwork | TestNetworkNoAppView>) {
191
+ const users = await createUsers(sc, 'deep', ['op'] as const)
192
+ const { op } = users
193
+
194
+ let counter = 0
195
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
196
+ const recursiveReply = async (rFn: ReplyFn) => {
197
+ if (counter < 18) {
198
+ counter++
199
+ await rFn(op, async (r) => recursiveReply(r))
200
+ }
201
+ }
202
+ await recursiveReply(r)
203
+ })
204
+
205
+ return {
206
+ seedClient: sc,
207
+ users,
208
+ root,
209
+ r,
210
+ }
211
+ }
212
+
213
+ export async function branchingFactor(
214
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
215
+ ) {
216
+ const users = await createUsers(sc, 'bf', ['op', 'bob'] as const)
217
+ const { op, bob } = users
218
+
219
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
220
+ await r(bob, async (r) => {
221
+ await r(bob, async (r) => {
222
+ await r(bob)
223
+ await r(bob)
224
+ await r(bob)
225
+ await r(bob)
226
+ })
227
+ await r(bob, async (r) => {
228
+ await r(bob)
229
+ await r(bob)
230
+ await r(bob)
231
+ await r(bob)
232
+ })
233
+ await r(bob, async (r) => {
234
+ await r(bob)
235
+ await r(bob)
236
+ await r(bob)
237
+ await r(bob)
238
+ })
239
+ await r(bob, async (r) => {
240
+ await r(bob)
241
+ await r(bob)
242
+ await r(bob)
243
+ await r(bob)
244
+ })
245
+ })
246
+ await r(bob, async (r) => {
247
+ await r(bob, async (r) => {
248
+ // This is the only case in this seed where a reply has 1 reply instead of 4,
249
+ // to have cases of different lengths in the same tree.
250
+ await r(bob)
251
+ })
252
+ await r(bob, async (r) => {
253
+ await r(bob)
254
+ await r(bob)
255
+ await r(bob)
256
+ await r(bob)
257
+ })
258
+ await r(bob, async (r) => {
259
+ await r(bob)
260
+ await r(bob)
261
+ await r(bob)
262
+ await r(bob)
263
+ })
264
+ await r(bob, async (r) => {
265
+ await r(bob)
266
+ await r(bob)
267
+ await r(bob)
268
+ await r(bob)
269
+ })
270
+ })
271
+ await r(bob, async (r) => {
272
+ await r(bob, async (r) => {
273
+ await r(bob)
274
+ await r(bob)
275
+ await r(bob)
276
+ await r(bob)
277
+ })
278
+ await r(bob, async (r) => {
279
+ await r(bob)
280
+ await r(bob)
281
+ await r(bob)
282
+ await r(bob)
283
+ })
284
+ await r(bob, async (r) => {
285
+ await r(bob)
286
+ await r(bob)
287
+ await r(bob)
288
+ await r(bob)
289
+ })
290
+ await r(bob, async (r) => {
291
+ await r(bob)
292
+ await r(bob)
293
+ await r(bob)
294
+ await r(bob)
295
+ })
296
+ })
297
+ await r(bob, async (r) => {
298
+ await r(bob, async (r) => {
299
+ await r(bob)
300
+ await r(bob)
301
+ await r(bob)
302
+ await r(bob)
303
+ // This is the only case in this seed where a reply has 5 replies instead of 4,
304
+ // to have cases of different lengths in the same tree.
305
+ await r(bob)
306
+ })
307
+ await r(bob, async (r) => {
308
+ await r(bob)
309
+ await r(bob)
310
+ await r(bob)
311
+ await r(bob)
312
+ })
313
+ await r(bob, async (r) => {
314
+ await r(bob)
315
+ await r(bob)
316
+ await r(bob)
317
+ await r(bob)
318
+ })
319
+ await r(bob, async (r) => {
320
+ await r(bob)
321
+ await r(bob)
322
+ await r(bob)
323
+ await r(bob)
324
+ })
325
+ })
326
+ })
327
+
328
+ return {
329
+ seedClient: sc,
330
+ users,
331
+ root,
332
+ r,
333
+ }
334
+ }
335
+
336
+ export async function annotateMoreReplies(
337
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
338
+ ) {
339
+ const users = await createUsers(sc, 'mr', ['op', 'alice'] as const)
340
+ const { op, alice } = users
341
+
342
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
343
+ await r(alice, async (r) => {
344
+ await r(alice, async (r) => {
345
+ await r(alice, async (r) => {
346
+ await r(alice, async (r) => {
347
+ // more replies... (below = 4)
348
+ await r(alice, async (r) => {
349
+ await r(alice)
350
+ })
351
+ await r(alice)
352
+ await r(alice, async (r) => {
353
+ await r(alice, async (r) => {
354
+ await r(alice)
355
+ })
356
+ })
357
+ await r(alice)
358
+ await r(alice)
359
+ })
360
+ })
361
+ })
362
+ await r(alice, async (r) => {
363
+ await r(alice, async (r) => {
364
+ await r(alice)
365
+ })
366
+ })
367
+ })
368
+ await r(alice, async (r) => {
369
+ await r(alice, async (r) => {
370
+ await r(alice)
371
+ await r(alice)
372
+ // more replies... (branchingFactor = 2)
373
+ await r(alice)
374
+ await r(alice)
375
+ await r(alice)
376
+ })
377
+ await r(alice, async (r) => {
378
+ await r(alice)
379
+ await r(alice)
380
+ })
381
+ // more replies... (branchingFactor = 2)
382
+ await r(alice)
383
+ })
384
+ await r(alice) // anchor reply not limited by branchingFactor
385
+ })
386
+
387
+ return {
388
+ seedClient: sc,
389
+ users,
390
+ root,
391
+ r,
392
+ }
393
+ }
394
+
395
+ export async function annotateOP(
396
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
397
+ ) {
398
+ const users = await createUsers(sc, 'op', ['op', 'alice', 'bob'] as const)
399
+ const { op, alice, bob } = users
400
+
401
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
402
+ await r(op, async (r) => {
403
+ await r(op, async (r) => {
404
+ await r(op)
405
+ })
406
+ })
407
+ await r(alice, async (r) => {
408
+ await r(alice)
409
+ })
410
+ await r(op, async (r) => {
411
+ await r(bob, async (r) => {
412
+ await r(op)
413
+ })
414
+ })
415
+ })
416
+
417
+ return {
418
+ seedClient: sc,
419
+ users,
420
+ root,
421
+ r,
422
+ }
423
+ }
424
+
425
+ export async function sort(sc: SeedClient<TestNetwork | TestNetworkNoAppView>) {
426
+ const users = await createUsers(sc, 'sort', [
427
+ 'op',
428
+ 'alice',
429
+ 'bob',
430
+ 'carol',
431
+ ] as const)
432
+ const { op, alice, bob, carol } = users
433
+
434
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
435
+ // 0 likes
436
+ await r(alice, async (r) => {
437
+ await r(carol) // 0 likes
438
+ await r(alice) // 2 likes
439
+ await r(bob) // 1 like
440
+ })
441
+ // 3 likes
442
+ await r(carol, async (r) => {
443
+ await r(bob) // 1 like
444
+ await r(carol) // 2 likes
445
+ await r(alice) // 0 likes
446
+ })
447
+ // 2 likes
448
+ await r(bob, async (r) => {
449
+ await r(bob) // 2 likes
450
+ await r(alice) // 1 like
451
+ await r(carol) // 0 likes
452
+ })
453
+ })
454
+
455
+ // likes depth 1
456
+ await sc.like(alice.did, r['2'].ref)
457
+ await sc.like(carol.did, r['2'].ref)
458
+ await sc.like(op.did, r['1'].ref) // op like
459
+ await sc.like(bob.did, r['1'].ref)
460
+ await sc.like(carol.did, r['1'].ref)
461
+
462
+ // likes depth 2
463
+ await sc.like(bob.did, r['0.1'].ref)
464
+ await sc.like(carol.did, r['0.1'].ref)
465
+ await sc.like(op.did, r['0.2'].ref) // op like
466
+ await sc.like(bob.did, r['1.1'].ref)
467
+ await sc.like(carol.did, r['1.1'].ref)
468
+ await sc.like(bob.did, r['1.0'].ref)
469
+ await sc.like(bob.did, r['2.0'].ref)
470
+ await sc.like(carol.did, r['2.0'].ref)
471
+ await sc.like(bob.did, r['2.1'].ref)
472
+
473
+ return {
474
+ seedClient: sc,
475
+ users,
476
+ root,
477
+ r,
478
+ }
479
+ }
480
+
481
+ export async function bumpOpAndViewer(
482
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
483
+ ) {
484
+ const users = await createUsers(sc, 'bumpOV', [
485
+ 'op',
486
+ 'viewer',
487
+ 'alice',
488
+ 'bob',
489
+ 'carol',
490
+ ] as const)
491
+ const { op, viewer, alice, bob, carol } = users
492
+
493
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
494
+ // 1 like
495
+ await r(alice, async (r) => {
496
+ await r(carol) // 0 likes
497
+ await r(alice) // 2 likes
498
+ await r(bob) // 1 like
499
+ await r(viewer) // 0 likes
500
+ await r(op) // 0 likes
501
+ })
502
+ // 3 likes
503
+ await r(carol, async (r) => {
504
+ await r(bob) // 1 like
505
+ await r(carol) // 2 likes
506
+ await r(op) // 0 likes
507
+ await r(viewer) // 1 like
508
+ await r(alice) // 0 likes
509
+ })
510
+ // 2 likes
511
+ await r(bob, async (r) => {
512
+ await r(viewer) // 0 likes
513
+ await r(bob) // 4 likes
514
+ await r(op) // 0 likes
515
+ await r(alice) // 1 like
516
+ await r(carol) // 1 like
517
+ })
518
+ // 0 likes
519
+ await r(op, async (r) => {
520
+ await r(viewer) // 0 likes
521
+ await r(bob) // 0 likes
522
+ await r(op) // 0 likes
523
+ await r(alice) // 0 likes
524
+ await r(carol) // 0 likes
525
+ })
526
+ // 0 likes
527
+ await r(viewer, async (r) => {
528
+ await r(bob) // 1 like
529
+ await r(carol) // 1 like
530
+ await r(op) // 0 likes
531
+ await r(viewer) // 0 likes
532
+ await r(alice) // 0 likes
533
+ })
534
+ })
535
+
536
+ // likes depth 1
537
+ await sc.like(alice.did, r['2'].ref)
538
+ await sc.like(carol.did, r['2'].ref)
539
+ await sc.like(viewer.did, r['0'].ref)
540
+ await sc.like(op.did, r['1'].ref) // op like
541
+ await sc.like(bob.did, r['1'].ref)
542
+ await sc.like(carol.did, r['1'].ref)
543
+
544
+ // likes depth 2
545
+ await sc.like(bob.did, r['0.1'].ref)
546
+ await sc.like(carol.did, r['0.1'].ref)
547
+ await sc.like(op.did, r['0.2'].ref) // op like
548
+ await sc.like(bob.did, r['1.1'].ref)
549
+ await sc.like(carol.did, r['1.1'].ref)
550
+ await sc.like(bob.did, r['1.0'].ref)
551
+ await sc.like(alice.did, r['2.1'].ref)
552
+ await sc.like(bob.did, r['2.1'].ref)
553
+ await sc.like(carol.did, r['2.1'].ref)
554
+ await sc.like(viewer.did, r['2.1'].ref)
555
+ await sc.like(bob.did, r['1.3'].ref)
556
+ await sc.like(bob.did, r['2.3'].ref)
557
+ await sc.like(viewer.did, r['2.4'].ref)
558
+ await sc.like(viewer.did, r['4.0'].ref)
559
+ await sc.like(alice.did, r['4.1'].ref)
560
+
561
+ return {
562
+ seedClient: sc,
563
+ users,
564
+ root,
565
+ r,
566
+ }
567
+ }
568
+
569
+ export async function bumpGroupSorting(
570
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
571
+ ) {
572
+ const users = await createUsers(sc, 'bumpGS', [
573
+ 'op',
574
+ 'viewer',
575
+ 'alice',
576
+ ] as const)
577
+ const { op, viewer, alice } = users
578
+
579
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
580
+ await r(viewer)
581
+ await r(op)
582
+ await r(alice)
583
+ await r(op)
584
+ await r(viewer)
585
+ await r(op)
586
+ await r(alice)
587
+ await r(viewer)
588
+ })
589
+
590
+ return {
591
+ seedClient: sc,
592
+ users,
593
+ root,
594
+ r,
595
+ }
596
+ }
597
+
598
+ export async function bumpFollows(
599
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
600
+ ) {
601
+ const users = await createUsers(sc, 'bumpF', [
602
+ 'op',
603
+ 'viewerF',
604
+ 'viewerNoF',
605
+ 'alice',
606
+ 'bob',
607
+ 'carol',
608
+ ] as const)
609
+
610
+ const { op, viewerF, viewerNoF, alice, bob, carol } = users
611
+
612
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
613
+ await r(alice)
614
+ await r(bob)
615
+ await r(carol)
616
+ await r(op)
617
+ await r(viewerF)
618
+ await r(viewerNoF)
619
+ })
620
+
621
+ await sc.follow(viewerF.did, alice.did)
622
+ await sc.follow(viewerF.did, bob.did)
623
+ // Does not follow carol.
624
+
625
+ return {
626
+ seedClient: sc,
627
+ users,
628
+ root,
629
+ r,
630
+ }
631
+ }
632
+
633
+ export async function blockDeletionAuth(
634
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
635
+ ) {
636
+ const users = await createUsers(sc, 'bda', [
637
+ 'op',
638
+ 'opBlocked',
639
+ 'alice',
640
+ 'auth',
641
+ 'blocker',
642
+ 'blocked',
643
+ ] as const)
644
+
645
+ const { op, opBlocked, alice, auth, blocker, blocked } = users
646
+
647
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
648
+ // 1p block, hidden for `blocked`.
649
+ await r(blocker, async (r) => {
650
+ await r(alice)
651
+ })
652
+
653
+ // 3p block, hidden for all.
654
+ await r(opBlocked, async (r) => {
655
+ await r(op)
656
+ await r(alice)
657
+ })
658
+
659
+ // Deleted, hidden for all.
660
+ await r(alice, async (r) => {
661
+ await r(alice)
662
+ })
663
+
664
+ // User configured to only be seend by authenticated users.
665
+ // Requires the test sets a `!no-unauthenticated` label for this user.
666
+ await r(auth, async (r) => {
667
+ // Another auth-only to show that the parent chain is preserved in the thread.
668
+ await r(auth, async (r) => {
669
+ await r(alice)
670
+ })
671
+ })
672
+ })
673
+
674
+ await sc.deletePost(alice.did, r['2'].ref.uri)
675
+ await sc.block(blocker.did, blocked.did)
676
+ await sc.block(op.did, opBlocked.did)
677
+
678
+ return {
679
+ seedClient: sc,
680
+ users,
681
+ root,
682
+ r,
683
+ }
684
+ }
685
+
686
+ export async function mutes(
687
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
688
+ ) {
689
+ const users = await createUsers(sc, 'mutes', [
690
+ 'op',
691
+ 'opMuted',
692
+ 'alice',
693
+ 'muted',
694
+ 'muter',
695
+ ] as const)
696
+
697
+ const { op, opMuted, alice, muted, muter } = users
698
+
699
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
700
+ await r(opMuted, async (r) => {
701
+ await r(alice)
702
+ await r(muted)
703
+ })
704
+
705
+ await r(muted, async (r) => {
706
+ await r(opMuted)
707
+ await r(alice)
708
+ })
709
+ })
710
+
711
+ await sc.mute(op.did, opMuted.did)
712
+ await sc.mute(muter.did, muted.did)
713
+
714
+ return {
715
+ seedClient: sc,
716
+ users,
717
+ root,
718
+ r,
719
+ }
720
+ }
721
+
722
+ export async function threadgated(
723
+ sc: SeedClient<TestNetwork | TestNetworkNoAppView>,
724
+ ) {
725
+ const users = await createUsers(sc, 'tg', [
726
+ 'op',
727
+ 'opMuted',
728
+ 'viewer',
729
+ 'alice',
730
+ 'bob',
731
+ ] as const)
732
+
733
+ const { op, opMuted, alice, bob } = users
734
+
735
+ const { root, replies: r } = await createThread(sc, op, async (r) => {
736
+ // Muted moves down below threadgated.
737
+ await r(opMuted)
738
+
739
+ // Threadgated moves down.
740
+ await r(alice, async (r) => {
741
+ await r(alice)
742
+ await r(bob)
743
+ await r(op) // OP moves up.
744
+ })
745
+
746
+ await r(bob, async (r) => {
747
+ await r(alice)
748
+ await r(bob) // Threadgated is omitted if fetched from the root.
749
+ await r(op) // OP moves down.
750
+ })
751
+ })
752
+
753
+ await sc.agent.app.bsky.feed.threadgate.create(
754
+ {
755
+ repo: op.did,
756
+ rkey: root.ref.uri.rkey,
757
+ },
758
+ {
759
+ post: root.ref.uriStr,
760
+ createdAt: new Date().toISOString(),
761
+ hiddenReplies: [r['1'].ref.uriStr, r['2.1'].ref.uriStr],
762
+ },
763
+ sc.getHeaders(op.did),
764
+ )
765
+
766
+ // Just throw a mute there to test the prioritization between muted and threadgated.
767
+ await sc.mute(op.did, opMuted.did)
768
+
769
+ return {
770
+ seedClient: sc,
771
+ users,
772
+ root,
773
+ r,
774
+ }
775
+ }