lean-s3 0.6.1 → 0.6.2

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/dist/index.d.ts CHANGED
@@ -45,6 +45,15 @@ type DeleteObjectsOptions = {
45
45
  bucket?: string;
46
46
  signal?: AbortSignal;
47
47
  };
48
+ type DeleteObjectsResult = {
49
+ errors: DeleteObjectsError[];
50
+ };
51
+ type DeleteObjectsError = {
52
+ code: string;
53
+ key: string;
54
+ message: string;
55
+ versionId: string;
56
+ };
48
57
  interface S3FilePresignOptions {
49
58
  contentHash: Buffer;
50
59
  /** Seconds. */
@@ -58,6 +67,7 @@ type ListObjectsOptions = {
58
67
  bucket?: string;
59
68
  prefix?: string;
60
69
  maxKeys?: number;
70
+ delimiter?: string;
61
71
  startAfter?: string;
62
72
  continuationToken?: string;
63
73
  signal?: AbortSignal;
@@ -348,9 +358,7 @@ declare class S3Client {
348
358
  /**
349
359
  * Uses [`DeleteObjects`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) to delete multiple objects in a single request.
350
360
  */
351
- deleteObjects(objects: readonly S3BucketEntry[] | readonly string[], options?: DeleteObjectsOptions): Promise<{
352
- errors: any;
353
- } | null>;
361
+ deleteObjects(objects: readonly S3BucketEntry[] | readonly string[], options?: DeleteObjectsOptions): Promise<DeleteObjectsResult>;
354
362
  /**
355
363
  * Do not use this. This is an internal method.
356
364
  * TODO: Maybe move this into a separate free function?
@@ -379,9 +387,7 @@ declare class S3Stat {
379
387
 
380
388
  declare class S3File {
381
389
  #private;
382
- /**
383
- * @internal
384
- */
390
+ /** @internal */
385
391
  constructor(client: S3Client, path: ObjectKey, start: number | undefined, end: number | undefined, contentType: string | undefined);
386
392
  slice(start?: number | undefined, end?: number | undefined, contentType?: string | undefined): S3File;
387
393
  /**
@@ -391,13 +397,13 @@ declare class S3File {
391
397
  * @throws {S3Error} If the file does not exist or the server has some other issues.
392
398
  * @throws {Error} If the server returns an invalid response.
393
399
  */
394
- stat({ signal }?: Partial<S3StatOptions>): Promise<S3Stat>;
400
+ stat(options?: Partial<S3StatOptions>): Promise<S3Stat>;
395
401
  /**
396
402
  * Check if a file exists in the bucket. Uses `HEAD` request to check existence.
397
403
  *
398
404
  * @remarks Uses [`HeadObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
399
405
  */
400
- exists({ signal, }?: Partial<S3FileExistsOptions>): Promise<boolean>;
406
+ exists(options?: Partial<S3FileExistsOptions>): Promise<boolean>;
401
407
  /**
402
408
  * Delete a file from the bucket.
403
409
  *
@@ -409,18 +415,18 @@ declare class S3File {
409
415
  * @example
410
416
  * ```js
411
417
  * // Simple delete
412
- * await client.unlink("old-file.txt");
418
+ * await client.delete("old-file.txt");
413
419
  *
414
420
  * // With error handling
415
421
  * try {
416
- * await client.unlink("file.dat");
422
+ * await client.delete("file.dat");
417
423
  * console.log("File deleted");
418
424
  * } catch (err) {
419
425
  * console.error("Delete failed:", err);
420
426
  * }
421
427
  * ```
422
428
  */
423
- delete({ signal }?: Partial<S3FileDeleteOptions>): Promise<void>;
429
+ delete(options?: Partial<S3FileDeleteOptions>): Promise<void>;
424
430
  toString(): string;
425
431
  json(): Promise<unknown>;
426
432
  bytes(): Promise<Uint8Array>;
@@ -484,4 +490,4 @@ type BucketInfo = {
484
490
  type?: string;
485
491
  };
486
492
 
487
- export { type AbortMultipartUploadOptions, type Acl, type BucketCreationOptions, type BucketDeletionOptions, type BucketExistsOptions, type BucketInfo, type BucketLocationInfo, type ByteSource, type ChecksumAlgorithm, type ChecksumType, type CompleteMultipartUploadOptions, type CompleteMultipartUploadResult, type CreateFileInstanceOptions, type CreateMultipartUploadOptions, type CreateMultipartUploadResult, type DeleteObjectsOptions, type HttpMethod, type ListMultipartUploadsOptions, type ListMultipartUploadsResult, type ListObjectsIteratingOptions, type ListObjectsOptions, type ListObjectsResult, type ListPartsOptions, type ListPartsResult, type MultipartUpload, type MultipartUploadPart, type OverridableS3ClientOptions, type PresignableHttpMethod, S3BucketEntry, S3Client, type S3ClientOptions, S3Error, type S3ErrorOptions, S3File, type S3FileDeleteOptions, type S3FileExistsOptions, type S3FilePresignOptions, S3Stat, type S3StatOptions, type StorageClass, type UndiciBodyInit, type UploadPartOptions, type UploadPartResult };
493
+ export { type AbortMultipartUploadOptions, type Acl, type BucketCreationOptions, type BucketDeletionOptions, type BucketExistsOptions, type BucketInfo, type BucketLocationInfo, type ByteSource, type ChecksumAlgorithm, type ChecksumType, type CompleteMultipartUploadOptions, type CompleteMultipartUploadResult, type CreateFileInstanceOptions, type CreateMultipartUploadOptions, type CreateMultipartUploadResult, type DeleteObjectsError, type DeleteObjectsOptions, type DeleteObjectsResult, type HttpMethod, type ListMultipartUploadsOptions, type ListMultipartUploadsResult, type ListObjectsIteratingOptions, type ListObjectsOptions, type ListObjectsResult, type ListPartsOptions, type ListPartsResult, type MultipartUpload, type MultipartUploadPart, type OverridableS3ClientOptions, type PresignableHttpMethod, S3BucketEntry, S3Client, type S3ClientOptions, S3Error, type S3ErrorOptions, S3File, type S3FileDeleteOptions, type S3FileExistsOptions, type S3FilePresignOptions, S3Stat, type S3StatOptions, type StorageClass, type UndiciBodyInit, type UploadPartOptions, type UploadPartResult };
package/dist/index.js CHANGED
@@ -202,7 +202,7 @@ function buildRequestUrl(endpoint, bucket, region, path) {
202
202
  const result = new URL(endpointWithBucketAndRegion);
203
203
  const pathPrefix = result.pathname.endsWith("/") ? result.pathname : `${result.pathname}/`;
204
204
  const pathSuffix = replacedBucket ? normalizePath(path) : `${normalizedBucket}/${normalizePath(path)}`;
205
- result.pathname = pathPrefix + pathSuffix.replaceAll(":", "%3A").replaceAll("+", "%2B").replaceAll("(", "%28").replaceAll(")", "%29");
205
+ result.pathname = pathPrefix + pathSuffix.replaceAll(":", "%3A").replaceAll("+", "%2B").replaceAll("(", "%28").replaceAll(")", "%29").replaceAll(",", "%2C");
206
206
  return result;
207
207
  }
208
208
  function replaceDomainPlaceholders(endpoint, bucket, region) {
@@ -721,7 +721,7 @@ var S3Client = class {
721
721
  options.signal
722
722
  );
723
723
  if (response.statusCode === 200) {
724
- await response.body.dump();
724
+ response.body.dump();
725
725
  const etag = response.headers.etag;
726
726
  if (typeof etag !== "string" || etag.length === 0) {
727
727
  throw new S3Error("Unknown", "", {
@@ -848,7 +848,7 @@ var S3Client = class {
848
848
  if (400 <= response.statusCode && response.statusCode < 500) {
849
849
  throw await getResponseError(response, "");
850
850
  }
851
- await response.body.dump();
851
+ response.body.dump();
852
852
  if (response.statusCode === 200) {
853
853
  return;
854
854
  }
@@ -876,7 +876,7 @@ var S3Client = class {
876
876
  if (400 <= response.statusCode && response.statusCode < 500) {
877
877
  throw await getResponseError(response, "");
878
878
  }
879
- await response.body.dump();
879
+ response.body.dump();
880
880
  if (response.statusCode === 204) {
881
881
  return;
882
882
  }
@@ -903,7 +903,7 @@ var S3Client = class {
903
903
  if (response.statusCode !== 404 && 400 <= response.statusCode && response.statusCode < 500) {
904
904
  throw await getResponseError(response, "");
905
905
  }
906
- await response.body.dump();
906
+ response.body.dump();
907
907
  if (response.statusCode === 200) {
908
908
  return true;
909
909
  }
@@ -956,6 +956,12 @@ var S3Client = class {
956
956
  }
957
957
  query += `&max-keys=${options.maxKeys}`;
958
958
  }
959
+ if (typeof options.delimiter !== "undefined") {
960
+ if (typeof options.delimiter !== "string") {
961
+ throw new TypeError("`delimiter` must be a `string`.");
962
+ }
963
+ query += `&delimiter=${options.delimiter === "/" ? "/" : encodeURIComponent(options.delimiter)}`;
964
+ }
959
965
  if (options.prefix) {
960
966
  if (typeof options.prefix !== "string") {
961
967
  throw new TypeError("`prefix` must be a `string`.");
@@ -1051,7 +1057,7 @@ var S3Client = class {
1051
1057
  versionId: e.VersionId
1052
1058
  })) ?? []
1053
1059
  );
1054
- return errors.length > 0 ? { errors } : null;
1060
+ return { errors };
1055
1061
  }
1056
1062
  if (400 <= response.statusCode && response.statusCode < 500) {
1057
1063
  throw await getResponseError(response, "");
@@ -1352,9 +1358,7 @@ var S3File = class _S3File {
1352
1358
  #start;
1353
1359
  #end;
1354
1360
  #contentType;
1355
- /**
1356
- * @internal
1357
- */
1361
+ /** @internal */
1358
1362
  constructor(client, path, start, end, contentType) {
1359
1363
  if (typeof start === "number" && start < 0) {
1360
1364
  throw new Error("Invalid slice `start`.");
@@ -1385,7 +1389,7 @@ var S3File = class _S3File {
1385
1389
  * @throws {S3Error} If the file does not exist or the server has some other issues.
1386
1390
  * @throws {Error} If the server returns an invalid response.
1387
1391
  */
1388
- async stat({ signal } = {}) {
1392
+ async stat(options = {}) {
1389
1393
  const response = await this.#client[signedRequest](
1390
1394
  "HEAD",
1391
1395
  this.#path,
@@ -1395,9 +1399,9 @@ var S3File = class _S3File {
1395
1399
  void 0,
1396
1400
  void 0,
1397
1401
  void 0,
1398
- signal
1402
+ options.signal
1399
1403
  );
1400
- await response.body.dump();
1404
+ response.body.dump();
1401
1405
  if (200 <= response.statusCode && response.statusCode < 300) {
1402
1406
  const result = S3Stat.tryParseFromHeaders(response.headers);
1403
1407
  if (!result) {
@@ -1416,9 +1420,7 @@ var S3File = class _S3File {
1416
1420
  *
1417
1421
  * @remarks Uses [`HeadObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
1418
1422
  */
1419
- async exists({
1420
- signal
1421
- } = {}) {
1423
+ async exists(options = {}) {
1422
1424
  const response = await this.#client[signedRequest](
1423
1425
  "HEAD",
1424
1426
  this.#path,
@@ -1428,9 +1430,9 @@ var S3File = class _S3File {
1428
1430
  void 0,
1429
1431
  void 0,
1430
1432
  void 0,
1431
- signal
1433
+ options.signal
1432
1434
  );
1433
- await response.body.dump();
1435
+ response.body.dump();
1434
1436
  if (200 <= response.statusCode && response.statusCode < 300) {
1435
1437
  return true;
1436
1438
  }
@@ -1452,18 +1454,18 @@ var S3File = class _S3File {
1452
1454
  * @example
1453
1455
  * ```js
1454
1456
  * // Simple delete
1455
- * await client.unlink("old-file.txt");
1457
+ * await client.delete("old-file.txt");
1456
1458
  *
1457
1459
  * // With error handling
1458
1460
  * try {
1459
- * await client.unlink("file.dat");
1461
+ * await client.delete("file.dat");
1460
1462
  * console.log("File deleted");
1461
1463
  * } catch (err) {
1462
1464
  * console.error("Delete failed:", err);
1463
1465
  * }
1464
1466
  * ```
1465
1467
  */
1466
- async delete({ signal } = {}) {
1468
+ async delete(options = {}) {
1467
1469
  const response = await this.#client[signedRequest](
1468
1470
  "DELETE",
1469
1471
  this.#path,
@@ -1473,10 +1475,10 @@ var S3File = class _S3File {
1473
1475
  void 0,
1474
1476
  void 0,
1475
1477
  void 0,
1476
- signal
1478
+ options.signal
1477
1479
  );
1478
1480
  if (response.statusCode === 204) {
1479
- await response.body.dump();
1481
+ response.body.dump();
1480
1482
  return;
1481
1483
  }
1482
1484
  throw await getResponseError(response, this.#path);
@@ -1556,10 +1558,6 @@ var S3File = class _S3File {
1556
1558
  }
1557
1559
  /*
1558
1560
  // Future API?
1559
- writer(): WritableStream<ArrayBufferLike | ArrayBufferView> {
1560
- throw new Error("Not implemented");
1561
- }
1562
- // Future API?
1563
1561
  setTags(): Promise<void> {
1564
1562
  throw new Error("Not implemented");
1565
1563
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "lean-s3",
3
3
  "author": "Niklas Mollenhauer",
4
4
  "license": "MIT",
5
- "version": "0.6.1",
5
+ "version": "0.6.2",
6
6
  "description": "A server-side S3 API for the regular user.",
7
7
  "keywords": [
8
8
  "s3",
@@ -44,16 +44,15 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "fast-xml-parser": "^5.2.5",
47
- "i": "^0.3.7",
48
47
  "undici": "^7.10.0"
49
48
  },
50
49
  "devDependencies": {
51
- "@biomejs/biome": "2.0.0",
50
+ "@biomejs/biome": "2.0.5",
52
51
  "@testcontainers/localstack": "^11.0.3",
53
52
  "@testcontainers/minio": "^11.0.3",
54
- "@types/node": "^24.0.3",
55
- "@typescript/native-preview": "^7.0.0-dev.20250620.1",
56
- "expect": "^30.0.2",
53
+ "@types/node": "^24.0.4",
54
+ "@typescript/native-preview": "^7.0.0-dev.20250626.1",
55
+ "expect": "^30.0.3",
57
56
  "lefthook": "^1.11.14",
58
57
  "tsup": "^8.5.0",
59
58
  "tsx": "^4.20.3",