@geekmidas/client 10.0.0-alpha.80 → 10.0.0-alpha.82

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/dist/fetcher.cjs CHANGED
@@ -22,6 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  }) : target, mod));
23
23
  //#endregion
24
24
  const require_methods = require("./methods.cjs");
25
+ const require_telemetry = require("./telemetry.cjs");
25
26
  let qs = require("qs");
26
27
  qs = __toESM(qs, 1);
27
28
  //#region src/fetcher.ts
@@ -30,6 +31,7 @@ var TypedFetcher = class TypedFetcher {
30
31
  defaultHeaders;
31
32
  options;
32
33
  fetchFn;
34
+ injectTraceContext;
33
35
  static getFetchFn(fn) {
34
36
  if (fn) return fn;
35
37
  if (typeof window !== "undefined" && typeof window.fetch === "function") return window.fetch.bind(window);
@@ -41,6 +43,7 @@ var TypedFetcher = class TypedFetcher {
41
43
  this.defaultHeaders = options.headers || {};
42
44
  this.options = options;
43
45
  this.fetchFn = TypedFetcher.getFetchFn(options.fetch);
46
+ this.injectTraceContext = require_telemetry.traceContextInjector(this.baseURL, options.telemetry);
44
47
  }
45
48
  async request(endpoint, config) {
46
49
  const { method, route } = this.parseEndpoint(endpoint);
@@ -70,6 +73,7 @@ var TypedFetcher = class TypedFetcher {
70
73
  "Content-Type": "application/json"
71
74
  };
72
75
  }
76
+ this.injectTraceContext?.(`${this.baseURL}${url}`, requestConfig.headers);
73
77
  if (this.options.onRequest) requestConfig = await this.options.onRequest(requestConfig);
74
78
  try {
75
79
  let response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.cjs","names":["methodCalls"],"sources":["../src/fetcher.ts"],"sourcesContent":["import qs from 'qs';\nimport { methodCalls } from './methods';\nimport type {\n\tEndpointString,\n\tErrorTransformer,\n\tExtractEndpointResponse,\n\tFetcherOptions,\n\tFilteredRequestConfig,\n\tParseEndpoint,\n\tTypedApiFunction,\n\tTypedEndpoint,\n\tWrappedResult,\n} from './types';\n\nexport type { FetcherOptions, WrappedResult } from './types';\n\nexport class TypedFetcher<Paths> {\n\tprivate baseURL: string;\n\tprivate defaultHeaders: Record<string, string>;\n\tprivate options: FetcherOptions;\n\tprivate fetchFn: FetchFn;\n\n\tstatic getFetchFn(fn?: FetchFn): FetchFn {\n\t\tif (fn) {\n\t\t\treturn fn;\n\t\t}\n\n\t\tif (typeof window !== 'undefined' && typeof window.fetch === 'function') {\n\t\t\treturn window.fetch.bind(window);\n\t\t}\n\n\t\tif (\n\t\t\ttypeof globalThis !== 'undefined' &&\n\t\t\ttypeof globalThis.fetch === 'function'\n\t\t) {\n\t\t\treturn globalThis.fetch.bind(globalThis);\n\t\t}\n\n\t\tthrow new Error('No fetch implementation found');\n\t}\n\n\tconstructor(options: FetcherOptions = {}) {\n\t\tthis.baseURL = options.baseURL || '';\n\t\tthis.defaultHeaders = options.headers || {};\n\t\tthis.options = options;\n\t\tthis.fetchFn = TypedFetcher.getFetchFn(options.fetch);\n\t}\n\n\tasync request<T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t): Promise<ExtractEndpointResponse<Paths, T>> {\n\t\tconst { method, route } = this.parseEndpoint(endpoint);\n\n\t\t// Replace path parameters\n\t\tlet url = route;\n\t\tif (config && 'params' in config && config.params) {\n\t\t\tObject.entries(config.params as Record<string, unknown>).forEach(\n\t\t\t\t([key, value]) => {\n\t\t\t\t\turl = url.replace(`{${key}}`, encodeURIComponent(String(value)));\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Add query parameters\n\t\tif (config && 'query' in config && config.query) {\n\t\t\tconst queryString = qs.stringify(config.query, {\n\t\t\t\tencode: true,\n\t\t\t\tarrayFormat: 'brackets',\n\t\t\t\tskipNulls: true,\n\t\t\t});\n\t\t\tif (queryString) {\n\t\t\t\turl += `?${queryString}`;\n\t\t\t}\n\t\t}\n\n\t\t// Build request configuration\n\t\tlet requestConfig: RequestInit = {\n\t\t\tmethod: method.toUpperCase(),\n\t\t\theaders: {\n\t\t\t\t...this.defaultHeaders,\n\t\t\t\t...((config && 'headers' in config && config.headers) || {}),\n\t\t\t},\n\t\t};\n\n\t\t// Add body if present\n\t\tif (config && 'body' in config && config.body) {\n\t\t\trequestConfig.body = JSON.stringify(config.body);\n\t\t\trequestConfig.headers = {\n\t\t\t\t...requestConfig.headers,\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t};\n\t\t}\n\n\t\t// Apply request interceptor\n\t\tif (this.options.onRequest) {\n\t\t\trequestConfig = await this.options.onRequest(requestConfig);\n\t\t}\n\n\t\ttry {\n\t\t\t// Make the request\n\t\t\tlet response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);\n\n\t\t\t// Apply response interceptor\n\t\t\tif (this.options.onResponse) {\n\t\t\t\tresponse = await this.options.onResponse(response);\n\t\t\t}\n\n\t\t\t// Handle errors\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow response;\n\t\t\t}\n\n\t\t\t// Handle empty responses (204 No Content, etc.)\n\t\t\tif (\n\t\t\t\tresponse.status === 204 ||\n\t\t\t\tresponse.headers.get('content-length') === '0'\n\t\t\t) {\n\t\t\t\treturn undefined as ExtractEndpointResponse<Paths, T>;\n\t\t\t}\n\n\t\t\t// Parse JSON response\n\t\t\tconst data = await response.json();\n\t\t\treturn data as ExtractEndpointResponse<Paths, T>;\n\t\t} catch (error) {\n\t\t\t// Apply error handler\n\t\t\tif (this.options.onError) {\n\t\t\t\t// @ts-expect-error\n\t\t\t\tawait this.options.onError(error);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\twrap<E = unknown>(onError?: ErrorTransformer<E>) {\n\t\treturn <T extends TypedEndpoint<Paths>>(\n\t\t\tendpoint: T,\n\t\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t\t): Promise<WrappedResult<ExtractEndpointResponse<Paths, T>, E>> =>\n\t\t\tthis.request(endpoint, config).then(\n\t\t\t\t(data) => ({ ok: true as const, data }),\n\t\t\t\tasync (error) => ({\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: onError ? await onError(error) : (error as E),\n\t\t\t\t}),\n\t\t\t);\n\t}\n\n\tprivate parseEndpoint<T extends EndpointString>(\n\t\tendpoint: T,\n\t): ParseEndpoint<T> {\n\t\tconst [method, ...routeParts] = endpoint.split(' ');\n\t\tconst route = routeParts.join(' ');\n\t\treturn { method: method?.toLowerCase() ?? '', route } as ParseEndpoint<T>;\n\t}\n}\n\nexport function createTypedFetcher<Paths>(options?: FetcherOptions) {\n\tconst fetcher = new TypedFetcher<Paths>(options);\n\tconst fn = <T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t) => fetcher.request(endpoint, config);\n\n\treturn Object.assign(fn, methodCalls<Paths>(fn as TypedApiFunction<Paths>), {\n\t\twrap: <E = unknown>(onError?: ErrorTransformer<E>) =>\n\t\t\tObject.assign(fetcher.wrap(onError), fn, {\n\t\t\t\twrap: undefined,\n\t\t\t}),\n\t});\n}\n\nexport type FetchFn = typeof fetch;\n"],"mappings":"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"}
1
+ {"version":3,"file":"fetcher.cjs","names":["traceContextInjector","methodCalls"],"sources":["../src/fetcher.ts"],"sourcesContent":["import qs from 'qs';\nimport { methodCalls } from './methods';\nimport { type TraceContextInjector, traceContextInjector } from './telemetry';\nimport type {\n\tEndpointString,\n\tErrorTransformer,\n\tExtractEndpointResponse,\n\tFetcherOptions,\n\tFilteredRequestConfig,\n\tParseEndpoint,\n\tTypedApiFunction,\n\tTypedEndpoint,\n\tWrappedResult,\n} from './types';\n\nexport type { FetcherOptions, WrappedResult } from './types';\n\nexport class TypedFetcher<Paths> {\n\tprivate baseURL: string;\n\tprivate defaultHeaders: Record<string, string>;\n\tprivate options: FetcherOptions;\n\tprivate fetchFn: FetchFn;\n\tprivate injectTraceContext: TraceContextInjector | undefined;\n\n\tstatic getFetchFn(fn?: FetchFn): FetchFn {\n\t\tif (fn) {\n\t\t\treturn fn;\n\t\t}\n\n\t\tif (typeof window !== 'undefined' && typeof window.fetch === 'function') {\n\t\t\treturn window.fetch.bind(window);\n\t\t}\n\n\t\tif (\n\t\t\ttypeof globalThis !== 'undefined' &&\n\t\t\ttypeof globalThis.fetch === 'function'\n\t\t) {\n\t\t\treturn globalThis.fetch.bind(globalThis);\n\t\t}\n\n\t\tthrow new Error('No fetch implementation found');\n\t}\n\n\tconstructor(options: FetcherOptions = {}) {\n\t\tthis.baseURL = options.baseURL || '';\n\t\tthis.defaultHeaders = options.headers || {};\n\t\tthis.options = options;\n\t\tthis.fetchFn = TypedFetcher.getFetchFn(options.fetch);\n\t\tthis.injectTraceContext = traceContextInjector(\n\t\t\tthis.baseURL,\n\t\t\toptions.telemetry,\n\t\t);\n\t}\n\n\tasync request<T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t): Promise<ExtractEndpointResponse<Paths, T>> {\n\t\tconst { method, route } = this.parseEndpoint(endpoint);\n\n\t\t// Replace path parameters\n\t\tlet url = route;\n\t\tif (config && 'params' in config && config.params) {\n\t\t\tObject.entries(config.params as Record<string, unknown>).forEach(\n\t\t\t\t([key, value]) => {\n\t\t\t\t\turl = url.replace(`{${key}}`, encodeURIComponent(String(value)));\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Add query parameters\n\t\tif (config && 'query' in config && config.query) {\n\t\t\tconst queryString = qs.stringify(config.query, {\n\t\t\t\tencode: true,\n\t\t\t\tarrayFormat: 'brackets',\n\t\t\t\tskipNulls: true,\n\t\t\t});\n\t\t\tif (queryString) {\n\t\t\t\turl += `?${queryString}`;\n\t\t\t}\n\t\t}\n\n\t\t// Build request configuration\n\t\tlet requestConfig: RequestInit = {\n\t\t\tmethod: method.toUpperCase(),\n\t\t\theaders: {\n\t\t\t\t...this.defaultHeaders,\n\t\t\t\t...((config && 'headers' in config && config.headers) || {}),\n\t\t\t},\n\t\t};\n\n\t\t// Add body if present\n\t\tif (config && 'body' in config && config.body) {\n\t\t\trequestConfig.body = JSON.stringify(config.body);\n\t\t\trequestConfig.headers = {\n\t\t\t\t...requestConfig.headers,\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t};\n\t\t}\n\n\t\t// Trace context, before the interceptor so it sees the final headers\n\t\tthis.injectTraceContext?.(\n\t\t\t`${this.baseURL}${url}`,\n\t\t\trequestConfig.headers as Record<string, string>,\n\t\t);\n\n\t\t// Apply request interceptor\n\t\tif (this.options.onRequest) {\n\t\t\trequestConfig = await this.options.onRequest(requestConfig);\n\t\t}\n\n\t\ttry {\n\t\t\t// Make the request\n\t\t\tlet response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);\n\n\t\t\t// Apply response interceptor\n\t\t\tif (this.options.onResponse) {\n\t\t\t\tresponse = await this.options.onResponse(response);\n\t\t\t}\n\n\t\t\t// Handle errors\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow response;\n\t\t\t}\n\n\t\t\t// Handle empty responses (204 No Content, etc.)\n\t\t\tif (\n\t\t\t\tresponse.status === 204 ||\n\t\t\t\tresponse.headers.get('content-length') === '0'\n\t\t\t) {\n\t\t\t\treturn undefined as ExtractEndpointResponse<Paths, T>;\n\t\t\t}\n\n\t\t\t// Parse JSON response\n\t\t\tconst data = await response.json();\n\t\t\treturn data as ExtractEndpointResponse<Paths, T>;\n\t\t} catch (error) {\n\t\t\t// Apply error handler\n\t\t\tif (this.options.onError) {\n\t\t\t\t// @ts-expect-error\n\t\t\t\tawait this.options.onError(error);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\twrap<E = unknown>(onError?: ErrorTransformer<E>) {\n\t\treturn <T extends TypedEndpoint<Paths>>(\n\t\t\tendpoint: T,\n\t\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t\t): Promise<WrappedResult<ExtractEndpointResponse<Paths, T>, E>> =>\n\t\t\tthis.request(endpoint, config).then(\n\t\t\t\t(data) => ({ ok: true as const, data }),\n\t\t\t\tasync (error) => ({\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: onError ? await onError(error) : (error as E),\n\t\t\t\t}),\n\t\t\t);\n\t}\n\n\tprivate parseEndpoint<T extends EndpointString>(\n\t\tendpoint: T,\n\t): ParseEndpoint<T> {\n\t\tconst [method, ...routeParts] = endpoint.split(' ');\n\t\tconst route = routeParts.join(' ');\n\t\treturn { method: method?.toLowerCase() ?? '', route } as ParseEndpoint<T>;\n\t}\n}\n\nexport function createTypedFetcher<Paths>(options?: FetcherOptions) {\n\tconst fetcher = new TypedFetcher<Paths>(options);\n\tconst fn = <T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t) => fetcher.request(endpoint, config);\n\n\treturn Object.assign(fn, methodCalls<Paths>(fn as TypedApiFunction<Paths>), {\n\t\twrap: <E = unknown>(onError?: ErrorTransformer<E>) =>\n\t\t\tObject.assign(fetcher.wrap(onError), fn, {\n\t\t\t\twrap: undefined,\n\t\t\t}),\n\t});\n}\n\nexport type FetchFn = typeof fetch;\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ export declare class TypedFetcher<Paths> {
6
6
  private defaultHeaders;
7
7
  private options;
8
8
  private fetchFn;
9
+ private injectTraceContext;
9
10
  static getFetchFn(fn?: FetchFn): FetchFn;
10
11
  constructor(options?: FetcherOptions);
11
12
  request<T extends TypedEndpoint<Paths>>(endpoint: T, config?: FilteredRequestConfig<Paths, T>): Promise<ExtractEndpointResponse<Paths, T>>;
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.d.cts","names":["_$__methods0.MethodCalls"],"sources":["../src/fetcher.ts"],"mappings":";;;qBAgBa,aAAa;UACjB;UACA;UACA;UACA;SAED,WAAW,KAAK,UAAU;EAmBjC,YAAY,UAAS;EAOf,QAAQ,UAAU,cAAc,QACrC,UAAU,GACV,SAAS,sBAAsB,OAAO,KACpC,QAAQ,wBAAwB,OAAO;EAmF1C,KAAK,aAAa,UAAU,iBAAiB,MACpC,UAAU,cAAc,QAAM,UAC3B,GAAC,SACF,sBAAsB,OAAO,OACpC,QAAQ,cAAc,wBAAwB,OAAO,IAAI;UAUrD;;wBASO,mBAAmB,OAAO,UAAU,mBAEvC,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA,OAAAA,YAAA;EAIjC,OAAA,aAAW,UAAY,iBAAiB,SA9BvC,UAAU,cAAa,QAAO,UAAA,GAAA,SAAA,sBAAA,OAAA,mBAAA,QAAA,cAAA,wBAAA,OAAA,IAAA,SAwB3B,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA;;;;YAW9B,iBAAiB"}
1
+ {"version":3,"file":"fetcher.d.cts","names":["_$__methods0.MethodCalls"],"sources":["../src/fetcher.ts"],"mappings":";;;qBAiBa,aAAa;UACjB;UACA;UACA;UACA;UACA;SAED,WAAW,KAAK,UAAU;EAmBjC,YAAY,UAAS;EAWf,QAAQ,UAAU,cAAc,QACrC,UAAU,GACV,SAAS,sBAAsB,OAAO,KACpC,QAAQ,wBAAwB,OAAO;EAyF1C,KAAK,aAAa,UAAU,iBAAiB,MACpC,UAAU,cAAc,QAAM,UAC3B,GAAC,SACF,sBAAsB,OAAO,OACpC,QAAQ,cAAc,wBAAwB,OAAO,IAAI;UAUrD;;wBASO,mBAAmB,OAAO,UAAU,mBAEvC,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA,OAAAA,YAAA;EAIjC,OAAA,aAAW,UAAY,iBAAiB,SA9BvC,UAAU,cAAa,QAAO,UAAA,GAAA,SAAA,sBAAA,OAAA,mBAAA,QAAA,cAAA,wBAAA,OAAA,IAAA,SAwB3B,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA;;;;YAW9B,iBAAiB"}
@@ -6,6 +6,7 @@ export declare class TypedFetcher<Paths> {
6
6
  private defaultHeaders;
7
7
  private options;
8
8
  private fetchFn;
9
+ private injectTraceContext;
9
10
  static getFetchFn(fn?: FetchFn): FetchFn;
10
11
  constructor(options?: FetcherOptions);
11
12
  request<T extends TypedEndpoint<Paths>>(endpoint: T, config?: FilteredRequestConfig<Paths, T>): Promise<ExtractEndpointResponse<Paths, T>>;
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.d.mts","names":["_$__methods0.MethodCalls"],"sources":["../src/fetcher.ts"],"mappings":";;;qBAgBa,aAAa;UACjB;UACA;UACA;UACA;SAED,WAAW,KAAK,UAAU;EAmBjC,YAAY,UAAS;EAOf,QAAQ,UAAU,cAAc,QACrC,UAAU,GACV,SAAS,sBAAsB,OAAO,KACpC,QAAQ,wBAAwB,OAAO;EAmF1C,KAAK,aAAa,UAAU,iBAAiB,MACpC,UAAU,cAAc,QAAM,UAC3B,GAAC,SACF,sBAAsB,OAAO,OACpC,QAAQ,cAAc,wBAAwB,OAAO,IAAI;UAUrD;;wBASO,mBAAmB,OAAO,UAAU,mBAEvC,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA,OAAAA,YAAA;EAIjC,OAAA,aAAW,UAAY,iBAAiB,SA9BvC,UAAU,cAAa,QAAO,UAAA,GAAA,SAAA,sBAAA,OAAA,mBAAA,QAAA,cAAA,wBAAA,OAAA,IAAA,SAwB3B,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA;;;;YAW9B,iBAAiB"}
1
+ {"version":3,"file":"fetcher.d.mts","names":["_$__methods0.MethodCalls"],"sources":["../src/fetcher.ts"],"mappings":";;;qBAiBa,aAAa;UACjB;UACA;UACA;UACA;UACA;SAED,WAAW,KAAK,UAAU;EAmBjC,YAAY,UAAS;EAWf,QAAQ,UAAU,cAAc,QACrC,UAAU,GACV,SAAS,sBAAsB,OAAO,KACpC,QAAQ,wBAAwB,OAAO;EAyF1C,KAAK,aAAa,UAAU,iBAAiB,MACpC,UAAU,cAAc,QAAM,UAC3B,GAAC,SACF,sBAAsB,OAAO,OACpC,QAAQ,cAAc,wBAAwB,OAAO,IAAI;UAUrD;;wBASO,mBAAmB,OAAO,UAAU,mBAEvC,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA,OAAAA,YAAA;EAIjC,OAAA,aAAW,UAAY,iBAAiB,SA9BvC,UAAU,cAAa,QAAO,UAAA,GAAA,SAAA,sBAAA,OAAA,mBAAA,QAAA,cAAA,wBAAA,OAAA,IAAA,SAwB3B,UAAU,cAAc,QAAM,UAC/B,GAAC,SACF,sBAAsB,OAAO,OAAE,QAAA,wBAAA,OAAA;;;;YAW9B,iBAAiB"}
package/dist/fetcher.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { methodCalls } from "./methods.mjs";
2
+ import { traceContextInjector } from "./telemetry.mjs";
2
3
  import qs from "qs";
3
4
  //#region src/fetcher.ts
4
5
  var TypedFetcher = class TypedFetcher {
@@ -6,6 +7,7 @@ var TypedFetcher = class TypedFetcher {
6
7
  defaultHeaders;
7
8
  options;
8
9
  fetchFn;
10
+ injectTraceContext;
9
11
  static getFetchFn(fn) {
10
12
  if (fn) return fn;
11
13
  if (typeof window !== "undefined" && typeof window.fetch === "function") return window.fetch.bind(window);
@@ -17,6 +19,7 @@ var TypedFetcher = class TypedFetcher {
17
19
  this.defaultHeaders = options.headers || {};
18
20
  this.options = options;
19
21
  this.fetchFn = TypedFetcher.getFetchFn(options.fetch);
22
+ this.injectTraceContext = traceContextInjector(this.baseURL, options.telemetry);
20
23
  }
21
24
  async request(endpoint, config) {
22
25
  const { method, route } = this.parseEndpoint(endpoint);
@@ -46,6 +49,7 @@ var TypedFetcher = class TypedFetcher {
46
49
  "Content-Type": "application/json"
47
50
  };
48
51
  }
52
+ this.injectTraceContext?.(`${this.baseURL}${url}`, requestConfig.headers);
49
53
  if (this.options.onRequest) requestConfig = await this.options.onRequest(requestConfig);
50
54
  try {
51
55
  let response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.mjs","names":[],"sources":["../src/fetcher.ts"],"sourcesContent":["import qs from 'qs';\nimport { methodCalls } from './methods';\nimport type {\n\tEndpointString,\n\tErrorTransformer,\n\tExtractEndpointResponse,\n\tFetcherOptions,\n\tFilteredRequestConfig,\n\tParseEndpoint,\n\tTypedApiFunction,\n\tTypedEndpoint,\n\tWrappedResult,\n} from './types';\n\nexport type { FetcherOptions, WrappedResult } from './types';\n\nexport class TypedFetcher<Paths> {\n\tprivate baseURL: string;\n\tprivate defaultHeaders: Record<string, string>;\n\tprivate options: FetcherOptions;\n\tprivate fetchFn: FetchFn;\n\n\tstatic getFetchFn(fn?: FetchFn): FetchFn {\n\t\tif (fn) {\n\t\t\treturn fn;\n\t\t}\n\n\t\tif (typeof window !== 'undefined' && typeof window.fetch === 'function') {\n\t\t\treturn window.fetch.bind(window);\n\t\t}\n\n\t\tif (\n\t\t\ttypeof globalThis !== 'undefined' &&\n\t\t\ttypeof globalThis.fetch === 'function'\n\t\t) {\n\t\t\treturn globalThis.fetch.bind(globalThis);\n\t\t}\n\n\t\tthrow new Error('No fetch implementation found');\n\t}\n\n\tconstructor(options: FetcherOptions = {}) {\n\t\tthis.baseURL = options.baseURL || '';\n\t\tthis.defaultHeaders = options.headers || {};\n\t\tthis.options = options;\n\t\tthis.fetchFn = TypedFetcher.getFetchFn(options.fetch);\n\t}\n\n\tasync request<T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t): Promise<ExtractEndpointResponse<Paths, T>> {\n\t\tconst { method, route } = this.parseEndpoint(endpoint);\n\n\t\t// Replace path parameters\n\t\tlet url = route;\n\t\tif (config && 'params' in config && config.params) {\n\t\t\tObject.entries(config.params as Record<string, unknown>).forEach(\n\t\t\t\t([key, value]) => {\n\t\t\t\t\turl = url.replace(`{${key}}`, encodeURIComponent(String(value)));\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Add query parameters\n\t\tif (config && 'query' in config && config.query) {\n\t\t\tconst queryString = qs.stringify(config.query, {\n\t\t\t\tencode: true,\n\t\t\t\tarrayFormat: 'brackets',\n\t\t\t\tskipNulls: true,\n\t\t\t});\n\t\t\tif (queryString) {\n\t\t\t\turl += `?${queryString}`;\n\t\t\t}\n\t\t}\n\n\t\t// Build request configuration\n\t\tlet requestConfig: RequestInit = {\n\t\t\tmethod: method.toUpperCase(),\n\t\t\theaders: {\n\t\t\t\t...this.defaultHeaders,\n\t\t\t\t...((config && 'headers' in config && config.headers) || {}),\n\t\t\t},\n\t\t};\n\n\t\t// Add body if present\n\t\tif (config && 'body' in config && config.body) {\n\t\t\trequestConfig.body = JSON.stringify(config.body);\n\t\t\trequestConfig.headers = {\n\t\t\t\t...requestConfig.headers,\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t};\n\t\t}\n\n\t\t// Apply request interceptor\n\t\tif (this.options.onRequest) {\n\t\t\trequestConfig = await this.options.onRequest(requestConfig);\n\t\t}\n\n\t\ttry {\n\t\t\t// Make the request\n\t\t\tlet response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);\n\n\t\t\t// Apply response interceptor\n\t\t\tif (this.options.onResponse) {\n\t\t\t\tresponse = await this.options.onResponse(response);\n\t\t\t}\n\n\t\t\t// Handle errors\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow response;\n\t\t\t}\n\n\t\t\t// Handle empty responses (204 No Content, etc.)\n\t\t\tif (\n\t\t\t\tresponse.status === 204 ||\n\t\t\t\tresponse.headers.get('content-length') === '0'\n\t\t\t) {\n\t\t\t\treturn undefined as ExtractEndpointResponse<Paths, T>;\n\t\t\t}\n\n\t\t\t// Parse JSON response\n\t\t\tconst data = await response.json();\n\t\t\treturn data as ExtractEndpointResponse<Paths, T>;\n\t\t} catch (error) {\n\t\t\t// Apply error handler\n\t\t\tif (this.options.onError) {\n\t\t\t\t// @ts-expect-error\n\t\t\t\tawait this.options.onError(error);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\twrap<E = unknown>(onError?: ErrorTransformer<E>) {\n\t\treturn <T extends TypedEndpoint<Paths>>(\n\t\t\tendpoint: T,\n\t\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t\t): Promise<WrappedResult<ExtractEndpointResponse<Paths, T>, E>> =>\n\t\t\tthis.request(endpoint, config).then(\n\t\t\t\t(data) => ({ ok: true as const, data }),\n\t\t\t\tasync (error) => ({\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: onError ? await onError(error) : (error as E),\n\t\t\t\t}),\n\t\t\t);\n\t}\n\n\tprivate parseEndpoint<T extends EndpointString>(\n\t\tendpoint: T,\n\t): ParseEndpoint<T> {\n\t\tconst [method, ...routeParts] = endpoint.split(' ');\n\t\tconst route = routeParts.join(' ');\n\t\treturn { method: method?.toLowerCase() ?? '', route } as ParseEndpoint<T>;\n\t}\n}\n\nexport function createTypedFetcher<Paths>(options?: FetcherOptions) {\n\tconst fetcher = new TypedFetcher<Paths>(options);\n\tconst fn = <T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t) => fetcher.request(endpoint, config);\n\n\treturn Object.assign(fn, methodCalls<Paths>(fn as TypedApiFunction<Paths>), {\n\t\twrap: <E = unknown>(onError?: ErrorTransformer<E>) =>\n\t\t\tObject.assign(fetcher.wrap(onError), fn, {\n\t\t\t\twrap: undefined,\n\t\t\t}),\n\t});\n}\n\nexport type FetchFn = typeof fetch;\n"],"mappings":";;;AAgBA,IAAa,eAAb,MAAa,aAAoB;CAChC;CACA;CACA;CACA;CAEA,OAAO,WAAW,IAAuB;EACxC,IAAI,IACH,OAAO;EAGR,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,UAAU,YAC5D,OAAO,OAAO,MAAM,KAAK,MAAM;EAGhC,IACC,OAAO,eAAe,eACtB,OAAO,WAAW,UAAU,YAE5B,OAAO,WAAW,MAAM,KAAK,UAAU;EAGxC,MAAM,IAAI,MAAM,+BAA+B;CAChD;CAEA,YAAY,UAA0B,CAAC,GAAG;EACzC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,iBAAiB,QAAQ,WAAW,CAAC;EAC1C,KAAK,UAAU;EACf,KAAK,UAAU,aAAa,WAAW,QAAQ,KAAK;CACrD;CAEA,MAAM,QACL,UACA,QAC6C;EAC7C,MAAM,EAAE,QAAQ,UAAU,KAAK,cAAc,QAAQ;EAGrD,IAAI,MAAM;EACV,IAAI,UAAU,YAAY,UAAU,OAAO,QAC1C,OAAO,QAAQ,OAAO,MAAiC,CAAC,CAAC,SACvD,CAAC,KAAK,WAAW;GACjB,MAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,mBAAmB,OAAO,KAAK,CAAC,CAAC;EAChE,CACD;EAID,IAAI,UAAU,WAAW,UAAU,OAAO,OAAO;GAChD,MAAM,cAAc,GAAG,UAAU,OAAO,OAAO;IAC9C,QAAQ;IACR,aAAa;IACb,WAAW;GACZ,CAAC;GACD,IAAI,aACH,OAAO,IAAI;EAEb;EAGA,IAAI,gBAA6B;GAChC,QAAQ,OAAO,YAAY;GAC3B,SAAS;IACR,GAAG,KAAK;IACR,GAAK,UAAU,aAAa,UAAU,OAAO,WAAY,CAAC;GAC3D;EACD;EAGA,IAAI,UAAU,UAAU,UAAU,OAAO,MAAM;GAC9C,cAAc,OAAO,KAAK,UAAU,OAAO,IAAI;GAC/C,cAAc,UAAU;IACvB,GAAG,cAAc;IACjB,gBAAgB;GACjB;EACD;EAGA,IAAI,KAAK,QAAQ,WAChB,gBAAgB,MAAM,KAAK,QAAQ,UAAU,aAAa;EAG3D,IAAI;GAEH,IAAI,WAAW,MAAM,KAAK,QAAQ,GAAG,KAAK,UAAU,OAAO,aAAa;GAGxE,IAAI,KAAK,QAAQ,YAChB,WAAW,MAAM,KAAK,QAAQ,WAAW,QAAQ;GAIlD,IAAI,CAAC,SAAS,IACb,MAAM;GAIP,IACC,SAAS,WAAW,OACpB,SAAS,QAAQ,IAAI,gBAAgB,MAAM,KAE3C;GAKD,OAAO,MADY,SAAS,KAAK;EAElC,SAAS,OAAO;GAEf,IAAI,KAAK,QAAQ,SAEhB,MAAM,KAAK,QAAQ,QAAQ,KAAK;GAEjC,MAAM;EACP;CACD;CAEA,KAAkB,SAA+B;EAChD,QACC,UACA,WAEA,KAAK,QAAQ,UAAU,MAAM,CAAC,CAAC,MAC7B,UAAU;GAAE,IAAI;GAAe;EAAK,IACrC,OAAO,WAAW;GACjB,IAAI;GACJ,OAAO,UAAU,MAAM,QAAQ,KAAK,IAAK;EAC1C,EACD;CACF;CAEA,cACC,UACmB;EACnB,MAAM,CAAC,QAAQ,GAAG,cAAc,SAAS,MAAM,GAAG;EAClD,MAAM,QAAQ,WAAW,KAAK,GAAG;EACjC,OAAO;GAAE,QAAQ,QAAQ,YAAY,KAAK;GAAI;EAAM;CACrD;AACD;AAEA,SAAgB,mBAA0B,SAA0B;CACnE,MAAM,UAAU,IAAI,aAAoB,OAAO;CAC/C,MAAM,MACL,UACA,WACI,QAAQ,QAAQ,UAAU,MAAM;CAErC,OAAO,OAAO,OAAO,IAAI,YAAmB,EAA6B,GAAG,EAC3E,OAAoB,YACnB,OAAO,OAAO,QAAQ,KAAK,OAAO,GAAG,IAAI,EACxC,MAAM,KAAA,EACP,CAAC,EACH,CAAC;AACF"}
1
+ {"version":3,"file":"fetcher.mjs","names":[],"sources":["../src/fetcher.ts"],"sourcesContent":["import qs from 'qs';\nimport { methodCalls } from './methods';\nimport { type TraceContextInjector, traceContextInjector } from './telemetry';\nimport type {\n\tEndpointString,\n\tErrorTransformer,\n\tExtractEndpointResponse,\n\tFetcherOptions,\n\tFilteredRequestConfig,\n\tParseEndpoint,\n\tTypedApiFunction,\n\tTypedEndpoint,\n\tWrappedResult,\n} from './types';\n\nexport type { FetcherOptions, WrappedResult } from './types';\n\nexport class TypedFetcher<Paths> {\n\tprivate baseURL: string;\n\tprivate defaultHeaders: Record<string, string>;\n\tprivate options: FetcherOptions;\n\tprivate fetchFn: FetchFn;\n\tprivate injectTraceContext: TraceContextInjector | undefined;\n\n\tstatic getFetchFn(fn?: FetchFn): FetchFn {\n\t\tif (fn) {\n\t\t\treturn fn;\n\t\t}\n\n\t\tif (typeof window !== 'undefined' && typeof window.fetch === 'function') {\n\t\t\treturn window.fetch.bind(window);\n\t\t}\n\n\t\tif (\n\t\t\ttypeof globalThis !== 'undefined' &&\n\t\t\ttypeof globalThis.fetch === 'function'\n\t\t) {\n\t\t\treturn globalThis.fetch.bind(globalThis);\n\t\t}\n\n\t\tthrow new Error('No fetch implementation found');\n\t}\n\n\tconstructor(options: FetcherOptions = {}) {\n\t\tthis.baseURL = options.baseURL || '';\n\t\tthis.defaultHeaders = options.headers || {};\n\t\tthis.options = options;\n\t\tthis.fetchFn = TypedFetcher.getFetchFn(options.fetch);\n\t\tthis.injectTraceContext = traceContextInjector(\n\t\t\tthis.baseURL,\n\t\t\toptions.telemetry,\n\t\t);\n\t}\n\n\tasync request<T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t): Promise<ExtractEndpointResponse<Paths, T>> {\n\t\tconst { method, route } = this.parseEndpoint(endpoint);\n\n\t\t// Replace path parameters\n\t\tlet url = route;\n\t\tif (config && 'params' in config && config.params) {\n\t\t\tObject.entries(config.params as Record<string, unknown>).forEach(\n\t\t\t\t([key, value]) => {\n\t\t\t\t\turl = url.replace(`{${key}}`, encodeURIComponent(String(value)));\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Add query parameters\n\t\tif (config && 'query' in config && config.query) {\n\t\t\tconst queryString = qs.stringify(config.query, {\n\t\t\t\tencode: true,\n\t\t\t\tarrayFormat: 'brackets',\n\t\t\t\tskipNulls: true,\n\t\t\t});\n\t\t\tif (queryString) {\n\t\t\t\turl += `?${queryString}`;\n\t\t\t}\n\t\t}\n\n\t\t// Build request configuration\n\t\tlet requestConfig: RequestInit = {\n\t\t\tmethod: method.toUpperCase(),\n\t\t\theaders: {\n\t\t\t\t...this.defaultHeaders,\n\t\t\t\t...((config && 'headers' in config && config.headers) || {}),\n\t\t\t},\n\t\t};\n\n\t\t// Add body if present\n\t\tif (config && 'body' in config && config.body) {\n\t\t\trequestConfig.body = JSON.stringify(config.body);\n\t\t\trequestConfig.headers = {\n\t\t\t\t...requestConfig.headers,\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t};\n\t\t}\n\n\t\t// Trace context, before the interceptor so it sees the final headers\n\t\tthis.injectTraceContext?.(\n\t\t\t`${this.baseURL}${url}`,\n\t\t\trequestConfig.headers as Record<string, string>,\n\t\t);\n\n\t\t// Apply request interceptor\n\t\tif (this.options.onRequest) {\n\t\t\trequestConfig = await this.options.onRequest(requestConfig);\n\t\t}\n\n\t\ttry {\n\t\t\t// Make the request\n\t\t\tlet response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);\n\n\t\t\t// Apply response interceptor\n\t\t\tif (this.options.onResponse) {\n\t\t\t\tresponse = await this.options.onResponse(response);\n\t\t\t}\n\n\t\t\t// Handle errors\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow response;\n\t\t\t}\n\n\t\t\t// Handle empty responses (204 No Content, etc.)\n\t\t\tif (\n\t\t\t\tresponse.status === 204 ||\n\t\t\t\tresponse.headers.get('content-length') === '0'\n\t\t\t) {\n\t\t\t\treturn undefined as ExtractEndpointResponse<Paths, T>;\n\t\t\t}\n\n\t\t\t// Parse JSON response\n\t\t\tconst data = await response.json();\n\t\t\treturn data as ExtractEndpointResponse<Paths, T>;\n\t\t} catch (error) {\n\t\t\t// Apply error handler\n\t\t\tif (this.options.onError) {\n\t\t\t\t// @ts-expect-error\n\t\t\t\tawait this.options.onError(error);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\twrap<E = unknown>(onError?: ErrorTransformer<E>) {\n\t\treturn <T extends TypedEndpoint<Paths>>(\n\t\t\tendpoint: T,\n\t\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t\t): Promise<WrappedResult<ExtractEndpointResponse<Paths, T>, E>> =>\n\t\t\tthis.request(endpoint, config).then(\n\t\t\t\t(data) => ({ ok: true as const, data }),\n\t\t\t\tasync (error) => ({\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: onError ? await onError(error) : (error as E),\n\t\t\t\t}),\n\t\t\t);\n\t}\n\n\tprivate parseEndpoint<T extends EndpointString>(\n\t\tendpoint: T,\n\t): ParseEndpoint<T> {\n\t\tconst [method, ...routeParts] = endpoint.split(' ');\n\t\tconst route = routeParts.join(' ');\n\t\treturn { method: method?.toLowerCase() ?? '', route } as ParseEndpoint<T>;\n\t}\n}\n\nexport function createTypedFetcher<Paths>(options?: FetcherOptions) {\n\tconst fetcher = new TypedFetcher<Paths>(options);\n\tconst fn = <T extends TypedEndpoint<Paths>>(\n\t\tendpoint: T,\n\t\tconfig?: FilteredRequestConfig<Paths, T>,\n\t) => fetcher.request(endpoint, config);\n\n\treturn Object.assign(fn, methodCalls<Paths>(fn as TypedApiFunction<Paths>), {\n\t\twrap: <E = unknown>(onError?: ErrorTransformer<E>) =>\n\t\t\tObject.assign(fetcher.wrap(onError), fn, {\n\t\t\t\twrap: undefined,\n\t\t\t}),\n\t});\n}\n\nexport type FetchFn = typeof fetch;\n"],"mappings":";;;;AAiBA,IAAa,eAAb,MAAa,aAAoB;CAChC;CACA;CACA;CACA;CACA;CAEA,OAAO,WAAW,IAAuB;EACxC,IAAI,IACH,OAAO;EAGR,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,UAAU,YAC5D,OAAO,OAAO,MAAM,KAAK,MAAM;EAGhC,IACC,OAAO,eAAe,eACtB,OAAO,WAAW,UAAU,YAE5B,OAAO,WAAW,MAAM,KAAK,UAAU;EAGxC,MAAM,IAAI,MAAM,+BAA+B;CAChD;CAEA,YAAY,UAA0B,CAAC,GAAG;EACzC,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,iBAAiB,QAAQ,WAAW,CAAC;EAC1C,KAAK,UAAU;EACf,KAAK,UAAU,aAAa,WAAW,QAAQ,KAAK;EACpD,KAAK,qBAAqB,qBACzB,KAAK,SACL,QAAQ,SACT;CACD;CAEA,MAAM,QACL,UACA,QAC6C;EAC7C,MAAM,EAAE,QAAQ,UAAU,KAAK,cAAc,QAAQ;EAGrD,IAAI,MAAM;EACV,IAAI,UAAU,YAAY,UAAU,OAAO,QAC1C,OAAO,QAAQ,OAAO,MAAiC,CAAC,CAAC,SACvD,CAAC,KAAK,WAAW;GACjB,MAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,mBAAmB,OAAO,KAAK,CAAC,CAAC;EAChE,CACD;EAID,IAAI,UAAU,WAAW,UAAU,OAAO,OAAO;GAChD,MAAM,cAAc,GAAG,UAAU,OAAO,OAAO;IAC9C,QAAQ;IACR,aAAa;IACb,WAAW;GACZ,CAAC;GACD,IAAI,aACH,OAAO,IAAI;EAEb;EAGA,IAAI,gBAA6B;GAChC,QAAQ,OAAO,YAAY;GAC3B,SAAS;IACR,GAAG,KAAK;IACR,GAAK,UAAU,aAAa,UAAU,OAAO,WAAY,CAAC;GAC3D;EACD;EAGA,IAAI,UAAU,UAAU,UAAU,OAAO,MAAM;GAC9C,cAAc,OAAO,KAAK,UAAU,OAAO,IAAI;GAC/C,cAAc,UAAU;IACvB,GAAG,cAAc;IACjB,gBAAgB;GACjB;EACD;EAGA,KAAK,qBACJ,GAAG,KAAK,UAAU,OAClB,cAAc,OACf;EAGA,IAAI,KAAK,QAAQ,WAChB,gBAAgB,MAAM,KAAK,QAAQ,UAAU,aAAa;EAG3D,IAAI;GAEH,IAAI,WAAW,MAAM,KAAK,QAAQ,GAAG,KAAK,UAAU,OAAO,aAAa;GAGxE,IAAI,KAAK,QAAQ,YAChB,WAAW,MAAM,KAAK,QAAQ,WAAW,QAAQ;GAIlD,IAAI,CAAC,SAAS,IACb,MAAM;GAIP,IACC,SAAS,WAAW,OACpB,SAAS,QAAQ,IAAI,gBAAgB,MAAM,KAE3C;GAKD,OAAO,MADY,SAAS,KAAK;EAElC,SAAS,OAAO;GAEf,IAAI,KAAK,QAAQ,SAEhB,MAAM,KAAK,QAAQ,QAAQ,KAAK;GAEjC,MAAM;EACP;CACD;CAEA,KAAkB,SAA+B;EAChD,QACC,UACA,WAEA,KAAK,QAAQ,UAAU,MAAM,CAAC,CAAC,MAC7B,UAAU;GAAE,IAAI;GAAe;EAAK,IACrC,OAAO,WAAW;GACjB,IAAI;GACJ,OAAO,UAAU,MAAM,QAAQ,KAAK,IAAK;EAC1C,EACD;CACF;CAEA,cACC,UACmB;EACnB,MAAM,CAAC,QAAQ,GAAG,cAAc,SAAS,MAAM,GAAG;EAClD,MAAM,QAAQ,WAAW,KAAK,GAAG;EACjC,OAAO;GAAE,QAAQ,QAAQ,YAAY,KAAK;GAAI;EAAM;CACrD;AACD;AAEA,SAAgB,mBAA0B,SAA0B;CACnE,MAAM,UAAU,IAAI,aAAoB,OAAO;CAC/C,MAAM,MACL,UACA,WACI,QAAQ,QAAQ,UAAU,MAAM;CAErC,OAAO,OAAO,OAAO,IAAI,YAAmB,EAA6B,GAAG,EAC3E,OAAoB,YACnB,OAAO,OAAO,QAAQ,KAAK,OAAO,GAAG,IAAI,EACxC,MAAM,KAAA,EACP,CAAC,EACH,CAAC;AACF"}
package/dist/index.cjs CHANGED
@@ -1,11 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_methods = require("./methods.cjs");
3
+ const require_telemetry = require("./telemetry.cjs");
3
4
  const require_fetcher = require("./fetcher.cjs");
4
5
  const require_auth_fetcher = require("./auth-fetcher.cjs");
5
6
  require("./infer.cjs");
6
7
  require("./types.cjs");
7
8
  exports.CALL_METHODS = require_methods.CALL_METHODS;
9
+ exports.InvalidClientSampleRate = require_telemetry.InvalidClientSampleRate;
8
10
  exports.TypedFetcher = require_fetcher.TypedFetcher;
9
11
  exports.createAuthAwareFetcher = require_auth_fetcher.createAuthAwareFetcher;
10
12
  exports.createTypedFetcher = require_fetcher.createTypedFetcher;
11
13
  exports.methodCalls = require_methods.methodCalls;
14
+ exports.sampledAt = require_telemetry.sampledAt;
15
+ exports.traceContextInjector = require_telemetry.traceContextInjector;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
+ import { a as traceContextInjector, i as sampledAt, n as InvalidClientSampleRate, r as TraceContextInjector, t as ClientTelemetryOptions } from "./telemetry-BbRAq81t.cjs";
1
2
  import { EndpointString, ErrorTransformer, ExtractEndpointConfig, ExtractEndpointResponse, ExtractMethod, ExtractPathParams, ExtractQueryParams, ExtractRequestBody, ExtractResponse, FetcherOptions, FilterEndpointByMethod, FilteredRequestConfig, IsConfigRequired, MutationEndpoint, OpenAPIRoutes, ParseEndpoint, QueryEndpoint, RequestConfig, TypedApiFunction, TypedEndpoint, ValidEndpoint, WrappedApiFunction, WrappedResult } from "./types.cjs";
2
3
  import { CALL_METHODS, CallMethod, MethodCall, MethodCalls, RoutesFor, methodCalls } from "./methods.cjs";
3
4
  import { ApiKeyProvider, AuthFetcherOptions, AuthStrategy, AuthenticatedEndpoints, AwsSigner, GetEndpointAuth, PublicEndpoints, SecuritySchemeObject, TokenProvider, UsedSecuritySchemes, createAuthAwareFetcher } from "./auth-fetcher.cjs";
4
5
  import { FetchFn, TypedFetcher, createTypedFetcher } from "./fetcher.cjs";
5
6
  import { InferOpenApi, InferOpenApiFromEndpoint } from "./infer.cjs";
6
- export { ApiKeyProvider, AuthFetcherOptions, AuthStrategy, AuthenticatedEndpoints, AwsSigner, CALL_METHODS, CallMethod, EndpointString, ErrorTransformer, ExtractEndpointConfig, ExtractEndpointResponse, ExtractMethod, ExtractPathParams, ExtractQueryParams, ExtractRequestBody, ExtractResponse, FetchFn, FetcherOptions, FilterEndpointByMethod, FilteredRequestConfig, GetEndpointAuth, InferOpenApi, InferOpenApiFromEndpoint, IsConfigRequired, MethodCall, MethodCalls, MutationEndpoint, OpenAPIRoutes, ParseEndpoint, PublicEndpoints, QueryEndpoint, RequestConfig, RoutesFor, SecuritySchemeObject, TokenProvider, TypedApiFunction, TypedEndpoint, TypedFetcher, UsedSecuritySchemes, ValidEndpoint, WrappedApiFunction, WrappedResult, createAuthAwareFetcher, createTypedFetcher, methodCalls };
7
+ export { ApiKeyProvider, AuthFetcherOptions, AuthStrategy, AuthenticatedEndpoints, AwsSigner, CALL_METHODS, CallMethod, ClientTelemetryOptions, EndpointString, ErrorTransformer, ExtractEndpointConfig, ExtractEndpointResponse, ExtractMethod, ExtractPathParams, ExtractQueryParams, ExtractRequestBody, ExtractResponse, FetchFn, FetcherOptions, FilterEndpointByMethod, FilteredRequestConfig, GetEndpointAuth, InferOpenApi, InferOpenApiFromEndpoint, InvalidClientSampleRate, IsConfigRequired, MethodCall, MethodCalls, MutationEndpoint, OpenAPIRoutes, ParseEndpoint, PublicEndpoints, QueryEndpoint, RequestConfig, RoutesFor, SecuritySchemeObject, TokenProvider, TraceContextInjector, TypedApiFunction, TypedEndpoint, TypedFetcher, UsedSecuritySchemes, ValidEndpoint, WrappedApiFunction, WrappedResult, createAuthAwareFetcher, createTypedFetcher, methodCalls, sampledAt, traceContextInjector };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
+ import { a as traceContextInjector, i as sampledAt, n as InvalidClientSampleRate, r as TraceContextInjector, t as ClientTelemetryOptions } from "./telemetry-BbRAq81t.mjs";
1
2
  import { EndpointString, ErrorTransformer, ExtractEndpointConfig, ExtractEndpointResponse, ExtractMethod, ExtractPathParams, ExtractQueryParams, ExtractRequestBody, ExtractResponse, FetcherOptions, FilterEndpointByMethod, FilteredRequestConfig, IsConfigRequired, MutationEndpoint, OpenAPIRoutes, ParseEndpoint, QueryEndpoint, RequestConfig, TypedApiFunction, TypedEndpoint, ValidEndpoint, WrappedApiFunction, WrappedResult } from "./types.mjs";
2
3
  import { CALL_METHODS, CallMethod, MethodCall, MethodCalls, RoutesFor, methodCalls } from "./methods.mjs";
3
4
  import { ApiKeyProvider, AuthFetcherOptions, AuthStrategy, AuthenticatedEndpoints, AwsSigner, GetEndpointAuth, PublicEndpoints, SecuritySchemeObject, TokenProvider, UsedSecuritySchemes, createAuthAwareFetcher } from "./auth-fetcher.mjs";
4
5
  import { FetchFn, TypedFetcher, createTypedFetcher } from "./fetcher.mjs";
5
6
  import { InferOpenApi, InferOpenApiFromEndpoint } from "./infer.mjs";
6
- export { ApiKeyProvider, AuthFetcherOptions, AuthStrategy, AuthenticatedEndpoints, AwsSigner, CALL_METHODS, CallMethod, EndpointString, ErrorTransformer, ExtractEndpointConfig, ExtractEndpointResponse, ExtractMethod, ExtractPathParams, ExtractQueryParams, ExtractRequestBody, ExtractResponse, FetchFn, FetcherOptions, FilterEndpointByMethod, FilteredRequestConfig, GetEndpointAuth, InferOpenApi, InferOpenApiFromEndpoint, IsConfigRequired, MethodCall, MethodCalls, MutationEndpoint, OpenAPIRoutes, ParseEndpoint, PublicEndpoints, QueryEndpoint, RequestConfig, RoutesFor, SecuritySchemeObject, TokenProvider, TypedApiFunction, TypedEndpoint, TypedFetcher, UsedSecuritySchemes, ValidEndpoint, WrappedApiFunction, WrappedResult, createAuthAwareFetcher, createTypedFetcher, methodCalls };
7
+ export { ApiKeyProvider, AuthFetcherOptions, AuthStrategy, AuthenticatedEndpoints, AwsSigner, CALL_METHODS, CallMethod, ClientTelemetryOptions, EndpointString, ErrorTransformer, ExtractEndpointConfig, ExtractEndpointResponse, ExtractMethod, ExtractPathParams, ExtractQueryParams, ExtractRequestBody, ExtractResponse, FetchFn, FetcherOptions, FilterEndpointByMethod, FilteredRequestConfig, GetEndpointAuth, InferOpenApi, InferOpenApiFromEndpoint, InvalidClientSampleRate, IsConfigRequired, MethodCall, MethodCalls, MutationEndpoint, OpenAPIRoutes, ParseEndpoint, PublicEndpoints, QueryEndpoint, RequestConfig, RoutesFor, SecuritySchemeObject, TokenProvider, TraceContextInjector, TypedApiFunction, TypedEndpoint, TypedFetcher, UsedSecuritySchemes, ValidEndpoint, WrappedApiFunction, WrappedResult, createAuthAwareFetcher, createTypedFetcher, methodCalls, sampledAt, traceContextInjector };
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { CALL_METHODS, methodCalls } from "./methods.mjs";
2
+ import { InvalidClientSampleRate, sampledAt, traceContextInjector } from "./telemetry.mjs";
2
3
  import { TypedFetcher, createTypedFetcher } from "./fetcher.mjs";
3
4
  import { createAuthAwareFetcher } from "./auth-fetcher.mjs";
4
5
  import "./infer.mjs";
5
6
  import "./types.mjs";
6
- export { CALL_METHODS, TypedFetcher, createAuthAwareFetcher, createTypedFetcher, methodCalls };
7
+ export { CALL_METHODS, InvalidClientSampleRate, TypedFetcher, createAuthAwareFetcher, createTypedFetcher, methodCalls, sampledAt, traceContextInjector };
@@ -0,0 +1,65 @@
1
+ //#region src/telemetry.d.ts
2
+ /**
3
+ * W3C trace context on the requests a client makes to its own API, so a user
4
+ * action in the browser and the API request it causes are one trace.
5
+ *
6
+ * Two levels, chosen per request:
7
+ *
8
+ * - **An OpenTelemetry context is active** — a browser SDK has a span open, or
9
+ * this runs on a server (a Next.js server component, an API calling another
10
+ * API) inside a request span. The globally registered propagator writes the
11
+ * headers, so the current span is the parent: `traceparent`, `tracestate`,
12
+ * and whatever else that propagator carries.
13
+ * - **Otherwise** (level 1): one trace id per page view — per page load in a
14
+ * browser, per client in Node — a fresh span id per request, and a sampled
15
+ * flag decided once per page view from {@link ClientTelemetryOptions.sampleRate}.
16
+ *
17
+ * Nothing is sent to any origin but the API's own: a trace id is an identifier,
18
+ * and a third party has no business correlating a user's requests by it.
19
+ *
20
+ * `@opentelemetry/api` is not imported. Every copy of it — whichever version
21
+ * an app bundles — registers its globals on `globalThis` under
22
+ * `Symbol.for('opentelemetry.js.api.1')`, which is how two copies in one page
23
+ * already share a provider. Reading the propagator and context manager from
24
+ * there costs a browser bundle nothing when no SDK is loaded, and still finds
25
+ * the SDK when one is.
26
+ */
27
+ /** Telemetry on a client: `true` for the defaults, or these options. */
28
+ interface ClientTelemetryOptions {
29
+ /**
30
+ * The fraction of page views whose requests are sampled, from 0 to 1, when
31
+ * no OpenTelemetry context is active. Decided once per page view, so a
32
+ * page's requests are all kept or all dropped together.
33
+ *
34
+ * Use the stage's rate. The API caps an incoming sampled flag at its own
35
+ * rate anyway, and decides from the trace id the same way this does, so a
36
+ * page view sampled here at the API's rate is sampled there too.
37
+ * @default 1
38
+ */
39
+ sampleRate?: number;
40
+ }
41
+ /** A `sampleRate` outside 0–1, which no sampler can honour. */
42
+ declare class InvalidClientSampleRate extends Error {
43
+ readonly sampleRate: number;
44
+ constructor(sampleRate: number);
45
+ }
46
+ /** Adds trace headers to a request to `url`, when it is the API's own. */
47
+ type TraceContextInjector = (url: string, headers: Record<string, string>) => void;
48
+ /**
49
+ * Whether a trace is sampled at `rate`, from its id.
50
+ *
51
+ * The same rule as OpenTelemetry's `TraceIdRatioBasedSampler`: the id's four
52
+ * 32-bit words XORed together, under `rate` of the 32-bit range. The trace id
53
+ * is random, so this samples `rate` of page views — and the API, applying the
54
+ * same rule to the same id at the same rate, keeps exactly the page views kept
55
+ * here rather than a random `rate` of them.
56
+ */
57
+ declare function sampledAt(traceId: string, rate: number): boolean;
58
+ /**
59
+ * An injector for a client whose API is at `baseURL`, or undefined when
60
+ * telemetry is off.
61
+ */
62
+ declare function traceContextInjector(baseURL: string, telemetry: boolean | ClientTelemetryOptions | undefined): TraceContextInjector | undefined;
63
+ //#endregion
64
+ export { traceContextInjector as a, sampledAt as i, InvalidClientSampleRate as n, TraceContextInjector as r, ClientTelemetryOptions as t };
65
+ //# sourceMappingURL=telemetry-BbRAq81t.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-BbRAq81t.d.cts","names":[],"sources":["../src/telemetry.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;UA2BiB;;;;;;;;;;;EAWhB;;;cAIY,gCAAgC;WACvB;EAArB,YAAqB;;;KASV,wBACX,aACA,SAAS;;;;;;;;;;iBAiEM,UAAU,iBAAiB;;;;;iBA6C3B,qBACf,iBACA,qBAAqB,qCACnB"}
@@ -0,0 +1,65 @@
1
+ //#region src/telemetry.d.ts
2
+ /**
3
+ * W3C trace context on the requests a client makes to its own API, so a user
4
+ * action in the browser and the API request it causes are one trace.
5
+ *
6
+ * Two levels, chosen per request:
7
+ *
8
+ * - **An OpenTelemetry context is active** — a browser SDK has a span open, or
9
+ * this runs on a server (a Next.js server component, an API calling another
10
+ * API) inside a request span. The globally registered propagator writes the
11
+ * headers, so the current span is the parent: `traceparent`, `tracestate`,
12
+ * and whatever else that propagator carries.
13
+ * - **Otherwise** (level 1): one trace id per page view — per page load in a
14
+ * browser, per client in Node — a fresh span id per request, and a sampled
15
+ * flag decided once per page view from {@link ClientTelemetryOptions.sampleRate}.
16
+ *
17
+ * Nothing is sent to any origin but the API's own: a trace id is an identifier,
18
+ * and a third party has no business correlating a user's requests by it.
19
+ *
20
+ * `@opentelemetry/api` is not imported. Every copy of it — whichever version
21
+ * an app bundles — registers its globals on `globalThis` under
22
+ * `Symbol.for('opentelemetry.js.api.1')`, which is how two copies in one page
23
+ * already share a provider. Reading the propagator and context manager from
24
+ * there costs a browser bundle nothing when no SDK is loaded, and still finds
25
+ * the SDK when one is.
26
+ */
27
+ /** Telemetry on a client: `true` for the defaults, or these options. */
28
+ interface ClientTelemetryOptions {
29
+ /**
30
+ * The fraction of page views whose requests are sampled, from 0 to 1, when
31
+ * no OpenTelemetry context is active. Decided once per page view, so a
32
+ * page's requests are all kept or all dropped together.
33
+ *
34
+ * Use the stage's rate. The API caps an incoming sampled flag at its own
35
+ * rate anyway, and decides from the trace id the same way this does, so a
36
+ * page view sampled here at the API's rate is sampled there too.
37
+ * @default 1
38
+ */
39
+ sampleRate?: number;
40
+ }
41
+ /** A `sampleRate` outside 0–1, which no sampler can honour. */
42
+ declare class InvalidClientSampleRate extends Error {
43
+ readonly sampleRate: number;
44
+ constructor(sampleRate: number);
45
+ }
46
+ /** Adds trace headers to a request to `url`, when it is the API's own. */
47
+ type TraceContextInjector = (url: string, headers: Record<string, string>) => void;
48
+ /**
49
+ * Whether a trace is sampled at `rate`, from its id.
50
+ *
51
+ * The same rule as OpenTelemetry's `TraceIdRatioBasedSampler`: the id's four
52
+ * 32-bit words XORed together, under `rate` of the 32-bit range. The trace id
53
+ * is random, so this samples `rate` of page views — and the API, applying the
54
+ * same rule to the same id at the same rate, keeps exactly the page views kept
55
+ * here rather than a random `rate` of them.
56
+ */
57
+ declare function sampledAt(traceId: string, rate: number): boolean;
58
+ /**
59
+ * An injector for a client whose API is at `baseURL`, or undefined when
60
+ * telemetry is off.
61
+ */
62
+ declare function traceContextInjector(baseURL: string, telemetry: boolean | ClientTelemetryOptions | undefined): TraceContextInjector | undefined;
63
+ //#endregion
64
+ export { traceContextInjector as a, sampledAt as i, InvalidClientSampleRate as n, TraceContextInjector as r, ClientTelemetryOptions as t };
65
+ //# sourceMappingURL=telemetry-BbRAq81t.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-BbRAq81t.d.mts","names":[],"sources":["../src/telemetry.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;UA2BiB;;;;;;;;;;;EAWhB;;;cAIY,gCAAgC;WACvB;EAArB,YAAqB;;;KASV,wBACX,aACA,SAAS;;;;;;;;;;iBAiEM,UAAU,iBAAiB;;;;;iBA6C3B,qBACf,iBACA,qBAAqB,qCACnB"}
@@ -0,0 +1,109 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/telemetry.ts
3
+ /** A `sampleRate` outside 0–1, which no sampler can honour. */
4
+ var InvalidClientSampleRate = class extends Error {
5
+ sampleRate;
6
+ constructor(sampleRate) {
7
+ super(`telemetry.sampleRate must be a number from 0 (no page views) to 1 (every page view), got ${sampleRate}. Pass the stage's sample rate, such as 0.1.`);
8
+ this.sampleRate = sampleRate;
9
+ this.name = "InvalidClientSampleRate";
10
+ }
11
+ };
12
+ const OTEL_GLOBAL = Symbol.for("opentelemetry.js.api.1");
13
+ const setter = { set(carrier, key, value) {
14
+ carrier[key] = value;
15
+ } };
16
+ /**
17
+ * The headers the registered propagator writes for the active context, or
18
+ * undefined when there is no SDK or no span to continue.
19
+ */
20
+ function fromActiveContext() {
21
+ const api = globalThis[OTEL_GLOBAL];
22
+ if (!api?.propagation || !api.context) return void 0;
23
+ const carrier = {};
24
+ try {
25
+ api.propagation.inject(api.context.active(), carrier, setter);
26
+ } catch {
27
+ return;
28
+ }
29
+ return carrier.traceparent ? carrier : void 0;
30
+ }
31
+ /** `bytes` crypto-random bytes as lowercase hex, never all zeros. */
32
+ function randomHex(bytes) {
33
+ const buffer = new Uint8Array(bytes);
34
+ do
35
+ globalThis.crypto.getRandomValues(buffer);
36
+ while (buffer.every((byte) => byte === 0));
37
+ let hex = "";
38
+ for (const byte of buffer) hex += byte.toString(16).padStart(2, "0");
39
+ return hex;
40
+ }
41
+ /**
42
+ * Whether a trace is sampled at `rate`, from its id.
43
+ *
44
+ * The same rule as OpenTelemetry's `TraceIdRatioBasedSampler`: the id's four
45
+ * 32-bit words XORed together, under `rate` of the 32-bit range. The trace id
46
+ * is random, so this samples `rate` of page views — and the API, applying the
47
+ * same rule to the same id at the same rate, keeps exactly the page views kept
48
+ * here rather than a random `rate` of them.
49
+ */
50
+ function sampledAt(traceId, rate) {
51
+ if (rate >= 1) return true;
52
+ if (rate <= 0) return false;
53
+ let accumulation = 0;
54
+ for (let i = 0; i < traceId.length; i += 8) accumulation = (accumulation ^ parseInt(traceId.slice(i, i + 8), 16)) >>> 0;
55
+ return accumulation < Math.floor(rate * 4294967295);
56
+ }
57
+ let browserPageView;
58
+ /**
59
+ * The page view a request belongs to: the page load's in a browser, shared by
60
+ * every client on the page, or this client's own elsewhere — a server process
61
+ * outlives any one user action, so one trace for all of it would mean nothing.
62
+ */
63
+ function pageViewFor(own) {
64
+ if (!(typeof window !== "undefined" && typeof document !== "undefined")) return own();
65
+ browserPageView ??= { traceId: randomHex(16) };
66
+ return browserPageView;
67
+ }
68
+ /** The origin of `url`, resolved the way `fetch` would, or undefined. */
69
+ function originOf(url) {
70
+ try {
71
+ const base = globalThis.location?.href;
72
+ return new URL(url, base).origin;
73
+ } catch {
74
+ return;
75
+ }
76
+ }
77
+ /**
78
+ * An injector for a client whose API is at `baseURL`, or undefined when
79
+ * telemetry is off.
80
+ */
81
+ function traceContextInjector(baseURL, telemetry) {
82
+ if (!telemetry) return void 0;
83
+ const sampleRate = telemetry === true ? 1 : telemetry.sampleRate ?? 1;
84
+ if (!Number.isFinite(sampleRate) || sampleRate < 0 || sampleRate > 1) throw new InvalidClientSampleRate(sampleRate);
85
+ const apiOrigin = originOf(baseURL || "/");
86
+ let own;
87
+ const ownPageView = () => {
88
+ own ??= { traceId: randomHex(16) };
89
+ return own;
90
+ };
91
+ return (url, headers) => {
92
+ if (!apiOrigin || originOf(url) !== apiOrigin) return;
93
+ if (Object.keys(headers).some((h) => h.toLowerCase() === "traceparent")) return;
94
+ const active = fromActiveContext();
95
+ if (active) {
96
+ Object.assign(headers, active);
97
+ return;
98
+ }
99
+ const { traceId } = pageViewFor(ownPageView);
100
+ const flags = sampledAt(traceId, sampleRate) ? "01" : "00";
101
+ headers.traceparent = `00-${traceId}-${randomHex(8)}-${flags}`;
102
+ };
103
+ }
104
+ //#endregion
105
+ exports.InvalidClientSampleRate = InvalidClientSampleRate;
106
+ exports.sampledAt = sampledAt;
107
+ exports.traceContextInjector = traceContextInjector;
108
+
109
+ //# sourceMappingURL=telemetry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.cjs","names":[],"sources":["../src/telemetry.ts"],"sourcesContent":["/**\n * W3C trace context on the requests a client makes to its own API, so a user\n * action in the browser and the API request it causes are one trace.\n *\n * Two levels, chosen per request:\n *\n * - **An OpenTelemetry context is active** — a browser SDK has a span open, or\n * this runs on a server (a Next.js server component, an API calling another\n * API) inside a request span. The globally registered propagator writes the\n * headers, so the current span is the parent: `traceparent`, `tracestate`,\n * and whatever else that propagator carries.\n * - **Otherwise** (level 1): one trace id per page view — per page load in a\n * browser, per client in Node — a fresh span id per request, and a sampled\n * flag decided once per page view from {@link ClientTelemetryOptions.sampleRate}.\n *\n * Nothing is sent to any origin but the API's own: a trace id is an identifier,\n * and a third party has no business correlating a user's requests by it.\n *\n * `@opentelemetry/api` is not imported. Every copy of it — whichever version\n * an app bundles — registers its globals on `globalThis` under\n * `Symbol.for('opentelemetry.js.api.1')`, which is how two copies in one page\n * already share a provider. Reading the propagator and context manager from\n * there costs a browser bundle nothing when no SDK is loaded, and still finds\n * the SDK when one is.\n */\n\n/** Telemetry on a client: `true` for the defaults, or these options. */\nexport interface ClientTelemetryOptions {\n\t/**\n\t * The fraction of page views whose requests are sampled, from 0 to 1, when\n\t * no OpenTelemetry context is active. Decided once per page view, so a\n\t * page's requests are all kept or all dropped together.\n\t *\n\t * Use the stage's rate. The API caps an incoming sampled flag at its own\n\t * rate anyway, and decides from the trace id the same way this does, so a\n\t * page view sampled here at the API's rate is sampled there too.\n\t * @default 1\n\t */\n\tsampleRate?: number;\n}\n\n/** A `sampleRate` outside 0–1, which no sampler can honour. */\nexport class InvalidClientSampleRate extends Error {\n\tconstructor(readonly sampleRate: number) {\n\t\tsuper(\n\t\t\t`telemetry.sampleRate must be a number from 0 (no page views) to 1 (every page view), got ${sampleRate}. Pass the stage's sample rate, such as 0.1.`,\n\t\t);\n\t\tthis.name = 'InvalidClientSampleRate';\n\t}\n}\n\n/** Adds trace headers to a request to `url`, when it is the API's own. */\nexport type TraceContextInjector = (\n\turl: string,\n\theaders: Record<string, string>,\n) => void;\n\n/** The slice of `@opentelemetry/api`'s global registry this reads. */\ninterface OtelGlobal {\n\tcontext?: { active(): unknown };\n\tpropagation?: {\n\t\tinject(\n\t\t\tcontext: unknown,\n\t\t\tcarrier: Record<string, string>,\n\t\t\tsetter: {\n\t\t\t\tset(carrier: Record<string, string>, key: string, value: string): void;\n\t\t\t},\n\t\t): void;\n\t};\n}\n\nconst OTEL_GLOBAL = Symbol.for('opentelemetry.js.api.1');\n\nconst setter = {\n\tset(carrier: Record<string, string>, key: string, value: string) {\n\t\tcarrier[key] = value;\n\t},\n};\n\n/**\n * The headers the registered propagator writes for the active context, or\n * undefined when there is no SDK or no span to continue.\n */\nfunction fromActiveContext(): Record<string, string> | undefined {\n\tconst api = (globalThis as Record<symbol, OtelGlobal | undefined>)[\n\t\tOTEL_GLOBAL\n\t];\n\tif (!api?.propagation || !api.context) return undefined;\n\n\tconst carrier: Record<string, string> = {};\n\ttry {\n\t\tapi.propagation.inject(api.context.active(), carrier, setter);\n\t} catch {\n\t\treturn undefined;\n\t}\n\t// The W3C propagator writes nothing without a valid, unsuppressed span.\n\treturn carrier.traceparent ? carrier : undefined;\n}\n\n/** `bytes` crypto-random bytes as lowercase hex, never all zeros. */\nfunction randomHex(bytes: number): string {\n\tconst buffer = new Uint8Array(bytes);\n\tdo {\n\t\tglobalThis.crypto.getRandomValues(buffer);\n\t} while (buffer.every((byte) => byte === 0));\n\tlet hex = '';\n\tfor (const byte of buffer) hex += byte.toString(16).padStart(2, '0');\n\treturn hex;\n}\n\n/**\n * Whether a trace is sampled at `rate`, from its id.\n *\n * The same rule as OpenTelemetry's `TraceIdRatioBasedSampler`: the id's four\n * 32-bit words XORed together, under `rate` of the 32-bit range. The trace id\n * is random, so this samples `rate` of page views — and the API, applying the\n * same rule to the same id at the same rate, keeps exactly the page views kept\n * here rather than a random `rate` of them.\n */\nexport function sampledAt(traceId: string, rate: number): boolean {\n\tif (rate >= 1) return true;\n\tif (rate <= 0) return false;\n\tlet accumulation = 0;\n\tfor (let i = 0; i < traceId.length; i += 8) {\n\t\taccumulation = (accumulation ^ parseInt(traceId.slice(i, i + 8), 16)) >>> 0;\n\t}\n\treturn accumulation < Math.floor(rate * 0xffffffff);\n}\n\n/** A page view: one trace id for every request made during it. */\ninterface PageView {\n\ttraceId: string;\n}\n\nlet browserPageView: PageView | undefined;\n\n/**\n * The page view a request belongs to: the page load's in a browser, shared by\n * every client on the page, or this client's own elsewhere — a server process\n * outlives any one user action, so one trace for all of it would mean nothing.\n */\nfunction pageViewFor(own: () => PageView): PageView {\n\tconst browser =\n\t\ttypeof window !== 'undefined' && typeof document !== 'undefined';\n\tif (!browser) return own();\n\tbrowserPageView ??= { traceId: randomHex(16) };\n\treturn browserPageView;\n}\n\n/** The origin of `url`, resolved the way `fetch` would, or undefined. */\nfunction originOf(url: string): string | undefined {\n\ttry {\n\t\tconst base = (globalThis as { location?: { href?: string } }).location\n\t\t\t?.href;\n\t\treturn new URL(url, base).origin;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * An injector for a client whose API is at `baseURL`, or undefined when\n * telemetry is off.\n */\nexport function traceContextInjector(\n\tbaseURL: string,\n\ttelemetry: boolean | ClientTelemetryOptions | undefined,\n): TraceContextInjector | undefined {\n\tif (!telemetry) return undefined;\n\n\tconst sampleRate = telemetry === true ? 1 : (telemetry.sampleRate ?? 1);\n\tif (!Number.isFinite(sampleRate) || sampleRate < 0 || sampleRate > 1) {\n\t\tthrow new InvalidClientSampleRate(sampleRate);\n\t}\n\n\tconst apiOrigin = originOf(baseURL || '/');\n\tlet own: PageView | undefined;\n\tconst ownPageView = () => {\n\t\town ??= { traceId: randomHex(16) };\n\t\treturn own;\n\t};\n\n\treturn (url, headers) => {\n\t\t// Only the API's own origin: never a third party, and never anything\n\t\t// whose origin cannot be told.\n\t\tif (!apiOrigin || originOf(url) !== apiOrigin) return;\n\t\t// A caller that set its own context keeps it.\n\t\tif (Object.keys(headers).some((h) => h.toLowerCase() === 'traceparent'))\n\t\t\treturn;\n\n\t\tconst active = fromActiveContext();\n\t\tif (active) {\n\t\t\tObject.assign(headers, active);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { traceId } = pageViewFor(ownPageView);\n\t\tconst flags = sampledAt(traceId, sampleRate) ? '01' : '00';\n\t\theaders.traceparent = `00-${traceId}-${randomHex(8)}-${flags}`;\n\t};\n}\n"],"mappings":";;;AA0CA,IAAa,0BAAb,cAA6C,MAAM;CAC7B;CAArB,YAAY,YAA6B;EACxC,MACC,4FAA4F,WAAW,6CACxG;EAHoB,KAAA,aAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAsBA,MAAM,cAAc,OAAO,IAAI,wBAAwB;AAEvD,MAAM,SAAS,EACd,IAAI,SAAiC,KAAa,OAAe;CAChE,QAAQ,OAAO;AAChB,EACD;;;;;AAMA,SAAS,oBAAwD;CAChE,MAAM,MAAO,WACZ;CAED,IAAI,CAAC,KAAK,eAAe,CAAC,IAAI,SAAS,OAAO,KAAA;CAE9C,MAAM,UAAkC,CAAC;CACzC,IAAI;EACH,IAAI,YAAY,OAAO,IAAI,QAAQ,OAAO,GAAG,SAAS,MAAM;CAC7D,QAAQ;EACP;CACD;CAEA,OAAO,QAAQ,cAAc,UAAU,KAAA;AACxC;;AAGA,SAAS,UAAU,OAAuB;CACzC,MAAM,SAAS,IAAI,WAAW,KAAK;CACnC;EACC,WAAW,OAAO,gBAAgB,MAAM;QAChC,OAAO,OAAO,SAAS,SAAS,CAAC;CAC1C,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,QAAQ,OAAO,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CACnE,OAAO;AACR;;;;;;;;;;AAWA,SAAgB,UAAU,SAAiB,MAAuB;CACjE,IAAI,QAAQ,GAAG,OAAO;CACtB,IAAI,QAAQ,GAAG,OAAO;CACtB,IAAI,eAAe;CACnB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GACxC,gBAAgB,eAAe,SAAS,QAAQ,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO;CAE3E,OAAO,eAAe,KAAK,MAAM,OAAO,UAAU;AACnD;AAOA,IAAI;;;;;;AAOJ,SAAS,YAAY,KAA+B;CAGnD,IAAI,EADH,OAAO,WAAW,eAAe,OAAO,aAAa,cACxC,OAAO,IAAI;CACzB,oBAAoB,EAAE,SAAS,UAAU,EAAE,EAAE;CAC7C,OAAO;AACR;;AAGA,SAAS,SAAS,KAAiC;CAClD,IAAI;EACH,MAAM,OAAQ,WAAgD,UAC3D;EACH,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC;CAC3B,QAAQ;EACP;CACD;AACD;;;;;AAMA,SAAgB,qBACf,SACA,WACmC;CACnC,IAAI,CAAC,WAAW,OAAO,KAAA;CAEvB,MAAM,aAAa,cAAc,OAAO,IAAK,UAAU,cAAc;CACrE,IAAI,CAAC,OAAO,SAAS,UAAU,KAAK,aAAa,KAAK,aAAa,GAClE,MAAM,IAAI,wBAAwB,UAAU;CAG7C,MAAM,YAAY,SAAS,WAAW,GAAG;CACzC,IAAI;CACJ,MAAM,oBAAoB;EACzB,QAAQ,EAAE,SAAS,UAAU,EAAE,EAAE;EACjC,OAAO;CACR;CAEA,QAAQ,KAAK,YAAY;EAGxB,IAAI,CAAC,aAAa,SAAS,GAAG,MAAM,WAAW;EAE/C,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,MAAM,MAAM,EAAE,YAAY,MAAM,aAAa,GACrE;EAED,MAAM,SAAS,kBAAkB;EACjC,IAAI,QAAQ;GACX,OAAO,OAAO,SAAS,MAAM;GAC7B;EACD;EAEA,MAAM,EAAE,YAAY,YAAY,WAAW;EAC3C,MAAM,QAAQ,UAAU,SAAS,UAAU,IAAI,OAAO;EACtD,QAAQ,cAAc,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,GAAG;CACxD;AACD"}
@@ -0,0 +1,2 @@
1
+ import { a as traceContextInjector, i as sampledAt, n as InvalidClientSampleRate, r as TraceContextInjector, t as ClientTelemetryOptions } from "./telemetry-BbRAq81t.cjs";
2
+ export { ClientTelemetryOptions, InvalidClientSampleRate, TraceContextInjector, sampledAt, traceContextInjector };
@@ -0,0 +1,2 @@
1
+ import { a as traceContextInjector, i as sampledAt, n as InvalidClientSampleRate, r as TraceContextInjector, t as ClientTelemetryOptions } from "./telemetry-BbRAq81t.mjs";
2
+ export { ClientTelemetryOptions, InvalidClientSampleRate, TraceContextInjector, sampledAt, traceContextInjector };
@@ -0,0 +1,106 @@
1
+ //#region src/telemetry.ts
2
+ /** A `sampleRate` outside 0–1, which no sampler can honour. */
3
+ var InvalidClientSampleRate = class extends Error {
4
+ sampleRate;
5
+ constructor(sampleRate) {
6
+ super(`telemetry.sampleRate must be a number from 0 (no page views) to 1 (every page view), got ${sampleRate}. Pass the stage's sample rate, such as 0.1.`);
7
+ this.sampleRate = sampleRate;
8
+ this.name = "InvalidClientSampleRate";
9
+ }
10
+ };
11
+ const OTEL_GLOBAL = Symbol.for("opentelemetry.js.api.1");
12
+ const setter = { set(carrier, key, value) {
13
+ carrier[key] = value;
14
+ } };
15
+ /**
16
+ * The headers the registered propagator writes for the active context, or
17
+ * undefined when there is no SDK or no span to continue.
18
+ */
19
+ function fromActiveContext() {
20
+ const api = globalThis[OTEL_GLOBAL];
21
+ if (!api?.propagation || !api.context) return void 0;
22
+ const carrier = {};
23
+ try {
24
+ api.propagation.inject(api.context.active(), carrier, setter);
25
+ } catch {
26
+ return;
27
+ }
28
+ return carrier.traceparent ? carrier : void 0;
29
+ }
30
+ /** `bytes` crypto-random bytes as lowercase hex, never all zeros. */
31
+ function randomHex(bytes) {
32
+ const buffer = new Uint8Array(bytes);
33
+ do
34
+ globalThis.crypto.getRandomValues(buffer);
35
+ while (buffer.every((byte) => byte === 0));
36
+ let hex = "";
37
+ for (const byte of buffer) hex += byte.toString(16).padStart(2, "0");
38
+ return hex;
39
+ }
40
+ /**
41
+ * Whether a trace is sampled at `rate`, from its id.
42
+ *
43
+ * The same rule as OpenTelemetry's `TraceIdRatioBasedSampler`: the id's four
44
+ * 32-bit words XORed together, under `rate` of the 32-bit range. The trace id
45
+ * is random, so this samples `rate` of page views — and the API, applying the
46
+ * same rule to the same id at the same rate, keeps exactly the page views kept
47
+ * here rather than a random `rate` of them.
48
+ */
49
+ function sampledAt(traceId, rate) {
50
+ if (rate >= 1) return true;
51
+ if (rate <= 0) return false;
52
+ let accumulation = 0;
53
+ for (let i = 0; i < traceId.length; i += 8) accumulation = (accumulation ^ parseInt(traceId.slice(i, i + 8), 16)) >>> 0;
54
+ return accumulation < Math.floor(rate * 4294967295);
55
+ }
56
+ let browserPageView;
57
+ /**
58
+ * The page view a request belongs to: the page load's in a browser, shared by
59
+ * every client on the page, or this client's own elsewhere — a server process
60
+ * outlives any one user action, so one trace for all of it would mean nothing.
61
+ */
62
+ function pageViewFor(own) {
63
+ if (!(typeof window !== "undefined" && typeof document !== "undefined")) return own();
64
+ browserPageView ??= { traceId: randomHex(16) };
65
+ return browserPageView;
66
+ }
67
+ /** The origin of `url`, resolved the way `fetch` would, or undefined. */
68
+ function originOf(url) {
69
+ try {
70
+ const base = globalThis.location?.href;
71
+ return new URL(url, base).origin;
72
+ } catch {
73
+ return;
74
+ }
75
+ }
76
+ /**
77
+ * An injector for a client whose API is at `baseURL`, or undefined when
78
+ * telemetry is off.
79
+ */
80
+ function traceContextInjector(baseURL, telemetry) {
81
+ if (!telemetry) return void 0;
82
+ const sampleRate = telemetry === true ? 1 : telemetry.sampleRate ?? 1;
83
+ if (!Number.isFinite(sampleRate) || sampleRate < 0 || sampleRate > 1) throw new InvalidClientSampleRate(sampleRate);
84
+ const apiOrigin = originOf(baseURL || "/");
85
+ let own;
86
+ const ownPageView = () => {
87
+ own ??= { traceId: randomHex(16) };
88
+ return own;
89
+ };
90
+ return (url, headers) => {
91
+ if (!apiOrigin || originOf(url) !== apiOrigin) return;
92
+ if (Object.keys(headers).some((h) => h.toLowerCase() === "traceparent")) return;
93
+ const active = fromActiveContext();
94
+ if (active) {
95
+ Object.assign(headers, active);
96
+ return;
97
+ }
98
+ const { traceId } = pageViewFor(ownPageView);
99
+ const flags = sampledAt(traceId, sampleRate) ? "01" : "00";
100
+ headers.traceparent = `00-${traceId}-${randomHex(8)}-${flags}`;
101
+ };
102
+ }
103
+ //#endregion
104
+ export { InvalidClientSampleRate, sampledAt, traceContextInjector };
105
+
106
+ //# sourceMappingURL=telemetry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.mjs","names":[],"sources":["../src/telemetry.ts"],"sourcesContent":["/**\n * W3C trace context on the requests a client makes to its own API, so a user\n * action in the browser and the API request it causes are one trace.\n *\n * Two levels, chosen per request:\n *\n * - **An OpenTelemetry context is active** — a browser SDK has a span open, or\n * this runs on a server (a Next.js server component, an API calling another\n * API) inside a request span. The globally registered propagator writes the\n * headers, so the current span is the parent: `traceparent`, `tracestate`,\n * and whatever else that propagator carries.\n * - **Otherwise** (level 1): one trace id per page view — per page load in a\n * browser, per client in Node — a fresh span id per request, and a sampled\n * flag decided once per page view from {@link ClientTelemetryOptions.sampleRate}.\n *\n * Nothing is sent to any origin but the API's own: a trace id is an identifier,\n * and a third party has no business correlating a user's requests by it.\n *\n * `@opentelemetry/api` is not imported. Every copy of it — whichever version\n * an app bundles — registers its globals on `globalThis` under\n * `Symbol.for('opentelemetry.js.api.1')`, which is how two copies in one page\n * already share a provider. Reading the propagator and context manager from\n * there costs a browser bundle nothing when no SDK is loaded, and still finds\n * the SDK when one is.\n */\n\n/** Telemetry on a client: `true` for the defaults, or these options. */\nexport interface ClientTelemetryOptions {\n\t/**\n\t * The fraction of page views whose requests are sampled, from 0 to 1, when\n\t * no OpenTelemetry context is active. Decided once per page view, so a\n\t * page's requests are all kept or all dropped together.\n\t *\n\t * Use the stage's rate. The API caps an incoming sampled flag at its own\n\t * rate anyway, and decides from the trace id the same way this does, so a\n\t * page view sampled here at the API's rate is sampled there too.\n\t * @default 1\n\t */\n\tsampleRate?: number;\n}\n\n/** A `sampleRate` outside 0–1, which no sampler can honour. */\nexport class InvalidClientSampleRate extends Error {\n\tconstructor(readonly sampleRate: number) {\n\t\tsuper(\n\t\t\t`telemetry.sampleRate must be a number from 0 (no page views) to 1 (every page view), got ${sampleRate}. Pass the stage's sample rate, such as 0.1.`,\n\t\t);\n\t\tthis.name = 'InvalidClientSampleRate';\n\t}\n}\n\n/** Adds trace headers to a request to `url`, when it is the API's own. */\nexport type TraceContextInjector = (\n\turl: string,\n\theaders: Record<string, string>,\n) => void;\n\n/** The slice of `@opentelemetry/api`'s global registry this reads. */\ninterface OtelGlobal {\n\tcontext?: { active(): unknown };\n\tpropagation?: {\n\t\tinject(\n\t\t\tcontext: unknown,\n\t\t\tcarrier: Record<string, string>,\n\t\t\tsetter: {\n\t\t\t\tset(carrier: Record<string, string>, key: string, value: string): void;\n\t\t\t},\n\t\t): void;\n\t};\n}\n\nconst OTEL_GLOBAL = Symbol.for('opentelemetry.js.api.1');\n\nconst setter = {\n\tset(carrier: Record<string, string>, key: string, value: string) {\n\t\tcarrier[key] = value;\n\t},\n};\n\n/**\n * The headers the registered propagator writes for the active context, or\n * undefined when there is no SDK or no span to continue.\n */\nfunction fromActiveContext(): Record<string, string> | undefined {\n\tconst api = (globalThis as Record<symbol, OtelGlobal | undefined>)[\n\t\tOTEL_GLOBAL\n\t];\n\tif (!api?.propagation || !api.context) return undefined;\n\n\tconst carrier: Record<string, string> = {};\n\ttry {\n\t\tapi.propagation.inject(api.context.active(), carrier, setter);\n\t} catch {\n\t\treturn undefined;\n\t}\n\t// The W3C propagator writes nothing without a valid, unsuppressed span.\n\treturn carrier.traceparent ? carrier : undefined;\n}\n\n/** `bytes` crypto-random bytes as lowercase hex, never all zeros. */\nfunction randomHex(bytes: number): string {\n\tconst buffer = new Uint8Array(bytes);\n\tdo {\n\t\tglobalThis.crypto.getRandomValues(buffer);\n\t} while (buffer.every((byte) => byte === 0));\n\tlet hex = '';\n\tfor (const byte of buffer) hex += byte.toString(16).padStart(2, '0');\n\treturn hex;\n}\n\n/**\n * Whether a trace is sampled at `rate`, from its id.\n *\n * The same rule as OpenTelemetry's `TraceIdRatioBasedSampler`: the id's four\n * 32-bit words XORed together, under `rate` of the 32-bit range. The trace id\n * is random, so this samples `rate` of page views — and the API, applying the\n * same rule to the same id at the same rate, keeps exactly the page views kept\n * here rather than a random `rate` of them.\n */\nexport function sampledAt(traceId: string, rate: number): boolean {\n\tif (rate >= 1) return true;\n\tif (rate <= 0) return false;\n\tlet accumulation = 0;\n\tfor (let i = 0; i < traceId.length; i += 8) {\n\t\taccumulation = (accumulation ^ parseInt(traceId.slice(i, i + 8), 16)) >>> 0;\n\t}\n\treturn accumulation < Math.floor(rate * 0xffffffff);\n}\n\n/** A page view: one trace id for every request made during it. */\ninterface PageView {\n\ttraceId: string;\n}\n\nlet browserPageView: PageView | undefined;\n\n/**\n * The page view a request belongs to: the page load's in a browser, shared by\n * every client on the page, or this client's own elsewhere — a server process\n * outlives any one user action, so one trace for all of it would mean nothing.\n */\nfunction pageViewFor(own: () => PageView): PageView {\n\tconst browser =\n\t\ttypeof window !== 'undefined' && typeof document !== 'undefined';\n\tif (!browser) return own();\n\tbrowserPageView ??= { traceId: randomHex(16) };\n\treturn browserPageView;\n}\n\n/** The origin of `url`, resolved the way `fetch` would, or undefined. */\nfunction originOf(url: string): string | undefined {\n\ttry {\n\t\tconst base = (globalThis as { location?: { href?: string } }).location\n\t\t\t?.href;\n\t\treturn new URL(url, base).origin;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * An injector for a client whose API is at `baseURL`, or undefined when\n * telemetry is off.\n */\nexport function traceContextInjector(\n\tbaseURL: string,\n\ttelemetry: boolean | ClientTelemetryOptions | undefined,\n): TraceContextInjector | undefined {\n\tif (!telemetry) return undefined;\n\n\tconst sampleRate = telemetry === true ? 1 : (telemetry.sampleRate ?? 1);\n\tif (!Number.isFinite(sampleRate) || sampleRate < 0 || sampleRate > 1) {\n\t\tthrow new InvalidClientSampleRate(sampleRate);\n\t}\n\n\tconst apiOrigin = originOf(baseURL || '/');\n\tlet own: PageView | undefined;\n\tconst ownPageView = () => {\n\t\town ??= { traceId: randomHex(16) };\n\t\treturn own;\n\t};\n\n\treturn (url, headers) => {\n\t\t// Only the API's own origin: never a third party, and never anything\n\t\t// whose origin cannot be told.\n\t\tif (!apiOrigin || originOf(url) !== apiOrigin) return;\n\t\t// A caller that set its own context keeps it.\n\t\tif (Object.keys(headers).some((h) => h.toLowerCase() === 'traceparent'))\n\t\t\treturn;\n\n\t\tconst active = fromActiveContext();\n\t\tif (active) {\n\t\t\tObject.assign(headers, active);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { traceId } = pageViewFor(ownPageView);\n\t\tconst flags = sampledAt(traceId, sampleRate) ? '01' : '00';\n\t\theaders.traceparent = `00-${traceId}-${randomHex(8)}-${flags}`;\n\t};\n}\n"],"mappings":";;AA0CA,IAAa,0BAAb,cAA6C,MAAM;CAC7B;CAArB,YAAY,YAA6B;EACxC,MACC,4FAA4F,WAAW,6CACxG;EAHoB,KAAA,aAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAsBA,MAAM,cAAc,OAAO,IAAI,wBAAwB;AAEvD,MAAM,SAAS,EACd,IAAI,SAAiC,KAAa,OAAe;CAChE,QAAQ,OAAO;AAChB,EACD;;;;;AAMA,SAAS,oBAAwD;CAChE,MAAM,MAAO,WACZ;CAED,IAAI,CAAC,KAAK,eAAe,CAAC,IAAI,SAAS,OAAO,KAAA;CAE9C,MAAM,UAAkC,CAAC;CACzC,IAAI;EACH,IAAI,YAAY,OAAO,IAAI,QAAQ,OAAO,GAAG,SAAS,MAAM;CAC7D,QAAQ;EACP;CACD;CAEA,OAAO,QAAQ,cAAc,UAAU,KAAA;AACxC;;AAGA,SAAS,UAAU,OAAuB;CACzC,MAAM,SAAS,IAAI,WAAW,KAAK;CACnC;EACC,WAAW,OAAO,gBAAgB,MAAM;QAChC,OAAO,OAAO,SAAS,SAAS,CAAC;CAC1C,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,QAAQ,OAAO,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CACnE,OAAO;AACR;;;;;;;;;;AAWA,SAAgB,UAAU,SAAiB,MAAuB;CACjE,IAAI,QAAQ,GAAG,OAAO;CACtB,IAAI,QAAQ,GAAG,OAAO;CACtB,IAAI,eAAe;CACnB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GACxC,gBAAgB,eAAe,SAAS,QAAQ,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO;CAE3E,OAAO,eAAe,KAAK,MAAM,OAAO,UAAU;AACnD;AAOA,IAAI;;;;;;AAOJ,SAAS,YAAY,KAA+B;CAGnD,IAAI,EADH,OAAO,WAAW,eAAe,OAAO,aAAa,cACxC,OAAO,IAAI;CACzB,oBAAoB,EAAE,SAAS,UAAU,EAAE,EAAE;CAC7C,OAAO;AACR;;AAGA,SAAS,SAAS,KAAiC;CAClD,IAAI;EACH,MAAM,OAAQ,WAAgD,UAC3D;EACH,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC;CAC3B,QAAQ;EACP;CACD;AACD;;;;;AAMA,SAAgB,qBACf,SACA,WACmC;CACnC,IAAI,CAAC,WAAW,OAAO,KAAA;CAEvB,MAAM,aAAa,cAAc,OAAO,IAAK,UAAU,cAAc;CACrE,IAAI,CAAC,OAAO,SAAS,UAAU,KAAK,aAAa,KAAK,aAAa,GAClE,MAAM,IAAI,wBAAwB,UAAU;CAG7C,MAAM,YAAY,SAAS,WAAW,GAAG;CACzC,IAAI;CACJ,MAAM,oBAAoB;EACzB,QAAQ,EAAE,SAAS,UAAU,EAAE,EAAE;EACjC,OAAO;CACR;CAEA,QAAQ,KAAK,YAAY;EAGxB,IAAI,CAAC,aAAa,SAAS,GAAG,MAAM,WAAW;EAE/C,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,MAAM,MAAM,EAAE,YAAY,MAAM,aAAa,GACrE;EAED,MAAM,SAAS,kBAAkB;EACjC,IAAI,QAAQ;GACX,OAAO,OAAO,SAAS,MAAM;GAC7B;EACD;EAEA,MAAM,EAAE,YAAY,YAAY,WAAW;EAC3C,MAAM,QAAQ,UAAU,SAAS,UAAU,IAAI,OAAO;EACtD,QAAQ,cAAc,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,GAAG;CACxD;AACD"}
package/dist/types.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as ClientTelemetryOptions } from "./telemetry-BbRAq81t.cjs";
1
2
  //#region src/types.d.ts
2
3
  export type OpenAPIRoutes<Paths> = keyof Paths;
3
4
  type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options';
@@ -100,6 +101,13 @@ export interface FetcherOptions {
100
101
  onResponse?: (response: Response) => Response | Promise<Response>;
101
102
  onError?: (error: Error) => void | Promise<void>;
102
103
  fetch?: typeof fetch;
104
+ /**
105
+ * W3C trace context (`traceparent`, `tracestate`) on every request to
106
+ * `baseURL`'s origin — never another. Continues the active OpenTelemetry
107
+ * span when there is one; otherwise a trace per page view, sampled at
108
+ * `sampleRate`. Off unless set.
109
+ */
110
+ telemetry?: boolean | ClientTelemetryOptions;
103
111
  }
104
112
  export type WrappedResult<T, E = unknown> = {
105
113
  ok: true;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";YAAY,cAAc,eAAe;KAEpC;YASO,cAAc,OAAO,cAAc,cAAc,UAAU,cAChE,MAAM,QACZ;YAGW,kBACX,OACA,cAAc,cAAc,UACzB,MAAM;EACT;IAAe,aAAa;;IAE1B;YAGS,mBACX,OACA,cAAc,cAAc,QAC5B,eAAe,cAAc,OAAO,UACjC,MAAM,OAAO;EAAkB;IAAe,cAAc;;IAC7D;YAGS,mBACX,OACA,cAAc,cAAc,QAC5B,eAAe,cAAc,OAAO,UACjC,MAAM,OAAO;EAAkB,oBAAoB;KAIpD,YAAY,+BAEX,YAAY;EAAc;IAAY,2BAA2B;;IAChE;YAIO,gBACX,OACA,cAAc,cAAc,QAC5B,eAAe,cAAc,OAAO,UACjC,MAAM,OAAO;EAChB;IAAc;MAAQ;QAAY,2BAA2B;;;;IAE3D,IACA,MAAM,OAAO;EACZ;IAAc;MAAQ;QAAY,2BAA2B;;;;IAE7D;YAGQ,cACX,OACA,cAAc,cAAc,QAC5B,eAAe,cAAc,OAAO;EAEpC,SAAS,kBAAkB,OAAO;EAClC,QAAQ,mBAAmB,OAAO,OAAO;EACzC,OAAO,mBAAmB,OAAO,OAAO;EACxC,UAAU;;YAGC,oBAAoB;YAIpB,cAAc,YACxB,eAAe,WACd,UAAU,cAAc,MAAM,QAAQ,iBAAiB,mBAC9C,oBACI,WACb,cAAc,MAAM,QAAQ,sBACvB;YAGI,cAAc,SACzB,cAAc,sBAAqB,MAAK,mBAAmB;YAGhD,uBACX,OACA,yBACG,cAAc,sBAAqB,KACpC,aAAa,qBACZ;YAKQ,cAAc,SAAS,uBAAuB;YAG9C,iBAAiB,SAC1B,uBAAuB,iBACvB,uBAAuB,kBACvB,uBAAuB,gBACvB,uBAAuB;YAEd,cAAc,UAAU,kBACnC,mBAAmB,gBAAgB;EAC9B,QAAQ,UAAU;EAAS,OAAO;;YAG5B,wBACX,OACA,UAAU,kBACP,mBAAmB,gBAAgB,UACpC,cAAc,cAAc,SAC3B,UAAU,gBAAgB,cAAc,OAAO,SAC9C,gBAAgB,OAAO,OAAO,UAAU;YAKjC,sBACX,OACA,UAAU,kBACP,mBAAmB,gBAAgB,UACpC,cAAc,cAAc,SAC3B,UAAU,gBAAgB,cAAc,OAAO,SAC9C,cAAc,OAAO,OAAO,UAAU;;;;;;;;YAY/B,sBACX,OACA,UAAU,kBACP,mBAAmB,gBAAgB,UACpC,cAAc,cAAc,SAC3B,UAAU,gBAAgB,cAAc,OAAO,SAC9C,mBACA,kBAAkB,OAAO,QACzB,mBAAmB,OAAO,OAAO,UAAU,UAC3C,mBAAmB,OAAO,OAAO,UAAU;;;;;;KAW3C,mBAAmB,SAAS,QAAQ,SAAS,uBAE/C;EAAkC,QAAQ;OAEzC;EAAgC,MAAM;OAEtC,0CAEY;EACR,QAAQ;;EACR,OAAO;;EAEZ,UAAU;;;;;KAOR,qBAAqB,QAAQ,WAAW,IAAI,EAAE;;KAG9C,iBAAiB,WAAW,6CAEnB;;;;YAOF,iBACX,OACA,UAAU,kBACP,mBAAmB,gBAAgB,UACpC,cAAc,cAAc,SAC3B,UAAU,gBAAgB,cAAc,OAAO,SAC9C,kBAAkB,OAAO,uBACxB,mBAAmB,OAAO,OAAO,UAAU,yBAC1C,iBACA,mBAAmB,OAAO,OAAO,UAAU;;;;;YAYtC,iBAAiB,UAAU,UAAU,cAAc,QAC9D,UAAU,MACP,MAAM,iBAAiB,OAAO,mBAC7B,QAAQ,sBAAsB,OAAO,OACrC,SAAS,sBAAsB,OAAO,QACtC,QAAQ,wBAAwB,OAAO;iBAE3B;EAChB;EACA,UAAU;EACV,aAAa,QAAQ,gBAAgB,cAAc,QAAQ;EAC3D,cAAc,UAAU,aAAa,WAAW,QAAQ;EACxD,WAAW,OAAO,iBAAiB;EACnC,eAAe;;YAGJ,cAAc,GAAG;EACxB;EAAU,MAAM;;EAChB;EAAW,OAAO;;YAEX,iBAAiB,MAAM,mBAAmB,IAAI,QAAQ;YAEtD,mBAAmB,OAAO,gBACrC,UAAU,cAAc,QAExB,UAAU,MACP,MAAM,iBAAiB,OAAO,mBAC7B,QAAQ,sBAAsB,OAAO,OACrC,SAAS,sBAAsB,OAAO,QACtC,QAAQ,cAAc,wBAAwB,OAAO,IAAI"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":"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"}
package/dist/types.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as ClientTelemetryOptions } from "./telemetry-BbRAq81t.mjs";
1
2
  //#region src/types.d.ts
2
3
  export type OpenAPIRoutes<Paths> = keyof Paths;
3
4
  type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options';
@@ -100,6 +101,13 @@ export interface FetcherOptions {
100
101
  onResponse?: (response: Response) => Response | Promise<Response>;
101
102
  onError?: (error: Error) => void | Promise<void>;
102
103
  fetch?: typeof fetch;
104
+ /**
105
+ * W3C trace context (`traceparent`, `tracestate`) on every request to
106
+ * `baseURL`'s origin — never another. Continues the active OpenTelemetry
107
+ * span when there is one; otherwise a trace per page view, sampled at
108
+ * `sampleRate`. Off unless set.
109
+ */
110
+ telemetry?: boolean | ClientTelemetryOptions;
103
111
  }
104
112
  export type WrappedResult<T, E = unknown> = {
105
113
  ok: true;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/client",
3
- "version": "10.0.0-alpha.80",
3
+ "version": "10.0.0-alpha.82",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -83,6 +83,16 @@
83
83
  "types": "./dist/endpoint-hooks.d.cts",
84
84
  "default": "./dist/endpoint-hooks.cjs"
85
85
  }
86
+ },
87
+ "./telemetry": {
88
+ "import": {
89
+ "types": "./dist/telemetry.d.mts",
90
+ "default": "./dist/telemetry.mjs"
91
+ },
92
+ "require": {
93
+ "types": "./dist/telemetry.d.cts",
94
+ "default": "./dist/telemetry.cjs"
95
+ }
86
96
  }
87
97
  },
88
98
  "files": [
@@ -101,6 +111,8 @@
101
111
  "qs": "~6.16.0"
102
112
  },
103
113
  "devDependencies": {
114
+ "@opentelemetry/api": "~1.9.1",
115
+ "@opentelemetry/sdk-trace-node": "~2.11.0",
104
116
  "@tanstack/react-query": "~5.104.0",
105
117
  "@testing-library/dom": "~10.4.2",
106
118
  "@testing-library/jest-dom": "~6.6.3",
@@ -111,16 +123,16 @@
111
123
  "@types/react-dom": "~19.3.0",
112
124
  "jsdom": "~26.1.0",
113
125
  "msw": "~2.15.0",
114
- "@geekmidas/constructs": "^10.0.0-alpha.80",
115
- "@geekmidas/schema": "^10.0.0-alpha.80"
126
+ "@geekmidas/constructs": "^10.0.0-alpha.82",
127
+ "@geekmidas/schema": "^10.0.0-alpha.82"
116
128
  },
117
129
  "peerDependencies": {
118
130
  "@tanstack/react-query": ">=5.0.0",
119
131
  "react": ">=18.0.0",
120
132
  "react-dom": ">=18.0.0",
121
133
  "zod": ">=4.6.5",
122
- "@geekmidas/constructs": "^10.0.0-alpha.80",
123
- "@geekmidas/schema": "^10.0.0-alpha.80"
134
+ "@geekmidas/constructs": "^10.0.0-alpha.82",
135
+ "@geekmidas/schema": "^10.0.0-alpha.82"
124
136
  },
125
137
  "peerDependenciesMeta": {
126
138
  "@geekmidas/constructs": {