@sanity/sdk 2.11.1 → 2.13.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 (60) hide show
  1. package/dist/_chunks-dts/utils.d.ts +175 -19
  2. package/dist/_chunks-es/_internal.js +41 -26
  3. package/dist/_chunks-es/_internal.js.map +1 -1
  4. package/dist/_chunks-es/createGroqSearchFilter.js +15 -4
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
  6. package/dist/_chunks-es/telemetryManager.js +25 -19
  7. package/dist/_chunks-es/telemetryManager.js.map +1 -1
  8. package/dist/_chunks-es/version.js +1 -1
  9. package/dist/_exports/_internal.d.ts +27 -11
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +465 -131
  12. package/dist/index.js.map +1 -1
  13. package/package.json +11 -11
  14. package/src/_exports/index.ts +23 -2
  15. package/src/config/sanityConfig.ts +12 -0
  16. package/src/document/actions.test.ts +112 -1
  17. package/src/document/actions.ts +148 -1
  18. package/src/document/applyDocumentActions.test.ts +24 -0
  19. package/src/document/applyDocumentActions.ts +17 -5
  20. package/src/document/documentConstants.ts +7 -0
  21. package/src/document/documentStore.test.ts +69 -0
  22. package/src/document/documentStore.ts +42 -10
  23. package/src/document/events.test.ts +57 -2
  24. package/src/document/events.ts +43 -24
  25. package/src/document/listen.ts +1 -1
  26. package/src/document/permissions.test.ts +79 -0
  27. package/src/document/permissions.ts +8 -7
  28. package/src/document/processActions/create.ts +7 -4
  29. package/src/document/processActions/delete.ts +4 -4
  30. package/src/document/processActions/discard.ts +2 -2
  31. package/src/document/processActions/edit.ts +13 -47
  32. package/src/document/processActions/processActions.ts +53 -3
  33. package/src/document/processActions/publish.ts +4 -4
  34. package/src/document/processActions/releaseArchive.ts +77 -0
  35. package/src/document/processActions/releaseCreate.ts +59 -0
  36. package/src/document/processActions/releaseDelete.ts +65 -0
  37. package/src/document/processActions/releaseEdit.ts +37 -0
  38. package/src/document/processActions/releasePublish.ts +45 -0
  39. package/src/document/processActions/releaseSchedule.ts +87 -0
  40. package/src/document/processActions/releaseUtil.ts +31 -0
  41. package/src/document/processActions/shared.ts +108 -4
  42. package/src/document/processActions/unpublish.ts +3 -3
  43. package/src/document/processActions.test.ts +423 -1
  44. package/src/document/reducers.ts +44 -8
  45. package/src/document/resourceRules.test.ts +178 -0
  46. package/src/document/resourceRules.ts +117 -0
  47. package/src/releases/getPerspectiveState.test.ts +1 -1
  48. package/src/releases/releasesStore.test.ts +50 -1
  49. package/src/releases/releasesStore.ts +41 -18
  50. package/src/releases/utils/sortReleases.test.ts +2 -2
  51. package/src/releases/utils/sortReleases.ts +1 -1
  52. package/src/telemetry/environment.test.ts +119 -0
  53. package/src/telemetry/environment.ts +92 -0
  54. package/src/telemetry/{__telemetry__/sdk.telemetry.ts → events.ts} +9 -9
  55. package/src/telemetry/initTelemetry.test.ts +240 -16
  56. package/src/telemetry/initTelemetry.ts +39 -16
  57. package/src/telemetry/telemetryManager.test.ts +129 -65
  58. package/src/telemetry/telemetryManager.ts +41 -29
  59. package/src/telemetry/devMode.test.ts +0 -60
  60. package/src/telemetry/devMode.ts +0 -41
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { switchMap, from, firstValueFrom, EMPTY, asapScheduler, distinctUntilChanged, map as map$1, combineLatest, of, concatMap, withLatestFrom, filter as filter$1, concat, timer, throwError, first as first$1, Subject, takeUntil, share, partition, merge, shareReplay, tap as tap$1, catchError as catchError$1, startWith as startWith$1, pairwise as pairwise$1, groupBy as groupBy$1, mergeMap as mergeMap$1, throttle, race, skip, Observable, NEVER, fromEvent, Subscription, debounceTime, defer } from "rxjs";
2
- import { createLogger, pickProperties, insecureRandomId, getClientState, bindActionGlobally, createStateSourceAction, setCleanupTimeout, omitProperty, defineStore, authStore, AuthStateType, getCleanedUrl, getTokenFromLocation, createLoggedInAuthState, getAuthCode, REQUEST_TAG_PREFIX, DEFAULT_API_VERSION, getDefaultLocation, isDeepEqual, configureLogging as configureLogging$1, isReleasePerspective, bindActionByResource, isDatasetResource, isMediaLibraryResource, isCanvasResource, getUsersKey, addSubscription, parseUsersKey, getClient, PROJECT_API_VERSION, setUsersError, setUsersData, API_VERSION as API_VERSION$8, getDashboardOrganizationId as getDashboardOrganizationId$1, USERS_STATE_CLEAR_DELAY, removeSubscription, updateLastLoadMoreRequest, cancelRequest, initializeRequest, getTokenState, getQueryState, resolveQuery, bindActionByResourceAndPerspective, PREVIEW_PROJECTION, transformProjectionToPreview } from "./_chunks-es/createGroqSearchFilter.js";
3
- import { createGroqSearchFilter, getActiveReleasesState, getAuthState, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType, getCurrentUserState, getIsInDashboardState, getLoginUrlState, getPerspectiveState, getQueryKey, isCanvasSource, isDatasetSource, isMediaLibrarySource, isProjectUserNotFoundClientError, isStudioConfig, parseQueryKey, setAuthToken } from "./_chunks-es/createGroqSearchFilter.js";
1
+ import { switchMap, from, firstValueFrom, EMPTY, asapScheduler, distinctUntilChanged, map as map$1, combineLatest, of, concatMap, withLatestFrom, filter as filter$1, concat, timer, throwError, first as first$1, Subject, takeUntil, share, partition, merge, shareReplay, tap as tap$1, catchError as catchError$1, startWith as startWith$1, pairwise as pairwise$1, groupBy as groupBy$1, mergeMap as mergeMap$1, retry, throttle, race, skip, Observable, NEVER, fromEvent, Subscription, debounceTime, defer } from "rxjs";
2
+ import { createLogger, pickProperties, insecureRandomId, getClientState, bindActionGlobally, createStateSourceAction, setCleanupTimeout, omitProperty, defineStore, authStore, AuthStateType, getCleanedUrl, getTokenFromLocation, createLoggedInAuthState, getAuthCode, REQUEST_TAG_PREFIX, DEFAULT_API_VERSION, getDefaultLocation, isDeepEqual, configureLogging as configureLogging$1, isReleasePerspective, bindActionByResource, isDatasetResource, isMediaLibraryResource, isCanvasResource, getCurrentUserState, getUsersKey, addSubscription, parseUsersKey, getClient, PROJECT_API_VERSION, setUsersError, setUsersData, API_VERSION as API_VERSION$8, getDashboardOrganizationId as getDashboardOrganizationId$1, USERS_STATE_CLEAR_DELAY, removeSubscription, updateLastLoadMoreRequest, cancelRequest, initializeRequest, getTokenState, getQueryState, resolveQuery, bindActionByResourceAndPerspective, PREVIEW_PROJECTION, transformProjectionToPreview } from "./_chunks-es/createGroqSearchFilter.js";
3
+ import { createGroqSearchFilter, getActiveReleasesState, getAllReleasesState, getAuthState, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType, getIsInDashboardState, getLoginUrlState, getPerspectiveState, getQueryKey, isCanvasSource, isDatasetSource, isMediaLibrarySource, isProjectUserNotFoundClientError, isStudioConfig, parseQueryKey, setAuthToken } from "./_chunks-es/createGroqSearchFilter.js";
4
4
  import { first, switchMap as switchMap$1, groupBy, mergeMap, startWith, pairwise, filter, map, delay, tap, catchError, scan, share as share$1 } from "rxjs/operators";
5
5
  import { createController, createNode } from "@sanity/comlink";
6
6
  import { createSelector } from "reselect";
@@ -519,7 +519,7 @@ const handleAuthCallback = bindActionGlobally(
519
519
  status: nodeEntry.status
520
520
  } : void 0),
521
521
  onSubscribe: ({ state, instance }, nodeInput) => {
522
- const nodeName = nodeInput.name, subscriberId = Symbol("comlink-node-subscriber");
522
+ const nodeName = nodeInput.name, subscriberId = /* @__PURE__ */ Symbol("comlink-node-subscriber");
523
523
  getOrCreateNode(instance, nodeInput);
524
524
  let subs = state.get().subscriptions.get(nodeName);
525
525
  return subs || (subs = /* @__PURE__ */ new Set(), state.get().subscriptions.set(nodeName, subs)), subs.add(subscriberId), () => {
@@ -642,7 +642,47 @@ function discardDocument(doc) {
642
642
  documentId: effectiveDocumentId
643
643
  };
644
644
  }
645
- const DOCUMENT_STATE_CLEAR_DELAY = 1e3, INITIAL_OUTGOING_THROTTLE_TIME = 1e3, API_VERSION$4 = "v2025-05-06";
645
+ function createRelease(handle, metadata = { releaseType: "undecided" }) {
646
+ return { type: "release.create", ...handle, metadata };
647
+ }
648
+ function editRelease(handle, patch) {
649
+ return { type: "release.edit", ...handle, patch };
650
+ }
651
+ function publishRelease(handle) {
652
+ return { type: "release.publish", ...handle };
653
+ }
654
+ function scheduleRelease(handle, publishAt) {
655
+ return { type: "release.schedule", ...handle, publishAt };
656
+ }
657
+ function unscheduleRelease(handle) {
658
+ return { type: "release.unschedule", ...handle };
659
+ }
660
+ function archiveRelease(handle) {
661
+ return { type: "release.archive", ...handle };
662
+ }
663
+ function unarchiveRelease(handle) {
664
+ return { type: "release.unarchive", ...handle };
665
+ }
666
+ function deleteRelease(handle) {
667
+ return { type: "release.delete", ...handle };
668
+ }
669
+ const DOCUMENT_STATE_CLEAR_DELAY = 1e3, INITIAL_OUTGOING_THROTTLE_TIME = 1e3, API_VERSION$4 = "v2025-05-06", OUT_OF_SYNC_RETRY_BASE_DELAY = 500, OUT_OF_SYNC_RETRY_MAX_DELAY = 1e4, RELEASE_DOCUMENTS_PATH = "_.releases";
670
+ function getReleaseDocumentId(releaseId) {
671
+ return `${RELEASE_DOCUMENTS_PATH}.${releaseId}`;
672
+ }
673
+ const RELEASE_ACTION_TYPES = /* @__PURE__ */ new Set([
674
+ "release.create",
675
+ "release.edit",
676
+ "release.publish",
677
+ "release.schedule",
678
+ "release.unschedule",
679
+ "release.archive",
680
+ "release.unarchive",
681
+ "release.delete"
682
+ ]);
683
+ function isReleaseAction(action) {
684
+ return RELEASE_ACTION_TYPES.has(action.type);
685
+ }
646
686
  function generateArrayKey(length = 12) {
647
687
  const numBytes = Math.ceil(length / 2), bytes = crypto.getRandomValues(new Uint8Array(numBytes));
648
688
  return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, length);
@@ -1146,6 +1186,52 @@ class MultiKeyWeakMap {
1146
1186
  this.#setDeep(arrangedKeys, this.#rootMap, value);
1147
1187
  }
1148
1188
  }
1189
+ function checkGrant(grantExpr, document, identity) {
1190
+ const value = evaluateSync(grantExpr, { params: { document }, identity });
1191
+ return value.type === "boolean" && value.data;
1192
+ }
1193
+ class ActionError extends Error {
1194
+ documentId;
1195
+ transactionId;
1196
+ constructor(options) {
1197
+ super(options.message), Object.assign(this, options);
1198
+ }
1199
+ }
1200
+ class PermissionActionError extends ActionError {
1201
+ }
1202
+ function applySingleDocPatch({
1203
+ base: initialBase,
1204
+ working: initialWorking,
1205
+ documentId,
1206
+ patches,
1207
+ transactionId,
1208
+ timestamp,
1209
+ grants,
1210
+ identity,
1211
+ notFoundMessage = "Cannot edit document because it does not exist.",
1212
+ permissionMessage = `You do not have permission to edit document "${documentId}".`
1213
+ }) {
1214
+ let base = initialBase, working = initialWorking;
1215
+ const userPatches = patches?.map((patch) => ({ patch: { id: documentId, ...patch } }));
1216
+ if (!userPatches?.length)
1217
+ return { base, working, diffedPatches: [], workingMutations: [] };
1218
+ if (!working[documentId] || !base[documentId])
1219
+ throw new ActionError({ documentId, transactionId, message: notFoundMessage });
1220
+ const baseBefore = base[documentId];
1221
+ base = processMutations({ documents: base, transactionId, mutations: userPatches, timestamp });
1222
+ const baseAfter = base[documentId], diffedPatches = diffValue(baseBefore, baseAfter), workingBefore = working[documentId];
1223
+ if (!checkGrant(grants.update, workingBefore, identity))
1224
+ throw new PermissionActionError({ documentId, transactionId, message: permissionMessage });
1225
+ const workingMutations = diffedPatches.map((patch) => ({
1226
+ patch: { id: documentId, ...patch }
1227
+ }));
1228
+ return working = processMutations({
1229
+ documents: working,
1230
+ transactionId,
1231
+ mutations: workingMutations,
1232
+ timestamp
1233
+ }), { base, working, diffedPatches, workingMutations };
1234
+ }
1149
1235
  function createGrantsLookup(datasetAcl) {
1150
1236
  const filtersByGrant = {
1151
1237
  create: /* @__PURE__ */ new Set(),
@@ -1156,7 +1242,7 @@ function createGrantsLookup(datasetAcl) {
1156
1242
  for (const entry of datasetAcl)
1157
1243
  for (const grant of entry.permissions) {
1158
1244
  const set2 = filtersByGrant[grant];
1159
- set2.add(entry.filter), filtersByGrant[grant] = set2;
1245
+ set2 && (set2.add(entry.filter), filtersByGrant[grant] = set2);
1160
1246
  }
1161
1247
  return Object.fromEntries(
1162
1248
  Object.entries(filtersByGrant).map(([grant, filters]) => {
@@ -1206,22 +1292,18 @@ const documentsCache = new MultiKeyWeakMap(), actionsCache = /* @__PURE__ */ new
1206
1292
  const normalizedActions = Array.isArray(actions) ? actions : [actions], actionsKey = JSON.stringify(normalizedActions);
1207
1293
  return nestedCache.get(actionsKey) || (nestedCache.set(actionsKey, normalizedActions), normalizedActions);
1208
1294
  }
1209
- );
1210
- function checkGrant$1(grantExpr, document) {
1211
- const value = evaluateSync(grantExpr, { params: { document } });
1212
- return value.type === "boolean" && value.data;
1213
- }
1214
- const enNarrowConjunction = new Intl.ListFormat("en", { style: "narrow", type: "conjunction" });
1295
+ ), enNarrowConjunction = new Intl.ListFormat("en", { style: "narrow", type: "conjunction" });
1215
1296
  function calculatePermissions(...args) {
1216
1297
  return _calculatePermissions(...args);
1217
1298
  }
1218
1299
  const _calculatePermissions = createSelector(
1219
1300
  [
1220
1301
  ({ state: { grants } }) => grants,
1302
+ ({ state: { identity } }) => identity,
1221
1303
  documentsSelector,
1222
1304
  memoizedActionsSelector
1223
1305
  ],
1224
- (grants, documents, actions) => {
1306
+ (grants, identity, documents, actions) => {
1225
1307
  if (!documents || !grants || !actions) return;
1226
1308
  const timestamp = (/* @__PURE__ */ new Date()).toISOString(), reasons = [];
1227
1309
  try {
@@ -1231,7 +1313,8 @@ const _calculatePermissions = createSelector(
1231
1313
  working: documents,
1232
1314
  base: documents,
1233
1315
  timestamp,
1234
- grants
1316
+ grants,
1317
+ identity
1235
1318
  });
1236
1319
  } catch (error) {
1237
1320
  if (error instanceof PermissionActionError)
@@ -1253,7 +1336,7 @@ const _calculatePermissions = createSelector(
1253
1336
  if (action.type === "document.edit" && !action.patches?.length) {
1254
1337
  const docId = action.documentId;
1255
1338
  let doc;
1256
- action.liveEdit ? doc = documents[docId] : isReleasePerspective(action.perspective) ? doc = documents[getVersionId(DocumentId(docId), action.perspective.releaseName)] : doc = documents[getDraftId(DocumentId(docId))] ?? documents[getPublishedId(DocumentId(docId))], doc ? checkGrant$1(grants.update, doc) || reasons.push({
1339
+ action.liveEdit ? doc = documents[docId] : isReleasePerspective(action.perspective) ? doc = documents[getVersionId(DocumentId(docId), action.perspective.releaseName)] : doc = documents[getDraftId(DocumentId(docId))] ?? documents[getPublishedId(DocumentId(docId))], doc ? checkGrant(grants.update, doc, identity) || reasons.push({
1257
1340
  type: "access",
1258
1341
  message: `You are not allowed to edit the document with ID "${docId}".`,
1259
1342
  documentId: docId
@@ -1277,21 +1360,8 @@ const _calculatePermissions = createSelector(
1277
1360
  };
1278
1361
  }
1279
1362
  );
1280
- function checkGrant(grantExpr, document) {
1281
- const value = evaluateSync(grantExpr, { params: { document } });
1282
- return value.type === "boolean" && value.data;
1283
- }
1284
- class ActionError extends Error {
1285
- documentId;
1286
- transactionId;
1287
- constructor(options) {
1288
- super(options.message), Object.assign(this, options);
1289
- }
1290
- }
1291
- class PermissionActionError extends ActionError {
1292
- }
1293
1363
  function handleCreate(action, ctx) {
1294
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1364
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1295
1365
  let { base, working } = ctx;
1296
1366
  const documentId = getId(action.documentId);
1297
1367
  if (action.liveEdit) {
@@ -1316,7 +1386,7 @@ function handleCreate(action, ctx) {
1316
1386
  transactionId,
1317
1387
  mutations: mutations2,
1318
1388
  timestamp
1319
- }), !checkGrant(grants.create, working[documentId]))
1389
+ }), !checkGrant(grants.create, working[documentId], identity))
1320
1390
  throw new PermissionActionError({
1321
1391
  documentId,
1322
1392
  transactionId,
@@ -1354,13 +1424,13 @@ function handleCreate(action, ctx) {
1354
1424
  transactionId,
1355
1425
  mutations,
1356
1426
  timestamp
1357
- }), versionId && !checkGrant(grants.create, working[versionId]))
1427
+ }), versionId && !checkGrant(grants.create, working[versionId], identity))
1358
1428
  throw new PermissionActionError({
1359
1429
  documentId,
1360
1430
  transactionId,
1361
1431
  message: `You do not have permission to create a release version for document "${documentId}".`
1362
1432
  });
1363
- if (!versionId && !checkGrant(grants.create, working[draftId]))
1433
+ if (!versionId && !checkGrant(grants.create, working[draftId], identity))
1364
1434
  throw new PermissionActionError({
1365
1435
  documentId,
1366
1436
  transactionId,
@@ -1373,7 +1443,7 @@ function handleCreate(action, ctx) {
1373
1443
  }), { base, working };
1374
1444
  }
1375
1445
  function handleDelete(action, ctx) {
1376
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1446
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1377
1447
  let { base, working } = ctx;
1378
1448
  const documentId = action.documentId;
1379
1449
  if (isReleasePerspective(action.perspective))
@@ -1389,7 +1459,7 @@ function handleDelete(action, ctx) {
1389
1459
  transactionId,
1390
1460
  message: "The document you are trying to delete does not exist."
1391
1461
  });
1392
- if (!checkGrant(grants.update, working[documentId]))
1462
+ if (!checkGrant(grants.update, working[documentId], identity))
1393
1463
  throw new PermissionActionError({
1394
1464
  documentId,
1395
1465
  transactionId,
@@ -1405,7 +1475,7 @@ function handleDelete(action, ctx) {
1405
1475
  transactionId,
1406
1476
  message: working[draftId] ? "Cannot delete a document without a published version." : "The document you are trying to delete does not exist."
1407
1477
  });
1408
- const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId]), cantDeletePublished = working[publishedId] && !checkGrant(grants.update, working[publishedId]);
1478
+ const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId], identity), cantDeletePublished = working[publishedId] && !checkGrant(grants.update, working[publishedId], identity);
1409
1479
  if (cantDeleteDraft || cantDeletePublished)
1410
1480
  throw new PermissionActionError({
1411
1481
  documentId,
@@ -1420,7 +1490,7 @@ function handleDelete(action, ctx) {
1420
1490
  }), { base, working };
1421
1491
  }
1422
1492
  function handleDiscard(action, ctx) {
1423
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1493
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1424
1494
  let { base, working } = ctx;
1425
1495
  const documentId = getId(action.documentId);
1426
1496
  if (action.liveEdit)
@@ -1436,7 +1506,7 @@ function handleDiscard(action, ctx) {
1436
1506
  transactionId,
1437
1507
  message: `There is no draft or version available to discard for document "${documentId}".`
1438
1508
  });
1439
- if (!checkGrant(grants.update, working[versionId]))
1509
+ if (!checkGrant(grants.update, working[versionId], identity))
1440
1510
  throw new PermissionActionError({
1441
1511
  documentId,
1442
1512
  transactionId,
@@ -1448,39 +1518,21 @@ function handleDiscard(action, ctx) {
1448
1518
  }), { base, working };
1449
1519
  }
1450
1520
  function handleEdit(action, ctx) {
1451
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1521
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1452
1522
  let { base, working } = ctx;
1453
1523
  const documentId = getId(action.documentId);
1454
1524
  if (action.liveEdit) {
1455
- const userPatches2 = action.patches?.map((patch) => ({ patch: { id: documentId, ...patch } }));
1456
- if (!userPatches2?.length) return { base, working };
1457
- if (!working[documentId] || !base[documentId])
1458
- throw new ActionError({
1459
- documentId,
1460
- transactionId,
1461
- message: "Cannot edit document because it does not exist."
1462
- });
1463
- const baseBefore2 = base[documentId];
1464
- userPatches2 && (base = processMutations({
1465
- documents: base,
1466
- transactionId,
1467
- mutations: userPatches2,
1468
- timestamp
1469
- }));
1470
- const baseAfter2 = base[documentId], patches2 = diffValue(baseBefore2, baseAfter2), workingBefore2 = working[documentId];
1471
- if (!checkGrant(grants.update, workingBefore2))
1472
- throw new PermissionActionError({
1473
- documentId,
1474
- transactionId,
1475
- message: `You do not have permission to edit document "${documentId}".`
1476
- });
1477
- const workingMutations2 = patches2.map((patch) => ({ patch: { id: documentId, ...patch } }));
1478
- return working = processMutations({
1479
- documents: working,
1525
+ const result = applySingleDocPatch({
1526
+ base,
1527
+ working,
1528
+ documentId,
1529
+ patches: action.patches,
1480
1530
  transactionId,
1481
- mutations: workingMutations2,
1482
- timestamp
1483
- }), outgoingMutations.push(...workingMutations2), { base, working };
1531
+ timestamp,
1532
+ grants,
1533
+ identity
1534
+ });
1535
+ return outgoingMutations.push(...result.workingMutations), { base: result.base, working: result.working };
1484
1536
  }
1485
1537
  const versionId = isReleasePerspective(action.perspective) ? getVersionId(DocumentId(documentId), action.perspective.releaseName) : void 0, draftId = getDraftId(DocumentId(documentId)), publishedId = getPublishedId(DocumentId(documentId)), patchDocumentId = isReleasePerspective(action.perspective) ? versionId : draftId, userPatches = action.patches?.map((patch) => ({
1486
1538
  patch: { id: patchDocumentId, ...patch }
@@ -1511,7 +1563,7 @@ function handleEdit(action, ctx) {
1511
1563
  const baseAfter = base[patchDocumentId], patches = diffValue(baseBefore, baseAfter), workingMutations = [];
1512
1564
  if (!isReleasePerspective(action.perspective) && !working[draftId] && working[publishedId]) {
1513
1565
  const newDraftFromPublished = { ...working[publishedId], _id: draftId };
1514
- if (!checkGrant(grants.create, newDraftFromPublished))
1566
+ if (!checkGrant(grants.create, newDraftFromPublished, identity))
1515
1567
  throw new PermissionActionError({
1516
1568
  documentId,
1517
1569
  transactionId,
@@ -1520,7 +1572,7 @@ function handleEdit(action, ctx) {
1520
1572
  workingMutations.push({ create: newDraftFromPublished });
1521
1573
  }
1522
1574
  const workingBefore = working[patchDocumentId] ?? working[publishedId];
1523
- if (!checkGrant(grants.update, workingBefore))
1575
+ if (!checkGrant(grants.update, workingBefore, identity))
1524
1576
  throw new PermissionActionError({
1525
1577
  documentId,
1526
1578
  transactionId,
@@ -1541,7 +1593,7 @@ function handleEdit(action, ctx) {
1541
1593
  ), { base, working };
1542
1594
  }
1543
1595
  function handlePublish(action, ctx) {
1544
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1596
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1545
1597
  let { base, working } = ctx;
1546
1598
  const documentId = getId(action.documentId);
1547
1599
  if (action.liveEdit || isReleasePerspective(action.perspective))
@@ -1564,19 +1616,19 @@ function handlePublish(action, ctx) {
1564
1616
  message: "Publish aborted: The document has changed elsewhere. Please try again."
1565
1617
  });
1566
1618
  const newPublishedFromDraft = { ...strengthenOnPublish(workingDraft), _id: publishedId }, mutations = [{ delete: { id: draftId } }, { createOrReplace: newPublishedFromDraft }];
1567
- if (working[draftId] && !checkGrant(grants.update, working[draftId]))
1619
+ if (working[draftId] && !checkGrant(grants.update, working[draftId], identity))
1568
1620
  throw new PermissionActionError({
1569
1621
  documentId,
1570
1622
  transactionId,
1571
1623
  message: `Publish failed: You do not have permission to update the draft for "${documentId}".`
1572
1624
  });
1573
- if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft))
1625
+ if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft, identity))
1574
1626
  throw new PermissionActionError({
1575
1627
  documentId,
1576
1628
  transactionId,
1577
1629
  message: `Publish failed: You do not have permission to update the published version of "${documentId}".`
1578
1630
  });
1579
- if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft))
1631
+ if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft, identity))
1580
1632
  throw new PermissionActionError({
1581
1633
  documentId,
1582
1634
  transactionId,
@@ -1603,8 +1655,191 @@ function strengthenOnPublish(draft) {
1603
1655
  }
1604
1656
  return strengthen(draft);
1605
1657
  }
1658
+ function handleReleaseArchive(action, ctx) {
1659
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1660
+ if (!existing)
1661
+ throw new ActionError({
1662
+ documentId: releaseDocumentId,
1663
+ transactionId,
1664
+ message: `Cannot archive release "${action.releaseId}" because it does not exist.`
1665
+ });
1666
+ if (!checkGrant(grants.update, existing, identity))
1667
+ throw new PermissionActionError({
1668
+ documentId: releaseDocumentId,
1669
+ transactionId,
1670
+ message: `You do not have permission to archive release "${action.releaseId}".`
1671
+ });
1672
+ return outgoingActions.push({
1673
+ actionType: "sanity.action.release.archive",
1674
+ releaseId: action.releaseId
1675
+ }), { base, working };
1676
+ }
1677
+ function handleReleaseUnarchive(action, ctx) {
1678
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1679
+ if (!existing)
1680
+ throw new ActionError({
1681
+ documentId: releaseDocumentId,
1682
+ transactionId,
1683
+ message: `Cannot unarchive release "${action.releaseId}" because it does not exist.`
1684
+ });
1685
+ if (!checkGrant(grants.update, existing, identity))
1686
+ throw new PermissionActionError({
1687
+ documentId: releaseDocumentId,
1688
+ transactionId,
1689
+ message: `You do not have permission to unarchive release "${action.releaseId}".`
1690
+ });
1691
+ return outgoingActions.push({
1692
+ actionType: "sanity.action.release.unarchive",
1693
+ releaseId: action.releaseId
1694
+ }), { base, working };
1695
+ }
1696
+ function handleReleaseCreate(action, ctx) {
1697
+ const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx;
1698
+ let { base, working } = ctx;
1699
+ const releaseDocumentId = getReleaseDocumentId(action.releaseId);
1700
+ if (working[releaseDocumentId] || base[releaseDocumentId])
1701
+ throw new ActionError({
1702
+ documentId: releaseDocumentId,
1703
+ transactionId,
1704
+ message: `A release with id "${action.releaseId}" already exists.`
1705
+ });
1706
+ const mutations = [{ create: {
1707
+ _id: releaseDocumentId,
1708
+ _type: "system.release",
1709
+ name: action.releaseId,
1710
+ state: "active",
1711
+ metadata: action.metadata
1712
+ } }];
1713
+ if (base = processMutations({ documents: base, transactionId, mutations, timestamp }), working = processMutations({ documents: working, transactionId, mutations, timestamp }), !checkGrant(grants.create, working[releaseDocumentId], identity))
1714
+ throw new PermissionActionError({
1715
+ documentId: releaseDocumentId,
1716
+ transactionId,
1717
+ message: `You do not have permission to create release "${action.releaseId}".`
1718
+ });
1719
+ return outgoingMutations.push(...mutations), outgoingActions.push({
1720
+ actionType: "sanity.action.release.create",
1721
+ releaseId: action.releaseId,
1722
+ metadata: action.metadata
1723
+ }), { base, working };
1724
+ }
1725
+ const DELETABLE_STATES = /* @__PURE__ */ new Set(["archived", "published"]);
1726
+ function handleReleaseDelete(action, ctx) {
1727
+ const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx;
1728
+ let { base, working } = ctx;
1729
+ const releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1730
+ if (!existing)
1731
+ throw new ActionError({
1732
+ documentId: releaseDocumentId,
1733
+ transactionId,
1734
+ message: `Cannot delete release "${action.releaseId}" because it does not exist.`
1735
+ });
1736
+ const state = existing.state;
1737
+ if (state && typeof state == "string" && !DELETABLE_STATES.has(state))
1738
+ throw new ActionError({
1739
+ documentId: releaseDocumentId,
1740
+ transactionId,
1741
+ message: `Cannot delete release "${action.releaseId}" while it is "${state}". Archive it first.`
1742
+ });
1743
+ if (!checkGrant(grants.update, existing, identity))
1744
+ throw new PermissionActionError({
1745
+ documentId: releaseDocumentId,
1746
+ transactionId,
1747
+ message: `You do not have permission to delete release "${action.releaseId}".`
1748
+ });
1749
+ const mutations = [{ delete: { id: releaseDocumentId } }];
1750
+ return base = processMutations({ documents: base, transactionId, mutations, timestamp }), working = processMutations({ documents: working, transactionId, mutations, timestamp }), outgoingMutations.push(...mutations), outgoingActions.push({
1751
+ actionType: "sanity.action.release.delete",
1752
+ releaseId: action.releaseId
1753
+ }), { base, working };
1754
+ }
1755
+ function handleReleaseEdit(action, ctx) {
1756
+ const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx, { base, working } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), result = applySingleDocPatch({
1757
+ base,
1758
+ working,
1759
+ documentId: releaseDocumentId,
1760
+ patches: [action.patch],
1761
+ transactionId,
1762
+ timestamp,
1763
+ grants,
1764
+ identity,
1765
+ notFoundMessage: `Cannot edit release "${action.releaseId}" because it does not exist.`,
1766
+ permissionMessage: `You do not have permission to edit release "${action.releaseId}".`
1767
+ });
1768
+ return outgoingMutations.push(...result.workingMutations), outgoingActions.push(
1769
+ ...result.diffedPatches.map((patch) => ({
1770
+ actionType: "sanity.action.release.edit",
1771
+ releaseId: action.releaseId,
1772
+ patch
1773
+ }))
1774
+ ), { base: result.base, working: result.working };
1775
+ }
1776
+ function handleReleasePublish(action, ctx) {
1777
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1778
+ if (!existing)
1779
+ throw new ActionError({
1780
+ documentId: releaseDocumentId,
1781
+ transactionId,
1782
+ message: `Cannot publish release "${action.releaseId}" because it does not exist.`
1783
+ });
1784
+ if (!checkGrant(grants.update, existing, identity))
1785
+ throw new PermissionActionError({
1786
+ documentId: releaseDocumentId,
1787
+ transactionId,
1788
+ message: `You do not have permission to publish release "${action.releaseId}".`
1789
+ });
1790
+ return outgoingActions.push({
1791
+ actionType: "sanity.action.release.publish",
1792
+ releaseId: action.releaseId
1793
+ }), { base, working };
1794
+ }
1795
+ function handleReleaseSchedule(action, ctx) {
1796
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId);
1797
+ if (Number.isNaN(Date.parse(action.publishAt)))
1798
+ throw new ActionError({
1799
+ documentId: releaseDocumentId,
1800
+ transactionId,
1801
+ message: `Cannot schedule release "${action.releaseId}": "publishAt" must be a valid ISO 8601 timestamp (received "${action.publishAt}").`
1802
+ });
1803
+ const existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1804
+ if (!existing)
1805
+ throw new ActionError({
1806
+ documentId: releaseDocumentId,
1807
+ transactionId,
1808
+ message: `Cannot schedule release "${action.releaseId}" because it does not exist.`
1809
+ });
1810
+ if (!checkGrant(grants.update, existing, identity))
1811
+ throw new PermissionActionError({
1812
+ documentId: releaseDocumentId,
1813
+ transactionId,
1814
+ message: `You do not have permission to schedule release "${action.releaseId}".`
1815
+ });
1816
+ return outgoingActions.push({
1817
+ actionType: "sanity.action.release.schedule",
1818
+ releaseId: action.releaseId,
1819
+ publishAt: action.publishAt
1820
+ }), { base, working };
1821
+ }
1822
+ function handleReleaseUnschedule(action, ctx) {
1823
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1824
+ if (!existing)
1825
+ throw new ActionError({
1826
+ documentId: releaseDocumentId,
1827
+ transactionId,
1828
+ message: `Cannot unschedule release "${action.releaseId}" because it does not exist.`
1829
+ });
1830
+ if (!checkGrant(grants.update, existing, identity))
1831
+ throw new PermissionActionError({
1832
+ documentId: releaseDocumentId,
1833
+ transactionId,
1834
+ message: `You do not have permission to unschedule release "${action.releaseId}".`
1835
+ });
1836
+ return outgoingActions.push({
1837
+ actionType: "sanity.action.release.unschedule",
1838
+ releaseId: action.releaseId
1839
+ }), { base, working };
1840
+ }
1606
1841
  function handleUnpublish(action, ctx) {
1607
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1842
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1608
1843
  let { base, working } = ctx;
1609
1844
  const documentId = getId(action.documentId);
1610
1845
  if (action.liveEdit || isReleasePerspective(action.perspective))
@@ -1624,13 +1859,13 @@ function handleUnpublish(action, ctx) {
1624
1859
  { delete: { id: publishedId } },
1625
1860
  { createIfNotExists: newDraftFromPublished }
1626
1861
  ];
1627
- if (!checkGrant(grants.update, sourceDoc))
1862
+ if (!checkGrant(grants.update, sourceDoc, identity))
1628
1863
  throw new PermissionActionError({
1629
1864
  documentId,
1630
1865
  transactionId,
1631
1866
  message: `You do not have permission to unpublish the document "${documentId}".`
1632
1867
  });
1633
- if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished))
1868
+ if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished, identity))
1634
1869
  throw new PermissionActionError({
1635
1870
  documentId,
1636
1871
  transactionId,
@@ -1656,10 +1891,17 @@ function processActions({
1656
1891
  working: initialWorking,
1657
1892
  base: initialBase,
1658
1893
  timestamp,
1659
- grants
1894
+ grants,
1895
+ identity
1660
1896
  }) {
1661
1897
  let base = { ...initialBase }, working = { ...initialWorking };
1662
- const outgoingActions = [], outgoingMutations = [];
1898
+ const outgoingActions = [], outgoingMutations = [], liveEditAction = actions.find((action) => !isReleaseAction(action) && action.liveEdit), otherAction = actions.find((action) => isReleaseAction(action) || !action.liveEdit);
1899
+ if (liveEditAction && otherAction)
1900
+ throw new ActionError({
1901
+ documentId: liveEditAction.documentId,
1902
+ transactionId,
1903
+ message: "Cannot combine liveEdit document actions with other actions in the same transaction. Submit them as separate transactions."
1904
+ });
1663
1905
  for (const action of actions) {
1664
1906
  const result = dispatch(action, {
1665
1907
  base,
@@ -1667,6 +1909,7 @@ function processActions({
1667
1909
  transactionId,
1668
1910
  timestamp,
1669
1911
  grants,
1912
+ identity,
1670
1913
  outgoingActions,
1671
1914
  outgoingMutations
1672
1915
  });
@@ -1697,6 +1940,22 @@ function dispatch(action, ctx) {
1697
1940
  return handlePublish(action, ctx);
1698
1941
  case "document.unpublish":
1699
1942
  return handleUnpublish(action, ctx);
1943
+ case "release.create":
1944
+ return handleReleaseCreate(action, ctx);
1945
+ case "release.edit":
1946
+ return handleReleaseEdit(action, ctx);
1947
+ case "release.publish":
1948
+ return handleReleasePublish(action, ctx);
1949
+ case "release.schedule":
1950
+ return handleReleaseSchedule(action, ctx);
1951
+ case "release.unschedule":
1952
+ return handleReleaseUnschedule(action, ctx);
1953
+ case "release.archive":
1954
+ return handleReleaseArchive(action, ctx);
1955
+ case "release.unarchive":
1956
+ return handleReleaseUnarchive(action, ctx);
1957
+ case "release.delete":
1958
+ return handleReleaseDelete(action, ctx);
1700
1959
  default:
1701
1960
  throw new Error(
1702
1961
  `Unknown action type: "${// @ts-expect-error invalid input
@@ -1735,7 +1994,8 @@ function applyFirstQueuedTransaction(prev) {
1735
1994
  working,
1736
1995
  base: working,
1737
1996
  timestamp,
1738
- grants: prev.grants
1997
+ grants: prev.grants,
1998
+ identity: prev.identity
1739
1999
  }), applied = {
1740
2000
  ...queued,
1741
2001
  ...result,
@@ -1779,29 +2039,31 @@ function batchAppliedTransactions([curr, ...rest]) {
1779
2039
  };
1780
2040
  if (!rest.length) return editAction;
1781
2041
  const next = batchAppliedTransactions(rest);
1782
- if (next)
1783
- return next.disableBatching || !!action.liveEdit != !!next.actions[0]?.liveEdit ? editAction : {
1784
- disableBatching: !1,
1785
- // Use the transactionId from the later (next) transaction.
1786
- transactionId: next.transactionId,
1787
- // Accumulate actions: current action first, then later ones.
1788
- actions: [action, ...next.actions],
1789
- // Merge outgoingActions in order.
1790
- outgoingActions: [...curr.outgoingActions, ...next.outgoingActions],
1791
- // Batched transaction IDs: preserve order by placing curr first.
1792
- batchedTransactionIds: [curr.transactionId, ...next.batchedTransactionIds],
1793
- // Merge outgoingMutations in order.
1794
- outgoingMutations: [...curr.outgoingMutations, ...next.outgoingMutations],
1795
- // Working state reflects the latest optimistic changes: later transactions override earlier.
1796
- working: { ...curr.working, ...next.working },
1797
- // Base state (base, previous, previousRevs) must reflect the original state.
1798
- // Use curr values (the earliest transaction) to override later ones.
1799
- previousRevs: { ...next.previousRevs, ...curr.previousRevs },
1800
- previous: { ...next.previous, ...curr.previous },
1801
- base: { ...next.base, ...curr.base },
1802
- // Use the earliest timestamp from curr.
1803
- timestamp: curr.timestamp ?? next.timestamp
1804
- };
2042
+ if (!next) return;
2043
+ if (next.disableBatching) return editAction;
2044
+ const nextFirst = next.actions[0], nextLiveEdit = nextFirst && "liveEdit" in nextFirst ? nextFirst.liveEdit : !1;
2045
+ return !!action.liveEdit != !!nextLiveEdit ? editAction : {
2046
+ disableBatching: !1,
2047
+ // Use the transactionId from the later (next) transaction.
2048
+ transactionId: next.transactionId,
2049
+ // Accumulate actions: current action first, then later ones.
2050
+ actions: [action, ...next.actions],
2051
+ // Merge outgoingActions in order.
2052
+ outgoingActions: [...curr.outgoingActions, ...next.outgoingActions],
2053
+ // Batched transaction IDs: preserve order by placing curr first.
2054
+ batchedTransactionIds: [curr.transactionId, ...next.batchedTransactionIds],
2055
+ // Merge outgoingMutations in order.
2056
+ outgoingMutations: [...curr.outgoingMutations, ...next.outgoingMutations],
2057
+ // Working state reflects the latest optimistic changes: later transactions override earlier.
2058
+ working: { ...curr.working, ...next.working },
2059
+ // Base state (base, previous, previousRevs) must reflect the original state.
2060
+ // Use curr values (the earliest transaction) to override later ones.
2061
+ previousRevs: { ...next.previousRevs, ...curr.previousRevs },
2062
+ previous: { ...next.previous, ...curr.previous },
2063
+ base: { ...next.base, ...curr.base },
2064
+ // Use the earliest timestamp from curr.
2065
+ timestamp: curr.timestamp ?? next.timestamp
2066
+ };
1805
2067
  }
1806
2068
  function transitionAppliedTransactionsToOutgoing(prev) {
1807
2069
  if (prev.outgoing) return prev;
@@ -1855,7 +2117,7 @@ function revertOutgoingTransaction(prev) {
1855
2117
  const nextApplied = [];
1856
2118
  for (const t of prev.applied)
1857
2119
  try {
1858
- const next = processActions({ ...t, working, grants: prev.grants });
2120
+ const next = processActions({ ...t, working, grants: prev.grants, identity: prev.identity });
1859
2121
  working = next.working, nextApplied.push({ ...t, ...next });
1860
2122
  } catch (error) {
1861
2123
  if (error instanceof ActionError) continue;
@@ -1902,7 +2164,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
1902
2164
  const nextApplied = [];
1903
2165
  for (const curr of prev.applied)
1904
2166
  try {
1905
- const next = processActions({ ...curr, working, grants: prev.grants });
2167
+ const next = processActions({ ...curr, working, grants: prev.grants, identity: prev.identity });
1906
2168
  working = next.working, nextApplied.push({ ...curr, ...next });
1907
2169
  } catch (error) {
1908
2170
  if (error instanceof ActionError) {
@@ -1978,32 +2240,43 @@ function manageSubscriberIds({ state }, handles) {
1978
2240
  }
1979
2241
  function getDocumentIdsFromHandleLikes(handles) {
1980
2242
  return handles.flatMap((handle) => {
2243
+ if ("type" in handle && isReleaseAction(handle))
2244
+ return [getReleaseDocumentId(handle.releaseId)];
1981
2245
  const idsForDocument = [];
1982
2246
  return handle.documentId ? handle.liveEdit ? [handle.documentId] : (isReleasePerspective(handle.perspective) && idsForDocument.push(
1983
2247
  getVersionId(DocumentId(handle.documentId), handle.perspective.releaseName)
1984
2248
  ), idsForDocument.push(getPublishedId(DocumentId(handle.documentId))), idsForDocument.push(getDraftId(DocumentId(handle.documentId))), idsForDocument) : [];
1985
2249
  });
1986
2250
  }
2251
+ const actionMap = {
2252
+ "document.create": "created",
2253
+ "document.delete": "deleted",
2254
+ "document.discard": "discarded",
2255
+ "document.edit": "edited",
2256
+ "document.publish": "published",
2257
+ "document.unpublish": "unpublished",
2258
+ "release.create": "created",
2259
+ "release.edit": "edited",
2260
+ "release.delete": "deleted"
2261
+ };
1987
2262
  function getDocumentEvents(outgoing) {
1988
- const documentIdsByAction = Object.entries(
1989
- outgoing.actions.reduce(
1990
- (acc, { type, documentId }) => {
1991
- const ids = acc[type] || /* @__PURE__ */ new Set();
1992
- return documentId && ids.add(documentId), acc[type] = ids, acc;
1993
- },
1994
- {}
1995
- )
1996
- ), actionMap = {
1997
- "document.create": "created",
1998
- "document.delete": "deleted",
1999
- "document.discard": "discarded",
2000
- "document.edit": "edited",
2001
- "document.publish": "published",
2002
- "document.unpublish": "unpublished"
2003
- };
2004
- return documentIdsByAction.flatMap(
2005
- ([actionType, documentIds]) => Array.from(documentIds).map(
2006
- (documentId) => ({ type: actionMap[actionType], documentId, outgoing })
2263
+ const documentIdsByAction = outgoing.actions.reduce(
2264
+ (acc, action) => {
2265
+ if (!(action.type in actionMap)) return acc;
2266
+ const documentId = isReleaseAction(action) ? getReleaseDocumentId(action.releaseId) : action.documentId;
2267
+ if (!documentId) return acc;
2268
+ const type = action.type, ids = acc[type] ?? /* @__PURE__ */ new Set();
2269
+ return ids.add(documentId), acc[type] = ids, acc;
2270
+ },
2271
+ {}
2272
+ );
2273
+ return Object.entries(documentIdsByAction).flatMap(
2274
+ ([actionType, documentIds]) => Array.from(documentIds ?? []).map(
2275
+ (documentId) => ({
2276
+ type: actionMap[actionType],
2277
+ documentId,
2278
+ outgoing
2279
+ })
2007
2280
  )
2008
2281
  );
2009
2282
  }
@@ -2079,7 +2352,8 @@ const documentStore = defineStore({
2079
2352
  subscribeToQueuedAndApplyNextTransaction(context),
2080
2353
  subscribeToSubscriptionsAndListenToDocuments(context),
2081
2354
  subscribeToAppliedAndSubmitNextTransaction(context),
2082
- subscribeToClientAndFetchDatasetAcl(context)
2355
+ subscribeToClientAndFetchDatasetAcl(context),
2356
+ subscribeToCurrentUserAndSetIdentity(context)
2083
2357
  ];
2084
2358
  return () => {
2085
2359
  sharedListener.dispose(), subscriptions.forEach((subscription) => subscription.unsubscribe());
@@ -2227,7 +2501,7 @@ const _resolveDocument = bindActionByResource(
2227
2501
  result,
2228
2502
  outgoing
2229
2503
  }));
2230
- return outgoing.actions.some((action) => action.liveEdit) ? client.observable.mutate(outgoing.outgoingMutations, {
2504
+ return outgoing.actions.some((action) => !isReleaseAction(action) && action.liveEdit) ? client.observable.mutate(outgoing.outgoingMutations, {
2231
2505
  transactionId: outgoing.transactionId,
2232
2506
  visibility: "async",
2233
2507
  returnDocuments: !1,
@@ -2272,8 +2546,15 @@ const _resolveDocument = bindActionByResource(
2272
2546
  mergeMap$1(
2273
2547
  (group) => group.pipe(
2274
2548
  switchMap((e) => e.add ? listen(context, e.id).pipe(
2275
- catchError$1((error) => {
2276
- throw error instanceof OutOfSyncError && listen(context, e.id), error;
2549
+ retry({
2550
+ delay: (error, retryCount) => {
2551
+ if (!(error instanceof OutOfSyncError)) return throwError(() => error);
2552
+ const backoff = Math.min(
2553
+ OUT_OF_SYNC_RETRY_BASE_DELAY * 2 ** (retryCount - 1),
2554
+ OUT_OF_SYNC_RETRY_MAX_DELAY
2555
+ );
2556
+ return timer(backoff);
2557
+ }
2277
2558
  }),
2278
2559
  tap$1(
2279
2560
  (remote) => state.set(
@@ -2312,15 +2593,58 @@ const _resolveDocument = bindActionByResource(
2312
2593
  ).subscribe({
2313
2594
  error: (error) => state.set("setError", { error })
2314
2595
  });
2315
- };
2596
+ }, subscribeToCurrentUserAndSetIdentity = ({
2597
+ instance,
2598
+ state
2599
+ }) => getCurrentUserState(instance).observable.subscribe({
2600
+ next: (currentUser) => state.set("setIdentity", { identity: currentUser?.id }),
2601
+ // A transient identity-fetch failure (network blip, expired token, or a
2602
+ // normal logout/re-login transition) should not brick all document
2603
+ // operations. Reset the identity to `undefined` and keep going — GROQ's
2604
+ // `identity()` then evaluates to null, matching the unauthenticated state.
2605
+ error: () => state.set("setIdentity", { identity: void 0 })
2606
+ }), MEDIA_LIBRARY_DRAFTED_TYPES = /* @__PURE__ */ new Set(["sanity.asset"]);
2607
+ function getEffectiveDocumentModel(resource, documentType) {
2608
+ return resource ? isCanvasResource(resource) ? { liveEdit: !0, supportsReleases: !1 } : isMediaLibraryResource(resource) ? { liveEdit: !(documentType && MEDIA_LIBRARY_DRAFTED_TYPES.has(documentType)), supportsReleases: !1 } : { liveEdit: void 0, supportsReleases: !0 } : { liveEdit: void 0, supportsReleases: !0 };
2609
+ }
2610
+ function describeResource(resource) {
2611
+ return resource && isCanvasResource(resource) ? "Canvas" : resource && isMediaLibraryResource(resource) ? "Media Library" : "this resource";
2612
+ }
2613
+ function normalizeActionsForResource(actions, resource) {
2614
+ const stripped = [], normalized = actions.map((action) => {
2615
+ if (action.type !== "document.edit") return action;
2616
+ const { liveEdit: forcedLiveEdit, supportsReleases } = getEffectiveDocumentModel(
2617
+ resource,
2618
+ action.documentType
2619
+ ), shouldRemovePerspective = isReleasePerspective(action.perspective) && !supportsReleases, shouldForceLiveEdit = forcedLiveEdit === !0 && !action.liveEdit;
2620
+ if (!shouldRemovePerspective && !shouldForceLiveEdit) return action;
2621
+ const corrected = { ...action };
2622
+ return shouldForceLiveEdit && (corrected.liveEdit = !0), shouldRemovePerspective && (corrected.perspective = void 0), corrected.documentId = getEffectiveDocumentId({
2623
+ ...corrected,
2624
+ documentId: getPublishedId(DocumentId(corrected.documentId))
2625
+ }), shouldRemovePerspective && stripped.push({ documentType: action.documentType, documentId: corrected.documentId }), corrected;
2626
+ });
2627
+ if (stripped.length > 0) {
2628
+ const docs = stripped.map((e) => `${e.documentType} (${e.documentId})`).join(", ");
2629
+ console.warn(
2630
+ `[sanity-sdk] ${describeResource(resource)} does not support release perspectives \u2014 falling back to the standard editing path for: ${docs}`
2631
+ );
2632
+ }
2633
+ return normalized;
2634
+ }
2316
2635
  function applyDocumentActions(...args) {
2317
2636
  return boundApplyDocumentActions(...args);
2318
2637
  }
2319
2638
  const boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocumentActions);
2320
- async function _applyDocumentActions({ state }, { actions, transactionId = crypto.randomUUID(), disableBatching }) {
2321
- const { events } = state.get(), transaction = {
2639
+ async function _applyDocumentActions({ state }, {
2640
+ actions,
2641
+ resource,
2642
+ transactionId = crypto.randomUUID(),
2643
+ disableBatching
2644
+ }) {
2645
+ const { events } = state.get(), normalizedActions = normalizeActionsForResource(actions, resource), transaction = {
2322
2646
  transactionId,
2323
- actions,
2647
+ actions: normalizedActions,
2324
2648
  ...disableBatching && { disableBatching }
2325
2649
  }, fatalError$ = state.observable.pipe(
2326
2650
  map$1((s) => s.error),
@@ -3270,6 +3594,7 @@ export {
3270
3594
  agentTransform,
3271
3595
  agentTranslate,
3272
3596
  applyDocumentActions,
3597
+ archiveRelease,
3273
3598
  configureLogging,
3274
3599
  createDatasetHandle,
3275
3600
  createDocument,
@@ -3277,13 +3602,17 @@ export {
3277
3602
  createDocumentTypeHandle,
3278
3603
  createGroqSearchFilter,
3279
3604
  createProjectHandle,
3605
+ createRelease,
3280
3606
  createSanityInstance,
3281
3607
  defineIntent,
3282
3608
  deleteDocument,
3609
+ deleteRelease,
3283
3610
  destroyController,
3284
3611
  discardDocument,
3285
3612
  editDocument,
3613
+ editRelease,
3286
3614
  getActiveReleasesState,
3615
+ getAllReleasesState,
3287
3616
  getAuthState,
3288
3617
  getClient,
3289
3618
  getClientErrorApiBody,
@@ -3316,6 +3645,7 @@ export {
3316
3645
  getProjectsState,
3317
3646
  getQueryKey,
3318
3647
  getQueryState,
3648
+ getReleaseDocumentId,
3319
3649
  getTokenState,
3320
3650
  getUserState,
3321
3651
  getUsersKey,
@@ -3338,6 +3668,7 @@ export {
3338
3668
  parseQueryKey,
3339
3669
  parseUsersKey,
3340
3670
  publishDocument,
3671
+ publishRelease,
3341
3672
  releaseChannel,
3342
3673
  releaseNode,
3343
3674
  resolveDatasets,
@@ -3353,11 +3684,14 @@ export {
3353
3684
  resolveQuery,
3354
3685
  resolveUser,
3355
3686
  resolveUsers,
3687
+ scheduleRelease,
3356
3688
  setAuthToken,
3357
3689
  slicePath2 as slicePath,
3358
3690
  stringifyPath2 as stringifyPath,
3359
3691
  subscribeDocumentEvents,
3360
3692
  transformProjectionToPreview,
3361
- unpublishDocument
3693
+ unarchiveRelease,
3694
+ unpublishDocument,
3695
+ unscheduleRelease
3362
3696
  };
3363
3697
  //# sourceMappingURL=index.js.map