@sanity/sdk 2.9.0 → 2.11.0

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 (73) hide show
  1. package/dist/_chunks-dts/utils.d.ts +295 -69
  2. package/dist/_chunks-es/_internal.js +3 -14
  3. package/dist/_chunks-es/_internal.js.map +1 -1
  4. package/dist/_chunks-es/createGroqSearchFilter.js +129 -59
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
  6. package/dist/_chunks-es/version.js +1 -1
  7. package/dist/_exports/_internal.d.ts +16 -2
  8. package/dist/_exports/_internal.js +3 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +275 -149
  11. package/dist/index.js.map +1 -1
  12. package/package.json +11 -15
  13. package/src/_exports/_internal.ts +1 -0
  14. package/src/_exports/index.ts +33 -2
  15. package/src/agent/agentActions.ts +21 -25
  16. package/src/client/clientStore.test.ts +24 -60
  17. package/src/client/clientStore.ts +49 -56
  18. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  19. package/src/comlink/node/actions/getOrCreateNode.test.ts +5 -2
  20. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  21. package/src/comlink/node/actions/releaseNode.test.ts +3 -3
  22. package/src/config/sanityConfig.ts +72 -13
  23. package/src/document/applyDocumentActions.test.ts +7 -7
  24. package/src/document/applyDocumentActions.ts +5 -5
  25. package/src/document/documentStore.test.ts +68 -62
  26. package/src/document/documentStore.ts +33 -38
  27. package/src/document/processActions.ts +2 -2
  28. package/src/document/reducers.ts +4 -4
  29. package/src/document/sharedListener.ts +5 -7
  30. package/src/organization/organization.test-d.ts +102 -0
  31. package/src/organization/organization.test.ts +138 -0
  32. package/src/organization/organization.ts +166 -0
  33. package/src/organizations/organizations.test-d.ts +77 -0
  34. package/src/organizations/organizations.test.ts +150 -0
  35. package/src/organizations/organizations.ts +132 -0
  36. package/src/presence/bifurTransport.test.ts +46 -6
  37. package/src/presence/bifurTransport.ts +13 -1
  38. package/src/presence/presenceStore.test.ts +101 -5
  39. package/src/presence/presenceStore.ts +96 -24
  40. package/src/preview/getPreviewState.ts +1 -1
  41. package/src/preview/previewProjectionUtils.test.ts +4 -4
  42. package/src/preview/previewProjectionUtils.ts +6 -7
  43. package/src/preview/resolvePreview.ts +5 -1
  44. package/src/project/project.test-d.ts +93 -0
  45. package/src/project/project.test.ts +108 -10
  46. package/src/project/project.ts +152 -26
  47. package/src/projection/getProjectionState.ts +4 -4
  48. package/src/projection/projectionStore.test.ts +2 -2
  49. package/src/projection/resolveProjection.ts +2 -2
  50. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  51. package/src/projection/subscribeToStateAndFetchBatches.ts +11 -15
  52. package/src/projects/projects.test-d.ts +38 -0
  53. package/src/projects/projects.test.ts +104 -38
  54. package/src/projects/projects.ts +74 -14
  55. package/src/query/queryStore.test.ts +12 -12
  56. package/src/query/queryStore.ts +10 -11
  57. package/src/query/reducers.ts +3 -3
  58. package/src/releases/getPerspectiveState.ts +5 -5
  59. package/src/releases/releasesStore.test.ts +6 -6
  60. package/src/releases/releasesStore.ts +9 -9
  61. package/src/store/createActionBinder.test.ts +31 -31
  62. package/src/store/createActionBinder.ts +43 -38
  63. package/src/store/createSanityInstance.ts +5 -6
  64. package/src/telemetry/devMode.test.ts +8 -0
  65. package/src/telemetry/devMode.ts +10 -9
  66. package/src/telemetry/initTelemetry.test.ts +0 -17
  67. package/src/telemetry/initTelemetry.ts +2 -12
  68. package/src/users/reducers.ts +3 -4
  69. package/src/utils/createFetcherStore.ts +6 -4
  70. package/src/utils/isImportError.test.ts +72 -0
  71. package/src/utils/isImportError.ts +34 -0
  72. package/src/utils/object.test.ts +95 -0
  73. package/src/utils/object.ts +142 -0
@@ -67,7 +67,7 @@ let instance: SanityInstance
67
67
  let instance1: SanityInstance
68
68
  let instance2: SanityInstance
69
69
 
70
- const source = {projectId: 'p', dataset: 'd'}
70
+ const resource = {projectId: 'p', dataset: 'd'}
71
71
  const source1 = {projectId: 'p', dataset: 'd1'}
72
72
  const source2 = {projectId: 'p', dataset: 'd2'}
73
73
 
@@ -99,7 +99,7 @@ it('creates, edits, and publishes a document', async () => {
99
99
  // Create a new document
100
100
  const {appeared} = await applyDocumentActions(instance, {
101
101
  actions: [createDocument(doc)],
102
- source,
102
+ resource,
103
103
  })
104
104
  expect(appeared).toContain(getDraftId(documentId))
105
105
 
@@ -109,7 +109,7 @@ it('creates, edits, and publishes a document', async () => {
109
109
  // Edit the document – add a title
110
110
  await applyDocumentActions(instance, {
111
111
  actions: [editDocument(doc, {set: {title: 'My First Article'}})],
112
- source,
112
+ resource,
113
113
  })
114
114
  currentDoc = documentState.getCurrent()
115
115
  expect(currentDoc?.title).toEqual('My First Article')
@@ -117,7 +117,7 @@ it('creates, edits, and publishes a document', async () => {
117
117
  // Publish the document; the resulting transactionId is used as the new _rev
118
118
  const {transactionId, submitted} = await applyDocumentActions(instance, {
119
119
  actions: [publishDocument(doc)],
120
- source,
120
+ resource,
121
121
  })
122
122
  await submitted()
123
123
  currentDoc = documentState.getCurrent()
@@ -144,7 +144,7 @@ it('creates a document with initial values', async () => {
144
144
  count: 42,
145
145
  }),
146
146
  ],
147
- source,
147
+ resource,
148
148
  })
149
149
  expect(appeared).toContain(getDraftId(documentId))
150
150
 
@@ -177,7 +177,7 @@ it('edits existing documents', async () => {
177
177
 
178
178
  await applyDocumentActions(instance, {
179
179
  actions: [editDocument(doc, {set: {title: 'updated title'}})],
180
- source,
180
+ resource,
181
181
  })
182
182
  expect(state.getCurrent()).toMatchObject({
183
183
  _id: getDraftId(documentId),
@@ -208,11 +208,11 @@ it('sets optimistic changes synchronously', async () => {
208
208
 
209
209
  // then the actions are synchronous
210
210
  expect(state1.getCurrent()).toBeNull()
211
- applyDocumentActions(instance1, {actions: [createDocument(doc1)], source: source1})
211
+ applyDocumentActions(instance1, {actions: [createDocument(doc1)], resource: source1})
212
212
  expect(state1.getCurrent()).toMatchObject({_id: getDraftId(doc1.documentId)})
213
213
  const actionResult1Promise = applyDocumentActions(instance1, {
214
214
  actions: [editDocument(doc1, {set: {title: 'initial title'}})],
215
- source: source1,
215
+ resource: source1,
216
216
  })
217
217
  expect(state1.getCurrent()?.title).toBe('initial title')
218
218
 
@@ -233,7 +233,7 @@ it('sets optimistic changes synchronously', async () => {
233
233
  // synchronous for state 2
234
234
  const actionResult2Promise = applyDocumentActions(instance2, {
235
235
  actions: [editDocument(doc2, {set: {title: 'updated title'}})],
236
- source: source2,
236
+ resource: source2,
237
237
  })
238
238
  expect(state2.getCurrent()?.title).toBe('updated title')
239
239
  // async for state 1
@@ -256,7 +256,7 @@ it('propagates changes between two instances', async () => {
256
256
  const state2Unsubscribe = state2.subscribe()
257
257
 
258
258
  // Create the document from instance1.
259
- await applyDocumentActions(instance1, {actions: [createDocument(doc)], source: source1}).then(
259
+ await applyDocumentActions(instance1, {actions: [createDocument(doc)], resource: source1}).then(
260
260
  (r) => r.submitted(),
261
261
  )
262
262
 
@@ -268,7 +268,7 @@ it('propagates changes between two instances', async () => {
268
268
  // Now, edit the document from instance2.
269
269
  await applyDocumentActions(instance2, {
270
270
  actions: [editDocument(doc, {set: {title: 'Hello world!'}})],
271
- source: source2,
271
+ resource: source2,
272
272
  }).then((r) => r.submitted())
273
273
 
274
274
  const updated1 = state1.getCurrent()
@@ -296,19 +296,19 @@ it('handles concurrent edits and resolves conflicts', async () => {
296
296
  createDocument(doc),
297
297
  editDocument(doc, {set: {title: 'The quick brown fox jumps over the lazy dog'}}),
298
298
  ],
299
- source,
299
+ resource,
300
300
  }).then((res) => res.submitted())
301
301
 
302
302
  // Both instances now issue an edit simultaneously.
303
303
  const p1 = applyDocumentActions(instance1, {
304
304
  actions: [editDocument(doc, {set: {title: 'The quick brown fox jumps over the lazy cat'}})],
305
- source: source1,
305
+ resource: source1,
306
306
  }).then((r) => r.submitted())
307
307
  const p2 = applyDocumentActions(instance2, {
308
308
  actions: [
309
309
  editDocument(doc, {set: {title: 'The quick brown elephant jumps over the lazy dog'}}),
310
310
  ],
311
- source: source2,
311
+ resource: source2,
312
312
  }).then((r) => r.submitted())
313
313
 
314
314
  // Wait for both actions to complete (or reject).
@@ -331,10 +331,10 @@ it('unpublishes and discards a document', async () => {
331
331
  const unsubscribe = documentState.subscribe()
332
332
 
333
333
  // Create and publish the document.
334
- await applyDocumentActions(instance, {actions: [createDocument(doc)], source})
334
+ await applyDocumentActions(instance, {actions: [createDocument(doc)], resource})
335
335
  const afterPublish = await applyDocumentActions(instance, {
336
336
  actions: [publishDocument(doc)],
337
- source,
337
+ resource,
338
338
  })
339
339
  const publishedDoc = documentState.getCurrent()
340
340
  expect(publishedDoc).toMatchObject({
@@ -343,13 +343,13 @@ it('unpublishes and discards a document', async () => {
343
343
  })
344
344
 
345
345
  // Unpublish the document (which should delete the published version and create a draft).
346
- await applyDocumentActions(instance, {actions: [unpublishDocument(doc)], source})
346
+ await applyDocumentActions(instance, {actions: [unpublishDocument(doc)], resource})
347
347
  const afterUnpublish = documentState.getCurrent()
348
348
  // In our mock implementation the _id remains the same but the published copy is removed.
349
349
  expect(afterUnpublish?._id).toEqual(getDraftId(documentId))
350
350
 
351
351
  // Discard the draft (which deletes the draft version).
352
- await applyDocumentActions(instance, {actions: [discardDocument(doc)], source})
352
+ await applyDocumentActions(instance, {actions: [discardDocument(doc)], resource})
353
353
  const afterDiscard = documentState.getCurrent()
354
354
  expect(afterDiscard).toBeNull()
355
355
 
@@ -365,13 +365,13 @@ it('deletes a document', async () => {
365
365
 
366
366
  await applyDocumentActions(instance, {
367
367
  actions: [createDocument(doc), publishDocument(doc)],
368
- source,
368
+ resource,
369
369
  })
370
370
  const docValue = documentState.getCurrent()
371
371
  expect(docValue).toBeDefined()
372
372
 
373
373
  // Delete the document.
374
- await applyDocumentActions(instance, {actions: [deleteDocument(doc)], source})
374
+ await applyDocumentActions(instance, {actions: [deleteDocument(doc)], resource})
375
375
  const afterDelete = documentState.getCurrent()
376
376
  expect(afterDelete).toBeNull()
377
377
 
@@ -387,7 +387,7 @@ it('cleans up document state when there are no subscribers', async () => {
387
387
  const unsubscribe = documentState.subscribe()
388
388
 
389
389
  // Create a document.
390
- await applyDocumentActions(instance, {actions: [createDocument(doc)], source})
390
+ await applyDocumentActions(instance, {actions: [createDocument(doc)], resource})
391
391
  expect(documentState.getCurrent()).toBeDefined()
392
392
 
393
393
  // Unsubscribe from the document.
@@ -414,7 +414,7 @@ it('fetches documents if there are no active subscriptions for the actions appli
414
414
  // transaction for this action has been accepted by the server
415
415
  const setNewTitle = applyDocumentActions(instance, {
416
416
  actions: [editDocument(doc, {set: {title: 'new title'}})],
417
- source,
417
+ resource,
418
418
  })
419
419
  expect(getCurrent()?.title).toBeUndefined()
420
420
  expect(getDocumentSyncStatus(instance, doc).getCurrent()).toBe(false)
@@ -425,12 +425,12 @@ it('fetches documents if there are no active subscriptions for the actions appli
425
425
  // there is an active subscriber now so the edits are synchronous
426
426
  applyDocumentActions(instance, {
427
427
  actions: [editDocument(doc, {set: {title: 'updated title'}})],
428
- source,
428
+ resource,
429
429
  })
430
430
  expect(getCurrent()?.title).toBe('updated title')
431
431
  applyDocumentActions(instance, {
432
432
  actions: [editDocument(doc, {set: {title: 'updated title!'}})],
433
- source,
433
+ resource,
434
434
  })
435
435
  expect(getCurrent()?.title).toBe('updated title!')
436
436
 
@@ -439,7 +439,7 @@ it('fetches documents if there are no active subscriptions for the actions appli
439
439
  // await submitted in order to test that there is no subscriptions
440
440
  const result = await applyDocumentActions(instance, {
441
441
  actions: [editDocument(doc, {set: {title: 'updated title'}})],
442
- source,
442
+ resource,
443
443
  })
444
444
  await result.submitted()
445
445
 
@@ -448,7 +448,7 @@ it('fetches documents if there are no active subscriptions for the actions appli
448
448
 
449
449
  const setNewNewTitle = applyDocumentActions(instance, {
450
450
  actions: [editDocument(doc, {set: {title: 'new new title'}})],
451
- source,
451
+ resource,
452
452
  })
453
453
  // now we'll have to await again
454
454
  expect(getCurrent()?.title).toBe(undefined)
@@ -463,15 +463,18 @@ it('batches edit transaction into one outgoing transaction', async () => {
463
463
  const unsubscribe = getDocumentState(instance, doc).subscribe()
464
464
 
465
465
  // this creates its own transaction
466
- applyDocumentActions(instance, {actions: [createDocument(doc)], source})
466
+ applyDocumentActions(instance, {actions: [createDocument(doc)], resource})
467
467
 
468
468
  // these get batched into one
469
- applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'name!'}})], source})
470
- applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'name!!'}})], source})
471
- applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'name!!!'}})], source})
469
+ applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'name!'}})], resource})
470
+ applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'name!!'}})], resource})
471
+ applyDocumentActions(instance, {
472
+ actions: [editDocument(doc, {set: {title: 'name!!!'}})],
473
+ resource,
474
+ })
472
475
  const res = await applyDocumentActions(instance, {
473
476
  actions: [editDocument(doc, {set: {title: 'name!!!!'}})],
474
- source,
477
+ resource,
475
478
  })
476
479
  await res.submitted()
477
480
 
@@ -503,7 +506,7 @@ it('submits liveEdit document edits through observable.mutate', async () => {
503
506
 
504
507
  const result = await applyDocumentActions(instance, {
505
508
  actions: [editDocument(liveDoc, {set: {title: 'patched via mutate'}})],
506
- source,
509
+ resource,
507
510
  })
508
511
  await result.submitted()
509
512
 
@@ -539,7 +542,7 @@ it('provides the consistency status via `getDocumentSyncStatus`', async () => {
539
542
  const unsubscribe = syncStatus.subscribe()
540
543
  expect(syncStatus.getCurrent()).toBe(true)
541
544
 
542
- const applied = applyDocumentActions(instance, {actions: [createDocument(doc)], source})
545
+ const applied = applyDocumentActions(instance, {actions: [createDocument(doc)], resource})
543
546
  expect(syncStatus.getCurrent()).toBe(false)
544
547
 
545
548
  const createResult = await applied
@@ -550,15 +553,15 @@ it('provides the consistency status via `getDocumentSyncStatus`', async () => {
550
553
 
551
554
  applyDocumentActions(instance, {
552
555
  actions: [editDocument(doc, {set: {title: 'initial name'}})],
553
- source,
556
+ resource,
554
557
  })
555
558
  expect(syncStatus.getCurrent()).toBe(false)
556
559
 
557
560
  applyDocumentActions(instance, {
558
561
  actions: [editDocument(doc, {set: {title: 'updated name'}})],
559
- source,
562
+ resource,
560
563
  })
561
- const publishResult = applyDocumentActions(instance, {actions: [publishDocument(doc)], source})
564
+ const publishResult = applyDocumentActions(instance, {actions: [publishDocument(doc)], resource})
562
565
  expect(syncStatus.getCurrent()).toBe(false)
563
566
  await publishResult.then((res) => res.submitted())
564
567
  expect(syncStatus.getCurrent()).toBe(true)
@@ -576,7 +579,7 @@ it('reverts failed outgoing transaction locally', async () => {
576
579
 
577
580
  const revertedEventPromise = new Promise<TransactionRevertedEvent>((resolve) => {
578
581
  const unsubscribe = subscribeDocumentEvents(instance, {
579
- source,
582
+ resource,
580
583
  eventHandler: (e) => {
581
584
  if (e.type === 'reverted') {
582
585
  resolve(e)
@@ -592,11 +595,11 @@ it('reverts failed outgoing transaction locally', async () => {
592
595
  const {getCurrent, subscribe} = getDocumentState(instance, doc)
593
596
  const unsubscribe = subscribe()
594
597
 
595
- await applyDocumentActions(instance, {actions: [createDocument(doc)], source})
596
- applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'the'}})], source})
598
+ await applyDocumentActions(instance, {actions: [createDocument(doc)], resource})
599
+ applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'the'}})], resource})
597
600
  applyDocumentActions(instance, {
598
601
  actions: [editDocument(doc, {set: {title: 'the quick'}})],
599
- source,
602
+ resource,
600
603
  })
601
604
 
602
605
  // this edit action is simulated to fail from the backend and will be reverted
@@ -604,16 +607,16 @@ it('reverts failed outgoing transaction locally', async () => {
604
607
  actions: [editDocument(doc, {set: {title: 'the quick brown'}})],
605
608
  transactionId: 'force-revert',
606
609
  disableBatching: true,
607
- source,
610
+ resource,
608
611
  })
609
612
 
610
613
  applyDocumentActions(instance, {
611
614
  actions: [editDocument(doc, {set: {title: 'the quick brown fox'}})],
612
- source,
615
+ resource,
613
616
  })
614
617
  await applyDocumentActions(instance, {
615
618
  actions: [editDocument(doc, {set: {title: 'the quick brown fox jumps'}})],
616
- source,
619
+ resource,
617
620
  }).then((e) => e.submitted())
618
621
 
619
622
  await expect(revertedEventPromise).resolves.toMatchObject({
@@ -633,7 +636,7 @@ it('reverts failed outgoing transaction locally', async () => {
633
636
  // check that we can still edit after recovering from the error
634
637
  applyDocumentActions(instance, {
635
638
  actions: [editDocument(doc, {set: {title: 'TEST the quick fox jumps'}})],
636
- source,
639
+ resource,
637
640
  })
638
641
  expect(getCurrent()?.title).toBe('TEST the quick fox jumps')
639
642
 
@@ -644,7 +647,7 @@ it('reverts failed outgoing transaction locally', async () => {
644
647
  it('removes a queued transaction if it fails to apply', async () => {
645
648
  const actionErrorEventPromise = new Promise<ActionErrorEvent>((resolve) => {
646
649
  const unsubscribe = subscribeDocumentEvents(instance, {
647
- source,
650
+ resource,
648
651
  eventHandler: (e) => {
649
652
  if (e.type === 'error') {
650
653
  resolve(e)
@@ -661,7 +664,7 @@ it('removes a queued transaction if it fails to apply', async () => {
661
664
  await expect(
662
665
  applyDocumentActions(instance, {
663
666
  actions: [editDocument(doc, {set: {title: "can't set"}})],
664
- source,
667
+ resource,
665
668
  }),
666
669
  ).rejects.toThrowError(/Cannot edit document/)
667
670
 
@@ -672,8 +675,11 @@ it('removes a queued transaction if it fails to apply', async () => {
672
675
  })
673
676
 
674
677
  // editing should still work after though (no crashing)
675
- await applyDocumentActions(instance, {actions: [createDocument(doc)], source})
676
- applyDocumentActions(instance, {actions: [editDocument(doc, {set: {title: 'can set!'}})], source})
678
+ await applyDocumentActions(instance, {actions: [createDocument(doc)], resource})
679
+ applyDocumentActions(instance, {
680
+ actions: [editDocument(doc, {set: {title: 'can set!'}})],
681
+ resource,
682
+ })
677
683
 
678
684
  expect(state.getCurrent()?.title).toBe('can set!')
679
685
 
@@ -693,7 +699,7 @@ it('returns allowed true when no permission errors occur', async () => {
693
699
  })
694
700
  const state = getDocumentState(instance, doc)
695
701
  const unsubscribe = state.subscribe()
696
- await applyDocumentActions(instance, {actions: [createDocument(doc)], source}).then((r) =>
702
+ await applyDocumentActions(instance, {actions: [createDocument(doc)], resource}).then((r) =>
697
703
  r.submitted(),
698
704
  )
699
705
 
@@ -718,7 +724,7 @@ it("should reject applying the action if a precondition isn't met", async () =>
718
724
  const doc = createDocumentHandle({documentId: 'does-not-exist', documentType: 'article'})
719
725
 
720
726
  await expect(
721
- applyDocumentActions(instance, {actions: [deleteDocument(doc)], source}),
727
+ applyDocumentActions(instance, {actions: [deleteDocument(doc)], resource}),
722
728
  ).rejects.toThrow('The document you are trying to delete does not exist.')
723
729
  })
724
730
 
@@ -729,7 +735,7 @@ it("should reject applying the action if a permission isn't met", async () => {
729
735
  vi.mocked(client.request).mockResolvedValue(datasetAcl)
730
736
 
731
737
  await expect(
732
- applyDocumentActions(instance, {actions: [createDocument(doc)], source}),
738
+ applyDocumentActions(instance, {actions: [createDocument(doc)], resource}),
733
739
  ).rejects.toThrow('You do not have permission to create a draft for document "does-not-exist".')
734
740
  })
735
741
 
@@ -768,11 +774,11 @@ it('fetches dataset ACL and updates grants in the document store state', async (
768
774
  })
769
775
  })
770
776
 
771
- it('fetches ACL for MediaLibrarySource', async () => {
777
+ it('fetches ACL for MediaLibraryResource', async () => {
772
778
  const mediaLibraryInstance = createSanityInstance({
773
779
  projectId: 'p',
774
780
  dataset: 'd',
775
- sources: {
781
+ resources: {
776
782
  'media-library': {mediaLibraryId: 'test-media-library'},
777
783
  },
778
784
  })
@@ -785,18 +791,18 @@ it('fetches ACL for MediaLibrarySource', async () => {
785
791
 
786
792
  const result = await resolvePermissions(mediaLibraryInstance, {
787
793
  actions: [createDocument(doc)],
788
- source: mediaLibrarySource,
794
+ resource: mediaLibrarySource,
789
795
  })
790
796
 
791
797
  expect(result).toEqual({allowed: true})
792
798
  mediaLibraryInstance.dispose()
793
799
  })
794
800
 
795
- it('fetches ACL for CanvasSource', async () => {
801
+ it('fetches ACL for CanvasResource', async () => {
796
802
  const canvasInstance = createSanityInstance({
797
803
  projectId: 'p',
798
804
  dataset: 'd',
799
- sources: {
805
+ resources: {
800
806
  canvas: {canvasId: 'test-canvas'},
801
807
  },
802
808
  })
@@ -809,7 +815,7 @@ it('fetches ACL for CanvasSource', async () => {
809
815
 
810
816
  const result = await resolvePermissions(canvasInstance, {
811
817
  actions: [createDocument(doc)],
812
- source: canvasSource,
818
+ resource: canvasSource,
813
819
  })
814
820
 
815
821
  expect(result).toEqual({allowed: true})
@@ -826,7 +832,7 @@ it('returns a promise that resolves when a document has been loaded in the store
826
832
  const oneOffInstance = createSanityInstance({projectId: 'p', dataset: 'd'})
827
833
  const result = await applyDocumentActions(oneOffInstance, {
828
834
  actions: [createDocument(doc), editDocument(doc, {set: {title: 'initial title'}})],
829
- source,
835
+ resource,
830
836
  })
831
837
  await result.submitted() // wait till submitted to server before resolving
832
838
 
@@ -840,7 +846,7 @@ it('returns a promise that resolves when a document has been loaded in the store
840
846
 
841
847
  it('emits an event for each action after an outgoing transaction has been accepted', async () => {
842
848
  const handler = vi.fn()
843
- const unsubscribe = subscribeDocumentEvents(instance, {source, eventHandler: handler})
849
+ const unsubscribe = subscribeDocumentEvents(instance, {resource, eventHandler: handler})
844
850
 
845
851
  const documentId = DocumentId(crypto.randomUUID())
846
852
  const doc = createDocumentHandle({documentId, documentType: 'article'})
@@ -852,7 +858,7 @@ it('emits an event for each action after an outgoing transaction has been accept
852
858
  editDocument(doc, {set: {title: 'new name'}}),
853
859
  publishDocument(doc),
854
860
  ],
855
- source,
861
+ resource,
856
862
  }).then((e) => e.submitted())
857
863
  expect(handler).toHaveBeenCalledTimes(4)
858
864
 
@@ -863,7 +869,7 @@ it('emits an event for each action after an outgoing transaction has been accept
863
869
  editDocument(doc, {set: {title: 'updated name'}}),
864
870
  discardDocument(doc),
865
871
  ],
866
- source,
872
+ resource,
867
873
  }).then((e) => e.submitted())
868
874
  expect(handler).toHaveBeenCalledTimes(9)
869
875
 
@@ -879,7 +885,7 @@ it('emits an event for each action after an outgoing transaction has been accept
879
885
  [{type: 'accepted', outgoing: {transactionId: tnx2.transactionId}}],
880
886
  ])
881
887
 
882
- await applyDocumentActions(instance, {actions: [deleteDocument(doc)], source})
888
+ await applyDocumentActions(instance, {actions: [deleteDocument(doc)], resource})
883
889
 
884
890
  unsubscribe()
885
891
  })
@@ -29,15 +29,15 @@ import {
29
29
  import {type ClientOptions, getClientState} from '../client/clientStore'
30
30
  import {
31
31
  type DocumentHandle,
32
- type DocumentSource,
33
- isCanvasSource,
34
- isDatasetSource,
35
- isMediaLibrarySource,
32
+ type DocumentResource,
33
+ isCanvasResource,
34
+ isDatasetResource,
35
+ isMediaLibraryResource,
36
36
  } from '../config/sanityConfig'
37
37
  import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
38
38
  import {
39
- bindActionBySource,
40
- type BoundSourceKey,
39
+ bindActionByResource,
40
+ type BoundResourceKey,
41
41
  type StoreAction,
42
42
  } from '../store/createActionBinder'
43
43
  import {type SanityInstance} from '../store/createSanityInstance'
@@ -122,15 +122,15 @@ export interface DocumentState {
122
122
  unverifiedRevisions?: {[TTransactionId in string]?: UnverifiedDocumentRevision}
123
123
  }
124
124
 
125
- export const documentStore = defineStore<DocumentStoreState, BoundSourceKey>({
125
+ export const documentStore = defineStore<DocumentStoreState, BoundResourceKey>({
126
126
  name: 'Document',
127
- getInitialState: (instance, {source}) => ({
127
+ getInitialState: (instance, {resource}) => ({
128
128
  documentStates: {},
129
129
  // these can be emptied on refetch
130
130
  queued: [],
131
131
  applied: [],
132
- sharedListener: createSharedListener(instance, source),
133
- fetchDocument: createFetchDocument(instance, source),
132
+ sharedListener: createSharedListener(instance, resource),
133
+ fetchDocument: createFetchDocument(instance, resource),
134
134
  events: new Subject(),
135
135
  }),
136
136
  initialize(context) {
@@ -198,7 +198,7 @@ export function getDocumentState(
198
198
  return _getDocumentState(...args)
199
199
  }
200
200
 
201
- const _getDocumentState = bindActionBySource(
201
+ const _getDocumentState = bindActionByResource(
202
202
  documentStore,
203
203
  createStateSourceAction({
204
204
  selector: ({state: {error, documentStates}}, options: DocumentOptions<string | undefined>) => {
@@ -255,7 +255,7 @@ export function resolveDocument(
255
255
  ): Promise<SanityDocument | null> {
256
256
  return _resolveDocument(...args)
257
257
  }
258
- const _resolveDocument = bindActionBySource(
258
+ const _resolveDocument = bindActionByResource(
259
259
  documentStore,
260
260
  ({instance}, docHandle: DocumentHandle<string, string, string>) => {
261
261
  return firstValueFrom(
@@ -268,7 +268,7 @@ const _resolveDocument = bindActionBySource(
268
268
  )
269
269
 
270
270
  /** @beta */
271
- export const getDocumentSyncStatus = bindActionBySource(
271
+ export const getDocumentSyncStatus = bindActionByResource(
272
272
  documentStore,
273
273
  createStateSourceAction({
274
274
  selector: (
@@ -300,12 +300,12 @@ export const getDocumentSyncStatus = bindActionBySource(
300
300
  )
301
301
 
302
302
  type PermissionsStateOptions = {
303
- source?: DocumentSource
303
+ resource?: DocumentResource
304
304
  actions: DocumentAction[]
305
305
  }
306
306
 
307
307
  /** @beta */
308
- export const getPermissionsState = bindActionBySource(
308
+ export const getPermissionsState = bindActionByResource(
309
309
  documentStore,
310
310
  createStateSourceAction({
311
311
  selector: calculatePermissions,
@@ -320,7 +320,7 @@ export const getPermissionsState = bindActionBySource(
320
320
  )
321
321
 
322
322
  /** @beta */
323
- export const resolvePermissions = bindActionBySource(
323
+ export const resolvePermissions = bindActionByResource(
324
324
  documentStore,
325
325
  ({instance}, options: PermissionsStateOptions) => {
326
326
  return firstValueFrom(
@@ -330,9 +330,9 @@ export const resolvePermissions = bindActionBySource(
330
330
  )
331
331
 
332
332
  /** @beta */
333
- export const subscribeDocumentEvents = bindActionBySource(
333
+ export const subscribeDocumentEvents = bindActionByResource(
334
334
  documentStore,
335
- ({state}, options: {source?: DocumentSource; eventHandler: (e: DocumentEvent) => void}) => {
335
+ ({state}, options: {resource?: DocumentResource; eventHandler: (e: DocumentEvent) => void}) => {
336
336
  const {events} = state.get()
337
337
  const subscription = events.subscribe(options.eventHandler)
338
338
  return () => subscription.unsubscribe()
@@ -341,7 +341,7 @@ export const subscribeDocumentEvents = bindActionBySource(
341
341
 
342
342
  const subscribeToQueuedAndApplyNextTransaction = ({
343
343
  state,
344
- }: StoreContext<DocumentStoreState, BoundSourceKey>) => {
344
+ }: StoreContext<DocumentStoreState, BoundResourceKey>) => {
345
345
  const {events} = state.get()
346
346
  return state.observable
347
347
  .pipe(
@@ -372,8 +372,8 @@ const subscribeToQueuedAndApplyNextTransaction = ({
372
372
  const subscribeToAppliedAndSubmitNextTransaction = ({
373
373
  state,
374
374
  instance,
375
- key: {source},
376
- }: StoreContext<DocumentStoreState, BoundSourceKey>) => {
375
+ key: {resource},
376
+ }: StoreContext<DocumentStoreState, BoundResourceKey>) => {
377
377
  const {events} = state.get()
378
378
 
379
379
  return state.observable
@@ -396,8 +396,7 @@ const subscribeToAppliedAndSubmitNextTransaction = ({
396
396
  withLatestFrom(
397
397
  getClientState(instance, {
398
398
  apiVersion: API_VERSION,
399
- // TODO: remove in v3 when we're ready for everything to be queried via source
400
- source: source && !isDatasetSource(source) ? source : undefined,
399
+ resource,
401
400
  }).observable,
402
401
  ),
403
402
  concatMap(([outgoing, client]) => {
@@ -450,7 +449,7 @@ const subscribeToAppliedAndSubmitNextTransaction = ({
450
449
  }
451
450
 
452
451
  const subscribeToSubscriptionsAndListenToDocuments = (
453
- context: StoreContext<DocumentStoreState, BoundSourceKey>,
452
+ context: StoreContext<DocumentStoreState, BoundResourceKey>,
454
453
  ) => {
455
454
  const {state} = context
456
455
  const {events} = state.get()
@@ -518,23 +517,19 @@ const subscribeToSubscriptionsAndListenToDocuments = (
518
517
  const subscribeToClientAndFetchDatasetAcl = ({
519
518
  instance,
520
519
  state,
521
- key: {source},
522
- }: StoreContext<DocumentStoreState, BoundSourceKey>) => {
523
- const clientOptions: ClientOptions = {apiVersion: API_VERSION}
524
- // TODO: remove in v3 when we're ready for everything to be queried via source
525
- if (source && !isDatasetSource(source)) {
526
- clientOptions.source = source
527
- }
520
+ key: {resource},
521
+ }: StoreContext<DocumentStoreState, BoundResourceKey>) => {
522
+ const clientOptions: ClientOptions = {apiVersion: API_VERSION, resource}
528
523
 
529
524
  let uri: string
530
- if (source && isDatasetSource(source)) {
531
- uri = `/projects/${source.projectId}/datasets/${source.dataset}/acl`
532
- } else if (source && isMediaLibrarySource(source)) {
533
- uri = `/media-libraries/${source.mediaLibraryId}/acl`
534
- } else if (source && isCanvasSource(source)) {
535
- uri = `/canvases/${source.canvasId}/acl`
525
+ if (resource && isDatasetResource(resource)) {
526
+ uri = `/projects/${resource.projectId}/datasets/${resource.dataset}/acl`
527
+ } else if (resource && isMediaLibraryResource(resource)) {
528
+ uri = `/media-libraries/${resource.mediaLibraryId}/acl`
529
+ } else if (resource && isCanvasResource(resource)) {
530
+ uri = `/canvases/${resource.canvasId}/acl`
536
531
  } else {
537
- throw new Error(`Received invalid source: ${JSON.stringify(source)}`)
532
+ throw new Error(`Received invalid resource: ${JSON.stringify(resource)}`)
538
533
  }
539
534
 
540
535
  return getClientState(instance, clientOptions)
@@ -7,9 +7,9 @@ import {
7
7
  type SanityDocument,
8
8
  } from '@sanity/types'
9
9
  import {evaluateSync, type ExprNode} from 'groq-js'
10
- import {isEqual} from 'lodash-es'
11
10
 
12
11
  import {isReleasePerspective} from '../releases/utils/isReleasePerspective'
12
+ import {isDeepEqual} from '../utils/object'
13
13
  import {type DocumentAction} from './actions'
14
14
  import {type Grant} from './permissions'
15
15
  import {type DocumentSet, getId, processMutations} from './processMutations'
@@ -568,7 +568,7 @@ export function processActions({
568
568
 
569
569
  // Before proceeding, verify that the working draft is identical to the base draft.
570
570
  // TODO: is it enough just to check for the _rev or nah?
571
- if (!isEqual(workingDraft, baseDraft)) {
571
+ if (!isDeepEqual(workingDraft, baseDraft)) {
572
572
  throw new ActionError({
573
573
  documentId,
574
574
  transactionId,