@sanity/sdk 0.0.0-alpha.21 → 0.0.0-alpha.23

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 (127) hide show
  1. package/dist/index.d.ts +428 -325
  2. package/dist/index.js +1618 -1553
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -7
  5. package/src/_exports/index.ts +31 -30
  6. package/src/auth/authStore.test.ts +149 -104
  7. package/src/auth/authStore.ts +51 -100
  8. package/src/auth/handleAuthCallback.test.ts +67 -34
  9. package/src/auth/handleAuthCallback.ts +8 -7
  10. package/src/auth/logout.test.ts +61 -29
  11. package/src/auth/logout.ts +26 -28
  12. package/src/auth/refreshStampedToken.test.ts +9 -9
  13. package/src/auth/refreshStampedToken.ts +62 -56
  14. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
  15. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
  16. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
  17. package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
  18. package/src/client/clientStore.test.ts +131 -67
  19. package/src/client/clientStore.ts +117 -116
  20. package/src/comlink/controller/actions/destroyController.test.ts +38 -13
  21. package/src/comlink/controller/actions/destroyController.ts +11 -15
  22. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
  23. package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
  24. package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
  25. package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
  26. package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
  27. package/src/comlink/controller/actions/releaseChannel.ts +22 -21
  28. package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
  29. package/src/comlink/controller/comlinkControllerStore.ts +44 -5
  30. package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
  31. package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
  32. package/src/comlink/node/actions/releaseNode.test.ts +75 -55
  33. package/src/comlink/node/actions/releaseNode.ts +19 -21
  34. package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
  35. package/src/comlink/node/comlinkNodeStore.ts +22 -5
  36. package/src/config/authConfig.ts +79 -0
  37. package/src/config/sanityConfig.ts +48 -0
  38. package/src/datasets/datasets.test.ts +2 -2
  39. package/src/datasets/datasets.ts +18 -5
  40. package/src/document/actions.test.ts +22 -10
  41. package/src/document/actions.ts +44 -56
  42. package/src/document/applyDocumentActions.test.ts +96 -36
  43. package/src/document/applyDocumentActions.ts +140 -99
  44. package/src/document/documentStore.test.ts +103 -155
  45. package/src/document/documentStore.ts +247 -237
  46. package/src/document/listen.ts +56 -55
  47. package/src/document/patchOperations.ts +0 -43
  48. package/src/document/permissions.test.ts +25 -12
  49. package/src/document/permissions.ts +11 -4
  50. package/src/document/processActions.test.ts +41 -8
  51. package/src/document/reducers.test.ts +87 -16
  52. package/src/document/reducers.ts +2 -2
  53. package/src/document/sharedListener.test.ts +34 -16
  54. package/src/document/sharedListener.ts +33 -11
  55. package/src/preview/getPreviewState.test.ts +40 -39
  56. package/src/preview/getPreviewState.ts +68 -56
  57. package/src/preview/previewConstants.ts +43 -0
  58. package/src/preview/previewQuery.test.ts +1 -1
  59. package/src/preview/previewQuery.ts +4 -5
  60. package/src/preview/previewStore.test.ts +13 -58
  61. package/src/preview/previewStore.ts +7 -21
  62. package/src/preview/resolvePreview.test.ts +33 -104
  63. package/src/preview/resolvePreview.ts +11 -21
  64. package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
  65. package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
  66. package/src/preview/util.ts +1 -0
  67. package/src/project/project.test.ts +3 -3
  68. package/src/project/project.ts +28 -5
  69. package/src/projection/getProjectionState.test.ts +69 -49
  70. package/src/projection/getProjectionState.ts +42 -50
  71. package/src/projection/projectionQuery.ts +1 -1
  72. package/src/projection/projectionStore.test.ts +13 -51
  73. package/src/projection/projectionStore.ts +6 -18
  74. package/src/projection/resolveProjection.test.ts +32 -127
  75. package/src/projection/resolveProjection.ts +15 -28
  76. package/src/projection/subscribeToStateAndFetchBatches.test.ts +105 -90
  77. package/src/projection/subscribeToStateAndFetchBatches.ts +94 -81
  78. package/src/projection/util.ts +2 -0
  79. package/src/projects/projects.test.ts +13 -4
  80. package/src/projects/projects.ts +6 -1
  81. package/src/query/queryStore.test.ts +10 -47
  82. package/src/query/queryStore.ts +151 -133
  83. package/src/query/queryStoreConstants.ts +2 -0
  84. package/src/store/createActionBinder.test.ts +153 -0
  85. package/src/store/createActionBinder.ts +176 -0
  86. package/src/store/createSanityInstance.test.ts +84 -0
  87. package/src/store/createSanityInstance.ts +124 -0
  88. package/src/store/createStateSourceAction.test.ts +196 -0
  89. package/src/store/createStateSourceAction.ts +260 -0
  90. package/src/store/createStoreInstance.test.ts +81 -0
  91. package/src/store/createStoreInstance.ts +80 -0
  92. package/src/store/createStoreState.test.ts +85 -0
  93. package/src/store/createStoreState.ts +92 -0
  94. package/src/store/defineStore.test.ts +18 -0
  95. package/src/store/defineStore.ts +81 -0
  96. package/src/users/reducers.test.ts +318 -0
  97. package/src/users/reducers.ts +88 -0
  98. package/src/users/types.ts +46 -4
  99. package/src/users/usersConstants.ts +4 -0
  100. package/src/users/usersStore.test.ts +350 -223
  101. package/src/users/usersStore.ts +285 -149
  102. package/src/utils/createFetcherStore.test.ts +6 -7
  103. package/src/utils/createFetcherStore.ts +150 -153
  104. package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
  105. package/src/auth/fetchLoginUrls.test.ts +0 -163
  106. package/src/auth/fetchLoginUrls.ts +0 -74
  107. package/src/common/createLiveEventSubscriber.test.ts +0 -121
  108. package/src/common/createLiveEventSubscriber.ts +0 -55
  109. package/src/common/types.ts +0 -4
  110. package/src/instance/identity.test.ts +0 -46
  111. package/src/instance/identity.ts +0 -29
  112. package/src/instance/sanityInstance.test.ts +0 -77
  113. package/src/instance/sanityInstance.ts +0 -57
  114. package/src/instance/types.ts +0 -37
  115. package/src/preview/getPreviewProjection.ts +0 -45
  116. package/src/resources/README.md +0 -370
  117. package/src/resources/createAction.test.ts +0 -101
  118. package/src/resources/createAction.ts +0 -44
  119. package/src/resources/createResource.test.ts +0 -112
  120. package/src/resources/createResource.ts +0 -102
  121. package/src/resources/createStateSourceAction.test.ts +0 -114
  122. package/src/resources/createStateSourceAction.ts +0 -83
  123. package/src/resources/createStore.test.ts +0 -67
  124. package/src/resources/createStore.ts +0 -46
  125. package/src/store/createStore.test.ts +0 -108
  126. package/src/store/createStore.ts +0 -106
  127. /package/src/{common/util.ts → utils/hashString.ts} +0 -0
@@ -0,0 +1,48 @@
1
+ import {type SanityDocumentLike} from '@sanity/types'
2
+
3
+ import {type AuthConfig} from './authConfig'
4
+
5
+ /**
6
+ * @public
7
+ */
8
+ export interface ProjectHandle {
9
+ projectId?: string | undefined
10
+ }
11
+
12
+ /**
13
+ * @public
14
+ */
15
+ export interface DatasetHandle extends ProjectHandle {
16
+ dataset?: string | undefined
17
+ }
18
+
19
+ /** @public */
20
+ export interface DocumentTypeHandle<TDocument extends SanityDocumentLike = SanityDocumentLike>
21
+ extends DatasetHandle {
22
+ documentId?: string
23
+ documentType: TDocument['_type']
24
+ }
25
+
26
+ /**
27
+ * @public
28
+ * A minimal set of metadata for a given document, comprising the document's ID and type.
29
+ * Used by most document-related hooks (such as {@link usePreview}, {@link useDocument}, and {@link useEditDocument})
30
+ * to reference a particular document without fetching the entire document upfront.
31
+ * @category Types
32
+ */
33
+ export interface DocumentHandle<TDocument extends SanityDocumentLike = SanityDocumentLike>
34
+ extends DocumentTypeHandle<TDocument> {
35
+ documentId: string
36
+ }
37
+
38
+ /**
39
+ * Represents the complete configuration for a Sanity SDK instance
40
+ * @public
41
+ */
42
+ export interface SanityConfig extends DatasetHandle {
43
+ /**
44
+ * Authentication configuration for the instance
45
+ * @remarks Merged with parent configurations when using createChild
46
+ */
47
+ auth?: AuthConfig
48
+ }
@@ -3,8 +3,8 @@ import {of} from 'rxjs'
3
3
  import {describe, it} from 'vitest'
4
4
 
5
5
  import {getClientState} from '../client/clientStore'
6
- import {createSanityInstance} from '../instance/sanityInstance'
7
- import {type StateSource} from '../resources/createStateSourceAction'
6
+ import {createSanityInstance} from '../store/createSanityInstance'
7
+ import {type StateSource} from '../store/createStateSourceAction'
8
8
  import {resolveDatasets} from './datasets'
9
9
 
10
10
  vi.mock('../client/clientStore')
@@ -1,16 +1,29 @@
1
1
  import {switchMap} from 'rxjs'
2
2
 
3
3
  import {getClientState} from '../client/clientStore'
4
+ import {type ProjectHandle} from '../config/sanityConfig'
4
5
  import {createFetcherStore} from '../utils/createFetcherStore'
5
6
 
7
+ const API_VERSION = 'v2025-02-19'
8
+
6
9
  /** @public */
7
10
  export const datasets = createFetcherStore({
8
11
  name: 'Datasets',
9
- getKey: () => 'datasets',
10
- fetcher: (instance) => () =>
11
- getClientState(instance, {apiVersion: 'vX', scope: 'project'}).observable.pipe(
12
- switchMap((client) => client.observable.datasets.list()),
13
- ),
12
+ getKey: (instance, options?: ProjectHandle) => {
13
+ const projectId = options?.projectId ?? instance.config.projectId
14
+ if (!projectId) {
15
+ throw new Error('A projectId is required to use the project API.')
16
+ }
17
+ return projectId
18
+ },
19
+ fetcher: (instance) => (options?: ProjectHandle) => {
20
+ return getClientState(instance, {
21
+ apiVersion: API_VERSION,
22
+ // non-null assertion is fine because we check above
23
+ projectId: (options?.projectId ?? instance.config.projectId)!,
24
+ useProjectHostname: true,
25
+ }).observable.pipe(switchMap((client) => client.observable.datasets.list()))
26
+ },
14
27
  })
15
28
 
16
29
  /** @public */
@@ -2,6 +2,7 @@ import {at, patch, set, setIfMissing} from '@sanity/mutate'
2
2
  import {type PatchOperations} from '@sanity/types'
3
3
  import {describe, expect, it} from 'vitest'
4
4
 
5
+ import {type DocumentHandle} from '../config/sanityConfig'
5
6
  import {
6
7
  createDocument,
7
8
  deleteDocument,
@@ -10,14 +11,13 @@ import {
10
11
  publishDocument,
11
12
  unpublishDocument,
12
13
  } from '../document/actions'
13
- import {type DocumentHandle} from './patchOperations'
14
14
 
15
15
  const dummyPatch: PatchOperations = {
16
16
  diffMatchPatch: {'dummy.path': 'dummy patch'},
17
17
  }
18
18
 
19
- const dummyDocHandle: DocumentHandle = {_id: 'drafts.abc123', _type: 'testType'}
20
- const dummyDocString = {_id: 'drafts.abc123', _type: 'testType'}
19
+ const dummyDocHandle: DocumentHandle = {documentId: 'drafts.abc123', documentType: 'testType'}
20
+ const dummyDocString = {documentId: 'drafts.abc123', documentType: 'testType'}
21
21
 
22
22
  describe('document actions', () => {
23
23
  describe('createDocument', () => {
@@ -26,20 +26,20 @@ describe('document actions', () => {
26
26
  expect(action).toEqual({
27
27
  type: 'document.create',
28
28
  // getId returns the input if it does not end with a dot.
29
- documentId: dummyDocHandle._id,
30
- documentType: dummyDocHandle._type,
29
+ documentId: 'abc123',
30
+ documentType: dummyDocHandle.documentType,
31
31
  })
32
32
  })
33
33
 
34
34
  it('creates a document action from a document type handle', () => {
35
35
  // A document type handle is similar to a document handle,
36
36
  // but _id is optional.
37
- const typeHandle = {_id: 'abc456', _type: 'anotherType'}
37
+ const typeHandle = {documentId: 'abc456', documentType: 'anotherType'}
38
38
  const action = createDocument(typeHandle)
39
39
  expect(action).toEqual({
40
40
  type: 'document.create',
41
- documentId: typeHandle._id,
42
- documentType: typeHandle._type,
41
+ documentId: 'abc456',
42
+ documentType: typeHandle.documentType,
43
43
  })
44
44
  })
45
45
  })
@@ -51,6 +51,7 @@ describe('document actions', () => {
51
51
  expect(action).toEqual({
52
52
  type: 'document.delete',
53
53
  documentId: 'abc123',
54
+ documentType: dummyDocString.documentType,
54
55
  })
55
56
  })
56
57
 
@@ -59,6 +60,7 @@ describe('document actions', () => {
59
60
  expect(action).toEqual({
60
61
  type: 'document.delete',
61
62
  documentId: 'abc123',
63
+ documentType: dummyDocHandle.documentType,
62
64
  })
63
65
  })
64
66
  })
@@ -69,6 +71,7 @@ describe('document actions', () => {
69
71
  expect(action).toEqual({
70
72
  type: 'document.edit',
71
73
  documentId: 'abc123',
74
+ documentType: dummyDocString.documentType,
72
75
  patches: [dummyPatch],
73
76
  })
74
77
  })
@@ -78,14 +81,16 @@ describe('document actions', () => {
78
81
  expect(action).toEqual({
79
82
  type: 'document.edit',
80
83
  documentId: 'abc123',
84
+ documentType: dummyDocHandle.documentType,
81
85
  patches: [dummyPatch],
82
86
  })
83
87
  })
84
88
 
85
89
  it('allows @sanity/mutate-style patches', () => {
86
90
  const action = editDocument(
91
+ dummyDocHandle,
87
92
  patch(
88
- dummyDocString._id,
93
+ dummyDocHandle.documentId,
89
94
  [
90
95
  at('published', set(true)),
91
96
  at('address', setIfMissing({_type: 'address'})),
@@ -96,7 +101,8 @@ describe('document actions', () => {
96
101
  )
97
102
 
98
103
  expect(action).toEqual({
99
- documentId: 'drafts.abc123',
104
+ documentId: 'abc123',
105
+ documentType: 'testType',
100
106
  patches: [
101
107
  {ifRevisionID: 'txn0', set: {published: true}},
102
108
  {ifRevisionID: 'txn0', setIfMissing: {address: {_type: 'address'}}},
@@ -113,6 +119,7 @@ describe('document actions', () => {
113
119
  expect(action).toEqual({
114
120
  type: 'document.publish',
115
121
  documentId: 'abc123',
122
+ documentType: dummyDocString.documentType,
116
123
  })
117
124
  })
118
125
 
@@ -121,6 +128,7 @@ describe('document actions', () => {
121
128
  expect(action).toEqual({
122
129
  type: 'document.publish',
123
130
  documentId: 'abc123',
131
+ documentType: dummyDocHandle.documentType,
124
132
  })
125
133
  })
126
134
  })
@@ -131,6 +139,7 @@ describe('document actions', () => {
131
139
  expect(action).toEqual({
132
140
  type: 'document.unpublish',
133
141
  documentId: 'abc123',
142
+ documentType: dummyDocString.documentType,
134
143
  })
135
144
  })
136
145
 
@@ -139,6 +148,7 @@ describe('document actions', () => {
139
148
  expect(action).toEqual({
140
149
  type: 'document.unpublish',
141
150
  documentId: 'abc123',
151
+ documentType: dummyDocHandle.documentType,
142
152
  })
143
153
  })
144
154
  })
@@ -149,6 +159,7 @@ describe('document actions', () => {
149
159
  expect(action).toEqual({
150
160
  type: 'document.discard',
151
161
  documentId: 'abc123',
162
+ documentType: dummyDocString.documentType,
152
163
  })
153
164
  })
154
165
 
@@ -157,6 +168,7 @@ describe('document actions', () => {
157
168
  expect(action).toEqual({
158
169
  type: 'document.discard',
159
170
  documentId: 'abc123',
171
+ documentType: dummyDocHandle.documentType,
160
172
  })
161
173
  })
162
174
  })
@@ -2,12 +2,8 @@ import {SanityEncoder} from '@sanity/mutate'
2
2
  import {type PatchMutation as SanityMutatePatchMutation} from '@sanity/mutate/_unstable_store'
3
3
  import {type PatchMutation, type PatchOperations, type SanityDocumentLike} from '@sanity/types'
4
4
 
5
+ import {type DocumentHandle, type DocumentTypeHandle} from '../config/sanityConfig'
5
6
  import {getPublishedId} from '../utils/ids'
6
- import {
7
- type DocumentHandle,
8
- type DocumentResourceId,
9
- type DocumentTypeHandle,
10
- } from './patchOperations'
11
7
 
12
8
  const isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation => {
13
9
  if (typeof value !== 'object' || !value) return false
@@ -18,51 +14,40 @@ const isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation
18
14
  }
19
15
 
20
16
  /** @beta */
21
- export interface CreateDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike> {
17
+ export interface CreateDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike>
18
+ extends DocumentTypeHandle<TDocument> {
22
19
  type: 'document.create'
23
- documentId?: string
24
- resourceId?: DocumentResourceId
25
- documentType: TDocument['_type']
26
20
  }
27
21
 
28
- // the unused `_TDocument` is primarily for typescript meta-programming to
29
- // capture and preserve the document type as best as possible
30
22
  /** @beta */
31
- export interface DeleteDocumentAction<_TDocument extends SanityDocumentLike = SanityDocumentLike> {
23
+ export interface DeleteDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike>
24
+ extends DocumentHandle<TDocument> {
32
25
  type: 'document.delete'
33
- documentId: string
34
- resourceId?: DocumentResourceId
35
26
  }
36
27
 
37
28
  /** @beta */
38
- export interface EditDocumentAction<_TDocument extends SanityDocumentLike = SanityDocumentLike> {
29
+ export interface EditDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike>
30
+ extends DocumentHandle<TDocument> {
39
31
  type: 'document.edit'
40
- documentId: string
41
- resourceId?: DocumentResourceId
42
32
  patches?: PatchOperations[]
43
33
  }
44
34
 
45
35
  /** @beta */
46
- export interface PublishDocumentAction<_TDocument extends SanityDocumentLike = SanityDocumentLike> {
36
+ export interface PublishDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike>
37
+ extends DocumentHandle<TDocument> {
47
38
  type: 'document.publish'
48
- documentId: string
49
- resourceId?: DocumentResourceId
50
39
  }
51
40
 
52
41
  /** @beta */
53
- export interface UnpublishDocumentAction<
54
- _TDocument extends SanityDocumentLike = SanityDocumentLike,
55
- > {
42
+ export interface UnpublishDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike>
43
+ extends DocumentHandle<TDocument> {
56
44
  type: 'document.unpublish'
57
- documentId: string
58
- resourceId?: DocumentResourceId
59
45
  }
60
46
 
61
47
  /** @beta */
62
- export interface DiscardDocumentAction<_TDocument extends SanityDocumentLike = SanityDocumentLike> {
48
+ export interface DiscardDocumentAction<TDocument extends SanityDocumentLike = SanityDocumentLike>
49
+ extends DocumentHandle<TDocument> {
63
50
  type: 'document.discard'
64
- documentId: string
65
- resourceId?: DocumentResourceId
66
51
  }
67
52
 
68
53
  /** @beta */
@@ -76,13 +61,12 @@ export type DocumentAction<TDocument extends SanityDocumentLike = SanityDocument
76
61
 
77
62
  /** @beta */
78
63
  export function createDocument<TDocument extends SanityDocumentLike>(
79
- doc: DocumentTypeHandle<TDocument> | DocumentHandle<TDocument>,
64
+ doc: DocumentTypeHandle<TDocument>,
80
65
  ): CreateDocumentAction<TDocument> {
81
66
  return {
82
67
  type: 'document.create',
83
- ...(doc._id && {documentId: doc._id}),
84
- documentType: doc._type,
85
- ...(doc.resourceId && {resourceId: doc.resourceId}),
68
+ ...doc,
69
+ ...(doc.documentId && {documentId: getPublishedId(doc.documentId)}),
86
70
  }
87
71
  }
88
72
 
@@ -92,29 +76,25 @@ export function deleteDocument<TDocument extends SanityDocumentLike>(
92
76
  ): DeleteDocumentAction<TDocument> {
93
77
  return {
94
78
  type: 'document.delete',
95
- documentId: getPublishedId(doc._id),
96
- ...(doc.resourceId && {resourceId: doc.resourceId}),
79
+ ...doc,
80
+ documentId: getPublishedId(doc.documentId),
97
81
  }
98
82
  }
99
83
 
100
84
  function convertSanityMutatePatch(
101
85
  sanityPatchMutation: SanityMutatePatchMutation,
102
- ): EditDocumentAction {
86
+ ): EditDocumentAction['patches'] {
103
87
  const encoded = SanityEncoder.encode(sanityPatchMutation) as PatchMutation[]
104
-
105
- return {
106
- documentId: sanityPatchMutation.id,
107
- type: 'document.edit',
108
- patches: encoded.map((i) => {
109
- const copy: PatchOperations = {...i.patch}
110
- if ('id' in copy) delete copy.id
111
- return copy
112
- }),
113
- }
88
+ return encoded.map((i) => {
89
+ const copy: PatchOperations = {...i.patch}
90
+ if ('id' in copy) delete copy.id
91
+ return copy
92
+ })
114
93
  }
115
94
 
116
95
  /** @beta */
117
96
  export function editDocument<TDocument extends SanityDocumentLike>(
97
+ doc: DocumentHandle<TDocument>,
118
98
  sanityMutatePatch: SanityMutatePatchMutation,
119
99
  ): EditDocumentAction<TDocument>
120
100
  /** @beta */
@@ -124,16 +104,24 @@ export function editDocument<TDocument extends SanityDocumentLike>(
124
104
  ): EditDocumentAction<TDocument>
125
105
  /** @beta */
126
106
  export function editDocument<TDocument extends SanityDocumentLike>(
127
- doc: SanityMutatePatchMutation | DocumentHandle<TDocument>,
128
- patches?: PatchOperations | PatchOperations[],
107
+ doc: DocumentHandle<TDocument>,
108
+ patches?: PatchOperations | PatchOperations[] | SanityMutatePatchMutation,
129
109
  ): EditDocumentAction<TDocument> {
130
- if (isSanityMutatePatch(doc)) return convertSanityMutatePatch(doc)
110
+ if (isSanityMutatePatch(patches)) {
111
+ const converted = convertSanityMutatePatch(patches) ?? []
112
+ return {
113
+ ...doc,
114
+ type: 'document.edit',
115
+ documentId: getPublishedId(doc.documentId),
116
+ patches: converted,
117
+ }
118
+ }
131
119
 
132
120
  return {
121
+ ...doc,
133
122
  type: 'document.edit',
134
- documentId: getPublishedId(doc._id),
123
+ documentId: getPublishedId(doc.documentId),
135
124
  ...(patches && {patches: Array.isArray(patches) ? patches : [patches]}),
136
- ...(doc.resourceId && {resourceId: doc.resourceId}),
137
125
  }
138
126
  }
139
127
 
@@ -143,8 +131,8 @@ export function publishDocument<TDocument extends SanityDocumentLike>(
143
131
  ): PublishDocumentAction<TDocument> {
144
132
  return {
145
133
  type: 'document.publish',
146
- documentId: getPublishedId(doc._id),
147
- ...(doc.resourceId && {resourceId: doc.resourceId}),
134
+ ...doc,
135
+ documentId: getPublishedId(doc.documentId),
148
136
  }
149
137
  }
150
138
 
@@ -154,8 +142,8 @@ export function unpublishDocument<TDocument extends SanityDocumentLike>(
154
142
  ): UnpublishDocumentAction<TDocument> {
155
143
  return {
156
144
  type: 'document.unpublish',
157
- documentId: getPublishedId(doc._id),
158
- ...(doc.resourceId && {resourceId: doc.resourceId}),
145
+ ...doc,
146
+ documentId: getPublishedId(doc.documentId),
159
147
  }
160
148
  }
161
149
 
@@ -165,7 +153,7 @@ export function discardDocument<TDocument extends SanityDocumentLike>(
165
153
  ): DiscardDocumentAction<TDocument> {
166
154
  return {
167
155
  type: 'document.discard',
168
- documentId: getPublishedId(doc._id),
169
- ...(doc.resourceId && {resourceId: doc.resourceId}),
156
+ ...doc,
157
+ documentId: getPublishedId(doc.documentId),
170
158
  }
171
159
  }
@@ -3,15 +3,20 @@ import {type SanityDocument} from '@sanity/types'
3
3
  import {Subject} from 'rxjs'
4
4
  import {describe, expect, it} from 'vitest'
5
5
 
6
- import {createSanityInstance} from '../instance/sanityInstance'
7
- import {type ActionContext} from '../resources/createAction'
8
- import {createResourceState} from '../resources/createResource'
6
+ import {bindActionByDataset} from '../store/createActionBinder'
7
+ import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
8
+ import {} from '../store/createStateSourceAction'
9
+ import {createStoreState, type StoreState} from '../store/createStoreState'
9
10
  import {type DocumentAction} from './actions'
10
- import {applyDocumentActions} from './applyDocumentActions'
11
11
  import {type DocumentStoreState} from './documentStore'
12
12
  import {type DocumentEvent} from './events'
13
13
  import {type AppliedTransaction, type OutgoingTransaction} from './reducers'
14
14
 
15
+ vi.mock('../store/createActionBinder', async (importOriginal) => ({
16
+ ...(await importOriginal<typeof import('../store/createActionBinder')>()),
17
+ bindActionByDataset: vi.fn(),
18
+ }))
19
+
15
20
  type TestState = Pick<
16
21
  DocumentStoreState,
17
22
  'documentStates' | 'queued' | 'applied' | 'outgoing' | 'error' | 'events'
@@ -26,8 +31,13 @@ const exampleDoc: SanityDocument = {
26
31
  }
27
32
 
28
33
  describe('applyDocumentActions', () => {
29
- it('resolves with a successful applied transaction and accepted event', async () => {
30
- const eventsSubject = new Subject<DocumentEvent>()
34
+ let state: StoreState<TestState>
35
+ let instance: SanityInstance
36
+ let eventsSubject: Subject<DocumentEvent>
37
+ let applyDocumentActions: typeof import('./applyDocumentActions').applyDocumentActions
38
+
39
+ beforeEach(async () => {
40
+ eventsSubject = new Subject<DocumentEvent>()
31
41
  const initialState: TestState = {
32
42
  documentStates: {},
33
43
  queued: [],
@@ -36,24 +46,35 @@ describe('applyDocumentActions', () => {
36
46
  error: undefined,
37
47
  events: eventsSubject,
38
48
  }
39
- const state = createResourceState(initialState)
40
- const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
41
- const context: ActionContext<TestState> = {instance, state}
49
+ state = createStoreState(initialState)
50
+ instance = createSanityInstance({projectId: 'p', dataset: 'd'})
51
+
52
+ vi.mocked(bindActionByDataset).mockImplementation(
53
+ (_storeDef, action) =>
54
+ (instanceParam: SanityInstance, ...params: unknown[]) =>
55
+ action({instance: instanceParam, state}, ...params),
56
+ )
57
+ // Import dynamically to ensure mocks are set up before the module under test is loaded
58
+ const module = await import('./applyDocumentActions')
59
+ applyDocumentActions = module.applyDocumentActions
60
+ })
61
+
62
+ afterEach(() => {
63
+ instance.dispose()
64
+ })
42
65
 
66
+ it('resolves with a successful applied transaction and accepted event', async () => {
43
67
  const action: DocumentAction = {
44
68
  type: 'document.edit',
45
69
  documentId: 'doc1',
70
+ documentType: 'example',
46
71
  patches: [{set: {foo: 'bar'}}],
47
72
  }
48
73
 
49
74
  // Call applyDocumentActions with a fixed transactionId for reproducibility.
50
- const applyPromise = applyDocumentActions(
51
- context as ActionContext<DocumentStoreState>,
52
- action,
53
- {
54
- transactionId: 'txn-success',
55
- },
56
- )
75
+ const applyPromise = applyDocumentActions(instance, action, {
76
+ transactionId: 'txn-success',
77
+ })
57
78
 
58
79
  const appliedTx: AppliedTransaction = {
59
80
  transactionId: 'txn-success',
@@ -99,33 +120,17 @@ describe('applyDocumentActions', () => {
99
120
  })
100
121
 
101
122
  it('throws an error if a transaction error event is emitted', async () => {
102
- const eventsSubject = new Subject<DocumentEvent>()
103
- const initialState: TestState = {
104
- documentStates: {},
105
- queued: [],
106
- applied: [],
107
- outgoing: undefined,
108
- error: undefined,
109
- events: eventsSubject,
110
- }
111
- const state = createResourceState(initialState)
112
- const instance = createSanityInstance({projectId: 'p', dataset: 'd'})
113
- const context: ActionContext<TestState> = {instance, state}
114
-
115
123
  const action: DocumentAction = {
116
124
  type: 'document.edit',
117
125
  documentId: 'doc1',
126
+ documentType: 'example',
118
127
  patches: [{set: {foo: 'error'}}],
119
128
  }
120
129
 
121
130
  // Call applyDocumentActions with a fixed transactionId.
122
- const applyPromise = applyDocumentActions(
123
- context as ActionContext<DocumentStoreState>,
124
- action,
125
- {
126
- transactionId: 'txn-error',
127
- },
128
- )
131
+ const applyPromise = applyDocumentActions(instance, action, {
132
+ transactionId: 'txn-error',
133
+ })
129
134
 
130
135
  const errorEvent: DocumentEvent = {
131
136
  type: 'error',
@@ -138,4 +143,59 @@ describe('applyDocumentActions', () => {
138
143
 
139
144
  await expect(applyPromise).rejects.toThrow('Simulated error')
140
145
  })
146
+
147
+ it('matches parent instance via child when action projectId and dataset do not match child config', async () => {
148
+ // Create a parent instance
149
+ const parentInstance = createSanityInstance({projectId: 'p', dataset: 'd'})
150
+ // Create a child instance with different config
151
+ const childInstance = parentInstance.createChild({projectId: 'child-p', dataset: 'child-d'})
152
+ // Use the child instance in context
153
+ // Create an action that refers to the parent's configuration
154
+ const action: DocumentAction = {
155
+ type: 'document.edit',
156
+ documentId: 'doc1',
157
+ documentType: 'example',
158
+ patches: [{set: {foo: 'childTest'}}],
159
+ projectId: 'p',
160
+ dataset: 'd',
161
+ }
162
+ // Call applyDocumentActions with the context using childInstance, but with action requiring parent's config
163
+ const applyPromise = applyDocumentActions(childInstance, action, {
164
+ transactionId: 'txn-child-match',
165
+ })
166
+
167
+ // Simulate an applied transaction on the parent's instance
168
+ const appliedTx: AppliedTransaction = {
169
+ transactionId: 'txn-child-match',
170
+ actions: [action],
171
+ disableBatching: false,
172
+ outgoingActions: [],
173
+ outgoingMutations: [],
174
+ base: {doc1: {...exampleDoc, _id: 'doc1', foo: 'old', _rev: 'rev-old'}},
175
+ working: {doc1: {...exampleDoc, _id: 'doc1', foo: 'childTest', _rev: 'rev-new'}},
176
+ previous: {doc1: {...exampleDoc, _id: 'doc1', foo: 'old', _rev: 'rev-old'}},
177
+ previousRevs: {doc1: 'rev-old'},
178
+ timestamp: new Date().toISOString(),
179
+ }
180
+ state.set('simulateApplied', {applied: [appliedTx]})
181
+
182
+ const result = await applyPromise
183
+ expect(result.transactionId).toEqual('txn-child-match')
184
+ expect(result.documents).toEqual(appliedTx.working)
185
+ expect(result.previous).toEqual(appliedTx.previous)
186
+ expect(result.previousRevs).toEqual(appliedTx.previousRevs)
187
+
188
+ const acceptedResult = {transactionId: 'accepted-child'}
189
+ const acceptedEvent: DocumentEvent = {
190
+ type: 'accepted',
191
+ outgoing: {batchedTransactionIds: ['txn-child-match']} as OutgoingTransaction,
192
+ result: acceptedResult,
193
+ }
194
+ eventsSubject.next(acceptedEvent)
195
+ const submittedResult = await result.submitted()
196
+ expect(submittedResult).toEqual(acceptedResult)
197
+
198
+ childInstance.dispose()
199
+ parentInstance.dispose()
200
+ })
141
201
  })