import type { FilesPlugin, UploadResult } from "../index.js"; /** * Which objects to put in an archive: an explicit array of keys, or every key * under a `prefix` (resolved with `listAll`, so it spans pages). An omitted * `prefix` selects the whole bucket. */ export type ZipSelection = readonly string[] | { prefix?: string; }; /** * How entry bodies are stored in the archive. `"deflate"` (the default) * compresses each entry with the platform {@link CompressionStream}; * `"store"` writes the bytes verbatim — the right choice when the sources are * already compressed (JPEG, video, encrypted blobs), where deflate only burns * CPU. */ export type ZipMethod = "deflate" | "store"; export interface ZipOptions { /** How to store entry bodies. Defaults to `"deflate"`. See {@link ZipMethod}. */ method?: ZipMethod; /** * Derive an entry's path inside the archive from its key. Defaults to the * key itself, so the archive mirrors the bucket layout. Use it to strip a * prefix (`(key) => key.slice("exports/".length)`) or to flatten folders. * Two keys mapping to the same name throw rather than silently producing an * ambiguous archive. */ name?: (key: string) => string; } export interface UnzipOptions { /** * Key prefix the extracted entries are uploaded under (a trailing `/` is * added when missing). Defaults to `""` — entry paths become keys verbatim. */ into?: string; /** * Keep only the entries this returns `true` for. Receives the entry's path * as recorded in the archive (before `into` is prepended). */ filter?: (name: string) => boolean; } /** * The methods {@link zip} grafts onto a {@link Files} instance. A `type` * rather than an `interface` so it satisfies the `Record` * constraint on {@link FilesPlugin}'s extension parameter — an interface has no * implicit index signature and wouldn't be assignable. */ export type ZipApi = { /** * Stream many stored objects as one ZIP archive. Entries are downloaded and * written one at a time as the consumer reads, so memory stays flat no * matter how many keys are selected — pipe it straight into a `Response`. * Selection and download failures surface as errors on the stream; * cancelling it stops the remaining work. */ zip(selection: ZipSelection, options?: ZipOptions): ReadableStream; /** Build the same archive and store it back at `key` (`application/zip`). */ zipTo(key: string, selection: ZipSelection, options?: ZipOptions): Promise; /** * Extract a stored ZIP archive into individual objects: each file entry is * uploaded under {@link UnzipOptions.into} + its archive path, with a * content type inferred from its extension. Directory entries are skipped. * Returns one {@link UploadResult} per extracted entry, in archive order. */ unzip(key: string, options?: UnzipOptions): Promise; }; /** * Bundle stored objects into ZIP archives — and back out of them — entirely * through the instance. An `extend`-only (Tier C) plugin: it intercepts * nothing, it adds three methods. `files.zip(selection)` streams many keys as * one standard ZIP (deflate via the platform {@link CompressionStream}, no * native deps); `files.zipTo(key, selection)` stores that archive back as an * object; `files.unzip(key)` extracts an archive's entries into individual * objects. * * Everything goes through the fully-wrapped instance, so it composes with the * rest of the pipeline: with `encryption()` installed, zipped entries are * read as plaintext and an archive stored via `zipTo` is encrypted at rest. * Array position therefore doesn't matter — there's no `wrap` to order. * * Trade-offs, by design: * - **No ZIP64.** Archives are classic ZIP: at most 65535 entries and 4 GiB * per entry / per archive, failing closed (never silently corrupting) when * a limit is crossed. Reading a ZIP64 archive throws too. * - **`zip()` streams; `unzip()` buffers.** Writing needs only one entry in * flight at a time, so archives of many objects stream with flat memory. * Reading needs the central directory at the end of the file, so `unzip` * downloads the whole archive into memory first. * - **Entry names are validated on both sides.** Writing rejects duplicate * names, `..` segments, backslashes, and absolute paths; extraction rejects * the same (zip-slip), and refuses encrypted entries and unknown * compression methods rather than guessing. * - **`store` is for already-compressed sources.** The default `deflate` * shrinks text well, but JPEGs, videos, and `encryption()`-at-rest objects * read back as high-entropy bytes — pass `method: "store"` to skip the * wasted CPU. * * @example * ```ts * import { createFiles } from "files-sdk"; * import { s3 } from "files-sdk/s3"; * import { zip } from "files-sdk/zip"; * * const files = createFiles({ * adapter: s3({ bucket: "uploads" }), * plugins: [zip()], * }); * * // Stream a folder as a download: * return new Response(files.zip({ prefix: "reports/2026/" }), { * headers: { "Content-Disposition": 'attachment; filename="reports.zip"' }, * }); * * // Or store an archive, and unpack one: * await files.zipTo("exports/all.zip", ["a.csv", "b.csv"]); * await files.unzip("incoming/batch.zip", { into: "imported/" }); * ``` */ export declare const zip: () => FilesPlugin; //# sourceMappingURL=index.d.ts.map