@dwtechs/antity 0.5.0 → 0.7.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
@@ -57,7 +57,7 @@ const entity = new Entity("consumers", [
57
57
  min: 0,
58
58
  max: 120,
59
59
  typeCheck: true,
60
- operations: ["select", "update", "delete"],
60
+ operations: ["GET", "PUT", "DELETE"],
61
61
  required: true,
62
62
  safe: true,
63
63
  sanitize: true,
@@ -73,7 +73,7 @@ const entity = new Entity("consumers", [
73
73
  min: 0,
74
74
  max: 255,
75
75
  typeCheck: true,
76
- operations: ["select", "insert", "update", "delete"],
76
+ operations: ["GET", "POST", "PUT", "DELETE"],
77
77
  required: true,
78
78
  safe: true,
79
79
  sanitize: true,
@@ -89,7 +89,7 @@ const entity = new Entity("consumers", [
89
89
  min: 0,
90
90
  max: 255,
91
91
  typeCheck: true,
92
- operations: ["select", "insert", "update", "delete"],
92
+ operations: ["GET", "POST", "PUT", "DELETE"],
93
93
  required: true,
94
94
  safe: true,
95
95
  sanitize: true,
@@ -105,7 +105,7 @@ const entity = new Entity("consumers", [
105
105
  min: 0,
106
106
  max: 255,
107
107
  typeCheck: true,
108
- operations: ["select", "insert", "update", "delete"],
108
+ operations: ["GET", "POST", "PUT", "DELETE"],
109
109
  required: true,
110
110
  safe: true,
111
111
  sanitize: true,
@@ -117,47 +117,45 @@ const entity = new Entity("consumers", [
117
117
  },
118
118
  ]);
119
119
 
120
- req.body = entity.normalize(req.body); // will also sanitize if true
121
- const check = entity.validate(req.body, "select");
122
- const unsafeProps = entity.getUnsafeProps();
120
+ // add a consumer. Used when loggin in from user service
121
+ router.post("/", entity.normalize, entity.validate, ...);
123
122
 
124
123
  ```
125
124
 
126
-
127
125
  ## API Reference
128
126
 
129
127
 
130
128
  ```javascript
131
129
 
132
- type Type = "boolean" |
133
- "string" |
134
- "number" |
135
- "integer" |
136
- "float" |
137
- "even" |
138
- "odd" |
139
- "positve" |
140
- "negative" |
141
- "powerOfTwo" |
142
- "ascii" |
143
- "array" |
144
- "jwt" |
145
- "symbol" |
146
- "email" |
147
- "regex" |
148
- "json" |
149
- "ipAddress" |
150
- "slug" |
151
- "hexadecimal" |
152
- "date" |
153
- "timestamp" |
154
- "function" |
155
- "htmlElement" |
156
- "htmlEventAttribute" |
157
- "node" |
158
- "object";
130
+ type Type =
131
+ "boolean" |
132
+ "string" |
133
+ "number" |
134
+ "integer" |
135
+ "float" |
136
+ "even" |
137
+ "odd" |
138
+ "positive" |
139
+ "negative" |
140
+ "powerOfTwo" |
141
+ "ascii" |
142
+ "array" |
143
+ "jwt" |
144
+ "symbol" |
145
+ "email" |
146
+ "regex" |
147
+ "json" |
148
+ "ipAddress" |
149
+ "slug" |
150
+ "hexadecimal" |
151
+ "date" |
152
+ "timestamp" |
153
+ "function" |
154
+ "htmlElement" |
155
+ "htmlEventAttribute" |
156
+ "node" |
157
+ "object";
159
158
 
160
- type Operation = "select" | "insert" | "update" | "merge" | "delete";
161
159
  type Method = "GET" | "PATCH" | "PUT" | "POST" | "DELETE";
162
160
 
163
161
  class Property {
@@ -168,7 +166,7 @@ class Property {
168
166
  required: boolean;
169
167
  safe: boolean;
170
168
  typeCheck: boolean;
171
- operations: Operation[];
169
+ methods: Method[];
172
170
  sanitize: boolean;
173
171
  normalize: boolean;
174
172
  control: boolean;
@@ -178,40 +176,38 @@ class Property {
178
176
  };
179
177
 
180
178
  class Entity {
181
- table: string;
182
- cols: Record<Operation, string[]>;
183
- properties: Property[];
184
- normalize(rows: Record<string, any>[]): Record<string, any>[]; // will also sanitize if true
185
- validate(rows: Record<string, any>[], operation: Operation | Method): string | null;
186
- getTable(): string;
187
- // if pagination is true for a select operation, it will return cols + a total column
188
- getCols(operation: Operation, stringify?: boolean, pagination?: boolean): string[] | string;
189
- getUnsafeProps(): string[];
179
+ constructor(name: string, properties: Property[]);
180
+ get name(): string;
181
+ get unsafeProps(): string[];
182
+ get properties(): Property[];
183
+ set name(name: string);
184
+
185
+ /**
186
+ * Retrieves a property from the `properties` array that matches the specified key.
187
+ *
188
+ * @param {string} key - The key of the property to retrieve.
189
+ * @returns {Property | undefined} - The property object if found, otherwise `undefined`.
190
+ */
191
+ getProp(key: string): Property | undefined;
192
+
193
+ /**
194
+ * Normalizes an array of records by applying sanitization and normalization
195
+ * rules defined in the `properties` of the class.
196
+ */
197
+ normalize(req: Request, _res: Response, next: NextFunction): void;
198
+
199
+ /**
200
+ * Validates a set of rows against the defined properties and operation/method.
201
+ *
202
+ * If a property is required and missing, or if it fails the control checks, the function returns an error message.
203
+ * Otherwise, it returns `null` indicating successful validation.
204
+ */
205
+ validate(req: Request, _res: Response, next: NextFunction): void;
190
206
  }
191
207
 
192
208
  ```
193
-
194
- ### REST Methods mapping
195
-
196
- When validating request inputs, you can use REST methods or SQL operations as second parameter
197
-
198
- | REST method | SQL operation |
199
- | :---------- | :------------ |
200
- | GET | select |
201
- | PATCH | update |
202
- | PUT | update |
203
- | POST | insert |
204
- | DELETE | delete |
205
-
206
- examples :
207
-
208
- ```javascript
209
-
210
- entity.validate(req.body, "GET"); // will validate "select" properties
211
- entity.validate(req.body, "PUT"); // will validate "update" properties
212
- entity.validate(req.body, "DELETE"); // will validate "delete" properties
213
-
214
- ```
209
+ normalize() and validate() methods are made to be used as Express.js middlewares.
210
+ Each method will look for data to work on in the **req.body.rows** parmeter.
215
211
 
216
212
 
217
213
  ### Available options for a property
package/dist/antity.d.ts CHANGED
@@ -24,10 +24,8 @@ SOFTWARE.
24
24
  https://github.com/DWTechs/Antity.js
25
25
  */
26
26
 
27
-
28
- type Operation = typeof Operations[number];
27
+ import type { Request, Response, NextFunction } from 'express';
29
28
  type Method = typeof Methods[number];
30
-
31
29
  type Type =
32
30
  "boolean" |
33
31
  "string" |
@@ -57,38 +55,31 @@ type Type =
57
55
  "node" |
58
56
  "object";
59
57
 
60
- declare const Operations: readonly [ "select", "insert", "update", "merge", "delete" ];
61
58
  declare const Methods: readonly [ "GET", "PATCH", "PUT", "POST", "DELETE" ];
62
59
 
63
60
  declare const Types: Record<Type, {
64
- validate: (v: any, min: number | Date, max: number | Date, typeCheck: boolean) => boolean;
61
+ validate: (
62
+ v: any,
63
+ min: number | Date,
64
+ max: number | Date,
65
+ typeCheck: boolean
66
+ ) => boolean;
65
67
  }>;
66
68
 
67
- declare const Required: {
68
- validate: (v: any) => boolean;
69
- };
70
-
71
69
  declare class Entity {
72
- table: string;
73
- cols: Record<Operation, string[]>;
74
- properties: Property[];
75
- constructor(table: string, properties: Property[]);
76
- getTable(): string;
77
- getCols(operation: Operation, stringify?: boolean, pagination?: boolean): string[] | string;
78
- getUnsafeProps(): string[];
79
- normalize(rows: Record<string, any>[]): Record<string, any>[];
80
- validate(rows: Record<string, any>[], operation: Operation | Method): string | null;
81
- private require;
82
- private control;
83
- private sanitize;
84
- private trim;
70
+ private _name;
71
+ private _unsafeProps;
72
+ private _properties;
73
+ constructor(name: string, properties: Property[]);
74
+ get name(): string;
75
+ get unsafeProps(): string[];
76
+ get properties(): Property[];
77
+ set name(name: string);
78
+ getProp(key: string): Property | undefined;
79
+ normalize(req: Request, _res: Response, next: NextFunction): void;
80
+ validate(req: Request, _res: Response, next: NextFunction): void;
85
81
  }
86
82
 
87
- declare const Messages: {
88
- missing: (key: string) => string;
89
- invalid: (key: string, type: Type) => string;
90
- };
91
-
92
83
  declare class Property {
93
84
  key: string;
94
85
  type: Type;
@@ -97,21 +88,22 @@ declare class Property {
97
88
  required: boolean;
98
89
  safe: boolean;
99
90
  typeCheck: boolean;
100
- operations: Operation[];
91
+ methods: Method[];
101
92
  sanitize: boolean;
102
93
  normalize: boolean;
103
94
  control: boolean;
104
95
  sanitizer: ((v: any) => any) | null;
105
96
  normalizer: ((v: any) => any) | null;
106
97
  controller: ((v: any) => any) | null;
107
- constructor(key: string,
98
+ constructor(
99
+ key: string,
108
100
  type: Type,
109
101
  min: number | Date,
110
102
  max: number | Date,
111
103
  required: boolean,
112
104
  safe: boolean,
113
105
  typeCheck: boolean,
114
- operations: Operation[],
106
+ methods: Method[],
115
107
  sanitize: boolean,
116
108
  normalize: boolean,
117
109
  control: boolean,
@@ -121,13 +113,11 @@ declare class Property {
121
113
  );
122
114
  }
123
115
 
124
- export type { Type, Operation, Method };
116
+ export type { Type, Method };
125
117
  export {
126
118
  Entity,
127
119
  Property,
128
- Messages,
129
120
  Types,
130
- Required,
131
- Operations
121
+ Methods,
132
122
  };
133
123
 
package/dist/antity.js CHANGED
@@ -24,10 +24,10 @@ 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, isNil, isString, isProperty, isArray, isIn, isDate, isInteger } from '@dwtechs/checkard';
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';
28
28
  import { log } from '@dwtechs/winstan';
29
29
 
30
- const Operations = ["select", "insert", "update", "merge", "delete"];
30
+ const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
31
31
 
32
32
  const Types = {
33
33
  boolean: {
@@ -112,20 +112,17 @@ const Types = {
112
112
  validate: (v, _min, _max, _typeCheck) => isObject(v)
113
113
  }
114
114
  };
115
- const Required = {
116
- validate: (v) => !isNil(v)
117
- };
118
115
 
119
116
  class Property {
120
- constructor(key, type, min, max, required, safe, typeCheck, operations, sanitize, normalize, control, sanitizer, normalizer, controller) {
121
- if (!isString(key, true))
122
- throw new Error(`Property"key" must be a string. Received ${key}`);
123
- if (!isProperty(type, Types))
117
+ constructor(key, type, min, max, required, safe, typeCheck, methods, sanitize, normalize, control, sanitizer, normalizer, controller) {
118
+ if (!isString(key, "!0"))
119
+ throw new Error(`Property "key" must be a string. Received ${key}`);
120
+ if (!isProperty(Types, type))
124
121
  throw new Error(`Property "type" must be a valid type. Received ${type}`);
125
- if (isArray(operations)) {
126
- for (const o of operations) {
127
- if (!isIn(o, Operations))
128
- throw new Error(`Property "operations" must be an array of SQL operations. Received ${o}`);
122
+ if (isArray(methods)) {
123
+ for (const m of methods) {
124
+ if (!isIn(Methods, m))
125
+ throw new Error(`Property "methods" must be an array of REST methods. Received ${m}`);
129
126
  }
130
127
  }
131
128
  this.key = key;
@@ -135,7 +132,7 @@ class Property {
135
132
  this.required = isBoolean(required) ? required : false;
136
133
  this.safe = isBoolean(safe) ? safe : true;
137
134
  this.typeCheck = isBoolean(typeCheck) ? typeCheck : false;
138
- this.operations = operations || Operations;
135
+ this.methods = methods || Methods;
139
136
  this.sanitize = isBoolean(sanitize) ? sanitize : true;
140
137
  this.normalize = isBoolean(normalize) ? normalize : false;
141
138
  this.control = isBoolean(control) ? control : true;
@@ -146,53 +143,44 @@ class Property {
146
143
  interval(val, type, integerDefault, dateDefault) {
147
144
  if (type === "date")
148
145
  return isDate(val) ? val : new Date(dateDefault);
149
- return isInteger(val, true) ? val : integerDefault;
146
+ return (isNumber(val, true) && isInteger(val, true)) ? val : integerDefault;
150
147
  }
151
148
  }
152
149
 
153
- const Messages = {
154
- missing: (key) => `Missing ${key}`,
155
- invalid: (key, type) => `Invalid ${key}, must be of type ${type}`,
156
- };
157
-
158
- const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
159
-
160
150
  class Entity {
161
- constructor(table, properties) {
162
- this.table = table;
163
- this.properties = [];
164
- this.cols = {
165
- select: [],
166
- insert: [],
167
- update: [],
168
- merge: [],
169
- delete: []
170
- };
171
- this.unsafeProps = [];
151
+ constructor(name, properties) {
152
+ this._name = name;
153
+ this._properties = [];
154
+ this._unsafeProps = [];
172
155
  for (const p of properties) {
173
- const prop = new Property(p.key, p.type, p.min, p.max, p.required, p.safe, p.typeCheck, p.operations, p.sanitize, p.normalize, p.control, p.sanitizer, p.normalizer, p.controller);
174
- this.properties.push(prop);
175
- for (const o of p.operations) {
176
- if (o === "update")
177
- this.cols[o].push(`${p.key} = $${this.cols[o].length + 1}`);
178
- else
179
- this.cols[o].push(p.key);
180
- }
156
+ const prop = new Property(p.key, p.type, p.min, p.max, p.required, p.safe, p.typeCheck, p.methods, p.sanitize, p.normalize, p.control, p.sanitizer, p.normalizer, p.controller);
157
+ this._properties.push(prop);
181
158
  if (!prop.safe)
182
- this.unsafeProps.push(prop.key);
159
+ this._unsafeProps.push(prop.key);
183
160
  }
184
161
  }
185
- getTable() {
186
- return this.table;
162
+ get name() {
163
+ return this._name;
187
164
  }
188
- getCols(operation, stringify, pagination) {
189
- const cols = pagination && operation === "select" ? [...this.cols[operation], "COUNT(*) OVER () AS total"] : this.cols[operation];
190
- return stringify ? cols.join(', ') : cols;
165
+ get unsafeProps() {
166
+ return this._unsafeProps;
191
167
  }
192
- getUnsafeProps() {
193
- return this.unsafeProps;
168
+ get properties() {
169
+ return this._properties;
194
170
  }
195
- normalize(rows) {
171
+ set name(name) {
172
+ if (!isString(name, "!0"))
173
+ throw new Error('name must be a string of length > 0');
174
+ this._name = name;
175
+ }
176
+ getProp(key) {
177
+ return this.properties.find(p => p.key === key);
178
+ }
179
+ normalize(req, _res, next) {
180
+ var _a;
181
+ const rows = (_a = req.body) === null || _a === void 0 ? void 0 : _a.rows;
182
+ if (!isObject(rows))
183
+ return next({ status: 400, msg: "Normalize: no rows found in request body" });
196
184
  for (const r of rows) {
197
185
  for (const { key, type, sanitize, normalize, sanitizer, normalizer, } of this.properties) {
198
186
  let v = r[key];
@@ -209,39 +197,50 @@ class Entity {
209
197
  }
210
198
  }
211
199
  }
212
- return rows;
200
+ next();
213
201
  }
214
- validate(rows, operation) {
215
- if (isIn(operation, Methods))
216
- operation = this.mapMethods(operation);
202
+ validate(req, _res, next) {
203
+ var _a;
204
+ const rows = req.body;
205
+ const method = req.method;
206
+ if (!isObject((_a = req.body) === null || _a === void 0 ? void 0 : _a.rows))
207
+ return next({ status: 400, msg: "Sanitize: no rows found in request body" });
208
+ if (!isIn(Methods, method))
209
+ return next({
210
+ status: 400,
211
+ msg: `Invalid REST method. Received: ${method}. Must be one of: ${Methods.toString()}`
212
+ });
217
213
  for (const r of rows) {
218
- for (const { key, type, min, max, required, typeCheck, operations, control, controller } of this.properties) {
214
+ for (const { key, type, min, max, required, typeCheck, methods, control, controller } of this.properties) {
219
215
  const v = r[key];
220
- if (isIn(operation, operations)) {
216
+ if (isIn(methods, method)) {
221
217
  if (required) {
222
218
  const rq = this.require(v, key, type);
223
219
  if (rq)
224
- return rq;
220
+ return next(rq);
225
221
  }
226
222
  if (v && control) {
227
223
  const ct = this.control(v, key, type, min, max, typeCheck, controller);
228
224
  if (ct)
229
- return ct;
225
+ return next(ct);
230
226
  }
231
227
  }
232
228
  }
233
229
  }
234
- return null;
230
+ next();
235
231
  }
236
232
  require(v, key, type) {
237
233
  log.debug(`require ${key}: ${type} = ${v}`);
238
- return Required.validate(v) ? null : Messages.missing(key);
234
+ return isNil(v) ? { status: 400, msg: `Missing ${key} of type ${type}` } : null;
239
235
  }
240
236
  control(v, key, type, min, max, typeCheck, cb) {
241
237
  log.debug(`control ${key}: ${type} = ${v}`);
238
+ let val;
242
239
  if (cb)
243
- return cb(v) ? null : Messages.invalid(key, type);
244
- return Types[type].validate(v, min, max, typeCheck) ? null : Messages.invalid(key, type);
240
+ val = cb(v);
241
+ else
242
+ val = Types[type].validate(v, min, max, typeCheck);
243
+ return val ? null : { status: 400, msg: `Invalid ${key}, must be of type ${type}` };
245
244
  }
246
245
  sanitize(v, cb) {
247
246
  if (cb)
@@ -255,33 +254,18 @@ class Entity {
255
254
  return this.trim(v);
256
255
  }
257
256
  trim(v) {
258
- if (isString(v, true))
257
+ if (isString(v, "!0"))
259
258
  return v.trim();
260
259
  if (isObject(v, true)) {
261
- if (v.has)
262
- for (const p in v) {
263
- if (v.prototype.hasOwnProperty.call(p)) {
264
- let o = v[p];
265
- if (isString(o, true))
266
- o = o.trim();
267
- }
260
+ for (const k in v) {
261
+ if (Object.prototype.hasOwnProperty.call(v, k)) {
262
+ let o = v[k];
263
+ if (isString(o, "!0", null))
264
+ o = o.trim();
268
265
  }
269
- return v;
270
- }
271
- }
272
- mapMethods(method) {
273
- switch (method) {
274
- case "GET":
275
- return Operations[0];
276
- case "PATCH":
277
- return Operations[2];
278
- case "PUT":
279
- return Operations[2];
280
- case "POST":
281
- return Operations[1];
282
- case "DELETE":
283
- return Operations[4];
266
+ }
284
267
  }
268
+ return v;
285
269
  }
286
270
  }
287
271
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwtechs/antity",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Open source library for easy entity management",
5
5
  "keywords": [
6
6
  "entities"
@@ -25,6 +25,7 @@
25
25
  "contributors": [],
26
26
  "scripts": {
27
27
  "start": "",
28
+ "prebuild": "npm install",
28
29
  "build": "node ./scripts/clear && tsc && npm run rollup && node ./scripts/copy && npm run test",
29
30
  "rollup:mjs": "rollup --config rollup.config.mjs",
30
31
  "rollup:cjs": "rollup --config rollup.config.cjs.mjs",
@@ -35,8 +36,9 @@
35
36
  "dist/"
36
37
  ],
37
38
  "dependencies": {
38
- "@dwtechs/checkard": "2.29.2",
39
- "@dwtechs/winstan": "0.3.0",
39
+ "@dwtechs/checkard": "3.2.3",
40
+ "@dwtechs/winstan": "0.4.0",
41
+ "@dwtechs/sparray": "0.1.1",
40
42
  "pg": "8.13.1"
41
43
  },
42
44
  "devDependencies": {
@@ -46,6 +48,7 @@
46
48
  "@rollup/plugin-node-resolve": "15.3.0",
47
49
  "@types/jest": "29.5.14",
48
50
  "@types/node": "22.10.1",
51
+ "@types/express": "5.0.0",
49
52
  "babel-jest": "29.7.0",
50
53
  "core-js": "3.33.0",
51
54
  "jest": "29.7.0",