@lara-node/middlewares 0.1.1 → 0.1.3

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/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # @lara-node/middlewares
2
+
3
+ Predefined class-based middleware for Lara-Node applications. Every middleware is a plain class with a `handle()` method, keeping them testable and dependency-injectable.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @lara-node/middlewares
9
+ ```
10
+
11
+ ## Quick Setup
12
+
13
+ Register the core middleware stack in your Express app:
14
+
15
+ ```typescript
16
+ import express from 'express';
17
+ import {
18
+ asyncContext,
19
+ requestLogger,
20
+ validatorAttach,
21
+ responseExtender,
22
+ errorHandler,
23
+ } from '@lara-node/middlewares';
24
+
25
+ const app = express();
26
+ app.use(express.json());
27
+ app.use(asyncContext); // async context per request
28
+ app.use(requestLogger); // colored request log
29
+ app.use(validatorAttach); // attaches req.validate()
30
+ app.use(responseExtender); // adds res.jsonAsync(), auto-serializes Models
31
+
32
+ // ... routes ...
33
+
34
+ app.use(errorHandler); // 4-arg error handler (must be last)
35
+ ```
36
+
37
+ ## Middleware Reference
38
+
39
+ ### AsyncContextMiddleware
40
+
41
+ Stores the request in `AsyncLocalStorage` so it's accessible anywhere in the call stack without prop-drilling:
42
+
43
+ ```typescript
44
+ import { asyncLocalStorage } from '@lara-node/middlewares';
45
+
46
+ // Anywhere in the request lifecycle:
47
+ const store = asyncLocalStorage.getStore(); // { req, user? }
48
+ ```
49
+
50
+ ### RequestLoggerMiddleware
51
+
52
+ Logs `METHOD PATH STATUS TIME IP [user:id]` to stdout on response finish. Color-coded: green (2xx), yellow (4xx), red (5xx).
53
+
54
+ ### ValidatorMiddleware
55
+
56
+ Attaches `req.validate()` to every request:
57
+
58
+ ```typescript
59
+ // In any controller:
60
+ const data = await req.validate({
61
+ name: 'required|string|min:2|max:100',
62
+ email: 'required|email',
63
+ age: 'required|integer|min:18',
64
+ });
65
+ ```
66
+
67
+ Throws `ValidationError` (caught by `ErrorHandlerMiddleware` → HTTP 422) on failure.
68
+
69
+ ### ResponseExtenderMiddleware
70
+
71
+ Adds `res.jsonAsync()` and overrides `res.json()` to automatically call `model.toJSONAsync()` on `@lara-node/db` Model instances (handles hidden fields, casts, relations):
72
+
73
+ ```typescript
74
+ // All equivalent — Model is auto-serialized:
75
+ res.json(user);
76
+ res.json([user1, user2]);
77
+ res.json(await User.paginate(15, 1)); // QueryResult
78
+ await res.jsonAsync(user);
79
+ ```
80
+
81
+ ### AuthMiddleware
82
+
83
+ JWT authentication. Verifies `Authorization: Bearer <token>` and optionally loads the user from the database:
84
+
85
+ ```typescript
86
+ import { AuthMiddleware } from '@lara-node/middlewares';
87
+
88
+ const auth = new AuthMiddleware({
89
+ // Optional: load full user from DB (adds req.user with roles/permissions)
90
+ userLoader: async (uid) => {
91
+ const user = await User.with(['roles', 'roles.permissions']).find(uid);
92
+ if (!user) return null;
93
+ return {
94
+ id: user.id,
95
+ roles: user.roles.map(r => r.slug),
96
+ permissions: user.roles.flatMap(r => r.permissions.map(p => p.slug)),
97
+ };
98
+ },
99
+ // Optional: decrypt token before verification
100
+ decryptToken: (token) => myDecrypt(token),
101
+ }).toHandler();
102
+
103
+ app.use('/api/protected', auth);
104
+ ```
105
+
106
+ If no `userLoader` is provided, `req.user` is populated from the JWT payload (`{ id: sub, roles, permissions }`).
107
+
108
+ ### AuthorizeByStatusMiddleware
109
+
110
+ Rejects requests where `req.user.status !== 'active'` or `req.user.isActive()` returns false:
111
+
112
+ ```typescript
113
+ import { authorizeByStatus } from '@lara-node/middlewares';
114
+
115
+ app.use('/api/protected', auth, authorizeByStatus);
116
+ ```
117
+
118
+ ### ErrorHandlerMiddleware
119
+
120
+ Express 4-argument error handler. Handles:
121
+ - `ValidationError` → 422 with `{ success: false, errors, messages }`
122
+ - Any `err.status` (400–599) → that status code
123
+ - Everything else → 500
124
+ - `err.stack` included in non-production responses
125
+
126
+ ```typescript
127
+ import { errorHandler } from '@lara-node/middlewares';
128
+ app.use(errorHandler); // must be registered last
129
+ ```
130
+
131
+ ## Authorization Helpers
132
+
133
+ ```typescript
134
+ import { authorizeRoles, authorizePermissions } from '@lara-node/middlewares';
135
+
136
+ // Allow only admin or moderator:
137
+ router.get('/admin', auth, authorizeRoles('admin', 'moderator'), handler);
138
+
139
+ // Require a specific permission:
140
+ router.delete('/users/:id', auth, authorizePermissions('delete_users'), handler);
141
+ ```
142
+
143
+ ## Class-Based Usage
144
+
145
+ All middleware can also be used as classes (useful for testing or custom wiring):
146
+
147
+ ```typescript
148
+ import { AuthMiddleware } from '@lara-node/middlewares';
149
+
150
+ // Class form:
151
+ const authMiddleware = new AuthMiddleware({ userLoader });
152
+ app.use(authMiddleware.handle.bind(authMiddleware));
153
+ // or:
154
+ app.use(authMiddleware.toHandler());
155
+ ```
156
+
157
+ ## Express Type Augmentation
158
+
159
+ This package augments the Express `Request` and `Response` interfaces globally:
160
+
161
+ ```typescript
162
+ // req.user is typed:
163
+ req.user?.id // number | string
164
+ req.user?.roles // string[]
165
+ req.user?.permissions // string[]
166
+
167
+ // req.validate is typed:
168
+ const data = await req.validate<{ email: string }>(rules);
169
+
170
+ // res.jsonAsync is typed:
171
+ await res.jsonAsync(model);
172
+ ```
173
+
174
+ You can also declare these locally in your project via `src/types/express.d.ts` — the `create-lara-node` scaffold generates this file automatically.
175
+
176
+ ## Writing Custom Middleware
177
+
178
+ Follow the same class pattern:
179
+
180
+ ```typescript
181
+ import { Request, Response, NextFunction } from 'express';
182
+
183
+ export class MaintenanceModeMiddleware {
184
+ handle(req: Request, res: Response, next: NextFunction): void {
185
+ if (process.env.MAINTENANCE === 'true') {
186
+ res.status(503).json({ message: 'Service temporarily unavailable.' });
187
+ return;
188
+ }
189
+ next();
190
+ }
191
+
192
+ toHandler() {
193
+ return (req: Request, res: Response, next: NextFunction) =>
194
+ this.handle(req, res, next);
195
+ }
196
+ }
197
+ ```
198
+
199
+ ## Async Context Store
200
+
201
+ The store is populated by `AsyncContextMiddleware` and augmented by `AuthMiddleware`:
202
+
203
+ ```typescript
204
+ import { asyncLocalStorage } from '@lara-node/middlewares';
205
+
206
+ function getCurrentUser() {
207
+ return asyncLocalStorage.getStore()?.user ?? null;
208
+ }
209
+
210
+ function getCurrentRequest() {
211
+ return asyncLocalStorage.getStore()?.req;
212
+ }
213
+ ```
214
+
215
+ ## License
216
+
217
+ MIT
@@ -24,6 +24,7 @@ interface AuthMiddlewareOptions {
24
24
  id: number | string;
25
25
  roles?: string[];
26
26
  permissions?: string[];
27
+ [key: string]: any;
27
28
  } | null>;
28
29
  decryptToken?: (token: string) => string;
29
30
  }
@@ -59,4 +60,4 @@ declare const errorHandler: (err: any, req: Request, res: Response, next: NextFu
59
60
 
60
61
  //#endregion
61
62
  export { AsyncContextMiddleware, AuthMiddleware, AuthMiddlewareOptions, AuthorizeByStatusMiddleware, ErrorHandlerMiddleware, RequestLoggerMiddleware, ResponseExtenderMiddleware, ValidatorMiddleware, asyncContext, asyncContextMiddleware, asyncLocalStorage, authorizeByStatus, authorizeByStatusMiddleware, authorizePermissions, authorizeRoles, errorHandler, errorHandlerMiddleware, requestLogger, requestLoggerMiddleware, responseExtender, responseExtenderMiddleware, validatorAttach, validatorMiddleware };
62
- //# sourceMappingURL=index-B9p8lFgD.d.ts.map
63
+ //# sourceMappingURL=index-CC42rCIZ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-CC42rCIZ.d.ts","names":[],"sources":["../src/index.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { AsyncLocalStorage } from "async_hooks";
2
2
  import jwt from "jsonwebtoken";
3
3
  import { ValidationError, validate } from "@lara-node/validator";
4
+ import { Model } from "@lara-node/db";
4
5
  //#region src/index.ts
5
6
  const asyncLocalStorage = new AsyncLocalStorage();
6
7
  var AsyncContextMiddleware = class {
@@ -58,21 +59,45 @@ async function serializeItem(item) {
58
59
  if (item && typeof item.toJSONAsync === "function") return await item.toJSONAsync();
59
60
  return item;
60
61
  }
62
+ function containsModels(obj, visited = /* @__PURE__ */ new WeakSet()) {
63
+ if (obj === null || obj === void 0) return false;
64
+ const isObj = typeof obj === "object";
65
+ if (isObj && visited.has(obj)) return false;
66
+ if (isObj) visited.add(obj);
67
+ if (obj instanceof Model) return true;
68
+ if (Array.isArray(obj)) return obj.some((item) => containsModels(item, visited));
69
+ if (isQueryResult(obj)) return obj.data.some((item) => containsModels(item, visited));
70
+ if (isObj) return Object.values(obj).some((val) => containsModels(val, visited));
71
+ return false;
72
+ }
61
73
  var ResponseExtenderMiddleware = class {
62
74
  handle(_req, res, next) {
63
75
  const originalJson = res.json.bind(res);
64
76
  res.jsonAsync = async function(data) {
65
- if (data && typeof data.toJSONAsync === "function") return originalJson(await data.toJSONAsync());
66
- if (Array.isArray(data)) return originalJson(await Promise.all(data.map(serializeItem)));
77
+ if (data instanceof Model) return originalJson(await data.toJSONAsync());
78
+ if (Array.isArray(data)) {
79
+ if (data.length === 0) return originalJson(data);
80
+ return originalJson(await Promise.all(data.map(serializeItem)));
81
+ }
67
82
  if (isQueryResult(data)) {
68
- const processed = await Promise.all(data.data.map(serializeItem));
69
- return originalJson({
70
- ...data,
71
- data: processed
72
- });
83
+ if (data.data.length > 0) {
84
+ const processed = await Promise.all(data.data.map(serializeItem));
85
+ return originalJson({
86
+ ...data,
87
+ data: processed
88
+ });
89
+ }
90
+ return originalJson(data);
73
91
  }
74
92
  return originalJson(data);
75
93
  };
94
+ res.json = function(data) {
95
+ if (data instanceof Model) return res.jsonAsync(data);
96
+ if (Array.isArray(data) && data.length > 0 && data.some((item) => item instanceof Model)) return res.jsonAsync(data);
97
+ if (isQueryResult(data)) return res.jsonAsync(data);
98
+ if (containsModels(data)) return res.jsonAsync(data);
99
+ return originalJson(data);
100
+ }.bind(res);
76
101
  next();
77
102
  }
78
103
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"async_hooks\";\nimport { Request, Response, NextFunction } from \"express\";\nimport jwt from \"jsonwebtoken\";\nimport { ValidationError, validate, RuleFn, RuleSpec } from \"@lara-node/validator\";\n\n// ─── Shared async context storage ──────────────────────────────────────────────\n\nexport const asyncLocalStorage = new AsyncLocalStorage<Record<string, any>>();\n\n// ─── Type augmentations ─────────────────────────────────────────────────────────\n\ndeclare global {\n namespace Express {\n interface Request {\n user?: {\n id: number | string;\n roles?: string[];\n permissions?: string[];\n };\n validate: <T extends Record<string, any>>(\n payloadOrRules?: any,\n rulesMaybe?: Record<string, RuleSpec> | Record<string, string | RuleFn>,\n customMessages?: Record<string, string>,\n ) => Promise<T>;\n }\n interface Response {\n jsonAsync: <T>(data: T) => Promise<Response>;\n }\n }\n}\n\n// ─── AsyncContextMiddleware ─────────────────────────────────────────────────────\n\nexport class AsyncContextMiddleware {\n handle(req: Request, _res: Response, next: NextFunction): void {\n asyncLocalStorage.run({ req }, () => next());\n }\n}\n\n// ─── RequestLoggerMiddleware ────────────────────────────────────────────────────\n\nexport class RequestLoggerMiddleware {\n handle(req: Request, res: Response, next: NextFunction): void {\n const start = process.hrtime();\n const { method, originalUrl } = req;\n const ip = (req.ip || req.headers[\"x-forwarded-for\"] || (req.socket && req.socket.remoteAddress)) as string | undefined;\n\n res.on(\"finish\", () => {\n const [sec, nano] = process.hrtime(start);\n const ms = (sec * 1e3 + nano / 1e6).toFixed(2);\n const status = res.statusCode;\n const reset = \"\\x1b[0m\";\n let color = \"\\x1b[32m\";\n if (status >= 500) color = \"\\x1b[31m\";\n else if (status >= 400) color = \"\\x1b[33m\";\n const maybeUser = (req as any).user;\n const userInfo = maybeUser ? ` - user:${maybeUser.id ?? maybeUser.email ?? JSON.stringify(maybeUser)}` : \"\";\n const query = req.query && Object.keys(req.query).length ? ` query=${JSON.stringify(req.query)}` : \"\";\n const params = req.params && Object.keys(req.params).length ? ` params=${JSON.stringify(req.params)}` : \"\";\n console.log(`${method} ${originalUrl} ${color}${status}${reset} - ${ms} ms - ${ip || \"-\"}${userInfo}${query}${params}`);\n });\n\n next();\n }\n}\n\n// ─── ValidatorMiddleware ────────────────────────────────────────────────────────\n\nexport class ValidatorMiddleware {\n handle(req: Request & { validate?: any }, _res: Response, next: NextFunction): void {\n req.validate = async function <T extends Record<string, any>>(\n payloadOrRules: any,\n maybeRules?: any,\n customMessages?: Record<string, string>,\n ): Promise<T> {\n let payload: any;\n let rules: any;\n\n if (\n maybeRules === undefined &&\n typeof payloadOrRules === \"object\" &&\n !Array.isArray(payloadOrRules) &&\n Object.keys(payloadOrRules || {}).length &&\n Object.values(payloadOrRules).every(\n (v) => typeof v === \"string\" || typeof v === \"function\" || (typeof v === \"object\" && v && \"rule\" in (v as any)),\n )\n ) {\n rules = payloadOrRules;\n payload = (req as any).body?.payload ?? (req as any).body;\n } else if (maybeRules !== undefined) {\n payload = payloadOrRules;\n rules = maybeRules;\n } else {\n payload = payloadOrRules ?? ((req as any).body?.payload ?? (req as any).body);\n rules = maybeRules;\n }\n\n if (!rules) throw new Error(\"No validation rules provided\");\n return await validate<T>(payload, rules, customMessages);\n };\n\n next();\n }\n}\n\n// ─── ResponseExtenderMiddleware ─────────────────────────────────────────────────\n\nfunction isQueryResult(obj: any): boolean {\n return obj && typeof obj === \"object\" && Array.isArray(obj.data);\n}\n\nasync function serializeItem(item: any): Promise<any> {\n if (item && typeof item.toJSONAsync === \"function\") return await item.toJSONAsync();\n return item;\n}\n\nexport class ResponseExtenderMiddleware {\n handle(_req: Request, res: Response, next: NextFunction): void {\n const originalJson = res.json.bind(res);\n\n res.jsonAsync = async function <T>(data: T): Promise<Response> {\n if (data && typeof (data as any).toJSONAsync === \"function\") {\n return originalJson(await (data as any).toJSONAsync());\n }\n if (Array.isArray(data)) {\n return originalJson(await Promise.all(data.map(serializeItem)));\n }\n if (isQueryResult(data)) {\n const processed = await Promise.all((data as any).data.map(serializeItem));\n return originalJson({ ...(data as any), data: processed });\n }\n return originalJson(data);\n };\n\n next();\n }\n}\n\n// ─── AuthMiddleware ─────────────────────────────────────────────────────────────\n\nexport interface AuthMiddlewareOptions {\n userLoader?: (uid: string | number) => Promise<{ id: number | string; roles?: string[]; permissions?: string[] } | null>;\n decryptToken?: (token: string) => string;\n}\n\nexport class AuthMiddleware {\n private userLoader?: (uid: string | number) => Promise<any | null>;\n private decryptFn?: (token: string) => string;\n\n constructor(options: AuthMiddlewareOptions = {}) {\n this.userLoader = options.userLoader;\n this.decryptFn = options.decryptToken;\n }\n\n async handle(req: Request, res: Response, next: NextFunction): Promise<void> {\n const header = req.headers[\"authorization\"] || \"\";\n const token = header.startsWith(\"Bearer \") ? header.slice(7) : \"\";\n\n if (!token) {\n res.status(401).json({ message: \"Unauthorized\" });\n return;\n }\n\n try {\n const JWT_SECRET = process.env.JWT_SECRET || \"dev-secret-change\";\n const rawToken = this.decryptFn ? this.decryptFn(token) : token;\n const decoded = jwt.verify(rawToken, JWT_SECRET) as any;\n const uid = decoded.sub;\n\n if (this.userLoader) {\n const user = await this.userLoader(uid);\n if (!user) { res.status(401).json({ message: \"Unauthorized\" }); return; }\n req.user = { id: user.id, roles: user.roles, permissions: user.permissions };\n const store = asyncLocalStorage.getStore();\n if (store) store.user = user;\n } else {\n req.user = { id: uid, roles: decoded.roles, permissions: decoded.permissions };\n }\n\n next();\n } catch {\n res.status(401).json({ message: \"Unauthorized\" });\n }\n }\n\n toHandler(): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return this.handle.bind(this);\n }\n}\n\n// ─── AuthorizeByStatusMiddleware ────────────────────────────────────────────────\n\nexport class AuthorizeByStatusMiddleware {\n handle(req: Request, res: Response, next: NextFunction): void {\n const user = req.user as any;\n if (!user) { res.status(401).json({ message: \"Unauthorized\" }); return; }\n if (typeof user.isActive === \"function\" && !user.isActive()) {\n res.status(401).json({ message: \"Account Inactive\" });\n return;\n }\n if (user.status && user.status !== \"active\") {\n res.status(401).json({ message: \"Account Inactive\" });\n return;\n }\n next();\n }\n}\n\n// ─── ErrorHandlerMiddleware ─────────────────────────────────────────────────────\n\nexport class ErrorHandlerMiddleware {\n handle(err: any, _req: Request, res: Response, _next: NextFunction): void {\n if (res.headersSent) return;\n\n if (err instanceof ValidationError) {\n res.status(422).json({ success: false, errors: err.errors, messages: err.messages, message: err.message });\n return;\n }\n\n const status =\n typeof err.status === \"number\" && err.status >= 400 && err.status < 600 ? err.status : 500;\n const message = err.message || \"Internal Server Error\";\n const payload: any = { success: false, message };\n if (err.code) payload.code = err.code;\n if (err.errors && typeof err.errors === \"object\") payload.errors = err.errors;\n if (process.env.NODE_ENV !== \"production\" && err.stack)\n payload.stack = err.stack.split(\"\\n\").map((l: string) => l.trim());\n\n res.status(status).json(payload);\n }\n\n toHandler(): (err: any, req: Request, res: Response, next: NextFunction) => void {\n return this.handle.bind(this);\n }\n}\n\n// ─── Authorization helpers ──────────────────────────────────────────────────────\n\nexport function authorizeRoles(...roles: string[]) {\n return (req: Request, res: Response, next: NextFunction): void => {\n const userRoles = req.user?.roles || [];\n if (!roles.some((r) => userRoles.includes(r))) {\n res.status(403).json({ message: \"Forbidden\" });\n return;\n }\n next();\n };\n}\n\nexport function authorizePermissions(...perms: string[]) {\n return (req: Request, res: Response, next: NextFunction): void => {\n const userPerms = req.user?.permissions || [];\n if (!perms.some((p) => userPerms.includes(p))) {\n res.status(403).json({ message: \"Forbidden\" });\n return;\n }\n next();\n };\n}\n\n// ─── Singleton instances & function-form exports ────────────────────────────────\n\nexport const asyncContextMiddleware = new AsyncContextMiddleware();\nexport const requestLoggerMiddleware = new RequestLoggerMiddleware();\nexport const validatorMiddleware = new ValidatorMiddleware();\nexport const responseExtenderMiddleware = new ResponseExtenderMiddleware();\nexport const authorizeByStatusMiddleware = new AuthorizeByStatusMiddleware();\nexport const errorHandlerMiddleware = new ErrorHandlerMiddleware();\n\nexport const asyncContext = (req: Request, res: Response, next: NextFunction) =>\n asyncContextMiddleware.handle(req, res, next);\nexport const requestLogger = (req: Request, res: Response, next: NextFunction) =>\n requestLoggerMiddleware.handle(req, res, next);\nexport const validatorAttach = (req: Request, res: Response, next: NextFunction) =>\n validatorMiddleware.handle(req, res, next);\nexport const responseExtender = (req: Request, res: Response, next: NextFunction) =>\n responseExtenderMiddleware.handle(req, res, next);\nexport const authorizeByStatus = (req: Request, res: Response, next: NextFunction) =>\n authorizeByStatusMiddleware.handle(req, res, next);\nexport const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) =>\n errorHandlerMiddleware.handle(err, req, res, next);\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"async_hooks\";\nimport { Request, Response, NextFunction } from \"express\";\nimport jwt from \"jsonwebtoken\";\nimport { ValidationError, validate, RuleFn, RuleSpec } from \"@lara-node/validator\";\nimport { Model, QueryResult } from \"@lara-node/db\";\n\n// ─── Shared async context storage ──────────────────────────────────────────────\n\nexport const asyncLocalStorage = new AsyncLocalStorage<Record<string, any>>();\n\n// ─── Type augmentations ─────────────────────────────────────────────────────────\n\ndeclare global {\n namespace Express {\n interface Request {\n user?: {\n id: number | string;\n email?: string;\n name?: string;\n roles?: string[];\n permissions?: string[];\n [key: string]: any;\n };\n validate: <T extends Record<string, any>>(\n payloadOrRules?: any,\n rulesMaybe?: Record<string, RuleSpec> | Record<string, string | RuleFn>,\n customMessages?: Record<string, string>,\n ) => Promise<T>;\n }\n interface Response {\n jsonAsync: <T>(data: T) => Promise<Response>;\n }\n }\n}\n\n// ─── AsyncContextMiddleware ─────────────────────────────────────────────────────\n\nexport class AsyncContextMiddleware {\n handle(req: Request, _res: Response, next: NextFunction): void {\n asyncLocalStorage.run({ req }, () => next());\n }\n}\n\n// ─── RequestLoggerMiddleware ────────────────────────────────────────────────────\n\nexport class RequestLoggerMiddleware {\n handle(req: Request, res: Response, next: NextFunction): void {\n const start = process.hrtime();\n const { method, originalUrl } = req;\n const ip = (req.ip || req.headers[\"x-forwarded-for\"] || (req.socket && req.socket.remoteAddress)) as string | undefined;\n\n res.on(\"finish\", () => {\n const [sec, nano] = process.hrtime(start);\n const ms = (sec * 1e3 + nano / 1e6).toFixed(2);\n const status = res.statusCode;\n const reset = \"\\x1b[0m\";\n let color = \"\\x1b[32m\";\n if (status >= 500) color = \"\\x1b[31m\";\n else if (status >= 400) color = \"\\x1b[33m\";\n const maybeUser = (req as any).user;\n const userInfo = maybeUser ? ` - user:${maybeUser.id ?? maybeUser.email ?? JSON.stringify(maybeUser)}` : \"\";\n const query = req.query && Object.keys(req.query).length ? ` query=${JSON.stringify(req.query)}` : \"\";\n const params = req.params && Object.keys(req.params).length ? ` params=${JSON.stringify(req.params)}` : \"\";\n console.log(`${method} ${originalUrl} ${color}${status}${reset} - ${ms} ms - ${ip || \"-\"}${userInfo}${query}${params}`);\n });\n\n next();\n }\n}\n\n// ─── ValidatorMiddleware ────────────────────────────────────────────────────────\n\nexport class ValidatorMiddleware {\n handle(req: Request & { validate?: any }, _res: Response, next: NextFunction): void {\n req.validate = async function <T extends Record<string, any>>(\n payloadOrRules: any,\n maybeRules?: any,\n customMessages?: Record<string, string>,\n ): Promise<T> {\n let payload: any;\n let rules: any;\n\n if (\n maybeRules === undefined &&\n typeof payloadOrRules === \"object\" &&\n !Array.isArray(payloadOrRules) &&\n Object.keys(payloadOrRules || {}).length &&\n Object.values(payloadOrRules).every(\n (v) => typeof v === \"string\" || typeof v === \"function\" || (typeof v === \"object\" && v && \"rule\" in (v as any)),\n )\n ) {\n rules = payloadOrRules;\n payload = (req as any).body?.payload ?? (req as any).body;\n } else if (maybeRules !== undefined) {\n payload = payloadOrRules;\n rules = maybeRules;\n } else {\n payload = payloadOrRules ?? ((req as any).body?.payload ?? (req as any).body);\n rules = maybeRules;\n }\n\n if (!rules) throw new Error(\"No validation rules provided\");\n return await validate<T>(payload, rules, customMessages);\n };\n\n next();\n }\n}\n\n// ─── ResponseExtenderMiddleware ─────────────────────────────────────────────────\n\nfunction isQueryResult(obj: any): obj is QueryResult<any> {\n return obj && typeof obj === \"object\" && Array.isArray(obj.data);\n}\n\nasync function serializeItem(item: any): Promise<any> {\n if (item && typeof item.toJSONAsync === \"function\") return await item.toJSONAsync();\n return item;\n}\n\nfunction containsModels(obj: any, visited = new WeakSet()): boolean {\n if (obj === null || obj === undefined) return false;\n const isObj = typeof obj === \"object\";\n if (isObj && visited.has(obj)) return false;\n if (isObj) visited.add(obj);\n if (obj instanceof Model) return true;\n if (Array.isArray(obj)) return obj.some((item) => containsModels(item, visited));\n if (isQueryResult(obj)) return obj.data.some((item) => containsModels(item, visited));\n if (isObj) return Object.values(obj).some((val) => containsModels(val, visited));\n return false;\n}\n\nexport class ResponseExtenderMiddleware {\n handle(_req: Request, res: Response, next: NextFunction): void {\n const originalJson = res.json.bind(res);\n\n res.jsonAsync = async function <T>(data: T): Promise<Response> {\n if (data instanceof Model) return originalJson(await (data as any).toJSONAsync());\n if (Array.isArray(data)) {\n if (data.length === 0) return originalJson(data);\n return originalJson(await Promise.all(data.map(serializeItem)));\n }\n if (isQueryResult(data)) {\n if ((data as any).data.length > 0) {\n const processed = await Promise.all((data as any).data.map(serializeItem));\n return originalJson({ ...(data as any), data: processed });\n }\n return originalJson(data);\n }\n return originalJson(data);\n };\n\n res.json = function <T>(this: Response, data?: T): Response {\n if (data instanceof Model) return (res as any).jsonAsync(data);\n if (Array.isArray(data) && data.length > 0 && data.some((item: any) => item instanceof Model))\n return (res as any).jsonAsync(data);\n if (isQueryResult(data)) return (res as any).jsonAsync(data);\n if (containsModels(data)) return (res as any).jsonAsync(data);\n return originalJson(data);\n }.bind(res);\n\n next();\n }\n}\n\n// ─── AuthMiddleware ─────────────────────────────────────────────────────────────\n\nexport interface AuthMiddlewareOptions {\n userLoader?: (uid: string | number) => Promise<{ id: number | string; roles?: string[]; permissions?: string[]; [key: string]: any } | null>;\n decryptToken?: (token: string) => string;\n}\n\nexport class AuthMiddleware {\n private userLoader?: (uid: string | number) => Promise<any | null>;\n private decryptFn?: (token: string) => string;\n\n constructor(options: AuthMiddlewareOptions = {}) {\n this.userLoader = options.userLoader;\n this.decryptFn = options.decryptToken;\n }\n\n async handle(req: Request, res: Response, next: NextFunction): Promise<void> {\n const header = req.headers[\"authorization\"] || \"\";\n const token = header.startsWith(\"Bearer \") ? header.slice(7) : \"\";\n\n if (!token) {\n res.status(401).json({ message: \"Unauthorized\" });\n return;\n }\n\n try {\n const JWT_SECRET = process.env.JWT_SECRET || \"dev-secret-change\";\n const rawToken = this.decryptFn ? this.decryptFn(token) : token;\n const decoded = jwt.verify(rawToken, JWT_SECRET) as any;\n const uid = decoded.sub;\n\n if (this.userLoader) {\n const user = await this.userLoader(uid);\n if (!user) { res.status(401).json({ message: \"Unauthorized\" }); return; }\n req.user = { id: user.id, roles: user.roles, permissions: user.permissions };\n const store = asyncLocalStorage.getStore();\n if (store) store.user = user;\n } else {\n req.user = { id: uid, roles: decoded.roles, permissions: decoded.permissions };\n }\n\n next();\n } catch {\n res.status(401).json({ message: \"Unauthorized\" });\n }\n }\n\n toHandler(): (req: Request, res: Response, next: NextFunction) => Promise<void> {\n return this.handle.bind(this);\n }\n}\n\n// ─── AuthorizeByStatusMiddleware ────────────────────────────────────────────────\n\nexport class AuthorizeByStatusMiddleware {\n handle(req: Request, res: Response, next: NextFunction): void {\n const user = req.user as any;\n if (!user) { res.status(401).json({ message: \"Unauthorized\" }); return; }\n if (typeof user.isActive === \"function\" && !user.isActive()) {\n res.status(401).json({ message: \"Account Inactive\" }); return;\n }\n if (user.status && user.status !== \"active\") {\n res.status(401).json({ message: \"Account Inactive\" }); return;\n }\n next();\n }\n}\n\n// ─── ErrorHandlerMiddleware ─────────────────────────────────────────────────────\n\nexport class ErrorHandlerMiddleware {\n handle(err: any, _req: Request, res: Response, _next: NextFunction): void {\n if (res.headersSent) return;\n\n if (err instanceof ValidationError) {\n res.status(422).json({ success: false, errors: err.errors, messages: err.messages, message: err.message });\n return;\n }\n\n const status =\n typeof err.status === \"number\" && err.status >= 400 && err.status < 600 ? err.status : 500;\n const message = err.message || \"Internal Server Error\";\n const payload: any = { success: false, message };\n if (err.code) payload.code = err.code;\n if (err.errors && typeof err.errors === \"object\") payload.errors = err.errors;\n if (process.env.NODE_ENV !== \"production\" && err.stack)\n payload.stack = err.stack.split(\"\\n\").map((l: string) => l.trim());\n\n res.status(status).json(payload);\n }\n\n toHandler(): (err: any, req: Request, res: Response, next: NextFunction) => void {\n return this.handle.bind(this);\n }\n}\n\n// ─── Authorization helpers ──────────────────────────────────────────────────────\n\nexport function authorizeRoles(...roles: string[]) {\n return (req: Request, res: Response, next: NextFunction): void => {\n const userRoles = req.user?.roles || [];\n if (!roles.some((r) => userRoles.includes(r))) {\n res.status(403).json({ message: \"Forbidden\" }); return;\n }\n next();\n };\n}\n\nexport function authorizePermissions(...perms: string[]) {\n return (req: Request, res: Response, next: NextFunction): void => {\n const userPerms = req.user?.permissions || [];\n if (!perms.some((p) => userPerms.includes(p))) {\n res.status(403).json({ message: \"Forbidden\" }); return;\n }\n next();\n };\n}\n\n// ─── Singleton instances & function-form exports ────────────────────────────────\n\nexport const asyncContextMiddleware = new AsyncContextMiddleware();\nexport const requestLoggerMiddleware = new RequestLoggerMiddleware();\nexport const validatorMiddleware = new ValidatorMiddleware();\nexport const responseExtenderMiddleware = new ResponseExtenderMiddleware();\nexport const authorizeByStatusMiddleware = new AuthorizeByStatusMiddleware();\nexport const errorHandlerMiddleware = new ErrorHandlerMiddleware();\n\nexport const asyncContext = (req: Request, res: Response, next: NextFunction) =>\n asyncContextMiddleware.handle(req, res, next);\nexport const requestLogger = (req: Request, res: Response, next: NextFunction) =>\n requestLoggerMiddleware.handle(req, res, next);\nexport const validatorAttach = (req: Request, res: Response, next: NextFunction) =>\n validatorMiddleware.handle(req, res, next);\nexport const responseExtender = (req: Request, res: Response, next: NextFunction) =>\n responseExtenderMiddleware.handle(req, res, next);\nexport const authorizeByStatus = (req: Request, res: Response, next: NextFunction) =>\n authorizeByStatusMiddleware.handle(req, res, next);\nexport const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) =>\n errorHandlerMiddleware.handle(err, req, res, next);\n"],"mappings":";;;;;AAQA,MAAa,oBAAoB,IAAI,kBAAuC;AA6B5E,IAAa,yBAAb,MAAoC;CAClC,OAAO,KAAc,MAAgB,MAA0B;EAC7D,kBAAkB,IAAI,EAAE,IAAI,SAAS,KAAK,CAAC;CAC7C;AACF;AAIA,IAAa,0BAAb,MAAqC;CACnC,OAAO,KAAc,KAAe,MAA0B;EAC5D,MAAM,QAAQ,QAAQ,OAAO;EAC7B,MAAM,EAAE,QAAQ,gBAAgB;EAChC,MAAM,KAAM,IAAI,MAAM,IAAI,QAAQ,sBAAuB,IAAI,UAAU,IAAI,OAAO;EAElF,IAAI,GAAG,gBAAgB;GACrB,MAAM,CAAC,KAAK,QAAQ,QAAQ,OAAO,KAAK;GACxC,MAAM,MAAM,MAAM,MAAM,OAAO,KAAK,QAAQ,CAAC;GAC7C,MAAM,SAAS,IAAI;GACnB,MAAM,QAAQ;GACd,IAAI,QAAQ;GACZ,IAAI,UAAU,KAAK,QAAQ;QACtB,IAAI,UAAU,KAAK,QAAQ;GAChC,MAAM,YAAa,IAAY;GAC/B,MAAM,WAAW,YAAY,WAAW,UAAU,MAAM,UAAU,SAAS,KAAK,UAAU,SAAS,MAAM;GACzG,MAAM,QAAQ,IAAI,SAAS,OAAO,KAAK,IAAI,KAAK,EAAE,SAAS,UAAU,KAAK,UAAU,IAAI,KAAK,MAAM;GACnG,MAAM,SAAS,IAAI,UAAU,OAAO,KAAK,IAAI,MAAM,EAAE,SAAS,WAAW,KAAK,UAAU,IAAI,MAAM,MAAM;GACxG,QAAQ,IAAI,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,SAAS,MAAM,KAAK,GAAG,QAAQ,MAAM,MAAM,WAAW,QAAQ,QAAQ;EACxH,CAAC;EAED,KAAK;CACP;AACF;AAIA,IAAa,sBAAb,MAAiC;CAC/B,OAAO,KAAmC,MAAgB,MAA0B;EAClF,IAAI,WAAW,eACb,gBACA,YACA,gBACY;GACZ,IAAI;GACJ,IAAI;GAEJ,IACE,eAAe,KAAA,KACf,OAAO,mBAAmB,YAC1B,CAAC,MAAM,QAAQ,cAAc,KAC7B,OAAO,KAAK,kBAAkB,CAAC,CAAC,EAAE,UAClC,OAAO,OAAO,cAAc,EAAE,OAC3B,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,cAAe,OAAO,MAAM,YAAY,KAAK,UAAW,CACvG,GACA;IACA,QAAQ;IACR,UAAW,IAAY,MAAM,WAAY,IAAY;GACvD,OAAO,IAAI,eAAe,KAAA,GAAW;IACnC,UAAU;IACV,QAAQ;GACV,OAAO;IACL,UAAU,kBAAoB,IAAY,MAAM,WAAY,IAAY;IACxE,QAAQ;GACV;GAEA,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,8BAA8B;GAC1D,OAAO,MAAM,SAAY,SAAS,OAAO,cAAc;EACzD;EAEA,KAAK;CACP;AACF;AAIA,SAAS,cAAc,KAAmC;CACxD,OAAO,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,IAAI;AACjE;AAEA,eAAe,cAAc,MAAyB;CACpD,IAAI,QAAQ,OAAO,KAAK,gBAAgB,YAAY,OAAO,MAAM,KAAK,YAAY;CAClF,OAAO;AACT;AAEA,SAAS,eAAe,KAAU,0BAAU,IAAI,QAAQ,GAAY;CAClE,IAAI,QAAQ,QAAQ,QAAQ,KAAA,GAAW,OAAO;CAC9C,MAAM,QAAQ,OAAO,QAAQ;CAC7B,IAAI,SAAS,QAAQ,IAAI,GAAG,GAAG,OAAO;CACtC,IAAI,OAAO,QAAQ,IAAI,GAAG;CAC1B,IAAI,eAAe,OAAO,OAAO;CACjC,IAAI,MAAM,QAAQ,GAAG,GAAG,OAAO,IAAI,MAAM,SAAS,eAAe,MAAM,OAAO,CAAC;CAC/E,IAAI,cAAc,GAAG,GAAG,OAAO,IAAI,KAAK,MAAM,SAAS,eAAe,MAAM,OAAO,CAAC;CACpF,IAAI,OAAO,OAAO,OAAO,OAAO,GAAG,EAAE,MAAM,QAAQ,eAAe,KAAK,OAAO,CAAC;CAC/E,OAAO;AACT;AAEA,IAAa,6BAAb,MAAwC;CACtC,OAAO,MAAe,KAAe,MAA0B;EAC7D,MAAM,eAAe,IAAI,KAAK,KAAK,GAAG;EAEtC,IAAI,YAAY,eAAmB,MAA4B;GAC7D,IAAI,gBAAgB,OAAO,OAAO,aAAa,MAAO,KAAa,YAAY,CAAC;GAChF,IAAI,MAAM,QAAQ,IAAI,GAAG;IACvB,IAAI,KAAK,WAAW,GAAG,OAAO,aAAa,IAAI;IAC/C,OAAO,aAAa,MAAM,QAAQ,IAAI,KAAK,IAAI,aAAa,CAAC,CAAC;GAChE;GACA,IAAI,cAAc,IAAI,GAAG;IACvB,IAAK,KAAa,KAAK,SAAS,GAAG;KACjC,MAAM,YAAY,MAAM,QAAQ,IAAK,KAAa,KAAK,IAAI,aAAa,CAAC;KACzE,OAAO,aAAa;MAAE,GAAI;MAAc,MAAM;KAAU,CAAC;IAC3D;IACA,OAAO,aAAa,IAAI;GAC1B;GACA,OAAO,aAAa,IAAI;EAC1B;EAEA,IAAI,OAAO,SAA6B,MAAoB;GAC1D,IAAI,gBAAgB,OAAO,OAAQ,IAAY,UAAU,IAAI;GAC7D,IAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,KAAK,KAAK,MAAM,SAAc,gBAAgB,KAAK,GAC1F,OAAQ,IAAY,UAAU,IAAI;GACpC,IAAI,cAAc,IAAI,GAAG,OAAQ,IAAY,UAAU,IAAI;GAC3D,IAAI,eAAe,IAAI,GAAG,OAAQ,IAAY,UAAU,IAAI;GAC5D,OAAO,aAAa,IAAI;EAC1B,EAAE,KAAK,GAAG;EAEV,KAAK;CACP;AACF;AASA,IAAa,iBAAb,MAA4B;CAI1B,YAAY,UAAiC,CAAC,GAAG;EAC/C,KAAK,aAAa,QAAQ;EAC1B,KAAK,YAAY,QAAQ;CAC3B;CAEA,MAAM,OAAO,KAAc,KAAe,MAAmC;EAC3E,MAAM,SAAS,IAAI,QAAQ,oBAAoB;EAC/C,MAAM,QAAQ,OAAO,WAAW,SAAS,IAAI,OAAO,MAAM,CAAC,IAAI;EAE/D,IAAI,CAAC,OAAO;GACV,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,eAAe,CAAC;GAChD;EACF;EAEA,IAAI;GACF,MAAM,aAAa,QAAQ,IAAI,cAAc;GAC7C,MAAM,WAAW,KAAK,YAAY,KAAK,UAAU,KAAK,IAAI;GAC1D,MAAM,UAAU,IAAI,OAAO,UAAU,UAAU;GAC/C,MAAM,MAAM,QAAQ;GAEpB,IAAI,KAAK,YAAY;IACnB,MAAM,OAAO,MAAM,KAAK,WAAW,GAAG;IACtC,IAAI,CAAC,MAAM;KAAE,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,eAAe,CAAC;KAAG;IAAQ;IACxE,IAAI,OAAO;KAAE,IAAI,KAAK;KAAI,OAAO,KAAK;KAAO,aAAa,KAAK;IAAY;IAC3E,MAAM,QAAQ,kBAAkB,SAAS;IACzC,IAAI,OAAO,MAAM,OAAO;GAC1B,OACE,IAAI,OAAO;IAAE,IAAI;IAAK,OAAO,QAAQ;IAAO,aAAa,QAAQ;GAAY;GAG/E,KAAK;EACP,QAAQ;GACN,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,eAAe,CAAC;EAClD;CACF;CAEA,YAAgF;EAC9E,OAAO,KAAK,OAAO,KAAK,IAAI;CAC9B;AACF;AAIA,IAAa,8BAAb,MAAyC;CACvC,OAAO,KAAc,KAAe,MAA0B;EAC5D,MAAM,OAAO,IAAI;EACjB,IAAI,CAAC,MAAM;GAAE,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,eAAe,CAAC;GAAG;EAAQ;EACxE,IAAI,OAAO,KAAK,aAAa,cAAc,CAAC,KAAK,SAAS,GAAG;GAC3D,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,mBAAmB,CAAC;GAAG;EACzD;EACA,IAAI,KAAK,UAAU,KAAK,WAAW,UAAU;GAC3C,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,mBAAmB,CAAC;GAAG;EACzD;EACA,KAAK;CACP;AACF;AAIA,IAAa,yBAAb,MAAoC;CAClC,OAAO,KAAU,MAAe,KAAe,OAA2B;EACxE,IAAI,IAAI,aAAa;EAErB,IAAI,eAAe,iBAAiB;GAClC,IAAI,OAAO,GAAG,EAAE,KAAK;IAAE,SAAS;IAAO,QAAQ,IAAI;IAAQ,UAAU,IAAI;IAAU,SAAS,IAAI;GAAQ,CAAC;GACzG;EACF;EAEA,MAAM,SACJ,OAAO,IAAI,WAAW,YAAY,IAAI,UAAU,OAAO,IAAI,SAAS,MAAM,IAAI,SAAS;EAEzF,MAAM,UAAe;GAAE,SAAS;GAAO,SADvB,IAAI,WAAW;EACgB;EAC/C,IAAI,IAAI,MAAM,QAAQ,OAAO,IAAI;EACjC,IAAI,IAAI,UAAU,OAAO,IAAI,WAAW,UAAU,QAAQ,SAAS,IAAI;EACvE,IAAI,QAAQ,IAAI,aAAa,gBAAgB,IAAI,OAC/C,QAAQ,QAAQ,IAAI,MAAM,MAAM,IAAI,EAAE,KAAK,MAAc,EAAE,KAAK,CAAC;EAEnE,IAAI,OAAO,MAAM,EAAE,KAAK,OAAO;CACjC;CAEA,YAAiF;EAC/E,OAAO,KAAK,OAAO,KAAK,IAAI;CAC9B;AACF;AAIA,SAAgB,eAAe,GAAG,OAAiB;CACjD,QAAQ,KAAc,KAAe,SAA6B;EAChE,MAAM,YAAY,IAAI,MAAM,SAAS,CAAC;EACtC,IAAI,CAAC,MAAM,MAAM,MAAM,UAAU,SAAS,CAAC,CAAC,GAAG;GAC7C,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,YAAY,CAAC;GAAG;EAClD;EACA,KAAK;CACP;AACF;AAEA,SAAgB,qBAAqB,GAAG,OAAiB;CACvD,QAAQ,KAAc,KAAe,SAA6B;EAChE,MAAM,YAAY,IAAI,MAAM,eAAe,CAAC;EAC5C,IAAI,CAAC,MAAM,MAAM,MAAM,UAAU,SAAS,CAAC,CAAC,GAAG;GAC7C,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,YAAY,CAAC;GAAG;EAClD;EACA,KAAK;CACP;AACF;AAIA,MAAa,yBAAyB,IAAI,uBAAuB;AACjE,MAAa,0BAA0B,IAAI,wBAAwB;AACnE,MAAa,sBAAsB,IAAI,oBAAoB;AAC3D,MAAa,6BAA6B,IAAI,2BAA2B;AACzE,MAAa,8BAA8B,IAAI,4BAA4B;AAC3E,MAAa,yBAAyB,IAAI,uBAAuB;AAEjE,MAAa,gBAAgB,KAAc,KAAe,SACxD,uBAAuB,OAAO,KAAK,KAAK,IAAI;AAC9C,MAAa,iBAAiB,KAAc,KAAe,SACzD,wBAAwB,OAAO,KAAK,KAAK,IAAI;AAC/C,MAAa,mBAAmB,KAAc,KAAe,SAC3D,oBAAoB,OAAO,KAAK,KAAK,IAAI;AAC3C,MAAa,oBAAoB,KAAc,KAAe,SAC5D,2BAA2B,OAAO,KAAK,KAAK,IAAI;AAClD,MAAa,qBAAqB,KAAc,KAAe,SAC7D,4BAA4B,OAAO,KAAK,KAAK,IAAI;AACnD,MAAa,gBAAgB,KAAU,KAAc,KAAe,SAClE,uBAAuB,OAAO,KAAK,KAAK,KAAK,IAAI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lara-node/middlewares",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Predefined class-based middleware for Lara-Node applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,8 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "jsonwebtoken": "^9.0.2",
19
- "@lara-node/validator": "0.1.1"
19
+ "@lara-node/validator": "0.1.2",
20
+ "@lara-node/db": "0.1.1"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@types/express": "^5.0.6",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-B9p8lFgD.d.ts","names":[],"sources":["../src/index.ts"],"mappings":"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"}