/** * Copyright (c) 2026 AzerothJS. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** One file part of a multipart body. */ export interface UploadedFile { /** The form field name this file was posted under. */ name: string; /** The client-supplied filename, verbatim. UNTRUSTED: sanitize before touching a filesystem. */ filename: string; /** * The part's declared Content-Type (application/octet-stream when the client omits it). * UNTRUSTED: client-declared, never verified against the payload. */ contentType: string; /** The raw file bytes. */ data: Uint8Array; } /** A parsed multipart/form-data body: text fields plus file parts. */ export interface MultipartBody { /** Text fields, repeated names preserved - the same container readForm returns. */ fields: URLSearchParams; /** File parts in posted order. */ files: UploadedFile[]; } /** * Caps for {@link readMultipart} - three independent axes (total bytes, part count, per-file * bytes), each ON by default. A cap crossing aborts the parse mid-stream as a 413; nothing is * buffered past the limit. */ export interface MultipartOptions { /** Total body cap in bytes (default 8 MiB - form-with-files is legitimately larger than JSON). */ limit?: number; /** Maximum number of parts, fields and files together (default 256). */ maxParts?: number; /** Per-file cap in bytes (default: the total limit). */ maxFileSize?: number; } /** * Reads and parses a multipart/form-data request. Throws UnsupportedMediaTypeError for other * content types, PayloadTooLargeError over the caps, and BadRequestError (code * 'malformed-multipart') for framing violations. */ export declare function readMultipart(request: Request, options?: MultipartOptions): Promise; /** * Extracts the boundary parameter from a multipart/form-data content type, handling the * quoted and unquoted forms. Returns null when the type is not multipart/form-data or the * boundary is missing/oversized (RFC 2046 caps it at 70 characters). */ export declare function boundaryOf(contentType: string): string | null; /** One part of a multipart body, delivered incrementally by {@link streamMultipart}. */ export interface MultipartPartStream { /** The form field name this part was posted under. */ name: string; /** The client-supplied filename, or null for a text field. UNTRUSTED: sanitize before touching a filesystem. */ filename: string | null; /** * The part's declared Content-Type (application/octet-stream when the client omits it). * UNTRUSTED: client-declared, never verified against the payload. */ contentType: string; /** * The payload bytes as they arrive off the socket. Single-pass: consume (or skip) this * stream BEFORE advancing the iterator - advancing discards whatever remains unread. */ stream: ReadableStream; /** * Buffers the whole payload - the per-part sink for parts known to be small. Guarded by * a cap (default 8 MiB) because buffering is exactly what streaming mode exists to avoid. */ bytes(limit?: number): Promise; /** The payload as UTF-8 text, same cap as {@link MultipartPartStream.bytes}. */ text(limit?: number): Promise; } /** * Caps for {@link streamMultipart}, the pull-based twin: per-part limits only, because the * whole point is that file payloads stream to their sink instead of buffering - total size is * the sink's business. */ export interface StreamMultipartOptions { /** Maximum number of parts, fields and files together (default 256). */ maxParts?: number; /** * Per-part payload cap in bytes. UNLIMITED by default - the consumer's sink governs in * streaming mode, and a surprise default cap would fail exactly the large uploads this * mode exists for. Set it when the route knows its ceiling; it binds every part, * including ones the consumer skips. */ maxPartBytes?: number; } /** * Streams a multipart/form-data request part by part - the beyond-memory mode. Same * validation posture as {@link readMultipart}: wrong content type is a 415, framing * violations are typed 400s, part-count, preamble, and header caps hold. Payload SIZE is * the consumer's to govern (or {@link StreamMultipartOptions.maxPartBytes}). * * ```ts * for await (const part of streamMultipart(context.request)) * { * if (part.filename !== null) { await part.stream.pipeTo(diskSink(part)); } * else { fields.append(part.name, await part.text()); } * } * ``` */ export declare function streamMultipart(request: Request, options?: StreamMultipartOptions): AsyncGenerator;