@nebutra/uploads 0.1.2 → 2.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAIlF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,CAM5E;AAkFD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAOxF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,kBAAkB,CAyB1D"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAIlF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,CAM5E;AA6FD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAOxF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,kBAAkB,CAyB1D"}
package/dist/factory.js CHANGED
@@ -40,8 +40,19 @@ function createUploadProviderByType(type, config) {
40
40
  * Auto-detect provider based on environment
41
41
  */
42
42
  function autoDetectProvider(config) {
43
- // Priority 1: Check explicit UPLOAD_PROVIDER
44
- const explicitProvider = process.env.UPLOAD_PROVIDER;
43
+ // Priority 1: Check explicit UPLOAD_PROVIDER (r2 is an alias for S3-compatible)
44
+ const explicitRaw = process.env.UPLOAD_PROVIDER?.trim().toLowerCase();
45
+ if (explicitRaw === "r2") {
46
+ logger.info("Using explicit upload provider", { provider: "r2→s3" });
47
+ return new S3UploadProvider({
48
+ accessKeyId: process.env.R2_ACCESS_KEY_ID || "",
49
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY || "",
50
+ region: "auto",
51
+ ...(process.env.R2_ENDPOINT !== undefined ? { endpoint: process.env.R2_ENDPOINT } : {}),
52
+ ...(process.env.R2_PUBLIC_URL !== undefined ? { publicUrl: process.env.R2_PUBLIC_URL } : {}),
53
+ });
54
+ }
55
+ const explicitProvider = explicitRaw;
45
56
  if (explicitProvider) {
46
57
  logger.info("Using explicit upload provider", { provider: explicitProvider });
47
58
  return createUploadProviderByType(explicitProvider, config);
@@ -11,6 +11,8 @@ export declare class LocalUploadProvider implements UploadProvider {
11
11
  * Build object key with tenant prefix and UUID
12
12
  */
13
13
  private buildKey;
14
+ /** Prefer an explicit full object key (contains `/`) over generating a new one. */
15
+ private resolveKey;
14
16
  /**
15
17
  * Generate presigned URL for simple uploads
16
18
  */
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/providers/local.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,qBAAa,mBAAoB,YAAW,cAAc;IACxD,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,cAAc,CAA2D;gBAErE,MAAM,EAAE,mBAAmB;IAcvC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAQhB;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB3E;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAmC9F;;OAEG;IACG,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EAAE,GACrB,OAAO,CAAC,cAAc,CAAC;IAsC1B;;OAEG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzE;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAcpE;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7D;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI3F;;OAEG;YACW,SAAS;IAUvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,mBAAmB,CAUzD"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/providers/local.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,qBAAa,mBAAoB,YAAW,cAAc;IACxD,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,cAAc,CAA2D;gBAErE,MAAM,EAAE,mBAAmB;IAcvC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAQhB,mFAAmF;IACnF,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB3E;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAmC9F;;OAEG;IACG,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EAAE,GACrB,OAAO,CAAC,cAAc,CAAC;IAsC1B;;OAEG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzE;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAcpE;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7D;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI3F;;OAEG;YACW,SAAS;IAUvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,mBAAmB,CAUzD"}
@@ -31,11 +31,17 @@ export class LocalUploadProvider {
31
31
  }
32
32
  return `${id}/${target.key.split("/").pop()}`;
33
33
  }
34
+ /** Prefer an explicit full object key (contains `/`) over generating a new one. */
35
+ resolveKey(target) {
36
+ if (target.key.includes("/"))
37
+ return target.key;
38
+ return this.buildKey(target);
39
+ }
34
40
  /**
35
41
  * Generate presigned URL for simple uploads
36
42
  */
37
43
  async createPresignedUpload(target) {
38
- const key = this.buildKey(target);
44
+ const key = this.resolveKey(target);
39
45
  // Ensure directory exists
40
46
  await this.ensureDir(target.bucket);
41
47
  const expiresIn = 3600; // 1 hour
@@ -8,10 +8,17 @@ export declare class S3UploadProvider implements UploadProvider {
8
8
  private config;
9
9
  private uploadTracking;
10
10
  constructor(config: S3ProviderConfig);
11
+ /**
12
+ * AWS SDK `Client.send` types sometimes fail to resolve under standalone pnpm +
13
+ * moduleResolution bundler; call through a narrow helper so builds stay portable.
14
+ */
15
+ private send;
11
16
  /**
12
17
  * Build object key with tenant prefix
13
18
  */
14
19
  private buildKey;
20
+ /** Prefer an explicit full object key (contains `/`) over generating a new one. */
21
+ private resolveKey;
15
22
  /**
16
23
  * Generate presigned PUT URL for simple uploads
17
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/providers/s3.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAOZ;IACF,OAAO,CAAC,cAAc,CAA+C;gBAEzD,MAAM,EAAE,gBAAgB;IAwBpC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAQhB;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA2B3E;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAgE9F;;OAEG;YACW,mBAAmB;IAUjC;;OAEG;IACG,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EAAE,GACpB,OAAO,CAAC,cAAc,CAAC;IAuC1B;;OAEG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxE;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA4BpE;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAS/F;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,CAoBnD"}
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/providers/s3.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAOZ;IACF,OAAO,CAAC,cAAc,CAA+C;gBAEzD,MAAM,EAAE,gBAAgB;IAwBpC;;;OAGG;IACH,OAAO,CAAC,IAAI;IAIZ;;OAEG;IACH,OAAO,CAAC,QAAQ;IAQhB,mFAAmF;IACnF,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA2B3E;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAgE9F;;OAEG;YACW,mBAAmB;IAUjC;;OAEG;IACG,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EAAE,GACpB,OAAO,CAAC,cAAc,CAAC;IAuC1B;;OAEG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxE;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA4BpE;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAS/F;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,CAqBnD"}
@@ -31,6 +31,13 @@ export class S3UploadProvider {
31
31
  });
32
32
  logger.info("S3 upload provider initialized", { provider: "s3", region: this.config.region });
33
33
  }
34
+ /**
35
+ * AWS SDK `Client.send` types sometimes fail to resolve under standalone pnpm +
36
+ * moduleResolution bundler; call through a narrow helper so builds stay portable.
37
+ */
38
+ send(command) {
39
+ return this.client.send(command);
40
+ }
34
41
  /**
35
42
  * Build object key with tenant prefix
36
43
  */
@@ -41,11 +48,17 @@ export class S3UploadProvider {
41
48
  }
42
49
  return `${id}/${target.key.split("/").pop()}`;
43
50
  }
51
+ /** Prefer an explicit full object key (contains `/`) over generating a new one. */
52
+ resolveKey(target) {
53
+ if (target.key.includes("/"))
54
+ return target.key;
55
+ return this.buildKey(target);
56
+ }
44
57
  /**
45
58
  * Generate presigned PUT URL for simple uploads
46
59
  */
47
60
  async createPresignedUpload(target) {
48
- const key = this.buildKey(target);
61
+ const key = this.resolveKey(target);
49
62
  const expiresIn = 3600; // 1 hour
50
63
  const command = new PutObjectCommand({
51
64
  Bucket: target.bucket,
@@ -78,7 +91,7 @@ export class S3UploadProvider {
78
91
  Metadata: target.metadata,
79
92
  ...(target.acl !== undefined ? { ACL: target.acl } : {}),
80
93
  });
81
- const response = await this.client.send(createCmd);
94
+ const response = await this.send(createCmd);
82
95
  if (!response.UploadId) {
83
96
  throw new Error("Failed to initiate multipart upload: no UploadId returned");
84
97
  }
@@ -150,7 +163,7 @@ export class S3UploadProvider {
150
163
  Parts: completedParts,
151
164
  },
152
165
  });
153
- const response = await this.client.send(command);
166
+ const response = await this.send(command);
154
167
  const url = this.generateDownloadUrl(key);
155
168
  logger.info("Multipart upload completed", { key, bucket, uploadId, etag: response.ETag });
156
169
  return {
@@ -173,7 +186,7 @@ export class S3UploadProvider {
173
186
  Key: key,
174
187
  UploadId: uploadId,
175
188
  });
176
- await this.client.send(command);
189
+ await this.send(command);
177
190
  this.uploadTracking.delete(uploadId);
178
191
  logger.info("Multipart upload aborted", { key, bucket, uploadId });
179
192
  }
@@ -188,7 +201,7 @@ export class S3UploadProvider {
188
201
  Key: key,
189
202
  ContentType: target.contentType,
190
203
  });
191
- const response = await this.client.send(createCmd);
204
+ const response = await this.send(createCmd);
192
205
  if (!response.UploadId) {
193
206
  throw new Error("Failed to create Tus upload session");
194
207
  }
@@ -211,7 +224,7 @@ export class S3UploadProvider {
211
224
  Bucket: bucket,
212
225
  Key: key,
213
226
  });
214
- await this.client.send(command);
227
+ await this.send(command);
215
228
  logger.debug("File deleted from S3", { key, bucket });
216
229
  }
217
230
  /**
@@ -243,7 +256,8 @@ export class S3UploadProvider {
243
256
  * Create S3 provider from environment variables
244
257
  */
245
258
  export function createS3Provider() {
246
- const endpoint = process.env.S3_ENDPOINT;
259
+ // R2 uses R2_ENDPOINT; generic S3 uses S3_ENDPOINT. Prefer explicit S3, else R2.
260
+ const endpoint = process.env.S3_ENDPOINT ?? process.env.R2_ENDPOINT;
247
261
  const publicUrl = process.env.S3_PUBLIC_URL ?? process.env.R2_PUBLIC_URL;
248
262
  const config = {
249
263
  accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? process.env.R2_ACCESS_KEY_ID ?? "",
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@nebutra/uploads",
3
- "version": "0.1.2",
3
+ "version": "2.0.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "nebutra": {
8
+ "graph": "core",
8
9
  "status": "foundation",
9
10
  "productionReady": false,
10
11
  "requires": [
@@ -43,7 +44,7 @@
43
44
  "@aws-sdk/client-s3": "^3.700.0",
44
45
  "@aws-sdk/s3-request-presigner": "^3.700.0",
45
46
  "zod": "^4.3.6",
46
- "@nebutra/logger": "0.1.2"
47
+ "@nebutra/logger": "2.0.0"
47
48
  },
48
49
  "peerDependencies": {
49
50
  "@vercel/blob": ">=0.23.0",