@sanity/client 7.20.0 → 7.22.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.
@@ -810,6 +810,32 @@ export declare interface ClientConfig {
810
810
  * Lineage token for recursion control
811
811
  */
812
812
  lineage?: string
813
+ /**
814
+ * A custom request handler that intercepts all HTTP requests made by the client.
815
+ *
816
+ * Useful for logging, adding custom headers, refreshing auth tokens, rate limiting, etc.
817
+ *
818
+ * When using `withConfig()`, the new handler **replaces** the previous one (it does not
819
+ * wrap it). To compose handlers, you can chain them manually:
820
+ *
821
+ * ```ts
822
+ * const parent = createClient({...config, _requestHandler: handlerA})
823
+ * const child = parent.withConfig({
824
+ * _requestHandler: (req, defaultRequester) =>
825
+ * handlerB(req, (opts) => handlerA(opts, defaultRequester)),
826
+ * })
827
+ * ```
828
+ *
829
+ * Setting `_requestHandler` to `undefined` via `withConfig()` removes the handler.
830
+ *
831
+ * Note: This only applies to HTTP requests. Real-time listener connections
832
+ * (`client.listen()`) use EventSource and are not intercepted by this handler.
833
+ *
834
+ * @internal
835
+ * @deprecated Don't use outside of Sanity internals
836
+ * @see {@link RequestHandler}
837
+ */
838
+ _requestHandler?: RequestHandler
813
839
  }
814
840
 
815
841
  declare type ClientConfigResource =
@@ -835,6 +861,7 @@ export declare class ClientError extends Error {
835
861
  response: ErrorProps['response']
836
862
  statusCode: ErrorProps['statusCode']
837
863
  responseBody: ErrorProps['responseBody']
864
+ traceId: ErrorProps['traceId']
838
865
  details: ErrorProps['details']
839
866
  constructor(res: Any, context?: HttpContext)
840
867
  }
@@ -1391,6 +1418,7 @@ export declare interface ErrorProps {
1391
1418
  response: Any
1392
1419
  statusCode: number
1393
1420
  responseBody: Any
1421
+ traceId?: string
1394
1422
  details: Any
1395
1423
  }
1396
1424
 
@@ -1541,7 +1569,11 @@ export declare type FitMode = 'preserve' | 'stretch' | 'crop' | 'smartcrop' | 'p
1541
1569
  * @returns A formatted error message string.
1542
1570
  * @public
1543
1571
  */
1544
- export declare function formatQueryParseError(error: QueryParseError, tag?: string | null): string
1572
+ export declare function formatQueryParseError(
1573
+ error: QueryParseError,
1574
+ tag?: string | null,
1575
+ traceId?: string,
1576
+ ): string
1545
1577
 
1546
1578
  /** @beta */
1547
1579
  declare type GenerateAsyncInstruction<T extends Record<string, Any> = Record<string, Any>> = (
@@ -2161,6 +2193,7 @@ export declare class LiveClient {
2161
2193
  events({
2162
2194
  includeDrafts,
2163
2195
  tag: _tag,
2196
+ waitFor,
2164
2197
  }?: {
2165
2198
  includeDrafts?: boolean
2166
2199
  /**
@@ -2169,6 +2202,11 @@ export declare class LiveClient {
2169
2202
  * @defaultValue `undefined`
2170
2203
  */
2171
2204
  tag?: string
2205
+ /**
2206
+ * Delays events until after a Sanity Function has processed them and called the callback endpoint.
2207
+ * When omitted, events are delivered immediately.
2208
+ */
2209
+ waitFor?: 'function'
2172
2210
  }): Observable<LiveEvent>
2173
2211
  }
2174
2212
 
@@ -4778,6 +4816,44 @@ export declare interface ReplaceVersionAction {
4778
4816
  /** @public */
4779
4817
  export declare const requester: Requester
4780
4818
 
4819
+ /**
4820
+ * A function that intercepts HTTP requests made by the client.
4821
+ *
4822
+ * Receives the resolved request options, a `defaultRequester` function that
4823
+ * executes the request through the normal pipeline, and a `client` instance
4824
+ * without a `_requestHandler` (to avoid recursive interception).
4825
+ *
4826
+ * The consumer can:
4827
+ * - Modify request options before calling `defaultRequester`
4828
+ * - Transform the response stream (e.g. via `pipe`)
4829
+ * - Skip `defaultRequester` entirely and return a custom Observable
4830
+ * - Use `client` to make additional requests (e.g. refresh an auth token on 401)
4831
+ *
4832
+ * When set via `withConfig()`, the new handler **replaces** (not wraps) the previous one.
4833
+ *
4834
+ * Note: This only applies to HTTP requests. Real-time listener connections
4835
+ * (`client.listen()`) use EventSource and are not intercepted by this handler.
4836
+ *
4837
+ * @param request - The resolved request options including `url`
4838
+ * @param defaultRequester - Executes the request through the normal pipeline
4839
+ * @param client - A client instance with the same configuration but without a `_requestHandler`,
4840
+ * useful for making side requests (e.g. token refresh) without triggering the handler recursively
4841
+ *
4842
+ * @internal
4843
+ * @deprecated Don't use outside of Sanity internals
4844
+ */
4845
+ export declare type RequestHandler = (
4846
+ request: RequestOptions & {
4847
+ url: string
4848
+ },
4849
+ defaultRequester: (
4850
+ options: RequestOptions & {
4851
+ url: string
4852
+ },
4853
+ ) => Observable<HttpRequestEvent>,
4854
+ client: SanityClient,
4855
+ ) => Observable<HttpRequestEvent>
4856
+
4781
4857
  /** @internal */
4782
4858
  export declare interface RequestObservableOptions extends Omit<RequestOptions, 'url'> {
4783
4859
  url?: string
@@ -5809,6 +5885,7 @@ export declare class ServerError extends Error {
5809
5885
  response: ErrorProps['response']
5810
5886
  statusCode: ErrorProps['statusCode']
5811
5887
  responseBody: ErrorProps['responseBody']
5888
+ traceId: ErrorProps['traceId']
5812
5889
  details: ErrorProps['details']
5813
5890
  constructor(res: Any)
5814
5891
  }
@@ -810,6 +810,32 @@ export declare interface ClientConfig {
810
810
  * Lineage token for recursion control
811
811
  */
812
812
  lineage?: string
813
+ /**
814
+ * A custom request handler that intercepts all HTTP requests made by the client.
815
+ *
816
+ * Useful for logging, adding custom headers, refreshing auth tokens, rate limiting, etc.
817
+ *
818
+ * When using `withConfig()`, the new handler **replaces** the previous one (it does not
819
+ * wrap it). To compose handlers, you can chain them manually:
820
+ *
821
+ * ```ts
822
+ * const parent = createClient({...config, _requestHandler: handlerA})
823
+ * const child = parent.withConfig({
824
+ * _requestHandler: (req, defaultRequester) =>
825
+ * handlerB(req, (opts) => handlerA(opts, defaultRequester)),
826
+ * })
827
+ * ```
828
+ *
829
+ * Setting `_requestHandler` to `undefined` via `withConfig()` removes the handler.
830
+ *
831
+ * Note: This only applies to HTTP requests. Real-time listener connections
832
+ * (`client.listen()`) use EventSource and are not intercepted by this handler.
833
+ *
834
+ * @internal
835
+ * @deprecated Don't use outside of Sanity internals
836
+ * @see {@link RequestHandler}
837
+ */
838
+ _requestHandler?: RequestHandler
813
839
  }
814
840
 
815
841
  declare type ClientConfigResource =
@@ -835,6 +861,7 @@ export declare class ClientError extends Error {
835
861
  response: ErrorProps['response']
836
862
  statusCode: ErrorProps['statusCode']
837
863
  responseBody: ErrorProps['responseBody']
864
+ traceId: ErrorProps['traceId']
838
865
  details: ErrorProps['details']
839
866
  constructor(res: Any, context?: HttpContext)
840
867
  }
@@ -1391,6 +1418,7 @@ export declare interface ErrorProps {
1391
1418
  response: Any
1392
1419
  statusCode: number
1393
1420
  responseBody: Any
1421
+ traceId?: string
1394
1422
  details: Any
1395
1423
  }
1396
1424
 
@@ -1541,7 +1569,11 @@ export declare type FitMode = 'preserve' | 'stretch' | 'crop' | 'smartcrop' | 'p
1541
1569
  * @returns A formatted error message string.
1542
1570
  * @public
1543
1571
  */
1544
- export declare function formatQueryParseError(error: QueryParseError, tag?: string | null): string
1572
+ export declare function formatQueryParseError(
1573
+ error: QueryParseError,
1574
+ tag?: string | null,
1575
+ traceId?: string,
1576
+ ): string
1545
1577
 
1546
1578
  /** @beta */
1547
1579
  declare type GenerateAsyncInstruction<T extends Record<string, Any> = Record<string, Any>> = (
@@ -2161,6 +2193,7 @@ export declare class LiveClient {
2161
2193
  events({
2162
2194
  includeDrafts,
2163
2195
  tag: _tag,
2196
+ waitFor,
2164
2197
  }?: {
2165
2198
  includeDrafts?: boolean
2166
2199
  /**
@@ -2169,6 +2202,11 @@ export declare class LiveClient {
2169
2202
  * @defaultValue `undefined`
2170
2203
  */
2171
2204
  tag?: string
2205
+ /**
2206
+ * Delays events until after a Sanity Function has processed them and called the callback endpoint.
2207
+ * When omitted, events are delivered immediately.
2208
+ */
2209
+ waitFor?: 'function'
2172
2210
  }): Observable<LiveEvent>
2173
2211
  }
2174
2212
 
@@ -4778,6 +4816,44 @@ export declare interface ReplaceVersionAction {
4778
4816
  /** @public */
4779
4817
  export declare const requester: Requester
4780
4818
 
4819
+ /**
4820
+ * A function that intercepts HTTP requests made by the client.
4821
+ *
4822
+ * Receives the resolved request options, a `defaultRequester` function that
4823
+ * executes the request through the normal pipeline, and a `client` instance
4824
+ * without a `_requestHandler` (to avoid recursive interception).
4825
+ *
4826
+ * The consumer can:
4827
+ * - Modify request options before calling `defaultRequester`
4828
+ * - Transform the response stream (e.g. via `pipe`)
4829
+ * - Skip `defaultRequester` entirely and return a custom Observable
4830
+ * - Use `client` to make additional requests (e.g. refresh an auth token on 401)
4831
+ *
4832
+ * When set via `withConfig()`, the new handler **replaces** (not wraps) the previous one.
4833
+ *
4834
+ * Note: This only applies to HTTP requests. Real-time listener connections
4835
+ * (`client.listen()`) use EventSource and are not intercepted by this handler.
4836
+ *
4837
+ * @param request - The resolved request options including `url`
4838
+ * @param defaultRequester - Executes the request through the normal pipeline
4839
+ * @param client - A client instance with the same configuration but without a `_requestHandler`,
4840
+ * useful for making side requests (e.g. token refresh) without triggering the handler recursively
4841
+ *
4842
+ * @internal
4843
+ * @deprecated Don't use outside of Sanity internals
4844
+ */
4845
+ export declare type RequestHandler = (
4846
+ request: RequestOptions & {
4847
+ url: string
4848
+ },
4849
+ defaultRequester: (
4850
+ options: RequestOptions & {
4851
+ url: string
4852
+ },
4853
+ ) => Observable<HttpRequestEvent>,
4854
+ client: SanityClient,
4855
+ ) => Observable<HttpRequestEvent>
4856
+
4781
4857
  /** @internal */
4782
4858
  export declare interface RequestObservableOptions extends Omit<RequestOptions, 'url'> {
4783
4859
  url?: string
@@ -5809,6 +5885,7 @@ export declare class ServerError extends Error {
5809
5885
  response: ErrorProps['response']
5810
5886
  statusCode: ErrorProps['statusCode']
5811
5887
  responseBody: ErrorProps['responseBody']
5888
+ traceId: ErrorProps['traceId']
5812
5889
  details: ErrorProps['details']
5813
5890
  constructor(res: Any)
5814
5891
  }
@@ -85,6 +85,7 @@ class ClientError extends Error {
85
85
  response;
86
86
  statusCode = 400;
87
87
  responseBody;
88
+ traceId;
88
89
  details;
89
90
  constructor(res, context) {
90
91
  const props = extractErrorProps(res, context);
@@ -95,6 +96,7 @@ class ServerError extends Error {
95
96
  response;
96
97
  statusCode = 500;
97
98
  responseBody;
99
+ traceId;
98
100
  details;
99
101
  constructor(res) {
100
102
  const props = extractErrorProps(res);
@@ -106,29 +108,30 @@ function extractErrorProps(res, context) {
106
108
  response: res,
107
109
  statusCode: res.statusCode,
108
110
  responseBody: stringifyBody(body, res),
111
+ traceId: extractTraceId(res),
109
112
  message: "",
110
113
  details: void 0
111
114
  };
112
115
  if (!isRecord(body))
113
- return props.message = httpErrorMessage(res, body), props;
116
+ return props.message = `${httpErrorMessage(res, body)}${formatTraceId(props.traceId)}`, props;
114
117
  const error = body.error;
115
118
  if (typeof error == "string" && typeof body.message == "string")
116
- return props.message = `${error} - ${body.message}`, props;
119
+ return props.message = `${error} - ${body.message}${formatTraceId(props.traceId)}`, props;
117
120
  if (typeof error != "object" || error === null)
118
- return typeof error == "string" ? props.message = error : typeof body.message == "string" ? props.message = body.message : props.message = httpErrorMessage(res, body), props;
121
+ return typeof error == "string" ? props.message = `${error}${formatTraceId(props.traceId)}` : typeof body.message == "string" ? props.message = `${body.message}${formatTraceId(props.traceId)}` : props.message = `${httpErrorMessage(res, body)}${formatTraceId(props.traceId)}`, props;
119
122
  if (isMutationError(error) || isActionError(error)) {
120
123
  const allItems = error.items || [], items = allItems.slice(0, MAX_ITEMS_IN_ERROR_MESSAGE).map((item) => item.error?.description).filter(Boolean);
121
124
  let itemsStr = items.length ? `:
122
125
  - ${items.join(`
123
126
  - `)}` : "";
124
127
  return allItems.length > MAX_ITEMS_IN_ERROR_MESSAGE && (itemsStr += `
125
- ...and ${allItems.length - MAX_ITEMS_IN_ERROR_MESSAGE} more`), props.message = `${error.description}${itemsStr}`, props.details = body.error, props;
128
+ ...and ${allItems.length - MAX_ITEMS_IN_ERROR_MESSAGE} more`), props.message = `${error.description}${formatTraceId(props.traceId)}${itemsStr}`, props.details = body.error, props;
126
129
  }
127
130
  if (isQueryParseError(error)) {
128
131
  const tag = context?.options?.query?.tag;
129
- return props.message = formatQueryParseError(error, tag), props.details = body.error, props;
132
+ return props.message = formatQueryParseError(error, tag, props.traceId), props.details = body.error, props;
130
133
  }
131
- return "description" in error && typeof error.description == "string" ? (props.message = error.description, props.details = error, props) : (props.message = httpErrorMessage(res, body), props);
134
+ return "description" in error && typeof error.description == "string" ? (props.message = `${error.description}${formatTraceId(props.traceId)}`, props.details = error, props) : (props.message = `${httpErrorMessage(res, body)}${formatTraceId(props.traceId)}`, props);
132
135
  }
133
136
  function isMutationError(error) {
134
137
  return "type" in error && error.type === "mutationError" && "description" in error && typeof error.description == "string";
@@ -139,23 +142,32 @@ function isActionError(error) {
139
142
  function isQueryParseError(error) {
140
143
  return isRecord(error) && error.type === "queryParseError" && typeof error.query == "string" && typeof error.start == "number" && typeof error.end == "number";
141
144
  }
142
- function formatQueryParseError(error, tag) {
143
- const { query, start, end, description } = error;
145
+ function formatQueryParseError(error, tag, traceId) {
146
+ const { query, start, end, description } = error, withTraceId = traceId ? `
147
+ (traceId: ${traceId})` : "";
144
148
  if (!query || typeof start > "u")
145
- return `GROQ query parse error: ${description}`;
149
+ return `GROQ query parse error: ${description}${withTraceId}`;
146
150
  const withTag = tag ? `
147
151
 
148
152
  Tag: ${tag}` : "";
149
153
  return `GROQ query parse error:
150
- ${codeFrame(query, { start, end }, description)}${withTag}`;
154
+ ${codeFrame(query, { start, end }, description)}${withTag}${withTraceId}`;
151
155
  }
152
156
  function httpErrorMessage(res, body) {
153
157
  const details = typeof body == "string" ? ` (${sliceWithEllipsis(body, 100)})` : "", statusMessage = res.statusMessage ? ` ${res.statusMessage}` : "";
154
158
  return `${res.method}-request to ${res.url} resulted in HTTP ${res.statusCode}${statusMessage}${details}`;
155
159
  }
160
+ function extractTraceId(res) {
161
+ const traceparent = res?.headers?.traceparent;
162
+ if (traceparent)
163
+ return traceparent.split("-")[1];
164
+ }
156
165
  function stringifyBody(body, res) {
157
166
  return (res.headers["content-type"] || "").toLowerCase().indexOf("application/json") !== -1 ? JSON.stringify(body, null, 2) : body;
158
167
  }
168
+ function formatTraceId(traceId) {
169
+ return traceId ? ` (traceId: ${traceId})` : "";
170
+ }
159
171
  function sliceWithEllipsis(str, max) {
160
172
  return str.length > max ? `${str.slice(0, max)}\u2026` : str;
161
173
  }
@@ -1451,7 +1463,8 @@ class LiveClient {
1451
1463
  */
1452
1464
  events({
1453
1465
  includeDrafts = !1,
1454
- tag: _tag
1466
+ tag: _tag,
1467
+ waitFor
1455
1468
  } = {}) {
1456
1469
  const {
1457
1470
  projectId: projectId2,
@@ -1470,7 +1483,7 @@ class LiveClient {
1470
1483
  "The live events API requires a token or withCredentials when 'includeDrafts: true'. Please update your client configuration. The token should have the lowest possible access role."
1471
1484
  );
1472
1485
  const path = _getDataUrl(this.#client, "live/events"), url = new URL(this.#client.getUrl(path, !1)), tag = _tag && requestTagPrefix ? [requestTagPrefix, _tag].join(".") : _tag;
1473
- tag && url.searchParams.set("tag", tag), includeDrafts && url.searchParams.set("includeDrafts", "true");
1486
+ tag && url.searchParams.set("tag", tag), includeDrafts && url.searchParams.set("includeDrafts", "true"), waitFor && url.searchParams.set("waitFor", waitFor);
1474
1487
  const esOptions = {};
1475
1488
  includeDrafts && withCredentials && (esOptions.withCredentials = !0), (includeDrafts && token || configHeaders) && (esOptions.headers = {}, includeDrafts && token && (esOptions.headers.Authorization = `Bearer ${token}`), configHeaders && Object.assign(esOptions.headers, configHeaders));
1476
1489
  const key = `${url.href}::${JSON.stringify(esOptions)}`, existing = eventsCache.get(key);
@@ -2368,13 +2381,22 @@ class ObservableSanityClient {
2368
2381
  * Private properties
2369
2382
  */
2370
2383
  #clientConfig;
2384
+ #originalHttpRequest;
2371
2385
  #httpRequest;
2372
2386
  /**
2373
2387
  * Instance properties
2374
2388
  */
2375
2389
  listen = _listen;
2376
2390
  constructor(httpRequest, config = defaultConfig) {
2377
- this.config(config), this.#httpRequest = httpRequest, this.assets = new ObservableAssetsClient(this, this.#httpRequest), this.datasets = new ObservableDatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.mediaLibrary = {
2391
+ this.config(config), this.#originalHttpRequest = httpRequest;
2392
+ const requestHandler = config._requestHandler;
2393
+ this.#httpRequest = requestHandler ? /* @__PURE__ */ (() => {
2394
+ let bareClient;
2395
+ return (options, requester2) => {
2396
+ const opts = options;
2397
+ return bareClient || (bareClient = new SanityClient(httpRequest, { ...config, _requestHandler: void 0 })), requestHandler(opts, (o) => httpRequest(o, requester2), bareClient);
2398
+ };
2399
+ })() : httpRequest, this.assets = new ObservableAssetsClient(this, this.#httpRequest), this.datasets = new ObservableDatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.mediaLibrary = {
2378
2400
  video: new ObservableMediaLibraryVideoClient(this, this.#httpRequest)
2379
2401
  }, this.projects = new ObservableProjectsClient(this, this.#httpRequest), this.users = new ObservableUsersClient(this, this.#httpRequest), this.agent = {
2380
2402
  action: new ObservableAgentsActionClient(this, this.#httpRequest)
@@ -2384,7 +2406,7 @@ class ObservableSanityClient {
2384
2406
  * Clone the client - returns a new instance
2385
2407
  */
2386
2408
  clone() {
2387
- return new ObservableSanityClient(this.#httpRequest, this.config());
2409
+ return new ObservableSanityClient(this.#originalHttpRequest, this.config());
2388
2410
  }
2389
2411
  config(newConfig) {
2390
2412
  if (newConfig === void 0)
@@ -2402,7 +2424,7 @@ class ObservableSanityClient {
2402
2424
  */
2403
2425
  withConfig(newConfig) {
2404
2426
  const thisConfig = this.config();
2405
- return new ObservableSanityClient(this.#httpRequest, {
2427
+ return new ObservableSanityClient(this.#originalHttpRequest, {
2406
2428
  ...thisConfig,
2407
2429
  ...newConfig,
2408
2430
  stega: {
@@ -2632,13 +2654,22 @@ class SanityClient {
2632
2654
  * Private properties
2633
2655
  */
2634
2656
  #clientConfig;
2657
+ #originalHttpRequest;
2635
2658
  #httpRequest;
2636
2659
  /**
2637
2660
  * Instance properties
2638
2661
  */
2639
2662
  listen = _listen;
2640
2663
  constructor(httpRequest, config = defaultConfig) {
2641
- this.config(config), this.#httpRequest = httpRequest, this.assets = new AssetsClient(this, this.#httpRequest), this.datasets = new DatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.mediaLibrary = {
2664
+ this.config(config), this.#originalHttpRequest = httpRequest;
2665
+ const requestHandler = config._requestHandler;
2666
+ this.#httpRequest = requestHandler ? /* @__PURE__ */ (() => {
2667
+ let bareClient;
2668
+ return (options, requester2) => {
2669
+ const opts = options;
2670
+ return bareClient || (bareClient = new SanityClient(httpRequest, { ...config, _requestHandler: void 0 })), requestHandler(opts, (o) => httpRequest(o, requester2), bareClient);
2671
+ };
2672
+ })() : httpRequest, this.assets = new AssetsClient(this, this.#httpRequest), this.datasets = new DatasetsClient(this, this.#httpRequest), this.live = new LiveClient(this), this.mediaLibrary = {
2642
2673
  video: new MediaLibraryVideoClient(this, this.#httpRequest)
2643
2674
  }, this.projects = new ProjectsClient(this, this.#httpRequest), this.users = new UsersClient(this, this.#httpRequest), this.agent = {
2644
2675
  action: new AgentActionsClient(this, this.#httpRequest)
@@ -2648,7 +2679,7 @@ class SanityClient {
2648
2679
  * Clone the client - returns a new instance
2649
2680
  */
2650
2681
  clone() {
2651
- return new SanityClient(this.#httpRequest, this.config());
2682
+ return new SanityClient(this.#originalHttpRequest, this.config());
2652
2683
  }
2653
2684
  config(newConfig) {
2654
2685
  if (newConfig === void 0)
@@ -2666,7 +2697,7 @@ class SanityClient {
2666
2697
  */
2667
2698
  withConfig(newConfig) {
2668
2699
  const thisConfig = this.config();
2669
- return new SanityClient(this.#httpRequest, {
2700
+ return new SanityClient(this.#originalHttpRequest, {
2670
2701
  ...thisConfig,
2671
2702
  ...newConfig,
2672
2703
  stega: {