lean-s3 0.3.1 → 0.3.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 +3 -5
- package/dist/index.js +50 -48
- package/package.json +22 -8
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare class S3BucketEntry {
|
|
|
22
22
|
|
|
23
23
|
declare const write: unique symbol;
|
|
24
24
|
declare const stream: unique symbol;
|
|
25
|
+
declare const signedRequest: unique symbol;
|
|
25
26
|
interface S3ClientOptions {
|
|
26
27
|
bucket: string;
|
|
27
28
|
region: string;
|
|
@@ -209,7 +210,7 @@ declare class S3Client {
|
|
|
209
210
|
* TODO: Maybe move this into a separate free function?
|
|
210
211
|
* @internal
|
|
211
212
|
*/
|
|
212
|
-
|
|
213
|
+
[signedRequest](method: HttpMethod, pathWithoutBucket: string, query: string | undefined, body: UndiciBodyInit | undefined, additionalSignedHeaders: Record<string, string> | undefined, additionalUnsignedHeaders: Record<string, string> | undefined, contentHash: Buffer | undefined, bucket: string | undefined, signal?: AbortSignal | undefined): Promise<Dispatcher.ResponseData<null>>;
|
|
213
214
|
/**
|
|
214
215
|
* @internal
|
|
215
216
|
* @param {import("./index.d.ts").UndiciBodyInit} data TODO
|
|
@@ -275,13 +276,10 @@ declare class S3File {
|
|
|
275
276
|
*/
|
|
276
277
|
delete({ signal }?: Partial<S3FileDeleteOptions>): Promise<void>;
|
|
277
278
|
toString(): string;
|
|
278
|
-
/** @returns {Promise<unknown>} */
|
|
279
279
|
json(): Promise<unknown>;
|
|
280
|
-
|
|
280
|
+
bytes(): Promise<Uint8Array>;
|
|
281
281
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
282
|
-
/** @returns {Promise<string>} */
|
|
283
282
|
text(): Promise<string>;
|
|
284
|
-
/** @returns {Promise<Blob>} */
|
|
285
283
|
blob(): Promise<Blob>;
|
|
286
284
|
/** @returns {ReadableStream<Uint8Array>} */
|
|
287
285
|
stream(): ReadableStream<Uint8Array>;
|
package/dist/index.js
CHANGED
|
@@ -292,9 +292,36 @@ function parseAndGetXmlError(body, path) {
|
|
|
292
292
|
});
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
// src/request.ts
|
|
296
|
+
function getAuthorizationHeader(keyCache, method, path, query, date, sortedSignedHeaders, region, contentHashStr, accessKeyId, secretAccessKey) {
|
|
297
|
+
const dataDigest = createCanonicalDataDigest(
|
|
298
|
+
method,
|
|
299
|
+
path,
|
|
300
|
+
query,
|
|
301
|
+
sortedSignedHeaders,
|
|
302
|
+
contentHashStr
|
|
303
|
+
);
|
|
304
|
+
const signingKey = keyCache.computeIfAbsent(
|
|
305
|
+
date,
|
|
306
|
+
region,
|
|
307
|
+
accessKeyId,
|
|
308
|
+
secretAccessKey
|
|
309
|
+
);
|
|
310
|
+
const signature = signCanonicalDataHash(
|
|
311
|
+
signingKey,
|
|
312
|
+
dataDigest,
|
|
313
|
+
date,
|
|
314
|
+
region
|
|
315
|
+
);
|
|
316
|
+
const signedHeadersSpec = Object.keys(sortedSignedHeaders).join(";");
|
|
317
|
+
const credentialSpec = `${accessKeyId}/${date.date}/${region}/s3/aws4_request`;
|
|
318
|
+
return `AWS4-HMAC-SHA256 Credential=${credentialSpec}, SignedHeaders=${signedHeadersSpec}, Signature=${signature}`;
|
|
319
|
+
}
|
|
320
|
+
|
|
295
321
|
// src/S3Client.ts
|
|
296
322
|
var write = Symbol("write");
|
|
297
323
|
var stream = Symbol("stream");
|
|
324
|
+
var signedRequest = Symbol("signedRequest");
|
|
298
325
|
var xmlParser2 = new XMLParser2();
|
|
299
326
|
var xmlBuilder = new XMLBuilder({
|
|
300
327
|
attributeNamePrefix: "$",
|
|
@@ -456,7 +483,7 @@ var S3Client = class {
|
|
|
456
483
|
}))
|
|
457
484
|
}
|
|
458
485
|
});
|
|
459
|
-
const response = await this
|
|
486
|
+
const response = await this[signedRequest](
|
|
460
487
|
"POST",
|
|
461
488
|
"",
|
|
462
489
|
"delete=",
|
|
@@ -538,7 +565,7 @@ var S3Client = class {
|
|
|
538
565
|
}) : void 0;
|
|
539
566
|
}
|
|
540
567
|
const additionalSignedHeaders = body ? { "content-md5": md5Base64(body) } : void 0;
|
|
541
|
-
const response = await this
|
|
568
|
+
const response = await this[signedRequest](
|
|
542
569
|
"PUT",
|
|
543
570
|
"",
|
|
544
571
|
void 0,
|
|
@@ -567,7 +594,7 @@ var S3Client = class {
|
|
|
567
594
|
*/
|
|
568
595
|
async deleteBucket(name, options) {
|
|
569
596
|
ensureValidBucketName(name);
|
|
570
|
-
const response = await this
|
|
597
|
+
const response = await this[signedRequest](
|
|
571
598
|
"DELETE",
|
|
572
599
|
"",
|
|
573
600
|
void 0,
|
|
@@ -595,7 +622,7 @@ var S3Client = class {
|
|
|
595
622
|
*/
|
|
596
623
|
async bucketExists(name, options) {
|
|
597
624
|
ensureValidBucketName(name);
|
|
598
|
-
const response = await this
|
|
625
|
+
const response = await this[signedRequest](
|
|
599
626
|
"HEAD",
|
|
600
627
|
"",
|
|
601
628
|
void 0,
|
|
@@ -669,7 +696,7 @@ var S3Client = class {
|
|
|
669
696
|
}
|
|
670
697
|
query += `&start-after=${encodeURIComponent(options.startAfter)}`;
|
|
671
698
|
}
|
|
672
|
-
const response = await this
|
|
699
|
+
const response = await this[signedRequest](
|
|
673
700
|
"GET",
|
|
674
701
|
"",
|
|
675
702
|
query,
|
|
@@ -720,7 +747,7 @@ var S3Client = class {
|
|
|
720
747
|
* TODO: Maybe move this into a separate free function?
|
|
721
748
|
* @internal
|
|
722
749
|
*/
|
|
723
|
-
async
|
|
750
|
+
async [signedRequest](method, pathWithoutBucket, query, body, additionalSignedHeaders, additionalUnsignedHeaders, contentHash, bucket, signal = void 0) {
|
|
724
751
|
const endpoint = this.#options.endpoint;
|
|
725
752
|
const region = this.#options.region;
|
|
726
753
|
const effectiveBucket = bucket ?? this.#options.bucket;
|
|
@@ -748,7 +775,8 @@ var S3Client = class {
|
|
|
748
775
|
dispatcher: this.#dispatcher,
|
|
749
776
|
headers: {
|
|
750
777
|
...headersToBeSigned,
|
|
751
|
-
authorization:
|
|
778
|
+
authorization: getAuthorizationHeader(
|
|
779
|
+
this.#keyCache,
|
|
752
780
|
method,
|
|
753
781
|
url.pathname,
|
|
754
782
|
query ?? "",
|
|
@@ -799,7 +827,8 @@ var S3Client = class {
|
|
|
799
827
|
dispatcher: this.#dispatcher,
|
|
800
828
|
headers: {
|
|
801
829
|
...headersToBeSigned,
|
|
802
|
-
authorization:
|
|
830
|
+
authorization: getAuthorizationHeader(
|
|
831
|
+
this.#keyCache,
|
|
803
832
|
"PUT",
|
|
804
833
|
url.pathname,
|
|
805
834
|
url.search,
|
|
@@ -866,7 +895,8 @@ var S3Client = class {
|
|
|
866
895
|
dispatcher: this.#dispatcher,
|
|
867
896
|
headers: {
|
|
868
897
|
...headersToBeSigned,
|
|
869
|
-
authorization:
|
|
898
|
+
authorization: getAuthorizationHeader(
|
|
899
|
+
this.#keyCache,
|
|
870
900
|
"GET",
|
|
871
901
|
url.pathname,
|
|
872
902
|
url.search,
|
|
@@ -953,30 +983,6 @@ var S3Client = class {
|
|
|
953
983
|
}
|
|
954
984
|
});
|
|
955
985
|
}
|
|
956
|
-
#getAuthorizationHeader(method, path, query, date, sortedSignedHeaders, region, contentHashStr, accessKeyId, secretAccessKey) {
|
|
957
|
-
const dataDigest = createCanonicalDataDigest(
|
|
958
|
-
method,
|
|
959
|
-
path,
|
|
960
|
-
query,
|
|
961
|
-
sortedSignedHeaders,
|
|
962
|
-
contentHashStr
|
|
963
|
-
);
|
|
964
|
-
const signingKey = this.#keyCache.computeIfAbsent(
|
|
965
|
-
date,
|
|
966
|
-
region,
|
|
967
|
-
accessKeyId,
|
|
968
|
-
secretAccessKey
|
|
969
|
-
);
|
|
970
|
-
const signature = signCanonicalDataHash(
|
|
971
|
-
signingKey,
|
|
972
|
-
dataDigest,
|
|
973
|
-
date,
|
|
974
|
-
region
|
|
975
|
-
);
|
|
976
|
-
const signedHeadersSpec = Object.keys(sortedSignedHeaders).join(";");
|
|
977
|
-
const credentialSpec = `${accessKeyId}/${date.date}/${region}/s3/aws4_request`;
|
|
978
|
-
return `AWS4-HMAC-SHA256 Credential=${credentialSpec}, SignedHeaders=${signedHeadersSpec}, Signature=${signature}`;
|
|
979
|
-
}
|
|
980
986
|
};
|
|
981
987
|
function buildSearchParams(amzCredential, date, expiresIn, headerList, contentHashStr, storageClass, sessionToken, acl) {
|
|
982
988
|
let res = "";
|
|
@@ -1016,6 +1022,11 @@ function ensureValidBucketName(name) {
|
|
|
1016
1022
|
}
|
|
1017
1023
|
}
|
|
1018
1024
|
|
|
1025
|
+
// src/assertNever.ts
|
|
1026
|
+
function assertNever(v) {
|
|
1027
|
+
throw new TypeError(`Expected value not to have type ${typeof v}`);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1019
1030
|
// src/S3File.ts
|
|
1020
1031
|
var S3File = class _S3File {
|
|
1021
1032
|
#client;
|
|
@@ -1057,7 +1068,7 @@ var S3File = class _S3File {
|
|
|
1057
1068
|
* @throws {Error} If the server returns an invalid response.
|
|
1058
1069
|
*/
|
|
1059
1070
|
async stat({ signal } = {}) {
|
|
1060
|
-
const response = await this.#client
|
|
1071
|
+
const response = await this.#client[signedRequest](
|
|
1061
1072
|
"HEAD",
|
|
1062
1073
|
this.#path,
|
|
1063
1074
|
void 0,
|
|
@@ -1090,7 +1101,7 @@ var S3File = class _S3File {
|
|
|
1090
1101
|
async exists({
|
|
1091
1102
|
signal
|
|
1092
1103
|
} = {}) {
|
|
1093
|
-
const response = await this.#client
|
|
1104
|
+
const response = await this.#client[signedRequest](
|
|
1094
1105
|
"HEAD",
|
|
1095
1106
|
this.#path,
|
|
1096
1107
|
void 0,
|
|
@@ -1135,7 +1146,7 @@ var S3File = class _S3File {
|
|
|
1135
1146
|
* ```
|
|
1136
1147
|
*/
|
|
1137
1148
|
async delete({ signal } = {}) {
|
|
1138
|
-
const response = await this.#client
|
|
1149
|
+
const response = await this.#client[signedRequest](
|
|
1139
1150
|
"DELETE",
|
|
1140
1151
|
this.#path,
|
|
1141
1152
|
void 0,
|
|
@@ -1155,24 +1166,18 @@ var S3File = class _S3File {
|
|
|
1155
1166
|
toString() {
|
|
1156
1167
|
return `S3File { path: "${this.#path}" }`;
|
|
1157
1168
|
}
|
|
1158
|
-
/** @returns {Promise<unknown>} */
|
|
1159
1169
|
json() {
|
|
1160
1170
|
return new Response(this.stream()).json();
|
|
1161
1171
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
// return new Response(this.stream()).bytes(); // TODO: Does this exist?
|
|
1166
|
-
// }
|
|
1167
|
-
/** @returns {Promise<ArrayBuffer>} */
|
|
1172
|
+
bytes() {
|
|
1173
|
+
return new Response(this.stream()).arrayBuffer().then((ab) => new Uint8Array(ab));
|
|
1174
|
+
}
|
|
1168
1175
|
arrayBuffer() {
|
|
1169
1176
|
return new Response(this.stream()).arrayBuffer();
|
|
1170
1177
|
}
|
|
1171
|
-
/** @returns {Promise<string>} */
|
|
1172
1178
|
text() {
|
|
1173
1179
|
return new Response(this.stream()).text();
|
|
1174
1180
|
}
|
|
1175
|
-
/** @returns {Promise<Blob>} */
|
|
1176
1181
|
blob() {
|
|
1177
1182
|
return new Response(this.stream()).blob();
|
|
1178
1183
|
}
|
|
@@ -1245,9 +1250,6 @@ var S3File = class _S3File {
|
|
|
1245
1250
|
}
|
|
1246
1251
|
*/
|
|
1247
1252
|
};
|
|
1248
|
-
function assertNever(v) {
|
|
1249
|
-
throw new TypeError(`Expected value not to have type ${typeof v}`);
|
|
1250
|
-
}
|
|
1251
1253
|
export {
|
|
1252
1254
|
S3BucketEntry,
|
|
1253
1255
|
S3Client,
|
package/package.json
CHANGED
|
@@ -2,11 +2,25 @@
|
|
|
2
2
|
"name": "lean-s3",
|
|
3
3
|
"author": "Niklas Mollenhauer",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.2",
|
|
6
6
|
"description": "A server-side S3 API for the regular user.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"s3",
|
|
9
|
-
"client"
|
|
9
|
+
"client",
|
|
10
|
+
"s3 client",
|
|
11
|
+
"s3 sdk",
|
|
12
|
+
"b2",
|
|
13
|
+
"b2 client",
|
|
14
|
+
"r2",
|
|
15
|
+
"r2 client",
|
|
16
|
+
"cloudflare",
|
|
17
|
+
"cloudflare r2",
|
|
18
|
+
"AWS S3",
|
|
19
|
+
"Azure Blob Storage",
|
|
20
|
+
"Google Cloud Storage",
|
|
21
|
+
"Ceph",
|
|
22
|
+
"mibion",
|
|
23
|
+
"backblaze"
|
|
10
24
|
],
|
|
11
25
|
"repository": {
|
|
12
26
|
"type": "git",
|
|
@@ -20,14 +34,18 @@
|
|
|
20
34
|
"type": "module",
|
|
21
35
|
"scripts": {
|
|
22
36
|
"build": "tsup",
|
|
23
|
-
"test": "tsgo && node --test
|
|
24
|
-
"test:integration": "tsgo && node --test
|
|
37
|
+
"test": "tsgo && node --test src/*.test.ts",
|
|
38
|
+
"test:integration": "tsgo && node --test src/test.integration.ts",
|
|
25
39
|
"ci": "biome ci ./src",
|
|
26
40
|
"docs": "typedoc",
|
|
27
41
|
"lint": "biome lint ./src",
|
|
28
42
|
"format": "biome format --write ./src && biome lint --write ./src && biome check --write ./src",
|
|
29
43
|
"prepublishOnly": "npm run build"
|
|
30
44
|
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"fast-xml-parser": "^5.2.5",
|
|
47
|
+
"undici": "^7.10.0"
|
|
48
|
+
},
|
|
31
49
|
"devDependencies": {
|
|
32
50
|
"@biomejs/biome": "^1.9.4",
|
|
33
51
|
"@testcontainers/localstack": "^11.0.3",
|
|
@@ -41,9 +59,5 @@
|
|
|
41
59
|
},
|
|
42
60
|
"engines": {
|
|
43
61
|
"node": "^20.19.0 || ^22.14.0 || ^24.0.0"
|
|
44
|
-
},
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"fast-xml-parser": "^5.2.5",
|
|
47
|
-
"undici": "^7.10.0"
|
|
48
62
|
}
|
|
49
63
|
}
|