lean-s3 0.6.0 → 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
@@ -20,6 +20,14 @@ declare class S3BucketEntry {
20
20
  static parse(source: any): S3BucketEntry;
21
21
  }
22
22
 
23
+ declare const __brand: unique symbol;
24
+ type Brand<B> = {
25
+ [__brand]: B;
26
+ };
27
+ type Branded<T, B> = T & Brand<B>;
28
+ type BucketName = Branded<string, "BucketName">;
29
+ type ObjectKey = Branded<string, "ObjectKey">;
30
+
23
31
  declare const write: unique symbol;
24
32
  declare const stream: unique symbol;
25
33
  declare const signedRequest: unique symbol;
@@ -34,8 +42,18 @@ interface S3ClientOptions {
34
42
  type OverridableS3ClientOptions = Pick<S3ClientOptions, "region" | "bucket" | "endpoint">;
35
43
  type CreateFileInstanceOptions = {};
36
44
  type DeleteObjectsOptions = {
45
+ bucket?: string;
37
46
  signal?: AbortSignal;
38
47
  };
48
+ type DeleteObjectsResult = {
49
+ errors: DeleteObjectsError[];
50
+ };
51
+ type DeleteObjectsError = {
52
+ code: string;
53
+ key: string;
54
+ message: string;
55
+ versionId: string;
56
+ };
39
57
  interface S3FilePresignOptions {
40
58
  contentHash: Buffer;
41
59
  /** Seconds. */
@@ -49,6 +67,7 @@ type ListObjectsOptions = {
49
67
  bucket?: string;
50
68
  prefix?: string;
51
69
  maxKeys?: number;
70
+ delimiter?: string;
52
71
  startAfter?: string;
53
72
  continuationToken?: string;
54
73
  signal?: AbortSignal;
@@ -274,19 +293,19 @@ declare class S3Client {
274
293
  * @throws {RangeError} If `key` is not at least 1 character long.
275
294
  * @throws {Error} If `uploadId` is not provided.
276
295
  */
277
- abortMultipartUpload(key: string, uploadId: string, options?: AbortMultipartUploadOptions): Promise<void>;
296
+ abortMultipartUpload(path: string, uploadId: string, options?: AbortMultipartUploadOptions): Promise<void>;
278
297
  /**
279
298
  * @remarks Uses [`CompleteMultipartUpload`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html).
280
299
  * @throws {RangeError} If `key` is not at least 1 character long.
281
300
  * @throws {Error} If `uploadId` is not provided.
282
301
  */
283
- completeMultipartUpload(key: string, uploadId: string, parts: readonly MultipartUploadPart[], options?: CompleteMultipartUploadOptions): Promise<CompleteMultipartUploadResult>;
302
+ completeMultipartUpload(path: string, uploadId: string, parts: readonly MultipartUploadPart[], options?: CompleteMultipartUploadOptions): Promise<CompleteMultipartUploadResult>;
284
303
  /**
285
304
  * @remarks Uses [`UploadPart`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html).
286
305
  * @throws {RangeError} If `key` is not at least 1 character long.
287
306
  * @throws {Error} If `uploadId` is not provided.
288
307
  */
289
- uploadPart(key: string, uploadId: string, data: UndiciBodyInit, partNumber: number, options?: UploadPartOptions): Promise<UploadPartResult>;
308
+ uploadPart(path: string, uploadId: string, data: UndiciBodyInit, partNumber: number, options?: UploadPartOptions): Promise<UploadPartResult>;
290
309
  /**
291
310
  * @remarks Uses [`ListParts`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html).
292
311
  * @throws {RangeError} If `key` is not at least 1 character long.
@@ -295,7 +314,7 @@ declare class S3Client {
295
314
  * @throws {RangeError} If `options.maxParts` is <= 0.
296
315
  * @throws {TypeError} If `options.partNumberMarker` is not a `string`.
297
316
  */
298
- listParts(key: string, uploadId: string, options?: ListPartsOptions): Promise<ListPartsResult>;
317
+ listParts(path: string, uploadId: string, options?: ListPartsOptions): Promise<ListPartsResult>;
299
318
  /**
300
319
  * Creates a new bucket on the S3 server.
301
320
  *
@@ -339,15 +358,13 @@ declare class S3Client {
339
358
  /**
340
359
  * Uses [`DeleteObjects`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) to delete multiple objects in a single request.
341
360
  */
342
- deleteObjects(objects: readonly S3BucketEntry[] | readonly string[], options?: DeleteObjectsOptions): Promise<{
343
- errors: any;
344
- } | null>;
361
+ deleteObjects(objects: readonly S3BucketEntry[] | readonly string[], options?: DeleteObjectsOptions): Promise<DeleteObjectsResult>;
345
362
  /**
346
363
  * Do not use this. This is an internal method.
347
364
  * TODO: Maybe move this into a separate free function?
348
365
  * @internal
349
366
  */
350
- [signedRequest](method: HttpMethod, pathWithoutBucket: string, query: string | undefined, body: UndiciBodyInit | undefined, additionalSignedHeaders: Record<string, string> | undefined, additionalUnsignedHeaders: Record<string, string> | undefined, contentHash: Buffer | undefined, bucket: string | undefined, signal?: AbortSignal | undefined): Promise<Dispatcher.ResponseData<null>>;
367
+ [signedRequest](method: HttpMethod, pathWithoutBucket: ObjectKey, query: string | undefined, body: UndiciBodyInit | undefined, additionalSignedHeaders: Record<string, string> | undefined, additionalUnsignedHeaders: Record<string, string> | undefined, contentHash: Buffer | undefined, bucket: BucketName | undefined, signal?: AbortSignal | undefined): Promise<Dispatcher.ResponseData<null>>;
351
368
  /**
352
369
  * @internal
353
370
  * @param {import("./index.d.ts").UndiciBodyInit} data TODO
@@ -370,10 +387,8 @@ declare class S3Stat {
370
387
 
371
388
  declare class S3File {
372
389
  #private;
373
- /**
374
- * @internal
375
- */
376
- constructor(client: S3Client, path: string, start: number | undefined, end: number | undefined, contentType: string | undefined);
390
+ /** @internal */
391
+ constructor(client: S3Client, path: ObjectKey, start: number | undefined, end: number | undefined, contentType: string | undefined);
377
392
  slice(start?: number | undefined, end?: number | undefined, contentType?: string | undefined): S3File;
378
393
  /**
379
394
  * Get the stat of a file in the bucket. Uses `HEAD` request to check existence.
@@ -382,13 +397,13 @@ declare class S3File {
382
397
  * @throws {S3Error} If the file does not exist or the server has some other issues.
383
398
  * @throws {Error} If the server returns an invalid response.
384
399
  */
385
- stat({ signal }?: Partial<S3StatOptions>): Promise<S3Stat>;
400
+ stat(options?: Partial<S3StatOptions>): Promise<S3Stat>;
386
401
  /**
387
402
  * Check if a file exists in the bucket. Uses `HEAD` request to check existence.
388
403
  *
389
404
  * @remarks Uses [`HeadObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
390
405
  */
391
- exists({ signal, }?: Partial<S3FileExistsOptions>): Promise<boolean>;
406
+ exists(options?: Partial<S3FileExistsOptions>): Promise<boolean>;
392
407
  /**
393
408
  * Delete a file from the bucket.
394
409
  *
@@ -400,18 +415,18 @@ declare class S3File {
400
415
  * @example
401
416
  * ```js
402
417
  * // Simple delete
403
- * await client.unlink("old-file.txt");
418
+ * await client.delete("old-file.txt");
404
419
  *
405
420
  * // With error handling
406
421
  * try {
407
- * await client.unlink("file.dat");
422
+ * await client.delete("file.dat");
408
423
  * console.log("File deleted");
409
424
  * } catch (err) {
410
425
  * console.error("Delete failed:", err);
411
426
  * }
412
427
  * ```
413
428
  */
414
- delete({ signal }?: Partial<S3FileDeleteOptions>): Promise<void>;
429
+ delete(options?: Partial<S3FileDeleteOptions>): Promise<void>;
415
430
  toString(): string;
416
431
  json(): Promise<unknown>;
417
432
  bytes(): Promise<Uint8Array>;
@@ -475,4 +490,4 @@ type BucketInfo = {
475
490
  type?: string;
476
491
  };
477
492
 
478
- 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) {
@@ -318,6 +318,34 @@ function getAuthorizationHeader(keyCache, method, path, query, date, sortedSigne
318
318
  return `AWS4-HMAC-SHA256 Credential=${credentialSpec}, SignedHeaders=${signedHeadersSpec}, Signature=${signature}`;
319
319
  }
320
320
 
321
+ // src/branded.ts
322
+ function ensureValidBucketName(name) {
323
+ if (name.length < 3 || name.length > 63) {
324
+ throw new Error("`name` must be between 3 and 63 characters long.");
325
+ }
326
+ if (name.startsWith(".") || name.endsWith(".")) {
327
+ throw new Error("`name` must not start or end with a period (.)");
328
+ }
329
+ if (!/^[a-z0-9.-]+$/.test(name)) {
330
+ throw new Error(
331
+ "`name` can only contain lowercase letters, numbers, periods (.), and hyphens (-)."
332
+ );
333
+ }
334
+ if (name.includes("..")) {
335
+ throw new Error("`name` must not contain two adjacent periods (..)");
336
+ }
337
+ return name;
338
+ }
339
+ function ensureValidPath(path) {
340
+ if (typeof path !== "string") {
341
+ throw new TypeError("`path` must be a `string`.");
342
+ }
343
+ if (path.length < 1) {
344
+ throw new RangeError("`path` must be at least 1 character long.");
345
+ }
346
+ return path;
347
+ }
348
+
321
349
  // src/S3Client.ts
322
350
  var write = Symbol("write");
323
351
  var stream = Symbol("stream");
@@ -412,7 +440,13 @@ var S3Client = class {
412
440
  * ```
413
441
  */
414
442
  file(path, _options) {
415
- return new S3File(this, path, void 0, void 0, void 0);
443
+ return new S3File(
444
+ this,
445
+ ensureValidPath(path),
446
+ void 0,
447
+ void 0,
448
+ void 0
449
+ );
416
450
  }
417
451
  /**
418
452
  * Generate a presigned URL for temporary access to a file.
@@ -489,11 +523,10 @@ var S3Client = class {
489
523
  //#region multipart uploads
490
524
  async createMultipartUpload(key, options = {}) {
491
525
  if (key.length < 1) {
492
- throw new RangeError("`key` must be at least 1 character long.");
493
526
  }
494
527
  const response = await this[signedRequest](
495
528
  "POST",
496
- key,
529
+ ensureValidPath(key),
497
530
  "uploads=",
498
531
  void 0,
499
532
  void 0,
@@ -596,16 +629,13 @@ var S3Client = class {
596
629
  * @throws {RangeError} If `key` is not at least 1 character long.
597
630
  * @throws {Error} If `uploadId` is not provided.
598
631
  */
599
- async abortMultipartUpload(key, uploadId, options = {}) {
600
- if (key.length < 1) {
601
- throw new RangeError("`key` must be at least 1 character long.");
602
- }
632
+ async abortMultipartUpload(path, uploadId, options = {}) {
603
633
  if (!uploadId) {
604
634
  throw new Error("`uploadId` is required.");
605
635
  }
606
636
  const response = await this[signedRequest](
607
637
  "DELETE",
608
- key,
638
+ ensureValidPath(path),
609
639
  `uploadId=${encodeURIComponent(uploadId)}`,
610
640
  void 0,
611
641
  void 0,
@@ -615,7 +645,7 @@ var S3Client = class {
615
645
  options.signal
616
646
  );
617
647
  if (response.statusCode !== 204) {
618
- throw await getResponseError(response, key);
648
+ throw await getResponseError(response, path);
619
649
  }
620
650
  }
621
651
  /**
@@ -623,10 +653,7 @@ var S3Client = class {
623
653
  * @throws {RangeError} If `key` is not at least 1 character long.
624
654
  * @throws {Error} If `uploadId` is not provided.
625
655
  */
626
- async completeMultipartUpload(key, uploadId, parts, options = {}) {
627
- if (key.length < 1) {
628
- throw new RangeError("`key` must be at least 1 character long.");
629
- }
656
+ async completeMultipartUpload(path, uploadId, parts, options = {}) {
630
657
  if (!uploadId) {
631
658
  throw new Error("`uploadId` is required.");
632
659
  }
@@ -640,7 +667,7 @@ var S3Client = class {
640
667
  });
641
668
  const response = await this[signedRequest](
642
669
  "POST",
643
- key,
670
+ ensureValidPath(path),
644
671
  `uploadId=${encodeURIComponent(uploadId)}`,
645
672
  body,
646
673
  void 0,
@@ -650,7 +677,7 @@ var S3Client = class {
650
677
  options.signal
651
678
  );
652
679
  if (response.statusCode !== 200) {
653
- throw await getResponseError(response, key);
680
+ throw await getResponseError(response, path);
654
681
  }
655
682
  const text = await response.body.text();
656
683
  const res = ensureParsedXml(text).CompleteMultipartUploadResult ?? {};
@@ -672,10 +699,7 @@ var S3Client = class {
672
699
  * @throws {RangeError} If `key` is not at least 1 character long.
673
700
  * @throws {Error} If `uploadId` is not provided.
674
701
  */
675
- async uploadPart(key, uploadId, data, partNumber, options = {}) {
676
- if (key.length < 1) {
677
- throw new RangeError("`key` must be at least 1 character long.");
678
- }
702
+ async uploadPart(path, uploadId, data, partNumber, options = {}) {
679
703
  if (!uploadId) {
680
704
  throw new Error("`uploadId` is required.");
681
705
  }
@@ -687,7 +711,7 @@ var S3Client = class {
687
711
  }
688
712
  const response = await this[signedRequest](
689
713
  "PUT",
690
- key,
714
+ ensureValidPath(path),
691
715
  `partNumber=${partNumber}&uploadId=${encodeURIComponent(uploadId)}`,
692
716
  data,
693
717
  void 0,
@@ -697,7 +721,7 @@ var S3Client = class {
697
721
  options.signal
698
722
  );
699
723
  if (response.statusCode === 200) {
700
- await response.body.dump();
724
+ response.body.dump();
701
725
  const etag = response.headers.etag;
702
726
  if (typeof etag !== "string" || etag.length === 0) {
703
727
  throw new S3Error("Unknown", "", {
@@ -719,7 +743,7 @@ var S3Client = class {
719
743
  * @throws {RangeError} If `options.maxParts` is <= 0.
720
744
  * @throws {TypeError} If `options.partNumberMarker` is not a `string`.
721
745
  */
722
- async listParts(key, uploadId, options = {}) {
746
+ async listParts(path, uploadId, options = {}) {
723
747
  let query = "";
724
748
  if (options.maxParts) {
725
749
  if (typeof options.maxParts !== "number") {
@@ -739,7 +763,7 @@ var S3Client = class {
739
763
  query += `&uploadId=${encodeURIComponent(uploadId)}`;
740
764
  const response = await this[signedRequest](
741
765
  "GET",
742
- key,
766
+ ensureValidPath(path),
743
767
  // We always have a leading &, so we can slice the leading & away (this way, we have less conditionals on the hot path); see benchmark-operations.js
744
768
  query.substring(1),
745
769
  void 0,
@@ -771,7 +795,7 @@ var S3Client = class {
771
795
  )
772
796
  };
773
797
  }
774
- throw await getResponseError(response, key);
798
+ throw await getResponseError(response, path);
775
799
  }
776
800
  //#endregion
777
801
  //#region bucket operations
@@ -824,7 +848,7 @@ var S3Client = class {
824
848
  if (400 <= response.statusCode && response.statusCode < 500) {
825
849
  throw await getResponseError(response, "");
826
850
  }
827
- await response.body.dump();
851
+ response.body.dump();
828
852
  if (response.statusCode === 200) {
829
853
  return;
830
854
  }
@@ -852,7 +876,7 @@ var S3Client = class {
852
876
  if (400 <= response.statusCode && response.statusCode < 500) {
853
877
  throw await getResponseError(response, "");
854
878
  }
855
- await response.body.dump();
879
+ response.body.dump();
856
880
  if (response.statusCode === 204) {
857
881
  return;
858
882
  }
@@ -879,7 +903,7 @@ var S3Client = class {
879
903
  if (response.statusCode !== 404 && 400 <= response.statusCode && response.statusCode < 500) {
880
904
  throw await getResponseError(response, "");
881
905
  }
882
- await response.body.dump();
906
+ response.body.dump();
883
907
  if (response.statusCode === 200) {
884
908
  return true;
885
909
  }
@@ -932,6 +956,12 @@ var S3Client = class {
932
956
  }
933
957
  query += `&max-keys=${options.maxKeys}`;
934
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
+ }
935
965
  if (options.prefix) {
936
966
  if (typeof options.prefix !== "string") {
937
967
  throw new TypeError("`prefix` must be a `string`.");
@@ -952,7 +982,7 @@ var S3Client = class {
952
982
  void 0,
953
983
  void 0,
954
984
  void 0,
955
- options.bucket ?? this.#options.bucket,
985
+ ensureValidBucketName(options.bucket ?? this.#options.bucket),
956
986
  options.signal
957
987
  );
958
988
  if (response.statusCode !== 200) {
@@ -1004,7 +1034,7 @@ var S3Client = class {
1004
1034
  },
1005
1035
  void 0,
1006
1036
  void 0,
1007
- this.#options.bucket,
1037
+ ensureValidBucketName(options.bucket ?? this.#options.bucket),
1008
1038
  options.signal
1009
1039
  );
1010
1040
  if (response.statusCode === 200) {
@@ -1027,7 +1057,7 @@ var S3Client = class {
1027
1057
  versionId: e.VersionId
1028
1058
  })) ?? []
1029
1059
  );
1030
- return errors.length > 0 ? { errors } : null;
1060
+ return { errors };
1031
1061
  }
1032
1062
  if (400 <= response.statusCode && response.statusCode < 500) {
1033
1063
  throw await getResponseError(response, "");
@@ -1299,23 +1329,6 @@ function buildSearchParams(amzCredential, date, expiresIn, headerList, contentHa
1299
1329
  }
1300
1330
  return res;
1301
1331
  }
1302
- function ensureValidBucketName(name) {
1303
- if (name.length < 3 || name.length > 63) {
1304
- throw new Error("`name` must be between 3 and 63 characters long.");
1305
- }
1306
- if (name.startsWith(".") || name.endsWith(".")) {
1307
- throw new Error("`name` must not start or end with a period (.)");
1308
- }
1309
- if (!/^[a-z0-9.-]+$/.test(name)) {
1310
- throw new Error(
1311
- "`name` can only contain lowercase letters, numbers, periods (.), and hyphens (-)."
1312
- );
1313
- }
1314
- if (name.includes("..")) {
1315
- throw new Error("`name` must not contain two adjacent periods (..)");
1316
- }
1317
- return name;
1318
- }
1319
1332
  function ensureParsedXml(text) {
1320
1333
  try {
1321
1334
  const r = xmlParser2.parse(text);
@@ -1345,9 +1358,7 @@ var S3File = class _S3File {
1345
1358
  #start;
1346
1359
  #end;
1347
1360
  #contentType;
1348
- /**
1349
- * @internal
1350
- */
1361
+ /** @internal */
1351
1362
  constructor(client, path, start, end, contentType) {
1352
1363
  if (typeof start === "number" && start < 0) {
1353
1364
  throw new Error("Invalid slice `start`.");
@@ -1378,7 +1389,7 @@ var S3File = class _S3File {
1378
1389
  * @throws {S3Error} If the file does not exist or the server has some other issues.
1379
1390
  * @throws {Error} If the server returns an invalid response.
1380
1391
  */
1381
- async stat({ signal } = {}) {
1392
+ async stat(options = {}) {
1382
1393
  const response = await this.#client[signedRequest](
1383
1394
  "HEAD",
1384
1395
  this.#path,
@@ -1388,9 +1399,9 @@ var S3File = class _S3File {
1388
1399
  void 0,
1389
1400
  void 0,
1390
1401
  void 0,
1391
- signal
1402
+ options.signal
1392
1403
  );
1393
- await response.body.dump();
1404
+ response.body.dump();
1394
1405
  if (200 <= response.statusCode && response.statusCode < 300) {
1395
1406
  const result = S3Stat.tryParseFromHeaders(response.headers);
1396
1407
  if (!result) {
@@ -1409,9 +1420,7 @@ var S3File = class _S3File {
1409
1420
  *
1410
1421
  * @remarks Uses [`HeadObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
1411
1422
  */
1412
- async exists({
1413
- signal
1414
- } = {}) {
1423
+ async exists(options = {}) {
1415
1424
  const response = await this.#client[signedRequest](
1416
1425
  "HEAD",
1417
1426
  this.#path,
@@ -1421,9 +1430,9 @@ var S3File = class _S3File {
1421
1430
  void 0,
1422
1431
  void 0,
1423
1432
  void 0,
1424
- signal
1433
+ options.signal
1425
1434
  );
1426
- await response.body.dump();
1435
+ response.body.dump();
1427
1436
  if (200 <= response.statusCode && response.statusCode < 300) {
1428
1437
  return true;
1429
1438
  }
@@ -1445,18 +1454,18 @@ var S3File = class _S3File {
1445
1454
  * @example
1446
1455
  * ```js
1447
1456
  * // Simple delete
1448
- * await client.unlink("old-file.txt");
1457
+ * await client.delete("old-file.txt");
1449
1458
  *
1450
1459
  * // With error handling
1451
1460
  * try {
1452
- * await client.unlink("file.dat");
1461
+ * await client.delete("file.dat");
1453
1462
  * console.log("File deleted");
1454
1463
  * } catch (err) {
1455
1464
  * console.error("Delete failed:", err);
1456
1465
  * }
1457
1466
  * ```
1458
1467
  */
1459
- async delete({ signal } = {}) {
1468
+ async delete(options = {}) {
1460
1469
  const response = await this.#client[signedRequest](
1461
1470
  "DELETE",
1462
1471
  this.#path,
@@ -1466,10 +1475,10 @@ var S3File = class _S3File {
1466
1475
  void 0,
1467
1476
  void 0,
1468
1477
  void 0,
1469
- signal
1478
+ options.signal
1470
1479
  );
1471
1480
  if (response.statusCode === 204) {
1472
- await response.body.dump();
1481
+ response.body.dump();
1473
1482
  return;
1474
1483
  }
1475
1484
  throw await getResponseError(response, this.#path);
@@ -1549,10 +1558,6 @@ var S3File = class _S3File {
1549
1558
  }
1550
1559
  /*
1551
1560
  // Future API?
1552
- writer(): WritableStream<ArrayBufferLike | ArrayBufferView> {
1553
- throw new Error("Not implemented");
1554
- }
1555
- // Future API?
1556
1561
  setTags(): Promise<void> {
1557
1562
  throw new Error("Not implemented");
1558
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.0",
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",