zlient 3.0.2 → 3.1.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.
package/README.md CHANGED
@@ -50,7 +50,7 @@ const client = new HttpClient({
50
50
  baseUrls: {
51
51
  default: 'https://api.example.com',
52
52
  },
53
- retry: { maxRetries: 3 },
53
+ retry: { maxAttempts: 3, baseDelayMs: 1000 },
54
54
  });
55
55
  ```
56
56
 
@@ -125,6 +125,23 @@ console.log(user.name);
125
125
 
126
126
  ## Advanced Usage
127
127
 
128
+ ### Retry Configuration
129
+
130
+ Zlient automatically retries failed requests with exponential backoff. Customize the retry behavior:
131
+
132
+ ```typescript
133
+ const client = new HttpClient({
134
+ baseUrls: { default: 'https://api.example.com' },
135
+ retry: {
136
+ maxAttempts: 3, // Total attempts (including initial request)
137
+ baseDelayMs: 1000, // Base delay for exponential backoff
138
+ retryMethods: ['GET', 'POST', 'PUT'], // Methods to retry
139
+ retryStatusCodes: [500, 502, 503, 504], // Status codes to retry
140
+ respectRetryAfter: true, // Honor Retry-After header
141
+ },
142
+ });
143
+ ```
144
+
128
145
  ### Authentication
129
146
 
130
147
  Zlient provides built-in auth providers that safely handle headers.
package/dist/auth.d.ts CHANGED
@@ -36,7 +36,7 @@ export interface AuthProvider {
36
36
  * Use this when you don't need authentication.
37
37
  */
38
38
  export declare class NoAuth implements AuthProvider {
39
- apply(): Promise<void>;
39
+ apply(_req: AuthContext): Promise<void>;
40
40
  }
41
41
  /**
42
42
  * API Key authentication provider.
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/C;AAED;;;GAGG;AACH,qBAAa,MAAO,YAAW,YAAY;IACnC,KAAK;CAGZ;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAW,YAAW,YAAY;IACjC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQ5E,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,WAAW;CAgBjC;AACD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAgB,YAAW,YAAY;IACtC,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;IACtD,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW;CAclC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/C;AAED;;;GAGG;AACH,qBAAa,MAAO,YAAW,YAAY;IACnC,KAAK,CAAC,IAAI,EAAE,WAAW;CAG9B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAW,YAAW,YAAY;IACjC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQ5E,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,WAAW;CAgBjC;AACD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAgB,YAAW,YAAY;IACtC,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM;IACtD,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW;CAclC"}
@@ -1 +1 @@
1
- {"version":3,"file":"base-endpoint.d.ts","sourceRoot":"","sources":["../../lib/endpoint/base-endpoint.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-endpoint.d.ts","sourceRoot":"","sources":["../../lib/endpoint/base-endpoint.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import type { AuthProvider } from '../auth';
2
2
  import { EndpointCall, EndpointConfig } from '../endpoint/base-endpoint';
3
3
  import { ClientOptions, HTTPMethod, HTTPStatusCodeNumber, RequestOptions, ResponseSchema, StandardSchemaV1 } from '../types';
4
4
  /**
5
- * HTTP client with built-in retry logic, authentication, and interceptors.
5
+ * HTTP client with built-in authentication, and interceptors.
6
6
  * Supports multiple base URLs, type-safe requests, and comprehensive error handling.
7
7
  *
8
8
  * @example
@@ -10,7 +10,6 @@ import { ClientOptions, HTTPMethod, HTTPStatusCodeNumber, RequestOptions, Respon
10
10
  * const client = new HttpClient({
11
11
  * baseUrls: { default: 'https://api.example.com' },
12
12
  * headers: { 'Content-Type': 'application/json' },
13
- * retry: { maxRetries: 3, baseDelayMs: 1000 },
14
13
  * timeout: { requestTimeoutMs: 30000 }
15
14
  * });
16
15
  *
@@ -22,7 +21,7 @@ export declare class HttpClient {
22
21
  private baseUrls;
23
22
  private headers;
24
23
  private interceptors;
25
- private retry;
24
+ private retryPolicy;
26
25
  private timeoutMs?;
27
26
  private auth;
28
27
  private logger;
@@ -46,16 +45,6 @@ export declare class HttpClient {
46
45
  */
47
46
  setAuth(auth: AuthProvider): void;
48
47
  private resolveBaseUrl;
49
- /**
50
- * Sleep for a specified duration (used for retry backoff).
51
- * @private
52
- */
53
- private sleep;
54
- /**
55
- * Execute a function with retry logic and exponential backoff.
56
- * @private
57
- */
58
- private withRetry;
59
48
  /**
60
49
  * Run all registered before-request hooks.
61
50
  * @private
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../lib/http/http-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../lib/http/http-client.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -5,7 +5,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
5
5
  * Use this when you don't need authentication.
6
6
  */
7
7
  var NoAuth = class {
8
- async apply() {}
8
+ async apply(_req) {}
9
9
  };
10
10
  /**
11
11
  * API Key authentication provider.
@@ -539,7 +539,7 @@ var ConsoleMetricsCollector = class {
539
539
  //#endregion
540
540
  //#region lib/http/http-client.ts
541
541
  /**
542
- * HTTP client with built-in retry logic, authentication, and interceptors.
542
+ * HTTP client with built-in authentication, and interceptors.
543
543
  * Supports multiple base URLs, type-safe requests, and comprehensive error handling.
544
544
  *
545
545
  * @example
@@ -547,7 +547,6 @@ var ConsoleMetricsCollector = class {
547
547
  * const client = new HttpClient({
548
548
  * baseUrls: { default: 'https://api.example.com' },
549
549
  * headers: { 'Content-Type': 'application/json' },
550
- * retry: { maxRetries: 3, baseDelayMs: 1000 },
551
550
  * timeout: { requestTimeoutMs: 30000 }
552
551
  * });
553
552
  *
@@ -569,19 +568,12 @@ var HttpClient = class {
569
568
  this.baseUrls = opts.baseUrls;
570
569
  this.headers = opts.headers ?? { "Content-Type": "application/json" };
571
570
  this.interceptors = opts.interceptors ?? {};
572
- this.retry = opts.retry ?? {
573
- maxRetries: 2,
574
- baseDelayMs: 250,
575
- jitter: .2,
576
- retryMethods: ["GET", "HEAD"]
571
+ this.retryPolicy = opts.retry ?? {
572
+ maxAttempts: 0,
573
+ baseDelayMs: 1e3
577
574
  };
578
- if (!this.retry.retryStatusCodes) this.retry.retryStatusCodes = Object.keys(HTTPStatusCode).filter((key) => {
579
- const code = HTTPStatusCode[key];
580
- return typeof code === "number" && code >= 500;
581
- });
582
- if (this.retry.maxRetries < 0) throw new Error("retry.maxRetries must be non-negative");
583
- if (this.retry.baseDelayMs < 0) throw new Error("retry.baseDelayMs must be non-negative");
584
- if (this.retry.jitter !== void 0 && (this.retry.jitter < 0 || this.retry.jitter > 1)) throw new Error("retry.jitter must be between 0 and 1");
575
+ if (!Number.isFinite(this.retryPolicy.maxAttempts) || this.retryPolicy.maxAttempts < 0) throw new Error("retry.maxAttempts must be a non-negative finite number");
576
+ if (this.retryPolicy.baseDelayMs < 0) throw new Error("retry.baseDelayMs must be non-negative");
585
577
  this.timeoutMs = opts.timeout?.requestTimeoutMs;
586
578
  if (this.timeoutMs !== void 0 && this.timeoutMs < 0) throw new Error("timeout.requestTimeoutMs must be non-negative");
587
579
  this.auth = opts["auth"] ?? new NoAuth();
@@ -611,33 +603,6 @@ var HttpClient = class {
611
603
  return url.replace(/\/$/, "");
612
604
  }
613
605
  /**
614
- * Sleep for a specified duration (used for retry backoff).
615
- * @private
616
- */
617
- sleep(ms) {
618
- return new Promise((res) => setTimeout(res, ms));
619
- }
620
- /**
621
- * Execute a function with retry logic and exponential backoff.
622
- * @private
623
- */
624
- async withRetry(fn, canRetry) {
625
- let attempt = 0;
626
- const { maxRetries, baseDelayMs, jitter = .2 } = this.retry;
627
- while (true) try {
628
- return await fn();
629
- } catch (err) {
630
- if (attempt >= maxRetries || !canRetry({
631
- attempt,
632
- error: err
633
- })) throw err;
634
- const backoff = baseDelayMs * 2 ** attempt;
635
- const j = 1 + (Math.random() * 2 - 1) * jitter;
636
- await this.sleep(backoff * j);
637
- attempt++;
638
- }
639
- }
640
- /**
641
606
  * Run all registered before-request hooks.
642
607
  * @private
643
608
  */
@@ -729,6 +694,7 @@ var HttpClient = class {
729
694
  if (init.__urlOverride) url = init.__urlOverride;
730
695
  await this.runBeforeHooks(url, init);
731
696
  let refreshAttempted = false;
697
+ let retryAttempt = 0;
732
698
  const doFetch = async () => {
733
699
  while (true) {
734
700
  let timeoutId;
@@ -739,7 +705,10 @@ var HttpClient = class {
739
705
  }, this.timeoutMs);
740
706
  try {
741
707
  if (refreshAttempted && !options?.skipAuth) {
742
- const freshInit = { ...init };
708
+ const freshInit = {
709
+ ...init,
710
+ headers: typeof init.headers === "object" && !(init.headers instanceof Headers) && !Array.isArray(init.headers) ? { ...init.headers } : init.headers
711
+ };
743
712
  await this.auth.apply({
744
713
  url,
745
714
  init: freshInit,
@@ -758,6 +727,41 @@ var HttpClient = class {
758
727
  }
759
728
  const status = res.status;
760
729
  const contentType = res.headers.get("content-type") || "";
730
+ if (!res.ok) {
731
+ if (!options?.skipRetry && this.retryPolicy.maxAttempts > 0 && retryAttempt < this.retryPolicy.maxAttempts && this.retryPolicy.retryStatusCodes?.includes(status) && this.retryPolicy.retryMethods?.includes(method)) {
732
+ let shouldRetry = true;
733
+ if (this.retryPolicy.shouldRetry) shouldRetry = await this.retryPolicy.shouldRetry({
734
+ url,
735
+ method,
736
+ status,
737
+ attempt: retryAttempt,
738
+ response: res.clone()
739
+ });
740
+ if (shouldRetry) {
741
+ retryAttempt++;
742
+ let delay = this.retryPolicy.baseDelayMs * 2 ** (retryAttempt - 1);
743
+ if (this.retryPolicy.respectRetryAfter) {
744
+ const retryAfter = res.headers.get("Retry-After") || res.headers.get("retry-after");
745
+ if (retryAfter) {
746
+ delay = parseInt(retryAfter, 10) * 1e3;
747
+ this.logger.warn(`Request failed with status ${status}. Retrying after ${delay}ms due to Retry-After header...`, {
748
+ method,
749
+ url,
750
+ status,
751
+ retryAttempt: retryAttempt + 1
752
+ });
753
+ }
754
+ } else this.logger.warn(`Request failed with status ${status}. Retrying attempt ${retryAttempt} after ${delay}ms...`, {
755
+ method,
756
+ url,
757
+ status,
758
+ retryAttempt
759
+ });
760
+ await new Promise((resolve) => setTimeout(resolve, delay));
761
+ continue;
762
+ }
763
+ }
764
+ }
761
765
  let data;
762
766
  if (contentType.includes("json")) data = await res.json();
763
767
  else if (contentType.includes("application/octet-stream") || contentType.includes("application/pdf") || contentType.includes("image/") || contentType.includes("video/") || contentType.includes("audio/") || contentType.startsWith("application/zip") || contentType.startsWith("application/x-")) data = await res.blob();
@@ -804,18 +808,7 @@ var HttpClient = class {
804
808
  }
805
809
  }
806
810
  };
807
- const canRetry = ({ error }) => {
808
- if (error && typeof error === "object" && "name" in error) {
809
- const errorName = error.name;
810
- if (errorName === "AbortError" || errorName === "TimeoutError") return false;
811
- }
812
- if (error instanceof ApiError && error.status) {
813
- if (this.retry.retryStatusCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) return true;
814
- }
815
- return false;
816
- };
817
- if (options?.skipRetry || !this.retry.retryMethods?.includes(method)) return doFetch();
818
- return this.withRetry(doFetch, canRetry);
811
+ return doFetch();
819
812
  }
820
813
  /**
821
814
  * Convenience method for GET requests.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../lib/auth.ts","../lib/types.ts","../lib/validation.ts","../lib/endpoint/base-endpoint.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts"],"sourcesContent":["import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n *\n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n *\n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply() {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n *\n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n *\n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n const value = this.opts.value;\n if (this.opts.header) {\n if (init.headers instanceof Headers) {\n init.headers.set(this.opts.header, value);\n } else if (Array.isArray(init.headers)) {\n init.headers.push([this.opts.header, value]);\n } else {\n init.headers = { ...init.headers, [this.opts.header]: value };\n }\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, value);\n init.__urlOverride = u.toString();\n }\n }\n}\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n *\n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n *\n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) {}\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n const authHeader = `Bearer ${token}`;\n if (init.headers instanceof Headers) {\n init.headers.set('Authorization', authHeader);\n } else if (Array.isArray(init.headers)) {\n init.headers.push(['Authorization', authHeader]);\n } else {\n init.headers = { ...init.headers, Authorization: authHeader };\n }\n }\n}\n","import { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\n// ========================\n// Standard Schema v1 Types\n// ========================\n\n/** The Standard Schema interface for validation libraries (Zod, Valibot, ArkType, etc.) */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1.Props<Input, Output>;\n}\n\nexport declare namespace StandardSchemaV1 {\n /** The Standard Schema properties interface. */\n export interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output> | undefined;\n }\n\n /** The result interface of the validate function. */\n export type Result<Output> = SuccessResult<Output> | FailureResult;\n\n /** The result interface if validation succeeds. */\n export interface SuccessResult<Output> {\n readonly value: Output;\n readonly issues?: undefined;\n }\n\n /** The result interface if validation fails. */\n export interface FailureResult {\n readonly issues: ReadonlyArray<Issue>;\n }\n\n /** The issue interface of the failure output. */\n export interface Issue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;\n }\n\n /** The path segment interface of the issue. */\n export interface PathSegment {\n readonly key: PropertyKey;\n }\n\n /** The Standard types interface. */\n export interface Types<Input = unknown, Output = Input> {\n readonly input: Input;\n readonly output: Output;\n }\n\n /** Infers the input type of a Standard Schema. */\n export type InferInput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['input'];\n\n /** Infers the output type of a Standard Schema. */\n export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['output'];\n}\n\n/** Infers the input type of a Standard Schema. */\nexport type InferInput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferInput<Schema>;\n\n/** Infers the output type of a Standard Schema. */\nexport type InferOutput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferOutput<Schema>;\n\n/** A schema map where keys are HTTP status codes and values are Standard Schema validators. */\nexport type SchemaMap = Record<number, StandardSchemaV1>;\n\n/** A Standard Schema or a map of status codes to schemas. */\nexport type ResponseSchema = StandardSchemaV1 | SchemaMap;\n\nexport type Dictionary<T> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n *\n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n} & Record<string, string>;\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n *\n * @example\n * ```ts\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT']\n * }\n * ```\n */\nexport type RetryStrategy = {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** HTTP status codes eligible for retry */\n retryStatusCodes?: HTTPStatusCodeKey[];\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\nexport const HTTPStatusCode = {\n // 1xx — Informational\n CONTINUE: 100,\n SWITCHING_PROTOCOLS: 101,\n PROCESSING: 102,\n EARLY_HINTS: 103,\n\n // 2xx — Success\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NON_AUTHORITATIVE_INFORMATION: 203,\n NO_CONTENT: 204,\n RESET_CONTENT: 205,\n PARTIAL_CONTENT: 206,\n MULTI_STATUS: 207,\n ALREADY_REPORTED: 208,\n IM_USED: 226,\n\n // 3xx — Redirection\n MULTIPLE_CHOICES: 300,\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n SEE_OTHER: 303,\n NOT_MODIFIED: 304,\n USE_PROXY: 305,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // 4xx — Client Errors\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n LENGTH_REQUIRED: 411,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n URI_TOO_LONG: 414,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n EXPECTATION_FAILED: 417,\n IM_A_TEAPOT: 418,\n MISDIRECTED_REQUEST: 421,\n UNPROCESSABLE_ENTITY: 422,\n LOCKED: 423,\n FAILED_DEPENDENCY: 424,\n TOO_EARLY: 425,\n UPGRADE_REQUIRED: 426,\n PRECONDITION_REQUIRED: 428,\n TOO_MANY_REQUESTS: 429,\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n\n // 5xx — Server Errors\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n VARIANT_ALSO_NEGOTIATES: 506,\n INSUFFICIENT_STORAGE: 507,\n LOOP_DETECTED: 508,\n NOT_EXTENDED: 510,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n} as const;\n\nexport type HTTPStatusCodeKey = keyof typeof HTTPStatusCode;\nexport type HTTPStatusCodeNumber = (typeof HTTPStatusCode)[HTTPStatusCodeKey];\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n *\n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryStrategy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n /**\n * Callback to handle 401 Unauthorized responses.\n * Return true to retry the request (e.g. after refreshing tokens),\n * or false to return the 401 response as is.\n */\n onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n}\n\n/** Result of a safe parse operation using Standard Schema. */\nexport type SafeParseResult<T> =\n | { success: true; data: T }\n | { success: false; issues: ReadonlyArray<StandardSchemaV1.Issue> };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n *\n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n /** Validation issues from Standard Schema-compatible libraries (Zod, Valibot, ArkType, etc.) */\n public validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n\n constructor(\n message: string,\n options?: {\n status?: number;\n cause?: unknown;\n details?: unknown;\n validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause;\n this.validationIssues = options?.validationIssues;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has validationIssues)\n */\n isValidationError(): boolean {\n return !!this.validationIssues && this.validationIssues.length > 0;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n validationIssues: this.validationIssues,\n stack: this.stack,\n };\n }\n}\n\n/**\n * Error thrown when an endpoint receives a response with a status code\n * that has no defined schema in the endpoint configuration.\n */\nexport class SchemaDefinitionError extends Error {\n constructor(public status: number) {\n super(`No schema defined for status code ${status}`);\n this.name = 'SchemaDefinitionError';\n\n // Maintains proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SchemaDefinitionError);\n }\n }\n}\n\n/** Generic paginated response type. */\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Options that can be passed to individual requests to override defaults.\n *\n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n /** Skip authentication for this request */\n skipAuth?: boolean;\n /** Skip retry logic for this request */\n skipRetry?: boolean;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n *\n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import { ApiError, SafeParseResult, StandardSchemaV1 } from './types';\n\n/**\n * Safely parse/validate data with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.).\n * Returns a result object with success status and data or issues.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Result object with success flag and data/issues\n *\n * @example\n * ```ts\n * // Works with Zod\n * import { z } from 'zod';\n * const result = await safeParse(z.object({ name: z.string() }), userData);\n *\n * // Works with Valibot\n * import * as v from 'valibot';\n * const result = await safeParse(v.object({ name: v.string() }), userData);\n *\n * // Works with ArkType\n * import { type } from 'arktype';\n * const result = await safeParse(type({ name: 'string' }), userData);\n *\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.issues);\n * }\n * ```\n */\nexport async function safeParse<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<SafeParseResult<StandardSchemaV1.InferOutput<T>>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n return { success: false, issues: result.issues };\n }\n\n return { success: true, data: result.value as StandardSchemaV1.InferOutput<T> };\n}\n\n/**\n * Parse/validate data with any Standard Schema-compatible library, throwing an ApiError on failure.\n * Use this when you want to fail fast on invalid data.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails (with validationIssues property)\n *\n * @example\n * ```ts\n * // Works with any Standard Schema-compatible library\n * try {\n * const user = await parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.isValidationError()) {\n * console.error('Validation failed:', error.validationIssues);\n * }\n * }\n * ```\n */\nexport async function parseOrThrow<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<StandardSchemaV1.InferOutput<T>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n const messages = result.issues.map((issue) => issue.message).join(', ');\n throw new ApiError(`Validation failed: ${messages}`, {\n validationIssues: result.issues,\n });\n }\n\n return result.value as StandardSchemaV1.InferOutput<T>;\n}\n\n/**\n * Type guard to check if a value is a Standard Schema-compatible validator.\n * Handles both object-based schemas (Zod, Valibot) and function-based schemas (ArkType).\n *\n * @param value - Value to check\n * @returns True if the value implements Standard Schema v1\n */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (value === null || value === undefined) return false;\n\n // Standard Schema can be implemented on objects or functions (ArkType uses functions)\n if (typeof value !== 'object' && typeof value !== 'function') return false;\n\n const schema = value as StandardSchemaV1;\n return (\n '~standard' in schema &&\n typeof schema['~standard'] === 'object' &&\n schema['~standard'] !== null &&\n schema['~standard'].version === 1 &&\n typeof schema['~standard'].validate === 'function'\n );\n}\n","import { HttpClient } from '../http/http-client';\nimport {\n HTTPMethod,\n ResponseSchema,\n SchemaDefinitionError,\n SchemaMap,\n StandardSchemaV1,\n} from '../types';\nimport { isStandardSchema, parseOrThrow } from '../validation';\n\nexport type EndpointConfig<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n method: keyof typeof HTTPMethod;\n path: string | ((params: StandardSchemaV1.InferOutput<Exclude<PathSchema, undefined>>) => string);\n response: ResSchema;\n request?: ReqSchema;\n query?: QuerySchema;\n pathParams?: PathSchema;\n mustHeaderKeys?: MustHeaderKeys;\n advanced?: {\n baseUrlKey?: string;\n skipAuth?: boolean;\n skipRequestValidation?: boolean;\n skipResponseValidation?: boolean;\n skipRetry?: boolean;\n };\n description?: string;\n};\n\n// Helper type to create required headers from mustHeaderKeys\ntype RequiredHeaders<Keys extends readonly string[]> = Keys extends readonly []\n ? Record<string, string> | undefined\n : { [K in Keys[number]]: string } & Record<string, string>;\n\nexport type EndpointCallParams<\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n data?: ReqSchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<ReqSchema> : never;\n query?: QuerySchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<QuerySchema> : never;\n pathParams?: PathSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferInput<PathSchema>\n : never;\n signal?: globalThis.AbortSignal;\n} & (MustHeaderKeys extends readonly []\n ? { headers?: Record<string, string> }\n : { headers: RequiredHeaders<MustHeaderKeys> });\n\n// Helper to extract the response type from a schema which might be a single schema or a status map\ntype InferResponse<S> = S extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<S>\n : S extends SchemaMap\n ? { [K in keyof S]: S[K] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S[K]> : never }[keyof S]\n : never;\n\nexport type EndpointCall<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = (\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n) => Promise<InferResponse<ResSchema>>;\n\nexport class EndpointImpl<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> {\n constructor(\n private client: HttpClient,\n private config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ) {}\n\n async call(\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): Promise<InferResponse<ResSchema>> {\n const { data, query, pathParams, signal } = params;\n const headers = 'headers' in params ? params.headers : undefined;\n\n const skipRequestValidation = this.config.advanced?.skipRequestValidation ?? false;\n const skipResponseValidation = this.config.advanced?.skipResponseValidation ?? false;\n\n // Validate required headers\n if (this.config.mustHeaderKeys && this.config.mustHeaderKeys.length > 0) {\n const missingHeaders = this.config.mustHeaderKeys.filter(\n (key) => !headers || !(key in headers)\n );\n if (missingHeaders.length > 0) {\n throw new Error(`Missing required header(s): ${missingHeaders.join(', ')}`);\n }\n }\n\n // Validate Request Body using Standard Schema\n if (!skipRequestValidation && this.config.request && data !== undefined) {\n await parseOrThrow(this.config.request, data);\n }\n\n // Validate Query Params using Standard Schema\n if (!skipRequestValidation && this.config.query && query !== undefined) {\n await parseOrThrow(this.config.query, query);\n }\n\n // Validate Path Params using Standard Schema\n if (!skipRequestValidation && this.config.pathParams && pathParams !== undefined) {\n await parseOrThrow(this.config.pathParams, pathParams);\n }\n\n // Check for missing required params\n if (this.config.request && data === undefined) {\n throw new Error('Missing required request body (data)');\n }\n if (this.config.pathParams && pathParams === undefined) {\n throw new Error('Missing required path parameters (pathParams)');\n }\n\n // Resolve Path\n let pathStr: string;\n if (typeof this.config.path === 'function') {\n if (!pathParams) throw new Error('Path function requires pathParams');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pathStr = this.config.path(pathParams as any);\n } else {\n pathStr = this.config.path;\n }\n\n const { data: responseData, status } = await this.client.request(\n this.config.method,\n pathStr,\n data,\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: query as any,\n headers,\n baseUrlKey: this.config.advanced?.baseUrlKey,\n skipAuth: this.config.advanced?.skipAuth,\n skipRetry: this.config.advanced?.skipRetry,\n signal,\n }\n );\n\n // Handle Response Validation\n const schema = this.config.response;\n if (skipResponseValidation) {\n return responseData as InferResponse<ResSchema>;\n }\n\n if (isStandardSchema(schema)) {\n // Single schema for all success codes\n return (await parseOrThrow(schema, responseData)) as InferResponse<ResSchema>;\n }\n\n // Map of status codes to schemas\n const schemaMap = schema as SchemaMap;\n const specificSchema = schemaMap[status];\n if (!specificSchema) {\n // No schema defined for this status code\n throw new SchemaDefinitionError(status);\n }\n\n return (await parseOrThrow(specificSchema, responseData)) as InferResponse<ResSchema>;\n }\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n *\n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) {}\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n *\n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n\n // Calculate stats in a single pass to avoid stack overflow with spread operator\n let sum = 0;\n let min = Infinity;\n let max = -Infinity;\n\n for (const m of this.metrics) {\n const d = m.durationMs;\n sum += d;\n if (d < min) min = d;\n if (d > max) max = d;\n }\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: min === Infinity ? 0 : min,\n maxDurationMs: max === -Infinity ? 0 : max,\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { EndpointCall, EndpointConfig, EndpointImpl } from '../endpoint/base-endpoint';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n HTTPStatusCode,\n HTTPStatusCodeKey,\n HTTPStatusCodeNumber,\n Interceptors,\n RequestOptions,\n ResponseSchema,\n RetryStrategy,\n StandardSchemaV1,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in retry logic, authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n *\n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n *\n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retry: RetryStrategy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n private onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n\n /**\n * Creates a new HTTP client instance.\n *\n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n this.retry = opts.retry ?? {\n maxRetries: 2,\n baseDelayMs: 250,\n jitter: 0.2,\n retryMethods: ['GET', 'HEAD'],\n };\n\n // Default retryStatusCodes if not provided\n if (!this.retry.retryStatusCodes) {\n this.retry.retryStatusCodes = (\n Object.keys(HTTPStatusCode) as HTTPStatusCodeKey[]\n ).filter((key) => {\n const code = HTTPStatusCode[key];\n return typeof code === 'number' && code >= 500;\n });\n }\n\n // Validate retry configuration\n if (this.retry.maxRetries < 0) {\n throw new Error('retry.maxRetries must be non-negative');\n }\n if (this.retry.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n if (this.retry.jitter !== undefined && (this.retry.jitter < 0 || this.retry.jitter > 1)) {\n throw new Error('retry.jitter must be between 0 and 1');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n this.onUnauthenticated = opts.onUnauthenticated;\n }\n\n /**\n * Set or update the authentication provider.\n *\n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Sleep for a specified duration (used for retry backoff).\n * @private\n */\n private sleep(ms: number) {\n return new Promise((res) => setTimeout(res, ms));\n }\n\n /**\n * Execute a function with retry logic and exponential backoff.\n * @private\n */\n private async withRetry<T>(\n fn: () => Promise<T>,\n canRetry: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean\n ): Promise<T> {\n let attempt = 0;\n const { maxRetries, baseDelayMs, jitter = 0.2 } = this.retry;\n while (true) {\n try {\n return await fn();\n } catch (err: unknown) {\n if (attempt >= maxRetries || !canRetry({ attempt, error: err })) throw err;\n const backoff = baseDelayMs * 2 ** attempt;\n const j = 1 + (Math.random() * 2 - 1) * jitter;\n await this.sleep(backoff * j);\n attempt++;\n }\n }\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Get all configured base URLs.\n *\n * @returns Object mapping base URL keys to their resolved URLs\n */\n public getBaseUrls() {\n return this.baseUrls;\n }\n\n /**\n * Get the resolved base URL for a given key.\n *\n * @param key - Base URL key (defaults to 'default' if not provided)\n * @returns Resolved base URL string\n */\n public getBaseUrl(key: string) {\n return this.resolveBaseUrl(key);\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n *\n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n *\n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n\n // Handle FormData, Blob, ArrayBuffer: pass through without stringifying\n let requestBody: BodyInit | undefined;\n if (body != null) {\n if (body instanceof FormData) {\n requestBody = body;\n // Remove Content-Type header so browser can set it with proper boundary\n delete headers['Content-Type'];\n } else if (body instanceof Blob || body instanceof ArrayBuffer) {\n requestBody = body;\n } else if (headers['Content-Type']?.includes('json')) {\n requestBody = JSON.stringify(body);\n } else {\n requestBody = String(body);\n }\n }\n\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body: requestBody,\n signal,\n };\n\n if (!options?.skipAuth) {\n await this.auth.apply({ url, init, options });\n }\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n // Track refresh attempts to prevent infinite loops\n let refreshAttempted = false;\n\n const doFetch = async () => {\n // Loop for potential token refresh retry\n while (true) {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n // Re-apply auth headers if this is a retry (unless skipAuth is set)\n if (refreshAttempted && !options?.skipAuth) {\n const freshInit = { ...init };\n // We need to re-run apply to get new token\n await this.auth.apply({ url, init: freshInit, options });\n // Update headers with potentially new token\n init.headers = freshInit.headers;\n }\n\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n\n if (res.status === 401 && this.onUnauthenticated && !refreshAttempted) {\n const shouldRetry = await this.onUnauthenticated(res.clone() as unknown as Response);\n if (shouldRetry) {\n refreshAttempted = true;\n // Clear timeout before retrying\n if (timeoutId) clearTimeout(timeoutId);\n continue;\n }\n }\n\n const status = res.status as HTTPStatusCodeNumber;\n const contentType = res.headers.get('content-type') || '';\n \n // Handle different response types appropriately\n let data: any;\n if (contentType.includes('json')) {\n data = await res.json();\n } else if (\n contentType.includes('application/octet-stream') ||\n contentType.includes('application/pdf') ||\n contentType.includes('image/') ||\n contentType.includes('video/') ||\n contentType.includes('audio/') ||\n contentType.startsWith('application/zip') ||\n contentType.startsWith('application/x-')\n ) {\n // Return binary data as Blob for file downloads\n data = await res.blob();\n } else {\n data = await res.text();\n }\n \n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, status: status };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n }\n };\n\n const canRetry = ({ error }: { error?: unknown }) => {\n // Don't retry timeouts or aborts\n if (error && typeof error === 'object' && 'name' in error) {\n const errorName = (error as { name?: string }).name;\n if (errorName === 'AbortError' || errorName === 'TimeoutError') return false;\n }\n // Retry on network errors or configured status codes\n if (error instanceof ApiError && error.status) {\n const retryCodes = this.retry.retryStatusCodes;\n if (retryCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) {\n return true;\n }\n }\n return false;\n };\n\n if (options?.skipRetry || !this.retry.retryMethods?.includes(method)) {\n return doFetch();\n }\n\n return this.withRetry(doFetch, canRetry);\n }\n\n /**\n * Convenience method for GET requests.\n *\n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n *\n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n *\n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n *\n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n *\n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n\n /**\n * Create a strongly-typed endpoint builder.\n * Works with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.)\n *\n * @param config - Endpoint configuration with schemas\n * @returns Endpoint call function\n *\n * @example\n * ```ts\n * // With Zod\n * import { z } from 'zod';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: z.object({ id: z.string(), name: z.string() }),\n * pathParams: z.object({ id: z.string() }),\n * });\n *\n * // With Valibot\n * import * as v from 'valibot';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: v.object({ id: v.string(), name: v.string() }),\n * pathParams: v.object({ id: v.string() }),\n * });\n * ```\n */\n createEndpoint<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n >(\n config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): EndpointCall<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys> {\n const endpoint = new EndpointImpl(this, config);\n return (params) => endpoint.call(params);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../lib/auth.ts","../lib/types.ts","../lib/validation.ts","../lib/endpoint/base-endpoint.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts"],"sourcesContent":["import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n *\n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n *\n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply(_req: AuthContext) {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n *\n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n *\n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n const value = this.opts.value;\n if (this.opts.header) {\n if (init.headers instanceof Headers) {\n init.headers.set(this.opts.header, value);\n } else if (Array.isArray(init.headers)) {\n init.headers.push([this.opts.header, value]);\n } else {\n init.headers = { ...init.headers, [this.opts.header]: value };\n }\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, value);\n init.__urlOverride = u.toString();\n }\n }\n}\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n *\n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n *\n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) {}\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n const authHeader = `Bearer ${token}`;\n if (init.headers instanceof Headers) {\n init.headers.set('Authorization', authHeader);\n } else if (Array.isArray(init.headers)) {\n init.headers.push(['Authorization', authHeader]);\n } else {\n init.headers = { ...init.headers, Authorization: authHeader };\n }\n }\n}\n","import { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\n// ========================\n// Standard Schema v1 Types\n// ========================\n\n/** The Standard Schema interface for validation libraries (Zod, Valibot, ArkType, etc.) */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1.Props<Input, Output>;\n}\n\nexport declare namespace StandardSchemaV1 {\n /** The Standard Schema properties interface. */\n export interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output> | undefined;\n }\n\n /** The result interface of the validate function. */\n export type Result<Output> = SuccessResult<Output> | FailureResult;\n\n /** The result interface if validation succeeds. */\n export interface SuccessResult<Output> {\n readonly value: Output;\n readonly issues?: undefined;\n }\n\n /** The result interface if validation fails. */\n export interface FailureResult {\n readonly issues: ReadonlyArray<Issue>;\n }\n\n /** The issue interface of the failure output. */\n export interface Issue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;\n }\n\n /** The path segment interface of the issue. */\n export interface PathSegment {\n readonly key: PropertyKey;\n }\n\n /** The Standard types interface. */\n export interface Types<Input = unknown, Output = Input> {\n readonly input: Input;\n readonly output: Output;\n }\n\n /** Infers the input type of a Standard Schema. */\n export type InferInput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['input'];\n\n /** Infers the output type of a Standard Schema. */\n export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['output'];\n}\n\n/** Infers the input type of a Standard Schema. */\nexport type InferInput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferInput<Schema>;\n\n/** Infers the output type of a Standard Schema. */\nexport type InferOutput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferOutput<Schema>;\n\n/** A schema map where keys are HTTP status codes and values are Standard Schema validators. */\nexport type SchemaMap = Record<number, StandardSchemaV1>;\n\n/** A Standard Schema or a map of status codes to schemas. */\nexport type ResponseSchema = StandardSchemaV1 | SchemaMap;\n\nexport type Dictionary<T> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n *\n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n} & Record<string, string>;\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n *\n * @example\n * ```ts\n * {\n * maxAttempts: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT'],\n * retryStatusCodes: [500, 502, 503, 504],\n * retryNetworkErrors: true,\n * respectRetryAfter: true,\n * }\n * ```\n */\nexport type RetryPolicy = {\n /** Maximum number of retry attempts */\n maxAttempts: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** HTTP status codes eligible for retry */\n retryStatusCodes?: HTTPStatusCodeNumber[];\n /** Whether to respect 'Retry-After' header if present in the response */\n respectRetryAfter?: boolean;\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: {\n url: string;\n method: HttpMethod;\n status: HTTPStatusCodeNumber;\n attempt: number;\n response?: Response;\n }) => Promise<boolean> | boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\nexport const HTTPStatusCode = {\n // 1xx — Informational\n CONTINUE: 100,\n SWITCHING_PROTOCOLS: 101,\n PROCESSING: 102,\n EARLY_HINTS: 103,\n\n // 2xx — Success\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NON_AUTHORITATIVE_INFORMATION: 203,\n NO_CONTENT: 204,\n RESET_CONTENT: 205,\n PARTIAL_CONTENT: 206,\n MULTI_STATUS: 207,\n ALREADY_REPORTED: 208,\n IM_USED: 226,\n\n // 3xx — Redirection\n MULTIPLE_CHOICES: 300,\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n SEE_OTHER: 303,\n NOT_MODIFIED: 304,\n USE_PROXY: 305,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // 4xx — Client Errors\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n LENGTH_REQUIRED: 411,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n URI_TOO_LONG: 414,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n EXPECTATION_FAILED: 417,\n IM_A_TEAPOT: 418,\n MISDIRECTED_REQUEST: 421,\n UNPROCESSABLE_ENTITY: 422,\n LOCKED: 423,\n FAILED_DEPENDENCY: 424,\n TOO_EARLY: 425,\n UPGRADE_REQUIRED: 426,\n PRECONDITION_REQUIRED: 428,\n TOO_MANY_REQUESTS: 429,\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n\n // 5xx — Server Errors\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n VARIANT_ALSO_NEGOTIATES: 506,\n INSUFFICIENT_STORAGE: 507,\n LOOP_DETECTED: 508,\n NOT_EXTENDED: 510,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n} as const;\n\nexport type HTTPStatusCodeKey = keyof typeof HTTPStatusCode;\nexport type HTTPStatusCodeNumber = (typeof HTTPStatusCode)[HTTPStatusCodeKey];\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n *\n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxAttempts: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryPolicy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n /**\n * Callback to handle 401 Unauthorized responses.\n * Return true to retry the request (e.g. after refreshing tokens),\n * or false to return the 401 response as is.\n */\n onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n}\n\n/** Result of a safe parse operation using Standard Schema. */\nexport type SafeParseResult<T> =\n | { success: true; data: T }\n | { success: false; issues: ReadonlyArray<StandardSchemaV1.Issue> };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n *\n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n /** Validation issues from Standard Schema-compatible libraries (Zod, Valibot, ArkType, etc.) */\n public validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n\n constructor(\n message: string,\n options?: {\n status?: number;\n cause?: unknown;\n details?: unknown;\n validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause;\n this.validationIssues = options?.validationIssues;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has validationIssues)\n */\n isValidationError(): boolean {\n return !!this.validationIssues && this.validationIssues.length > 0;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n validationIssues: this.validationIssues,\n stack: this.stack,\n };\n }\n}\n\n/**\n * Error thrown when an endpoint receives a response with a status code\n * that has no defined schema in the endpoint configuration.\n */\nexport class SchemaDefinitionError extends Error {\n constructor(public status: number) {\n super(`No schema defined for status code ${status}`);\n this.name = 'SchemaDefinitionError';\n\n // Maintains proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SchemaDefinitionError);\n }\n }\n}\n\n/** Generic paginated response type. */\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Options that can be passed to individual requests to override defaults.\n *\n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n /** Skip authentication for this request */\n skipAuth?: boolean;\n /** Skip retry logic for this request */\n skipRetry?: boolean;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n *\n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import { ApiError, SafeParseResult, StandardSchemaV1 } from './types';\n\n/**\n * Safely parse/validate data with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.).\n * Returns a result object with success status and data or issues.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Result object with success flag and data/issues\n *\n * @example\n * ```ts\n * // Works with Zod\n * import { z } from 'zod';\n * const result = await safeParse(z.object({ name: z.string() }), userData);\n *\n * // Works with Valibot\n * import * as v from 'valibot';\n * const result = await safeParse(v.object({ name: v.string() }), userData);\n *\n * // Works with ArkType\n * import { type } from 'arktype';\n * const result = await safeParse(type({ name: 'string' }), userData);\n *\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.issues);\n * }\n * ```\n */\nexport async function safeParse<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<SafeParseResult<StandardSchemaV1.InferOutput<T>>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n return { success: false, issues: result.issues };\n }\n\n return { success: true, data: result.value as StandardSchemaV1.InferOutput<T> };\n}\n\n/**\n * Parse/validate data with any Standard Schema-compatible library, throwing an ApiError on failure.\n * Use this when you want to fail fast on invalid data.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails (with validationIssues property)\n *\n * @example\n * ```ts\n * // Works with any Standard Schema-compatible library\n * try {\n * const user = await parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.isValidationError()) {\n * console.error('Validation failed:', error.validationIssues);\n * }\n * }\n * ```\n */\nexport async function parseOrThrow<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<StandardSchemaV1.InferOutput<T>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n const messages = result.issues.map((issue) => issue.message).join(', ');\n throw new ApiError(`Validation failed: ${messages}`, {\n validationIssues: result.issues,\n });\n }\n\n return result.value as StandardSchemaV1.InferOutput<T>;\n}\n\n/**\n * Type guard to check if a value is a Standard Schema-compatible validator.\n * Handles both object-based schemas (Zod, Valibot) and function-based schemas (ArkType).\n *\n * @param value - Value to check\n * @returns True if the value implements Standard Schema v1\n */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (value === null || value === undefined) return false;\n\n // Standard Schema can be implemented on objects or functions (ArkType uses functions)\n if (typeof value !== 'object' && typeof value !== 'function') return false;\n\n const schema = value as StandardSchemaV1;\n return (\n '~standard' in schema &&\n typeof schema['~standard'] === 'object' &&\n schema['~standard'] !== null &&\n schema['~standard'].version === 1 &&\n typeof schema['~standard'].validate === 'function'\n );\n}\n","import { HttpClient } from '../http/http-client';\nimport {\n HTTPMethod,\n ResponseSchema,\n SchemaDefinitionError,\n SchemaMap,\n StandardSchemaV1,\n} from '../types';\nimport { isStandardSchema, parseOrThrow } from '../validation';\n\nexport type EndpointConfig<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n method: keyof typeof HTTPMethod;\n path: string | ((params: StandardSchemaV1.InferOutput<Exclude<PathSchema, undefined>>) => string);\n response: ResSchema;\n request?: ReqSchema;\n query?: QuerySchema;\n pathParams?: PathSchema;\n mustHeaderKeys?: MustHeaderKeys;\n advanced?: {\n baseUrlKey?: string;\n skipAuth?: boolean;\n skipRequestValidation?: boolean;\n skipResponseValidation?: boolean;\n skipRetry?: boolean;\n };\n description?: string;\n};\n\n// Helper type to create required headers from mustHeaderKeys\ntype RequiredHeaders<Keys extends readonly string[]> = Keys extends readonly []\n ? Record<string, string> | undefined\n : { [K in Keys[number]]: string } & Record<string, string>;\n\nexport type EndpointCallParams<\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n data?: ReqSchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<ReqSchema> : never;\n query?: QuerySchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<QuerySchema> : never;\n pathParams?: PathSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferInput<PathSchema>\n : never;\n signal?: globalThis.AbortSignal;\n} & (MustHeaderKeys extends readonly []\n ? { headers?: Record<string, string> }\n : { headers: RequiredHeaders<MustHeaderKeys> });\n\n// Helper to extract the response type from a schema which might be a single schema or a status map\ntype InferResponse<S> = S extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<S>\n : S extends SchemaMap\n ? {\n [K in keyof S]: S[K] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S[K]> : never;\n }[keyof S]\n : never;\n\nexport type EndpointCall<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = (\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n) => Promise<InferResponse<ResSchema>>;\n\nexport class EndpointImpl<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> {\n constructor(\n private client: HttpClient,\n private config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ) {}\n\n async call(\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): Promise<InferResponse<ResSchema>> {\n const { data, query, pathParams, signal } = params;\n const headers = 'headers' in params ? params.headers : undefined;\n\n const skipRequestValidation = this.config.advanced?.skipRequestValidation ?? false;\n const skipResponseValidation = this.config.advanced?.skipResponseValidation ?? false;\n\n // Validate required headers\n if (this.config.mustHeaderKeys && this.config.mustHeaderKeys.length > 0) {\n const missingHeaders = this.config.mustHeaderKeys.filter(\n (key) => !headers || !(key in headers)\n );\n if (missingHeaders.length > 0) {\n throw new Error(`Missing required header(s): ${missingHeaders.join(', ')}`);\n }\n }\n\n // Validate Request Body using Standard Schema\n if (!skipRequestValidation && this.config.request && data !== undefined) {\n await parseOrThrow(this.config.request, data);\n }\n\n // Validate Query Params using Standard Schema\n if (!skipRequestValidation && this.config.query && query !== undefined) {\n await parseOrThrow(this.config.query, query);\n }\n\n // Validate Path Params using Standard Schema\n if (!skipRequestValidation && this.config.pathParams && pathParams !== undefined) {\n await parseOrThrow(this.config.pathParams, pathParams);\n }\n\n // Check for missing required params\n if (this.config.request && data === undefined) {\n throw new Error('Missing required request body (data)');\n }\n if (this.config.pathParams && pathParams === undefined) {\n throw new Error('Missing required path parameters (pathParams)');\n }\n\n // Resolve Path\n let pathStr: string;\n if (typeof this.config.path === 'function') {\n if (!pathParams) throw new Error('Path function requires pathParams');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pathStr = this.config.path(pathParams as any);\n } else {\n pathStr = this.config.path;\n }\n\n const { data: responseData, status } = await this.client.request(\n this.config.method,\n pathStr,\n data,\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: query as any,\n headers,\n baseUrlKey: this.config.advanced?.baseUrlKey,\n skipAuth: this.config.advanced?.skipAuth,\n skipRetry: this.config.advanced?.skipRetry,\n signal,\n }\n );\n\n // Handle Response Validation\n const schema = this.config.response;\n if (skipResponseValidation) {\n return responseData as InferResponse<ResSchema>;\n }\n\n if (isStandardSchema(schema)) {\n // Single schema for all success codes\n return (await parseOrThrow(schema, responseData)) as InferResponse<ResSchema>;\n }\n\n // Map of status codes to schemas\n const schemaMap = schema as SchemaMap;\n const specificSchema = schemaMap[status];\n if (!specificSchema) {\n // No schema defined for this status code\n throw new SchemaDefinitionError(status);\n }\n\n return (await parseOrThrow(specificSchema, responseData)) as InferResponse<ResSchema>;\n }\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n *\n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) {}\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n *\n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n\n // Calculate stats in a single pass to avoid stack overflow with spread operator\n let sum = 0;\n let min = Infinity;\n let max = -Infinity;\n\n for (const m of this.metrics) {\n const d = m.durationMs;\n sum += d;\n if (d < min) min = d;\n if (d > max) max = d;\n }\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: min === Infinity ? 0 : min,\n maxDurationMs: max === -Infinity ? 0 : max,\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { EndpointCall, EndpointConfig, EndpointImpl } from '../endpoint/base-endpoint';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n HTTPStatusCodeNumber,\n Interceptors,\n RequestOptions,\n ResponseSchema,\n RetryPolicy,\n StandardSchemaV1,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n *\n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n *\n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retryPolicy: RetryPolicy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n private onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n\n /**\n * Creates a new HTTP client instance.\n *\n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n\n this.retryPolicy = opts.retry ?? { maxAttempts: 0, baseDelayMs: 1000 };\n\n // Validate retry policy\n if (!Number.isFinite(this.retryPolicy.maxAttempts) || this.retryPolicy.maxAttempts < 0) {\n throw new Error('retry.maxAttempts must be a non-negative finite number');\n }\n if (this.retryPolicy.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n this.onUnauthenticated = opts.onUnauthenticated;\n }\n\n /**\n * Set or update the authentication provider.\n *\n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Get all configured base URLs.\n *\n * @returns Object mapping base URL keys to their resolved URLs\n */\n public getBaseUrls() {\n return this.baseUrls;\n }\n\n /**\n * Get the resolved base URL for a given key.\n *\n * @param key - Base URL key (defaults to 'default' if not provided)\n * @returns Resolved base URL string\n */\n public getBaseUrl(key: string) {\n return this.resolveBaseUrl(key);\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n *\n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n *\n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n\n // Handle FormData, Blob, ArrayBuffer: pass through without stringifying\n let requestBody: BodyInit | undefined;\n if (body != null) {\n if (body instanceof FormData) {\n requestBody = body;\n // Remove Content-Type header so browser can set it with proper boundary\n delete headers['Content-Type'];\n } else if (body instanceof Blob || body instanceof ArrayBuffer) {\n requestBody = body;\n } else if (headers['Content-Type']?.includes('json')) {\n requestBody = JSON.stringify(body);\n } else {\n requestBody = String(body);\n }\n }\n\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body: requestBody,\n signal,\n };\n\n if (!options?.skipAuth) {\n await this.auth.apply({ url, init, options });\n }\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n // Track refresh attempts to prevent infinite loops\n let refreshAttempted = false;\n let retryAttempt = 0;\n\n const doFetch = async () => {\n // Loop for potential token refresh retry\n while (true) {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n // Re-apply auth headers if this is a retry (unless skipAuth is set)\n if (refreshAttempted && !options?.skipAuth) {\n const freshInit = {\n ...init,\n headers:\n typeof init.headers === 'object' &&\n !(init.headers instanceof Headers) &&\n !Array.isArray(init.headers)\n ? { ...(init.headers as Record<string, string>) }\n : init.headers,\n };\n // We need to re-run apply to get new token\n await this.auth.apply({ url, init: freshInit, options });\n // Update headers with potentially new token\n init.headers = freshInit.headers;\n }\n\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n\n if (res.status === 401 && this.onUnauthenticated && !refreshAttempted) {\n const shouldRetry = await this.onUnauthenticated(res.clone() as unknown as Response);\n if (shouldRetry) {\n refreshAttempted = true;\n // Clear timeout before retrying\n if (timeoutId) clearTimeout(timeoutId);\n continue;\n }\n }\n\n const status = res.status as HTTPStatusCodeNumber;\n const contentType = res.headers.get('content-type') || '';\n\n // Check for errors first\n if (!res.ok) {\n if (\n !options?.skipRetry &&\n this.retryPolicy.maxAttempts > 0 &&\n retryAttempt < this.retryPolicy.maxAttempts &&\n this.retryPolicy.retryStatusCodes?.includes(status) &&\n this.retryPolicy.retryMethods?.includes(method)\n ) {\n let shouldRetry = true;\n if (this.retryPolicy.shouldRetry) {\n shouldRetry = await this.retryPolicy.shouldRetry({\n url,\n method,\n status,\n attempt: retryAttempt,\n response: res.clone() as unknown as Response,\n });\n }\n if (shouldRetry) {\n retryAttempt++;\n let delay = this.retryPolicy.baseDelayMs * 2 ** (retryAttempt - 1);\n if (this.retryPolicy.respectRetryAfter) {\n const retryAfter =\n res.headers.get('Retry-After') || res.headers.get('retry-after');\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n this.logger.warn(\n `Request failed with status ${status}. Retrying after ${delay}ms due to Retry-After header...`,\n { method, url, status, retryAttempt: retryAttempt + 1 }\n );\n }\n } else {\n this.logger.warn(\n `Request failed with status ${status}. Retrying attempt ${retryAttempt} after ${delay}ms...`,\n { method, url, status, retryAttempt }\n );\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n }\n }\n\n // Handle different response types appropriately\n let data: any;\n if (contentType.includes('json')) {\n data = await res.json();\n } else if (\n contentType.includes('application/octet-stream') ||\n contentType.includes('application/pdf') ||\n contentType.includes('image/') ||\n contentType.includes('video/') ||\n contentType.includes('audio/') ||\n contentType.startsWith('application/zip') ||\n contentType.startsWith('application/x-')\n ) {\n // Return binary data as Blob for file downloads\n data = await res.blob();\n } else {\n data = await res.text();\n }\n\n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, status: status };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n }\n };\n\n return doFetch();\n }\n\n /**\n * Convenience method for GET requests.\n *\n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n *\n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n *\n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n *\n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n *\n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n\n /**\n * Create a strongly-typed endpoint builder.\n * Works with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.)\n *\n * @param config - Endpoint configuration with schemas\n * @returns Endpoint call function\n *\n * @example\n * ```ts\n * // With Zod\n * import { z } from 'zod';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: z.object({ id: z.string(), name: z.string() }),\n * pathParams: z.object({ id: z.string() }),\n * });\n *\n * // With Valibot\n * import * as v from 'valibot';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: v.object({ id: v.string(), name: v.string() }),\n * pathParams: v.object({ id: v.string() }),\n * });\n * ```\n */\n createEndpoint<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n >(\n config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): EndpointCall<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys> {\n const endpoint = new EndpointImpl(this, config);\n return (params) => endpoint.call(params);\n }\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Use this when you don't need authentication.
5
5
  */
6
6
  var NoAuth = class {
7
- async apply() {}
7
+ async apply(_req) {}
8
8
  };
9
9
  /**
10
10
  * API Key authentication provider.
@@ -538,7 +538,7 @@ var ConsoleMetricsCollector = class {
538
538
  //#endregion
539
539
  //#region lib/http/http-client.ts
540
540
  /**
541
- * HTTP client with built-in retry logic, authentication, and interceptors.
541
+ * HTTP client with built-in authentication, and interceptors.
542
542
  * Supports multiple base URLs, type-safe requests, and comprehensive error handling.
543
543
  *
544
544
  * @example
@@ -546,7 +546,6 @@ var ConsoleMetricsCollector = class {
546
546
  * const client = new HttpClient({
547
547
  * baseUrls: { default: 'https://api.example.com' },
548
548
  * headers: { 'Content-Type': 'application/json' },
549
- * retry: { maxRetries: 3, baseDelayMs: 1000 },
550
549
  * timeout: { requestTimeoutMs: 30000 }
551
550
  * });
552
551
  *
@@ -568,19 +567,12 @@ var HttpClient = class {
568
567
  this.baseUrls = opts.baseUrls;
569
568
  this.headers = opts.headers ?? { "Content-Type": "application/json" };
570
569
  this.interceptors = opts.interceptors ?? {};
571
- this.retry = opts.retry ?? {
572
- maxRetries: 2,
573
- baseDelayMs: 250,
574
- jitter: .2,
575
- retryMethods: ["GET", "HEAD"]
570
+ this.retryPolicy = opts.retry ?? {
571
+ maxAttempts: 0,
572
+ baseDelayMs: 1e3
576
573
  };
577
- if (!this.retry.retryStatusCodes) this.retry.retryStatusCodes = Object.keys(HTTPStatusCode).filter((key) => {
578
- const code = HTTPStatusCode[key];
579
- return typeof code === "number" && code >= 500;
580
- });
581
- if (this.retry.maxRetries < 0) throw new Error("retry.maxRetries must be non-negative");
582
- if (this.retry.baseDelayMs < 0) throw new Error("retry.baseDelayMs must be non-negative");
583
- if (this.retry.jitter !== void 0 && (this.retry.jitter < 0 || this.retry.jitter > 1)) throw new Error("retry.jitter must be between 0 and 1");
574
+ if (!Number.isFinite(this.retryPolicy.maxAttempts) || this.retryPolicy.maxAttempts < 0) throw new Error("retry.maxAttempts must be a non-negative finite number");
575
+ if (this.retryPolicy.baseDelayMs < 0) throw new Error("retry.baseDelayMs must be non-negative");
584
576
  this.timeoutMs = opts.timeout?.requestTimeoutMs;
585
577
  if (this.timeoutMs !== void 0 && this.timeoutMs < 0) throw new Error("timeout.requestTimeoutMs must be non-negative");
586
578
  this.auth = opts["auth"] ?? new NoAuth();
@@ -610,33 +602,6 @@ var HttpClient = class {
610
602
  return url.replace(/\/$/, "");
611
603
  }
612
604
  /**
613
- * Sleep for a specified duration (used for retry backoff).
614
- * @private
615
- */
616
- sleep(ms) {
617
- return new Promise((res) => setTimeout(res, ms));
618
- }
619
- /**
620
- * Execute a function with retry logic and exponential backoff.
621
- * @private
622
- */
623
- async withRetry(fn, canRetry) {
624
- let attempt = 0;
625
- const { maxRetries, baseDelayMs, jitter = .2 } = this.retry;
626
- while (true) try {
627
- return await fn();
628
- } catch (err) {
629
- if (attempt >= maxRetries || !canRetry({
630
- attempt,
631
- error: err
632
- })) throw err;
633
- const backoff = baseDelayMs * 2 ** attempt;
634
- const j = 1 + (Math.random() * 2 - 1) * jitter;
635
- await this.sleep(backoff * j);
636
- attempt++;
637
- }
638
- }
639
- /**
640
605
  * Run all registered before-request hooks.
641
606
  * @private
642
607
  */
@@ -728,6 +693,7 @@ var HttpClient = class {
728
693
  if (init.__urlOverride) url = init.__urlOverride;
729
694
  await this.runBeforeHooks(url, init);
730
695
  let refreshAttempted = false;
696
+ let retryAttempt = 0;
731
697
  const doFetch = async () => {
732
698
  while (true) {
733
699
  let timeoutId;
@@ -738,7 +704,10 @@ var HttpClient = class {
738
704
  }, this.timeoutMs);
739
705
  try {
740
706
  if (refreshAttempted && !options?.skipAuth) {
741
- const freshInit = { ...init };
707
+ const freshInit = {
708
+ ...init,
709
+ headers: typeof init.headers === "object" && !(init.headers instanceof Headers) && !Array.isArray(init.headers) ? { ...init.headers } : init.headers
710
+ };
742
711
  await this.auth.apply({
743
712
  url,
744
713
  init: freshInit,
@@ -757,6 +726,41 @@ var HttpClient = class {
757
726
  }
758
727
  const status = res.status;
759
728
  const contentType = res.headers.get("content-type") || "";
729
+ if (!res.ok) {
730
+ if (!options?.skipRetry && this.retryPolicy.maxAttempts > 0 && retryAttempt < this.retryPolicy.maxAttempts && this.retryPolicy.retryStatusCodes?.includes(status) && this.retryPolicy.retryMethods?.includes(method)) {
731
+ let shouldRetry = true;
732
+ if (this.retryPolicy.shouldRetry) shouldRetry = await this.retryPolicy.shouldRetry({
733
+ url,
734
+ method,
735
+ status,
736
+ attempt: retryAttempt,
737
+ response: res.clone()
738
+ });
739
+ if (shouldRetry) {
740
+ retryAttempt++;
741
+ let delay = this.retryPolicy.baseDelayMs * 2 ** (retryAttempt - 1);
742
+ if (this.retryPolicy.respectRetryAfter) {
743
+ const retryAfter = res.headers.get("Retry-After") || res.headers.get("retry-after");
744
+ if (retryAfter) {
745
+ delay = parseInt(retryAfter, 10) * 1e3;
746
+ this.logger.warn(`Request failed with status ${status}. Retrying after ${delay}ms due to Retry-After header...`, {
747
+ method,
748
+ url,
749
+ status,
750
+ retryAttempt: retryAttempt + 1
751
+ });
752
+ }
753
+ } else this.logger.warn(`Request failed with status ${status}. Retrying attempt ${retryAttempt} after ${delay}ms...`, {
754
+ method,
755
+ url,
756
+ status,
757
+ retryAttempt
758
+ });
759
+ await new Promise((resolve) => setTimeout(resolve, delay));
760
+ continue;
761
+ }
762
+ }
763
+ }
760
764
  let data;
761
765
  if (contentType.includes("json")) data = await res.json();
762
766
  else if (contentType.includes("application/octet-stream") || contentType.includes("application/pdf") || contentType.includes("image/") || contentType.includes("video/") || contentType.includes("audio/") || contentType.startsWith("application/zip") || contentType.startsWith("application/x-")) data = await res.blob();
@@ -803,18 +807,7 @@ var HttpClient = class {
803
807
  }
804
808
  }
805
809
  };
806
- const canRetry = ({ error }) => {
807
- if (error && typeof error === "object" && "name" in error) {
808
- const errorName = error.name;
809
- if (errorName === "AbortError" || errorName === "TimeoutError") return false;
810
- }
811
- if (error instanceof ApiError && error.status) {
812
- if (this.retry.retryStatusCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) return true;
813
- }
814
- return false;
815
- };
816
- if (options?.skipRetry || !this.retry.retryMethods?.includes(method)) return doFetch();
817
- return this.withRetry(doFetch, canRetry);
810
+ return doFetch();
818
811
  }
819
812
  /**
820
813
  * Convenience method for GET requests.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../lib/auth.ts","../lib/types.ts","../lib/validation.ts","../lib/endpoint/base-endpoint.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts"],"sourcesContent":["import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n *\n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n *\n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply() {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n *\n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n *\n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n const value = this.opts.value;\n if (this.opts.header) {\n if (init.headers instanceof Headers) {\n init.headers.set(this.opts.header, value);\n } else if (Array.isArray(init.headers)) {\n init.headers.push([this.opts.header, value]);\n } else {\n init.headers = { ...init.headers, [this.opts.header]: value };\n }\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, value);\n init.__urlOverride = u.toString();\n }\n }\n}\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n *\n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n *\n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) {}\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n const authHeader = `Bearer ${token}`;\n if (init.headers instanceof Headers) {\n init.headers.set('Authorization', authHeader);\n } else if (Array.isArray(init.headers)) {\n init.headers.push(['Authorization', authHeader]);\n } else {\n init.headers = { ...init.headers, Authorization: authHeader };\n }\n }\n}\n","import { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\n// ========================\n// Standard Schema v1 Types\n// ========================\n\n/** The Standard Schema interface for validation libraries (Zod, Valibot, ArkType, etc.) */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1.Props<Input, Output>;\n}\n\nexport declare namespace StandardSchemaV1 {\n /** The Standard Schema properties interface. */\n export interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output> | undefined;\n }\n\n /** The result interface of the validate function. */\n export type Result<Output> = SuccessResult<Output> | FailureResult;\n\n /** The result interface if validation succeeds. */\n export interface SuccessResult<Output> {\n readonly value: Output;\n readonly issues?: undefined;\n }\n\n /** The result interface if validation fails. */\n export interface FailureResult {\n readonly issues: ReadonlyArray<Issue>;\n }\n\n /** The issue interface of the failure output. */\n export interface Issue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;\n }\n\n /** The path segment interface of the issue. */\n export interface PathSegment {\n readonly key: PropertyKey;\n }\n\n /** The Standard types interface. */\n export interface Types<Input = unknown, Output = Input> {\n readonly input: Input;\n readonly output: Output;\n }\n\n /** Infers the input type of a Standard Schema. */\n export type InferInput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['input'];\n\n /** Infers the output type of a Standard Schema. */\n export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['output'];\n}\n\n/** Infers the input type of a Standard Schema. */\nexport type InferInput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferInput<Schema>;\n\n/** Infers the output type of a Standard Schema. */\nexport type InferOutput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferOutput<Schema>;\n\n/** A schema map where keys are HTTP status codes and values are Standard Schema validators. */\nexport type SchemaMap = Record<number, StandardSchemaV1>;\n\n/** A Standard Schema or a map of status codes to schemas. */\nexport type ResponseSchema = StandardSchemaV1 | SchemaMap;\n\nexport type Dictionary<T> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n *\n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n} & Record<string, string>;\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n *\n * @example\n * ```ts\n * {\n * maxRetries: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT']\n * }\n * ```\n */\nexport type RetryStrategy = {\n /** Maximum number of retry attempts */\n maxRetries: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** HTTP status codes eligible for retry */\n retryStatusCodes?: HTTPStatusCodeKey[];\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\nexport const HTTPStatusCode = {\n // 1xx — Informational\n CONTINUE: 100,\n SWITCHING_PROTOCOLS: 101,\n PROCESSING: 102,\n EARLY_HINTS: 103,\n\n // 2xx — Success\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NON_AUTHORITATIVE_INFORMATION: 203,\n NO_CONTENT: 204,\n RESET_CONTENT: 205,\n PARTIAL_CONTENT: 206,\n MULTI_STATUS: 207,\n ALREADY_REPORTED: 208,\n IM_USED: 226,\n\n // 3xx — Redirection\n MULTIPLE_CHOICES: 300,\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n SEE_OTHER: 303,\n NOT_MODIFIED: 304,\n USE_PROXY: 305,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // 4xx — Client Errors\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n LENGTH_REQUIRED: 411,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n URI_TOO_LONG: 414,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n EXPECTATION_FAILED: 417,\n IM_A_TEAPOT: 418,\n MISDIRECTED_REQUEST: 421,\n UNPROCESSABLE_ENTITY: 422,\n LOCKED: 423,\n FAILED_DEPENDENCY: 424,\n TOO_EARLY: 425,\n UPGRADE_REQUIRED: 426,\n PRECONDITION_REQUIRED: 428,\n TOO_MANY_REQUESTS: 429,\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n\n // 5xx — Server Errors\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n VARIANT_ALSO_NEGOTIATES: 506,\n INSUFFICIENT_STORAGE: 507,\n LOOP_DETECTED: 508,\n NOT_EXTENDED: 510,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n} as const;\n\nexport type HTTPStatusCodeKey = keyof typeof HTTPStatusCode;\nexport type HTTPStatusCodeNumber = (typeof HTTPStatusCode)[HTTPStatusCodeKey];\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n *\n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryStrategy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n /**\n * Callback to handle 401 Unauthorized responses.\n * Return true to retry the request (e.g. after refreshing tokens),\n * or false to return the 401 response as is.\n */\n onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n}\n\n/** Result of a safe parse operation using Standard Schema. */\nexport type SafeParseResult<T> =\n | { success: true; data: T }\n | { success: false; issues: ReadonlyArray<StandardSchemaV1.Issue> };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n *\n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n /** Validation issues from Standard Schema-compatible libraries (Zod, Valibot, ArkType, etc.) */\n public validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n\n constructor(\n message: string,\n options?: {\n status?: number;\n cause?: unknown;\n details?: unknown;\n validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause;\n this.validationIssues = options?.validationIssues;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has validationIssues)\n */\n isValidationError(): boolean {\n return !!this.validationIssues && this.validationIssues.length > 0;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n validationIssues: this.validationIssues,\n stack: this.stack,\n };\n }\n}\n\n/**\n * Error thrown when an endpoint receives a response with a status code\n * that has no defined schema in the endpoint configuration.\n */\nexport class SchemaDefinitionError extends Error {\n constructor(public status: number) {\n super(`No schema defined for status code ${status}`);\n this.name = 'SchemaDefinitionError';\n\n // Maintains proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SchemaDefinitionError);\n }\n }\n}\n\n/** Generic paginated response type. */\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Options that can be passed to individual requests to override defaults.\n *\n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n /** Skip authentication for this request */\n skipAuth?: boolean;\n /** Skip retry logic for this request */\n skipRetry?: boolean;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n *\n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import { ApiError, SafeParseResult, StandardSchemaV1 } from './types';\n\n/**\n * Safely parse/validate data with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.).\n * Returns a result object with success status and data or issues.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Result object with success flag and data/issues\n *\n * @example\n * ```ts\n * // Works with Zod\n * import { z } from 'zod';\n * const result = await safeParse(z.object({ name: z.string() }), userData);\n *\n * // Works with Valibot\n * import * as v from 'valibot';\n * const result = await safeParse(v.object({ name: v.string() }), userData);\n *\n * // Works with ArkType\n * import { type } from 'arktype';\n * const result = await safeParse(type({ name: 'string' }), userData);\n *\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.issues);\n * }\n * ```\n */\nexport async function safeParse<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<SafeParseResult<StandardSchemaV1.InferOutput<T>>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n return { success: false, issues: result.issues };\n }\n\n return { success: true, data: result.value as StandardSchemaV1.InferOutput<T> };\n}\n\n/**\n * Parse/validate data with any Standard Schema-compatible library, throwing an ApiError on failure.\n * Use this when you want to fail fast on invalid data.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails (with validationIssues property)\n *\n * @example\n * ```ts\n * // Works with any Standard Schema-compatible library\n * try {\n * const user = await parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.isValidationError()) {\n * console.error('Validation failed:', error.validationIssues);\n * }\n * }\n * ```\n */\nexport async function parseOrThrow<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<StandardSchemaV1.InferOutput<T>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n const messages = result.issues.map((issue) => issue.message).join(', ');\n throw new ApiError(`Validation failed: ${messages}`, {\n validationIssues: result.issues,\n });\n }\n\n return result.value as StandardSchemaV1.InferOutput<T>;\n}\n\n/**\n * Type guard to check if a value is a Standard Schema-compatible validator.\n * Handles both object-based schemas (Zod, Valibot) and function-based schemas (ArkType).\n *\n * @param value - Value to check\n * @returns True if the value implements Standard Schema v1\n */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (value === null || value === undefined) return false;\n\n // Standard Schema can be implemented on objects or functions (ArkType uses functions)\n if (typeof value !== 'object' && typeof value !== 'function') return false;\n\n const schema = value as StandardSchemaV1;\n return (\n '~standard' in schema &&\n typeof schema['~standard'] === 'object' &&\n schema['~standard'] !== null &&\n schema['~standard'].version === 1 &&\n typeof schema['~standard'].validate === 'function'\n );\n}\n","import { HttpClient } from '../http/http-client';\nimport {\n HTTPMethod,\n ResponseSchema,\n SchemaDefinitionError,\n SchemaMap,\n StandardSchemaV1,\n} from '../types';\nimport { isStandardSchema, parseOrThrow } from '../validation';\n\nexport type EndpointConfig<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n method: keyof typeof HTTPMethod;\n path: string | ((params: StandardSchemaV1.InferOutput<Exclude<PathSchema, undefined>>) => string);\n response: ResSchema;\n request?: ReqSchema;\n query?: QuerySchema;\n pathParams?: PathSchema;\n mustHeaderKeys?: MustHeaderKeys;\n advanced?: {\n baseUrlKey?: string;\n skipAuth?: boolean;\n skipRequestValidation?: boolean;\n skipResponseValidation?: boolean;\n skipRetry?: boolean;\n };\n description?: string;\n};\n\n// Helper type to create required headers from mustHeaderKeys\ntype RequiredHeaders<Keys extends readonly string[]> = Keys extends readonly []\n ? Record<string, string> | undefined\n : { [K in Keys[number]]: string } & Record<string, string>;\n\nexport type EndpointCallParams<\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n data?: ReqSchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<ReqSchema> : never;\n query?: QuerySchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<QuerySchema> : never;\n pathParams?: PathSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferInput<PathSchema>\n : never;\n signal?: globalThis.AbortSignal;\n} & (MustHeaderKeys extends readonly []\n ? { headers?: Record<string, string> }\n : { headers: RequiredHeaders<MustHeaderKeys> });\n\n// Helper to extract the response type from a schema which might be a single schema or a status map\ntype InferResponse<S> = S extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<S>\n : S extends SchemaMap\n ? { [K in keyof S]: S[K] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S[K]> : never }[keyof S]\n : never;\n\nexport type EndpointCall<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = (\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n) => Promise<InferResponse<ResSchema>>;\n\nexport class EndpointImpl<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> {\n constructor(\n private client: HttpClient,\n private config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ) {}\n\n async call(\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): Promise<InferResponse<ResSchema>> {\n const { data, query, pathParams, signal } = params;\n const headers = 'headers' in params ? params.headers : undefined;\n\n const skipRequestValidation = this.config.advanced?.skipRequestValidation ?? false;\n const skipResponseValidation = this.config.advanced?.skipResponseValidation ?? false;\n\n // Validate required headers\n if (this.config.mustHeaderKeys && this.config.mustHeaderKeys.length > 0) {\n const missingHeaders = this.config.mustHeaderKeys.filter(\n (key) => !headers || !(key in headers)\n );\n if (missingHeaders.length > 0) {\n throw new Error(`Missing required header(s): ${missingHeaders.join(', ')}`);\n }\n }\n\n // Validate Request Body using Standard Schema\n if (!skipRequestValidation && this.config.request && data !== undefined) {\n await parseOrThrow(this.config.request, data);\n }\n\n // Validate Query Params using Standard Schema\n if (!skipRequestValidation && this.config.query && query !== undefined) {\n await parseOrThrow(this.config.query, query);\n }\n\n // Validate Path Params using Standard Schema\n if (!skipRequestValidation && this.config.pathParams && pathParams !== undefined) {\n await parseOrThrow(this.config.pathParams, pathParams);\n }\n\n // Check for missing required params\n if (this.config.request && data === undefined) {\n throw new Error('Missing required request body (data)');\n }\n if (this.config.pathParams && pathParams === undefined) {\n throw new Error('Missing required path parameters (pathParams)');\n }\n\n // Resolve Path\n let pathStr: string;\n if (typeof this.config.path === 'function') {\n if (!pathParams) throw new Error('Path function requires pathParams');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pathStr = this.config.path(pathParams as any);\n } else {\n pathStr = this.config.path;\n }\n\n const { data: responseData, status } = await this.client.request(\n this.config.method,\n pathStr,\n data,\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: query as any,\n headers,\n baseUrlKey: this.config.advanced?.baseUrlKey,\n skipAuth: this.config.advanced?.skipAuth,\n skipRetry: this.config.advanced?.skipRetry,\n signal,\n }\n );\n\n // Handle Response Validation\n const schema = this.config.response;\n if (skipResponseValidation) {\n return responseData as InferResponse<ResSchema>;\n }\n\n if (isStandardSchema(schema)) {\n // Single schema for all success codes\n return (await parseOrThrow(schema, responseData)) as InferResponse<ResSchema>;\n }\n\n // Map of status codes to schemas\n const schemaMap = schema as SchemaMap;\n const specificSchema = schemaMap[status];\n if (!specificSchema) {\n // No schema defined for this status code\n throw new SchemaDefinitionError(status);\n }\n\n return (await parseOrThrow(specificSchema, responseData)) as InferResponse<ResSchema>;\n }\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n *\n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) {}\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n *\n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n\n // Calculate stats in a single pass to avoid stack overflow with spread operator\n let sum = 0;\n let min = Infinity;\n let max = -Infinity;\n\n for (const m of this.metrics) {\n const d = m.durationMs;\n sum += d;\n if (d < min) min = d;\n if (d > max) max = d;\n }\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: min === Infinity ? 0 : min,\n maxDurationMs: max === -Infinity ? 0 : max,\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { EndpointCall, EndpointConfig, EndpointImpl } from '../endpoint/base-endpoint';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n HTTPStatusCode,\n HTTPStatusCodeKey,\n HTTPStatusCodeNumber,\n Interceptors,\n RequestOptions,\n ResponseSchema,\n RetryStrategy,\n StandardSchemaV1,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in retry logic, authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n *\n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * retry: { maxRetries: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n *\n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retry: RetryStrategy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n private onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n\n /**\n * Creates a new HTTP client instance.\n *\n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n this.retry = opts.retry ?? {\n maxRetries: 2,\n baseDelayMs: 250,\n jitter: 0.2,\n retryMethods: ['GET', 'HEAD'],\n };\n\n // Default retryStatusCodes if not provided\n if (!this.retry.retryStatusCodes) {\n this.retry.retryStatusCodes = (\n Object.keys(HTTPStatusCode) as HTTPStatusCodeKey[]\n ).filter((key) => {\n const code = HTTPStatusCode[key];\n return typeof code === 'number' && code >= 500;\n });\n }\n\n // Validate retry configuration\n if (this.retry.maxRetries < 0) {\n throw new Error('retry.maxRetries must be non-negative');\n }\n if (this.retry.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n if (this.retry.jitter !== undefined && (this.retry.jitter < 0 || this.retry.jitter > 1)) {\n throw new Error('retry.jitter must be between 0 and 1');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n this.onUnauthenticated = opts.onUnauthenticated;\n }\n\n /**\n * Set or update the authentication provider.\n *\n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Sleep for a specified duration (used for retry backoff).\n * @private\n */\n private sleep(ms: number) {\n return new Promise((res) => setTimeout(res, ms));\n }\n\n /**\n * Execute a function with retry logic and exponential backoff.\n * @private\n */\n private async withRetry<T>(\n fn: () => Promise<T>,\n canRetry: (ctx: { attempt: number; error?: unknown; response?: Response }) => boolean\n ): Promise<T> {\n let attempt = 0;\n const { maxRetries, baseDelayMs, jitter = 0.2 } = this.retry;\n while (true) {\n try {\n return await fn();\n } catch (err: unknown) {\n if (attempt >= maxRetries || !canRetry({ attempt, error: err })) throw err;\n const backoff = baseDelayMs * 2 ** attempt;\n const j = 1 + (Math.random() * 2 - 1) * jitter;\n await this.sleep(backoff * j);\n attempt++;\n }\n }\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Get all configured base URLs.\n *\n * @returns Object mapping base URL keys to their resolved URLs\n */\n public getBaseUrls() {\n return this.baseUrls;\n }\n\n /**\n * Get the resolved base URL for a given key.\n *\n * @param key - Base URL key (defaults to 'default' if not provided)\n * @returns Resolved base URL string\n */\n public getBaseUrl(key: string) {\n return this.resolveBaseUrl(key);\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n *\n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n *\n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n\n // Handle FormData, Blob, ArrayBuffer: pass through without stringifying\n let requestBody: BodyInit | undefined;\n if (body != null) {\n if (body instanceof FormData) {\n requestBody = body;\n // Remove Content-Type header so browser can set it with proper boundary\n delete headers['Content-Type'];\n } else if (body instanceof Blob || body instanceof ArrayBuffer) {\n requestBody = body;\n } else if (headers['Content-Type']?.includes('json')) {\n requestBody = JSON.stringify(body);\n } else {\n requestBody = String(body);\n }\n }\n\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body: requestBody,\n signal,\n };\n\n if (!options?.skipAuth) {\n await this.auth.apply({ url, init, options });\n }\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n // Track refresh attempts to prevent infinite loops\n let refreshAttempted = false;\n\n const doFetch = async () => {\n // Loop for potential token refresh retry\n while (true) {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n // Re-apply auth headers if this is a retry (unless skipAuth is set)\n if (refreshAttempted && !options?.skipAuth) {\n const freshInit = { ...init };\n // We need to re-run apply to get new token\n await this.auth.apply({ url, init: freshInit, options });\n // Update headers with potentially new token\n init.headers = freshInit.headers;\n }\n\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n\n if (res.status === 401 && this.onUnauthenticated && !refreshAttempted) {\n const shouldRetry = await this.onUnauthenticated(res.clone() as unknown as Response);\n if (shouldRetry) {\n refreshAttempted = true;\n // Clear timeout before retrying\n if (timeoutId) clearTimeout(timeoutId);\n continue;\n }\n }\n\n const status = res.status as HTTPStatusCodeNumber;\n const contentType = res.headers.get('content-type') || '';\n \n // Handle different response types appropriately\n let data: any;\n if (contentType.includes('json')) {\n data = await res.json();\n } else if (\n contentType.includes('application/octet-stream') ||\n contentType.includes('application/pdf') ||\n contentType.includes('image/') ||\n contentType.includes('video/') ||\n contentType.includes('audio/') ||\n contentType.startsWith('application/zip') ||\n contentType.startsWith('application/x-')\n ) {\n // Return binary data as Blob for file downloads\n data = await res.blob();\n } else {\n data = await res.text();\n }\n \n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, status: status };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n }\n };\n\n const canRetry = ({ error }: { error?: unknown }) => {\n // Don't retry timeouts or aborts\n if (error && typeof error === 'object' && 'name' in error) {\n const errorName = (error as { name?: string }).name;\n if (errorName === 'AbortError' || errorName === 'TimeoutError') return false;\n }\n // Retry on network errors or configured status codes\n if (error instanceof ApiError && error.status) {\n const retryCodes = this.retry.retryStatusCodes;\n if (retryCodes?.some((codeKey) => HTTPStatusCode[codeKey] === error.status)) {\n return true;\n }\n }\n return false;\n };\n\n if (options?.skipRetry || !this.retry.retryMethods?.includes(method)) {\n return doFetch();\n }\n\n return this.withRetry(doFetch, canRetry);\n }\n\n /**\n * Convenience method for GET requests.\n *\n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n *\n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n *\n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n *\n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n *\n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n\n /**\n * Create a strongly-typed endpoint builder.\n * Works with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.)\n *\n * @param config - Endpoint configuration with schemas\n * @returns Endpoint call function\n *\n * @example\n * ```ts\n * // With Zod\n * import { z } from 'zod';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: z.object({ id: z.string(), name: z.string() }),\n * pathParams: z.object({ id: z.string() }),\n * });\n *\n * // With Valibot\n * import * as v from 'valibot';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: v.object({ id: v.string(), name: v.string() }),\n * pathParams: v.object({ id: v.string() }),\n * });\n * ```\n */\n createEndpoint<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n >(\n config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): EndpointCall<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys> {\n const endpoint = new EndpointImpl(this, config);\n return (params) => endpoint.call(params);\n }\n}\n"],"mappings":";;;;;AAsCA,IAAa,SAAb,MAA4C;CAC1C,MAAM,QAAQ;;;;;;;;;;;;;;;AAkBhB,IAAa,aAAb,MAAgD;CAC9C,YAAY,MAAkE;AAA1D,OAAA,OAAA;AAClB,MAAI,CAAC,KAAK,UAAU,CAAC,KAAK,MACxB,OAAM,IAAI,MAAM,4DAAwD;AAE1E,MAAI,KAAK,UAAU,KAAK,MACtB,OAAM,IAAI,MAAM,8DAA0D;;CAG9E,MAAM,EAAE,KAAK,QAAqB;EAChC,MAAM,QAAQ,KAAK,KAAK;AACxB,MAAI,KAAK,KAAK,OACZ,KAAI,KAAK,mBAAmB,QAC1B,MAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,MAAM;WAChC,MAAM,QAAQ,KAAK,QAAQ,CACpC,MAAK,QAAQ,KAAK,CAAC,KAAK,KAAK,QAAQ,MAAM,CAAC;MAE5C,MAAK,UAAU;GAAE,GAAG,KAAK;IAAU,KAAK,KAAK,SAAS;GAAO;WAEtD,KAAK,KAAK,OAAO;GAC1B,MAAM,IAAI,IAAI,IAAI,IAAI;AACtB,KAAE,aAAa,IAAI,KAAK,KAAK,OAAO,MAAM;AAC1C,QAAK,gBAAgB,EAAE,UAAU;;;;;;;;;;;;;;;;;;;AAmBvC,IAAa,kBAAb,MAAqD;CACnD,YAAY,UAAkD;AAA1C,OAAA,WAAA;;CACpB,MAAM,MAAM,EAAE,QAAqB;EACjC,MAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,+CAA+C;EAEjE,MAAM,aAAa,UAAU;AAC7B,MAAI,KAAK,mBAAmB,QAC1B,MAAK,QAAQ,IAAI,iBAAiB,WAAW;WACpC,MAAM,QAAQ,KAAK,QAAQ,CACpC,MAAK,QAAQ,KAAK,CAAC,iBAAiB,WAAW,CAAC;MAEhD,MAAK,UAAU;GAAE,GAAG,KAAK;GAAS,eAAe;GAAY;;;;;ACiBnE,MAAa,aAAa;CACxB,KAAK;CACL,MAAM;CACN,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,SAAS;CACV;AAID,MAAa,iBAAiB;CAE5B,UAAU;CACV,qBAAqB;CACrB,YAAY;CACZ,aAAa;CAGb,IAAI;CACJ,SAAS;CACT,UAAU;CACV,+BAA+B;CAC/B,YAAY;CACZ,eAAe;CACf,iBAAiB;CACjB,cAAc;CACd,kBAAkB;CAClB,SAAS;CAGT,kBAAkB;CAClB,mBAAmB;CACnB,OAAO;CACP,WAAW;CACX,cAAc;CACd,WAAW;CACX,oBAAoB;CACpB,oBAAoB;CAGpB,aAAa;CACb,cAAc;CACd,kBAAkB;CAClB,WAAW;CACX,WAAW;CACX,oBAAoB;CACpB,gBAAgB;CAChB,+BAA+B;CAC/B,iBAAiB;CACjB,UAAU;CACV,MAAM;CACN,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;CACd,wBAAwB;CACxB,uBAAuB;CACvB,oBAAoB;CACpB,aAAa;CACb,qBAAqB;CACrB,sBAAsB;CACtB,QAAQ;CACR,mBAAmB;CACnB,WAAW;CACX,kBAAkB;CAClB,uBAAuB;CACvB,mBAAmB;CACnB,iCAAiC;CACjC,+BAA+B;CAG/B,uBAAuB;CACvB,iBAAiB;CACjB,aAAa;CACb,qBAAqB;CACrB,iBAAiB;CACjB,4BAA4B;CAC5B,yBAAyB;CACzB,sBAAsB;CACtB,eAAe;CACf,cAAc;CACd,iCAAiC;CAClC;;;;;;;;;;;;;AA0FD,IAAa,WAAb,MAAa,iBAAiB,MAAM;CAMlC,YACE,SACA,SAMA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS,SAAS;AACvB,OAAK,UAAU,SAAS;AACxB,OAAK,QAAQ,SAAS;AACtB,OAAK,mBAAmB,SAAS;AAGjC,MAAI,MAAM,kBACR,OAAM,kBAAkB,MAAM,SAAS;;;;;CAO3C,oBAA6B;AAC3B,SAAO,CAAC,CAAC,KAAK,oBAAoB,KAAK,iBAAiB,SAAS;;;;;CAMnE,gBAAyB;AACvB,SAAO,CAAC,CAAC,KAAK,UAAU,KAAK,UAAU,OAAO,KAAK,SAAS;;;;;CAM9D,gBAAyB;AACvB,SAAO,CAAC,CAAC,KAAK,UAAU,KAAK,UAAU;;;;;CAMzC,SAAS;AACP,SAAO;GACL,MAAM,KAAK;GACX,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,SAAS,KAAK;GACd,kBAAkB,KAAK;GACvB,OAAO,KAAK;GACb;;;;;;;AAQL,IAAa,wBAAb,MAAa,8BAA8B,MAAM;CAC/C,YAAY,QAAuB;AACjC,QAAM,qCAAqC,SAAS;AADnC,OAAA,SAAA;AAEjB,OAAK,OAAO;AAGZ,MAAI,MAAM,kBACR,OAAM,kBAAkB,MAAM,sBAAsB;;;;;;;;;;;;;;;;AAqD1D,SAAgB,cAAc,GAAqC;AACjE,KAAI,CAAC,EAAG,QAAO;AACf,KAAI,aAAa,iBAAiB;EAChC,MAAM,IAAI,EAAE,UAAU;AACtB,SAAO,IAAI,IAAI,MAAM;;CAEvB,MAAM,SAAS,IAAI,iBAAiB;AACpC,QAAO,QAAQ,EAAE,CAAC,SAAS,CAAC,GAAG,OAAO;AACpC,MAAI,MAAM,KAAA,EACR,QAAO,OAAO,GAAG,OAAO,EAAE,CAAC;GAE7B;CACF,MAAM,IAAI,OAAO,UAAU;AAC3B,QAAO,IAAI,IAAI,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5ZvB,eAAsB,UACpB,QACA,MAC2D;CAC3D,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,KAAK;AAEvD,KAAI,OAAO,OACT,QAAO;EAAE,SAAS;EAAO,QAAQ,OAAO;EAAQ;AAGlD,QAAO;EAAE,SAAS;EAAM,MAAM,OAAO;EAA0C;;;;;;;;;;;;;;;;;;;;;;;;AAyBjF,eAAsB,aACpB,QACA,MAC0C;CAC1C,MAAM,SAAS,MAAM,OAAO,aAAa,SAAS,KAAK;AAEvD,KAAI,OAAO,OAET,OAAM,IAAI,SAAS,sBADF,OAAO,OAAO,KAAK,UAAU,MAAM,QAAQ,CAAC,KAAK,KAAK,IAClB,EACnD,kBAAkB,OAAO,QAC1B,CAAC;AAGJ,QAAO,OAAO;;;;;;;;;AAUhB,SAAgB,iBAAiB,OAA2C;AAC1E,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAGlD,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAY,QAAO;CAErE,MAAM,SAAS;AACf,QACE,eAAe,UACf,OAAO,OAAO,iBAAiB,YAC/B,OAAO,iBAAiB,QACxB,OAAO,aAAa,YAAY,KAChC,OAAO,OAAO,aAAa,aAAa;;;;AC7B5C,IAAa,eAAb,MAME;CACA,YACE,QACA,QACA;AAFQ,OAAA,SAAA;AACA,OAAA,SAAA;;CAGV,MAAM,KACJ,QACmC;EACnC,MAAM,EAAE,MAAM,OAAO,YAAY,WAAW;EAC5C,MAAM,UAAU,aAAa,SAAS,OAAO,UAAU,KAAA;EAEvD,MAAM,wBAAwB,KAAK,OAAO,UAAU,yBAAyB;EAC7E,MAAM,yBAAyB,KAAK,OAAO,UAAU,0BAA0B;AAG/E,MAAI,KAAK,OAAO,kBAAkB,KAAK,OAAO,eAAe,SAAS,GAAG;GACvE,MAAM,iBAAiB,KAAK,OAAO,eAAe,QAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,SAC/B;AACD,OAAI,eAAe,SAAS,EAC1B,OAAM,IAAI,MAAM,+BAA+B,eAAe,KAAK,KAAK,GAAG;;AAK/E,MAAI,CAAC,yBAAyB,KAAK,OAAO,WAAW,SAAS,KAAA,EAC5D,OAAM,aAAa,KAAK,OAAO,SAAS,KAAK;AAI/C,MAAI,CAAC,yBAAyB,KAAK,OAAO,SAAS,UAAU,KAAA,EAC3D,OAAM,aAAa,KAAK,OAAO,OAAO,MAAM;AAI9C,MAAI,CAAC,yBAAyB,KAAK,OAAO,cAAc,eAAe,KAAA,EACrE,OAAM,aAAa,KAAK,OAAO,YAAY,WAAW;AAIxD,MAAI,KAAK,OAAO,WAAW,SAAS,KAAA,EAClC,OAAM,IAAI,MAAM,uCAAuC;AAEzD,MAAI,KAAK,OAAO,cAAc,eAAe,KAAA,EAC3C,OAAM,IAAI,MAAM,gDAAgD;EAIlE,IAAI;AACJ,MAAI,OAAO,KAAK,OAAO,SAAS,YAAY;AAC1C,OAAI,CAAC,WAAY,OAAM,IAAI,MAAM,oCAAoC;AAErE,aAAU,KAAK,OAAO,KAAK,WAAkB;QAE7C,WAAU,KAAK,OAAO;EAGxB,MAAM,EAAE,MAAM,cAAc,WAAW,MAAM,KAAK,OAAO,QACvD,KAAK,OAAO,QACZ,SACA,MACA;GAES;GACP;GACA,YAAY,KAAK,OAAO,UAAU;GAClC,UAAU,KAAK,OAAO,UAAU;GAChC,WAAW,KAAK,OAAO,UAAU;GACjC;GACD,CACF;EAGD,MAAM,SAAS,KAAK,OAAO;AAC3B,MAAI,uBACF,QAAO;AAGT,MAAI,iBAAiB,OAAO,CAE1B,QAAQ,MAAM,aAAa,QAAQ,aAAa;EAKlD,MAAM,iBADY,OACe;AACjC,MAAI,CAAC,eAEH,OAAM,IAAI,sBAAsB,OAAO;AAGzC,SAAQ,MAAM,aAAa,gBAAgB,aAAa;;;;;;;;ACvK5D,IAAY,WAAL,yBAAA,UAAA;AACL,UAAA,WAAA;AACA,UAAA,UAAA;AACA,UAAA,UAAA;AACA,UAAA,WAAA;;KACD;;;;;AAkCD,IAAa,gBAAb,MAA6C;CAC3C,YAAY,WAA6B,SAAS,MAAM;AAApC,OAAA,WAAA;;CAEpB,IAAI,OAAuB;EACzB,MAAM,SAAS;GAAC,SAAS;GAAO,SAAS;GAAM,SAAS;GAAM,SAAS;GAAM;AAI7E,MAHwB,OAAO,QAAQ,MAAM,MAAM,GAC7B,OAAO,QAAQ,KAAK,SAAS,CAEd;EAErC,MAAM,SAAS;GACb,GAAG;GACH,OAAO,MAAM,QACT;IACE,SAAS,MAAM,MAAM;IACrB,OAAO,MAAM,MAAM;IACnB,MAAM,MAAM,MAAM;IACnB,GACD,KAAA;GACL;AAED,UAAQ,MAAM,OAAd;GACE,KAAK,SAAS;AACZ,YAAQ,MAAM,KAAK,UAAU,OAAO,CAAC;AACrC;GACF,KAAK,SAAS;AACZ,YAAQ,KAAK,KAAK,UAAU,OAAO,CAAC;AACpC;GACF,KAAK,SAAS;AACZ,YAAQ,KAAK,KAAK,UAAU,OAAO,CAAC;AACpC;GACF,KAAK,SAAS;AACZ,YAAQ,MAAM,KAAK,UAAU,OAAO,CAAC;AACrC;;;;;;;;AASR,IAAa,aAAb,MAA0C;CACxC,IAAI,QAAwB;;;;;AAQ9B,IAAa,aAAb,MAAwB;CACtB,YAAY,QAAwB;AAAhB,OAAA,SAAA;;CAEpB,MAAM,SAAiB,SAAyC;AAC9D,OAAK,OAAO,IAAI;GACd,OAAO,SAAS;GAChB;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACD,CAAC;;CAGJ,KAAK,SAAiB,SAAyC;AAC7D,OAAK,OAAO,IAAI;GACd,OAAO,SAAS;GAChB;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACD,CAAC;;CAGJ,KAAK,SAAiB,SAAyC;AAC7D,OAAK,OAAO,IAAI;GACd,OAAO,SAAS;GAChB;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACD,CAAC;;CAGJ,MAAM,SAAiB,OAAe,SAAyC;AAC7E,OAAK,OAAO,IAAI;GACd,OAAO,SAAS;GAChB;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACA;GACD,CAAC;;;;;;;;AC9FN,IAAa,uBAAb,MAA8D;CAC5D,QAAQ,UAAgC;;;;;;AAS1C,IAAa,2BAAb,MAAkE;CAIhE,YAAY,aAAa,KAAM;iBAHK,EAAE;AAIpC,OAAK,aAAa;;CAGpB,QAAQ,SAA+B;AACrC,OAAK,QAAQ,KAAK,QAAQ;AAC1B,MAAI,KAAK,QAAQ,SAAS,KAAK,WAC7B,MAAK,QAAQ,OAAO;;;;;CAOxB,aAA+B;AAC7B,SAAO,CAAC,GAAG,KAAK,QAAQ;;;;;CAM1B,aAOE;AACA,MAAI,KAAK,QAAQ,WAAW,EAC1B,QAAO;GACL,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,eAAe;GACf,eAAe;GACf,eAAe;GAChB;EAGH,MAAM,aAAa,KAAK,QAAQ,QAAQ,MAAM,EAAE,QAAQ,CAAC;EAGzD,IAAI,MAAM;EACV,IAAI,MAAM;EACV,IAAI,MAAM;AAEV,OAAK,MAAM,KAAK,KAAK,SAAS;GAC5B,MAAM,IAAI,EAAE;AACZ,UAAO;AACP,OAAI,IAAI,IAAK,OAAM;AACnB,OAAI,IAAI,IAAK,OAAM;;AAGrB,SAAO;GACL,OAAO,KAAK,QAAQ;GACpB;GACA,QAAQ,KAAK,QAAQ,SAAS;GAC9B,eAAe,MAAM,KAAK,QAAQ;GAClC,eAAe,QAAQ,WAAW,IAAI;GACtC,eAAe,QAAQ,YAAY,IAAI;GACxC;;;;;CAMH,QAAc;AACZ,OAAK,UAAU,EAAE;;;;;;AAOrB,IAAa,0BAAb,MAAiE;CAC/D,QAAQ,SAA+B;AACrC,UAAQ,IAAI,aAAa,KAAK,UAAU,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;AC1FrD,IAAa,aAAb,MAAwB;;;;;;;CAkBtB,YAAY,MAAqB;AAC/B,OAAK,YAAY,KAAK,SAAU,WAAW,OAAO,KAAK,WAAW;AAClE,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,6DAA6D;AAG/E,MAAI,CAAC,KAAK,YAAY,OAAO,KAAK,aAAa,SAC7C,OAAM,IAAI,MAAM,kDAAkD;AAEpE,MAAI,CAAC,KAAK,SAAS,QACjB,OAAM,IAAI,MAAM,0CAAwC;AAG1D,OAAK,WAAW,KAAK;AACrB,OAAK,UAAU,KAAK,WAAW,EAAE,gBAAgB,oBAAoB;AACrE,OAAK,eAAe,KAAK,gBAAgB,EAAE;AAC3C,OAAK,QAAQ,KAAK,SAAS;GACzB,YAAY;GACZ,aAAa;GACb,QAAQ;GACR,cAAc,CAAC,OAAO,OAAO;GAC9B;AAGD,MAAI,CAAC,KAAK,MAAM,iBACd,MAAK,MAAM,mBACT,OAAO,KAAK,eAAe,CAC3B,QAAQ,QAAQ;GAChB,MAAM,OAAO,eAAe;AAC5B,UAAO,OAAO,SAAS,YAAY,QAAQ;IAC3C;AAIJ,MAAI,KAAK,MAAM,aAAa,EAC1B,OAAM,IAAI,MAAM,wCAAwC;AAE1D,MAAI,KAAK,MAAM,cAAc,EAC3B,OAAM,IAAI,MAAM,yCAAyC;AAE3D,MAAI,KAAK,MAAM,WAAW,KAAA,MAAc,KAAK,MAAM,SAAS,KAAK,KAAK,MAAM,SAAS,GACnF,OAAM,IAAI,MAAM,uCAAuC;AAGzD,OAAK,YAAY,KAAK,SAAS;AAC/B,MAAI,KAAK,cAAc,KAAA,KAAa,KAAK,YAAY,EACnD,OAAM,IAAI,MAAM,gDAAgD;AAGlE,OAAK,OAAO,KAAK,WAAW,IAAI,QAAQ;AACxC,OAAK,SAAS,IAAI,WAAW,KAAK,UAAU,IAAI,YAAY,CAAC;AAC7D,OAAK,UAAU,KAAK,WAAW,IAAI,sBAAsB;AACzD,OAAK,oBAAoB,KAAK;;;;;;;;;;;CAYhC,QAAQ,MAAoB;AAC1B,OAAK,OAAO;;CAGd,eAAuB,KAAkC;EACvD,MAAM,IAAa,OAAkB;EACrC,MAAM,MAAM,KAAK,SAAS;AAC1B,MAAI,CAAC,KAAK;GACR,MAAM,gBAAgB,OAAO,KAAK,KAAK,SAAS,CAAC,KAAK,KAAK;AAC3D,SAAM,IAAI,MAAM,yBAAyB,EAAE,qBAAqB,gBAAgB;;AAElF,SAAO,IAAI,QAAQ,OAAO,GAAG;;;;;;CAO/B,MAAc,IAAY;AACxB,SAAO,IAAI,SAAS,QAAQ,WAAW,KAAK,GAAG,CAAC;;;;;;CAOlD,MAAc,UACZ,IACA,UACY;EACZ,IAAI,UAAU;EACd,MAAM,EAAE,YAAY,aAAa,SAAS,OAAQ,KAAK;AACvD,SAAO,KACL,KAAI;AACF,UAAO,MAAM,IAAI;WACV,KAAc;AACrB,OAAI,WAAW,cAAc,CAAC,SAAS;IAAE;IAAS,OAAO;IAAK,CAAC,CAAE,OAAM;GACvE,MAAM,UAAU,cAAc,KAAK;GACnC,MAAM,IAAI,KAAK,KAAK,QAAQ,GAAG,IAAI,KAAK;AACxC,SAAM,KAAK,MAAM,UAAU,EAAE;AAC7B;;;;;;;CASN,MAAc,eAAe,KAAa,MAAgD;AACxF,OAAK,MAAM,KAAK,KAAK,aAAa,iBAAiB,EAAE,CACnD,OAAM,EAAE;GAAE;GAAK;GAAM,CAAC;;;;;;CAQ1B,MAAc,cAAc,KAAc,KAAe,QAAkB;AACzE,OAAK,MAAM,KAAK,KAAK,aAAa,iBAAiB,EAAE,CACnD,OAAM,EAAE;GAAE,SAAS;GAAK,UAAU;GAAK;GAAQ,CAAC;;;;;;;CASpD,cAAqB;AACnB,SAAO,KAAK;;;;;;;;CASd,WAAkB,KAAa;AAC7B,SAAO,KAAK,eAAe,IAAI;;;;;;;;;;;;;;;;;;;;CAqBjC,MAAM,QACJ,QACA,MACA,MACA,SACoD;EACpD,MAAM,YAAY,KAAK,KAAK;EAE5B,IAAI,MAAM,GADG,KAAK,eAAe,SAAS,WAAW,GACjC,OAAO,cAAc,SAAS,MAAM;AAExD,OAAK,OAAO,MAAM,0BAA0B;GAC1C;GACA;GACA,YAAY,SAAS;GACrB,SAAS,SAAS,KAAA;GACnB,CAAC;EAEF,MAAM,UAAU;GAAE,GAAG,KAAK;GAAS,GAAI,SAAS,WAAW,EAAE;GAAG;EAEhE,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,SAAS,SAAS,UAAU,WAAW;EAG7C,IAAI;AACJ,MAAI,QAAQ,KACV,KAAI,gBAAgB,UAAU;AAC5B,iBAAc;AAEd,UAAO,QAAQ;aACN,gBAAgB,QAAQ,gBAAgB,YACjD,eAAc;WACL,QAAQ,iBAAiB,SAAS,OAAO,CAClD,eAAc,KAAK,UAAU,KAAK;MAElC,eAAc,OAAO,KAAK;EAI9B,MAAM,OAAiD;GACrD;GACA;GACA,MAAM;GACN;GACD;AAED,MAAI,CAAC,SAAS,SACZ,OAAM,KAAK,KAAK,MAAM;GAAE;GAAK;GAAM;GAAS,CAAC;AAE/C,MAAI,KAAK,cAAe,OAAM,KAAK;AACnC,QAAM,KAAK,eAAe,KAAK,KAAK;EAEpC,IAAI,mBAAmB;EAEvB,MAAM,UAAU,YAAY;AAE1B,UAAO,MAAM;IAEX,IAAI;AACJ,QAAI,KAAK,aAAa,CAAC,SAAS,OAC9B,aAAY,iBAAiB;KAC3B,MAAM,+BAAe,IAAI,MAAM,kBAAkB;AACjD,kBAAa,OAAO;AACpB,gBAAW,MAAM,aAAa;OAC7B,KAAK,UAAU;AAGpB,QAAI;AAEF,SAAI,oBAAoB,CAAC,SAAS,UAAU;MAC1C,MAAM,YAAY,EAAE,GAAG,MAAM;AAE7B,YAAM,KAAK,KAAK,MAAM;OAAE;OAAK,MAAM;OAAW;OAAS,CAAC;AAExD,WAAK,UAAU,UAAU;;KAG3B,MAAM,MAAM,IAAI,QAAQ,KAAK,KAAK;KAElC,MAAM,MAAM,MAAM,KAAK,UAAU,IAAI;AAErC,SAAI,IAAI,WAAW,OAAO,KAAK,qBAAqB,CAAC;UAC/B,MAAM,KAAK,kBAAkB,IAAI,OAAO,CAAwB,EACnE;AACf,0BAAmB;AAEnB,WAAI,UAAW,cAAa,UAAU;AACtC;;;KAIJ,MAAM,SAAS,IAAI;KACnB,MAAM,cAAc,IAAI,QAAQ,IAAI,eAAe,IAAI;KAGvD,IAAI;AACJ,SAAI,YAAY,SAAS,OAAO,CAC9B,QAAO,MAAM,IAAI,MAAM;cAEvB,YAAY,SAAS,2BAA2B,IAChD,YAAY,SAAS,kBAAkB,IACvC,YAAY,SAAS,SAAS,IAC9B,YAAY,SAAS,SAAS,IAC9B,YAAY,SAAS,SAAS,IAC9B,YAAY,WAAW,kBAAkB,IACzC,YAAY,WAAW,iBAAiB,CAGxC,QAAO,MAAM,IAAI,MAAM;SAEvB,QAAO,MAAM,IAAI,MAAM;AAGzB,WAAM,KAAK,cAAc,IAAI,QAAQ,KAAK,KAAK,EAAE,KAAK,KAAK;KAE3D,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,UAAK,OAAO,KAAK,2BAA2B;MAC1C;MACA;MACA,QAAQ,IAAI;MACZ,YAAY;MACb,CAAC;AAEF,UAAK,QAAQ,QAAQ;MACnB;MACA;MACA,QAAQ,IAAI;MACZ,YAAY;MACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;MACnC,SAAS;MACV,CAAC;AAEF,YAAO;MAAQ;MAAmB;MAAQ;aACnC,OAAO;KACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,UAAK,OAAO,MAAM,uBAAuB,OAAgB;MACvD;MACA;MACA,YAAY;MACb,CAAC;AAEF,UAAK,QAAQ,QAAQ;MACnB;MACA;MACA,QAAQ,iBAAiB,WAAW,MAAM,SAAS,KAAA;MACnD,YAAY;MACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;MACnC,SAAS;MACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;MAC9D,CAAC;AAEF,WAAM;cACE;AACR,SAAI,UAAW,cAAa,UAAU;;;;EAK5C,MAAM,YAAY,EAAE,YAAiC;AAEnD,OAAI,SAAS,OAAO,UAAU,YAAY,UAAU,OAAO;IACzD,MAAM,YAAa,MAA4B;AAC/C,QAAI,cAAc,gBAAgB,cAAc,eAAgB,QAAO;;AAGzE,OAAI,iBAAiB,YAAY,MAAM;QAClB,KAAK,MAAM,kBACd,MAAM,YAAY,eAAe,aAAa,MAAM,OAAO,CACzE,QAAO;;AAGX,UAAO;;AAGT,MAAI,SAAS,aAAa,CAAC,KAAK,MAAM,cAAc,SAAS,OAAO,CAClE,QAAO,SAAS;AAGlB,SAAO,KAAK,UAAU,SAAS,SAAS;;;;;;;;;;CAW1C,MAAM,IACJ,MACA,SACoD;AACpD,SAAO,KAAK,QAAW,OAAO,MAAM,KAAA,GAAW,QAAQ;;;;;;;;;;CAWzD,MAAM,KACJ,MACA,MACA,SACoD;AACpD,SAAO,KAAK,QAAW,QAAQ,MAAM,MAAM,QAAQ;;;;;;;;;;CAWrD,MAAM,IACJ,MACA,MACA,SACoD;AACpD,SAAO,KAAK,QAAW,OAAO,MAAM,MAAM,QAAQ;;;;;;;;;;CAWpD,MAAM,MACJ,MACA,MACA,SACoD;AACpD,SAAO,KAAK,QAAW,SAAS,MAAM,MAAM,QAAQ;;;;;;;;;;CAWtD,MAAM,OACJ,MACA,SACoD;AACpD,SAAO,KAAK,QAAW,UAAU,MAAM,KAAA,GAAW,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+B5D,eAOE,QAC6E;EAC7E,MAAM,WAAW,IAAI,aAAa,MAAM,OAAO;AAC/C,UAAQ,WAAW,SAAS,KAAK,OAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../lib/auth.ts","../lib/types.ts","../lib/validation.ts","../lib/endpoint/base-endpoint.ts","../lib/logger.ts","../lib/metrics.ts","../lib/http/http-client.ts"],"sourcesContent":["import type { RequestOptions as ReqOpts } from './types';\n\n/**\n * Extended RequestInit with URL override capability for query-based auth.\n */\nexport interface AuthContext {\n url: string;\n init: RequestInit & { __urlOverride?: string };\n options?: ReqOpts;\n}\n\n/**\n * Interface for authentication providers.\n * Implement this to create custom authentication strategies.\n *\n * @example\n * ```ts\n * class CustomAuth implements AuthProvider {\n * async apply({ init }) {\n * init.headers = { ...init.headers, 'X-Custom-Auth': 'token' };\n * }\n * }\n * ```\n */\nexport interface AuthProvider {\n /**\n * Apply authentication to the outgoing request.\n * Called after SDK headers are assembled, but before request is sent.\n *\n * @param req - Request context including URL, init, and options\n */\n apply(req: AuthContext): Promise<void> | void;\n}\n\n/**\n * No-op authentication provider (no authentication applied).\n * Use this when you don't need authentication.\n */\nexport class NoAuth implements AuthProvider {\n async apply(_req: AuthContext) {\n /* no-op */\n }\n}\n\n/**\n * API Key authentication provider.\n * Supports both header-based and query parameter-based authentication.\n *\n * @example\n * ```ts\n * // Header-based\n * const auth = new ApiKeyAuth({ header: 'X-API-Key', value: 'secret' });\n *\n * // Query parameter-based\n * const auth = new ApiKeyAuth({ query: 'apiKey', value: 'secret' });\n * ```\n */\nexport class ApiKeyAuth implements AuthProvider {\n constructor(private opts: { header?: string; query?: string; value: string }) {\n if (!opts.header && !opts.query) {\n throw new Error('ApiKeyAuth requires either \"header\" or \"query\" option');\n }\n if (opts.header && opts.query) {\n throw new Error('ApiKeyAuth cannot use both \"header\" and \"query\" options');\n }\n }\n apply({ url, init }: AuthContext) {\n const value = this.opts.value;\n if (this.opts.header) {\n if (init.headers instanceof Headers) {\n init.headers.set(this.opts.header, value);\n } else if (Array.isArray(init.headers)) {\n init.headers.push([this.opts.header, value]);\n } else {\n init.headers = { ...init.headers, [this.opts.header]: value };\n }\n } else if (this.opts.query) {\n const u = new URL(url);\n u.searchParams.set(this.opts.query, value);\n init.__urlOverride = u.toString();\n }\n }\n}\n/**\n * Bearer token authentication provider.\n * Supports both static tokens and dynamic token fetching (e.g., for OAuth2 refresh).\n *\n * @example\n * ```ts\n * // Static token\n * const auth = new BearerTokenAuth(() => 'my-token');\n *\n * // Dynamic token with refresh\n * const auth = new BearerTokenAuth(async () => {\n * return await refreshAccessToken();\n * });\n * ```\n */\nexport class BearerTokenAuth implements AuthProvider {\n constructor(private getToken: () => Promise<string> | string) {}\n async apply({ init }: AuthContext) {\n const token = await this.getToken();\n if (!token) {\n throw new Error('BearerTokenAuth: token is empty or undefined');\n }\n const authHeader = `Bearer ${token}`;\n if (init.headers instanceof Headers) {\n init.headers.set('Authorization', authHeader);\n } else if (Array.isArray(init.headers)) {\n init.headers.push(['Authorization', authHeader]);\n } else {\n init.headers = { ...init.headers, Authorization: authHeader };\n }\n }\n}\n","import { AuthProvider } from './auth';\nimport { Logger } from './logger';\nimport { MetricsCollector } from './metrics';\n\n// ========================\n// Standard Schema v1 Types\n// ========================\n\n/** The Standard Schema interface for validation libraries (Zod, Valibot, ArkType, etc.) */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaV1.Props<Input, Output>;\n}\n\nexport declare namespace StandardSchemaV1 {\n /** The Standard Schema properties interface. */\n export interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output> | undefined;\n }\n\n /** The result interface of the validate function. */\n export type Result<Output> = SuccessResult<Output> | FailureResult;\n\n /** The result interface if validation succeeds. */\n export interface SuccessResult<Output> {\n readonly value: Output;\n readonly issues?: undefined;\n }\n\n /** The result interface if validation fails. */\n export interface FailureResult {\n readonly issues: ReadonlyArray<Issue>;\n }\n\n /** The issue interface of the failure output. */\n export interface Issue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;\n }\n\n /** The path segment interface of the issue. */\n export interface PathSegment {\n readonly key: PropertyKey;\n }\n\n /** The Standard types interface. */\n export interface Types<Input = unknown, Output = Input> {\n readonly input: Input;\n readonly output: Output;\n }\n\n /** Infers the input type of a Standard Schema. */\n export type InferInput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['input'];\n\n /** Infers the output type of a Standard Schema. */\n export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n >['output'];\n}\n\n/** Infers the input type of a Standard Schema. */\nexport type InferInput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferInput<Schema>;\n\n/** Infers the output type of a Standard Schema. */\nexport type InferOutput<Schema extends StandardSchemaV1> = StandardSchemaV1.InferOutput<Schema>;\n\n/** A schema map where keys are HTTP status codes and values are Standard Schema validators. */\nexport type SchemaMap = Record<number, StandardSchemaV1>;\n\n/** A Standard Schema or a map of status codes to schemas. */\nexport type ResponseSchema = StandardSchemaV1 | SchemaMap;\n\nexport type Dictionary<T> = Record<string, T>;\n\nexport type FetchLike = (input: string | Request | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Map of base URLs for different services.\n * The 'default' key is required and used when no specific key is provided.\n *\n * @example\n * ```ts\n * {\n * default: 'https://api.example.com',\n * auth: 'https://auth.example.com',\n * cdn: 'https://cdn.example.com'\n * }\n * ```\n */\nexport type BaseUrlMap = {\n default: string;\n} & Record<string, string>;\n\n/**\n * Configuration for retry behavior on failed requests.\n * Implements exponential backoff with optional jitter.\n *\n * @example\n * ```ts\n * {\n * maxAttempts: 3,\n * baseDelayMs: 1000,\n * jitter: 0.2,\n * retryMethods: ['GET', 'HEAD', 'PUT'],\n * retryStatusCodes: [500, 502, 503, 504],\n * retryNetworkErrors: true,\n * respectRetryAfter: true,\n * }\n * ```\n */\nexport type RetryPolicy = {\n /** Maximum number of retry attempts */\n maxAttempts: number;\n /** Base delay in milliseconds (will be exponentially increased) */\n baseDelayMs: number;\n /** Jitter factor 0..1 to randomize delays and prevent thundering herd */\n jitter?: number;\n /** HTTP methods eligible for retry */\n retryMethods?: (keyof typeof HTTPMethod)[];\n /** HTTP status codes eligible for retry */\n retryStatusCodes?: HTTPStatusCodeNumber[];\n /** Whether to respect 'Retry-After' header if present in the response */\n respectRetryAfter?: boolean;\n /** Custom function to determine if a request should be retried */\n shouldRetry?: (ctx: {\n url: string;\n method: HttpMethod;\n status: HTTPStatusCodeNumber;\n attempt: number;\n response?: Response;\n }) => Promise<boolean> | boolean;\n};\n\nexport const HTTPMethod = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n PATCH: 'PATCH',\n DELETE: 'DELETE',\n HEAD: 'HEAD',\n OPTIONS: 'OPTIONS',\n} as const;\n\nexport type HttpMethod = keyof typeof HTTPMethod;\n\nexport const HTTPStatusCode = {\n // 1xx — Informational\n CONTINUE: 100,\n SWITCHING_PROTOCOLS: 101,\n PROCESSING: 102,\n EARLY_HINTS: 103,\n\n // 2xx — Success\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NON_AUTHORITATIVE_INFORMATION: 203,\n NO_CONTENT: 204,\n RESET_CONTENT: 205,\n PARTIAL_CONTENT: 206,\n MULTI_STATUS: 207,\n ALREADY_REPORTED: 208,\n IM_USED: 226,\n\n // 3xx — Redirection\n MULTIPLE_CHOICES: 300,\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n SEE_OTHER: 303,\n NOT_MODIFIED: 304,\n USE_PROXY: 305,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // 4xx — Client Errors\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n LENGTH_REQUIRED: 411,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n URI_TOO_LONG: 414,\n UNSUPPORTED_MEDIA_TYPE: 415,\n RANGE_NOT_SATISFIABLE: 416,\n EXPECTATION_FAILED: 417,\n IM_A_TEAPOT: 418,\n MISDIRECTED_REQUEST: 421,\n UNPROCESSABLE_ENTITY: 422,\n LOCKED: 423,\n FAILED_DEPENDENCY: 424,\n TOO_EARLY: 425,\n UPGRADE_REQUIRED: 426,\n PRECONDITION_REQUIRED: 428,\n TOO_MANY_REQUESTS: 429,\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n UNAVAILABLE_FOR_LEGAL_REASONS: 451,\n\n // 5xx — Server Errors\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n VARIANT_ALSO_NEGOTIATES: 506,\n INSUFFICIENT_STORAGE: 507,\n LOOP_DETECTED: 508,\n NOT_EXTENDED: 510,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n} as const;\n\nexport type HTTPStatusCodeKey = keyof typeof HTTPStatusCode;\nexport type HTTPStatusCodeNumber = (typeof HTTPStatusCode)[HTTPStatusCodeKey];\n\n/**\n * Hook called after a response is received and parsed.\n * Useful for logging, metrics, or global error handling.\n */\nexport type AfterResponseHook = (ctx: {\n request: Request;\n response: Response;\n parsed?: unknown;\n}) => Promise<void> | void;\n\n/**\n * Hook called before a request is sent.\n * Useful for logging, adding headers, or modifying the request.\n */\nexport type BeforeRequestHook = (ctx: { url: string; init: RequestInit }) => Promise<void> | void;\n\nexport interface Interceptors {\n /** Hooks executed before each request is sent */\n beforeRequest?: BeforeRequestHook[];\n /** Hooks executed after each response is received */\n afterResponse?: AfterResponseHook[];\n}\n\nexport interface TimeoutOptions {\n /** Request timeout in milliseconds */\n requestTimeoutMs?: number;\n}\n\n/**\n * Configuration options for the HTTP client.\n *\n * @example\n * ```ts\n * const options: ClientOptions = {\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'X-API-Version': '1.0' },\n * retry: { maxAttempts: 3, baseDelayMs: 1000 },\n * timeout: { requestTimeoutMs: 30000 }\n * }\n * ```\n */\nexport interface ClientOptions {\n /** Map of base URLs for different services */\n baseUrls: BaseUrlMap;\n /** Custom fetch implementation (defaults to globalThis.fetch) */\n fetch?: FetchLike;\n /** Default headers applied to all requests */\n headers?: Record<string, string>;\n /** Retry strategy configuration */\n retry?: RetryPolicy;\n /** Request/response interceptors */\n interceptors?: Interceptors;\n /** Timeout configuration */\n timeout?: TimeoutOptions;\n /** Authentication provider */\n auth?: AuthProvider;\n /** Logger instance */\n logger?: Logger;\n /** Metrics collector */\n metrics?: MetricsCollector;\n /**\n * Callback to handle 401 Unauthorized responses.\n * Return true to retry the request (e.g. after refreshing tokens),\n * or false to return the 401 response as is.\n */\n onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n}\n\n/** Result of a safe parse operation using Standard Schema. */\nexport type SafeParseResult<T> =\n | { success: true; data: T }\n | { success: false; issues: ReadonlyArray<StandardSchemaV1.Issue> };\n\n/**\n * Custom error class for API-related errors.\n * Includes HTTP status codes, response details, and validation errors.\n *\n * @example\n * ```ts\n * throw new ApiError('Invalid request', {\n * status: 400,\n * details: { field: 'email', message: 'Invalid format' }\n * });\n * ```\n */\nexport class ApiError extends Error {\n public status?: number;\n public details?: unknown;\n /** Validation issues from Standard Schema-compatible libraries (Zod, Valibot, ArkType, etc.) */\n public validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n\n constructor(\n message: string,\n options?: {\n status?: number;\n cause?: unknown;\n details?: unknown;\n validationIssues?: ReadonlyArray<StandardSchemaV1.Issue>;\n }\n ) {\n super(message);\n this.name = 'ApiError';\n this.status = options?.status;\n this.details = options?.details;\n this.cause = options?.cause;\n this.validationIssues = options?.validationIssues;\n\n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ApiError);\n }\n }\n\n /**\n * Check if this is a validation error (has validationIssues)\n */\n isValidationError(): boolean {\n return !!this.validationIssues && this.validationIssues.length > 0;\n }\n\n /**\n * Check if this is a client error (4xx status)\n */\n isClientError(): boolean {\n return !!this.status && this.status >= 400 && this.status < 500;\n }\n\n /**\n * Check if this is a server error (5xx status)\n */\n isServerError(): boolean {\n return !!this.status && this.status >= 500;\n }\n\n /**\n * Get a formatted error message with all available details\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n status: this.status,\n details: this.details,\n validationIssues: this.validationIssues,\n stack: this.stack,\n };\n }\n}\n\n/**\n * Error thrown when an endpoint receives a response with a status code\n * that has no defined schema in the endpoint configuration.\n */\nexport class SchemaDefinitionError extends Error {\n constructor(public status: number) {\n super(`No schema defined for status code ${status}`);\n this.name = 'SchemaDefinitionError';\n\n // Maintains proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, SchemaDefinitionError);\n }\n }\n}\n\n/** Generic paginated response type. */\nexport type Paginated<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n};\n\n/**\n * Options that can be passed to individual requests to override defaults.\n *\n * @example\n * ```ts\n * await endpoint.call(data, {\n * baseUrlKey: 'v2',\n * headers: { 'X-Custom': 'value' },\n * query: { filter: 'active' }\n * });\n * ```\n */\nexport type RequestOptions = {\n /** Override base URL for a single call */\n baseUrlKey?: keyof BaseUrlMap;\n /** Additional headers for this call only */\n headers?: Record<string, string>;\n /** Abort controller signal for cancellation */\n signal?: AbortSignal;\n /** Custom query params */\n query?: URLSearchParams | Record<string, string | number | boolean | undefined>;\n /** Skip authentication for this request */\n skipAuth?: boolean;\n /** Skip retry logic for this request */\n skipRetry?: boolean;\n};\n\n/**\n * Converts query parameters to a URL query string.\n * Filters out undefined values automatically.\n *\n * @param q - Query parameters as URLSearchParams or object\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * toQueryString({ page: 1, filter: 'active' }) // \"?page=1&filter=active\"\n * toQueryString({ optional: undefined }) // \"\"\n * ```\n */\nexport function toQueryString(q?: RequestOptions['query']): string {\n if (!q) return '';\n if (q instanceof URLSearchParams) {\n const s = q.toString();\n return s ? `?${s}` : '';\n }\n const params = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v !== undefined) {\n params.append(k, String(v));\n }\n });\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n","import { ApiError, SafeParseResult, StandardSchemaV1 } from './types';\n\n/**\n * Safely parse/validate data with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.).\n * Returns a result object with success status and data or issues.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Result object with success flag and data/issues\n *\n * @example\n * ```ts\n * // Works with Zod\n * import { z } from 'zod';\n * const result = await safeParse(z.object({ name: z.string() }), userData);\n *\n * // Works with Valibot\n * import * as v from 'valibot';\n * const result = await safeParse(v.object({ name: v.string() }), userData);\n *\n * // Works with ArkType\n * import { type } from 'arktype';\n * const result = await safeParse(type({ name: 'string' }), userData);\n *\n * if (result.success) {\n * console.log(result.data);\n * } else {\n * console.error(result.issues);\n * }\n * ```\n */\nexport async function safeParse<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<SafeParseResult<StandardSchemaV1.InferOutput<T>>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n return { success: false, issues: result.issues };\n }\n\n return { success: true, data: result.value as StandardSchemaV1.InferOutput<T> };\n}\n\n/**\n * Parse/validate data with any Standard Schema-compatible library, throwing an ApiError on failure.\n * Use this when you want to fail fast on invalid data.\n *\n * @param schema - A Standard Schema-compatible validator\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {ApiError} If validation fails (with validationIssues property)\n *\n * @example\n * ```ts\n * // Works with any Standard Schema-compatible library\n * try {\n * const user = await parseOrThrow(UserSchema, userData);\n * console.log(user);\n * } catch (error) {\n * if (error instanceof ApiError && error.isValidationError()) {\n * console.error('Validation failed:', error.validationIssues);\n * }\n * }\n * ```\n */\nexport async function parseOrThrow<T extends StandardSchemaV1>(\n schema: T,\n data: unknown\n): Promise<StandardSchemaV1.InferOutput<T>> {\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n const messages = result.issues.map((issue) => issue.message).join(', ');\n throw new ApiError(`Validation failed: ${messages}`, {\n validationIssues: result.issues,\n });\n }\n\n return result.value as StandardSchemaV1.InferOutput<T>;\n}\n\n/**\n * Type guard to check if a value is a Standard Schema-compatible validator.\n * Handles both object-based schemas (Zod, Valibot) and function-based schemas (ArkType).\n *\n * @param value - Value to check\n * @returns True if the value implements Standard Schema v1\n */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n if (value === null || value === undefined) return false;\n\n // Standard Schema can be implemented on objects or functions (ArkType uses functions)\n if (typeof value !== 'object' && typeof value !== 'function') return false;\n\n const schema = value as StandardSchemaV1;\n return (\n '~standard' in schema &&\n typeof schema['~standard'] === 'object' &&\n schema['~standard'] !== null &&\n schema['~standard'].version === 1 &&\n typeof schema['~standard'].validate === 'function'\n );\n}\n","import { HttpClient } from '../http/http-client';\nimport {\n HTTPMethod,\n ResponseSchema,\n SchemaDefinitionError,\n SchemaMap,\n StandardSchemaV1,\n} from '../types';\nimport { isStandardSchema, parseOrThrow } from '../validation';\n\nexport type EndpointConfig<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n method: keyof typeof HTTPMethod;\n path: string | ((params: StandardSchemaV1.InferOutput<Exclude<PathSchema, undefined>>) => string);\n response: ResSchema;\n request?: ReqSchema;\n query?: QuerySchema;\n pathParams?: PathSchema;\n mustHeaderKeys?: MustHeaderKeys;\n advanced?: {\n baseUrlKey?: string;\n skipAuth?: boolean;\n skipRequestValidation?: boolean;\n skipResponseValidation?: boolean;\n skipRetry?: boolean;\n };\n description?: string;\n};\n\n// Helper type to create required headers from mustHeaderKeys\ntype RequiredHeaders<Keys extends readonly string[]> = Keys extends readonly []\n ? Record<string, string> | undefined\n : { [K in Keys[number]]: string } & Record<string, string>;\n\nexport type EndpointCallParams<\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = {\n data?: ReqSchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<ReqSchema> : never;\n query?: QuerySchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<QuerySchema> : never;\n pathParams?: PathSchema extends StandardSchemaV1\n ? StandardSchemaV1.InferInput<PathSchema>\n : never;\n signal?: globalThis.AbortSignal;\n} & (MustHeaderKeys extends readonly []\n ? { headers?: Record<string, string> }\n : { headers: RequiredHeaders<MustHeaderKeys> });\n\n// Helper to extract the response type from a schema which might be a single schema or a status map\ntype InferResponse<S> = S extends StandardSchemaV1\n ? StandardSchemaV1.InferOutput<S>\n : S extends SchemaMap\n ? {\n [K in keyof S]: S[K] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S[K]> : never;\n }[keyof S]\n : never;\n\nexport type EndpointCall<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> = (\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n) => Promise<InferResponse<ResSchema>>;\n\nexport class EndpointImpl<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined,\n QuerySchema extends StandardSchemaV1 | undefined,\n PathSchema extends StandardSchemaV1 | undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n> {\n constructor(\n private client: HttpClient,\n private config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ) {}\n\n async call(\n params: EndpointCallParams<ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): Promise<InferResponse<ResSchema>> {\n const { data, query, pathParams, signal } = params;\n const headers = 'headers' in params ? params.headers : undefined;\n\n const skipRequestValidation = this.config.advanced?.skipRequestValidation ?? false;\n const skipResponseValidation = this.config.advanced?.skipResponseValidation ?? false;\n\n // Validate required headers\n if (this.config.mustHeaderKeys && this.config.mustHeaderKeys.length > 0) {\n const missingHeaders = this.config.mustHeaderKeys.filter(\n (key) => !headers || !(key in headers)\n );\n if (missingHeaders.length > 0) {\n throw new Error(`Missing required header(s): ${missingHeaders.join(', ')}`);\n }\n }\n\n // Validate Request Body using Standard Schema\n if (!skipRequestValidation && this.config.request && data !== undefined) {\n await parseOrThrow(this.config.request, data);\n }\n\n // Validate Query Params using Standard Schema\n if (!skipRequestValidation && this.config.query && query !== undefined) {\n await parseOrThrow(this.config.query, query);\n }\n\n // Validate Path Params using Standard Schema\n if (!skipRequestValidation && this.config.pathParams && pathParams !== undefined) {\n await parseOrThrow(this.config.pathParams, pathParams);\n }\n\n // Check for missing required params\n if (this.config.request && data === undefined) {\n throw new Error('Missing required request body (data)');\n }\n if (this.config.pathParams && pathParams === undefined) {\n throw new Error('Missing required path parameters (pathParams)');\n }\n\n // Resolve Path\n let pathStr: string;\n if (typeof this.config.path === 'function') {\n if (!pathParams) throw new Error('Path function requires pathParams');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pathStr = this.config.path(pathParams as any);\n } else {\n pathStr = this.config.path;\n }\n\n const { data: responseData, status } = await this.client.request(\n this.config.method,\n pathStr,\n data,\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n query: query as any,\n headers,\n baseUrlKey: this.config.advanced?.baseUrlKey,\n skipAuth: this.config.advanced?.skipAuth,\n skipRetry: this.config.advanced?.skipRetry,\n signal,\n }\n );\n\n // Handle Response Validation\n const schema = this.config.response;\n if (skipResponseValidation) {\n return responseData as InferResponse<ResSchema>;\n }\n\n if (isStandardSchema(schema)) {\n // Single schema for all success codes\n return (await parseOrThrow(schema, responseData)) as InferResponse<ResSchema>;\n }\n\n // Map of status codes to schemas\n const schemaMap = schema as SchemaMap;\n const specificSchema = schemaMap[status];\n if (!specificSchema) {\n // No schema defined for this status code\n throw new SchemaDefinitionError(status);\n }\n\n return (await parseOrThrow(specificSchema, responseData)) as InferResponse<ResSchema>;\n }\n}\n","/**\n * Log levels for structured logging.\n */\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\n/**\n * Structured log entry with metadata.\n */\nexport interface LogEntry {\n level: LogLevel;\n message: string;\n timestamp: string;\n context?: Record<string, unknown>;\n error?: Error;\n}\n\n/**\n * Logger interface for custom logger implementations.\n * Implement this to integrate with your logging infrastructure.\n *\n * @example\n * ```ts\n * class ConsoleLogger implements Logger {\n * log(entry: LogEntry) {\n * console.log(JSON.stringify(entry));\n * }\n * }\n * ```\n */\nexport interface Logger {\n log(entry: LogEntry): void;\n}\n\n/**\n * Default console logger implementation.\n * Formats log entries as JSON for easy parsing.\n */\nexport class ConsoleLogger implements Logger {\n constructor(private minLevel: LogLevel = LogLevel.INFO) {}\n\n log(entry: LogEntry): void {\n const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR];\n const entryLevelIndex = levels.indexOf(entry.level);\n const minLevelIndex = levels.indexOf(this.minLevel);\n\n if (entryLevelIndex < minLevelIndex) return;\n\n const output = {\n ...entry,\n error: entry.error\n ? {\n message: entry.error.message,\n stack: entry.error.stack,\n name: entry.error.name,\n }\n : undefined,\n };\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(JSON.stringify(output));\n break;\n case LogLevel.INFO:\n console.info(JSON.stringify(output));\n break;\n case LogLevel.WARN:\n console.warn(JSON.stringify(output));\n break;\n case LogLevel.ERROR:\n console.error(JSON.stringify(output));\n break;\n }\n }\n}\n\n/**\n * No-op logger that discards all log entries.\n * Use this in production if you don't want any logging.\n */\nexport class NoOpLogger implements Logger {\n log(_entry: LogEntry): void {\n // no-op\n }\n}\n\n/**\n * Utility class for creating structured log entries.\n */\nexport class LoggerUtil {\n constructor(private logger: Logger) {}\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.DEBUG,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.INFO,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.WARN,\n message,\n timestamp: new Date().toISOString(),\n context,\n });\n }\n\n error(message: string, error?: Error, context?: Record<string, unknown>): void {\n this.logger.log({\n level: LogLevel.ERROR,\n message,\n timestamp: new Date().toISOString(),\n context,\n error,\n });\n }\n}\n","/**\n * Metrics data for HTTP requests.\n */\nexport interface RequestMetrics {\n method: string;\n path: string;\n status?: number;\n durationMs: number;\n timestamp: string;\n success: boolean;\n error?: string;\n}\n\n/**\n * Interface for metrics collectors.\n * Implement this to integrate with your monitoring infrastructure (DataDog, Prometheus, etc.).\n *\n * @example\n * ```ts\n * class DataDogMetrics implements MetricsCollector {\n * collect(metrics: RequestMetrics) {\n * dogstatsd.histogram('http.request.duration', metrics.durationMs, {\n * method: metrics.method,\n * status: String(metrics.status)\n * });\n * }\n * }\n * ```\n */\nexport interface MetricsCollector {\n collect(metrics: RequestMetrics): void;\n}\n\n/**\n * No-op metrics collector that discards all metrics.\n */\nexport class NoOpMetricsCollector implements MetricsCollector {\n collect(_metrics: RequestMetrics): void {\n // no-op\n }\n}\n\n/**\n * In-memory metrics collector for testing and development.\n * Stores metrics in memory with configurable retention.\n */\nexport class InMemoryMetricsCollector implements MetricsCollector {\n private metrics: RequestMetrics[] = [];\n private readonly maxEntries: number;\n\n constructor(maxEntries = 1000) {\n this.maxEntries = maxEntries;\n }\n\n collect(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n if (this.metrics.length > this.maxEntries) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get all collected metrics.\n */\n getMetrics(): RequestMetrics[] {\n return [...this.metrics];\n }\n\n /**\n * Get metrics summary statistics.\n */\n getSummary(): {\n total: number;\n successful: number;\n failed: number;\n avgDurationMs: number;\n minDurationMs: number;\n maxDurationMs: number;\n } {\n if (this.metrics.length === 0) {\n return {\n total: 0,\n successful: 0,\n failed: 0,\n avgDurationMs: 0,\n minDurationMs: 0,\n maxDurationMs: 0,\n };\n }\n\n const successful = this.metrics.filter((m) => m.success).length;\n\n // Calculate stats in a single pass to avoid stack overflow with spread operator\n let sum = 0;\n let min = Infinity;\n let max = -Infinity;\n\n for (const m of this.metrics) {\n const d = m.durationMs;\n sum += d;\n if (d < min) min = d;\n if (d > max) max = d;\n }\n\n return {\n total: this.metrics.length,\n successful,\n failed: this.metrics.length - successful,\n avgDurationMs: sum / this.metrics.length,\n minDurationMs: min === Infinity ? 0 : min,\n maxDurationMs: max === -Infinity ? 0 : max,\n };\n }\n\n /**\n * Clear all collected metrics.\n */\n clear(): void {\n this.metrics = [];\n }\n}\n\n/**\n * Console-based metrics collector for debugging.\n */\nexport class ConsoleMetricsCollector implements MetricsCollector {\n collect(metrics: RequestMetrics): void {\n console.log('[METRICS]', JSON.stringify(metrics));\n }\n}\n","import type { AuthProvider } from '../auth';\nimport { NoAuth } from '../auth';\nimport { EndpointCall, EndpointConfig, EndpointImpl } from '../endpoint/base-endpoint';\nimport { LoggerUtil, NoOpLogger } from '../logger';\nimport { MetricsCollector, NoOpMetricsCollector } from '../metrics';\nimport {\n ApiError,\n ClientOptions,\n FetchLike,\n HTTPMethod,\n HTTPStatusCodeNumber,\n Interceptors,\n RequestOptions,\n ResponseSchema,\n RetryPolicy,\n StandardSchemaV1,\n toQueryString,\n} from '../types';\n\n/**\n * HTTP client with built-in authentication, and interceptors.\n * Supports multiple base URLs, type-safe requests, and comprehensive error handling.\n *\n * @example\n * ```ts\n * const client = new HttpClient({\n * baseUrls: { default: 'https://api.example.com' },\n * headers: { 'Content-Type': 'application/json' },\n * timeout: { requestTimeoutMs: 30000 }\n * });\n *\n * const { data } = await client.request('GET', '/users', undefined, { query: { page: 1 } });\n * ```\n */\nexport class HttpClient {\n private fetchImpl: FetchLike;\n private baseUrls: ClientOptions['baseUrls'];\n private headers: Record<string, string>;\n private interceptors: Interceptors;\n private retryPolicy: RetryPolicy;\n private timeoutMs?: number;\n private auth: AuthProvider;\n private logger: LoggerUtil;\n private metrics: MetricsCollector;\n private onUnauthenticated?: (response: Response) => Promise<boolean> | boolean;\n\n /**\n * Creates a new HTTP client instance.\n *\n * @param opts - Client configuration options\n * @throws {Error} If no fetch implementation is available\n */\n constructor(opts: ClientOptions) {\n this.fetchImpl = opts.fetch ?? (globalThis.fetch?.bind(globalThis) as FetchLike);\n if (!this.fetchImpl)\n throw new Error('No fetch implementation found. Pass one via options.fetch.');\n\n // Validate baseUrls configuration\n if (!opts.baseUrls || typeof opts.baseUrls !== 'object') {\n throw new Error('baseUrls must be provided and must be an object');\n }\n if (!opts.baseUrls.default) {\n throw new Error('baseUrls must include a \"default\" key');\n }\n\n this.baseUrls = opts.baseUrls;\n this.headers = opts.headers ?? { 'Content-Type': 'application/json' };\n this.interceptors = opts.interceptors ?? {};\n\n this.retryPolicy = opts.retry ?? { maxAttempts: 0, baseDelayMs: 1000 };\n\n // Validate retry policy\n if (!Number.isFinite(this.retryPolicy.maxAttempts) || this.retryPolicy.maxAttempts < 0) {\n throw new Error('retry.maxAttempts must be a non-negative finite number');\n }\n if (this.retryPolicy.baseDelayMs < 0) {\n throw new Error('retry.baseDelayMs must be non-negative');\n }\n\n this.timeoutMs = opts.timeout?.requestTimeoutMs;\n if (this.timeoutMs !== undefined && this.timeoutMs < 0) {\n throw new Error('timeout.requestTimeoutMs must be non-negative');\n }\n\n this.auth = opts['auth'] ?? new NoAuth();\n this.logger = new LoggerUtil(opts.logger ?? new NoOpLogger());\n this.metrics = opts.metrics ?? new NoOpMetricsCollector();\n this.onUnauthenticated = opts.onUnauthenticated;\n }\n\n /**\n * Set or update the authentication provider.\n *\n * @param auth - Authentication provider instance\n * @example\n * ```ts\n * client.setAuth(new BearerTokenAuth(() => getToken()));\n * ```\n */\n setAuth(auth: AuthProvider) {\n this.auth = auth;\n }\n\n private resolveBaseUrl(key?: keyof typeof this.baseUrls) {\n const k: string = (key as string) || 'default';\n const url = this.baseUrls[k];\n if (!url) {\n const availableKeys = Object.keys(this.baseUrls).join(', ');\n throw new Error(`Unknown baseUrl key: \"${k}\". Available keys: ${availableKeys}`);\n }\n return url.replace(/\\/$/, '');\n }\n\n /**\n * Run all registered before-request hooks.\n * @private\n */\n private async runBeforeHooks(url: string, init: RequestInit & { __urlOverride?: string }) {\n for (const h of this.interceptors.beforeRequest ?? []) {\n await h({ url, init });\n }\n }\n\n /**\n * Run all registered after-response hooks.\n * @private\n */\n private async runAfterHooks(req: Request, res: Response, parsed?: unknown) {\n for (const h of this.interceptors.afterResponse ?? []) {\n await h({ request: req, response: res, parsed });\n }\n }\n\n /**\n * Get all configured base URLs.\n *\n * @returns Object mapping base URL keys to their resolved URLs\n */\n public getBaseUrls() {\n return this.baseUrls;\n }\n\n /**\n * Get the resolved base URL for a given key.\n *\n * @param key - Base URL key (defaults to 'default' if not provided)\n * @returns Resolved base URL string\n */\n public getBaseUrl(key: string) {\n return this.resolveBaseUrl(key);\n }\n\n /**\n * Make an HTTP request with automatic retry, authentication, and validation.\n *\n * @param method - HTTP method (GET, POST, PUT, etc.)\n * @param path - Request path (will be appended to base URL)\n * @param body - Request body (will be JSON.stringify'd if Content-Type is json)\n * @param options - Additional request options (headers, query params, etc.)\n * @returns Promise resolving to response data and Response object\n * @throws {ApiError} If request fails or response validation fails\n *\n * @example\n * ```ts\n * const { data, response } = await client.request('GET', '/users', undefined, {\n * query: { page: 1, limit: 10 },\n * headers: { 'X-Custom': 'value' }\n * });\n * ```\n */\n async request<T = unknown>(\n method: keyof typeof HTTPMethod,\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n const startTime = Date.now();\n const base = this.resolveBaseUrl(options?.baseUrlKey);\n let url = `${base}${path}${toQueryString(options?.query)}`;\n\n this.logger.debug('HTTP request initiated', {\n method,\n path,\n baseUrlKey: options?.baseUrlKey,\n hasBody: body !== undefined,\n });\n\n const headers = { ...this.headers, ...(options?.headers ?? {}) };\n\n const controller = new AbortController();\n const signal = options?.signal ?? controller.signal;\n\n // Handle FormData, Blob, ArrayBuffer: pass through without stringifying\n let requestBody: BodyInit | undefined;\n if (body != null) {\n if (body instanceof FormData) {\n requestBody = body;\n // Remove Content-Type header so browser can set it with proper boundary\n delete headers['Content-Type'];\n } else if (body instanceof Blob || body instanceof ArrayBuffer) {\n requestBody = body;\n } else if (headers['Content-Type']?.includes('json')) {\n requestBody = JSON.stringify(body);\n } else {\n requestBody = String(body);\n }\n }\n\n const init: RequestInit & { __urlOverride?: string } = {\n method,\n headers,\n body: requestBody,\n signal,\n };\n\n if (!options?.skipAuth) {\n await this.auth.apply({ url, init, options });\n }\n if (init.__urlOverride) url = init.__urlOverride;\n await this.runBeforeHooks(url, init);\n // Track refresh attempts to prevent infinite loops\n let refreshAttempted = false;\n let retryAttempt = 0;\n\n const doFetch = async () => {\n // Loop for potential token refresh retry\n while (true) {\n // Apply timeout if configured\n let timeoutId: NodeJS.Timeout | number | undefined;\n if (this.timeoutMs && !options?.signal) {\n timeoutId = setTimeout(() => {\n const timeoutError = new Error('Request timeout');\n timeoutError.name = 'TimeoutError';\n controller.abort(timeoutError);\n }, this.timeoutMs);\n }\n\n try {\n // Re-apply auth headers if this is a retry (unless skipAuth is set)\n if (refreshAttempted && !options?.skipAuth) {\n const freshInit = {\n ...init,\n headers:\n typeof init.headers === 'object' &&\n !(init.headers instanceof Headers) &&\n !Array.isArray(init.headers)\n ? { ...(init.headers as Record<string, string>) }\n : init.headers,\n };\n // We need to re-run apply to get new token\n await this.auth.apply({ url, init: freshInit, options });\n // Update headers with potentially new token\n init.headers = freshInit.headers;\n }\n\n const req = new Request(url, init);\n\n const res = await this.fetchImpl(req);\n\n if (res.status === 401 && this.onUnauthenticated && !refreshAttempted) {\n const shouldRetry = await this.onUnauthenticated(res.clone() as unknown as Response);\n if (shouldRetry) {\n refreshAttempted = true;\n // Clear timeout before retrying\n if (timeoutId) clearTimeout(timeoutId);\n continue;\n }\n }\n\n const status = res.status as HTTPStatusCodeNumber;\n const contentType = res.headers.get('content-type') || '';\n\n // Check for errors first\n if (!res.ok) {\n if (\n !options?.skipRetry &&\n this.retryPolicy.maxAttempts > 0 &&\n retryAttempt < this.retryPolicy.maxAttempts &&\n this.retryPolicy.retryStatusCodes?.includes(status) &&\n this.retryPolicy.retryMethods?.includes(method)\n ) {\n let shouldRetry = true;\n if (this.retryPolicy.shouldRetry) {\n shouldRetry = await this.retryPolicy.shouldRetry({\n url,\n method,\n status,\n attempt: retryAttempt,\n response: res.clone() as unknown as Response,\n });\n }\n if (shouldRetry) {\n retryAttempt++;\n let delay = this.retryPolicy.baseDelayMs * 2 ** (retryAttempt - 1);\n if (this.retryPolicy.respectRetryAfter) {\n const retryAfter =\n res.headers.get('Retry-After') || res.headers.get('retry-after');\n if (retryAfter) {\n delay = parseInt(retryAfter, 10) * 1000;\n this.logger.warn(\n `Request failed with status ${status}. Retrying after ${delay}ms due to Retry-After header...`,\n { method, url, status, retryAttempt: retryAttempt + 1 }\n );\n }\n } else {\n this.logger.warn(\n `Request failed with status ${status}. Retrying attempt ${retryAttempt} after ${delay}ms...`,\n { method, url, status, retryAttempt }\n );\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n }\n }\n\n // Handle different response types appropriately\n let data: any;\n if (contentType.includes('json')) {\n data = await res.json();\n } else if (\n contentType.includes('application/octet-stream') ||\n contentType.includes('application/pdf') ||\n contentType.includes('image/') ||\n contentType.includes('video/') ||\n contentType.includes('audio/') ||\n contentType.startsWith('application/zip') ||\n contentType.startsWith('application/x-')\n ) {\n // Return binary data as Blob for file downloads\n data = await res.blob();\n } else {\n data = await res.text();\n }\n\n await this.runAfterHooks(new Request(url, init), res, data);\n\n const duration = Date.now() - startTime;\n this.logger.info('HTTP request successful', {\n method,\n url,\n status: res.status,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: res.status,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: true,\n });\n\n return { data: data as T, status: status };\n } catch (error) {\n const duration = Date.now() - startTime;\n this.logger.error('HTTP request failed', error as Error, {\n method,\n url,\n durationMs: duration,\n });\n\n this.metrics.collect({\n method,\n path,\n status: error instanceof ApiError ? error.status : undefined,\n durationMs: duration,\n timestamp: new Date().toISOString(),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n }\n };\n\n return doFetch();\n }\n\n /**\n * Convenience method for GET requests.\n *\n * @example\n * ```ts\n * const { data } = await client.get('/users', { query: { page: 1 } });\n * ```\n */\n async get<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n /**\n * Convenience method for POST requests.\n *\n * @example\n * ```ts\n * const { data } = await client.post('/users', { name: 'John' });\n * ```\n */\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('POST', path, body, options);\n }\n\n /**\n * Convenience method for PUT requests.\n *\n * @example\n * ```ts\n * const { data } = await client.put('/users/1', { name: 'John Updated' });\n * ```\n */\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PUT', path, body, options);\n }\n\n /**\n * Convenience method for PATCH requests.\n *\n * @example\n * ```ts\n * const { data } = await client.patch('/users/1', { name: 'John' });\n * ```\n */\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n /**\n * Convenience method for DELETE requests.\n *\n * @example\n * ```ts\n * const { data } = await client.delete('/users/1');\n * ```\n */\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<{ data: T; status: HTTPStatusCodeNumber }> {\n return this.request<T>('DELETE', path, undefined, options);\n }\n\n /**\n * Create a strongly-typed endpoint builder.\n * Works with any Standard Schema-compatible library (Zod, Valibot, ArkType, etc.)\n *\n * @param config - Endpoint configuration with schemas\n * @returns Endpoint call function\n *\n * @example\n * ```ts\n * // With Zod\n * import { z } from 'zod';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: z.object({ id: z.string(), name: z.string() }),\n * pathParams: z.object({ id: z.string() }),\n * });\n *\n * // With Valibot\n * import * as v from 'valibot';\n * const getUser = client.createEndpoint({\n * method: 'GET',\n * path: '/users/:id',\n * response: v.object({ id: v.string(), name: v.string() }),\n * pathParams: v.object({ id: v.string() }),\n * });\n * ```\n */\n createEndpoint<\n ResSchema extends ResponseSchema,\n ReqSchema extends StandardSchemaV1 | undefined = undefined,\n QuerySchema extends StandardSchemaV1 | undefined = undefined,\n PathSchema extends StandardSchemaV1 | undefined = undefined,\n MustHeaderKeys extends readonly string[] = readonly [],\n >(\n config: EndpointConfig<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys>\n ): EndpointCall<ResSchema, ReqSchema, QuerySchema, PathSchema, MustHeaderKeys> {\n const endpoint = new EndpointImpl(this, config);\n return (params) => endpoint.call(params);\n }\n}\n"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -76,16 +76,19 @@ export type BaseUrlMap = {
76
76
  * @example
77
77
  * ```ts
78
78
  * {
79
- * maxRetries: 3,
79
+ * maxAttempts: 3,
80
80
  * baseDelayMs: 1000,
81
81
  * jitter: 0.2,
82
- * retryMethods: ['GET', 'HEAD', 'PUT']
82
+ * retryMethods: ['GET', 'HEAD', 'PUT'],
83
+ * retryStatusCodes: [500, 502, 503, 504],
84
+ * retryNetworkErrors: true,
85
+ * respectRetryAfter: true,
83
86
  * }
84
87
  * ```
85
88
  */
86
- export type RetryStrategy = {
89
+ export type RetryPolicy = {
87
90
  /** Maximum number of retry attempts */
88
- maxRetries: number;
91
+ maxAttempts: number;
89
92
  /** Base delay in milliseconds (will be exponentially increased) */
90
93
  baseDelayMs: number;
91
94
  /** Jitter factor 0..1 to randomize delays and prevent thundering herd */
@@ -93,13 +96,17 @@ export type RetryStrategy = {
93
96
  /** HTTP methods eligible for retry */
94
97
  retryMethods?: (keyof typeof HTTPMethod)[];
95
98
  /** HTTP status codes eligible for retry */
96
- retryStatusCodes?: HTTPStatusCodeKey[];
99
+ retryStatusCodes?: HTTPStatusCodeNumber[];
100
+ /** Whether to respect 'Retry-After' header if present in the response */
101
+ respectRetryAfter?: boolean;
97
102
  /** Custom function to determine if a request should be retried */
98
103
  shouldRetry?: (ctx: {
104
+ url: string;
105
+ method: HttpMethod;
106
+ status: HTTPStatusCodeNumber;
99
107
  attempt: number;
100
- error?: unknown;
101
108
  response?: Response;
102
- }) => boolean;
109
+ }) => Promise<boolean> | boolean;
103
110
  };
104
111
  export declare const HTTPMethod: {
105
112
  readonly GET: "GET";
@@ -212,7 +219,7 @@ export interface TimeoutOptions {
212
219
  * const options: ClientOptions = {
213
220
  * baseUrls: { default: 'https://api.example.com' },
214
221
  * headers: { 'X-API-Version': '1.0' },
215
- * retry: { maxRetries: 3, baseDelayMs: 1000 },
222
+ * retry: { maxAttempts: 3, baseDelayMs: 1000 },
216
223
  * timeout: { requestTimeoutMs: 30000 }
217
224
  * }
218
225
  * ```
@@ -225,7 +232,7 @@ export interface ClientOptions {
225
232
  /** Default headers applied to all requests */
226
233
  headers?: Record<string, string>;
227
234
  /** Retry strategy configuration */
228
- retry?: RetryStrategy;
235
+ retry?: RetryPolicy;
229
236
  /** Request/response interceptors */
230
237
  interceptors?: Interceptors;
231
238
  /** Timeout configuration */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zlient",
3
- "version": "3.0.2",
3
+ "version": "3.1.0",
4
4
  "homepage": "https://emirhangumus.github.io/zlient/",
5
5
  "author": "Emirhan Gümüş",
6
6
  "repository": {
@@ -71,7 +71,7 @@
71
71
  "prepublishOnly": "npm run build",
72
72
  "lint": "eslint lib/**/*.ts",
73
73
  "lint:fix": "eslint lib/**/*.ts --fix",
74
- "format": "prettier --write \"lib/**/*.ts\"",
74
+ "format": "prettier --write \"lib/**/*.ts\" \"test/**/*.ts\" \"docs/**/*.md\"",
75
75
  "format:check": "prettier --check \"lib/**/*.ts\"",
76
76
  "typecheck": "tsc --noEmit",
77
77
  "test": "bun test",