@sanity/sdk 3.0.0-rc.0 → 3.0.0-rc.2

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 (241) hide show
  1. package/README.md +12 -0
  2. package/dist/_exports/_internal.d.ts +163 -0
  3. package/dist/_exports/_internal.d.ts.map +1 -0
  4. package/dist/_exports/_internal.js +332 -0
  5. package/dist/_exports/_internal.js.map +1 -0
  6. package/dist/_exports/agent.d.ts +65 -0
  7. package/dist/_exports/agent.d.ts.map +1 -0
  8. package/dist/_exports/agent.js +71 -0
  9. package/dist/_exports/agent.js.map +1 -0
  10. package/dist/_exports/comlink.d.ts +118 -0
  11. package/dist/_exports/comlink.d.ts.map +1 -0
  12. package/dist/_exports/comlink.js +69 -0
  13. package/dist/_exports/comlink.js.map +1 -0
  14. package/dist/authStore-CtE9bLHW.js +754 -0
  15. package/dist/authStore-CtE9bLHW.js.map +1 -0
  16. package/dist/authStore-DKFX8fn7.d.ts +3179 -0
  17. package/dist/authStore-DKFX8fn7.d.ts.map +1 -0
  18. package/dist/clientStore-BtE2-q2i.js +90 -0
  19. package/dist/clientStore-BtE2-q2i.js.map +1 -0
  20. package/dist/createSanityInstance-DUI1bGIg.d.ts +278 -0
  21. package/dist/createSanityInstance-DUI1bGIg.d.ts.map +1 -0
  22. package/dist/createStateSourceAction-BOKtqFNm.js +300 -0
  23. package/dist/createStateSourceAction-BOKtqFNm.js.map +1 -0
  24. package/dist/getEnv-D5RGkQLu.js +8 -0
  25. package/dist/getEnv-D5RGkQLu.js.map +1 -0
  26. package/dist/getNodeState-CR3__B2D.js +67 -0
  27. package/dist/getNodeState-CR3__B2D.js.map +1 -0
  28. package/dist/index.d.ts +3 -2332
  29. package/dist/index.js +4171 -4332
  30. package/dist/index.js.map +1 -1
  31. package/dist/logger-DAtJb5RQ.js +173 -0
  32. package/dist/logger-DAtJb5RQ.js.map +1 -0
  33. package/dist/object-CxXlSVg7.js +83 -0
  34. package/dist/object-CxXlSVg7.js.map +1 -0
  35. package/dist/queryStore-DPf9sN3V.js +1346 -0
  36. package/dist/queryStore-DPf9sN3V.js.map +1 -0
  37. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  38. package/dist/setCleanupTimeout-BiM6q1KF.js +31 -0
  39. package/dist/setCleanupTimeout-BiM6q1KF.js.map +1 -0
  40. package/dist/telemetryManager-Dq_qCkeI.js +127 -0
  41. package/dist/telemetryManager-Dq_qCkeI.js.map +1 -0
  42. package/dist/version-BChjsi4u.js +9 -0
  43. package/dist/version-BChjsi4u.js.map +1 -0
  44. package/package.json +47 -52
  45. package/src/_exports/_internal.ts +27 -0
  46. package/src/_exports/agent.ts +19 -0
  47. package/src/_exports/comlink.ts +16 -0
  48. package/src/_exports/index.ts +125 -64
  49. package/src/access/checkPermissions.test.ts +55 -0
  50. package/src/access/checkPermissions.ts +54 -0
  51. package/src/agent/agentActions.test.ts +16 -60
  52. package/src/agent/agentActions.ts +25 -38
  53. package/src/applications/applications.test-d.ts +88 -0
  54. package/src/applications/applications.test.ts +130 -0
  55. package/src/applications/applications.ts +367 -0
  56. package/src/auth/authLogger.ts +30 -0
  57. package/src/auth/authStore.test.ts +194 -13
  58. package/src/auth/authStore.ts +58 -26
  59. package/src/auth/getOrganizationVerificationState.test.ts +18 -17
  60. package/src/auth/getOrganizationVerificationState.ts +4 -4
  61. package/src/auth/handleAuthCallback.test.ts +35 -1
  62. package/src/auth/handleAuthCallback.ts +25 -6
  63. package/src/auth/logout.test.ts +75 -2
  64. package/src/auth/logout.ts +23 -4
  65. package/src/auth/refreshStampedToken.test.ts +219 -469
  66. package/src/auth/refreshStampedToken.ts +79 -195
  67. package/src/auth/studioAuth.ts +1 -1
  68. package/src/auth/studioModeAuth.ts +2 -1
  69. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +146 -13
  70. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +37 -6
  71. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +2 -0
  72. package/src/client/clientStore.test.ts +62 -37
  73. package/src/client/clientStore.ts +45 -75
  74. package/src/client/liveEvents.test.ts +151 -0
  75. package/src/client/liveEvents.ts +107 -0
  76. package/src/comlink/controller/actions/destroyController.test.ts +4 -1
  77. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +4 -1
  78. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  79. package/src/comlink/controller/actions/getOrCreateController.test.ts +4 -1
  80. package/src/comlink/controller/actions/releaseChannel.test.ts +1 -1
  81. package/src/comlink/controller/comlinkControllerStore.test.ts +4 -1
  82. package/src/comlink/node/actions/getOrCreateNode.test.ts +9 -3
  83. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  84. package/src/comlink/node/actions/releaseNode.test.ts +7 -4
  85. package/src/comlink/node/comlinkNodeStore.test.ts +2 -2
  86. package/src/comlink/node/getNodeState.test.ts +1 -1
  87. package/src/comlink/node/getNodeState.ts +2 -1
  88. package/src/comments/addonDatasetStore.test.ts +215 -0
  89. package/src/comments/addonDatasetStore.ts +249 -0
  90. package/src/comments/buildCommentThreads.test.ts +83 -0
  91. package/src/comments/buildCommentThreads.ts +54 -0
  92. package/src/comments/commentActions.test.ts +540 -0
  93. package/src/comments/commentActions.ts +555 -0
  94. package/src/comments/commentFieldPath.test.ts +40 -0
  95. package/src/comments/commentFieldPath.ts +44 -0
  96. package/src/comments/commentsConstants.ts +80 -0
  97. package/src/comments/commentsStore.test.ts +419 -0
  98. package/src/comments/commentsStore.ts +483 -0
  99. package/src/comments/normalizeComment.test.ts +116 -0
  100. package/src/comments/normalizeComment.ts +45 -0
  101. package/src/comments/observeComments.test.ts +193 -0
  102. package/src/comments/observeComments.ts +113 -0
  103. package/src/comments/reducers.test.ts +277 -0
  104. package/src/comments/reducers.ts +316 -0
  105. package/src/comments/types.ts +249 -0
  106. package/src/comments/weakenReferences.test.ts +47 -0
  107. package/src/comments/weakenReferences.ts +29 -0
  108. package/src/config/__tests__/handles.test.ts +18 -12
  109. package/src/config/handles.ts +25 -7
  110. package/src/config/sanityConfig.ts +40 -82
  111. package/src/datasets/datasets.test.ts +2 -2
  112. package/src/datasets/datasets.ts +10 -4
  113. package/src/document/actions.test.ts +115 -33
  114. package/src/document/actions.ts +212 -15
  115. package/src/document/applyDocumentActions.test.ts +35 -13
  116. package/src/document/applyDocumentActions.ts +30 -9
  117. package/src/document/documentConstants.ts +15 -0
  118. package/src/document/documentStore.concurrency.test.ts +1306 -0
  119. package/src/document/documentStore.test.ts +753 -273
  120. package/src/document/documentStore.ts +197 -75
  121. package/src/document/events.test.ts +57 -2
  122. package/src/document/events.ts +82 -26
  123. package/src/document/listen.test.ts +56 -0
  124. package/src/document/listen.ts +74 -21
  125. package/src/document/listenerEventOperators.test.ts +311 -0
  126. package/src/document/listenerEventOperators.ts +217 -0
  127. package/src/document/patchOperations.test.ts +76 -2
  128. package/src/document/patchOperations.ts +60 -1
  129. package/src/document/permissions.test.ts +107 -60
  130. package/src/document/permissions.ts +44 -19
  131. package/src/document/processActions/create.ts +138 -0
  132. package/src/document/processActions/delete.ts +100 -0
  133. package/src/document/processActions/discard.ts +63 -0
  134. package/src/document/processActions/edit.ts +154 -0
  135. package/src/document/processActions/processActions.ts +218 -0
  136. package/src/document/processActions/publish.ts +120 -0
  137. package/src/document/processActions/releaseArchive.ts +77 -0
  138. package/src/document/processActions/releaseCreate.ts +59 -0
  139. package/src/document/processActions/releaseDelete.ts +65 -0
  140. package/src/document/processActions/releaseEdit.ts +37 -0
  141. package/src/document/processActions/releasePublish.ts +45 -0
  142. package/src/document/processActions/releaseSchedule.ts +87 -0
  143. package/src/document/processActions/releaseUtil.ts +31 -0
  144. package/src/document/processActions/shared.ts +308 -0
  145. package/src/document/processActions/unpublish.ts +85 -0
  146. package/src/document/processActions.test.ts +1129 -121
  147. package/src/document/reducers.test.ts +772 -64
  148. package/src/document/reducers.ts +205 -51
  149. package/src/document/resourceRules.test.ts +178 -0
  150. package/src/document/resourceRules.ts +117 -0
  151. package/src/document/sharedListener.test.ts +16 -14
  152. package/src/document/sharedListener.ts +24 -8
  153. package/src/document/util.test.ts +46 -0
  154. package/src/document/util.ts +50 -0
  155. package/src/favorites/favorites.test.ts +5 -6
  156. package/src/installations/installations.test-d.ts +73 -0
  157. package/src/installations/installations.test.ts +62 -0
  158. package/src/installations/installations.ts +206 -0
  159. package/src/organization/organization.test-d.ts +36 -0
  160. package/src/organization/organization.test.ts +138 -0
  161. package/src/organization/organization.ts +138 -0
  162. package/src/organizations/organizations.test-d.ts +56 -0
  163. package/src/organizations/organizations.test.ts +150 -0
  164. package/src/organizations/organizations.ts +107 -0
  165. package/src/presence/bifurTransport.test.ts +201 -9
  166. package/src/presence/bifurTransport.ts +98 -14
  167. package/src/presence/paths.test.ts +70 -0
  168. package/src/presence/paths.ts +41 -0
  169. package/src/presence/presenceStore.test.ts +915 -24
  170. package/src/presence/presenceStore.ts +576 -104
  171. package/src/presence/types.ts +175 -3
  172. package/src/preview/previewProjectionUtils.test.ts +9 -22
  173. package/src/preview/previewProjectionUtils.ts +7 -3
  174. package/src/preview/types.ts +0 -15
  175. package/src/project/project.test-d.ts +37 -0
  176. package/src/project/project.test.ts +157 -12
  177. package/src/project/project.ts +138 -25
  178. package/src/projection/getProjectionState.test.ts +18 -20
  179. package/src/projection/getProjectionState.ts +23 -3
  180. package/src/projection/projectionQuery.ts +5 -5
  181. package/src/projection/projectionStore.test.ts +1 -9
  182. package/src/projection/resolveProjection.test.ts +1 -2
  183. package/src/projection/resolveProjection.ts +16 -0
  184. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  185. package/src/projection/subscribeToStateAndFetchBatches.ts +32 -23
  186. package/src/projects/projects.test-d.ts +32 -0
  187. package/src/projects/projects.test.ts +108 -42
  188. package/src/projects/projects.ts +63 -27
  189. package/src/query/queryStore.test.ts +259 -123
  190. package/src/query/queryStore.ts +85 -69
  191. package/src/query/reducers.test.ts +0 -42
  192. package/src/query/reducers.ts +2 -18
  193. package/src/releases/getPerspectiveState.test.ts +17 -23
  194. package/src/releases/getPerspectiveState.ts +8 -20
  195. package/src/releases/observeReleases.test.ts +127 -0
  196. package/src/releases/observeReleases.ts +102 -0
  197. package/src/releases/releasesStore.test.ts +100 -53
  198. package/src/releases/releasesStore.ts +69 -44
  199. package/src/releases/utils/sortReleases.test.ts +38 -2
  200. package/src/releases/utils/sortReleases.ts +15 -10
  201. package/src/store/createActionBinder.test.ts +109 -90
  202. package/src/store/createActionBinder.ts +36 -28
  203. package/src/store/createSanityInstance.test.ts +15 -13
  204. package/src/store/createSanityInstance.ts +14 -2
  205. package/src/store/createStateSourceAction.test.ts +64 -2
  206. package/src/store/createStateSourceAction.ts +38 -43
  207. package/src/store/createStoreInstance.test.ts +4 -2
  208. package/src/store/fetcherStore.test-d.ts +121 -0
  209. package/src/store/fetcherStore.test.ts +299 -0
  210. package/src/store/fetcherStore.ts +801 -0
  211. package/src/telemetry/environment.test.ts +119 -0
  212. package/src/telemetry/environment.ts +92 -0
  213. package/src/telemetry/events.ts +42 -0
  214. package/src/telemetry/initTelemetry.test.ts +432 -0
  215. package/src/telemetry/initTelemetry.ts +218 -0
  216. package/src/telemetry/telemetryManager.test.ts +327 -0
  217. package/src/telemetry/telemetryManager.ts +199 -0
  218. package/src/users/reducers.test.ts +3 -1
  219. package/src/users/reducers.ts +5 -6
  220. package/src/users/types.ts +4 -4
  221. package/src/users/usersStore.test.ts +16 -12
  222. package/src/users/usersStore.ts +5 -1
  223. package/src/utils/buildQuery.test.ts +14 -0
  224. package/src/utils/buildQuery.ts +17 -0
  225. package/src/utils/createFetcherStore.test.ts +7 -5
  226. package/src/utils/createFetcherStore.ts +8 -5
  227. package/src/utils/getStagingApiHost.test.ts +21 -0
  228. package/src/utils/getStagingApiHost.ts +14 -0
  229. package/src/utils/ids.test.ts +1 -29
  230. package/src/utils/ids.ts +0 -10
  231. package/src/utils/includeShape.ts +36 -0
  232. package/src/utils/isImportError.test.ts +72 -0
  233. package/src/utils/isImportError.ts +34 -0
  234. package/src/utils/logger.test.ts +12 -0
  235. package/src/utils/logger.ts +8 -3
  236. package/src/utils/object.test.ts +95 -0
  237. package/src/utils/object.ts +142 -0
  238. package/src/utils/setCleanupTimeout.ts +37 -0
  239. package/src/document/processActions.ts +0 -707
  240. package/src/utils/defineIntent.test.ts +0 -477
  241. package/src/utils/defineIntent.ts +0 -244
@@ -1,9 +1,15 @@
1
+ import {type ReleaseDocument} from '@sanity/client'
1
2
  import {SanityEncoder} from '@sanity/mutate'
2
3
  import {type PatchMutation as SanityMutatePatchMutation} from '@sanity/mutate/_unstable_store'
3
- import {type PatchMutation, type PatchOperations, type SanityDocument} from '@sanity/types'
4
+ import {type PatchMutation, type PatchOperations} from '@sanity/types'
5
+ import {type SanityDocument} from 'groq'
4
6
 
5
- import {type DocumentHandle, type DocumentTypeHandle} from '../config/sanityConfig'
6
- import {getPublishedId} from '../utils/ids'
7
+ import {
8
+ type DocumentHandle,
9
+ type DocumentTypeHandle,
10
+ type ReleaseHandle,
11
+ } from '../config/sanityConfig'
12
+ import {getEffectiveDocumentId} from './util'
7
13
 
8
14
  const isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation => {
9
15
  if (typeof value !== 'object' || !value) return false
@@ -30,7 +36,10 @@ export interface CreateDocumentAction<
30
36
  * System fields (_id, _type, _rev, _createdAt, _updatedAt) are omitted as they are set automatically.
31
37
  */
32
38
  initialValue?: Partial<
33
- Omit<SanityDocument, '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'>
39
+ Omit<
40
+ SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>,
41
+ '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'
42
+ >
34
43
  >
35
44
  }
36
45
 
@@ -59,6 +68,21 @@ export interface EditDocumentAction<
59
68
  > extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
60
69
  type: 'document.edit'
61
70
  patches?: PatchOperations[]
71
+ /**
72
+ * When `true`, the given `patches` are forwarded verbatim to the server and
73
+ * applied as-is locally, instead of being re-derived by diffing document
74
+ * snapshots. This preserves the operational intent of the patches (e.g.
75
+ * keyed array inserts/unsets), which lets concurrent edits from other
76
+ * clients interleave coherently instead of being overwritten by
77
+ * position-based patches computed from a stale snapshot.
78
+ *
79
+ * Use this when the patches were produced by an editor that tracks its own
80
+ * operations (e.g. a collaborative text editor). During a rebase onto a
81
+ * changed remote document, preserved patches are re-applied operationally;
82
+ * if a patch can no longer apply, the transaction is skipped and reported
83
+ * as a `rebase-error` document event.
84
+ */
85
+ preserveOperations?: boolean
62
86
  }
63
87
 
64
88
  /**
@@ -116,6 +140,17 @@ export type DocumentAction<
116
140
  | UnpublishDocumentAction<TDocumentType, TDataset, TProjectId>
117
141
  | DiscardDocumentAction<TDocumentType, TDataset, TProjectId>
118
142
 
143
+ /**
144
+ * Union of every action accepted by `applyDocumentActions` — both document-
145
+ * level actions and release-lifecycle actions.
146
+ * @beta
147
+ */
148
+ export type Action<
149
+ TDocumentType extends string = string,
150
+ TDataset extends string = string,
151
+ TProjectId extends string = string,
152
+ > = DocumentAction<TDocumentType, TDataset, TProjectId> | ReleaseAction
153
+
119
154
  /**
120
155
  * Creates a `CreateDocumentAction` object.
121
156
  * @param doc - A handle identifying the document type, dataset, and project. An optional `documentId` can be provided.
@@ -130,15 +165,21 @@ export function createDocument<
130
165
  >(
131
166
  doc: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>,
132
167
  initialValue?: Partial<
133
- Omit<SanityDocument, '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'>
168
+ Omit<
169
+ SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>,
170
+ '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'
171
+ >
134
172
  >,
135
173
  ): CreateDocumentAction<TDocumentType, TDataset, TProjectId> {
174
+ // users may pass in an explicit documentId -- make sure we format it correctly for the action
175
+ let effectiveDocumentId
176
+ if (typeof doc.documentId === 'string') {
177
+ effectiveDocumentId = getEffectiveDocumentId({...doc, documentId: doc.documentId})
178
+ }
136
179
  return {
137
180
  type: 'document.create',
138
181
  ...doc,
139
- ...(doc.documentId && {
140
- documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
141
- }),
182
+ ...(effectiveDocumentId && {documentId: effectiveDocumentId}),
142
183
  ...(initialValue && {initialValue}),
143
184
  }
144
185
  }
@@ -156,10 +197,11 @@ export function deleteDocument<
156
197
  >(
157
198
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
158
199
  ): DeleteDocumentAction<TDocumentType, TDataset, TProjectId> {
200
+ const effectiveDocumentId = getEffectiveDocumentId(doc)
159
201
  return {
160
202
  type: 'document.delete',
161
203
  ...doc,
162
- documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
204
+ documentId: effectiveDocumentId,
163
205
  }
164
206
  }
165
207
 
@@ -174,12 +216,25 @@ function convertSanityMutatePatch(
174
216
  })
175
217
  }
176
218
 
219
+ /**
220
+ * Options for creating an `EditDocumentAction`.
221
+ * @beta
222
+ */
223
+ export interface EditDocumentOptions {
224
+ /**
225
+ * Forward the given patches verbatim instead of re-deriving them by
226
+ * diffing document snapshots. See {@link EditDocumentAction.preserveOperations}.
227
+ */
228
+ preserveOperations?: boolean
229
+ }
230
+
177
231
  /**
178
232
  * Creates an `EditDocumentAction` object with patches for modifying a document.
179
233
  * Accepts patches in either the standard `PatchOperations` format or as a `SanityMutatePatchMutation` from `@sanity/mutate`.
180
234
  *
181
235
  * @param doc - A handle uniquely identifying the document to be edited.
182
236
  * @param sanityMutatePatch - A patch mutation object from `@sanity/mutate`.
237
+ * @param options - Options controlling how the patches are processed.
183
238
  * @returns An `EditDocumentAction` object ready for dispatch.
184
239
  * @beta
185
240
  */
@@ -190,12 +245,14 @@ export function editDocument<
190
245
  >(
191
246
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
192
247
  sanityMutatePatch: SanityMutatePatchMutation,
248
+ options?: EditDocumentOptions,
193
249
  ): EditDocumentAction<TDocumentType, TDataset, TProjectId>
194
250
  /**
195
251
  * Creates an `EditDocumentAction` object with patches for modifying a document.
196
252
  *
197
253
  * @param doc - A handle uniquely identifying the document to be edited.
198
254
  * @param patches - A single patch operation or an array of patch operations.
255
+ * @param options - Options controlling how the patches are processed.
199
256
  * @returns An `EditDocumentAction` object ready for dispatch.
200
257
  * @beta
201
258
  */
@@ -206,6 +263,7 @@ export function editDocument<
206
263
  >(
207
264
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
208
265
  patches?: PatchOperations | PatchOperations[],
266
+ options?: EditDocumentOptions,
209
267
  ): EditDocumentAction<TDocumentType, TDataset, TProjectId>
210
268
  /**
211
269
  * Creates an `EditDocumentAction` object with patches for modifying a document.
@@ -213,6 +271,7 @@ export function editDocument<
213
271
  *
214
272
  * @param doc - A handle uniquely identifying the document to be edited.
215
273
  * @param patches - Patches in various formats (`PatchOperations`, `PatchOperations[]`, or `SanityMutatePatchMutation`).
274
+ * @param options - Options controlling how the patches are processed.
216
275
  * @returns An `EditDocumentAction` object ready for dispatch.
217
276
  */
218
277
  export function editDocument<
@@ -222,24 +281,28 @@ export function editDocument<
222
281
  >(
223
282
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
224
283
  patches?: PatchOperations | PatchOperations[] | SanityMutatePatchMutation,
284
+ options?: EditDocumentOptions,
225
285
  ): EditDocumentAction<TDocumentType, TDataset, TProjectId> {
226
- const documentId = doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId)
286
+ const effectiveDocumentId = getEffectiveDocumentId(doc)
287
+ const preserveOperations = options?.preserveOperations && {preserveOperations: true as const}
227
288
 
228
289
  if (isSanityMutatePatch(patches)) {
229
290
  const converted = convertSanityMutatePatch(patches) ?? []
230
291
  return {
231
292
  ...doc,
232
293
  type: 'document.edit',
233
- documentId,
294
+ documentId: effectiveDocumentId,
234
295
  patches: converted,
296
+ ...preserveOperations,
235
297
  }
236
298
  }
237
299
 
238
300
  return {
239
301
  ...doc,
240
302
  type: 'document.edit',
241
- documentId,
303
+ documentId: effectiveDocumentId,
242
304
  ...(patches && {patches: Array.isArray(patches) ? patches : [patches]}),
305
+ ...preserveOperations,
243
306
  }
244
307
  }
245
308
 
@@ -256,10 +319,11 @@ export function publishDocument<
256
319
  >(
257
320
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
258
321
  ): PublishDocumentAction<TDocumentType, TDataset, TProjectId> {
322
+ const effectiveDocumentId = getEffectiveDocumentId(doc)
259
323
  return {
260
324
  type: 'document.publish',
261
325
  ...doc,
262
- documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
326
+ documentId: effectiveDocumentId,
263
327
  }
264
328
  }
265
329
 
@@ -276,10 +340,11 @@ export function unpublishDocument<
276
340
  >(
277
341
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
278
342
  ): UnpublishDocumentAction<TDocumentType, TDataset, TProjectId> {
343
+ const effectiveDocumentId = getEffectiveDocumentId(doc)
279
344
  return {
280
345
  type: 'document.unpublish',
281
346
  ...doc,
282
- documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
347
+ documentId: effectiveDocumentId,
283
348
  }
284
349
  }
285
350
 
@@ -296,9 +361,141 @@ export function discardDocument<
296
361
  >(
297
362
  doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,
298
363
  ): DiscardDocumentAction<TDocumentType, TDataset, TProjectId> {
364
+ const effectiveDocumentId = getEffectiveDocumentId(doc)
299
365
  return {
300
366
  type: 'document.discard',
301
367
  ...doc,
302
- documentId: doc.liveEdit ? doc.documentId : getPublishedId(doc.documentId),
368
+ documentId: effectiveDocumentId,
303
369
  }
304
370
  }
371
+
372
+ /**
373
+ * Creates a new release. The `releaseId` must be unique within the current
374
+ * retention period.
375
+ * @beta
376
+ */
377
+ export interface CreateReleaseAction extends ReleaseHandle {
378
+ type: 'release.create'
379
+ metadata: ReleaseDocument['metadata']
380
+ }
381
+
382
+ /**
383
+ * Patches the metadata of an existing release.
384
+ * @beta
385
+ */
386
+ export interface EditReleaseAction extends ReleaseHandle {
387
+ type: 'release.edit'
388
+ patch: PatchOperations
389
+ }
390
+
391
+ /**
392
+ * Publishes all version documents in a release.
393
+ * @beta
394
+ */
395
+ export interface PublishReleaseAction extends ReleaseHandle {
396
+ type: 'release.publish'
397
+ }
398
+
399
+ /**
400
+ * Schedules a release to be published at the given UTC time. Locks the
401
+ * version documents server-side until the release is unscheduled or published.
402
+ * @beta
403
+ */
404
+ export interface ScheduleReleaseAction extends ReleaseHandle {
405
+ type: 'release.schedule'
406
+ publishAt: string
407
+ }
408
+
409
+ /**
410
+ * Unschedules a release that was previously scheduled, returning it to the
411
+ * active editable state.
412
+ * @beta
413
+ */
414
+ export interface UnscheduleReleaseAction extends ReleaseHandle {
415
+ type: 'release.unschedule'
416
+ }
417
+
418
+ /**
419
+ * Archives an active release. Version documents within the release are
420
+ * removed and no longer queryable, though still recoverable through history
421
+ * during the retention period.
422
+ * @beta
423
+ */
424
+ export interface ArchiveReleaseAction extends ReleaseHandle {
425
+ type: 'release.archive'
426
+ }
427
+
428
+ /**
429
+ * Restores an archived release. Only possible during the retention period.
430
+ * @beta
431
+ */
432
+ export interface UnarchiveReleaseAction extends ReleaseHandle {
433
+ type: 'release.unarchive'
434
+ }
435
+
436
+ /**
437
+ * Permanently deletes an archived or published release. To remove an active
438
+ * release, use the archive action first.
439
+ * @beta
440
+ */
441
+ export interface DeleteReleaseAction extends ReleaseHandle {
442
+ type: 'release.delete'
443
+ }
444
+
445
+ /**
446
+ * Union of all release actions that can be dispatched alongside document
447
+ * actions through `applyDocumentActions`.
448
+ * @beta
449
+ */
450
+ export type ReleaseAction =
451
+ | CreateReleaseAction
452
+ | EditReleaseAction
453
+ | PublishReleaseAction
454
+ | ScheduleReleaseAction
455
+ | UnscheduleReleaseAction
456
+ | ArchiveReleaseAction
457
+ | UnarchiveReleaseAction
458
+ | DeleteReleaseAction
459
+
460
+ /** @beta */
461
+ export function createRelease(
462
+ handle: ReleaseHandle,
463
+ metadata: ReleaseDocument['metadata'] = {releaseType: 'undecided'},
464
+ ): CreateReleaseAction {
465
+ return {type: 'release.create', ...handle, metadata}
466
+ }
467
+
468
+ /** @beta */
469
+ export function editRelease(handle: ReleaseHandle, patch: PatchOperations): EditReleaseAction {
470
+ return {type: 'release.edit', ...handle, patch}
471
+ }
472
+
473
+ /** @beta */
474
+ export function publishRelease(handle: ReleaseHandle): PublishReleaseAction {
475
+ return {type: 'release.publish', ...handle}
476
+ }
477
+
478
+ /** @beta */
479
+ export function scheduleRelease(handle: ReleaseHandle, publishAt: string): ScheduleReleaseAction {
480
+ return {type: 'release.schedule', ...handle, publishAt}
481
+ }
482
+
483
+ /** @beta */
484
+ export function unscheduleRelease(handle: ReleaseHandle): UnscheduleReleaseAction {
485
+ return {type: 'release.unschedule', ...handle}
486
+ }
487
+
488
+ /** @beta */
489
+ export function archiveRelease(handle: ReleaseHandle): ArchiveReleaseAction {
490
+ return {type: 'release.archive', ...handle}
491
+ }
492
+
493
+ /** @beta */
494
+ export function unarchiveRelease(handle: ReleaseHandle): UnarchiveReleaseAction {
495
+ return {type: 'release.unarchive', ...handle}
496
+ }
497
+
498
+ /** @beta */
499
+ export function deleteRelease(handle: ReleaseHandle): DeleteReleaseAction {
500
+ return {type: 'release.delete', ...handle}
501
+ }
@@ -1,11 +1,9 @@
1
- // applyDocumentActions.test.ts
2
1
  import {type SanityDocument} from '@sanity/types'
3
2
  import {Subject} from 'rxjs'
4
3
  import {describe, expect, it} from 'vitest'
5
4
 
6
5
  import {bindActionByResource} from '../store/createActionBinder'
7
6
  import {createSanityInstance, type SanityInstance} from '../store/createSanityInstance'
8
- import {} from '../store/createStateSourceAction'
9
7
  import {createStoreState, type StoreState} from '../store/createStoreState'
10
8
  import {type DocumentAction} from './actions'
11
9
  import {type DocumentStoreState} from './documentStore'
@@ -47,7 +45,7 @@ describe('applyDocumentActions', () => {
47
45
  events: eventsSubject,
48
46
  }
49
47
  state = createStoreState(initialState)
50
- instance = createSanityInstance()
48
+ instance = createSanityInstance({projectId: 'p', dataset: 'd'})
51
49
  const key = {name: 'p.d', resource: {projectId: 'p', dataset: 'd'}}
52
50
 
53
51
  vi.mocked(bindActionByResource).mockImplementation(
@@ -69,7 +67,6 @@ describe('applyDocumentActions', () => {
69
67
  documentId: 'doc1',
70
68
  documentType: 'example',
71
69
  patches: [{set: {foo: 'bar'}}],
72
- resource: {projectId: 'p', dataset: 'd'},
73
70
  }
74
71
 
75
72
  // Call applyDocumentActions with a fixed transactionId for reproducibility.
@@ -128,7 +125,6 @@ describe('applyDocumentActions', () => {
128
125
  documentId: 'doc1',
129
126
  documentType: 'example',
130
127
  patches: [{set: {foo: 'error'}}],
131
- resource: {projectId: 'p', dataset: 'd'},
132
128
  }
133
129
 
134
130
  // Call applyDocumentActions with a fixed transactionId.
@@ -150,24 +146,26 @@ describe('applyDocumentActions', () => {
150
146
  await expect(applyPromise).rejects.toThrow('Simulated error')
151
147
  })
152
148
 
153
- it('uses explicit resource even when instance default differs', async () => {
154
- const otherInstance = createSanityInstance({
155
- defaultResource: {projectId: 'child-p', dataset: 'child-d'},
156
- })
149
+ it('uses the explicit resource when it does not match the instance config', async () => {
150
+ // The instance is configured for one resource...
151
+ const scopedInstance = createSanityInstance({projectId: 'child-p', dataset: 'child-d'})
152
+ // ...while the action refers to a different one
157
153
  const action: DocumentAction = {
158
154
  type: 'document.edit',
159
155
  documentId: 'doc1',
160
156
  documentType: 'example',
161
157
  patches: [{set: {foo: 'childTest'}}],
162
- resource: {projectId: 'p', dataset: 'd'},
158
+ projectId: 'p',
159
+ dataset: 'd',
163
160
  }
164
- const applyPromise = applyDocumentActions(instance, {
161
+ // The explicit `resource` option should win over the instance's own config
162
+ const applyPromise = applyDocumentActions(scopedInstance, {
165
163
  actions: [action],
166
164
  transactionId: 'txn-child-match',
167
165
  resource: {projectId: 'p', dataset: 'd'},
168
166
  })
169
167
 
170
- // Simulate an applied transaction on the parent's instance
168
+ // Simulate an applied transaction for the explicitly-passed resource
171
169
  const appliedTx: AppliedTransaction = {
172
170
  transactionId: 'txn-child-match',
173
171
  actions: [action],
@@ -198,6 +196,30 @@ describe('applyDocumentActions', () => {
198
196
  const submittedResult = await result.submitted()
199
197
  expect(submittedResult).toEqual(acceptedResult)
200
198
 
201
- otherInstance.dispose()
199
+ scopedInstance.dispose()
200
+ })
201
+
202
+ it('normalizes actions for the bound resource before queueing', async () => {
203
+ // A plain edit action with no `liveEdit` flag. Canvas forces liveEdit, so
204
+ // the queued action should come back with `liveEdit: true` — proving that
205
+ // `applyDocumentActions` runs `normalizeActionsForResource`.
206
+ const action: DocumentAction = {
207
+ type: 'document.edit',
208
+ documentId: 'doc1',
209
+ documentType: 'sanity.canvas.document',
210
+ patches: [{set: {foo: 'bar'}}],
211
+ }
212
+
213
+ // Don't await — we only need to inspect the synchronously-queued transaction.
214
+ applyDocumentActions(instance, {
215
+ actions: [action],
216
+ transactionId: 'txn-normalize',
217
+ resource: {canvasId: 'c'},
218
+ })
219
+
220
+ const queued = state.get().queued.find((t) => t.transactionId === 'txn-normalize')
221
+ expect(queued).toBeDefined()
222
+ const [queuedAction] = queued!.actions
223
+ expect(queuedAction).toMatchObject({type: 'document.edit', liveEdit: true})
202
224
  })
203
225
  })
@@ -1,15 +1,16 @@
1
- import {type SanityClient} from '@sanity/client'
2
- import {type SanityDocument} from '@sanity/types'
1
+ import {type SanityDocument} from 'groq'
3
2
  import {distinctUntilChanged, filter, first, firstValueFrom, map, race} from 'rxjs'
4
3
 
5
4
  import {type DocumentResource} from '../config/sanityConfig'
6
5
  import {bindActionByResource} from '../store/createActionBinder'
7
6
  import {type SanityInstance} from '../store/createSanityInstance'
8
7
  import {type StoreContext} from '../store/defineStore'
9
- import {type DocumentAction} from './actions'
8
+ import {type Action} from './actions'
10
9
  import {documentStore, type DocumentStoreState} from './documentStore'
10
+ import {type DocumentTransactionSubmissionResult} from './events'
11
11
  import {type DocumentSet} from './processMutations'
12
12
  import {type AppliedTransaction, type QueuedTransaction, queueTransaction} from './reducers'
13
+ import {normalizeActionsForResource} from './resourceRules'
13
14
 
14
15
  /** @beta */
15
16
  export interface ActionsResult<TDocument extends SanityDocument = SanityDocument> {
@@ -20,20 +21,21 @@ export interface ActionsResult<TDocument extends SanityDocument = SanityDocument
20
21
  appeared: string[]
21
22
  updated: string[]
22
23
  disappeared: string[]
23
- submitted: () => ReturnType<SanityClient['action']>
24
+ submitted: () => Promise<DocumentTransactionSubmissionResult>
24
25
  }
25
26
 
26
27
  /** @beta */
27
28
  export interface ApplyDocumentActionsOptions {
28
29
  /**
29
- * List of actions to apply.
30
+ * List of actions to apply. Accepts both document actions and release
31
+ * lifecycle actions because they share the same transaction pipeline.
30
32
  */
31
- actions: DocumentAction[]
33
+ actions: Action[]
32
34
 
33
35
  /**
34
36
  * The resource to which the documents being acted on belong.
35
37
  */
36
- resource: DocumentResource
38
+ resource?: DocumentResource
37
39
 
38
40
  /**
39
41
  * Optionally provide an ID to be used as this transaction ID
@@ -45,6 +47,15 @@ export interface ApplyDocumentActionsOptions {
45
47
  disableBatching?: boolean
46
48
  }
47
49
 
50
+ /** @beta */
51
+ export function applyDocumentActions<
52
+ TDocumentType extends string = string,
53
+ TDataset extends string = string,
54
+ TProjectId extends string = string,
55
+ >(
56
+ instance: SanityInstance,
57
+ options: ApplyDocumentActionsOptions,
58
+ ): Promise<ActionsResult<SanityDocument<TDocumentType, `${TProjectId}.${TDataset}`>>>
48
59
  /** @beta */
49
60
  export function applyDocumentActions(
50
61
  instance: SanityInstance,
@@ -63,13 +74,23 @@ const boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocu
63
74
  /** @internal */
64
75
  async function _applyDocumentActions(
65
76
  {state}: StoreContext<DocumentStoreState>,
66
- {actions, transactionId = crypto.randomUUID(), disableBatching}: ApplyDocumentActionsOptions,
77
+ {
78
+ actions,
79
+ resource,
80
+ transactionId = crypto.randomUUID(),
81
+ disableBatching,
82
+ }: ApplyDocumentActionsOptions,
67
83
  ): Promise<ActionsResult> {
68
84
  const {events} = state.get()
69
85
 
86
+ // Rewrite edit actions to match the bound resource's editing model (e.g.
87
+ // forcing liveEdit for Canvas, stripping unsupported release perspectives).
88
+ // Non-edit and release actions pass through unchanged.
89
+ const normalizedActions = normalizeActionsForResource(actions, resource)
90
+
70
91
  const transaction: QueuedTransaction = {
71
92
  transactionId,
72
- actions,
93
+ actions: normalizedActions,
73
94
  ...(disableBatching && {disableBatching}),
74
95
  }
75
96
 
@@ -8,3 +8,18 @@
8
8
  export const DOCUMENT_STATE_CLEAR_DELAY = 1000
9
9
  export const INITIAL_OUTGOING_THROTTLE_TIME = 1000
10
10
  export const API_VERSION = 'v2025-05-06'
11
+
12
+ /**
13
+ * Base delay (ms) before retrying a document listener after an `OutOfSyncError`.
14
+ * Backoff doubles on each successive retry, capped at {@link OUT_OF_SYNC_RETRY_MAX_DELAY}.
15
+ */
16
+ export const OUT_OF_SYNC_RETRY_BASE_DELAY = 500
17
+ export const OUT_OF_SYNC_RETRY_MAX_DELAY = 10_000
18
+
19
+ /**
20
+ * Base delay (ms) before retrying the dataset ACL fetch after a transient
21
+ * failure (a network error or 5xx response). Backoff doubles on each
22
+ * successive retry, capped at {@link ACL_RETRY_MAX_DELAY}.
23
+ */
24
+ export const ACL_RETRY_BASE_DELAY = 500
25
+ export const ACL_RETRY_MAX_DELAY = 10_000