@atproto/bsky 0.0.237 → 0.0.238

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 +11 -0
  2. package/dist/api/com/atproto/repo/getRecord.js +2 -2
  3. package/dist/api/com/atproto/repo/getRecord.js.map +1 -1
  4. package/dist/hydration/hydrator.d.ts.map +1 -1
  5. package/dist/hydration/hydrator.js +3 -2
  6. package/dist/hydration/hydrator.js.map +1 -1
  7. package/dist/hydration/label.d.ts +1 -1
  8. package/dist/hydration/label.d.ts.map +1 -1
  9. package/dist/hydration/label.js +4 -4
  10. package/dist/hydration/label.js.map +1 -1
  11. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
  12. package/dist/lexicons/app/bsky/actor/status.defs.d.ts.map +1 -1
  13. package/dist/lexicons/app/bsky/draft/defs.defs.d.ts +22 -0
  14. package/dist/lexicons/app/bsky/draft/defs.defs.d.ts.map +1 -1
  15. package/dist/lexicons/app/bsky/draft/defs.defs.js +11 -0
  16. package/dist/lexicons/app/bsky/draft/defs.defs.js.map +1 -1
  17. package/dist/lexicons/app/bsky/embed/gallery.d.ts +3 -0
  18. package/dist/lexicons/app/bsky/embed/gallery.d.ts.map +1 -0
  19. package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts +130 -0
  20. package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts.map +1 -0
  21. package/dist/lexicons/app/bsky/embed/gallery.defs.js +47 -0
  22. package/dist/lexicons/app/bsky/embed/gallery.defs.js.map +1 -0
  23. package/dist/lexicons/app/bsky/embed/gallery.js +6 -0
  24. package/dist/lexicons/app/bsky/embed/gallery.js.map +1 -0
  25. package/dist/lexicons/app/bsky/embed/record.defs.d.ts +2 -1
  26. package/dist/lexicons/app/bsky/embed/record.defs.d.ts.map +1 -1
  27. package/dist/lexicons/app/bsky/embed/record.defs.js +2 -0
  28. package/dist/lexicons/app/bsky/embed/record.defs.js.map +1 -1
  29. package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts +13 -12
  30. package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts.map +1 -1
  31. package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.js +3 -0
  32. package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.js.map +1 -1
  33. package/dist/lexicons/app/bsky/embed.d.ts +1 -0
  34. package/dist/lexicons/app/bsky/embed.d.ts.map +1 -1
  35. package/dist/lexicons/app/bsky/embed.js +1 -0
  36. package/dist/lexicons/app/bsky/embed.js.map +1 -1
  37. package/dist/lexicons/app/bsky/feed/defs.defs.d.ts +2 -1
  38. package/dist/lexicons/app/bsky/feed/defs.defs.d.ts.map +1 -1
  39. package/dist/lexicons/app/bsky/feed/defs.defs.js +2 -0
  40. package/dist/lexicons/app/bsky/feed/defs.defs.js.map +1 -1
  41. package/dist/lexicons/app/bsky/feed/generator.defs.d.ts.map +1 -1
  42. package/dist/lexicons/app/bsky/feed/like.defs.d.ts.map +1 -1
  43. package/dist/lexicons/app/bsky/feed/post.defs.d.ts +12 -11
  44. package/dist/lexicons/app/bsky/feed/post.defs.d.ts.map +1 -1
  45. package/dist/lexicons/app/bsky/feed/post.defs.js +2 -0
  46. package/dist/lexicons/app/bsky/feed/post.defs.js.map +1 -1
  47. package/dist/lexicons/app/bsky/feed/postgate.defs.d.ts.map +1 -1
  48. package/dist/lexicons/app/bsky/feed/repost.defs.d.ts.map +1 -1
  49. package/dist/lexicons/app/bsky/feed/threadgate.defs.d.ts.map +1 -1
  50. package/dist/lexicons/app/bsky/graph/block.defs.d.ts.map +1 -1
  51. package/dist/lexicons/app/bsky/graph/follow.defs.d.ts.map +1 -1
  52. package/dist/lexicons/app/bsky/graph/list.defs.d.ts.map +1 -1
  53. package/dist/lexicons/app/bsky/graph/listblock.defs.d.ts.map +1 -1
  54. package/dist/lexicons/app/bsky/graph/listitem.defs.d.ts.map +1 -1
  55. package/dist/lexicons/app/bsky/graph/starterpack.defs.d.ts.map +1 -1
  56. package/dist/lexicons/app/bsky/graph/verification.defs.d.ts.map +1 -1
  57. package/dist/lexicons/app/bsky/labeler/service.defs.d.ts.map +1 -1
  58. package/dist/lexicons/app/bsky/notification/declaration.defs.d.ts.map +1 -1
  59. package/dist/lexicons/chat/bsky/actor/declaration.defs.d.ts.map +1 -1
  60. package/dist/lexicons/com/atproto/lexicon/schema.defs.d.ts.map +1 -1
  61. package/dist/lexicons/com/germnetwork/declaration.defs.d.ts.map +1 -1
  62. package/dist/lexicons/site/standard/document.defs.d.ts.map +1 -1
  63. package/dist/lexicons/site/standard/graph/recommend.defs.d.ts.map +1 -1
  64. package/dist/lexicons/site/standard/graph/subscription.defs.d.ts.map +1 -1
  65. package/dist/lexicons/site/standard/publication.defs.d.ts.map +1 -1
  66. package/dist/lexicons/site/standard/theme/basic.defs.d.ts.map +1 -1
  67. package/dist/views/index.d.ts +4 -1
  68. package/dist/views/index.d.ts.map +1 -1
  69. package/dist/views/index.js +39 -5
  70. package/dist/views/index.js.map +1 -1
  71. package/dist/views/types.d.ts +8 -2
  72. package/dist/views/types.d.ts.map +1 -1
  73. package/dist/views/types.js +2 -0
  74. package/dist/views/types.js.map +1 -1
  75. package/package.json +7 -7
  76. package/src/api/com/atproto/repo/getRecord.ts +2 -2
  77. package/src/hydration/hydrator.ts +3 -6
  78. package/src/hydration/label.ts +3 -3
  79. package/src/views/index.ts +61 -11
  80. package/src/views/types.ts +9 -0
  81. package/tests/views/__snapshots__/posts.test.ts.snap +251 -0
  82. package/tests/views/drafts.test.ts +105 -0
  83. package/tests/views/posts.test.ts +134 -0
  84. package/tsconfig.build.tsbuildinfo +1 -1
@@ -8,6 +8,7 @@ import {
8
8
  vi,
9
9
  } from 'vitest'
10
10
  import {
11
+ $Typed,
11
12
  AppBskyDraftCreateDraft,
12
13
  AppBskyDraftDefs,
13
14
  AppBskyDraftGetDrafts,
@@ -302,6 +303,110 @@ describe('appview drafts views', () => {
302
303
  expect(paginated.at(-1)?.id).toBe(full.at(-1)?.id)
303
304
  })
304
305
  })
306
+
307
+ describe('gallery embed', () => {
308
+ const galleryItem = (
309
+ i: number,
310
+ ): $Typed<AppBskyDraftDefs.DraftEmbedImage> => ({
311
+ $type: 'app.bsky.draft.defs#draftEmbedImage',
312
+ localRef: { path: `/local/img-${i}.jpg` },
313
+ alt: `image ${i}`,
314
+ })
315
+
316
+ const galleryDraft = (size: number): AppBskyDraftDefs.Draft => ({
317
+ posts: [
318
+ {
319
+ text: 'gallery draft',
320
+ embedGallery: {
321
+ items: Array.from({ length: size }, (_, i) => galleryItem(i)),
322
+ },
323
+ },
324
+ ],
325
+ })
326
+
327
+ it('round-trips a draft with embedGallery', async () => {
328
+ await create(alice, galleryDraft(3))
329
+ const { data } = await get(alice)
330
+ expect(data.drafts).toHaveLength(1)
331
+
332
+ const post = data.drafts[0].draft.posts[0]
333
+ expect(post.embedGallery?.items).toHaveLength(3)
334
+ post.embedGallery?.items.forEach((item, i) => {
335
+ expect(item.$type).toBe('app.bsky.draft.defs#draftEmbedImage')
336
+ if (AppBskyDraftDefs.isDraftEmbedImage(item)) {
337
+ expect(item.localRef.path).toBe(`/local/img-${i}.jpg`)
338
+ expect(item.alt).toBe(`image ${i}`)
339
+ }
340
+ })
341
+ })
342
+
343
+ it('updates a draft to add a gallery', async () => {
344
+ await create(alice, { posts: [{ text: 'text only' }] })
345
+ const { data: before } = await get(alice)
346
+ expect(before.drafts).toHaveLength(1)
347
+ expect(before.drafts[0].draft.posts[0].embedGallery).toBeUndefined()
348
+
349
+ const draftId = before.drafts[0].id
350
+ await update(alice, {
351
+ id: draftId,
352
+ draft: galleryDraft(2),
353
+ })
354
+
355
+ const { data: after } = await get(alice)
356
+ expect(after.drafts).toHaveLength(1)
357
+ expect(after.drafts[0].id).toBe(draftId)
358
+ expect(after.drafts[0].draft.posts[0].embedGallery?.items).toHaveLength(2)
359
+ })
360
+
361
+ it('updates a draft to change gallery items', async () => {
362
+ await create(alice, galleryDraft(2))
363
+ const { data: before } = await get(alice)
364
+ expect(before.drafts[0].draft.posts[0].embedGallery?.items).toHaveLength(
365
+ 2,
366
+ )
367
+
368
+ const draftId = before.drafts[0].id
369
+ await update(alice, {
370
+ id: draftId,
371
+ draft: galleryDraft(5),
372
+ })
373
+
374
+ const { data: after } = await get(alice)
375
+ const post = after.drafts[0].draft.posts[0]
376
+ expect(post.embedGallery?.items).toHaveLength(5)
377
+ // Confirm full replacement (new items 0..4, not appended onto old 0..1).
378
+ post.embedGallery?.items.forEach((item, i) => {
379
+ if (AppBskyDraftDefs.isDraftEmbedImage(item)) {
380
+ expect(item.localRef.path).toBe(`/local/img-${i}.jpg`)
381
+ }
382
+ })
383
+ })
384
+
385
+ it('rejects embedGallery.items exceeding maxLength=20', async () => {
386
+ await expect(create(alice, galleryDraft(21))).rejects.toThrow()
387
+ })
388
+
389
+ it('rejects gallery items without $type', async () => {
390
+ const badDraft = {
391
+ posts: [
392
+ {
393
+ text: 'gallery without $type',
394
+ embedGallery: {
395
+ items: [
396
+ // Union members must be $type-tagged. Cast away types so the
397
+ // request reaches the server, where lex validation rejects it.
398
+ {
399
+ localRef: { path: '/local/untagged.jpg' },
400
+ alt: 'untagged',
401
+ } as unknown as $Typed<AppBskyDraftDefs.DraftEmbedImage>,
402
+ ],
403
+ },
404
+ },
405
+ ],
406
+ }
407
+ await expect(create(alice, badDraft)).rejects.toThrow()
408
+ })
409
+ })
305
410
  })
306
411
 
307
412
  const clearDrafts = async (db: Database) => {
@@ -1,5 +1,6 @@
1
1
  import { afterAll, beforeAll, describe, expect, it } from 'vitest'
2
2
  import {
3
+ AppBskyEmbedGallery,
3
4
  AppBskyEmbedRecord,
4
5
  AppBskyEmbedRecordWithMedia,
5
6
  AppBskyEmbedVideo,
@@ -291,4 +292,137 @@ describe('pds posts views', () => {
291
292
  expect(data.posts.length).toBe(1)
292
293
  expect(forSnapshot(data.posts[0])).toMatchSnapshot()
293
294
  })
295
+
296
+ it('embeds gallery.', async () => {
297
+ const img1 = await sc.uploadFile(
298
+ sc.dids.alice,
299
+ '../dev-env/assets/key-landscape-small.jpg',
300
+ 'image/jpeg',
301
+ )
302
+ const img2 = await sc.uploadFile(
303
+ sc.dids.alice,
304
+ '../dev-env/assets/key-portrait-small.jpg',
305
+ 'image/jpeg',
306
+ )
307
+ const { uri } = await pdsAgent.api.app.bsky.feed.post.create(
308
+ { repo: sc.dids.alice },
309
+ {
310
+ text: 'gallery',
311
+ createdAt: new Date().toISOString(),
312
+ embed: {
313
+ $type: 'app.bsky.embed.gallery',
314
+ items: [
315
+ {
316
+ $type: 'app.bsky.embed.gallery#image',
317
+ image: img1.image,
318
+ alt: 'landscape',
319
+ aspectRatio: { width: 4, height: 3 },
320
+ },
321
+ {
322
+ $type: 'app.bsky.embed.gallery#image',
323
+ image: img2.image,
324
+ alt: 'portrait',
325
+ aspectRatio: { width: 3, height: 4 },
326
+ },
327
+ ],
328
+ } satisfies AppBskyEmbedGallery.Main,
329
+ },
330
+ sc.getHeaders(sc.dids.alice),
331
+ )
332
+ await network.processAll()
333
+ const { data } = await agent.app.bsky.feed.getPosts({ uris: [uri] })
334
+ expect(data.posts.length).toBe(1)
335
+ expect(forSnapshot(data.posts[0])).toMatchSnapshot()
336
+ })
337
+
338
+ it('embeds gallery with record.', async () => {
339
+ const img = await sc.uploadFile(
340
+ sc.dids.alice,
341
+ '../dev-env/assets/key-landscape-small.jpg',
342
+ 'image/jpeg',
343
+ )
344
+ const embedRecord = await pdsAgent.api.app.bsky.feed.post.create(
345
+ { repo: sc.dids.alice },
346
+ {
347
+ text: 'embedded',
348
+ createdAt: new Date().toISOString(),
349
+ },
350
+ sc.getHeaders(sc.dids.alice),
351
+ )
352
+ const { uri } = await pdsAgent.api.app.bsky.feed.post.create(
353
+ { repo: sc.dids.alice },
354
+ {
355
+ text: 'gallery + record',
356
+ createdAt: new Date().toISOString(),
357
+ embed: {
358
+ $type: 'app.bsky.embed.recordWithMedia',
359
+ record: {
360
+ record: {
361
+ uri: embedRecord.uri,
362
+ cid: embedRecord.cid,
363
+ },
364
+ } satisfies AppBskyEmbedRecord.Main,
365
+ media: {
366
+ $type: 'app.bsky.embed.gallery',
367
+ items: [
368
+ {
369
+ $type: 'app.bsky.embed.gallery#image',
370
+ image: img.image,
371
+ alt: 'landscape',
372
+ aspectRatio: { width: 4, height: 3 },
373
+ },
374
+ ],
375
+ } satisfies AppBskyEmbedGallery.Main,
376
+ } satisfies AppBskyEmbedRecordWithMedia.Main,
377
+ },
378
+ sc.getHeaders(sc.dids.alice),
379
+ )
380
+ await network.processAll()
381
+ const { data } = await agent.app.bsky.feed.getPosts({ uris: [uri] })
382
+ expect(data.posts.length).toBe(1)
383
+ expect(forSnapshot(data.posts[0])).toMatchSnapshot()
384
+ })
385
+
386
+ it('truncates gallery view to soft limit of 10 items.', async () => {
387
+ const img = await sc.uploadFile(
388
+ sc.dids.alice,
389
+ '../dev-env/assets/key-landscape-small.jpg',
390
+ 'image/jpeg',
391
+ )
392
+ const items: AppBskyEmbedGallery.Main['items'] = Array.from(
393
+ { length: 11 },
394
+ (_, i) => ({
395
+ $type: 'app.bsky.embed.gallery#image',
396
+ image: img.image,
397
+ alt: `item ${i}`,
398
+ aspectRatio: { width: 4, height: 3 },
399
+ }),
400
+ )
401
+ const { uri } = await pdsAgent.api.app.bsky.feed.post.create(
402
+ { repo: sc.dids.alice },
403
+ {
404
+ text: 'oversize gallery',
405
+ createdAt: new Date().toISOString(),
406
+ embed: {
407
+ $type: 'app.bsky.embed.gallery',
408
+ items,
409
+ } satisfies AppBskyEmbedGallery.Main,
410
+ },
411
+ sc.getHeaders(sc.dids.alice),
412
+ )
413
+ await network.processAll()
414
+ const { data } = await agent.app.bsky.feed.getPosts({ uris: [uri] })
415
+ expect(data.posts.length).toBe(1)
416
+ const embed = data.posts[0].embed
417
+ if (!embed || !AppBskyEmbedGallery.isView(embed)) {
418
+ throw new Error('expected gallery view')
419
+ }
420
+ expect(embed.items).toHaveLength(10)
421
+ // Verify the AppView keeps the head of the items list (not the tail).
422
+ embed.items.forEach((item, i) => {
423
+ if (AppBskyEmbedGallery.isViewImage(item)) {
424
+ expect(item.alt).toBe(`item ${i}`)
425
+ }
426
+ })
427
+ })
294
428
  })