@sanity/sdk 3.0.0-rc.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +12 -0
  2. package/dist/_exports/_internal.d.ts +71 -3
  3. package/dist/_exports/_internal.d.ts.map +1 -1
  4. package/dist/_exports/_internal.js +332 -24
  5. package/dist/_exports/_internal.js.map +1 -1
  6. package/dist/_exports/agent.d.ts +65 -0
  7. package/dist/_exports/agent.d.ts.map +1 -0
  8. package/dist/_exports/agent.js +71 -0
  9. package/dist/_exports/agent.js.map +1 -0
  10. package/dist/_exports/comlink.d.ts +118 -0
  11. package/dist/_exports/comlink.d.ts.map +1 -0
  12. package/dist/_exports/comlink.js +69 -0
  13. package/dist/_exports/comlink.js.map +1 -0
  14. package/dist/authStore-C0o1NOxW.d.ts +3165 -0
  15. package/dist/authStore-C0o1NOxW.d.ts.map +1 -0
  16. package/dist/authStore-CtE9bLHW.js +754 -0
  17. package/dist/authStore-CtE9bLHW.js.map +1 -0
  18. package/dist/clientStore-BtE2-q2i.js +90 -0
  19. package/dist/clientStore-BtE2-q2i.js.map +1 -0
  20. package/dist/createSanityInstance-DUI1bGIg.d.ts +278 -0
  21. package/dist/createSanityInstance-DUI1bGIg.d.ts.map +1 -0
  22. package/dist/createStateSourceAction-BOKtqFNm.js +300 -0
  23. package/dist/createStateSourceAction-BOKtqFNm.js.map +1 -0
  24. package/dist/getEnv-D5RGkQLu.js +8 -0
  25. package/dist/getEnv-D5RGkQLu.js.map +1 -0
  26. package/dist/getNodeState-CR3__B2D.js +67 -0
  27. package/dist/getNodeState-CR3__B2D.js.map +1 -0
  28. package/dist/index.d.ts +3 -2335
  29. package/dist/index.js +4048 -3878
  30. package/dist/index.js.map +1 -1
  31. package/dist/logger-DAtJb5RQ.js +173 -0
  32. package/dist/logger-DAtJb5RQ.js.map +1 -0
  33. package/dist/object-CxXlSVg7.js +83 -0
  34. package/dist/object-CxXlSVg7.js.map +1 -0
  35. package/dist/queryStore-C7yMHPUI.js +1346 -0
  36. package/dist/queryStore-C7yMHPUI.js.map +1 -0
  37. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  38. package/dist/setCleanupTimeout-BiM6q1KF.js +31 -0
  39. package/dist/setCleanupTimeout-BiM6q1KF.js.map +1 -0
  40. package/dist/telemetryManager-UIEiMtC0.js +127 -0
  41. package/dist/telemetryManager-UIEiMtC0.js.map +1 -0
  42. package/dist/version-D8RP_Q59.js +9 -0
  43. package/dist/version-D8RP_Q59.js.map +1 -0
  44. package/package.json +11 -17
  45. package/src/_exports/_internal.ts +1 -0
  46. package/src/_exports/agent.ts +19 -0
  47. package/src/_exports/comlink.ts +16 -0
  48. package/src/_exports/index.ts +45 -90
  49. package/src/applications/applications.test-d.ts +5 -0
  50. package/src/applications/applications.ts +5 -2
  51. package/src/auth/authMode.test.ts +0 -25
  52. package/src/auth/authMode.ts +3 -6
  53. package/src/auth/authStore.test.ts +1 -1
  54. package/src/auth/authStore.ts +0 -1
  55. package/src/comments/addonDatasetStore.test.ts +215 -0
  56. package/src/comments/addonDatasetStore.ts +249 -0
  57. package/src/comments/buildCommentThreads.test.ts +83 -0
  58. package/src/comments/buildCommentThreads.ts +54 -0
  59. package/src/comments/commentActions.test.ts +540 -0
  60. package/src/comments/commentActions.ts +555 -0
  61. package/src/comments/commentFieldPath.test.ts +40 -0
  62. package/src/comments/commentFieldPath.ts +44 -0
  63. package/src/comments/commentsConstants.ts +80 -0
  64. package/src/comments/commentsStore.test.ts +419 -0
  65. package/src/comments/commentsStore.ts +483 -0
  66. package/src/comments/normalizeComment.test.ts +116 -0
  67. package/src/comments/normalizeComment.ts +45 -0
  68. package/src/comments/observeComments.test.ts +193 -0
  69. package/src/comments/observeComments.ts +113 -0
  70. package/src/comments/reducers.test.ts +277 -0
  71. package/src/comments/reducers.ts +316 -0
  72. package/src/comments/types.ts +249 -0
  73. package/src/comments/weakenReferences.test.ts +47 -0
  74. package/src/comments/weakenReferences.ts +29 -0
  75. package/src/config/sanityConfig.ts +3 -75
  76. package/src/datasets/datasets.test.ts +44 -9
  77. package/src/datasets/datasets.ts +24 -23
  78. package/src/document/applyDocumentActions.test.ts +8 -12
  79. package/src/document/documentConstants.ts +17 -0
  80. package/src/document/documentStore.test.ts +173 -19
  81. package/src/document/documentStore.ts +36 -5
  82. package/src/document/patchOperations.test.ts +27 -0
  83. package/src/document/patchOperations.ts +10 -1
  84. package/src/document/permissions.test.ts +3 -1
  85. package/src/document/reducers.test.ts +357 -0
  86. package/src/document/reducers.ts +125 -27
  87. package/src/document/util.test.ts +46 -0
  88. package/src/document/util.ts +37 -1
  89. package/src/favorites/favorites.test.ts +64 -24
  90. package/src/favorites/favorites.ts +73 -22
  91. package/src/installations/installations.test-d.ts +5 -0
  92. package/src/installations/installations.ts +4 -0
  93. package/src/presence/bifurTransport.test.ts +155 -3
  94. package/src/presence/bifurTransport.ts +79 -12
  95. package/src/presence/paths.test.ts +70 -0
  96. package/src/presence/paths.ts +41 -0
  97. package/src/presence/presenceStore.test.ts +822 -2
  98. package/src/presence/presenceStore.ts +518 -102
  99. package/src/presence/types.ts +175 -3
  100. package/src/preview/types.ts +0 -25
  101. package/src/projection/projectionStore.test.ts +0 -8
  102. package/src/projection/types.ts +0 -8
  103. package/src/query/queryStore.ts +7 -6
  104. package/src/store/createActionBinder.test.ts +51 -29
  105. package/src/store/createActionBinder.ts +16 -62
  106. package/src/store/createSanityInstance.test.ts +0 -68
  107. package/src/store/createSanityInstance.ts +0 -63
  108. package/src/store/createStateSourceAction.ts +1 -1
  109. package/src/store/fetcherStore.test.ts +23 -1
  110. package/src/store/fetcherStore.ts +3 -3
  111. package/src/users/reducers.test.ts +0 -3
  112. package/dist/_chunks-dts/createGroqSearchFilter.d.ts +0 -1147
  113. package/dist/_chunks-dts/createGroqSearchFilter.d.ts.map +0 -1
  114. package/dist/_chunks-es/_internal.js +0 -134
  115. package/dist/_chunks-es/_internal.js.map +0 -1
  116. package/dist/_chunks-es/createGroqSearchFilter.js +0 -1864
  117. package/dist/_chunks-es/createGroqSearchFilter.js.map +0 -1
  118. package/dist/_chunks-es/telemetryManager.js +0 -93
  119. package/dist/_chunks-es/telemetryManager.js.map +0 -1
  120. package/dist/index.d.ts.map +0 -1
  121. package/src/applications/userApplications.test.ts +0 -126
  122. package/src/applications/userApplications.ts +0 -261
  123. package/src/preview/getPreviewState.test.ts +0 -137
  124. package/src/preview/getPreviewState.ts +0 -69
  125. package/src/preview/resolvePreview.test.ts +0 -64
  126. package/src/preview/resolvePreview.ts +0 -43
  127. package/src/utils/createFetcherStore.test.ts +0 -171
  128. package/src/utils/createFetcherStore.ts +0 -284
package/README.md CHANGED
@@ -9,6 +9,18 @@ The App SDK Core is a pure TypeScript implementation of the App SDK’s business
9
9
 
10
10
  We highly recommend that users default to using the hooks provided by the React SDK for building custom apps on the Sanity platform, unless you’re looking to create your own Sanity App SDK using this core layer.
11
11
 
12
+ ## Entry points
13
+
14
+ The package exposes multiple entry points:
15
+
16
+ | Entry point | Contents |
17
+ | --------------------- | ----------------------------------------------------------------------- |
18
+ | `@sanity/sdk` | Core SDK — auth, documents, queries, presence, projects, users, etc. |
19
+ | `@sanity/sdk/agent` | AI agent utilities — `agentGenerate`, `agentPatch`, `agentPrompt`, etc. |
20
+ | `@sanity/sdk/comlink` | Comlink channel/controller/node utilities and message types |
21
+
22
+ `@sanity/sdk-react` re-exports the main `@sanity/sdk` entry point only. Import the agent and comlink utilities from their sub-entries directly.
23
+
12
24
  **Looking for our React SDK?** You’ll find it on:
13
25
 
14
26
  - [GitHub](https://github.com/sanity-io/sdk/tree/main/packages/react)
@@ -1,5 +1,49 @@
1
- import { A as transformProjectionToPreview, B as getClientErrorApiBody, E as parseQueryKey, H as getClientErrorApiType, R as PREVIEW_PROJECTION, S as defineMutation, U as isProjectUserNotFoundClientError, V as getClientErrorApiDescription, _ as FetcherDefinition, b as MutationResult, ct as isStudioConfig, g as Fetcher, h as CacheWriter, m as CacheTag, n as getUsersKey, ot as SanityInstance, r as parseUsersKey, t as createGroqSearchFilter, v as FetcherSnapshot, w as getQueryKey, x as defineFetcher, y as MutationDefinition, z as ApiErrorBody } from "../_chunks-dts/createGroqSearchFilter.js";
2
- import { SanityClient } from "@sanity/client";
1
+ import { m as SanityConfig, s as DocumentResource, t as SanityInstance } from "../createSanityInstance-DUI1bGIg.js";
2
+ import { B as parseQueryKey, R as getQueryKey, Si as defineMutation, _i as FetcherDefinition, bi as MutationResult, et as PreviewQueryResult, gi as Fetcher, gr as getCommentsOptionsKey, hi as CacheWriter, mi as CacheTag, tt as PreviewValue, vi as FetcherSnapshot, vr as parseCommentsOptionsKey, xi as defineFetcher, yi as MutationDefinition, yt as GetUsersOptions } from "../authStore-C0o1NOxW.js";
3
+ import { ClientError, SanityClient } from "@sanity/client";
4
+ import { Observable } from "rxjs";
5
+ import { CurrentUser } from "@sanity/types";
6
+ /**
7
+ * Returns `true` when the config indicates the SDK is running inside a Studio.
8
+ *
9
+ * @internal
10
+ */
11
+ declare function isStudioConfig(config: SanityConfig): boolean;
12
+ /** @internal */
13
+ type ApiErrorBody = {
14
+ error?: {
15
+ type?: string;
16
+ description?: string;
17
+ };
18
+ type?: string;
19
+ description?: string;
20
+ message?: string;
21
+ };
22
+ /** @internal Extracts the structured API error body from a ClientError, if present. */
23
+ declare function getClientErrorApiBody(error: ClientError): ApiErrorBody | undefined;
24
+ /** @internal Returns the error type string from an API error body, if available. */
25
+ declare function getClientErrorApiType(error: ClientError): string | undefined;
26
+ /** @internal Returns the error description string from an API error body, if available. */
27
+ declare function getClientErrorApiDescription(error: ClientError): string | undefined;
28
+ /** @internal True if the error represents a projectUserNotFoundError. */
29
+ declare function isProjectUserNotFoundClientError(error: ClientError): boolean;
30
+ /**
31
+ * Generates a GROQ projection for preview data without requiring a schema.
32
+ * Uses common field names to make educated guesses about which fields to use.
33
+ *
34
+ * @internal
35
+ */
36
+ declare const PREVIEW_PROJECTION: string;
37
+ /**
38
+ * Transforms a projection result (with titleCandidates, subtitleCandidates, media)
39
+ * into a PreviewValue (with title, subtitle, media).
40
+ *
41
+ * @param projectionResult - The raw projection result from GROQ
42
+ * @param instance - The Sanity instance to use for client configuration
43
+ * @param resource - Data resource for the preview
44
+ * @internal
45
+ */
46
+ declare function transformProjectionToPreview(instance: SanityInstance, projectionResult: PreviewQueryResult, resource?: DocumentResource): PreviewValue;
3
47
  /**
4
48
  * Manages telemetry for a single SDK instance.
5
49
  *
@@ -77,6 +121,30 @@ declare function getTelemetryManager(instance: SanityInstance): TelemetryManager
77
121
  * @internal
78
122
  */
79
123
  declare function trackHookMounted(instance: SanityInstance, hookName: string): void;
124
+ /** @internal */
125
+ declare const getUsersKey: (instance: SanityInstance, { resourceType, organizationId, batchSize, projectId, userId }?: GetUsersOptions) => string;
126
+ /** @internal */
127
+ declare const parseUsersKey: (key: string) => {
128
+ batchSize: number;
129
+ resourceType?: "organization" | "project";
130
+ projectId?: string;
131
+ organizationId?: string;
132
+ userId?: string;
133
+ };
134
+ /**
135
+ * Creates a GROQ search filter string (`[@] match text::query("...")`)
136
+ * from a raw search query string.
137
+ *
138
+ * It applies wildcard ('*') logic to the last eligible token and escapes
139
+ * double quotes within the search term.
140
+ *
141
+ * If the input query is empty or only whitespace, it returns an empty string.
142
+ *
143
+ * @param query - The raw input search string.
144
+ * @returns The GROQ search filter string, or an empty string.
145
+ * @internal
146
+ */
147
+ declare function createGroqSearchFilter(query: string): string;
80
148
  /**
81
149
  * Creates a shallow copy containing only the provided properties.
82
150
  *
@@ -91,5 +159,5 @@ declare function pickProperties<T extends object, K extends keyof T>(value: T, k
91
159
  * @internal
92
160
  */
93
161
  declare function isDeepEqual<T>(left: T, right: T): boolean;
94
- export { type ApiErrorBody, type CacheTag, type CacheWriter, type Fetcher, type FetcherDefinition, type FetcherSnapshot, type MutationDefinition, type MutationResult, PREVIEW_PROJECTION, createGroqSearchFilter, defineFetcher, defineMutation, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType, getQueryKey, getTelemetryManager, getUsersKey, initTelemetry, isDeepEqual, isProjectUserNotFoundClientError, isStudioConfig, parseQueryKey, parseUsersKey, pickProperties, trackHookMounted, transformProjectionToPreview };
162
+ export { type ApiErrorBody, type CacheTag, type CacheWriter, type Fetcher, type FetcherDefinition, type FetcherSnapshot, type MutationDefinition, type MutationResult, PREVIEW_PROJECTION, createGroqSearchFilter, defineFetcher, defineMutation, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType, getCommentsOptionsKey, getQueryKey, getTelemetryManager, getUsersKey, initTelemetry, isDeepEqual, isProjectUserNotFoundClientError, isStudioConfig, parseCommentsOptionsKey, parseQueryKey, parseUsersKey, pickProperties, trackHookMounted, transformProjectionToPreview };
95
163
  //# sourceMappingURL=_internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_internal.d.ts","names":[],"sources":["../../src/telemetry/telemetryManager.ts","../../src/telemetry/initTelemetry.ts","../../src/utils/object.ts"],"mappings":";;;;;;;;;;;;;UAgCiB;;;;;;;EAOf,gBAAgB;;EAGhB,kBAAkB;IAAO;IAAmB;IAAqB;;;EAGjE,iBAAiB;;EAGjB,SAAS,mBAAmB;;EAG5B;;EAGA;;WAGS,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCXN,cAAc,UAAU,gBAAgB;;;;;;;iBAoIxC,oBAAoB,UAAU,iBAAiB;;;;;;;;iBAW/C,iBAAiB,UAAU,gBAAgB;;;;;;iBCvJ3C,eAAe,kBAAkB,gBAAgB,GAC/D,OAAO,GACP,eAAe,MACd,KAAK,GAAG;;;;;;;;iBA2DK,YAAY,GAAG,MAAM,GAAG,OAAO"}
1
+ {"version":3,"file":"_internal.d.ts","names":[],"sources":["../../src/auth/authMode.ts","../../src/auth/utils.ts","../../src/preview/previewConstants.ts","../../src/preview/previewProjectionUtils.ts","../../src/telemetry/telemetryManager.ts","../../src/telemetry/initTelemetry.ts","../../src/users/reducers.ts","../../src/utils/createGroqSearchFilter.ts","../../src/utils/object.ts"],"mappings":";;;;;;;;;;iBAsCgB,eAAe,QAAQ;;KC0I3B;EACV;IAAS;IAAe;;EACxB;EACA;EACA;;;iBAIc,sBAAsB,OAAO,cAAc;;iBAM3C,sBAAsB,OAAO;;iBAM7B,6BAA6B,OAAO;;iBAMpC,iCAAiC,OAAO;;;;;;;cCpL3C;;;;;;;;;;iBCiDG,6BACd,UAAU,gBACV,kBAAkB,oBAClB,WAAW,mBACV;;;;;;;;;;;;UC3Cc;;;;;;;EAOf,gBAAgB;;EAGhB,kBAAkB;IAAO;IAAmB;IAAqB;;;EAGjE,iBAAiB;;EAGjB,SAAS,mBAAmB;;EAG5B;;EAGA;;WAGS,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCXN,cAAc,UAAU,gBAAgB;;;;;;;iBAoIxC,oBAAoB,UAAU,iBAAiB;;;;;;;;iBAW/C,iBAAiB,UAAU,gBAAgB;;cCvL9C,cACX,UAAU,kBACV,cAAA,gBAAA,WAAA,WAAA,WAMG;;cAWQ,gBACX;EAEA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;iBCYc,uBAAuB;;;;;;iBCNvB,eAAe,kBAAkB,gBAAgB,GAC/D,OAAO,GACP,eAAe,MACd,KAAK,GAAG;;;;;;;;iBA2DK,YAAY,GAAG,MAAM,GAAG,OAAO"}
@@ -1,24 +1,332 @@
1
- import { PREVIEW_PROJECTION, createGroqSearchFilter, defineFetcher, defineMutation, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType, getQueryKey, getUsersKey, isDeepEqual, isProjectUserNotFoundClientError, isStudioConfig, parseQueryKey, parseUsersKey, pickProperties, transformProjectionToPreview } from "../_chunks-es/createGroqSearchFilter.js";
2
- import { getTelemetryManager, initTelemetry, trackHookMounted } from "../_chunks-es/_internal.js";
3
- export {
4
- PREVIEW_PROJECTION,
5
- createGroqSearchFilter,
6
- defineFetcher,
7
- defineMutation,
8
- getClientErrorApiBody,
9
- getClientErrorApiDescription,
10
- getClientErrorApiType,
11
- getQueryKey,
12
- getTelemetryManager,
13
- getUsersKey,
14
- initTelemetry,
15
- isDeepEqual,
16
- isProjectUserNotFoundClientError,
17
- isStudioConfig,
18
- parseQueryKey,
19
- parseUsersKey,
20
- pickProperties,
21
- trackHookMounted,
22
- transformProjectionToPreview
23
- };
24
- //# sourceMappingURL=_internal.js.map
1
+ import { C as parseCommentsOptionsKey, U as defineFetcher, W as defineMutation, f as parseUsersKey, r as parseQueryKey, t as getQueryKey, u as getUsersKey, x as getCommentsOptionsKey } from "../queryStore-C7yMHPUI.js";
2
+ import { n as createLogger } from "../logger-DAtJb5RQ.js";
3
+ import { g as getClientErrorApiType, h as getClientErrorApiDescription, m as getClientErrorApiBody, x as isStudioConfig, y as isProjectUserNotFoundClientError } from "../authStore-CtE9bLHW.js";
4
+ import { i as pickProperties, n as isObject, t as isDeepEqual } from "../object-CxXlSVg7.js";
5
+ import { n as getClient } from "../clientStore-BtE2-q2i.js";
6
+ import "@sanity/client";
7
+ import { createImageUrlBuilder } from "@sanity/image-url";
8
+ /**
9
+ * The fields to check for a title.
10
+ * The order of the items in the array defines the priority.
11
+ *
12
+ * @internal
13
+ */
14
+ const TITLE_CANDIDATES = [
15
+ "title",
16
+ "name",
17
+ "label",
18
+ "heading",
19
+ "header",
20
+ "caption"
21
+ ], SUBTITLE_CANDIDATES = [
22
+ "description",
23
+ "subtitle",
24
+ ...TITLE_CANDIDATES
25
+ ], PREVIEW_PROJECTION = `{
26
+ // Get all potential title fields
27
+ "titleCandidates": {
28
+ ${TITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(",\n ")}
29
+ },
30
+ // Get all potential subtitle fields
31
+ "subtitleCandidates": {
32
+ ${SUBTITLE_CANDIDATES.map((field) => `"${field}": ${field}`).join(",\n ")}
33
+ },
34
+ "media": coalesce(
35
+ select(
36
+ defined(asset) => {"type": "image-asset", "_ref": asset._ref},
37
+ defined(image.asset) => {"type": "image-asset", "_ref": image.asset._ref},
38
+ defined(mainImage.asset) => {"type": "image-asset", "_ref": mainImage.asset._ref},
39
+ null
40
+ )
41
+ ),
42
+ _type,
43
+ _id,
44
+ _updatedAt
45
+ }`;
46
+ /**
47
+ * Checks if the provided value has `_ref` property that is a string and starts with `image-`
48
+ */
49
+ function hasImageRef(value) {
50
+ return isObject(value) && "_ref" in value && typeof value._ref == "string";
51
+ }
52
+ /**
53
+ * Normalizes a media asset to a preview value.
54
+ * Adds a url to a media asset reference using `@sanity/image-url`.
55
+ *
56
+ * @internal
57
+ */
58
+ function normalizeMedia(media, client) {
59
+ if (!media || !hasImageRef(media)) return null;
60
+ let url = createImageUrlBuilder(client).image({ _ref: media._ref }).url();
61
+ return {
62
+ type: "image-asset",
63
+ _ref: media._ref,
64
+ url
65
+ };
66
+ }
67
+ /**
68
+ * Finds a single field value from a set of candidates based on a priority list of field names.
69
+ * Returns the first non-empty string value found from the candidates matching the priority list order.
70
+ *
71
+ * @internal
72
+ */
73
+ function findFirstDefined(fieldsToSearch, candidates, exclude) {
74
+ if (candidates) for (let field of fieldsToSearch) {
75
+ let value = candidates[field];
76
+ if (typeof value == "string" && value.trim() !== "" && value !== exclude) return value;
77
+ }
78
+ }
79
+ /**
80
+ * Transforms a projection result (with titleCandidates, subtitleCandidates, media)
81
+ * into a PreviewValue (with title, subtitle, media).
82
+ *
83
+ * @param projectionResult - The raw projection result from GROQ
84
+ * @param instance - The Sanity instance to use for client configuration
85
+ * @param resource - Data resource for the preview
86
+ * @internal
87
+ */
88
+ function transformProjectionToPreview(instance, projectionResult, resource) {
89
+ let title = findFirstDefined(TITLE_CANDIDATES, projectionResult.titleCandidates), subtitle = findFirstDefined(SUBTITLE_CANDIDATES, projectionResult.subtitleCandidates, title), client = getClient(instance, {
90
+ apiVersion: "v2025-05-06",
91
+ resource
92
+ });
93
+ return {
94
+ title: String(title || `${projectionResult._type}: ${projectionResult._id}`),
95
+ subtitle: subtitle || void 0,
96
+ media: normalizeMedia(projectionResult.media, client),
97
+ ...projectionResult._status && { _status: projectionResult._status }
98
+ };
99
+ }
100
+ /**
101
+ * Hostname suffixes that count as Sanity-controlled for the purposes of
102
+ * production telemetry. A user reaching one of these hosts in the
103
+ * browser is authenticated against Sanity and has a Populus consent
104
+ * record, so we apply the same telemetry rules as the Studio's
105
+ * `telemetry-sink`.
106
+ *
107
+ * The leading `.` is required: it ensures suffix matches hit a real
108
+ * subdomain boundary, so apex matches (`sanity.io`, `sanity.studio`)
109
+ * and lookalikes (`evilsanity.studio`) are excluded. Only production
110
+ * hosts are listed; staging (`*.sanity.work`) and preview
111
+ * (`*.sanity.dev`) hosts are excluded by omission.
112
+ *
113
+ * @internal
114
+ */
115
+ const SANITY_CONTROLLED_HOST_SUFFIXES = [".sanity.studio", ".sanity.io"];
116
+ function getBrowserHostname(win) {
117
+ let hostname = win.location?.hostname;
118
+ return typeof hostname == "string" && hostname.length > 0 ? hostname.toLowerCase() : null;
119
+ }
120
+ function isLocalHostname(hostname) {
121
+ return hostname === "localhost" || hostname === "127.0.0.1";
122
+ }
123
+ function isSanityControlledHostname(hostname) {
124
+ return SANITY_CONTROLLED_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix));
125
+ }
126
+ /**
127
+ * Returns the telemetry environment for the current runtime, or `null`
128
+ * when telemetry should not run at all.
129
+ *
130
+ * Browser:
131
+ * - `localhost` / `127.0.0.1` → `'development'`
132
+ * - host matches the Sanity-controlled allowlist → `'production'`
133
+ * - anything else (customer domains, unknown contexts) → `null`
134
+ *
135
+ * Node (scripts, SSR, tests):
136
+ * - `NODE_ENV=development` → `'development'`
137
+ * - otherwise → `null`. Production-side server runtimes don't carry the
138
+ * browser-authenticated user/consent assumption, so we don't enable
139
+ * them under the production gate.
140
+ *
141
+ * Bracket-notation `process.env['NODE_ENV']` is used to avoid bundler
142
+ * dead-code replacement.
143
+ *
144
+ * @internal
145
+ */
146
+ function getTelemetryEnvironment() {
147
+ if (typeof window < "u") {
148
+ let hostname = getBrowserHostname(window);
149
+ return hostname ? isLocalHostname(hostname) ? "development" : isSanityControlledHostname(hostname) ? "production" : null : null;
150
+ }
151
+ return typeof process < "u" && process.env?.NODE_ENV === "development" ? "development" : null;
152
+ }
153
+ const logger = createLogger("telemetry"), telemetryManagers = /* @__PURE__ */ new WeakMap(), pendingHooks = /* @__PURE__ */ new WeakMap(), initInFlight = /* @__PURE__ */ new WeakSet();
154
+ /**
155
+ * Initializes telemetry for a SDK instance if the runtime environment
156
+ * qualifies. The environment is resolved by `getTelemetryEnvironment()`:
157
+ *
158
+ * - `'development'` — local dev servers (`localhost` / `127.0.0.1`, or
159
+ * Node with `NODE_ENV=development`). This is the original opt-in
160
+ * surface.
161
+ * - `'production'` — apps deployed to Sanity-controlled domains
162
+ * (e.g. `*.sanity.studio`, the dashboard). End users are
163
+ * authenticated Sanity users with Populus consent records, so we
164
+ * apply the same consent gate as the Studio's `telemetry-sink`.
165
+ *
166
+ * Apps on customer-controlled domains return `null` and skip telemetry
167
+ * entirely.
168
+ *
169
+ * `telemetryManager` and `clientStore` are dynamically imported so the
170
+ * telemetry code path stays out of production bundles for apps that
171
+ * don't qualify. Only the lightweight environment check runs at boot.
172
+ *
173
+ * The `projectId` must be passed explicitly because the resource
174
+ * configuration is typically set by the React layer after the
175
+ * instance has already been created.
176
+ *
177
+ * @internal
178
+ */
179
+ function initTelemetry(instance, projectId) {
180
+ let environment = getTelemetryEnvironment();
181
+ if (!environment) {
182
+ logger.trace("initTelemetry skipped: environment not eligible", { internal: !0 });
183
+ return;
184
+ }
185
+ if (!projectId) {
186
+ logger.trace("initTelemetry skipped: no projectId", { internal: !0 });
187
+ return;
188
+ }
189
+ telemetryManagers.has(instance) || initInFlight.has(instance) || (initInFlight.add(instance), logger.debug("initializing telemetry", {
190
+ projectId,
191
+ environment
192
+ }), Promise.all([
193
+ import("../telemetryManager-UIEiMtC0.js").then((n) => n.t),
194
+ import("../clientStore-BtE2-q2i.js").then((n) => n.t),
195
+ import("../authStore-CtE9bLHW.js").then((n) => n.n)
196
+ ]).then(async ([{ createTelemetryManager }, { getClient }, { getTokenState }]) => {
197
+ if (instance.isDisposed()) {
198
+ initInFlight.delete(instance), logger.debug("telemetry skipped: instance disposed before imports resolved");
199
+ return;
200
+ }
201
+ let token = getTokenState(instance).getCurrent();
202
+ if (logger.trace("auth token check", {
203
+ tokenPresent: !!token,
204
+ internal: !0
205
+ }), !token && (logger.debug("waiting for auth token"), !await new Promise((resolve) => {
206
+ if (instance.isDisposed()) return resolve(!1);
207
+ let cleanup = { unsubscribe: () => {} }, unsub = instance.onDispose(() => {
208
+ cleanup.unsubscribe(), resolve(!1);
209
+ }), received = !1, sub = getTokenState(instance).observable.subscribe((t) => {
210
+ received || !t || (received = !0, logger.debug("auth token received"), unsub(), resolve(!0), cleanup.unsubscribe());
211
+ });
212
+ cleanup.unsubscribe = () => sub.unsubscribe(), received && cleanup.unsubscribe();
213
+ }) || instance.isDisposed())) {
214
+ initInFlight.delete(instance), logger.debug("telemetry skipped: no token resolved or instance disposed");
215
+ return;
216
+ }
217
+ let manager = createTelemetryManager({
218
+ sessionId: instance.instanceId,
219
+ getClient: () => getClient(instance, { apiVersion: "2024-11-12" }),
220
+ projectId,
221
+ environment
222
+ }), consented = await manager.checkConsent();
223
+ if (logger.debug("consent check complete", { consented }), !consented || instance.isDisposed()) {
224
+ initInFlight.delete(instance), manager.dispose();
225
+ return;
226
+ }
227
+ initInFlight.delete(instance), telemetryManagers.set(instance, manager);
228
+ let buffered = pendingHooks.get(instance);
229
+ if (buffered) {
230
+ logger.debug("flushing buffered hooks", { hooks: Array.from(buffered) });
231
+ for (let hookName of buffered) manager.logHookFirstUsed(hookName);
232
+ pendingHooks.delete(instance);
233
+ }
234
+ let config = instance.config, perspective = typeof config.perspective == "string" ? config.perspective : "published", authMethod = config.auth?.token ? "token" : config.studio?.auth?.token ? "studio" : "default";
235
+ logger.info("telemetry session started", {
236
+ projectId,
237
+ perspective,
238
+ authMethod,
239
+ environment
240
+ }), manager.logSessionStarted({
241
+ projectId,
242
+ perspective,
243
+ authMethod
244
+ }), instance.onDispose(() => {
245
+ manager.endSession(), telemetryManagers.delete(instance), logger.debug("telemetry session ended");
246
+ });
247
+ }).catch((err) => {
248
+ initInFlight.delete(instance), logger.warn("telemetry init failed", { error: err });
249
+ }));
250
+ }
251
+ /**
252
+ * Retrieves the telemetry manager for an instance, if one exists.
253
+ * Returns undefined when telemetry is disabled or not yet initialized.
254
+ *
255
+ * @internal
256
+ */
257
+ function getTelemetryManager(instance) {
258
+ return telemetryManagers.get(instance);
259
+ }
260
+ /**
261
+ * Record a hook name for an instance. If the telemetry manager is
262
+ * already initialized the event is logged immediately. Otherwise
263
+ * the name is buffered and flushed when init completes.
264
+ *
265
+ * @internal
266
+ */
267
+ function trackHookMounted(instance, hookName) {
268
+ if (!getTelemetryEnvironment()) return;
269
+ let manager = findManager(instance);
270
+ if (manager) {
271
+ logger.trace("hook mounted (logged)", {
272
+ hookName,
273
+ internal: !0
274
+ }), manager.logHookFirstUsed(hookName);
275
+ return;
276
+ }
277
+ let root = getRootInstance(instance), hooks = pendingHooks.get(root);
278
+ hooks || (hooks = /* @__PURE__ */ new Set(), pendingHooks.set(root, hooks)), hooks.has(hookName) || logger.trace("hook mounted (buffered)", {
279
+ hookName,
280
+ internal: !0
281
+ }), hooks.add(hookName);
282
+ }
283
+ function findManager(instance) {
284
+ return telemetryManagers.get(instance);
285
+ }
286
+ function getRootInstance(instance) {
287
+ return instance;
288
+ }
289
+ const TOKEN_REGEX = /(?:[^\s"]+|"[^"]*")+/g;
290
+ /**
291
+ * @internal
292
+ * Checks if a token starts with the negation character.
293
+ */
294
+ function isNegationToken(token) {
295
+ return token !== void 0 && token.trim().startsWith("-");
296
+ }
297
+ /**
298
+ * @internal
299
+ * Checks if a token ends with the wildcard character.
300
+ */
301
+ function isPrefixToken(token) {
302
+ return token !== void 0 && token.trim().endsWith("*");
303
+ }
304
+ /**
305
+ * @internal
306
+ * Checks if a token is enclosed in double quotes.
307
+ */
308
+ function isExactMatchToken(token) {
309
+ return !!token && token.length >= 2 && token.startsWith("\"") && token.endsWith("\"");
310
+ }
311
+ /**
312
+ * Creates a GROQ search filter string (`[@] match text::query("...")`)
313
+ * from a raw search query string.
314
+ *
315
+ * It applies wildcard ('*') logic to the last eligible token and escapes
316
+ * double quotes within the search term.
317
+ *
318
+ * If the input query is empty or only whitespace, it returns an empty string.
319
+ *
320
+ * @param query - The raw input search string.
321
+ * @returns The GROQ search filter string, or an empty string.
322
+ * @internal
323
+ */
324
+ function createGroqSearchFilter(query) {
325
+ let trimmedQuery = query.trim();
326
+ if (!trimmedQuery) return "";
327
+ let tokens = trimmedQuery.match(TOKEN_REGEX) ?? [], reversedIndex = [...tokens].reverse().findIndex((token) => !isNegationToken(token) && !isExactMatchToken(token)), finalIncrementalTokenIndex = reversedIndex === -1 ? -1 : tokens.length - 1 - reversedIndex, finalIncrementalToken = tokens[finalIncrementalTokenIndex], processedTokens = [...tokens];
328
+ return finalIncrementalToken !== void 0 && !isPrefixToken(finalIncrementalToken) && processedTokens.splice(finalIncrementalTokenIndex, 1, `${finalIncrementalToken}*`), `[@] match text::query("${processedTokens.join(" ").replace(/"/g, "\\\"")}")`;
329
+ }
330
+ export { PREVIEW_PROJECTION, createGroqSearchFilter, defineFetcher, defineMutation, getClientErrorApiBody, getClientErrorApiDescription, getClientErrorApiType, getCommentsOptionsKey, getQueryKey, getTelemetryManager, getUsersKey, initTelemetry, isDeepEqual, isProjectUserNotFoundClientError, isStudioConfig, parseCommentsOptionsKey, parseQueryKey, parseUsersKey, pickProperties, trackHookMounted, transformProjectionToPreview };
331
+
332
+ //# sourceMappingURL=_internal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_internal.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"_internal.js","names":[],"sources":["../../src/preview/previewConstants.ts","../../src/preview/previewProjectionUtils.ts","../../src/telemetry/environment.ts","../../src/telemetry/initTelemetry.ts","../../src/utils/createGroqSearchFilter.ts"],"sourcesContent":["/**\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 {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 ])\n .then(async ([{createTelemetryManager}, {getClient}, {getTokenState}]) => {\n if (instance.isDisposed()) {\n initInFlight.delete(instance)\n logger.debug('telemetry skipped: instance disposed before imports resolved')\n return\n }\n\n // Wait for the auth store to resolve a token. The client needs a\n // Bearer token for the consent check and batch POSTs. If the token\n // is already available (e.g. static token config), this resolves\n // immediately. For OAuth/localStorage discovery it waits for the\n // first emission. For unauthenticated apps the promise never\n // resolves, which is fine since telemetry requires auth anyway.\n const token = getTokenState(instance).getCurrent()\n logger.trace('auth token check', {tokenPresent: !!token, internal: true})\n\n if (!token) {\n logger.debug('waiting for auth token')\n const hasToken = await new Promise<boolean>((resolve) => {\n if (instance.isDisposed()) return resolve(false)\n const cleanup = {unsubscribe: () => {}}\n const unsub = instance.onDispose(() => {\n cleanup.unsubscribe()\n resolve(false)\n })\n // 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 manager = createTelemetryManager({\n sessionId: instance.instanceId,\n getClient: () => getClient(instance, {apiVersion: DEFAULT_TELEMETRY_API_VERSION}),\n projectId,\n environment,\n })\n\n const consented = await manager.checkConsent()\n logger.debug('consent check complete', {consented})\n if (!consented || instance.isDisposed()) {\n initInFlight.delete(instance)\n manager.dispose()\n return\n }\n\n initInFlight.delete(instance)\n telemetryManagers.set(instance, manager)\n\n const buffered = pendingHooks.get(instance)\n if (buffered) {\n logger.debug('flushing buffered hooks', {hooks: Array.from(buffered)})\n for (const hookName of buffered) {\n manager.logHookFirstUsed(hookName)\n }\n pendingHooks.delete(instance)\n }\n\n const config = instance.config\n const perspective = typeof config.perspective === 'string' ? config.perspective : 'published'\n const authMethod = config.auth?.token\n ? 'token'\n : config.studio?.auth?.token\n ? 'studio'\n : 'default'\n\n logger.info('telemetry session started', {projectId, perspective, authMethod, environment})\n manager.logSessionStarted({\n projectId,\n perspective,\n authMethod,\n })\n\n instance.onDispose(() => {\n manager.endSession()\n telemetryManagers.delete(instance)\n logger.debug('telemetry session ended')\n })\n })\n .catch((err) => {\n initInFlight.delete(instance)\n logger.warn('telemetry init failed', {error: err})\n })\n}\n\n/**\n * Retrieves the telemetry manager for an instance, if one exists.\n * Returns undefined when telemetry is disabled or not yet initialized.\n *\n * @internal\n */\nexport function getTelemetryManager(instance: SanityInstance): TelemetryManager | undefined {\n return telemetryManagers.get(instance)\n}\n\n/**\n * Record a hook name for an instance. If the telemetry manager is\n * already initialized the event is logged immediately. Otherwise\n * the name is buffered and flushed when init completes.\n *\n * @internal\n */\nexport function trackHookMounted(instance: SanityInstance, hookName: string): void {\n if (!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 * double quotes within the search term.\n *\n * If the input query is empty or only whitespace, it returns an empty string.\n *\n * @param query - The raw input search string.\n * @returns The GROQ search filter string, or an empty string.\n * @internal\n */\nexport function createGroqSearchFilter(query: string): string {\n // Trim leading/trailing whitespace from the overall query first\n const trimmedQuery = query.trim()\n if (!trimmedQuery) {\n return '' // Return empty if query is empty or just whitespace\n }\n\n // Extract tokens using the regex\n const tokens = trimmedQuery.match(TOKEN_REGEX) ?? []\n\n // Find the index of the last token eligible for wildcard appending\n const reversedTokens = [...tokens].reverse()\n const reversedIndex = reversedTokens.findIndex(\n (token: string) => !isNegationToken(token) && !isExactMatchToken(token),\n )\n const finalIncrementalTokenIndex = reversedIndex === -1 ? -1 : tokens.length - 1 - reversedIndex\n\n // Get the actual token based on the found index\n const finalIncrementalToken = tokens[finalIncrementalTokenIndex]\n\n const processedTokens = [...tokens]\n // If a suitable token was found and it doesn't already end with a wildcard,\n // apply the wildcard.\n if (finalIncrementalToken !== undefined && !isPrefixToken(finalIncrementalToken)) {\n // Replace the identified token with its wildcarded version\n processedTokens.splice(\n finalIncrementalTokenIndex,\n 1,\n `${finalIncrementalToken}${WILDCARD_TOKEN}`,\n )\n }\n\n // Join the tokens back into a space-separated string\n const wildcardSearch = processedTokens.join(' ')\n\n // Escape double quotes within the final search term for the GROQ query\n const escapedSearch = wildcardSearch.replace(/\"/g, '\\\\\"')\n\n // Construct the final GROQ filter clause\n return `[@] match text::query(\"${escapedSearch}\")`\n}\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { s as DocumentResource, t as SanityInstance } from "../createSanityInstance-DUI1bGIg.js";
2
+ import { SanityClient } from "@sanity/client";
3
+ import { Observable } from "rxjs";
4
+ /** @alpha */
5
+ type AgentGenerateOptions = Parameters<SanityClient['observable']['agent']['action']['generate']>[0];
6
+ /** @alpha */
7
+ type AgentTransformOptions = Parameters<SanityClient['observable']['agent']['action']['transform']>[0];
8
+ /** @alpha */
9
+ type AgentTranslateOptions = Parameters<SanityClient['observable']['agent']['action']['translate']>[0];
10
+ /** @alpha */
11
+ type AgentPromptOptions = Parameters<SanityClient['agent']['action']['prompt']>[0];
12
+ /** @alpha */
13
+ type AgentPatchOptions = Parameters<SanityClient['agent']['action']['patch']>[0];
14
+ /** @alpha */
15
+ type AgentGenerateResult = Awaited<ReturnType<SanityClient['observable']['agent']['action']['generate']>>;
16
+ /** @alpha */
17
+ type AgentTransformResult = Awaited<ReturnType<SanityClient['observable']['agent']['action']['transform']>>;
18
+ /** @alpha */
19
+ type AgentTranslateResult = Awaited<ReturnType<SanityClient['observable']['agent']['action']['translate']>>;
20
+ /** @alpha */
21
+ type AgentPromptResult = Awaited<ReturnType<SanityClient['agent']['action']['prompt']>>;
22
+ /** @alpha */
23
+ type AgentPatchResult = Awaited<ReturnType<SanityClient['agent']['action']['patch']>>;
24
+ /**
25
+ * Generates a new document using the agent.
26
+ * @param instance - The Sanity instance.
27
+ * @param options - The options for the agent generate action. See the [Agent Actions API](https://www.sanity.io/docs/agent-actions/introduction) for more details.
28
+ * @returns An Observable emitting the result of the agent generate action.
29
+ * @alpha
30
+ */
31
+ declare function agentGenerate(instance: SanityInstance, options: AgentGenerateOptions, resource?: DocumentResource): AgentGenerateResult;
32
+ /**
33
+ * Transforms a document using the agent.
34
+ * @param instance - The Sanity instance.
35
+ * @param options - The options for the agent transform action. See the [Agent Actions API](https://www.sanity.io/docs/agent-actions/introduction) for more details.
36
+ * @returns An Observable emitting the result of the agent transform action.
37
+ * @alpha
38
+ */
39
+ declare function agentTransform(instance: SanityInstance, options: AgentTransformOptions, resource?: DocumentResource): AgentTransformResult;
40
+ /**
41
+ * Translates a document using the agent.
42
+ * @param instance - The Sanity instance.
43
+ * @param options - The options for the agent translate action. See the [Agent Actions API](https://www.sanity.io/docs/agent-actions/introduction) for more details.
44
+ * @returns An Observable emitting the result of the agent translate action.
45
+ * @alpha
46
+ */
47
+ declare function agentTranslate(instance: SanityInstance, options: AgentTranslateOptions, resource?: DocumentResource): AgentTranslateResult;
48
+ /**
49
+ * Prompts the agent using the same instruction template format as the other actions, but returns text or json instead of acting on a document.
50
+ * @param instance - The Sanity instance.
51
+ * @param options - The options for the agent prompt action. See the [Agent Actions API](https://www.sanity.io/docs/agent-actions/introduction) for more details.
52
+ * @returns An Observable emitting the result of the agent prompt action.
53
+ * @alpha
54
+ */
55
+ declare function agentPrompt(instance: SanityInstance, options: AgentPromptOptions, resource?: DocumentResource): Observable<AgentPromptResult>;
56
+ /**
57
+ * Patches a document using the agent.
58
+ * @param instance - The Sanity instance.
59
+ * @param options - The options for the agent patch action. See the [Agent Actions API](https://www.sanity.io/docs/agent-actions/introduction) for more details.
60
+ * @returns An Observable emitting the result of the agent patch action.
61
+ * @alpha
62
+ */
63
+ declare function agentPatch(instance: SanityInstance, options: AgentPatchOptions, resource?: DocumentResource): Observable<AgentPatchResult>;
64
+ export { type AgentGenerateOptions, type AgentGenerateResult, type AgentPatchOptions, type AgentPatchResult, type AgentPromptOptions, type AgentPromptResult, type AgentTransformOptions, type AgentTransformResult, type AgentTranslateOptions, type AgentTranslateResult, agentGenerate, agentPatch, agentPrompt, agentTransform, agentTranslate };
65
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","names":[],"sources":["../../src/agent/agentActions.ts"],"mappings":";;;;KAUY,uBAAuB,WACjC;;KAIU,wBAAwB,WAClC;;KAIU,wBAAwB,WAClC;;KAIU,qBAAqB,WAAW;;KAGhC,oBAAoB,WAAW;;KAG/B,sBAAsB,QAChC,WAAW;;KAID,uBAAuB,QACjC,WAAW;;KAID,uBAAuB,QACjC,WAAW;;KAID,oBAAoB,QAAQ,WAAW;;KAGvC,mBAAmB,QAAQ,WAAW;;;;;;;;iBASlC,cACd,UAAU,gBACV,SAAS,sBACT,WAAW,mBACV;;;;;;;;iBAaa,eACd,UAAU,gBACV,SAAS,uBACT,WAAW,mBACV;;;;;;;;iBAaa,eACd,UAAU,gBACV,SAAS,uBACT,WAAW,mBACV;;;;;;;;iBAaa,YACd,UAAU,gBACV,SAAS,oBACT,WAAW,mBACV,WAAW;;;;;;;;iBAaE,WACd,UAAU,gBACV,SAAS,mBACT,WAAW,mBACV,WAAW"}