@uploadbox/core 0.1.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.
Files changed (76) hide show
  1. package/dist/builder.d.ts +11 -0
  2. package/dist/builder.d.ts.map +1 -0
  3. package/dist/builder.js +27 -0
  4. package/dist/builder.js.map +1 -0
  5. package/dist/create-uploadbox.d.ts +13 -0
  6. package/dist/create-uploadbox.d.ts.map +1 -0
  7. package/dist/create-uploadbox.js +31 -0
  8. package/dist/create-uploadbox.js.map +1 -0
  9. package/dist/errors.d.ts +22 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/errors.js +53 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/file-types.d.ts +6 -0
  14. package/dist/file-types.d.ts.map +1 -0
  15. package/dist/file-types.js +39 -0
  16. package/dist/file-types.js.map +1 -0
  17. package/dist/hooks/image-resize.d.ts +24 -0
  18. package/dist/hooks/image-resize.d.ts.map +1 -0
  19. package/dist/hooks/image-resize.js +78 -0
  20. package/dist/hooks/image-resize.js.map +1 -0
  21. package/dist/hooks/virus-scan.d.ts +24 -0
  22. package/dist/hooks/virus-scan.d.ts.map +1 -0
  23. package/dist/hooks/virus-scan.js +69 -0
  24. package/dist/hooks/virus-scan.js.map +1 -0
  25. package/dist/index.d.ts +15 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +12 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/processing.d.ts +25 -0
  30. package/dist/processing.d.ts.map +1 -0
  31. package/dist/processing.js +2 -0
  32. package/dist/processing.js.map +1 -0
  33. package/dist/s3-multipart.d.ts +12 -0
  34. package/dist/s3-multipart.d.ts.map +1 -0
  35. package/dist/s3-multipart.js +72 -0
  36. package/dist/s3-multipart.js.map +1 -0
  37. package/dist/s3.d.ts +20 -0
  38. package/dist/s3.d.ts.map +1 -0
  39. package/dist/s3.js +95 -0
  40. package/dist/s3.js.map +1 -0
  41. package/dist/server-api.d.ts +43 -0
  42. package/dist/server-api.d.ts.map +1 -0
  43. package/dist/server-api.js +48 -0
  44. package/dist/server-api.js.map +1 -0
  45. package/dist/server-upload.d.ts +19 -0
  46. package/dist/server-upload.d.ts.map +1 -0
  47. package/dist/server-upload.js +53 -0
  48. package/dist/server-upload.js.map +1 -0
  49. package/dist/types.d.ts +94 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +3 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/utils.d.ts +7 -0
  54. package/dist/utils.d.ts.map +1 -0
  55. package/dist/utils.js +50 -0
  56. package/dist/utils.js.map +1 -0
  57. package/dist/validation.d.ts +3 -0
  58. package/dist/validation.d.ts.map +1 -0
  59. package/dist/validation.js +44 -0
  60. package/dist/validation.js.map +1 -0
  61. package/package.json +58 -0
  62. package/src/builder.ts +39 -0
  63. package/src/create-uploadbox.ts +46 -0
  64. package/src/errors.ts +65 -0
  65. package/src/file-types.ts +37 -0
  66. package/src/hooks/image-resize.ts +102 -0
  67. package/src/hooks/virus-scan.ts +95 -0
  68. package/src/index.ts +38 -0
  69. package/src/processing.ts +28 -0
  70. package/src/s3-multipart.ts +107 -0
  71. package/src/s3.ts +142 -0
  72. package/src/server-api.ts +81 -0
  73. package/src/server-upload.ts +88 -0
  74. package/src/types.ts +105 -0
  75. package/src/utils.ts +54 -0
  76. package/src/validation.ts +52 -0
@@ -0,0 +1,11 @@
1
+ import type { FileRouteConfig, FileRoute, MiddlewareFn, OnUploadCompleteFn } from "./types.js";
2
+ export declare class UploadBuilder<TMetadata = Record<string, unknown>, TReturn = unknown> {
3
+ /** @internal */ _config: FileRouteConfig;
4
+ /** @internal */ _middleware: MiddlewareFn<TMetadata>;
5
+ /** @internal */ _onUploadComplete: OnUploadCompleteFn<TMetadata, TReturn>;
6
+ constructor(config: FileRouteConfig);
7
+ middleware<TNewMetadata>(fn: MiddlewareFn<TNewMetadata>): UploadBuilder<TNewMetadata, TReturn>;
8
+ onUploadComplete<TNewReturn>(fn: OnUploadCompleteFn<TMetadata, TNewReturn>): FileRoute<TMetadata, TNewReturn>;
9
+ }
10
+ export declare function createUploadBuilder(config: FileRouteConfig): UploadBuilder;
11
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,YAAY,EACZ,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,qBAAa,aAAa,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO;IAC/E,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC;IAC1C,gBAAgB,CAAC,WAAW,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACtD,gBAAgB,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAE/D,MAAM,EAAE,eAAe;IAMnC,UAAU,CAAC,YAAY,EAAE,EAAE,EAAE,YAAY,CAAC,YAAY,CAAC,GAAG,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC;IAO9F,gBAAgB,CAAC,UAAU,EACzB,EAAE,EAAE,kBAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,GAC5C,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC;CAOpC;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,aAAa,CAE1E"}
@@ -0,0 +1,27 @@
1
+ export class UploadBuilder {
2
+ /** @internal */ _config;
3
+ /** @internal */ _middleware;
4
+ /** @internal */ _onUploadComplete;
5
+ constructor(config) {
6
+ this._config = config;
7
+ this._middleware = (async () => ({}));
8
+ this._onUploadComplete = (async () => undefined);
9
+ }
10
+ middleware(fn) {
11
+ const builder = new UploadBuilder(this._config);
12
+ builder._middleware = fn;
13
+ builder._onUploadComplete = this._onUploadComplete;
14
+ return builder;
15
+ }
16
+ onUploadComplete(fn) {
17
+ return {
18
+ _config: this._config,
19
+ _middleware: this._middleware,
20
+ _onUploadComplete: fn,
21
+ };
22
+ }
23
+ }
24
+ export function createUploadBuilder(config) {
25
+ return new UploadBuilder(config);
26
+ }
27
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,aAAa;IACxB,gBAAgB,CAAC,OAAO,CAAkB;IAC1C,gBAAgB,CAAC,WAAW,CAA0B;IACtD,gBAAgB,CAAC,iBAAiB,CAAyC;IAE3E,YAAY,MAAuB;QACjC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAc,CAA4B,CAAC;QAC9E,IAAI,CAAC,iBAAiB,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAsD,CAAC;IACxG,CAAC;IAED,UAAU,CAAe,EAA8B;QACrD,MAAM,OAAO,GAAG,IAAI,aAAa,CAAwB,IAAI,CAAC,OAAO,CAAC,CAAC;QACvE,OAAO,CAAC,WAAW,GAAG,EAAE,CAAC;QACzB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAyE,CAAC;QAC3G,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,gBAAgB,CACd,EAA6C;QAE7C,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,iBAAiB,EAAE,EAAE;SACtB,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAuB;IACzD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { FileRouter, FileRouteConfig, UploadboxConfig } from "./types.js";
2
+ import type { S3Client } from "@aws-sdk/client-s3";
3
+ export interface UploadboxInstance<TRouter extends FileRouter> {
4
+ router: TRouter;
5
+ s3Client: S3Client;
6
+ config: UploadboxConfig;
7
+ }
8
+ export declare function createUploadbox<TRouter extends FileRouter>(opts: {
9
+ router: TRouter;
10
+ config?: Partial<UploadboxConfig>;
11
+ }): UploadboxInstance<TRouter>;
12
+ export declare function f(config: FileRouteConfig): import("./builder.js").UploadBuilder<Record<string, unknown>, unknown>;
13
+ //# sourceMappingURL=create-uploadbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-uploadbox.d.ts","sourceRoot":"","sources":["../src/create-uploadbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,WAAW,iBAAiB,CAAC,OAAO,SAAS,UAAU;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;CACzB;AAiBD,wBAAgB,eAAe,CAAC,OAAO,SAAS,UAAU,EAAE,IAAI,EAAE;IAChE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;CACnC,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAW7B;AAGD,wBAAgB,CAAC,CAAC,MAAM,EAAE,eAAe,0EAExC"}
@@ -0,0 +1,31 @@
1
+ import { createUploadBuilder } from "./builder.js";
2
+ import { createS3Client } from "./s3.js";
3
+ function getConfigFromEnv() {
4
+ return {
5
+ region: process.env.UPLOADBOX_AWS_REGION ?? "us-east-1",
6
+ bucket: process.env.UPLOADBOX_S3_BUCKET ?? "",
7
+ accessKeyId: process.env.UPLOADBOX_AWS_ACCESS_KEY_ID ?? "",
8
+ secretAccessKey: process.env.UPLOADBOX_AWS_SECRET_ACCESS_KEY ?? "",
9
+ cdnBaseUrl: process.env.UPLOADBOX_CDN_BASE_URL,
10
+ endpoint: process.env.UPLOADBOX_S3_ENDPOINT,
11
+ forcePathStyle: process.env.UPLOADBOX_S3_FORCE_PATH_STYLE === "true",
12
+ presignedUrlExpiry: process.env.UPLOADBOX_PRESIGNED_URL_EXPIRY
13
+ ? parseInt(process.env.UPLOADBOX_PRESIGNED_URL_EXPIRY, 10)
14
+ : undefined,
15
+ };
16
+ }
17
+ export function createUploadbox(opts) {
18
+ const envConfig = getConfigFromEnv();
19
+ const config = { ...envConfig, ...opts.config };
20
+ const s3Client = createS3Client(config);
21
+ return {
22
+ router: opts.router,
23
+ s3Client,
24
+ config,
25
+ };
26
+ }
27
+ // The `f` function — shorthand for creating a file route builder
28
+ export function f(config) {
29
+ return createUploadBuilder(config);
30
+ }
31
+ //# sourceMappingURL=create-uploadbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-uploadbox.js","sourceRoot":"","sources":["../src/create-uploadbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AASzC,SAAS,gBAAgB;IACvB,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,WAAW;QACvD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE;QAC7C,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,EAAE;QAC1D,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,EAAE;QAClE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAC9C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB;QAC3C,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,6BAA6B,KAAK,MAAM;QACpE,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,8BAA8B;YAC5D,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,EAAE,CAAC;YAC1D,CAAC,CAAC,SAAS;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAA6B,IAG3D;IACC,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAoB,EAAE,GAAG,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAEjE,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAExC,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ;QACR,MAAM;KACP,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,CAAC,CAAC,MAAuB;IACvC,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,22 @@
1
+ export declare class UploadboxError extends Error {
2
+ readonly code: string;
3
+ readonly statusCode: number;
4
+ constructor(code: string, message: string, statusCode?: number);
5
+ static fileTooLarge(maxSize: string): UploadboxError;
6
+ static invalidFileType(type: string): UploadboxError;
7
+ static tooManyFiles(max: number): UploadboxError;
8
+ static tooFewFiles(min: number): UploadboxError;
9
+ static routeNotFound(route: string): UploadboxError;
10
+ static uploadFailed(reason: string): UploadboxError;
11
+ static unauthorized(message?: string): UploadboxError;
12
+ static rateLimited(retryAfter: number): UploadboxError;
13
+ static forbidden(message?: string): UploadboxError;
14
+ static quotaExceeded(): UploadboxError;
15
+ static s3Error(message: string): UploadboxError;
16
+ toJSON(): {
17
+ error: string;
18
+ message: string;
19
+ statusCode: number;
20
+ };
21
+ }
22
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,UAAU,EAAE,MAAM,CAAC;gBAEvB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY;IAOnE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM;IAInC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM;IAInC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM;IAI/B,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM;IAI9B,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM;IAIlC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM;IAIlC,MAAM,CAAC,YAAY,CAAC,OAAO,SAAiB;IAI5C,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM;IAMrC,MAAM,CAAC,SAAS,CAAC,OAAO,SAAc;IAItC,MAAM,CAAC,aAAa;IAIpB,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM;IAI9B,MAAM;;;;;CAOP"}
package/dist/errors.js ADDED
@@ -0,0 +1,53 @@
1
+ export class UploadboxError extends Error {
2
+ code;
3
+ statusCode;
4
+ constructor(code, message, statusCode = 400) {
5
+ super(message);
6
+ this.name = "UploadboxError";
7
+ this.code = code;
8
+ this.statusCode = statusCode;
9
+ }
10
+ static fileTooLarge(maxSize) {
11
+ return new UploadboxError("FILE_TOO_LARGE", `File exceeds maximum size of ${maxSize}`, 413);
12
+ }
13
+ static invalidFileType(type) {
14
+ return new UploadboxError("INVALID_FILE_TYPE", `File type "${type}" is not allowed`, 415);
15
+ }
16
+ static tooManyFiles(max) {
17
+ return new UploadboxError("TOO_MANY_FILES", `Maximum ${max} files allowed`, 400);
18
+ }
19
+ static tooFewFiles(min) {
20
+ return new UploadboxError("TOO_FEW_FILES", `Minimum ${min} files required`, 400);
21
+ }
22
+ static routeNotFound(route) {
23
+ return new UploadboxError("ROUTE_NOT_FOUND", `Upload route "${route}" not found`, 404);
24
+ }
25
+ static uploadFailed(reason) {
26
+ return new UploadboxError("UPLOAD_FAILED", `Upload failed: ${reason}`, 500);
27
+ }
28
+ static unauthorized(message = "Unauthorized") {
29
+ return new UploadboxError("UNAUTHORIZED", message, 401);
30
+ }
31
+ static rateLimited(retryAfter) {
32
+ const err = new UploadboxError("RATE_LIMITED", `Rate limit exceeded. Retry after ${retryAfter} seconds`, 429);
33
+ err.retryAfter = retryAfter;
34
+ return err;
35
+ }
36
+ static forbidden(message = "Forbidden") {
37
+ return new UploadboxError("FORBIDDEN", message, 403);
38
+ }
39
+ static quotaExceeded() {
40
+ return new UploadboxError("QUOTA_EXCEEDED", "Storage or upload quota exceeded", 413);
41
+ }
42
+ static s3Error(message) {
43
+ return new UploadboxError("S3_ERROR", `S3 error: ${message}`, 500);
44
+ }
45
+ toJSON() {
46
+ return {
47
+ error: this.code,
48
+ message: this.message,
49
+ statusCode: this.statusCode,
50
+ };
51
+ }
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvB,IAAI,CAAS;IACb,UAAU,CAAS;IAEnC,YAAY,IAAY,EAAE,OAAe,EAAE,aAAqB,GAAG;QACjE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAe;QACjC,OAAO,IAAI,cAAc,CAAC,gBAAgB,EAAE,gCAAgC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,IAAY;QACjC,OAAO,IAAI,cAAc,CAAC,mBAAmB,EAAE,cAAc,IAAI,kBAAkB,EAAE,GAAG,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,GAAW;QAC7B,OAAO,IAAI,cAAc,CAAC,gBAAgB,EAAE,WAAW,GAAG,gBAAgB,EAAE,GAAG,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,GAAW;QAC5B,OAAO,IAAI,cAAc,CAAC,eAAe,EAAE,WAAW,GAAG,iBAAiB,EAAE,GAAG,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,KAAa;QAChC,OAAO,IAAI,cAAc,CAAC,iBAAiB,EAAE,iBAAiB,KAAK,aAAa,EAAE,GAAG,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,MAAc;QAChC,OAAO,IAAI,cAAc,CAAC,eAAe,EAAE,kBAAkB,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAO,GAAG,cAAc;QAC1C,OAAO,IAAI,cAAc,CAAC,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,UAAkB;QACnC,MAAM,GAAG,GAAG,IAAI,cAAc,CAAC,cAAc,EAAE,oCAAoC,UAAU,UAAU,EAAE,GAAG,CAAC,CAAC;QAC7G,GAAW,CAAC,UAAU,GAAG,UAAU,CAAC;QACrC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,CAAC,SAAS,CAAC,OAAO,GAAG,WAAW;QACpC,OAAO,IAAI,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,aAAa;QAClB,OAAO,IAAI,cAAc,CAAC,gBAAgB,EAAE,kCAAkC,EAAE,GAAG,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAAe;QAC5B,OAAO,IAAI,cAAc,CAAC,UAAU,EAAE,aAAa,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC;IACrE,CAAC;IAED,MAAM;QACJ,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import type { FileType } from "./types.js";
2
+ export declare const FILE_TYPE_MIME_MAP: Record<FileType, string[]>;
3
+ export declare const DEFAULT_MAX_FILE_SIZES: Record<FileType, string>;
4
+ export declare function getFileTypeFromMime(mimeType: string): FileType | null;
5
+ export declare function isFileTypeAllowed(mimeType: string, allowedTypes: FileType[]): boolean;
6
+ //# sourceMappingURL=file-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-types.d.ts","sourceRoot":"","sources":["../src/file-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAOzD,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAO3D,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAUrE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,OAAO,CAIrF"}
@@ -0,0 +1,39 @@
1
+ export const FILE_TYPE_MIME_MAP = {
2
+ image: ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml", "image/avif", "image/heic", "image/heif"],
3
+ video: ["video/mp4", "video/webm", "video/ogg", "video/quicktime", "video/x-msvideo"],
4
+ audio: ["audio/mpeg", "audio/ogg", "audio/wav", "audio/webm", "audio/aac", "audio/flac"],
5
+ pdf: ["application/pdf"],
6
+ text: ["text/plain", "text/csv", "text/html", "text/css", "text/javascript", "application/json", "application/xml"],
7
+ blob: ["application/octet-stream"],
8
+ };
9
+ export const DEFAULT_MAX_FILE_SIZES = {
10
+ image: "4MB",
11
+ video: "64MB",
12
+ audio: "16MB",
13
+ pdf: "16MB",
14
+ text: "1MB",
15
+ blob: "8MB",
16
+ };
17
+ export function getFileTypeFromMime(mimeType) {
18
+ for (const [fileType, mimes] of Object.entries(FILE_TYPE_MIME_MAP)) {
19
+ if (mimes.includes(mimeType))
20
+ return fileType;
21
+ }
22
+ // Fallback: check prefix
23
+ if (mimeType.startsWith("image/"))
24
+ return "image";
25
+ if (mimeType.startsWith("video/"))
26
+ return "video";
27
+ if (mimeType.startsWith("audio/"))
28
+ return "audio";
29
+ if (mimeType.startsWith("text/"))
30
+ return "text";
31
+ return null;
32
+ }
33
+ export function isFileTypeAllowed(mimeType, allowedTypes) {
34
+ if (allowedTypes.includes("blob"))
35
+ return true;
36
+ const fileType = getFileTypeFromMime(mimeType);
37
+ return fileType !== null && allowedTypes.includes(fileType);
38
+ }
39
+ //# sourceMappingURL=file-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-types.js","sourceRoot":"","sources":["../src/file-types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAA+B;IAC5D,KAAK,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC;IACxH,KAAK,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,CAAC;IACrF,KAAK,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,CAAC;IACxF,GAAG,EAAE,CAAC,iBAAiB,CAAC;IACxB,IAAI,EAAE,CAAC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,CAAC;IACnH,IAAI,EAAE,CAAC,0BAA0B,CAAC;CACnC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,KAAK,EAAE,KAAK;IACZ,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,MAAM;IACb,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACnE,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAoB,CAAC;IAC5D,CAAC;IACD,yBAAyB;IACzB,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC;IAClD,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC;IAClD,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,OAAO,CAAC;IAClD,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC;IAChD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,YAAwB;IAC1E,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,QAAQ,KAAK,IAAI,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ProcessingHook } from "../processing.js";
2
+ interface ImageResizeOptions {
3
+ maxWidth?: number;
4
+ maxHeight?: number;
5
+ quality?: number;
6
+ format?: "jpeg" | "png" | "webp";
7
+ }
8
+ /**
9
+ * Example image resize hook using sharp.
10
+ * Install sharp as a dependency to use this hook:
11
+ * npm install sharp
12
+ *
13
+ * Usage:
14
+ * import { createImageResizeHook } from "@uploadbox/core/hooks/image-resize";
15
+ *
16
+ * const resizeHook = createImageResizeHook({
17
+ * maxWidth: 1920,
18
+ * maxHeight: 1080,
19
+ * quality: 80,
20
+ * });
21
+ */
22
+ export declare function createImageResizeHook(options?: ImageResizeOptions): ProcessingHook;
23
+ export {};
24
+ //# sourceMappingURL=image-resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-resize.d.ts","sourceRoot":"","sources":["../../src/hooks/image-resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA2C,MAAM,kBAAkB,CAAC;AAEhG,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CAClC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,kBAAuB,GAAG,cAAc,CA8EtF"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Example image resize hook using sharp.
3
+ * Install sharp as a dependency to use this hook:
4
+ * npm install sharp
5
+ *
6
+ * Usage:
7
+ * import { createImageResizeHook } from "@uploadbox/core/hooks/image-resize";
8
+ *
9
+ * const resizeHook = createImageResizeHook({
10
+ * maxWidth: 1920,
11
+ * maxHeight: 1080,
12
+ * quality: 80,
13
+ * });
14
+ */
15
+ export function createImageResizeHook(options = {}) {
16
+ const { maxWidth = 1920, maxHeight = 1080, quality = 80, format = "webp", } = options;
17
+ return {
18
+ name: "image-resize",
19
+ timeoutMs: 60000,
20
+ shouldRun(ctx) {
21
+ return ctx.file.type.startsWith("image/");
22
+ },
23
+ async run(ctx) {
24
+ try {
25
+ // Dynamic import to make sharp optional
26
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
27
+ let sharp;
28
+ try {
29
+ sharp = await Function('return import("sharp")')();
30
+ }
31
+ catch {
32
+ return {
33
+ success: false,
34
+ error: "sharp is not installed. Run: npm install sharp",
35
+ };
36
+ }
37
+ const { GetObjectCommand, PutObjectCommand } = await import("@aws-sdk/client-s3");
38
+ // Download file from S3
39
+ const response = await ctx.s3Client.send(new GetObjectCommand({
40
+ Bucket: ctx.config.bucket,
41
+ Key: ctx.file.key,
42
+ }));
43
+ const body = await response.Body?.transformToByteArray();
44
+ if (!body) {
45
+ return { success: false, error: "Failed to download file from S3" };
46
+ }
47
+ // Resize image
48
+ const sharpFn = sharp.default ?? sharp;
49
+ const resized = await sharpFn(Buffer.from(body))
50
+ .resize(maxWidth, maxHeight, { fit: "inside", withoutEnlargement: true })
51
+ .toFormat(format, { quality })
52
+ .toBuffer();
53
+ // Upload resized image back to S3
54
+ await ctx.s3Client.send(new PutObjectCommand({
55
+ Bucket: ctx.config.bucket,
56
+ Key: ctx.file.key,
57
+ Body: resized,
58
+ ContentType: `image/${format}`,
59
+ }));
60
+ return {
61
+ success: true,
62
+ data: {
63
+ originalSize: body.length,
64
+ resizedSize: resized.length,
65
+ format,
66
+ },
67
+ };
68
+ }
69
+ catch (err) {
70
+ return {
71
+ success: false,
72
+ error: err.message,
73
+ };
74
+ }
75
+ },
76
+ };
77
+ }
78
+ //# sourceMappingURL=image-resize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-resize.js","sourceRoot":"","sources":["../../src/hooks/image-resize.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAA8B,EAAE;IACpE,MAAM,EACJ,QAAQ,GAAG,IAAI,EACf,SAAS,GAAG,IAAI,EAChB,OAAO,GAAG,EAAE,EACZ,MAAM,GAAG,MAAM,GAChB,GAAG,OAAO,CAAC;IAEZ,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,KAAK;QAEhB,SAAS,CAAC,GAAsB;YAC9B,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5C,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,GAAsB;YAC9B,IAAI,CAAC;gBACH,wCAAwC;gBACxC,8DAA8D;gBAC9D,IAAI,KAAU,CAAC;gBACf,IAAI,CAAC;oBACH,KAAK,GAAG,MAAO,QAAQ,CAAC,wBAAwB,CAAC,EAAmB,CAAC;gBACvE,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO;wBACL,OAAO,EAAE,KAAK;wBACd,KAAK,EAAE,gDAAgD;qBACxD,CAAC;gBACJ,CAAC;gBAED,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;gBAElF,wBAAwB;gBACxB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CACtC,IAAI,gBAAgB,CAAC;oBACnB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM;oBACzB,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG;iBAClB,CAAC,CACH,CAAC;gBAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC;gBACzD,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;gBACtE,CAAC;gBAED,eAAe;gBACf,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;gBACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;qBAC7C,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;qBACxE,QAAQ,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC;qBAC7B,QAAQ,EAAE,CAAC;gBAEd,kCAAkC;gBAClC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CACrB,IAAI,gBAAgB,CAAC;oBACnB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM;oBACzB,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG;oBACjB,IAAI,EAAE,OAAO;oBACb,WAAW,EAAE,SAAS,MAAM,EAAE;iBAC/B,CAAC,CACH,CAAC;gBAEF,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE;wBACJ,YAAY,EAAE,IAAI,CAAC,MAAM;wBACzB,WAAW,EAAE,OAAO,CAAC,MAAM;wBAC3B,MAAM;qBACP;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,KAAK,EAAG,GAAa,CAAC,OAAO;iBAC9B,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ProcessingHook } from "../processing.js";
2
+ interface VirusScanOptions {
3
+ /** ClamAV REST API endpoint (e.g., http://localhost:3310/scan) */
4
+ clamavUrl: string;
5
+ /** Whether to delete the file if a virus is found */
6
+ deleteOnDetection?: boolean;
7
+ /** Timeout in ms for the scan request */
8
+ timeoutMs?: number;
9
+ }
10
+ /**
11
+ * Example virus scan hook using ClamAV REST API.
12
+ * Requires a running ClamAV REST service.
13
+ *
14
+ * Usage:
15
+ * import { createVirusScanHook } from "@uploadbox/core/hooks/virus-scan";
16
+ *
17
+ * const scanHook = createVirusScanHook({
18
+ * clamavUrl: "http://localhost:3310/scan",
19
+ * deleteOnDetection: true,
20
+ * });
21
+ */
22
+ export declare function createVirusScanHook(options: VirusScanOptions): ProcessingHook;
23
+ export {};
24
+ //# sourceMappingURL=virus-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virus-scan.d.ts","sourceRoot":"","sources":["../../src/hooks/virus-scan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA2C,MAAM,kBAAkB,CAAC;AAEhG,UAAU,gBAAgB;IACxB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,CAuE7E"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Example virus scan hook using ClamAV REST API.
3
+ * Requires a running ClamAV REST service.
4
+ *
5
+ * Usage:
6
+ * import { createVirusScanHook } from "@uploadbox/core/hooks/virus-scan";
7
+ *
8
+ * const scanHook = createVirusScanHook({
9
+ * clamavUrl: "http://localhost:3310/scan",
10
+ * deleteOnDetection: true,
11
+ * });
12
+ */
13
+ export function createVirusScanHook(options) {
14
+ const { clamavUrl, deleteOnDetection = true, timeoutMs = 30000, } = options;
15
+ return {
16
+ name: "virus-scan",
17
+ timeoutMs,
18
+ shouldRun() {
19
+ // Scan all uploaded files
20
+ return true;
21
+ },
22
+ async run(ctx) {
23
+ try {
24
+ const { GetObjectCommand, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
25
+ // Download file from S3
26
+ const response = await ctx.s3Client.send(new GetObjectCommand({
27
+ Bucket: ctx.config.bucket,
28
+ Key: ctx.file.key,
29
+ }));
30
+ const body = await response.Body?.transformToByteArray();
31
+ if (!body) {
32
+ return { success: false, error: "Failed to download file from S3" };
33
+ }
34
+ // Send to ClamAV for scanning
35
+ const scanResponse = await fetch(clamavUrl, {
36
+ method: "POST",
37
+ body: Buffer.from(body),
38
+ headers: {
39
+ "Content-Type": "application/octet-stream",
40
+ },
41
+ signal: AbortSignal.timeout(timeoutMs),
42
+ });
43
+ const scanResult = await scanResponse.text();
44
+ const isClean = scanResponse.ok && !scanResult.toLowerCase().includes("found");
45
+ if (!isClean && deleteOnDetection) {
46
+ await ctx.s3Client.send(new DeleteObjectCommand({
47
+ Bucket: ctx.config.bucket,
48
+ Key: ctx.file.key,
49
+ }));
50
+ }
51
+ return {
52
+ success: isClean,
53
+ data: {
54
+ clean: isClean,
55
+ scanResult: scanResult.slice(0, 500),
56
+ },
57
+ error: isClean ? undefined : `Virus detected: ${scanResult.slice(0, 200)}`,
58
+ };
59
+ }
60
+ catch (err) {
61
+ return {
62
+ success: false,
63
+ error: `Virus scan failed: ${err.message}`,
64
+ };
65
+ }
66
+ },
67
+ };
68
+ }
69
+ //# sourceMappingURL=virus-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virus-scan.js","sourceRoot":"","sources":["../../src/hooks/virus-scan.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAyB;IAC3D,MAAM,EACJ,SAAS,EACT,iBAAiB,GAAG,IAAI,EACxB,SAAS,GAAG,KAAK,GAClB,GAAG,OAAO,CAAC;IAEZ,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,SAAS;QAET,SAAS;YACP,0BAA0B;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,GAAsB;YAC9B,IAAI,CAAC;gBACH,MAAM,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;gBAErF,wBAAwB;gBACxB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CACtC,IAAI,gBAAgB,CAAC;oBACnB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM;oBACzB,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG;iBAClB,CAAC,CACH,CAAC;gBAEF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC;gBACzD,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;gBACtE,CAAC;gBAED,8BAA8B;gBAC9B,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;oBAC1C,MAAM,EAAE,MAAM;oBACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;oBACvB,OAAO,EAAE;wBACP,cAAc,EAAE,0BAA0B;qBAC3C;oBACD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;iBACvC,CAAC,CAAC;gBAEH,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC;gBAC7C,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAE/E,IAAI,CAAC,OAAO,IAAI,iBAAiB,EAAE,CAAC;oBAClC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CACrB,IAAI,mBAAmB,CAAC;wBACtB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM;wBACzB,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG;qBAClB,CAAC,CACH,CAAC;gBACJ,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE,OAAO;oBAChB,IAAI,EAAE;wBACJ,KAAK,EAAE,OAAO;wBACd,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;qBACrC;oBACD,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;iBAC3E,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,sBAAuB,GAAa,CAAC,OAAO,EAAE;iBACtD,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { createUploadbox, f } from "./create-uploadbox.js";
2
+ export { UploadboxApi } from "./server-api.js";
3
+ export { UploadboxError } from "./errors.js";
4
+ export { createUploadBuilder } from "./builder.js";
5
+ export { validateFiles } from "./validation.js";
6
+ export { createS3Client, generatePresignedPutUrl, generatePresignedGetUrl, headObject, deleteObject, deleteObjects, listObjects } from "./s3.js";
7
+ export { parseFileSize, formatFileSize, generateFileKey, generateUploadId } from "./utils.js";
8
+ export { FILE_TYPE_MIME_MAP, DEFAULT_MAX_FILE_SIZES, getFileTypeFromMime, isFileTypeAllowed } from "./file-types.js";
9
+ export { serverUpload, serverUploadMany } from "./server-upload.js";
10
+ export type { ServerUploadInput, ServerUploadResult } from "./server-upload.js";
11
+ export { createMultipartUpload, generatePresignedPartUrls, completeMultipartUpload, abortMultipartUpload, } from "./s3-multipart.js";
12
+ export type { FileType, FileSize, ACL, ContentDisposition, FileRouteConfig, FileInfo, UploadedFileData, MiddlewareFn, OnUploadCompleteFn, FileRoute, FileRouter, PresignedUrlResponse, UploadCompleteResponse, UploadboxConfig, RouterConfig, AuthContext, MultipartPresignedResponse, CompletedPartInfo, } from "./types.js";
13
+ export { MULTIPART_THRESHOLD, DEFAULT_PART_SIZE } from "./types.js";
14
+ export type { ProcessingHook, ProcessingContext, ProcessingHookResult, ProcessingPipelineConfig } from "./processing.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACjJ,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,0BAA0B,EAC1B,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export { createUploadbox, f } from "./create-uploadbox.js";
2
+ export { UploadboxApi } from "./server-api.js";
3
+ export { UploadboxError } from "./errors.js";
4
+ export { createUploadBuilder } from "./builder.js";
5
+ export { validateFiles } from "./validation.js";
6
+ export { createS3Client, generatePresignedPutUrl, generatePresignedGetUrl, headObject, deleteObject, deleteObjects, listObjects } from "./s3.js";
7
+ export { parseFileSize, formatFileSize, generateFileKey, generateUploadId } from "./utils.js";
8
+ export { FILE_TYPE_MIME_MAP, DEFAULT_MAX_FILE_SIZES, getFileTypeFromMime, isFileTypeAllowed } from "./file-types.js";
9
+ export { serverUpload, serverUploadMany } from "./server-upload.js";
10
+ export { createMultipartUpload, generatePresignedPartUrls, completeMultipartUpload, abortMultipartUpload, } from "./s3-multipart.js";
11
+ export { MULTIPART_THRESHOLD, DEFAULT_PART_SIZE } from "./types.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACjJ,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEpE,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAqB3B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { UploadedFileData, UploadboxConfig } from "./types.js";
2
+ import type { S3Client } from "@aws-sdk/client-s3";
3
+ export interface ProcessingContext {
4
+ file: UploadedFileData;
5
+ metadata: Record<string, unknown>;
6
+ s3Client: S3Client;
7
+ config: UploadboxConfig;
8
+ }
9
+ export interface ProcessingHookResult {
10
+ success: boolean;
11
+ data?: Record<string, unknown>;
12
+ error?: string;
13
+ }
14
+ export interface ProcessingHook {
15
+ name: string;
16
+ shouldRun: (ctx: ProcessingContext) => boolean;
17
+ run: (ctx: ProcessingContext) => Promise<ProcessingHookResult>;
18
+ timeoutMs?: number;
19
+ }
20
+ export interface ProcessingPipelineConfig {
21
+ hooks: ProcessingHook[];
22
+ mode?: "sequential" | "parallel";
23
+ continueOnError?: boolean;
24
+ }
25
+ //# sourceMappingURL=processing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processing.d.ts","sourceRoot":"","sources":["../src/processing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC;IAC/C,GAAG,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=processing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processing.js","sourceRoot":"","sources":["../src/processing.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ export declare function createMultipartUpload(client: S3Client, bucket: string, key: string, contentType: string): Promise<string>;
3
+ export declare function generatePresignedPartUrls(client: S3Client, bucket: string, key: string, uploadId: string, partNumbers: number[], expiresIn?: number): Promise<{
4
+ partNumber: number;
5
+ url: string;
6
+ }[]>;
7
+ export declare function completeMultipartUpload(client: S3Client, bucket: string, key: string, uploadId: string, parts: {
8
+ partNumber: number;
9
+ etag: string;
10
+ }[]): Promise<void>;
11
+ export declare function abortMultipartUpload(client: S3Client, bucket: string, key: string, uploadId: string): Promise<void>;
12
+ //# sourceMappingURL=s3-multipart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-multipart.d.ts","sourceRoot":"","sources":["../src/s3-multipart.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAKT,MAAM,oBAAoB,CAAC;AAI5B,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAiBhD;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAC5C,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAaf"}
@@ -0,0 +1,72 @@
1
+ import { CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand, AbortMultipartUploadCommand, } from "@aws-sdk/client-s3";
2
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
3
+ import { UploadboxError } from "./errors.js";
4
+ export async function createMultipartUpload(client, bucket, key, contentType) {
5
+ try {
6
+ const response = await client.send(new CreateMultipartUploadCommand({
7
+ Bucket: bucket,
8
+ Key: key,
9
+ ContentType: contentType,
10
+ }));
11
+ if (!response.UploadId) {
12
+ throw new Error("No UploadId returned from S3");
13
+ }
14
+ return response.UploadId;
15
+ }
16
+ catch (err) {
17
+ if (err instanceof UploadboxError)
18
+ throw err;
19
+ throw UploadboxError.s3Error(err.message);
20
+ }
21
+ }
22
+ export async function generatePresignedPartUrls(client, bucket, key, uploadId, partNumbers, expiresIn = 3600) {
23
+ try {
24
+ return await Promise.all(partNumbers.map(async (partNumber) => {
25
+ const command = new UploadPartCommand({
26
+ Bucket: bucket,
27
+ Key: key,
28
+ UploadId: uploadId,
29
+ PartNumber: partNumber,
30
+ });
31
+ const url = await getSignedUrl(client, command, { expiresIn });
32
+ return { partNumber, url };
33
+ }));
34
+ }
35
+ catch (err) {
36
+ throw UploadboxError.s3Error(err.message);
37
+ }
38
+ }
39
+ export async function completeMultipartUpload(client, bucket, key, uploadId, parts) {
40
+ try {
41
+ await client.send(new CompleteMultipartUploadCommand({
42
+ Bucket: bucket,
43
+ Key: key,
44
+ UploadId: uploadId,
45
+ MultipartUpload: {
46
+ Parts: parts
47
+ .sort((a, b) => a.partNumber - b.partNumber)
48
+ .map((p) => ({
49
+ PartNumber: p.partNumber,
50
+ ETag: p.etag,
51
+ })),
52
+ },
53
+ }));
54
+ }
55
+ catch (err) {
56
+ throw UploadboxError.s3Error(err.message);
57
+ }
58
+ }
59
+ export async function abortMultipartUpload(client, bucket, key, uploadId) {
60
+ try {
61
+ await client.send(new AbortMultipartUploadCommand({
62
+ Bucket: bucket,
63
+ Key: key,
64
+ UploadId: uploadId,
65
+ }));
66
+ }
67
+ catch (err) {
68
+ // Ignore errors when aborting — the upload may already be aborted
69
+ console.warn("[uploadbox] Failed to abort multipart upload:", err.message);
70
+ }
71
+ }
72
+ //# sourceMappingURL=s3-multipart.js.map