@dwtechs/antity 0.9.2 → 0.11.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/README.md CHANGED
@@ -120,6 +120,8 @@ const entity = new Entity("consumers", [
120
120
 
121
121
  // add a consumer. Used when loggin in from user service
122
122
  router.post("/", entity.normalize, entity.validate, ...);
123
+ // or use check method to both normalize and validate at once
124
+ router.put("/", entity.check, ...);
123
125
 
124
126
  ```
125
127
 
@@ -143,6 +145,7 @@ type Type =
143
145
  "array" |
144
146
  "jwt" |
145
147
  "symbol" |
148
+ "password" |
146
149
  "email" |
147
150
  "regex" |
148
151
  "json" |
@@ -162,8 +165,8 @@ type Method = "GET" | "PATCH" | "PUT" | "POST" | "DELETE";
162
165
  class Property {
163
166
  key: string;
164
167
  type: Type;
165
- min: number | Date;
166
- max: number | Date;
168
+ min: number | Date | null;
169
+ max: number | Date | null;
167
170
  required: boolean;
168
171
  safe: boolean;
169
172
  typeCheck: boolean;
@@ -184,34 +187,143 @@ class Entity {
184
187
  set name(name: string);
185
188
 
186
189
  /**
187
- * Retrieves a property from the `properties` array that matches the specified key.
190
+ * Returns a single property object matching the given key.
188
191
  *
189
- * @param {string} key - The key of the property to retrieve.
190
- * @returns {Property | undefined} - The property object if found, otherwise `undefined`.
192
+ * - Searches the entity's properties for a property with the specified key
193
+ * - Useful for dynamic validation, normalization, or documentation
194
+ *
195
+ * @param {string} key - The property key to look up
196
+ * @returns {Property | undefined} The Property object if found, otherwise undefined
197
+ *
198
+ * **Input Properties Required:**
199
+ * - `key` (string) - Property key to look up
200
+ *
201
+ * **Output Properties:**
202
+ * - Property object matching the key, or undefined if not found
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const prop = entity.getProp('firstName');
207
+ * // prop contains the Property object for 'firstName' or undefined
208
+ * ```
191
209
  */
192
210
  getProp(key: string): Property | undefined;
193
211
 
194
212
  /**
195
- * Retrieves a list of properties associated with a specific method.
213
+ * Returns all properties configured for a given REST method.
214
+ *
215
+ * - Filters the entity's properties by the specified method (e.g., 'POST', 'GET')
216
+ * - Useful for dynamic validation, normalization, or documentation
196
217
  *
197
- * @param {Method} method - The method to filter properties by.
198
- * @returns {Property[]} An array of properties that are associated with the specified method.
218
+ * @param {Method} method - The REST method to filter properties by (e.g., 'POST', 'GET')
219
+ * @returns {Property[]} Array of Property objects associated with the method
220
+ *
221
+ * **Input Properties Required:**
222
+ * - `method` (string) - REST method to filter by
223
+ *
224
+ * **Output Properties:**
225
+ * - Array of Property objects matching the method
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * const postProps = entity.getPropsByMethod('POST');
230
+ * // postProps contains all properties relevant for POST requests
231
+ * ```
199
232
  */
200
233
  getPropsByMethod(method: Method): Property[];
201
234
 
202
235
  /**
203
- * Normalizes an array of records by applying sanitization and normalization
204
- * rules defined in the `properties` of the class.
236
+ * Normalizes each row in req.body.rows according to property config and HTTP method.
237
+ *
238
+ * - Applies sanitization if `sanitize: true` and method matches
239
+ * - Applies normalization if `normalize: true` and method matches
240
+ * - Mutates req.body.rows with sanitized/normalized values
241
+ * - Calls next(error) on failure, next() on success
242
+ *
243
+ * @param {Request} req - Express request object containing rows
244
+ * @param {Response} _res - Express response object (not used)
245
+ * @param {NextFunction} next - Express next function
246
+ *
247
+ * @returns {void}
248
+ *
249
+ * **Input Properties Required:**
250
+ * - `req.body.rows` (array) - Array of objects to normalize
251
+ * - Each property config can specify sanitize, normalize, etc.
252
+ *
253
+ * **Output Properties:**
254
+ * - Mutates `req.body.rows` with sanitized/normalized values
255
+ * - Calls next(error) if any row fails normalization, next() if all pass
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * router.post('/entity', entity.normalize, (req, res) => {
260
+ * // req.body.rows are now sanitized and normalized
261
+ * res.json({ success: true });
262
+ * });
263
+ * ```
205
264
  */
206
- normalize(req: Request, _res: Response, next: NextFunction): void;
265
+ normalize: (req: Request, _res: Response, next: NextFunction) => void;
207
266
 
208
267
  /**
209
- * Validates a set of rows against the defined properties and operation/method.
268
+ * Validates each row in req.body.rows according to property config and HTTP method.
269
+ *
270
+ * - Checks required properties and validates values
271
+ * - Calls next(error) on failure, next() on success
272
+ *
273
+ * @param {Request} req - Express request object containing rows
274
+ * @param {Response} _res - Express response object (not used)
275
+ * @param {NextFunction} next - Express next function
276
+ *
277
+ * @returns {void}
278
+ *
279
+ * **Input Properties Required:**
280
+ * - `req.body.rows` (array) - Array of objects to validate
281
+ * - Each property config can specify validate, required, etc.
282
+ *
283
+ * **Output Properties:**
284
+ * - Calls next(error) if any row fails validation, next() if all pass
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * router.post('/entity', entity.validate, (req, res) => {
289
+ * // req.body.rows are now validated
290
+ * res.json({ success: true });
291
+ * });
292
+ * ```
293
+ */
294
+ validate: (req: Request, _res: Response, next: NextFunction) => void;
295
+
296
+ /**
297
+ * Checks, sanitizes, normalizes, and validates each row in req.body.rows according to property config and HTTP method.
298
+ *
299
+ * - Applies sanitization if `sanitize: true` and method matches
300
+ * - Applies normalization if `normalize: true` and method matches
301
+ * - Checks required properties and validates values
302
+ * - Calls next(error) on failure, next() on success
303
+ *
304
+ * @param {Request} req - Express request object containing rows
305
+ * @param {Response} _res - Express response object (not used)
306
+ * @param {NextFunction} next - Express next function
210
307
  *
211
- * If a property is required and missing, or if it fails the control checks, the function returns an error message.
212
- * Otherwise, it returns `null` indicating successful validation.
308
+ * @returns {void}
309
+ *
310
+ * **Input Properties Required:**
311
+ * - `req.body.rows` (array) - Array of objects to check
312
+ * - Each property config can specify sanitize, normalize, validate, required, etc.
313
+ *
314
+ * **Output Properties:**
315
+ * - Mutates `req.body.rows` with sanitized/normalized values
316
+ * - Calls next(error) if any row fails checks, next() if all pass
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * router.post('/entity', entity.check, (req, res) => {
321
+ * // req.body.rows are now sanitized, normalized, and validated
322
+ * res.json({ success: true });
323
+ * });
324
+ * ```
213
325
  */
214
- validate(req: Request, _res: Response, next: NextFunction): void;
326
+ check: (req: Request, _res: Response, next: NextFunction) => void;
215
327
  }
216
328
 
217
329
  ```
@@ -219,6 +331,35 @@ normalize() and validate() methods are made to be used as Express.js middlewares
219
331
  Each method will look for data to work on in the **req.body.rows** parameter.
220
332
 
221
333
 
334
+ ### Password validation
335
+
336
+ Password validation will have the following options by default :
337
+
338
+ ```javascript
339
+ const PWD_MIN_LENGTH = 9;
340
+ const PWD_MAX_LENGTH = 20;
341
+ const PWD_NUMBERS = true; // password must contain a number
342
+ const PWD_UPPERCASE = true; // password must contain an uppercase letter
343
+ const PWD_LOWERCASE = true; // password must contain a lowercase letter
344
+ const PWD_SYMBOLS = true; // password must contain at least one of the following symbol character : !@#%*_-+=:?><./()
345
+ ```
346
+
347
+ #### Environment variables
348
+
349
+ You can update password default validator by setting the following environment variables :
350
+
351
+ ```javascript
352
+ PWD_MIN_LENGTH_POLICY,
353
+ PWD_MAX_LENGTH_POLICY,
354
+ PWD_NUMBERS_POLICY,
355
+ PWD_UPPERCASE_POLICY,
356
+ PWD_LOWERCASE_POLICY,
357
+ PWD_SYMBOLS_POLICY
358
+ ```
359
+
360
+ Properties **min** and **max** of the password properties will override default and environement variable if set.
361
+
362
+
222
363
  ### Available options for a property
223
364
 
224
365
  Any of these can be passed into the options object for each function.
package/dist/antity.d.ts CHANGED
@@ -42,6 +42,7 @@ type Type =
42
42
  "array" |
43
43
  "jwt" |
44
44
  "symbol" |
45
+ "password" |
45
46
  "email" |
46
47
  "regex" |
47
48
  "json" |
@@ -69,13 +70,14 @@ declare class Entity {
69
70
  getPropsByMethod(method: Method): Property[];
70
71
  normalize: (req: Request, _res: Response, next: NextFunction) => void;
71
72
  validate: (req: Request, _res: Response, next: NextFunction) => void;
73
+ check: (req: Request, _res: Response, next: NextFunction) => void;
72
74
  }
73
75
 
74
76
  declare class Property {
75
77
  key: string;
76
78
  type: Type;
77
- min: number | Date;
78
- max: number | Date;
79
+ min: number | Date | null;
80
+ max: number | Date | null;
79
81
  required: boolean;
80
82
  safe: boolean;
81
83
  typeCheck: boolean;
@@ -89,8 +91,8 @@ declare class Property {
89
91
  constructor(
90
92
  key: string,
91
93
  type: Type,
92
- min: number | Date,
93
- max: number | Date,
94
+ min: number | Date | null,
95
+ max: number | Date | null,
94
96
  required: boolean,
95
97
  safe: boolean,
96
98
  typeCheck: boolean,
package/dist/antity.js CHANGED
@@ -24,11 +24,18 @@ SOFTWARE.
24
24
  https://github.com/DWTechs/Antity.js
25
25
  */
26
26
 
27
- import { isBoolean, isStringOfLength, isValidNumber, isValidInteger, isValidFloat, isEven, isOdd, isPositive, isNegative, isPowerOfTwo, isAscii, isArrayOfLength, isEmail, isRegex, isJson, isJWT, isSymbol, isIpAddress, isSlug, isHexadecimal, isValidDate, isValidTimestamp, isFunction, isHtmlElement, isHtmlEventAttribute, isNode, isObject, isString, isProperty, isArray, isIn, isDate, isNumber, isInteger, isNil } from '@dwtechs/checkard';
27
+ import { isBoolean, isStringOfLength, isValidNumber, isValidInteger, isValidFloat, isEven, isOdd, isPositive, isNegative, isPowerOfTwo, isAscii, isArrayOfLength, isValidPassword, isEmail, isRegex, isJson, isJWT, isSymbol, isIpAddress, isSlug, isHexadecimal, isValidDate, isValidTimestamp, isFunction, isHtmlElement, isHtmlEventAttribute, isNode, isObject, isString, isProperty, isArray, isIn, isDate, isNumber, isInteger, isNil } from '@dwtechs/checkard';
28
28
  import { log } from '@dwtechs/winstan';
29
29
 
30
30
  const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
31
31
 
32
+ const { PWD_MIN_LENGTH_POLICY, PWD_MAX_LENGTH_POLICY, PWD_NUMBERS_POLICY, PWD_UPPERCASE_POLICY, PWD_LOWERCASE_POLICY, PWD_SYMBOLS_POLICY } = process.env;
33
+ const PWD_MIN_LENGTH = PWD_MIN_LENGTH_POLICY ? +PWD_MIN_LENGTH_POLICY : 9;
34
+ const PWD_MAX_LENGTH = PWD_MAX_LENGTH_POLICY ? +PWD_MAX_LENGTH_POLICY : 20;
35
+ const PWD_NUMBERS = PWD_NUMBERS_POLICY ? true : false;
36
+ const PWD_UPPERCASE = PWD_UPPERCASE_POLICY ? true : false;
37
+ const PWD_LOWERCASE = PWD_LOWERCASE_POLICY ? true : false;
38
+ const PWD_SYMBOLS = PWD_SYMBOLS_POLICY ? true : false;
32
39
  const Types = {
33
40
  boolean: {
34
41
  validate: (v, _min, _max, _typeCheck) => isBoolean(v)
@@ -66,6 +73,19 @@ const Types = {
66
73
  array: {
67
74
  validate: (v, min, max, _typeCheck) => isArrayOfLength(v, min || undefined, max || undefined)
68
75
  },
76
+ password: {
77
+ validate: (v, min, max, _typeCheck) => {
78
+ const o = {
79
+ minLength: min || PWD_MIN_LENGTH,
80
+ maxLength: max || PWD_MAX_LENGTH,
81
+ lowerCase: PWD_LOWERCASE,
82
+ upperCase: PWD_UPPERCASE,
83
+ number: PWD_NUMBERS,
84
+ specialCharacter: PWD_SYMBOLS,
85
+ };
86
+ return isValidPassword(v, o);
87
+ }
88
+ },
69
89
  email: {
70
90
  validate: (v, _min, _max, _typeCheck) => isEmail(v)
71
91
  },
@@ -185,12 +205,12 @@ function control(v, key, type, min, max, typeCheck, cb) {
185
205
  c += ` and >= ${min}`;
186
206
  if (!isNil(max))
187
207
  c += ` and <= ${max}`;
188
- return val ? null : { status: 400, msg: `Invalid ${key}, must be of type ${type}${c}` };
208
+ return val ? null : { statusCode: 400, message: `Invalid ${key}, must be of type ${type}${c}` };
189
209
  }
190
210
 
191
211
  function require(v, key, type) {
192
212
  log.debug(`require ${key}: ${type} = ${v}`);
193
- return isNil(v) ? { status: 400, msg: `Missing ${key} of type ${type}` } : null;
213
+ return isNil(v) ? { statusCode: 400, message: `Missing ${key} of type ${type}` } : null;
194
214
  }
195
215
 
196
216
  class Entity {
@@ -199,7 +219,7 @@ class Entity {
199
219
  var _a;
200
220
  const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
201
221
  if (!isArray(rows, "!0"))
202
- return next({ status: 400, msg: "Normalize: no rows found in request body" });
222
+ return next({ statusCode: 400, message: "Normalize: no rows found in request body" });
203
223
  for (const r of rows) {
204
224
  for (const { key, type, sanitize: sanitize$1, normalize, sanitizer, normalizer, } of this._properties) {
205
225
  let v = r[key];
@@ -223,11 +243,11 @@ class Entity {
223
243
  const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
224
244
  const method = req.method;
225
245
  if (!isArray(rows, "!0"))
226
- return next({ status: 400, msg: "Sanitize: no rows found in request body" });
246
+ return next({ statusCode: 400, message: "Validate: no rows found in request body" });
227
247
  if (!isIn(Methods, method))
228
248
  return next({
229
- status: 400,
230
- msg: `Invalid REST method. Received: ${method}. Must be one of: ${Methods.toString()}`
249
+ statusCode: 400,
250
+ message: `Invalid REST method. Received: ${method}. Must be one of: ${Methods.toString()}`
231
251
  });
232
252
  for (const r of rows) {
233
253
  for (const { key, type, min, max, required, typeCheck, methods, validate, validator } of this._properties) {
@@ -248,6 +268,47 @@ class Entity {
248
268
  }
249
269
  next();
250
270
  };
271
+ this.check = (req, _res, next) => {
272
+ var _a;
273
+ const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
274
+ const method = req.method;
275
+ if (!isArray(rows, "!0"))
276
+ return next({ statusCode: 400, message: "Check: no rows found in request body" });
277
+ if (!isIn(Methods, method))
278
+ return next({
279
+ statusCode: 400,
280
+ message: `Invalid REST method. Received: ${method}. Must be one of: ${Methods.toString()}`
281
+ });
282
+ for (const r of rows) {
283
+ for (const { key, type, min, max, required, typeCheck, methods, validate, sanitize: sanitize$1, normalize, sanitizer, normalizer, validator } of this._properties) {
284
+ let v = r[key];
285
+ if (isIn(methods, method)) {
286
+ if (v) {
287
+ if (sanitize$1) {
288
+ log.debug(`sanitize ${key}: ${type} = ${v}`);
289
+ v = sanitize(v, sanitizer);
290
+ }
291
+ if (normalize && isFunction(normalizer)) {
292
+ log.debug(`normalize ${key}: ${type} = ${v}`);
293
+ v = normalizer(v);
294
+ }
295
+ r[key] = v;
296
+ if (validate) {
297
+ const ct = control(v, key, type, min, max, typeCheck, validator);
298
+ if (ct)
299
+ return next(ct);
300
+ }
301
+ }
302
+ if (required) {
303
+ const rq = require(v, key, type);
304
+ if (rq)
305
+ return next(rq);
306
+ }
307
+ }
308
+ }
309
+ }
310
+ next();
311
+ };
251
312
  this._name = name;
252
313
  this._properties = [];
253
314
  this._unsafeProps = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwtechs/antity",
3
- "version": "0.9.2",
3
+ "version": "0.11.0",
4
4
  "description": "Open source library for easy entity management",
5
5
  "keywords": [
6
6
  "entities"