/** * network.ts — Optimised HTTP validation for remote Markdown links. * * Strategy: * 1. Fast `HEAD` request → compare ETag first (no body transfer). * 2. If ETag missing or mismatched, stream the response body through an * incremental SHA-256 hash so we never hold the full payload in memory. */ import { type SsrfOptions } from "./ssrf.js"; /** Result of a network validation probe. */ export interface ValidationResult { /** Whether the remote content is unchanged since the last lockfile entry. */ unchanged: boolean; /** SHA-256 hex digest of the response body (empty string for HEAD-only hits). */ sha256: string; /** ETag header value if the server returned one. */ etag: string | null; /** Raw byte length of the response body (0 for HEAD). */ byteLength: number; /** Approximate token cost of the raw body. */ tokenCost: number; /** Content-Type header value if the server returned one. */ contentType: string | null; /** * Absolute path the body was written to during the streamed GET, * when {@link ValidateOptions.cacheDir} is set. `null` if no body * was fetched (HEAD fast-path) or `cacheDir` was not requested. */ cachedBodyPath: string | null; } /** Options accepted by `validateUrl`. */ export interface ValidateOptions extends SsrfOptions { /** Previously known ETag, used for fast `If-None-Match` comparison. */ knownEtag: string | null; /** Previously known SHA-256, used to detect unchanged content. */ knownSha256: string | null; /** AbortSignal to cancel the in-flight request. */ signal?: AbortSignal; /** Per-request timeout in milliseconds (default 15 000). */ timeoutMs?: number; /** Maximum number of redirects to follow (default 5). */ maxRedirects?: number; /** * When set, the streamed GET body is written to a file under this * directory (named `.raw`) as it streams — chunks are * hashed AND tee'd to disk in a single pass, so O(1) memory still * holds. `cachedBodyPath` in the result is the path written. * Unchanged (HEAD fast-path) responses do NOT write to disk here; * the caller uses `knownSha256` to find the existing cache file. */ cacheDir?: string; /** * Refuse the body if it exceeds this many bytes. Default: unlimited. * When streaming and the accumulated byte length crosses this limit * the request is destroyed with an `oversized` error. */ maxBytes?: number; /** * Allowlist of Content-Type prefixes (e.g. `["text/html", "text/plain"]`). * When set, a `Content-Type` header that does not start with one of the * allowed prefixes causes the body to be skipped with an error. * Default: any content type allowed. */ allowedContentTypes?: readonly string[]; } /** * Validate a single URL. * * Performs a `HEAD` first. If the server returns an ETag that matches * `knownEtag`, the content is considered unchanged and no body is fetched. * Otherwise a `GET` is issued and the body is streamed through an * incremental SHA-256 hasher. */ export declare function validateUrl(url: string, opts: ValidateOptions): Promise; //# sourceMappingURL=network.d.ts.map