arthron 1.1.0 → 1.2.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins-Cigqz2SS.cjs","names":["originOf","MAX_TIMER_MS","settle"],"sources":["../src/request/url.ts","../src/error/arthron-error.ts","../src/request/body.ts","../src/plugins/bearer-auth/bearer-auth.ts","../src/response/rebuild-response.ts","../src/error/create-error.ts","../src/plugins/logging/logging.ts","../src/plugins/retry/retry.ts","../src/plugins/timeout/timeout.ts","../src/plugins/upload-progress/form-data.ts","../src/plugins/upload-progress/progress-event.ts","../src/plugins/upload-progress/request-parts.ts","../src/plugins/upload-progress/counting-fetch.ts","../src/plugins/upload-progress/xhr-fetch.ts","../src/plugins/upload-progress/upload-progress.ts"],"sourcesContent":["import type { QueryParams } from \"../types/request.types\";\n\nconst ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\\/\\//i;\nconst HTTP_URL = /^https?:\\/\\//i;\n\nexport function isAbsoluteUrl(path: string): boolean {\n return ABSOLUTE_URL.test(path);\n}\n\n/**\n * Origin + pathname only — the query and userinfo tend to carry secrets.\n * For anything printed: log lines, error messages, `ArthronError.toJSON`.\n */\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}`;\n } catch {\n return url.split(/[?#]/)[0] ?? url;\n }\n}\n\n/**\n * `text` with `url` redacted wherever it appears, as given or as the URL\n * parser normalizes it — for a runtime's own message that repeats the URL.\n */\nexport function redactUrlIn(text: string, url: string): string {\n const shown = redactUrl(url);\n let href: string | undefined;\n try {\n href = new URL(url).href;\n } catch {}\n let out = text.split(url).join(shown);\n if (href && href !== url) out = out.split(href).join(shown);\n return out;\n}\n\n/**\n * `undefined` when `url` has no origin of its own. A relative baseURL takes\n * the page's origin in a browser and has none elsewhere.\n */\nfunction originOf(url: string): string | undefined {\n const page = (globalThis as { location?: { href?: string } }).location?.href;\n try {\n return new URL(url, page).origin;\n } catch {\n return undefined;\n }\n}\n\n/** `path` is absolute and points away from `baseURL`'s origin. */\nexport function leavesBase(baseURL: string | undefined, path: string): boolean {\n if (baseURL === undefined || !isAbsoluteUrl(path)) return false;\n const base = originOf(baseURL);\n return base === undefined || originOf(path) !== base;\n}\n\nfunction toQueryString(params: QueryParams | undefined): string {\n if (!params) return \"\";\n if (Object.prototype.toString.call(params) === \"[object URLSearchParams]\") {\n return params.toString();\n }\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (item === null || item === undefined) continue;\n // `String({})` is \"[object Object]\" and a Date prints in local time —\n // neither is what a server expects.\n if (typeof item === \"object\" || typeof item === \"function\") {\n throw new TypeError(\n `arthron: params.${key} is not a string, number or boolean — pass one of those or an array of them (dates: date.toISOString())`,\n );\n }\n search.append(key, String(item));\n }\n }\n return search.toString();\n}\n\n/**\n * Fill `{name}` placeholders with encoded values, so each one stays inside\n * its segment: \"/\", \"?\" and \"#\" are encoded, and the values WHATWG URL would\n * turn into a different path — \"\", \".\" and \"..\" — are refused. A placeholder\n * without a value, or a value without a placeholder, is a mistake too.\n */\nexport function fillPath(\n template: string,\n values: Record<string, string | number>,\n): string {\n const used = new Set<string>();\n const filled = template.replace(/\\{([^{}/]+)\\}/g, (_, name: string) => {\n if (!Object.hasOwn(values, name)) {\n throw new TypeError(\n `arthron: path \"${template}\" has {${name}} but no value for it — pass path: { ${name}: … }`,\n );\n }\n used.add(name);\n const value = String(values[name]);\n if (value === \"\" || value === \".\" || value === \"..\") {\n throw new TypeError(\n `arthron: path value ${name}=${JSON.stringify(value)} would change the path, not fill a segment — pass a real id`,\n );\n }\n return encodeURIComponent(value);\n });\n const unused = Object.keys(values).find((name) => !used.has(name));\n if (unused !== undefined) {\n throw new TypeError(\n `arthron: path \"${template}\" has no {${unused}} — add the placeholder or drop the value`,\n );\n }\n return filled;\n}\n\n/**\n * Join `baseURL` and `path` by plain string concatenation so a path prefix in\n * the base (`/v1`) survives a leading slash in the path. WHATWG `new URL()`\n * would drop it. An absolute path replaces the base only when it stays on the\n * base's origin, or when `allowAbsoluteUrls` says other origins are fine — a\n * path built from user input or an API response must not carry the client's\n * credentials somewhere else.\n */\nexport function resolveUrl(\n baseURL: string | undefined,\n path: string,\n params?: QueryParams,\n allowAbsoluteUrls = false,\n): string {\n let url: string;\n if (isAbsoluteUrl(path)) {\n if (!HTTP_URL.test(path)) {\n const scheme = path.slice(0, path.indexOf(\":\") + 1);\n throw new TypeError(\n `arthron: only http(s) URLs can be requested, got a \"${scheme}\" URL — use an http:// or https:// URL`,\n );\n }\n if (!allowAbsoluteUrls && leavesBase(baseURL, path)) {\n throw new TypeError(\n `arthron: ${originOf(path)} is outside baseURL ${(baseURL && originOf(baseURL)) ?? baseURL} — pass a path relative to baseURL, or set arthron({ allowAbsoluteUrls: true }) to allow other origins (config headers are not sent to them)`,\n );\n }\n url = path;\n } else {\n if (baseURL === undefined) {\n throw new TypeError(\n `arthron: relative path \"${redactUrl(path)}\" needs a baseURL — pass baseURL in arthron({ baseURL }) or use an absolute URL`,\n );\n }\n url = `${baseURL.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n }\n const query = toQueryString(params);\n if (!query) return url;\n // The query goes before any #fragment, or the server never sees it.\n const hash = url.indexOf(\"#\");\n const [base, fragment] =\n hash === -1 ? [url, \"\"] : [url.slice(0, hash), url.slice(hash)];\n return `${base}${base.includes(\"?\") ? \"&\" : \"?\"}${query}${fragment}`;\n}\n","import { redactUrl } from \"../request/url\";\nimport type { RequestContext } from \"../types/request.types\";\n\ntype ArthronErrorInit = {\n message: string;\n status: number;\n statusText: string;\n headers: Headers;\n data: unknown;\n detail?: string | undefined;\n request: RequestContext;\n response?: Response;\n cause?: unknown;\n};\n\n/**\n * Marks an ArthronError across copies of this package. The ESM and CJS\n * builds can both end up loaded (a CJS dependency creates the client, ESM\n * app code catches), each with its own class — `Symbol.for` is shared.\n */\nconst BRAND = Symbol.for(\"arthron.ArthronError\");\n\n/**\n * The only error arthron itself produces. Two sources: a response with\n * `ok === false`, or fetch rejecting outright (network failure, `status` 0).\n * Everything else — cancellation, interceptor bugs — is rethrown untouched.\n */\nexport class ArthronError extends Error {\n override readonly name = \"ArthronError\";\n /** HTTP status of the failed response; `0` when fetch itself failed (network). */\n readonly status: number;\n readonly statusText: string;\n /** Response headers; empty for network failures. Not enumerable — kept out of logs. */\n declare readonly headers: Headers;\n /** Parsed body: JSON if it parses, else the text, else `undefined`. */\n readonly data: unknown;\n /**\n * The server's own explanation, picked out of `data` (by default\n * `message` → `error` → `error.message` → `detail`; see the `errorDetail`\n * config). `undefined` when the body had none. Untrusted server text: show\n * it to users as text, never as HTML. `message`, by contrast, is written by\n * arthron — method, URL without query, status — and is meant for logs.\n */\n readonly detail: string | undefined;\n /**\n * The context that was sent — url and init after request interceptors.\n * Not enumerable: it holds credentials, so `JSON.stringify`, `console.error`\n * and error trackers leave it out. Read it directly when you need it.\n */\n declare readonly request: RequestContext;\n /** The failed Response, still readable — its body holds what arthron read (at most 64 KiB). Absent for network failures. Not enumerable. */\n declare readonly response: Response | undefined;\n /** The underlying error for network failures. */\n override readonly cause: unknown;\n\n constructor(init: ArthronErrorInit) {\n super(init.message);\n this.status = init.status;\n this.statusText = init.statusText;\n this.data = init.data;\n this.detail = init.detail;\n this.cause = init.cause;\n Object.defineProperties(this, {\n headers: { value: init.headers },\n request: { value: init.request },\n response: { value: init.response },\n [BRAND]: { value: true },\n });\n }\n\n /** `instanceof ArthronError` holds for an error from any copy of the package. */\n static override [Symbol.hasInstance](value: unknown): boolean {\n return this === ArthronError\n ? isArthronError(value)\n : Function.prototype[Symbol.hasInstance].call(this, value);\n }\n\n /** What serializers see: no headers, no body, no query or userinfo. */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n detail: this.detail,\n status: this.status,\n statusText: this.statusText,\n method: this.request.init.method ?? \"GET\",\n url: redactUrl(this.request.url),\n data: this.data,\n };\n }\n}\n\n/** `true` for errors arthron produced: a `!ok` response or a network failure. Cancellations and plugin bugs are not ArthronErrors. */\nexport function isArthronError(error: unknown): error is ArthronError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { [BRAND]?: unknown })[BRAND] === true\n );\n}\n","type Serialized = {\n body: BodyInit | undefined;\n headers: Record<string, string>;\n};\n\nconst RAW_TAGS = new Set([\n \"FormData\",\n \"Blob\",\n \"File\",\n \"ArrayBuffer\",\n \"URLSearchParams\",\n \"ReadableStream\",\n]);\n\n/**\n * `Symbol.toStringTag`, not `instanceof`: a body from another realm or a\n * polyfill (formdata-node, fetch-blob) must not be JSON-encoded into `{}`.\n */\nexport const tagOf = (value: unknown): string =>\n Object.prototype.toString.call(value).slice(8, -1);\n\nexport function isReadableStream(value: unknown): value is ReadableStream {\n return tagOf(value) === \"ReadableStream\";\n}\n\n/**\n * Whether `RequestCtor` can send a ReadableStream body. Node and Chromium\n * read `duplex` and leave the stream alone; Firefox ignores `duplex` and\n * would send the text \"[object ReadableStream]\"; older Safari refuses it.\n * The check from\n * https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests\n */\nexport function canStreamRequestBody(RequestCtor: typeof Request): boolean {\n let duplexRead = false;\n try {\n const request = new RequestCtor(\"https://stream.test\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexRead = true;\n return \"half\";\n },\n } as RequestInit);\n return duplexRead && !request.headers.has(\"content-type\");\n } catch {\n return false;\n }\n}\n\nlet runtimeStreams: boolean | undefined;\n\n/** `canStreamRequestBody` for the global `Request`, checked once. */\nexport function runtimeStreamsRequestBody(): boolean {\n runtimeStreams ??=\n typeof Request !== \"function\" || canStreamRequestBody(Request);\n return runtimeStreams;\n}\n\n/**\n * What JSON.stringify would turn into `{}`, `undefined` or internal state\n * instead of the data — with the fix, so it reads as an error message.\n */\nfunction mangledByJson(body: unknown): string | undefined {\n if (typeof body === \"function\" || typeof body === \"symbol\") {\n return `a ${typeof body}, which JSON cannot carry — send its data instead`;\n }\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) {\n return undefined;\n }\n if (Symbol.asyncIterator in body) {\n return \"an async iterable (a Node stream?) — pass a ReadableStream (Readable.toWeb(stream)) or collect it first\";\n }\n if (Symbol.iterator in body) {\n return \"an iterable such as a Map, Set or generator — convert it to an array or plain object first\";\n }\n return undefined;\n}\n\n/** Bodies fetch already knows how to send. Content-Type is left to the caller / runtime. */\nfunction isRawBody(body: unknown): body is BodyInit {\n if (typeof body === \"string\") return true;\n if (ArrayBuffer.isView(body)) return true;\n return RAW_TAGS.has(tagOf(body));\n}\n\n/**\n * Turn a caller-supplied body into something fetch accepts. Raw bodies pass\n * through untouched; anything else is JSON with `content-type` set — unless\n * the caller already set one (headers arrive lowercased, so a plain lookup is\n * enough).\n */\nexport function serializeBody(\n body: unknown,\n headers: Record<string, string>,\n): Serialized {\n if (body === undefined) return { body: undefined, headers };\n if (tagOf(body) === \"FormData\") {\n // The runtime must write `multipart/form-data; boundary=…` itself; any\n // content-type set here (a JSON default, typically) breaks the upload.\n const { \"content-type\": _dropped, ...rest } = headers;\n return { body: body as FormData, headers: rest };\n }\n if (isRawBody(body)) return { body, headers };\n const mangled = mangledByJson(body);\n if (mangled) {\n throw new TypeError(`arthron: the body is ${mangled}`);\n }\n const type = headers[\"content-type\"];\n if (type !== undefined && !/\\bjson\\b/i.test(type)) {\n throw new TypeError(\n `arthron: a ${typeof body} body is sent as JSON, but content-type is \"${type}\" — pass a string, URLSearchParams or FormData for that content type, or drop the header`,\n );\n }\n const withType =\n \"content-type\" in headers\n ? headers\n : { ...headers, \"content-type\": \"application/json\" };\n return { body: JSON.stringify(body), headers: withType };\n}\n","import { isArthronError } from \"../../error/arthron-error\";\nimport { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { BearerAuthOptions } from \"./bearer-auth.types\";\n\n/** The origin of an absolute http(s) URL; `undefined` for anything else. */\nfunction originOf(url: string): string | undefined {\n try {\n const { protocol, origin } = new URL(url);\n return protocol === \"http:\" || protocol === \"https:\" ? origin : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction allowedOrigins(origin: BearerAuthOptions[\"origin\"]): Set<string> {\n const urls = typeof origin === \"string\" ? [origin] : origin;\n if (urls.length === 0) {\n throw new TypeError(\n 'arthron: bearerAuth({ origin }) needs at least one URL — e.g. origin: \"https://api.example.com\"',\n );\n }\n return new Set(\n urls.map((url) => {\n const found = originOf(url);\n if (found) return found;\n throw new TypeError(\n `arthron: bearerAuth({ origin }) needs an http(s) URL, got ${JSON.stringify(url)} — e.g. origin: \"https://api.example.com\"`,\n );\n }),\n );\n}\n\n/**\n * Bearer-token auth. A request interceptor sets `authorization: Bearer\n * <token>` on every attempt to `origin`; with `refresh`, a middleware turns a\n * 401 into one refresh and one more try. Concurrent 401s share the refresh\n * (with refresh-token rotation, parallel refreshes revoke each other), and a\n * call whose token was replaced meanwhile just retries. A call opts out with\n * `meta: { bearerAuth: false }`.\n */\nexport function bearerAuth(options: BearerAuthOptions): Plugin {\n const { token, refresh } = options;\n const allowed = allowedOrigins(options.origin);\n const ours = (url: string) => allowed.has(originOf(url) ?? \"\");\n const bearer = async () => {\n const value = await token();\n return value ? `Bearer ${value}` : undefined;\n };\n let refreshing: Promise<void> | undefined;\n\n return {\n name: \"bearerAuth\",\n request: async (ctx) => {\n if (ctx.meta?.bearerAuth === false || !ours(ctx.url)) return ctx;\n const value = await bearer();\n if (!value) return ctx;\n return {\n ...ctx,\n init: {\n ...ctx.init,\n headers: { ...ctx.init.headers, authorization: value },\n },\n };\n },\n middleware: async (ctx, next) => {\n if (!refresh || ctx.meta?.bearerAuth === false) return next(ctx);\n // A stream is read by the first try; there is nothing left to resend.\n if (isReadableStream(ctx.init.body)) return next(ctx);\n try {\n return await next(ctx);\n } catch (error) {\n if (!isArthronError(error) || error.status !== 401) throw error;\n if (!ours(error.request.url)) throw error;\n // Refresh only if the token that failed is still the current one —\n // otherwise another call has refreshed it already.\n if (error.request.init.headers.authorization === (await bearer())) {\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n await refreshing;\n }\n return next(ctx);\n }\n },\n };\n}\n","/**\n * A new Response with `body` but everything else taken from `original`. The\n * constructor resets `url`, `redirected` and `type`, so those are copied over\n * as own properties — callers that resolve relative links or detect\n * redirects keep working.\n */\nexport function rebuildResponse(\n original: Response,\n body: BodyInit | null,\n): Response {\n const rebuilt = new Response(body, {\n status: original.status,\n statusText: original.statusText,\n headers: original.headers,\n });\n const { url, redirected, type } = original;\n return withFields(rebuilt, { url, redirected, type });\n}\n\n/** The fields a fetched Response has and the constructor resets. */\nexport type FetchedFields = Pick<Response, \"url\" | \"redirected\" | \"type\">;\n\n/** Set those fields on `target` — and on its `clone()`s as well. */\nexport function withFields(target: Response, fields: FetchedFields): Response {\n const clone = target.clone.bind(target);\n return Object.defineProperties(target, {\n url: { value: fields.url },\n redirected: { value: fields.redirected },\n type: { value: fields.type },\n clone: { value: () => withFields(clone(), fields) },\n });\n}\n","import { isReadableStream } from \"../request/body\";\nimport { redactUrl, redactUrlIn } from \"../request/url\";\nimport { rebuildResponse } from \"../response/rebuild-response\";\nimport type { RequestContext } from \"../types/request.types\";\nimport type { ErrorDetailResolver } from \"../types/response.types\";\nimport { ArthronError } from \"./arthron-error\";\n\n/**\n * Cancellation is not an HTTP failure. Matched by name so it works across\n * realms — or by the request's own `signal`, since `abort(reason)` rejects\n * with whatever reason the caller chose.\n */\nexport function isCancellation(\n error: unknown,\n signal?: AbortSignal | null,\n): boolean {\n if (signal?.aborted) return true;\n const name = (error as { name?: unknown } | null | undefined)?.name;\n return name === \"AbortError\" || name === \"TimeoutError\";\n}\n\n/**\n * What a cancelled call rejects with: its signal's reason. WebKit's fetch\n * rejects, and errors a half-read body, with a generic AbortError instead,\n * dropping a TimeoutError or the caller's own reason.\n */\nexport function cancellationError(\n error: unknown,\n signal?: AbortSignal | null,\n): unknown {\n return signal?.aborted ? signal.reason : error;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/** `message` → `error` (string) → `error.message` → `detail`. */\nexport function defaultErrorDetail(data: unknown): string | undefined {\n if (!isRecord(data)) return undefined;\n if (typeof data.message === \"string\") return data.message;\n if (typeof data.error === \"string\") return data.error;\n if (isRecord(data.error) && typeof data.error.message === \"string\") {\n return data.error.message;\n }\n if (typeof data.detail === \"string\") return data.detail;\n return undefined;\n}\n\n/** How much of an error body arthron reads. Error bodies are for diagnosis; a hostile or broken server must not be able to fill memory with one. */\nconst MAX_ERROR_BODY_BYTES = 64 * 1024;\n\n/** Up to `limit` bytes of the body; the rest is cancelled unread. `undefined` when there is no body or reading it failed. */\nasync function readCapped(\n response: Response,\n limit: number,\n): Promise<{ bytes: Uint8Array<ArrayBuffer>; truncated: boolean } | undefined> {\n if (!response.body) return undefined;\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n const chunks: Uint8Array[] = [];\n let size = 0;\n try {\n // Inside the try: a body that is already locked or used counts as unreadable.\n reader = response.body.getReader();\n while (size <= limit) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n size += value.byteLength;\n }\n } catch {\n return undefined;\n } finally {\n await reader?.cancel().catch(() => {});\n reader?.releaseLock();\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return size > limit\n ? { bytes: bytes.subarray(0, limit), truncated: true }\n : { bytes, truncated: false };\n}\n\n/** JSON if the whole body parses, else the text (cut at the limit), else `undefined` when empty. */\nfunction parseBody(read: {\n bytes: Uint8Array<ArrayBuffer>;\n truncated: boolean;\n}): unknown {\n const text = new TextDecoder().decode(read.bytes);\n if (text === \"\") return undefined;\n if (read.truncated) return text;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\n/** The request as error messages name it: method and URL, no query or userinfo. */\nfunction describeRequest(request: RequestContext): string {\n return `${request.init.method ?? \"GET\"} ${redactUrl(request.url)}`;\n}\n\nexport async function responseError(\n response: Response,\n request: RequestContext,\n resolve: ErrorDetailResolver = defaultErrorDetail,\n): Promise<ArthronError> {\n const read = await readCapped(response, MAX_ERROR_BODY_BYTES);\n const data = read && parseBody(read);\n // The body was consumed above; hand the caller a Response over what was read.\n if (read) response = rebuildResponse(response, read.bytes);\n const status = response.statusText\n ? `${response.status} ${response.statusText}`\n : String(response.status);\n return new ArthronError({\n // arthron's own words for logs; the server's go in `detail`.\n message: `${describeRequest(request)} failed with ${status}`,\n detail: resolve(data, response) || undefined,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n data,\n request,\n response,\n });\n}\n\n/**\n * Browsers that stream a request body (Chromium, current WebKit) do it only\n * over HTTP/2, and fail one sent to an HTTP/1.1 server with the same bare\n * \"Failed to fetch\" / \"Load failed\" as a dead server, so name the likely cause.\n */\nconst STREAM_BODY_HINT =\n \" — in a browser, a ReadableStream body streams only over HTTP/2; send a Blob or ArrayBuffer to an HTTP/1.1 server\";\n\nexport function networkError(\n cause: unknown,\n request: RequestContext,\n): ArthronError {\n // Some runtimes' messages repeat the URL, query and userinfo included.\n const reason =\n cause instanceof Error && cause.message\n ? redactUrlIn(cause.message, request.url)\n : \"network error\";\n const hint = isReadableStream(request.init.body) ? STREAM_BODY_HINT : \"\";\n return new ArthronError({\n message: `${describeRequest(request)} failed: ${reason}${hint}`,\n status: 0,\n statusText: \"\",\n headers: new Headers(),\n data: undefined,\n request,\n cause,\n });\n}\n","import { isArthronError } from \"../../error/arthron-error\";\nimport { isCancellation } from \"../../error/create-error\";\nimport { redactUrl } from \"../../request/url\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { LogEvent, LoggingOptions } from \"./logging.types\";\n\nfunction consoleSink(logger: Pick<Console, \"log\" | \"error\">) {\n return (e: LogEvent) => {\n if (e.phase === \"start\") {\n logger.log(`[arthron] → ${e.method} ${e.path}`);\n } else if (e.phase === \"success\") {\n logger.log(`[arthron] ← ${e.method} ${e.path} ${e.status} ${e.ms}ms`);\n } else {\n logger.error(\n `[arthron] ✕ ${e.method} ${e.path} ${e.status ?? \"-\"} ${e.ms}ms`,\n );\n }\n };\n}\n\n/**\n * One event when a request starts, one when it settles, with status and\n * elapsed time. Wraps the whole call, so retries are included in the time.\n * Default output is a console line per event; pass `onEvent` to get the\n * structured event instead. Cancellations are not reported.\n */\nexport function logging(options: LoggingOptions = {}): Plugin {\n const {\n enabled = true,\n logger = console,\n onEvent,\n now = () => performance.now(),\n } = options;\n const emit = onEvent ?? consoleSink(logger);\n return {\n name: \"logging\",\n middleware: async (ctx, next) => {\n if (!enabled) return next(ctx);\n const method = ctx.init.method ?? \"GET\";\n const path = redactUrl(ctx.url);\n const started = now();\n const ms = () => Math.round(now() - started);\n\n emit({ phase: \"start\", method, path });\n let res: Response;\n try {\n res = await next(ctx);\n } catch (error) {\n if (!isCancellation(error, ctx.init.signal)) {\n const status = isArthronError(error) ? error.status : undefined;\n emit({ phase: \"error\", method, path, status, ms: ms(), error });\n }\n throw error;\n }\n // Outside the try: a sink that throws is a plugin bug, not a failed request.\n emit({ phase: \"success\", method, path, status: res.status, ms: ms() });\n return res;\n },\n };\n}\n","import { type ArthronError, isArthronError } from \"../../error/arthron-error\";\nimport { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { RetryOptions } from \"../../types/request.types\";\n\nconst DEFAULT_RETRY_ON = [408, 429, 500, 502, 503, 504] as const;\nconst DEFAULT_METHODS = [\n \"GET\",\n \"HEAD\",\n \"OPTIONS\",\n \"QUERY\",\n \"PUT\",\n \"DELETE\",\n] as const;\nconst DEFAULT_MAX_RETRY_AFTER = 60_000;\n/** setTimeout fires at once past this, so every wait is capped to it. */\nconst MAX_TIMER_MS = 2 ** 31 - 1;\n\nfunction defaultSleep(ms: number, signal?: AbortSignal | null): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/** `Retry-After` as ms — seconds or an HTTP-date. `undefined` when absent or unparsable. */\nfunction retryAfterMs(error: ArthronError): number | undefined {\n const value = error.headers.get(\"retry-after\")?.trim();\n if (!value) return undefined;\n const seconds = Number(value);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const at = Date.parse(value);\n if (Number.isNaN(at)) return undefined;\n return Math.max(0, at - Date.now());\n}\n\n/** The rule without `shouldRetry`: a network error or a `retryOn` status, on a method in `methods`. */\nfunction byDefault(\n thrown: unknown,\n method: string,\n settings: Settings,\n): boolean {\n if (!settings.allowed.has(method) || !isArthronError(thrown)) return false;\n return thrown.status === 0 || settings.retryOn.includes(thrown.status);\n}\n\n/** `RetryOptions` with defaults filled in, ready for one call. */\ntype Settings = {\n retries: number;\n retryOn: readonly number[];\n allowed: Set<string>;\n delay: (retry: number, error: unknown) => number;\n maxRetryAfter: number;\n shouldRetry: RetryOptions[\"shouldRetry\"];\n onRetry: RetryOptions[\"onRetry\"];\n};\n\n/** Defaults filled in and `retries` checked — `attempt >= NaN` is never true, so an unset env var would retry forever. */\nfunction settle(options: Omit<RetryOptions, \"sleep\">, where: string): Settings {\n const {\n retries = 2,\n retryOn = DEFAULT_RETRY_ON,\n methods = DEFAULT_METHODS,\n delay = (retry) => Math.random() * 300 * 2 ** retry,\n maxRetryAfter = DEFAULT_MAX_RETRY_AFTER,\n shouldRetry,\n onRetry,\n } = options;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new TypeError(\n `arthron: ${where} retries: ${retries} needs a whole number ≥ 0 — e.g. retries: 2`,\n );\n }\n const allowed = new Set(methods.map((m) => m.toUpperCase()));\n return {\n retries,\n retryOn,\n allowed,\n delay,\n maxRetryAfter,\n shouldRetry,\n onRetry,\n };\n}\n\n/**\n * Retry on network errors and the configured statuses by calling `next`\n * again — the whole attempt (request interceptors included) is re-run.\n * Only `ArthronError` is considered; cancellation and plugin bugs pass through.\n * A call can turn it off or override its options with `meta.retry`.\n */\nexport function retry(options: RetryOptions = {}): Plugin {\n const { sleep = defaultSleep, ...rest } = options;\n const base = settle(rest, \"retry()\");\n\n return {\n name: \"retry\",\n middleware: async (ctx, next) => {\n const override = ctx.meta?.retry;\n if (override === false) return next(ctx);\n const settings = override\n ? settle({ ...rest, ...override }, \"meta.retry\")\n : base;\n const method = (ctx.init.method ?? \"GET\").toUpperCase();\n const resendable = !isReadableStream(ctx.init.body);\n\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await next(ctx);\n } catch (thrown) {\n // Not even shouldRetry can lift these.\n if (!resendable || attempt >= settings.retries) throw thrown;\n if (ctx.init.signal?.aborted) throw thrown;\n const verdict = settings.shouldRetry?.(thrown, attempt, ctx);\n if (verdict === false) throw thrown;\n if (verdict === undefined && !byDefault(thrown, method, settings)) {\n throw thrown;\n }\n const retryAfter = isArthronError(thrown)\n ? retryAfterMs(thrown)\n : undefined;\n if (retryAfter !== undefined && retryAfter > settings.maxRetryAfter) {\n throw thrown;\n }\n const wait = Math.min(\n retryAfter ?? settings.delay(attempt, thrown),\n MAX_TIMER_MS,\n );\n settings.onRetry?.(thrown, attempt, wait);\n await sleep(wait, ctx.init.signal);\n }\n }\n },\n };\n}\n","import { rebuildResponse } from \"../../response/rebuild-response\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { TimeoutOptions } from \"./timeout.types\";\n\nconst MAX_TIMER_MS = 2 ** 31 - 1;\n\n/**\n * Wrap `body` so the deadline keeps running until it is fully read, and so an\n * abort while reading surfaces as a stream error instead of a silent stall.\n */\nfunction guardBody(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n finish: () => void,\n onChunk: () => void,\n): ReadableStream<Uint8Array> {\n const reader = body.getReader();\n return new ReadableStream<Uint8Array>({\n start(controller) {\n signal.addEventListener(\n \"abort\",\n () => {\n controller.error(signal.reason);\n reader.cancel(signal.reason).catch(() => {});\n },\n { once: true },\n );\n },\n async pull(controller) {\n const { value, done } = await reader.read();\n if (done) {\n finish();\n controller.close();\n return;\n }\n onChunk();\n controller.enqueue(value);\n },\n cancel(reason) {\n finish();\n return reader.cancel(reason);\n },\n });\n}\n\n/** setTimeout fires at once for NaN, negatives and anything past 2^31-1. */\nfunction checkDeadline(ms: number, where: string): void {\n if (ms === Number.POSITIVE_INFINITY || (ms >= 0 && ms <= MAX_TIMER_MS))\n return;\n throw new TypeError(\n `arthron: ${where} needs 0–${MAX_TIMER_MS} ms — pass Infinity for no deadline`,\n );\n}\n\n/** A timer that aborts `controller` with a TimeoutError and never keeps Node alive on its own. */\nfunction deadlineTimer(\n controller: AbortController,\n ms: number,\n message: string,\n): ReturnType<typeof setTimeout> {\n const timer = setTimeout(\n () => controller.abort(new DOMException(message, \"TimeoutError\")),\n ms,\n );\n (timer as { unref?: () => unknown }).unref?.();\n return timer;\n}\n\n/** `timeout(ms)`, `timeout({ total, idle })` or the deprecated `timeout(ms, { idle })`, checked. */\nfunction settle(\n first: number | TimeoutOptions,\n second: Pick<TimeoutOptions, \"idle\">,\n): { ms: number; idle: number | undefined } {\n if (typeof first === \"number\") {\n checkDeadline(first, `timeout(${first})`);\n const { idle } = second;\n if (idle !== undefined) {\n checkDeadline(idle, `timeout(…, { idle: ${idle} })`);\n }\n return { ms: first, idle };\n }\n const { total, idle } = first;\n if (total === undefined && idle === undefined) {\n throw new TypeError(\n \"arthron: timeout({}) needs total or idle — e.g. timeout({ total: 10_000 }) or timeout({ idle: 30_000 })\",\n );\n }\n if (total !== undefined) checkDeadline(total, `timeout({ total: ${total} })`);\n if (idle !== undefined) checkDeadline(idle, `timeout({ idle: ${idle} })`);\n return { ms: total ?? Number.POSITIVE_INFINITY, idle };\n}\n\n/**\n * Abort the request when it takes longer than `ms` — counted from the start\n * of the attempt until the body has been fully read, so a server that sends\n * headers and then stalls still trips it. `timeout({ idle })` instead aborts\n * once no data has arrived for that long, and never cuts off a stream that\n * keeps flowing; `timeout({ total, idle })` does both. Rejects with a\n * `TimeoutError` (a `DOMException`, like `AbortSignal.timeout()` would give);\n * it is not an `ArthronError`, so error interceptors and `retry` leave it\n * alone.\n *\n * A call can change the total deadline with `meta.timeout`.\n *\n * Deadlines are timers on an AbortController — not `AbortSignal.timeout`,\n * since the idle one restarts — joined to the caller's signal with\n * `AbortSignal.any`.\n */\nexport function timeout(ms: number): Plugin;\nexport function timeout(options: TimeoutOptions): Plugin;\n/** @deprecated Use `timeout({ total: ms, idle })`; this form goes in the next major. */\nexport function timeout(\n ms: number,\n options: Pick<TimeoutOptions, \"idle\">,\n): Plugin;\nexport function timeout(\n first: number | TimeoutOptions,\n second: Pick<TimeoutOptions, \"idle\"> = {},\n): Plugin {\n const { ms, idle } = settle(first, second);\n return {\n name: \"timeout\",\n middleware: async (ctx, next) => {\n const deadline = ctx.meta?.timeout ?? ms;\n if (deadline !== ms) checkDeadline(deadline, `meta.timeout: ${deadline}`);\n const idleMs = idle === Number.POSITIVE_INFINITY ? undefined : idle;\n if (deadline === Number.POSITIVE_INFINITY && idleMs === undefined) {\n return next(ctx);\n }\n const controller = new AbortController();\n const outer = ctx.init.signal;\n // The caller's abort and our deadlines, as one signal.\n const signal = outer\n ? AbortSignal.any([outer, controller.signal])\n : controller.signal;\n\n const total =\n deadline === Number.POSITIVE_INFINITY\n ? undefined\n : deadlineTimer(\n controller,\n deadline,\n `arthron: request timed out after ${deadline}ms`,\n );\n let quiet: ReturnType<typeof setTimeout> | undefined;\n // Restarted whenever something arrives: the headers, then every chunk.\n const stillAlive = () => {\n if (idleMs === undefined) return;\n clearTimeout(quiet);\n quiet = deadlineTimer(\n controller,\n idleMs,\n `arthron: no data for ${idleMs}ms`,\n );\n };\n const finish = () => {\n clearTimeout(total);\n clearTimeout(quiet);\n };\n stillAlive();\n\n let res: Response;\n try {\n res = await next({\n ...ctx,\n init: { ...ctx.init, signal },\n });\n } catch (thrown) {\n finish();\n throw thrown;\n }\n if (!res.body) {\n finish();\n return res;\n }\n stillAlive();\n return rebuildResponse(\n res,\n guardBody(res.body, signal, finish, stillAlive),\n );\n },\n };\n}\n","const encoder = new TextEncoder();\n\n/** Names and filenames inside the quoted Content-Disposition parameters. */\nconst escapeName = (name: string): string =>\n name.replace(/\\n/g, \"%0A\").replace(/\\r/g, \"%0D\").replace(/\"/g, \"%22\");\n\n/** String values go out with CRLF line breaks, as the runtime's encoder sends them. */\nconst normalizeBreaks = (value: string): string =>\n value.replace(/\\r\\n|\\r|\\n/g, \"\\r\\n\");\n\nfunction boundary(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(12));\n return `----arthron${Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\ntype Part = Uint8Array | Blob;\n\n/**\n * A FormData body as multipart/form-data, with its length known before\n * anything is read: the parts' headers and string values are encoded up\n * front, files are only counted by size and streamed while the body is\n * sent — a large file is not held in memory to learn the length.\n */\nexport function encodeFormData(form: FormData): {\n stream: ReadableStream<Uint8Array>;\n total: number;\n type: string;\n} {\n const b = boundary();\n const parts: Part[] = [];\n for (const [name, value] of form) {\n let head = `--${b}\\r\\nContent-Disposition: form-data; name=\"${escapeName(name)}\"`;\n if (typeof value === \"string\") {\n parts.push(encoder.encode(`${head}\\r\\n\\r\\n`));\n parts.push(encoder.encode(normalizeBreaks(value)));\n } else {\n head += `; filename=\"${escapeName(value.name)}\"\\r\\nContent-Type: ${value.type || \"application/octet-stream\"}`;\n parts.push(encoder.encode(`${head}\\r\\n\\r\\n`));\n parts.push(value);\n }\n parts.push(encoder.encode(\"\\r\\n\"));\n }\n parts.push(encoder.encode(`--${b}--\\r\\n`));\n\n const total = parts.reduce(\n (sum, part) =>\n sum + (part instanceof Uint8Array ? part.byteLength : part.size),\n 0,\n );\n let index = 0;\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n const stream = new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n for (;;) {\n if (reader) {\n const { value, done } = await reader.read();\n if (!done) return controller.enqueue(value);\n reader = undefined;\n }\n const part = parts[index++];\n if (part === undefined) return controller.close();\n if (part instanceof Uint8Array) return controller.enqueue(part);\n reader = part.stream().getReader();\n }\n },\n cancel: (reason) => reader?.cancel(reason),\n },\n { highWaterMark: 0 },\n );\n return { stream, total, type: `multipart/form-data; boundary=${b}` };\n}\n","import type { UploadProgressEvent } from \"../../types/request.types\";\n\n/** An empty body is complete at once, not `0 / 0`. */\nexport function progressEvent(\n loaded: number,\n total: number | undefined,\n): UploadProgressEvent {\n const progress =\n total === undefined ? undefined : total === 0 ? 1 : loaded / total;\n return { loaded, total, progress };\n}\n","/**\n * What a transport needs from fetch's arguments, in any shape a plugin\n * wrapping it may pass: arthron sends a URL string and a lowercase header\n * record, a wrapper may pass a Request, a URL or Headers.\n */\nexport const urlOf = (input: RequestInfo | URL): string =>\n input instanceof Request ? input.url : String(input);\n\n/** A Request's headers, then init's on top, as one lowercase record. */\nexport function headersOf(\n input: RequestInfo | URL,\n init: RequestInit,\n): Record<string, string> {\n const merged = new Headers(\n input instanceof Request ? input.headers : undefined,\n );\n for (const [name, value] of new Headers(init.headers))\n merged.set(name, value);\n return Object.fromEntries(merged);\n}\n","import { tagOf } from \"../../request/body\";\nimport type { UploadProgressEvent } from \"../../types/request.types\";\nimport { encodeFormData } from \"./form-data\";\nimport { progressEvent } from \"./progress-event\";\nimport { headersOf } from \"./request-parts\";\n\n/**\n * The most one progress step covers. Chunks are cut to it whatever their\n * source: Node hands an in-memory Blob over as a single chunk, which would\n * jump from 0 to 100%.\n */\nconst SLICE = 64 * 1024;\n\ntype Source = {\n stream: ReadableStream<Uint8Array>;\n total: number | undefined;\n /** The content-type fetch would have derived from the body itself. */\n type: string | undefined;\n};\n\nconst fromBytes = (bytes: Uint8Array, type?: string | null): Source => ({\n // One chunk; the counting stream cuts it into SLICE-sized steps.\n stream: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n }),\n total: bytes.byteLength,\n type: type ?? undefined,\n});\n\n/** The body as bytes to count, or `undefined` for none (or one fetch should get as is). */\nasync function sourceOf(\n body: unknown,\n headers: Record<string, string>,\n): Promise<Source | undefined> {\n if (body === undefined || body === null) return undefined;\n if (typeof body === \"string\") {\n return fromBytes(\n new TextEncoder().encode(body),\n \"text/plain;charset=UTF-8\",\n );\n }\n if (ArrayBuffer.isView(body)) {\n return fromBytes(\n new Uint8Array(body.buffer, body.byteOffset, body.byteLength),\n );\n }\n switch (tagOf(body)) {\n case \"Blob\":\n case \"File\": {\n const blob = body as Blob;\n return {\n stream: blob.stream(),\n total: blob.size,\n type: blob.type || undefined,\n };\n }\n case \"ArrayBuffer\":\n return fromBytes(new Uint8Array(body as ArrayBuffer));\n case \"URLSearchParams\":\n return fromBytes(\n new TextEncoder().encode(String(body)),\n \"application/x-www-form-urlencoded;charset=UTF-8\",\n );\n case \"FormData\": {\n // Encoded here so its length is known while files still stream.\n const { stream, total, type } = encodeFormData(body as FormData);\n return { stream, total, type };\n }\n case \"ReadableStream\": {\n const length = Number(headers[\"content-length\"]);\n return {\n stream: body as ReadableStream<Uint8Array>,\n total: Number.isSafeInteger(length) && length >= 0 ? length : undefined,\n type: undefined,\n };\n }\n default:\n return undefined;\n }\n}\n\n/**\n * `base` with the request body counted as it is read. The body is swapped\n * here, inside the call, so the context the plugins see keeps the caller's\n * own. A callback that throws fails the call with its own error, not the\n * \"fetch failed\" the body error would become.\n */\nexport function countingFetch(\n base: typeof fetch,\n onProgress: (event: UploadProgressEvent) => void,\n): typeof fetch {\n return (async (input: RequestInfo | URL, init: RequestInit = {}) => {\n const headers = headersOf(input, init);\n const source = await sourceOf(init.body, headers);\n if (!source) return base(input, init);\n const { total, type } = source;\n if (total !== undefined && !(\"content-length\" in headers)) {\n headers[\"content-length\"] = String(total);\n }\n if (type && !(\"content-type\" in headers)) headers[\"content-type\"] = type;\n\n const signal = init.signal;\n const reader = source.stream.getReader();\n let loaded = 0;\n // What is left of a chunk longer than SLICE.\n let rest: Uint8Array | undefined;\n let reported = false;\n // Once the response is in, the server has answered: no more progress.\n let answered = false;\n let failure: { error: unknown } | undefined;\n const report = () => {\n if (answered) return;\n reported = true;\n try {\n onProgress(progressEvent(loaded, total));\n } catch (error) {\n failure = { error };\n throw error;\n }\n };\n const body = new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n const stopped = async () => {\n if (!signal?.aborted) return false;\n await reader.cancel(signal.reason).catch(() => {});\n controller.error(signal.reason);\n return true;\n };\n if (await stopped()) return;\n let chunk = rest;\n if (!chunk?.byteLength) {\n const { value, done } = await reader.read();\n if (done) {\n // An empty body still says it is done, once.\n if (!reported) report();\n return controller.close();\n }\n chunk = value;\n // The abort may have come while that read was pending.\n if (await stopped()) return;\n }\n rest = chunk.subarray(SLICE);\n const piece = chunk.subarray(0, SLICE);\n loaded += piece.byteLength;\n report();\n controller.enqueue(piece);\n },\n cancel: (reason) => reader.cancel(reason),\n },\n { highWaterMark: 0 },\n );\n try {\n const res = await base(input, {\n ...init,\n body,\n headers,\n duplex: \"half\",\n } as RequestInit);\n answered = true;\n return res;\n } catch (error) {\n throw failure ? failure.error : error;\n }\n }) as typeof fetch;\n}\n","import {\n type FetchedFields,\n withFields,\n} from \"../../response/rebuild-response\";\nimport type { UploadProgressEvent } from \"../../types/request.types\";\nimport { progressEvent } from \"./progress-event\";\nimport { headersOf, urlOf } from \"./request-parts\";\n\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);\n\n/** `getAllResponseHeaders()` as `Headers`; lines without a name are skipped. */\nexport function parseResponseHeaders(raw: string): Headers {\n const headers = new Headers();\n for (const line of raw.split(/\\r?\\n/)) {\n const colon = line.indexOf(\":\");\n if (colon <= 0) continue;\n const name = line.slice(0, colon).trim();\n if (name) headers.append(name, line.slice(colon + 1).trim());\n }\n return headers;\n}\n\n/**\n * What fetch would report for this response. XHR gives only the final URL;\n * the request URL is normalized (case, default port, no fragment) before\n * comparing, so writing it differently is not a redirect.\n */\nexport function responseFields(\n requestUrl: string,\n responseURL: string,\n pageHref: string | undefined,\n): FetchedFields {\n const requested = new URL(requestUrl, pageHref);\n requested.hash = \"\";\n const final = new URL(responseURL || requested.href, pageHref);\n const sameOrigin =\n pageHref !== undefined && final.origin === new URL(pageHref).origin;\n return {\n url: final.href,\n redirected: final.href !== requested.href,\n type: sameOrigin ? \"basic\" : \"cors\",\n };\n}\n\n/**\n * A fetch over XMLHttpRequest, for the one thing fetch cannot do in a\n * browser: report upload progress. It honours what an arthron attempt\n * carries — method, headers, body, `credentials: \"include\"`, `signal` — and\n * fails the way fetch does, so `send` makes the same ArthronErrors: a\n * network failure rejects with a TypeError, an abort with the signal's\n * reason. The response arrives whole; XHR does not stream it.\n */\nexport function xhrFetch(\n onProgress: (event: UploadProgressEvent) => void,\n): typeof fetch {\n return ((input: RequestInfo | URL, init: RequestInit = {}) =>\n new Promise<Response>((resolve, reject) => {\n const url = urlOf(input);\n const { signal } = init;\n if (signal?.aborted) return reject(signal.reason);\n\n const xhr = new XMLHttpRequest();\n let settled = false;\n const settle = (done: () => void) => {\n if (settled) return;\n settled = true;\n signal?.removeEventListener(\"abort\", onAbort);\n done();\n };\n const fail = (error: unknown) => {\n settle(() => reject(error));\n xhr.abort();\n };\n const onAbort = () => fail(signal?.reason);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n let reported: UploadProgressEvent | undefined;\n const report = (loaded: number, total: number | undefined) => {\n if (\n settled ||\n (reported?.loaded === loaded && reported.total === total)\n ) {\n return;\n }\n reported = progressEvent(loaded, total);\n try {\n onProgress(reported);\n } catch (error) {\n // The callback's own error, not a network failure to retry.\n fail(error);\n }\n };\n const fromEvent = (e: ProgressEvent) =>\n report(e.loaded, e.lengthComputable ? e.total : undefined);\n xhr.upload.onprogress = fromEvent;\n // Browsers thin out progress events; the last one says it all went.\n xhr.upload.onload = fromEvent;\n\n xhr.onload = () => {\n // An empty body fires no upload events: it is done at once.\n if (!reported) report(0, 0);\n if (xhr.status === 0) {\n return settle(() => reject(new TypeError(\"Network request failed\")));\n }\n const headers = parseResponseHeaders(xhr.getAllResponseHeaders());\n const body = NULL_BODY_STATUSES.has(xhr.status) ? null : xhr.response;\n const res = new Response(body, {\n status: xhr.status,\n statusText: xhr.statusText,\n headers,\n });\n const page = (globalThis as { location?: { href?: string } }).location\n ?.href;\n settle(() =>\n resolve(withFields(res, responseFields(url, xhr.responseURL, page))),\n );\n };\n xhr.onerror = xhr.ontimeout = () =>\n settle(() => reject(new TypeError(\"Network request failed\")));\n\n xhr.open(init.method ?? \"GET\", url);\n xhr.responseType = \"arraybuffer\";\n if (init.credentials === \"include\") xhr.withCredentials = true;\n for (const [name, value] of Object.entries(headersOf(input, init))) {\n xhr.setRequestHeader(name, value);\n }\n xhr.send((init.body ?? null) as XMLHttpRequestBodyInit | null);\n })) as typeof fetch;\n}\n","import { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type {\n RequestContext,\n UploadProgressEvent,\n} from \"../../types/request.types\";\nimport { countingFetch } from \"./counting-fetch\";\nimport { xhrFetch } from \"./xhr-fetch\";\n\ntype Runtime = {\n versions?: Readonly<Record<string, string | undefined>>;\n hasXhr: boolean;\n userAgent?: string;\n deno?: boolean;\n};\n\n/**\n * How a progress call can be sent here. Node first — jsdom on Node has an\n * XMLHttpRequest too, but Node's fetch is the real transport there. Bun,\n * Deno, Electron and Cloudflare Workers also expose `process.versions.node`;\n * only Node was shown to keep content-length on a counted body, and an\n * Electron renderer has XHR.\n */\nexport function transportFor(runtime: Runtime): \"count\" | \"xhr\" | undefined {\n const { versions, hasXhr, userAgent, deno } = runtime;\n const node =\n Boolean(versions?.node) &&\n !versions?.bun &&\n !versions?.electron &&\n !deno &&\n userAgent !== \"Cloudflare-Workers\";\n if (node) return \"count\";\n return hasXhr ? \"xhr\" : undefined;\n}\n\nfunction currentRuntime(): Runtime {\n const g = globalThis as {\n process?: { versions?: Record<string, string | undefined> };\n navigator?: { userAgent?: string };\n };\n const runtime: Runtime = {\n hasXhr: typeof XMLHttpRequest === \"function\",\n deno: \"Deno\" in globalThis,\n };\n if (g.process?.versions) runtime.versions = g.process.versions;\n if (g.navigator?.userAgent) runtime.userAgent = g.navigator.userAgent;\n return runtime;\n}\n\n/**\n * What an XHR cannot honour and whose loss would change the call: it always\n * follows redirects, has no no-cors mode, and always sends cookies to its own\n * origin. `undefined` when the call can go over XHR.\n */\nexport function xhrRefusal(\n init: Pick<RequestInit, \"redirect\" | \"mode\" | \"credentials\">,\n url: string,\n pageHref: string | undefined,\n): string | undefined {\n if (init.redirect === \"manual\" || init.redirect === \"error\") {\n return `redirect: \"${init.redirect}\" — XMLHttpRequest always follows redirects`;\n }\n if (init.mode === \"no-cors\") {\n return 'mode: \"no-cors\" — XMLHttpRequest has no no-cors mode';\n }\n if (init.credentials === \"omit\" && pageHref) {\n const page = new URL(pageHref).origin;\n if (new URL(url, pageHref).origin === page) {\n return 'credentials: \"omit\" on this origin — XMLHttpRequest always sends cookies to its own origin';\n }\n }\n return undefined;\n}\n\n/**\n * The attempt with its own signal, joined to the call's. A callback that\n * throws aborts the attempt with its error: send passes an abort reason on\n * untouched, so it stays a plugin exception — not a network ArthronError\n * that error interceptors would see and retry would repeat.\n */\nfunction withOwnSignal(\n ctx: RequestContext,\n onProgress: (event: UploadProgressEvent) => void,\n): { ctx: RequestContext; report: (event: UploadProgressEvent) => void } {\n const own = new AbortController();\n const outer = ctx.init.signal;\n const signal = outer ? AbortSignal.any([outer, own.signal]) : own.signal;\n const report = (event: UploadProgressEvent) => {\n if (own.signal.aborted) return;\n try {\n onProgress(event);\n } catch (error) {\n own.abort(error);\n }\n };\n return { ctx: { ...ctx, init: { ...ctx.init, signal } }, report };\n}\n\n/**\n * Does nothing by itself — only a call that passes `meta.onUploadProgress`\n * reports upload progress. On such a call it changes how that one attempt\n * is sent: over XMLHttpRequest in a browser, with a counted body in Node.\n * Every other call, on this client too, goes out exactly as before.\n *\n * Progress needs the runtime's own fetch: with an injected `fetch`, another\n * plugin's transport, or a runtime that is neither (Deno, Bun, Cloudflare\n * Workers, a service worker), the call is sent as usual without progress.\n * Put it last in `use`: the call's signal carries a throwing callback's\n * error, and an interceptor after it that replaces the signal would cut it.\n */\nexport function uploadProgress(): Plugin {\n return {\n name: \"uploadProgress\",\n request: (ctx) => {\n const onProgress = ctx.meta?.onUploadProgress;\n const { body } = ctx.init;\n if (!onProgress || body === undefined || body === null) return ctx;\n if (ctx.fetch !== globalThis.fetch) return ctx;\n const transport = transportFor(currentRuntime());\n if (!transport) return ctx;\n if (transport === \"xhr\") {\n const page = (globalThis as { location?: { href?: string } }).location\n ?.href;\n const refusal = xhrRefusal(ctx.init, ctx.url, page);\n if (refusal) {\n throw new TypeError(\n `arthron: onUploadProgress cannot be combined with ${refusal}; drop one of them`,\n );\n }\n // XHR cannot send a stream; it goes out as usual, without progress.\n if (isReadableStream(body)) return ctx;\n }\n const { ctx: next, report } = withOwnSignal(ctx, onProgress);\n return {\n ...next,\n fetch:\n transport === \"xhr\"\n ? xhrFetch(report)\n : countingFetch(ctx.fetch, report),\n };\n },\n };\n}\n"],"mappings":"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"}
package/dist/plugins.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_plugins = require("./plugins-CVrzb8x6.cjs");
2
+ const require_plugins = require("./plugins-Cigqz2SS.cjs");
3
3
  exports.bearerAuth = require_plugins.bearerAuth;
4
4
  exports.logging = require_plugins.logging;
5
5
  exports.retry = require_plugins.retry;
6
6
  exports.timeout = require_plugins.timeout;
7
+ exports.uploadProgress = require_plugins.uploadProgress;
@@ -1,2 +1,2 @@
1
- import { a as LogEvent, b as RetryOptions, c as BearerAuthOptions, i as logging, n as TimeoutOptions, o as LoggingOptions, r as retry, s as bearerAuth, t as timeout } from "./index-Bz1P80JN.cjs";
2
- export { type BearerAuthOptions, type LogEvent, type LoggingOptions, type RetryOptions, type TimeoutOptions, bearerAuth, logging, retry, timeout };
1
+ import { S as UploadProgressEvent, a as logging, c as bearerAuth, i as retry, l as BearerAuthOptions, n as timeout, o as LogEvent, r as TimeoutOptions, s as LoggingOptions, t as uploadProgress, x as RetryOptions } from "./index-C3lylSNd.cjs";
2
+ export { type BearerAuthOptions, type LogEvent, type LoggingOptions, type RetryOptions, type TimeoutOptions, type UploadProgressEvent, bearerAuth, logging, retry, timeout, uploadProgress };
@@ -1,2 +1,2 @@
1
- import { a as LogEvent, b as RetryOptions, c as BearerAuthOptions, i as logging, n as TimeoutOptions, o as LoggingOptions, r as retry, s as bearerAuth, t as timeout } from "./index-Bz1P80JN.mjs";
2
- export { type BearerAuthOptions, type LogEvent, type LoggingOptions, type RetryOptions, type TimeoutOptions, bearerAuth, logging, retry, timeout };
1
+ import { S as UploadProgressEvent, a as logging, c as bearerAuth, i as retry, l as BearerAuthOptions, n as timeout, o as LogEvent, r as TimeoutOptions, s as LoggingOptions, t as uploadProgress, x as RetryOptions } from "./index-C3lylSNd.mjs";
2
+ export { type BearerAuthOptions, type LogEvent, type LoggingOptions, type RetryOptions, type TimeoutOptions, type UploadProgressEvent, bearerAuth, logging, retry, timeout, uploadProgress };
package/dist/plugins.mjs CHANGED
@@ -1 +1 @@
1
- export { c as bearerAuth, r as logging, n as retry, t as timeout } from "./plugins-ChNZ2Q8t.mjs";
1
+ export { l as bearerAuth, i as logging, r as retry, n as timeout, t as uploadProgress } from "./plugins-CHCMxtAE.mjs";
package/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > A fetch client assembled from plugins. Returns native `Response`; middleware controls flow (can retry via `next`), interceptors transform values. Zero dependencies, ESM + CJS, Node 22+ and browsers, TypeScript 5.0+.
4
4
 
5
- Import: `import { arthron, isArthronError, SchemaError, definePlugin } from "arthron";` and the built-in plugins `import { bearerAuth, retry, timeout, logging } from "arthron/plugins";` (also exported from "arthron" as deprecated aliases until the next major, bearerAuth excepted).
5
+ Import: `import { arthron, isArthronError, SchemaError, definePlugin } from "arthron";` and the built-in plugins `import { bearerAuth, retry, timeout, logging, uploadProgress } from "arthron/plugins";` (also exported from "arthron" as deprecated aliases until the next major, bearerAuth excepted).
6
6
 
7
7
  ## Create
8
8
 
@@ -68,14 +68,14 @@ type Plugin = {
68
68
  response?: (res, ctx) => Response; // per successful attempt; body is single-use, clone() to peek
69
69
  error?: (err: ArthronError, ctx) => ArthronError; // per failed attempt; return value is always thrown
70
70
  };
71
- type RequestContext = { url: string; init: Omit<RequestInit, "headers"> & { headers: Record<string, string> }; meta: ArthronMeta }; // headers: lowercase record, re-normalized after each request interceptor — spreading is safe
72
- // meta: per-call plugin settings from api.get(path, { meta }), never sent. Built-ins read meta.bearerAuth (false), meta.retry (false | options) and meta.timeout (ms).
71
+ type RequestContext = { url: string; init: Omit<RequestInit, "headers"> & { headers: Record<string, string> }; meta: ArthronMeta; fetch?: typeof fetch }; // fetch: the fetch this attempt uses (client's, else global) — a plugin may replace or wrap it per attempt; send still makes every ArthronError. headers: lowercase record, re-normalized after each request interceptor — spreading is safe
72
+ // meta: per-call plugin settings from api.get(path, { meta }), never sent. Built-ins read meta.bearerAuth (false), meta.onUploadProgress (callback), meta.retry (false | options) and meta.timeout (ms).
73
73
  // Add keys: declare module "arthron" { interface ArthronMeta { skipTrace?: boolean } }
74
74
  ```
75
75
 
76
76
  Order: `middleware[0]( middleware[1]( attempt: request interceptors → fetch → response | error interceptors ) )` — a middleware is called once; code after `await next(ctx)` runs after the response interceptors. Inside `try`, write `return await next(ctx)` or the catch misses the error. Default order: `[logging(), bearerAuth(…), timeout(ms), retry()]` (timeout before retry = whole call, after = per attempt). Same-named plugins replace each other; for two timeouts rename one: `{ ...timeout(30_000), name: "callTimeout" }`. Values that must be fresh per attempt (token, signature) → request interceptor; the same on every attempt (idempotency key) → middleware before retry. Each attempt gets a fresh copy of ctx (headers, and a FormData/URLSearchParams body).
77
77
 
78
- `api.use(...plugins)`, `api.extend(config)` and `api.without(...names)` return NEW clients; the original is unchanged. A named plugin replaces the one with the same name at its position (built-ins: "bearerAuth", "retry", "timeout", "logging"); unnamed plugins append.
78
+ `api.use(...plugins)`, `api.extend(config)` and `api.without(...names)` return NEW clients; the original is unchanged. A named plugin replaces the one with the same name at its position (built-ins: "bearerAuth", "retry", "timeout", "logging", "uploadProgress"); unnamed plugins append.
79
79
 
80
80
  Helpers (identity, for typing): `definePlugin(p)`, `defineMiddleware(m)`, `defineInterceptor.request(f) / .response(f) / .error(f)`.
81
81
 
@@ -85,6 +85,7 @@ Helpers (identity, for typing): `definePlugin(p)`, `defineMiddleware(m)`, `defin
85
85
  - `timeout(ms)` | `timeout({ total?, idle? })` — `total` (= `ms`): deadline from attempt start until the body is fully read; `idle`: aborts after that many ms without data (headers or chunks), so `timeout({ idle: 30_000 })` never cuts a flowing stream. Both together: whichever trips first. `{}` → TypeError. Deprecated: `timeout(ms, { idle })`. Rejects with TimeoutError (not ArthronError; retry ignores it). Place after `retry` for per-attempt, before it for total. Per call: `meta: { timeout: ms }` (Infinity lifts it). `Infinity` = no deadline; NaN/negative/>2^31-1 → TypeError. Keeps `res.url`/`redirected`/`type` (through `clone()` too); timers are unref'd.
86
86
  - `bearerAuth({ origin, token, refresh? })` — `authorization: Bearer <token>` on every attempt, only to requests on `origin` (a URL or array; only origins count, non-http(s) → TypeError). `token: () => string | null | undefined | Promise<…>`, read per attempt; empty → no header. With `refresh: () => Promise<void>`, a 401 refreshes and redoes the call once: concurrent 401s share one refresh, a call whose token was already replaced retries without refreshing, a second 401 is thrown, a rejected refresh reaches every waiting call unchanged. Without `refresh`, or for a ReadableStream body (cannot be resent), 401 is thrown without refreshing. Per call: `meta: { bearerAuth: false }` = no token, no refresh — required on the login/refresh requests if they use the same client (else the refresh's own 401 waits on itself). Token is opaque: refresh ahead of a JWT's exp inside `token()`.
87
87
  - `logging({ enabled=true, logger=console, onEvent?, now? })` — start/success/error lines or structured events `{ phase, method, path, status?, ms?, error? }`. Never prints query strings or userinfo. Does not log cancellations.
88
+ - `uploadProgress()` — does nothing alone; a call with `meta: { onUploadProgress: ({ loaded, total, progress }) => … }` and a body reports upload progress (total/progress undefined when the size is unknown; restarts at 0 per attempt; no events after the response or an abort). Browser (incl. Electron renderer): sent over XMLHttpRequest (cross-origin → CORS preflight; response not streamed; redirect "manual"/"error", mode "no-cors", credentials "omit" to the page's origin → TypeError before sending; cache/priority/referrerPolicy/integrity/keepalive ignored; ReadableStream body → no progress). Node (incl. jsdom on Node): fetch with the body counted in ≤ 64 KiB steps, content-length and Blob content-type kept (S3 presigned PUT works); FormData encoded with a known length, files streamed. Deno/Bun/Cloudflare Workers/Electron main/service workers, an injected fetch or another transport plugin → sent as usual without progress. A throwing callback rejects the call with its own error (not ArthronError, not retried) — keep uploadProgress last in use. Works through ctx.fetch.
88
89
 
89
90
  ## Recipes
90
91
 
@@ -107,7 +108,7 @@ for await (const { data } of api.post("/v1/chat/completions", { model, messages,
107
108
 
108
109
  ## Non-goals
109
110
 
110
- No request/response key transformation, no caching, no dedupe, no upload progress, no backend-envelope unwrapping. Do those in your adapter layer.
111
+ No request/response key transformation, no caching, no dedupe, no backend-envelope unwrapping. Do those in your adapter layer.
111
112
 
112
113
  ## Types
113
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arthron",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Composable fetch client — create from config, plug in interceptors and middleware",
5
5
  "license": "MIT",
6
6
  "author": "lapidix",
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins-CVrzb8x6.cjs","names":["originOf","MAX_TIMER_MS","settle"],"sources":["../src/request/url.ts","../src/error/arthron-error.ts","../src/request/body.ts","../src/plugins/bearer-auth/bearer-auth.ts","../src/response/rebuild-response.ts","../src/error/create-error.ts","../src/plugins/logging/logging.ts","../src/plugins/retry/retry.ts","../src/plugins/timeout/timeout.ts"],"sourcesContent":["import type { QueryParams } from \"../types/request.types\";\n\nconst ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\\/\\//i;\nconst HTTP_URL = /^https?:\\/\\//i;\n\nexport function isAbsoluteUrl(path: string): boolean {\n return ABSOLUTE_URL.test(path);\n}\n\n/**\n * Origin + pathname only — the query and userinfo tend to carry secrets.\n * For anything printed: log lines, error messages, `ArthronError.toJSON`.\n */\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}`;\n } catch {\n return url.split(/[?#]/)[0] ?? url;\n }\n}\n\n/**\n * `text` with `url` redacted wherever it appears, as given or as the URL\n * parser normalizes it — for a runtime's own message that repeats the URL.\n */\nexport function redactUrlIn(text: string, url: string): string {\n const shown = redactUrl(url);\n let href: string | undefined;\n try {\n href = new URL(url).href;\n } catch {}\n let out = text.split(url).join(shown);\n if (href && href !== url) out = out.split(href).join(shown);\n return out;\n}\n\n/**\n * `undefined` when `url` has no origin of its own. A relative baseURL takes\n * the page's origin in a browser and has none elsewhere.\n */\nfunction originOf(url: string): string | undefined {\n const page = (globalThis as { location?: { href?: string } }).location?.href;\n try {\n return new URL(url, page).origin;\n } catch {\n return undefined;\n }\n}\n\n/** `path` is absolute and points away from `baseURL`'s origin. */\nexport function leavesBase(baseURL: string | undefined, path: string): boolean {\n if (baseURL === undefined || !isAbsoluteUrl(path)) return false;\n const base = originOf(baseURL);\n return base === undefined || originOf(path) !== base;\n}\n\nfunction toQueryString(params: QueryParams | undefined): string {\n if (!params) return \"\";\n if (Object.prototype.toString.call(params) === \"[object URLSearchParams]\") {\n return params.toString();\n }\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (item === null || item === undefined) continue;\n // `String({})` is \"[object Object]\" and a Date prints in local time —\n // neither is what a server expects.\n if (typeof item === \"object\" || typeof item === \"function\") {\n throw new TypeError(\n `arthron: params.${key} is not a string, number or boolean — pass one of those or an array of them (dates: date.toISOString())`,\n );\n }\n search.append(key, String(item));\n }\n }\n return search.toString();\n}\n\n/**\n * Fill `{name}` placeholders with encoded values, so each one stays inside\n * its segment: \"/\", \"?\" and \"#\" are encoded, and the values WHATWG URL would\n * turn into a different path — \"\", \".\" and \"..\" — are refused. A placeholder\n * without a value, or a value without a placeholder, is a mistake too.\n */\nexport function fillPath(\n template: string,\n values: Record<string, string | number>,\n): string {\n const used = new Set<string>();\n const filled = template.replace(/\\{([^{}/]+)\\}/g, (_, name: string) => {\n if (!Object.hasOwn(values, name)) {\n throw new TypeError(\n `arthron: path \"${template}\" has {${name}} but no value for it — pass path: { ${name}: … }`,\n );\n }\n used.add(name);\n const value = String(values[name]);\n if (value === \"\" || value === \".\" || value === \"..\") {\n throw new TypeError(\n `arthron: path value ${name}=${JSON.stringify(value)} would change the path, not fill a segment — pass a real id`,\n );\n }\n return encodeURIComponent(value);\n });\n const unused = Object.keys(values).find((name) => !used.has(name));\n if (unused !== undefined) {\n throw new TypeError(\n `arthron: path \"${template}\" has no {${unused}} — add the placeholder or drop the value`,\n );\n }\n return filled;\n}\n\n/**\n * Join `baseURL` and `path` by plain string concatenation so a path prefix in\n * the base (`/v1`) survives a leading slash in the path. WHATWG `new URL()`\n * would drop it. An absolute path replaces the base only when it stays on the\n * base's origin, or when `allowAbsoluteUrls` says other origins are fine — a\n * path built from user input or an API response must not carry the client's\n * credentials somewhere else.\n */\nexport function resolveUrl(\n baseURL: string | undefined,\n path: string,\n params?: QueryParams,\n allowAbsoluteUrls = false,\n): string {\n let url: string;\n if (isAbsoluteUrl(path)) {\n if (!HTTP_URL.test(path)) {\n const scheme = path.slice(0, path.indexOf(\":\") + 1);\n throw new TypeError(\n `arthron: only http(s) URLs can be requested, got a \"${scheme}\" URL — use an http:// or https:// URL`,\n );\n }\n if (!allowAbsoluteUrls && leavesBase(baseURL, path)) {\n throw new TypeError(\n `arthron: ${originOf(path)} is outside baseURL ${(baseURL && originOf(baseURL)) ?? baseURL} — pass a path relative to baseURL, or set arthron({ allowAbsoluteUrls: true }) to allow other origins (config headers are not sent to them)`,\n );\n }\n url = path;\n } else {\n if (baseURL === undefined) {\n throw new TypeError(\n `arthron: relative path \"${redactUrl(path)}\" needs a baseURL — pass baseURL in arthron({ baseURL }) or use an absolute URL`,\n );\n }\n url = `${baseURL.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n }\n const query = toQueryString(params);\n if (!query) return url;\n // The query goes before any #fragment, or the server never sees it.\n const hash = url.indexOf(\"#\");\n const [base, fragment] =\n hash === -1 ? [url, \"\"] : [url.slice(0, hash), url.slice(hash)];\n return `${base}${base.includes(\"?\") ? \"&\" : \"?\"}${query}${fragment}`;\n}\n","import { redactUrl } from \"../request/url\";\nimport type { RequestContext } from \"../types/request.types\";\n\ntype ArthronErrorInit = {\n message: string;\n status: number;\n statusText: string;\n headers: Headers;\n data: unknown;\n detail?: string | undefined;\n request: RequestContext;\n response?: Response;\n cause?: unknown;\n};\n\n/**\n * Marks an ArthronError across copies of this package. The ESM and CJS\n * builds can both end up loaded (a CJS dependency creates the client, ESM\n * app code catches), each with its own class — `Symbol.for` is shared.\n */\nconst BRAND = Symbol.for(\"arthron.ArthronError\");\n\n/**\n * The only error arthron itself produces. Two sources: a response with\n * `ok === false`, or fetch rejecting outright (network failure, `status` 0).\n * Everything else — cancellation, interceptor bugs — is rethrown untouched.\n */\nexport class ArthronError extends Error {\n override readonly name = \"ArthronError\";\n /** HTTP status of the failed response; `0` when fetch itself failed (network). */\n readonly status: number;\n readonly statusText: string;\n /** Response headers; empty for network failures. Not enumerable — kept out of logs. */\n declare readonly headers: Headers;\n /** Parsed body: JSON if it parses, else the text, else `undefined`. */\n readonly data: unknown;\n /**\n * The server's own explanation, picked out of `data` (by default\n * `message` → `error` → `error.message` → `detail`; see the `errorDetail`\n * config). `undefined` when the body had none. Untrusted server text: show\n * it to users as text, never as HTML. `message`, by contrast, is written by\n * arthron — method, URL without query, status — and is meant for logs.\n */\n readonly detail: string | undefined;\n /**\n * The context that was sent — url and init after request interceptors.\n * Not enumerable: it holds credentials, so `JSON.stringify`, `console.error`\n * and error trackers leave it out. Read it directly when you need it.\n */\n declare readonly request: RequestContext;\n /** The failed Response, still readable — its body holds what arthron read (at most 64 KiB). Absent for network failures. Not enumerable. */\n declare readonly response: Response | undefined;\n /** The underlying error for network failures. */\n override readonly cause: unknown;\n\n constructor(init: ArthronErrorInit) {\n super(init.message);\n this.status = init.status;\n this.statusText = init.statusText;\n this.data = init.data;\n this.detail = init.detail;\n this.cause = init.cause;\n Object.defineProperties(this, {\n headers: { value: init.headers },\n request: { value: init.request },\n response: { value: init.response },\n [BRAND]: { value: true },\n });\n }\n\n /** `instanceof ArthronError` holds for an error from any copy of the package. */\n static override [Symbol.hasInstance](value: unknown): boolean {\n return this === ArthronError\n ? isArthronError(value)\n : Function.prototype[Symbol.hasInstance].call(this, value);\n }\n\n /** What serializers see: no headers, no body, no query or userinfo. */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n detail: this.detail,\n status: this.status,\n statusText: this.statusText,\n method: this.request.init.method ?? \"GET\",\n url: redactUrl(this.request.url),\n data: this.data,\n };\n }\n}\n\n/** `true` for errors arthron produced: a `!ok` response or a network failure. Cancellations and plugin bugs are not ArthronErrors. */\nexport function isArthronError(error: unknown): error is ArthronError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { [BRAND]?: unknown })[BRAND] === true\n );\n}\n","type Serialized = {\n body: BodyInit | undefined;\n headers: Record<string, string>;\n};\n\nconst RAW_TAGS = new Set([\n \"FormData\",\n \"Blob\",\n \"File\",\n \"ArrayBuffer\",\n \"URLSearchParams\",\n \"ReadableStream\",\n]);\n\n/**\n * `Symbol.toStringTag`, not `instanceof`: a body from another realm or a\n * polyfill (formdata-node, fetch-blob) must not be JSON-encoded into `{}`.\n */\nconst tagOf = (value: unknown): string =>\n Object.prototype.toString.call(value).slice(8, -1);\n\nexport function isReadableStream(value: unknown): value is ReadableStream {\n return tagOf(value) === \"ReadableStream\";\n}\n\n/**\n * Whether `RequestCtor` can send a ReadableStream body. Node and Chromium\n * read `duplex` and leave the stream alone; Firefox ignores `duplex` and\n * would send the text \"[object ReadableStream]\"; older Safari refuses it.\n * The check from\n * https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests\n */\nexport function canStreamRequestBody(RequestCtor: typeof Request): boolean {\n let duplexRead = false;\n try {\n const request = new RequestCtor(\"https://stream.test\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexRead = true;\n return \"half\";\n },\n } as RequestInit);\n return duplexRead && !request.headers.has(\"content-type\");\n } catch {\n return false;\n }\n}\n\nlet runtimeStreams: boolean | undefined;\n\n/** `canStreamRequestBody` for the global `Request`, checked once. */\nexport function runtimeStreamsRequestBody(): boolean {\n runtimeStreams ??=\n typeof Request !== \"function\" || canStreamRequestBody(Request);\n return runtimeStreams;\n}\n\n/**\n * What JSON.stringify would turn into `{}`, `undefined` or internal state\n * instead of the data — with the fix, so it reads as an error message.\n */\nfunction mangledByJson(body: unknown): string | undefined {\n if (typeof body === \"function\" || typeof body === \"symbol\") {\n return `a ${typeof body}, which JSON cannot carry — send its data instead`;\n }\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) {\n return undefined;\n }\n if (Symbol.asyncIterator in body) {\n return \"an async iterable (a Node stream?) — pass a ReadableStream (Readable.toWeb(stream)) or collect it first\";\n }\n if (Symbol.iterator in body) {\n return \"an iterable such as a Map, Set or generator — convert it to an array or plain object first\";\n }\n return undefined;\n}\n\n/** Bodies fetch already knows how to send. Content-Type is left to the caller / runtime. */\nfunction isRawBody(body: unknown): body is BodyInit {\n if (typeof body === \"string\") return true;\n if (ArrayBuffer.isView(body)) return true;\n return RAW_TAGS.has(tagOf(body));\n}\n\n/**\n * Turn a caller-supplied body into something fetch accepts. Raw bodies pass\n * through untouched; anything else is JSON with `content-type` set — unless\n * the caller already set one (headers arrive lowercased, so a plain lookup is\n * enough).\n */\nexport function serializeBody(\n body: unknown,\n headers: Record<string, string>,\n): Serialized {\n if (body === undefined) return { body: undefined, headers };\n if (tagOf(body) === \"FormData\") {\n // The runtime must write `multipart/form-data; boundary=…` itself; any\n // content-type set here (a JSON default, typically) breaks the upload.\n const { \"content-type\": _dropped, ...rest } = headers;\n return { body: body as FormData, headers: rest };\n }\n if (isRawBody(body)) return { body, headers };\n const mangled = mangledByJson(body);\n if (mangled) {\n throw new TypeError(`arthron: the body is ${mangled}`);\n }\n const type = headers[\"content-type\"];\n if (type !== undefined && !/\\bjson\\b/i.test(type)) {\n throw new TypeError(\n `arthron: a ${typeof body} body is sent as JSON, but content-type is \"${type}\" — pass a string, URLSearchParams or FormData for that content type, or drop the header`,\n );\n }\n const withType =\n \"content-type\" in headers\n ? headers\n : { ...headers, \"content-type\": \"application/json\" };\n return { body: JSON.stringify(body), headers: withType };\n}\n","import { isArthronError } from \"../../error/arthron-error\";\nimport { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { BearerAuthOptions } from \"./bearer-auth.types\";\n\n/** The origin of an absolute http(s) URL; `undefined` for anything else. */\nfunction originOf(url: string): string | undefined {\n try {\n const { protocol, origin } = new URL(url);\n return protocol === \"http:\" || protocol === \"https:\" ? origin : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction allowedOrigins(origin: BearerAuthOptions[\"origin\"]): Set<string> {\n const urls = typeof origin === \"string\" ? [origin] : origin;\n if (urls.length === 0) {\n throw new TypeError(\n 'arthron: bearerAuth({ origin }) needs at least one URL — e.g. origin: \"https://api.example.com\"',\n );\n }\n return new Set(\n urls.map((url) => {\n const found = originOf(url);\n if (found) return found;\n throw new TypeError(\n `arthron: bearerAuth({ origin }) needs an http(s) URL, got ${JSON.stringify(url)} — e.g. origin: \"https://api.example.com\"`,\n );\n }),\n );\n}\n\n/**\n * Bearer-token auth. A request interceptor sets `authorization: Bearer\n * <token>` on every attempt to `origin`; with `refresh`, a middleware turns a\n * 401 into one refresh and one more try. Concurrent 401s share the refresh\n * (with refresh-token rotation, parallel refreshes revoke each other), and a\n * call whose token was replaced meanwhile just retries. A call opts out with\n * `meta: { bearerAuth: false }`.\n */\nexport function bearerAuth(options: BearerAuthOptions): Plugin {\n const { token, refresh } = options;\n const allowed = allowedOrigins(options.origin);\n const ours = (url: string) => allowed.has(originOf(url) ?? \"\");\n const bearer = async () => {\n const value = await token();\n return value ? `Bearer ${value}` : undefined;\n };\n let refreshing: Promise<void> | undefined;\n\n return {\n name: \"bearerAuth\",\n request: async (ctx) => {\n if (ctx.meta?.bearerAuth === false || !ours(ctx.url)) return ctx;\n const value = await bearer();\n if (!value) return ctx;\n return {\n ...ctx,\n init: {\n ...ctx.init,\n headers: { ...ctx.init.headers, authorization: value },\n },\n };\n },\n middleware: async (ctx, next) => {\n if (!refresh || ctx.meta?.bearerAuth === false) return next(ctx);\n // A stream is read by the first try; there is nothing left to resend.\n if (isReadableStream(ctx.init.body)) return next(ctx);\n try {\n return await next(ctx);\n } catch (error) {\n if (!isArthronError(error) || error.status !== 401) throw error;\n if (!ours(error.request.url)) throw error;\n // Refresh only if the token that failed is still the current one —\n // otherwise another call has refreshed it already.\n if (error.request.init.headers.authorization === (await bearer())) {\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n await refreshing;\n }\n return next(ctx);\n }\n },\n };\n}\n","/**\n * A new Response with `body` but everything else taken from `original`. The\n * constructor resets `url`, `redirected` and `type`, so those are copied over\n * as own properties — callers that resolve relative links or detect\n * redirects keep working.\n */\nexport function rebuildResponse(\n original: Response,\n body: BodyInit | null,\n): Response {\n const rebuilt = new Response(body, {\n status: original.status,\n statusText: original.statusText,\n headers: original.headers,\n });\n return withFetchedFields(rebuilt, original);\n}\n\n/** Copy the fields the constructor resets — onto `clone()`s as well. */\nfunction withFetchedFields(target: Response, from: Response): Response {\n const clone = target.clone.bind(target);\n return Object.defineProperties(target, {\n url: { value: from.url },\n redirected: { value: from.redirected },\n type: { value: from.type },\n clone: { value: () => withFetchedFields(clone(), from) },\n });\n}\n","import { isReadableStream } from \"../request/body\";\nimport { redactUrl, redactUrlIn } from \"../request/url\";\nimport { rebuildResponse } from \"../response/rebuild-response\";\nimport type { RequestContext } from \"../types/request.types\";\nimport type { ErrorDetailResolver } from \"../types/response.types\";\nimport { ArthronError } from \"./arthron-error\";\n\n/**\n * Cancellation is not an HTTP failure. Matched by name so it works across\n * realms — or by the request's own `signal`, since `abort(reason)` rejects\n * with whatever reason the caller chose.\n */\nexport function isCancellation(\n error: unknown,\n signal?: AbortSignal | null,\n): boolean {\n if (signal?.aborted) return true;\n const name = (error as { name?: unknown } | null | undefined)?.name;\n return name === \"AbortError\" || name === \"TimeoutError\";\n}\n\n/**\n * What a cancelled call rejects with: its signal's reason. WebKit's fetch\n * rejects, and errors a half-read body, with a generic AbortError instead,\n * dropping a TimeoutError or the caller's own reason.\n */\nexport function cancellationError(\n error: unknown,\n signal?: AbortSignal | null,\n): unknown {\n return signal?.aborted ? signal.reason : error;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/** `message` → `error` (string) → `error.message` → `detail`. */\nexport function defaultErrorDetail(data: unknown): string | undefined {\n if (!isRecord(data)) return undefined;\n if (typeof data.message === \"string\") return data.message;\n if (typeof data.error === \"string\") return data.error;\n if (isRecord(data.error) && typeof data.error.message === \"string\") {\n return data.error.message;\n }\n if (typeof data.detail === \"string\") return data.detail;\n return undefined;\n}\n\n/** How much of an error body arthron reads. Error bodies are for diagnosis; a hostile or broken server must not be able to fill memory with one. */\nconst MAX_ERROR_BODY_BYTES = 64 * 1024;\n\n/** Up to `limit` bytes of the body; the rest is cancelled unread. `undefined` when there is no body or reading it failed. */\nasync function readCapped(\n response: Response,\n limit: number,\n): Promise<{ bytes: Uint8Array<ArrayBuffer>; truncated: boolean } | undefined> {\n if (!response.body) return undefined;\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n const chunks: Uint8Array[] = [];\n let size = 0;\n try {\n // Inside the try: a body that is already locked or used counts as unreadable.\n reader = response.body.getReader();\n while (size <= limit) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n size += value.byteLength;\n }\n } catch {\n return undefined;\n } finally {\n await reader?.cancel().catch(() => {});\n reader?.releaseLock();\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return size > limit\n ? { bytes: bytes.subarray(0, limit), truncated: true }\n : { bytes, truncated: false };\n}\n\n/** JSON if the whole body parses, else the text (cut at the limit), else `undefined` when empty. */\nfunction parseBody(read: {\n bytes: Uint8Array<ArrayBuffer>;\n truncated: boolean;\n}): unknown {\n const text = new TextDecoder().decode(read.bytes);\n if (text === \"\") return undefined;\n if (read.truncated) return text;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\n/** The request as error messages name it: method and URL, no query or userinfo. */\nfunction describeRequest(request: RequestContext): string {\n return `${request.init.method ?? \"GET\"} ${redactUrl(request.url)}`;\n}\n\nexport async function responseError(\n response: Response,\n request: RequestContext,\n resolve: ErrorDetailResolver = defaultErrorDetail,\n): Promise<ArthronError> {\n const read = await readCapped(response, MAX_ERROR_BODY_BYTES);\n const data = read && parseBody(read);\n // The body was consumed above; hand the caller a Response over what was read.\n if (read) response = rebuildResponse(response, read.bytes);\n const status = response.statusText\n ? `${response.status} ${response.statusText}`\n : String(response.status);\n return new ArthronError({\n // arthron's own words for logs; the server's go in `detail`.\n message: `${describeRequest(request)} failed with ${status}`,\n detail: resolve(data, response) || undefined,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n data,\n request,\n response,\n });\n}\n\n/**\n * Browsers that stream a request body (Chromium, current WebKit) do it only\n * over HTTP/2, and fail one sent to an HTTP/1.1 server with the same bare\n * \"Failed to fetch\" / \"Load failed\" as a dead server, so name the likely cause.\n */\nconst STREAM_BODY_HINT =\n \" — in a browser, a ReadableStream body streams only over HTTP/2; send a Blob or ArrayBuffer to an HTTP/1.1 server\";\n\nexport function networkError(\n cause: unknown,\n request: RequestContext,\n): ArthronError {\n // Some runtimes' messages repeat the URL, query and userinfo included.\n const reason =\n cause instanceof Error && cause.message\n ? redactUrlIn(cause.message, request.url)\n : \"network error\";\n const hint = isReadableStream(request.init.body) ? STREAM_BODY_HINT : \"\";\n return new ArthronError({\n message: `${describeRequest(request)} failed: ${reason}${hint}`,\n status: 0,\n statusText: \"\",\n headers: new Headers(),\n data: undefined,\n request,\n cause,\n });\n}\n","import { isArthronError } from \"../../error/arthron-error\";\nimport { isCancellation } from \"../../error/create-error\";\nimport { redactUrl } from \"../../request/url\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { LogEvent, LoggingOptions } from \"./logging.types\";\n\nfunction consoleSink(logger: Pick<Console, \"log\" | \"error\">) {\n return (e: LogEvent) => {\n if (e.phase === \"start\") {\n logger.log(`[arthron] → ${e.method} ${e.path}`);\n } else if (e.phase === \"success\") {\n logger.log(`[arthron] ← ${e.method} ${e.path} ${e.status} ${e.ms}ms`);\n } else {\n logger.error(\n `[arthron] ✕ ${e.method} ${e.path} ${e.status ?? \"-\"} ${e.ms}ms`,\n );\n }\n };\n}\n\n/**\n * One event when a request starts, one when it settles, with status and\n * elapsed time. Wraps the whole call, so retries are included in the time.\n * Default output is a console line per event; pass `onEvent` to get the\n * structured event instead. Cancellations are not reported.\n */\nexport function logging(options: LoggingOptions = {}): Plugin {\n const {\n enabled = true,\n logger = console,\n onEvent,\n now = () => performance.now(),\n } = options;\n const emit = onEvent ?? consoleSink(logger);\n return {\n name: \"logging\",\n middleware: async (ctx, next) => {\n if (!enabled) return next(ctx);\n const method = ctx.init.method ?? \"GET\";\n const path = redactUrl(ctx.url);\n const started = now();\n const ms = () => Math.round(now() - started);\n\n emit({ phase: \"start\", method, path });\n let res: Response;\n try {\n res = await next(ctx);\n } catch (error) {\n if (!isCancellation(error, ctx.init.signal)) {\n const status = isArthronError(error) ? error.status : undefined;\n emit({ phase: \"error\", method, path, status, ms: ms(), error });\n }\n throw error;\n }\n // Outside the try: a sink that throws is a plugin bug, not a failed request.\n emit({ phase: \"success\", method, path, status: res.status, ms: ms() });\n return res;\n },\n };\n}\n","import { type ArthronError, isArthronError } from \"../../error/arthron-error\";\nimport { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { RetryOptions } from \"../../types/request.types\";\n\nconst DEFAULT_RETRY_ON = [408, 429, 500, 502, 503, 504] as const;\nconst DEFAULT_METHODS = [\n \"GET\",\n \"HEAD\",\n \"OPTIONS\",\n \"QUERY\",\n \"PUT\",\n \"DELETE\",\n] as const;\nconst DEFAULT_MAX_RETRY_AFTER = 60_000;\n/** setTimeout fires at once past this, so every wait is capped to it. */\nconst MAX_TIMER_MS = 2 ** 31 - 1;\n\nfunction defaultSleep(ms: number, signal?: AbortSignal | null): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/** `Retry-After` as ms — seconds or an HTTP-date. `undefined` when absent or unparsable. */\nfunction retryAfterMs(error: ArthronError): number | undefined {\n const value = error.headers.get(\"retry-after\")?.trim();\n if (!value) return undefined;\n const seconds = Number(value);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const at = Date.parse(value);\n if (Number.isNaN(at)) return undefined;\n return Math.max(0, at - Date.now());\n}\n\n/** The rule without `shouldRetry`: a network error or a `retryOn` status, on a method in `methods`. */\nfunction byDefault(\n thrown: unknown,\n method: string,\n settings: Settings,\n): boolean {\n if (!settings.allowed.has(method) || !isArthronError(thrown)) return false;\n return thrown.status === 0 || settings.retryOn.includes(thrown.status);\n}\n\n/** `RetryOptions` with defaults filled in, ready for one call. */\ntype Settings = {\n retries: number;\n retryOn: readonly number[];\n allowed: Set<string>;\n delay: (retry: number, error: unknown) => number;\n maxRetryAfter: number;\n shouldRetry: RetryOptions[\"shouldRetry\"];\n onRetry: RetryOptions[\"onRetry\"];\n};\n\n/** Defaults filled in and `retries` checked — `attempt >= NaN` is never true, so an unset env var would retry forever. */\nfunction settle(options: Omit<RetryOptions, \"sleep\">, where: string): Settings {\n const {\n retries = 2,\n retryOn = DEFAULT_RETRY_ON,\n methods = DEFAULT_METHODS,\n delay = (retry) => Math.random() * 300 * 2 ** retry,\n maxRetryAfter = DEFAULT_MAX_RETRY_AFTER,\n shouldRetry,\n onRetry,\n } = options;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new TypeError(\n `arthron: ${where} retries: ${retries} needs a whole number ≥ 0 — e.g. retries: 2`,\n );\n }\n const allowed = new Set(methods.map((m) => m.toUpperCase()));\n return {\n retries,\n retryOn,\n allowed,\n delay,\n maxRetryAfter,\n shouldRetry,\n onRetry,\n };\n}\n\n/**\n * Retry on network errors and the configured statuses by calling `next`\n * again — the whole attempt (request interceptors included) is re-run.\n * Only `ArthronError` is considered; cancellation and plugin bugs pass through.\n * A call can turn it off or override its options with `meta.retry`.\n */\nexport function retry(options: RetryOptions = {}): Plugin {\n const { sleep = defaultSleep, ...rest } = options;\n const base = settle(rest, \"retry()\");\n\n return {\n name: \"retry\",\n middleware: async (ctx, next) => {\n const override = ctx.meta?.retry;\n if (override === false) return next(ctx);\n const settings = override\n ? settle({ ...rest, ...override }, \"meta.retry\")\n : base;\n const method = (ctx.init.method ?? \"GET\").toUpperCase();\n const resendable = !isReadableStream(ctx.init.body);\n\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await next(ctx);\n } catch (thrown) {\n // Not even shouldRetry can lift these.\n if (!resendable || attempt >= settings.retries) throw thrown;\n if (ctx.init.signal?.aborted) throw thrown;\n const verdict = settings.shouldRetry?.(thrown, attempt, ctx);\n if (verdict === false) throw thrown;\n if (verdict === undefined && !byDefault(thrown, method, settings)) {\n throw thrown;\n }\n const retryAfter = isArthronError(thrown)\n ? retryAfterMs(thrown)\n : undefined;\n if (retryAfter !== undefined && retryAfter > settings.maxRetryAfter) {\n throw thrown;\n }\n const wait = Math.min(\n retryAfter ?? settings.delay(attempt, thrown),\n MAX_TIMER_MS,\n );\n settings.onRetry?.(thrown, attempt, wait);\n await sleep(wait, ctx.init.signal);\n }\n }\n },\n };\n}\n","import { rebuildResponse } from \"../../response/rebuild-response\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { TimeoutOptions } from \"./timeout.types\";\n\nconst MAX_TIMER_MS = 2 ** 31 - 1;\n\n/**\n * Wrap `body` so the deadline keeps running until it is fully read, and so an\n * abort while reading surfaces as a stream error instead of a silent stall.\n */\nfunction guardBody(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n finish: () => void,\n onChunk: () => void,\n): ReadableStream<Uint8Array> {\n const reader = body.getReader();\n return new ReadableStream<Uint8Array>({\n start(controller) {\n signal.addEventListener(\n \"abort\",\n () => {\n controller.error(signal.reason);\n reader.cancel(signal.reason).catch(() => {});\n },\n { once: true },\n );\n },\n async pull(controller) {\n const { value, done } = await reader.read();\n if (done) {\n finish();\n controller.close();\n return;\n }\n onChunk();\n controller.enqueue(value);\n },\n cancel(reason) {\n finish();\n return reader.cancel(reason);\n },\n });\n}\n\n/** setTimeout fires at once for NaN, negatives and anything past 2^31-1. */\nfunction checkDeadline(ms: number, where: string): void {\n if (ms === Number.POSITIVE_INFINITY || (ms >= 0 && ms <= MAX_TIMER_MS))\n return;\n throw new TypeError(\n `arthron: ${where} needs 0–${MAX_TIMER_MS} ms — pass Infinity for no deadline`,\n );\n}\n\n/** A timer that aborts `controller` with a TimeoutError and never keeps Node alive on its own. */\nfunction deadlineTimer(\n controller: AbortController,\n ms: number,\n message: string,\n): ReturnType<typeof setTimeout> {\n const timer = setTimeout(\n () => controller.abort(new DOMException(message, \"TimeoutError\")),\n ms,\n );\n (timer as { unref?: () => unknown }).unref?.();\n return timer;\n}\n\n/** `timeout(ms)`, `timeout({ total, idle })` or the deprecated `timeout(ms, { idle })`, checked. */\nfunction settle(\n first: number | TimeoutOptions,\n second: Pick<TimeoutOptions, \"idle\">,\n): { ms: number; idle: number | undefined } {\n if (typeof first === \"number\") {\n checkDeadline(first, `timeout(${first})`);\n const { idle } = second;\n if (idle !== undefined) {\n checkDeadline(idle, `timeout(…, { idle: ${idle} })`);\n }\n return { ms: first, idle };\n }\n const { total, idle } = first;\n if (total === undefined && idle === undefined) {\n throw new TypeError(\n \"arthron: timeout({}) needs total or idle — e.g. timeout({ total: 10_000 }) or timeout({ idle: 30_000 })\",\n );\n }\n if (total !== undefined) checkDeadline(total, `timeout({ total: ${total} })`);\n if (idle !== undefined) checkDeadline(idle, `timeout({ idle: ${idle} })`);\n return { ms: total ?? Number.POSITIVE_INFINITY, idle };\n}\n\n/**\n * Abort the request when it takes longer than `ms` — counted from the start\n * of the attempt until the body has been fully read, so a server that sends\n * headers and then stalls still trips it. `timeout({ idle })` instead aborts\n * once no data has arrived for that long, and never cuts off a stream that\n * keeps flowing; `timeout({ total, idle })` does both. Rejects with a\n * `TimeoutError` (a `DOMException`, like `AbortSignal.timeout()` would give);\n * it is not an `ArthronError`, so error interceptors and `retry` leave it\n * alone.\n *\n * A call can change the total deadline with `meta.timeout`.\n *\n * Deadlines are timers on an AbortController — not `AbortSignal.timeout`,\n * since the idle one restarts — joined to the caller's signal with\n * `AbortSignal.any`.\n */\nexport function timeout(ms: number): Plugin;\nexport function timeout(options: TimeoutOptions): Plugin;\n/** @deprecated Use `timeout({ total: ms, idle })`; this form goes in the next major. */\nexport function timeout(\n ms: number,\n options: Pick<TimeoutOptions, \"idle\">,\n): Plugin;\nexport function timeout(\n first: number | TimeoutOptions,\n second: Pick<TimeoutOptions, \"idle\"> = {},\n): Plugin {\n const { ms, idle } = settle(first, second);\n return {\n name: \"timeout\",\n middleware: async (ctx, next) => {\n const deadline = ctx.meta?.timeout ?? ms;\n if (deadline !== ms) checkDeadline(deadline, `meta.timeout: ${deadline}`);\n const idleMs = idle === Number.POSITIVE_INFINITY ? undefined : idle;\n if (deadline === Number.POSITIVE_INFINITY && idleMs === undefined) {\n return next(ctx);\n }\n const controller = new AbortController();\n const outer = ctx.init.signal;\n // The caller's abort and our deadlines, as one signal.\n const signal = outer\n ? AbortSignal.any([outer, controller.signal])\n : controller.signal;\n\n const total =\n deadline === Number.POSITIVE_INFINITY\n ? undefined\n : deadlineTimer(\n controller,\n deadline,\n `arthron: request timed out after ${deadline}ms`,\n );\n let quiet: ReturnType<typeof setTimeout> | undefined;\n // Restarted whenever something arrives: the headers, then every chunk.\n const stillAlive = () => {\n if (idleMs === undefined) return;\n clearTimeout(quiet);\n quiet = deadlineTimer(\n controller,\n idleMs,\n `arthron: no data for ${idleMs}ms`,\n );\n };\n const finish = () => {\n clearTimeout(total);\n clearTimeout(quiet);\n };\n stillAlive();\n\n let res: Response;\n try {\n res = await next({\n ...ctx,\n init: { ...ctx.init, signal },\n });\n } catch (thrown) {\n finish();\n throw thrown;\n }\n if (!res.body) {\n finish();\n return res;\n }\n stillAlive();\n return rebuildResponse(\n res,\n guardBody(res.body, signal, finish, stillAlive),\n );\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins-ChNZ2Q8t.mjs","names":["originOf","MAX_TIMER_MS","settle"],"sources":["../src/request/url.ts","../src/error/arthron-error.ts","../src/request/body.ts","../src/plugins/bearer-auth/bearer-auth.ts","../src/response/rebuild-response.ts","../src/error/create-error.ts","../src/plugins/logging/logging.ts","../src/plugins/retry/retry.ts","../src/plugins/timeout/timeout.ts"],"sourcesContent":["import type { QueryParams } from \"../types/request.types\";\n\nconst ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\\/\\//i;\nconst HTTP_URL = /^https?:\\/\\//i;\n\nexport function isAbsoluteUrl(path: string): boolean {\n return ABSOLUTE_URL.test(path);\n}\n\n/**\n * Origin + pathname only — the query and userinfo tend to carry secrets.\n * For anything printed: log lines, error messages, `ArthronError.toJSON`.\n */\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.origin}${parsed.pathname}`;\n } catch {\n return url.split(/[?#]/)[0] ?? url;\n }\n}\n\n/**\n * `text` with `url` redacted wherever it appears, as given or as the URL\n * parser normalizes it — for a runtime's own message that repeats the URL.\n */\nexport function redactUrlIn(text: string, url: string): string {\n const shown = redactUrl(url);\n let href: string | undefined;\n try {\n href = new URL(url).href;\n } catch {}\n let out = text.split(url).join(shown);\n if (href && href !== url) out = out.split(href).join(shown);\n return out;\n}\n\n/**\n * `undefined` when `url` has no origin of its own. A relative baseURL takes\n * the page's origin in a browser and has none elsewhere.\n */\nfunction originOf(url: string): string | undefined {\n const page = (globalThis as { location?: { href?: string } }).location?.href;\n try {\n return new URL(url, page).origin;\n } catch {\n return undefined;\n }\n}\n\n/** `path` is absolute and points away from `baseURL`'s origin. */\nexport function leavesBase(baseURL: string | undefined, path: string): boolean {\n if (baseURL === undefined || !isAbsoluteUrl(path)) return false;\n const base = originOf(baseURL);\n return base === undefined || originOf(path) !== base;\n}\n\nfunction toQueryString(params: QueryParams | undefined): string {\n if (!params) return \"\";\n if (Object.prototype.toString.call(params) === \"[object URLSearchParams]\") {\n return params.toString();\n }\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (item === null || item === undefined) continue;\n // `String({})` is \"[object Object]\" and a Date prints in local time —\n // neither is what a server expects.\n if (typeof item === \"object\" || typeof item === \"function\") {\n throw new TypeError(\n `arthron: params.${key} is not a string, number or boolean — pass one of those or an array of them (dates: date.toISOString())`,\n );\n }\n search.append(key, String(item));\n }\n }\n return search.toString();\n}\n\n/**\n * Fill `{name}` placeholders with encoded values, so each one stays inside\n * its segment: \"/\", \"?\" and \"#\" are encoded, and the values WHATWG URL would\n * turn into a different path — \"\", \".\" and \"..\" — are refused. A placeholder\n * without a value, or a value without a placeholder, is a mistake too.\n */\nexport function fillPath(\n template: string,\n values: Record<string, string | number>,\n): string {\n const used = new Set<string>();\n const filled = template.replace(/\\{([^{}/]+)\\}/g, (_, name: string) => {\n if (!Object.hasOwn(values, name)) {\n throw new TypeError(\n `arthron: path \"${template}\" has {${name}} but no value for it — pass path: { ${name}: … }`,\n );\n }\n used.add(name);\n const value = String(values[name]);\n if (value === \"\" || value === \".\" || value === \"..\") {\n throw new TypeError(\n `arthron: path value ${name}=${JSON.stringify(value)} would change the path, not fill a segment — pass a real id`,\n );\n }\n return encodeURIComponent(value);\n });\n const unused = Object.keys(values).find((name) => !used.has(name));\n if (unused !== undefined) {\n throw new TypeError(\n `arthron: path \"${template}\" has no {${unused}} — add the placeholder or drop the value`,\n );\n }\n return filled;\n}\n\n/**\n * Join `baseURL` and `path` by plain string concatenation so a path prefix in\n * the base (`/v1`) survives a leading slash in the path. WHATWG `new URL()`\n * would drop it. An absolute path replaces the base only when it stays on the\n * base's origin, or when `allowAbsoluteUrls` says other origins are fine — a\n * path built from user input or an API response must not carry the client's\n * credentials somewhere else.\n */\nexport function resolveUrl(\n baseURL: string | undefined,\n path: string,\n params?: QueryParams,\n allowAbsoluteUrls = false,\n): string {\n let url: string;\n if (isAbsoluteUrl(path)) {\n if (!HTTP_URL.test(path)) {\n const scheme = path.slice(0, path.indexOf(\":\") + 1);\n throw new TypeError(\n `arthron: only http(s) URLs can be requested, got a \"${scheme}\" URL — use an http:// or https:// URL`,\n );\n }\n if (!allowAbsoluteUrls && leavesBase(baseURL, path)) {\n throw new TypeError(\n `arthron: ${originOf(path)} is outside baseURL ${(baseURL && originOf(baseURL)) ?? baseURL} — pass a path relative to baseURL, or set arthron({ allowAbsoluteUrls: true }) to allow other origins (config headers are not sent to them)`,\n );\n }\n url = path;\n } else {\n if (baseURL === undefined) {\n throw new TypeError(\n `arthron: relative path \"${redactUrl(path)}\" needs a baseURL — pass baseURL in arthron({ baseURL }) or use an absolute URL`,\n );\n }\n url = `${baseURL.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n }\n const query = toQueryString(params);\n if (!query) return url;\n // The query goes before any #fragment, or the server never sees it.\n const hash = url.indexOf(\"#\");\n const [base, fragment] =\n hash === -1 ? [url, \"\"] : [url.slice(0, hash), url.slice(hash)];\n return `${base}${base.includes(\"?\") ? \"&\" : \"?\"}${query}${fragment}`;\n}\n","import { redactUrl } from \"../request/url\";\nimport type { RequestContext } from \"../types/request.types\";\n\ntype ArthronErrorInit = {\n message: string;\n status: number;\n statusText: string;\n headers: Headers;\n data: unknown;\n detail?: string | undefined;\n request: RequestContext;\n response?: Response;\n cause?: unknown;\n};\n\n/**\n * Marks an ArthronError across copies of this package. The ESM and CJS\n * builds can both end up loaded (a CJS dependency creates the client, ESM\n * app code catches), each with its own class — `Symbol.for` is shared.\n */\nconst BRAND = Symbol.for(\"arthron.ArthronError\");\n\n/**\n * The only error arthron itself produces. Two sources: a response with\n * `ok === false`, or fetch rejecting outright (network failure, `status` 0).\n * Everything else — cancellation, interceptor bugs — is rethrown untouched.\n */\nexport class ArthronError extends Error {\n override readonly name = \"ArthronError\";\n /** HTTP status of the failed response; `0` when fetch itself failed (network). */\n readonly status: number;\n readonly statusText: string;\n /** Response headers; empty for network failures. Not enumerable — kept out of logs. */\n declare readonly headers: Headers;\n /** Parsed body: JSON if it parses, else the text, else `undefined`. */\n readonly data: unknown;\n /**\n * The server's own explanation, picked out of `data` (by default\n * `message` → `error` → `error.message` → `detail`; see the `errorDetail`\n * config). `undefined` when the body had none. Untrusted server text: show\n * it to users as text, never as HTML. `message`, by contrast, is written by\n * arthron — method, URL without query, status — and is meant for logs.\n */\n readonly detail: string | undefined;\n /**\n * The context that was sent — url and init after request interceptors.\n * Not enumerable: it holds credentials, so `JSON.stringify`, `console.error`\n * and error trackers leave it out. Read it directly when you need it.\n */\n declare readonly request: RequestContext;\n /** The failed Response, still readable — its body holds what arthron read (at most 64 KiB). Absent for network failures. Not enumerable. */\n declare readonly response: Response | undefined;\n /** The underlying error for network failures. */\n override readonly cause: unknown;\n\n constructor(init: ArthronErrorInit) {\n super(init.message);\n this.status = init.status;\n this.statusText = init.statusText;\n this.data = init.data;\n this.detail = init.detail;\n this.cause = init.cause;\n Object.defineProperties(this, {\n headers: { value: init.headers },\n request: { value: init.request },\n response: { value: init.response },\n [BRAND]: { value: true },\n });\n }\n\n /** `instanceof ArthronError` holds for an error from any copy of the package. */\n static override [Symbol.hasInstance](value: unknown): boolean {\n return this === ArthronError\n ? isArthronError(value)\n : Function.prototype[Symbol.hasInstance].call(this, value);\n }\n\n /** What serializers see: no headers, no body, no query or userinfo. */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n detail: this.detail,\n status: this.status,\n statusText: this.statusText,\n method: this.request.init.method ?? \"GET\",\n url: redactUrl(this.request.url),\n data: this.data,\n };\n }\n}\n\n/** `true` for errors arthron produced: a `!ok` response or a network failure. Cancellations and plugin bugs are not ArthronErrors. */\nexport function isArthronError(error: unknown): error is ArthronError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { [BRAND]?: unknown })[BRAND] === true\n );\n}\n","type Serialized = {\n body: BodyInit | undefined;\n headers: Record<string, string>;\n};\n\nconst RAW_TAGS = new Set([\n \"FormData\",\n \"Blob\",\n \"File\",\n \"ArrayBuffer\",\n \"URLSearchParams\",\n \"ReadableStream\",\n]);\n\n/**\n * `Symbol.toStringTag`, not `instanceof`: a body from another realm or a\n * polyfill (formdata-node, fetch-blob) must not be JSON-encoded into `{}`.\n */\nconst tagOf = (value: unknown): string =>\n Object.prototype.toString.call(value).slice(8, -1);\n\nexport function isReadableStream(value: unknown): value is ReadableStream {\n return tagOf(value) === \"ReadableStream\";\n}\n\n/**\n * Whether `RequestCtor` can send a ReadableStream body. Node and Chromium\n * read `duplex` and leave the stream alone; Firefox ignores `duplex` and\n * would send the text \"[object ReadableStream]\"; older Safari refuses it.\n * The check from\n * https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests\n */\nexport function canStreamRequestBody(RequestCtor: typeof Request): boolean {\n let duplexRead = false;\n try {\n const request = new RequestCtor(\"https://stream.test\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexRead = true;\n return \"half\";\n },\n } as RequestInit);\n return duplexRead && !request.headers.has(\"content-type\");\n } catch {\n return false;\n }\n}\n\nlet runtimeStreams: boolean | undefined;\n\n/** `canStreamRequestBody` for the global `Request`, checked once. */\nexport function runtimeStreamsRequestBody(): boolean {\n runtimeStreams ??=\n typeof Request !== \"function\" || canStreamRequestBody(Request);\n return runtimeStreams;\n}\n\n/**\n * What JSON.stringify would turn into `{}`, `undefined` or internal state\n * instead of the data — with the fix, so it reads as an error message.\n */\nfunction mangledByJson(body: unknown): string | undefined {\n if (typeof body === \"function\" || typeof body === \"symbol\") {\n return `a ${typeof body}, which JSON cannot carry — send its data instead`;\n }\n if (typeof body !== \"object\" || body === null || Array.isArray(body)) {\n return undefined;\n }\n if (Symbol.asyncIterator in body) {\n return \"an async iterable (a Node stream?) — pass a ReadableStream (Readable.toWeb(stream)) or collect it first\";\n }\n if (Symbol.iterator in body) {\n return \"an iterable such as a Map, Set or generator — convert it to an array or plain object first\";\n }\n return undefined;\n}\n\n/** Bodies fetch already knows how to send. Content-Type is left to the caller / runtime. */\nfunction isRawBody(body: unknown): body is BodyInit {\n if (typeof body === \"string\") return true;\n if (ArrayBuffer.isView(body)) return true;\n return RAW_TAGS.has(tagOf(body));\n}\n\n/**\n * Turn a caller-supplied body into something fetch accepts. Raw bodies pass\n * through untouched; anything else is JSON with `content-type` set — unless\n * the caller already set one (headers arrive lowercased, so a plain lookup is\n * enough).\n */\nexport function serializeBody(\n body: unknown,\n headers: Record<string, string>,\n): Serialized {\n if (body === undefined) return { body: undefined, headers };\n if (tagOf(body) === \"FormData\") {\n // The runtime must write `multipart/form-data; boundary=…` itself; any\n // content-type set here (a JSON default, typically) breaks the upload.\n const { \"content-type\": _dropped, ...rest } = headers;\n return { body: body as FormData, headers: rest };\n }\n if (isRawBody(body)) return { body, headers };\n const mangled = mangledByJson(body);\n if (mangled) {\n throw new TypeError(`arthron: the body is ${mangled}`);\n }\n const type = headers[\"content-type\"];\n if (type !== undefined && !/\\bjson\\b/i.test(type)) {\n throw new TypeError(\n `arthron: a ${typeof body} body is sent as JSON, but content-type is \"${type}\" — pass a string, URLSearchParams or FormData for that content type, or drop the header`,\n );\n }\n const withType =\n \"content-type\" in headers\n ? headers\n : { ...headers, \"content-type\": \"application/json\" };\n return { body: JSON.stringify(body), headers: withType };\n}\n","import { isArthronError } from \"../../error/arthron-error\";\nimport { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { BearerAuthOptions } from \"./bearer-auth.types\";\n\n/** The origin of an absolute http(s) URL; `undefined` for anything else. */\nfunction originOf(url: string): string | undefined {\n try {\n const { protocol, origin } = new URL(url);\n return protocol === \"http:\" || protocol === \"https:\" ? origin : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction allowedOrigins(origin: BearerAuthOptions[\"origin\"]): Set<string> {\n const urls = typeof origin === \"string\" ? [origin] : origin;\n if (urls.length === 0) {\n throw new TypeError(\n 'arthron: bearerAuth({ origin }) needs at least one URL — e.g. origin: \"https://api.example.com\"',\n );\n }\n return new Set(\n urls.map((url) => {\n const found = originOf(url);\n if (found) return found;\n throw new TypeError(\n `arthron: bearerAuth({ origin }) needs an http(s) URL, got ${JSON.stringify(url)} — e.g. origin: \"https://api.example.com\"`,\n );\n }),\n );\n}\n\n/**\n * Bearer-token auth. A request interceptor sets `authorization: Bearer\n * <token>` on every attempt to `origin`; with `refresh`, a middleware turns a\n * 401 into one refresh and one more try. Concurrent 401s share the refresh\n * (with refresh-token rotation, parallel refreshes revoke each other), and a\n * call whose token was replaced meanwhile just retries. A call opts out with\n * `meta: { bearerAuth: false }`.\n */\nexport function bearerAuth(options: BearerAuthOptions): Plugin {\n const { token, refresh } = options;\n const allowed = allowedOrigins(options.origin);\n const ours = (url: string) => allowed.has(originOf(url) ?? \"\");\n const bearer = async () => {\n const value = await token();\n return value ? `Bearer ${value}` : undefined;\n };\n let refreshing: Promise<void> | undefined;\n\n return {\n name: \"bearerAuth\",\n request: async (ctx) => {\n if (ctx.meta?.bearerAuth === false || !ours(ctx.url)) return ctx;\n const value = await bearer();\n if (!value) return ctx;\n return {\n ...ctx,\n init: {\n ...ctx.init,\n headers: { ...ctx.init.headers, authorization: value },\n },\n };\n },\n middleware: async (ctx, next) => {\n if (!refresh || ctx.meta?.bearerAuth === false) return next(ctx);\n // A stream is read by the first try; there is nothing left to resend.\n if (isReadableStream(ctx.init.body)) return next(ctx);\n try {\n return await next(ctx);\n } catch (error) {\n if (!isArthronError(error) || error.status !== 401) throw error;\n if (!ours(error.request.url)) throw error;\n // Refresh only if the token that failed is still the current one —\n // otherwise another call has refreshed it already.\n if (error.request.init.headers.authorization === (await bearer())) {\n refreshing ??= refresh().finally(() => {\n refreshing = undefined;\n });\n await refreshing;\n }\n return next(ctx);\n }\n },\n };\n}\n","/**\n * A new Response with `body` but everything else taken from `original`. The\n * constructor resets `url`, `redirected` and `type`, so those are copied over\n * as own properties — callers that resolve relative links or detect\n * redirects keep working.\n */\nexport function rebuildResponse(\n original: Response,\n body: BodyInit | null,\n): Response {\n const rebuilt = new Response(body, {\n status: original.status,\n statusText: original.statusText,\n headers: original.headers,\n });\n return withFetchedFields(rebuilt, original);\n}\n\n/** Copy the fields the constructor resets — onto `clone()`s as well. */\nfunction withFetchedFields(target: Response, from: Response): Response {\n const clone = target.clone.bind(target);\n return Object.defineProperties(target, {\n url: { value: from.url },\n redirected: { value: from.redirected },\n type: { value: from.type },\n clone: { value: () => withFetchedFields(clone(), from) },\n });\n}\n","import { isReadableStream } from \"../request/body\";\nimport { redactUrl, redactUrlIn } from \"../request/url\";\nimport { rebuildResponse } from \"../response/rebuild-response\";\nimport type { RequestContext } from \"../types/request.types\";\nimport type { ErrorDetailResolver } from \"../types/response.types\";\nimport { ArthronError } from \"./arthron-error\";\n\n/**\n * Cancellation is not an HTTP failure. Matched by name so it works across\n * realms — or by the request's own `signal`, since `abort(reason)` rejects\n * with whatever reason the caller chose.\n */\nexport function isCancellation(\n error: unknown,\n signal?: AbortSignal | null,\n): boolean {\n if (signal?.aborted) return true;\n const name = (error as { name?: unknown } | null | undefined)?.name;\n return name === \"AbortError\" || name === \"TimeoutError\";\n}\n\n/**\n * What a cancelled call rejects with: its signal's reason. WebKit's fetch\n * rejects, and errors a half-read body, with a generic AbortError instead,\n * dropping a TimeoutError or the caller's own reason.\n */\nexport function cancellationError(\n error: unknown,\n signal?: AbortSignal | null,\n): unknown {\n return signal?.aborted ? signal.reason : error;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\n/** `message` → `error` (string) → `error.message` → `detail`. */\nexport function defaultErrorDetail(data: unknown): string | undefined {\n if (!isRecord(data)) return undefined;\n if (typeof data.message === \"string\") return data.message;\n if (typeof data.error === \"string\") return data.error;\n if (isRecord(data.error) && typeof data.error.message === \"string\") {\n return data.error.message;\n }\n if (typeof data.detail === \"string\") return data.detail;\n return undefined;\n}\n\n/** How much of an error body arthron reads. Error bodies are for diagnosis; a hostile or broken server must not be able to fill memory with one. */\nconst MAX_ERROR_BODY_BYTES = 64 * 1024;\n\n/** Up to `limit` bytes of the body; the rest is cancelled unread. `undefined` when there is no body or reading it failed. */\nasync function readCapped(\n response: Response,\n limit: number,\n): Promise<{ bytes: Uint8Array<ArrayBuffer>; truncated: boolean } | undefined> {\n if (!response.body) return undefined;\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n const chunks: Uint8Array[] = [];\n let size = 0;\n try {\n // Inside the try: a body that is already locked or used counts as unreadable.\n reader = response.body.getReader();\n while (size <= limit) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n size += value.byteLength;\n }\n } catch {\n return undefined;\n } finally {\n await reader?.cancel().catch(() => {});\n reader?.releaseLock();\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return size > limit\n ? { bytes: bytes.subarray(0, limit), truncated: true }\n : { bytes, truncated: false };\n}\n\n/** JSON if the whole body parses, else the text (cut at the limit), else `undefined` when empty. */\nfunction parseBody(read: {\n bytes: Uint8Array<ArrayBuffer>;\n truncated: boolean;\n}): unknown {\n const text = new TextDecoder().decode(read.bytes);\n if (text === \"\") return undefined;\n if (read.truncated) return text;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\n/** The request as error messages name it: method and URL, no query or userinfo. */\nfunction describeRequest(request: RequestContext): string {\n return `${request.init.method ?? \"GET\"} ${redactUrl(request.url)}`;\n}\n\nexport async function responseError(\n response: Response,\n request: RequestContext,\n resolve: ErrorDetailResolver = defaultErrorDetail,\n): Promise<ArthronError> {\n const read = await readCapped(response, MAX_ERROR_BODY_BYTES);\n const data = read && parseBody(read);\n // The body was consumed above; hand the caller a Response over what was read.\n if (read) response = rebuildResponse(response, read.bytes);\n const status = response.statusText\n ? `${response.status} ${response.statusText}`\n : String(response.status);\n return new ArthronError({\n // arthron's own words for logs; the server's go in `detail`.\n message: `${describeRequest(request)} failed with ${status}`,\n detail: resolve(data, response) || undefined,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n data,\n request,\n response,\n });\n}\n\n/**\n * Browsers that stream a request body (Chromium, current WebKit) do it only\n * over HTTP/2, and fail one sent to an HTTP/1.1 server with the same bare\n * \"Failed to fetch\" / \"Load failed\" as a dead server, so name the likely cause.\n */\nconst STREAM_BODY_HINT =\n \" — in a browser, a ReadableStream body streams only over HTTP/2; send a Blob or ArrayBuffer to an HTTP/1.1 server\";\n\nexport function networkError(\n cause: unknown,\n request: RequestContext,\n): ArthronError {\n // Some runtimes' messages repeat the URL, query and userinfo included.\n const reason =\n cause instanceof Error && cause.message\n ? redactUrlIn(cause.message, request.url)\n : \"network error\";\n const hint = isReadableStream(request.init.body) ? STREAM_BODY_HINT : \"\";\n return new ArthronError({\n message: `${describeRequest(request)} failed: ${reason}${hint}`,\n status: 0,\n statusText: \"\",\n headers: new Headers(),\n data: undefined,\n request,\n cause,\n });\n}\n","import { isArthronError } from \"../../error/arthron-error\";\nimport { isCancellation } from \"../../error/create-error\";\nimport { redactUrl } from \"../../request/url\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { LogEvent, LoggingOptions } from \"./logging.types\";\n\nfunction consoleSink(logger: Pick<Console, \"log\" | \"error\">) {\n return (e: LogEvent) => {\n if (e.phase === \"start\") {\n logger.log(`[arthron] → ${e.method} ${e.path}`);\n } else if (e.phase === \"success\") {\n logger.log(`[arthron] ← ${e.method} ${e.path} ${e.status} ${e.ms}ms`);\n } else {\n logger.error(\n `[arthron] ✕ ${e.method} ${e.path} ${e.status ?? \"-\"} ${e.ms}ms`,\n );\n }\n };\n}\n\n/**\n * One event when a request starts, one when it settles, with status and\n * elapsed time. Wraps the whole call, so retries are included in the time.\n * Default output is a console line per event; pass `onEvent` to get the\n * structured event instead. Cancellations are not reported.\n */\nexport function logging(options: LoggingOptions = {}): Plugin {\n const {\n enabled = true,\n logger = console,\n onEvent,\n now = () => performance.now(),\n } = options;\n const emit = onEvent ?? consoleSink(logger);\n return {\n name: \"logging\",\n middleware: async (ctx, next) => {\n if (!enabled) return next(ctx);\n const method = ctx.init.method ?? \"GET\";\n const path = redactUrl(ctx.url);\n const started = now();\n const ms = () => Math.round(now() - started);\n\n emit({ phase: \"start\", method, path });\n let res: Response;\n try {\n res = await next(ctx);\n } catch (error) {\n if (!isCancellation(error, ctx.init.signal)) {\n const status = isArthronError(error) ? error.status : undefined;\n emit({ phase: \"error\", method, path, status, ms: ms(), error });\n }\n throw error;\n }\n // Outside the try: a sink that throws is a plugin bug, not a failed request.\n emit({ phase: \"success\", method, path, status: res.status, ms: ms() });\n return res;\n },\n };\n}\n","import { type ArthronError, isArthronError } from \"../../error/arthron-error\";\nimport { isReadableStream } from \"../../request/body\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { RetryOptions } from \"../../types/request.types\";\n\nconst DEFAULT_RETRY_ON = [408, 429, 500, 502, 503, 504] as const;\nconst DEFAULT_METHODS = [\n \"GET\",\n \"HEAD\",\n \"OPTIONS\",\n \"QUERY\",\n \"PUT\",\n \"DELETE\",\n] as const;\nconst DEFAULT_MAX_RETRY_AFTER = 60_000;\n/** setTimeout fires at once past this, so every wait is capped to it. */\nconst MAX_TIMER_MS = 2 ** 31 - 1;\n\nfunction defaultSleep(ms: number, signal?: AbortSignal | null): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/** `Retry-After` as ms — seconds or an HTTP-date. `undefined` when absent or unparsable. */\nfunction retryAfterMs(error: ArthronError): number | undefined {\n const value = error.headers.get(\"retry-after\")?.trim();\n if (!value) return undefined;\n const seconds = Number(value);\n if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1000);\n const at = Date.parse(value);\n if (Number.isNaN(at)) return undefined;\n return Math.max(0, at - Date.now());\n}\n\n/** The rule without `shouldRetry`: a network error or a `retryOn` status, on a method in `methods`. */\nfunction byDefault(\n thrown: unknown,\n method: string,\n settings: Settings,\n): boolean {\n if (!settings.allowed.has(method) || !isArthronError(thrown)) return false;\n return thrown.status === 0 || settings.retryOn.includes(thrown.status);\n}\n\n/** `RetryOptions` with defaults filled in, ready for one call. */\ntype Settings = {\n retries: number;\n retryOn: readonly number[];\n allowed: Set<string>;\n delay: (retry: number, error: unknown) => number;\n maxRetryAfter: number;\n shouldRetry: RetryOptions[\"shouldRetry\"];\n onRetry: RetryOptions[\"onRetry\"];\n};\n\n/** Defaults filled in and `retries` checked — `attempt >= NaN` is never true, so an unset env var would retry forever. */\nfunction settle(options: Omit<RetryOptions, \"sleep\">, where: string): Settings {\n const {\n retries = 2,\n retryOn = DEFAULT_RETRY_ON,\n methods = DEFAULT_METHODS,\n delay = (retry) => Math.random() * 300 * 2 ** retry,\n maxRetryAfter = DEFAULT_MAX_RETRY_AFTER,\n shouldRetry,\n onRetry,\n } = options;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new TypeError(\n `arthron: ${where} retries: ${retries} needs a whole number ≥ 0 — e.g. retries: 2`,\n );\n }\n const allowed = new Set(methods.map((m) => m.toUpperCase()));\n return {\n retries,\n retryOn,\n allowed,\n delay,\n maxRetryAfter,\n shouldRetry,\n onRetry,\n };\n}\n\n/**\n * Retry on network errors and the configured statuses by calling `next`\n * again — the whole attempt (request interceptors included) is re-run.\n * Only `ArthronError` is considered; cancellation and plugin bugs pass through.\n * A call can turn it off or override its options with `meta.retry`.\n */\nexport function retry(options: RetryOptions = {}): Plugin {\n const { sleep = defaultSleep, ...rest } = options;\n const base = settle(rest, \"retry()\");\n\n return {\n name: \"retry\",\n middleware: async (ctx, next) => {\n const override = ctx.meta?.retry;\n if (override === false) return next(ctx);\n const settings = override\n ? settle({ ...rest, ...override }, \"meta.retry\")\n : base;\n const method = (ctx.init.method ?? \"GET\").toUpperCase();\n const resendable = !isReadableStream(ctx.init.body);\n\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await next(ctx);\n } catch (thrown) {\n // Not even shouldRetry can lift these.\n if (!resendable || attempt >= settings.retries) throw thrown;\n if (ctx.init.signal?.aborted) throw thrown;\n const verdict = settings.shouldRetry?.(thrown, attempt, ctx);\n if (verdict === false) throw thrown;\n if (verdict === undefined && !byDefault(thrown, method, settings)) {\n throw thrown;\n }\n const retryAfter = isArthronError(thrown)\n ? retryAfterMs(thrown)\n : undefined;\n if (retryAfter !== undefined && retryAfter > settings.maxRetryAfter) {\n throw thrown;\n }\n const wait = Math.min(\n retryAfter ?? settings.delay(attempt, thrown),\n MAX_TIMER_MS,\n );\n settings.onRetry?.(thrown, attempt, wait);\n await sleep(wait, ctx.init.signal);\n }\n }\n },\n };\n}\n","import { rebuildResponse } from \"../../response/rebuild-response\";\nimport type { Plugin } from \"../../types/plugin.types\";\nimport type { TimeoutOptions } from \"./timeout.types\";\n\nconst MAX_TIMER_MS = 2 ** 31 - 1;\n\n/**\n * Wrap `body` so the deadline keeps running until it is fully read, and so an\n * abort while reading surfaces as a stream error instead of a silent stall.\n */\nfunction guardBody(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n finish: () => void,\n onChunk: () => void,\n): ReadableStream<Uint8Array> {\n const reader = body.getReader();\n return new ReadableStream<Uint8Array>({\n start(controller) {\n signal.addEventListener(\n \"abort\",\n () => {\n controller.error(signal.reason);\n reader.cancel(signal.reason).catch(() => {});\n },\n { once: true },\n );\n },\n async pull(controller) {\n const { value, done } = await reader.read();\n if (done) {\n finish();\n controller.close();\n return;\n }\n onChunk();\n controller.enqueue(value);\n },\n cancel(reason) {\n finish();\n return reader.cancel(reason);\n },\n });\n}\n\n/** setTimeout fires at once for NaN, negatives and anything past 2^31-1. */\nfunction checkDeadline(ms: number, where: string): void {\n if (ms === Number.POSITIVE_INFINITY || (ms >= 0 && ms <= MAX_TIMER_MS))\n return;\n throw new TypeError(\n `arthron: ${where} needs 0–${MAX_TIMER_MS} ms — pass Infinity for no deadline`,\n );\n}\n\n/** A timer that aborts `controller` with a TimeoutError and never keeps Node alive on its own. */\nfunction deadlineTimer(\n controller: AbortController,\n ms: number,\n message: string,\n): ReturnType<typeof setTimeout> {\n const timer = setTimeout(\n () => controller.abort(new DOMException(message, \"TimeoutError\")),\n ms,\n );\n (timer as { unref?: () => unknown }).unref?.();\n return timer;\n}\n\n/** `timeout(ms)`, `timeout({ total, idle })` or the deprecated `timeout(ms, { idle })`, checked. */\nfunction settle(\n first: number | TimeoutOptions,\n second: Pick<TimeoutOptions, \"idle\">,\n): { ms: number; idle: number | undefined } {\n if (typeof first === \"number\") {\n checkDeadline(first, `timeout(${first})`);\n const { idle } = second;\n if (idle !== undefined) {\n checkDeadline(idle, `timeout(…, { idle: ${idle} })`);\n }\n return { ms: first, idle };\n }\n const { total, idle } = first;\n if (total === undefined && idle === undefined) {\n throw new TypeError(\n \"arthron: timeout({}) needs total or idle — e.g. timeout({ total: 10_000 }) or timeout({ idle: 30_000 })\",\n );\n }\n if (total !== undefined) checkDeadline(total, `timeout({ total: ${total} })`);\n if (idle !== undefined) checkDeadline(idle, `timeout({ idle: ${idle} })`);\n return { ms: total ?? Number.POSITIVE_INFINITY, idle };\n}\n\n/**\n * Abort the request when it takes longer than `ms` — counted from the start\n * of the attempt until the body has been fully read, so a server that sends\n * headers and then stalls still trips it. `timeout({ idle })` instead aborts\n * once no data has arrived for that long, and never cuts off a stream that\n * keeps flowing; `timeout({ total, idle })` does both. Rejects with a\n * `TimeoutError` (a `DOMException`, like `AbortSignal.timeout()` would give);\n * it is not an `ArthronError`, so error interceptors and `retry` leave it\n * alone.\n *\n * A call can change the total deadline with `meta.timeout`.\n *\n * Deadlines are timers on an AbortController — not `AbortSignal.timeout`,\n * since the idle one restarts — joined to the caller's signal with\n * `AbortSignal.any`.\n */\nexport function timeout(ms: number): Plugin;\nexport function timeout(options: TimeoutOptions): Plugin;\n/** @deprecated Use `timeout({ total: ms, idle })`; this form goes in the next major. */\nexport function timeout(\n ms: number,\n options: Pick<TimeoutOptions, \"idle\">,\n): Plugin;\nexport function timeout(\n first: number | TimeoutOptions,\n second: Pick<TimeoutOptions, \"idle\"> = {},\n): Plugin {\n const { ms, idle } = settle(first, second);\n return {\n name: \"timeout\",\n middleware: async (ctx, next) => {\n const deadline = ctx.meta?.timeout ?? ms;\n if (deadline !== ms) checkDeadline(deadline, `meta.timeout: ${deadline}`);\n const idleMs = idle === Number.POSITIVE_INFINITY ? undefined : idle;\n if (deadline === Number.POSITIVE_INFINITY && idleMs === undefined) {\n return next(ctx);\n }\n const controller = new AbortController();\n const outer = ctx.init.signal;\n // The caller's abort and our deadlines, as one signal.\n const signal = outer\n ? AbortSignal.any([outer, controller.signal])\n : controller.signal;\n\n const total =\n deadline === Number.POSITIVE_INFINITY\n ? undefined\n : deadlineTimer(\n controller,\n deadline,\n `arthron: request timed out after ${deadline}ms`,\n );\n let quiet: ReturnType<typeof setTimeout> | undefined;\n // Restarted whenever something arrives: the headers, then every chunk.\n const stillAlive = () => {\n if (idleMs === undefined) return;\n clearTimeout(quiet);\n quiet = deadlineTimer(\n controller,\n idleMs,\n `arthron: no data for ${idleMs}ms`,\n );\n };\n const finish = () => {\n clearTimeout(total);\n clearTimeout(quiet);\n };\n stillAlive();\n\n let res: Response;\n try {\n res = await next({\n ...ctx,\n init: { ...ctx.init, signal },\n });\n } catch (thrown) {\n finish();\n throw thrown;\n }\n if (!res.body) {\n finish();\n return res;\n }\n stillAlive();\n return rebuildResponse(\n res,\n guardBody(res.body, signal, finish, stillAlive),\n );\n },\n };\n}\n"],"mappings":";AAEA,MAAM,eAAe;AACrB,MAAM,WAAW;AAEjB,SAAgB,cAAc,MAAuB;CACnD,OAAO,aAAa,KAAK,IAAI;AAC/B;;;;;AAMA,SAAgB,UAAU,KAAqB;CAC7C,IAAI;EACF,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,OAAO,GAAG,OAAO,SAAS,OAAO;CACnC,QAAQ;EACN,OAAO,IAAI,MAAM,MAAM,CAAC,CAAC,MAAM;CACjC;AACF;;;;;AAMA,SAAgB,YAAY,MAAc,KAAqB;CAC7D,MAAM,QAAQ,UAAU,GAAG;CAC3B,IAAI;CACJ,IAAI;EACF,OAAO,IAAI,IAAI,GAAG,CAAC,CAAC;CACtB,QAAQ,CAAC;CACT,IAAI,MAAM,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK;CACpC,IAAI,QAAQ,SAAS,KAAK,MAAM,IAAI,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK;CAC1D,OAAO;AACT;;;;;AAMA,SAASA,WAAS,KAAiC;CACjD,MAAM,OAAQ,WAAgD,UAAU;CACxE,IAAI;EACF,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC;CAC5B,QAAQ;EACN;CACF;AACF;;AAGA,SAAgB,WAAW,SAA6B,MAAuB;CAC7E,IAAI,YAAY,KAAA,KAAa,CAAC,cAAc,IAAI,GAAG,OAAO;CAC1D,MAAM,OAAOA,WAAS,OAAO;CAC7B,OAAO,SAAS,KAAA,KAAaA,WAAS,IAAI,MAAM;AAClD;AAEA,SAAS,cAAc,QAAyC;CAC9D,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,UAAU,SAAS,KAAK,MAAM,MAAM,4BAC7C,OAAO,OAAO,SAAS;CAEzB,MAAM,SAAS,IAAI,gBAAgB;CACnC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC9C,KAAK,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG;EACzD,IAAI,SAAS,QAAQ,SAAS,KAAA,GAAW;EAGzC,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,YAC9C,MAAM,IAAI,UACR,mBAAmB,IAAI,wGACzB;EAEF,OAAO,OAAO,KAAK,OAAO,IAAI,CAAC;CACjC;CAEF,OAAO,OAAO,SAAS;AACzB;;;;;;;AAQA,SAAgB,SACd,UACA,QACQ;CACR,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,SAAS,SAAS,QAAQ,mBAAmB,GAAG,SAAiB;EACrE,IAAI,CAAC,OAAO,OAAO,QAAQ,IAAI,GAC7B,MAAM,IAAI,UACR,kBAAkB,SAAS,SAAS,KAAK,uCAAuC,KAAK,MACvF;EAEF,KAAK,IAAI,IAAI;EACb,MAAM,QAAQ,OAAO,OAAO,KAAK;EACjC,IAAI,UAAU,MAAM,UAAU,OAAO,UAAU,MAC7C,MAAM,IAAI,UACR,uBAAuB,KAAK,GAAG,KAAK,UAAU,KAAK,EAAE,4DACvD;EAEF,OAAO,mBAAmB,KAAK;CACjC,CAAC;CACD,MAAM,SAAS,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC;CACjE,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,UACR,kBAAkB,SAAS,YAAY,OAAO,0CAChD;CAEF,OAAO;AACT;;;;;;;;;AAUA,SAAgB,WACd,SACA,MACA,QACA,oBAAoB,OACZ;CACR,IAAI;CACJ,IAAI,cAAc,IAAI,GAAG;EACvB,IAAI,CAAC,SAAS,KAAK,IAAI,GAAG;GACxB,MAAM,SAAS,KAAK,MAAM,GAAG,KAAK,QAAQ,GAAG,IAAI,CAAC;GAClD,MAAM,IAAI,UACR,uDAAuD,OAAO,uCAChE;EACF;EACA,IAAI,CAAC,qBAAqB,WAAW,SAAS,IAAI,GAChD,MAAM,IAAI,UACR,YAAYA,WAAS,IAAI,EAAE,uBAAuB,WAAWA,WAAS,OAAO,MAAM,QAAQ,6IAC7F;EAEF,MAAM;CACR,OAAO;EACL,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,UACR,2BAA2B,UAAU,IAAI,EAAE,gFAC7C;EAEF,MAAM,GAAG,QAAQ,QAAQ,QAAQ,EAAE,EAAE,GAAG,KAAK,QAAQ,QAAQ,EAAE;CACjE;CACA,MAAM,QAAQ,cAAc,MAAM;CAClC,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,OAAO,IAAI,QAAQ,GAAG;CAC5B,MAAM,CAAC,MAAM,YACX,SAAS,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,MAAM,IAAI,CAAC;CAChE,OAAO,GAAG,OAAO,KAAK,SAAS,GAAG,IAAI,MAAM,MAAM,QAAQ;AAC5D;;;;;;;;ACzIA,MAAM,QAAQ,OAAO,IAAI,sBAAsB;;;;;;AAO/C,IAAa,eAAb,MAAa,qBAAqB,MAAM;CACtC,OAAyB;;CAEzB;CACA;;CAIA;;;;;;;;CAQA;;CAUA;CAEA,YAAY,MAAwB;EAClC,MAAM,KAAK,OAAO;EAClB,KAAK,SAAS,KAAK;EACnB,KAAK,aAAa,KAAK;EACvB,KAAK,OAAO,KAAK;EACjB,KAAK,SAAS,KAAK;EACnB,KAAK,QAAQ,KAAK;EAClB,OAAO,iBAAiB,MAAM;GAC5B,SAAS,EAAE,OAAO,KAAK,QAAQ;GAC/B,SAAS,EAAE,OAAO,KAAK,QAAQ;GAC/B,UAAU,EAAE,OAAO,KAAK,SAAS;IAChC,QAAQ,EAAE,OAAO,KAAK;EACzB,CAAC;CACH;;CAGA,QAAiB,OAAO,aAAa,OAAyB;EAC5D,OAAO,SAAS,eACZ,eAAe,KAAK,IACpB,SAAS,UAAU,OAAO,YAAY,CAAC,KAAK,MAAM,KAAK;CAC7D;;CAGA,SAAS;EACP,OAAO;GACL,MAAM,KAAK;GACX,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,YAAY,KAAK;GACjB,QAAQ,KAAK,QAAQ,KAAK,UAAU;GACpC,KAAK,UAAU,KAAK,QAAQ,GAAG;GAC/B,MAAM,KAAK;EACb;CACF;AACF;;AAGA,SAAgB,eAAe,OAAuC;CACpE,OACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAgC,WAAW;AAEhD;;;AC9FA,MAAM,2BAAW,IAAI,IAAI;CACvB;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;AAMD,MAAM,SAAS,UACb,OAAO,UAAU,SAAS,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAEnD,SAAgB,iBAAiB,OAAyC;CACxE,OAAO,MAAM,KAAK,MAAM;AAC1B;;;;;;;;AASA,SAAgB,qBAAqB,aAAsC;CACzE,IAAI,aAAa;CACjB,IAAI;EACF,MAAM,UAAU,IAAI,YAAY,uBAAuB;GACrD,QAAQ;GACR,MAAM,IAAI,eAAe;GACzB,IAAI,SAAS;IACX,aAAa;IACb,OAAO;GACT;EACF,CAAgB;EAChB,OAAO,cAAc,CAAC,QAAQ,QAAQ,IAAI,cAAc;CAC1D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,IAAI;;AAGJ,SAAgB,4BAAqC;CACnD,mBACE,OAAO,YAAY,cAAc,qBAAqB,OAAO;CAC/D,OAAO;AACT;;;;;AAMA,SAAS,cAAc,MAAmC;CACxD,IAAI,OAAO,SAAS,cAAc,OAAO,SAAS,UAChD,OAAO,KAAK,OAAO,KAAK;CAE1B,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,GACjE;CAEF,IAAI,OAAO,iBAAiB,MAC1B,OAAO;CAET,IAAI,OAAO,YAAY,MACrB,OAAO;AAGX;;AAGA,SAAS,UAAU,MAAiC;CAClD,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,YAAY,OAAO,IAAI,GAAG,OAAO;CACrC,OAAO,SAAS,IAAI,MAAM,IAAI,CAAC;AACjC;;;;;;;AAQA,SAAgB,cACd,MACA,SACY;CACZ,IAAI,SAAS,KAAA,GAAW,OAAO;EAAE,MAAM,KAAA;EAAW;CAAQ;CAC1D,IAAI,MAAM,IAAI,MAAM,YAAY;EAG9B,MAAM,EAAE,gBAAgB,UAAU,GAAG,SAAS;EAC9C,OAAO;GAAQ;GAAkB,SAAS;EAAK;CACjD;CACA,IAAI,UAAU,IAAI,GAAG,OAAO;EAAE;EAAM;CAAQ;CAC5C,MAAM,UAAU,cAAc,IAAI;CAClC,IAAI,SACF,MAAM,IAAI,UAAU,wBAAwB,SAAS;CAEvD,MAAM,OAAO,QAAQ;CACrB,IAAI,SAAS,KAAA,KAAa,CAAC,YAAY,KAAK,IAAI,GAC9C,MAAM,IAAI,UACR,cAAc,OAAO,KAAK,8CAA8C,KAAK,yFAC/E;CAEF,MAAM,WACJ,kBAAkB,UACd,UACA;EAAE,GAAG;EAAS,gBAAgB;CAAmB;CACvD,OAAO;EAAE,MAAM,KAAK,UAAU,IAAI;EAAG,SAAS;CAAS;AACzD;;;;AChHA,SAAS,SAAS,KAAiC;CACjD,IAAI;EACF,MAAM,EAAE,UAAU,WAAW,IAAI,IAAI,GAAG;EACxC,OAAO,aAAa,WAAW,aAAa,WAAW,SAAS,KAAA;CAClE,QAAQ;EACN;CACF;AACF;AAEA,SAAS,eAAe,QAAkD;CACxE,MAAM,OAAO,OAAO,WAAW,WAAW,CAAC,MAAM,IAAI;CACrD,IAAI,KAAK,WAAW,GAClB,MAAM,IAAI,UACR,mGACF;CAEF,OAAO,IAAI,IACT,KAAK,KAAK,QAAQ;EAChB,MAAM,QAAQ,SAAS,GAAG;EAC1B,IAAI,OAAO,OAAO;EAClB,MAAM,IAAI,UACR,6DAA6D,KAAK,UAAU,GAAG,EAAE,0CACnF;CACF,CAAC,CACH;AACF;;;;;;;;;AAUA,SAAgB,WAAW,SAAoC;CAC7D,MAAM,EAAE,OAAO,YAAY;CAC3B,MAAM,UAAU,eAAe,QAAQ,MAAM;CAC7C,MAAM,QAAQ,QAAgB,QAAQ,IAAI,SAAS,GAAG,KAAK,EAAE;CAC7D,MAAM,SAAS,YAAY;EACzB,MAAM,QAAQ,MAAM,MAAM;EAC1B,OAAO,QAAQ,UAAU,UAAU,KAAA;CACrC;CACA,IAAI;CAEJ,OAAO;EACL,MAAM;EACN,SAAS,OAAO,QAAQ;GACtB,IAAI,IAAI,MAAM,eAAe,SAAS,CAAC,KAAK,IAAI,GAAG,GAAG,OAAO;GAC7D,MAAM,QAAQ,MAAM,OAAO;GAC3B,IAAI,CAAC,OAAO,OAAO;GACnB,OAAO;IACL,GAAG;IACH,MAAM;KACJ,GAAG,IAAI;KACP,SAAS;MAAE,GAAG,IAAI,KAAK;MAAS,eAAe;KAAM;IACvD;GACF;EACF;EACA,YAAY,OAAO,KAAK,SAAS;GAC/B,IAAI,CAAC,WAAW,IAAI,MAAM,eAAe,OAAO,OAAO,KAAK,GAAG;GAE/D,IAAI,iBAAiB,IAAI,KAAK,IAAI,GAAG,OAAO,KAAK,GAAG;GACpD,IAAI;IACF,OAAO,MAAM,KAAK,GAAG;GACvB,SAAS,OAAO;IACd,IAAI,CAAC,eAAe,KAAK,KAAK,MAAM,WAAW,KAAK,MAAM;IAC1D,IAAI,CAAC,KAAK,MAAM,QAAQ,GAAG,GAAG,MAAM;IAGpC,IAAI,MAAM,QAAQ,KAAK,QAAQ,kBAAmB,MAAM,OAAO,GAAI;KACjE,eAAe,QAAQ,CAAC,CAAC,cAAc;MACrC,aAAa,KAAA;KACf,CAAC;KACD,MAAM;IACR;IACA,OAAO,KAAK,GAAG;GACjB;EACF;CACF;AACF;;;;;;;;;AChFA,SAAgB,gBACd,UACA,MACU;CAMV,OAAO,kBAAkB,IALL,SAAS,MAAM;EACjC,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB,SAAS,SAAS;CACpB,CAC+B,GAAG,QAAQ;AAC5C;;AAGA,SAAS,kBAAkB,QAAkB,MAA0B;CACrE,MAAM,QAAQ,OAAO,MAAM,KAAK,MAAM;CACtC,OAAO,OAAO,iBAAiB,QAAQ;EACrC,KAAK,EAAE,OAAO,KAAK,IAAI;EACvB,YAAY,EAAE,OAAO,KAAK,WAAW;EACrC,MAAM,EAAE,OAAO,KAAK,KAAK;EACzB,OAAO,EAAE,aAAa,kBAAkB,MAAM,GAAG,IAAI,EAAE;CACzD,CAAC;AACH;;;;;;;;ACfA,SAAgB,eACd,OACA,QACS;CACT,IAAI,QAAQ,SAAS,OAAO;CAC5B,MAAM,OAAQ,OAAiD;CAC/D,OAAO,SAAS,gBAAgB,SAAS;AAC3C;;;;;;AAOA,SAAgB,kBACd,OACA,QACS;CACT,OAAO,QAAQ,UAAU,OAAO,SAAS;AAC3C;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU;;AAGzC,SAAgB,mBAAmB,MAAmC;CACpE,IAAI,CAAC,SAAS,IAAI,GAAG,OAAO,KAAA;CAC5B,IAAI,OAAO,KAAK,YAAY,UAAU,OAAO,KAAK;CAClD,IAAI,OAAO,KAAK,UAAU,UAAU,OAAO,KAAK;CAChD,IAAI,SAAS,KAAK,KAAK,KAAK,OAAO,KAAK,MAAM,YAAY,UACxD,OAAO,KAAK,MAAM;CAEpB,IAAI,OAAO,KAAK,WAAW,UAAU,OAAO,KAAK;AAEnD;;AAGA,MAAM,uBAAuB;;AAG7B,eAAe,WACb,UACA,OAC6E;CAC7E,IAAI,CAAC,SAAS,MAAM,OAAO,KAAA;CAC3B,IAAI;CACJ,MAAM,SAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,IAAI;EAEF,SAAS,SAAS,KAAK,UAAU;EACjC,OAAO,QAAQ,OAAO;GACpB,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,OAAO,KAAK,KAAK;GACjB,QAAQ,MAAM;EAChB;CACF,QAAQ;EACN;CACF,UAAU;EACR,MAAM,QAAQ,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;EACrC,QAAQ,YAAY;CACtB;CACA,MAAM,QAAQ,IAAI,WAAW,IAAI;CACjC,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI,OAAO,MAAM;EACvB,UAAU,MAAM;CAClB;CACA,OAAO,OAAO,QACV;EAAE,OAAO,MAAM,SAAS,GAAG,KAAK;EAAG,WAAW;CAAK,IACnD;EAAE;EAAO,WAAW;CAAM;AAChC;;AAGA,SAAS,UAAU,MAGP;CACV,MAAM,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,KAAK;CAChD,IAAI,SAAS,IAAI,OAAO,KAAA;CACxB,IAAI,KAAK,WAAW,OAAO;CAC3B,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAS,gBAAgB,SAAiC;CACxD,OAAO,GAAG,QAAQ,KAAK,UAAU,MAAM,GAAG,UAAU,QAAQ,GAAG;AACjE;AAEA,eAAsB,cACpB,UACA,SACA,UAA+B,oBACR;CACvB,MAAM,OAAO,MAAM,WAAW,UAAU,oBAAoB;CAC5D,MAAM,OAAO,QAAQ,UAAU,IAAI;CAEnC,IAAI,MAAM,WAAW,gBAAgB,UAAU,KAAK,KAAK;CACzD,MAAM,SAAS,SAAS,aACpB,GAAG,SAAS,OAAO,GAAG,SAAS,eAC/B,OAAO,SAAS,MAAM;CAC1B,OAAO,IAAI,aAAa;EAEtB,SAAS,GAAG,gBAAgB,OAAO,EAAE,eAAe;EACpD,QAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAA;EACnC,QAAQ,SAAS;EACjB,YAAY,SAAS;EACrB,SAAS,SAAS;EAClB;EACA;EACA;CACF,CAAC;AACH;;;;;;AAOA,MAAM,mBACJ;AAEF,SAAgB,aACd,OACA,SACc;CAEd,MAAM,SACJ,iBAAiB,SAAS,MAAM,UAC5B,YAAY,MAAM,SAAS,QAAQ,GAAG,IACtC;CACN,MAAM,OAAO,iBAAiB,QAAQ,KAAK,IAAI,IAAI,mBAAmB;CACtE,OAAO,IAAI,aAAa;EACtB,SAAS,GAAG,gBAAgB,OAAO,EAAE,WAAW,SAAS;EACzD,QAAQ;EACR,YAAY;EACZ,SAAS,IAAI,QAAQ;EACrB,MAAM,KAAA;EACN;EACA;CACF,CAAC;AACH;;;ACxJA,SAAS,YAAY,QAAwC;CAC3D,QAAQ,MAAgB;EACtB,IAAI,EAAE,UAAU,SACd,OAAO,IAAI,eAAe,EAAE,OAAO,GAAG,EAAE,MAAM;OACzC,IAAI,EAAE,UAAU,WACrB,OAAO,IAAI,eAAe,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,GAAG,EAAE,GAAG,GAAG;OAEpE,OAAO,MACL,eAAe,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,UAAU,IAAI,GAAG,EAAE,GAAG,GAC/D;CAEJ;AACF;;;;;;;AAQA,SAAgB,QAAQ,UAA0B,CAAC,GAAW;CAC5D,MAAM,EACJ,UAAU,MACV,SAAS,SACT,SACA,YAAY,YAAY,IAAI,MAC1B;CACJ,MAAM,OAAO,WAAW,YAAY,MAAM;CAC1C,OAAO;EACL,MAAM;EACN,YAAY,OAAO,KAAK,SAAS;GAC/B,IAAI,CAAC,SAAS,OAAO,KAAK,GAAG;GAC7B,MAAM,SAAS,IAAI,KAAK,UAAU;GAClC,MAAM,OAAO,UAAU,IAAI,GAAG;GAC9B,MAAM,UAAU,IAAI;GACpB,MAAM,WAAW,KAAK,MAAM,IAAI,IAAI,OAAO;GAE3C,KAAK;IAAE,OAAO;IAAS;IAAQ;GAAK,CAAC;GACrC,IAAI;GACJ,IAAI;IACF,MAAM,MAAM,KAAK,GAAG;GACtB,SAAS,OAAO;IACd,IAAI,CAAC,eAAe,OAAO,IAAI,KAAK,MAAM,GAAG;KAC3C,MAAM,SAAS,eAAe,KAAK,IAAI,MAAM,SAAS,KAAA;KACtD,KAAK;MAAE,OAAO;MAAS;MAAQ;MAAM;MAAQ,IAAI,GAAG;MAAG;KAAM,CAAC;IAChE;IACA,MAAM;GACR;GAEA,KAAK;IAAE,OAAO;IAAW;IAAQ;IAAM,QAAQ,IAAI;IAAQ,IAAI,GAAG;GAAE,CAAC;GACrE,OAAO;EACT;CACF;AACF;;;ACtDA,MAAM,mBAAmB;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG;AACtD,MAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;AACF;AACA,MAAM,0BAA0B;;AAEhC,MAAMC,iBAAe,KAAK,KAAK;AAE/B,SAAS,aAAa,IAAY,QAA4C;CAC5E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,QAAQ,SAAS,OAAO,OAAO,OAAO,MAAM;EAChD,MAAM,gBAAgB;GACpB,aAAa,KAAK;GAClB,OAAO,QAAQ,MAAM;EACvB;EACA,MAAM,QAAQ,iBAAiB;GAC7B,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACV,GAAG,EAAE;EACL,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC3D,CAAC;AACH;;AAGA,SAAS,aAAa,OAAyC;CAC7D,MAAM,QAAQ,MAAM,QAAQ,IAAI,aAAa,CAAC,EAAE,KAAK;CACrD,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,MAAM,UAAU,OAAO,KAAK;CAC5B,IAAI,CAAC,OAAO,MAAM,OAAO,GAAG,OAAO,KAAK,IAAI,GAAG,UAAU,GAAI;CAC7D,MAAM,KAAK,KAAK,MAAM,KAAK;CAC3B,IAAI,OAAO,MAAM,EAAE,GAAG,OAAO,KAAA;CAC7B,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC;AACpC;;AAGA,SAAS,UACP,QACA,QACA,UACS;CACT,IAAI,CAAC,SAAS,QAAQ,IAAI,MAAM,KAAK,CAAC,eAAe,MAAM,GAAG,OAAO;CACrE,OAAO,OAAO,WAAW,KAAK,SAAS,QAAQ,SAAS,OAAO,MAAM;AACvE;;AAcA,SAASC,SAAO,SAAsC,OAAyB;CAC7E,MAAM,EACJ,UAAU,GACV,UAAU,kBACV,UAAU,iBACV,SAAS,UAAU,KAAK,OAAO,IAAI,MAAM,KAAK,OAC9C,gBAAgB,yBAChB,aACA,YACE;CACJ,IAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,GAC1C,MAAM,IAAI,UACR,YAAY,MAAM,YAAY,QAAQ,4CACxC;CAGF,OAAO;EACL;EACA;EACA,SAAA,IAJkB,IAAI,QAAQ,KAAK,MAAM,EAAE,YAAY,CAAC,CAIlD;EACN;EACA;EACA;EACA;CACF;AACF;;;;;;;AAQA,SAAgB,MAAM,UAAwB,CAAC,GAAW;CACxD,MAAM,EAAE,QAAQ,cAAc,GAAG,SAAS;CAC1C,MAAM,OAAOA,SAAO,MAAM,SAAS;CAEnC,OAAO;EACL,MAAM;EACN,YAAY,OAAO,KAAK,SAAS;GAC/B,MAAM,WAAW,IAAI,MAAM;GAC3B,IAAI,aAAa,OAAO,OAAO,KAAK,GAAG;GACvC,MAAM,WAAW,WACbA,SAAO;IAAE,GAAG;IAAM,GAAG;GAAS,GAAG,YAAY,IAC7C;GACJ,MAAM,UAAU,IAAI,KAAK,UAAU,MAAA,CAAO,YAAY;GACtD,MAAM,aAAa,CAAC,iBAAiB,IAAI,KAAK,IAAI;GAElD,KAAK,IAAI,UAAU,IAAK,WAAW,GACjC,IAAI;IACF,OAAO,MAAM,KAAK,GAAG;GACvB,SAAS,QAAQ;IAEf,IAAI,CAAC,cAAc,WAAW,SAAS,SAAS,MAAM;IACtD,IAAI,IAAI,KAAK,QAAQ,SAAS,MAAM;IACpC,MAAM,UAAU,SAAS,cAAc,QAAQ,SAAS,GAAG;IAC3D,IAAI,YAAY,OAAO,MAAM;IAC7B,IAAI,YAAY,KAAA,KAAa,CAAC,UAAU,QAAQ,QAAQ,QAAQ,GAC9D,MAAM;IAER,MAAM,aAAa,eAAe,MAAM,IACpC,aAAa,MAAM,IACnB,KAAA;IACJ,IAAI,eAAe,KAAA,KAAa,aAAa,SAAS,eACpD,MAAM;IAER,MAAM,OAAO,KAAK,IAChB,cAAc,SAAS,MAAM,SAAS,MAAM,GAC5CD,cACF;IACA,SAAS,UAAU,QAAQ,SAAS,IAAI;IACxC,MAAM,MAAM,MAAM,IAAI,KAAK,MAAM;GACnC;EAEJ;CACF;AACF;;;AC1IA,MAAM,eAAe,KAAK,KAAK;;;;;AAM/B,SAAS,UACP,MACA,QACA,QACA,SAC4B;CAC5B,MAAM,SAAS,KAAK,UAAU;CAC9B,OAAO,IAAI,eAA2B;EACpC,MAAM,YAAY;GAChB,OAAO,iBACL,eACM;IACJ,WAAW,MAAM,OAAO,MAAM;IAC9B,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;GAC7C,GACA,EAAE,MAAM,KAAK,CACf;EACF;EACA,MAAM,KAAK,YAAY;GACrB,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;IACR,OAAO;IACP,WAAW,MAAM;IACjB;GACF;GACA,QAAQ;GACR,WAAW,QAAQ,KAAK;EAC1B;EACA,OAAO,QAAQ;GACb,OAAO;GACP,OAAO,OAAO,OAAO,MAAM;EAC7B;CACF,CAAC;AACH;;AAGA,SAAS,cAAc,IAAY,OAAqB;CACtD,IAAI,OAAO,OAAO,qBAAsB,MAAM,KAAK,MAAM,cACvD;CACF,MAAM,IAAI,UACR,YAAY,MAAM,WAAW,aAAa,oCAC5C;AACF;;AAGA,SAAS,cACP,YACA,IACA,SAC+B;CAC/B,MAAM,QAAQ,iBACN,WAAW,MAAM,IAAI,aAAa,SAAS,cAAc,CAAC,GAChE,EACF;CACA,MAAqC,QAAQ;CAC7C,OAAO;AACT;;AAGA,SAAS,OACP,OACA,QAC0C;CAC1C,IAAI,OAAO,UAAU,UAAU;EAC7B,cAAc,OAAO,WAAW,MAAM,EAAE;EACxC,MAAM,EAAE,SAAS;EACjB,IAAI,SAAS,KAAA,GACX,cAAc,MAAM,sBAAsB,KAAK,IAAI;EAErD,OAAO;GAAE,IAAI;GAAO;EAAK;CAC3B;CACA,MAAM,EAAE,OAAO,SAAS;CACxB,IAAI,UAAU,KAAA,KAAa,SAAS,KAAA,GAClC,MAAM,IAAI,UACR,yGACF;CAEF,IAAI,UAAU,KAAA,GAAW,cAAc,OAAO,oBAAoB,MAAM,IAAI;CAC5E,IAAI,SAAS,KAAA,GAAW,cAAc,MAAM,mBAAmB,KAAK,IAAI;CACxE,OAAO;EAAE,IAAI,SAAS,OAAO;EAAmB;CAAK;AACvD;AAyBA,SAAgB,QACd,OACA,SAAuC,CAAC,GAChC;CACR,MAAM,EAAE,IAAI,SAAS,OAAO,OAAO,MAAM;CACzC,OAAO;EACL,MAAM;EACN,YAAY,OAAO,KAAK,SAAS;GAC/B,MAAM,WAAW,IAAI,MAAM,WAAW;GACtC,IAAI,aAAa,IAAI,cAAc,UAAU,iBAAiB,UAAU;GACxE,MAAM,SAAS,SAAS,OAAO,oBAAoB,KAAA,IAAY;GAC/D,IAAI,aAAa,OAAO,qBAAqB,WAAW,KAAA,GACtD,OAAO,KAAK,GAAG;GAEjB,MAAM,aAAa,IAAI,gBAAgB;GACvC,MAAM,QAAQ,IAAI,KAAK;GAEvB,MAAM,SAAS,QACX,YAAY,IAAI,CAAC,OAAO,WAAW,MAAM,CAAC,IAC1C,WAAW;GAEf,MAAM,QACJ,aAAa,OAAO,oBAChB,KAAA,IACA,cACE,YACA,UACA,oCAAoC,SAAS,GAC/C;GACN,IAAI;GAEJ,MAAM,mBAAmB;IACvB,IAAI,WAAW,KAAA,GAAW;IAC1B,aAAa,KAAK;IAClB,QAAQ,cACN,YACA,QACA,wBAAwB,OAAO,GACjC;GACF;GACA,MAAM,eAAe;IACnB,aAAa,KAAK;IAClB,aAAa,KAAK;GACpB;GACA,WAAW;GAEX,IAAI;GACJ,IAAI;IACF,MAAM,MAAM,KAAK;KACf,GAAG;KACH,MAAM;MAAE,GAAG,IAAI;MAAM;KAAO;IAC9B,CAAC;GACH,SAAS,QAAQ;IACf,OAAO;IACP,MAAM;GACR;GACA,IAAI,CAAC,IAAI,MAAM;IACb,OAAO;IACP,OAAO;GACT;GACA,WAAW;GACX,OAAO,gBACL,KACA,UAAU,IAAI,MAAM,QAAQ,QAAQ,UAAU,CAChD;EACF;CACF;AACF"}