@sanity/client 6.28.2 → 6.28.3-instruct.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/client",
3
- "version": "6.28.2",
3
+ "version": "6.28.3-instruct.1",
4
4
  "description": "Client for retrieving, creating and patching data from Sanity.io",
5
5
  "keywords": [
6
6
  "sanity",
@@ -125,39 +125,39 @@
125
125
  "devDependencies": {
126
126
  "@edge-runtime/types": "^4.0.0",
127
127
  "@edge-runtime/vm": "^5.0.0",
128
- "@eslint/eslintrc": "^3.2.0",
129
- "@eslint/js": "^9.20.0",
130
- "@rollup/plugin-commonjs": "^28.0.2",
131
- "@rollup/plugin-node-resolve": "^16.0.0",
128
+ "@eslint/eslintrc": "^3.3.0",
129
+ "@eslint/js": "^9.22.0",
130
+ "@rollup/plugin-commonjs": "^28.0.3",
131
+ "@rollup/plugin-node-resolve": "^16.0.1",
132
132
  "@sanity/client-latest": "npm:@sanity/client@latest",
133
- "@sanity/pkg-utils": "^7.0.4",
133
+ "@sanity/pkg-utils": "^7.1.1",
134
134
  "@types/json-diff": "^1.0.3",
135
135
  "@types/node": "^22.9.0",
136
- "@typescript-eslint/eslint-plugin": "^8.24.0",
137
- "@typescript-eslint/parser": "^8.24.0",
136
+ "@typescript-eslint/eslint-plugin": "^8.26.1",
137
+ "@typescript-eslint/parser": "^8.26.1",
138
138
  "@vercel/stega": "0.1.2",
139
- "@vitest/coverage-v8": "3.0.5",
140
- "eslint": "^9.20.0",
141
- "eslint-config-prettier": "^10.0.1",
139
+ "@vitest/coverage-v8": "3.0.9",
140
+ "eslint": "^9.22.0",
141
+ "eslint-config-prettier": "^10.1.1",
142
142
  "eslint-formatter-compact": "^8.40.0",
143
143
  "eslint-plugin-prettier": "^5.2.3",
144
144
  "eslint-plugin-simple-import-sort": "^12.1.1",
145
145
  "faucet": "^0.0.4",
146
- "globals": "^15.14.0",
146
+ "globals": "^15.15.0",
147
147
  "happy-dom": "^12.10.3",
148
148
  "json-diff": "^1.0.6",
149
149
  "ls-engines": "^0.9.3",
150
- "msw": "^2.7.0",
151
- "next": "^15.1.7",
150
+ "msw": "^2.7.3",
151
+ "next": "^15.2.3",
152
152
  "nock": "^13.5.6",
153
- "prettier": "^3.5.0",
154
- "prettier-plugin-packagejson": "^2.5.8",
153
+ "prettier": "^3.5.3",
154
+ "prettier-plugin-packagejson": "^2.5.10",
155
155
  "rimraf": "^5.0.7",
156
- "rollup": "^4.34.6",
156
+ "rollup": "^4.36.0",
157
157
  "sse-channel": "^4.0.0",
158
- "terser": "^5.38.2",
159
- "typescript": "5.7.3",
160
- "vitest": "3.0.5"
158
+ "terser": "^5.39.0",
159
+ "typescript": "5.8.2",
160
+ "vitest": "3.0.9"
161
161
  },
162
162
  "packageManager": "npm@11.0.0",
163
163
  "engines": {
@@ -1,5 +1,6 @@
1
1
  import {lastValueFrom, Observable} from 'rxjs'
2
2
 
3
+ import {AiClient, ObservableAiClient} from './ai/AiClient'
3
4
  import {AssetsClient, ObservableAssetsClient} from './assets/AssetsClient'
4
5
  import {defaultConfig, initConfig} from './config'
5
6
  import * as dataMethods from './data/dataMethods'
@@ -65,7 +66,7 @@ export class ObservableSanityClient {
65
66
  live: LiveClient
66
67
  projects: ObservableProjectsClient
67
68
  users: ObservableUsersClient
68
-
69
+ ai: ObservableAiClient
69
70
  /**
70
71
  * Private properties
71
72
  */
@@ -87,6 +88,7 @@ export class ObservableSanityClient {
87
88
  this.live = new LiveClient(this)
88
89
  this.projects = new ObservableProjectsClient(this, this.#httpRequest)
89
90
  this.users = new ObservableUsersClient(this, this.#httpRequest)
91
+ this.ai = new ObservableAiClient(this, this.#httpRequest)
90
92
  }
91
93
 
92
94
  /**
@@ -733,6 +735,7 @@ export class SanityClient {
733
735
  live: LiveClient
734
736
  projects: ProjectsClient
735
737
  users: UsersClient
738
+ ai: AiClient
736
739
 
737
740
  /**
738
741
  * Observable version of the Sanity client, with the same configuration as the promise-based one
@@ -760,6 +763,7 @@ export class SanityClient {
760
763
  this.live = new LiveClient(this)
761
764
  this.projects = new ProjectsClient(this, this.#httpRequest)
762
765
  this.users = new UsersClient(this, this.#httpRequest)
766
+ this.ai = new AiClient(this, this.#httpRequest)
763
767
 
764
768
  this.observable = new ObservableSanityClient(httpRequest, config)
765
769
  }
@@ -0,0 +1,93 @@
1
+ import {lastValueFrom, type Observable} from 'rxjs'
2
+
3
+ import {_request} from '../data/dataMethods'
4
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
5
+ import type {
6
+ Any,
7
+ HttpRequest,
8
+ IdentifiedSanityDocumentStub,
9
+ InstructAsyncInstruction,
10
+ InstructInstruction,
11
+ InstructSyncInstruction,
12
+ } from '../types'
13
+ import {hasDataset} from '../validators'
14
+
15
+ function _instruct<
16
+ DocumentShape extends Record<string, Any>,
17
+ Req extends InstructInstruction<DocumentShape>,
18
+ >(
19
+ client: SanityClient | ObservableSanityClient,
20
+ httpRequest: HttpRequest,
21
+ request: Req,
22
+ ): Observable<
23
+ Req['async'] extends true ? {_id: string} : IdentifiedSanityDocumentStub & DocumentShape
24
+ > {
25
+ const dataset = hasDataset(client.config())
26
+ return _request(client, httpRequest, {
27
+ method: 'POST',
28
+ uri: `/assist/tasks/instruct/${dataset}`,
29
+ body: request,
30
+ })
31
+ }
32
+
33
+ /** @public */
34
+ export class ObservableAiClient {
35
+ #client: ObservableSanityClient
36
+ #httpRequest: HttpRequest
37
+ constructor(client: ObservableSanityClient, httpRequest: HttpRequest) {
38
+ this.#client = client
39
+ this.#httpRequest = httpRequest
40
+ }
41
+
42
+ instruct(request: InstructAsyncInstruction): Observable<{_id: string}>
43
+
44
+ instruct<DocumentShape extends Record<string, Any>>(
45
+ request: InstructSyncInstruction<DocumentShape>,
46
+ ): Observable<IdentifiedSanityDocumentStub & DocumentShape>
47
+
48
+ /**
49
+ * Run an ad-hoc instruction for a target document.
50
+ * @param request instruction request
51
+ */
52
+ instruct<
53
+ DocumentShape extends Record<string, Any>,
54
+ Req extends InstructInstruction<DocumentShape>,
55
+ >(
56
+ request: Req,
57
+ ): Observable<
58
+ Req['async'] extends true ? {_id: string} : IdentifiedSanityDocumentStub & DocumentShape
59
+ > {
60
+ return _instruct(this.#client, this.#httpRequest, request)
61
+ }
62
+ }
63
+
64
+ /** @public */
65
+ export class AiClient {
66
+ #client: SanityClient
67
+ #httpRequest: HttpRequest
68
+ constructor(client: SanityClient, httpRequest: HttpRequest) {
69
+ this.#client = client
70
+ this.#httpRequest = httpRequest
71
+ }
72
+
73
+ instruct(request: InstructAsyncInstruction): Promise<{_id: string}>
74
+
75
+ instruct<DocumentShape extends Record<string, Any>>(
76
+ request: InstructSyncInstruction<DocumentShape>,
77
+ ): Promise<IdentifiedSanityDocumentStub & DocumentShape>
78
+
79
+ /**
80
+ * Run an ad-hoc instruction for a target document.
81
+ * @param request instruction request
82
+ */
83
+ instruct<
84
+ DocumentShape extends Record<string, Any>,
85
+ Req extends InstructInstruction<DocumentShape>,
86
+ >(
87
+ request: Req,
88
+ ): Promise<
89
+ Req['async'] extends true ? {_id: string} : IdentifiedSanityDocumentStub & DocumentShape
90
+ > {
91
+ return lastValueFrom(_instruct(this.#client, this.#httpRequest, request))
92
+ }
93
+ }
@@ -0,0 +1,206 @@
1
+ //Request shape
2
+
3
+ import type {Any, SanityDocumentStub} from '@sanity/client'
4
+
5
+ /** @beta */
6
+ export interface InstructConstantInstructionParam {
7
+ type: 'constant'
8
+ value: string
9
+ }
10
+
11
+ /**
12
+ *
13
+ * Includes a LLM-friendly version of the field value in the instruction
14
+ * @beta
15
+ * */
16
+ export interface InstructFieldInstructionParam {
17
+ type: 'field'
18
+ /**
19
+ * Examples: 'title', 'array[_key=="key"].field'
20
+ */
21
+ path: string
22
+ /**
23
+ * If omitted, implicitly uses the documentId of the instruction target
24
+ */
25
+ documentId?: string
26
+ }
27
+
28
+ /**
29
+ *
30
+ * Includes a LLM-friendly version of the document in the instruction
31
+ * @beta
32
+ * */
33
+ export interface DocumentInstructionParam {
34
+ type: 'document'
35
+ /**
36
+ * If omitted, implicitly uses the documentId of the instruction target
37
+ */
38
+ documentId?: string
39
+ }
40
+
41
+ /**
42
+ * Includes a LLM-friendly version of GROQ query result in the instruction
43
+ * @beta
44
+ * */
45
+ export interface InstructGroqInstructionParam {
46
+ type: 'groq'
47
+ query: string
48
+ params?: Record<string, string>
49
+ }
50
+
51
+ /** @beta */
52
+ export type InstructInstructionParam =
53
+ | string
54
+ | InstructConstantInstructionParam
55
+ | InstructFieldInstructionParam
56
+ | DocumentInstructionParam
57
+ | InstructGroqInstructionParam
58
+
59
+ /** @beta */
60
+ export type InstructInstructionParams = Record<string, InstructInstructionParam>
61
+
62
+ interface InstructRequestBase {
63
+ /** schemaId as reported by sanity deploy / sanity schema store */
64
+ schemaId: string
65
+ /** string template using $variable – more on this below under "Dynamic instruction" */
66
+ instruction: string
67
+ /** param values for the string template, keys are the variable name, ie if the template has "$variable", one key must be "variable" */
68
+ instructionParams?: InstructInstructionParams
69
+ /**
70
+ * Optional document path output target for the instruction.
71
+ * When provided, the instruction will apply to this path in the document and its children.
72
+ *
73
+ * ## Examples
74
+ * - `path: 'title'` will output to the title field in the document
75
+ * - `path: 'array[_key="xx"]'` will output to the item with `_key: 'xx'` in the array field
76
+ */
77
+ path?: string
78
+
79
+ /**
80
+ * Controls sub-paths in the document that can be output to.
81
+ *
82
+ * The string-paths are relative to the `path` param
83
+ *
84
+ * Note: these path strings are less strictly validated than the `path` param itself:
85
+ * if an relative-path does not exist or is invalid, it will be silently ignored.
86
+ *
87
+ * @see InstructRequestBase#conditionalPaths
88
+ * @see InstructRequestBase#outputTypes
89
+ */
90
+ relativeOutputPaths?: {include: string[]} | {exclude: string[]}
91
+
92
+ /**
93
+ * Controls which types the instruction is allowed to output to.
94
+ *
95
+ * @see InstructRequestBase#relativeOutputPaths
96
+ * @see InstructRequestBase#conditionalPaths
97
+ */
98
+ outputTypes?: {include: string[]} | {exclude: string[]}
99
+
100
+ /**
101
+ * When a type or field in the schema has a function set for `hidden` or `readOnly`, it is conditional.
102
+ *
103
+ * By default, AI Instruct will not output to conditional `readOnly` and `hidden` fields,
104
+ * ie, they are considered to resolve to `readOnly: true` / `hidden: true`.
105
+ *
106
+ * `conditionalPaths` param allows setting the default conditional value for
107
+ * `hidden` and `readOnly` to false,
108
+ * or individually set `hidden` and `readOnly` state for individual document paths.
109
+ *
110
+ *
111
+ * Note: fields and types with explicit readOnly: true or hidden: true in the schema, are not available to AI Instruct,
112
+ * and cannot be changed via conditionalPaths.
113
+ *
114
+ * conditionalPaths state only apply to fields and types that have conditional `hidden` or `readOnly` in their schema definition.
115
+ *
116
+ * @see InstructRequestBase#relativeOutputPaths
117
+ * @see InstructRequestBase#outputTypes
118
+ */
119
+ conditionalPaths?: {
120
+ defaultReadOnly?: boolean
121
+ defaultHidden?: boolean
122
+ paths?: {
123
+ /** path here is not a relative path: it must be the full document path, regardless of `path` param on the request itself */
124
+ path: string
125
+ readOnly: boolean
126
+ hidden: boolean
127
+ }[]
128
+ }
129
+ }
130
+
131
+ interface Sync {
132
+ /**
133
+ * By default, skipWrite: false.
134
+ * Write enabled operations will mutate the target document, and emit AI presence in the studio.
135
+ *
136
+ * When skipWrite: true, the api will not mutate any documents nor emit presence.
137
+ * Ie, when true, no changes will be made to content-lake
138
+ *
139
+ * skipWrite: true is incompatible with async: true,
140
+ * as skipWrite implies that you will use the return value of the operation
141
+ */
142
+ skipWrite?: boolean
143
+
144
+ /**
145
+ * When async: true, requests responds with status 201 and {_id} as response body as soon as the request is validated.
146
+ * The instruction operation will carry on in the background.
147
+ *
148
+ * When async: false (default), requests respond with status 200 and the document value after instruction has been applied.
149
+ *
150
+ * async: true is incompatible with skipWrite: true, as async: true does not return the resulting document
151
+ */
152
+ async?: false
153
+ }
154
+
155
+ interface Async {
156
+ /**
157
+ * When async: true, requests responds with status 201 and {_id} as response body as soon as the request is validated.
158
+ * The instruction operation will carry on in the background.
159
+ *
160
+ * When async: false (default), requests respond with status 200 and the document value after instruction has been applied.
161
+ *
162
+ * async: true is incompatible with skipWrite, as async: true does not return the resulting document
163
+ */
164
+ async: true
165
+ }
166
+
167
+ /**
168
+ * Instruction for an existing document.
169
+ * @beta
170
+ */
171
+ interface ExistingDocumentRequest {
172
+ documentId: string
173
+ }
174
+
175
+ /**
176
+ * Instruction to create a new document
177
+ * @beta
178
+ */
179
+ interface CreateDocumentRequest<T extends Record<string, Any> = Record<string, Any>> {
180
+ createDocument: {
181
+ /** if no _id is provided, one will be generated. _id is always returned when the requests succeed */
182
+ _id?: string
183
+ _type: string
184
+ } & SanityDocumentStub<T>
185
+ }
186
+
187
+ /** @beta */
188
+ export type InstructSyncInstruction<T extends Record<string, Any> = Record<string, Any>> = (
189
+ | ExistingDocumentRequest
190
+ | CreateDocumentRequest<T>
191
+ ) &
192
+ InstructRequestBase &
193
+ Sync
194
+
195
+ /** @beta */
196
+ export type InstructAsyncInstruction<T extends Record<string, Any> = Record<string, Any>> = (
197
+ | ExistingDocumentRequest
198
+ | CreateDocumentRequest<T>
199
+ ) &
200
+ InstructRequestBase &
201
+ Async
202
+
203
+ /** @beta */
204
+ export type InstructInstruction<T extends Record<string, Any> = Record<string, Any>> =
205
+ | InstructSyncInstruction<T>
206
+ | InstructAsyncInstruction<T>
package/src/config.ts CHANGED
@@ -28,30 +28,15 @@ function validateApiVersion(apiVersion: string) {
28
28
  }
29
29
  }
30
30
 
31
- const VALID_PERSPECTIVE = /^[a-z0-9_]+$/i
32
-
33
31
  /**
34
32
  * @internal - it may have breaking changes in any release
35
33
  */
36
34
  export function validateApiPerspective(
37
35
  perspective: unknown,
38
36
  ): asserts perspective is ClientPerspective {
39
- if (Array.isArray(perspective)) {
40
- if (perspective.includes('raw')) {
41
- throw new TypeError(
42
- `Invalid API perspective value: "raw". The raw-perspective can not be combined with other perspectives`,
43
- )
44
- }
45
- }
46
-
47
- const invalid = (Array.isArray(perspective) ? perspective : [perspective]).filter(
48
- (perspectiveName) =>
49
- typeof perspectiveName !== 'string' || !VALID_PERSPECTIVE.test(perspectiveName),
50
- )
51
- if (invalid.length > 0) {
52
- const formatted = invalid.map((v) => JSON.stringify(v))
37
+ if (Array.isArray(perspective) && perspective.length > 1 && perspective.includes('raw')) {
53
38
  throw new TypeError(
54
- `Invalid API perspective value${invalid.length === 1 ? '' : 's'}: ${formatted.join(', ')}, expected \`published\`, \`drafts\`, \`raw\` or a release identifier string`,
39
+ `Invalid API perspective value: "raw". The raw-perspective can not be combined with other perspectives`,
55
40
  )
56
41
  }
57
42
  }
@@ -122,7 +107,13 @@ export const initConfig = (
122
107
  const isBrowser = typeof window !== 'undefined' && window.location && window.location.hostname
123
108
  const isLocalhost = isBrowser && isLocal(window.location.hostname)
124
109
 
125
- if (isBrowser && isLocalhost && newConfig.token && newConfig.ignoreBrowserTokenWarning !== true) {
110
+ const hasToken = Boolean(newConfig.token)
111
+ if (newConfig.withCredentials && hasToken) {
112
+ warnings.printCredentialedTokenWarning()
113
+ newConfig.withCredentials = false
114
+ }
115
+
116
+ if (isBrowser && isLocalhost && hasToken && newConfig.ignoreBrowserTokenWarning !== true) {
126
117
  warnings.printBrowserTokenWarning()
127
118
  } else if (typeof newConfig.useCdn === 'undefined') {
128
119
  warnings.printCdnWarning()
@@ -84,7 +84,7 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
84
84
  const listenFor = options.events ? options.events : ['mutation']
85
85
 
86
86
  const esOptions: EventSourceInit & {headers?: Record<string, string>} = {}
87
- if (token || withCredentials) {
87
+ if (withCredentials) {
88
88
  esOptions.withCredentials = true
89
89
  }
90
90
 
@@ -18,7 +18,7 @@ export function requestOptions(config: Any, overrides: Any = {}): Omit<RequestOp
18
18
 
19
19
  const withCredentials = Boolean(
20
20
  typeof overrides.withCredentials === 'undefined'
21
- ? config.token || config.withCredentials
21
+ ? config.withCredentials
22
22
  : overrides.withCredentials,
23
23
  )
24
24
 
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@ export const requester = exp.requester
11
11
 
12
12
  /**
13
13
  * @remarks
14
- * As of API version `v2025-02-19`, the default perspective used by the client has changed from `raw` to `published`. {@link https://www.sanity.io/changelog/e93a2d5a-9cee-4801-829e-8d3394bfed85|Changelog}
14
+ * As of API version `v2025-02-19`, the default perspective used by the client has changed from `raw` to `published`. {@link https://www.sanity.io/changelog/676aaa9d-2da6-44fb-abe5-580f28047c10|Changelog}
15
15
  * @public
16
16
  */
17
17
  export const createClient = exp.createClient
package/src/types.ts CHANGED
@@ -64,7 +64,7 @@ export interface ClientConfig {
64
64
  /**
65
65
  * What perspective to use for the client. See {@link https://www.sanity.io/docs/perspectives|perspective documentation}
66
66
  * @remarks
67
- * As of API version `v2025-02-19`, the default perspective has changed from `raw` to `published`. {@link https://www.sanity.io/changelog/e93a2d5a-9cee-4801-829e-8d3394bfed85|Changelog}
67
+ * As of API version `v2025-02-19`, the default perspective has changed from `raw` to `published`. {@link https://www.sanity.io/changelog/676aaa9d-2da6-44fb-abe5-580f28047c10|Changelog}
68
68
  * @defaultValue 'published'
69
69
  */
70
70
  perspective?: ClientPerspective
@@ -72,7 +72,7 @@ export interface ClientConfig {
72
72
 
73
73
  /**
74
74
  @remarks
75
- * As of API version `v2025-02-19`, the default perspective has changed from `raw` to `published`. {@link https://www.sanity.io/changelog/e93a2d5a-9cee-4801-829e-8d3394bfed85|Changelog}
75
+ * As of API version `v2025-02-19`, the default perspective has changed from `raw` to `published`. {@link https://www.sanity.io/changelog/676aaa9d-2da6-44fb-abe5-580f28047c10|Changelog}
76
76
  */
77
77
  apiVersion?: string
78
78
  proxy?: string
@@ -944,7 +944,7 @@ export interface ListenOptions {
944
944
 
945
945
  /**
946
946
  * Whether to include events for drafts and versions. As of API Version >= v2025-02-19, only events
947
- * for published documents will be included by default (see {@link https://www.sanity.io/changelog/e93a2d5a-9cee-4801-829e-8d3394bfed85|Changelog})
947
+ * for published documents will be included by default (see {@link https://www.sanity.io/changelog/676aaa9d-2da6-44fb-abe5-580f28047c10|Changelog})
948
948
  * If you need events from drafts and versions, set this to `true`.
949
949
  * Note: Keep in mind that additional document variants may be introduced in the future, so it's
950
950
  * recommended to respond to events in a way that's tolerant of potential future variants, e.g. by
@@ -1336,6 +1336,16 @@ export type ClientReturn<
1336
1336
  Fallback = Any,
1337
1337
  > = GroqString extends keyof SanityQueries ? SanityQueries[GroqString] : Fallback
1338
1338
 
1339
+ export type {
1340
+ InstructAsyncInstruction,
1341
+ InstructConstantInstructionParam,
1342
+ InstructFieldInstructionParam,
1343
+ InstructGroqInstructionParam,
1344
+ InstructInstruction,
1345
+ InstructInstructionParam,
1346
+ InstructInstructionParams,
1347
+ InstructSyncInstruction,
1348
+ } from './ai/types'
1339
1349
  export type {
1340
1350
  ContentSourceMapParsedPath,
1341
1351
  ContentSourceMapParsedPathKeyedSegment,
@@ -62,7 +62,10 @@ function _shareReplayLatest<T>(config: ShareReplayLatestConfig<T>): MonoTypeOper
62
62
  )
63
63
  const emitLatest = new Observable<T>((subscriber) => {
64
64
  if (emitted) {
65
- subscriber.next(latest)
65
+ subscriber.next(
66
+ // this cast is safe because of the emitted check which asserts that we got T from the source
67
+ latest as T,
68
+ )
66
69
  }
67
70
  subscriber.complete()
68
71
  })
package/src/warnings.ts CHANGED
@@ -33,6 +33,11 @@ export const printBrowserTokenWarning = createWarningPrinter([
33
33
  )} for more information and how to hide this warning.`,
34
34
  ])
35
35
 
36
+ export const printCredentialedTokenWarning = createWarningPrinter([
37
+ 'You have configured Sanity client to use a token, but also provided `withCredentials: true`.',
38
+ 'This is no longer supported - only token will be used - remove `withCredentials: true`.',
39
+ ])
40
+
36
41
  export const printNoApiVersionSpecifiedWarning = createWarningPrinter([
37
42
  'Using the Sanity client without specifying an API version is deprecated.',
38
43
  `See ${generateHelpUrl('js-client-api-version')}`,