@nebutra/uploads 0.1.2 → 3.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;AAKH,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;AAmFD;;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,CAkB1D"}
package/dist/factory.js CHANGED
@@ -2,7 +2,6 @@
2
2
  * Upload provider factory with auto-detection
3
3
  */
4
4
  import { logger } from "@nebutra/logger";
5
- import { createVercelBlobProvider, VercelBlobProvider } from "./providers/blob";
6
5
  import { createLocalProvider, LocalUploadProvider } from "./providers/local";
7
6
  import { createS3Provider, S3UploadProvider } from "./providers/s3";
8
7
  let instance = null;
@@ -25,9 +24,6 @@ function createUploadProviderByType(type, config) {
25
24
  }
26
25
  return new S3UploadProvider(config.s3);
27
26
  }
28
- if (type === "blob") {
29
- return createVercelBlobProvider();
30
- }
31
27
  if (type === "local") {
32
28
  if (!config?.local) {
33
29
  return createLocalProvider();
@@ -40,18 +36,24 @@ function createUploadProviderByType(type, config) {
40
36
  * Auto-detect provider based on environment
41
37
  */
42
38
  function autoDetectProvider(config) {
43
- // Priority 1: Check explicit UPLOAD_PROVIDER
44
- const explicitProvider = process.env.UPLOAD_PROVIDER;
39
+ // Priority 1: Check explicit UPLOAD_PROVIDER (r2 is an alias for S3-compatible)
40
+ const explicitRaw = process.env.UPLOAD_PROVIDER?.trim().toLowerCase();
41
+ if (explicitRaw === "r2") {
42
+ logger.info("Using explicit upload provider", { provider: "r2→s3" });
43
+ return new S3UploadProvider({
44
+ accessKeyId: process.env.R2_ACCESS_KEY_ID || "",
45
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY || "",
46
+ region: "auto",
47
+ ...(process.env.R2_ENDPOINT !== undefined ? { endpoint: process.env.R2_ENDPOINT } : {}),
48
+ ...(process.env.R2_PUBLIC_URL !== undefined ? { publicUrl: process.env.R2_PUBLIC_URL } : {}),
49
+ });
50
+ }
51
+ const explicitProvider = explicitRaw;
45
52
  if (explicitProvider) {
46
53
  logger.info("Using explicit upload provider", { provider: explicitProvider });
47
54
  return createUploadProviderByType(explicitProvider, config);
48
55
  }
49
- // Priority 2: Check for Vercel Blob token (zero-config on Vercel)
50
- if (process.env.BLOB_READ_WRITE_TOKEN) {
51
- logger.info("Auto-detected Vercel Blob token, using Blob provider");
52
- return createVercelBlobProvider();
53
- }
54
- // Priority 3: Check for R2 credentials
56
+ // Priority 2: Check for R2 credentials
55
57
  if (process.env.R2_ACCESS_KEY_ID || process.env.R2_SECRET_ACCESS_KEY) {
56
58
  logger.info("Auto-detected R2 credentials, using S3 provider");
57
59
  return new S3UploadProvider({
@@ -67,7 +69,7 @@ function autoDetectProvider(config) {
67
69
  logger.info("Auto-detected AWS credentials, using S3 provider");
68
70
  return createS3Provider();
69
71
  }
70
- // Priority 4: Check for custom S3 endpoint (Minio, etc.)
72
+ // Priority 4: Check for custom S3 endpoint (Minio, Aliyun OSS, etc.)
71
73
  if (process.env.S3_ENDPOINT) {
72
74
  logger.info("Using custom S3 endpoint", { endpoint: process.env.S3_ENDPOINT });
73
75
  return new S3UploadProvider({
@@ -110,15 +112,9 @@ export function getActiveProviderType() {
110
112
  if (instance instanceof S3UploadProvider) {
111
113
  return "s3";
112
114
  }
113
- if (instance instanceof VercelBlobProvider) {
114
- return "blob";
115
- }
116
115
  if (instance instanceof LocalUploadProvider) {
117
116
  return "local";
118
117
  }
119
- if (process.env.UPLOAD_PROVIDER === "blob" || process.env.BLOB_READ_WRITE_TOKEN) {
120
- return "blob";
121
- }
122
118
  if (process.env.UPLOAD_PROVIDER === "s3" ||
123
119
  process.env.R2_ACCESS_KEY_ID ||
124
120
  process.env.AWS_ACCESS_KEY_ID ||
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  * Handles presigned, multipart, and Tus protocol uploads
4
4
  */
5
5
  export { createUploadProvider, getActiveProviderType, getUploadProvider, resetUploadProvider, } from "./factory";
6
- export { createVercelBlobProvider, VercelBlobProvider } from "./providers/blob";
7
6
  export { createLocalProvider, LocalUploadProvider } from "./providers/local";
8
7
  export { createS3Provider, S3UploadProvider } from "./providers/s3";
9
8
  export type { ACLType, CompletePart, LocalProviderConfig, MultipartUpload, PresignedUpload, ProviderConfig, S3ProviderConfig, TusUploadConfig, UploadComplete, UploadMethod, UploadPart, UploadProvider, UploadProviderType, UploadTarget, ValidationRules, } from "./types";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEpE,YAAY,EACV,OAAO,EACP,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,eAAe,EACpB,uBAAuB,EACvB,cAAc,GACf,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEpE,YAAY,EACV,OAAO,EACP,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,eAAe,EACpB,uBAAuB,EACvB,cAAc,GACf,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@
5
5
  // Factory
6
6
  export { createUploadProvider, getActiveProviderType, getUploadProvider, resetUploadProvider, } from "./factory";
7
7
  // Providers
8
- export { createVercelBlobProvider, VercelBlobProvider } from "./providers/blob";
9
8
  export { createLocalProvider, LocalUploadProvider } from "./providers/local";
10
9
  export { createS3Provider, S3UploadProvider } from "./providers/s3";
11
10
  // Validation
@@ -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/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Core types for large file uploads
3
3
  */
4
- export type UploadProviderType = "s3" | "blob" | "local";
4
+ export type UploadProviderType = "s3" | "local";
5
5
  export type UploadMethod = "PUT" | "POST";
6
6
  export type ACLType = "private" | "public-read";
7
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@nebutra/uploads",
3
- "version": "0.1.2",
3
+ "version": "3.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,16 +44,12 @@
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": "3.0.0"
47
48
  },
48
49
  "peerDependencies": {
49
- "@vercel/blob": ">=0.23.0",
50
50
  "tus-js-client": "^4.2.0"
51
51
  },
52
52
  "peerDependenciesMeta": {
53
- "@vercel/blob": {
54
- "optional": true
55
- },
56
53
  "tus-js-client": {
57
54
  "optional": true
58
55
  }
@@ -1,28 +0,0 @@
1
- /**
2
- * Vercel Blob upload provider — zero-config file uploads on Vercel.
3
- *
4
- * Uses @vercel/blob for server-side uploads with public URLs.
5
- * Requires BLOB_READ_WRITE_TOKEN env var (auto-provisioned on Vercel).
6
- *
7
- * Note: Vercel Blob doesn't support true multipart uploads or Tus protocol.
8
- * For files > 500MB, use the S3 provider instead.
9
- */
10
- import type { CompletePart, MultipartUpload, PresignedUpload, TusUploadConfig, UploadComplete, UploadProvider, UploadTarget } from "../types";
11
- export interface VercelBlobConfig {
12
- token?: string;
13
- }
14
- export declare class VercelBlobProvider implements UploadProvider {
15
- private token;
16
- constructor(config?: VercelBlobConfig);
17
- private buildPathname;
18
- createPresignedUpload(target: UploadTarget): Promise<PresignedUpload>;
19
- createMultipartUpload(target: UploadTarget, _partCount: number): Promise<MultipartUpload>;
20
- completeMultipartUpload(_uploadId: string, key: string, _parts: CompletePart[]): Promise<UploadComplete>;
21
- abortMultipartUpload(_uploadId: string, key: string): Promise<void>;
22
- getTusEndpoint(_target: UploadTarget): Promise<TusUploadConfig>;
23
- deleteFile(_bucket: string, key: string): Promise<void>;
24
- getDownloadUrl(_bucket: string, key: string, _expiresInSec?: number): Promise<string>;
25
- close(): Promise<void>;
26
- }
27
- export declare function createVercelBlobProvider(config?: VercelBlobConfig): VercelBlobProvider;
28
- //# sourceMappingURL=blob.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../../src/providers/blob.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,kBAAmB,YAAW,cAAc;IACvD,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,GAAE,gBAAqB;IAIzC,OAAO,CAAC,aAAa;IAKf,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAyBrE,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAazF,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,EAAE,GACrB,OAAO,CAAC,cAAc,CAAC;IAWpB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnE,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAM/D,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQrF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,kBAAkB,CAEtF"}
@@ -1,88 +0,0 @@
1
- /**
2
- * Vercel Blob upload provider — zero-config file uploads on Vercel.
3
- *
4
- * Uses @vercel/blob for server-side uploads with public URLs.
5
- * Requires BLOB_READ_WRITE_TOKEN env var (auto-provisioned on Vercel).
6
- *
7
- * Note: Vercel Blob doesn't support true multipart uploads or Tus protocol.
8
- * For files > 500MB, use the S3 provider instead.
9
- */
10
- export class VercelBlobProvider {
11
- token;
12
- constructor(config = {}) {
13
- this.token = config.token ?? process.env.BLOB_READ_WRITE_TOKEN ?? "";
14
- }
15
- buildPathname(target) {
16
- const prefix = target.tenantId ? `${target.tenantId}/` : "";
17
- return `${prefix}${target.key}`;
18
- }
19
- async createPresignedUpload(target) {
20
- // Vercel Blob uses server-side put() — we generate a URL that the client
21
- // can PUT to via the handleUpload client helper, or we return the blob URL
22
- // after server-side upload.
23
- const blob = await import("@vercel/blob");
24
- const pathname = this.buildPathname(target);
25
- const result = await blob.put(pathname, "", {
26
- access: "public",
27
- contentType: target.contentType,
28
- token: this.token,
29
- addRandomSuffix: true,
30
- });
31
- return {
32
- url: result.url,
33
- method: "PUT",
34
- headers: {
35
- "Content-Type": target.contentType,
36
- "x-vercel-blob-token": this.token,
37
- },
38
- expiresAt: new Date(Date.now() + 3600 * 1000), // 1 hour
39
- };
40
- }
41
- async createMultipartUpload(target, _partCount) {
42
- // Vercel Blob handles large files internally — no client-side multipart needed
43
- // We simulate the interface for compatibility
44
- const presigned = await this.createPresignedUpload(target);
45
- return {
46
- uploadId: `blob_${Date.now()}`,
47
- key: this.buildPathname(target),
48
- bucket: target.bucket,
49
- parts: [{ partNumber: 1, presignedUrl: presigned.url }],
50
- completeUrl: presigned.url,
51
- };
52
- }
53
- async completeMultipartUpload(_uploadId, key, _parts) {
54
- // Vercel Blob uploads are atomic — no completion step needed
55
- return {
56
- key,
57
- bucket: "vercel-blob",
58
- url: `https://blob.vercel-storage.com/${key}`,
59
- size: 0,
60
- contentType: "application/octet-stream",
61
- };
62
- }
63
- async abortMultipartUpload(_uploadId, key) {
64
- // Clean up the blob if partially uploaded
65
- await this.deleteFile("vercel-blob", key);
66
- }
67
- async getTusEndpoint(_target) {
68
- throw new Error("Vercel Blob does not support Tus protocol. Use S3 provider for resumable uploads.");
69
- }
70
- async deleteFile(_bucket, key) {
71
- const blob = await import("@vercel/blob");
72
- const url = key.startsWith("http") ? key : `https://blob.vercel-storage.com/${key}`;
73
- await blob.del(url, { token: this.token });
74
- }
75
- async getDownloadUrl(_bucket, key, _expiresInSec) {
76
- // Vercel Blob URLs are public by default — no signed URL needed
77
- const blob = await import("@vercel/blob");
78
- const url = key.startsWith("http") ? key : `https://blob.vercel-storage.com/${key}`;
79
- const meta = await blob.head(url, { token: this.token });
80
- return meta.url;
81
- }
82
- async close() {
83
- // No-op — Vercel Blob has no persistent connections
84
- }
85
- }
86
- export function createVercelBlobProvider(config) {
87
- return new VercelBlobProvider(config);
88
- }