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 +24 -8
- package/dist/index.js +151 -86
- package/package.json +7 -6
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
|
|
32
|
-
declare const
|
|
33
|
-
declare const
|
|
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,
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
|
|
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
|
|
377
|
-
var
|
|
378
|
-
var
|
|
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
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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,
|
|
515
|
-
const contentLength =
|
|
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 =
|
|
522
|
-
const contentType =
|
|
523
|
-
const region
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
560
|
+
options.storageClass,
|
|
539
561
|
this.#options.sessionToken,
|
|
540
|
-
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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 [
|
|
1211
|
-
const
|
|
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 [
|
|
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
|
-
[
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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[
|
|
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[
|
|
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.
|
|
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.
|
|
51
|
-
"@testcontainers/localstack": "^11.2.
|
|
52
|
-
"@testcontainers/minio": "^11.2.
|
|
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.
|
|
54
|
+
"@typescript/native-preview": "^7.0.0-dev.20250708.1",
|
|
55
55
|
"expect": "^30.0.4",
|
|
56
|
-
"lefthook": "^1.
|
|
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"
|