@api-wrappers/api-core 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,6 +22,7 @@ their internal HTTP layer consistent and testable.
22
22
  - Built-in auth, cache, logger, rate-limit, retry, and timeout plugins.
23
23
  - Fetch transport with JSON bodies, raw string bodies, abort signals, and
24
24
  timeout handling.
25
+ - Response parsing for JSON, text, and binary payloads.
25
26
  - Query string support for primitives and repeated array values.
26
27
  - ESM and CommonJS builds with TypeScript declarations.
27
28
 
@@ -168,6 +169,16 @@ const games = await client.post<Game[]>(
168
169
  );
169
170
  ```
170
171
 
172
+ Binary responses can stay on the shared request path by selecting an explicit
173
+ response type:
174
+
175
+ ```ts
176
+ const bytes = await client.post<ArrayBuffer>("/games.pb", query, {
177
+ headers: { accept: "application/octet-stream" },
178
+ responseType: "arrayBuffer",
179
+ });
180
+ ```
181
+
171
182
  ### Response Metadata
172
183
 
173
184
  Use `requestWithResponse` when a wrapper needs more than the parsed body:
package/dist/index.cjs CHANGED
@@ -217,6 +217,9 @@ function isPlainObject(value) {
217
217
  }
218
218
  //#endregion
219
219
  //#region src/transport/fetchTransport.ts
220
+ const defaultFetch = (input, init) => {
221
+ return globalThis.fetch(input, init);
222
+ };
220
223
  /**
221
224
  * Creates a {@link Transport} backed by the provided `fetch` function.
222
225
  * Use this when you need a polyfill or a custom fetch interceptor:
@@ -228,7 +231,7 @@ function isPlainObject(value) {
228
231
  * const transport = createFetchTransport(nodeFetch as typeof globalThis.fetch);
229
232
  * ```
230
233
  */
231
- function createFetchTransport(fetchFn = globalThis.fetch) {
234
+ function createFetchTransport(fetchFn = defaultFetch) {
232
235
  return { async execute(ctx) {
233
236
  const url = buildUrl(ctx.url, ctx.query);
234
237
  const init = {
@@ -457,7 +460,7 @@ var BaseHttpClient = class {
457
460
  }
458
461
  throw err;
459
462
  }
460
- const parsedBody = await parseBody(rawResponse);
463
+ const parsedBody = await parseBody(rawResponse, rawResponse.ok ? options.responseType : options.errorResponseType ?? "auto");
461
464
  let resCtx = {
462
465
  request: ctx,
463
466
  response: rawResponse,
@@ -598,11 +601,15 @@ var BaseHttpClient = class {
598
601
  await sleep(Math.round(ms));
599
602
  }
600
603
  };
601
- async function parseBody(response) {
604
+ async function parseBody(response, responseType = "auto") {
605
+ if (responseType === "arrayBuffer") return response.arrayBuffer();
606
+ if (responseType === "blob") return response.blob();
602
607
  if (response.status === 204 || response.status === 205) return void 0;
603
608
  if (response.headers.get("content-length") === "0") return void 0;
604
609
  const text = await response.text();
610
+ if (responseType === "text") return text;
605
611
  if (!text) return void 0;
612
+ if (responseType === "json") return JSON.parse(text);
606
613
  if ((response.headers.get("content-type") ?? "").includes("application/json")) return JSON.parse(text);
607
614
  return text;
608
615
  }
@@ -978,7 +985,56 @@ function createTimeoutPlugin(options) {
978
985
  * query string while still giving GraphQL-aware tooling a familiar `gql` tag.
979
986
  */
980
987
  function gql(chunks, ...values) {
981
- return chunks.reduce((source, chunk, index) => `${source}${chunk}${index in values ? String(values[index]) : ""}`, "");
988
+ return dedupeFragmentDefinitions(chunks.reduce((source, chunk, index) => `${source}${chunk}${index in values ? String(values[index]) : ""}`, ""));
989
+ }
990
+ function dedupeFragmentDefinitions(source) {
991
+ const seen = /* @__PURE__ */ new Set();
992
+ const fragmentPattern = /\bfragment\s+([_A-Za-z][_0-9A-Za-z]*)\s+on\s+[_A-Za-z][_0-9A-Za-z]*/g;
993
+ let result = "";
994
+ let cursor = 0;
995
+ let match = fragmentPattern.exec(source);
996
+ while (match) {
997
+ const name = match[1];
998
+ if (!name) {
999
+ match = fragmentPattern.exec(source);
1000
+ continue;
1001
+ }
1002
+ const bodyStart = source.indexOf("{", fragmentPattern.lastIndex);
1003
+ if (bodyStart === -1) {
1004
+ match = fragmentPattern.exec(source);
1005
+ continue;
1006
+ }
1007
+ const bodyEnd = findMatchingBrace(source, bodyStart);
1008
+ if (bodyEnd === -1) {
1009
+ match = fragmentPattern.exec(source);
1010
+ continue;
1011
+ }
1012
+ const fragmentStart = match.index;
1013
+ const fragmentEnd = consumeTrailingWhitespace(source, bodyEnd + 1);
1014
+ if (!seen.has(name)) {
1015
+ seen.add(name);
1016
+ result += source.slice(cursor, fragmentEnd);
1017
+ } else result += source.slice(cursor, fragmentStart);
1018
+ cursor = fragmentEnd;
1019
+ fragmentPattern.lastIndex = fragmentEnd;
1020
+ match = fragmentPattern.exec(source);
1021
+ }
1022
+ return result + source.slice(cursor);
1023
+ }
1024
+ function findMatchingBrace(source, openBraceIndex) {
1025
+ let depth = 0;
1026
+ for (let index = openBraceIndex; index < source.length; index++) {
1027
+ const char = source[index];
1028
+ if (char === "{") depth++;
1029
+ if (char === "}") depth--;
1030
+ if (depth === 0) return index;
1031
+ }
1032
+ return -1;
1033
+ }
1034
+ function consumeTrailingWhitespace(source, index) {
1035
+ let next = index;
1036
+ while (next < source.length && /\s/.test(source[next] ?? "")) next++;
1037
+ return next;
982
1038
  }
983
1039
  //#endregion
984
1040
  exports.ApiError = ApiError;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/graphql/GraphQLRequestError.ts","../src/plugin/PluginManager.ts","../src/errors/TimeoutError.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/transport/fetchTransport.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/gql.ts"],"sourcesContent":["export class ApiError extends Error {\n\treadonly status: number;\n\treadonly responseBody: unknown;\n\toverride readonly cause: unknown;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tstatus: number,\n\t\tresponseBody?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t\tthis.status = status;\n\t\tthis.responseBody = responseBody;\n\t\tthis.cause = cause;\n\t}\n}\n","import { ApiError } from \"./ApiError\";\n\nexport class RateLimitError extends ApiError {\n\treadonly retryAfterMs: number | undefined;\n\n\tconstructor(retryAfterMs?: number, responseBody?: unknown, cause?: unknown) {\n\t\tsuper(\"Rate limit exceeded\", 429, responseBody, cause);\n\t\tthis.name = \"RateLimitError\";\n\t\tthis.retryAfterMs = retryAfterMs;\n\t}\n}\n","import { ApiError } from \"../errors/ApiError\";\nimport type { GraphQLErrorDetail } from \"./types\";\n\n/**\n * Thrown when a GraphQL server returns a well-formed HTTP 200 response that\n * contains a non-empty `errors` array.\n *\n * Extends {@link ApiError} so that code catching `ApiError` also catches\n * GraphQL-level failures. Callers that need to inspect the individual error\n * objects can narrow with `instanceof GraphQLRequestError` and read\n * `graphqlErrors`.\n *\n * When the server returns both `data` and `errors` (partial result), the\n * partial data is available on `partialData` but the error is still thrown —\n * callers must explicitly opt in to consuming partial results.\n *\n * @example\n * ```ts\n * import { GraphQLRequestError } from \"@api-wrappers/api-core\";\n *\n * try {\n * const data = await client.graphql<MyQuery>(\"/graphql\", { query: QUERY });\n * } catch (err) {\n * if (err instanceof GraphQLRequestError) {\n * for (const e of err.graphqlErrors) {\n * console.error(e.message, e.path);\n * }\n * }\n * }\n * ```\n */\nexport class GraphQLRequestError extends ApiError {\n\t/** The errors array from the GraphQL response envelope. */\n\treadonly graphqlErrors: readonly GraphQLErrorDetail[];\n\t/**\n\t * Partial `data` returned alongside `errors`, if any. `undefined` when\n\t * the server returned no `data` field.\n\t */\n\treadonly partialData: unknown;\n\n\tconstructor(\n\t\terrors: GraphQLErrorDetail[],\n\t\tpartialData?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tconst message = errors.map((e) => e.message).join(\"; \");\n\t\t// Status 200: the HTTP request succeeded; the failure is at the\n\t\t// GraphQL application layer, not the transport layer.\n\t\tsuper(`GraphQL errors: ${message}`, 200, { errors }, cause);\n\t\tthis.name = \"GraphQLRequestError\";\n\t\tthis.graphqlErrors = errors;\n\t\tthis.partialData = partialData;\n\t}\n}\n","import type { LoggerInterface } from \"../client/types\";\nimport type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport type { ApiPlugin } from \"./types\";\n\nexport class PluginManager {\n\tprivate readonly plugins: ApiPlugin[] = [];\n\tprivate readonly logger: LoggerInterface;\n\n\t/**\n\t * @param logger - Logger used when an `onError` handler itself throws.\n\t * Defaults to `console`. Pass a no-op object to silence all output.\n\t */\n\tconstructor(logger: LoggerInterface = console) {\n\t\tthis.logger = logger;\n\t}\n\n\tregister(plugin: ApiPlugin): void {\n\t\tif (plugin.enabled === false) return;\n\t\tthis.plugins.push(plugin);\n\t\t// Sort ascending so lower priority numbers run first in beforeRequest.\n\t\tthis.plugins.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));\n\t}\n\n\tgetAll(): readonly ApiPlugin[] {\n\t\treturn this.plugins;\n\t}\n\n\tasync setup(client: unknown): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\tawait plugin.setup?.(client);\n\t\t}\n\t}\n\n\t/**\n\t * Runs `beforeRequest` in ascending priority order (lowest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync beforeRequest(ctx: RequestContext): Promise<RequestContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.beforeRequest?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"beforeRequest\", err, current);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `afterResponse` in descending priority order (highest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync afterResponse(ctx: ResponseContext): Promise<ResponseContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.afterResponse?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"afterResponse\", err);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `onError` on all plugins in registration order. A plugin throwing\n\t * here is caught and logged via the configured logger but does not\n\t * interrupt other `onError` handlers.\n\t */\n\tasync onError(error: unknown, ctx: RequestContext): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tawait plugin.onError?.(error, ctx);\n\t\t\t} catch (inner) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t`[PluginManager] Plugin \"${plugin.name}\" threw inside onError:`,\n\t\t\t\t\tinner,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\tawait plugin.dispose?.();\n\t\t}\n\t}\n}\n\nexport function getPluginErrorContext(\n\terror: unknown,\n): RequestContext | undefined {\n\tif (!error || typeof error !== \"object\") return undefined;\n\treturn (error as { requestContext?: RequestContext }).requestContext;\n}\n\nfunction wrapPluginError(\n\tname: string,\n\thook: string,\n\tcause: unknown,\n\trequestContext?: RequestContext,\n): Error {\n\tconst message = `Plugin \"${name}\" threw during \"${hook}\"`;\n\tconst err = new Error(message, { cause }) as Error & {\n\t\trequestContext?: RequestContext;\n\t};\n\terr.name = \"PluginError\";\n\terr.requestContext = requestContext;\n\treturn err;\n}\n","export class TimeoutError extends Error {\n\toverride readonly cause: unknown;\n\n\tconstructor(message = \"Request timed out\", cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"TimeoutError\";\n\t\tthis.cause = cause;\n\t}\n}\n","import type { QueryParams } from \"../types/common\";\n\n/**\n * Appends a query string to a URL. Skips nullish values and repeats keys for\n * array values so APIs like TMDB can accept `with_genres=1&with_genres=2`.\n */\nexport function buildUrl(base: string, query?: QueryParams): string {\n\tif (!query || Object.keys(query).length === 0) return base;\n\n\tconst params = new URLSearchParams();\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tif (value === undefined || value === null) continue;\n\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\tparams.append(key, String(item));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tparams.append(key, String(value));\n\t\t}\n\t}\n\n\tconst qs = params.toString();\n\tif (!qs) return base;\n\n\tconst separator = base.includes(\"?\")\n\t\t? base.endsWith(\"?\") || base.endsWith(\"&\")\n\t\t\t? \"\"\n\t\t\t: \"&\"\n\t\t: \"?\";\n\treturn `${base}${separator}${qs}`;\n}\n","export function isPlainObject(\n\tvalue: unknown,\n): value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst proto = Object.getPrototypeOf(value) as unknown;\n\treturn proto === Object.prototype || proto === null;\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport { TimeoutError } from \"../errors/TimeoutError\";\nimport { buildUrl } from \"../utils/buildUrl\";\nimport { isPlainObject } from \"../utils/isPlainObject\";\nimport type { Transport } from \"./types\";\n\n/**\n * Creates a {@link Transport} backed by the provided `fetch` function.\n * Use this when you need a polyfill or a custom fetch interceptor:\n *\n * ```ts\n * import nodeFetch from \"node-fetch\";\n * createClient({ fetch: nodeFetch as typeof globalThis.fetch });\n * // — or set it directly on the transport:\n * const transport = createFetchTransport(nodeFetch as typeof globalThis.fetch);\n * ```\n */\nexport function createFetchTransport(\n\tfetchFn: typeof globalThis.fetch = globalThis.fetch,\n): Transport {\n\treturn {\n\t\tasync execute(ctx: RequestContext): Promise<Response> {\n\t\t\tconst url = buildUrl(ctx.url, ctx.query);\n\t\t\tconst init: RequestInit = {\n\t\t\t\tmethod: ctx.method,\n\t\t\t\theaders: ctx.headers,\n\t\t\t};\n\n\t\t\tconst hasBody =\n\t\t\t\tctx.body !== undefined && ctx.method !== \"GET\" && ctx.method !== \"HEAD\";\n\n\t\t\tif (hasBody) {\n\t\t\t\tinit.body = serializeRequestBody(ctx.body, ctx.headers);\n\t\t\t}\n\n\t\t\tif (ctx.timeoutMs !== undefined || ctx.signal) {\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tlet timedOut = false;\n\t\t\t\tconst abortFromParent = () => controller.abort(ctx.signal?.reason);\n\t\t\t\tconst timer =\n\t\t\t\t\tctx.timeoutMs !== undefined\n\t\t\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t\t\t}, ctx.timeoutMs)\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tif (ctx.signal) {\n\t\t\t\t\tif (ctx.signal.aborted) {\n\t\t\t\t\t\tcontroller.abort(ctx.signal.reason);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.signal.addEventListener(\"abort\", abortFromParent, {\n\t\t\t\t\t\t\tonce: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treturn await fetchFn(url, { ...init, signal: controller.signal });\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (timedOut && err instanceof Error && err.name === \"AbortError\") {\n\t\t\t\t\t\tthrow new TimeoutError(\n\t\t\t\t\t\t\t`Request timed out after ${ctx.timeoutMs}ms`,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t} finally {\n\t\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\t\tctx.signal?.removeEventListener(\"abort\", abortFromParent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn fetchFn(url, init);\n\t\t},\n\t};\n}\n\n/**\n * Default {@link Transport} backed by the global `fetch` API.\n *\n * Behaviour:\n * - Builds the final URL from `ctx.url` + `ctx.query` via {@link buildUrl}.\n * - Serialises `ctx.body` to JSON for non-GET/HEAD requests.\n * - Wires an `AbortController` when `ctx.timeoutMs` is set; throws\n * {@link TimeoutError} on abort.\n *\n * Replace this with a custom {@link Transport} in tests, or provide a custom\n * `fetch` function via {@link ClientConfig.fetch}.\n */\nexport const fetchTransport: Transport = createFetchTransport();\n\nfunction serializeRequestBody(\n\tbody: unknown,\n\theaders: Record<string, string>,\n): BodyInit {\n\tif (isBodyInit(body)) return body;\n\n\tconst contentType = headers[\"content-type\"] ?? \"\";\n\tif (\n\t\tisPlainObject(body) ||\n\t\tArray.isArray(body) ||\n\t\tcontentType.includes(\"json\")\n\t) {\n\t\treturn JSON.stringify(body);\n\t}\n\n\treturn String(body);\n}\n\nfunction isBodyInit(body: unknown): body is BodyInit {\n\tif (typeof body === \"string\") return true;\n\tif (body instanceof ArrayBuffer) return true;\n\tif (ArrayBuffer.isView(body)) return true;\n\tif (typeof Blob !== \"undefined\" && body instanceof Blob) return true;\n\tif (typeof FormData !== \"undefined\" && body instanceof FormData) return true;\n\tif (\n\t\ttypeof URLSearchParams !== \"undefined\" &&\n\t\tbody instanceof URLSearchParams\n\t) {\n\t\treturn true;\n\t}\n\tif (typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n","/**\n * Merges header objects left to right. Keys are normalized to\n * lowercase so merging is case-insensitive. Later sources win.\n */\nexport function mergeHeaders(\n\t...sources: (Record<string, string> | undefined)[]\n): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tfor (const [key, value] of Object.entries(source)) {\n\t\t\tresult[key.toLowerCase()] = value;\n\t\t}\n\t}\n\treturn result;\n}\n","/**\n * Joins a client base URL and request path without requiring callers to keep\n * slashes perfectly aligned. Absolute request URLs are returned unchanged.\n */\nexport function resolveUrl(baseUrl: string, path: string): string {\n\tif (/^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(path)) return path;\n\n\tconst base = baseUrl.replace(/\\/+$/, \"\");\n\tconst next = path.replace(/^\\/+/, \"\");\n\n\treturn next ? `${base}/${next}` : base;\n}\n","export function sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport { ApiError } from \"../errors/ApiError\";\nimport { RateLimitError } from \"../errors/RateLimitError\";\nimport { GraphQLRequestError } from \"../graphql/GraphQLRequestError\";\nimport type { GraphQLRequestOptions, GraphQLResponse } from \"../graphql/types\";\nimport { getPluginErrorContext, PluginManager } from \"../plugin/PluginManager\";\nimport {\n\tcreateFetchTransport,\n\tfetchTransport,\n} from \"../transport/fetchTransport\";\nimport type { HttpMethod, QueryParams } from \"../types/common\";\nimport { mergeHeaders } from \"../utils/mergeHeaders\";\nimport { resolveUrl } from \"../utils/resolveUrl\";\nimport { sleep } from \"../utils/sleep\";\nimport type { ClientConfig } from \"./types\";\n\n/** Per-request options passed to {@link BaseHttpClient.request} and the convenience methods. */\nexport interface RequestOptions {\n\t/** HTTP method. Defaults to `\"GET\"`. */\n\tmethod?: HttpMethod;\n\t/**\n\t * Additional headers merged on top of `ClientConfig.defaultHeaders`.\n\t * These take precedence; `content-type: application/json` is always\n\t * present and is the lowest-priority default.\n\t */\n\theaders?: Record<string, string>;\n\t/** Request body. Serialised to JSON by {@link fetchTransport}. Ignored for GET and HEAD. */\n\tbody?: unknown;\n\t/**\n\t * Query string parameters appended to the URL. `undefined` values are\n\t * omitted. Numbers and booleans are coerced to strings. Array values are\n\t * emitted as repeated query parameters.\n\t */\n\tquery?: QueryParams;\n\t/** Optional caller-provided abort signal. Composes with `timeoutMs`. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Per-request timeout override in milliseconds. Takes precedence over\n\t * `ClientConfig.timeoutMs`. Throws {@link TimeoutError} when exceeded.\n\t */\n\ttimeoutMs?: number;\n\t/**\n\t * Explicit cache key used by {@link createCachePlugin}. When omitted the\n\t * plugin derives a key from the method, URL, and query string.\n\t */\n\tcacheKey?: string;\n\t/**\n\t * Arbitrary string tags attached to the request context. Plugins may use\n\t * these for cache invalidation, metrics grouping, or filtering.\n\t */\n\ttags?: string[];\n}\n\nexport interface ApiResponse<T = unknown> {\n\tdata: T;\n\tresponse: Response;\n\trequest: RequestContext;\n\tmeta: Record<string, unknown>;\n}\n\nconst DEFAULT_RETRIABLE_STATUS_CODES = [429, 500, 502, 503, 504];\n\n/**\n * Core HTTP client. Manages the plugin lifecycle, retry loop, and transport\n * dispatch for all requests.\n *\n * Plugins are initialised lazily on the first call to {@link request} (or any\n * convenience method). Call {@link dispose} when the client is no longer\n * needed so plugins can release timers, connections, or cache handles.\n *\n * Extend this class to add domain-specific methods while keeping the plugin\n * and transport infrastructure intact.\n *\n * @example\n * ```ts\n * // Prefer createClient() in application code:\n * const client = createClient({ baseUrl: \"https://api.example.com/v1\" });\n *\n * // Or subclass for wrapper packages:\n * class MyApiClient extends BaseHttpClient {\n * getUser(id: string) { return this.get<User>(`/users/${id}`); }\n * }\n * ```\n */\nexport class BaseHttpClient {\n\tprotected readonly config: ClientConfig;\n\tprotected readonly pluginManager: PluginManager;\n\tprivate initialized = false;\n\tprivate initPromise: Promise<void> | undefined;\n\n\tconstructor(config: ClientConfig) {\n\t\tthis.config = config;\n\t\tthis.pluginManager = new PluginManager(config.logger);\n\t\tfor (const plugin of config.plugins ?? []) {\n\t\t\tthis.pluginManager.register(plugin);\n\t\t}\n\t}\n\n\t/** Initializes all plugins. Called lazily on first request. */\n\tasync init(): Promise<void> {\n\t\tif (this.initialized) return;\n\t\tthis.initPromise ??= this.pluginManager\n\t\t\t.setup(this)\n\t\t\t.then(() => {\n\t\t\t\tthis.initialized = true;\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\tthis.initPromise = undefined;\n\t\t\t\tthrow err;\n\t\t\t});\n\t\tawait this.initPromise;\n\t}\n\n\t/** Disposes all plugins. Call when the client is no longer needed. */\n\tasync dispose(): Promise<void> {\n\t\tawait this.pluginManager.dispose();\n\t\tthis.initialized = false;\n\t\tthis.initPromise = undefined;\n\t}\n\n\t/**\n\t * Executes an HTTP request through the full plugin pipeline.\n\t *\n\t * Lifecycle per attempt:\n\t * 1. Build `RequestContext` with merged headers, query, and retry state.\n\t * 2. Run `beforeRequest` hooks (ascending priority). A plugin may set\n\t * `ctx.syntheticResponse` to skip the transport entirely (e.g. cache hit).\n\t * 3. Merge any `retry.*` meta written by {@link createRetryPlugin}.\n\t * 4. Call transport (skipped when `syntheticResponse` is set).\n\t * 5. Parse the response body (JSON or text).\n\t * 6. Run `afterResponse` hooks (descending priority).\n\t * 7. Retry on retriable status codes; throw on terminal failures.\n\t *\n\t * @param path - Path appended to `ClientConfig.baseUrl`. Should start with `/`.\n\t * @param options - Per-request overrides for method, headers, body, query, etc.\n\t * @returns The parsed response body cast to `T`.\n\t * @throws {@link ApiError} for non-2xx responses.\n\t * @throws {@link RateLimitError} for 429 responses.\n\t * @throws {@link TimeoutError} when `timeoutMs` is exceeded.\n\t */\n\tasync request<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<T> {\n\t\tconst result = await this.requestWithResponse<T>(path, options);\n\t\treturn result.data;\n\t}\n\n\t/**\n\t * Executes a request and returns the parsed body plus the final response\n\t * context. Use this in wrappers that need response headers, status, or\n\t * plugin metadata while keeping the same error/retry behaviour as\n\t * {@link request}.\n\t */\n\tasync requestWithResponse<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<ApiResponse<T>> {\n\t\tawait this.init();\n\n\t\tconst transport =\n\t\t\tthis.config.transport ??\n\t\t\t(this.config.fetch\n\t\t\t\t? createFetchTransport(this.config.fetch)\n\t\t\t\t: fetchTransport);\n\t\tconst retryCfg = this.config.retry;\n\t\t// These are `let` so createRetryPlugin can override them per-request via\n\t\t// ctx.meta after beforeRequest runs (see merge block below).\n\t\tlet maxAttempts = retryCfg?.maxAttempts ?? 1;\n\t\tlet baseDelay = retryCfg?.delayMs ?? 500;\n\t\tlet jitter = retryCfg?.jitter ?? true;\n\t\tlet retriableCodes =\n\t\t\tretryCfg?.retriableStatusCodes ?? DEFAULT_RETRIABLE_STATUS_CODES;\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tconst baseCtx: RequestContext = {\n\t\t\t\turl: resolveUrl(this.config.baseUrl, path),\n\t\t\t\tmethod: options.method ?? \"GET\",\n\t\t\t\theaders: mergeHeaders(\n\t\t\t\t\t{ \"content-type\": \"application/json\" },\n\t\t\t\t\tthis.config.defaultHeaders,\n\t\t\t\t\toptions.headers,\n\t\t\t\t),\n\t\t\t\tbody: options.body,\n\t\t\t\tquery: options.query,\n\t\t\t\tsignal: options.signal,\n\t\t\t\tmeta: {},\n\t\t\t\tcacheKey: options.cacheKey,\n\t\t\t\ttags: options.tags,\n\t\t\t\tretryCount: maxAttempts - 1 - attempt,\n\t\t\t\tattempt,\n\t\t\t\ttimeoutMs: options.timeoutMs ?? this.config.timeoutMs,\n\t\t\t};\n\n\t\t\tlet ctx: RequestContext;\n\t\t\ttry {\n\t\t\t\tctx = await this.pluginManager.beforeRequest(baseCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(\n\t\t\t\t\terr,\n\t\t\t\t\tgetPluginErrorContext(err) ?? baseCtx,\n\t\t\t\t);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\t// Merge per-request retry overrides written by createRetryPlugin.\n\t\t\t// Only keys explicitly set by the plugin are present, so unset keys\n\t\t\t// leave the config-level defaults untouched.\n\t\t\t// Because the for-loop condition re-evaluates `attempt < maxAttempts`\n\t\t\t// on every iteration, updating maxAttempts here takes effect\n\t\t\t// immediately for all remaining attempts.\n\t\t\tif (ctx.meta[\"retry.maxAttempts\"] !== undefined)\n\t\t\t\tmaxAttempts = ctx.meta[\"retry.maxAttempts\"] as number;\n\t\t\tif (ctx.meta[\"retry.delayMs\"] !== undefined)\n\t\t\t\tbaseDelay = ctx.meta[\"retry.delayMs\"] as number;\n\t\t\tif (ctx.meta[\"retry.jitter\"] !== undefined)\n\t\t\t\tjitter = ctx.meta[\"retry.jitter\"] as boolean;\n\t\t\tif (ctx.meta[\"retry.retriableStatusCodes\"] !== undefined)\n\t\t\t\tretriableCodes = ctx.meta[\"retry.retriableStatusCodes\"] as number[];\n\n\t\t\tlet rawResponse: Response;\n\t\t\tif (ctx.syntheticResponse) {\n\t\t\t\t// A plugin (e.g. cache) pre-populated the response — skip the\n\t\t\t\t// network entirely.\n\t\t\t\trawResponse = ctx.syntheticResponse;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\trawResponse = await transport.execute(ctx);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\t\tlastError = err;\n\n\t\t\t\t\tif (attempt < maxAttempts - 1) {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst parsedBody = await parseBody(rawResponse);\n\n\t\t\tlet resCtx: ResponseContext = {\n\t\t\t\trequest: ctx,\n\t\t\t\tresponse: rawResponse,\n\t\t\t\tparsedBody,\n\t\t\t\tmeta: {},\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tresCtx = await this.pluginManager.afterResponse(resCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tif (!rawResponse.ok) {\n\t\t\t\tconst shouldRetry =\n\t\t\t\t\tretriableCodes.includes(rawResponse.status) &&\n\t\t\t\t\tattempt < maxAttempts - 1;\n\n\t\t\t\tif (shouldRetry) {\n\t\t\t\t\tif (rawResponse.status === 429) {\n\t\t\t\t\t\tconst wait = readRetryAfterMs(rawResponse);\n\t\t\t\t\t\tawait this.waitForRetry(attempt, wait ?? baseDelay, false);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t}\n\t\t\t\t\tlastError = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst err = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdata: resCtx.parsedBody as T,\n\t\t\t\tresponse: resCtx.response,\n\t\t\t\trequest: resCtx.request,\n\t\t\t\tmeta: resCtx.meta,\n\t\t\t};\n\t\t}\n\n\t\tthrow lastError;\n\t}\n\n\t// ─── Convenience methods ────────────────────────────────────────────────────\n\t// Each method is a thin wrapper around request() that fixes the HTTP verb.\n\n\t/** Sends a GET request. The response body is not cached unless a cache plugin is registered. */\n\tget<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"GET\" });\n\t}\n\n\tpost<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"POST\", body });\n\t}\n\n\tput<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PUT\", body });\n\t}\n\n\tpatch<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PATCH\", body });\n\t}\n\n\tdelete<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"DELETE\" });\n\t}\n\n\thead<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"HEAD\" });\n\t}\n\n\toptions<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"OPTIONS\" });\n\t}\n\n\t/**\n\t * Executes a GraphQL query or mutation against a single endpoint path.\n\t *\n\t * The request is a `POST` with `content-type: application/json` carrying\n\t * `{ query, variables?, operationName? }` as the body. It flows through\n\t * the full plugin lifecycle (beforeRequest → transport → afterResponse →\n\t * onError) and respects all retry configuration, exactly like REST calls.\n\t *\n\t * **Error handling:**\n\t * - HTTP-level failures (429, 500, timeout) throw the same error classes\n\t * as REST requests (`RateLimitError`, `ApiError`, `TimeoutError`).\n\t * - A successful HTTP 200 that contains a non-empty `errors` array throws\n\t * {@link GraphQLRequestError}, which extends `ApiError`.\n\t *\n\t * **Caching:**\n\t * The cache plugin skips `POST` requests by default. Pass an explicit\n\t * `cacheKey` in options to opt a specific operation into caching.\n\t *\n\t * @typeParam TData - Shape of the `data` field in the GraphQL response.\n\t * @typeParam TVariables - Shape of the `variables` object. Defaults to\n\t * `Record<string, unknown>`.\n\t * @param path - Endpoint path, e.g. `\"/graphql\"`. Appended to `baseUrl`.\n\t * @param options - Query document, variables, and optional per-request overrides.\n\t * @returns The `data` field from the GraphQL response envelope.\n\t * @throws {@link GraphQLRequestError} when `response.errors` is non-empty.\n\t * @throws {@link ApiError} / {@link RateLimitError} / {@link TimeoutError} on\n\t * HTTP-level failures.\n\t *\n\t * @example\n\t * ```ts\n\t * const data = await client.graphql<GetUserQuery, GetUserQueryVariables>(\n\t * \"/graphql\",\n\t * { query: GET_USER, variables: { id: \"123\" } },\n\t * );\n\t * ```\n\t */\n\tasync graphql<\n\t\tTData = unknown,\n\t\tTVariables extends object = Record<string, unknown>,\n\t>(path: string, options: GraphQLRequestOptions<TVariables>): Promise<TData> {\n\t\tconst {\n\t\t\tquery,\n\t\t\tvariables,\n\t\t\toperationName,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t} = options;\n\n\t\tconst envelope = await this.request<GraphQLResponse<TData>>(path, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: {\n\t\t\t\tquery,\n\t\t\t\t...(variables !== undefined && { variables }),\n\t\t\t\t...(operationName !== undefined && { operationName }),\n\t\t\t},\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t});\n\n\t\t// Surface GraphQL application-layer errors as a typed exception.\n\t\t// We throw even when partial data is present — callers who need\n\t\t// partial results can catch GraphQLRequestError and read .partialData.\n\t\tif (envelope.errors && envelope.errors.length > 0) {\n\t\t\tthrow new GraphQLRequestError(envelope.errors, envelope.data);\n\t\t}\n\n\t\t// data may be undefined if the server returned an empty response —\n\t\t// safe to cast because TData defaults to unknown.\n\t\treturn envelope.data as TData;\n\t}\n\n\tprivate async waitForRetry(\n\t\tattempt: number,\n\t\tbaseDelay: number,\n\t\tuseJitter: boolean,\n\t): Promise<void> {\n\t\t// Exponential backoff: delay * 2^attempt\n\t\tconst exponential = baseDelay * 2 ** attempt;\n\t\tconst ms = useJitter\n\t\t\t? exponential * (0.5 + Math.random() * 0.5)\n\t\t\t: exponential;\n\t\tawait sleep(Math.round(ms));\n\t}\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function parseBody(response: Response): Promise<unknown> {\n\tif (response.status === 204 || response.status === 205) return undefined;\n\tif (response.headers.get(\"content-length\") === \"0\") return undefined;\n\n\tconst text = await response.text();\n\tif (!text) return undefined;\n\n\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\tif (contentType.includes(\"application/json\")) {\n\t\treturn JSON.parse(text);\n\t}\n\treturn text;\n}\n\nfunction normalizeHttpError(response: Response, body: unknown): ApiError {\n\tif (response.status === 429) {\n\t\treturn new RateLimitError(readRetryAfterMs(response), body);\n\t}\n\treturn new ApiError(\n\t\t`Request failed with status ${response.status}`,\n\t\tresponse.status,\n\t\tbody,\n\t);\n}\n\nfunction readRetryAfterMs(response: Response): number | undefined {\n\tconst raw = response.headers.get(\"retry-after\");\n\tif (!raw) return undefined;\n\n\tconst seconds = Number(raw);\n\tif (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n\n\tconst date = Date.parse(raw);\n\tif (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n\n\treturn undefined;\n}\n","import { BaseHttpClient } from \"./BaseHttpClient\";\nimport type { ClientConfig } from \"./types\";\n\n/**\n * Factory function that creates a {@link BaseHttpClient} from the given\n * config. Prefer this over `new BaseHttpClient(config)` in application code\n * so that the concrete class stays an implementation detail.\n *\n * @example\n * ```ts\n * const client = createClient({\n * baseUrl: \"https://api.example.com/v1\",\n * defaultHeaders: { \"x-api-key\": \"secret\" },\n * retry: { maxAttempts: 3, delayMs: 300 },\n * plugins: [createLoggerPlugin(), createCachePlugin({ ttlMs: 60_000 })],\n * });\n * ```\n */\nexport function createClient(config: ClientConfig): BaseHttpClient {\n\treturn new BaseHttpClient(config);\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { MaybePromise } from \"../../types/common\";\nimport type { AuthPluginOptions } from \"./types\";\n\ntype TokenInput =\n\t| string\n\t| (() => MaybePromise<string | null | undefined>)\n\t| AuthPluginOptions;\n\n/**\n * Adds an auth token header before each request. The token can be static or\n * loaded asynchronously per request, which covers wrappers with refreshable\n * access tokens.\n */\nexport function createAuthPlugin(input: TokenInput): ApiPlugin {\n\tconst options = normalizeOptions(input);\n\tconst headerName = (options.headerName ?? \"authorization\").toLowerCase();\n\tconst scheme = options.scheme === undefined ? \"Bearer\" : options.scheme;\n\n\treturn {\n\t\tname: \"auth\",\n\t\tpriority: 2,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst token = await options.getToken();\n\t\t\tif (!token) return ctx;\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\theaders: {\n\t\t\t\t\t...ctx.headers,\n\t\t\t\t\t[headerName]: scheme ? `${scheme} ${token}` : token,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction normalizeOptions(input: TokenInput): AuthPluginOptions {\n\tif (typeof input === \"string\") {\n\t\treturn { getToken: () => input };\n\t}\n\tif (typeof input === \"function\") {\n\t\treturn { getToken: input };\n\t}\n\treturn input;\n}\n","import type { CacheStore } from \"./types\";\n\ninterface CacheEntry {\n\tvalue: unknown;\n\texpiresAt: number | null;\n}\n\nexport class MemoryStore implements CacheStore {\n\tprivate readonly store = new Map<string, CacheEntry>();\n\n\tget(key: string): unknown | undefined {\n\t\tconst entry = this.store.get(key);\n\t\tif (!entry) return undefined;\n\t\tif (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n\t\t\tthis.store.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn entry.value;\n\t}\n\n\tset(key: string, value: unknown, ttlMs?: number): void {\n\t\tthis.store.set(key, {\n\t\t\tvalue,\n\t\t\texpiresAt: ttlMs != null ? Date.now() + ttlMs : null,\n\t\t});\n\t}\n\n\tdelete(key: string): void {\n\t\tthis.store.delete(key);\n\t}\n\n\tclear(): void {\n\t\tthis.store.clear();\n\t}\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport { MemoryStore } from \"./memoryStore\";\nimport type { CachePlugin, CachePluginOptions } from \"./types\";\n\nconst DEFAULT_CACHEABLE_METHODS = [\"GET\"] as const;\nconst CACHE_HIT_META_KEY = \"cache.hit\";\n\nexport function createCachePlugin(\n\toptions: CachePluginOptions = {},\n): CachePlugin {\n\tconst store = options.store ?? new MemoryStore();\n\tconst ttlMs = options.ttlMs;\n\tconst methods: string[] = options.methods ?? [...DEFAULT_CACHEABLE_METHODS];\n\tconst generateKey = options.generateKey ?? defaultCacheKey;\n\n\t// tag → Set<cacheKey>: populated during afterResponse, used by invalidateByTag.\n\tconst tagIndex = new Map<string, Set<string>>();\n\n\treturn {\n\t\tname: \"cache\",\n\t\tpriority: 20,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tif (!methods.includes(ctx.method)) return ctx;\n\n\t\t\tconst key = ctx.cacheKey ?? generateKey(ctx);\n\t\t\tconst cached = await store.get(key);\n\n\t\t\tif (cached !== undefined) {\n\t\t\t\t// Build a synthetic Response so the rest of the pipeline\n\t\t\t\t// (afterResponse, status checks) sees a uniform shape.\n\t\t\t\tconst syntheticResponse = new Response(JSON.stringify(cached), {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\n\t\t\t\t// Setting syntheticResponse tells BaseHttpClient to skip the\n\t\t\t\t// transport entirely and use this response directly.\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\t...ctx.meta,\n\t\t\t\t\t\t[CACHE_HIT_META_KEY]: { key, data: cached },\n\t\t\t\t\t},\n\t\t\t\t\tsyntheticResponse,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, \"cache.key\": key },\n\t\t\t};\n\t\t},\n\n\t\tasync afterResponse(ctx) {\n\t\t\tconst hit = ctx.request.meta[CACHE_HIT_META_KEY] as\n\t\t\t\t| { key: string; data: unknown }\n\t\t\t\t| undefined;\n\n\t\t\tif (hit) {\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tparsedBody: hit.data,\n\t\t\t\t\tmeta: { ...ctx.meta, \"cache.served\": true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst key = ctx.request.meta[\"cache.key\"] as string | undefined;\n\t\t\tif (key && methods.includes(ctx.request.method) && ctx.response.ok) {\n\t\t\t\tawait store.set(key, ctx.parsedBody, ttlMs);\n\t\t\t\tctx.meta[\"cache.stored\"] = true;\n\n\t\t\t\t// Record tag → key associations for invalidateByTag.\n\t\t\t\tfor (const tag of ctx.request.tags ?? []) {\n\t\t\t\t\tif (!tagIndex.has(tag)) tagIndex.set(tag, new Set());\n\t\t\t\t\ttagIndex.get(tag)?.add(key);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ctx;\n\t\t},\n\n\t\tasync invalidate(key: string): Promise<void> {\n\t\t\tawait store.delete(key);\n\t\t\t// Clean up any tag index entries pointing to this key.\n\t\t\tfor (const keys of tagIndex.values()) {\n\t\t\t\tkeys.delete(key);\n\t\t\t}\n\t\t},\n\n\t\tasync invalidateByTag(tag: string): Promise<void> {\n\t\t\tconst keys = tagIndex.get(tag);\n\t\t\tif (!keys || keys.size === 0) return;\n\t\t\tfor (const key of keys) {\n\t\t\t\tawait store.delete(key);\n\t\t\t\t// Remove this key from all other tag index entries too.\n\t\t\t\tfor (const otherKeys of tagIndex.values()) {\n\t\t\t\t\totherKeys.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttagIndex.delete(tag);\n\t\t},\n\t};\n}\n\nfunction defaultCacheKey(ctx: RequestContext): string {\n\tconst queryStr = ctx.query\n\t\t? new URLSearchParams(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tObject.entries(ctx.query)\n\t\t\t\t\t\t.filter(([, v]) => v !== undefined)\n\t\t\t\t\t\t.map(([k, v]) => [k, String(v)]),\n\t\t\t\t),\n\t\t\t).toString()\n\t\t: \"\";\n\treturn `${ctx.method}:${ctx.url}${queryStr ? `?${queryStr}` : \"\"}`;\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { LoggerPluginOptions } from \"./types\";\n\n/**\n * Creates a plugin that logs request start, response status, and errors.\n *\n * Log lines are prefixed with `[api-core]` and include the HTTP method, URL,\n * attempt number (on `beforeRequest`), and status code (on `afterResponse`).\n *\n * Priority `10` means it runs _after_ auth or header-mutation plugins\n * (priority < 10) so the logged URL and headers reflect the final request,\n * but _before_ the cache plugin (priority `20`) so cache hits are still\n * visible in the log.\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createLoggerPlugin({ logRequest: true, logResponse: true })],\n * });\n * ```\n */\nexport function createLoggerPlugin(\n\toptions: LoggerPluginOptions = {},\n): ApiPlugin {\n\tconst {\n\t\tlogRequest = true,\n\t\tlogResponse = true,\n\t\tlogError = true,\n\t\tlogger = console,\n\t} = options;\n\n\treturn {\n\t\tname: \"logger\",\n\t\tpriority: 10,\n\n\t\tbeforeRequest(ctx) {\n\t\t\tif (logRequest) {\n\t\t\t\tlogger.info(`[api-core] --> ${ctx.method} ${ctx.url}`, {\n\t\t\t\t\tattempt: ctx.attempt,\n\t\t\t\t\tbody: ctx.body,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\tif (logResponse) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`[api-core] <-- ${ctx.response.status} ${ctx.request.method} ${ctx.request.url}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(error, ctx) {\n\t\t\tif (logError) {\n\t\t\t\tlogger.error(`[api-core] ERR ${ctx.method} ${ctx.url}`, error);\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport type { ApiPlugin } from \"../../plugin/types\";\nimport type { RateLimitPluginOptions } from \"./types\";\n\nconst RELEASE_META_KEY = \"rateLimit.release\";\n\ninterface QueueItem {\n\tresolve: (release: () => void) => void;\n}\n\n/**\n * Throttles request starts before they reach the transport. Supports\n * concurrency, minimum spacing, and fixed-window request budgets.\n */\nexport function createRateLimitPlugin(\n\toptions: RateLimitPluginOptions = {},\n): ApiPlugin {\n\tconst maxConcurrent = options.maxConcurrent ?? Number.POSITIVE_INFINITY;\n\tconst minTimeMs = options.minTimeMs ?? 0;\n\tconst maxRequestsPerInterval = options.maxRequestsPerInterval;\n\tconst intervalMs = options.intervalMs;\n\n\tif (maxConcurrent <= 0) {\n\t\tthrow new Error(\"maxConcurrent must be greater than 0\");\n\t}\n\tif (minTimeMs < 0) {\n\t\tthrow new Error(\"minTimeMs must be greater than or equal to 0\");\n\t}\n\tif (\n\t\t(maxRequestsPerInterval !== undefined || intervalMs !== undefined) &&\n\t\t(!maxRequestsPerInterval ||\n\t\t\tmaxRequestsPerInterval <= 0 ||\n\t\t\t!intervalMs ||\n\t\t\tintervalMs <= 0)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"maxRequestsPerInterval and intervalMs must both be greater than 0\",\n\t\t);\n\t}\n\n\tconst queue: QueueItem[] = [];\n\tconst starts: number[] = [];\n\tlet active = 0;\n\tlet lastStartAt = 0;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\n\tconst processQueue = () => {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\n\t\twhile (queue.length > 0) {\n\t\t\tconst now = Date.now();\n\t\t\tpruneStarts(now);\n\n\t\t\tif (active >= maxConcurrent) return;\n\n\t\t\tconst waitMs = getWaitMs(now);\n\t\t\tif (waitMs > 0) {\n\t\t\t\ttimer = setTimeout(processQueue, waitMs);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst item = queue.shift();\n\t\t\tif (!item) return;\n\n\t\t\tactive++;\n\t\t\tlastStartAt = now;\n\t\t\tstarts.push(now);\n\n\t\t\tlet released = false;\n\t\t\titem.resolve(() => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tactive--;\n\t\t\t\tprocessQueue();\n\t\t\t});\n\t\t}\n\t};\n\n\tconst acquire = () =>\n\t\tnew Promise<() => void>((resolve) => {\n\t\t\tqueue.push({ resolve });\n\t\t\tprocessQueue();\n\t\t});\n\n\tconst release = (ctx: RequestContext) => {\n\t\tconst releaseFn = ctx.meta[RELEASE_META_KEY] as (() => void) | undefined;\n\t\tif (!releaseFn) return;\n\t\tdelete ctx.meta[RELEASE_META_KEY];\n\t\treleaseFn();\n\t};\n\n\tconst pruneStarts = (now: number) => {\n\t\tif (!intervalMs) return;\n\t\twhile (starts.length > 0 && now - (starts[0] ?? 0) >= intervalMs) {\n\t\t\tstarts.shift();\n\t\t}\n\t};\n\n\tconst getWaitMs = (now: number): number => {\n\t\tconst spacingWait = Math.max(0, lastStartAt + minTimeMs - now);\n\t\tif (!maxRequestsPerInterval || !intervalMs) return spacingWait;\n\n\t\tif (starts.length < maxRequestsPerInterval) return spacingWait;\n\n\t\tconst oldest = starts[0] ?? now;\n\t\tconst intervalWait = Math.max(0, oldest + intervalMs - now);\n\t\treturn Math.max(spacingWait, intervalWait);\n\t};\n\n\treturn {\n\t\tname: \"rate-limit\",\n\t\tpriority: 1,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst releaseFn = await acquire();\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, [RELEASE_META_KEY]: releaseFn },\n\t\t\t};\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\trelease(ctx.request);\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(_error, ctx) {\n\t\t\trelease(ctx);\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { RetryPluginOptions } from \"./types\";\n\n/**\n * Writes retry configuration into request context meta so the\n * BaseHttpClient retry loop can read it. Use this when you need\n * per-request retry overrides rather than global ClientConfig.retry.\n */\nexport function createRetryPlugin(options: RetryPluginOptions = {}): ApiPlugin {\n\treturn {\n\t\tname: \"retry\",\n\t\tpriority: 5,\n\n\t\tbeforeRequest(ctx) {\n\t\t\t// Only write keys for values the caller explicitly provided.\n\t\t\t// Unset options fall through to ClientConfig.retry defaults inside\n\t\t\t// BaseHttpClient, so the plugin does not need to supply fallbacks.\n\t\t\tif (options.maxAttempts !== undefined)\n\t\t\t\tctx.meta[\"retry.maxAttempts\"] = options.maxAttempts;\n\t\t\tif (options.delayMs !== undefined)\n\t\t\t\tctx.meta[\"retry.delayMs\"] = options.delayMs;\n\t\t\tif (options.jitter !== undefined)\n\t\t\t\tctx.meta[\"retry.jitter\"] = options.jitter;\n\t\t\tif (options.retriableStatusCodes !== undefined)\n\t\t\t\tctx.meta[\"retry.retriableStatusCodes\"] = options.retriableStatusCodes;\n\t\t\treturn ctx;\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { TimeoutPluginOptions } from \"./types\";\n\n/**\n * Sets `ctx.timeoutMs` on every request so all requests made by this client\n * abort after the configured duration. The actual abort and\n * {@link TimeoutError} are handled by {@link fetchTransport}.\n *\n * Priority `1` ensures the timeout is stamped before any other plugin (e.g.\n * logger, cache) runs — plugins that read `ctx.timeoutMs` will always see it.\n * Use a `beforeRequest` hook with a lower priority to override per-request.\n *\n * Prefer `ClientConfig.timeoutMs` for a static global timeout. Use this\n * plugin when you need to set or change the timeout through the plugin\n * pipeline (e.g. from environment config loaded asynchronously in `setup`).\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createTimeoutPlugin({ timeoutMs: 5_000 })],\n * });\n * ```\n */\nexport function createTimeoutPlugin(options: TimeoutPluginOptions): ApiPlugin {\n\treturn {\n\t\tname: \"timeout\",\n\t\tpriority: 1,\n\n\t\tbeforeRequest(ctx) {\n\t\t\treturn { ...ctx, timeoutMs: options.timeoutMs };\n\t\t},\n\t};\n}\n","/**\n * Lightweight GraphQL template tag.\n *\n * This intentionally does not parse into a DocumentNode. It preserves the\n * query string while still giving GraphQL-aware tooling a familiar `gql` tag.\n */\nexport function gql(\n\tchunks: TemplateStringsArray,\n\t...values: unknown[]\n): string {\n\treturn chunks.reduce(\n\t\t(source, chunk, index) =>\n\t\t\t`${source}${chunk}${index in values ? String(values[index]) : \"\"}`,\n\t\t\"\",\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/graphql/GraphQLRequestError.ts","../src/plugin/PluginManager.ts","../src/errors/TimeoutError.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/transport/fetchTransport.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/gql.ts"],"sourcesContent":["export class ApiError extends Error {\n\treadonly status: number;\n\treadonly responseBody: unknown;\n\toverride readonly cause: unknown;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tstatus: number,\n\t\tresponseBody?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t\tthis.status = status;\n\t\tthis.responseBody = responseBody;\n\t\tthis.cause = cause;\n\t}\n}\n","import { ApiError } from \"./ApiError\";\n\nexport class RateLimitError extends ApiError {\n\treadonly retryAfterMs: number | undefined;\n\n\tconstructor(retryAfterMs?: number, responseBody?: unknown, cause?: unknown) {\n\t\tsuper(\"Rate limit exceeded\", 429, responseBody, cause);\n\t\tthis.name = \"RateLimitError\";\n\t\tthis.retryAfterMs = retryAfterMs;\n\t}\n}\n","import { ApiError } from \"../errors/ApiError\";\nimport type { GraphQLErrorDetail } from \"./types\";\n\n/**\n * Thrown when a GraphQL server returns a well-formed HTTP 200 response that\n * contains a non-empty `errors` array.\n *\n * Extends {@link ApiError} so that code catching `ApiError` also catches\n * GraphQL-level failures. Callers that need to inspect the individual error\n * objects can narrow with `instanceof GraphQLRequestError` and read\n * `graphqlErrors`.\n *\n * When the server returns both `data` and `errors` (partial result), the\n * partial data is available on `partialData` but the error is still thrown —\n * callers must explicitly opt in to consuming partial results.\n *\n * @example\n * ```ts\n * import { GraphQLRequestError } from \"@api-wrappers/api-core\";\n *\n * try {\n * const data = await client.graphql<MyQuery>(\"/graphql\", { query: QUERY });\n * } catch (err) {\n * if (err instanceof GraphQLRequestError) {\n * for (const e of err.graphqlErrors) {\n * console.error(e.message, e.path);\n * }\n * }\n * }\n * ```\n */\nexport class GraphQLRequestError extends ApiError {\n\t/** The errors array from the GraphQL response envelope. */\n\treadonly graphqlErrors: readonly GraphQLErrorDetail[];\n\t/**\n\t * Partial `data` returned alongside `errors`, if any. `undefined` when\n\t * the server returned no `data` field.\n\t */\n\treadonly partialData: unknown;\n\n\tconstructor(\n\t\terrors: GraphQLErrorDetail[],\n\t\tpartialData?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tconst message = errors.map((e) => e.message).join(\"; \");\n\t\t// Status 200: the HTTP request succeeded; the failure is at the\n\t\t// GraphQL application layer, not the transport layer.\n\t\tsuper(`GraphQL errors: ${message}`, 200, { errors }, cause);\n\t\tthis.name = \"GraphQLRequestError\";\n\t\tthis.graphqlErrors = errors;\n\t\tthis.partialData = partialData;\n\t}\n}\n","import type { LoggerInterface } from \"../client/types\";\nimport type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport type { ApiPlugin } from \"./types\";\n\nexport class PluginManager {\n\tprivate readonly plugins: ApiPlugin[] = [];\n\tprivate readonly logger: LoggerInterface;\n\n\t/**\n\t * @param logger - Logger used when an `onError` handler itself throws.\n\t * Defaults to `console`. Pass a no-op object to silence all output.\n\t */\n\tconstructor(logger: LoggerInterface = console) {\n\t\tthis.logger = logger;\n\t}\n\n\tregister(plugin: ApiPlugin): void {\n\t\tif (plugin.enabled === false) return;\n\t\tthis.plugins.push(plugin);\n\t\t// Sort ascending so lower priority numbers run first in beforeRequest.\n\t\tthis.plugins.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));\n\t}\n\n\tgetAll(): readonly ApiPlugin[] {\n\t\treturn this.plugins;\n\t}\n\n\tasync setup(client: unknown): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\tawait plugin.setup?.(client);\n\t\t}\n\t}\n\n\t/**\n\t * Runs `beforeRequest` in ascending priority order (lowest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync beforeRequest(ctx: RequestContext): Promise<RequestContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.beforeRequest?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"beforeRequest\", err, current);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `afterResponse` in descending priority order (highest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync afterResponse(ctx: ResponseContext): Promise<ResponseContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.afterResponse?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"afterResponse\", err);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `onError` on all plugins in registration order. A plugin throwing\n\t * here is caught and logged via the configured logger but does not\n\t * interrupt other `onError` handlers.\n\t */\n\tasync onError(error: unknown, ctx: RequestContext): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tawait plugin.onError?.(error, ctx);\n\t\t\t} catch (inner) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t`[PluginManager] Plugin \"${plugin.name}\" threw inside onError:`,\n\t\t\t\t\tinner,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\tawait plugin.dispose?.();\n\t\t}\n\t}\n}\n\nexport function getPluginErrorContext(\n\terror: unknown,\n): RequestContext | undefined {\n\tif (!error || typeof error !== \"object\") return undefined;\n\treturn (error as { requestContext?: RequestContext }).requestContext;\n}\n\nfunction wrapPluginError(\n\tname: string,\n\thook: string,\n\tcause: unknown,\n\trequestContext?: RequestContext,\n): Error {\n\tconst message = `Plugin \"${name}\" threw during \"${hook}\"`;\n\tconst err = new Error(message, { cause }) as Error & {\n\t\trequestContext?: RequestContext;\n\t};\n\terr.name = \"PluginError\";\n\terr.requestContext = requestContext;\n\treturn err;\n}\n","export class TimeoutError extends Error {\n\toverride readonly cause: unknown;\n\n\tconstructor(message = \"Request timed out\", cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"TimeoutError\";\n\t\tthis.cause = cause;\n\t}\n}\n","import type { QueryParams } from \"../types/common\";\n\n/**\n * Appends a query string to a URL. Skips nullish values and repeats keys for\n * array values so APIs like TMDB can accept `with_genres=1&with_genres=2`.\n */\nexport function buildUrl(base: string, query?: QueryParams): string {\n\tif (!query || Object.keys(query).length === 0) return base;\n\n\tconst params = new URLSearchParams();\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tif (value === undefined || value === null) continue;\n\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\tparams.append(key, String(item));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tparams.append(key, String(value));\n\t\t}\n\t}\n\n\tconst qs = params.toString();\n\tif (!qs) return base;\n\n\tconst separator = base.includes(\"?\")\n\t\t? base.endsWith(\"?\") || base.endsWith(\"&\")\n\t\t\t? \"\"\n\t\t\t: \"&\"\n\t\t: \"?\";\n\treturn `${base}${separator}${qs}`;\n}\n","export function isPlainObject(\n\tvalue: unknown,\n): value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst proto = Object.getPrototypeOf(value) as unknown;\n\treturn proto === Object.prototype || proto === null;\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport { TimeoutError } from \"../errors/TimeoutError\";\nimport { buildUrl } from \"../utils/buildUrl\";\nimport { isPlainObject } from \"../utils/isPlainObject\";\nimport type { Transport } from \"./types\";\n\nconst defaultFetch: typeof globalThis.fetch = (input, init) => {\n\treturn globalThis.fetch(input, init);\n};\n\n/**\n * Creates a {@link Transport} backed by the provided `fetch` function.\n * Use this when you need a polyfill or a custom fetch interceptor:\n *\n * ```ts\n * import nodeFetch from \"node-fetch\";\n * createClient({ fetch: nodeFetch as typeof globalThis.fetch });\n * // — or set it directly on the transport:\n * const transport = createFetchTransport(nodeFetch as typeof globalThis.fetch);\n * ```\n */\nexport function createFetchTransport(\n\tfetchFn: typeof globalThis.fetch = defaultFetch,\n): Transport {\n\treturn {\n\t\tasync execute(ctx: RequestContext): Promise<Response> {\n\t\t\tconst url = buildUrl(ctx.url, ctx.query);\n\t\t\tconst init: RequestInit = {\n\t\t\t\tmethod: ctx.method,\n\t\t\t\theaders: ctx.headers,\n\t\t\t};\n\n\t\t\tconst hasBody =\n\t\t\t\tctx.body !== undefined && ctx.method !== \"GET\" && ctx.method !== \"HEAD\";\n\n\t\t\tif (hasBody) {\n\t\t\t\tinit.body = serializeRequestBody(ctx.body, ctx.headers);\n\t\t\t}\n\n\t\t\tif (ctx.timeoutMs !== undefined || ctx.signal) {\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tlet timedOut = false;\n\t\t\t\tconst abortFromParent = () => controller.abort(ctx.signal?.reason);\n\t\t\t\tconst timer =\n\t\t\t\t\tctx.timeoutMs !== undefined\n\t\t\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t\t\t}, ctx.timeoutMs)\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tif (ctx.signal) {\n\t\t\t\t\tif (ctx.signal.aborted) {\n\t\t\t\t\t\tcontroller.abort(ctx.signal.reason);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.signal.addEventListener(\"abort\", abortFromParent, {\n\t\t\t\t\t\t\tonce: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treturn await fetchFn(url, { ...init, signal: controller.signal });\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (timedOut && err instanceof Error && err.name === \"AbortError\") {\n\t\t\t\t\t\tthrow new TimeoutError(\n\t\t\t\t\t\t\t`Request timed out after ${ctx.timeoutMs}ms`,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t} finally {\n\t\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\t\tctx.signal?.removeEventListener(\"abort\", abortFromParent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn fetchFn(url, init);\n\t\t},\n\t};\n}\n\n/**\n * Default {@link Transport} backed by the global `fetch` API.\n *\n * Behaviour:\n * - Builds the final URL from `ctx.url` + `ctx.query` via {@link buildUrl}.\n * - Serialises `ctx.body` to JSON for non-GET/HEAD requests.\n * - Wires an `AbortController` when `ctx.timeoutMs` is set; throws\n * {@link TimeoutError} on abort.\n *\n * Replace this with a custom {@link Transport} in tests, or provide a custom\n * `fetch` function via {@link ClientConfig.fetch}.\n */\nexport const fetchTransport: Transport = createFetchTransport();\n\nfunction serializeRequestBody(\n\tbody: unknown,\n\theaders: Record<string, string>,\n): BodyInit {\n\tif (isBodyInit(body)) return body;\n\n\tconst contentType = headers[\"content-type\"] ?? \"\";\n\tif (\n\t\tisPlainObject(body) ||\n\t\tArray.isArray(body) ||\n\t\tcontentType.includes(\"json\")\n\t) {\n\t\treturn JSON.stringify(body);\n\t}\n\n\treturn String(body);\n}\n\nfunction isBodyInit(body: unknown): body is BodyInit {\n\tif (typeof body === \"string\") return true;\n\tif (body instanceof ArrayBuffer) return true;\n\tif (ArrayBuffer.isView(body)) return true;\n\tif (typeof Blob !== \"undefined\" && body instanceof Blob) return true;\n\tif (typeof FormData !== \"undefined\" && body instanceof FormData) return true;\n\tif (\n\t\ttypeof URLSearchParams !== \"undefined\" &&\n\t\tbody instanceof URLSearchParams\n\t) {\n\t\treturn true;\n\t}\n\tif (typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n","/**\n * Merges header objects left to right. Keys are normalized to\n * lowercase so merging is case-insensitive. Later sources win.\n */\nexport function mergeHeaders(\n\t...sources: (Record<string, string> | undefined)[]\n): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tfor (const [key, value] of Object.entries(source)) {\n\t\t\tresult[key.toLowerCase()] = value;\n\t\t}\n\t}\n\treturn result;\n}\n","/**\n * Joins a client base URL and request path without requiring callers to keep\n * slashes perfectly aligned. Absolute request URLs are returned unchanged.\n */\nexport function resolveUrl(baseUrl: string, path: string): string {\n\tif (/^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(path)) return path;\n\n\tconst base = baseUrl.replace(/\\/+$/, \"\");\n\tconst next = path.replace(/^\\/+/, \"\");\n\n\treturn next ? `${base}/${next}` : base;\n}\n","export function sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport { ApiError } from \"../errors/ApiError\";\nimport { RateLimitError } from \"../errors/RateLimitError\";\nimport { GraphQLRequestError } from \"../graphql/GraphQLRequestError\";\nimport type { GraphQLRequestOptions, GraphQLResponse } from \"../graphql/types\";\nimport { getPluginErrorContext, PluginManager } from \"../plugin/PluginManager\";\nimport {\n\tcreateFetchTransport,\n\tfetchTransport,\n} from \"../transport/fetchTransport\";\nimport type { HttpMethod, QueryParams } from \"../types/common\";\nimport { mergeHeaders } from \"../utils/mergeHeaders\";\nimport { resolveUrl } from \"../utils/resolveUrl\";\nimport { sleep } from \"../utils/sleep\";\nimport type { ClientConfig } from \"./types\";\n\n/** Per-request options passed to {@link BaseHttpClient.request} and the convenience methods. */\nexport type ResponseType = \"auto\" | \"json\" | \"text\" | \"arrayBuffer\" | \"blob\";\n\nexport interface RequestOptions {\n\t/** HTTP method. Defaults to `\"GET\"`. */\n\tmethod?: HttpMethod;\n\t/**\n\t * Additional headers merged on top of `ClientConfig.defaultHeaders`.\n\t * These take precedence; `content-type: application/json` is always\n\t * present and is the lowest-priority default.\n\t */\n\theaders?: Record<string, string>;\n\t/** Request body. Serialised to JSON by {@link fetchTransport}. Ignored for GET and HEAD. */\n\tbody?: unknown;\n\t/**\n\t * Query string parameters appended to the URL. `undefined` values are\n\t * omitted. Numbers and booleans are coerced to strings. Array values are\n\t * emitted as repeated query parameters.\n\t */\n\tquery?: QueryParams;\n\t/** Optional caller-provided abort signal. Composes with `timeoutMs`. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Per-request timeout override in milliseconds. Takes precedence over\n\t * `ClientConfig.timeoutMs`. Throws {@link TimeoutError} when exceeded.\n\t */\n\ttimeoutMs?: number;\n\t/**\n\t * Explicit cache key used by {@link createCachePlugin}. When omitted the\n\t * plugin derives a key from the method, URL, and query string.\n\t */\n\tcacheKey?: string;\n\t/**\n\t * Arbitrary string tags attached to the request context. Plugins may use\n\t * these for cache invalidation, metrics grouping, or filtering.\n\t */\n\ttags?: string[];\n\t/**\n\t * Controls how the response body is parsed. Defaults to content-type based\n\t * parsing: JSON responses become objects, everything else becomes text.\n\t */\n\tresponseType?: ResponseType;\n\t/**\n\t * Optional response parser for non-2xx bodies. Defaults to `\"auto\"` so APIs\n\t * that return binary success payloads can still surface text/JSON errors.\n\t */\n\terrorResponseType?: ResponseType;\n}\n\nexport interface ApiResponse<T = unknown> {\n\tdata: T;\n\tresponse: Response;\n\trequest: RequestContext;\n\tmeta: Record<string, unknown>;\n}\n\nconst DEFAULT_RETRIABLE_STATUS_CODES = [429, 500, 502, 503, 504];\n\n/**\n * Core HTTP client. Manages the plugin lifecycle, retry loop, and transport\n * dispatch for all requests.\n *\n * Plugins are initialised lazily on the first call to {@link request} (or any\n * convenience method). Call {@link dispose} when the client is no longer\n * needed so plugins can release timers, connections, or cache handles.\n *\n * Extend this class to add domain-specific methods while keeping the plugin\n * and transport infrastructure intact.\n *\n * @example\n * ```ts\n * // Prefer createClient() in application code:\n * const client = createClient({ baseUrl: \"https://api.example.com/v1\" });\n *\n * // Or subclass for wrapper packages:\n * class MyApiClient extends BaseHttpClient {\n * getUser(id: string) { return this.get<User>(`/users/${id}`); }\n * }\n * ```\n */\nexport class BaseHttpClient {\n\tprotected readonly config: ClientConfig;\n\tprotected readonly pluginManager: PluginManager;\n\tprivate initialized = false;\n\tprivate initPromise: Promise<void> | undefined;\n\n\tconstructor(config: ClientConfig) {\n\t\tthis.config = config;\n\t\tthis.pluginManager = new PluginManager(config.logger);\n\t\tfor (const plugin of config.plugins ?? []) {\n\t\t\tthis.pluginManager.register(plugin);\n\t\t}\n\t}\n\n\t/** Initializes all plugins. Called lazily on first request. */\n\tasync init(): Promise<void> {\n\t\tif (this.initialized) return;\n\t\tthis.initPromise ??= this.pluginManager\n\t\t\t.setup(this)\n\t\t\t.then(() => {\n\t\t\t\tthis.initialized = true;\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\tthis.initPromise = undefined;\n\t\t\t\tthrow err;\n\t\t\t});\n\t\tawait this.initPromise;\n\t}\n\n\t/** Disposes all plugins. Call when the client is no longer needed. */\n\tasync dispose(): Promise<void> {\n\t\tawait this.pluginManager.dispose();\n\t\tthis.initialized = false;\n\t\tthis.initPromise = undefined;\n\t}\n\n\t/**\n\t * Executes an HTTP request through the full plugin pipeline.\n\t *\n\t * Lifecycle per attempt:\n\t * 1. Build `RequestContext` with merged headers, query, and retry state.\n\t * 2. Run `beforeRequest` hooks (ascending priority). A plugin may set\n\t * `ctx.syntheticResponse` to skip the transport entirely (e.g. cache hit).\n\t * 3. Merge any `retry.*` meta written by {@link createRetryPlugin}.\n\t * 4. Call transport (skipped when `syntheticResponse` is set).\n\t * 5. Parse the response body (JSON or text).\n\t * 6. Run `afterResponse` hooks (descending priority).\n\t * 7. Retry on retriable status codes; throw on terminal failures.\n\t *\n\t * @param path - Path appended to `ClientConfig.baseUrl`. Should start with `/`.\n\t * @param options - Per-request overrides for method, headers, body, query, etc.\n\t * @returns The parsed response body cast to `T`.\n\t * @throws {@link ApiError} for non-2xx responses.\n\t * @throws {@link RateLimitError} for 429 responses.\n\t * @throws {@link TimeoutError} when `timeoutMs` is exceeded.\n\t */\n\tasync request<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<T> {\n\t\tconst result = await this.requestWithResponse<T>(path, options);\n\t\treturn result.data;\n\t}\n\n\t/**\n\t * Executes a request and returns the parsed body plus the final response\n\t * context. Use this in wrappers that need response headers, status, or\n\t * plugin metadata while keeping the same error/retry behaviour as\n\t * {@link request}.\n\t */\n\tasync requestWithResponse<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<ApiResponse<T>> {\n\t\tawait this.init();\n\n\t\tconst transport =\n\t\t\tthis.config.transport ??\n\t\t\t(this.config.fetch\n\t\t\t\t? createFetchTransport(this.config.fetch)\n\t\t\t\t: fetchTransport);\n\t\tconst retryCfg = this.config.retry;\n\t\t// These are `let` so createRetryPlugin can override them per-request via\n\t\t// ctx.meta after beforeRequest runs (see merge block below).\n\t\tlet maxAttempts = retryCfg?.maxAttempts ?? 1;\n\t\tlet baseDelay = retryCfg?.delayMs ?? 500;\n\t\tlet jitter = retryCfg?.jitter ?? true;\n\t\tlet retriableCodes =\n\t\t\tretryCfg?.retriableStatusCodes ?? DEFAULT_RETRIABLE_STATUS_CODES;\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tconst baseCtx: RequestContext = {\n\t\t\t\turl: resolveUrl(this.config.baseUrl, path),\n\t\t\t\tmethod: options.method ?? \"GET\",\n\t\t\t\theaders: mergeHeaders(\n\t\t\t\t\t{ \"content-type\": \"application/json\" },\n\t\t\t\t\tthis.config.defaultHeaders,\n\t\t\t\t\toptions.headers,\n\t\t\t\t),\n\t\t\t\tbody: options.body,\n\t\t\t\tquery: options.query,\n\t\t\t\tsignal: options.signal,\n\t\t\t\tmeta: {},\n\t\t\t\tcacheKey: options.cacheKey,\n\t\t\t\ttags: options.tags,\n\t\t\t\tretryCount: maxAttempts - 1 - attempt,\n\t\t\t\tattempt,\n\t\t\t\ttimeoutMs: options.timeoutMs ?? this.config.timeoutMs,\n\t\t\t};\n\n\t\t\tlet ctx: RequestContext;\n\t\t\ttry {\n\t\t\t\tctx = await this.pluginManager.beforeRequest(baseCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(\n\t\t\t\t\terr,\n\t\t\t\t\tgetPluginErrorContext(err) ?? baseCtx,\n\t\t\t\t);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\t// Merge per-request retry overrides written by createRetryPlugin.\n\t\t\t// Only keys explicitly set by the plugin are present, so unset keys\n\t\t\t// leave the config-level defaults untouched.\n\t\t\t// Because the for-loop condition re-evaluates `attempt < maxAttempts`\n\t\t\t// on every iteration, updating maxAttempts here takes effect\n\t\t\t// immediately for all remaining attempts.\n\t\t\tif (ctx.meta[\"retry.maxAttempts\"] !== undefined)\n\t\t\t\tmaxAttempts = ctx.meta[\"retry.maxAttempts\"] as number;\n\t\t\tif (ctx.meta[\"retry.delayMs\"] !== undefined)\n\t\t\t\tbaseDelay = ctx.meta[\"retry.delayMs\"] as number;\n\t\t\tif (ctx.meta[\"retry.jitter\"] !== undefined)\n\t\t\t\tjitter = ctx.meta[\"retry.jitter\"] as boolean;\n\t\t\tif (ctx.meta[\"retry.retriableStatusCodes\"] !== undefined)\n\t\t\t\tretriableCodes = ctx.meta[\"retry.retriableStatusCodes\"] as number[];\n\n\t\t\tlet rawResponse: Response;\n\t\t\tif (ctx.syntheticResponse) {\n\t\t\t\t// A plugin (e.g. cache) pre-populated the response — skip the\n\t\t\t\t// network entirely.\n\t\t\t\trawResponse = ctx.syntheticResponse;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\trawResponse = await transport.execute(ctx);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\t\tlastError = err;\n\n\t\t\t\t\tif (attempt < maxAttempts - 1) {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst parsedBody = await parseBody(\n\t\t\t\trawResponse,\n\t\t\t\trawResponse.ok\n\t\t\t\t\t? options.responseType\n\t\t\t\t\t: (options.errorResponseType ?? \"auto\"),\n\t\t\t);\n\n\t\t\tlet resCtx: ResponseContext = {\n\t\t\t\trequest: ctx,\n\t\t\t\tresponse: rawResponse,\n\t\t\t\tparsedBody,\n\t\t\t\tmeta: {},\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tresCtx = await this.pluginManager.afterResponse(resCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tif (!rawResponse.ok) {\n\t\t\t\tconst shouldRetry =\n\t\t\t\t\tretriableCodes.includes(rawResponse.status) &&\n\t\t\t\t\tattempt < maxAttempts - 1;\n\n\t\t\t\tif (shouldRetry) {\n\t\t\t\t\tif (rawResponse.status === 429) {\n\t\t\t\t\t\tconst wait = readRetryAfterMs(rawResponse);\n\t\t\t\t\t\tawait this.waitForRetry(attempt, wait ?? baseDelay, false);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t}\n\t\t\t\t\tlastError = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst err = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdata: resCtx.parsedBody as T,\n\t\t\t\tresponse: resCtx.response,\n\t\t\t\trequest: resCtx.request,\n\t\t\t\tmeta: resCtx.meta,\n\t\t\t};\n\t\t}\n\n\t\tthrow lastError;\n\t}\n\n\t// ─── Convenience methods ────────────────────────────────────────────────────\n\t// Each method is a thin wrapper around request() that fixes the HTTP verb.\n\n\t/** Sends a GET request. The response body is not cached unless a cache plugin is registered. */\n\tget<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"GET\" });\n\t}\n\n\tpost<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"POST\", body });\n\t}\n\n\tput<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PUT\", body });\n\t}\n\n\tpatch<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PATCH\", body });\n\t}\n\n\tdelete<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"DELETE\" });\n\t}\n\n\thead<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"HEAD\" });\n\t}\n\n\toptions<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"OPTIONS\" });\n\t}\n\n\t/**\n\t * Executes a GraphQL query or mutation against a single endpoint path.\n\t *\n\t * The request is a `POST` with `content-type: application/json` carrying\n\t * `{ query, variables?, operationName? }` as the body. It flows through\n\t * the full plugin lifecycle (beforeRequest → transport → afterResponse →\n\t * onError) and respects all retry configuration, exactly like REST calls.\n\t *\n\t * **Error handling:**\n\t * - HTTP-level failures (429, 500, timeout) throw the same error classes\n\t * as REST requests (`RateLimitError`, `ApiError`, `TimeoutError`).\n\t * - A successful HTTP 200 that contains a non-empty `errors` array throws\n\t * {@link GraphQLRequestError}, which extends `ApiError`.\n\t *\n\t * **Caching:**\n\t * The cache plugin skips `POST` requests by default. Pass an explicit\n\t * `cacheKey` in options to opt a specific operation into caching.\n\t *\n\t * @typeParam TData - Shape of the `data` field in the GraphQL response.\n\t * @typeParam TVariables - Shape of the `variables` object. Defaults to\n\t * `Record<string, unknown>`.\n\t * @param path - Endpoint path, e.g. `\"/graphql\"`. Appended to `baseUrl`.\n\t * @param options - Query document, variables, and optional per-request overrides.\n\t * @returns The `data` field from the GraphQL response envelope.\n\t * @throws {@link GraphQLRequestError} when `response.errors` is non-empty.\n\t * @throws {@link ApiError} / {@link RateLimitError} / {@link TimeoutError} on\n\t * HTTP-level failures.\n\t *\n\t * @example\n\t * ```ts\n\t * const data = await client.graphql<GetUserQuery, GetUserQueryVariables>(\n\t * \"/graphql\",\n\t * { query: GET_USER, variables: { id: \"123\" } },\n\t * );\n\t * ```\n\t */\n\tasync graphql<\n\t\tTData = unknown,\n\t\tTVariables extends object = Record<string, unknown>,\n\t>(path: string, options: GraphQLRequestOptions<TVariables>): Promise<TData> {\n\t\tconst {\n\t\t\tquery,\n\t\t\tvariables,\n\t\t\toperationName,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t} = options;\n\n\t\tconst envelope = await this.request<GraphQLResponse<TData>>(path, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: {\n\t\t\t\tquery,\n\t\t\t\t...(variables !== undefined && { variables }),\n\t\t\t\t...(operationName !== undefined && { operationName }),\n\t\t\t},\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t});\n\n\t\t// Surface GraphQL application-layer errors as a typed exception.\n\t\t// We throw even when partial data is present — callers who need\n\t\t// partial results can catch GraphQLRequestError and read .partialData.\n\t\tif (envelope.errors && envelope.errors.length > 0) {\n\t\t\tthrow new GraphQLRequestError(envelope.errors, envelope.data);\n\t\t}\n\n\t\t// data may be undefined if the server returned an empty response —\n\t\t// safe to cast because TData defaults to unknown.\n\t\treturn envelope.data as TData;\n\t}\n\n\tprivate async waitForRetry(\n\t\tattempt: number,\n\t\tbaseDelay: number,\n\t\tuseJitter: boolean,\n\t): Promise<void> {\n\t\t// Exponential backoff: delay * 2^attempt\n\t\tconst exponential = baseDelay * 2 ** attempt;\n\t\tconst ms = useJitter\n\t\t\t? exponential * (0.5 + Math.random() * 0.5)\n\t\t\t: exponential;\n\t\tawait sleep(Math.round(ms));\n\t}\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function parseBody(\n\tresponse: Response,\n\tresponseType: ResponseType = \"auto\",\n): Promise<unknown> {\n\tif (responseType === \"arrayBuffer\") return response.arrayBuffer();\n\tif (responseType === \"blob\") return response.blob();\n\n\tif (response.status === 204 || response.status === 205) return undefined;\n\tif (response.headers.get(\"content-length\") === \"0\") return undefined;\n\n\tconst text = await response.text();\n\tif (responseType === \"text\") return text;\n\tif (!text) return undefined;\n\n\tif (responseType === \"json\") return JSON.parse(text);\n\n\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\tif (contentType.includes(\"application/json\")) {\n\t\treturn JSON.parse(text);\n\t}\n\treturn text;\n}\n\nfunction normalizeHttpError(response: Response, body: unknown): ApiError {\n\tif (response.status === 429) {\n\t\treturn new RateLimitError(readRetryAfterMs(response), body);\n\t}\n\treturn new ApiError(\n\t\t`Request failed with status ${response.status}`,\n\t\tresponse.status,\n\t\tbody,\n\t);\n}\n\nfunction readRetryAfterMs(response: Response): number | undefined {\n\tconst raw = response.headers.get(\"retry-after\");\n\tif (!raw) return undefined;\n\n\tconst seconds = Number(raw);\n\tif (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n\n\tconst date = Date.parse(raw);\n\tif (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n\n\treturn undefined;\n}\n","import { BaseHttpClient } from \"./BaseHttpClient\";\nimport type { ClientConfig } from \"./types\";\n\n/**\n * Factory function that creates a {@link BaseHttpClient} from the given\n * config. Prefer this over `new BaseHttpClient(config)` in application code\n * so that the concrete class stays an implementation detail.\n *\n * @example\n * ```ts\n * const client = createClient({\n * baseUrl: \"https://api.example.com/v1\",\n * defaultHeaders: { \"x-api-key\": \"secret\" },\n * retry: { maxAttempts: 3, delayMs: 300 },\n * plugins: [createLoggerPlugin(), createCachePlugin({ ttlMs: 60_000 })],\n * });\n * ```\n */\nexport function createClient(config: ClientConfig): BaseHttpClient {\n\treturn new BaseHttpClient(config);\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { MaybePromise } from \"../../types/common\";\nimport type { AuthPluginOptions } from \"./types\";\n\ntype TokenInput =\n\t| string\n\t| (() => MaybePromise<string | null | undefined>)\n\t| AuthPluginOptions;\n\n/**\n * Adds an auth token header before each request. The token can be static or\n * loaded asynchronously per request, which covers wrappers with refreshable\n * access tokens.\n */\nexport function createAuthPlugin(input: TokenInput): ApiPlugin {\n\tconst options = normalizeOptions(input);\n\tconst headerName = (options.headerName ?? \"authorization\").toLowerCase();\n\tconst scheme = options.scheme === undefined ? \"Bearer\" : options.scheme;\n\n\treturn {\n\t\tname: \"auth\",\n\t\tpriority: 2,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst token = await options.getToken();\n\t\t\tif (!token) return ctx;\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\theaders: {\n\t\t\t\t\t...ctx.headers,\n\t\t\t\t\t[headerName]: scheme ? `${scheme} ${token}` : token,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction normalizeOptions(input: TokenInput): AuthPluginOptions {\n\tif (typeof input === \"string\") {\n\t\treturn { getToken: () => input };\n\t}\n\tif (typeof input === \"function\") {\n\t\treturn { getToken: input };\n\t}\n\treturn input;\n}\n","import type { CacheStore } from \"./types\";\n\ninterface CacheEntry {\n\tvalue: unknown;\n\texpiresAt: number | null;\n}\n\nexport class MemoryStore implements CacheStore {\n\tprivate readonly store = new Map<string, CacheEntry>();\n\n\tget(key: string): unknown | undefined {\n\t\tconst entry = this.store.get(key);\n\t\tif (!entry) return undefined;\n\t\tif (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n\t\t\tthis.store.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn entry.value;\n\t}\n\n\tset(key: string, value: unknown, ttlMs?: number): void {\n\t\tthis.store.set(key, {\n\t\t\tvalue,\n\t\t\texpiresAt: ttlMs != null ? Date.now() + ttlMs : null,\n\t\t});\n\t}\n\n\tdelete(key: string): void {\n\t\tthis.store.delete(key);\n\t}\n\n\tclear(): void {\n\t\tthis.store.clear();\n\t}\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport { MemoryStore } from \"./memoryStore\";\nimport type { CachePlugin, CachePluginOptions } from \"./types\";\n\nconst DEFAULT_CACHEABLE_METHODS = [\"GET\"] as const;\nconst CACHE_HIT_META_KEY = \"cache.hit\";\n\nexport function createCachePlugin(\n\toptions: CachePluginOptions = {},\n): CachePlugin {\n\tconst store = options.store ?? new MemoryStore();\n\tconst ttlMs = options.ttlMs;\n\tconst methods: string[] = options.methods ?? [...DEFAULT_CACHEABLE_METHODS];\n\tconst generateKey = options.generateKey ?? defaultCacheKey;\n\n\t// tag → Set<cacheKey>: populated during afterResponse, used by invalidateByTag.\n\tconst tagIndex = new Map<string, Set<string>>();\n\n\treturn {\n\t\tname: \"cache\",\n\t\tpriority: 20,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tif (!methods.includes(ctx.method)) return ctx;\n\n\t\t\tconst key = ctx.cacheKey ?? generateKey(ctx);\n\t\t\tconst cached = await store.get(key);\n\n\t\t\tif (cached !== undefined) {\n\t\t\t\t// Build a synthetic Response so the rest of the pipeline\n\t\t\t\t// (afterResponse, status checks) sees a uniform shape.\n\t\t\t\tconst syntheticResponse = new Response(JSON.stringify(cached), {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\n\t\t\t\t// Setting syntheticResponse tells BaseHttpClient to skip the\n\t\t\t\t// transport entirely and use this response directly.\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\t...ctx.meta,\n\t\t\t\t\t\t[CACHE_HIT_META_KEY]: { key, data: cached },\n\t\t\t\t\t},\n\t\t\t\t\tsyntheticResponse,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, \"cache.key\": key },\n\t\t\t};\n\t\t},\n\n\t\tasync afterResponse(ctx) {\n\t\t\tconst hit = ctx.request.meta[CACHE_HIT_META_KEY] as\n\t\t\t\t| { key: string; data: unknown }\n\t\t\t\t| undefined;\n\n\t\t\tif (hit) {\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tparsedBody: hit.data,\n\t\t\t\t\tmeta: { ...ctx.meta, \"cache.served\": true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst key = ctx.request.meta[\"cache.key\"] as string | undefined;\n\t\t\tif (key && methods.includes(ctx.request.method) && ctx.response.ok) {\n\t\t\t\tawait store.set(key, ctx.parsedBody, ttlMs);\n\t\t\t\tctx.meta[\"cache.stored\"] = true;\n\n\t\t\t\t// Record tag → key associations for invalidateByTag.\n\t\t\t\tfor (const tag of ctx.request.tags ?? []) {\n\t\t\t\t\tif (!tagIndex.has(tag)) tagIndex.set(tag, new Set());\n\t\t\t\t\ttagIndex.get(tag)?.add(key);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ctx;\n\t\t},\n\n\t\tasync invalidate(key: string): Promise<void> {\n\t\t\tawait store.delete(key);\n\t\t\t// Clean up any tag index entries pointing to this key.\n\t\t\tfor (const keys of tagIndex.values()) {\n\t\t\t\tkeys.delete(key);\n\t\t\t}\n\t\t},\n\n\t\tasync invalidateByTag(tag: string): Promise<void> {\n\t\t\tconst keys = tagIndex.get(tag);\n\t\t\tif (!keys || keys.size === 0) return;\n\t\t\tfor (const key of keys) {\n\t\t\t\tawait store.delete(key);\n\t\t\t\t// Remove this key from all other tag index entries too.\n\t\t\t\tfor (const otherKeys of tagIndex.values()) {\n\t\t\t\t\totherKeys.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttagIndex.delete(tag);\n\t\t},\n\t};\n}\n\nfunction defaultCacheKey(ctx: RequestContext): string {\n\tconst queryStr = ctx.query\n\t\t? new URLSearchParams(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tObject.entries(ctx.query)\n\t\t\t\t\t\t.filter(([, v]) => v !== undefined)\n\t\t\t\t\t\t.map(([k, v]) => [k, String(v)]),\n\t\t\t\t),\n\t\t\t).toString()\n\t\t: \"\";\n\treturn `${ctx.method}:${ctx.url}${queryStr ? `?${queryStr}` : \"\"}`;\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { LoggerPluginOptions } from \"./types\";\n\n/**\n * Creates a plugin that logs request start, response status, and errors.\n *\n * Log lines are prefixed with `[api-core]` and include the HTTP method, URL,\n * attempt number (on `beforeRequest`), and status code (on `afterResponse`).\n *\n * Priority `10` means it runs _after_ auth or header-mutation plugins\n * (priority < 10) so the logged URL and headers reflect the final request,\n * but _before_ the cache plugin (priority `20`) so cache hits are still\n * visible in the log.\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createLoggerPlugin({ logRequest: true, logResponse: true })],\n * });\n * ```\n */\nexport function createLoggerPlugin(\n\toptions: LoggerPluginOptions = {},\n): ApiPlugin {\n\tconst {\n\t\tlogRequest = true,\n\t\tlogResponse = true,\n\t\tlogError = true,\n\t\tlogger = console,\n\t} = options;\n\n\treturn {\n\t\tname: \"logger\",\n\t\tpriority: 10,\n\n\t\tbeforeRequest(ctx) {\n\t\t\tif (logRequest) {\n\t\t\t\tlogger.info(`[api-core] --> ${ctx.method} ${ctx.url}`, {\n\t\t\t\t\tattempt: ctx.attempt,\n\t\t\t\t\tbody: ctx.body,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\tif (logResponse) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`[api-core] <-- ${ctx.response.status} ${ctx.request.method} ${ctx.request.url}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(error, ctx) {\n\t\t\tif (logError) {\n\t\t\t\tlogger.error(`[api-core] ERR ${ctx.method} ${ctx.url}`, error);\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport type { ApiPlugin } from \"../../plugin/types\";\nimport type { RateLimitPluginOptions } from \"./types\";\n\nconst RELEASE_META_KEY = \"rateLimit.release\";\n\ninterface QueueItem {\n\tresolve: (release: () => void) => void;\n}\n\n/**\n * Throttles request starts before they reach the transport. Supports\n * concurrency, minimum spacing, and fixed-window request budgets.\n */\nexport function createRateLimitPlugin(\n\toptions: RateLimitPluginOptions = {},\n): ApiPlugin {\n\tconst maxConcurrent = options.maxConcurrent ?? Number.POSITIVE_INFINITY;\n\tconst minTimeMs = options.minTimeMs ?? 0;\n\tconst maxRequestsPerInterval = options.maxRequestsPerInterval;\n\tconst intervalMs = options.intervalMs;\n\n\tif (maxConcurrent <= 0) {\n\t\tthrow new Error(\"maxConcurrent must be greater than 0\");\n\t}\n\tif (minTimeMs < 0) {\n\t\tthrow new Error(\"minTimeMs must be greater than or equal to 0\");\n\t}\n\tif (\n\t\t(maxRequestsPerInterval !== undefined || intervalMs !== undefined) &&\n\t\t(!maxRequestsPerInterval ||\n\t\t\tmaxRequestsPerInterval <= 0 ||\n\t\t\t!intervalMs ||\n\t\t\tintervalMs <= 0)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"maxRequestsPerInterval and intervalMs must both be greater than 0\",\n\t\t);\n\t}\n\n\tconst queue: QueueItem[] = [];\n\tconst starts: number[] = [];\n\tlet active = 0;\n\tlet lastStartAt = 0;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\n\tconst processQueue = () => {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\n\t\twhile (queue.length > 0) {\n\t\t\tconst now = Date.now();\n\t\t\tpruneStarts(now);\n\n\t\t\tif (active >= maxConcurrent) return;\n\n\t\t\tconst waitMs = getWaitMs(now);\n\t\t\tif (waitMs > 0) {\n\t\t\t\ttimer = setTimeout(processQueue, waitMs);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst item = queue.shift();\n\t\t\tif (!item) return;\n\n\t\t\tactive++;\n\t\t\tlastStartAt = now;\n\t\t\tstarts.push(now);\n\n\t\t\tlet released = false;\n\t\t\titem.resolve(() => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tactive--;\n\t\t\t\tprocessQueue();\n\t\t\t});\n\t\t}\n\t};\n\n\tconst acquire = () =>\n\t\tnew Promise<() => void>((resolve) => {\n\t\t\tqueue.push({ resolve });\n\t\t\tprocessQueue();\n\t\t});\n\n\tconst release = (ctx: RequestContext) => {\n\t\tconst releaseFn = ctx.meta[RELEASE_META_KEY] as (() => void) | undefined;\n\t\tif (!releaseFn) return;\n\t\tdelete ctx.meta[RELEASE_META_KEY];\n\t\treleaseFn();\n\t};\n\n\tconst pruneStarts = (now: number) => {\n\t\tif (!intervalMs) return;\n\t\twhile (starts.length > 0 && now - (starts[0] ?? 0) >= intervalMs) {\n\t\t\tstarts.shift();\n\t\t}\n\t};\n\n\tconst getWaitMs = (now: number): number => {\n\t\tconst spacingWait = Math.max(0, lastStartAt + minTimeMs - now);\n\t\tif (!maxRequestsPerInterval || !intervalMs) return spacingWait;\n\n\t\tif (starts.length < maxRequestsPerInterval) return spacingWait;\n\n\t\tconst oldest = starts[0] ?? now;\n\t\tconst intervalWait = Math.max(0, oldest + intervalMs - now);\n\t\treturn Math.max(spacingWait, intervalWait);\n\t};\n\n\treturn {\n\t\tname: \"rate-limit\",\n\t\tpriority: 1,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst releaseFn = await acquire();\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, [RELEASE_META_KEY]: releaseFn },\n\t\t\t};\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\trelease(ctx.request);\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(_error, ctx) {\n\t\t\trelease(ctx);\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { RetryPluginOptions } from \"./types\";\n\n/**\n * Writes retry configuration into request context meta so the\n * BaseHttpClient retry loop can read it. Use this when you need\n * per-request retry overrides rather than global ClientConfig.retry.\n */\nexport function createRetryPlugin(options: RetryPluginOptions = {}): ApiPlugin {\n\treturn {\n\t\tname: \"retry\",\n\t\tpriority: 5,\n\n\t\tbeforeRequest(ctx) {\n\t\t\t// Only write keys for values the caller explicitly provided.\n\t\t\t// Unset options fall through to ClientConfig.retry defaults inside\n\t\t\t// BaseHttpClient, so the plugin does not need to supply fallbacks.\n\t\t\tif (options.maxAttempts !== undefined)\n\t\t\t\tctx.meta[\"retry.maxAttempts\"] = options.maxAttempts;\n\t\t\tif (options.delayMs !== undefined)\n\t\t\t\tctx.meta[\"retry.delayMs\"] = options.delayMs;\n\t\t\tif (options.jitter !== undefined)\n\t\t\t\tctx.meta[\"retry.jitter\"] = options.jitter;\n\t\t\tif (options.retriableStatusCodes !== undefined)\n\t\t\t\tctx.meta[\"retry.retriableStatusCodes\"] = options.retriableStatusCodes;\n\t\t\treturn ctx;\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { TimeoutPluginOptions } from \"./types\";\n\n/**\n * Sets `ctx.timeoutMs` on every request so all requests made by this client\n * abort after the configured duration. The actual abort and\n * {@link TimeoutError} are handled by {@link fetchTransport}.\n *\n * Priority `1` ensures the timeout is stamped before any other plugin (e.g.\n * logger, cache) runs — plugins that read `ctx.timeoutMs` will always see it.\n * Use a `beforeRequest` hook with a lower priority to override per-request.\n *\n * Prefer `ClientConfig.timeoutMs` for a static global timeout. Use this\n * plugin when you need to set or change the timeout through the plugin\n * pipeline (e.g. from environment config loaded asynchronously in `setup`).\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createTimeoutPlugin({ timeoutMs: 5_000 })],\n * });\n * ```\n */\nexport function createTimeoutPlugin(options: TimeoutPluginOptions): ApiPlugin {\n\treturn {\n\t\tname: \"timeout\",\n\t\tpriority: 1,\n\n\t\tbeforeRequest(ctx) {\n\t\t\treturn { ...ctx, timeoutMs: options.timeoutMs };\n\t\t},\n\t};\n}\n","/**\n * Lightweight GraphQL template tag.\n *\n * This intentionally does not parse into a DocumentNode. It preserves the\n * query string while still giving GraphQL-aware tooling a familiar `gql` tag.\n */\nexport function gql(\n\tchunks: TemplateStringsArray,\n\t...values: unknown[]\n): string {\n\tconst source = chunks.reduce(\n\t\t(source, chunk, index) =>\n\t\t\t`${source}${chunk}${index in values ? String(values[index]) : \"\"}`,\n\t\t\"\",\n\t);\n\n\treturn dedupeFragmentDefinitions(source);\n}\n\nfunction dedupeFragmentDefinitions(source: string): string {\n\tconst seen = new Set<string>();\n\tconst fragmentPattern =\n\t\t/\\bfragment\\s+([_A-Za-z][_0-9A-Za-z]*)\\s+on\\s+[_A-Za-z][_0-9A-Za-z]*/g;\n\n\tlet result = \"\";\n\tlet cursor = 0;\n\tlet match = fragmentPattern.exec(source);\n\n\twhile (match) {\n\t\tconst name = match[1];\n\t\tif (!name) {\n\t\t\tmatch = fragmentPattern.exec(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst bodyStart = source.indexOf(\"{\", fragmentPattern.lastIndex);\n\t\tif (bodyStart === -1) {\n\t\t\tmatch = fragmentPattern.exec(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst bodyEnd = findMatchingBrace(source, bodyStart);\n\t\tif (bodyEnd === -1) {\n\t\t\tmatch = fragmentPattern.exec(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fragmentStart = match.index;\n\t\tconst fragmentEnd = consumeTrailingWhitespace(source, bodyEnd + 1);\n\n\t\tif (!seen.has(name)) {\n\t\t\tseen.add(name);\n\t\t\tresult += source.slice(cursor, fragmentEnd);\n\t\t} else {\n\t\t\tresult += source.slice(cursor, fragmentStart);\n\t\t}\n\n\t\tcursor = fragmentEnd;\n\t\tfragmentPattern.lastIndex = fragmentEnd;\n\t\tmatch = fragmentPattern.exec(source);\n\t}\n\n\treturn result + source.slice(cursor);\n}\n\nfunction findMatchingBrace(source: string, openBraceIndex: number): number {\n\tlet depth = 0;\n\n\tfor (let index = openBraceIndex; index < source.length; index++) {\n\t\tconst char = source[index];\n\t\tif (char === \"{\") depth++;\n\t\tif (char === \"}\") depth--;\n\t\tif (depth === 0) return index;\n\t}\n\n\treturn -1;\n}\n\nfunction consumeTrailingWhitespace(source: string, index: number): number {\n\tlet next = index;\n\twhile (next < source.length && /\\s/.test(source[next] ?? \"\")) {\n\t\tnext++;\n\t}\n\treturn next;\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -294,6 +294,7 @@ declare class PluginManager {
294
294
  //#endregion
295
295
  //#region src/client/BaseHttpClient.d.ts
296
296
  /** Per-request options passed to {@link BaseHttpClient.request} and the convenience methods. */
297
+ type ResponseType = "auto" | "json" | "text" | "arrayBuffer" | "blob";
297
298
  interface RequestOptions {
298
299
  /** HTTP method. Defaults to `"GET"`. */
299
300
  method?: HttpMethod;
@@ -328,6 +329,16 @@ interface RequestOptions {
328
329
  * these for cache invalidation, metrics grouping, or filtering.
329
330
  */
330
331
  tags?: string[];
332
+ /**
333
+ * Controls how the response body is parsed. Defaults to content-type based
334
+ * parsing: JSON responses become objects, everything else becomes text.
335
+ */
336
+ responseType?: ResponseType;
337
+ /**
338
+ * Optional response parser for non-2xx bodies. Defaults to `"auto"` so APIs
339
+ * that return binary success payloads can still surface text/JSON errors.
340
+ */
341
+ errorResponseType?: ResponseType;
331
342
  }
332
343
  interface ApiResponse<T = unknown> {
333
344
  data: T;
@@ -764,5 +775,5 @@ declare function resolveUrl(baseUrl: string, path: string): string;
764
775
  //#region src/utils/sleep.d.ts
765
776
  declare function sleep(ms: number): Promise<void>;
766
777
  //#endregion
767
- export { ApiError, type ApiPlugin, type ApiResponse, type AuthPluginOptions, BaseHttpClient, type CachePlugin, type CachePluginOptions, type CacheStore, type ClientConfig, type GraphQLErrorDetail, GraphQLRequestError, type GraphQLRequestOptions, type GraphQLResponse, type HttpMethod, type LoggerInterface, type LoggerPluginOptions, type MaybePromise, MemoryStore, PluginManager, type QueryParams, type QueryPrimitive, type QueryValue, RateLimitError, type RateLimitPluginOptions, type RequestContext, type RequestOptions, type ResponseContext, type RetryConfig, type RetryPluginOptions, TimeoutError, type TimeoutPluginOptions, type Transport, buildUrl, createAuthPlugin, createCachePlugin, createClient, createFetchTransport, createLoggerPlugin, createRateLimitPlugin, createRetryPlugin, createTimeoutPlugin, fetchTransport, gql, isPlainObject, mergeHeaders, resolveUrl, sleep };
778
+ export { ApiError, type ApiPlugin, type ApiResponse, type AuthPluginOptions, BaseHttpClient, type CachePlugin, type CachePluginOptions, type CacheStore, type ClientConfig, type GraphQLErrorDetail, GraphQLRequestError, type GraphQLRequestOptions, type GraphQLResponse, type HttpMethod, type LoggerInterface, type LoggerPluginOptions, type MaybePromise, MemoryStore, PluginManager, type QueryParams, type QueryPrimitive, type QueryValue, RateLimitError, type RateLimitPluginOptions, type RequestContext, type RequestOptions, type ResponseContext, type ResponseType, type RetryConfig, type RetryPluginOptions, TimeoutError, type TimeoutPluginOptions, type Transport, buildUrl, createAuthPlugin, createCachePlugin, createClient, createFetchTransport, createLoggerPlugin, createRateLimitPlugin, createRetryPlugin, createTimeoutPlugin, fetchTransport, gql, isPlainObject, mergeHeaders, resolveUrl, sleep };
768
779
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/common.ts","../src/context/RequestContext.ts","../src/graphql/types.ts","../src/context/ResponseContext.ts","../src/plugin/types.ts","../src/transport/types.ts","../src/client/types.ts","../src/plugin/PluginManager.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/errors/TimeoutError.ts","../src/plugins/auth/types.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/types.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/logger/types.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/types.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/types.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/types.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/GraphQLRequestError.ts","../src/graphql/gql.ts","../src/transport/fetchTransport.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/common.ts","../src/context/RequestContext.ts","../src/graphql/types.ts","../src/context/ResponseContext.ts","../src/plugin/types.ts","../src/transport/types.ts","../src/client/types.ts","../src/plugin/PluginManager.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/errors/TimeoutError.ts","../src/plugins/auth/types.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/types.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/logger/types.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/types.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/types.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/types.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/GraphQLRequestError.ts","../src/graphql/gql.ts","../src/transport/fetchTransport.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -294,6 +294,7 @@ declare class PluginManager {
294
294
  //#endregion
295
295
  //#region src/client/BaseHttpClient.d.ts
296
296
  /** Per-request options passed to {@link BaseHttpClient.request} and the convenience methods. */
297
+ type ResponseType = "auto" | "json" | "text" | "arrayBuffer" | "blob";
297
298
  interface RequestOptions {
298
299
  /** HTTP method. Defaults to `"GET"`. */
299
300
  method?: HttpMethod;
@@ -328,6 +329,16 @@ interface RequestOptions {
328
329
  * these for cache invalidation, metrics grouping, or filtering.
329
330
  */
330
331
  tags?: string[];
332
+ /**
333
+ * Controls how the response body is parsed. Defaults to content-type based
334
+ * parsing: JSON responses become objects, everything else becomes text.
335
+ */
336
+ responseType?: ResponseType;
337
+ /**
338
+ * Optional response parser for non-2xx bodies. Defaults to `"auto"` so APIs
339
+ * that return binary success payloads can still surface text/JSON errors.
340
+ */
341
+ errorResponseType?: ResponseType;
331
342
  }
332
343
  interface ApiResponse<T = unknown> {
333
344
  data: T;
@@ -764,5 +775,5 @@ declare function resolveUrl(baseUrl: string, path: string): string;
764
775
  //#region src/utils/sleep.d.ts
765
776
  declare function sleep(ms: number): Promise<void>;
766
777
  //#endregion
767
- export { ApiError, type ApiPlugin, type ApiResponse, type AuthPluginOptions, BaseHttpClient, type CachePlugin, type CachePluginOptions, type CacheStore, type ClientConfig, type GraphQLErrorDetail, GraphQLRequestError, type GraphQLRequestOptions, type GraphQLResponse, type HttpMethod, type LoggerInterface, type LoggerPluginOptions, type MaybePromise, MemoryStore, PluginManager, type QueryParams, type QueryPrimitive, type QueryValue, RateLimitError, type RateLimitPluginOptions, type RequestContext, type RequestOptions, type ResponseContext, type RetryConfig, type RetryPluginOptions, TimeoutError, type TimeoutPluginOptions, type Transport, buildUrl, createAuthPlugin, createCachePlugin, createClient, createFetchTransport, createLoggerPlugin, createRateLimitPlugin, createRetryPlugin, createTimeoutPlugin, fetchTransport, gql, isPlainObject, mergeHeaders, resolveUrl, sleep };
778
+ export { ApiError, type ApiPlugin, type ApiResponse, type AuthPluginOptions, BaseHttpClient, type CachePlugin, type CachePluginOptions, type CacheStore, type ClientConfig, type GraphQLErrorDetail, GraphQLRequestError, type GraphQLRequestOptions, type GraphQLResponse, type HttpMethod, type LoggerInterface, type LoggerPluginOptions, type MaybePromise, MemoryStore, PluginManager, type QueryParams, type QueryPrimitive, type QueryValue, RateLimitError, type RateLimitPluginOptions, type RequestContext, type RequestOptions, type ResponseContext, type ResponseType, type RetryConfig, type RetryPluginOptions, TimeoutError, type TimeoutPluginOptions, type Transport, buildUrl, createAuthPlugin, createCachePlugin, createClient, createFetchTransport, createLoggerPlugin, createRateLimitPlugin, createRetryPlugin, createTimeoutPlugin, fetchTransport, gql, isPlainObject, mergeHeaders, resolveUrl, sleep };
768
779
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/common.ts","../src/context/RequestContext.ts","../src/graphql/types.ts","../src/context/ResponseContext.ts","../src/plugin/types.ts","../src/transport/types.ts","../src/client/types.ts","../src/plugin/PluginManager.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/errors/TimeoutError.ts","../src/plugins/auth/types.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/types.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/logger/types.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/types.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/types.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/types.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/GraphQLRequestError.ts","../src/graphql/gql.ts","../src/transport/fetchTransport.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/common.ts","../src/context/RequestContext.ts","../src/graphql/types.ts","../src/context/ResponseContext.ts","../src/plugin/types.ts","../src/transport/types.ts","../src/client/types.ts","../src/plugin/PluginManager.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/errors/TimeoutError.ts","../src/plugins/auth/types.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/types.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/logger/types.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/types.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/types.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/types.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/GraphQLRequestError.ts","../src/graphql/gql.ts","../src/transport/fetchTransport.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -216,6 +216,9 @@ function isPlainObject(value) {
216
216
  }
217
217
  //#endregion
218
218
  //#region src/transport/fetchTransport.ts
219
+ const defaultFetch = (input, init) => {
220
+ return globalThis.fetch(input, init);
221
+ };
219
222
  /**
220
223
  * Creates a {@link Transport} backed by the provided `fetch` function.
221
224
  * Use this when you need a polyfill or a custom fetch interceptor:
@@ -227,7 +230,7 @@ function isPlainObject(value) {
227
230
  * const transport = createFetchTransport(nodeFetch as typeof globalThis.fetch);
228
231
  * ```
229
232
  */
230
- function createFetchTransport(fetchFn = globalThis.fetch) {
233
+ function createFetchTransport(fetchFn = defaultFetch) {
231
234
  return { async execute(ctx) {
232
235
  const url = buildUrl(ctx.url, ctx.query);
233
236
  const init = {
@@ -456,7 +459,7 @@ var BaseHttpClient = class {
456
459
  }
457
460
  throw err;
458
461
  }
459
- const parsedBody = await parseBody(rawResponse);
462
+ const parsedBody = await parseBody(rawResponse, rawResponse.ok ? options.responseType : options.errorResponseType ?? "auto");
460
463
  let resCtx = {
461
464
  request: ctx,
462
465
  response: rawResponse,
@@ -597,11 +600,15 @@ var BaseHttpClient = class {
597
600
  await sleep(Math.round(ms));
598
601
  }
599
602
  };
600
- async function parseBody(response) {
603
+ async function parseBody(response, responseType = "auto") {
604
+ if (responseType === "arrayBuffer") return response.arrayBuffer();
605
+ if (responseType === "blob") return response.blob();
601
606
  if (response.status === 204 || response.status === 205) return void 0;
602
607
  if (response.headers.get("content-length") === "0") return void 0;
603
608
  const text = await response.text();
609
+ if (responseType === "text") return text;
604
610
  if (!text) return void 0;
611
+ if (responseType === "json") return JSON.parse(text);
605
612
  if ((response.headers.get("content-type") ?? "").includes("application/json")) return JSON.parse(text);
606
613
  return text;
607
614
  }
@@ -977,7 +984,56 @@ function createTimeoutPlugin(options) {
977
984
  * query string while still giving GraphQL-aware tooling a familiar `gql` tag.
978
985
  */
979
986
  function gql(chunks, ...values) {
980
- return chunks.reduce((source, chunk, index) => `${source}${chunk}${index in values ? String(values[index]) : ""}`, "");
987
+ return dedupeFragmentDefinitions(chunks.reduce((source, chunk, index) => `${source}${chunk}${index in values ? String(values[index]) : ""}`, ""));
988
+ }
989
+ function dedupeFragmentDefinitions(source) {
990
+ const seen = /* @__PURE__ */ new Set();
991
+ const fragmentPattern = /\bfragment\s+([_A-Za-z][_0-9A-Za-z]*)\s+on\s+[_A-Za-z][_0-9A-Za-z]*/g;
992
+ let result = "";
993
+ let cursor = 0;
994
+ let match = fragmentPattern.exec(source);
995
+ while (match) {
996
+ const name = match[1];
997
+ if (!name) {
998
+ match = fragmentPattern.exec(source);
999
+ continue;
1000
+ }
1001
+ const bodyStart = source.indexOf("{", fragmentPattern.lastIndex);
1002
+ if (bodyStart === -1) {
1003
+ match = fragmentPattern.exec(source);
1004
+ continue;
1005
+ }
1006
+ const bodyEnd = findMatchingBrace(source, bodyStart);
1007
+ if (bodyEnd === -1) {
1008
+ match = fragmentPattern.exec(source);
1009
+ continue;
1010
+ }
1011
+ const fragmentStart = match.index;
1012
+ const fragmentEnd = consumeTrailingWhitespace(source, bodyEnd + 1);
1013
+ if (!seen.has(name)) {
1014
+ seen.add(name);
1015
+ result += source.slice(cursor, fragmentEnd);
1016
+ } else result += source.slice(cursor, fragmentStart);
1017
+ cursor = fragmentEnd;
1018
+ fragmentPattern.lastIndex = fragmentEnd;
1019
+ match = fragmentPattern.exec(source);
1020
+ }
1021
+ return result + source.slice(cursor);
1022
+ }
1023
+ function findMatchingBrace(source, openBraceIndex) {
1024
+ let depth = 0;
1025
+ for (let index = openBraceIndex; index < source.length; index++) {
1026
+ const char = source[index];
1027
+ if (char === "{") depth++;
1028
+ if (char === "}") depth--;
1029
+ if (depth === 0) return index;
1030
+ }
1031
+ return -1;
1032
+ }
1033
+ function consumeTrailingWhitespace(source, index) {
1034
+ let next = index;
1035
+ while (next < source.length && /\s/.test(source[next] ?? "")) next++;
1036
+ return next;
981
1037
  }
982
1038
  //#endregion
983
1039
  export { ApiError, BaseHttpClient, GraphQLRequestError, MemoryStore, PluginManager, RateLimitError, TimeoutError, buildUrl, createAuthPlugin, createCachePlugin, createClient, createFetchTransport, createLoggerPlugin, createRateLimitPlugin, createRetryPlugin, createTimeoutPlugin, fetchTransport, gql, isPlainObject, mergeHeaders, resolveUrl, sleep };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/graphql/GraphQLRequestError.ts","../src/plugin/PluginManager.ts","../src/errors/TimeoutError.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/transport/fetchTransport.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/gql.ts"],"sourcesContent":["export class ApiError extends Error {\n\treadonly status: number;\n\treadonly responseBody: unknown;\n\toverride readonly cause: unknown;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tstatus: number,\n\t\tresponseBody?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t\tthis.status = status;\n\t\tthis.responseBody = responseBody;\n\t\tthis.cause = cause;\n\t}\n}\n","import { ApiError } from \"./ApiError\";\n\nexport class RateLimitError extends ApiError {\n\treadonly retryAfterMs: number | undefined;\n\n\tconstructor(retryAfterMs?: number, responseBody?: unknown, cause?: unknown) {\n\t\tsuper(\"Rate limit exceeded\", 429, responseBody, cause);\n\t\tthis.name = \"RateLimitError\";\n\t\tthis.retryAfterMs = retryAfterMs;\n\t}\n}\n","import { ApiError } from \"../errors/ApiError\";\nimport type { GraphQLErrorDetail } from \"./types\";\n\n/**\n * Thrown when a GraphQL server returns a well-formed HTTP 200 response that\n * contains a non-empty `errors` array.\n *\n * Extends {@link ApiError} so that code catching `ApiError` also catches\n * GraphQL-level failures. Callers that need to inspect the individual error\n * objects can narrow with `instanceof GraphQLRequestError` and read\n * `graphqlErrors`.\n *\n * When the server returns both `data` and `errors` (partial result), the\n * partial data is available on `partialData` but the error is still thrown —\n * callers must explicitly opt in to consuming partial results.\n *\n * @example\n * ```ts\n * import { GraphQLRequestError } from \"@api-wrappers/api-core\";\n *\n * try {\n * const data = await client.graphql<MyQuery>(\"/graphql\", { query: QUERY });\n * } catch (err) {\n * if (err instanceof GraphQLRequestError) {\n * for (const e of err.graphqlErrors) {\n * console.error(e.message, e.path);\n * }\n * }\n * }\n * ```\n */\nexport class GraphQLRequestError extends ApiError {\n\t/** The errors array from the GraphQL response envelope. */\n\treadonly graphqlErrors: readonly GraphQLErrorDetail[];\n\t/**\n\t * Partial `data` returned alongside `errors`, if any. `undefined` when\n\t * the server returned no `data` field.\n\t */\n\treadonly partialData: unknown;\n\n\tconstructor(\n\t\terrors: GraphQLErrorDetail[],\n\t\tpartialData?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tconst message = errors.map((e) => e.message).join(\"; \");\n\t\t// Status 200: the HTTP request succeeded; the failure is at the\n\t\t// GraphQL application layer, not the transport layer.\n\t\tsuper(`GraphQL errors: ${message}`, 200, { errors }, cause);\n\t\tthis.name = \"GraphQLRequestError\";\n\t\tthis.graphqlErrors = errors;\n\t\tthis.partialData = partialData;\n\t}\n}\n","import type { LoggerInterface } from \"../client/types\";\nimport type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport type { ApiPlugin } from \"./types\";\n\nexport class PluginManager {\n\tprivate readonly plugins: ApiPlugin[] = [];\n\tprivate readonly logger: LoggerInterface;\n\n\t/**\n\t * @param logger - Logger used when an `onError` handler itself throws.\n\t * Defaults to `console`. Pass a no-op object to silence all output.\n\t */\n\tconstructor(logger: LoggerInterface = console) {\n\t\tthis.logger = logger;\n\t}\n\n\tregister(plugin: ApiPlugin): void {\n\t\tif (plugin.enabled === false) return;\n\t\tthis.plugins.push(plugin);\n\t\t// Sort ascending so lower priority numbers run first in beforeRequest.\n\t\tthis.plugins.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));\n\t}\n\n\tgetAll(): readonly ApiPlugin[] {\n\t\treturn this.plugins;\n\t}\n\n\tasync setup(client: unknown): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\tawait plugin.setup?.(client);\n\t\t}\n\t}\n\n\t/**\n\t * Runs `beforeRequest` in ascending priority order (lowest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync beforeRequest(ctx: RequestContext): Promise<RequestContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.beforeRequest?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"beforeRequest\", err, current);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `afterResponse` in descending priority order (highest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync afterResponse(ctx: ResponseContext): Promise<ResponseContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.afterResponse?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"afterResponse\", err);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `onError` on all plugins in registration order. A plugin throwing\n\t * here is caught and logged via the configured logger but does not\n\t * interrupt other `onError` handlers.\n\t */\n\tasync onError(error: unknown, ctx: RequestContext): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tawait plugin.onError?.(error, ctx);\n\t\t\t} catch (inner) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t`[PluginManager] Plugin \"${plugin.name}\" threw inside onError:`,\n\t\t\t\t\tinner,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\tawait plugin.dispose?.();\n\t\t}\n\t}\n}\n\nexport function getPluginErrorContext(\n\terror: unknown,\n): RequestContext | undefined {\n\tif (!error || typeof error !== \"object\") return undefined;\n\treturn (error as { requestContext?: RequestContext }).requestContext;\n}\n\nfunction wrapPluginError(\n\tname: string,\n\thook: string,\n\tcause: unknown,\n\trequestContext?: RequestContext,\n): Error {\n\tconst message = `Plugin \"${name}\" threw during \"${hook}\"`;\n\tconst err = new Error(message, { cause }) as Error & {\n\t\trequestContext?: RequestContext;\n\t};\n\terr.name = \"PluginError\";\n\terr.requestContext = requestContext;\n\treturn err;\n}\n","export class TimeoutError extends Error {\n\toverride readonly cause: unknown;\n\n\tconstructor(message = \"Request timed out\", cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"TimeoutError\";\n\t\tthis.cause = cause;\n\t}\n}\n","import type { QueryParams } from \"../types/common\";\n\n/**\n * Appends a query string to a URL. Skips nullish values and repeats keys for\n * array values so APIs like TMDB can accept `with_genres=1&with_genres=2`.\n */\nexport function buildUrl(base: string, query?: QueryParams): string {\n\tif (!query || Object.keys(query).length === 0) return base;\n\n\tconst params = new URLSearchParams();\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tif (value === undefined || value === null) continue;\n\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\tparams.append(key, String(item));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tparams.append(key, String(value));\n\t\t}\n\t}\n\n\tconst qs = params.toString();\n\tif (!qs) return base;\n\n\tconst separator = base.includes(\"?\")\n\t\t? base.endsWith(\"?\") || base.endsWith(\"&\")\n\t\t\t? \"\"\n\t\t\t: \"&\"\n\t\t: \"?\";\n\treturn `${base}${separator}${qs}`;\n}\n","export function isPlainObject(\n\tvalue: unknown,\n): value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst proto = Object.getPrototypeOf(value) as unknown;\n\treturn proto === Object.prototype || proto === null;\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport { TimeoutError } from \"../errors/TimeoutError\";\nimport { buildUrl } from \"../utils/buildUrl\";\nimport { isPlainObject } from \"../utils/isPlainObject\";\nimport type { Transport } from \"./types\";\n\n/**\n * Creates a {@link Transport} backed by the provided `fetch` function.\n * Use this when you need a polyfill or a custom fetch interceptor:\n *\n * ```ts\n * import nodeFetch from \"node-fetch\";\n * createClient({ fetch: nodeFetch as typeof globalThis.fetch });\n * // — or set it directly on the transport:\n * const transport = createFetchTransport(nodeFetch as typeof globalThis.fetch);\n * ```\n */\nexport function createFetchTransport(\n\tfetchFn: typeof globalThis.fetch = globalThis.fetch,\n): Transport {\n\treturn {\n\t\tasync execute(ctx: RequestContext): Promise<Response> {\n\t\t\tconst url = buildUrl(ctx.url, ctx.query);\n\t\t\tconst init: RequestInit = {\n\t\t\t\tmethod: ctx.method,\n\t\t\t\theaders: ctx.headers,\n\t\t\t};\n\n\t\t\tconst hasBody =\n\t\t\t\tctx.body !== undefined && ctx.method !== \"GET\" && ctx.method !== \"HEAD\";\n\n\t\t\tif (hasBody) {\n\t\t\t\tinit.body = serializeRequestBody(ctx.body, ctx.headers);\n\t\t\t}\n\n\t\t\tif (ctx.timeoutMs !== undefined || ctx.signal) {\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tlet timedOut = false;\n\t\t\t\tconst abortFromParent = () => controller.abort(ctx.signal?.reason);\n\t\t\t\tconst timer =\n\t\t\t\t\tctx.timeoutMs !== undefined\n\t\t\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t\t\t}, ctx.timeoutMs)\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tif (ctx.signal) {\n\t\t\t\t\tif (ctx.signal.aborted) {\n\t\t\t\t\t\tcontroller.abort(ctx.signal.reason);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.signal.addEventListener(\"abort\", abortFromParent, {\n\t\t\t\t\t\t\tonce: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treturn await fetchFn(url, { ...init, signal: controller.signal });\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (timedOut && err instanceof Error && err.name === \"AbortError\") {\n\t\t\t\t\t\tthrow new TimeoutError(\n\t\t\t\t\t\t\t`Request timed out after ${ctx.timeoutMs}ms`,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t} finally {\n\t\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\t\tctx.signal?.removeEventListener(\"abort\", abortFromParent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn fetchFn(url, init);\n\t\t},\n\t};\n}\n\n/**\n * Default {@link Transport} backed by the global `fetch` API.\n *\n * Behaviour:\n * - Builds the final URL from `ctx.url` + `ctx.query` via {@link buildUrl}.\n * - Serialises `ctx.body` to JSON for non-GET/HEAD requests.\n * - Wires an `AbortController` when `ctx.timeoutMs` is set; throws\n * {@link TimeoutError} on abort.\n *\n * Replace this with a custom {@link Transport} in tests, or provide a custom\n * `fetch` function via {@link ClientConfig.fetch}.\n */\nexport const fetchTransport: Transport = createFetchTransport();\n\nfunction serializeRequestBody(\n\tbody: unknown,\n\theaders: Record<string, string>,\n): BodyInit {\n\tif (isBodyInit(body)) return body;\n\n\tconst contentType = headers[\"content-type\"] ?? \"\";\n\tif (\n\t\tisPlainObject(body) ||\n\t\tArray.isArray(body) ||\n\t\tcontentType.includes(\"json\")\n\t) {\n\t\treturn JSON.stringify(body);\n\t}\n\n\treturn String(body);\n}\n\nfunction isBodyInit(body: unknown): body is BodyInit {\n\tif (typeof body === \"string\") return true;\n\tif (body instanceof ArrayBuffer) return true;\n\tif (ArrayBuffer.isView(body)) return true;\n\tif (typeof Blob !== \"undefined\" && body instanceof Blob) return true;\n\tif (typeof FormData !== \"undefined\" && body instanceof FormData) return true;\n\tif (\n\t\ttypeof URLSearchParams !== \"undefined\" &&\n\t\tbody instanceof URLSearchParams\n\t) {\n\t\treturn true;\n\t}\n\tif (typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n","/**\n * Merges header objects left to right. Keys are normalized to\n * lowercase so merging is case-insensitive. Later sources win.\n */\nexport function mergeHeaders(\n\t...sources: (Record<string, string> | undefined)[]\n): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tfor (const [key, value] of Object.entries(source)) {\n\t\t\tresult[key.toLowerCase()] = value;\n\t\t}\n\t}\n\treturn result;\n}\n","/**\n * Joins a client base URL and request path without requiring callers to keep\n * slashes perfectly aligned. Absolute request URLs are returned unchanged.\n */\nexport function resolveUrl(baseUrl: string, path: string): string {\n\tif (/^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(path)) return path;\n\n\tconst base = baseUrl.replace(/\\/+$/, \"\");\n\tconst next = path.replace(/^\\/+/, \"\");\n\n\treturn next ? `${base}/${next}` : base;\n}\n","export function sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport { ApiError } from \"../errors/ApiError\";\nimport { RateLimitError } from \"../errors/RateLimitError\";\nimport { GraphQLRequestError } from \"../graphql/GraphQLRequestError\";\nimport type { GraphQLRequestOptions, GraphQLResponse } from \"../graphql/types\";\nimport { getPluginErrorContext, PluginManager } from \"../plugin/PluginManager\";\nimport {\n\tcreateFetchTransport,\n\tfetchTransport,\n} from \"../transport/fetchTransport\";\nimport type { HttpMethod, QueryParams } from \"../types/common\";\nimport { mergeHeaders } from \"../utils/mergeHeaders\";\nimport { resolveUrl } from \"../utils/resolveUrl\";\nimport { sleep } from \"../utils/sleep\";\nimport type { ClientConfig } from \"./types\";\n\n/** Per-request options passed to {@link BaseHttpClient.request} and the convenience methods. */\nexport interface RequestOptions {\n\t/** HTTP method. Defaults to `\"GET\"`. */\n\tmethod?: HttpMethod;\n\t/**\n\t * Additional headers merged on top of `ClientConfig.defaultHeaders`.\n\t * These take precedence; `content-type: application/json` is always\n\t * present and is the lowest-priority default.\n\t */\n\theaders?: Record<string, string>;\n\t/** Request body. Serialised to JSON by {@link fetchTransport}. Ignored for GET and HEAD. */\n\tbody?: unknown;\n\t/**\n\t * Query string parameters appended to the URL. `undefined` values are\n\t * omitted. Numbers and booleans are coerced to strings. Array values are\n\t * emitted as repeated query parameters.\n\t */\n\tquery?: QueryParams;\n\t/** Optional caller-provided abort signal. Composes with `timeoutMs`. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Per-request timeout override in milliseconds. Takes precedence over\n\t * `ClientConfig.timeoutMs`. Throws {@link TimeoutError} when exceeded.\n\t */\n\ttimeoutMs?: number;\n\t/**\n\t * Explicit cache key used by {@link createCachePlugin}. When omitted the\n\t * plugin derives a key from the method, URL, and query string.\n\t */\n\tcacheKey?: string;\n\t/**\n\t * Arbitrary string tags attached to the request context. Plugins may use\n\t * these for cache invalidation, metrics grouping, or filtering.\n\t */\n\ttags?: string[];\n}\n\nexport interface ApiResponse<T = unknown> {\n\tdata: T;\n\tresponse: Response;\n\trequest: RequestContext;\n\tmeta: Record<string, unknown>;\n}\n\nconst DEFAULT_RETRIABLE_STATUS_CODES = [429, 500, 502, 503, 504];\n\n/**\n * Core HTTP client. Manages the plugin lifecycle, retry loop, and transport\n * dispatch for all requests.\n *\n * Plugins are initialised lazily on the first call to {@link request} (or any\n * convenience method). Call {@link dispose} when the client is no longer\n * needed so plugins can release timers, connections, or cache handles.\n *\n * Extend this class to add domain-specific methods while keeping the plugin\n * and transport infrastructure intact.\n *\n * @example\n * ```ts\n * // Prefer createClient() in application code:\n * const client = createClient({ baseUrl: \"https://api.example.com/v1\" });\n *\n * // Or subclass for wrapper packages:\n * class MyApiClient extends BaseHttpClient {\n * getUser(id: string) { return this.get<User>(`/users/${id}`); }\n * }\n * ```\n */\nexport class BaseHttpClient {\n\tprotected readonly config: ClientConfig;\n\tprotected readonly pluginManager: PluginManager;\n\tprivate initialized = false;\n\tprivate initPromise: Promise<void> | undefined;\n\n\tconstructor(config: ClientConfig) {\n\t\tthis.config = config;\n\t\tthis.pluginManager = new PluginManager(config.logger);\n\t\tfor (const plugin of config.plugins ?? []) {\n\t\t\tthis.pluginManager.register(plugin);\n\t\t}\n\t}\n\n\t/** Initializes all plugins. Called lazily on first request. */\n\tasync init(): Promise<void> {\n\t\tif (this.initialized) return;\n\t\tthis.initPromise ??= this.pluginManager\n\t\t\t.setup(this)\n\t\t\t.then(() => {\n\t\t\t\tthis.initialized = true;\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\tthis.initPromise = undefined;\n\t\t\t\tthrow err;\n\t\t\t});\n\t\tawait this.initPromise;\n\t}\n\n\t/** Disposes all plugins. Call when the client is no longer needed. */\n\tasync dispose(): Promise<void> {\n\t\tawait this.pluginManager.dispose();\n\t\tthis.initialized = false;\n\t\tthis.initPromise = undefined;\n\t}\n\n\t/**\n\t * Executes an HTTP request through the full plugin pipeline.\n\t *\n\t * Lifecycle per attempt:\n\t * 1. Build `RequestContext` with merged headers, query, and retry state.\n\t * 2. Run `beforeRequest` hooks (ascending priority). A plugin may set\n\t * `ctx.syntheticResponse` to skip the transport entirely (e.g. cache hit).\n\t * 3. Merge any `retry.*` meta written by {@link createRetryPlugin}.\n\t * 4. Call transport (skipped when `syntheticResponse` is set).\n\t * 5. Parse the response body (JSON or text).\n\t * 6. Run `afterResponse` hooks (descending priority).\n\t * 7. Retry on retriable status codes; throw on terminal failures.\n\t *\n\t * @param path - Path appended to `ClientConfig.baseUrl`. Should start with `/`.\n\t * @param options - Per-request overrides for method, headers, body, query, etc.\n\t * @returns The parsed response body cast to `T`.\n\t * @throws {@link ApiError} for non-2xx responses.\n\t * @throws {@link RateLimitError} for 429 responses.\n\t * @throws {@link TimeoutError} when `timeoutMs` is exceeded.\n\t */\n\tasync request<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<T> {\n\t\tconst result = await this.requestWithResponse<T>(path, options);\n\t\treturn result.data;\n\t}\n\n\t/**\n\t * Executes a request and returns the parsed body plus the final response\n\t * context. Use this in wrappers that need response headers, status, or\n\t * plugin metadata while keeping the same error/retry behaviour as\n\t * {@link request}.\n\t */\n\tasync requestWithResponse<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<ApiResponse<T>> {\n\t\tawait this.init();\n\n\t\tconst transport =\n\t\t\tthis.config.transport ??\n\t\t\t(this.config.fetch\n\t\t\t\t? createFetchTransport(this.config.fetch)\n\t\t\t\t: fetchTransport);\n\t\tconst retryCfg = this.config.retry;\n\t\t// These are `let` so createRetryPlugin can override them per-request via\n\t\t// ctx.meta after beforeRequest runs (see merge block below).\n\t\tlet maxAttempts = retryCfg?.maxAttempts ?? 1;\n\t\tlet baseDelay = retryCfg?.delayMs ?? 500;\n\t\tlet jitter = retryCfg?.jitter ?? true;\n\t\tlet retriableCodes =\n\t\t\tretryCfg?.retriableStatusCodes ?? DEFAULT_RETRIABLE_STATUS_CODES;\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tconst baseCtx: RequestContext = {\n\t\t\t\turl: resolveUrl(this.config.baseUrl, path),\n\t\t\t\tmethod: options.method ?? \"GET\",\n\t\t\t\theaders: mergeHeaders(\n\t\t\t\t\t{ \"content-type\": \"application/json\" },\n\t\t\t\t\tthis.config.defaultHeaders,\n\t\t\t\t\toptions.headers,\n\t\t\t\t),\n\t\t\t\tbody: options.body,\n\t\t\t\tquery: options.query,\n\t\t\t\tsignal: options.signal,\n\t\t\t\tmeta: {},\n\t\t\t\tcacheKey: options.cacheKey,\n\t\t\t\ttags: options.tags,\n\t\t\t\tretryCount: maxAttempts - 1 - attempt,\n\t\t\t\tattempt,\n\t\t\t\ttimeoutMs: options.timeoutMs ?? this.config.timeoutMs,\n\t\t\t};\n\n\t\t\tlet ctx: RequestContext;\n\t\t\ttry {\n\t\t\t\tctx = await this.pluginManager.beforeRequest(baseCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(\n\t\t\t\t\terr,\n\t\t\t\t\tgetPluginErrorContext(err) ?? baseCtx,\n\t\t\t\t);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\t// Merge per-request retry overrides written by createRetryPlugin.\n\t\t\t// Only keys explicitly set by the plugin are present, so unset keys\n\t\t\t// leave the config-level defaults untouched.\n\t\t\t// Because the for-loop condition re-evaluates `attempt < maxAttempts`\n\t\t\t// on every iteration, updating maxAttempts here takes effect\n\t\t\t// immediately for all remaining attempts.\n\t\t\tif (ctx.meta[\"retry.maxAttempts\"] !== undefined)\n\t\t\t\tmaxAttempts = ctx.meta[\"retry.maxAttempts\"] as number;\n\t\t\tif (ctx.meta[\"retry.delayMs\"] !== undefined)\n\t\t\t\tbaseDelay = ctx.meta[\"retry.delayMs\"] as number;\n\t\t\tif (ctx.meta[\"retry.jitter\"] !== undefined)\n\t\t\t\tjitter = ctx.meta[\"retry.jitter\"] as boolean;\n\t\t\tif (ctx.meta[\"retry.retriableStatusCodes\"] !== undefined)\n\t\t\t\tretriableCodes = ctx.meta[\"retry.retriableStatusCodes\"] as number[];\n\n\t\t\tlet rawResponse: Response;\n\t\t\tif (ctx.syntheticResponse) {\n\t\t\t\t// A plugin (e.g. cache) pre-populated the response — skip the\n\t\t\t\t// network entirely.\n\t\t\t\trawResponse = ctx.syntheticResponse;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\trawResponse = await transport.execute(ctx);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\t\tlastError = err;\n\n\t\t\t\t\tif (attempt < maxAttempts - 1) {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst parsedBody = await parseBody(rawResponse);\n\n\t\t\tlet resCtx: ResponseContext = {\n\t\t\t\trequest: ctx,\n\t\t\t\tresponse: rawResponse,\n\t\t\t\tparsedBody,\n\t\t\t\tmeta: {},\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tresCtx = await this.pluginManager.afterResponse(resCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tif (!rawResponse.ok) {\n\t\t\t\tconst shouldRetry =\n\t\t\t\t\tretriableCodes.includes(rawResponse.status) &&\n\t\t\t\t\tattempt < maxAttempts - 1;\n\n\t\t\t\tif (shouldRetry) {\n\t\t\t\t\tif (rawResponse.status === 429) {\n\t\t\t\t\t\tconst wait = readRetryAfterMs(rawResponse);\n\t\t\t\t\t\tawait this.waitForRetry(attempt, wait ?? baseDelay, false);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t}\n\t\t\t\t\tlastError = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst err = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdata: resCtx.parsedBody as T,\n\t\t\t\tresponse: resCtx.response,\n\t\t\t\trequest: resCtx.request,\n\t\t\t\tmeta: resCtx.meta,\n\t\t\t};\n\t\t}\n\n\t\tthrow lastError;\n\t}\n\n\t// ─── Convenience methods ────────────────────────────────────────────────────\n\t// Each method is a thin wrapper around request() that fixes the HTTP verb.\n\n\t/** Sends a GET request. The response body is not cached unless a cache plugin is registered. */\n\tget<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"GET\" });\n\t}\n\n\tpost<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"POST\", body });\n\t}\n\n\tput<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PUT\", body });\n\t}\n\n\tpatch<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PATCH\", body });\n\t}\n\n\tdelete<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"DELETE\" });\n\t}\n\n\thead<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"HEAD\" });\n\t}\n\n\toptions<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"OPTIONS\" });\n\t}\n\n\t/**\n\t * Executes a GraphQL query or mutation against a single endpoint path.\n\t *\n\t * The request is a `POST` with `content-type: application/json` carrying\n\t * `{ query, variables?, operationName? }` as the body. It flows through\n\t * the full plugin lifecycle (beforeRequest → transport → afterResponse →\n\t * onError) and respects all retry configuration, exactly like REST calls.\n\t *\n\t * **Error handling:**\n\t * - HTTP-level failures (429, 500, timeout) throw the same error classes\n\t * as REST requests (`RateLimitError`, `ApiError`, `TimeoutError`).\n\t * - A successful HTTP 200 that contains a non-empty `errors` array throws\n\t * {@link GraphQLRequestError}, which extends `ApiError`.\n\t *\n\t * **Caching:**\n\t * The cache plugin skips `POST` requests by default. Pass an explicit\n\t * `cacheKey` in options to opt a specific operation into caching.\n\t *\n\t * @typeParam TData - Shape of the `data` field in the GraphQL response.\n\t * @typeParam TVariables - Shape of the `variables` object. Defaults to\n\t * `Record<string, unknown>`.\n\t * @param path - Endpoint path, e.g. `\"/graphql\"`. Appended to `baseUrl`.\n\t * @param options - Query document, variables, and optional per-request overrides.\n\t * @returns The `data` field from the GraphQL response envelope.\n\t * @throws {@link GraphQLRequestError} when `response.errors` is non-empty.\n\t * @throws {@link ApiError} / {@link RateLimitError} / {@link TimeoutError} on\n\t * HTTP-level failures.\n\t *\n\t * @example\n\t * ```ts\n\t * const data = await client.graphql<GetUserQuery, GetUserQueryVariables>(\n\t * \"/graphql\",\n\t * { query: GET_USER, variables: { id: \"123\" } },\n\t * );\n\t * ```\n\t */\n\tasync graphql<\n\t\tTData = unknown,\n\t\tTVariables extends object = Record<string, unknown>,\n\t>(path: string, options: GraphQLRequestOptions<TVariables>): Promise<TData> {\n\t\tconst {\n\t\t\tquery,\n\t\t\tvariables,\n\t\t\toperationName,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t} = options;\n\n\t\tconst envelope = await this.request<GraphQLResponse<TData>>(path, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: {\n\t\t\t\tquery,\n\t\t\t\t...(variables !== undefined && { variables }),\n\t\t\t\t...(operationName !== undefined && { operationName }),\n\t\t\t},\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t});\n\n\t\t// Surface GraphQL application-layer errors as a typed exception.\n\t\t// We throw even when partial data is present — callers who need\n\t\t// partial results can catch GraphQLRequestError and read .partialData.\n\t\tif (envelope.errors && envelope.errors.length > 0) {\n\t\t\tthrow new GraphQLRequestError(envelope.errors, envelope.data);\n\t\t}\n\n\t\t// data may be undefined if the server returned an empty response —\n\t\t// safe to cast because TData defaults to unknown.\n\t\treturn envelope.data as TData;\n\t}\n\n\tprivate async waitForRetry(\n\t\tattempt: number,\n\t\tbaseDelay: number,\n\t\tuseJitter: boolean,\n\t): Promise<void> {\n\t\t// Exponential backoff: delay * 2^attempt\n\t\tconst exponential = baseDelay * 2 ** attempt;\n\t\tconst ms = useJitter\n\t\t\t? exponential * (0.5 + Math.random() * 0.5)\n\t\t\t: exponential;\n\t\tawait sleep(Math.round(ms));\n\t}\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function parseBody(response: Response): Promise<unknown> {\n\tif (response.status === 204 || response.status === 205) return undefined;\n\tif (response.headers.get(\"content-length\") === \"0\") return undefined;\n\n\tconst text = await response.text();\n\tif (!text) return undefined;\n\n\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\tif (contentType.includes(\"application/json\")) {\n\t\treturn JSON.parse(text);\n\t}\n\treturn text;\n}\n\nfunction normalizeHttpError(response: Response, body: unknown): ApiError {\n\tif (response.status === 429) {\n\t\treturn new RateLimitError(readRetryAfterMs(response), body);\n\t}\n\treturn new ApiError(\n\t\t`Request failed with status ${response.status}`,\n\t\tresponse.status,\n\t\tbody,\n\t);\n}\n\nfunction readRetryAfterMs(response: Response): number | undefined {\n\tconst raw = response.headers.get(\"retry-after\");\n\tif (!raw) return undefined;\n\n\tconst seconds = Number(raw);\n\tif (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n\n\tconst date = Date.parse(raw);\n\tif (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n\n\treturn undefined;\n}\n","import { BaseHttpClient } from \"./BaseHttpClient\";\nimport type { ClientConfig } from \"./types\";\n\n/**\n * Factory function that creates a {@link BaseHttpClient} from the given\n * config. Prefer this over `new BaseHttpClient(config)` in application code\n * so that the concrete class stays an implementation detail.\n *\n * @example\n * ```ts\n * const client = createClient({\n * baseUrl: \"https://api.example.com/v1\",\n * defaultHeaders: { \"x-api-key\": \"secret\" },\n * retry: { maxAttempts: 3, delayMs: 300 },\n * plugins: [createLoggerPlugin(), createCachePlugin({ ttlMs: 60_000 })],\n * });\n * ```\n */\nexport function createClient(config: ClientConfig): BaseHttpClient {\n\treturn new BaseHttpClient(config);\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { MaybePromise } from \"../../types/common\";\nimport type { AuthPluginOptions } from \"./types\";\n\ntype TokenInput =\n\t| string\n\t| (() => MaybePromise<string | null | undefined>)\n\t| AuthPluginOptions;\n\n/**\n * Adds an auth token header before each request. The token can be static or\n * loaded asynchronously per request, which covers wrappers with refreshable\n * access tokens.\n */\nexport function createAuthPlugin(input: TokenInput): ApiPlugin {\n\tconst options = normalizeOptions(input);\n\tconst headerName = (options.headerName ?? \"authorization\").toLowerCase();\n\tconst scheme = options.scheme === undefined ? \"Bearer\" : options.scheme;\n\n\treturn {\n\t\tname: \"auth\",\n\t\tpriority: 2,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst token = await options.getToken();\n\t\t\tif (!token) return ctx;\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\theaders: {\n\t\t\t\t\t...ctx.headers,\n\t\t\t\t\t[headerName]: scheme ? `${scheme} ${token}` : token,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction normalizeOptions(input: TokenInput): AuthPluginOptions {\n\tif (typeof input === \"string\") {\n\t\treturn { getToken: () => input };\n\t}\n\tif (typeof input === \"function\") {\n\t\treturn { getToken: input };\n\t}\n\treturn input;\n}\n","import type { CacheStore } from \"./types\";\n\ninterface CacheEntry {\n\tvalue: unknown;\n\texpiresAt: number | null;\n}\n\nexport class MemoryStore implements CacheStore {\n\tprivate readonly store = new Map<string, CacheEntry>();\n\n\tget(key: string): unknown | undefined {\n\t\tconst entry = this.store.get(key);\n\t\tif (!entry) return undefined;\n\t\tif (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n\t\t\tthis.store.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn entry.value;\n\t}\n\n\tset(key: string, value: unknown, ttlMs?: number): void {\n\t\tthis.store.set(key, {\n\t\t\tvalue,\n\t\t\texpiresAt: ttlMs != null ? Date.now() + ttlMs : null,\n\t\t});\n\t}\n\n\tdelete(key: string): void {\n\t\tthis.store.delete(key);\n\t}\n\n\tclear(): void {\n\t\tthis.store.clear();\n\t}\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport { MemoryStore } from \"./memoryStore\";\nimport type { CachePlugin, CachePluginOptions } from \"./types\";\n\nconst DEFAULT_CACHEABLE_METHODS = [\"GET\"] as const;\nconst CACHE_HIT_META_KEY = \"cache.hit\";\n\nexport function createCachePlugin(\n\toptions: CachePluginOptions = {},\n): CachePlugin {\n\tconst store = options.store ?? new MemoryStore();\n\tconst ttlMs = options.ttlMs;\n\tconst methods: string[] = options.methods ?? [...DEFAULT_CACHEABLE_METHODS];\n\tconst generateKey = options.generateKey ?? defaultCacheKey;\n\n\t// tag → Set<cacheKey>: populated during afterResponse, used by invalidateByTag.\n\tconst tagIndex = new Map<string, Set<string>>();\n\n\treturn {\n\t\tname: \"cache\",\n\t\tpriority: 20,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tif (!methods.includes(ctx.method)) return ctx;\n\n\t\t\tconst key = ctx.cacheKey ?? generateKey(ctx);\n\t\t\tconst cached = await store.get(key);\n\n\t\t\tif (cached !== undefined) {\n\t\t\t\t// Build a synthetic Response so the rest of the pipeline\n\t\t\t\t// (afterResponse, status checks) sees a uniform shape.\n\t\t\t\tconst syntheticResponse = new Response(JSON.stringify(cached), {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\n\t\t\t\t// Setting syntheticResponse tells BaseHttpClient to skip the\n\t\t\t\t// transport entirely and use this response directly.\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\t...ctx.meta,\n\t\t\t\t\t\t[CACHE_HIT_META_KEY]: { key, data: cached },\n\t\t\t\t\t},\n\t\t\t\t\tsyntheticResponse,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, \"cache.key\": key },\n\t\t\t};\n\t\t},\n\n\t\tasync afterResponse(ctx) {\n\t\t\tconst hit = ctx.request.meta[CACHE_HIT_META_KEY] as\n\t\t\t\t| { key: string; data: unknown }\n\t\t\t\t| undefined;\n\n\t\t\tif (hit) {\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tparsedBody: hit.data,\n\t\t\t\t\tmeta: { ...ctx.meta, \"cache.served\": true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst key = ctx.request.meta[\"cache.key\"] as string | undefined;\n\t\t\tif (key && methods.includes(ctx.request.method) && ctx.response.ok) {\n\t\t\t\tawait store.set(key, ctx.parsedBody, ttlMs);\n\t\t\t\tctx.meta[\"cache.stored\"] = true;\n\n\t\t\t\t// Record tag → key associations for invalidateByTag.\n\t\t\t\tfor (const tag of ctx.request.tags ?? []) {\n\t\t\t\t\tif (!tagIndex.has(tag)) tagIndex.set(tag, new Set());\n\t\t\t\t\ttagIndex.get(tag)?.add(key);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ctx;\n\t\t},\n\n\t\tasync invalidate(key: string): Promise<void> {\n\t\t\tawait store.delete(key);\n\t\t\t// Clean up any tag index entries pointing to this key.\n\t\t\tfor (const keys of tagIndex.values()) {\n\t\t\t\tkeys.delete(key);\n\t\t\t}\n\t\t},\n\n\t\tasync invalidateByTag(tag: string): Promise<void> {\n\t\t\tconst keys = tagIndex.get(tag);\n\t\t\tif (!keys || keys.size === 0) return;\n\t\t\tfor (const key of keys) {\n\t\t\t\tawait store.delete(key);\n\t\t\t\t// Remove this key from all other tag index entries too.\n\t\t\t\tfor (const otherKeys of tagIndex.values()) {\n\t\t\t\t\totherKeys.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttagIndex.delete(tag);\n\t\t},\n\t};\n}\n\nfunction defaultCacheKey(ctx: RequestContext): string {\n\tconst queryStr = ctx.query\n\t\t? new URLSearchParams(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tObject.entries(ctx.query)\n\t\t\t\t\t\t.filter(([, v]) => v !== undefined)\n\t\t\t\t\t\t.map(([k, v]) => [k, String(v)]),\n\t\t\t\t),\n\t\t\t).toString()\n\t\t: \"\";\n\treturn `${ctx.method}:${ctx.url}${queryStr ? `?${queryStr}` : \"\"}`;\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { LoggerPluginOptions } from \"./types\";\n\n/**\n * Creates a plugin that logs request start, response status, and errors.\n *\n * Log lines are prefixed with `[api-core]` and include the HTTP method, URL,\n * attempt number (on `beforeRequest`), and status code (on `afterResponse`).\n *\n * Priority `10` means it runs _after_ auth or header-mutation plugins\n * (priority < 10) so the logged URL and headers reflect the final request,\n * but _before_ the cache plugin (priority `20`) so cache hits are still\n * visible in the log.\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createLoggerPlugin({ logRequest: true, logResponse: true })],\n * });\n * ```\n */\nexport function createLoggerPlugin(\n\toptions: LoggerPluginOptions = {},\n): ApiPlugin {\n\tconst {\n\t\tlogRequest = true,\n\t\tlogResponse = true,\n\t\tlogError = true,\n\t\tlogger = console,\n\t} = options;\n\n\treturn {\n\t\tname: \"logger\",\n\t\tpriority: 10,\n\n\t\tbeforeRequest(ctx) {\n\t\t\tif (logRequest) {\n\t\t\t\tlogger.info(`[api-core] --> ${ctx.method} ${ctx.url}`, {\n\t\t\t\t\tattempt: ctx.attempt,\n\t\t\t\t\tbody: ctx.body,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\tif (logResponse) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`[api-core] <-- ${ctx.response.status} ${ctx.request.method} ${ctx.request.url}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(error, ctx) {\n\t\t\tif (logError) {\n\t\t\t\tlogger.error(`[api-core] ERR ${ctx.method} ${ctx.url}`, error);\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport type { ApiPlugin } from \"../../plugin/types\";\nimport type { RateLimitPluginOptions } from \"./types\";\n\nconst RELEASE_META_KEY = \"rateLimit.release\";\n\ninterface QueueItem {\n\tresolve: (release: () => void) => void;\n}\n\n/**\n * Throttles request starts before they reach the transport. Supports\n * concurrency, minimum spacing, and fixed-window request budgets.\n */\nexport function createRateLimitPlugin(\n\toptions: RateLimitPluginOptions = {},\n): ApiPlugin {\n\tconst maxConcurrent = options.maxConcurrent ?? Number.POSITIVE_INFINITY;\n\tconst minTimeMs = options.minTimeMs ?? 0;\n\tconst maxRequestsPerInterval = options.maxRequestsPerInterval;\n\tconst intervalMs = options.intervalMs;\n\n\tif (maxConcurrent <= 0) {\n\t\tthrow new Error(\"maxConcurrent must be greater than 0\");\n\t}\n\tif (minTimeMs < 0) {\n\t\tthrow new Error(\"minTimeMs must be greater than or equal to 0\");\n\t}\n\tif (\n\t\t(maxRequestsPerInterval !== undefined || intervalMs !== undefined) &&\n\t\t(!maxRequestsPerInterval ||\n\t\t\tmaxRequestsPerInterval <= 0 ||\n\t\t\t!intervalMs ||\n\t\t\tintervalMs <= 0)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"maxRequestsPerInterval and intervalMs must both be greater than 0\",\n\t\t);\n\t}\n\n\tconst queue: QueueItem[] = [];\n\tconst starts: number[] = [];\n\tlet active = 0;\n\tlet lastStartAt = 0;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\n\tconst processQueue = () => {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\n\t\twhile (queue.length > 0) {\n\t\t\tconst now = Date.now();\n\t\t\tpruneStarts(now);\n\n\t\t\tif (active >= maxConcurrent) return;\n\n\t\t\tconst waitMs = getWaitMs(now);\n\t\t\tif (waitMs > 0) {\n\t\t\t\ttimer = setTimeout(processQueue, waitMs);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst item = queue.shift();\n\t\t\tif (!item) return;\n\n\t\t\tactive++;\n\t\t\tlastStartAt = now;\n\t\t\tstarts.push(now);\n\n\t\t\tlet released = false;\n\t\t\titem.resolve(() => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tactive--;\n\t\t\t\tprocessQueue();\n\t\t\t});\n\t\t}\n\t};\n\n\tconst acquire = () =>\n\t\tnew Promise<() => void>((resolve) => {\n\t\t\tqueue.push({ resolve });\n\t\t\tprocessQueue();\n\t\t});\n\n\tconst release = (ctx: RequestContext) => {\n\t\tconst releaseFn = ctx.meta[RELEASE_META_KEY] as (() => void) | undefined;\n\t\tif (!releaseFn) return;\n\t\tdelete ctx.meta[RELEASE_META_KEY];\n\t\treleaseFn();\n\t};\n\n\tconst pruneStarts = (now: number) => {\n\t\tif (!intervalMs) return;\n\t\twhile (starts.length > 0 && now - (starts[0] ?? 0) >= intervalMs) {\n\t\t\tstarts.shift();\n\t\t}\n\t};\n\n\tconst getWaitMs = (now: number): number => {\n\t\tconst spacingWait = Math.max(0, lastStartAt + minTimeMs - now);\n\t\tif (!maxRequestsPerInterval || !intervalMs) return spacingWait;\n\n\t\tif (starts.length < maxRequestsPerInterval) return spacingWait;\n\n\t\tconst oldest = starts[0] ?? now;\n\t\tconst intervalWait = Math.max(0, oldest + intervalMs - now);\n\t\treturn Math.max(spacingWait, intervalWait);\n\t};\n\n\treturn {\n\t\tname: \"rate-limit\",\n\t\tpriority: 1,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst releaseFn = await acquire();\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, [RELEASE_META_KEY]: releaseFn },\n\t\t\t};\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\trelease(ctx.request);\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(_error, ctx) {\n\t\t\trelease(ctx);\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { RetryPluginOptions } from \"./types\";\n\n/**\n * Writes retry configuration into request context meta so the\n * BaseHttpClient retry loop can read it. Use this when you need\n * per-request retry overrides rather than global ClientConfig.retry.\n */\nexport function createRetryPlugin(options: RetryPluginOptions = {}): ApiPlugin {\n\treturn {\n\t\tname: \"retry\",\n\t\tpriority: 5,\n\n\t\tbeforeRequest(ctx) {\n\t\t\t// Only write keys for values the caller explicitly provided.\n\t\t\t// Unset options fall through to ClientConfig.retry defaults inside\n\t\t\t// BaseHttpClient, so the plugin does not need to supply fallbacks.\n\t\t\tif (options.maxAttempts !== undefined)\n\t\t\t\tctx.meta[\"retry.maxAttempts\"] = options.maxAttempts;\n\t\t\tif (options.delayMs !== undefined)\n\t\t\t\tctx.meta[\"retry.delayMs\"] = options.delayMs;\n\t\t\tif (options.jitter !== undefined)\n\t\t\t\tctx.meta[\"retry.jitter\"] = options.jitter;\n\t\t\tif (options.retriableStatusCodes !== undefined)\n\t\t\t\tctx.meta[\"retry.retriableStatusCodes\"] = options.retriableStatusCodes;\n\t\t\treturn ctx;\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { TimeoutPluginOptions } from \"./types\";\n\n/**\n * Sets `ctx.timeoutMs` on every request so all requests made by this client\n * abort after the configured duration. The actual abort and\n * {@link TimeoutError} are handled by {@link fetchTransport}.\n *\n * Priority `1` ensures the timeout is stamped before any other plugin (e.g.\n * logger, cache) runs — plugins that read `ctx.timeoutMs` will always see it.\n * Use a `beforeRequest` hook with a lower priority to override per-request.\n *\n * Prefer `ClientConfig.timeoutMs` for a static global timeout. Use this\n * plugin when you need to set or change the timeout through the plugin\n * pipeline (e.g. from environment config loaded asynchronously in `setup`).\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createTimeoutPlugin({ timeoutMs: 5_000 })],\n * });\n * ```\n */\nexport function createTimeoutPlugin(options: TimeoutPluginOptions): ApiPlugin {\n\treturn {\n\t\tname: \"timeout\",\n\t\tpriority: 1,\n\n\t\tbeforeRequest(ctx) {\n\t\t\treturn { ...ctx, timeoutMs: options.timeoutMs };\n\t\t},\n\t};\n}\n","/**\n * Lightweight GraphQL template tag.\n *\n * This intentionally does not parse into a DocumentNode. It preserves the\n * query string while still giving GraphQL-aware tooling a familiar `gql` tag.\n */\nexport function gql(\n\tchunks: TemplateStringsArray,\n\t...values: unknown[]\n): string {\n\treturn chunks.reduce(\n\t\t(source, chunk, index) =>\n\t\t\t`${source}${chunk}${index in values ? String(values[index]) : \"\"}`,\n\t\t\"\",\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/ApiError.ts","../src/errors/RateLimitError.ts","../src/graphql/GraphQLRequestError.ts","../src/plugin/PluginManager.ts","../src/errors/TimeoutError.ts","../src/utils/buildUrl.ts","../src/utils/isPlainObject.ts","../src/transport/fetchTransport.ts","../src/utils/mergeHeaders.ts","../src/utils/resolveUrl.ts","../src/utils/sleep.ts","../src/client/BaseHttpClient.ts","../src/client/createClient.ts","../src/plugins/auth/authPlugin.ts","../src/plugins/cache/memoryStore.ts","../src/plugins/cache/cachePlugin.ts","../src/plugins/logger/loggerPlugin.ts","../src/plugins/rateLimit/rateLimitPlugin.ts","../src/plugins/retry/retryPlugin.ts","../src/plugins/timeout/timeoutPlugin.ts","../src/graphql/gql.ts"],"sourcesContent":["export class ApiError extends Error {\n\treadonly status: number;\n\treadonly responseBody: unknown;\n\toverride readonly cause: unknown;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tstatus: number,\n\t\tresponseBody?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t\tthis.status = status;\n\t\tthis.responseBody = responseBody;\n\t\tthis.cause = cause;\n\t}\n}\n","import { ApiError } from \"./ApiError\";\n\nexport class RateLimitError extends ApiError {\n\treadonly retryAfterMs: number | undefined;\n\n\tconstructor(retryAfterMs?: number, responseBody?: unknown, cause?: unknown) {\n\t\tsuper(\"Rate limit exceeded\", 429, responseBody, cause);\n\t\tthis.name = \"RateLimitError\";\n\t\tthis.retryAfterMs = retryAfterMs;\n\t}\n}\n","import { ApiError } from \"../errors/ApiError\";\nimport type { GraphQLErrorDetail } from \"./types\";\n\n/**\n * Thrown when a GraphQL server returns a well-formed HTTP 200 response that\n * contains a non-empty `errors` array.\n *\n * Extends {@link ApiError} so that code catching `ApiError` also catches\n * GraphQL-level failures. Callers that need to inspect the individual error\n * objects can narrow with `instanceof GraphQLRequestError` and read\n * `graphqlErrors`.\n *\n * When the server returns both `data` and `errors` (partial result), the\n * partial data is available on `partialData` but the error is still thrown —\n * callers must explicitly opt in to consuming partial results.\n *\n * @example\n * ```ts\n * import { GraphQLRequestError } from \"@api-wrappers/api-core\";\n *\n * try {\n * const data = await client.graphql<MyQuery>(\"/graphql\", { query: QUERY });\n * } catch (err) {\n * if (err instanceof GraphQLRequestError) {\n * for (const e of err.graphqlErrors) {\n * console.error(e.message, e.path);\n * }\n * }\n * }\n * ```\n */\nexport class GraphQLRequestError extends ApiError {\n\t/** The errors array from the GraphQL response envelope. */\n\treadonly graphqlErrors: readonly GraphQLErrorDetail[];\n\t/**\n\t * Partial `data` returned alongside `errors`, if any. `undefined` when\n\t * the server returned no `data` field.\n\t */\n\treadonly partialData: unknown;\n\n\tconstructor(\n\t\terrors: GraphQLErrorDetail[],\n\t\tpartialData?: unknown,\n\t\tcause?: unknown,\n\t) {\n\t\tconst message = errors.map((e) => e.message).join(\"; \");\n\t\t// Status 200: the HTTP request succeeded; the failure is at the\n\t\t// GraphQL application layer, not the transport layer.\n\t\tsuper(`GraphQL errors: ${message}`, 200, { errors }, cause);\n\t\tthis.name = \"GraphQLRequestError\";\n\t\tthis.graphqlErrors = errors;\n\t\tthis.partialData = partialData;\n\t}\n}\n","import type { LoggerInterface } from \"../client/types\";\nimport type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport type { ApiPlugin } from \"./types\";\n\nexport class PluginManager {\n\tprivate readonly plugins: ApiPlugin[] = [];\n\tprivate readonly logger: LoggerInterface;\n\n\t/**\n\t * @param logger - Logger used when an `onError` handler itself throws.\n\t * Defaults to `console`. Pass a no-op object to silence all output.\n\t */\n\tconstructor(logger: LoggerInterface = console) {\n\t\tthis.logger = logger;\n\t}\n\n\tregister(plugin: ApiPlugin): void {\n\t\tif (plugin.enabled === false) return;\n\t\tthis.plugins.push(plugin);\n\t\t// Sort ascending so lower priority numbers run first in beforeRequest.\n\t\tthis.plugins.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));\n\t}\n\n\tgetAll(): readonly ApiPlugin[] {\n\t\treturn this.plugins;\n\t}\n\n\tasync setup(client: unknown): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\tawait plugin.setup?.(client);\n\t\t}\n\t}\n\n\t/**\n\t * Runs `beforeRequest` in ascending priority order (lowest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync beforeRequest(ctx: RequestContext): Promise<RequestContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.beforeRequest?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"beforeRequest\", err, current);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `afterResponse` in descending priority order (highest first).\n\t * Each plugin may return a mutated context.\n\t */\n\tasync afterResponse(ctx: ResponseContext): Promise<ResponseContext> {\n\t\tlet current = ctx;\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\ttry {\n\t\t\t\tconst result = await plugin.afterResponse?.(current);\n\t\t\t\tif (result != null) current = result;\n\t\t\t} catch (err) {\n\t\t\t\tthrow wrapPluginError(plugin.name, \"afterResponse\", err);\n\t\t\t}\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Runs `onError` on all plugins in registration order. A plugin throwing\n\t * here is caught and logged via the configured logger but does not\n\t * interrupt other `onError` handlers.\n\t */\n\tasync onError(error: unknown, ctx: RequestContext): Promise<void> {\n\t\tfor (const plugin of this.plugins) {\n\t\t\ttry {\n\t\t\t\tawait plugin.onError?.(error, ctx);\n\t\t\t} catch (inner) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t`[PluginManager] Plugin \"${plugin.name}\" threw inside onError:`,\n\t\t\t\t\tinner,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tfor (const plugin of [...this.plugins].reverse()) {\n\t\t\tawait plugin.dispose?.();\n\t\t}\n\t}\n}\n\nexport function getPluginErrorContext(\n\terror: unknown,\n): RequestContext | undefined {\n\tif (!error || typeof error !== \"object\") return undefined;\n\treturn (error as { requestContext?: RequestContext }).requestContext;\n}\n\nfunction wrapPluginError(\n\tname: string,\n\thook: string,\n\tcause: unknown,\n\trequestContext?: RequestContext,\n): Error {\n\tconst message = `Plugin \"${name}\" threw during \"${hook}\"`;\n\tconst err = new Error(message, { cause }) as Error & {\n\t\trequestContext?: RequestContext;\n\t};\n\terr.name = \"PluginError\";\n\terr.requestContext = requestContext;\n\treturn err;\n}\n","export class TimeoutError extends Error {\n\toverride readonly cause: unknown;\n\n\tconstructor(message = \"Request timed out\", cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.name = \"TimeoutError\";\n\t\tthis.cause = cause;\n\t}\n}\n","import type { QueryParams } from \"../types/common\";\n\n/**\n * Appends a query string to a URL. Skips nullish values and repeats keys for\n * array values so APIs like TMDB can accept `with_genres=1&with_genres=2`.\n */\nexport function buildUrl(base: string, query?: QueryParams): string {\n\tif (!query || Object.keys(query).length === 0) return base;\n\n\tconst params = new URLSearchParams();\n\tfor (const [key, value] of Object.entries(query)) {\n\t\tif (value === undefined || value === null) continue;\n\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\tparams.append(key, String(item));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tparams.append(key, String(value));\n\t\t}\n\t}\n\n\tconst qs = params.toString();\n\tif (!qs) return base;\n\n\tconst separator = base.includes(\"?\")\n\t\t? base.endsWith(\"?\") || base.endsWith(\"&\")\n\t\t\t? \"\"\n\t\t\t: \"&\"\n\t\t: \"?\";\n\treturn `${base}${separator}${qs}`;\n}\n","export function isPlainObject(\n\tvalue: unknown,\n): value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst proto = Object.getPrototypeOf(value) as unknown;\n\treturn proto === Object.prototype || proto === null;\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport { TimeoutError } from \"../errors/TimeoutError\";\nimport { buildUrl } from \"../utils/buildUrl\";\nimport { isPlainObject } from \"../utils/isPlainObject\";\nimport type { Transport } from \"./types\";\n\nconst defaultFetch: typeof globalThis.fetch = (input, init) => {\n\treturn globalThis.fetch(input, init);\n};\n\n/**\n * Creates a {@link Transport} backed by the provided `fetch` function.\n * Use this when you need a polyfill or a custom fetch interceptor:\n *\n * ```ts\n * import nodeFetch from \"node-fetch\";\n * createClient({ fetch: nodeFetch as typeof globalThis.fetch });\n * // — or set it directly on the transport:\n * const transport = createFetchTransport(nodeFetch as typeof globalThis.fetch);\n * ```\n */\nexport function createFetchTransport(\n\tfetchFn: typeof globalThis.fetch = defaultFetch,\n): Transport {\n\treturn {\n\t\tasync execute(ctx: RequestContext): Promise<Response> {\n\t\t\tconst url = buildUrl(ctx.url, ctx.query);\n\t\t\tconst init: RequestInit = {\n\t\t\t\tmethod: ctx.method,\n\t\t\t\theaders: ctx.headers,\n\t\t\t};\n\n\t\t\tconst hasBody =\n\t\t\t\tctx.body !== undefined && ctx.method !== \"GET\" && ctx.method !== \"HEAD\";\n\n\t\t\tif (hasBody) {\n\t\t\t\tinit.body = serializeRequestBody(ctx.body, ctx.headers);\n\t\t\t}\n\n\t\t\tif (ctx.timeoutMs !== undefined || ctx.signal) {\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tlet timedOut = false;\n\t\t\t\tconst abortFromParent = () => controller.abort(ctx.signal?.reason);\n\t\t\t\tconst timer =\n\t\t\t\t\tctx.timeoutMs !== undefined\n\t\t\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t\t\t}, ctx.timeoutMs)\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tif (ctx.signal) {\n\t\t\t\t\tif (ctx.signal.aborted) {\n\t\t\t\t\t\tcontroller.abort(ctx.signal.reason);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.signal.addEventListener(\"abort\", abortFromParent, {\n\t\t\t\t\t\t\tonce: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treturn await fetchFn(url, { ...init, signal: controller.signal });\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (timedOut && err instanceof Error && err.name === \"AbortError\") {\n\t\t\t\t\t\tthrow new TimeoutError(\n\t\t\t\t\t\t\t`Request timed out after ${ctx.timeoutMs}ms`,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t} finally {\n\t\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\t\tctx.signal?.removeEventListener(\"abort\", abortFromParent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn fetchFn(url, init);\n\t\t},\n\t};\n}\n\n/**\n * Default {@link Transport} backed by the global `fetch` API.\n *\n * Behaviour:\n * - Builds the final URL from `ctx.url` + `ctx.query` via {@link buildUrl}.\n * - Serialises `ctx.body` to JSON for non-GET/HEAD requests.\n * - Wires an `AbortController` when `ctx.timeoutMs` is set; throws\n * {@link TimeoutError} on abort.\n *\n * Replace this with a custom {@link Transport} in tests, or provide a custom\n * `fetch` function via {@link ClientConfig.fetch}.\n */\nexport const fetchTransport: Transport = createFetchTransport();\n\nfunction serializeRequestBody(\n\tbody: unknown,\n\theaders: Record<string, string>,\n): BodyInit {\n\tif (isBodyInit(body)) return body;\n\n\tconst contentType = headers[\"content-type\"] ?? \"\";\n\tif (\n\t\tisPlainObject(body) ||\n\t\tArray.isArray(body) ||\n\t\tcontentType.includes(\"json\")\n\t) {\n\t\treturn JSON.stringify(body);\n\t}\n\n\treturn String(body);\n}\n\nfunction isBodyInit(body: unknown): body is BodyInit {\n\tif (typeof body === \"string\") return true;\n\tif (body instanceof ArrayBuffer) return true;\n\tif (ArrayBuffer.isView(body)) return true;\n\tif (typeof Blob !== \"undefined\" && body instanceof Blob) return true;\n\tif (typeof FormData !== \"undefined\" && body instanceof FormData) return true;\n\tif (\n\t\ttypeof URLSearchParams !== \"undefined\" &&\n\t\tbody instanceof URLSearchParams\n\t) {\n\t\treturn true;\n\t}\n\tif (typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n","/**\n * Merges header objects left to right. Keys are normalized to\n * lowercase so merging is case-insensitive. Later sources win.\n */\nexport function mergeHeaders(\n\t...sources: (Record<string, string> | undefined)[]\n): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tfor (const [key, value] of Object.entries(source)) {\n\t\t\tresult[key.toLowerCase()] = value;\n\t\t}\n\t}\n\treturn result;\n}\n","/**\n * Joins a client base URL and request path without requiring callers to keep\n * slashes perfectly aligned. Absolute request URLs are returned unchanged.\n */\nexport function resolveUrl(baseUrl: string, path: string): string {\n\tif (/^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(path)) return path;\n\n\tconst base = baseUrl.replace(/\\/+$/, \"\");\n\tconst next = path.replace(/^\\/+/, \"\");\n\n\treturn next ? `${base}/${next}` : base;\n}\n","export function sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { RequestContext } from \"../context/RequestContext\";\nimport type { ResponseContext } from \"../context/ResponseContext\";\nimport { ApiError } from \"../errors/ApiError\";\nimport { RateLimitError } from \"../errors/RateLimitError\";\nimport { GraphQLRequestError } from \"../graphql/GraphQLRequestError\";\nimport type { GraphQLRequestOptions, GraphQLResponse } from \"../graphql/types\";\nimport { getPluginErrorContext, PluginManager } from \"../plugin/PluginManager\";\nimport {\n\tcreateFetchTransport,\n\tfetchTransport,\n} from \"../transport/fetchTransport\";\nimport type { HttpMethod, QueryParams } from \"../types/common\";\nimport { mergeHeaders } from \"../utils/mergeHeaders\";\nimport { resolveUrl } from \"../utils/resolveUrl\";\nimport { sleep } from \"../utils/sleep\";\nimport type { ClientConfig } from \"./types\";\n\n/** Per-request options passed to {@link BaseHttpClient.request} and the convenience methods. */\nexport type ResponseType = \"auto\" | \"json\" | \"text\" | \"arrayBuffer\" | \"blob\";\n\nexport interface RequestOptions {\n\t/** HTTP method. Defaults to `\"GET\"`. */\n\tmethod?: HttpMethod;\n\t/**\n\t * Additional headers merged on top of `ClientConfig.defaultHeaders`.\n\t * These take precedence; `content-type: application/json` is always\n\t * present and is the lowest-priority default.\n\t */\n\theaders?: Record<string, string>;\n\t/** Request body. Serialised to JSON by {@link fetchTransport}. Ignored for GET and HEAD. */\n\tbody?: unknown;\n\t/**\n\t * Query string parameters appended to the URL. `undefined` values are\n\t * omitted. Numbers and booleans are coerced to strings. Array values are\n\t * emitted as repeated query parameters.\n\t */\n\tquery?: QueryParams;\n\t/** Optional caller-provided abort signal. Composes with `timeoutMs`. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Per-request timeout override in milliseconds. Takes precedence over\n\t * `ClientConfig.timeoutMs`. Throws {@link TimeoutError} when exceeded.\n\t */\n\ttimeoutMs?: number;\n\t/**\n\t * Explicit cache key used by {@link createCachePlugin}. When omitted the\n\t * plugin derives a key from the method, URL, and query string.\n\t */\n\tcacheKey?: string;\n\t/**\n\t * Arbitrary string tags attached to the request context. Plugins may use\n\t * these for cache invalidation, metrics grouping, or filtering.\n\t */\n\ttags?: string[];\n\t/**\n\t * Controls how the response body is parsed. Defaults to content-type based\n\t * parsing: JSON responses become objects, everything else becomes text.\n\t */\n\tresponseType?: ResponseType;\n\t/**\n\t * Optional response parser for non-2xx bodies. Defaults to `\"auto\"` so APIs\n\t * that return binary success payloads can still surface text/JSON errors.\n\t */\n\terrorResponseType?: ResponseType;\n}\n\nexport interface ApiResponse<T = unknown> {\n\tdata: T;\n\tresponse: Response;\n\trequest: RequestContext;\n\tmeta: Record<string, unknown>;\n}\n\nconst DEFAULT_RETRIABLE_STATUS_CODES = [429, 500, 502, 503, 504];\n\n/**\n * Core HTTP client. Manages the plugin lifecycle, retry loop, and transport\n * dispatch for all requests.\n *\n * Plugins are initialised lazily on the first call to {@link request} (or any\n * convenience method). Call {@link dispose} when the client is no longer\n * needed so plugins can release timers, connections, or cache handles.\n *\n * Extend this class to add domain-specific methods while keeping the plugin\n * and transport infrastructure intact.\n *\n * @example\n * ```ts\n * // Prefer createClient() in application code:\n * const client = createClient({ baseUrl: \"https://api.example.com/v1\" });\n *\n * // Or subclass for wrapper packages:\n * class MyApiClient extends BaseHttpClient {\n * getUser(id: string) { return this.get<User>(`/users/${id}`); }\n * }\n * ```\n */\nexport class BaseHttpClient {\n\tprotected readonly config: ClientConfig;\n\tprotected readonly pluginManager: PluginManager;\n\tprivate initialized = false;\n\tprivate initPromise: Promise<void> | undefined;\n\n\tconstructor(config: ClientConfig) {\n\t\tthis.config = config;\n\t\tthis.pluginManager = new PluginManager(config.logger);\n\t\tfor (const plugin of config.plugins ?? []) {\n\t\t\tthis.pluginManager.register(plugin);\n\t\t}\n\t}\n\n\t/** Initializes all plugins. Called lazily on first request. */\n\tasync init(): Promise<void> {\n\t\tif (this.initialized) return;\n\t\tthis.initPromise ??= this.pluginManager\n\t\t\t.setup(this)\n\t\t\t.then(() => {\n\t\t\t\tthis.initialized = true;\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\tthis.initPromise = undefined;\n\t\t\t\tthrow err;\n\t\t\t});\n\t\tawait this.initPromise;\n\t}\n\n\t/** Disposes all plugins. Call when the client is no longer needed. */\n\tasync dispose(): Promise<void> {\n\t\tawait this.pluginManager.dispose();\n\t\tthis.initialized = false;\n\t\tthis.initPromise = undefined;\n\t}\n\n\t/**\n\t * Executes an HTTP request through the full plugin pipeline.\n\t *\n\t * Lifecycle per attempt:\n\t * 1. Build `RequestContext` with merged headers, query, and retry state.\n\t * 2. Run `beforeRequest` hooks (ascending priority). A plugin may set\n\t * `ctx.syntheticResponse` to skip the transport entirely (e.g. cache hit).\n\t * 3. Merge any `retry.*` meta written by {@link createRetryPlugin}.\n\t * 4. Call transport (skipped when `syntheticResponse` is set).\n\t * 5. Parse the response body (JSON or text).\n\t * 6. Run `afterResponse` hooks (descending priority).\n\t * 7. Retry on retriable status codes; throw on terminal failures.\n\t *\n\t * @param path - Path appended to `ClientConfig.baseUrl`. Should start with `/`.\n\t * @param options - Per-request overrides for method, headers, body, query, etc.\n\t * @returns The parsed response body cast to `T`.\n\t * @throws {@link ApiError} for non-2xx responses.\n\t * @throws {@link RateLimitError} for 429 responses.\n\t * @throws {@link TimeoutError} when `timeoutMs` is exceeded.\n\t */\n\tasync request<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<T> {\n\t\tconst result = await this.requestWithResponse<T>(path, options);\n\t\treturn result.data;\n\t}\n\n\t/**\n\t * Executes a request and returns the parsed body plus the final response\n\t * context. Use this in wrappers that need response headers, status, or\n\t * plugin metadata while keeping the same error/retry behaviour as\n\t * {@link request}.\n\t */\n\tasync requestWithResponse<T = unknown>(\n\t\tpath: string,\n\t\toptions: RequestOptions = {},\n\t): Promise<ApiResponse<T>> {\n\t\tawait this.init();\n\n\t\tconst transport =\n\t\t\tthis.config.transport ??\n\t\t\t(this.config.fetch\n\t\t\t\t? createFetchTransport(this.config.fetch)\n\t\t\t\t: fetchTransport);\n\t\tconst retryCfg = this.config.retry;\n\t\t// These are `let` so createRetryPlugin can override them per-request via\n\t\t// ctx.meta after beforeRequest runs (see merge block below).\n\t\tlet maxAttempts = retryCfg?.maxAttempts ?? 1;\n\t\tlet baseDelay = retryCfg?.delayMs ?? 500;\n\t\tlet jitter = retryCfg?.jitter ?? true;\n\t\tlet retriableCodes =\n\t\t\tretryCfg?.retriableStatusCodes ?? DEFAULT_RETRIABLE_STATUS_CODES;\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tconst baseCtx: RequestContext = {\n\t\t\t\turl: resolveUrl(this.config.baseUrl, path),\n\t\t\t\tmethod: options.method ?? \"GET\",\n\t\t\t\theaders: mergeHeaders(\n\t\t\t\t\t{ \"content-type\": \"application/json\" },\n\t\t\t\t\tthis.config.defaultHeaders,\n\t\t\t\t\toptions.headers,\n\t\t\t\t),\n\t\t\t\tbody: options.body,\n\t\t\t\tquery: options.query,\n\t\t\t\tsignal: options.signal,\n\t\t\t\tmeta: {},\n\t\t\t\tcacheKey: options.cacheKey,\n\t\t\t\ttags: options.tags,\n\t\t\t\tretryCount: maxAttempts - 1 - attempt,\n\t\t\t\tattempt,\n\t\t\t\ttimeoutMs: options.timeoutMs ?? this.config.timeoutMs,\n\t\t\t};\n\n\t\t\tlet ctx: RequestContext;\n\t\t\ttry {\n\t\t\t\tctx = await this.pluginManager.beforeRequest(baseCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(\n\t\t\t\t\terr,\n\t\t\t\t\tgetPluginErrorContext(err) ?? baseCtx,\n\t\t\t\t);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\t// Merge per-request retry overrides written by createRetryPlugin.\n\t\t\t// Only keys explicitly set by the plugin are present, so unset keys\n\t\t\t// leave the config-level defaults untouched.\n\t\t\t// Because the for-loop condition re-evaluates `attempt < maxAttempts`\n\t\t\t// on every iteration, updating maxAttempts here takes effect\n\t\t\t// immediately for all remaining attempts.\n\t\t\tif (ctx.meta[\"retry.maxAttempts\"] !== undefined)\n\t\t\t\tmaxAttempts = ctx.meta[\"retry.maxAttempts\"] as number;\n\t\t\tif (ctx.meta[\"retry.delayMs\"] !== undefined)\n\t\t\t\tbaseDelay = ctx.meta[\"retry.delayMs\"] as number;\n\t\t\tif (ctx.meta[\"retry.jitter\"] !== undefined)\n\t\t\t\tjitter = ctx.meta[\"retry.jitter\"] as boolean;\n\t\t\tif (ctx.meta[\"retry.retriableStatusCodes\"] !== undefined)\n\t\t\t\tretriableCodes = ctx.meta[\"retry.retriableStatusCodes\"] as number[];\n\n\t\t\tlet rawResponse: Response;\n\t\t\tif (ctx.syntheticResponse) {\n\t\t\t\t// A plugin (e.g. cache) pre-populated the response — skip the\n\t\t\t\t// network entirely.\n\t\t\t\trawResponse = ctx.syntheticResponse;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\trawResponse = await transport.execute(ctx);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\t\tlastError = err;\n\n\t\t\t\t\tif (attempt < maxAttempts - 1) {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst parsedBody = await parseBody(\n\t\t\t\trawResponse,\n\t\t\t\trawResponse.ok\n\t\t\t\t\t? options.responseType\n\t\t\t\t\t: (options.errorResponseType ?? \"auto\"),\n\t\t\t);\n\n\t\t\tlet resCtx: ResponseContext = {\n\t\t\t\trequest: ctx,\n\t\t\t\tresponse: rawResponse,\n\t\t\t\tparsedBody,\n\t\t\t\tmeta: {},\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tresCtx = await this.pluginManager.afterResponse(resCtx);\n\t\t\t} catch (err) {\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tif (!rawResponse.ok) {\n\t\t\t\tconst shouldRetry =\n\t\t\t\t\tretriableCodes.includes(rawResponse.status) &&\n\t\t\t\t\tattempt < maxAttempts - 1;\n\n\t\t\t\tif (shouldRetry) {\n\t\t\t\t\tif (rawResponse.status === 429) {\n\t\t\t\t\t\tconst wait = readRetryAfterMs(rawResponse);\n\t\t\t\t\t\tawait this.waitForRetry(attempt, wait ?? baseDelay, false);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.waitForRetry(attempt, baseDelay, jitter);\n\t\t\t\t\t}\n\t\t\t\t\tlastError = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst err = normalizeHttpError(rawResponse, resCtx.parsedBody);\n\t\t\t\tawait this.pluginManager.onError(err, ctx);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdata: resCtx.parsedBody as T,\n\t\t\t\tresponse: resCtx.response,\n\t\t\t\trequest: resCtx.request,\n\t\t\t\tmeta: resCtx.meta,\n\t\t\t};\n\t\t}\n\n\t\tthrow lastError;\n\t}\n\n\t// ─── Convenience methods ────────────────────────────────────────────────────\n\t// Each method is a thin wrapper around request() that fixes the HTTP verb.\n\n\t/** Sends a GET request. The response body is not cached unless a cache plugin is registered. */\n\tget<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"GET\" });\n\t}\n\n\tpost<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"POST\", body });\n\t}\n\n\tput<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PUT\", body });\n\t}\n\n\tpatch<T = unknown>(\n\t\tpath: string,\n\t\tbody?: unknown,\n\t\toptions?: Omit<RequestOptions, \"method\" | \"body\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"PATCH\", body });\n\t}\n\n\tdelete<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"DELETE\" });\n\t}\n\n\thead<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"HEAD\" });\n\t}\n\n\toptions<T = unknown>(\n\t\tpath: string,\n\t\toptions?: Omit<RequestOptions, \"method\">,\n\t): Promise<T> {\n\t\treturn this.request<T>(path, { ...options, method: \"OPTIONS\" });\n\t}\n\n\t/**\n\t * Executes a GraphQL query or mutation against a single endpoint path.\n\t *\n\t * The request is a `POST` with `content-type: application/json` carrying\n\t * `{ query, variables?, operationName? }` as the body. It flows through\n\t * the full plugin lifecycle (beforeRequest → transport → afterResponse →\n\t * onError) and respects all retry configuration, exactly like REST calls.\n\t *\n\t * **Error handling:**\n\t * - HTTP-level failures (429, 500, timeout) throw the same error classes\n\t * as REST requests (`RateLimitError`, `ApiError`, `TimeoutError`).\n\t * - A successful HTTP 200 that contains a non-empty `errors` array throws\n\t * {@link GraphQLRequestError}, which extends `ApiError`.\n\t *\n\t * **Caching:**\n\t * The cache plugin skips `POST` requests by default. Pass an explicit\n\t * `cacheKey` in options to opt a specific operation into caching.\n\t *\n\t * @typeParam TData - Shape of the `data` field in the GraphQL response.\n\t * @typeParam TVariables - Shape of the `variables` object. Defaults to\n\t * `Record<string, unknown>`.\n\t * @param path - Endpoint path, e.g. `\"/graphql\"`. Appended to `baseUrl`.\n\t * @param options - Query document, variables, and optional per-request overrides.\n\t * @returns The `data` field from the GraphQL response envelope.\n\t * @throws {@link GraphQLRequestError} when `response.errors` is non-empty.\n\t * @throws {@link ApiError} / {@link RateLimitError} / {@link TimeoutError} on\n\t * HTTP-level failures.\n\t *\n\t * @example\n\t * ```ts\n\t * const data = await client.graphql<GetUserQuery, GetUserQueryVariables>(\n\t * \"/graphql\",\n\t * { query: GET_USER, variables: { id: \"123\" } },\n\t * );\n\t * ```\n\t */\n\tasync graphql<\n\t\tTData = unknown,\n\t\tTVariables extends object = Record<string, unknown>,\n\t>(path: string, options: GraphQLRequestOptions<TVariables>): Promise<TData> {\n\t\tconst {\n\t\t\tquery,\n\t\t\tvariables,\n\t\t\toperationName,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t} = options;\n\n\t\tconst envelope = await this.request<GraphQLResponse<TData>>(path, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: {\n\t\t\t\tquery,\n\t\t\t\t...(variables !== undefined && { variables }),\n\t\t\t\t...(operationName !== undefined && { operationName }),\n\t\t\t},\n\t\t\theaders,\n\t\t\tsignal,\n\t\t\ttimeoutMs,\n\t\t\tcacheKey,\n\t\t\ttags,\n\t\t});\n\n\t\t// Surface GraphQL application-layer errors as a typed exception.\n\t\t// We throw even when partial data is present — callers who need\n\t\t// partial results can catch GraphQLRequestError and read .partialData.\n\t\tif (envelope.errors && envelope.errors.length > 0) {\n\t\t\tthrow new GraphQLRequestError(envelope.errors, envelope.data);\n\t\t}\n\n\t\t// data may be undefined if the server returned an empty response —\n\t\t// safe to cast because TData defaults to unknown.\n\t\treturn envelope.data as TData;\n\t}\n\n\tprivate async waitForRetry(\n\t\tattempt: number,\n\t\tbaseDelay: number,\n\t\tuseJitter: boolean,\n\t): Promise<void> {\n\t\t// Exponential backoff: delay * 2^attempt\n\t\tconst exponential = baseDelay * 2 ** attempt;\n\t\tconst ms = useJitter\n\t\t\t? exponential * (0.5 + Math.random() * 0.5)\n\t\t\t: exponential;\n\t\tawait sleep(Math.round(ms));\n\t}\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nasync function parseBody(\n\tresponse: Response,\n\tresponseType: ResponseType = \"auto\",\n): Promise<unknown> {\n\tif (responseType === \"arrayBuffer\") return response.arrayBuffer();\n\tif (responseType === \"blob\") return response.blob();\n\n\tif (response.status === 204 || response.status === 205) return undefined;\n\tif (response.headers.get(\"content-length\") === \"0\") return undefined;\n\n\tconst text = await response.text();\n\tif (responseType === \"text\") return text;\n\tif (!text) return undefined;\n\n\tif (responseType === \"json\") return JSON.parse(text);\n\n\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\tif (contentType.includes(\"application/json\")) {\n\t\treturn JSON.parse(text);\n\t}\n\treturn text;\n}\n\nfunction normalizeHttpError(response: Response, body: unknown): ApiError {\n\tif (response.status === 429) {\n\t\treturn new RateLimitError(readRetryAfterMs(response), body);\n\t}\n\treturn new ApiError(\n\t\t`Request failed with status ${response.status}`,\n\t\tresponse.status,\n\t\tbody,\n\t);\n}\n\nfunction readRetryAfterMs(response: Response): number | undefined {\n\tconst raw = response.headers.get(\"retry-after\");\n\tif (!raw) return undefined;\n\n\tconst seconds = Number(raw);\n\tif (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);\n\n\tconst date = Date.parse(raw);\n\tif (!Number.isNaN(date)) return Math.max(0, date - Date.now());\n\n\treturn undefined;\n}\n","import { BaseHttpClient } from \"./BaseHttpClient\";\nimport type { ClientConfig } from \"./types\";\n\n/**\n * Factory function that creates a {@link BaseHttpClient} from the given\n * config. Prefer this over `new BaseHttpClient(config)` in application code\n * so that the concrete class stays an implementation detail.\n *\n * @example\n * ```ts\n * const client = createClient({\n * baseUrl: \"https://api.example.com/v1\",\n * defaultHeaders: { \"x-api-key\": \"secret\" },\n * retry: { maxAttempts: 3, delayMs: 300 },\n * plugins: [createLoggerPlugin(), createCachePlugin({ ttlMs: 60_000 })],\n * });\n * ```\n */\nexport function createClient(config: ClientConfig): BaseHttpClient {\n\treturn new BaseHttpClient(config);\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { MaybePromise } from \"../../types/common\";\nimport type { AuthPluginOptions } from \"./types\";\n\ntype TokenInput =\n\t| string\n\t| (() => MaybePromise<string | null | undefined>)\n\t| AuthPluginOptions;\n\n/**\n * Adds an auth token header before each request. The token can be static or\n * loaded asynchronously per request, which covers wrappers with refreshable\n * access tokens.\n */\nexport function createAuthPlugin(input: TokenInput): ApiPlugin {\n\tconst options = normalizeOptions(input);\n\tconst headerName = (options.headerName ?? \"authorization\").toLowerCase();\n\tconst scheme = options.scheme === undefined ? \"Bearer\" : options.scheme;\n\n\treturn {\n\t\tname: \"auth\",\n\t\tpriority: 2,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst token = await options.getToken();\n\t\t\tif (!token) return ctx;\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\theaders: {\n\t\t\t\t\t...ctx.headers,\n\t\t\t\t\t[headerName]: scheme ? `${scheme} ${token}` : token,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction normalizeOptions(input: TokenInput): AuthPluginOptions {\n\tif (typeof input === \"string\") {\n\t\treturn { getToken: () => input };\n\t}\n\tif (typeof input === \"function\") {\n\t\treturn { getToken: input };\n\t}\n\treturn input;\n}\n","import type { CacheStore } from \"./types\";\n\ninterface CacheEntry {\n\tvalue: unknown;\n\texpiresAt: number | null;\n}\n\nexport class MemoryStore implements CacheStore {\n\tprivate readonly store = new Map<string, CacheEntry>();\n\n\tget(key: string): unknown | undefined {\n\t\tconst entry = this.store.get(key);\n\t\tif (!entry) return undefined;\n\t\tif (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n\t\t\tthis.store.delete(key);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn entry.value;\n\t}\n\n\tset(key: string, value: unknown, ttlMs?: number): void {\n\t\tthis.store.set(key, {\n\t\t\tvalue,\n\t\t\texpiresAt: ttlMs != null ? Date.now() + ttlMs : null,\n\t\t});\n\t}\n\n\tdelete(key: string): void {\n\t\tthis.store.delete(key);\n\t}\n\n\tclear(): void {\n\t\tthis.store.clear();\n\t}\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport { MemoryStore } from \"./memoryStore\";\nimport type { CachePlugin, CachePluginOptions } from \"./types\";\n\nconst DEFAULT_CACHEABLE_METHODS = [\"GET\"] as const;\nconst CACHE_HIT_META_KEY = \"cache.hit\";\n\nexport function createCachePlugin(\n\toptions: CachePluginOptions = {},\n): CachePlugin {\n\tconst store = options.store ?? new MemoryStore();\n\tconst ttlMs = options.ttlMs;\n\tconst methods: string[] = options.methods ?? [...DEFAULT_CACHEABLE_METHODS];\n\tconst generateKey = options.generateKey ?? defaultCacheKey;\n\n\t// tag → Set<cacheKey>: populated during afterResponse, used by invalidateByTag.\n\tconst tagIndex = new Map<string, Set<string>>();\n\n\treturn {\n\t\tname: \"cache\",\n\t\tpriority: 20,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tif (!methods.includes(ctx.method)) return ctx;\n\n\t\t\tconst key = ctx.cacheKey ?? generateKey(ctx);\n\t\t\tconst cached = await store.get(key);\n\n\t\t\tif (cached !== undefined) {\n\t\t\t\t// Build a synthetic Response so the rest of the pipeline\n\t\t\t\t// (afterResponse, status checks) sees a uniform shape.\n\t\t\t\tconst syntheticResponse = new Response(JSON.stringify(cached), {\n\t\t\t\t\tstatus: 200,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\n\t\t\t\t// Setting syntheticResponse tells BaseHttpClient to skip the\n\t\t\t\t// transport entirely and use this response directly.\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\t...ctx.meta,\n\t\t\t\t\t\t[CACHE_HIT_META_KEY]: { key, data: cached },\n\t\t\t\t\t},\n\t\t\t\t\tsyntheticResponse,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, \"cache.key\": key },\n\t\t\t};\n\t\t},\n\n\t\tasync afterResponse(ctx) {\n\t\t\tconst hit = ctx.request.meta[CACHE_HIT_META_KEY] as\n\t\t\t\t| { key: string; data: unknown }\n\t\t\t\t| undefined;\n\n\t\t\tif (hit) {\n\t\t\t\treturn {\n\t\t\t\t\t...ctx,\n\t\t\t\t\tparsedBody: hit.data,\n\t\t\t\t\tmeta: { ...ctx.meta, \"cache.served\": true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst key = ctx.request.meta[\"cache.key\"] as string | undefined;\n\t\t\tif (key && methods.includes(ctx.request.method) && ctx.response.ok) {\n\t\t\t\tawait store.set(key, ctx.parsedBody, ttlMs);\n\t\t\t\tctx.meta[\"cache.stored\"] = true;\n\n\t\t\t\t// Record tag → key associations for invalidateByTag.\n\t\t\t\tfor (const tag of ctx.request.tags ?? []) {\n\t\t\t\t\tif (!tagIndex.has(tag)) tagIndex.set(tag, new Set());\n\t\t\t\t\ttagIndex.get(tag)?.add(key);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ctx;\n\t\t},\n\n\t\tasync invalidate(key: string): Promise<void> {\n\t\t\tawait store.delete(key);\n\t\t\t// Clean up any tag index entries pointing to this key.\n\t\t\tfor (const keys of tagIndex.values()) {\n\t\t\t\tkeys.delete(key);\n\t\t\t}\n\t\t},\n\n\t\tasync invalidateByTag(tag: string): Promise<void> {\n\t\t\tconst keys = tagIndex.get(tag);\n\t\t\tif (!keys || keys.size === 0) return;\n\t\t\tfor (const key of keys) {\n\t\t\t\tawait store.delete(key);\n\t\t\t\t// Remove this key from all other tag index entries too.\n\t\t\t\tfor (const otherKeys of tagIndex.values()) {\n\t\t\t\t\totherKeys.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttagIndex.delete(tag);\n\t\t},\n\t};\n}\n\nfunction defaultCacheKey(ctx: RequestContext): string {\n\tconst queryStr = ctx.query\n\t\t? new URLSearchParams(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tObject.entries(ctx.query)\n\t\t\t\t\t\t.filter(([, v]) => v !== undefined)\n\t\t\t\t\t\t.map(([k, v]) => [k, String(v)]),\n\t\t\t\t),\n\t\t\t).toString()\n\t\t: \"\";\n\treturn `${ctx.method}:${ctx.url}${queryStr ? `?${queryStr}` : \"\"}`;\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { LoggerPluginOptions } from \"./types\";\n\n/**\n * Creates a plugin that logs request start, response status, and errors.\n *\n * Log lines are prefixed with `[api-core]` and include the HTTP method, URL,\n * attempt number (on `beforeRequest`), and status code (on `afterResponse`).\n *\n * Priority `10` means it runs _after_ auth or header-mutation plugins\n * (priority < 10) so the logged URL and headers reflect the final request,\n * but _before_ the cache plugin (priority `20`) so cache hits are still\n * visible in the log.\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createLoggerPlugin({ logRequest: true, logResponse: true })],\n * });\n * ```\n */\nexport function createLoggerPlugin(\n\toptions: LoggerPluginOptions = {},\n): ApiPlugin {\n\tconst {\n\t\tlogRequest = true,\n\t\tlogResponse = true,\n\t\tlogError = true,\n\t\tlogger = console,\n\t} = options;\n\n\treturn {\n\t\tname: \"logger\",\n\t\tpriority: 10,\n\n\t\tbeforeRequest(ctx) {\n\t\t\tif (logRequest) {\n\t\t\t\tlogger.info(`[api-core] --> ${ctx.method} ${ctx.url}`, {\n\t\t\t\t\tattempt: ctx.attempt,\n\t\t\t\t\tbody: ctx.body,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\tif (logResponse) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`[api-core] <-- ${ctx.response.status} ${ctx.request.method} ${ctx.request.url}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(error, ctx) {\n\t\t\tif (logError) {\n\t\t\t\tlogger.error(`[api-core] ERR ${ctx.method} ${ctx.url}`, error);\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { RequestContext } from \"../../context/RequestContext\";\nimport type { ApiPlugin } from \"../../plugin/types\";\nimport type { RateLimitPluginOptions } from \"./types\";\n\nconst RELEASE_META_KEY = \"rateLimit.release\";\n\ninterface QueueItem {\n\tresolve: (release: () => void) => void;\n}\n\n/**\n * Throttles request starts before they reach the transport. Supports\n * concurrency, minimum spacing, and fixed-window request budgets.\n */\nexport function createRateLimitPlugin(\n\toptions: RateLimitPluginOptions = {},\n): ApiPlugin {\n\tconst maxConcurrent = options.maxConcurrent ?? Number.POSITIVE_INFINITY;\n\tconst minTimeMs = options.minTimeMs ?? 0;\n\tconst maxRequestsPerInterval = options.maxRequestsPerInterval;\n\tconst intervalMs = options.intervalMs;\n\n\tif (maxConcurrent <= 0) {\n\t\tthrow new Error(\"maxConcurrent must be greater than 0\");\n\t}\n\tif (minTimeMs < 0) {\n\t\tthrow new Error(\"minTimeMs must be greater than or equal to 0\");\n\t}\n\tif (\n\t\t(maxRequestsPerInterval !== undefined || intervalMs !== undefined) &&\n\t\t(!maxRequestsPerInterval ||\n\t\t\tmaxRequestsPerInterval <= 0 ||\n\t\t\t!intervalMs ||\n\t\t\tintervalMs <= 0)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"maxRequestsPerInterval and intervalMs must both be greater than 0\",\n\t\t);\n\t}\n\n\tconst queue: QueueItem[] = [];\n\tconst starts: number[] = [];\n\tlet active = 0;\n\tlet lastStartAt = 0;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\n\tconst processQueue = () => {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\n\t\twhile (queue.length > 0) {\n\t\t\tconst now = Date.now();\n\t\t\tpruneStarts(now);\n\n\t\t\tif (active >= maxConcurrent) return;\n\n\t\t\tconst waitMs = getWaitMs(now);\n\t\t\tif (waitMs > 0) {\n\t\t\t\ttimer = setTimeout(processQueue, waitMs);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst item = queue.shift();\n\t\t\tif (!item) return;\n\n\t\t\tactive++;\n\t\t\tlastStartAt = now;\n\t\t\tstarts.push(now);\n\n\t\t\tlet released = false;\n\t\t\titem.resolve(() => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tactive--;\n\t\t\t\tprocessQueue();\n\t\t\t});\n\t\t}\n\t};\n\n\tconst acquire = () =>\n\t\tnew Promise<() => void>((resolve) => {\n\t\t\tqueue.push({ resolve });\n\t\t\tprocessQueue();\n\t\t});\n\n\tconst release = (ctx: RequestContext) => {\n\t\tconst releaseFn = ctx.meta[RELEASE_META_KEY] as (() => void) | undefined;\n\t\tif (!releaseFn) return;\n\t\tdelete ctx.meta[RELEASE_META_KEY];\n\t\treleaseFn();\n\t};\n\n\tconst pruneStarts = (now: number) => {\n\t\tif (!intervalMs) return;\n\t\twhile (starts.length > 0 && now - (starts[0] ?? 0) >= intervalMs) {\n\t\t\tstarts.shift();\n\t\t}\n\t};\n\n\tconst getWaitMs = (now: number): number => {\n\t\tconst spacingWait = Math.max(0, lastStartAt + minTimeMs - now);\n\t\tif (!maxRequestsPerInterval || !intervalMs) return spacingWait;\n\n\t\tif (starts.length < maxRequestsPerInterval) return spacingWait;\n\n\t\tconst oldest = starts[0] ?? now;\n\t\tconst intervalWait = Math.max(0, oldest + intervalMs - now);\n\t\treturn Math.max(spacingWait, intervalWait);\n\t};\n\n\treturn {\n\t\tname: \"rate-limit\",\n\t\tpriority: 1,\n\n\t\tasync beforeRequest(ctx) {\n\t\t\tconst releaseFn = await acquire();\n\t\t\treturn {\n\t\t\t\t...ctx,\n\t\t\t\tmeta: { ...ctx.meta, [RELEASE_META_KEY]: releaseFn },\n\t\t\t};\n\t\t},\n\n\t\tafterResponse(ctx) {\n\t\t\trelease(ctx.request);\n\t\t\treturn ctx;\n\t\t},\n\n\t\tonError(_error, ctx) {\n\t\t\trelease(ctx);\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { RetryPluginOptions } from \"./types\";\n\n/**\n * Writes retry configuration into request context meta so the\n * BaseHttpClient retry loop can read it. Use this when you need\n * per-request retry overrides rather than global ClientConfig.retry.\n */\nexport function createRetryPlugin(options: RetryPluginOptions = {}): ApiPlugin {\n\treturn {\n\t\tname: \"retry\",\n\t\tpriority: 5,\n\n\t\tbeforeRequest(ctx) {\n\t\t\t// Only write keys for values the caller explicitly provided.\n\t\t\t// Unset options fall through to ClientConfig.retry defaults inside\n\t\t\t// BaseHttpClient, so the plugin does not need to supply fallbacks.\n\t\t\tif (options.maxAttempts !== undefined)\n\t\t\t\tctx.meta[\"retry.maxAttempts\"] = options.maxAttempts;\n\t\t\tif (options.delayMs !== undefined)\n\t\t\t\tctx.meta[\"retry.delayMs\"] = options.delayMs;\n\t\t\tif (options.jitter !== undefined)\n\t\t\t\tctx.meta[\"retry.jitter\"] = options.jitter;\n\t\t\tif (options.retriableStatusCodes !== undefined)\n\t\t\t\tctx.meta[\"retry.retriableStatusCodes\"] = options.retriableStatusCodes;\n\t\t\treturn ctx;\n\t\t},\n\t};\n}\n","import type { ApiPlugin } from \"../../plugin/types\";\nimport type { TimeoutPluginOptions } from \"./types\";\n\n/**\n * Sets `ctx.timeoutMs` on every request so all requests made by this client\n * abort after the configured duration. The actual abort and\n * {@link TimeoutError} are handled by {@link fetchTransport}.\n *\n * Priority `1` ensures the timeout is stamped before any other plugin (e.g.\n * logger, cache) runs — plugins that read `ctx.timeoutMs` will always see it.\n * Use a `beforeRequest` hook with a lower priority to override per-request.\n *\n * Prefer `ClientConfig.timeoutMs` for a static global timeout. Use this\n * plugin when you need to set or change the timeout through the plugin\n * pipeline (e.g. from environment config loaded asynchronously in `setup`).\n *\n * @example\n * ```ts\n * createClient({\n * baseUrl: \"https://api.example.com\",\n * plugins: [createTimeoutPlugin({ timeoutMs: 5_000 })],\n * });\n * ```\n */\nexport function createTimeoutPlugin(options: TimeoutPluginOptions): ApiPlugin {\n\treturn {\n\t\tname: \"timeout\",\n\t\tpriority: 1,\n\n\t\tbeforeRequest(ctx) {\n\t\t\treturn { ...ctx, timeoutMs: options.timeoutMs };\n\t\t},\n\t};\n}\n","/**\n * Lightweight GraphQL template tag.\n *\n * This intentionally does not parse into a DocumentNode. It preserves the\n * query string while still giving GraphQL-aware tooling a familiar `gql` tag.\n */\nexport function gql(\n\tchunks: TemplateStringsArray,\n\t...values: unknown[]\n): string {\n\tconst source = chunks.reduce(\n\t\t(source, chunk, index) =>\n\t\t\t`${source}${chunk}${index in values ? String(values[index]) : \"\"}`,\n\t\t\"\",\n\t);\n\n\treturn dedupeFragmentDefinitions(source);\n}\n\nfunction dedupeFragmentDefinitions(source: string): string {\n\tconst seen = new Set<string>();\n\tconst fragmentPattern =\n\t\t/\\bfragment\\s+([_A-Za-z][_0-9A-Za-z]*)\\s+on\\s+[_A-Za-z][_0-9A-Za-z]*/g;\n\n\tlet result = \"\";\n\tlet cursor = 0;\n\tlet match = fragmentPattern.exec(source);\n\n\twhile (match) {\n\t\tconst name = match[1];\n\t\tif (!name) {\n\t\t\tmatch = fragmentPattern.exec(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst bodyStart = source.indexOf(\"{\", fragmentPattern.lastIndex);\n\t\tif (bodyStart === -1) {\n\t\t\tmatch = fragmentPattern.exec(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst bodyEnd = findMatchingBrace(source, bodyStart);\n\t\tif (bodyEnd === -1) {\n\t\t\tmatch = fragmentPattern.exec(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fragmentStart = match.index;\n\t\tconst fragmentEnd = consumeTrailingWhitespace(source, bodyEnd + 1);\n\n\t\tif (!seen.has(name)) {\n\t\t\tseen.add(name);\n\t\t\tresult += source.slice(cursor, fragmentEnd);\n\t\t} else {\n\t\t\tresult += source.slice(cursor, fragmentStart);\n\t\t}\n\n\t\tcursor = fragmentEnd;\n\t\tfragmentPattern.lastIndex = fragmentEnd;\n\t\tmatch = fragmentPattern.exec(source);\n\t}\n\n\treturn result + source.slice(cursor);\n}\n\nfunction findMatchingBrace(source: string, openBraceIndex: number): number {\n\tlet depth = 0;\n\n\tfor (let index = openBraceIndex; index < source.length; index++) {\n\t\tconst char = source[index];\n\t\tif (char === \"{\") depth++;\n\t\tif (char === \"}\") depth--;\n\t\tif (depth === 0) return index;\n\t}\n\n\treturn -1;\n}\n\nfunction consumeTrailingWhitespace(source: string, index: number): number {\n\tlet next = index;\n\twhile (next < source.length && /\\s/.test(source[next] ?? \"\")) {\n\t\tnext++;\n\t}\n\treturn next;\n}\n"],"mappings":"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"}
@@ -49,6 +49,8 @@ await client.get("/movies", {
49
49
  | `signal` | Caller-provided abort signal. Composes with timeout handling. |
50
50
  | `cacheKey` | Explicit key for cache plugins. |
51
51
  | `tags` | Labels plugins can use for cache invalidation or metrics. |
52
+ | `responseType` | Override response parsing with `json`, `text`, `arrayBuffer`, or `blob`. Defaults to `auto`. |
53
+ | `errorResponseType` | Override non-2xx body parsing. Defaults to `auto`. |
52
54
 
53
55
  ## Response Metadata
54
56
 
@@ -86,6 +88,19 @@ const games = await client.post<Game[]>(
86
88
  );
87
89
  ```
88
90
 
91
+ ## Binary Responses
92
+
93
+ Use `responseType: "arrayBuffer"` for endpoints that return binary payloads
94
+ while still going through retries, plugins, timeouts, and the configured
95
+ transport.
96
+
97
+ ```ts
98
+ const bytes = await client.post<ArrayBuffer>("/games.pb", "fields id;", {
99
+ headers: { accept: "application/octet-stream" },
100
+ responseType: "arrayBuffer",
101
+ });
102
+ ```
103
+
89
104
  ## Custom Fetch Or Transport
90
105
 
91
106
  Use `fetch` when you only need to swap the fetch implementation:
@@ -39,6 +39,18 @@ class MyApiClient extends BaseHttpClient {
39
39
  | `init()` | Initialize plugins. Called lazily before the first request. |
40
40
  | `dispose()` | Run plugin cleanup hooks. |
41
41
 
42
+ ## Response Parsing
43
+
44
+ Requests parse JSON automatically when the response content type is JSON and
45
+ fall back to text for other bodies. Pass `responseType` to override that:
46
+
47
+ ```ts
48
+ await client.get<string>("/robots.txt", { responseType: "text" });
49
+ await client.post<ArrayBuffer>("/games.pb", query, {
50
+ responseType: "arrayBuffer",
51
+ });
52
+ ```
53
+
42
54
  ## `ApiResponse<T>`
43
55
 
44
56
  Returned by `requestWithResponse`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-wrappers/api-core",
3
- "version": "0.0.2",
3
+ "version": "1.0.0",
4
4
  "description": "Shared HTTP client runtime for the api-wrappers organisation. Provides request orchestration, a plugin lifecycle, transport abstraction, and built-in cache/retry/logger plugins.",
5
5
  "repository": {
6
6
  "type": "git",