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 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
- _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
+ [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
- /** @returns {Promise<ArrayBuffer>} */
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._signedRequest(
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._signedRequest(
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._signedRequest(
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._signedRequest(
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._signedRequest(
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 _signedRequest(method, pathWithoutBucket, query, body, additionalSignedHeaders, additionalUnsignedHeaders, contentHash, bucket, signal = void 0) {
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: this.#getAuthorizationHeader(
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: this.#getAuthorizationHeader(
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: this.#getAuthorizationHeader(
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._signedRequest(
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._signedRequest(
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._signedRequest(
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
- // TODO
1163
- // /** @returns {Promise<Uint8Array>} */
1164
- // bytes() {
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.1",
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 dist/*.test.js",
24
- "test:integration": "tsgo && node --test dist/test.integration.js",
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
  }