@sanity/sdk 3.4.0 → 3.5.0-next.20260922182803
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.
- package/dist/_exports/_internal.d.ts +3 -3
- package/dist/_exports/_internal.d.ts.map +1 -1
- package/dist/_exports/_internal.js +1 -1
- package/dist/_exports/_internal.js.map +1 -1
- package/dist/_exports/comlink.d.ts +1 -1
- package/dist/_exports/dashboard.d.ts +2 -2
- package/dist/{applications-Bb-0Kgkc.d.ts → applications-BKrtfXA2.d.ts} +5 -1
- package/dist/{applications-Bb-0Kgkc.d.ts.map → applications-BKrtfXA2.d.ts.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/topicStore-CPhGph6z.js.map +1 -1
- package/dist/{topicStore-fPK2CfqH.d.ts → topicStore-e_kQ8ZvS.d.ts} +22 -5
- package/dist/topicStore-e_kQ8ZvS.d.ts.map +1 -0
- package/dist/{version-C7m7OAy9.js → version-BKdIDRP1.js} +2 -2
- package/dist/{version-C7m7OAy9.js.map → version-BKdIDRP1.js.map} +1 -1
- package/package.json +1 -1
- package/src/_exports/dashboard.ts +1 -0
- package/src/applications/applicationOrigin.ts +3 -1
- package/src/dashboard/messageBus/topics.test.ts +9 -2
- package/src/dashboard/messageBus/topics.ts +20 -2
- package/src/installations/installations.ts +4 -0
- package/dist/topicStore-fPK2CfqH.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-C7m7OAy9.js","names":["addSubscriber","removeSubscriber","addSubscriber","removeSubscriber"],"sources":["../src/utils/ids.ts","../src/store/fetcherStore.ts","../src/comments/commentsConstants.ts","../src/comments/addonDatasetStore.ts","../src/comments/commentFieldPath.ts","../src/comments/buildCommentThreads.ts","../src/comments/normalizeComment.ts","../src/comments/observeComments.ts","../src/comments/reducers.ts","../src/comments/commentsStore.ts","../src/users/usersConstants.ts","../src/users/reducers.ts","../src/client/liveEvents.ts","../src/releases/observeReleases.ts","../src/releases/utils/sortReleases.ts","../src/releases/releasesStore.ts","../src/releases/getPerspectiveState.ts","../src/query/queryStoreConstants.ts","../src/query/reducers.ts","../src/query/queryStore.ts","../package.json","../src/version.ts"],"sourcesContent":["const ID_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n\n/**\n * Generates a random ID of the given length from the alphabet `[a-zA-Z0-9]`, the same shape\n * `@sanity/client` and Sanity Studio use for short IDs such as release IDs.\n *\n * Ported from `@sanity/client`'s `generateReleaseId`, which uses rejection sampling to keep\n * the output unbiased. Uses `crypto.getRandomValues`, which unlike `crypto.randomUUID` is\n * also available on insecure origins (plain http).\n *\n * @internal\n */\nexport function randomId(length: number = 8): string {\n let id = ''\n while (id.length < length) {\n const bytes = crypto.getRandomValues(new Uint8Array(length - id.length))\n for (const byte of bytes) {\n const index = byte & 63\n if (index < ID_ALPHABET.length) id += ID_ALPHABET.charAt(index)\n }\n }\n return id\n}\n\n/**\n * Generates a random v4 UUID.\n *\n * Prefers the native `crypto.randomUUID`, which is only available in secure contexts\n * (https, localhost). Falls back to deriving the UUID from `crypto.getRandomValues`,\n * which has no such restriction, so ID generation also works when an app is served over\n * plain http, e.g. when testing from a phone on a local network.\n *\n * @internal\n */\nexport function randomUuid(): string {\n // eslint-disable-next-line no-restricted-properties -- this is the sanctioned wrapper around crypto.randomUUID\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n\n const bytes = crypto.getRandomValues(new Uint8Array(16))\n const hex = Array.from(bytes, (byte, index) => {\n // Set the version (4) and variant (RFC 4122) bits\n if (index === 6) byte = (byte & 0x0f) | 0x40\n if (index === 8) byte = (byte & 0x3f) | 0x80\n return byte.toString(16).padStart(2, '0')\n }).join('')\n\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n","import {filter, firstValueFrom, map, type Observable, Subscription, take} from 'rxjs'\n\nimport {setCleanupInterval, setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {bindActionGlobally} from './createActionBinder'\nimport {type SanityInstance} from './createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from './createStateSourceAction'\nimport {type StoreState} from './createStoreState'\nimport {defineStore, type StoreContext} from './defineStore'\n\nconst DEFAULT_STALE_TIME = 30_000\nconst DEFAULT_GC_TIME = 300_000\n\n/**\n * Identifies cached data for invalidation, matched across the whole store.\n * A fetcher declares the tags each of its entries carries (via\n * {@link FetcherDefinition.tags}); a mutation declares the tags it invalidates\n * (via {@link MutationDefinition.invalidates}).\n *\n * When invalidating,`{type: 'organization'}` matches every entry carrying any `organization` tag;\n * `{type: 'organization', id: 'abc'}` matches only entries carrying that exact pair.\n * List entries carry a `{type, id: 'LIST'}` sentinel plus one tag per item, so a\n * \"create\" mutation can refetch lists without touching unrelated detail entries.\n *\n * @internal\n */\nexport interface CacheTag {\n type: string\n /** Specific id, or the `'LIST'` sentinel for list entries */\n id?: string | number\n}\n\n/**\n * The reactive value of one cache entry, as exposed by\n * {@link Fetcher.getState}. React hooks consume this envelope directly.\n * Discriminated on `status`, so narrowing gives correctly-typed `data`:\n * `'success'` is the only arm carrying data, and a background failure while\n * data still renders surfaces on the `'success'` arm's `error`.\n *\n * `isFetching` is common to all arms: a fetch for this entry is in flight\n * (initial load, revalidation, or poll).\n *\n * @internal\n */\nexport type FetcherSnapshot<TData> =\n | {\n /** No data yet — initial fetch in flight or not started */\n status: 'pending'\n data: undefined\n error: undefined\n isFetching: boolean\n dataUpdatedAt: undefined\n }\n | {\n /** Data available — possibly stale, possibly revalidating */\n status: 'success'\n data: TData\n /** Most recent background-fetch failure, cleared by the next successful fetch */\n error: unknown\n isFetching: boolean\n /** Epoch ms of the fetch that produced `data` */\n dataUpdatedAt: number\n }\n | {\n /** The last fetch failed and no data exists to serve */\n status: 'error'\n data: undefined\n error: unknown\n isFetching: boolean\n dataUpdatedAt: undefined\n }\n\n/**\n * A typed description of one cached, parameterised read of an API response —\n * here is a single cache per {@link SanityInstance}; every definition registers\n * into it and its `name` namespaces its keys. A list and a detail read are two\n * definitions over the one cache, not two caches.\n *\n * Entry lifecycle: fresh → stale → collected. Fresh entries are served from\n * cache with no fetch; stale entries are served immediately and revalidated in\n * the background (stale-while-revalidate); collected entries are removed once\n * their `gcTime` elapses after the last unsubscribe, so the next subscriber\n * suspends.\n *\n * @internal\n */\nexport interface FetcherDefinition<TParams extends unknown[], TData> {\n /** Namespaces this fetcher's keys in the shared cache; used for devtools and request-tag derivation */\n name: string\n /**\n * Fetches the data once. Return an observable that emits the result and\n * completes.\n */\n fetch: (instance: SanityInstance) => (...params: TParams) => Observable<TData>\n /** Converts params into the cache key that identifies an entry (unique within `name`) */\n getKey: (instance: SanityInstance, ...params: TParams) => string\n /**\n * Cache tags each entry carries, for cache-wide invalidation. A static array,\n * or a function of the fetched data when tags depend on the response (e.g. one\n * tag per list item).\n */\n tags?: CacheTag[] | ((data: TData, ...params: TParams) => CacheTag[])\n /**\n * How long fetched data is fresh, in ms. Fresh entries never refetch;\n * stale entries serve cached data and revalidate in the background.\n * @defaultValue 30_000\n */\n staleTime?: number\n /**\n * How long an entry survives after its last unsubscribe before it is garbage\n * collected, in ms. The clock starts at the last unsubscribe — entries with\n * active subscribers are never collected.\n * @defaultValue 300_000\n */\n gcTime?: number\n /**\n * Refetch on this interval (ms) while the entry has at least one subscriber,\n * independent of staleness. Ticks are skipped while `document.hidden`. Off by default.\n */\n refetchInterval?: number\n /**\n * Consulted once, when an entry is being created and no cached data exists for\n * it. Return data pulled from another fetcher in the same cache — read via its\n * ordinary `getState(instance, ...).getCurrent()`, e.g. a detail entry pulling\n * its item out of the cached list — to skip or soften the initial fetch.\n * `dataUpdatedAt` must be when the source data was fetched, not \"now\": staleness\n * is measured from it. Return `undefined` to fetch as normal.\n */\n initialData?: (\n instance: SanityInstance,\n ...params: TParams\n ) => {data: TData; dataUpdatedAt: number} | undefined\n}\n\n/**\n * A typed handle over the shared fetcher cache, returned by\n * {@link defineFetcher}. It is a view onto the entries under its definition's\n * `name`, not a cache of its own.\n *\n * @internal\n */\nexport interface Fetcher<TParams extends unknown[], TData> {\n /** Reactive envelope for one entry. Subscribing triggers fetching per staleness rules. */\n getState: (instance: SanityInstance, ...params: TParams) => StateSource<FetcherSnapshot<TData>>\n /**\n * Resolves with the entry's first available data — the promise a suspending\n * component throws. Rejects if the initial fetch fails.\n */\n resolveState: (instance: SanityInstance, ...params: TParams) => Promise<TData>\n /**\n * Marks the entry stale. If it has active subscribers it refetches immediately;\n * otherwise it refetches on next subscribe.\n */\n invalidate: (instance: SanityInstance, ...params: TParams) => void\n /** {@link Fetcher.invalidate} for every entry under this fetcher's `name`. */\n invalidateAll: (instance: SanityInstance) => void\n /**\n * Imperative fetch, bypassing staleness. Dedupes against an in-flight fetch.\n * Resolves with the entry's data as held by the cache once the fetch settles.\n */\n refetch: (instance: SanityInstance, ...params: TParams) => Promise<TData>\n /**\n * Writes an entry's data directly. Returns an undo handle — inside a\n * mutation's `onMutate` these writes are recorded and rolled back automatically\n * on failure.\n */\n setData: (\n instance: SanityInstance,\n params: TParams,\n updater: TData | ((current: TData | undefined) => TData),\n ) => {undo: () => void}\n}\n\n/**\n * Writes an entry's data in any fetcher. Passed to {@link MutationDefinition}\n * callbacks — mutations are the only holders of arbitrary write access; read\n * fetchers pull their data (see {@link FetcherDefinition.initialData}). Inside\n * `onMutate`, every write is recorded and undone automatically if the mutation\n * fails.\n *\n * @internal\n */\nexport type CacheWriter = <TParams extends unknown[], TData>(\n fetcher: Fetcher<TParams, TData>,\n params: TParams,\n updater: TData | ((current: TData | undefined) => TData),\n) => void\n\n/**\n * Resolves when the mutation settles on the server. `invalidated` tracks the\n * background reconciliation — the refetches triggered by\n * {@link MutationDefinition.invalidates} — for the rare caller that must wait for\n * full reconvergence rather than server settlement.\n *\n * @internal\n */\nexport interface MutationResult<TResult> {\n /** The server response */\n data: TResult\n /** Settles when the refetches of matched active entries complete */\n invalidated: Promise<void>\n}\n\n/**\n * Defines a mutation with optimistic cache updates. Lifecycle:\n *\n * 1. `onMutate` applies optimistic writes synchronously — the UI updates before\n * the request leaves. Each entry carries a version counter that increments on\n * every write. An optimistic write bumps the counter, so a revalidation already\n * in flight lands \"out of date\" and is discarded rather than clobbering the\n * optimistic write.\n * 2. The request runs. On failure, recorded writes are rolled back automatically\n * — the cache is restored exactly, so failure ends the lifecycle here.\n * 3. On success, `onSuccess` may write the server response into the cache directly.\n * 4. On success, `invalidates` tags mark matching entries across the cache\n * stale (active entries refetch), so the cache reconverges on server truth\n * even if the optimistic shape was wrong.\n *\n * @internal\n */\nexport interface MutationDefinition<TInput, TResult> {\n /** Unique mutation name, used for devtools and request-tag derivation */\n name: string\n /** Creates the observable that performs the server mutation */\n mutationFn: (instance: SanityInstance) => (input: TInput) => Observable<TResult>\n /** Optimistic cache writes, applied synchronously before the request */\n onMutate?: (write: CacheWriter, input: TInput) => void\n /** Reconcile the cache with the server response, avoiding a refetch round-trip */\n onSuccess?: (write: CacheWriter, result: TResult, input: TInput) => void\n /**\n * Cache tags to invalidate on success. Failure invalidates nothing: recorded\n * optimistic writes roll back to the exact pre-mutation cache, and any\n * server-side ambiguity (e.g. timeout after commit) heals via `staleTime`.\n * A static array, or a function of the result/input.\n */\n invalidates?: CacheTag[] | ((result: TResult, input: TInput) => CacheTag[])\n}\n\n// --- Internal cache machinery -------------------------------------------------\n\n/** Internal bookkeeping carried by every entry, on top of the public {@link FetcherSnapshot} fields. */\ninterface CacheEntryMeta {\n fetcherName: string\n /** The entry's cache key — unique across the whole cache */\n key: string\n params: unknown[]\n instance: SanityInstance\n /** Bumps on every write; a fetch whose result lands on a changed version is discarded */\n version: number\n /** Forced stale by invalidation, independent of `staleTime` */\n forcedStale: boolean\n tags: CacheTag[]\n subscriptions: number\n}\n\n/**\n * One entry in the shared cache: a {@link FetcherSnapshot} plus internal\n * bookkeeping. Discriminated on `status` like the snapshot, so `data` and\n * `dataUpdatedAt` are correctly typed per status. Generic over the fetcher's\n * `TData`; the store holds `CacheEntry` (i.e. `CacheEntry<unknown>`) because it\n * is shared across fetchers, and {@link asTyped} recovers a specific `TData` at\n * the read boundary. Immutable: replaced, never mutated in place.\n */\ntype CacheEntry<TData = unknown> = FetcherSnapshot<TData> & CacheEntryMeta\n\ninterface CacheState {\n entries: {[compoundKey: string]: CacheEntry}\n}\n\ninterface InflightFetch {\n sub: Subscription\n promise: Promise<unknown>\n resolve: (data: unknown) => void\n reject: (error: unknown) => void\n startVersion: number\n}\n\n// Erased definition type used by the machinery, which is generic-agnostic.\ntype AnyDefinition = FetcherDefinition<unknown[], unknown>\n\n// Module-level: the cache store is global (one instance across all Sanity\n// instances, keyed only by fetcher key), so this bookkeeping lives alongside it.\nconst definitions = new Map<string, AnyDefinition>()\nconst inflight = new Map<string, InflightFetch>()\nconst gcTimers = new Map<string, ReturnType<typeof setTimeout>>()\nconst intervalTimers = new Map<string, ReturnType<typeof setInterval>>()\nconst snapshotCache = new WeakMap<CacheEntry, FetcherSnapshot<unknown>>()\n\nconst PENDING_SNAPSHOT: FetcherSnapshot<never> = {\n status: 'pending',\n data: undefined,\n error: undefined,\n isFetching: false,\n dataUpdatedAt: undefined,\n}\n\n/** Builds the cache key that identifies one entry: the fetcher's name namespacing its raw key. */\nconst entryKey = (fetcherName: string, rawKey: string): string => `${fetcherName}:${rawKey}`\n\nconst computeTags = (def: AnyDefinition, data: unknown, params: unknown[]): CacheTag[] =>\n typeof def.tags === 'function' ? def.tags(data, ...params) : (def.tags ?? [])\n\n/**\n * Distinguishes an updater function from a replacement value. Sound because\n * cached data is API response data — never itself callable — so a callable\n * `updater` can only be the function arm. This lets us narrow without an `as`.\n */\nconst isUpdaterFn = <T>(\n updater: T | ((current: T | undefined) => T),\n): updater is (current: T | undefined) => T => typeof updater === 'function'\n\n/** Applies an updater — a replacement value, or a function of the current value. */\nconst applyUpdater = <T>(updater: T | ((current: T | undefined) => T), current: T | undefined): T =>\n isUpdaterFn(updater) ? updater(current) : updater\n\n/** Whether an entry should fetch on subscribe: not already fetching, and either empty or stale. */\nconst needsFetch = (entry: CacheEntry, def: AnyDefinition): boolean => {\n if (entry.isFetching) return false\n if (entry.status !== 'success') return true\n return (\n entry.forcedStale ||\n Date.now() - (entry.dataUpdatedAt ?? 0) >= (def.staleTime ?? DEFAULT_STALE_TIME)\n )\n}\n\n/**\n * Views a stored entry as the calling fetcher's typed entry. The cache holds\n * every fetcher's entries as `CacheEntry<unknown>`, but keys are namespaced by\n * fetcher name, so an entry read under a fetcher's key holds that fetcher's\n * `TData` — the one sound `unknown → TData` seam (cf. TanStack's `getQueryData`).\n */\nconst asTyped = <TData>(entry: CacheEntry): CacheEntry<TData> => entry as CacheEntry<TData>\n\n/** Snapshot for a given entry, memoised by entry identity so unrelated cache changes don't re-emit. */\nconst toSnapshot = <TData>(entry: CacheEntry<TData> | undefined): FetcherSnapshot<TData> => {\n if (!entry) return PENDING_SNAPSHOT\n const cached = snapshotCache.get(entry)\n if (cached) return cached as FetcherSnapshot<TData>\n\n let snapshot: FetcherSnapshot<TData>\n if (entry.status === 'success') {\n snapshot = {\n status: 'success',\n data: entry.data,\n error: entry.error,\n isFetching: entry.isFetching,\n dataUpdatedAt: entry.dataUpdatedAt,\n }\n } else if (entry.status === 'error') {\n snapshot = {\n status: 'error',\n data: undefined,\n error: entry.error,\n isFetching: entry.isFetching,\n dataUpdatedAt: undefined,\n }\n } else {\n snapshot = {\n status: 'pending',\n data: undefined,\n error: undefined,\n isFetching: entry.isFetching,\n dataUpdatedAt: undefined,\n }\n }\n\n snapshotCache.set(entry, snapshot)\n return snapshot\n}\n\nconst cacheStore = defineStore<CacheState>({\n name: 'FetcherCache',\n getInitialState: () => ({entries: {}}),\n initialize: () => () => {\n // The global store is disposed only when the last Sanity instance is: tear\n // down every outstanding timer and fetch so nothing leaks between lifetimes.\n for (const timer of gcTimers.values()) clearTimeout(timer)\n for (const timer of intervalTimers.values()) clearInterval(timer)\n for (const fetch of inflight.values()) fetch.sub.unsubscribe()\n gcTimers.clear()\n intervalTimers.clear()\n inflight.clear()\n },\n})\n\n/** Immutably replace (or delete, when the updater returns `undefined`) one entry. */\nconst updateEntry = (\n state: StoreState<CacheState>,\n key: string,\n actionName: string,\n updater: (prev: CacheEntry | undefined) => CacheEntry | undefined,\n): void => {\n state.set(actionName, (prev: CacheState) => {\n const next = updater(prev.entries[key])\n if (next === prev.entries[key]) return prev\n const entries = {...prev.entries}\n if (next === undefined) delete entries[key]\n else entries[key] = next\n return {entries}\n })\n}\n\nconst baseEntry = (\n def: AnyDefinition,\n key: string,\n instance: SanityInstance,\n params: unknown[],\n): CacheEntry => {\n const seeded = def.initialData?.(instance, ...params)\n if (seeded) {\n return {\n fetcherName: def.name,\n key,\n params,\n instance,\n status: 'success',\n data: seeded.data,\n error: undefined,\n dataUpdatedAt: seeded.dataUpdatedAt,\n isFetching: false,\n version: 0,\n forcedStale: false,\n tags: computeTags(def, seeded.data, params),\n subscriptions: 0,\n }\n }\n return {\n fetcherName: def.name,\n key,\n params,\n instance,\n status: 'pending',\n data: undefined,\n error: undefined,\n dataUpdatedAt: undefined,\n isFetching: false,\n version: 0,\n forcedStale: false,\n tags: [],\n subscriptions: 0,\n }\n}\n\n/**\n * Runs (or dedupes/restarts) the fetch for one entry, wiring the result back\n * into the cache. Returns a promise resolving with the entry's data once the\n * fetch settles — rejecting only when the entry has no data to serve.\n */\nconst startFetch = (\n state: StoreState<CacheState>,\n def: AnyDefinition,\n instance: SanityInstance,\n params: unknown[],\n force: boolean,\n): Promise<unknown> => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n\n const existing = inflight.get(key)\n if (existing && !force) return existing.promise\n if (existing) {\n existing.sub.unsubscribe()\n inflight.delete(key)\n }\n\n updateEntry(state, key, 'fetchStart', (prev) => ({\n ...(prev ?? baseEntry(def, key, instance, params)),\n isFetching: true,\n }))\n const startVersion = state.get().entries[key]?.version ?? 0\n\n let resolve!: (data: unknown) => void\n let reject!: (error: unknown) => void\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res\n reject = rej\n })\n\n // A forced fetch pre-empted one already in flight: forward this fetch's\n // settlement to the pre-empted promise so its awaiters don't hang forever.\n if (existing) void promise.then(existing.resolve, existing.reject)\n\n // Register before subscribing so a synchronous observable (whose `next`/\n // `complete` fire during `.subscribe()`) sees — and can clean up — its own record.\n const sub = new Subscription()\n inflight.set(key, {sub, promise, resolve, reject, startVersion})\n\n sub.add(\n def\n .fetch(instance)(...params)\n .pipe(take(1))\n .subscribe({\n next: (data) => {\n const current = state.get().entries[key]\n if (current && current.version !== startVersion) {\n // A write (optimistic mutation) landed mid-fetch: discard the now-stale\n // server result and keep the written data. Tear the fetch down here —\n // a non-completing source won't fire `complete` to clear `isFetching`.\n inflight.delete(key)\n if (current.isFetching) {\n updateEntry(\n state,\n key,\n 'fetchDiscard',\n (prev) => prev && {...prev, isFetching: false},\n )\n }\n resolve(current.data)\n sub.unsubscribe()\n return\n }\n updateEntry(state, key, 'fetchSuccess', (prev) => ({\n ...(prev ?? baseEntry(def, key, instance, params)),\n status: 'success',\n data,\n error: undefined,\n dataUpdatedAt: Date.now(),\n isFetching: false,\n forcedStale: false,\n tags: computeTags(def, data, params),\n }))\n resolve(data)\n },\n error: (error) => {\n updateEntry(state, key, 'fetchError', (prev) => {\n if (!prev) return prev\n // Keep rendering data if we have it (background failure); otherwise surface as error.\n if (prev.status === 'success') return {...prev, error, isFetching: false}\n return {...prev, status: 'error', error, isFetching: false}\n })\n inflight.delete(key)\n reject(error)\n },\n complete: () => {\n inflight.delete(key)\n const current = state.get().entries[key]\n if (current?.isFetching) {\n updateEntry(state, key, 'fetchSettle', (prev) => prev && {...prev, isFetching: false})\n }\n },\n }),\n )\n\n return promise\n}\n\n/**\n * Trigger a fetch without awaiting it. The failure is already reflected in the\n * entry's snapshot, so swallow the promise rejection to avoid an unhandled one.\n */\nconst detach = (promise: Promise<unknown>): void => {\n void promise.catch(() => {})\n}\n\nconst markStale = (state: StoreState<CacheState>, key: string): void =>\n updateEntry(state, key, 'markStale', (prev) => prev && {...prev, forcedStale: true})\n\n/** Polls an entry on `refetchInterval` while it has subscribers, skipping ticks while the tab is hidden. */\nconst startInterval = (\n state: StoreState<CacheState>,\n def: AnyDefinition,\n instance: SanityInstance,\n params: unknown[],\n key: string,\n): void => {\n if (!def.refetchInterval || intervalTimers.has(key)) return\n const timer = setCleanupInterval(() => {\n if (typeof document !== 'undefined' && document.hidden) return\n const entry = state.get().entries[key]\n if (entry && entry.subscriptions > 0) detach(startFetch(state, def, instance, params, true))\n }, def.refetchInterval)\n intervalTimers.set(key, timer)\n}\n\n/**\n * Schedules an unsubscribed entry for garbage collection: after `gcTime` (from\n * now) its in-flight fetch is torn down and the entry removed. No-op if the\n * entry still has subscribers or a GC timer is already pending. Called from\n * every path that can leave an entry at zero subscribers — the unsubscribe\n * teardown, and the imperative `refetch`/`setData` writes that create entries\n * without ever subscribing. A later subscribe cancels the pending timer.\n */\nconst scheduleGc = (state: StoreState<CacheState>, def: AnyDefinition, key: string): void => {\n if (gcTimers.has(key)) return\n const entry = state.get().entries[key]\n if (!entry || entry.subscriptions > 0) return\n const timer = setCleanupTimeout(() => {\n gcTimers.delete(key)\n const fetch = inflight.get(key)\n if (fetch) {\n fetch.sub.unsubscribe()\n inflight.delete(key)\n }\n updateEntry(state, key, 'gc', () => undefined)\n }, def.gcTime ?? DEFAULT_GC_TIME)\n gcTimers.set(key, timer)\n}\n\n/** Shared across all fetchers: scans the whole cache and revalidates entries carrying any matched tag. */\nconst invalidateByTags = bindActionGlobally(\n cacheStore,\n ({state}: StoreContext<CacheState>, targets: CacheTag[]): Promise<void> => {\n const refetches: Promise<unknown>[] = []\n for (const [key, entry] of Object.entries(state.get().entries)) {\n const matches = entry.tags.some((tag) =>\n targets.some(\n (target) => tag.type === target.type && (target.id === undefined || tag.id === target.id),\n ),\n )\n if (!matches) continue\n markStale(state, key)\n if (entry.subscriptions <= 0) continue\n const def = definitions.get(entry.fetcherName)\n // Reconciliation is best-effort: a failed refetch surfaces on its snapshot,\n // and must not reject the caller's `invalidated` promise.\n if (def)\n refetches.push(\n startFetch(state, def, entry.instance, entry.params, true).catch(() => undefined),\n )\n }\n return Promise.all(refetches).then(() => undefined)\n },\n)\n\n/**\n * Registers a fetcher definition against the shared cache and returns its typed\n * handle.\n *\n * @internal\n */\nexport function defineFetcher<TParams extends unknown[], TData>(\n definition: FetcherDefinition<TParams, TData>,\n): Fetcher<TParams, TData> {\n const def = definition as unknown as AnyDefinition\n definitions.set(definition.name, def)\n\n const getState = bindActionGlobally(\n cacheStore,\n createStateSourceAction<CacheState, TParams, FetcherSnapshot<TData>>({\n selector: ({state, instance}: SelectorContext<CacheState>, ...params: TParams) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n const entry = state.entries[key]\n return toSnapshot(entry && asTyped<TData>(entry))\n },\n onSubscribe: ({state, instance}: StoreContext<CacheState>, ...params: TParams) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n\n const pendingGc = gcTimers.get(key)\n if (pendingGc) {\n clearTimeout(pendingGc)\n gcTimers.delete(key)\n }\n\n updateEntry(state, key, 'subscribe', (prev) => ({\n ...(prev ?? baseEntry(def, key, instance, params)),\n instance,\n subscriptions: (prev?.subscriptions ?? 0) + 1,\n }))\n startInterval(state, def, instance, params, key)\n\n // Defer the fetch decision off the subscribe call stack to avoid a\n // re-entrant cache write during subscription setup.\n queueMicrotask(() => {\n const entry = state.get().entries[key]\n if (entry && entry.subscriptions > 0 && needsFetch(entry, def)) {\n detach(startFetch(state, def, instance, params, false))\n }\n })\n\n return () => {\n updateEntry(\n state,\n key,\n 'unsubscribe',\n (prev) => prev && {...prev, subscriptions: Math.max(0, prev.subscriptions - 1)},\n )\n const entry = state.get().entries[key]\n if (!entry || entry.subscriptions > 0) return\n\n const interval = intervalTimers.get(key)\n if (interval) {\n clearInterval(interval)\n intervalTimers.delete(key)\n }\n scheduleGc(state, def, key)\n }\n },\n }),\n )\n\n const resolveState = (instance: SanityInstance, ...params: TParams): Promise<TData> =>\n firstValueFrom(\n getState(instance, ...params).observable.pipe(\n filter(\n (snapshot): snapshot is Exclude<FetcherSnapshot<TData>, {status: 'pending'}> =>\n snapshot.status !== 'pending',\n ),\n map((snapshot) => {\n if (snapshot.status === 'error') throw snapshot.error\n return snapshot.data\n }),\n ),\n )\n\n const invalidate = bindActionGlobally(\n cacheStore,\n ({state, instance}: StoreContext<CacheState>, ...params: TParams) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n markStale(state, key)\n const entry = state.get().entries[key]\n if (entry && entry.subscriptions > 0) detach(startFetch(state, def, instance, params, true))\n },\n )\n\n const invalidateAll = bindActionGlobally(cacheStore, ({state}: StoreContext<CacheState>) => {\n const prefix = `${def.name}:`\n for (const [key, entry] of Object.entries(state.get().entries)) {\n if (!key.startsWith(prefix)) continue\n markStale(state, key)\n if (entry.subscriptions > 0)\n detach(startFetch(state, def, entry.instance, entry.params, true))\n }\n })\n\n const refetch = bindActionGlobally(\n cacheStore,\n ({state, instance}: StoreContext<CacheState>, ...params: TParams): Promise<TData> => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n const promise = startFetch(state, def, instance, params, true) as Promise<TData>\n scheduleGc(state, def, key)\n return promise\n },\n )\n\n const setData = bindActionGlobally(\n cacheStore,\n (\n {state, instance}: StoreContext<CacheState>,\n params: TParams,\n updater: TData | ((current: TData | undefined) => TData),\n ) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n let previous: CacheEntry | undefined\n updateEntry(state, key, 'setData', (prev) => {\n previous = prev\n const from = asTyped<TData>(prev ?? baseEntry(def, key, instance, params))\n const nextData = applyUpdater(updater, from.data)\n return {\n ...from,\n status: 'success',\n data: nextData,\n dataUpdatedAt: from.dataUpdatedAt ?? Date.now(),\n version: from.version + 1,\n tags: computeTags(def, nextData, params),\n }\n })\n scheduleGc(state, def, key)\n return {undo: () => updateEntry(state, key, 'undoSetData', () => previous)}\n },\n )\n\n return {getState, resolveState, invalidate, invalidateAll, refetch, setData}\n}\n\n/**\n * Creates a bound mutation from its definition. The returned action resolves at\n * server settlement — not after invalidation refetches, which run as background\n * reconciliation (see {@link MutationResult.invalidated}).\n *\n * @internal\n */\nexport function defineMutation<TInput, TResult>(\n definition: MutationDefinition<TInput, TResult>,\n): (instance: SanityInstance, input: TInput) => Promise<MutationResult<TResult>> {\n return (instance, input) => {\n const undos: Array<() => void> = []\n const write: CacheWriter = (fetcher, params, updater) => {\n undos.push(fetcher.setData(instance, params, updater).undo)\n }\n\n definition.onMutate?.(write, input)\n\n return firstValueFrom(definition.mutationFn(instance)(input)).then(\n (result) => {\n definition.onSuccess?.(write, result, input)\n const targets =\n typeof definition.invalidates === 'function'\n ? definition.invalidates(result, input)\n : (definition.invalidates ?? [])\n const invalidated = targets.length ? invalidateByTags(instance, targets) : Promise.resolve()\n return {data: result, invalidated}\n },\n (error) => {\n for (let i = undos.length - 1; i >= 0; i--) undos[i]()\n throw error\n },\n )\n }\n}\n","import {type ListenOptions} from '@sanity/client'\n\n/** Data API version used for reading and writing comments in the addon dataset. */\nexport const COMMENTS_API_VERSION = 'v2025-05-06'\n\n/** Projects API version used to discover and provision the addon dataset. */\nexport const ADDON_DATASET_API_VERSION = 'v2025-02-19'\n\n/**\n * The dataset profile the Studio tags its comments dataset with. Discovery\n * matches on it, so changing it stops the SDK and the Studio sharing comments.\n */\nexport const ADDON_DATASET_PROFILE = 'comments'\n\n/**\n * How long a comment list outlives its last subscriber before being dropped.\n *\n * Long enough that navigating away from a document and straight back reuses the\n * loaded list instead of refetching and re-suspending.\n */\nexport const COMMENTS_STATE_CLEAR_DELAY = 5000\n\n/**\n * Kept identical to the Studio's own listener options\n * (`packages/sanity/src/core/comments/store/useCommentsStore.ts:29-35`), apart\n * from the request tag. `includeResult` is what lets a mutation event carry the\n * changed comment, which is what makes per-comment reconciliation possible\n * rather than refetching the list.\n */\nexport const LISTEN_OPTIONS: ListenOptions = {\n events: ['welcome', 'mutation', 'reconnect'],\n includeResult: true,\n includeAllVersions: true,\n visibility: 'query',\n tag: 'comments.listen',\n}\n\nconst QUERY_PROJECTION = `{\n _createdAt,\n _id,\n _rev,\n authorId,\n contentSnapshot,\n context,\n lastEditedAt,\n message,\n parentCommentId,\n reactions,\n status,\n target,\n threadId\n}`\n\nconst BASE_FILTERS = [`_type == \"comment\"`, `target.document._ref == $documentId`]\nconst VERSION_FILTER = `target.documentVersionId == $documentVersionId`\nconst NO_VERSION_FILTER = `!defined(target.documentVersionId)`\n\n/**\n * Which comments belong to a document, matching the Studio's filter.\n *\n * A release's comments are kept apart from the default ones: asking for a\n * release returns only that release's comments, and asking without one returns\n * only comments with no version at all.\n */\nfunction buildCommentsFilter(documentVersionId?: string): string {\n return [...BASE_FILTERS, documentVersionId ? VERSION_FILTER : NO_VERSION_FILTER].join(' && ')\n}\n\n/** The snapshot query. Newest comment first, as the Studio orders it. */\nexport function buildCommentsQuery(documentVersionId?: string): string {\n return `*[${buildCommentsFilter(documentVersionId)}] ${QUERY_PROJECTION} | order(_createdAt desc)`\n}\n\n/**\n * The listener query. Unprojected on purpose: mutation events carry the whole\n * comment, so there is nothing to trim.\n */\nexport function buildCommentsListenQuery(documentVersionId?: string): string {\n return `*[${buildCommentsFilter(documentVersionId)}]`\n}\n","import {type SanityClient} from '@sanity/client'\nimport {\n catchError,\n distinctUntilChanged,\n EMPTY,\n filter,\n firstValueFrom,\n map,\n type Observable,\n of,\n switchMap,\n take,\n} from 'rxjs'\n\nimport {getClient, getClientState} from '../client/clientStore'\nimport {\n type DatasetResource,\n type DocumentResource,\n isDatasetResource,\n} from '../config/sanityConfig'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {\n ADDON_DATASET_API_VERSION,\n ADDON_DATASET_PROFILE,\n COMMENTS_API_VERSION,\n} from './commentsConstants'\n\ninterface AddonDatasetStoreState {\n /**\n * `unknown` until discovery answers. `missing` means the project has no\n * comments dataset yet, which is the normal state until someone writes a\n * first comment.\n */\n status: 'unknown' | 'resolved' | 'missing'\n datasetName?: string\n /** In flight provisioning, so concurrent first comments issue one POST. */\n provisioning?: Promise<string>\n}\n\n/**\n * Comments need a project and a dataset. There is nowhere to put them for a\n * media library or a canvas, and no Studio to interoperate with either.\n *\n * @internal\n */\nexport function assertDatasetResource(resource: DocumentResource): DatasetResource {\n if (!isDatasetResource(resource)) {\n throw new Error(\n `Comments are only supported for dataset resources, received: ${JSON.stringify(resource)}`,\n )\n }\n return resource\n}\n\nfunction isMissingOrForbidden(error: unknown): boolean {\n if (typeof error !== 'object' || error === null || !('statusCode' in error)) return false\n const {statusCode} = error as {statusCode?: unknown}\n return statusCode === 403 || statusCode === 404\n}\n\nfunction requestAddonDataset(\n client: SanityClient,\n {projectId, dataset}: DatasetResource,\n): Observable<string | undefined> {\n return client.observable\n .request<{name: string}[] | undefined>({\n url: `/projects/${projectId}/datasets?datasetProfile=${ADDON_DATASET_PROFILE}&addonFor=${dataset}`,\n tag: 'comments.addon-dataset.list',\n })\n .pipe(map((datasets) => datasets?.[0]?.name))\n}\n\nfunction discover(\n instance: SanityInstance,\n resource: DatasetResource,\n): Observable<string | undefined> {\n return getClientState(instance, {\n apiVersion: ADDON_DATASET_API_VERSION,\n projectId: resource.projectId,\n useProjectHostname: true,\n }).observable.pipe(\n switchMap((client) =>\n requestAddonDataset(client, resource).pipe(\n // A missing dataset and a dataset this user cannot see both mean there\n // are no comments to read. Transient failures stay pending so a new\n // authenticated client can retry discovery.\n catchError((error: unknown) => (isMissingOrForbidden(error) ? of(undefined) : EMPTY)),\n ),\n ),\n )\n}\n\nasync function discoverOnce(\n instance: SanityInstance,\n resource: DatasetResource,\n): Promise<string | undefined> {\n const client = await firstValueFrom(\n getClientState(instance, {\n apiVersion: ADDON_DATASET_API_VERSION,\n projectId: resource.projectId,\n useProjectHostname: true,\n }).observable.pipe(take(1)),\n )\n\n try {\n return await firstValueFrom(requestAddonDataset(client, resource))\n } catch (error) {\n if (isMissingOrForbidden(error)) return undefined\n throw error\n }\n}\n\nconst addonDatasetStore = defineStore<AddonDatasetStoreState, BoundResourceKey>({\n name: 'CommentsAddonDataset',\n getInitialState: () => ({status: 'unknown'}),\n initialize: ({instance, state, key}) => {\n const resource = assertDatasetResource(key.resource)\n\n const subscription = discover(instance, resource).subscribe((datasetName) =>\n state.set(\n 'setAddonDataset',\n datasetName ? {status: 'resolved', datasetName} : {status: 'missing'},\n ),\n )\n\n return () => subscription.unsubscribe()\n },\n})\n\n/**\n * The addon dataset's name: `undefined` while discovery is in flight, `null`\n * when the project has none yet.\n *\n * @internal\n */\nexport const getAddonDatasetState: (\n instance: SanityInstance,\n options: {resource?: DocumentResource},\n) => StateSource<string | null | undefined> = bindActionByResource(\n addonDatasetStore,\n createStateSourceAction(({state}: {state: AddonDatasetStoreState}) => {\n if (state.status === 'unknown') return undefined\n return state.datasetName ?? null\n }),\n)\n\n/**\n * Resolves the addon dataset, creating it if the project has none.\n *\n * Safe to call concurrently: the first call owns the request and later ones\n * await the same promise. Provisioning is a one-time, project-wide side effect,\n * so this is only called from the write path, when we know there is a comment\n * to store.\n *\n * @internal\n */\nexport const provisionAddonDataset: (\n instance: SanityInstance,\n options: {resource?: DocumentResource},\n) => Promise<string> = bindActionByResource(\n addonDatasetStore,\n ({instance, state, key}: StoreContext<AddonDatasetStoreState, BoundResourceKey>) => {\n const current = state.get()\n if (current.datasetName) return Promise.resolve(current.datasetName)\n if (current.provisioning) return current.provisioning\n\n const resource = assertDatasetResource(key.resource)\n\n const provisioning = (async () => {\n // Another user may have provisioned it since this client started up, in\n // which case creating it again would fail. Ask before writing.\n const existing = await discoverOnce(instance, resource)\n if (existing) return existing\n\n const client = getClient(instance, {\n apiVersion: ADDON_DATASET_API_VERSION,\n projectId: resource.projectId,\n useProjectHostname: true,\n })\n\n const response = await client.request<{datasetName?: string} | undefined>({\n url: `/comments/${resource.dataset}/setup`,\n method: 'POST',\n tag: 'comments.addon-dataset.setup',\n })\n\n if (!response?.datasetName) {\n throw new Error('Creating the comments addon dataset returned no dataset name.')\n }\n\n return response.datasetName\n })()\n .then((datasetName) => {\n state.set('setAddonDataset', {\n status: 'resolved',\n datasetName,\n provisioning: undefined,\n })\n return datasetName\n })\n .catch((error: unknown) => {\n // Clear the in-flight promise so a retry is not permanently stuck on\n // this failure.\n state.set('clearProvisioning', {provisioning: undefined})\n throw error\n })\n\n state.set('startProvisioning', {provisioning})\n\n return provisioning\n },\n)\n\n/**\n * A client pointed at the addon dataset, or `null` while the project has none.\n *\n * Emits again whenever the dataset is discovered or provisioned, and whenever\n * the auth token changes. Long-lived readers must follow it rather than holding\n * a client, because the client store drops every cached client on a token\n * change.\n *\n * @internal\n */\nexport const observeAddonDatasetClient: (\n instance: SanityInstance,\n options: {resource?: DocumentResource},\n) => Observable<SanityClient | null> = bindActionByResource(\n addonDatasetStore,\n ({instance, key}: StoreContext<AddonDatasetStoreState, BoundResourceKey>) => {\n const {projectId} = assertDatasetResource(key.resource)\n\n return getAddonDatasetState(instance, {resource: key.resource}).observable.pipe(\n distinctUntilChanged(),\n filter((datasetName): datasetName is string | null => datasetName !== undefined),\n switchMap((datasetName) =>\n typeof datasetName === 'string'\n ? getClientState(instance, {\n apiVersion: COMMENTS_API_VERSION,\n projectId,\n dataset: datasetName,\n }).observable\n : of(null),\n ),\n )\n },\n)\n","import {stringifyPath} from '@sanity/json-match'\nimport {type Path} from '@sanity/types'\n\n/** A dotted segment GROQ would read as a literal rather than a field name. */\nconst RESERVED_SEGMENT = /\\.(true|false|null)(?=$|[.[])/g\n\n/**\n * One quoted string, or one run of text containing no quote at all. Splitting on\n * this is what keeps the rewrite below out of `[_key==\"...\"]` values.\n */\nconst QUOTED_OR_PLAIN = /\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|[^\"']+/g\n\nfunction quoteReservedFieldSegments(path: string): string {\n return path.replace(QUOTED_OR_PLAIN, (chunk) =>\n chunk.startsWith('\"') || chunk.startsWith(\"'\")\n ? chunk\n : chunk.replace(RESERVED_SEGMENT, '[\"$1\"]'),\n )\n}\n\n/**\n * Normalises a field path to the string form comments are stored in.\n *\n * Comments keep `target.path.field` as a string rather than a path array,\n * because that is what the Studio writes and reading it back has to match\n * exactly. A string given here is parsed and re-stringified, so both a path\n * array and a hand-written string come out in the same canonical form.\n *\n * `undefined` and `[]` both produce `''`. Callers writing a comment reject that\n * rather than passing it on, since a comment has to point at a field.\n *\n * @example\n * ```ts\n * toCommentFieldPath(['body', {_key: 'intro'}, 'content'])\n * // 'body[_key==\"intro\"].content'\n * toCommentFieldPath(undefined)\n * // ''\n * ```\n *\n * @internal\n */\nexport function toCommentFieldPath(path?: string | Path): string {\n return quoteReservedFieldSegments(stringifyPath(path))\n}\n","import {type Comment, type CommentThread} from './types'\n\n/**\n * Groups a flat comment list into threads.\n *\n * A thread's parent is the comment with no `parentCommentId`; replies point at\n * the parent's `id`. Replies whose parent is missing from the input are\n * dropped, which is what happens when a parent is deleted but its replies have\n * not disappeared yet.\n *\n * Threads come back in the order their parents appear in `comments`, so passing\n * the store's newest-first list gives newest-thread-first. Replies within a\n * thread are always oldest first, since that is reading order.\n *\n * @internal\n */\nexport function buildCommentThreads(comments: Comment[]): CommentThread[] {\n const repliesByParent = new Map<string, Comment[]>()\n\n for (const comment of comments) {\n if (!comment.parentCommentId) continue\n const existing = repliesByParent.get(comment.parentCommentId)\n if (existing) {\n existing.push(comment)\n } else {\n repliesByParent.set(comment.parentCommentId, [comment])\n }\n }\n\n const threads: CommentThread[] = []\n\n for (const parentComment of comments) {\n if (parentComment.parentCommentId) continue\n\n // The array is built above and owned here, so sorting in place is safe.\n const replies = (repliesByParent.get(parentComment.id) ?? []).sort((a, b) =>\n a.createdAt.localeCompare(b.createdAt),\n )\n\n const lastReply = replies[replies.length - 1]\n\n threads.push({\n threadId: parentComment.threadId,\n fieldPath: parentComment.fieldPath,\n parentComment,\n replies,\n commentsCount: replies.length + 1,\n status: parentComment.status,\n lastActivityAt: lastReply ? lastReply.createdAt : parentComment.createdAt,\n })\n }\n\n return threads\n}\n","import {type Comment, type StoredComment} from './types'\n\n/**\n * Turns a stored comment into the shape consumers see.\n *\n * This is the whole seam between the storage format and the public API. When\n * comments move to the organization-level store, the three fields that differ\n * are handled here and nothing downstream changes:\n *\n * - the author moves from `authorId` to `_system.createdBy`\n * - `target.document` becomes a global document reference, so `documentId` is\n * parsed out of a `resourceType:resourceId:documentId` string rather than\n * read off `_ref`\n * - `_type` goes from `comment` to `sanity.comment`, and stops being exposed\n *\n * @internal\n */\nexport function normalizeComment(stored: StoredComment): Comment {\n const {target} = stored\n\n return {\n id: stored._id,\n createdAt: stored._createdAt,\n // Omitted rather than emptied when absent, so a consumer can tell \"no\n // author recorded\" from a user whose id happens to be falsy.\n ...(stored.authorId ? {authorId: stored.authorId} : {}),\n message: stored.message,\n threadId: stored.threadId,\n ...(stored.parentCommentId ? {parentCommentId: stored.parentCommentId} : {}),\n status: stored.status,\n ...(stored.lastEditedAt ? {lastEditedAt: stored.lastEditedAt} : {}),\n documentId: target.document._ref,\n documentType: target.documentType,\n fieldPath: target.path?.field ?? '',\n ...(target.path?.selection ? {selection: target.path.selection} : {}),\n ...(stored.contentSnapshot === undefined ? {} : {contentSnapshot: stored.contentSnapshot}),\n // Dropped: the stored `_key`, which only exists to address the array item.\n reactions: (stored.reactions ?? []).map(({shortName, userId, addedAt}) => ({\n shortName,\n userId,\n addedAt,\n })),\n ...(stored._state ? {state: stored._state} : {}),\n }\n}\n","import {type ListenEvent, type SanityClient} from '@sanity/client'\nimport {filter, map, Observable, share, switchMap, take} from 'rxjs'\n\nimport {buildCommentsListenQuery, buildCommentsQuery, LISTEN_OPTIONS} from './commentsConstants'\nimport {type StoredComment} from './types'\n\n/** What the listener saw, in terms the store can apply. */\nexport type CommentsEvent =\n | {type: 'snapshot'; comments: StoredComment[]}\n | {type: 'appear'; comment: StoredComment}\n | {type: 'disappear'; commentId: string}\n | {type: 'update'; comment: StoredComment; transactionId: string}\n | {type: 'error'; error: unknown}\n\nfunction toCommentsEvent(event: ListenEvent<StoredComment>): CommentsEvent | undefined {\n if (event.type !== 'mutation') return undefined\n\n if (event.transition === 'disappear') {\n return {type: 'disappear', commentId: event.documentId}\n }\n\n // `includeResult` is on, so a create or update carries the whole comment. An\n // event without one is nothing we can apply.\n if (!event.result) return undefined\n\n if (event.transition === 'appear') {\n return {type: 'appear', comment: event.result}\n }\n\n return {type: 'update', comment: event.result, transactionId: event.transactionId}\n}\n\n/**\n * Watches one document's comments: a snapshot on connect, then live changes.\n *\n * The Studio awaits its snapshot inside the event handler, which loses any\n * mutation that arrives while the fetch is in flight, because the older\n * snapshot overwrites it. Here the mutations that arrive during a fetch are\n * held and replayed straight after the snapshot, so nothing is dropped.\n *\n * A reconnect produces a fresh `welcome`, which cancels any fetch still running\n * and starts a new one. Reconnects emit nothing themselves: dropping the list\n * while reconnecting would only make readers suspend again for data we still\n * have.\n *\n * @internal\n */\nexport function observeComments(options: {\n client: SanityClient\n documentId: string\n documentVersionId?: string\n}): Observable<CommentsEvent> {\n const {client, documentId, documentVersionId} = options\n\n const params = {\n documentId,\n ...(documentVersionId ? {documentVersionId} : {}),\n }\n\n const events$ = client.observable\n .listen<StoredComment>(buildCommentsListenQuery(documentVersionId), params, LISTEN_OPTIONS)\n .pipe(share())\n\n const mutations$ = events$.pipe(\n map(toCommentsEvent),\n filter((event): event is CommentsEvent => event !== undefined),\n )\n\n return events$.pipe(\n filter((event) => event.type === 'welcome'),\n switchMap(\n () =>\n new Observable<CommentsEvent>((observer) => {\n // Subscribe before the fetch starts so nothing between the two is missed.\n // The array only exists while the snapshot is in flight. Once flushed,\n // live mutations pass through without retaining their history.\n const buffered: CommentsEvent[] = []\n let snapshotReceived = false\n const mutationSubscription = mutations$.subscribe({\n next: (event) => {\n if (snapshotReceived) observer.next(event)\n else buffered.push(event)\n },\n error: (error: unknown) => observer.error(error),\n })\n\n const snapshotSubscription = client.observable\n .fetch<StoredComment[]>(buildCommentsQuery(documentVersionId), params, {\n tag: 'comments.list',\n })\n .pipe(take(1))\n .subscribe({\n next: (comments) => {\n observer.next({type: 'snapshot', comments})\n snapshotReceived = true\n for (const event of buffered) observer.next(event)\n buffered.length = 0\n },\n error: (error: unknown) => {\n observer.next({type: 'error', error})\n observer.complete()\n },\n })\n\n return () => {\n mutationSubscription.unsubscribe()\n snapshotSubscription.unsubscribe()\n buffered.length = 0\n }\n }),\n ),\n )\n}\n","import {omitProperty} from '../utils/object'\nimport {type CommentPostPayload, type StoredComment} from './types'\n\ninterface CommentsEntry {\n /** `undefined` until the first snapshot arrives, which is what suspends readers. */\n comments?: Record<string, StoredComment>\n error?: unknown\n subscribers: string[]\n}\n\nexport interface CommentsStoreState {\n entries: {[key: string]: CommentsEntry | undefined}\n /** Comment creates that have not been confirmed by the server yet. */\n pendingCreates: {[commentId: string]: true | undefined}\n /**\n * The most recent transaction this client started per comment, held only\n * while the write is in flight.\n *\n * The listener echoes our own writes back. Without this, an echo from an\n * earlier transaction could arrive after a later one and undo it on screen.\n * Cleared as soon as the write settles: applying our own echo after that is\n * harmless, because it carries the same data we already show.\n */\n pendingTransactions: {[commentId: string]: string | undefined}\n error?: unknown\n}\n\nexport interface CommentsKeyParts {\n documentId: string\n documentVersionId?: string\n}\n\nexport function getCommentsKey({documentId, documentVersionId}: CommentsKeyParts): string {\n return JSON.stringify([documentId, documentVersionId ?? null])\n}\n\nexport function parseCommentsKey(key: string): CommentsKeyParts {\n const [documentId, documentVersionId] = JSON.parse(key) as [string, string | null]\n return {documentId, ...(documentVersionId ? {documentVersionId} : {})}\n}\n\nexport const addSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n const subscribers = [...(entry?.subscribers ?? []), subscriptionId]\n return {...prev, entries: {...prev.entries, [key]: {...entry, subscribers}}}\n }\n\nexport const removeSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n const subscribers = entry.subscribers.filter((id) => id !== subscriptionId)\n if (!subscribers.length) {\n const pendingCreates = {...prev.pendingCreates}\n const pendingTransactions = {...prev.pendingTransactions}\n\n for (const commentId of Object.keys(entry.comments ?? {})) {\n delete pendingCreates[commentId]\n delete pendingTransactions[commentId]\n }\n\n return {\n ...prev,\n entries: omitProperty(prev.entries, key),\n pendingCreates,\n pendingTransactions,\n }\n }\n return {...prev, entries: {...prev.entries, [key]: {...entry, subscribers}}}\n }\n\n/** Replaces an entry's contents with a freshly fetched snapshot. */\nexport const setComments =\n (key: string, comments: StoredComment[]) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n const byId = Object.fromEntries(comments.map((comment) => [comment._id, comment]))\n const pendingCreates = {...prev.pendingCreates}\n\n for (const [commentId, localComment] of Object.entries(entry.comments ?? {})) {\n if (Object.hasOwn(byId, commentId)) {\n delete pendingCreates[commentId]\n } else if (Object.hasOwn(pendingCreates, commentId) || localComment._state) {\n // A snapshot can race an in-flight create. Failed creates are also local\n // drafts and must remain available for retry.\n byId[commentId] = localComment\n }\n }\n\n return {\n ...prev,\n entries: {...prev.entries, [key]: {...entry, comments: byId, error: undefined}},\n pendingCreates,\n }\n }\n\nexport const setCommentsError =\n (key: string, error: unknown) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n return {...prev, entries: {...prev.entries, [key]: {...entry, error}}}\n }\n\n/** A comment as it arrived from the server, replacing whatever we held. */\nexport const receiveComment =\n (key: string, comment: StoredComment) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n const pendingCreates = omitProperty(prev.pendingCreates, comment._id)\n if (!entry) return {...prev, pendingCreates}\n return {\n ...prev,\n pendingCreates,\n entries: {\n ...prev.entries,\n [key]: {...entry, comments: {...entry.comments, [comment._id]: comment}},\n },\n }\n }\n\n/**\n * A comment we just wrote, shown before the server confirms it.\n *\n * `_createdAt` is assigned by the server, but the list sorts on it, so an\n * optimistic comment needs a stand-in until the real value arrives.\n */\ntype OptimisticComment = CommentPostPayload & {_createdAt?: string; _rev?: string}\n\n/** A create that already failed once is being retried, not written fresh. */\nfunction isRetryingCreate(existing: StoredComment | undefined): boolean {\n const state = existing?._state?.type\n return state === 'createError' || state === 'createRetrying'\n}\n\nfunction mergeOptimisticComment(\n existing: StoredComment | undefined,\n comment: OptimisticComment,\n): StoredComment {\n return {\n ...existing,\n ...comment,\n _createdAt: comment._createdAt ?? existing?._createdAt ?? new Date().toISOString(),\n _rev: comment._rev ?? existing?._rev ?? '',\n ...(isRetryingCreate(existing) ? {_state: {type: 'createRetrying'} as const} : {}),\n }\n}\n\nexport const addComment =\n (key: string, comment: OptimisticComment) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n\n const existing =\n entry.comments && Object.hasOwn(entry.comments, comment._id)\n ? entry.comments[comment._id]\n : undefined\n\n return {\n ...prev,\n pendingCreates: {...prev.pendingCreates, [comment._id]: true},\n entries: {\n ...prev.entries,\n [key]: {\n ...entry,\n comments: {\n ...entry.comments,\n [comment._id]: mergeOptimisticComment(existing, comment),\n },\n },\n },\n }\n }\n\n/**\n * Merges a partial update into whichever entry holds the comment.\n *\n * Comment ids are unique, so this touches one entry in practice. Searching by\n * id rather than taking a key means callers editing a comment do not have to\n * say which document it belongs to.\n */\nexport const applyCommentUpdate =\n (commentId: string, patch: Partial<StoredComment>) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entries = {...prev.entries}\n let changed = false\n\n for (const [key, entry] of Object.entries(prev.entries)) {\n const comment = entry?.comments?.[commentId]\n if (!entry || !comment) continue\n changed = true\n entries[key] = {\n ...entry,\n comments: {...entry.comments, [commentId]: {...comment, ...patch}},\n }\n }\n\n return changed ? {...prev, entries} : prev\n }\n\n/** Removes a comment and, when it is a thread parent, its replies. */\nexport const removeCommentById =\n (commentId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entries = {...prev.entries}\n const removedIds = new Set<string>()\n let changed = false\n\n for (const [key, entry] of Object.entries(prev.entries)) {\n if (!entry?.comments) continue\n\n const remaining = Object.fromEntries(\n Object.entries(entry.comments).filter(([id, comment]) => {\n const keep = id !== commentId && comment.parentCommentId !== commentId\n if (!keep) removedIds.add(id)\n return keep\n }),\n )\n\n if (Object.keys(remaining).length === Object.keys(entry.comments).length) continue\n\n changed = true\n entries[key] = {...entry, comments: remaining}\n }\n\n if (!changed) return prev\n\n const pendingCreates = {...prev.pendingCreates}\n const pendingTransactions = {...prev.pendingTransactions}\n for (const id of removedIds) {\n delete pendingCreates[id]\n delete pendingTransactions[id]\n }\n\n return {...prev, entries, pendingCreates, pendingTransactions}\n }\n\nexport const setPendingTransaction =\n (commentId: string, transactionId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => ({\n ...prev,\n pendingTransactions: {...prev.pendingTransactions, [commentId]: transactionId},\n })\n\nexport const clearPendingTransaction =\n (commentId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => ({\n ...prev,\n pendingTransactions: omitProperty(prev.pendingTransactions, commentId),\n })\n\n/** Marks a pending create as failed, unless another request already confirmed it. */\nexport const setCommentCreateError =\n (commentId: string, error: Error) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n if (!Object.hasOwn(prev.pendingCreates, commentId)) return prev\n const next = applyCommentUpdate(commentId, {_state: {type: 'createError', error}})(prev)\n return {...next, pendingCreates: omitProperty(next.pendingCreates, commentId)}\n }\n\n/** Restores an optimistic edit if the failed transaction is still current. */\nexport const rollbackCommentUpdate =\n (commentId: string, transactionId: string, previous: StoredComment) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n if (\n !Object.hasOwn(prev.pendingTransactions, commentId) ||\n prev.pendingTransactions[commentId] !== transactionId\n ) {\n return prev\n }\n\n const entries = Object.fromEntries(\n Object.entries(prev.entries).map(([key, entry]) => {\n if (!entry?.comments?.[commentId]) return [key, entry]\n return [key, {...entry, comments: {...entry.comments, [commentId]: previous}}]\n }),\n )\n\n return {\n ...prev,\n entries,\n pendingTransactions: omitProperty(prev.pendingTransactions, commentId),\n }\n }\n\n/** Restores comments removed optimistically when the server delete fails. */\nexport const restoreComments =\n (removed: Array<{key: string; comments: StoredComment[]}>) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entries = {...prev.entries}\n let changed = false\n\n for (const {key, comments} of removed) {\n const entry = entries[key]\n if (!entry) continue\n const missing = comments.filter(\n (comment) => !Object.hasOwn(entry.comments ?? {}, comment._id),\n )\n if (!missing.length) continue\n changed = true\n entries[key] = {\n ...entry,\n comments: Object.fromEntries([\n ...Object.entries(entry.comments ?? {}),\n ...missing.map((comment) => [comment._id, comment] as const),\n ]),\n }\n }\n\n return changed ? {...prev, entries} : prev\n }\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\nimport {type Path} from '@sanity/types'\nimport {\n catchError,\n distinctUntilChanged,\n EMPTY,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n NEVER,\n Observable,\n pairwise,\n race,\n startWith,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {type StoreState} from '../store/createStoreState'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {randomId} from '../utils/ids'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {observeAddonDatasetClient} from './addonDatasetStore'\nimport {buildCommentThreads} from './buildCommentThreads'\nimport {toCommentFieldPath} from './commentFieldPath'\nimport {COMMENTS_STATE_CLEAR_DELAY} from './commentsConstants'\nimport {normalizeComment} from './normalizeComment'\nimport {type CommentsEvent, observeComments} from './observeComments'\nimport {\n addSubscriber,\n clearPendingTransaction,\n type CommentsKeyParts,\n type CommentsStoreState,\n getCommentsKey,\n parseCommentsKey,\n receiveComment,\n removeCommentById,\n removeSubscriber,\n setComments,\n setCommentsError,\n} from './reducers'\nimport {type Comment, type CommentStatus, type CommentThread, type StoredComment} from './types'\n\n/**\n * Which comments to read.\n * @beta\n */\nexport interface CommentsOptions extends DocumentHandle {\n /**\n * Narrow to one field. Omit to get every comment on the document.\n */\n fieldPath?: string | Path\n /** Narrow to open or resolved threads. Omit for both. */\n status?: CommentStatus\n}\n\n/** @beta */\nexport interface ResolveCommentsOptions extends CommentsOptions {\n signal?: AbortSignal\n}\n\n/**\n * Fields on {@link CommentsOptions} that are deliberately left out of the key\n * below, because neither changes which comments an option set addresses.\n *\n * `source` is the deprecated alias for `resource` and is folded into it before\n * a key is ever built, so keying on both would give one list two keys.\n * `liveEdit` describes the document rather than its comments, which hang off\n * the published id whether or not drafts exist.\n */\ntype CommentsKeyIrrelevantField = 'source' | 'liveEdit'\n\n/**\n * A stable string standing for one set of read options.\n *\n * Only React needs this: it holds one state source steady across renders and\n * defers swapping to a new one while the previous list is still on screen.\n * `fieldPath` is normalised on the way in, so a path array and the equivalent\n * string address the same list.\n *\n * @internal\n */\nexport function getCommentsOptionsKey(options: CommentsOptions): string {\n return JSON.stringify({\n documentId: options.documentId,\n documentType: options.documentType,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n fieldPath: options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n status: options.status,\n // The `Record` half makes a new field on `CommentsOptions` a compile\n // error here unless it is listed above or named as irrelevant. Left to\n // `satisfies CommentsOptions` alone, a forgotten field would just be\n // absent from the key, and a reader would keep the list it had while the\n // caller thought it had asked for a different one.\n } satisfies CommentsOptions &\n Record<Exclude<keyof CommentsOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseCommentsOptionsKey(key: string): CommentsOptions {\n return JSON.parse(key) as CommentsOptions\n}\n\n/**\n * Which comment list an option set addresses.\n *\n * Comments hang off the published id, so a draft and its published document\n * share one list. A release keeps its own.\n */\nexport function toCommentsKeyParts(\n instance: SanityInstance,\n options: CommentsOptions,\n): CommentsKeyParts {\n const perspective = options.perspective ?? instance.config.perspective\n return {\n documentId: getPublishedId(DocumentId(options.documentId)),\n ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),\n }\n}\n\nfunction applyEvent(\n state: StoreState<CommentsStoreState>,\n key: string,\n event: CommentsEvent,\n): void {\n switch (event.type) {\n case 'snapshot':\n state.set('setComments', setComments(key, event.comments))\n return\n case 'appear':\n state.set('receiveComment', receiveComment(key, event.comment))\n return\n case 'disappear':\n state.set('removeComment', removeCommentById(event.commentId))\n return\n case 'error':\n state.set('setCommentsError', setCommentsError(key, event.error))\n return\n case 'update': {\n const pending = state.get().pendingTransactions[event.comment._id]\n\n // Our own writes come back through the listener. When a later transaction\n // is already in flight for this comment, an echo of an earlier one would\n // undo it on screen, so drop it and wait for the one we are expecting.\n if (pending && pending !== event.transactionId) return\n\n state.set('receiveComment', receiveComment(key, event.comment))\n if (pending) {\n state.set('clearPendingTransaction', clearPendingTransaction(event.comment._id))\n }\n }\n }\n}\n\nconst watchSubscribedDocuments = ({\n state,\n instance,\n key,\n}: StoreContext<CommentsStoreState, BoundResourceKey>) => {\n const client$ = observeAddonDatasetClient(instance, {resource: key.resource})\n\n return state.observable\n .pipe(\n map((current) => new Set(Object.keys(current.entries))),\n distinctUntilChanged(\n (a, b) => a.size === b.size && Array.from(b).every((entry) => a.has(entry)),\n ),\n startWith(new Set<string>()),\n pairwise(),\n mergeMap(([previous, current]) => [\n ...Array.from(current)\n .filter((entry) => !previous.has(entry))\n .map((entry) => ({key: entry, added: true})),\n ...Array.from(previous)\n .filter((entry) => !current.has(entry))\n .map((entry) => ({key: entry, added: false})),\n ]),\n groupBy((event) => event.key),\n mergeMap((group$) =>\n group$.pipe(\n switchMap((event) => {\n if (!event.added) return EMPTY\n\n const {documentId, documentVersionId} = parseCommentsKey(group$.key)\n\n return client$.pipe(\n switchMap((client) => {\n if (!client) {\n // A dataset that does not exist holds no comments. Settling on\n // an empty list matters: leaving it unset would suspend\n // readers forever on a project nobody has commented in.\n state.set('setComments', setComments(group$.key, []))\n return EMPTY\n }\n\n return observeComments({client, documentId, documentVersionId}).pipe(\n tap((commentsEvent) => applyEvent(state, group$.key, commentsEvent)),\n // Keep following the addon client after one listener fails.\n // A token refresh or reconnect can then supply a new client.\n catchError((error: unknown) => {\n state.set('setCommentsError', setCommentsError(group$.key, error))\n return EMPTY\n }),\n )\n }),\n )\n }),\n ),\n ),\n )\n .subscribe({error: (error: unknown) => state.set('setError', {error})})\n}\n\nexport const commentsStore = defineStore<CommentsStoreState, BoundResourceKey>({\n name: 'Comments',\n getInitialState: () => ({\n entries: {},\n pendingCreates: {},\n pendingTransactions: {},\n }),\n initialize: (context) => {\n const subscription = watchSubscribedDocuments(context)\n return () => subscription.unsubscribe()\n },\n})\n\n/**\n * Filtered lists and their threads, cached against the comment map they came\n * from.\n *\n * Selectors run on every store change, and a fresh array each time would make\n * `useSyncExternalStore` re-render whenever anything anywhere in the store\n * moved. Keying on the comment map means a change to some other document, or\n * to an unrelated part of the state, hands back the identical array. Entries\n * die with the map they belong to.\n */\nconst normalizedCache = new WeakMap<object, Comment[]>()\nconst filteredCache = new WeakMap<object, Map<string, Comment[]>>()\nconst threadCache = new WeakMap<object, Map<string, CommentThread[]>>()\n\n/**\n * The stored map turned into what consumers see, newest first.\n *\n * Cached alongside the filters below rather than done per read, so the\n * normalised objects keep their identity for as long as the stored map does.\n */\nfunction normalizeAll(commentsById: Record<string, StoredComment>): Comment[] {\n const cached = normalizedCache.get(commentsById)\n if (cached) return cached\n\n // Newest first, matching the query's order. The store keys comments by id, so\n // the order has to be reapplied here.\n const normalized = Object.values(commentsById)\n .map(normalizeComment)\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n\n normalizedCache.set(commentsById, normalized)\n return normalized\n}\n\nfunction filterComments(\n all: Comment[],\n fieldPath: string | undefined,\n status: CommentStatus | undefined,\n): Comment[] {\n let byFilter = filteredCache.get(all)\n if (!byFilter) {\n byFilter = new Map()\n filteredCache.set(all, byFilter)\n }\n\n // `\\0` stands in for \"no field filter\", which is not the same as `''`.\n const cacheKey = `${fieldPath ?? '\\0'}|${status ?? ''}`\n const cached = byFilter.get(cacheKey)\n if (cached) return cached\n\n const filtered = all.filter((comment) => {\n if (status && comment.status !== status) return false\n if (fieldPath === undefined) return true\n return comment.fieldPath === fieldPath\n })\n\n byFilter.set(cacheKey, filtered)\n return filtered\n}\n\nfunction selectComments(\n {state, instance}: SelectorContext<CommentsStoreState>,\n options: CommentsOptions,\n): Comment[] | undefined {\n if (state.error) throw state.error\n\n const entry = state.entries[getCommentsKey(toCommentsKeyParts(instance, options))]\n if (entry?.error) throw entry.error\n if (!entry?.comments) return undefined\n\n return filterComments(\n normalizeAll(entry.comments),\n options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n options.status,\n )\n}\n\nfunction selectCommentThreads(\n context: SelectorContext<CommentsStoreState>,\n options: CommentsOptions,\n): CommentThread[] | undefined {\n const comments = selectComments(context, {...options, fieldPath: undefined, status: undefined})\n if (comments === undefined) return undefined\n\n let byFilter = threadCache.get(comments)\n if (!byFilter) {\n byFilter = new Map()\n threadCache.set(comments, byFilter)\n }\n\n const fieldPath =\n options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath)\n const cacheKey = `${fieldPath ?? '\\0'}|${options.status ?? ''}`\n const cached = byFilter.get(cacheKey)\n if (cached) return cached\n\n const threads = buildCommentThreads(comments).filter((thread) => {\n if (options.status && thread.parentComment.status !== options.status) return false\n if (fieldPath === undefined) return true\n return thread.fieldPath === fieldPath\n })\n byFilter.set(cacheKey, threads)\n return threads\n}\n\n/**\n * Every comment on a document, newest first.\n *\n * `undefined` until the first snapshot arrives. Replies are included; use\n * {@link getCommentThreadsState} to read them grouped.\n *\n * @beta\n */\nexport const getCommentsState: (\n instance: SanityInstance,\n options: CommentsOptions,\n) => StateSource<Comment[] | undefined> = bindActionByResource(\n commentsStore,\n createStateSourceAction({\n selector: selectComments,\n onSubscribe: ({state, instance}, options: CommentsOptions) => {\n const key = getCommentsKey(toCommentsKeyParts(instance, options))\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n return () => {\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId)),\n COMMENTS_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Threads on a document, newest thread first, each with its replies oldest first.\n *\n * A thread's `status` and `fieldPath` come from its first comment, so filtering\n * by either selects whole threads rather than individual replies.\n *\n * @beta\n */\nexport const getCommentThreadsState: (\n instance: SanityInstance,\n options: CommentsOptions,\n) => StateSource<CommentThread[] | undefined> = bindActionByResource(\n commentsStore,\n createStateSourceAction({\n selector: selectCommentThreads,\n onSubscribe: ({state, instance}, options: CommentsOptions) => {\n const key = getCommentsKey(toCommentsKeyParts(instance, options))\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n return () => {\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId)),\n COMMENTS_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Waits for a document's comments to load.\n *\n * Holds a subscriber only while resolving, so a component that suspends on this\n * and then errors before mounting does not strand the list. Throw the promise\n * for Suspense, then read through {@link getCommentsState}.\n *\n * @beta\n */\nexport const resolveComments: (\n instance: SanityInstance,\n options: ResolveCommentsOptions,\n) => Promise<Comment[]> = bindActionByResource(\n commentsStore,\n (\n {state, instance}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {signal, ...options}: ResolveCommentsOptions,\n ) => resolveList(state, instance, options, signal, getCommentsState),\n)\n\n/**\n * Waits for a document's comments to load, grouped into threads.\n * @beta\n */\nexport const resolveCommentThreads: (\n instance: SanityInstance,\n options: ResolveCommentsOptions,\n) => Promise<CommentThread[]> = bindActionByResource(\n commentsStore,\n (\n {state, instance}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {signal, ...options}: ResolveCommentsOptions,\n ) => resolveList(state, instance, options, signal, getCommentThreadsState),\n)\n\nfunction resolveList<T>(\n state: StoreState<CommentsStoreState>,\n instance: SanityInstance,\n options: CommentsOptions,\n signal: AbortSignal | undefined,\n getState: (i: SanityInstance, o: CommentsOptions) => StateSource<T | undefined>,\n): Promise<T> {\n const key = getCommentsKey(toCommentsKeyParts(instance, options))\n const {getCurrent} = getState(instance, options)\n\n // Loading is driven by subscribers, so without one here nothing would ever\n // fetch and this promise would never settle. Holding it only for the duration\n // of the resolve also means a component that suspends and then errors before\n // mounting does not leave a subscriber-less entry behind holding its error.\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n const release = () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId))\n\n const aborted$ = signal\n ? new Observable<never>((observer) => {\n const listener = () => {\n // Release now rather than after the delay: when this was the only\n // reader, dropping the key tears down the listener immediately, which\n // is the point of aborting.\n release()\n observer.error(new DOMException('The operation was aborted.', 'AbortError'))\n }\n signal.addEventListener('abort', listener)\n return () => signal.removeEventListener('abort', listener)\n })\n : NEVER\n\n const resolved$ = state.observable.pipe(\n map(() => getCurrent()),\n first((value): value is T => value !== undefined),\n )\n\n const promise = firstValueFrom(race([resolved$, aborted$]))\n const releaseLater = () => setCleanupTimeout(release, COMMENTS_STATE_CLEAR_DELAY)\n promise.then(releaseLater, releaseLater)\n return promise\n}\n","// NOTE: currently this API is only available on vX\nexport const API_VERSION = 'vX'\nexport const PROJECT_API_VERSION = '2025-07-18'\nexport const USERS_STATE_CLEAR_DELAY = 5000\nexport const DEFAULT_USERS_BATCH_SIZE = 100\n","import {type SanityInstance} from '../store/createSanityInstance'\nimport {omitProperty} from '../utils/object'\nimport {type GetUsersOptions, type SanityUserResponse, type UsersStoreState} from './types'\nimport {DEFAULT_USERS_BATCH_SIZE} from './usersConstants'\n\n/** @internal */\nexport const getUsersKey = (\n instance: SanityInstance,\n {\n resourceType,\n organizationId,\n batchSize = DEFAULT_USERS_BATCH_SIZE,\n projectId = instance.config.projectId,\n userId,\n }: GetUsersOptions = {},\n): string =>\n JSON.stringify({\n resourceType,\n organizationId,\n batchSize,\n projectId,\n userId,\n } satisfies ReturnType<typeof parseUsersKey>)\n\n/** @internal */\nexport const parseUsersKey = (\n key: string,\n): {\n batchSize: number\n resourceType?: 'organization' | 'project'\n projectId?: string\n organizationId?: string\n userId?: string\n} => JSON.parse(key)\n\nexport const addSubscription =\n (subscriptionId: string, key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n const subscriptions = [...(group?.subscriptions ?? []), subscriptionId]\n return {...prev, users: {...prev.users, [key]: {...group, subscriptions}}}\n }\n\nexport const removeSubscription =\n (subscriptionId: string, key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n const subscriptions = group.subscriptions.filter((id) => id !== subscriptionId)\n if (!subscriptions.length) return {...prev, users: omitProperty(prev.users, key)}\n return {...prev, users: {...prev.users, [key]: {...group, subscriptions}}}\n }\n\nexport const setUsersData =\n (key: string, {data, nextCursor, totalCount}: SanityUserResponse) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n const users = [...(group.users ?? []), ...data]\n return {...prev, users: {...prev.users, [key]: {...group, users, totalCount, nextCursor}}}\n }\n\nexport const updateLastLoadMoreRequest =\n (timestamp: string, key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n return {...prev, users: {...prev.users, [key]: {...group, lastLoadMoreRequest: timestamp}}}\n }\n\nexport const setUsersError =\n (key: string, error: unknown) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n return {...prev, users: {...prev.users, [key]: {...group, error}}}\n }\n\nexport const cancelRequest =\n (key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n if (group.subscriptions.length) return prev\n return {...prev, users: omitProperty(prev.users, key)}\n }\n\nexport const initializeRequest =\n (key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n if (prev.users[key]) return prev\n return {...prev, users: {...prev.users, [key]: {subscriptions: []}}}\n }\n","import {\n ConnectionFailedError,\n CorsOriginError,\n DisconnectError,\n type LiveEventMessage,\n} from '@sanity/client'\nimport {catchError, defer, EMPTY, filter, type Observable, retry, switchMap} from 'rxjs'\n\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {getClientState} from './clientStore'\n\n/**\n * All live-events subscriptions must use identical connection options\n * (API version, request tag, `includeDrafts`): `@sanity/client` caches the\n * underlying EventSource per URL + options, so divergent options would\n * silently split what should be a single shared connection.\n */\nconst LIVE_EVENTS_API_VERSION = 'v2025-05-06'\n\n/**\n * Delay before re-establishing the live events connection after a\n * server-initiated error (e.g. ChannelError, MessageError). Live errors are\n * retried rather than surfaced because an errored live connection would\n * otherwise poison the consuming store. Plain connection drops never reach\n * this — they are reconnected inside `@sanity/client`.\n *\n * @internal\n */\nexport const LIVE_EVENTS_RETRY_DELAY = 1000\n\n/**\n * Options for {@link observeLiveEvents}.\n * @internal\n */\nexport interface ObserveLiveEventsOptions {\n /** Resource to scope the underlying client and live connection to. */\n resource?: DocumentResource\n /**\n * Called when the connection fails due to a CORS misconfiguration. The\n * error is swallowed (the stream never errors; it idles until a new client\n * is emitted) so consumers can surface it as store state instead of a\n * stream error.\n */\n onCorsError: (error: unknown) => void\n}\n\n/**\n * Emits Live Content API messages for a dataset so consumers can invalidate\n * fetched data via sync tags.\n *\n * All consumers observe the same underlying EventSource connection:\n * `@sanity/client` caches the live-events stream per URL and connection\n * options, and every subscription made through this function uses the same\n * API version and request tag.\n *\n * @internal\n */\nexport function observeLiveEvents(\n instance: SanityInstance,\n {resource, onCorsError}: ObserveLiveEventsOptions,\n): Observable<LiveEventMessage> {\n return getClientState(instance, {\n apiVersion: LIVE_EVENTS_API_VERSION,\n resource,\n }).observable.pipe(\n switchMap((client) =>\n defer(() =>\n client.live.events({includeDrafts: !!client.config().token, tag: 'live-events'}),\n ).pipe(\n catchError((error) => {\n if (error instanceof CorsOriginError) {\n // Swallow CORS errors without bubbling up so that they can be\n // handled by the consumer (e.g. shown via the Cors Error component)\n onCorsError(error)\n return EMPTY\n }\n if (error instanceof DisconnectError) {\n // The server explicitly told this client to stop reconnecting —\n // end live updates without erroring (consumers keep serving data,\n // they just stop receiving live invalidation)\n return EMPTY\n }\n if (\n error instanceof ConnectionFailedError &&\n typeof error.status === 'number' &&\n error.status >= 400 &&\n error.status < 500\n ) {\n // The server rejected the connection with a 4xx (e.g. a 401 from\n // an expired token) — it will keep rejecting, so retrying would\n // reconnect once per second forever. End live updates like a\n // DisconnectError. A ConnectionFailedError without a status stays\n // retryable: it may be a transient network failure.\n return EMPTY\n }\n throw error\n }),\n // Server-initiated live errors (e.g. ChannelError, MessageError) are\n // retried, not surfaced. Dropped connections are already reconnected\n // inside @sanity/client.\n retry({delay: LIVE_EVENTS_RETRY_DELAY}),\n ),\n ),\n filter((e): e is LiveEventMessage => e.type === 'message'),\n )\n}\n","import {type ReleaseDocument, type SyncTag} from '@sanity/client'\nimport {\n BehaviorSubject,\n combineLatest,\n distinctUntilChanged,\n filter,\n map,\n type Observable,\n startWith,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {observeLiveEvents} from '../client/liveEvents'\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\n\nconst RELEASES_QUERY = 'releases::all()'\nconst RELEASES_API_VERSION = 'v2025-05-06'\n\n/**\n * Options for {@link observeReleases}.\n * @internal\n */\nexport interface ObserveReleasesOptions {\n /** Resource to scope the underlying client and live connection to. */\n resource?: DocumentResource\n /**\n * Called when the live events connection fails due to a CORS\n * misconfiguration. See `observeLiveEvents`.\n */\n onCorsError: (error: unknown) => void\n}\n\n/**\n * Emits every release document (including archived and published), refetching\n * whenever a Live Content API event matches the query's sync tags.\n *\n * This fetches directly through the client store — deliberately not via the\n * query store — because release data is itself an input to query perspective\n * resolution (`getPerspectiveState`); fetching through the query store would\n * make the two stores mutually dependent.\n *\n * @internal\n */\nexport function observeReleases(\n instance: SanityInstance,\n {resource, onCorsError}: ObserveReleasesOptions,\n): Observable<ReleaseDocument[] | undefined> {\n const client$ = getClientState(instance, {\n apiVersion: RELEASES_API_VERSION,\n resource,\n }).observable\n\n return client$.pipe(\n switchMap((client) => {\n const syncTags$ = new BehaviorSubject<SyncTag[] | undefined>(undefined)\n\n // Pair the latest live message with the latest known sync tags so that\n // a message arriving while a fetch is still in flight is re-evaluated\n // (and refetched if it matches) once that fetch's tags land — instead\n // of being dropped and leaving the store stale until the next event.\n const refetchEventId$ = combineLatest([\n observeLiveEvents(instance, {resource, onCorsError}).pipe(startWith(undefined)),\n syncTags$,\n ]).pipe(\n filter(\n ([message, syncTags]) =>\n message === undefined || message.tags.some((tag) => syncTags?.includes(tag)),\n ),\n map(([message]) => message?.id),\n distinctUntilChanged(),\n )\n\n return refetchEventId$.pipe(\n switchMap((lastLiveEventId) =>\n client.observable.fetch<ReleaseDocument[]>(\n RELEASES_QUERY,\n {},\n {\n perspective: 'raw',\n filterResponse: false,\n returnQuery: false,\n lastLiveEventId,\n tag: 'releases',\n },\n ),\n ),\n tap((response) => {\n syncTags$.next(response.syncTags)\n }),\n map((response) => response.result),\n )\n }),\n // Emit synchronously on subscribe (mirroring StateSource semantics, which\n // the releases store previously consumed) so the store can immediately\n // record an empty list: consumers distinguish \"no releases yet\" from\n // \"never loaded\", and the React hooks don't suspend on first load.\n startWith(undefined),\n )\n}\n","import {type ReleaseDocument} from '@sanity/client'\n\n// mirrors the order of the releases in the releases list in Studio\n// https://github.com/sanity-io/sanity/blob/main/packages/sanity/src/core/releases/hooks/utils.ts\nexport function sortReleases(releases: ReleaseDocument[] = []): ReleaseDocument[] {\n // The order should always be:\n // [undecided (sortByCreatedAt), scheduled(sortBy publishAt || metadata.intendedPublishAt), asap(sortByCreatedAt)]\n return [...releases].sort((a, b) => {\n // metadata is optional on a release document; guard against releases that\n // have no metadata at all so sorting can't throw and wipe out the list.\n const aType = a.metadata?.releaseType\n const bType = b.metadata?.releaseType\n\n // undecided are always first, then by createdAt descending\n if (aType === 'undecided' && bType !== 'undecided') {\n return -1\n }\n if (aType !== 'undecided' && bType === 'undecided') {\n return 1\n }\n if (aType === 'undecided' && bType === 'undecided') {\n // Sort by createdAt\n return new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime()\n }\n\n // Scheduled are always at the middle, then by publishAt descending\n if (aType === 'scheduled' && bType === 'scheduled') {\n const aPublishAt = a['publishAt'] || a.metadata?.['intendedPublishAt']\n if (!aPublishAt) {\n return 1\n }\n const bPublishAt = b['publishAt'] || b.metadata?.['intendedPublishAt']\n if (!bPublishAt) {\n return -1\n }\n return new Date(bPublishAt).getTime() - new Date(aPublishAt).getTime()\n }\n\n // ASAP are always last, then by createdAt descending\n if (aType === 'asap' && bType !== 'asap') {\n return 1\n }\n if (aType !== 'asap' && bType === 'asap') {\n return -1\n }\n if (aType === 'asap' && bType === 'asap') {\n // Sort by createdAt\n return new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime()\n }\n\n return 0\n })\n}\n","import {type ReleaseDocument} from '@sanity/client'\nimport {map} from 'rxjs'\n\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {observeReleases} from './observeReleases'\nimport {sortReleases} from './utils/sortReleases'\n\nconst ARCHIVED_RELEASE_STATES = ['archived', 'published']\nconst STABLE_EMPTY_RELEASES: ReleaseDocument[] = []\n\n/**\n * Lifecycle states a release document can be in. Mirrors the server's\n * `ReleaseState`.\n * @beta\n */\nexport type ReleaseState =\n | 'active'\n | 'archiving'\n | 'unarchiving'\n | 'archived'\n | 'published'\n | 'publishing'\n | 'scheduled'\n | 'scheduling'\n\nexport interface ReleasesStoreState {\n activeReleases?: ReleaseDocument[]\n allReleases?: ReleaseDocument[]\n error?: unknown\n}\n\nexport const releasesStore = defineStore<ReleasesStoreState, BoundResourceKey>({\n name: 'Releases',\n getInitialState: (): ReleasesStoreState => ({\n activeReleases: undefined,\n allReleases: undefined,\n }),\n initialize: (context) => {\n const subscription = subscribeToReleases(context)\n return () => subscription.unsubscribe()\n },\n})\n\n/**\n * Get the active releases from the store.\n * @internal\n */\nconst _getActiveReleasesState = bindActionByResource(\n releasesStore,\n createStateSourceAction({\n selector: ({state}) => {\n if (state.error) throw state.error\n return state.activeReleases\n },\n }),\n)\n\n/**\n * Get the active releases from the store.\n * @internal\n */\nexport const getActiveReleasesState = (\n instance: SanityInstance,\n options?: {resource?: DocumentResource},\n): StateSource<ReleaseDocument[] | undefined> =>\n // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options\n _getActiveReleasesState(instance, options ?? {})\n\n/**\n * Get every release in the store, including archived and published.\n * @internal\n */\nconst _getAllReleasesState = bindActionByResource(\n releasesStore,\n createStateSourceAction({\n selector: ({state}) => {\n if (state.error) throw state.error\n return state.allReleases\n },\n }),\n)\n\n/**\n * Get every release in the store, including archived and published.\n * @internal\n */\nexport const getAllReleasesState = (\n instance: SanityInstance,\n options?: {resource?: DocumentResource},\n): StateSource<ReleaseDocument[] | undefined> => _getAllReleasesState(instance, options ?? {})\n\nconst subscribeToReleases = ({\n instance,\n state,\n key: {resource},\n}: StoreContext<ReleasesStoreState, BoundResourceKey>) => {\n const releases$ = observeReleases(instance, {\n resource,\n // Surface CORS errors as store state instead of erroring the stream so\n // they can be handled by the Cors Error component\n onCorsError: (error) => state.set('setError', {error}),\n })\n return (\n releases$\n .pipe(\n map((releases) => {\n // logic here mirrors that of studio:\n // https://github.com/sanity-io/sanity/blob/156e8fa482703d99219f08da7bacb384517f1513/packages/sanity/src/core/releases/store/useActiveReleases.ts#L29\n const sorted = sortReleases(releases ?? STABLE_EMPTY_RELEASES).reverse()\n state.set('setReleases', {\n allReleases: sorted,\n activeReleases: sorted.filter(\n (release) => !ARCHIVED_RELEASE_STATES.includes(release.state),\n ),\n })\n }),\n )\n // live-connection errors are already retried inside observeLiveEvents;\n // a fetch error is terminal and surfaced via setError\n .subscribe({error: (error) => state.set('setError', {error})})\n )\n}\n","import {createSelector} from 'reselect'\n\nimport {type DocumentResource, type PerspectiveHandle} from '../config/sanityConfig'\nimport {bindActionByResource, type BoundStoreAction} from '../store/createActionBinder'\nimport {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'\nimport {releasesStore, type ReleasesStoreState} from './releasesStore'\nimport {isReleasePerspective} from './utils/isReleasePerspective'\nimport {sortReleases} from './utils/sortReleases'\n\nconst DEFAULT_PERSPECTIVE = 'drafts'\n\n// Cache for options\nconst optionsCache = new Map<string, Map<string, PerspectiveHandle>>()\n\nconst selectInstancePerspective = (context: SelectorContext<ReleasesStoreState>, _?: unknown) =>\n context.instance.config.perspective\nconst selectActiveReleases = (context: SelectorContext<ReleasesStoreState>) =>\n context.state.activeReleases\nconst selectOptions = (\n _context: SelectorContext<ReleasesStoreState>,\n options: PerspectiveHandle & {projectId?: string; dataset?: string; resource?: DocumentResource},\n) => options\n\nconst memoizedOptionsSelector = createSelector(\n [selectActiveReleases, selectOptions],\n (activeReleases, options) => {\n if (!options || !activeReleases) return options\n\n // Use release document IDs as the cache key\n const releaseIds = activeReleases.map((release) => release._id).join(',')\n let nestedCache = optionsCache.get(releaseIds)\n if (!nestedCache) {\n nestedCache = new Map<string, PerspectiveHandle>()\n optionsCache.set(releaseIds, nestedCache)\n }\n\n const optionsKey = JSON.stringify(options)\n let cachedOptions = nestedCache.get(optionsKey)\n\n if (!cachedOptions) {\n cachedOptions = options\n nestedCache.set(optionsKey, cachedOptions)\n }\n return cachedOptions\n },\n)\n\nconst _getPerspectiveStateSelector = createStateSourceAction({\n selector: createSelector(\n [selectInstancePerspective, selectActiveReleases, memoizedOptionsSelector],\n (instancePerspective, activeReleases, memoizedOptions) => {\n const perspective = memoizedOptions?.perspective ?? instancePerspective ?? DEFAULT_PERSPECTIVE\n\n if (!isReleasePerspective(perspective)) return perspective\n\n // if there are no active releases we can't compute the release perspective\n if (!activeReleases || activeReleases.length === 0) return undefined\n\n const releaseNames = sortReleases(activeReleases).map((release) => release.name)\n const index = releaseNames.findIndex((name) => name === perspective.releaseName)\n\n if (index < 0) {\n throw new Error(`Release \"${perspective.releaseName}\" not found in active releases`)\n }\n\n const filteredReleases = releaseNames.slice(0, index + 1) // Include the release itself\n\n return ['drafts', ...filteredReleases]\n .filter((name) => !perspective.excludedPerspectives?.includes(name))\n .reverse()\n },\n ),\n})\n\ntype OmitFirst<T extends unknown[]> = T extends [unknown, ...infer R] ? R : never\ntype SelectorParams = OmitFirst<Parameters<typeof _getPerspectiveStateSelector>>\ntype BoundGetPerspectiveState = BoundStoreAction<\n ReleasesStoreState,\n SelectorParams,\n ReturnType<typeof _getPerspectiveStateSelector>\n>\n\nconst _boundGetPerspectiveState = bindActionByResource(\n releasesStore,\n _getPerspectiveStateSelector,\n) as BoundGetPerspectiveState\n\n/**\n * Provides a subscribable state source for a \"perspective\" for the Sanity client,\n * which is used to fetch documents as though certain Content Releases are active.\n *\n * @param instance - The Sanity instance to get the perspective for\n * @param options - The options for the perspective -- usually a release name\n *\n * @returns A subscribable perspective value, usually a list of applicable release names,\n * or a single release name / default perspective (such as 'drafts').\n *\n * @public\n */\nexport const getPerspectiveState: BoundGetPerspectiveState = (instance, ...rest) =>\n // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options\n _boundGetPerspectiveState(instance, ...(rest.length ? rest : [{}]))\n","/**\n * When a query has no more subscribers, its state is cleaned up and removed\n * from the store. A delay used to prevent re-creating resources when the last\n * subscriber is removed quickly before another one is added. This is helpful\n * when used in a frontend where components may suspend or transition to\n * different views quickly.\n */\nexport const QUERY_STATE_CLEAR_DELAY = 1000\nexport const QUERY_STORE_API_VERSION = 'v2025-05-06'\nexport const QUERY_STORE_DEFAULT_PERSPECTIVE = 'drafts'\n","import {omitProperty} from '../utils/object'\n\ninterface QueryState {\n syncTags?: string[]\n result?: unknown\n error?: unknown\n lastLiveEventId?: string\n subscribers: string[]\n}\n\nexport interface QueryStoreState {\n queries: {[key: string]: QueryState | undefined}\n error?: unknown\n}\n\nexport const setQueryError =\n (key: string, error: unknown) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, error}}}\n }\n\nexport const setQueryData =\n (key: string, result: unknown, syncTags?: string[]) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n return {\n ...prev,\n queries: {...prev.queries, [key]: {...prevQuery, result: result ?? null, syncTags}},\n }\n }\n\nexport const setLastLiveEventId =\n (key: string, lastLiveEventId: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, lastLiveEventId}}}\n }\n\nexport const addSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n const subscribers = [...(prevQuery?.subscribers ?? []), subscriptionId]\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}\n }\n\nexport const removeSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId)\n if (!subscribers.length) return {...prev, queries: omitProperty(prev.queries, key)}\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}\n }\n","import {type ResponseQueryOptions} from '@sanity/client'\nimport {\n catchError,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n NEVER,\n Observable,\n of,\n pairwise,\n race,\n share,\n startWith,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {observeLiveEvents} from '../client/liveEvents'\nimport {type DatasetHandle} from '../config/sanityConfig'\nimport {getPerspectiveState} from '../releases/getPerspectiveState'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {type StoreState} from '../store/createStoreState'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {type ResolveQueryResult} from '../typegen/resolve'\nimport {randomId} from '../utils/ids'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {\n QUERY_STATE_CLEAR_DELAY,\n QUERY_STORE_API_VERSION,\n QUERY_STORE_DEFAULT_PERSPECTIVE,\n} from './queryStoreConstants'\nimport {\n addSubscriber,\n type QueryStoreState,\n removeSubscriber,\n setLastLiveEventId,\n setQueryData,\n setQueryError,\n} from './reducers'\n\n/**\n * @beta\n */\nexport interface QueryOptions<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>\n extends\n Pick<ResponseQueryOptions, 'useCdn' | 'cache' | 'next' | 'cacheMode' | 'tag'>,\n DatasetHandle<TDataset, TProjectId> {\n query: TQuery\n params?: Record<string, unknown>\n}\n\n/**\n * @beta\n */\nexport interface ResolveQueryOptions<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends QueryOptions<TQuery, TDataset, TProjectId> {\n signal?: AbortSignal\n}\n\nconst EMPTY_ARRAY: never[] = []\n\n/** @internal */\nexport const getQueryKey = (instance: SanityInstance, options: QueryOptions): string =>\n JSON.stringify(normalizeOptionsWithPerspective(instance, options))\n/** @internal */\nexport const parseQueryKey = (key: string): QueryOptions => JSON.parse(key)\n\n/**\n * Ensures the query key includes an effective perspective so that\n * implicit differences (e.g. different instance.config.perspective)\n * don't collide in the dataset-scoped store.\n *\n * Since perspectives are unique, we can depend on the release stacks\n * to be correct when we retrieve the results.\n *\n */\nfunction normalizeOptionsWithPerspective(\n instance: SanityInstance,\n options: QueryOptions,\n): QueryOptions {\n if (options.perspective !== undefined) return options\n const instancePerspective = instance.config.perspective\n return {\n ...options,\n perspective:\n instancePerspective !== undefined ? instancePerspective : QUERY_STORE_DEFAULT_PERSPECTIVE,\n }\n}\n\nconst queryStore = defineStore<QueryStoreState, BoundResourceKey>({\n name: 'QueryStore',\n getInitialState: () => ({queries: {}}),\n initialize(context) {\n const subscriptions = [\n listenForNewSubscribersAndFetch(context),\n listenToLiveClientAndSetLastLiveEventIds(context),\n ]\n\n return () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe()\n }\n }\n },\n})\n\nconst errorHandler = (state: StoreState<{error?: unknown}>) => {\n return (error: unknown): void => state.set('setError', {error})\n}\n\nconst listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QueryStoreState>) => {\n return state.observable\n .pipe(\n map((s) => new Set(Object.keys(s.queries))),\n distinctUntilChanged((curr, next) => {\n if (curr.size !== next.size) return false\n return Array.from(next).every((i) => curr.has(i))\n }),\n startWith(new Set<string>()),\n pairwise(),\n mergeMap(([curr, next]) => {\n const added = Array.from(next).filter((i) => !curr.has(i))\n const removed = Array.from(curr).filter((i) => !next.has(i))\n\n return [\n ...added.map((key) => ({key, added: true})),\n ...removed.map((key) => ({key, added: false})),\n ]\n }),\n groupBy((i) => i.key),\n mergeMap((group$) =>\n group$.pipe(\n switchMap((e) => {\n if (!e.added) return EMPTY\n\n const lastLiveEventId$ = state.observable.pipe(\n map((s) => s.queries[group$.key]?.lastLiveEventId),\n distinctUntilChanged(),\n )\n const {\n query,\n params,\n projectId,\n dataset,\n tag,\n resource,\n perspective: perspectiveFromOptions,\n ...restOptions\n } = parseQueryKey(group$.key)\n\n // Short-circuit perspective resolution for non-release perspectives to avoid\n // touching the releases store (and its initialization) unnecessarily.\n const perspective$ = isReleasePerspective(perspectiveFromOptions)\n ? getPerspectiveState(instance, {\n perspective: perspectiveFromOptions,\n resource,\n }).observable.pipe(filter(Boolean))\n : of(perspectiveFromOptions ?? QUERY_STORE_DEFAULT_PERSPECTIVE)\n\n const client$ = getClientState(instance, {\n apiVersion: QUERY_STORE_API_VERSION,\n projectId,\n dataset,\n resource,\n }).observable\n\n return combineLatest({\n lastLiveEventId: lastLiveEventId$,\n client: client$,\n perspective: perspective$,\n }).pipe(\n switchMap(({lastLiveEventId, client, perspective}) =>\n client.observable.fetch(query, params, {\n ...restOptions,\n perspective,\n filterResponse: false,\n returnQuery: false,\n lastLiveEventId,\n tag,\n }),\n ),\n tap(({result, syncTags}) => {\n state.set('setQueryData', setQueryData(group$.key, result, syncTags))\n }),\n // Catch inside the per-event stream: erroring the group pipe would\n // complete the group's subscription, and since `groupBy` above never\n // removes its group subjects, re-adding the key would emit into a\n // subject with no subscribers — the key could never be fetched again.\n catchError((error) => {\n state.set('setQueryError', setQueryError(group$.key, error))\n return EMPTY\n }),\n )\n }),\n ),\n ),\n )\n .subscribe({error: errorHandler(state)})\n}\n\nconst listenToLiveClientAndSetLastLiveEventIds = ({\n state,\n instance,\n key: {resource},\n}: StoreContext<QueryStoreState, BoundResourceKey>) => {\n const liveMessages$ = observeLiveEvents(instance, {\n resource,\n // Surface CORS errors as store state (handled by the Cors Error\n // component) instead of erroring the stream: a stream error here would\n // reach the outer subscription's errorHandler, set the store-wide\n // `state.error`, and permanently brick every query selector.\n onCorsError: (error) => state.set('setError', {error}),\n }).pipe(share())\n\n return state.observable\n .pipe(\n mergeMap((s) => Object.entries(s.queries)),\n groupBy(([key]) => key),\n mergeMap((group$) => {\n const syncTags$ = group$.pipe(\n map(([, queryState]) => queryState),\n map((i) => i?.syncTags ?? EMPTY_ARRAY),\n distinctUntilChanged(),\n )\n\n return combineLatest([liveMessages$, syncTags$]).pipe(\n filter(([message, syncTags]) => message.tags.some((tag) => syncTags.includes(tag))),\n tap(([message]) => {\n state.set('setLastLiveEventId', setLastLiveEventId(group$.key, message.id))\n }),\n )\n }),\n )\n .subscribe({error: errorHandler(state)})\n}\n\n/**\n * Returns the state source for a query.\n *\n * This function returns a state source that represents the current result of a GROQ query.\n * Subscribing to the state source will instruct the SDK to fetch the query (if not already fetched)\n * and will keep the query live using the Live content API (considering sync tags) to provide up-to-date results.\n * When the last subscriber is removed, the query state is automatically cleaned up from the store.\n *\n * Note: This functionality is for advanced users who want to build their own framework integrations.\n * Our SDK also provides a React integration (useQuery hook) for convenient usage.\n *\n * Note: Automatic cleanup can interfere with React Suspense because if a component suspends while being the only subscriber,\n * cleanup might occur unexpectedly. In such cases, consider using `resolveQuery` instead.\n *\n * @beta\n */\nexport function getQueryState<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n queryOptions: QueryOptions<TQuery, TDataset, TProjectId>,\n): StateSource<ResolveQueryResult<TQuery, `${TProjectId}.${TDataset}`> | undefined>\n\n/** @beta */\nexport function getQueryState<TData>(\n instance: SanityInstance,\n queryOptions: QueryOptions,\n): StateSource<TData | undefined>\n\n/** @beta */\nexport function getQueryState(\n instance: SanityInstance,\n queryOptions: QueryOptions,\n): StateSource<unknown>\n\n/** @beta */\nexport function getQueryState(\n ...args: Parameters<typeof _getQueryState>\n): ReturnType<typeof _getQueryState> {\n return _getQueryState(...args)\n}\nconst _getQueryState = bindActionByResource(\n queryStore,\n createStateSourceAction({\n selector: ({state, instance}: SelectorContext<QueryStoreState>, options: QueryOptions) => {\n if (state.error) throw state.error\n const key = getQueryKey(instance, options)\n const queryState = state.queries[key]\n if (queryState?.error) throw queryState.error\n return queryState?.result\n },\n onSubscribe: ({state, instance}, options: QueryOptions) => {\n const subscriptionId = randomId(16)\n const key = getQueryKey(instance, options)\n\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n return () => {\n // this runs on unsubscribe\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId)),\n QUERY_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Resolves the result of a query without registering a lasting subscriber.\n *\n * This function fetches the result of a GROQ query and returns a promise that resolves with the query result.\n * It registers a temporary subscriber for the lifetime of the resolution (released shortly after the promise\n * settles), so the query state participates in normal cleanup even when no lasting subscriber ever attaches —\n * e.g. when a suspended component errors before committing. This makes it ideal for use with React Suspense,\n * where the returned promise is thrown to delay rendering until the query result becomes available.\n * Once the promise resolves, it is expected that a real subscriber will be added via `getQueryState` to manage ongoing updates.\n *\n * Additionally, an optional AbortSignal can be provided to cancel the query and immediately clear the associated state\n * if there are no other active subscribers.\n *\n * @beta\n */\nexport function resolveQuery<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n queryOptions: ResolveQueryOptions<TQuery, TDataset, TProjectId>,\n): Promise<ResolveQueryResult<TQuery, `${TProjectId}.${TDataset}`>>\n\n/** @beta */\nexport function resolveQuery<TData>(\n instance: SanityInstance,\n queryOptions: ResolveQueryOptions,\n): Promise<TData>\n/** @beta */\nexport function resolveQuery(...args: Parameters<typeof _resolveQuery>): Promise<unknown> {\n return _resolveQuery(...args)\n}\nconst _resolveQuery = bindActionByResource(\n queryStore,\n ({state, instance}, {signal, ...options}: ResolveQueryOptions) => {\n const normalized = normalizeOptionsWithPerspective(instance, options)\n const {getCurrent} = getQueryState(instance, normalized)\n const key = getQueryKey(instance, normalized)\n\n // Hold a temporary subscription for the lifetime of this resolution so the\n // key participates in normal cleanup. Without one, a fetch that errors\n // while a component is suspended leaves a subscriber-less key behind — the\n // component never commits, so no other subscriber exists — and its stored\n // error would be rethrown on every future mount without ever refetching.\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n const aborted$ = signal\n ? new Observable<void>((observer) => {\n const cleanup = () => {\n signal.removeEventListener('abort', listener)\n }\n\n const listener = () => {\n observer.error(new DOMException('The operation was aborted.', 'AbortError'))\n observer.complete()\n cleanup()\n }\n signal.addEventListener('abort', listener)\n\n return cleanup\n }).pipe(\n catchError((error) => {\n if (error instanceof Error && error.name === 'AbortError') {\n // Release immediately (not after the clear delay) so that when\n // this was the only subscriber, the key removal tears down the\n // in-flight request right away — e.g. when useQuery switches\n // to a different query\n state.set('removeSubscriber', removeSubscriber(key, subscriptionId))\n }\n throw error\n }),\n )\n : NEVER\n\n const resolved$ = state.observable.pipe(\n map(getCurrent),\n first((i) => i !== undefined),\n )\n\n const promise = firstValueFrom(race([resolved$, aborted$]))\n const releaseSubscriber = () => {\n setCleanupTimeout(() => {\n state.set('removeSubscriber', removeSubscriber(key, subscriptionId))\n }, QUERY_STATE_CLEAR_DELAY)\n }\n promise.then(releaseSubscriber, releaseSubscriber)\n return promise\n },\n)\n","","import {version} from '../package.json'\nimport {getEnv} from './utils/getEnv'\n\n/**\n * This version is provided by pkg-utils at build time\n * @internal\n */\nexport const CORE_SDK_VERSION = getEnv('PKG_VERSION') || `${version}-development`\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"version-BKdIDRP1.js","names":["addSubscriber","removeSubscriber","addSubscriber","removeSubscriber"],"sources":["../src/utils/ids.ts","../src/store/fetcherStore.ts","../src/comments/commentsConstants.ts","../src/comments/addonDatasetStore.ts","../src/comments/commentFieldPath.ts","../src/comments/buildCommentThreads.ts","../src/comments/normalizeComment.ts","../src/comments/observeComments.ts","../src/comments/reducers.ts","../src/comments/commentsStore.ts","../src/users/usersConstants.ts","../src/users/reducers.ts","../src/client/liveEvents.ts","../src/releases/observeReleases.ts","../src/releases/utils/sortReleases.ts","../src/releases/releasesStore.ts","../src/releases/getPerspectiveState.ts","../src/query/queryStoreConstants.ts","../src/query/reducers.ts","../src/query/queryStore.ts","../package.json","../src/version.ts"],"sourcesContent":["const ID_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n\n/**\n * Generates a random ID of the given length from the alphabet `[a-zA-Z0-9]`, the same shape\n * `@sanity/client` and Sanity Studio use for short IDs such as release IDs.\n *\n * Ported from `@sanity/client`'s `generateReleaseId`, which uses rejection sampling to keep\n * the output unbiased. Uses `crypto.getRandomValues`, which unlike `crypto.randomUUID` is\n * also available on insecure origins (plain http).\n *\n * @internal\n */\nexport function randomId(length: number = 8): string {\n let id = ''\n while (id.length < length) {\n const bytes = crypto.getRandomValues(new Uint8Array(length - id.length))\n for (const byte of bytes) {\n const index = byte & 63\n if (index < ID_ALPHABET.length) id += ID_ALPHABET.charAt(index)\n }\n }\n return id\n}\n\n/**\n * Generates a random v4 UUID.\n *\n * Prefers the native `crypto.randomUUID`, which is only available in secure contexts\n * (https, localhost). Falls back to deriving the UUID from `crypto.getRandomValues`,\n * which has no such restriction, so ID generation also works when an app is served over\n * plain http, e.g. when testing from a phone on a local network.\n *\n * @internal\n */\nexport function randomUuid(): string {\n // eslint-disable-next-line no-restricted-properties -- this is the sanctioned wrapper around crypto.randomUUID\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID()\n\n const bytes = crypto.getRandomValues(new Uint8Array(16))\n const hex = Array.from(bytes, (byte, index) => {\n // Set the version (4) and variant (RFC 4122) bits\n if (index === 6) byte = (byte & 0x0f) | 0x40\n if (index === 8) byte = (byte & 0x3f) | 0x80\n return byte.toString(16).padStart(2, '0')\n }).join('')\n\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n","import {filter, firstValueFrom, map, type Observable, Subscription, take} from 'rxjs'\n\nimport {setCleanupInterval, setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {bindActionGlobally} from './createActionBinder'\nimport {type SanityInstance} from './createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from './createStateSourceAction'\nimport {type StoreState} from './createStoreState'\nimport {defineStore, type StoreContext} from './defineStore'\n\nconst DEFAULT_STALE_TIME = 30_000\nconst DEFAULT_GC_TIME = 300_000\n\n/**\n * Identifies cached data for invalidation, matched across the whole store.\n * A fetcher declares the tags each of its entries carries (via\n * {@link FetcherDefinition.tags}); a mutation declares the tags it invalidates\n * (via {@link MutationDefinition.invalidates}).\n *\n * When invalidating,`{type: 'organization'}` matches every entry carrying any `organization` tag;\n * `{type: 'organization', id: 'abc'}` matches only entries carrying that exact pair.\n * List entries carry a `{type, id: 'LIST'}` sentinel plus one tag per item, so a\n * \"create\" mutation can refetch lists without touching unrelated detail entries.\n *\n * @internal\n */\nexport interface CacheTag {\n type: string\n /** Specific id, or the `'LIST'` sentinel for list entries */\n id?: string | number\n}\n\n/**\n * The reactive value of one cache entry, as exposed by\n * {@link Fetcher.getState}. React hooks consume this envelope directly.\n * Discriminated on `status`, so narrowing gives correctly-typed `data`:\n * `'success'` is the only arm carrying data, and a background failure while\n * data still renders surfaces on the `'success'` arm's `error`.\n *\n * `isFetching` is common to all arms: a fetch for this entry is in flight\n * (initial load, revalidation, or poll).\n *\n * @internal\n */\nexport type FetcherSnapshot<TData> =\n | {\n /** No data yet — initial fetch in flight or not started */\n status: 'pending'\n data: undefined\n error: undefined\n isFetching: boolean\n dataUpdatedAt: undefined\n }\n | {\n /** Data available — possibly stale, possibly revalidating */\n status: 'success'\n data: TData\n /** Most recent background-fetch failure, cleared by the next successful fetch */\n error: unknown\n isFetching: boolean\n /** Epoch ms of the fetch that produced `data` */\n dataUpdatedAt: number\n }\n | {\n /** The last fetch failed and no data exists to serve */\n status: 'error'\n data: undefined\n error: unknown\n isFetching: boolean\n dataUpdatedAt: undefined\n }\n\n/**\n * A typed description of one cached, parameterised read of an API response —\n * here is a single cache per {@link SanityInstance}; every definition registers\n * into it and its `name` namespaces its keys. A list and a detail read are two\n * definitions over the one cache, not two caches.\n *\n * Entry lifecycle: fresh → stale → collected. Fresh entries are served from\n * cache with no fetch; stale entries are served immediately and revalidated in\n * the background (stale-while-revalidate); collected entries are removed once\n * their `gcTime` elapses after the last unsubscribe, so the next subscriber\n * suspends.\n *\n * @internal\n */\nexport interface FetcherDefinition<TParams extends unknown[], TData> {\n /** Namespaces this fetcher's keys in the shared cache; used for devtools and request-tag derivation */\n name: string\n /**\n * Fetches the data once. Return an observable that emits the result and\n * completes.\n */\n fetch: (instance: SanityInstance) => (...params: TParams) => Observable<TData>\n /** Converts params into the cache key that identifies an entry (unique within `name`) */\n getKey: (instance: SanityInstance, ...params: TParams) => string\n /**\n * Cache tags each entry carries, for cache-wide invalidation. A static array,\n * or a function of the fetched data when tags depend on the response (e.g. one\n * tag per list item).\n */\n tags?: CacheTag[] | ((data: TData, ...params: TParams) => CacheTag[])\n /**\n * How long fetched data is fresh, in ms. Fresh entries never refetch;\n * stale entries serve cached data and revalidate in the background.\n * @defaultValue 30_000\n */\n staleTime?: number\n /**\n * How long an entry survives after its last unsubscribe before it is garbage\n * collected, in ms. The clock starts at the last unsubscribe — entries with\n * active subscribers are never collected.\n * @defaultValue 300_000\n */\n gcTime?: number\n /**\n * Refetch on this interval (ms) while the entry has at least one subscriber,\n * independent of staleness. Ticks are skipped while `document.hidden`. Off by default.\n */\n refetchInterval?: number\n /**\n * Consulted once, when an entry is being created and no cached data exists for\n * it. Return data pulled from another fetcher in the same cache — read via its\n * ordinary `getState(instance, ...).getCurrent()`, e.g. a detail entry pulling\n * its item out of the cached list — to skip or soften the initial fetch.\n * `dataUpdatedAt` must be when the source data was fetched, not \"now\": staleness\n * is measured from it. Return `undefined` to fetch as normal.\n */\n initialData?: (\n instance: SanityInstance,\n ...params: TParams\n ) => {data: TData; dataUpdatedAt: number} | undefined\n}\n\n/**\n * A typed handle over the shared fetcher cache, returned by\n * {@link defineFetcher}. It is a view onto the entries under its definition's\n * `name`, not a cache of its own.\n *\n * @internal\n */\nexport interface Fetcher<TParams extends unknown[], TData> {\n /** Reactive envelope for one entry. Subscribing triggers fetching per staleness rules. */\n getState: (instance: SanityInstance, ...params: TParams) => StateSource<FetcherSnapshot<TData>>\n /**\n * Resolves with the entry's first available data — the promise a suspending\n * component throws. Rejects if the initial fetch fails.\n */\n resolveState: (instance: SanityInstance, ...params: TParams) => Promise<TData>\n /**\n * Marks the entry stale. If it has active subscribers it refetches immediately;\n * otherwise it refetches on next subscribe.\n */\n invalidate: (instance: SanityInstance, ...params: TParams) => void\n /** {@link Fetcher.invalidate} for every entry under this fetcher's `name`. */\n invalidateAll: (instance: SanityInstance) => void\n /**\n * Imperative fetch, bypassing staleness. Dedupes against an in-flight fetch.\n * Resolves with the entry's data as held by the cache once the fetch settles.\n */\n refetch: (instance: SanityInstance, ...params: TParams) => Promise<TData>\n /**\n * Writes an entry's data directly. Returns an undo handle — inside a\n * mutation's `onMutate` these writes are recorded and rolled back automatically\n * on failure.\n */\n setData: (\n instance: SanityInstance,\n params: TParams,\n updater: TData | ((current: TData | undefined) => TData),\n ) => {undo: () => void}\n}\n\n/**\n * Writes an entry's data in any fetcher. Passed to {@link MutationDefinition}\n * callbacks — mutations are the only holders of arbitrary write access; read\n * fetchers pull their data (see {@link FetcherDefinition.initialData}). Inside\n * `onMutate`, every write is recorded and undone automatically if the mutation\n * fails.\n *\n * @internal\n */\nexport type CacheWriter = <TParams extends unknown[], TData>(\n fetcher: Fetcher<TParams, TData>,\n params: TParams,\n updater: TData | ((current: TData | undefined) => TData),\n) => void\n\n/**\n * Resolves when the mutation settles on the server. `invalidated` tracks the\n * background reconciliation — the refetches triggered by\n * {@link MutationDefinition.invalidates} — for the rare caller that must wait for\n * full reconvergence rather than server settlement.\n *\n * @internal\n */\nexport interface MutationResult<TResult> {\n /** The server response */\n data: TResult\n /** Settles when the refetches of matched active entries complete */\n invalidated: Promise<void>\n}\n\n/**\n * Defines a mutation with optimistic cache updates. Lifecycle:\n *\n * 1. `onMutate` applies optimistic writes synchronously — the UI updates before\n * the request leaves. Each entry carries a version counter that increments on\n * every write. An optimistic write bumps the counter, so a revalidation already\n * in flight lands \"out of date\" and is discarded rather than clobbering the\n * optimistic write.\n * 2. The request runs. On failure, recorded writes are rolled back automatically\n * — the cache is restored exactly, so failure ends the lifecycle here.\n * 3. On success, `onSuccess` may write the server response into the cache directly.\n * 4. On success, `invalidates` tags mark matching entries across the cache\n * stale (active entries refetch), so the cache reconverges on server truth\n * even if the optimistic shape was wrong.\n *\n * @internal\n */\nexport interface MutationDefinition<TInput, TResult> {\n /** Unique mutation name, used for devtools and request-tag derivation */\n name: string\n /** Creates the observable that performs the server mutation */\n mutationFn: (instance: SanityInstance) => (input: TInput) => Observable<TResult>\n /** Optimistic cache writes, applied synchronously before the request */\n onMutate?: (write: CacheWriter, input: TInput) => void\n /** Reconcile the cache with the server response, avoiding a refetch round-trip */\n onSuccess?: (write: CacheWriter, result: TResult, input: TInput) => void\n /**\n * Cache tags to invalidate on success. Failure invalidates nothing: recorded\n * optimistic writes roll back to the exact pre-mutation cache, and any\n * server-side ambiguity (e.g. timeout after commit) heals via `staleTime`.\n * A static array, or a function of the result/input.\n */\n invalidates?: CacheTag[] | ((result: TResult, input: TInput) => CacheTag[])\n}\n\n// --- Internal cache machinery -------------------------------------------------\n\n/** Internal bookkeeping carried by every entry, on top of the public {@link FetcherSnapshot} fields. */\ninterface CacheEntryMeta {\n fetcherName: string\n /** The entry's cache key — unique across the whole cache */\n key: string\n params: unknown[]\n instance: SanityInstance\n /** Bumps on every write; a fetch whose result lands on a changed version is discarded */\n version: number\n /** Forced stale by invalidation, independent of `staleTime` */\n forcedStale: boolean\n tags: CacheTag[]\n subscriptions: number\n}\n\n/**\n * One entry in the shared cache: a {@link FetcherSnapshot} plus internal\n * bookkeeping. Discriminated on `status` like the snapshot, so `data` and\n * `dataUpdatedAt` are correctly typed per status. Generic over the fetcher's\n * `TData`; the store holds `CacheEntry` (i.e. `CacheEntry<unknown>`) because it\n * is shared across fetchers, and {@link asTyped} recovers a specific `TData` at\n * the read boundary. Immutable: replaced, never mutated in place.\n */\ntype CacheEntry<TData = unknown> = FetcherSnapshot<TData> & CacheEntryMeta\n\ninterface CacheState {\n entries: {[compoundKey: string]: CacheEntry}\n}\n\ninterface InflightFetch {\n sub: Subscription\n promise: Promise<unknown>\n resolve: (data: unknown) => void\n reject: (error: unknown) => void\n startVersion: number\n}\n\n// Erased definition type used by the machinery, which is generic-agnostic.\ntype AnyDefinition = FetcherDefinition<unknown[], unknown>\n\n// Module-level: the cache store is global (one instance across all Sanity\n// instances, keyed only by fetcher key), so this bookkeeping lives alongside it.\nconst definitions = new Map<string, AnyDefinition>()\nconst inflight = new Map<string, InflightFetch>()\nconst gcTimers = new Map<string, ReturnType<typeof setTimeout>>()\nconst intervalTimers = new Map<string, ReturnType<typeof setInterval>>()\nconst snapshotCache = new WeakMap<CacheEntry, FetcherSnapshot<unknown>>()\n\nconst PENDING_SNAPSHOT: FetcherSnapshot<never> = {\n status: 'pending',\n data: undefined,\n error: undefined,\n isFetching: false,\n dataUpdatedAt: undefined,\n}\n\n/** Builds the cache key that identifies one entry: the fetcher's name namespacing its raw key. */\nconst entryKey = (fetcherName: string, rawKey: string): string => `${fetcherName}:${rawKey}`\n\nconst computeTags = (def: AnyDefinition, data: unknown, params: unknown[]): CacheTag[] =>\n typeof def.tags === 'function' ? def.tags(data, ...params) : (def.tags ?? [])\n\n/**\n * Distinguishes an updater function from a replacement value. Sound because\n * cached data is API response data — never itself callable — so a callable\n * `updater` can only be the function arm. This lets us narrow without an `as`.\n */\nconst isUpdaterFn = <T>(\n updater: T | ((current: T | undefined) => T),\n): updater is (current: T | undefined) => T => typeof updater === 'function'\n\n/** Applies an updater — a replacement value, or a function of the current value. */\nconst applyUpdater = <T>(updater: T | ((current: T | undefined) => T), current: T | undefined): T =>\n isUpdaterFn(updater) ? updater(current) : updater\n\n/** Whether an entry should fetch on subscribe: not already fetching, and either empty or stale. */\nconst needsFetch = (entry: CacheEntry, def: AnyDefinition): boolean => {\n if (entry.isFetching) return false\n if (entry.status !== 'success') return true\n return (\n entry.forcedStale ||\n Date.now() - (entry.dataUpdatedAt ?? 0) >= (def.staleTime ?? DEFAULT_STALE_TIME)\n )\n}\n\n/**\n * Views a stored entry as the calling fetcher's typed entry. The cache holds\n * every fetcher's entries as `CacheEntry<unknown>`, but keys are namespaced by\n * fetcher name, so an entry read under a fetcher's key holds that fetcher's\n * `TData` — the one sound `unknown → TData` seam (cf. TanStack's `getQueryData`).\n */\nconst asTyped = <TData>(entry: CacheEntry): CacheEntry<TData> => entry as CacheEntry<TData>\n\n/** Snapshot for a given entry, memoised by entry identity so unrelated cache changes don't re-emit. */\nconst toSnapshot = <TData>(entry: CacheEntry<TData> | undefined): FetcherSnapshot<TData> => {\n if (!entry) return PENDING_SNAPSHOT\n const cached = snapshotCache.get(entry)\n if (cached) return cached as FetcherSnapshot<TData>\n\n let snapshot: FetcherSnapshot<TData>\n if (entry.status === 'success') {\n snapshot = {\n status: 'success',\n data: entry.data,\n error: entry.error,\n isFetching: entry.isFetching,\n dataUpdatedAt: entry.dataUpdatedAt,\n }\n } else if (entry.status === 'error') {\n snapshot = {\n status: 'error',\n data: undefined,\n error: entry.error,\n isFetching: entry.isFetching,\n dataUpdatedAt: undefined,\n }\n } else {\n snapshot = {\n status: 'pending',\n data: undefined,\n error: undefined,\n isFetching: entry.isFetching,\n dataUpdatedAt: undefined,\n }\n }\n\n snapshotCache.set(entry, snapshot)\n return snapshot\n}\n\nconst cacheStore = defineStore<CacheState>({\n name: 'FetcherCache',\n getInitialState: () => ({entries: {}}),\n initialize: () => () => {\n // The global store is disposed only when the last Sanity instance is: tear\n // down every outstanding timer and fetch so nothing leaks between lifetimes.\n for (const timer of gcTimers.values()) clearTimeout(timer)\n for (const timer of intervalTimers.values()) clearInterval(timer)\n for (const fetch of inflight.values()) fetch.sub.unsubscribe()\n gcTimers.clear()\n intervalTimers.clear()\n inflight.clear()\n },\n})\n\n/** Immutably replace (or delete, when the updater returns `undefined`) one entry. */\nconst updateEntry = (\n state: StoreState<CacheState>,\n key: string,\n actionName: string,\n updater: (prev: CacheEntry | undefined) => CacheEntry | undefined,\n): void => {\n state.set(actionName, (prev: CacheState) => {\n const next = updater(prev.entries[key])\n if (next === prev.entries[key]) return prev\n const entries = {...prev.entries}\n if (next === undefined) delete entries[key]\n else entries[key] = next\n return {entries}\n })\n}\n\nconst baseEntry = (\n def: AnyDefinition,\n key: string,\n instance: SanityInstance,\n params: unknown[],\n): CacheEntry => {\n const seeded = def.initialData?.(instance, ...params)\n if (seeded) {\n return {\n fetcherName: def.name,\n key,\n params,\n instance,\n status: 'success',\n data: seeded.data,\n error: undefined,\n dataUpdatedAt: seeded.dataUpdatedAt,\n isFetching: false,\n version: 0,\n forcedStale: false,\n tags: computeTags(def, seeded.data, params),\n subscriptions: 0,\n }\n }\n return {\n fetcherName: def.name,\n key,\n params,\n instance,\n status: 'pending',\n data: undefined,\n error: undefined,\n dataUpdatedAt: undefined,\n isFetching: false,\n version: 0,\n forcedStale: false,\n tags: [],\n subscriptions: 0,\n }\n}\n\n/**\n * Runs (or dedupes/restarts) the fetch for one entry, wiring the result back\n * into the cache. Returns a promise resolving with the entry's data once the\n * fetch settles — rejecting only when the entry has no data to serve.\n */\nconst startFetch = (\n state: StoreState<CacheState>,\n def: AnyDefinition,\n instance: SanityInstance,\n params: unknown[],\n force: boolean,\n): Promise<unknown> => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n\n const existing = inflight.get(key)\n if (existing && !force) return existing.promise\n if (existing) {\n existing.sub.unsubscribe()\n inflight.delete(key)\n }\n\n updateEntry(state, key, 'fetchStart', (prev) => ({\n ...(prev ?? baseEntry(def, key, instance, params)),\n isFetching: true,\n }))\n const startVersion = state.get().entries[key]?.version ?? 0\n\n let resolve!: (data: unknown) => void\n let reject!: (error: unknown) => void\n const promise = new Promise<unknown>((res, rej) => {\n resolve = res\n reject = rej\n })\n\n // A forced fetch pre-empted one already in flight: forward this fetch's\n // settlement to the pre-empted promise so its awaiters don't hang forever.\n if (existing) void promise.then(existing.resolve, existing.reject)\n\n // Register before subscribing so a synchronous observable (whose `next`/\n // `complete` fire during `.subscribe()`) sees — and can clean up — its own record.\n const sub = new Subscription()\n inflight.set(key, {sub, promise, resolve, reject, startVersion})\n\n sub.add(\n def\n .fetch(instance)(...params)\n .pipe(take(1))\n .subscribe({\n next: (data) => {\n const current = state.get().entries[key]\n if (current && current.version !== startVersion) {\n // A write (optimistic mutation) landed mid-fetch: discard the now-stale\n // server result and keep the written data. Tear the fetch down here —\n // a non-completing source won't fire `complete` to clear `isFetching`.\n inflight.delete(key)\n if (current.isFetching) {\n updateEntry(\n state,\n key,\n 'fetchDiscard',\n (prev) => prev && {...prev, isFetching: false},\n )\n }\n resolve(current.data)\n sub.unsubscribe()\n return\n }\n updateEntry(state, key, 'fetchSuccess', (prev) => ({\n ...(prev ?? baseEntry(def, key, instance, params)),\n status: 'success',\n data,\n error: undefined,\n dataUpdatedAt: Date.now(),\n isFetching: false,\n forcedStale: false,\n tags: computeTags(def, data, params),\n }))\n resolve(data)\n },\n error: (error) => {\n updateEntry(state, key, 'fetchError', (prev) => {\n if (!prev) return prev\n // Keep rendering data if we have it (background failure); otherwise surface as error.\n if (prev.status === 'success') return {...prev, error, isFetching: false}\n return {...prev, status: 'error', error, isFetching: false}\n })\n inflight.delete(key)\n reject(error)\n },\n complete: () => {\n inflight.delete(key)\n const current = state.get().entries[key]\n if (current?.isFetching) {\n updateEntry(state, key, 'fetchSettle', (prev) => prev && {...prev, isFetching: false})\n }\n },\n }),\n )\n\n return promise\n}\n\n/**\n * Trigger a fetch without awaiting it. The failure is already reflected in the\n * entry's snapshot, so swallow the promise rejection to avoid an unhandled one.\n */\nconst detach = (promise: Promise<unknown>): void => {\n void promise.catch(() => {})\n}\n\nconst markStale = (state: StoreState<CacheState>, key: string): void =>\n updateEntry(state, key, 'markStale', (prev) => prev && {...prev, forcedStale: true})\n\n/** Polls an entry on `refetchInterval` while it has subscribers, skipping ticks while the tab is hidden. */\nconst startInterval = (\n state: StoreState<CacheState>,\n def: AnyDefinition,\n instance: SanityInstance,\n params: unknown[],\n key: string,\n): void => {\n if (!def.refetchInterval || intervalTimers.has(key)) return\n const timer = setCleanupInterval(() => {\n if (typeof document !== 'undefined' && document.hidden) return\n const entry = state.get().entries[key]\n if (entry && entry.subscriptions > 0) detach(startFetch(state, def, instance, params, true))\n }, def.refetchInterval)\n intervalTimers.set(key, timer)\n}\n\n/**\n * Schedules an unsubscribed entry for garbage collection: after `gcTime` (from\n * now) its in-flight fetch is torn down and the entry removed. No-op if the\n * entry still has subscribers or a GC timer is already pending. Called from\n * every path that can leave an entry at zero subscribers — the unsubscribe\n * teardown, and the imperative `refetch`/`setData` writes that create entries\n * without ever subscribing. A later subscribe cancels the pending timer.\n */\nconst scheduleGc = (state: StoreState<CacheState>, def: AnyDefinition, key: string): void => {\n if (gcTimers.has(key)) return\n const entry = state.get().entries[key]\n if (!entry || entry.subscriptions > 0) return\n const timer = setCleanupTimeout(() => {\n gcTimers.delete(key)\n const fetch = inflight.get(key)\n if (fetch) {\n fetch.sub.unsubscribe()\n inflight.delete(key)\n }\n updateEntry(state, key, 'gc', () => undefined)\n }, def.gcTime ?? DEFAULT_GC_TIME)\n gcTimers.set(key, timer)\n}\n\n/** Shared across all fetchers: scans the whole cache and revalidates entries carrying any matched tag. */\nconst invalidateByTags = bindActionGlobally(\n cacheStore,\n ({state}: StoreContext<CacheState>, targets: CacheTag[]): Promise<void> => {\n const refetches: Promise<unknown>[] = []\n for (const [key, entry] of Object.entries(state.get().entries)) {\n const matches = entry.tags.some((tag) =>\n targets.some(\n (target) => tag.type === target.type && (target.id === undefined || tag.id === target.id),\n ),\n )\n if (!matches) continue\n markStale(state, key)\n if (entry.subscriptions <= 0) continue\n const def = definitions.get(entry.fetcherName)\n // Reconciliation is best-effort: a failed refetch surfaces on its snapshot,\n // and must not reject the caller's `invalidated` promise.\n if (def)\n refetches.push(\n startFetch(state, def, entry.instance, entry.params, true).catch(() => undefined),\n )\n }\n return Promise.all(refetches).then(() => undefined)\n },\n)\n\n/**\n * Registers a fetcher definition against the shared cache and returns its typed\n * handle.\n *\n * @internal\n */\nexport function defineFetcher<TParams extends unknown[], TData>(\n definition: FetcherDefinition<TParams, TData>,\n): Fetcher<TParams, TData> {\n const def = definition as unknown as AnyDefinition\n definitions.set(definition.name, def)\n\n const getState = bindActionGlobally(\n cacheStore,\n createStateSourceAction<CacheState, TParams, FetcherSnapshot<TData>>({\n selector: ({state, instance}: SelectorContext<CacheState>, ...params: TParams) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n const entry = state.entries[key]\n return toSnapshot(entry && asTyped<TData>(entry))\n },\n onSubscribe: ({state, instance}: StoreContext<CacheState>, ...params: TParams) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n\n const pendingGc = gcTimers.get(key)\n if (pendingGc) {\n clearTimeout(pendingGc)\n gcTimers.delete(key)\n }\n\n updateEntry(state, key, 'subscribe', (prev) => ({\n ...(prev ?? baseEntry(def, key, instance, params)),\n instance,\n subscriptions: (prev?.subscriptions ?? 0) + 1,\n }))\n startInterval(state, def, instance, params, key)\n\n // Defer the fetch decision off the subscribe call stack to avoid a\n // re-entrant cache write during subscription setup.\n queueMicrotask(() => {\n const entry = state.get().entries[key]\n if (entry && entry.subscriptions > 0 && needsFetch(entry, def)) {\n detach(startFetch(state, def, instance, params, false))\n }\n })\n\n return () => {\n updateEntry(\n state,\n key,\n 'unsubscribe',\n (prev) => prev && {...prev, subscriptions: Math.max(0, prev.subscriptions - 1)},\n )\n const entry = state.get().entries[key]\n if (!entry || entry.subscriptions > 0) return\n\n const interval = intervalTimers.get(key)\n if (interval) {\n clearInterval(interval)\n intervalTimers.delete(key)\n }\n scheduleGc(state, def, key)\n }\n },\n }),\n )\n\n const resolveState = (instance: SanityInstance, ...params: TParams): Promise<TData> =>\n firstValueFrom(\n getState(instance, ...params).observable.pipe(\n filter(\n (snapshot): snapshot is Exclude<FetcherSnapshot<TData>, {status: 'pending'}> =>\n snapshot.status !== 'pending',\n ),\n map((snapshot) => {\n if (snapshot.status === 'error') throw snapshot.error\n return snapshot.data\n }),\n ),\n )\n\n const invalidate = bindActionGlobally(\n cacheStore,\n ({state, instance}: StoreContext<CacheState>, ...params: TParams) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n markStale(state, key)\n const entry = state.get().entries[key]\n if (entry && entry.subscriptions > 0) detach(startFetch(state, def, instance, params, true))\n },\n )\n\n const invalidateAll = bindActionGlobally(cacheStore, ({state}: StoreContext<CacheState>) => {\n const prefix = `${def.name}:`\n for (const [key, entry] of Object.entries(state.get().entries)) {\n if (!key.startsWith(prefix)) continue\n markStale(state, key)\n if (entry.subscriptions > 0)\n detach(startFetch(state, def, entry.instance, entry.params, true))\n }\n })\n\n const refetch = bindActionGlobally(\n cacheStore,\n ({state, instance}: StoreContext<CacheState>, ...params: TParams): Promise<TData> => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n const promise = startFetch(state, def, instance, params, true) as Promise<TData>\n scheduleGc(state, def, key)\n return promise\n },\n )\n\n const setData = bindActionGlobally(\n cacheStore,\n (\n {state, instance}: StoreContext<CacheState>,\n params: TParams,\n updater: TData | ((current: TData | undefined) => TData),\n ) => {\n const key = entryKey(def.name, def.getKey(instance, ...params))\n let previous: CacheEntry | undefined\n updateEntry(state, key, 'setData', (prev) => {\n previous = prev\n const from = asTyped<TData>(prev ?? baseEntry(def, key, instance, params))\n const nextData = applyUpdater(updater, from.data)\n return {\n ...from,\n status: 'success',\n data: nextData,\n dataUpdatedAt: from.dataUpdatedAt ?? Date.now(),\n version: from.version + 1,\n tags: computeTags(def, nextData, params),\n }\n })\n scheduleGc(state, def, key)\n return {undo: () => updateEntry(state, key, 'undoSetData', () => previous)}\n },\n )\n\n return {getState, resolveState, invalidate, invalidateAll, refetch, setData}\n}\n\n/**\n * Creates a bound mutation from its definition. The returned action resolves at\n * server settlement — not after invalidation refetches, which run as background\n * reconciliation (see {@link MutationResult.invalidated}).\n *\n * @internal\n */\nexport function defineMutation<TInput, TResult>(\n definition: MutationDefinition<TInput, TResult>,\n): (instance: SanityInstance, input: TInput) => Promise<MutationResult<TResult>> {\n return (instance, input) => {\n const undos: Array<() => void> = []\n const write: CacheWriter = (fetcher, params, updater) => {\n undos.push(fetcher.setData(instance, params, updater).undo)\n }\n\n definition.onMutate?.(write, input)\n\n return firstValueFrom(definition.mutationFn(instance)(input)).then(\n (result) => {\n definition.onSuccess?.(write, result, input)\n const targets =\n typeof definition.invalidates === 'function'\n ? definition.invalidates(result, input)\n : (definition.invalidates ?? [])\n const invalidated = targets.length ? invalidateByTags(instance, targets) : Promise.resolve()\n return {data: result, invalidated}\n },\n (error) => {\n for (let i = undos.length - 1; i >= 0; i--) undos[i]()\n throw error\n },\n )\n }\n}\n","import {type ListenOptions} from '@sanity/client'\n\n/** Data API version used for reading and writing comments in the addon dataset. */\nexport const COMMENTS_API_VERSION = 'v2025-05-06'\n\n/** Projects API version used to discover and provision the addon dataset. */\nexport const ADDON_DATASET_API_VERSION = 'v2025-02-19'\n\n/**\n * The dataset profile the Studio tags its comments dataset with. Discovery\n * matches on it, so changing it stops the SDK and the Studio sharing comments.\n */\nexport const ADDON_DATASET_PROFILE = 'comments'\n\n/**\n * How long a comment list outlives its last subscriber before being dropped.\n *\n * Long enough that navigating away from a document and straight back reuses the\n * loaded list instead of refetching and re-suspending.\n */\nexport const COMMENTS_STATE_CLEAR_DELAY = 5000\n\n/**\n * Kept identical to the Studio's own listener options\n * (`packages/sanity/src/core/comments/store/useCommentsStore.ts:29-35`), apart\n * from the request tag. `includeResult` is what lets a mutation event carry the\n * changed comment, which is what makes per-comment reconciliation possible\n * rather than refetching the list.\n */\nexport const LISTEN_OPTIONS: ListenOptions = {\n events: ['welcome', 'mutation', 'reconnect'],\n includeResult: true,\n includeAllVersions: true,\n visibility: 'query',\n tag: 'comments.listen',\n}\n\nconst QUERY_PROJECTION = `{\n _createdAt,\n _id,\n _rev,\n authorId,\n contentSnapshot,\n context,\n lastEditedAt,\n message,\n parentCommentId,\n reactions,\n status,\n target,\n threadId\n}`\n\nconst BASE_FILTERS = [`_type == \"comment\"`, `target.document._ref == $documentId`]\nconst VERSION_FILTER = `target.documentVersionId == $documentVersionId`\nconst NO_VERSION_FILTER = `!defined(target.documentVersionId)`\n\n/**\n * Which comments belong to a document, matching the Studio's filter.\n *\n * A release's comments are kept apart from the default ones: asking for a\n * release returns only that release's comments, and asking without one returns\n * only comments with no version at all.\n */\nfunction buildCommentsFilter(documentVersionId?: string): string {\n return [...BASE_FILTERS, documentVersionId ? VERSION_FILTER : NO_VERSION_FILTER].join(' && ')\n}\n\n/** The snapshot query. Newest comment first, as the Studio orders it. */\nexport function buildCommentsQuery(documentVersionId?: string): string {\n return `*[${buildCommentsFilter(documentVersionId)}] ${QUERY_PROJECTION} | order(_createdAt desc)`\n}\n\n/**\n * The listener query. Unprojected on purpose: mutation events carry the whole\n * comment, so there is nothing to trim.\n */\nexport function buildCommentsListenQuery(documentVersionId?: string): string {\n return `*[${buildCommentsFilter(documentVersionId)}]`\n}\n","import {type SanityClient} from '@sanity/client'\nimport {\n catchError,\n distinctUntilChanged,\n EMPTY,\n filter,\n firstValueFrom,\n map,\n type Observable,\n of,\n switchMap,\n take,\n} from 'rxjs'\n\nimport {getClient, getClientState} from '../client/clientStore'\nimport {\n type DatasetResource,\n type DocumentResource,\n isDatasetResource,\n} from '../config/sanityConfig'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {\n ADDON_DATASET_API_VERSION,\n ADDON_DATASET_PROFILE,\n COMMENTS_API_VERSION,\n} from './commentsConstants'\n\ninterface AddonDatasetStoreState {\n /**\n * `unknown` until discovery answers. `missing` means the project has no\n * comments dataset yet, which is the normal state until someone writes a\n * first comment.\n */\n status: 'unknown' | 'resolved' | 'missing'\n datasetName?: string\n /** In flight provisioning, so concurrent first comments issue one POST. */\n provisioning?: Promise<string>\n}\n\n/**\n * Comments need a project and a dataset. There is nowhere to put them for a\n * media library or a canvas, and no Studio to interoperate with either.\n *\n * @internal\n */\nexport function assertDatasetResource(resource: DocumentResource): DatasetResource {\n if (!isDatasetResource(resource)) {\n throw new Error(\n `Comments are only supported for dataset resources, received: ${JSON.stringify(resource)}`,\n )\n }\n return resource\n}\n\nfunction isMissingOrForbidden(error: unknown): boolean {\n if (typeof error !== 'object' || error === null || !('statusCode' in error)) return false\n const {statusCode} = error as {statusCode?: unknown}\n return statusCode === 403 || statusCode === 404\n}\n\nfunction requestAddonDataset(\n client: SanityClient,\n {projectId, dataset}: DatasetResource,\n): Observable<string | undefined> {\n return client.observable\n .request<{name: string}[] | undefined>({\n url: `/projects/${projectId}/datasets?datasetProfile=${ADDON_DATASET_PROFILE}&addonFor=${dataset}`,\n tag: 'comments.addon-dataset.list',\n })\n .pipe(map((datasets) => datasets?.[0]?.name))\n}\n\nfunction discover(\n instance: SanityInstance,\n resource: DatasetResource,\n): Observable<string | undefined> {\n return getClientState(instance, {\n apiVersion: ADDON_DATASET_API_VERSION,\n projectId: resource.projectId,\n useProjectHostname: true,\n }).observable.pipe(\n switchMap((client) =>\n requestAddonDataset(client, resource).pipe(\n // A missing dataset and a dataset this user cannot see both mean there\n // are no comments to read. Transient failures stay pending so a new\n // authenticated client can retry discovery.\n catchError((error: unknown) => (isMissingOrForbidden(error) ? of(undefined) : EMPTY)),\n ),\n ),\n )\n}\n\nasync function discoverOnce(\n instance: SanityInstance,\n resource: DatasetResource,\n): Promise<string | undefined> {\n const client = await firstValueFrom(\n getClientState(instance, {\n apiVersion: ADDON_DATASET_API_VERSION,\n projectId: resource.projectId,\n useProjectHostname: true,\n }).observable.pipe(take(1)),\n )\n\n try {\n return await firstValueFrom(requestAddonDataset(client, resource))\n } catch (error) {\n if (isMissingOrForbidden(error)) return undefined\n throw error\n }\n}\n\nconst addonDatasetStore = defineStore<AddonDatasetStoreState, BoundResourceKey>({\n name: 'CommentsAddonDataset',\n getInitialState: () => ({status: 'unknown'}),\n initialize: ({instance, state, key}) => {\n const resource = assertDatasetResource(key.resource)\n\n const subscription = discover(instance, resource).subscribe((datasetName) =>\n state.set(\n 'setAddonDataset',\n datasetName ? {status: 'resolved', datasetName} : {status: 'missing'},\n ),\n )\n\n return () => subscription.unsubscribe()\n },\n})\n\n/**\n * The addon dataset's name: `undefined` while discovery is in flight, `null`\n * when the project has none yet.\n *\n * @internal\n */\nexport const getAddonDatasetState: (\n instance: SanityInstance,\n options: {resource?: DocumentResource},\n) => StateSource<string | null | undefined> = bindActionByResource(\n addonDatasetStore,\n createStateSourceAction(({state}: {state: AddonDatasetStoreState}) => {\n if (state.status === 'unknown') return undefined\n return state.datasetName ?? null\n }),\n)\n\n/**\n * Resolves the addon dataset, creating it if the project has none.\n *\n * Safe to call concurrently: the first call owns the request and later ones\n * await the same promise. Provisioning is a one-time, project-wide side effect,\n * so this is only called from the write path, when we know there is a comment\n * to store.\n *\n * @internal\n */\nexport const provisionAddonDataset: (\n instance: SanityInstance,\n options: {resource?: DocumentResource},\n) => Promise<string> = bindActionByResource(\n addonDatasetStore,\n ({instance, state, key}: StoreContext<AddonDatasetStoreState, BoundResourceKey>) => {\n const current = state.get()\n if (current.datasetName) return Promise.resolve(current.datasetName)\n if (current.provisioning) return current.provisioning\n\n const resource = assertDatasetResource(key.resource)\n\n const provisioning = (async () => {\n // Another user may have provisioned it since this client started up, in\n // which case creating it again would fail. Ask before writing.\n const existing = await discoverOnce(instance, resource)\n if (existing) return existing\n\n const client = getClient(instance, {\n apiVersion: ADDON_DATASET_API_VERSION,\n projectId: resource.projectId,\n useProjectHostname: true,\n })\n\n const response = await client.request<{datasetName?: string} | undefined>({\n url: `/comments/${resource.dataset}/setup`,\n method: 'POST',\n tag: 'comments.addon-dataset.setup',\n })\n\n if (!response?.datasetName) {\n throw new Error('Creating the comments addon dataset returned no dataset name.')\n }\n\n return response.datasetName\n })()\n .then((datasetName) => {\n state.set('setAddonDataset', {\n status: 'resolved',\n datasetName,\n provisioning: undefined,\n })\n return datasetName\n })\n .catch((error: unknown) => {\n // Clear the in-flight promise so a retry is not permanently stuck on\n // this failure.\n state.set('clearProvisioning', {provisioning: undefined})\n throw error\n })\n\n state.set('startProvisioning', {provisioning})\n\n return provisioning\n },\n)\n\n/**\n * A client pointed at the addon dataset, or `null` while the project has none.\n *\n * Emits again whenever the dataset is discovered or provisioned, and whenever\n * the auth token changes. Long-lived readers must follow it rather than holding\n * a client, because the client store drops every cached client on a token\n * change.\n *\n * @internal\n */\nexport const observeAddonDatasetClient: (\n instance: SanityInstance,\n options: {resource?: DocumentResource},\n) => Observable<SanityClient | null> = bindActionByResource(\n addonDatasetStore,\n ({instance, key}: StoreContext<AddonDatasetStoreState, BoundResourceKey>) => {\n const {projectId} = assertDatasetResource(key.resource)\n\n return getAddonDatasetState(instance, {resource: key.resource}).observable.pipe(\n distinctUntilChanged(),\n filter((datasetName): datasetName is string | null => datasetName !== undefined),\n switchMap((datasetName) =>\n typeof datasetName === 'string'\n ? getClientState(instance, {\n apiVersion: COMMENTS_API_VERSION,\n projectId,\n dataset: datasetName,\n }).observable\n : of(null),\n ),\n )\n },\n)\n","import {stringifyPath} from '@sanity/json-match'\nimport {type Path} from '@sanity/types'\n\n/** A dotted segment GROQ would read as a literal rather than a field name. */\nconst RESERVED_SEGMENT = /\\.(true|false|null)(?=$|[.[])/g\n\n/**\n * One quoted string, or one run of text containing no quote at all. Splitting on\n * this is what keeps the rewrite below out of `[_key==\"...\"]` values.\n */\nconst QUOTED_OR_PLAIN = /\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|[^\"']+/g\n\nfunction quoteReservedFieldSegments(path: string): string {\n return path.replace(QUOTED_OR_PLAIN, (chunk) =>\n chunk.startsWith('\"') || chunk.startsWith(\"'\")\n ? chunk\n : chunk.replace(RESERVED_SEGMENT, '[\"$1\"]'),\n )\n}\n\n/**\n * Normalises a field path to the string form comments are stored in.\n *\n * Comments keep `target.path.field` as a string rather than a path array,\n * because that is what the Studio writes and reading it back has to match\n * exactly. A string given here is parsed and re-stringified, so both a path\n * array and a hand-written string come out in the same canonical form.\n *\n * `undefined` and `[]` both produce `''`. Callers writing a comment reject that\n * rather than passing it on, since a comment has to point at a field.\n *\n * @example\n * ```ts\n * toCommentFieldPath(['body', {_key: 'intro'}, 'content'])\n * // 'body[_key==\"intro\"].content'\n * toCommentFieldPath(undefined)\n * // ''\n * ```\n *\n * @internal\n */\nexport function toCommentFieldPath(path?: string | Path): string {\n return quoteReservedFieldSegments(stringifyPath(path))\n}\n","import {type Comment, type CommentThread} from './types'\n\n/**\n * Groups a flat comment list into threads.\n *\n * A thread's parent is the comment with no `parentCommentId`; replies point at\n * the parent's `id`. Replies whose parent is missing from the input are\n * dropped, which is what happens when a parent is deleted but its replies have\n * not disappeared yet.\n *\n * Threads come back in the order their parents appear in `comments`, so passing\n * the store's newest-first list gives newest-thread-first. Replies within a\n * thread are always oldest first, since that is reading order.\n *\n * @internal\n */\nexport function buildCommentThreads(comments: Comment[]): CommentThread[] {\n const repliesByParent = new Map<string, Comment[]>()\n\n for (const comment of comments) {\n if (!comment.parentCommentId) continue\n const existing = repliesByParent.get(comment.parentCommentId)\n if (existing) {\n existing.push(comment)\n } else {\n repliesByParent.set(comment.parentCommentId, [comment])\n }\n }\n\n const threads: CommentThread[] = []\n\n for (const parentComment of comments) {\n if (parentComment.parentCommentId) continue\n\n // The array is built above and owned here, so sorting in place is safe.\n const replies = (repliesByParent.get(parentComment.id) ?? []).sort((a, b) =>\n a.createdAt.localeCompare(b.createdAt),\n )\n\n const lastReply = replies[replies.length - 1]\n\n threads.push({\n threadId: parentComment.threadId,\n fieldPath: parentComment.fieldPath,\n parentComment,\n replies,\n commentsCount: replies.length + 1,\n status: parentComment.status,\n lastActivityAt: lastReply ? lastReply.createdAt : parentComment.createdAt,\n })\n }\n\n return threads\n}\n","import {type Comment, type StoredComment} from './types'\n\n/**\n * Turns a stored comment into the shape consumers see.\n *\n * This is the whole seam between the storage format and the public API. When\n * comments move to the organization-level store, the three fields that differ\n * are handled here and nothing downstream changes:\n *\n * - the author moves from `authorId` to `_system.createdBy`\n * - `target.document` becomes a global document reference, so `documentId` is\n * parsed out of a `resourceType:resourceId:documentId` string rather than\n * read off `_ref`\n * - `_type` goes from `comment` to `sanity.comment`, and stops being exposed\n *\n * @internal\n */\nexport function normalizeComment(stored: StoredComment): Comment {\n const {target} = stored\n\n return {\n id: stored._id,\n createdAt: stored._createdAt,\n // Omitted rather than emptied when absent, so a consumer can tell \"no\n // author recorded\" from a user whose id happens to be falsy.\n ...(stored.authorId ? {authorId: stored.authorId} : {}),\n message: stored.message,\n threadId: stored.threadId,\n ...(stored.parentCommentId ? {parentCommentId: stored.parentCommentId} : {}),\n status: stored.status,\n ...(stored.lastEditedAt ? {lastEditedAt: stored.lastEditedAt} : {}),\n documentId: target.document._ref,\n documentType: target.documentType,\n fieldPath: target.path?.field ?? '',\n ...(target.path?.selection ? {selection: target.path.selection} : {}),\n ...(stored.contentSnapshot === undefined ? {} : {contentSnapshot: stored.contentSnapshot}),\n // Dropped: the stored `_key`, which only exists to address the array item.\n reactions: (stored.reactions ?? []).map(({shortName, userId, addedAt}) => ({\n shortName,\n userId,\n addedAt,\n })),\n ...(stored._state ? {state: stored._state} : {}),\n }\n}\n","import {type ListenEvent, type SanityClient} from '@sanity/client'\nimport {filter, map, Observable, share, switchMap, take} from 'rxjs'\n\nimport {buildCommentsListenQuery, buildCommentsQuery, LISTEN_OPTIONS} from './commentsConstants'\nimport {type StoredComment} from './types'\n\n/** What the listener saw, in terms the store can apply. */\nexport type CommentsEvent =\n | {type: 'snapshot'; comments: StoredComment[]}\n | {type: 'appear'; comment: StoredComment}\n | {type: 'disappear'; commentId: string}\n | {type: 'update'; comment: StoredComment; transactionId: string}\n | {type: 'error'; error: unknown}\n\nfunction toCommentsEvent(event: ListenEvent<StoredComment>): CommentsEvent | undefined {\n if (event.type !== 'mutation') return undefined\n\n if (event.transition === 'disappear') {\n return {type: 'disappear', commentId: event.documentId}\n }\n\n // `includeResult` is on, so a create or update carries the whole comment. An\n // event without one is nothing we can apply.\n if (!event.result) return undefined\n\n if (event.transition === 'appear') {\n return {type: 'appear', comment: event.result}\n }\n\n return {type: 'update', comment: event.result, transactionId: event.transactionId}\n}\n\n/**\n * Watches one document's comments: a snapshot on connect, then live changes.\n *\n * The Studio awaits its snapshot inside the event handler, which loses any\n * mutation that arrives while the fetch is in flight, because the older\n * snapshot overwrites it. Here the mutations that arrive during a fetch are\n * held and replayed straight after the snapshot, so nothing is dropped.\n *\n * A reconnect produces a fresh `welcome`, which cancels any fetch still running\n * and starts a new one. Reconnects emit nothing themselves: dropping the list\n * while reconnecting would only make readers suspend again for data we still\n * have.\n *\n * @internal\n */\nexport function observeComments(options: {\n client: SanityClient\n documentId: string\n documentVersionId?: string\n}): Observable<CommentsEvent> {\n const {client, documentId, documentVersionId} = options\n\n const params = {\n documentId,\n ...(documentVersionId ? {documentVersionId} : {}),\n }\n\n const events$ = client.observable\n .listen<StoredComment>(buildCommentsListenQuery(documentVersionId), params, LISTEN_OPTIONS)\n .pipe(share())\n\n const mutations$ = events$.pipe(\n map(toCommentsEvent),\n filter((event): event is CommentsEvent => event !== undefined),\n )\n\n return events$.pipe(\n filter((event) => event.type === 'welcome'),\n switchMap(\n () =>\n new Observable<CommentsEvent>((observer) => {\n // Subscribe before the fetch starts so nothing between the two is missed.\n // The array only exists while the snapshot is in flight. Once flushed,\n // live mutations pass through without retaining their history.\n const buffered: CommentsEvent[] = []\n let snapshotReceived = false\n const mutationSubscription = mutations$.subscribe({\n next: (event) => {\n if (snapshotReceived) observer.next(event)\n else buffered.push(event)\n },\n error: (error: unknown) => observer.error(error),\n })\n\n const snapshotSubscription = client.observable\n .fetch<StoredComment[]>(buildCommentsQuery(documentVersionId), params, {\n tag: 'comments.list',\n })\n .pipe(take(1))\n .subscribe({\n next: (comments) => {\n observer.next({type: 'snapshot', comments})\n snapshotReceived = true\n for (const event of buffered) observer.next(event)\n buffered.length = 0\n },\n error: (error: unknown) => {\n observer.next({type: 'error', error})\n observer.complete()\n },\n })\n\n return () => {\n mutationSubscription.unsubscribe()\n snapshotSubscription.unsubscribe()\n buffered.length = 0\n }\n }),\n ),\n )\n}\n","import {omitProperty} from '../utils/object'\nimport {type CommentPostPayload, type StoredComment} from './types'\n\ninterface CommentsEntry {\n /** `undefined` until the first snapshot arrives, which is what suspends readers. */\n comments?: Record<string, StoredComment>\n error?: unknown\n subscribers: string[]\n}\n\nexport interface CommentsStoreState {\n entries: {[key: string]: CommentsEntry | undefined}\n /** Comment creates that have not been confirmed by the server yet. */\n pendingCreates: {[commentId: string]: true | undefined}\n /**\n * The most recent transaction this client started per comment, held only\n * while the write is in flight.\n *\n * The listener echoes our own writes back. Without this, an echo from an\n * earlier transaction could arrive after a later one and undo it on screen.\n * Cleared as soon as the write settles: applying our own echo after that is\n * harmless, because it carries the same data we already show.\n */\n pendingTransactions: {[commentId: string]: string | undefined}\n error?: unknown\n}\n\nexport interface CommentsKeyParts {\n documentId: string\n documentVersionId?: string\n}\n\nexport function getCommentsKey({documentId, documentVersionId}: CommentsKeyParts): string {\n return JSON.stringify([documentId, documentVersionId ?? null])\n}\n\nexport function parseCommentsKey(key: string): CommentsKeyParts {\n const [documentId, documentVersionId] = JSON.parse(key) as [string, string | null]\n return {documentId, ...(documentVersionId ? {documentVersionId} : {})}\n}\n\nexport const addSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n const subscribers = [...(entry?.subscribers ?? []), subscriptionId]\n return {...prev, entries: {...prev.entries, [key]: {...entry, subscribers}}}\n }\n\nexport const removeSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n const subscribers = entry.subscribers.filter((id) => id !== subscriptionId)\n if (!subscribers.length) {\n const pendingCreates = {...prev.pendingCreates}\n const pendingTransactions = {...prev.pendingTransactions}\n\n for (const commentId of Object.keys(entry.comments ?? {})) {\n delete pendingCreates[commentId]\n delete pendingTransactions[commentId]\n }\n\n return {\n ...prev,\n entries: omitProperty(prev.entries, key),\n pendingCreates,\n pendingTransactions,\n }\n }\n return {...prev, entries: {...prev.entries, [key]: {...entry, subscribers}}}\n }\n\n/** Replaces an entry's contents with a freshly fetched snapshot. */\nexport const setComments =\n (key: string, comments: StoredComment[]) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n const byId = Object.fromEntries(comments.map((comment) => [comment._id, comment]))\n const pendingCreates = {...prev.pendingCreates}\n\n for (const [commentId, localComment] of Object.entries(entry.comments ?? {})) {\n if (Object.hasOwn(byId, commentId)) {\n delete pendingCreates[commentId]\n } else if (Object.hasOwn(pendingCreates, commentId) || localComment._state) {\n // A snapshot can race an in-flight create. Failed creates are also local\n // drafts and must remain available for retry.\n byId[commentId] = localComment\n }\n }\n\n return {\n ...prev,\n entries: {...prev.entries, [key]: {...entry, comments: byId, error: undefined}},\n pendingCreates,\n }\n }\n\nexport const setCommentsError =\n (key: string, error: unknown) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n return {...prev, entries: {...prev.entries, [key]: {...entry, error}}}\n }\n\n/** A comment as it arrived from the server, replacing whatever we held. */\nexport const receiveComment =\n (key: string, comment: StoredComment) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n const pendingCreates = omitProperty(prev.pendingCreates, comment._id)\n if (!entry) return {...prev, pendingCreates}\n return {\n ...prev,\n pendingCreates,\n entries: {\n ...prev.entries,\n [key]: {...entry, comments: {...entry.comments, [comment._id]: comment}},\n },\n }\n }\n\n/**\n * A comment we just wrote, shown before the server confirms it.\n *\n * `_createdAt` is assigned by the server, but the list sorts on it, so an\n * optimistic comment needs a stand-in until the real value arrives.\n */\ntype OptimisticComment = CommentPostPayload & {_createdAt?: string; _rev?: string}\n\n/** A create that already failed once is being retried, not written fresh. */\nfunction isRetryingCreate(existing: StoredComment | undefined): boolean {\n const state = existing?._state?.type\n return state === 'createError' || state === 'createRetrying'\n}\n\nfunction mergeOptimisticComment(\n existing: StoredComment | undefined,\n comment: OptimisticComment,\n): StoredComment {\n return {\n ...existing,\n ...comment,\n _createdAt: comment._createdAt ?? existing?._createdAt ?? new Date().toISOString(),\n _rev: comment._rev ?? existing?._rev ?? '',\n ...(isRetryingCreate(existing) ? {_state: {type: 'createRetrying'} as const} : {}),\n }\n}\n\nexport const addComment =\n (key: string, comment: OptimisticComment) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entry = prev.entries[key]\n if (!entry) return prev\n\n const existing =\n entry.comments && Object.hasOwn(entry.comments, comment._id)\n ? entry.comments[comment._id]\n : undefined\n\n return {\n ...prev,\n pendingCreates: {...prev.pendingCreates, [comment._id]: true},\n entries: {\n ...prev.entries,\n [key]: {\n ...entry,\n comments: {\n ...entry.comments,\n [comment._id]: mergeOptimisticComment(existing, comment),\n },\n },\n },\n }\n }\n\n/**\n * Merges a partial update into whichever entry holds the comment.\n *\n * Comment ids are unique, so this touches one entry in practice. Searching by\n * id rather than taking a key means callers editing a comment do not have to\n * say which document it belongs to.\n */\nexport const applyCommentUpdate =\n (commentId: string, patch: Partial<StoredComment>) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entries = {...prev.entries}\n let changed = false\n\n for (const [key, entry] of Object.entries(prev.entries)) {\n const comment = entry?.comments?.[commentId]\n if (!entry || !comment) continue\n changed = true\n entries[key] = {\n ...entry,\n comments: {...entry.comments, [commentId]: {...comment, ...patch}},\n }\n }\n\n return changed ? {...prev, entries} : prev\n }\n\n/** Removes a comment and, when it is a thread parent, its replies. */\nexport const removeCommentById =\n (commentId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entries = {...prev.entries}\n const removedIds = new Set<string>()\n let changed = false\n\n for (const [key, entry] of Object.entries(prev.entries)) {\n if (!entry?.comments) continue\n\n const remaining = Object.fromEntries(\n Object.entries(entry.comments).filter(([id, comment]) => {\n const keep = id !== commentId && comment.parentCommentId !== commentId\n if (!keep) removedIds.add(id)\n return keep\n }),\n )\n\n if (Object.keys(remaining).length === Object.keys(entry.comments).length) continue\n\n changed = true\n entries[key] = {...entry, comments: remaining}\n }\n\n if (!changed) return prev\n\n const pendingCreates = {...prev.pendingCreates}\n const pendingTransactions = {...prev.pendingTransactions}\n for (const id of removedIds) {\n delete pendingCreates[id]\n delete pendingTransactions[id]\n }\n\n return {...prev, entries, pendingCreates, pendingTransactions}\n }\n\nexport const setPendingTransaction =\n (commentId: string, transactionId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => ({\n ...prev,\n pendingTransactions: {...prev.pendingTransactions, [commentId]: transactionId},\n })\n\nexport const clearPendingTransaction =\n (commentId: string) =>\n (prev: CommentsStoreState): CommentsStoreState => ({\n ...prev,\n pendingTransactions: omitProperty(prev.pendingTransactions, commentId),\n })\n\n/** Marks a pending create as failed, unless another request already confirmed it. */\nexport const setCommentCreateError =\n (commentId: string, error: Error) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n if (!Object.hasOwn(prev.pendingCreates, commentId)) return prev\n const next = applyCommentUpdate(commentId, {_state: {type: 'createError', error}})(prev)\n return {...next, pendingCreates: omitProperty(next.pendingCreates, commentId)}\n }\n\n/** Restores an optimistic edit if the failed transaction is still current. */\nexport const rollbackCommentUpdate =\n (commentId: string, transactionId: string, previous: StoredComment) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n if (\n !Object.hasOwn(prev.pendingTransactions, commentId) ||\n prev.pendingTransactions[commentId] !== transactionId\n ) {\n return prev\n }\n\n const entries = Object.fromEntries(\n Object.entries(prev.entries).map(([key, entry]) => {\n if (!entry?.comments?.[commentId]) return [key, entry]\n return [key, {...entry, comments: {...entry.comments, [commentId]: previous}}]\n }),\n )\n\n return {\n ...prev,\n entries,\n pendingTransactions: omitProperty(prev.pendingTransactions, commentId),\n }\n }\n\n/** Restores comments removed optimistically when the server delete fails. */\nexport const restoreComments =\n (removed: Array<{key: string; comments: StoredComment[]}>) =>\n (prev: CommentsStoreState): CommentsStoreState => {\n const entries = {...prev.entries}\n let changed = false\n\n for (const {key, comments} of removed) {\n const entry = entries[key]\n if (!entry) continue\n const missing = comments.filter(\n (comment) => !Object.hasOwn(entry.comments ?? {}, comment._id),\n )\n if (!missing.length) continue\n changed = true\n entries[key] = {\n ...entry,\n comments: Object.fromEntries([\n ...Object.entries(entry.comments ?? {}),\n ...missing.map((comment) => [comment._id, comment] as const),\n ]),\n }\n }\n\n return changed ? {...prev, entries} : prev\n }\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\nimport {type Path} from '@sanity/types'\nimport {\n catchError,\n distinctUntilChanged,\n EMPTY,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n NEVER,\n Observable,\n pairwise,\n race,\n startWith,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {type StoreState} from '../store/createStoreState'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {randomId} from '../utils/ids'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {observeAddonDatasetClient} from './addonDatasetStore'\nimport {buildCommentThreads} from './buildCommentThreads'\nimport {toCommentFieldPath} from './commentFieldPath'\nimport {COMMENTS_STATE_CLEAR_DELAY} from './commentsConstants'\nimport {normalizeComment} from './normalizeComment'\nimport {type CommentsEvent, observeComments} from './observeComments'\nimport {\n addSubscriber,\n clearPendingTransaction,\n type CommentsKeyParts,\n type CommentsStoreState,\n getCommentsKey,\n parseCommentsKey,\n receiveComment,\n removeCommentById,\n removeSubscriber,\n setComments,\n setCommentsError,\n} from './reducers'\nimport {type Comment, type CommentStatus, type CommentThread, type StoredComment} from './types'\n\n/**\n * Which comments to read.\n * @beta\n */\nexport interface CommentsOptions extends DocumentHandle {\n /**\n * Narrow to one field. Omit to get every comment on the document.\n */\n fieldPath?: string | Path\n /** Narrow to open or resolved threads. Omit for both. */\n status?: CommentStatus\n}\n\n/** @beta */\nexport interface ResolveCommentsOptions extends CommentsOptions {\n signal?: AbortSignal\n}\n\n/**\n * Fields on {@link CommentsOptions} that are deliberately left out of the key\n * below, because neither changes which comments an option set addresses.\n *\n * `source` is the deprecated alias for `resource` and is folded into it before\n * a key is ever built, so keying on both would give one list two keys.\n * `liveEdit` describes the document rather than its comments, which hang off\n * the published id whether or not drafts exist.\n */\ntype CommentsKeyIrrelevantField = 'source' | 'liveEdit'\n\n/**\n * A stable string standing for one set of read options.\n *\n * Only React needs this: it holds one state source steady across renders and\n * defers swapping to a new one while the previous list is still on screen.\n * `fieldPath` is normalised on the way in, so a path array and the equivalent\n * string address the same list.\n *\n * @internal\n */\nexport function getCommentsOptionsKey(options: CommentsOptions): string {\n return JSON.stringify({\n documentId: options.documentId,\n documentType: options.documentType,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n fieldPath: options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n status: options.status,\n // The `Record` half makes a new field on `CommentsOptions` a compile\n // error here unless it is listed above or named as irrelevant. Left to\n // `satisfies CommentsOptions` alone, a forgotten field would just be\n // absent from the key, and a reader would keep the list it had while the\n // caller thought it had asked for a different one.\n } satisfies CommentsOptions &\n Record<Exclude<keyof CommentsOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseCommentsOptionsKey(key: string): CommentsOptions {\n return JSON.parse(key) as CommentsOptions\n}\n\n/**\n * Which comment list an option set addresses.\n *\n * Comments hang off the published id, so a draft and its published document\n * share one list. A release keeps its own.\n */\nexport function toCommentsKeyParts(\n instance: SanityInstance,\n options: CommentsOptions,\n): CommentsKeyParts {\n const perspective = options.perspective ?? instance.config.perspective\n return {\n documentId: getPublishedId(DocumentId(options.documentId)),\n ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),\n }\n}\n\nfunction applyEvent(\n state: StoreState<CommentsStoreState>,\n key: string,\n event: CommentsEvent,\n): void {\n switch (event.type) {\n case 'snapshot':\n state.set('setComments', setComments(key, event.comments))\n return\n case 'appear':\n state.set('receiveComment', receiveComment(key, event.comment))\n return\n case 'disappear':\n state.set('removeComment', removeCommentById(event.commentId))\n return\n case 'error':\n state.set('setCommentsError', setCommentsError(key, event.error))\n return\n case 'update': {\n const pending = state.get().pendingTransactions[event.comment._id]\n\n // Our own writes come back through the listener. When a later transaction\n // is already in flight for this comment, an echo of an earlier one would\n // undo it on screen, so drop it and wait for the one we are expecting.\n if (pending && pending !== event.transactionId) return\n\n state.set('receiveComment', receiveComment(key, event.comment))\n if (pending) {\n state.set('clearPendingTransaction', clearPendingTransaction(event.comment._id))\n }\n }\n }\n}\n\nconst watchSubscribedDocuments = ({\n state,\n instance,\n key,\n}: StoreContext<CommentsStoreState, BoundResourceKey>) => {\n const client$ = observeAddonDatasetClient(instance, {resource: key.resource})\n\n return state.observable\n .pipe(\n map((current) => new Set(Object.keys(current.entries))),\n distinctUntilChanged(\n (a, b) => a.size === b.size && Array.from(b).every((entry) => a.has(entry)),\n ),\n startWith(new Set<string>()),\n pairwise(),\n mergeMap(([previous, current]) => [\n ...Array.from(current)\n .filter((entry) => !previous.has(entry))\n .map((entry) => ({key: entry, added: true})),\n ...Array.from(previous)\n .filter((entry) => !current.has(entry))\n .map((entry) => ({key: entry, added: false})),\n ]),\n groupBy((event) => event.key),\n mergeMap((group$) =>\n group$.pipe(\n switchMap((event) => {\n if (!event.added) return EMPTY\n\n const {documentId, documentVersionId} = parseCommentsKey(group$.key)\n\n return client$.pipe(\n switchMap((client) => {\n if (!client) {\n // A dataset that does not exist holds no comments. Settling on\n // an empty list matters: leaving it unset would suspend\n // readers forever on a project nobody has commented in.\n state.set('setComments', setComments(group$.key, []))\n return EMPTY\n }\n\n return observeComments({client, documentId, documentVersionId}).pipe(\n tap((commentsEvent) => applyEvent(state, group$.key, commentsEvent)),\n // Keep following the addon client after one listener fails.\n // A token refresh or reconnect can then supply a new client.\n catchError((error: unknown) => {\n state.set('setCommentsError', setCommentsError(group$.key, error))\n return EMPTY\n }),\n )\n }),\n )\n }),\n ),\n ),\n )\n .subscribe({error: (error: unknown) => state.set('setError', {error})})\n}\n\nexport const commentsStore = defineStore<CommentsStoreState, BoundResourceKey>({\n name: 'Comments',\n getInitialState: () => ({\n entries: {},\n pendingCreates: {},\n pendingTransactions: {},\n }),\n initialize: (context) => {\n const subscription = watchSubscribedDocuments(context)\n return () => subscription.unsubscribe()\n },\n})\n\n/**\n * Filtered lists and their threads, cached against the comment map they came\n * from.\n *\n * Selectors run on every store change, and a fresh array each time would make\n * `useSyncExternalStore` re-render whenever anything anywhere in the store\n * moved. Keying on the comment map means a change to some other document, or\n * to an unrelated part of the state, hands back the identical array. Entries\n * die with the map they belong to.\n */\nconst normalizedCache = new WeakMap<object, Comment[]>()\nconst filteredCache = new WeakMap<object, Map<string, Comment[]>>()\nconst threadCache = new WeakMap<object, Map<string, CommentThread[]>>()\n\n/**\n * The stored map turned into what consumers see, newest first.\n *\n * Cached alongside the filters below rather than done per read, so the\n * normalised objects keep their identity for as long as the stored map does.\n */\nfunction normalizeAll(commentsById: Record<string, StoredComment>): Comment[] {\n const cached = normalizedCache.get(commentsById)\n if (cached) return cached\n\n // Newest first, matching the query's order. The store keys comments by id, so\n // the order has to be reapplied here.\n const normalized = Object.values(commentsById)\n .map(normalizeComment)\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n\n normalizedCache.set(commentsById, normalized)\n return normalized\n}\n\nfunction filterComments(\n all: Comment[],\n fieldPath: string | undefined,\n status: CommentStatus | undefined,\n): Comment[] {\n let byFilter = filteredCache.get(all)\n if (!byFilter) {\n byFilter = new Map()\n filteredCache.set(all, byFilter)\n }\n\n // `\\0` stands in for \"no field filter\", which is not the same as `''`.\n const cacheKey = `${fieldPath ?? '\\0'}|${status ?? ''}`\n const cached = byFilter.get(cacheKey)\n if (cached) return cached\n\n const filtered = all.filter((comment) => {\n if (status && comment.status !== status) return false\n if (fieldPath === undefined) return true\n return comment.fieldPath === fieldPath\n })\n\n byFilter.set(cacheKey, filtered)\n return filtered\n}\n\nfunction selectComments(\n {state, instance}: SelectorContext<CommentsStoreState>,\n options: CommentsOptions,\n): Comment[] | undefined {\n if (state.error) throw state.error\n\n const entry = state.entries[getCommentsKey(toCommentsKeyParts(instance, options))]\n if (entry?.error) throw entry.error\n if (!entry?.comments) return undefined\n\n return filterComments(\n normalizeAll(entry.comments),\n options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n options.status,\n )\n}\n\nfunction selectCommentThreads(\n context: SelectorContext<CommentsStoreState>,\n options: CommentsOptions,\n): CommentThread[] | undefined {\n const comments = selectComments(context, {...options, fieldPath: undefined, status: undefined})\n if (comments === undefined) return undefined\n\n let byFilter = threadCache.get(comments)\n if (!byFilter) {\n byFilter = new Map()\n threadCache.set(comments, byFilter)\n }\n\n const fieldPath =\n options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath)\n const cacheKey = `${fieldPath ?? '\\0'}|${options.status ?? ''}`\n const cached = byFilter.get(cacheKey)\n if (cached) return cached\n\n const threads = buildCommentThreads(comments).filter((thread) => {\n if (options.status && thread.parentComment.status !== options.status) return false\n if (fieldPath === undefined) return true\n return thread.fieldPath === fieldPath\n })\n byFilter.set(cacheKey, threads)\n return threads\n}\n\n/**\n * Every comment on a document, newest first.\n *\n * `undefined` until the first snapshot arrives. Replies are included; use\n * {@link getCommentThreadsState} to read them grouped.\n *\n * @beta\n */\nexport const getCommentsState: (\n instance: SanityInstance,\n options: CommentsOptions,\n) => StateSource<Comment[] | undefined> = bindActionByResource(\n commentsStore,\n createStateSourceAction({\n selector: selectComments,\n onSubscribe: ({state, instance}, options: CommentsOptions) => {\n const key = getCommentsKey(toCommentsKeyParts(instance, options))\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n return () => {\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId)),\n COMMENTS_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Threads on a document, newest thread first, each with its replies oldest first.\n *\n * A thread's `status` and `fieldPath` come from its first comment, so filtering\n * by either selects whole threads rather than individual replies.\n *\n * @beta\n */\nexport const getCommentThreadsState: (\n instance: SanityInstance,\n options: CommentsOptions,\n) => StateSource<CommentThread[] | undefined> = bindActionByResource(\n commentsStore,\n createStateSourceAction({\n selector: selectCommentThreads,\n onSubscribe: ({state, instance}, options: CommentsOptions) => {\n const key = getCommentsKey(toCommentsKeyParts(instance, options))\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n return () => {\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId)),\n COMMENTS_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Waits for a document's comments to load.\n *\n * Holds a subscriber only while resolving, so a component that suspends on this\n * and then errors before mounting does not strand the list. Throw the promise\n * for Suspense, then read through {@link getCommentsState}.\n *\n * @beta\n */\nexport const resolveComments: (\n instance: SanityInstance,\n options: ResolveCommentsOptions,\n) => Promise<Comment[]> = bindActionByResource(\n commentsStore,\n (\n {state, instance}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {signal, ...options}: ResolveCommentsOptions,\n ) => resolveList(state, instance, options, signal, getCommentsState),\n)\n\n/**\n * Waits for a document's comments to load, grouped into threads.\n * @beta\n */\nexport const resolveCommentThreads: (\n instance: SanityInstance,\n options: ResolveCommentsOptions,\n) => Promise<CommentThread[]> = bindActionByResource(\n commentsStore,\n (\n {state, instance}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {signal, ...options}: ResolveCommentsOptions,\n ) => resolveList(state, instance, options, signal, getCommentThreadsState),\n)\n\nfunction resolveList<T>(\n state: StoreState<CommentsStoreState>,\n instance: SanityInstance,\n options: CommentsOptions,\n signal: AbortSignal | undefined,\n getState: (i: SanityInstance, o: CommentsOptions) => StateSource<T | undefined>,\n): Promise<T> {\n const key = getCommentsKey(toCommentsKeyParts(instance, options))\n const {getCurrent} = getState(instance, options)\n\n // Loading is driven by subscribers, so without one here nothing would ever\n // fetch and this promise would never settle. Holding it only for the duration\n // of the resolve also means a component that suspends and then errors before\n // mounting does not leave a subscriber-less entry behind holding its error.\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n const release = () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId))\n\n const aborted$ = signal\n ? new Observable<never>((observer) => {\n const listener = () => {\n // Release now rather than after the delay: when this was the only\n // reader, dropping the key tears down the listener immediately, which\n // is the point of aborting.\n release()\n observer.error(new DOMException('The operation was aborted.', 'AbortError'))\n }\n signal.addEventListener('abort', listener)\n return () => signal.removeEventListener('abort', listener)\n })\n : NEVER\n\n const resolved$ = state.observable.pipe(\n map(() => getCurrent()),\n first((value): value is T => value !== undefined),\n )\n\n const promise = firstValueFrom(race([resolved$, aborted$]))\n const releaseLater = () => setCleanupTimeout(release, COMMENTS_STATE_CLEAR_DELAY)\n promise.then(releaseLater, releaseLater)\n return promise\n}\n","// NOTE: currently this API is only available on vX\nexport const API_VERSION = 'vX'\nexport const PROJECT_API_VERSION = '2025-07-18'\nexport const USERS_STATE_CLEAR_DELAY = 5000\nexport const DEFAULT_USERS_BATCH_SIZE = 100\n","import {type SanityInstance} from '../store/createSanityInstance'\nimport {omitProperty} from '../utils/object'\nimport {type GetUsersOptions, type SanityUserResponse, type UsersStoreState} from './types'\nimport {DEFAULT_USERS_BATCH_SIZE} from './usersConstants'\n\n/** @internal */\nexport const getUsersKey = (\n instance: SanityInstance,\n {\n resourceType,\n organizationId,\n batchSize = DEFAULT_USERS_BATCH_SIZE,\n projectId = instance.config.projectId,\n userId,\n }: GetUsersOptions = {},\n): string =>\n JSON.stringify({\n resourceType,\n organizationId,\n batchSize,\n projectId,\n userId,\n } satisfies ReturnType<typeof parseUsersKey>)\n\n/** @internal */\nexport const parseUsersKey = (\n key: string,\n): {\n batchSize: number\n resourceType?: 'organization' | 'project'\n projectId?: string\n organizationId?: string\n userId?: string\n} => JSON.parse(key)\n\nexport const addSubscription =\n (subscriptionId: string, key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n const subscriptions = [...(group?.subscriptions ?? []), subscriptionId]\n return {...prev, users: {...prev.users, [key]: {...group, subscriptions}}}\n }\n\nexport const removeSubscription =\n (subscriptionId: string, key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n const subscriptions = group.subscriptions.filter((id) => id !== subscriptionId)\n if (!subscriptions.length) return {...prev, users: omitProperty(prev.users, key)}\n return {...prev, users: {...prev.users, [key]: {...group, subscriptions}}}\n }\n\nexport const setUsersData =\n (key: string, {data, nextCursor, totalCount}: SanityUserResponse) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n const users = [...(group.users ?? []), ...data]\n return {...prev, users: {...prev.users, [key]: {...group, users, totalCount, nextCursor}}}\n }\n\nexport const updateLastLoadMoreRequest =\n (timestamp: string, key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n return {...prev, users: {...prev.users, [key]: {...group, lastLoadMoreRequest: timestamp}}}\n }\n\nexport const setUsersError =\n (key: string, error: unknown) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n return {...prev, users: {...prev.users, [key]: {...group, error}}}\n }\n\nexport const cancelRequest =\n (key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n const group = prev.users[key]\n if (!group) return prev\n if (group.subscriptions.length) return prev\n return {...prev, users: omitProperty(prev.users, key)}\n }\n\nexport const initializeRequest =\n (key: string) =>\n (prev: UsersStoreState): UsersStoreState => {\n if (prev.users[key]) return prev\n return {...prev, users: {...prev.users, [key]: {subscriptions: []}}}\n }\n","import {\n ConnectionFailedError,\n CorsOriginError,\n DisconnectError,\n type LiveEventMessage,\n} from '@sanity/client'\nimport {catchError, defer, EMPTY, filter, type Observable, retry, switchMap} from 'rxjs'\n\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {getClientState} from './clientStore'\n\n/**\n * All live-events subscriptions must use identical connection options\n * (API version, request tag, `includeDrafts`): `@sanity/client` caches the\n * underlying EventSource per URL + options, so divergent options would\n * silently split what should be a single shared connection.\n */\nconst LIVE_EVENTS_API_VERSION = 'v2025-05-06'\n\n/**\n * Delay before re-establishing the live events connection after a\n * server-initiated error (e.g. ChannelError, MessageError). Live errors are\n * retried rather than surfaced because an errored live connection would\n * otherwise poison the consuming store. Plain connection drops never reach\n * this — they are reconnected inside `@sanity/client`.\n *\n * @internal\n */\nexport const LIVE_EVENTS_RETRY_DELAY = 1000\n\n/**\n * Options for {@link observeLiveEvents}.\n * @internal\n */\nexport interface ObserveLiveEventsOptions {\n /** Resource to scope the underlying client and live connection to. */\n resource?: DocumentResource\n /**\n * Called when the connection fails due to a CORS misconfiguration. The\n * error is swallowed (the stream never errors; it idles until a new client\n * is emitted) so consumers can surface it as store state instead of a\n * stream error.\n */\n onCorsError: (error: unknown) => void\n}\n\n/**\n * Emits Live Content API messages for a dataset so consumers can invalidate\n * fetched data via sync tags.\n *\n * All consumers observe the same underlying EventSource connection:\n * `@sanity/client` caches the live-events stream per URL and connection\n * options, and every subscription made through this function uses the same\n * API version and request tag.\n *\n * @internal\n */\nexport function observeLiveEvents(\n instance: SanityInstance,\n {resource, onCorsError}: ObserveLiveEventsOptions,\n): Observable<LiveEventMessage> {\n return getClientState(instance, {\n apiVersion: LIVE_EVENTS_API_VERSION,\n resource,\n }).observable.pipe(\n switchMap((client) =>\n defer(() =>\n client.live.events({includeDrafts: !!client.config().token, tag: 'live-events'}),\n ).pipe(\n catchError((error) => {\n if (error instanceof CorsOriginError) {\n // Swallow CORS errors without bubbling up so that they can be\n // handled by the consumer (e.g. shown via the Cors Error component)\n onCorsError(error)\n return EMPTY\n }\n if (error instanceof DisconnectError) {\n // The server explicitly told this client to stop reconnecting —\n // end live updates without erroring (consumers keep serving data,\n // they just stop receiving live invalidation)\n return EMPTY\n }\n if (\n error instanceof ConnectionFailedError &&\n typeof error.status === 'number' &&\n error.status >= 400 &&\n error.status < 500\n ) {\n // The server rejected the connection with a 4xx (e.g. a 401 from\n // an expired token) — it will keep rejecting, so retrying would\n // reconnect once per second forever. End live updates like a\n // DisconnectError. A ConnectionFailedError without a status stays\n // retryable: it may be a transient network failure.\n return EMPTY\n }\n throw error\n }),\n // Server-initiated live errors (e.g. ChannelError, MessageError) are\n // retried, not surfaced. Dropped connections are already reconnected\n // inside @sanity/client.\n retry({delay: LIVE_EVENTS_RETRY_DELAY}),\n ),\n ),\n filter((e): e is LiveEventMessage => e.type === 'message'),\n )\n}\n","import {type ReleaseDocument, type SyncTag} from '@sanity/client'\nimport {\n BehaviorSubject,\n combineLatest,\n distinctUntilChanged,\n filter,\n map,\n type Observable,\n startWith,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {observeLiveEvents} from '../client/liveEvents'\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\n\nconst RELEASES_QUERY = 'releases::all()'\nconst RELEASES_API_VERSION = 'v2025-05-06'\n\n/**\n * Options for {@link observeReleases}.\n * @internal\n */\nexport interface ObserveReleasesOptions {\n /** Resource to scope the underlying client and live connection to. */\n resource?: DocumentResource\n /**\n * Called when the live events connection fails due to a CORS\n * misconfiguration. See `observeLiveEvents`.\n */\n onCorsError: (error: unknown) => void\n}\n\n/**\n * Emits every release document (including archived and published), refetching\n * whenever a Live Content API event matches the query's sync tags.\n *\n * This fetches directly through the client store — deliberately not via the\n * query store — because release data is itself an input to query perspective\n * resolution (`getPerspectiveState`); fetching through the query store would\n * make the two stores mutually dependent.\n *\n * @internal\n */\nexport function observeReleases(\n instance: SanityInstance,\n {resource, onCorsError}: ObserveReleasesOptions,\n): Observable<ReleaseDocument[] | undefined> {\n const client$ = getClientState(instance, {\n apiVersion: RELEASES_API_VERSION,\n resource,\n }).observable\n\n return client$.pipe(\n switchMap((client) => {\n const syncTags$ = new BehaviorSubject<SyncTag[] | undefined>(undefined)\n\n // Pair the latest live message with the latest known sync tags so that\n // a message arriving while a fetch is still in flight is re-evaluated\n // (and refetched if it matches) once that fetch's tags land — instead\n // of being dropped and leaving the store stale until the next event.\n const refetchEventId$ = combineLatest([\n observeLiveEvents(instance, {resource, onCorsError}).pipe(startWith(undefined)),\n syncTags$,\n ]).pipe(\n filter(\n ([message, syncTags]) =>\n message === undefined || message.tags.some((tag) => syncTags?.includes(tag)),\n ),\n map(([message]) => message?.id),\n distinctUntilChanged(),\n )\n\n return refetchEventId$.pipe(\n switchMap((lastLiveEventId) =>\n client.observable.fetch<ReleaseDocument[]>(\n RELEASES_QUERY,\n {},\n {\n perspective: 'raw',\n filterResponse: false,\n returnQuery: false,\n lastLiveEventId,\n tag: 'releases',\n },\n ),\n ),\n tap((response) => {\n syncTags$.next(response.syncTags)\n }),\n map((response) => response.result),\n )\n }),\n // Emit synchronously on subscribe (mirroring StateSource semantics, which\n // the releases store previously consumed) so the store can immediately\n // record an empty list: consumers distinguish \"no releases yet\" from\n // \"never loaded\", and the React hooks don't suspend on first load.\n startWith(undefined),\n )\n}\n","import {type ReleaseDocument} from '@sanity/client'\n\n// mirrors the order of the releases in the releases list in Studio\n// https://github.com/sanity-io/sanity/blob/main/packages/sanity/src/core/releases/hooks/utils.ts\nexport function sortReleases(releases: ReleaseDocument[] = []): ReleaseDocument[] {\n // The order should always be:\n // [undecided (sortByCreatedAt), scheduled(sortBy publishAt || metadata.intendedPublishAt), asap(sortByCreatedAt)]\n return [...releases].sort((a, b) => {\n // metadata is optional on a release document; guard against releases that\n // have no metadata at all so sorting can't throw and wipe out the list.\n const aType = a.metadata?.releaseType\n const bType = b.metadata?.releaseType\n\n // undecided are always first, then by createdAt descending\n if (aType === 'undecided' && bType !== 'undecided') {\n return -1\n }\n if (aType !== 'undecided' && bType === 'undecided') {\n return 1\n }\n if (aType === 'undecided' && bType === 'undecided') {\n // Sort by createdAt\n return new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime()\n }\n\n // Scheduled are always at the middle, then by publishAt descending\n if (aType === 'scheduled' && bType === 'scheduled') {\n const aPublishAt = a['publishAt'] || a.metadata?.['intendedPublishAt']\n if (!aPublishAt) {\n return 1\n }\n const bPublishAt = b['publishAt'] || b.metadata?.['intendedPublishAt']\n if (!bPublishAt) {\n return -1\n }\n return new Date(bPublishAt).getTime() - new Date(aPublishAt).getTime()\n }\n\n // ASAP are always last, then by createdAt descending\n if (aType === 'asap' && bType !== 'asap') {\n return 1\n }\n if (aType !== 'asap' && bType === 'asap') {\n return -1\n }\n if (aType === 'asap' && bType === 'asap') {\n // Sort by createdAt\n return new Date(b._createdAt).getTime() - new Date(a._createdAt).getTime()\n }\n\n return 0\n })\n}\n","import {type ReleaseDocument} from '@sanity/client'\nimport {map} from 'rxjs'\n\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {observeReleases} from './observeReleases'\nimport {sortReleases} from './utils/sortReleases'\n\nconst ARCHIVED_RELEASE_STATES = ['archived', 'published']\nconst STABLE_EMPTY_RELEASES: ReleaseDocument[] = []\n\n/**\n * Lifecycle states a release document can be in. Mirrors the server's\n * `ReleaseState`.\n * @beta\n */\nexport type ReleaseState =\n | 'active'\n | 'archiving'\n | 'unarchiving'\n | 'archived'\n | 'published'\n | 'publishing'\n | 'scheduled'\n | 'scheduling'\n\nexport interface ReleasesStoreState {\n activeReleases?: ReleaseDocument[]\n allReleases?: ReleaseDocument[]\n error?: unknown\n}\n\nexport const releasesStore = defineStore<ReleasesStoreState, BoundResourceKey>({\n name: 'Releases',\n getInitialState: (): ReleasesStoreState => ({\n activeReleases: undefined,\n allReleases: undefined,\n }),\n initialize: (context) => {\n const subscription = subscribeToReleases(context)\n return () => subscription.unsubscribe()\n },\n})\n\n/**\n * Get the active releases from the store.\n * @internal\n */\nconst _getActiveReleasesState = bindActionByResource(\n releasesStore,\n createStateSourceAction({\n selector: ({state}) => {\n if (state.error) throw state.error\n return state.activeReleases\n },\n }),\n)\n\n/**\n * Get the active releases from the store.\n * @internal\n */\nexport const getActiveReleasesState = (\n instance: SanityInstance,\n options?: {resource?: DocumentResource},\n): StateSource<ReleaseDocument[] | undefined> =>\n // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options\n _getActiveReleasesState(instance, options ?? {})\n\n/**\n * Get every release in the store, including archived and published.\n * @internal\n */\nconst _getAllReleasesState = bindActionByResource(\n releasesStore,\n createStateSourceAction({\n selector: ({state}) => {\n if (state.error) throw state.error\n return state.allReleases\n },\n }),\n)\n\n/**\n * Get every release in the store, including archived and published.\n * @internal\n */\nexport const getAllReleasesState = (\n instance: SanityInstance,\n options?: {resource?: DocumentResource},\n): StateSource<ReleaseDocument[] | undefined> => _getAllReleasesState(instance, options ?? {})\n\nconst subscribeToReleases = ({\n instance,\n state,\n key: {resource},\n}: StoreContext<ReleasesStoreState, BoundResourceKey>) => {\n const releases$ = observeReleases(instance, {\n resource,\n // Surface CORS errors as store state instead of erroring the stream so\n // they can be handled by the Cors Error component\n onCorsError: (error) => state.set('setError', {error}),\n })\n return (\n releases$\n .pipe(\n map((releases) => {\n // logic here mirrors that of studio:\n // https://github.com/sanity-io/sanity/blob/156e8fa482703d99219f08da7bacb384517f1513/packages/sanity/src/core/releases/store/useActiveReleases.ts#L29\n const sorted = sortReleases(releases ?? STABLE_EMPTY_RELEASES).reverse()\n state.set('setReleases', {\n allReleases: sorted,\n activeReleases: sorted.filter(\n (release) => !ARCHIVED_RELEASE_STATES.includes(release.state),\n ),\n })\n }),\n )\n // live-connection errors are already retried inside observeLiveEvents;\n // a fetch error is terminal and surfaced via setError\n .subscribe({error: (error) => state.set('setError', {error})})\n )\n}\n","import {createSelector} from 'reselect'\n\nimport {type DocumentResource, type PerspectiveHandle} from '../config/sanityConfig'\nimport {bindActionByResource, type BoundStoreAction} from '../store/createActionBinder'\nimport {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'\nimport {releasesStore, type ReleasesStoreState} from './releasesStore'\nimport {isReleasePerspective} from './utils/isReleasePerspective'\nimport {sortReleases} from './utils/sortReleases'\n\nconst DEFAULT_PERSPECTIVE = 'drafts'\n\n// Cache for options\nconst optionsCache = new Map<string, Map<string, PerspectiveHandle>>()\n\nconst selectInstancePerspective = (context: SelectorContext<ReleasesStoreState>, _?: unknown) =>\n context.instance.config.perspective\nconst selectActiveReleases = (context: SelectorContext<ReleasesStoreState>) =>\n context.state.activeReleases\nconst selectOptions = (\n _context: SelectorContext<ReleasesStoreState>,\n options: PerspectiveHandle & {projectId?: string; dataset?: string; resource?: DocumentResource},\n) => options\n\nconst memoizedOptionsSelector = createSelector(\n [selectActiveReleases, selectOptions],\n (activeReleases, options) => {\n if (!options || !activeReleases) return options\n\n // Use release document IDs as the cache key\n const releaseIds = activeReleases.map((release) => release._id).join(',')\n let nestedCache = optionsCache.get(releaseIds)\n if (!nestedCache) {\n nestedCache = new Map<string, PerspectiveHandle>()\n optionsCache.set(releaseIds, nestedCache)\n }\n\n const optionsKey = JSON.stringify(options)\n let cachedOptions = nestedCache.get(optionsKey)\n\n if (!cachedOptions) {\n cachedOptions = options\n nestedCache.set(optionsKey, cachedOptions)\n }\n return cachedOptions\n },\n)\n\nconst _getPerspectiveStateSelector = createStateSourceAction({\n selector: createSelector(\n [selectInstancePerspective, selectActiveReleases, memoizedOptionsSelector],\n (instancePerspective, activeReleases, memoizedOptions) => {\n const perspective = memoizedOptions?.perspective ?? instancePerspective ?? DEFAULT_PERSPECTIVE\n\n if (!isReleasePerspective(perspective)) return perspective\n\n // if there are no active releases we can't compute the release perspective\n if (!activeReleases || activeReleases.length === 0) return undefined\n\n const releaseNames = sortReleases(activeReleases).map((release) => release.name)\n const index = releaseNames.findIndex((name) => name === perspective.releaseName)\n\n if (index < 0) {\n throw new Error(`Release \"${perspective.releaseName}\" not found in active releases`)\n }\n\n const filteredReleases = releaseNames.slice(0, index + 1) // Include the release itself\n\n return ['drafts', ...filteredReleases]\n .filter((name) => !perspective.excludedPerspectives?.includes(name))\n .reverse()\n },\n ),\n})\n\ntype OmitFirst<T extends unknown[]> = T extends [unknown, ...infer R] ? R : never\ntype SelectorParams = OmitFirst<Parameters<typeof _getPerspectiveStateSelector>>\ntype BoundGetPerspectiveState = BoundStoreAction<\n ReleasesStoreState,\n SelectorParams,\n ReturnType<typeof _getPerspectiveStateSelector>\n>\n\nconst _boundGetPerspectiveState = bindActionByResource(\n releasesStore,\n _getPerspectiveStateSelector,\n) as BoundGetPerspectiveState\n\n/**\n * Provides a subscribable state source for a \"perspective\" for the Sanity client,\n * which is used to fetch documents as though certain Content Releases are active.\n *\n * @param instance - The Sanity instance to get the perspective for\n * @param options - The options for the perspective -- usually a release name\n *\n * @returns A subscribable perspective value, usually a list of applicable release names,\n * or a single release name / default perspective (such as 'drafts').\n *\n * @public\n */\nexport const getPerspectiveState: BoundGetPerspectiveState = (instance, ...rest) =>\n // bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options\n _boundGetPerspectiveState(instance, ...(rest.length ? rest : [{}]))\n","/**\n * When a query has no more subscribers, its state is cleaned up and removed\n * from the store. A delay used to prevent re-creating resources when the last\n * subscriber is removed quickly before another one is added. This is helpful\n * when used in a frontend where components may suspend or transition to\n * different views quickly.\n */\nexport const QUERY_STATE_CLEAR_DELAY = 1000\nexport const QUERY_STORE_API_VERSION = 'v2025-05-06'\nexport const QUERY_STORE_DEFAULT_PERSPECTIVE = 'drafts'\n","import {omitProperty} from '../utils/object'\n\ninterface QueryState {\n syncTags?: string[]\n result?: unknown\n error?: unknown\n lastLiveEventId?: string\n subscribers: string[]\n}\n\nexport interface QueryStoreState {\n queries: {[key: string]: QueryState | undefined}\n error?: unknown\n}\n\nexport const setQueryError =\n (key: string, error: unknown) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, error}}}\n }\n\nexport const setQueryData =\n (key: string, result: unknown, syncTags?: string[]) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n return {\n ...prev,\n queries: {...prev.queries, [key]: {...prevQuery, result: result ?? null, syncTags}},\n }\n }\n\nexport const setLastLiveEventId =\n (key: string, lastLiveEventId: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, lastLiveEventId}}}\n }\n\nexport const addSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n const subscribers = [...(prevQuery?.subscribers ?? []), subscriptionId]\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}\n }\n\nexport const removeSubscriber =\n (key: string, subscriptionId: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId)\n if (!subscribers.length) return {...prev, queries: omitProperty(prev.queries, key)}\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}\n }\n","import {type ResponseQueryOptions} from '@sanity/client'\nimport {\n catchError,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n NEVER,\n Observable,\n of,\n pairwise,\n race,\n share,\n startWith,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {observeLiveEvents} from '../client/liveEvents'\nimport {type DatasetHandle} from '../config/sanityConfig'\nimport {getPerspectiveState} from '../releases/getPerspectiveState'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {type StoreState} from '../store/createStoreState'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {type ResolveQueryResult} from '../typegen/resolve'\nimport {randomId} from '../utils/ids'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {\n QUERY_STATE_CLEAR_DELAY,\n QUERY_STORE_API_VERSION,\n QUERY_STORE_DEFAULT_PERSPECTIVE,\n} from './queryStoreConstants'\nimport {\n addSubscriber,\n type QueryStoreState,\n removeSubscriber,\n setLastLiveEventId,\n setQueryData,\n setQueryError,\n} from './reducers'\n\n/**\n * @beta\n */\nexport interface QueryOptions<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>\n extends\n Pick<ResponseQueryOptions, 'useCdn' | 'cache' | 'next' | 'cacheMode' | 'tag'>,\n DatasetHandle<TDataset, TProjectId> {\n query: TQuery\n params?: Record<string, unknown>\n}\n\n/**\n * @beta\n */\nexport interface ResolveQueryOptions<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends QueryOptions<TQuery, TDataset, TProjectId> {\n signal?: AbortSignal\n}\n\nconst EMPTY_ARRAY: never[] = []\n\n/** @internal */\nexport const getQueryKey = (instance: SanityInstance, options: QueryOptions): string =>\n JSON.stringify(normalizeOptionsWithPerspective(instance, options))\n/** @internal */\nexport const parseQueryKey = (key: string): QueryOptions => JSON.parse(key)\n\n/**\n * Ensures the query key includes an effective perspective so that\n * implicit differences (e.g. different instance.config.perspective)\n * don't collide in the dataset-scoped store.\n *\n * Since perspectives are unique, we can depend on the release stacks\n * to be correct when we retrieve the results.\n *\n */\nfunction normalizeOptionsWithPerspective(\n instance: SanityInstance,\n options: QueryOptions,\n): QueryOptions {\n if (options.perspective !== undefined) return options\n const instancePerspective = instance.config.perspective\n return {\n ...options,\n perspective:\n instancePerspective !== undefined ? instancePerspective : QUERY_STORE_DEFAULT_PERSPECTIVE,\n }\n}\n\nconst queryStore = defineStore<QueryStoreState, BoundResourceKey>({\n name: 'QueryStore',\n getInitialState: () => ({queries: {}}),\n initialize(context) {\n const subscriptions = [\n listenForNewSubscribersAndFetch(context),\n listenToLiveClientAndSetLastLiveEventIds(context),\n ]\n\n return () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe()\n }\n }\n },\n})\n\nconst errorHandler = (state: StoreState<{error?: unknown}>) => {\n return (error: unknown): void => state.set('setError', {error})\n}\n\nconst listenForNewSubscribersAndFetch = ({state, instance}: StoreContext<QueryStoreState>) => {\n return state.observable\n .pipe(\n map((s) => new Set(Object.keys(s.queries))),\n distinctUntilChanged((curr, next) => {\n if (curr.size !== next.size) return false\n return Array.from(next).every((i) => curr.has(i))\n }),\n startWith(new Set<string>()),\n pairwise(),\n mergeMap(([curr, next]) => {\n const added = Array.from(next).filter((i) => !curr.has(i))\n const removed = Array.from(curr).filter((i) => !next.has(i))\n\n return [\n ...added.map((key) => ({key, added: true})),\n ...removed.map((key) => ({key, added: false})),\n ]\n }),\n groupBy((i) => i.key),\n mergeMap((group$) =>\n group$.pipe(\n switchMap((e) => {\n if (!e.added) return EMPTY\n\n const lastLiveEventId$ = state.observable.pipe(\n map((s) => s.queries[group$.key]?.lastLiveEventId),\n distinctUntilChanged(),\n )\n const {\n query,\n params,\n projectId,\n dataset,\n tag,\n resource,\n perspective: perspectiveFromOptions,\n ...restOptions\n } = parseQueryKey(group$.key)\n\n // Short-circuit perspective resolution for non-release perspectives to avoid\n // touching the releases store (and its initialization) unnecessarily.\n const perspective$ = isReleasePerspective(perspectiveFromOptions)\n ? getPerspectiveState(instance, {\n perspective: perspectiveFromOptions,\n resource,\n }).observable.pipe(filter(Boolean))\n : of(perspectiveFromOptions ?? QUERY_STORE_DEFAULT_PERSPECTIVE)\n\n const client$ = getClientState(instance, {\n apiVersion: QUERY_STORE_API_VERSION,\n projectId,\n dataset,\n resource,\n }).observable\n\n return combineLatest({\n lastLiveEventId: lastLiveEventId$,\n client: client$,\n perspective: perspective$,\n }).pipe(\n switchMap(({lastLiveEventId, client, perspective}) =>\n client.observable.fetch(query, params, {\n ...restOptions,\n perspective,\n filterResponse: false,\n returnQuery: false,\n lastLiveEventId,\n tag,\n }),\n ),\n tap(({result, syncTags}) => {\n state.set('setQueryData', setQueryData(group$.key, result, syncTags))\n }),\n // Catch inside the per-event stream: erroring the group pipe would\n // complete the group's subscription, and since `groupBy` above never\n // removes its group subjects, re-adding the key would emit into a\n // subject with no subscribers — the key could never be fetched again.\n catchError((error) => {\n state.set('setQueryError', setQueryError(group$.key, error))\n return EMPTY\n }),\n )\n }),\n ),\n ),\n )\n .subscribe({error: errorHandler(state)})\n}\n\nconst listenToLiveClientAndSetLastLiveEventIds = ({\n state,\n instance,\n key: {resource},\n}: StoreContext<QueryStoreState, BoundResourceKey>) => {\n const liveMessages$ = observeLiveEvents(instance, {\n resource,\n // Surface CORS errors as store state (handled by the Cors Error\n // component) instead of erroring the stream: a stream error here would\n // reach the outer subscription's errorHandler, set the store-wide\n // `state.error`, and permanently brick every query selector.\n onCorsError: (error) => state.set('setError', {error}),\n }).pipe(share())\n\n return state.observable\n .pipe(\n mergeMap((s) => Object.entries(s.queries)),\n groupBy(([key]) => key),\n mergeMap((group$) => {\n const syncTags$ = group$.pipe(\n map(([, queryState]) => queryState),\n map((i) => i?.syncTags ?? EMPTY_ARRAY),\n distinctUntilChanged(),\n )\n\n return combineLatest([liveMessages$, syncTags$]).pipe(\n filter(([message, syncTags]) => message.tags.some((tag) => syncTags.includes(tag))),\n tap(([message]) => {\n state.set('setLastLiveEventId', setLastLiveEventId(group$.key, message.id))\n }),\n )\n }),\n )\n .subscribe({error: errorHandler(state)})\n}\n\n/**\n * Returns the state source for a query.\n *\n * This function returns a state source that represents the current result of a GROQ query.\n * Subscribing to the state source will instruct the SDK to fetch the query (if not already fetched)\n * and will keep the query live using the Live content API (considering sync tags) to provide up-to-date results.\n * When the last subscriber is removed, the query state is automatically cleaned up from the store.\n *\n * Note: This functionality is for advanced users who want to build their own framework integrations.\n * Our SDK also provides a React integration (useQuery hook) for convenient usage.\n *\n * Note: Automatic cleanup can interfere with React Suspense because if a component suspends while being the only subscriber,\n * cleanup might occur unexpectedly. In such cases, consider using `resolveQuery` instead.\n *\n * @beta\n */\nexport function getQueryState<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n queryOptions: QueryOptions<TQuery, TDataset, TProjectId>,\n): StateSource<ResolveQueryResult<TQuery, `${TProjectId}.${TDataset}`> | undefined>\n\n/** @beta */\nexport function getQueryState<TData>(\n instance: SanityInstance,\n queryOptions: QueryOptions,\n): StateSource<TData | undefined>\n\n/** @beta */\nexport function getQueryState(\n instance: SanityInstance,\n queryOptions: QueryOptions,\n): StateSource<unknown>\n\n/** @beta */\nexport function getQueryState(\n ...args: Parameters<typeof _getQueryState>\n): ReturnType<typeof _getQueryState> {\n return _getQueryState(...args)\n}\nconst _getQueryState = bindActionByResource(\n queryStore,\n createStateSourceAction({\n selector: ({state, instance}: SelectorContext<QueryStoreState>, options: QueryOptions) => {\n if (state.error) throw state.error\n const key = getQueryKey(instance, options)\n const queryState = state.queries[key]\n if (queryState?.error) throw queryState.error\n return queryState?.result\n },\n onSubscribe: ({state, instance}, options: QueryOptions) => {\n const subscriptionId = randomId(16)\n const key = getQueryKey(instance, options)\n\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n return () => {\n // this runs on unsubscribe\n setCleanupTimeout(\n () => state.set('removeSubscriber', removeSubscriber(key, subscriptionId)),\n QUERY_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Resolves the result of a query without registering a lasting subscriber.\n *\n * This function fetches the result of a GROQ query and returns a promise that resolves with the query result.\n * It registers a temporary subscriber for the lifetime of the resolution (released shortly after the promise\n * settles), so the query state participates in normal cleanup even when no lasting subscriber ever attaches —\n * e.g. when a suspended component errors before committing. This makes it ideal for use with React Suspense,\n * where the returned promise is thrown to delay rendering until the query result becomes available.\n * Once the promise resolves, it is expected that a real subscriber will be added via `getQueryState` to manage ongoing updates.\n *\n * Additionally, an optional AbortSignal can be provided to cancel the query and immediately clear the associated state\n * if there are no other active subscribers.\n *\n * @beta\n */\nexport function resolveQuery<\n TQuery extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n queryOptions: ResolveQueryOptions<TQuery, TDataset, TProjectId>,\n): Promise<ResolveQueryResult<TQuery, `${TProjectId}.${TDataset}`>>\n\n/** @beta */\nexport function resolveQuery<TData>(\n instance: SanityInstance,\n queryOptions: ResolveQueryOptions,\n): Promise<TData>\n/** @beta */\nexport function resolveQuery(...args: Parameters<typeof _resolveQuery>): Promise<unknown> {\n return _resolveQuery(...args)\n}\nconst _resolveQuery = bindActionByResource(\n queryStore,\n ({state, instance}, {signal, ...options}: ResolveQueryOptions) => {\n const normalized = normalizeOptionsWithPerspective(instance, options)\n const {getCurrent} = getQueryState(instance, normalized)\n const key = getQueryKey(instance, normalized)\n\n // Hold a temporary subscription for the lifetime of this resolution so the\n // key participates in normal cleanup. Without one, a fetch that errors\n // while a component is suspended leaves a subscriber-less key behind — the\n // component never commits, so no other subscriber exists — and its stored\n // error would be rethrown on every future mount without ever refetching.\n const subscriptionId = randomId(16)\n state.set('addSubscriber', addSubscriber(key, subscriptionId))\n\n const aborted$ = signal\n ? new Observable<void>((observer) => {\n const cleanup = () => {\n signal.removeEventListener('abort', listener)\n }\n\n const listener = () => {\n observer.error(new DOMException('The operation was aborted.', 'AbortError'))\n observer.complete()\n cleanup()\n }\n signal.addEventListener('abort', listener)\n\n return cleanup\n }).pipe(\n catchError((error) => {\n if (error instanceof Error && error.name === 'AbortError') {\n // Release immediately (not after the clear delay) so that when\n // this was the only subscriber, the key removal tears down the\n // in-flight request right away — e.g. when useQuery switches\n // to a different query\n state.set('removeSubscriber', removeSubscriber(key, subscriptionId))\n }\n throw error\n }),\n )\n : NEVER\n\n const resolved$ = state.observable.pipe(\n map(getCurrent),\n first((i) => i !== undefined),\n )\n\n const promise = firstValueFrom(race([resolved$, aborted$]))\n const releaseSubscriber = () => {\n setCleanupTimeout(() => {\n state.set('removeSubscriber', removeSubscriber(key, subscriptionId))\n }, QUERY_STATE_CLEAR_DELAY)\n }\n promise.then(releaseSubscriber, releaseSubscriber)\n return promise\n },\n)\n","","import {version} from '../package.json'\nimport {getEnv} from './utils/getEnv'\n\n/**\n * This version is provided by pkg-utils at build time\n * @internal\n */\nexport const CORE_SDK_VERSION = getEnv('PKG_VERSION') || `${version}-development`\n"],"mappings":"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"}
|