@sanity/sdk 3.1.0 → 3.3.0-rc.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/_exports/_internal.d.ts +52 -21
- package/dist/_exports/_internal.d.ts.map +1 -1
- package/dist/_exports/_internal.js +10 -9
- package/dist/_exports/_internal.js.map +1 -1
- package/dist/_exports/agent.d.ts +7 -7
- package/dist/_exports/agent.d.ts.map +1 -1
- package/dist/_exports/agent.js +1 -1
- package/dist/_exports/comlink.d.ts +9 -9
- package/dist/_exports/comlink.d.ts.map +1 -1
- package/dist/_exports/comlink.js +3 -2
- package/dist/_exports/comlink.js.map +1 -1
- package/dist/_exports/dashboard.d.ts +2 -0
- package/dist/_exports/dashboard.js +2 -0
- package/dist/{authStore-C-OEkGaM.d.ts → authStore-BGoEGxOq.d.ts} +531 -63
- package/dist/authStore-BGoEGxOq.d.ts.map +1 -0
- package/dist/{clientStore-gUALQ0qW.js → clientStore-QDrJNTM4.js} +128 -7
- package/dist/clientStore-QDrJNTM4.js.map +1 -0
- package/dist/{createSanityInstance-DCePpBYW.d.ts → createSanityInstance-BQIqyGLX.d.ts} +16 -1
- package/dist/createSanityInstance-BQIqyGLX.d.ts.map +1 -0
- package/dist/{object-BwynGPRZ.js → createStateSourceAction-DdAhEfnZ.js} +2 -82
- package/dist/createStateSourceAction-DdAhEfnZ.js.map +1 -0
- package/dist/defineProperty-Bho9Ngsi.js +31 -0
- package/dist/{getNodeState-E0hjqFOo.js → getNodeState-B4nG0sjm.js} +3 -2
- package/dist/{getNodeState-E0hjqFOo.js.map → getNodeState-B4nG0sjm.js.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +371 -54
- package/dist/index.js.map +1 -1
- package/dist/object-CxXlSVg7.js +83 -0
- package/dist/object-CxXlSVg7.js.map +1 -0
- package/dist/topicStore-Ct7IFNtY.js +565 -0
- package/dist/topicStore-Ct7IFNtY.js.map +1 -0
- package/dist/topicStore-DXvuH9h4.d.ts +435 -0
- package/dist/topicStore-DXvuH9h4.d.ts.map +1 -0
- package/dist/{version-C90rNWaX.js → version-CIhX5ppJ.js} +6 -6
- package/dist/{version-C90rNWaX.js.map → version-CIhX5ppJ.js.map} +1 -1
- package/package.json +20 -19
- package/src/_exports/_internal.ts +12 -0
- package/src/_exports/dashboard.ts +36 -0
- package/src/_exports/index.ts +17 -1
- package/src/applications/applications.test-d.ts +28 -4
- package/src/applications/applications.ts +39 -14
- package/src/auth/authMode.test.ts +24 -0
- package/src/auth/authMode.ts +8 -3
- package/src/auth/authStore.test.ts +18 -0
- package/src/auth/authStore.ts +15 -0
- package/src/auth/authStrategy.ts +2 -0
- package/src/auth/handleAuthCallback.test.ts +33 -0
- package/src/auth/handleAuthCallback.ts +11 -1
- package/src/auth/logout.test.ts +53 -0
- package/src/auth/logout.ts +19 -1
- package/src/auth/oauth/oauthActions.test.ts +593 -0
- package/src/auth/oauth/oauthActions.ts +438 -0
- package/src/auth/oauth/oauthAuth.test.ts +229 -0
- package/src/auth/oauth/oauthAuth.ts +178 -0
- package/src/auth/oauth/pkce.test.ts +44 -0
- package/src/auth/oauth/pkce.ts +60 -0
- package/src/auth/oauth/types.ts +12 -0
- package/src/config/authConfig.ts +18 -0
- package/src/dashboard/messageBus/__fixtures__/test-topics.ts +18 -0
- package/src/dashboard/messageBus/bus.test.ts +1033 -0
- package/src/dashboard/messageBus/bus.ts +1246 -0
- package/src/dashboard/messageBus/store.test.ts +142 -0
- package/src/dashboard/messageBus/store.ts +74 -0
- package/src/dashboard/messageBus/topicStore.test.ts +172 -0
- package/src/dashboard/messageBus/topicStore.ts +184 -0
- package/src/dashboard/messageBus/topics.test.ts +71 -0
- package/src/dashboard/messageBus/topics.ts +260 -0
- package/src/document/actions.ts +3 -3
- package/src/document/applyDocumentActions.ts +3 -3
- package/src/document/documentStore.ts +12 -12
- package/src/document/processMutations.ts +58 -22
- package/src/installations/installations.test-d.ts +3 -3
- package/src/installations/installations.ts +2 -19
- package/src/projection/getProjectionState.ts +2 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/query/queryStore.ts +3 -3
- package/src/typegen/defineProjection.test.ts +17 -0
- package/src/typegen/defineProjection.ts +19 -0
- package/src/typegen/groqCompat.ts +122 -0
- package/src/typegen/resolve.ts +37 -0
- package/src/utils/createGroqSearchFilter.test.ts +8 -0
- package/src/utils/createGroqSearchFilter.ts +6 -3
- package/dist/authStore-C-OEkGaM.d.ts.map +0 -1
- package/dist/clientStore-gUALQ0qW.js.map +0 -1
- package/dist/createSanityInstance-DCePpBYW.d.ts.map +0 -1
- package/dist/object-BwynGPRZ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { D as
|
|
2
|
-
import {
|
|
3
|
-
import { A as clearPendingTransaction, B as assertDatasetResource, C as getCommentsState, D as toCommentsKeyParts, E as resolveComments, F as rollbackCommentUpdate, G as defineMutation, H as provisionAddonDataset, I as setCommentCreateError, K as randomId, L as setPendingTransaction, M as receiveComment, N as removeCommentById, O as addComment, P as restoreComments, R as normalizeComment, T as resolveCommentThreads, U as COMMENTS_API_VERSION, V as getAddonDatasetState, W as defineFetcher, _ as updateLastLoadMoreRequest, a as resolveQuery, b as commentsStore, c as getAllReleasesState, d as getUsersKey, f as initializeRequest, g as setUsersError, h as setUsersData, j as getCommentsKey, k as applyCommentUpdate, l as addSubscription, m as removeSubscription, o as getPerspectiveState, p as parseUsersKey, q as randomUuid, r as getQueryState, s as getActiveReleasesState, t as CORE_SDK_VERSION, u as cancelRequest, v as PROJECT_API_VERSION, x as getCommentThreadsState, y as USERS_STATE_CLEAR_DELAY, z as toCommentFieldPath } from "./version-
|
|
1
|
+
import { D as getAuthLogger, E as REQUEST_TAG_PREFIX, O as configureLogging$1, S as AuthStateType, T as DEFAULT_BASE, b as getTokenFromLocation, c as getDashboardOrganizationId, d as getTokenState, f as setAuthToken, h as getCleanedUrl, i as authStore, k as createLogger, l as getIsInDashboardState, m as getAuthCode, n as getClient, o as getAuthState, p as createLoggedInAuthState, r as getClientState, s as getCurrentUserState, u as getLoginUrlState, w as DEFAULT_API_VERSION, y as getDefaultLocation } from "./clientStore-QDrJNTM4.js";
|
|
2
|
+
import { c as isReleasePerspective, d as isMediaLibraryResource, i as bindActionGlobally, l as isCanvasResource, n as bindActionByResource, o as defineStore, r as bindActionByResourceAndPerspective, t as createStateSourceAction, u as isDatasetResource } from "./createStateSourceAction-DdAhEfnZ.js";
|
|
3
|
+
import { A as clearPendingTransaction, B as assertDatasetResource, C as getCommentsState, D as toCommentsKeyParts, E as resolveComments, F as rollbackCommentUpdate, G as defineMutation, H as provisionAddonDataset, I as setCommentCreateError, K as randomId, L as setPendingTransaction, M as receiveComment, N as removeCommentById, O as addComment, P as restoreComments, R as normalizeComment, T as resolveCommentThreads, U as COMMENTS_API_VERSION, V as getAddonDatasetState, W as defineFetcher, _ as updateLastLoadMoreRequest, a as resolveQuery, b as commentsStore, c as getAllReleasesState, d as getUsersKey, f as initializeRequest, g as setUsersError, h as setUsersData, j as getCommentsKey, k as applyCommentUpdate, l as addSubscription, m as removeSubscription, o as getPerspectiveState, p as parseUsersKey, q as randomUuid, r as getQueryState, s as getActiveReleasesState, t as CORE_SDK_VERSION, u as cancelRequest, v as PROJECT_API_VERSION, x as getCommentThreadsState, y as USERS_STATE_CLEAR_DELAY, z as toCommentFieldPath } from "./version-CIhX5ppJ.js";
|
|
4
|
+
import { r as omitProperty, t as isDeepEqual } from "./object-CxXlSVg7.js";
|
|
4
5
|
import { n as setCleanupTimeout } from "./setCleanupTimeout-BiM6q1KF.js";
|
|
5
|
-
import { t as
|
|
6
|
+
import { t as _defineProperty } from "./defineProperty-Bho9Ngsi.js";
|
|
7
|
+
import { t as getNodeState } from "./getNodeState-B4nG0sjm.js";
|
|
6
8
|
import { ClientError, CorsOriginError } from "@sanity/client";
|
|
7
9
|
import { EMPTY, NEVER, Observable, Subject, Subscription, auditTime, catchError, combineLatest, concat, concatMap, debounceTime, defer, distinctUntilChanged, filter, first, firstValueFrom, from, fromEvent, groupBy, map, merge, mergeMap, of, pairwise, partition, race, retry, share, shareReplay, skip, startWith, switchMap, take, takeUntil, tap, throttle, throwError, timer, withLatestFrom } from "rxjs";
|
|
8
10
|
import { isKeySegment, isKeyedObject } from "@sanity/types";
|
|
@@ -10,7 +12,6 @@ import { DocumentId, getDraftId, getPublishedId, getVersionId, isDraftId, isPubl
|
|
|
10
12
|
import { getIndexForKey, getIndexForKey as getIndexForKey$1, getPathDepth, joinPaths, jsonMatch, jsonMatch as jsonMatch$1, slicePath, slicePath as slicePath$1, stringifyPath, stringifyPath as stringifyPath$1 } from "@sanity/json-match";
|
|
11
13
|
import { SanityEncoder } from "@sanity/mutate";
|
|
12
14
|
import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
|
|
13
|
-
import "groq";
|
|
14
15
|
import { evaluateSync, parse } from "groq-js";
|
|
15
16
|
import { catchError as catchError$1, distinctUntilChanged as distinctUntilChanged$1, map as map$1, mergeMap as mergeMap$1, retry as retry$1, scan, share as share$1, switchMap as switchMap$1 } from "rxjs/operators";
|
|
16
17
|
import { applyPatches, parsePatch } from "@sanity/diff-match-patch";
|
|
@@ -302,11 +303,295 @@ function observeOrganizationVerificationState(instance, projectIds) {
|
|
|
302
303
|
}), distinctUntilChanged((prev, curr) => prev.error === curr.error));
|
|
303
304
|
}
|
|
304
305
|
/**
|
|
306
|
+
* Encodes a byte array as a base64url string (RFC 4648 §5) with no padding,
|
|
307
|
+
* as required for PKCE `code_verifier`/`code_challenge` values.
|
|
308
|
+
*
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
311
|
+
function base64UrlEncode(bytes) {
|
|
312
|
+
let binary = "";
|
|
313
|
+
for (let byte of bytes) binary += String.fromCharCode(byte);
|
|
314
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
315
|
+
}
|
|
316
|
+
function randomString() {
|
|
317
|
+
let bytes = /* @__PURE__ */ new Uint8Array(32);
|
|
318
|
+
return crypto.getRandomValues(bytes), base64UrlEncode(bytes);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Generates a PKCE `code_verifier`
|
|
322
|
+
*
|
|
323
|
+
* @internal
|
|
324
|
+
*/
|
|
325
|
+
function generateCodeVerifier() {
|
|
326
|
+
return randomString();
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Generates the `state` parameter used to defend against CSRF on the OAuth
|
|
330
|
+
* callback.
|
|
331
|
+
*
|
|
332
|
+
* @internal
|
|
333
|
+
*/
|
|
334
|
+
function generateState() {
|
|
335
|
+
return randomString();
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Derives the PKCE `code_challenge` from a `code_verifier` using
|
|
339
|
+
* `base64url(sha256(verifier))` (the `S256` method).
|
|
340
|
+
*
|
|
341
|
+
* @internal
|
|
342
|
+
*/
|
|
343
|
+
async function generateCodeChallenge(codeVerifier) {
|
|
344
|
+
let data = new TextEncoder().encode(codeVerifier), digest = await crypto.subtle.digest("SHA-256", data);
|
|
345
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
346
|
+
}
|
|
347
|
+
/** sessionStorage key for the PKCE `code_verifier`. */
|
|
348
|
+
const OAUTH_VERIFIER_KEY = "__sanity_oauth_verifier", OAUTH_STATE_KEY = "__sanity_oauth_state", OAUTH_RETURN_TO_KEY = "__sanity_oauth_return_to", OAUTH_CALLBACK_PARAMS = [
|
|
349
|
+
"code",
|
|
350
|
+
"state",
|
|
351
|
+
"error",
|
|
352
|
+
"error_description"
|
|
353
|
+
];
|
|
354
|
+
/** Strips the OAuth callback params from a URL. */
|
|
355
|
+
function stripOAuthParams(href) {
|
|
356
|
+
let url = new URL(href, DEFAULT_BASE);
|
|
357
|
+
for (let param of OAUTH_CALLBACK_PARAMS) url.searchParams.delete(param);
|
|
358
|
+
return url.toString();
|
|
359
|
+
}
|
|
360
|
+
/** Whether `href` is the registered redirect URI (origin + pathname; query and hash ignored). */
|
|
361
|
+
function isCallbackRoute(href, redirectUri) {
|
|
362
|
+
let current = new URL(href, DEFAULT_BASE), redirect = new URL(redirectUri, DEFAULT_BASE);
|
|
363
|
+
return current.origin === redirect.origin && current.pathname === redirect.pathname;
|
|
364
|
+
}
|
|
365
|
+
/** Builds the RFC 8707 resource indicator for an organisation. */
|
|
366
|
+
function getResourceIndicator(organizationId) {
|
|
367
|
+
return `urn:io.sanity:organization:${organizationId}`;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Serialises tokens for storage, converting `expiresAt` to an ISO string.
|
|
371
|
+
*
|
|
372
|
+
* @internal
|
|
373
|
+
*/
|
|
374
|
+
function serializeTokens(tokens) {
|
|
375
|
+
return JSON.stringify({
|
|
376
|
+
accessToken: tokens.accessToken,
|
|
377
|
+
tokenType: tokens.tokenType,
|
|
378
|
+
expiresIn: tokens.expiresIn,
|
|
379
|
+
expiresAt: tokens.expiresAt.toISOString(),
|
|
380
|
+
...tokens.refreshToken !== void 0 && { refreshToken: tokens.refreshToken }
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Reads the store options, throwing when the instance was not configured for
|
|
385
|
+
* OAuth.
|
|
386
|
+
*/
|
|
387
|
+
function getOAuthOptions(state) {
|
|
388
|
+
let { options } = state;
|
|
389
|
+
if (!options.oauth) throw Error("OAuth is not configured on this instance (missing `auth.oauth`).");
|
|
390
|
+
return options;
|
|
391
|
+
}
|
|
392
|
+
/** Creates a client for the (unauthenticated) public OAuth token/revoke calls. */
|
|
393
|
+
function createOAuthClient(options) {
|
|
394
|
+
return options.clientFactory({
|
|
395
|
+
apiVersion: "v1",
|
|
396
|
+
requestTagPrefix: REQUEST_TAG_PREFIX,
|
|
397
|
+
useProjectHostname: !1,
|
|
398
|
+
useCdn: !1,
|
|
399
|
+
...options.apiHost && { apiHost: options.apiHost }
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
/** Converts a token endpoint response to the {@link OAuthTokens} shape. */
|
|
403
|
+
function toOAuthTokens(response) {
|
|
404
|
+
return {
|
|
405
|
+
accessToken: response.access_token,
|
|
406
|
+
tokenType: "bearer",
|
|
407
|
+
expiresIn: response.expires_in,
|
|
408
|
+
expiresAt: new Date(Date.now() + response.expires_in * 1e3),
|
|
409
|
+
...response.refresh_token !== void 0 && { refreshToken: response.refresh_token }
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Starts the OAuth authorization-code + PKCE flow: generates a `code_verifier`,
|
|
414
|
+
* `code_challenge` and `state`, persists the verifier and state to
|
|
415
|
+
* `sessionStorage`, then navigates the browser to the authorize endpoint.
|
|
416
|
+
*
|
|
417
|
+
* @public
|
|
418
|
+
*/
|
|
419
|
+
const startOAuthAuthorization = bindActionGlobally(authStore, async ({ state, instance }) => {
|
|
420
|
+
let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), codeVerifier = generateCodeVerifier(), oauthState = generateState(), codeChallenge = await generateCodeChallenge(codeVerifier), session = typeof sessionStorage < "u" ? sessionStorage : void 0;
|
|
421
|
+
session?.setItem(OAUTH_VERIFIER_KEY, codeVerifier), session?.setItem(OAUTH_STATE_KEY, oauthState);
|
|
422
|
+
let currentHref = getDefaultLocation();
|
|
423
|
+
if (currentHref !== "http://localhost") {
|
|
424
|
+
let returnTo = isCallbackRoute(currentHref, options.oauth.redirectUri) ? stripOAuthParams(currentHref) : currentHref;
|
|
425
|
+
session?.setItem(OAUTH_RETURN_TO_KEY, returnTo);
|
|
426
|
+
}
|
|
427
|
+
let authorizeUrl = new URL("/v1/auth/oauth/authorize", options.apiHost ?? "https://api.sanity.io");
|
|
428
|
+
authorizeUrl.searchParams.set("response_type", "code"), authorizeUrl.searchParams.set("client_id", options.oauth.clientId), authorizeUrl.searchParams.set("redirect_uri", options.oauth.redirectUri), authorizeUrl.searchParams.set("state", oauthState), authorizeUrl.searchParams.set("code_challenge", codeChallenge), authorizeUrl.searchParams.set("code_challenge_method", "S256"), authorizeUrl.searchParams.append("resource", getResourceIndicator(options.oauth.organizationId)), logger.info("Starting OAuth authorization"), typeof window < "u" && typeof window.location?.assign == "function" && window.location.assign(authorizeUrl.toString());
|
|
429
|
+
}), handleOAuthCallback = bindActionGlobally(authStore, async ({ state, instance }, locationHref = getDefaultLocation()) => {
|
|
430
|
+
let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), { authState } = state.get();
|
|
431
|
+
if (authState.type === "logging-in" && authState.isExchangingToken) return logger.debug("Skipping OAuth callback - token exchange already in progress"), !1;
|
|
432
|
+
let callbackUrl = new URL(locationHref, DEFAULT_BASE), code = callbackUrl.searchParams.get("code"), returnedState = callbackUrl.searchParams.get("state"), error = callbackUrl.searchParams.get("error"), errorDescription = callbackUrl.searchParams.get("error_description"), session = typeof sessionStorage < "u" ? sessionStorage : void 0, cleanedUrl = stripOAuthParams(locationHref), rejectCallback = (name, message) => authState.type === "logged-in" ? (logger.warn(`${message} — ignoring callback, session already established`), cleanedUrl) : (logger.error(`${message} — rejecting callback`), clearOAuthArtifacts(session), state.set(name, { authState: {
|
|
433
|
+
type: "error",
|
|
434
|
+
error: Error(message)
|
|
435
|
+
} }), cleanedUrl);
|
|
436
|
+
if (error) return rejectCallback("oauthCallbackError", errorDescription ? `${error}: ${errorDescription}` : error);
|
|
437
|
+
if (!code) return logger.debug("No OAuth code found in callback URL"), !1;
|
|
438
|
+
let storedState = session?.getItem("__sanity_oauth_state") ?? null;
|
|
439
|
+
if (!returnedState || !storedState || returnedState !== storedState) return rejectCallback("oauthStateMismatch", "OAuth state mismatch");
|
|
440
|
+
let codeVerifier = session?.getItem("__sanity_oauth_verifier") ?? null;
|
|
441
|
+
if (!codeVerifier) return rejectCallback("oauthVerifierMissing", "OAuth code verifier missing");
|
|
442
|
+
logger.info("Exchanging OAuth code for tokens"), state.set("oauthExchange", { authState: {
|
|
443
|
+
type: "logging-in",
|
|
444
|
+
isExchangingToken: !0
|
|
445
|
+
} });
|
|
446
|
+
try {
|
|
447
|
+
let client = createOAuthClient(options), params = new URLSearchParams({
|
|
448
|
+
grant_type: "authorization_code",
|
|
449
|
+
code,
|
|
450
|
+
code_verifier: codeVerifier,
|
|
451
|
+
redirect_uri: options.oauth.redirectUri,
|
|
452
|
+
client_id: options.oauth.clientId,
|
|
453
|
+
resource: getResourceIndicator(options.oauth.organizationId)
|
|
454
|
+
}), tokens = toOAuthTokens(await client.request({
|
|
455
|
+
method: "POST",
|
|
456
|
+
url: "/auth/oauth/token",
|
|
457
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
458
|
+
body: params.toString(),
|
|
459
|
+
tag: "oauth.token"
|
|
460
|
+
}));
|
|
461
|
+
options.storageArea?.setItem(options.storageKey, serializeTokens(tokens));
|
|
462
|
+
let returnTo = getSameOriginReturnTo(session, options.oauth.redirectUri);
|
|
463
|
+
return clearOAuthArtifacts(session), logger.info("OAuth tokens obtained, user logged in"), state.set("oauthLoggedIn", {
|
|
464
|
+
authState: createLoggedInAuthState(tokens.accessToken, null),
|
|
465
|
+
oauthTokens: tokens
|
|
466
|
+
}), returnTo ?? cleanedUrl;
|
|
467
|
+
} catch (exchangeError) {
|
|
468
|
+
return logger.error("Failed to exchange OAuth code for tokens", { error: exchangeError }), clearOAuthArtifacts(session), state.set("oauthExchangeError", { authState: {
|
|
469
|
+
type: "error",
|
|
470
|
+
error: exchangeError
|
|
471
|
+
} }), cleanedUrl;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
/**
|
|
475
|
+
* A refresh failure is unrecoverable only when the token endpoint rejects the
|
|
476
|
+
* refresh token itself (a 4xx). Rate-limit (429) and request-timeout (408)
|
|
477
|
+
* responses, 5xx errors and network failures are transient, so the session is
|
|
478
|
+
* kept intact for the caller to retry rather than forcing a logout.
|
|
479
|
+
*/
|
|
480
|
+
function isUnrecoverableRefreshError(error) {
|
|
481
|
+
return error instanceof ClientError && error.statusCode !== 408 && error.statusCode !== 429;
|
|
482
|
+
}
|
|
483
|
+
let refreshInFlight = null;
|
|
484
|
+
/**
|
|
485
|
+
* Refreshes the OAuth tokens using the `refresh_token` grant. Concurrent
|
|
486
|
+
* callers share a single in-flight request. An unrecoverable failure (a 4xx
|
|
487
|
+
* rejecting the refresh token) clears the tokens and transitions to
|
|
488
|
+
* `LOGGED_OUT`; transient failures (network, 5xx, rate limits) leave the
|
|
489
|
+
* session intact and rethrow so the caller can retry.
|
|
490
|
+
*
|
|
491
|
+
* @public
|
|
492
|
+
*/
|
|
493
|
+
const refreshOAuthTokens = bindActionGlobally(authStore, (context) => refreshInFlight || (refreshInFlight = doRefreshOAuthTokens(context).finally(() => {
|
|
494
|
+
refreshInFlight = null;
|
|
495
|
+
}), refreshInFlight));
|
|
496
|
+
async function doRefreshOAuthTokens({ state, instance }) {
|
|
497
|
+
let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), current = state.get().oauthTokens;
|
|
498
|
+
if (!current?.refreshToken) return logger.warn("No refresh token available — logging out"), options.storageArea?.removeItem(options.storageKey), state.set("oauthRefreshNoToken", {
|
|
499
|
+
authState: {
|
|
500
|
+
type: "logged-out",
|
|
501
|
+
isDestroyingSession: !1
|
|
502
|
+
},
|
|
503
|
+
oauthTokens: void 0
|
|
504
|
+
}), null;
|
|
505
|
+
try {
|
|
506
|
+
let client = createOAuthClient(options), params = new URLSearchParams({
|
|
507
|
+
grant_type: "refresh_token",
|
|
508
|
+
refresh_token: current.refreshToken,
|
|
509
|
+
client_id: options.oauth.clientId,
|
|
510
|
+
resource: getResourceIndicator(options.oauth.organizationId)
|
|
511
|
+
}), tokens = toOAuthTokens(await client.request({
|
|
512
|
+
method: "POST",
|
|
513
|
+
url: "/auth/oauth/token",
|
|
514
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
515
|
+
body: params.toString(),
|
|
516
|
+
tag: "oauth.refresh"
|
|
517
|
+
}));
|
|
518
|
+
return tokens.refreshToken ||= current.refreshToken, options.storageArea?.setItem(options.storageKey, serializeTokens(tokens)), logger.info("OAuth tokens refreshed"), state.set("oauthRefreshed", {
|
|
519
|
+
authState: createLoggedInAuthState(tokens.accessToken, null),
|
|
520
|
+
oauthTokens: tokens
|
|
521
|
+
}), tokens;
|
|
522
|
+
} catch (error) {
|
|
523
|
+
throw isUnrecoverableRefreshError(error) ? (logger.error("OAuth token refresh failed — logging out", { error }), options.storageArea?.removeItem(options.storageKey), state.set("oauthRefreshFailed", {
|
|
524
|
+
authState: {
|
|
525
|
+
type: "logged-out",
|
|
526
|
+
isDestroyingSession: !1
|
|
527
|
+
},
|
|
528
|
+
oauthTokens: void 0
|
|
529
|
+
}), error) : (logger.warn("OAuth token refresh failed — keeping session for retry", { error }), error);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Revokes the OAuth tokens then clears local storage and transitions to `LOGGED_OUT`.
|
|
534
|
+
* Local state is cleared even if the revoke request fails.
|
|
535
|
+
*
|
|
536
|
+
* @public
|
|
537
|
+
*/
|
|
538
|
+
const revokeOAuthTokens = bindActionGlobally(authStore, async ({ state, instance }) => {
|
|
539
|
+
let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), current = state.get().oauthTokens, token = current?.refreshToken ?? current?.accessToken;
|
|
540
|
+
try {
|
|
541
|
+
if (token) {
|
|
542
|
+
let client = createOAuthClient(options), params = new URLSearchParams({
|
|
543
|
+
token,
|
|
544
|
+
client_id: options.oauth.clientId
|
|
545
|
+
});
|
|
546
|
+
await client.request({
|
|
547
|
+
method: "POST",
|
|
548
|
+
url: "/auth/oauth/revoke",
|
|
549
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
550
|
+
body: params.toString(),
|
|
551
|
+
tag: "oauth.revoke"
|
|
552
|
+
}), logger.info("OAuth tokens revoked");
|
|
553
|
+
}
|
|
554
|
+
} catch (error) {
|
|
555
|
+
logger.warn("OAuth revoke request failed — clearing local state anyway", { error });
|
|
556
|
+
} finally {
|
|
557
|
+
options.storageArea?.removeItem(options.storageKey), state.set("oauthRevoked", {
|
|
558
|
+
authState: {
|
|
559
|
+
type: "logged-out",
|
|
560
|
+
isDestroyingSession: !1
|
|
561
|
+
},
|
|
562
|
+
oauthTokens: void 0
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}), getOAuthTokensState = bindActionGlobally(authStore, createStateSourceAction(({ state }) => state.oauthTokens ?? null));
|
|
566
|
+
/** Removes the transient PKCE artifacts from session storage. */
|
|
567
|
+
function clearOAuthArtifacts(session) {
|
|
568
|
+
session?.removeItem(OAUTH_VERIFIER_KEY), session?.removeItem(OAUTH_STATE_KEY), session?.removeItem(OAUTH_RETURN_TO_KEY);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Reads the stashed pre-authorize location, only honouring it when it shares
|
|
572
|
+
* the registered redirect URI's origin so a tampered value cannot redirect
|
|
573
|
+
* off-site. (`origin` includes the scheme, so non-http(s) URLs are rejected.)
|
|
574
|
+
*/
|
|
575
|
+
function getSameOriginReturnTo(session, redirectUri) {
|
|
576
|
+
let returnTo = session?.getItem("__sanity_oauth_return_to") ?? null;
|
|
577
|
+
if (!returnTo) return null;
|
|
578
|
+
try {
|
|
579
|
+
return new URL(returnTo).origin === new URL(redirectUri).origin ? returnTo : null;
|
|
580
|
+
} catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Handles the auth redirect callback. In OAuth mode this delegates to
|
|
586
|
+
* {@link handleOAuthCallback}; otherwise it exchanges the legacy `sid` for a
|
|
587
|
+
* long-lived token.
|
|
588
|
+
*
|
|
305
589
|
* @public
|
|
306
590
|
*/
|
|
307
591
|
const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instance }, locationHref = getDefaultLocation()) => {
|
|
308
|
-
let logger = getAuthLogger(instance), { providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey } = state.get().options;
|
|
592
|
+
let logger = getAuthLogger(instance), { providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey, oauth } = state.get().options;
|
|
309
593
|
if (providedToken) return logger.debug("Skipping auth callback - token already provided"), !1;
|
|
594
|
+
if (oauth) return logger.debug("OAuth mode - delegating to handleOAuthCallback"), handleOAuthCallback(instance, locationHref);
|
|
310
595
|
let { authState } = state.get();
|
|
311
596
|
if (authState.type === "logging-in" && authState.isExchangingToken) return logger.debug("Skipping auth callback - token exchange already in progress"), !1;
|
|
312
597
|
let cleanedUrl = getCleanedUrl(locationHref), tokenFromUrl = getTokenFromLocation(locationHref);
|
|
@@ -353,7 +638,7 @@ const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instanc
|
|
|
353
638
|
} }), cleanedUrl;
|
|
354
639
|
}
|
|
355
640
|
}), logout = bindActionGlobally(authStore, async ({ state, instance }) => {
|
|
356
|
-
let logger = getAuthLogger(instance), { clientFactory, apiHost, providedToken, storageArea, storageKey } = state.get().options;
|
|
641
|
+
let logger = getAuthLogger(instance), { clientFactory, apiHost, providedToken, storageArea, storageKey, oauth } = state.get().options;
|
|
357
642
|
if (providedToken) {
|
|
358
643
|
logger.debug("Skipping logout - token is statically provided");
|
|
359
644
|
return;
|
|
@@ -363,6 +648,13 @@ const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instanc
|
|
|
363
648
|
logger.debug("Skipping logout - already in progress");
|
|
364
649
|
return;
|
|
365
650
|
}
|
|
651
|
+
if (oauth) {
|
|
652
|
+
logger.debug("OAuth mode - revoking tokens instead of calling /auth/logout"), state.set("loggingOut", { authState: {
|
|
653
|
+
type: "logged-out",
|
|
654
|
+
isDestroyingSession: !0
|
|
655
|
+
} }), await revokeOAuthTokens(instance);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
366
658
|
let token = authState.type === "logged-in" && authState.token;
|
|
367
659
|
try {
|
|
368
660
|
if (token) {
|
|
@@ -387,10 +679,13 @@ const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instanc
|
|
|
387
679
|
} catch (error) {
|
|
388
680
|
throw logger.error("Logout request failed", { error }), error;
|
|
389
681
|
} finally {
|
|
390
|
-
logger.info("User logged out, clearing stored tokens"), state.set("logoutSuccess", {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
682
|
+
logger.info("User logged out, clearing stored tokens"), state.set("logoutSuccess", {
|
|
683
|
+
authState: {
|
|
684
|
+
type: "logged-out",
|
|
685
|
+
isDestroyingSession: !1
|
|
686
|
+
},
|
|
687
|
+
oauthTokens: void 0
|
|
688
|
+
}), storageArea?.removeItem(storageKey), storageArea?.removeItem(`${storageKey}_last_refresh`);
|
|
394
689
|
}
|
|
395
690
|
});
|
|
396
691
|
function weaken(node) {
|
|
@@ -1228,7 +1523,29 @@ function unsetDeep(input, path) {
|
|
|
1228
1523
|
let segment = _segment;
|
|
1229
1524
|
return typeof segment == "number" && Array.isArray(input) && (segment = segment < 0 ? input.length + segment : segment), segment in input ? restOfPath.length ? Array.isArray(input) ? input.map((nestedInput, index) => index === segment ? unsetDeep(nestedInput, restOfPath) : nestedInput) : Object.fromEntries(Object.entries(input).map(([key, value]) => key === segment ? [key, unsetDeep(value, restOfPath)] : [key, value])) : Array.isArray(input) ? input.filter((_nestedInput, index) => index !== segment) : Object.fromEntries(Object.entries(input).filter(([key]) => key !== segment.toString())) : input;
|
|
1230
1525
|
}
|
|
1231
|
-
|
|
1526
|
+
/**
|
|
1527
|
+
* Exposes the patch operations used by {@link processMutations}.
|
|
1528
|
+
*
|
|
1529
|
+
* @remarks
|
|
1530
|
+
* Property order determines execution order in {@link processMutations}:
|
|
1531
|
+
* `ifRevisionID`, `set`, `setIfMissing`, `unset`, `inc`, `dec`, `insert`,
|
|
1532
|
+
* then `diffMatchPatch`. Reordering the properties changes mutation results.
|
|
1533
|
+
* Freezes the collection to prevent callers from replacing the functions
|
|
1534
|
+
* used by the SDK's mutation evaluator.
|
|
1535
|
+
*
|
|
1536
|
+
* @see https://www.sanity.io/docs/http-mutations#5b4db1396e56
|
|
1537
|
+
*
|
|
1538
|
+
* @example
|
|
1539
|
+
* ```ts
|
|
1540
|
+
* const document = {items: [{_key: 'a', title: 'Before'}]}
|
|
1541
|
+
* const updated = patchOperations.set<typeof document>(document, {
|
|
1542
|
+
* 'items[_key=="a"].title': 'After',
|
|
1543
|
+
* })
|
|
1544
|
+
* ```
|
|
1545
|
+
*
|
|
1546
|
+
* @internal
|
|
1547
|
+
*/
|
|
1548
|
+
const patchOperations = Object.freeze({
|
|
1232
1549
|
ifRevisionID,
|
|
1233
1550
|
set,
|
|
1234
1551
|
setIfMissing,
|
|
@@ -1237,7 +1554,7 @@ const patchOperations = {
|
|
|
1237
1554
|
dec,
|
|
1238
1555
|
insert,
|
|
1239
1556
|
diffMatchPatch
|
|
1240
|
-
};
|
|
1557
|
+
});
|
|
1241
1558
|
/**
|
|
1242
1559
|
* Implements ID generation:
|
|
1243
1560
|
*
|
|
@@ -1265,18 +1582,31 @@ function getDocumentIds(selection) {
|
|
|
1265
1582
|
return [];
|
|
1266
1583
|
}
|
|
1267
1584
|
/**
|
|
1268
|
-
* Applies
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1585
|
+
* Applies mutations to an in-memory set of Sanity documents.
|
|
1586
|
+
* Deleted documents remain in the returned set with a value of `null`.
|
|
1587
|
+
*
|
|
1588
|
+
* Include every document affected by `mutations` in `documents`, using `null`
|
|
1589
|
+
* for documents that do not exist.
|
|
1590
|
+
*
|
|
1591
|
+
* Returns `documents` itself when `mutations` is empty. Otherwise, returns a
|
|
1592
|
+
* shallow copy of that object. The result can reuse document objects and
|
|
1593
|
+
* nested objects from `documents`. Mutating a reused object through the
|
|
1594
|
+
* result also changes the input.
|
|
1595
|
+
*
|
|
1596
|
+
* Sets `_rev` to `transactionId` on created, replaced, and patched documents.
|
|
1597
|
+
* Uses `timestamp` for timestamps unless the mutation preserves them.
|
|
1598
|
+
* Ignores `merge` patches; `inc` and `dec` skip values that are not numbers.
|
|
1599
|
+
* Generates document IDs when omitted or ending in `.`.
|
|
1600
|
+
* Patch operations can generate missing array keys.
|
|
1601
|
+
*
|
|
1602
|
+
* Does not persist documents or run Content Lake's server validation.
|
|
1274
1603
|
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1604
|
+
* @throws If a selection uses a query, a `create` targets an existing document,
|
|
1605
|
+
* or a `patch` targets a document that does not exist.
|
|
1606
|
+
* @throws If a patch operation fails, including a revision check failure or
|
|
1607
|
+
* `diffMatchPatch` applied to a value other than a string or `undefined`.
|
|
1277
1608
|
*
|
|
1278
|
-
*
|
|
1279
|
-
* and `_createdAt` timestamps.
|
|
1609
|
+
* @internal
|
|
1280
1610
|
*/
|
|
1281
1611
|
function processMutations({ documents, mutations, transactionId, timestamp }) {
|
|
1282
1612
|
if (!mutations.length) return documents;
|
|
@@ -1336,36 +1666,6 @@ function processMutations({ documents, mutations, transactionId, timestamp }) {
|
|
|
1336
1666
|
}
|
|
1337
1667
|
return dataset;
|
|
1338
1668
|
}
|
|
1339
|
-
function _typeof(o) {
|
|
1340
|
-
"@babel/helpers - typeof";
|
|
1341
|
-
return _typeof = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(o) {
|
|
1342
|
-
return typeof o;
|
|
1343
|
-
} : function(o) {
|
|
1344
|
-
return o && typeof Symbol == "function" && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
1345
|
-
}, _typeof(o);
|
|
1346
|
-
}
|
|
1347
|
-
function toPrimitive(t, r) {
|
|
1348
|
-
if (_typeof(t) != "object" || !t) return t;
|
|
1349
|
-
var e = t[Symbol.toPrimitive];
|
|
1350
|
-
if (e !== void 0) {
|
|
1351
|
-
var i = e.call(t, r || "default");
|
|
1352
|
-
if (_typeof(i) != "object") return i;
|
|
1353
|
-
throw TypeError("@@toPrimitive must return a primitive value.");
|
|
1354
|
-
}
|
|
1355
|
-
return (r === "string" ? String : Number)(t);
|
|
1356
|
-
}
|
|
1357
|
-
function toPropertyKey(t) {
|
|
1358
|
-
var i = toPrimitive(t, "string");
|
|
1359
|
-
return _typeof(i) == "symbol" ? i : i + "";
|
|
1360
|
-
}
|
|
1361
|
-
function _defineProperty(e, r, t) {
|
|
1362
|
-
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
1363
|
-
value: t,
|
|
1364
|
-
enumerable: !0,
|
|
1365
|
-
configurable: !0,
|
|
1366
|
-
writable: !0
|
|
1367
|
-
}) : e[r] = t, e;
|
|
1368
|
-
}
|
|
1369
1669
|
var OutOfSyncError = class extends Error {
|
|
1370
1670
|
constructor(message, state) {
|
|
1371
1671
|
super(message), _defineProperty(
|
|
@@ -4228,6 +4528,23 @@ function resolveProjection(...args) {
|
|
|
4228
4528
|
*/
|
|
4229
4529
|
const _resolveProjection = bindActionByResourceAndPerspective(projectionStore, ({ instance }, options) => firstValueFrom(getProjectionState(instance, options).observable.pipe(filter((state) => !!state?.data))));
|
|
4230
4530
|
/**
|
|
4531
|
+
* Preserves a projection string's literal type and returns the string unchanged.
|
|
4532
|
+
* Use this import when migrating an app with existing experimental Typegen output.
|
|
4533
|
+
*
|
|
4534
|
+
* Current Typegen does not scan this helper. New or changed projections need an
|
|
4535
|
+
* explicit result type until projection generation is supported.
|
|
4536
|
+
*
|
|
4537
|
+
* @example
|
|
4538
|
+
* ```ts
|
|
4539
|
+
* const preview = defineProjection('{title, "author": author->name}')
|
|
4540
|
+
* ```
|
|
4541
|
+
*
|
|
4542
|
+
* @beta
|
|
4543
|
+
*/
|
|
4544
|
+
function defineProjection(projection) {
|
|
4545
|
+
return projection;
|
|
4546
|
+
}
|
|
4547
|
+
/**
|
|
4231
4548
|
* @public
|
|
4232
4549
|
* Extracts the project ID from a CorsOriginError message.
|
|
4233
4550
|
* @param error - The error to extract the project ID from.
|
|
@@ -4265,6 +4582,6 @@ function isImportError(error) {
|
|
|
4265
4582
|
let message = error.message || "";
|
|
4266
4583
|
return /Loading chunk [\w-]+ failed/i.test(message) || /Failed to fetch dynamically imported module/i.test(message) || /error loading dynamically imported module/i.test(message) || /Import(?:ing)? a module script failed/i.test(message) || /Unable to preload CSS/i.test(message);
|
|
4267
4584
|
}
|
|
4268
|
-
export { AuthStateType, CORE_SDK_VERSION, application, applications, applyDocumentActions, archiveRelease, checkPermissions, configureLogging, createComment, createDatasetHandle, createDocument, createDocumentHandle, createDocumentTypeHandle, createProjectHandle, createRelease, createSanityInstance, datasets, deleteApplication, deleteDocument, deleteRelease, discardDocument, editDocument, editRelease, favorites, getActiveReleasesState, getAllReleasesState, getAuthState, getClient, getClientState, getCommentThreadsState, getCommentsState, getCorsErrorProjectId, getCurrentUserState, getDashboardOrganizationId, getDocumentPresence, getDocumentState, getDocumentSyncStatus, getIndexForKey, getIsInDashboardState, getLoginUrlState, getPathDepth, getPermissionsState, getPerspectiveState, getPresence, getProjectionState, getQueryState, getReleaseDocumentId, getTokenState, getUserState, getUsersState, handleAuthCallback, installation, installations, isCanvasResource, isDatasetResource, isImportError, isMediaLibraryResource, joinPaths, jsonMatch, loadMoreUsers, logout, observeOrganizationVerificationState, organization, organizations, project, projects, publishDocument, publishRelease, removeComment, replyToComment, reportPresence, resolveCommentThreads, resolveComments, resolveDocument, resolvePermissions, resolveProjection, resolveQuery, resolveUser, resolveUsers, scheduleRelease, setAuthToken, setCommentStatus, setFavorite, slicePath, stringifyPath, subscribeDocumentEvents, unarchiveRelease, unpublishDocument, unscheduleRelease, updateApplication, updateComment };
|
|
4585
|
+
export { AuthStateType, CORE_SDK_VERSION, application, applications, applyDocumentActions, archiveRelease, checkPermissions, configureLogging, createComment, createDatasetHandle, createDocument, createDocumentHandle, createDocumentTypeHandle, createProjectHandle, createRelease, createSanityInstance, datasets, defineProjection, deleteApplication, deleteDocument, deleteRelease, discardDocument, editDocument, editRelease, favorites, getActiveReleasesState, getAllReleasesState, getAuthState, getClient, getClientState, getCommentThreadsState, getCommentsState, getCorsErrorProjectId, getCurrentUserState, getDashboardOrganizationId, getDocumentPresence, getDocumentState, getDocumentSyncStatus, getIndexForKey, getIsInDashboardState, getLoginUrlState, getOAuthTokensState, getPathDepth, getPermissionsState, getPerspectiveState, getPresence, getProjectionState, getQueryState, getReleaseDocumentId, getTokenState, getUserState, getUsersState, handleAuthCallback, handleOAuthCallback, installation, installations, isCanvasResource, isDatasetResource, isImportError, isMediaLibraryResource, joinPaths, jsonMatch, loadMoreUsers, logout, observeOrganizationVerificationState, organization, organizations, patchOperations, processMutations, project, projects, publishDocument, publishRelease, refreshOAuthTokens, removeComment, replyToComment, reportPresence, resolveCommentThreads, resolveComments, resolveDocument, resolvePermissions, resolveProjection, resolveQuery, resolveUser, resolveUsers, revokeOAuthTokens, scheduleRelease, setAuthToken, setCommentStatus, setFavorite, slicePath, startOAuthAuthorization, stringifyPath, subscribeDocumentEvents, unarchiveRelease, unpublishDocument, unscheduleRelease, updateApplication, updateComment };
|
|
4269
4586
|
|
|
4270
4587
|
//# sourceMappingURL=index.js.map
|