skyguard-js 1.0.2 → 1.1.1

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 (87) hide show
  1. package/README.md +211 -54
  2. package/dist/app.d.ts +43 -7
  3. package/dist/app.js +60 -18
  4. package/dist/crypto/hasher.d.ts +91 -0
  5. package/dist/crypto/hasher.js +220 -0
  6. package/dist/crypto/index.d.ts +2 -0
  7. package/dist/crypto/index.js +12 -0
  8. package/dist/crypto/jwt.d.ts +34 -0
  9. package/dist/crypto/jwt.js +112 -0
  10. package/dist/exceptions/uploadException.d.ts +6 -0
  11. package/dist/exceptions/uploadException.js +15 -0
  12. package/dist/helpers/http.d.ts +1 -2
  13. package/dist/helpers/http.js +3 -5
  14. package/dist/http/logger.d.ts +2 -3
  15. package/dist/http/logger.js +2 -2
  16. package/dist/http/nodeNativeHttp.d.ts +0 -2
  17. package/dist/http/nodeNativeHttp.js +8 -16
  18. package/dist/http/request.d.ts +29 -46
  19. package/dist/http/request.js +40 -62
  20. package/dist/http/response.d.ts +10 -9
  21. package/dist/http/response.js +43 -32
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.js +12 -1
  24. package/dist/middlewares/auth.d.ts +34 -0
  25. package/dist/middlewares/auth.js +57 -0
  26. package/dist/middlewares/cors.js +7 -9
  27. package/dist/middlewares/index.d.ts +1 -0
  28. package/dist/middlewares/index.js +3 -1
  29. package/dist/middlewares/session.js +1 -1
  30. package/dist/parsers/jsonParser.js +2 -1
  31. package/dist/parsers/multipartParser.d.ts +12 -1
  32. package/dist/parsers/multipartParser.js +45 -14
  33. package/dist/parsers/parserInterface.d.ts +28 -2
  34. package/dist/parsers/parserInterface.js +14 -0
  35. package/dist/parsers/textParser.js +3 -2
  36. package/dist/parsers/urlEncodedParser.js +2 -1
  37. package/dist/parsers/xmlParser.js +3 -2
  38. package/dist/routing/router.js +18 -10
  39. package/dist/sessions/index.d.ts +1 -1
  40. package/dist/static/fileDownload.d.ts +4 -2
  41. package/dist/static/fileDownload.js +1 -6
  42. package/dist/static/fileStaticHandler.js +1 -1
  43. package/dist/storage/storage.d.ts +118 -0
  44. package/dist/storage/storage.js +178 -0
  45. package/dist/storage/types.d.ts +128 -0
  46. package/dist/storage/types.js +31 -0
  47. package/dist/storage/uploader.d.ts +196 -0
  48. package/dist/storage/uploader.js +370 -0
  49. package/dist/types/index.d.ts +0 -13
  50. package/dist/validators/index.d.ts +0 -1
  51. package/dist/validators/index.js +1 -3
  52. package/dist/validators/rules/arrayRule.d.ts +16 -0
  53. package/dist/validators/rules/arrayRule.js +66 -0
  54. package/dist/validators/rules/booleanRule.js +2 -2
  55. package/dist/validators/rules/index.d.ts +2 -1
  56. package/dist/validators/rules/index.js +5 -3
  57. package/dist/validators/rules/literalRule.d.ts +7 -0
  58. package/dist/validators/rules/literalRule.js +18 -0
  59. package/dist/validators/rules/numberRule.d.ts +1 -0
  60. package/dist/validators/rules/numberRule.js +2 -0
  61. package/dist/validators/rules/stringRule.d.ts +57 -1
  62. package/dist/validators/rules/stringRule.js +108 -6
  63. package/dist/validators/validationRule.d.ts +17 -15
  64. package/dist/validators/validationRule.js +20 -12
  65. package/dist/validators/validationSchema.d.ts +52 -112
  66. package/dist/validators/validationSchema.js +113 -144
  67. package/dist/views/engineTemplate.d.ts +56 -0
  68. package/dist/views/engineTemplate.js +64 -0
  69. package/package.json +11 -11
  70. package/dist/exceptions/index.d.ts +0 -5
  71. package/dist/exceptions/index.js +0 -16
  72. package/dist/helpers/app.d.ts +0 -4
  73. package/dist/helpers/app.js +0 -12
  74. package/dist/helpers/index.d.ts +0 -1
  75. package/dist/helpers/index.js +0 -9
  76. package/dist/validators/rules/emailRule.d.ts +0 -12
  77. package/dist/validators/rules/emailRule.js +0 -24
  78. package/dist/views/helpersTemplate.d.ts +0 -104
  79. package/dist/views/helpersTemplate.js +0 -186
  80. package/dist/views/index.d.ts +0 -4
  81. package/dist/views/index.js +0 -9
  82. package/dist/views/raptorEngine.d.ts +0 -127
  83. package/dist/views/raptorEngine.js +0 -165
  84. package/dist/views/templateEngine.d.ts +0 -80
  85. package/dist/views/templateEngine.js +0 -204
  86. package/dist/views/view.d.ts +0 -55
  87. package/dist/views/view.js +0 -2
package/README.md CHANGED
@@ -26,11 +26,20 @@ At its current stage, the framework focuses on **routing**, **internal architect
26
26
  - Request / Response abstractions
27
27
  - Declarative data validation
28
28
  - Simple template engine with layouts and helpers
29
+ - Built-in HTTP exceptions
30
+ - Password hashing and JWT token generation
31
+ - CORS middleware
32
+ - File uploads (via middleware)
29
33
  - Static file serving
30
34
  - Session handling (via middleware)
31
35
 
32
36
  ---
33
37
 
38
+ > [!NOTE]
39
+ > It is recommended to develop with `TypeScript` for a more secure and efficient development process; the framework already has native support for `TypeScript` and includes the necessary types.
40
+
41
+ ---
42
+
34
43
  ## 📦 Installation
35
44
 
36
45
  ```bash
@@ -63,11 +72,11 @@ Routes are registered using HTTP methods on the `app` instance.
63
72
 
64
73
  ```ts
65
74
  app.get("/posts/{id}", (request: Request) => {
66
- return Response.json(request.getParams());
75
+ return Response.json(request.params);
67
76
  });
68
77
 
69
78
  app.post("/posts", (request: Request) => {
70
- return Response.json(request.getData());
79
+ return Response.json(request.data);
71
80
  });
72
81
  ```
73
82
 
@@ -99,7 +108,7 @@ const authMiddleware = async (
99
108
  request: Request,
100
109
  next: RouteHandler,
101
110
  ): Promise<Response> => {
102
- if (request.getHeaders["authorization"] !== "secret") {
111
+ if (request.headers["authorization"] !== "secret") {
103
112
  return Response.json({ message: "Unauthorized" }).setStatus(401);
104
113
  }
105
114
 
@@ -121,37 +130,53 @@ app.get("/secure", () => Response.json({ secure: true }), [authMiddleware]);
121
130
 
122
131
  ---
123
132
 
124
- ## Static Files
133
+ ## 🌐 CORS Middleware
134
+
135
+ To enable CORS, use the built-in `cors` middleware.
136
+
137
+ ```ts
138
+ import { cors } from "skyguard-js/middlewares";
139
+
140
+ app.middlewares([
141
+ cors({
142
+ origin: ["http://localhost:3000", "https://myapp.com"],
143
+ methods: ["GET", "POST"],
144
+ allowedHeaders: ["Content-Type", "Authorization"],
145
+ credentials: true,
146
+ }),
147
+ ]);
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 📌 Static Files
125
153
 
126
- To serve static files, use the `static` method.
154
+ To serve static files, use the application's `staticFiles` method with the directory path. The name of the folder will determine the initial route prefix.
127
155
 
128
156
  ```ts
129
157
  import { join } from "node:path";
130
158
 
131
- app.static(join(__dirname, "..", "static"));
159
+ app.staticFiles(join(__dirname, "..", "static"));
160
+
161
+ // Route http://localhost:3000/static/style.css will serve the file located at ./static/style.css
132
162
  ```
133
163
 
134
164
  ---
135
165
 
136
- ## 📦 Data Validation
166
+ ## Data Validation
137
167
 
138
168
  Skyguard.js provides a **declarative validation system** using schemas.
139
169
 
140
170
  ```ts
141
- import { ValidationSchema } from "skyguard-js/validation";
142
-
143
- export const userSchema = ValidationSchema.create()
144
- .field("name")
145
- .required("Name is required")
146
- .string({ maxLength: 60 })
147
- .field("email")
148
- .required()
149
- .email()
150
- .field("age")
151
- .number({ min: 18, max: 99 })
152
- .field("active")
153
- .boolean()
154
- .build();
171
+ import { validator } from "skyguard-js/validation";
172
+
173
+ const userSchema = validator.schema({
174
+ name: validator.string({ maxLength: 60 }),
175
+ email: validator.email().required(),
176
+ age: validator.number({ min: 18 }),
177
+ active: validator.boolean().required(),
178
+ birthdate: validator.date({ max: new Date() }),
179
+ });
155
180
 
156
181
  app.post("/users", (request: Request) => {
157
182
  const validatedData = request.validateData(userSchema);
@@ -172,62 +197,194 @@ Validation is:
172
197
 
173
198
  ---
174
199
 
175
- ## 📄 Views & Template Engine
200
+ ## 🚨 Exceptions & Error Handling
176
201
 
177
- To render HTML views, use the `render` helper.
202
+ The framework provides a set of built-in HTTP exceptions that can be thrown from route handlers or middleware. When an exception is thrown, the framework detects it and sends an appropriate HTTP response with the status code and message you specified in the class.
178
203
 
179
204
  ```ts
180
- import { render } from "skyguard-js/helpers";
205
+ import { NotFoundError, InternalServerError } from "skyguard-js/exceptions";
181
206
 
182
- app.get("/home", () => {
183
- return render(
184
- "home",
185
- {
186
- title: "Products",
187
- products: [
188
- { name: "Laptop", price: 999.99 },
189
- { name: "Mouse", price: 29.99 },
190
- ],
191
- user: { name: "John", role: "admin" },
192
- },
193
- "main",
194
- );
207
+ const listResources = ["1", "2", "3"];
208
+
209
+ app.get("/resource/{id}", (request: Request) => {
210
+ const resource = request.params["id"];
211
+
212
+ if (!listResources.includes(resource)) {
213
+ throw new NotFoundError("Resource not found");
214
+ }
215
+
216
+ return Response.json(resource);
217
+ });
218
+
219
+ app.get("/divide", (request: Request) => {
220
+ try {
221
+ const { a, b } = request.query;
222
+ const result = Number(a) / Number(b);
223
+
224
+ return Response.json({ result });
225
+ } catch (error) {
226
+ throw new InternalServerError(
227
+ "An error occurred while processing your request",
228
+ );
229
+ }
195
230
  });
196
231
  ```
197
232
 
198
- ### Supported features
233
+ ---
234
+
235
+ ## 🧱 Sessions
236
+
237
+ To handle sessions, you must use the framework’s built-in middleware. Depending on where you want to store them (in memory, in files, or in a database), you need to use the corresponding storage class.
238
+
239
+ ```ts
240
+ import { sessions } from "skyguard-js/middlewares";
241
+ import { FileSessionStorage } from "skyguard-js";
242
+
243
+ app.middlewares([sessions(FileSessionStorage)]);
244
+
245
+ app.post("/login", (request: Request) => {
246
+ const { username, password } = request.data;
247
+
248
+ if (username === "admin" && password === "secret") {
249
+ request.session.set("user", {
250
+ id: 1,
251
+ username: "admin",
252
+ role: "admin",
253
+ });
199
254
 
200
- - Variable interpolation (`{{ variable }}`)
201
- - Conditionals (`{{#if}}`)
202
- - Loops (`{{#each}}`)
203
- - Layouts
204
- - Partials
205
- - Built-in helpers (`upper`, `lower`, `date`)
206
- - Custom helpers
255
+ return json({ message: "Logged in" });
256
+ }
257
+
258
+ throw new UnauthorizedError("Invalid credentials");
259
+ });
260
+
261
+ app.get("/me", (request: Request) => {
262
+ const user = request.session.get("user");
263
+
264
+ if (!user) throw new UnauthorizedError("Not authenticated");
265
+ return json({ user });
266
+ });
267
+ ```
207
268
 
208
269
  ---
209
270
 
210
- ## 🧱 Project Status
271
+ ## 🛡️ Security
272
+
273
+ The framework includes some password hashing and JWT token generation functions, and also includes JWT authentication middleware.
274
+
275
+ ```ts
276
+ import { hash, verify, createJWT } from "skyguard-js/security";
277
+ import { authJWT } from "skyguard-js/middlewares";
211
278
 
212
- ⚠️ **Early-stage project**
279
+ app.post("/register", async (request: Request) => {
280
+ const { username, password } = request.data;
281
+ const hashedPassword = await hash(password);
213
282
 
214
- - Not production-ready
215
- - API may change
216
- - Features are still evolving
217
- - Intended primarily for learning and experimentation
283
+ // Save username and hashedPassword to database
284
+ // ...
285
+
286
+ return Response.json({ message: "User registered" });
287
+ });
288
+
289
+ app.post("/login", async (request: Request) => {
290
+ const { username, password } = request.data;
291
+
292
+ // Retrieve user from database by username
293
+ // ...
294
+
295
+ const isValid = await verify(password, user.hashedPassword);
296
+
297
+ if (!isValid) {
298
+ throw new UnauthorizedError("Invalid credentials");
299
+ }
300
+
301
+ const token = createJWT({ sub: user.id, role: user.role }, "1h");
302
+
303
+ return Response.json({ token });
304
+ });
305
+ ```
306
+
307
+ ---
308
+
309
+ ## 📂 File Uploads
310
+
311
+ To handle file uploads, use the built-in `createUploader` function to create an uploader middleware with the desired storage configuration.
312
+
313
+ ```ts
314
+ import { createUploader, StorageType } from "skyguard-js";
315
+
316
+ const uploader = createUploader({
317
+ storageType: StorageType.DISK,
318
+ storageOptions: {
319
+ destination: "./uploads",
320
+ },
321
+ });
322
+
323
+ app.post(
324
+ "/upload",
325
+ (request: Request) => {
326
+ return Response.json({
327
+ message: "File uploaded successfully",
328
+ file: request.file,
329
+ });
330
+ },
331
+ [uploader.single("file")],
332
+ );
333
+ ```
334
+
335
+ ---
336
+
337
+ ## 📄 Views & Template Engine
338
+
339
+ To render views, you must first set up the template engine using the `engineTemplates` method of the `app`, set the view path with the `views` method of the `app`, and then you can use the `render` method within your handlers to render the views with the data you want to pass.
340
+
341
+ ```ts
342
+ import { engine } from "express-handlebars";
343
+ import ejs from "ejs";
344
+ import { join } from "node:path";
345
+
346
+ app.views(__dirname, "views");
347
+
348
+ // Config for Express Handlebars
349
+ app.engineTemplates(
350
+ "hbs",
351
+ engine({
352
+ extname: "hbs",
353
+ layoutsDir: join(__dirname, "views"),
354
+ defaultLayout: "main",
355
+ }),
356
+ );
357
+
358
+ // Config for EJS
359
+ app.engineTemplates("ejs", (templatePath, data) => {
360
+ return ejs.renderFile(templatePath, data);
361
+ });
362
+
363
+ app.get("/home", () => {
364
+ return render("index", {
365
+ title: "Home Page",
366
+ message: "Welcome to the home page!",
367
+ });
368
+ });
369
+ ```
370
+
371
+ Currently, it works with third-party template engines such as **Express Handlebars**, **Pug**, and **EJS**, but the idea is to implement its own template engine in the future.
218
372
 
219
373
  ---
220
374
 
221
375
  ## 🔮 Roadmap (Tentative)
222
376
 
223
377
  - Middleware system (✅)
224
- - Template engine (✅)
378
+ - Template engines supported (✅)
225
379
  - Request / Response abstraction (✅)
226
380
  - Data validation (✅)
227
381
  - Error handling improvements (✅)
228
- - Sessions & cookies (in progress)
229
- - Authentication & authorization
382
+ - Sessions & cookies ()
383
+ - Passoword hashing & JWT tokens (✅)
384
+ - File uploads (✅)
230
385
  - Database & ORM integration
386
+ - Authentication & authorization
387
+ - WebSockets
231
388
 
232
389
  ---
233
390
 
package/dist/app.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { RouterGroup } from "./routing";
2
- import { type View } from "./views";
3
2
  import type { Middleware, RouteHandler } from "./types";
3
+ import { type TemplateEngineFunction } from "./views/engineTemplate";
4
4
  /**
5
5
  * The `App` class acts as the **execution kernel** and **lifecycle orchestrator**
6
6
  * of the framework.
@@ -22,14 +22,14 @@ import type { Middleware, RouteHandler } from "./types";
22
22
  export declare class App {
23
23
  /** Main routing system */
24
24
  private router;
25
- /**
26
- * View engine used to render templates.
27
- *
28
- * Typically consumed inside controllers to generate HTML responses.
29
- */
30
- view: View;
31
25
  /** Static file handler (optional) */
32
26
  private staticFileHandler;
27
+ /** Logger instance for request logging */
28
+ private logger;
29
+ /** Timestamp marking the start of request processing (for logging) */
30
+ private startTime;
31
+ /** View engine for rendering templates (optional) */
32
+ private viewEngine;
33
33
  /**
34
34
  * Bootstraps and configures the application.
35
35
  *
@@ -65,6 +65,42 @@ export declare class App {
65
65
  * // /public/css/style.css → /css/style.css
66
66
  */
67
67
  staticFiles(publicPath: string): void;
68
+ /**
69
+ * Configures the views directory for template rendering.
70
+ *
71
+ * @param pathSegments Path segments leading to the views directory
72
+ * @example
73
+ * app.views(__dirname, "views");
74
+ */
75
+ views(...pathSegments: string[]): void;
76
+ /**
77
+ * Configures the template engine for rendering views.
78
+ *
79
+ * @param extension - File extension associated with the template engine (e.g. "hbs", "ejs")
80
+ * @param engine - Function that renders a template given its path and data
81
+ *
82
+ * @example
83
+ * app.engineTemplates("hbs", (templatePath, data) => {
84
+ * const content = fs.readFileSync(templatePath, "utf-8");
85
+ * return content.replace(/{{\s*(\w+)\s*}}/g, (_, key) => {
86
+ * return data[key] ?? "";
87
+ * });
88
+ * });
89
+ *
90
+ * // With express-handlebars
91
+ * app.engineTemplates(
92
+ * "hbs",
93
+ * engine({
94
+ * extname: "hbs",
95
+ * layoutsDir: join(__dirname, "views"),
96
+ * defaultLayout: "main",
97
+ * }),
98
+ * );
99
+ *
100
+ * The `extension` parameter allows the framework to automatically select the correct
101
+ * template engine based on the file extension of the view being rendered.
102
+ */
103
+ engineTemplates(extension: string, engine: TemplateEngineFunction): void;
68
104
  /**
69
105
  * Starts the HTTP server on the given port.
70
106
  *
package/dist/app.js CHANGED
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createApp = exports.App = void 0;
4
4
  const routing_1 = require("./routing");
5
5
  const http_1 = require("./http");
6
- const exceptions_1 = require("./exceptions");
7
- const views_1 = require("./views");
6
+ const validationException_1 = require("./exceptions/validationException");
8
7
  const node_path_1 = require("node:path");
9
- const app_1 = require("./helpers/app");
10
8
  const fileStaticHandler_1 = require("./static/fileStaticHandler");
11
9
  const node_http_1 = require("node:http");
12
10
  const httpExceptions_1 = require("./exceptions/httpExceptions");
11
+ const engineTemplate_1 = require("./views/engineTemplate");
12
+ const container_1 = require("./container/container");
13
13
  /**
14
14
  * The `App` class acts as the **execution kernel** and **lifecycle orchestrator**
15
15
  * of the framework.
@@ -31,14 +31,14 @@ const httpExceptions_1 = require("./exceptions/httpExceptions");
31
31
  class App {
32
32
  /** Main routing system */
33
33
  router;
34
- /**
35
- * View engine used to render templates.
36
- *
37
- * Typically consumed inside controllers to generate HTML responses.
38
- */
39
- view;
40
34
  /** Static file handler (optional) */
41
35
  staticFileHandler = null;
36
+ /** Logger instance for request logging */
37
+ logger;
38
+ /** Timestamp marking the start of request processing (for logging) */
39
+ startTime;
40
+ /** View engine for rendering templates (optional) */
41
+ viewEngine;
42
42
  /**
43
43
  * Bootstraps and configures the application.
44
44
  *
@@ -49,9 +49,10 @@ class App {
49
49
  * @returns The singleton `App` instance
50
50
  */
51
51
  static bootstrap() {
52
- const app = (0, app_1.singleton)(App);
52
+ const app = container_1.Container.singleton(App);
53
53
  app.router = new routing_1.Router();
54
- app.view = new views_1.RaptorEngine((0, node_path_1.join)(__dirname, "..", "views"));
54
+ app.logger = new http_1.Logger();
55
+ app.viewEngine = container_1.Container.singleton(engineTemplate_1.ViewEngine);
55
56
  return app;
56
57
  }
57
58
  /**
@@ -71,8 +72,8 @@ class App {
71
72
  async handle(adapter) {
72
73
  try {
73
74
  const request = await adapter.getRequest();
74
- if (this.staticFileHandler && request.getMethod === http_1.HttpMethods.get) {
75
- const staticResponse = await this.staticFileHandler.tryServeFile(request.getUrl);
75
+ if (this.staticFileHandler && request.method === http_1.HttpMethods.get) {
76
+ const staticResponse = await this.staticFileHandler.tryServeFile(request.url);
76
77
  if (staticResponse) {
77
78
  adapter.sendResponse(staticResponse);
78
79
  return;
@@ -97,6 +98,46 @@ class App {
97
98
  staticFiles(publicPath) {
98
99
  this.staticFileHandler = new fileStaticHandler_1.StaticFileHandler(publicPath);
99
100
  }
101
+ /**
102
+ * Configures the views directory for template rendering.
103
+ *
104
+ * @param pathSegments Path segments leading to the views directory
105
+ * @example
106
+ * app.views(__dirname, "views");
107
+ */
108
+ views(...pathSegments) {
109
+ this.viewEngine.setViewsPath((0, node_path_1.join)(...pathSegments));
110
+ }
111
+ /**
112
+ * Configures the template engine for rendering views.
113
+ *
114
+ * @param extension - File extension associated with the template engine (e.g. "hbs", "ejs")
115
+ * @param engine - Function that renders a template given its path and data
116
+ *
117
+ * @example
118
+ * app.engineTemplates("hbs", (templatePath, data) => {
119
+ * const content = fs.readFileSync(templatePath, "utf-8");
120
+ * return content.replace(/{{\s*(\w+)\s*}}/g, (_, key) => {
121
+ * return data[key] ?? "";
122
+ * });
123
+ * });
124
+ *
125
+ * // With express-handlebars
126
+ * app.engineTemplates(
127
+ * "hbs",
128
+ * engine({
129
+ * extname: "hbs",
130
+ * layoutsDir: join(__dirname, "views"),
131
+ * defaultLayout: "main",
132
+ * }),
133
+ * );
134
+ *
135
+ * The `extension` parameter allows the framework to automatically select the correct
136
+ * template engine based on the file extension of the view being rendered.
137
+ */
138
+ engineTemplates(extension, engine) {
139
+ this.viewEngine.setEngine(extension, engine);
140
+ }
100
141
  /**
101
142
  * Starts the HTTP server on the given port.
102
143
  *
@@ -107,8 +148,10 @@ class App {
107
148
  */
108
149
  run(port, callback, hostname = "127.0.0.1") {
109
150
  (0, node_http_1.createServer)((req, res) => {
151
+ this.startTime = process.hrtime.bigint();
110
152
  const adapter = new http_1.NodeHttpAdapter(req, res);
111
153
  void this.handle(adapter);
154
+ this.logger.log(req, res, this.startTime);
112
155
  }).listen(port, hostname, () => {
113
156
  callback();
114
157
  });
@@ -172,20 +215,19 @@ class App {
172
215
  */
173
216
  handleError(error, adapter) {
174
217
  if (error instanceof httpExceptions_1.HttpException) {
175
- adapter.sendResponse(http_1.Response.json(error.toJSON()).setStatus(error.statusCode));
218
+ adapter.sendResponse(http_1.Response.json(error.toJSON()).setStatusCode(error.statusCode));
176
219
  return;
177
220
  }
178
- if (error instanceof exceptions_1.ValidationException) {
221
+ if (error instanceof validationException_1.ValidationException) {
179
222
  adapter.sendResponse(http_1.Response.json({
180
- success: false,
181
223
  errors: error.getErrorsByField(),
182
- }).setStatus(400));
224
+ }).setStatusCode(400));
183
225
  return;
184
226
  }
185
227
  adapter.sendResponse(http_1.Response.json({
186
228
  statusCode: 500,
187
229
  message: "Internal Server Error",
188
- }).setStatus(500));
230
+ }).setStatusCode(500));
189
231
  console.error(error);
190
232
  }
191
233
  }
@@ -0,0 +1,91 @@
1
+ type ScryptOptions = {
2
+ cost: number;
3
+ blockSize: number;
4
+ parallelization: number;
5
+ maxmem?: number;
6
+ };
7
+ /**
8
+ * Hashes a plaintext password using scrypt with an unique random salt.
9
+ *
10
+ * Output format:
11
+ * `scrypt$<cost>$<blockSize>$<parallelization>$<saltHex>$<hashHex>`
12
+ *
13
+ * @param password - Plaintext password.
14
+ * @param saltLength - Salt length in bytes. Default `16` (recommended minimum).
15
+ * @param params - Scrypt work-factor params. Defaults to `DEFAULT_PARAMS`.
16
+ * @param pepper - Optional server secret mixed into the password (e.g., from env var).
17
+ * @returns A compact encoded hash string containing algorithm parameters + salt + derived key.
18
+ */
19
+ export declare const hash: (password: string, saltLength?: number, params?: ScryptOptions, pepper?: string) => Promise<string>;
20
+ /**
21
+ * Verifies a plaintext password against a stored scrypt hash string.
22
+ *
23
+ * Safe failure behavior:
24
+ * - Returns `false` for any parsing error, invalid encoding, mismatched lengths,
25
+ * or scrypt errors. This avoids leaking details about why verification failed.
26
+ *
27
+ * @param password - Plaintext password to verify.
28
+ * @param storedHash - Stored hash string in the compact format.
29
+ * @param pepper - Optional server secret; must match the one used when hashing.
30
+ * @returns `true` if the password matches, otherwise `false`.
31
+ */
32
+ export declare const verify: (password: string, storedHash: string, pepper?: string) => Promise<boolean>;
33
+ /**
34
+ * Indicates whether a stored hash should be regenerated using the current parameters.
35
+ *
36
+ * Use this after successful login:
37
+ * - If `needsRehash(...) === true`, compute a new hash using `hash(...)` with
38
+ * the latest parameters and store it back to the DB.
39
+ *
40
+ * This enables gradual upgrades of the work factor without forcing password resets.
41
+ *
42
+ * @param storedHash - Stored hash string in compact format.
43
+ * @param params - Desired/current scrypt params. Defaults to `DEFAULT_PARAMS`.
44
+ * @returns `true` if the hash is missing/invalid or was produced with different parameters.
45
+ */
46
+ export declare const needsRehash: (storedHash: string, params?: ScryptOptions) => boolean;
47
+ /**
48
+ * Hashes multiple passwords using controlled concurrency.
49
+ *
50
+ * Why limit concurrency?
51
+ * scrypt is intentionally CPU + memory intensive. Running too many in parallel can:
52
+ * - saturate the libuv threadpool,
53
+ * - exceed memory limits (especially in containers),
54
+ * - increase latency for the rest of the application.
55
+ *
56
+ * The `concurrency` option caps the number of simultaneous scrypt operations.
57
+ *
58
+ * @param passwords - List of plaintext passwords.
59
+ * @param options - Optional hashing controls:
60
+ * - saltLength: salt bytes (default 16)
61
+ * - params: scrypt params (default DEFAULT_PARAMS)
62
+ * - pepper: optional server secret
63
+ * - concurrency: max simultaneous operations (default 4)
64
+ * @returns Array of compact hash strings in the same order as input.
65
+ */
66
+ export declare const hashBatch: (passwords: string[], options?: {
67
+ saltLength?: number;
68
+ params?: ScryptOptions;
69
+ pepper?: string;
70
+ concurrency?: number;
71
+ }) => Promise<string[]>;
72
+ /**
73
+ * Verifies multiple password/hash pairs using controlled concurrency.
74
+ *
75
+ * This is useful for bulk checks or migrations. Concurrency is typically higher
76
+ * than hashing, but still should be bounded to avoid saturating the threadpool.
77
+ *
78
+ * @param credentials - Array of `{ password, hash }` pairs.
79
+ * @param options - Optional verification controls:
80
+ * - pepper: optional server secret
81
+ * - concurrency: max simultaneous operations (default 8)
82
+ * @returns Array of booleans in the same order as input.
83
+ */
84
+ export declare const verifyBatch: (credentials: Array<{
85
+ password: string;
86
+ hash: string;
87
+ }>, options?: {
88
+ pepper?: string;
89
+ concurrency?: number;
90
+ }) => Promise<boolean[]>;
91
+ export {};