@sanity/client 7.26.2 → 8.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 (130) hide show
  1. package/README.md +5 -377
  2. package/dist/browserUpload-CQgx9YYo.js +86 -0
  3. package/dist/browserUpload-CQgx9YYo.js.map +1 -0
  4. package/dist/browserUpload-icWlVP15.js +86 -0
  5. package/dist/browserUpload-icWlVP15.js.map +1 -0
  6. package/dist/config-a8VajuEY.js +126 -0
  7. package/dist/config-a8VajuEY.js.map +1 -0
  8. package/dist/csm.d.ts +122 -335
  9. package/dist/csm.js +79 -91
  10. package/dist/csm.js.map +1 -1
  11. package/dist/dist-C9ExSk2R.js +73 -0
  12. package/dist/dist-C9ExSk2R.js.map +1 -0
  13. package/dist/dist-Z8cIRxoB.js +69 -0
  14. package/dist/dist-Z8cIRxoB.js.map +1 -0
  15. package/dist/index.d.ts +107 -6723
  16. package/dist/index.js +2822 -2847
  17. package/dist/index.js.map +1 -1
  18. package/dist/{index.d.cts → index.node.d.ts} +4291 -5328
  19. package/dist/index.node.js +3131 -0
  20. package/dist/index.node.js.map +1 -0
  21. package/dist/isRecord-Kfmt-nk-.js +7 -0
  22. package/dist/isRecord-Kfmt-nk-.js.map +1 -0
  23. package/dist/media-library.d.ts +9 -100
  24. package/dist/media-library.js +36 -14
  25. package/dist/media-library.js.map +1 -1
  26. package/dist/request-CJxcN16k.js +372 -0
  27. package/dist/request-CJxcN16k.js.map +1 -0
  28. package/dist/request-k7VS_NnC.js +354 -0
  29. package/dist/request-k7VS_NnC.js.map +1 -0
  30. package/dist/resolveEditInfo-sq7yF78q.js +325 -0
  31. package/dist/resolveEditInfo-sq7yF78q.js.map +1 -0
  32. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  33. package/dist/stega.d.ts +78 -7137
  34. package/dist/stega.js +27 -20
  35. package/dist/stega.js.map +1 -1
  36. package/dist/stegaClean-C18wLWau.js +21 -0
  37. package/dist/stegaClean-C18wLWau.js.map +1 -0
  38. package/dist/stegaEncodeSourceMap-B2fGArSf.js +163 -0
  39. package/dist/stegaEncodeSourceMap-B2fGArSf.js.map +1 -0
  40. package/dist/stegaEncodeSourceMap-DkoIlutY.js +367 -0
  41. package/dist/stegaEncodeSourceMap-DkoIlutY.js.map +1 -0
  42. package/dist/{index.browser.d.cts → types-CUxZSgB2.d.ts} +3956 -5256
  43. package/dist/types-CfGzbXrl.d.ts +108 -0
  44. package/package.json +64 -141
  45. package/src/SanityClient.ts +82 -117
  46. package/src/agent/actions/AgentActionsClient.ts +12 -12
  47. package/src/agent/actions/commonTypes.ts +2 -1
  48. package/src/agent/actions/generate.ts +26 -6
  49. package/src/agent/actions/patch.ts +27 -7
  50. package/src/agent/actions/prompt.ts +19 -5
  51. package/src/agent/actions/transform.ts +23 -5
  52. package/src/agent/actions/translate.ts +23 -5
  53. package/src/assets/AssetsClient.ts +71 -23
  54. package/src/csm/applySourceDocuments.ts +3 -1
  55. package/src/csm/createEditUrl.ts +1 -1
  56. package/src/csm/draftUtils.ts +0 -1
  57. package/src/csm/index.ts +2 -2
  58. package/src/csm/studioPath.ts +2 -1
  59. package/src/csm/types.ts +8 -2
  60. package/src/data/dataMethods.ts +801 -349
  61. package/src/data/eventsource.ts +13 -10
  62. package/src/data/listen.ts +18 -23
  63. package/src/data/live.ts +44 -27
  64. package/src/data/resolveEventSourceFetch.ts +96 -0
  65. package/src/datasets/DatasetsClient.ts +58 -41
  66. package/src/defineCreateClient.ts +35 -23
  67. package/src/http/browserMiddleware.ts +7 -1
  68. package/src/http/browserUpload.ts +162 -0
  69. package/src/http/errors.ts +60 -6
  70. package/src/http/nodeMiddleware.ts +94 -30
  71. package/src/http/request.ts +310 -87
  72. package/src/http/requestOptions.ts +131 -15
  73. package/src/{index.browser.ts → index.node.ts} +9 -4
  74. package/src/index.ts +4 -3
  75. package/src/mediaLibrary/MediaLibraryVideoClient.ts +26 -11
  76. package/src/projects/ProjectsClient.ts +18 -16
  77. package/src/releases/ReleasesClient.ts +38 -33
  78. package/src/stega/index.ts +1 -2
  79. package/src/stega/stegaEncodeSourceMap.ts +5 -0
  80. package/src/stega/types.ts +2 -2
  81. package/src/types.ts +148 -105
  82. package/src/users/UsersClient.ts +10 -14
  83. package/src/util/shareReplayLatest.ts +1 -1
  84. package/src/warnings.ts +6 -1
  85. package/dist/_chunks-cjs/config.cjs +0 -206
  86. package/dist/_chunks-cjs/config.cjs.map +0 -1
  87. package/dist/_chunks-cjs/isRecord.cjs +0 -6
  88. package/dist/_chunks-cjs/isRecord.cjs.map +0 -1
  89. package/dist/_chunks-cjs/resolveEditInfo.cjs +0 -334
  90. package/dist/_chunks-cjs/resolveEditInfo.cjs.map +0 -1
  91. package/dist/_chunks-cjs/stegaClean.cjs +0 -46
  92. package/dist/_chunks-cjs/stegaClean.cjs.map +0 -1
  93. package/dist/_chunks-cjs/stegaEncodeSourceMap.cjs +0 -388
  94. package/dist/_chunks-cjs/stegaEncodeSourceMap.cjs.map +0 -1
  95. package/dist/_chunks-es/config.js +0 -207
  96. package/dist/_chunks-es/config.js.map +0 -1
  97. package/dist/_chunks-es/isRecord.js +0 -7
  98. package/dist/_chunks-es/isRecord.js.map +0 -1
  99. package/dist/_chunks-es/resolveEditInfo.js +0 -335
  100. package/dist/_chunks-es/resolveEditInfo.js.map +0 -1
  101. package/dist/_chunks-es/stegaClean.js +0 -47
  102. package/dist/_chunks-es/stegaClean.js.map +0 -1
  103. package/dist/_chunks-es/stegaEncodeSourceMap.js +0 -389
  104. package/dist/_chunks-es/stegaEncodeSourceMap.js.map +0 -1
  105. package/dist/csm.cjs +0 -99
  106. package/dist/csm.cjs.map +0 -1
  107. package/dist/csm.d.cts +0 -391
  108. package/dist/index.browser.cjs +0 -3106
  109. package/dist/index.browser.cjs.map +0 -1
  110. package/dist/index.browser.d.ts +0 -6802
  111. package/dist/index.browser.js +0 -3082
  112. package/dist/index.browser.js.map +0 -1
  113. package/dist/index.cjs +0 -2946
  114. package/dist/index.cjs.map +0 -1
  115. package/dist/media-library.cjs +0 -20
  116. package/dist/media-library.cjs.map +0 -1
  117. package/dist/media-library.d.cts +0 -117
  118. package/dist/stega.browser.cjs +0 -29
  119. package/dist/stega.browser.cjs.map +0 -1
  120. package/dist/stega.browser.d.cts +0 -7200
  121. package/dist/stega.browser.d.ts +0 -7200
  122. package/dist/stega.browser.js +0 -24
  123. package/dist/stega.browser.js.map +0 -1
  124. package/dist/stega.cjs +0 -29
  125. package/dist/stega.cjs.map +0 -1
  126. package/dist/stega.d.cts +0 -7200
  127. package/src/data/eventsourcePolyfill.ts +0 -7
  128. package/umd/.gitkeep +0 -1
  129. package/umd/sanityClient.js +0 -6543
  130. package/umd/sanityClient.min.js +0 -6
@@ -0,0 +1,162 @@
1
+ import {createDebug} from 'obug'
2
+ import {Observable} from 'rxjs'
3
+
4
+ import type {UploadEvent} from '../types'
5
+ import {ClientError, httpResponseFromFetch, ServerError} from './errors'
6
+ import {parseJsonText} from './request'
7
+
8
+ const log = createDebug('sanity:client')
9
+
10
+ let nextRequestId = 1
11
+
12
+ /**
13
+ * Options for a browser-side asset upload that needs progress events.
14
+ *
15
+ * @internal
16
+ */
17
+ export interface BrowserUploadOptions {
18
+ url: string
19
+ method: string
20
+ headers: Record<string, string>
21
+ body: unknown
22
+ withCredentials: boolean
23
+ /** Milliseconds before the upload is aborted; `false` and `0` both disable the timeout. */
24
+ timeout?: number | false
25
+ signal?: AbortSignal
26
+ }
27
+
28
+ /**
29
+ * Run an asset upload through `XMLHttpRequest` so we can surface per-chunk
30
+ * upload progress events. get-it v9 / fetch has no equivalent hook in the
31
+ * browser, so the observable asset-upload API falls back to this path when
32
+ * `XMLHttpRequest` is available.
33
+ *
34
+ * @internal
35
+ */
36
+ export function uploadWithProgress<T>(options: BrowserUploadOptions): Observable<UploadEvent<T>> {
37
+ return new Observable<UploadEvent<T>>((subscriber) => {
38
+ const xhr = new XMLHttpRequest()
39
+ const requestId = nextRequestId++
40
+ const {url, method, headers, body, withCredentials, timeout, signal} = options
41
+
42
+ log('[%d] %s %s (XHR upload with progress)', requestId, method, url)
43
+
44
+ xhr.open(method, url)
45
+ xhr.withCredentials = withCredentials
46
+ if (typeof timeout === 'number' && timeout > 0) {
47
+ xhr.timeout = timeout
48
+ }
49
+
50
+ for (const [key, value] of Object.entries(headers)) {
51
+ xhr.setRequestHeader(key, value)
52
+ }
53
+
54
+ xhr.upload.onprogress = (e) => {
55
+ subscriber.next({
56
+ type: 'progress',
57
+ stage: 'upload',
58
+ percent: e.lengthComputable ? Math.round((e.loaded / e.total) * 100) : 0,
59
+ total: e.total || undefined,
60
+ loaded: e.loaded,
61
+ lengthComputable: e.lengthComputable,
62
+ })
63
+ }
64
+
65
+ xhr.onload = () => {
66
+ log('[%d] %s %s — %d', requestId, method, url, xhr.status)
67
+
68
+ if (xhr.status >= 400) {
69
+ // Same typed errors as the fetch transport, so consumers can keep
70
+ // detecting `ClientError`/`ServerError` and reading `statusCode`,
71
+ // `responseBody` and the structured API `details` on failed uploads.
72
+ const errorHeaders = parseXhrResponseHeaders(xhr.getAllResponseHeaders())
73
+ const canonical = httpResponseFromFetch(
74
+ {
75
+ status: xhr.status,
76
+ statusText: xhr.statusText,
77
+ headers: errorHeaders,
78
+ body: parseJsonText(xhr.responseText, errorHeaders),
79
+ },
80
+ url,
81
+ method,
82
+ )
83
+ subscriber.error(
84
+ xhr.status >= 500 ? new ServerError(canonical) : new ClientError(canonical),
85
+ )
86
+ return
87
+ }
88
+
89
+ let responseBody: T
90
+ try {
91
+ responseBody = JSON.parse(xhr.responseText) as T
92
+ } catch {
93
+ subscriber.error(new Error('Failed to parse upload response as JSON'))
94
+ return
95
+ }
96
+
97
+ subscriber.next({type: 'response', body: responseBody})
98
+ subscriber.complete()
99
+ }
100
+
101
+ xhr.onerror = () => {
102
+ log('[%d] %s %s — network error', requestId, method, url)
103
+ subscriber.error(new Error('XHR upload network error'))
104
+ }
105
+
106
+ xhr.ontimeout = () => {
107
+ log('[%d] %s %s — timed out after %dms', requestId, method, url, timeout)
108
+ // Same error shape as the fetch transport's timeout rejection.
109
+ subscriber.error(
110
+ new DOMException(
111
+ `The operation timed out after ${timeout}ms while attempting to reach ${url}`,
112
+ 'TimeoutError',
113
+ ),
114
+ )
115
+ }
116
+
117
+ xhr.onabort = () => {
118
+ subscriber.error(new DOMException('Upload aborted', 'AbortError'))
119
+ }
120
+
121
+ const onSignalAbort = () => xhr.abort()
122
+ if (signal) {
123
+ if (signal.aborted) {
124
+ // `xhr.abort()` before `send()` fires no `abort` event per spec, so
125
+ // error out directly instead of relying on `onabort`.
126
+ subscriber.error(new DOMException('Upload aborted', 'AbortError'))
127
+ return undefined
128
+ }
129
+ signal.addEventListener('abort', onSignalAbort, {once: true})
130
+ }
131
+
132
+ xhr.send(body as XMLHttpRequestBodyInit)
133
+
134
+ // Unsubscribing cancels the in-flight upload, mirroring how the fetch
135
+ // path aborts its request (`_observe`). After settle this is a no-op —
136
+ // except for detaching from the caller's signal, which may be long-lived
137
+ // and must not accumulate a listener per upload.
138
+ return () => {
139
+ signal?.removeEventListener('abort', onSignalAbort)
140
+ xhr.abort()
141
+ }
142
+ })
143
+ }
144
+
145
+ /**
146
+ * Parse `XMLHttpRequest.getAllResponseHeaders()` output (CRLF-separated
147
+ * `name: value` lines) into a `Headers` instance.
148
+ */
149
+ function parseXhrResponseHeaders(raw: string): Headers {
150
+ const headers = new Headers()
151
+ for (const line of raw.split('\r\n')) {
152
+ const separator = line.indexOf(':')
153
+ if (separator <= 0) continue
154
+ try {
155
+ headers.append(line.slice(0, separator).trim(), line.slice(separator + 1).trim())
156
+ } catch {
157
+ // Skip header lines the Headers constructor rejects — better a partial
158
+ // header record on the error than no error details at all.
159
+ }
160
+ }
161
+ return headers
162
+ }
@@ -1,11 +1,66 @@
1
- import type {HttpContext} from 'get-it'
2
-
3
1
  import type {ActionError, Any, ErrorProps, MutationError, QueryParseError} from '../types'
4
2
  import {codeFrame} from '../util/codeFrame'
5
3
  import {isRecord} from '../util/isRecord'
6
4
 
7
5
  const MAX_ITEMS_IN_ERROR_MESSAGE = 5
8
6
 
7
+ /**
8
+ * Canonical HTTP response shape used internally to build {@link ClientError}
9
+ * and {@link ServerError}. Decouples the error layer from any particular
10
+ * transport library — adapters in the HTTP layer (e.g. `httpResponseFromGetIt`)
11
+ * project transport-specific response shapes into this stable form before
12
+ * constructing errors.
13
+ *
14
+ * Matches the public {@link HttpError.response} shape so consumers see no
15
+ * difference in error properties when the underlying transport changes.
16
+ *
17
+ * @internal
18
+ */
19
+ export interface CanonicalHttpResponse {
20
+ statusCode: number
21
+ statusMessage: string | null
22
+ headers: Record<string, string>
23
+ body: unknown
24
+ url: string
25
+ method: string
26
+ }
27
+
28
+ /**
29
+ * Adapter for buffered responses from get-it v9 (`BufferedResponse`-shaped).
30
+ *
31
+ * The URL and method aren't on the response itself in v9, so the request
32
+ * options must be passed alongside.
33
+ *
34
+ * @internal
35
+ */
36
+ export function httpResponseFromFetch(
37
+ res: {
38
+ status: number
39
+ statusText: string
40
+ headers: Headers
41
+ body: unknown
42
+ },
43
+ reqUrl: string,
44
+ reqMethod: string,
45
+ ): CanonicalHttpResponse {
46
+ return {
47
+ statusCode: res.status,
48
+ statusMessage: res.statusText || null,
49
+ headers: headersToRecord(res.headers),
50
+ body: res.body,
51
+ url: reqUrl,
52
+ method: reqMethod,
53
+ }
54
+ }
55
+
56
+ function headersToRecord(headers: Headers): Record<string, string> {
57
+ const out: Record<string, string> = {}
58
+ headers.forEach((value, key) => {
59
+ out[key] = value
60
+ })
61
+ return out
62
+ }
63
+
9
64
  /**
10
65
  * Shared properties for HTTP errors (eg both ClientError and ServerError)
11
66
  * Use `isHttpError` for type narrowing and accessing response properties.
@@ -67,8 +122,8 @@ export class ClientError extends Error {
67
122
  traceId: ErrorProps['traceId']
68
123
  details: ErrorProps['details']
69
124
 
70
- constructor(res: Any, context?: HttpContext) {
71
- const props = extractErrorProps(res, context)
125
+ constructor(res: Any, tag?: string) {
126
+ const props = extractErrorProps(res, tag)
72
127
  super(props.message)
73
128
  Object.assign(this, props)
74
129
  }
@@ -89,7 +144,7 @@ export class ServerError extends Error {
89
144
  }
90
145
  }
91
146
 
92
- function extractErrorProps(res: Any, context?: HttpContext): ErrorProps {
147
+ function extractErrorProps(res: Any, tag?: string): ErrorProps {
93
148
  const body = res.body
94
149
  const props = {
95
150
  response: res,
@@ -144,7 +199,6 @@ function extractErrorProps(res: Any, context?: HttpContext): ErrorProps {
144
199
 
145
200
  // Query parse errors
146
201
  if (isQueryParseError(error)) {
147
- const tag = context?.options?.query?.tag
148
202
  props.message = formatQueryParseError(error, tag, props.traceId)
149
203
  props.details = body.error
150
204
  return props
@@ -1,41 +1,105 @@
1
- import type {Middleware, RequestOptions} from 'get-it'
2
- import {agent, debug, headers} from 'get-it/middleware'
1
+ import {Readable} from 'node:stream'
2
+
3
+ import type {FetchFunction} from 'get-it'
4
+ import {debug} from 'get-it/middleware'
5
+ import {createNodeFetch} from 'get-it/node'
6
+ import {createDebug} from 'obug'
3
7
 
4
8
  import {name, version} from '../../package.json'
9
+ import type {EnvironmentOptions, LegacyMiddleware} from './request'
10
+
11
+ const log = createDebug('sanity:client')
12
+
13
+ function isNodeReadableStream(value: unknown): value is Readable {
14
+ if (typeof value !== 'object' || value === null) return false
15
+ if (!('pipe' in value)) return false
16
+ return typeof value.pipe === 'function'
17
+ }
18
+
19
+ // One undici-backed fetch per (proxy URL) so callers don't pay the cost of
20
+ // rebuilding a dispatcher on every request when they use the same proxy.
21
+ const proxyFetchCache = new Map<string, ReturnType<typeof createNodeFetch>>()
22
+
23
+ function getProxyFetch(proxyUrl: string): ReturnType<typeof createNodeFetch> {
24
+ const cached = proxyFetchCache.get(proxyUrl)
25
+ if (cached) return cached
26
+ const fetch = createNodeFetch({proxy: proxyUrl, connections: 30})
27
+ proxyFetchCache.set(proxyUrl, fetch)
28
+ return fetch
29
+ }
30
+
31
+ // The environment's default fetch, shared by all SSE connections that have no
32
+ // explicit proxy configured. Built lazily so clients that never use
33
+ // `listen()`/`live.events()` don't pay for the dispatcher.
34
+ let envDefaultFetch: ReturnType<typeof createNodeFetch> | undefined
5
35
 
6
- const middleware: Middleware[] = [
7
- debug({verbose: true, namespace: 'sanity:client'}),
8
- headers({'User-Agent': `${name} ${version}`}),
36
+ /**
37
+ * Exposed via `EnvironmentOptions.resolveFetch` so EventSource connections
38
+ * use the same transport as regular requests instead of falling back to
39
+ * `globalThis.fetch`. Everything get-it's Node fetch provides then applies
40
+ * to SSE too: the undici dispatcher configuration, an explicit `proxy`
41
+ * config (pass the URL), and env-proxy support
42
+ * (`HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`, which Node's own global fetch does
43
+ * not read — that is opt-in via `NODE_USE_ENV_PROXY`, and not on our 22.12
44
+ * floor). Reached via the env rather than a direct import so `get-it/node`
45
+ * (and with it `undici`) stays out of the browser bundle.
46
+ *
47
+ * @internal
48
+ */
49
+ function resolveFetch(proxyUrl?: string): FetchFunction {
50
+ if (typeof proxyUrl === 'string') {
51
+ return getProxyFetch(proxyUrl)
52
+ }
53
+ envDefaultFetch ??= createNodeFetch()
54
+ return envDefaultFetch
55
+ }
9
56
 
10
- // Lineage is used for recursion control/tracing and can be passed either through
11
- // client constructor or through environent variable.
12
- // Not used in browser environments.
57
+ const middleware: LegacyMiddleware[] = [
58
+ debug({log: (message, ...args) => log(message, ...args), verbose: true}),
59
+
60
+ // Lineage is used for recursion control/tracing and can be passed either
61
+ // through the client config (arrives as `meta.lineage`, set by
62
+ // `requestOptions`) or through an environment variable. Not used in
63
+ // browser environments.
13
64
  {
14
- processOptions(opts: RequestOptions & {lineage?: string}) {
65
+ beforeRequest(opts) {
66
+ const configLineage = typeof opts.meta?.lineage === 'string' ? opts.meta.lineage : undefined
15
67
  const lineage =
16
- (typeof process !== 'undefined' && process.env.X_SANITY_LINEAGE) || opts.lineage
17
-
18
- if (lineage) {
19
- opts.headers = opts.headers || {}
20
- opts.headers['x-sanity-lineage'] = lineage
21
- }
22
- return opts
68
+ (typeof process !== 'undefined' && process.env.X_SANITY_LINEAGE) || configLineage
69
+ if (!lineage) return opts
70
+ const headers = new Headers(opts.headers)
71
+ headers.set('x-sanity-lineage', lineage)
72
+ return {...opts, headers}
23
73
  },
24
74
  },
25
75
 
26
- // Enable keep-alive, and in addition limit the number of sockets that can be opened.
27
- // This avoids opening too many connections to the server if someone tries to execute
28
- // a bunch of requests in parallel. It's recommended to have a concurrency limit
29
- // at a "higher limit" (i.e. you shouldn't actually execute hundreds of requests in parallel),
30
- // and this is mainly to minimize the impact for the network and server.
31
- //
32
- // We're currently matching the same defaults as browsers:
33
- // https://stackoverflow.com/questions/26003756/is-there-a-limit-practical-or-otherwise-to-the-number-of-web-sockets-a-page-op
34
- agent({
35
- keepAlive: true,
36
- maxSockets: 30,
37
- maxTotalSockets: 256,
38
- }),
76
+ // Asset uploads can pass a Node.js Readable stream (from
77
+ // `fs.createReadStream(...)`) as the body. The HTTP transport's body type
78
+ // guard only accepts Web streams, so we project Node streams here
79
+ // undici's fetch accepts Web streams natively.
80
+ {
81
+ beforeRequest(opts) {
82
+ if (!isNodeReadableStream(opts.body)) return opts
83
+ return {...opts, body: Readable.toWeb(opts.body)}
84
+ },
85
+ },
39
86
  ]
40
87
 
41
- export default middleware
88
+ /**
89
+ * Node-specific environment options used to wire up `createRequester`.
90
+ *
91
+ * - `User-Agent` defaults to `@sanity/client <version>`.
92
+ * - Falls back to get-it/node's default undici-backed fetch (which honours
93
+ * `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` env vars via undici's
94
+ * `EnvHttpProxyAgent`). Per-request proxy overrides are wired in via the
95
+ * wrapping middleware above.
96
+ *
97
+ * @internal
98
+ */
99
+ const environment: EnvironmentOptions = {
100
+ headers: {'User-Agent': `${name} ${version}`},
101
+ middleware,
102
+ resolveFetch,
103
+ }
104
+
105
+ export default environment