@dwtechs/antity 0.1.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/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/antity.cjs.js +282 -0
- package/dist/antity.d.ts +113 -0
- package/dist/antity.js +177 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 DWTechs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
|
|
2
|
+
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
[](https://www.npmjs.com/package/@dwtechs/antity)
|
|
4
|
+
[](https://www.npmjs.com/package/@dwtechs/antity)
|
|
5
|
+
[](https://www.npmjs.com/package/@dwtechs/antity)
|
|
6
|
+
|
|
7
|
+
- [Synopsis](#synopsis)
|
|
8
|
+
- [Support](#support)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Usage](#usage)
|
|
11
|
+
- [ES6](#es6)
|
|
12
|
+
- [API Reference](#api-reference)
|
|
13
|
+
- [Contributors](#contributors)
|
|
14
|
+
- [Stack](#stack)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Synopsis
|
|
18
|
+
|
|
19
|
+
**[Antity.js](https://github.com/DWTechs/Antity.js)** is an Open source library for easy entity management.
|
|
20
|
+
|
|
21
|
+
- Only 1 small dependency to check inputs variables
|
|
22
|
+
- Very lightweight
|
|
23
|
+
- Thoroughly tested
|
|
24
|
+
- Works in Javascript, Typescript
|
|
25
|
+
- Can be used as EcmaScrypt module
|
|
26
|
+
- Written in Typescript
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Support
|
|
30
|
+
|
|
31
|
+
- node: 22
|
|
32
|
+
|
|
33
|
+
This is the oldest targeted versions. The library should work properly on older versions of Node.js but we do not support it officially.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
$ npm i @dwtechs/antity
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### ES6 / TypeScript
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
|
|
50
|
+
import { Entity } from "@dwtechs/antity";
|
|
51
|
+
import { normalizeName, normalizeNickname } from "@dwtechs/checkard";
|
|
52
|
+
|
|
53
|
+
const entity = new Entity("consumer", "consumers", [
|
|
54
|
+
{
|
|
55
|
+
key: "id",
|
|
56
|
+
type: "integer",
|
|
57
|
+
min: 0,
|
|
58
|
+
max: 120,
|
|
59
|
+
typeCheck: true,
|
|
60
|
+
methods: ["get", "put", "delete"],
|
|
61
|
+
required: true,
|
|
62
|
+
sanitize: true,
|
|
63
|
+
normalize: true,
|
|
64
|
+
control: true,
|
|
65
|
+
sanitizer: null,
|
|
66
|
+
normalizer: null,
|
|
67
|
+
controller: null,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: "firstName",
|
|
71
|
+
type: "string",
|
|
72
|
+
min: 0,
|
|
73
|
+
max: 255,
|
|
74
|
+
typeCheck: true,
|
|
75
|
+
methods: ["get", "post", "put", "delete"],
|
|
76
|
+
required: true,
|
|
77
|
+
sanitize: true,
|
|
78
|
+
normalize: true,
|
|
79
|
+
control: true,
|
|
80
|
+
sanitizer: null,
|
|
81
|
+
normalizer: normalizeName,
|
|
82
|
+
controller: null,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
key: "lastName",
|
|
86
|
+
type: "string",
|
|
87
|
+
min: 0,
|
|
88
|
+
max: 255,
|
|
89
|
+
typeCheck: true,
|
|
90
|
+
methods: ["get", "post", "put", "delete"],
|
|
91
|
+
required: true,
|
|
92
|
+
sanitize: true,
|
|
93
|
+
normalize: true,
|
|
94
|
+
control: true,
|
|
95
|
+
sanitizer: null,
|
|
96
|
+
normalizer: normalizeName,
|
|
97
|
+
controller: null,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: "nickname",
|
|
101
|
+
type: "string",
|
|
102
|
+
min: 0,
|
|
103
|
+
max: 255,
|
|
104
|
+
typeCheck: true,
|
|
105
|
+
methods: ["get", "post", "put", "delete"],
|
|
106
|
+
required: true,
|
|
107
|
+
sanitize: true,
|
|
108
|
+
normalize: true,
|
|
109
|
+
control: true,
|
|
110
|
+
sanitizer: null,
|
|
111
|
+
normalizer: normalizeNickname,
|
|
112
|
+
controller: null,
|
|
113
|
+
},
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
req.body = entity.normalize(req.body);
|
|
117
|
+
const check = entity.validate(req.body, req.method);
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
## API Reference
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
|
|
127
|
+
type Type = "boolean" | "string" | "number" | "integer" | "array";
|
|
128
|
+
type Method = "GET" | "PATCH" | "PUT" | "POST" | "DELETE";
|
|
129
|
+
|
|
130
|
+
class Property {
|
|
131
|
+
key: string;
|
|
132
|
+
type: Type;
|
|
133
|
+
min: number;
|
|
134
|
+
max: number;
|
|
135
|
+
required: boolean;
|
|
136
|
+
typeCheck: boolean;
|
|
137
|
+
verbs: Verb[];
|
|
138
|
+
sanitize: boolean;
|
|
139
|
+
normalize: boolean;
|
|
140
|
+
control: boolean;
|
|
141
|
+
sanitizer: Function | null;
|
|
142
|
+
normalizer: Function | null;
|
|
143
|
+
controller: Function | null;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
class Entity {
|
|
147
|
+
name: string;
|
|
148
|
+
table: string;
|
|
149
|
+
properties: Property[];
|
|
150
|
+
normalize(rows: Record<string, any>[]): Record<string, any>[];
|
|
151
|
+
validate(rows: Record<string, any>[], method: Method): string | null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
### Available options for a property
|
|
158
|
+
|
|
159
|
+
Any of these can be passed into the options object for each function.
|
|
160
|
+
|
|
161
|
+
| Name | Type | Description | Default value |
|
|
162
|
+
| :-------------- | :------------------------ | :------------------------------------------------ | :-------------- |
|
|
163
|
+
| key | string | Name of the property |
|
|
164
|
+
| type | Type | Type of the property |
|
|
165
|
+
| min | number | Minimum value | 0
|
|
166
|
+
| max | number | Maximum value | 999999999
|
|
167
|
+
| required | boolean | Is this property required for these methods | false
|
|
168
|
+
| typeCheck | boolean | Type is checked if true | false
|
|
169
|
+
| methods | Method[] | REST Methods that use the property | [ "GET", "PATCH", "PUT", "POST", "DELETE" ]
|
|
170
|
+
| sanitize | boolean | Sanitize the property if true | true
|
|
171
|
+
| normalize | boolean | Normalize the property if true | false
|
|
172
|
+
| control | boolean | Control the property if true | true
|
|
173
|
+
| sanitizer | ((v:any) => any) \| null | Sanitizer function if sanitize is true | null
|
|
174
|
+
| normalizer | ((v:any) => any) \| null | Normalizer function if normalize is true | null
|
|
175
|
+
| controller | ((v:any, min:number, max:number, typeCheck:boolean) => any) \| null | Controller function if control is true | null
|
|
176
|
+
|
|
177
|
+
* *Min and max parameters are not used for boolean type*
|
|
178
|
+
* *TypeCheck Parameter is not used for boolean, string and array types*
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## Contributors
|
|
182
|
+
|
|
183
|
+
Antity.js is still in development and we would be glad to get all the help you can provide.
|
|
184
|
+
To contribute please read **[contributor.md](https://github.com/DWTechs/Antity.js/blob/main/contributor.md)** for detailed installation guide.
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## Stack
|
|
188
|
+
|
|
189
|
+
| Purpose | Choice | Motivation |
|
|
190
|
+
| :-------------- | :------------------------------------------: | -------------------------------------------------------------: |
|
|
191
|
+
| repository | [Github](https://github.com/) | hosting for software development version control using Git |
|
|
192
|
+
| package manager | [npm](https://www.npmjs.com/get-npm) | default node.js package manager |
|
|
193
|
+
| language | [TypeScript](https://www.typescriptlang.org) | static type checking along with the latest ECMAScript features |
|
|
194
|
+
| module bundler | [Rollup](https://rollupjs.org) | advanced module bundler for ES6 modules |
|
|
195
|
+
| unit testing | [Jest](https://jestjs.io/) | delightful testing with a focus on simplicity |
|
|
@@ -0,0 +1,282 @@
|
|
|
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
|
+
function isInteger(n, type = true) {
|
|
88
|
+
if (!isNumber(n, type))
|
|
89
|
+
return false;
|
|
90
|
+
const int = Number.parseInt(String(n), 10);
|
|
91
|
+
return type ? n === int : n == int;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isValidNumber(n, min = -999999999, max = 999999999, type = true) {
|
|
95
|
+
return isNumber(n, type) && n >= min && n <= max;
|
|
96
|
+
}
|
|
97
|
+
function isValidInteger(n, min = -999999999, max = 999999999, type = true) {
|
|
98
|
+
return isInteger(n, type) && n >= min && n <= max;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isArray(a, comp, len) {
|
|
102
|
+
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;
|
|
103
|
+
}
|
|
104
|
+
function isArrayOfLength(a, min = -999999999, max = 999999999) {
|
|
105
|
+
if (isArray(a, null, null)) {
|
|
106
|
+
const n = a.length;
|
|
107
|
+
return n >= min && n <= max;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function isIn(val, arr) {
|
|
112
|
+
return isArray(arr, '>', 0) ? arr.includes(val) : false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isString(s, required = false) {
|
|
116
|
+
return typeof s === "string" && (required ? !!s : true);
|
|
117
|
+
}
|
|
118
|
+
function isStringOfLength(s, min = 0, max = 999999999) {
|
|
119
|
+
if (isString(s, false)) {
|
|
120
|
+
const l = s.length;
|
|
121
|
+
return l >= min && l <= max;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isObject(o, empty = false) {
|
|
127
|
+
return o !== null && typeof o === "object" && !isArray(o) && (empty ? !!Object.keys(o).length : true);
|
|
128
|
+
}
|
|
129
|
+
function isProperty(val, obj) {
|
|
130
|
+
const v = String(val);
|
|
131
|
+
return isString(v, true) && isObject(obj) ? Object.keys(obj).includes(v) : false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
|
|
135
|
+
|
|
136
|
+
const Types = {
|
|
137
|
+
boolean: {
|
|
138
|
+
validate: (v, _min, _max, _typeCheck) => isBoolean(v)
|
|
139
|
+
},
|
|
140
|
+
string: {
|
|
141
|
+
validate: (v, min, max, _typeCheck) => isStringOfLength(v, min, max)
|
|
142
|
+
},
|
|
143
|
+
number: {
|
|
144
|
+
validate: (v, min, max, typeCheck) => isValidNumber(v, min || undefined, max || undefined, typeCheck || undefined)
|
|
145
|
+
},
|
|
146
|
+
integer: {
|
|
147
|
+
validate: (v, min, max, typeCheck) => isValidInteger(v, min || undefined, max || undefined, typeCheck || undefined)
|
|
148
|
+
},
|
|
149
|
+
array: {
|
|
150
|
+
validate: (v, min, max, _typeCheck) => isArrayOfLength(v, min, max)
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const Required = {
|
|
154
|
+
validate: (v) => !isNil(v)
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
class Property {
|
|
158
|
+
constructor(key, type, min, max, required, typeCheck, methods, sanitize, normalize, control, sanitizer, normalizer, controller) {
|
|
159
|
+
if (!isString(key, true))
|
|
160
|
+
throw new Error(`Property key must be a string. Received ${key}`);
|
|
161
|
+
if (!isProperty(type, Types))
|
|
162
|
+
throw new Error(`Property type must be a valid type. Received ${type}`);
|
|
163
|
+
if (isArray(methods)) {
|
|
164
|
+
for (const m of methods) {
|
|
165
|
+
if (!isIn(m, Methods))
|
|
166
|
+
throw new Error(`Property methods must be an array of REST Methods. Received ${m}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
this.key = key;
|
|
170
|
+
this.type = type;
|
|
171
|
+
this.min = isInteger(min, true) ? min : 0;
|
|
172
|
+
this.max = isInteger(max, true) ? max : 999999999;
|
|
173
|
+
this.required = isBoolean(required) ? required : false;
|
|
174
|
+
this.typeCheck = isBoolean(typeCheck) ? typeCheck : false;
|
|
175
|
+
this.methods = methods || Methods;
|
|
176
|
+
this.sanitize = isBoolean(sanitize) ? sanitize : true;
|
|
177
|
+
this.normalize = isBoolean(normalize) ? normalize : false;
|
|
178
|
+
this.control = isBoolean(control) ? control : true;
|
|
179
|
+
this.sanitizer = isFunction(sanitizer) ? sanitizer : null;
|
|
180
|
+
this.normalizer = isFunction(normalizer) ? normalizer : null;
|
|
181
|
+
this.controller = isFunction(controller) ? controller : null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const Messages = {
|
|
186
|
+
missing: (key) => `Missing ${key}`,
|
|
187
|
+
invalid: (key, type) => `Invalid ${key}, must be of type ${type}`,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
class Entity {
|
|
191
|
+
constructor(name, table, properties) {
|
|
192
|
+
this.name = name;
|
|
193
|
+
this.table = table;
|
|
194
|
+
this.properties = this.init(properties);
|
|
195
|
+
}
|
|
196
|
+
init(properties) {
|
|
197
|
+
const props = [];
|
|
198
|
+
for (const p of properties) {
|
|
199
|
+
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));
|
|
200
|
+
}
|
|
201
|
+
return props;
|
|
202
|
+
}
|
|
203
|
+
cols(method) {
|
|
204
|
+
const cols = [];
|
|
205
|
+
for (const p of this.properties) {
|
|
206
|
+
if (isIn(method, p.methods))
|
|
207
|
+
cols.push(p.key);
|
|
208
|
+
}
|
|
209
|
+
return cols;
|
|
210
|
+
}
|
|
211
|
+
normalize(rows) {
|
|
212
|
+
for (const r of rows) {
|
|
213
|
+
for (const { key, sanitize, normalize, sanitizer, normalizer, } of this.properties) {
|
|
214
|
+
let v = r[key];
|
|
215
|
+
if (v) {
|
|
216
|
+
if (sanitize)
|
|
217
|
+
v = this.sanitize(v, sanitizer);
|
|
218
|
+
if (normalize && normalizer)
|
|
219
|
+
v = normalizer(v);
|
|
220
|
+
r[key] = v;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return rows;
|
|
225
|
+
}
|
|
226
|
+
validate(rows, method) {
|
|
227
|
+
for (const r of rows) {
|
|
228
|
+
for (const { key, type, min, max, required, typeCheck, methods, control, controller } of this.properties) {
|
|
229
|
+
const v = r[key];
|
|
230
|
+
if (method && isIn(method, methods)) {
|
|
231
|
+
if (required) {
|
|
232
|
+
const rq = this.require(v, key);
|
|
233
|
+
if (rq)
|
|
234
|
+
return rq;
|
|
235
|
+
}
|
|
236
|
+
if (control) {
|
|
237
|
+
const ct = this.control(v, key, type, min, max, typeCheck, controller);
|
|
238
|
+
if (ct)
|
|
239
|
+
return ct;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
require(v, key) {
|
|
247
|
+
return Required.validate(v) ? null : Messages.missing(key);
|
|
248
|
+
}
|
|
249
|
+
control(v, key, type, min, max, typeCheck, cb) {
|
|
250
|
+
if (cb)
|
|
251
|
+
return cb(v) ? null : Messages.invalid(key, type);
|
|
252
|
+
return Types[type].validate(v, min, max, typeCheck) ? null : Messages.invalid(key, type);
|
|
253
|
+
}
|
|
254
|
+
sanitize(v, cb) {
|
|
255
|
+
if (cb)
|
|
256
|
+
return cb(v);
|
|
257
|
+
if (isArray(v, null, null)) {
|
|
258
|
+
for (let d of v) {
|
|
259
|
+
d = this.trim(d);
|
|
260
|
+
}
|
|
261
|
+
return v;
|
|
262
|
+
}
|
|
263
|
+
return this.trim(v);
|
|
264
|
+
}
|
|
265
|
+
trim(v) {
|
|
266
|
+
if (isString(v, true))
|
|
267
|
+
return v.trim();
|
|
268
|
+
if (isObject(v, true)) {
|
|
269
|
+
if (v.has)
|
|
270
|
+
for (const p in v) {
|
|
271
|
+
if (v.prototype.hasOwnProperty.call(p)) {
|
|
272
|
+
let o = v[p];
|
|
273
|
+
if (isString(o, true))
|
|
274
|
+
o = o.trim();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return v;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
exports.Entity = Entity;
|
package/dist/antity.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
|
|
28
|
+
type Type = keyof typeof Types;
|
|
29
|
+
type Method = typeof Methods[number];
|
|
30
|
+
|
|
31
|
+
declare const Methods: readonly ["GET", "PATCH", "PUT", "POST", "DELETE"];
|
|
32
|
+
|
|
33
|
+
declare const Types: {
|
|
34
|
+
readonly boolean: {
|
|
35
|
+
readonly validate: (v: any, _min: number, _max: number, _typeCheck: boolean) => v is boolean;
|
|
36
|
+
};
|
|
37
|
+
readonly string: {
|
|
38
|
+
readonly validate: (v: any, min: number, max: number, _typeCheck: boolean) => v is string;
|
|
39
|
+
};
|
|
40
|
+
readonly number: {
|
|
41
|
+
readonly validate: (v: any, min: number, max: number, typeCheck: boolean) => v is number;
|
|
42
|
+
};
|
|
43
|
+
readonly integer: {
|
|
44
|
+
readonly validate: (v: any, min: number, max: number, typeCheck: boolean) => v is number;
|
|
45
|
+
};
|
|
46
|
+
readonly array: {
|
|
47
|
+
readonly validate: (v: any, min: number, max: number, _typeCheck: boolean) => v is any[];
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
declare const Required: {
|
|
52
|
+
validate: (v: any) => boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
declare class Entity {
|
|
56
|
+
name: string;
|
|
57
|
+
table: string;
|
|
58
|
+
properties: Property[];
|
|
59
|
+
constructor(name: string, table: string, properties: Property[]);
|
|
60
|
+
private init;
|
|
61
|
+
cols(method: Method): string[];
|
|
62
|
+
normalize(rows: Record<string, any>[]): Record<string, any>[];
|
|
63
|
+
validate(rows: Record<string, any>[], method: Method): string | null;
|
|
64
|
+
private require;
|
|
65
|
+
private control;
|
|
66
|
+
private sanitize;
|
|
67
|
+
private trim;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare const Messages: {
|
|
71
|
+
missing: (key: string) => string;
|
|
72
|
+
invalid: (key: string, type: Type) => string;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
declare class Property {
|
|
76
|
+
key: string;
|
|
77
|
+
type: Type;
|
|
78
|
+
min: number;
|
|
79
|
+
max: number;
|
|
80
|
+
required: boolean;
|
|
81
|
+
typeCheck: boolean;
|
|
82
|
+
methods: Method[];
|
|
83
|
+
sanitize: boolean;
|
|
84
|
+
normalize: boolean;
|
|
85
|
+
control: boolean;
|
|
86
|
+
sanitizer: ((v: any) => any) | null;
|
|
87
|
+
normalizer: ((v: any) => any) | null;
|
|
88
|
+
controller: ((v: any) => any) | null;
|
|
89
|
+
constructor(key: string,
|
|
90
|
+
type: Type,
|
|
91
|
+
min: number,
|
|
92
|
+
max: number,
|
|
93
|
+
required: boolean,
|
|
94
|
+
typeCheck: boolean,
|
|
95
|
+
methods: Method[],
|
|
96
|
+
sanitize: boolean,
|
|
97
|
+
normalize: boolean,
|
|
98
|
+
control: boolean,
|
|
99
|
+
sanitizer: ((v: any) => any) | null,
|
|
100
|
+
normalizer: ((v: any) => any) | null,
|
|
101
|
+
controller: ((v: any) => any) | null
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
export type { Type, Method };
|
|
105
|
+
export {
|
|
106
|
+
Entity,
|
|
107
|
+
Property,
|
|
108
|
+
Messages,
|
|
109
|
+
Types,
|
|
110
|
+
Required,
|
|
111
|
+
Methods
|
|
112
|
+
};
|
|
113
|
+
|
package/dist/antity.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
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
|
+
import { isBoolean, isStringOfLength, isValidNumber, isValidInteger, isArrayOfLength, isNil, isString, isProperty, isArray, isIn, isInteger, isFunction, isObject } from '@dwtechs/checkard';
|
|
28
|
+
|
|
29
|
+
const Methods = ["GET", "PATCH", "PUT", "POST", "DELETE"];
|
|
30
|
+
|
|
31
|
+
const Types = {
|
|
32
|
+
boolean: {
|
|
33
|
+
validate: (v, _min, _max, _typeCheck) => isBoolean(v)
|
|
34
|
+
},
|
|
35
|
+
string: {
|
|
36
|
+
validate: (v, min, max, _typeCheck) => isStringOfLength(v, min, max)
|
|
37
|
+
},
|
|
38
|
+
number: {
|
|
39
|
+
validate: (v, min, max, typeCheck) => isValidNumber(v, min || undefined, max || undefined, typeCheck || undefined)
|
|
40
|
+
},
|
|
41
|
+
integer: {
|
|
42
|
+
validate: (v, min, max, typeCheck) => isValidInteger(v, min || undefined, max || undefined, typeCheck || undefined)
|
|
43
|
+
},
|
|
44
|
+
array: {
|
|
45
|
+
validate: (v, min, max, _typeCheck) => isArrayOfLength(v, min, max)
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const Required = {
|
|
49
|
+
validate: (v) => !isNil(v)
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
class Property {
|
|
53
|
+
constructor(key, type, min, max, required, typeCheck, methods, sanitize, normalize, control, sanitizer, normalizer, controller) {
|
|
54
|
+
if (!isString(key, true))
|
|
55
|
+
throw new Error(`Property key must be a string. Received ${key}`);
|
|
56
|
+
if (!isProperty(type, Types))
|
|
57
|
+
throw new Error(`Property type must be a valid type. Received ${type}`);
|
|
58
|
+
if (isArray(methods)) {
|
|
59
|
+
for (const m of methods) {
|
|
60
|
+
if (!isIn(m, Methods))
|
|
61
|
+
throw new Error(`Property methods must be an array of REST Methods. Received ${m}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
this.key = key;
|
|
65
|
+
this.type = type;
|
|
66
|
+
this.min = isInteger(min, true) ? min : 0;
|
|
67
|
+
this.max = isInteger(max, true) ? max : 999999999;
|
|
68
|
+
this.required = isBoolean(required) ? required : false;
|
|
69
|
+
this.typeCheck = isBoolean(typeCheck) ? typeCheck : false;
|
|
70
|
+
this.methods = methods || Methods;
|
|
71
|
+
this.sanitize = isBoolean(sanitize) ? sanitize : true;
|
|
72
|
+
this.normalize = isBoolean(normalize) ? normalize : false;
|
|
73
|
+
this.control = isBoolean(control) ? control : true;
|
|
74
|
+
this.sanitizer = isFunction(sanitizer) ? sanitizer : null;
|
|
75
|
+
this.normalizer = isFunction(normalizer) ? normalizer : null;
|
|
76
|
+
this.controller = isFunction(controller) ? controller : null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const Messages = {
|
|
81
|
+
missing: (key) => `Missing ${key}`,
|
|
82
|
+
invalid: (key, type) => `Invalid ${key}, must be of type ${type}`,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
class Entity {
|
|
86
|
+
constructor(name, table, properties) {
|
|
87
|
+
this.name = name;
|
|
88
|
+
this.table = table;
|
|
89
|
+
this.properties = this.init(properties);
|
|
90
|
+
}
|
|
91
|
+
init(properties) {
|
|
92
|
+
const props = [];
|
|
93
|
+
for (const p of properties) {
|
|
94
|
+
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));
|
|
95
|
+
}
|
|
96
|
+
return props;
|
|
97
|
+
}
|
|
98
|
+
cols(method) {
|
|
99
|
+
const cols = [];
|
|
100
|
+
for (const p of this.properties) {
|
|
101
|
+
if (isIn(method, p.methods))
|
|
102
|
+
cols.push(p.key);
|
|
103
|
+
}
|
|
104
|
+
return cols;
|
|
105
|
+
}
|
|
106
|
+
normalize(rows) {
|
|
107
|
+
for (const r of rows) {
|
|
108
|
+
for (const { key, sanitize, normalize, sanitizer, normalizer, } of this.properties) {
|
|
109
|
+
let v = r[key];
|
|
110
|
+
if (v) {
|
|
111
|
+
if (sanitize)
|
|
112
|
+
v = this.sanitize(v, sanitizer);
|
|
113
|
+
if (normalize && normalizer)
|
|
114
|
+
v = normalizer(v);
|
|
115
|
+
r[key] = v;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return rows;
|
|
120
|
+
}
|
|
121
|
+
validate(rows, method) {
|
|
122
|
+
for (const r of rows) {
|
|
123
|
+
for (const { key, type, min, max, required, typeCheck, methods, control, controller } of this.properties) {
|
|
124
|
+
const v = r[key];
|
|
125
|
+
if (method && isIn(method, methods)) {
|
|
126
|
+
if (required) {
|
|
127
|
+
const rq = this.require(v, key);
|
|
128
|
+
if (rq)
|
|
129
|
+
return rq;
|
|
130
|
+
}
|
|
131
|
+
if (control) {
|
|
132
|
+
const ct = this.control(v, key, type, min, max, typeCheck, controller);
|
|
133
|
+
if (ct)
|
|
134
|
+
return ct;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
require(v, key) {
|
|
142
|
+
return Required.validate(v) ? null : Messages.missing(key);
|
|
143
|
+
}
|
|
144
|
+
control(v, key, type, min, max, typeCheck, cb) {
|
|
145
|
+
if (cb)
|
|
146
|
+
return cb(v) ? null : Messages.invalid(key, type);
|
|
147
|
+
return Types[type].validate(v, min, max, typeCheck) ? null : Messages.invalid(key, type);
|
|
148
|
+
}
|
|
149
|
+
sanitize(v, cb) {
|
|
150
|
+
if (cb)
|
|
151
|
+
return cb(v);
|
|
152
|
+
if (isArray(v, null, null)) {
|
|
153
|
+
for (let d of v) {
|
|
154
|
+
d = this.trim(d);
|
|
155
|
+
}
|
|
156
|
+
return v;
|
|
157
|
+
}
|
|
158
|
+
return this.trim(v);
|
|
159
|
+
}
|
|
160
|
+
trim(v) {
|
|
161
|
+
if (isString(v, true))
|
|
162
|
+
return v.trim();
|
|
163
|
+
if (isObject(v, true)) {
|
|
164
|
+
if (v.has)
|
|
165
|
+
for (const p in v) {
|
|
166
|
+
if (v.prototype.hasOwnProperty.call(p)) {
|
|
167
|
+
let o = v[p];
|
|
168
|
+
if (isString(o, true))
|
|
169
|
+
o = o.trim();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return v;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export { Entity };
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dwtechs/antity",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Open source library for easy entity management",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"entities"
|
|
7
|
+
],
|
|
8
|
+
"homepage": "https://github.com/DWTechs/Antity.js",
|
|
9
|
+
"main": "dist/antity",
|
|
10
|
+
"types": "dist/antity",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/DWTechs/Antity.js"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/DWTechs/Antity.js/issues",
|
|
17
|
+
"email": ""
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Ludovic Cluber",
|
|
22
|
+
"email": "http://www.lcluber.com/contact",
|
|
23
|
+
"url": "http://www.lcluber.com"
|
|
24
|
+
},
|
|
25
|
+
"contributors": [],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "",
|
|
28
|
+
"build": "node ./scripts/clear && tsc && npm run rollup && node ./scripts/copy && npm run test",
|
|
29
|
+
"rollup:mjs": "rollup --config rollup.config.mjs",
|
|
30
|
+
"rollup:cjs": "rollup --config rollup.config.cjs.mjs",
|
|
31
|
+
"rollup": "npm run rollup:mjs && npm run rollup:cjs",
|
|
32
|
+
"test": "jest --coverage"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist/"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@dwtechs/checkard": "2.29.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@babel/core": "7.26.0",
|
|
42
|
+
"@babel/preset-env": "7.26.0",
|
|
43
|
+
"@rollup/plugin-commonjs": "28.0.1",
|
|
44
|
+
"@rollup/plugin-node-resolve": "15.3.0",
|
|
45
|
+
"@types/jest": "29.5.14",
|
|
46
|
+
"babel-jest": "29.7.0",
|
|
47
|
+
"core-js": "3.33.0",
|
|
48
|
+
"jest": "29.7.0",
|
|
49
|
+
"rollup": "4.24.0",
|
|
50
|
+
"typescript": "5.6.3"
|
|
51
|
+
}
|
|
52
|
+
}
|