lean-s3 0.9.11 → 0.9.13

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.mts CHANGED
@@ -18,7 +18,7 @@ declare class S3BucketEntry {
18
18
  /**
19
19
  * @internal
20
20
  */
21
- static parse(source: any): S3BucketEntry;
21
+ static parse(this: void, source: any): S3BucketEntry;
22
22
  }
23
23
  //#endregion
24
24
  //#region src/branded.d.ts
package/dist/index.mjs CHANGED
@@ -3,7 +3,6 @@ import { Readable } from "node:stream";
3
3
  import { Agent, request } from "undici";
4
4
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
5
5
  import { createHash, createHmac } from "node:crypto";
6
-
7
6
  //#region src/S3Stat.ts
8
7
  var S3Stat = class S3Stat {
9
8
  etag;
@@ -30,7 +29,6 @@ var S3Stat = class S3Stat {
30
29
  return new S3Stat(etag, new Date(lm), size, ct);
31
30
  }
32
31
  };
33
-
34
32
  //#endregion
35
33
  //#region src/S3Error.ts
36
34
  var S3Error = class extends Error {
@@ -48,7 +46,6 @@ var S3Error = class extends Error {
48
46
  this.status = status;
49
47
  }
50
48
  };
51
-
52
49
  //#endregion
53
50
  //#region src/S3BucketEntry.ts
54
51
  /**
@@ -78,7 +75,6 @@ var S3BucketEntry = class S3BucketEntry {
78
75
  return new S3BucketEntry(source.Key, source.Size, new Date(source.LastModified), source.ETag, source.StorageClass, source.ChecksumAlgorithm, source.ChecksumType);
79
76
  }
80
77
  };
81
-
82
78
  //#endregion
83
79
  //#region src/sign.ts
84
80
  function deriveSigningKey(date, region, secretAccessKey) {
@@ -116,7 +112,6 @@ function sha256(data) {
116
112
  function md5Base64(data) {
117
113
  return createHash("md5").update(data).digest("base64");
118
114
  }
119
-
120
115
  //#endregion
121
116
  //#region src/KeyCache.ts
122
117
  var KeyCache = class {
@@ -135,7 +130,6 @@ var KeyCache = class {
135
130
  return newKey;
136
131
  }
137
132
  };
138
-
139
133
  //#endregion
140
134
  //#region src/AmzDate.ts
141
135
  const ONE_DAY = 1e3 * 60 * 60 * 24;
@@ -157,7 +151,6 @@ function pad4(v) {
157
151
  function pad2(v) {
158
152
  return v < 10 ? `0${v}` : v.toString();
159
153
  }
160
-
161
154
  //#endregion
162
155
  //#region src/url.ts
163
156
  function buildRequestUrl(endpoint, bucket, region, path) {
@@ -196,7 +189,6 @@ function prepareHeadersForSigning(unfilteredHeadersUnsorted) {
196
189
  function getRangeHeader(start, endExclusive) {
197
190
  return typeof start === "number" || typeof endExclusive === "number" ? `bytes=${start ?? 0}-${typeof endExclusive === "number" ? endExclusive - 1 : ""}` : void 0;
198
191
  }
199
-
200
192
  //#endregion
201
193
  //#region src/error.ts
202
194
  const xmlParser$1 = new XMLParser();
@@ -236,17 +228,14 @@ function parseAndGetXmlError(body, path) {
236
228
  }
237
229
  return new S3Error(error.Code || "Unknown", path, { message: error.Message || void 0 });
238
230
  }
239
-
240
231
  //#endregion
241
232
  //#region src/request.ts
242
233
  function getAuthorizationHeader(keyCache, method, path, query, date, sortedSignedHeaders, region, contentHashStr, accessKeyId, secretAccessKey) {
243
234
  const dataDigest = createCanonicalDataDigest(method, path, query, sortedSignedHeaders, contentHashStr);
244
- const signingKey = keyCache.computeIfAbsent(date, region, accessKeyId, secretAccessKey);
245
- const signature = signCanonicalDataHash(signingKey, dataDigest, date, region);
235
+ const signature = signCanonicalDataHash(keyCache.computeIfAbsent(date, region, accessKeyId, secretAccessKey), dataDigest, date, region);
246
236
  const signedHeadersSpec = Object.keys(sortedSignedHeaders).join(";");
247
237
  return `AWS4-HMAC-SHA256 Credential=${`${accessKeyId}/${date.date}/${region}/s3/aws4_request`}, SignedHeaders=${signedHeadersSpec}, Signature=${signature}`;
248
238
  }
249
-
250
239
  //#endregion
251
240
  //#region src/branded.ts
252
241
  function ensureValidBucketName(bucket) {
@@ -282,13 +271,11 @@ function ensureValidRegion(region) {
282
271
  if (region.length < 1) throw new RangeError("`region` must be at least 1 character long.");
283
272
  return region;
284
273
  }
285
-
286
274
  //#endregion
287
275
  //#region src/assertNever.ts
288
276
  function assertNever(v) {
289
277
  throw new TypeError(`Expected value not to have type ${typeof v}`);
290
278
  }
291
-
292
279
  //#endregion
293
280
  //#region src/encode.ts
294
281
  /**
@@ -311,7 +298,6 @@ function getContentDispositionHeader(value) {
311
298
  function encodeURIComponentExtended(value) {
312
299
  return encodeURIComponent(value).replaceAll(":", "%3A").replaceAll("+", "%2B").replaceAll("(", "%28").replaceAll(")", "%29").replaceAll(",", "%2C").replaceAll("'", "%27").replaceAll("*", "%2A");
313
300
  }
314
-
315
301
  //#endregion
316
302
  //#region src/S3Client.ts
317
303
  const kWrite = Symbol("kWrite");
@@ -442,8 +428,7 @@ var S3Client = class {
442
428
  const contentDisposition = options.response?.contentDisposition;
443
429
  const responseContentDisposition = contentDisposition ? getContentDispositionHeader(contentDisposition) : void 0;
444
430
  const res = buildRequestUrl(endpoint, bucket, region, ensureValidPath(path));
445
- const now = /* @__PURE__ */ new Date();
446
- const date = getAmzDate(now);
431
+ const date = getAmzDate(/* @__PURE__ */ new Date());
447
432
  const query = buildSearchParams(`${this.#options.accessKeyId}/${date.date}/${region}/s3/aws4_request`, date, options.expiresIn ?? 3600, typeof contentLength === "number" || typeof contentType === "string" ? typeof contentLength === "number" && typeof contentType === "string" ? "content-length;content-type;host" : typeof contentLength === "number" ? "content-length;host" : typeof contentType === "string" ? "content-type;host" : "" : "host", unsignedPayload, options.storageClass, this.#options.sessionToken, options.acl, responseContentDisposition);
448
433
  const dataDigest = typeof contentLength === "number" || typeof contentType === "string" ? createCanonicalDataDigest(method, res.pathname, query, typeof contentLength === "number" && typeof contentType === "string" ? {
449
434
  "content-length": String(contentLength),
@@ -456,8 +441,7 @@ var S3Client = class {
456
441
  "content-type": contentType,
457
442
  host: res.host
458
443
  } : {}, unsignedPayload) : createCanonicalDataDigestHostOnly(method, res.pathname, query, res.host);
459
- const signingKey = this.#keyCache.computeIfAbsent(date, region, this.#options.accessKeyId, this.#options.secretAccessKey);
460
- res.search = `${query}&X-Amz-Signature=${signCanonicalDataHash(signingKey, dataDigest, date, region)}`;
444
+ res.search = `${query}&X-Amz-Signature=${signCanonicalDataHash(this.#keyCache.computeIfAbsent(date, region, this.#options.accessKeyId, this.#options.secretAccessKey), dataDigest, date, region)}`;
461
445
  return res.toString();
462
446
  }
463
447
  presignPost(options) {
@@ -925,11 +909,11 @@ var S3Client = class {
925
909
  async [kSignedRequest](region, endpoint, bucket, method, pathWithoutBucket, query, body, additionalSignedHeaders, additionalUnsignedHeaders, contentHash, signal) {
926
910
  const url = buildRequestUrl(endpoint, bucket, region, pathWithoutBucket);
927
911
  if (query) url.search = query;
928
- const now$2 = now();
929
- const contentHashStr = contentHash?.toString("hex") ?? unsignedPayload;
912
+ const now$1 = now();
913
+ const contentHashStr = contentHash?.toString("hex") ?? "UNSIGNED-PAYLOAD";
930
914
  const headersToBeSigned = prepareHeadersForSigning({
931
915
  host: url.host,
932
- "x-amz-date": now$2.dateTime,
916
+ "x-amz-date": now$1.dateTime,
933
917
  "x-amz-content-sha256": contentHashStr,
934
918
  ...additionalSignedHeaders
935
919
  });
@@ -940,7 +924,7 @@ var S3Client = class {
940
924
  dispatcher: this.#dispatcher,
941
925
  headers: {
942
926
  ...headersToBeSigned,
943
- authorization: getAuthorizationHeader(this.#keyCache, method, url.pathname, query ?? "", now$2, headersToBeSigned, region, contentHashStr, this.#options.accessKeyId, this.#options.secretAccessKey),
927
+ authorization: getAuthorizationHeader(this.#keyCache, method, url.pathname, query ?? "", now$1, headersToBeSigned, region, contentHashStr, this.#options.accessKeyId, this.#options.secretAccessKey),
944
928
  ...additionalUnsignedHeaders,
945
929
  "user-agent": "lean-s3"
946
930
  },
@@ -960,15 +944,15 @@ var S3Client = class {
960
944
  const endpoint = this.#options.endpoint;
961
945
  const region = this.#options.region;
962
946
  const url = buildRequestUrl(endpoint, bucket, region, path);
963
- const now$1 = now();
964
- const contentHashStr = contentHash?.toString("hex") ?? unsignedPayload;
947
+ const now$2 = now();
948
+ const contentHashStr = contentHash?.toString("hex") ?? "UNSIGNED-PAYLOAD";
965
949
  const headersToBeSigned = prepareHeadersForSigning({
966
950
  "content-length": contentLength?.toString() ?? void 0,
967
951
  "content-type": contentType,
968
952
  host: url.host,
969
953
  range: getRangeHeader(rageStart, rangeEndExclusive),
970
954
  "x-amz-content-sha256": contentHashStr,
971
- "x-amz-date": now$1.dateTime
955
+ "x-amz-date": now$2.dateTime
972
956
  });
973
957
  let response;
974
958
  try {
@@ -978,7 +962,7 @@ var S3Client = class {
978
962
  dispatcher: this.#dispatcher,
979
963
  headers: {
980
964
  ...headersToBeSigned,
981
- authorization: getAuthorizationHeader(this.#keyCache, "PUT", url.pathname, url.search, now$1, headersToBeSigned, region, contentHashStr, this.#options.accessKeyId, this.#options.secretAccessKey),
965
+ authorization: getAuthorizationHeader(this.#keyCache, "PUT", url.pathname, url.search, now$2, headersToBeSigned, region, contentHashStr, this.#options.accessKeyId, this.#options.secretAccessKey),
982
966
  "user-agent": "lean-s3"
983
967
  },
984
968
  body: data
@@ -1004,7 +988,7 @@ var S3Client = class {
1004
988
  const now$3 = now();
1005
989
  const url = buildRequestUrl(endpoint, bucket, region, path);
1006
990
  const range = getRangeHeader(rageStart, rangeEndExclusive);
1007
- const contentHashStr = contentHash?.toString("hex") ?? unsignedPayload;
991
+ const contentHashStr = contentHash?.toString("hex") ?? "UNSIGNED-PAYLOAD";
1008
992
  const headersToBeSigned = prepareHeadersForSigning({
1009
993
  "amz-sdk-invocation-id": crypto.randomUUID(),
1010
994
  host: url.host,
@@ -1120,7 +1104,6 @@ function ensureParsedXml(text) {
1120
1104
  });
1121
1105
  }
1122
1106
  }
1123
-
1124
1107
  //#endregion
1125
1108
  //#region src/S3File.ts
1126
1109
  var S3File = class S3File {
@@ -1289,6 +1272,5 @@ var S3File = class S3File {
1289
1272
  return `S3File ${nodeUtil.formatWithOptions(options, properties)}`;
1290
1273
  }
1291
1274
  };
1292
-
1293
1275
  //#endregion
1294
- export { S3BucketEntry, S3Client, S3Error, S3File, S3Stat };
1276
+ export { S3BucketEntry, S3Client, S3Error, S3File, S3Stat };
package/package.json CHANGED
@@ -1,64 +1,67 @@
1
1
  {
2
2
  "name": "lean-s3",
3
- "author": "Niklas Mollenhauer",
4
- "license": "MIT",
5
- "version": "0.9.11",
3
+ "version": "0.9.13",
6
4
  "description": "A server-side S3 API for the regular user.",
7
5
  "keywords": [
8
- "s3",
9
- "client",
10
- "s3 client",
11
- "s3 sdk",
6
+ "AWS S3",
7
+ "Azure Blob Storage",
8
+ "Ceph",
9
+ "Google Cloud Storage",
12
10
  "b2",
13
11
  "b2 client",
14
- "r2",
15
- "r2 client",
12
+ "backblaze",
13
+ "client",
16
14
  "cloudflare",
17
15
  "cloudflare r2",
18
- "AWS S3",
19
- "Azure Blob Storage",
20
- "Google Cloud Storage",
21
- "Ceph",
22
16
  "mibion",
23
- "backblaze"
17
+ "r2",
18
+ "r2 client",
19
+ "s3",
20
+ "s3 client",
21
+ "s3 sdk"
24
22
  ],
23
+ "license": "MIT",
24
+ "author": "Niklas Mollenhauer",
25
25
  "repository": {
26
26
  "type": "git",
27
27
  "url": "https://github.com/nikeee/lean-s3"
28
28
  },
29
- "types": "./dist/index.d.mts",
30
- "main": "./dist/index.mjs",
31
- "type": "module",
32
29
  "files": [
33
30
  "dist"
34
31
  ],
32
+ "type": "module",
33
+ "main": "./dist/index.mjs",
34
+ "types": "./dist/index.d.mts",
35
35
  "scripts": {
36
36
  "build": "tsdown",
37
37
  "test": "tsgo && tsx --test src/*.test.ts src/test/*.test.ts",
38
38
  "test:integration": "tsgo && tsx --test src/test/test.integration.ts",
39
- "ci": "biome ci ./src",
39
+ "ci": "oxlint --type-aware --deny-warnings -f github",
40
40
  "docs": "typedoc",
41
- "lint": "biome lint ./src",
42
- "format": "biome check --write ./src",
41
+ "format": "oxfmt",
42
+ "lint": "oxlint --type-aware",
43
+ "lint:fix": "oxlint --type-aware --fix",
43
44
  "prepublishOnly": "npm run build"
44
45
  },
45
46
  "dependencies": {
46
- "fast-xml-parser": "^5.3.4",
47
- "undici": "^7.20.0"
47
+ "fast-xml-parser": "^5.5.6",
48
+ "undici": "^7.24.4"
48
49
  },
49
50
  "devDependencies": {
50
- "@biomejs/biome": "2.3.14",
51
- "@testcontainers/localstack": "^11.11.0",
52
- "@testcontainers/minio": "^11.11.0",
53
- "@testcontainers/s3mock": "^11.11.0",
54
- "@types/node": "^25.2.0",
55
- "@typescript/native-preview": "^7.0.0-dev.20260203.1",
56
- "expect": "^30.2.0",
57
- "lefthook": "^2.1.0",
58
- "testcontainers": "^11.11.0",
59
- "tsdown": "^0.20.1",
51
+ "@testcontainers/localstack": "^11.13.0",
52
+ "@testcontainers/minio": "^11.13.0",
53
+ "@testcontainers/s3mock": "^11.13.0",
54
+ "@types/node": "^25.5.0",
55
+ "@typescript/native-preview": "^7.0.0-dev.20260317.1",
56
+ "expect": "^30.3.0",
57
+ "lefthook": "^2.1.4",
58
+ "oxfmt": "^0.41.0",
59
+ "oxlint": "^1.56.0",
60
+ "oxlint-tsgolint": "^0.17.0",
61
+ "testcontainers": "^11.13.0",
62
+ "tsdown": "^0.21.4",
60
63
  "tsx": "^4.21.0",
61
- "typedoc": "^0.28.16"
64
+ "typedoc": "^0.28.17"
62
65
  },
63
66
  "engines": {
64
67
  "node": "^20.19.5 || ^22.21.1 || ^24.11.0 || ^25.1.0"