lean-s3 0.7.3 → 0.7.4

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
@@ -27,10 +27,13 @@ type Brand<B> = {
27
27
  type Branded<T, B> = T & Brand<B>;
28
28
  type BucketName = Branded<string, "BucketName">;
29
29
  type ObjectKey = Branded<string, "ObjectKey">;
30
+ type Endpoint = Branded<string, "Endpoint">;
31
+ type Region = Branded<string, "Region">;
30
32
 
31
- declare const write: unique symbol;
32
- declare const stream: unique symbol;
33
- declare const signedRequest: unique symbol;
33
+ declare const kWrite: unique symbol;
34
+ declare const kStream: unique symbol;
35
+ declare const kSignedRequest: unique symbol;
36
+ declare const kGetEffectiveParams: unique symbol;
34
37
  interface S3ClientOptions {
35
38
  bucket: string;
36
39
  region: string;
@@ -287,6 +290,7 @@ declare class S3Client {
287
290
  * @param options The default options to use for the S3 client.
288
291
  */
289
292
  constructor(options: S3ClientOptions);
293
+ [kGetEffectiveParams](region: string | undefined | null, endpoint: string | undefined | null, bucket: string | undefined | null): [region: Region, endpoint: Endpoint, bucket: BucketName];
290
294
  /**
291
295
  * Creates an S3File instance for the given path.
292
296
  *
@@ -346,7 +350,7 @@ declare class S3Client {
346
350
  * });
347
351
  * ```
348
352
  */
349
- presign(path: string, optio2ns?: S3FilePresignOptions): string;
353
+ presign(path: string, options?: S3FilePresignOptions): string;
350
354
  createMultipartUpload(key: string, options?: CreateMultipartUploadOptions): Promise<CreateMultipartUploadResult>;
351
355
  /**
352
356
  * @remarks Uses [`ListMultipartUploads`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html).
@@ -441,16 +445,16 @@ declare class S3Client {
441
445
  * TODO: Maybe move this into a separate free function?
442
446
  * @internal
443
447
  */
444
- [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>>;
448
+ [kSignedRequest](region: Region, endpoint: Endpoint, bucket: BucketName, method: HttpMethod, pathWithoutBucket: ObjectKey, query: string | undefined, body: UndiciBodyInit | undefined, additionalSignedHeaders: Record<string, string> | undefined, additionalUnsignedHeaders: Record<string, string> | undefined, contentHash: Buffer | undefined, signal?: AbortSignal | undefined): Promise<Dispatcher.ResponseData<null>>;
445
449
  /**
446
450
  * @internal
447
451
  * @param {import("./index.d.ts").UndiciBodyInit} data TODO
448
452
  */
449
- [write](path: string, data: UndiciBodyInit, contentType: string, contentLength: number | undefined, contentHash: Buffer | undefined, rageStart: number | undefined, rangeEndExclusive: number | undefined, signal?: AbortSignal | undefined): Promise<void>;
453
+ [kWrite](path: ObjectKey, data: UndiciBodyInit, contentType: string, contentLength: number | undefined, contentHash: Buffer | undefined, rageStart: number | undefined, rangeEndExclusive: number | undefined, signal?: AbortSignal | undefined): Promise<void>;
450
454
  /**
451
455
  * @internal
452
456
  */
453
- [stream](path: string, contentHash: Buffer | undefined, rageStart: number | undefined, rangeEndExclusive: number | undefined): stream_web.ReadableStream<Uint8Array<ArrayBufferLike>>;
457
+ [kStream](path: ObjectKey, contentHash: Buffer | undefined, rageStart: number | undefined, rangeEndExclusive: number | undefined): stream_web.ReadableStream<Uint8Array<ArrayBufferLike>>;
454
458
  }
455
459
 
456
460
  declare class S3Stat {
@@ -470,7 +474,19 @@ declare class S3File {
470
474
  #private;
471
475
  /** @internal */
472
476
  constructor(client: S3Client, path: ObjectKey, start: number | undefined, end: number | undefined, contentType: string | undefined);
473
- slice(start?: number | undefined, end?: number | undefined, contentType?: string | undefined): S3File;
477
+ /**
478
+ * Creates and returns a new {@link S3File} containing a subset of this {@link S3File} data.
479
+ * @param start The starting index.
480
+ * @param end The ending index, exclusive.
481
+ */
482
+ slice(start?: number, end?: number): S3File;
483
+ /**
484
+ * Creates and returns a new {@link S3File} containing a subset of this {@link S3File} data.
485
+ * @param start The starting index.
486
+ * @param end The ending index, exclusive.
487
+ * @param contentType The content-type for the new {@link S3File}.
488
+ */
489
+ slice(start?: number, end?: number, contentType?: string): S3File;
474
490
  /**
475
491
  * Get the stat of a file in the bucket. Uses `HEAD` request to check existence.
476
492
  *
package/dist/index.js CHANGED
@@ -320,6 +320,9 @@ function getAuthorizationHeader(keyCache, method, path, query, date, sortedSigne
320
320
 
321
321
  // src/branded.ts
322
322
  function ensureValidBucketName(name) {
323
+ if (typeof name !== "string") {
324
+ throw new TypeError("`name` must be a `string`.");
325
+ }
323
326
  if (name.length < 3 || name.length > 63) {
324
327
  throw new Error("`name` must be between 3 and 63 characters long.");
325
328
  }
@@ -345,6 +348,24 @@ function ensureValidPath(path) {
345
348
  }
346
349
  return path;
347
350
  }
351
+ function ensureValidEndpoint(endpoint) {
352
+ if (typeof endpoint !== "string") {
353
+ throw new TypeError("`endpoint` must be a `string`.");
354
+ }
355
+ if (endpoint.length < 1) {
356
+ throw new RangeError("`endpoint` must be at least 1 character long.");
357
+ }
358
+ return endpoint;
359
+ }
360
+ function ensureValidRegion(region) {
361
+ if (typeof region !== "string") {
362
+ throw new TypeError("`region` must be a `string`.");
363
+ }
364
+ if (region.length < 1) {
365
+ throw new RangeError("`region` must be at least 1 character long.");
366
+ }
367
+ return region;
368
+ }
348
369
 
349
370
  // src/assertNever.ts
350
371
  function assertNever(v) {
@@ -373,9 +394,10 @@ function encodeURIComponentExtended(value) {
373
394
  }
374
395
 
375
396
  // src/S3Client.ts
376
- var write = Symbol("write");
377
- var stream = Symbol("stream");
378
- var signedRequest = Symbol("signedRequest");
397
+ var kWrite = Symbol("kWrite");
398
+ var kStream = Symbol("kStream");
399
+ var kSignedRequest = Symbol("kSignedRequest");
400
+ var kGetEffectiveParams = Symbol("kGetEffectiveParams");
379
401
  var xmlParser2 = new XMLParser2({
380
402
  ignoreAttributes: true,
381
403
  isArray: (_, jPath) => jPath === "ListMultipartUploadsResult.Upload" || jPath === "ListBucketResult.Contents" || jPath === "ListPartsResult.Part" || jPath === "DeleteResult.Deleted" || jPath === "DeleteResult.Error"
@@ -387,7 +409,7 @@ var xmlBuilder = new XMLBuilder({
387
409
  var S3Client = class {
388
410
  #options;
389
411
  #keyCache = new KeyCache();
390
- // TODO: pass options to this in client? Do we want to expose tjhe internal use of undici?
412
+ // TODO: pass options to this in client? Do we want to expose the internal use of undici?
391
413
  #dispatcher = new Agent();
392
414
  /**
393
415
  * Create a new instance of an S3 bucket so that credentials can be managed from a single instance instead of being passed to every method.
@@ -424,26 +446,19 @@ var S3Client = class {
424
446
  this.#options = {
425
447
  accessKeyId,
426
448
  secretAccessKey,
427
- endpoint,
428
- region,
429
- bucket,
449
+ endpoint: ensureValidEndpoint(options.endpoint),
450
+ region: ensureValidRegion(options.region),
451
+ bucket: ensureValidBucketName(options.bucket),
430
452
  sessionToken
431
453
  };
432
454
  }
433
- // Maybe future API
434
- /*
435
- cors = {
436
- get: () => {
437
- // TODO: GetBucketCors
438
- },
439
- set: () => {
440
- // TODO: PutBucketCors
441
- },
442
- delete: () => {
443
- // TODO: DeleteBucketCors
444
- },
445
- };
446
- */
455
+ [kGetEffectiveParams](region, endpoint, bucket) {
456
+ return [
457
+ ensureValidRegion(region ?? this.#options.region),
458
+ ensureValidEndpoint(endpoint ?? this.#options.endpoint),
459
+ ensureValidBucketName(bucket ?? this.#options.bucket)
460
+ ];
461
+ }
447
462
  /**
448
463
  * Creates an S3File instance for the given path.
449
464
  *
@@ -511,33 +526,40 @@ var S3Client = class {
511
526
  * });
512
527
  * ```
513
528
  */
514
- presign(path, optio2ns = {}) {
515
- const contentLength = optio2ns.contentLength ?? void 0;
529
+ presign(path, options = {}) {
530
+ const contentLength = options.contentLength ?? void 0;
516
531
  if (typeof contentLength === "number") {
517
532
  if (contentLength < 0) {
518
533
  throw new RangeError("`contentLength` must be >= 0.");
519
534
  }
520
535
  }
521
- const method = optio2ns.method ?? "GET";
522
- const contentType = optio2ns.type ?? void 0;
523
- const region = optio2ns.region ?? this.#options.region;
524
- const bucket = optio2ns.bucket ?? this.#options.bucket;
525
- const endpoint = optio2ns.endpoint ?? this.#options.endpoint;
526
- const responseOptions = optio2ns.response;
536
+ const method = options.method ?? "GET";
537
+ const contentType = options.type ?? void 0;
538
+ const [region, endpoint, bucket] = this[kGetEffectiveParams](
539
+ options.region,
540
+ options.endpoint,
541
+ options.bucket
542
+ );
543
+ const responseOptions = options.response;
527
544
  const contentDisposition = responseOptions?.contentDisposition;
528
545
  const responseContentDisposition = contentDisposition ? getContentDispositionHeader(contentDisposition) : void 0;
529
- const res = buildRequestUrl(endpoint, bucket, region, path);
546
+ const res = buildRequestUrl(
547
+ endpoint,
548
+ bucket,
549
+ region,
550
+ ensureValidPath(path)
551
+ );
530
552
  const now2 = /* @__PURE__ */ new Date();
531
553
  const date = getAmzDate(now2);
532
554
  const query = buildSearchParams(
533
555
  `${this.#options.accessKeyId}/${date.date}/${region}/s3/aws4_request`,
534
556
  date,
535
- optio2ns.expiresIn ?? 3600,
557
+ options.expiresIn ?? 3600,
536
558
  typeof contentLength === "number" || typeof contentType === "string" ? typeof contentLength === "number" && typeof contentType === "string" ? "content-length;content-type;host" : typeof contentLength === "number" ? "content-length;host" : typeof contentType === "string" ? "content-type;host" : "" : "host",
537
559
  unsignedPayload,
538
- optio2ns.storageClass,
560
+ options.storageClass,
539
561
  this.#options.sessionToken,
540
- optio2ns.acl,
562
+ options.acl,
541
563
  responseContentDisposition
542
564
  );
543
565
  const dataDigest = typeof contentLength === "number" || typeof contentType === "string" ? createCanonicalDataDigest(
@@ -575,7 +597,10 @@ var S3Client = class {
575
597
  async createMultipartUpload(key, options = {}) {
576
598
  if (key.length < 1) {
577
599
  }
578
- const response = await this[signedRequest](
600
+ const response = await this[kSignedRequest](
601
+ this.#options.region,
602
+ this.#options.endpoint,
603
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
579
604
  "POST",
580
605
  ensureValidPath(key),
581
606
  "uploads=",
@@ -583,7 +608,6 @@ var S3Client = class {
583
608
  void 0,
584
609
  void 0,
585
610
  void 0,
586
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
587
611
  options.signal
588
612
  );
589
613
  if (response.statusCode !== 200) {
@@ -602,9 +626,6 @@ var S3Client = class {
602
626
  * @throws {RangeError} If `options.maxKeys` is not between `1` and `1000`.
603
627
  */
604
628
  async listMultipartUploads(options = {}) {
605
- const bucket = ensureValidBucketName(
606
- options.bucket ?? this.#options.bucket
607
- );
608
629
  let query = "uploads=";
609
630
  if (options.delimiter) {
610
631
  if (typeof options.delimiter !== "string") {
@@ -633,7 +654,10 @@ var S3Client = class {
633
654
  }
634
655
  query += `&prefix=${encodeURIComponent(options.prefix)}`;
635
656
  }
636
- const response = await this[signedRequest](
657
+ const response = await this[kSignedRequest](
658
+ this.#options.region,
659
+ this.#options.endpoint,
660
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
637
661
  "GET",
638
662
  "",
639
663
  query,
@@ -641,7 +665,6 @@ var S3Client = class {
641
665
  void 0,
642
666
  void 0,
643
667
  void 0,
644
- bucket,
645
668
  options.signal
646
669
  );
647
670
  if (response.statusCode !== 200) {
@@ -684,7 +707,10 @@ var S3Client = class {
684
707
  if (!uploadId) {
685
708
  throw new Error("`uploadId` is required.");
686
709
  }
687
- const response = await this[signedRequest](
710
+ const response = await this[kSignedRequest](
711
+ this.#options.region,
712
+ this.#options.endpoint,
713
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
688
714
  "DELETE",
689
715
  ensureValidPath(path),
690
716
  `uploadId=${encodeURIComponent(uploadId)}`,
@@ -692,10 +718,9 @@ var S3Client = class {
692
718
  void 0,
693
719
  void 0,
694
720
  void 0,
695
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
696
721
  options.signal
697
722
  );
698
- if (response.statusCode !== 204) {
723
+ if (response.statusCode !== 204 && response.statusCode !== 200) {
699
724
  throw await getResponseError(response, path);
700
725
  }
701
726
  }
@@ -716,7 +741,10 @@ var S3Client = class {
716
741
  }))
717
742
  }
718
743
  });
719
- const response = await this[signedRequest](
744
+ const response = await this[kSignedRequest](
745
+ this.#options.region,
746
+ this.#options.endpoint,
747
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
720
748
  "POST",
721
749
  ensureValidPath(path),
722
750
  `uploadId=${encodeURIComponent(uploadId)}`,
@@ -724,7 +752,6 @@ var S3Client = class {
724
752
  void 0,
725
753
  void 0,
726
754
  void 0,
727
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
728
755
  options.signal
729
756
  );
730
757
  if (response.statusCode !== 200) {
@@ -760,7 +787,10 @@ var S3Client = class {
760
787
  if (typeof partNumber !== "number" || partNumber <= 0) {
761
788
  throw new Error("`partNumber` has to be a `number` which is >= 1.");
762
789
  }
763
- const response = await this[signedRequest](
790
+ const response = await this[kSignedRequest](
791
+ this.#options.region,
792
+ this.#options.endpoint,
793
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
764
794
  "PUT",
765
795
  ensureValidPath(path),
766
796
  `partNumber=${partNumber}&uploadId=${encodeURIComponent(uploadId)}`,
@@ -768,7 +798,6 @@ var S3Client = class {
768
798
  void 0,
769
799
  void 0,
770
800
  void 0,
771
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
772
801
  options.signal
773
802
  );
774
803
  if (response.statusCode === 200) {
@@ -812,7 +841,10 @@ var S3Client = class {
812
841
  query += `&part-number-marker=${encodeURIComponent(options.partNumberMarker)}`;
813
842
  }
814
843
  query += `&uploadId=${encodeURIComponent(uploadId)}`;
815
- const response = await this[signedRequest](
844
+ const response = await this[kSignedRequest](
845
+ this.#options.region,
846
+ this.#options.endpoint,
847
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
816
848
  "GET",
817
849
  ensureValidPath(path),
818
850
  // 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
@@ -821,7 +853,6 @@ var S3Client = class {
821
853
  void 0,
822
854
  void 0,
823
855
  void 0,
824
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
825
856
  options?.signal
826
857
  );
827
858
  if (response.statusCode === 200) {
@@ -885,7 +916,10 @@ var S3Client = class {
885
916
  }) : void 0;
886
917
  }
887
918
  const additionalSignedHeaders = body ? { "content-md5": md5Base64(body) } : void 0;
888
- const response = await this[signedRequest](
919
+ const response = await this[kSignedRequest](
920
+ this.#options.region,
921
+ this.#options.endpoint,
922
+ ensureValidBucketName(name),
889
923
  "PUT",
890
924
  "",
891
925
  void 0,
@@ -893,7 +927,6 @@ var S3Client = class {
893
927
  additionalSignedHeaders,
894
928
  void 0,
895
929
  void 0,
896
- ensureValidBucketName(name),
897
930
  options?.signal
898
931
  );
899
932
  if (400 <= response.statusCode && response.statusCode < 500) {
@@ -913,7 +946,10 @@ var S3Client = class {
913
946
  * @remarks Uses [`DeleteBucket`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html).
914
947
  */
915
948
  async deleteBucket(name, options) {
916
- const response = await this[signedRequest](
949
+ const response = await this[kSignedRequest](
950
+ this.#options.region,
951
+ this.#options.endpoint,
952
+ ensureValidBucketName(name),
917
953
  "DELETE",
918
954
  "",
919
955
  void 0,
@@ -921,7 +957,6 @@ var S3Client = class {
921
957
  void 0,
922
958
  void 0,
923
959
  void 0,
924
- ensureValidBucketName(name),
925
960
  options?.signal
926
961
  );
927
962
  if (400 <= response.statusCode && response.statusCode < 500) {
@@ -940,7 +975,10 @@ var S3Client = class {
940
975
  * @remarks Uses [`HeadBucket`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html).
941
976
  */
942
977
  async bucketExists(name, options) {
943
- const response = await this[signedRequest](
978
+ const response = await this[kSignedRequest](
979
+ this.#options.region,
980
+ this.#options.endpoint,
981
+ ensureValidBucketName(name),
944
982
  "HEAD",
945
983
  "",
946
984
  void 0,
@@ -948,7 +986,6 @@ var S3Client = class {
948
986
  void 0,
949
987
  void 0,
950
988
  void 0,
951
- ensureValidBucketName(name),
952
989
  options?.signal
953
990
  );
954
991
  if (response.statusCode !== 404 && 400 <= response.statusCode && response.statusCode < 500) {
@@ -979,7 +1016,10 @@ var S3Client = class {
979
1016
  }))
980
1017
  }
981
1018
  });
982
- const response = await this[signedRequest](
1019
+ const response = await this[kSignedRequest](
1020
+ this.#options.region,
1021
+ this.#options.endpoint,
1022
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
983
1023
  "PUT",
984
1024
  "",
985
1025
  "cors=",
@@ -990,7 +1030,6 @@ var S3Client = class {
990
1030
  },
991
1031
  void 0,
992
1032
  void 0,
993
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
994
1033
  options.signal
995
1034
  );
996
1035
  if (response.statusCode === 200) {
@@ -1008,7 +1047,10 @@ var S3Client = class {
1008
1047
  * @remarks Uses [`GetBucketCors`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketCors.html).
1009
1048
  */
1010
1049
  async getBucketCors(options = {}) {
1011
- const response = await this[signedRequest](
1050
+ const response = await this[kSignedRequest](
1051
+ this.#options.region,
1052
+ this.#options.endpoint,
1053
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
1012
1054
  "GET",
1013
1055
  "",
1014
1056
  "cors=",
@@ -1017,7 +1059,6 @@ var S3Client = class {
1017
1059
  void 0,
1018
1060
  void 0,
1019
1061
  void 0,
1020
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
1021
1062
  options.signal
1022
1063
  );
1023
1064
  if (response.statusCode !== 200) {
@@ -1030,7 +1071,10 @@ var S3Client = class {
1030
1071
  * @remarks Uses [`DeleteBucketCors`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html).
1031
1072
  */
1032
1073
  async deleteBucketCors(options = {}) {
1033
- const response = await this[signedRequest](
1074
+ const response = await this[kSignedRequest](
1075
+ this.#options.region,
1076
+ this.#options.endpoint,
1077
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
1034
1078
  "DELETE",
1035
1079
  "",
1036
1080
  "cors=",
@@ -1039,7 +1083,6 @@ var S3Client = class {
1039
1083
  void 0,
1040
1084
  void 0,
1041
1085
  void 0,
1042
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
1043
1086
  options.signal
1044
1087
  );
1045
1088
  if (response.statusCode !== 204) {
@@ -1109,7 +1152,10 @@ var S3Client = class {
1109
1152
  }
1110
1153
  query += `&start-after=${encodeURIComponent(options.startAfter)}`;
1111
1154
  }
1112
- const response = await this[signedRequest](
1155
+ const response = await this[kSignedRequest](
1156
+ ensureValidRegion(this.#options.region),
1157
+ ensureValidEndpoint(this.#options.endpoint),
1158
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
1113
1159
  "GET",
1114
1160
  "",
1115
1161
  query,
@@ -1117,7 +1163,6 @@ var S3Client = class {
1117
1163
  void 0,
1118
1164
  void 0,
1119
1165
  void 0,
1120
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
1121
1166
  options.signal
1122
1167
  );
1123
1168
  if (response.statusCode !== 200) {
@@ -1158,7 +1203,10 @@ var S3Client = class {
1158
1203
  }))
1159
1204
  }
1160
1205
  });
1161
- const response = await this[signedRequest](
1206
+ const response = await this[kSignedRequest](
1207
+ this.#options.region,
1208
+ this.#options.endpoint,
1209
+ options.bucket ? ensureValidBucketName(options.bucket) : this.#options.bucket,
1162
1210
  "POST",
1163
1211
  "",
1164
1212
  "delete=",
@@ -1169,7 +1217,6 @@ var S3Client = class {
1169
1217
  },
1170
1218
  void 0,
1171
1219
  void 0,
1172
- ensureValidBucketName(options.bucket ?? this.#options.bucket),
1173
1220
  options.signal
1174
1221
  );
1175
1222
  if (response.statusCode === 200) {
@@ -1207,16 +1254,8 @@ var S3Client = class {
1207
1254
  * TODO: Maybe move this into a separate free function?
1208
1255
  * @internal
1209
1256
  */
1210
- async [signedRequest](method, pathWithoutBucket, query, body, additionalSignedHeaders, additionalUnsignedHeaders, contentHash, bucket, signal = void 0) {
1211
- const endpoint = this.#options.endpoint;
1212
- const region = this.#options.region;
1213
- const effectiveBucket = bucket ?? this.#options.bucket;
1214
- const url = buildRequestUrl(
1215
- endpoint,
1216
- effectiveBucket,
1217
- region,
1218
- pathWithoutBucket
1219
- );
1257
+ async [kSignedRequest](region, endpoint, bucket, method, pathWithoutBucket, query, body, additionalSignedHeaders, additionalUnsignedHeaders, contentHash, signal = void 0) {
1258
+ const url = buildRequestUrl(endpoint, bucket, region, pathWithoutBucket);
1220
1259
  if (query) {
1221
1260
  url.search = query;
1222
1261
  }
@@ -1264,7 +1303,7 @@ var S3Client = class {
1264
1303
  * @internal
1265
1304
  * @param {import("./index.d.ts").UndiciBodyInit} data TODO
1266
1305
  */
1267
- async [write](path, data, contentType, contentLength, contentHash, rageStart, rangeEndExclusive, signal = void 0) {
1306
+ async [kWrite](path, data, contentType, contentLength, contentHash, rageStart, rangeEndExclusive, signal = void 0) {
1268
1307
  const bucket = this.#options.bucket;
1269
1308
  const endpoint = this.#options.endpoint;
1270
1309
  const region = this.#options.region;
@@ -1320,7 +1359,7 @@ var S3Client = class {
1320
1359
  /**
1321
1360
  * @internal
1322
1361
  */
1323
- [stream](path, contentHash, rageStart, rangeEndExclusive) {
1362
+ [kStream](path, contentHash, rageStart, rangeEndExclusive) {
1324
1363
  const bucket = this.#options.bucket;
1325
1364
  const endpoint = this.#options.endpoint;
1326
1365
  const region = this.#options.region;
@@ -1505,7 +1544,12 @@ var S3File = class _S3File {
1505
1544
  this.#end = end;
1506
1545
  this.#contentType = contentType ?? "application/octet-stream";
1507
1546
  }
1508
- // TODO: slice overloads
1547
+ /**
1548
+ * Creates and returns a new {@link S3File} containing a subset of this {@link S3File} data.
1549
+ * @param start The starting index.
1550
+ * @param end The ending index, exclusive.
1551
+ * @param contentType The content-type for the new {@link S3File}.
1552
+ */
1509
1553
  slice(start, end, contentType) {
1510
1554
  return new _S3File(
1511
1555
  this.#client,
@@ -1523,7 +1567,15 @@ var S3File = class _S3File {
1523
1567
  * @throws {Error} If the server returns an invalid response.
1524
1568
  */
1525
1569
  async stat(options = {}) {
1526
- const response = await this.#client[signedRequest](
1570
+ const [region, endpoint, bucket] = this.#client[kGetEffectiveParams](
1571
+ options.region,
1572
+ options.endpoint,
1573
+ options.bucket
1574
+ );
1575
+ const response = await this.#client[kSignedRequest](
1576
+ region,
1577
+ endpoint,
1578
+ bucket,
1527
1579
  "HEAD",
1528
1580
  this.#path,
1529
1581
  void 0,
@@ -1531,7 +1583,6 @@ var S3File = class _S3File {
1531
1583
  void 0,
1532
1584
  void 0,
1533
1585
  void 0,
1534
- void 0,
1535
1586
  options.signal
1536
1587
  );
1537
1588
  response.body.dump();
@@ -1554,7 +1605,15 @@ var S3File = class _S3File {
1554
1605
  * @remarks Uses [`HeadObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
1555
1606
  */
1556
1607
  async exists(options = {}) {
1557
- const response = await this.#client[signedRequest](
1608
+ const [region, endpoint, bucket] = this.#client[kGetEffectiveParams](
1609
+ options.region,
1610
+ options.endpoint,
1611
+ options.bucket
1612
+ );
1613
+ const response = await this.#client[kSignedRequest](
1614
+ region,
1615
+ endpoint,
1616
+ bucket,
1558
1617
  "HEAD",
1559
1618
  this.#path,
1560
1619
  void 0,
@@ -1562,7 +1621,6 @@ var S3File = class _S3File {
1562
1621
  void 0,
1563
1622
  void 0,
1564
1623
  void 0,
1565
- void 0,
1566
1624
  options.signal
1567
1625
  );
1568
1626
  response.body.dump();
@@ -1599,7 +1657,15 @@ var S3File = class _S3File {
1599
1657
  * ```
1600
1658
  */
1601
1659
  async delete(options = {}) {
1602
- const response = await this.#client[signedRequest](
1660
+ const [region, endpoint, bucket] = this.#client[kGetEffectiveParams](
1661
+ options.region,
1662
+ options.endpoint,
1663
+ options.bucket
1664
+ );
1665
+ const response = await this.#client[kSignedRequest](
1666
+ region,
1667
+ endpoint,
1668
+ bucket,
1603
1669
  "DELETE",
1604
1670
  this.#path,
1605
1671
  void 0,
@@ -1607,7 +1673,6 @@ var S3File = class _S3File {
1607
1673
  void 0,
1608
1674
  void 0,
1609
1675
  void 0,
1610
- void 0,
1611
1676
  options.signal
1612
1677
  );
1613
1678
  if (response.statusCode === 204) {
@@ -1638,7 +1703,7 @@ var S3File = class _S3File {
1638
1703
  }
1639
1704
  /** @returns {ReadableStream<Uint8Array>} */
1640
1705
  stream() {
1641
- return this.#client[stream](this.#path, void 0, this.#start, this.#end);
1706
+ return this.#client[kStream](this.#path, void 0, this.#start, this.#end);
1642
1707
  }
1643
1708
  async #transformData(data) {
1644
1709
  if (typeof data === "string") {
@@ -1681,7 +1746,7 @@ var S3File = class _S3File {
1681
1746
  async write(data, options = {}) {
1682
1747
  const signal = void 0;
1683
1748
  const [bytes, length, hash] = await this.#transformData(data);
1684
- return await this.#client[write](
1749
+ return await this.#client[kWrite](
1685
1750
  this.#path,
1686
1751
  bytes,
1687
1752
  options.type ?? this.#contentType,
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.7.3",
5
+ "version": "0.7.4",
6
6
  "description": "A server-side S3 API for the regular user.",
7
7
  "keywords": [
8
8
  "s3",
@@ -47,13 +47,14 @@
47
47
  "undici": "^7.11.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@biomejs/biome": "2.0.6",
51
- "@testcontainers/localstack": "^11.2.0",
52
- "@testcontainers/minio": "^11.2.0",
50
+ "@biomejs/biome": "2.1.1",
51
+ "@testcontainers/localstack": "^11.2.1",
52
+ "@testcontainers/minio": "^11.2.1",
53
53
  "@types/node": "^24.0.10",
54
- "@typescript/native-preview": "^7.0.0-dev.20250705.1",
54
+ "@typescript/native-preview": "^7.0.0-dev.20250708.1",
55
55
  "expect": "^30.0.4",
56
- "lefthook": "^1.11.16",
56
+ "lefthook": "^1.12.0",
57
+ "testcontainers": "^11.2.1",
57
58
  "tsup": "^8.5.0",
58
59
  "tsx": "^4.20.3",
59
60
  "typedoc": "^0.28.7"