skyguard-js 1.1.4 → 1.1.5

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 CHANGED
@@ -168,11 +168,12 @@ app.staticFiles(join(__dirname, "..", "static"));
168
168
 
169
169
  ## ⛔ Data Validation
170
170
 
171
- To validate data in the body of client requests, the framework provides the creation of validation schemas, which are created as follows:
171
+ To validate the data in the body of client requests, the framework provides the creation of validation schemes and a middleware function to validate the body of HTTP requests, used as follows:
172
172
 
173
173
  ```ts
174
- import { v, schema } from "skyguard-js";
174
+ import { v, schema, validateData } from "skyguard-js";
175
175
 
176
+ // Created Schema
176
177
  const userSchema = schema({
177
178
  name: v.string({ maxLength: 60 }),
178
179
  email: v.email(),
@@ -181,17 +182,26 @@ const userSchema = schema({
181
182
  birthdate: v.date({ max: new Date() }),
182
183
  });
183
184
 
184
- app.post("/users", (request: Request) => {
185
- const validatedData = request.validateData(userSchema);
185
+ // Typing Interface
186
+ interface User {
187
+ name: string;
188
+ email: string;
189
+ age: number;
190
+ active: boolean;
191
+ birthdate: Date;
192
+ }
186
193
 
187
- return Response.json({
188
- success: true,
189
- data: validatedData,
190
- });
191
- });
194
+ app.post(
195
+ "/test",
196
+ (request: Request) => {
197
+ const data = request.getData<User>();
198
+ return json(data).setStatusCode(201);
199
+ },
200
+ [validateData(userSchema)],
201
+ );
192
202
  ```
193
203
 
194
- By default each property you define in the schema is required, to define it optional you use the `.optional()` or `.default(value)` function
204
+ To type the request body, an interface is used and the .getData() method is used, which allows returning the typed bodym. By default each property you define in the schema is required, to define it optional you use the `.optional()` or `.default(value)` function
195
205
 
196
206
  Validation is:
197
207
 
package/dist/app.d.ts CHANGED
@@ -24,10 +24,6 @@ export declare class App {
24
24
  private router;
25
25
  /** Static file handler (optional) */
26
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
27
  /** View engine for rendering templates (optional) */
32
28
  private viewEngine;
33
29
  /**
package/dist/app.js CHANGED
@@ -33,10 +33,6 @@ class App {
33
33
  router;
34
34
  /** Static file handler (optional) */
35
35
  staticFileHandler = null;
36
- /** Logger instance for request logging */
37
- logger;
38
- /** Timestamp marking the start of request processing (for logging) */
39
- startTime;
40
36
  /** View engine for rendering templates (optional) */
41
37
  viewEngine;
42
38
  /**
@@ -51,7 +47,6 @@ class App {
51
47
  static bootstrap() {
52
48
  const app = container_1.Container.singleton(App);
53
49
  app.router = container_1.Container.singleton(routing_1.Router);
54
- app.logger = container_1.Container.singleton(http_1.Logger);
55
50
  app.viewEngine = container_1.Container.singleton(engineTemplate_1.ViewEngine);
56
51
  return app;
57
52
  }
@@ -149,10 +144,8 @@ class App {
149
144
  */
150
145
  run(port, callback, hostname = "127.0.0.1") {
151
146
  (0, node_http_1.createServer)((req, res) => {
152
- this.startTime = process.hrtime.bigint();
153
147
  const adapter = new http_1.NodeHttpAdapter(req, res);
154
148
  void this.handle(adapter);
155
- this.logger.log(req, res, this.startTime);
156
149
  }).listen(port, hostname, () => {
157
150
  callback();
158
151
  });
@@ -14,6 +14,10 @@ export declare class NodeHttpAdapter implements HttpAdapter {
14
14
  private readonly req;
15
15
  private readonly res;
16
16
  private contentParser;
17
+ /** Logger instance for request logging */
18
+ private logger;
19
+ /** Timestamp marking the start of request processing (for logging) */
20
+ private startTime;
17
21
  /**
18
22
  * @param req - Native Node.js incoming request
19
23
  * @param res - Native Node.js server response
@@ -4,6 +4,7 @@ exports.NodeHttpAdapter = void 0;
4
4
  const httpMethods_1 = require("./httpMethods");
5
5
  const request_1 = require("./request");
6
6
  const contentParserManager_1 = require("../parsers/contentParserManager");
7
+ const logger_1 = require("./logger");
7
8
  /**
8
9
  * Node.js HTTP adapter.
9
10
  *
@@ -16,6 +17,10 @@ class NodeHttpAdapter {
16
17
  req;
17
18
  res;
18
19
  contentParser;
20
+ /** Logger instance for request logging */
21
+ logger;
22
+ /** Timestamp marking the start of request processing (for logging) */
23
+ startTime;
19
24
  /**
20
25
  * @param req - Native Node.js incoming request
21
26
  * @param res - Native Node.js server response
@@ -23,6 +28,8 @@ class NodeHttpAdapter {
23
28
  constructor(req, res) {
24
29
  this.req = req;
25
30
  this.res = res;
31
+ this.startTime = process.hrtime.bigint();
32
+ this.logger = new logger_1.Logger();
26
33
  this.contentParser = new contentParserManager_1.ContentParserManager();
27
34
  }
28
35
  /**
@@ -33,10 +40,10 @@ class NodeHttpAdapter {
33
40
  */
34
41
  async getRequest() {
35
42
  const url = new URL(this.req.url || "", `http://${this.req.headers.host}`);
36
- const request = new request_1.Request(url.pathname)
37
- .setMethod(this.req.method || httpMethods_1.HttpMethods.get)
38
- .setQuery(Object.fromEntries(url.searchParams.entries()))
39
- .setHeaders(this.req.headers);
43
+ const request = new request_1.Request(url.pathname);
44
+ request.setMethod(this.req.method || httpMethods_1.HttpMethods.get);
45
+ request.setQuery(Object.fromEntries(url.searchParams.entries()));
46
+ request.setHeaders(this.req.headers);
40
47
  if (request.method === httpMethods_1.HttpMethods.post ||
41
48
  request.method === httpMethods_1.HttpMethods.put ||
42
49
  request.method === httpMethods_1.HttpMethods.patch) {
@@ -63,6 +70,7 @@ class NodeHttpAdapter {
63
70
  }
64
71
  if (!response.content)
65
72
  this.res.removeHeader("Content-Type");
73
+ this.logger.log(this.req, this.res, this.startTime);
66
74
  this.res.end(response.content);
67
75
  }
68
76
  }
@@ -1,6 +1,5 @@
1
1
  import { HttpMethods } from "./httpMethods";
2
2
  import type { Headers } from "../types";
3
- import { type FieldDefinition } from "../validators";
4
3
  import { Session } from "../sessions";
5
4
  import type { UploadedFile } from "../parsers/parserInterface";
6
5
  /**
@@ -36,43 +35,30 @@ export declare class Request {
36
35
  * This object can be freely used by middlewares and route handlers to store
37
36
  * arbitrary data during the request lifecycle.
38
37
  */
39
- state: Record<string, any>;
40
- /** Single uploaded file metadata. */
41
- file?: UploadedFile;
38
+ state: Record<string, unknown>;
42
39
  /** Multiple uploaded files metadata. */
43
- files?: UploadedFile[] | Record<string, UploadedFile[]>;
40
+ files?: UploadedFile | UploadedFile[] | Record<string, UploadedFile[]>;
44
41
  constructor(url: string);
45
42
  get url(): string;
46
43
  get method(): HttpMethods;
47
- setMethod(method: HttpMethods): this;
44
+ setMethod(method: HttpMethods): void;
48
45
  get headers(): Headers;
49
- setHeaders(headers: Headers): this;
46
+ setHeaders(headers: Headers): void;
50
47
  get query(): Record<string, string>;
51
- setQuery(query: Record<string, string>): this;
48
+ setQuery(query: Record<string, string>): void;
52
49
  get params(): Record<string, string>;
53
- setParams(params: Record<string, string>): this;
54
- get data(): Record<string, unknown>;
55
- setData(data: Record<string, any>): this;
50
+ setParams(params: Record<string, string>): void;
51
+ get data(): Record<string, any>;
52
+ setData(data: Record<string, any>): void;
53
+ getData<T>(): Partial<T>;
56
54
  get session(): Session;
57
55
  setSession(session: Session): void;
58
56
  /**
59
57
  * Returns all request cookies as a key-value object.
60
58
  */
61
59
  get cookies(): Record<string, string>;
62
- /**
63
- * Returns a cookie value by name.
64
- */
65
- getCookie(name: string): string | undefined;
66
60
  /**
67
61
  * Checks whether a cookie exists.
68
62
  */
69
63
  hasCookie(name: string): boolean;
70
- /**
71
- * Validates the request payload against a validation schema.
72
- *
73
- * Throws if validation fails.
74
- *
75
- * @param schema - Validation rules mapped by field name
76
- */
77
- validateData(schema: Map<string, FieldDefinition>): unknown;
78
64
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Request = void 0;
4
- const validators_1 = require("../validators");
5
4
  const cookies_1 = require("../sessions/cookies");
6
5
  /**
7
6
  * Represents an incoming client request within the framework.
@@ -37,8 +36,6 @@ class Request {
37
36
  * arbitrary data during the request lifecycle.
38
37
  */
39
38
  state = {};
40
- /** Single uploaded file metadata. */
41
- file;
42
39
  /** Multiple uploaded files metadata. */
43
40
  files;
44
41
  constructor(url) {
@@ -52,35 +49,33 @@ class Request {
52
49
  }
53
50
  setMethod(method) {
54
51
  this._method = method;
55
- return this;
56
52
  }
57
53
  get headers() {
58
54
  return this._headers;
59
55
  }
60
56
  setHeaders(headers) {
61
57
  this._headers = headers;
62
- return this;
63
58
  }
64
59
  get query() {
65
60
  return this._query;
66
61
  }
67
62
  setQuery(query) {
68
63
  this._query = query;
69
- return this;
70
64
  }
71
65
  get params() {
72
66
  return this._params;
73
67
  }
74
68
  setParams(params) {
75
69
  this._params = params;
76
- return this;
77
70
  }
78
71
  get data() {
79
72
  return this._data;
80
73
  }
81
74
  setData(data) {
82
75
  this._data = data;
83
- return this;
76
+ }
77
+ getData() {
78
+ return this._data;
84
79
  }
85
80
  get session() {
86
81
  return this._session;
@@ -94,27 +89,11 @@ class Request {
94
89
  get cookies() {
95
90
  return (0, cookies_1.parseCookies)(this._headers?.cookie);
96
91
  }
97
- /**
98
- * Returns a cookie value by name.
99
- */
100
- getCookie(name) {
101
- return this.cookies[name];
102
- }
103
92
  /**
104
93
  * Checks whether a cookie exists.
105
94
  */
106
95
  hasCookie(name) {
107
96
  return name in this.cookies;
108
97
  }
109
- /**
110
- * Validates the request payload against a validation schema.
111
- *
112
- * Throws if validation fails.
113
- *
114
- * @param schema - Validation rules mapped by field name
115
- */
116
- validateData(schema) {
117
- return validators_1.Validator.validateOrFail(this.data, schema);
118
- }
119
98
  }
120
99
  exports.Request = Request;
@@ -84,7 +84,7 @@ export declare class Response {
84
84
  * return Response.json([{ id: 1 }, { id: 2 }]);
85
85
  * });
86
86
  */
87
- static json<T>(data: T): Response;
87
+ static json(data: unknown): Response;
88
88
  /**
89
89
  * Creates a plain text response.
90
90
  *
package/dist/index.d.ts CHANGED
@@ -6,4 +6,4 @@ export { FileSessionStorage, MemorySessionStorage } from "./sessions";
6
6
  export { HttpMethods } from "./http/httpMethods";
7
7
  export { createUploader } from "./storage/uploader";
8
8
  export { StorageType } from "./storage/types";
9
- export { v, schema } from "./validators/validationSchema";
9
+ export { v, schema, validateData } from "./validators/validationSchema";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.schema = exports.v = exports.StorageType = exports.createUploader = exports.HttpMethods = exports.MemorySessionStorage = exports.FileSessionStorage = exports.RouterGroup = exports.Response = exports.Request = exports.createApp = void 0;
3
+ exports.validateData = exports.schema = exports.v = exports.StorageType = exports.createUploader = exports.HttpMethods = exports.MemorySessionStorage = exports.FileSessionStorage = exports.RouterGroup = exports.Response = exports.Request = exports.createApp = void 0;
4
4
  var app_1 = require("./app");
5
5
  Object.defineProperty(exports, "createApp", { enumerable: true, get: function () { return app_1.createApp; } });
6
6
  var http_1 = require("./http");
@@ -20,3 +20,4 @@ Object.defineProperty(exports, "StorageType", { enumerable: true, get: function
20
20
  var validationSchema_1 = require("./validators/validationSchema");
21
21
  Object.defineProperty(exports, "v", { enumerable: true, get: function () { return validationSchema_1.v; } });
22
22
  Object.defineProperty(exports, "schema", { enumerable: true, get: function () { return validationSchema_1.schema; } });
23
+ Object.defineProperty(exports, "validateData", { enumerable: true, get: function () { return validationSchema_1.validateData; } });
@@ -14,15 +14,21 @@ const http_1 = require("../http");
14
14
  const resolveOrigin = (requestOrigin, config) => {
15
15
  if (!requestOrigin)
16
16
  return null;
17
+ const cleanOrigin = (origin) => origin.endsWith("/") ? origin.slice(0, -1) : origin;
17
18
  if (typeof config.origin === "function") {
18
- const out = config.origin(requestOrigin);
19
- return out ? requestOrigin : null;
19
+ return config.origin(requestOrigin) ? requestOrigin : null;
20
20
  }
21
- if (Array.isArray(config.origin))
22
- return config.origin.includes(requestOrigin) ? requestOrigin : null;
23
- if (config.origin === "*")
21
+ if (Array.isArray(config.origin)) {
22
+ return config.origin.map(cleanOrigin).includes(cleanOrigin(requestOrigin))
23
+ ? requestOrigin
24
+ : null;
25
+ }
26
+ if (config.origin === "*") {
24
27
  return config.credentials ? requestOrigin : "*";
25
- return config.origin;
28
+ }
29
+ return cleanOrigin(config.origin) === cleanOrigin(requestOrigin)
30
+ ? requestOrigin
31
+ : null;
26
32
  };
27
33
  /**
28
34
  * Native CORS middleware, generates a CORS configuration for the server.
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Layer = void 0;
4
- const escapeRegex = (value) => {
5
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6
- };
7
4
  /**
8
5
  * Represents a single route layer in the routing system.
9
6
  *
@@ -34,7 +31,7 @@ class Layer {
34
31
  */
35
32
  constructor(url, action) {
36
33
  const paramRegex = /\{([a-zA-Z]+)\}/g;
37
- const escapedUrl = escapeRegex(url);
34
+ const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38
35
  const regexSource = escapedUrl.replace(/\\{([a-zA-Z]+)\\}/g, "([a-zA-Z0-9]+)");
39
36
  this.url = url;
40
37
  this.regex = new RegExp(`^${regexSource}/?$`);
@@ -87,7 +87,7 @@ class Uploader {
87
87
  this.validateFileSize(fileData);
88
88
  await this.applyFileFilter(request, fileData);
89
89
  const file = await this.processFile(request, fileData);
90
- request.file = file;
90
+ request.files = file;
91
91
  this.attachFieldsToRequest(request, multipartData);
92
92
  return await next(request);
93
93
  };
@@ -1,6 +1,7 @@
1
1
  import type { FieldDefinition, RuleOptions } from "./types";
2
2
  import type { BaseValidationRule } from "./validationRule";
3
3
  import { BooleanRule, DateRule, type DateRuleOptions, NumberRule, type NumberRuleOptions, StringRule, type StringRuleOptions, ArrayRule, type ArrayRuleOptions, LiteralRule, ObjectRule, BigIntRule, UnionRule } from "./rules";
4
+ import { Middleware } from "types";
4
5
  /**
5
6
  * Main validator class - provides factory methods for creating validators
6
7
  */
@@ -121,4 +122,12 @@ declare class ValidatorRules {
121
122
  */
122
123
  export declare const schema: (schemaDefinition: Record<string, BaseValidationRule>) => Map<string, FieldDefinition>;
123
124
  export declare const v: ValidatorRules;
125
+ /**
126
+ * Validates the request payload against a validation schema.
127
+ *
128
+ * Throws if validation fails.
129
+ *
130
+ * @param schema - Validation rules mapped by field name
131
+ */
132
+ export declare const validateData: (schema: Map<string, FieldDefinition>) => Middleware;
124
133
  export {};
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.v = exports.schema = void 0;
3
+ exports.validateData = exports.v = exports.schema = void 0;
4
4
  const rules_1 = require("./rules");
5
+ const validator_1 = require("./validator");
5
6
  /**
6
7
  * Main validator class - provides factory methods for creating validators
7
8
  */
@@ -202,3 +203,18 @@ const schema = (schemaDefinition) => {
202
203
  exports.schema = schema;
203
204
  // Export a singleton instance for convenience
204
205
  exports.v = new ValidatorRules();
206
+ /**
207
+ * Validates the request payload against a validation schema.
208
+ *
209
+ * Throws if validation fails.
210
+ *
211
+ * @param schema - Validation rules mapped by field name
212
+ */
213
+ const validateData = (schema) => {
214
+ return (request, next) => {
215
+ const validData = validator_1.Validator.validateOrFail(request.data, schema);
216
+ request.setData(validData);
217
+ return next(request);
218
+ };
219
+ };
220
+ exports.validateData = validateData;
@@ -31,5 +31,5 @@ export declare class Validator {
31
31
  * @returns The original data when validation succeeds
32
32
  * @throws {ValidationException} When validation fails
33
33
  */
34
- static validateOrFail(data: Record<string, unknown>, schema: Map<string, FieldDefinition>): unknown;
34
+ static validateOrFail(data: Record<string, unknown>, schema: Map<string, FieldDefinition>): Record<string, unknown>;
35
35
  }
@@ -68,7 +68,7 @@ class Validator {
68
68
  if (result.errors.length !== 0)
69
69
  throw new validationException_1.ValidationException(result.errors);
70
70
  for (const [fieldName, fieldDef] of schema.entries()) {
71
- if (!(fieldName in result) && fieldDef.defaultValue !== undefined)
71
+ if (!(fieldName in result.data) && fieldDef.defaultValue)
72
72
  result.data[fieldName] = fieldDef.defaultValue;
73
73
  }
74
74
  return result.data;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skyguard-js",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "A lightweight, dependency-free TypeScript backend framework",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",