@atproto/bsky 0.0.249 → 0.0.251

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 (84) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/api/age-assurance/const.d.ts.map +1 -1
  3. package/dist/api/age-assurance/const.js +19 -0
  4. package/dist/api/age-assurance/const.js.map +1 -1
  5. package/dist/api/age-assurance/kws/age-verified.d.ts +20 -0
  6. package/dist/api/age-assurance/kws/age-verified.d.ts.map +1 -1
  7. package/dist/api/age-assurance/kws/age-verified.js +6 -0
  8. package/dist/api/age-assurance/kws/age-verified.js.map +1 -1
  9. package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
  10. package/dist/api/app/bsky/feed/getFeed.js +4 -0
  11. package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
  12. package/dist/api/app/bsky/feed/searchPosts.js +5 -0
  13. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  14. package/dist/api/app/bsky/feed/searchPostsV2.js +5 -0
  15. package/dist/api/app/bsky/feed/searchPostsV2.js.map +1 -1
  16. package/dist/api/app/bsky/graph/muteActor.d.ts.map +1 -1
  17. package/dist/api/app/bsky/graph/muteActor.js +3 -0
  18. package/dist/api/app/bsky/graph/muteActor.js.map +1 -1
  19. package/dist/api/kws/types.d.ts +33 -0
  20. package/dist/api/kws/types.d.ts.map +1 -1
  21. package/dist/api/kws/types.js +3 -0
  22. package/dist/api/kws/types.js.map +1 -1
  23. package/dist/config.d.ts +2 -0
  24. package/dist/config.d.ts.map +1 -1
  25. package/dist/config.js +5 -0
  26. package/dist/config.js.map +1 -1
  27. package/dist/data-plane/index.d.ts +0 -1
  28. package/dist/data-plane/index.d.ts.map +1 -1
  29. package/dist/data-plane/index.js +0 -1
  30. package/dist/data-plane/index.js.map +1 -1
  31. package/dist/data-plane/server/bsync-subscription.d.ts +47 -0
  32. package/dist/data-plane/server/bsync-subscription.d.ts.map +1 -0
  33. package/dist/data-plane/server/bsync-subscription.js +461 -0
  34. package/dist/data-plane/server/bsync-subscription.js.map +1 -0
  35. package/dist/data-plane/server/index.d.ts +1 -0
  36. package/dist/data-plane/server/index.d.ts.map +1 -1
  37. package/dist/data-plane/server/index.js +1 -0
  38. package/dist/data-plane/server/index.js.map +1 -1
  39. package/dist/hydration/feed.d.ts +1 -2
  40. package/dist/hydration/feed.d.ts.map +1 -1
  41. package/dist/hydration/feed.js.map +1 -1
  42. package/dist/lexicons/app/bsky/ageassurance/defs.defs.d.ts +4 -0
  43. package/dist/lexicons/app/bsky/ageassurance/defs.defs.d.ts.map +1 -1
  44. package/dist/lexicons/app/bsky/ageassurance/defs.defs.js +3 -0
  45. package/dist/lexicons/app/bsky/ageassurance/defs.defs.js.map +1 -1
  46. package/dist/lexicons/chat/bsky/convo/defs.defs.js +1 -1
  47. package/dist/lexicons/chat/bsky/convo/defs.defs.js.map +1 -1
  48. package/dist/lexicons/chat/bsky/group/editGroup.defs.d.ts +4 -4
  49. package/dist/lexicons/chat/bsky/group/editGroup.defs.js +2 -2
  50. package/dist/lexicons/chat/bsky/group/editGroup.defs.js.map +1 -1
  51. package/dist/lexicons/chat/bsky/moderation/defs.defs.js +1 -1
  52. package/dist/lexicons/chat/bsky/moderation/defs.defs.js.map +1 -1
  53. package/package.json +10 -10
  54. package/src/api/age-assurance/const.ts +19 -0
  55. package/src/api/age-assurance/kws/age-verified.test.ts +98 -0
  56. package/src/api/age-assurance/kws/age-verified.ts +6 -0
  57. package/src/api/app/bsky/feed/getFeed.ts +5 -0
  58. package/src/api/app/bsky/feed/searchPosts.ts +7 -0
  59. package/src/api/app/bsky/feed/searchPostsV2.ts +7 -0
  60. package/src/api/app/bsky/graph/muteActor.ts +3 -0
  61. package/src/api/kws/types.ts +11 -0
  62. package/src/api/kws/util.test.ts +50 -0
  63. package/src/config.ts +9 -0
  64. package/src/data-plane/index.ts +0 -1
  65. package/src/data-plane/{bsync/index.ts → server/bsync-subscription.ts} +245 -121
  66. package/src/data-plane/server/index.ts +1 -0
  67. package/src/hydration/feed.ts +1 -2
  68. package/tests/__snapshots__/feed-generation.test.ts.snap +62 -15
  69. package/tests/data-plane/thread-mutes.test.ts +2 -0
  70. package/tests/feed-generation.test.ts +55 -9
  71. package/tests/stash.test.ts +6 -1
  72. package/tests/views/age-assurance-v2.test.ts +3 -0
  73. package/tests/views/age-assurance.test.ts +8 -0
  74. package/tests/views/bookmarks.test.ts +19 -0
  75. package/tests/views/drafts.test.ts +25 -2
  76. package/tests/views/mute-lists.test.ts +2 -0
  77. package/tests/views/mutes.test.ts +1 -0
  78. package/tests/views/notifications.test.ts +16 -0
  79. package/tests/views/post-search.test.ts +81 -0
  80. package/tsconfig.build.tsbuildinfo +1 -1
  81. package/dist/data-plane/bsync/index.d.ts +0 -11
  82. package/dist/data-plane/bsync/index.d.ts.map +0 -1
  83. package/dist/data-plane/bsync/index.js +0 -376
  84. package/dist/data-plane/bsync/index.js.map +0 -1
@@ -366,7 +366,6 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
366
366
  "viewer": {},
367
367
  },
368
368
  {
369
- "acceptsInteractions": true,
370
369
  "cid": "cids(3)",
371
370
  "creator": {
372
371
  "associated": {
@@ -404,9 +403,9 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
404
403
  "muted": false,
405
404
  },
406
405
  },
407
- "description": "Has acceptsInteractions set to true",
406
+ "description": "Returns a post with skeletonReasonPin",
408
407
  "did": "user(0)",
409
- "displayName": "Accepts Interactions",
408
+ "displayName": "Pinned",
410
409
  "indexedAt": "1970-01-01T00:00:00.000Z",
411
410
  "labels": [],
412
411
  "likeCount": 0,
@@ -414,7 +413,55 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
414
413
  "viewer": {},
415
414
  },
416
415
  {
416
+ "acceptsInteractions": true,
417
417
  "cid": "cids(4)",
418
+ "creator": {
419
+ "associated": {
420
+ "activitySubscription": {
421
+ "allowSubscriptions": "followers",
422
+ },
423
+ },
424
+ "avatar": "https://bsky.public.url/img/avatar/plain/user(2)/cids(1)",
425
+ "createdAt": "1970-01-01T00:00:00.000Z",
426
+ "description": "its me!",
427
+ "did": "user(1)",
428
+ "displayName": "ali",
429
+ "handle": "alice.test",
430
+ "indexedAt": "1970-01-01T00:00:00.000Z",
431
+ "labels": [
432
+ {
433
+ "cid": "cids(2)",
434
+ "cts": "1970-01-01T00:00:00.000Z",
435
+ "src": "user(1)",
436
+ "uri": "record(3)",
437
+ "val": "self-label-a",
438
+ },
439
+ {
440
+ "cid": "cids(2)",
441
+ "cts": "1970-01-01T00:00:00.000Z",
442
+ "src": "user(1)",
443
+ "uri": "record(3)",
444
+ "val": "self-label-b",
445
+ },
446
+ ],
447
+ "viewer": {
448
+ "blockedBy": false,
449
+ "followedBy": "record(2)",
450
+ "following": "record(1)",
451
+ "muted": false,
452
+ },
453
+ },
454
+ "description": "Has acceptsInteractions set to true",
455
+ "did": "user(0)",
456
+ "displayName": "Accepts Interactions",
457
+ "indexedAt": "1970-01-01T00:00:00.000Z",
458
+ "labels": [],
459
+ "likeCount": 0,
460
+ "uri": "record(5)",
461
+ "viewer": {},
462
+ },
463
+ {
464
+ "cid": "cids(5)",
418
465
  "contentMode": "app.bsky.feed.defs#contentModeVideo",
419
466
  "creator": {
420
467
  "associated": {
@@ -458,11 +505,11 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
458
505
  "indexedAt": "1970-01-01T00:00:00.000Z",
459
506
  "labels": [],
460
507
  "likeCount": 0,
461
- "uri": "record(5)",
508
+ "uri": "record(6)",
462
509
  "viewer": {},
463
510
  },
464
511
  {
465
- "cid": "cids(5)",
512
+ "cid": "cids(6)",
466
513
  "creator": {
467
514
  "associated": {
468
515
  "activitySubscription": {
@@ -505,11 +552,11 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
505
552
  "indexedAt": "1970-01-01T00:00:00.000Z",
506
553
  "labels": [],
507
554
  "likeCount": 0,
508
- "uri": "record(6)",
555
+ "uri": "record(7)",
509
556
  "viewer": {},
510
557
  },
511
558
  {
512
- "cid": "cids(6)",
559
+ "cid": "cids(7)",
513
560
  "creator": {
514
561
  "associated": {
515
562
  "activitySubscription": {
@@ -552,11 +599,11 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
552
599
  "indexedAt": "1970-01-01T00:00:00.000Z",
553
600
  "labels": [],
554
601
  "likeCount": 0,
555
- "uri": "record(7)",
602
+ "uri": "record(8)",
556
603
  "viewer": {},
557
604
  },
558
605
  {
559
- "cid": "cids(7)",
606
+ "cid": "cids(8)",
560
607
  "creator": {
561
608
  "associated": {
562
609
  "activitySubscription": {
@@ -599,11 +646,11 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
599
646
  "indexedAt": "1970-01-01T00:00:00.000Z",
600
647
  "labels": [],
601
648
  "likeCount": 0,
602
- "uri": "record(8)",
649
+ "uri": "record(9)",
603
650
  "viewer": {},
604
651
  },
605
652
  {
606
- "cid": "cids(8)",
653
+ "cid": "cids(9)",
607
654
  "creator": {
608
655
  "associated": {
609
656
  "activitySubscription": {
@@ -646,11 +693,11 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
646
693
  "indexedAt": "1970-01-01T00:00:00.000Z",
647
694
  "labels": [],
648
695
  "likeCount": 0,
649
- "uri": "record(9)",
696
+ "uri": "record(10)",
650
697
  "viewer": {},
651
698
  },
652
699
  {
653
- "cid": "cids(9)",
700
+ "cid": "cids(10)",
654
701
  "creator": {
655
702
  "associated": {
656
703
  "activitySubscription": {
@@ -693,9 +740,9 @@ exports[`feed generation > getActorFeeds fetches feed generators by actor. 1`] =
693
740
  "indexedAt": "1970-01-01T00:00:00.000Z",
694
741
  "labels": [],
695
742
  "likeCount": 2,
696
- "uri": "record(10)",
743
+ "uri": "record(11)",
697
744
  "viewer": {
698
- "like": "record(11)",
745
+ "like": "record(12)",
699
746
  },
700
747
  },
701
748
  ]
@@ -43,6 +43,7 @@ describe('thread mutes', () => {
43
43
  ),
44
44
  },
45
45
  )
46
+ await network.processAll()
46
47
  })
47
48
 
48
49
  it('notes that threads are muted in viewer state', async () => {
@@ -137,6 +138,7 @@ describe('thread mutes', () => {
137
138
  ),
138
139
  },
139
140
  )
141
+ await network.processAll()
140
142
  })
141
143
 
142
144
  it('no longer notes that threads are muted in viewer state after unmuting', async () => {
@@ -41,6 +41,7 @@ describe('feed generation', () => {
41
41
  let feedUriNeedsAuth: string
42
42
  let feedUriContentModeVideo: string
43
43
  let feedUriAcceptsInteractions: string
44
+ let feedUriPinned: string
44
45
  let starterPackRef: { uri: string; cid: string }
45
46
 
46
47
  beforeAll(async () => {
@@ -77,6 +78,7 @@ describe('feed generation', () => {
77
78
  'app.bsky.feed.generator',
78
79
  'accepts-interactions',
79
80
  )
81
+ const pinnedUri = AtUri.make(alice, 'app.bsky.feed.generator', 'pinned')
80
82
  gen = await network.createFeedGen({
81
83
  [allUri.toString()]: feedGenHandler('all'),
82
84
  [evenUri.toString()]: feedGenHandler('even'),
@@ -91,6 +93,7 @@ describe('feed generation', () => {
91
93
  [acceptsInteractionsUri.toString()]: feedGenHandler(
92
94
  'accepts-interactions',
93
95
  ),
96
+ [pinnedUri.toString()]: feedGenHandler('pinned'),
94
97
  })
95
98
 
96
99
  const feedSuggestions = [
@@ -210,6 +213,16 @@ describe('feed generation', () => {
210
213
  },
211
214
  sc.getHeaders(alice),
212
215
  )
216
+ const pinned = await pdsAgent.api.app.bsky.feed.generator.create(
217
+ { repo: alice, rkey: 'pinned' },
218
+ {
219
+ did: gen.did,
220
+ displayName: 'Pinned',
221
+ description: 'Returns a post with skeletonReasonPin',
222
+ createdAt: new Date().toISOString(),
223
+ },
224
+ sc.getHeaders(alice),
225
+ )
213
226
  await network.processAll()
214
227
  await network.bsky.ctx.dataplane.takedownRecord({
215
228
  recordUri: prime.uri,
@@ -226,6 +239,7 @@ describe('feed generation', () => {
226
239
  feedUriNeedsAuth = needsAuth.uri
227
240
  feedUriContentModeVideo = contentModeVideo.uri
228
241
  feedUriAcceptsInteractions = acceptsInteraction.uri
242
+ feedUriPinned = pinned.uri
229
243
  })
230
244
 
231
245
  it('feed gen records can be updated', async () => {
@@ -269,15 +283,16 @@ describe('feed generation', () => {
269
283
 
270
284
  const paginatedAll = results(await paginateAll(paginator))
271
285
 
272
- expect(paginatedAll.length).toEqual(8)
286
+ expect(paginatedAll.length).toEqual(9)
273
287
  expect(paginatedAll[0].uri).toEqual(feedUriOdd)
274
- expect(paginatedAll[1].uri).toEqual(feedUriAcceptsInteractions)
275
- expect(paginatedAll[2].uri).toEqual(feedUriContentModeVideo)
276
- expect(paginatedAll[3].uri).toEqual(feedUriNeedsAuth)
277
- expect(paginatedAll[4].uri).toEqual(feedUriBadPaginationCursor)
278
- expect(paginatedAll[5].uri).toEqual(feedUriBadPaginationLimit)
279
- expect(paginatedAll[6].uri).toEqual(feedUriEven)
280
- expect(paginatedAll[7].uri).toEqual(feedUriAll)
288
+ expect(paginatedAll[1].uri).toEqual(feedUriPinned)
289
+ expect(paginatedAll[2].uri).toEqual(feedUriAcceptsInteractions)
290
+ expect(paginatedAll[3].uri).toEqual(feedUriContentModeVideo)
291
+ expect(paginatedAll[4].uri).toEqual(feedUriNeedsAuth)
292
+ expect(paginatedAll[5].uri).toEqual(feedUriBadPaginationCursor)
293
+ expect(paginatedAll[6].uri).toEqual(feedUriBadPaginationLimit)
294
+ expect(paginatedAll[7].uri).toEqual(feedUriEven)
295
+ expect(paginatedAll[8].uri).toEqual(feedUriAll)
281
296
  expect(paginatedAll.map((fg) => fg.uri)).not.toContain(feedUriPrime) // taken-down
282
297
  expect(forSnapshot(paginatedAll)).toMatchSnapshot()
283
298
  })
@@ -813,6 +828,21 @@ describe('feed generation', () => {
813
828
  )
814
829
  })
815
830
 
831
+ it('propagates skeletonReasonPin as ReasonPin.', async () => {
832
+ const feed = await agent.api.app.bsky.feed.getFeed(
833
+ { feed: feedUriPinned },
834
+ {
835
+ headers: await network.serviceHeaders(
836
+ alice,
837
+ ids.AppBskyFeedGetFeed,
838
+ gen.did,
839
+ ),
840
+ },
841
+ )
842
+ expect(feed.data.feed).toHaveLength(1)
843
+ expect(AppBskyFeedDefs.isReasonPin(feed.data.feed[0].reason)).toBe(true)
844
+ })
845
+
816
846
  it('returns an upstream failure error when the feed is down.', async () => {
817
847
  await gen.close() // @NOTE must be last test
818
848
  const tryGetFeed = agent.api.app.bsky.feed.getFeed(
@@ -838,9 +868,25 @@ describe('feed generation', () => {
838
868
  | 'bad-pagination-limit'
839
869
  | 'bad-pagination-cursor'
840
870
  | 'needs-auth'
841
- | 'accepts-interactions',
871
+ | 'accepts-interactions'
872
+ | 'pinned',
842
873
  ): SkeletonHandler =>
843
874
  async ({ req, params }) => {
875
+ if (feedName === 'pinned') {
876
+ return {
877
+ encoding: 'application/json',
878
+ body: {
879
+ feed: [
880
+ {
881
+ post: sc.posts[sc.dids.alice][0].ref.uriStr,
882
+ reason: {
883
+ $type: 'app.bsky.feed.defs#skeletonReasonPin',
884
+ },
885
+ },
886
+ ],
887
+ } satisfies app.bsky.feed.getFeedSkeleton.$OutputBody,
888
+ }
889
+ }
844
890
  if (feedName === 'needs-auth' && !req.headers.authorization) {
845
891
  throw new AuthRequiredError('This feed requires auth')
846
892
  }
@@ -44,7 +44,12 @@ describe('private data', () => {
44
44
  })
45
45
 
46
46
  beforeEach(async () => network.processAll())
47
- afterEach(async () => clearPrivateData(network.bsky.db))
47
+ afterEach(async () => {
48
+ // Drain pending bsync ops before clearing, so a stale op can't land after
49
+ // the reset.
50
+ await network.processAll()
51
+ await clearPrivateData(network.bsky.db)
52
+ })
48
53
  afterAll(async () => network?.close())
49
54
 
50
55
  describe('create', () => {
@@ -170,6 +170,9 @@ describe('age assurance v2 views', () => {
170
170
 
171
171
  afterEach(async () => {
172
172
  vi.resetAllMocks()
173
+ // Drain in-flight bsync ops before resetting state directly, so an op from
174
+ // a test that doesn't read it back can't leak into the next test.
175
+ await network.processAll()
173
176
  await clearPrivateData(db)
174
177
  await clearActorAgeAssurance(db)
175
178
  })
@@ -97,6 +97,9 @@ describe('age assurance views', () => {
97
97
  })
98
98
 
99
99
  afterEach(async () => {
100
+ // Drain pending bsync ops before clearing, so a stale op can't land after
101
+ // the reset.
102
+ await network.processAll()
100
103
  await clearPrivateData(db)
101
104
  await clearActorAgeAssurance(db)
102
105
  })
@@ -225,6 +228,7 @@ describe('age assurance views', () => {
225
228
  externalPayload,
226
229
  status,
227
230
  })
231
+ await network.processAll()
228
232
  const finalizedState = await getAgeAssurance(actor)
229
233
  expect(finalizedState).toStrictEqual({
230
234
  status: 'assured',
@@ -264,6 +268,7 @@ describe('age assurance views', () => {
264
268
  `${redirectUrl}?actorDid=${encodeURIComponent(actorDid)}&result=success`,
265
269
  )
266
270
 
271
+ await network.processAll()
267
272
  const state2 = await getAgeAssurance(actorDid)
268
273
  expect(state2).toStrictEqual({
269
274
  status: 'assured',
@@ -288,6 +293,7 @@ describe('age assurance views', () => {
288
293
  `${redirectUrl}?result=unknown`,
289
294
  )
290
295
 
296
+ await network.processAll()
291
297
  const state = await getAgeAssurance(actorDid)
292
298
  expect(state).toStrictEqual({
293
299
  status: 'pending',
@@ -323,6 +329,7 @@ describe('age assurance views', () => {
323
329
  })
324
330
  expect(webhookRes.status).toBe(200)
325
331
 
332
+ await network.processAll()
326
333
  const state2 = await getAgeAssurance(actorDid)
327
334
  expect(state2).toStrictEqual({
328
335
  status: 'assured',
@@ -346,6 +353,7 @@ describe('age assurance views', () => {
346
353
  })
347
354
  expect(webhookRes.status).toBe(500)
348
355
 
356
+ await network.processAll()
349
357
  const state = await getAgeAssurance(actorDid)
350
358
  expect(state).toStrictEqual({
351
359
  status: 'pending',
@@ -56,6 +56,9 @@ describe('appview bookmarks views', () => {
56
56
 
57
57
  afterEach(async () => {
58
58
  vi.resetAllMocks()
59
+ // Drain pending bsync ops before clearing, so a stale op can't land after
60
+ // the reset.
61
+ await network.processAll()
59
62
  await clearPrivateData(db)
60
63
  await clearBookmarks(db)
61
64
  })
@@ -114,6 +117,7 @@ describe('appview bookmarks views', () => {
114
117
 
115
118
  await create(bob, sc.posts[bob][0].ref)
116
119
  await create(bob, sc.posts[carol][0].ref)
120
+ await network.processAll()
117
121
 
118
122
  const { data: dataAlice } = await get(alice)
119
123
  expect(dataAlice.bookmarks).toHaveLength(3)
@@ -126,10 +130,12 @@ describe('appview bookmarks views', () => {
126
130
  const uri = sc.posts[alice][0].ref
127
131
 
128
132
  await create(alice, uri)
133
+ await network.processAll()
129
134
  const { data: data0 } = await get(alice)
130
135
  expect(data0.bookmarks).toHaveLength(1)
131
136
 
132
137
  await create(alice, uri)
138
+ await network.processAll()
133
139
  const { data: data1 } = await get(alice)
134
140
  expect(data1.bookmarks).toHaveLength(1)
135
141
  })
@@ -147,12 +153,14 @@ describe('appview bookmarks views', () => {
147
153
  await create(alice, sc.posts[alice][0].ref)
148
154
  await create(alice, sc.posts[bob][0].ref)
149
155
  await create(alice, sc.posts[carol][0].ref)
156
+ await network.processAll()
150
157
 
151
158
  const { data: dataBefore } = await get(alice)
152
159
  expect(dataBefore.bookmarks).toHaveLength(3)
153
160
 
154
161
  await del(alice, sc.posts[alice][0].ref)
155
162
  await del(alice, sc.posts[carol][0].ref)
163
+ await network.processAll()
156
164
 
157
165
  const { data: dataAfter } = await get(alice)
158
166
  expect(dataAfter.bookmarks).toHaveLength(1)
@@ -161,12 +169,17 @@ describe('appview bookmarks views', () => {
161
169
  it('is idempotent', async () => {
162
170
  const uri = sc.posts[alice][0].ref
163
171
  await create(alice, uri)
172
+ // deleteBookmark resolves the bookmark's key from indexed state, so the
173
+ // create must be indexed before the delete is issued.
174
+ await network.processAll()
164
175
 
165
176
  await del(alice, uri)
177
+ await network.processAll()
166
178
  const { data: data0 } = await get(alice)
167
179
  expect(data0.bookmarks).toHaveLength(0)
168
180
 
169
181
  await del(alice, uri)
182
+ await network.processAll()
170
183
  const { data: data1 } = await get(alice)
171
184
  expect(data1.bookmarks).toHaveLength(0)
172
185
  })
@@ -192,12 +205,14 @@ describe('appview bookmarks views', () => {
192
205
  expect(postBefore.viewer?.bookmarked).toBe(false)
193
206
 
194
207
  await create(alice, ref)
208
+ await network.processAll()
195
209
  const postAfterCreate = await getPost(alice, ref)
196
210
  expect(postAfterCreate.viewer?.bookmarked).toBe(true)
197
211
  const postAfterCreateForBob = await getPost(bob, ref)
198
212
  expect(postAfterCreateForBob.viewer?.bookmarked).toBe(false)
199
213
 
200
214
  await del(alice, ref)
215
+ await network.processAll()
201
216
  const postAfterDel = await getPost(alice, ref)
202
217
  expect(postAfterDel.viewer?.bookmarked).toBe(false)
203
218
  })
@@ -210,16 +225,19 @@ describe('appview bookmarks views', () => {
210
225
 
211
226
  await create(alice, uri)
212
227
  await create(carol, uri)
228
+ await network.processAll()
213
229
  const postAfterCreate = await getPost(alice, uri)
214
230
  expect(postAfterCreate.bookmarkCount).toBe(2)
215
231
  const postAfterCreateForBob = await getPost(bob, uri)
216
232
  expect(postAfterCreateForBob.bookmarkCount).toBe(2)
217
233
 
218
234
  await del(alice, uri)
235
+ await network.processAll()
219
236
  const postAfterAliceDel = await getPost(alice, uri)
220
237
  expect(postAfterAliceDel.bookmarkCount).toBe(1)
221
238
 
222
239
  await del(carol, uri)
240
+ await network.processAll()
223
241
  const postAfterCarolDel = await getPost(carol, uri)
224
242
  expect(postAfterCarolDel.bookmarkCount).toBe(0)
225
243
  })
@@ -232,6 +250,7 @@ describe('appview bookmarks views', () => {
232
250
  await create(alice, sc.posts[carol][0].ref)
233
251
  await create(alice, sc.posts[dan][0].ref)
234
252
  await create(alice, sc.posts[dan][1].ref)
253
+ await network.processAll()
235
254
 
236
255
  const results = (out: AppBskyBookmarkGetBookmarks.OutputSchema[]) =>
237
256
  out.flatMap((res) => res.bookmarks)
@@ -48,8 +48,13 @@ describe('appview drafts views', () => {
48
48
  })
49
49
 
50
50
  beforeEach(async () => network.processAll())
51
- afterEach(async () => vi.resetAllMocks())
52
- afterEach(async () => clearDrafts(network.bsky.db))
51
+ afterEach(async () => {
52
+ vi.resetAllMocks()
53
+ // Drain in-flight bsync operations before resetting state directly,
54
+ // otherwise a late-applied op can resurrect state after the reset.
55
+ await network.processAll()
56
+ await clearDrafts(network.bsky.db)
57
+ })
53
58
  afterAll(async () => network?.close())
54
59
 
55
60
  const makeDraft = (): AppBskyDraftDefs.Draft => ({
@@ -113,6 +118,7 @@ describe('appview drafts views', () => {
113
118
 
114
119
  await create(bob, makeDraft())
115
120
  await create(bob, makeDraft())
121
+ await network.processAll()
116
122
 
117
123
  const { data: dataAlice } = await get(alice)
118
124
  expect(dataAlice.drafts).toHaveLength(3)
@@ -131,6 +137,7 @@ describe('appview drafts views', () => {
131
137
  }
132
138
 
133
139
  await create(alice, draft)
140
+ await network.processAll()
134
141
  const { data } = await get(alice)
135
142
  expect(data.drafts).toHaveLength(1)
136
143
  expect(data.drafts[0].draft.posts).toHaveLength(3)
@@ -159,6 +166,7 @@ describe('appview drafts views', () => {
159
166
  }
160
167
 
161
168
  await create(alice, draft1)
169
+ await network.processAll()
162
170
  const { data: data0 } = await get(alice)
163
171
  expect(data0.drafts).toHaveLength(1)
164
172
  expect(data0.drafts[0].draft.posts[0].text).toBe('First version')
@@ -170,6 +178,7 @@ describe('appview drafts views', () => {
170
178
  }
171
179
 
172
180
  await update(alice, draft2)
181
+ await network.processAll()
173
182
  const { data: data1 } = await get(alice)
174
183
  expect(data1.drafts).toHaveLength(1)
175
184
  expect(data1.drafts[0].draft.posts[0].text).toBe('Updated version')
@@ -182,6 +191,7 @@ describe('appview drafts views', () => {
182
191
  }
183
192
 
184
193
  await update(alice, nonExistingDraft)
194
+ await network.processAll()
185
195
  const { data } = await get(alice)
186
196
  expect(data.drafts).toHaveLength(0)
187
197
  })
@@ -192,6 +202,7 @@ describe('appview drafts views', () => {
192
202
  await create(alice, makeDraft())
193
203
  await create(alice, makeDraft())
194
204
  await create(alice, makeDraft())
205
+ await network.processAll()
195
206
 
196
207
  const { data: dataBefore } = await get(alice)
197
208
  expect(dataBefore.drafts).toHaveLength(3)
@@ -202,6 +213,7 @@ describe('appview drafts views', () => {
202
213
 
203
214
  await del(alice, draft1Id)
204
215
  await del(alice, draft3Id)
216
+ await network.processAll()
205
217
 
206
218
  const { data: dataAfter } = await get(alice)
207
219
  expect(dataAfter.drafts).toHaveLength(1)
@@ -210,16 +222,19 @@ describe('appview drafts views', () => {
210
222
 
211
223
  it('is idempotent', async () => {
212
224
  await create(alice, makeDraft())
225
+ await network.processAll()
213
226
 
214
227
  const { data: data0 } = await get(alice)
215
228
  expect(data0.drafts).toHaveLength(1)
216
229
  const draftId = data0.drafts[0].id
217
230
 
218
231
  await del(alice, draftId)
232
+ await network.processAll()
219
233
  const { data: data1 } = await get(alice)
220
234
  expect(data1.drafts).toHaveLength(0)
221
235
 
222
236
  await del(alice, draftId)
237
+ await network.processAll()
223
238
  const { data: data2 } = await get(alice)
224
239
  expect(data2.drafts).toHaveLength(0)
225
240
  })
@@ -235,6 +250,7 @@ describe('appview drafts views', () => {
235
250
  await create(alice, makeDraft())
236
251
  await create(alice, makeDraft())
237
252
  await create(bob, makeDraft())
253
+ await network.processAll()
238
254
 
239
255
  const { data: dataAlice } = await get(alice)
240
256
  expect(dataAlice.drafts).toHaveLength(2)
@@ -246,6 +262,7 @@ describe('appview drafts views', () => {
246
262
  it('includes timestamps', async () => {
247
263
  const beforeCreate = new Date()
248
264
  await create(alice, makeDraft())
265
+ await network.processAll()
249
266
  const afterCreate = new Date()
250
267
 
251
268
  const { data } = await get(alice)
@@ -264,6 +281,7 @@ describe('appview drafts views', () => {
264
281
  for (let i = 0; i < 7; i++) {
265
282
  await create(alice, makeDraft())
266
283
  }
284
+ await network.processAll()
267
285
 
268
286
  const results = (out: AppBskyDraftGetDrafts.OutputSchema[]) =>
269
287
  out.flatMap((res) => res.drafts)
@@ -319,6 +337,7 @@ describe('appview drafts views', () => {
319
337
 
320
338
  it('round-trips a draft with embedGallery', async () => {
321
339
  await create(alice, galleryDraft(3))
340
+ await network.processAll()
322
341
  const { data } = await get(alice)
323
342
  expect(data.drafts).toHaveLength(1)
324
343
 
@@ -335,6 +354,7 @@ describe('appview drafts views', () => {
335
354
 
336
355
  it('updates a draft to add a gallery', async () => {
337
356
  await create(alice, { posts: [{ text: 'text only' }] })
357
+ await network.processAll()
338
358
  const { data: before } = await get(alice)
339
359
  expect(before.drafts).toHaveLength(1)
340
360
  expect(before.drafts[0].draft.posts[0].embedGallery).toBeUndefined()
@@ -344,6 +364,7 @@ describe('appview drafts views', () => {
344
364
  id: draftId,
345
365
  draft: galleryDraft(2),
346
366
  })
367
+ await network.processAll()
347
368
 
348
369
  const { data: after } = await get(alice)
349
370
  expect(after.drafts).toHaveLength(1)
@@ -353,6 +374,7 @@ describe('appview drafts views', () => {
353
374
 
354
375
  it('updates a draft to change gallery items', async () => {
355
376
  await create(alice, galleryDraft(2))
377
+ await network.processAll()
356
378
  const { data: before } = await get(alice)
357
379
  expect(before.drafts[0].draft.posts[0].embedGallery?.items).toHaveLength(
358
380
  2,
@@ -363,6 +385,7 @@ describe('appview drafts views', () => {
363
385
  id: draftId,
364
386
  draft: galleryDraft(5),
365
387
  })
388
+ await network.processAll()
366
389
 
367
390
  const { data: after } = await get(alice)
368
391
  const post = after.drafts[0].draft.posts[0]
@@ -334,6 +334,7 @@ describe('bsky views with mutes from mute lists', () => {
334
334
  ),
335
335
  },
336
336
  )
337
+ await network.processAll()
337
338
 
338
339
  const res = await agent.api.app.bsky.graph.getListMutes(
339
340
  {},
@@ -392,6 +393,7 @@ describe('bsky views with mutes from mute lists', () => {
392
393
  ),
393
394
  },
394
395
  )
396
+ await network.processAll()
395
397
 
396
398
  const res = await agent.api.app.bsky.graph.getListMutes(
397
399
  {},
@@ -275,6 +275,7 @@ describe('mute views', () => {
275
275
  encoding: 'application/json',
276
276
  },
277
277
  )
278
+ await network.processAll()
278
279
 
279
280
  const { data: final } = await agent.api.app.bsky.graph.getMutes(
280
281
  {},