UNPKG

s3mini

Version:

đź‘¶ Tiny & fast S3 client for node and edge computing platforms

446 lines (442 loc) • 21.7 kB
interface S3Config { accessKeyId: string; secretAccessKey: string; endpoint: string; region?: string; requestSizeInBytes?: number; requestAbortTimeout?: number; logger?: Logger; } 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 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; } | { error: ListBucketError; }; interface ListMultipartUploadSuccess { listMultipartUploadsResult: { bucket: string; key: string; uploadId: string; size?: number; mtime?: Date | undefined; 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; /** * 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 CoreS3({ * accessKeyId: 'your-access-key', * secretAccessKey: 'your-secret-key', * endpoint: 'https://your-s3-endpoint.com', * region: 'us-east-1' // 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 { /** * Creates an instance of the S3 class. * * @constructor * @param {Object} config - Configuration options for the S3 instance. * @param {string} config.accessKeyId - The access key ID for authentication. * @param {string} config.secretAccessKey - The secret access key for authentication. * @param {string} config.endpoint - The endpoint URL of the S3-compatible service. * @param {string} [config.region='auto'] - The region of the S3 service. * @param {number} [config.requestSizeInBytes=8388608] - The request size of a single request in bytes (AWS S3 is 8MB). * @param {number} [config.requestAbortTimeout=undefined] - The timeout in milliseconds after which a request should be aborted (careful on streamed requests). * @param {Object} [config.logger=null] - A logger object with methods like info, warn, error. * @throws {TypeError} Will throw an error if required parameters are missing or of incorrect type. */ private accessKeyId; private secretAccessKey; private endpoint; private region; private requestSizeInBytes; private requestAbortTimeout?; private logger?; private signingKeyDate?; private signingKey?; constructor({ accessKeyId, secretAccessKey, endpoint, region, requestSizeInBytes, requestAbortTimeout, logger, }: S3Config); private _sanitize; private _log; private _validateConstructorParams; private _ensureValidUrl; private _validateMethodIsGetOrHead; private _checkKey; private _checkDelimiter; private _checkPrefix; private _checkOpts; private _filterIfHeaders; private _validateUploadPartParams; private _sign; private _buildCanonicalHeaders; private _buildCanonicalRequest; private _buildCredentialScope; private _buildStringToSign; private _calculateSignature; private _buildAuthorizationHeader; private _signedRequest; /** * Gets the current configuration properties of the S3 instance. * @returns {IT.S3Config} The current S3 configuration object containing all settings. * @example * const config = s3.getProps(); * console.log(config.endpoint); // 'https://s3.amazonaws.com/my-bucket' */ getProps(): S3Config; /** * Updates the configuration properties of the S3 instance. * @param {IT.S3Config} props - The new configuration object. * @param {string} props.accessKeyId - The access key ID for authentication. * @param {string} props.secretAccessKey - The secret access key for authentication. * @param {string} props.endpoint - The endpoint URL of the S3-compatible service. * @param {string} [props.region='auto'] - The region of the S3 service. * @param {number} [props.requestSizeInBytes=8388608] - The request size of a single request in bytes. * @param {number} [props.requestAbortTimeout] - The timeout in milliseconds after which a request should be aborted. * @param {IT.Logger} [props.logger] - A logger object with methods like info, warn, error. * @throws {TypeError} Will throw an error if required parameters are missing or of incorrect type. * @example * s3.setProps({ * accessKeyId: 'new-access-key', * secretAccessKey: 'new-secret-key', * endpoint: 'https://new-endpoint.com/my-bucket', * region: 'us-west-2' // by default is auto * }); */ setProps(props: S3Config): void; /** * 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>; /** * 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 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. * @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 | Buffer, fileType?: string, ssecHeaders?: SSECHeaders): 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: Buffer | 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; /** * 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 _handleErrorResponse; private _buildCanonicalQueryString; private _getSignatureKey; } /** * @deprecated Use `S3mini` instead. */ declare const s3mini: typeof S3mini; /** * 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, s3mini, sanitizeETag }; export type { CompleteMultipartUploadResult, ErrorWithCode, ExistResponseCode, ListBucketResponse, ListMultipartUploadResponse, Logger, S3Config, UploadPart };