lean-s3 0.6.0 → 0.6.1
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 +15 -6
- package/dist/index.js +49 -42
- package/package.json +1 -1
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,6 +42,7 @@ 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
|
};
|
|
39
48
|
interface S3FilePresignOptions {
|
|
@@ -274,19 +283,19 @@ declare class S3Client {
|
|
|
274
283
|
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
275
284
|
* @throws {Error} If `uploadId` is not provided.
|
|
276
285
|
*/
|
|
277
|
-
abortMultipartUpload(
|
|
286
|
+
abortMultipartUpload(path: string, uploadId: string, options?: AbortMultipartUploadOptions): Promise<void>;
|
|
278
287
|
/**
|
|
279
288
|
* @remarks Uses [`CompleteMultipartUpload`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html).
|
|
280
289
|
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
281
290
|
* @throws {Error} If `uploadId` is not provided.
|
|
282
291
|
*/
|
|
283
|
-
completeMultipartUpload(
|
|
292
|
+
completeMultipartUpload(path: string, uploadId: string, parts: readonly MultipartUploadPart[], options?: CompleteMultipartUploadOptions): Promise<CompleteMultipartUploadResult>;
|
|
284
293
|
/**
|
|
285
294
|
* @remarks Uses [`UploadPart`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html).
|
|
286
295
|
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
287
296
|
* @throws {Error} If `uploadId` is not provided.
|
|
288
297
|
*/
|
|
289
|
-
uploadPart(
|
|
298
|
+
uploadPart(path: string, uploadId: string, data: UndiciBodyInit, partNumber: number, options?: UploadPartOptions): Promise<UploadPartResult>;
|
|
290
299
|
/**
|
|
291
300
|
* @remarks Uses [`ListParts`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html).
|
|
292
301
|
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
@@ -295,7 +304,7 @@ declare class S3Client {
|
|
|
295
304
|
* @throws {RangeError} If `options.maxParts` is <= 0.
|
|
296
305
|
* @throws {TypeError} If `options.partNumberMarker` is not a `string`.
|
|
297
306
|
*/
|
|
298
|
-
listParts(
|
|
307
|
+
listParts(path: string, uploadId: string, options?: ListPartsOptions): Promise<ListPartsResult>;
|
|
299
308
|
/**
|
|
300
309
|
* Creates a new bucket on the S3 server.
|
|
301
310
|
*
|
|
@@ -347,7 +356,7 @@ declare class S3Client {
|
|
|
347
356
|
* TODO: Maybe move this into a separate free function?
|
|
348
357
|
* @internal
|
|
349
358
|
*/
|
|
350
|
-
[signedRequest](method: HttpMethod, pathWithoutBucket:
|
|
359
|
+
[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
360
|
/**
|
|
352
361
|
* @internal
|
|
353
362
|
* @param {import("./index.d.ts").UndiciBodyInit} data TODO
|
|
@@ -373,7 +382,7 @@ declare class S3File {
|
|
|
373
382
|
/**
|
|
374
383
|
* @internal
|
|
375
384
|
*/
|
|
376
|
-
constructor(client: S3Client, path:
|
|
385
|
+
constructor(client: S3Client, path: ObjectKey, start: number | undefined, end: number | undefined, contentType: string | undefined);
|
|
377
386
|
slice(start?: number | undefined, end?: number | undefined, contentType?: string | undefined): S3File;
|
|
378
387
|
/**
|
|
379
388
|
* Get the stat of a file in the bucket. Uses `HEAD` request to check existence.
|
package/dist/index.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
714
|
+
ensureValidPath(path),
|
|
691
715
|
`partNumber=${partNumber}&uploadId=${encodeURIComponent(uploadId)}`,
|
|
692
716
|
data,
|
|
693
717
|
void 0,
|
|
@@ -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(
|
|
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
|
-
|
|
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,
|
|
798
|
+
throw await getResponseError(response, path);
|
|
775
799
|
}
|
|
776
800
|
//#endregion
|
|
777
801
|
//#region bucket operations
|
|
@@ -952,7 +976,7 @@ var S3Client = class {
|
|
|
952
976
|
void 0,
|
|
953
977
|
void 0,
|
|
954
978
|
void 0,
|
|
955
|
-
options.bucket ?? this.#options.bucket,
|
|
979
|
+
ensureValidBucketName(options.bucket ?? this.#options.bucket),
|
|
956
980
|
options.signal
|
|
957
981
|
);
|
|
958
982
|
if (response.statusCode !== 200) {
|
|
@@ -1004,7 +1028,7 @@ var S3Client = class {
|
|
|
1004
1028
|
},
|
|
1005
1029
|
void 0,
|
|
1006
1030
|
void 0,
|
|
1007
|
-
this.#options.bucket,
|
|
1031
|
+
ensureValidBucketName(options.bucket ?? this.#options.bucket),
|
|
1008
1032
|
options.signal
|
|
1009
1033
|
);
|
|
1010
1034
|
if (response.statusCode === 200) {
|
|
@@ -1299,23 +1323,6 @@ function buildSearchParams(amzCredential, date, expiresIn, headerList, contentHa
|
|
|
1299
1323
|
}
|
|
1300
1324
|
return res;
|
|
1301
1325
|
}
|
|
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
1326
|
function ensureParsedXml(text) {
|
|
1320
1327
|
try {
|
|
1321
1328
|
const r = xmlParser2.parse(text);
|