import { FilesError } from "./internal/errors.js"; import type { Receipt } from "./internal/receipts.js"; import type { ResumableDriver, ResumableDriverOptions, UploadControl } from "./internal/resumable.js"; export { FilesError, type FilesErrorCode } from "./internal/errors.js"; export { UploadControl } from "./internal/resumable.js"; export type { OffsetResumableDriver, PartMeta, PartsResumableDriver, ResumableDriver, ResumableDriverOptions, ResumableUploadSession, UploadControlStatus, } from "./internal/resumable.js"; export type { Receipt, ReceiptOp } from "./internal/receipts.js"; export type { BodySource, StoredFileMeta } from "./internal/stored-file.js"; export { createStoredFile } from "./internal/stored-file.js"; export { sync, type SyncCompare, type SyncOptions, type SyncProgress, type SyncResult, } from "./internal/sync.js"; export { transfer, type TransferOptions, type TransferProgress, type TransferResult, } from "./internal/transfer.js"; export { type Provider, PROVIDER_NAMES, type ProviderSlug, } from "./providers/index.js"; export type Body = Blob | File | ReadableStream | ArrayBuffer | ArrayBufferView | Uint8Array | string; export interface RetryBackoffContext { /** * Retry attempt number, starting at 1 for the first retry after the * initial failed call. */ attempt: number; error: FilesError; } export type RetryOptions = number | { max: number; backoff?: (ctx: RetryBackoffContext) => number; }; export interface OperationOptions { /** * Abort the operation when this signal is aborted. When both constructor * and per-call signals are provided, either one can abort the call. */ signal?: AbortSignal; /** * Overall timeout in milliseconds, applied to each attempt. A timeout * aborts the operation and is not retried. `0` or a negative value * disables timeout handling. */ timeout?: number; /** * Retry provider failures. A number is treated as `{ max: number }`. */ retries?: RetryOptions; } /** * A single upload-progress report. Passed to {@link UploadOptions.onProgress} * (and {@link UploadManyOptions.onProgress}, which also carries the item `key`). */ export interface UploadProgress { /** Cumulative bytes sent so far. */ loaded: number; /** * Total bytes to send, when known. Present for buffered bodies (`File`, * `Blob`, `ArrayBuffer`, `Uint8Array`, `string`); omitted for * `ReadableStream` bodies of unknown length, where only `loaded` can be * reported. */ total?: number; } /** * Tuning for multipart uploads. Pass `multipart: true` for sensible defaults, * or an object to override them. See {@link UploadOptions.multipart}. */ export interface MultipartOptions { /** * Size of each uploaded part, in bytes. Defaults to 5 MiB * (`5 * 1024 * 1024`) — also the S3-enforced minimum for every part except * the last. Adapters that chunk natively round this to their own valid * granularity (OneDrive to a 320-KiB multiple, GCS/Firebase to 256 KiB). */ partSize?: number; /** * How many parts upload in parallel. Defaults to `4`. Higher values trade * memory (up to `partSize * concurrency` buffered at once) for throughput. */ concurrency?: number; } export interface UploadOptions extends OperationOptions { /** * MIME type stored alongside the object and returned to readers in the * `Content-Type` response header. Inferred from `File` / `Blob` `type` * when not set; falls back to `application/octet-stream`. */ contentType?: string; /** * `Cache-Control` header stored on the object. Sent verbatim to the * provider; controls how downstream caches and browsers cache reads of * this key. * * **Throws** a {@link FilesError} on adapters with no cache-control field * (FTP, SFTP, Dropbox, Box, OneDrive, SharePoint, Cloudinary, Appwrite, * PocketBase, Bunny Storage, Convex, UploadThing, Bun's S3) rather than * silently dropping it — check {@link Adapter.supportsCacheControl} to branch * at runtime. */ cacheControl?: string; /** * Arbitrary user metadata stored alongside the object. Returned by * `head()` and `list()` where the provider supports it. * * **Throws** a {@link FilesError} on adapters with no user-metadata primitive * (Vercel Blob, UploadThing, FTP, SFTP, Dropbox, Box, OneDrive, SharePoint, * Cloudinary, Appwrite, PocketBase, Bunny Storage, Convex, Bun's S3) rather * than silently dropping it, mirroring the {@link DownloadOptions.range} gate. * An empty object is treated as "no metadata" and never throws. Check * {@link Adapter.supportsMetadata} to branch at runtime. */ metadata?: Record; /** * Called as the upload makes progress, for driving a progress bar. * * Granularity depends on the body and the adapter: * - A `ReadableStream` body is reported byte-by-byte as the adapter * consumes it (`total` is omitted unless the length is known). * - A buffered body (`File`, `Blob`, `ArrayBuffer`, `Uint8Array`, `string`) * is handed to the provider whole, so it reports `{ loaded: 0, total }` * then `{ loaded: total, total }` — unless the adapter reports true * progress itself (see below). * - **S3 and the S3-compatible adapters** report true byte-level progress * for every body type (including multipart for large files). This path * uses `@aws-sdk/lib-storage`, an optional peer dependency that must be * installed when `onProgress` is used with those adapters. * * Only fires while the upload is in flight and on success; a failed upload * does not emit a final event. On retry, progress restarts. */ onProgress?: (progress: UploadProgress) => void; /** * Upload the body in parallel parts instead of a single request. Pass `true` * for sensible defaults (5 MiB parts, 4 in flight), or an object to tune * `partSize` / `concurrency`. Multipart is the robust path for large objects * and for `ReadableStream` bodies of unknown length: a single PUT must buffer * or know the length up front, while multipart streams part-by-part. * * On S3-family adapters this routes through `@aws-sdk/lib-storage` (an * optional peer dependency) and is **auto-engaged for unknown-length * streams** even when this flag is unset. OneDrive, GCS, Firebase, and Azure * map it to their native chunking; other adapters already stream or chunk * transparently and ignore it. */ multipart?: boolean | MultipartOptions; /** * Drive the upload through a pause-able, resumable session. Construct an * {@link UploadControl}, pass it here, and call `pause()` / `resume()` / * `abort()` on it; persist `control.toJSON()` and rehydrate with * `UploadControl.from(token)` to resume in a later process. * * Requires a body with a known length (`File`, `Blob`, `ArrayBuffer`, a typed * array, or `string`) — a `ReadableStream` can't be re-read to resume. * Supported on S3 and the S3-compatible adapters, GCS, Firebase Storage, * Azure Blob, OneDrive, and Dropbox; other adapters throw. Not available in * the array (bulk) form of `upload`. */ control?: UploadControl; } export interface UploadResult { key: string; size: number; contentType: string; etag?: string; lastModified?: number; } export interface StoredFile { name: string; size: number; type: string; lastModified?: number; arrayBuffer(): Promise; text(): Promise; stream(): ReadableStream; blob(): Promise; key: string; etag?: string; metadata?: Record; } /** * A contiguous byte range to download, mirroring the HTTP `Range` header * (`bytes=start-end`) the supporting adapters issue under the hood. * * Both bounds are **0-based**, and `end` is **inclusive** — `{ start: 0, end: * 99 }` is the first 100 bytes, matching the wire semantics of S3, GCS, Azure, * and `fetch`. This is deliberately *not* `slice()` semantics (where `end` * would be exclusive); the bytes returned line up with what a `Range` request * would yield. */ export interface ByteRange { /** First byte to return, 0-based and inclusive. Must be a non-negative integer. */ start: number; /** * Last byte to return, 0-based and **inclusive**. Omit to read from `start` * to the end of the object (`bytes=start-`). When set, must be an integer * `>= start`. */ end?: number; } export interface DownloadOptions extends OperationOptions { as?: "blob" | "stream"; /** * Download only a contiguous slice of the object instead of the whole thing * — the building block for video seeking and resumable downloads. The * returned {@link StoredFile} carries just the requested bytes, and its * `size` reflects the range length (not the full object). * * **Supported** by the adapters with a native byte-range primitive: S3 and * the S3-compatible adapters (R2 over HTTP, MinIO, DigitalOcean Spaces, * Wasabi, Tigris, Backblaze B2, Storj, Hetzner, Akamai, and the rest of the * `s3()` family), Bun's S3, Google Cloud Storage, Firebase Storage, Azure * Blob, the local `fs` adapter, the in-memory adapter, and SFTP / FTP. SFTP * uses native read-stream offsets; FTP begins the transfer at the REST start * offset and trims a bounded `end` client-side (an open-ended range transfers * only what's needed). * * **Throws** a {@link FilesError} on adapters with no range primitive * (most SaaS/document providers) rather than silently downloading the whole * object and slicing it — so the bandwidth saving is never quietly lost. * Check {@link Adapter.supportsRange} to branch at runtime. */ range?: ByteRange; } export interface ListOptions extends OperationOptions { /** * Filter results to keys that start with this string. Omit to list * everything in the bucket. */ prefix?: string; /** * Continuation token from a prior result. Pass the `cursor` field of the * previous page back in to fetch the next page; omit on the first call. * * A cursor is only valid for the exact `prefix` **and** `delimiter` it was * produced with — hold both constant across a paginated sequence. */ cursor?: string; /** * Maximum number of items to return per page. Capped per-provider (most * providers max around 1000). Defaults to 1000. */ limit?: number; /** * Collapse keys at this boundary into "folders" (S3-style common prefixes), * the building block for a file-browser UI. With `delimiter: "/"` and * `prefix: "photos/"`, the page's `items` are only the direct files * (`photos/cover.jpg`) and {@link ListResult.prefixes} holds the subfolders * (`photos/2023/`, `photos/2024/`) — the keys nested deeper are folded into * those prefixes rather than listed. * * **Supported** by the object-store adapters with native common-prefix * listing (S3 and the whole `s3()` family, R2, Google Cloud Storage, * Firebase Storage, Azure Blob), the local `fs`, in-memory, FTP, SFTP, * Google Drive, and Cloudinary adapters (any delimiter string), plus the * folder-based providers (Vercel Blob, Netlify Blobs, Supabase, Dropbox, * Box, OneDrive, SharePoint) which only accept `"/"`. * * **Throws** a {@link FilesError} on adapters with no folder concept * (UploadThing, Appwrite, PocketBase, Convex, Bun's S3) rather than silently * returning a flat list. Check {@link Adapter.supportsDelimiter} to branch at * runtime. Must be a non-empty string. */ delimiter?: string; } export interface ListResult { items: StoredFile[]; /** * Common prefixes ("folders") when {@link ListOptions.delimiter} is set — * full keys including the trailing delimiter, e.g. `["photos/2023/", * "photos/2024/"]`. Omitted when no delimiter is set or none are found. When * the {@link Files} instance has a client `prefix`, these are scoped/stripped * identically to item keys. */ prefixes?: string[]; cursor?: string; } /** * How a string {@link SearchOptions.match | pattern} is interpreted by * {@link Files.search}. A `RegExp` pattern ignores this and always matches by * regex. * * - `"glob"` (default) — standard glob (via picomatch): `*` matches a run of * characters within a path segment, `**` spans segments, `?` is a single * non-`/` character, plus `[a-z]` classes, `{a,b}` braces, and a leading `!` * to negate. Dotfiles are matched (keys are opaque, not hidden files). A glob * with no wildcards (e.g. `"report.pdf"`) is an **exact** key match, not a * substring match. * - `"regex"` — the string is compiled to a `RegExp` (with the `u` flag) and * tested against the key. * - `"substring"` — matches keys that contain the pattern anywhere. * - `"exact"` — matches keys equal to the pattern. */ export type SearchMatch = "glob" | "regex" | "substring" | "exact"; export interface SearchOptions extends OperationOptions { /** * Restrict the underlying walk to keys starting with this prefix. For a glob * pattern a literal prefix is inferred automatically (`uploads/2024/*.pdf` * walks only the `uploads/2024` prefix); set this explicitly to bound a * `regex`, `substring`, or `caseInsensitive` search — which have no inferable * prefix — to part of a large bucket. Composes with the {@link Files} * instance `prefix` exactly like {@link ListOptions.prefix}. */ prefix?: string; /** * Page size for each underlying `list()` call — the walk's batch size, not a * cap on results. See {@link ListOptions.limit}. */ limit?: number; /** * Stop after yielding this many matches. Omit to yield every match (the walk * still pages lazily, so memory stays bounded). */ maxResults?: number; /** * How to interpret a string `pattern`. Defaults to `"glob"`. Ignored when * `pattern` is a `RegExp`. */ match?: SearchMatch; /** * Match case-insensitively. Adds the `i` flag for `glob` and `regex` and * lowercases both sides for `substring` and `exact`. A `RegExp` that already * sets its own `i` flag is honored regardless. When set, the automatic glob * prefix push-down is disabled (a provider prefix filter is case-sensitive), * so pass an explicit `prefix` to bound a case-insensitive search. Defaults * to `false`. */ caseInsensitive?: boolean; } export interface DeleteManyOptions { /** * How many per-key deletes run in parallel when an adapter falls back to * repeated `delete()` calls. Defaults to `8`. Adapters with a native bulk * primitive (S3, Supabase, UploadThing) ignore this — they delete in one * request. */ concurrency?: number; /** * When `true`, stop at the first failure and return immediately with the * keys deleted so far plus that error. When `false` (default), process * every key and collect per-key failures in `errors`. */ stopOnError?: boolean; } export interface DeleteManyError { key: string; error: FilesError; } export interface DeleteManyResult { /** Keys that were deleted, in the order they were supplied. */ deleted: string[]; /** Per-key failures. Omitted entirely when every key succeeded. */ errors?: DeleteManyError[]; } /** * Shared controls for the array form of the bulk methods (`upload`, * `download`, `head`, `exists`). Unlike `delete`, none of these have a native * provider batch primitive, so the SDK always fans out to per-key calls. */ export interface BulkOptions { /** * How many per-key operations run in parallel. Defaults to `8`. Ignored * when `stopOnError` is set — that path runs sequentially. */ concurrency?: number; /** * When `true`, stop at the first failure and return immediately with the * results gathered so far plus that error. When `false` (default), process * every item and collect per-key failures in `errors`. */ stopOnError?: boolean; } /** A single per-key failure from the array form of a bulk method. */ export interface BulkError { key: string; error: FilesError; } /** One item in the array form of {@link Files.upload}. */ export interface UploadManyItem { key: string; body: Body; /** Per-item MIME type. See {@link UploadOptions.contentType}. */ contentType?: string; /** Per-item `Cache-Control`. See {@link UploadOptions.cacheControl}. */ cacheControl?: string; /** Per-item user metadata. See {@link UploadOptions.metadata}. */ metadata?: Record; /** Per-item multipart toggle/tuning. See {@link UploadOptions.multipart}. */ multipart?: boolean | MultipartOptions; } export interface UploadManyResult { /** Successful uploads, in the order their items were supplied. */ uploaded: UploadResult[]; /** Per-item failures. Omitted entirely when every item succeeded. */ errors?: BulkError[]; } export interface UploadManyOptions extends BulkOptions { /** * Called as each item makes progress. Same semantics as * {@link UploadOptions.onProgress}, with the item's `key` added so callers * can attribute the report to a file when several upload concurrently. */ onProgress?: (progress: UploadProgress & { key: string; }) => void; } export interface DownloadManyOptions extends BulkOptions { /** Applied to every download. See {@link DownloadOptions.as}. */ as?: "blob" | "stream"; } export interface DownloadManyResult { /** Downloaded files, in the order their keys were supplied. */ downloaded: StoredFile[]; /** Per-key failures. Omitted entirely when every key succeeded. */ errors?: BulkError[]; } export interface HeadManyResult { /** Metadata results, in the order their keys were supplied. */ files: StoredFile[]; /** Per-key failures. Omitted entirely when every key succeeded. */ errors?: BulkError[]; } export interface ExistsManyResult { /** Keys that exist, in input order. */ existing: string[]; /** Keys the provider reports as missing, in input order. */ missing: string[]; /** * Keys whose existence couldn't be determined — a hard error (auth, * transport) rather than a clean present/absent answer. Omitted when none. */ errors?: BulkError[]; } export interface UrlOptions extends OperationOptions { /** * Override the adapter's default URL expiry, in seconds. * * **Honored** by adapters that sign (S3, Cloudflare R2 over HTTP, MinIO, * DigitalOcean Spaces, Storj, Hetzner, Akamai, Backblaze B2, Wasabi, * Tigris, and the R2 binding when HTTP credentials are also configured) — those * adapters return a presigned URL that expires after `expiresIn` seconds. * * **Ignored** by Vercel Blob (public): the underlying CDN URL has no * expiry, and the adapter returns it unchanged. If you need expiring * URLs there, you'll need a different provider — Vercel Blob has no * signing primitive. * * **N/A** for adapters where `url()` throws (Vercel Blob private; the * R2 binding without `publicBaseUrl` and without HTTP credentials). */ expiresIn?: number; /** * Override the `Content-Disposition` header on the response. * * **Strongly recommended** for buckets that contain user-uploaded * content. Without this override, the browser uses the stored * Content-Type to decide whether to render or download, which means a * user-uploaded `.html` (or SVG with embedded scripts) will execute * inline at your bucket's origin — stored XSS in the trust context of * your domain. Pass `"attachment"` (or `'attachment; filename="..."'`) * to force a download. * * **Forces the signing path.** On signing adapters (S3, R2 HTTP, MinIO, * DigitalOcean Spaces, Storj, Hetzner, Akamai, Backblaze B2, Wasabi, * Tigris, R2 hybrid), passing this option always returns a * presigned URL — * even when `publicBaseUrl` is configured, because a permanent CDN URL * has no signature in which to bind the override. If `publicBaseUrl` * was the deliberate choice and you also need the security override, * the override wins (it's the safe default). * * **Throws** on Vercel Blob (no Content-Disposition primitive) and on * the R2 binding without HTTP credentials (can't sign). These cases * fail loudly rather than silently dropping the security ask. */ responseContentDisposition?: string; } export interface SignUploadOptions extends OperationOptions { /** * How long the signed URL stays valid, in seconds. After it elapses, the * URL stops working and the client must request a new one. */ expiresIn: number; /** * MIME type bound into the signature when the provider supports doing so. * Adapters that cannot enforce it at the signed URL layer throw rather * than returning an advisory header. */ contentType?: string; /** * Maximum upload size in bytes, enforced server-side. * * **Strongly recommended when supported.** When omitted, the adapter falls * back to a presigned PUT URL with no server-side size limit — anyone with * the URL can upload an arbitrarily large file until `expiresIn` elapses. * When set, supporting adapters use a presigned POST form (S3/R2) that * enforces the size via a `content-length-range` policy. Adapters whose * direct-upload primitive cannot enforce this fail closed. */ maxSize?: number; /** * Minimum upload size in bytes for the presigned POST policy. Defaults to * `1` — empty uploads are usually a sign of a broken client, and the most * common application assumption ("file present means real content") fails * silently when 0-byte objects can land. Pass `0` if you genuinely want to * allow empty uploads. Only used by adapters that can enforce `maxSize`; * adapters whose direct-upload primitive cannot enforce this fail closed. */ minSize?: number; } export type SignedUpload = { method: "PUT"; url: string; headers?: Record; } | { method: "POST"; url: string; fields: Record; }; /** How an adapter's {@link Adapter.url} produces a download URL. */ export interface SignedUrlCapability { /** * `true` when `url()` mints a signed or tokenized download URL that grants * access without the caller's own credentials and is more than a permanent * public link (an S3 SigV4 URL, an Azure SAS, a GCS signed URL, a Box or * PocketBase access-token URL, …). Whether such a URL honors * {@link UrlOptions.expiresIn} exactly is a separate, per-provider detail — * some providers pin the lifetime server-side and ignore the request; see the * provider-gaps page. `false` when the adapter has no signing primitive: it * returns only a permanent public URL and ignores `expiresIn` (Vercel Blob, * Appwrite, Convex), or throws because it cannot mint a URL at all (the * filesystem, FTP/SFTP, OneDrive / Google Drive outside their public-link * mode). When `false`, prefer `download()`. */ supported: boolean; /** * Hard upper bound on `expiresIn`, in seconds, when the provider enforces one * in code (e.g. Azure clamps user-delegation SAS to 7 days; Dropbox temporary * links are a fixed 4 hours). Omitted when there is no code-enforced cap — * note this is distinct from soft infra limits the SDK passes through without * checking (AWS SigV4's 604800-second ceiling is documented, not enforced * here; see the provider-gaps page). */ maxExpiresIn?: number; } /** * A queryable snapshot of what the underlying adapter can do, exposed via * {@link Files.capabilities}. Lets callers, AI tool wrappers, and validators * branch on capability up front instead of relying on a throw at call time * (e.g. skip `range` planning when `rangeRead` is `false`, or fall back to * `download()` when `signedUrl.supported` is `false`). * * Every flag mirrors an operation the unified API actually exposes. The first * six are derived from the same per-adapter flags / optional methods the * {@link Files} wrapper already gates on, so they can never drift from the * runtime behavior. `serverSideCopy` and `signedUrl` are declared per-adapter * (no operation-level flag carries them) and default to the conservative value * when an adapter does not advertise them. */ export interface AdapterCapabilities { /** `download({ range })` returns only the requested bytes. From {@link Adapter.supportsRange}. */ rangeRead: boolean; /** `upload({ onProgress })` reports byte-level progress natively. From {@link Adapter.reportsUploadProgress}. */ uploadProgress: boolean; /** `list({ delimiter })` returns common prefixes. From {@link Adapter.supportsDelimiter}. */ delimiter: boolean; /** `upload({ metadata })` persists arbitrary user metadata. From {@link Adapter.supportsMetadata}. */ metadata: boolean; /** `upload({ cacheControl })` stores a Cache-Control header. From {@link Adapter.supportsCacheControl}. */ cacheControl: boolean; /** `upload({ control })` resumable / multipart uploads. From {@link Adapter.resumableUpload}. */ multipart: boolean; /** `copy()` runs server-side with no body re-transfer. From {@link Adapter.supportsServerSideCopy}. */ serverSideCopy: boolean; /** How `url()` produces a download URL. From {@link Adapter.signedUrl}. */ signedUrl: SignedUrlCapability; } export interface Adapter { readonly name: string; readonly raw: Raw; /** * Set `true` when `upload` reports byte-level progress by calling * `opts.onProgress` itself (e.g. via a provider's native upload-progress * hook). The {@link Files} wrapper then defers progress entirely to the * adapter. When unset, the wrapper handles `onProgress` generically: * byte-level for `ReadableStream` bodies, start/finish for buffered ones. */ readonly reportsUploadProgress?: boolean; /** * Set `true` when `download` honors {@link DownloadOptions.range} by issuing * a real byte-range request to the provider. The {@link Files} wrapper gates * on this: a `range` passed to an adapter without it throws before any * provider call, rather than silently downloading the whole object. Leave * unset for adapters whose provider has no range primitive. */ readonly supportsRange?: boolean; /** * Set `true` when `list` honors {@link ListOptions.delimiter} by returning * S3-style common prefixes in {@link ListResult.prefixes}. The {@link Files} * wrapper gates on this: a `delimiter` passed to an adapter without it throws * before any provider call, rather than silently returning a flat list. * Leave unset for adapters whose provider has no folder/prefix concept. */ readonly supportsDelimiter?: boolean; /** * Set `true` when `upload` persists {@link UploadOptions.metadata} (arbitrary * user metadata) on the stored object. The {@link Files} wrapper gates on * this exactly like {@link Adapter.supportsRange}: a non-empty `metadata` * passed to an adapter without it throws before any provider call, rather * than silently dropping it. Leave unset for adapters whose provider has no * arbitrary-metadata field. */ readonly supportsMetadata?: boolean; /** * Set `true` when `upload` honors {@link UploadOptions.cacheControl} by * storing it on the object. The {@link Files} wrapper gates on this exactly * like {@link Adapter.supportsRange}: a `cacheControl` passed to an adapter * without it throws before any provider call, rather than silently dropping * it. Leave unset for adapters whose provider has no cache-control field. */ readonly supportsCacheControl?: boolean; /** * Set `true` when `copy` runs server-side — a provider copy API call that * never re-transfers the body through this process (S3 `CopyObject`, Azure * `syncCopyFromURL`, a native filesystem rename, …). Leave unset for adapters * whose only copy path streams or buffers the bytes client-side * (download-then-reupload). Purely advisory — surfaced via * {@link Files.capabilities} as {@link AdapterCapabilities.serverSideCopy} so * callers can reason about the cost of a large `copy()`; it does not gate the * operation (every adapter's `copy` works regardless). */ readonly supportsServerSideCopy?: boolean; /** * Describes how `url` produces a download URL, surfaced via * {@link Files.capabilities} as {@link AdapterCapabilities.signedUrl}. Leave * unset for adapters that cannot mint a usable URL — it defaults to * `{ supported: false }`, the conservative value, so a caller that doesn't * advertise still reads as "no signed URL" rather than a wrong `true`. * Advisory only; it does not gate `url()`. */ readonly signedUrl?: SignedUrlCapability; upload(key: string, body: Body, opts?: UploadOptions): Promise; /** * Download an object's body and metadata. When {@link DownloadOptions.range} * is set, adapters that advertise {@link Adapter.supportsRange} must return * only the requested bytes, with `size` set to the range length. */ download(key: string, opts?: DownloadOptions): Promise; /** * Fetch metadata only — does not transfer the body. * * **Note:** the returned `StoredFile` still exposes `text()` / * `arrayBuffer()` / `blob()` / `stream()`, but those accessors lazily * issue a full GET on first use. If you only want metadata, don't call * the body accessors. They are not free. */ head(key: string, opts?: OperationOptions): Promise; /** * Check whether `key` exists without fetching its body. * * Returns `true` when the object exists, `false` when the provider reports * `NotFound`, and rethrows every other error (permissions, transport * failures, bad credentials, etc.). */ exists(key: string, opts?: OperationOptions): Promise; delete(key: string, opts?: OperationOptions): Promise; /** * Delete many keys in one call. Optional: when an adapter omits it, the * SDK fans out to `delete()` with bounded concurrency. Adapters that * implement it should use a native bulk primitive where one exists. */ deleteMany?(keys: string[], opts?: DeleteManyOptions): Promise; copy(from: string, to: string, opts?: OperationOptions): Promise; /** * Move (rename) `from` to `to`. Optional: when an adapter omits it, the SDK * falls back to `copy()` then `delete()`. Adapters should implement it when * the provider has a native rename that's atomic or avoids re-transferring * the body (the local filesystem, FTP, SFTP) — the copy+delete fallback on * those round-trips the bytes. */ move?(from: string, to: string, opts?: OperationOptions): Promise; list(opts?: ListOptions): Promise; /** * Return a URL the caller can use to fetch `key`. * * Adapters return the most direct URL they can produce: * * - **S3 / R2 (HTTP) / MinIO / DigitalOcean Spaces / Storj / Hetzner / Akamai / Backblaze B2 / Wasabi / Tigris** sign a `GetObject` request — the URL * expires after `opts.expiresIn` seconds (or the adapter's default, * typically 3600). If the adapter was constructed with * `publicBaseUrl`, the URL is built against that origin instead and * does not expire. * - **R2 (binding)** uses `publicBaseUrl` if configured, falls back to * HTTP signing if HTTP credentials were also passed (hybrid mode), * and otherwise throws. * - **Vercel Blob (public)** returns the permanent CDN URL. * `expiresIn` is ignored. * - **Vercel Blob (private)** throws — there is no URL primitive for * private blobs. Use `download()` instead. * * **Caller is responsible for URL-encoding.** Adapters do not escape * special characters in keys when building URLs against a * `publicBaseUrl` or Vercel Blob's fast path — the key is embedded * literally. If `key` is derived from untrusted input, callers should * validate or `encodeURIComponent`-style escape segments before * passing it in. */ url(key: string, opts?: UrlOptions): Promise; signedUploadUrl(key: string, opts: SignUploadOptions): Promise; /** * Build a {@link ResumableDriver} for a pause-able / resumable upload of * `key`. Optional: only adapters whose provider exposes a resumable or * multipart-with-listable-parts primitive implement it. When omitted, an * `upload()` call that passes {@link UploadOptions.control} throws an * unsupported-operation error before any provider call (mirroring the * {@link Adapter.supportsRange} gate). The returned driver is synchronous to * construct; it establishes the provider session lazily in `begin()`. */ resumableUpload?(key: string, opts: ResumableDriverOptions): ResumableDriver; } /** The public {@link Files} method a hook event describes. */ export type FilesActionType = "upload" | "download" | "head" | "exists" | "delete" | "copy" | "move" | "list" | "url" | "signedUploadUrl"; /** * Delivered to {@link FilesHooks.onAction} once when a public operation * settles — on success and on failure. The array form of an operation reports * the caller's `keys` and emits a single event carrying the aggregated * `result` (any per-item failures live in that result's `errors`); the single * form reports `key`, or `from` / `to` for `copy` and `move`. Keys are always * the ones the caller passed, never the internal prefixed path. */ export interface FilesActionEvent { type: FilesActionType; /** Caller-facing key, for single-key operations. */ key?: string; /** Caller-facing keys, for the array form. */ keys?: string[]; /** `copy` source / destination, as passed by the caller. */ from?: string; to?: string; status: "success" | "error"; /** The resolved value, on success. */ result?: unknown; /** The error, on failure — also delivered to {@link FilesHooks.onError}. */ error?: FilesError; /** Wall-clock duration of the public call, in milliseconds. */ durationMs: number; /** * A provenance {@link Receipt} for the call, present **only** when the * `receipts` option is on and `type` is a mutating verb (`upload`, `delete`, * `copy`, `move`) that **succeeded**. Absent on reads, on failures, on * `signedUploadUrl`, and on every instance with receipts off — so an existing * `onAction` consumer that never opted in sees the exact payload it always * has. See {@link FilesOptions.receipts}. */ receipt?: Receipt; } /** * Delivered to {@link FilesHooks.onError} when a public call rejects, just * before the matching `onAction({ status: "error" })`. Partial failures * collected in a bulk result's `errors[]` are not rejections and do not fire * it. */ export interface FilesErrorEvent { type: FilesActionType; key?: string; keys?: string[]; from?: string; to?: string; error: FilesError; durationMs: number; } /** * Delivered to {@link FilesHooks.onRetry} each time the SDK schedules a retry * for a single-operation call. Not fired on the first attempt, for * non-retryable errors, or for stream uploads (which never retry); bulk calls * do not retry, so they never fire it either. */ export interface FilesRetryEvent { type: FilesActionType; key?: string; from?: string; to?: string; /** The retry about to be scheduled — `1` is the first retry. */ attempt: number; /** Total retries allowed for this call. */ maxRetries: number; /** Milliseconds the SDK will wait before that attempt. */ delayMs: number; /** The error that triggered the retry. */ error: FilesError; } /** * Observability callbacks for a {@link Files} instance, passed as * `new Files({ hooks })`. Each mirrors the lightweight * {@link UploadOptions.onProgress} style — caller-facing payloads, no internal * adapter detail — and is fire-and-forget: the SDK calls it but does not await * it, and a hook that throws can never fail the operation it observes. */ export interface FilesHooks { onAction?: (event: FilesActionEvent) => void; onError?: (event: FilesErrorEvent) => void; onRetry?: (event: FilesRetryEvent) => void; } export interface FilesOptions extends OperationOptions { adapter: A; prefix?: string; /** * When `true`, block every write surface on this instance (`upload`, * `delete`, `copy`, `move`, `signedUploadUrl`, and the write helpers on * `file(key)`) with `FilesError("ReadOnly", ...)`. */ readonly?: boolean; /** Observability callbacks — see {@link FilesHooks}. */ hooks?: FilesHooks; /** * Opt into provenance {@link Receipt}s for mutating calls (`upload`, `delete`, * `copy`, `move`). **Off by default** — when unset or `false`, the instance * behaves exactly as before: no receipt is built and nothing is hashed. * * - `true` — attach a {@link Receipt} (without `sha256`) to the success * {@link FilesActionEvent} of each mutating call. Every field is derived * from work the SDK already does, so this adds no per-call cost. * - `{ sha256: true }` — additionally fingerprint the upload body, taken * **as passed to `upload()`** (before any plugin transform). This is the one * field with a real per-call cost, so it stays off until asked for by name. * The `sha256` is present only on an `upload` of a buffered body; a * streaming upload (which the SDK never buffers) omits it. With a * body-transforming plugin (`encryption`, `compression`) the stored bytes * differ from this hash, but it matches what `download` returns. * * Receipts ride on the existing {@link FilesHooks.onAction} event as an * additive `receipt` field, so reading them is `new Files({ receipts: true, * hooks: { onAction: (e) => e.receipt && record(e.receipt) } })` — no new * operation, callback, or return-shape change. */ receipts?: boolean | { sha256?: boolean; }; /** * Ordered list of {@link FilesPlugin}s wrapping this instance. `plugins[0]` * is the outermost layer of the onion. For plugins that add methods via * `extend`, use {@link createFiles} so the new surface shows up on the type. */ plugins?: readonly FilesPlugin[]; } export interface FileHandle { readonly key: string; upload(body: Body, opts?: UploadOptions): Promise; download(opts?: DownloadOptions): Promise; head(opts?: OperationOptions): Promise; exists(opts?: OperationOptions): Promise; delete(opts?: OperationOptions): Promise; url(opts?: UrlOptions): Promise; signedUploadUrl(opts: SignUploadOptions): Promise; copyTo(destinationKey: string, opts?: OperationOptions): Promise; copyFrom(sourceKey: string, opts?: OperationOptions): Promise; /** Move this key to `destinationKey`. See {@link Files.move}. */ moveTo(destinationKey: string, opts?: OperationOptions): Promise; /** Move `sourceKey` onto this key. See {@link Files.move}. */ moveFrom(sourceKey: string, opts?: OperationOptions): Promise; } /** * A single in-flight operation handed to a {@link FilesPlugin}. One variant per * public verb (mirroring {@link FilesActionType}), carrying the caller-facing, * **un-prefixed** inputs — a plugin never sees the internal prefixed path, the * same rule {@link FilesHooks} follow. * * The array form of `upload` / `download` / `head` / `exists` / `delete` fans * out to one op per item, each marked `bulk: true`, so a plugin can tell a * single call from one element of a batch. `copy`, `move`, `list`, `url`, and * `signedUploadUrl` have no array form and are always single. */ export type FilesOperation = { kind: "upload"; key: string; body: Body; options?: UploadOptions; bulk?: true; } | { kind: "download"; key: string; options?: DownloadOptions; bulk?: true; } | { kind: "head"; key: string; options?: OperationOptions; bulk?: true; } | { kind: "exists"; key: string; options?: OperationOptions; bulk?: true; } | { kind: "delete"; key: string; options?: OperationOptions; bulk?: true; } | { kind: "copy"; from: string; to: string; options?: OperationOptions; } | { kind: "move"; from: string; to: string; options?: OperationOptions; } | { kind: "list"; options?: ListOptions; } | { kind: "url"; key: string; options?: UrlOptions; } | { kind: "signedUploadUrl"; key: string; options?: SignUploadOptions; }; /** * The value a given {@link FilesOperation} resolves to — the result map that * keeps a plugin's `wrap` / `next` fully typed per verb. Mirrors the return * type of the matching {@link Files} method; `delete` / `copy` / `move` resolve * to no value (`undefined`). */ export type OperationResult = O extends { kind: "upload"; } ? UploadResult : O extends { kind: "download" | "head"; } ? StoredFile : O extends { kind: "exists"; } ? boolean : O extends { kind: "list"; } ? ListResult : O extends { kind: "url"; } ? string : O extends { kind: "signedUploadUrl"; } ? SignedUpload : undefined; /** * Continue inward through the plugin onion. Call it from a `wrap` to run the * next plugin (and ultimately the real operation), optionally passing a * transformed op. For a single operation `next` is retry-wrapped, so calling it * more than once re-enters the retry loop. */ export type PluginNext = (op: O) => Promise>; /** * An opt-in extension to a {@link Files} instance, passed as * `new Files({ plugins: [...] })`. Plugins compose as an **ordered onion** — * `plugins[0]` is outermost — and offer two independent capabilities: * * - `wrap` intercepts every operation: transform the op, veto it by throwing, * or observe it. This is the interceptable superset of {@link FilesHooks}, * which can only observe. Reach for {@link handlers} to author a per-verb * `wrap` with auto-passthrough instead of a raw one. * - `extend` contributes new namespaced surface (e.g. `files.usage()`). It runs * once at construction against the fully-wrapped instance, so an extension * method that calls back into `files.upload(...)` also goes through the onion. * Use {@link createFiles} to surface the added methods on the static type. * * Plugins run **inside** the `onAction` / `onError` hooks but **outside** retry * and prefixing: a `wrap` runs once on caller-facing keys, and retries resend * whatever body it produced. */ export interface FilesPlugin = Record> { /** Identifies the plugin in collision errors and diagnostics. */ readonly name: string; /** Tier A/B: wrap any operation. Call `next` to continue inward. */ wrap?: (op: O, next: PluginNext) => Promise>; /** Tier C: contribute namespaced surface. The only part that changes the type. */ extend?: (files: Files) => Ext; } /** * A per-verb handler map for {@link handlers}: list only the operations you * care about, each typed to its own op and `next`. Verbs you omit pass straight * through untouched. */ export type PluginHandlers = { [K in FilesOperation["kind"]]?: (op: Extract, next: (op: Extract) => Promise>>) => Promise>>; }; /** Distribute a union into the intersection of its members. */ type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; /** * The combined surface every plugin's `extend` contributes, as one * intersection — what {@link createFiles} grafts onto the {@link Files} type. */ export type ExtensionsOf

= UnionToIntersection<{ [K in keyof P]: NonNullable extends (files: Files) => infer E ? E : unknown; }[number]>; export declare class Files { #private; constructor(opts: FilesOptions); get raw(): A["raw"]; get adapter(): A; /** * The instance's key prefix (normalized — no leading/trailing slashes), or * `""` when none was configured. Plugins that build an internal * {@link Files} over {@link Files.adapter} (e.g. `extend` methods that * address the same store directly) must re-apply it, or their keys won't * line up with operations that ran through the instance. */ get prefix(): string; /** * A queryable snapshot of what the underlying adapter can do — see * {@link AdapterCapabilities}. Lets callers branch on capability up front * (e.g. skip a `range` request when `rangeRead` is `false`, or fall back to * `download()` when `signedUrl.supported` is `false`) instead of discovering * the limit by catching a throw at call time. * * Derived live from the adapter on each read, so a plugin that swaps * behaviors is always reflected. The derived flags read the exact per-adapter * flags / optional methods the wrapper gates on, so they cannot drift from * runtime behavior; `serverSideCopy` and `signedUrl` come from what the * adapter declares, defaulting to the conservative value when it declares * nothing. */ get capabilities(): AdapterCapabilities; readonly(): Files; file(key: string): FileHandle; /** * Upload one object or many. * * - `upload(key, body, opts)` stores a single object and resolves to its * {@link UploadResult}. A failure **throws** a {@link FilesError}. * - `upload(items)` stores many in one call — each item carries its own * `key`, `body`, and optional `contentType` / `cacheControl` / `metadata` * — and resolves to an {@link UploadManyResult}. It does **not** throw on * partial failure: successes land in `uploaded`, per-item failures * (including invalid keys) in `errors`, both in the order supplied. The * SDK fans out with bounded `concurrency` (default 8); `stopOnError` * short-circuits at the first failure. * * Both forms honor the client's `prefix`; the array form reports the keys * the caller passed, not the internal prefixed paths. */ upload(key: string, body: Body, opts?: UploadOptions): Promise; upload(items: UploadManyItem[], opts?: UploadManyOptions): Promise; /** * Download one object or many. * * - `download(key, opts)` resolves to a single {@link StoredFile}; a missing * key (or any failure) **throws** a {@link FilesError}. * - `download(keys, opts)` resolves to a {@link DownloadManyResult} and does * **not** throw on partial failure: successes land in `downloaded`, * per-key failures (a missing key included) in `errors`, both in input * order. `as` applies to every download; the SDK fans out with bounded * `concurrency` (default 8) and `stopOnError` stops at the first failure. * * Both forms honor the client's `prefix` and report the caller's keys. */ download(key: string, opts?: DownloadOptions): Promise; download(keys: string[], opts?: DownloadManyOptions): Promise; /** * Fetch metadata only — does not transfer the body. Pass one key for a * single {@link StoredFile} (throws on failure), or an array for a * {@link HeadManyResult} (`files` + per-key `errors`, never throws on * partial failure; honors `concurrency` / `stopOnError`). * * **Note:** the returned `StoredFile` still exposes `text()` / * `arrayBuffer()` / `blob()` / `stream()`, but those accessors lazily * issue a full GET on first use. If you only want metadata, don't call * the body accessors. They are not free. */ head(key: string, opts?: OperationOptions): Promise; head(keys: string[], opts?: BulkOptions): Promise; /** * Check whether one key or many exist, without fetching bodies. * * - `exists(key)` resolves to `true` when the object exists and `false` when * the adapter reports `NotFound`. Other failures still propagate so * callers do not treat auth or transport errors as "missing file". * - `exists(keys)` resolves to an {@link ExistsManyResult}: keys split into * `existing` / `missing` (both in input order), with hard errors (auth, * transport) collected in `errors` rather than thrown. The SDK fans out * with bounded `concurrency` (default 8); `stopOnError` stops at the first * hard error. */ exists(key: string, opts?: OperationOptions): Promise; exists(keys: string[], opts?: BulkOptions): Promise; /** * Remove one key or many. * * - `delete(key)` removes a single object and resolves to `void`. A failure * (including a missing key on providers that don't treat delete as * idempotent) **throws** a {@link FilesError}. * - `delete(keys)` removes many in one call and resolves to a * {@link DeleteManyResult}. It does **not** throw on partial failure — * per-key failures (and invalid keys) are collected in `errors`, deleted * keys in `deleted`, both in the order supplied. The adapter's native * bulk primitive is used when available, otherwise the SDK fans out to * single deletes with bounded `concurrency`. With `stopOnError`, the first * failure short-circuits and returns the keys deleted so far plus that * error. * * Both forms honor the client's `prefix`; the array form reports the keys * the caller passed, not the internal prefixed paths. */ delete(key: string, opts?: OperationOptions): Promise; delete(keys: string[], opts?: DeleteManyOptions): Promise; copy(from: string, to: string, opts?: OperationOptions): Promise; /** * Move (rename) `from` to `to`, resolving to `void`. A failure (e.g. a * missing source) **throws** a {@link FilesError}. * * Uses the adapter's native rename when it has one (the local filesystem, * FTP, SFTP) and otherwise falls back to `copy()` then `delete()` — the same * two-step every object store does, since none offer an atomic move. The * fallback is therefore **not atomic**: a crash between the copy and the * delete can leave the object at both keys. * * Moving a key onto itself (`from === to`, after the client `prefix` is * applied) is a no-op — the fallback would otherwise copy the object onto * itself and then delete it, destroying it. * * Honors the client's `prefix`; the action hook reports the keys the caller * passed, not the internal prefixed paths. */ move(from: string, to: string, opts?: OperationOptions): Promise; list(opts?: ListOptions): Promise; /** * Iterate every object, transparently following the cursor across pages. * * `list()` returns one page plus a `cursor`; most callers actually want * "walk everything under this prefix", which means a manual cursor loop. * This is that loop as an async iterable: * * ```ts * for await (const file of files.listAll({ prefix: "avatars/" })) { * console.log(file.key, file.size); * } * ``` * * `prefix` scopes the walk and `limit` sets the page size (how many keys * each underlying `list()` fetches), not a total cap — pass a `cursor` to * resume from a prior position. Each page is a real `list()` call, so it * honors the client `prefix`, retries/timeouts, and fires one `onAction` * `list` hook per page. Stop early by `break`ing out of the loop; no further * pages are fetched. * * @yields {StoredFile} each stored object, one page at a time, following the cursor. */ listAll(opts?: ListOptions): AsyncGenerator; /** * Find objects whose key matches `pattern`, walking every page like * {@link listAll}. * * `pattern` is a glob by default (standard glob via picomatch: `*` stays * within a path segment, `**` spans segments, `?` is one character, plus * `[a-z]` classes, `{a,b}` braces, and `!` negation). Pass a `RegExp`, or * `match: "regex" | "substring" | "exact"`, to switch how a string is * interpreted (see {@link SearchMatch}). Matching is against the * caller-facing key, so the instance `prefix` is already stripped. * * ```ts * for await (const file of files.search("photos/*.jpg")) { * console.log(file.key, file.size); * } * // "**" spans path segments — every object under uploads/, at any depth: * const all = await Array.fromAsync(files.search("uploads/**")); * ``` * * For a glob, the literal head is pushed down as a `list()` prefix so the * walk is scoped (`uploads/2024/*.pdf` only lists the `uploads/2024` prefix); * for other modes pass {@link SearchOptions.prefix} to bound it. Search reads * every page under that prefix — use {@link SearchOptions.maxResults} or * `break` to stop early. Matching against `file.key` reuses the same page walk * as {@link listAll}, so retries/timeouts and the `onAction` `list` hook apply * per page. * * @yields {StoredFile} each matching object, following the cursor across pages. */ search(pattern: string | RegExp, opts?: SearchOptions): AsyncGenerator; /** * Return a URL the caller can use to fetch `key`. * * The exact URL kind depends on the adapter — see {@link Adapter.url} * for the per-provider behavior. In short: signing adapters (S3, R2 * HTTP, MinIO, DigitalOcean Spaces, Storj, Hetzner, Akamai, Backblaze B2, * Wasabi, Tigris) return an expiring presigned URL by default; * Vercel-Blob-public returns its permanent CDN URL; configurations * with no URL primitive (Vercel-Blob-private, R2 binding without * `publicBaseUrl`/HTTP creds) throw. * * **Caller is responsible for URL-encoding.** Adapters do not escape * special characters in keys when building URLs against a * `publicBaseUrl` or Vercel Blob's fast path. If `key` is derived * from untrusted input, callers should validate or escape it. */ url(key: string, opts?: UrlOptions): Promise; signedUploadUrl(key: string, opts: SignUploadOptions): Promise; } /** * Author a {@link FilesPlugin.wrap} as a per-verb map instead of a single * function over the whole {@link FilesOperation} union. List only the verbs you * care about — each handler is typed to its own op and a same-kind `next` — and * every other verb passes straight through untouched. * * ```ts * const encryption = (key: CryptoKey): FilesPlugin => ({ * name: "encryption", * wrap: handlers({ * upload: (op, next) => * seal(op.body, key).then(({ body, iv }) => * next({ ...op, body, options: { ...op.options, metadata: { ...op.options?.metadata, iv } } }) * ), * download: (op, next) => next(op).then((file) => unseal(file, key)), * }), * }); * ``` * * The internal casts are confined here so plugin authors never write one. */ export declare const handlers: (map: PluginHandlers) => NonNullable; /** * Construct a {@link Files} instance whose static type includes the methods * contributed by each plugin's `extend`. Identical to `new Files(opts)` at * runtime — a class constructor can't return `this & Ext` keyed off its * arguments, so this factory is the seam that surfaces e.g. `files.usage()` or * `files.image.resize(...)` on the type. * * ```ts * const files = createFiles({ * adapter: s3({ bucket: "uploads" }), * plugins: [usage()], * }); * files.usage(); // typed, from usage().extend * ``` * * Plugins that only `wrap` (no `extend`) work with plain `new Files({ plugins })` * — they add no surface, so there is nothing extra to type. */ export declare const createFiles: (opts: FilesOptions & { plugins?: P; }) => Files & ExtensionsOf

; //# sourceMappingURL=index.d.ts.map