@sanity/client 8.2.0 → 8.4.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 (43) hide show
  1. package/README.md +2 -2
  2. package/dist/{browserUpload-2tz6Sdqp.js → browserUpload-C7PwCs-C.js} +6 -9
  3. package/dist/browserUpload-C7PwCs-C.js.map +1 -0
  4. package/dist/{browserUpload-CwpNx7Vl.js → browserUpload-D-2Rmfjo.js} +6 -9
  5. package/dist/browserUpload-D-2Rmfjo.js.map +1 -0
  6. package/dist/{config-3wiPP-sZ.js → config-CgJ16jET.js} +4 -2
  7. package/dist/config-CgJ16jET.js.map +1 -0
  8. package/dist/csm.js +1 -1
  9. package/dist/{dist-C9ExSk2R.js → dist-C5K_YcEU.js} +3 -2
  10. package/dist/{dist-C9ExSk2R.js.map → dist-C5K_YcEU.js.map} +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +942 -115
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.node.d.ts +3345 -16
  15. package/dist/index.node.js +865 -59
  16. package/dist/index.node.js.map +1 -1
  17. package/dist/media-library.d.ts +1 -1
  18. package/dist/rolldown-runtime-4YWMqDIC.js +9 -0
  19. package/dist/{stegaEncodeSourceMap-DbM2fTN4.js → stegaEncodeSourceMap-CO1HKnm2.js} +2 -2
  20. package/dist/{stegaEncodeSourceMap-DbM2fTN4.js.map → stegaEncodeSourceMap-CO1HKnm2.js.map} +1 -1
  21. package/dist/{types-nJhm5Nyq.d.ts → types-DiPF0ENT.d.ts} +3346 -17
  22. package/package.json +3 -3
  23. package/src/SanityClient.ts +7 -0
  24. package/src/assets/AssetsClient.ts +5 -0
  25. package/src/collaboration/types.ts +59 -8
  26. package/src/config.ts +1 -0
  27. package/src/context/ContextClient.ts +1006 -0
  28. package/src/context/openapi.json +5345 -0
  29. package/src/context/reads.ts +206 -0
  30. package/src/context/store.ts +100 -0
  31. package/src/context/types.gen.ts +2428 -0
  32. package/src/context/types.ts +228 -0
  33. package/src/data/dataMethods.ts +4 -1
  34. package/src/defineCreateClient.ts +1 -0
  35. package/src/functions/FunctionsClient.ts +52 -9
  36. package/src/functions/invoke.ts +47 -6
  37. package/src/http/browserUpload.ts +0 -12
  38. package/src/types.ts +29 -3
  39. package/src/util/createVersionId.ts +15 -5
  40. package/src/validators.ts +1 -0
  41. package/dist/browserUpload-2tz6Sdqp.js.map +0 -1
  42. package/dist/browserUpload-CwpNx7Vl.js.map +0 -1
  43. package/dist/config-3wiPP-sZ.js.map +0 -1
@@ -0,0 +1,228 @@
1
+ import type {
2
+ ListenOptions,
3
+ RequestOptions as MainRequestOptions,
4
+ ResumableListenOptions,
5
+ UploadBody,
6
+ } from '../types'
7
+ import type {components, paths} from './types.gen'
8
+
9
+ /** Options accepted by every Context method. @beta */
10
+ export type RequestOptions = {signal?: AbortSignal; tag?: string}
11
+
12
+ /** @internal */
13
+ export const possibleStoreRequestOptions = ['headers', 'signal', 'tag', 'timeout', 'token'] as const
14
+
15
+ /**
16
+ * Request options honored by `context.fetch`.
17
+ *
18
+ * @beta
19
+ */
20
+ export type ContextRequestOptions = Pick<
21
+ MainRequestOptions,
22
+ (typeof possibleStoreRequestOptions)[number]
23
+ >
24
+
25
+ /**
26
+ * Listener options for `context.listen`.
27
+ *
28
+ * `includeAllVersions` is left out: Context documents are written by the
29
+ * Context API with no drafts or versions, so it would never make a
30
+ * difference.
31
+ *
32
+ * @beta
33
+ */
34
+ export type ContextListenOptions =
35
+ | Omit<ListenOptions, 'includeAllVersions'>
36
+ | Omit<ResumableListenOptions, 'includeAllVersions'>
37
+
38
+ /**
39
+ * A file import. The client stages the upload, PUTs the bytes straight to
40
+ * storage with a signed URL, and confirms. The Context API never holds the
41
+ * file content.
42
+ * @beta
43
+ */
44
+ export type CreateFileImportParams = {
45
+ type: 'file'
46
+ /** Same shapes `assets.upload` accepts, minus node streams: the bytes go
47
+ * out through `fetch`, which has no portable stream support. */
48
+ file: Exclude<UploadBody, NodeJS.ReadableStream>
49
+ filename: string
50
+ contentType?: string
51
+ }
52
+
53
+ type KnowledgeBasesPath = '/{apiVersion}/context/knowledge-bases'
54
+ type ConversationPath =
55
+ '/{apiVersion}/context/organizations/{organizationId}/conversations/{threadId}'
56
+ type KnowledgeBasePath = '/{apiVersion}/context/knowledge-bases/{knowledgeBaseId}'
57
+ type ImportsPath = `${KnowledgeBasePath}/imports`
58
+ type ImportPath = `${KnowledgeBasePath}/imports/{importId}`
59
+ type UploadsPath = `${KnowledgeBasePath}/imports/uploads`
60
+ type EntryRebuildPath = `${KnowledgeBasePath}/entries/{entryPath}/rebuild`
61
+ type SourcesPath = `${KnowledgeBasePath}/sources`
62
+ type SourcePath = `${KnowledgeBasePath}/sources/{sourceId}`
63
+ type SourceContentPath = `${KnowledgeBasePath}/sources/{sourceId}/content`
64
+ type IssuesApplyPath = `${KnowledgeBasePath}/issues/apply`
65
+ type InstructionPath = `${KnowledgeBasePath}/instructions/{instructionId}`
66
+ type JobPath = `${KnowledgeBasePath}/jobs/{jobId}`
67
+ type IssueResolvePath = `${KnowledgeBasePath}/issues/{issueId}/resolve`
68
+ type IssueDismissPath = `${KnowledgeBasePath}/issues/{issueId}/dismiss`
69
+ type IssueReopenPath = `${KnowledgeBasePath}/issues/{issueId}/reopen`
70
+ type InstructionsPath = `${KnowledgeBasePath}/instructions`
71
+
72
+ type JsonResponse<T> = T extends {content: {'application/json': infer R}} ? R : never
73
+ type JsonBody<T> = T extends {
74
+ requestBody: {content: {'application/json': infer R}}
75
+ }
76
+ ? R
77
+ : never
78
+
79
+ /**
80
+ * A knowledge base: one buildable body of knowledge inside Context.
81
+ * @beta
82
+ */
83
+ export type KnowledgeBase = JsonResponse<paths[KnowledgeBasePath]['get']['responses']['200']>
84
+
85
+ /**
86
+ * Parameters for creating a knowledge base.
87
+ * @beta
88
+ */
89
+ export type CreateKnowledgeBaseParams = JsonBody<paths[KnowledgeBasesPath]['post']>
90
+ /** @beta */
91
+ export type EditKnowledgeBaseParams = JsonBody<paths[KnowledgeBasePath]['patch']>
92
+
93
+ /**
94
+ * Parameters for importing content. Discriminated on `type`:
95
+ * inline text, a website crawl, or a Sanity dataset bind.
96
+ * @beta
97
+ */
98
+ export type CreateImportParams = JsonBody<paths[ImportsPath]['post']>
99
+
100
+ /**
101
+ * Accepted async work. Poll the job with `jobs.get` until it reaches a
102
+ * terminal state.
103
+ * @beta
104
+ */
105
+ export type JobAccepted = JsonResponse<
106
+ paths[`${KnowledgeBasePath}/build`]['post']['responses']['202']
107
+ >
108
+
109
+ /** @beta */
110
+ export type Job = JsonResponse<paths[JobPath]['get']['responses']['200']>
111
+
112
+ /**
113
+ * Accepted entry rebuild: the job to poll plus every entry the rebuild
114
+ * touches.
115
+ * @beta
116
+ */
117
+ export type RebuildEntryResponse = JsonResponse<paths[EntryRebuildPath]['post']['responses']['202']>
118
+
119
+ /** @beta */
120
+ export type ApplyIssuesParams = JsonBody<paths[IssuesApplyPath]['post']>
121
+ /** @beta */
122
+ export type ApplyIssuesResponse = JsonResponse<paths[IssuesApplyPath]['post']['responses']['202']>
123
+ /** @beta */
124
+ export type ResolveIssueParams = JsonBody<paths[IssueResolvePath]['post']>
125
+ /** @beta */
126
+ export type ResolveIssueResponse = JsonResponse<paths[IssueResolvePath]['post']['responses']['200']>
127
+ /** @beta */
128
+ export type DismissIssueResponse = JsonResponse<paths[IssueDismissPath]['post']['responses']['200']>
129
+ /** @beta */
130
+ export type ReopenIssueResponse = JsonResponse<paths[IssueReopenPath]['post']['responses']['200']>
131
+
132
+ /** @beta */
133
+ export type CreateInstructionParams = JsonBody<paths[InstructionsPath]['post']>
134
+ /**
135
+ * A standing instruction, as the instruction endpoints return it.
136
+ * @beta
137
+ */
138
+ export type Instruction = JsonResponse<paths[InstructionPath]['patch']['responses']['200']>
139
+ /** The created instruction, wrapped the way the create endpoint returns it. @beta */
140
+ export type CreateInstructionResponse = JsonResponse<
141
+ paths[InstructionsPath]['post']['responses']['201']
142
+ >
143
+ /** @beta */
144
+ export type EditInstructionParams = JsonBody<paths[InstructionPath]['patch']>
145
+
146
+ /** @beta */
147
+ export type KnowledgeBasesResponse = JsonResponse<
148
+ paths[KnowledgeBasesPath]['get']['responses']['200']
149
+ >
150
+
151
+ /** @beta */
152
+ export type ImportsResponse = JsonResponse<paths[ImportsPath]['get']['responses']['200']>
153
+ /** @beta */
154
+ export type Import = ImportsResponse['data'][number]
155
+ /** @beta */
156
+ export type ImportDetail = JsonResponse<paths[ImportPath]['get']['responses']['200']>
157
+ /** @beta */
158
+ export type ImportDownloadResponse = JsonResponse<
159
+ paths[`${ImportPath}/download`]['get']['responses']['200']
160
+ >
161
+
162
+ /**
163
+ * The staged half of a file upload: PUT the bytes to `uploadUrl`, then
164
+ * confirm with the complete endpoint. `imports.create({type: 'file'})` does
165
+ * all of this in one call.
166
+ * @beta
167
+ */
168
+ export type StagedUpload = JsonResponse<paths[UploadsPath]['post']['responses']['201']>
169
+
170
+ /** @beta */
171
+ export type SourcesResponse = JsonResponse<paths[SourcesPath]['get']['responses']['200']>
172
+ /** @beta */
173
+ export type Source = SourcesResponse['data'][number]
174
+ /** @beta */
175
+ export type SourceDetail = JsonResponse<paths[SourcePath]['get']['responses']['200']>
176
+ /** @beta */
177
+ export type SourceContentResponse = JsonResponse<
178
+ paths[SourceContentPath]['get']['responses']['200']
179
+ >
180
+
181
+ /**
182
+ * A recorded conversation: one agent thread's transcript plus the
183
+ * classification recorded on it. Standalone org-level telemetry — dimensions
184
+ * (MCP endpoints, app, the customer's own keys) live in the `metadata` bag.
185
+ * @beta
186
+ */
187
+ export type Conversation = JsonResponse<paths[ConversationPath]['put']['responses']['200']>
188
+ /**
189
+ * Body for the conversation ingest upsert. Messages replace the stored
190
+ * transcript wholesale; `metadata` and model fields only overwrite when
191
+ * present.
192
+ * @beta
193
+ */
194
+ export type SaveConversationParams = JsonBody<paths[ConversationPath]['put']>
195
+ /** Exactly one of a verdict (`coreMetrics`) or a failure (`classificationError`). @beta */
196
+ export type ClassifyConversationParams = JsonBody<paths[ConversationPath]['patch']>
197
+
198
+ /**
199
+ * The raw `sanity.context.entry` document shape, as stored in the
200
+ * organization's document store. For typing GROQ reads.
201
+ * @beta
202
+ */
203
+ export type EntryDoc = components['schemas']['EntryDoc']
204
+ /** @beta */
205
+ export type IssueDoc = components['schemas']['IssueDoc']
206
+ /** @beta */
207
+ export type InstructionDoc = components['schemas']['InstructionDoc']
208
+ /**
209
+ * The raw `sanity.context.mcp` document shape (an MCP endpoint
210
+ * configuration), as stored in the organization's document store. For
211
+ * typing GROQ reads.
212
+ * @beta
213
+ */
214
+ export type McpDoc = components['schemas']['McpDoc']
215
+
216
+ /**
217
+ * The metadata view of an entry, as `entries.list` projects it. Bodies stay
218
+ * behind `entries.get` (or a GROQ read through `context.fetch`).
219
+ * @beta
220
+ */
221
+ export type Entry = Pick<EntryDoc, '_id' | 'path' | 'title' | 'tldr' | 'status'>
222
+
223
+ /**
224
+ * The raw `sanity.context.conversation` document shape, as stored in the
225
+ * organization's document store. For typing GROQ reads.
226
+ * @beta
227
+ */
228
+ export type ConversationDoc = components['schemas']['ConversationDoc']
@@ -1128,7 +1128,7 @@ export function _prepareRequest(client: Client, options: RequestObservableOption
1128
1128
  *
1129
1129
  * @internal
1130
1130
  */
1131
- function _observe<R>(
1131
+ export function _observe<R>(
1132
1132
  userSignal: AbortSignal | undefined,
1133
1133
  run: (signal: AbortSignal) => Promise<R>,
1134
1134
  ): Observable<R> {
@@ -1291,6 +1291,9 @@ const resourceDataBase = (config: InitializedClientConfig): string => {
1291
1291
  case 'canvas': {
1292
1292
  return `/canvases/${id}`
1293
1293
  }
1294
+ case 'knowledge-base': {
1295
+ return `/knowledge-bases/${id}`
1296
+ }
1294
1297
  case 'media-library': {
1295
1298
  return `/media-libraries/${id}`
1296
1299
  }
@@ -26,6 +26,7 @@ export {
26
26
  isQueryParseError,
27
27
  ServerError,
28
28
  } from './http/errors'
29
+ export * as Context from './context/types'
29
30
  export * from './SanityClient'
30
31
  export * from './types'
31
32
 
@@ -2,7 +2,7 @@ import {lastValueFrom, type Observable} from 'rxjs'
2
2
 
3
3
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
4
4
  import type {HttpRequest} from '../types'
5
- import {_invoke, type InvokeFunctionRequest} from './invoke'
5
+ import {_invoke, type InvokeFunctionOptions, type InvokeFunctionRequest} from './invoke'
6
6
 
7
7
  /** @public */
8
8
  export class ObservableFunctionsClient {
@@ -17,16 +17,36 @@ export class ObservableFunctionsClient {
17
17
  * Invoke a deployed function by its blueprint name.
18
18
  *
19
19
  * The name is resolved within the stack given by `stackId` on the request or
20
- * the client config. Passes the function's return value once it finishes.
20
+ * the client config. Starts the invocation and emits `undefined` as soon as
21
+ * it is accepted; pass `{sync: true}` to wait for the function's return value
22
+ * instead.
21
23
  *
22
24
  * @param functionName - name of the function, as declared in the blueprint
23
25
  * @param request - payload and request options
26
+ * @param options - invocation options
24
27
  */
28
+ invoke(
29
+ functionName: string,
30
+ request?: InvokeFunctionRequest,
31
+ options?: InvokeFunctionOptions & {sync?: false},
32
+ ): Observable<undefined>
33
+ invoke<R = unknown>(
34
+ functionName: string,
35
+ request: InvokeFunctionRequest | undefined,
36
+ options: InvokeFunctionOptions & {sync: true},
37
+ ): Observable<R>
38
+ invoke<R = unknown>(
39
+ functionName: string,
40
+ request?: InvokeFunctionRequest,
41
+ options?: InvokeFunctionOptions,
42
+ ): Observable<R | undefined>
43
+ // Implementation signature — not part of the public API.
25
44
  invoke<R = unknown>(
26
45
  functionName: string,
27
46
  request?: InvokeFunctionRequest,
47
+ options?: InvokeFunctionOptions,
28
48
  ): Observable<R | undefined> {
29
- return _invoke<R>(this.#client, this.#httpRequest, functionName, request)
49
+ return _invoke<R>(this.#client, this.#httpRequest, functionName, request, options)
30
50
  }
31
51
  }
32
52
 
@@ -44,23 +64,46 @@ export class FunctionsClient {
44
64
  *
45
65
  * The name is resolved within the stack given by `stackId` on the request or
46
66
  * the client config, which costs one extra request per call. Rejects if the
47
- * stack has no function by that name, or if the name resolves to anything
48
- * other than a `sanity.function.pubsub` function.
67
+ * stack has no function by that name, or if the name resolves to a function
68
+ * type that cannot be invoked the way it was asked for.
49
69
  *
50
70
  * The lookup is scoped to `projectId`, or to `organizationId` when one is set
51
71
  * for a stack deployed at organization scope.
52
72
  *
53
- * The request stays open until the function finishes, and resolves with its
54
- * return value, or `undefined` if it returns nothing. Long-running functions
55
- * may need an explicit `timeout`.
73
+ * The invocation is started by default: the promise resolves with `undefined`
74
+ * as soon as the call is accepted, without waiting for the function to run.
75
+ * Pass `{sync: true}` to keep the request open until the function finishes
76
+ * and resolve with its return value — long-running functions may then need an
77
+ * explicit `timeout`. Only `sanity.function.pubsub` functions can be invoked
78
+ * synchronously.
56
79
  *
57
80
  * @param functionName - name of the function, as declared in the blueprint
58
81
  * @param request - payload and request options
82
+ * @param options - invocation options
59
83
  */
84
+ invoke(
85
+ functionName: string,
86
+ request?: InvokeFunctionRequest,
87
+ options?: InvokeFunctionOptions & {sync?: false},
88
+ ): Promise<undefined>
89
+ invoke<R = unknown>(
90
+ functionName: string,
91
+ request: InvokeFunctionRequest | undefined,
92
+ options: InvokeFunctionOptions & {sync: true},
93
+ ): Promise<R>
94
+ invoke<R = unknown>(
95
+ functionName: string,
96
+ request?: InvokeFunctionRequest,
97
+ options?: InvokeFunctionOptions,
98
+ ): Promise<R | undefined>
99
+ // Implementation signature — not part of the public API.
60
100
  invoke<R = unknown>(
61
101
  functionName: string,
62
102
  request?: InvokeFunctionRequest,
103
+ options?: InvokeFunctionOptions,
63
104
  ): Promise<R | undefined> {
64
- return lastValueFrom(_invoke<R>(this.#client, this.#httpRequest, functionName, request))
105
+ return lastValueFrom(
106
+ _invoke<R>(this.#client, this.#httpRequest, functionName, request, options),
107
+ )
65
108
  }
66
109
  }
@@ -6,7 +6,15 @@ import type {HttpRequest, InitializedClientConfig} from '../types'
6
6
 
7
7
  /** Function resource types in a blueprint are namespaced under this prefix. */
8
8
  const FUNCTION_RESOURCE_PREFIX = 'sanity.function.'
9
- const INVOKABLE_FUNCTION_TYPE = 'sanity.function.pubsub'
9
+ const SYNC_INVOCABLE_FUNCTION_TYPES = ['sanity.function.pubsub']
10
+ const ASYNC_INVOCABLE_FUNCTION_TYPES = [
11
+ 'sanity.function.durable',
12
+ 'sanity.function.pubsub',
13
+ 'sanity.function.queue',
14
+ ]
15
+ const INVOCABLE_FUNCTION_TYPES = [
16
+ ...new Set([...SYNC_INVOCABLE_FUNCTION_TYPES, ...ASYNC_INVOCABLE_FUNCTION_TYPES]),
17
+ ]
10
18
 
11
19
  /** @public */
12
20
  export interface InvokeFunctionEvent {
@@ -37,6 +45,18 @@ export interface InvokeFunctionRequest {
37
45
  signal?: AbortSignal
38
46
  }
39
47
 
48
+ /** @public */
49
+ export interface InvokeFunctionOptions {
50
+ /**
51
+ * Wait for the function to finish and resolve with its return value.
52
+ *
53
+ * Defaults to `false`: the invocation is started, the request resolves as soon
54
+ * as it is accepted, and the value is always `undefined`. Only function types
55
+ * that support running inline can be invoked synchronously.
56
+ */
57
+ sync?: boolean
58
+ }
59
+
40
60
  /**
41
61
  * Subset of a stack resource the client needs in order to resolve a name.
42
62
  *
@@ -109,6 +129,7 @@ function _resolveFunctionId(
109
129
  stackId: string,
110
130
  headers: Record<string, string>,
111
131
  request: InvokeFunctionRequest | undefined,
132
+ sync: boolean,
112
133
  ): Observable<string> {
113
134
  return _requestObservable<{resources?: StackResource[]}>(client, httpRequest, {
114
135
  method: 'GET',
@@ -132,9 +153,15 @@ function _resolveFunctionId(
132
153
  )
133
154
  }
134
155
 
135
- if (match.type !== INVOKABLE_FUNCTION_TYPE) {
156
+ if (!INVOCABLE_FUNCTION_TYPES.includes(match.type)) {
136
157
  throw new Error(`Function invocation is not supported for ${match.type}`)
137
158
  }
159
+ if (sync && !SYNC_INVOCABLE_FUNCTION_TYPES.includes(match.type)) {
160
+ throw new Error(`Synchronous function invocation is not supported for ${match.type}`)
161
+ }
162
+ if (!sync && !ASYNC_INVOCABLE_FUNCTION_TYPES.includes(match.type)) {
163
+ throw new Error(`Asynchronous function invocation is not supported for ${match.type}`)
164
+ }
138
165
 
139
166
  return match.externalId
140
167
  }),
@@ -147,6 +174,7 @@ export function _invoke<R = unknown>(
147
174
  httpRequest: HttpRequest,
148
175
  functionName: string,
149
176
  request?: InvokeFunctionRequest,
177
+ options?: InvokeFunctionOptions,
150
178
  ): Observable<R | undefined> {
151
179
  // Deferred so a bad config surfaces as an error on the returned observable
152
180
  // (and so a rejected promise) rather than throwing at the call site.
@@ -154,17 +182,30 @@ export function _invoke<R = unknown>(
154
182
  const config = client.config()
155
183
  const headers = scopeHeaders(config, request)
156
184
  const stackId = resolveStackId(config, request)
157
-
158
- return _resolveFunctionId(client, httpRequest, functionName, stackId, headers, request).pipe(
159
- // A function that returns nothing answers 204, which the transport parses
185
+ const sync = options?.sync ?? false
186
+
187
+ return _resolveFunctionId(
188
+ client,
189
+ httpRequest,
190
+ functionName,
191
+ stackId,
192
+ headers,
193
+ request,
194
+ sync,
195
+ ).pipe(
196
+ // An async invocation is only acknowledged (202, no body), and a sync
197
+ // function that returns nothing answers 204, which the transport parses
160
198
  // to an `undefined` body. The status code is not observable from here —
161
199
  // the `HttpRequest` boundary resolves to the body alone — so an empty
162
200
  // response and a function that returned nothing both surface as
163
201
  // `undefined`.
202
+ //
203
+ // The route is async by default, so `sync=false` is left off the wire
204
+ // rather than spelled out.
164
205
  mergeMap((functionId) =>
165
206
  _requestObservable<R | undefined>(client, httpRequest, {
166
207
  method: 'POST',
167
- url: `/functions/${functionId}/invoke`,
208
+ url: `/functions/${functionId}/invoke${sync ? '?sync=true' : ''}`,
168
209
  headers,
169
210
  body: {event: {data: request?.event?.data ?? {}}},
170
211
  timeout: request?.timeout,
@@ -1,14 +1,9 @@
1
- import {createDebug} from 'obug'
2
1
  import {Observable} from 'rxjs'
3
2
 
4
3
  import type {UploadEvent} from '../types'
5
4
  import {ClientError, httpResponseFromFetch, ServerError} from './errors'
6
5
  import {parseJsonText} from './request'
7
6
 
8
- const log = createDebug('sanity:client')
9
-
10
- let nextRequestId = 1
11
-
12
7
  /**
13
8
  * Options for a browser-side asset upload that needs progress events.
14
9
  *
@@ -36,11 +31,8 @@ export interface BrowserUploadOptions {
36
31
  export function uploadWithProgress<T>(options: BrowserUploadOptions): Observable<UploadEvent<T>> {
37
32
  return new Observable<UploadEvent<T>>((subscriber) => {
38
33
  const xhr = new XMLHttpRequest()
39
- const requestId = nextRequestId++
40
34
  const {url, method, headers, body, withCredentials, timeout, signal} = options
41
35
 
42
- log('[%d] %s %s (XHR upload with progress)', requestId, method, url)
43
-
44
36
  xhr.open(method, url)
45
37
  xhr.withCredentials = withCredentials
46
38
  if (typeof timeout === 'number' && timeout > 0) {
@@ -63,8 +55,6 @@ export function uploadWithProgress<T>(options: BrowserUploadOptions): Observable
63
55
  }
64
56
 
65
57
  xhr.onload = () => {
66
- log('[%d] %s %s — %d', requestId, method, url, xhr.status)
67
-
68
58
  if (xhr.status >= 400) {
69
59
  // Same typed errors as the fetch transport, so consumers can keep
70
60
  // detecting `ClientError`/`ServerError` and reading `statusCode`,
@@ -100,12 +90,10 @@ export function uploadWithProgress<T>(options: BrowserUploadOptions): Observable
100
90
  }
101
91
 
102
92
  xhr.onerror = () => {
103
- log('[%d] %s %s — network error', requestId, method, url)
104
93
  subscriber.error(new Error('XHR upload network error'))
105
94
  }
106
95
 
107
96
  xhr.ontimeout = () => {
108
- log('[%d] %s %s — timed out after %dms', requestId, method, url, timeout)
109
97
  // Same error shape as the fetch transport's timeout rejection.
110
98
  subscriber.error(
111
99
  new DOMException(
package/src/types.ts CHANGED
@@ -109,6 +109,10 @@ type ClientConfigResource =
109
109
  type: 'canvas'
110
110
  id: string
111
111
  }
112
+ | {
113
+ type: 'knowledge-base'
114
+ id: string
115
+ }
112
116
  | {
113
117
  type: 'media-library'
114
118
  id: string
@@ -314,6 +318,17 @@ export interface ClientConfig {
314
318
  collaboration?: {
315
319
  organizationId?: string
316
320
  }
321
+
322
+ /**
323
+ * Organization-scoped configuration for Context APIs.
324
+ *
325
+ * Currently this is used by `context.insights` methods.
326
+ *
327
+ * @beta
328
+ */
329
+ context?: {
330
+ organizationId?: string
331
+ }
317
332
  }
318
333
 
319
334
  /** @public */
@@ -620,6 +635,7 @@ export type DatasetAclMode = 'public' | 'private' | 'custom'
620
635
  /** @public */
621
636
  export type DatasetCreateOptions = {
622
637
  aclMode?: DatasetAclMode
638
+ description?: string
623
639
  embeddings?: {
624
640
  enabled: boolean
625
641
  projection?: string
@@ -629,6 +645,7 @@ export type DatasetCreateOptions = {
629
645
  /** @public */
630
646
  export type DatasetEditOptions = {
631
647
  aclMode?: DatasetAclMode
648
+ description?: string
632
649
  }
633
650
 
634
651
  /** @public */
@@ -645,17 +662,21 @@ export type EmbeddingsSettingsBody = {
645
662
  }
646
663
 
647
664
  /** @public */
648
- export type DatasetResponse = {datasetName: string; aclMode: DatasetAclMode}
665
+ export type DatasetResponse = {datasetName: string; aclMode: DatasetAclMode; description: string}
649
666
  /** @public */
650
667
  export type DatasetsResponse = {
651
668
  name: string
652
669
  aclMode: DatasetAclMode
670
+ description: string
653
671
  createdAt: string
654
672
  createdByUserId: string
655
673
  addonFor: string | null
656
674
  datasetProfile: string
657
675
  features: string[]
658
- tags: string[]
676
+ tags: {
677
+ name: string
678
+ title: string
679
+ }[]
659
680
  }[]
660
681
 
661
682
  /** @public */
@@ -2297,7 +2318,11 @@ export type {
2297
2318
  TranslateTarget,
2298
2319
  TranslateTargetInclude,
2299
2320
  } from './agent/actions/translate'
2300
- export type {InvokeFunctionEvent, InvokeFunctionRequest} from './functions/invoke'
2321
+ export type {
2322
+ InvokeFunctionEvent,
2323
+ InvokeFunctionOptions,
2324
+ InvokeFunctionRequest,
2325
+ } from './functions/invoke'
2301
2326
  export type {
2302
2327
  CollaborationCommentCreate,
2303
2328
  CollaborationCommentDocument,
@@ -2312,6 +2337,7 @@ export type {
2312
2337
  CollaborationCommentTarget,
2313
2338
  CollaborationCommentUpdate,
2314
2339
  CollaborationCommentRange,
2340
+ CollaborationCommentFieldValue,
2315
2341
  } from './collaboration/types'
2316
2342
  export type {
2317
2343
  ContentSourceMapParsedPath,
@@ -5,20 +5,30 @@ import {
5
5
  isDraftId,
6
6
  isVersionId,
7
7
  } from '@sanity/client/csm'
8
- import {customAlphabet} from 'nanoid'
9
8
 
10
9
  import type {IdentifiedSanityDocumentStub, SanityDocumentStub} from '../types'
11
10
  import {validateVersionIdMatch} from '../validators'
12
11
 
12
+ const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
13
+
13
14
  /**
14
15
  * @internal
15
16
  *
16
17
  * ~24 years (or 7.54e+8 seconds) needed, in order to have a 1% probability of at least one collision if 10 ID's are generated every hour.
17
18
  */
18
- export const generateReleaseId = customAlphabet(
19
- 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
20
- 8,
21
- )
19
+ export function generateReleaseId() {
20
+ let id = ''
21
+
22
+ while (id.length < 8) {
23
+ const bytes = crypto.getRandomValues(new Uint8Array(8 - id.length))
24
+ for (const byte of bytes) {
25
+ const index = byte & 63
26
+ if (index < alphabet.length) id += alphabet[index]
27
+ }
28
+ }
29
+
30
+ return id
31
+ }
22
32
 
23
33
  /** @internal */
24
34
  export const getDocumentVersionId = (publishedId: string, releaseId?: string) =>
package/src/validators.ts CHANGED
@@ -128,6 +128,7 @@ export const resourceConfig = (config: InitializedClientConfig): void => {
128
128
  return
129
129
  }
130
130
  case 'dashboard':
131
+ case 'knowledge-base':
131
132
  case 'media-library':
132
133
  case 'canvas': {
133
134
  return
@@ -1 +0,0 @@
1
- {"version":3,"file":"browserUpload-2tz6Sdqp.js","names":[],"sources":["../src/http/browserUpload.ts"],"sourcesContent":["import {createDebug} from 'obug'\nimport {Observable} from 'rxjs'\n\nimport type {UploadEvent} from '../types'\nimport {ClientError, httpResponseFromFetch, ServerError} from './errors'\nimport {parseJsonText} from './request'\n\nconst log = createDebug('sanity:client')\n\nlet nextRequestId = 1\n\n/**\n * Options for a browser-side asset upload that needs progress events.\n *\n * @internal\n */\nexport interface BrowserUploadOptions {\n url: string\n method: string\n headers: Record<string, string>\n body: unknown\n withCredentials: boolean\n /** Milliseconds before the upload is aborted; `false` and `0` both disable the timeout. */\n timeout?: number | false\n signal?: AbortSignal\n}\n\n/**\n * Run an asset upload through `XMLHttpRequest` so we can surface per-chunk\n * upload progress events. get-it v9 / fetch has no equivalent hook in the\n * browser, so the observable asset-upload API falls back to this path when\n * `XMLHttpRequest` is available.\n *\n * @internal\n */\nexport function uploadWithProgress<T>(options: BrowserUploadOptions): Observable<UploadEvent<T>> {\n return new Observable<UploadEvent<T>>((subscriber) => {\n const xhr = new XMLHttpRequest()\n const requestId = nextRequestId++\n const {url, method, headers, body, withCredentials, timeout, signal} = options\n\n log('[%d] %s %s (XHR upload with progress)', requestId, method, url)\n\n xhr.open(method, url)\n xhr.withCredentials = withCredentials\n if (typeof timeout === 'number' && timeout > 0) {\n xhr.timeout = timeout\n }\n\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value)\n }\n\n xhr.upload.onprogress = (e) => {\n subscriber.next({\n type: 'progress',\n stage: 'upload',\n percent: e.lengthComputable ? Math.round((e.loaded / e.total) * 100) : 0,\n total: e.total || undefined,\n loaded: e.loaded,\n lengthComputable: e.lengthComputable,\n })\n }\n\n xhr.onload = () => {\n log('[%d] %s %s — %d', requestId, method, url, xhr.status)\n\n if (xhr.status >= 400) {\n // Same typed errors as the fetch transport, so consumers can keep\n // detecting `ClientError`/`ServerError` and reading `statusCode`,\n // `responseBody` and the structured API `details` on failed uploads.\n const errorHeaders = parseXhrResponseHeaders(xhr.getAllResponseHeaders())\n const canonical = httpResponseFromFetch(\n {\n status: xhr.status,\n statusText: xhr.statusText,\n headers: errorHeaders,\n body: parseJsonText(xhr.responseText, errorHeaders),\n url: xhr.responseURL,\n },\n url,\n method,\n )\n subscriber.error(\n xhr.status >= 500 ? new ServerError(canonical) : new ClientError(canonical),\n )\n return\n }\n\n let responseBody: T\n try {\n responseBody = JSON.parse(xhr.responseText) as T\n } catch {\n subscriber.error(new Error('Failed to parse upload response as JSON'))\n return\n }\n\n subscriber.next({type: 'response', body: responseBody})\n subscriber.complete()\n }\n\n xhr.onerror = () => {\n log('[%d] %s %s — network error', requestId, method, url)\n subscriber.error(new Error('XHR upload network error'))\n }\n\n xhr.ontimeout = () => {\n log('[%d] %s %s — timed out after %dms', requestId, method, url, timeout)\n // Same error shape as the fetch transport's timeout rejection.\n subscriber.error(\n new DOMException(\n `The operation timed out after ${timeout}ms while attempting to reach ${url}`,\n 'TimeoutError',\n ),\n )\n }\n\n xhr.onabort = () => {\n subscriber.error(new DOMException('Upload aborted', 'AbortError'))\n }\n\n const onSignalAbort = () => xhr.abort()\n if (signal) {\n if (signal.aborted) {\n // `xhr.abort()` before `send()` fires no `abort` event per spec, so\n // error out directly instead of relying on `onabort`.\n subscriber.error(new DOMException('Upload aborted', 'AbortError'))\n return undefined\n }\n signal.addEventListener('abort', onSignalAbort, {once: true})\n }\n\n xhr.send(body as XMLHttpRequestBodyInit)\n\n // Unsubscribing cancels the in-flight upload, mirroring how the fetch\n // path aborts its request (`_observe`). After settle this is a no-op —\n // except for detaching from the caller's signal, which may be long-lived\n // and must not accumulate a listener per upload.\n return () => {\n signal?.removeEventListener('abort', onSignalAbort)\n xhr.abort()\n }\n })\n}\n\n/**\n * Parse `XMLHttpRequest.getAllResponseHeaders()` output (CRLF-separated\n * `name: value` lines) into a `Headers` instance.\n */\nfunction parseXhrResponseHeaders(raw: string): Headers {\n const headers = new Headers()\n for (const line of raw.split('\\r\\n')) {\n const separator = line.indexOf(':')\n if (separator <= 0) continue\n try {\n headers.append(line.slice(0, separator).trim(), line.slice(separator + 1).trim())\n } catch {\n // Skip header lines the Headers constructor rejects — better a partial\n // header record on the error than no error details at all.\n }\n }\n return headers\n}\n"],"mappings":";;;AAOA,MAAM,MAAM,YAAY,eAAe;AAEvC,IAAI,gBAAgB;;;;;;;;;AA0BpB,SAAgB,mBAAsB,SAA2D;CAC/F,OAAO,IAAI,YAA4B,eAAe;EACpD,IAAM,MAAM,IAAI,eAAe,GACzB,YAAY,iBACZ,EAAC,KAAK,QAAQ,SAAS,MAAM,iBAAiB,SAAS,WAAU;EAMvE,AAJA,IAAI,yCAAyC,WAAW,QAAQ,GAAG,GAEnE,IAAI,KAAK,QAAQ,GAAG,GACpB,IAAI,kBAAkB,iBAClB,OAAO,WAAY,YAAY,UAAU,MAC3C,IAAI,UAAU;EAGhB,KAAK,IAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAC/C,IAAI,iBAAiB,KAAK,KAAK;EAmEjC,AAhEA,IAAI,OAAO,cAAc,MAAM;GAC7B,WAAW,KAAK;IACd,MAAM;IACN,OAAO;IACP,SAAS,EAAE,mBAAmB,KAAK,MAAO,EAAE,SAAS,EAAE,QAAS,GAAG,IAAI;IACvE,OAAO,EAAE,SAAS,KAAA;IAClB,QAAQ,EAAE;IACV,kBAAkB,EAAE;GACtB,CAAC;EACH,GAEA,IAAI,eAAe;GAGjB,IAFA,IAAI,mBAAmB,WAAW,QAAQ,KAAK,IAAI,MAAM,GAErD,IAAI,UAAU,KAAK;IAIrB,IAAM,eAAe,wBAAwB,IAAI,sBAAsB,CAAC,GAClE,YAAY,sBAChB;KACE,QAAQ,IAAI;KACZ,YAAY,IAAI;KAChB,SAAS;KACT,MAAM,cAAc,IAAI,cAAc,YAAY;KAClD,KAAK,IAAI;IACX,GACA,KACA,MACF;IACA,WAAW,MACT,IAAI,UAAU,MAAM,IAAI,YAAY,SAAS,IAAI,IAAI,YAAY,SAAS,CAC5E;IACA;GACF;GAEA,IAAI;GACJ,IAAI;IACF,eAAe,KAAK,MAAM,IAAI,YAAY;GAC5C,QAAQ;IACN,WAAW,MAAM,gBAAI,MAAM,yCAAyC,CAAC;IACrE;GACF;GAGA,AADA,WAAW,KAAK;IAAC,MAAM;IAAY,MAAM;GAAY,CAAC,GACtD,WAAW,SAAS;EACtB,GAEA,IAAI,gBAAgB;GAElB,AADA,IAAI,8BAA8B,WAAW,QAAQ,GAAG,GACxD,WAAW,MAAM,gBAAI,MAAM,0BAA0B,CAAC;EACxD,GAEA,IAAI,kBAAkB;GAGpB,AAFA,IAAI,qCAAqC,WAAW,QAAQ,KAAK,OAAO,GAExE,WAAW,MACT,IAAI,aACF,iCAAiC,QAAQ,+BAA+B,OACxE,cACF,CACF;EACF,GAEA,IAAI,gBAAgB;GAClB,WAAW,MAAM,IAAI,aAAa,kBAAkB,YAAY,CAAC;EACnE;EAEA,IAAM,sBAAsB,IAAI,MAAM;EACtC,IAAI,QAAQ;GACV,IAAI,OAAO,SAAS;IAGlB,WAAW,MAAM,IAAI,aAAa,kBAAkB,YAAY,CAAC;IACjE;GACF;GACA,OAAO,iBAAiB,SAAS,eAAe,EAAC,MAAM,GAAI,CAAC;EAC9D;EAQA,OANA,IAAI,KAAK,IAA8B,SAM1B;GAEX,AADA,QAAQ,oBAAoB,SAAS,aAAa,GAClD,IAAI,MAAM;EACZ;CACF,CAAC;AACH;;;;;AAMA,SAAS,wBAAwB,KAAsB;CACrD,IAAM,UAAU,IAAI,QAAQ;CAC5B,KAAK,IAAM,QAAQ,IAAI,MAAM,MAAM,GAAG;EACpC,IAAM,YAAY,KAAK,QAAQ,GAAG;EAC9B,mBAAa,IACjB,IAAI;GACF,QAAQ,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC,KAAK,GAAG,KAAK,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC;EAClF,QAAQ,CAGR;CACF;CACA,OAAO;AACT"}