import { Readable } from 'node:stream'; import { b as HttpError, V as Validation, M as Metrics, c as HttpErrorBody } from "./types.d-DFh9698o.js"; import { LRUCache } from 'lru-cache'; /** * Simple cache interface that any cache implementation can follow */ interface Cache { /** Clear all cache entries */ clear: () => void | Promise; /** Delete a value from cache */ delete: (key: K) => boolean | Promise; /** Get a value from cache */ get: (key: K) => V | undefined | Promise; /** Check if a key exists in cache */ has: (key: K) => boolean | Promise; /** * Optional. Iterate over the cache's current keys. Required for prefix-based * eviction (`clearCache(fileId)` in transformers) — when absent, per-file * clear falls back to a full `clear()`. `LRUCache` from `lru-cache` * satisfies this out of the box. */ keys?: () => Iterable; /** Set a value in cache */ set: (key: K, value: V, options?: CacheOptions) => boolean | Promise; } /** * Options for cache operations */ interface CacheOptions { /** TTL in milliseconds */ ttl?: number; } /** * Canonical error codes used across handlers and storage adapters. * These codes map to standardized HTTP status codes and error messages. */ declare enum ERRORS { BAD_REQUEST = "BadRequest", CHECKSUM_MISMATCH = "ChecksumMismatch", FILE_CONFLICT = "FileConflict", FILE_ERROR = "FileError", FILE_LOCKED = "FileLocked", FILE_NOT_ALLOWED = "FileNotAllowed", FILE_NOT_FOUND = "FileNotFound", FORBIDDEN = "Forbidden", GONE = "Gone", INVALID_FILE_NAME = "InvalidFileName", INVALID_FILE_SIZE = "InvalidFileSize", INVALID_RANGE = "InvalidRange", INVALID_TYPE = "Invalidtype", METHOD_NOT_ALLOWED = "MethodNotAllowed", PRECONDITION_FAILED = "PreconditionFailed", READ_ONLY = "ReadOnly", REQUEST_ABORTED = "RequestAborted", REQUEST_ENTITY_TOO_LARGE = "RequestEntityTooLarge", STORAGE_BUSY = "StorageBusy", STORAGE_ERROR = "StorageError", TOO_MANY_REQUESTS = "TooManyRequests", UNKNOWN_ERROR = "UnknownError", UNPROCESSABLE_ENTITY = "UnprocessableEntity", UNSUPPORTED_CHECKSUM_ALGORITHM = "UnsupportedChecksumAlgorithm", UNSUPPORTED_MEDIA_TYPE = "UnsupportedMediaType" } /** * Type mapping of error codes to standardized HTTP error responses. * @template T - The error code type (defaults to string) */ type ErrorResponses = { [K in T]: HttpError; }; /** * Mapping of error codes to HttpError response objects. * @returns Map of error codes to standardized HTTP error responses */ declare const ErrorMap: ErrorResponses; /** * Error subclass carrying a stable error code and optional detail. * Provides structured error information for upload operations. */ declare class UploadError extends Error { override name: string; /** The standardized error code from the ERRORS enum */ UploadErrorCode: ERRORS; /** Optional additional error details */ detail?: unknown; /** * Creates a new UploadError instance. * @param code Standardized error code (defaults to UNKNOWN_ERROR) * @param message Human-readable error message (defaults to the code) * @param detail Optional additional error details */ constructor(code?: ERRORS, message?: string, detail?: unknown); } /** * Type guard to check if an error is an UploadError instance. * @param error Error to check * @returns True if the error is an UploadError with a valid error code */ declare const isUploadError: (error: unknown) => error is UploadError; /** * Best-effort extraction of an HTTP status from a native SDK error. * * Covers the shapes used by the consumer-provider SDKs: * - Dropbox `DropboxResponseError` → `error.status` * - Box `BoxAPIError` / Microsoft Graph errors → `error.statusCode` * - googleapis errors → `error.code` (numeric) or `error.response.status` * - Supabase / fetch-style errors → `error.status` or `error.response.status` */ declare const extractHttpStatus: (error: unknown) => number | undefined; /** * Map an HTTP status code to the closest canonical ERRORS value. * * Returns STORAGE_ERROR for unknown/5xx and BAD_REQUEST for unmapped 4xx. * 401 (unauthenticated) collapses to FORBIDDEN because the ERRORS enum has * no dedicated UNAUTHORIZED value; callers needing to distinguish the two * should inspect the native error on `UploadError.detail`. */ declare const mapStatusToErrorCode: (status?: number) => ERRORS; /** * Normalize a native storage-SDK error into an `UploadError`. * * The native error is preserved on `.detail` so advanced callers can drop down * to provider-specific shapes when needed. Already-wrapped `UploadError` * instances pass through unchanged. * @example * ```ts * try { * await client.filesCopyV2({ from_path, to_path }); * } catch (error) { * throw wrapStorageError(error, { adapter: "Dropbox", operation: "copy" }); * } * ``` */ declare const wrapStorageError: (error: unknown, options: { adapter: string; code?: ERRORS; message?: string; operation: string; status?: number; }) => UploadError; /** * Convenience function to throw an UploadError from a string error code. * Looks up the appropriate error message from ErrorMap. * @param UploadErrorCode String error code to convert to UploadError * @param detail Optional additional error details * @throws UploadError with the specified code and message */ declare const throwErrorCode: (UploadErrorCode: ERRORS | string, detail?: string) => never; /** * A simple lock map keyed by strings, backed by LRUCache. Locks * automatically expire according to the configured TTL preventing deadlocks. It takes no `max`: * a size bound would evict the least recently used lock while it is still held. * * Each successful `lock()` returns a unique token; the corresponding `unlock(key, token)` * call only releases the lock when the token matches the current holder. This prevents a * stale lock owner (e.g. one whose entry TTL'd out and was re-acquired by another caller) * from releasing another caller's lock by accident. * * With `maxHoldMs`, a held lock is renewed before its TTL runs out, so a holder that outlasts the * TTL keeps it; renewal stops after `maxHoldMs`, so a hung holder loses the lock a TTL later. */ declare class Locker extends LRUCache { private readonly maxHoldMs; /** Renewal timers of the held locks, by lock token. */ private readonly renewals; constructor({ maxHoldMs, ...options }?: Omit>, "max" | "maxSize"> & { maxHoldMs?: number; }); /** * Acquires a lock for the specified key. * @returns A unique token identifying the lock holder. * @throws Error if the key is already locked. */ lock(key: K): string; /** * Releases the lock for the specified key, but only if the token matches the current holder. * Silently no-ops when the lock has expired or is owned by someone else — releasing somebody * else's lock is worse than leaving a stale entry to TTL out on its own. * @returns true if the lock was released, false if it was already gone or owned by another caller. */ unlock(key: K, token: string): boolean; private stopRenewal; } /** * Retry configuration options for storage operations */ interface RetryConfig { /** * Multiplier for exponential backoff (e.g., 2 means delays double each retry) * @default 2 */ backoffMultiplier?: number; /** * Custom function to calculate delay for a specific retry attempt * @param attempt The current retry attempt (0-indexed) * @param error The error that occurred * @returns Delay in milliseconds, or undefined to use default exponential backoff */ calculateDelay?: (attempt: number, error: unknown) => number | undefined; /** * Initial delay in milliseconds before first retry * @default 1000 */ initialDelay?: number; /** * Maximum delay in milliseconds between retries * @default 30000 */ maxDelay?: number; /** * Maximum number of retry attempts * @default 3 */ maxRetries?: number; /** * Fire-and-forget hook invoked **before** each retry attempt's backoff sleep. * `attempt` is 1-based (1 = first retry, i.e. after the initial call failed). * Exceptions thrown by this callback are swallowed so an observability hook * can never fail the underlying operation. */ onRetry?: (attempt: number, error: unknown) => void; /** * HTTP status codes that should trigger a retry * @default [408, 429, 500, 502, 503, 504] */ retryableStatusCodes?: number[]; /** * Custom function to determine if an error should be retried. * * Tri-state: return `true` to force a retry, `false` to authoritatively * suppress it (the built-in {@link isRetryableError} heuristics are *not* * consulted), or `undefined` to defer the decision to those heuristics. * @param error The error that occurred * @returns `true`/`false` to decide, or `undefined` to defer to the built-in heuristics */ shouldRetry?: (error: unknown) => boolean | undefined; } /** * Determines if an error is retryable based on common patterns * @param error The error to check * @param retryableStatusCodes HTTP status codes that should trigger retry * @returns true if the error is retryable */ declare const isRetryableError: (error: unknown, retryableStatusCodes?: number[]) => boolean; /** * Retry an async operation with exponential backoff * @param fn The async function to retry * @param config Retry configuration * @returns The result of the function * @throws The last error if all retries are exhausted */ declare const retry: (function_: () => Promise, config?: RetryConfig) => Promise; /** * Create a retry wrapper function with pre-configured settings. * @param config Retry configuration * @returns A function that wraps async operations with retry logic */ declare const createRetryWrapper: (config?: RetryConfig) => (function_: () => Promise) => Promise; /** * Configurable validation system for file upload constraints. * Supports multiple validation rules with custom error responses. * @template T - The type being validated */ declare class Validator { private prefix; private validators; /** * Creates a new Validator instance. * @param prefix Prefix for generated error codes (default: "ValidationError") */ constructor(prefix?: string); /** * Adds validation rules to the validator. * Each rule must include an `isValid` function. * @param config Validation configuration object * @throws TypeError if any validator is missing the isValid function */ add(config: Validation): void; /** * Verifies an object against all configured validation rules. * Throws ValidationError on first validation failure. * @param t Object to validate * @throws ValidationError if validation fails */ verify(t: T): Promise; } interface MetaStorageOptions { logger?: Console; prefix?: string; suffix?: string; } interface LocalMetaStorageOptions extends MetaStorageOptions { /** * Where the upload metadata should be stored */ directory?: string; } declare class Metadata { [key: string]: unknown; psize?: number | string; pname?: string; pfiletype?: string; ptype?: string; pmimeType?: string; pcontentType?: string; ptitle?: string; pfilename?: string; poriginalName?: string; plastModified?: number | string; } interface FileInit { contentType?: string; expiredAt?: Date | number | string; /** * Explicit identifier. When provided, the File constructor uses it directly * instead of generating a random one (nanoid). Lets callers map a * user-chosen storage key (e.g. `"avatars/abc.png"`) onto the metadata id. */ id?: string; metadata: Metadata; originalName?: string; size?: number | string; storageClass?: string; ttl?: number | string; } interface FileReturn extends Omit, "id" | "storageClass" | "ttl" | "expiredAt"> { content: Buffer; ETag?: string; expiredAt?: Date | number | string; id: string; modifiedAt?: Date | number | string; name: string; storageClass?: string; } type UploadEventTypeValue = "completed" | "created" | "deleted" | "part" | "updated"; type UploadEventType = UploadEventTypeValue; interface FileQuery { id: string; name?: string; size?: number; } interface Checksum { checksum?: string; checksumAlgorithm?: string; } interface FilePart extends Checksum, FileQuery { body: Readable; contentLength?: number; start: number; } type DateType = Date | number | string; declare class File implements FileInit { bytesWritten: number; contentType: string; originalName: string; id: string; metadata: Metadata; name: string; size?: number; status?: UploadEventType; expiredAt?: DateType; createdAt?: DateType; modifiedAt?: DateType; hash?: { algorithm: string; value: string; }; content?: Buffer; ETag?: string; constructor({ contentType, expiredAt, id, metadata, originalName, size }: FileInit); } type UploadFile = Readonly; /** * Key of the version token a {@link MetaStorage} attaches to the records it reads (an ETag, a * generation, a content hash). A non-enumerable symbol property: never persisted, and invisible to * equality checks and serialization, so a copy of a record has to carry it over explicitly. */ declare const META_VERSION: unique symbol; /** * Metadata key of the claim a writer stores on an upload to keep other processes from writing * the same upload at once (see `BaseStorage.claimWrite`). Server bookkeeping, never client data. */ declare const WRITE_CLAIM_KEY = "_writeClaim"; /** * Returns the version token {@link MetaStorage.get} attached to `file`, if any. * @param file A record read from a meta storage, or a copy of one * @returns The version token */ declare const getMetaVersion: (file: object) => string | undefined; /** * Attaches a version token to `file`; `undefined` removes it. * @param file The record * @param version The version token */ declare const setMetaVersion: (file: object, version: string | undefined) => void; /** * Stores upload metadata. */ declare class MetaStorage { /** * Whether {@link MetaStorage.saveIfVersion} is implemented. Records written by several * requests at once (chunked uploads) are then merged safely across processes, not just * within one. */ readonly supportsConditionalSave: boolean; prefix: string; suffix: string; protected readonly logger?: Console; private accessCheckPromise?; constructor(config?: MetaStorageOptions); /** * Saves upload metadata. */ save(_id: string, file: T): Promise; /** * Saves upload metadata only if the stored record still has `version`, the token * {@link MetaStorage.get} attached to it (an atomic compare-and-swap). On success the new * version is attached to the returned record. * @returns The saved record, or `undefined` when the stored record changed or is gone */ saveIfVersion(_id: string, _file: T, _version: string): Promise; /** * Deletes an upload metadata. */ delete(_id: string): Promise; /** * Retrieves upload metadata. * @throws {UploadError} FILE_NOT_FOUND when no record exists; any other error is a failure of the store */ get(_id: string): Promise; /** * Lists the stored upload records. * @returns The records, or `undefined` when this store can't enumerate them */ list(): Promise; /** * Marks upload active. */ touch(_id: string, _file: T): Promise; /** * Backend access probe (e.g. a bucket HEAD request). Subclasses set it in their constructor * only when they created the backend client themselves; a caller-supplied client is trusted * as-is and never probed. */ protected accessProbe?: () => Promise; /** * Runs {@link MetaStorage.accessProbe} once, lazily, on the first operation instead of from * the constructor. A detached probe can only end up as an unhandled rejection; awaiting it * here throws the failure to the caller of the operation instead. A failed probe is forgotten * so the next operation retries it (e.g. after a transient network error). Returns * immediately when no probe is set. */ protected ensureAccess(): Promise; getMetaName(id: string): string; getIdFromMetaName(name: string): string; } type OnCreate = (file: TFile) => Promise | void; type OnUpdate = (file: TFile) => Promise | void; type OnComplete = (file: TFile, response: TResponse, request?: TRequest) => Promise | void; type OnDelete = (file: TFile) => Promise | void; type OnError = (error: HttpError) => Promise | void; /** What a provider reports about an object it stores, see `BaseStorage.findStoredObject`. */ interface StoredObject { contentType?: string; etag?: string; /** Provider-specific fields of the described file. */ extra?: Record; /** Size in bytes, when the provider reports it. */ size?: number; } interface PurgeList { items: UploadFile[]; maxAgeMs: number; } interface ExpirationOptions { /** * Age of the upload, after which it is considered expired and can be deleted */ maxAge: number | string; /** * Auto purging interval for expired upload */ purgeInterval?: number | string; /** * Auto prolong expiring upload */ rolling?: boolean; } interface BaseStorageOptions extends GenericStorageConfig { /** Allowed MIME types */ allowMIME?: string[]; /** The full path of the folder where the uploaded asset will be stored. */ assetFolder?: string; /** Cache instance to use for caching */ cache?: Cache; /** * Automatic cleaning of abandoned and completed upload * @example * ```ts * app.use( * '/upload', * Upload.upload({ * directory: 'upload', * expiration: { maxAge: '6h', purgeInterval: '30min' }, * onComplete * }) * ); * ``` */ expiration?: ExpirationOptions; /** File naming function */ filename?: (file: T) => string; /** * File name validation function. * Returns true if the filename is valid, false otherwise. * @default Cloud storage platforms: permissive (only blocks path traversal and null bytes) * @default DiskStorage: strict (blocks filesystem-incompatible characters) * @example * ```ts * fileNameValidation: (name: string) => { * // Custom validation logic * return name.length > 0 && !name.includes('../'); * } * ``` */ fileNameValidation?: (name: string) => boolean; /** Logger injection */ logger?: Console; /** Limiting the size of custom metadata */ maxMetadataSize?: number | string; /** File size limit */ maxUploadSize?: number | string; /** Provide custom meta storage */ metaStorage?: MetaStorage; /** Metrics injection for observability */ metrics?: Metrics; /** Callback function that is called when an upload is completed */ onComplete?: OnComplete; /** Callback function that is called when a new upload is created */ onCreate?: OnCreate; /** Callback function that is called when an upload is cancelled */ onDelete?: OnDelete; /** Customize error response */ onError?: OnError; /** Callback function that is called when an upload is updated */ onUpdate?: OnUpdate; /** Force relative URI in Location header */ useRelativeLocation?: boolean; /** Upload validation options */ validation?: Validation; } type DiskStorageOptions = BaseStorageOptions & { /** * Uploads directory. */ directory: string; /** * Configuring metafile storage on the local disk * @example * ```ts * const storage = new DiskStorage({ * directory: 'upload', * metaStorageConfig: { directory: '/tmp/upload-metafiles', prefix: '.' } * }); * ``` */ metaStorageConfig?: LocalMetaStorageOptions; }; type DiskStorageWithChecksumOptions = DiskStorageOptions & { /** * Enable/disable file/range checksum calculation */ checksum?: boolean | "md5" | "sha1"; }; /** * Unified storage configuration */ interface GenericStorageConfig { /** Allow additional properties for specific storage backends */ [key: string]: unknown; /** Base path/prefix for all operations */ basePath?: string; /** Cache TTL */ cacheTTL?: number; /** Supported checksum algorithms */ checksumTypes?: string[]; /** * Default abort signal merged into every operation. A per-call * `OperationOptions.signal` is combined with this, not replaced — either * one aborts the call. Note this signal lives for the storage instance's * lifetime: once it aborts, every subsequent operation fails fast. */ defaultSignal?: AbortSignal; /** * Default per-operation timeout in milliseconds, applied per retry * attempt when a call omits `OperationOptions.timeout`. A per-call * `timeout` (including `0` to explicitly disable) takes precedence. */ defaultTimeout?: number; /** Logger instance */ logger?: Console; /** Maximum file size */ maxFileSize?: number | string; /** Metrics instance for observability */ metrics?: Metrics; /** Retry configuration for transient failures */ retryConfig?: RetryConfig; } /** * Per-operation overrides for cancellation, deadlines and retries. * * Threaded through the public storage operations down to the backend SDK call. * Backends that wrap an SDK with native cancellation honour `signal`/`timeout` * directly; backends without a cancellation primitive still fail fast at the * storage layer, but the provider request may continue in the background. */ interface OperationOptions { /** * Retry override for this call. A number is treated as `maxRetries`; an * object is shallow-merged over the backend's configured `RetryConfig`. * Aborted and timed-out operations are never retried regardless of this. */ retries?: number | RetryConfig; /** * Abort the operation when this signal fires. Merged with any * per-call `timeout` via `AbortSignal.any`. */ signal?: AbortSignal; /** * Per-operation timeout in milliseconds. `0` or a negative value * disables the timeout. Applied per retry attempt. */ timeout?: number; } /** * ETag predicates for a conditional operation. Adapters evaluate them natively (an `If-Match` / * `If-None-Match` request header, or a compare under the storage lock) and fail with * `ERRORS.PRECONDITION_FAILED` (412) when the predicate does not hold. ETags compare without their * surrounding quotes, so `"abc"` and `abc` are the same validator. */ interface ConditionalOptions { /** Proceed only when the stored object's ETag equals this value. */ ifMatch?: string; /** `"*"`: proceed only when nothing is stored under the key (create-only write). */ ifNoneMatch?: "*"; } /** * Conditional predicates for `BaseStorage.copy`: the source must still carry `sourceIfMatch`; * `ifMatch` / `ifNoneMatch` apply to the copy destination. */ interface CopyConditionalOptions extends ConditionalOptions { sourceIfMatch?: string; } /** * Which conditional primitives an adapter evaluates natively. The `Files` facade rejects a * predicate the adapter cannot honour with `METHOD_NOT_ALLOWED` instead of ignoring it. */ interface ConditionalSupport { /** `copy` with `sourceIfMatch` and an optional destination predicate. */ copy: boolean; /** Create-only upload (`ifNoneMatch: "*"`). */ create: boolean; /** `delete` with `ifMatch`. */ delete: boolean; /** `download` / `head` with `ifMatch`. */ read: boolean; /** Replace-if-match upload (`ifMatch`). */ replace: boolean; } /** * A presigned browser-form `POST` upload: send `multipart/form-data` to `url` with every entry of * `fields`, followed by the `file` field. The provider enforces the signed policy (size range, * content type, key, expiry) before it stores anything. */ interface UploadPostPolicy { fields: Record; url: string; } /** Options for `BaseStorage.getUploadPost`. */ interface UploadPostOptions extends OperationOptions { contentType?: string; expiresIn?: number; /** Largest accepted body in bytes. */ maxSize?: number; /** Smallest accepted body in bytes. */ minSize?: number; } /** * Batch operation result for a single file */ interface BatchOperationResult { /** Error message if operation failed */ error?: string; /** File that was successfully operated on */ file?: T; /** File ID */ id: string; /** Whether the operation was successful */ success: boolean; } /** * Response from batch operations (deleteBatch, copyBatch, moveBatch) */ interface BatchOperationResponse { /** Failed operations with error details */ failed: { error: string; id: string; }[]; /** Total number of failed operations */ failedCount: number; /** Successfully processed files */ successful: T[]; /** Total number of successful operations */ successfulCount: number; } /** * Default filename validation for cloud storage platforms. * Permissive validation that only blocks dangerous patterns (path traversal, null bytes). * Cloud storage platforms (S3, Azure, GCS) accept most special characters and handle URL encoding automatically. */ declare const defaultCloudStorageFileNameValidation: (name: string) => boolean; /** * Default filename validation for local filesystems. * Stricter validation that blocks filesystem-incompatible characters. */ declare const defaultFilesystemFileNameValidation: (name: string) => boolean; /** * Abstract base class for all storage backends. * @template TFile The file type used by this storage backend. * @template TFileReturn The return type for file retrieval operations. * @remarks * ## Error Handling * * All storage operations follow consistent error handling patterns: * - Operations throw `UploadError` with specific error codes (see ERRORS enum) * - Common error codes: FILE_NOT_FOUND, GONE (expired), FILE_LOCKED, STORAGE_BUSY * - Errors are normalized with storage class context via `normalizeError()` * - Batch operations capture individual failures without stopping the batch * * ## Retry Behavior * * Storage implementations handle retries differently: * * ### Cloud Storage (S3, GCS, Azure, Netlify Blob) * - Use configurable retry wrappers via `retryConfig` option * - Default retryable status codes: 408, 429, 500, 502, 503, 504 * - Retry logic handles transient network errors and rate limiting * - Custom `shouldRetry` functions can be provided for advanced retry logic * * ### Local Storage (DiskStorage) * - No automatic retries (filesystem operations are typically immediate) * - Errors are thrown directly for immediate feedback * * ## Operation Instrumentation * * All public operations are automatically instrumented via `instrumentOperation()`: * - Metrics are recorded for operation count, duration, and errors * - File sizes are tracked for operations that return file objects * - Error metrics include error messages for debugging * * ## Metadata Caching * * File metadata is automatically cached to reduce storage API calls: * - Cache is updated on save, delete, and get operations * - Cache is invalidated when metadata is deleted * - Implementations can override caching behavior if needed */ declare abstract class BaseStorage { /** * Hook called when a new file is created. * @param file The newly created file object. * @remarks This hook is called after file metadata is saved but before returning the file. * Can be used for side effects like logging, notifications, or custom processing. */ onCreate: (file: TFile) => Promise | void; /** * Hook called when file metadata is updated. * @param file The updated file object. * @remarks This hook is called after metadata is updated and saved. * Can be used for side effects like logging or custom processing. */ onUpdate: (file: TFile) => Promise | void; /** * Hook called when a file upload is completed. * @param file The completed file object. * @param response The response object that can be modified in place (headers, statusCode, body). * @param request Optional request object for additional context. * @remarks This hook is called when file status becomes "completed". * The response object can be modified directly to add headers or change the status code. */ onComplete: (file: TFile, response: unknown, request?: unknown) => Promise | void; /** * Hook called when a file is deleted. * @param file The deleted file object. * @remarks This hook is called after the file is deleted but before returning. * Can be used for side effects like cleanup or logging. */ onDelete: (file: TFile) => Promise | void; /** * Hook called when an error occurs during storage operations. * @param error The HTTP error object that can be modified in place. * @remarks This hook allows customizing error responses by modifying the error object. * The error object can be modified to change headers, statusCode, or body properties. * Error formatting happens in handlers after this hook is called. */ onError: (error: HttpError) => Promise | void; isReady: boolean; /** * Backend access probe registered by {@link BaseStorage.startAccessCheck}. */ protected accessProbe?: () => Promise; private readyPromise?; errorResponses: ErrorResponses; cache: Cache; readonly logger?: Console; readonly metrics: Metrics; readonly genericConfig: BaseStorageOptions; maxMetadataSize: number; checksumTypes: string[]; /** * Adapter capability flag: when `true`, the adapter honours * `OperationOptions.range` on {@link BaseStorage.get} and * {@link BaseStorage.getStream}, returning only the requested byte slice. * The `Files` facade gates `download({ range })` on this flag and throws * `METHOD_NOT_ALLOWED` for adapters that haven't opted in. */ readonly supportsRange: boolean; /** * Adapter capability flag: when `true`, the adapter persists user-supplied * key/value metadata alongside the object and returns it on `get`/`head`. * Defaults to `true` — most backends carry metadata. Adapters that drop or * ignore custom metadata override this to `false`; the `Files` facade reads * it via {@link Files.capabilities} so callers can fail fast instead of * silently losing metadata. */ readonly supportsMetadata: boolean; /** * Adapter capability flag: when `true`, the adapter honours an object * `cacheControl` directive on write. Defaults to `false` — most backends * have no cache-control concept. Exposed through {@link Files.capabilities}. */ readonly supportsCacheControl: boolean; /** * Adapter capability flag: when `true`, the adapter implements * {@link BaseStorage.listDirectory} so the provider collapses keys into * common prefixes server-side. The `Files` facade prefers this native path * for `list({ delimiter })`; adapters that leave it `false` fall back to * synthesizing the directory view from a full listing in the facade. */ readonly supportsDelimiter: boolean; /** * Adapter capability flag: when `true`, the adapter reports its own * byte-level `onProgress` events during `write`. The `Files` facade skips * its coarse "start/done" emission for adapters that report progress * natively, so callers see one consistent stream of events. */ readonly reportsUploadProgress: boolean; /** * Adapter capability flag: when `true`, the adapter can assemble one object from several * writes (TUS PATCH chunks, resumed uploads, deferred lengths, concatenation). Defaults to * `true`. Adapters that can only store an object in a single request override this to * `false`; {@link BaseStorage.tusExtension} then stops advertising `creation-defer-length` * and `concatenation`, and the TUS handler rejects partial chunks before reading the body. */ readonly supportsResumableWrites: boolean; /** * Adapter capability flag: when `true`, the adapter only appends, so a write has to start * where the stored upload ends ({@link BaseStorage.assertContiguousWrite}). Its `bytesWritten` * is then always the length of the stored prefix, and a "completed" status it reports is * final. Adapters that write at any offset keep `false`: their `bytesWritten` is only the * furthest byte written, and a chunked upload's progress comes from its recorded chunks. */ readonly sequentialWrites: boolean; /** * Adapter capability flag: when `true`, the adapter has to know a chunk's length before it can * stream it to the provider (an S3 part, a GCS `Content-Range`, an Azure block). The TUS handler * then answers a PATCH without `Content-Length` with 411 Length Required instead of buffering an * unbounded body. Adapters that stream a body of unknown length keep `false`. */ readonly requiresContentLength: boolean; /** * Adapter capability flags: which ETag predicates ({@link ConditionalOptions}) the adapter * evaluates natively. All `false` by default; the `Files` facade rejects a predicate whose flag * is off with `METHOD_NOT_ALLOWED` and surfaces the flags as `Files.capabilities.conditional`. */ readonly conditionalSupport: ConditionalSupport; /** * Adapter capability flag: when `true`, {@link BaseStorage.getUploadPost} signs a browser-form * `POST` policy whose size range the provider enforces. Defaults to `false`. */ readonly supportsUploadPost: boolean; /** * Stable identifier of the adapter, written into resume tokens (`UploadControl.toJSON()`). Unlike * `constructor.name` it survives minifiers and is inherited by subclasses. */ readonly storageKind: string; /** * Longest `expiresIn` (seconds) the adapter can sign a URL or upload policy for, when the * provider has a hard ceiling (SigV4: 7 days). `undefined` when unknown or unlimited. */ readonly maxSignedUrlExpiresIn: number | undefined; maxUploadSize: number; protected expiration?: { maxAge?: string | number; purgeInterval?: string | number; rolling?: boolean; }; protected locker: Locker; /** Tail of the in-flight metadata saves per chunked-upload id, see {@link BaseStorage.saveMeta}. */ private readonly chunkedMetaSaves; /** * Upload records of in-flight conditional uploads, by id. A conditional `create` parks its record * here instead of saving it over the one the predicate is about; the `write` that commits the * bytes takes it back, so a failed predicate leaves the stored record untouched. */ private readonly pendingConditional; protected namingFunction: (file: TFile) => string; protected validation: Validator; protected abstract meta: MetaStorage; protected assetFolder: string | undefined; /** * Limits the number of concurrent upload requests */ protected concurrency?: number; /** * Handle for the {@link startAutoPurge} interval, retained so it can be * cleared via {@link stopAutoPurge}/{@link close}. */ protected autoPurgeTimer?: ReturnType; protected constructor(config: BaseStorageOptions); /** * Escape hatch returning the underlying native client (S3Client, BlobServiceClient, etc.) * for provider-specific operations not covered by the unified API. * Returns `undefined` when no native client is available (e.g. DiskStorage). */ get raw(): unknown; /** * Returns a presigned URL for downloading the object at `key`. * Throws `ERRORS.METHOD_NOT_ALLOWED` when the adapter has no native presign support. * @param _key Storage key. * @param _options Optional expiry and response overrides. */ getReadUrl(_key: string, _options?: OperationOptions & { expiresIn?: number; responseContentDisposition?: string; responseContentType?: string; }): Promise; /** * Returns a presigned URL for uploading a single object to `key`. * Throws `ERRORS.METHOD_NOT_ALLOWED` when the adapter has no native presign support. * @param _key Storage key. * @param _options Optional content type, expiry, and content length hints, plus per-call signal/timeout/retries. */ getUploadUrl(_key: string, _options?: OperationOptions & { contentLength?: number; contentType?: string; expiresIn?: number; }): Promise; /** * Signs a browser-form `POST` upload of a single object to `key`, whose policy makes the provider * enforce `minSize`/`maxSize` (and `contentType`, when given). Adapters that set * {@link BaseStorage.supportsUploadPost} override this. * Throws `ERRORS.METHOD_NOT_ALLOWED` when the adapter has no POST policy support. * @param _key Storage key. * @param _options Size range, content type and expiry of the policy. */ getUploadPost(_key: string, _options?: UploadPostOptions): Promise; get tusExtension(): string[]; /** * Validates a file against configured validation rules. * @param file File object to validate. * @returns Promise resolving to undefined if file is valid, throws ValidationError otherwise. * @throws {ValidationError} If validation fails */ validate(file: TFile): Promise; /** * Validates an upload before it is created, named as `create` would name it, so a caller can * refuse it before touching an existing file (e.g. a REST `PUT` replacing one). * @param config Upload to validate. * @throws {ValidationError} When the upload is not allowed. */ validateInit(config: FileInit): Promise; /** * The name `create` stores an upload under. * @param config Upload, with its `id`. * @returns The stored name. */ protected nameOf(config: FileInit): string; /** * Checks if a file exists by querying its metadata. * @param query File query containing the file ID to check. * @param query.id File ID to check. * @returns Promise resolving to true if file exists, false otherwise. * @remarks Only a missing upload answers false; any other failure throws, so a failed lookup * never reads as "absent" (a transfer with `overwrite: false` would replace the destination). */ exists(query: FileQuery, options?: OperationOptions): Promise; /** * Normalizes errors with storage-specific context. * @param error The error to normalize. * @returns Normalized HTTP error with storage class context added to the message. * @remarks Errors are enhanced with the storage class name for better debugging. */ normalizeError(error: Error): HttpError; /** * Gets the storage configuration. * @returns The current storage configuration options. */ get config(): BaseStorageOptions; /** * Saves upload metadata to the metadata storage. * @param file File object containing metadata to save. * @returns Promise resolving to the saved file object. * @remarks Updates timestamps and caches the file metadata. * @throws {UploadError} If `file.id` contains path-traversal sequences, null bytes, or is an absolute path — patterns that could let a local MetaStorage implementation write outside its directory. */ saveMeta(file: TFile): Promise; private persistMeta; /** * Saves a chunked record with an optimistic compare-and-swap, safe across processes. The * record normally carries the version its writer read, so there is no extra read; only * when another request saved in between is the stored record read and merged, then the * save retried. */ private saveChunkedMetaConditionally; /** * Resolves once the storage is ready. Runs the access probe registered by * {@link BaseStorage.startAccessCheck} if it has not succeeded yet; a failed probe is thrown * to the caller and forgotten, so the next call retries it (e.g. after a transient network * error at startup) instead of the storage staying unready forever. */ ensureReady(): Promise; /** * Marks the storage unready and starts probing the backend with `probe`; {@link BaseStorage.isReady} * turns true once it succeeds. Call it at the end of the subclass constructor, after the * backend client exists. A failure is logged here and retried by {@link BaseStorage.ensureReady}. */ protected startAccessCheck(probe: () => Promise): void; /** * Rejects ids containing `..` segments, null bytes, or absolute paths — defense-in-depth * so any MetaStorage / facade / handler that uses `id` as part of a filesystem path or * cross-bucket lookup can't be escaped. Public so the `Files` facade and Fetch handlers * can validate user-supplied keys before any adapter call. */ static assertSafeId(id: string): void; /** * Deletes upload metadata from the metadata storage. * @param id File ID whose metadata should be deleted. * @returns Promise resolving when metadata is deleted. * @remarks Also removes the file from the cache. */ deleteMeta(id: string): Promise; /** * Describes the object stored under an ID that has no upload metadata: a finished upload whose * metadata the provider dropped, or an object written by other means. Used so the REST `PUT` * doesn't create a file over an existing object (#919). Never expose the result to a caller: it * answers for any object in the bucket (#918). * @param id Key of the object. * @param options Operation options. * @returns The stored object as a completed file, or `undefined` when none exists. Any other * failure throws, so a failed lookup never reads as "absent". */ findStoredObject(id: string, options?: OperationOptions): Promise; /** * @deprecated Use {@link BaseStorage.findStoredObject}. */ getCompletedFile(id: string, options?: OperationOptions): Promise; /** * Reports the object stored under `id`, for {@link BaseStorage.findStoredObject}. Providers that * can describe an object without reading it override this; the default reports none. * @param _id Key of the object. * @param _options Operation options. * @returns The object, or `undefined` only when none is stored; any other failure throws. */ protected statObject(_id: string, _options?: OperationOptions): Promise; /** * The upload's metadata, or `undefined` when it has none. * @param id File ID of the upload. * @returns The metadata, or `undefined` when there is none. Any other failure of the meta * storage throws, so it never reads as a missing upload. */ protected findMeta(id: string): Promise; /** * The unfinished upload a `create` under `id` resumes. A completed one is not resumed: the * create starts a fresh upload that replaces it, so re-uploading a key stores the new content. * @param id File ID of the upload. * @returns The unfinished upload's metadata, or `undefined` when there is none to resume. */ protected findResumable(id: string): Promise; /** * The name an upload is stored under: the one its metadata records (a custom `filename` differs * from the ID), or the ID itself for an object without metadata. * @param id File ID of the upload. * @returns The stored name. */ protected storedName(id: string): Promise; /** * {@link BaseStorage.storedName} for a read: an expired upload answers GONE instead of being served. * @param id File ID of the upload. * @returns The stored name. * @throws {UploadError} If the upload has expired (ERRORS.GONE). */ protected readableName(id: string): Promise; /** * Retrieves upload metadata by file ID. * @param id File ID to retrieve metadata for. * @returns Promise resolving to the file metadata object. * @throws {UploadError} If the file metadata cannot be found (ERRORS.FILE_NOT_FOUND). Any other * failure of the meta storage is rethrown as-is, so it never reads as a missing upload. * @remarks Caches the retrieved metadata for faster subsequent access. With `ifMatch` (an exact * read, only set by the `Files` facade on adapters declaring `conditionalSupport.read`) it * answers only for the expected generation, see {@link BaseStorage.currentETag}. */ getMeta(id: string, options?: ConditionalOptions & OperationOptions): Promise; /** * The ETag of the object an upload record describes, as the conditional checks see it. Defaults * to the ETag the record stores; adapters whose records may be stale (another client can write * the object) override this to ask the object itself. * @param file Upload record. * @param _options Per-call signal/timeout/retries. * @returns The current ETag, or `undefined` when nothing is stored. */ protected currentETag(file: TFile, _options?: OperationOptions): Promise; /** * The ETag of the object stored under `name`, as {@link BaseStorage.statObject} reports it. * @param name Key of the object. * @param options Per-call signal/timeout/retries. * @returns The ETag, or `undefined` when nothing is stored. */ protected storedETag(name: string, options?: OperationOptions): Promise; /** * Park the record of a conditional upload until its commit (see {@link BaseStorage.takeConditional}). * @param file Record of the conditional upload. * @throws {UploadError} FILE_LOCKED when another conditional upload of the same id is in flight */ protected parkConditional(file: TFile): void; /** * Take back the parked record of a conditional upload. * @param id File ID of the upload. * @returns The record, or `undefined` when `id` has no conditional upload in flight. */ protected takeConditional(id: string, options?: ConditionalOptions): TFile | undefined; /** * Checks if a file has expired and deletes it if so. * @param file File object to check for expiration. * @returns Promise resolving to the file object if not expired. * @throws {UploadError} If the file has expired (ERRORS.GONE). * @remarks If the file is expired, it is automatically deleted and the metadata is removed. */ checkIfExpired(file: TFile): Promise; /** * Searches for and purges expired uploads. * @param maxAge Maximum age of files to keep (files older than this will be purged). * Can be a number (milliseconds) or string (e.g., "1h", "30m", "7d"). * If not provided, uses the expiration.maxAge from configuration. * @returns Promise resolving to a list of purged files. * @remarks * Errors during individual file deletions are logged but do not stop the purge process. * Files with corrupted metadata are skipped with a warning. * Only uploads with metadata are deleted, aged by it: rolling expiration if configured (based on * modifiedAt) or fixed expiration (based on createdAt), either prolonged by a later `expiredAt` * (an `update` with a `ttl`), so purge never deletes an upload {@link BaseStorage.checkIfExpired} keeps. */ purge(maxAge?: number | string): Promise; /** * Uploads {@link BaseStorage.purge} checks for expiry, by upload id: the records of the meta * storage, or {@link BaseStorage.list} when it can't enumerate them. `list` yields stored names, * which differ from the upload ids under a custom `filename`, and often no `createdAt`. */ protected listUploads(): Promise; /** * Gets an uploaded file by ID. * @param query File query containing the file ID to retrieve. * @param query.id File ID to retrieve. * @param options Optional per-call signal/timeout/retries. * @returns Promise resolving to the file data including content. * @throws {UploadError} If the file cannot be found (ERRORS.FILE_NOT_FOUND) or has expired (ERRORS.GONE). * @remarks This method loads the entire file content into memory. For large files, use getStream() instead. */ abstract get({ id }: FileQuery, options?: ConditionalOptions & OperationOptions): Promise; /** * Gets an uploaded file as a readable stream for efficient large file handling. * @param query File query containing the file ID to stream. * @param query.id File ID to stream. * @param options Optional per-call signal/timeout/retries (and, on range-capable adapters, a `range`). * @returns Promise resolving to an object containing the stream, headers, and size. * @throws {UploadError} If the file cannot be found (ERRORS.FILE_NOT_FOUND) or has expired (ERRORS.GONE). * @remarks * Default implementation falls back to get() and creates a stream from the buffer. * Storage implementations should override this for better streaming performance. * Headers include Content-Type, Content-Length, ETag, and Last-Modified. */ getStream({ id }: FileQuery, options?: OperationOptions): Promise<{ headers?: Record; size?: number; stream: Readable; }>; /** * Retrieves a list of uploaded files. * @param _limit Maximum number of files to return (default: 1000). * @returns Promise resolving to an array of file metadata objects. * @throws {Error} If not implemented by the storage backend. * @remarks Storage implementations must override this method. */ list(_limit?: number, _options?: OperationOptions): Promise; /** * Directory-style listing: collapse keys that share a path segment into common prefixes, * server-side, instead of returning every object. Adapters that can push the `delimiter` down to * the provider (S3 family, GCS, Azure) override this and set * {@link BaseStorage.supportsDelimiter} to `true`; the default throws so the `Files` facade knows * to fall back to synthesizing the view from {@link BaseStorage.list}. * @param _options Listing options — `delimiter` (required), plus optional `prefix`/`limit` and the usual per-call signal/timeout/retries. * @returns Direct-child files and the common prefixes one delimiter level below `prefix`. * @throws {UploadError} `METHOD_NOT_ALLOWED` when the adapter has no native delimiter support. */ listDirectory(_options?: OperationOptions & { delimiter: string; limit?: number; prefix?: string; }): Promise<{ files: TFile[]; prefixes: string[]; }>; /** * Updates file metadata with user-provided key-value pairs. * @param query File query containing the file ID to update. * @param query.id File ID to update. * @param metadata Partial file object containing fields to update. * @returns Promise resolving to the updated file object. * @throws {UploadError} If the file cannot be found (ERRORS.FILE_NOT_FOUND). * @remarks * Supports TTL (time-to-live) option: if metadata contains a 'ttl' field, * it will be converted to an 'expiredAt' timestamp. * TTL can be a number (milliseconds) or string (e.g., "1h", "30m", "7d"). */ update({ id }: FileQuery, metadata: Partial): Promise; /** * Creates a new upload and saves its metadata. * @param file File initialization configuration. * @param options Optional per-call signal/timeout/retries. * @returns Promise resolving to the created file object. */ abstract create(file: FileInit, options?: ConditionalOptions & OperationOptions): Promise; /** * Writes part and/or returns status of an upload. * @param part File part, query, or full file object to write. * @param options Optional per-call signal/timeout/retries. * @returns Promise resolving to the updated file object. */ abstract write(part: FilePart | FileQuery | TFile, options?: ConditionalOptions & OperationOptions): Promise; /** * Deletes an upload and its metadata. * @param query File query containing the file ID to delete. * @param query.id File ID to delete. * @param options Optional per-call signal/timeout/retries. * @returns Promise resolving to the deleted file object with status: "deleted". * @throws {UploadError} If the file metadata cannot be found. */ abstract delete(query: FileQuery, options?: ConditionalOptions & OperationOptions): Promise; /** * Deletes an upload this storage created. Unlike {@link BaseStorage.delete}, which some * providers apply to any object stored under a key, an ID without upload metadata is refused: * it may name an object the upload routes never created. * @param id File ID of the upload. * @param options Optional per-call signal/timeout/retries. * @returns The deleted file. * @throws {UploadError} FILE_NOT_FOUND when the ID has no upload metadata. */ deleteUpload(id: string, options?: OperationOptions): Promise; /** * {@link BaseStorage.deleteBatch} for uploads this storage created: IDs without upload metadata * fail as not found instead of being deleted (see {@link BaseStorage.deleteUpload}). * @param ids File IDs of the uploads. * @param options Optional per-call signal/timeout/retries. * @returns The deleted files and the failures, untracked IDs first. */ deleteUploads(ids: string[], options?: OperationOptions): Promise>; /** * Replaces the upload `id` with the bytes `write` stores, keeping the old file until the new one * is complete. `write` stores the replacement under a staging upload (`create`d from `config` * under a fresh id); only once it completed is it committed over `id` by * {@link BaseStorage.commitReplacement}. A failing `write` (the client breaking off, the * provider failing) deletes the staging upload and leaves `id` untouched. A staging upload a * crashed process leaves behind is an ordinary unfinished upload, which `purge` removes once it expires. * @param id File ID of the upload to replace. * @param config The replacement, validated like a new upload under `id`. * @param write Stores the replacement's bytes under the staging upload's id. * @returns The replaced upload. */ replaceUpload(id: string, config: FileInit, write: (stagingId: string) => Promise): Promise; /** * Puts a completed staging upload in place of the upload `id`, for {@link BaseStorage.replaceUpload}. * The default copies it through the adapter: it deletes `id`, creates it anew from `config` and * writes the staged bytes. Only a provider failure during that copy can lose the old file; the * client's body is fully stored by then. Adapters that can swap an object in one step override it. * @param id File ID of the upload to replace. * @param config The replacement. * @param staged The completed staging upload; {@link BaseStorage.replaceUpload} deletes it once * this succeeded, and keeps it when this fails (it may be the only complete copy left). * @returns The replaced upload. */ protected commitReplacement(id: string, config: FileInit, staged: TFile): Promise; /** * Copies an upload file to a new location. * @param name Source file name/ID. * @param destination Destination file name/ID. * @param options Optional copy options including storage class plus per-call signal/timeout/retries. * @returns Promise resolving to the copied file object. * @throws {UploadError} If the source file cannot be found. */ abstract copy(name: string, destination: string, options?: CopyConditionalOptions & OperationOptions & { storageClass?: string; }): Promise; /** * Moves an upload file to a new location. * @param name Source file name/ID. * @param destination Destination file name/ID. * @param options Optional per-call signal/timeout/retries. * @returns Promise resolving to the moved file object. * @throws {UploadError} If the source file cannot be found. */ abstract move(name: string, destination: string, options?: OperationOptions): Promise; /** * Deletes multiple files in a single batch operation. * @param ids Array of file IDs to delete. * @returns Promise resolving to batch operation response with successful and failed deletions. * @remarks * Processes all deletions in parallel using Promise.allSettled. * Individual failures do not stop the batch operation. * Each deletion is wrapped in error handling to capture failures. * Metrics are recorded for the batch operation and individual failures. * Returns both successful and failed operations with detailed error information. */ deleteBatch(ids: string[], options?: OperationOptions): Promise>; /** * Copies multiple files in a single batch operation. * @param operations Array of copy operations, each containing: * source: Source file ID. * destination: Destination file ID or path. * options: Optional copy options including storage class. * @returns Promise resolving to batch operation response with successful and failed copies. * @remarks * Processes all copies in parallel using Promise.allSettled. * Individual failures do not stop the batch operation. * Each copy operation is wrapped in error handling to capture failures. * Metrics are recorded for the batch operation and individual failures. * Returns both successful and failed operations with detailed error information. */ copyBatch(operations: { destination: string; options?: { storageClass?: string; }; source: string; }[]): Promise>; /** * Moves multiple files in a single batch operation. * @param operations Array of move operations, each containing: * source: Source file ID. * destination: Destination file ID or path. * @returns Promise resolving to batch operation response with successful and failed moves. * @remarks * Processes all moves in parallel using Promise.allSettled. * Individual failures do not stop the batch operation. * Each move operation is wrapped in error handling to capture failures. * Metrics are recorded for the batch operation and individual failures. * Returns both successful and failed operations with detailed error information. */ moveBatch(operations: { destination: string; source: string; }[]): Promise>; /** * Prevent upload from being accessed by multiple requests. * Returns a unique token that must be passed back to `unlock()` so a caller whose lock TTL'd * out cannot accidentally release a newly-acquired lock held by another request. */ protected lock(key: string): Promise; protected unlock(key: string, token?: string): Promise; /** * Run a function while holding the lock for `key`. The lock is released even if `fn` throws, * and only released when the caller still owns the lock (token-verified). Public so that * handlers can serialize cross-call read-modify-write sequences on the same upload (e.g. * appending to `_chunks` during chunked PATCH). */ withLock(key: string, function_: () => Promise): Promise; /** * Claims upload `id` for one writer across processes sharing the meta store: a claim token * ({@link WRITE_CLAIM_KEY}) is saved with a compare-and-swap against the record just read. Call * it before reading the state a write depends on (e.g. a TUS offset); while the claim holds, * no other claimant gets in, so that state can only change through the claimant. * * The claim expires LOCK_TTL_MS after its last renewal, so a crashed holder blocks nobody for * long; a live holder renews it until it releases it, at most LOCK_MAX_HOLD_MS. * * Without conditional saves in the meta store (or a store that gives no version), nothing is * claimed, and writers are only serialized within one process. * @param id Upload ID * @returns Releases the claim; never throws * @throws {UploadError} FILE_LOCKED when another writer holds a live claim or wins the race to it; * FILE_NOT_FOUND when there is no such upload */ claimWrite(id: string): Promise<() => Promise>; /** * Guards adapters that can only store an object in one request. Without it, a chunked or * resumable upload (e.g. TUS with a chunk size) stores its first chunk as the whole file * and reports the upload as completed, silently losing the rest of the data. * * Call it twice: before buffering the body (without `received`, so the declared * `part.contentLength` is checked and a doomed body is never read), and again after * buffering with the real byte count. * @param part The part being written * @param file The upload it belongs to * @param received Number of bytes this write carries; defaults to the declared `part.contentLength` * @throws {UploadError} METHOD_NOT_ALLOWED when the write isn't the whole file */ protected assertWholeFileWrite(part: { contentLength?: number; start?: number; }, file: TFile, received?: number | undefined): void; /** * Guards adapters that append parts strictly in order (S3 parts, Azure blocks, GCS resumable * sessions): a part must start exactly where the stored upload ends, otherwise it would leave * a gap in, or overlap, the assembled object. * @param part The part being written * @param file The upload it belongs to * @throws {UploadError} FILE_CONFLICT when `part.start` differs from `file.bytesWritten` */ protected assertContiguousWrite(part: { start?: number; }, file: TFile): void; protected isUnsupportedChecksum(algorithm?: string): boolean; protected startAutoPurge(purgeInterval: number): void; /** * Stops the auto-purge interval started by {@link startAutoPurge}, if any. * Safe to call multiple times. Call this to release a storage instance that * was constructed with `expiration.purgeInterval` so it can be garbage * collected and the event loop can drain. */ stopAutoPurge(): void; /** * Releases resources held by this storage instance (currently the auto-purge * timer). Provided so callers and the `Files` facade can tear a storage down * deterministically. */ close(): Promise; protected updateTimestamps(file: TFile): TFile; /** * Backend-resolved retry configuration that {@link runOperation} layers * per-call overrides on top of. Defaults to the `retryConfig` option; * subclasses that build their own `RetryConfig` (S3, Azure, Netlify, …) * override this. Unset options fall back to the retry engine's defaults. */ protected getRetryConfig(): RetryConfig | undefined; /** * Runs a backend SDK call with per-operation cancellation, timeout and * retry handling. * * The provided `signal` is the merge of the caller's `options.signal` and * a fresh per-attempt `options.timeout` deadline (via `AbortSignal.any` / * `AbortSignal.timeout`). Pass it to the underlying SDK so the provider * request is actually cancelled rather than merely abandoned. * * Aborted or timed-out calls are never retried. Pass * `{ replayable: false }` for operations whose body is a one-shot stream — * a consumed `Readable`/`ReadableStream` cannot be safely re-sent, so the * retry count is forced to zero. * @param options Per-call overrides (`signal`, `timeout`, `retries`). * @param function_ Receives the merged abort signal for the attempt. * @param operationConfig `replayable: false` disables retries for non-replayable bodies. */ protected runOperation(options: OperationOptions | undefined, function_: (signal: AbortSignal | undefined) => Promise, operationConfig?: { replayable?: boolean; }): Promise; /** * Instruments a storage operation with metrics and error tracking. * @param operation Operation name (e.g., "create", "delete", "copy"). * @param function_ The operation function to execute. * @param attributes Additional attributes to include in metrics. * @returns Promise resolving to the operation result. * @throws Re-throws any errors from the operation function. * @remarks * Records operation count, duration, and error metrics. * Tracks file sizes for operations returning file objects. * Error metrics include error messages for debugging. * All public methods should use this wrapper for consistent instrumentation. */ protected instrumentOperation(operation: string, function_: () => Promise, attributes?: Record): Promise; } export { defaultCloudStorageFileNameValidation as A, BaseStorage as B, ConditionalSupport as C, DiskStorageOptions as D, ErrorResponses as E, File as F, defaultFilesystemFileNameValidation as G, extractHttpStatus as H, getMetaVersion as I, isRetryableError as J, isUploadError as K, LocalMetaStorageOptions as L, MetaStorage as M, mapStatusToErrorCode as N, OperationOptions as O, PurgeList as P, retry as Q, RetryConfig as R, StoredObject as S, setMetaVersion as T, UploadFile as U, throwErrorCode as V, WRITE_CLAIM_KEY as W, wrapStorageError as X, Cache as Y, UploadError as a, UploadEventType as b, FileReturn as c, createRetryWrapper as d, FileInit as e, BaseStorageOptions as f, ConditionalOptions as g, FilePart as h, FileQuery as i, CopyConditionalOptions as j, Metadata as k, BatchOperationResponse as l, BatchOperationResult as m, DiskStorageWithChecksumOptions as n, ERRORS as o, ErrorMap as p, ExpirationOptions as q, META_VERSION as r, MetaStorageOptions as s, OnComplete as t, OnCreate as u, OnDelete as v, OnError as w, OnUpdate as x, UploadPostOptions as y, UploadPostPolicy as z };