lean-s3 0.4.1 → 0.6.0
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/README.md +2 -0
- package/dist/index.d.ts +60 -18
- package/dist/index.js +132 -14
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -137,6 +137,8 @@ See [DESIGN_DECISIONS.md](./DESIGN_DECISIONS.md) to read about why this library
|
|
|
137
137
|
- ✅ [`PutObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) via `S3File.write`
|
|
138
138
|
- ✅ [`HeadObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) via `S3File.exists`/`S3File.stat`
|
|
139
139
|
- ✅ [`ListMultipartUploads`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) via `.listMultipartUploads`
|
|
140
|
+
- ✅ [`ListParts`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) via `.listParts`
|
|
141
|
+
- ✅ [`UploadPart`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) via `.uploadPart`
|
|
140
142
|
- ✅ [`CompleteMultipartUpload`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) via `.completeMultipartUpload`
|
|
141
143
|
- ✅ [`AbortMultipartUpload`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) via `.abortMultipartUpload`
|
|
142
144
|
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ interface S3FilePresignOptions {
|
|
|
41
41
|
/** Seconds. */
|
|
42
42
|
expiresIn: number;
|
|
43
43
|
method: PresignableHttpMethod;
|
|
44
|
+
contentLength: number;
|
|
44
45
|
storageClass: StorageClass;
|
|
45
46
|
acl: Acl;
|
|
46
47
|
}
|
|
@@ -99,7 +100,7 @@ type CreateMultipartUploadOptions = {
|
|
|
99
100
|
bucket?: string;
|
|
100
101
|
signal?: AbortSignal;
|
|
101
102
|
};
|
|
102
|
-
type
|
|
103
|
+
type CreateMultipartUploadResult = {
|
|
103
104
|
bucket: string;
|
|
104
105
|
key: string;
|
|
105
106
|
uploadId: string;
|
|
@@ -128,7 +129,44 @@ type MultipartUploadPart = {
|
|
|
128
129
|
partNumber: number;
|
|
129
130
|
etag: string;
|
|
130
131
|
};
|
|
131
|
-
type
|
|
132
|
+
type UploadPartOptions = {
|
|
133
|
+
bucket?: string;
|
|
134
|
+
signal?: AbortSignal;
|
|
135
|
+
};
|
|
136
|
+
type UploadPartResult = {
|
|
137
|
+
partNumber: number;
|
|
138
|
+
etag: string;
|
|
139
|
+
};
|
|
140
|
+
type ListPartsOptions = {
|
|
141
|
+
maxParts?: number;
|
|
142
|
+
partNumberMarker?: string;
|
|
143
|
+
bucket?: string;
|
|
144
|
+
signal?: AbortSignal;
|
|
145
|
+
};
|
|
146
|
+
type ListPartsResult = {
|
|
147
|
+
bucket: string;
|
|
148
|
+
key: string;
|
|
149
|
+
uploadId: string;
|
|
150
|
+
partNumberMarker?: string;
|
|
151
|
+
nextPartNumberMarker?: string;
|
|
152
|
+
maxParts?: number;
|
|
153
|
+
isTruncated: boolean;
|
|
154
|
+
parts: Array<{
|
|
155
|
+
checksumCRC32?: string;
|
|
156
|
+
checksumCRC32C?: string;
|
|
157
|
+
checksumCRC64NVME?: string;
|
|
158
|
+
checksumSHA1?: string;
|
|
159
|
+
checksumSHA256?: string;
|
|
160
|
+
etag: string;
|
|
161
|
+
lastModified: Date;
|
|
162
|
+
partNumber: number;
|
|
163
|
+
size: number;
|
|
164
|
+
}>;
|
|
165
|
+
storageClass?: StorageClass;
|
|
166
|
+
checksumAlgorithm?: ChecksumAlgorithm;
|
|
167
|
+
checksumType?: ChecksumType;
|
|
168
|
+
};
|
|
169
|
+
type ListObjectsResult = {
|
|
132
170
|
name: string;
|
|
133
171
|
prefix: string | undefined;
|
|
134
172
|
startAfter: string | undefined;
|
|
@@ -224,8 +262,8 @@ declare class S3Client {
|
|
|
224
262
|
* ```
|
|
225
263
|
*/
|
|
226
264
|
presign(path: string, { method, expiresIn, // TODO: Maybe rename this to expiresInSeconds
|
|
227
|
-
storageClass, acl, region: regionOverride, bucket: bucketOverride, endpoint: endpointOverride, }?: Partial<S3FilePresignOptions & OverridableS3ClientOptions>): string;
|
|
228
|
-
createMultipartUpload(key: string, options?: CreateMultipartUploadOptions): Promise<
|
|
265
|
+
storageClass, contentLength, acl, region: regionOverride, bucket: bucketOverride, endpoint: endpointOverride, }?: Partial<S3FilePresignOptions & OverridableS3ClientOptions>): string;
|
|
266
|
+
createMultipartUpload(key: string, options?: CreateMultipartUploadOptions): Promise<CreateMultipartUploadResult>;
|
|
229
267
|
/**
|
|
230
268
|
* @remarks Uses [`ListMultipartUploads`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html).
|
|
231
269
|
* @throws {RangeError} If `options.maxKeys` is not between `1` and `1000`.
|
|
@@ -242,18 +280,22 @@ declare class S3Client {
|
|
|
242
280
|
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
243
281
|
* @throws {Error} If `uploadId` is not provided.
|
|
244
282
|
*/
|
|
245
|
-
completeMultipartUpload(key: string, uploadId: string, parts: readonly MultipartUploadPart[], options?: CompleteMultipartUploadOptions): Promise<
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
283
|
+
completeMultipartUpload(key: string, uploadId: string, parts: readonly MultipartUploadPart[], options?: CompleteMultipartUploadOptions): Promise<CompleteMultipartUploadResult>;
|
|
284
|
+
/**
|
|
285
|
+
* @remarks Uses [`UploadPart`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html).
|
|
286
|
+
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
287
|
+
* @throws {Error} If `uploadId` is not provided.
|
|
288
|
+
*/
|
|
289
|
+
uploadPart(key: string, uploadId: string, data: UndiciBodyInit, partNumber: number, options?: UploadPartOptions): Promise<UploadPartResult>;
|
|
290
|
+
/**
|
|
291
|
+
* @remarks Uses [`ListParts`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html).
|
|
292
|
+
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
293
|
+
* @throws {Error} If `uploadId` is not provided.
|
|
294
|
+
* @throws {TypeError} If `options.maxParts` is not a `number`.
|
|
295
|
+
* @throws {RangeError} If `options.maxParts` is <= 0.
|
|
296
|
+
* @throws {TypeError} If `options.partNumberMarker` is not a `string`.
|
|
297
|
+
*/
|
|
298
|
+
listParts(key: string, uploadId: string, options?: ListPartsOptions): Promise<ListPartsResult>;
|
|
257
299
|
/**
|
|
258
300
|
* Creates a new bucket on the S3 server.
|
|
259
301
|
*
|
|
@@ -293,7 +335,7 @@ declare class S3Client {
|
|
|
293
335
|
*
|
|
294
336
|
* @throws {RangeError} If `maxKeys` is not between `1` and `1000`.
|
|
295
337
|
*/
|
|
296
|
-
list(options?: ListObjectsOptions): Promise<
|
|
338
|
+
list(options?: ListObjectsOptions): Promise<ListObjectsResult>;
|
|
297
339
|
/**
|
|
298
340
|
* Uses [`DeleteObjects`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) to delete multiple objects in a single request.
|
|
299
341
|
*/
|
|
@@ -433,4 +475,4 @@ type BucketInfo = {
|
|
|
433
475
|
type?: string;
|
|
434
476
|
};
|
|
435
477
|
|
|
436
|
-
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
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -324,7 +324,7 @@ var stream = Symbol("stream");
|
|
|
324
324
|
var signedRequest = Symbol("signedRequest");
|
|
325
325
|
var xmlParser2 = new XMLParser2({
|
|
326
326
|
ignoreAttributes: true,
|
|
327
|
-
isArray: (_, jPath) => jPath === "ListMultipartUploadsResult.Upload" || jPath === "ListBucketResult.Contents" || jPath === "DeleteResult.Deleted" || jPath === "DeleteResult.Error"
|
|
327
|
+
isArray: (_, jPath) => jPath === "ListMultipartUploadsResult.Upload" || jPath === "ListBucketResult.Contents" || jPath === "ListPartsResult.Part" || jPath === "DeleteResult.Deleted" || jPath === "DeleteResult.Error"
|
|
328
328
|
});
|
|
329
329
|
var xmlBuilder = new XMLBuilder({
|
|
330
330
|
attributeNamePrefix: "$",
|
|
@@ -431,11 +431,17 @@ var S3Client = class {
|
|
|
431
431
|
expiresIn = 3600,
|
|
432
432
|
// TODO: Maybe rename this to expiresInSeconds
|
|
433
433
|
storageClass,
|
|
434
|
+
contentLength,
|
|
434
435
|
acl,
|
|
435
436
|
region: regionOverride,
|
|
436
437
|
bucket: bucketOverride,
|
|
437
438
|
endpoint: endpointOverride
|
|
438
439
|
} = {}) {
|
|
440
|
+
if (typeof contentLength === "number") {
|
|
441
|
+
if (contentLength < 0) {
|
|
442
|
+
throw new RangeError("`contentLength` must be >= 0.");
|
|
443
|
+
}
|
|
444
|
+
}
|
|
439
445
|
const now2 = /* @__PURE__ */ new Date();
|
|
440
446
|
const date = getAmzDate(now2);
|
|
441
447
|
const options = this.#options;
|
|
@@ -447,13 +453,19 @@ var S3Client = class {
|
|
|
447
453
|
`${options.accessKeyId}/${date.date}/${region}/s3/aws4_request`,
|
|
448
454
|
date,
|
|
449
455
|
expiresIn,
|
|
450
|
-
"host",
|
|
451
|
-
|
|
456
|
+
typeof contentLength === "number" ? "content-length;host" : "host",
|
|
457
|
+
unsignedPayload,
|
|
452
458
|
storageClass,
|
|
453
459
|
options.sessionToken,
|
|
454
460
|
acl
|
|
455
461
|
);
|
|
456
|
-
const dataDigest =
|
|
462
|
+
const dataDigest = typeof contentLength === "number" ? createCanonicalDataDigest(
|
|
463
|
+
method,
|
|
464
|
+
res.pathname,
|
|
465
|
+
query,
|
|
466
|
+
{ "content-length": String(contentLength), host: res.host },
|
|
467
|
+
unsignedPayload
|
|
468
|
+
) : createCanonicalDataDigestHostOnly(
|
|
457
469
|
method,
|
|
458
470
|
res.pathname,
|
|
459
471
|
query,
|
|
@@ -512,28 +524,28 @@ var S3Client = class {
|
|
|
512
524
|
let query = "uploads=";
|
|
513
525
|
if (options.delimiter) {
|
|
514
526
|
if (typeof options.delimiter !== "string") {
|
|
515
|
-
throw new TypeError("`delimiter`
|
|
527
|
+
throw new TypeError("`delimiter` must be a `string`.");
|
|
516
528
|
}
|
|
517
529
|
query += `&delimiter=${encodeURIComponent(options.delimiter)}`;
|
|
518
530
|
}
|
|
519
531
|
if (options.keyMarker) {
|
|
520
532
|
if (typeof options.keyMarker !== "string") {
|
|
521
|
-
throw new TypeError("`keyMarker`
|
|
533
|
+
throw new TypeError("`keyMarker` must be a `string`.");
|
|
522
534
|
}
|
|
523
535
|
query += `&key-marker=${encodeURIComponent(options.keyMarker)}`;
|
|
524
536
|
}
|
|
525
537
|
if (typeof options.maxUploads !== "undefined") {
|
|
526
538
|
if (typeof options.maxUploads !== "number") {
|
|
527
|
-
throw new TypeError("`maxUploads`
|
|
539
|
+
throw new TypeError("`maxUploads` must be a `number`.");
|
|
528
540
|
}
|
|
529
541
|
if (options.maxUploads < 1 || options.maxUploads > 1e3) {
|
|
530
|
-
throw new RangeError("`maxUploads`
|
|
542
|
+
throw new RangeError("`maxUploads` has to be between 1 and 1000.");
|
|
531
543
|
}
|
|
532
544
|
query += `&max-uploads=${options.maxUploads}`;
|
|
533
545
|
}
|
|
534
546
|
if (options.prefix) {
|
|
535
547
|
if (typeof options.prefix !== "string") {
|
|
536
|
-
throw new TypeError("`prefix`
|
|
548
|
+
throw new TypeError("`prefix` must be a `string`.");
|
|
537
549
|
}
|
|
538
550
|
query += `&prefix=${encodeURIComponent(options.prefix)}`;
|
|
539
551
|
}
|
|
@@ -655,6 +667,112 @@ var S3Client = class {
|
|
|
655
667
|
checksumType: res.ChecksumType || void 0
|
|
656
668
|
};
|
|
657
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* @remarks Uses [`UploadPart`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html).
|
|
672
|
+
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
673
|
+
* @throws {Error} If `uploadId` is not provided.
|
|
674
|
+
*/
|
|
675
|
+
async uploadPart(key, uploadId, data, partNumber, options = {}) {
|
|
676
|
+
if (key.length < 1) {
|
|
677
|
+
throw new RangeError("`key` must be at least 1 character long.");
|
|
678
|
+
}
|
|
679
|
+
if (!uploadId) {
|
|
680
|
+
throw new Error("`uploadId` is required.");
|
|
681
|
+
}
|
|
682
|
+
if (!data) {
|
|
683
|
+
throw new Error("`data` is required.");
|
|
684
|
+
}
|
|
685
|
+
if (typeof partNumber !== "number" || partNumber <= 0) {
|
|
686
|
+
throw new Error("`partNumber` has to be a `number` which is >= 1.");
|
|
687
|
+
}
|
|
688
|
+
const response = await this[signedRequest](
|
|
689
|
+
"PUT",
|
|
690
|
+
key,
|
|
691
|
+
`partNumber=${partNumber}&uploadId=${encodeURIComponent(uploadId)}`,
|
|
692
|
+
data,
|
|
693
|
+
void 0,
|
|
694
|
+
void 0,
|
|
695
|
+
void 0,
|
|
696
|
+
ensureValidBucketName(options.bucket ?? this.#options.bucket),
|
|
697
|
+
options.signal
|
|
698
|
+
);
|
|
699
|
+
if (response.statusCode === 200) {
|
|
700
|
+
await response.body.dump();
|
|
701
|
+
const etag = response.headers.etag;
|
|
702
|
+
if (typeof etag !== "string" || etag.length === 0) {
|
|
703
|
+
throw new S3Error("Unknown", "", {
|
|
704
|
+
message: "Response did not contain an etag."
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
partNumber,
|
|
709
|
+
etag
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
throw await getResponseError(response, "");
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* @remarks Uses [`ListParts`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html).
|
|
716
|
+
* @throws {RangeError} If `key` is not at least 1 character long.
|
|
717
|
+
* @throws {Error} If `uploadId` is not provided.
|
|
718
|
+
* @throws {TypeError} If `options.maxParts` is not a `number`.
|
|
719
|
+
* @throws {RangeError} If `options.maxParts` is <= 0.
|
|
720
|
+
* @throws {TypeError} If `options.partNumberMarker` is not a `string`.
|
|
721
|
+
*/
|
|
722
|
+
async listParts(key, uploadId, options = {}) {
|
|
723
|
+
let query = "";
|
|
724
|
+
if (options.maxParts) {
|
|
725
|
+
if (typeof options.maxParts !== "number") {
|
|
726
|
+
throw new TypeError("`maxParts` must be a `number`.");
|
|
727
|
+
}
|
|
728
|
+
if (options.maxParts <= 0) {
|
|
729
|
+
throw new RangeError("`maxParts` must be >= 1.");
|
|
730
|
+
}
|
|
731
|
+
query += `&max-parts=${options.maxParts}`;
|
|
732
|
+
}
|
|
733
|
+
if (options.partNumberMarker) {
|
|
734
|
+
if (typeof options.partNumberMarker !== "string") {
|
|
735
|
+
throw new TypeError("`partNumberMarker` must be a `string`.");
|
|
736
|
+
}
|
|
737
|
+
query += `&part-number-marker=${encodeURIComponent(options.partNumberMarker)}`;
|
|
738
|
+
}
|
|
739
|
+
query += `&uploadId=${encodeURIComponent(uploadId)}`;
|
|
740
|
+
const response = await this[signedRequest](
|
|
741
|
+
"GET",
|
|
742
|
+
key,
|
|
743
|
+
// 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
|
+
query.substring(1),
|
|
745
|
+
void 0,
|
|
746
|
+
void 0,
|
|
747
|
+
void 0,
|
|
748
|
+
void 0,
|
|
749
|
+
ensureValidBucketName(options.bucket ?? this.#options.bucket),
|
|
750
|
+
options?.signal
|
|
751
|
+
);
|
|
752
|
+
if (response.statusCode === 200) {
|
|
753
|
+
const text = await response.body.text();
|
|
754
|
+
const root = ensureParsedXml(text).ListPartsResult ?? {};
|
|
755
|
+
return {
|
|
756
|
+
bucket: root.Bucket,
|
|
757
|
+
key: root.Key,
|
|
758
|
+
uploadId: root.UploadId,
|
|
759
|
+
partNumberMarker: root.PartNumberMarker ?? void 0,
|
|
760
|
+
nextPartNumberMarker: root.NextPartNumberMarker ?? void 0,
|
|
761
|
+
maxParts: root.MaxParts ?? 1e3,
|
|
762
|
+
isTruncated: root.IsTruncated ?? false,
|
|
763
|
+
parts: (
|
|
764
|
+
// biome-ignore lint/suspicious/noExplicitAny: parsing code
|
|
765
|
+
root.Part?.map((part) => ({
|
|
766
|
+
etag: part.ETag,
|
|
767
|
+
lastModified: part.LastModified ? new Date(part.LastModified) : void 0,
|
|
768
|
+
partNumber: part.PartNumber ?? void 0,
|
|
769
|
+
size: part.Size ?? void 0
|
|
770
|
+
})) ?? []
|
|
771
|
+
)
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
throw await getResponseError(response, key);
|
|
775
|
+
}
|
|
658
776
|
//#endregion
|
|
659
777
|
//#region bucket operations
|
|
660
778
|
/**
|
|
@@ -800,29 +918,29 @@ var S3Client = class {
|
|
|
800
918
|
let query = "";
|
|
801
919
|
if (typeof options.continuationToken !== "undefined") {
|
|
802
920
|
if (typeof options.continuationToken !== "string") {
|
|
803
|
-
throw new TypeError("`continuationToken`
|
|
921
|
+
throw new TypeError("`continuationToken` must be a `string`.");
|
|
804
922
|
}
|
|
805
923
|
query += `continuation-token=${encodeURIComponent(options.continuationToken)}&`;
|
|
806
924
|
}
|
|
807
925
|
query += "list-type=2";
|
|
808
926
|
if (typeof options.maxKeys !== "undefined") {
|
|
809
927
|
if (typeof options.maxKeys !== "number") {
|
|
810
|
-
throw new TypeError("`maxKeys`
|
|
928
|
+
throw new TypeError("`maxKeys` must be a `number`.");
|
|
811
929
|
}
|
|
812
930
|
if (options.maxKeys < 1 || options.maxKeys > 1e3) {
|
|
813
|
-
throw new RangeError("`maxKeys`
|
|
931
|
+
throw new RangeError("`maxKeys` has to be between 1 and 1000.");
|
|
814
932
|
}
|
|
815
933
|
query += `&max-keys=${options.maxKeys}`;
|
|
816
934
|
}
|
|
817
935
|
if (options.prefix) {
|
|
818
936
|
if (typeof options.prefix !== "string") {
|
|
819
|
-
throw new TypeError("`prefix`
|
|
937
|
+
throw new TypeError("`prefix` must be a `string`.");
|
|
820
938
|
}
|
|
821
939
|
query += `&prefix=${encodeURIComponent(options.prefix)}`;
|
|
822
940
|
}
|
|
823
941
|
if (typeof options.startAfter !== "undefined") {
|
|
824
942
|
if (typeof options.startAfter !== "string") {
|
|
825
|
-
throw new TypeError("`startAfter`
|
|
943
|
+
throw new TypeError("`startAfter` must be a `string`.");
|
|
826
944
|
}
|
|
827
945
|
query += `&start-after=${encodeURIComponent(options.startAfter)}`;
|
|
828
946
|
}
|
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.
|
|
5
|
+
"version": "0.6.0",
|
|
6
6
|
"description": "A server-side S3 API for the regular user.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"s3",
|
|
@@ -44,15 +44,16 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"fast-xml-parser": "^5.2.5",
|
|
47
|
+
"i": "^0.3.7",
|
|
47
48
|
"undici": "^7.10.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"@biomejs/biome": "
|
|
51
|
+
"@biomejs/biome": "2.0.0",
|
|
51
52
|
"@testcontainers/localstack": "^11.0.3",
|
|
52
53
|
"@testcontainers/minio": "^11.0.3",
|
|
53
54
|
"@types/node": "^24.0.3",
|
|
54
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
55
|
-
"expect": "^30.0.
|
|
55
|
+
"@typescript/native-preview": "^7.0.0-dev.20250620.1",
|
|
56
|
+
"expect": "^30.0.2",
|
|
56
57
|
"lefthook": "^1.11.14",
|
|
57
58
|
"tsup": "^8.5.0",
|
|
58
59
|
"tsx": "^4.20.3",
|