/** * RFC 9530 Digest Fields — `Content-Digest` and `Repr-Digest`. * * `digestAuth(opts)` automatically computes a digest of the outgoing * request body and attaches it as a Structured Field dictionary header. * `verifyDigest(response)` reads the digest from a received response and * verifies it against the body bytes; mismatch throws `DigestMismatchError`. * * Hashes are computed via `crypto.subtle.digest` so any runtime with the * Web Crypto API (Node ≥ 19, Bun, Deno, browsers) works without a * polyfill. Bodies that arrive as `ReadableStream` are tee'd so the * caller's body remains readable — we drain one branch into the digest * input and pass the other along to the wire. * * @example * ```ts * import { createMisina } from "misina" * import { digestAuth, verifyDigest } from "misina/digest" * * const api = createMisina({ baseURL, use: [digestAuth({ algorithm: "sha-256" })] }) * const res = await api.post("/upload", body) // Content-Digest auto-added * * await verifyDigest(res.raw) // throws DigestMismatchError on mismatch * ``` */ import type { MisinaPlugin } from "../types.mjs"; export type DigestAlgorithm = "sha-256" | "sha-512"; /** RFC 9530 §1: which header carries the digest. */ export type DigestField = "content-digest" | "repr-digest"; export interface DigestOptions { /** Hash algorithm. Default: `'sha-256'`. */ algorithm?: DigestAlgorithm; /** * Header to write. `'content-digest'` covers the transferred body * (RFC 9530 §3); `'repr-digest'` covers the representation before * content-coding (§4). Default: `'content-digest'`. */ field?: DigestField; /** * Skip digesting when no body is present (most GETs). Default: true. */ skipEmptyBody?: boolean; } export declare class DigestMismatchError extends Error { override readonly name = "DigestMismatchError"; readonly algorithm: string; readonly expected: string; readonly actual: string; constructor(algorithm: string, expected: string, actual: string); } /** * Plugin that automatically generates a `Content-Digest` (or `Repr-Digest`) * header on outgoing requests. The hook reads the body bytes, digests them * with `crypto.subtle.digest`, and writes a Structured Field dictionary * entry of the form `=::` to the configured header. * * If the request has no body (or `skipEmptyBody` is true and the body is * empty) the header is not added. */ export declare function digestAuth(options?: DigestOptions): MisinaPlugin; /** * Verify a response's `Content-Digest` (or `Repr-Digest`) against its * body. Throws `DigestMismatchError` on mismatch. Returns silently if * the response has no digest header (RFC 9530 says the receiver MUST * NOT fail when the field is absent). * * Reads the body once via `response.clone().arrayBuffer()` so the * caller's response stream remains untouched. */ export declare function verifyDigest(response: Response, options?: { field?: DigestField; }): Promise;