@sanity/sdk 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks-dts/utils.d.ts +295 -69
- package/dist/_chunks-es/_internal.js +3 -14
- package/dist/_chunks-es/_internal.js.map +1 -1
- package/dist/_chunks-es/createGroqSearchFilter.js +129 -59
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
- package/dist/_chunks-es/version.js +1 -1
- package/dist/_exports/_internal.d.ts +16 -2
- package/dist/_exports/_internal.js +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +275 -149
- package/dist/index.js.map +1 -1
- package/package.json +11 -15
- package/src/_exports/_internal.ts +1 -0
- package/src/_exports/index.ts +33 -2
- package/src/agent/agentActions.ts +21 -25
- package/src/client/clientStore.test.ts +24 -60
- package/src/client/clientStore.ts +49 -56
- package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
- package/src/comlink/node/actions/getOrCreateNode.test.ts +5 -2
- package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
- package/src/comlink/node/actions/releaseNode.test.ts +3 -3
- package/src/config/sanityConfig.ts +72 -13
- package/src/document/applyDocumentActions.test.ts +7 -7
- package/src/document/applyDocumentActions.ts +5 -5
- package/src/document/documentStore.test.ts +68 -62
- package/src/document/documentStore.ts +33 -38
- package/src/document/processActions.ts +2 -2
- package/src/document/reducers.ts +4 -4
- package/src/document/sharedListener.ts +5 -7
- package/src/organization/organization.test-d.ts +102 -0
- package/src/organization/organization.test.ts +138 -0
- package/src/organization/organization.ts +166 -0
- package/src/organizations/organizations.test-d.ts +77 -0
- package/src/organizations/organizations.test.ts +150 -0
- package/src/organizations/organizations.ts +132 -0
- package/src/presence/bifurTransport.test.ts +46 -6
- package/src/presence/bifurTransport.ts +13 -1
- package/src/presence/presenceStore.test.ts +101 -5
- package/src/presence/presenceStore.ts +96 -24
- package/src/preview/getPreviewState.ts +1 -1
- package/src/preview/previewProjectionUtils.test.ts +4 -4
- package/src/preview/previewProjectionUtils.ts +6 -7
- package/src/preview/resolvePreview.ts +5 -1
- package/src/project/project.test-d.ts +93 -0
- package/src/project/project.test.ts +108 -10
- package/src/project/project.ts +152 -26
- package/src/projection/getProjectionState.ts +4 -4
- package/src/projection/projectionStore.test.ts +2 -2
- package/src/projection/resolveProjection.ts +2 -2
- package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
- package/src/projection/subscribeToStateAndFetchBatches.ts +11 -15
- package/src/projects/projects.test-d.ts +38 -0
- package/src/projects/projects.test.ts +104 -38
- package/src/projects/projects.ts +74 -14
- package/src/query/queryStore.test.ts +12 -12
- package/src/query/queryStore.ts +10 -11
- package/src/query/reducers.ts +3 -3
- package/src/releases/getPerspectiveState.ts +5 -5
- package/src/releases/releasesStore.test.ts +6 -6
- package/src/releases/releasesStore.ts +9 -9
- package/src/store/createActionBinder.test.ts +31 -31
- package/src/store/createActionBinder.ts +43 -38
- package/src/store/createSanityInstance.ts +5 -6
- package/src/telemetry/devMode.test.ts +8 -0
- package/src/telemetry/devMode.ts +10 -9
- package/src/telemetry/initTelemetry.test.ts +0 -17
- package/src/telemetry/initTelemetry.ts +2 -12
- package/src/users/reducers.ts +3 -4
- package/src/utils/createFetcherStore.ts +6 -4
- package/src/utils/isImportError.test.ts +72 -0
- package/src/utils/isImportError.ts +34 -0
- package/src/utils/object.test.ts +95 -0
- package/src/utils/object.ts +142 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_internal.js","sources":["../../src/telemetry/devMode.ts","../../src/telemetry/__telemetry__/sdk.telemetry.ts","../../src/telemetry/initTelemetry.ts"],"sourcesContent":["/**\n * Checks whether the current URL points to a local development server.\n *\n * @param win - The window object to check\n * @returns True if running on localhost or 127.0.0.1\n * @internal\n */\nfunction isLocalUrl(win: Window): boolean {\n const url = win.location?.href\n if (!url) return false\n return (\n url.startsWith('http://localhost') ||\n url.startsWith('https://localhost') ||\n url.startsWith('http://127.0.0.1') ||\n url.startsWith('https://127.0.0.1')\n )\n}\n\n/**\n * Determines whether the SDK should enable dev-mode telemetry.\n *\n * Combines a browser URL check (localhost/127.0.0.1) with a Node.js\n * environment variable check (`NODE_ENV === 'development'`). Returns\n * false in production environments so bundlers can tree-shake the\n * telemetry code path entirely.\n *\n * @returns True if the SDK is running in a development environment\n * @internal\n */\nexport function isDevMode(): boolean {\n if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'production') {\n return false\n }\n\n if (typeof window !== 'undefined') {\n return isLocalUrl(window)\n }\n\n return typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development'\n}\n","import {defineEvent} from '@sanity/telemetry'\n\n/** @internal */\nexport const SDKDevSessionStarted = defineEvent<{\n version: string\n projectId: string\n perspective: string\n authMethod: string\n}>({\n name: 'SDK Dev Session Started',\n version: 1,\n description: 'SDK instance created in development mode',\n})\n\n/** @internal */\nexport const SDKHookMounted = defineEvent<{\n hookName: string\n}>({\n name: 'SDK Hook Mounted',\n version: 1,\n description: 'An SDK hook was mounted for the first time in this session',\n})\n\n/** @internal */\nexport const SDKDevSessionEnded = defineEvent<{\n durationSeconds: number\n hooksUsed: string[]\n}>({\n name: 'SDK Dev Session Ended',\n version: 1,\n description: 'SDK instance disposed in development mode',\n})\n\n/** @internal */\nexport const SDKDevError = defineEvent<{\n errorType: string\n hookName: string\n}>({\n name: 'SDK Dev Error',\n version: 1,\n description: 'Runtime error caught during SDK development',\n})\n","import {type SanityInstance} from '../store/createSanityInstance'\nimport {createLogger} from '../utils/logger'\nimport {isDevMode} from './devMode'\nimport {type TelemetryManager} from './telemetryManager'\n\nconst DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'\n\nconst logger = createLogger('telemetry')\n\n/**\n * Per-instance map of active telemetry managers. Allows the React\n * package (and other consumers) to look up the manager for a given\n * instance and log hook usage / errors without importing the full\n * telemetry module themselves.\n *\n * @internal\n */\nconst telemetryManagers = new WeakMap<SanityInstance, TelemetryManager>()\nconst pendingHooks = new WeakMap<SanityInstance, Set<string>>()\nconst initInFlight = new WeakSet<SanityInstance>()\n\n/**\n * Initializes dev-mode telemetry for a SDK instance if the environment\n * qualifies. Both `telemetryManager` and `clientStore` are dynamically\n * imported to avoid circular dependencies and to keep telemetry code\n * out of production bundles via code splitting.\n *\n * The `projectId` must be passed explicitly because the resource\n * configuration is typically set by the React layer after the\n * instance has already been created.\n *\n * @internal\n */\nexport function initTelemetry(instance: SanityInstance, projectId: string): void {\n if (!isDevMode()) {\n logger.trace('initTelemetry skipped: not dev mode', {internal: true})\n return\n }\n if (!projectId) {\n logger.trace('initTelemetry skipped: no projectId', {internal: true})\n return\n }\n if (telemetryManagers.has(instance) || initInFlight.has(instance)) {\n return\n }\n initInFlight.add(instance)\n\n logger.debug('initializing telemetry', {projectId})\n\n Promise.all([\n import('./telemetryManager'),\n import('../client/clientStore'),\n import('../auth/authStore'),\n ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n const sub = getTokenState(instance).observable.subscribe((t) => {\n if (t) {\n logger.debug('auth token received')\n sub.unsubscribe()\n unsub()\n resolve(true)\n }\n })\n cleanup.unsubscribe = () => sub.unsubscribe()\n })\n if (!hasToken || instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: no token resolved or instance disposed')\n return\n }\n }\n\n const manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n logger.info('telemetry session started', {projectId, perspective, authMethod})\n manager.logSessionStarted({\n projectId,\n perspective,\n authMethod,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!isDevMode()) return\n\n const manager = findManager(instance)\n if (manager) {\n logger.trace('hook mounted (logged)', {hookName, internal: true})\n manager.logHookFirstUsed(hookName)\n return\n }\n\n const root = getRootInstance(instance)\n let hooks = pendingHooks.get(root)\n if (!hooks) {\n hooks = new Set()\n pendingHooks.set(root, hooks)\n }\n if (!hooks.has(hookName)) {\n logger.trace('hook mounted (buffered)', {hookName, internal: true})\n }\n hooks.add(hookName)\n}\n\nfunction findManager(instance: SanityInstance): TelemetryManager | undefined {\n let current: SanityInstance | undefined = instance\n while (current) {\n const manager = telemetryManagers.get(current)\n if (manager) return manager\n current = typeof current.getParent === 'function' ? current.getParent() : undefined\n }\n return undefined\n}\n\nfunction getRootInstance(instance: SanityInstance): SanityInstance {\n let current = instance\n while (typeof current.getParent === 'function' && current.getParent()) {\n current = current.getParent()!\n }\n return current\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_internal.js","sources":["../../src/telemetry/devMode.ts","../../src/telemetry/__telemetry__/sdk.telemetry.ts","../../src/telemetry/initTelemetry.ts"],"sourcesContent":["/**\n * Checks whether the current URL points to a local development server.\n *\n * @param win - The window object to check\n * @returns True if running on localhost or 127.0.0.1\n * @internal\n */\nfunction isLocalUrl(win: Window): boolean {\n const url = win.location?.href\n if (!url) return false\n return (\n url.startsWith('http://localhost') ||\n url.startsWith('https://localhost') ||\n url.startsWith('http://127.0.0.1') ||\n url.startsWith('https://127.0.0.1')\n )\n}\n\n/**\n * Determines whether the SDK should enable dev-mode telemetry for the\n * SDK consumer (i.e. a developer building an app with `@sanity/sdk`).\n *\n * Browser: returns true only when the URL is `localhost` or `127.0.0.1`.\n * The URL check is the primary signal because consumer bundlers may or\n * may not forward `NODE_ENV` to the browser reliably.\n *\n * Node (scripts / non-browser): falls back to `NODE_ENV === 'development'`.\n *\n * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler\n * dead-code replacement.\n *\n * @returns True if the SDK is running in a development environment\n * @internal\n */\nexport function isDevMode(): boolean {\n if (typeof window !== 'undefined') {\n return isLocalUrl(window)\n }\n\n return typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development'\n}\n","import {defineEvent} from '@sanity/telemetry'\n\n/** @internal */\nexport const SDKDevSessionStarted = defineEvent<{\n version: string\n projectId: string\n perspective: string\n authMethod: string\n}>({\n name: 'SDK Dev Session Started',\n version: 1,\n description: 'SDK instance created in development mode',\n})\n\n/** @internal */\nexport const SDKHookMounted = defineEvent<{\n hookName: string\n}>({\n name: 'SDK Hook Mounted',\n version: 1,\n description: 'An SDK hook was mounted for the first time in this session',\n})\n\n/** @internal */\nexport const SDKDevSessionEnded = defineEvent<{\n durationSeconds: number\n hooksUsed: string[]\n}>({\n name: 'SDK Dev Session Ended',\n version: 1,\n description: 'SDK instance disposed in development mode',\n})\n\n/** @internal */\nexport const SDKDevError = defineEvent<{\n errorType: string\n hookName: string\n}>({\n name: 'SDK Dev Error',\n version: 1,\n description: 'Runtime error caught during SDK development',\n})\n","import {type SanityInstance} from '../store/createSanityInstance'\nimport {createLogger} from '../utils/logger'\nimport {isDevMode} from './devMode'\nimport {type TelemetryManager} from './telemetryManager'\n\nconst DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'\n\nconst logger = createLogger('telemetry')\n\n/**\n * Per-instance map of active telemetry managers. Allows the React\n * package (and other consumers) to look up the manager for a given\n * instance and log hook usage / errors without importing the full\n * telemetry module themselves.\n *\n * @internal\n */\nconst telemetryManagers = new WeakMap<SanityInstance, TelemetryManager>()\nconst pendingHooks = new WeakMap<SanityInstance, Set<string>>()\nconst initInFlight = new WeakSet<SanityInstance>()\n\n/**\n * Initializes dev-mode telemetry for a SDK instance if the environment\n * qualifies. Both `telemetryManager` and `clientStore` are dynamically\n * imported to avoid circular dependencies and to keep telemetry code\n * out of production bundles via code splitting.\n *\n * The `projectId` must be passed explicitly because the resource\n * configuration is typically set by the React layer after the\n * instance has already been created.\n *\n * @internal\n */\nexport function initTelemetry(instance: SanityInstance, projectId: string): void {\n if (!isDevMode()) {\n logger.trace('initTelemetry skipped: not dev mode', {internal: true})\n return\n }\n if (!projectId) {\n logger.trace('initTelemetry skipped: no projectId', {internal: true})\n return\n }\n if (telemetryManagers.has(instance) || initInFlight.has(instance)) {\n return\n }\n initInFlight.add(instance)\n\n logger.debug('initializing telemetry', {projectId})\n\n Promise.all([\n import('./telemetryManager'),\n import('../client/clientStore'),\n import('../auth/authStore'),\n ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n const sub = getTokenState(instance).observable.subscribe((t) => {\n if (t) {\n logger.debug('auth token received')\n sub.unsubscribe()\n unsub()\n resolve(true)\n }\n })\n cleanup.unsubscribe = () => sub.unsubscribe()\n })\n if (!hasToken || instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: no token resolved or instance disposed')\n return\n }\n }\n\n const manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n logger.info('telemetry session started', {projectId, perspective, authMethod})\n manager.logSessionStarted({\n projectId,\n perspective,\n authMethod,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!isDevMode()) return\n\n const manager = findManager(instance)\n if (manager) {\n logger.trace('hook mounted (logged)', {hookName, internal: true})\n manager.logHookFirstUsed(hookName)\n return\n }\n\n const root = getRootInstance(instance)\n let hooks = pendingHooks.get(root)\n if (!hooks) {\n hooks = new Set()\n pendingHooks.set(root, hooks)\n }\n if (!hooks.has(hookName)) {\n logger.trace('hook mounted (buffered)', {hookName, internal: true})\n }\n hooks.add(hookName)\n}\n\nfunction findManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\nfunction getRootInstance(instance: SanityInstance): SanityInstance {\n return instance\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import { pick, omit, isObject } from "lodash-es";
|
|
2
1
|
import { createClient, CorsOriginError } from "@sanity/client";
|
|
3
|
-
import { Observable, map, distinctUntilChanged, shareReplay, skip, defer, finalize, filter, exhaustMap,
|
|
2
|
+
import { Observable, map, distinctUntilChanged, shareReplay, skip, defer, finalize, filter, exhaustMap, timer, switchMap, takeWhile, from, firstValueFrom, EMPTY, fromEvent, catchError, NEVER, first, race, startWith, pairwise, mergeMap, groupBy, of, combineLatest, tap, share } from "rxjs";
|
|
4
3
|
import { createSelector } from "reselect";
|
|
5
4
|
import { createImageUrlBuilder } from "@sanity/image-url";
|
|
6
5
|
import { devtools } from "zustand/middleware";
|
|
7
6
|
import { createStore } from "zustand/vanilla";
|
|
7
|
+
function isDatasetResource(resource) {
|
|
8
|
+
return "projectId" in resource && "dataset" in resource;
|
|
9
|
+
}
|
|
10
|
+
function isMediaLibraryResource(resource) {
|
|
11
|
+
return "mediaLibraryId" in resource;
|
|
12
|
+
}
|
|
13
|
+
function isCanvasResource(resource) {
|
|
14
|
+
return "canvasId" in resource;
|
|
15
|
+
}
|
|
8
16
|
function isDatasetSource(source) {
|
|
9
|
-
return
|
|
17
|
+
return isDatasetResource(source);
|
|
10
18
|
}
|
|
11
19
|
function isMediaLibrarySource(source) {
|
|
12
|
-
return
|
|
20
|
+
return isMediaLibraryResource(source);
|
|
13
21
|
}
|
|
14
22
|
function isCanvasSource(source) {
|
|
15
|
-
return
|
|
23
|
+
return isCanvasResource(source);
|
|
16
24
|
}
|
|
17
25
|
const isReleasePerspective = (perspective) => typeof perspective == "object" && perspective !== null && "releaseName" in perspective;
|
|
18
26
|
function insecureRandomId() {
|
|
@@ -132,6 +140,73 @@ function createLogger(namespace, baseContext) {
|
|
|
132
140
|
getInstanceContext: () => baseContext?.instanceContext
|
|
133
141
|
};
|
|
134
142
|
}
|
|
143
|
+
function isObject(value) {
|
|
144
|
+
return typeof value == "object" && value !== null;
|
|
145
|
+
}
|
|
146
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key), isPlainObject = (value) => {
|
|
147
|
+
if (!isObject(value)) return !1;
|
|
148
|
+
const prototype = Object.getPrototypeOf(value);
|
|
149
|
+
return prototype === Object.prototype || prototype === null;
|
|
150
|
+
};
|
|
151
|
+
function omitProperty(value, key) {
|
|
152
|
+
if (!value) return {};
|
|
153
|
+
const { [key]: _omitted, ...rest } = value;
|
|
154
|
+
return rest;
|
|
155
|
+
}
|
|
156
|
+
function pickProperties(value, keys) {
|
|
157
|
+
const result = {};
|
|
158
|
+
for (const key of keys)
|
|
159
|
+
hasOwn(value, key) && (result[key] = value[key]);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
const areSetsEqual = (left, right) => {
|
|
163
|
+
if (left.size !== right.size) return !1;
|
|
164
|
+
const unmatched = [...right];
|
|
165
|
+
outer: for (const leftValue of left) {
|
|
166
|
+
for (let index = 0; index < unmatched.length; index++)
|
|
167
|
+
if (isDeepEqual(leftValue, unmatched[index])) {
|
|
168
|
+
unmatched.splice(index, 1);
|
|
169
|
+
continue outer;
|
|
170
|
+
}
|
|
171
|
+
return !1;
|
|
172
|
+
}
|
|
173
|
+
return unmatched.length === 0;
|
|
174
|
+
}, areMapsEqual = (left, right) => {
|
|
175
|
+
if (left.size !== right.size) return !1;
|
|
176
|
+
const unmatched = [...right.entries()];
|
|
177
|
+
outer: for (const [leftKey, leftValue] of left) {
|
|
178
|
+
for (let index = 0; index < unmatched.length; index++) {
|
|
179
|
+
const [rightKey, rightValue] = unmatched[index];
|
|
180
|
+
if (isDeepEqual(leftKey, rightKey) && isDeepEqual(leftValue, rightValue)) {
|
|
181
|
+
unmatched.splice(index, 1);
|
|
182
|
+
continue outer;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return !1;
|
|
186
|
+
}
|
|
187
|
+
return unmatched.length === 0;
|
|
188
|
+
};
|
|
189
|
+
function isDeepEqual(left, right) {
|
|
190
|
+
if (Object.is(left, right)) return !0;
|
|
191
|
+
if (!isObject(left) || !isObject(right)) return !1;
|
|
192
|
+
if (left instanceof Date && right instanceof Date)
|
|
193
|
+
return left.getTime() === right.getTime();
|
|
194
|
+
if (left instanceof RegExp && right instanceof RegExp)
|
|
195
|
+
return left.source === right.source && left.flags === right.flags;
|
|
196
|
+
if (left instanceof Set && right instanceof Set)
|
|
197
|
+
return areSetsEqual(left, right);
|
|
198
|
+
if (left instanceof Map && right instanceof Map)
|
|
199
|
+
return areMapsEqual(left, right);
|
|
200
|
+
if (Array.isArray(left) || Array.isArray(right))
|
|
201
|
+
return !Array.isArray(left) || !Array.isArray(right) || left.length !== right.length ? !1 : left.every((value, index) => isDeepEqual(value, right[index]));
|
|
202
|
+
if (!isPlainObject(left) || !isPlainObject(right)) return !1;
|
|
203
|
+
const leftKeys = Object.keys(left), rightKeys = Object.keys(right);
|
|
204
|
+
if (leftKeys.length !== rightKeys.length) return !1;
|
|
205
|
+
for (const key of leftKeys)
|
|
206
|
+
if (!hasOwn(right, key) || !isDeepEqual(left[key], right[key]))
|
|
207
|
+
return !1;
|
|
208
|
+
return !0;
|
|
209
|
+
}
|
|
135
210
|
function getEnv(key) {
|
|
136
211
|
if (typeof import.meta < "u" && import.meta.env)
|
|
137
212
|
return import.meta.env[key];
|
|
@@ -186,36 +261,31 @@ function createActionBinder(keyFn) {
|
|
|
186
261
|
};
|
|
187
262
|
};
|
|
188
263
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
name = `${source.projectId}.${source.dataset}`;
|
|
199
|
-
else if (isMediaLibrarySource(source))
|
|
200
|
-
name = `media-library:${source.mediaLibraryId}`;
|
|
201
|
-
else if (isCanvasSource(source))
|
|
202
|
-
name = `canvas:${source.canvasId}`;
|
|
264
|
+
const createResourceKey = (instance, resource) => {
|
|
265
|
+
let name, resourceForKey;
|
|
266
|
+
if (resource) {
|
|
267
|
+
if (resourceForKey = resource, isDatasetResource(resource))
|
|
268
|
+
name = `${resource.projectId}.${resource.dataset}`;
|
|
269
|
+
else if (isMediaLibraryResource(resource))
|
|
270
|
+
name = `media-library:${resource.mediaLibraryId}`;
|
|
271
|
+
else if (isCanvasResource(resource))
|
|
272
|
+
name = `canvas:${resource.canvasId}`;
|
|
203
273
|
else
|
|
204
|
-
throw new Error(`Received invalid
|
|
205
|
-
return { name,
|
|
274
|
+
throw new Error(`Received invalid resource: ${JSON.stringify(resource)}`);
|
|
275
|
+
return { name, resource: resourceForKey };
|
|
206
276
|
}
|
|
207
277
|
const { projectId, dataset } = instance.config;
|
|
208
278
|
if (!projectId || !dataset)
|
|
209
279
|
throw new Error("This API requires a project ID and dataset configured.");
|
|
210
|
-
return { name: `${projectId}.${dataset}`,
|
|
211
|
-
},
|
|
212
|
-
const {
|
|
280
|
+
return { name: `${projectId}.${dataset}`, resource: { projectId, dataset } };
|
|
281
|
+
}, bindActionByResource = createActionBinder((instance, { resource }) => createResourceKey(instance, resource)), bindActionByResourceAndPerspective = createActionBinder((instance, options) => {
|
|
282
|
+
const { resource, perspective } = options, utilizedPerspective = perspective ?? instance.config.perspective ?? "drafts";
|
|
213
283
|
let perspectiveKey;
|
|
214
284
|
isReleasePerspective(utilizedPerspective) ? perspectiveKey = utilizedPerspective.releaseName : typeof utilizedPerspective == "string" ? perspectiveKey = utilizedPerspective : perspectiveKey = JSON.stringify(utilizedPerspective);
|
|
215
|
-
const sourceKey =
|
|
285
|
+
const sourceKey = createResourceKey(instance, resource);
|
|
216
286
|
return {
|
|
217
287
|
name: `${sourceKey.name}:${perspectiveKey}`,
|
|
218
|
-
|
|
288
|
+
resource: sourceKey.resource,
|
|
219
289
|
perspective: utilizedPerspective
|
|
220
290
|
};
|
|
221
291
|
}), bindActionGlobally = createActionBinder((..._rest) => ({ name: "global" }));
|
|
@@ -931,8 +1001,7 @@ const DEFAULT_API_VERSION = "2024-11-12", DEFAULT_REQUEST_TAG_PREFIX = "sanity.s
|
|
|
931
1001
|
apiVersion: null,
|
|
932
1002
|
requestTagPrefix: null,
|
|
933
1003
|
useProjectHostname: null,
|
|
934
|
-
|
|
935
|
-
source: null
|
|
1004
|
+
resource: null
|
|
936
1005
|
}), DEFAULT_CLIENT_CONFIG = {
|
|
937
1006
|
apiVersion: DEFAULT_API_VERSION,
|
|
938
1007
|
useCdn: !1,
|
|
@@ -955,7 +1024,7 @@ const DEFAULT_API_VERSION = "2024-11-12", DEFAULT_REQUEST_TAG_PREFIX = "sanity.s
|
|
|
955
1024
|
state.set("setTokenAndResetClients", { token, clients: {} });
|
|
956
1025
|
}), listenToAuthMethod = ({ instance, state }) => getAuthMethodState(instance).observable.subscribe((authMethod) => {
|
|
957
1026
|
state.set("setAuthMethod", { authMethod });
|
|
958
|
-
}), getClientConfigKey = (options) => JSON.stringify(
|
|
1027
|
+
}), getClientConfigKey = (options) => JSON.stringify(pickProperties(options, allowedKeys)), getClient = bindActionGlobally(
|
|
959
1028
|
clientStore,
|
|
960
1029
|
({ state, instance }, options) => {
|
|
961
1030
|
if (!options || typeof options != "object")
|
|
@@ -970,21 +1039,19 @@ const DEFAULT_API_VERSION = "2024-11-12", DEFAULT_REQUEST_TAG_PREFIX = "sanity.s
|
|
|
970
1039
|
);
|
|
971
1040
|
}
|
|
972
1041
|
const tokenFromState = state.get().token, { clients, authMethod } = state.get();
|
|
973
|
-
let resource;
|
|
974
|
-
options.
|
|
975
|
-
const
|
|
1042
|
+
let projectId = options.projectId ?? instance.config.projectId, dataset = options.dataset ?? instance.config.dataset, resource;
|
|
1043
|
+
options.resource && (isMediaLibraryResource(options.resource) ? resource = { type: "media-library", id: options.resource.mediaLibraryId } : isCanvasResource(options.resource) ? resource = { type: "canvas", id: options.resource.canvasId } : isDatasetResource(options.resource) && (projectId = options.resource.projectId, dataset = options.resource.dataset));
|
|
1044
|
+
const apiHost = options.apiHost ?? instance.config.auth?.apiHost ?? getStagingApiHost(), effectiveOptions = {
|
|
976
1045
|
...DEFAULT_CLIENT_CONFIG,
|
|
977
1046
|
...(options.scope === "global" || !projectId || resource) && { useProjectHostname: !1 },
|
|
978
1047
|
token: authMethod === "cookie" ? void 0 : tokenFromState ?? void 0,
|
|
979
1048
|
...options,
|
|
980
1049
|
...projectId && { projectId },
|
|
981
1050
|
...dataset && { dataset },
|
|
982
|
-
...
|
|
983
|
-
...
|
|
1051
|
+
...resource ? { resource } : { resource: void 0 },
|
|
1052
|
+
...apiHost && { apiHost }
|
|
984
1053
|
};
|
|
985
|
-
resource && (
|
|
986
|
-
"Both source and explicit projectId/dataset are provided. The source will be used and projectId/dataset will be ignored."
|
|
987
|
-
), delete effectiveOptions.projectId, delete effectiveOptions.dataset), effectiveOptions.token === null || typeof effectiveOptions.token > "u" ? (delete effectiveOptions.token, authMethod === "cookie" && (effectiveOptions.withCredentials = !0)) : delete effectiveOptions.withCredentials;
|
|
1054
|
+
resource && (delete effectiveOptions.projectId, delete effectiveOptions.dataset), effectiveOptions.token === null || typeof effectiveOptions.token > "u" ? (delete effectiveOptions.token, authMethod === "cookie" && (effectiveOptions.withCredentials = !0)) : delete effectiveOptions.withCredentials;
|
|
988
1055
|
const key = getClientConfigKey(effectiveOptions);
|
|
989
1056
|
if (clients[key]) return clients[key];
|
|
990
1057
|
const client = createClient(effectiveOptions);
|
|
@@ -1022,7 +1089,7 @@ const API_VERSION$1 = "vX", PROJECT_API_VERSION = "2025-07-18", USERS_STATE_CLEA
|
|
|
1022
1089
|
const group = prev.users[key];
|
|
1023
1090
|
if (!group) return prev;
|
|
1024
1091
|
const subscriptions = group.subscriptions.filter((id) => id !== subscriptionId);
|
|
1025
|
-
return subscriptions.length ? { ...prev, users: { ...prev.users, [key]: { ...group, subscriptions } } } : { ...prev, users:
|
|
1092
|
+
return subscriptions.length ? { ...prev, users: { ...prev.users, [key]: { ...group, subscriptions } } } : { ...prev, users: omitProperty(prev.users, key) };
|
|
1026
1093
|
}, setUsersData = (key, { data, nextCursor, totalCount }) => (prev) => {
|
|
1027
1094
|
const group = prev.users[key];
|
|
1028
1095
|
if (!group) return prev;
|
|
@@ -1036,7 +1103,7 @@ const API_VERSION$1 = "vX", PROJECT_API_VERSION = "2025-07-18", USERS_STATE_CLEA
|
|
|
1036
1103
|
return group ? { ...prev, users: { ...prev.users, [key]: { ...group, error } } } : prev;
|
|
1037
1104
|
}, cancelRequest = (key) => (prev) => {
|
|
1038
1105
|
const group = prev.users[key];
|
|
1039
|
-
return !group || group.subscriptions.length ? prev : { ...prev, users:
|
|
1106
|
+
return !group || group.subscriptions.length ? prev : { ...prev, users: omitProperty(prev.users, key) };
|
|
1040
1107
|
}, initializeRequest = (key) => (prev) => prev.users[key] ? prev : { ...prev, users: { ...prev.users, [key]: { subscriptions: [] } } };
|
|
1041
1108
|
function sortReleases(releases = []) {
|
|
1042
1109
|
return [...releases].sort((a, b) => {
|
|
@@ -1065,23 +1132,23 @@ const ARCHIVED_RELEASE_STATES = ["archived", "published"], STABLE_EMPTY_RELEASES
|
|
|
1065
1132
|
const subscription = subscribeToReleases(context);
|
|
1066
1133
|
return () => subscription.unsubscribe();
|
|
1067
1134
|
}
|
|
1068
|
-
}, _getActiveReleasesState =
|
|
1135
|
+
}, _getActiveReleasesState = bindActionByResource(
|
|
1069
1136
|
releasesStore,
|
|
1070
1137
|
createStateSourceAction({
|
|
1071
1138
|
selector: ({ state }, _) => state.activeReleases
|
|
1072
1139
|
})
|
|
1073
1140
|
), getActiveReleasesState = (instance, options) => (
|
|
1074
|
-
//
|
|
1141
|
+
// bindActionByResource keyFn destructures { resource } from the first param, so pass {} when no options
|
|
1075
1142
|
_getActiveReleasesState(instance, options ?? {})
|
|
1076
1143
|
), RELEASES_QUERY = "releases::all()", subscribeToReleases = ({
|
|
1077
1144
|
instance,
|
|
1078
1145
|
state,
|
|
1079
|
-
key: {
|
|
1146
|
+
key: { resource }
|
|
1080
1147
|
}) => {
|
|
1081
1148
|
const { observable: releases$ } = getQueryState(instance, {
|
|
1082
1149
|
query: RELEASES_QUERY,
|
|
1083
1150
|
perspective: "raw",
|
|
1084
|
-
|
|
1151
|
+
resource,
|
|
1085
1152
|
tag: "releases"
|
|
1086
1153
|
});
|
|
1087
1154
|
return releases$.pipe(
|
|
@@ -1117,7 +1184,7 @@ const ARCHIVED_RELEASE_STATES = ["archived", "published"], STABLE_EMPTY_RELEASES
|
|
|
1117
1184
|
)
|
|
1118
1185
|
});
|
|
1119
1186
|
let _boundGetPerspectiveState;
|
|
1120
|
-
const getPerspectiveState = (instance, ...rest) => (_boundGetPerspectiveState || (_boundGetPerspectiveState =
|
|
1187
|
+
const getPerspectiveState = (instance, ...rest) => (_boundGetPerspectiveState || (_boundGetPerspectiveState = bindActionByResource(
|
|
1121
1188
|
releasesStore,
|
|
1122
1189
|
_getPerspectiveStateSelector
|
|
1123
1190
|
)), _boundGetPerspectiveState(instance, ...rest.length ? rest : [{}])), QUERY_STATE_CLEAR_DELAY = 1e3, QUERY_STORE_API_VERSION = "v2025-05-06", QUERY_STORE_DEFAULT_PERSPECTIVE = "drafts", setQueryError = (key, error) => (prev) => {
|
|
@@ -1139,10 +1206,10 @@ const getPerspectiveState = (instance, ...rest) => (_boundGetPerspectiveState ||
|
|
|
1139
1206
|
const prevQuery = prev.queries[key];
|
|
1140
1207
|
if (!prevQuery) return prev;
|
|
1141
1208
|
const subscribers = prevQuery.subscribers.filter((id) => id !== subscriptionId);
|
|
1142
|
-
return subscribers.length ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, subscribers } } } : { ...prev, queries:
|
|
1209
|
+
return subscribers.length ? { ...prev, queries: { ...prev.queries, [key]: { ...prevQuery, subscribers } } } : { ...prev, queries: omitProperty(prev.queries, key) };
|
|
1143
1210
|
}, cancelQuery = (key) => (prev) => {
|
|
1144
1211
|
const prevQuery = prev.queries[key];
|
|
1145
|
-
return !prevQuery || prevQuery.subscribers.length ? prev : { ...prev, queries:
|
|
1212
|
+
return !prevQuery || prevQuery.subscribers.length ? prev : { ...prev, queries: omitProperty(prev.queries, key) };
|
|
1146
1213
|
}, initializeQuery = (key) => (prev) => prev.queries[key] ? prev : { ...prev, queries: { ...prev.queries, [key]: { subscribers: [] } } }, EMPTY_ARRAY = [], getQueryKey = (options) => JSON.stringify(options), parseQueryKey = (key) => JSON.parse(key);
|
|
1147
1214
|
function normalizeOptionsWithPerspective(instance, options) {
|
|
1148
1215
|
if (options.perspective !== void 0) return options;
|
|
@@ -1191,7 +1258,7 @@ const queryStore = {
|
|
|
1191
1258
|
projectId,
|
|
1192
1259
|
dataset,
|
|
1193
1260
|
tag,
|
|
1194
|
-
|
|
1261
|
+
resource,
|
|
1195
1262
|
perspective: perspectiveFromOptions,
|
|
1196
1263
|
...restOptions
|
|
1197
1264
|
} = parseQueryKey(group$.key), perspective$ = isReleasePerspective(perspectiveFromOptions) ? getPerspectiveState(instance, {
|
|
@@ -1200,7 +1267,7 @@ const queryStore = {
|
|
|
1200
1267
|
apiVersion: QUERY_STORE_API_VERSION,
|
|
1201
1268
|
projectId,
|
|
1202
1269
|
dataset,
|
|
1203
|
-
|
|
1270
|
+
resource
|
|
1204
1271
|
}).observable;
|
|
1205
1272
|
return combineLatest({
|
|
1206
1273
|
lastLiveEventId: lastLiveEventId$,
|
|
@@ -1228,12 +1295,11 @@ const queryStore = {
|
|
|
1228
1295
|
).subscribe({ error: errorHandler(state) }), listenToLiveClientAndSetLastLiveEventIds = ({
|
|
1229
1296
|
state,
|
|
1230
1297
|
instance,
|
|
1231
|
-
key: {
|
|
1298
|
+
key: { resource }
|
|
1232
1299
|
}) => {
|
|
1233
1300
|
const liveMessages$ = getClientState(instance, {
|
|
1234
1301
|
apiVersion: QUERY_STORE_API_VERSION,
|
|
1235
|
-
|
|
1236
|
-
...source && !isDatasetSource(source) ? { source } : {}
|
|
1302
|
+
resource
|
|
1237
1303
|
}).observable.pipe(
|
|
1238
1304
|
switchMap(
|
|
1239
1305
|
(client) => defer(
|
|
@@ -1270,7 +1336,7 @@ const queryStore = {
|
|
|
1270
1336
|
function getQueryState(...args) {
|
|
1271
1337
|
return _getQueryState(...args);
|
|
1272
1338
|
}
|
|
1273
|
-
const _getQueryState =
|
|
1339
|
+
const _getQueryState = bindActionByResource(
|
|
1274
1340
|
queryStore,
|
|
1275
1341
|
createStateSourceAction({
|
|
1276
1342
|
selector: ({ state, instance }, options) => {
|
|
@@ -1293,7 +1359,7 @@ const _getQueryState = bindActionBySource(
|
|
|
1293
1359
|
function resolveQuery(...args) {
|
|
1294
1360
|
return _resolveQuery(...args);
|
|
1295
1361
|
}
|
|
1296
|
-
const _resolveQuery =
|
|
1362
|
+
const _resolveQuery = bindActionByResource(
|
|
1297
1363
|
queryStore,
|
|
1298
1364
|
({ state, instance }, { signal, ...options }) => {
|
|
1299
1365
|
const normalized = normalizeOptionsWithPerspective(instance, options), { getCurrent } = getQueryState(instance, normalized), key = getQueryKey(normalized), aborted$ = signal ? new Observable((observer) => {
|
|
@@ -1358,11 +1424,10 @@ function findFirstDefined(fieldsToSearch, candidates, exclude) {
|
|
|
1358
1424
|
return value;
|
|
1359
1425
|
}
|
|
1360
1426
|
}
|
|
1361
|
-
function transformProjectionToPreview(instance, projectionResult,
|
|
1427
|
+
function transformProjectionToPreview(instance, projectionResult, resource) {
|
|
1362
1428
|
const title = findFirstDefined(TITLE_CANDIDATES, projectionResult.titleCandidates), subtitle = findFirstDefined(SUBTITLE_CANDIDATES, projectionResult.subtitleCandidates, title), client = getClient(instance, {
|
|
1363
1429
|
apiVersion: API_VERSION,
|
|
1364
|
-
|
|
1365
|
-
source: source && !isDatasetSource(source) ? source : void 0
|
|
1430
|
+
resource
|
|
1366
1431
|
});
|
|
1367
1432
|
return {
|
|
1368
1433
|
title: String(title || `${projectionResult._type}: ${projectionResult._id}`),
|
|
@@ -1405,9 +1470,8 @@ export {
|
|
|
1405
1470
|
addSubscription,
|
|
1406
1471
|
authStore,
|
|
1407
1472
|
authStore$1,
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
bindActionBySourceAndPerspective,
|
|
1473
|
+
bindActionByResource,
|
|
1474
|
+
bindActionByResourceAndPerspective,
|
|
1411
1475
|
bindActionGlobally,
|
|
1412
1476
|
cancelRequest,
|
|
1413
1477
|
clientStore$1 as clientStore,
|
|
@@ -1440,14 +1504,20 @@ export {
|
|
|
1440
1504
|
getUsersKey,
|
|
1441
1505
|
initializeRequest,
|
|
1442
1506
|
insecureRandomId,
|
|
1507
|
+
isCanvasResource,
|
|
1443
1508
|
isCanvasSource,
|
|
1509
|
+
isDatasetResource,
|
|
1444
1510
|
isDatasetSource,
|
|
1511
|
+
isDeepEqual,
|
|
1512
|
+
isMediaLibraryResource,
|
|
1445
1513
|
isMediaLibrarySource,
|
|
1446
1514
|
isProjectUserNotFoundClientError,
|
|
1447
1515
|
isReleasePerspective,
|
|
1448
1516
|
isStudioConfig,
|
|
1517
|
+
omitProperty,
|
|
1449
1518
|
parseQueryKey,
|
|
1450
1519
|
parseUsersKey,
|
|
1520
|
+
pickProperties,
|
|
1451
1521
|
removeSubscription,
|
|
1452
1522
|
resolveQuery,
|
|
1453
1523
|
setAuthToken,
|