@sanity/sdk 0.0.0-chore-react-18-compat.1 → 0.0.0-chore-react-18-compat.3

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 (134) hide show
  1. package/dist/index.d.ts +441 -322
  2. package/dist/index.js +1685 -1481
  3. package/dist/index.js.map +1 -1
  4. package/package.json +13 -15
  5. package/src/_exports/index.ts +32 -30
  6. package/src/auth/authStore.test.ts +149 -104
  7. package/src/auth/authStore.ts +51 -100
  8. package/src/auth/handleAuthCallback.test.ts +67 -34
  9. package/src/auth/handleAuthCallback.ts +8 -7
  10. package/src/auth/logout.test.ts +61 -29
  11. package/src/auth/logout.ts +26 -28
  12. package/src/auth/refreshStampedToken.test.ts +197 -91
  13. package/src/auth/refreshStampedToken.ts +170 -59
  14. package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +5 -5
  15. package/src/auth/subscribeToStateAndFetchCurrentUser.ts +45 -47
  16. package/src/auth/subscribeToStorageEventsAndSetToken.test.ts +4 -5
  17. package/src/auth/subscribeToStorageEventsAndSetToken.ts +22 -24
  18. package/src/client/clientStore.test.ts +131 -67
  19. package/src/client/clientStore.ts +117 -116
  20. package/src/comlink/controller/actions/destroyController.test.ts +38 -13
  21. package/src/comlink/controller/actions/destroyController.ts +11 -15
  22. package/src/comlink/controller/actions/getOrCreateChannel.test.ts +56 -27
  23. package/src/comlink/controller/actions/getOrCreateChannel.ts +37 -35
  24. package/src/comlink/controller/actions/getOrCreateController.test.ts +27 -16
  25. package/src/comlink/controller/actions/getOrCreateController.ts +23 -22
  26. package/src/comlink/controller/actions/releaseChannel.test.ts +37 -13
  27. package/src/comlink/controller/actions/releaseChannel.ts +22 -21
  28. package/src/comlink/controller/comlinkControllerStore.test.ts +65 -36
  29. package/src/comlink/controller/comlinkControllerStore.ts +44 -5
  30. package/src/comlink/node/actions/getOrCreateNode.test.ts +31 -15
  31. package/src/comlink/node/actions/getOrCreateNode.ts +30 -29
  32. package/src/comlink/node/actions/releaseNode.test.ts +75 -55
  33. package/src/comlink/node/actions/releaseNode.ts +19 -21
  34. package/src/comlink/node/comlinkNodeStore.test.ts +6 -11
  35. package/src/comlink/node/comlinkNodeStore.ts +22 -5
  36. package/src/config/authConfig.ts +79 -0
  37. package/src/config/sanityConfig.ts +48 -0
  38. package/src/datasets/datasets.test.ts +2 -2
  39. package/src/datasets/datasets.ts +18 -5
  40. package/src/document/actions.test.ts +22 -10
  41. package/src/document/actions.ts +44 -56
  42. package/src/document/applyDocumentActions.test.ts +96 -36
  43. package/src/document/applyDocumentActions.ts +140 -99
  44. package/src/document/documentStore.test.ts +103 -155
  45. package/src/document/documentStore.ts +247 -238
  46. package/src/document/listen.ts +56 -55
  47. package/src/document/patchOperations.ts +0 -43
  48. package/src/document/permissions.test.ts +25 -12
  49. package/src/document/permissions.ts +11 -4
  50. package/src/document/processActions.test.ts +41 -8
  51. package/src/document/reducers.test.ts +87 -16
  52. package/src/document/reducers.ts +2 -2
  53. package/src/document/sharedListener.test.ts +34 -16
  54. package/src/document/sharedListener.ts +33 -11
  55. package/src/preview/getPreviewState.test.ts +40 -39
  56. package/src/preview/getPreviewState.ts +68 -56
  57. package/src/preview/previewConstants.ts +43 -0
  58. package/src/preview/previewQuery.test.ts +1 -1
  59. package/src/preview/previewQuery.ts +4 -5
  60. package/src/preview/previewStore.test.ts +13 -58
  61. package/src/preview/previewStore.ts +7 -21
  62. package/src/preview/resolvePreview.test.ts +33 -104
  63. package/src/preview/resolvePreview.ts +11 -21
  64. package/src/preview/subscribeToStateAndFetchBatches.test.ts +96 -97
  65. package/src/preview/subscribeToStateAndFetchBatches.ts +85 -81
  66. package/src/preview/util.ts +1 -0
  67. package/src/project/project.test.ts +3 -3
  68. package/src/project/project.ts +28 -5
  69. package/src/projection/getProjectionState.test.ts +188 -72
  70. package/src/projection/getProjectionState.ts +92 -62
  71. package/src/projection/projectionQuery.test.ts +114 -12
  72. package/src/projection/projectionQuery.ts +75 -32
  73. package/src/projection/projectionStore.test.ts +13 -51
  74. package/src/projection/projectionStore.ts +6 -43
  75. package/src/projection/resolveProjection.test.ts +32 -127
  76. package/src/projection/resolveProjection.ts +16 -28
  77. package/src/projection/subscribeToStateAndFetchBatches.test.ts +203 -116
  78. package/src/projection/subscribeToStateAndFetchBatches.ts +140 -85
  79. package/src/projection/types.ts +50 -0
  80. package/src/projection/util.ts +3 -1
  81. package/src/projects/projects.test.ts +13 -4
  82. package/src/projects/projects.ts +6 -1
  83. package/src/query/queryStore.test.ts +10 -47
  84. package/src/query/queryStore.ts +151 -133
  85. package/src/query/queryStoreConstants.ts +2 -0
  86. package/src/store/createActionBinder.test.ts +153 -0
  87. package/src/store/createActionBinder.ts +176 -0
  88. package/src/store/createSanityInstance.test.ts +84 -0
  89. package/src/store/createSanityInstance.ts +124 -0
  90. package/src/store/createStateSourceAction.test.ts +196 -0
  91. package/src/store/createStateSourceAction.ts +260 -0
  92. package/src/store/createStoreInstance.test.ts +81 -0
  93. package/src/store/createStoreInstance.ts +80 -0
  94. package/src/store/createStoreState.test.ts +85 -0
  95. package/src/store/createStoreState.ts +92 -0
  96. package/src/store/defineStore.test.ts +18 -0
  97. package/src/store/defineStore.ts +81 -0
  98. package/src/users/reducers.test.ts +318 -0
  99. package/src/users/reducers.ts +88 -0
  100. package/src/users/types.ts +46 -4
  101. package/src/users/usersConstants.ts +4 -0
  102. package/src/users/usersStore.test.ts +350 -223
  103. package/src/users/usersStore.ts +285 -149
  104. package/src/utils/createFetcherStore.test.ts +6 -7
  105. package/src/utils/createFetcherStore.ts +150 -153
  106. package/src/utils/createGroqSearchFilter.test.ts +75 -0
  107. package/src/utils/createGroqSearchFilter.ts +85 -0
  108. package/src/{common/util.test.ts → utils/hashString.test.ts} +1 -1
  109. package/dist/index.cjs +0 -4888
  110. package/dist/index.cjs.map +0 -1
  111. package/dist/index.d.cts +0 -2121
  112. package/src/auth/fetchLoginUrls.test.ts +0 -163
  113. package/src/auth/fetchLoginUrls.ts +0 -74
  114. package/src/common/createLiveEventSubscriber.test.ts +0 -121
  115. package/src/common/createLiveEventSubscriber.ts +0 -55
  116. package/src/common/types.ts +0 -4
  117. package/src/instance/identity.test.ts +0 -46
  118. package/src/instance/identity.ts +0 -29
  119. package/src/instance/sanityInstance.test.ts +0 -77
  120. package/src/instance/sanityInstance.ts +0 -57
  121. package/src/instance/types.ts +0 -37
  122. package/src/preview/getPreviewProjection.ts +0 -45
  123. package/src/resources/README.md +0 -370
  124. package/src/resources/createAction.test.ts +0 -101
  125. package/src/resources/createAction.ts +0 -44
  126. package/src/resources/createResource.test.ts +0 -112
  127. package/src/resources/createResource.ts +0 -102
  128. package/src/resources/createStateSourceAction.test.ts +0 -114
  129. package/src/resources/createStateSourceAction.ts +0 -83
  130. package/src/resources/createStore.test.ts +0 -67
  131. package/src/resources/createStore.ts +0 -46
  132. package/src/store/createStore.test.ts +0 -108
  133. package/src/store/createStore.ts +0 -106
  134. /package/src/{common/util.ts → utils/hashString.ts} +0 -0
package/dist/index.js CHANGED
@@ -1,17 +1,63 @@
1
1
  import { createClient } from "@sanity/client";
2
- import { Observable, share, map, distinctUntilChanged, skip, filter, switchMap, interval, takeWhile, fromEvent, EMPTY, defer, firstValueFrom, from, asapScheduler, concatMap, of, withLatestFrom, concat, timer, throwError, first as first$1, partition, merge, shareReplay, Subject, 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, combineLatest, debounceTime, NEVER } from "rxjs";
2
+ import { Observable, share, map, distinctUntilChanged, skip, filter, switchMap, timer, from, takeWhile, firstValueFrom, fromEvent, EMPTY, defer, asapScheduler, concatMap, of, 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, combineLatest, debounceTime, Subscription } from "rxjs";
3
3
  import { devtools } from "zustand/middleware";
4
- import { createStore as createStore$1 } from "zustand/vanilla";
5
- import { createSelector } from "reselect";
6
- import { isEqual, omit, isObject, noop } from "lodash-es";
4
+ import { createStore } from "zustand/vanilla";
5
+ import { pick, isEqual, omit, isObject } from "lodash-es";
7
6
  import { createController, createNode } from "@sanity/comlink";
8
7
  import { first, switchMap as switchMap$1, groupBy, mergeMap, startWith, pairwise, filter as filter$1, map as map$1, delay, tap, catchError, scan } from "rxjs/operators";
9
8
  import { SanityEncoder } from "@sanity/mutate";
10
9
  import { getPublishedId as getPublishedId$1 } from "@sanity/client/csm";
10
+ import { createSelector } from "reselect";
11
11
  import { applyPatches, parsePatch, makePatches, stringifyPatches } from "@sanity/diff-match-patch";
12
12
  import { isKeySegment, isKeyedObject } from "@sanity/types";
13
13
  import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
14
14
  var AuthStateType = /* @__PURE__ */ ((AuthStateType2) => (AuthStateType2.LOGGED_IN = "logged-in", AuthStateType2.LOGGING_IN = "logging-in", AuthStateType2.ERROR = "error", AuthStateType2.LOGGED_OUT = "logged-out", AuthStateType2))(AuthStateType || {});
15
+ function getPublishedId(id) {
16
+ const draftsPrefix = "drafts.";
17
+ return id.startsWith(draftsPrefix) ? id.slice(draftsPrefix.length) : id;
18
+ }
19
+ function getDraftId(id) {
20
+ const draftsPrefix = "drafts.";
21
+ return id.startsWith(draftsPrefix) ? id : `${draftsPrefix}${id}`;
22
+ }
23
+ function insecureRandomId() {
24
+ return Array.from({ length: 16 }, () => Math.floor(Math.random() * 16).toString(16)).join("");
25
+ }
26
+ function createSanityInstance(config = {}) {
27
+ const instanceId = crypto.randomUUID(), disposeListeners = /* @__PURE__ */ new Map(), disposed = { current: !1 }, instance = {
28
+ instanceId,
29
+ config,
30
+ isDisposed: () => disposed.current,
31
+ dispose: () => {
32
+ disposed.current || (disposed.current = !0, disposeListeners.forEach((listener) => listener()), disposeListeners.clear());
33
+ },
34
+ onDispose: (cb) => {
35
+ const listenerId = insecureRandomId();
36
+ return disposeListeners.set(listenerId, cb), () => {
37
+ disposeListeners.delete(listenerId);
38
+ };
39
+ },
40
+ getParent: () => {
41
+ },
42
+ createChild: (next) => Object.assign(
43
+ createSanityInstance({
44
+ ...config,
45
+ ...next,
46
+ ...config.auth === next.auth ? config.auth : config.auth && next.auth && { auth: { ...config.auth, ...next.auth } }
47
+ }),
48
+ { getParent: () => instance }
49
+ ),
50
+ match: (targetConfig) => {
51
+ if (Object.entries(pick(targetConfig, "auth", "projectId", "dataset")).every(
52
+ ([key, value]) => config[key] === value
53
+ ))
54
+ return instance;
55
+ const parent = instance.getParent();
56
+ if (parent) return parent.match(targetConfig);
57
+ }
58
+ };
59
+ return instance;
60
+ }
15
61
  function getEnv(key) {
16
62
  if (typeof import.meta < "u" && import.meta.env)
17
63
  return import.meta.env[key];
@@ -20,68 +66,78 @@ function getEnv(key) {
20
66
  if (typeof window < "u" && window.ENV)
21
67
  return window.ENV?.[key];
22
68
  }
23
- const resourceCache = /* @__PURE__ */ new WeakMap();
24
- function createResourceState(initialState, devToolsOptions) {
25
- const store = createStore$1()(devtools(() => initialState, devToolsOptions));
69
+ function createStoreState(initialState, devToolsOptions) {
70
+ const store = createStore()(devtools(() => initialState, devToolsOptions));
26
71
  return {
27
72
  get: store.getState,
28
73
  set: (actionKey, updatedState) => {
29
- store.getState() !== updatedState && store.setState(updatedState, !1, actionKey);
74
+ const currentState = store.getState(), nextState = typeof updatedState == "function" ? updatedState(currentState) : updatedState;
75
+ currentState !== nextState && store.setState(nextState, !1, actionKey);
30
76
  },
31
77
  observable: new Observable((observer) => {
32
78
  const emit = () => observer.next(store.getState());
33
- return emit(), store.subscribe(emit);
79
+ emit();
80
+ const unsubscribe = store.subscribe(emit);
81
+ return () => unsubscribe();
34
82
  })
35
83
  };
36
84
  }
37
- function initializeResource(instance, resource) {
38
- const fullName = resource.name === "Auth" ? "Auth-global" : `${resource.name}-${instance.identity.resourceId}`, initialState = resource.getInitialState(instance), state = createResourceState(initialState, {
39
- name: fullName,
40
- enabled: !!getEnv("DEV")
41
- }), dispose = resource.initialize?.call({ instance, state }, instance) ?? (() => {
42
- });
43
- return { state, dispose };
44
- }
45
- function getOrCreateResource(instance, resource) {
46
- const fullName = resource.name === "Auth" ? "Auth-global" : `${resource.name}-${instance.identity.resourceId}`;
47
- resourceCache.has(instance.identity) || resourceCache.set(instance.identity, /* @__PURE__ */ new Map());
48
- const initializedResources = resourceCache.get(instance.identity), cached = initializedResources.get(fullName);
49
- if (cached) return cached;
50
- const result = initializeResource(instance, resource);
51
- return initializedResources.set(fullName, result), result;
52
- }
53
- function disposeResources(identity) {
54
- const resources = resourceCache.get(identity);
55
- if (resources)
56
- for (const resource of resources.values())
57
- resource.dispose();
58
- }
59
- function createAction(resource, actionDefinition) {
60
- return (dependencies, ...args) => {
61
- const instance = "state" in dependencies ? dependencies.instance : dependencies, { state } = "state" in dependencies ? dependencies : getOrCreateResource(dependencies, resource), actionContext = { instance, state };
62
- return actionDefinition(actionContext).bind(actionContext)(...args);
85
+ function createStoreInstance(instance, { name, getInitialState, initialize }) {
86
+ const state = createStoreState(getInitialState(instance), {
87
+ enabled: !!getEnv("DEV"),
88
+ name: `${name}-${instance.config.projectId}.${instance.config.dataset}`
89
+ }), dispose = initialize?.({ state, instance }), disposed = { current: !1 };
90
+ return {
91
+ state,
92
+ dispose: () => {
93
+ disposed.current || (disposed.current = !0, dispose?.());
94
+ },
95
+ isDisposed: () => disposed.current
63
96
  };
64
97
  }
65
- function createInternalAction(actionDefinition) {
66
- return (actionContext, ...args) => actionDefinition(actionContext).bind(actionContext)(...args);
98
+ function createActionBinder(keyFn) {
99
+ const instanceRegistry = /* @__PURE__ */ new Map(), storeRegistry = /* @__PURE__ */ new Map();
100
+ return function(storeDefinition, action) {
101
+ return function(instance, ...params) {
102
+ const keySuffix = keyFn(instance.config), compositeKey = storeDefinition.name + (keySuffix ? `:${keySuffix}` : "");
103
+ let instances = instanceRegistry.get(compositeKey);
104
+ instances || (instances = /* @__PURE__ */ new Set(), instanceRegistry.set(compositeKey, instances)), instances.has(instance.instanceId) || (instances.add(instance.instanceId), instance.onDispose(() => {
105
+ instances.delete(instance.instanceId), instances.size === 0 && (storeRegistry.get(compositeKey)?.dispose(), storeRegistry.delete(compositeKey), instanceRegistry.delete(compositeKey));
106
+ }));
107
+ let storeInstance = storeRegistry.get(compositeKey);
108
+ return storeInstance || (storeInstance = createStoreInstance(instance, storeDefinition), storeRegistry.set(compositeKey, storeInstance)), action({ instance, state: storeInstance.state }, ...params);
109
+ };
110
+ };
67
111
  }
68
- function createStateSourceAction(resource, options) {
69
- 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;
70
- return createAction(resource, ({ state }) => function(...args) {
71
- const getCurrent = () => selector(state.get(), ...args), subscribe = (onStoreChanged) => {
72
- const cleanup = subscribeHandler?.(this, ...args), subscription = state.observable.pipe(
112
+ const bindActionByDataset = createActionBinder(({ projectId, dataset }) => {
113
+ if (!projectId || !dataset)
114
+ throw new Error("This API requires a project ID and dataset configured.");
115
+ return `${projectId}.${dataset}`;
116
+ }), bindActionGlobally = createActionBinder(() => "global");
117
+ function createStateSourceAction(options) {
118
+ 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();
119
+ function stateSourceAction(context, ...params) {
120
+ const { state, instance } = context, getCurrent = () => {
121
+ const currentState = state.get();
122
+ if (typeof currentState != "object" || currentState === null)
123
+ throw new Error(
124
+ `Expected store state to be an object but got "${typeof currentState}" instead`
125
+ );
126
+ let instanceCache = selectorContextCache.get(currentState);
127
+ instanceCache || (instanceCache = /* @__PURE__ */ new WeakMap(), selectorContextCache.set(currentState, instanceCache));
128
+ let selectorContext = instanceCache.get(instance);
129
+ return selectorContext || (selectorContext = { state: currentState, instance }, instanceCache.set(instance, selectorContext)), selector(selectorContext, ...params);
130
+ }, subscribe = (onStoreChanged) => {
131
+ const cleanup = subscribeHandler?.(context, ...params), subscription = state.observable.pipe(
132
+ // Derive value from current state
73
133
  map(getCurrent),
134
+ // Filter unchanged values using custom equality check
74
135
  distinctUntilChanged(isEqual2),
75
- // skip the first emission because we only want to emit when the
76
- // value changes. `distinctUntilChanged` will always emit the first
77
- // the first value so we skip this emission
136
+ // Skip initial emission since we only want changes
78
137
  skip(1)
79
138
  ).subscribe({
80
139
  next: () => onStoreChanged?.(),
81
- // the convention is to have the selector throw the error so we
82
- // invoke onStoreChanged on error as well. this will cause the
83
- // observable code path below to emit an error because the selector
84
- // will throw and that will be used to emit an .error on the observer
140
+ // Propagate selector errors to both subscription types
85
141
  error: () => onStoreChanged?.()
86
142
  });
87
143
  return () => {
@@ -102,96 +158,144 @@ function createStateSourceAction(resource, options) {
102
158
  subscribe,
103
159
  observable
104
160
  };
105
- });
161
+ }
162
+ return stateSourceAction;
106
163
  }
107
- const DEFAULT_BASE = "http://localhost", AUTH_CODE_PARAM = "sid", DEFAULT_API_VERSION$1 = "2021-06-07", REQUEST_TAG_PREFIX = "sanity.sdk.auth", refreshStampedToken = createInternalAction(
108
- ({ state }) => {
109
- const { clientFactory, apiHost, storageArea, storageKey } = state.get().options, refreshToken$ = state.observable.pipe(
110
- map(({ authState }) => authState),
111
- filter(
112
- (authState) => authState.type === AuthStateType.LOGGED_IN
113
- ),
114
- distinctUntilChanged(),
115
- filter((authState) => authState.token.includes("-st")),
116
- // Ensure we only try to refresh stamped tokens
117
- switchMap(
118
- (authState) => interval(10 * 60 * 1e3).pipe(
119
- takeWhile(() => state.get().authState.type === AuthStateType.LOGGED_IN),
120
- map(() => authState.token),
121
- distinctUntilChanged(),
122
- map(
123
- (token) => clientFactory({
124
- apiVersion: DEFAULT_API_VERSION$1,
125
- requestTagPrefix: "sdk.token-refresh",
126
- useProjectHostname: !1,
127
- token,
128
- ignoreBrowserTokenWarning: !0,
129
- ...apiHost && { apiHost }
130
- })
131
- ),
132
- switchMap(
133
- (client) => client.observable.request({
134
- uri: "auth/refresh-token",
135
- method: "POST",
136
- body: {
137
- token: authState.token
138
- }
139
- })
140
- )
141
- )
142
- )
143
- );
144
- return function() {
145
- return refreshToken$.subscribe({
146
- next: (response) => {
147
- state.set("setRefreshStampedToken", (prev) => ({
148
- authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, token: response.token } : prev.authState
149
- })), storageArea?.setItem(storageKey, JSON.stringify({ token: response.token }));
150
- },
151
- error: (error) => {
152
- state.set("setRefreshStampedTokenError", { authState: { type: AuthStateType.ERROR, error } });
153
- }
154
- });
155
- };
164
+ const DEFAULT_BASE = "http://localhost", AUTH_CODE_PARAM = "sid", DEFAULT_API_VERSION$1 = "2021-06-07", REQUEST_TAG_PREFIX = "sanity.sdk.auth", REFRESH_INTERVAL = 12 * 60 * 60 * 1e3, LOCK_NAME = "sanity-token-refresh-lock";
165
+ function getLastRefreshTime(storageArea, storageKey) {
166
+ try {
167
+ const data = storageArea?.getItem(`${storageKey}_last_refresh`);
168
+ return data ? parseInt(data, 10) : 0;
169
+ } catch {
170
+ return 0;
156
171
  }
157
- ), subscribeToStateAndFetchCurrentUser = createInternalAction(
158
- ({ state }) => {
159
- const { clientFactory, apiHost } = state.get().options, currentUser$ = state.observable.pipe(
160
- map(({ authState }) => authState),
161
- filter(
162
- (authState) => authState.type === AuthStateType.LOGGED_IN && !authState.currentUser
163
- ),
164
- map((authState) => authState.token),
165
- distinctUntilChanged()
166
- ).pipe(
167
- map(
168
- (token) => clientFactory({
169
- apiVersion: DEFAULT_API_VERSION$1,
170
- requestTagPrefix: REQUEST_TAG_PREFIX,
171
- token,
172
- ignoreBrowserTokenWarning: !0,
173
- useProjectHostname: !1,
174
- ...apiHost && { apiHost }
175
- })
176
- ),
177
- switchMap(
178
- (client) => client.observable.request({ uri: "/users/me", method: "GET" })
179
- )
180
- );
181
- return function() {
182
- return currentUser$.subscribe({
183
- next: (currentUser) => {
184
- state.set("setCurrentUser", (prev) => ({
185
- authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, currentUser } : prev.authState
186
- }));
187
- },
188
- error: (error) => {
189
- state.set("setError", { authState: { type: AuthStateType.ERROR, error } });
190
- }
191
- });
192
- };
172
+ }
173
+ function setLastRefreshTime(storageArea, storageKey) {
174
+ try {
175
+ storageArea?.setItem(`${storageKey}_last_refresh`, Date.now().toString());
176
+ } catch {
193
177
  }
194
- );
178
+ }
179
+ function getNextRefreshDelay(storageArea, storageKey) {
180
+ const lastRefresh = getLastRefreshTime(storageArea, storageKey);
181
+ if (!lastRefresh) return 0;
182
+ const now = Date.now(), nextRefreshTime = lastRefresh + REFRESH_INTERVAL;
183
+ return Math.max(0, nextRefreshTime - now);
184
+ }
185
+ function createTokenRefreshStream(token, clientFactory, apiHost) {
186
+ return new Observable((subscriber) => {
187
+ const subscription = clientFactory({
188
+ apiVersion: DEFAULT_API_VERSION$1,
189
+ requestTagPrefix: "sdk.token-refresh",
190
+ useProjectHostname: !1,
191
+ token,
192
+ ignoreBrowserTokenWarning: !0,
193
+ ...apiHost && { apiHost }
194
+ }).observable.request({
195
+ uri: "auth/refresh-token",
196
+ method: "POST",
197
+ body: {
198
+ token
199
+ }
200
+ }).subscribe(subscriber);
201
+ return () => subscription.unsubscribe();
202
+ });
203
+ }
204
+ async function acquireTokenRefreshLock(refreshFn, storageArea, storageKey) {
205
+ if (!navigator.locks)
206
+ return !0;
207
+ try {
208
+ return await navigator.locks.request(LOCK_NAME, { mode: "exclusive" }, async (lock) => {
209
+ if (!lock) return !1;
210
+ for (; ; ) {
211
+ const delay2 = getNextRefreshDelay(storageArea, storageKey);
212
+ delay2 > 0 && await new Promise((resolve) => setTimeout(resolve, delay2)), await refreshFn(), setLastRefreshTime(storageArea, storageKey), await new Promise((resolve) => setTimeout(resolve, REFRESH_INTERVAL));
213
+ }
214
+ }) === !0;
215
+ } catch {
216
+ return !1;
217
+ }
218
+ }
219
+ const refreshStampedToken = ({ state }) => {
220
+ const { clientFactory, apiHost, storageArea, storageKey } = state.get().options;
221
+ return state.observable.pipe(
222
+ map((storeState) => ({
223
+ authState: storeState.authState,
224
+ dashboardContext: storeState.dashboardContext
225
+ })),
226
+ filter(
227
+ (storeState) => storeState.authState.type === AuthStateType.LOGGED_IN
228
+ ),
229
+ distinctUntilChanged(),
230
+ filter((storeState) => storeState.authState.token.includes("-st")),
231
+ // Ensure we only try to refresh stamped tokens
232
+ switchMap((storeState) => {
233
+ const performRefresh = async () => {
234
+ const response = await firstValueFrom(
235
+ createTokenRefreshStream(storeState.authState.token, clientFactory, apiHost)
236
+ );
237
+ state.set("setRefreshStampedToken", (prev) => ({
238
+ authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, token: response.token } : prev.authState
239
+ })), storageArea?.setItem(storageKey, JSON.stringify({ token: response.token }));
240
+ };
241
+ return storeState.dashboardContext ? timer(0, REFRESH_INTERVAL).pipe(
242
+ takeWhile(() => storeState.authState.type === AuthStateType.LOGGED_IN),
243
+ switchMap(
244
+ () => createTokenRefreshStream(storeState.authState.token, clientFactory, apiHost)
245
+ )
246
+ ) : from(acquireTokenRefreshLock(performRefresh, storageArea, storageKey)).pipe(
247
+ filter((hasLock) => hasLock),
248
+ // This observable will never emit after the first value
249
+ // because acquireTokenRefreshLock handles the refresh loop internally
250
+ map(() => ({ token: storeState.authState.token }))
251
+ );
252
+ })
253
+ ).subscribe({
254
+ next: (response) => {
255
+ state.set("setRefreshStampedToken", (prev) => ({
256
+ authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, token: response.token } : prev.authState
257
+ })), storageArea?.setItem(storageKey, JSON.stringify({ token: response.token }));
258
+ },
259
+ error: (error) => {
260
+ state.set("setRefreshStampedTokenError", { authState: { type: AuthStateType.ERROR, error } });
261
+ }
262
+ });
263
+ }, subscribeToStateAndFetchCurrentUser = ({
264
+ state
265
+ }) => {
266
+ const { clientFactory, apiHost } = state.get().options;
267
+ return state.observable.pipe(
268
+ map(({ authState }) => authState),
269
+ filter(
270
+ (authState) => authState.type === AuthStateType.LOGGED_IN && !authState.currentUser
271
+ ),
272
+ map((authState) => authState.token),
273
+ distinctUntilChanged()
274
+ ).pipe(
275
+ map(
276
+ (token) => clientFactory({
277
+ apiVersion: DEFAULT_API_VERSION$1,
278
+ requestTagPrefix: REQUEST_TAG_PREFIX,
279
+ token,
280
+ ignoreBrowserTokenWarning: !0,
281
+ useProjectHostname: !1,
282
+ ...apiHost && { apiHost }
283
+ })
284
+ ),
285
+ switchMap(
286
+ (client) => client.observable.request({ uri: "/users/me", method: "GET" })
287
+ )
288
+ ).subscribe({
289
+ next: (currentUser) => {
290
+ state.set("setCurrentUser", (prev) => ({
291
+ authState: prev.authState.type === AuthStateType.LOGGED_IN ? { ...prev.authState, currentUser } : prev.authState
292
+ }));
293
+ },
294
+ error: (error) => {
295
+ state.set("setError", { authState: { type: AuthStateType.ERROR, error } });
296
+ }
297
+ });
298
+ };
195
299
  function getAuthCode(callbackUrl, locationHref) {
196
300
  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, authCode = new URLSearchParams(loc.hash.slice(1)).get(AUTH_CODE_PARAM) || new URLSearchParams(loc.search).get(AUTH_CODE_PARAM);
197
301
  return authCode && callbackLocationMatches ? authCode : null;
@@ -226,24 +330,22 @@ function getDefaultLocation() {
226
330
  return DEFAULT_BASE;
227
331
  }
228
332
  }
229
- const subscribeToStorageEventsAndSetToken = createInternalAction(
230
- ({ state }) => {
231
- const { storageArea, storageKey } = state.get().options, tokenFromStorage$ = defer(getStorageEvents).pipe(
232
- filter(
233
- (e3) => e3.storageArea === storageArea && e3.key === storageKey
234
- ),
235
- map(() => getTokenFromStorage(storageArea, storageKey)),
236
- distinctUntilChanged()
237
- );
238
- return function() {
239
- return tokenFromStorage$.subscribe((token) => {
240
- state.set("updateTokenFromStorageEvent", {
241
- authState: token ? { type: AuthStateType.LOGGED_IN, token, currentUser: null } : { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }
242
- });
243
- });
244
- };
245
- }
246
- );
333
+ const subscribeToStorageEventsAndSetToken = ({
334
+ state
335
+ }) => {
336
+ const { storageArea, storageKey } = state.get().options;
337
+ return defer(getStorageEvents).pipe(
338
+ filter(
339
+ (e3) => e3.storageArea === storageArea && e3.key === storageKey
340
+ ),
341
+ map(() => getTokenFromStorage(storageArea, storageKey)),
342
+ distinctUntilChanged()
343
+ ).subscribe((token) => {
344
+ state.set("updateTokenFromStorageEvent", {
345
+ authState: token ? { type: AuthStateType.LOGGED_IN, token, currentUser: null } : { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }
346
+ });
347
+ });
348
+ };
247
349
  let tokenRefresherRunning = !1;
248
350
  const authStore = {
249
351
  name: "Auth",
@@ -257,12 +359,20 @@ const authStore = {
257
359
  initialLocationHref = getDefaultLocation(),
258
360
  storageArea = getDefaultStorage()
259
361
  } = instance.config.auth ?? {}, storageKey = "__sanity_auth_token";
362
+ let loginDomain = "https://www.sanity.io";
363
+ try {
364
+ apiHost && new URL(apiHost).hostname.endsWith(".sanity.work") && (loginDomain = "https://www.sanity.work");
365
+ } catch {
366
+ }
367
+ const loginUrl = new URL("/login", loginDomain);
368
+ loginUrl.searchParams.set("origin", initialLocationHref), loginUrl.searchParams.set("type", "stampedToken"), loginUrl.searchParams.set("withSid", "true");
260
369
  let authState;
261
370
  const token = getTokenFromStorage(storageArea, storageKey);
262
371
  return providedToken ? authState = { type: AuthStateType.LOGGED_IN, token: providedToken, currentUser: null } : getAuthCode(callbackUrl, initialLocationHref) ? authState = { type: AuthStateType.LOGGING_IN, isExchangingToken: !1 } : token ? authState = { type: AuthStateType.LOGGED_IN, token, currentUser: null } : authState = { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }, {
263
372
  authState,
264
373
  options: {
265
374
  apiHost,
375
+ loginUrl: loginUrl.toString(),
266
376
  callbackUrl,
267
377
  customProviders,
268
378
  providedToken,
@@ -273,61 +383,36 @@ const authStore = {
273
383
  }
274
384
  };
275
385
  },
276
- initialize() {
277
- const stateSubscription = subscribeToStateAndFetchCurrentUser(this);
278
- let storageEventsSubscription;
279
- this.state.get().options?.storageArea && (storageEventsSubscription = subscribeToStorageEventsAndSetToken(this));
280
- let refreshStampedTokenSubscription;
281
- return tokenRefresherRunning || (tokenRefresherRunning = !0, refreshStampedTokenSubscription = refreshStampedToken(this)), () => {
282
- stateSubscription.unsubscribe(), storageEventsSubscription?.unsubscribe(), refreshStampedTokenSubscription?.unsubscribe();
386
+ initialize(context) {
387
+ const subscriptions = [];
388
+ return subscriptions.push(subscribeToStateAndFetchCurrentUser(context)), context.state.get().options?.storageArea && subscriptions.push(subscribeToStorageEventsAndSetToken(context)), tokenRefresherRunning || (tokenRefresherRunning = !0, subscriptions.push(refreshStampedToken(context))), () => {
389
+ for (const subscription of subscriptions)
390
+ subscription.unsubscribe();
283
391
  };
284
392
  }
285
- }, getCurrentUserState = createStateSourceAction(
393
+ }, getCurrentUserState = bindActionGlobally(
286
394
  authStore,
287
- ({ authState }) => authState.type === AuthStateType.LOGGED_IN ? authState.currentUser : null
288
- ), getTokenState = createStateSourceAction(
395
+ createStateSourceAction(
396
+ ({ state: { authState } }) => authState.type === AuthStateType.LOGGED_IN ? authState.currentUser : null
397
+ )
398
+ ), getTokenState = bindActionGlobally(
289
399
  authStore,
290
- ({ authState }) => authState.type === AuthStateType.LOGGED_IN ? authState.token : null
291
- ), getLoginUrlsState = createStateSourceAction(
400
+ createStateSourceAction(
401
+ ({ state: { authState } }) => authState.type === AuthStateType.LOGGED_IN ? authState.token : null
402
+ )
403
+ ), getLoginUrlState = bindActionGlobally(
292
404
  authStore,
293
- ({ providers }) => providers ?? null
294
- ), getAuthState = createStateSourceAction(authStore, ({ authState }) => authState), getDashboardOrganizationId = createStateSourceAction(
405
+ createStateSourceAction(({ state: { options } }) => options.loginUrl)
406
+ ), getAuthState = bindActionGlobally(
295
407
  authStore,
296
- ({ dashboardContext }) => dashboardContext?.orgId
297
- ), fetchLoginUrls = createAction(authStore, ({ state }) => {
298
- const { callbackUrl, clientFactory, apiHost, customProviders } = state.get().options, client = clientFactory({
299
- apiVersion: DEFAULT_API_VERSION$1,
300
- requestTagPrefix: REQUEST_TAG_PREFIX,
301
- useProjectHostname: !1,
302
- ...apiHost && { apiHost }
303
- });
304
- return async function() {
305
- const cachedProviders = state.get().providers;
306
- if (cachedProviders) return cachedProviders;
307
- const { providers: defaultProviders } = await client.request({
308
- uri: "/auth/providers",
309
- tag: "fetch-providers"
310
- });
311
- let providers;
312
- if (typeof customProviders == "function")
313
- providers = await customProviders(defaultProviders);
314
- else if (!customProviders?.length)
315
- providers = defaultProviders;
316
- else {
317
- const customProviderUrls = new Set(customProviders.map((p2) => p2.url));
318
- providers = defaultProviders.filter((official) => !customProviderUrls.has(official.url)).concat(customProviders);
319
- }
320
- const configuredProviders = providers.map((provider) => {
321
- const url = new URL(provider.url), origin = new URL(
322
- callbackUrl ? new URL(callbackUrl, new URL(getDefaultLocation()).origin).toString() : getDefaultLocation()
323
- ), hashParams = new URLSearchParams(origin.hash.slice(1));
324
- return hashParams.delete("sid"), origin.hash = hashParams.toString(), origin.searchParams.delete("sid"), origin.searchParams.delete("url"), origin.searchParams.delete("error"), url.searchParams.set("origin", origin.toString()), url.searchParams.set("withSid", "true"), url.searchParams.set("type", "stampedToken"), { ...provider, url: url.toString() };
325
- });
326
- return state.set("fetchedLoginUrls", { providers: configuredProviders }), configuredProviders;
327
- };
328
- }), handleAuthCallback = createAction(authStore, ({ state }) => {
329
- const { providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey } = state.get().options;
330
- return async function(locationHref = getDefaultLocation()) {
408
+ createStateSourceAction(({ state: { authState } }) => authState)
409
+ ), getDashboardOrganizationId = bindActionGlobally(
410
+ authStore,
411
+ createStateSourceAction(({ state: { dashboardContext } }) => dashboardContext?.orgId)
412
+ ), handleAuthCallback = bindActionGlobally(
413
+ authStore,
414
+ async ({ state }, locationHref = getDefaultLocation()) => {
415
+ const { providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey } = state.get().options;
331
416
  if (providedToken) return !1;
332
417
  const { authState } = state.get();
333
418
  if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken) return !1;
@@ -364,35 +449,403 @@ const authStore = {
364
449
  } catch (error) {
365
450
  return state.set("exchangeSessionForTokenError", { authState: { type: AuthStateType.ERROR, error } }), !1;
366
451
  }
452
+ }
453
+ ), logout = bindActionGlobally(authStore, async ({ state }) => {
454
+ const { clientFactory, apiHost, providedToken, storageArea, storageKey } = state.get().options;
455
+ if (providedToken) return;
456
+ const { authState } = state.get();
457
+ if (authState.type === AuthStateType.LOGGED_OUT && authState.isDestroyingSession) return;
458
+ const token = authState.type === AuthStateType.LOGGED_IN && authState.token;
459
+ try {
460
+ token && (state.set("loggingOut", {
461
+ authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !0 }
462
+ }), await clientFactory({
463
+ token,
464
+ requestTagPrefix: REQUEST_TAG_PREFIX,
465
+ apiVersion: DEFAULT_API_VERSION$1,
466
+ ...apiHost && { apiHost },
467
+ useProjectHostname: !1
468
+ }).request({ uri: "/auth/logout", method: "POST" }));
469
+ } finally {
470
+ state.set("logoutSuccess", {
471
+ authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }
472
+ }), storageArea?.removeItem(storageKey);
473
+ }
474
+ }), DEFAULT_API_VERSION = "2024-11-12", DEFAULT_REQUEST_TAG_PREFIX = "sanity.sdk", allowedKeys = Object.keys({
475
+ apiHost: null,
476
+ useCdn: null,
477
+ token: null,
478
+ perspective: null,
479
+ proxy: null,
480
+ withCredentials: null,
481
+ timeout: null,
482
+ maxRetries: null,
483
+ dataset: null,
484
+ projectId: null,
485
+ scope: null,
486
+ apiVersion: null,
487
+ requestTagPrefix: null,
488
+ useProjectHostname: null
489
+ }), DEFAULT_CLIENT_CONFIG = {
490
+ apiVersion: DEFAULT_API_VERSION,
491
+ useCdn: !1,
492
+ ignoreBrowserTokenWarning: !0,
493
+ allowReconfigure: !1,
494
+ requestTagPrefix: DEFAULT_REQUEST_TAG_PREFIX
495
+ }, clientStore = {
496
+ name: "clientStore",
497
+ getInitialState: (instance) => ({
498
+ clients: {},
499
+ token: getTokenState(instance).getCurrent()
500
+ }),
501
+ initialize(context) {
502
+ const subscription = listenToToken(context);
503
+ return () => subscription.unsubscribe();
504
+ }
505
+ }, listenToToken = ({ instance, state }) => getTokenState(instance).observable.subscribe((token) => {
506
+ state.set("setTokenAndResetClients", { token, clients: {} });
507
+ }), getClientConfigKey = (options) => JSON.stringify(pick(options, ...allowedKeys)), getClient = bindActionGlobally(
508
+ clientStore,
509
+ ({ state, instance }, options) => {
510
+ const disallowedKeys = Object.keys(options).filter((key2) => !allowedKeys.includes(key2));
511
+ if (disallowedKeys.length > 0) {
512
+ const listFormatter = new Intl.ListFormat("en", { style: "long", type: "conjunction" });
513
+ throw new Error(
514
+ `The client options provided contains unsupported properties: ${listFormatter.format(disallowedKeys)}. Allowed keys are: ${listFormatter.format(allowedKeys)}.`
515
+ );
516
+ }
517
+ const { token, clients } = state.get(), projectId = options.projectId ?? instance.config.projectId, dataset = options.dataset ?? instance.config.dataset, apiHost = options.apiHost ?? instance.config.auth?.apiHost, effectiveOptions = {
518
+ ...DEFAULT_CLIENT_CONFIG,
519
+ ...(options.scope === "global" || !projectId) && { useProjectHostname: !1 },
520
+ ...token && { token },
521
+ ...options,
522
+ ...projectId && { projectId },
523
+ ...dataset && { dataset },
524
+ ...apiHost && { apiHost }
525
+ }, key = getClientConfigKey(effectiveOptions);
526
+ if (clients[key]) return clients[key];
527
+ const client = createClient(effectiveOptions);
528
+ return state.set("addClient", (prev) => ({ clients: { ...prev.clients, [key]: client } })), client;
529
+ }
530
+ ), getClientState = bindActionGlobally(
531
+ clientStore,
532
+ createStateSourceAction(({ instance }, options) => getClient(instance, options))
533
+ ), destroyController$1 = ({ state }) => {
534
+ const { controller } = state.get();
535
+ controller && (controller.destroy(), state.set("destroyController", {
536
+ controller: null,
537
+ channels: /* @__PURE__ */ new Map()
538
+ }));
539
+ }, getOrCreateChannel$1 = ({ state }, options) => {
540
+ const controller = state.get().controller;
541
+ if (!controller)
542
+ throw new Error("Controller must be initialized before using or creating channels");
543
+ const channels = state.get().channels, existing = channels.get(options.name);
544
+ if (existing) {
545
+ if (!isEqual(existing.options, options))
546
+ throw new Error(`Channel "${options.name}" already exists with different options`);
547
+ return state.set("incrementChannelRefCount", {
548
+ channels: new Map(channels).set(options.name, {
549
+ ...existing,
550
+ refCount: existing.refCount + 1
551
+ })
552
+ }), existing.channel.start(), existing.channel;
553
+ }
554
+ const channel = controller.createChannel(options);
555
+ return channel.start(), state.set("createChannel", {
556
+ channels: new Map(channels).set(options.name, {
557
+ channel,
558
+ options,
559
+ refCount: 1
560
+ })
561
+ }), channel;
562
+ }, getOrCreateController$1 = ({ state, instance }, targetOrigin) => {
563
+ const { controller, controllerOrigin } = state.get();
564
+ if (controller && controllerOrigin === targetOrigin)
565
+ return controller;
566
+ controller && destroyController$1({ state });
567
+ const newController = createController({ targetOrigin });
568
+ return state.set("initializeController", {
569
+ controllerOrigin: targetOrigin,
570
+ controller: newController
571
+ }), newController;
572
+ }, releaseChannel$1 = ({ state }, name) => {
573
+ const channels = state.get().channels, channelEntry = channels.get(name);
574
+ if (channelEntry) {
575
+ const newRefCount = channelEntry.refCount === 0 ? 0 : channelEntry.refCount - 1;
576
+ newRefCount === 0 ? (channelEntry.channel.stop(), channels.delete(name), state.set("releaseChannel", { channels: new Map(channels) })) : state.set("releaseChannel", {
577
+ channels: new Map(channels).set(name, {
578
+ ...channelEntry,
579
+ refCount: newRefCount
580
+ })
581
+ });
582
+ }
583
+ }, comlinkControllerStore = {
584
+ name: "connectionStore",
585
+ getInitialState: () => ({
586
+ controller: null,
587
+ controllerOrigin: null,
588
+ channels: /* @__PURE__ */ new Map()
589
+ }),
590
+ initialize({ instance }) {
591
+ return () => {
592
+ destroyController(instance);
593
+ };
594
+ }
595
+ }, destroyController = bindActionGlobally(
596
+ comlinkControllerStore,
597
+ destroyController$1
598
+ ), getOrCreateChannel = bindActionGlobally(
599
+ comlinkControllerStore,
600
+ getOrCreateChannel$1
601
+ ), getOrCreateController = bindActionGlobally(
602
+ comlinkControllerStore,
603
+ getOrCreateController$1
604
+ ), releaseChannel = bindActionGlobally(comlinkControllerStore, releaseChannel$1), getOrCreateNode$1 = ({ state }, options) => {
605
+ const nodes = state.get().nodes, existing = nodes.get(options.name);
606
+ if (existing) {
607
+ if (!isEqual(existing.options, options))
608
+ throw new Error(`Node "${options.name}" already exists with different options`);
609
+ return state.set("incrementNodeRefCount", {
610
+ nodes: new Map(nodes).set(options.name, {
611
+ ...existing,
612
+ refCount: existing.refCount + 1
613
+ })
614
+ }), existing.node.start(), existing.node;
615
+ }
616
+ const node = createNode(options);
617
+ return node.start(), nodes.set(options.name, { node, options, refCount: 1 }), state.set("createNode", { nodes }), node;
618
+ }, releaseNode$1 = ({ state }, name) => {
619
+ const nodes = state.get().nodes, nodeEntry = nodes.get(name);
620
+ if (nodeEntry) {
621
+ const newRefCount = nodeEntry.refCount === 0 ? 0 : nodeEntry.refCount - 1;
622
+ newRefCount === 0 ? (nodeEntry.node.stop(), nodes.delete(name), state.set("releaseNode", { nodes: new Map(nodes) })) : state.set("releaseNode", {
623
+ nodes: new Map(nodes).set(name, {
624
+ ...nodeEntry,
625
+ refCount: newRefCount
626
+ })
627
+ });
628
+ }
629
+ }, comlinkNodeStore = {
630
+ name: "nodeStore",
631
+ getInitialState: () => ({
632
+ nodes: /* @__PURE__ */ new Map()
633
+ }),
634
+ initialize({ state }) {
635
+ return () => {
636
+ state.get().nodes.forEach(({ node }) => {
637
+ node.stop();
638
+ });
639
+ };
640
+ }
641
+ }, releaseNode = bindActionGlobally(comlinkNodeStore, releaseNode$1), getOrCreateNode = bindActionGlobally(comlinkNodeStore, getOrCreateNode$1);
642
+ function createFetcherStore({
643
+ name,
644
+ fetcher: getObservable,
645
+ getKey,
646
+ fetchThrottleInternal = 1e3,
647
+ stateExpirationDelay = 5e3
648
+ }) {
649
+ const store = {
650
+ name,
651
+ getInitialState: () => ({
652
+ stateByParams: {}
653
+ }),
654
+ initialize: (context) => {
655
+ const subscription = subscribeToSubscriptionsAndFetch(context);
656
+ return () => subscription.unsubscribe();
657
+ }
658
+ }, subscribeToSubscriptionsAndFetch = ({
659
+ state
660
+ }) => state.observable.pipe(
661
+ // Map the state to an array of [serialized, entry] pairs.
662
+ switchMap$1((s2) => {
663
+ const entries = Object.entries(s2.stateByParams);
664
+ return entries.length > 0 ? from(entries) : EMPTY;
665
+ }),
666
+ // Group by the serialized key.
667
+ groupBy(([key]) => key),
668
+ mergeMap(
669
+ (group$) => group$.pipe(
670
+ // Emit an initial value for pairwise comparisons.
671
+ startWith([group$.key, void 0]),
672
+ pairwise(),
673
+ // Trigger only when the subscriptions array grows.
674
+ filter$1(([[, prevEntry], [, currEntry]]) => {
675
+ const prevSubs = prevEntry?.subscriptions ?? [];
676
+ return (currEntry?.subscriptions ?? []).length > prevSubs.length;
677
+ }),
678
+ map$1(([, [, currEntry]]) => currEntry),
679
+ // Only trigger if we haven't fetched recently.
680
+ filter$1((entry) => {
681
+ const lastFetch = entry?.lastFetchInitiatedAt;
682
+ return lastFetch ? Date.now() - new Date(lastFetch).getTime() >= fetchThrottleInternal : !0;
683
+ }),
684
+ switchMap$1((entry) => entry ? (state.set("setLastFetchInitiatedAt", (prev) => ({
685
+ stateByParams: {
686
+ ...prev.stateByParams,
687
+ [entry.key]: {
688
+ ...entry,
689
+ ...prev.stateByParams[entry.key],
690
+ lastFetchInitiatedAt: (/* @__PURE__ */ new Date()).toISOString()
691
+ }
692
+ }
693
+ })), getObservable(entry.instance)(...entry.params).pipe(
694
+ // the `createStateSourceAction` util requires the update
695
+ // to
696
+ delay(0, asapScheduler),
697
+ tap(
698
+ (data) => state.set("setData", (prev) => ({
699
+ stateByParams: {
700
+ ...prev.stateByParams,
701
+ [entry.key]: {
702
+ ...omit(entry, "error"),
703
+ ...omit(prev.stateByParams[entry.key], "error"),
704
+ data
705
+ }
706
+ }
707
+ }))
708
+ ),
709
+ catchError((error) => (state.set("setError", (prev) => ({
710
+ stateByParams: {
711
+ ...prev.stateByParams,
712
+ [entry.key]: {
713
+ ...entry,
714
+ ...prev.stateByParams[entry.key],
715
+ error
716
+ }
717
+ }
718
+ })), EMPTY))
719
+ )) : EMPTY)
720
+ )
721
+ )
722
+ ).subscribe({
723
+ error: (error) => state.set("setError", { error })
724
+ }), getState = bindActionGlobally(
725
+ store,
726
+ createStateSourceAction({
727
+ selector: ({
728
+ instance,
729
+ state: { stateByParams, error }
730
+ }, ...params) => {
731
+ if (error) throw error;
732
+ const key = getKey(instance, ...params), entry = stateByParams[key];
733
+ if (entry?.error) throw entry.error;
734
+ return entry?.data;
735
+ },
736
+ onSubscribe: ({ instance, state }, ...params) => {
737
+ const subscriptionId = insecureRandomId(), key = getKey(instance, ...params);
738
+ return state.set("addSubscription", (prev) => ({
739
+ stateByParams: {
740
+ ...prev.stateByParams,
741
+ [key]: {
742
+ ...prev.stateByParams[key],
743
+ instance,
744
+ key,
745
+ params: prev.stateByParams[key]?.params || params,
746
+ subscriptions: [...prev.stateByParams[key]?.subscriptions || [], subscriptionId]
747
+ }
748
+ }
749
+ })), () => {
750
+ setTimeout(() => {
751
+ state.set("removeSubscription", (prev) => {
752
+ const entry = prev.stateByParams[key];
753
+ if (!entry) return prev;
754
+ const newSubs = (entry.subscriptions || []).filter((id) => id !== subscriptionId);
755
+ return newSubs.length === 0 ? { stateByParams: omit(prev.stateByParams, key) } : {
756
+ stateByParams: {
757
+ ...prev.stateByParams,
758
+ [key]: {
759
+ ...entry,
760
+ subscriptions: newSubs
761
+ }
762
+ }
763
+ };
764
+ });
765
+ }, stateExpirationDelay);
766
+ };
767
+ }
768
+ })
769
+ ), resolveState = bindActionGlobally(
770
+ store,
771
+ ({ instance }, ...params) => firstValueFrom(getState(instance, ...params).observable.pipe(first((i2) => i2 !== void 0)))
772
+ );
773
+ return { getState, resolveState };
774
+ }
775
+ const API_VERSION$5 = "v2025-02-19", datasets = createFetcherStore({
776
+ name: "Datasets",
777
+ getKey: (instance, options) => {
778
+ const projectId = options?.projectId ?? instance.config.projectId;
779
+ if (!projectId)
780
+ throw new Error("A projectId is required to use the project API.");
781
+ return projectId;
782
+ },
783
+ fetcher: (instance) => (options) => getClientState(instance, {
784
+ apiVersion: API_VERSION$5,
785
+ // non-null assertion is fine because we check above
786
+ projectId: options?.projectId ?? instance.config.projectId,
787
+ useProjectHostname: !0
788
+ }).observable.pipe(switchMap((client) => client.observable.datasets.list()))
789
+ }), getDatasetsState = datasets.getState, resolveDatasets = datasets.resolveState, isSanityMutatePatch = (value) => !(typeof value != "object" || !value || !("type" in value) || typeof value.type != "string" || value.type !== "patch" || !("id" in value) || typeof value.id != "string" || !("patches" in value) || !Array.isArray(value.patches));
790
+ function createDocument(doc) {
791
+ return {
792
+ type: "document.create",
793
+ ...doc,
794
+ ...doc.documentId && { documentId: getPublishedId(doc.documentId) }
795
+ };
796
+ }
797
+ function deleteDocument(doc) {
798
+ return {
799
+ type: "document.delete",
800
+ ...doc,
801
+ documentId: getPublishedId(doc.documentId)
802
+ };
803
+ }
804
+ function convertSanityMutatePatch(sanityPatchMutation) {
805
+ return SanityEncoder.encode(sanityPatchMutation).map((i2) => {
806
+ const copy = { ...i2.patch };
807
+ return "id" in copy && delete copy.id, copy;
808
+ });
809
+ }
810
+ function editDocument(doc, patches) {
811
+ if (isSanityMutatePatch(patches)) {
812
+ const converted = convertSanityMutatePatch(patches) ?? [];
813
+ return {
814
+ ...doc,
815
+ type: "document.edit",
816
+ documentId: getPublishedId(doc.documentId),
817
+ patches: converted
818
+ };
819
+ }
820
+ return {
821
+ ...doc,
822
+ type: "document.edit",
823
+ documentId: getPublishedId(doc.documentId),
824
+ ...patches && { patches: Array.isArray(patches) ? patches : [patches] }
825
+ };
826
+ }
827
+ function publishDocument(doc) {
828
+ return {
829
+ type: "document.publish",
830
+ ...doc,
831
+ documentId: getPublishedId(doc.documentId)
832
+ };
833
+ }
834
+ function unpublishDocument(doc) {
835
+ return {
836
+ type: "document.unpublish",
837
+ ...doc,
838
+ documentId: getPublishedId(doc.documentId)
367
839
  };
368
- }), logout = createAction(authStore, ({ state }) => {
369
- const { clientFactory, apiHost, providedToken, storageArea, storageKey } = state.get().options;
370
- return async function() {
371
- if (providedToken) return;
372
- const { authState } = state.get();
373
- if (authState.type === AuthStateType.LOGGED_OUT && authState.isDestroyingSession) return;
374
- const token = authState.type === AuthStateType.LOGGED_IN && authState.token;
375
- try {
376
- token && (state.set("loggingOut", {
377
- authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !0 }
378
- }), await clientFactory({
379
- token,
380
- requestTagPrefix: REQUEST_TAG_PREFIX,
381
- apiVersion: DEFAULT_API_VERSION$1,
382
- ...apiHost && { apiHost },
383
- useProjectHostname: !1
384
- }).request({ uri: "/auth/logout", method: "POST" }));
385
- } finally {
386
- state.set("logoutSuccess", {
387
- authState: { type: AuthStateType.LOGGED_OUT, isDestroyingSession: !1 }
388
- }), storageArea?.removeItem(storageKey);
389
- }
840
+ }
841
+ function discardDocument(doc) {
842
+ return {
843
+ type: "document.discard",
844
+ ...doc,
845
+ documentId: getPublishedId(doc.documentId)
390
846
  };
391
- });
392
- function getResourceId(documentResourceId) {
393
- if (documentResourceId)
394
- return documentResourceId.split(":")[1];
395
847
  }
848
+ const DOCUMENT_STATE_CLEAR_DELAY = 1e3, INITIAL_OUTGOING_THROTTLE_TIME = 1e3, API_VERSION$4 = "vX";
396
849
  function parseBracketContent(content) {
397
850
  const rangeMatch = content.match(/^(\d*):(\d*)$/);
398
851
  if (rangeMatch) {
@@ -635,447 +1088,77 @@ function ifRevisionID(input, revisionId) {
635
1088
  );
636
1089
  return input;
637
1090
  }
638
- function isNonNullable(t2) {
639
- return t2 != null;
640
- }
641
- const indexCache = /* @__PURE__ */ new WeakMap();
642
- function getIndexForKey(input, key) {
643
- if (!Array.isArray(input)) return;
644
- const cached = indexCache.get(input);
645
- if (cached) return cached[key];
646
- const lookup = input.reduce((acc, next, index) => (typeof next?._key == "string" && (acc[next._key] = index), acc), {});
647
- return indexCache.set(input, lookup), lookup[key];
648
- }
649
- function getDeep(input, path) {
650
- const [currentSegment, ...restOfPath] = path;
651
- if (currentSegment === void 0) return input;
652
- if (typeof input != "object" || input === null) return;
653
- let key;
654
- if (isKeySegment(currentSegment) ? key = getIndexForKey(input, currentSegment._key) : (typeof currentSegment == "string" || typeof currentSegment == "number") && (key = currentSegment), key === void 0) return;
655
- const nestedInput = typeof key == "number" && Array.isArray(input) ? input.at(key) : input[key];
656
- return getDeep(nestedInput, restOfPath);
657
- }
658
- function setDeep(input, path, value) {
659
- const [currentSegment, ...restOfPath] = path;
660
- if (currentSegment === void 0) return value;
661
- if (typeof input != "object" || input === null) {
662
- if (typeof currentSegment == "string")
663
- return { [currentSegment]: setDeep(null, restOfPath, value) };
664
- let index;
665
- if (isKeySegment(currentSegment))
666
- index = 0;
667
- else if (typeof currentSegment == "number" && currentSegment >= 0)
668
- index = currentSegment;
669
- else
670
- return input;
671
- return [
672
- // fill until index
673
- ...Array.from({ length: index }).fill(null),
674
- // then set deep here
675
- setDeep(null, restOfPath, value)
676
- ];
677
- }
678
- if (Array.isArray(input)) {
679
- let index;
680
- return isKeySegment(currentSegment) ? index = getIndexForKey(input, currentSegment._key) : typeof currentSegment == "number" && (index = currentSegment < 0 ? input.length + currentSegment : currentSegment), index === void 0 ? input : index in input ? input.map(
681
- (nestedInput, i2) => i2 === index ? setDeep(nestedInput, restOfPath, value) : nestedInput
682
- ) : [
683
- ...input,
684
- ...Array.from({ length: index - input.length }).fill(null),
685
- setDeep(null, restOfPath, value)
686
- ];
687
- }
688
- return typeof currentSegment == "object" ? input : currentSegment in input ? Object.fromEntries(
689
- Object.entries(input).map(
690
- ([key, nestedInput]) => key === currentSegment ? [key, setDeep(nestedInput, restOfPath, value)] : [key, nestedInput]
691
- )
692
- ) : { ...input, [currentSegment]: setDeep(null, restOfPath, value) };
693
- }
694
- function unsetDeep(input, path) {
695
- const [currentSegment, ...restOfPath] = path;
696
- if (currentSegment === void 0 || typeof input != "object" || input === null) return input;
697
- let _segment;
698
- if (isKeySegment(currentSegment) ? _segment = getIndexForKey(input, currentSegment._key) : (typeof currentSegment == "string" || typeof currentSegment == "number") && (_segment = currentSegment), _segment === void 0) return input;
699
- let segment = _segment;
700
- return typeof segment == "number" && Array.isArray(input) && (segment = segment < 0 ? input.length + segment : segment), segment in input ? restOfPath.length ? Array.isArray(input) ? input.map(
701
- (nestedInput, index) => index === segment ? unsetDeep(nestedInput, restOfPath) : nestedInput
702
- ) : Object.fromEntries(
703
- Object.entries(input).map(
704
- ([key, value]) => key === segment ? [key, unsetDeep(value, restOfPath)] : [key, value]
705
- )
706
- ) : Array.isArray(input) ? input.filter((_nestedInput, index) => index !== segment) : Object.fromEntries(Object.entries(input).filter(([key]) => key !== segment.toString())) : input;
707
- }
708
- const DEFAULT_API_VERSION = "2024-11-12", DEFAULT_REQUEST_TAG_PREFIX = "sanity.sdk", clientStore = {
709
- name: "clientStore",
710
- getInitialState: (instance) => {
711
- const { identity, config } = instance, defaultClient = createClient({
712
- projectId: identity.projectId,
713
- dataset: identity.dataset,
714
- token: config?.auth?.token,
715
- useCdn: !1,
716
- apiVersion: DEFAULT_API_VERSION,
717
- requestTagPrefix: DEFAULT_REQUEST_TAG_PREFIX,
718
- ...config?.auth?.apiHost ? { apiHost: config.auth.apiHost } : {}
719
- }), defaultGlobalClient = createClient({
720
- token: config?.auth?.token,
721
- useCdn: !1,
722
- apiVersion: "vX",
723
- // Many global APIs are only available under this version, we may need to support other versions in the future
724
- useProjectHostname: !1,
725
- requestTagPrefix: DEFAULT_REQUEST_TAG_PREFIX,
726
- ...config?.auth?.apiHost ? { apiHost: config.auth.apiHost } : {}
727
- });
728
- return {
729
- defaultClient,
730
- defaultGlobalClient
731
- };
732
- },
733
- initialize() {
734
- const authEventSubscription = subscribeToAuthEvents(this);
735
- return () => {
736
- authEventSubscription.unsubscribe();
737
- };
738
- }
739
- }, receiveToken = (prev, token) => {
740
- const newDefaultClient = prev.defaultClient.withConfig({
741
- token
742
- }), newGlobalClient = prev.defaultGlobalClient.withConfig({
743
- token
744
- });
745
- return {
746
- defaultClient: newDefaultClient,
747
- defaultGlobalClient: newGlobalClient
748
- };
749
- }, subscribeToAuthEvents = createInternalAction(
750
- ({ instance, state }) => () => getTokenState(instance).observable.subscribe((newToken) => {
751
- state.set("receiveToken", (prev) => receiveToken(prev, newToken ?? void 0));
752
- })
753
- ), optionsCache = /* @__PURE__ */ new WeakMap(), defaultClientSelector = (state, options) => options?.scope === "global" ? state.defaultGlobalClient : state.defaultClient, memoizedOptionsSelector = createSelector(
754
- [defaultClientSelector, (_state, options) => options],
755
- (client, options) => {
756
- let nestedCache = optionsCache.get(client);
757
- nestedCache || (nestedCache = /* @__PURE__ */ new Map(), optionsCache.set(client, nestedCache));
758
- const key = JSON.stringify(options);
759
- return nestedCache.get(key) || (nestedCache.set(key, options), options);
760
- }
761
- ), clientSelector = createSelector(
762
- [defaultClientSelector, memoizedOptionsSelector],
763
- (client, options) => client.withConfig(options)
764
- ), getClient = createAction(
765
- clientStore,
766
- ({ state }) => (options) => clientSelector(state.get(), options)
767
- ), getClientState = createStateSourceAction(clientStore, clientSelector), comlinkControllerStore = {
768
- name: "connectionStore",
769
- getInitialState: () => ({
770
- controller: null,
771
- controllerOrigin: null,
772
- channels: /* @__PURE__ */ new Map()
773
- }),
774
- initialize() {
775
- return () => {
776
- destroyController(this);
777
- };
778
- }
779
- }, destroyController = createInternalAction(
780
- ({ state }) => () => {
781
- const { controller } = state.get();
782
- controller && (controller.destroy(), state.set("destroyController", {
783
- controller: null,
784
- channels: /* @__PURE__ */ new Map()
785
- }));
786
- }
787
- ), getOrCreateChannel = createAction(comlinkControllerStore, ({ state }) => (options) => {
788
- const controller = state.get().controller;
789
- if (!controller)
790
- throw new Error("Controller must be initialized before using or creating channels");
791
- const channels = state.get().channels, existing = channels.get(options.name);
792
- if (existing) {
793
- if (!isEqual(existing.options, options))
794
- throw new Error(`Channel "${options.name}" already exists with different options`);
795
- return state.set("incrementChannelRefCount", {
796
- channels: new Map(channels).set(options.name, {
797
- ...existing,
798
- refCount: existing.refCount + 1
799
- })
800
- }), existing.channel.start(), existing.channel;
801
- }
802
- const channel = controller.createChannel(options);
803
- return channel.start(), state.set("createChannel", {
804
- channels: new Map(channels).set(options.name, {
805
- channel,
806
- options,
807
- refCount: 1
808
- })
809
- }), channel;
810
- }), getOrCreateController = createAction(comlinkControllerStore, ({ state, instance }) => (targetOrigin) => {
811
- const { controller, controllerOrigin } = state.get();
812
- if (controller && controllerOrigin === targetOrigin)
813
- return controller;
814
- controller && destroyController({ state, instance });
815
- const newController = createController({ targetOrigin });
816
- return state.set("initializeController", {
817
- controllerOrigin: targetOrigin,
818
- controller: newController
819
- }), newController;
820
- }), releaseChannel = createAction(comlinkControllerStore, ({ state }) => (name) => {
821
- const channels = state.get().channels, channelEntry = channels.get(name);
822
- if (channelEntry) {
823
- const newRefCount = channelEntry.refCount === 0 ? 0 : channelEntry.refCount - 1;
824
- newRefCount === 0 ? (channelEntry.channel.stop(), channels.delete(name), state.set("releaseChannel", { channels: new Map(channels) })) : state.set("releaseChannel", {
825
- channels: new Map(channels).set(name, {
826
- ...channelEntry,
827
- refCount: newRefCount
828
- })
829
- });
830
- }
831
- }), comlinkNodeStore = {
832
- name: "nodeStore",
833
- getInitialState: () => ({
834
- nodes: /* @__PURE__ */ new Map()
835
- }),
836
- initialize() {
837
- return () => {
838
- this.state.get().nodes.forEach(({ node }) => {
839
- node.stop();
840
- });
841
- };
842
- }
843
- }, getOrCreateNode = createAction(comlinkNodeStore, ({ state }) => (options) => {
844
- const nodes = state.get().nodes, existing = nodes.get(options.name);
845
- if (existing) {
846
- if (!isEqual(existing.options, options))
847
- throw new Error(`Node "${options.name}" already exists with different options`);
848
- return state.set("incrementNodeRefCount", {
849
- nodes: new Map(nodes).set(options.name, {
850
- ...existing,
851
- refCount: existing.refCount + 1
852
- })
853
- }), existing.node.start(), existing.node;
854
- }
855
- const node = createNode(options);
856
- return node.start(), nodes.set(options.name, { node, options, refCount: 1 }), state.set("createNode", { nodes }), node;
857
- }), releaseNode = createAction(comlinkNodeStore, ({ state }) => (name) => {
858
- const nodes = state.get().nodes, nodeEntry = nodes.get(name);
859
- if (nodeEntry) {
860
- const newRefCount = nodeEntry.refCount === 0 ? 0 : nodeEntry.refCount - 1;
861
- newRefCount === 0 ? (nodeEntry.node.stop(), nodes.delete(name), state.set("releaseNode", { nodes: new Map(nodes) })) : state.set("releaseNode", {
862
- nodes: new Map(nodes).set(name, {
863
- ...nodeEntry,
864
- refCount: newRefCount
865
- })
866
- });
867
- }
868
- });
869
- function getPublishedId(id) {
870
- const draftsPrefix = "drafts.";
871
- return id.startsWith(draftsPrefix) ? id.slice(draftsPrefix.length) : id;
872
- }
873
- function getDraftId(id) {
874
- const draftsPrefix = "drafts.";
875
- return id.startsWith(draftsPrefix) ? id : `${draftsPrefix}${id}`;
876
- }
877
- function insecureRandomId() {
878
- return Array.from({ length: 16 }, () => Math.floor(Math.random() * 16).toString(16)).join("");
879
- }
880
- function createFetcherStore({
881
- name,
882
- fetcher: getObservable,
883
- getKey,
884
- fetchThrottleInternal = 1e3,
885
- stateExpirationDelay = 5e3
886
- }) {
887
- const store = {
888
- name,
889
- getInitialState: () => ({
890
- stateByParams: {}
891
- }),
892
- initialize() {
893
- const subscription = subscribeToSubscriptionsAndFetch(this);
894
- return () => subscription.unsubscribe();
895
- }
896
- }, subscribeToSubscriptionsAndFetch = createInternalAction(
897
- ({ instance, state }) => function() {
898
- const factoryFn = getObservable(instance);
899
- return state.observable.pipe(
900
- // Map the state to an array of [serialized, entry] pairs.
901
- switchMap$1((s2) => {
902
- const entries = Object.entries(s2.stateByParams);
903
- return entries.length > 0 ? from(entries) : EMPTY;
904
- }),
905
- // Group by the serialized key.
906
- groupBy(([key]) => key),
907
- mergeMap(
908
- (group$) => group$.pipe(
909
- // Emit an initial value for pairwise comparisons.
910
- startWith([
911
- group$.key,
912
- void 0
913
- ]),
914
- pairwise(),
915
- // Trigger only when the subscriptions array grows.
916
- filter$1(([[, prevEntry], [, currEntry]]) => {
917
- const prevSubs = prevEntry?.subscriptions ?? [];
918
- return (currEntry?.subscriptions ?? []).length > prevSubs.length;
919
- }),
920
- map$1(([, [, currEntry]]) => currEntry),
921
- // Only trigger if we haven't fetched recently.
922
- filter$1((entry) => {
923
- const lastFetch = entry?.lastFetchInitiatedAt;
924
- return lastFetch ? Date.now() - new Date(lastFetch).getTime() >= fetchThrottleInternal : !0;
925
- }),
926
- switchMap$1((entry) => entry ? (state.set(
927
- "setLastFetchInitiatedAt",
928
- (prev) => ({
929
- stateByParams: {
930
- ...prev.stateByParams,
931
- [entry.key]: {
932
- ...entry,
933
- ...prev.stateByParams[entry.key],
934
- lastFetchInitiatedAt: (/* @__PURE__ */ new Date()).toISOString()
935
- }
936
- }
937
- })
938
- ), factoryFn(...entry.params).pipe(
939
- // the `createStateSourceAction` util requires the update
940
- // to
941
- delay(0, asapScheduler),
942
- tap(
943
- (data) => state.set("setData", (prev) => ({
944
- stateByParams: {
945
- ...prev.stateByParams,
946
- [entry.key]: {
947
- ...omit(entry, "error"),
948
- ...omit(prev.stateByParams[entry.key], "error"),
949
- data
950
- }
951
- }
952
- }))
953
- ),
954
- catchError((error) => (state.set("setError", (prev) => ({
955
- stateByParams: {
956
- ...prev.stateByParams,
957
- [entry.key]: {
958
- ...entry,
959
- ...prev.stateByParams[entry.key],
960
- error
961
- }
962
- }
963
- })), EMPTY))
964
- )) : EMPTY)
965
- )
966
- )
967
- ).subscribe({
968
- error: (error) => state.set("setError", { error })
969
- });
970
- }
971
- ), getState2 = createStateSourceAction(store, {
972
- selector: ({ stateByParams, error }, ...params) => {
973
- if (error) throw error;
974
- const key = getKey(...params), entry = stateByParams[key];
975
- if (entry?.error) throw entry.error;
976
- return entry?.data;
977
- },
978
- onSubscribe: ({ state }, ...params) => {
979
- const subscriptionId = insecureRandomId(), key = getKey(...params);
980
- return state.set("addSubscription", (prev) => ({
981
- stateByParams: {
982
- ...prev.stateByParams,
983
- [key]: {
984
- ...prev.stateByParams[key],
985
- key,
986
- params: prev.stateByParams[key]?.params || params,
987
- subscriptions: [...prev.stateByParams[key]?.subscriptions || [], subscriptionId]
988
- }
989
- }
990
- })), () => {
991
- setTimeout(() => {
992
- state.set("removeSubscription", (prev) => {
993
- const entry = prev.stateByParams[key];
994
- if (!entry) return prev;
995
- const newSubs = (entry.subscriptions || []).filter((id) => id !== subscriptionId);
996
- return newSubs.length === 0 ? { stateByParams: omit(prev.stateByParams, key) } : {
997
- stateByParams: {
998
- ...prev.stateByParams,
999
- [key]: {
1000
- ...entry,
1001
- subscriptions: newSubs
1002
- }
1003
- }
1004
- };
1005
- });
1006
- }, stateExpirationDelay);
1007
- };
1008
- }
1009
- }), resolveState = createAction(store, () => function(...params) {
1010
- return firstValueFrom(
1011
- getState2(this, ...params).observable.pipe(first((i2) => i2 !== void 0))
1012
- );
1013
- });
1014
- return { getState: getState2, resolveState };
1015
- }
1016
- const datasets = createFetcherStore({
1017
- name: "Datasets",
1018
- getKey: () => "datasets",
1019
- fetcher: (instance) => () => getClientState(instance, { apiVersion: "vX", scope: "project" }).observable.pipe(
1020
- switchMap((client) => client.observable.datasets.list())
1021
- )
1022
- }), getDatasetsState = datasets.getState, resolveDatasets = datasets.resolveState, isSanityMutatePatch = (value) => !(typeof value != "object" || !value || !("type" in value) || typeof value.type != "string" || value.type !== "patch" || !("id" in value) || typeof value.id != "string" || !("patches" in value) || !Array.isArray(value.patches));
1023
- function createDocument(doc) {
1024
- return {
1025
- type: "document.create",
1026
- ...doc._id && { documentId: doc._id },
1027
- documentType: doc._type,
1028
- ...doc.resourceId && { resourceId: doc.resourceId }
1029
- };
1030
- }
1031
- function deleteDocument(doc) {
1032
- return {
1033
- type: "document.delete",
1034
- documentId: getPublishedId(doc._id),
1035
- ...doc.resourceId && { resourceId: doc.resourceId }
1036
- };
1037
- }
1038
- function convertSanityMutatePatch(sanityPatchMutation) {
1039
- const encoded = SanityEncoder.encode(sanityPatchMutation);
1040
- return {
1041
- documentId: sanityPatchMutation.id,
1042
- type: "document.edit",
1043
- patches: encoded.map((i2) => {
1044
- const copy = { ...i2.patch };
1045
- return "id" in copy && delete copy.id, copy;
1046
- })
1047
- };
1091
+ function isNonNullable(t2) {
1092
+ return t2 != null;
1048
1093
  }
1049
- function editDocument(doc, patches) {
1050
- return isSanityMutatePatch(doc) ? convertSanityMutatePatch(doc) : {
1051
- type: "document.edit",
1052
- documentId: getPublishedId(doc._id),
1053
- ...patches && { patches: Array.isArray(patches) ? patches : [patches] },
1054
- ...doc.resourceId && { resourceId: doc.resourceId }
1055
- };
1094
+ const indexCache = /* @__PURE__ */ new WeakMap();
1095
+ function getIndexForKey(input, key) {
1096
+ if (!Array.isArray(input)) return;
1097
+ const cached = indexCache.get(input);
1098
+ if (cached) return cached[key];
1099
+ const lookup = input.reduce((acc, next, index) => (typeof next?._key == "string" && (acc[next._key] = index), acc), {});
1100
+ return indexCache.set(input, lookup), lookup[key];
1056
1101
  }
1057
- function publishDocument(doc) {
1058
- return {
1059
- type: "document.publish",
1060
- documentId: getPublishedId(doc._id),
1061
- ...doc.resourceId && { resourceId: doc.resourceId }
1062
- };
1102
+ function getDeep(input, path) {
1103
+ const [currentSegment, ...restOfPath] = path;
1104
+ if (currentSegment === void 0) return input;
1105
+ if (typeof input != "object" || input === null) return;
1106
+ let key;
1107
+ if (isKeySegment(currentSegment) ? key = getIndexForKey(input, currentSegment._key) : (typeof currentSegment == "string" || typeof currentSegment == "number") && (key = currentSegment), key === void 0) return;
1108
+ const nestedInput = typeof key == "number" && Array.isArray(input) ? input.at(key) : input[key];
1109
+ return getDeep(nestedInput, restOfPath);
1063
1110
  }
1064
- function unpublishDocument(doc) {
1065
- return {
1066
- type: "document.unpublish",
1067
- documentId: getPublishedId(doc._id),
1068
- ...doc.resourceId && { resourceId: doc.resourceId }
1069
- };
1111
+ function setDeep(input, path, value) {
1112
+ const [currentSegment, ...restOfPath] = path;
1113
+ if (currentSegment === void 0) return value;
1114
+ if (typeof input != "object" || input === null) {
1115
+ if (typeof currentSegment == "string")
1116
+ return { [currentSegment]: setDeep(null, restOfPath, value) };
1117
+ let index;
1118
+ if (isKeySegment(currentSegment))
1119
+ index = 0;
1120
+ else if (typeof currentSegment == "number" && currentSegment >= 0)
1121
+ index = currentSegment;
1122
+ else
1123
+ return input;
1124
+ return [
1125
+ // fill until index
1126
+ ...Array.from({ length: index }).fill(null),
1127
+ // then set deep here
1128
+ setDeep(null, restOfPath, value)
1129
+ ];
1130
+ }
1131
+ if (Array.isArray(input)) {
1132
+ let index;
1133
+ return isKeySegment(currentSegment) ? index = getIndexForKey(input, currentSegment._key) : typeof currentSegment == "number" && (index = currentSegment < 0 ? input.length + currentSegment : currentSegment), index === void 0 ? input : index in input ? input.map(
1134
+ (nestedInput, i2) => i2 === index ? setDeep(nestedInput, restOfPath, value) : nestedInput
1135
+ ) : [
1136
+ ...input,
1137
+ ...Array.from({ length: index - input.length }).fill(null),
1138
+ setDeep(null, restOfPath, value)
1139
+ ];
1140
+ }
1141
+ return typeof currentSegment == "object" ? input : currentSegment in input ? Object.fromEntries(
1142
+ Object.entries(input).map(
1143
+ ([key, nestedInput]) => key === currentSegment ? [key, setDeep(nestedInput, restOfPath, value)] : [key, nestedInput]
1144
+ )
1145
+ ) : { ...input, [currentSegment]: setDeep(null, restOfPath, value) };
1070
1146
  }
1071
- function discardDocument(doc) {
1072
- return {
1073
- type: "document.discard",
1074
- documentId: getPublishedId(doc._id),
1075
- ...doc.resourceId && { resourceId: doc.resourceId }
1076
- };
1147
+ function unsetDeep(input, path) {
1148
+ const [currentSegment, ...restOfPath] = path;
1149
+ if (currentSegment === void 0 || typeof input != "object" || input === null) return input;
1150
+ let _segment;
1151
+ if (isKeySegment(currentSegment) ? _segment = getIndexForKey(input, currentSegment._key) : (typeof currentSegment == "string" || typeof currentSegment == "number") && (_segment = currentSegment), _segment === void 0) return input;
1152
+ let segment = _segment;
1153
+ return typeof segment == "number" && Array.isArray(input) && (segment = segment < 0 ? input.length + segment : segment), segment in input ? restOfPath.length ? Array.isArray(input) ? input.map(
1154
+ (nestedInput, index) => index === segment ? unsetDeep(nestedInput, restOfPath) : nestedInput
1155
+ ) : Object.fromEntries(
1156
+ Object.entries(input).map(
1157
+ ([key, value]) => key === segment ? [key, unsetDeep(value, restOfPath)] : [key, value]
1158
+ )
1159
+ ) : Array.isArray(input) ? input.filter((_nestedInput, index) => index !== segment) : Object.fromEntries(Object.entries(input).filter(([key]) => key !== segment.toString())) : input;
1077
1160
  }
1078
- const DOCUMENT_STATE_CLEAR_DELAY = 1e3, INITIAL_OUTGOING_THROTTLE_TIME = 1e3, API_VERSION$2 = "vX", patchOperations = {
1161
+ const patchOperations = {
1079
1162
  ifRevisionID,
1080
1163
  set,
1081
1164
  setIfMissing,
@@ -1284,50 +1367,48 @@ function sortListenerEvents(options) {
1284
1367
  mergeMap((state) => of(...state.emitEvents))
1285
1368
  );
1286
1369
  }
1287
- const listen = createInternalAction(({ state }) => {
1370
+ const listen = ({ state }, documentId) => {
1288
1371
  const { sharedListener, fetchDocument } = state.get();
1289
- return function(documentId) {
1290
- return sharedListener.pipe(
1291
- concatMap((e3) => e3.type === "welcome" ? fetchDocument(documentId).pipe(
1292
- map((document) => ({ type: "sync", document }))
1293
- ) : e3.type === "mutation" && e3.documentId === documentId ? of(e3) : EMPTY),
1294
- sortListenerEvents(),
1295
- withLatestFrom(
1296
- state.observable.pipe(
1297
- map((s2) => s2.documentStates[documentId]),
1298
- filter(Boolean),
1299
- distinctUntilChanged()
1300
- )
1301
- ),
1302
- map(([next, documentState]) => {
1303
- if (next.type === "sync")
1304
- return {
1305
- type: "sync",
1306
- documentId,
1307
- document: next.document,
1308
- revision: next.document?._rev,
1309
- timestamp: next.document?._updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
1310
- };
1311
- const [document] = Object.values(
1312
- processMutations({
1313
- documents: { [documentId]: documentState.remote },
1314
- mutations: next.mutations,
1315
- transactionId: next.transactionId,
1316
- timestamp: next.timestamp
1317
- })
1318
- ), { previousRev, transactionId, timestamp } = next;
1372
+ return sharedListener.events.pipe(
1373
+ concatMap((e3) => e3.type === "welcome" ? fetchDocument(documentId).pipe(
1374
+ map((document) => ({ type: "sync", document }))
1375
+ ) : e3.type === "mutation" && e3.documentId === documentId ? of(e3) : EMPTY),
1376
+ sortListenerEvents(),
1377
+ withLatestFrom(
1378
+ state.observable.pipe(
1379
+ map((s2) => s2.documentStates[documentId]),
1380
+ filter(Boolean),
1381
+ distinctUntilChanged()
1382
+ )
1383
+ ),
1384
+ map(([next, documentState]) => {
1385
+ if (next.type === "sync")
1319
1386
  return {
1320
- type: "mutation",
1387
+ type: "sync",
1321
1388
  documentId,
1322
- document: document ?? null,
1323
- revision: transactionId,
1324
- timestamp,
1325
- ...previousRev && { previousRev }
1389
+ document: next.document,
1390
+ revision: next.document?._rev,
1391
+ timestamp: next.document?._updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
1326
1392
  };
1327
- })
1328
- );
1329
- };
1330
- });
1393
+ const [document] = Object.values(
1394
+ processMutations({
1395
+ documents: { [documentId]: documentState.remote },
1396
+ mutations: next.mutations,
1397
+ transactionId: next.transactionId,
1398
+ timestamp: next.timestamp
1399
+ })
1400
+ ), { previousRev, transactionId, timestamp } = next;
1401
+ return {
1402
+ type: "mutation",
1403
+ documentId,
1404
+ document: document ?? null,
1405
+ revision: transactionId,
1406
+ timestamp,
1407
+ ...previousRev && { previousRev }
1408
+ };
1409
+ })
1410
+ );
1411
+ };
1331
1412
  class e {
1332
1413
  pattern;
1333
1414
  patternRe;
@@ -3189,8 +3270,8 @@ function createGrantsLookup(datasetAcl) {
3189
3270
  }
3190
3271
  const documentsCache = new MultiKeyWeakMap(), actionsCache = /* @__PURE__ */ new WeakMap(), nullReplacer = {}, documentsSelector = createSelector(
3191
3272
  [
3192
- ({ documentStates }) => documentStates,
3193
- (_state, actions) => actions
3273
+ ({ state: { documentStates } }) => documentStates,
3274
+ (_context, actions) => actions
3194
3275
  ],
3195
3276
  (documentStates, actions) => {
3196
3277
  const documentIds = new Set(
@@ -3225,7 +3306,11 @@ function checkGrant$1(grantExpr, document) {
3225
3306
  return C(grantExpr, { params: { document } }).get();
3226
3307
  }
3227
3308
  const enNarrowConjunction = new Intl.ListFormat("en", { style: "narrow", type: "conjunction" }), calculatePermissions = createSelector(
3228
- [({ grants }) => grants, documentsSelector, memoizedActionsSelector],
3309
+ [
3310
+ ({ state: { grants } }) => grants,
3311
+ documentsSelector,
3312
+ memoizedActionsSelector
3313
+ ],
3229
3314
  (grants, documents, actions) => {
3230
3315
  if (!documents || !grants || !actions) return;
3231
3316
  const timestamp = (/* @__PURE__ */ new Date()).toISOString(), reasons = [];
@@ -3795,7 +3880,7 @@ function removeSubscriptionIdFromDocument(prev, documentId, subscriptionId) {
3795
3880
  }
3796
3881
  } : { ...prev, documentStates: omit(prev.documentStates, documentId) } : prev;
3797
3882
  }
3798
- function manageSubscriberIds(state, documentId) {
3883
+ function manageSubscriberIds({ state }, documentId) {
3799
3884
  const documentIds = Array.from(
3800
3885
  new Set(
3801
3886
  (Array.isArray(documentId) ? documentId : [documentId]).flatMap((id) => [
@@ -3853,9 +3938,11 @@ function getDocumentEvents(outgoing) {
3853
3938
  )
3854
3939
  );
3855
3940
  }
3856
- const API_VERSION$1 = "vX";
3941
+ const API_VERSION$3 = "vX";
3857
3942
  function createSharedListener(instance) {
3858
- const events$ = getClientState(instance, { apiVersion: API_VERSION$1 }).observable.pipe(
3943
+ const dispose$ = new Subject(), events$ = getClientState(instance, {
3944
+ apiVersion: API_VERSION$3
3945
+ }).observable.pipe(
3859
3946
  switchMap(
3860
3947
  (client) => (
3861
3948
  // TODO: it seems like the client.listen method is not emitting disconnected
@@ -3876,17 +3963,21 @@ function createSharedListener(instance) {
3876
3963
  )
3877
3964
  )
3878
3965
  ),
3966
+ takeUntil(dispose$),
3879
3967
  share()
3880
3968
  ), [welcome$, mutation$] = partition(events$, (e3) => e3.type === "welcome");
3881
- return merge(
3882
- // we replay the welcome event because that event kicks off fetching the document
3883
- welcome$.pipe(shareReplay(1)),
3884
- mutation$
3885
- );
3969
+ return {
3970
+ events: merge(
3971
+ // we replay the welcome event because that event kicks off fetching the document
3972
+ welcome$.pipe(shareReplay(1)),
3973
+ mutation$
3974
+ ),
3975
+ dispose: () => dispose$.next()
3976
+ };
3886
3977
  }
3887
3978
  function createFetchDocument(instance) {
3888
3979
  return function(documentId) {
3889
- return getClientState(instance, { apiVersion: API_VERSION$1 }).observable.pipe(
3980
+ return getClientState(instance, { apiVersion: API_VERSION$3 }).observable.pipe(
3890
3981
  switchMap((client) => createDocumentLoaderFromClient(client)(documentId)),
3891
3982
  map((result) => {
3892
3983
  if (!result.accessible) {
@@ -3910,330 +4001,458 @@ const documentStore = {
3910
4001
  fetchDocument: createFetchDocument(instance),
3911
4002
  events: new Subject()
3912
4003
  }),
3913
- initialize() {
3914
- const queuedTransactionSubscription = subscribeToQueuedAndApplyNextTransaction(this), subscriptionsSubscription = subscribeToSubscriptionsAndListenToDocuments(this), appliedSubscription = subscribeToAppliedAndSubmitNextTransaction(this), clientSubscription = subscribeToClientAndFetchDatasetAcl(this);
4004
+ initialize(context) {
4005
+ const { sharedListener } = context.state.get(), subscriptions = [
4006
+ subscribeToQueuedAndApplyNextTransaction(context),
4007
+ subscribeToSubscriptionsAndListenToDocuments(context),
4008
+ subscribeToAppliedAndSubmitNextTransaction(context),
4009
+ subscribeToClientAndFetchDatasetAcl(context)
4010
+ ];
3915
4011
  return () => {
3916
- queuedTransactionSubscription.unsubscribe(), subscriptionsSubscription.unsubscribe(), appliedSubscription.unsubscribe(), clientSubscription.unsubscribe();
4012
+ sharedListener.dispose(), subscriptions.forEach((subscription) => subscription.unsubscribe());
3917
4013
  };
3918
4014
  }
3919
4015
  };
3920
4016
  function getDocumentState(...args) {
3921
4017
  return _getDocumentState(...args);
3922
4018
  }
3923
- const _getDocumentState = createStateSourceAction(documentStore, {
3924
- selector: ({ error, documentStates }, doc, path) => {
3925
- const documentId = typeof doc == "string" ? doc : doc._id;
3926
- if (error) throw error;
3927
- const draftId = getDraftId(documentId), publishedId = getPublishedId$1(documentId), draft = documentStates[draftId]?.local, published = documentStates[publishedId]?.local, document = draft ?? published;
3928
- if (document !== void 0)
3929
- return path ? jsonMatch(document, path).at(0)?.value : document;
3930
- },
3931
- onSubscribe: ({ state }, doc) => manageSubscriberIds(state, typeof doc == "string" ? doc : doc._id)
3932
- });
4019
+ const _getDocumentState = bindActionByDataset(
4020
+ documentStore,
4021
+ createStateSourceAction({
4022
+ selector: ({ state: { error, documentStates } }, doc, path) => {
4023
+ const documentId = typeof doc == "string" ? doc : doc.documentId;
4024
+ if (error) throw error;
4025
+ const draftId = getDraftId(documentId), publishedId = getPublishedId$1(documentId), draft = documentStates[draftId]?.local, published = documentStates[publishedId]?.local, document = draft ?? published;
4026
+ if (document !== void 0)
4027
+ return path ? jsonMatch(document, path).at(0)?.value : document;
4028
+ },
4029
+ onSubscribe: (context, doc) => manageSubscriberIds(context, typeof doc == "string" ? doc : doc.documentId)
4030
+ })
4031
+ );
3933
4032
  function resolveDocument(...args) {
3934
4033
  return _resolveDocument(...args);
3935
4034
  }
3936
- const _resolveDocument = createAction(documentStore, () => function(doc) {
3937
- const documentId = typeof doc == "string" ? doc : doc._id;
3938
- return firstValueFrom(
3939
- getDocumentState(this, documentId).observable.pipe(filter((i2) => i2 !== void 0))
3940
- );
3941
- }), getDocumentSyncStatus = createStateSourceAction(documentStore, {
3942
- selector: ({ error, documentStates: documents, outgoing, applied, queued }, doc) => {
3943
- const documentId = doc._id;
3944
- if (error) throw error;
3945
- const draftId = getDraftId(documentId), publishedId = getPublishedId$1(documentId), draft = documents[draftId], published = documents[publishedId];
3946
- if (!(draft === void 0 || published === void 0))
3947
- return !queued.length && !applied.length && !outgoing;
3948
- },
3949
- onSubscribe: ({ state }, doc) => manageSubscriberIds(state, doc._id)
3950
- }), getPermissionsState = createStateSourceAction(documentStore, {
3951
- selector: calculatePermissions,
3952
- onSubscribe: ({ state }, actions) => manageSubscriberIds(state, getDocumentIdsFromActions(actions))
3953
- }), resolvePermissions = createAction(documentStore, () => function(actions) {
3954
- return firstValueFrom(
3955
- getPermissionsState(this, actions).observable.pipe(filter((i2) => i2 !== void 0))
3956
- );
3957
- }), subscribeDocumentEvents = createAction(documentStore, ({ state }) => {
3958
- const { events } = state.get();
3959
- return function(eventHandler) {
3960
- const subscription = events.subscribe(eventHandler);
3961
- return () => subscription.unsubscribe();
3962
- };
3963
- }), subscribeToQueuedAndApplyNextTransaction = createInternalAction(
3964
- ({ state }) => {
3965
- const { events } = state.get();
3966
- return function() {
3967
- return state.observable.pipe(
3968
- map(applyFirstQueuedTransaction),
3969
- distinctUntilChanged(),
3970
- tap$1((next) => state.set("applyFirstQueuedTransaction", next)),
3971
- catchError$1((error, caught) => {
3972
- if (error instanceof ActionError)
3973
- return state.set(
3974
- "removeQueuedTransaction",
3975
- (prev) => removeQueuedTransaction(prev, error.transactionId)
3976
- ), events.next({
3977
- type: "error",
3978
- message: error.message,
3979
- documentId: error.documentId,
3980
- transactionId: error.transactionId,
3981
- error
3982
- }), caught;
3983
- throw error;
3984
- })
3985
- ).subscribe({ error: (error) => state.set("setError", { error }) });
3986
- };
3987
- }
3988
- ), subscribeToAppliedAndSubmitNextTransaction = createInternalAction(
3989
- ({ state, instance }) => {
3990
- const { events } = state.get();
3991
- return function() {
3992
- return state.observable.pipe(
3993
- throttle(
3994
- (s2) => (
3995
- // if there is no outgoing transaction, we can throttle by the
3996
- // initial outgoing throttle time…
3997
- s2.outgoing ? (
3998
- // …otherwise, wait until the outgoing has been cleared
3999
- state.observable.pipe(first$1(({ outgoing }) => !outgoing))
4000
- ) : timer(INITIAL_OUTGOING_THROTTLE_TIME)
4001
- ),
4002
- { leading: !1, trailing: !0 }
4003
- ),
4004
- map(transitionAppliedTransactionsToOutgoing),
4005
- distinctUntilChanged((a2, b2) => a2.outgoing?.transactionId === b2.outgoing?.transactionId),
4006
- tap$1((next) => state.set("transitionAppliedTransactionsToOutgoing", next)),
4007
- map((s2) => s2.outgoing),
4008
- distinctUntilChanged(),
4009
- withLatestFrom(getClientState(instance, { apiVersion: API_VERSION$2 }).observable),
4010
- concatMap(([outgoing, client]) => outgoing ? client.observable.action(outgoing.outgoingActions, {
4011
- transactionId: outgoing.transactionId,
4012
- skipCrossDatasetReferenceValidation: !0
4013
- }).pipe(
4014
- catchError$1((error) => (state.set("revertOutgoingTransaction", revertOutgoingTransaction), events.next({ type: "reverted", message: error.message, outgoing, error }), EMPTY)),
4015
- map((result) => ({ result, outgoing }))
4016
- ) : EMPTY),
4017
- tap$1(({ outgoing, result }) => {
4018
- state.set("cleanupOutgoingTransaction", cleanupOutgoingTransaction);
4019
- for (const e3 of getDocumentEvents(outgoing)) events.next(e3);
4020
- events.next({ type: "accepted", outgoing, result });
4021
- })
4022
- ).subscribe({ error: (error) => state.set("setError", { error }) });
4023
- };
4035
+ const _resolveDocument = bindActionByDataset(
4036
+ documentStore,
4037
+ ({ instance }, doc) => {
4038
+ const documentId = typeof doc == "string" ? doc : doc.documentId;
4039
+ return firstValueFrom(
4040
+ getDocumentState(instance, documentId).observable.pipe(filter((i2) => i2 !== void 0))
4041
+ );
4024
4042
  }
4025
- ), subscribeToSubscriptionsAndListenToDocuments = createInternalAction(
4026
- ({ state }) => {
4027
- const { events } = state.get();
4028
- return function() {
4029
- return state.observable.pipe(
4030
- filter((s2) => !!s2.grants),
4031
- map((s2) => Object.keys(s2.documentStates)),
4032
- distinctUntilChanged((curr, next) => {
4033
- if (curr.length !== next.length) return !1;
4034
- const currSet = new Set(curr);
4035
- return next.every((i2) => currSet.has(i2));
4036
- }),
4037
- startWith$1(/* @__PURE__ */ new Set()),
4038
- pairwise$1(),
4039
- switchMap((pair) => {
4040
- const [curr, next] = pair.map((ids) => new Set(ids)), added = Array.from(next).filter((i2) => !curr.has(i2)), removed = Array.from(curr).filter((i2) => !next.has(i2)), changes = [
4041
- ...added.map((id) => ({ id, add: !0 })),
4042
- ...removed.map((id) => ({ id, add: !1 }))
4043
- ].sort((a2, b2) => {
4044
- const aIsDraft = a2.id === getDraftId(a2.id), bIsDraft = b2.id === getDraftId(b2.id);
4045
- return aIsDraft && bIsDraft ? a2.id.localeCompare(b2.id, "en-US") : aIsDraft ? -1 : bIsDraft ? 1 : a2.id.localeCompare(b2.id, "en-US");
4046
- });
4047
- return of(...changes);
4048
- }),
4049
- groupBy$1((i2) => i2.id),
4050
- mergeMap$1(
4051
- (group) => group.pipe(
4052
- switchMap((e3) => e3.add ? listen(this, e3.id).pipe(
4053
- catchError$1((error) => {
4054
- throw error instanceof OutOfSyncError && listen(this, e3.id), error;
4055
- }),
4056
- tap$1(
4057
- (remote) => state.set(
4058
- "applyRemoteDocument",
4059
- (prev) => applyRemoteDocument(prev, remote, events)
4060
- )
4061
- )
4062
- ) : EMPTY)
4063
- )
4064
- )
4065
- ).subscribe({ error: (error) => state.set("setError", { error }) });
4066
- };
4043
+ ), getDocumentSyncStatus = bindActionByDataset(
4044
+ documentStore,
4045
+ createStateSourceAction({
4046
+ selector: ({ state: { error, documentStates: documents, outgoing, applied, queued } }, doc) => {
4047
+ const documentId = typeof doc == "string" ? doc : doc.documentId;
4048
+ if (error) throw error;
4049
+ const draftId = getDraftId(documentId), publishedId = getPublishedId$1(documentId), draft = documents[draftId], published = documents[publishedId];
4050
+ if (!(draft === void 0 || published === void 0))
4051
+ return !queued.length && !applied.length && !outgoing;
4052
+ },
4053
+ onSubscribe: (context, doc) => manageSubscriberIds(context, doc.documentId)
4054
+ })
4055
+ ), getPermissionsState = bindActionByDataset(
4056
+ documentStore,
4057
+ createStateSourceAction({
4058
+ selector: calculatePermissions,
4059
+ onSubscribe: (context, actions) => manageSubscriberIds(context, getDocumentIdsFromActions(actions))
4060
+ })
4061
+ ), resolvePermissions = bindActionByDataset(
4062
+ documentStore,
4063
+ ({ instance }, actions) => firstValueFrom(
4064
+ getPermissionsState(instance, actions).observable.pipe(filter((i2) => i2 !== void 0))
4065
+ )
4066
+ ), subscribeDocumentEvents = bindActionByDataset(
4067
+ documentStore,
4068
+ ({ state }, eventHandler) => {
4069
+ const { events } = state.get(), subscription = events.subscribe(eventHandler);
4070
+ return () => subscription.unsubscribe();
4067
4071
  }
4068
- ), subscribeToClientAndFetchDatasetAcl = createInternalAction(
4069
- ({ instance, state }) => {
4070
- const { projectId, dataset } = instance.identity;
4071
- return function() {
4072
- return getClientState(instance, { apiVersion: API_VERSION$2 }).observable.pipe(
4073
- switchMap(
4074
- (client) => client.observable.request({
4075
- uri: `/projects/${projectId}/datasets/${dataset}/acl`,
4076
- tag: "acl.get",
4077
- withCredentials: !0
4078
- })
4079
- ),
4080
- tap$1((datasetAcl) => state.set("setGrants", { grants: createGrantsLookup(datasetAcl) }))
4081
- ).subscribe({
4082
- error: (error) => state.set("setError", { error })
4072
+ ), subscribeToQueuedAndApplyNextTransaction = ({ state }) => {
4073
+ const { events } = state.get();
4074
+ return state.observable.pipe(
4075
+ map(applyFirstQueuedTransaction),
4076
+ distinctUntilChanged(),
4077
+ tap$1((next) => state.set("applyFirstQueuedTransaction", next)),
4078
+ catchError$1((error, caught) => {
4079
+ if (error instanceof ActionError)
4080
+ return state.set(
4081
+ "removeQueuedTransaction",
4082
+ (prev) => removeQueuedTransaction(prev, error.transactionId)
4083
+ ), events.next({
4084
+ type: "error",
4085
+ message: error.message,
4086
+ documentId: error.documentId,
4087
+ transactionId: error.transactionId,
4088
+ error
4089
+ }), caught;
4090
+ throw error;
4091
+ })
4092
+ ).subscribe({ error: (error) => state.set("setError", { error }) });
4093
+ }, subscribeToAppliedAndSubmitNextTransaction = ({
4094
+ state,
4095
+ instance
4096
+ }) => {
4097
+ const { events } = state.get();
4098
+ return state.observable.pipe(
4099
+ throttle(
4100
+ (s2) => (
4101
+ // if there is no outgoing transaction, we can throttle by the
4102
+ // initial outgoing throttle time…
4103
+ s2.outgoing ? (
4104
+ // …otherwise, wait until the outgoing has been cleared
4105
+ state.observable.pipe(first$1(({ outgoing }) => !outgoing))
4106
+ ) : timer(INITIAL_OUTGOING_THROTTLE_TIME)
4107
+ ),
4108
+ { leading: !1, trailing: !0 }
4109
+ ),
4110
+ map(transitionAppliedTransactionsToOutgoing),
4111
+ distinctUntilChanged((a2, b2) => a2.outgoing?.transactionId === b2.outgoing?.transactionId),
4112
+ tap$1((next) => state.set("transitionAppliedTransactionsToOutgoing", next)),
4113
+ map((s2) => s2.outgoing),
4114
+ distinctUntilChanged(),
4115
+ withLatestFrom(getClientState(instance, { apiVersion: API_VERSION$4 }).observable),
4116
+ concatMap(([outgoing, client]) => outgoing ? client.observable.action(outgoing.outgoingActions, {
4117
+ transactionId: outgoing.transactionId,
4118
+ skipCrossDatasetReferenceValidation: !0
4119
+ }).pipe(
4120
+ catchError$1((error) => (state.set("revertOutgoingTransaction", revertOutgoingTransaction), events.next({ type: "reverted", message: error.message, outgoing, error }), EMPTY)),
4121
+ map((result) => ({ result, outgoing }))
4122
+ ) : EMPTY),
4123
+ tap$1(({ outgoing, result }) => {
4124
+ state.set("cleanupOutgoingTransaction", cleanupOutgoingTransaction);
4125
+ for (const e3 of getDocumentEvents(outgoing)) events.next(e3);
4126
+ events.next({ type: "accepted", outgoing, result });
4127
+ })
4128
+ ).subscribe({ error: (error) => state.set("setError", { error }) });
4129
+ }, subscribeToSubscriptionsAndListenToDocuments = (context) => {
4130
+ const { state } = context, { events } = state.get();
4131
+ return state.observable.pipe(
4132
+ filter((s2) => !!s2.grants),
4133
+ map((s2) => Object.keys(s2.documentStates)),
4134
+ distinctUntilChanged((curr, next) => {
4135
+ if (curr.length !== next.length) return !1;
4136
+ const currSet = new Set(curr);
4137
+ return next.every((i2) => currSet.has(i2));
4138
+ }),
4139
+ startWith$1(/* @__PURE__ */ new Set()),
4140
+ pairwise$1(),
4141
+ switchMap((pair) => {
4142
+ const [curr, next] = pair.map((ids) => new Set(ids)), added = Array.from(next).filter((i2) => !curr.has(i2)), removed = Array.from(curr).filter((i2) => !next.has(i2)), changes = [
4143
+ ...added.map((id) => ({ id, add: !0 })),
4144
+ ...removed.map((id) => ({ id, add: !1 }))
4145
+ ].sort((a2, b2) => {
4146
+ const aIsDraft = a2.id === getDraftId(a2.id), bIsDraft = b2.id === getDraftId(b2.id);
4147
+ return aIsDraft && bIsDraft ? a2.id.localeCompare(b2.id, "en-US") : aIsDraft ? -1 : bIsDraft ? 1 : a2.id.localeCompare(b2.id, "en-US");
4083
4148
  });
4084
- };
4085
- }
4086
- );
4149
+ return of(...changes);
4150
+ }),
4151
+ groupBy$1((i2) => i2.id),
4152
+ mergeMap$1(
4153
+ (group) => group.pipe(
4154
+ switchMap((e3) => e3.add ? listen(context, e3.id).pipe(
4155
+ catchError$1((error) => {
4156
+ throw error instanceof OutOfSyncError && listen(context, e3.id), error;
4157
+ }),
4158
+ tap$1(
4159
+ (remote) => state.set(
4160
+ "applyRemoteDocument",
4161
+ (prev) => applyRemoteDocument(prev, remote, events)
4162
+ )
4163
+ )
4164
+ ) : EMPTY)
4165
+ )
4166
+ )
4167
+ ).subscribe({ error: (error) => state.set("setError", { error }) });
4168
+ }, subscribeToClientAndFetchDatasetAcl = ({
4169
+ instance,
4170
+ state
4171
+ }) => {
4172
+ const { projectId, dataset } = instance.config;
4173
+ return getClientState(instance, { apiVersion: API_VERSION$4 }).observable.pipe(
4174
+ switchMap(
4175
+ (client) => client.observable.request({
4176
+ uri: `/projects/${projectId}/datasets/${dataset}/acl`,
4177
+ tag: "acl.get",
4178
+ withCredentials: !0
4179
+ })
4180
+ ),
4181
+ tap$1((datasetAcl) => state.set("setGrants", { grants: createGrantsLookup(datasetAcl) }))
4182
+ ).subscribe({
4183
+ error: (error) => state.set("setError", { error })
4184
+ });
4185
+ };
4087
4186
  function applyDocumentActions(...args) {
4088
- return _applyDocumentActions(...args);
4187
+ return boundApplyDocumentActions(...args);
4089
4188
  }
4090
- const _applyDocumentActions = createAction(documentStore, ({ state }) => {
4091
- const { events } = state.get();
4092
- return async function(action, { transactionId = crypto.randomUUID(), disableBatching } = {}) {
4093
- const actions = Array.isArray(action) ? action : [action], transaction = {
4094
- transactionId,
4095
- actions,
4096
- ...disableBatching && { disableBatching }
4097
- }, fatalError$ = state.observable.pipe(
4098
- map((s2) => s2.error),
4099
- first$1(Boolean),
4100
- map((error) => ({ type: "error", error }))
4101
- ), transactionError$ = events.pipe(
4102
- filter((e3) => e3.type === "error"),
4103
- first$1((e3) => e3.transactionId === transactionId)
4104
- ), appliedTransaction$ = state.observable.pipe(
4105
- map((s2) => s2.applied),
4106
- distinctUntilChanged(),
4107
- map((applied) => applied.find((t2) => t2.transactionId === transactionId)),
4108
- first$1(Boolean)
4109
- ), successfulTransaction$ = events.pipe(
4110
- filter((e3) => e3.type === "accepted"),
4111
- first$1((e3) => e3.outgoing.batchedTransactionIds.includes(transactionId))
4112
- ), rejectedTransaction$ = events.pipe(
4113
- filter((e3) => e3.type === "reverted"),
4114
- first$1((e3) => e3.outgoing.batchedTransactionIds.includes(transactionId))
4115
- ), appliedTransactionOrError = firstValueFrom(
4116
- race([fatalError$, transactionError$, appliedTransaction$])
4117
- ), acceptedOrRejectedTransaction = firstValueFrom(
4118
- race([successfulTransaction$, rejectedTransaction$, transactionError$])
4119
- );
4120
- state.set("queueTransaction", (prev) => queueTransaction(prev, transaction));
4121
- const result = await appliedTransactionOrError;
4122
- if ("type" in result && result.type === "error") throw result.error;
4123
- const { working: documents, previous, previousRevs } = result, existingIds = new Set(
4124
- Object.entries(previous).filter(([, value]) => !!value).map(([key]) => key)
4125
- ), resultingIds = new Set(
4126
- Object.entries(documents).filter(([, value]) => !!value).map(([key]) => key)
4127
- ), allIds = /* @__PURE__ */ new Set([...existingIds, ...resultingIds]), updated = [], appeared = [], disappeared = [];
4128
- for (const id of allIds)
4129
- existingIds.has(id) && resultingIds.has(id) ? updated.push(id) : !existingIds.has(id) && resultingIds.has(id) ? appeared.push(id) : !resultingIds.has(id) && existingIds.has(id) && disappeared.push(id);
4130
- async function submitted() {
4131
- const raceResult = await acceptedOrRejectedTransaction;
4132
- if (raceResult.type !== "accepted") throw raceResult.error;
4133
- return raceResult.result;
4189
+ const boundApplyDocumentActions = bindActionByDataset(documentStore, _applyDocumentActions);
4190
+ async function _applyDocumentActions({ instance, state }, actionOrActions, { transactionId = crypto.randomUUID(), disableBatching } = {}) {
4191
+ const actions = Array.isArray(actionOrActions) ? actionOrActions : [actionOrActions];
4192
+ let projectId, dataset;
4193
+ for (const action of actions)
4194
+ if (action.projectId) {
4195
+ if (projectId || (projectId = action.projectId), action.projectId !== projectId)
4196
+ throw new Error(
4197
+ `Mismatched project IDs found in actions. All actions must belong to the same project. Found "${action.projectId}" but expected "${projectId}".`
4198
+ );
4199
+ if (action.dataset && (dataset || (dataset = action.dataset), action.dataset !== dataset))
4200
+ throw new Error(
4201
+ `Mismatched datasets found in actions. All actions must belong to the same dataset. Found "${action.dataset}" but expected "${dataset}".`
4202
+ );
4134
4203
  }
4135
- return {
4136
- transactionId,
4137
- documents,
4138
- previous,
4139
- previousRevs,
4140
- appeared,
4141
- updated,
4142
- disappeared,
4143
- submitted
4144
- };
4204
+ if (projectId && projectId !== instance.config.projectId || dataset && dataset !== instance.config.dataset) {
4205
+ const matchedInstance = instance.match({ projectId, dataset });
4206
+ if (!matchedInstance)
4207
+ throw new Error(
4208
+ `Could not find a matching instance for projectId: "${projectId}" and dataset: "${dataset}"`
4209
+ );
4210
+ return boundApplyDocumentActions(matchedInstance, actionOrActions, {
4211
+ disableBatching,
4212
+ transactionId
4213
+ });
4214
+ }
4215
+ const { events } = state.get(), transaction = {
4216
+ transactionId,
4217
+ actions,
4218
+ ...disableBatching && { disableBatching }
4219
+ }, fatalError$ = state.observable.pipe(
4220
+ map((s2) => s2.error),
4221
+ first$1(Boolean),
4222
+ map((error) => ({ type: "error", error }))
4223
+ ), transactionError$ = events.pipe(
4224
+ filter((e3) => e3.type === "error"),
4225
+ first$1((e3) => e3.transactionId === transactionId)
4226
+ ), appliedTransaction$ = state.observable.pipe(
4227
+ map((s2) => s2.applied),
4228
+ distinctUntilChanged(),
4229
+ map((applied) => applied.find((t2) => t2.transactionId === transactionId)),
4230
+ first$1(Boolean)
4231
+ ), successfulTransaction$ = events.pipe(
4232
+ filter((e3) => e3.type === "accepted"),
4233
+ first$1((e3) => e3.outgoing.batchedTransactionIds.includes(transactionId))
4234
+ ), rejectedTransaction$ = events.pipe(
4235
+ filter((e3) => e3.type === "reverted"),
4236
+ first$1((e3) => e3.outgoing.batchedTransactionIds.includes(transactionId))
4237
+ ), appliedTransactionOrError = firstValueFrom(
4238
+ race([fatalError$, transactionError$, appliedTransaction$])
4239
+ ), acceptedOrRejectedTransaction = firstValueFrom(
4240
+ race([successfulTransaction$, rejectedTransaction$, transactionError$])
4241
+ );
4242
+ state.set("queueTransaction", (prev) => queueTransaction(prev, transaction));
4243
+ const result = await appliedTransactionOrError;
4244
+ if ("type" in result && result.type === "error") throw result.error;
4245
+ const { working: documents, previous, previousRevs } = result, existingIds = new Set(
4246
+ Object.entries(previous).filter(([, value]) => !!value).map(([key]) => key)
4247
+ ), resultingIds = new Set(
4248
+ Object.entries(documents).filter(([, value]) => !!value).map(([key]) => key)
4249
+ ), allIds = /* @__PURE__ */ new Set([...existingIds, ...resultingIds]), updated = [], appeared = [], disappeared = [];
4250
+ for (const id of allIds)
4251
+ existingIds.has(id) && resultingIds.has(id) ? updated.push(id) : !existingIds.has(id) && resultingIds.has(id) ? appeared.push(id) : !resultingIds.has(id) && existingIds.has(id) && disappeared.push(id);
4252
+ async function submitted() {
4253
+ const raceResult = await acceptedOrRejectedTransaction;
4254
+ if (raceResult.type !== "accepted") throw raceResult.error;
4255
+ return raceResult.result;
4256
+ }
4257
+ return {
4258
+ transactionId,
4259
+ documents,
4260
+ previous,
4261
+ previousRevs,
4262
+ appeared,
4263
+ updated,
4264
+ disappeared,
4265
+ submitted
4145
4266
  };
4146
- });
4147
- function getSdkIdentity({
4148
- projectId,
4149
- dataset
4150
- }) {
4151
- const id = generateId();
4152
- return Object.freeze({
4153
- id,
4154
- projectId,
4155
- dataset,
4156
- resourceId: `${projectId}.${dataset}`
4157
- });
4158
4267
  }
4159
- function generateId() {
4160
- return Array.from(
4161
- { length: 8 },
4162
- () => Math.floor(Math.random() * 16).toString(16).padStart(2, "0")
4163
- ).join("");
4164
- }
4165
- function createSanityInstance({
4166
- projectId = "",
4167
- dataset = "",
4168
- ...config
4169
- }) {
4170
- const identity = getSdkIdentity({ projectId, dataset });
4171
- return {
4172
- identity,
4173
- config,
4174
- dispose: () => disposeResources(identity)
4175
- };
4268
+ const QUERY_STATE_CLEAR_DELAY = 1e3, QUERY_STORE_API_VERSION = "vX", setQueryError = (key, error) => (prev) => {
4269
+ const prevQuery = prev.queries[key];
4270
+ return prevQuery ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, error } } } : prev;
4271
+ }, setQueryData = (key, result, syncTags) => (prev) => {
4272
+ const prevQuery = prev.queries[key];
4273
+ return prevQuery ? {
4274
+ ...prev,
4275
+ queries: { ...prev.queries, [key]: { ...prevQuery, result: result ?? null, syncTags } }
4276
+ } : prev;
4277
+ }, setLastLiveEventId = (key, lastLiveEventId) => (prev) => {
4278
+ const prevQuery = prev.queries[key];
4279
+ return prevQuery ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, lastLiveEventId } } } : prev;
4280
+ }, addSubscriber = (key, subscriptionId) => (prev) => {
4281
+ const prevQuery = prev.queries[key], subscribers = [...prevQuery?.subscribers ?? [], subscriptionId];
4282
+ return { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, subscribers } } };
4283
+ }, removeSubscriber = (key, subscriptionId) => (prev) => {
4284
+ const prevQuery = prev.queries[key];
4285
+ if (!prevQuery) return prev;
4286
+ const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId);
4287
+ return subscribers.length ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, subscribers } } } : { ...prev, queries: omit(prev.queries, key) };
4288
+ }, cancelQuery = (key) => (prev) => {
4289
+ const prevQuery = prev.queries[key];
4290
+ return !prevQuery || prevQuery.subscribers.length ? prev : { ...prev, queries: omit(prev.queries, key) };
4291
+ }, initializeQuery = (key) => (prev) => prev.queries[key] ? prev : { ...prev, queries: { ...prev.queries, [key]: { subscribers: [] } } }, EMPTY_ARRAY = [], getQueryKey = (query, options = {}) => JSON.stringify({ query, options }), parseQueryKey = (key) => JSON.parse(key), queryStore = {
4292
+ name: "QueryStore",
4293
+ getInitialState: () => ({ queries: {} }),
4294
+ initialize(context) {
4295
+ const subscriptions = [
4296
+ listenForNewSubscribersAndFetch(context),
4297
+ listenToLiveClientAndSetLastLiveEventIds(context)
4298
+ ];
4299
+ return () => {
4300
+ for (const subscription of subscriptions)
4301
+ subscription.unsubscribe();
4302
+ };
4303
+ }
4304
+ }, errorHandler$1 = (state) => (error) => state.set("setError", { error }), listenForNewSubscribersAndFetch = ({ state, instance }) => state.observable.pipe(
4305
+ map((s2) => new Set(Object.keys(s2.queries))),
4306
+ distinctUntilChanged((curr, next) => curr.size !== next.size ? !1 : Array.from(next).every((i2) => curr.has(i2))),
4307
+ startWith$1(/* @__PURE__ */ new Set()),
4308
+ pairwise$1(),
4309
+ mergeMap$1(([curr, next]) => {
4310
+ const added = Array.from(next).filter((i2) => !curr.has(i2)), removed = Array.from(curr).filter((i2) => !next.has(i2));
4311
+ return [
4312
+ ...added.map((key) => ({ key, added: !0 })),
4313
+ ...removed.map((key) => ({ key, added: !1 }))
4314
+ ];
4315
+ }),
4316
+ groupBy$1((i2) => i2.key),
4317
+ mergeMap$1(
4318
+ (group$) => group$.pipe(
4319
+ switchMap((e3) => {
4320
+ if (!e3.added) return EMPTY;
4321
+ const lastLiveEventId$ = state.observable.pipe(
4322
+ map((s2) => s2.queries[group$.key]?.lastLiveEventId),
4323
+ distinctUntilChanged()
4324
+ ), { query, options: { params, projectId, dataset, tag, ...options } = {} } = parseQueryKey(group$.key), client$ = getClientState(instance, {
4325
+ apiVersion: QUERY_STORE_API_VERSION,
4326
+ projectId,
4327
+ dataset
4328
+ }).observable;
4329
+ return combineLatest([lastLiveEventId$, client$]).pipe(
4330
+ switchMap(
4331
+ ([lastLiveEventId, client]) => client.observable.fetch(query, params, {
4332
+ ...options,
4333
+ filterResponse: !1,
4334
+ returnQuery: !1,
4335
+ lastLiveEventId,
4336
+ tag
4337
+ })
4338
+ )
4339
+ );
4340
+ }),
4341
+ catchError$1((error) => (state.set("setQueryError", setQueryError(group$.key, error)), EMPTY)),
4342
+ tap$1(({ result, syncTags }) => {
4343
+ state.set("setQueryData", setQueryData(group$.key, result, syncTags));
4344
+ })
4345
+ )
4346
+ )
4347
+ ).subscribe({ error: errorHandler$1(state) }), listenToLiveClientAndSetLastLiveEventIds = ({
4348
+ state,
4349
+ instance
4350
+ }) => {
4351
+ const liveMessages$ = getClientState(instance, {
4352
+ apiVersion: QUERY_STORE_API_VERSION
4353
+ }).observable.pipe(
4354
+ switchMap(
4355
+ (client) => client.live.events({ includeDrafts: !!client.config().token, tag: "query-store" })
4356
+ ),
4357
+ share(),
4358
+ filter((e3) => e3.type === "message")
4359
+ );
4360
+ return state.observable.pipe(
4361
+ mergeMap$1((s2) => Object.entries(s2.queries)),
4362
+ groupBy$1(([key]) => key),
4363
+ mergeMap$1((group$) => {
4364
+ const syncTags$ = group$.pipe(
4365
+ map(([, queryState]) => queryState),
4366
+ map((i2) => i2?.syncTags ?? EMPTY_ARRAY),
4367
+ distinctUntilChanged()
4368
+ );
4369
+ return combineLatest([liveMessages$, syncTags$]).pipe(
4370
+ filter(([message, syncTags]) => message.tags.some((tag) => syncTags.includes(tag))),
4371
+ tap$1(([message]) => {
4372
+ state.set("setLastLiveEventId", setLastLiveEventId(group$.key, message.id));
4373
+ })
4374
+ );
4375
+ })
4376
+ ).subscribe({ error: errorHandler$1(state) });
4377
+ };
4378
+ function getQueryState(...args) {
4379
+ return _getQueryState(...args);
4380
+ }
4381
+ const _getQueryState = bindActionByDataset(
4382
+ queryStore,
4383
+ createStateSourceAction({
4384
+ selector: ({ state }, query, options) => {
4385
+ if (state.error) throw state.error;
4386
+ const key = getQueryKey(query, options), queryState = state.queries[key];
4387
+ if (queryState?.error) throw queryState.error;
4388
+ return queryState?.result;
4389
+ },
4390
+ onSubscribe: ({ state }, query, options) => {
4391
+ const subscriptionId = insecureRandomId(), key = getQueryKey(query, options);
4392
+ return state.set("addSubscriber", addSubscriber(key, subscriptionId)), () => {
4393
+ setTimeout(
4394
+ () => state.set("removeSubscriber", removeSubscriber(key, subscriptionId)),
4395
+ QUERY_STATE_CLEAR_DELAY
4396
+ );
4397
+ };
4398
+ }
4399
+ })
4400
+ );
4401
+ function resolveQuery(...args) {
4402
+ return _resolveQuery(...args);
4176
4403
  }
4177
- function createLiveEventSubscriber(tag) {
4178
- return createInternalAction(({ instance, state }) => {
4179
- const client$ = getClientState(instance, { apiVersion: "vX" }).observable, syncTags$ = state.observable.pipe(
4180
- map((i2) => i2.syncTags),
4181
- distinctUntilChanged()
4404
+ const _resolveQuery = bindActionByDataset(
4405
+ queryStore,
4406
+ ({ state, instance }, query, { signal, ...options } = {}) => {
4407
+ const { getCurrent } = getQueryState(instance, query, options), key = getQueryKey(query, options), aborted$ = signal ? new Observable((observer) => {
4408
+ const cleanup = () => {
4409
+ signal.removeEventListener("abort", listener);
4410
+ }, listener = () => {
4411
+ observer.error(new DOMException("The operation was aborted.", "AbortError")), observer.complete(), cleanup();
4412
+ };
4413
+ return signal.addEventListener("abort", listener), cleanup;
4414
+ }).pipe(
4415
+ catchError$1((error) => {
4416
+ throw error instanceof Error && error.name === "AbortError" && state.set("cancelQuery", cancelQuery(key)), error;
4417
+ })
4418
+ ) : NEVER;
4419
+ state.set("initializeQuery", initializeQuery(key));
4420
+ const resolved$ = state.observable.pipe(
4421
+ map(getCurrent),
4422
+ first$1((i2) => i2 !== void 0)
4182
4423
  );
4183
- return function() {
4184
- const messageEvents$ = client$.pipe(
4185
- switchMap(
4186
- (client) => client.live.events({ includeDrafts: !!client.config().token, tag }).pipe(filter((e3) => e3.type === "message"))
4187
- )
4188
- );
4189
- return combineLatest([messageEvents$, syncTags$]).subscribe({
4190
- next: ([event, currentSyncTags]) => {
4191
- for (const eventTag of event.tags)
4192
- if (currentSyncTags[eventTag]) {
4193
- state.set("setLastLiveEventId", (prevState) => ({
4194
- ...prevState,
4195
- lastLiveEventId: event.id
4196
- }));
4197
- return;
4198
- }
4199
- }
4200
- });
4201
- };
4202
- });
4203
- }
4424
+ return firstValueFrom(race([resolved$, aborted$]));
4425
+ }
4426
+ );
4204
4427
  function hashString(str) {
4205
4428
  let hash = 0;
4206
4429
  for (let i2 = 0; i2 < str.length; i2++)
4207
4430
  hash = (hash * 31 + str.charCodeAt(i2)) % 2147483647;
4208
4431
  return Math.abs(hash).toString(16).padStart(8, "0");
4209
4432
  }
4210
- const TITLE_CANDIDATES = ["title", "name", "label", "heading", "header", "caption"], SUBTITLE_CANDIDATES = ["description", "subtitle", ...TITLE_CANDIDATES];
4211
- function getPreviewProjection() {
4212
- return `{
4213
- // Get all potential title fields
4214
- "titleCandidates": {
4215
- ${TITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(`,
4433
+ const TITLE_CANDIDATES = ["title", "name", "label", "heading", "header", "caption"], SUBTITLE_CANDIDATES = ["description", "subtitle", ...TITLE_CANDIDATES], PREVIEW_PROJECTION = `{
4434
+ // Get all potential title fields
4435
+ "titleCandidates": {
4436
+ ${TITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(`,
4216
4437
  `)}
4217
- },
4218
- // Get all potential subtitle fields
4219
- "subtitleCandidates": {
4220
- ${SUBTITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(`,
4438
+ },
4439
+ // Get all potential subtitle fields
4440
+ "subtitleCandidates": {
4441
+ ${SUBTITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(`,
4221
4442
  `)}
4222
- },
4223
- "media": coalesce(
4224
- select(
4225
- defined(asset) => {"type": "image-asset", "_ref": asset._ref},
4226
- defined(image.asset) => {"type": "image-asset", "_ref": image.asset._ref},
4227
- defined(mainImage.asset) => {"type": "image-asset", "_ref": mainImage.asset._ref},
4228
- null
4229
- )
4230
- ),
4231
- _type,
4232
- _id,
4233
- _updatedAt
4234
- }`;
4235
- }
4236
- const PREVIEW_TAG = "preview", STABLE_EMPTY_PREVIEW = { data: null, isPending: !1 }, STABLE_ERROR_PREVIEW = {
4443
+ },
4444
+ "media": coalesce(
4445
+ select(
4446
+ defined(asset) => {"type": "image-asset", "_ref": asset._ref},
4447
+ defined(image.asset) => {"type": "image-asset", "_ref": image.asset._ref},
4448
+ defined(mainImage.asset) => {"type": "image-asset", "_ref": mainImage.asset._ref},
4449
+ null
4450
+ )
4451
+ ),
4452
+ _type,
4453
+ _id,
4454
+ _updatedAt
4455
+ }`, PREVIEW_TAG = "preview", PREVIEW_PERSPECTIVE = "drafts", STABLE_EMPTY_PREVIEW = { data: null, isPending: !1 }, STABLE_ERROR_PREVIEW = {
4237
4456
  data: {
4238
4457
  title: "Preview Error",
4239
4458
  ...!!getEnv("DEV") && { subtitle: "Check the console for more details" }
@@ -4297,104 +4516,92 @@ function processPreviewQuery({
4297
4516
  );
4298
4517
  }
4299
4518
  function createPreviewQuery(documentIds) {
4300
- const allIds = Array.from(documentIds).flatMap((id) => [getPublishedId(id), getDraftId(id)]), projection = getPreviewProjection(), queryHash = hashString(projection);
4519
+ const allIds = Array.from(documentIds).flatMap((id) => [getPublishedId(id), getDraftId(id)]), queryHash = hashString(PREVIEW_PROJECTION);
4301
4520
  return {
4302
- query: `*[_id in $__ids_${queryHash}]${projection}`,
4521
+ query: `*[_id in $__ids_${queryHash}]${PREVIEW_PROJECTION}`,
4303
4522
  params: {
4304
4523
  [`__ids_${queryHash}`]: allIds
4305
4524
  }
4306
4525
  };
4307
4526
  }
4308
- const BATCH_DEBOUNCE_TIME$1 = 50, subscribeToStateAndFetchBatches$1 = createInternalAction(
4309
- ({ state, instance }) => function() {
4310
- const client$ = getClientState(instance, { apiVersion: "vX" }).observable, documentTypes$ = state.observable.pipe(
4311
- map((i2) => i2.documentTypes),
4312
- distinctUntilChanged()
4313
- ), lastLiveEventId$ = state.observable.pipe(
4314
- map((i2) => i2.lastLiveEventId),
4315
- distinctUntilChanged()
4316
- ), newSubscriberIds$ = state.observable.pipe(
4317
- map(({ subscriptions }) => new Set(Object.keys(subscriptions))),
4318
- distinctUntilChanged(
4319
- (a2, b2) => a2.size !== b2.size ? !1 : Array.from(a2).every((i2) => b2.has(i2))
4320
- ),
4321
- debounceTime(BATCH_DEBOUNCE_TIME$1),
4322
- startWith$1(/* @__PURE__ */ new Set()),
4323
- pairwise$1(),
4324
- tap$1(([prevIds, currIds]) => {
4325
- const newIds = [...currIds].filter((element) => !prevIds.has(element));
4326
- state.set("updatingPending", (prev) => {
4327
- const pendingValues = newIds.reduce((acc, id) => {
4328
- const prevValue = prev.values[id], value = prevValue?.data ? prevValue.data : null;
4329
- return acc[id] = { data: value, isPending: !0 }, acc;
4330
- }, {});
4331
- return { values: { ...prev.values, ...pendingValues } };
4332
- });
4333
- }),
4334
- withLatestFrom(documentTypes$),
4335
- map(([[, ids], documentTypes]) => ({ ids, documentTypes }))
4336
- );
4337
- return combineLatest([newSubscriberIds$, lastLiveEventId$, client$]).pipe(
4338
- switchMap(([{ ids }, lastLiveEventId, client]) => {
4339
- if (!ids.size) return EMPTY;
4340
- const { query, params } = createPreviewQuery(ids);
4341
- return client.observable.fetch(query, params, {
4342
- filterResponse: !1,
4343
- returnQuery: !1,
4344
- perspective: "drafts",
4527
+ const BATCH_DEBOUNCE_TIME$1 = 50, isSetEqual$1 = (a2, b2) => a2.size === b2.size && Array.from(a2).every((i2) => b2.has(i2)), subscribeToStateAndFetchBatches$1 = ({
4528
+ state,
4529
+ instance
4530
+ }) => state.observable.pipe(
4531
+ map(({ subscriptions }) => new Set(Object.keys(subscriptions))),
4532
+ distinctUntilChanged(isSetEqual$1),
4533
+ debounceTime(BATCH_DEBOUNCE_TIME$1),
4534
+ startWith$1(/* @__PURE__ */ new Set()),
4535
+ pairwise$1(),
4536
+ tap$1(([prevIds, currIds]) => {
4537
+ const newIds = [...currIds].filter((element) => !prevIds.has(element));
4538
+ state.set("updatingPending", (prev) => {
4539
+ const pendingValues = newIds.reduce((acc, id) => {
4540
+ const prevValue = prev.values[id], value = prevValue?.data ? prevValue.data : null;
4541
+ return acc[id] = { data: value, isPending: !0 }, acc;
4542
+ }, {});
4543
+ return { values: { ...prev.values, ...pendingValues } };
4544
+ });
4545
+ }),
4546
+ map(([, ids]) => ids),
4547
+ distinctUntilChanged(isSetEqual$1)
4548
+ ).pipe(
4549
+ switchMap((ids) => {
4550
+ if (!ids.size) return EMPTY;
4551
+ const { query, params } = createPreviewQuery(ids), controller = new AbortController();
4552
+ return new Observable((observer) => {
4553
+ const { getCurrent, observable } = getQueryState(instance, query, {
4554
+ params,
4555
+ tag: PREVIEW_TAG,
4556
+ perspective: PREVIEW_PERSPECTIVE
4557
+ }), subscription = defer(() => getCurrent() === void 0 ? from(
4558
+ resolveQuery(instance, query, {
4559
+ params,
4345
4560
  tag: PREVIEW_TAG,
4346
- lastLiveEventId
4347
- }).pipe(map((response) => ({ ...response, ids })));
4348
- }),
4349
- map(({ ids, result, syncTags }) => ({
4350
- syncTags,
4351
- values: processPreviewQuery({
4352
- projectId: instance.identity.projectId,
4353
- dataset: instance.identity.dataset,
4354
- ids,
4355
- results: result
4561
+ perspective: PREVIEW_PERSPECTIVE,
4562
+ signal: controller.signal
4356
4563
  })
4357
- }))
4358
- ).subscribe({
4359
- next: ({ syncTags = [], values }) => {
4360
- state.set("updateResult", (prev) => ({
4361
- values: { ...prev.values, ...values },
4362
- syncTags: syncTags.reduce((acc, next) => (acc[next] = !0, acc), {})
4363
- }));
4364
- }
4365
- });
4564
+ ).pipe(switchMap(() => observable)) : observable).pipe(filter((result) => result !== void 0)).subscribe(observer);
4565
+ return () => {
4566
+ controller.signal.aborted || controller.abort(), subscription.unsubscribe();
4567
+ };
4568
+ }).pipe(map((data) => ({ data, ids })));
4569
+ }),
4570
+ map(({ ids, data }) => ({
4571
+ values: processPreviewQuery({
4572
+ projectId: instance.config.projectId,
4573
+ dataset: instance.config.dataset,
4574
+ ids,
4575
+ results: data
4576
+ })
4577
+ }))
4578
+ ).subscribe({
4579
+ next: ({ values }) => {
4580
+ state.set("updateResult", (prev) => ({ values: { ...prev.values, ...values } }));
4366
4581
  }
4367
- ), previewStore = {
4582
+ }), previewStore = {
4368
4583
  name: "Preview",
4369
4584
  getInitialState() {
4370
4585
  return {
4371
- documentTypes: {},
4372
- lastLiveEventId: null,
4373
4586
  subscriptions: {},
4374
- syncTags: {},
4375
4587
  values: {}
4376
4588
  };
4377
4589
  },
4378
- initialize() {
4379
- const subscribeToLiveAndSetLastLiveEventId = createLiveEventSubscriber(PREVIEW_TAG), stateSubscriptionForBatches = subscribeToStateAndFetchBatches$1(this), liveSubscription = subscribeToLiveAndSetLastLiveEventId(this);
4380
- return () => {
4381
- stateSubscriptionForBatches.unsubscribe(), liveSubscription.unsubscribe();
4382
- };
4590
+ initialize: (context) => {
4591
+ const subscription = subscribeToStateAndFetchBatches$1(context);
4592
+ return () => subscription.unsubscribe;
4383
4593
  }
4384
- }, _getPreviewState = createStateSourceAction(
4594
+ };
4595
+ function getPreviewState(...args) {
4596
+ return _getPreviewState(...args);
4597
+ }
4598
+ const _getPreviewState = bindActionByDataset(
4385
4599
  previewStore,
4386
- (state, { document }) => state.values[document._id] ?? STABLE_EMPTY_PREVIEW
4387
- ), getPreviewState = createAction(previewStore, ({ state }) => function({ document }) {
4388
- const { _id, _type: documentType } = document, documentId = getPublishedId(_id), previewState = _getPreviewState(this, { document });
4389
- return {
4390
- ...previewState,
4391
- subscribe: (subscriber) => {
4392
- const subscriptionId = insecureRandomId();
4393
- state.set("addSubscription", (prev) => ({
4394
- documentTypes: {
4395
- ...prev.documentTypes,
4396
- [documentId]: documentType
4397
- },
4600
+ createStateSourceAction({
4601
+ selector: ({ state }, docHandle) => state.values[docHandle.documentId] ?? STABLE_EMPTY_PREVIEW,
4602
+ onSubscribe: ({ state }, docHandle) => {
4603
+ const subscriptionId = insecureRandomId(), documentId = getPublishedId(docHandle.documentId);
4604
+ return state.set("addSubscription", (prev) => ({
4398
4605
  subscriptions: {
4399
4606
  ...prev.subscriptions,
4400
4607
  [documentId]: {
@@ -4402,10 +4609,8 @@ const BATCH_DEBOUNCE_TIME$1 = 50, subscribeToStateAndFetchBatches$1 = createInte
4402
4609
  [subscriptionId]: !0
4403
4610
  }
4404
4611
  }
4405
- }));
4406
- const unsubscribe = previewState.subscribe(subscriber);
4407
- return () => {
4408
- unsubscribe(), state.set("removeSubscription", (prev) => {
4612
+ })), () => {
4613
+ state.set("removeSubscription", (prev) => {
4409
4614
  const documentSubscriptions = omit(prev.subscriptions[documentId], subscriptionId), hasSubscribers = !!Object.keys(documentSubscriptions).length, prevValue = prev.values[documentId], previewValue = prevValue?.data ? prevValue.data : null;
4410
4615
  return {
4411
4616
  subscriptions: hasSubscribers ? { ...prev.subscriptions, [documentId]: documentSubscriptions } : omit(prev.subscriptions, documentId),
@@ -4414,40 +4619,46 @@ const BATCH_DEBOUNCE_TIME$1 = 50, subscribeToStateAndFetchBatches$1 = createInte
4414
4619
  });
4415
4620
  };
4416
4621
  }
4417
- };
4418
- }), resolvePreview = createAction(previewStore, () => function({ document }) {
4419
- const { getCurrent, subscribe } = getPreviewState(this, { document });
4420
- return new Promise((resolve) => {
4421
- const unsubscribe = subscribe(() => {
4422
- const current = getCurrent();
4423
- current?.data && (resolve(current), unsubscribe());
4424
- });
4425
- });
4426
- }), project = createFetcherStore({
4622
+ })
4623
+ ), resolvePreview = bindActionByDataset(
4624
+ previewStore,
4625
+ ({ instance }, docHandle) => firstValueFrom(getPreviewState(instance, docHandle).observable.pipe(filter((i2) => !!i2.data)))
4626
+ ), API_VERSION$2 = "v2025-02-19", project = createFetcherStore({
4427
4627
  name: "Project",
4428
- getKey: (projectId) => projectId,
4429
- fetcher: (instance) => (projectId) => getClientState(instance, { apiVersion: "vX", scope: "global" }).observable.pipe(
4430
- switchMap((client) => client.observable.projects.getById(projectId))
4431
- )
4432
- }), getProjectState = project.getState, resolveProject = project.resolveState, PROJECTION_TAG = "sdk.projection", STABLE_EMPTY_PROJECTION = {
4433
- data: null,
4434
- isPending: !1
4435
- };
4436
- function validateProjection(projection) {
4437
- if (!projection.startsWith("{") || !projection.endsWith("}"))
4438
- throw new Error(
4439
- `Invalid projection format: "${projection}". Projections must be enclosed in curly braces, e.g. "{title, 'author': author.name}"`
4628
+ getKey: (instance, options) => {
4629
+ const projectId = options?.projectId ?? instance.config.projectId;
4630
+ if (!projectId)
4631
+ throw new Error("A projectId is required to use the project API.");
4632
+ return projectId;
4633
+ },
4634
+ fetcher: (instance) => (options = {}) => {
4635
+ const projectId = options.projectId ?? instance.config.projectId;
4636
+ return getClientState(instance, {
4637
+ apiVersion: API_VERSION$2,
4638
+ scope: "global",
4639
+ projectId
4640
+ }).observable.pipe(
4641
+ switchMap(
4642
+ (client) => client.observable.projects.getById(
4643
+ // non-null assertion is fine with the above throwing
4644
+ projectId ?? instance.config.projectId
4645
+ )
4646
+ )
4440
4647
  );
4441
- return projection;
4442
- }
4648
+ }
4649
+ }), getProjectState = project.getState, resolveProject = project.resolveState;
4443
4650
  function createProjectionQuery(documentIds, documentProjections) {
4444
- const projections = Array.from(documentIds).filter((id) => documentProjections[id]).map((id) => {
4445
- const projection = validateProjection(documentProjections[id]), projectionHash = hashString(projection);
4446
- return { documentId: id, projection, projectionHash };
4651
+ const projections = Array.from(documentIds).flatMap((id) => {
4652
+ const projectionsForDoc = documentProjections[id];
4653
+ return projectionsForDoc ? Object.entries(projectionsForDoc).map(([projectionHash, projection]) => ({
4654
+ documentId: id,
4655
+ projection,
4656
+ projectionHash
4657
+ })) : [];
4447
4658
  }).reduce((acc, { documentId, projection, projectionHash }) => {
4448
4659
  const obj = acc[projectionHash] ?? { documentIds: /* @__PURE__ */ new Set(), projection };
4449
4660
  return obj.documentIds.add(documentId), acc[projectionHash] = obj, acc;
4450
- }, {}), query = `[${Object.entries(projections).map(([projectionHash, { projection }]) => `...*[_id in $__ids_${projectionHash}]{_id,_type,_updatedAt,"result":{...${projection}}}`).join(",")}]`, params = Object.fromEntries(
4661
+ }, {}), query = `[${Object.entries(projections).map(([projectionHash, { projection }]) => `...*[_id in $__ids_${projectionHash}]{_id,_type,_updatedAt,"__projectionHash":"${projectionHash}","result":{...${projection}}}`).join(",")}]`, params = Object.fromEntries(
4451
4662
  Object.entries(projections).map(([projectionHash, value]) => {
4452
4663
  const idsInProjection = Array.from(value.documentIds).flatMap((id) => [
4453
4664
  getPublishedId(id),
@@ -4459,408 +4670,397 @@ function createProjectionQuery(documentIds, documentProjections) {
4459
4670
  return { query, params };
4460
4671
  }
4461
4672
  function processProjectionQuery({ ids, results }) {
4462
- const resultMap = results.reduce(
4463
- (acc, next) => (acc[next._id] = next, acc),
4464
- {}
4465
- );
4466
- return Object.fromEntries(
4467
- Array.from(ids).map((id) => {
4468
- const publishedId = getPublishedId(id), draftId = getDraftId(id), draftResult = resultMap[draftId], publishedResult = resultMap[publishedId], projectionResult = draftResult?.result ?? publishedResult?.result;
4469
- if (!projectionResult) return [id, { data: null, isPending: !1 }];
4470
- const status = {
4471
- ...draftResult?._updatedAt && { lastEditedDraftAt: draftResult._updatedAt },
4472
- ...publishedResult?._updatedAt && { lastEditedPublishedAt: publishedResult._updatedAt }
4473
- };
4474
- return [id, { data: { ...projectionResult, status }, isPending: !1 }];
4475
- })
4476
- );
4673
+ const groupedResults = {};
4674
+ for (const result of results) {
4675
+ const originalId = getPublishedId(result._id), hash = result.__projectionHash, isDraft = result._id.startsWith("drafts.");
4676
+ ids.has(originalId) && (groupedResults[originalId] || (groupedResults[originalId] = {}), groupedResults[originalId][hash] || (groupedResults[originalId][hash] = {}), isDraft ? groupedResults[originalId][hash].draft = result : groupedResults[originalId][hash].published = result);
4677
+ }
4678
+ const finalValues = {};
4679
+ for (const originalId of ids) {
4680
+ finalValues[originalId] = {};
4681
+ const projectionsForDoc = groupedResults[originalId];
4682
+ if (projectionsForDoc)
4683
+ for (const hash in projectionsForDoc) {
4684
+ const { draft, published } = projectionsForDoc[hash], projectionResultData = draft?.result ?? published?.result;
4685
+ if (!projectionResultData) {
4686
+ finalValues[originalId][hash] = { data: null, isPending: !1 };
4687
+ continue;
4688
+ }
4689
+ const status = {
4690
+ ...draft?._updatedAt && { lastEditedDraftAt: draft._updatedAt },
4691
+ ...published?._updatedAt && { lastEditedPublishedAt: published._updatedAt }
4692
+ };
4693
+ finalValues[originalId][hash] = {
4694
+ data: { ...projectionResultData, status },
4695
+ isPending: !1
4696
+ };
4697
+ }
4698
+ }
4699
+ return finalValues;
4477
4700
  }
4478
- const BATCH_DEBOUNCE_TIME = 50, subscribeToStateAndFetchBatches = createInternalAction(
4479
- ({ state, instance }) => function() {
4480
- const client$ = new Observable(
4481
- (observer) => getClientState(instance, { apiVersion: "vX" }).observable.subscribe(observer)
4482
- ), documentProjections$ = state.observable.pipe(
4483
- map((i2) => i2.documentProjections),
4484
- distinctUntilChanged()
4485
- ), lastLiveEventId$ = state.observable.pipe(
4486
- map((i2) => i2.lastLiveEventId),
4487
- distinctUntilChanged()
4488
- ), newSubscriberIds$ = state.observable.pipe(
4489
- map(({ subscriptions }) => new Set(Object.keys(subscriptions))),
4490
- distinctUntilChanged(
4491
- (a2, b2) => a2.size !== b2.size ? !1 : Array.from(a2).every((i2) => b2.has(i2))
4492
- ),
4493
- debounceTime(BATCH_DEBOUNCE_TIME),
4494
- startWith$1(/* @__PURE__ */ new Set()),
4495
- pairwise$1(),
4496
- tap$1(([prevIds, currIds]) => {
4497
- const newIds = [...currIds].filter((element) => !prevIds.has(element));
4498
- state.set("updatingPending", (prev) => {
4499
- const pendingValues = newIds.reduce((acc, id) => {
4500
- const prevValue = prev.values[id], value = prevValue?.data ? prevValue.data : null;
4501
- return acc[id] = { data: value, isPending: !0 }, acc;
4502
- }, {});
4503
- return { values: { ...prev.values, ...pendingValues } };
4504
- });
4505
- }),
4506
- withLatestFrom(documentProjections$),
4507
- map(([[, ids], documentProjections]) => ({ ids, documentProjections }))
4701
+ const PROJECTION_TAG = "sdk.projection", PROJECTION_PERSPECTIVE = "drafts", PROJECTION_STATE_CLEAR_DELAY = 1e3, STABLE_EMPTY_PROJECTION = {
4702
+ data: null,
4703
+ isPending: !1
4704
+ };
4705
+ function validateProjection(projection) {
4706
+ if (!projection.startsWith("{") || !projection.endsWith("}"))
4707
+ throw new Error(
4708
+ `Invalid projection format: "${projection}". Projections must be enclosed in curly braces, e.g. "{title, 'author': author.name}"`
4508
4709
  );
4509
- return combineLatest([newSubscriberIds$, lastLiveEventId$, client$]).pipe(
4510
- switchMap(([{ ids, documentProjections }, lastLiveEventId, client]) => {
4511
- if (!ids.size) return EMPTY;
4512
- const { query, params } = createProjectionQuery(ids, documentProjections);
4513
- return client.observable.fetch(query, params, {
4514
- filterResponse: !1,
4515
- returnQuery: !1,
4516
- perspective: "drafts",
4710
+ return projection;
4711
+ }
4712
+ const BATCH_DEBOUNCE_TIME = 50, isSetEqual = (a2, b2) => a2.size === b2.size && Array.from(a2).every((i2) => b2.has(i2)), subscribeToStateAndFetchBatches = ({
4713
+ state,
4714
+ instance
4715
+ }) => {
4716
+ const documentProjections$ = state.observable.pipe(
4717
+ map((s2) => s2.documentProjections),
4718
+ distinctUntilChanged(isEqual)
4719
+ ), activeDocumentIds$ = state.observable.pipe(
4720
+ map(({ subscriptions }) => new Set(Object.keys(subscriptions))),
4721
+ distinctUntilChanged(isSetEqual)
4722
+ ), pendingUpdateSubscription = activeDocumentIds$.pipe(
4723
+ debounceTime(BATCH_DEBOUNCE_TIME),
4724
+ startWith$1(/* @__PURE__ */ new Set()),
4725
+ pairwise$1(),
4726
+ tap$1(([prevIds, currIds]) => {
4727
+ const newIds = [...currIds].filter((id) => !prevIds.has(id));
4728
+ newIds.length !== 0 && state.set("updatingPending", (prev) => {
4729
+ const nextValues = { ...prev.values };
4730
+ for (const id of newIds) {
4731
+ const projectionsForDoc = prev.documentProjections[id];
4732
+ if (!projectionsForDoc) continue;
4733
+ const updatedValuesForDoc = { ...prev.values[id] ?? {} };
4734
+ for (const hash in projectionsForDoc) {
4735
+ const currentValue = updatedValuesForDoc[hash];
4736
+ updatedValuesForDoc[hash] = {
4737
+ data: currentValue?.data ?? null,
4738
+ isPending: !0
4739
+ };
4740
+ }
4741
+ nextValues[id] = updatedValuesForDoc;
4742
+ }
4743
+ return { values: nextValues };
4744
+ });
4745
+ })
4746
+ ).subscribe(), queryExecutionSubscription = combineLatest([activeDocumentIds$, documentProjections$]).pipe(
4747
+ debounceTime(BATCH_DEBOUNCE_TIME),
4748
+ distinctUntilChanged(isEqual)
4749
+ ).pipe(
4750
+ switchMap(([ids, documentProjections]) => {
4751
+ if (!ids.size) return EMPTY;
4752
+ const { query, params } = createProjectionQuery(ids, documentProjections), controller = new AbortController();
4753
+ return new Observable((observer) => {
4754
+ const { getCurrent, observable } = getQueryState(instance, query, {
4755
+ params,
4517
4756
  tag: PROJECTION_TAG,
4518
- lastLiveEventId
4519
- }).pipe(map((response) => ({ ...response, ids })));
4520
- }),
4521
- map(({ ids, result, syncTags }) => ({
4522
- syncTags,
4523
- values: processProjectionQuery({
4524
- ids,
4525
- results: result
4526
- })
4527
- }))
4528
- ).subscribe({
4529
- next: ({ syncTags = [], values }) => {
4530
- state.set("updateResult", (prev) => ({
4531
- values: { ...prev.values, ...values },
4532
- syncTags: syncTags.reduce((acc, next) => (acc[next] = !0, acc), {})
4533
- }));
4534
- }
4535
- });
4536
- }
4537
- ), projectionStore = {
4757
+ perspective: PROJECTION_PERSPECTIVE
4758
+ }), subscription = defer(() => getCurrent() === void 0 ? from(
4759
+ resolveQuery(instance, query, {
4760
+ params,
4761
+ tag: PROJECTION_TAG,
4762
+ perspective: PROJECTION_PERSPECTIVE,
4763
+ signal: controller.signal
4764
+ })
4765
+ ).pipe(switchMap(() => observable)) : observable).pipe(filter((result) => result !== void 0)).subscribe(observer);
4766
+ return () => {
4767
+ controller.signal.aborted || controller.abort(), subscription.unsubscribe();
4768
+ };
4769
+ }).pipe(map((data) => ({ data, ids })));
4770
+ }),
4771
+ map(
4772
+ ({ ids, data }) => processProjectionQuery({
4773
+ ids,
4774
+ results: data
4775
+ })
4776
+ )
4777
+ ).subscribe({
4778
+ next: (processedValues) => {
4779
+ state.set("updateResult", (prev) => {
4780
+ const nextValues = { ...prev.values };
4781
+ for (const docId in processedValues)
4782
+ processedValues[docId] && (nextValues[docId] = {
4783
+ ...prev.values[docId] ?? {},
4784
+ ...processedValues[docId]
4785
+ });
4786
+ return { values: nextValues };
4787
+ });
4788
+ },
4789
+ error: (err) => {
4790
+ console.error("Error fetching projection batches:", err);
4791
+ }
4792
+ });
4793
+ return new Subscription(() => {
4794
+ pendingUpdateSubscription.unsubscribe(), queryExecutionSubscription.unsubscribe();
4795
+ });
4796
+ }, projectionStore = {
4538
4797
  name: "Projection",
4539
4798
  getInitialState() {
4540
4799
  return {
4541
4800
  values: {},
4542
4801
  documentProjections: {},
4543
- subscriptions: {},
4544
- syncTags: {},
4545
- lastLiveEventId: null
4802
+ subscriptions: {}
4546
4803
  };
4547
4804
  },
4548
- initialize() {
4549
- const liveSubscription = createLiveEventSubscriber(PROJECTION_TAG)(this), batchSubscription = subscribeToStateAndFetchBatches(this);
4550
- return () => {
4551
- liveSubscription.unsubscribe(), batchSubscription.unsubscribe();
4552
- };
4805
+ initialize(context) {
4806
+ const batchSubscription = subscribeToStateAndFetchBatches(context);
4807
+ return () => batchSubscription.unsubscribe();
4553
4808
  }
4554
- }, getProjectStateSourceAction = createStateSourceAction(
4555
- projectionStore,
4556
- (state, { document }) => state.values[document._id] ?? STABLE_EMPTY_PROJECTION
4557
- );
4809
+ };
4558
4810
  function getProjectionState(...args) {
4559
4811
  return _getProjectionState(...args);
4560
4812
  }
4561
- const _getProjectionState = createAction(projectionStore, ({ state }) => function({
4562
- document,
4563
- projection
4564
- }) {
4565
- const { _id } = document, documentId = getPublishedId(_id), projectionState = getProjectStateSourceAction(this, { document, projection });
4566
- return {
4567
- ...projectionState,
4568
- subscribe: (subscriber) => {
4569
- const subscriptionId = insecureRandomId();
4570
- state.set("addSubscription", (prev) => ({
4813
+ const _getProjectionState = bindActionByDataset(
4814
+ projectionStore,
4815
+ createStateSourceAction({
4816
+ selector: ({ state }, options) => {
4817
+ const documentId = getPublishedId(options.documentId), projectionHash = hashString(options.projection);
4818
+ return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION;
4819
+ },
4820
+ onSubscribe: ({ state }, { projection, ...docHandle }) => {
4821
+ const subscriptionId = insecureRandomId(), documentId = getPublishedId(docHandle.documentId), validProjection = validateProjection(projection), projectionHash = hashString(validProjection);
4822
+ return state.set("addSubscription", (prev) => ({
4571
4823
  documentProjections: {
4572
4824
  ...prev.documentProjections,
4573
- [documentId]: validateProjection(projection)
4825
+ [documentId]: {
4826
+ ...prev.documentProjections[documentId],
4827
+ [projectionHash]: validProjection
4828
+ }
4574
4829
  },
4575
4830
  subscriptions: {
4576
4831
  ...prev.subscriptions,
4577
4832
  [documentId]: {
4578
4833
  ...prev.subscriptions[documentId],
4579
- [subscriptionId]: !0
4834
+ [projectionHash]: {
4835
+ ...prev.subscriptions[documentId]?.[projectionHash],
4836
+ [subscriptionId]: !0
4837
+ }
4580
4838
  }
4581
4839
  }
4582
- }));
4583
- const unsubscribe = projectionState.subscribe(subscriber);
4584
- return () => {
4585
- unsubscribe(), state.set("removeSubscription", (prev) => {
4586
- const documentSubscriptions = omit(prev.subscriptions[documentId], subscriptionId), hasSubscribers = !!Object.keys(documentSubscriptions).length, prevValue = prev.values[documentId], projectionValue = prevValue?.data ? prevValue.data : null;
4587
- return {
4588
- subscriptions: hasSubscribers ? { ...prev.subscriptions, [documentId]: documentSubscriptions } : omit(prev.subscriptions, documentId),
4589
- values: hasSubscribers ? prev.values : { ...prev.values, [documentId]: { data: projectionValue, isPending: !1 } }
4590
- };
4591
- });
4840
+ })), () => {
4841
+ setTimeout(() => {
4842
+ state.set("removeSubscription", (prev) => {
4843
+ const documentSubscriptionsForHash = omit(
4844
+ prev.subscriptions[documentId]?.[projectionHash],
4845
+ subscriptionId
4846
+ ), hasSubscribersForProjection = !!Object.keys(documentSubscriptionsForHash).length, nextSubscriptions = { ...prev.subscriptions }, nextDocumentProjections = { ...prev.documentProjections }, nextValues = { ...prev.values };
4847
+ if (hasSubscribersForProjection)
4848
+ nextSubscriptions[documentId] && (nextSubscriptions[documentId][projectionHash] = documentSubscriptionsForHash);
4849
+ else {
4850
+ delete nextSubscriptions[documentId][projectionHash], delete nextDocumentProjections[documentId][projectionHash];
4851
+ const currentProjectionValue = prev.values[documentId]?.[projectionHash];
4852
+ currentProjectionValue && nextValues[documentId] && (nextValues[documentId][projectionHash] = {
4853
+ data: currentProjectionValue.data,
4854
+ isPending: !1
4855
+ });
4856
+ }
4857
+ return Object.values(
4858
+ nextSubscriptions[documentId] ?? {}
4859
+ ).some((subs) => Object.keys(subs).length > 0) || (delete nextSubscriptions[documentId], delete nextDocumentProjections[documentId]), {
4860
+ subscriptions: nextSubscriptions,
4861
+ documentProjections: nextDocumentProjections,
4862
+ values: nextValues
4863
+ };
4864
+ });
4865
+ }, PROJECTION_STATE_CLEAR_DELAY);
4592
4866
  };
4593
4867
  }
4594
- };
4595
- }), resolveProjection = createAction(projectionStore, () => function({ document, projection }) {
4596
- const { getCurrent, subscribe } = getProjectionState(this, { document, projection });
4597
- return new Promise((resolve) => {
4598
- const unsubscribe = subscribe(() => {
4599
- const current = getCurrent();
4600
- current?.data && (resolve(current), unsubscribe());
4601
- });
4602
- });
4603
- }), projects = createFetcherStore({
4868
+ })
4869
+ ), resolveProjection = bindActionByDataset(
4870
+ projectionStore,
4871
+ ({ instance }, { projection, ...docHandle }) => firstValueFrom(
4872
+ getProjectionState(instance, { ...docHandle, projection }).observable.pipe(
4873
+ filter((i2) => !!i2.data)
4874
+ )
4875
+ )
4876
+ ), API_VERSION$1 = "v2025-02-19", projects = createFetcherStore({
4604
4877
  name: "Projects",
4605
4878
  getKey: () => "projects",
4606
- fetcher: (instance) => () => getClientState(instance, { apiVersion: "vX", scope: "global" }).observable.pipe(
4879
+ fetcher: (instance) => () => getClientState(instance, {
4880
+ apiVersion: API_VERSION$1,
4881
+ scope: "global"
4882
+ }).observable.pipe(
4607
4883
  switchMap((client) => client.observable.projects.list({ includeMembers: !1 }))
4608
4884
  )
4609
- }), getProjectsState = projects.getState, resolveProjects = projects.resolveState, QUERY_STATE_CLEAR_DELAY = 1e3, setQueryError = (key, error) => (prev) => {
4610
- const prevQuery = prev.queries[key];
4611
- return prevQuery ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, error } } } : prev;
4612
- }, setQueryData = (key, result, syncTags) => (prev) => {
4613
- const prevQuery = prev.queries[key];
4614
- return prevQuery ? {
4615
- ...prev,
4616
- queries: { ...prev.queries, [key]: { ...prevQuery, result: result ?? null, syncTags } }
4617
- } : prev;
4618
- }, setLastLiveEventId = (key, lastLiveEventId) => (prev) => {
4619
- const prevQuery = prev.queries[key];
4620
- return prevQuery ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, lastLiveEventId } } } : prev;
4621
- }, addSubscriber = (key, subscriptionId) => (prev) => {
4622
- const prevQuery = prev.queries[key], subscribers = [...prevQuery?.subscribers ?? [], subscriptionId];
4623
- return { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, subscribers } } };
4624
- }, removeSubscriber = (key, subscriptionId) => (prev) => {
4625
- const prevQuery = prev.queries[key];
4626
- if (!prevQuery) return prev;
4627
- const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId);
4628
- return subscribers.length ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, subscribers } } } : { ...prev, queries: omit(prev.queries, key) };
4629
- }, cancelQuery = (key) => (prev) => {
4630
- const prevQuery = prev.queries[key];
4631
- return !prevQuery || prevQuery.subscribers.length ? prev : { ...prev, queries: omit(prev.queries, key) };
4632
- }, initializeQuery = (key) => (prev) => prev.queries[key] ? prev : { ...prev, queries: { ...prev.queries, [key]: { subscribers: [] } } }, EMPTY_ARRAY = [], getQueryKey = (query, options = {}) => JSON.stringify({ query, options }), parseQueryKey = (key) => JSON.parse(key), queryStore = {
4633
- name: "QueryStore",
4634
- getInitialState: () => ({ queries: {} }),
4635
- initialize() {
4636
- const subscriptions = [
4637
- listenForNewSubscribersAndFetch(this),
4638
- listenToLiveClientAndSetLastLiveEventIds(this)
4639
- ];
4640
- return () => {
4641
- for (const subscription of subscriptions)
4642
- subscription.unsubscribe();
4643
- };
4644
- }
4645
- }, errorHandler = createInternalAction(({ state }) => function() {
4646
- return (error) => state.set("setError", { error });
4647
- }), listenForNewSubscribersAndFetch = createInternalAction(
4648
- ({ state, instance }) => function() {
4649
- return state.observable.pipe(
4650
- map((s2) => new Set(Object.keys(s2.queries))),
4651
- distinctUntilChanged((curr, next) => curr.size !== next.size ? !1 : Array.from(next).every((i2) => curr.has(i2))),
4652
- startWith$1(/* @__PURE__ */ new Set()),
4653
- pairwise$1(),
4654
- mergeMap$1(([curr, next]) => {
4655
- const added = Array.from(next).filter((i2) => !curr.has(i2)), removed = Array.from(curr).filter((i2) => !next.has(i2));
4656
- return [
4657
- ...added.map((key) => ({ key, added: !0 })),
4658
- ...removed.map((key) => ({ key, added: !1 }))
4659
- ];
4660
- }),
4661
- groupBy$1((i2) => i2.key),
4662
- mergeMap$1(
4663
- (group$) => group$.pipe(
4664
- switchMap((e3) => {
4665
- if (!e3.added) return EMPTY;
4666
- const lastLiveEventId$ = state.observable.pipe(
4667
- map((s2) => s2.queries[group$.key]?.lastLiveEventId),
4668
- distinctUntilChanged()
4669
- ), { query, options: { params, scope, ...options } = {} } = parseQueryKey(group$.key), client$ = getClientState(instance, { apiVersion: "vX", scope }).observable;
4670
- return combineLatest([lastLiveEventId$, client$]).pipe(
4671
- switchMap(
4672
- ([lastLiveEventId, client]) => client.observable.fetch(query, params, {
4673
- ...options,
4674
- filterResponse: !1,
4675
- returnQuery: !1,
4676
- lastLiveEventId
4677
- })
4678
- )
4679
- );
4680
- }),
4681
- catchError$1((error) => (state.set("setQueryError", setQueryError(group$.key, error)), EMPTY)),
4682
- tap$1(({ result, syncTags }) => {
4683
- state.set("setQueryData", setQueryData(group$.key, result, syncTags));
4684
- })
4685
- )
4686
- )
4687
- ).subscribe({ error: errorHandler(this) });
4885
+ }), getProjectsState = projects.getState, resolveProjects = projects.resolveState, API_VERSION = "vX", USERS_STATE_CLEAR_DELAY = 5e3, DEFAULT_USERS_BATCH_SIZE = 100, getUsersKey = (instance, {
4886
+ resourceType,
4887
+ organizationId,
4888
+ batchSize = DEFAULT_USERS_BATCH_SIZE,
4889
+ projectId = instance.config.projectId
4890
+ } = {}) => JSON.stringify({ resourceType, organizationId, batchSize, projectId }), parseUsersKey = (key) => JSON.parse(key), addSubscription = (subscriptionId, key) => (prev) => {
4891
+ const group = prev.users[key], subscriptions = [...group?.subscriptions ?? [], subscriptionId];
4892
+ return { ...prev, users: { ...prev.users, [key]: { ...group, subscriptions } } };
4893
+ }, removeSubscription = (subscriptionId, key) => (prev) => {
4894
+ const group = prev.users[key];
4895
+ if (!group) return prev;
4896
+ const subscriptions = group.subscriptions.filter((id) => id !== subscriptionId);
4897
+ return subscriptions.length ? { ...prev, users: { ...prev.users, [key]: { ...group, subscriptions } } } : { ...prev, users: omit(prev.users, key) };
4898
+ }, setUsersData = (key, { data, nextCursor, totalCount }) => (prev) => {
4899
+ const group = prev.users[key];
4900
+ if (!group) return prev;
4901
+ const users = [...group.users ?? [], ...data];
4902
+ return { ...prev, users: { ...prev.users, [key]: { ...group, users, totalCount, nextCursor } } };
4903
+ }, updateLastLoadMoreRequest = (timestamp, key) => (prev) => {
4904
+ const group = prev.users[key];
4905
+ return group ? { ...prev, users: { ...prev.users, [key]: { ...group, lastLoadMoreRequest: timestamp } } } : prev;
4906
+ }, setUsersError = (key, error) => (prev) => {
4907
+ const group = prev.users[key];
4908
+ return group ? { ...prev, users: { ...prev.users, [key]: { ...group, error } } } : prev;
4909
+ }, cancelRequest = (key) => (prev) => {
4910
+ const group = prev.users[key];
4911
+ return !group || group.subscriptions.length ? prev : { ...prev, users: omit(prev.users, key) };
4912
+ }, initializeRequest = (key) => (prev) => prev.users[key] ? prev : { ...prev, users: { ...prev.users, [key]: { subscriptions: [] } } }, usersStore = {
4913
+ name: "UsersStore",
4914
+ getInitialState: () => ({ users: {} }),
4915
+ initialize: (context) => {
4916
+ const subscription = listenForLoadMoreAndFetch(context);
4917
+ return () => subscription.unsubscribe();
4688
4918
  }
4689
- ), listenToLiveClientAndSetLastLiveEventIds = createInternalAction(
4690
- ({ state, instance }) => function() {
4691
- const liveMessages$ = getClientState(instance, { apiVersion: "vX" }).observable.pipe(
4692
- switchMap(
4693
- (client) => client.live.events({ includeDrafts: !!client.config().token, tag: "query-store" })
4694
- ),
4695
- share(),
4696
- filter((e3) => e3.type === "message")
4697
- );
4698
- return state.observable.pipe(
4699
- mergeMap$1((s2) => Object.entries(s2.queries)),
4700
- groupBy$1(([key]) => key),
4701
- mergeMap$1((group$) => {
4702
- const syncTags$ = group$.pipe(
4703
- map(([, queryState]) => queryState),
4704
- map((i2) => i2?.syncTags ?? EMPTY_ARRAY),
4919
+ }, errorHandler = (state) => (error) => state.set("setError", { error }), listenForLoadMoreAndFetch = ({ state, instance }) => state.observable.pipe(
4920
+ map((s2) => new Set(Object.keys(s2.users))),
4921
+ distinctUntilChanged((curr, next) => curr.size !== next.size ? !1 : Array.from(next).every((i2) => curr.has(i2))),
4922
+ startWith$1(/* @__PURE__ */ new Set()),
4923
+ pairwise$1(),
4924
+ mergeMap$1(([curr, next]) => {
4925
+ const added = Array.from(next).filter((i2) => !curr.has(i2)), removed = Array.from(curr).filter((i2) => !next.has(i2));
4926
+ return [
4927
+ ...added.map((key) => ({ key, added: !0 })),
4928
+ ...removed.map((key) => ({ key, added: !1 }))
4929
+ ];
4930
+ }),
4931
+ groupBy$1((i2) => i2.key),
4932
+ mergeMap$1(
4933
+ (group$) => group$.pipe(
4934
+ switchMap((e3) => {
4935
+ if (!e3.added) return EMPTY;
4936
+ const { batchSize, ...options } = parseUsersKey(group$.key), projectId = options.projectId ?? instance.config.projectId, resourceType = options.resourceType ?? (options.organizationId ? "organization" : projectId ? "project" : "organization"), organizationId$ = options.organizationId ? of(options.organizationId) : getDashboardOrganizationId(instance).observable.pipe(
4937
+ filter((i2) => typeof i2 == "string")
4938
+ ), resource$ = resourceType === "project" ? projectId ? of({ type: "project", id: projectId }) : throwError(() => new Error("Project ID required for this API.")) : organizationId$.pipe(map((id) => ({ type: "organization", id }))), client$ = getClientState(instance, {
4939
+ scope: "global",
4940
+ apiVersion: API_VERSION
4941
+ }).observable, loadMore$ = state.observable.pipe(
4942
+ map((s2) => s2.users[group$.key]?.lastLoadMoreRequest),
4705
4943
  distinctUntilChanged()
4944
+ ), cursor$ = state.observable.pipe(
4945
+ map((s2) => s2.users[group$.key]?.nextCursor),
4946
+ distinctUntilChanged(),
4947
+ filter((cursor) => cursor !== null)
4706
4948
  );
4707
- return combineLatest([liveMessages$, syncTags$]).pipe(
4708
- filter(([message, syncTags]) => message.tags.some((tag) => syncTags.includes(tag))),
4709
- tap$1(([message]) => {
4710
- state.set("setLastLiveEventId", setLastLiveEventId(group$.key, message.id));
4711
- })
4949
+ return combineLatest([resource$, client$, loadMore$]).pipe(
4950
+ withLatestFrom(cursor$),
4951
+ switchMap(
4952
+ ([[resource, client], cursor]) => client.observable.request({
4953
+ method: "GET",
4954
+ uri: `access/${resource.type}/${resource.id}/users`,
4955
+ query: cursor ? { nextCursor: cursor, limit: batchSize.toString() } : { limit: batchSize.toString() }
4956
+ })
4957
+ ),
4958
+ catchError$1((error) => (state.set("setUsersError", setUsersError(group$.key, error)), EMPTY)),
4959
+ tap$1((response) => state.set("setUsersData", setUsersData(group$.key, response)))
4712
4960
  );
4713
4961
  })
4714
- ).subscribe({ error: errorHandler(this) });
4715
- }
4716
- );
4717
- function getQueryState(...args) {
4718
- return _getQueryState(...args);
4719
- }
4720
- const _getQueryState = createStateSourceAction(queryStore, {
4721
- selector: (state, query, options) => {
4722
- if (state.error) throw state.error;
4723
- const key = getQueryKey(query, options), queryState = state.queries[key];
4724
- if (queryState?.error) throw queryState.error;
4725
- return queryState?.result;
4726
- },
4727
- onSubscribe: ({ state }, query, options) => {
4728
- const subscriptionId = insecureRandomId(), key = getQueryKey(query, options);
4729
- return state.set("addSubscriber", addSubscriber(key, subscriptionId)), () => {
4730
- setTimeout(
4731
- () => state.set("removeSubscriber", removeSubscriber(key, subscriptionId)),
4732
- QUERY_STATE_CLEAR_DELAY
4733
- );
4734
- };
4735
- }
4736
- });
4737
- function resolveQuery(...args) {
4738
- return _resolveQuery(...args);
4739
- }
4740
- const _resolveQuery = createAction(queryStore, ({ state }) => function(query, { signal, ...options } = {}) {
4741
- const { getCurrent } = getQueryState(this, query, options), key = getQueryKey(query, options), aborted$ = signal ? new Observable((observer) => {
4742
- const cleanup = () => {
4743
- signal.removeEventListener("abort", listener);
4744
- }, listener = () => {
4745
- observer.error(new DOMException("The operation was aborted.", "AbortError")), observer.complete(), cleanup();
4746
- };
4747
- return signal.addEventListener("abort", listener), cleanup;
4748
- }).pipe(
4749
- catchError$1((error) => {
4750
- throw error instanceof Error && error.name === "AbortError" && state.set("cancelQuery", cancelQuery(key)), error;
4751
- })
4752
- ) : NEVER;
4753
- state.set("initializeQuery", initializeQuery(key));
4754
- const resolved$ = state.observable.pipe(
4755
- map(getCurrent),
4756
- first$1((i2) => i2 !== void 0)
4757
- );
4758
- return firstValueFrom(race([resolved$, aborted$]));
4759
- });
4760
- function createStore(resource, actions) {
4761
- return function(dependencies) {
4762
- const instance = "instance" in dependencies ? dependencies.instance : dependencies, { state, dispose } = "state" in dependencies ? { state: dependencies.state, dispose: noop } : initializeResource(instance, resource), boundActions = Object.entries(actions).reduce((acc, [key, action]) => (acc[key] = action.bind(null, { state, instance }), acc), {});
4763
- return { dispose, ...boundActions };
4764
- };
4765
- }
4766
- const API_VERSION = "vX", usersStore = {
4767
- name: "users",
4768
- getInitialState: () => ({
4769
- users: [],
4770
- totalCount: 0,
4771
- nextCursor: null,
4772
- hasMore: !1,
4773
- initialFetchCompleted: !1,
4774
- options: {
4775
- resourceType: "",
4776
- resourceId: "",
4777
- limit: 100
4962
+ )
4963
+ )
4964
+ ).subscribe({ error: errorHandler(state) }), getUsersState = bindActionGlobally(
4965
+ usersStore,
4966
+ createStateSourceAction({
4967
+ selector: createSelector(
4968
+ [
4969
+ ({ instance, state }, options) => state.error ?? state.users[getUsersKey(instance, options)]?.error,
4970
+ ({ instance, state }, options) => state.users[getUsersKey(instance, options)]?.users,
4971
+ ({ instance, state }, options) => state.users[getUsersKey(instance, options)]?.totalCount,
4972
+ ({ instance, state }, options) => state.users[getUsersKey(instance, options)]?.nextCursor
4973
+ ],
4974
+ (error, data, totalCount, nextCursor) => {
4975
+ if (error) throw error;
4976
+ if (!(data === void 0 || totalCount === void 0 || nextCursor === void 0))
4977
+ return { data, totalCount, hasMore: nextCursor !== null };
4978
+ }
4979
+ ),
4980
+ onSubscribe: ({ instance, state }, options) => {
4981
+ const subscriptionId = insecureRandomId(), key = getUsersKey(instance, options);
4982
+ return state.set("addSubscription", addSubscription(subscriptionId, key)), () => {
4983
+ setTimeout(
4984
+ () => state.set("removeSubscription", removeSubscription(subscriptionId, key)),
4985
+ USERS_STATE_CLEAR_DELAY
4986
+ );
4987
+ };
4778
4988
  }
4779
- }),
4780
- initialize() {
4781
- return () => {
4782
- };
4989
+ })
4990
+ ), resolveUsers = bindActionGlobally(
4991
+ usersStore,
4992
+ async ({ state, instance }, { signal, ...options }) => {
4993
+ const key = getUsersKey(instance, options), { getCurrent } = getUsersState(instance, options), aborted$ = signal ? new Observable((observer) => {
4994
+ const cleanup = () => {
4995
+ signal.removeEventListener("abort", listener);
4996
+ }, listener = () => {
4997
+ observer.error(new DOMException("The operation was aborted.", "AbortError")), observer.complete(), cleanup();
4998
+ };
4999
+ return signal.addEventListener("abort", listener), cleanup;
5000
+ }).pipe(
5001
+ catchError$1((error) => {
5002
+ throw error instanceof Error && error.name === "AbortError" && state.set("cancelRequest", cancelRequest(key)), error;
5003
+ })
5004
+ ) : NEVER;
5005
+ state.set("initializeRequest", initializeRequest(key));
5006
+ const resolved$ = state.observable.pipe(
5007
+ map(getCurrent),
5008
+ first$1((i2) => i2 !== void 0)
5009
+ );
5010
+ return firstValueFrom(race([resolved$, aborted$]));
4783
5011
  }
4784
- }, getState = createStateSourceAction(
5012
+ ), loadMoreUsers = bindActionGlobally(
4785
5013
  usersStore,
4786
- createSelector(
4787
- [
4788
- (state) => state.users,
4789
- (state) => state.totalCount,
4790
- (state) => state.nextCursor,
4791
- (state) => state.hasMore,
4792
- (state) => state.initialFetchCompleted,
4793
- (state) => state.options
4794
- ],
4795
- (users, totalCount, nextCursor, hasMore, initialFetchCompleted, options) => ({
4796
- users,
4797
- totalCount,
4798
- nextCursor,
4799
- hasMore,
4800
- options,
4801
- initialFetchCompleted
4802
- })
4803
- )
4804
- ), fetchUsers = (instance, params) => {
4805
- const { resourceType, resourceId, nextCursor, limit = 100 } = params;
4806
- return getClient(instance, { scope: "global", apiVersion: API_VERSION }).request({
4807
- method: "GET",
4808
- uri: `access/${resourceType}/${resourceId}/users`,
4809
- query: nextCursor ? { nextCursor, limit: limit.toString() } : { limit: limit.toString() },
4810
- tag: "users"
4811
- });
4812
- }, loadMore = createAction(usersStore, ({ state, instance }) => async function() {
4813
- const { users, nextCursor, options } = state.get(), { resourceType, resourceId, limit } = options;
4814
- if (!resourceType || !resourceId)
4815
- throw new Error("Resource type and ID are required to load more users");
4816
- const response = await fetchUsers(instance, { resourceType, resourceId, nextCursor, limit }), allUsers = [...users, ...response.data], hasMore = allUsers.length < response.totalCount;
4817
- state.set("loadMore", {
4818
- users: allUsers,
4819
- totalCount: response.totalCount,
4820
- nextCursor: response.nextCursor,
4821
- hasMore
4822
- });
4823
- }), resolveUsers = createAction(usersStore, ({ state, instance }) => async function() {
4824
- const { options } = state.get(), { resourceType, resourceId, limit } = options;
4825
- if (!resourceType || !resourceId)
4826
- throw new Error("Resource type and ID are required to resolve users");
4827
- const response = await fetchUsers(instance, { resourceType, resourceId, limit }), hasMore = response.data.length < response.totalCount;
4828
- return state.set("resolveUsers", {
4829
- users: response.data,
4830
- totalCount: response.totalCount,
4831
- nextCursor: response.nextCursor,
4832
- hasMore,
4833
- initialFetchCompleted: !0
4834
- }), response;
4835
- }), setOptions = createAction(usersStore, ({ state }) => function(options) {
4836
- state.set("options", {
4837
- ...state.get(),
4838
- options: {
4839
- ...state.get().options,
4840
- resourceType: options.resourceType,
4841
- resourceId: options.resourceId
4842
- }
4843
- });
4844
- }), createUsersStore = createStore(usersStore, {
4845
- getState,
4846
- loadMore,
4847
- resolveUsers,
4848
- setOptions
4849
- });
4850
- var version = "0.0.0-chore-react-18-compat.1";
5014
+ async ({ state, instance }, options) => {
5015
+ const key = getUsersKey(instance, options), users = getUsersState(instance, options), usersState = users.getCurrent();
5016
+ if (!usersState)
5017
+ throw new Error("Users not loaded for specified resource. Please call resolveUsers first.");
5018
+ if (!usersState.hasMore)
5019
+ throw new Error("No more users available to load for this resource.");
5020
+ const promise = firstValueFrom(
5021
+ users.observable.pipe(
5022
+ filter((i2) => i2 !== void 0),
5023
+ skip(1)
5024
+ )
5025
+ ), timestamp = (/* @__PURE__ */ new Date()).toISOString();
5026
+ return state.set("updateLastLoadMoreRequest", updateLastLoadMoreRequest(timestamp, key)), await promise;
5027
+ }
5028
+ ), WILDCARD_TOKEN = "*", NEGATION_TOKEN = "-", TOKEN_REGEX = /(?:[^\s"]+|"[^"]*")+/g;
5029
+ function isNegationToken(token) {
5030
+ return typeof token < "u" && token.trim().startsWith(NEGATION_TOKEN);
5031
+ }
5032
+ function isPrefixToken(token) {
5033
+ return typeof token < "u" && token.trim().endsWith(WILDCARD_TOKEN);
5034
+ }
5035
+ function isExactMatchToken(token) {
5036
+ return !!token && token.length >= 2 && token.startsWith('"') && token.endsWith('"');
5037
+ }
5038
+ function createGroqSearchFilter(query) {
5039
+ const trimmedQuery = query.trim();
5040
+ if (!trimmedQuery)
5041
+ return "";
5042
+ const tokens = trimmedQuery.match(TOKEN_REGEX) ?? [], reversedIndex = [...tokens].reverse().findIndex(
5043
+ (token) => !isNegationToken(token) && !isExactMatchToken(token)
5044
+ ), finalIncrementalTokenIndex = reversedIndex === -1 ? -1 : tokens.length - 1 - reversedIndex, finalIncrementalToken = tokens[finalIncrementalTokenIndex], processedTokens = [...tokens];
5045
+ return finalIncrementalToken !== void 0 && !isPrefixToken(finalIncrementalToken) && processedTokens.splice(
5046
+ finalIncrementalTokenIndex,
5047
+ 1,
5048
+ `${finalIncrementalToken}${WILDCARD_TOKEN}`
5049
+ ), `[@] match text::query("${processedTokens.join(" ").replace(/"/g, '\\"')}")`;
5050
+ }
5051
+ var version = "0.0.0-chore-react-18-compat.3";
4851
5052
  const CORE_SDK_VERSION = getEnv("PKG_VERSION") || `${version}-development`;
4852
5053
  export {
4853
5054
  AuthStateType,
4854
5055
  CORE_SDK_VERSION,
4855
5056
  applyDocumentActions,
4856
5057
  createDocument,
5058
+ createGroqSearchFilter,
4857
5059
  createSanityInstance,
4858
- createUsersStore,
4859
5060
  deleteDocument,
4860
5061
  destroyController,
4861
5062
  discardDocument,
4862
5063
  editDocument,
4863
- fetchLoginUrls,
4864
5064
  getAuthState,
4865
5065
  getClient,
4866
5066
  getClientState,
@@ -4869,7 +5069,7 @@ export {
4869
5069
  getDatasetsState,
4870
5070
  getDocumentState,
4871
5071
  getDocumentSyncStatus,
4872
- getLoginUrlsState,
5072
+ getLoginUrlState,
4873
5073
  getOrCreateChannel,
4874
5074
  getOrCreateController,
4875
5075
  getOrCreateNode,
@@ -4880,12 +5080,15 @@ export {
4880
5080
  getProjectsState,
4881
5081
  getQueryKey,
4882
5082
  getQueryState,
4883
- getResourceId,
4884
5083
  getTokenState,
5084
+ getUsersKey,
5085
+ getUsersState,
4885
5086
  handleAuthCallback,
4886
5087
  jsonMatch,
5088
+ loadMoreUsers,
4887
5089
  logout,
4888
5090
  parseQueryKey,
5091
+ parseUsersKey,
4889
5092
  publishDocument,
4890
5093
  releaseChannel,
4891
5094
  releaseNode,
@@ -4897,6 +5100,7 @@ export {
4897
5100
  resolveProjection,
4898
5101
  resolveProjects,
4899
5102
  resolveQuery,
5103
+ resolveUsers,
4900
5104
  subscribeDocumentEvents,
4901
5105
  unpublishDocument
4902
5106
  };