/** * zipStore — a real `.zip` file, STORED (no compression), in ~150 lines and * zero dependencies. * * ## Why store-only is the honest choice here * * This writer exists for ONE payload: a bug-report bundle, which is JSON and * text. Deflate would shrink it well — and would cost this library either a * dependency (a zlib binding in a package that has none) or `node:zlib` (which * makes the export Node-only, when building the bundle in a browser is exactly * the flow the consent manifest is for). Store-only keeps the export * environment-neutral and the writer small enough to read in one sitting. * * The trade is stated rather than hidden: **a stored bundle is roughly the sum * of its files.** A 9 MB recording produces a ~9 MB zip. Every unzip tool reads * it — `unzip`, Finder, Explorer, `zipfile.ZipFile`, `jar` — because method 0 * is the oldest thing in the format. If a bundle is too big to attach, the * answer is to send fewer conversations (which the manifest's trim hints name), * not to compress harder; that is the same answer a deflated bundle would give * one doubling later. * * ## What it writes, exactly * * One local file header + data per entry, then one central-directory header per * entry, then the end-of-central-directory record — the classic three-part * layout from APPNOTE.TXT, no data descriptors, no zip64, no archive comment. * Names are UTF-8 with the language-encoding flag (bit 11) set, so a non-ASCII * name is not mojibake on a reader that honours the flag. * * ## What it refuses * * A path that would escape the extraction directory (`/leading`, `..`, a drive * letter, a backslash) is refused BY NAME rather than written — a bug-report * bundle is opened by a maintainer on their own machine, and "zip slip" is the * one way a report could hurt the person reading it. Duplicate names, empty * names, an entry over 4 GB and more than 65,535 entries are refused too: those * are the boundaries of the non-zip64 format, and writing past them silently * produces a file that fails to open. * * @example * ```ts * const bytes = zipStore([ * { name: 'manifest.json', data: new TextEncoder().encode('{}') }, * ]); * ``` * * @internal Not a public export. `exportBugReport` is the door. */ /** One file in the archive. Directories are implied by `/` in a name. */ export interface ZipEntry { /** Path inside the archive, `/`-separated and relative (`docs/a.json`). */ readonly name: string; /** The bytes. Text is the caller's `TextEncoder` output. */ readonly data: Uint8Array; } export interface ZipStoreOptions { /** * Timestamp stamped on every entry. Default: now. Passing one makes the * output byte-for-byte deterministic, which is what makes a zip testable. * DOS timestamps start in 1980; anything earlier is clamped to it. */ readonly modified?: Date; } /** * Build a stored (uncompressed) zip archive. * * @throws TypeError naming the offending entry when a name is unsafe or * duplicated, or when the archive would exceed the non-zip64 limits. */ export declare function zipStore(entries: readonly ZipEntry[], options?: ZipStoreOptions): Uint8Array; export declare function crc32(bytes: Uint8Array): number;