@sanity/sdk 3.12.0 → 3.13.0-next.20261106104949
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_exports/_internal.d.ts +11 -0
- package/dist/_exports/_internal.d.ts.map +1 -1
- package/dist/_exports/_internal.js +271 -6
- package/dist/_exports/_internal.js.map +1 -1
- package/dist/index.js +6 -515
- package/dist/index.js.map +1 -1
- package/dist/sentryBrowser-DT6hL7NZ.js +1 -0
- package/dist/version-DRnj2zr5.js +1072 -0
- package/dist/version-DRnj2zr5.js.map +1 -0
- package/package.json +3 -2
- package/src/_exports/_internal.ts +1 -0
- package/src/errorReporting/errorReporter.test.ts +207 -0
- package/src/errorReporting/errorReporter.ts +223 -0
- package/src/errorReporting/isErrorReportingEnabled.test.ts +67 -0
- package/src/errorReporting/isErrorReportingEnabled.ts +47 -0
- package/src/errorReporting/isKnownSdkError.test.ts +57 -0
- package/src/errorReporting/isKnownSdkError.ts +67 -0
- package/src/errorReporting/privacy.test.ts +67 -0
- package/src/errorReporting/privacy.ts +61 -0
- package/src/errorReporting/sentryBrowser.ts +10 -0
- package/src/errorReporting/sentryTypes.ts +7 -0
- package/dist/version-DfqE7Tzw.js +0 -559
- package/dist/version-DfqE7Tzw.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_internal.js","names":["\"NODE_ENV\""],"sources":["../../src/comments/legacy/commentsOptions.ts","../../src/applications/applicationOrigin.ts","../../src/comments/commentsOptions.ts","../../src/dashboard/messageBus/workerStore.ts","../../src/favorites/busFavorites.ts","../../src/preview/previewConstants.ts","../../src/preview/previewProjectionUtils.ts","../../src/telemetry/environment.ts","../../src/telemetry/events.ts","../../src/telemetry/telemetryManager.ts","../../src/telemetry/initTelemetry.ts","../../src/utils/createGroqSearchFilter.ts"],"sourcesContent":["// fallow-ignore-file code-duplication -- deprecated add-on dataset comments, kept alongside comments/ until removal in the next major\nimport {type Path} from '@sanity/types'\n\nimport {type DocumentHandle} from '../../config/sanityConfig'\nimport {toCommentFieldPath} from '../commentFieldPath'\nimport {type CommentStatus} from './types'\n\n/**\n * Which comments to read.\n * @beta\n * @deprecated Comments have moved to the organization collaboration API. Import from\n * `@sanity/sdk/collaboration`; this add-on dataset API is removed in the next major.\n */\nexport interface CommentsOptions extends DocumentHandle {\n /**\n * Narrow to one field. Omit to get every comment on the document.\n */\n fieldPath?: string | Path\n /** Narrow to open or resolved threads. Omit for both. */\n status?: CommentStatus\n}\n\n/**\n * @beta\n * @deprecated Comments have moved to the organization collaboration API. Import from\n * `@sanity/sdk/collaboration`; this add-on dataset API is removed in the next major.\n */\nexport interface ResolveCommentsOptions extends CommentsOptions {\n signal?: AbortSignal\n}\n\n/**\n * Fields on {@link CommentsOptions} that are deliberately left out of the key\n * below, because neither changes which comments an option set addresses.\n *\n * `source` is the deprecated alias for `resource` and is folded into it before\n * a key is ever built, so keying on both would give one list two keys.\n * `liveEdit` describes the document rather than its comments, which hang off\n * the published id whether or not drafts exist.\n */\ntype CommentsKeyIrrelevantField = 'source' | 'liveEdit'\n\n/**\n * A stable string standing for one set of read options.\n *\n * Only React needs this: it holds one state source steady across renders and\n * defers swapping to a new one while the previous list is still on screen.\n * `fieldPath` is normalised on the way in, so a path array and the equivalent\n * string address the same list.\n *\n * Kept apart from the store it keys, because React imports this to memoize and\n * pulling the store in with it would put both comment stores into every bundle\n * that reads a comment.\n *\n * @internal\n */\nexport function getCommentsOptionsKey(options: CommentsOptions): string {\n return JSON.stringify({\n documentId: options.documentId,\n documentType: options.documentType,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n fieldPath: options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n status: options.status,\n // The `Record` half makes a new field on `CommentsOptions` a compile\n // error here unless it is listed above or named as irrelevant. Left to\n // `satisfies CommentsOptions` alone, a forgotten field would just be\n // absent from the key, and a reader would keep the list it had while the\n // caller thought it had asked for a different one.\n } satisfies CommentsOptions &\n Record<Exclude<keyof CommentsOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseCommentsOptionsKey(key: string): CommentsOptions {\n return JSON.parse(key) as CommentsOptions\n}\n","import {type ApplicationBase} from './applications'\n\ntype SanityGlobal = typeof globalThis & {__SANITY_STAGING__?: boolean}\n\n/**\n * The origin an application is served from, or `null` when it has neither an external URL nor\n * a slug to derive one from. Callers treat `null` as \"nothing to load\" rather than failing the\n * whole list on one bad record.\n *\n * @internal\n */\nexport function getApplicationOrigin(\n application: Pick<ApplicationBase, 'externalUrl' | 'slug' | 'isSingleton' | 'organizationId'>,\n): string | null {\n if (application.externalUrl !== null) return new URL(application.externalUrl).origin\n if (application.slug === null) return null\n\n // Read at runtime, not via a bundler define: a remote is built once and runs in whichever\n // host page loaded it, and the host sets this flag. Mirrors workbench's `getSanityEnv`.\n const staging = (globalThis as SanityGlobal).__SANITY_STAGING__ === true\n if (application.isSingleton) {\n const domain = staging ? 'run.sanity.work' : 'sanity.run'\n return `https://${application.slug}-apps-${application.organizationId}.${domain}`\n }\n\n const domain = staging ? 'studio.sanity.work' : 'sanity.studio'\n return `https://${application.slug}.${domain}`\n}\n","import {type QueryParams} from '@sanity/client'\nimport {type Path} from '@sanity/types'\n\nimport {type DatasetHandle, type DocumentHandle} from '../config/sanityConfig'\nimport {toCommentFieldPath} from './commentFieldPath'\nimport {type CommentStatus} from './types'\n\n/**\n * Which of a document's comments a read covers.\n *\n * @beta\n */\nexport type CommentsReadScope = 'perspective' | 'exact' | 'all'\n\n/**\n * Which of a document's comments to read.\n * @beta\n */\nexport interface CommentsOptions extends DocumentHandle {\n /** Narrow to one field. Omit to get every comment on the document. */\n fieldPath?: string | Path\n /** Narrow to open or resolved threads. Omit for both. */\n status?: CommentStatus\n /**\n * Which of the document's comments to read.\n *\n * - `'perspective'` follows what you are viewing: a release shows that\n * release's comments, anything else pools draft and published.\n * - `'exact'` matches only the precise document id passed.\n * - `'all'` returns every comment on the document.\n *\n * `'perspective'` recognises a release from the `{releaseName}` form. A\n * stacked `ClientPerspective` array names several layers at once, with no\n * single release to read comments from, so it pools draft and published as\n * any other non-release perspective does. To read the pooled list while a\n * release is in view, pass a non-release `perspective` alongside; to name a\n * release yourself, pass a version id as the `documentId`.\n *\n * A content variant is stored as a version document, so under\n * `'perspective'` its comments form a list of their own, the way a release's\n * do. A variant's draft and published comments are separate lists: pairing\n * them needs the variant's name, which its document id does not carry.\n *\n * @defaultValue 'perspective'\n */\n scope?: CommentsReadScope\n}\n\n/** @beta */\nexport interface ResolveCommentsOptions extends CommentsOptions {\n signal?: AbortSignal\n}\n\n/**\n * An arbitrary comment query, for anything that is not \"comments on this\n * document\" — cross-document views, per-user views, organization-wide activity.\n *\n * @beta\n */\nexport interface CommentsQueryOptions extends DatasetHandle {\n /**\n * GROQ filter applied to comment documents. `_type == \"sanity.comment\"` is\n * added for you, so this only has to say which comments.\n *\n * It is evaluated against every comment the organization can read. The\n * resource picks the organization but does not narrow the results; add a\n * `target.document._ref` prefix condition to keep to one dataset.\n */\n filter: string\n params?: QueryParams\n}\n\n/** @beta */\nexport interface ResolveCommentsQueryOptions extends CommentsQueryOptions {\n signal?: AbortSignal\n}\n\n/**\n * Fields on the read option types that are deliberately left out of the keys\n * below, because none of them changes which comments an option set addresses.\n *\n * `source` is the deprecated alias for `resource` and is folded into it before\n * a key is ever built, so keying on both would give one list two keys.\n * `liveEdit` describes the document rather than its comments, which hang off\n * the published id whether or not drafts exist.\n */\ntype CommentsKeyIrrelevantField = 'source' | 'liveEdit'\n\n/**\n * A stable string standing for one set of document read options.\n *\n * Only React needs this: it holds one state source steady across renders and\n * defers swapping to a new one while the previous list is still on screen.\n * `fieldPath` is normalised on the way in, so a path array and the equivalent\n * string address the same list.\n *\n * Kept apart from the store it keys, because React imports this to memoize and\n * pulling the store in with it would put both comment stores into every bundle\n * that reads a comment.\n *\n * @internal\n */\nexport function getDocumentCommentsOptionsKey(options: CommentsOptions): string {\n return JSON.stringify({\n documentId: options.documentId,\n documentType: options.documentType,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n fieldPath: options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n status: options.status,\n scope: options.scope,\n // The `Record` half makes a new field on `CommentsOptions` a compile\n // error here unless it is listed above or named as irrelevant. Left to\n // `satisfies CommentsOptions` alone, a forgotten field would just be\n // absent from the key, and a reader would keep the list it had while the\n // caller thought it had asked for a different one.\n } satisfies CommentsOptions &\n Record<Exclude<keyof CommentsOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseDocumentCommentsOptionsKey(key: string): CommentsOptions {\n return JSON.parse(key) as CommentsOptions\n}\n\n/**\n * The same, for the GROQ escape hatch.\n *\n * @internal\n */\nexport function getCommentsQueryOptionsKey(options: CommentsQueryOptions): string {\n return JSON.stringify({\n filter: options.filter,\n params: options.params,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n } satisfies CommentsQueryOptions &\n Record<Exclude<keyof CommentsQueryOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseCommentsQueryOptionsKey(key: string): CommentsQueryOptions {\n return JSON.parse(key) as CommentsQueryOptions\n}\n","import {createActionBinder} from '../../store/createActionBinder'\nimport {defineStore} from '../../store/defineStore'\nimport {type MessageBusEmitOptions, workerMessages} from './bus'\nimport {requireDashboardMessageBus} from './store'\n\n/**\n * A view's client for one of its own application's workers, before `useWorker` types it.\n * @internal\n */\nexport interface WorkerConnection {\n post(payload?: unknown, options?: MessageBusEmitOptions): Promise<unknown>\n subscribe(handler: (message: unknown) => void): () => void\n}\n\ninterface DashboardWorkersState {\n workers: {[name: string]: WorkerConnection}\n}\n\nconst bindActionByInstance = createActionBinder<{name: string}, [name: string]>((instance) => ({\n name: instance.instanceId,\n}))\n\nconst dashboardWorkersStore = defineStore<DashboardWorkersState>({\n name: 'dashboardWorkers',\n getInitialState: () => ({workers: {}}),\n})\n\n/**\n * Returns the instance's client for one of its application's workers, creating it on first call.\n *\n * @remarks\n * The client lives with the instance rather than a component, so a render that suspended before\n * committing gets the same client when it renders again, and callers can key caches on it.\n * @internal\n */\nexport const getWorkerClient = bindActionByInstance(\n dashboardWorkersStore,\n ({instance, state}, name: string): WorkerConnection => {\n const current = state.get().workers[name]\n if (current) return current\n const messageBus = requireDashboardMessageBus(instance, `use worker \"${name}\"`)\n const client: WorkerConnection = {\n post: (payload, options) =>\n // Inside the executor, so a closed bus rejects instead of throwing.\n new Promise((resolve) =>\n resolve(messageBus.emit('workers.post', {name, payload}, options)),\n ),\n subscribe: (handler) => {\n const subscription = workerMessages(messageBus, name).subscribe(handler)\n return () => subscription.unsubscribe()\n },\n }\n state.set('addWorker', ({workers}) => ({workers: {...workers, [name]: client}}))\n return client\n },\n)\n","import {FETCH_TIMEOUT_DEFAULT} from '@sanity/comlink'\nimport {DocumentId, isPublishedIdEqual} from '@sanity/id-utils'\nimport {catchError, defer, distinctUntilChanged, EMPTY, map, merge, type Observable} from 'rxjs'\n\nimport {requireDashboardMessageBus} from '../dashboard/messageBus/store'\nimport {type CapabilityRecord, type FavoriteDocument} from '../dashboard/messageBus/topics'\nimport {getTopicState, resolveTopic} from '../dashboard/messageBus/topicStore'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type StateSource} from '../store/createStateSourceAction'\nimport {defineMutation} from '../store/fetcherStore'\nimport {\n type FavoriteDocumentContext,\n type FavoriteStatusResponse,\n type SetFavoriteInput,\n toFavoriteDocument,\n} from './favorites'\n\n// Published IDs mirror Dashboard's `_stableId` match.\nconst isSameFavorite = (\n a: FavoriteDocument['document'],\n b: FavoriteDocument['document'],\n): boolean =>\n a.type === b.type &&\n a.resource.id === b.resource.id &&\n a.resource.type === b.resource.type &&\n a.resource.schemaName === b.resource.schemaName &&\n isPublishedIdEqual(DocumentId(a.id), DocumentId(b.id))\n\n/**\n * A state source for whether a document is favorited, matched against the `favorites.documents`\n * the dashboard message bus host publishes. The message bus counterpart to the Comlink\n * {@link favorites} fetcher.\n *\n * @remarks\n * Returns `undefined` until the host publishes `applications.capabilities` and, while it provides\n * `favorites`, `favorites.documents`; use {@link resolveBusFavorite} to wait for them. Returns\n * `false` when the host does not provide `favorites` or the `favorites.documents` read fails.\n * A favorite is identified by its published document ID, type, resource and `schemaName`\n * (workspace), like in Dashboard.\n * @internal\n */\nexport function getBusFavoriteState(\n instance: SanityInstance,\n context: FavoriteDocumentContext,\n): StateSource<boolean | undefined> {\n const capabilities = getTopicState(instance, 'applications.capabilities')\n const documents = getTopicState(instance, 'favorites.documents')\n const target = toFavoriteDocument(context)\n\n const getCurrent = (): boolean | undefined => {\n const provided = capabilities.getCurrent() as CapabilityRecord | undefined\n if (provided === undefined) return undefined\n if (provided.favorites !== true) return false\n // A failed read degrades to \"not favorited\", like the Comlink fetcher.\n try {\n const favorites = documents.getCurrent() as FavoriteDocument[] | undefined\n return favorites?.some((favorite) => isSameFavorite(favorite.document, target))\n } catch {\n return false\n }\n }\n\n const subscribe = (onStoreChanged?: () => void) => {\n const unsubscribes = [\n capabilities.subscribe(onStoreChanged),\n documents.subscribe(onStoreChanged),\n ]\n return () => unsubscribes.forEach((unsubscribe) => unsubscribe())\n }\n\n const observable: Observable<boolean | undefined> = merge(\n capabilities.observable,\n documents.observable.pipe(catchError(() => EMPTY)),\n ).pipe(map(getCurrent), distinctUntilChanged())\n\n return {getCurrent, subscribe, observable}\n}\n\n/**\n * Resolves once {@link getBusFavoriteState} has the next value it waits for: the host's\n * capabilities first, then its `favorites.documents`.\n *\n * @internal\n */\nexport function resolveBusFavorite(instance: SanityInstance): Promise<void> {\n const capabilities = getTopicState(instance, 'applications.capabilities').getCurrent()\n return resolveTopic(\n instance,\n capabilities === undefined ? 'applications.capabilities' : 'favorites.documents',\n )\n}\n\n/**\n * Sets a document's favorite state over the dashboard message bus. The message bus counterpart\n * to `setFavorite`; the host keeps `favorites.documents` current, so there is no cache to\n * invalidate.\n *\n * @internal\n */\nexport const setBusFavorite = defineMutation<SetFavoriteInput, FavoriteStatusResponse>({\n name: 'setBusFavorite',\n mutationFn:\n (instance) =>\n ({isFavorited, ...context}) =>\n defer(() =>\n requireDashboardMessageBus(instance, 'update a favorite').emit(\n 'favorites.update',\n {document: toFavoriteDocument(context), favorited: isFavorited},\n // Matches the reply timeout of the Comlink write.\n {timeout: FETCH_TIMEOUT_DEFAULT},\n ),\n ).pipe(map(() => ({isFavorited}))),\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'\n\nimport {getClient} from '../client/clientStore'\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {isObject} from '../utils/object'\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 resource - Data resource for the preview\n * @internal\n */\nexport function transformProjectionToPreview(\n instance: SanityInstance,\n projectionResult: PreviewQueryResult,\n resource?: DocumentResource,\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 resource (if provided) or use the instance config\n const client = getClient(instance, {\n apiVersion: API_VERSION,\n resource,\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","/**\n * Telemetry environment classification.\n *\n * - `'development'` — the SDK is running on the local developer's machine\n * (`localhost` / `127.0.0.1` in the browser, or `NODE_ENV=development`\n * in Node). These are the original dev-only telemetry sessions.\n *\n * - `'production'` — the SDK is running on a production Sanity-controlled\n * domain (Studio deployments on `*.sanity.studio`, the dashboard on\n * `*.sanity.io`) where end users are authenticated Sanity users with\n * Populus consent records. Production telemetry is gated to this\n * allowlist; SDK apps deployed to customer-controlled domains do not\n * emit telemetry. Staging (`*.sanity.work`) and preview\n * (`*.sanity.dev`) hosts are deliberately excluded.\n *\n * @internal\n */\nexport type TelemetryEnvironment = 'development' | 'production'\n\n/**\n * Hostname suffixes that count as Sanity-controlled for the purposes of\n * production telemetry. A user reaching one of these hosts in the\n * browser is authenticated against Sanity and has a Populus consent\n * record, so we apply the same telemetry rules as the Studio's\n * `telemetry-sink`.\n *\n * The leading `.` is required: it ensures suffix matches hit a real\n * subdomain boundary, so apex matches (`sanity.io`, `sanity.studio`)\n * and lookalikes (`evilsanity.studio`) are excluded. Only production\n * hosts are listed; staging (`*.sanity.work`) and preview\n * (`*.sanity.dev`) hosts are excluded by omission.\n *\n * @internal\n */\nconst SANITY_CONTROLLED_HOST_SUFFIXES = ['.sanity.studio', '.sanity.io'] as const\n\nfunction getBrowserHostname(win: Window): string | null {\n const hostname = win.location?.hostname\n return typeof hostname === 'string' && hostname.length > 0 ? hostname.toLowerCase() : null\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '127.0.0.1'\n}\n\nfunction isSanityControlledHostname(hostname: string): boolean {\n return SANITY_CONTROLLED_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n}\n\n/**\n * Returns the telemetry environment for the current runtime, or `null`\n * when telemetry should not run at all.\n *\n * Browser:\n * - `localhost` / `127.0.0.1` → `'development'`\n * - host matches the Sanity-controlled allowlist → `'production'`\n * - anything else (customer domains, unknown contexts) → `null`\n *\n * Node (scripts, SSR, tests):\n * - `NODE_ENV=development` → `'development'`\n * - otherwise → `null`. Production-side server runtimes don't carry the\n * browser-authenticated user/consent assumption, so we don't enable\n * them under the production gate.\n *\n * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler\n * dead-code replacement.\n *\n * @internal\n */\nexport function getTelemetryEnvironment(): TelemetryEnvironment | null {\n if (typeof window !== 'undefined') {\n const hostname = getBrowserHostname(window)\n if (!hostname) return null\n if (isLocalHostname(hostname)) return 'development'\n if (isSanityControlledHostname(hostname)) return 'production'\n return null\n }\n\n if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development') {\n return 'development'\n }\n return null\n}\n\n/**\n * Convenience predicate for \"telemetry can run in this environment\".\n *\n * @internal\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryEnvironment() !== null\n}\n","import {defineEvent} from '@sanity/telemetry'\n\n/**\n * Whether the instance has Studio configuration. Apps include Dashboard and standalone apps.\n * This describes instance configuration, not credential source or who authored a hook call.\n * @internal\n */\nexport type TelemetryRuntimeContext = 'studio' | 'app'\n\n// Keep attribution in event data to preserve the warehouse's data_auth_method convention.\ninterface TelemetryAttribution {\n /** Configured credential source (token/studio/default), distinct from getAuthMethodState's transport. */\n authMethod: string\n runtimeContext: TelemetryRuntimeContext\n}\n\n/** @internal */\nexport const SDKSessionStarted = defineEvent<\n TelemetryAttribution & {\n version: string\n projectId: string\n perspective: string\n }\n>({\n name: 'SDK Session Started',\n version: 1,\n description: 'SDK instance created (environment is recorded in the event context)',\n})\n\n/** @internal */\nexport const SDKHookMounted = defineEvent<\n TelemetryAttribution & {\n hookName: string\n }\n>({\n name: 'SDK Hook Mounted',\n version: 1,\n description: 'An SDK hook was mounted for the first time in this session',\n})\n\n/** @internal */\nexport const SDKSessionEnded = defineEvent<\n TelemetryAttribution & {\n durationSeconds: number\n hooksUsed: string[]\n }\n>({\n name: 'SDK Session Ended',\n version: 1,\n description: 'SDK instance disposed (environment is recorded in the event context)',\n})\n\n/** @internal */\nexport const SDKError = defineEvent<{\n errorType: string\n hookName: string\n}>({\n name: 'SDK Error',\n version: 1,\n description: 'Runtime error caught in the SDK',\n})\n","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 {type TelemetryEnvironment} from './environment'\nimport {\n SDKError,\n SDKHookMounted,\n SDKSessionEnded,\n SDKSessionStarted,\n type TelemetryRuntimeContext,\n} from './events'\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 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 * The `environment` is captured at construction time and recorded in\n * the event context so downstream pipelines can distinguish dev and\n * production sessions.\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 Session Started\" event */\n logSessionStarted(data: {projectId: string; perspective: string}): void\n\n /** Log a \"SDK Hook Mounted\" event (deduplicated per hook name) */\n logHookFirstUsed(hookName: string): void\n\n /** Log a \"SDK Error\" event */\n logError(errorType: string, hookName: string): void\n\n /** Log a \"SDK 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 environment: TelemetryEnvironment\n authMethod: string\n runtimeContext: TelemetryRuntimeContext\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, environment, authMethod, runtimeContext} = 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 url: '/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 // Trace events (`trace.start` / `trace.log` / `trace.error` /\n // `trace.complete`) arrive with their own caller-provided `context`.\n // Log / userProperties events don't. Narrow via `in` so `event`\n // stays typed as `TelemetryEvent`, then merge so we don't drop the\n // trace context. SDK-owned fields below win on conflict.\n const existing =\n 'context' in event ? (event.context as Record<string, unknown> | undefined) : undefined\n return {\n ...event,\n context: {\n ...existing,\n version: CORE_SDK_VERSION,\n environment,\n origin: typeof window !== 'undefined' ? window.location.origin : 'node',\n },\n }\n })\n\n const sendEvents = async (batch: TelemetryEvent[]): Promise<unknown> => {\n const client = getClient()\n log.debug('sending event batch', {batchSize: batch.length, environment})\n return client.request({\n url: '/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 Session Started', {\n projectId: data.projectId,\n perspective: data.perspective,\n authMethod,\n runtimeContext,\n version: CORE_SDK_VERSION,\n environment,\n })\n logger.log(SDKSessionStarted, {\n version: CORE_SDK_VERSION,\n ...data,\n authMethod,\n runtimeContext,\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, authMethod, runtimeContext})\n logger.log(SDKHookMounted, {hookName, authMethod, runtimeContext})\n },\n\n logError(errorType: string, hookName: string) {\n log.debug('event: SDK Error', {errorType, hookName})\n logger.log(SDKError, {errorType, hookName})\n },\n\n endSession() {\n const durationSeconds = Math.round((Date.now() - startedAt) / 1000)\n log.debug('event: SDK Session Ended', {\n durationSeconds,\n hooksUsed: [...emittedHooks],\n authMethod,\n runtimeContext,\n environment,\n })\n logger.log(SDKSessionEnded, {\n durationSeconds,\n hooksUsed: [...emittedHooks],\n authMethod,\n runtimeContext,\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","import {type SanityInstance} from '../store/createSanityInstance'\nimport {createLogger} from '../utils/logger'\nimport {getTelemetryEnvironment} from './environment'\nimport {type TelemetryManager} from './telemetryManager'\n\nconst DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'\n\nconst logger = createLogger('telemetry')\n\n/**\n * Per-instance map of active telemetry managers. Allows the React\n * package (and other consumers) to look up the manager for a given\n * instance and log hook usage / errors without importing the full\n * telemetry module themselves.\n *\n * @internal\n */\nconst telemetryManagers = new WeakMap<SanityInstance, TelemetryManager>()\nconst pendingHooks = new WeakMap<SanityInstance, Set<string>>()\nconst initInFlight = new WeakSet<SanityInstance>()\n\n/**\n * Initializes telemetry for a SDK instance if the runtime environment\n * qualifies. The environment is resolved by `getTelemetryEnvironment()`:\n *\n * - `'development'` — local dev servers (`localhost` / `127.0.0.1`, or\n * Node with `NODE_ENV=development`). This is the original opt-in\n * surface.\n * - `'production'` — apps deployed to Sanity-controlled domains\n * (e.g. `*.sanity.studio`, the dashboard). End users are\n * authenticated Sanity users with Populus consent records, so we\n * apply the same consent gate as the Studio's `telemetry-sink`.\n *\n * Apps on customer-controlled domains return `null` and skip telemetry\n * entirely.\n *\n * `telemetryManager` and `clientStore` are dynamically imported so the\n * telemetry code path stays out of production bundles for apps that\n * don't qualify. Only the lightweight environment check runs at boot.\n *\n * The `projectId` must be passed explicitly because the resource\n * configuration is typically set by the React layer after the\n * instance has already been created.\n *\n * @internal\n */\nexport function initTelemetry(instance: SanityInstance, projectId: string): void {\n const environment = getTelemetryEnvironment()\n if (!environment) {\n logger.trace('initTelemetry skipped: environment not eligible', {internal: true})\n return\n }\n if (!projectId) {\n logger.trace('initTelemetry skipped: no projectId', {internal: true})\n return\n }\n if (telemetryManagers.has(instance) || initInFlight.has(instance)) {\n return\n }\n initInFlight.add(instance)\n\n logger.debug('initializing telemetry', {projectId, environment})\n\n Promise.all([\n import('./telemetryManager'),\n import('../client/clientStore'),\n import('../auth/authStore'),\n import('../auth/authMode'),\n ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}, {isStudioConfig}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n // The token observable is a `shareReplay({bufferSize: 1, refCount: true})`\n // (see `createStateSourceAction`), so it can deliver a buffered value\n // synchronously while we're still inside `.subscribe(cb)`. At that\n // moment `sub` is in the TDZ, so the callback can't reach it. We\n // gate on a `received` flag and let the post-subscribe block do the\n // unsubscribe in the sync-emission case.\n let received = false\n const sub = getTokenState(instance).observable.subscribe((t) => {\n if (received || !t) return\n received = true\n logger.debug('auth token received')\n unsub()\n resolve(true)\n cleanup.unsubscribe()\n })\n cleanup.unsubscribe = () => sub.unsubscribe()\n if (received) cleanup.unsubscribe()\n })\n if (!hasToken || instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: no token resolved or instance disposed')\n return\n }\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n // Studio instances can use explicit tokens. Keep runtime context independent of auth.\n const runtimeContext = isStudioConfig(config) ? 'studio' : 'app'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n const manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n environment,\n authMethod,\n runtimeContext,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n logger.info('telemetry session started', {\n projectId,\n perspective,\n authMethod,\n runtimeContext,\n environment,\n })\n manager.logSessionStarted({\n projectId,\n perspective,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!getTelemetryEnvironment()) return\n\n const manager = findManager(instance)\n if (manager) {\n logger.trace('hook mounted (logged)', {hookName, internal: true})\n manager.logHookFirstUsed(hookName)\n return\n }\n\n const root = getRootInstance(instance)\n let hooks = pendingHooks.get(root)\n if (!hooks) {\n hooks = new Set()\n pendingHooks.set(root, hooks)\n }\n if (!hooks.has(hookName)) {\n logger.trace('hook mounted (buffered)', {hookName, internal: true})\n }\n hooks.add(hookName)\n}\n\nfunction findManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\nfunction getRootInstance(instance: SanityInstance): SanityInstance {\n return instance\n}\n","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 * backslashes and 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 backslashes and double quotes within the final search term for the\n // GROQ query. Backslashes go first, so the escapes added for quotes are not\n // themselves escaped. Without this a trailing backslash would escape the\n // closing quote and let the input break out of the string literal.\n const escapedSearch = wildcardSearch.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n\n // Construct the final GROQ filter clause\n return `[@] match text::query(\"${escapedSearch}\")`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_internal.js","names":["\"NODE_ENV\""],"sources":["../../src/comments/legacy/commentsOptions.ts","../../src/applications/applicationOrigin.ts","../../src/comments/commentsOptions.ts","../../src/dashboard/messageBus/workerStore.ts","../../src/errorReporting/isErrorReportingEnabled.ts","../../src/errorReporting/isKnownSdkError.ts","../../src/errorReporting/privacy.ts","../../src/errorReporting/errorReporter.ts","../../src/favorites/busFavorites.ts","../../src/preview/previewConstants.ts","../../src/preview/previewProjectionUtils.ts","../../src/telemetry/environment.ts","../../src/telemetry/events.ts","../../src/telemetry/telemetryManager.ts","../../src/telemetry/initTelemetry.ts","../../src/utils/createGroqSearchFilter.ts"],"sourcesContent":["// fallow-ignore-file code-duplication -- deprecated add-on dataset comments, kept alongside comments/ until removal in the next major\nimport {type Path} from '@sanity/types'\n\nimport {type DocumentHandle} from '../../config/sanityConfig'\nimport {toCommentFieldPath} from '../commentFieldPath'\nimport {type CommentStatus} from './types'\n\n/**\n * Which comments to read.\n * @beta\n * @deprecated Comments have moved to the organization collaboration API. Import from\n * `@sanity/sdk/collaboration`; this add-on dataset API is removed in the next major.\n */\nexport interface CommentsOptions extends DocumentHandle {\n /**\n * Narrow to one field. Omit to get every comment on the document.\n */\n fieldPath?: string | Path\n /** Narrow to open or resolved threads. Omit for both. */\n status?: CommentStatus\n}\n\n/**\n * @beta\n * @deprecated Comments have moved to the organization collaboration API. Import from\n * `@sanity/sdk/collaboration`; this add-on dataset API is removed in the next major.\n */\nexport interface ResolveCommentsOptions extends CommentsOptions {\n signal?: AbortSignal\n}\n\n/**\n * Fields on {@link CommentsOptions} that are deliberately left out of the key\n * below, because neither changes which comments an option set addresses.\n *\n * `source` is the deprecated alias for `resource` and is folded into it before\n * a key is ever built, so keying on both would give one list two keys.\n * `liveEdit` describes the document rather than its comments, which hang off\n * the published id whether or not drafts exist.\n */\ntype CommentsKeyIrrelevantField = 'source' | 'liveEdit'\n\n/**\n * A stable string standing for one set of read options.\n *\n * Only React needs this: it holds one state source steady across renders and\n * defers swapping to a new one while the previous list is still on screen.\n * `fieldPath` is normalised on the way in, so a path array and the equivalent\n * string address the same list.\n *\n * Kept apart from the store it keys, because React imports this to memoize and\n * pulling the store in with it would put both comment stores into every bundle\n * that reads a comment.\n *\n * @internal\n */\nexport function getCommentsOptionsKey(options: CommentsOptions): string {\n return JSON.stringify({\n documentId: options.documentId,\n documentType: options.documentType,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n fieldPath: options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n status: options.status,\n // The `Record` half makes a new field on `CommentsOptions` a compile\n // error here unless it is listed above or named as irrelevant. Left to\n // `satisfies CommentsOptions` alone, a forgotten field would just be\n // absent from the key, and a reader would keep the list it had while the\n // caller thought it had asked for a different one.\n } satisfies CommentsOptions &\n Record<Exclude<keyof CommentsOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseCommentsOptionsKey(key: string): CommentsOptions {\n return JSON.parse(key) as CommentsOptions\n}\n","import {type ApplicationBase} from './applications'\n\ntype SanityGlobal = typeof globalThis & {__SANITY_STAGING__?: boolean}\n\n/**\n * The origin an application is served from, or `null` when it has neither an external URL nor\n * a slug to derive one from. Callers treat `null` as \"nothing to load\" rather than failing the\n * whole list on one bad record.\n *\n * @internal\n */\nexport function getApplicationOrigin(\n application: Pick<ApplicationBase, 'externalUrl' | 'slug' | 'isSingleton' | 'organizationId'>,\n): string | null {\n if (application.externalUrl !== null) return new URL(application.externalUrl).origin\n if (application.slug === null) return null\n\n // Read at runtime, not via a bundler define: a remote is built once and runs in whichever\n // host page loaded it, and the host sets this flag. Mirrors workbench's `getSanityEnv`.\n const staging = (globalThis as SanityGlobal).__SANITY_STAGING__ === true\n if (application.isSingleton) {\n const domain = staging ? 'run.sanity.work' : 'sanity.run'\n return `https://${application.slug}-apps-${application.organizationId}.${domain}`\n }\n\n const domain = staging ? 'studio.sanity.work' : 'sanity.studio'\n return `https://${application.slug}.${domain}`\n}\n","import {type QueryParams} from '@sanity/client'\nimport {type Path} from '@sanity/types'\n\nimport {type DatasetHandle, type DocumentHandle} from '../config/sanityConfig'\nimport {toCommentFieldPath} from './commentFieldPath'\nimport {type CommentStatus} from './types'\n\n/**\n * Which of a document's comments a read covers.\n *\n * @beta\n */\nexport type CommentsReadScope = 'perspective' | 'exact' | 'all'\n\n/**\n * Which of a document's comments to read.\n * @beta\n */\nexport interface CommentsOptions extends DocumentHandle {\n /** Narrow to one field. Omit to get every comment on the document. */\n fieldPath?: string | Path\n /** Narrow to open or resolved threads. Omit for both. */\n status?: CommentStatus\n /**\n * Which of the document's comments to read.\n *\n * - `'perspective'` follows what you are viewing: a release shows that\n * release's comments, anything else pools draft and published.\n * - `'exact'` matches only the precise document id passed.\n * - `'all'` returns every comment on the document.\n *\n * `'perspective'` recognises a release from the `{releaseName}` form. A\n * stacked `ClientPerspective` array names several layers at once, with no\n * single release to read comments from, so it pools draft and published as\n * any other non-release perspective does. To read the pooled list while a\n * release is in view, pass a non-release `perspective` alongside; to name a\n * release yourself, pass a version id as the `documentId`.\n *\n * A content variant is stored as a version document, so under\n * `'perspective'` its comments form a list of their own, the way a release's\n * do. A variant's draft and published comments are separate lists: pairing\n * them needs the variant's name, which its document id does not carry.\n *\n * @defaultValue 'perspective'\n */\n scope?: CommentsReadScope\n}\n\n/** @beta */\nexport interface ResolveCommentsOptions extends CommentsOptions {\n signal?: AbortSignal\n}\n\n/**\n * An arbitrary comment query, for anything that is not \"comments on this\n * document\" — cross-document views, per-user views, organization-wide activity.\n *\n * @beta\n */\nexport interface CommentsQueryOptions extends DatasetHandle {\n /**\n * GROQ filter applied to comment documents. `_type == \"sanity.comment\"` is\n * added for you, so this only has to say which comments.\n *\n * It is evaluated against every comment the organization can read. The\n * resource picks the organization but does not narrow the results; add a\n * `target.document._ref` prefix condition to keep to one dataset.\n */\n filter: string\n params?: QueryParams\n}\n\n/** @beta */\nexport interface ResolveCommentsQueryOptions extends CommentsQueryOptions {\n signal?: AbortSignal\n}\n\n/**\n * Fields on the read option types that are deliberately left out of the keys\n * below, because none of them changes which comments an option set addresses.\n *\n * `source` is the deprecated alias for `resource` and is folded into it before\n * a key is ever built, so keying on both would give one list two keys.\n * `liveEdit` describes the document rather than its comments, which hang off\n * the published id whether or not drafts exist.\n */\ntype CommentsKeyIrrelevantField = 'source' | 'liveEdit'\n\n/**\n * A stable string standing for one set of document read options.\n *\n * Only React needs this: it holds one state source steady across renders and\n * defers swapping to a new one while the previous list is still on screen.\n * `fieldPath` is normalised on the way in, so a path array and the equivalent\n * string address the same list.\n *\n * Kept apart from the store it keys, because React imports this to memoize and\n * pulling the store in with it would put both comment stores into every bundle\n * that reads a comment.\n *\n * @internal\n */\nexport function getDocumentCommentsOptionsKey(options: CommentsOptions): string {\n return JSON.stringify({\n documentId: options.documentId,\n documentType: options.documentType,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n fieldPath: options.fieldPath === undefined ? undefined : toCommentFieldPath(options.fieldPath),\n status: options.status,\n scope: options.scope,\n // The `Record` half makes a new field on `CommentsOptions` a compile\n // error here unless it is listed above or named as irrelevant. Left to\n // `satisfies CommentsOptions` alone, a forgotten field would just be\n // absent from the key, and a reader would keep the list it had while the\n // caller thought it had asked for a different one.\n } satisfies CommentsOptions &\n Record<Exclude<keyof CommentsOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseDocumentCommentsOptionsKey(key: string): CommentsOptions {\n return JSON.parse(key) as CommentsOptions\n}\n\n/**\n * The same, for the GROQ escape hatch.\n *\n * @internal\n */\nexport function getCommentsQueryOptionsKey(options: CommentsQueryOptions): string {\n return JSON.stringify({\n filter: options.filter,\n params: options.params,\n projectId: options.projectId,\n dataset: options.dataset,\n resource: options.resource,\n perspective: options.perspective,\n } satisfies CommentsQueryOptions &\n Record<Exclude<keyof CommentsQueryOptions, CommentsKeyIrrelevantField>, unknown>)\n}\n\n/** @internal */\nexport function parseCommentsQueryOptionsKey(key: string): CommentsQueryOptions {\n return JSON.parse(key) as CommentsQueryOptions\n}\n","import {createActionBinder} from '../../store/createActionBinder'\nimport {defineStore} from '../../store/defineStore'\nimport {type MessageBusEmitOptions, workerMessages} from './bus'\nimport {requireDashboardMessageBus} from './store'\n\n/**\n * A view's client for one of its own application's workers, before `useWorker` types it.\n * @internal\n */\nexport interface WorkerConnection {\n post(payload?: unknown, options?: MessageBusEmitOptions): Promise<unknown>\n subscribe(handler: (message: unknown) => void): () => void\n}\n\ninterface DashboardWorkersState {\n workers: {[name: string]: WorkerConnection}\n}\n\nconst bindActionByInstance = createActionBinder<{name: string}, [name: string]>((instance) => ({\n name: instance.instanceId,\n}))\n\nconst dashboardWorkersStore = defineStore<DashboardWorkersState>({\n name: 'dashboardWorkers',\n getInitialState: () => ({workers: {}}),\n})\n\n/**\n * Returns the instance's client for one of its application's workers, creating it on first call.\n *\n * @remarks\n * The client lives with the instance rather than a component, so a render that suspended before\n * committing gets the same client when it renders again, and callers can key caches on it.\n * @internal\n */\nexport const getWorkerClient = bindActionByInstance(\n dashboardWorkersStore,\n ({instance, state}, name: string): WorkerConnection => {\n const current = state.get().workers[name]\n if (current) return current\n const messageBus = requireDashboardMessageBus(instance, `use worker \"${name}\"`)\n const client: WorkerConnection = {\n post: (payload, options) =>\n // Inside the executor, so a closed bus rejects instead of throwing.\n new Promise((resolve) =>\n resolve(messageBus.emit('workers.post', {name, payload}, options)),\n ),\n subscribe: (handler) => {\n const subscription = workerMessages(messageBus, name).subscribe(handler)\n return () => subscription.unsubscribe()\n },\n }\n state.set('addWorker', ({workers}) => ({workers: {...workers, [name]: client}}))\n return client\n },\n)\n","/**\n * localStorage key that forces error reporting on, for debugging the reporter itself.\n */\nconst DEBUG_KEY = 'SANITY_DEBUG_ERROR_REPORTING'\n\n/**\n * Whether the document has an import map with an `@sanity/sdk` entry. The Sanity CLI adds one\n * when an app is deployed with `autoUpdates`, so its presence means Sanity serves the SDK bundle.\n */\nfunction hasSdkPackageInImportMap(): boolean {\n if (typeof document === 'undefined' || !('querySelectorAll' in document)) return false\n\n return Array.from(document.querySelectorAll('script[type=\"importmap\"]')).some((entry) => {\n if (!entry.textContent) return false\n try {\n const imports = JSON.parse(entry.textContent).imports\n return Boolean(imports) && '@sanity/sdk' in imports\n } catch {\n return false\n }\n })\n}\n\n/**\n * Whether the debug flag is set in localStorage. It also lets the origin filter accept this\n * repo's own source paths, which only appear in local development.\n *\n * @internal\n */\nexport function hasDebugFlag(): boolean {\n try {\n return typeof localStorage !== 'undefined' && Boolean(localStorage.getItem(DEBUG_KEY))\n } catch {\n return false\n }\n}\n\n/**\n * Error reporting only runs in apps where Sanity serves the SDK bundle (auto-updating apps), or\n * when the debug flag is set. It never runs outside a browser.\n *\n * @internal\n */\nexport function isErrorReportingEnabled(): boolean {\n if (typeof window === 'undefined') return false\n return hasDebugFlag() || hasSdkPackageInImportMap()\n}\n","import {ClientError, ConnectionFailedError, isTimeoutError, ServerError} from '@sanity/client'\n\nimport {MessageBusError} from '../dashboard/messageBus/bus'\nimport {TopicError} from '../dashboard/messageBus/topicStore'\nimport {DocumentInaccessibleError} from '../document/documentInaccessibleError'\nimport {ActionError} from '../document/processActions/shared'\n\nconst MAX_CAUSE_DEPTH = 5\n\n/** Browsers word a failed `fetch` differently, and all of them throw a plain `TypeError`. */\nconst NETWORK_FAILURE_MESSAGE = /failed to fetch|networkerror|load failed|network request failed/i\n\n/**\n * Aborting a request on teardown is routine. Matched by name because `DOMException` and abort\n * errors from other libraries do not share a class.\n */\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError'\n}\n\nfunction isNetworkFailure(error: unknown): boolean {\n return error instanceof TypeError && NETWORK_FAILURE_MESSAGE.test(error.message)\n}\n\n/** Known errors that cannot be matched by class. */\nconst KNOWN_ERROR_CHECKS: ((error: unknown) => boolean)[] = [\n isTimeoutError,\n isAbortError,\n isNetworkFailure,\n]\n\nfunction isKnownErrorInstance(error: unknown): boolean {\n // Built per call so the classes are read after every module has finished loading\n const knownClasses = [\n ActionError,\n DocumentInaccessibleError,\n MessageBusError,\n TopicError,\n // 4xx and 5xx responses from the Sanity API reflect the app's permissions, queries, or API\n // availability, not a defect in the SDK\n ClientError,\n ServerError,\n // The user is offline\n ConnectionFailedError,\n ]\n return (\n knownClasses.some((knownClass) => error instanceof knownClass) ||\n KNOWN_ERROR_CHECKS.some((check) => check(error))\n )\n}\n\n/**\n * Whether an error is an expected state caused by the app's configuration, the user's\n * permissions, the user's network, or the Sanity API, and so is not worth reporting as an SDK\n * bug. Wrapped errors are checked through their `cause` chain.\n *\n * @internal\n */\nexport function isKnownSdkError(error: unknown): boolean {\n let current = error\n for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth++) {\n if (isKnownErrorInstance(current)) return true\n if (!(current instanceof Error) || current.cause === undefined) return false\n current = current.cause\n }\n return false\n}\n","import {type BrowserOptions, type ErrorEvent} from './sentryTypes'\n\n/**\n * Key fragments Sentry denies when `sendDefaultPii` is left unset. The Sentry v11 migration guide\n * uses the same list for headers and query params.\n */\nconst PII_KEY_DENYLIST = ['forwarded', '-ip', 'remote-', 'via', '-user']\n\n/**\n * Turns off every automatic data category that can carry user or customer data. Matches the\n * posture Sanity Studio uses for its own Sentry client.\n *\n * `userInfo: false` also stops Relay from inferring the client IP.\n */\nexport const sdkSentryDataCollection = {\n userInfo: false,\n cookies: false,\n httpHeaders: {\n request: {deny: PII_KEY_DENYLIST},\n response: {deny: PII_KEY_DENYLIST},\n },\n httpBodies: [],\n urlQueryParams: {deny: PII_KEY_DENYLIST},\n genAI: {inputs: false, outputs: false},\n databaseQueryData: false,\n graphQL: {document: false, variables: false},\n queues: false,\n} satisfies NonNullable<BrowserOptions['dataCollection']>\n\nconst KEPT_TAGS = ['handled', 'errorBoundary']\n\n/**\n * Sentry `beforeSend` hook that rebuilds the event from an allowlist of fields. Sentry merges\n * the global and isolation scopes into every event, and when the host page runs the same Sentry\n * version those scopes are the host's: its tags, extras, breadcrumbs, and contexts would be\n * sent along with the SDK's error. Anything not listed here is dropped, and the zeroed IP stops\n * Sentry from filling one in from the connection. Exception messages and stack frames are kept\n * as they are.\n */\nexport function beforeSend(event: ErrorEvent): ErrorEvent {\n const tags: NonNullable<ErrorEvent['tags']> = {}\n for (const key of KEPT_TAGS) {\n const value = event.tags?.[key]\n if (value !== undefined) tags[key] = value\n }\n\n return {\n type: undefined,\n event_id: event.event_id,\n timestamp: event.timestamp,\n platform: event.platform,\n level: event.level,\n release: event.release,\n environment: event.environment,\n message: event.message,\n exception: event.exception,\n sdk: event.sdk,\n tags,\n user: {ip_address: '0.0.0.0'},\n }\n}\n","import {CORE_SDK_VERSION} from '../version'\nimport {hasDebugFlag, isErrorReportingEnabled} from './isErrorReportingEnabled'\nimport {isKnownSdkError} from './isKnownSdkError'\nimport {beforeSend, sdkSentryDataCollection} from './privacy'\nimport {type BrowserOptions, type ErrorEvent, type Event, type Scope} from './sentryTypes'\n\n/**\n * The DSN of the SDK project in Sentry. It is a public ingest key, not a secret.\n */\nconst SDK_DSN = 'https://08e08ab0cbf33a49f7f5066597d5e374@sentry.sanity.io/4512347487666176'\n\n/**\n * Events go through the Sanity intake tunnel, which forwards them to Sentry. This avoids ad\n * blockers and customer CSP `connect-src` rules that block the Sentry ingest host.\n */\nconst SDK_TUNNEL = 'https://api.sanity.io/vX/intake/tracing'\n\n/** Most events a single page load may send. A render loop can throw thousands of errors. */\nconst MAX_EVENTS_PER_SESSION = 10\n\n/** Events with the same message inside this window are reported once. */\nconst DUPLICATE_WINDOW_MS = 5 * 60 * 1000\n\n/** Failed loads of the Sentry chunk allowed before the reporter gives up for the page load. */\nconst MAX_LOAD_FAILURES = 3\n\n/** @internal */\nexport interface ReportErrorInfo {\n /** The name of the error boundary that caught the error. */\n errorBoundary?: string\n}\n\n/** @internal */\nexport interface ErrorReporterOptions {\n /** Replaces the fetch transport, for tests. */\n transport?: BrowserOptions['transport']\n}\n\n/**\n * The value of the error that was thrown. When `linkedErrors` adds the causes, they come first\n * and the thrown error is last.\n */\nfunction getThrownValue(event: Event): string | undefined {\n return event.exception?.values?.at(-1)?.value ?? event.message\n}\n\n/**\n * Caps the events per session and drops repeats of the same message inside a time window.\n */\nfunction limitEventsIntegration() {\n const seen = new Map<string, number>()\n let sent = 0\n\n return {\n name: 'SdkLimitEvents',\n processEvent(event: Event): Event | null {\n if (sent >= MAX_EVENTS_PER_SESSION) return null\n\n const message = getThrownValue(event) ?? ''\n const now = Date.now()\n const previous = seen.get(message)\n if (previous !== undefined && now - previous < DUPLICATE_WINDOW_MS) return null\n\n seen.set(message, now)\n sent++\n return event\n },\n }\n}\n\n/**\n * Matches the SDK bundle served by the module server (`@sanity__sdk`, `@sanity__sdk-react`) and\n * the same packages under `node_modules` (`@sanity/sdk`, or `@sanity_sdk-react` in a Vite\n * dependency cache). The package name must end at a path separator or a file extension, so\n * `@sanity/sdk-other` does not match.\n */\nconst SDK_MODULE_PATTERN = /(?:@|%40)sanity(?:__|_|\\/|%2F)sdk(?:-react)?(?=[/.]|$)/\n\n/** This repo's own sources, which only show up in local development with the debug flag set. */\nconst SDK_SOURCE_PATTERN = /\\/packages\\/(?:core|react)\\/(?:src|dist)\\//\n\n/** Frame names the engine uses for code without a URL, such as `JSON.parse (<anonymous>)`. */\nconst BUILTIN_FRAME_PATTERN = /^(?:<.*>|native)$/\n\nfunction isSdkFilename(filename: string): boolean {\n return SDK_MODULE_PATTERN.test(filename) || (hasDebugFlag() && SDK_SOURCE_PATTERN.test(filename))\n}\n\n/**\n * The file of the innermost frame that has a URL. Builtins such as `JSON.parse` and `new URL`\n * sit on top of the stack when they throw, and the code that called them decides the owner.\n */\nfunction getThrowingFilename(event: Event): string | undefined {\n const frames = event.exception?.values?.at(-1)?.stacktrace?.frames ?? []\n for (let index = frames.length - 1; index >= 0; index--) {\n const filename = frames[index]?.filename\n if (filename && !BUILTIN_FRAME_PATTERN.test(filename)) return filename\n }\n return undefined\n}\n\n/**\n * Keeps only events thrown by SDK code. Error boundaries also catch errors thrown by the app\n * rendered beneath them, so the frame that threw decides who owns the error. Events with no\n * usable frame cannot be attributed and are dropped.\n */\nfunction sdkOriginIntegration() {\n return {\n name: 'SdkOrigin',\n processEvent(event: Event): Event | null {\n const filename = getThrowingFilename(event)\n return filename && isSdkFilename(filename) ? event : null\n },\n }\n}\n\n/**\n * Sentry marks every error it is given as already captured, and skips marked errors in every\n * Sentry client on the page. Capturing a copy keeps that mark off the app's own error objects.\n */\nfunction toCapturable(error: Error): Error {\n const copy = new Error(\n error.message,\n error.cause === undefined ? undefined : {cause: error.cause},\n )\n copy.name = error.name\n copy.stack = error.stack\n return copy\n}\n\nfunction getEnvironment(): string {\n const hostname = typeof window === 'undefined' ? '' : window.location?.hostname\n return hostname === 'localhost' || hostname === '127.0.0.1' ? 'development' : 'production'\n}\n\n/**\n * Creates a reporter that sends unexpected SDK errors to Sentry. It owns its own Sentry client\n * and scope, and installs no global error handlers. Nothing is sent unless `reportError` is\n * called.\n *\n * @internal\n */\nexport function createErrorReporter(options: ErrorReporterOptions = {}): {\n reportError: (error: unknown, info?: ReportErrorInfo) => void\n} {\n let scopePromise: Promise<Scope> | undefined\n let loadFailures = 0\n\n function getScope(): Promise<Scope> {\n scopePromise ??= import('./sentryBrowser')\n .then((sentry) => {\n const client = new sentry.BrowserClient({\n dsn: SDK_DSN,\n tunnel: SDK_TUNNEL,\n release: String(CORE_SDK_VERSION),\n environment: getEnvironment(),\n dataCollection: sdkSentryDataCollection,\n stackParser: sentry.defaultStackParser,\n transport: options.transport ?? sentry.makeFetchTransport,\n integrations: [\n sentry.linkedErrorsIntegration(),\n sdkOriginIntegration(),\n limitEventsIntegration(),\n ],\n beforeSend: (event: ErrorEvent) => beforeSend(event),\n })\n\n const scope = new sentry.Scope()\n scope.setClient(client)\n client.init()\n return scope\n })\n .catch((error: unknown) => {\n // Let the next report try again, for a chunk that failed to load while offline\n scopePromise = undefined\n loadFailures++\n throw error\n })\n return scopePromise\n }\n\n function reportError(error: unknown, info: ReportErrorInfo = {}): void {\n // This runs inside `componentDidCatch`, where a throw would reach the parent boundary\n try {\n // Sentry builds a synthetic stack for non-Error values, and that stack points into this\n // module, so such values would be attributed to the SDK\n if (\n !(error instanceof Error) ||\n loadFailures >= MAX_LOAD_FAILURES ||\n !isErrorReportingEnabled() ||\n isKnownSdkError(error)\n ) {\n return\n }\n\n const capturable = toCapturable(error)\n const {errorBoundary} = info\n const tags = errorBoundary ? {handled: 'no', errorBoundary} : {handled: 'no'}\n\n getScope()\n .then((scope) => {\n scope.captureException(capturable, {\n mechanism: {handled: false, type: 'generic'},\n captureContext: {tags},\n })\n })\n .catch(() => {\n // Nothing to do. The reporter must not add errors to the app it reports for.\n })\n } catch {\n // Same: never throw into the caller\n }\n }\n\n return {reportError}\n}\n\n/**\n * Reports an unexpected error from the SDK to Sentry, when error reporting is enabled.\n *\n * @internal\n */\nexport const {reportError} = createErrorReporter()\n","import {FETCH_TIMEOUT_DEFAULT} from '@sanity/comlink'\nimport {DocumentId, isPublishedIdEqual} from '@sanity/id-utils'\nimport {catchError, defer, distinctUntilChanged, EMPTY, map, merge, type Observable} from 'rxjs'\n\nimport {requireDashboardMessageBus} from '../dashboard/messageBus/store'\nimport {type CapabilityRecord, type FavoriteDocument} from '../dashboard/messageBus/topics'\nimport {getTopicState, resolveTopic} from '../dashboard/messageBus/topicStore'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type StateSource} from '../store/createStateSourceAction'\nimport {defineMutation} from '../store/fetcherStore'\nimport {\n type FavoriteDocumentContext,\n type FavoriteStatusResponse,\n type SetFavoriteInput,\n toFavoriteDocument,\n} from './favorites'\n\n// Published IDs mirror Dashboard's `_stableId` match.\nconst isSameFavorite = (\n a: FavoriteDocument['document'],\n b: FavoriteDocument['document'],\n): boolean =>\n a.type === b.type &&\n a.resource.id === b.resource.id &&\n a.resource.type === b.resource.type &&\n a.resource.schemaName === b.resource.schemaName &&\n isPublishedIdEqual(DocumentId(a.id), DocumentId(b.id))\n\n/**\n * A state source for whether a document is favorited, matched against the `favorites.documents`\n * the dashboard message bus host publishes. The message bus counterpart to the Comlink\n * {@link favorites} fetcher.\n *\n * @remarks\n * Returns `undefined` until the host publishes `applications.capabilities` and, while it provides\n * `favorites`, `favorites.documents`; use {@link resolveBusFavorite} to wait for them. Returns\n * `false` when the host does not provide `favorites` or the `favorites.documents` read fails.\n * A favorite is identified by its published document ID, type, resource and `schemaName`\n * (workspace), like in Dashboard.\n * @internal\n */\nexport function getBusFavoriteState(\n instance: SanityInstance,\n context: FavoriteDocumentContext,\n): StateSource<boolean | undefined> {\n const capabilities = getTopicState(instance, 'applications.capabilities')\n const documents = getTopicState(instance, 'favorites.documents')\n const target = toFavoriteDocument(context)\n\n const getCurrent = (): boolean | undefined => {\n const provided = capabilities.getCurrent() as CapabilityRecord | undefined\n if (provided === undefined) return undefined\n if (provided.favorites !== true) return false\n // A failed read degrades to \"not favorited\", like the Comlink fetcher.\n try {\n const favorites = documents.getCurrent() as FavoriteDocument[] | undefined\n return favorites?.some((favorite) => isSameFavorite(favorite.document, target))\n } catch {\n return false\n }\n }\n\n const subscribe = (onStoreChanged?: () => void) => {\n const unsubscribes = [\n capabilities.subscribe(onStoreChanged),\n documents.subscribe(onStoreChanged),\n ]\n return () => unsubscribes.forEach((unsubscribe) => unsubscribe())\n }\n\n const observable: Observable<boolean | undefined> = merge(\n capabilities.observable,\n documents.observable.pipe(catchError(() => EMPTY)),\n ).pipe(map(getCurrent), distinctUntilChanged())\n\n return {getCurrent, subscribe, observable}\n}\n\n/**\n * Resolves once {@link getBusFavoriteState} has the next value it waits for: the host's\n * capabilities first, then its `favorites.documents`.\n *\n * @internal\n */\nexport function resolveBusFavorite(instance: SanityInstance): Promise<void> {\n const capabilities = getTopicState(instance, 'applications.capabilities').getCurrent()\n return resolveTopic(\n instance,\n capabilities === undefined ? 'applications.capabilities' : 'favorites.documents',\n )\n}\n\n/**\n * Sets a document's favorite state over the dashboard message bus. The message bus counterpart\n * to `setFavorite`; the host keeps `favorites.documents` current, so there is no cache to\n * invalidate.\n *\n * @internal\n */\nexport const setBusFavorite = defineMutation<SetFavoriteInput, FavoriteStatusResponse>({\n name: 'setBusFavorite',\n mutationFn:\n (instance) =>\n ({isFavorited, ...context}) =>\n defer(() =>\n requireDashboardMessageBus(instance, 'update a favorite').emit(\n 'favorites.update',\n {document: toFavoriteDocument(context), favorited: isFavorited},\n // Matches the reply timeout of the Comlink write.\n {timeout: FETCH_TIMEOUT_DEFAULT},\n ),\n ).pipe(map(() => ({isFavorited}))),\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'\n\nimport {getClient} from '../client/clientStore'\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {isObject} from '../utils/object'\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 resource - Data resource for the preview\n * @internal\n */\nexport function transformProjectionToPreview(\n instance: SanityInstance,\n projectionResult: PreviewQueryResult,\n resource?: DocumentResource,\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 resource (if provided) or use the instance config\n const client = getClient(instance, {\n apiVersion: API_VERSION,\n resource,\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","/**\n * Telemetry environment classification.\n *\n * - `'development'` — the SDK is running on the local developer's machine\n * (`localhost` / `127.0.0.1` in the browser, or `NODE_ENV=development`\n * in Node). These are the original dev-only telemetry sessions.\n *\n * - `'production'` — the SDK is running on a production Sanity-controlled\n * domain (Studio deployments on `*.sanity.studio`, the dashboard on\n * `*.sanity.io`) where end users are authenticated Sanity users with\n * Populus consent records. Production telemetry is gated to this\n * allowlist; SDK apps deployed to customer-controlled domains do not\n * emit telemetry. Staging (`*.sanity.work`) and preview\n * (`*.sanity.dev`) hosts are deliberately excluded.\n *\n * @internal\n */\nexport type TelemetryEnvironment = 'development' | 'production'\n\n/**\n * Hostname suffixes that count as Sanity-controlled for the purposes of\n * production telemetry. A user reaching one of these hosts in the\n * browser is authenticated against Sanity and has a Populus consent\n * record, so we apply the same telemetry rules as the Studio's\n * `telemetry-sink`.\n *\n * The leading `.` is required: it ensures suffix matches hit a real\n * subdomain boundary, so apex matches (`sanity.io`, `sanity.studio`)\n * and lookalikes (`evilsanity.studio`) are excluded. Only production\n * hosts are listed; staging (`*.sanity.work`) and preview\n * (`*.sanity.dev`) hosts are excluded by omission.\n *\n * @internal\n */\nconst SANITY_CONTROLLED_HOST_SUFFIXES = ['.sanity.studio', '.sanity.io'] as const\n\nfunction getBrowserHostname(win: Window): string | null {\n const hostname = win.location?.hostname\n return typeof hostname === 'string' && hostname.length > 0 ? hostname.toLowerCase() : null\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '127.0.0.1'\n}\n\nfunction isSanityControlledHostname(hostname: string): boolean {\n return SANITY_CONTROLLED_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix))\n}\n\n/**\n * Returns the telemetry environment for the current runtime, or `null`\n * when telemetry should not run at all.\n *\n * Browser:\n * - `localhost` / `127.0.0.1` → `'development'`\n * - host matches the Sanity-controlled allowlist → `'production'`\n * - anything else (customer domains, unknown contexts) → `null`\n *\n * Node (scripts, SSR, tests):\n * - `NODE_ENV=development` → `'development'`\n * - otherwise → `null`. Production-side server runtimes don't carry the\n * browser-authenticated user/consent assumption, so we don't enable\n * them under the production gate.\n *\n * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler\n * dead-code replacement.\n *\n * @internal\n */\nexport function getTelemetryEnvironment(): TelemetryEnvironment | null {\n if (typeof window !== 'undefined') {\n const hostname = getBrowserHostname(window)\n if (!hostname) return null\n if (isLocalHostname(hostname)) return 'development'\n if (isSanityControlledHostname(hostname)) return 'production'\n return null\n }\n\n if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development') {\n return 'development'\n }\n return null\n}\n\n/**\n * Convenience predicate for \"telemetry can run in this environment\".\n *\n * @internal\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryEnvironment() !== null\n}\n","import {defineEvent} from '@sanity/telemetry'\n\n/**\n * Whether the instance has Studio configuration. Apps include Dashboard and standalone apps.\n * This describes instance configuration, not credential source or who authored a hook call.\n * @internal\n */\nexport type TelemetryRuntimeContext = 'studio' | 'app'\n\n// Keep attribution in event data to preserve the warehouse's data_auth_method convention.\ninterface TelemetryAttribution {\n /** Configured credential source (token/studio/default), distinct from getAuthMethodState's transport. */\n authMethod: string\n runtimeContext: TelemetryRuntimeContext\n}\n\n/** @internal */\nexport const SDKSessionStarted = defineEvent<\n TelemetryAttribution & {\n version: string\n projectId: string\n perspective: string\n }\n>({\n name: 'SDK Session Started',\n version: 1,\n description: 'SDK instance created (environment is recorded in the event context)',\n})\n\n/** @internal */\nexport const SDKHookMounted = defineEvent<\n TelemetryAttribution & {\n hookName: string\n }\n>({\n name: 'SDK Hook Mounted',\n version: 1,\n description: 'An SDK hook was mounted for the first time in this session',\n})\n\n/** @internal */\nexport const SDKSessionEnded = defineEvent<\n TelemetryAttribution & {\n durationSeconds: number\n hooksUsed: string[]\n }\n>({\n name: 'SDK Session Ended',\n version: 1,\n description: 'SDK instance disposed (environment is recorded in the event context)',\n})\n\n/** @internal */\nexport const SDKError = defineEvent<{\n errorType: string\n hookName: string\n}>({\n name: 'SDK Error',\n version: 1,\n description: 'Runtime error caught in the SDK',\n})\n","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 {type TelemetryEnvironment} from './environment'\nimport {\n SDKError,\n SDKHookMounted,\n SDKSessionEnded,\n SDKSessionStarted,\n type TelemetryRuntimeContext,\n} from './events'\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 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 * The `environment` is captured at construction time and recorded in\n * the event context so downstream pipelines can distinguish dev and\n * production sessions.\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 Session Started\" event */\n logSessionStarted(data: {projectId: string; perspective: string}): void\n\n /** Log a \"SDK Hook Mounted\" event (deduplicated per hook name) */\n logHookFirstUsed(hookName: string): void\n\n /** Log a \"SDK Error\" event */\n logError(errorType: string, hookName: string): void\n\n /** Log a \"SDK 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 environment: TelemetryEnvironment\n authMethod: string\n runtimeContext: TelemetryRuntimeContext\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, environment, authMethod, runtimeContext} = 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 url: '/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 // Trace events (`trace.start` / `trace.log` / `trace.error` /\n // `trace.complete`) arrive with their own caller-provided `context`.\n // Log / userProperties events don't. Narrow via `in` so `event`\n // stays typed as `TelemetryEvent`, then merge so we don't drop the\n // trace context. SDK-owned fields below win on conflict.\n const existing =\n 'context' in event ? (event.context as Record<string, unknown> | undefined) : undefined\n return {\n ...event,\n context: {\n ...existing,\n version: CORE_SDK_VERSION,\n environment,\n origin: typeof window !== 'undefined' ? window.location.origin : 'node',\n },\n }\n })\n\n const sendEvents = async (batch: TelemetryEvent[]): Promise<unknown> => {\n const client = getClient()\n log.debug('sending event batch', {batchSize: batch.length, environment})\n return client.request({\n url: '/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 Session Started', {\n projectId: data.projectId,\n perspective: data.perspective,\n authMethod,\n runtimeContext,\n version: CORE_SDK_VERSION,\n environment,\n })\n logger.log(SDKSessionStarted, {\n version: CORE_SDK_VERSION,\n ...data,\n authMethod,\n runtimeContext,\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, authMethod, runtimeContext})\n logger.log(SDKHookMounted, {hookName, authMethod, runtimeContext})\n },\n\n logError(errorType: string, hookName: string) {\n log.debug('event: SDK Error', {errorType, hookName})\n logger.log(SDKError, {errorType, hookName})\n },\n\n endSession() {\n const durationSeconds = Math.round((Date.now() - startedAt) / 1000)\n log.debug('event: SDK Session Ended', {\n durationSeconds,\n hooksUsed: [...emittedHooks],\n authMethod,\n runtimeContext,\n environment,\n })\n logger.log(SDKSessionEnded, {\n durationSeconds,\n hooksUsed: [...emittedHooks],\n authMethod,\n runtimeContext,\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","import {type SanityInstance} from '../store/createSanityInstance'\nimport {createLogger} from '../utils/logger'\nimport {getTelemetryEnvironment} from './environment'\nimport {type TelemetryManager} from './telemetryManager'\n\nconst DEFAULT_TELEMETRY_API_VERSION = '2024-11-12'\n\nconst logger = createLogger('telemetry')\n\n/**\n * Per-instance map of active telemetry managers. Allows the React\n * package (and other consumers) to look up the manager for a given\n * instance and log hook usage / errors without importing the full\n * telemetry module themselves.\n *\n * @internal\n */\nconst telemetryManagers = new WeakMap<SanityInstance, TelemetryManager>()\nconst pendingHooks = new WeakMap<SanityInstance, Set<string>>()\nconst initInFlight = new WeakSet<SanityInstance>()\n\n/**\n * Initializes telemetry for a SDK instance if the runtime environment\n * qualifies. The environment is resolved by `getTelemetryEnvironment()`:\n *\n * - `'development'` — local dev servers (`localhost` / `127.0.0.1`, or\n * Node with `NODE_ENV=development`). This is the original opt-in\n * surface.\n * - `'production'` — apps deployed to Sanity-controlled domains\n * (e.g. `*.sanity.studio`, the dashboard). End users are\n * authenticated Sanity users with Populus consent records, so we\n * apply the same consent gate as the Studio's `telemetry-sink`.\n *\n * Apps on customer-controlled domains return `null` and skip telemetry\n * entirely.\n *\n * `telemetryManager` and `clientStore` are dynamically imported so the\n * telemetry code path stays out of production bundles for apps that\n * don't qualify. Only the lightweight environment check runs at boot.\n *\n * The `projectId` must be passed explicitly because the resource\n * configuration is typically set by the React layer after the\n * instance has already been created.\n *\n * @internal\n */\nexport function initTelemetry(instance: SanityInstance, projectId: string): void {\n const environment = getTelemetryEnvironment()\n if (!environment) {\n logger.trace('initTelemetry skipped: environment not eligible', {internal: true})\n return\n }\n if (!projectId) {\n logger.trace('initTelemetry skipped: no projectId', {internal: true})\n return\n }\n if (telemetryManagers.has(instance) || initInFlight.has(instance)) {\n return\n }\n initInFlight.add(instance)\n\n logger.debug('initializing telemetry', {projectId, environment})\n\n Promise.all([\n import('./telemetryManager'),\n import('../client/clientStore'),\n import('../auth/authStore'),\n import('../auth/authMode'),\n ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}, {isStudioConfig}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n // The token observable is a `shareReplay({bufferSize: 1, refCount: true})`\n // (see `createStateSourceAction`), so it can deliver a buffered value\n // synchronously while we're still inside `.subscribe(cb)`. At that\n // moment `sub` is in the TDZ, so the callback can't reach it. We\n // gate on a `received` flag and let the post-subscribe block do the\n // unsubscribe in the sync-emission case.\n let received = false\n const sub = getTokenState(instance).observable.subscribe((t) => {\n if (received || !t) return\n received = true\n logger.debug('auth token received')\n unsub()\n resolve(true)\n cleanup.unsubscribe()\n })\n cleanup.unsubscribe = () => sub.unsubscribe()\n if (received) cleanup.unsubscribe()\n })\n if (!hasToken || instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: no token resolved or instance disposed')\n return\n }\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n // Studio instances can use explicit tokens. Keep runtime context independent of auth.\n const runtimeContext = isStudioConfig(config) ? 'studio' : 'app'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n const manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n environment,\n authMethod,\n runtimeContext,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n logger.info('telemetry session started', {\n projectId,\n perspective,\n authMethod,\n runtimeContext,\n environment,\n })\n manager.logSessionStarted({\n projectId,\n perspective,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!getTelemetryEnvironment()) return\n\n const manager = findManager(instance)\n if (manager) {\n logger.trace('hook mounted (logged)', {hookName, internal: true})\n manager.logHookFirstUsed(hookName)\n return\n }\n\n const root = getRootInstance(instance)\n let hooks = pendingHooks.get(root)\n if (!hooks) {\n hooks = new Set()\n pendingHooks.set(root, hooks)\n }\n if (!hooks.has(hookName)) {\n logger.trace('hook mounted (buffered)', {hookName, internal: true})\n }\n hooks.add(hookName)\n}\n\nfunction findManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\nfunction getRootInstance(instance: SanityInstance): SanityInstance {\n return instance\n}\n","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 * backslashes and 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 backslashes and double quotes within the final search term for the\n // GROQ query. Backslashes go first, so the escapes added for quotes are not\n // themselves escaped. Without this a trailing backslash would escape the\n // closing quote and let the input break out of the string literal.\n const escapedSearch = wildcardSearch.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n\n // Construct the final GROQ filter clause\n return `[@] match text::query(\"${escapedSearch}\")`\n}\n"],"mappings":"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"}
|