@sanity/sdk 2.12.0 → 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 (30) hide show
  1. package/dist/_chunks-dts/utils.d.ts +4 -0
  2. package/dist/_chunks-es/version.js +1 -1
  3. package/dist/index.js +160 -106
  4. package/dist/index.js.map +1 -1
  5. package/package.json +11 -11
  6. package/src/document/applyDocumentActions.test.ts +24 -0
  7. package/src/document/applyDocumentActions.ts +13 -2
  8. package/src/document/documentConstants.ts +7 -0
  9. package/src/document/documentStore.test.ts +69 -0
  10. package/src/document/documentStore.ts +36 -5
  11. package/src/document/listen.ts +1 -1
  12. package/src/document/permissions.test.ts +79 -0
  13. package/src/document/permissions.ts +8 -7
  14. package/src/document/processActions/create.ts +7 -4
  15. package/src/document/processActions/delete.ts +4 -4
  16. package/src/document/processActions/discard.ts +2 -2
  17. package/src/document/processActions/edit.ts +4 -3
  18. package/src/document/processActions/processActions.ts +9 -0
  19. package/src/document/processActions/publish.ts +4 -4
  20. package/src/document/processActions/releaseArchive.ts +4 -4
  21. package/src/document/processActions/releaseCreate.ts +2 -2
  22. package/src/document/processActions/releaseDelete.ts +2 -2
  23. package/src/document/processActions/releaseEdit.ts +2 -1
  24. package/src/document/processActions/releasePublish.ts +2 -2
  25. package/src/document/processActions/releaseSchedule.ts +4 -4
  26. package/src/document/processActions/shared.ts +15 -3
  27. package/src/document/processActions/unpublish.ts +3 -3
  28. package/src/document/reducers.ts +4 -3
  29. package/src/document/resourceRules.test.ts +178 -0
  30. package/src/document/resourceRules.ts +117 -0
@@ -1359,6 +1359,10 @@ interface DocumentStoreState {
1359
1359
  applied: AppliedTransaction[];
1360
1360
  outgoing?: OutgoingTransaction;
1361
1361
  grants?: Record<Grant, ExprNode>;
1362
+ /**
1363
+ * The current user's identity (their user ID).
1364
+ */
1365
+ identity?: string;
1362
1366
  error?: unknown;
1363
1367
  sharedListener: SharedListener;
1364
1368
  fetchDocument: (documentId: string) => Observable<SanityDocument$2 | null>;
@@ -1,5 +1,5 @@
1
1
  import { getEnv } from "./createGroqSearchFilter.js";
2
- var version = "2.12.0";
2
+ var version = "2.13.0";
3
3
  const CORE_SDK_VERSION = getEnv("PKG_VERSION") || `${version}-development`;
4
4
  export {
5
5
  CORE_SDK_VERSION
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, getAllReleasesState, 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), () => {
@@ -666,7 +666,7 @@ function unarchiveRelease(handle) {
666
666
  function deleteRelease(handle) {
667
667
  return { type: "release.delete", ...handle };
668
668
  }
669
- const DOCUMENT_STATE_CLEAR_DELAY = 1e3, INITIAL_OUTGOING_THROTTLE_TIME = 1e3, API_VERSION$4 = "v2025-05-06", RELEASE_DOCUMENTS_PATH = "_.releases";
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
670
  function getReleaseDocumentId(releaseId) {
671
671
  return `${RELEASE_DOCUMENTS_PATH}.${releaseId}`;
672
672
  }
@@ -1186,6 +1186,52 @@ class MultiKeyWeakMap {
1186
1186
  this.#setDeep(arrangedKeys, this.#rootMap, value);
1187
1187
  }
1188
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
+ }
1189
1235
  function createGrantsLookup(datasetAcl) {
1190
1236
  const filtersByGrant = {
1191
1237
  create: /* @__PURE__ */ new Set(),
@@ -1196,7 +1242,7 @@ function createGrantsLookup(datasetAcl) {
1196
1242
  for (const entry of datasetAcl)
1197
1243
  for (const grant of entry.permissions) {
1198
1244
  const set2 = filtersByGrant[grant];
1199
- set2.add(entry.filter), filtersByGrant[grant] = set2;
1245
+ set2 && (set2.add(entry.filter), filtersByGrant[grant] = set2);
1200
1246
  }
1201
1247
  return Object.fromEntries(
1202
1248
  Object.entries(filtersByGrant).map(([grant, filters]) => {
@@ -1246,22 +1292,18 @@ const documentsCache = new MultiKeyWeakMap(), actionsCache = /* @__PURE__ */ new
1246
1292
  const normalizedActions = Array.isArray(actions) ? actions : [actions], actionsKey = JSON.stringify(normalizedActions);
1247
1293
  return nestedCache.get(actionsKey) || (nestedCache.set(actionsKey, normalizedActions), normalizedActions);
1248
1294
  }
1249
- );
1250
- function checkGrant$1(grantExpr, document) {
1251
- const value = evaluateSync(grantExpr, { params: { document } });
1252
- return value.type === "boolean" && value.data;
1253
- }
1254
- const enNarrowConjunction = new Intl.ListFormat("en", { style: "narrow", type: "conjunction" });
1295
+ ), enNarrowConjunction = new Intl.ListFormat("en", { style: "narrow", type: "conjunction" });
1255
1296
  function calculatePermissions(...args) {
1256
1297
  return _calculatePermissions(...args);
1257
1298
  }
1258
1299
  const _calculatePermissions = createSelector(
1259
1300
  [
1260
1301
  ({ state: { grants } }) => grants,
1302
+ ({ state: { identity } }) => identity,
1261
1303
  documentsSelector,
1262
1304
  memoizedActionsSelector
1263
1305
  ],
1264
- (grants, documents, actions) => {
1306
+ (grants, identity, documents, actions) => {
1265
1307
  if (!documents || !grants || !actions) return;
1266
1308
  const timestamp = (/* @__PURE__ */ new Date()).toISOString(), reasons = [];
1267
1309
  try {
@@ -1271,7 +1313,8 @@ const _calculatePermissions = createSelector(
1271
1313
  working: documents,
1272
1314
  base: documents,
1273
1315
  timestamp,
1274
- grants
1316
+ grants,
1317
+ identity
1275
1318
  });
1276
1319
  } catch (error) {
1277
1320
  if (error instanceof PermissionActionError)
@@ -1293,7 +1336,7 @@ const _calculatePermissions = createSelector(
1293
1336
  if (action.type === "document.edit" && !action.patches?.length) {
1294
1337
  const docId = action.documentId;
1295
1338
  let doc;
1296
- 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({
1297
1340
  type: "access",
1298
1341
  message: `You are not allowed to edit the document with ID "${docId}".`,
1299
1342
  documentId: docId
@@ -1317,53 +1360,8 @@ const _calculatePermissions = createSelector(
1317
1360
  };
1318
1361
  }
1319
1362
  );
1320
- function checkGrant(grantExpr, document) {
1321
- const value = evaluateSync(grantExpr, { params: { document } });
1322
- return value.type === "boolean" && value.data;
1323
- }
1324
- class ActionError extends Error {
1325
- documentId;
1326
- transactionId;
1327
- constructor(options) {
1328
- super(options.message), Object.assign(this, options);
1329
- }
1330
- }
1331
- class PermissionActionError extends ActionError {
1332
- }
1333
- function applySingleDocPatch({
1334
- base: initialBase,
1335
- working: initialWorking,
1336
- documentId,
1337
- patches,
1338
- transactionId,
1339
- timestamp,
1340
- grants,
1341
- notFoundMessage = "Cannot edit document because it does not exist.",
1342
- permissionMessage = `You do not have permission to edit document "${documentId}".`
1343
- }) {
1344
- let base = initialBase, working = initialWorking;
1345
- const userPatches = patches?.map((patch) => ({ patch: { id: documentId, ...patch } }));
1346
- if (!userPatches?.length)
1347
- return { base, working, diffedPatches: [], workingMutations: [] };
1348
- if (!working[documentId] || !base[documentId])
1349
- throw new ActionError({ documentId, transactionId, message: notFoundMessage });
1350
- const baseBefore = base[documentId];
1351
- base = processMutations({ documents: base, transactionId, mutations: userPatches, timestamp });
1352
- const baseAfter = base[documentId], diffedPatches = diffValue(baseBefore, baseAfter), workingBefore = working[documentId];
1353
- if (!checkGrant(grants.update, workingBefore))
1354
- throw new PermissionActionError({ documentId, transactionId, message: permissionMessage });
1355
- const workingMutations = diffedPatches.map((patch) => ({
1356
- patch: { id: documentId, ...patch }
1357
- }));
1358
- return working = processMutations({
1359
- documents: working,
1360
- transactionId,
1361
- mutations: workingMutations,
1362
- timestamp
1363
- }), { base, working, diffedPatches, workingMutations };
1364
- }
1365
1363
  function handleCreate(action, ctx) {
1366
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1364
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1367
1365
  let { base, working } = ctx;
1368
1366
  const documentId = getId(action.documentId);
1369
1367
  if (action.liveEdit) {
@@ -1388,7 +1386,7 @@ function handleCreate(action, ctx) {
1388
1386
  transactionId,
1389
1387
  mutations: mutations2,
1390
1388
  timestamp
1391
- }), !checkGrant(grants.create, working[documentId]))
1389
+ }), !checkGrant(grants.create, working[documentId], identity))
1392
1390
  throw new PermissionActionError({
1393
1391
  documentId,
1394
1392
  transactionId,
@@ -1426,13 +1424,13 @@ function handleCreate(action, ctx) {
1426
1424
  transactionId,
1427
1425
  mutations,
1428
1426
  timestamp
1429
- }), versionId && !checkGrant(grants.create, working[versionId]))
1427
+ }), versionId && !checkGrant(grants.create, working[versionId], identity))
1430
1428
  throw new PermissionActionError({
1431
1429
  documentId,
1432
1430
  transactionId,
1433
1431
  message: `You do not have permission to create a release version for document "${documentId}".`
1434
1432
  });
1435
- if (!versionId && !checkGrant(grants.create, working[draftId]))
1433
+ if (!versionId && !checkGrant(grants.create, working[draftId], identity))
1436
1434
  throw new PermissionActionError({
1437
1435
  documentId,
1438
1436
  transactionId,
@@ -1445,7 +1443,7 @@ function handleCreate(action, ctx) {
1445
1443
  }), { base, working };
1446
1444
  }
1447
1445
  function handleDelete(action, ctx) {
1448
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1446
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1449
1447
  let { base, working } = ctx;
1450
1448
  const documentId = action.documentId;
1451
1449
  if (isReleasePerspective(action.perspective))
@@ -1461,7 +1459,7 @@ function handleDelete(action, ctx) {
1461
1459
  transactionId,
1462
1460
  message: "The document you are trying to delete does not exist."
1463
1461
  });
1464
- if (!checkGrant(grants.update, working[documentId]))
1462
+ if (!checkGrant(grants.update, working[documentId], identity))
1465
1463
  throw new PermissionActionError({
1466
1464
  documentId,
1467
1465
  transactionId,
@@ -1477,7 +1475,7 @@ function handleDelete(action, ctx) {
1477
1475
  transactionId,
1478
1476
  message: working[draftId] ? "Cannot delete a document without a published version." : "The document you are trying to delete does not exist."
1479
1477
  });
1480
- 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);
1481
1479
  if (cantDeleteDraft || cantDeletePublished)
1482
1480
  throw new PermissionActionError({
1483
1481
  documentId,
@@ -1492,7 +1490,7 @@ function handleDelete(action, ctx) {
1492
1490
  }), { base, working };
1493
1491
  }
1494
1492
  function handleDiscard(action, ctx) {
1495
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1493
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1496
1494
  let { base, working } = ctx;
1497
1495
  const documentId = getId(action.documentId);
1498
1496
  if (action.liveEdit)
@@ -1508,7 +1506,7 @@ function handleDiscard(action, ctx) {
1508
1506
  transactionId,
1509
1507
  message: `There is no draft or version available to discard for document "${documentId}".`
1510
1508
  });
1511
- if (!checkGrant(grants.update, working[versionId]))
1509
+ if (!checkGrant(grants.update, working[versionId], identity))
1512
1510
  throw new PermissionActionError({
1513
1511
  documentId,
1514
1512
  transactionId,
@@ -1520,7 +1518,7 @@ function handleDiscard(action, ctx) {
1520
1518
  }), { base, working };
1521
1519
  }
1522
1520
  function handleEdit(action, ctx) {
1523
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1521
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1524
1522
  let { base, working } = ctx;
1525
1523
  const documentId = getId(action.documentId);
1526
1524
  if (action.liveEdit) {
@@ -1531,7 +1529,8 @@ function handleEdit(action, ctx) {
1531
1529
  patches: action.patches,
1532
1530
  transactionId,
1533
1531
  timestamp,
1534
- grants
1532
+ grants,
1533
+ identity
1535
1534
  });
1536
1535
  return outgoingMutations.push(...result.workingMutations), { base: result.base, working: result.working };
1537
1536
  }
@@ -1564,7 +1563,7 @@ function handleEdit(action, ctx) {
1564
1563
  const baseAfter = base[patchDocumentId], patches = diffValue(baseBefore, baseAfter), workingMutations = [];
1565
1564
  if (!isReleasePerspective(action.perspective) && !working[draftId] && working[publishedId]) {
1566
1565
  const newDraftFromPublished = { ...working[publishedId], _id: draftId };
1567
- if (!checkGrant(grants.create, newDraftFromPublished))
1566
+ if (!checkGrant(grants.create, newDraftFromPublished, identity))
1568
1567
  throw new PermissionActionError({
1569
1568
  documentId,
1570
1569
  transactionId,
@@ -1573,7 +1572,7 @@ function handleEdit(action, ctx) {
1573
1572
  workingMutations.push({ create: newDraftFromPublished });
1574
1573
  }
1575
1574
  const workingBefore = working[patchDocumentId] ?? working[publishedId];
1576
- if (!checkGrant(grants.update, workingBefore))
1575
+ if (!checkGrant(grants.update, workingBefore, identity))
1577
1576
  throw new PermissionActionError({
1578
1577
  documentId,
1579
1578
  transactionId,
@@ -1594,7 +1593,7 @@ function handleEdit(action, ctx) {
1594
1593
  ), { base, working };
1595
1594
  }
1596
1595
  function handlePublish(action, ctx) {
1597
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1596
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1598
1597
  let { base, working } = ctx;
1599
1598
  const documentId = getId(action.documentId);
1600
1599
  if (action.liveEdit || isReleasePerspective(action.perspective))
@@ -1617,19 +1616,19 @@ function handlePublish(action, ctx) {
1617
1616
  message: "Publish aborted: The document has changed elsewhere. Please try again."
1618
1617
  });
1619
1618
  const newPublishedFromDraft = { ...strengthenOnPublish(workingDraft), _id: publishedId }, mutations = [{ delete: { id: draftId } }, { createOrReplace: newPublishedFromDraft }];
1620
- if (working[draftId] && !checkGrant(grants.update, working[draftId]))
1619
+ if (working[draftId] && !checkGrant(grants.update, working[draftId], identity))
1621
1620
  throw new PermissionActionError({
1622
1621
  documentId,
1623
1622
  transactionId,
1624
1623
  message: `Publish failed: You do not have permission to update the draft for "${documentId}".`
1625
1624
  });
1626
- if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft))
1625
+ if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft, identity))
1627
1626
  throw new PermissionActionError({
1628
1627
  documentId,
1629
1628
  transactionId,
1630
1629
  message: `Publish failed: You do not have permission to update the published version of "${documentId}".`
1631
1630
  });
1632
- if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft))
1631
+ if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft, identity))
1633
1632
  throw new PermissionActionError({
1634
1633
  documentId,
1635
1634
  transactionId,
@@ -1657,14 +1656,14 @@ function strengthenOnPublish(draft) {
1657
1656
  return strengthen(draft);
1658
1657
  }
1659
1658
  function handleReleaseArchive(action, ctx) {
1660
- const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1659
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1661
1660
  if (!existing)
1662
1661
  throw new ActionError({
1663
1662
  documentId: releaseDocumentId,
1664
1663
  transactionId,
1665
1664
  message: `Cannot archive release "${action.releaseId}" because it does not exist.`
1666
1665
  });
1667
- if (!checkGrant(grants.update, existing))
1666
+ if (!checkGrant(grants.update, existing, identity))
1668
1667
  throw new PermissionActionError({
1669
1668
  documentId: releaseDocumentId,
1670
1669
  transactionId,
@@ -1676,14 +1675,14 @@ function handleReleaseArchive(action, ctx) {
1676
1675
  }), { base, working };
1677
1676
  }
1678
1677
  function handleReleaseUnarchive(action, ctx) {
1679
- const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1678
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1680
1679
  if (!existing)
1681
1680
  throw new ActionError({
1682
1681
  documentId: releaseDocumentId,
1683
1682
  transactionId,
1684
1683
  message: `Cannot unarchive release "${action.releaseId}" because it does not exist.`
1685
1684
  });
1686
- if (!checkGrant(grants.update, existing))
1685
+ if (!checkGrant(grants.update, existing, identity))
1687
1686
  throw new PermissionActionError({
1688
1687
  documentId: releaseDocumentId,
1689
1688
  transactionId,
@@ -1695,7 +1694,7 @@ function handleReleaseUnarchive(action, ctx) {
1695
1694
  }), { base, working };
1696
1695
  }
1697
1696
  function handleReleaseCreate(action, ctx) {
1698
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1697
+ const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx;
1699
1698
  let { base, working } = ctx;
1700
1699
  const releaseDocumentId = getReleaseDocumentId(action.releaseId);
1701
1700
  if (working[releaseDocumentId] || base[releaseDocumentId])
@@ -1711,7 +1710,7 @@ function handleReleaseCreate(action, ctx) {
1711
1710
  state: "active",
1712
1711
  metadata: action.metadata
1713
1712
  } }];
1714
- if (base = processMutations({ documents: base, transactionId, mutations, timestamp }), working = processMutations({ documents: working, transactionId, mutations, timestamp }), !checkGrant(grants.create, working[releaseDocumentId]))
1713
+ if (base = processMutations({ documents: base, transactionId, mutations, timestamp }), working = processMutations({ documents: working, transactionId, mutations, timestamp }), !checkGrant(grants.create, working[releaseDocumentId], identity))
1715
1714
  throw new PermissionActionError({
1716
1715
  documentId: releaseDocumentId,
1717
1716
  transactionId,
@@ -1725,7 +1724,7 @@ function handleReleaseCreate(action, ctx) {
1725
1724
  }
1726
1725
  const DELETABLE_STATES = /* @__PURE__ */ new Set(["archived", "published"]);
1727
1726
  function handleReleaseDelete(action, ctx) {
1728
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1727
+ const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx;
1729
1728
  let { base, working } = ctx;
1730
1729
  const releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1731
1730
  if (!existing)
@@ -1741,7 +1740,7 @@ function handleReleaseDelete(action, ctx) {
1741
1740
  transactionId,
1742
1741
  message: `Cannot delete release "${action.releaseId}" while it is "${state}". Archive it first.`
1743
1742
  });
1744
- if (!checkGrant(grants.update, existing))
1743
+ if (!checkGrant(grants.update, existing, identity))
1745
1744
  throw new PermissionActionError({
1746
1745
  documentId: releaseDocumentId,
1747
1746
  transactionId,
@@ -1754,7 +1753,7 @@ function handleReleaseDelete(action, ctx) {
1754
1753
  }), { base, working };
1755
1754
  }
1756
1755
  function handleReleaseEdit(action, ctx) {
1757
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx, { base, working } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), result = applySingleDocPatch({
1756
+ const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx, { base, working } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), result = applySingleDocPatch({
1758
1757
  base,
1759
1758
  working,
1760
1759
  documentId: releaseDocumentId,
@@ -1762,6 +1761,7 @@ function handleReleaseEdit(action, ctx) {
1762
1761
  transactionId,
1763
1762
  timestamp,
1764
1763
  grants,
1764
+ identity,
1765
1765
  notFoundMessage: `Cannot edit release "${action.releaseId}" because it does not exist.`,
1766
1766
  permissionMessage: `You do not have permission to edit release "${action.releaseId}".`
1767
1767
  });
@@ -1774,14 +1774,14 @@ function handleReleaseEdit(action, ctx) {
1774
1774
  ), { base: result.base, working: result.working };
1775
1775
  }
1776
1776
  function handleReleasePublish(action, ctx) {
1777
- const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1777
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1778
1778
  if (!existing)
1779
1779
  throw new ActionError({
1780
1780
  documentId: releaseDocumentId,
1781
1781
  transactionId,
1782
1782
  message: `Cannot publish release "${action.releaseId}" because it does not exist.`
1783
1783
  });
1784
- if (!checkGrant(grants.update, existing))
1784
+ if (!checkGrant(grants.update, existing, identity))
1785
1785
  throw new PermissionActionError({
1786
1786
  documentId: releaseDocumentId,
1787
1787
  transactionId,
@@ -1793,7 +1793,7 @@ function handleReleasePublish(action, ctx) {
1793
1793
  }), { base, working };
1794
1794
  }
1795
1795
  function handleReleaseSchedule(action, ctx) {
1796
- const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId);
1796
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId);
1797
1797
  if (Number.isNaN(Date.parse(action.publishAt)))
1798
1798
  throw new ActionError({
1799
1799
  documentId: releaseDocumentId,
@@ -1807,7 +1807,7 @@ function handleReleaseSchedule(action, ctx) {
1807
1807
  transactionId,
1808
1808
  message: `Cannot schedule release "${action.releaseId}" because it does not exist.`
1809
1809
  });
1810
- if (!checkGrant(grants.update, existing))
1810
+ if (!checkGrant(grants.update, existing, identity))
1811
1811
  throw new PermissionActionError({
1812
1812
  documentId: releaseDocumentId,
1813
1813
  transactionId,
@@ -1820,14 +1820,14 @@ function handleReleaseSchedule(action, ctx) {
1820
1820
  }), { base, working };
1821
1821
  }
1822
1822
  function handleReleaseUnschedule(action, ctx) {
1823
- const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1823
+ const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
1824
1824
  if (!existing)
1825
1825
  throw new ActionError({
1826
1826
  documentId: releaseDocumentId,
1827
1827
  transactionId,
1828
1828
  message: `Cannot unschedule release "${action.releaseId}" because it does not exist.`
1829
1829
  });
1830
- if (!checkGrant(grants.update, existing))
1830
+ if (!checkGrant(grants.update, existing, identity))
1831
1831
  throw new PermissionActionError({
1832
1832
  documentId: releaseDocumentId,
1833
1833
  transactionId,
@@ -1839,7 +1839,7 @@ function handleReleaseUnschedule(action, ctx) {
1839
1839
  }), { base, working };
1840
1840
  }
1841
1841
  function handleUnpublish(action, ctx) {
1842
- const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
1842
+ const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
1843
1843
  let { base, working } = ctx;
1844
1844
  const documentId = getId(action.documentId);
1845
1845
  if (action.liveEdit || isReleasePerspective(action.perspective))
@@ -1859,13 +1859,13 @@ function handleUnpublish(action, ctx) {
1859
1859
  { delete: { id: publishedId } },
1860
1860
  { createIfNotExists: newDraftFromPublished }
1861
1861
  ];
1862
- if (!checkGrant(grants.update, sourceDoc))
1862
+ if (!checkGrant(grants.update, sourceDoc, identity))
1863
1863
  throw new PermissionActionError({
1864
1864
  documentId,
1865
1865
  transactionId,
1866
1866
  message: `You do not have permission to unpublish the document "${documentId}".`
1867
1867
  });
1868
- if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished))
1868
+ if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished, identity))
1869
1869
  throw new PermissionActionError({
1870
1870
  documentId,
1871
1871
  transactionId,
@@ -1891,7 +1891,8 @@ function processActions({
1891
1891
  working: initialWorking,
1892
1892
  base: initialBase,
1893
1893
  timestamp,
1894
- grants
1894
+ grants,
1895
+ identity
1895
1896
  }) {
1896
1897
  let base = { ...initialBase }, working = { ...initialWorking };
1897
1898
  const outgoingActions = [], outgoingMutations = [], liveEditAction = actions.find((action) => !isReleaseAction(action) && action.liveEdit), otherAction = actions.find((action) => isReleaseAction(action) || !action.liveEdit);
@@ -1908,6 +1909,7 @@ function processActions({
1908
1909
  transactionId,
1909
1910
  timestamp,
1910
1911
  grants,
1912
+ identity,
1911
1913
  outgoingActions,
1912
1914
  outgoingMutations
1913
1915
  });
@@ -1992,7 +1994,8 @@ function applyFirstQueuedTransaction(prev) {
1992
1994
  working,
1993
1995
  base: working,
1994
1996
  timestamp,
1995
- grants: prev.grants
1997
+ grants: prev.grants,
1998
+ identity: prev.identity
1996
1999
  }), applied = {
1997
2000
  ...queued,
1998
2001
  ...result,
@@ -2114,7 +2117,7 @@ function revertOutgoingTransaction(prev) {
2114
2117
  const nextApplied = [];
2115
2118
  for (const t of prev.applied)
2116
2119
  try {
2117
- const next = processActions({ ...t, working, grants: prev.grants });
2120
+ const next = processActions({ ...t, working, grants: prev.grants, identity: prev.identity });
2118
2121
  working = next.working, nextApplied.push({ ...t, ...next });
2119
2122
  } catch (error) {
2120
2123
  if (error instanceof ActionError) continue;
@@ -2161,7 +2164,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
2161
2164
  const nextApplied = [];
2162
2165
  for (const curr of prev.applied)
2163
2166
  try {
2164
- const next = processActions({ ...curr, working, grants: prev.grants });
2167
+ const next = processActions({ ...curr, working, grants: prev.grants, identity: prev.identity });
2165
2168
  working = next.working, nextApplied.push({ ...curr, ...next });
2166
2169
  } catch (error) {
2167
2170
  if (error instanceof ActionError) {
@@ -2349,7 +2352,8 @@ const documentStore = defineStore({
2349
2352
  subscribeToQueuedAndApplyNextTransaction(context),
2350
2353
  subscribeToSubscriptionsAndListenToDocuments(context),
2351
2354
  subscribeToAppliedAndSubmitNextTransaction(context),
2352
- subscribeToClientAndFetchDatasetAcl(context)
2355
+ subscribeToClientAndFetchDatasetAcl(context),
2356
+ subscribeToCurrentUserAndSetIdentity(context)
2353
2357
  ];
2354
2358
  return () => {
2355
2359
  sharedListener.dispose(), subscriptions.forEach((subscription) => subscription.unsubscribe());
@@ -2542,8 +2546,15 @@ const _resolveDocument = bindActionByResource(
2542
2546
  mergeMap$1(
2543
2547
  (group) => group.pipe(
2544
2548
  switchMap((e) => e.add ? listen(context, e.id).pipe(
2545
- catchError$1((error) => {
2546
- 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
+ }
2547
2558
  }),
2548
2559
  tap$1(
2549
2560
  (remote) => state.set(
@@ -2582,15 +2593,58 @@ const _resolveDocument = bindActionByResource(
2582
2593
  ).subscribe({
2583
2594
  error: (error) => state.set("setError", { error })
2584
2595
  });
2585
- };
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
+ }
2586
2635
  function applyDocumentActions(...args) {
2587
2636
  return boundApplyDocumentActions(...args);
2588
2637
  }
2589
2638
  const boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocumentActions);
2590
- async function _applyDocumentActions({ state }, { actions, transactionId = crypto.randomUUID(), disableBatching }) {
2591
- 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 = {
2592
2646
  transactionId,
2593
- actions,
2647
+ actions: normalizedActions,
2594
2648
  ...disableBatching && { disableBatching }
2595
2649
  }, fatalError$ = state.observable.pipe(
2596
2650
  map$1((s) => s.error),