lean-s3 0.8.15 → 0.9.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.mts CHANGED
@@ -98,7 +98,10 @@ type DeleteObjectsError = {
98
98
  };
99
99
  interface S3FilePresignOptions extends OverridableS3ClientOptions {
100
100
  contentHash?: Buffer;
101
- /** Seconds. */
101
+ /**
102
+ * In seconds.
103
+ * @default 3600 (1 hour)
104
+ */
102
105
  expiresIn?: number;
103
106
  method?: PresignableHttpMethod;
104
107
  contentLength?: number;
@@ -128,6 +131,25 @@ interface S3FilePresignOptions extends OverridableS3ClientOptions {
128
131
  contentDisposition?: ContentDisposition;
129
132
  };
130
133
  }
134
+ /**
135
+ * Ref: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html#sigv4-ConditionMatching
136
+ */
137
+ type ConditionMatchType = "starts-with" | "eq" | "content-length-range";
138
+ type PostPolicyCondition = [string, string] | [ConditionMatchType, string | number, string | number];
139
+ interface PresignPostOptions extends OverridableS3ClientOptions {
140
+ key: string;
141
+ /**
142
+ * In seconds.
143
+ * @default 3600 (1 hour)
144
+ */
145
+ expiresIn?: number;
146
+ fields?: Record<string, string>;
147
+ conditions?: PostPolicyCondition[];
148
+ }
149
+ type PresignPostResult = {
150
+ url: string;
151
+ fields: Record<string, string>;
152
+ };
131
153
  type CopyObjectOptions = {
132
154
  /** Set this to override the {@link S3ClientOptions#bucket} that was passed on creation of the {@link S3Client}. */
133
155
  sourceBucket?: string;
@@ -387,7 +409,7 @@ declare class S3Client {
387
409
  /**
388
410
  * Create a new instance of an S3 bucket so that credentials can be managed from a single instance instead of being passed to every method.
389
411
  *
390
- * @param options The default options to use for the S3 client.
412
+ * @param options The default options to use for the S3 client.
391
413
  */
392
414
  constructor(options: S3ClientOptions);
393
415
  /** @internal */
@@ -452,6 +474,7 @@ declare class S3Client {
452
474
  * ```
453
475
  */
454
476
  presign(path: string, options?: S3FilePresignOptions): string;
477
+ presignPost(options: PresignPostOptions): PresignPostResult;
455
478
  /**
456
479
  * Copies an object from a source to a destination.
457
480
  * @remarks Uses [`CopyObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html).
package/dist/index.mjs CHANGED
@@ -84,8 +84,11 @@ var S3BucketEntry = class S3BucketEntry {
84
84
  function deriveSigningKey(date, region, secretAccessKey) {
85
85
  return createHmac("sha256", createHmac("sha256", createHmac("sha256", createHmac("sha256", `AWS4${secretAccessKey}`).update(date).digest()).update(region).digest()).update("s3").digest()).update("aws4_request").digest();
86
86
  }
87
- function signCanonicalDataHash(signinKey, canonicalDataHash, date, region) {
88
- return createHmac("sha256", signinKey).update(`AWS4-HMAC-SHA256\n${date.dateTime}\n${date.date}/${region}/s3/aws4_request\n${canonicalDataHash}`).digest("hex");
87
+ function signEncodedPolicy(signingKey, encodedPolicy) {
88
+ return createHmac("sha256", signingKey).update(encodedPolicy).digest("hex");
89
+ }
90
+ function signCanonicalDataHash(signingKey, canonicalDataHash, date, region) {
91
+ return createHmac("sha256", signingKey).update(`AWS4-HMAC-SHA256\n${date.dateTime}\n${date.date}/${region}/s3/aws4_request\n${canonicalDataHash}`).digest("hex");
89
92
  }
90
93
  const unsignedPayload = "UNSIGNED-PAYLOAD";
91
94
  /**
@@ -346,7 +349,7 @@ var S3Client = class {
346
349
  /**
347
350
  * Create a new instance of an S3 bucket so that credentials can be managed from a single instance instead of being passed to every method.
348
351
  *
349
- * @param options The default options to use for the S3 client.
352
+ * @param options The default options to use for the S3 client.
350
353
  */
351
354
  constructor(options) {
352
355
  if (!options) throw new Error("`options` is required.");
@@ -457,6 +460,76 @@ var S3Client = class {
457
460
  res.search = `${query}&X-Amz-Signature=${signCanonicalDataHash(signingKey, dataDigest, date, region)}`;
458
461
  return res.toString();
459
462
  }
463
+ presignPost(options) {
464
+ const now$1 = /* @__PURE__ */ new Date();
465
+ const date = getAmzDate(now$1);
466
+ const key = options.key;
467
+ const region = ensureValidRegion(options.region ?? this.#options.region);
468
+ const bucket = ensureValidBucketName(options.bucket ?? this.#options.bucket);
469
+ const endpoint = ensureValidEndpoint(options.endpoint ?? this.#options.endpoint);
470
+ const expiresIn = options.expiresIn ?? 3600;
471
+ const credential = `${this.#options.accessKeyId}/${date.date}/${region}/s3/aws4_request`;
472
+ const fields = {
473
+ ...options.fields,
474
+ bucket,
475
+ "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
476
+ "X-Amz-Credential": credential,
477
+ "X-Amz-Date": date.dateTime,
478
+ ...this.#options.sessionToken ? { "X-Amz-Security-Token": this.#options.sessionToken } : void 0
479
+ };
480
+ const policy = {
481
+ expiration: new Date(now$1.getTime() + expiresIn * 1e3).toISOString().replace(/\.\d{3}Z$/, "Z"),
482
+ conditions: [
483
+ [
484
+ "eq",
485
+ "$bucket",
486
+ bucket
487
+ ],
488
+ key.endsWith("{{filename}}") ? [
489
+ "starts-with",
490
+ "$key",
491
+ key.substring(0, key.lastIndexOf("{{filename}}"))
492
+ ] : [
493
+ "eq",
494
+ "$key",
495
+ key
496
+ ],
497
+ ...options.conditions ? options.conditions : [],
498
+ [
499
+ "eq",
500
+ "$x-amz-algorithm",
501
+ "AWS4-HMAC-SHA256"
502
+ ],
503
+ [
504
+ "eq",
505
+ "$x-amz-credential",
506
+ credential
507
+ ],
508
+ [
509
+ "eq",
510
+ "$x-amz-date",
511
+ date.dateTime
512
+ ]
513
+ ]
514
+ };
515
+ if (this.#options.sessionToken) policy.conditions.push([
516
+ "eq",
517
+ "$x-amz-security-token",
518
+ this.#options.sessionToken
519
+ ]);
520
+ const policyJson = JSON.stringify(policy);
521
+ const encodedPolicy = Buffer.from(policyJson).toString("base64");
522
+ const signingKey = this.#keyCache.computeIfAbsent(date, region, this.#options.accessKeyId, this.#options.secretAccessKey);
523
+ return {
524
+ url: buildRequestUrl(endpoint, bucket, region, "").toString(),
525
+ fields: {
526
+ ...fields,
527
+ key,
528
+ Policy: encodedPolicy,
529
+ "X-Amz-Signature": signEncodedPolicy(signingKey, encodedPolicy)
530
+ }
531
+ };
532
+ }
460
533
  /**
461
534
  * Copies an object from a source to a destination.
462
535
  * @remarks Uses [`CopyObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html).
@@ -707,6 +780,7 @@ var S3Client = class {
707
780
  const body = xmlBuilder.build({ CORSConfiguration: { CORSRule: rules.map((r) => ({
708
781
  AllowedOrigin: r.allowedOrigins,
709
782
  AllowedMethod: r.allowedMethods,
783
+ AllowedHeader: r.allowedHeaders,
710
784
  ExposeHeader: r.exposeHeaders,
711
785
  ID: r.id ?? void 0,
712
786
  MaxAgeSeconds: r.maxAgeSeconds ?? void 0
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.8.15",
5
+ "version": "0.9.2",
6
6
  "description": "A server-side S3 API for the regular user.",
7
7
  "keywords": [
8
8
  "s3",
@@ -47,16 +47,16 @@
47
47
  "undici": "^7.16.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@biomejs/biome": "2.3.6",
50
+ "@biomejs/biome": "2.3.7",
51
51
  "@testcontainers/localstack": "^11.8.1",
52
52
  "@testcontainers/minio": "^11.8.1",
53
53
  "@testcontainers/s3mock": "^11.8.1",
54
54
  "@types/node": "^24.10.1",
55
- "@typescript/native-preview": "^7.0.0-dev.20251117.1",
55
+ "@typescript/native-preview": "^7.0.0-dev.20251125.1",
56
56
  "expect": "^30.2.0",
57
57
  "lefthook": "^2.0.4",
58
58
  "testcontainers": "^11.8.1",
59
- "tsdown": "^0.16.5",
59
+ "tsdown": "^0.16.7",
60
60
  "tsx": "^4.20.6",
61
61
  "typedoc": "^0.28.14"
62
62
  },