@sanity/sdk 2.6.0 → 2.7.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 (39) hide show
  1. package/dist/index.d.ts +124 -13
  2. package/dist/index.js +468 -243
  3. package/dist/index.js.map +1 -1
  4. package/package.json +5 -4
  5. package/src/_exports/index.ts +3 -0
  6. package/src/auth/authMode.test.ts +56 -0
  7. package/src/auth/authMode.ts +71 -0
  8. package/src/auth/authStore.test.ts +85 -4
  9. package/src/auth/authStore.ts +63 -125
  10. package/src/auth/authStrategy.ts +39 -0
  11. package/src/auth/dashboardAuth.ts +132 -0
  12. package/src/auth/standaloneAuth.ts +109 -0
  13. package/src/auth/studioAuth.ts +217 -0
  14. package/src/auth/studioModeAuth.test.ts +43 -1
  15. package/src/auth/studioModeAuth.ts +10 -1
  16. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +21 -6
  17. package/src/config/sanityConfig.ts +48 -7
  18. package/src/projection/getProjectionState.ts +6 -5
  19. package/src/projection/projectionQuery.test.ts +38 -55
  20. package/src/projection/projectionQuery.ts +27 -31
  21. package/src/projection/projectionStore.test.ts +4 -4
  22. package/src/projection/projectionStore.ts +3 -2
  23. package/src/projection/resolveProjection.ts +2 -2
  24. package/src/projection/statusQuery.test.ts +35 -0
  25. package/src/projection/statusQuery.ts +71 -0
  26. package/src/projection/subscribeToStateAndFetchBatches.test.ts +63 -50
  27. package/src/projection/subscribeToStateAndFetchBatches.ts +106 -27
  28. package/src/projection/types.ts +12 -0
  29. package/src/projection/util.ts +0 -1
  30. package/src/query/queryStore.test.ts +64 -0
  31. package/src/query/queryStore.ts +30 -10
  32. package/src/releases/getPerspectiveState.test.ts +17 -14
  33. package/src/releases/getPerspectiveState.ts +58 -38
  34. package/src/releases/releasesStore.test.ts +59 -61
  35. package/src/releases/releasesStore.ts +21 -35
  36. package/src/releases/utils/isReleasePerspective.ts +7 -0
  37. package/src/store/createActionBinder.test.ts +211 -1
  38. package/src/store/createActionBinder.ts +95 -17
  39. package/src/store/createSanityInstance.ts +3 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { Observable, share, map, distinctUntilChanged, skip, filter, exhaustMap, from, timer, switchMap, takeWhile, firstValueFrom, fromEvent, EMPTY, defer, asapScheduler, combineLatest, of, concatMap, withLatestFrom, concat, throwError, first as first$1, Subject, takeUntil, partition, merge, shareReplay, tap as tap$1, catchError as catchError$1, startWith as startWith$1, pairwise as pairwise$1, groupBy as groupBy$1, mergeMap as mergeMap$1, throttle, race, NEVER, Subscription, retry, debounceTime as debounceTime$1 } from "rxjs";
1
+ import { Observable, share, map, distinctUntilChanged, skip, filter, exhaustMap, from, timer, switchMap, takeWhile, firstValueFrom, fromEvent, EMPTY, defer, asapScheduler, combineLatest, of, concatMap, withLatestFrom, concat, throwError, first as first$1, Subject, takeUntil, partition, merge, shareReplay, tap as tap$1, catchError as catchError$1, startWith as startWith$1, pairwise as pairwise$1, groupBy as groupBy$1, mergeMap as mergeMap$1, throttle, race, NEVER, Subscription, debounceTime } from "rxjs";
2
2
  import { createClient, CorsOriginError } from "@sanity/client";
3
3
  import { pick, omit, isEqual, isObject } from "lodash-es";
4
4
  import { devtools } from "zustand/middleware";
5
5
  import { createStore } from "zustand/vanilla";
6
- import { first, switchMap as switchMap$1, groupBy, mergeMap, startWith, pairwise, filter as filter$1, map as map$1, delay, tap, catchError, scan, share as share$1, take, debounceTime } from "rxjs/operators";
6
+ import { first, switchMap as switchMap$1, groupBy, mergeMap, startWith, pairwise, filter as filter$1, map as map$1, delay, tap, catchError, scan, share as share$1 } from "rxjs/operators";
7
7
  import { createController, createNode } from "@sanity/comlink";
8
8
  import { createSelector } from "reselect";
9
9
  import { SanityEncoder } from "@sanity/mutate";
@@ -17,6 +17,7 @@ import { isKeySegment, isKeyedObject } from "@sanity/types";
17
17
  import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
18
18
  import { SDK_CHANNEL_NAME, SDK_NODE_NAME } from "@sanity/message-protocol";
19
19
  import { fromUrl } from "@sanity/bifur-client";
20
+ import { DocumentId, getPublishedId as getPublishedId$2, getDraftId as getDraftId$1, getVersionId, isDraftId, isVersionId, isPublishedId } from "@sanity/id-utils";
20
21
  function isDatasetSource(source) {
21
22
  return "projectId" in source && "dataset" in source;
22
23
  }
@@ -26,6 +27,7 @@ function isMediaLibrarySource(source) {
26
27
  function isCanvasSource(source) {
27
28
  return "canvasId" in source;
28
29
  }
30
+ const isReleasePerspective = (perspective) => typeof perspective == "object" && perspective !== null && "releaseName" in perspective;
29
31
  function getPublishedId(id) {
30
32
  const draftsPrefix = "drafts.";
31
33
  return id.startsWith(draftsPrefix) ? id.slice(draftsPrefix.length) : id;
@@ -166,7 +168,9 @@ function createSanityInstance(config = {}) {
166
168
  projectId: config.projectId,
167
169
  dataset: config.dataset,
168
170
  perspective: config.perspective,
169
- studioMode: config.studioMode?.enabled,
171
+ hasStudioConfig: !!config.studio,
172
+ hasStudioTokenSource: !!config.studio?.auth?.token,
173
+ legacyStudioMode: config.studioMode?.enabled,
170
174
  hasAuthProviders: !!config.auth?.providers,
171
175
  hasAuthToken: !!config.auth?.token
172
176
  });
@@ -279,16 +283,33 @@ const bindActionByDataset = createActionBinder((instance, options) => {
279
283
  if (!projectId || !dataset)
280
284
  throw new Error("This API requires a project ID and dataset configured.");
281
285
  return { name: `${projectId}.${dataset}`, projectId, dataset };
282
- }), bindActionBySource = createActionBinder((instance, { source }) => {
286
+ }), createSourceKey = (instance, source) => {
287
+ let name, sourceForKey;
283
288
  if (source) {
284
- let id;
285
- if (isDatasetSource(source) ? id = `${source.projectId}.${source.dataset}` : isMediaLibrarySource(source) ? id = `media-library:${source.mediaLibraryId}` : isCanvasSource(source) && (id = `canvas:${source.canvasId}`), !id) throw new Error(`Received invalid source: ${JSON.stringify(source)}`);
286
- return { name: id };
289
+ if (sourceForKey = source, isDatasetSource(source))
290
+ name = `${source.projectId}.${source.dataset}`;
291
+ else if (isMediaLibrarySource(source))
292
+ name = `media-library:${source.mediaLibraryId}`;
293
+ else if (isCanvasSource(source))
294
+ name = `canvas:${source.canvasId}`;
295
+ else
296
+ throw new Error(`Received invalid source: ${JSON.stringify(source)}`);
297
+ return { name, source: sourceForKey };
287
298
  }
288
299
  const { projectId, dataset } = instance.config;
289
300
  if (!projectId || !dataset)
290
301
  throw new Error("This API requires a project ID and dataset configured.");
291
- return { name: `${projectId}.${dataset}` };
302
+ return { name: `${projectId}.${dataset}`, source: { projectId, dataset } };
303
+ }, bindActionBySource = createActionBinder((instance, { source }) => createSourceKey(instance, source)), bindActionBySourceAndPerspective = createActionBinder((instance, options) => {
304
+ const { source, perspective } = options, utilizedPerspective = perspective ?? instance.config.perspective ?? "drafts";
305
+ let perspectiveKey;
306
+ isReleasePerspective(utilizedPerspective) ? perspectiveKey = utilizedPerspective.releaseName : typeof utilizedPerspective == "string" ? perspectiveKey = utilizedPerspective : perspectiveKey = JSON.stringify(utilizedPerspective);
307
+ const sourceKey = createSourceKey(instance, source);
308
+ return {
309
+ name: `${sourceKey.name}:${perspectiveKey}`,
310
+ source: sourceKey.source,
311
+ perspective: utilizedPerspective
312
+ };
292
313
  }), bindActionGlobally = createActionBinder((..._rest) => ({ name: "global" }));
293
314
  function createStateSourceAction(options) {
294
315
  const selector = typeof options == "function" ? options : options.selector, subscribeHandler = options && "onSubscribe" in options ? options.onSubscribe : void 0, isEqual2 = options && "isEqual" in options ? options.isEqual ?? Object.is : Object.is, selectorContextCache = /* @__PURE__ */ new WeakMap();
@@ -337,8 +358,25 @@ function createStateSourceAction(options) {
337
358
  }
338
359
  return stateSourceAction;
339
360
  }
361
+ const DEFAULT_BASE = "http://localhost", AUTH_CODE_PARAM = "sid", DEFAULT_API_VERSION$1 = "2021-06-07", REQUEST_TAG_PREFIX = "sanity.sdk.auth";
362
+ function resolveAuthMode(config, locationHref) {
363
+ return isStudioConfig(config) ? "studio" : detectDashboardContext(locationHref) ? "dashboard" : "standalone";
364
+ }
365
+ function isStudioConfig(config) {
366
+ return !!config.studio || !!config.studioMode?.enabled;
367
+ }
368
+ function detectDashboardContext(locationHref) {
369
+ try {
370
+ const contextParam = new URL(locationHref, DEFAULT_BASE).searchParams.get("_context");
371
+ if (!contextParam) return !1;
372
+ const parsed = JSON.parse(contextParam);
373
+ return typeof parsed == "object" && parsed !== null && !Array.isArray(parsed) && Object.keys(parsed).length > 0;
374
+ } catch (err) {
375
+ return console.error("Failed to parse dashboard context from initial location:", err), !1;
376
+ }
377
+ }
340
378
  var AuthStateType = /* @__PURE__ */ ((AuthStateType2) => (AuthStateType2.LOGGED_IN = "logged-in", AuthStateType2.LOGGING_IN = "logging-in", AuthStateType2.ERROR = "error", AuthStateType2.LOGGED_OUT = "logged-out", AuthStateType2))(AuthStateType || {});
341
- const DEFAULT_BASE = "http://localhost", AUTH_CODE_PARAM = "sid", DEFAULT_API_VERSION$1 = "2021-06-07", REQUEST_TAG_PREFIX = "sanity.sdk.auth", REFRESH_INTERVAL = 720 * 60 * 1e3, LOCK_NAME = "sanity-token-refresh-lock";
379
+ const REFRESH_INTERVAL = 720 * 60 * 1e3, LOCK_NAME = "sanity-token-refresh-lock";
342
380
  function getLastRefreshTime(storageArea, storageKey) {
343
381
  try {
344
382
  const data = storageArea?.getItem(`${storageKey}_last_refresh`), parsed = data ? parseInt(data, 10) : 0;
@@ -501,6 +539,47 @@ const refreshStampedToken = ({ state }) => {
501
539
  state.set("setRefreshStampedTokenError", { authState: { type: AuthStateType.ERROR, error } });
502
540
  }
503
541
  });
542
+ }, subscribeToStateAndFetchCurrentUser = ({ state, instance }, fetchOptions) => {
543
+ const { clientFactory, apiHost } = state.get().options, useProjectHostname = fetchOptions?.useProjectHostname ?? isStudioConfig(instance.config), projectId = instance.config.projectId;
544
+ return state.observable.pipe(
545
+ map(({ authState, options: storeOptions }) => ({
546
+ authState,
547
+ authMethod: storeOptions.authMethod
548
+ })),
549
+ filter(
550
+ (value) => value.authState.type === AuthStateType.LOGGED_IN && !value.authState.currentUser
551
+ ),
552
+ map((value) => ({ token: value.authState.token, authMethod: value.authMethod })),
553
+ distinctUntilChanged(
554
+ (prev, curr) => prev.token === curr.token && prev.authMethod === curr.authMethod
555
+ )
556
+ ).pipe(
557
+ map(
558
+ ({ token, authMethod }) => clientFactory({
559
+ apiVersion: DEFAULT_API_VERSION$1,
560
+ requestTagPrefix: REQUEST_TAG_PREFIX,
561
+ token: authMethod === "cookie" ? void 0 : token,
562
+ ignoreBrowserTokenWarning: !0,
563
+ useProjectHostname,
564
+ useCdn: !1,
565
+ ...authMethod === "cookie" ? { withCredentials: !0 } : {},
566
+ ...useProjectHostname && projectId ? { projectId } : {},
567
+ ...apiHost && { apiHost }
568
+ })
569
+ ),
570
+ switchMap(
571
+ (client) => client.observable.request({ uri: "/users/me", method: "GET" })
572
+ )
573
+ ).subscribe({
574
+ next: (currentUser) => {
575
+ state.set("setCurrentUser", (prev) => ({
576
+ authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, currentUser } : prev.authState
577
+ }));
578
+ },
579
+ error: (error) => {
580
+ state.set("setError", { authState: { type: AuthStateType.ERROR, error } });
581
+ }
582
+ });
504
583
  };
505
584
  function getAuthCode(callbackUrl, locationHref) {
506
585
  const loc = new URL(locationHref, DEFAULT_BASE), callbackLocation = callbackUrl ? new URL(callbackUrl, DEFAULT_BASE) : void 0, callbackLocationMatches = callbackLocation ? loc.pathname.toLowerCase().startsWith(callbackLocation.pathname.toLowerCase()) : !0;
@@ -575,66 +654,53 @@ function getClientErrorApiDescription(error) {
575
654
  function isProjectUserNotFoundClientError(error) {
576
655
  return getClientErrorApiType(error) === "projectUserNotFoundError";
577
656
  }
578
- async function checkForCookieAuth(projectId, clientFactory) {
579
- if (!projectId) return !1;
657
+ function parseDashboardContext(locationHref) {
580
658
  try {
581
- return typeof (await clientFactory({
582
- projectId,
583
- useCdn: !1
584
- }).request({
585
- uri: "/users/me",
586
- withCredentials: !0,
587
- tag: "users.get-current"
588
- }))?.id == "string";
589
- } catch {
590
- return !1;
659
+ const contextParam = new URL(locationHref, DEFAULT_BASE).searchParams.get("_context");
660
+ if (contextParam) {
661
+ const parsedContext = JSON.parse(contextParam);
662
+ if (parsedContext && typeof parsedContext == "object" && !Array.isArray(parsedContext) && Object.keys(parsedContext).length > 0)
663
+ return delete parsedContext.sid, parsedContext;
664
+ }
665
+ } catch (err) {
666
+ console.error("Failed to parse dashboard context from initial location:", err);
591
667
  }
668
+ return {};
669
+ }
670
+ function getDashboardInitialState(options) {
671
+ const { authConfig, initialLocationHref } = options, providedToken = authConfig.token, callbackUrl = authConfig.callbackUrl, storageKey = "__sanity_auth_token", dashboardContext = parseDashboardContext(initialLocationHref), storageArea = void 0;
672
+ return providedToken ? {
673
+ authState: { type: AuthStateType.LOGGED_IN, token: providedToken, currentUser: null },
674
+ storageKey,
675
+ storageArea,
676
+ authMethod: void 0,
677
+ dashboardContext
678
+ } : getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref) ? {
679
+ authState: { type: AuthStateType.LOGGING_IN, isExchangingToken: !1 },
680
+ storageKey,
681
+ storageArea,
682
+ authMethod: void 0,
683
+ dashboardContext
684
+ } : {
685
+ authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 },
686
+ storageKey,
687
+ storageArea,
688
+ authMethod: void 0,
689
+ dashboardContext
690
+ };
592
691
  }
593
- function getStudioTokenFromLocalStorage(storageArea, storageKey) {
594
- return !storageArea || !storageKey ? null : getTokenFromStorage(storageArea, storageKey) || null;
595
- }
596
- const subscribeToStateAndFetchCurrentUser = ({
597
- state,
598
- instance
599
- }) => {
600
- const { clientFactory, apiHost } = state.get().options, useProjectHostname = !!instance.config.studioMode?.enabled, projectId = instance.config.projectId;
601
- return state.observable.pipe(
602
- map(({ authState, options }) => ({ authState, authMethod: options.authMethod })),
603
- filter(
604
- (value) => value.authState.type === AuthStateType.LOGGED_IN && !value.authState.currentUser
605
- ),
606
- map((value) => ({ token: value.authState.token, authMethod: value.authMethod })),
607
- distinctUntilChanged(
608
- (prev, curr) => prev.token === curr.token && prev.authMethod === curr.authMethod
609
- )
610
- ).pipe(
611
- map(
612
- ({ token, authMethod }) => clientFactory({
613
- apiVersion: DEFAULT_API_VERSION$1,
614
- requestTagPrefix: REQUEST_TAG_PREFIX,
615
- token: authMethod === "cookie" ? void 0 : token,
616
- ignoreBrowserTokenWarning: !0,
617
- useProjectHostname,
618
- useCdn: !1,
619
- ...authMethod === "cookie" ? { withCredentials: !0 } : {},
620
- ...useProjectHostname && projectId ? { projectId } : {},
621
- ...apiHost && { apiHost }
622
- })
623
- ),
624
- switchMap(
625
- (client) => client.observable.request({ uri: "/users/me", method: "GET" })
626
- )
627
- ).subscribe({
628
- next: (currentUser) => {
629
- state.set("setCurrentUser", (prev) => ({
630
- authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, currentUser } : prev.authState
631
- }));
692
+ function initializeDashboardAuth(context, tokenRefresherRunning2) {
693
+ const subscriptions = [];
694
+ let startedRefresher = !1;
695
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !1 })), tokenRefresherRunning2 || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
696
+ dispose: () => {
697
+ for (const subscription of subscriptions)
698
+ subscription.unsubscribe();
632
699
  },
633
- error: (error) => {
634
- state.set("setError", { authState: { type: AuthStateType.ERROR, error } });
635
- }
636
- });
637
- }, subscribeToStorageEventsAndSetToken = ({
700
+ tokenRefresherStarted: startedRefresher
701
+ };
702
+ }
703
+ const subscribeToStorageEventsAndSetToken = ({
638
704
  state
639
705
  }) => {
640
706
  const { storageArea, storageKey } = state.get().options;
@@ -650,6 +716,159 @@ const subscribeToStateAndFetchCurrentUser = ({
650
716
  });
651
717
  });
652
718
  };
719
+ function getStandaloneInitialState(options) {
720
+ const { authConfig, initialLocationHref } = options, providedToken = authConfig.token, callbackUrl = authConfig.callbackUrl, storageKey = "__sanity_auth_token", storageArea = authConfig.storageArea ?? getDefaultStorage();
721
+ if (providedToken)
722
+ return {
723
+ authState: { type: AuthStateType.LOGGED_IN, token: providedToken, currentUser: null },
724
+ storageKey,
725
+ storageArea,
726
+ authMethod: void 0,
727
+ dashboardContext: {}
728
+ };
729
+ if (getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref))
730
+ return {
731
+ authState: { type: AuthStateType.LOGGING_IN, isExchangingToken: !1 },
732
+ storageKey,
733
+ storageArea,
734
+ authMethod: void 0,
735
+ dashboardContext: {}
736
+ };
737
+ const token = getTokenFromStorage(storageArea, storageKey);
738
+ return token ? {
739
+ authState: { type: AuthStateType.LOGGED_IN, token, currentUser: null },
740
+ storageKey,
741
+ storageArea,
742
+ authMethod: "localstorage",
743
+ dashboardContext: {}
744
+ } : {
745
+ authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 },
746
+ storageKey,
747
+ storageArea,
748
+ authMethod: void 0,
749
+ dashboardContext: {}
750
+ };
751
+ }
752
+ function initializeStandaloneAuth(context, tokenRefresherRunning2) {
753
+ const subscriptions = [];
754
+ let startedRefresher = !1;
755
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !1 })), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context)), tokenRefresherRunning2 || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
756
+ dispose: () => {
757
+ for (const subscription of subscriptions)
758
+ subscription.unsubscribe();
759
+ },
760
+ tokenRefresherStarted: startedRefresher
761
+ };
762
+ }
763
+ const COOKIE_AUTH_TIMEOUT_MS = 1e4;
764
+ async function checkForCookieAuth(projectId, clientFactory) {
765
+ if (!projectId) return !1;
766
+ try {
767
+ const user = await clientFactory({
768
+ projectId,
769
+ useCdn: !1,
770
+ requestTagPrefix: "sdk",
771
+ timeout: COOKIE_AUTH_TIMEOUT_MS
772
+ }).request({
773
+ uri: "/users/me",
774
+ withCredentials: !0,
775
+ tag: "users.get-current"
776
+ });
777
+ return user != null && typeof user == "object" && typeof user.id == "string";
778
+ } catch {
779
+ return !1;
780
+ }
781
+ }
782
+ function getStudioTokenFromLocalStorage(storageArea, storageKey) {
783
+ return !storageArea || !storageKey ? null : getTokenFromStorage(storageArea, storageKey) || null;
784
+ }
785
+ function getStudioInitialState(options) {
786
+ const { authConfig, projectId, tokenSource } = options, storageArea = authConfig.storageArea ?? getDefaultStorage(), studioStorageKey = `__studio_auth_token_${projectId ?? ""}`;
787
+ if (tokenSource)
788
+ return {
789
+ authState: { type: AuthStateType.LOGGING_IN, isExchangingToken: !1 },
790
+ storageKey: studioStorageKey,
791
+ storageArea,
792
+ authMethod: void 0,
793
+ dashboardContext: {}
794
+ };
795
+ const providedToken = authConfig.token;
796
+ let authMethod;
797
+ const token = getStudioTokenFromLocalStorage(storageArea, studioStorageKey);
798
+ return token && (authMethod = "localstorage"), providedToken ? {
799
+ authState: { type: AuthStateType.LOGGED_IN, token: providedToken, currentUser: null },
800
+ storageKey: studioStorageKey,
801
+ storageArea,
802
+ authMethod,
803
+ dashboardContext: {}
804
+ } : token ? {
805
+ authState: { type: AuthStateType.LOGGED_IN, token, currentUser: null },
806
+ storageKey: studioStorageKey,
807
+ storageArea,
808
+ authMethod: "localstorage",
809
+ dashboardContext: {}
810
+ } : {
811
+ authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 },
812
+ storageKey: studioStorageKey,
813
+ storageArea,
814
+ authMethod: void 0,
815
+ dashboardContext: {}
816
+ };
817
+ }
818
+ function initializeStudioAuth(context, tokenRefresherRunning2) {
819
+ const tokenSource = context.instance.config.studio?.auth?.token;
820
+ return tokenSource ? initializeWithTokenSource(context, tokenSource) : initializeWithFallback(context, tokenRefresherRunning2);
821
+ }
822
+ function initializeWithTokenSource(context, tokenSource) {
823
+ const subscriptions = [];
824
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !0 }));
825
+ const tokenSub = tokenSource.subscribe({
826
+ next: (token) => {
827
+ const { state } = context;
828
+ token ? state.set("studioTokenSource", (prev) => ({
829
+ options: { ...prev.options, authMethod: void 0 },
830
+ authState: { type: AuthStateType.LOGGED_IN, token, currentUser: null }
831
+ })) : state.set("studioTokenSourceLoggedOut", (prev) => ({
832
+ options: { ...prev.options, authMethod: void 0 },
833
+ authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }
834
+ }));
835
+ }
836
+ });
837
+ return {
838
+ dispose: () => {
839
+ tokenSub.unsubscribe();
840
+ for (const subscription of subscriptions)
841
+ subscription.unsubscribe();
842
+ },
843
+ // Studio handles token refresh — do not start the SDK's refresher
844
+ tokenRefresherStarted: !1
845
+ };
846
+ }
847
+ function initializeWithFallback(context, tokenRefresherRunning2) {
848
+ const subscriptions = [];
849
+ let startedRefresher = !1;
850
+ subscriptions.push(subscribeToStateAndFetchCurrentUser(context, { useProjectHostname: !0 })), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context));
851
+ try {
852
+ const { instance, state } = context;
853
+ if (!(state.get().authState?.type === AuthStateType.LOGGED_IN && state.get().authState.token)) {
854
+ const projectIdValue = instance.config.projectId, clientFactory = state.get().options.clientFactory;
855
+ checkForCookieAuth(projectIdValue, clientFactory).then((isCookieAuthEnabled) => {
856
+ isCookieAuthEnabled && state.set("enableCookieAuth", (prev) => ({
857
+ options: { ...prev.options, authMethod: "cookie" },
858
+ authState: prev.authState.type === AuthStateType.LOGGED_IN ? prev.authState : { type: AuthStateType.LOGGED_IN, token: "", currentUser: null }
859
+ }));
860
+ });
861
+ }
862
+ } catch {
863
+ }
864
+ return tokenRefresherRunning2 || (startedRefresher = !0, subscriptions.push(refreshStampedToken(context))), {
865
+ dispose: () => {
866
+ for (const subscription of subscriptions)
867
+ subscription.unsubscribe();
868
+ },
869
+ tokenRefresherStarted: startedRefresher
870
+ };
871
+ }
653
872
  let tokenRefresherRunning = !1;
654
873
  const authStore = {
655
874
  name: "Auth",
@@ -661,9 +880,7 @@ const authStore = {
661
880
  token: providedToken,
662
881
  clientFactory = createClient,
663
882
  initialLocationHref = getDefaultLocation()
664
- } = instance.config.auth ?? {};
665
- let storageArea = instance.config.auth?.storageArea, storageKey = "__sanity_auth_token";
666
- const studioModeEnabled = instance.config.studioMode?.enabled;
883
+ } = instance.config.auth ?? {}, authConfig = instance.config.auth ?? {};
667
884
  let loginDomain = "https://www.sanity.io";
668
885
  try {
669
886
  apiHost && new URL(apiHost).hostname.endsWith(".sanity.work") && (loginDomain = "https://www.sanity.work");
@@ -671,27 +888,27 @@ const authStore = {
671
888
  }
672
889
  const loginUrl = new URL("/login", loginDomain);
673
890
  loginUrl.searchParams.set("origin", getCleanedUrl(initialLocationHref)), loginUrl.searchParams.set("type", "stampedToken"), loginUrl.searchParams.set("withSid", "true");
674
- let dashboardContext = {}, isInDashboard = !1;
675
- try {
676
- const contextParam = new URL(initialLocationHref).searchParams.get("_context");
677
- if (contextParam) {
678
- const parsedContext = JSON.parse(contextParam);
679
- parsedContext && typeof parsedContext == "object" && Object.keys(parsedContext).length > 0 && (delete parsedContext.sid, dashboardContext = parsedContext, isInDashboard = !0);
680
- }
681
- } catch (err) {
682
- console.error("Failed to parse dashboard context from initial location:", err);
891
+ const mode = resolveAuthMode(instance.config, initialLocationHref), strategyOptions = {
892
+ authConfig,
893
+ projectId: instance.config.projectId,
894
+ initialLocationHref,
895
+ tokenSource: instance.config.studio?.auth?.token
896
+ };
897
+ let result;
898
+ switch (mode) {
899
+ case "studio":
900
+ result = getStudioInitialState(strategyOptions);
901
+ break;
902
+ case "dashboard":
903
+ result = getDashboardInitialState(strategyOptions);
904
+ break;
905
+ case "standalone":
906
+ result = getStandaloneInitialState(strategyOptions);
907
+ break;
683
908
  }
684
- (!isInDashboard || studioModeEnabled) && (storageArea = storageArea ?? getDefaultStorage());
685
- let token, authMethod;
686
- if (studioModeEnabled) {
687
- const studioStorageKey = `__studio_auth_token_${instance.config.projectId ?? ""}`;
688
- storageKey = studioStorageKey, token = getStudioTokenFromLocalStorage(storageArea, studioStorageKey), token && (authMethod = "localstorage");
689
- } else
690
- token = getTokenFromStorage(storageArea, storageKey), token && (authMethod = "localstorage");
691
- let authState;
692
- return providedToken ? authState = { type: AuthStateType.LOGGED_IN, token: providedToken, currentUser: null } : token && studioModeEnabled ? authState = { type: AuthStateType.LOGGED_IN, token: token ?? "", currentUser: null } : getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref) ? authState = { type: AuthStateType.LOGGING_IN, isExchangingToken: !1 } : token && !isInDashboard && !studioModeEnabled ? authState = { type: AuthStateType.LOGGED_IN, token, currentUser: null } : authState = { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }, {
693
- authState,
694
- dashboardContext,
909
+ return {
910
+ authState: result.authState,
911
+ dashboardContext: result.dashboardContext,
695
912
  options: {
696
913
  apiHost,
697
914
  loginUrl: loginUrl.toString(),
@@ -700,32 +917,27 @@ const authStore = {
700
917
  providedToken,
701
918
  clientFactory,
702
919
  initialLocationHref,
703
- storageKey,
704
- storageArea,
705
- authMethod
920
+ storageKey: result.storageKey,
921
+ storageArea: result.storageArea,
922
+ authMethod: result.authMethod
706
923
  }
707
924
  };
708
925
  },
709
926
  initialize(context) {
710
- const subscriptions = [];
711
- subscriptions.push(subscribeToStateAndFetchCurrentUser(context)), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context));
712
- try {
713
- const { instance, state } = context, studioModeEnabled = !!instance.config.studioMode?.enabled, token = state.get().authState?.type === AuthStateType.LOGGED_IN ? state.get().authState.token : null;
714
- if (studioModeEnabled && !token) {
715
- const projectId = instance.config.projectId, clientFactory = state.get().options.clientFactory;
716
- checkForCookieAuth(projectId, clientFactory).then((isCookieAuthEnabled) => {
717
- isCookieAuthEnabled && state.set("enableCookieAuth", (prev) => ({
718
- options: { ...prev.options, authMethod: "cookie" },
719
- authState: prev.authState.type === AuthStateType.LOGGED_IN ? prev.authState : { type: AuthStateType.LOGGED_IN, token: "", currentUser: null }
720
- }));
721
- });
722
- }
723
- } catch {
927
+ const initialLocationHref = context.state.get().options?.initialLocationHref ?? getDefaultLocation(), mode = resolveAuthMode(context.instance.config, initialLocationHref);
928
+ let initResult;
929
+ switch (mode) {
930
+ case "studio":
931
+ initResult = initializeStudioAuth(context, tokenRefresherRunning);
932
+ break;
933
+ case "dashboard":
934
+ initResult = initializeDashboardAuth(context, tokenRefresherRunning);
935
+ break;
936
+ case "standalone":
937
+ initResult = initializeStandaloneAuth(context, tokenRefresherRunning);
938
+ break;
724
939
  }
725
- return tokenRefresherRunning || (tokenRefresherRunning = !0, subscriptions.push(refreshStampedToken(context))), () => {
726
- for (const subscription of subscriptions)
727
- subscription.unsubscribe();
728
- };
940
+ return initResult.tokenRefresherStarted && (tokenRefresherRunning = !0), initResult.dispose;
729
941
  }
730
942
  }, getCurrentUserState = bindActionGlobally(
731
943
  authStore,
@@ -1786,7 +1998,7 @@ function sortListenerEvents(options) {
1786
1998
  mergeMap((state) => of(...state.emitEvents))
1787
1999
  );
1788
2000
  }
1789
- const listen$1 = ({ state }, documentId) => {
2001
+ const listen = ({ state }, documentId) => {
1790
2002
  const { sharedListener, fetchDocument } = state.get();
1791
2003
  return sharedListener.events.pipe(
1792
2004
  concatMap((e) => e.type === "welcome" ? fetchDocument(documentId).pipe(
@@ -2940,9 +3152,9 @@ const _resolveDocument = bindActionByDataset(
2940
3152
  groupBy$1((i) => i.id),
2941
3153
  mergeMap$1(
2942
3154
  (group) => group.pipe(
2943
- switchMap((e) => e.add ? listen$1(context, e.id).pipe(
3155
+ switchMap((e) => e.add ? listen(context, e.id).pipe(
2944
3156
  catchError$1((error) => {
2945
- throw error instanceof OutOfSyncError && listen$1(context, e.id), error;
3157
+ throw error instanceof OutOfSyncError && listen(context, e.id), error;
2946
3158
  }),
2947
3159
  tap$1(
2948
3160
  (remote) => state.set(
@@ -3423,40 +3635,7 @@ const handleIncomingMessage = (event) => {
3423
3635
  return () => subscription.unsubscribe();
3424
3636
  }
3425
3637
  })
3426
- ), fetch = (client, query, params, options) => defer(
3427
- () => client.observable.fetch(query, params, {
3428
- tag: options.tag,
3429
- filterResponse: !0
3430
- })
3431
- ), listen = (client, query, params, options) => defer(
3432
- () => client.listen(query, params, {
3433
- events: ["welcome", "mutation", "reconnect"],
3434
- includeResult: !1,
3435
- visibility: "query",
3436
- tag: options.tag
3437
- })
3438
3638
  );
3439
- function isWelcomeEvent(event) {
3440
- return event.type === "welcome";
3441
- }
3442
- const listenQuery = (client, query, params = {}, options = {}) => {
3443
- const fetchQuery = query, listenerQuery = query, fetchOnce$ = fetch(client, fetchQuery, params, options), events$ = listen(client, listenerQuery, params, options).pipe(
3444
- mergeMap((ev, i) => i === 0 && !isWelcomeEvent(ev) ? throwError(
3445
- () => new Error(
3446
- ev.type === "reconnect" ? "Could not establish EventSource connection" : `Received unexpected type of first event "${ev.type}"`
3447
- )
3448
- ) : of(ev)),
3449
- share$1()
3450
- ), [welcome$, mutationAndReconnect$] = partition(events$, isWelcomeEvent), isRelevantEvent = (event) => !options.transitions || event.type !== "mutation" ? !0 : options.transitions.includes(event.transition);
3451
- return merge(
3452
- welcome$.pipe(take(1)),
3453
- mutationAndReconnect$.pipe(filter$1(isRelevantEvent), debounceTime(options.throttleTime || 1e3))
3454
- ).pipe(
3455
- // will cancel any in-flight request when a new one comes in
3456
- // but ensures we always get the latest data
3457
- switchMap(() => fetchOnce$)
3458
- );
3459
- };
3460
3639
  function sortReleases(releases = []) {
3461
3640
  return [...releases].sort((a, b) => {
3462
3641
  if (a.metadata.releaseType === "undecided" && b.metadata.releaseType !== "undecided")
@@ -3475,7 +3654,7 @@ function sortReleases(releases = []) {
3475
3654
  return a.metadata.releaseType === "asap" && b.metadata.releaseType !== "asap" ? 1 : a.metadata.releaseType !== "asap" && b.metadata.releaseType === "asap" ? -1 : a.metadata.releaseType === "asap" && b.metadata.releaseType === "asap" ? new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime() : 0;
3476
3655
  });
3477
3656
  }
3478
- const ARCHIVED_RELEASE_STATES = ["archived", "published"], releasesStore = {
3657
+ const ARCHIVED_RELEASE_STATES = ["archived", "published"], STABLE_EMPTY_RELEASES = [], releasesStore = {
3479
3658
  name: "Releases",
3480
3659
  getInitialState: () => ({
3481
3660
  activeReleases: void 0
@@ -3489,43 +3668,26 @@ const ARCHIVED_RELEASE_STATES = ["archived", "published"], releasesStore = {
3489
3668
  createStateSourceAction({
3490
3669
  selector: ({ state }, _) => state.activeReleases
3491
3670
  })
3492
- ), RELEASES_QUERY = "releases::all()", QUERY_PARAMS = {}, subscribeToReleases = ({
3671
+ ), RELEASES_QUERY = "releases::all()", subscribeToReleases = ({
3493
3672
  instance,
3494
3673
  state,
3495
3674
  key: { projectId, dataset }
3496
- }) => getClientState(instance, {
3497
- apiVersion: "2025-04-10",
3498
- perspective: "raw",
3499
- projectId,
3500
- dataset
3501
- }).observable.pipe(
3502
- switchMap(
3503
- (client) => (
3504
- // releases are system documents, and are not supported by useQueryState
3505
- listenQuery(client, RELEASES_QUERY, QUERY_PARAMS, {
3506
- tag: "releases-listener",
3507
- throttleTime: 1e3,
3508
- transitions: ["update", "appear", "disappear"]
3509
- }).pipe(
3510
- retry({
3511
- count: 3,
3512
- delay: (error, retryCount) => (console.error("[releases] Error in subscription:", error, "Retry count:", retryCount), timer(Math.min(1e3 * Math.pow(2, retryCount), 1e4)))
3513
- }),
3514
- catchError$1((error) => (state.set("setError", { error }), EMPTY))
3515
- )
3516
- )
3517
- )
3518
- ).subscribe({
3519
- next: (releases) => {
3520
- state.set("setActiveReleases", {
3521
- activeReleases: sortReleases(releases ?? []).filter((release) => !ARCHIVED_RELEASE_STATES.includes(release.state)).reverse()
3522
- });
3523
- }
3524
- });
3525
- function isReleasePerspective(perspective) {
3526
- return typeof perspective == "object" && perspective !== null && "releaseName" in perspective;
3527
- }
3528
- const DEFAULT_PERSPECTIVE = "drafts", optionsCache = /* @__PURE__ */ new Map(), selectInstancePerspective = (context, _) => context.instance.config.perspective, selectActiveReleases = (context) => context.state.activeReleases, selectOptions = (_context, options) => options, memoizedOptionsSelector = createSelector(
3675
+ }) => {
3676
+ const { observable: releases$ } = getQueryState(instance, {
3677
+ query: RELEASES_QUERY,
3678
+ perspective: "raw",
3679
+ projectId,
3680
+ dataset,
3681
+ tag: "releases"
3682
+ });
3683
+ return releases$.pipe(
3684
+ map((releases) => {
3685
+ state.set("setActiveReleases", {
3686
+ activeReleases: sortReleases(releases ?? STABLE_EMPTY_RELEASES).filter((release) => !ARCHIVED_RELEASE_STATES.includes(release.state)).reverse()
3687
+ });
3688
+ })
3689
+ ).subscribe({ error: (error) => state.set("setError", { error }) });
3690
+ }, DEFAULT_PERSPECTIVE = "drafts", optionsCache = /* @__PURE__ */ new Map(), selectInstancePerspective = (context, _) => context.instance.config.perspective, selectActiveReleases = (context) => context.state.activeReleases, selectOptions = (_context, options) => options, memoizedOptionsSelector = createSelector(
3529
3691
  [selectActiveReleases, selectOptions],
3530
3692
  (activeReleases, options) => {
3531
3693
  if (!options || !activeReleases) return options;
@@ -3536,25 +3698,25 @@ const DEFAULT_PERSPECTIVE = "drafts", optionsCache = /* @__PURE__ */ new Map(),
3536
3698
  let cachedOptions = nestedCache.get(optionsKey);
3537
3699
  return cachedOptions || (cachedOptions = options, nestedCache.set(optionsKey, cachedOptions)), cachedOptions;
3538
3700
  }
3539
- ), getPerspectiveState = bindActionByDataset(
3701
+ ), _getPerspectiveStateSelector = createStateSourceAction({
3702
+ selector: createSelector(
3703
+ [selectInstancePerspective, selectActiveReleases, memoizedOptionsSelector],
3704
+ (instancePerspective, activeReleases, memoizedOptions) => {
3705
+ const perspective = memoizedOptions?.perspective ?? instancePerspective ?? DEFAULT_PERSPECTIVE;
3706
+ if (!isReleasePerspective(perspective)) return perspective;
3707
+ if (!activeReleases || activeReleases.length === 0) return;
3708
+ const releaseNames = sortReleases(activeReleases).map((release) => release.name), index = releaseNames.findIndex((name) => name === perspective.releaseName);
3709
+ if (index < 0)
3710
+ throw new Error(`Release "${perspective.releaseName}" not found in active releases`);
3711
+ return ["drafts", ...releaseNames.slice(0, index + 1)].filter((name) => !perspective.excludedPerspectives?.includes(name)).reverse();
3712
+ }
3713
+ )
3714
+ });
3715
+ let _boundGetPerspectiveState;
3716
+ const getPerspectiveState = (...args) => (_boundGetPerspectiveState || (_boundGetPerspectiveState = bindActionByDataset(
3540
3717
  releasesStore,
3541
- createStateSourceAction({
3542
- selector: createSelector(
3543
- [selectInstancePerspective, selectActiveReleases, memoizedOptionsSelector],
3544
- (instancePerspective, activeReleases, memoizedOptions) => {
3545
- const perspective = memoizedOptions?.perspective ?? instancePerspective ?? DEFAULT_PERSPECTIVE;
3546
- if (!isReleasePerspective(perspective)) return perspective;
3547
- if (!activeReleases || activeReleases.length === 0) return;
3548
- const releaseNames = sortReleases(activeReleases).map((release) => release.name), index = releaseNames.findIndex((name) => name === perspective.releaseName);
3549
- if (index < 0)
3550
- throw new Error(`Release "${perspective.releaseName}" not found in active releases`);
3551
- return ["drafts", ...releaseNames.slice(0, index + 1)].filter(
3552
- (name) => !perspective.excludedPerspectives?.includes(name)
3553
- );
3554
- }
3555
- )
3556
- })
3557
- ), QUERY_STATE_CLEAR_DELAY = 1e3, QUERY_STORE_API_VERSION = "v2025-05-06", QUERY_STORE_DEFAULT_PERSPECTIVE = "drafts", setQueryError = (key, error) => (prev) => {
3718
+ _getPerspectiveStateSelector
3719
+ )), _boundGetPerspectiveState(...args)), QUERY_STATE_CLEAR_DELAY = 1e3, QUERY_STORE_API_VERSION = "v2025-05-06", QUERY_STORE_DEFAULT_PERSPECTIVE = "drafts", setQueryError = (key, error) => (prev) => {
3558
3720
  const prevQuery = prev.queries[key];
3559
3721
  return prevQuery ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, error } } } : prev;
3560
3722
  }, setQueryData = (key, result, syncTags) => (prev) => {
@@ -3628,17 +3790,21 @@ const queryStore = {
3628
3790
  source,
3629
3791
  perspective: perspectiveFromOptions,
3630
3792
  ...restOptions
3631
- } = parseQueryKey(group$.key), perspective$ = getPerspectiveState(instance, {
3793
+ } = parseQueryKey(group$.key), perspective$ = isReleasePerspective(perspectiveFromOptions) ? getPerspectiveState(instance, {
3632
3794
  perspective: perspectiveFromOptions
3633
- }).observable.pipe(filter(Boolean)), client$ = getClientState(instance, {
3795
+ }).observable.pipe(filter(Boolean)) : of(perspectiveFromOptions ?? QUERY_STORE_DEFAULT_PERSPECTIVE), client$ = getClientState(instance, {
3634
3796
  apiVersion: QUERY_STORE_API_VERSION,
3635
3797
  projectId,
3636
3798
  dataset,
3637
3799
  source
3638
3800
  }).observable;
3639
- return combineLatest([lastLiveEventId$, client$, perspective$]).pipe(
3801
+ return combineLatest({
3802
+ lastLiveEventId: lastLiveEventId$,
3803
+ client: client$,
3804
+ perspective: perspective$
3805
+ }).pipe(
3640
3806
  switchMap(
3641
- ([lastLiveEventId, client, perspective]) => client.observable.fetch(query, params, {
3807
+ ({ lastLiveEventId, client, perspective }) => client.observable.fetch(query, params, {
3642
3808
  ...restOptions,
3643
3809
  perspective,
3644
3810
  filterResponse: !1,
@@ -3657,10 +3823,13 @@ const queryStore = {
3657
3823
  )
3658
3824
  ).subscribe({ error: errorHandler(state) }), listenToLiveClientAndSetLastLiveEventIds = ({
3659
3825
  state,
3660
- instance
3826
+ instance,
3827
+ key: { source }
3661
3828
  }) => {
3662
3829
  const liveMessages$ = getClientState(instance, {
3663
- apiVersion: QUERY_STORE_API_VERSION
3830
+ apiVersion: QUERY_STORE_API_VERSION,
3831
+ // temporary guard here until we're ready for everything to be queried via global api
3832
+ ...source && !isDatasetSource(source) ? { source } : {}
3664
3833
  }).observable.pipe(
3665
3834
  switchMap(
3666
3835
  (client) => defer(
@@ -3850,7 +4019,7 @@ const BATCH_DEBOUNCE_TIME$1 = 50, isSetEqual$1 = (a, b) => a.size === b.size &&
3850
4019
  }) => state.observable.pipe(
3851
4020
  map(({ subscriptions }) => new Set(Object.keys(subscriptions))),
3852
4021
  distinctUntilChanged(isSetEqual$1),
3853
- debounceTime$1(BATCH_DEBOUNCE_TIME$1),
4022
+ debounceTime(BATCH_DEBOUNCE_TIME$1),
3854
4023
  startWith$1(/* @__PURE__ */ new Set()),
3855
4024
  pairwise$1(),
3856
4025
  tap$1(([prevIds, currIds]) => {
@@ -3949,7 +4118,7 @@ const _getPreviewState = bindActionByDataset(
3949
4118
  ), resolvePreview = bindActionByDataset(
3950
4119
  previewStore,
3951
4120
  ({ instance }, docHandle) => firstValueFrom(getPreviewState(instance, docHandle).observable.pipe(filter((i) => !!i.data)))
3952
- ), PROJECTION_TAG = "projection", PROJECTION_PERSPECTIVE = "raw", PROJECTION_STATE_CLEAR_DELAY = 1e3, STABLE_EMPTY_PROJECTION = {
4121
+ ), PROJECTION_TAG = "projection", PROJECTION_STATE_CLEAR_DELAY = 1e3, STABLE_EMPTY_PROJECTION = {
3953
4122
  data: null,
3954
4123
  isPending: !1
3955
4124
  };
@@ -3973,20 +4142,21 @@ function createProjectionQuery(documentIds, documentProjections) {
3973
4142
  return obj.documentIds.add(documentId), acc[projectionHash] = obj, acc;
3974
4143
  }, {}), query = `[${Object.entries(projections).map(([projectionHash, { projection }]) => `...*[_id in $__ids_${projectionHash}]{_id,_type,_updatedAt,"__projectionHash":"${projectionHash}","result":{...${projection}}}`).join(",")}]`, params = Object.fromEntries(
3975
4144
  Object.entries(projections).map(([projectionHash, value]) => {
3976
- const idsInProjection = Array.from(value.documentIds).flatMap((id) => [
3977
- getPublishedId(id),
3978
- getDraftId(id)
3979
- ]);
4145
+ const idsInProjection = Array.from(value.documentIds).flatMap((id) => DocumentId(id));
3980
4146
  return [`__ids_${projectionHash}`, Array.from(idsInProjection)];
3981
4147
  })
3982
4148
  );
3983
4149
  return { query, params };
3984
4150
  }
3985
- function processProjectionQuery({ ids, results }) {
4151
+ function processProjectionQuery({
4152
+ ids,
4153
+ results,
4154
+ documentStatuses
4155
+ }) {
3986
4156
  const groupedResults = {};
3987
4157
  for (const result of results) {
3988
- const originalId = getPublishedId(result._id), hash = result.__projectionHash, isDraft = result._id.startsWith("drafts.");
3989
- ids.has(originalId) && (groupedResults[originalId] || (groupedResults[originalId] = {}), groupedResults[originalId][hash] || (groupedResults[originalId][hash] = {}), isDraft ? groupedResults[originalId][hash].draft = result : groupedResults[originalId][hash].published = result);
4158
+ const originalId = getPublishedId(result._id), hash = result.__projectionHash;
4159
+ ids.has(originalId) && (groupedResults[originalId] || (groupedResults[originalId] = {}), groupedResults[originalId][hash] || (groupedResults[originalId][hash] = void 0), groupedResults[originalId][hash] = result);
3990
4160
  }
3991
4161
  const finalValues = {};
3992
4162
  for (const originalId of ids) {
@@ -3994,27 +4164,40 @@ function processProjectionQuery({ ids, results }) {
3994
4164
  const projectionsForDoc = groupedResults[originalId];
3995
4165
  if (projectionsForDoc)
3996
4166
  for (const hash in projectionsForDoc) {
3997
- const { draft, published } = projectionsForDoc[hash], projectionResultData = draft?.result ?? published?.result;
4167
+ const projectionResultData = projectionsForDoc[hash]?.result;
3998
4168
  if (!projectionResultData) {
3999
4169
  finalValues[originalId][hash] = { data: null, isPending: !1 };
4000
4170
  continue;
4001
4171
  }
4002
- const _status = {
4003
- ...draft?._updatedAt && { lastEditedDraftAt: draft._updatedAt },
4004
- ...published?._updatedAt && { lastEditedPublishedAt: published._updatedAt }
4005
- };
4172
+ const statusFromStore = documentStatuses?.[originalId];
4006
4173
  finalValues[originalId][hash] = {
4007
- data: { ...projectionResultData, _status },
4174
+ data: { ...projectionResultData, _status: statusFromStore },
4008
4175
  isPending: !1
4009
4176
  };
4010
4177
  }
4011
4178
  }
4012
4179
  return finalValues;
4013
4180
  }
4181
+ function buildStatusQueryIds(documentIds, perspective) {
4182
+ const ids = [], releaseName = isReleasePerspective(perspective) ? perspective.releaseName : null;
4183
+ for (const id of documentIds) {
4184
+ const publishedId = getPublishedId$2(DocumentId(id)), draftId = getDraftId$1(publishedId);
4185
+ ids.push(draftId, publishedId), releaseName && ids.push(getVersionId(publishedId, releaseName));
4186
+ }
4187
+ return ids;
4188
+ }
4189
+ function processStatusQueryResults(results) {
4190
+ const documentStatuses = {};
4191
+ for (const result of results) {
4192
+ const id = DocumentId(result._id), updatedAt = result._updatedAt, publishedId = getPublishedId$2(id), statusData = documentStatuses[publishedId] ?? {};
4193
+ isDraftId(id) ? statusData.lastEditedDraftAt = updatedAt : isVersionId(id) ? statusData.lastEditedVersionAt = updatedAt : isPublishedId(id) && (statusData.lastEditedPublishedAt = updatedAt), documentStatuses[publishedId] = statusData;
4194
+ }
4195
+ return documentStatuses;
4196
+ }
4014
4197
  const BATCH_DEBOUNCE_TIME = 50, isSetEqual = (a, b) => a.size === b.size && Array.from(a).every((i) => b.has(i)), subscribeToStateAndFetchBatches = ({
4015
4198
  state,
4016
4199
  instance,
4017
- key: { projectId, dataset }
4200
+ key: { source, perspective }
4018
4201
  }) => {
4019
4202
  const documentProjections$ = state.observable.pipe(
4020
4203
  map((s) => s.documentProjections),
@@ -4023,7 +4206,7 @@ const BATCH_DEBOUNCE_TIME = 50, isSetEqual = (a, b) => a.size === b.size && Arra
4023
4206
  map(({ subscriptions }) => new Set(Object.keys(subscriptions))),
4024
4207
  distinctUntilChanged(isSetEqual)
4025
4208
  ), pendingUpdateSubscription = activeDocumentIds$.pipe(
4026
- debounceTime$1(BATCH_DEBOUNCE_TIME),
4209
+ debounceTime(BATCH_DEBOUNCE_TIME),
4027
4210
  startWith$1(/* @__PURE__ */ new Set()),
4028
4211
  pairwise$1(),
4029
4212
  tap$1(([prevIds, currIds]) => {
@@ -4047,42 +4230,82 @@ const BATCH_DEBOUNCE_TIME = 50, isSetEqual = (a, b) => a.size === b.size && Arra
4047
4230
  });
4048
4231
  })
4049
4232
  ).subscribe(), queryExecutionSubscription = combineLatest([activeDocumentIds$, documentProjections$]).pipe(
4050
- debounceTime$1(BATCH_DEBOUNCE_TIME),
4233
+ debounceTime(BATCH_DEBOUNCE_TIME),
4051
4234
  distinctUntilChanged(isEqual)
4052
4235
  ).pipe(
4053
4236
  switchMap(([ids, documentProjections]) => {
4054
4237
  if (!ids.size) return EMPTY;
4055
- const { query, params } = createProjectionQuery(ids, documentProjections), controller = new AbortController();
4056
- return new Observable((observer) => {
4238
+ const { query, params } = createProjectionQuery(ids, documentProjections), controller = new AbortController(), statusQueryIds = buildStatusQueryIds(ids, perspective), statusQuery = "*[_id in $statusIds]{_id, _updatedAt}", statusParams = { statusIds: statusQueryIds }, projectionQuery$ = new Observable((observer) => {
4057
4239
  const { getCurrent, observable } = getQueryState(instance, {
4058
4240
  query,
4059
4241
  params,
4060
- projectId,
4061
- dataset,
4062
4242
  tag: PROJECTION_TAG,
4063
- perspective: PROJECTION_PERSPECTIVE
4243
+ perspective,
4244
+ // temporary guard here until we're ready for everything to be queried via global API
4245
+ ...source && !isDatasetSource(source) ? { source } : {}
4064
4246
  }), subscription = defer(() => getCurrent() === void 0 ? from(
4065
4247
  resolveQuery(instance, {
4066
4248
  query,
4067
4249
  params,
4068
- projectId,
4069
- dataset,
4070
4250
  tag: PROJECTION_TAG,
4071
- perspective: PROJECTION_PERSPECTIVE,
4072
- signal: controller.signal
4251
+ signal: controller.signal,
4252
+ perspective,
4253
+ // temporary guard here until we're ready for everything to be queried via global API in v3
4254
+ ...source && !isDatasetSource(source) ? { source } : {}
4073
4255
  })
4074
4256
  ).pipe(switchMap(() => observable)) : observable).pipe(filter((result) => result !== void 0)).subscribe(observer);
4075
4257
  return () => {
4076
4258
  controller.signal.aborted || controller.abort(), subscription.unsubscribe();
4077
4259
  };
4078
- }).pipe(map((data) => ({ data, ids })));
4260
+ }), statusQuery$ = new Observable((observer) => {
4261
+ const { getCurrent, observable } = getQueryState(instance, {
4262
+ query: statusQuery,
4263
+ params: statusParams,
4264
+ tag: PROJECTION_TAG,
4265
+ perspective: "raw",
4266
+ // temporary guard here until we're ready for everything to be queried via global API
4267
+ ...source && !isDatasetSource(source) ? { source } : {}
4268
+ }), subscription = defer(() => getCurrent() === void 0 ? from(
4269
+ resolveQuery(instance, {
4270
+ query: statusQuery,
4271
+ params: statusParams,
4272
+ tag: PROJECTION_TAG,
4273
+ signal: controller.signal,
4274
+ perspective: "raw",
4275
+ // temporary guard here until we're ready for everything to be queried via global API
4276
+ ...source && !isDatasetSource(source) ? { source } : {}
4277
+ })
4278
+ ).pipe(switchMap(() => observable)) : observable).pipe(filter((result) => result !== void 0)).subscribe(observer);
4279
+ return () => {
4280
+ subscription.unsubscribe();
4281
+ };
4282
+ });
4283
+ return combineLatest([projectionQuery$, statusQuery$]).pipe(
4284
+ filter(
4285
+ (pair) => pair[0] !== void 0 && pair[1] !== void 0
4286
+ ),
4287
+ map(([projection, status]) => ({
4288
+ data: projection,
4289
+ ids,
4290
+ statusResults: status
4291
+ }))
4292
+ );
4079
4293
  }),
4080
- map(
4081
- ({ ids, data }) => processProjectionQuery({
4294
+ map(({ ids, data, statusResults }) => {
4295
+ const documentStatuses = processStatusQueryResults(statusResults);
4296
+ state.set("updateStatuses", (prev) => ({
4297
+ documentStatuses: {
4298
+ ...prev.documentStatuses,
4299
+ ...documentStatuses
4300
+ }
4301
+ }));
4302
+ const currentState = state.get();
4303
+ return processProjectionQuery({
4082
4304
  ids,
4083
- results: data
4084
- })
4085
- )
4305
+ results: data,
4306
+ documentStatuses: currentState.documentStatuses
4307
+ });
4308
+ })
4086
4309
  ).subscribe({
4087
4310
  next: (processedValues) => {
4088
4311
  state.set("updateResult", (prev) => {
@@ -4108,7 +4331,8 @@ const BATCH_DEBOUNCE_TIME = 50, isSetEqual = (a, b) => a.size === b.size && Arra
4108
4331
  return {
4109
4332
  values: {},
4110
4333
  documentProjections: {},
4111
- subscriptions: {}
4334
+ subscriptions: {},
4335
+ documentStatuses: {}
4112
4336
  };
4113
4337
  },
4114
4338
  initialize(context) {
@@ -4119,15 +4343,15 @@ const BATCH_DEBOUNCE_TIME = 50, isSetEqual = (a, b) => a.size === b.size && Arra
4119
4343
  function getProjectionState(...args) {
4120
4344
  return _getProjectionState(...args);
4121
4345
  }
4122
- const _getProjectionState = bindActionByDataset(
4346
+ const _getProjectionState = bindActionBySourceAndPerspective(
4123
4347
  projectionStore,
4124
4348
  createStateSourceAction({
4125
4349
  selector: ({ state }, options) => {
4126
- const documentId = getPublishedId(options.documentId), projectionHash = hashString(options.projection);
4350
+ const documentId = getPublishedId$2(DocumentId(options.documentId)), projectionHash = hashString(options.projection);
4127
4351
  return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION;
4128
4352
  },
4129
4353
  onSubscribe: ({ state }, options) => {
4130
- const { projection, ...docHandle } = options, subscriptionId = insecureRandomId(), documentId = getPublishedId(docHandle.documentId), validProjection = validateProjection(projection), projectionHash = hashString(validProjection);
4354
+ const { projection, ...docHandle } = options, subscriptionId = insecureRandomId(), documentId = getPublishedId$2(DocumentId(docHandle.documentId)), validProjection = validateProjection(projection), projectionHash = hashString(validProjection);
4131
4355
  return state.set("addSubscription", (prev) => ({
4132
4356
  documentProjections: {
4133
4357
  ...prev.documentProjections,
@@ -4179,7 +4403,7 @@ const _getProjectionState = bindActionByDataset(
4179
4403
  function resolveProjection(...args) {
4180
4404
  return _resolveProjection(...args);
4181
4405
  }
4182
- const _resolveProjection = bindActionByDataset(
4406
+ const _resolveProjection = bindActionBySourceAndPerspective(
4183
4407
  projectionStore,
4184
4408
  ({ instance }, options) => firstValueFrom(
4185
4409
  getProjectionState(instance, options).observable.pipe(
@@ -4287,7 +4511,7 @@ function getCorsErrorProjectId(error) {
4287
4511
  const projMatch = (error.message || "").match(/manage\/project\/([^/?#]+)/);
4288
4512
  return projMatch ? projMatch[1] : null;
4289
4513
  }
4290
- var version = "2.6.0";
4514
+ var version = "2.7.0";
4291
4515
  const CORE_SDK_VERSION = getEnv("PKG_VERSION") || `${version}-development`;
4292
4516
  export {
4293
4517
  AuthStateType,
@@ -4351,6 +4575,7 @@ export {
4351
4575
  isDatasetSource,
4352
4576
  isMediaLibrarySource,
4353
4577
  isProjectUserNotFoundClientError,
4578
+ isStudioConfig,
4354
4579
  joinPaths,
4355
4580
  jsonMatch2 as jsonMatch,
4356
4581
  loadMoreUsers,