@sanity/sdk 2.8.0 → 2.9.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 +2396 -0
- package/dist/_chunks-es/_internal.js +129 -0
- package/dist/_chunks-es/_internal.js.map +1 -0
- package/dist/_chunks-es/createGroqSearchFilter.js +1460 -0
- package/dist/_chunks-es/createGroqSearchFilter.js.map +1 -0
- package/dist/_chunks-es/telemetryManager.js +87 -0
- package/dist/_chunks-es/telemetryManager.js.map +1 -0
- package/dist/_chunks-es/version.js +7 -0
- package/dist/_chunks-es/version.js.map +1 -0
- package/dist/_exports/_internal.d.ts +64 -0
- package/dist/_exports/_internal.js +20 -0
- package/dist/_exports/_internal.js.map +1 -0
- package/dist/index.d.ts +2 -2343
- package/dist/index.js +383 -1777
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
- package/src/_exports/_internal.ts +14 -0
- package/src/_exports/index.ts +10 -1
- package/src/auth/authStore.test.ts +150 -1
- package/src/auth/authStore.ts +11 -11
- package/src/auth/dashboardAuth.ts +2 -2
- package/src/auth/handleAuthCallback.ts +9 -3
- package/src/auth/logout.test.ts +1 -1
- package/src/auth/logout.ts +1 -1
- package/src/auth/refreshStampedToken.test.ts +118 -1
- package/src/auth/refreshStampedToken.ts +3 -2
- package/src/auth/standaloneAuth.ts +9 -3
- package/src/auth/studioAuth.ts +34 -7
- package/src/auth/studioModeAuth.ts +2 -1
- package/src/auth/subscribeToStateAndFetchCurrentUser.test.ts +10 -2
- package/src/auth/subscribeToStateAndFetchCurrentUser.ts +5 -1
- package/src/auth/subscribeToStorageEventsAndSetToken.ts +2 -2
- package/src/auth/utils.ts +33 -0
- package/src/client/clientStore.test.ts +14 -0
- package/src/client/clientStore.ts +2 -1
- package/src/comlink/node/getNodeState.ts +2 -1
- package/src/config/sanityConfig.ts +6 -0
- package/src/document/actions.ts +18 -11
- package/src/document/applyDocumentActions.test.ts +7 -6
- package/src/document/applyDocumentActions.ts +10 -4
- package/src/document/documentStore.test.ts +536 -188
- package/src/document/documentStore.ts +142 -76
- package/src/document/events.ts +7 -2
- package/src/document/permissions.test.ts +18 -16
- package/src/document/permissions.ts +35 -11
- package/src/document/processActions.test.ts +359 -32
- package/src/document/processActions.ts +104 -76
- package/src/document/reducers.test.ts +117 -29
- package/src/document/reducers.ts +43 -36
- package/src/document/sharedListener.ts +16 -6
- package/src/document/util.ts +14 -0
- package/src/favorites/favorites.test.ts +9 -2
- package/src/presence/bifurTransport.ts +6 -1
- package/src/preview/getPreviewState.test.ts +115 -98
- package/src/preview/getPreviewState.ts +38 -60
- package/src/preview/previewProjectionUtils.test.ts +179 -0
- package/src/preview/previewProjectionUtils.ts +93 -0
- package/src/preview/resolvePreview.test.ts +42 -25
- package/src/preview/resolvePreview.ts +29 -10
- package/src/preview/{previewStore.ts → types.ts} +8 -17
- package/src/projection/getProjectionState.test.ts +16 -16
- package/src/projection/getProjectionState.ts +2 -1
- package/src/projection/projectionQuery.ts +2 -3
- package/src/projection/types.ts +1 -1
- package/src/query/queryStore.ts +2 -1
- package/src/releases/getPerspectiveState.ts +7 -6
- package/src/releases/releasesStore.test.ts +20 -5
- package/src/releases/releasesStore.ts +20 -8
- package/src/store/createStateSourceAction.test.ts +62 -0
- package/src/store/createStateSourceAction.ts +34 -39
- package/src/telemetry/__telemetry__/sdk.telemetry.ts +42 -0
- package/src/telemetry/devMode.test.ts +52 -0
- package/src/telemetry/devMode.ts +40 -0
- package/src/telemetry/initTelemetry.test.ts +225 -0
- package/src/telemetry/initTelemetry.ts +205 -0
- package/src/telemetry/telemetryManager.test.ts +263 -0
- package/src/telemetry/telemetryManager.ts +187 -0
- package/src/users/usersStore.test.ts +1 -0
- package/src/users/usersStore.ts +5 -1
- package/src/utils/createFetcherStore.test.ts +6 -4
- package/src/utils/createFetcherStore.ts +2 -1
- package/src/utils/getStagingApiHost.test.ts +21 -0
- package/src/utils/getStagingApiHost.ts +14 -0
- package/src/utils/ids.test.ts +1 -29
- package/src/utils/ids.ts +0 -10
- package/src/utils/setCleanupTimeout.ts +24 -0
- package/src/preview/previewQuery.test.ts +0 -236
- package/src/preview/previewQuery.ts +0 -153
- package/src/preview/previewStore.test.ts +0 -36
- package/src/preview/subscribeToStateAndFetchBatches.test.ts +0 -221
- package/src/preview/subscribeToStateAndFetchBatches.ts +0 -112
- package/src/preview/util.ts +0 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGroqSearchFilter.js","sources":["../../src/config/sanityConfig.ts","../../src/releases/utils/isReleasePerspective.ts","../../src/utils/ids.ts","../../src/utils/logger.ts","../../src/utils/getEnv.ts","../../src/store/createStoreState.ts","../../src/store/defineStore.ts","../../src/store/createStoreInstance.ts","../../src/store/createActionBinder.ts","../../src/store/createStateSourceAction.ts","../../src/utils/getStagingApiHost.ts","../../src/auth/authConstants.ts","../../src/auth/authMode.ts","../../src/auth/authStateType.ts","../../src/auth/refreshStampedToken.ts","../../src/auth/subscribeToStateAndFetchCurrentUser.ts","../../src/auth/utils.ts","../../src/auth/dashboardAuth.ts","../../src/auth/subscribeToStorageEventsAndSetToken.ts","../../src/auth/standaloneAuth.ts","../../src/auth/studioModeAuth.ts","../../src/auth/studioAuth.ts","../../src/auth/authStore.ts","../../src/client/clientStore.ts","../../src/utils/setCleanupTimeout.ts","../../src/users/usersConstants.ts","../../src/users/reducers.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","../../src/preview/previewConstants.ts","../../src/preview/previewProjectionUtils.ts","../../src/utils/createGroqSearchFilter.ts"],"sourcesContent":["import {type ClientPerspective, type StackablePerspective} from '@sanity/client'\n\nimport {type AuthConfig} from './authConfig'\n\n/**\n * A minimal Observable-compatible interface for subscribing to token changes.\n * Any object with a `subscribe` method that follows this contract will work,\n * including RxJS Observables. This avoids coupling the SDK to a specific\n * reactive library.\n *\n * @public\n */\nexport interface TokenSource {\n /** Subscribe to token emissions. Emits `null` when logged out. */\n subscribe(observer: {next: (token: string | null) => void}): {unsubscribe(): void}\n}\n\n/**\n * Studio-specific configuration for the SDK.\n * When present, the SDK operates in studio mode and derives auth from the\n * provided token source instead of discovering tokens independently.\n *\n * @public\n */\nexport interface StudioConfig {\n /**\n * Whether the Studio has already determined the user is authenticated.\n * When `true` and the token source emits `null`, the SDK infers\n * cookie-based auth is in use rather than transitioning to logged-out.\n */\n authenticated?: boolean\n /** Reactive auth token source from the Studio's auth store. */\n auth?: {\n /**\n * A reactive token source. The SDK subscribes and stays in sync — the\n * Studio is the single authority for auth and handles token refresh.\n *\n * Optional because older Studios may not expose it. When absent, the\n * SDK falls back to localStorage/cookie discovery.\n */\n token?: TokenSource\n }\n}\n\n/**\n * Represents the minimal configuration required to identify a Sanity project.\n * @public\n */\nexport interface ProjectHandle<TProjectId extends string = string> {\n projectId?: TProjectId\n}\n\n/**\n * @public\n */\nexport type ReleasePerspective = {\n releaseName: string\n excludedPerspectives?: StackablePerspective[]\n}\n\n/**\n * @public\n */\nexport interface PerspectiveHandle {\n perspective?: ClientPerspective | ReleasePerspective\n}\n\n/**\n * @public\n */\nexport interface DatasetHandle<TDataset extends string = string, TProjectId extends string = string>\n extends ProjectHandle<TProjectId>, PerspectiveHandle {\n dataset?: TDataset\n /**\n * @beta\n * Explicit source object to use for this operation.\n */\n source?: DocumentSource\n}\n\n/**\n * Identifies a specific document type within a Sanity dataset and project.\n * Includes `projectId`, `dataset`, and `documentType`.\n * Optionally includes a `documentId` and `liveEdit` flag.\n * @public\n */\nexport interface DocumentTypeHandle<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DatasetHandle<TDataset, TProjectId> {\n documentId?: string\n documentType: TDocumentType\n /**\n * Indicates whether this document uses liveEdit mode.\n * When `true`, the document does not use the draft/published model and edits are applied directly to the document.\n * @see https://www.sanity.io/docs/content-lake/drafts#ca0663a8f002\n */\n liveEdit?: boolean\n}\n\n/**\n * Uniquely identifies a specific document within a Sanity dataset and project.\n * Includes `projectId`, `dataset`, `documentType`, and the required `documentId`.\n * Commonly used by document-related hooks and components to reference a document without fetching its full content initially.\n * @public\n */\nexport interface DocumentHandle<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentTypeHandle<TDocumentType, TDataset, TProjectId> {\n documentId: string\n}\n\n/**\n * Represents the complete configuration for a Sanity SDK instance\n * @public\n */\nexport interface SanityConfig extends DatasetHandle, PerspectiveHandle {\n /**\n * Authentication configuration for the instance\n * @remarks Merged with parent configurations when using createChild\n */\n auth?: AuthConfig\n /**\n * Studio configuration provided by a Sanity Studio workspace.\n * When present, the SDK operates in studio mode and derives auth from the\n * workspace's reactive token source — no manual configuration needed.\n *\n * @remarks Typically set automatically by `SanityApp` when it detects an\n * `SDKStudioContext` provider. Can also be set explicitly for programmatic use.\n */\n studio?: StudioConfig\n\n /**\n * Studio mode configuration for use of the SDK in a Sanity Studio.\n * @remarks Controls whether studio mode features are enabled.\n * @deprecated Use `studio` instead, which provides richer integration\n * with the Studio's workspace (auth token sync, etc.).\n */\n studioMode?: {\n enabled: boolean\n }\n\n /**\n * @beta\n * A list of named sources to use for this instance.\n */\n sources?: Record<string, DocumentSource>\n}\n\n/**\n * A document source can be used for querying.\n * This will soon be the default way to identify where you are querying from.\n *\n * @beta\n */\nexport type DocumentSource = DatasetSource | MediaLibrarySource | CanvasSource\n\n/**\n * @beta\n */\nexport type DatasetSource = {projectId: string; dataset: string}\n\n/**\n * @beta\n */\nexport type MediaLibrarySource = {mediaLibraryId: string}\n\n/**\n * @beta\n */\nexport type CanvasSource = {canvasId: string}\n\n/**\n * @beta\n */\nexport function isDatasetSource(source: DocumentSource): source is DatasetSource {\n return 'projectId' in source && 'dataset' in source\n}\n\n/**\n * @beta\n */\nexport function isMediaLibrarySource(source: DocumentSource): source is MediaLibrarySource {\n return 'mediaLibraryId' in source\n}\n\n/**\n * @beta\n */\nexport function isCanvasSource(source: DocumentSource): source is CanvasSource {\n return 'canvasId' in source\n}\n","import {type PerspectiveHandle, type ReleasePerspective} from '../../config/sanityConfig'\n\nexport const isReleasePerspective = (\n perspective: PerspectiveHandle['perspective'],\n): perspective is ReleasePerspective => {\n return typeof perspective === 'object' && perspective !== null && 'releaseName' in perspective\n}\n","export function insecureRandomId(): string {\n return Array.from({length: 16}, () => Math.floor(Math.random() * 16).toString(16)).join('')\n}\n","/**\n * Logging infrastructure for the Sanity SDK\n *\n * Provides multi-level, namespace-based logging for both SDK users and maintainers.\n * In production builds, all logging can be stripped via tree-shaking.\n *\n * @example SDK User\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * configureLogging({\n * level: 'info',\n * namespaces: ['auth', 'document']\n * })\n * ```\n *\n * @example SDK Maintainer\n * ```ts\n * configureLogging({\n * level: 'trace',\n * namespaces: ['*'],\n * internal: true\n * })\n * ```\n */\n\n/**\n * Log levels in order of verbosity (least to most)\n * - error: Critical failures that prevent operation\n * - warn: Issues that may cause problems but don't stop execution\n * - info: High-level informational messages (SDK user level)\n * - debug: Detailed debugging information (maintainer level)\n * - trace: Very detailed tracing (maintainer level, includes RxJS streams)\n * @public\n */\nexport type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace'\n\n/**\n * Log namespaces organize logs by functional domain\n *\n * @remarks\n * This is an extensible string type. As logging is added to more modules,\n * additional namespaces will be recognized. Currently implemented namespaces\n * will be documented as they are added.\n * @internal\n */\nexport type LogNamespace = string\n\n/**\n * Configuration for the logging system\n * @public\n */\nexport interface LoggerConfig {\n /**\n * Minimum log level to output\n * @defaultValue 'warn'\n */\n level?: LogLevel\n\n /**\n * Namespaces to enable. Use ['*'] for all namespaces\n * @defaultValue []\n * @remarks\n * Available namespaces depend on which modules have logging integrated.\n * Check the SDK documentation for the current list of instrumented modules.\n * @example ['auth', 'document']\n */\n namespaces?: string[]\n\n /**\n * Enable internal/maintainer-level logging\n * Shows RxJS streams, store internals, etc.\n * @defaultValue false\n */\n internal?: boolean\n\n /**\n * Custom log handler (for testing or custom output)\n * @defaultValue console methods\n */\n handler?: LogHandler\n\n /**\n * Enable timestamps in log output\n * @defaultValue true\n */\n timestamps?: boolean\n\n /**\n * Enable in production builds\n * @defaultValue false\n */\n enableInProduction?: boolean\n}\n\n/**\n * Custom log handler interface\n *\n * @internal\n */\nexport interface LogHandler {\n error: (message: string, context?: LogContext) => void\n warn: (message: string, context?: LogContext) => void\n info: (message: string, context?: LogContext) => void\n debug: (message: string, context?: LogContext) => void\n trace: (message: string, context?: LogContext) => void\n}\n\n/**\n * Context object attached to log messages\n *\n * This interface allows you to attach arbitrary contextual data to log messages.\n * The index signature `[key: string]: unknown` enables you to add any custom\n * properties relevant to your log entry (e.g., `userId`, `documentId`, `action`, etc.).\n *\n * **Sensitive data sanitization:**\n * Top-level keys containing sensitive names (`token`, `password`, `secret`, `apiKey`,\n * `authorization`) are automatically redacted to `[REDACTED]` in log output.\n *\n * @example\n * ```ts\n * logger.info('User logged in', {\n * userId: '123', // Custom context\n * action: 'login', // Custom context\n * token: 'secret' // Will be redacted to [REDACTED]\n * })\n * ```\n *\n * @internal\n */\nexport interface LogContext {\n /**\n * Custom context properties that provide additional information about the log entry.\n * Any key-value pairs can be added here (e.g., userId, documentId, requestId, etc.).\n * Keys with sensitive names (token, password, secret, apiKey, authorization) are\n * automatically sanitized.\n */\n [key: string]: unknown\n /** Error object if logging an error */\n error?: Error | unknown\n /** Duration in milliseconds for timed operations */\n duration?: number\n /** Stack trace for debugging */\n stack?: string\n /** Instance context (automatically added when available) */\n instanceContext?: InstanceContext\n}\n\n/**\n * Instance context information automatically added to logs\n * @internal\n */\nexport interface InstanceContext {\n /** Unique instance ID */\n instanceId?: string\n /** Project ID */\n projectId?: string\n /** Dataset name */\n dataset?: string\n}\n\n/**\n * Logger instance for a specific namespace\n * @internal\n */\nexport interface Logger {\n readonly namespace: string\n error: (message: string, context?: LogContext) => void\n warn: (message: string, context?: LogContext) => void\n info: (message: string, context?: LogContext) => void\n debug: (message: string, context?: LogContext) => void\n trace: (message: string, context?: LogContext) => void\n /** Check if a log level is enabled (for performance-sensitive code) */\n isLevelEnabled: (level: LogLevel) => boolean\n /** Create a child logger with extended context */\n child: (context: LogContext) => Logger\n /** Get the instance context if available */\n getInstanceContext: () => InstanceContext | undefined\n}\n\n// Log level priority (lower number = higher priority)\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n trace: 4,\n}\n\n// Default logging configuration\nconst DEFAULT_CONFIG: Required<LoggerConfig> = {\n level: 'warn',\n namespaces: [],\n internal: false,\n timestamps: true,\n enableInProduction: false,\n handler: {\n // eslint-disable-next-line no-console\n error: console.error.bind(console),\n // eslint-disable-next-line no-console\n warn: console.warn.bind(console),\n // eslint-disable-next-line no-console\n info: console.info.bind(console),\n // eslint-disable-next-line no-console\n debug: console.debug.bind(console),\n // eslint-disable-next-line no-console\n trace: console.debug.bind(console), // trace uses console.debug\n },\n}\n\n/**\n * Parse DEBUG environment variable for automatic logging configuration\n *\n * Supports patterns similar to Sanity CLI/Studio:\n * - DEBUG=sanity:* (all namespaces, debug level)\n * - DEBUG=sanity:auth,sanity:document (specific namespaces)\n * - DEBUG=sanity:trace:* (all namespaces, trace level)\n * - DEBUG=sanity:*:internal (enable internal logs)\n *\n * @internal\n */\nexport function parseDebugEnvVar(): Partial<LoggerConfig> | null {\n if (typeof process === 'undefined' || !process.env?.['DEBUG']) {\n return null\n }\n\n const debug = process.env['DEBUG']\n\n // Only process if it includes 'sanity'\n if (!debug.includes('sanity')) {\n return null\n }\n\n const config: Partial<LoggerConfig> = {}\n\n // Parse level from pattern like \"sanity:trace:*\" or \"sanity:debug:*\"\n const levelMatch = debug.match(/sanity:(trace|debug|info|warn|error):/)\n const hasLevelSpecifier = !!levelMatch\n if (levelMatch) {\n config.level = levelMatch[1] as LogLevel\n } else {\n // Default to debug level if just \"sanity:*\" or \"sanity:namespace\"\n config.level = 'debug'\n }\n\n // Parse namespaces\n if (debug === 'sanity') {\n config.namespaces = ['*']\n } else if (hasLevelSpecifier && debug.match(/sanity:(trace|debug|info|warn|error):\\*/)) {\n // Pattern like \"sanity:trace:*\" - wildcard after level\n config.namespaces = ['*']\n } else if (!hasLevelSpecifier && debug.includes('sanity:*')) {\n // Pattern like \"sanity:*\" - wildcard without level\n config.namespaces = ['*']\n } else {\n // Extract specific namespaces like \"sanity:auth,sanity:document\"\n const namespaces = debug\n .split(',')\n .filter((s) => s.includes('sanity:'))\n .map((s) => {\n // Remove 'sanity:' prefix\n const cleaned = s.replace(/^sanity:/, '')\n // If there's a level specifier, skip it\n if (hasLevelSpecifier && cleaned.match(/^(trace|debug|info|warn|error):/)) {\n // Get the part after the level: \"trace:auth\" -> \"auth\"\n return cleaned.split(':').slice(1).join(':')\n }\n // Otherwise get the first part: \"auth:something\" -> \"auth\"\n return cleaned.split(':')[0]\n })\n .filter(Boolean)\n .filter((ns) => ns !== '*') // Filter out wildcards\n\n if (namespaces.length > 0) {\n config.namespaces = namespaces\n }\n }\n\n // Check for internal flag: DEBUG=sanity:*:internal\n if (debug.includes(':internal')) {\n config.internal = true\n }\n\n return config\n}\n\n// Global configuration - initialized with env var settings if present\nconst envConfig = parseDebugEnvVar()\nlet globalConfig: Required<LoggerConfig> = {\n ...DEFAULT_CONFIG,\n ...(envConfig ?? {}),\n}\n\n// Log that env var configuration was detected (only if DEBUG is set)\n// Note: This runs at module initialization, difficult to test without complex module mocking\n/* c8 ignore next 14 */\nif (envConfig) {\n const shouldLog =\n ['info', 'debug', 'trace'].includes(globalConfig.level) || globalConfig.level === 'warn'\n if (shouldLog) {\n // eslint-disable-next-line no-console\n console.info(\n `[${new Date().toISOString()}] [INFO] [sdk] Logging auto-configured from DEBUG environment variable`,\n {\n level: globalConfig.level,\n namespaces: globalConfig.namespaces,\n internal: globalConfig.internal,\n source: 'env:DEBUG',\n value: typeof process !== 'undefined' ? process.env?.['DEBUG'] : undefined,\n },\n )\n }\n}\n\n/**\n * Configure the global logging system\n * @public\n */\nexport function configureLogging(config: LoggerConfig): void {\n globalConfig = {\n ...globalConfig,\n ...config,\n handler: config.handler ?? globalConfig.handler,\n }\n}\n\n/**\n * Get the current logging configuration\n * @internal\n */\nexport function getLoggingConfig(): Readonly<Required<LoggerConfig>> {\n return globalConfig\n}\n\n/**\n * Reset logging to default configuration\n * @internal\n */\nexport function resetLogging(): void {\n globalConfig = {...DEFAULT_CONFIG}\n}\n\n/**\n * Check if logging is enabled in the current environment\n * @internal\n */\nfunction isLoggingEnabled(): boolean {\n // In production, only log if explicitly enabled\n if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'production') {\n return globalConfig.enableInProduction\n }\n return true\n}\n\n/**\n * Check if a namespace is enabled\n * @internal\n */\nfunction isNamespaceEnabled(namespace: string): boolean {\n if (!isLoggingEnabled()) return false\n if (globalConfig.namespaces.includes('*')) return true\n return globalConfig.namespaces.includes(namespace)\n}\n\n/**\n * Check if a log level is enabled\n * @internal\n */\nfunction isLevelEnabled(level: LogLevel): boolean {\n if (!isLoggingEnabled()) return false\n return LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[globalConfig.level]\n}\n\n/**\n * Format a log message with timestamp, namespace, and instance context\n * @internal\n */\nfunction formatMessage(\n namespace: LogNamespace,\n level: LogLevel,\n message: string,\n context?: LogContext,\n): [string, LogContext | undefined] {\n const parts: string[] = []\n\n if (globalConfig.timestamps) {\n const timestamp = new Date().toISOString()\n parts.push(`[${timestamp}]`)\n }\n\n parts.push(`[${level.toUpperCase()}]`)\n parts.push(`[${namespace}]`)\n\n // Add instance context if available\n const instanceContext = context?.instanceContext\n if (instanceContext) {\n if (instanceContext.projectId) {\n parts.push(`[project:${instanceContext.projectId}]`)\n }\n if (instanceContext.dataset) {\n parts.push(`[dataset:${instanceContext.dataset}]`)\n }\n if (instanceContext.instanceId) {\n parts.push(`[instance:${instanceContext.instanceId.slice(0, 8)}]`)\n }\n }\n\n parts.push(message)\n\n return [parts.join(' '), context]\n}\n\n/**\n * Sanitize context for logging (remove sensitive data)\n * @internal\n * @remarks\n * This performs shallow sanitization only - it redacts top-level keys that contain\n * sensitive names (token, password, secret, apiKey, authorization).\n * Nested sensitive data (e.g., `\\{auth: \\{token: 'secret'\\}\\}`) will NOT be redacted.\n * If deep sanitization is needed in the future, this can be enhanced to recursively\n * traverse nested objects.\n */\nfunction sanitizeContext(context?: LogContext): LogContext | undefined {\n if (!context || Object.keys(context).length === 0) return undefined\n\n const sanitized = {...context}\n\n // Remove or redact sensitive fields at the top level\n const sensitiveKeys = ['token', 'password', 'secret', 'apiKey', 'authorization']\n for (const key of Object.keys(sanitized)) {\n if (sensitiveKeys.some((sensitive) => key.toLowerCase().includes(sensitive))) {\n sanitized[key] = '[REDACTED]'\n }\n }\n\n return sanitized\n}\n\n/**\n * Create a logger for a specific namespace\n * @param namespace - A string identifier for the logging domain (e.g., 'auth', 'document')\n * @param baseContext - Optional base context to include in all log messages\n * @remarks\n * If baseContext includes an `instanceContext` property (with projectId, dataset, instanceId),\n * it will be automatically formatted into the log output for easier debugging of\n * multi-instance scenarios.\n * @public\n */\nexport function createLogger(namespace: string, baseContext?: LogContext): Logger {\n const logAtLevel = (level: LogLevel, message: string, context?: LogContext) => {\n // Early return if namespace or level not enabled\n if (!isNamespaceEnabled(namespace)) return\n if (!isLevelEnabled(level)) return\n\n // Skip internal logs if not enabled\n if (context?.['internal'] && !globalConfig.internal) return\n\n const mergedContext = {...baseContext, ...context}\n const sanitized = sanitizeContext(mergedContext)\n const [formatted, finalContext] = formatMessage(namespace, level, message, sanitized)\n\n globalConfig.handler[level](formatted, finalContext)\n }\n\n const logger: Logger = {\n namespace,\n error: (message, context) => logAtLevel('error', message, context),\n warn: (message, context) => logAtLevel('warn', message, context),\n info: (message, context) => logAtLevel('info', message, context),\n debug: (message, context) => logAtLevel('debug', message, context),\n trace: (message, context) => logAtLevel('trace', message, {...context, internal: true}),\n isLevelEnabled: (level) => isNamespaceEnabled(namespace) && isLevelEnabled(level),\n child: (childContext) => createLogger(namespace, {...baseContext, ...childContext}),\n getInstanceContext: () => baseContext?.instanceContext,\n }\n\n return logger\n}\n\n/**\n * Create a performance timer for measuring operation duration\n * @internal\n */\nexport function createTimer(\n namespace: string,\n operation: string,\n): {end: (message?: string, context?: LogContext) => number} {\n const logger = createLogger(namespace)\n const start = performance.now()\n\n return {\n end: (message?: string, context?: LogContext): number => {\n const duration = performance.now() - start\n logger.debug(message ?? `${operation} completed`, {\n ...context,\n operation,\n duration,\n })\n return duration\n },\n }\n}\n\n/**\n * Utility to log RxJS operator execution (for maintainers)\n * Will be exported in future PR when logging is added to stores\n * @internal\n */\n/* c8 ignore next 4 */\nfunction logRxJSOperator(namespace: string, operator: string, context?: LogContext): void {\n const logger = createLogger(namespace)\n logger.trace(`RxJS: ${operator}`, {...context, internal: true, operator})\n}\n\n/**\n * Extract instance context from a SanityInstance for logging\n * Will be exported in future PR when logging is added to stores\n * @param instance - The SanityInstance to extract context from\n * @returns Instance context suitable for logging\n * @internal\n */\n/* c8 ignore next 7 */\nfunction getInstanceContext(instance: {\n instanceId?: string\n config?: {projectId?: string; dataset?: string}\n}): InstanceContext {\n return {\n instanceId: instance.instanceId,\n projectId: instance.config?.projectId,\n dataset: instance.config?.dataset,\n }\n}\n\n// Prevent unused function warnings - these will be exported and used in future PRs\n/* c8 ignore next 2 */\nvoid logRxJSOperator\nvoid getInstanceContext\n","// Local type declaration for Remix\ntype WindowWithEnv = Window &\n typeof globalThis & {\n ENV?: Record<string, unknown>\n }\n\ntype KnownEnvVar = 'DEV' | 'PKG_VERSION'\n\nexport function getEnv(key: KnownEnvVar): unknown {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n // Vite environment variables\n return (import.meta.env as unknown as Record<string, unknown>)[key]\n } else if (typeof process !== 'undefined' && process.env) {\n // Node.js or server-side environment variables\n return process.env[key]\n } else if (typeof window !== 'undefined' && (window as WindowWithEnv).ENV) {\n // Remix-style client-side environment variables\n return (window as WindowWithEnv).ENV?.[key]\n }\n return undefined\n}\n","import {Observable} from 'rxjs'\nimport {devtools, type DevtoolsOptions} from 'zustand/middleware'\nimport {createStore} from 'zustand/vanilla'\n\n/**\n * Represents a reactive store state container with multiple access patterns\n */\nexport interface StoreState<TState> {\n /**\n * Gets the current state value\n *\n * @remarks\n * This is a direct synchronous accessor that doesn't trigger subscriptions\n */\n get: () => TState\n\n /**\n * Updates the store state\n * @param name - Action name for devtools tracking\n * @param updatedState - New state value or updater function\n *\n * @remarks\n * When providing a partial object, previous top-level keys not included in\n * the update will be preserved.\n */\n set: (name: string, updatedState: Partial<TState> | ((s: TState) => Partial<TState>)) => void\n\n /**\n * Observable stream of state changes\n * @remarks\n * - Emits immediately with current state on subscription\n * - Shares underlying subscription between observers\n * - Only emits when state reference changes\n * - Completes when store is disposed\n */\n observable: Observable<TState>\n}\n\n/**\n * Creates a reactive store state container with multiple access patterns\n * @param initialState - Initial state value for the store\n * @param devToolsOptions - Configuration for Zustand devtools integration\n * @returns StoreState instance with get/set/observable interface\n *\n * @example\n * ```typescript\n * // Create a simple counter store\n * const counterStore = createStoreState({ count: 0 });\n *\n * // Update state\n * counterStore.set('increment', { count: 1 });\n *\n * // Observe changes\n * counterStore.observable.subscribe(console.log);\n * ```\n *\n * @remarks\n * Uses Zustand for state management under the hood with RxJS for observable interface.\n * Designed to work with both imperative and reactive programming patterns.\n */\nexport function createStoreState<TState>(\n initialState: TState,\n devToolsOptions?: DevtoolsOptions,\n): StoreState<TState> {\n // Create underlying Zustand store with devtools integration\n const store = createStore<TState>()(devtools(() => initialState, devToolsOptions))\n\n return {\n get: store.getState,\n set: (actionKey, updatedState) => {\n const currentState = store.getState()\n const nextState =\n typeof updatedState === 'function' ? updatedState(currentState) : updatedState\n\n // Optimization: Skip update if state reference remains the same\n if (currentState !== nextState) {\n store.setState(nextState, false, actionKey)\n }\n },\n observable: new Observable((observer) => {\n // Emit current state immediately on subscription\n const emit = () => observer.next(store.getState())\n emit()\n\n // Subscribe to Zustand store changes\n const unsubscribe = store.subscribe(emit)\n\n // Cleanup when observable unsubscribed\n return () => unsubscribe()\n }),\n }\n}\n","import {type SanityInstance} from './createSanityInstance'\nimport {type StoreState} from './createStoreState'\n\n/**\n * Context object provided to store initialization functions\n */\nexport interface StoreContext<TState, TKey = unknown> {\n /**\n * Sanity instance associated with this store\n *\n * @remarks\n * Provides access to the Sanity configuration and instance lifecycle methods\n */\n instance: SanityInstance\n\n /**\n * Reactive store state management utilities\n *\n * @remarks\n * Contains methods for getting/setting state and observing changes\n */\n state: StoreState<TState>\n\n /**\n * The key used to instantiate the store.\n */\n key: TKey\n}\n\n/**\n * Defines the structure and behavior of a store\n *\n * @remarks\n * Stores are isolated state containers that can be associated with Sanity instances.\n * Each store definition creates a separate state instance per composite key.\n */\nexport interface StoreDefinition<TState, TKey = unknown> {\n /**\n * Unique name for the store\n *\n * @remarks\n * Used for debugging, devtools integration, and store identification\n */\n name: string\n\n /**\n * Creates the initial state for the store\n * @param instance - Sanity instance the store is being created for\n * @returns Initial state value\n *\n * @remarks\n * Called when a new store instance is created. Can use Sanity instance\n * configuration to determine initial state.\n */\n getInitialState: (instance: SanityInstance, key: TKey) => TState\n\n /**\n * Optional initialization function\n * @param context - Store context with state and instance access\n * @returns Optional cleanup function for store disposal\n *\n * @remarks\n * Use this for:\n * - Setting up event listeners\n * - Initial data fetching\n * - Connecting external services\n *\n * Return a cleanup function to:\n * - Remove event listeners\n * - Cancel pending operations\n * - Dispose external connections\n */\n initialize?: (context: StoreContext<TState, TKey>) => (() => void) | undefined\n}\n\n/**\n * Typescript helper function for creating store definitions\n *\n * @param storeDefinition - Configuration object defining the store\n * @returns The finalized store definition\n */\nexport function defineStore<TState, TKey = unknown>(\n storeDefinition: StoreDefinition<TState, TKey>,\n): StoreDefinition<TState, TKey> {\n return storeDefinition\n}\n","import {getEnv} from '../utils/getEnv'\nimport {type SanityInstance} from './createSanityInstance'\nimport {createStoreState, type StoreState} from './createStoreState'\nimport {type StoreDefinition} from './defineStore'\n\n/**\n * Represents a running instance of a store with its own state and lifecycle\n *\n * @remarks\n * Each StoreInstance is tied to a specific SanityInstance, manages its own state,\n * and can be independently disposed when no longer needed.\n */\nexport interface StoreInstance<TState> {\n /**\n * Access to the reactive state container for this store instance\n */\n state: StoreState<TState>\n\n /**\n * Checks if this store instance has been disposed\n * @returns Boolean indicating disposed state\n */\n isDisposed: () => void\n\n /**\n * Cleans up this store instance and runs any initialization cleanup functions\n * @remarks Triggers the cleanup function returned from the initialize method\n */\n dispose: () => void\n}\n\n/**\n * Creates a new instance of a store from a store definition\n *\n * @param instance - The Sanity instance this store will be associated with\n * @param storeDefinition - The store definition containing initial state and initialization logic\n * @returns A store instance with state management and lifecycle methods\n *\n * @remarks\n * The store instance maintains its own state that is scoped to the given Sanity instance.\n * If the store definition includes an initialize function, it will be called during\n * instance creation, and its cleanup function will be called during disposal.\n *\n * @example\n * ```ts\n * const counterStore = defineStore({\n * name: 'Counter',\n * getInitialState: () => ({ count: 0 }),\n * initialize: ({state}) => {\n * console.log('Counter store initialized')\n * return () => console.log('Counter store disposed')\n * }\n * })\n *\n * const instance = createStoreInstance(sanityInstance, counterStore)\n * // Later when done with the store:\n * instance.dispose()\n * ```\n */\nexport function createStoreInstance<TState, TKey extends {name: string}>(\n instance: SanityInstance,\n key: TKey,\n {name, getInitialState, initialize}: StoreDefinition<TState, TKey>,\n): StoreInstance<TState> {\n const state = createStoreState(getInitialState(instance, key), {\n enabled: !!getEnv('DEV'),\n name: `${name}-${key.name}`,\n })\n const dispose = initialize?.({state, instance, key})\n const disposed = {current: false}\n\n return {\n state,\n dispose: () => {\n if (disposed.current) return\n disposed.current = true\n dispose?.()\n },\n isDisposed: () => disposed.current,\n }\n}\n","import {type ClientPerspective} from '@sanity/client'\n\nimport {\n type DatasetHandle,\n type DocumentSource,\n isCanvasSource,\n isDatasetSource,\n isMediaLibrarySource,\n type ReleasePerspective,\n} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type SanityInstance} from './createSanityInstance'\nimport {createStoreInstance, type StoreInstance} from './createStoreInstance'\nimport {type StoreState} from './createStoreState'\nimport {type StoreContext, type StoreDefinition} from './defineStore'\n\nexport interface BoundSourceKey {\n name: string\n source: DocumentSource\n}\nexport interface BoundPerspectiveKey extends BoundSourceKey {\n perspective: ClientPerspective | ReleasePerspective\n}\nexport interface BoundDatasetKey {\n name: string\n projectId: string\n dataset: string\n}\n\n/**\n * Defines a store action that operates on a specific state type\n */\nexport type StoreAction<TState, TParams extends unknown[], TReturn, TKey = unknown> = (\n context: StoreContext<TState, TKey>,\n ...params: TParams\n) => TReturn\n\n/**\n * Represents a store action that has been bound to a specific store instance\n */\nexport type BoundStoreAction<_TState, TParams extends unknown[], TReturn> = (\n instance: SanityInstance,\n ...params: TParams\n) => TReturn\n\n/**\n * Creates an action binder function that uses the provided key function\n * to determine how store instances are shared between Sanity instances\n *\n * @param keyFn - Function that generates a key from a Sanity config\n * @returns A function that binds store actions to Sanity instances\n *\n * @remarks\n * Action binders determine how store instances are shared across multiple\n * Sanity instances. The key function determines which instances share state.\n *\n * @example\n * ```ts\n * // Create a custom binder that uses a tenant ID for isolation\n * const bindActionByTenant = createActionBinder(config => config.tenantId || 'default')\n *\n * // Use the custom binder with a store definition\n * const getTenantUsers = bindActionByTenant(\n * userStore,\n * ({state}) => state.get().users\n * )\n * ```\n */\nexport function createActionBinder<\n TKey extends {name: string},\n TKeyParams extends unknown[] = unknown[],\n>(keyFn: (instance: SanityInstance, ...params: TKeyParams) => TKey) {\n const instanceRegistry = new Map<string, Set<string>>()\n const storeRegistry = new Map<string, StoreInstance<unknown>>()\n\n /**\n * Binds a store action to a store definition\n *\n * @param storeDefinition - The store definition\n * @param action - The action to bind\n * @returns A function that executes the action with a Sanity instance\n */\n return function bindAction<TState, TParams extends TKeyParams, TReturn>(\n storeDefinition: StoreDefinition<TState, TKey>,\n action: StoreAction<TState, TParams, TReturn, TKey>,\n ): BoundStoreAction<TState, TParams, TReturn> {\n return function boundAction(instance: SanityInstance, ...params: TParams) {\n const key = keyFn(instance, ...params)\n const compositeKey = storeDefinition.name + (key.name ? `:${key.name}` : '')\n\n // Get or create instance set for this composite key\n let instances = instanceRegistry.get(compositeKey)\n if (!instances) {\n instances = new Set<string>()\n instanceRegistry.set(compositeKey, instances)\n }\n\n // Register instance for disposal tracking\n if (!instances.has(instance.instanceId)) {\n instances.add(instance.instanceId)\n instance.onDispose(() => {\n instances.delete(instance.instanceId)\n\n // Clean up when last instance is disposed\n if (instances.size === 0) {\n storeRegistry.get(compositeKey)?.dispose()\n storeRegistry.delete(compositeKey)\n instanceRegistry.delete(compositeKey)\n }\n })\n }\n\n // Get or create store instance\n let storeInstance = storeRegistry.get(compositeKey)\n if (!storeInstance) {\n storeInstance = createStoreInstance(instance, key, storeDefinition)\n storeRegistry.set(compositeKey, storeInstance)\n }\n\n // Execute action with store context\n return action({instance, state: storeInstance.state as StoreState<TState>, key}, ...params)\n }\n }\n}\n\n/**\n * Binds an action to a store that's scoped to a specific project and dataset\n *\n * @remarks\n * This creates actions that operate on state isolated to a specific projectId and dataset.\n * Different project/dataset combinations will have separate states.\n *\n * @throws Error if projectId or dataset is missing from the Sanity instance config\n *\n * @example\n * ```ts\n * // Define a store\n * const documentStore = defineStore<DocumentState>({\n * name: 'Document',\n * getInitialState: () => ({ documents: {} }),\n * // ...\n * })\n *\n * // Create dataset-specific actions\n * export const fetchDocument = bindActionByDataset(\n * documentStore,\n * ({instance, state}, documentId) => {\n * // This state is isolated to the specific project/dataset\n * // ...fetch logic...\n * }\n * )\n *\n * // Usage\n * fetchDocument(sanityInstance, 'doc123')\n * ```\n */\nexport const bindActionByDataset = createActionBinder<\n BoundDatasetKey,\n [(object & {projectId?: string; dataset?: string})?, ...unknown[]]\n>((instance, options) => {\n const projectId = options?.projectId ?? instance.config.projectId\n const dataset = options?.dataset ?? instance.config.dataset\n if (!projectId || !dataset) {\n throw new Error('This API requires a project ID and dataset configured.')\n }\n return {name: `${projectId}.${dataset}`, projectId, dataset}\n})\n\nconst createSourceKey = (instance: SanityInstance, source?: DocumentSource): BoundSourceKey => {\n let name: string | undefined\n let sourceForKey: DocumentSource | undefined\n if (source) {\n sourceForKey = source\n if (isDatasetSource(source)) {\n name = `${source.projectId}.${source.dataset}`\n } else if (isMediaLibrarySource(source)) {\n name = `media-library:${source.mediaLibraryId}`\n } else if (isCanvasSource(source)) {\n name = `canvas:${source.canvasId}`\n } else {\n throw new Error(`Received invalid source: ${JSON.stringify(source)}`)\n }\n return {name, source: sourceForKey}\n }\n\n // TODO: remove reference to instance.config when we get to v3\n const {projectId, dataset} = instance.config\n if (!projectId || !dataset) {\n throw new Error('This API requires a project ID and dataset configured.')\n }\n return {name: `${projectId}.${dataset}`, source: {projectId, dataset}}\n}\n\n/**\n * Binds an action to a store that's scoped to a specific document source.\n **/\nexport const bindActionBySource = createActionBinder<\n BoundSourceKey,\n [{source?: DocumentSource}, ...unknown[]]\n>((instance, {source}) => {\n return createSourceKey(instance, source)\n})\n\n/**\n * Binds an action to a store that's scoped to a specific document source and perspective.\n *\n * @remarks\n * This creates actions that operate on state isolated to a specific document source and perspective.\n * Different document sources and perspectives will have separate states.\n *\n * This is mostly useful for stores that do batch fetching operations, since the query store\n * can isolate single queries by perspective.\n *\n * @throws Error if source or perspective is missing from the Sanity instance config\n *\n * @example\n * ```ts\n * // Define a store\n * const documentStore = defineStore<DocumentState>({\n * name: 'Document',\n * getInitialState: () => ({ documents: {} }),\n * // ...\n * })\n *\n * // Create source-and-perspective-specific actions\n * export const fetchDocuments = bindActionBySourceAndPerspective(\n * documentStore,\n * ({instance, state}, documentId) => {\n * // This state is isolated to the specific document source and perspective\n * // ...fetch logic...\n * }\n * )\n *\n * // Usage\n * fetchDocument(sanityInstance, 'doc123')\n * ```\n */\nexport const bindActionBySourceAndPerspective = createActionBinder<\n BoundPerspectiveKey,\n [DatasetHandle, ...unknown[]]\n>((instance, options): BoundPerspectiveKey => {\n const {source, perspective} = options\n // TODO: remove reference to instance.config.perspective when we get to v3\n const utilizedPerspective = perspective ?? instance.config.perspective ?? 'drafts'\n let perspectiveKey: string\n if (isReleasePerspective(utilizedPerspective)) {\n perspectiveKey = utilizedPerspective.releaseName\n } else if (typeof utilizedPerspective === 'string') {\n perspectiveKey = utilizedPerspective\n } else {\n // \"StackablePerspective\", shouldn't be a common case, but just in case\n perspectiveKey = JSON.stringify(utilizedPerspective)\n }\n const sourceKey = createSourceKey(instance, source)\n\n return {\n name: `${sourceKey.name}:${perspectiveKey}`,\n source: sourceKey.source,\n perspective: utilizedPerspective,\n }\n})\n\n/**\n * Binds an action to a global store that's shared across all Sanity instances\n *\n * @remarks\n * This creates actions that operate on state shared globally across all Sanity instances.\n * Use this for features like authentication where the state should be the same\n * regardless of which project or dataset is being used.\n *\n * @example\n * ```ts\n * // Define a store\n * const authStore = defineStore<AuthState>({\n * name: 'Auth',\n * getInitialState: () => ({\n * user: null,\n * isAuthenticated: false\n * }),\n * // ...\n * })\n *\n * // Create global actions\n * export const getCurrentUser = bindActionGlobally(\n * authStore,\n * ({state}) => state.get().user\n * )\n *\n * export const login = bindActionGlobally(\n * authStore,\n * ({state, instance}, credentials) => {\n * // Login logic that affects global state\n * // ...\n * }\n * )\n *\n * // Usage with any instance\n * getCurrentUser(sanityInstance)\n * ```\n */\nexport const bindActionGlobally = createActionBinder((..._rest) => ({name: 'global'}))\n","import {defer, distinctUntilChanged, finalize, map, Observable, shareReplay, skip} from 'rxjs'\n\nimport {type StoreAction} from './createActionBinder'\nimport {type SanityInstance} from './createSanityInstance'\nimport {type StoreContext} from './defineStore'\n\n/**\n * Represents a reactive state source that provides synchronized access to store data\n *\n * @remarks\n * Designed to work with React's useSyncExternalStore hook. Provides three ways to access data:\n * 1. `getCurrent()` for synchronous current value access\n * 2. `subscribe()` for imperative change notifications\n * 3. `observable` for reactive stream access\n *\n * @public\n */\nexport interface StateSource<T> {\n /**\n * Subscribes to state changes with optional callback\n * @param onStoreChanged - Called whenever relevant state changes occur\n * @returns Unsubscribe function to clean up the subscription\n */\n subscribe: (onStoreChanged?: () => void) => () => void\n\n /**\n * Gets the current derived state value\n *\n * @remarks\n * Safe to call without subscription. Will always return the latest value\n * based on the current store state and selector parameters.\n */\n getCurrent: () => T\n\n /**\n * Observable stream of state values\n *\n * @remarks\n * Shares a single underlying subscription between all observers. Emits:\n * - Immediately with current value on subscription\n * - On every relevant state change\n * - Errors if selector throws\n */\n observable: Observable<T>\n}\n\n/**\n * Context passed to selectors when deriving state\n *\n * @remarks\n * Provides access to both the current state value and the Sanity instance,\n * allowing selectors to use configuration values when computing derived state.\n * The context is memoized for each state object and instance combination\n * to optimize performance and prevent unnecessary recalculations.\n *\n * @example\n * ```ts\n * // Using both state and instance in a selector (psuedo example)\n * const getUserByProjectId = createStateSourceAction(\n * ({ state, instance }: SelectorContext<UsersState>, options?: ProjectHandle) => {\n * const allUsers = state.users\n * const projectId = options?.projectId ?? instance.config.projectId\n * return allUsers.filter(user => user.projectId === projectId)\n * }\n * )\n * ```\n */\nexport interface SelectorContext<TState> {\n /**\n * The current state object from the store\n */\n state: TState\n\n /**\n * The Sanity instance associated with this state\n */\n instance: SanityInstance\n}\n\n/**\n * Function type for selecting derived state from store state and parameters\n * @public\n */\nexport type Selector<TState, TParams extends unknown[], TReturn> = (\n context: SelectorContext<TState>,\n ...params: TParams\n) => TReturn\n\n/**\n * Configuration options for creating a state source action\n */\ninterface StateSourceOptions<TState, TParams extends unknown[], TReturn, TKey> {\n /**\n * Selector function that derives the desired value from store state\n *\n * @remarks\n * Will be called on every store change. Should be pure function.\n * Thrown errors will propagate to observable subscribers.\n */\n selector: Selector<TState, TParams, TReturn>\n\n /**\n * Optional setup/cleanup handler for subscriptions\n *\n * @param context - Store context containing state and instance\n * @param params - Action parameters provided during invocation\n * @returns Optional cleanup function called when subscription ends\n */\n onSubscribe?: (context: StoreContext<TState, TKey>, ...params: TParams) => void | (() => void)\n\n /**\n * Equality function to prevent unnecessary updates\n */\n isEqual?: (prev: TReturn, curr: TReturn) => boolean\n}\n\n/**\n * Creates a state source action that generates StateSource instances\n *\n * @remarks\n * The returned action can be bound to a store using createActionBinder.\n * When invoked, returns a StateSource that stays synchronized with the store.\n *\n * Key performance features:\n * - Memoizes selector contexts to prevent redundant object creation\n * - Only runs selectors when the underlying state changes\n *\n * For complex data transformations, consider using memoized selectors\n * (like those from Reselect) to prevent expensive recalculations.\n *\n * @example\n * ```ts\n * // Create a simple counter source\n * const getCount = createStateSourceAction(({state}: SelectorContext<CounterState>) => state.count)\n * ```\n *\n * @example\n * ```ts\n * // Create a parameterized source with setup/cleanup\n * const getItem = createStateSourceAction({\n * selector: ({state}, index: number) => state.items[index],\n * onSubscribe: (context, index) => {\n * trackItemSubscription(index)\n * return () => untrackItem(index)\n * }\n * })\n * ```\n *\n * @example\n * ```ts\n * // Binding a state source to a specific store\n * const documentStore = defineStore<DocumentState>({\n * name: 'Documents',\n * getInitialState: () => ({ documents: {} }),\n * // ...\n * })\n *\n * const getDocument = bindActionByDataset(\n * documentStore,\n * createStateSourceAction(({state}, documentId: string) => state.documents[documentId])\n * )\n *\n * // Usage\n * const documentSource = getDocument(sanityInstance, 'doc123')\n * const doc = documentSource.getCurrent()\n * const subscription = documentSource.observable.subscribe(updatedDoc => {\n * console.log('Document changed:', updatedDoc)\n * })\n * ```\n */\nexport function createStateSourceAction<TState, TParams extends unknown[], TReturn, TKey = unknown>(\n options: Selector<TState, TParams, TReturn> | StateSourceOptions<TState, TParams, TReturn, TKey>,\n): StoreAction<TState, TParams, StateSource<TReturn>, TKey> {\n const selector = typeof options === 'function' ? options : options.selector\n const subscribeHandler = options && 'onSubscribe' in options ? options.onSubscribe : undefined\n const isEqual = options && 'isEqual' in options ? (options.isEqual ?? Object.is) : Object.is\n const selectorContextCache = new WeakMap<\n object,\n WeakMap<SanityInstance, SelectorContext<TState>>\n >()\n\n /**\n * The state source action implementation\n * @param context - Store context providing access to state and instance\n * @param params - Parameters provided when invoking the bound action\n */\n function stateSourceAction(context: StoreContext<TState, TKey>, ...params: TParams) {\n const {state, instance} = context\n\n const getCurrent = (currentState: TState) => {\n if (typeof currentState !== 'object' || currentState === null) {\n throw new Error(\n `Expected store state to be an object but got \"${typeof currentState}\" instead`,\n )\n }\n\n let instanceCache = selectorContextCache.get(currentState)\n if (!instanceCache) {\n instanceCache = new WeakMap<SanityInstance, SelectorContext<TState>>()\n selectorContextCache.set(currentState, instanceCache)\n }\n let selectorContext = instanceCache.get(instance)\n if (!selectorContext) {\n selectorContext = {state: currentState, instance}\n instanceCache.set(instance, selectorContext)\n }\n return selector(selectorContext, ...params)\n }\n\n // `state.observable` will emit the current value immediately and\n // hence we inherit the same behavior here.\n let values = state.observable.pipe(map(getCurrent), distinctUntilChanged(isEqual))\n\n if (subscribeHandler) {\n values = withSubscribeHook(values, () => subscribeHandler(context, ...params))\n }\n\n // Share but replay the latest value so every subscriber gets an\n // initial synchronous emission, matching `state.observable`. That keeps\n // `skip(1)` in `subscribe()` aligned with \"skip current snapshot\" rather than\n // silently eating the first real update after multicasting.\n const sharedValues = values.pipe(shareReplay({bufferSize: 1, refCount: true}))\n\n const subscribe = (onStoreChanged?: () => void) => {\n const subscription = sharedValues.pipe(skip(1)).subscribe({\n next: () => onStoreChanged?.(),\n // Propagate selector errors to both subscription types\n error: () => onStoreChanged?.(),\n })\n\n return () => {\n subscription.unsubscribe()\n }\n }\n\n return {\n getCurrent: () => getCurrent(state.get()),\n subscribe,\n observable: sharedValues,\n }\n }\n\n return stateSourceAction\n}\n\n/**\n * Creates a new Observable which wraps an existing Observable which will invoke\n * the function when a new subscriber appears.\n */\nfunction withSubscribeHook<T>(obs: Observable<T>, fn: () => void | (() => void)): Observable<T> {\n return defer(() => {\n const cleanup = fn()\n return cleanup ? obs.pipe(finalize(() => cleanup())) : obs\n })\n}\n","declare const __SANITY_STAGING__: boolean | undefined\n\n/**\n * Returns the staging API host if the `__SANITY_STAGING__` build-time flag is\n * set to `true` (mirroring how Sanity Studio detects staging builds).\n *\n * @internal\n */\nexport function getStagingApiHost(): string | undefined {\n if (typeof __SANITY_STAGING__ !== 'undefined' && __SANITY_STAGING__ === true) {\n return 'https://api.sanity.work'\n }\n return undefined\n}\n","export const DEFAULT_BASE = 'http://localhost'\nexport const AUTH_CODE_PARAM = 'sid'\nexport const DEFAULT_API_VERSION = '2021-06-07'\nexport const REQUEST_TAG_PREFIX = 'sanity.sdk.auth'\n","import {type SanityConfig} from '../config/sanityConfig'\nimport {DEFAULT_BASE} from './authConstants'\n\n/**\n * The runtime auth mode determines which authentication strategy the SDK uses.\n *\n * - `studio` — Running inside Sanity Studio. Token is discovered from\n * Studio's localStorage entry or via cookie auth.\n * - `dashboard` — Running inside the Sanity Dashboard iframe. Token is\n * provided by the parent frame via Comlink.\n * - `standalone` — Running as an independent app. Token comes from\n * localStorage or the OAuth login flow.\n *\n * @internal\n */\ntype AuthMode = 'studio' | 'dashboard' | 'standalone'\n\n/**\n * Determines the auth mode from instance config and environment.\n *\n * Priority:\n * 1. `studio` config provided → `'studio'`\n * 2. `studioMode.enabled` in config (deprecated) → `'studio'`\n * 3. Dashboard context detected (`_context` URL param with content) → `'dashboard'`\n * 4. Otherwise → `'standalone'`\n *\n * @internal\n */\nexport function resolveAuthMode(config: SanityConfig, locationHref: string): AuthMode {\n if (isStudioConfig(config)) return 'studio'\n if (detectDashboardContext(locationHref)) return 'dashboard'\n return 'standalone'\n}\n\n/**\n * Returns `true` when the config indicates the SDK is running inside a Studio.\n * Checks the new `studio` field first, then falls back to the deprecated\n * `studioMode.enabled` for backwards compatibility.\n *\n * @internal\n */\nexport function isStudioConfig(config: SanityConfig): boolean {\n return !!config.studio || !!config.studioMode?.enabled\n}\n\n/**\n * Checks whether the given location href contains a `_context` URL parameter\n * with a non-empty JSON object, indicating the SDK is running inside the\n * Sanity Dashboard.\n *\n * @internal\n */\nfunction detectDashboardContext(locationHref: string): boolean {\n try {\n const parsedUrl = new URL(locationHref, DEFAULT_BASE)\n const contextParam = parsedUrl.searchParams.get('_context')\n if (!contextParam) return false\n\n const parsed: unknown = JSON.parse(contextParam)\n return (\n typeof parsed === 'object' &&\n parsed !== null &&\n !Array.isArray(parsed) &&\n Object.keys(parsed as Record<string, unknown>).length > 0\n )\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('Failed to parse dashboard context from initial location:', err)\n return false\n }\n}\n","/**\n * Represents the various states the authentication type can be in.\n *\n * @public\n */\nexport enum AuthStateType {\n LOGGED_IN = 'logged-in',\n LOGGING_IN = 'logging-in',\n ERROR = 'error',\n LOGGED_OUT = 'logged-out',\n}\n","import {\n distinctUntilChanged,\n exhaustMap,\n filter,\n firstValueFrom,\n from,\n map,\n Observable,\n type Subscription,\n switchMap,\n takeWhile,\n timer,\n} from 'rxjs'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'\nimport {AuthStateType} from './authStateType'\nimport {type AuthState, type AuthStoreState} from './authStore'\n\nconst REFRESH_INTERVAL = 12 * 60 * 60 * 1000 // 12 hours in milliseconds\nconst LOCK_NAME = 'sanity-token-refresh-lock'\n\n/** @internal */\nexport function getLastRefreshTime(storageArea: Storage | undefined, storageKey: string): number {\n try {\n const data = storageArea?.getItem(`${storageKey}_last_refresh`)\n const parsed = data ? parseInt(data, 10) : 0\n return isNaN(parsed) ? 0 : parsed\n } catch {\n return 0\n }\n}\n\n/** @internal */\nexport function setLastRefreshTime(storageArea: Storage | undefined, storageKey: string): void {\n try {\n storageArea?.setItem(`${storageKey}_last_refresh`, Date.now().toString())\n } catch {\n // Ignore storage errors\n }\n}\n\n/** @internal */\nexport function getNextRefreshDelay(storageArea: Storage | undefined, storageKey: string): number {\n const lastRefresh = getLastRefreshTime(storageArea, storageKey)\n if (!lastRefresh) return 0\n\n const now = Date.now()\n const nextRefreshTime = lastRefresh + REFRESH_INTERVAL\n return Math.max(0, nextRefreshTime - now)\n}\n\nfunction createTokenRefreshStream(\n token: string,\n clientFactory: AuthStoreState['options']['clientFactory'],\n apiHost: string | undefined,\n): Observable<{token: string}> {\n return new Observable((subscriber) => {\n const client = clientFactory({\n apiVersion: DEFAULT_API_VERSION,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n useProjectHostname: false,\n useCdn: false,\n token,\n ignoreBrowserTokenWarning: true,\n ...(apiHost && {apiHost}),\n })\n\n const subscription = client.observable\n .request<{token: string}>({\n uri: 'auth/refresh-token',\n method: 'POST',\n tag: 'refresh-token',\n body: {\n token,\n },\n })\n .subscribe(subscriber)\n\n return () => subscription.unsubscribe()\n })\n}\n\nasync function acquireTokenRefreshLock(\n refreshFn: () => Promise<void>,\n storageArea: Storage | undefined,\n storageKey: string,\n): Promise<boolean> {\n if (!navigator.locks) {\n // If Web Locks API is not supported, perform an immediate, uncoordinated refresh.\n // eslint-disable-next-line no-console\n console.warn('Web Locks API not supported. Proceeding with uncoordinated refresh.')\n await refreshFn()\n setLastRefreshTime(storageArea, storageKey)\n return true // Indicate success to allow stream processing, though it won't loop.\n }\n\n try {\n // Attempt to acquire an exclusive lock for token refresh coordination.\n // The callback handles the continuous refresh cycle while the lock is held.\n const result = await navigator.locks.request(LOCK_NAME, {mode: 'exclusive'}, async (lock) => {\n if (!lock) return false // Lock not granted\n\n // Problematic infinite loop - needs redesign for graceful termination.\n // This loop continuously refreshes the token at REFRESH_INTERVAL.\n while (true) {\n const delay = getNextRefreshDelay(storageArea, storageKey)\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n try {\n await refreshFn()\n setLastRefreshTime(storageArea, storageKey)\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Token refresh failed within lock:', error)\n // Decide how to handle errors - break, retry, etc.? Currently logs and continues.\n }\n // Wait for the next interval\n await new Promise((resolve) => setTimeout(resolve, REFRESH_INTERVAL))\n }\n // Unreachable due to while(true)\n })\n // The promise from navigator.locks.request resolves with the callback's return value,\n // but only if the callback finishes. The infinite loop prevents this.\n return result === true\n } catch (error) {\n // Handle potential errors during the initial lock request itself.\n // eslint-disable-next-line no-console\n console.error('Failed to request token refresh lock:', error)\n return false // Indicate lock acquisition failure.\n }\n}\n\nfunction shouldRefreshToken(lastRefresh: number | undefined): boolean {\n if (!lastRefresh) return true\n const timeSinceLastRefresh = Date.now() - lastRefresh\n return timeSinceLastRefresh >= REFRESH_INTERVAL\n}\n\n/**\n * @internal\n */\nexport const refreshStampedToken = ({state}: StoreContext<AuthStoreState>): Subscription => {\n const {clientFactory, apiHost, storageArea, storageKey} = state.get().options\n\n const refreshToken$ = state.observable.pipe(\n map((storeState) => ({\n authState: storeState.authState,\n dashboardContext: storeState.dashboardContext,\n })),\n filter(\n (\n storeState,\n ): storeState is {\n authState: Extract<AuthState, {type: AuthStateType.LOGGED_IN}>\n dashboardContext: AuthStoreState['dashboardContext']\n } => storeState.authState.type === AuthStateType.LOGGED_IN,\n ),\n distinctUntilChanged(\n (prev, curr) =>\n prev.authState.type === curr.authState.type &&\n prev.authState.token === curr.authState.token && // Only care about token for distinctness here\n prev.dashboardContext === curr.dashboardContext,\n ), // Make distinctness check explicit\n filter((storeState) => storeState.authState.token.includes('-st')), // Ensure we only try to refresh stamped tokens\n exhaustMap((storeState) => {\n // USE exhaustMap instead of switchMap\n // Create a function that performs a single refresh and updates state/storage\n const performRefresh = async () => {\n // Read the latest token directly from state inside refresh\n const currentState = state.get()\n if (currentState.authState.type !== AuthStateType.LOGGED_IN) {\n throw new Error('User logged out before refresh could complete') // Abort refresh\n }\n const currentToken = currentState.authState.token\n\n const response = await firstValueFrom(\n createTokenRefreshStream(currentToken, clientFactory, apiHost),\n )\n\n state.set('setRefreshStampedToken', (prev) => ({\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? {...prev.authState, token: response.token}\n : prev.authState,\n }))\n storageArea?.setItem(storageKey, JSON.stringify({token: response.token}))\n }\n\n if (storeState.dashboardContext) {\n return new Observable<{token: string}>((subscriber) => {\n const visibilityHandler = () => {\n const currentState = state.get()\n if (\n document.visibilityState === 'visible' &&\n currentState.authState.type === AuthStateType.LOGGED_IN &&\n shouldRefreshToken(currentState.authState.lastTokenRefresh)\n ) {\n createTokenRefreshStream(\n currentState.authState.token,\n clientFactory,\n apiHost,\n ).subscribe({\n next: (response) => {\n state.set('setRefreshStampedToken', (prev) => ({\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? {\n ...prev.authState,\n token: response.token,\n lastTokenRefresh: Date.now(),\n }\n : prev.authState,\n }))\n subscriber.next(response)\n },\n error: (error) => subscriber.error(error),\n })\n }\n }\n\n const timerSubscription = timer(REFRESH_INTERVAL, REFRESH_INTERVAL)\n .pipe(\n filter(() => document.visibilityState === 'visible'),\n switchMap(() => {\n const currentState = state.get().authState\n if (currentState.type !== AuthStateType.LOGGED_IN) {\n throw new Error('User logged out before refresh could complete')\n }\n return createTokenRefreshStream(currentState.token, clientFactory, apiHost)\n }),\n )\n .subscribe({\n next: (response) => {\n state.set('setRefreshStampedToken', (prev) => ({\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? {\n ...prev.authState,\n token: response.token,\n lastTokenRefresh: Date.now(),\n }\n : prev.authState,\n }))\n subscriber.next(response)\n },\n error: (error) => subscriber.error(error),\n })\n\n document.addEventListener('visibilitychange', visibilityHandler)\n\n return () => {\n document.removeEventListener('visibilitychange', visibilityHandler)\n timerSubscription.unsubscribe()\n }\n }).pipe(\n takeWhile(() => state.get().authState.type === AuthStateType.LOGGED_IN),\n map((response: {token: string}) => ({token: response.token})),\n )\n }\n\n // If not in dashboard context, use lock-based refresh\n return from(acquireTokenRefreshLock(performRefresh, storageArea, storageKey)).pipe(\n filter((hasLock) => hasLock),\n map(() => {\n const currentState = state.get().authState\n if (currentState.type !== AuthStateType.LOGGED_IN) {\n throw new Error('User logged out before refresh could complete')\n }\n return {token: currentState.token} as const\n }),\n )\n }),\n )\n\n return refreshToken$.subscribe({\n next: (response: {token: string}) => {\n state.set('setRefreshStampedToken', (prev) => ({\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? {\n ...prev.authState,\n token: response.token,\n lastTokenRefresh: Date.now(),\n }\n : prev.authState,\n }))\n storageArea?.setItem(storageKey, JSON.stringify({token: response.token}))\n },\n error: (error) => {\n state.set('setRefreshStampedTokenError', {authState: {type: AuthStateType.ERROR, error}})\n },\n })\n}\n","import {type CurrentUser} from '@sanity/types'\nimport {distinctUntilChanged, filter, map, type Subscription, switchMap} from 'rxjs'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'\nimport {isStudioConfig} from './authMode'\nimport {AuthStateType} from './authStateType'\nimport {type AuthMethodOptions, type AuthState, type AuthStoreState} from './authStore'\n\n/**\n * Subscribes to auth state changes and fetches the current user when\n * the state transitions to `LOGGED_IN` without a `currentUser`.\n *\n * @param context - The store context for the auth store.\n * @param fetchOptions - Configuration options. When `useProjectHostname` is\n * `true`, requests use the project-specific hostname (required for Studio\n * cookie auth). Set to `true` for studio mode, `false` otherwise.\n *\n * @internal\n */\nexport const subscribeToStateAndFetchCurrentUser = (\n {state, instance}: StoreContext<AuthStoreState>,\n fetchOptions?: {useProjectHostname?: boolean},\n): Subscription => {\n const {clientFactory, apiHost} = state.get().options\n const useProjectHostname = fetchOptions?.useProjectHostname ?? isStudioConfig(instance.config)\n const projectId = instance.config.projectId\n\n const currentUser$ = state.observable\n .pipe(\n map(({authState, options: storeOptions}) => ({\n authState,\n authMethod: storeOptions.authMethod,\n })),\n filter(\n (\n value,\n ): value is {\n authState: Extract<AuthState, {type: AuthStateType.LOGGED_IN}>\n authMethod: AuthMethodOptions\n } => value.authState.type === AuthStateType.LOGGED_IN && !value.authState.currentUser,\n ),\n map((value) => ({token: value.authState.token, authMethod: value.authMethod})),\n distinctUntilChanged(\n (prev, curr) => prev.token === curr.token && prev.authMethod === curr.authMethod,\n ),\n )\n .pipe(\n map(({token, authMethod}) =>\n clientFactory({\n apiVersion: DEFAULT_API_VERSION,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n token: authMethod === 'cookie' ? undefined : token,\n ignoreBrowserTokenWarning: true,\n useProjectHostname,\n useCdn: false,\n ...(authMethod === 'cookie' ? {withCredentials: true} : {}),\n ...(useProjectHostname && projectId ? {projectId} : {}),\n ...(apiHost && {apiHost}),\n }),\n ),\n switchMap((client) =>\n client.observable.request<CurrentUser>({\n uri: '/users/me',\n method: 'GET',\n tag: 'users.get-current',\n }),\n ),\n )\n\n return currentUser$.subscribe({\n next: (currentUser) => {\n state.set('setCurrentUser', (prev) => ({\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? {...prev.authState, currentUser}\n : prev.authState,\n }))\n },\n error: (error) => {\n state.set('setError', {authState: {type: AuthStateType.ERROR, error}})\n },\n })\n}\n","import {type ClientError} from '@sanity/client'\nimport {type CurrentUser} from '@sanity/types'\nimport {EMPTY, fromEvent, Observable} from 'rxjs'\n\nimport {AUTH_CODE_PARAM, DEFAULT_BASE} from './authConstants'\nimport {AuthStateType} from './authStateType'\nimport {type LoggedInAuthState} from './authStore'\n\n/**\n * Creates a properly initialized {@link LoggedInAuthState}.\n *\n * For stamped tokens (containing `\"-st\"`), `lastTokenRefresh` is set to\n * `Date.now()` so that the visibility-change handler in\n * {@link refreshStampedToken} does not trigger an unnecessary refresh the\n * first time the tab becomes visible.\n *\n * @param token - The auth token.\n * @param currentUser - The current user, or `null` if not yet fetched.\n * @param existingLastTokenRefresh - An existing timestamp to preserve\n * (e.g. when updating a token while keeping the previous refresh time).\n * @internal\n */\nexport function createLoggedInAuthState(\n token: string,\n currentUser: CurrentUser | null,\n existingLastTokenRefresh?: number,\n): LoggedInAuthState {\n const isStampedToken = token.includes('-st')\n const lastTokenRefresh = existingLastTokenRefresh ?? (isStampedToken ? Date.now() : undefined)\n\n return {\n type: AuthStateType.LOGGED_IN,\n token,\n currentUser,\n ...(lastTokenRefresh !== undefined && {lastTokenRefresh}),\n }\n}\n\nexport function getAuthCode(callbackUrl: string | undefined, locationHref: string): string | null {\n const loc = new URL(locationHref, DEFAULT_BASE)\n const callbackLocation = callbackUrl ? new URL(callbackUrl, DEFAULT_BASE) : undefined\n const callbackLocationMatches = callbackLocation\n ? loc.pathname.toLowerCase().startsWith(callbackLocation.pathname.toLowerCase())\n : true\n\n // First, try getting the auth code (sid) from hash or search params directly\n let authCode =\n new URLSearchParams(loc.hash.slice(1)).get(AUTH_CODE_PARAM) ||\n new URLSearchParams(loc.search).get(AUTH_CODE_PARAM)\n\n // If not found directly, try extracting it from the _context parameter as a fallback\n if (!authCode) {\n const contextParam = new URLSearchParams(loc.search).get('_context')\n if (contextParam) {\n try {\n const parsedContext = JSON.parse(contextParam)\n if (\n parsedContext &&\n typeof parsedContext === 'object' &&\n typeof parsedContext.sid === 'string' &&\n parsedContext.sid // Ensure it's not an empty string\n ) {\n authCode = parsedContext.sid\n }\n } catch {\n // Silently ignore _context JSON parsing errors; authCode remains null/empty\n }\n }\n }\n\n return authCode && callbackLocationMatches ? authCode : null\n}\n\nexport function getTokenFromLocation(locationHref: string): string | null {\n const loc = new URL(locationHref)\n const token = new URLSearchParams(loc.hash.slice(1)).get('token')\n return token ? token : null\n}\n\n/**\n * Attempts to retrieve a token from the configured storage.\n * If invalid or not present, returns null.\n */\nexport function getTokenFromStorage(\n storageArea: Storage | undefined,\n storageKey: string,\n): string | null {\n if (!storageArea) return null\n const item = storageArea.getItem(storageKey)\n if (item === null) return null\n\n try {\n const parsed: unknown = JSON.parse(item)\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n !('token' in parsed) ||\n typeof parsed.token !== 'string'\n ) {\n throw new Error('Invalid stored auth data structure')\n }\n return parsed.token\n } catch {\n storageArea.removeItem(storageKey)\n return null\n }\n}\n\n/**\n * Creates an observable stream of storage events. If not in a browser environment,\n * returns an EMPTY observable.\n */\nexport function getStorageEvents(): Observable<StorageEvent> {\n const isBrowser = typeof window !== 'undefined' && typeof window.addEventListener === 'function'\n\n if (!isBrowser) {\n return EMPTY\n }\n\n return fromEvent<StorageEvent>(window, 'storage')\n}\n\n/**\n * Returns a default storage instance (localStorage) if available.\n * If not available or an error occurs, returns undefined.\n */\nexport function getDefaultStorage(): Storage | undefined {\n try {\n if (typeof localStorage !== 'undefined' && typeof localStorage.getItem === 'function') {\n return localStorage\n }\n return undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Returns the default location to use.\n * Tries accessing `location.href`, falls back to a default base if not available or on error.\n */\nexport function getDefaultLocation(): string {\n try {\n if (typeof location === 'undefined') return DEFAULT_BASE\n if (typeof location.href === 'string') return location.href\n return DEFAULT_BASE\n } catch {\n return DEFAULT_BASE\n }\n}\n\n/**\n * Cleans up the URL by removing the `token` from the hash and the `sid` and `url` search params.\n * @internal\n */\nexport function getCleanedUrl(locationUrl: string): string {\n const loc = new URL(locationUrl)\n // Remove only the `token` param from the hash while preserving other fragments\n const rawHash = loc.hash.startsWith('#') ? loc.hash.slice(1) : loc.hash\n if (rawHash && rawHash.includes('=')) {\n const hashParams = new URLSearchParams(rawHash)\n hashParams.delete('token')\n hashParams.delete('withSid')\n const nextHash = hashParams.toString()\n loc.hash = nextHash ? `#${nextHash}` : ''\n }\n loc.searchParams.delete('sid')\n loc.searchParams.delete('url')\n return loc.toString()\n}\n\n// -----------------------------------------------------------------------------\n// ClientError helpers (shared)\n// -----------------------------------------------------------------------------\n\n/** @internal */\nexport type ApiErrorBody = {\n error?: {type?: string; description?: string}\n type?: string\n description?: string\n message?: string\n}\n\n/** @internal Extracts the structured API error body from a ClientError, if present. */\nexport function getClientErrorApiBody(error: ClientError): ApiErrorBody | undefined {\n const body: unknown = (error as ClientError).response?.body\n return body && typeof body === 'object' ? (body as ApiErrorBody) : undefined\n}\n\n/** @internal Returns the error type string from an API error body, if available. */\nexport function getClientErrorApiType(error: ClientError): string | undefined {\n const body = getClientErrorApiBody(error)\n return body?.error?.type ?? body?.type\n}\n\n/** @internal Returns the error description string from an API error body, if available. */\nexport function getClientErrorApiDescription(error: ClientError): string | undefined {\n const body = getClientErrorApiBody(error)\n return body?.error?.description ?? body?.description\n}\n\n/** @internal True if the error represents a projectUserNotFoundError. */\nexport function isProjectUserNotFoundClientError(error: ClientError): boolean {\n return getClientErrorApiType(error) === 'projectUserNotFoundError'\n}\n","import {type Subscription} from 'rxjs'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {DEFAULT_BASE} from './authConstants'\nimport {AuthStateType} from './authStateType'\nimport {type AuthStoreState, type DashboardContext} from './authStore'\nimport {type AuthStrategyOptions, type AuthStrategyResult} from './authStrategy'\nimport {refreshStampedToken} from './refreshStampedToken'\nimport {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'\nimport {createLoggedInAuthState, getAuthCode, getTokenFromLocation} from './utils'\n\n/**\n * Parses the dashboard context from a location href's `_context` URL parameter.\n * Strips the `sid` property from the parsed context (it's handled separately).\n *\n * @internal\n */\nfunction parseDashboardContext(locationHref: string): DashboardContext {\n try {\n const parsedUrl = new URL(locationHref, DEFAULT_BASE)\n const contextParam = parsedUrl.searchParams.get('_context')\n if (contextParam) {\n const parsedContext = JSON.parse(contextParam)\n\n if (\n parsedContext &&\n typeof parsedContext === 'object' &&\n !Array.isArray(parsedContext) &&\n Object.keys(parsedContext).length > 0\n ) {\n // Explicitly remove the 'sid' property before assigning\n delete parsedContext.sid\n return parsedContext as DashboardContext\n }\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('Failed to parse dashboard context from initial location:', err)\n }\n return {} // Empty dashboard context if parsing fails\n}\n\n/**\n * Resolves the initial auth state for Dashboard mode.\n *\n * In dashboard mode the token is provided by the parent frame via Comlink,\n * so the SDK starts in a `LOGGED_OUT` state and waits for the token to arrive.\n * The `_context` URL parameter provides dashboard metadata (orgId, mode, env).\n *\n * A provided token (`auth.token`) or an auth code/callback still takes\n * precedence, even when running inside the dashboard.\n *\n * @internal\n */\nexport function getDashboardInitialState(options: AuthStrategyOptions): AuthStrategyResult {\n const {authConfig, initialLocationHref} = options\n const providedToken = authConfig.token\n const callbackUrl = authConfig.callbackUrl\n const storageKey = '__sanity_auth_token'\n\n const dashboardContext = parseDashboardContext(initialLocationHref)\n\n // Dashboard does NOT use localStorage — token comes from the parent frame\n const storageArea = undefined\n\n // Provided token always wins, even in dashboard\n if (providedToken) {\n return {\n authState: createLoggedInAuthState(providedToken, null),\n storageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext,\n }\n }\n\n // Check for auth code or token-from-location (callback handling)\n if (getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref)) {\n return {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: false},\n storageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext,\n }\n }\n\n // Default: logged out, waiting for Comlink to provide token\n return {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n storageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext,\n }\n}\n\n/**\n * Initialize Dashboard auth subscriptions:\n * - Subscribe to state changes and fetch current user\n * - Start stamped token refresh\n *\n * Note: storage events are NOT subscribed in dashboard mode since\n * the dashboard does not use localStorage for token persistence.\n *\n * @internal\n */\nexport function initializeDashboardAuth(\n context: StoreContext<AuthStoreState>,\n tokenRefresherRunning: boolean,\n): {dispose: () => void; tokenRefresherStarted: boolean} {\n const subscriptions: Subscription[] = []\n let startedRefresher = false\n\n subscriptions.push(subscribeToStateAndFetchCurrentUser(context, {useProjectHostname: false}))\n\n // Dashboard does not subscribe to storage events\n\n if (!tokenRefresherRunning) {\n startedRefresher = true\n subscriptions.push(refreshStampedToken(context))\n }\n\n return {\n dispose: () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe()\n }\n },\n tokenRefresherStarted: startedRefresher,\n }\n}\n","import {defer, distinctUntilChanged, filter, map, type Subscription} from 'rxjs'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {AuthStateType} from './authStateType'\nimport {type AuthStoreState} from './authStore'\nimport {createLoggedInAuthState, getStorageEvents, getTokenFromStorage} from './utils'\n\nexport const subscribeToStorageEventsAndSetToken = ({\n state,\n}: StoreContext<AuthStoreState>): Subscription => {\n const {storageArea, storageKey} = state.get().options\n\n const tokenFromStorage$ = defer(getStorageEvents).pipe(\n filter(\n (e): e is StorageEvent & {newValue: string} =>\n e.storageArea === storageArea && e.key === storageKey,\n ),\n map(() => getTokenFromStorage(storageArea, storageKey)),\n distinctUntilChanged(),\n )\n\n return tokenFromStorage$.subscribe((token) => {\n state.set('updateTokenFromStorageEvent', {\n authState: token\n ? createLoggedInAuthState(token, null)\n : {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n })\n })\n}\n","import {type Subscription} from 'rxjs'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {AuthStateType} from './authStateType'\nimport {type AuthStoreState} from './authStore'\nimport {type AuthStrategyOptions, type AuthStrategyResult} from './authStrategy'\nimport {refreshStampedToken} from './refreshStampedToken'\nimport {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'\nimport {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'\nimport {\n createLoggedInAuthState,\n getAuthCode,\n getDefaultStorage,\n getTokenFromLocation,\n getTokenFromStorage,\n} from './utils'\n\n/**\n * Resolves the initial auth state for Standalone mode.\n *\n * Token discovery order:\n * 1. Provided token (`auth.token` in config)\n * 2. Auth code or token from location (OAuth callback)\n * 3. localStorage: `__sanity_auth_token`\n * 4. Falls back to `LOGGED_OUT`\n *\n * @internal\n */\nexport function getStandaloneInitialState(options: AuthStrategyOptions): AuthStrategyResult {\n const {authConfig, initialLocationHref} = options\n const providedToken = authConfig.token\n const callbackUrl = authConfig.callbackUrl\n const storageKey = '__sanity_auth_token'\n const storageArea = authConfig.storageArea ?? getDefaultStorage()\n\n // Provided token always wins\n if (providedToken) {\n return {\n authState: createLoggedInAuthState(providedToken, null),\n storageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext: {},\n }\n }\n\n // Check for auth code or token-from-location (OAuth callback)\n if (getAuthCode(callbackUrl, initialLocationHref) || getTokenFromLocation(initialLocationHref)) {\n return {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: false},\n storageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext: {},\n }\n }\n\n // Try localStorage\n const token = getTokenFromStorage(storageArea, storageKey)\n if (token) {\n return {\n authState: createLoggedInAuthState(token, null),\n storageKey,\n storageArea,\n authMethod: 'localstorage',\n dashboardContext: {},\n }\n }\n\n // No token found\n return {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n storageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext: {},\n }\n}\n\n/**\n * Initialize Standalone auth subscriptions:\n * - Subscribe to state changes and fetch current user\n * - Subscribe to storage events for token key\n * - Start stamped token refresh\n *\n * @internal\n */\nexport function initializeStandaloneAuth(\n context: StoreContext<AuthStoreState>,\n tokenRefresherRunning: boolean,\n): {dispose: () => void; tokenRefresherStarted: boolean} {\n const subscriptions: Subscription[] = []\n let startedRefresher = false\n\n subscriptions.push(subscribeToStateAndFetchCurrentUser(context, {useProjectHostname: false}))\n\n const storageArea = context.state.get().options?.storageArea\n if (storageArea) {\n subscriptions.push(subscribeToStorageEventsAndSetToken(context))\n }\n\n if (!tokenRefresherRunning) {\n startedRefresher = true\n subscriptions.push(refreshStampedToken(context))\n }\n\n return {\n dispose: () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe()\n }\n },\n tokenRefresherStarted: startedRefresher,\n }\n}\n","import {type ClientConfig, type SanityClient} from '@sanity/client'\n\nimport {REQUEST_TAG_PREFIX} from './authConstants'\nimport {getTokenFromStorage} from './utils'\n\n/**\n * Cookie auth is a best-effort probe — if the API doesn't respond quickly,\n * the user likely isn't cookie-authenticated and we should move on rather\n * than block the auth flow indefinitely.\n */\nconst COOKIE_AUTH_TIMEOUT_MS = 10_000\n\n/**\n * Attempts to check for cookie auth by making a withCredentials request to the users endpoint.\n * @param projectId - The project ID to check for cookie auth.\n * @param clientFactory - A factory function that creates a Sanity client.\n * @returns True if the user is authenticated, false otherwise.\n * @internal\n */\nexport async function checkForCookieAuth(\n projectId: string | undefined,\n clientFactory: (config: ClientConfig) => SanityClient,\n): Promise<boolean> {\n if (!projectId) return false\n try {\n const client = clientFactory({\n projectId,\n useCdn: false,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n timeout: COOKIE_AUTH_TIMEOUT_MS,\n })\n const user = await client.request({\n uri: '/users/me',\n withCredentials: true,\n tag: 'users.get-current',\n })\n return user != null && typeof user === 'object' && typeof user.id === 'string'\n } catch {\n return false\n }\n}\n\n/**\n * Attempts to retrieve a studio token from local storage.\n * @param storageArea - The storage area to retrieve the token from.\n * @param storageKey - The storage key to retrieve the token from.\n * @returns The studio token or null if it does not exist.\n * @internal\n */\nexport function getStudioTokenFromLocalStorage(\n storageArea: Storage | undefined,\n storageKey: string | undefined,\n): string | null {\n if (!storageArea || !storageKey) return null\n const token = getTokenFromStorage(storageArea, storageKey)\n if (token) {\n return token\n }\n return null\n}\n","import {type Subscription} from 'rxjs'\n\nimport {type TokenSource} from '../config/sanityConfig'\nimport {type StoreContext} from '../store/defineStore'\nimport {AuthStateType} from './authStateType'\nimport {type AuthStoreState, type LoggedInAuthState} from './authStore'\nimport {type AuthStrategyOptions, type AuthStrategyResult} from './authStrategy'\nimport {refreshStampedToken} from './refreshStampedToken'\nimport {checkForCookieAuth, getStudioTokenFromLocalStorage} from './studioModeAuth'\nimport {subscribeToStateAndFetchCurrentUser} from './subscribeToStateAndFetchCurrentUser'\nimport {subscribeToStorageEventsAndSetToken} from './subscribeToStorageEventsAndSetToken'\nimport {createLoggedInAuthState, getDefaultStorage} from './utils'\n\n/**\n * Resolves the initial auth state for Studio mode.\n *\n * When a `tokenSource` is provided (via `config.studio.auth.token`), the\n * initial state is `LOGGING_IN` — the actual token arrives asynchronously\n * via the subscription set up in `initializeStudioAuth`.\n *\n * Fallback token discovery order (no tokenSource):\n * 1. Provided token (`auth.token` in config)\n * 2. localStorage: `__studio_auth_token_${projectId}`\n * 3. Falls back to `LOGGED_OUT` (cookie auth is checked async in `initializeStudioAuth`)\n *\n * @internal\n */\nexport function getStudioInitialState(options: AuthStrategyOptions): AuthStrategyResult {\n const {authConfig, projectId, tokenSource} = options\n const storageArea = authConfig.storageArea ?? getDefaultStorage()\n const studioStorageKey = `__studio_auth_token_${projectId ?? ''}`\n\n // When a reactive token source is available, start in LOGGING_IN state.\n // The actual token will arrive via subscription in initializeStudioAuth.\n if (tokenSource) {\n return {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: false},\n storageKey: studioStorageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext: {},\n }\n }\n\n // Fallback: discover token from config or localStorage\n const providedToken = authConfig.token\n\n // Check localStorage first — mirrors original authStore behavior where\n // the localStorage read always runs before the providedToken check.\n let authMethod: AuthStrategyResult['authMethod'] = undefined\n const token = getStudioTokenFromLocalStorage(storageArea, studioStorageKey)\n if (token) {\n authMethod = 'localstorage'\n }\n\n if (providedToken) {\n return {\n authState: createLoggedInAuthState(providedToken, null),\n storageKey: studioStorageKey,\n storageArea,\n authMethod,\n dashboardContext: {},\n }\n }\n\n if (token) {\n return {\n authState: createLoggedInAuthState(token, null),\n storageKey: studioStorageKey,\n storageArea,\n authMethod: 'localstorage',\n dashboardContext: {},\n }\n }\n\n // No token found — start logged out, cookie auth will be checked asynchronously\n return {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n storageKey: studioStorageKey,\n storageArea,\n authMethod: undefined,\n dashboardContext: {},\n }\n}\n\n/**\n * Initialize Studio auth subscriptions.\n *\n * When a `tokenSource` is available (reactive path), subscribes to it for\n * ongoing token sync. The Studio is the token authority — no independent\n * token refresh or cookie auth probing is needed.\n *\n * Fallback path (no tokenSource):\n * - Subscribe to state changes and fetch current user\n * - Subscribe to storage events for studio token key\n * - Check for cookie auth asynchronously if no token was found\n * - Start stamped token refresh\n *\n * @internal\n */\nexport function initializeStudioAuth(\n context: StoreContext<AuthStoreState>,\n tokenRefresherRunning: boolean,\n): {dispose: () => void; tokenRefresherStarted: boolean} {\n const tokenSource = context.instance.config.studio?.auth?.token\n\n // Reactive token path — Studio provides the token\n if (tokenSource) {\n return initializeWithTokenSource(context, tokenSource)\n }\n\n // Fallback path — discover token from localStorage/cookies\n return initializeWithFallback(context, tokenRefresherRunning)\n}\n\n/**\n * Subscribe to a reactive token source from the Studio workspace.\n *\n * When the token source emits a non-null token, the SDK uses it directly.\n * When it emits `null`, the behavior depends on the `authenticated` flag\n * from the Studio's workspace config:\n *\n * - `authenticated: true` — the Studio has already verified the user is\n * logged in (e.g. via cookie auth). The SDK treats the null token as\n * cookie-based auth and stays in the LOGGED_IN state.\n *\n * - `authenticated` absent/false — the user is genuinely not authenticated;\n * transition to LOGGED_OUT.\n *\n * No async cookie probing is needed here because this code path only runs\n * when a Studio provides SDKStudioContext, and the Studio's Workspace type\n * always includes `authenticated`. The async `checkForCookieAuth` fallback\n * remains in `initializeWithFallback` for the non-Studio path.\n */\nfunction initializeWithTokenSource(\n context: StoreContext<AuthStoreState>,\n tokenSource: TokenSource,\n): {dispose: () => void; tokenRefresherStarted: boolean} {\n const subscriptions: Subscription[] = []\n const studioAuthenticated = context.instance.config.studio?.authenticated === true\n\n // Subscribe to the current user fetcher — runs whenever auth state changes\n subscriptions.push(subscribeToStateAndFetchCurrentUser(context, {useProjectHostname: true}))\n\n // Subscribe to the Studio's token stream\n const tokenSub = tokenSource.subscribe({\n next: (token) => {\n const {state} = context\n if (token) {\n // Studio provided a real token — use it directly\n state.set('studioTokenSource', (prev) => ({\n options: {...prev.options, authMethod: undefined},\n authState: createLoggedInAuthState(token, null),\n }))\n } else if (studioAuthenticated) {\n // The Studio says the user is authenticated — null token means\n // cookie-based auth is in use. Stay logged in with cookie method.\n state.set('studioTokenSourceCookieAuth', (prev) => ({\n options: {...prev.options, authMethod: 'cookie'},\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? prev.authState\n : createLoggedInAuthState('', null),\n }))\n } else {\n // No token and Studio doesn't confirm authentication — logged out\n state.set('studioTokenSourceLoggedOut', (prev) => ({\n options: {...prev.options, authMethod: undefined},\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n }))\n }\n },\n })\n\n return {\n dispose: () => {\n tokenSub.unsubscribe()\n for (const subscription of subscriptions) {\n subscription.unsubscribe()\n }\n },\n // Studio handles token refresh — do not start the SDK's refresher\n tokenRefresherStarted: false,\n }\n}\n\n/**\n * Fallback initialization when no reactive token source is available.\n * Uses localStorage/cookie discovery (existing behavior).\n */\nfunction initializeWithFallback(\n context: StoreContext<AuthStoreState>,\n tokenRefresherRunning: boolean,\n): {dispose: () => void; tokenRefresherStarted: boolean} {\n const subscriptions: Subscription[] = []\n let startedRefresher = false\n\n subscriptions.push(subscribeToStateAndFetchCurrentUser(context, {useProjectHostname: true}))\n\n const storageArea = context.state.get().options?.storageArea\n if (storageArea) {\n subscriptions.push(subscribeToStorageEventsAndSetToken(context))\n }\n\n // If no token found during getInitialState, try cookie auth asynchronously\n try {\n const {instance, state} = context\n const token: string | null =\n state.get().authState?.type === AuthStateType.LOGGED_IN\n ? (state.get().authState as LoggedInAuthState).token\n : null\n\n if (!token) {\n const projectIdValue = instance.config.projectId\n const clientFactory = state.get().options.clientFactory\n checkForCookieAuth(projectIdValue, clientFactory).then((isCookieAuthEnabled) => {\n if (!isCookieAuthEnabled) return\n state.set('enableCookieAuth', (prev) => ({\n options: {...prev.options, authMethod: 'cookie'},\n authState:\n prev.authState.type === AuthStateType.LOGGED_IN\n ? prev.authState\n : createLoggedInAuthState('', null),\n }))\n })\n }\n } catch {\n // best-effort cookie detection\n }\n\n if (!tokenRefresherRunning) {\n startedRefresher = true\n subscriptions.push(refreshStampedToken(context))\n }\n\n return {\n dispose: () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe()\n }\n },\n tokenRefresherStarted: startedRefresher,\n }\n}\n","import {type ClientConfig, createClient, type SanityClient} from '@sanity/client'\nimport {type CurrentUser} from '@sanity/types'\n\nimport {type AuthConfig, type AuthProvider} from '../config/authConfig'\nimport {bindActionGlobally} from '../store/createActionBinder'\nimport {createStateSourceAction} from '../store/createStateSourceAction'\nimport {defineStore} from '../store/defineStore'\nimport {getStagingApiHost} from '../utils/getStagingApiHost'\nimport {resolveAuthMode} from './authMode'\nimport {AuthStateType} from './authStateType'\nimport {type AuthStrategyOptions} from './authStrategy'\nimport {getDashboardInitialState, initializeDashboardAuth} from './dashboardAuth'\nimport {getStandaloneInitialState, initializeStandaloneAuth} from './standaloneAuth'\nimport {getStudioInitialState, initializeStudioAuth} from './studioAuth'\nimport {createLoggedInAuthState, getCleanedUrl, getDefaultLocation} from './utils'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Represents the various states the authentication can be in.\n *\n * @public\n */\nexport type AuthState = LoggedInAuthState | LoggedOutAuthState | LoggingInAuthState | ErrorAuthState\n\n/**\n * Logged-in state from the auth state.\n * @public\n */\nexport type LoggedInAuthState = {\n type: AuthStateType.LOGGED_IN\n token: string\n currentUser: CurrentUser | null\n lastTokenRefresh?: number\n}\n\n/**\n * Logged-out state from the auth state.\n * @public\n */\nexport type LoggedOutAuthState = {type: AuthStateType.LOGGED_OUT; isDestroyingSession: boolean}\n\n/**\n * Logging-in state from the auth state.\n * @public\n */\nexport type LoggingInAuthState = {type: AuthStateType.LOGGING_IN; isExchangingToken: boolean}\n\n/**\n * Error state from the auth state.\n * @public\n */\nexport type ErrorAuthState = {type: AuthStateType.ERROR; error: unknown}\n\n/**\n * Represents the various states the authentication can be in.\n *\n * @public\n */\nexport interface DashboardContext {\n mode?: string\n env?: string\n orgId?: string\n}\n\n/**\n * The method of authentication used.\n * @internal\n */\nexport type AuthMethodOptions = 'localstorage' | 'cookie' | undefined\n\nlet tokenRefresherRunning = false\n\n/**\n * @public\n */\nexport interface AuthStoreState {\n authState: AuthState\n providers?: AuthProvider[]\n options: {\n initialLocationHref: string\n clientFactory: (config: ClientConfig) => SanityClient\n customProviders: AuthConfig['providers']\n storageKey: string\n storageArea: Storage | undefined\n apiHost: string | undefined\n loginUrl: string\n callbackUrl: string | undefined\n providedToken: string | undefined\n authMethod: AuthMethodOptions\n }\n dashboardContext?: DashboardContext\n}\n\n// ---------------------------------------------------------------------------\n// Store definition — thin orchestrator\n// ---------------------------------------------------------------------------\n\nexport const authStore = defineStore<AuthStoreState>({\n name: 'Auth',\n\n getInitialState(instance) {\n const {\n apiHost: configApiHost,\n callbackUrl,\n providers: customProviders,\n token: providedToken,\n clientFactory = createClient,\n initialLocationHref = getDefaultLocation(),\n } = instance.config.auth ?? {}\n\n const apiHost = configApiHost ?? getStagingApiHost()\n const authConfig = instance.config.auth ?? {}\n\n // Build login URL (used by standalone mode, but always computed for the\n // public `getLoginUrlState` accessor)\n let loginDomain = 'https://www.sanity.io'\n try {\n if (apiHost && new URL(apiHost).hostname.endsWith('.sanity.work')) {\n loginDomain = 'https://www.sanity.work'\n }\n } catch {\n /* empty */\n }\n const loginUrl = new URL('/login', loginDomain)\n loginUrl.searchParams.set('origin', getCleanedUrl(initialLocationHref))\n loginUrl.searchParams.set('type', 'stampedToken') // Token must be stamped to have an sid passed back\n loginUrl.searchParams.set('withSid', 'true')\n\n // Resolve auth mode and delegate to the appropriate strategy\n const mode = resolveAuthMode(instance.config, initialLocationHref)\n\n const strategyOptions: AuthStrategyOptions = {\n authConfig,\n projectId: instance.config.projectId,\n initialLocationHref,\n clientFactory,\n tokenSource: instance.config.studio?.auth?.token,\n }\n\n let result\n switch (mode) {\n case 'studio':\n result = getStudioInitialState(strategyOptions)\n break\n case 'dashboard':\n result = getDashboardInitialState(strategyOptions)\n break\n case 'standalone':\n result = getStandaloneInitialState(strategyOptions)\n break\n }\n\n return {\n authState: result.authState,\n dashboardContext: result.dashboardContext,\n options: {\n apiHost,\n loginUrl: loginUrl.toString(),\n callbackUrl,\n customProviders,\n providedToken,\n clientFactory,\n initialLocationHref,\n storageKey: result.storageKey,\n storageArea: result.storageArea,\n authMethod: result.authMethod,\n },\n }\n },\n\n initialize(context) {\n const initialLocationHref =\n context.state.get().options?.initialLocationHref ?? getDefaultLocation()\n const mode = resolveAuthMode(context.instance.config, initialLocationHref)\n\n let initResult\n switch (mode) {\n case 'studio':\n initResult = initializeStudioAuth(context, tokenRefresherRunning)\n break\n case 'dashboard':\n initResult = initializeDashboardAuth(context, tokenRefresherRunning)\n break\n case 'standalone':\n initResult = initializeStandaloneAuth(context, tokenRefresherRunning)\n break\n }\n\n if (initResult.tokenRefresherStarted) {\n tokenRefresherRunning = true\n }\n\n return initResult.dispose\n },\n})\n\n// ---------------------------------------------------------------------------\n// Public bound actions\n// ---------------------------------------------------------------------------\n\n/**\n * @public\n */\nexport const getCurrentUserState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state: {authState}}) =>\n authState.type === AuthStateType.LOGGED_IN ? authState.currentUser : null,\n ),\n)\n\n/**\n * @public\n */\nexport const getTokenState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state: {authState}}) =>\n authState.type === AuthStateType.LOGGED_IN ? authState.token : null,\n ),\n)\n\n/**\n * @internal\n */\nexport const getAuthMethodState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state: {options}}) => options.authMethod),\n)\n\n/**\n * @public\n */\nexport const getLoginUrlState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state: {options}}) => options.loginUrl),\n)\n\n/**\n * @public\n */\nexport const getAuthState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state: {authState}}) => authState),\n)\n\n/**\n * @public\n */\nexport const getDashboardOrganizationId = bindActionGlobally(\n authStore,\n createStateSourceAction(({state: {dashboardContext}}) => dashboardContext?.orgId),\n)\n\n/**\n * Returns a state source indicating if the SDK is running within a dashboard context.\n * @public\n */\nexport const getIsInDashboardState = bindActionGlobally(\n authStore,\n createStateSourceAction(\n ({state: {dashboardContext}}) =>\n // Check if dashboardContext exists and is not empty\n !!dashboardContext && Object.keys(dashboardContext).length > 0,\n ),\n)\n\n/**\n * Action to explicitly set the authentication token.\n * Used internally by the Comlink token refresh.\n * @internal\n */\nexport const setAuthToken = bindActionGlobally(authStore, ({state}, token: string | null) => {\n const currentAuthState = state.get().authState\n if (token) {\n // Update state only if the new token is different or currently logged out\n if (currentAuthState.type !== AuthStateType.LOGGED_IN || currentAuthState.token !== token) {\n const currentUser =\n currentAuthState.type === AuthStateType.LOGGED_IN ? currentAuthState.currentUser : null\n const preservedLastTokenRefresh =\n currentAuthState.type === AuthStateType.LOGGED_IN\n ? currentAuthState.lastTokenRefresh\n : undefined\n state.set('setToken', {\n authState: createLoggedInAuthState(token, currentUser, preservedLastTokenRefresh),\n })\n }\n } else {\n // Handle setting token to null (logging out)\n if (currentAuthState.type !== AuthStateType.LOGGED_OUT) {\n state.set('setToken', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n })\n }\n }\n})\n","import {type ClientConfig, createClient, type SanityClient} from '@sanity/client'\nimport {pick} from 'lodash-es'\n\nimport {getAuthMethodState, getTokenState} from '../auth/authStore'\nimport {\n type DocumentSource,\n isCanvasSource,\n isDatasetSource,\n isMediaLibrarySource,\n} from '../config/sanityConfig'\nimport {bindActionGlobally} from '../store/createActionBinder'\nimport {createStateSourceAction} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {getStagingApiHost} from '../utils/getStagingApiHost'\n\nconst DEFAULT_API_VERSION = '2024-11-12'\nconst DEFAULT_REQUEST_TAG_PREFIX = 'sanity.sdk'\n\ntype AllowedClientConfigKey =\n | 'useCdn'\n | 'token'\n | 'perspective'\n | 'apiHost'\n | 'proxy'\n | 'withCredentials'\n | 'timeout'\n | 'maxRetries'\n | 'dataset'\n | 'projectId'\n | 'requestTagPrefix'\n | 'useProjectHostname'\n\nconst allowedKeys = Object.keys({\n 'apiHost': null,\n 'useCdn': null,\n 'token': null,\n 'perspective': null,\n 'proxy': null,\n 'withCredentials': null,\n 'timeout': null,\n 'maxRetries': null,\n 'dataset': null,\n 'projectId': null,\n 'scope': null,\n 'apiVersion': null,\n 'requestTagPrefix': null,\n 'useProjectHostname': null,\n '~experimental_resource': null,\n 'source': null,\n} satisfies Record<keyof ClientOptions, null>) as (keyof ClientOptions)[]\n\nconst DEFAULT_CLIENT_CONFIG: ClientConfig = {\n apiVersion: DEFAULT_API_VERSION,\n useCdn: false,\n ignoreBrowserTokenWarning: true,\n allowReconfigure: false,\n requestTagPrefix: DEFAULT_REQUEST_TAG_PREFIX,\n}\n\n/**\n * States tracked by the client store\n * @public\n */\nexport interface ClientStoreState {\n token: string | null\n clients: {[TKey in string]?: SanityClient}\n authMethod?: 'localstorage' | 'cookie'\n}\n\ninterface ClientResource {\n type: 'dataset' | 'media-library' | 'canvas'\n id: string\n}\n\n/**\n * Options used when retrieving a client instance from the client store.\n *\n * This interface extends the base {@link ClientConfig} and adds:\n *\n * - **apiVersion:** A required string indicating the API version for the client.\n * - **scope:** An optional flag to choose between the project-specific client\n * ('project') and the global client ('global'). When set to `'global'`, the\n * global client is used.\n *\n * These options are utilized by `getClient` and `getClientState` to configure and\n * return appropriate client instances that automatically handle authentication\n * updates and configuration changes.\n *\n * @public\n */\nexport interface ClientOptions extends Pick<ClientConfig, AllowedClientConfigKey> {\n /**\n * An optional flag to choose between the default client (typically project-level)\n * and the global client ('global'). When set to `'global'`, the global client\n * is used.\n */\n 'scope'?: 'default' | 'global'\n /**\n * A required string indicating the API version for the client.\n */\n 'apiVersion': string\n /**\n * @internal\n */\n '~experimental_resource'?: ClientConfig['~experimental_resource']\n\n /**\n * @internal\n */\n 'source'?: DocumentSource\n}\n\nconst clientStore = defineStore<ClientStoreState>({\n name: 'clientStore',\n\n getInitialState: (instance) => ({\n clients: {},\n token: getTokenState(instance).getCurrent(),\n }),\n\n initialize(context) {\n const subscription = listenToToken(context)\n const authMethodSubscription = listenToAuthMethod(context)\n return () => {\n subscription.unsubscribe()\n authMethodSubscription.unsubscribe()\n }\n },\n})\n\n/**\n * Updates the client store state when a token is received.\n * @internal\n */\nconst listenToToken = ({instance, state}: StoreContext<ClientStoreState>) => {\n return getTokenState(instance).observable.subscribe((token) => {\n state.set('setTokenAndResetClients', {token, clients: {}})\n })\n}\n\nconst listenToAuthMethod = ({instance, state}: StoreContext<ClientStoreState>) => {\n return getAuthMethodState(instance).observable.subscribe((authMethod) => {\n state.set('setAuthMethod', {authMethod})\n })\n}\n\nconst getClientConfigKey = (options: ClientOptions) => JSON.stringify(pick(options, ...allowedKeys))\n\n/**\n * Retrieves a Sanity client instance configured with the provided options.\n *\n * This function returns a client instance configured for the project or as a\n * global client based on the options provided. It ensures efficient reuse of\n * client instances by returning the same instance for the same options.\n * For automatic handling of authentication token updates, consider using\n * `getClientState`.\n *\n * @public\n */\nexport const getClient = bindActionGlobally(\n clientStore,\n ({state, instance}, options: ClientOptions) => {\n if (!options || typeof options !== 'object') {\n throw new Error(\n 'getClient() requires a configuration object with at least an \"apiVersion\" property. ' +\n 'Example: getClient(instance, { apiVersion: \"2024-11-12\" })',\n )\n }\n\n // Check for disallowed keys\n const providedKeys = Object.keys(options) as (keyof ClientOptions)[]\n const disallowedKeys = providedKeys.filter((key) => !allowedKeys.includes(key))\n\n if (disallowedKeys.length > 0) {\n const listFormatter = new Intl.ListFormat('en', {style: 'long', type: 'conjunction'})\n throw new Error(\n `The client options provided contains unsupported properties: ${listFormatter.format(disallowedKeys)}. ` +\n `Allowed keys are: ${listFormatter.format(allowedKeys)}.`,\n )\n }\n\n const tokenFromState = state.get().token\n const {clients, authMethod} = state.get()\n\n let resource: ClientResource | undefined\n\n if (options.source) {\n if (isDatasetSource(options.source)) {\n resource = {type: 'dataset', id: `${options.source.projectId}.${options.source.dataset}`}\n } else if (isMediaLibrarySource(options.source)) {\n resource = {type: 'media-library', id: options.source.mediaLibraryId}\n } else if (isCanvasSource(options.source)) {\n resource = {type: 'canvas', id: options.source.canvasId}\n }\n }\n\n const projectId = options.projectId ?? instance.config.projectId\n const dataset = options.dataset ?? instance.config.dataset\n const apiHost = options.apiHost ?? instance.config.auth?.apiHost ?? getStagingApiHost()\n\n const effectiveOptions: ClientOptions = {\n ...DEFAULT_CLIENT_CONFIG,\n ...((options.scope === 'global' || !projectId || resource) && {useProjectHostname: false}),\n token: authMethod === 'cookie' ? undefined : (tokenFromState ?? undefined),\n ...options,\n ...(projectId && {projectId}),\n ...(dataset && {dataset}),\n ...(apiHost && {apiHost}),\n ...(resource && {'~experimental_resource': resource}),\n }\n\n // When a source is provided, don't use projectId/dataset - the client should be \"projectless\"\n // The client code itself will ignore the non-source config, so we do this to prevent confusing the user.\n // (ref: https://github.com/sanity-io/client/blob/5c23f81f5ab93a53f5b22b39845c867988508d84/src/data/dataMethods.ts#L691)\n if (resource) {\n if (options.projectId || options.dataset) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Both source and explicit projectId/dataset are provided. The source will be used and projectId/dataset will be ignored.',\n )\n }\n delete effectiveOptions.projectId\n delete effectiveOptions.dataset\n }\n\n if (effectiveOptions.token === null || typeof effectiveOptions.token === 'undefined') {\n delete effectiveOptions.token\n if (authMethod === 'cookie') {\n effectiveOptions.withCredentials = true\n }\n } else {\n delete effectiveOptions.withCredentials\n }\n\n const key = getClientConfigKey(effectiveOptions)\n\n if (clients[key]) return clients[key]\n\n const client = createClient(effectiveOptions)\n state.set('addClient', (prev) => ({clients: {...prev.clients, [key]: client}}))\n\n return client\n },\n)\n\n/**\n * Returns a state source for the Sanity client instance.\n *\n * This function provides a subscribable state source that emits updated client\n * instances whenever relevant configurations change (such as authentication tokens).\n * Use this when you need to react to client configuration changes in your application.\n *\n * @public\n */\nexport const getClientState = bindActionGlobally(\n clientStore,\n createStateSourceAction(({instance}, options: ClientOptions) => getClient(instance, options)),\n)\n","/**\n * Like `setTimeout`, but calls `.unref()` on the timer when running in Node.js.\n *\n * In Node.js, active timers prevent the process from exiting. Cleanup timers\n * (e.g. deferred subscription removal) should not keep the process alive -\n * `.unref()` lets the process exit naturally while still firing the timer if\n * the process happens to still be running.\n *\n * In browsers, `setTimeout` returns a number and has no `.unref()` method,\n * so this is a no-op there.\n */\nexport function setCleanupTimeout(fn: () => void, delay: number): ReturnType<typeof setTimeout> {\n const timer = setTimeout(fn, delay)\n\n // In Node.js, setTimeout returns an object with an `unref()` method.\n // In browsers, it returns a number. We assign to `unknown` and narrow\n // at runtime so this works in both environments without type assertions.\n const t: unknown = timer\n if (typeof t === 'object' && t !== null && 'unref' in t && typeof t.unref === 'function') {\n t.unref()\n }\n\n return timer\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 {omit} from 'lodash-es'\n\nimport {type SanityInstance} from '../store/createSanityInstance'\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: omit(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: omit(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 {type ReleaseDocument} from '../releasesStore'\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 // undecided are always first, then by createdAt descending\n if (a.metadata.releaseType === 'undecided' && b.metadata.releaseType !== 'undecided') {\n return -1\n }\n if (a.metadata.releaseType !== 'undecided' && b.metadata.releaseType === 'undecided') {\n return 1\n }\n if (a.metadata.releaseType === 'undecided' && b.metadata.releaseType === '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 (a.metadata.releaseType === 'scheduled' && b.metadata.releaseType === '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 (a.metadata.releaseType === 'asap' && b.metadata.releaseType !== 'asap') {\n return 1\n }\n if (a.metadata.releaseType !== 'asap' && b.metadata.releaseType === 'asap') {\n return -1\n }\n if (a.metadata.releaseType === 'asap' && b.metadata.releaseType === '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 SanityDocument} from '@sanity/types'\nimport {map} from 'rxjs'\n\nimport {type DocumentSource, isDatasetSource} from '../config/sanityConfig'\n/*\n * Although this is an import dependency cycle, it is not a logical cycle:\n * 1. releasesStore uses queryStore as a data source\n * 2. queryStore calls getPerspectiveState for computing release perspectives\n * 3. getPerspectiveState uses releasesStore as a data source\n * 4. however, queryStore does not use getPerspectiveState for the perspective used in releasesStore (\"raw\")\n */\n// eslint-disable-next-line import/no-cycle\nimport {getQueryState} from '../query/queryStore'\nimport {bindActionBySource, type BoundSourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {sortReleases} from './utils/sortReleases'\n\nconst ARCHIVED_RELEASE_STATES = ['archived', 'published']\nconst STABLE_EMPTY_RELEASES: ReleaseDocument[] = []\n\n/**\n * Represents a document in a Sanity dataset that represents release options.\n * @internal\n */\nexport type ReleaseDocument = SanityDocument & {\n name: string\n publishAt?: string\n state: 'active' | 'scheduled'\n metadata: {\n title: string\n releaseType: 'asap' | 'scheduled' | 'undecided'\n intendedPublishAt?: string\n description?: string\n }\n}\n\nexport interface ReleasesStoreState {\n activeReleases?: ReleaseDocument[]\n error?: unknown\n}\n\nexport const releasesStore = defineStore<ReleasesStoreState, BoundSourceKey>({\n name: 'Releases',\n getInitialState: (): ReleasesStoreState => ({\n activeReleases: 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 = bindActionBySource(\n releasesStore,\n createStateSourceAction({\n selector: ({state}, _?) => state.activeReleases,\n }),\n)\n\n/**\n * Get the active releases from the store.\n * @internal\n */\nexport const getActiveReleasesState = (\n instance: SanityInstance,\n options?: {source?: DocumentSource},\n): StateSource<ReleaseDocument[] | undefined> =>\n // bindActionBySource keyFn destructures { source } from the first param, so pass {} when no options\n _getActiveReleasesState(instance, options ?? {})\n\nconst RELEASES_QUERY = 'releases::all()'\n\nconst subscribeToReleases = ({\n instance,\n state,\n key: {source},\n}: StoreContext<ReleasesStoreState, BoundSourceKey>) => {\n const {observable: releases$} = getQueryState<ReleaseDocument[]>(instance, {\n query: RELEASES_QUERY,\n perspective: 'raw',\n source: source && !isDatasetSource(source) ? source : undefined,\n tag: 'releases',\n })\n return 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 state.set('setActiveReleases', {\n activeReleases: sortReleases(releases ?? STABLE_EMPTY_RELEASES)\n .filter((release) => !ARCHIVED_RELEASE_STATES.includes(release.state))\n .reverse(),\n })\n }),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n","import {createSelector} from 'reselect'\n\nimport {type DocumentSource, type PerspectiveHandle} from '../config/sanityConfig'\nimport {bindActionBySource, type BoundStoreAction} from '../store/createActionBinder'\nimport {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'\n/*\n * Although this is an import dependency cycle, it is not a logical cycle:\n * 1. getPerspectiveState uses releasesStore as a data source\n * 2. releasesStore uses queryStore as a data source\n * 3. queryStore calls getPerspectiveState for computing release perspectives\n * 4. however, queryStore does not use getPerspectiveState for the perspective used in releasesStore (\"raw\")\n */\n// eslint-disable-next-line import/no-cycle\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; source?: DocumentSource},\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\n// Lazily bind the action itself to avoid circular import initialization issues with `releasesStore`\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\nlet _boundGetPerspectiveState: BoundGetPerspectiveState | undefined\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 if (!_boundGetPerspectiveState) {\n _boundGetPerspectiveState = bindActionBySource(\n releasesStore,\n _getPerspectiveStateSelector,\n ) as BoundGetPerspectiveState\n }\n // bindActionBySource keyFn destructures { source } from the first param, so pass {} when no options\n return _boundGetPerspectiveState(instance, ...(rest.length ? rest : [{}]))\n}\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 {omit} from 'lodash-es'\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: omit(prev.queries, key)}\n return {...prev, queries: {...prev.queries, [key]: {...prevQuery, subscribers}}}\n }\n\nexport const cancelQuery =\n (key: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n const prevQuery = prev.queries[key]\n if (!prevQuery) return prev\n if (prevQuery.subscribers.length) return prev\n return {...prev, queries: omit(prev.queries, key)}\n }\n\nexport const initializeQuery =\n (key: string) =>\n (prev: QueryStoreState): QueryStoreState => {\n if (prev.queries[key]) return prev\n return {...prev, queries: {...prev.queries, [key]: {subscribers: []}}}\n }\n","import {CorsOriginError, type ResponseQueryOptions} from '@sanity/client'\nimport {type SanityQueryResult} from 'groq'\nimport {\n catchError,\n combineLatest,\n defer,\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 {type DatasetHandle, isDatasetSource} from '../config/sanityConfig'\n/*\n * Although this is an import dependency cycle, it is not a logical cycle:\n * 1. queryStore uses getPerspectiveState when resolving release perspectives\n * 2. getPerspectiveState uses releasesStore as a data source\n * 3. releasesStore uses queryStore as a data source\n * 4. however, queryStore does not use getPerspectiveState for the perspective used in releasesStore (\"raw\")\n */\n// eslint-disable-next-line import/no-cycle\nimport {getPerspectiveState} from '../releases/getPerspectiveState'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionBySource, type BoundSourceKey} 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 {insecureRandomId} 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 cancelQuery,\n initializeQuery,\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/** @beta */\nexport const getQueryKey = (options: QueryOptions): string => JSON.stringify(options)\n/** @beta */\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, BoundSourceKey>({\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 source,\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 }).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 source,\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 )\n }),\n catchError((error) => {\n state.set('setQueryError', setQueryError(group$.key, error))\n return EMPTY\n }),\n tap(({result, syncTags}) => {\n state.set('setQueryData', setQueryData(group$.key, result, syncTags))\n }),\n ),\n ),\n )\n .subscribe({error: errorHandler(state)})\n}\n\nconst listenToLiveClientAndSetLastLiveEventIds = ({\n state,\n instance,\n key: {source},\n}: StoreContext<QueryStoreState, BoundSourceKey>) => {\n const liveMessages$ = getClientState(instance, {\n apiVersion: QUERY_STORE_API_VERSION,\n // temporary guard here until we're ready for everything to be queried via global api\n ...(source && !isDatasetSource(source) ? {source} : {}),\n }).observable.pipe(\n switchMap((client) =>\n defer(() =>\n client.live.events({includeDrafts: !!client.config().token, tag: 'query-store'}),\n ).pipe(\n catchError((error) => {\n if (error instanceof CorsOriginError) {\n // Swallow only CORS errors in store without bubbling up so that they are handled by the Cors Error component\n state.set('setError', {error})\n return EMPTY\n }\n throw error\n }),\n ),\n ),\n share(),\n filter((e) => e.type === 'message'),\n )\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<SanityQueryResult<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 = bindActionBySource(\n queryStore,\n createStateSourceAction({\n selector: ({state, instance}: SelectorContext<QueryStoreState>, options: QueryOptions) => {\n if (state.error) throw state.error\n const key = getQueryKey(normalizeOptionsWithPerspective(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 = insecureRandomId()\n const key = getQueryKey(normalizeOptionsWithPerspective(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 * Unlike `getQueryState`, which registers subscribers to keep the query live and performs automatic cleanup,\n * `resolveQuery` does not track subscribers. This makes it ideal for use with React Suspense, where the returned\n * 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 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<SanityQueryResult<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 = bindActionBySource(\n queryStore,\n ({state, instance}, {signal, ...options}: ResolveQueryOptions) => {\n const normalized = normalizeOptionsWithPerspective(instance, options)\n const {getCurrent} = getQueryState(instance, normalized)\n const key = getQueryKey(normalized)\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 state.set('cancelQuery', cancelQuery(key))\n }\n throw error\n }),\n )\n : NEVER\n\n state.set('initializeQuery', initializeQuery(key))\n\n const resolved$ = state.observable.pipe(\n map(getCurrent),\n first((i) => i !== undefined),\n )\n\n return firstValueFrom(race([resolved$, aborted$]))\n },\n)\n","/**\n * The fields to check for a title.\n * The order of the items in the array defines the priority.\n *\n * @internal\n */\nexport const TITLE_CANDIDATES = ['title', 'name', 'label', 'heading', 'header', 'caption']\n\n/**\n * The fields to check for a subtitle.\n * The order of the items in the array defines the priority.\n *\n * @internal\n */\nexport const SUBTITLE_CANDIDATES = ['description', 'subtitle', ...TITLE_CANDIDATES]\n\n/**\n * Generates a GROQ projection for preview data without requiring a schema.\n * Uses common field names to make educated guesses about which fields to use.\n *\n * @internal\n */\nexport const PREVIEW_PROJECTION = `{\n // Get all potential title fields\n \"titleCandidates\": {\n ${TITLE_CANDIDATES.map((field) => `\"${field}\": ${field}`).join(',\\n ')}\n },\n // Get all potential subtitle fields\n \"subtitleCandidates\": {\n ${SUBTITLE_CANDIDATES.map((field) => `\"${field}\": ${field}`).join(',\\n ')}\n },\n \"media\": coalesce(\n select(\n defined(asset) => {\"type\": \"image-asset\", \"_ref\": asset._ref},\n defined(image.asset) => {\"type\": \"image-asset\", \"_ref\": image.asset._ref},\n defined(mainImage.asset) => {\"type\": \"image-asset\", \"_ref\": mainImage.asset._ref},\n null\n )\n ),\n _type,\n _id,\n _updatedAt\n}`\n","import {type SanityClient} from '@sanity/client'\nimport {createImageUrlBuilder} from '@sanity/image-url'\nimport {isObject} from 'lodash-es'\n\nimport {getClient} from '../client/clientStore'\nimport {type DocumentSource, isDatasetSource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {SUBTITLE_CANDIDATES, TITLE_CANDIDATES} from './previewConstants'\nimport {type PreviewQueryResult, type PreviewValue} from './types'\n\nconst API_VERSION = 'v2025-05-06'\n\n/**\n * Checks if the provided value has `_ref` property that is a string and starts with `image-`\n */\nfunction hasImageRef<T>(value: unknown): value is T & {_ref: string} {\n return isObject(value) && '_ref' in value && typeof (value as {_ref: unknown})._ref === 'string'\n}\n\n/**\n * Normalizes a media asset to a preview value.\n * Adds a url to a media asset reference using `@sanity/image-url`.\n *\n * @internal\n */\nexport function normalizeMedia(media: unknown, client: SanityClient): PreviewValue['media'] {\n if (!media) return null\n if (!hasImageRef(media)) return null\n\n const builder = createImageUrlBuilder(client)\n const url = builder.image({_ref: media._ref}).url()\n\n return {\n type: 'image-asset',\n _ref: media._ref,\n url,\n }\n}\n\n/**\n * Finds a single field value from a set of candidates based on a priority list of field names.\n * Returns the first non-empty string value found from the candidates matching the priority list order.\n *\n * @internal\n */\nfunction findFirstDefined(\n fieldsToSearch: string[],\n candidates: Record<string, unknown>,\n exclude?: unknown,\n): string | undefined {\n if (!candidates) return undefined\n\n for (const field of fieldsToSearch) {\n const value = candidates[field]\n if (typeof value === 'string' && value.trim() !== '' && value !== exclude) {\n return value\n }\n }\n\n return undefined\n}\n\n/**\n * Transforms a projection result (with titleCandidates, subtitleCandidates, media)\n * into a PreviewValue (with title, subtitle, media).\n *\n * @param projectionResult - The raw projection result from GROQ\n * @param instance - The Sanity instance to use for client configuration\n * @param source - Data source for the preview\n * @internal\n */\nexport function transformProjectionToPreview(\n instance: SanityInstance,\n projectionResult: PreviewQueryResult,\n source?: DocumentSource,\n): PreviewValue {\n const title = findFirstDefined(TITLE_CANDIDATES, projectionResult.titleCandidates)\n const subtitle = findFirstDefined(SUBTITLE_CANDIDATES, projectionResult.subtitleCandidates, title)\n\n // Get a client for the source (if provided) or use the instance config\n const client = getClient(instance, {\n apiVersion: API_VERSION,\n // TODO: remove in v3 when we're ready for everything to be queried via source\n source: source && !isDatasetSource(source) ? source : undefined,\n })\n\n return {\n title: String(title || `${projectionResult._type}: ${projectionResult._id}`),\n subtitle: subtitle || undefined,\n media: normalizeMedia(projectionResult.media, client),\n ...(projectionResult._status && {_status: projectionResult._status}),\n }\n}\n","const WILDCARD_TOKEN = '*'\nconst NEGATION_TOKEN = '-'\n// This regex handles simple cases including quoted phrases.\n// More complex query syntaxes might need a more robust parser.\nconst TOKEN_REGEX = /(?:[^\\s\"]+|\"[^\"]*\")+/g\n\n/**\n * @internal\n * Checks if a token starts with the negation character.\n */\nfunction isNegationToken(token: string | undefined): boolean {\n return typeof token !== 'undefined' && token.trim().startsWith(NEGATION_TOKEN)\n}\n\n/**\n * @internal\n * Checks if a token ends with the wildcard character.\n */\nfunction isPrefixToken(token: string | undefined): boolean {\n return typeof token !== 'undefined' && token.trim().endsWith(WILDCARD_TOKEN)\n}\n\n/**\n * @internal\n * Checks if a token is enclosed in double quotes.\n */\nfunction isExactMatchToken(token: string | undefined): boolean {\n // Ensure the token exists, has at least 2 characters, and starts/ends with \"\n return !!token && token.length >= 2 && token.startsWith('\"') && token.endsWith('\"')\n}\n\n/**\n * Creates a GROQ search filter string (`[@] match text::query(\"...\")`)\n * from a raw search query string.\n *\n * It applies wildcard ('*') logic to the last eligible token and escapes\n * double quotes within the search term.\n *\n * If the input query is empty or only whitespace, it returns an empty string.\n *\n * @param query - The raw input search string.\n * @returns The GROQ search filter string, or an empty string.\n * @internal\n */\nexport function createGroqSearchFilter(query: string): string {\n // Trim leading/trailing whitespace from the overall query first\n const trimmedQuery = query.trim()\n if (!trimmedQuery) {\n return '' // Return empty if query is empty or just whitespace\n }\n\n // Extract tokens using the regex\n const tokens = trimmedQuery.match(TOKEN_REGEX) ?? []\n\n // Find the index of the last token eligible for wildcard appending\n const reversedTokens = [...tokens].reverse()\n const reversedIndex = reversedTokens.findIndex(\n (token: string) => !isNegationToken(token) && !isExactMatchToken(token),\n )\n const finalIncrementalTokenIndex = reversedIndex === -1 ? -1 : tokens.length - 1 - reversedIndex\n\n // Get the actual token based on the found index\n const finalIncrementalToken = tokens[finalIncrementalTokenIndex]\n\n const processedTokens = [...tokens]\n // If a suitable token was found and it doesn't already end with a wildcard,\n // apply the wildcard.\n if (finalIncrementalToken !== undefined && !isPrefixToken(finalIncrementalToken)) {\n // Replace the identified token with its wildcarded version\n processedTokens.splice(\n finalIncrementalTokenIndex,\n 1,\n `${finalIncrementalToken}${WILDCARD_TOKEN}`,\n )\n }\n\n // Join the tokens back into a space-separated string\n const wildcardSearch = processedTokens.join(' ')\n\n // Escape double quotes within the final search term for the GROQ query\n const escapedSearch = wildcardSearch.replace(/\"/g, '\\\\\"')\n\n // Construct the final GROQ filter clause\n return `[@] match text::query(\"${escapedSearch}\")`\n}\n"],"names":["DEFAULT_API_VERSION","AuthStateType","tokenRefresherRunning","key","timer","API_VERSION"],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createBatchedStore } from "@sanity/telemetry";
|
|
2
|
+
import { createLogger } from "./createGroqSearchFilter.js";
|
|
3
|
+
import { CORE_SDK_VERSION } from "./version.js";
|
|
4
|
+
import { SDKDevSessionEnded, SDKDevError, SDKHookMounted, SDKDevSessionStarted } from "./_internal.js";
|
|
5
|
+
const FLUSH_INTERVAL_MS = 3e4, CONSENT_TAG = "telemetry-consent.sdk", BATCH_TAG = "telemetry.batch", log = createLogger("telemetry");
|
|
6
|
+
function createTelemetryManager(options) {
|
|
7
|
+
const { sessionId, getClient, projectId } = options, startedAt = Date.now(), emittedHooks = /* @__PURE__ */ new Set();
|
|
8
|
+
let cachedConsent = null;
|
|
9
|
+
const resolveConsent = async () => {
|
|
10
|
+
if (cachedConsent) return cachedConsent;
|
|
11
|
+
try {
|
|
12
|
+
cachedConsent = await getClient().request({
|
|
13
|
+
uri: "/intake/telemetry-status",
|
|
14
|
+
tag: CONSENT_TAG
|
|
15
|
+
});
|
|
16
|
+
} catch {
|
|
17
|
+
cachedConsent = { status: "undetermined" };
|
|
18
|
+
}
|
|
19
|
+
return cachedConsent;
|
|
20
|
+
}, enrichBatch = (batch) => batch.map((event) => ({
|
|
21
|
+
...event,
|
|
22
|
+
context: {
|
|
23
|
+
version: CORE_SDK_VERSION,
|
|
24
|
+
environment: "development",
|
|
25
|
+
origin: typeof window < "u" ? window.location.origin : "node"
|
|
26
|
+
}
|
|
27
|
+
})), store = createBatchedStore(
|
|
28
|
+
sessionId,
|
|
29
|
+
{
|
|
30
|
+
flushInterval: FLUSH_INTERVAL_MS,
|
|
31
|
+
resolveConsent,
|
|
32
|
+
sendEvents: async (batch) => {
|
|
33
|
+
const client = getClient();
|
|
34
|
+
return log.debug("sending event batch", { batchSize: batch.length }), client.request({
|
|
35
|
+
uri: "/intake/batch",
|
|
36
|
+
method: "POST",
|
|
37
|
+
body: { projectId, batch: enrichBatch(batch) },
|
|
38
|
+
tag: BATCH_TAG
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
), logger = store.logger;
|
|
43
|
+
return {
|
|
44
|
+
async checkConsent() {
|
|
45
|
+
const { status } = await resolveConsent();
|
|
46
|
+
return status === "granted";
|
|
47
|
+
},
|
|
48
|
+
logSessionStarted(data) {
|
|
49
|
+
log.debug("event: SDK Dev Session Started", {
|
|
50
|
+
projectId: data.projectId,
|
|
51
|
+
perspective: data.perspective,
|
|
52
|
+
authMethod: data.authMethod,
|
|
53
|
+
version: CORE_SDK_VERSION
|
|
54
|
+
}), logger.log(SDKDevSessionStarted, {
|
|
55
|
+
version: CORE_SDK_VERSION,
|
|
56
|
+
...data
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
logHookFirstUsed(hookName) {
|
|
60
|
+
emittedHooks.has(hookName) || (emittedHooks.add(hookName), log.debug("event: SDK Hook Mounted", { hookName }), logger.log(SDKHookMounted, { hookName }));
|
|
61
|
+
},
|
|
62
|
+
logDevError(errorType, hookName) {
|
|
63
|
+
log.debug("event: SDK Dev Error", { errorType, hookName }), logger.log(SDKDevError, { errorType, hookName });
|
|
64
|
+
},
|
|
65
|
+
endSession() {
|
|
66
|
+
const durationSeconds = Math.round((Date.now() - startedAt) / 1e3);
|
|
67
|
+
log.debug("event: SDK Dev Session Ended", {
|
|
68
|
+
durationSeconds,
|
|
69
|
+
hooksUsed: [...emittedHooks]
|
|
70
|
+
}), logger.log(SDKDevSessionEnded, {
|
|
71
|
+
durationSeconds,
|
|
72
|
+
hooksUsed: [...emittedHooks]
|
|
73
|
+
}), store.flush().catch(() => {
|
|
74
|
+
}), store.end();
|
|
75
|
+
},
|
|
76
|
+
dispose() {
|
|
77
|
+
store.end();
|
|
78
|
+
},
|
|
79
|
+
get hooksUsed() {
|
|
80
|
+
return emittedHooks;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
createTelemetryManager
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=telemetryManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetryManager.js","sources":["../../src/telemetry/telemetryManager.ts"],"sourcesContent":["import {type SanityClient} from '@sanity/client'\nimport {\n type ConsentStatus,\n createBatchedStore,\n type SessionId,\n type TelemetryEvent,\n type TelemetryLogger,\n type TelemetryStore,\n} from '@sanity/telemetry'\n\nimport {createLogger} from '../utils/logger'\nimport {CORE_SDK_VERSION} from '../version'\nimport {\n SDKDevError,\n SDKDevSessionEnded,\n SDKDevSessionStarted,\n SDKHookMounted,\n} from './__telemetry__/sdk.telemetry'\n\nconst FLUSH_INTERVAL_MS = 30_000\nconst CONSENT_TAG = 'telemetry-consent.sdk'\nconst BATCH_TAG = 'telemetry.batch'\n\nconst log = createLogger('telemetry')\n\n/**\n * Manages dev-mode telemetry for a single SDK instance.\n *\n * Wraps `@sanity/telemetry`'s batched store with SDK-specific concerns:\n * consent caching, session lifecycle events, and hook usage tracking.\n *\n * @internal\n */\nexport interface TelemetryManager {\n /**\n * Eagerly resolve and cache the user's consent status.\n * Returns true only when the user has explicitly opted in (`granted`).\n * Call this before logging any events to avoid buffering events that\n * will be dropped on the first flush.\n */\n checkConsent(): Promise<boolean>\n\n /** Log a \"SDK Dev Session Started\" event */\n logSessionStarted(data: {projectId: string; perspective: string; authMethod: string}): void\n\n /** Log a \"SDK Hook First Used\" event (deduplicated per hook name) */\n logHookFirstUsed(hookName: string): void\n\n /** Log a \"SDK Dev Error\" event */\n logDevError(errorType: string, hookName: string): void\n\n /** Log a \"SDK Dev Session Ended\" event and tear down the store */\n endSession(): void\n\n /** Tear down the store without logging a session-end event */\n dispose(): void\n\n /** The set of hook names used during this session */\n readonly hooksUsed: ReadonlySet<string>\n}\n\ninterface TelemetryManagerOptions {\n sessionId: string\n getClient: () => SanityClient\n projectId: string\n}\n\n/**\n * Creates a telemetry manager for a single SDK instance session.\n *\n * The manager initializes a `createBatchedStore` from `@sanity/telemetry`,\n * caches the consent check for the lifetime of the session, and provides\n * typed methods for each SDK telemetry event.\n *\n * @internal\n */\nexport function createTelemetryManager(options: TelemetryManagerOptions): TelemetryManager {\n const {sessionId, getClient, projectId} = options\n const startedAt = Date.now()\n const emittedHooks = new Set<string>()\n\n let cachedConsent: {status: ConsentStatus} | null = null\n\n const resolveConsent = async (): Promise<{status: ConsentStatus}> => {\n if (cachedConsent) return cachedConsent\n try {\n const client = getClient()\n const result = await client.request<{status: ConsentStatus}>({\n uri: '/intake/telemetry-status',\n tag: CONSENT_TAG,\n })\n cachedConsent = result\n } catch {\n cachedConsent = {status: 'undetermined'}\n }\n return cachedConsent\n }\n\n const enrichBatch = (batch: TelemetryEvent[]) =>\n batch.map((event) => ({\n ...event,\n context: {\n version: CORE_SDK_VERSION,\n environment: 'development' as const,\n origin: typeof window !== 'undefined' ? window.location.origin : 'node',\n },\n }))\n\n const sendEvents = async (batch: TelemetryEvent[]): Promise<unknown> => {\n const client = getClient()\n log.debug('sending event batch', {batchSize: batch.length})\n return client.request({\n uri: '/intake/batch',\n method: 'POST',\n body: {projectId, batch: enrichBatch(batch)},\n tag: BATCH_TAG,\n })\n }\n\n const store: TelemetryStore<Record<string, unknown>> = createBatchedStore(\n sessionId as SessionId,\n {\n flushInterval: FLUSH_INTERVAL_MS,\n resolveConsent,\n sendEvents,\n },\n )\n\n const logger: TelemetryLogger<Record<string, unknown>> = store.logger\n\n return {\n async checkConsent() {\n const {status} = await resolveConsent()\n return status === 'granted'\n },\n\n logSessionStarted(data) {\n log.debug('event: SDK Dev Session Started', {\n projectId: data.projectId,\n perspective: data.perspective,\n authMethod: data.authMethod,\n version: CORE_SDK_VERSION,\n })\n logger.log(SDKDevSessionStarted, {\n version: CORE_SDK_VERSION,\n ...data,\n })\n },\n\n logHookFirstUsed(hookName: string) {\n if (emittedHooks.has(hookName)) return\n emittedHooks.add(hookName)\n log.debug('event: SDK Hook Mounted', {hookName})\n logger.log(SDKHookMounted, {hookName})\n },\n\n logDevError(errorType: string, hookName: string) {\n log.debug('event: SDK Dev Error', {errorType, hookName})\n logger.log(SDKDevError, {errorType, hookName})\n },\n\n endSession() {\n const durationSeconds = Math.round((Date.now() - startedAt) / 1000)\n log.debug('event: SDK Dev Session Ended', {\n durationSeconds,\n hooksUsed: [...emittedHooks],\n })\n logger.log(SDKDevSessionEnded, {\n durationSeconds,\n hooksUsed: [...emittedHooks],\n })\n\n store.flush().catch(() => {\n // Best-effort flush on dispose; swallow errors\n })\n store.end()\n },\n\n dispose() {\n store.end()\n },\n\n get hooksUsed(): ReadonlySet<string> {\n return emittedHooks\n },\n }\n}\n"],"names":[],"mappings":";;;;AAmBA,MAAM,oBAAoB,KACpB,cAAc,yBACd,YAAY,mBAEZ,MAAM,aAAa,WAAW;AAqD7B,SAAS,uBAAuB,SAAoD;AACzF,QAAM,EAAC,WAAW,WAAW,UAAA,IAAa,SACpC,YAAY,KAAK,IAAA,GACjB,eAAe,oBAAI,IAAA;AAEzB,MAAI,gBAAgD;AAEpD,QAAM,iBAAiB,YAA8C;AACnE,QAAI,cAAe,QAAO;AAC1B,QAAI;AAMF,sBAJe,MADA,UAAA,EACa,QAAiC;AAAA,QAC3D,KAAK;AAAA,QACL,KAAK;AAAA,MAAA,CACN;AAAA,IAEH,QAAQ;AACN,sBAAgB,EAAC,QAAQ,eAAA;AAAA,IAC3B;AACA,WAAO;AAAA,EACT,GAEM,cAAc,CAAC,UACnB,MAAM,IAAI,CAAC,WAAW;AAAA,IACpB,GAAG;AAAA,IACH,SAAS;AAAA,MACP,SAAS;AAAA,MACT,aAAa;AAAA,MACb,QAAQ,OAAO,SAAW,MAAc,OAAO,SAAS,SAAS;AAAA,IAAA;AAAA,EACnE,EACA,GAaE,QAAiD;AAAA,IACrD;AAAA,IACA;AAAA,MACE,eAAe;AAAA,MACf;AAAA,MACA,YAhBe,OAAO,UAA8C;AACtE,cAAM,SAAS,UAAA;AACf,eAAA,IAAI,MAAM,uBAAuB,EAAC,WAAW,MAAM,OAAA,CAAO,GACnD,OAAO,QAAQ;AAAA,UACpB,KAAK;AAAA,UACL,QAAQ;AAAA,UACR,MAAM,EAAC,WAAW,OAAO,YAAY,KAAK,EAAA;AAAA,UAC1C,KAAK;AAAA,QAAA,CACN;AAAA,MACH;AAAA,IAAA;AAAA,EAQE,GAGI,SAAmD,MAAM;AAE/D,SAAO;AAAA,IACL,MAAM,eAAe;AACnB,YAAM,EAAC,WAAU,MAAM,eAAA;AACvB,aAAO,WAAW;AAAA,IACpB;AAAA,IAEA,kBAAkB,MAAM;AACtB,UAAI,MAAM,kCAAkC;AAAA,QAC1C,WAAW,KAAK;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,YAAY,KAAK;AAAA,QACjB,SAAS;AAAA,MAAA,CACV,GACD,OAAO,IAAI,sBAAsB;AAAA,QAC/B,SAAS;AAAA,QACT,GAAG;AAAA,MAAA,CACJ;AAAA,IACH;AAAA,IAEA,iBAAiB,UAAkB;AAC7B,mBAAa,IAAI,QAAQ,MAC7B,aAAa,IAAI,QAAQ,GACzB,IAAI,MAAM,2BAA2B,EAAC,SAAA,CAAS,GAC/C,OAAO,IAAI,gBAAgB,EAAC,UAAS;AAAA,IACvC;AAAA,IAEA,YAAY,WAAmB,UAAkB;AAC/C,UAAI,MAAM,wBAAwB,EAAC,WAAW,SAAA,CAAS,GACvD,OAAO,IAAI,aAAa,EAAC,WAAW,UAAS;AAAA,IAC/C;AAAA,IAEA,aAAa;AACX,YAAM,kBAAkB,KAAK,OAAO,KAAK,IAAA,IAAQ,aAAa,GAAI;AAClE,UAAI,MAAM,gCAAgC;AAAA,QACxC;AAAA,QACA,WAAW,CAAC,GAAG,YAAY;AAAA,MAAA,CAC5B,GACD,OAAO,IAAI,oBAAoB;AAAA,QAC7B;AAAA,QACA,WAAW,CAAC,GAAG,YAAY;AAAA,MAAA,CAC5B,GAED,MAAM,MAAA,EAAQ,MAAM,MAAM;AAAA,MAE1B,CAAC,GACD,MAAM,IAAA;AAAA,IACR;AAAA,IAEA,UAAU;AACR,YAAM,IAAA;AAAA,IACR;AAAA,IAEA,IAAI,YAAiC;AACnC,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sources":["../../src/version.ts"],"sourcesContent":["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"],"names":[],"mappings":";;AAOO,MAAM,mBAAmB,OAAO,aAAa,KAAK,GAAG,OAAO;"}
|