@atproto/bsky 0.0.176 → 0.0.178

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 (64) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/api/app/bsky/graph/getList.js +1 -6
  3. package/dist/api/app/bsky/graph/getList.js.map +1 -1
  4. package/dist/api/app/bsky/graph/getLists.d.ts.map +1 -1
  5. package/dist/api/app/bsky/graph/getLists.js +16 -4
  6. package/dist/api/app/bsky/graph/getLists.js.map +1 -1
  7. package/dist/api/app/bsky/graph/getListsWithMembership.d.ts +4 -0
  8. package/dist/api/app/bsky/graph/getListsWithMembership.d.ts.map +1 -0
  9. package/dist/api/app/bsky/graph/getListsWithMembership.js +88 -0
  10. package/dist/api/app/bsky/graph/getListsWithMembership.js.map +1 -0
  11. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.d.ts +4 -0
  12. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.d.ts.map +1 -0
  13. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js +72 -0
  14. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js.map +1 -0
  15. package/dist/api/index.d.ts.map +1 -1
  16. package/dist/api/index.js +4 -0
  17. package/dist/api/index.js.map +1 -1
  18. package/dist/hydration/graph.d.ts +4 -0
  19. package/dist/hydration/graph.d.ts.map +1 -1
  20. package/dist/hydration/graph.js.map +1 -1
  21. package/dist/hydration/hydrator.d.ts +3 -1
  22. package/dist/hydration/hydrator.d.ts.map +1 -1
  23. package/dist/hydration/hydrator.js +27 -0
  24. package/dist/hydration/hydrator.js.map +1 -1
  25. package/dist/lexicon/index.d.ts +234 -230
  26. package/dist/lexicon/index.d.ts.map +1 -1
  27. package/dist/lexicon/index.js +682 -674
  28. package/dist/lexicon/index.js.map +1 -1
  29. package/dist/lexicon/lexicons.d.ts +17073 -16785
  30. package/dist/lexicon/lexicons.d.ts.map +1 -1
  31. package/dist/lexicon/lexicons.js +9126 -8980
  32. package/dist/lexicon/lexicons.js.map +1 -1
  33. package/dist/lexicon/types/app/bsky/graph/getLists.d.ts +2 -0
  34. package/dist/lexicon/types/app/bsky/graph/getLists.d.ts.map +1 -1
  35. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.d.ts +40 -0
  36. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.d.ts.map +1 -0
  37. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.js +16 -0
  38. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.js.map +1 -0
  39. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.d.ts +38 -0
  40. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.d.ts.map +1 -0
  41. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.js +16 -0
  42. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.js.map +1 -0
  43. package/dist/views/index.d.ts +2 -1
  44. package/dist/views/index.d.ts.map +1 -1
  45. package/dist/views/index.js +6 -0
  46. package/dist/views/index.js.map +1 -1
  47. package/package.json +11 -11
  48. package/src/api/app/bsky/graph/getList.ts +3 -5
  49. package/src/api/app/bsky/graph/getLists.ts +18 -5
  50. package/src/api/app/bsky/graph/getListsWithMembership.ts +139 -0
  51. package/src/api/app/bsky/graph/getStarterPacksWithMembership.ts +135 -0
  52. package/src/api/index.ts +4 -0
  53. package/src/hydration/graph.ts +8 -0
  54. package/src/hydration/hydrator.ts +43 -0
  55. package/src/lexicon/index.ts +1247 -1221
  56. package/src/lexicon/lexicons.ts +9494 -9341
  57. package/src/lexicon/types/app/bsky/graph/getLists.ts +2 -0
  58. package/src/lexicon/types/app/bsky/graph/getListsWithMembership.ts +63 -0
  59. package/src/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.ts +65 -0
  60. package/src/views/index.ts +11 -0
  61. package/tests/views/__snapshots__/lists.test.ts.snap +160 -8
  62. package/tests/views/lists.test.ts +270 -36
  63. package/tests/views/starter-packs.test.ts +132 -3
  64. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,15 +1,26 @@
1
1
  import { AtpAgent } from '@atproto/api'
2
2
  import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
3
3
  import { ids } from '../../src/lexicon/lexicons'
4
- import { forSnapshot } from '../_util'
4
+ import { OutputSchema as GetListsOutputSchema } from '../../src/lexicon/types/app/bsky/graph/getLists'
5
+ import {
6
+ ListWithMembership,
7
+ OutputSchema as GetListsWithMembershipOutputSchema,
8
+ } from '../../src/lexicon/types/app/bsky/graph/getListsWithMembership'
9
+ import { forSnapshot, paginateAll } from '../_util'
5
10
 
6
11
  describe('bsky actor likes feed views', () => {
7
12
  let network: TestNetwork
8
13
  let agent: AtpAgent
9
14
  let sc: SeedClient
10
15
 
16
+ let blockList: string
11
17
  let curateList: string
12
18
  let referenceList: string
19
+ let eveListItemCur: string
20
+ let frankieListItemCur: string
21
+ let frankieListItemMod: string
22
+ let gretaListItemMod: string
23
+
13
24
  let alice: string
14
25
  let eve: string
15
26
  let frankie: string
@@ -38,30 +49,53 @@ describe('bsky actor likes feed views', () => {
38
49
  password: 'hunter4real',
39
50
  })
40
51
 
41
- const newRefList = await sc.createList(
52
+ const newRefList = await sc.createList(sc.dids.eve, 'ref0', 'reference')
53
+ await sc.addToList(sc.dids.eve, sc.dids.eve, newRefList)
54
+ await sc.addToList(sc.dids.eve, sc.dids.bob, newRefList)
55
+ await sc.addToList(sc.dids.eve, sc.dids.frankie, newRefList)
56
+
57
+ const newCurList = await sc.createList(sc.dids.eve, 'cur0', 'curate')
58
+ await sc.createList(sc.dids.eve, 'cur1', 'curate')
59
+ await sc.createList(sc.dids.eve, 'cur2', 'curate')
60
+ const newEveListItemCur = await sc.addToList(
61
+ sc.dids.eve,
42
62
  sc.dids.eve,
43
- 'blah starter pack list!',
44
- 'reference',
63
+ newCurList,
45
64
  )
46
- const newCurrList = await sc.createList(
65
+ await sc.addToList(sc.dids.eve, sc.dids.bob, newCurList)
66
+ const newFrankieListItemCur = await sc.addToList(
47
67
  sc.dids.eve,
48
- 'blah curate list!',
49
- 'curate',
68
+ sc.dids.frankie,
69
+ newCurList,
50
70
  )
51
71
 
52
- await sc.addToList(sc.dids.eve, sc.dids.eve, newRefList)
53
- await sc.addToList(sc.dids.eve, sc.dids.bob, newRefList)
54
- await sc.addToList(sc.dids.eve, sc.dids.frankie, newRefList)
55
-
56
- await sc.addToList(sc.dids.eve, sc.dids.eve, newCurrList)
57
- await sc.addToList(sc.dids.eve, sc.dids.bob, newCurrList)
58
- await sc.addToList(sc.dids.eve, sc.dids.frankie, newCurrList)
72
+ const newBlockList = await sc.createList(sc.dids.eve, 'mod0', 'mod')
73
+ await sc.createList(sc.dids.eve, 'mod1', 'mod')
74
+ await sc.createList(sc.dids.eve, 'mod2', 'mod')
75
+ const newFrankieListItemMod = await sc.addToList(
76
+ sc.dids.eve,
77
+ sc.dids.frankie,
78
+ newBlockList,
79
+ )
80
+ const newGretaListItemMod = await sc.addToList(
81
+ sc.dids.eve,
82
+ sc.dids.greta,
83
+ newBlockList,
84
+ )
59
85
 
86
+ await sc.block(sc.dids.frankie, sc.dids.greta)
60
87
  await sc.block(sc.dids.frankie, sc.dids.eve)
61
88
 
62
89
  await network.processAll()
63
- curateList = newCurrList.uriStr
90
+ blockList = newBlockList.uriStr
91
+ curateList = newCurList.uriStr
64
92
  referenceList = newRefList.uriStr
93
+
94
+ eveListItemCur = newEveListItemCur.uriStr
95
+ frankieListItemCur = newFrankieListItemCur.uriStr
96
+ frankieListItemMod = newFrankieListItemMod.uriStr
97
+ gretaListItemMod = newGretaListItemMod.uriStr
98
+
65
99
  alice = sc.dids.alice
66
100
  eve = sc.dids.eve
67
101
  frankie = sc.dids.frankie
@@ -73,12 +107,10 @@ describe('bsky actor likes feed views', () => {
73
107
  })
74
108
 
75
109
  it('does not include reference lists in getActorLists', async () => {
76
- await sc.createList(eve, 'cool curate list', 'curate')
77
- await network.processAll()
78
- const view = await agent.api.app.bsky.graph.getLists({
110
+ const view = await agent.app.bsky.graph.getLists({
79
111
  actor: eve,
80
112
  })
81
- expect(view.data.lists.length).toBe(2)
113
+ expect(view.data.lists.length).toBe(6)
82
114
  expect(forSnapshot(view.data.lists)).toMatchSnapshot()
83
115
  })
84
116
 
@@ -86,12 +118,79 @@ describe('bsky actor likes feed views', () => {
86
118
  const view = await agent.app.bsky.graph.getLists({
87
119
  actor: 'eve.test',
88
120
  })
89
- expect(view.data.lists.length).toBe(2)
121
+ expect(view.data.lists.length).toBe(6)
90
122
  expect(forSnapshot(view.data.lists)).toMatchSnapshot()
91
123
  })
92
124
 
125
+ it('allows filtering by list purpose', async () => {
126
+ const viewCurate = await agent.app.bsky.graph.getLists({
127
+ actor: eve,
128
+ purposes: ['curatelist'],
129
+ })
130
+ expect(viewCurate.data.lists.length).toBe(3)
131
+
132
+ const viewMod = await agent.app.bsky.graph.getLists({
133
+ actor: eve,
134
+ purposes: ['modlist'],
135
+ })
136
+ expect(viewMod.data.lists.length).toBe(3)
137
+
138
+ const viewAll = await agent.app.bsky.graph.getLists({
139
+ actor: eve,
140
+ purposes: ['curatelist', 'modlist'],
141
+ })
142
+ expect(viewAll.data.lists.length).toBe(6)
143
+ })
144
+
145
+ it.each([
146
+ { expected: 6, purposes: [] },
147
+ { expected: 6, purposes: ['curatelist', 'modlist'] },
148
+ { expected: 3, purposes: ['curatelist'] },
149
+ { expected: 3, purposes: ['modlist'] },
150
+ { expected: 0, purposes: ['referencelist'] }, // not supported on getLists.
151
+ ])(
152
+ 'paginates for purposes filter: $purposes',
153
+ async ({ expected, purposes }) => {
154
+ const results = (out: GetListsOutputSchema[]) =>
155
+ out.flatMap((res) => res.lists)
156
+ const paginator = async (cursor?: string) => {
157
+ const res = await agent.app.bsky.graph.getLists(
158
+ { actor: eve, purposes, limit: 2, cursor },
159
+ {
160
+ headers: await network.serviceHeaders(
161
+ eve,
162
+ ids.AppBskyGraphGetLists,
163
+ ),
164
+ },
165
+ )
166
+ return res.data
167
+ }
168
+
169
+ const paginatedAll = await paginateAll(paginator)
170
+ paginatedAll.forEach((res) =>
171
+ expect(res.lists.length).toBeLessThanOrEqual(2),
172
+ )
173
+
174
+ const full = await agent.app.bsky.graph.getLists(
175
+ { actor: eve, purposes },
176
+ {
177
+ headers: await network.serviceHeaders(eve, ids.AppBskyGraphGetLists),
178
+ },
179
+ )
180
+ expect(full.data.lists.length).toBe(expected)
181
+
182
+ const sortedFull = results([full.data]).sort((a, b) =>
183
+ a.uri > b.uri ? 1 : -1,
184
+ )
185
+ const sortedPaginated = results(paginatedAll).sort((a, b) =>
186
+ a.uri > b.uri ? 1 : -1,
187
+ )
188
+ expect(sortedPaginated).toEqual(sortedFull)
189
+ },
190
+ )
191
+
93
192
  it('does not include users with creator block relationship in reference lists for non-creator, in-list viewers', async () => {
94
- const curView = await agent.api.app.bsky.graph.getList(
193
+ const curView = await agent.app.bsky.graph.getList(
95
194
  {
96
195
  list: curateList,
97
196
  },
@@ -102,7 +201,7 @@ describe('bsky actor likes feed views', () => {
102
201
  expect(curView.data.items.length).toBe(2)
103
202
  expect(forSnapshot(curView.data.items)).toMatchSnapshot()
104
203
 
105
- const refView = await agent.api.app.bsky.graph.getList(
204
+ const refView = await agent.app.bsky.graph.getList(
106
205
  { list: referenceList },
107
206
  {
108
207
  headers: await network.serviceHeaders(frankie, ids.AppBskyGraphGetList),
@@ -113,7 +212,7 @@ describe('bsky actor likes feed views', () => {
113
212
  })
114
213
 
115
214
  it('does not include users with creator block relationship in reference lists for non-creator, not-in-list viewers', async () => {
116
- const curView = await agent.api.app.bsky.graph.getList(
215
+ const curView = await agent.app.bsky.graph.getList(
117
216
  {
118
217
  list: curateList,
119
218
  },
@@ -122,7 +221,7 @@ describe('bsky actor likes feed views', () => {
122
221
  expect(curView.data.items.length).toBe(2)
123
222
  expect(forSnapshot(curView.data.items)).toMatchSnapshot()
124
223
 
125
- const refView = await agent.api.app.bsky.graph.getList(
224
+ const refView = await agent.app.bsky.graph.getList(
126
225
  { list: referenceList },
127
226
  { headers: await network.serviceHeaders(greta, ids.AppBskyGraphGetList) },
128
227
  )
@@ -131,13 +230,13 @@ describe('bsky actor likes feed views', () => {
131
230
  })
132
231
 
133
232
  it('does not include users with creator block relationship in reference and curate lists for signed-out viewers', async () => {
134
- const curView = await agent.api.app.bsky.graph.getList({
233
+ const curView = await agent.app.bsky.graph.getList({
135
234
  list: curateList,
136
235
  })
137
236
  expect(curView.data.items.length).toBe(2)
138
237
  expect(forSnapshot(curView.data.items)).toMatchSnapshot()
139
238
 
140
- const refView = await agent.api.app.bsky.graph.getList({
239
+ const refView = await agent.app.bsky.graph.getList({
141
240
  list: referenceList,
142
241
  })
143
242
  expect(refView.data.items.length).toBe(2)
@@ -145,14 +244,14 @@ describe('bsky actor likes feed views', () => {
145
244
  })
146
245
 
147
246
  it('does include users with creator block relationship in reference lists for creator', async () => {
148
- const curView = await agent.api.app.bsky.graph.getList(
247
+ const curView = await agent.app.bsky.graph.getList(
149
248
  { list: curateList },
150
249
  { headers: await network.serviceHeaders(eve, ids.AppBskyGraphGetList) },
151
250
  )
152
251
  expect(curView.data.items.length).toBe(3)
153
252
  expect(forSnapshot(curView.data.items)).toMatchSnapshot()
154
253
 
155
- const refView = await agent.api.app.bsky.graph.getList(
254
+ const refView = await agent.app.bsky.graph.getList(
156
255
  { list: referenceList },
157
256
  { headers: await network.serviceHeaders(eve, ids.AppBskyGraphGetList) },
158
257
  )
@@ -161,17 +260,152 @@ describe('bsky actor likes feed views', () => {
161
260
  })
162
261
 
163
262
  it('does return all users regardless of creator block relationship in moderation lists', async () => {
164
- const blockList = await sc.createList(eve, 'block list', 'mod')
165
- await sc.addToList(eve, frankie, blockList)
166
- await sc.addToList(eve, greta, blockList)
167
- await sc.block(frankie, greta)
168
- await network.processAll()
169
-
170
- const view = await agent.api.app.bsky.graph.getList(
171
- { list: blockList.uriStr },
263
+ const view = await agent.app.bsky.graph.getList(
264
+ { list: blockList },
172
265
  { headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetList) },
173
266
  )
174
267
  expect(view.data.items.length).toBe(2)
175
268
  expect(forSnapshot(view.data.items)).toMatchSnapshot()
176
269
  })
270
+
271
+ describe('list membership', () => {
272
+ const uriSort = (a: string, b: string) => (a > b ? 1 : -1)
273
+ const membershipsUris = (lwms: ListWithMembership[]): string[] =>
274
+ lwms
275
+ .map((lwm) => lwm.listItem?.uri)
276
+ .filter((li): li is string => typeof li === 'string')
277
+ .sort(uriSort)
278
+
279
+ it('returns all lists by the user', async () => {
280
+ const view = await agent.app.bsky.graph.getListsWithMembership(
281
+ { actor: frankie },
282
+ {
283
+ headers: await network.serviceHeaders(
284
+ eve,
285
+ ids.AppBskyGraphGetListsWithMembership,
286
+ ),
287
+ },
288
+ )
289
+ expect(view.data.listsWithMembership.length).toBe(6)
290
+ })
291
+
292
+ it('finds self membership', async () => {
293
+ const view = await agent.app.bsky.graph.getListsWithMembership(
294
+ { actor: eve },
295
+ {
296
+ headers: await network.serviceHeaders(
297
+ eve,
298
+ ids.AppBskyGraphGetListsWithMembership,
299
+ ),
300
+ },
301
+ )
302
+
303
+ expect(view.data.listsWithMembership.length).toBe(6)
304
+ const memberships = membershipsUris(view.data.listsWithMembership)
305
+ const expectedMemberships = [eveListItemCur].sort(uriSort)
306
+ expect(memberships).toEqual(expectedMemberships)
307
+ })
308
+
309
+ it('finds membership in curatelist and modlist if actor is in both and purpose filter includes both', async () => {
310
+ const view = await agent.app.bsky.graph.getListsWithMembership(
311
+ { actor: frankie },
312
+ {
313
+ headers: await network.serviceHeaders(
314
+ eve,
315
+ ids.AppBskyGraphGetListsWithMembership,
316
+ ),
317
+ },
318
+ )
319
+
320
+ expect(view.data.listsWithMembership.length).toBe(6)
321
+ const memberships = membershipsUris(view.data.listsWithMembership)
322
+ const expectedMemberships = [frankieListItemCur, frankieListItemMod].sort(
323
+ uriSort,
324
+ )
325
+ expect(memberships).toEqual(expectedMemberships)
326
+ })
327
+
328
+ it('finds modlist membership filtering by modlist', async () => {
329
+ const view = await agent.app.bsky.graph.getListsWithMembership(
330
+ { actor: greta, purposes: ['modlist'] },
331
+ {
332
+ headers: await network.serviceHeaders(
333
+ eve,
334
+ ids.AppBskyGraphGetListsWithMembership,
335
+ ),
336
+ },
337
+ )
338
+
339
+ expect(view.data.listsWithMembership.length).toBe(3)
340
+ const memberships = membershipsUris(view.data.listsWithMembership)
341
+ const expectedMemberships = [gretaListItemMod].sort(uriSort)
342
+ expect(memberships).toEqual(expectedMemberships)
343
+ })
344
+
345
+ it('does not find modlist membership filtering by curatelist', async () => {
346
+ const view = await agent.app.bsky.graph.getListsWithMembership(
347
+ { actor: greta, purposes: ['curatelist'] },
348
+ {
349
+ headers: await network.serviceHeaders(
350
+ eve,
351
+ ids.AppBskyGraphGetListsWithMembership,
352
+ ),
353
+ },
354
+ )
355
+
356
+ expect(view.data.listsWithMembership.length).toBe(3)
357
+ const memberships = membershipsUris(view.data.listsWithMembership)
358
+ expect(memberships.length).toBe(0)
359
+ })
360
+
361
+ it.each([
362
+ { expected: 6, purposes: [] },
363
+ { expected: 6, purposes: ['curatelist', 'modlist'] },
364
+ { expected: 3, purposes: ['curatelist'] },
365
+ { expected: 3, purposes: ['modlist'] },
366
+ { expected: 0, purposes: ['referencelist'] }, // not supported on getLists.
367
+ ])(
368
+ 'paginates for purposes filter: $purposes',
369
+ async ({ expected, purposes }) => {
370
+ const results = (out: GetListsWithMembershipOutputSchema[]) =>
371
+ out.flatMap((res) => res.listsWithMembership)
372
+ const paginator = async (cursor?: string) => {
373
+ const res = await agent.app.bsky.graph.getListsWithMembership(
374
+ { actor: eve, purposes, limit: 2, cursor },
375
+ {
376
+ headers: await network.serviceHeaders(
377
+ eve,
378
+ ids.AppBskyGraphGetListsWithMembership,
379
+ ),
380
+ },
381
+ )
382
+ return res.data
383
+ }
384
+
385
+ const paginatedAll = await paginateAll(paginator)
386
+ paginatedAll.forEach((res) =>
387
+ expect(res.listsWithMembership.length).toBeLessThanOrEqual(2),
388
+ )
389
+
390
+ const full = await agent.app.bsky.graph.getListsWithMembership(
391
+ { actor: eve, purposes },
392
+ {
393
+ headers: await network.serviceHeaders(
394
+ eve,
395
+ ids.AppBskyGraphGetListsWithMembership,
396
+ ),
397
+ },
398
+ )
399
+ expect(full.data.listsWithMembership.length).toBe(expected)
400
+
401
+ const sortedFull = results([full.data]).sort((a, b) =>
402
+ a.list.uri > b.list.uri ? 1 : -1,
403
+ )
404
+ const sortedPaginated = results(paginatedAll).sort((a, b) =>
405
+ a.list.uri > b.list.uri ? 1 : -1,
406
+ )
407
+ expect(sortedPaginated).toEqual(sortedFull)
408
+ },
409
+ )
410
+ })
177
411
  })
@@ -3,7 +3,11 @@ import { AtpAgent, asPredicate } from '@atproto/api'
3
3
  import { RecordRef, SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
4
4
  import { ids } from '../../src/lexicon/lexicons'
5
5
  import { validateRecord as validateProfileRecord } from '../../src/lexicon/types/app/bsky/actor/profile'
6
- import { forSnapshot } from '../_util'
6
+ import {
7
+ OutputSchema as GetStarterPacksWithMembershipOutputSchema,
8
+ StarterPackWithMembership,
9
+ } from '../../src/lexicon/types/app/bsky/graph/getStarterPacksWithMembership'
10
+ import { forSnapshot, paginateAll } from '../_util'
7
11
 
8
12
  const isValidProfile = asPredicate(validateProfileRecord)
9
13
 
@@ -24,9 +28,7 @@ describe('starter packs', () => {
24
28
  sc = network.getSeedClient()
25
29
  await basicSeed(sc)
26
30
  await network.processAll()
27
- })
28
31
 
29
- beforeAll(async () => {
30
32
  const feedgen = await sc.createFeedGen(
31
33
  sc.dids.alice,
32
34
  'did:web:example.com',
@@ -262,4 +264,131 @@ describe('starter packs', () => {
262
264
  ])
263
265
  })
264
266
  })
267
+
268
+ describe('starter pack membership', () => {
269
+ const membershipsUris = (lwms: StarterPackWithMembership[]): string[] =>
270
+ lwms
271
+ .map((spwm) => spwm.listItem?.uri)
272
+ .filter((li): li is string => typeof li === 'string')
273
+
274
+ it('returns all SPs by the user', async () => {
275
+ const view = await agent.app.bsky.graph.getStarterPacksWithMembership(
276
+ { actor: sc.dids.bob },
277
+ {
278
+ headers: await network.serviceHeaders(
279
+ sc.dids.alice,
280
+ ids.AppBskyGraphGetStarterPacksWithMembership,
281
+ ),
282
+ },
283
+ )
284
+ expect(view.data.starterPacksWithMembership.length).toBe(3)
285
+ })
286
+
287
+ it('finds self membership', async () => {
288
+ const view = await agent.app.bsky.graph.getStarterPacksWithMembership(
289
+ { actor: sc.dids.alice },
290
+ {
291
+ headers: await network.serviceHeaders(
292
+ sc.dids.alice,
293
+ ids.AppBskyGraphGetStarterPacksWithMembership,
294
+ ),
295
+ },
296
+ )
297
+
298
+ expect(view.data.starterPacksWithMembership.length).toBe(3)
299
+ const memberships = membershipsUris(view.data.starterPacksWithMembership)
300
+ expect(memberships.length).toBe(1)
301
+ })
302
+
303
+ it(`finds other user's membership`, async () => {
304
+ const view = await agent.app.bsky.graph.getStarterPacksWithMembership(
305
+ { actor: sc.dids.bob },
306
+ {
307
+ headers: await network.serviceHeaders(
308
+ sc.dids.alice,
309
+ ids.AppBskyGraphGetStarterPacksWithMembership,
310
+ ),
311
+ },
312
+ )
313
+
314
+ expect(view.data.starterPacksWithMembership.length).toBe(3)
315
+ const memberships = membershipsUris(view.data.starterPacksWithMembership)
316
+ expect(memberships.length).toBe(1)
317
+ })
318
+
319
+ it('finds that user has no memberships', async () => {
320
+ // @NOTE: dan is not in bob's SP.
321
+ const view = await agent.app.bsky.graph.getStarterPacksWithMembership(
322
+ { actor: sc.dids.dan },
323
+ {
324
+ headers: await network.serviceHeaders(
325
+ sc.dids.bob,
326
+ ids.AppBskyGraphGetStarterPacksWithMembership,
327
+ ),
328
+ },
329
+ )
330
+
331
+ expect(view.data.starterPacksWithMembership.length).toBe(1)
332
+ const memberships = membershipsUris(view.data.starterPacksWithMembership)
333
+ expect(memberships.length).toBe(0)
334
+ })
335
+
336
+ it('finds empty list of SPs if user has none', async () => {
337
+ const view = await agent.app.bsky.graph.getStarterPacksWithMembership(
338
+ { actor: sc.dids.bob },
339
+ {
340
+ headers: await network.serviceHeaders(
341
+ sc.dids.carol,
342
+ ids.AppBskyGraphGetStarterPacksWithMembership,
343
+ ),
344
+ },
345
+ )
346
+
347
+ expect(view.data.starterPacksWithMembership.length).toBe(0)
348
+ })
349
+
350
+ it('paginates SPs with memberships', async () => {
351
+ const viewer = sc.dids.alice
352
+ const actor = sc.dids.bob
353
+
354
+ const results = (out: GetStarterPacksWithMembershipOutputSchema[]) =>
355
+ out.flatMap((res) => res.starterPacksWithMembership)
356
+ const paginator = async (cursor?: string) => {
357
+ const res = await agent.app.bsky.graph.getStarterPacksWithMembership(
358
+ { actor, limit: 2, cursor },
359
+ {
360
+ headers: await network.serviceHeaders(
361
+ viewer,
362
+ ids.AppBskyGraphGetStarterPacksWithMembership,
363
+ ),
364
+ },
365
+ )
366
+ return res.data
367
+ }
368
+
369
+ const paginatedAll = await paginateAll(paginator)
370
+ paginatedAll.forEach((res) =>
371
+ expect(res.starterPacksWithMembership.length).toBeLessThanOrEqual(2),
372
+ )
373
+
374
+ const full = await agent.app.bsky.graph.getStarterPacksWithMembership(
375
+ { actor },
376
+ {
377
+ headers: await network.serviceHeaders(
378
+ viewer,
379
+ ids.AppBskyGraphGetStarterPacksWithMembership,
380
+ ),
381
+ },
382
+ )
383
+ expect(full.data.starterPacksWithMembership.length).toBe(3)
384
+
385
+ const sortedFull = results([full.data]).sort((a, b) =>
386
+ a.starterPack.uri > b.starterPack.uri ? 1 : -1,
387
+ )
388
+ const sortedPaginated = results(paginatedAll).sort((a, b) =>
389
+ a.starterPack.uri > b.starterPack.uri ? 1 : -1,
390
+ )
391
+ expect(sortedPaginated).toEqual(sortedFull)
392
+ })
393
+ })
265
394
  })