@openstage/monadyssey-fetch 3.0.0-beta.7 → 3.0.0-rc.1

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"monadyssey-fetch.umd.js","names":[],"sources":["../src/http-error.ts","../src/http-request.ts","../src/http-response.ts","../src/http-transport.ts","../src/interceptors.ts","../src/sse.ts","../src/sse-client.ts","../src/http-client.ts"],"sourcesContent":["import { Option, Some } from \"@openstage/monadyssey-core\";\n\n/**\n * How a request failed.\n * - `\"request\"`: the request could not be built — typically the body could not be serialized.\n * Nothing was sent.\n * - `\"response\"`: the server answered with a non-2xx status, or a 2xx whose body could not be parsed.\n * - `\"network\"`: no response arrived — DNS failure, offline, CORS block, or a rejected interceptor.\n * - `\"timeout\"`: the client's `timeout` option fired before the response, or its body, completed.\n * The request may have reached the server.\n * - `\"aborted\"`: the request was aborted by something other than IO cancellation, such as an\n * interceptor's own signal (an IO cancelled via `fiber.cancel()` or `IO.timeout` ends as\n * `Cancelled`, not as an `HttpError`).\n */\nexport type HttpErrorKind = \"request\" | \"response\" | \"network\" | \"timeout\" | \"aborted\";\n\n/** @internal */\nexport const HttpErrorMessage = {\n describe: (cause: unknown): string =>\n cause instanceof Error\n ? cause.message\n : typeof cause === \"string\"\n ? cause\n : typeof cause === \"object\" && cause !== null && \"message\" in cause\n ? String((cause as { message: unknown }).message)\n : \"An unknown error occurred.\",\n\n /** Response errors keep the pre-3.0 wording; the other kinds carry no status. */\n format: (kind: HttpErrorKind, url: string, status: number, rawMessage: string, timeoutMs?: number): string => {\n switch (kind) {\n case \"response\":\n return `Request to '${url}' failed with status ${status} and message: ${rawMessage}.`;\n case \"request\":\n return `Request to '${url}' could not be sent: ${rawMessage}.`;\n case \"timeout\":\n return `Request to '${url}' timed out after ${timeoutMs} ms.`;\n case \"aborted\":\n return `Request to '${url}' was aborted before a response arrived: ${rawMessage}.`;\n default:\n return `Request to '${url}' failed before a response arrived: ${rawMessage}.`;\n }\n },\n};\n\n/**\n * Represents an HTTP error encountered during a request.\n *\n * Extends the native `Error` class with additional context: the transport outcome (`kind`), HTTP\n * status code, raw error message, response body, request URL, response headers, and the\n * underlying cause.\n *\n * `kind` tells the two facts apart that `status` alone cannot: \"the server said 500\" is\n * `kind: \"response\", status: 500`, while \"nothing answered\" is `kind: \"network\", status: 0`.\n * For every kind other than `\"response\"`, `status` is `0` and `body` is `null`.\n *\n * @example\n * client.post(\"/payments\", payment).mapErr((error) =>\n * error.kind === \"response\"\n * ? { kind: \"rejected\", status: error.status }\n * : { kind: \"outcomeUnknown\" } // the request may or may not have reached the server\n * );\n */\nexport class HttpError extends Error {\n /** The transport outcome. See {@link HttpErrorKind}. */\n public readonly kind: HttpErrorKind;\n /** The HTTP status code, or `0` when no response arrived (`kind !== \"response\"`). */\n public readonly status: number;\n public readonly rawMessage: string;\n /** The parsed response body, or `null` when none was parsed. */\n public readonly body: unknown;\n public readonly url: string;\n public readonly headers?: Record<string, string>;\n /** The underlying exception, when there was one (a `TypeError` from `fetch`, a parse error, an abort reason). */\n public readonly cause?: unknown;\n /** The timeout that fired, in milliseconds. Set only when `kind === \"timeout\"`. */\n public readonly timeoutMs?: number;\n\n constructor(\n status: number,\n rawMessage: string,\n body: unknown,\n url: string,\n headers?: Record<string, string>,\n kind: HttpErrorKind = \"response\",\n cause?: unknown,\n timeoutMs?: number\n ) {\n super(HttpErrorMessage.format(kind, url, status, rawMessage, timeoutMs));\n this.name = \"HttpError\";\n this.kind = kind;\n this.status = status;\n this.rawMessage = rawMessage;\n this.body = body;\n this.url = url;\n this.headers = headers;\n this.cause = cause;\n this.timeoutMs = timeoutMs;\n }\n\n /**\n * An error for a request that received no response: DNS failure, offline, CORS block, or a\n * rejected interceptor. `status` is `0` and `body` is `null`.\n *\n * @param {string} url The request URL.\n * @param {unknown} cause The underlying exception (typically the `TypeError` thrown by `fetch`).\n */\n static network(url: string, cause: unknown): HttpError {\n return new HttpError(0, HttpErrorMessage.describe(cause), null, url, undefined, \"network\", cause);\n }\n\n /**\n * An error for a request whose `timeout` option fired before the response, or its body, completed.\n * `status` is `0`, `body` is `null`, and `timeoutMs` records the timeout.\n *\n * @param {string} url The request URL.\n * @param {number} after The timeout in milliseconds.\n */\n static timeout(url: string, after: number): HttpError {\n return new HttpError(0, `Request timed out after ${after} ms`, null, url, undefined, \"timeout\", undefined, after);\n }\n\n /**\n * An error for a request that could not be built, so nothing was sent — typically a body that\n * could not be serialized. `status` is `0` and `body` is `null`.\n *\n * @param {string} url The request URL.\n * @param {unknown} cause The underlying exception (typically a `TypeError` from `JSON.stringify`).\n */\n static request(url: string, cause: unknown): HttpError {\n return new HttpError(0, HttpErrorMessage.describe(cause), null, url, undefined, \"request\", cause);\n }\n\n /**\n * An error for a request aborted by something other than IO cancellation, such as an\n * interceptor aborting the signal. `status` is `0` and `body` is `null`.\n *\n * @param {string} url The request URL.\n * @param {unknown} [cause] The abort reason, if any.\n */\n static aborted(url: string, cause?: unknown): HttpError {\n return new HttpError(0, HttpErrorMessage.describe(cause), null, url, undefined, \"aborted\", cause);\n }\n\n /**\n * Narrows the `unknown` error body to a concrete type using a runtime type guard.\n * Returns `Some(body)` if the guard passes, `None` otherwise — so the narrow is *checked*,\n * not asserted. This keeps the unsafe cast out of consumer code: the body stays `unknown`\n * by default (forcing a conscious narrow), and this is the safe way to perform it.\n *\n * @template T The expected body shape.\n * @param {(x: unknown) => x is T} guard A type guard validating the body's shape.\n * @returns {Option<T>} `Some(body)` if the guard matches, `None` otherwise.\n *\n * @example\n * interface ApiError { code: string; message: string }\n * const isApiError = (x: unknown): x is ApiError =>\n * typeof x === \"object\" && x !== null && \"code\" in x && \"message\" in x;\n *\n * httpError.bodyAs(isApiError).fold(\n * () => \"unrecognized error shape\",\n * (e) => `${e.code}: ${e.message}`\n * );\n */\n bodyAs<T>(guard: (x: unknown) => x is T): Option<T> {\n return guard(this.body) ? (Some.of(this.body as NonNullable<T>) as Option<T>) : Option.empty<T>();\n }\n}\n","import { Credentials, Method, Options } from \"./options\";\n\n/** @internal */\nexport interface RequestDefaults {\n readonly headers: Record<string, string>;\n readonly credentials: Credentials;\n}\n\n/** @internal */\nexport interface PreparedRequest {\n readonly method: Method;\n readonly headers: Record<string, string>;\n readonly credentials: Credentials;\n readonly body: BodyInit | undefined;\n}\n\n/** Bodies `fetch` sends as-is: never JSON-encoded, never given an automatic content type. */\nconst isNativeBody = (body: unknown): boolean =>\n typeof body !== \"object\" ||\n body === null ||\n body instanceof FormData ||\n body instanceof Blob ||\n body instanceof ArrayBuffer ||\n body instanceof URLSearchParams ||\n (typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream) ||\n ArrayBuffer.isView(body);\n\nconst normalizeHeaders = (headers: Record<string, string | undefined>): Record<string, string> =>\n Object.fromEntries(\n Object.entries(headers)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([key, value]) => [key.toLowerCase(), value])\n );\n\n/** `FormData` must carry no content type so the browser can set the multipart boundary. */\nconst withContentType = (headers: Record<string, string>, body: unknown): Record<string, string> => {\n if (body instanceof FormData) {\n const { \"content-type\": _dropped, ...rest } = headers;\n return rest;\n }\n const autoJson = body != null && typeof body === \"object\" && !isNativeBody(body) && !(\"content-type\" in headers);\n return autoJson ? { ...headers, \"content-type\": \"application/json\" } : headers;\n};\n\nconst serializeBody = (method: Method, body: unknown, headers: Record<string, string>): BodyInit | undefined =>\n method === \"GET\" || method === \"HEAD\" || body == null\n ? undefined\n : isNativeBody(body)\n ? (body as BodyInit)\n : headers[\"content-type\"] === \"application/json\"\n ? JSON.stringify(body)\n : (body as BodyInit);\n\n/** @internal */\nexport const HttpRequest = {\n isNativeBody,\n normalizeHeaders,\n\n /** Per-request headers win over defaults; names are lowercased; the body follows the content type. */\n prepare: (method: Method, options: Options<unknown>, defaults: RequestDefaults): PreparedRequest => {\n const headers = withContentType(\n normalizeHeaders({ ...defaults.headers, ...(options.headers ?? {}) }),\n options.body\n );\n return {\n method,\n headers,\n credentials: options.credentials ?? defaults.credentials,\n body: serializeBody(method, options.body, headers),\n };\n },\n};\n","import { IO, Option } from \"@openstage/monadyssey-core\";\nimport { HttpError, HttpErrorMessage } from \"./http-error\";\nimport { ResponseType } from \"./options\";\n\ntype BodyReader = (response: Response) => Promise<unknown>;\n\nconst bodyReaders = {\n json: (response) => response.json(),\n text: (response) => response.text(),\n blob: (response) => response.blob(),\n arrayBuffer: (response) => response.arrayBuffer(),\n formData: (response) => response.formData(),\n} satisfies Record<ResponseType, BodyReader>;\n\n/** An own-property lookup, so an untyped `responseType` such as `\"toString\"` is rejected. */\nconst readerFor = (responseType: ResponseType): Option<BodyReader> =>\n Object.prototype.hasOwnProperty.call(bodyReaders, responseType)\n ? Option.of(bodyReaders[responseType])\n : Option.empty<BodyReader>();\n\nconst headers = (response: Response): Record<string, string> => {\n try {\n return Object.fromEntries(response.headers.entries());\n } catch {\n return {};\n }\n};\n\n/** The server answered but the body could not be used; the real status is kept, the body is `null`. */\nconst error = (cause: unknown, url: string, response: Response): HttpError =>\n new HttpError(response.status, HttpErrorMessage.describe(cause), null, url, headers(response), \"response\", cause);\n\n/** The server answered with a non-2xx status; the parsed body travels with the error. */\nconst failure = (response: Response, body: unknown, url: string): HttpError =>\n new HttpError(response.status, response.statusText, body, url, headers(response));\n\n/** @internal */\nexport const HttpResponse = {\n headers,\n error,\n failure,\n\n /** `204` and `205` carry no body and yield `null`. */\n read: (response: Response, responseType: ResponseType, url: string): IO<HttpError, unknown> =>\n IO.from(readerFor(responseType), () => error(`Unsupported response type: ${responseType}`, url, response)).flatMap(\n (read) =>\n IO.lift(\n () => (response.status === 204 || response.status === 205 ? null : read(response)),\n (cause) => error(cause, url, response)\n )\n ),\n\n settle: (response: Response, parsed: unknown, url: string): IO<HttpError, unknown> =>\n response.ok ? IO.of(parsed) : IO.fail(failure(response, parsed, url)),\n};\n","import { HttpError } from \"./http-error\";\n\nconst isAbort = (e: unknown): boolean =>\n typeof e === \"object\" && e !== null && \"name\" in e && (e as { name: unknown }).name === \"AbortError\";\n\n/** @internal */\nexport const HttpTransport = {\n /** An interceptor may reject with a ready-made `HttpError`; it is passed through untouched. */\n error: (e: unknown, url: string): HttpError =>\n e instanceof HttpError ? e : isAbort(e) ? HttpError.aborted(url, e) : HttpError.network(url, e),\n\n /** A timeout means the caller stopped waiting and an abort means someone wanted it stopped: neither is retried. */\n isRetryable: (error: HttpError): boolean =>\n error.kind === \"network\" || (error.kind === \"response\" && error.status >= 500),\n};\n","import { HttpInterceptor } from \"./options\";\n\n/** @internal */\nexport type Send = (request: RequestInit) => Promise<Response>;\n\n/** @internal */\nexport const Interceptors = {\n /** A right fold, so the first registered interceptor is outermost. */\n run: (interceptors: readonly HttpInterceptor[], request: RequestInit, send: Send): Promise<Response> =>\n interceptors.reduceRight<Send>((next, interceptor) => (r) => interceptor.intercept(r, next), send)(request),\n};\n","import { List, Stream } from \"@openstage/monadyssey-core\";\nimport type { Pipe } from \"@openstage/monadyssey-core\";\n\n/**\n * A single server-sent event parsed from a text/event-stream response.\n *\n * @property id - The event id, if the server sent an `id:` field. Used to resume a stream.\n * @property event - The event type. Defaults to `\"message\"` when the server sends no `event:` field.\n * @property data - The event payload. Multiple `data:` lines are joined with a newline.\n * @property retry - The reconnection time in milliseconds, if the server sent a `retry:` field.\n */\nexport interface ServerSentEvent {\n readonly id?: string;\n readonly event: string;\n readonly data: string;\n readonly retry?: number;\n}\n\n/** `null` when the block dispatches no event (comment-only, or no `data:`), per the event-stream rules. */\nconst parseBlock = (block: string): ServerSentEvent | null => {\n let event: string | undefined;\n let id: string | undefined;\n let retry: number | undefined;\n const data: string[] = [];\n\n for (const line of block.split(\"\\n\")) {\n if (line === \"\" || line.startsWith(\":\")) continue; // blank line or comment\n const colon = line.indexOf(\":\");\n const field = colon === -1 ? line : line.slice(0, colon);\n let value = colon === -1 ? \"\" : line.slice(colon + 1);\n if (value.startsWith(\" \")) value = value.slice(1);\n\n switch (field) {\n case \"event\":\n event = value;\n break;\n case \"data\":\n data.push(value);\n break;\n case \"id\":\n id = value;\n break;\n case \"retry\":\n if (/^\\d+$/.test(value)) retry = Number.parseInt(value, 10);\n break;\n default:\n break;\n }\n }\n\n if (data.length === 0) return null;\n return {\n event: event ?? \"message\",\n data: data.join(\"\\n\"),\n ...(id !== undefined ? { id } : {}),\n ...(retry !== undefined ? { retry } : {}),\n };\n};\n\n/**\n * Reassembles a byte stream into complete events; partial input and split multi-byte characters\n * are buffered. Call `reset` when a new connection begins.\n *\n * @internal\n */\nexport class FrameBuffer {\n private decoder = new TextDecoder(\"utf-8\");\n private buffered = \"\";\n\n push(chunk: Uint8Array): ServerSentEvent[] {\n let text = this.buffered + this.decoder.decode(chunk, { stream: true });\n let pendingCR = false;\n if (text.endsWith(\"\\r\")) {\n text = text.slice(0, -1); // hold a trailing CR: it may be the first half of a split CRLF\n pendingCR = true;\n }\n text = text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n const blocks = text.split(\"\\n\\n\");\n let rest = blocks.pop() ?? \"\";\n if (pendingCR) rest += \"\\r\";\n this.buffered = rest;\n return blocks.map(parseBlock).filter((event): event is ServerSentEvent => event !== null);\n }\n\n reset(): void {\n this.decoder = new TextDecoder(\"utf-8\");\n this.buffered = \"\";\n }\n}\n\n/**\n * A stream transformation that decodes raw response bytes into {@link ServerSentEvent}s, buffering\n * partial input across chunks. Single-use: each application maintains its own decoding state.\n *\n * @typeParam E - The error type carried by the surrounding stream.\n * @returns A pipe from a byte stream to a stream of parsed events.\n */\nexport const decodeEvents = <E>(): Pipe<E, Uint8Array, ServerSentEvent> => {\n const frames = new FrameBuffer();\n return (bytes: Stream<E, Uint8Array>) =>\n bytes.mapAccumulate(null, (chunk) => [null, List.from(frames.push(chunk))] as const);\n};\n","import { IO, Option, Stream } from \"@openstage/monadyssey-core\";\nimport { HttpError } from \"./http-error\";\nimport { HttpRequest } from \"./http-request\";\nimport { HttpResponse } from \"./http-response\";\nimport { HttpTransport } from \"./http-transport\";\nimport { Interceptors } from \"./interceptors\";\nimport { Credentials, HttpInterceptor } from \"./options\";\nimport { FrameBuffer, ServerSentEvent } from \"./sse\";\n\n/** A server-sent `retry:` below this is raised to it, so a misbehaving server cannot force a tight loop. */\nconst MIN_SERVER_RETRY_MS = 100;\n\ntype Reader = ReadableStreamDefaultReader<Uint8Array>;\n\n/** A cancellable sleep. The abort listener is removed when the timer fires, so waits do not accumulate listeners. */\nconst delay = (ms: number): IO<never, void> =>\n IO.cancellable<never, void>(\n (signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\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/**\n * Carried across reconnections within one run. Owns the one `AbortController` every connection\n * of the run is opened with, linked once to the run's signal; a connection registers a single\n * listener on it and removes it when closed, so listeners stay bounded however long the run lasts.\n */\nclass SseSession {\n readonly controller = new AbortController();\n readonly frames = new FrameBuffer();\n readonly pending: ServerSentEvent[] = [];\n lastEventId: string | undefined = undefined;\n retryMs: number;\n private connection: { readonly reader: Reader; readonly unlink: () => void } | null = null;\n private readonly unlinkParent: () => void;\n\n constructor(retryMs: number, parent: AbortSignal) {\n this.retryMs = retryMs;\n const onAbort = () => this.controller.abort(parent.reason);\n parent.addEventListener(\"abort\", onAbort, { once: true });\n this.unlinkParent = () => parent.removeEventListener(\"abort\", onAbort);\n }\n\n get reader(): Reader | null {\n return this.connection?.reader ?? null;\n }\n\n attach(reader: Reader): void {\n const onAbort = () => reader.cancel().catch(() => {});\n this.controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n this.connection = { reader, unlink: () => this.controller.signal.removeEventListener(\"abort\", onAbort) };\n this.frames.reset();\n }\n\n closeConnection(): void {\n if (this.connection) {\n this.connection.unlink();\n this.connection.reader.cancel().catch(() => {});\n this.connection = null;\n }\n }\n\n close(): void {\n this.closeConnection();\n this.unlinkParent();\n this.controller.abort();\n }\n}\n\n/** @internal */\nexport interface SseConfig {\n readonly url: string;\n readonly headers: Record<string, string>;\n readonly defaultHeaders: Record<string, string>;\n readonly credentials: Credentials;\n readonly interceptors: readonly HttpInterceptor[];\n readonly reconnect: boolean;\n readonly retry: number;\n}\n\nconst stream = (config: SseConfig): Stream<HttpError, ServerSentEvent> => {\n const { url, headers, defaultHeaders, credentials, interceptors, reconnect, retry } = config;\n\n const open = (session: SseSession): IO<HttpError, Reader> => {\n const prepared = HttpRequest.prepare(\n \"GET\",\n {\n headers: {\n ...headers,\n accept: \"text/event-stream\",\n ...(session.lastEventId !== undefined ? { \"last-event-id\": session.lastEventId } : {}),\n },\n credentials,\n },\n { headers: defaultHeaders, credentials }\n );\n return IO.lift<HttpError, Response>(\n () =>\n Interceptors.run(interceptors, { ...prepared, signal: session.controller.signal }, (req) => fetch(url, req)),\n (e: unknown) => HttpTransport.error(e, url)\n )\n .flatMap((response) =>\n response.ok\n ? IO.of(response)\n : HttpResponse.read(response, \"text\", url)\n .flatMapErr(() => IO.of<unknown>(null))\n .flatMap((body) => IO.fail<HttpError, Response>(HttpResponse.failure(response, body, url)))\n )\n .flatMap((response) =>\n response.body === null\n ? IO.fail<HttpError, Reader>(HttpResponse.error(\"Response has no body\", url, response))\n : IO.of(response.body.getReader())\n );\n };\n\n const readChunk = (reader: Reader): IO<HttpError, ReadableStreamReadResult<Uint8Array>> =>\n IO.lift(\n () => reader.read(),\n (e: unknown) => HttpTransport.error(e, url)\n );\n\n type EventStep = Option<[ServerSentEvent, SseSession]>;\n const done = IO.of<EventStep>(Option.empty());\n\n const retryLater = (session: SseSession): IO<HttpError, EventStep> =>\n delay(session.retryMs).flatMap(() => step(session));\n\n const step = (session: SseSession): IO<HttpError, EventStep> => {\n if (session.pending.length > 0) {\n const event = session.pending.shift() as ServerSentEvent;\n if (event.id !== undefined) session.lastEventId = event.id;\n if (event.retry !== undefined) session.retryMs = Math.max(event.retry, MIN_SERVER_RETRY_MS);\n return IO.of(Option.of<[ServerSentEvent, SseSession]>([event, session]));\n }\n\n const reader = session.reader;\n if (reader === null) {\n return open(session).foldM<HttpError, EventStep>(\n (error) => (reconnect && HttpTransport.isRetryable(error) ? retryLater(session) : IO.fail(error)),\n (opened) => {\n session.attach(opened);\n return step(session);\n }\n );\n }\n\n return readChunk(reader).foldM<HttpError, EventStep>(\n (error) => {\n session.closeConnection();\n return reconnect && HttpTransport.isRetryable(error) ? retryLater(session) : IO.fail(error);\n },\n (result) => {\n if (result.done) {\n session.closeConnection();\n return reconnect ? retryLater(session) : done;\n }\n session.pending.push(...session.frames.push(result.value));\n return step(session);\n }\n );\n };\n\n return Stream.bracket<HttpError, HttpError, SseSession, ServerSentEvent>(\n IO.cancellable((signal: AbortSignal) => new SseSession(retry, signal)),\n (session) => Stream.unfoldEval(session, step),\n (session) => IO.lift(() => session.close())\n );\n};\n\n/** @internal */\nexport const SseClient = { stream };\n","import { IO, Stream } from \"@openstage/monadyssey-core\";\nimport { HttpError } from \"./http-error\";\nimport { HttpRequest, PreparedRequest } from \"./http-request\";\nimport { HttpResponse } from \"./http-response\";\nimport { HttpTransport } from \"./http-transport\";\nimport { Interceptors } from \"./interceptors\";\nimport { SseClient } from \"./sse-client\";\nimport { Credentials, HttpClientConfig, HttpInterceptor, Method, Options, SseOptions } from \"./options\";\nimport { ServerSentEvent } from \"./sse\";\n\n/**\n * A composable HTTP client that wraps the native `fetch` API, returning `IO` instances instead of Promises.\n *\n * Unlike v1, `HttpClient` is instantiable — each instance carries its own configuration (base URL,\n * interceptors, default headers, timeout, credentials). This allows different parts of an application\n * to use independently configured clients.\n *\n * All HTTP methods return `IO<HttpError, A | null>`, enabling lazy execution, functional composition,\n * and explicit error handling. Cancellation is supported: when an IO is cancelled (via fiber or\n * timeout), the underlying `fetch` call is aborted through `AbortSignal`.\n *\n * **On the `| null` in the return type:** when the server responds with `204 No Content` or\n * `205 Reset Content`, there is no body to parse and the IO succeeds with `null`. This is a\n * protocol-level fact (the HTTP spec says these statuses have no body), distinct from a\n * domain-level optionality. For that reason the methods return `IO<HttpError, A | null>` rather\n * than `IO<HttpError, Option<A>>`. `Option` is the right tool when a value might be absent at\n * the domain level; `| null` is honest when the absence is structural to the protocol. For the\n * 95% case where you control the endpoint and never receive 204, the `| null` is a brief\n * `?? defaultValue` away.\n *\n * **Default credentials:** `same-origin`, matching the platform `fetch` default. Set\n * `credentials: \"include\"` explicitly per-client or per-request if you need cookies to flow\n * cross-origin.\n *\n * @example\n * const api = new HttpClient({\n * baseUrl: \"https://api.example.com\",\n * interceptors: [authInterceptor],\n * defaultHeaders: { \"Accept\": \"application/json\" },\n * timeout: 5000,\n * });\n *\n * const users = api.get<User[]>(\"/users\");\n */\nexport class HttpClient {\n private readonly interceptors: readonly HttpInterceptor[];\n private readonly baseUrl: string | undefined;\n private readonly defaultHeaders: Record<string, string>;\n private readonly defaultTimeout: number | undefined;\n private readonly defaultCredentials: Credentials;\n\n /**\n * Creates a new HttpClient with the given configuration.\n *\n * @param {HttpClientConfig} config - Configuration for the client.\n */\n constructor(config: HttpClientConfig = {}) {\n this.interceptors = Object.freeze([...(config.interceptors ?? [])]);\n this.baseUrl = config.baseUrl;\n this.defaultHeaders = config.defaultHeaders ?? {};\n this.defaultTimeout = config.timeout;\n this.defaultCredentials = config.credentials ?? \"same-origin\";\n }\n\n /**\n * Performs a GET request.\n *\n * @template A - The expected type of the response body.\n * @param {string} uri - The URL or path to request.\n * @param {Omit<Options<A>, \"body\">} [options] - Request options (excluding body).\n * @returns {IO<HttpError, A | null>} An IO representing the result.\n */\n get(uri: string, options: Omit<Options, \"body\"> & { observe: \"response\" }): IO<HttpError, Response>;\n get<A = unknown>(uri: string, options?: Omit<Options<A>, \"body\">): IO<HttpError, A | null>;\n get<A = unknown>(uri: string, options?: Omit<Options<A>, \"body\">): IO<HttpError, Response | A | null> {\n return this.request<A>(uri, \"GET\", options);\n }\n\n /**\n * Performs a POST request.\n *\n * @template A - The expected type of the response body.\n * @param {string} uri - The URL or path to request.\n * @param {unknown} [body] - The request payload.\n * @param {Options<A>} [options] - Request options.\n * @returns {IO<HttpError, A | null>} An IO representing the result.\n */\n post(uri: string, body: unknown, options: Options & { observe: \"response\" }): IO<HttpError, Response>;\n post<A = unknown>(uri: string, body?: unknown, options?: Options<A>): IO<HttpError, A | null>;\n post<A = unknown>(uri: string, body?: unknown, options?: Options<A>): IO<HttpError, Response | A | null> {\n return this.request<A>(uri, \"POST\", { ...options, body });\n }\n\n /**\n * Performs a PUT request.\n *\n * @template A - The expected type of the response body.\n * @param {string} uri - The URL or path to request.\n * @param {unknown} [body] - The request payload.\n * @param {Options<A>} [options] - Request options.\n * @returns {IO<HttpError, A | null>} An IO representing the result.\n */\n put(uri: string, body: unknown, options: Options & { observe: \"response\" }): IO<HttpError, Response>;\n put<A = unknown>(uri: string, body?: unknown, options?: Options<A>): IO<HttpError, A | null>;\n put<A = unknown>(uri: string, body?: unknown, options?: Options<A>): IO<HttpError, Response | A | null> {\n return this.request<A>(uri, \"PUT\", { ...options, body });\n }\n\n /**\n * Performs a PATCH request.\n *\n * @template A - The expected type of the response body.\n * @param {string} uri - The URL or path to request.\n * @param {unknown} [body] - The request payload.\n * @param {Options<A>} [options] - Request options.\n * @returns {IO<HttpError, A | null>} An IO representing the result.\n */\n patch(uri: string, body: unknown, options: Options & { observe: \"response\" }): IO<HttpError, Response>;\n patch<A = unknown>(uri: string, body?: unknown, options?: Options<A>): IO<HttpError, A | null>;\n patch<A = unknown>(uri: string, body?: unknown, options?: Options<A>): IO<HttpError, Response | A | null> {\n return this.request<A>(uri, \"PATCH\", { ...options, body });\n }\n\n /**\n * Performs a DELETE request.\n *\n * @template A - The expected type of the response body.\n * @param {string} uri - The URL or path to request.\n * @param {Omit<Options<A>, \"body\">} [options] - Request options (excluding body).\n * @returns {IO<HttpError, A | null>} An IO representing the result.\n */\n delete(uri: string, options: Omit<Options, \"body\"> & { observe: \"response\" }): IO<HttpError, Response>;\n delete<A = unknown>(uri: string, options?: Omit<Options<A>, \"body\">): IO<HttpError, A | null>;\n delete<A = unknown>(uri: string, options?: Omit<Options<A>, \"body\">): IO<HttpError, Response | A | null> {\n return this.request<A>(uri, \"DELETE\", options);\n }\n\n /**\n * Performs a custom HTTP request with the specified method.\n *\n * @template A - The expected type of the response body.\n * @param {string} uri - The URL or path to request.\n * @param {Method} method - The HTTP method.\n * @param {Options<A>} [options] - Request options.\n * @returns {IO<HttpError, A | null>} An IO representing the result.\n */\n fetch(uri: string, method: Method, options: Options & { observe: \"response\" }): IO<HttpError, Response>;\n fetch<A = unknown>(uri: string, method: Method, options?: Options<A>): IO<HttpError, A | null>;\n fetch<A = unknown>(uri: string, method: Method, options: Options<A> = {}): IO<HttpError, Response | A | null> {\n return this.request<A>(uri, method, options);\n }\n\n /**\n * Opens a server-sent-events stream and emits each {@link ServerSentEvent} as it arrives.\n *\n * The request goes through the same pipeline as every other method, so headers (including\n * `Authorization`), credentials, interceptors, and the base URL all apply. No request timeout is\n * used — the connection is long-lived. Cancelling the terminal `IO` (via `fork`/`timeout`) aborts\n * the connection and releases resources.\n *\n * By default the stream reconnects automatically when the connection ends or fails with a\n * retryable error (network error or `5xx`), resuming with `Last-Event-ID` and re-applying headers\n * and credentials on each attempt; terminal `4xx` responses (including auth failures) end the\n * stream. See {@link SseOptions} to configure or disable this.\n *\n * @param {string} uri - The URL or path to connect to.\n * @param {SseOptions} [options] - Headers, credentials, and reconnection settings.\n * @returns {Stream<HttpError, ServerSentEvent>} A stream of parsed events.\n *\n * @example\n * const events = client.sse(\"/events\", { headers: { Authorization: `Bearer ${token}` } });\n * await events.runForEach((event) => IO.lift(() => handle(event))).unsafeRun();\n */\n sse(uri: string, options: SseOptions = {}): Stream<HttpError, ServerSentEvent> {\n return SseClient.stream({\n url: this.resolveUrl(uri),\n headers: options.headers ?? {},\n defaultHeaders: this.defaultHeaders,\n credentials: options.credentials ?? this.defaultCredentials,\n interceptors: this.interceptors,\n reconnect: options.reconnect ?? true,\n retry: options.retry ?? 3000,\n });\n }\n\n private resolveUrl(uri: string): string {\n if (!this.baseUrl) return uri;\n try {\n new URL(uri);\n return uri;\n } catch {\n const base = this.baseUrl.endsWith(\"/\") ? this.baseUrl.slice(0, -1) : this.baseUrl;\n const path = uri.startsWith(\"/\") ? uri : `/${uri}`;\n return `${base}${path}`;\n }\n }\n\n private request<A = unknown>(\n uri: string,\n method: Method,\n options: Options<A> = {}\n ): IO<HttpError, Response | A | null> {\n const url = this.resolveUrl(uri);\n const timeout = options.timeout ?? this.defaultTimeout;\n const { responseType = \"json\", observe = \"body\", transform = (data: unknown) => data as A } = options;\n const defaults = { headers: this.defaultHeaders, credentials: this.defaultCredentials };\n\n const prepare: IO<HttpError, PreparedRequest> = IO.lift(\n () => HttpRequest.prepare(method, options, defaults),\n (e: unknown) => HttpError.request(url, e)\n );\n\n const send = (prepared: PreparedRequest): IO<HttpError, Response> =>\n IO.cancellable(\n (signal: AbortSignal) => Interceptors.run(this.interceptors, { ...prepared, signal }, (req) => fetch(url, req)),\n (e: unknown) => HttpTransport.error(e, url)\n );\n\n const decode = (response: Response): IO<HttpError, A | null> =>\n HttpResponse.read(response, responseType, url)\n .flatMap((parsed) => HttpResponse.settle(response, parsed, url))\n .flatMap((parsed) =>\n IO.lift(\n () => transform(parsed),\n (e: unknown) => HttpResponse.error(e, url, response)\n )\n );\n\n const outcome = prepare\n .flatMap(send)\n .flatMap((response): IO<HttpError, Response | A | null> =>\n response.ok && observe === \"response\" ? IO.of(response) : decode(response)\n );\n\n return timeout == null ? outcome : outcome.timeout(timeout, () => HttpError.timeout(url, timeout));\n }\n}\n"],"mappings":"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"}