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 +33 -18
- package/dist/index.js +73 -68
- package/package.json +5 -6
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
418
|
+
* await client.delete("old-file.txt");
|
|
404
419
|
*
|
|
405
420
|
* // With error handling
|
|
406
421
|
* try {
|
|
407
|
-
* await client.
|
|
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(
|
|
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(
|
|
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,
|
|
@@ -697,7 +721,7 @@ var S3Client = class {
|
|
|
697
721
|
options.signal
|
|
698
722
|
);
|
|
699
723
|
if (response.statusCode === 200) {
|
|
700
|
-
|
|
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(
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
1457
|
+
* await client.delete("old-file.txt");
|
|
1449
1458
|
*
|
|
1450
1459
|
* // With error handling
|
|
1451
1460
|
* try {
|
|
1452
|
-
* await client.
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
55
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
56
|
-
"expect": "^30.0.
|
|
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",
|