@sanity/sdk 3.0.0-rc.1 → 3.0.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 (128) hide show
  1. package/README.md +12 -0
  2. package/dist/_exports/_internal.d.ts +71 -3
  3. package/dist/_exports/_internal.d.ts.map +1 -1
  4. package/dist/_exports/_internal.js +332 -24
  5. package/dist/_exports/_internal.js.map +1 -1
  6. package/dist/_exports/agent.d.ts +65 -0
  7. package/dist/_exports/agent.d.ts.map +1 -0
  8. package/dist/_exports/agent.js +71 -0
  9. package/dist/_exports/agent.js.map +1 -0
  10. package/dist/_exports/comlink.d.ts +118 -0
  11. package/dist/_exports/comlink.d.ts.map +1 -0
  12. package/dist/_exports/comlink.js +69 -0
  13. package/dist/_exports/comlink.js.map +1 -0
  14. package/dist/authStore-C0o1NOxW.d.ts +3165 -0
  15. package/dist/authStore-C0o1NOxW.d.ts.map +1 -0
  16. package/dist/authStore-CtE9bLHW.js +754 -0
  17. package/dist/authStore-CtE9bLHW.js.map +1 -0
  18. package/dist/clientStore-BtE2-q2i.js +90 -0
  19. package/dist/clientStore-BtE2-q2i.js.map +1 -0
  20. package/dist/createSanityInstance-DUI1bGIg.d.ts +278 -0
  21. package/dist/createSanityInstance-DUI1bGIg.d.ts.map +1 -0
  22. package/dist/createStateSourceAction-BOKtqFNm.js +300 -0
  23. package/dist/createStateSourceAction-BOKtqFNm.js.map +1 -0
  24. package/dist/getEnv-D5RGkQLu.js +8 -0
  25. package/dist/getEnv-D5RGkQLu.js.map +1 -0
  26. package/dist/getNodeState-CR3__B2D.js +67 -0
  27. package/dist/getNodeState-CR3__B2D.js.map +1 -0
  28. package/dist/index.d.ts +3 -2335
  29. package/dist/index.js +4048 -3878
  30. package/dist/index.js.map +1 -1
  31. package/dist/logger-DAtJb5RQ.js +173 -0
  32. package/dist/logger-DAtJb5RQ.js.map +1 -0
  33. package/dist/object-CxXlSVg7.js +83 -0
  34. package/dist/object-CxXlSVg7.js.map +1 -0
  35. package/dist/queryStore-C7yMHPUI.js +1346 -0
  36. package/dist/queryStore-C7yMHPUI.js.map +1 -0
  37. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  38. package/dist/setCleanupTimeout-BiM6q1KF.js +31 -0
  39. package/dist/setCleanupTimeout-BiM6q1KF.js.map +1 -0
  40. package/dist/telemetryManager-UIEiMtC0.js +127 -0
  41. package/dist/telemetryManager-UIEiMtC0.js.map +1 -0
  42. package/dist/version-D8RP_Q59.js +9 -0
  43. package/dist/version-D8RP_Q59.js.map +1 -0
  44. package/package.json +11 -17
  45. package/src/_exports/_internal.ts +1 -0
  46. package/src/_exports/agent.ts +19 -0
  47. package/src/_exports/comlink.ts +16 -0
  48. package/src/_exports/index.ts +45 -90
  49. package/src/applications/applications.test-d.ts +5 -0
  50. package/src/applications/applications.ts +5 -2
  51. package/src/auth/authMode.test.ts +0 -25
  52. package/src/auth/authMode.ts +3 -6
  53. package/src/auth/authStore.test.ts +1 -1
  54. package/src/auth/authStore.ts +0 -1
  55. package/src/comments/addonDatasetStore.test.ts +215 -0
  56. package/src/comments/addonDatasetStore.ts +249 -0
  57. package/src/comments/buildCommentThreads.test.ts +83 -0
  58. package/src/comments/buildCommentThreads.ts +54 -0
  59. package/src/comments/commentActions.test.ts +540 -0
  60. package/src/comments/commentActions.ts +555 -0
  61. package/src/comments/commentFieldPath.test.ts +40 -0
  62. package/src/comments/commentFieldPath.ts +44 -0
  63. package/src/comments/commentsConstants.ts +80 -0
  64. package/src/comments/commentsStore.test.ts +419 -0
  65. package/src/comments/commentsStore.ts +483 -0
  66. package/src/comments/normalizeComment.test.ts +116 -0
  67. package/src/comments/normalizeComment.ts +45 -0
  68. package/src/comments/observeComments.test.ts +193 -0
  69. package/src/comments/observeComments.ts +113 -0
  70. package/src/comments/reducers.test.ts +277 -0
  71. package/src/comments/reducers.ts +316 -0
  72. package/src/comments/types.ts +249 -0
  73. package/src/comments/weakenReferences.test.ts +47 -0
  74. package/src/comments/weakenReferences.ts +29 -0
  75. package/src/config/sanityConfig.ts +3 -75
  76. package/src/datasets/datasets.test.ts +44 -9
  77. package/src/datasets/datasets.ts +24 -23
  78. package/src/document/applyDocumentActions.test.ts +8 -12
  79. package/src/document/documentConstants.ts +17 -0
  80. package/src/document/documentStore.test.ts +173 -19
  81. package/src/document/documentStore.ts +36 -5
  82. package/src/document/patchOperations.test.ts +27 -0
  83. package/src/document/patchOperations.ts +10 -1
  84. package/src/document/permissions.test.ts +3 -1
  85. package/src/document/reducers.test.ts +357 -0
  86. package/src/document/reducers.ts +125 -27
  87. package/src/document/util.test.ts +46 -0
  88. package/src/document/util.ts +37 -1
  89. package/src/favorites/favorites.test.ts +64 -24
  90. package/src/favorites/favorites.ts +73 -22
  91. package/src/installations/installations.test-d.ts +5 -0
  92. package/src/installations/installations.ts +4 -0
  93. package/src/presence/bifurTransport.test.ts +155 -3
  94. package/src/presence/bifurTransport.ts +79 -12
  95. package/src/presence/paths.test.ts +70 -0
  96. package/src/presence/paths.ts +41 -0
  97. package/src/presence/presenceStore.test.ts +822 -2
  98. package/src/presence/presenceStore.ts +518 -102
  99. package/src/presence/types.ts +175 -3
  100. package/src/preview/types.ts +0 -25
  101. package/src/projection/projectionStore.test.ts +0 -8
  102. package/src/projection/types.ts +0 -8
  103. package/src/query/queryStore.ts +7 -6
  104. package/src/store/createActionBinder.test.ts +51 -29
  105. package/src/store/createActionBinder.ts +16 -62
  106. package/src/store/createSanityInstance.test.ts +0 -68
  107. package/src/store/createSanityInstance.ts +0 -63
  108. package/src/store/createStateSourceAction.ts +1 -1
  109. package/src/store/fetcherStore.test.ts +23 -1
  110. package/src/store/fetcherStore.ts +3 -3
  111. package/src/users/reducers.test.ts +0 -3
  112. package/dist/_chunks-dts/createGroqSearchFilter.d.ts +0 -1147
  113. package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +0 -1
  114. package/dist/_chunks-es/_internal.js +0 -134
  115. package/dist/_chunks-es/_internal.js.map +0 -1
  116. package/dist/_chunks-es/createGroqSearchFilter.js +0 -1864
  117. package/dist/_chunks-es/createGroqSearchFilter.js.map +0 -1
  118. package/dist/_chunks-es/telemetryManager.js +0 -93
  119. package/dist/_chunks-es/telemetryManager.js.map +0 -1
  120. package/dist/index.d.ts.map +0 -1
  121. package/src/applications/userApplications.test.ts +0 -126
  122. package/src/applications/userApplications.ts +0 -261
  123. package/src/preview/getPreviewState.test.ts +0 -137
  124. package/src/preview/getPreviewState.ts +0 -69
  125. package/src/preview/resolvePreview.test.ts +0 -64
  126. package/src/preview/resolvePreview.ts +0 -43
  127. package/src/utils/createFetcherStore.test.ts +0 -171
  128. package/src/utils/createFetcherStore.ts +0 -284
@@ -0,0 +1,754 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-vyAXikos.js";
2
+ import { a as defineStore, i as bindActionGlobally, t as createStateSourceAction } from "./createStateSourceAction-BOKtqFNm.js";
3
+ import { n as createLogger } from "./logger-DAtJb5RQ.js";
4
+ import { createClient } from "@sanity/client";
5
+ import { EMPTY, Observable, catchError, defer, distinctUntilChanged, exhaustMap, filter, fromEvent, map, switchMap, takeWhile, timer } from "rxjs";
6
+ import "@sanity/types";
7
+ /**
8
+ * Returns the staging API host if the `__SANITY_STAGING__` build-time flag is
9
+ * set to `true` (mirroring how Sanity Studio detects staging builds).
10
+ *
11
+ * @internal
12
+ */
13
+ function getStagingApiHost() {
14
+ if (typeof __SANITY_STAGING__ < "u" && __SANITY_STAGING__ === !0) return "https://api.sanity.work";
15
+ }
16
+ const loggers = /* @__PURE__ */ new WeakMap();
17
+ /**
18
+ * Returns the auth logger for a Sanity instance, creating it on first use.
19
+ *
20
+ * The logger is cached per instance so repeated action calls (logout,
21
+ * setAuthToken, handleAuthCallback) reuse the same logger instead of
22
+ * rebuilding it. The instance details are nested under `instanceContext`,
23
+ * which is what the logger's formatter reads to render the
24
+ * `[project:x] [dataset:y] [instance:z]` prefix.
25
+ *
26
+ * @internal
27
+ */
28
+ function getAuthLogger(instance) {
29
+ let logger = loggers.get(instance);
30
+ return logger || (logger = createLogger("auth", { instanceContext: {
31
+ instanceId: instance.instanceId,
32
+ projectId: instance.config.projectId,
33
+ dataset: instance.config.dataset
34
+ } }), loggers.set(instance, logger)), logger;
35
+ }
36
+ const DEFAULT_BASE = "http://localhost", DEFAULT_API_VERSION = "2021-06-07", REQUEST_TAG_PREFIX = "sanity.sdk.auth";
37
+ /**
38
+ * Determines the auth mode from instance config and environment.
39
+ *
40
+ * Priority:
41
+ * 1. `studio` config provided → `'studio'`
42
+ * 2. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
43
+ * 3. Otherwise → `'standalone'`
44
+ *
45
+ * @internal
46
+ */
47
+ function resolveAuthMode(config, locationHref) {
48
+ return isStudioConfig(config) ? "studio" : detectDashboardContext(locationHref) ? "dashboard" : "standalone";
49
+ }
50
+ /**
51
+ * Returns `true` when the config indicates the SDK is running inside a Studio.
52
+ *
53
+ * @internal
54
+ */
55
+ function isStudioConfig(config) {
56
+ return !!config.studio;
57
+ }
58
+ /**
59
+ * Checks whether the given location href contains a `_context` URL parameter
60
+ * with a non-empty JSON object, indicating the SDK is running inside the
61
+ * Sanity Dashboard.
62
+ *
63
+ * @internal
64
+ */
65
+ function detectDashboardContext(locationHref) {
66
+ try {
67
+ let contextParam = new URL(locationHref, DEFAULT_BASE).searchParams.get("_context");
68
+ if (!contextParam) return !1;
69
+ let parsed = JSON.parse(contextParam);
70
+ return typeof parsed == "object" && !!parsed && !Array.isArray(parsed) && Object.keys(parsed).length > 0;
71
+ } catch (err) {
72
+ return console.error("Failed to parse dashboard context from initial location:", err), !1;
73
+ }
74
+ }
75
+ /**
76
+ * Represents the various states the authentication type can be in.
77
+ *
78
+ * @public
79
+ */
80
+ let AuthStateType = /* @__PURE__ */ function(AuthStateType) {
81
+ return AuthStateType.LOGGED_IN = "logged-in", AuthStateType.LOGGING_IN = "logging-in", AuthStateType.ERROR = "error", AuthStateType.LOGGED_OUT = "logged-out", AuthStateType;
82
+ }({});
83
+ const REFRESH_INTERVAL = 432e5;
84
+ function createTokenRefreshStream(token, clientFactory, apiHost) {
85
+ return new Observable((subscriber) => {
86
+ let subscription = clientFactory({
87
+ apiVersion: DEFAULT_API_VERSION,
88
+ requestTagPrefix: REQUEST_TAG_PREFIX,
89
+ useProjectHostname: !1,
90
+ useCdn: !1,
91
+ token,
92
+ ignoreBrowserTokenWarning: !0,
93
+ ...apiHost && { apiHost }
94
+ }).observable.request({
95
+ uri: "auth/refresh-token",
96
+ method: "POST",
97
+ tag: "refresh-token",
98
+ body: { token }
99
+ }).subscribe(subscriber);
100
+ return () => subscription.unsubscribe();
101
+ });
102
+ }
103
+ function shouldRefreshToken(lastRefresh) {
104
+ return !lastRefresh || Date.now() - lastRefresh >= REFRESH_INTERVAL;
105
+ }
106
+ /**
107
+ * Periodically refreshes stamped auth tokens (those containing `-st`).
108
+ *
109
+ * Two triggers cause a refresh while the user stays logged in:
110
+ * - a 12-hour interval timer (only fires while the tab is visible)
111
+ * - the tab becoming visible again after `lastTokenRefresh` has gone stale
112
+ *
113
+ * Non-stamped tokens (e.g. personal access tokens) are never refreshed.
114
+ *
115
+ * @internal
116
+ */
117
+ const refreshStampedToken = ({ state, instance }) => {
118
+ let logger = getAuthLogger(instance), { clientFactory, apiHost, storageArea, storageKey } = state.get().options;
119
+ return state.observable.pipe(map((storeState) => storeState.authState), filter((authState) => authState.type === "logged-in"), distinctUntilChanged((prev, curr) => prev.token === curr.token), filter((authState) => authState.token.includes("-st")), exhaustMap(() => new Observable((subscriber) => {
120
+ let visibilityHandler = () => {
121
+ let currentState = state.get();
122
+ document.visibilityState === "visible" && currentState.authState.type === "logged-in" && shouldRefreshToken(currentState.authState.lastTokenRefresh) && createTokenRefreshStream(currentState.authState.token, clientFactory, apiHost).subscribe({
123
+ next: (response) => {
124
+ state.set("setRefreshStampedToken", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
125
+ ...prev.authState,
126
+ token: response.token,
127
+ lastTokenRefresh: Date.now()
128
+ } : prev.authState })), subscriber.next(response);
129
+ },
130
+ error: (error) => subscriber.error(error)
131
+ });
132
+ }, timerSubscription = timer(REFRESH_INTERVAL, REFRESH_INTERVAL).pipe(filter(() => document.visibilityState === "visible"), switchMap(() => {
133
+ let currentState = state.get().authState;
134
+ if (currentState.type !== "logged-in") throw Error("User logged out before refresh could complete");
135
+ return createTokenRefreshStream(currentState.token, clientFactory, apiHost);
136
+ })).subscribe({
137
+ next: (response) => {
138
+ state.set("setRefreshStampedToken", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
139
+ ...prev.authState,
140
+ token: response.token,
141
+ lastTokenRefresh: Date.now()
142
+ } : prev.authState })), subscriber.next(response);
143
+ },
144
+ error: (error) => subscriber.error(error)
145
+ });
146
+ return document.addEventListener("visibilitychange", visibilityHandler), () => {
147
+ document.removeEventListener("visibilitychange", visibilityHandler), timerSubscription.unsubscribe();
148
+ };
149
+ }).pipe(takeWhile(() => state.get().authState.type === "logged-in"), map((response) => ({ token: response.token }))))).subscribe({
150
+ next: (response) => {
151
+ logger.debug("Token refresh completed, updating state"), state.set("setRefreshStampedToken", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
152
+ ...prev.authState,
153
+ token: response.token,
154
+ lastTokenRefresh: Date.now()
155
+ } : prev.authState })), storageArea?.setItem(storageKey, JSON.stringify({ token: response.token }));
156
+ },
157
+ error: (error) => {
158
+ logger.error("Token refresh failed", { error }), state.set("setRefreshStampedTokenError", { authState: {
159
+ type: "error",
160
+ error
161
+ } });
162
+ }
163
+ });
164
+ }, subscribeToStateAndFetchCurrentUser = ({ state, instance }, fetchOptions) => {
165
+ let logger = getAuthLogger(instance), { clientFactory, apiHost } = state.get().options, useProjectHostname = fetchOptions?.useProjectHostname ?? isStudioConfig(instance.config), projectId = instance.config.projectId;
166
+ return state.observable.pipe(map(({ authState, options: storeOptions }) => ({
167
+ authState,
168
+ authMethod: storeOptions.authMethod
169
+ })), filter((value) => value.authState.type === "logged-in" && !value.authState.currentUser), map((value) => ({
170
+ token: value.authState.token,
171
+ authMethod: value.authMethod
172
+ })), distinctUntilChanged((prev, curr) => prev.token === curr.token && prev.authMethod === curr.authMethod)).pipe(map(({ token, authMethod }) => clientFactory({
173
+ apiVersion: DEFAULT_API_VERSION,
174
+ requestTagPrefix: REQUEST_TAG_PREFIX,
175
+ token: authMethod === "cookie" ? void 0 : token,
176
+ ignoreBrowserTokenWarning: !0,
177
+ useProjectHostname,
178
+ useCdn: !1,
179
+ ...authMethod === "cookie" ? { withCredentials: !0 } : {},
180
+ ...useProjectHostname && projectId ? { projectId } : {},
181
+ ...apiHost && { apiHost }
182
+ })), switchMap((client) => client.observable.request({
183
+ uri: "/users/me",
184
+ method: "GET",
185
+ tag: "users.get-current"
186
+ }).pipe(
187
+ /**
188
+ * Catch inside switchMap so the outer subscription survives.
189
+ * Without this, a 401 terminates the subscription permanently
190
+ * and subsequent token refreshes via comlink never re-fetch /users/me.
191
+ * @see SDK-1409
192
+ */
193
+ catchError((error) => (logger.error("Failed to fetch current user", { error }), state.set("setError", { authState: {
194
+ type: "error",
195
+ error
196
+ } }), EMPTY))
197
+ ))).subscribe({ next: (currentUser) => {
198
+ logger.info("Current user fetched successfully", { hasEmail: !!currentUser.email }), logger.debug("Current user details", { userId: currentUser.id }), state.set("setCurrentUser", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
199
+ ...prev.authState,
200
+ currentUser
201
+ } : prev.authState }));
202
+ } });
203
+ };
204
+ /**
205
+ * Creates a properly initialized {@link LoggedInAuthState}.
206
+ *
207
+ * For stamped tokens (containing `"-st"`), `lastTokenRefresh` is set to
208
+ * `Date.now()` so that the visibility-change handler in
209
+ * {@link refreshStampedToken} does not trigger an unnecessary refresh the
210
+ * first time the tab becomes visible.
211
+ *
212
+ * @param token - The auth token.
213
+ * @param currentUser - The current user, or `null` if not yet fetched.
214
+ * @param existingLastTokenRefresh - An existing timestamp to preserve
215
+ * (e.g. when updating a token while keeping the previous refresh time).
216
+ * @internal
217
+ */
218
+ function createLoggedInAuthState(token, currentUser, existingLastTokenRefresh) {
219
+ let isStampedToken = token.includes("-st"), lastTokenRefresh = existingLastTokenRefresh ?? (isStampedToken ? Date.now() : void 0);
220
+ return {
221
+ type: "logged-in",
222
+ token,
223
+ currentUser,
224
+ ...lastTokenRefresh !== void 0 && { lastTokenRefresh }
225
+ };
226
+ }
227
+ function getAuthCode(callbackUrl, locationHref) {
228
+ let loc = new URL(locationHref, DEFAULT_BASE), callbackLocation = callbackUrl ? new URL(callbackUrl, DEFAULT_BASE) : void 0, callbackLocationMatches = !callbackLocation || loc.pathname.toLowerCase().startsWith(callbackLocation.pathname.toLowerCase()), authCode = new URLSearchParams(loc.hash.slice(1)).get("sid") || new URLSearchParams(loc.search).get("sid");
229
+ if (!authCode) {
230
+ let contextParam = new URLSearchParams(loc.search).get("_context");
231
+ if (contextParam) try {
232
+ let parsedContext = JSON.parse(contextParam);
233
+ parsedContext && typeof parsedContext == "object" && typeof parsedContext.sid == "string" && parsedContext.sid && (authCode = parsedContext.sid);
234
+ } catch {}
235
+ }
236
+ return authCode && callbackLocationMatches ? authCode : null;
237
+ }
238
+ function getTokenFromLocation(locationHref) {
239
+ let loc = new URL(locationHref);
240
+ return new URLSearchParams(loc.hash.slice(1)).get("token") || null;
241
+ }
242
+ /**
243
+ * Attempts to retrieve a token from the configured storage.
244
+ * If invalid or not present, returns null.
245
+ */
246
+ function getTokenFromStorage(storageArea, storageKey) {
247
+ if (!storageArea) return null;
248
+ let item = storageArea.getItem(storageKey);
249
+ if (item === null) return null;
250
+ try {
251
+ let parsed = JSON.parse(item);
252
+ if (typeof parsed != "object" || !parsed || !("token" in parsed) || typeof parsed.token != "string") throw Error("Invalid stored auth data structure");
253
+ return parsed.token;
254
+ } catch {
255
+ return storageArea.removeItem(storageKey), null;
256
+ }
257
+ }
258
+ /**
259
+ * Creates an observable stream of storage events. If not in a browser environment,
260
+ * returns an EMPTY observable.
261
+ */
262
+ function getStorageEvents() {
263
+ return typeof window < "u" && typeof window.addEventListener == "function" ? fromEvent(window, "storage") : EMPTY;
264
+ }
265
+ /**
266
+ * Returns a default storage instance (localStorage) if available.
267
+ * If not available or an error occurs, returns undefined.
268
+ */
269
+ function getDefaultStorage() {
270
+ try {
271
+ return typeof localStorage < "u" && typeof localStorage.getItem == "function" ? localStorage : void 0;
272
+ } catch {
273
+ return;
274
+ }
275
+ }
276
+ /**
277
+ * Returns the default location to use.
278
+ * Tries accessing `location.href`, falls back to a default base if not available or on error.
279
+ */
280
+ function getDefaultLocation() {
281
+ try {
282
+ return typeof location > "u" ? DEFAULT_BASE : typeof location.href == "string" ? location.href : DEFAULT_BASE;
283
+ } catch {
284
+ return DEFAULT_BASE;
285
+ }
286
+ }
287
+ /**
288
+ * Cleans up the URL by removing the `token` from the hash and the `sid` and `url` search params.
289
+ * @internal
290
+ */
291
+ function getCleanedUrl(locationUrl) {
292
+ let loc = new URL(locationUrl), rawHash = loc.hash.startsWith("#") ? loc.hash.slice(1) : loc.hash;
293
+ if (rawHash && rawHash.includes("=")) {
294
+ let hashParams = new URLSearchParams(rawHash);
295
+ hashParams.delete("token"), hashParams.delete("withSid");
296
+ let nextHash = hashParams.toString();
297
+ loc.hash = nextHash ? `#${nextHash}` : "";
298
+ }
299
+ return loc.searchParams.delete("sid"), loc.searchParams.delete("url"), loc.toString();
300
+ }
301
+ /** @internal Extracts the structured API error body from a ClientError, if present. */
302
+ function getClientErrorApiBody(error) {
303
+ let body = error.response?.body;
304
+ return body && typeof body == "object" ? body : void 0;
305
+ }
306
+ /** @internal Returns the error type string from an API error body, if available. */
307
+ function getClientErrorApiType(error) {
308
+ let body = getClientErrorApiBody(error);
309
+ return body?.error?.type ?? body?.type;
310
+ }
311
+ /** @internal Returns the error description string from an API error body, if available. */
312
+ function getClientErrorApiDescription(error) {
313
+ let body = getClientErrorApiBody(error);
314
+ return body?.error?.description ?? body?.description;
315
+ }
316
+ /** @internal True if the error represents a projectUserNotFoundError. */
317
+ function isProjectUserNotFoundClientError(error) {
318
+ return getClientErrorApiType(error) === "projectUserNotFoundError";
319
+ }
320
+ /**
321
+ * Parses the dashboard context from a location href's `_context` URL parameter.
322
+ * Strips the `sid` property from the parsed context (it's handled separately).
323
+ *
324
+ * @internal
325
+ */
326
+ function parseDashboardContext(locationHref) {
327
+ try {
328
+ let contextParam = new URL(locationHref, DEFAULT_BASE).searchParams.get("_context");
329
+ if (contextParam) {
330
+ let parsedContext = JSON.parse(contextParam);
331
+ if (parsedContext && typeof parsedContext == "object" && !Array.isArray(parsedContext) && Object.keys(parsedContext).length > 0) return delete parsedContext.sid, parsedContext;
332
+ }
333
+ } catch (err) {
334
+ console.error("Failed to parse dashboard context from initial location:", err);
335
+ }
336
+ return {};
337
+ }
338
+ /**
339
+ * Resolves the initial auth state for Dashboard mode.
340
+ *
341
+ * In dashboard mode the token is provided by the parent frame via Comlink,
342
+ * so the SDK starts in a `LOGGED_OUT` state and waits for the token to arrive.
343
+ * The `_context` URL parameter provides dashboard metadata (orgId, mode, env).
344
+ *
345
+ * A provided token (`auth.token`) or an auth code/callback still takes
346
+ * precedence, even when running inside the dashboard.
347
+ *
348
+ * @internal
349
+ */
350
+ function getDashboardInitialState(options) {
351
+ let { authConfig, initialLocationHref } = options, providedToken = authConfig.token, callbackUrl = authConfig.callbackUrl, storageKey = "__sanity_auth_token", dashboardContext = parseDashboardContext(initialLocationHref);
352
+ return providedToken ? {
353
+ authState: createLoggedInAuthState(providedToken, null),
354
+ storageKey,
355
+ storageArea: void 0,
356
+ authMethod: void 0,
357
+ dashboardContext
358
+ } : getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref) ? {
359
+ authState: {
360
+ type: "logging-in",
361
+ isExchangingToken: !1
362
+ },
363
+ storageKey,
364
+ storageArea: void 0,
365
+ authMethod: void 0,
366
+ dashboardContext
367
+ } : {
368
+ authState: {
369
+ type: "logged-out",
370
+ isDestroyingSession: !1
371
+ },
372
+ storageKey,
373
+ storageArea: void 0,
374
+ authMethod: void 0,
375
+ dashboardContext
376
+ };
377
+ }
378
+ /**
379
+ * Initialize Dashboard auth subscriptions:
380
+ * - Subscribe to state changes and fetch current user
381
+ * - Start stamped token refresh
382
+ *
383
+ * Note: storage events are NOT subscribed in dashboard mode since
384
+ * the dashboard does not use localStorage for token persistence.
385
+ *
386
+ * @internal
387
+ */
388
+ function initializeDashboardAuth(context, tokenRefresherRunning) {
389
+ let subscriptions = [], startedRefresher = !1;
390
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !1 })), tokenRefresherRunning || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
391
+ dispose: () => {
392
+ for (let subscription of subscriptions) subscription.unsubscribe();
393
+ },
394
+ tokenRefresherStarted: startedRefresher
395
+ };
396
+ }
397
+ const subscribeToStorageEventsAndSetToken = ({ state }) => {
398
+ let { storageArea, storageKey } = state.get().options;
399
+ return defer(getStorageEvents).pipe(filter((e) => e.storageArea === storageArea && e.key === storageKey), map(() => getTokenFromStorage(storageArea, storageKey)), distinctUntilChanged()).subscribe((token) => {
400
+ state.set("updateTokenFromStorageEvent", { authState: token ? createLoggedInAuthState(token, null) : {
401
+ type: "logged-out",
402
+ isDestroyingSession: !1
403
+ } });
404
+ });
405
+ };
406
+ /**
407
+ * Resolves the initial auth state for Standalone mode.
408
+ *
409
+ * Token discovery order:
410
+ * 1. Provided token (`auth.token` in config)
411
+ * 2. Auth code or token from location (OAuth callback)
412
+ * 3. localStorage: `__sanity_auth_token`
413
+ * 4. Falls back to `LOGGED_OUT`
414
+ *
415
+ * @internal
416
+ */
417
+ function getStandaloneInitialState(options) {
418
+ let { authConfig, initialLocationHref } = options, providedToken = authConfig.token, callbackUrl = authConfig.callbackUrl, storageKey = "__sanity_auth_token", storageArea = authConfig.storageArea ?? getDefaultStorage();
419
+ if (providedToken) return {
420
+ authState: createLoggedInAuthState(providedToken, null),
421
+ storageKey,
422
+ storageArea,
423
+ authMethod: void 0,
424
+ dashboardContext: {}
425
+ };
426
+ if (getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref)) return {
427
+ authState: {
428
+ type: "logging-in",
429
+ isExchangingToken: !1
430
+ },
431
+ storageKey,
432
+ storageArea,
433
+ authMethod: void 0,
434
+ dashboardContext: {}
435
+ };
436
+ let token = getTokenFromStorage(storageArea, storageKey);
437
+ return token ? {
438
+ authState: createLoggedInAuthState(token, null),
439
+ storageKey,
440
+ storageArea,
441
+ authMethod: "localstorage",
442
+ dashboardContext: {}
443
+ } : {
444
+ authState: {
445
+ type: "logged-out",
446
+ isDestroyingSession: !1
447
+ },
448
+ storageKey,
449
+ storageArea,
450
+ authMethod: void 0,
451
+ dashboardContext: {}
452
+ };
453
+ }
454
+ /**
455
+ * Initialize Standalone auth subscriptions:
456
+ * - Subscribe to state changes and fetch current user
457
+ * - Subscribe to storage events for token key
458
+ * - Start stamped token refresh
459
+ *
460
+ * @internal
461
+ */
462
+ function initializeStandaloneAuth(context, tokenRefresherRunning) {
463
+ let subscriptions = [], startedRefresher = !1;
464
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !1 })), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context)), tokenRefresherRunning || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
465
+ dispose: () => {
466
+ for (let subscription of subscriptions) subscription.unsubscribe();
467
+ },
468
+ tokenRefresherStarted: startedRefresher
469
+ };
470
+ }
471
+ /**
472
+ * Attempts to check for cookie auth by making a withCredentials request to the users endpoint.
473
+ * @param projectId - The project ID to check for cookie auth.
474
+ * @param clientFactory - A factory function that creates a Sanity client.
475
+ * @returns True if the user is authenticated, false otherwise.
476
+ * @internal
477
+ */
478
+ async function checkForCookieAuth(projectId, clientFactory) {
479
+ if (!projectId) return !1;
480
+ try {
481
+ let user = await clientFactory({
482
+ projectId,
483
+ useCdn: !1,
484
+ requestTagPrefix: REQUEST_TAG_PREFIX,
485
+ timeout: 1e4
486
+ }).request({
487
+ uri: "/users/me",
488
+ withCredentials: !0,
489
+ tag: "users.get-current"
490
+ });
491
+ return typeof user == "object" && !!user && typeof user.id == "string";
492
+ } catch {
493
+ return !1;
494
+ }
495
+ }
496
+ /**
497
+ * Attempts to retrieve a studio token from local storage.
498
+ * @param storageArea - The storage area to retrieve the token from.
499
+ * @param storageKey - The storage key to retrieve the token from.
500
+ * @returns The studio token or null if it does not exist.
501
+ * @internal
502
+ */
503
+ function getStudioTokenFromLocalStorage(storageArea, storageKey) {
504
+ return !storageArea || !storageKey ? null : getTokenFromStorage(storageArea, storageKey) || null;
505
+ }
506
+ /**
507
+ * Resolves the initial auth state for Studio mode.
508
+ *
509
+ * When a `tokenSource` is provided (via `config.studio.auth.token`), the
510
+ * initial state is `LOGGING_IN` — the actual token arrives asynchronously
511
+ * via the subscription set up in `initializeStudioAuth`.
512
+ *
513
+ * Fallback token discovery order (no tokenSource):
514
+ * 1. Provided token (`auth.token` in config)
515
+ * 2. localStorage: `__studio_auth_token_${projectId}`
516
+ * 3. Falls back to `LOGGED_OUT` (cookie auth is checked async in `initializeStudioAuth`)
517
+ *
518
+ * @internal
519
+ */
520
+ function getStudioInitialState(options) {
521
+ let { authConfig, projectId, tokenSource } = options, storageArea = authConfig.storageArea ?? getDefaultStorage(), studioStorageKey = `__studio_auth_token_${projectId ?? ""}`;
522
+ if (tokenSource) return {
523
+ authState: {
524
+ type: "logging-in",
525
+ isExchangingToken: !1
526
+ },
527
+ storageKey: studioStorageKey,
528
+ storageArea,
529
+ authMethod: void 0,
530
+ dashboardContext: {}
531
+ };
532
+ let providedToken = authConfig.token, authMethod, token = getStudioTokenFromLocalStorage(storageArea, studioStorageKey);
533
+ return token && (authMethod = "localstorage"), providedToken ? {
534
+ authState: createLoggedInAuthState(providedToken, null),
535
+ storageKey: studioStorageKey,
536
+ storageArea,
537
+ authMethod,
538
+ dashboardContext: {}
539
+ } : token ? {
540
+ authState: createLoggedInAuthState(token, null),
541
+ storageKey: studioStorageKey,
542
+ storageArea,
543
+ authMethod: "localstorage",
544
+ dashboardContext: {}
545
+ } : {
546
+ authState: {
547
+ type: "logged-out",
548
+ isDestroyingSession: !1
549
+ },
550
+ storageKey: studioStorageKey,
551
+ storageArea,
552
+ authMethod: void 0,
553
+ dashboardContext: {}
554
+ };
555
+ }
556
+ /**
557
+ * Initialize Studio auth subscriptions.
558
+ *
559
+ * When a `tokenSource` is available (reactive path), subscribes to it for
560
+ * ongoing token sync. The Studio is the token authority — no independent
561
+ * token refresh or cookie auth probing is needed.
562
+ *
563
+ * Fallback path (no tokenSource):
564
+ * - Subscribe to state changes and fetch current user
565
+ * - Subscribe to storage events for studio token key
566
+ * - Check for cookie auth asynchronously if no token was found
567
+ * - Start stamped token refresh
568
+ *
569
+ * @internal
570
+ */
571
+ function initializeStudioAuth(context, tokenRefresherRunning) {
572
+ let tokenSource = context.instance.config.studio?.auth?.token;
573
+ return tokenSource ? initializeWithTokenSource(context, tokenSource) : initializeWithFallback(context, tokenRefresherRunning);
574
+ }
575
+ /**
576
+ * Subscribe to a reactive token source from the Studio workspace.
577
+ *
578
+ * When the token source emits a non-null token, the SDK uses it directly.
579
+ * When it emits `null`, the behavior depends on the `authenticated` flag
580
+ * from the Studio's workspace config:
581
+ *
582
+ * - `authenticated: true` — the Studio has already verified the user is
583
+ * logged in (e.g. via cookie auth). The SDK treats the null token as
584
+ * cookie-based auth and stays in the LOGGED_IN state.
585
+ *
586
+ * - `authenticated` absent/false — the user is genuinely not authenticated;
587
+ * transition to LOGGED_OUT.
588
+ *
589
+ * No async cookie probing is needed here because this code path only runs
590
+ * when a Studio provides SDKStudioContext, and the Studio's Workspace type
591
+ * always includes `authenticated`. The async `checkForCookieAuth` fallback
592
+ * remains in `initializeWithFallback` for the non-Studio path.
593
+ */
594
+ function initializeWithTokenSource(context, tokenSource) {
595
+ let subscriptions = [], studioAuthenticated = context.instance.config.studio?.authenticated === !0;
596
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !0 }));
597
+ let tokenSub = tokenSource.subscribe({ next: (token) => {
598
+ let { state } = context;
599
+ token ? state.set("studioTokenSource", (prev) => ({
600
+ options: {
601
+ ...prev.options,
602
+ authMethod: void 0
603
+ },
604
+ authState: createLoggedInAuthState(token, null)
605
+ })) : studioAuthenticated ? state.set("studioTokenSourceCookieAuth", (prev) => ({
606
+ options: {
607
+ ...prev.options,
608
+ authMethod: "cookie"
609
+ },
610
+ authState: prev.authState.type === "logged-in" ? prev.authState : createLoggedInAuthState("", null)
611
+ })) : state.set("studioTokenSourceLoggedOut", (prev) => ({
612
+ options: {
613
+ ...prev.options,
614
+ authMethod: void 0
615
+ },
616
+ authState: {
617
+ type: "logged-out",
618
+ isDestroyingSession: !1
619
+ }
620
+ }));
621
+ } });
622
+ return {
623
+ dispose: () => {
624
+ tokenSub.unsubscribe();
625
+ for (let subscription of subscriptions) subscription.unsubscribe();
626
+ },
627
+ tokenRefresherStarted: !1
628
+ };
629
+ }
630
+ /**
631
+ * Fallback initialization when no reactive token source is available.
632
+ * Uses localStorage/cookie discovery (existing behavior).
633
+ */
634
+ function initializeWithFallback(context, tokenRefresherRunning) {
635
+ let subscriptions = [], startedRefresher = !1;
636
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !0 })), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context));
637
+ try {
638
+ let { instance, state } = context;
639
+ if (!(state.get().authState?.type === "logged-in" && state.get().authState.token)) {
640
+ let projectIdValue = instance.config.projectId, clientFactory = state.get().options.clientFactory;
641
+ checkForCookieAuth(projectIdValue, clientFactory).then((isCookieAuthEnabled) => {
642
+ isCookieAuthEnabled && state.set("enableCookieAuth", (prev) => ({
643
+ options: {
644
+ ...prev.options,
645
+ authMethod: "cookie"
646
+ },
647
+ authState: prev.authState.type === "logged-in" ? prev.authState : createLoggedInAuthState("", null)
648
+ }));
649
+ });
650
+ }
651
+ } catch {}
652
+ return tokenRefresherRunning || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
653
+ dispose: () => {
654
+ for (let subscription of subscriptions) subscription.unsubscribe();
655
+ },
656
+ tokenRefresherStarted: startedRefresher
657
+ };
658
+ }
659
+ var authStore_exports = /* @__PURE__ */ __exportAll({
660
+ authStore: () => authStore,
661
+ getAuthMethodState: () => getAuthMethodState,
662
+ getAuthState: () => getAuthState,
663
+ getCurrentUserState: () => getCurrentUserState,
664
+ getDashboardOrganizationId: () => getDashboardOrganizationId,
665
+ getIsInDashboardState: () => getIsInDashboardState,
666
+ getLoginUrlState: () => getLoginUrlState,
667
+ getTokenState: () => getTokenState,
668
+ setAuthToken: () => setAuthToken
669
+ });
670
+ let tokenRefresherRunning = !1;
671
+ const authStore = defineStore({
672
+ name: "Auth",
673
+ getInitialState(instance) {
674
+ let logger = getAuthLogger(instance);
675
+ logger.debug("Initializing auth store", {
676
+ hasProvidedToken: !!instance.config.auth?.token,
677
+ hasCustomProviders: !!(instance.config.auth?.providers && instance.config.auth.providers.length > 0)
678
+ });
679
+ let { apiHost: configApiHost, callbackUrl, providers: customProviders, token: providedToken, clientFactory = createClient, initialLocationHref = getDefaultLocation() } = instance.config.auth ?? {}, apiHost = configApiHost ?? getStagingApiHost(), authConfig = instance.config.auth ?? {}, loginDomain = "https://www.sanity.io";
680
+ try {
681
+ apiHost && new URL(apiHost).hostname.endsWith(".sanity.work") && (loginDomain = "https://www.sanity.work");
682
+ } catch {}
683
+ let loginUrl = new URL("/login", loginDomain);
684
+ loginUrl.searchParams.set("origin", getCleanedUrl(initialLocationHref)), loginUrl.searchParams.set("type", "stampedToken"), loginUrl.searchParams.set("withSid", "true");
685
+ let mode = resolveAuthMode(instance.config, initialLocationHref), strategyOptions = {
686
+ authConfig,
687
+ projectId: instance.config.projectId,
688
+ initialLocationHref,
689
+ clientFactory,
690
+ tokenSource: instance.config.studio?.auth?.token
691
+ }, result;
692
+ switch (mode) {
693
+ case "studio":
694
+ result = getStudioInitialState(strategyOptions);
695
+ break;
696
+ case "dashboard":
697
+ result = getDashboardInitialState(strategyOptions);
698
+ break;
699
+ case "standalone": result = getStandaloneInitialState(strategyOptions);
700
+ }
701
+ return logger.debug("Auth state initialized", {
702
+ authStateType: result.authState.type,
703
+ mode,
704
+ authMethod: result.authMethod
705
+ }), {
706
+ authState: result.authState,
707
+ dashboardContext: result.dashboardContext,
708
+ options: {
709
+ apiHost,
710
+ loginUrl: loginUrl.toString(),
711
+ callbackUrl,
712
+ customProviders,
713
+ providedToken,
714
+ clientFactory,
715
+ initialLocationHref,
716
+ storageKey: result.storageKey,
717
+ storageArea: result.storageArea,
718
+ authMethod: result.authMethod
719
+ }
720
+ };
721
+ },
722
+ initialize(context) {
723
+ let logger = getAuthLogger(context.instance), initialLocationHref = context.state.get().options?.initialLocationHref ?? getDefaultLocation(), mode = resolveAuthMode(context.instance.config, initialLocationHref);
724
+ logger.debug("Setting up auth subscriptions", { mode });
725
+ let initResult;
726
+ switch (mode) {
727
+ case "studio":
728
+ initResult = initializeStudioAuth(context, tokenRefresherRunning);
729
+ break;
730
+ case "dashboard":
731
+ initResult = initializeDashboardAuth(context, tokenRefresherRunning);
732
+ break;
733
+ case "standalone": initResult = initializeStandaloneAuth(context, tokenRefresherRunning);
734
+ }
735
+ return initResult.tokenRefresherStarted && (tokenRefresherRunning = !0), () => {
736
+ logger.debug("Cleaning up auth subscriptions"), initResult.dispose();
737
+ };
738
+ }
739
+ }), getCurrentUserState = bindActionGlobally(authStore, createStateSourceAction(({ state: { authState } }) => authState.type === "logged-in" ? authState.currentUser : null)), getTokenState = bindActionGlobally(authStore, createStateSourceAction(({ state: { authState } }) => authState.type === "logged-in" ? authState.token : null)), getAuthMethodState = bindActionGlobally(authStore, createStateSourceAction(({ state: { options } }) => options.authMethod)), getLoginUrlState = bindActionGlobally(authStore, createStateSourceAction(({ state: { options } }) => options.loginUrl)), getAuthState = bindActionGlobally(authStore, createStateSourceAction(({ state: { authState } }) => authState)), getDashboardOrganizationId = bindActionGlobally(authStore, createStateSourceAction(({ state: { dashboardContext } }) => dashboardContext?.orgId)), getIsInDashboardState = bindActionGlobally(authStore, createStateSourceAction(({ state: { dashboardContext } }) => !!dashboardContext && Object.keys(dashboardContext).length > 0)), setAuthToken = bindActionGlobally(authStore, ({ state, instance }, token) => {
740
+ let logger = getAuthLogger(instance), currentAuthState = state.get().authState;
741
+ if (token) {
742
+ if (currentAuthState.type !== "logged-in" || currentAuthState.token !== token) {
743
+ logger.info("Setting auth token");
744
+ let currentUser = currentAuthState.type === "logged-in" ? currentAuthState.currentUser : null, preservedLastTokenRefresh = currentAuthState.type === "logged-in" ? currentAuthState.lastTokenRefresh : void 0;
745
+ state.set("setToken", { authState: createLoggedInAuthState(token, currentUser, preservedLastTokenRefresh) });
746
+ }
747
+ } else currentAuthState.type !== "logged-out" && (logger.info("Clearing auth token"), state.set("setToken", { authState: {
748
+ type: "logged-out",
749
+ isDestroyingSession: !1
750
+ } }));
751
+ });
752
+ export { REQUEST_TAG_PREFIX as C, DEFAULT_API_VERSION as S, getStagingApiHost as T, getDefaultLocation as _, getCurrentUserState as a, AuthStateType as b, getLoginUrlState as c, createLoggedInAuthState as d, getAuthCode as f, getClientErrorApiType as g, getClientErrorApiDescription as h, getAuthState as i, getTokenState as l, getClientErrorApiBody as m, authStore_exports as n, getDashboardOrganizationId as o, getCleanedUrl as p, getAuthMethodState as r, getIsInDashboardState as s, authStore as t, setAuthToken as u, getTokenFromLocation as v, getAuthLogger as w, isStudioConfig as x, isProjectUserNotFoundClientError as y };
753
+
754
+ //# sourceMappingURL=authStore-CtE9bLHW.js.map