@sanity/sdk 2.19.0 → 2.20.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 (62) hide show
  1. package/dist/_exports/_internal.d.ts +2 -2
  2. package/dist/_exports/_internal.js +196 -22
  3. package/dist/_exports/_internal.js.map +1 -1
  4. package/dist/authStore-CWTAuieY.js +1072 -0
  5. package/dist/authStore-CWTAuieY.js.map +1 -0
  6. package/dist/clientStore-CMzJ7XhZ.js +168 -0
  7. package/dist/clientStore-CMzJ7XhZ.js.map +1 -0
  8. package/dist/createGroqSearchFilter-B0NdA-EP.js +1222 -0
  9. package/dist/createGroqSearchFilter-B0NdA-EP.js.map +1 -0
  10. package/dist/getEnv-76AjZd_f.js +178 -0
  11. package/dist/getEnv-76AjZd_f.js.map +1 -0
  12. package/dist/index.d.ts +2 -2105
  13. package/dist/index.js +4140 -3870
  14. package/dist/index.js.map +1 -1
  15. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  16. package/dist/telemetryManager-Bd3O8z2S.js +127 -0
  17. package/dist/telemetryManager-Bd3O8z2S.js.map +1 -0
  18. package/dist/utils-BMwA8Kg9.d.ts +3334 -0
  19. package/dist/utils-BMwA8Kg9.d.ts.map +1 -0
  20. package/dist/version-DL2l07HG.js +9 -0
  21. package/dist/version-DL2l07HG.js.map +1 -0
  22. package/package.json +12 -20
  23. package/src/_exports/_internal.ts +1 -0
  24. package/src/_exports/index.ts +30 -0
  25. package/src/comments/addonDatasetStore.test.ts +215 -0
  26. package/src/comments/addonDatasetStore.ts +249 -0
  27. package/src/comments/buildCommentThreads.test.ts +83 -0
  28. package/src/comments/buildCommentThreads.ts +54 -0
  29. package/src/comments/commentActions.test.ts +540 -0
  30. package/src/comments/commentActions.ts +555 -0
  31. package/src/comments/commentFieldPath.test.ts +40 -0
  32. package/src/comments/commentFieldPath.ts +44 -0
  33. package/src/comments/commentsConstants.ts +80 -0
  34. package/src/comments/commentsStore.test.ts +419 -0
  35. package/src/comments/commentsStore.ts +483 -0
  36. package/src/comments/normalizeComment.test.ts +116 -0
  37. package/src/comments/normalizeComment.ts +45 -0
  38. package/src/comments/observeComments.test.ts +193 -0
  39. package/src/comments/observeComments.ts +113 -0
  40. package/src/comments/reducers.test.ts +277 -0
  41. package/src/comments/reducers.ts +316 -0
  42. package/src/comments/types.ts +249 -0
  43. package/src/comments/weakenReferences.test.ts +47 -0
  44. package/src/comments/weakenReferences.ts +29 -0
  45. package/src/document/documentConstants.ts +8 -0
  46. package/src/document/documentStore.test.ts +98 -1
  47. package/src/document/documentStore.ts +36 -5
  48. package/src/document/patchOperations.test.ts +27 -0
  49. package/src/document/patchOperations.ts +10 -1
  50. package/src/document/permissions.test.ts +3 -1
  51. package/src/document/reducers.test.ts +133 -0
  52. package/src/document/reducers.ts +18 -3
  53. package/src/projection/projectionStore.test.ts +0 -8
  54. package/dist/_chunks-dts/createGroqSearchFilter.d.ts +0 -923
  55. package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +0 -1
  56. package/dist/_chunks-es/_internal.js +0 -134
  57. package/dist/_chunks-es/_internal.js.map +0 -1
  58. package/dist/_chunks-es/createGroqSearchFilter.js +0 -1590
  59. package/dist/_chunks-es/createGroqSearchFilter.js.map +0 -1
  60. package/dist/_chunks-es/telemetryManager.js +0 -93
  61. package/dist/_chunks-es/telemetryManager.js.map +0 -1
  62. package/dist/index.d.ts.map +0 -1
@@ -0,0 +1,1072 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-vyAXikos.js";
2
+ import { r as createLogger, t as getEnv } from "./getEnv-76AjZd_f.js";
3
+ import { createClient } from "@sanity/client";
4
+ import { EMPTY, Observable, catchError, defer, distinctUntilChanged, exhaustMap, filter, finalize, fromEvent, map, shareReplay, skip, switchMap, takeWhile, timer } from "rxjs";
5
+ import "@sanity/types";
6
+ import { devtools } from "zustand/middleware";
7
+ import { createStore } from "zustand/vanilla";
8
+ /**
9
+ * @beta
10
+ */
11
+ function isDatasetResource(resource) {
12
+ return "projectId" in resource && "dataset" in resource;
13
+ }
14
+ /**
15
+ * @beta
16
+ */
17
+ function isMediaLibraryResource(resource) {
18
+ return "mediaLibraryId" in resource;
19
+ }
20
+ /**
21
+ * @beta
22
+ */
23
+ function isCanvasResource(resource) {
24
+ return "canvasId" in resource;
25
+ }
26
+ /**
27
+ * @deprecated Use `isDatasetResource` instead.
28
+ * @beta
29
+ */
30
+ function isDatasetSource(source) {
31
+ return isDatasetResource(source);
32
+ }
33
+ /**
34
+ * @deprecated Use `isMediaLibraryResource` instead.
35
+ * @beta
36
+ */
37
+ function isMediaLibrarySource(source) {
38
+ return isMediaLibraryResource(source);
39
+ }
40
+ /**
41
+ * @deprecated Use `isCanvasResource` instead.
42
+ * @beta
43
+ */
44
+ function isCanvasSource(source) {
45
+ return isCanvasResource(source);
46
+ }
47
+ const isReleasePerspective = (perspective) => typeof perspective == "object" && !!perspective && "releaseName" in perspective;
48
+ /**
49
+ * Creates a reactive store state container with multiple access patterns
50
+ * @param initialState - Initial state value for the store
51
+ * @param devToolsOptions - Configuration for Zustand devtools integration
52
+ * @returns StoreState instance with get/set/observable interface
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * // Create a simple counter store
57
+ * const counterStore = createStoreState({ count: 0 });
58
+ *
59
+ * // Update state
60
+ * counterStore.set('increment', { count: 1 });
61
+ *
62
+ * // Observe changes
63
+ * counterStore.observable.subscribe(console.log);
64
+ * ```
65
+ *
66
+ * @remarks
67
+ * Uses Zustand for state management under the hood with RxJS for observable interface.
68
+ * Designed to work with both imperative and reactive programming patterns.
69
+ */
70
+ function createStoreState(initialState, devToolsOptions) {
71
+ let store = createStore()(devtools(() => initialState, devToolsOptions));
72
+ return {
73
+ get: store.getState,
74
+ set: (actionKey, updatedState) => {
75
+ let currentState = store.getState(), nextState = typeof updatedState == "function" ? updatedState(currentState) : updatedState;
76
+ currentState !== nextState && store.setState(nextState, !1, actionKey);
77
+ },
78
+ observable: new Observable((observer) => {
79
+ let emit = () => observer.next(store.getState());
80
+ emit();
81
+ let unsubscribe = store.subscribe(emit);
82
+ return () => unsubscribe();
83
+ })
84
+ };
85
+ }
86
+ /**
87
+ * Typescript helper function for creating store definitions
88
+ *
89
+ * @param storeDefinition - Configuration object defining the store
90
+ * @returns The finalized store definition
91
+ */
92
+ function defineStore(storeDefinition) {
93
+ return storeDefinition;
94
+ }
95
+ /**
96
+ * Creates a new instance of a store from a store definition
97
+ *
98
+ * @param instance - The Sanity instance this store will be associated with
99
+ * @param storeDefinition - The store definition containing initial state and initialization logic
100
+ * @returns A store instance with state management and lifecycle methods
101
+ *
102
+ * @remarks
103
+ * The store instance maintains its own state that is scoped to the given Sanity instance.
104
+ * If the store definition includes an initialize function, it will be called during
105
+ * instance creation, and its cleanup function will be called during disposal.
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * const counterStore = defineStore({
110
+ * name: 'Counter',
111
+ * getInitialState: () => ({ count: 0 }),
112
+ * initialize: ({state}) => {
113
+ * console.log('Counter store initialized')
114
+ * return () => console.log('Counter store disposed')
115
+ * }
116
+ * })
117
+ *
118
+ * const instance = createStoreInstance(sanityInstance, counterStore)
119
+ * // Later when done with the store:
120
+ * instance.dispose()
121
+ * ```
122
+ */
123
+ function createStoreInstance(instance, key, { name, getInitialState, initialize }) {
124
+ let state = createStoreState(getInitialState(instance, key), {
125
+ enabled: !!getEnv("DEV"),
126
+ name: `${name}-${key.name}`
127
+ }), dispose = initialize?.({
128
+ state,
129
+ instance,
130
+ key
131
+ }), disposed = { current: !1 };
132
+ return {
133
+ state,
134
+ dispose: () => {
135
+ disposed.current || (disposed.current = !0, dispose?.());
136
+ },
137
+ isDisposed: () => disposed.current
138
+ };
139
+ }
140
+ /**
141
+ * Creates an action binder function that uses the provided key function
142
+ * to determine how store instances are shared between Sanity instances
143
+ *
144
+ * @param keyFn - Function that generates a key from a Sanity config
145
+ * @returns A function that binds store actions to Sanity instances
146
+ *
147
+ * @remarks
148
+ * Action binders determine how store instances are shared across multiple
149
+ * Sanity instances. The key function determines which instances share state.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * // Create a custom binder that uses a tenant ID for isolation
154
+ * const bindActionByTenant = createActionBinder(config => config.tenantId || 'default')
155
+ *
156
+ * // Use the custom binder with a store definition
157
+ * const getTenantUsers = bindActionByTenant(
158
+ * userStore,
159
+ * ({state}) => state.get().users
160
+ * )
161
+ * ```
162
+ */
163
+ function createActionBinder(keyFn) {
164
+ let instanceRegistry = /* @__PURE__ */ new Map(), storeRegistry = /* @__PURE__ */ new Map();
165
+ /**
166
+ * Binds a store action to a store definition
167
+ *
168
+ * @param storeDefinition - The store definition
169
+ * @param action - The action to bind
170
+ * @returns A function that executes the action with a Sanity instance
171
+ */
172
+ return function bindAction(storeDefinition, action) {
173
+ return function boundAction(instance, ...params) {
174
+ let key = keyFn(instance, ...params), compositeKey = storeDefinition.name + (key.name ? `:${key.name}` : ""), instances = instanceRegistry.get(compositeKey);
175
+ instances || (instances = /* @__PURE__ */ new Set(), instanceRegistry.set(compositeKey, instances)), instances.has(instance.instanceId) || (instances.add(instance.instanceId), instance.onDispose(() => {
176
+ instances.delete(instance.instanceId), instances.size === 0 && (storeRegistry.get(compositeKey)?.dispose(), storeRegistry.delete(compositeKey), instanceRegistry.delete(compositeKey));
177
+ }));
178
+ let storeInstance = storeRegistry.get(compositeKey);
179
+ return storeInstance || (storeInstance = createStoreInstance(instance, key, storeDefinition), storeRegistry.set(compositeKey, storeInstance)), action({
180
+ instance,
181
+ state: storeInstance.state,
182
+ key
183
+ }, ...params);
184
+ };
185
+ };
186
+ }
187
+ const createResourceKey = (instance, resource) => {
188
+ let name, resourceForKey;
189
+ if (resource) {
190
+ if (resourceForKey = resource, isDatasetResource(resource)) name = `${resource.projectId}.${resource.dataset}`;
191
+ else if (isMediaLibraryResource(resource)) name = `media-library:${resource.mediaLibraryId}`;
192
+ else if (isCanvasResource(resource)) name = `canvas:${resource.canvasId}`;
193
+ else throw Error(`Received invalid resource: ${JSON.stringify(resource)}`);
194
+ return {
195
+ name,
196
+ resource: resourceForKey
197
+ };
198
+ }
199
+ let { projectId, dataset } = instance.config;
200
+ if (!projectId || !dataset) throw Error("This API requires a project ID and dataset configured.");
201
+ return {
202
+ name: `${projectId}.${dataset}`,
203
+ resource: {
204
+ projectId,
205
+ dataset
206
+ }
207
+ };
208
+ }, bindActionByResource = createActionBinder((instance, { resource }) => createResourceKey(instance, resource)), bindActionByResourceAndPerspective = createActionBinder((instance, options) => {
209
+ let { resource, perspective } = options, utilizedPerspective = perspective ?? instance.config.perspective ?? "drafts", perspectiveKey;
210
+ perspectiveKey = isReleasePerspective(utilizedPerspective) ? utilizedPerspective.releaseName : typeof utilizedPerspective == "string" ? utilizedPerspective : JSON.stringify(utilizedPerspective);
211
+ let sourceKey = createResourceKey(instance, resource);
212
+ return {
213
+ name: `${sourceKey.name}:${perspectiveKey}`,
214
+ resource: sourceKey.resource,
215
+ perspective: utilizedPerspective
216
+ };
217
+ }), bindActionGlobally = createActionBinder((..._rest) => ({ name: "global" }));
218
+ /**
219
+ * Creates a state source action that generates StateSource instances
220
+ *
221
+ * @remarks
222
+ * The returned action can be bound to a store using createActionBinder.
223
+ * When invoked, returns a StateSource that stays synchronized with the store.
224
+ *
225
+ * Key performance features:
226
+ * - Memoizes selector contexts to prevent redundant object creation
227
+ * - Only runs selectors when the underlying state changes
228
+ *
229
+ * For complex data transformations, consider using memoized selectors
230
+ * (like those from Reselect) to prevent expensive recalculations.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * // Create a simple counter source
235
+ * const getCount = createStateSourceAction(({state}: SelectorContext<CounterState>) => state.count)
236
+ * ```
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * // Create a parameterized source with setup/cleanup
241
+ * const getItem = createStateSourceAction({
242
+ * selector: ({state}, index: number) => state.items[index],
243
+ * onSubscribe: (context, index) => {
244
+ * trackItemSubscription(index)
245
+ * return () => untrackItem(index)
246
+ * }
247
+ * })
248
+ * ```
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * // Binding a state source to a specific store
253
+ * const documentStore = defineStore<DocumentState>({
254
+ * name: 'Documents',
255
+ * getInitialState: () => ({ documents: {} }),
256
+ * // ...
257
+ * })
258
+ *
259
+ * const getDocument = bindActionByDataset(
260
+ * documentStore,
261
+ * createStateSourceAction(({state}, documentId: string) => state.documents[documentId])
262
+ * )
263
+ *
264
+ * // Usage
265
+ * const documentSource = getDocument(sanityInstance, 'doc123')
266
+ * const doc = documentSource.getCurrent()
267
+ * const subscription = documentSource.observable.subscribe(updatedDoc => {
268
+ * console.log('Document changed:', updatedDoc)
269
+ * })
270
+ * ```
271
+ */
272
+ function createStateSourceAction(options) {
273
+ let selector = typeof options == "function" ? options : options.selector, subscribeHandler = options && "onSubscribe" in options ? options.onSubscribe : void 0, isEqual = options && "isEqual" in options ? options.isEqual ?? Object.is : Object.is, selectorContextCache = /* @__PURE__ */ new WeakMap();
274
+ /**
275
+ * The state source action implementation
276
+ * @param context - Store context providing access to state and instance
277
+ * @param params - Parameters provided when invoking the bound action
278
+ */
279
+ function stateSourceAction(context, ...params) {
280
+ let { state, instance } = context, getCurrent = (currentState) => {
281
+ if (typeof currentState != "object" || !currentState) throw Error(`Expected store state to be an object but got "${typeof currentState}" instead`);
282
+ let instanceCache = selectorContextCache.get(currentState);
283
+ instanceCache || (instanceCache = /* @__PURE__ */ new WeakMap(), selectorContextCache.set(currentState, instanceCache));
284
+ let selectorContext = instanceCache.get(instance);
285
+ return selectorContext || (selectorContext = {
286
+ state: currentState,
287
+ instance
288
+ }, instanceCache.set(instance, selectorContext)), selector(selectorContext, ...params);
289
+ }, values = state.observable.pipe(map(getCurrent), distinctUntilChanged(isEqual));
290
+ subscribeHandler && (values = withSubscribeHook(values, () => subscribeHandler(context, ...params)));
291
+ let sharedValues = values.pipe(shareReplay({
292
+ bufferSize: 1,
293
+ refCount: !0
294
+ })), subscribe = (onStoreChanged) => {
295
+ let subscription = sharedValues.pipe(skip(1)).subscribe({
296
+ next: () => onStoreChanged?.(),
297
+ error: () => onStoreChanged?.()
298
+ });
299
+ return () => {
300
+ subscription.unsubscribe();
301
+ };
302
+ };
303
+ return {
304
+ getCurrent: () => getCurrent(state.get()),
305
+ subscribe,
306
+ observable: sharedValues
307
+ };
308
+ }
309
+ return stateSourceAction;
310
+ }
311
+ /**
312
+ * Creates a new Observable which wraps an existing Observable which will invoke
313
+ * the function when a new subscriber appears.
314
+ */
315
+ function withSubscribeHook(obs, fn) {
316
+ return defer(() => {
317
+ let cleanup = fn();
318
+ return cleanup ? obs.pipe(finalize(() => cleanup())) : obs;
319
+ });
320
+ }
321
+ /**
322
+ * Returns the staging API host if the `__SANITY_STAGING__` build-time flag is
323
+ * set to `true` (mirroring how Sanity Studio detects staging builds).
324
+ *
325
+ * @internal
326
+ */
327
+ function getStagingApiHost() {
328
+ if (typeof __SANITY_STAGING__ < "u" && __SANITY_STAGING__ === !0) return "https://api.sanity.work";
329
+ }
330
+ const loggers = /* @__PURE__ */ new WeakMap();
331
+ /**
332
+ * Returns the auth logger for a Sanity instance, creating it on first use.
333
+ *
334
+ * The logger is cached per instance so repeated action calls (logout,
335
+ * setAuthToken, handleAuthCallback) reuse the same logger instead of
336
+ * rebuilding it. The instance details are nested under `instanceContext`,
337
+ * which is what the logger's formatter reads to render the
338
+ * `[project:x] [dataset:y] [instance:z]` prefix.
339
+ *
340
+ * @internal
341
+ */
342
+ function getAuthLogger(instance) {
343
+ let logger = loggers.get(instance);
344
+ return logger || (logger = createLogger("auth", { instanceContext: {
345
+ instanceId: instance.instanceId,
346
+ projectId: instance.config.projectId,
347
+ dataset: instance.config.dataset
348
+ } }), loggers.set(instance, logger)), logger;
349
+ }
350
+ const DEFAULT_BASE = "http://localhost", DEFAULT_API_VERSION = "2021-06-07", REQUEST_TAG_PREFIX = "sanity.sdk.auth";
351
+ /**
352
+ * Determines the auth mode from instance config and environment.
353
+ *
354
+ * Priority:
355
+ * 1. `studio` config provided → `'studio'`
356
+ * 2. `studioMode.enabled` in config (deprecated) → `'studio'`
357
+ * 3. Dashboard context detected (`_context` URL param with content) → `'dashboard'`
358
+ * 4. Otherwise → `'standalone'`
359
+ *
360
+ * @internal
361
+ */
362
+ function resolveAuthMode(config, locationHref) {
363
+ return isStudioConfig(config) ? "studio" : detectDashboardContext(locationHref) ? "dashboard" : "standalone";
364
+ }
365
+ /**
366
+ * Returns `true` when the config indicates the SDK is running inside a Studio.
367
+ * Checks the new `studio` field first, then falls back to the deprecated
368
+ * `studioMode.enabled` for backwards compatibility.
369
+ *
370
+ * @internal
371
+ */
372
+ function isStudioConfig(config) {
373
+ return !!config.studio || !!config.studioMode?.enabled;
374
+ }
375
+ /**
376
+ * Checks whether the given location href contains a `_context` URL parameter
377
+ * with a non-empty JSON object, indicating the SDK is running inside the
378
+ * Sanity Dashboard.
379
+ *
380
+ * @internal
381
+ */
382
+ function detectDashboardContext(locationHref) {
383
+ try {
384
+ let contextParam = new URL(locationHref, DEFAULT_BASE).searchParams.get("_context");
385
+ if (!contextParam) return !1;
386
+ let parsed = JSON.parse(contextParam);
387
+ return typeof parsed == "object" && !!parsed && !Array.isArray(parsed) && Object.keys(parsed).length > 0;
388
+ } catch (err) {
389
+ return console.error("Failed to parse dashboard context from initial location:", err), !1;
390
+ }
391
+ }
392
+ /**
393
+ * Represents the various states the authentication type can be in.
394
+ *
395
+ * @public
396
+ */
397
+ let AuthStateType = /* @__PURE__ */ function(AuthStateType) {
398
+ return AuthStateType.LOGGED_IN = "logged-in", AuthStateType.LOGGING_IN = "logging-in", AuthStateType.ERROR = "error", AuthStateType.LOGGED_OUT = "logged-out", AuthStateType;
399
+ }({});
400
+ const REFRESH_INTERVAL = 432e5;
401
+ function createTokenRefreshStream(token, clientFactory, apiHost) {
402
+ return new Observable((subscriber) => {
403
+ let subscription = clientFactory({
404
+ apiVersion: DEFAULT_API_VERSION,
405
+ requestTagPrefix: REQUEST_TAG_PREFIX,
406
+ useProjectHostname: !1,
407
+ useCdn: !1,
408
+ token,
409
+ ignoreBrowserTokenWarning: !0,
410
+ ...apiHost && { apiHost }
411
+ }).observable.request({
412
+ uri: "auth/refresh-token",
413
+ method: "POST",
414
+ tag: "refresh-token",
415
+ body: { token }
416
+ }).subscribe(subscriber);
417
+ return () => subscription.unsubscribe();
418
+ });
419
+ }
420
+ function shouldRefreshToken(lastRefresh) {
421
+ return !lastRefresh || Date.now() - lastRefresh >= REFRESH_INTERVAL;
422
+ }
423
+ /**
424
+ * Periodically refreshes stamped auth tokens (those containing `-st`).
425
+ *
426
+ * Two triggers cause a refresh while the user stays logged in:
427
+ * - a 12-hour interval timer (only fires while the tab is visible)
428
+ * - the tab becoming visible again after `lastTokenRefresh` has gone stale
429
+ *
430
+ * Non-stamped tokens (e.g. personal access tokens) are never refreshed.
431
+ *
432
+ * @internal
433
+ */
434
+ const refreshStampedToken = ({ state, instance }) => {
435
+ let logger = getAuthLogger(instance), { clientFactory, apiHost, storageArea, storageKey } = state.get().options;
436
+ 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) => {
437
+ let visibilityHandler = () => {
438
+ let currentState = state.get();
439
+ document.visibilityState === "visible" && currentState.authState.type === "logged-in" && shouldRefreshToken(currentState.authState.lastTokenRefresh) && createTokenRefreshStream(currentState.authState.token, clientFactory, apiHost).subscribe({
440
+ next: (response) => {
441
+ state.set("setRefreshStampedToken", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
442
+ ...prev.authState,
443
+ token: response.token,
444
+ lastTokenRefresh: Date.now()
445
+ } : prev.authState })), subscriber.next(response);
446
+ },
447
+ error: (error) => subscriber.error(error)
448
+ });
449
+ }, timerSubscription = timer(REFRESH_INTERVAL, REFRESH_INTERVAL).pipe(filter(() => document.visibilityState === "visible"), switchMap(() => {
450
+ let currentState = state.get().authState;
451
+ if (currentState.type !== "logged-in") throw Error("User logged out before refresh could complete");
452
+ return createTokenRefreshStream(currentState.token, clientFactory, apiHost);
453
+ })).subscribe({
454
+ next: (response) => {
455
+ state.set("setRefreshStampedToken", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
456
+ ...prev.authState,
457
+ token: response.token,
458
+ lastTokenRefresh: Date.now()
459
+ } : prev.authState })), subscriber.next(response);
460
+ },
461
+ error: (error) => subscriber.error(error)
462
+ });
463
+ return document.addEventListener("visibilitychange", visibilityHandler), () => {
464
+ document.removeEventListener("visibilitychange", visibilityHandler), timerSubscription.unsubscribe();
465
+ };
466
+ }).pipe(takeWhile(() => state.get().authState.type === "logged-in"), map((response) => ({ token: response.token }))))).subscribe({
467
+ next: (response) => {
468
+ logger.debug("Token refresh completed, updating state"), state.set("setRefreshStampedToken", (prev) => ({ authState: prev.authState.type === "logged-in" ? {
469
+ ...prev.authState,
470
+ token: response.token,
471
+ lastTokenRefresh: Date.now()
472
+ } : prev.authState })), storageArea?.setItem(storageKey, JSON.stringify({ token: response.token }));
473
+ },
474
+ error: (error) => {
475
+ logger.error("Token refresh failed", { error }), state.set("setRefreshStampedTokenError", { authState: {
476
+ type: "error",
477
+ error
478
+ } });
479
+ }
480
+ });
481
+ }, subscribeToStateAndFetchCurrentUser = ({ state, instance }, fetchOptions) => {
482
+ let logger = getAuthLogger(instance), { clientFactory, apiHost } = state.get().options, useProjectHostname = fetchOptions?.useProjectHostname ?? isStudioConfig(instance.config), projectId = instance.config.projectId;
483
+ return state.observable.pipe(map(({ authState, options: storeOptions }) => ({
484
+ authState,
485
+ authMethod: storeOptions.authMethod
486
+ })), filter((value) => value.authState.type === "logged-in" && !value.authState.currentUser), map((value) => ({
487
+ token: value.authState.token,
488
+ authMethod: value.authMethod
489
+ })), distinctUntilChanged((prev, curr) => prev.token === curr.token && prev.authMethod === curr.authMethod)).pipe(map(({ token, authMethod }) => clientFactory({
490
+ apiVersion: DEFAULT_API_VERSION,
491
+ requestTagPrefix: REQUEST_TAG_PREFIX,
492
+ token: authMethod === "cookie" ? void 0 : token,
493
+ ignoreBrowserTokenWarning: !0,
494
+ useProjectHostname,
495
+ useCdn: !1,
496
+ ...authMethod === "cookie" ? { withCredentials: !0 } : {},
497
+ ...useProjectHostname && projectId ? { projectId } : {},
498
+ ...apiHost && { apiHost }
499
+ })), switchMap((client) => client.observable.request({
500
+ uri: "/users/me",
501
+ method: "GET",
502
+ tag: "users.get-current"
503
+ }).pipe(
504
+ /**
505
+ * Catch inside switchMap so the outer subscription survives.
506
+ * Without this, a 401 terminates the subscription permanently
507
+ * and subsequent token refreshes via comlink never re-fetch /users/me.
508
+ * @see SDK-1409
509
+ */
510
+ catchError((error) => (logger.error("Failed to fetch current user", { error }), state.set("setError", { authState: {
511
+ type: "error",
512
+ error
513
+ } }), EMPTY))
514
+ ))).subscribe({ next: (currentUser) => {
515
+ 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" ? {
516
+ ...prev.authState,
517
+ currentUser
518
+ } : prev.authState }));
519
+ } });
520
+ };
521
+ /**
522
+ * Creates a properly initialized {@link LoggedInAuthState}.
523
+ *
524
+ * For stamped tokens (containing `"-st"`), `lastTokenRefresh` is set to
525
+ * `Date.now()` so that the visibility-change handler in
526
+ * {@link refreshStampedToken} does not trigger an unnecessary refresh the
527
+ * first time the tab becomes visible.
528
+ *
529
+ * @param token - The auth token.
530
+ * @param currentUser - The current user, or `null` if not yet fetched.
531
+ * @param existingLastTokenRefresh - An existing timestamp to preserve
532
+ * (e.g. when updating a token while keeping the previous refresh time).
533
+ * @internal
534
+ */
535
+ function createLoggedInAuthState(token, currentUser, existingLastTokenRefresh) {
536
+ let isStampedToken = token.includes("-st"), lastTokenRefresh = existingLastTokenRefresh ?? (isStampedToken ? Date.now() : void 0);
537
+ return {
538
+ type: "logged-in",
539
+ token,
540
+ currentUser,
541
+ ...lastTokenRefresh !== void 0 && { lastTokenRefresh }
542
+ };
543
+ }
544
+ function getAuthCode(callbackUrl, locationHref) {
545
+ 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");
546
+ if (!authCode) {
547
+ let contextParam = new URLSearchParams(loc.search).get("_context");
548
+ if (contextParam) try {
549
+ let parsedContext = JSON.parse(contextParam);
550
+ parsedContext && typeof parsedContext == "object" && typeof parsedContext.sid == "string" && parsedContext.sid && (authCode = parsedContext.sid);
551
+ } catch {}
552
+ }
553
+ return authCode && callbackLocationMatches ? authCode : null;
554
+ }
555
+ function getTokenFromLocation(locationHref) {
556
+ let loc = new URL(locationHref);
557
+ return new URLSearchParams(loc.hash.slice(1)).get("token") || null;
558
+ }
559
+ /**
560
+ * Attempts to retrieve a token from the configured storage.
561
+ * If invalid or not present, returns null.
562
+ */
563
+ function getTokenFromStorage(storageArea, storageKey) {
564
+ if (!storageArea) return null;
565
+ let item = storageArea.getItem(storageKey);
566
+ if (item === null) return null;
567
+ try {
568
+ let parsed = JSON.parse(item);
569
+ if (typeof parsed != "object" || !parsed || !("token" in parsed) || typeof parsed.token != "string") throw Error("Invalid stored auth data structure");
570
+ return parsed.token;
571
+ } catch {
572
+ return storageArea.removeItem(storageKey), null;
573
+ }
574
+ }
575
+ /**
576
+ * Creates an observable stream of storage events. If not in a browser environment,
577
+ * returns an EMPTY observable.
578
+ */
579
+ function getStorageEvents() {
580
+ return typeof window < "u" && typeof window.addEventListener == "function" ? fromEvent(window, "storage") : EMPTY;
581
+ }
582
+ /**
583
+ * Returns a default storage instance (localStorage) if available.
584
+ * If not available or an error occurs, returns undefined.
585
+ */
586
+ function getDefaultStorage() {
587
+ try {
588
+ return typeof localStorage < "u" && typeof localStorage.getItem == "function" ? localStorage : void 0;
589
+ } catch {
590
+ return;
591
+ }
592
+ }
593
+ /**
594
+ * Returns the default location to use.
595
+ * Tries accessing `location.href`, falls back to a default base if not available or on error.
596
+ */
597
+ function getDefaultLocation() {
598
+ try {
599
+ return typeof location > "u" ? DEFAULT_BASE : typeof location.href == "string" ? location.href : DEFAULT_BASE;
600
+ } catch {
601
+ return DEFAULT_BASE;
602
+ }
603
+ }
604
+ /**
605
+ * Cleans up the URL by removing the `token` from the hash and the `sid` and `url` search params.
606
+ * @internal
607
+ */
608
+ function getCleanedUrl(locationUrl) {
609
+ let loc = new URL(locationUrl), rawHash = loc.hash.startsWith("#") ? loc.hash.slice(1) : loc.hash;
610
+ if (rawHash && rawHash.includes("=")) {
611
+ let hashParams = new URLSearchParams(rawHash);
612
+ hashParams.delete("token"), hashParams.delete("withSid");
613
+ let nextHash = hashParams.toString();
614
+ loc.hash = nextHash ? `#${nextHash}` : "";
615
+ }
616
+ return loc.searchParams.delete("sid"), loc.searchParams.delete("url"), loc.toString();
617
+ }
618
+ /** @internal Extracts the structured API error body from a ClientError, if present. */
619
+ function getClientErrorApiBody(error) {
620
+ let body = error.response?.body;
621
+ return body && typeof body == "object" ? body : void 0;
622
+ }
623
+ /** @internal Returns the error type string from an API error body, if available. */
624
+ function getClientErrorApiType(error) {
625
+ let body = getClientErrorApiBody(error);
626
+ return body?.error?.type ?? body?.type;
627
+ }
628
+ /** @internal Returns the error description string from an API error body, if available. */
629
+ function getClientErrorApiDescription(error) {
630
+ let body = getClientErrorApiBody(error);
631
+ return body?.error?.description ?? body?.description;
632
+ }
633
+ /** @internal True if the error represents a projectUserNotFoundError. */
634
+ function isProjectUserNotFoundClientError(error) {
635
+ return getClientErrorApiType(error) === "projectUserNotFoundError";
636
+ }
637
+ /**
638
+ * Parses the dashboard context from a location href's `_context` URL parameter.
639
+ * Strips the `sid` property from the parsed context (it's handled separately).
640
+ *
641
+ * @internal
642
+ */
643
+ function parseDashboardContext(locationHref) {
644
+ try {
645
+ let contextParam = new URL(locationHref, DEFAULT_BASE).searchParams.get("_context");
646
+ if (contextParam) {
647
+ let parsedContext = JSON.parse(contextParam);
648
+ if (parsedContext && typeof parsedContext == "object" && !Array.isArray(parsedContext) && Object.keys(parsedContext).length > 0) return delete parsedContext.sid, parsedContext;
649
+ }
650
+ } catch (err) {
651
+ console.error("Failed to parse dashboard context from initial location:", err);
652
+ }
653
+ return {};
654
+ }
655
+ /**
656
+ * Resolves the initial auth state for Dashboard mode.
657
+ *
658
+ * In dashboard mode the token is provided by the parent frame via Comlink,
659
+ * so the SDK starts in a `LOGGED_OUT` state and waits for the token to arrive.
660
+ * The `_context` URL parameter provides dashboard metadata (orgId, mode, env).
661
+ *
662
+ * A provided token (`auth.token`) or an auth code/callback still takes
663
+ * precedence, even when running inside the dashboard.
664
+ *
665
+ * @internal
666
+ */
667
+ function getDashboardInitialState(options) {
668
+ let { authConfig, initialLocationHref } = options, providedToken = authConfig.token, callbackUrl = authConfig.callbackUrl, storageKey = "__sanity_auth_token", dashboardContext = parseDashboardContext(initialLocationHref);
669
+ return providedToken ? {
670
+ authState: createLoggedInAuthState(providedToken, null),
671
+ storageKey,
672
+ storageArea: void 0,
673
+ authMethod: void 0,
674
+ dashboardContext
675
+ } : getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref) ? {
676
+ authState: {
677
+ type: "logging-in",
678
+ isExchangingToken: !1
679
+ },
680
+ storageKey,
681
+ storageArea: void 0,
682
+ authMethod: void 0,
683
+ dashboardContext
684
+ } : {
685
+ authState: {
686
+ type: "logged-out",
687
+ isDestroyingSession: !1
688
+ },
689
+ storageKey,
690
+ storageArea: void 0,
691
+ authMethod: void 0,
692
+ dashboardContext
693
+ };
694
+ }
695
+ /**
696
+ * Initialize Dashboard auth subscriptions:
697
+ * - Subscribe to state changes and fetch current user
698
+ * - Start stamped token refresh
699
+ *
700
+ * Note: storage events are NOT subscribed in dashboard mode since
701
+ * the dashboard does not use localStorage for token persistence.
702
+ *
703
+ * @internal
704
+ */
705
+ function initializeDashboardAuth(context, tokenRefresherRunning) {
706
+ let subscriptions = [], startedRefresher = !1;
707
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !1 })), tokenRefresherRunning || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
708
+ dispose: () => {
709
+ for (let subscription of subscriptions) subscription.unsubscribe();
710
+ },
711
+ tokenRefresherStarted: startedRefresher
712
+ };
713
+ }
714
+ const subscribeToStorageEventsAndSetToken = ({ state }) => {
715
+ let { storageArea, storageKey } = state.get().options;
716
+ return defer(getStorageEvents).pipe(filter((e) => e.storageArea === storageArea && e.key === storageKey), map(() => getTokenFromStorage(storageArea, storageKey)), distinctUntilChanged()).subscribe((token) => {
717
+ state.set("updateTokenFromStorageEvent", { authState: token ? createLoggedInAuthState(token, null) : {
718
+ type: "logged-out",
719
+ isDestroyingSession: !1
720
+ } });
721
+ });
722
+ };
723
+ /**
724
+ * Resolves the initial auth state for Standalone mode.
725
+ *
726
+ * Token discovery order:
727
+ * 1. Provided token (`auth.token` in config)
728
+ * 2. Auth code or token from location (OAuth callback)
729
+ * 3. localStorage: `__sanity_auth_token`
730
+ * 4. Falls back to `LOGGED_OUT`
731
+ *
732
+ * @internal
733
+ */
734
+ function getStandaloneInitialState(options) {
735
+ let { authConfig, initialLocationHref } = options, providedToken = authConfig.token, callbackUrl = authConfig.callbackUrl, storageKey = "__sanity_auth_token", storageArea = authConfig.storageArea ?? getDefaultStorage();
736
+ if (providedToken) return {
737
+ authState: createLoggedInAuthState(providedToken, null),
738
+ storageKey,
739
+ storageArea,
740
+ authMethod: void 0,
741
+ dashboardContext: {}
742
+ };
743
+ if (getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref)) return {
744
+ authState: {
745
+ type: "logging-in",
746
+ isExchangingToken: !1
747
+ },
748
+ storageKey,
749
+ storageArea,
750
+ authMethod: void 0,
751
+ dashboardContext: {}
752
+ };
753
+ let token = getTokenFromStorage(storageArea, storageKey);
754
+ return token ? {
755
+ authState: createLoggedInAuthState(token, null),
756
+ storageKey,
757
+ storageArea,
758
+ authMethod: "localstorage",
759
+ dashboardContext: {}
760
+ } : {
761
+ authState: {
762
+ type: "logged-out",
763
+ isDestroyingSession: !1
764
+ },
765
+ storageKey,
766
+ storageArea,
767
+ authMethod: void 0,
768
+ dashboardContext: {}
769
+ };
770
+ }
771
+ /**
772
+ * Initialize Standalone auth subscriptions:
773
+ * - Subscribe to state changes and fetch current user
774
+ * - Subscribe to storage events for token key
775
+ * - Start stamped token refresh
776
+ *
777
+ * @internal
778
+ */
779
+ function initializeStandaloneAuth(context, tokenRefresherRunning) {
780
+ let subscriptions = [], startedRefresher = !1;
781
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !1 })), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context)), tokenRefresherRunning || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
782
+ dispose: () => {
783
+ for (let subscription of subscriptions) subscription.unsubscribe();
784
+ },
785
+ tokenRefresherStarted: startedRefresher
786
+ };
787
+ }
788
+ /**
789
+ * Attempts to check for cookie auth by making a withCredentials request to the users endpoint.
790
+ * @param projectId - The project ID to check for cookie auth.
791
+ * @param clientFactory - A factory function that creates a Sanity client.
792
+ * @returns True if the user is authenticated, false otherwise.
793
+ * @internal
794
+ */
795
+ async function checkForCookieAuth(projectId, clientFactory) {
796
+ if (!projectId) return !1;
797
+ try {
798
+ let user = await clientFactory({
799
+ projectId,
800
+ useCdn: !1,
801
+ requestTagPrefix: REQUEST_TAG_PREFIX,
802
+ timeout: 1e4
803
+ }).request({
804
+ uri: "/users/me",
805
+ withCredentials: !0,
806
+ tag: "users.get-current"
807
+ });
808
+ return typeof user == "object" && !!user && typeof user.id == "string";
809
+ } catch {
810
+ return !1;
811
+ }
812
+ }
813
+ /**
814
+ * Attempts to retrieve a studio token from local storage.
815
+ * @param storageArea - The storage area to retrieve the token from.
816
+ * @param storageKey - The storage key to retrieve the token from.
817
+ * @returns The studio token or null if it does not exist.
818
+ * @internal
819
+ */
820
+ function getStudioTokenFromLocalStorage(storageArea, storageKey) {
821
+ return !storageArea || !storageKey ? null : getTokenFromStorage(storageArea, storageKey) || null;
822
+ }
823
+ /**
824
+ * Resolves the initial auth state for Studio mode.
825
+ *
826
+ * When a `tokenSource` is provided (via `config.studio.auth.token`), the
827
+ * initial state is `LOGGING_IN` — the actual token arrives asynchronously
828
+ * via the subscription set up in `initializeStudioAuth`.
829
+ *
830
+ * Fallback token discovery order (no tokenSource):
831
+ * 1. Provided token (`auth.token` in config)
832
+ * 2. localStorage: `__studio_auth_token_${projectId}`
833
+ * 3. Falls back to `LOGGED_OUT` (cookie auth is checked async in `initializeStudioAuth`)
834
+ *
835
+ * @internal
836
+ */
837
+ function getStudioInitialState(options) {
838
+ let { authConfig, projectId, tokenSource } = options, storageArea = authConfig.storageArea ?? getDefaultStorage(), studioStorageKey = `__studio_auth_token_${projectId ?? ""}`;
839
+ if (tokenSource) return {
840
+ authState: {
841
+ type: "logging-in",
842
+ isExchangingToken: !1
843
+ },
844
+ storageKey: studioStorageKey,
845
+ storageArea,
846
+ authMethod: void 0,
847
+ dashboardContext: {}
848
+ };
849
+ let providedToken = authConfig.token, authMethod, token = getStudioTokenFromLocalStorage(storageArea, studioStorageKey);
850
+ return token && (authMethod = "localstorage"), providedToken ? {
851
+ authState: createLoggedInAuthState(providedToken, null),
852
+ storageKey: studioStorageKey,
853
+ storageArea,
854
+ authMethod,
855
+ dashboardContext: {}
856
+ } : token ? {
857
+ authState: createLoggedInAuthState(token, null),
858
+ storageKey: studioStorageKey,
859
+ storageArea,
860
+ authMethod: "localstorage",
861
+ dashboardContext: {}
862
+ } : {
863
+ authState: {
864
+ type: "logged-out",
865
+ isDestroyingSession: !1
866
+ },
867
+ storageKey: studioStorageKey,
868
+ storageArea,
869
+ authMethod: void 0,
870
+ dashboardContext: {}
871
+ };
872
+ }
873
+ /**
874
+ * Initialize Studio auth subscriptions.
875
+ *
876
+ * When a `tokenSource` is available (reactive path), subscribes to it for
877
+ * ongoing token sync. The Studio is the token authority — no independent
878
+ * token refresh or cookie auth probing is needed.
879
+ *
880
+ * Fallback path (no tokenSource):
881
+ * - Subscribe to state changes and fetch current user
882
+ * - Subscribe to storage events for studio token key
883
+ * - Check for cookie auth asynchronously if no token was found
884
+ * - Start stamped token refresh
885
+ *
886
+ * @internal
887
+ */
888
+ function initializeStudioAuth(context, tokenRefresherRunning) {
889
+ let tokenSource = context.instance.config.studio?.auth?.token;
890
+ return tokenSource ? initializeWithTokenSource(context, tokenSource) : initializeWithFallback(context, tokenRefresherRunning);
891
+ }
892
+ /**
893
+ * Subscribe to a reactive token source from the Studio workspace.
894
+ *
895
+ * When the token source emits a non-null token, the SDK uses it directly.
896
+ * When it emits `null`, the behavior depends on the `authenticated` flag
897
+ * from the Studio's workspace config:
898
+ *
899
+ * - `authenticated: true` — the Studio has already verified the user is
900
+ * logged in (e.g. via cookie auth). The SDK treats the null token as
901
+ * cookie-based auth and stays in the LOGGED_IN state.
902
+ *
903
+ * - `authenticated` absent/false — the user is genuinely not authenticated;
904
+ * transition to LOGGED_OUT.
905
+ *
906
+ * No async cookie probing is needed here because this code path only runs
907
+ * when a Studio provides SDKStudioContext, and the Studio's Workspace type
908
+ * always includes `authenticated`. The async `checkForCookieAuth` fallback
909
+ * remains in `initializeWithFallback` for the non-Studio path.
910
+ */
911
+ function initializeWithTokenSource(context, tokenSource) {
912
+ let subscriptions = [], studioAuthenticated = context.instance.config.studio?.authenticated === !0;
913
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !0 }));
914
+ let tokenSub = tokenSource.subscribe({ next: (token) => {
915
+ let { state } = context;
916
+ token ? state.set("studioTokenSource", (prev) => ({
917
+ options: {
918
+ ...prev.options,
919
+ authMethod: void 0
920
+ },
921
+ authState: createLoggedInAuthState(token, null)
922
+ })) : studioAuthenticated ? state.set("studioTokenSourceCookieAuth", (prev) => ({
923
+ options: {
924
+ ...prev.options,
925
+ authMethod: "cookie"
926
+ },
927
+ authState: prev.authState.type === "logged-in" ? prev.authState : createLoggedInAuthState("", null)
928
+ })) : state.set("studioTokenSourceLoggedOut", (prev) => ({
929
+ options: {
930
+ ...prev.options,
931
+ authMethod: void 0
932
+ },
933
+ authState: {
934
+ type: "logged-out",
935
+ isDestroyingSession: !1
936
+ }
937
+ }));
938
+ } });
939
+ return {
940
+ dispose: () => {
941
+ tokenSub.unsubscribe();
942
+ for (let subscription of subscriptions) subscription.unsubscribe();
943
+ },
944
+ tokenRefresherStarted: !1
945
+ };
946
+ }
947
+ /**
948
+ * Fallback initialization when no reactive token source is available.
949
+ * Uses localStorage/cookie discovery (existing behavior).
950
+ */
951
+ function initializeWithFallback(context, tokenRefresherRunning) {
952
+ let subscriptions = [], startedRefresher = !1;
953
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !0 })), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context));
954
+ try {
955
+ let { instance, state } = context;
956
+ if (!(state.get().authState?.type === "logged-in" && state.get().authState.token)) {
957
+ let projectIdValue = instance.config.projectId, clientFactory = state.get().options.clientFactory;
958
+ checkForCookieAuth(projectIdValue, clientFactory).then((isCookieAuthEnabled) => {
959
+ isCookieAuthEnabled && state.set("enableCookieAuth", (prev) => ({
960
+ options: {
961
+ ...prev.options,
962
+ authMethod: "cookie"
963
+ },
964
+ authState: prev.authState.type === "logged-in" ? prev.authState : createLoggedInAuthState("", null)
965
+ }));
966
+ });
967
+ }
968
+ } catch {}
969
+ return tokenRefresherRunning || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
970
+ dispose: () => {
971
+ for (let subscription of subscriptions) subscription.unsubscribe();
972
+ },
973
+ tokenRefresherStarted: startedRefresher
974
+ };
975
+ }
976
+ var authStore_exports = /* @__PURE__ */ __exportAll({
977
+ authStore: () => authStore,
978
+ getAuthMethodState: () => getAuthMethodState,
979
+ getAuthState: () => getAuthState,
980
+ getCurrentUserState: () => getCurrentUserState,
981
+ getDashboardOrganizationId: () => getDashboardOrganizationId,
982
+ getIsInDashboardState: () => getIsInDashboardState,
983
+ getLoginUrlState: () => getLoginUrlState,
984
+ getTokenState: () => getTokenState,
985
+ setAuthToken: () => setAuthToken
986
+ });
987
+ let tokenRefresherRunning = !1;
988
+ const authStore = defineStore({
989
+ name: "Auth",
990
+ getInitialState(instance) {
991
+ let logger = getAuthLogger(instance);
992
+ logger.debug("Initializing auth store", {
993
+ hasProvidedToken: !!instance.config.auth?.token,
994
+ hasCustomProviders: !!(instance.config.auth?.providers && instance.config.auth.providers.length > 0),
995
+ studioMode: instance.config.studioMode?.enabled ?? !1
996
+ });
997
+ 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";
998
+ try {
999
+ apiHost && new URL(apiHost).hostname.endsWith(".sanity.work") && (loginDomain = "https://www.sanity.work");
1000
+ } catch {}
1001
+ let loginUrl = new URL("/login", loginDomain);
1002
+ loginUrl.searchParams.set("origin", getCleanedUrl(initialLocationHref)), loginUrl.searchParams.set("type", "stampedToken"), loginUrl.searchParams.set("withSid", "true");
1003
+ let mode = resolveAuthMode(instance.config, initialLocationHref), strategyOptions = {
1004
+ authConfig,
1005
+ projectId: instance.config.projectId,
1006
+ initialLocationHref,
1007
+ clientFactory,
1008
+ tokenSource: instance.config.studio?.auth?.token
1009
+ }, result;
1010
+ switch (mode) {
1011
+ case "studio":
1012
+ result = getStudioInitialState(strategyOptions);
1013
+ break;
1014
+ case "dashboard":
1015
+ result = getDashboardInitialState(strategyOptions);
1016
+ break;
1017
+ case "standalone": result = getStandaloneInitialState(strategyOptions);
1018
+ }
1019
+ return logger.debug("Auth state initialized", {
1020
+ authStateType: result.authState.type,
1021
+ mode,
1022
+ authMethod: result.authMethod
1023
+ }), {
1024
+ authState: result.authState,
1025
+ dashboardContext: result.dashboardContext,
1026
+ options: {
1027
+ apiHost,
1028
+ loginUrl: loginUrl.toString(),
1029
+ callbackUrl,
1030
+ customProviders,
1031
+ providedToken,
1032
+ clientFactory,
1033
+ initialLocationHref,
1034
+ storageKey: result.storageKey,
1035
+ storageArea: result.storageArea,
1036
+ authMethod: result.authMethod
1037
+ }
1038
+ };
1039
+ },
1040
+ initialize(context) {
1041
+ let logger = getAuthLogger(context.instance), initialLocationHref = context.state.get().options?.initialLocationHref ?? getDefaultLocation(), mode = resolveAuthMode(context.instance.config, initialLocationHref);
1042
+ logger.debug("Setting up auth subscriptions", { mode });
1043
+ let initResult;
1044
+ switch (mode) {
1045
+ case "studio":
1046
+ initResult = initializeStudioAuth(context, tokenRefresherRunning);
1047
+ break;
1048
+ case "dashboard":
1049
+ initResult = initializeDashboardAuth(context, tokenRefresherRunning);
1050
+ break;
1051
+ case "standalone": initResult = initializeStandaloneAuth(context, tokenRefresherRunning);
1052
+ }
1053
+ return initResult.tokenRefresherStarted && (tokenRefresherRunning = !0), () => {
1054
+ logger.debug("Cleaning up auth subscriptions"), initResult.dispose();
1055
+ };
1056
+ }
1057
+ }), 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) => {
1058
+ let logger = getAuthLogger(instance), currentAuthState = state.get().authState;
1059
+ if (token) {
1060
+ if (currentAuthState.type !== "logged-in" || currentAuthState.token !== token) {
1061
+ logger.info("Setting auth token");
1062
+ let currentUser = currentAuthState.type === "logged-in" ? currentAuthState.currentUser : null, preservedLastTokenRefresh = currentAuthState.type === "logged-in" ? currentAuthState.lastTokenRefresh : void 0;
1063
+ state.set("setToken", { authState: createLoggedInAuthState(token, currentUser, preservedLastTokenRefresh) });
1064
+ }
1065
+ } else currentAuthState.type !== "logged-out" && (logger.info("Clearing auth token"), state.set("setToken", { authState: {
1066
+ type: "logged-out",
1067
+ isDestroyingSession: !1
1068
+ } }));
1069
+ });
1070
+ export { defineStore as A, REQUEST_TAG_PREFIX as C, bindActionByResource as D, createStateSourceAction as E, isDatasetSource as F, isMediaLibraryResource as I, isMediaLibrarySource as L, isCanvasResource as M, isCanvasSource as N, bindActionByResourceAndPerspective as O, isDatasetResource as P, 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, isReleasePerspective as j, bindActionGlobally as k, 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 };
1071
+
1072
+ //# sourceMappingURL=authStore-CWTAuieY.js.map