s3mini
Version:
đź‘¶ Tiny & fast S3 client for node and edge computing platforms
584 lines (580 loc) • 28.1 kB
TypeScript
interface S3Config {
accessKeyId: string;
secretAccessKey: string;
endpoint: string;
region?: string;
requestSizeInBytes?: number;
requestAbortTimeout?: number;
logger?: Logger;
fetch?: typeof fetch;
}
interface SSECHeaders {
'x-amz-server-side-encryption-customer-algorithm': string;
'x-amz-server-side-encryption-customer-key': string;
'x-amz-server-side-encryption-customer-key-md5': string;
}
interface AWSHeaders {
[k: `x-amz-${string}`]: string;
}
interface Logger {
info: (message: string, ...args: unknown[]) => void;
warn: (message: string, ...args: unknown[]) => void;
error: (message: string, ...args: unknown[]) => void;
}
interface UploadPart {
partNumber: number;
etag: string;
}
interface ListObject {
Key: string;
Size: number;
LastModified: Date;
ETag: string;
StorageClass: string;
}
interface CompleteMultipartUploadResult {
location: string;
bucket: string;
key: string;
etag: string;
eTag: string;
ETag: string;
}
interface ListBucketResult {
keyCount: string;
contents?: Array<Record<string, unknown>>;
}
interface ListBucketError {
error: {
code: string;
message: string;
};
}
type ListBucketResponse = {
listBucketResult: ListBucketResult;
} | ListBucketError;
interface ListMultipartUploadSuccess {
listMultipartUploadsResult: {
bucket: string;
key: string;
uploadId: string;
size?: number;
mtime?: Date;
etag?: string;
eTag?: string;
parts: UploadPart[];
isTruncated: boolean;
uploads: UploadPart[];
};
}
interface MultipartUploadError {
error: {
code: string;
message: string;
};
}
interface ErrorWithCode {
code?: string;
cause?: {
code?: string;
};
}
type ListMultipartUploadResponse = ListMultipartUploadSuccess | MultipartUploadError;
type HttpMethod = 'POST' | 'GET' | 'HEAD' | 'PUT' | 'DELETE';
type ExistResponseCode = false | true | null;
interface CopyObjectOptions {
/**
* Specifies whether the metadata is copied from the source object or replaced with metadata provided in the request.
* Valid values: 'COPY' | 'REPLACE'
* Default: 'COPY'
*/
metadataDirective?: 'COPY' | 'REPLACE';
/**
* Metadata to be set on the destination object when metadataDirective is 'REPLACE'.
* Keys can be provided with or without 'x-amz-meta-' prefix.
*/
metadata?: Record<string, string>;
contentType?: string;
/**
* Storage class for the destination object.
* Valid values: 'STANDARD' | 'REDUCED_REDUNDANCY' | 'STANDARD_IA' | 'ONEZONE_IA' | 'INTELLIGENT_TIERING' | 'GLACIER' | 'DEEP_ARCHIVE' | 'GLACIER_IR'
*/
storageClass?: string;
/**
* Specifies whether the object tag-set is copied from the source object or replaced with tag-set provided in the request.
* Valid values: 'COPY' | 'REPLACE'
*/
taggingDirective?: 'COPY' | 'REPLACE';
/**
* If the bucket is configured as a website, redirects requests for this object to another object or URL.
*/
websiteRedirectLocation?: string;
/**
* Server-Side Encryption with Customer-Provided Keys headers for the source object.
* Should include:
* - x-amz-copy-source-server-side-encryption-customer-algorithm
* - x-amz-copy-source-server-side-encryption-customer-key
* - x-amz-copy-source-server-side-encryption-customer-key-MD5
*/
sourceSSECHeaders?: Record<string, string | number>;
destinationSSECHeaders?: SSECHeaders;
additionalHeaders?: Record<string, string | number>;
}
interface CopyObjectResult {
etag: string;
lastModified?: Date;
}
/**
* Where Buffer is available, e.g. when @types/node is loaded, we want to use it.
* But it should be excluded in other environments (e.g. Cloudflare).
*/
type MaybeBuffer = typeof globalThis extends {
Buffer?: infer B;
} ? B extends new (...a: unknown[]) => unknown ? InstanceType<B> : ArrayBuffer | Uint8Array : ArrayBuffer | Uint8Array;
/**
* S3 class for interacting with S3-compatible object storage services.
* This class provides methods for common S3 operations such as uploading, downloading,
* and deleting objects, as well as multipart uploads.
*
* @class
* @example
* const s3 = new S3mini({
* accessKeyId: 'your-access-key',
* secretAccessKey: 'your-secret-key',
* endpoint: 'https://your-s3-endpoint.com/bucket-name',
* region: 'auto' // by default is auto
* });
*
* // Upload a file
* await s3.putObject('example.txt', 'Hello, World!');
*
* // Download a file
* const content = await s3.getObject('example.txt');
*
* // Delete a file
* await s3.deleteObject('example.txt');
*/
declare class S3mini {
#private;
readonly endpoint: URL;
readonly region: string;
readonly bucketName: string;
readonly requestSizeInBytes: number;
readonly requestAbortTimeout?: number;
readonly logger?: Logger;
readonly _fetch: typeof fetch;
private signingKeyDate?;
private signingKey?;
constructor({ accessKeyId, secretAccessKey, endpoint, region, requestSizeInBytes, requestAbortTimeout, logger, fetch, }: S3Config);
private _sanitize;
private _log;
private _validateConstructorParams;
/**
* Check if credentials are configured (non-empty).
* @returns true if both accessKeyId and secretAccessKey are non-empty.
*/
private _hasCredentials;
private _ensureValidUrl;
private _validateMethodIsGetOrHead;
private _checkKey;
private _checkDelimiter;
private _checkPrefix;
private _checkOpts;
private _filterIfHeaders;
private _validateData;
private _validateUploadPartParams;
private _sign;
private _signedRequest;
/**
* Sanitizes an ETag value by removing surrounding quotes and whitespace.
* Still returns RFC compliant ETag. https://www.rfc-editor.org/rfc/rfc9110#section-8.8.3
* @param {string} etag - The ETag value to sanitize.
* @returns {string} The sanitized ETag value.
* @example
* const cleanEtag = s3.sanitizeETag('"abc123"'); // Returns: 'abc123'
*/
sanitizeETag(etag: string): string;
/**
* Creates a new bucket.
* This method sends a request to create a new bucket in the specified in endpoint.
* @returns A promise that resolves to true if the bucket was created successfully, false otherwise.
*/
createBucket(): Promise<boolean>;
private _extractBucketName;
/**
* Checks if a bucket exists.
* This method sends a request to check if the specified bucket exists in the S3-compatible service.
* @returns A promise that resolves to true if the bucket exists, false otherwise.
*/
bucketExists(): Promise<boolean>;
/**
* Lists objects in the bucket with optional filtering and no pagination.
* This method retrieves all objects matching the criteria (not paginated like listObjectsV2).
* @param {string} [delimiter='/'] - The delimiter to use for grouping objects.
* @param {string} [prefix=''] - The prefix to filter objects by.
* @param {number} [maxKeys] - The maximum number of keys to return. If not provided, all keys will be returned.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @returns {Promise<IT.ListObject[] | null>} A promise that resolves to an array of objects or null if the bucket is empty.
* @example
* // List all objects
* const objects = await s3.listObjects();
*
* // List objects with prefix
* const photos = await s3.listObjects('/', 'photos/', 100);
*/
listObjects(delimiter?: string, prefix?: string, maxKeys?: number, opts?: Record<string, unknown>): Promise<ListObject[] | null>;
/**
* Lists objects in the bucket with optional filtering and pagination using a continuation token.
* This method retrieves objects matching the criteria (paginated like listObjectsV2).
* @param {string} [delimiter='/'] - The delimiter to use for grouping objects.
* @param {string} [prefix=''] - The prefix to filter objects by.
* @param {number} [maxKeys] - The maximum number of keys to return. Uses a default value of 100.
* @param {string} [nextContinuationToken] - The nextContinuationToken to continue previous results. If not provided, starts from the beginning.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @returns {Promise<{objects: IT.ListObject[] | null; nextContinuationToken?: string } | undefined | null>} A promise that resolves to an array of objects or null if the bucket is empty, along with nextContinuationToken if there are more reccords.
* @example
* // List all objects
* const { objects, nextContinuationToken } = await s3.listObjectsPaged();
*
* // List 200 objects with prefix
* const photos = await s3.listObjectsPaged('/', 'photos/', 200, "token...");
*/
listObjectsPaged(delimiter?: string, prefix?: string, maxKeys?: number, nextContinuationToken?: string, opts?: Record<string, unknown>): Promise<{
objects: ListObject[] | null;
nextContinuationToken?: string;
} | undefined | null>;
private _fetchObjectBatch;
private _buildListObjectsQuery;
private _handleListObjectsError;
private _parseListObjectsResponse;
private _extractObjectsFromResponse;
private _extractContinuationToken;
/**
* Lists multipart uploads in the bucket.
* This method sends a request to list multipart uploads in the specified bucket.
* @param {string} [delimiter='/'] - The delimiter to use for grouping uploads.
* @param {string} [prefix=''] - The prefix to filter uploads by.
* @param {IT.HttpMethod} [method='GET'] - The HTTP method to use for the request (GET or HEAD).
* @param {Record<string, string | number | boolean | undefined>} [opts={}] - Additional options for the request.
* @returns A promise that resolves to a list of multipart uploads or an error.
*/
listMultipartUploads(delimiter?: string, prefix?: string, method?: HttpMethod, opts?: Record<string, string | number | boolean | undefined>): Promise<ListMultipartUploadSuccess | MultipartUploadError>;
/**
* Get an object from the S3-compatible service.
* This method sends a request to retrieve the specified object from the S3-compatible service.
* @param {string} key - The key of the object to retrieve.
* @param {Record<string, unknown>} [opts] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns A promise that resolves to the object data (string) or null if not found.
*/
getObject(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<string | null>;
/**
* Get an object response from the S3-compatible service.
* This method sends a request to retrieve the specified object and returns the full response.
* @param {string} key - The key of the object to retrieve.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns A promise that resolves to the Response object or null if not found.
*/
getObjectResponse(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<Response | null>;
/**
* Get an object as an ArrayBuffer from the S3-compatible service.
* This method sends a request to retrieve the specified object and returns it as an ArrayBuffer.
* @param {string} key - The key of the object to retrieve.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns A promise that resolves to the object data as an ArrayBuffer or null if not found.
*/
getObjectArrayBuffer(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<ArrayBuffer | null>;
/**
* Get an object as JSON from the S3-compatible service.
* This method sends a request to retrieve the specified object and returns it as JSON.
* @param {string} key - The key of the object to retrieve.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns A promise that resolves to the object data as JSON or null if not found.
*/
getObjectJSON<T = unknown>(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<T | null>;
/**
* Get an object with its ETag from the S3-compatible service.
* This method sends a request to retrieve the specified object and its ETag.
* @param {string} key - The key of the object to retrieve.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns A promise that resolves to an object containing the ETag and the object data as an ArrayBuffer or null if not found.
*/
getObjectWithETag(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<{
etag: string | null;
data: ArrayBuffer | null;
}>;
/**
* Get an object as a raw response from the S3-compatible service.
* This method sends a request to retrieve the specified object and returns the raw response.
* @param {string} key - The key of the object to retrieve.
* @param {boolean} [wholeFile=true] - Whether to retrieve the whole file or a range.
* @param {number} [rangeFrom=0] - The starting byte for the range (if not whole file).
* @param {number} [rangeTo=this.requestSizeInBytes] - The ending byte for the range (if not whole file).
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns A promise that resolves to the Response object.
*/
getObjectRaw(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<Response>;
/**
* Get the content length of an object.
* This method sends a HEAD request to retrieve the content length of the specified object.
* @param {string} key - The key of the object to retrieve the content length for.
* @returns A promise that resolves to the content length of the object in bytes, or 0 if not found.
* @throws {Error} If the content length header is not found in the response.
*/
getContentLength(key: string, ssecHeaders?: SSECHeaders): Promise<number>;
/**
* Checks if an object exists in the S3-compatible service.
* This method sends a HEAD request to check if the specified object exists.
* @param {string} key - The key of the object to check.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @returns A promise that resolves to true if the object exists, false if not found, or null if ETag mismatch.
*/
objectExists(key: string, opts?: Record<string, unknown>): Promise<ExistResponseCode>;
/**
* Retrieves the ETag of an object without downloading its content.
* @param {string} key - The key of the object to retrieve the ETag for.
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns {Promise<string | null>} A promise that resolves to the ETag value or null if the object is not found.
* @throws {Error} If the ETag header is not found in the response.
* @example
* const etag = await s3.getEtag('path/to/file.txt');
* if (etag) {
* console.log(`File ETag: ${etag}`);
* }
*/
getEtag(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<string | null>;
/**
* Uploads an object to the S3-compatible service.
* @param {string} key - The key/path where the object will be stored.
* @param {string | Buffer} data - The data to upload (string or Buffer).
* @param {string} [fileType='application/octet-stream'] - The MIME type of the file.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @param {IT.AWSHeaders} [additionalHeaders] - Additional x-amz-* headers specific to this request, if any.
* @returns {Promise<Response>} A promise that resolves to the Response object from the upload request.
* @throws {TypeError} If data is not a string or Buffer.
* @example
* // Upload text file
* await s3.putObject('hello.txt', 'Hello, World!', 'text/plain');
*
* // Upload binary data
* const buffer = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
* await s3.putObject('image.png', buffer, 'image/png');
*/
putObject(key: string, data: string | MaybeBuffer, fileType?: string, ssecHeaders?: SSECHeaders, additionalHeaders?: AWSHeaders): Promise<Response>;
/**
* Initiates a multipart upload and returns the upload ID.
* @param {string} key - The key/path where the object will be stored.
* @param {string} [fileType='application/octet-stream'] - The MIME type of the file.
* @param {IT.SSECHeaders?} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns {Promise<string>} A promise that resolves to the upload ID for the multipart upload.
* @throws {TypeError} If key is invalid or fileType is not a string.
* @throws {Error} If the multipart upload fails to initialize.
* @example
* const uploadId = await s3.getMultipartUploadId('large-file.zip', 'application/zip');
* console.log(`Started multipart upload: ${uploadId}`);
*/
getMultipartUploadId(key: string, fileType?: string, ssecHeaders?: SSECHeaders): Promise<string>;
/**
* Uploads a part in a multipart upload.
* @param {string} key - The key of the object being uploaded.
* @param {string} uploadId - The upload ID from getMultipartUploadId.
* @param {Buffer | string} data - The data for this part.
* @param {number} partNumber - The part number (must be between 1 and 10,000).
* @param {Record<string, unknown>} [opts={}] - Additional options for the request.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns {Promise<IT.UploadPart>} A promise that resolves to an object containing the partNumber and etag.
* @throws {TypeError} If any parameter is invalid.
* @example
* const part = await s3.uploadPart(
* 'large-file.zip',
* uploadId,
* partData,
* 1
* );
* console.log(`Part ${part.partNumber} uploaded with ETag: ${part.etag}`);
*/
uploadPart(key: string, uploadId: string, data: MaybeBuffer | string, partNumber: number, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<UploadPart>;
/**
* Completes a multipart upload by combining all uploaded parts.
* @param {string} key - The key of the object being uploaded.
* @param {string} uploadId - The upload ID from getMultipartUploadId.
* @param {Array<IT.UploadPart>} parts - Array of uploaded parts with partNumber and etag.
* @returns {Promise<IT.CompleteMultipartUploadResult>} A promise that resolves to the completion result containing the final ETag.
* @throws {Error} If the multipart upload fails to complete.
* @example
* const result = await s3.completeMultipartUpload(
* 'large-file.zip',
* uploadId,
* [
* { partNumber: 1, etag: 'abc123' },
* { partNumber: 2, etag: 'def456' }
* ]
* );
* console.log(`Upload completed with ETag: ${result.etag}`);
*/
completeMultipartUpload(key: string, uploadId: string, parts: Array<UploadPart>): Promise<CompleteMultipartUploadResult>;
/**
* Aborts a multipart upload and removes all uploaded parts.
* @param {string} key - The key of the object being uploaded.
* @param {string} uploadId - The upload ID to abort.
* @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
* @returns {Promise<object>} A promise that resolves to an object containing the abort status and details.
* @throws {TypeError} If key or uploadId is invalid.
* @throws {Error} If the abort operation fails.
* @example
* try {
* const result = await s3.abortMultipartUpload('large-file.zip', uploadId);
* console.log('Upload aborted:', result.status);
* } catch (error) {
* console.error('Failed to abort upload:', error);
* }
*/
abortMultipartUpload(key: string, uploadId: string, ssecHeaders?: SSECHeaders): Promise<object>;
private _buildCompleteMultipartUploadXml;
/**
* Executes the copy operation for local copying (same bucket/endpoint).
* @private
*/
private _executeCopyOperation;
/**
* Copies an object within the same bucket.
*
* @param {string} sourceKey - The key of the source object to copy
* @param {string} destinationKey - The key where the object will be copied to
* @param {IT.CopyObjectOptions} [options={}] - Copy operation options
* @param {string} [options.metadataDirective='COPY'] - How to handle metadata ('COPY' | 'REPLACE')
* @param {Record<string,string>} [options.metadata={}] - New metadata (only used if metadataDirective='REPLACE')
* @param {string} [options.contentType] - New content type for the destination object
* @param {string} [options.storageClass] - Storage class for the destination object
* @param {string} [options.taggingDirective] - How to handle object tags ('COPY' | 'REPLACE')
* @param {string} [options.websiteRedirectLocation] - Website redirect location for the destination
* @param {IT.SSECHeaders} [options.sourceSSECHeaders={}] - Encryption headers for reading source (if encrypted)
* @param {IT.SSECHeaders} [options.destinationSSECHeaders={}] - Encryption headers for destination
* @param {IT.AWSHeaders} [options.additionalHeaders={}] - Extra x-amz-* headers
*
* @returns {Promise<IT.CopyObjectResult>} Copy result with etag and lastModified date
* @throws {TypeError} If sourceKey or destinationKey is invalid
* @throws {Error} If copy operation fails or S3 returns an error
*
* @example
* // Simple copy
* const result = await s3.copyObject('report-2024.pdf', 'archive/report-2024.pdf');
* console.log(`Copied with ETag: ${result.etag}`);
*
* @example
* // Copy with new metadata and content type
* const result = await s3.copyObject('data.csv', 'processed/data.csv', {
* metadataDirective: 'REPLACE',
* metadata: {
* 'processed-date': new Date().toISOString(),
* 'original-name': 'data.csv'
* },
* contentType: 'text/csv; charset=utf-8'
* });
*
* @example
* // Copy encrypted object (Cloudflare R2 SSE-C)
* const ssecKey = 'n1TKiTaVHlYLMX9n0zHXyooMr026vOiTEFfT+719Hho=';
* await s3.copyObject('sensitive.json', 'backup/sensitive.json', {
* sourceSSECHeaders: {
* 'x-amz-copy-source-server-side-encryption-customer-algorithm': 'AES256',
* 'x-amz-copy-source-server-side-encryption-customer-key': ssecKey,
* 'x-amz-copy-source-server-side-encryption-customer-key-md5': 'gepZmzgR7Be/1+K1Aw+6ow=='
* },
* destinationSSECHeaders: {
* 'x-amz-server-side-encryption-customer-algorithm': 'AES256',
* 'x-amz-server-side-encryption-customer-key': ssecKey,
* 'x-amz-server-side-encryption-customer-key-md5': 'gepZmzgR7Be/1+K1Aw+6ow=='
* }
* });
*/
copyObject(sourceKey: string, destinationKey: string, options?: CopyObjectOptions): Promise<CopyObjectResult>;
private _buildSSECHeaders;
/**
* Moves an object within the same bucket (copy + delete atomic-like operation).
*
* WARNING: Not truly atomic - if delete fails after successful copy, the object
* will exist in both locations. Consider your use case carefully.
*
* @param {string} sourceKey - The key of the source object to move
* @param {string} destinationKey - The key where the object will be moved to
* @param {IT.CopyObjectOptions} [options={}] - Options passed to the copy operation
*
* @returns {Promise<IT.CopyObjectResult>} Result from the copy operation
* @throws {TypeError} If sourceKey or destinationKey is invalid
* @throws {Error} If copy succeeds but delete fails (includes copy result in error)
*
* @example
* // Simple move
* await s3.moveObject('temp/upload.tmp', 'files/document.pdf');
*
* @example
* // Move with metadata update
* await s3.moveObject('unprocessed/image.jpg', 'processed/image.jpg', {
* metadataDirective: 'REPLACE',
* metadata: {
* 'status': 'processed',
* 'processed-at': Date.now().toString()
* },
* contentType: 'image/jpeg'
* });
*
* @example
* // Safe move with error handling
* try {
* const result = await s3.moveObject('inbox/file.dat', 'archive/file.dat');
* console.log(`Moved successfully: ${result.etag}`);
* } catch (error) {
* // Check if copy succeeded but delete failed
* if (error.message.includes('delete source object after successful copy')) {
* console.warn('File copied but not deleted from source - manual cleanup needed');
* }
* }
*/
moveObject(sourceKey: string, destinationKey: string, options?: CopyObjectOptions): Promise<CopyObjectResult>;
private _buildMetadataHeaders;
private _parseCopyObjectResponse;
/**
* Deletes an object from the bucket.
* This method sends a request to delete the specified object from the bucket.
* @param {string} key - The key of the object to delete.
* @returns A promise that resolves to true if the object was deleted successfully, false otherwise.
*/
deleteObject(key: string): Promise<boolean>;
private _deleteObjectsProcess;
/**
* Deletes multiple objects from the bucket.
* @param {string[]} keys - An array of object keys to delete.
* @returns A promise that resolves to an array of booleans indicating success for each key in order.
*/
deleteObjects(keys: string[]): Promise<boolean[]>;
private _sendRequest;
private _parseErrorXml;
private _handleErrorResponse;
private _buildCanonicalQueryString;
private _getSignatureKey;
}
/**
* Sanitize ETag value by removing quotes and XML entities
* @param etag ETag value to sanitize
* @returns Sanitized ETag
*/
declare const sanitizeETag: (etag: string) => string;
/**
* Run async-returning tasks in batches with an *optional* minimum
* spacing (minIntervalMs) between the *start* times of successive batches.
*
* @param {Iterable<() => Promise<unknonw>>} tasks – functions returning Promises
* @param {number} [batchSize=30] – max concurrent requests
* @param {number} [minIntervalMs=0] – ≥0; 0 means “no pacing”
* @returns {Promise<Array<PromiseSettledResult<T>>>}
*/
declare const runInBatches: <T = unknown>(tasks: Iterable<() => Promise<T>>, batchSize?: number, minIntervalMs?: number) => Promise<Array<PromiseSettledResult<T>>>;
export { S3mini, S3mini as default, runInBatches, sanitizeETag };
export type { CompleteMultipartUploadResult, ErrorWithCode, ExistResponseCode, ListBucketResponse, ListMultipartUploadResponse, Logger, S3Config, UploadPart };