@forinda/kickjs-http 0.3.2 → 0.4.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.
- package/dist/application.js +2 -2
- package/dist/bootstrap.js +3 -3
- package/dist/{chunk-AK5TGZRS.js → chunk-4G2S7T4R.js} +4 -4
- package/dist/chunk-DUQ7SN7N.js +208 -0
- package/dist/chunk-DUQ7SN7N.js.map +1 -0
- package/dist/chunk-H4S527PH.js +97 -0
- package/dist/chunk-H4S527PH.js.map +1 -0
- package/dist/chunk-LEILPDMW.js +183 -0
- package/dist/chunk-LEILPDMW.js.map +1 -0
- package/dist/{chunk-DU3FQYET.js → chunk-LQ6RSWMX.js} +5 -1
- package/dist/chunk-LQ6RSWMX.js.map +1 -0
- package/dist/chunk-NQJNMKW5.js +158 -0
- package/dist/chunk-NQJNMKW5.js.map +1 -0
- package/dist/{chunk-7I33DN2G.js → chunk-OWLI3SBW.js} +2 -2
- package/dist/{chunk-BPXWHHCY.js → chunk-VFVMIFNZ.js} +9 -2
- package/dist/chunk-VFVMIFNZ.js.map +1 -0
- package/dist/context.d.ts +2 -0
- package/dist/context.js +1 -1
- package/dist/devtools.d.ts +85 -0
- package/dist/devtools.js +8 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +27 -11
- package/dist/middleware/rate-limit.d.ts +53 -0
- package/dist/middleware/rate-limit.js +8 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/session.d.ts +64 -0
- package/dist/middleware/session.js +8 -0
- package/dist/middleware/session.js.map +1 -0
- package/dist/middleware/upload.d.ts +35 -16
- package/dist/middleware/upload.js +5 -1
- package/dist/router-builder.js +3 -2
- package/package.json +15 -2
- package/dist/chunk-BPXWHHCY.js.map +0 -1
- package/dist/chunk-DU3FQYET.js.map +0 -1
- package/dist/chunk-RFOXGJ3G.js +0 -88
- package/dist/chunk-RFOXGJ3G.js.map +0 -1
- /package/dist/{chunk-AK5TGZRS.js.map → chunk-4G2S7T4R.js.map} +0 -0
- /package/dist/{chunk-7I33DN2G.js.map → chunk-OWLI3SBW.js.map} +0 -0
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import { Request, Response, NextFunction
|
|
1
|
+
import { RequestHandler, Request, Response, NextFunction } from 'express';
|
|
2
2
|
import { Options } from 'multer';
|
|
3
|
+
import { BaseUploadOptions, FileUploadConfig } from '@forinda/kickjs-core';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a list of file type identifiers to MIME type strings.
|
|
7
|
+
* Accepts short extensions (`'jpg'`, `'pdf'`) or full MIME types (`'image/jpeg'`).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* resolveMimeTypes(['jpg', 'png', 'application/pdf'])
|
|
12
|
+
* // → ['image/jpeg', 'image/png', 'application/pdf']
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare function resolveMimeTypes(types: string[]): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Upload options for the middleware.
|
|
18
|
+
* Extends BaseUploadOptions from core (shared with @FileUpload decorator)
|
|
19
|
+
* and adds Multer-specific storage options.
|
|
20
|
+
*/
|
|
21
|
+
interface UploadOptions extends BaseUploadOptions {
|
|
9
22
|
/** Multer storage config (default: memory storage) */
|
|
10
23
|
storage?: Options['storage'];
|
|
11
24
|
/** Multer dest for disk storage shorthand */
|
|
@@ -17,7 +30,7 @@ interface UploadOptions {
|
|
|
17
30
|
* @example
|
|
18
31
|
* ```ts
|
|
19
32
|
* @Post('/avatar')
|
|
20
|
-
* @Middleware(upload.single('avatar', { maxSize: 2 * 1024 * 1024, allowedTypes: ['
|
|
33
|
+
* @Middleware(upload.single('avatar', { maxSize: 2 * 1024 * 1024, allowedTypes: ['jpg', 'png'] }))
|
|
21
34
|
* async uploadAvatar(ctx: RequestContext) {
|
|
22
35
|
* ctx.json({ filename: ctx.file.originalname })
|
|
23
36
|
* }
|
|
@@ -33,20 +46,26 @@ declare function array(fieldName: string, maxCount?: number, options?: UploadOpt
|
|
|
33
46
|
*/
|
|
34
47
|
declare function none(options?: UploadOptions): RequestHandler;
|
|
35
48
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* Only cleans up disk-stored files (files with a `path` property).
|
|
49
|
+
* Removes temporary files from disk after the response is sent.
|
|
50
|
+
* Attach this as Express middleware after the upload middleware.
|
|
51
|
+
* Works with both `req.file` (single) and `req.files` (array).
|
|
40
52
|
*
|
|
41
53
|
* @example
|
|
42
54
|
* ```ts
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
55
|
+
* @Post('/process')
|
|
56
|
+
* @Middleware(upload.single('document', { dest: '/tmp/uploads' }), cleanupFiles())
|
|
57
|
+
* async processDocument(ctx: RequestContext) {
|
|
58
|
+
* ctx.json({ ok: true })
|
|
59
|
+
* }
|
|
47
60
|
* ```
|
|
48
61
|
*/
|
|
49
62
|
declare function cleanupFiles(): (req: Request, res: Response, next: NextFunction) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Build upload middleware from a @FileUpload decorator config.
|
|
65
|
+
* Used internally by the router builder when it detects FILE_UPLOAD metadata.
|
|
66
|
+
* Accepts the same FileUploadConfig interface used by the @FileUpload decorator.
|
|
67
|
+
*/
|
|
68
|
+
declare function buildUploadMiddleware(config: FileUploadConfig): RequestHandler;
|
|
50
69
|
/** Upload middleware factory with `.single()`, `.array()`, `.none()` methods */
|
|
51
70
|
declare const upload: {
|
|
52
71
|
single: typeof single;
|
|
@@ -54,4 +73,4 @@ declare const upload: {
|
|
|
54
73
|
none: typeof none;
|
|
55
74
|
};
|
|
56
75
|
|
|
57
|
-
export { type UploadOptions, cleanupFiles, upload };
|
|
76
|
+
export { type UploadOptions, buildUploadMiddleware, cleanupFiles, resolveMimeTypes, upload };
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildUploadMiddleware,
|
|
2
3
|
cleanupFiles,
|
|
4
|
+
resolveMimeTypes,
|
|
3
5
|
upload
|
|
4
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-LEILPDMW.js";
|
|
5
7
|
import "../chunk-WCQVDF3K.js";
|
|
6
8
|
export {
|
|
9
|
+
buildUploadMiddleware,
|
|
7
10
|
cleanupFiles,
|
|
11
|
+
resolveMimeTypes,
|
|
8
12
|
upload
|
|
9
13
|
};
|
|
10
14
|
//# sourceMappingURL=upload.js.map
|
package/dist/router-builder.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildRoutes,
|
|
3
3
|
getControllerPath
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VFVMIFNZ.js";
|
|
5
|
+
import "./chunk-LEILPDMW.js";
|
|
5
6
|
import "./chunk-RPN7UFUO.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-LQ6RSWMX.js";
|
|
7
8
|
import "./chunk-VXX2Y3TA.js";
|
|
8
9
|
import "./chunk-WCQVDF3K.js";
|
|
9
10
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forinda/kickjs-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Express 5 integration, router builder, RequestContext, and middleware for KickJS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,6 +30,14 @@
|
|
|
30
30
|
"import": "./dist/middleware/csrf.js",
|
|
31
31
|
"types": "./dist/middleware/csrf.d.ts"
|
|
32
32
|
},
|
|
33
|
+
"./middleware/rate-limit": {
|
|
34
|
+
"import": "./dist/middleware/rate-limit.js",
|
|
35
|
+
"types": "./dist/middleware/rate-limit.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./middleware/session": {
|
|
38
|
+
"import": "./dist/middleware/session.js",
|
|
39
|
+
"types": "./dist/middleware/session.d.ts"
|
|
40
|
+
},
|
|
33
41
|
"./middleware/upload": {
|
|
34
42
|
"import": "./dist/middleware/upload.js",
|
|
35
43
|
"types": "./dist/middleware/upload.d.ts"
|
|
@@ -46,6 +54,10 @@
|
|
|
46
54
|
"import": "./dist/middleware/error-handler.js",
|
|
47
55
|
"types": "./dist/middleware/error-handler.d.ts"
|
|
48
56
|
},
|
|
57
|
+
"./devtools": {
|
|
58
|
+
"import": "./dist/devtools.js",
|
|
59
|
+
"types": "./dist/devtools.d.ts"
|
|
60
|
+
},
|
|
49
61
|
"./query": {
|
|
50
62
|
"import": "./dist/query/index.js",
|
|
51
63
|
"types": "./dist/query/index.d.ts"
|
|
@@ -58,7 +70,7 @@
|
|
|
58
70
|
"cookie-parser": "^1.4.7",
|
|
59
71
|
"multer": "^2.1.1",
|
|
60
72
|
"reflect-metadata": "^0.2.2",
|
|
61
|
-
"@forinda/kickjs-core": "0.
|
|
73
|
+
"@forinda/kickjs-core": "0.4.0"
|
|
62
74
|
},
|
|
63
75
|
"peerDependencies": {
|
|
64
76
|
"express": "^5.1.0"
|
|
@@ -82,6 +94,7 @@
|
|
|
82
94
|
"engines": {
|
|
83
95
|
"node": ">=20.0"
|
|
84
96
|
},
|
|
97
|
+
"homepage": "https://forinda.github.io/kick-js/",
|
|
85
98
|
"scripts": {
|
|
86
99
|
"build": "tsup",
|
|
87
100
|
"dev": "tsup --watch",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/router-builder.ts"],"sourcesContent":["import 'reflect-metadata'\nimport { Router, type Request, type Response, type NextFunction } from 'express'\nimport {\n Container,\n METADATA,\n type RouteDefinition,\n type MiddlewareHandler,\n} from '@forinda/kickjs-core'\nimport { RequestContext } from './context'\nimport { validate } from './middleware/validate'\n\n/** Get the controller path set by @Controller('/path') */\nexport function getControllerPath(controllerClass: any): string {\n return Reflect.getMetadata(METADATA.CONTROLLER_PATH, controllerClass) || '/'\n}\n\n/**\n * Build an Express Router from a controller class decorated with @Get, @Post, etc.\n * Resolves the controller from the DI container, wraps handlers in RequestContext,\n * and applies class-level and method-level middleware.\n */\nexport function buildRoutes(controllerClass: any): Router {\n const router = Router()\n const container = Container.getInstance()\n const controllerPath = getControllerPath(controllerClass)\n const routes: RouteDefinition[] = Reflect.getMetadata(METADATA.ROUTES, controllerClass) || []\n\n // Class-level middleware\n const classMiddlewares: MiddlewareHandler[] =\n Reflect.getMetadata(METADATA.CLASS_MIDDLEWARES, controllerClass) || []\n\n for (const route of routes) {\n const method = route.method.toLowerCase() as 'get' | 'post' | 'put' | 'delete' | 'patch'\n let routePath = route.path === '/' ? '' : route.path\n const fullPath = controllerPath === '/' ? routePath || '/' : controllerPath + routePath\n\n // Method-level middleware\n const methodMiddlewares: MiddlewareHandler[] =\n Reflect.getMetadata(METADATA.METHOD_MIDDLEWARES, controllerClass, route.handlerName) || []\n\n // Build handler chain\n const handlers: any[] = []\n\n // Validation middleware (shared with standalone validate() export)\n if (route.validation) {\n handlers.push(validate(route.validation))\n }\n\n // Class + method middleware (wrapped as Express middleware with error catching)\n for (const mw of [...classMiddlewares, ...methodMiddlewares]) {\n handlers.push((req: Request, res: Response, next: NextFunction) => {\n const ctx = new RequestContext(req, res, next)\n Promise.resolve(mw(ctx, next)).catch(next)\n })\n }\n\n // Main handler — resolve controller per-request to respect DI scoping\n handlers.push(async (req: Request, res: Response, next: NextFunction) => {\n const ctx = new RequestContext(req, res, next)\n try {\n const controller = container.resolve(controllerClass)\n await controller[route.handlerName](ctx)\n } catch (err: any) {\n next(err)\n }\n })\n ;(router as any)[method](fullPath, ...handlers)\n }\n\n return router\n}\n"],"mappings":";;;;;;;;;;;AAAA,OAAO;AACP,SAASA,cAA8D;AACvE,SACEC,WACAC,gBAGK;AAKA,SAASC,kBAAkBC,iBAAoB;AACpD,SAAOC,QAAQC,YAAYC,SAASC,iBAAiBJ,eAAAA,KAAoB;AAC3E;AAFgBD;AAST,SAASM,YAAYL,iBAAoB;AAC9C,QAAMM,SAASC,OAAAA;AACf,QAAMC,YAAYC,UAAUC,YAAW;AACvC,QAAMC,iBAAiBZ,kBAAkBC,eAAAA;AACzC,QAAMY,SAA4BX,QAAQC,YAAYC,SAASU,QAAQb,eAAAA,KAAoB,CAAA;AAG3F,QAAMc,mBACJb,QAAQC,YAAYC,SAASY,mBAAmBf,eAAAA,KAAoB,CAAA;AAEtE,aAAWgB,SAASJ,QAAQ;AAC1B,UAAMK,SAASD,MAAMC,OAAOC,YAAW;AACvC,QAAIC,YAAYH,MAAMI,SAAS,MAAM,KAAKJ,MAAMI;AAChD,UAAMC,WAAWV,mBAAmB,MAAMQ,aAAa,MAAMR,iBAAiBQ;AAG9E,UAAMG,oBACJrB,QAAQC,YAAYC,SAASoB,oBAAoBvB,iBAAiBgB,MAAMQ,WAAW,KAAK,CAAA;AAG1F,UAAMC,WAAkB,CAAA;AAGxB,QAAIT,MAAMU,YAAY;AACpBD,eAASE,KAAKC,SAASZ,MAAMU,UAAU,CAAA;IACzC;AAGA,eAAWG,MAAM;SAAIf;SAAqBQ;OAAoB;AAC5DG,eAASE,KAAK,CAACG,KAAcC,KAAeC,SAAAA;AAC1C,cAAMC,MAAM,IAAIC,eAAeJ,KAAKC,KAAKC,IAAAA;AACzCG,gBAAQC,QAAQP,GAAGI,KAAKD,IAAAA,CAAAA,EAAOK,MAAML,IAAAA;MACvC,CAAA;IACF;AAGAP,aAASE,KAAK,OAAOG,KAAcC,KAAeC,SAAAA;AAChD,YAAMC,MAAM,IAAIC,eAAeJ,KAAKC,KAAKC,IAAAA;AACzC,UAAI;AACF,cAAMM,aAAa9B,UAAU4B,QAAQpC,eAAAA;AACrC,cAAMsC,WAAWtB,MAAMQ,WAAW,EAAES,GAAAA;MACtC,SAASM,KAAU;AACjBP,aAAKO,GAAAA;MACP;IACF,CAAA;AACEjC,WAAeW,MAAAA,EAAQI,UAAAA,GAAaI,QAAAA;EACxC;AAEA,SAAOnB;AACT;AAjDgBD;","names":["Router","Container","METADATA","getControllerPath","controllerClass","Reflect","getMetadata","METADATA","CONTROLLER_PATH","buildRoutes","router","Router","container","Container","getInstance","controllerPath","routes","ROUTES","classMiddlewares","CLASS_MIDDLEWARES","route","method","toLowerCase","routePath","path","fullPath","methodMiddlewares","METHOD_MIDDLEWARES","handlerName","handlers","validation","push","validate","mw","req","res","next","ctx","RequestContext","Promise","resolve","catch","controller","err"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts"],"sourcesContent":["import type { Request, Response, NextFunction } from 'express'\nimport { parseQuery, type ParsedQuery, type QueryFieldConfig } from './query'\n\n/**\n * Unified request/response abstraction passed to every controller method.\n * Shields handlers from raw Express objects and provides convenience methods.\n */\nexport class RequestContext<TBody = any, TParams = any, TQuery = any> {\n private metadata = new Map<string, any>()\n\n constructor(\n public readonly req: Request,\n public readonly res: Response,\n public readonly next: NextFunction,\n ) {}\n\n // ── Request Data ────────────────────────────────────────────────────\n\n get body(): TBody {\n return this.req.body as TBody\n }\n\n get params(): TParams {\n return this.req.params as TParams\n }\n\n get query(): TQuery {\n return this.req.query as TQuery\n }\n\n get headers() {\n return this.req.headers\n }\n\n get requestId(): string | undefined {\n return (this.req as any).requestId ?? (this.req.headers['x-request-id'] as string | undefined)\n }\n\n // ── Query String Parsing ───────────────────────────────────────────\n\n /**\n * Parse the request query string into structured filters, sort, pagination, and search.\n * Pass the result to an ORM query builder adapter (Drizzle, Prisma, Sequelize, etc.).\n *\n * @param fieldConfig - Optional whitelist for filterable, sortable, and searchable fields\n *\n * @example\n * ```ts\n * @Get('/')\n * async list(ctx: RequestContext) {\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * })\n * const q = drizzleAdapter.build(parsed, { columns })\n * // ... use q.where, q.orderBy, q.limit, q.offset\n * }\n * ```\n */\n qs(fieldConfig?: QueryFieldConfig): ParsedQuery {\n return parseQuery(this.req.query as Record<string, any>, fieldConfig)\n }\n\n // ── File Uploads ────────────────────────────────────────────────────\n\n /** Single uploaded file (requires @FileUpload({ mode: 'single' })) */\n get file(): any {\n return (this.req as any).file\n }\n\n /** Array of uploaded files (requires @FileUpload({ mode: 'array' })) */\n get files(): any[] | undefined {\n return (this.req as any).files\n }\n\n // ── Metadata Store ──────────────────────────────────────────────────\n\n get<T = any>(key: string): T | undefined {\n return this.metadata.get(key) as T | undefined\n }\n\n set(key: string, value: any): void {\n this.metadata.set(key, value)\n }\n\n // ── Response Helpers ────────────────────────────────────────────────\n\n json(data: any, status = 200) {\n return this.res.status(status).json(data)\n }\n\n created(data: any) {\n return this.res.status(201).json(data)\n }\n\n noContent() {\n return this.res.status(204).end()\n }\n\n notFound(message = 'Not Found') {\n return this.res.status(404).json({ message })\n }\n\n badRequest(message: string) {\n return this.res.status(400).json({ message })\n }\n\n html(content: string, status = 200) {\n return this.res.status(status).type('html').send(content)\n }\n\n download(buffer: Buffer, filename: string, contentType = 'application/octet-stream') {\n this.res.setHeader('Content-Disposition', `attachment; filename=\"${filename}\"`)\n this.res.setHeader('Content-Type', contentType)\n return this.res.send(buffer)\n }\n}\n"],"mappings":";;;;;;;;AAOO,IAAMA,iBAAN,MAAMA;EANb,OAMaA;;;;;;EACHC,WAAW,oBAAIC,IAAAA;EAEvB,YACkBC,KACAC,KACAC,MAChB;SAHgBF,MAAAA;SACAC,MAAAA;SACAC,OAAAA;EACf;;EAIH,IAAIC,OAAc;AAChB,WAAO,KAAKH,IAAIG;EAClB;EAEA,IAAIC,SAAkB;AACpB,WAAO,KAAKJ,IAAII;EAClB;EAEA,IAAIC,QAAgB;AAClB,WAAO,KAAKL,IAAIK;EAClB;EAEA,IAAIC,UAAU;AACZ,WAAO,KAAKN,IAAIM;EAClB;EAEA,IAAIC,YAAgC;AAClC,WAAQ,KAAKP,IAAYO,aAAc,KAAKP,IAAIM,QAAQ,cAAA;EAC1D;;;;;;;;;;;;;;;;;;;;;EAuBAE,GAAGC,aAA6C;AAC9C,WAAOC,WAAW,KAAKV,IAAIK,OAA8BI,WAAAA;EAC3D;;;EAKA,IAAIE,OAAY;AACd,WAAQ,KAAKX,IAAYW;EAC3B;;EAGA,IAAIC,QAA2B;AAC7B,WAAQ,KAAKZ,IAAYY;EAC3B;;EAIAC,IAAaC,KAA4B;AACvC,WAAO,KAAKhB,SAASe,IAAIC,GAAAA;EAC3B;EAEAC,IAAID,KAAaE,OAAkB;AACjC,SAAKlB,SAASiB,IAAID,KAAKE,KAAAA;EACzB;;EAIAC,KAAKC,MAAWC,SAAS,KAAK;AAC5B,WAAO,KAAKlB,IAAIkB,OAAOA,MAAAA,EAAQF,KAAKC,IAAAA;EACtC;EAEAE,QAAQF,MAAW;AACjB,WAAO,KAAKjB,IAAIkB,OAAO,GAAA,EAAKF,KAAKC,IAAAA;EACnC;EAEAG,YAAY;AACV,WAAO,KAAKpB,IAAIkB,OAAO,GAAA,EAAKG,IAAG;EACjC;EAEAC,SAASC,UAAU,aAAa;AAC9B,WAAO,KAAKvB,IAAIkB,OAAO,GAAA,EAAKF,KAAK;MAAEO;IAAQ,CAAA;EAC7C;EAEAC,WAAWD,SAAiB;AAC1B,WAAO,KAAKvB,IAAIkB,OAAO,GAAA,EAAKF,KAAK;MAAEO;IAAQ,CAAA;EAC7C;EAEAE,KAAKC,SAAiBR,SAAS,KAAK;AAClC,WAAO,KAAKlB,IAAIkB,OAAOA,MAAAA,EAAQS,KAAK,MAAA,EAAQC,KAAKF,OAAAA;EACnD;EAEAG,SAASC,QAAgBC,UAAkBC,cAAc,4BAA4B;AACnF,SAAKhC,IAAIiC,UAAU,uBAAuB,yBAAyBF,QAAAA,GAAW;AAC9E,SAAK/B,IAAIiC,UAAU,gBAAgBD,WAAAA;AACnC,WAAO,KAAKhC,IAAI4B,KAAKE,MAAAA;EACvB;AACF;","names":["RequestContext","metadata","Map","req","res","next","body","params","query","headers","requestId","qs","fieldConfig","parseQuery","file","files","get","key","set","value","json","data","status","created","noContent","end","notFound","message","badRequest","html","content","type","send","download","buffer","filename","contentType","setHeader"]}
|
package/dist/chunk-RFOXGJ3G.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__name
|
|
3
|
-
} from "./chunk-WCQVDF3K.js";
|
|
4
|
-
|
|
5
|
-
// src/middleware/upload.ts
|
|
6
|
-
import { unlink } from "fs/promises";
|
|
7
|
-
import multer from "multer";
|
|
8
|
-
function single(fieldName, options = {}) {
|
|
9
|
-
const m = createMulter(options);
|
|
10
|
-
return m.single(fieldName);
|
|
11
|
-
}
|
|
12
|
-
__name(single, "single");
|
|
13
|
-
function array(fieldName, maxCount = 10, options = {}) {
|
|
14
|
-
const m = createMulter(options);
|
|
15
|
-
return m.array(fieldName, maxCount);
|
|
16
|
-
}
|
|
17
|
-
__name(array, "array");
|
|
18
|
-
function none(options = {}) {
|
|
19
|
-
const m = createMulter(options);
|
|
20
|
-
return m.none();
|
|
21
|
-
}
|
|
22
|
-
__name(none, "none");
|
|
23
|
-
function createMulter(options) {
|
|
24
|
-
const limits = {
|
|
25
|
-
fileSize: options.maxSize ?? 5 * 1024 * 1024
|
|
26
|
-
};
|
|
27
|
-
const fileFilter = options.allowedTypes ? (_req, file, cb) => {
|
|
28
|
-
const allowed = options.allowedTypes.some((type) => {
|
|
29
|
-
if (type.endsWith("/*")) {
|
|
30
|
-
return file.mimetype.startsWith(type.replace("/*", "/"));
|
|
31
|
-
}
|
|
32
|
-
return file.mimetype === type;
|
|
33
|
-
});
|
|
34
|
-
if (allowed) {
|
|
35
|
-
cb(null, true);
|
|
36
|
-
} else {
|
|
37
|
-
cb(new Error(`File type ${file.mimetype} is not allowed`));
|
|
38
|
-
}
|
|
39
|
-
} : void 0;
|
|
40
|
-
const multerOptions = {
|
|
41
|
-
limits,
|
|
42
|
-
...fileFilter ? {
|
|
43
|
-
fileFilter
|
|
44
|
-
} : {},
|
|
45
|
-
...options.storage ? {
|
|
46
|
-
storage: options.storage
|
|
47
|
-
} : {},
|
|
48
|
-
...options.dest ? {
|
|
49
|
-
dest: options.dest
|
|
50
|
-
} : {}
|
|
51
|
-
};
|
|
52
|
-
return multer(multerOptions);
|
|
53
|
-
}
|
|
54
|
-
__name(createMulter, "createMulter");
|
|
55
|
-
function cleanupFiles() {
|
|
56
|
-
return (req, res, next) => {
|
|
57
|
-
res.on("finish", async () => {
|
|
58
|
-
const files = [];
|
|
59
|
-
if (req.file?.path) {
|
|
60
|
-
files.push(req.file);
|
|
61
|
-
}
|
|
62
|
-
if (Array.isArray(req.files)) {
|
|
63
|
-
for (const f of req.files) {
|
|
64
|
-
if (f?.path) files.push(f);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
for (const file of files) {
|
|
68
|
-
try {
|
|
69
|
-
await unlink(file.path);
|
|
70
|
-
} catch {
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
next();
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
__name(cleanupFiles, "cleanupFiles");
|
|
78
|
-
var upload = {
|
|
79
|
-
single,
|
|
80
|
-
array,
|
|
81
|
-
none
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export {
|
|
85
|
-
cleanupFiles,
|
|
86
|
-
upload
|
|
87
|
-
};
|
|
88
|
-
//# sourceMappingURL=chunk-RFOXGJ3G.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/middleware/upload.ts"],"sourcesContent":["import { unlink } from 'node:fs/promises'\nimport type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport multer, { type Options as MulterOptions } from 'multer'\n\nexport interface UploadOptions {\n /** Max file size in bytes (default: 5MB) */\n maxSize?: number\n /** Allowed MIME types (default: all) */\n allowedTypes?: string[]\n /** Multer storage config (default: memory storage) */\n storage?: MulterOptions['storage']\n /** Multer dest for disk storage shorthand */\n dest?: string\n}\n\n/**\n * Single file upload middleware. Attaches the file to `req.file`.\n *\n * @example\n * ```ts\n * @Post('/avatar')\n * @Middleware(upload.single('avatar', { maxSize: 2 * 1024 * 1024, allowedTypes: ['image/*'] }))\n * async uploadAvatar(ctx: RequestContext) {\n * ctx.json({ filename: ctx.file.originalname })\n * }\n * ```\n */\nfunction single(fieldName: string, options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.single(fieldName) as RequestHandler\n}\n\n/**\n * Multiple file upload middleware. Attaches files to `req.files`.\n */\nfunction array(fieldName: string, maxCount = 10, options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.array(fieldName, maxCount) as RequestHandler\n}\n\n/**\n * No file upload — just parse multipart form data without file fields.\n */\nfunction none(options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.none() as RequestHandler\n}\n\nfunction createMulter(options: UploadOptions) {\n const limits: MulterOptions['limits'] = {\n fileSize: options.maxSize ?? 5 * 1024 * 1024,\n }\n\n const fileFilter: MulterOptions['fileFilter'] = options.allowedTypes\n ? (_req, file, cb) => {\n const allowed = options.allowedTypes!.some((type) => {\n if (type.endsWith('/*')) {\n return file.mimetype.startsWith(type.replace('/*', '/'))\n }\n return file.mimetype === type\n })\n if (allowed) {\n cb(null, true)\n } else {\n cb(new Error(`File type ${file.mimetype} is not allowed`))\n }\n }\n : undefined\n\n const multerOptions: MulterOptions = {\n limits,\n ...(fileFilter ? { fileFilter } : {}),\n ...(options.storage ? { storage: options.storage } : {}),\n ...(options.dest ? { dest: options.dest } : {}),\n }\n\n return multer(multerOptions)\n}\n\n/**\n * Middleware that automatically cleans up uploaded files after the response\n * is sent. Attach this AFTER your upload middleware.\n *\n * Only cleans up disk-stored files (files with a `path` property).\n *\n * @example\n * ```ts\n * middleware: [\n * upload.single('file', { dest: '/tmp/uploads' }),\n * cleanupFiles(),\n * ]\n * ```\n */\nexport function cleanupFiles() {\n return (req: Request, res: Response, next: NextFunction) => {\n res.on('finish', async () => {\n const files: any[] = []\n\n if ((req as any).file?.path) {\n files.push((req as any).file)\n }\n if (Array.isArray((req as any).files)) {\n for (const f of (req as any).files) {\n if (f?.path) files.push(f)\n }\n }\n\n for (const file of files) {\n try {\n await unlink(file.path)\n } catch {\n // File may already be moved/deleted by the handler — ignore\n }\n }\n })\n\n next()\n }\n}\n\n/** Upload middleware factory with `.single()`, `.array()`, `.none()` methods */\nexport const upload = { single, array, none }\n"],"mappings":";;;;;AAAA,SAASA,cAAc;AAEvB,OAAOC,YAA+C;AAyBtD,SAASC,OAAOC,WAAmBC,UAAyB,CAAC,GAAC;AAC5D,QAAMC,IAAIC,aAAaF,OAAAA;AACvB,SAAOC,EAAEH,OAAOC,SAAAA;AAClB;AAHSD;AAQT,SAASK,MAAMJ,WAAmBK,WAAW,IAAIJ,UAAyB,CAAC,GAAC;AAC1E,QAAMC,IAAIC,aAAaF,OAAAA;AACvB,SAAOC,EAAEE,MAAMJ,WAAWK,QAAAA;AAC5B;AAHSD;AAQT,SAASE,KAAKL,UAAyB,CAAC,GAAC;AACvC,QAAMC,IAAIC,aAAaF,OAAAA;AACvB,SAAOC,EAAEI,KAAI;AACf;AAHSA;AAKT,SAASH,aAAaF,SAAsB;AAC1C,QAAMM,SAAkC;IACtCC,UAAUP,QAAQQ,WAAW,IAAI,OAAO;EAC1C;AAEA,QAAMC,aAA0CT,QAAQU,eACpD,CAACC,MAAMC,MAAMC,OAAAA;AACX,UAAMC,UAAUd,QAAQU,aAAcK,KAAK,CAACC,SAAAA;AAC1C,UAAIA,KAAKC,SAAS,IAAA,GAAO;AACvB,eAAOL,KAAKM,SAASC,WAAWH,KAAKI,QAAQ,MAAM,GAAA,CAAA;MACrD;AACA,aAAOR,KAAKM,aAAaF;IAC3B,CAAA;AACA,QAAIF,SAAS;AACXD,SAAG,MAAM,IAAA;IACX,OAAO;AACLA,SAAG,IAAIQ,MAAM,aAAaT,KAAKM,QAAQ,iBAAiB,CAAA;IAC1D;EACF,IACAI;AAEJ,QAAMC,gBAA+B;IACnCjB;IACA,GAAIG,aAAa;MAAEA;IAAW,IAAI,CAAC;IACnC,GAAIT,QAAQwB,UAAU;MAAEA,SAASxB,QAAQwB;IAAQ,IAAI,CAAC;IACtD,GAAIxB,QAAQyB,OAAO;MAAEA,MAAMzB,QAAQyB;IAAK,IAAI,CAAC;EAC/C;AAEA,SAAOC,OAAOH,aAAAA;AAChB;AA7BSrB;AA6CF,SAASyB,eAAAA;AACd,SAAO,CAACC,KAAcC,KAAeC,SAAAA;AACnCD,QAAIE,GAAG,UAAU,YAAA;AACf,YAAMC,QAAe,CAAA;AAErB,UAAKJ,IAAYhB,MAAMqB,MAAM;AAC3BD,cAAME,KAAMN,IAAYhB,IAAI;MAC9B;AACA,UAAIuB,MAAMC,QAASR,IAAYI,KAAK,GAAG;AACrC,mBAAWK,KAAMT,IAAYI,OAAO;AAClC,cAAIK,GAAGJ,KAAMD,OAAME,KAAKG,CAAAA;QAC1B;MACF;AAEA,iBAAWzB,QAAQoB,OAAO;AACxB,YAAI;AACF,gBAAMM,OAAO1B,KAAKqB,IAAI;QACxB,QAAQ;QAER;MACF;IACF,CAAA;AAEAH,SAAAA;EACF;AACF;AAzBgBH;AA4BT,IAAMY,SAAS;EAAEzC;EAAQK;EAAOE;AAAK;","names":["unlink","multer","single","fieldName","options","m","createMulter","array","maxCount","none","limits","fileSize","maxSize","fileFilter","allowedTypes","_req","file","cb","allowed","some","type","endsWith","mimetype","startsWith","replace","Error","undefined","multerOptions","storage","dest","multer","cleanupFiles","req","res","next","on","files","path","push","Array","isArray","f","unlink","upload"]}
|
|
File without changes
|
|
File without changes
|