@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.
Files changed (73) hide show
  1. package/dist/_chunks-dts/utils.d.ts +295 -69
  2. package/dist/_chunks-es/_internal.js +3 -14
  3. package/dist/_chunks-es/_internal.js.map +1 -1
  4. package/dist/_chunks-es/createGroqSearchFilter.js +129 -59
  5. package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -1
  6. package/dist/_chunks-es/version.js +1 -1
  7. package/dist/_exports/_internal.d.ts +16 -2
  8. package/dist/_exports/_internal.js +3 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +275 -149
  11. package/dist/index.js.map +1 -1
  12. package/package.json +11 -15
  13. package/src/_exports/_internal.ts +1 -0
  14. package/src/_exports/index.ts +33 -2
  15. package/src/agent/agentActions.ts +21 -25
  16. package/src/client/clientStore.test.ts +24 -60
  17. package/src/client/clientStore.ts +49 -56
  18. package/src/comlink/controller/actions/getOrCreateChannel.ts +2 -2
  19. package/src/comlink/node/actions/getOrCreateNode.test.ts +5 -2
  20. package/src/comlink/node/actions/getOrCreateNode.ts +2 -2
  21. package/src/comlink/node/actions/releaseNode.test.ts +3 -3
  22. package/src/config/sanityConfig.ts +72 -13
  23. package/src/document/applyDocumentActions.test.ts +7 -7
  24. package/src/document/applyDocumentActions.ts +5 -5
  25. package/src/document/documentStore.test.ts +68 -62
  26. package/src/document/documentStore.ts +33 -38
  27. package/src/document/processActions.ts +2 -2
  28. package/src/document/reducers.ts +4 -4
  29. package/src/document/sharedListener.ts +5 -7
  30. package/src/organization/organization.test-d.ts +102 -0
  31. package/src/organization/organization.test.ts +138 -0
  32. package/src/organization/organization.ts +166 -0
  33. package/src/organizations/organizations.test-d.ts +77 -0
  34. package/src/organizations/organizations.test.ts +150 -0
  35. package/src/organizations/organizations.ts +132 -0
  36. package/src/presence/bifurTransport.test.ts +46 -6
  37. package/src/presence/bifurTransport.ts +13 -1
  38. package/src/presence/presenceStore.test.ts +101 -5
  39. package/src/presence/presenceStore.ts +96 -24
  40. package/src/preview/getPreviewState.ts +1 -1
  41. package/src/preview/previewProjectionUtils.test.ts +4 -4
  42. package/src/preview/previewProjectionUtils.ts +6 -7
  43. package/src/preview/resolvePreview.ts +5 -1
  44. package/src/project/project.test-d.ts +93 -0
  45. package/src/project/project.test.ts +108 -10
  46. package/src/project/project.ts +152 -26
  47. package/src/projection/getProjectionState.ts +4 -4
  48. package/src/projection/projectionStore.test.ts +2 -2
  49. package/src/projection/resolveProjection.ts +2 -2
  50. package/src/projection/subscribeToStateAndFetchBatches.test.ts +1 -1
  51. package/src/projection/subscribeToStateAndFetchBatches.ts +11 -15
  52. package/src/projects/projects.test-d.ts +38 -0
  53. package/src/projects/projects.test.ts +104 -38
  54. package/src/projects/projects.ts +74 -14
  55. package/src/query/queryStore.test.ts +12 -12
  56. package/src/query/queryStore.ts +10 -11
  57. package/src/query/reducers.ts +3 -3
  58. package/src/releases/getPerspectiveState.ts +5 -5
  59. package/src/releases/releasesStore.test.ts +6 -6
  60. package/src/releases/releasesStore.ts +9 -9
  61. package/src/store/createActionBinder.test.ts +31 -31
  62. package/src/store/createActionBinder.ts +43 -38
  63. package/src/store/createSanityInstance.ts +5 -6
  64. package/src/telemetry/devMode.test.ts +8 -0
  65. package/src/telemetry/devMode.ts +10 -9
  66. package/src/telemetry/initTelemetry.test.ts +0 -17
  67. package/src/telemetry/initTelemetry.ts +2 -12
  68. package/src/users/reducers.ts +3 -4
  69. package/src/utils/createFetcherStore.ts +6 -4
  70. package/src/utils/isImportError.test.ts +72 -0
  71. package/src/utils/isImportError.ts +34 -0
  72. package/src/utils/object.test.ts +95 -0
  73. 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":";;AAOA,SAAS,WAAW,KAAsB;AACxC,QAAM,MAAM,IAAI,UAAU;AAC1B,SAAK,MAEH,IAAI,WAAW,kBAAkB,KACjC,IAAI,WAAW,mBAAmB,KAClC,IAAI,WAAW,kBAAkB,KACjC,IAAI,WAAW,mBAAmB,IALnB;AAOnB;AAkBO,SAAS,YAAqB;AACnC,SAAI,OAAO,SAAW,MACb,WAAW,MAAM,IAGnB,OAAO,UAAY,OAAe,QAAQ,KAAM,aAAgB;AACzE;ACrCO,MAAM,uBAAuB,YAKjC;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf,CAAC,GAGY,iBAAiB,YAE3B;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf,CAAC,GAGY,qBAAqB,YAG/B;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf,CAAC,GAGY,cAAc,YAGxB;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf,CAAC,GCpCK,gCAAgC,cAEhC,SAAS,aAAa,WAAW,GAUjC,oBAAoB,oBAAI,QAAA,GACxB,eAAe,oBAAI,QAAA,GACnB,mCAAmB,QAAA;AAclB,SAAS,cAAc,UAA0B,WAAyB;AAC/E,MAAI,CAAC,aAAa;AAChB,WAAO,MAAM,uCAAuC,EAAC,UAAU,IAAK;AACpE;AAAA,EACF;AACA,MAAI,CAAC,WAAW;AACd,WAAO,MAAM,uCAAuC,EAAC,UAAU,IAAK;AACpE;AAAA,EACF;AACI,oBAAkB,IAAI,QAAQ,KAAK,aAAa,IAAI,QAAQ,MAGhE,aAAa,IAAI,QAAQ,GAEzB,OAAO,MAAM,0BAA0B,EAAC,WAAU,GAElD,QAAQ,IAAI;AAAA,IACV,OAAO,uBAAoB;AAAA,IAC3B,OAAO,6BAAuB,EAAA,KAAA,SAAA,GAAA;AAAA,aAAA,EAAA;AAAA,IAAA,CAAA;AAAA,IAC9B,OAAO,6BAAmB,EAAA,KAAA,SAAA,GAAA;AAAA,aAAA,EAAA;AAAA,IAAA,CAAA;AAAA,EAAA,CAC3B,EACE,KAAK,OAAO,CAAC,EAAC,uBAAA,GAAyB,EAAC,UAAA,GAAY,EAAC,cAAA,CAAc,MAAM;AACxE,QAAI,SAAS,cAAc;AACzB,mBAAa,OAAO,QAAQ,GAC5B,OAAO,MAAM,8DAA8D;AAC3E;AAAA,IACF;AAQA,UAAM,QAAQ,cAAc,QAAQ,EAAE,WAAA;AAGtC,QAFA,OAAO,MAAM,oBAAoB,EAAC,cAAc,CAAC,CAAC,OAAO,UAAU,GAAA,CAAK,GAEpE,CAAC,UACH,OAAO,MAAM,wBAAwB,GAkBjC,CAjBa,MAAM,IAAI,QAAiB,CAAC,YAAY;AACvD,UAAI,SAAS,WAAA,EAAc,QAAO,QAAQ,EAAK;AAC/C,YAAM,UAAU,EAAC,aAAa,MAAM;AAAA,MAAC,KAC/B,QAAQ,SAAS,UAAU,MAAM;AACrC,gBAAQ,YAAA,GACR,QAAQ,EAAK;AAAA,MACf,CAAC,GACK,MAAM,cAAc,QAAQ,EAAE,WAAW,UAAU,CAAC,MAAM;AAC1D,cACF,OAAO,MAAM,qBAAqB,GAClC,IAAI,YAAA,GACJ,MAAA,GACA,QAAQ,EAAI;AAAA,MAEhB,CAAC;AACD,cAAQ,cAAc,MAAM,IAAI,YAAA;AAAA,IAClC,CAAC,KACgB,SAAS,WAAA,IAAc;AACtC,mBAAa,OAAO,QAAQ,GAC5B,OAAO,MAAM,2DAA2D;AACxE;AAAA,IACF;AAGF,UAAM,UAAU,uBAAuB;AAAA,MACrC,WAAW,SAAS;AAAA,MACpB,WAAW,MAAM,UAAU,UAAU,EAAC,YAAY,+BAA8B;AAAA,MAChF;AAAA,IAAA,CACD,GAEK,YAAY,MAAM,QAAQ,aAAA;AAEhC,QADA,OAAO,MAAM,0BAA0B,EAAC,WAAU,GAC9C,CAAC,aAAa,SAAS,cAAc;AACvC,mBAAa,OAAO,QAAQ,GAC5B,QAAQ,QAAA;AACR;AAAA,IACF;AAEA,iBAAa,OAAO,QAAQ,GAC5B,kBAAkB,IAAI,UAAU,OAAO;AAEvC,UAAM,WAAW,aAAa,IAAI,QAAQ;AAC1C,QAAI,UAAU;AACZ,aAAO,MAAM,2BAA2B,EAAC,OAAO,MAAM,KAAK,QAAQ,GAAE;AACrE,iBAAW,YAAY;AACrB,gBAAQ,iBAAiB,QAAQ;AAEnC,mBAAa,OAAO,QAAQ;AAAA,IAC9B;AAEA,UAAM,SAAS,SAAS,QAClB,cAAc,OAAO,OAAO,eAAgB,WAAW,OAAO,cAAc,aAC5E,aAAa,OAAO,MAAM,QAC5B,UACA,OAAO,QAAQ,MAAM,QACnB,WACA;AAEN,WAAO,KAAK,6BAA6B,EAAC,WAAW,aAAa,WAAA,CAAW,GAC7E,QAAQ,kBAAkB;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD,GAED,SAAS,UAAU,MAAM;AACvB,cAAQ,WAAA,GACR,kBAAkB,OAAO,QAAQ,GACjC,OAAO,MAAM,yBAAyB;AAAA,IACxC,CAAC;AAAA,EACH,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,iBAAa,OAAO,QAAQ,GAC5B,OAAO,KAAK,yBAAyB,EAAC,OAAO,KAAI;AAAA,EACnD,CAAC;AACL;AAQO,SAAS,oBAAoB,UAAwD;AAC1F,SAAO,kBAAkB,IAAI,QAAQ;AACvC;AASO,SAAS,iBAAiB,UAA0B,UAAwB;AACjF,MAAI,CAAC,YAAa;AAElB,QAAM,UAAU,YAAY,QAAQ;AACpC,MAAI,SAAS;AACX,WAAO,MAAM,yBAAyB,EAAC,UAAU,UAAU,IAAK,GAChE,QAAQ,iBAAiB,QAAQ;AACjC;AAAA,EACF;AAEA,QAAM,OAAuB;AAC7B,MAAI,QAAQ,aAAa,IAAI,IAAI;AAC5B,YACH,QAAQ,oBAAI,IAAA,GACZ,aAAa,IAAI,MAAM,KAAK,IAEzB,MAAM,IAAI,QAAQ,KACrB,OAAO,MAAM,2BAA2B,EAAC,UAAU,UAAU,IAAK,GAEpE,MAAM,IAAI,QAAQ;AACpB;AAEA,SAAS,YAAY,UAAwD;AAC3E,SAAO,kBAAkB,IAAI,QAAQ;AACvC;"}
@@ -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, from, timer, switchMap, takeWhile, firstValueFrom, fromEvent, EMPTY, catchError, NEVER, first, race, startWith, pairwise, mergeMap, groupBy, of, combineLatest, tap, share } from "rxjs";
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 "projectId" in source && "dataset" in source;
17
+ return isDatasetResource(source);
10
18
  }
11
19
  function isMediaLibrarySource(source) {
12
- return "mediaLibraryId" in source;
20
+ return isMediaLibraryResource(source);
13
21
  }
14
22
  function isCanvasSource(source) {
15
- return "canvasId" in source;
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 bindActionByDataset = createActionBinder((instance, options) => {
190
- const projectId = options?.projectId ?? instance.config.projectId, dataset = options?.dataset ?? instance.config.dataset;
191
- if (!projectId || !dataset)
192
- throw new Error("This API requires a project ID and dataset configured.");
193
- return { name: `${projectId}.${dataset}`, projectId, dataset };
194
- }), createSourceKey = (instance, source) => {
195
- let name, sourceForKey;
196
- if (source) {
197
- if (sourceForKey = source, isDatasetSource(source))
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 source: ${JSON.stringify(source)}`);
205
- return { name, source: sourceForKey };
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}`, source: { projectId, dataset } };
211
- }, bindActionBySource = createActionBinder((instance, { source }) => createSourceKey(instance, source)), bindActionBySourceAndPerspective = createActionBinder((instance, options) => {
212
- const { source, perspective } = options, utilizedPerspective = perspective ?? instance.config.perspective ?? "drafts";
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 = createSourceKey(instance, source);
285
+ const sourceKey = createResourceKey(instance, resource);
216
286
  return {
217
287
  name: `${sourceKey.name}:${perspectiveKey}`,
218
- source: sourceKey.source,
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
- "~experimental_resource": null,
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(pick(options, ...allowedKeys)), getClient = bindActionGlobally(
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.source && (isDatasetSource(options.source) ? resource = { type: "dataset", id: `${options.source.projectId}.${options.source.dataset}` } : isMediaLibrarySource(options.source) ? resource = { type: "media-library", id: options.source.mediaLibraryId } : isCanvasSource(options.source) && (resource = { type: "canvas", id: options.source.canvasId }));
975
- const projectId = options.projectId ?? instance.config.projectId, dataset = options.dataset ?? instance.config.dataset, apiHost = options.apiHost ?? instance.config.auth?.apiHost ?? getStagingApiHost(), effectiveOptions = {
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
- ...apiHost && { apiHost },
983
- ...resource && { "~experimental_resource": resource }
1051
+ ...resource ? { resource } : { resource: void 0 },
1052
+ ...apiHost && { apiHost }
984
1053
  };
985
- resource && ((options.projectId || options.dataset) && console.warn(
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: omit(prev.users, key) };
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: omit(prev.users, key) };
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 = bindActionBySource(
1135
+ }, _getActiveReleasesState = bindActionByResource(
1069
1136
  releasesStore,
1070
1137
  createStateSourceAction({
1071
1138
  selector: ({ state }, _) => state.activeReleases
1072
1139
  })
1073
1140
  ), getActiveReleasesState = (instance, options) => (
1074
- // bindActionBySource keyFn destructures { source } from the first param, so pass {} when no options
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: { source }
1146
+ key: { resource }
1080
1147
  }) => {
1081
1148
  const { observable: releases$ } = getQueryState(instance, {
1082
1149
  query: RELEASES_QUERY,
1083
1150
  perspective: "raw",
1084
- source: source && !isDatasetSource(source) ? source : void 0,
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 = bindActionBySource(
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: omit(prev.queries, key) };
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: omit(prev.queries, key) };
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
- source,
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
- source
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: { source }
1298
+ key: { resource }
1232
1299
  }) => {
1233
1300
  const liveMessages$ = getClientState(instance, {
1234
1301
  apiVersion: QUERY_STORE_API_VERSION,
1235
- // temporary guard here until we're ready for everything to be queried via global api
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 = bindActionBySource(
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 = bindActionBySource(
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, source) {
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
- // TODO: remove in v3 when we're ready for everything to be queried via source
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
- bindActionByDataset,
1409
- bindActionBySource,
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,