@sanity/sdk 2.12.0 → 2.14.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.
- package/dist/_chunks-dts/createGroqSearchFilter.d.ts +925 -0
- package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +261 -225
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
- package/dist/_chunks-es/version.js +1 -1
- package/dist/_exports/_internal.d.ts +3 -2
- package/dist/_exports/_internal.d.ts.map +1 -0
- package/dist/index.d.ts +1856 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +207 -133
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/auth/authLogger.ts +30 -0
- package/src/auth/authStore.test.ts +96 -1
- package/src/auth/authStore.ts +55 -24
- package/src/auth/handleAuthCallback.test.ts +23 -1
- package/src/auth/handleAuthCallback.ts +25 -6
- package/src/auth/logout.test.ts +68 -1
- package/src/auth/logout.ts +22 -3
- package/src/auth/refreshStampedToken.test.ts +15 -0
- package/src/auth/refreshStampedToken.ts +12 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +17 -2
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +9 -0
- package/src/document/applyDocumentActions.test.ts +24 -0
- package/src/document/applyDocumentActions.ts +13 -2
- package/src/document/documentConstants.ts +7 -0
- package/src/document/documentStore.test.ts +69 -0
- package/src/document/documentStore.ts +36 -5
- package/src/document/listen.ts +1 -1
- package/src/document/permissions.test.ts +79 -0
- package/src/document/permissions.ts +8 -7
- package/src/document/processActions/create.ts +7 -4
- package/src/document/processActions/delete.ts +4 -4
- package/src/document/processActions/discard.ts +2 -2
- package/src/document/processActions/edit.ts +4 -3
- package/src/document/processActions/processActions.ts +9 -0
- package/src/document/processActions/publish.ts +4 -4
- package/src/document/processActions/releaseArchive.ts +4 -4
- package/src/document/processActions/releaseCreate.ts +2 -2
- package/src/document/processActions/releaseDelete.ts +2 -2
- package/src/document/processActions/releaseEdit.ts +2 -1
- package/src/document/processActions/releasePublish.ts +2 -2
- package/src/document/processActions/releaseSchedule.ts +4 -4
- package/src/document/processActions/shared.ts +15 -3
- package/src/document/processActions/unpublish.ts +3 -3
- package/src/document/reducers.ts +4 -3
- package/src/document/resourceRules.test.ts +178 -0
- package/src/document/resourceRules.ts +117 -0
- package/dist/_chunks-dts/utils.d.ts +0 -2774
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,
|
|
3
|
-
import { createGroqSearchFilter, getActiveReleasesState, getAllReleasesState, getAuthState, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType,
|
|
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, getAuthLogger, 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, USERS_STATE_CLEAR_DELAY, removeSubscription, parseUsersKey, getClient, PROJECT_API_VERSION, setUsersError, setUsersData, API_VERSION as API_VERSION$8, getDashboardOrganizationId as getDashboardOrganizationId$1, 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";
|
|
@@ -328,30 +328,35 @@ function observeOrganizationVerificationState(instance, projectIds) {
|
|
|
328
328
|
}
|
|
329
329
|
const handleAuthCallback = bindActionGlobally(
|
|
330
330
|
authStore,
|
|
331
|
-
async ({ state }, locationHref = getDefaultLocation()) => {
|
|
332
|
-
const { providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey } = state.get().options;
|
|
333
|
-
if (providedToken)
|
|
331
|
+
async ({ state, instance }, locationHref = getDefaultLocation()) => {
|
|
332
|
+
const logger = getAuthLogger(instance), { providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey } = state.get().options;
|
|
333
|
+
if (providedToken)
|
|
334
|
+
return logger.debug("Skipping auth callback - token already provided"), !1;
|
|
334
335
|
const { authState } = state.get();
|
|
335
|
-
if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken)
|
|
336
|
+
if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken)
|
|
337
|
+
return logger.debug("Skipping auth callback - token exchange already in progress"), !1;
|
|
336
338
|
const cleanedUrl = getCleanedUrl(locationHref), tokenFromUrl = getTokenFromLocation(locationHref);
|
|
337
339
|
if (tokenFromUrl)
|
|
338
|
-
return state.set("setTokenFromUrl", {
|
|
340
|
+
return logger.info("Auth token found in URL, logging in"), state.set("setTokenFromUrl", {
|
|
339
341
|
authState: createLoggedInAuthState(tokenFromUrl, null)
|
|
340
342
|
}), cleanedUrl;
|
|
341
343
|
const authCode = getAuthCode(callbackUrl, locationHref);
|
|
342
|
-
if (!authCode)
|
|
344
|
+
if (!authCode)
|
|
345
|
+
return logger.debug("No auth code found in callback URL"), !1;
|
|
343
346
|
const parsedUrl = new URL(locationHref);
|
|
344
347
|
let dashboardContext = {};
|
|
345
348
|
try {
|
|
346
349
|
const contextParam = parsedUrl.searchParams.get("_context");
|
|
347
350
|
if (contextParam) {
|
|
348
351
|
const parsedContext = JSON.parse(contextParam);
|
|
349
|
-
parsedContext && typeof parsedContext == "object" && (delete parsedContext.sid, dashboardContext = parsedContext
|
|
352
|
+
parsedContext && typeof parsedContext == "object" && (delete parsedContext.sid, dashboardContext = parsedContext, logger.debug("Dashboard context parsed from callback URL", {
|
|
353
|
+
hasDashboardContext: !0
|
|
354
|
+
}));
|
|
350
355
|
}
|
|
351
356
|
} catch (err) {
|
|
352
|
-
|
|
357
|
+
logger.warn("Failed to parse dashboard context from callback URL", { error: err });
|
|
353
358
|
}
|
|
354
|
-
state.set("exchangeSessionForToken", {
|
|
359
|
+
logger.info("Exchanging auth code for token"), state.set("exchangeSessionForToken", {
|
|
355
360
|
authState: { type: AuthStateType.LOGGING_IN, isExchangingToken: !0 },
|
|
356
361
|
dashboardContext
|
|
357
362
|
});
|
|
@@ -362,36 +367,51 @@ const handleAuthCallback = bindActionGlobally(
|
|
|
362
367
|
useProjectHostname: !1,
|
|
363
368
|
useCdn: !1,
|
|
364
369
|
...apiHost && { apiHost }
|
|
365
|
-
})
|
|
370
|
+
});
|
|
371
|
+
logger.debug("Fetching token from auth endpoint");
|
|
372
|
+
const { token } = await client.request({
|
|
366
373
|
method: "GET",
|
|
367
374
|
uri: "/auth/fetch",
|
|
368
375
|
query: { sid: authCode },
|
|
369
376
|
tag: "fetch-token"
|
|
370
377
|
});
|
|
371
|
-
return storageArea?.setItem(storageKey, JSON.stringify({ token })), state.set("setToken", { authState: createLoggedInAuthState(token, null) }), cleanedUrl;
|
|
378
|
+
return logger.info("Auth token obtained successfully, user logged in"), storageArea?.setItem(storageKey, JSON.stringify({ token })), state.set("setToken", { authState: createLoggedInAuthState(token, null) }), cleanedUrl;
|
|
372
379
|
} catch (error) {
|
|
373
|
-
return state.set("exchangeSessionForTokenError", { authState: { type: AuthStateType.ERROR, error } }), cleanedUrl;
|
|
380
|
+
return logger.error("Failed to exchange auth code for token", { error }), state.set("exchangeSessionForTokenError", { authState: { type: AuthStateType.ERROR, error } }), cleanedUrl;
|
|
374
381
|
}
|
|
375
382
|
}
|
|
376
|
-
), logout = bindActionGlobally(authStore, async ({ state }) => {
|
|
377
|
-
const { clientFactory, apiHost, providedToken, storageArea, storageKey } = state.get().options;
|
|
378
|
-
if (providedToken)
|
|
383
|
+
), logout = bindActionGlobally(authStore, async ({ state, instance }) => {
|
|
384
|
+
const logger = getAuthLogger(instance), { clientFactory, apiHost, providedToken, storageArea, storageKey } = state.get().options;
|
|
385
|
+
if (providedToken) {
|
|
386
|
+
logger.debug("Skipping logout - token is statically provided");
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
379
389
|
const { authState } = state.get();
|
|
380
|
-
if (authState.type === AuthStateType.LOGGED_OUT && authState.isDestroyingSession)
|
|
390
|
+
if (authState.type === AuthStateType.LOGGED_OUT && authState.isDestroyingSession) {
|
|
391
|
+
logger.debug("Skipping logout - already in progress");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
381
394
|
const token = authState.type === AuthStateType.LOGGED_IN && authState.token;
|
|
382
395
|
try {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
396
|
+
if (token) {
|
|
397
|
+
logger.info("Logging out user"), state.set("loggingOut", {
|
|
398
|
+
authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !0 }
|
|
399
|
+
});
|
|
400
|
+
const client = clientFactory({
|
|
401
|
+
token,
|
|
402
|
+
requestTagPrefix: REQUEST_TAG_PREFIX,
|
|
403
|
+
apiVersion: DEFAULT_API_VERSION,
|
|
404
|
+
...apiHost && { apiHost },
|
|
405
|
+
useProjectHostname: !1,
|
|
406
|
+
useCdn: !1
|
|
407
|
+
});
|
|
408
|
+
logger.debug("Calling logout endpoint"), await client.request({ uri: "/auth/logout", method: "POST", tag: "logout" });
|
|
409
|
+
} else
|
|
410
|
+
logger.debug("No token to logout - already logged out");
|
|
411
|
+
} catch (error) {
|
|
412
|
+
throw logger.error("Logout request failed", { error }), error;
|
|
393
413
|
} finally {
|
|
394
|
-
state.set("logoutSuccess", {
|
|
414
|
+
logger.info("User logged out, clearing stored tokens"), state.set("logoutSuccess", {
|
|
395
415
|
authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }
|
|
396
416
|
}), storageArea?.removeItem(storageKey), storageArea?.removeItem(`${storageKey}_last_refresh`);
|
|
397
417
|
}
|
|
@@ -519,7 +539,7 @@ const handleAuthCallback = bindActionGlobally(
|
|
|
519
539
|
status: nodeEntry.status
|
|
520
540
|
} : void 0),
|
|
521
541
|
onSubscribe: ({ state, instance }, nodeInput) => {
|
|
522
|
-
const nodeName = nodeInput.name, subscriberId = Symbol("comlink-node-subscriber");
|
|
542
|
+
const nodeName = nodeInput.name, subscriberId = /* @__PURE__ */ Symbol("comlink-node-subscriber");
|
|
523
543
|
getOrCreateNode(instance, nodeInput);
|
|
524
544
|
let subs = state.get().subscriptions.get(nodeName);
|
|
525
545
|
return subs || (subs = /* @__PURE__ */ new Set(), state.get().subscriptions.set(nodeName, subs)), subs.add(subscriberId), () => {
|
|
@@ -666,7 +686,7 @@ function unarchiveRelease(handle) {
|
|
|
666
686
|
function deleteRelease(handle) {
|
|
667
687
|
return { type: "release.delete", ...handle };
|
|
668
688
|
}
|
|
669
|
-
const DOCUMENT_STATE_CLEAR_DELAY = 1e3, INITIAL_OUTGOING_THROTTLE_TIME = 1e3, API_VERSION$4 = "v2025-05-06", RELEASE_DOCUMENTS_PATH = "_.releases";
|
|
689
|
+
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
690
|
function getReleaseDocumentId(releaseId) {
|
|
671
691
|
return `${RELEASE_DOCUMENTS_PATH}.${releaseId}`;
|
|
672
692
|
}
|
|
@@ -1186,6 +1206,52 @@ class MultiKeyWeakMap {
|
|
|
1186
1206
|
this.#setDeep(arrangedKeys, this.#rootMap, value);
|
|
1187
1207
|
}
|
|
1188
1208
|
}
|
|
1209
|
+
function checkGrant(grantExpr, document, identity) {
|
|
1210
|
+
const value = evaluateSync(grantExpr, { params: { document }, identity });
|
|
1211
|
+
return value.type === "boolean" && value.data;
|
|
1212
|
+
}
|
|
1213
|
+
class ActionError extends Error {
|
|
1214
|
+
documentId;
|
|
1215
|
+
transactionId;
|
|
1216
|
+
constructor(options) {
|
|
1217
|
+
super(options.message), Object.assign(this, options);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
class PermissionActionError extends ActionError {
|
|
1221
|
+
}
|
|
1222
|
+
function applySingleDocPatch({
|
|
1223
|
+
base: initialBase,
|
|
1224
|
+
working: initialWorking,
|
|
1225
|
+
documentId,
|
|
1226
|
+
patches,
|
|
1227
|
+
transactionId,
|
|
1228
|
+
timestamp,
|
|
1229
|
+
grants,
|
|
1230
|
+
identity,
|
|
1231
|
+
notFoundMessage = "Cannot edit document because it does not exist.",
|
|
1232
|
+
permissionMessage = `You do not have permission to edit document "${documentId}".`
|
|
1233
|
+
}) {
|
|
1234
|
+
let base = initialBase, working = initialWorking;
|
|
1235
|
+
const userPatches = patches?.map((patch) => ({ patch: { id: documentId, ...patch } }));
|
|
1236
|
+
if (!userPatches?.length)
|
|
1237
|
+
return { base, working, diffedPatches: [], workingMutations: [] };
|
|
1238
|
+
if (!working[documentId] || !base[documentId])
|
|
1239
|
+
throw new ActionError({ documentId, transactionId, message: notFoundMessage });
|
|
1240
|
+
const baseBefore = base[documentId];
|
|
1241
|
+
base = processMutations({ documents: base, transactionId, mutations: userPatches, timestamp });
|
|
1242
|
+
const baseAfter = base[documentId], diffedPatches = diffValue(baseBefore, baseAfter), workingBefore = working[documentId];
|
|
1243
|
+
if (!checkGrant(grants.update, workingBefore, identity))
|
|
1244
|
+
throw new PermissionActionError({ documentId, transactionId, message: permissionMessage });
|
|
1245
|
+
const workingMutations = diffedPatches.map((patch) => ({
|
|
1246
|
+
patch: { id: documentId, ...patch }
|
|
1247
|
+
}));
|
|
1248
|
+
return working = processMutations({
|
|
1249
|
+
documents: working,
|
|
1250
|
+
transactionId,
|
|
1251
|
+
mutations: workingMutations,
|
|
1252
|
+
timestamp
|
|
1253
|
+
}), { base, working, diffedPatches, workingMutations };
|
|
1254
|
+
}
|
|
1189
1255
|
function createGrantsLookup(datasetAcl) {
|
|
1190
1256
|
const filtersByGrant = {
|
|
1191
1257
|
create: /* @__PURE__ */ new Set(),
|
|
@@ -1196,7 +1262,7 @@ function createGrantsLookup(datasetAcl) {
|
|
|
1196
1262
|
for (const entry of datasetAcl)
|
|
1197
1263
|
for (const grant of entry.permissions) {
|
|
1198
1264
|
const set2 = filtersByGrant[grant];
|
|
1199
|
-
set2.add(entry.filter), filtersByGrant[grant] = set2;
|
|
1265
|
+
set2 && (set2.add(entry.filter), filtersByGrant[grant] = set2);
|
|
1200
1266
|
}
|
|
1201
1267
|
return Object.fromEntries(
|
|
1202
1268
|
Object.entries(filtersByGrant).map(([grant, filters]) => {
|
|
@@ -1246,22 +1312,18 @@ const documentsCache = new MultiKeyWeakMap(), actionsCache = /* @__PURE__ */ new
|
|
|
1246
1312
|
const normalizedActions = Array.isArray(actions) ? actions : [actions], actionsKey = JSON.stringify(normalizedActions);
|
|
1247
1313
|
return nestedCache.get(actionsKey) || (nestedCache.set(actionsKey, normalizedActions), normalizedActions);
|
|
1248
1314
|
}
|
|
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" });
|
|
1315
|
+
), enNarrowConjunction = new Intl.ListFormat("en", { style: "narrow", type: "conjunction" });
|
|
1255
1316
|
function calculatePermissions(...args) {
|
|
1256
1317
|
return _calculatePermissions(...args);
|
|
1257
1318
|
}
|
|
1258
1319
|
const _calculatePermissions = createSelector(
|
|
1259
1320
|
[
|
|
1260
1321
|
({ state: { grants } }) => grants,
|
|
1322
|
+
({ state: { identity } }) => identity,
|
|
1261
1323
|
documentsSelector,
|
|
1262
1324
|
memoizedActionsSelector
|
|
1263
1325
|
],
|
|
1264
|
-
(grants, documents, actions) => {
|
|
1326
|
+
(grants, identity, documents, actions) => {
|
|
1265
1327
|
if (!documents || !grants || !actions) return;
|
|
1266
1328
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString(), reasons = [];
|
|
1267
1329
|
try {
|
|
@@ -1271,7 +1333,8 @@ const _calculatePermissions = createSelector(
|
|
|
1271
1333
|
working: documents,
|
|
1272
1334
|
base: documents,
|
|
1273
1335
|
timestamp,
|
|
1274
|
-
grants
|
|
1336
|
+
grants,
|
|
1337
|
+
identity
|
|
1275
1338
|
});
|
|
1276
1339
|
} catch (error) {
|
|
1277
1340
|
if (error instanceof PermissionActionError)
|
|
@@ -1293,7 +1356,7 @@ const _calculatePermissions = createSelector(
|
|
|
1293
1356
|
if (action.type === "document.edit" && !action.patches?.length) {
|
|
1294
1357
|
const docId = action.documentId;
|
|
1295
1358
|
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
|
|
1359
|
+
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
1360
|
type: "access",
|
|
1298
1361
|
message: `You are not allowed to edit the document with ID "${docId}".`,
|
|
1299
1362
|
documentId: docId
|
|
@@ -1317,53 +1380,8 @@ const _calculatePermissions = createSelector(
|
|
|
1317
1380
|
};
|
|
1318
1381
|
}
|
|
1319
1382
|
);
|
|
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
1383
|
function handleCreate(action, ctx) {
|
|
1366
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1384
|
+
const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
|
|
1367
1385
|
let { base, working } = ctx;
|
|
1368
1386
|
const documentId = getId(action.documentId);
|
|
1369
1387
|
if (action.liveEdit) {
|
|
@@ -1388,7 +1406,7 @@ function handleCreate(action, ctx) {
|
|
|
1388
1406
|
transactionId,
|
|
1389
1407
|
mutations: mutations2,
|
|
1390
1408
|
timestamp
|
|
1391
|
-
}), !checkGrant(grants.create, working[documentId]))
|
|
1409
|
+
}), !checkGrant(grants.create, working[documentId], identity))
|
|
1392
1410
|
throw new PermissionActionError({
|
|
1393
1411
|
documentId,
|
|
1394
1412
|
transactionId,
|
|
@@ -1426,13 +1444,13 @@ function handleCreate(action, ctx) {
|
|
|
1426
1444
|
transactionId,
|
|
1427
1445
|
mutations,
|
|
1428
1446
|
timestamp
|
|
1429
|
-
}), versionId && !checkGrant(grants.create, working[versionId]))
|
|
1447
|
+
}), versionId && !checkGrant(grants.create, working[versionId], identity))
|
|
1430
1448
|
throw new PermissionActionError({
|
|
1431
1449
|
documentId,
|
|
1432
1450
|
transactionId,
|
|
1433
1451
|
message: `You do not have permission to create a release version for document "${documentId}".`
|
|
1434
1452
|
});
|
|
1435
|
-
if (!versionId && !checkGrant(grants.create, working[draftId]))
|
|
1453
|
+
if (!versionId && !checkGrant(grants.create, working[draftId], identity))
|
|
1436
1454
|
throw new PermissionActionError({
|
|
1437
1455
|
documentId,
|
|
1438
1456
|
transactionId,
|
|
@@ -1445,7 +1463,7 @@ function handleCreate(action, ctx) {
|
|
|
1445
1463
|
}), { base, working };
|
|
1446
1464
|
}
|
|
1447
1465
|
function handleDelete(action, ctx) {
|
|
1448
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1466
|
+
const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
|
|
1449
1467
|
let { base, working } = ctx;
|
|
1450
1468
|
const documentId = action.documentId;
|
|
1451
1469
|
if (isReleasePerspective(action.perspective))
|
|
@@ -1461,7 +1479,7 @@ function handleDelete(action, ctx) {
|
|
|
1461
1479
|
transactionId,
|
|
1462
1480
|
message: "The document you are trying to delete does not exist."
|
|
1463
1481
|
});
|
|
1464
|
-
if (!checkGrant(grants.update, working[documentId]))
|
|
1482
|
+
if (!checkGrant(grants.update, working[documentId], identity))
|
|
1465
1483
|
throw new PermissionActionError({
|
|
1466
1484
|
documentId,
|
|
1467
1485
|
transactionId,
|
|
@@ -1477,7 +1495,7 @@ function handleDelete(action, ctx) {
|
|
|
1477
1495
|
transactionId,
|
|
1478
1496
|
message: working[draftId] ? "Cannot delete a document without a published version." : "The document you are trying to delete does not exist."
|
|
1479
1497
|
});
|
|
1480
|
-
const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId]), cantDeletePublished = working[publishedId] && !checkGrant(grants.update, working[publishedId]);
|
|
1498
|
+
const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId], identity), cantDeletePublished = working[publishedId] && !checkGrant(grants.update, working[publishedId], identity);
|
|
1481
1499
|
if (cantDeleteDraft || cantDeletePublished)
|
|
1482
1500
|
throw new PermissionActionError({
|
|
1483
1501
|
documentId,
|
|
@@ -1492,7 +1510,7 @@ function handleDelete(action, ctx) {
|
|
|
1492
1510
|
}), { base, working };
|
|
1493
1511
|
}
|
|
1494
1512
|
function handleDiscard(action, ctx) {
|
|
1495
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1513
|
+
const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
|
|
1496
1514
|
let { base, working } = ctx;
|
|
1497
1515
|
const documentId = getId(action.documentId);
|
|
1498
1516
|
if (action.liveEdit)
|
|
@@ -1508,7 +1526,7 @@ function handleDiscard(action, ctx) {
|
|
|
1508
1526
|
transactionId,
|
|
1509
1527
|
message: `There is no draft or version available to discard for document "${documentId}".`
|
|
1510
1528
|
});
|
|
1511
|
-
if (!checkGrant(grants.update, working[versionId]))
|
|
1529
|
+
if (!checkGrant(grants.update, working[versionId], identity))
|
|
1512
1530
|
throw new PermissionActionError({
|
|
1513
1531
|
documentId,
|
|
1514
1532
|
transactionId,
|
|
@@ -1520,7 +1538,7 @@ function handleDiscard(action, ctx) {
|
|
|
1520
1538
|
}), { base, working };
|
|
1521
1539
|
}
|
|
1522
1540
|
function handleEdit(action, ctx) {
|
|
1523
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1541
|
+
const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
|
|
1524
1542
|
let { base, working } = ctx;
|
|
1525
1543
|
const documentId = getId(action.documentId);
|
|
1526
1544
|
if (action.liveEdit) {
|
|
@@ -1531,7 +1549,8 @@ function handleEdit(action, ctx) {
|
|
|
1531
1549
|
patches: action.patches,
|
|
1532
1550
|
transactionId,
|
|
1533
1551
|
timestamp,
|
|
1534
|
-
grants
|
|
1552
|
+
grants,
|
|
1553
|
+
identity
|
|
1535
1554
|
});
|
|
1536
1555
|
return outgoingMutations.push(...result.workingMutations), { base: result.base, working: result.working };
|
|
1537
1556
|
}
|
|
@@ -1564,7 +1583,7 @@ function handleEdit(action, ctx) {
|
|
|
1564
1583
|
const baseAfter = base[patchDocumentId], patches = diffValue(baseBefore, baseAfter), workingMutations = [];
|
|
1565
1584
|
if (!isReleasePerspective(action.perspective) && !working[draftId] && working[publishedId]) {
|
|
1566
1585
|
const newDraftFromPublished = { ...working[publishedId], _id: draftId };
|
|
1567
|
-
if (!checkGrant(grants.create, newDraftFromPublished))
|
|
1586
|
+
if (!checkGrant(grants.create, newDraftFromPublished, identity))
|
|
1568
1587
|
throw new PermissionActionError({
|
|
1569
1588
|
documentId,
|
|
1570
1589
|
transactionId,
|
|
@@ -1573,7 +1592,7 @@ function handleEdit(action, ctx) {
|
|
|
1573
1592
|
workingMutations.push({ create: newDraftFromPublished });
|
|
1574
1593
|
}
|
|
1575
1594
|
const workingBefore = working[patchDocumentId] ?? working[publishedId];
|
|
1576
|
-
if (!checkGrant(grants.update, workingBefore))
|
|
1595
|
+
if (!checkGrant(grants.update, workingBefore, identity))
|
|
1577
1596
|
throw new PermissionActionError({
|
|
1578
1597
|
documentId,
|
|
1579
1598
|
transactionId,
|
|
@@ -1594,7 +1613,7 @@ function handleEdit(action, ctx) {
|
|
|
1594
1613
|
), { base, working };
|
|
1595
1614
|
}
|
|
1596
1615
|
function handlePublish(action, ctx) {
|
|
1597
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1616
|
+
const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
|
|
1598
1617
|
let { base, working } = ctx;
|
|
1599
1618
|
const documentId = getId(action.documentId);
|
|
1600
1619
|
if (action.liveEdit || isReleasePerspective(action.perspective))
|
|
@@ -1617,19 +1636,19 @@ function handlePublish(action, ctx) {
|
|
|
1617
1636
|
message: "Publish aborted: The document has changed elsewhere. Please try again."
|
|
1618
1637
|
});
|
|
1619
1638
|
const newPublishedFromDraft = { ...strengthenOnPublish(workingDraft), _id: publishedId }, mutations = [{ delete: { id: draftId } }, { createOrReplace: newPublishedFromDraft }];
|
|
1620
|
-
if (working[draftId] && !checkGrant(grants.update, working[draftId]))
|
|
1639
|
+
if (working[draftId] && !checkGrant(grants.update, working[draftId], identity))
|
|
1621
1640
|
throw new PermissionActionError({
|
|
1622
1641
|
documentId,
|
|
1623
1642
|
transactionId,
|
|
1624
1643
|
message: `Publish failed: You do not have permission to update the draft for "${documentId}".`
|
|
1625
1644
|
});
|
|
1626
|
-
if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft))
|
|
1645
|
+
if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft, identity))
|
|
1627
1646
|
throw new PermissionActionError({
|
|
1628
1647
|
documentId,
|
|
1629
1648
|
transactionId,
|
|
1630
1649
|
message: `Publish failed: You do not have permission to update the published version of "${documentId}".`
|
|
1631
1650
|
});
|
|
1632
|
-
if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft))
|
|
1651
|
+
if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft, identity))
|
|
1633
1652
|
throw new PermissionActionError({
|
|
1634
1653
|
documentId,
|
|
1635
1654
|
transactionId,
|
|
@@ -1657,14 +1676,14 @@ function strengthenOnPublish(draft) {
|
|
|
1657
1676
|
return strengthen(draft);
|
|
1658
1677
|
}
|
|
1659
1678
|
function handleReleaseArchive(action, ctx) {
|
|
1660
|
-
const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1679
|
+
const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1661
1680
|
if (!existing)
|
|
1662
1681
|
throw new ActionError({
|
|
1663
1682
|
documentId: releaseDocumentId,
|
|
1664
1683
|
transactionId,
|
|
1665
1684
|
message: `Cannot archive release "${action.releaseId}" because it does not exist.`
|
|
1666
1685
|
});
|
|
1667
|
-
if (!checkGrant(grants.update, existing))
|
|
1686
|
+
if (!checkGrant(grants.update, existing, identity))
|
|
1668
1687
|
throw new PermissionActionError({
|
|
1669
1688
|
documentId: releaseDocumentId,
|
|
1670
1689
|
transactionId,
|
|
@@ -1676,14 +1695,14 @@ function handleReleaseArchive(action, ctx) {
|
|
|
1676
1695
|
}), { base, working };
|
|
1677
1696
|
}
|
|
1678
1697
|
function handleReleaseUnarchive(action, ctx) {
|
|
1679
|
-
const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1698
|
+
const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1680
1699
|
if (!existing)
|
|
1681
1700
|
throw new ActionError({
|
|
1682
1701
|
documentId: releaseDocumentId,
|
|
1683
1702
|
transactionId,
|
|
1684
1703
|
message: `Cannot unarchive release "${action.releaseId}" because it does not exist.`
|
|
1685
1704
|
});
|
|
1686
|
-
if (!checkGrant(grants.update, existing))
|
|
1705
|
+
if (!checkGrant(grants.update, existing, identity))
|
|
1687
1706
|
throw new PermissionActionError({
|
|
1688
1707
|
documentId: releaseDocumentId,
|
|
1689
1708
|
transactionId,
|
|
@@ -1695,7 +1714,7 @@ function handleReleaseUnarchive(action, ctx) {
|
|
|
1695
1714
|
}), { base, working };
|
|
1696
1715
|
}
|
|
1697
1716
|
function handleReleaseCreate(action, ctx) {
|
|
1698
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1717
|
+
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx;
|
|
1699
1718
|
let { base, working } = ctx;
|
|
1700
1719
|
const releaseDocumentId = getReleaseDocumentId(action.releaseId);
|
|
1701
1720
|
if (working[releaseDocumentId] || base[releaseDocumentId])
|
|
@@ -1711,7 +1730,7 @@ function handleReleaseCreate(action, ctx) {
|
|
|
1711
1730
|
state: "active",
|
|
1712
1731
|
metadata: action.metadata
|
|
1713
1732
|
} }];
|
|
1714
|
-
if (base = processMutations({ documents: base, transactionId, mutations, timestamp }), working = processMutations({ documents: working, transactionId, mutations, timestamp }), !checkGrant(grants.create, working[releaseDocumentId]))
|
|
1733
|
+
if (base = processMutations({ documents: base, transactionId, mutations, timestamp }), working = processMutations({ documents: working, transactionId, mutations, timestamp }), !checkGrant(grants.create, working[releaseDocumentId], identity))
|
|
1715
1734
|
throw new PermissionActionError({
|
|
1716
1735
|
documentId: releaseDocumentId,
|
|
1717
1736
|
transactionId,
|
|
@@ -1725,7 +1744,7 @@ function handleReleaseCreate(action, ctx) {
|
|
|
1725
1744
|
}
|
|
1726
1745
|
const DELETABLE_STATES = /* @__PURE__ */ new Set(["archived", "published"]);
|
|
1727
1746
|
function handleReleaseDelete(action, ctx) {
|
|
1728
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1747
|
+
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx;
|
|
1729
1748
|
let { base, working } = ctx;
|
|
1730
1749
|
const releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1731
1750
|
if (!existing)
|
|
@@ -1741,7 +1760,7 @@ function handleReleaseDelete(action, ctx) {
|
|
|
1741
1760
|
transactionId,
|
|
1742
1761
|
message: `Cannot delete release "${action.releaseId}" while it is "${state}". Archive it first.`
|
|
1743
1762
|
});
|
|
1744
|
-
if (!checkGrant(grants.update, existing))
|
|
1763
|
+
if (!checkGrant(grants.update, existing, identity))
|
|
1745
1764
|
throw new PermissionActionError({
|
|
1746
1765
|
documentId: releaseDocumentId,
|
|
1747
1766
|
transactionId,
|
|
@@ -1754,7 +1773,7 @@ function handleReleaseDelete(action, ctx) {
|
|
|
1754
1773
|
}), { base, working };
|
|
1755
1774
|
}
|
|
1756
1775
|
function handleReleaseEdit(action, ctx) {
|
|
1757
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx, { base, working } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), result = applySingleDocPatch({
|
|
1776
|
+
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx, { base, working } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), result = applySingleDocPatch({
|
|
1758
1777
|
base,
|
|
1759
1778
|
working,
|
|
1760
1779
|
documentId: releaseDocumentId,
|
|
@@ -1762,6 +1781,7 @@ function handleReleaseEdit(action, ctx) {
|
|
|
1762
1781
|
transactionId,
|
|
1763
1782
|
timestamp,
|
|
1764
1783
|
grants,
|
|
1784
|
+
identity,
|
|
1765
1785
|
notFoundMessage: `Cannot edit release "${action.releaseId}" because it does not exist.`,
|
|
1766
1786
|
permissionMessage: `You do not have permission to edit release "${action.releaseId}".`
|
|
1767
1787
|
});
|
|
@@ -1774,14 +1794,14 @@ function handleReleaseEdit(action, ctx) {
|
|
|
1774
1794
|
), { base: result.base, working: result.working };
|
|
1775
1795
|
}
|
|
1776
1796
|
function handleReleasePublish(action, ctx) {
|
|
1777
|
-
const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1797
|
+
const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1778
1798
|
if (!existing)
|
|
1779
1799
|
throw new ActionError({
|
|
1780
1800
|
documentId: releaseDocumentId,
|
|
1781
1801
|
transactionId,
|
|
1782
1802
|
message: `Cannot publish release "${action.releaseId}" because it does not exist.`
|
|
1783
1803
|
});
|
|
1784
|
-
if (!checkGrant(grants.update, existing))
|
|
1804
|
+
if (!checkGrant(grants.update, existing, identity))
|
|
1785
1805
|
throw new PermissionActionError({
|
|
1786
1806
|
documentId: releaseDocumentId,
|
|
1787
1807
|
transactionId,
|
|
@@ -1793,7 +1813,7 @@ function handleReleasePublish(action, ctx) {
|
|
|
1793
1813
|
}), { base, working };
|
|
1794
1814
|
}
|
|
1795
1815
|
function handleReleaseSchedule(action, ctx) {
|
|
1796
|
-
const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId);
|
|
1816
|
+
const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId);
|
|
1797
1817
|
if (Number.isNaN(Date.parse(action.publishAt)))
|
|
1798
1818
|
throw new ActionError({
|
|
1799
1819
|
documentId: releaseDocumentId,
|
|
@@ -1807,7 +1827,7 @@ function handleReleaseSchedule(action, ctx) {
|
|
|
1807
1827
|
transactionId,
|
|
1808
1828
|
message: `Cannot schedule release "${action.releaseId}" because it does not exist.`
|
|
1809
1829
|
});
|
|
1810
|
-
if (!checkGrant(grants.update, existing))
|
|
1830
|
+
if (!checkGrant(grants.update, existing, identity))
|
|
1811
1831
|
throw new PermissionActionError({
|
|
1812
1832
|
documentId: releaseDocumentId,
|
|
1813
1833
|
transactionId,
|
|
@@ -1820,14 +1840,14 @@ function handleReleaseSchedule(action, ctx) {
|
|
|
1820
1840
|
}), { base, working };
|
|
1821
1841
|
}
|
|
1822
1842
|
function handleReleaseUnschedule(action, ctx) {
|
|
1823
|
-
const { base, working, grants, outgoingActions, transactionId } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1843
|
+
const { base, working, grants, outgoingActions, transactionId, identity } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), existing = working[releaseDocumentId] ?? base[releaseDocumentId];
|
|
1824
1844
|
if (!existing)
|
|
1825
1845
|
throw new ActionError({
|
|
1826
1846
|
documentId: releaseDocumentId,
|
|
1827
1847
|
transactionId,
|
|
1828
1848
|
message: `Cannot unschedule release "${action.releaseId}" because it does not exist.`
|
|
1829
1849
|
});
|
|
1830
|
-
if (!checkGrant(grants.update, existing))
|
|
1850
|
+
if (!checkGrant(grants.update, existing, identity))
|
|
1831
1851
|
throw new PermissionActionError({
|
|
1832
1852
|
documentId: releaseDocumentId,
|
|
1833
1853
|
transactionId,
|
|
@@ -1839,7 +1859,7 @@ function handleReleaseUnschedule(action, ctx) {
|
|
|
1839
1859
|
}), { base, working };
|
|
1840
1860
|
}
|
|
1841
1861
|
function handleUnpublish(action, ctx) {
|
|
1842
|
-
const { transactionId, timestamp, grants, outgoingActions, outgoingMutations } = ctx;
|
|
1862
|
+
const { transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations } = ctx;
|
|
1843
1863
|
let { base, working } = ctx;
|
|
1844
1864
|
const documentId = getId(action.documentId);
|
|
1845
1865
|
if (action.liveEdit || isReleasePerspective(action.perspective))
|
|
@@ -1859,13 +1879,13 @@ function handleUnpublish(action, ctx) {
|
|
|
1859
1879
|
{ delete: { id: publishedId } },
|
|
1860
1880
|
{ createIfNotExists: newDraftFromPublished }
|
|
1861
1881
|
];
|
|
1862
|
-
if (!checkGrant(grants.update, sourceDoc))
|
|
1882
|
+
if (!checkGrant(grants.update, sourceDoc, identity))
|
|
1863
1883
|
throw new PermissionActionError({
|
|
1864
1884
|
documentId,
|
|
1865
1885
|
transactionId,
|
|
1866
1886
|
message: `You do not have permission to unpublish the document "${documentId}".`
|
|
1867
1887
|
});
|
|
1868
|
-
if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished))
|
|
1888
|
+
if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished, identity))
|
|
1869
1889
|
throw new PermissionActionError({
|
|
1870
1890
|
documentId,
|
|
1871
1891
|
transactionId,
|
|
@@ -1891,7 +1911,8 @@ function processActions({
|
|
|
1891
1911
|
working: initialWorking,
|
|
1892
1912
|
base: initialBase,
|
|
1893
1913
|
timestamp,
|
|
1894
|
-
grants
|
|
1914
|
+
grants,
|
|
1915
|
+
identity
|
|
1895
1916
|
}) {
|
|
1896
1917
|
let base = { ...initialBase }, working = { ...initialWorking };
|
|
1897
1918
|
const outgoingActions = [], outgoingMutations = [], liveEditAction = actions.find((action) => !isReleaseAction(action) && action.liveEdit), otherAction = actions.find((action) => isReleaseAction(action) || !action.liveEdit);
|
|
@@ -1908,6 +1929,7 @@ function processActions({
|
|
|
1908
1929
|
transactionId,
|
|
1909
1930
|
timestamp,
|
|
1910
1931
|
grants,
|
|
1932
|
+
identity,
|
|
1911
1933
|
outgoingActions,
|
|
1912
1934
|
outgoingMutations
|
|
1913
1935
|
});
|
|
@@ -1992,7 +2014,8 @@ function applyFirstQueuedTransaction(prev) {
|
|
|
1992
2014
|
working,
|
|
1993
2015
|
base: working,
|
|
1994
2016
|
timestamp,
|
|
1995
|
-
grants: prev.grants
|
|
2017
|
+
grants: prev.grants,
|
|
2018
|
+
identity: prev.identity
|
|
1996
2019
|
}), applied = {
|
|
1997
2020
|
...queued,
|
|
1998
2021
|
...result,
|
|
@@ -2114,7 +2137,7 @@ function revertOutgoingTransaction(prev) {
|
|
|
2114
2137
|
const nextApplied = [];
|
|
2115
2138
|
for (const t of prev.applied)
|
|
2116
2139
|
try {
|
|
2117
|
-
const next = processActions({ ...t, working, grants: prev.grants });
|
|
2140
|
+
const next = processActions({ ...t, working, grants: prev.grants, identity: prev.identity });
|
|
2118
2141
|
working = next.working, nextApplied.push({ ...t, ...next });
|
|
2119
2142
|
} catch (error) {
|
|
2120
2143
|
if (error instanceof ActionError) continue;
|
|
@@ -2161,7 +2184,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
|
|
|
2161
2184
|
const nextApplied = [];
|
|
2162
2185
|
for (const curr of prev.applied)
|
|
2163
2186
|
try {
|
|
2164
|
-
const next = processActions({ ...curr, working, grants: prev.grants });
|
|
2187
|
+
const next = processActions({ ...curr, working, grants: prev.grants, identity: prev.identity });
|
|
2165
2188
|
working = next.working, nextApplied.push({ ...curr, ...next });
|
|
2166
2189
|
} catch (error) {
|
|
2167
2190
|
if (error instanceof ActionError) {
|
|
@@ -2349,7 +2372,8 @@ const documentStore = defineStore({
|
|
|
2349
2372
|
subscribeToQueuedAndApplyNextTransaction(context),
|
|
2350
2373
|
subscribeToSubscriptionsAndListenToDocuments(context),
|
|
2351
2374
|
subscribeToAppliedAndSubmitNextTransaction(context),
|
|
2352
|
-
subscribeToClientAndFetchDatasetAcl(context)
|
|
2375
|
+
subscribeToClientAndFetchDatasetAcl(context),
|
|
2376
|
+
subscribeToCurrentUserAndSetIdentity(context)
|
|
2353
2377
|
];
|
|
2354
2378
|
return () => {
|
|
2355
2379
|
sharedListener.dispose(), subscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
@@ -2542,8 +2566,15 @@ const _resolveDocument = bindActionByResource(
|
|
|
2542
2566
|
mergeMap$1(
|
|
2543
2567
|
(group) => group.pipe(
|
|
2544
2568
|
switchMap((e) => e.add ? listen(context, e.id).pipe(
|
|
2545
|
-
|
|
2546
|
-
|
|
2569
|
+
retry({
|
|
2570
|
+
delay: (error, retryCount) => {
|
|
2571
|
+
if (!(error instanceof OutOfSyncError)) return throwError(() => error);
|
|
2572
|
+
const backoff = Math.min(
|
|
2573
|
+
OUT_OF_SYNC_RETRY_BASE_DELAY * 2 ** (retryCount - 1),
|
|
2574
|
+
OUT_OF_SYNC_RETRY_MAX_DELAY
|
|
2575
|
+
);
|
|
2576
|
+
return timer(backoff);
|
|
2577
|
+
}
|
|
2547
2578
|
}),
|
|
2548
2579
|
tap$1(
|
|
2549
2580
|
(remote) => state.set(
|
|
@@ -2582,15 +2613,58 @@ const _resolveDocument = bindActionByResource(
|
|
|
2582
2613
|
).subscribe({
|
|
2583
2614
|
error: (error) => state.set("setError", { error })
|
|
2584
2615
|
});
|
|
2585
|
-
}
|
|
2616
|
+
}, subscribeToCurrentUserAndSetIdentity = ({
|
|
2617
|
+
instance,
|
|
2618
|
+
state
|
|
2619
|
+
}) => getCurrentUserState(instance).observable.subscribe({
|
|
2620
|
+
next: (currentUser) => state.set("setIdentity", { identity: currentUser?.id }),
|
|
2621
|
+
// A transient identity-fetch failure (network blip, expired token, or a
|
|
2622
|
+
// normal logout/re-login transition) should not brick all document
|
|
2623
|
+
// operations. Reset the identity to `undefined` and keep going — GROQ's
|
|
2624
|
+
// `identity()` then evaluates to null, matching the unauthenticated state.
|
|
2625
|
+
error: () => state.set("setIdentity", { identity: void 0 })
|
|
2626
|
+
}), MEDIA_LIBRARY_DRAFTED_TYPES = /* @__PURE__ */ new Set(["sanity.asset"]);
|
|
2627
|
+
function getEffectiveDocumentModel(resource, documentType) {
|
|
2628
|
+
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 };
|
|
2629
|
+
}
|
|
2630
|
+
function describeResource(resource) {
|
|
2631
|
+
return resource && isCanvasResource(resource) ? "Canvas" : resource && isMediaLibraryResource(resource) ? "Media Library" : "this resource";
|
|
2632
|
+
}
|
|
2633
|
+
function normalizeActionsForResource(actions, resource) {
|
|
2634
|
+
const stripped = [], normalized = actions.map((action) => {
|
|
2635
|
+
if (action.type !== "document.edit") return action;
|
|
2636
|
+
const { liveEdit: forcedLiveEdit, supportsReleases } = getEffectiveDocumentModel(
|
|
2637
|
+
resource,
|
|
2638
|
+
action.documentType
|
|
2639
|
+
), shouldRemovePerspective = isReleasePerspective(action.perspective) && !supportsReleases, shouldForceLiveEdit = forcedLiveEdit === !0 && !action.liveEdit;
|
|
2640
|
+
if (!shouldRemovePerspective && !shouldForceLiveEdit) return action;
|
|
2641
|
+
const corrected = { ...action };
|
|
2642
|
+
return shouldForceLiveEdit && (corrected.liveEdit = !0), shouldRemovePerspective && (corrected.perspective = void 0), corrected.documentId = getEffectiveDocumentId({
|
|
2643
|
+
...corrected,
|
|
2644
|
+
documentId: getPublishedId(DocumentId(corrected.documentId))
|
|
2645
|
+
}), shouldRemovePerspective && stripped.push({ documentType: action.documentType, documentId: corrected.documentId }), corrected;
|
|
2646
|
+
});
|
|
2647
|
+
if (stripped.length > 0) {
|
|
2648
|
+
const docs = stripped.map((e) => `${e.documentType} (${e.documentId})`).join(", ");
|
|
2649
|
+
console.warn(
|
|
2650
|
+
`[sanity-sdk] ${describeResource(resource)} does not support release perspectives \u2014 falling back to the standard editing path for: ${docs}`
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
return normalized;
|
|
2654
|
+
}
|
|
2586
2655
|
function applyDocumentActions(...args) {
|
|
2587
2656
|
return boundApplyDocumentActions(...args);
|
|
2588
2657
|
}
|
|
2589
2658
|
const boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocumentActions);
|
|
2590
|
-
async function _applyDocumentActions({ state }, {
|
|
2591
|
-
|
|
2659
|
+
async function _applyDocumentActions({ state }, {
|
|
2660
|
+
actions,
|
|
2661
|
+
resource,
|
|
2662
|
+
transactionId = crypto.randomUUID(),
|
|
2663
|
+
disableBatching
|
|
2664
|
+
}) {
|
|
2665
|
+
const { events } = state.get(), normalizedActions = normalizeActionsForResource(actions, resource), transaction = {
|
|
2592
2666
|
transactionId,
|
|
2593
|
-
actions,
|
|
2667
|
+
actions: normalizedActions,
|
|
2594
2668
|
...disableBatching && { disableBatching }
|
|
2595
2669
|
}, fatalError$ = state.observable.pipe(
|
|
2596
2670
|
map$1((s) => s.error),
|