import type { FilesPlugin } from "../index.js"; export interface DedupOptions { /** * Where the content-addressed blobs live, as a key prefix. Defaults to * `".dedup"`. The bytes of `photos/a.jpg` are stored once at * `".dedup/"`, and the logical key holds a small pointer to it. * Objects under this prefix are hidden from `list()` (unless you list within * it) and are never themselves de-duplicated. Don't store your own data here. */ prefix?: string; } /** * Content-address object bodies so identical content is stored only once. On * `upload` the body is hashed (SHA-256); the bytes are written a single time to * a content-addressed blob under a store prefix (`.dedup/` by default), and the * logical key holds a tiny pointer (an empty object whose `metadata` records the * hash). Re-uploading content already in the store **skips the byte upload** and * just writes the pointer — and because the pointer is what `copy` / `move` * relocate, copying a de-duplicated file is near-free and shares the same blob. * * Reads are transparent: `download` follows the pointer to the blob (ranges * included — blobs are stored verbatim), and `head` / `list` report the logical * size with the internal fields stripped, all for `upload([...])` / * `download([...])` bulk calls too. Objects without this plugin's marker * (pre-existing or written elsewhere) pass straight through, so it's safe to * enable on a mixed bucket. * * Provider-agnostic: it uses only the Web Crypto API (no native deps) and the * `metadata` the SDK already round-trips, so it works on any adapter that * supports metadata. De-duplication is most effective on the **outside** of the * array, before any body-transforming plugin — encrypted bytes don't de-dup * (a random per-object key makes identical inputs encrypt differently), so place * it first: `plugins: [dedup(), compression(), encryption(key)]`. * * Trade-offs, by design: * - **Buffers the whole body** to hash it, so it's unsuitable for unknown-length * streams and resumable uploads (the same gate `compression()` makes). * - **Reads cost a second fetch** — the pointer, then the blob (a ranged read * does a `head` first). `head` / `list` add nothing; they read the pointer. * - **`url()` / `signedUploadUrl()` throw** — a presigned GET would hand out the * empty pointer, and a presigned PUT would bypass content-addressing. Download * through the instance instead. * - **Blobs aren't garbage-collected.** `delete` (and overwrite) drop the * pointer but leave the content addressed, so it's reused if the content * reappears; reclaim unreferenced blobs with a storage lifecycle rule or a * periodic sweep. * * @param options optional `{ prefix }` — where blobs are stored. * @example * ```ts * import { createFiles } from "files-sdk"; * import { s3 } from "files-sdk/s3"; * import { dedup } from "files-sdk/dedup"; * * const files = createFiles({ * adapter: s3({ bucket: "uploads" }), * plugins: [dedup()], * }); * * await files.upload("a.png", bytes); * await files.upload("b.png", bytes); // same content — no second byte upload * await files.copy("a.png", "c.png"); // shares the one stored blob * ``` */ export declare const dedup: (options?: DedupOptions) => FilesPlugin; //# sourceMappingURL=index.d.ts.map