s3mini 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,12 @@ Dev:
23
23
  ![GitHub commit activity (branch)](https://img.shields.io/github/commit-activity/m/good-lly/s3mini/dev?color=greeen)
24
24
  ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/good-lly/s3mini)
25
25
  [![CodeQL Advanced](https://github.com/good-lly/s3mini/actions/workflows/codeql.yml/badge.svg?branch=dev)](https://github.com/good-lly/s3mini/actions/workflows/codeql.yml)
26
+ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=good-lly_s3mini&metric=bugs)](https://sonarcloud.io/summary/new_code?id=good-lly_s3mini)
27
+ [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=good-lly_s3mini&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=good-lly_s3mini)
28
+ [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=good-lly_s3mini&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=good-lly_s3mini)
29
+ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=good-lly_s3mini&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=good-lly_s3mini)
30
+ [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=good-lly_s3mini&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=good-lly_s3mini)
31
+ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=good-lly_s3mini&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=good-lly_s3mini)
26
32
  [![Test:e2e(all)](https://github.com/good-lly/s3mini/actions/workflows/test-e2e.yml/badge.svg?branch=dev)](https://github.com/good-lly/s3mini/actions/workflows/test-e2e.yml)
27
33
 
28
34
  ![GitHub Repo stars](https://img.shields.io/github/stars/good-lly/s3mini?style=social)
@@ -60,6 +66,7 @@ The library supports a subset of S3 operations, focusing on essential features,
60
66
  - ✅ GetObject (getObject, getObjectResponse, getObjectWithETag, getObjectRaw, getObjectArrayBuffer, getObjectJSON)
61
67
  - ✅ PutObject (putObject)
62
68
  - ✅ DeleteObject (deleteObject)
69
+ - ✅ DeleteObjects (deleteObjects)
63
70
  - ✅ HeadObject (objectExists, getEtag, getContentLength)
64
71
  - ✅ listMultipartUploads
65
72
  - ✅ CreateMultipartUpload (getMultipartUploadId)
@@ -68,6 +75,8 @@ The library supports a subset of S3 operations, focusing on essential features,
68
75
  - ✅ uploadPart
69
76
  - ❌ CopyObject: Not implemented (tbd)
70
77
 
78
+ Put/Get objects with SSE-C (server-side encryption with customer-provided keys) is supported, but only tested on Cloudflare R2!
79
+
71
80
  ## Installation
72
81
 
73
82
  ```bash
@@ -99,10 +108,14 @@ mv example.env .env
99
108
 
100
109
  ## Usage
101
110
 
111
+ > [!WARNING]
112
+ > `s3mini` is a deprecated alias retained solely for backward compatibility.
113
+ > It is scheduled for removal in a future release. Please migrate to the new `S3mini` class.
114
+
102
115
  ```typescript
103
- import { s3mini, sanitizeETag } from 's3mini';
116
+ import { S3mini, sanitizeETag } from 's3mini';
104
117
 
105
- const s3client = new s3mini({
118
+ const s3client = new S3mini({
106
119
  accessKeyId: config.accessKeyId,
107
120
  secretAccessKey: config.secretAccessKey,
108
121
  endpoint: config.endpoint,
@@ -149,7 +162,7 @@ const objectData: string | null = await s3client.getObject(smallObjectKey);
149
162
  console.log('Object data:', objectData);
150
163
 
151
164
  // get the object with ETag, null if not found
152
- const response2: Response = await s3mini.getObject(smallObjectKey, { 'if-none-match': etag });
165
+ const response2: Response = await S3mini.getObject(smallObjectKey, { 'if-none-match': etag });
153
166
  if (response2) {
154
167
  // ETag changed so we can get the object data and new ETag
155
168
  // Note: ETag is not guaranteed to be the same as the MD5 hash of the object
@@ -175,6 +188,10 @@ if (list) {
175
188
 
176
189
  // delete the object
177
190
  const wasDeleted: boolean = await s3client.deleteObject(smallObjectKey);
191
+ // to delete multiple objects, use deleteObjects method
192
+ // const keysToDelete: string[] = ['object1.txt', 'object2.txt'];
193
+ // const deletedArray: boolean[] = await s3client.deleteObjects(keysToDelete);
194
+ // Note: deleteObjects returns an array of booleans, one for each key, indicating if the object was deleted or not
178
195
 
179
196
  // Multipart upload
180
197
  const multipartKey = 'multipart-object.txt';
package/dist/s3mini.d.ts CHANGED
@@ -7,6 +7,14 @@ interface S3Config {
7
7
  requestAbortTimeout?: number;
8
8
  logger?: Logger;
9
9
  }
10
+ interface SSECHeaders {
11
+ 'x-amz-server-side-encryption-customer-algorithm': string;
12
+ 'x-amz-server-side-encryption-customer-key': string;
13
+ 'x-amz-server-side-encryption-customer-key-md5': string;
14
+ }
15
+ interface AWSHeaders {
16
+ [k: `x-amz-${string}`]: string;
17
+ }
10
18
  interface Logger {
11
19
  info: (message: string, ...args: unknown[]) => void;
12
20
  warn: (message: string, ...args: unknown[]) => void;
@@ -16,6 +24,13 @@ interface UploadPart {
16
24
  partNumber: number;
17
25
  etag: string;
18
26
  }
27
+ interface ListObject {
28
+ Key: string;
29
+ Size: number;
30
+ LastModified: Date;
31
+ ETag: string;
32
+ StorageClass: string;
33
+ }
19
34
  interface CompleteMultipartUploadResult {
20
35
  location: string;
21
36
  bucket: string;
@@ -45,7 +60,7 @@ interface ListMultipartUploadSuccess {
45
60
  key: string;
46
61
  uploadId: string;
47
62
  size?: number;
48
- mtime?: Date | undefined;
63
+ mtime?: Date;
49
64
  etag?: string;
50
65
  eTag?: string;
51
66
  parts: UploadPart[];
@@ -68,6 +83,55 @@ interface ErrorWithCode {
68
83
  type ListMultipartUploadResponse = ListMultipartUploadSuccess | MultipartUploadError;
69
84
  type HttpMethod = 'POST' | 'GET' | 'HEAD' | 'PUT' | 'DELETE';
70
85
  type ExistResponseCode = false | true | null;
86
+ interface CopyObjectOptions {
87
+ /**
88
+ * Specifies whether the metadata is copied from the source object or replaced with metadata provided in the request.
89
+ * Valid values: 'COPY' | 'REPLACE'
90
+ * Default: 'COPY'
91
+ */
92
+ metadataDirective?: 'COPY' | 'REPLACE';
93
+ /**
94
+ * Metadata to be set on the destination object when metadataDirective is 'REPLACE'.
95
+ * Keys can be provided with or without 'x-amz-meta-' prefix.
96
+ */
97
+ metadata?: Record<string, string>;
98
+ contentType?: string;
99
+ /**
100
+ * Storage class for the destination object.
101
+ * Valid values: 'STANDARD' | 'REDUCED_REDUNDANCY' | 'STANDARD_IA' | 'ONEZONE_IA' | 'INTELLIGENT_TIERING' | 'GLACIER' | 'DEEP_ARCHIVE' | 'GLACIER_IR'
102
+ */
103
+ storageClass?: string;
104
+ /**
105
+ * Specifies whether the object tag-set is copied from the source object or replaced with tag-set provided in the request.
106
+ * Valid values: 'COPY' | 'REPLACE'
107
+ */
108
+ taggingDirective?: 'COPY' | 'REPLACE';
109
+ /**
110
+ * If the bucket is configured as a website, redirects requests for this object to another object or URL.
111
+ */
112
+ websiteRedirectLocation?: string;
113
+ /**
114
+ * Server-Side Encryption with Customer-Provided Keys headers for the source object.
115
+ * Should include:
116
+ * - x-amz-copy-source-server-side-encryption-customer-algorithm
117
+ * - x-amz-copy-source-server-side-encryption-customer-key
118
+ * - x-amz-copy-source-server-side-encryption-customer-key-MD5
119
+ */
120
+ sourceSSECHeaders?: Record<string, string | number>;
121
+ destinationSSECHeaders?: SSECHeaders;
122
+ additionalHeaders?: Record<string, string | number>;
123
+ }
124
+ interface CopyObjectResult {
125
+ etag: string;
126
+ lastModified?: Date;
127
+ }
128
+ /**
129
+ * Where Buffer is available, e.g. when @types/node is loaded, we want to use it.
130
+ * But it should be excluded in other environments (e.g. Cloudflare).
131
+ */
132
+ type MaybeBuffer = typeof globalThis extends {
133
+ Buffer?: infer B;
134
+ } ? B extends new (...a: unknown[]) => unknown ? InstanceType<B> : ArrayBuffer | Uint8Array : ArrayBuffer | Uint8Array;
71
135
 
72
136
  /**
73
137
  * S3 class for interacting with S3-compatible object storage services.
@@ -79,8 +143,8 @@ type ExistResponseCode = false | true | null;
79
143
  * const s3 = new CoreS3({
80
144
  * accessKeyId: 'your-access-key',
81
145
  * secretAccessKey: 'your-secret-key',
82
- * endpoint: 'https://your-s3-endpoint.com',
83
- * region: 'us-east-1' // by default is auto
146
+ * endpoint: 'https://your-s3-endpoint.com/bucket-name',
147
+ * region: 'auto' // by default is auto
84
148
  * });
85
149
  *
86
150
  * // Upload a file
@@ -92,7 +156,7 @@ type ExistResponseCode = false | true | null;
92
156
  * // Delete a file
93
157
  * await s3.deleteObject('example.txt');
94
158
  */
95
- declare class s3mini {
159
+ declare class S3mini {
96
160
  /**
97
161
  * Creates an instance of the S3 class.
98
162
  *
@@ -107,13 +171,14 @@ declare class s3mini {
107
171
  * @param {Object} [config.logger=null] - A logger object with methods like info, warn, error.
108
172
  * @throws {TypeError} Will throw an error if required parameters are missing or of incorrect type.
109
173
  */
110
- private accessKeyId;
111
- private secretAccessKey;
112
- private endpoint;
113
- private region;
114
- private requestSizeInBytes;
115
- private requestAbortTimeout?;
116
- private logger?;
174
+ readonly accessKeyId: string;
175
+ readonly secretAccessKey: string;
176
+ readonly endpoint: URL;
177
+ readonly region: string;
178
+ readonly bucketName: string;
179
+ readonly requestSizeInBytes: number;
180
+ readonly requestAbortTimeout?: number;
181
+ readonly logger?: Logger;
117
182
  private signingKeyDate?;
118
183
  private signingKey?;
119
184
  constructor({ accessKeyId, secretAccessKey, endpoint, region, requestSizeInBytes, requestAbortTimeout, logger, }: S3Config);
@@ -127,43 +192,10 @@ declare class s3mini {
127
192
  private _checkPrefix;
128
193
  private _checkOpts;
129
194
  private _filterIfHeaders;
195
+ private _validateData;
130
196
  private _validateUploadPartParams;
131
197
  private _sign;
132
- private _buildCanonicalHeaders;
133
- private _buildCanonicalRequest;
134
- private _buildCredentialScope;
135
- private _buildStringToSign;
136
- private _calculateSignature;
137
- private _buildAuthorizationHeader;
138
198
  private _signedRequest;
139
- /**
140
- * Gets the current configuration properties of the S3 instance.
141
- * @returns {IT.S3Config} The current S3 configuration object containing all settings.
142
- * @example
143
- * const config = s3.getProps();
144
- * console.log(config.endpoint); // 'https://s3.amazonaws.com/my-bucket'
145
- */
146
- getProps(): S3Config;
147
- /**
148
- * Updates the configuration properties of the S3 instance.
149
- * @param {IT.S3Config} props - The new configuration object.
150
- * @param {string} props.accessKeyId - The access key ID for authentication.
151
- * @param {string} props.secretAccessKey - The secret access key for authentication.
152
- * @param {string} props.endpoint - The endpoint URL of the S3-compatible service.
153
- * @param {string} [props.region='auto'] - The region of the S3 service.
154
- * @param {number} [props.requestSizeInBytes=8388608] - The request size of a single request in bytes.
155
- * @param {number} [props.requestAbortTimeout] - The timeout in milliseconds after which a request should be aborted.
156
- * @param {IT.Logger} [props.logger] - A logger object with methods like info, warn, error.
157
- * @throws {TypeError} Will throw an error if required parameters are missing or of incorrect type.
158
- * @example
159
- * s3.setProps({
160
- * accessKeyId: 'new-access-key',
161
- * secretAccessKey: 'new-secret-key',
162
- * endpoint: 'https://new-endpoint.com/my-bucket',
163
- * region: 'us-west-2' // by default is auto
164
- * });
165
- */
166
- setProps(props: S3Config): void;
167
199
  /**
168
200
  * Sanitizes an ETag value by removing surrounding quotes and whitespace.
169
201
  * Still returns RFC compliant ETag. https://www.rfc-editor.org/rfc/rfc9110#section-8.8.3
@@ -179,6 +211,7 @@ declare class s3mini {
179
211
  * @returns A promise that resolves to true if the bucket was created successfully, false otherwise.
180
212
  */
181
213
  createBucket(): Promise<boolean>;
214
+ private _extractBucketName;
182
215
  /**
183
216
  * Checks if a bucket exists.
184
217
  * This method sends a request to check if the specified bucket exists in the S3-compatible service.
@@ -192,7 +225,7 @@ declare class s3mini {
192
225
  * @param {string} [prefix=''] - The prefix to filter objects by.
193
226
  * @param {number} [maxKeys] - The maximum number of keys to return. If not provided, all keys will be returned.
194
227
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
195
- * @returns {Promise<object[] | null>} A promise that resolves to an array of objects or null if the bucket is empty.
228
+ * @returns {Promise<IT.ListObject[] | null>} A promise that resolves to an array of objects or null if the bucket is empty.
196
229
  * @example
197
230
  * // List all objects
198
231
  * const objects = await s3.listObjects();
@@ -200,7 +233,13 @@ declare class s3mini {
200
233
  * // List objects with prefix
201
234
  * const photos = await s3.listObjects('/', 'photos/', 100);
202
235
  */
203
- listObjects(delimiter?: string, prefix?: string, maxKeys?: number, opts?: Record<string, unknown>): Promise<object[] | null>;
236
+ listObjects(delimiter?: string, prefix?: string, maxKeys?: number, opts?: Record<string, unknown>): Promise<ListObject[] | null>;
237
+ private _fetchObjectBatch;
238
+ private _buildListObjectsQuery;
239
+ private _handleListObjectsError;
240
+ private _parseListObjectsResponse;
241
+ private _extractObjectsFromResponse;
242
+ private _extractContinuationToken;
204
243
  /**
205
244
  * Lists multipart uploads in the bucket.
206
245
  * This method sends a request to list multipart uploads in the specified bucket.
@@ -215,42 +254,47 @@ declare class s3mini {
215
254
  * Get an object from the S3-compatible service.
216
255
  * This method sends a request to retrieve the specified object from the S3-compatible service.
217
256
  * @param {string} key - The key of the object to retrieve.
218
- * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
257
+ * @param {Record<string, unknown>} [opts] - Additional options for the request.
258
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
219
259
  * @returns A promise that resolves to the object data (string) or null if not found.
220
260
  */
221
- getObject(key: string, opts?: Record<string, unknown>): Promise<string | null>;
261
+ getObject(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<string | null>;
222
262
  /**
223
263
  * Get an object response from the S3-compatible service.
224
264
  * This method sends a request to retrieve the specified object and returns the full response.
225
265
  * @param {string} key - The key of the object to retrieve.
226
266
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
267
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
227
268
  * @returns A promise that resolves to the Response object or null if not found.
228
269
  */
229
- getObjectResponse(key: string, opts?: Record<string, unknown>): Promise<Response | null>;
270
+ getObjectResponse(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<Response | null>;
230
271
  /**
231
272
  * Get an object as an ArrayBuffer from the S3-compatible service.
232
273
  * This method sends a request to retrieve the specified object and returns it as an ArrayBuffer.
233
274
  * @param {string} key - The key of the object to retrieve.
234
275
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
276
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
235
277
  * @returns A promise that resolves to the object data as an ArrayBuffer or null if not found.
236
278
  */
237
- getObjectArrayBuffer(key: string, opts?: Record<string, unknown>): Promise<ArrayBuffer | null>;
279
+ getObjectArrayBuffer(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<ArrayBuffer | null>;
238
280
  /**
239
281
  * Get an object as JSON from the S3-compatible service.
240
282
  * This method sends a request to retrieve the specified object and returns it as JSON.
241
283
  * @param {string} key - The key of the object to retrieve.
242
284
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
285
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
243
286
  * @returns A promise that resolves to the object data as JSON or null if not found.
244
287
  */
245
- getObjectJSON<T = unknown>(key: string, opts?: Record<string, unknown>): Promise<T | null>;
288
+ getObjectJSON<T = unknown>(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<T | null>;
246
289
  /**
247
290
  * Get an object with its ETag from the S3-compatible service.
248
291
  * This method sends a request to retrieve the specified object and its ETag.
249
292
  * @param {string} key - The key of the object to retrieve.
250
293
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
294
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
251
295
  * @returns A promise that resolves to an object containing the ETag and the object data as an ArrayBuffer or null if not found.
252
296
  */
253
- getObjectWithETag(key: string, opts?: Record<string, unknown>): Promise<{
297
+ getObjectWithETag(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<{
254
298
  etag: string | null;
255
299
  data: ArrayBuffer | null;
256
300
  }>;
@@ -262,9 +306,10 @@ declare class s3mini {
262
306
  * @param {number} [rangeFrom=0] - The starting byte for the range (if not whole file).
263
307
  * @param {number} [rangeTo=this.requestSizeInBytes] - The ending byte for the range (if not whole file).
264
308
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
309
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
265
310
  * @returns A promise that resolves to the Response object.
266
311
  */
267
- getObjectRaw(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Record<string, unknown>): Promise<Response>;
312
+ getObjectRaw(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<Response>;
268
313
  /**
269
314
  * Get the content length of an object.
270
315
  * This method sends a HEAD request to retrieve the content length of the specified object.
@@ -272,7 +317,7 @@ declare class s3mini {
272
317
  * @returns A promise that resolves to the content length of the object in bytes, or 0 if not found.
273
318
  * @throws {Error} If the content length header is not found in the response.
274
319
  */
275
- getContentLength(key: string): Promise<number>;
320
+ getContentLength(key: string, ssecHeaders?: SSECHeaders): Promise<number>;
276
321
  /**
277
322
  * Checks if an object exists in the S3-compatible service.
278
323
  * This method sends a HEAD request to check if the specified object exists.
@@ -285,6 +330,7 @@ declare class s3mini {
285
330
  * Retrieves the ETag of an object without downloading its content.
286
331
  * @param {string} key - The key of the object to retrieve the ETag for.
287
332
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
333
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
288
334
  * @returns {Promise<string | null>} A promise that resolves to the ETag value or null if the object is not found.
289
335
  * @throws {Error} If the ETag header is not found in the response.
290
336
  * @example
@@ -293,12 +339,14 @@ declare class s3mini {
293
339
  * console.log(`File ETag: ${etag}`);
294
340
  * }
295
341
  */
296
- getEtag(key: string, opts?: Record<string, unknown>): Promise<string | null>;
342
+ getEtag(key: string, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<string | null>;
297
343
  /**
298
344
  * Uploads an object to the S3-compatible service.
299
345
  * @param {string} key - The key/path where the object will be stored.
300
346
  * @param {string | Buffer} data - The data to upload (string or Buffer).
301
347
  * @param {string} [fileType='application/octet-stream'] - The MIME type of the file.
348
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
349
+ * @param {IT.AWSHeaders} [additionalHeaders] - Additional x-amz-* headers specific to this request, if any.
302
350
  * @returns {Promise<Response>} A promise that resolves to the Response object from the upload request.
303
351
  * @throws {TypeError} If data is not a string or Buffer.
304
352
  * @example
@@ -309,11 +357,12 @@ declare class s3mini {
309
357
  * const buffer = Buffer.from([0x89, 0x50, 0x4e, 0x47]);
310
358
  * await s3.putObject('image.png', buffer, 'image/png');
311
359
  */
312
- putObject(key: string, data: string | Buffer, fileType?: string): Promise<Response>;
360
+ putObject(key: string, data: string | MaybeBuffer, fileType?: string, ssecHeaders?: SSECHeaders, additionalHeaders?: AWSHeaders): Promise<Response>;
313
361
  /**
314
362
  * Initiates a multipart upload and returns the upload ID.
315
363
  * @param {string} key - The key/path where the object will be stored.
316
364
  * @param {string} [fileType='application/octet-stream'] - The MIME type of the file.
365
+ * @param {IT.SSECHeaders?} [ssecHeaders] - Server-Side Encryption headers, if any.
317
366
  * @returns {Promise<string>} A promise that resolves to the upload ID for the multipart upload.
318
367
  * @throws {TypeError} If key is invalid or fileType is not a string.
319
368
  * @throws {Error} If the multipart upload fails to initialize.
@@ -321,7 +370,7 @@ declare class s3mini {
321
370
  * const uploadId = await s3.getMultipartUploadId('large-file.zip', 'application/zip');
322
371
  * console.log(`Started multipart upload: ${uploadId}`);
323
372
  */
324
- getMultipartUploadId(key: string, fileType?: string): Promise<string>;
373
+ getMultipartUploadId(key: string, fileType?: string, ssecHeaders?: SSECHeaders): Promise<string>;
325
374
  /**
326
375
  * Uploads a part in a multipart upload.
327
376
  * @param {string} key - The key of the object being uploaded.
@@ -329,6 +378,7 @@ declare class s3mini {
329
378
  * @param {Buffer | string} data - The data for this part.
330
379
  * @param {number} partNumber - The part number (must be between 1 and 10,000).
331
380
  * @param {Record<string, unknown>} [opts={}] - Additional options for the request.
381
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
332
382
  * @returns {Promise<IT.UploadPart>} A promise that resolves to an object containing the partNumber and etag.
333
383
  * @throws {TypeError} If any parameter is invalid.
334
384
  * @example
@@ -340,7 +390,7 @@ declare class s3mini {
340
390
  * );
341
391
  * console.log(`Part ${part.partNumber} uploaded with ETag: ${part.etag}`);
342
392
  */
343
- uploadPart(key: string, uploadId: string, data: Buffer | string, partNumber: number, opts?: Record<string, unknown>): Promise<UploadPart>;
393
+ uploadPart(key: string, uploadId: string, data: MaybeBuffer | string, partNumber: number, opts?: Record<string, unknown>, ssecHeaders?: SSECHeaders): Promise<UploadPart>;
344
394
  /**
345
395
  * Completes a multipart upload by combining all uploaded parts.
346
396
  * @param {string} key - The key of the object being uploaded.
@@ -364,6 +414,7 @@ declare class s3mini {
364
414
  * Aborts a multipart upload and removes all uploaded parts.
365
415
  * @param {string} key - The key of the object being uploaded.
366
416
  * @param {string} uploadId - The upload ID to abort.
417
+ * @param {IT.SSECHeaders} [ssecHeaders] - Server-Side Encryption headers, if any.
367
418
  * @returns {Promise<object>} A promise that resolves to an object containing the abort status and details.
368
419
  * @throws {TypeError} If key or uploadId is invalid.
369
420
  * @throws {Error} If the abort operation fails.
@@ -375,8 +426,111 @@ declare class s3mini {
375
426
  * console.error('Failed to abort upload:', error);
376
427
  * }
377
428
  */
378
- abortMultipartUpload(key: string, uploadId: string): Promise<object>;
429
+ abortMultipartUpload(key: string, uploadId: string, ssecHeaders?: SSECHeaders): Promise<object>;
379
430
  private _buildCompleteMultipartUploadXml;
431
+ /**
432
+ * Executes the copy operation for local copying (same bucket/endpoint).
433
+ * @private
434
+ */
435
+ private _executeCopyOperation;
436
+ /**
437
+ * Copies an object within the same bucket.
438
+ *
439
+ * @param {string} sourceKey - The key of the source object to copy
440
+ * @param {string} destinationKey - The key where the object will be copied to
441
+ * @param {IT.CopyObjectOptions} [options={}] - Copy operation options
442
+ * @param {string} [options.metadataDirective='COPY'] - How to handle metadata ('COPY' | 'REPLACE')
443
+ * @param {Record<string,string>} [options.metadata={}] - New metadata (only used if metadataDirective='REPLACE')
444
+ * @param {string} [options.contentType] - New content type for the destination object
445
+ * @param {string} [options.storageClass] - Storage class for the destination object
446
+ * @param {string} [options.taggingDirective] - How to handle object tags ('COPY' | 'REPLACE')
447
+ * @param {string} [options.websiteRedirectLocation] - Website redirect location for the destination
448
+ * @param {IT.SSECHeaders} [options.sourceSSECHeaders={}] - Encryption headers for reading source (if encrypted)
449
+ * @param {IT.SSECHeaders} [options.destinationSSECHeaders={}] - Encryption headers for destination
450
+ * @param {IT.AWSHeaders} [options.additionalHeaders={}] - Extra x-amz-* headers
451
+ *
452
+ * @returns {Promise<IT.CopyObjectResult>} Copy result with etag and lastModified date
453
+ * @throws {TypeError} If sourceKey or destinationKey is invalid
454
+ * @throws {Error} If copy operation fails or S3 returns an error
455
+ *
456
+ * @example
457
+ * // Simple copy
458
+ * const result = await s3.copyObject('report-2024.pdf', 'archive/report-2024.pdf');
459
+ * console.log(`Copied with ETag: ${result.etag}`);
460
+ *
461
+ * @example
462
+ * // Copy with new metadata and content type
463
+ * const result = await s3.copyObject('data.csv', 'processed/data.csv', {
464
+ * metadataDirective: 'REPLACE',
465
+ * metadata: {
466
+ * 'processed-date': new Date().toISOString(),
467
+ * 'original-name': 'data.csv'
468
+ * },
469
+ * contentType: 'text/csv; charset=utf-8'
470
+ * });
471
+ *
472
+ * @example
473
+ * // Copy encrypted object (Cloudflare R2 SSE-C)
474
+ * const ssecKey = 'n1TKiTaVHlYLMX9n0zHXyooMr026vOiTEFfT+719Hho=';
475
+ * await s3.copyObject('sensitive.json', 'backup/sensitive.json', {
476
+ * sourceSSECHeaders: {
477
+ * 'x-amz-copy-source-server-side-encryption-customer-algorithm': 'AES256',
478
+ * 'x-amz-copy-source-server-side-encryption-customer-key': ssecKey,
479
+ * 'x-amz-copy-source-server-side-encryption-customer-key-md5': 'gepZmzgR7Be/1+K1Aw+6ow=='
480
+ * },
481
+ * destinationSSECHeaders: {
482
+ * 'x-amz-server-side-encryption-customer-algorithm': 'AES256',
483
+ * 'x-amz-server-side-encryption-customer-key': ssecKey,
484
+ * 'x-amz-server-side-encryption-customer-key-md5': 'gepZmzgR7Be/1+K1Aw+6ow=='
485
+ * }
486
+ * });
487
+ */
488
+ copyObject(sourceKey: string, destinationKey: string, options?: CopyObjectOptions): Promise<CopyObjectResult>;
489
+ private _buildSSECHeaders;
490
+ /**
491
+ * Moves an object within the same bucket (copy + delete atomic-like operation).
492
+ *
493
+ * WARNING: Not truly atomic - if delete fails after successful copy, the object
494
+ * will exist in both locations. Consider your use case carefully.
495
+ *
496
+ * @param {string} sourceKey - The key of the source object to move
497
+ * @param {string} destinationKey - The key where the object will be moved to
498
+ * @param {IT.CopyObjectOptions} [options={}] - Options passed to the copy operation
499
+ *
500
+ * @returns {Promise<IT.CopyObjectResult>} Result from the copy operation
501
+ * @throws {TypeError} If sourceKey or destinationKey is invalid
502
+ * @throws {Error} If copy succeeds but delete fails (includes copy result in error)
503
+ *
504
+ * @example
505
+ * // Simple move
506
+ * await s3.moveObject('temp/upload.tmp', 'files/document.pdf');
507
+ *
508
+ * @example
509
+ * // Move with metadata update
510
+ * await s3.moveObject('unprocessed/image.jpg', 'processed/image.jpg', {
511
+ * metadataDirective: 'REPLACE',
512
+ * metadata: {
513
+ * 'status': 'processed',
514
+ * 'processed-at': Date.now().toString()
515
+ * },
516
+ * contentType: 'image/jpeg'
517
+ * });
518
+ *
519
+ * @example
520
+ * // Safe move with error handling
521
+ * try {
522
+ * const result = await s3.moveObject('inbox/file.dat', 'archive/file.dat');
523
+ * console.log(`Moved successfully: ${result.etag}`);
524
+ * } catch (error) {
525
+ * // Check if copy succeeded but delete failed
526
+ * if (error.message.includes('delete source object after successful copy')) {
527
+ * console.warn('File copied but not deleted from source - manual cleanup needed');
528
+ * }
529
+ * }
530
+ */
531
+ moveObject(sourceKey: string, destinationKey: string, options?: CopyObjectOptions): Promise<CopyObjectResult>;
532
+ private _buildMetadataHeaders;
533
+ private _parseCopyObjectResponse;
380
534
  /**
381
535
  * Deletes an object from the bucket.
382
536
  * This method sends a request to delete the specified object from the bucket.
@@ -392,6 +546,7 @@ declare class s3mini {
392
546
  */
393
547
  deleteObjects(keys: string[]): Promise<boolean[]>;
394
548
  private _sendRequest;
549
+ private _parseErrorXml;
395
550
  private _handleErrorResponse;
396
551
  private _buildCanonicalQueryString;
397
552
  private _getSignatureKey;
@@ -414,4 +569,5 @@ declare const sanitizeETag: (etag: string) => string;
414
569
  */
415
570
  declare const runInBatches: <T = unknown>(tasks: Iterable<() => Promise<T>>, batchSize?: number, minIntervalMs?: number) => Promise<Array<PromiseSettledResult<T>>>;
416
571
 
417
- export { type CompleteMultipartUploadResult, type ErrorWithCode, type ExistResponseCode, type ListBucketResponse, type ListMultipartUploadResponse, type Logger, type S3Config, type UploadPart, s3mini as default, runInBatches, s3mini, sanitizeETag };
572
+ export { S3mini, S3mini as default, runInBatches, sanitizeETag };
573
+ export type { CompleteMultipartUploadResult, ErrorWithCode, ExistResponseCode, ListBucketResponse, ListMultipartUploadResponse, Logger, S3Config, UploadPart };