@dwtechs/antity 0.3.0 → 0.5.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
@@ -50,15 +50,16 @@ $ npm i @dwtechs/antity
50
50
  import { Entity } from "@dwtechs/antity";
51
51
  import { normalizeName, normalizeNickname } from "@dwtechs/checkard";
52
52
 
53
- const entity = new Entity("consumer", "consumers", [
53
+ const entity = new Entity("consumers", [
54
54
  {
55
55
  key: "id",
56
56
  type: "integer",
57
57
  min: 0,
58
58
  max: 120,
59
59
  typeCheck: true,
60
- methods: ["get", "put", "delete"],
60
+ operations: ["select", "update", "delete"],
61
61
  required: true,
62
+ safe: true,
62
63
  sanitize: true,
63
64
  normalize: true,
64
65
  control: true,
@@ -72,8 +73,9 @@ const entity = new Entity("consumer", "consumers", [
72
73
  min: 0,
73
74
  max: 255,
74
75
  typeCheck: true,
75
- methods: ["get", "post", "put", "delete"],
76
+ operations: ["select", "insert", "update", "delete"],
76
77
  required: true,
78
+ safe: true,
77
79
  sanitize: true,
78
80
  normalize: true,
79
81
  control: true,
@@ -87,8 +89,9 @@ const entity = new Entity("consumer", "consumers", [
87
89
  min: 0,
88
90
  max: 255,
89
91
  typeCheck: true,
90
- methods: ["get", "post", "put", "delete"],
92
+ operations: ["select", "insert", "update", "delete"],
91
93
  required: true,
94
+ safe: true,
92
95
  sanitize: true,
93
96
  normalize: true,
94
97
  control: true,
@@ -102,8 +105,9 @@ const entity = new Entity("consumer", "consumers", [
102
105
  min: 0,
103
106
  max: 255,
104
107
  typeCheck: true,
105
- methods: ["get", "post", "put", "delete"],
108
+ operations: ["select", "insert", "update", "delete"],
106
109
  required: true,
110
+ safe: true,
107
111
  sanitize: true,
108
112
  normalize: true,
109
113
  control: true,
@@ -113,10 +117,9 @@ const entity = new Entity("consumer", "consumers", [
113
117
  },
114
118
  ]);
115
119
 
116
- req.body = entity.normalize(req.body);
117
- const check = entity.validate(req.body, req.method);
118
-
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
123
 
121
124
  ```
122
125
 
@@ -154,6 +157,7 @@ type Type = "boolean" |
154
157
  "node" |
155
158
  "object";
156
159
 
160
+ type Operation = "select" | "insert" | "update" | "merge" | "delete";
157
161
  type Method = "GET" | "PATCH" | "PUT" | "POST" | "DELETE";
158
162
 
159
163
  class Property {
@@ -162,8 +166,9 @@ class Property {
162
166
  min: number | Date;
163
167
  max: number | Date;
164
168
  required: boolean;
169
+ safe: boolean;
165
170
  typeCheck: boolean;
166
- methods: Method[];
171
+ operations: Operation[];
167
172
  sanitize: boolean;
168
173
  normalize: boolean;
169
174
  control: boolean;
@@ -173,18 +178,41 @@ class Property {
173
178
  };
174
179
 
175
180
  class Entity {
176
- name: string;
177
181
  table: string;
178
- cols: Record<Method, string>;
182
+ cols: Record<Operation, string[]>;
179
183
  properties: Property[];
180
- normalize(rows: Record<string, any>[]): Record<string, any>[];
181
- validate(rows: Record<string, any>[], method: Method): string | null;
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;
182
186
  getTable(): string;
183
- getCols(method: Method): 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[];
184
190
  }
185
191
 
186
192
  ```
187
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
+ ```
215
+
188
216
 
189
217
  ### Available options for a property
190
218
 
@@ -193,17 +221,18 @@ Any of these can be passed into the options object for each function.
193
221
  | Name | Type | Description | Default value |
194
222
  | :-------------- | :------------------------ | :------------------------------------------------ | :-------------- |
195
223
  | key | string | Name of the property |
196
- | type | Type | Type of the property |
197
- | min | number | Minimum value | 0
198
- | max | number | Maximum value | 999999999
199
- | required | boolean | Is this property required for these methods | false
224
+ | type | Type | Type of the property |
225
+ | min | number \| Date | Minimum value | 0 \| 1900-01-01
226
+ | max | number \| Date | Maximum value | 999999999 \| 2200-12-31
227
+ | required | boolean | Is this property required on insert | false
228
+ | safe | boolean | Is this property safe to send to the client | true
200
229
  | typeCheck | boolean | Type is checked if true | false
201
- | methods | Method[] | REST Methods that use the property | [ "GET", "PATCH", "PUT", "POST", "DELETE" ]
230
+ | operations | Operation[] | SQL DML operations concerned by the property | [ "select", "insert", "update", "merge", "delete" ]
202
231
  | sanitize | boolean | Sanitize the property if true | true
203
232
  | normalize | boolean | Normalize the property if true | false
204
233
  | control | boolean | Control the property if true | true
205
- | sanitizer | ((v:any) => any) \| null | Sanitizer function if sanitize is true | null
206
- | normalizer | ((v:any) => any) \| null | Normalizer function if normalize is true | null
234
+ | sanitizer | ((v:any) => any) \| null | Sanitizer function if sanitize is true | null
235
+ | normalizer | ((v:any) => any) \| null | Normalizer function if normalize is true | null
207
236
  | controller | ((v:any, min:number, max:number, typeCheck:boolean) => any) \| null | Controller function if control is true | null
208
237
 
209
238
  * *Min and max parameters are not used for boolean type*
package/dist/antity.d.ts CHANGED
@@ -25,7 +25,9 @@ https://github.com/DWTechs/Antity.js
25
25
  */
26
26
 
27
27
 
28
+ type Operation = typeof Operations[number];
28
29
  type Method = typeof Methods[number];
30
+
29
31
  type Type =
30
32
  "boolean" |
31
33
  "string" |
@@ -55,7 +57,8 @@ type Type =
55
57
  "node" |
56
58
  "object";
57
59
 
58
- declare const Methods: readonly ["GET", "PATCH", "PUT", "POST", "DELETE"];
60
+ declare const Operations: readonly [ "select", "insert", "update", "merge", "delete" ];
61
+ declare const Methods: readonly [ "GET", "PATCH", "PUT", "POST", "DELETE" ];
59
62
 
60
63
  declare const Types: Record<Type, {
61
64
  validate: (v: any, min: number | Date, max: number | Date, typeCheck: boolean) => boolean;
@@ -66,16 +69,15 @@ declare const Required: {
66
69
  };
67
70
 
68
71
  declare class Entity {
69
- name: string;
70
72
  table: string;
71
- cols: Record<Method, string>;
73
+ cols: Record<Operation, string[]>;
72
74
  properties: Property[];
73
- constructor(name: string, table: string, properties: Property[]);
74
- private init;
75
+ constructor(table: string, properties: Property[]);
75
76
  getTable(): string;
76
- getCols(method: Method): string;
77
+ getCols(operation: Operation, stringify?: boolean, pagination?: boolean): string[] | string;
78
+ getUnsafeProps(): string[];
77
79
  normalize(rows: Record<string, any>[]): Record<string, any>[];
78
- validate(rows: Record<string, any>[], method: Method): string | null;
80
+ validate(rows: Record<string, any>[], operation: Operation | Method): string | null;
79
81
  private require;
80
82
  private control;
81
83
  private sanitize;
@@ -90,11 +92,12 @@ declare const Messages: {
90
92
  declare class Property {
91
93
  key: string;
92
94
  type: Type;
93
- min: number;
94
- max: number;
95
+ min: number | Date;
96
+ max: number | Date;
95
97
  required: boolean;
98
+ safe: boolean;
96
99
  typeCheck: boolean;
97
- methods: Method[];
100
+ operations: Operation[];
98
101
  sanitize: boolean;
99
102
  normalize: boolean;
100
103
  control: boolean;
@@ -106,8 +109,9 @@ declare class Property {
106
109
  min: number | Date,
107
110
  max: number | Date,
108
111
  required: boolean,
112
+ safe: boolean,
109
113
  typeCheck: boolean,
110
- methods: Method[],
114
+ operations: Operation[],
111
115
  sanitize: boolean,
112
116
  normalize: boolean,
113
117
  control: boolean,
@@ -117,13 +121,13 @@ declare class Property {
117
121
  );
118
122
  }
119
123
 
120
- export type { Type, Method };
124
+ export type { Type, Operation, Method };
121
125
  export {
122
126
  Entity,
123
127
  Property,
124
128
  Messages,
125
129
  Types,
126
130
  Required,
127
- Methods
131
+ Operations
128
132
  };
129
133
 
package/dist/antity.js CHANGED
@@ -24,9 +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, 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, isNil, isString, isProperty, isArray, isIn, isDate, isInteger } from '@dwtechs/checkard';
28
+ import { log } from '@dwtechs/winstan';
28
29
 
29
- const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
30
+ const Operations = ["select", "insert", "update", "merge", "delete"];
30
31
 
31
32
  const Types = {
32
33
  boolean: {
@@ -116,24 +117,25 @@ const Required = {
116
117
  };
117
118
 
118
119
  class Property {
119
- constructor(key, type, min, max, required, typeCheck, methods, sanitize, normalize, control, sanitizer, normalizer, controller) {
120
+ constructor(key, type, min, max, required, safe, typeCheck, operations, sanitize, normalize, control, sanitizer, normalizer, controller) {
120
121
  if (!isString(key, true))
121
- throw new Error(`Property key must be a string. Received ${key}`);
122
+ throw new Error(`Property"key" must be a string. Received ${key}`);
122
123
  if (!isProperty(type, Types))
123
- throw new Error(`Property type must be a valid type. Received ${type}`);
124
- if (isArray(methods)) {
125
- for (const m of methods) {
126
- if (!isIn(m, Methods))
127
- throw new Error(`Property methods must be an array of REST Methods. Received ${m}`);
124
+ 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}`);
128
129
  }
129
130
  }
130
131
  this.key = key;
131
132
  this.type = type;
132
- this.min = isInteger(min, true) ? min : 0;
133
- this.max = isInteger(max, true) ? max : 999999999;
133
+ this.min = this.interval(min, type, 0, "1900-01-01T00:00:00Z");
134
+ this.max = this.interval(max, type, 999999999, "2200-12-31T00:00:00Z");
134
135
  this.required = isBoolean(required) ? required : false;
136
+ this.safe = isBoolean(safe) ? safe : true;
135
137
  this.typeCheck = isBoolean(typeCheck) ? typeCheck : false;
136
- this.methods = methods || Methods;
138
+ this.operations = operations || Operations;
137
139
  this.sanitize = isBoolean(sanitize) ? sanitize : true;
138
140
  this.normalize = isBoolean(normalize) ? normalize : false;
139
141
  this.control = isBoolean(control) ? control : true;
@@ -141,6 +143,11 @@ class Property {
141
143
  this.normalizer = isFunction(normalizer) ? normalizer : null;
142
144
  this.controller = isFunction(controller) ? controller : null;
143
145
  }
146
+ interval(val, type, integerDefault, dateDefault) {
147
+ if (type === "date")
148
+ return isDate(val) ? val : new Date(dateDefault);
149
+ return isInteger(val, true) ? val : integerDefault;
150
+ }
144
151
  }
145
152
 
146
153
  const Messages = {
@@ -148,57 +155,71 @@ const Messages = {
148
155
  invalid: (key, type) => `Invalid ${key}, must be of type ${type}`,
149
156
  };
150
157
 
158
+ const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
159
+
151
160
  class Entity {
152
- constructor(name, table, properties) {
153
- this.name = name;
161
+ constructor(table, properties) {
154
162
  this.table = table;
163
+ this.properties = [];
155
164
  this.cols = {
156
- GET: "",
157
- POST: "",
158
- PUT: "",
159
- PATCH: "",
160
- DELETE: "",
165
+ select: [],
166
+ insert: [],
167
+ update: [],
168
+ merge: [],
169
+ delete: []
161
170
  };
162
- this.properties = this.init(properties);
163
- }
164
- init(properties) {
165
- const props = [];
171
+ this.unsafeProps = [];
166
172
  for (const p of properties) {
167
- props.push(new Property(p.key, p.type, p.min, p.max, p.required, p.typeCheck, p.methods, p.sanitize, p.normalize, p.control, p.sanitizer, p.normalizer, p.controller));
168
- for (const m of p.methods) {
169
- this.cols[m] += this.cols[m].length ? `, ${p.key}` : `${p.key}`;
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);
170
180
  }
181
+ if (!prop.safe)
182
+ this.unsafeProps.push(prop.key);
171
183
  }
172
- return props;
173
184
  }
174
185
  getTable() {
175
186
  return this.table;
176
187
  }
177
- getCols(method) {
178
- return this.cols[method];
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;
191
+ }
192
+ getUnsafeProps() {
193
+ return this.unsafeProps;
179
194
  }
180
195
  normalize(rows) {
181
196
  for (const r of rows) {
182
- for (const { key, sanitize, normalize, sanitizer, normalizer, } of this.properties) {
197
+ for (const { key, type, sanitize, normalize, sanitizer, normalizer, } of this.properties) {
183
198
  let v = r[key];
184
199
  if (v) {
185
- if (sanitize)
200
+ if (sanitize) {
201
+ log.debug(`sanitize ${key}: ${type} = ${v}`);
186
202
  v = this.sanitize(v, sanitizer);
187
- if (normalize && normalizer)
203
+ }
204
+ if (normalize && normalizer) {
205
+ log.debug(`normalize ${key}: ${type} = ${v}`);
188
206
  v = normalizer(v);
207
+ }
189
208
  r[key] = v;
190
209
  }
191
210
  }
192
211
  }
193
212
  return rows;
194
213
  }
195
- validate(rows, method) {
214
+ validate(rows, operation) {
215
+ if (isIn(operation, Methods))
216
+ operation = this.mapMethods(operation);
196
217
  for (const r of rows) {
197
- for (const { key, type, min, max, required, typeCheck, methods, control, controller } of this.properties) {
218
+ for (const { key, type, min, max, required, typeCheck, operations, control, controller } of this.properties) {
198
219
  const v = r[key];
199
- if (method && isIn(method, methods)) {
220
+ if (isIn(operation, operations)) {
200
221
  if (required) {
201
- const rq = this.require(v, key);
222
+ const rq = this.require(v, key, type);
202
223
  if (rq)
203
224
  return rq;
204
225
  }
@@ -212,10 +233,12 @@ class Entity {
212
233
  }
213
234
  return null;
214
235
  }
215
- require(v, key) {
236
+ require(v, key, type) {
237
+ log.debug(`require ${key}: ${type} = ${v}`);
216
238
  return Required.validate(v) ? null : Messages.missing(key);
217
239
  }
218
240
  control(v, key, type, min, max, typeCheck, cb) {
241
+ log.debug(`control ${key}: ${type} = ${v}`);
219
242
  if (cb)
220
243
  return cb(v) ? null : Messages.invalid(key, type);
221
244
  return Types[type].validate(v, min, max, typeCheck) ? null : Messages.invalid(key, type);
@@ -246,6 +269,20 @@ class Entity {
246
269
  return v;
247
270
  }
248
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];
284
+ }
285
+ }
249
286
  }
250
287
 
251
288
  export { Entity };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwtechs/antity",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Open source library for easy entity management",
5
5
  "keywords": [
6
6
  "entities"
@@ -28,14 +28,16 @@
28
28
  "build": "node ./scripts/clear && tsc && npm run rollup && node ./scripts/copy && npm run test",
29
29
  "rollup:mjs": "rollup --config rollup.config.mjs",
30
30
  "rollup:cjs": "rollup --config rollup.config.cjs.mjs",
31
- "rollup": "npm run rollup:mjs && npm run rollup:cjs",
31
+ "rollup": "npm run rollup:mjs",
32
32
  "test": "jest --coverage"
33
33
  },
34
34
  "files": [
35
35
  "dist/"
36
36
  ],
37
37
  "dependencies": {
38
- "@dwtechs/checkard": "2.29.2"
38
+ "@dwtechs/checkard": "2.29.2",
39
+ "@dwtechs/winstan": "0.3.0",
40
+ "pg": "8.13.1"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@babel/core": "7.26.0",
@@ -43,6 +45,7 @@
43
45
  "@rollup/plugin-commonjs": "28.0.1",
44
46
  "@rollup/plugin-node-resolve": "15.3.0",
45
47
  "@types/jest": "29.5.14",
48
+ "@types/node": "22.10.1",
46
49
  "babel-jest": "29.7.0",
47
50
  "core-js": "3.33.0",
48
51
  "jest": "29.7.0",
@@ -1,556 +0,0 @@
1
- /*
2
- MIT License
3
-
4
- Copyright (c) 2024 DWTechs
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
23
-
24
- https://github.com/DWTechs/Antity.js
25
- */
26
-
27
- 'use strict';
28
-
29
- /*
30
- MIT License
31
-
32
- Copyright (c) 2009 DWTechs
33
-
34
- Permission is hereby granted, free of charge, to any person obtaining a copy
35
- of this software and associated documentation files (the "Software"), to deal
36
- in the Software without restriction, including without limitation the rights
37
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38
- copies of the Software, and to permit persons to whom the Software is
39
- furnished to do so, subject to the following conditions:
40
-
41
- The above copyright notice and this permission notice shall be included in all
42
- copies or substantial portions of the Software.
43
-
44
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50
- SOFTWARE.
51
-
52
- https://github.com/DWTechs/Checkard.js
53
- */
54
-
55
- function isNumeric(n) {
56
- return !isNaN(n - parseFloat(n));
57
- }
58
- function getTag(t) {
59
- return t == null ? t === undefined ? '[object Undefined]' : '[object Null]' : toString.call(t);
60
- }
61
-
62
- function isBoolean(b) {
63
- return typeof b === "boolean";
64
- }
65
- function isNumber(n, type = true) {
66
- return !isSymbol(n) && !((n === null || n === void 0 ? void 0 : n.constructor) === Array) && (type ? Number(n) === n : isNumeric(n));
67
- }
68
- function isSymbol(s) {
69
- const type = typeof s;
70
- return type === 'symbol' || (type === 'object' && s != null && getTag(s) === '[object Symbol]');
71
- }
72
- function isNil(n) {
73
- return n == null;
74
- }
75
-
76
- function isFunction(func) {
77
- return Boolean(func && getTag(func) === "[object Function]");
78
- }
79
-
80
- const comparisons = {
81
- '=': (a, b) => a == b,
82
- '<': (a, b) => a < b,
83
- '>': (a, b) => a > b,
84
- '<=': (a, b) => a <= b,
85
- '>=': (a, b) => a >= b
86
- };
87
-
88
- function isAscii(c, ext = true) {
89
- return isInteger(c, false) && ((ext && c >= 0 && c <= 255) || (c >= 0 && c <= 127));
90
- }
91
- function isInteger(n, type = true) {
92
- if (!isNumber(n, type))
93
- return false;
94
- const int = Number.parseInt(String(n), 10);
95
- return type ? n === int : n == int;
96
- }
97
- function isFloat(n, type = true) {
98
- if (isSymbol(n))
99
- return false;
100
- const modulo = n % 1 !== 0;
101
- return type ? (Number(n) === n && modulo) : (Number(n) == n && modulo);
102
- }
103
- function isEven(n, type = true) {
104
- return isInteger(n, type) && !(n & 1);
105
- }
106
- function isOdd(n, type = true) {
107
- return isInteger(n, type) && Boolean(n & 1);
108
- }
109
- function isOrigin(n, type = true) {
110
- return type ? n === 0 : n == 0;
111
- }
112
- function isPositive(n, type = true) {
113
- return isNumber(n, type) && n > 0;
114
- }
115
- function isNegative(n, type = true) {
116
- return isNumber(n, type) && n < 0;
117
- }
118
- function isPowerOfTwo(n, type = true) {
119
- return isInteger(n, type) && !isOrigin(n, type) && (n & (n - 1)) === 0;
120
- }
121
-
122
- function isValidNumber(n, min = -999999999, max = 999999999, type = true) {
123
- return isNumber(n, type) && n >= min && n <= max;
124
- }
125
- function isValidInteger(n, min = -999999999, max = 999999999, type = true) {
126
- return isInteger(n, type) && n >= min && n <= max;
127
- }
128
- function isValidFloat(n, min = -999999999.9, max = 999999999.9, type = true) {
129
- return isFloat(n, type) && n >= min && n <= max;
130
- }
131
-
132
- function isArray(a, comp, len) {
133
- return (a === null || a === void 0 ? void 0 : a.constructor) === Array ? (comp && isValidInteger(len, 0, 999999999)) ? comparisons.hasOwnProperty(comp) ? comparisons[comp](a.length, len) : false : true : false;
134
- }
135
- function isArrayOfLength(a, min = -999999999, max = 999999999) {
136
- if (isArray(a, null, null)) {
137
- const n = a.length;
138
- return n >= min && n <= max;
139
- }
140
- return false;
141
- }
142
- function isIn(val, arr) {
143
- return isArray(arr, '>', 0) ? arr.includes(val) : false;
144
- }
145
-
146
- function isString(s, required = false) {
147
- return typeof s === "string" && (required ? !!s : true);
148
- }
149
- function isStringOfLength(s, min = 0, max = 999999999) {
150
- if (isString(s, false)) {
151
- const l = s.length;
152
- return l >= min && l <= max;
153
- }
154
- return false;
155
- }
156
- function isJson(s) {
157
- if (!isString(s))
158
- return false;
159
- try {
160
- JSON.parse(s);
161
- }
162
- catch (e) {
163
- return false;
164
- }
165
- return true;
166
- }
167
- function isRegex(r, type = true) {
168
- if (type)
169
- return r instanceof RegExp;
170
- try {
171
- new RegExp(r);
172
- }
173
- catch (e) {
174
- return false;
175
- }
176
- return true;
177
- }
178
- const emailReg = /^(?=[a-z0-9@.!$%&'*+\/=?^_‘{|}~-]{6,254}$)(?=[a-z0-9.!#$%&'*+\/=?^_‘{|}~-]{1,64}@)[a-z0-9!#$%&'*+\/=?^‘{|}~]+(?:[\._-][a-z0-9!#$%&'*+\/=?^‘{|}~]+)*@(?:(?=[a-z0-9-]{1,63}\.)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?=[a-z0-9-]{2,63}$)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
179
- function isEmail(e) {
180
- return !isSymbol(e) && emailReg.test(String(e).toLowerCase());
181
- }
182
- const ipReg = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
183
- function isIpAddress(i) {
184
- return !isSymbol(i) && ipReg.test(i);
185
- }
186
- const b64Reg = /^[A-Za-z0-9\-_]+={0,2}$/;
187
- function isJWT(t) {
188
- if (!isString(t))
189
- return false;
190
- const p = t.split('.');
191
- if (p.length !== 3)
192
- return false;
193
- const header = p[0];
194
- const payload = p[1];
195
- const signature = p[3];
196
- if (b64Reg.test(header) && b64Reg.test(payload) && b64Reg.test(signature)) {
197
- try {
198
- return isJson(atob(header)) && isJson(atob(payload));
199
- }
200
- catch (e) {
201
- return false;
202
- }
203
- }
204
- return false;
205
- }
206
- const slugReg = /^[^\s-_](?!.*?[-_]{2,})[a-z0-9-\\][^\s]*[^-_\s]$/;
207
- function isSlug(s) {
208
- return isString(s) && slugReg.test(s);
209
- }
210
- const hexadecimal = /^(#|0x|0h)?[0-9A-F]+$/i;
211
- function isHexadecimal(s) {
212
- return isString(s) && hexadecimal.test(s);
213
- }
214
-
215
- function isObject(o, empty = false) {
216
- return o !== null && typeof o === "object" && !isArray(o) && (empty ? !!Object.keys(o).length : true);
217
- }
218
- function isProperty(val, obj) {
219
- const v = String(val);
220
- return isString(v, true) && isObject(obj) ? Object.keys(obj).includes(v) : false;
221
- }
222
-
223
- function isHtmlElement(h) {
224
- return Boolean(typeof HTMLElement === "object"
225
- ? h instanceof HTMLElement
226
- : h &&
227
- typeof h === "object" &&
228
- h.nodeType === 1 &&
229
- typeof h.nodeName === "string");
230
- }
231
- function isHtmlEventAttribute(h) {
232
- switch (h) {
233
- case "onafterprint":
234
- case "onbeforeprint":
235
- case "onbeforeunload":
236
- case "onerror":
237
- case "onhashchange":
238
- case "onload":
239
- case "onmessage":
240
- case "onoffline":
241
- case "ononline":
242
- case "onpagehide":
243
- case "onpageshow":
244
- case "onpopstate":
245
- case "onresize":
246
- case "onstorage":
247
- case "onunload":
248
- case "onblur":
249
- case "onchange":
250
- case "oncontextmenu":
251
- case "onfocus":
252
- case "oninput":
253
- case "oninvalid":
254
- case "onreset":
255
- case "onsearch":
256
- case "onselect":
257
- case "onsubmit":
258
- case "onkeydown":
259
- case "onkeypress":
260
- case "onkeyup":
261
- case "onclick":
262
- case "ondblclick":
263
- case "onmousedown":
264
- case "onmousemove":
265
- case "onmouseout":
266
- case "onmouseover":
267
- case "onmouseup":
268
- case "onmousewheel":
269
- case "onwheel":
270
- case "ondrag":
271
- case "ondragend":
272
- case "ondragenter":
273
- case "ondragleave":
274
- case "ondragover":
275
- case "ondragstart":
276
- case "ondrop":
277
- case "onscroll":
278
- case "oncopy":
279
- case "oncut":
280
- case "onpaste":
281
- case "onabort":
282
- case "oncanplay":
283
- case "oncanplaythrough":
284
- case "oncuechange":
285
- case "ondurationchange":
286
- case "onemptied":
287
- case "onended":
288
- case "onerror":
289
- case "onloadeddata":
290
- case "onloadedmetadata":
291
- case "onloadstart":
292
- case "onpause":
293
- case "onplay":
294
- case "onplaying":
295
- case "onprogress":
296
- case "onratechange":
297
- case "onseeked":
298
- case "onseeking":
299
- case "onstalled":
300
- case "onsuspend":
301
- case "ontimeupdate":
302
- case "onvolumechange":
303
- case "onwaiting":
304
- case "ontoggle":
305
- return true;
306
- default:
307
- return false;
308
- }
309
- }
310
- function isNode(n) {
311
- return Boolean(typeof Node === "object"
312
- ? n instanceof Node
313
- : n &&
314
- typeof n === "object" &&
315
- typeof n.nodeType === "number" &&
316
- typeof n.nodeName === "string");
317
- }
318
-
319
- function isDate(d) {
320
- return !isNaN(d) && d instanceof Date;
321
- }
322
- const minDate = new Date('1/1/1900');
323
- const maxDate = new Date('1/1/2200');
324
- function isValidDate(d, min = minDate, max = maxDate) {
325
- return isDate(d) && d >= min && d <= max;
326
- }
327
- function isTimestamp(t, type = true) {
328
- return isInteger(t, type) && isNumeric(new Date(parseInt(t + '')).getTime());
329
- }
330
- function isValidTimestamp(t, min = -2208989361000, max = 7258114800000, type = true) {
331
- return isTimestamp(t, type) && t >= min && t <= max;
332
- }
333
-
334
- const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
335
-
336
- const Types = {
337
- boolean: {
338
- validate: (v, _min, _max, _typeCheck) => isBoolean(v)
339
- },
340
- string: {
341
- validate: (v, min, max, _typeCheck) => isStringOfLength(v, min, max)
342
- },
343
- number: {
344
- validate: (v, min, max, typeCheck) => isValidNumber(v, min || undefined, max || undefined, typeCheck || undefined)
345
- },
346
- integer: {
347
- validate: (v, min, max, typeCheck) => isValidInteger(v, min || undefined, max || undefined, typeCheck || undefined)
348
- },
349
- float: {
350
- validate: (v, min, max, typeCheck) => isValidFloat(v, min || undefined, max || undefined, typeCheck || undefined)
351
- },
352
- even: {
353
- validate: (v, _min, _max, typeCheck) => isEven(v, typeCheck || undefined)
354
- },
355
- odd: {
356
- validate: (v, _min, _max, typeCheck) => isOdd(v, typeCheck || undefined)
357
- },
358
- positive: {
359
- validate: (v, _min, _max, typeCheck) => isPositive(v, typeCheck || undefined)
360
- },
361
- negative: {
362
- validate: (v, _min, _max, typeCheck) => isNegative(v, typeCheck || undefined)
363
- },
364
- powerOfTwo: {
365
- validate: (v, _min, _max, typeCheck) => isPowerOfTwo(v, typeCheck || undefined)
366
- },
367
- ascii: {
368
- validate: (v, _min, _max, typeCheck) => isAscii(v, typeCheck || undefined)
369
- },
370
- array: {
371
- validate: (v, min, max, _typeCheck) => isArrayOfLength(v, min || undefined, max || undefined)
372
- },
373
- email: {
374
- validate: (v, _min, _max, _typeCheck) => isEmail(v)
375
- },
376
- regex: {
377
- validate: (v, _min, _max, typeCheck) => isRegex(v, typeCheck || undefined)
378
- },
379
- json: {
380
- validate: (v, _min, _max, _typeCheck) => isJson(v)
381
- },
382
- jwt: {
383
- validate: (v, _min, _max, _typeCheck) => isJWT(v)
384
- },
385
- symbol: {
386
- validate: (v, _min, _max, _typeCheck) => isSymbol(v)
387
- },
388
- ipAddress: {
389
- validate: (v, _min, _max, _typeCheck) => isIpAddress(v)
390
- },
391
- slug: {
392
- validate: (v, _min, _max, _typeCheck) => isSlug(v)
393
- },
394
- hexadecimal: {
395
- validate: (v, _min, _max, _typeCheck) => isHexadecimal(v)
396
- },
397
- date: {
398
- validate: (v, min, max, _typeCheck) => isValidDate(v, min || undefined, max || undefined)
399
- },
400
- timestamp: {
401
- validate: (v, min, max, typeCheck) => isValidTimestamp(v, min || undefined, max || undefined, typeCheck || undefined)
402
- },
403
- function: {
404
- validate: (v, _min, _max, _typeCheck) => isFunction(v)
405
- },
406
- htmlElement: {
407
- validate: (v, _min, _max, _typeCheck) => isHtmlElement(v)
408
- },
409
- htmlEventAttribute: {
410
- validate: (v, _min, _max, _typeCheck) => isHtmlEventAttribute(v)
411
- },
412
- node: {
413
- validate: (v, _min, _max, _typeCheck) => isNode(v)
414
- },
415
- object: {
416
- validate: (v, _min, _max, _typeCheck) => isObject(v)
417
- }
418
- };
419
- const Required = {
420
- validate: (v) => !isNil(v)
421
- };
422
-
423
- class Property {
424
- constructor(key, type, min, max, required, typeCheck, methods, sanitize, normalize, control, sanitizer, normalizer, controller) {
425
- if (!isString(key, true))
426
- throw new Error(`Property key must be a string. Received ${key}`);
427
- if (!isProperty(type, Types))
428
- throw new Error(`Property type must be a valid type. Received ${type}`);
429
- if (isArray(methods)) {
430
- for (const m of methods) {
431
- if (!isIn(m, Methods))
432
- throw new Error(`Property methods must be an array of REST Methods. Received ${m}`);
433
- }
434
- }
435
- this.key = key;
436
- this.type = type;
437
- this.min = isInteger(min, true) ? min : 0;
438
- this.max = isInteger(max, true) ? max : 999999999;
439
- this.required = isBoolean(required) ? required : false;
440
- this.typeCheck = isBoolean(typeCheck) ? typeCheck : false;
441
- this.methods = methods || Methods;
442
- this.sanitize = isBoolean(sanitize) ? sanitize : true;
443
- this.normalize = isBoolean(normalize) ? normalize : false;
444
- this.control = isBoolean(control) ? control : true;
445
- this.sanitizer = isFunction(sanitizer) ? sanitizer : null;
446
- this.normalizer = isFunction(normalizer) ? normalizer : null;
447
- this.controller = isFunction(controller) ? controller : null;
448
- }
449
- }
450
-
451
- const Messages = {
452
- missing: (key) => `Missing ${key}`,
453
- invalid: (key, type) => `Invalid ${key}, must be of type ${type}`,
454
- };
455
-
456
- class Entity {
457
- constructor(name, table, properties) {
458
- this.name = name;
459
- this.table = table;
460
- this.cols = {
461
- GET: "",
462
- POST: "",
463
- PUT: "",
464
- PATCH: "",
465
- DELETE: "",
466
- };
467
- this.properties = this.init(properties);
468
- }
469
- init(properties) {
470
- const props = [];
471
- for (const p of properties) {
472
- props.push(new Property(p.key, p.type, p.min, p.max, p.required, p.typeCheck, p.methods, p.sanitize, p.normalize, p.control, p.sanitizer, p.normalizer, p.controller));
473
- for (const m of p.methods) {
474
- this.cols[m] += this.cols[m].length ? `, ${p.key}` : `${p.key}`;
475
- }
476
- }
477
- return props;
478
- }
479
- getTable() {
480
- return this.table;
481
- }
482
- getCols(method) {
483
- return this.cols[method];
484
- }
485
- normalize(rows) {
486
- for (const r of rows) {
487
- for (const { key, sanitize, normalize, sanitizer, normalizer, } of this.properties) {
488
- let v = r[key];
489
- if (v) {
490
- if (sanitize)
491
- v = this.sanitize(v, sanitizer);
492
- if (normalize && normalizer)
493
- v = normalizer(v);
494
- r[key] = v;
495
- }
496
- }
497
- }
498
- return rows;
499
- }
500
- validate(rows, method) {
501
- for (const r of rows) {
502
- for (const { key, type, min, max, required, typeCheck, methods, control, controller } of this.properties) {
503
- const v = r[key];
504
- if (method && isIn(method, methods)) {
505
- if (required) {
506
- const rq = this.require(v, key);
507
- if (rq)
508
- return rq;
509
- }
510
- if (v && control) {
511
- const ct = this.control(v, key, type, min, max, typeCheck, controller);
512
- if (ct)
513
- return ct;
514
- }
515
- }
516
- }
517
- }
518
- return null;
519
- }
520
- require(v, key) {
521
- return Required.validate(v) ? null : Messages.missing(key);
522
- }
523
- control(v, key, type, min, max, typeCheck, cb) {
524
- if (cb)
525
- return cb(v) ? null : Messages.invalid(key, type);
526
- return Types[type].validate(v, min, max, typeCheck) ? null : Messages.invalid(key, type);
527
- }
528
- sanitize(v, cb) {
529
- if (cb)
530
- return cb(v);
531
- if (isArray(v, null, null)) {
532
- for (let d of v) {
533
- d = this.trim(d);
534
- }
535
- return v;
536
- }
537
- return this.trim(v);
538
- }
539
- trim(v) {
540
- if (isString(v, true))
541
- return v.trim();
542
- if (isObject(v, true)) {
543
- if (v.has)
544
- for (const p in v) {
545
- if (v.prototype.hasOwnProperty.call(p)) {
546
- let o = v[p];
547
- if (isString(o, true))
548
- o = o.trim();
549
- }
550
- }
551
- return v;
552
- }
553
- }
554
- }
555
-
556
- exports.Entity = Entity;