import { Readable } from 'node:stream'; import { F as File, C as ConditionalSupport, M as MetaStorage, d as createRetryWrapper, R as RetryConfig, e as FileInit, f as BaseStorageOptions, g as ConditionalOptions, O as OperationOptions, h as FilePart, i as FileQuery, j as CopyConditionalOptions, c as FileReturn, S as StoredObject, B as BaseStorage } from "./storage.d-Cnjmzau5.js"; /** * Part interface for multipart uploads. */ interface Part { ETag?: string; PartNumber: number; Size?: number; } /** * An unfinished multipart upload, as ListMultipartUploads reports it. */ interface MultipartUpload { Initiated?: Date; Key?: string; UploadId?: string; } /** * Base file type for S3-compatible storage. */ interface S3CompatibleFile extends File { Parts?: Part[]; partSize?: number; partsUrls?: string[]; UploadId?: string; uri?: string; } /** * Per-call options forwarded to the underlying AWS SDK send(). */ interface S3CallOptions { /** Forwarded to `client.send(command, { abortSignal })`. */ signal?: AbortSignal; } /** * S3 API operations interface that must be implemented by concrete storage classes. */ interface S3ApiOperations { abortMultipartUpload: (params: { Bucket: string; Key: string; UploadId: string; }, options?: S3CallOptions) => Promise; checkBucketAccess: (params: { Bucket: string; }) => Promise; completeMultipartUpload: (params: { Bucket: string; IfMatch?: string; IfNoneMatch?: string; Key: string; Parts: { ETag: string; PartNumber: number; }[]; UploadId: string; }, options?: S3CallOptions) => Promise<{ ETag?: string; Location: string; }>; copyObject: (params: { ACL?: string; Bucket: string; CopySource: string; CopySourceIfMatch?: string; IfMatch?: string; IfNoneMatch?: string; Key: string; StorageClass?: string; }, options?: S3CallOptions) => Promise; createMultipartUpload: (params: { ACL?: string; Bucket: string; ContentType?: string; Key: string; Metadata?: Record; StorageClass?: string; }, options?: S3CallOptions) => Promise<{ UploadId: string; }>; deleteObject: (params: { Bucket: string; IfMatch?: string; Key: string; }, options?: S3CallOptions) => Promise; getObject: (params: { Bucket: string; IfMatch?: string; Key: string; Range?: string; }, options?: S3CallOptions) => Promise<{ Body?: ReadableStream | Readable; ContentLength?: number; ContentType?: string; ETag?: string; Expires?: Date; LastModified?: Date; Metadata?: Record; }>; getPresignedUrl: (params: { Bucket: string; expiresIn: number; Key: string; PartNumber: number; UploadId: string; }) => Promise; headObject: (params: { Bucket: string; Key: string; }, options?: S3CallOptions) => Promise<{ ContentLength?: number; ContentType?: string; ETag?: string; Expires?: Date; LastModified?: Date; Metadata?: Record; }>; listMultipartUploads: (params: { Bucket: string; KeyMarker?: string; UploadIdMarker?: string; }, options?: S3CallOptions) => Promise<{ IsTruncated?: boolean; NextKeyMarker?: string; NextUploadIdMarker?: string; Uploads?: MultipartUpload[]; }>; listObjectsV2: (params: { Bucket: string; ContinuationToken?: string; Delimiter?: string; MaxKeys?: number; Prefix?: string; }, options?: S3CallOptions) => Promise<{ CommonPrefixes?: { Prefix?: string; }[]; Contents?: { Key?: string; LastModified?: Date; }[]; IsTruncated?: boolean; NextContinuationToken?: string; }>; listParts: (params: { Bucket: string; Key: string; PartNumberMarker?: string; UploadId: string; }, options?: S3CallOptions) => Promise<{ IsTruncated?: boolean; NextPartNumberMarker?: string; Parts?: Part[]; }>; uploadPart: (params: { Body: Readable | ReadableStream | Uint8Array; Bucket: string; ContentLength?: number; ContentMD5?: string; Key: string; PartNumber: number; UploadId: string; }, options?: S3CallOptions) => Promise<{ ETag: string; }>; /** Copies a byte range (`bytes=a-b`) of an object into a part of a multipart upload. */ uploadPartCopy: (params: { Bucket: string; CopySource: string; CopySourceIfMatch?: string; CopySourceRange: string; Key: string; PartNumber: number; UploadId: string; }, options?: S3CallOptions) => Promise<{ ETag: string; }>; } /** * Base class for S3-compatible storage implementations. * Contains all shared business logic for S3 operations. * @template TFile The file type used by this storage backend. */ declare abstract class S3BaseStorage extends BaseStorage { /** * Only `md5` is verified by S3 itself (`Content-MD5` on UploadPart, `BadDigest` on mismatch). * The flexible `x-amz-checksum-*` algorithms (sha1/sha256/crc32/crc32c) are not offered: S3 * rejects a part checksum whose algorithm was not declared on CreateMultipartUpload, and a * TUS client only announces its algorithm per PATCH, after the multipart upload exists. */ override checksumTypes: string[]; override readonly supportsRange: boolean; override readonly supportsDelimiter: boolean; /** Parts are appended in order (see assertContiguousWrite). */ override readonly sequentialWrites: boolean; /** A part's length goes into the provider request before its bytes, so it must be known. */ override readonly requiresContentLength: boolean; /** * S3 evaluates `If-Match` / `If-None-Match` on CompleteMultipartUpload, GetObject, DeleteObject * and CopyObject (plus `x-amz-copy-source-if-match`). Enabled through the `conditional` option; * S3-compatible services differ in which of these headers they honour. */ override readonly conditionalSupport: ConditionalSupport; /** Set through the `uploadPost` option: not every S3-compatible service accepts browser-form POSTs. */ override readonly supportsUploadPost: boolean; override readonly maxSignedUrlExpiresIn: number | undefined; protected bucket: string; /** Set here for a caller-supplied or local meta storage, otherwise by the subclass constructor. */ protected meta: MetaStorage; /** * S3 multipart upload does not allow more than 10000 parts. */ protected readonly MAX_PARTS = 1e4; protected readonly partSize: number; protected readonly retry: ReturnType; protected readonly resolvedRetryConfig: RetryConfig; /** * Abstract method to get S3 API operations implementation. */ protected abstract getS3Api(): S3ApiOperations; /** * Abstract method to get the file class constructor. */ protected abstract getFileClass(): new (config: FileInit) => TFile; /** * Abstract method to get ACL value. */ protected abstract getAcl(): string | undefined; /** * Abstract method for access check. */ protected abstract accessCheck(maxWaitTime?: number): Promise; constructor(config: Omit, "metaStorage" | "retryConfig"> & { bucket: string; clientDirectUpload?: boolean; /** * Advertise and send conditional (ETag) requests: every kind, or only the ones set. Direct * client uploads never are. */ conditional?: boolean | Partial; metaStorage?: MetaStorage; metaStorageConfig?: unknown; partSize?: number | string; retryConfig?: RetryConfig; /** Sign browser-form POST policies ({@link BaseStorage.getUploadPost}). */ uploadPost?: boolean; }); protected override getRetryConfig(): RetryConfig; /** * Creates a new S3 multipart upload. */ create(config: FileInit, options?: ConditionalOptions & OperationOptions): Promise; /** * Writes data to an S3 multipart upload. */ write(part: FilePart | FileQuery | TFile, options?: ConditionalOptions & OperationOptions): Promise; /** {@link S3BaseStorage.write} under the upload's lock; `conditional` is a parked conditional upload. */ private writeLocked; /** * Deletes an upload and its metadata: aborts an unfinished multipart upload, or deletes the * object of a finished one. An object without metadata (completed by an older version, which * dropped it) is deleted too. */ delete({ id }: FileQuery, options?: ConditionalOptions & OperationOptions): Promise; /** * Copies an upload's object to `destination`, a key in the same bucket. The copy is an object * without upload metadata. */ copy(name: string, destination: string, options?: CopyConditionalOptions & OperationOptions & { storageClass?: string; }): Promise; /** * Copies the staged object over the upload's server-side: CopyObject replaces it in one step, * without the bytes passing through this process. {@link BaseStorage.replaceUpload} then * deletes the staged object and its record. */ protected override commitReplacement(id: string, config: FileInit, staged: TFile): Promise; /** * Moves an upload's object to `destination`, a key in the same bucket, and drops the source * object and its metadata. */ move(name: string, destination: string, options?: OperationOptions): Promise; /** * The uploads {@link BaseStorage.purge} checks: every object in the bucket (purge skips the ones * without upload metadata), and this storage's own unfinished multipart uploads, which no object * listing shows. Another client's multipart upload in a shared bucket is not ours to abort; * untracked leftovers are for an S3 lifecycle rule (AbortIncompleteMultipartUpload). */ protected override listUploads(): Promise; /** * Lists files in the bucket. */ override list(limit?: number, options?: OperationOptions): Promise; /** * Directory-style listing via S3's native `Delimiter`/`Prefix` — the provider returns the direct * child objects plus the `CommonPrefixes` ("subdirectories") one delimiter level below `prefix`, * so the whole subtree never has to be fetched. Pages until exhausted or `limit` direct files * have been collected; common prefixes are accumulated (deduped) across pages. */ override listDirectory(options?: OperationOptions & { delimiter: string; limit?: number; prefix?: string; }): Promise<{ files: TFile[]; prefixes: string[]; }>; /** * Whether an upload's object exists: the one its metadata names, or, for an ID without * metadata, the object stored under it. An unfinished upload has no object yet. */ override exists({ id }: FileQuery, options?: OperationOptions): Promise; /** * Gets an uploaded file by ID. */ get({ id }: FileQuery, options?: ConditionalOptions & OperationOptions & { range?: { end?: number; start: number; }; }): Promise; /** * Gets file stream (abstract - must be implemented by subclasses due to stream differences). */ abstract override getStream(query: FileQuery, options?: OperationOptions): Promise<{ headers?: Record; size?: number; stream: Readable; }>; /** * Builds presigned URLs for client uploads. */ protected buildPresigned(file: TFile): Promise; /** * Gets presigned URLs for all parts. */ protected getPartsPresignedUrls(file: TFile): Promise; /** * Gets parts for a multipart upload. */ protected getParts(file: TFile): Promise; /** * Completes a multipart upload. */ protected completeMultipartUpload(file: TFile, condition?: ConditionalOptions): Promise<{ ETag?: string; Location: string; }>; /** * Aborts a multipart upload. */ protected abortMultipartUpload(file: TFile, options?: OperationOptions): Promise; /** Every lookup of an object without upload metadata (exists, delete, copy, move) comes through here. */ protected override statObject(id: string, options?: OperationOptions): Promise; /** Also refuses a name {@link S3BaseStorage.create} would refuse, so a replacement fails before it is staged. */ override validateInit(config: FileInit): Promise; /** The key get and getStream read: never a metadata record, which an id without metadata would name. */ protected override readableName(id: string): Promise; /** Asks S3 for the object's ETag: the record's may predate a write by another client. */ protected override currentETag(file: TFile, options?: OperationOptions): Promise; /** * Whether a bucket key is a metadata record: they live next to the objects when the meta * storage uses this bucket, and must not be listed or purged as uploads of their own. With the * metadata stored elsewhere, a key ending in the suffix is an ordinary object. */ protected isMetaKey(key: string): boolean; /** * Refuses an object key that names a metadata record in this bucket: an upload stored there * would overwrite the record, and a read, copy or delete of it would expose or drop the record. * @throws {UploadError} INVALID_FILE_NAME */ protected assertNotMetaKey(key: string): void; /** * The upload's metadata, or for an ID without any, the object stored under it. `tracked` tells * whether there is metadata to delete with the upload. */ protected findUpload(id: string, options?: OperationOptions): Promise<{ file: TFile; tracked: boolean; }>; /** * Copies `file`'s object to `destination` in the same bucket. */ protected copyObject(file: TFile, destination: string, options?: CopyConditionalOptions & OperationOptions & { storageClass?: string; }): Promise; /** * CopyObject refuses sources over 5 GiB: copy those part by part (UploadPartCopy) into a * multipart upload, which takes over the source's type and metadata as CopyObject does. */ private copyObjectInParts; /** * Completes the multipart upload and keeps the finished upload's metadata, as the other * storages do. A failed completion leaves the metadata of the unfinished upload in place, so it * can be retried or aborted. */ protected internalOnComplete: (file: TFile, condition?: ConditionalOptions) => Promise<[{ ETag?: string; Location: string; }, TFile]>; } export { MultipartUpload as M, Part as P, S3CallOptions as S, S3ApiOperations as a, S3BaseStorage as b };