@sanity/sdk 3.0.0 → 3.2.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 (132) hide show
  1. package/dist/_exports/_internal.d.ts +72 -19
  2. package/dist/_exports/_internal.d.ts.map +1 -1
  3. package/dist/_exports/_internal.js +130 -11
  4. package/dist/_exports/_internal.js.map +1 -1
  5. package/dist/_exports/agent.d.ts +7 -7
  6. package/dist/_exports/agent.d.ts.map +1 -1
  7. package/dist/_exports/agent.js +1 -1
  8. package/dist/_exports/comlink.d.ts +9 -9
  9. package/dist/_exports/comlink.d.ts.map +1 -1
  10. package/dist/_exports/comlink.js +2 -2
  11. package/dist/_exports/dashboard.d.ts +2 -0
  12. package/dist/_exports/dashboard.js +2 -0
  13. package/dist/{authStore-C0o1NOxW.d.ts → authStore-BO2vch3I.d.ts} +516 -63
  14. package/dist/authStore-BO2vch3I.d.ts.map +1 -0
  15. package/dist/{authStore-CtE9bLHW.js → clientStore-BI4D8HhJ.js} +390 -15
  16. package/dist/clientStore-BI4D8HhJ.js.map +1 -0
  17. package/dist/{createSanityInstance-DUI1bGIg.d.ts → createSanityInstance-BQIqyGLX.d.ts} +17 -2
  18. package/dist/createSanityInstance-BQIqyGLX.d.ts.map +1 -0
  19. package/dist/{createStateSourceAction-BOKtqFNm.js → createStateSourceAction-DdAhEfnZ.js} +7 -3
  20. package/dist/createStateSourceAction-DdAhEfnZ.js.map +1 -0
  21. package/dist/defineProperty-Bho9Ngsi.js +31 -0
  22. package/dist/{getNodeState-CR3__B2D.js → getNodeState-B4nG0sjm.js} +2 -2
  23. package/dist/{getNodeState-CR3__B2D.js.map → getNodeState-B4nG0sjm.js.map} +1 -1
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.js +370 -90
  26. package/dist/index.js.map +1 -1
  27. package/dist/topicStore-Ct7IFNtY.js +565 -0
  28. package/dist/topicStore-Ct7IFNtY.js.map +1 -0
  29. package/dist/topicStore-D0WqhQGF.d.ts +435 -0
  30. package/dist/topicStore-D0WqhQGF.d.ts.map +1 -0
  31. package/dist/{queryStore-C7yMHPUI.js → version-BcRmCiYi.js} +48 -16
  32. package/dist/version-BcRmCiYi.js.map +1 -0
  33. package/package.json +19 -18
  34. package/src/_exports/_internal.ts +13 -0
  35. package/src/_exports/dashboard.ts +36 -0
  36. package/src/_exports/index.ts +17 -1
  37. package/src/access/checkPermissions.test.ts +1 -1
  38. package/src/access/checkPermissions.ts +1 -1
  39. package/src/applications/applications.test-d.ts +28 -4
  40. package/src/applications/applications.test.ts +4 -4
  41. package/src/applications/applications.ts +43 -18
  42. package/src/auth/authMode.test.ts +24 -0
  43. package/src/auth/authMode.ts +8 -3
  44. package/src/auth/authStore.ts +12 -0
  45. package/src/auth/authStrategy.ts +2 -0
  46. package/src/auth/handleAuthCallback.test.ts +2 -2
  47. package/src/auth/handleAuthCallback.ts +1 -1
  48. package/src/auth/logout.test.ts +46 -1
  49. package/src/auth/logout.ts +2 -1
  50. package/src/auth/oauth/oauthActions.test.ts +457 -0
  51. package/src/auth/oauth/oauthActions.ts +389 -0
  52. package/src/auth/oauth/oauthAuth.test.ts +206 -0
  53. package/src/auth/oauth/oauthAuth.ts +168 -0
  54. package/src/auth/oauth/pkce.test.ts +44 -0
  55. package/src/auth/oauth/pkce.ts +60 -0
  56. package/src/auth/oauth/types.ts +12 -0
  57. package/src/auth/refreshStampedToken.ts +1 -1
  58. package/src/auth/studioModeAuth.test.ts +1 -1
  59. package/src/auth/studioModeAuth.ts +1 -1
  60. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +2 -2
  61. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +1 -1
  62. package/src/comments/addonDatasetStore.test.ts +2 -2
  63. package/src/comments/addonDatasetStore.ts +2 -2
  64. package/src/comments/commentActions.ts +6 -5
  65. package/src/comments/commentsStore.ts +4 -4
  66. package/src/config/authConfig.ts +18 -0
  67. package/src/dashboard/messageBus/__fixtures__/test-topics.ts +18 -0
  68. package/src/dashboard/messageBus/bus.test.ts +1033 -0
  69. package/src/dashboard/messageBus/bus.ts +1246 -0
  70. package/src/dashboard/messageBus/store.test.ts +142 -0
  71. package/src/dashboard/messageBus/store.ts +74 -0
  72. package/src/dashboard/messageBus/topicStore.test.ts +172 -0
  73. package/src/dashboard/messageBus/topicStore.ts +184 -0
  74. package/src/dashboard/messageBus/topics.test.ts +71 -0
  75. package/src/dashboard/messageBus/topics.ts +260 -0
  76. package/src/document/actions.ts +3 -3
  77. package/src/document/applyDocumentActions.ts +5 -9
  78. package/src/document/documentStore.concurrency.test.ts +2 -1
  79. package/src/document/documentStore.test.ts +19 -18
  80. package/src/document/documentStore.ts +17 -17
  81. package/src/document/permissions.ts +2 -1
  82. package/src/document/processActions.test.ts +2 -1
  83. package/src/document/processMutations.ts +61 -24
  84. package/src/document/reducers.ts +2 -2
  85. package/src/installations/installations.test-d.ts +3 -3
  86. package/src/installations/installations.test.ts +2 -2
  87. package/src/installations/installations.ts +4 -21
  88. package/src/organization/organization.test.ts +2 -2
  89. package/src/organization/organization.ts +1 -1
  90. package/src/organizations/organizations.test.ts +2 -2
  91. package/src/organizations/organizations.ts +1 -1
  92. package/src/presence/presenceStore.test.ts +15 -22
  93. package/src/presence/presenceStore.ts +3 -2
  94. package/src/project/project.test.ts +3 -3
  95. package/src/project/project.ts +1 -1
  96. package/src/projection/getProjectionState.test.ts +6 -4
  97. package/src/projection/getProjectionState.ts +4 -4
  98. package/src/projection/resolveProjection.ts +2 -2
  99. package/src/projects/projects.test.ts +2 -2
  100. package/src/projects/projects.ts +1 -1
  101. package/src/query/queryStore.ts +6 -6
  102. package/src/store/createSanityInstance.test.ts +1 -1
  103. package/src/store/createSanityInstance.ts +4 -4
  104. package/src/store/createStoreInstance.test.ts +0 -5
  105. package/src/telemetry/telemetryManager.test.ts +1 -1
  106. package/src/telemetry/telemetryManager.ts +2 -2
  107. package/src/typegen/defineProjection.test.ts +17 -0
  108. package/src/typegen/defineProjection.ts +19 -0
  109. package/src/typegen/groqCompat.ts +122 -0
  110. package/src/typegen/resolve.ts +37 -0
  111. package/src/users/usersStore.test.ts +1 -1
  112. package/src/users/usersStore.ts +5 -5
  113. package/src/utils/createGroqSearchFilter.test.ts +8 -0
  114. package/src/utils/createGroqSearchFilter.ts +6 -3
  115. package/src/utils/ids.test.ts +40 -15
  116. package/src/utils/ids.ts +47 -2
  117. package/dist/authStore-C0o1NOxW.d.ts.map +0 -1
  118. package/dist/authStore-CtE9bLHW.js.map +0 -1
  119. package/dist/clientStore-BtE2-q2i.js +0 -90
  120. package/dist/clientStore-BtE2-q2i.js.map +0 -1
  121. package/dist/createSanityInstance-DUI1bGIg.d.ts.map +0 -1
  122. package/dist/createStateSourceAction-BOKtqFNm.js.map +0 -1
  123. package/dist/getEnv-D5RGkQLu.js +0 -8
  124. package/dist/getEnv-D5RGkQLu.js.map +0 -1
  125. package/dist/logger-DAtJb5RQ.js +0 -173
  126. package/dist/logger-DAtJb5RQ.js.map +0 -1
  127. package/dist/queryStore-C7yMHPUI.js.map +0 -1
  128. package/dist/rolldown-runtime-vyAXikos.js +0 -9
  129. package/dist/telemetryManager-UIEiMtC0.js +0 -127
  130. package/dist/telemetryManager-UIEiMtC0.js.map +0 -1
  131. package/dist/version-D8RP_Q59.js +0 -9
  132. package/dist/version-D8RP_Q59.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,12 +1,10 @@
1
- import { a as defineStore, c as isDatasetResource, i as bindActionGlobally, l as isMediaLibraryResource, n as bindActionByResource, o as isReleasePerspective, r as bindActionByResourceAndPerspective, s as isCanvasResource, t as createStateSourceAction } from "./createStateSourceAction-BOKtqFNm.js";
2
- import { A as getCommentsKey, B as getAddonDatasetState, D as addComment, E as toCommentsKeyParts, F as setCommentCreateError, G as insecureRandomId, H as COMMENTS_API_VERSION, I as setPendingTransaction, L as normalizeComment, M as removeCommentById, N as restoreComments, O as applyCommentUpdate, P as rollbackCommentUpdate, R as toCommentFieldPath, S as getCommentsState, T as resolveComments, U as defineFetcher, V as provisionAddonDataset, W as defineMutation, _ as PROJECT_API_VERSION, a as getPerspectiveState, b as getCommentThreadsState, c as addSubscription, d as initializeRequest, f as parseUsersKey, g as updateLastLoadMoreRequest, h as setUsersError, i as resolveQuery, j as receiveComment, k as clearPendingTransaction, l as cancelRequest, m as setUsersData, n as getQueryState, o as getActiveReleasesState, p as removeSubscription, s as getAllReleasesState, u as getUsersKey, v as USERS_STATE_CLEAR_DELAY, w as resolveCommentThreads, y as commentsStore, z as assertDatasetResource } from "./queryStore-C7yMHPUI.js";
3
- import { n as createLogger, t as configureLogging$1 } from "./logger-DAtJb5RQ.js";
4
- import { C as REQUEST_TAG_PREFIX, S as DEFAULT_API_VERSION, _ as getDefaultLocation, a as getCurrentUserState, b as AuthStateType, c as getLoginUrlState, d as createLoggedInAuthState, f as getAuthCode, i as getAuthState, l as getTokenState, o as getDashboardOrganizationId, p as getCleanedUrl, s as getIsInDashboardState, t as authStore, u as setAuthToken, v as getTokenFromLocation, w as getAuthLogger } from "./authStore-CtE9bLHW.js";
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-BI4D8HhJ.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-BcRmCiYi.js";
5
4
  import { r as omitProperty, t as isDeepEqual } from "./object-CxXlSVg7.js";
6
- import { n as getClient, r as getClientState } from "./clientStore-BtE2-q2i.js";
7
5
  import { n as setCleanupTimeout } from "./setCleanupTimeout-BiM6q1KF.js";
8
- import { t as getNodeState } from "./getNodeState-CR3__B2D.js";
9
- import { t as CORE_SDK_VERSION } from "./version-D8RP_Q59.js";
6
+ import { t as _defineProperty } from "./defineProperty-Bho9Ngsi.js";
7
+ import { t as getNodeState } from "./getNodeState-B4nG0sjm.js";
10
8
  import { ClientError, CorsOriginError } from "@sanity/client";
11
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";
12
10
  import { isKeySegment, isKeyedObject } from "@sanity/types";
@@ -14,7 +12,6 @@ import { DocumentId, getDraftId, getPublishedId, getVersionId, isDraftId, isPubl
14
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";
15
13
  import { SanityEncoder } from "@sanity/mutate";
16
14
  import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
17
- import "groq";
18
15
  import { evaluateSync, parse } from "groq-js";
19
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";
20
17
  import { applyPatches, parsePatch } from "@sanity/diff-match-patch";
@@ -30,7 +27,7 @@ import { fromUrl } from "@sanity/bifur-client";
30
27
  * @public
31
28
  */
32
29
  function createSanityInstance(config = {}) {
33
- let instanceId = crypto.randomUUID(), disposeListeners = /* @__PURE__ */ new Map(), disposed = { current: !1 }, instanceContext = {
30
+ let instanceId = randomId(16), disposeListeners = /* @__PURE__ */ new Map(), disposed = { current: !1 }, instanceContext = {
34
31
  instanceId,
35
32
  projectId: config.projectId,
36
33
  dataset: config.dataset
@@ -63,7 +60,7 @@ function createSanityInstance(config = {}) {
63
60
  }), disposed.current = !0, disposeListeners.forEach((listener) => listener()), disposeListeners.clear(), logger.info("Instance disposed");
64
61
  },
65
62
  onDispose: (cb) => {
66
- let listenerId = insecureRandomId();
63
+ let listenerId = randomId(16);
67
64
  return disposeListeners.set(listenerId, cb), () => {
68
65
  disposeListeners.delete(listenerId);
69
66
  };
@@ -77,7 +74,7 @@ const checkPermissions = defineFetcher({
77
74
  apiVersion: "v2025-07-11",
78
75
  scope: "global"
79
76
  }).observable.pipe(switchMap((client) => client.observable.request({
80
- uri: `/access/${resourceType}/${resourceId}/user-permissions/me/check`,
77
+ url: `/access/${resourceType}/${resourceId}/user-permissions/me/check`,
81
78
  query: { permissions },
82
79
  tag: "access.check"
83
80
  }).pipe(map((response) => response.data)))),
@@ -121,7 +118,7 @@ const applications = defineFetcher({
121
118
  apiVersion: "vX",
122
119
  scope: "global"
123
120
  }).observable.pipe(switchMap((client) => client.observable.request({
124
- uri: "/applications",
121
+ url: "/applications",
125
122
  query: buildQuery({
126
123
  organizationId: options.organizationId,
127
124
  type: options.type,
@@ -145,7 +142,7 @@ const applications = defineFetcher({
145
142
  apiVersion: "vX",
146
143
  scope: "global"
147
144
  }).observable.pipe(switchMap((client) => client.observable.request({
148
- uri: `/applications/${applicationId}`,
145
+ url: `/applications/${applicationId}`,
149
146
  query: buildQuery({ include: serializeInclude$1(options?.include) }),
150
147
  tag: "applications.get"
151
148
  }))),
@@ -159,7 +156,7 @@ const applications = defineFetcher({
159
156
  apiVersion: "vX",
160
157
  scope: "global"
161
158
  }).observable.pipe(switchMap((client) => client.observable.request({
162
- uri: `/applications/${applicationId}`,
159
+ url: `/applications/${applicationId}`,
163
160
  method: "PATCH",
164
161
  body,
165
162
  tag: "applications.update"
@@ -177,7 +174,7 @@ const applications = defineFetcher({
177
174
  apiVersion: "vX",
178
175
  scope: "global"
179
176
  }).observable.pipe(switchMap((client) => client.observable.request({
180
- uri: `/applications/${applicationId}`,
177
+ url: `/applications/${applicationId}`,
181
178
  method: "DELETE",
182
179
  tag: "applications.delete"
183
180
  }))),
@@ -222,7 +219,7 @@ const projects = defineFetcher({
222
219
  apiVersion: "v2025-02-19",
223
220
  scope: "global"
224
221
  }).observable.pipe(switchMap((client) => client.observable.request({
225
- uri: "/projects",
222
+ url: "/projects",
226
223
  query: buildQuery(normalizeProjectsOptions(options)),
227
224
  tag: "projects.list"
228
225
  }))),
@@ -265,7 +262,7 @@ const project = defineFetcher({
265
262
  apiVersion: "v2025-02-19",
266
263
  scope: "global"
267
264
  }).observable.pipe(switchMap((client) => client.observable.request({
268
- uri: `/projects/${resolveProjectId$1(instance, options)}`,
265
+ url: `/projects/${resolveProjectId$1(instance, options)}`,
269
266
  query: buildQuery(normalizeProjectOptions(options)),
270
267
  tag: "projects.get"
271
268
  }))),
@@ -345,7 +342,7 @@ const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instanc
345
342
  logger.debug("Fetching token from auth endpoint");
346
343
  let { token } = await client.request({
347
344
  method: "GET",
348
- uri: "/auth/fetch",
345
+ url: "/auth/fetch",
349
346
  query: { sid: authCode },
350
347
  tag: "fetch-token"
351
348
  });
@@ -383,7 +380,7 @@ const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instanc
383
380
  useCdn: !1
384
381
  });
385
382
  logger.debug("Calling logout endpoint"), await client.request({
386
- uri: "/auth/logout",
383
+ url: "/auth/logout",
387
384
  method: "POST",
388
385
  tag: "logout"
389
386
  });
@@ -391,12 +388,273 @@ const handleAuthCallback = bindActionGlobally(authStore, async ({ state, instanc
391
388
  } catch (error) {
392
389
  throw logger.error("Logout request failed", { error }), error;
393
390
  } finally {
394
- logger.info("User logged out, clearing stored tokens"), state.set("logoutSuccess", { authState: {
391
+ logger.info("User logged out, clearing stored tokens"), state.set("logoutSuccess", {
392
+ authState: {
393
+ type: "logged-out",
394
+ isDestroyingSession: !1
395
+ },
396
+ oauthTokens: void 0
397
+ }), storageArea?.removeItem(storageKey), storageArea?.removeItem(`${storageKey}_last_refresh`);
398
+ }
399
+ });
400
+ /**
401
+ * Encodes a byte array as a base64url string (RFC 4648 §5) with no padding,
402
+ * as required for PKCE `code_verifier`/`code_challenge` values.
403
+ *
404
+ * @internal
405
+ */
406
+ function base64UrlEncode(bytes) {
407
+ let binary = "";
408
+ for (let byte of bytes) binary += String.fromCharCode(byte);
409
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
410
+ }
411
+ function randomString() {
412
+ let bytes = /* @__PURE__ */ new Uint8Array(32);
413
+ return crypto.getRandomValues(bytes), base64UrlEncode(bytes);
414
+ }
415
+ /**
416
+ * Generates a PKCE `code_verifier`
417
+ *
418
+ * @internal
419
+ */
420
+ function generateCodeVerifier() {
421
+ return randomString();
422
+ }
423
+ /**
424
+ * Generates the `state` parameter used to defend against CSRF on the OAuth
425
+ * callback.
426
+ *
427
+ * @internal
428
+ */
429
+ function generateState() {
430
+ return randomString();
431
+ }
432
+ /**
433
+ * Derives the PKCE `code_challenge` from a `code_verifier` using
434
+ * `base64url(sha256(verifier))` (the `S256` method).
435
+ *
436
+ * @internal
437
+ */
438
+ async function generateCodeChallenge(codeVerifier) {
439
+ let data = new TextEncoder().encode(codeVerifier), digest = await crypto.subtle.digest("SHA-256", data);
440
+ return base64UrlEncode(new Uint8Array(digest));
441
+ }
442
+ /** sessionStorage key for the PKCE `code_verifier`. */
443
+ const OAUTH_VERIFIER_KEY = "__sanity_oauth_verifier", OAUTH_STATE_KEY = "__sanity_oauth_state";
444
+ /** Builds the RFC 8707 resource indicator for an organisation. */
445
+ function getResourceIndicator(organizationId) {
446
+ return `urn:io.sanity:organization:${organizationId}`;
447
+ }
448
+ /**
449
+ * Serialises tokens for storage, converting `expiresAt` to an ISO string.
450
+ *
451
+ * @internal
452
+ */
453
+ function serializeTokens(tokens) {
454
+ return JSON.stringify({
455
+ accessToken: tokens.accessToken,
456
+ tokenType: tokens.tokenType,
457
+ expiresIn: tokens.expiresIn,
458
+ expiresAt: tokens.expiresAt.toISOString(),
459
+ ...tokens.refreshToken !== void 0 && { refreshToken: tokens.refreshToken }
460
+ });
461
+ }
462
+ /**
463
+ * Reads the store options, throwing when the instance was not configured for
464
+ * OAuth.
465
+ */
466
+ function getOAuthOptions(state) {
467
+ let { options } = state;
468
+ if (!options.oauth) throw Error("OAuth is not configured on this instance (missing `auth.oauth`).");
469
+ return options;
470
+ }
471
+ /** Creates a client for the (unauthenticated) public OAuth token/revoke calls. */
472
+ function createOAuthClient(options) {
473
+ return options.clientFactory({
474
+ apiVersion: "v1",
475
+ requestTagPrefix: REQUEST_TAG_PREFIX,
476
+ useProjectHostname: !1,
477
+ useCdn: !1,
478
+ ...options.apiHost && { apiHost: options.apiHost }
479
+ });
480
+ }
481
+ /** Converts a token endpoint response to the {@link OAuthTokens} shape. */
482
+ function toOAuthTokens(response) {
483
+ return {
484
+ accessToken: response.access_token,
485
+ tokenType: "bearer",
486
+ expiresIn: response.expires_in,
487
+ expiresAt: new Date(Date.now() + response.expires_in * 1e3),
488
+ ...response.refresh_token !== void 0 && { refreshToken: response.refresh_token }
489
+ };
490
+ }
491
+ /**
492
+ * Starts the OAuth authorization-code + PKCE flow: generates a `code_verifier`,
493
+ * `code_challenge` and `state`, persists the verifier and state to
494
+ * `sessionStorage`, then navigates the browser to the authorize endpoint.
495
+ *
496
+ * @public
497
+ */
498
+ const startOAuthAuthorization = bindActionGlobally(authStore, async ({ state, instance }) => {
499
+ let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), codeVerifier = generateCodeVerifier(), oauthState = generateState(), codeChallenge = await generateCodeChallenge(codeVerifier), session = typeof sessionStorage < "u" ? sessionStorage : void 0;
500
+ session?.setItem(OAUTH_VERIFIER_KEY, codeVerifier), session?.setItem(OAUTH_STATE_KEY, oauthState);
501
+ let authorizeUrl = new URL("/v1/auth/oauth/authorize", options.apiHost ?? "https://api.sanity.io");
502
+ 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());
503
+ }), handleOAuthCallback = bindActionGlobally(authStore, async ({ state, instance }, locationHref = getDefaultLocation()) => {
504
+ let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), { authState } = state.get();
505
+ if (authState.type === "logging-in" && authState.isExchangingToken) return logger.debug("Skipping OAuth callback - token exchange already in progress"), !1;
506
+ 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, cleanedUrlObj = new URL(locationHref, DEFAULT_BASE);
507
+ for (let param of [
508
+ "code",
509
+ "state",
510
+ "error",
511
+ "error_description"
512
+ ]) cleanedUrlObj.searchParams.delete(param);
513
+ let cleanedUrl = cleanedUrlObj.toString();
514
+ if (error) return logger.warn("OAuth callback returned an error", {
515
+ error,
516
+ errorDescription
517
+ }), clearOAuthArtifacts(session), state.set("oauthCallbackError", { authState: {
518
+ type: "error",
519
+ error: Error(errorDescription ? `${error}: ${errorDescription}` : error)
520
+ } }), cleanedUrl;
521
+ if (!code) return logger.debug("No OAuth code found in callback URL"), !1;
522
+ let storedState = session?.getItem("__sanity_oauth_state") ?? null;
523
+ if (!returnedState || !storedState || returnedState !== storedState) return logger.error("OAuth state mismatch — rejecting callback"), clearOAuthArtifacts(session), state.set("oauthStateMismatch", { authState: {
524
+ type: "error",
525
+ error: /* @__PURE__ */ Error("OAuth state mismatch")
526
+ } }), cleanedUrl;
527
+ let codeVerifier = session?.getItem("__sanity_oauth_verifier") ?? null;
528
+ if (!codeVerifier) return logger.error("OAuth code verifier missing — cannot exchange code"), clearOAuthArtifacts(session), state.set("oauthVerifierMissing", { authState: {
529
+ type: "error",
530
+ error: /* @__PURE__ */ Error("OAuth code verifier missing")
531
+ } }), cleanedUrl;
532
+ logger.info("Exchanging OAuth code for tokens"), state.set("oauthExchange", { authState: {
533
+ type: "logging-in",
534
+ isExchangingToken: !0
535
+ } });
536
+ try {
537
+ let client = createOAuthClient(options), params = new URLSearchParams({
538
+ grant_type: "authorization_code",
539
+ code,
540
+ code_verifier: codeVerifier,
541
+ redirect_uri: options.oauth.redirectUri,
542
+ client_id: options.oauth.clientId,
543
+ resource: getResourceIndicator(options.oauth.organizationId)
544
+ }), tokens = toOAuthTokens(await client.request({
545
+ method: "POST",
546
+ url: "/auth/oauth/token",
547
+ headers: { "content-type": "application/x-www-form-urlencoded" },
548
+ body: params.toString(),
549
+ tag: "oauth.token"
550
+ }));
551
+ return options.storageArea?.setItem(options.storageKey, serializeTokens(tokens)), clearOAuthArtifacts(session), logger.info("OAuth tokens obtained, user logged in"), state.set("oauthLoggedIn", {
552
+ authState: createLoggedInAuthState(tokens.accessToken, null),
553
+ oauthTokens: tokens
554
+ }), cleanedUrl;
555
+ } catch (exchangeError) {
556
+ return logger.error("Failed to exchange OAuth code for tokens", { error: exchangeError }), clearOAuthArtifacts(session), state.set("oauthExchangeError", { authState: {
557
+ type: "error",
558
+ error: exchangeError
559
+ } }), cleanedUrl;
560
+ }
561
+ });
562
+ /**
563
+ * A refresh failure is unrecoverable only when the token endpoint rejects the
564
+ * refresh token itself (a 4xx). Rate-limit (429) and request-timeout (408)
565
+ * responses, 5xx errors and network failures are transient, so the session is
566
+ * kept intact for the caller to retry rather than forcing a logout.
567
+ */
568
+ function isUnrecoverableRefreshError(error) {
569
+ return error instanceof ClientError && error.statusCode !== 408 && error.statusCode !== 429;
570
+ }
571
+ let refreshInFlight = null;
572
+ /**
573
+ * Refreshes the OAuth tokens using the `refresh_token` grant. Concurrent
574
+ * callers share a single in-flight request. An unrecoverable failure (a 4xx
575
+ * rejecting the refresh token) clears the tokens and transitions to
576
+ * `LOGGED_OUT`; transient failures (network, 5xx, rate limits) leave the
577
+ * session intact and rethrow so the caller can retry.
578
+ *
579
+ * @public
580
+ */
581
+ const refreshOAuthTokens = bindActionGlobally(authStore, (context) => refreshInFlight || (refreshInFlight = doRefreshOAuthTokens(context).finally(() => {
582
+ refreshInFlight = null;
583
+ }), refreshInFlight));
584
+ async function doRefreshOAuthTokens({ state, instance }) {
585
+ let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), current = state.get().oauthTokens;
586
+ if (!current?.refreshToken) return logger.warn("No refresh token available — logging out"), options.storageArea?.removeItem(options.storageKey), state.set("oauthRefreshNoToken", {
587
+ authState: {
395
588
  type: "logged-out",
396
589
  isDestroyingSession: !1
397
- } }), storageArea?.removeItem(storageKey), storageArea?.removeItem(`${storageKey}_last_refresh`);
590
+ },
591
+ oauthTokens: void 0
592
+ }), null;
593
+ try {
594
+ let client = createOAuthClient(options), params = new URLSearchParams({
595
+ grant_type: "refresh_token",
596
+ refresh_token: current.refreshToken,
597
+ client_id: options.oauth.clientId,
598
+ resource: getResourceIndicator(options.oauth.organizationId)
599
+ }), tokens = toOAuthTokens(await client.request({
600
+ method: "POST",
601
+ url: "/auth/oauth/token",
602
+ headers: { "content-type": "application/x-www-form-urlencoded" },
603
+ body: params.toString(),
604
+ tag: "oauth.refresh"
605
+ }));
606
+ return tokens.refreshToken ||= current.refreshToken, options.storageArea?.setItem(options.storageKey, serializeTokens(tokens)), logger.info("OAuth tokens refreshed"), state.set("oauthRefreshed", {
607
+ authState: createLoggedInAuthState(tokens.accessToken, null),
608
+ oauthTokens: tokens
609
+ }), tokens;
610
+ } catch (error) {
611
+ throw isUnrecoverableRefreshError(error) ? (logger.error("OAuth token refresh failed — logging out", { error }), options.storageArea?.removeItem(options.storageKey), state.set("oauthRefreshFailed", {
612
+ authState: {
613
+ type: "logged-out",
614
+ isDestroyingSession: !1
615
+ },
616
+ oauthTokens: void 0
617
+ }), error) : (logger.warn("OAuth token refresh failed — keeping session for retry", { error }), error);
398
618
  }
399
- });
619
+ }
620
+ /**
621
+ * Revokes the OAuth tokens then clears local storage and transitions to `LOGGED_OUT`.
622
+ * Local state is cleared even if the revoke request fails.
623
+ *
624
+ * @public
625
+ */
626
+ const revokeOAuthTokens = bindActionGlobally(authStore, async ({ state, instance }) => {
627
+ let logger = getAuthLogger(instance), options = getOAuthOptions(state.get()), current = state.get().oauthTokens, token = current?.refreshToken ?? current?.accessToken;
628
+ try {
629
+ if (token) {
630
+ let client = createOAuthClient(options), params = new URLSearchParams({
631
+ token,
632
+ client_id: options.oauth.clientId
633
+ });
634
+ await client.request({
635
+ method: "POST",
636
+ url: "/auth/oauth/revoke",
637
+ headers: { "content-type": "application/x-www-form-urlencoded" },
638
+ body: params.toString(),
639
+ tag: "oauth.revoke"
640
+ }), logger.info("OAuth tokens revoked");
641
+ }
642
+ } catch (error) {
643
+ logger.warn("OAuth revoke request failed — clearing local state anyway", { error });
644
+ } finally {
645
+ options.storageArea?.removeItem(options.storageKey), state.set("oauthRevoked", {
646
+ authState: {
647
+ type: "logged-out",
648
+ isDestroyingSession: !1
649
+ },
650
+ oauthTokens: void 0
651
+ });
652
+ }
653
+ }), getOAuthTokensState = bindActionGlobally(authStore, createStateSourceAction(({ state }) => state.oauthTokens ?? null));
654
+ /** Removes the transient PKCE artifacts from session storage. */
655
+ function clearOAuthArtifacts(session) {
656
+ session?.removeItem(OAUTH_VERIFIER_KEY), session?.removeItem(OAUTH_STATE_KEY);
657
+ }
400
658
  function weaken(node) {
401
659
  return Array.isArray(node) ? node.map(weaken) : node && typeof node == "object" ? "_ref" in node ? {
402
660
  ...node,
@@ -517,7 +775,7 @@ const createComment = bindActionByResource(commentsStore, async (context, option
517
775
  let { instance, key } = context, perspective = options.perspective ?? instance.config.perspective, fieldPath = requireFieldPath(options.fieldPath);
518
776
  return postComment(context, options, buildCommentPayload({
519
777
  authorId: requireCurrentUserId(instance),
520
- commentId: options.commentId ?? crypto.randomUUID(),
778
+ commentId: options.commentId ?? randomUuid(),
521
779
  contentSnapshot: options.contentSnapshot,
522
780
  context: options.context,
523
781
  documentRevisionId: options.documentRevisionId,
@@ -527,7 +785,7 @@ const createComment = bindActionByResource(commentsStore, async (context, option
527
785
  fieldPath,
528
786
  selection: options.selection,
529
787
  status: options.status ?? "open",
530
- threadId: options.threadId ?? crypto.randomUUID(),
788
+ threadId: options.threadId ?? randomUuid(),
531
789
  ...isReleasePerspective(perspective) ? { documentVersionId: perspective.releaseName } : {}
532
790
  }));
533
791
  }), replyToComment = bindActionByResource(commentsStore, async (context, options) => {
@@ -538,7 +796,7 @@ const createComment = bindActionByResource(commentsStore, async (context, option
538
796
  let perspective = options.perspective ?? instance.config.perspective;
539
797
  return postComment(context, options, buildCommentPayload({
540
798
  authorId: requireCurrentUserId(instance),
541
- commentId: options.commentId ?? crypto.randomUUID(),
799
+ commentId: options.commentId ?? randomUuid(),
542
800
  handle: options,
543
801
  message: options.message,
544
802
  parentCommentId: parent?.parentCommentId ?? options.parentCommentId,
@@ -549,7 +807,7 @@ const createComment = bindActionByResource(commentsStore, async (context, option
549
807
  ...isReleasePerspective(perspective) ? { documentVersionId: perspective.releaseName } : {}
550
808
  }));
551
809
  }), updateComment = bindActionByResource(commentsStore, async ({ state, instance, key }, { commentId, message }) => {
552
- let lastEditedAt = (/* @__PURE__ */ new Date()).toISOString(), transactionId = crypto.randomUUID(), previous = findCommentById(state, commentId);
810
+ let lastEditedAt = (/* @__PURE__ */ new Date()).toISOString(), transactionId = randomUuid(), previous = findCommentById(state, commentId);
553
811
  previous && (state.set("setPendingTransaction", setPendingTransaction(commentId, transactionId)), state.set("updateComment", applyCommentUpdate(commentId, {
554
812
  message,
555
813
  lastEditedAt
@@ -564,7 +822,7 @@ const createComment = bindActionByResource(commentsStore, async (context, option
564
822
  throw previous && state.set("rollbackCommentUpdate", rollbackCommentUpdate(commentId, transactionId, previous)), error;
565
823
  }
566
824
  }), setCommentStatus = bindActionByResource(commentsStore, async ({ state, instance, key }, { commentId, status }) => {
567
- let transactionId = crypto.randomUUID(), previousComments = [];
825
+ let transactionId = randomUuid(), previousComments = [];
568
826
  for (let entry of Object.values(state.get().entries)) for (let candidate of Object.values(entry?.comments ?? {})) (candidate._id === commentId || candidate.parentCommentId === commentId) && (previousComments.push(candidate), state.set("setPendingTransaction", setPendingTransaction(candidate._id, transactionId)), state.set("updateCommentStatus", applyCommentUpdate(candidate._id, { status })));
569
827
  try {
570
828
  await (await getWritableClient(instance, key.resource, { createIfMissing: !1 })).mutate([{ patch: {
@@ -1232,7 +1490,29 @@ function unsetDeep(input, path) {
1232
1490
  let segment = _segment;
1233
1491
  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;
1234
1492
  }
1235
- const patchOperations = {
1493
+ /**
1494
+ * Exposes the patch operations used by {@link processMutations}.
1495
+ *
1496
+ * @remarks
1497
+ * Property order determines execution order in {@link processMutations}:
1498
+ * `ifRevisionID`, `set`, `setIfMissing`, `unset`, `inc`, `dec`, `insert`,
1499
+ * then `diffMatchPatch`. Reordering the properties changes mutation results.
1500
+ * Freezes the collection to prevent callers from replacing the functions
1501
+ * used by the SDK's mutation evaluator.
1502
+ *
1503
+ * @see https://www.sanity.io/docs/http-mutations#5b4db1396e56
1504
+ *
1505
+ * @example
1506
+ * ```ts
1507
+ * const document = {items: [{_key: 'a', title: 'Before'}]}
1508
+ * const updated = patchOperations.set<typeof document>(document, {
1509
+ * 'items[_key=="a"].title': 'After',
1510
+ * })
1511
+ * ```
1512
+ *
1513
+ * @internal
1514
+ */
1515
+ const patchOperations = Object.freeze({
1236
1516
  ifRevisionID,
1237
1517
  set,
1238
1518
  setIfMissing,
@@ -1241,7 +1521,7 @@ const patchOperations = {
1241
1521
  dec,
1242
1522
  insert,
1243
1523
  diffMatchPatch
1244
- };
1524
+ });
1245
1525
  /**
1246
1526
  * Implements ID generation:
1247
1527
  *
@@ -1258,7 +1538,7 @@ const patchOperations = {
1258
1538
  * [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)
1259
1539
  */
1260
1540
  function getId(id) {
1261
- return !id || typeof id != "string" ? crypto.randomUUID() : id.endsWith(".") ? `${id}${crypto.randomUUID()}` : id;
1541
+ return !id || typeof id != "string" ? randomUuid() : id.endsWith(".") ? `${id}${randomUuid()}` : id;
1262
1542
  }
1263
1543
  function getDocumentIds(selection) {
1264
1544
  if ("id" in selection) {
@@ -1269,18 +1549,31 @@ function getDocumentIds(selection) {
1269
1549
  return [];
1270
1550
  }
1271
1551
  /**
1272
- * Applies the given mutation to the given document set. Note, it is expected
1273
- * that all relevant documents that the mutations affect should be within the
1274
- * given `document` set. If a document does not exist, it should have the value
1275
- * `null`. If a document is deleted as a result of the mutations, it will still
1276
- * have its document ID present in the returns documents, but it will have a
1277
- * value of `null`.
1552
+ * Applies mutations to an in-memory set of Sanity documents.
1553
+ * Deleted documents remain in the returned set with a value of `null`.
1554
+ *
1555
+ * Include every document affected by `mutations` in `documents`, using `null`
1556
+ * for documents that do not exist.
1557
+ *
1558
+ * Returns `documents` itself when `mutations` is empty. Otherwise, returns a
1559
+ * shallow copy of that object. The result can reuse document objects and
1560
+ * nested objects from `documents`. Mutating a reused object through the
1561
+ * result also changes the input.
1278
1562
  *
1279
- * The given `transactionId` will be used as the resulting `_rev` for documents
1280
- * affected by the given set of mutations.
1563
+ * Sets `_rev` to `transactionId` on created, replaced, and patched documents.
1564
+ * Uses `timestamp` for timestamps unless the mutation preserves them.
1565
+ * Ignores `merge` patches; `inc` and `dec` skip values that are not numbers.
1566
+ * Generates document IDs when omitted or ending in `.`.
1567
+ * Patch operations can generate missing array keys.
1281
1568
  *
1282
- * If a `timestamp` is given, that will be used as for the relevant `_updatedAt`
1283
- * and `_createdAt` timestamps.
1569
+ * Does not persist documents or run Content Lake's server validation.
1570
+ *
1571
+ * @throws If a selection uses a query, a `create` targets an existing document,
1572
+ * or a `patch` targets a document that does not exist.
1573
+ * @throws If a patch operation fails, including a revision check failure or
1574
+ * `diffMatchPatch` applied to a value other than a string or `undefined`.
1575
+ *
1576
+ * @internal
1284
1577
  */
1285
1578
  function processMutations({ documents, mutations, transactionId, timestamp }) {
1286
1579
  if (!mutations.length) return documents;
@@ -1340,36 +1633,6 @@ function processMutations({ documents, mutations, transactionId, timestamp }) {
1340
1633
  }
1341
1634
  return dataset;
1342
1635
  }
1343
- function _typeof(o) {
1344
- "@babel/helpers - typeof";
1345
- return _typeof = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(o) {
1346
- return typeof o;
1347
- } : function(o) {
1348
- return o && typeof Symbol == "function" && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
1349
- }, _typeof(o);
1350
- }
1351
- function toPrimitive(t, r) {
1352
- if (_typeof(t) != "object" || !t) return t;
1353
- var e = t[Symbol.toPrimitive];
1354
- if (e !== void 0) {
1355
- var i = e.call(t, r || "default");
1356
- if (_typeof(i) != "object") return i;
1357
- throw TypeError("@@toPrimitive must return a primitive value.");
1358
- }
1359
- return (r === "string" ? String : Number)(t);
1360
- }
1361
- function toPropertyKey(t) {
1362
- var i = toPrimitive(t, "string");
1363
- return _typeof(i) == "symbol" ? i : i + "";
1364
- }
1365
- function _defineProperty(e, r, t) {
1366
- return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
1367
- value: t,
1368
- enumerable: !0,
1369
- configurable: !0,
1370
- writable: !0
1371
- }) : e[r] = t, e;
1372
- }
1373
1636
  var OutOfSyncError = class extends Error {
1374
1637
  constructor(message, state) {
1375
1638
  super(message), _defineProperty(
@@ -1790,7 +2053,7 @@ const _calculatePermissions = createSelector([
1790
2053
  try {
1791
2054
  processActions({
1792
2055
  actions,
1793
- transactionId: crypto.randomUUID(),
2056
+ transactionId: randomId(16),
1794
2057
  working: documents,
1795
2058
  base: documents,
1796
2059
  timestamp,
@@ -2835,7 +3098,7 @@ function evictOrphanedDocumentStates(documentStates) {
2835
3098
  return next;
2836
3099
  }
2837
3100
  function manageSubscriberIds({ state }, handles) {
2838
- let documentIds = getDocumentIdsFromHandleLikes(handles), subscriptionId = insecureRandomId();
3101
+ let documentIds = getDocumentIdsFromHandleLikes(handles), subscriptionId = randomId(16);
2839
3102
  return state.set("addSubscribers", (prev) => documentIds.reduce((acc, id) => addSubscriptionIdToDocument(acc, id, subscriptionId), prev)), () => {
2840
3103
  setCleanupTimeout(() => {
2841
3104
  state.set("removeSubscribers", (prev) => documentIds.reduce((acc, id) => removeSubscriptionIdFromDocument(acc, id, subscriptionId), prev));
@@ -3185,13 +3448,13 @@ const _resolveDocument = bindActionByResource(documentStore, ({ instance }, docH
3185
3448
  let clientOptions = {
3186
3449
  apiVersion: API_VERSION$4,
3187
3450
  resource
3188
- }, uri;
3189
- if (resource && isDatasetResource(resource)) uri = `/projects/${resource.projectId}/datasets/${resource.dataset}/acl`;
3190
- else if (resource && isMediaLibraryResource(resource)) uri = `/media-libraries/${resource.mediaLibraryId}/acl`;
3191
- else if (resource && isCanvasResource(resource)) uri = `/canvases/${resource.canvasId}/acl`;
3451
+ }, url;
3452
+ if (resource && isDatasetResource(resource)) url = `/projects/${resource.projectId}/datasets/${resource.dataset}/acl`;
3453
+ else if (resource && isMediaLibraryResource(resource)) url = `/media-libraries/${resource.mediaLibraryId}/acl`;
3454
+ else if (resource && isCanvasResource(resource)) url = `/canvases/${resource.canvasId}/acl`;
3192
3455
  else throw Error(`Received invalid resource: ${JSON.stringify(resource)}`);
3193
3456
  return getClientState(instance, clientOptions).observable.pipe(switchMap((client) => client.observable.request({
3194
- uri,
3457
+ url,
3195
3458
  tag: "acl.get"
3196
3459
  }).pipe(retry({ delay: (error, retryCount) => {
3197
3460
  let isTransientClientError = error instanceof ClientError && (error.statusCode === 408 || error.statusCode === 429);
@@ -3269,7 +3532,7 @@ function applyDocumentActions(...args) {
3269
3532
  }
3270
3533
  const boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocumentActions);
3271
3534
  /** @internal */
3272
- async function _applyDocumentActions({ state }, { actions, resource, transactionId = crypto.randomUUID(), disableBatching }) {
3535
+ async function _applyDocumentActions({ state }, { actions, resource, transactionId = randomUuid(), disableBatching }) {
3273
3536
  let { events } = state.get(), transaction = {
3274
3537
  transactionId,
3275
3538
  actions: normalizeActionsForResource(actions, resource),
@@ -3395,7 +3658,7 @@ const installations = defineFetcher({
3395
3658
  apiVersion: "vX",
3396
3659
  scope: "global"
3397
3660
  }).observable.pipe(switchMap((client) => client.observable.request({
3398
- uri: "/installations",
3661
+ url: "/installations",
3399
3662
  query: buildQuery({
3400
3663
  organizationId: options.organizationId,
3401
3664
  include: serializeInclude(options.include),
@@ -3418,7 +3681,7 @@ const installations = defineFetcher({
3418
3681
  apiVersion: "vX",
3419
3682
  scope: "global"
3420
3683
  }).observable.pipe(switchMap((client) => client.observable.request({
3421
- uri: `/installations/${installationId}`,
3684
+ url: `/installations/${installationId}`,
3422
3685
  query: buildQuery({ include: serializeInclude(options?.include) }),
3423
3686
  tag: "installations.get"
3424
3687
  }))),
@@ -3458,7 +3721,7 @@ const organization = defineFetcher({
3458
3721
  apiVersion: "v2025-02-19",
3459
3722
  scope: "global"
3460
3723
  }).observable.pipe(switchMap((client) => client.observable.request({
3461
- uri: `/organizations/${resolveOrganizationId(options)}`,
3724
+ url: `/organizations/${resolveOrganizationId(options)}`,
3462
3725
  query: buildQuery(normalizeOrganizationOptions(options)),
3463
3726
  tag: "organizations.get"
3464
3727
  }))),
@@ -3492,7 +3755,7 @@ const organizations = defineFetcher({
3492
3755
  apiVersion: "v2025-02-19",
3493
3756
  scope: "global"
3494
3757
  }).observable.pipe(switchMap((client) => client.observable.request({
3495
- uri: "/organizations",
3758
+ url: "/organizations",
3496
3759
  query: buildQuery(normalizeOrganizationsOptions(options)),
3497
3760
  tag: "organizations.list"
3498
3761
  }))),
@@ -3529,7 +3792,7 @@ const organizations = defineFetcher({
3529
3792
  useProjectHostname: !0
3530
3793
  }).observable.request({
3531
3794
  method: "GET",
3532
- uri: `/users/${userId}`,
3795
+ url: `/users/${userId}`,
3533
3796
  tag: "users.get"
3534
3797
  }).pipe(map((user) => ({
3535
3798
  data: [{
@@ -3558,7 +3821,7 @@ const organizations = defineFetcher({
3558
3821
  }), resourceType = options.resourceType || "project", resourceId = resourceType === "organization" ? options.organizationId : options.projectId;
3559
3822
  return resourceId ? client.observable.request({
3560
3823
  method: "GET",
3561
- uri: `access/${resourceType}/${resourceId}/users/${userId}`,
3824
+ url: `access/${resourceType}/${resourceId}/users/${userId}`,
3562
3825
  tag: "users.get"
3563
3826
  }).pipe(map((response) => "sanityUserId" in response ? {
3564
3827
  data: [response],
@@ -3582,7 +3845,7 @@ const organizations = defineFetcher({
3582
3845
  loadMore$
3583
3846
  ]).pipe(withLatestFrom(cursor$), switchMap(([[resource, client], cursor]) => client.observable.request({
3584
3847
  method: "GET",
3585
- uri: `access/${resource.type}/${resource.id}/users`,
3848
+ url: `access/${resource.type}/${resource.id}/users`,
3586
3849
  tag: "users.list",
3587
3850
  query: cursor ? {
3588
3851
  nextCursor: cursor,
@@ -3604,7 +3867,7 @@ const organizations = defineFetcher({
3604
3867
  };
3605
3868
  }),
3606
3869
  onSubscribe: ({ instance, state }, options) => {
3607
- let subscriptionId = insecureRandomId(), key = getUsersKey(instance, options);
3870
+ let subscriptionId = randomId(16), key = getUsersKey(instance, options);
3608
3871
  return state.set("addSubscription", addSubscription(subscriptionId, key)), () => {
3609
3872
  setCleanupTimeout(() => state.set("removeSubscription", removeSubscription(subscriptionId, key)), USERS_STATE_CLEAR_DELAY);
3610
3873
  };
@@ -3756,7 +4019,7 @@ const presenceStore = defineStore({
3756
4019
  initialize: (context) => {
3757
4020
  let { instance, state, key: { resource } } = context;
3758
4021
  if (isMediaLibraryResource(resource)) throw Error("Presence is not supported for media library resources.");
3759
- let sessionId = crypto.randomUUID(), client = isDatasetResource(resource) ? getClient(instance, {
4022
+ let sessionId = randomId(16), client = isDatasetResource(resource) ? getClient(instance, {
3760
4023
  apiVersion: PRESENCE_API_VERSION,
3761
4024
  projectId: resource.projectId,
3762
4025
  dataset: resource.dataset,
@@ -3839,7 +4102,7 @@ const presenceStore = defineStore({
3839
4102
  })), isCanvasResource(resource)) {
3840
4103
  let globalClient = getClient(instance, { apiVersion: PRESENCE_API_VERSION });
3841
4104
  subscription.add(globalClient.observable.request({
3842
- uri: `/canvases/${resource.canvasId}`,
4105
+ url: `/canvases/${resource.canvasId}`,
3843
4106
  tag: "canvases.get"
3844
4107
  }).subscribe({
3845
4108
  next: ({ organizationId }) => {
@@ -4181,7 +4444,7 @@ const _getProjectionState = bindActionByResourceAndPerspective(projectionStore,
4181
4444
  return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION;
4182
4445
  },
4183
4446
  onSubscribe: ({ state }, options) => {
4184
- let { projection, ...docHandle } = options, subscriptionId = insecureRandomId(), documentId = getPublishedId(DocumentId(docHandle.documentId)), validProjection = validateProjection(projection), projectionHash = hashString(validProjection);
4447
+ let { projection, ...docHandle } = options, subscriptionId = randomId(16), documentId = getPublishedId(DocumentId(docHandle.documentId)), validProjection = validateProjection(projection), projectionHash = hashString(validProjection);
4185
4448
  return state.set("addSubscription", (prev) => ({
4186
4449
  documentProjections: {
4187
4450
  ...prev.documentProjections,
@@ -4232,6 +4495,23 @@ function resolveProjection(...args) {
4232
4495
  */
4233
4496
  const _resolveProjection = bindActionByResourceAndPerspective(projectionStore, ({ instance }, options) => firstValueFrom(getProjectionState(instance, options).observable.pipe(filter((state) => !!state?.data))));
4234
4497
  /**
4498
+ * Preserves a projection string's literal type and returns the string unchanged.
4499
+ * Use this import when migrating an app with existing experimental Typegen output.
4500
+ *
4501
+ * Current Typegen does not scan this helper. New or changed projections need an
4502
+ * explicit result type until projection generation is supported.
4503
+ *
4504
+ * @example
4505
+ * ```ts
4506
+ * const preview = defineProjection('{title, "author": author->name}')
4507
+ * ```
4508
+ *
4509
+ * @beta
4510
+ */
4511
+ function defineProjection(projection) {
4512
+ return projection;
4513
+ }
4514
+ /**
4235
4515
  * @public
4236
4516
  * Extracts the project ID from a CorsOriginError message.
4237
4517
  * @param error - The error to extract the project ID from.
@@ -4269,6 +4549,6 @@ function isImportError(error) {
4269
4549
  let message = error.message || "";
4270
4550
  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);
4271
4551
  }
4272
- 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 };
4552
+ 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 };
4273
4553
 
4274
4554
  //# sourceMappingURL=index.js.map