@sanity/sdk 3.0.0-rc.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/_exports/_internal.d.ts +1 -1
  2. package/dist/_exports/_internal.js +2 -2
  3. package/dist/_exports/comlink.d.ts +1 -1
  4. package/dist/{authStore-DKFX8fn7.d.ts → authStore-C0o1NOxW.d.ts} +29 -43
  5. package/dist/authStore-C0o1NOxW.d.ts.map +1 -0
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js +131 -135
  8. package/dist/index.js.map +1 -1
  9. package/dist/{queryStore-DPf9sN3V.js → queryStore-C7yMHPUI.js} +2 -2
  10. package/dist/queryStore-C7yMHPUI.js.map +1 -0
  11. package/dist/{telemetryManager-Dq_qCkeI.js → telemetryManager-UIEiMtC0.js} +2 -2
  12. package/dist/{telemetryManager-Dq_qCkeI.js.map → telemetryManager-UIEiMtC0.js.map} +1 -1
  13. package/dist/{version-BChjsi4u.js → version-D8RP_Q59.js} +2 -2
  14. package/dist/{version-BChjsi4u.js.map → version-D8RP_Q59.js.map} +1 -1
  15. package/package.json +3 -3
  16. package/src/_exports/index.ts +7 -4
  17. package/src/datasets/datasets.test.ts +44 -9
  18. package/src/datasets/datasets.ts +24 -23
  19. package/src/document/documentConstants.ts +9 -0
  20. package/src/document/documentStore.test.ts +69 -0
  21. package/src/document/reducers.test.ts +224 -0
  22. package/src/document/reducers.ts +108 -25
  23. package/src/favorites/favorites.test.ts +64 -24
  24. package/src/favorites/favorites.ts +73 -22
  25. package/src/store/fetcherStore.test.ts +23 -1
  26. package/src/store/fetcherStore.ts +3 -3
  27. package/dist/authStore-DKFX8fn7.d.ts.map +0 -1
  28. package/dist/queryStore-DPf9sN3V.js.map +0 -1
  29. package/src/utils/createFetcherStore.test.ts +0 -171
  30. package/src/utils/createFetcherStore.ts +0 -284
package/dist/index.js CHANGED
@@ -1,22 +1,22 @@
1
1
  import { a as defineStore, c as isDatasetResource, i as bindActionGlobally, l as isMediaLibraryResource, n as bindActionByResource, o as isReleasePerspective, r as bindActionByResourceAndPerspective, s as isCanvasResource, t as createStateSourceAction } from "./createStateSourceAction-BOKtqFNm.js";
2
- import { A as getCommentsKey, B as getAddonDatasetState, D as addComment, E as toCommentsKeyParts, F as setCommentCreateError, G as insecureRandomId, H as COMMENTS_API_VERSION, I as setPendingTransaction, L as normalizeComment, M as removeCommentById, N as restoreComments, O as applyCommentUpdate, P as rollbackCommentUpdate, R as toCommentFieldPath, S as getCommentsState, T as resolveComments, U as defineFetcher, V as provisionAddonDataset, W as defineMutation, _ as PROJECT_API_VERSION, a as getPerspectiveState, b as getCommentThreadsState, c as addSubscription, d as initializeRequest, f as parseUsersKey, g as updateLastLoadMoreRequest, h as setUsersError, i as resolveQuery, j as receiveComment, k as clearPendingTransaction, l as cancelRequest, m as setUsersData, n as getQueryState, o as getActiveReleasesState, p as removeSubscription, s as getAllReleasesState, u as getUsersKey, v as USERS_STATE_CLEAR_DELAY, w as resolveCommentThreads, y as commentsStore, z as assertDatasetResource } from "./queryStore-DPf9sN3V.js";
2
+ import { A as getCommentsKey, B as getAddonDatasetState, D as addComment, E as toCommentsKeyParts, F as setCommentCreateError, G as insecureRandomId, H as COMMENTS_API_VERSION, I as setPendingTransaction, L as normalizeComment, M as removeCommentById, N as restoreComments, O as applyCommentUpdate, P as rollbackCommentUpdate, R as toCommentFieldPath, S as getCommentsState, T as resolveComments, U as defineFetcher, V as provisionAddonDataset, W as defineMutation, _ as PROJECT_API_VERSION, a as getPerspectiveState, b as getCommentThreadsState, c as addSubscription, d as initializeRequest, f as parseUsersKey, g as updateLastLoadMoreRequest, h as setUsersError, i as resolveQuery, j as receiveComment, k as clearPendingTransaction, l as cancelRequest, m as setUsersData, n as getQueryState, o as getActiveReleasesState, p as removeSubscription, s as getAllReleasesState, u as getUsersKey, v as USERS_STATE_CLEAR_DELAY, w as resolveCommentThreads, y as commentsStore, z as assertDatasetResource } from "./queryStore-C7yMHPUI.js";
3
3
  import { n as createLogger, t as configureLogging$1 } from "./logger-DAtJb5RQ.js";
4
4
  import { C as REQUEST_TAG_PREFIX, S as DEFAULT_API_VERSION, _ as getDefaultLocation, a as getCurrentUserState, b as AuthStateType, c as getLoginUrlState, d as createLoggedInAuthState, f as getAuthCode, i as getAuthState, l as getTokenState, o as getDashboardOrganizationId, p as getCleanedUrl, s as getIsInDashboardState, t as authStore, u as setAuthToken, v as getTokenFromLocation, w as getAuthLogger } from "./authStore-CtE9bLHW.js";
5
5
  import { r as omitProperty, t as isDeepEqual } from "./object-CxXlSVg7.js";
6
6
  import { n as getClient, r as getClientState } from "./clientStore-BtE2-q2i.js";
7
7
  import { n as setCleanupTimeout } from "./setCleanupTimeout-BiM6q1KF.js";
8
8
  import { t as getNodeState } from "./getNodeState-CR3__B2D.js";
9
- import { t as CORE_SDK_VERSION } from "./version-BChjsi4u.js";
9
+ import { t as CORE_SDK_VERSION } from "./version-D8RP_Q59.js";
10
10
  import { ClientError, CorsOriginError } from "@sanity/client";
11
- import { EMPTY, NEVER, Observable, Subject, Subscription, asapScheduler, auditTime, catchError, combineLatest, concat, concatMap, debounceTime, defer, distinctUntilChanged, filter, first, firstValueFrom, from, fromEvent, groupBy, map, merge, mergeMap, of, pairwise, partition, race, retry, share, shareReplay, skip, startWith, switchMap, take, takeUntil, tap, throttle, throwError, timer, withLatestFrom } from "rxjs";
11
+ import { EMPTY, NEVER, Observable, Subject, Subscription, auditTime, catchError, combineLatest, concat, concatMap, debounceTime, defer, distinctUntilChanged, filter, first, firstValueFrom, from, fromEvent, groupBy, map, merge, mergeMap, of, pairwise, partition, race, retry, share, shareReplay, skip, startWith, switchMap, take, takeUntil, tap, throttle, throwError, timer, withLatestFrom } from "rxjs";
12
12
  import { isKeySegment, isKeyedObject } from "@sanity/types";
13
13
  import { DocumentId, getDraftId, getPublishedId, getVersionId, isDraftId, isPublishedId, isVersionId } from "@sanity/id-utils";
14
14
  import { getIndexForKey, getIndexForKey as getIndexForKey$1, getPathDepth, joinPaths, jsonMatch, jsonMatch as jsonMatch$1, slicePath, slicePath as slicePath$1, stringifyPath, stringifyPath as stringifyPath$1 } from "@sanity/json-match";
15
- import { catchError as catchError$1, delay, distinctUntilChanged as distinctUntilChanged$1, filter as filter$1, first as first$1, groupBy as groupBy$1, map as map$1, mergeMap as mergeMap$1, pairwise as pairwise$1, retry as retry$1, scan, share as share$1, startWith as startWith$1, switchMap as switchMap$1, tap as tap$1 } from "rxjs/operators";
16
15
  import { SanityEncoder } from "@sanity/mutate";
17
16
  import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
18
17
  import "groq";
19
18
  import { evaluateSync, parse } from "groq-js";
19
+ import { catchError as catchError$1, distinctUntilChanged as distinctUntilChanged$1, map as map$1, mergeMap as mergeMap$1, retry as retry$1, scan, share as share$1, switchMap as switchMap$1 } from "rxjs/operators";
20
20
  import { applyPatches, parsePatch } from "@sanity/diff-match-patch";
21
21
  import { createSelector } from "reselect";
22
22
  import { diffValue } from "@sanity/diff-patch";
@@ -240,14 +240,14 @@ function normalizeProjectOptions(options) {
240
240
  includeFeatures: options?.includeFeatures ?? !0
241
241
  };
242
242
  }
243
- function resolveProjectId(instance, options) {
243
+ function resolveProjectId$1(instance, options) {
244
244
  let projectId = options?.projectId ?? instance.config.projectId;
245
245
  if (!projectId) throw Error("A projectId is required to use the project API.");
246
246
  return projectId;
247
247
  }
248
248
  /** @internal */
249
249
  function getProjectCacheKey(instance, options) {
250
- let projectId = resolveProjectId(instance, options), { includeMembers, includeFeatures } = normalizeProjectOptions(options);
250
+ let projectId = resolveProjectId$1(instance, options), { includeMembers, includeFeatures } = normalizeProjectOptions(options);
251
251
  return `project:${projectId}${includeMembers ? ":members" : ""}${includeFeatures ? ":features" : ""}`;
252
252
  }
253
253
  /**
@@ -265,7 +265,7 @@ const project = defineFetcher({
265
265
  apiVersion: "v2025-02-19",
266
266
  scope: "global"
267
267
  }).observable.pipe(switchMap((client) => client.observable.request({
268
- uri: `/projects/${resolveProjectId(instance, options)}`,
268
+ uri: `/projects/${resolveProjectId$1(instance, options)}`,
269
269
  query: buildQuery(normalizeProjectOptions(options)),
270
270
  tag: "projects.get"
271
271
  }))),
@@ -277,7 +277,7 @@ const project = defineFetcher({
277
277
  if (normalizeProjectOptions(options).includeMembers) return;
278
278
  let snapshot = projects.getState(instance).getCurrent();
279
279
  if (snapshot.status !== "success") return;
280
- let projectId = resolveProjectId(instance, options), match = snapshot.data.find((item) => item.id === projectId);
280
+ let projectId = resolveProjectId$1(instance, options), match = snapshot.data.find((item) => item.id === projectId);
281
281
  return match && {
282
282
  data: match,
283
283
  dataUpdatedAt: snapshot.dataUpdatedAt
@@ -768,117 +768,26 @@ function configureLogging(config) {
768
768
  source: "programmatic"
769
769
  });
770
770
  }
771
+ function resolveProjectId(instance, options) {
772
+ let projectId = options?.projectId ?? instance.config.projectId;
773
+ if (!projectId) throw Error("A projectId is required to use the datasets API.");
774
+ return projectId;
775
+ }
771
776
  /**
772
- * Creates a store from a function that returns an observable that fetches data
773
- * that supports parameterized state caching.
777
+ * Fetcher for a project's datasets (`GET /datasets`), on the shared fetcher
778
+ * cache.
774
779
  *
775
- * This function creates a resource store keyed by parameter values (using the
776
- * provided `getKey` function) and returns a state source (via `getState`)
777
- * that components can subscribe to. When a new subscription is added, and if
778
- * enough time has passed since the last fetch (controlled by
779
- * `fetchThrottleInternal`), it invokes the observable factory (via
780
- * `getObservable`) to fetch fresh data. The data is stored in state and can be
781
- * accessed reactively.
782
- *
783
- * Additionally, the store provides a `resolveState` function that returns a
784
- * Promise resolving with the next non-undefined value from the state source.
785
- *
786
- * State expiration is implemented: after the last subscription for a key is
787
- * removed, its state is cleared after `stateExpirationDelay` ms, causing
788
- * components to suspend until fresh data is fetched.
780
+ * @internal
789
781
  */
790
- function createFetcherStore({ name, fetcher: getObservable, getKey, fetchThrottleInternal = 1e3, stateExpirationDelay = 5e3 }) {
791
- let store = defineStore({
792
- name,
793
- getInitialState: () => ({ stateByParams: {} }),
794
- initialize: (context) => {
795
- let subscription = subscribeToSubscriptionsAndFetch(context);
796
- return () => subscription.unsubscribe();
797
- }
798
- }), subscribeToSubscriptionsAndFetch = ({ state }) => state.observable.pipe(switchMap$1((s) => {
799
- let entries = Object.entries(s.stateByParams);
800
- return entries.length > 0 ? from(entries) : EMPTY;
801
- }), groupBy$1(([key]) => key), mergeMap$1((group$) => group$.pipe(startWith$1([group$.key, void 0]), pairwise$1(), filter$1(([[, prevEntry], [, currEntry]]) => {
802
- let prevSubs = prevEntry?.subscriptions ?? [];
803
- return (currEntry?.subscriptions ?? []).length > prevSubs.length;
804
- }), map$1(([, [, currEntry]]) => currEntry), filter$1((entry) => {
805
- let lastFetch = entry?.lastFetchInitiatedAt;
806
- return !lastFetch || Date.now() - new Date(lastFetch).getTime() >= fetchThrottleInternal;
807
- }), switchMap$1((entry) => entry ? (state.set("setLastFetchInitiatedAt", (prev) => ({ stateByParams: {
808
- ...prev.stateByParams,
809
- [entry.key]: {
810
- ...entry,
811
- ...prev.stateByParams[entry.key],
812
- lastFetchInitiatedAt: (/* @__PURE__ */ new Date()).toISOString()
813
- }
814
- } })), getObservable(entry.instance)(...entry.params).pipe(delay(0, asapScheduler), tap$1((data) => state.set("setData", (prev) => ({ stateByParams: {
815
- ...prev.stateByParams,
816
- [entry.key]: {
817
- ...omitProperty(entry, "error"),
818
- ...prev.stateByParams[entry.key] ? omitProperty(prev.stateByParams[entry.key], "error") : {},
819
- data
820
- }
821
- } }))), catchError$1((error) => (state.set("setError", (prev) => ({ stateByParams: {
822
- ...prev.stateByParams,
823
- [entry.key]: {
824
- ...entry,
825
- ...prev.stateByParams[entry.key],
826
- error
827
- }
828
- } })), EMPTY)))) : EMPTY)))).subscribe({ error: (error) => state.set("setError", { error }) }), getState = bindActionGlobally(store, createStateSourceAction({
829
- selector: ({ instance, state: { stateByParams, error } }, ...params) => {
830
- if (error) throw error;
831
- let entry = stateByParams[getKey(instance, ...params)];
832
- if (entry?.error) throw entry.error;
833
- return entry?.data;
834
- },
835
- onSubscribe: ({ instance, state }, ...params) => {
836
- let subscriptionId = insecureRandomId(), key = getKey(instance, ...params);
837
- return state.set("addSubscription", (prev) => ({ stateByParams: {
838
- ...prev.stateByParams,
839
- [key]: {
840
- ...prev.stateByParams[key],
841
- instance,
842
- key,
843
- params: prev.stateByParams[key]?.params || params,
844
- subscriptions: [...prev.stateByParams[key]?.subscriptions || [], subscriptionId]
845
- }
846
- } })), () => {
847
- setCleanupTimeout(() => {
848
- state.set("removeSubscription", (prev) => {
849
- let entry = prev.stateByParams[key];
850
- if (!entry) return prev;
851
- let newSubs = (entry.subscriptions || []).filter((id) => id !== subscriptionId);
852
- return newSubs.length === 0 ? { stateByParams: omitProperty(prev.stateByParams, key) } : { stateByParams: {
853
- ...prev.stateByParams,
854
- [key]: {
855
- ...entry,
856
- subscriptions: newSubs
857
- }
858
- } };
859
- });
860
- }, stateExpirationDelay);
861
- };
862
- }
863
- }));
864
- return {
865
- getState,
866
- resolveState: bindActionGlobally(store, ({ instance }, ...params) => firstValueFrom(getState(instance, ...params).observable.pipe(first$1((i) => i !== void 0))))
867
- };
868
- }
869
- const datasets = createFetcherStore({
870
- name: "Datasets",
871
- getKey: (instance, options) => {
872
- let projectId = options?.projectId ?? instance.config.projectId;
873
- if (!projectId) throw Error("A projectId is required to use the project API.");
874
- return projectId;
875
- },
876
- fetcher: (instance) => (options) => getClientState(instance, {
782
+ const datasets = defineFetcher({
783
+ name: "datasets",
784
+ getKey: resolveProjectId,
785
+ fetch: (instance) => (options) => getClientState(instance, {
877
786
  apiVersion: "v2025-02-19",
878
- projectId: options?.projectId ?? instance.config.projectId,
787
+ projectId: resolveProjectId(instance, options),
879
788
  useProjectHostname: !0
880
789
  }).observable.pipe(switchMap((client) => client.observable.datasets.list()))
881
- }), getDatasetsState = datasets.getState, resolveDatasets = datasets.resolveState;
790
+ });
882
791
  /**
883
792
  * The id of the specific document a user is editing under a given perspective:
884
793
  * the draft, the published document, or a release version.
@@ -2717,7 +2626,8 @@ function cleanupOutgoingTransaction(prev) {
2717
2626
  for (let transactionId of outgoing.batchedTransactionIds) for (let documentId of ids) next = removeSubscriptionIdFromDocument(next, documentId, transactionId);
2718
2627
  return {
2719
2628
  ...next,
2720
- outgoing: void 0
2629
+ outgoing: void 0,
2630
+ documentStates: evictOrphanedDocumentStates(next.documentStates)
2721
2631
  };
2722
2632
  }
2723
2633
  function revertOutgoingTransaction(prev) {
@@ -2747,11 +2657,16 @@ function revertOutgoingTransaction(prev) {
2747
2657
  ...base,
2748
2658
  applied: nextApplied,
2749
2659
  outgoing: void 0,
2750
- documentStates: Object.fromEntries(Object.entries(base.documentStates).filter((e) => !!e[1]).map(([documentId, { unverifiedRevisions = {}, local, ...documentState }]) => [documentId, {
2751
- ...documentState,
2752
- local: documentId in working ? working[documentId] : local,
2753
- unverifiedRevisions: prev.outgoing && prev.outgoing.transactionId in unverifiedRevisions ? omitProperty(unverifiedRevisions, prev.outgoing.transactionId) : unverifiedRevisions
2754
- }]))
2660
+ documentStates: evictOrphanedDocumentStates(Object.fromEntries(Object.entries(base.documentStates).filter((e) => !!e[1]).map(([documentId, documentState]) => {
2661
+ let { unverifiedRevisions = {} } = documentState, next = {
2662
+ ...documentState,
2663
+ unverifiedRevisions: prev.outgoing && prev.outgoing.transactionId in unverifiedRevisions ? omitProperty(unverifiedRevisions, prev.outgoing.transactionId) : unverifiedRevisions
2664
+ };
2665
+ return !next.subscriptions.length && isAwaitingOwnEcho(next) ? [documentId, next] : [documentId, {
2666
+ ...next,
2667
+ local: documentId in working ? working[documentId] : next.local
2668
+ }];
2669
+ })))
2755
2670
  };
2756
2671
  }
2757
2672
  /**
@@ -2792,7 +2707,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
2792
2707
  let modifiesDocument = (transaction) => transaction.working[documentId]?._rev !== transaction.previousRevs[documentId], local = !prev.applied.some(modifiesDocument) && (!prev.outgoing || prev.outgoing.transactionId === revision || !modifiesDocument(prev.outgoing)) && !isDeepEqual(prevDocState.local, document) ? document : prevDocState.local;
2793
2708
  return {
2794
2709
  ...prev,
2795
- documentStates: {
2710
+ documentStates: evictOrphanedDocumentStates({
2796
2711
  ...prev.documentStates,
2797
2712
  [documentId]: {
2798
2713
  ...prevDocState,
@@ -2801,7 +2716,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
2801
2716
  local,
2802
2717
  unverifiedRevisions
2803
2718
  }
2804
- }
2719
+ })
2805
2720
  };
2806
2721
  }
2807
2722
  let working = {
@@ -2836,7 +2751,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
2836
2751
  return {
2837
2752
  ...prev,
2838
2753
  applied: nextApplied,
2839
- documentStates: {
2754
+ documentStates: evictOrphanedDocumentStates({
2840
2755
  ...prev.documentStates,
2841
2756
  [documentId]: {
2842
2757
  ...prevDocState,
@@ -2845,7 +2760,7 @@ function applyRemoteDocument(prev, { document, documentId, previousRev, revision
2845
2760
  local,
2846
2761
  unverifiedRevisions
2847
2762
  }
2848
- }
2763
+ })
2849
2764
  };
2850
2765
  }
2851
2766
  function addSubscriptionIdToDocument(prev, documentId, subscriptionId) {
@@ -2863,8 +2778,13 @@ function addSubscriptionIdToDocument(prev, documentId, subscriptionId) {
2863
2778
  };
2864
2779
  }
2865
2780
  function removeSubscriptionIdFromDocument(prev, documentId, subscriptionId) {
2866
- let prevDocState = prev.documentStates?.[documentId], subscriptions = (prevDocState?.subscriptions ?? []).filter((id) => id !== subscriptionId);
2867
- return prevDocState ? subscriptions.length ? {
2781
+ let prevDocState = prev.documentStates?.[documentId];
2782
+ if (!prevDocState) return prev;
2783
+ let subscriptions = prevDocState.subscriptions.filter((id) => id !== subscriptionId);
2784
+ return !subscriptions.length && !isAwaitingOwnEcho(prevDocState) ? {
2785
+ ...prev,
2786
+ documentStates: omitProperty(prev.documentStates, documentId)
2787
+ } : {
2868
2788
  ...prev,
2869
2789
  documentStates: {
2870
2790
  ...prev.documentStates,
@@ -2873,10 +2793,46 @@ function removeSubscriptionIdFromDocument(prev, documentId, subscriptionId) {
2873
2793
  subscriptions
2874
2794
  }
2875
2795
  }
2876
- } : {
2877
- ...prev,
2878
- documentStates: omitProperty(prev.documentStates, documentId)
2879
- } : prev;
2796
+ };
2797
+ }
2798
+ /**
2799
+ * Whether this document still expects the listener echo of one of its own
2800
+ * submitted transactions. That expectation is the only reason a document state
2801
+ * with no subscribers is kept around.
2802
+ *
2803
+ * Revisions stop counting after `UNVERIFIED_REVISION_RETENTION_TIME` because an
2804
+ * echo is not guaranteed to arrive at all: Content Lake records no transaction
2805
+ * when every operation resolves to a no-op (e.g. a keyed path that no longer
2806
+ * exists), while the client bumps `_rev` regardless and so tracks a revision
2807
+ * for it. Without the expiry, such a document (and the listener keyed off its
2808
+ * state) would be pinned for the lifetime of the store.
2809
+ *
2810
+ * `recentOwnTransactionIds` deliberately does not extend the wait: it is capped
2811
+ * rather than drained, so honoring it would keep every edited document alive.
2812
+ */
2813
+ function isAwaitingOwnEcho(documentState) {
2814
+ let expiresAfter = Date.now() - 3e4;
2815
+ return Object.values(documentState.unverifiedRevisions ?? {}).some((revision) => !!revision && new Date(revision.timestamp).getTime() > expiresAfter);
2816
+ }
2817
+ /**
2818
+ * Removes every document state that has no subscribers left and no own
2819
+ * transaction still awaiting its echo.
2820
+ *
2821
+ * This is a sweep rather than a decision made where the last subscriber leaves
2822
+ * because `removeSubscriptionIdFromDocument` defers that eviction while an echo
2823
+ * is in flight, and both halves of the condition can change without a
2824
+ * subscriber going anywhere: the echo can verify, be dropped by a sync, be
2825
+ * discarded by a revert, or simply never arrive. Every reducer that can settle
2826
+ * an in-flight transaction sweeps, so the deferred decision is always re-made.
2827
+ *
2828
+ * Note this can evict the very document whose remote event is being processed,
2829
+ * which synchronously unsubscribes that document's listener from inside its own
2830
+ * emission (see `subscribeToSubscriptionsAndListenToDocuments`).
2831
+ */
2832
+ function evictOrphanedDocumentStates(documentStates) {
2833
+ let next = documentStates;
2834
+ for (let [documentId, documentState] of Object.entries(documentStates)) documentState && (documentState.subscriptions.length || isAwaitingOwnEcho(documentState) || (next = omitProperty(next, documentId)));
2835
+ return next;
2880
2836
  }
2881
2837
  function manageSubscriberIds({ state }, handles) {
2882
2838
  let documentIds = getDocumentIdsFromHandleLikes(handles), subscriptionId = insecureRandomId();
@@ -3354,10 +3310,20 @@ async function _applyDocumentActions({ state }, { actions, resource, transaction
3354
3310
  function createFavoriteKey(context) {
3355
3311
  return `${context.documentId}:${context.documentType}:${context.resourceId}:${context.resourceType}${context.schemaName ? `:${context.schemaName}` : ""}`;
3356
3312
  }
3357
- const favorites = createFetcherStore({
3358
- name: "Favorites",
3313
+ /**
3314
+ * Fetcher for a document's favorite status, read from the dashboard over
3315
+ * comlink, on the shared fetcher cache.
3316
+ *
3317
+ * @internal
3318
+ */
3319
+ const favorites = defineFetcher({
3320
+ name: "favorites",
3359
3321
  getKey: (_instance, context) => createFavoriteKey(context),
3360
- fetcher: (instance) => (context) => {
3322
+ tags: (_data, context) => [{
3323
+ type: "favorite",
3324
+ id: createFavoriteKey(context)
3325
+ }],
3326
+ fetch: (instance) => (context) => {
3361
3327
  let nodeStateSource = getNodeState(instance, {
3362
3328
  name: SDK_NODE_NAME,
3363
3329
  connectTo: SDK_CHANNEL_NAME
@@ -3370,12 +3336,42 @@ const favorites = createFetcherStore({
3370
3336
  schemaName: context.schemaName
3371
3337
  }
3372
3338
  } };
3373
- return nodeStateSource.observable.pipe(filter((nodeState) => !!nodeState), shareReplay(1), switchMap((nodeState) => {
3339
+ return nodeStateSource.observable.pipe(first((nodeState) => !!nodeState), switchMap((nodeState) => {
3374
3340
  let node = nodeState.node;
3375
3341
  return from(node.fetch("dashboard/v1/events/favorite/query", payload)).pipe(map((response) => ({ isFavorited: response.isFavorited })), catchError((err) => (console.error("Favorites service connection error", err), of({ isFavorited: !1 }))));
3376
3342
  }));
3377
3343
  }
3378
- }), getFavoritesState = favorites.getState, resolveFavoritesState = favorites.resolveState;
3344
+ }), setFavorite = defineMutation({
3345
+ name: "setFavorite",
3346
+ mutationFn: (instance) => ({ isFavorited, ...context }) => {
3347
+ let nodeStateSource = getNodeState(instance, {
3348
+ name: SDK_NODE_NAME,
3349
+ connectTo: SDK_CHANNEL_NAME
3350
+ }), payload = {
3351
+ eventType: isFavorited ? "added" : "removed",
3352
+ document: {
3353
+ id: context.documentId,
3354
+ type: context.documentType,
3355
+ resource: {
3356
+ id: context.resourceId,
3357
+ type: context.resourceType,
3358
+ ...context.schemaName ? { schemaName: context.schemaName } : {}
3359
+ }
3360
+ }
3361
+ };
3362
+ return nodeStateSource.observable.pipe(first((nodeState) => !!nodeState), switchMap((nodeState) => {
3363
+ let node = nodeState.node;
3364
+ return from(node.fetch("dashboard/v1/events/favorite/mutate", payload)).pipe(map((response) => {
3365
+ if (!response.success) throw Error("Failed to update favorite status");
3366
+ return { isFavorited };
3367
+ }));
3368
+ }));
3369
+ },
3370
+ invalidates: (_result, input) => [{
3371
+ type: "favorite",
3372
+ id: createFavoriteKey(input)
3373
+ }]
3374
+ });
3379
3375
  /** The API takes `include` as a comma-separated list; sorted so key and query agree */
3380
3376
  function serializeInclude(include) {
3381
3377
  return include?.length ? [...include].sort().join(",") : void 0;
@@ -4273,6 +4269,6 @@ function isImportError(error) {
4273
4269
  let message = error.message || "";
4274
4270
  return /Loading chunk [\w-]+ failed/i.test(message) || /Failed to fetch dynamically imported module/i.test(message) || /error loading dynamically imported module/i.test(message) || /Import(?:ing)? a module script failed/i.test(message) || /Unable to preload CSS/i.test(message);
4275
4271
  }
4276
- export { AuthStateType, CORE_SDK_VERSION, application, applications, applyDocumentActions, archiveRelease, checkPermissions, configureLogging, createComment, createDatasetHandle, createDocument, createDocumentHandle, createDocumentTypeHandle, createProjectHandle, createRelease, createSanityInstance, deleteApplication, deleteDocument, deleteRelease, discardDocument, editDocument, editRelease, getActiveReleasesState, getAllReleasesState, getAuthState, getClient, getClientState, getCommentThreadsState, getCommentsState, getCorsErrorProjectId, getCurrentUserState, getDashboardOrganizationId, getDatasetsState, getDocumentPresence, getDocumentState, getDocumentSyncStatus, getFavoritesState, getIndexForKey, getIsInDashboardState, getLoginUrlState, getPathDepth, getPermissionsState, getPerspectiveState, getPresence, getProjectionState, getQueryState, getReleaseDocumentId, getTokenState, getUserState, getUsersState, handleAuthCallback, installation, installations, isCanvasResource, isDatasetResource, isImportError, isMediaLibraryResource, joinPaths, jsonMatch, loadMoreUsers, logout, observeOrganizationVerificationState, organization, organizations, project, projects, publishDocument, publishRelease, removeComment, replyToComment, reportPresence, resolveCommentThreads, resolveComments, resolveDatasets, resolveDocument, resolveFavoritesState, resolvePermissions, resolveProjection, resolveQuery, resolveUser, resolveUsers, scheduleRelease, setAuthToken, setCommentStatus, slicePath, stringifyPath, subscribeDocumentEvents, unarchiveRelease, unpublishDocument, unscheduleRelease, updateApplication, updateComment };
4272
+ export { AuthStateType, CORE_SDK_VERSION, application, applications, applyDocumentActions, archiveRelease, checkPermissions, configureLogging, createComment, createDatasetHandle, createDocument, createDocumentHandle, createDocumentTypeHandle, createProjectHandle, createRelease, createSanityInstance, datasets, deleteApplication, deleteDocument, deleteRelease, discardDocument, editDocument, editRelease, favorites, getActiveReleasesState, getAllReleasesState, getAuthState, getClient, getClientState, getCommentThreadsState, getCommentsState, getCorsErrorProjectId, getCurrentUserState, getDashboardOrganizationId, getDocumentPresence, getDocumentState, getDocumentSyncStatus, getIndexForKey, getIsInDashboardState, getLoginUrlState, getPathDepth, getPermissionsState, getPerspectiveState, getPresence, getProjectionState, getQueryState, getReleaseDocumentId, getTokenState, getUserState, getUsersState, handleAuthCallback, installation, installations, isCanvasResource, isDatasetResource, isImportError, isMediaLibraryResource, joinPaths, jsonMatch, loadMoreUsers, logout, observeOrganizationVerificationState, organization, organizations, project, projects, publishDocument, publishRelease, removeComment, replyToComment, reportPresence, resolveCommentThreads, resolveComments, resolveDocument, resolvePermissions, resolveProjection, resolveQuery, resolveUser, resolveUsers, scheduleRelease, setAuthToken, setCommentStatus, setFavorite, slicePath, stringifyPath, subscribeDocumentEvents, unarchiveRelease, unpublishDocument, unscheduleRelease, updateApplication, updateComment };
4277
4273
 
4278
4274
  //# sourceMappingURL=index.js.map