├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── jest.config.js
├── package-lock.json
├── package.json
├── packages
└── semantic-validator
│ └── package.json
├── scripts
└── build.sh
├── src
├── index.ts
└── lib
│ ├── converters.ts
│ ├── definition.ts
│ ├── is
│ ├── basic.ts
│ ├── index.ts
│ ├── list.ts
│ ├── math.ts
│ ├── text.ts
│ └── time.ts
│ ├── op
│ ├── basic.ts
│ ├── converter.ts
│ └── index.ts
│ ├── utils.ts
│ └── validators.ts
├── test
├── cases
│ └── some-simple-posts.ts
├── is
│ ├── basic.ts
│ ├── list.ts
│ ├── math.ts
│ ├── text.ts
│ └── time.ts
├── op
│ ├── basic.ts
│ └── converter.ts
└── tsconfig.json
├── tsconfig.json
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true
4 | },
5 | "parser": "@typescript-eslint/parser",
6 | "parserOptions": {
7 | "ecmaFeatures": {
8 | "jsx": false
9 | }
10 | },
11 | "plugins": [
12 | "@typescript-eslint"
13 | ],
14 | "extends": [
15 | "airbnb-base",
16 | "plugin:@typescript-eslint/recommended",
17 | "plugin:import/errors",
18 | "plugin:import/warnings",
19 | "plugin:import/typescript"
20 | ],
21 | "settings": {
22 | "import/resolver": {
23 | "typescript": {}
24 | }
25 | },
26 | "rules": {
27 | "max-len": [
28 | "error",
29 | {
30 | "code": 100
31 | }
32 | ],
33 | "prefer-destructuring": "off",
34 | "dot-notation": "off",
35 | "object-shorthand": "off",
36 | "import/prefer-default-export": "off",
37 | "import/no-named-as-default-member": "off",
38 | "@typescript-eslint/indent": [
39 | "error",
40 | 2
41 | ],
42 | "@typescript-eslint/prefer-interface": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/explicit-function-return-type": [
45 | "warn",
46 | {
47 | "allowExpressions": true,
48 | "allowTypedFunctionExpressions": true,
49 | "allowHigherOrderFunctions": true
50 | }
51 | ]
52 | },
53 | "overrides": [
54 | {
55 | "files": [
56 | "test/**/*.ts"
57 | ],
58 | "env": {
59 | "es6": true,
60 | "jest": true
61 | },
62 | "settings": {
63 | "import/resolver": {
64 | "typescript": {
65 | "directory": "./test"
66 | }
67 | }
68 | }
69 | }
70 | ]
71 | }
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # General
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 | # Thumbnails
7 | ._*
8 |
9 | # Windows thumbnail cache files
10 | Thumbs.db
11 | ehthumbs.db
12 | ehthumbs_vista.db
13 |
14 | # Dump file
15 | *.stackdump
16 |
17 | # Folder config file
18 | [Dd]esktop.ini
19 |
20 | # Logs
21 | logs
22 | *.log
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # Runtime data
28 | pids
29 | *.pid
30 | *.seed
31 | *.pid.lock
32 |
33 | # node-waf configuration
34 | .lock-wscript
35 |
36 | # Compiled binary addons (https://nodejs.org/api/addons.html)
37 | build/Release
38 |
39 | # Dependency directories
40 | node_modules/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # IDE
55 | .vscode
56 | .idea
57 |
58 | # Test coverage directory
59 | coverage
60 |
61 | # Distribution files
62 | dist
63 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - '10'
5 | branches:
6 | only:
7 | - master
8 | cache:
9 | directories:
10 | - node_modules
11 | before_install:
12 | - npm update
13 | install:
14 | - npm install
15 | script:
16 | - npm run test
17 | - npm run test:coveralls
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 ChenZiTW
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Semantic Validator
2 |
3 | A functional semantic validator tool for validation of several types and rich content, inspired by prop-types from React.
4 |
5 | [](https://badge.fury.io/js/semantic-validator)
6 | [](https://travis-ci.com/chenzitw/semantic-validator)
7 | [](https://coveralls.io/github/chenzitw/semantic-validator?branch=master)
8 |
9 |
10 | ## Get started
11 |
12 | ### Using NPM or Yarn
13 |
14 | First, install it via npm.
15 | ```sh
16 | npm i semantic-validator
17 | ```
18 | Then, import it from dependencies and enjoy it.
19 | ```javascript
20 | import { op, is } from 'semantic-validator';
21 |
22 | const validate = op.shape({
23 | id: is.integer(),
24 | tags: op.every(is.string()),
25 | });
26 | ```
27 |
28 | ### Using script tag
29 |
30 | First, add a script tag with to the HTML page.
31 | ```html
32 |
33 | ```
34 | Then, just enjoy it.
35 | ```javascript
36 | var op = semanticValidator.op;
37 | var is = semanticValidator.is;
38 |
39 | var validate = op.shape({
40 | id: is.integer(),
41 | tags: op.every(is.string())
42 | });
43 | ```
44 |
45 |
46 | ## Usage
47 |
48 | The base unit of the semantic validator is a function which accepts the value as an argument for validation. We define it in TypeScript like below.
49 | ```typescript
50 | type Validator = (val: T) => boolean;
51 | ```
52 |
53 | This validator tool has several methods in two types: validator operator (op) and validator creator (is).
54 |
55 | For example, we can call a validator creator such as `is.integer()` to create a validator which expects the number should be an integer. We can also call `is.greaterThan(100)` to create a validator which expects the number is greater than 100.
56 | If we want to match both conditions, we can use a validator operator like `op.and(validator1, validator2)` to combine two validators.
57 |
58 | ```javascript
59 | import { op, is } from 'semantic-validator';
60 |
61 | const isInt = is.integer();
62 | isInt(12); // => true;
63 | isInt(12.34); // => false;
64 |
65 | const isGt100 = is.greaterThan(100);
66 | isGt100(150); // => true;
67 | isGt100(50); // => false;
68 |
69 | const isIntGt100 = op.and(
70 | is.integer(),
71 | is.greaterThan(100),
72 | );
73 | isIntGt100(123); // => true;
74 | isIntGt100(123.456); // => false;
75 | isIntGt100(12); // => false;
76 | ```
77 |
78 | You can also integrate other validation tools together. Just follow the validator pattern.
79 |
80 | For example, there is a method `isUUID(str, version)` for validating a string. We can make a validator like `val => isUUID(val, 5)` and compose with other validator operators.
81 |
82 | ```javascript
83 | import { op, is } from 'semantic-validator';
84 |
85 | const validate = op.shape({
86 | id: (val => isUUID(val, 5)),
87 | name: is.string(),
88 | });
89 | validate({ id: '6fad3a7b-161b-5e10-b265-8d522f3f35b5', name: 'Agent K' }); // => true;
90 | validate({ id: 'abc', name: 'Agent K' }); // => false;
91 | ```
92 |
93 |
94 | ### Cheatsheet
95 |
96 | | Method | Description |
97 | | ---------------------------------------------------------------- | ---------------------------------------------------------------- |
98 | | **Basic validator operators** | |
99 | | > **`op.so(validator)`** | Pass the validator. |
100 | | > **`op.not(validator)`** | Not pass the validator. |
101 | | > **`op.and(...validators)`** | Pass all validators. |
102 | | > **`op.or(...validators)`** | Pass any validators. |
103 | | > **`op.every(validator)`** | Pass the validator on all elements in an array. |
104 | | > **`op.some(validator)`** | Pass the validator on any elements in an array. |
105 | | > **`op.shape({ ...validatorOfKeys })`** | Pass all validators for each properties of an object. |
106 | | > **`op.exact({ ...validatorOfKeys })`** | Pass all validators for each properties of an exact object. |
107 | | **Converter validator operators** | |
108 | | > **`op.convert(converter, validator)`** | Pass the validator after converted. |
109 | | > **`op.toFloat(validator)`** | Pass the validator after converted to a float. |
110 | | > **`op.toInteger(validator)`** | Pass the validator after converted to an integer. |
111 | | > **`op.toLength(validator)`** | Pass the validator after converted to the length. |
112 | | > **`op.toSplit(separator, validator)`** | Pass the validator after splitted the string as an array. |
113 | | > **`op.toKeys(validator)`** | Pass the validator after converted to keys of an object. |
114 | | > **`op.toValues(validator)`** | Pass the validator after converted to values of an object. |
115 | | > **`op.toDate(validator)`** | Pass the validator after converted to a date object. |
116 | | **Basic validator creators** | |
117 | | > **`is.same(value)`** | Is the same as the base value? |
118 | | > **`is.oneOf(...values)`** | Is the same as any base values? |
119 | | > **`is.defined()`** | Is defined (not undefined)? |
120 | | > **`is.notDefined()`** | Is undefined? |
121 | | > **`is.nul()`** | Is null? |
122 | | > **`is.nil()`** | Is undefined or null? |
123 | | > **`is.bool()`** | Is a boolean? |
124 | | > **`is.number()`** | Is a number? |
125 | | > **`is.string()`** | Is a string? |
126 | | > **`is.object()`** | Is a non null object? |
127 | | > **`is.func()`** | Is a function? |
128 | | > **`is.symbol()`** | Is a symbol? |
129 | | > **`is.instanceOf(constructor)`** | Is an instance of the constructor (class)? |
130 | | > **`is.float()`** | Is a valid (not `NaN` or `Infinity`) float? |
131 | | > **`is.integer()`** | Is a valid (not `NaN` or `Infinity`) integer? |
132 | | > **`is.array()`** | Is an array? |
133 | | > **`is.date()`** | Is a valid (not `Invalid Date`) date object? |
134 | | **Math validator creators** | |
135 | | > **`is.equalTo(num)`** | Is the number equal to the base number? |
136 | | > **`is.greaterThan(num)`** | Is the number greater than the base number? |
137 | | > **`is.atLeast(num)`** | Is the number at least the base number? |
138 | | > **`is.lessThan(num)`** | Is the number less than the base number? |
139 | | > **`is.atMost(num)`** | Is the number at most the base number? |
140 | | > **`is.between(min, max)`** | Is the number between the base numbers? |
141 | | > **`is.fromTo(min, max)`** | Is the number from and to the base numbers? |
142 | | **Text validator creators** | |
143 | | > **`is.match(regexp)`** | Is the string match the regular expression? |
144 | | > **`is.startsWith(wording)`** | Is the string starts with the wording? |
145 | | > **`is.endsWith(wording)`** | Is the string ends with the wording? |
146 | | > **`is.contains(wording)`** | Is the string contains the wording? |
147 | | **List validator creators** | |
148 | | > **`is.includes(...includings)`** | Is the array includes all includings? |
149 | | > **`is.excludes(...excludings)`** | Is the array excludes all excludings? |
150 | | > **`is.restrictedBy(...allowedItems)`** | Is the array only includes allowed items? |
151 | | > **`is.distinct()`** | Is items of the array are all different? |
152 | | **Date validator creators** | |
153 | | > **`is.moment(date)`** | Is the date object at the moment of the base date object? |
154 | | > **`is.laterThan(date)`** | Is the date object later than the base date object? |
155 | | > **`is.atEarliest(date)`** | Is the date object at earliest the base date object? |
156 | | > **`is.earlierThan(date)`** | Is the date object earlier than the base date object? |
157 | | > **`is.atLatest(date)`** | Is the date object at latest the base date object? |
158 |
159 |
160 | ### Validator operator reference
161 |
162 | #### Basic
163 |
164 | **op: so**
165 | Will be valid when the validator returns true. It is usually not necessary.
166 | ```javascript
167 | op.so(validator)
168 | ```
169 | Example:
170 | ```javascript
171 | const validate = op.so(is.same('hello'));
172 | validate('hello'); // => true
173 | validate('bye'); // => false
174 | ```
175 |
176 | **op: not**
177 | Will be valid when the validator returns false.
178 | ```javascript
179 | op.not(validator)
180 | ```
181 | Example:
182 | ```javascript
183 | const validate = op.not(is.same('hello'));
184 | validate('bye'); // => true
185 | validate('hello'); // => false
186 | ```
187 |
188 | **op: and**
189 | Will be valid when all validators return true.
190 | ```javascript
191 | op.and(validator1, validator2, ...validators)
192 | ```
193 | Example:
194 | ```javascript
195 | const validate = op.and(is.integer(), is.greaterThan(100));
196 | validate(120); // => true
197 | validate(80); // => false
198 | validate(123.456); // => false
199 | ```
200 |
201 | **op: or**
202 | Will be valid when any validator returns true.
203 | ```javascript
204 | op.or(validator1, validator2, ...validators)
205 | ```
206 | Example:
207 | ```javascript
208 | const validate = op.or(is.nul(), is.integer());
209 | validate(null); // => true
210 | validate(123); // => true
211 | validate('abc'); // => false
212 | ```
213 |
214 | **op: every**
215 | Will be valid when validator returns true on all elements in the array.
216 | ```javascript
217 | op.every(validator)
218 | ```
219 | Example:
220 | ```javascript
221 | const validate = op.every(is.integer());
222 | validate([1, 2, 3]); // => true
223 | validate([1, 2.22, 3]); // => false
224 | ```
225 |
226 | **op: some**
227 | Will be valid when validator returns true on any element in the array.
228 | ```javascript
229 | op.some(validator)
230 | ```
231 | Example:
232 | ```javascript
233 | const validate = op.some(is.integer());
234 | validate([1, 2, 3]); // => true
235 | validate([1.11, 2, 3.33]); // => true
236 | validate([1.11, 2.22, 3.33]); // => false
237 | ```
238 |
239 | **op: shape**
240 | Will be valid when value is an object and all validator returns true on each key.
241 | ```javascript
242 | op.shape({
243 | [key1]: validator1,
244 | [key2]: validator2,
245 | ...keysWithValidator,
246 | })
247 | ```
248 | Example:
249 | ```javascript
250 | const validate = op.shape({
251 | id: is.integer(),
252 | name: is.string(),
253 | });
254 | validate({ id: 123, name: 'Mr. Sandman' }); // => true
255 | validate({ id: 123, name: 'Mr. Sandman', active: true }); // => true
256 | validate({ id: 123 }); // => false
257 | validate({ id: 123, name: 456 }); // => false
258 | ```
259 |
260 | **op: exact**
261 | Will be valid when value is an object with specific keys and all validator returns true on each key.
262 | ```javascript
263 | op.exact({
264 | [key1]: validator1,
265 | [key2]: validator2,
266 | ...keysWithValidator,
267 | })
268 | ```
269 | Example:
270 | ```javascript
271 | const validate = op.exact({
272 | id: is.integer(),
273 | name: is.string(),
274 | });
275 | validate({ id: 123, name: 'Mr. Sandman' }); // => true
276 | validate({ id: 123, name: 'Mr. Sandman', active: true }); // => false
277 | validate({ id: 123 }); // => false
278 | validate({ id: 123, name: 456 }); // => false
279 | ```
280 |
281 |
282 | #### Converter
283 |
284 | **op: convert**
285 | Will be valid when successfully converts the value and the validation is success.
286 | ```javascript
287 | op.convert(converter, validator)
288 | ```
289 | Example:
290 | ```javascript
291 | const validate = op.convert(
292 | numeric => parseFloat(numeric),
293 | is.greaterThan(100)
294 | );
295 | validate('150'); // => true
296 | validate('50'); // => false
297 | ```
298 |
299 | **op: to float**
300 | Will be valid when successfully converts to a float and the validation is success.
301 | ```javascript
302 | op.toFloat(validator)
303 | ```
304 | Example:
305 | ```javascript
306 | const validate = op.toFloat(is.greaterThan(1.2));
307 | validate('1.5'); // => true
308 | validate('two'); // => false
309 | validate('0.8'); // => false
310 | ```
311 |
312 | **op: to integer**
313 | Will be valid when successfully converts to an integer and the validation is success.
314 | ```javascript
315 | op.toInteger(validator)
316 | ```
317 | Example:
318 | ```javascript
319 | const validate = op.toInteger(is.greaterThan(100));
320 | validate('150'); // => true
321 | validate('two'); // => false
322 | validate('123.456'); // => false
323 | validate('50'); // => false
324 | ```
325 |
326 | **op: to length**
327 | Will be valid when successfully get the length of an array or string and the validation is success.
328 | ```javascript
329 | op.toLength(validator)
330 | ```
331 | Example:
332 | ```javascript
333 | const validate = op.toLength(is.atLeast(3));
334 | validate(['a', 'b', 'c']); // => true
335 | validate('abc'); // => true
336 | validate(['a', 'b']); // => false
337 | validate('ab'); // => false
338 | validate(3); // => false
339 | ```
340 |
341 | **op: to split**
342 | Will be valid when successfully split to the array and the validation is success.
343 | ```javascript
344 | op.toSplit(separator, validator)
345 | ```
346 | Example:
347 | ```javascript
348 | const validate = op.toSplit(',', op.every(is.startsWith('c')));
349 | validate('candy,cookie,coffee'); // => true
350 | validate('candy,cookie,tea'); // => false
351 | validate(123); // => false
352 | ```
353 |
354 | **op: to keys**
355 | Will be valid when successfully get keys of an object and the validation is success.
356 | ```javascript
357 | op.toKeys(validator)
358 | ```
359 | Example:
360 | ```javascript
361 | const validate = op.toKeys(op.every(is.oneOf('id', 'name')));
362 | validate({ id: 123, name: 'Mario' }); // => true
363 | validate({ id: 123, name: 'Mario', age: 20 }); // => false
364 | ```
365 |
366 | **op: to values**
367 | Will be valid when successfully get values of an object and the validation is success.
368 | ```javascript
369 | op.toValues(validator)
370 | ```
371 | Example:
372 | ```javascript
373 | const validate = op.toValues(op.every(is.integer()));
374 | validate({ people: 64, seats: 80 }); // => true
375 | validate({ people: 640, seats: 'many' }); // => false
376 | ```
377 |
378 |
379 | ### Validator creator reference
380 |
381 | #### Basic
382 |
383 | **is: same**
384 | Will be valid when base value and compare value are the same by using the SameValueZero algorithm.
385 | ```javascript
386 | is.same(baseValue)
387 | ```
388 | Example:
389 | ```javascript
390 | const validate = is.same('hello');
391 | validate('hello'); // => true
392 | ```
393 |
394 | **is: one of**
395 | Will be valid when one of base values and compare value are the same by using the SameValueZero algorithm.
396 | ```javascript
397 | is.oneOf(...baseValues)
398 | ```
399 | Example:
400 | ```javascript
401 | const validate = is.oneOf(100, '100', 'one hundred');
402 | validate('100'); // => true
403 | ```
404 |
405 | **is: defined**
406 | Will be valid when the value is defined (not undefined).
407 | ```javascript
408 | is.defined()
409 | ```
410 | Example:
411 | ```javascript
412 | const validate = is.defined();
413 | validate(123); // => true
414 | validate(undefined); // => false
415 | ```
416 |
417 | **is: not defined**
418 | Will be valid when the value is not defined (undefined).
419 | ```javascript
420 | is.notDefined()
421 | ```
422 | Example:
423 | ```javascript
424 | const validate = is.notDefined();
425 | validate(undefined); // => true
426 | validate(123); // => false
427 | ```
428 |
429 | **is: nul**
430 | Will be valid when the value is null.
431 | ```javascript
432 | is.nul()
433 | ```
434 | Example:
435 | ```javascript
436 | const validate = is.nul();
437 | validate(null); // => true
438 | validate(123); // => false
439 | ```
440 |
441 | **is: nil**
442 | Will be valid when the value is undefined or null.
443 | ```javascript
444 | is.nil()
445 | ```
446 | Example:
447 | ```javascript
448 | const validate = is.nil();
449 | validate(undefined); // => true
450 | validate(null); // => true
451 | validate(123); // => false
452 | ```
453 |
454 | **is: bool**
455 | Will be valid when the value is a boolean.
456 | ```javascript
457 | is.bool()
458 | ```
459 | Example:
460 | ```javascript
461 | const validate = is.bool();
462 | validate(false); // => true
463 | validate(123); // => false
464 | ```
465 |
466 | **is: number**
467 | Will be valid when the value is a number.
468 | ```javascript
469 | is.number()
470 | ```
471 | Example:
472 | ```javascript
473 | const validate = is.number();
474 | validate(123); // => true
475 | validate('abc'); // => false
476 | ```
477 |
478 | **is: string**
479 | Will be valid when the value is a string.
480 | ```javascript
481 | is.string()
482 | ```
483 | Example:
484 | ```javascript
485 | const validate = is.string();
486 | validate('abc'); // => true
487 | validate(123); // => false
488 | ```
489 |
490 | **is: object**
491 | Will be valid when the value is a non null object.
492 | ```javascript
493 | is.object()
494 | ```
495 | Example:
496 | ```javascript
497 | const validate = is.object();
498 | validate({ key: 'value' }); // => true
499 | validate({}); // => true
500 | validate(null); // => false
501 | validate(123); // => false
502 | ```
503 |
504 | **is: func**
505 | Will be valid when the value is a function.
506 | ```javascript
507 | is.func()
508 | ```
509 | Example:
510 | ```javascript
511 | const validate = is.func();
512 | validate(() => {}); // => true
513 | validate(123); // => false
514 | ```
515 |
516 | **is: symbol**
517 | Will be valid when the value is a symbol.
518 | ```javascript
519 | is.symbol()
520 | ```
521 | Example:
522 | ```javascript
523 | const validate = is.symbol();
524 | validate(Symbol('abc')); // => true
525 | validate('abc'); // => false
526 | ```
527 |
528 | **is: instance of**
529 | Will be valid when the value is the instance of a constructor.
530 | ```javascript
531 | is.instanceOf(constructor)
532 | ```
533 | Example:
534 | ```javascript
535 | const validate = is.instanceOf(Date);
536 | validate(new Date()); // => true
537 | validate(123); // => false
538 | ```
539 |
540 | **is: float**
541 | Will be valid when the value is a float and not NaN or infinity.
542 | ```javascript
543 | is.float()
544 | ```
545 | Example:
546 | ```javascript
547 | const validate = is.float();
548 | validate(123.456); // => true
549 | validate(123); // => true
550 | validate('123'); // => false
551 | ```
552 |
553 | **is: integer**
554 | Will be valid when the value is a integer and not NaN or infinity.
555 | ```javascript
556 | is.integer()
557 | ```
558 | Example:
559 | ```javascript
560 | const validate = is.integer();
561 | validate(123); // => true
562 | validate(123.456); // => false
563 | ```
564 |
565 | **is: array**
566 | Will be valid when the value is an array object.
567 | ```javascript
568 | is.array()
569 | ```
570 | Example:
571 | ```javascript
572 | const validate = is.array();
573 | validate([1, 2, 3]); // => true
574 | ```
575 |
576 | **is: date**
577 | Will be valid when the value is a valid date object.
578 | ```javascript
579 | is.date()
580 | ```
581 | Example:
582 | ```javascript
583 | const validate = is.date();
584 | validate(new Date()); // => true
585 | validate(new Date('invalid date')); // => false
586 | validate('invalid date'); // => false
587 | ```
588 |
589 |
590 | #### Math
591 |
592 | **is: equal to**
593 | Will be valid when the value is equal to the number.
594 | ```javascript
595 | is.equalTo(num)
596 | ```
597 | Example:
598 | ```javascript
599 | const validate = is.equalTo(100);
600 | validate(100); // true
601 | validate(200); // false
602 | ```
603 |
604 | **is: greater than**
605 | Will be valid when the value is greater than the number.
606 | ```javascript
607 | is.greaterThan(num)
608 | ```
609 | Example:
610 | ```javascript
611 | const validate = is.greaterThan(100);
612 | validate(101); // => true
613 | validate(100); // => false
614 | ```
615 |
616 | **is: at least**
617 | Will be valid when the value is at least the number.
618 | ```javascript
619 | is.atLeast(num)
620 | ```
621 | Example:
622 | ```javascript
623 | const validate = is.atLeast(100);
624 | validate(100); // => true
625 | validate(99); // => false
626 | ```
627 |
628 | **is: less than**
629 | Will be valid when the value is less than the number.
630 | ```javascript
631 | is.lessThan(num)
632 | ```
633 | Example:
634 | ```javascript
635 | const validate = is.lessThan(200);
636 | validate(199); // => true
637 | validate(200); // => false
638 | ```
639 |
640 | **is: at most**
641 | Will be valid when the value is at most the number.
642 | ```javascript
643 | is.atMost(num)
644 | ```
645 | Example:
646 | ```javascript
647 | const validate = is.atMost(200);
648 | validate(200); // => true
649 | validate(201); // => false
650 | ```
651 |
652 | **is: between**
653 | Will be valid when the value is between the numbers.
654 | ```javascript
655 | is.between(min, max)
656 | ```
657 | Example:
658 | ```javascript
659 | const validate = is.between(0, 10);
660 | validate(5); // => true
661 | validate(0); // => false
662 | validate(10); // => false
663 | ```
664 |
665 | **is: from to**
666 | Will be valid when the value is from and to the numbers.
667 | ```javascript
668 | is.fromTo(min, max)
669 | ```
670 | Example:
671 | ```javascript
672 | const validate = is.fromTo(1, 9);
673 | validate(1); // => true
674 | validate(9); // => true
675 | validate(0); // => false
676 | validate(10); // => false
677 | ```
678 |
679 |
680 | #### Text
681 |
682 | **is: match**
683 | Will be valid when the value matches the regular expression.
684 | ```javascript
685 | is.match(regexp)
686 | ```
687 | Example:
688 | ```javascript
689 | const validate = is.match(/^b[aeiou]t$/);
690 | validate('bat'); // => true
691 | validate('brt'); // => false
692 | ```
693 |
694 | **is: starts with**
695 | Will be valid when the value starts with the wording.
696 | ```javascript
697 | is.startsWith(wording)
698 | ```
699 | Example:
700 | ```javascript
701 | const validate = is.startsWith('net');
702 | validate('network'); // => true
703 | validate('artwork'); // => false
704 | ```
705 |
706 | **is: ends with**
707 | Will be valid when the value ends with the wording.
708 | ```javascript
709 | is.endsWith(wording)
710 | ```
711 | Example:
712 | ```javascript
713 | const validate = is.endsWith('fox');
714 | validate('firefox'); // => true
715 | validate('firewall'); // => false
716 | ```
717 |
718 | **is: contains**
719 | Will be valid when the value contains the wording.
720 | ```javascript
721 | is.contains(wording)
722 | ```
723 | Example:
724 | ```javascript
725 | const validate = is.contains('lie');
726 | validate('believe'); // => true
727 | validate('behave'); // => false
728 | ```
729 |
730 |
731 | #### List
732 |
733 | **is: includes**
734 | Will be valid when the array value includes all includings.
735 | ```javascript
736 | is.includes(including1, including2, ...includings)
737 | ```
738 | Example:
739 | ```javascript
740 | const validate = is.includes(10, 20, 30);
741 | validate([0, 10, 20, 30, 40]); // => true
742 | validate([0, 20, 40]); // => false
743 | ```
744 |
745 | **is: excludes**
746 | Will be valid when the array value excludes all excludings.
747 | ```javascript
748 | is.excludes(excluding1, excluding2, ...excludings)
749 | ```
750 | Example:
751 | ```javascript
752 | const validate = is.excludes(5, 15, 25);
753 | validate([0, 10, 20, 30]); // => true
754 | validate([5, 10]); // => false
755 | ```
756 |
757 | **is: restricted by**
758 | Will be valid when the array value is restricted by allowed items.
759 | ```javascript
760 | is.restrictedBy(allowedItem1, allowedItem2, ...allowedItems)
761 | ```
762 | Example:
763 | ```javascript
764 | const validate = is.restrictedBy(5, 10, 15, 20);
765 | validate([10, 20]); // => true
766 | validate([5, 10, 15, 20, 25, 30]); // => false
767 | ```
768 |
769 | **is: distinct**
770 | Will be valid when items of array value are all different.
771 | ```javascript
772 | is.distinct()
773 | ```
774 | Example:
775 | ```javascript
776 | const validate = is.distinct();
777 | validate([1, 2, 3]); // => true
778 | validate([1, 2, 3, 3]]); // => false
779 | ```
780 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | moduleNameMapper: {
4 | '^semantic-validator$': '/src',
5 | '^semantic-validator/(.*)$': '/src/$1',
6 | },
7 | testMatch: ['/test/**/*.ts'],
8 | globals: {
9 | 'ts-jest': {
10 | tsConfig: '/test/tsconfig.json'
11 | }
12 | },
13 | collectCoverage: true,
14 | collectCoverageFrom: [
15 | '/src/lib/op/**/*.ts',
16 | '/src/lib/is/**/*.ts',
17 | ],
18 | coverageReporters: ['lcov', 'text-summary'],
19 | };
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "semantic-validator-dev",
3 | "version": "0.1.1",
4 | "description": "A functional semantic validator tool for validation of several types and rich content.",
5 | "private": true,
6 | "author": "ChenZi ",
7 | "license": "MIT",
8 | "repository": {
9 | "url": "https://github.com/chenzitw/semantic-validator.git",
10 | "type": "git"
11 | },
12 | "scripts": {
13 | "start": "ts-node --files -O '{\"module\":\"commonjs\"}' -r tsconfig-paths/register ./src",
14 | "lint": "tsc --noEmit && eslint {src,test}/**/* --quiet",
15 | "test": "jest --ForceExit",
16 | "test:coveralls": "cat ./coverage/lcov.info | coveralls",
17 | "build": "bash ./scripts/build.sh",
18 | "publish": "npm publish dist"
19 | },
20 | "devDependencies": {
21 | "@types/jest": "^24.0.15",
22 | "@types/node": "^12.0.10",
23 | "@types/validator": "^10.11.1",
24 | "@typescript-eslint/eslint-plugin": "^1.11.0",
25 | "@typescript-eslint/parser": "^1.11.0",
26 | "coveralls": "^3.0.4",
27 | "eslint": "^6.0.1",
28 | "eslint-config-airbnb-base": "^13.1.0",
29 | "eslint-import-resolver-typescript": "^1.1.1",
30 | "eslint-plugin-import": "^2.18.0",
31 | "jest": "^24.8.0",
32 | "ts-jest": "^24.0.2",
33 | "ts-loader": "^6.0.4",
34 | "ts-node": "^8.3.0",
35 | "tsconfig-paths": "^3.8.0",
36 | "typescript": "^3.5.2",
37 | "webpack": "^4.35.2",
38 | "webpack-cli": "^3.3.5"
39 | },
40 | "dependencies": {}
41 | }
42 |
--------------------------------------------------------------------------------
/packages/semantic-validator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "semantic-validator",
3 | "version": "0.1.1",
4 | "description": "A functional semantic validator tool for validation of several types and rich content.",
5 | "author": "ChenZi ",
6 | "license": "MIT",
7 | "repository": {
8 | "url": "https://github.com/chenzitw/semantic-validator.git",
9 | "type": "git"
10 | },
11 | "main": "index.js",
12 | "devDependencies": {},
13 | "dependencies": {}
14 | }
15 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | rm -rf ./dist
2 | mkdir dist
3 | NODE_ENV=production tsc --build
4 | NODE_ENV=production webpack --display
5 | cp ./packages/semantic-validator/package.json ./dist/package.json
6 | cp ./README.md ./dist/README.md
7 | cp ./LICENSE ./dist/LICENSE
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import op from './lib/op';
2 | import is from './lib/is';
3 |
4 | export {
5 | op,
6 | is,
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/converters.ts:
--------------------------------------------------------------------------------
1 | const convertAnyToNumber = (value: boolean | number | string): (undefined | number) => {
2 | switch (typeof value) {
3 | case 'boolean': return Number(value);
4 | case 'number': return (Number.isFinite(value)) ? value : undefined;
5 | case 'string': return (/^[-+]?[0-9]*\.?[0-9]+$/.test(value)) ? Number(value) : undefined;
6 | default: return undefined;
7 | }
8 | };
9 |
10 | export const floatConverter = (val: boolean | number | string): number => {
11 | const numericVal = convertAnyToNumber(val);
12 | if (numericVal === undefined) {
13 | throw new Error();
14 | }
15 | if (!Number.isFinite(numericVal)) {
16 | throw new Error();
17 | }
18 | return numericVal;
19 | };
20 |
21 | export const integerConverter = (val: boolean | number | string): number => {
22 | const numericVal = convertAnyToNumber(val);
23 | if (numericVal === undefined) {
24 | throw new Error();
25 | }
26 | if (!Number.isInteger(numericVal)) {
27 | throw new Error();
28 | }
29 | return numericVal;
30 | };
31 |
32 | export const lengthConverter = (val: string | any[]): number => {
33 | const length = val.length;
34 | if (length === undefined) {
35 | throw new Error();
36 | }
37 | return length;
38 | };
39 |
40 | export const keysConverter = Object.keys as ((val: T) => (keyof T)[]);
41 |
42 | export const valuesConverter = Object.values as ((val: T) => T[keyof T][]);
43 |
44 | export const dateConverter = (val: number): Date => {
45 | if (!Number.isInteger(val)) {
46 | throw new Error();
47 | }
48 | const date = new Date(val);
49 | if (Number.isNaN(date.getTime())) {
50 | throw new Error();
51 | }
52 | return date;
53 | };
54 |
--------------------------------------------------------------------------------
/src/lib/definition.ts:
--------------------------------------------------------------------------------
1 | export type Validator = (val: T) => boolean;
2 |
3 | export type SomeObject = {
4 | [K in string]: any;
5 | };
6 |
7 | export type ShapeValidation = {
8 | [K in keyof Partial]: Validator;
9 | }
10 |
11 | export type ExactValidation = {
12 | [K in keyof T]: Validator;
13 | }
14 |
15 | export type SomeFunction = (args: any[]) => any;
16 |
--------------------------------------------------------------------------------
/src/lib/is/basic.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | } from '../definition';
4 | import {
5 | isSameValueZero,
6 | } from '../utils';
7 | import {
8 | definedValidator,
9 | notDefinedValidator,
10 | nulValidator,
11 | nilValidator,
12 | booleanValidator,
13 | numberValidator,
14 | stringValidator,
15 | objectValidator,
16 | functionValidator,
17 | symbolValidator,
18 | floatValidator,
19 | integerValidator,
20 | arrayValidator,
21 | dateValidator,
22 | } from '../validators';
23 |
24 | /**
25 | * Is the same as the base value?
26 | */
27 | export const same = (target: any): Validator => (
28 | val => isSameValueZero(target, val)
29 | );
30 |
31 | /**
32 | * Is the same as any base values?
33 | */
34 | export const oneOf = (...list: any[]): Validator => (
35 | val => list.some(item => isSameValueZero(item, val))
36 | );
37 |
38 | /**
39 | * Is defined (not undefined)?
40 | */
41 | export const defined = (): Validator => definedValidator;
42 |
43 | /**
44 | * Is undefined?
45 | */
46 | export const notDefined = (): Validator => notDefinedValidator;
47 |
48 | /**
49 | * Is null?
50 | */
51 | export const nul = (): Validator => nulValidator;
52 |
53 | /**
54 | * Is undefined or null?
55 | */
56 | export const nil = (): Validator => nilValidator;
57 |
58 | /**
59 | * Is a boolean?
60 | */
61 | export const bool = (): Validator => booleanValidator;
62 |
63 | /**
64 | * Is a number?
65 | */
66 | export const number = (): Validator => numberValidator;
67 |
68 | /**
69 | * Is a string?
70 | */
71 | export const string = (): Validator => stringValidator;
72 |
73 | /**
74 | * Is a non null object?
75 | */
76 | export const object = (): Validator => objectValidator;
77 |
78 | /**
79 | * Is a function?
80 | */
81 | export const func = (): Validator => functionValidator;
82 |
83 | /**
84 | * Is a symbol?
85 | */
86 | export const symbol = (): Validator => symbolValidator;
87 |
88 | /**
89 | * Is an instance of the constructor (class)?
90 | */
91 | export const instanceOf = (constructor: any): Validator => val => (val instanceof constructor);
92 |
93 | /**
94 | * Is a valid (not `NaN` or `Infinity`) float?
95 | */
96 | export const float = (): Validator => floatValidator;
97 |
98 | /**
99 | * Is a valid (not `NaN` or `Infinity`) integer?
100 | */
101 | export const integer = (): Validator => integerValidator;
102 |
103 | /**
104 | * Is an array?
105 | */
106 | export const array = (): Validator => arrayValidator;
107 |
108 | /**
109 | * Is a valid (not `Invalid Date`) date object?
110 | */
111 | export const date = (): Validator => dateValidator;
112 |
--------------------------------------------------------------------------------
/src/lib/is/index.ts:
--------------------------------------------------------------------------------
1 | import * as basic from './basic';
2 | import * as math from './math';
3 | import * as text from './text';
4 | import * as list from './list';
5 |
6 | const is = {
7 | ...basic,
8 | ...math,
9 | ...text,
10 | ...list,
11 | };
12 |
13 | export default is;
14 |
--------------------------------------------------------------------------------
/src/lib/is/list.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | } from '../definition';
4 | import {
5 | distinctValidator,
6 | } from '../validators';
7 |
8 | /**
9 | * Is the array includes all includings?
10 | */
11 | export const includes = (...includings: any[]): Validator => (
12 | list => (Array.isArray(list) && includings.every(including => list.includes(including)))
13 | );
14 |
15 | /**
16 | * Is the array excludes all excludings?
17 | */
18 | export const excludes = (...excludings: any[]): Validator => (
19 | list => (Array.isArray(list) && excludings.every(excluding => !list.includes(excluding)))
20 | );
21 |
22 | /**
23 | * Is the array only includes allowed items?
24 | */
25 | export const restrictedBy = (...whitelist: any[]): Validator => (
26 | list => (Array.isArray(list) && list.every(item => whitelist.includes(item)))
27 | );
28 |
29 | /**
30 | * Is items of the array are all different?
31 | */
32 | export const distinct = (): Validator => distinctValidator;
33 |
--------------------------------------------------------------------------------
/src/lib/is/math.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | } from '../definition';
4 | import {
5 | falsyValidator,
6 | } from '../validators';
7 |
8 | /**
9 | * Is the number equal to the base number?
10 | */
11 | export const equalTo = (target: number): Validator => {
12 | if (!Number.isFinite(target)) {
13 | return falsyValidator;
14 | }
15 | return (
16 | num => (Number.isFinite(num) && (num === target))
17 | );
18 | };
19 |
20 | /**
21 | * Is the number greater than the base number?
22 | */
23 | export const greaterThan = (target: number): Validator => {
24 | if (!Number.isFinite(target)) {
25 | return falsyValidator;
26 | }
27 | return (
28 | num => (Number.isFinite(num) && (num > target))
29 | );
30 | };
31 |
32 | /**
33 | * Is the number at least the base number?
34 | */
35 | export const atLeast = (target: number): Validator => {
36 | if (!Number.isFinite(target)) {
37 | return falsyValidator;
38 | }
39 | return (
40 | num => (Number.isFinite(num) && (num >= target))
41 | );
42 | };
43 |
44 | /**
45 | * Is the number less than the base number?
46 | */
47 | export const lessThan = (target: number): Validator => {
48 | if (!Number.isFinite(target)) {
49 | return falsyValidator;
50 | }
51 | return (
52 | num => (Number.isFinite(num) && (num < target))
53 | );
54 | };
55 |
56 | /**
57 | * Is the number at most the base number?
58 | */
59 | export const atMost = (target: number): Validator => {
60 | if (!Number.isFinite(target)) {
61 | return falsyValidator;
62 | }
63 | return (
64 | num => (Number.isFinite(num) && (num <= target))
65 | );
66 | };
67 |
68 | /**
69 | * Is the number between the base numbers?
70 | */
71 | export const between = (min: number, max: number): Validator => {
72 | if (!Number.isFinite(min) || !Number.isFinite(max)) {
73 | return falsyValidator;
74 | }
75 | return (
76 | num => (Number.isFinite(num) && (num > Math.min(min, max)) && (num < Math.max(min, max)))
77 | );
78 | };
79 |
80 | /**
81 | * Is the number from and to the base numbers?
82 | */
83 | export const fromTo = (min: number, max: number): Validator => {
84 | if (!Number.isFinite(min) || !Number.isFinite(max)) {
85 | return falsyValidator;
86 | }
87 | return (
88 | num => (Number.isFinite(num) && (num >= Math.min(min, max)) && (num <= Math.max(min, max)))
89 | );
90 | };
91 |
--------------------------------------------------------------------------------
/src/lib/is/text.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | } from '../definition';
4 | import {
5 | falsyValidator,
6 | } from '../validators';
7 |
8 | /**
9 | * Is the string match the regular expression?
10 | */
11 | export const match = (regExp: RegExp): Validator => {
12 | if (!(regExp instanceof RegExp)) {
13 | return falsyValidator;
14 | }
15 | return (
16 | text => ((typeof text === 'string') && regExp.test(text))
17 | );
18 | };
19 |
20 | /**
21 | * Is the string starts with the wording?
22 | */
23 | export const startsWith = (wording: string): Validator => {
24 | if (typeof wording !== 'string') {
25 | return falsyValidator;
26 | }
27 | return (
28 | text => ((typeof text === 'string') && text.startsWith(wording))
29 | );
30 | };
31 |
32 | /**
33 | * Is the string ends with the wording?
34 | */
35 | export const endsWith = (wording: string): Validator => {
36 | if (typeof wording !== 'string') {
37 | return falsyValidator;
38 | }
39 | return (
40 | text => ((typeof text === 'string') && text.endsWith(wording))
41 | );
42 | };
43 |
44 | /**
45 | * Is the string contains the wording?
46 | */
47 | export const contains = (wording: string): Validator => {
48 | if (typeof wording !== 'string') {
49 | return falsyValidator;
50 | }
51 | return (
52 | text => ((typeof text === 'string') && text.includes(wording))
53 | );
54 | };
55 |
--------------------------------------------------------------------------------
/src/lib/is/time.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | } from '../definition';
4 | import {
5 | falsyValidator,
6 | } from '../validators';
7 |
8 | const isValidDate = (date: Date): boolean => (
9 | (date instanceof Date) && !Number.isNaN(date.getTime())
10 | );
11 |
12 | /**
13 | * Is the date object at the moment of the base date object?
14 | */
15 | export const moment = (base: Date): Validator => {
16 | if (!isValidDate(base)) {
17 | return falsyValidator;
18 | }
19 | return (
20 | time => (isValidDate(time) && (time.getTime() === base.getTime()))
21 | );
22 | };
23 |
24 | /**
25 | * Is the date object later than the base date object?
26 | */
27 | export const laterThan = (base: Date): Validator => {
28 | if (!isValidDate(base)) {
29 | return falsyValidator;
30 | }
31 | return (
32 | time => (isValidDate(time) && (time.getTime() > base.getTime()))
33 | );
34 | };
35 |
36 | /**
37 | * Is the date object at earliest the base date object?
38 | */
39 | export const atEarliest = (base: Date): Validator => {
40 | if (!isValidDate(base)) {
41 | return falsyValidator;
42 | }
43 | return (
44 | time => (isValidDate(time) && (time.getTime() >= base.getTime()))
45 | );
46 | };
47 |
48 | /**
49 | * Is the date object earlier than the base date object?
50 | */
51 | export const earlierThan = (base: Date): Validator => {
52 | if (!isValidDate(base)) {
53 | return falsyValidator;
54 | }
55 | return (
56 | time => (isValidDate(time) && (time.getTime() < base.getTime()))
57 | );
58 | };
59 |
60 | /**
61 | * Is the date object at latest the base date object?
62 | */
63 | export const atLatest = (base: Date): Validator => {
64 | if (!isValidDate(base)) {
65 | return falsyValidator;
66 | }
67 | return (
68 | time => (isValidDate(time) && (time.getTime() <= base.getTime()))
69 | );
70 | };
71 |
--------------------------------------------------------------------------------
/src/lib/op/basic.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | SomeObject,
4 | ShapeValidation,
5 | ExactValidation,
6 | } from '../definition';
7 | import {
8 | enhancedObjectEntries,
9 | haveSameObjectKeys,
10 | isShapeOrExactValidation,
11 | } from '../utils';
12 | import {
13 | falsyValidator,
14 | } from '../validators';
15 |
16 | /**
17 | * Pass the validator.
18 | */
19 | export const so = (validator: Validator): Validator => (
20 | val => validator(val)
21 | );
22 |
23 | /**
24 | * Not pass the validator.
25 | */
26 | export const not = (validator: Validator): Validator => (
27 | val => !validator(val)
28 | );
29 |
30 | /**
31 | * Pass all validators.
32 | */
33 | export const and = (...validators: Validator[]): Validator => (
34 | val => validators.every(validator => validator(val))
35 | );
36 |
37 | /**
38 | * Pass any validators.
39 | */
40 | export const or = (...validators: Validator[]): Validator => (
41 | val => validators.some(validator => validator(val))
42 | );
43 |
44 | /**
45 | * Pass the validator on all elements in an array.
46 | */
47 | export const every = (validator: Validator): Validator => (
48 | list => (Array.isArray(list) && list.every(item => validator(item)))
49 | );
50 |
51 | /**
52 | * Pass the validator on any elements in an array.
53 | */
54 | export const some = (validator: Validator): Validator => (
55 | list => (Array.isArray(list) && list.some(item => validator(item)))
56 | );
57 |
58 | /**
59 | * Pass all validators for each properties of an object.
60 | */
61 | export const shape = (
62 | /* eslint-disable-next-line arrow-parens */
63 | (shapeValidation: ShapeValidation): Validator => {
64 | if (!isShapeOrExactValidation(shapeValidation)) {
65 | return falsyValidator;
66 | }
67 | return (
68 | obj => (true
69 | && (typeof obj === 'object')
70 | && (obj !== null)
71 | && enhancedObjectEntries(shapeValidation).every(([key, validator]) => validator(obj[key]))
72 | )
73 | );
74 | }
75 | );
76 |
77 | /**
78 | * Pass all validators for each properties of an exact object.
79 | */
80 | export const exact = (
81 | /* eslint-disable-next-line arrow-parens */
82 | (exactValidation: ExactValidation): Validator => {
83 | if (!isShapeOrExactValidation(exactValidation)) {
84 | return falsyValidator;
85 | }
86 | return (
87 | obj => (true
88 | && (typeof obj === 'object')
89 | && (obj !== null)
90 | && haveSameObjectKeys(obj, exactValidation)
91 | && enhancedObjectEntries(exactValidation).every(([key, validator]) => validator(obj[key]))
92 | )
93 | );
94 | }
95 | );
96 |
--------------------------------------------------------------------------------
/src/lib/op/converter.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Validator,
3 | } from '../definition';
4 | import {
5 | floatConverter,
6 | integerConverter,
7 | lengthConverter,
8 | keysConverter,
9 | valuesConverter,
10 | dateConverter,
11 | } from '../converters';
12 |
13 | /**
14 | * Pass the validator after converted.
15 | */
16 | export const convert = (
17 | converter: (original: T1) => T2,
18 | validator?: Validator,
19 | ): Validator => (
20 | (val: T1): boolean => {
21 | let convertedVal;
22 | try {
23 | convertedVal = converter(val);
24 | } catch (error) {
25 | return false;
26 | }
27 | return (validator !== undefined) ? validator(convertedVal) : true;
28 | }
29 | );
30 |
31 | /**
32 | * Pass the validator after converted to a float.
33 | */
34 | export const toFloat = (validator?: Validator): Validator => (
35 | convert(floatConverter, validator)
36 | );
37 |
38 | /**
39 | * Pass the validator after converted to an integer.
40 | */
41 | export const toInteger = (validator?: Validator): Validator => (
42 | convert(integerConverter, validator)
43 | );
44 |
45 | /**
46 | * Pass the validator after converted to the length.
47 | */
48 | export const toLength = (validator?: Validator): Validator => (
49 | convert(lengthConverter, validator)
50 | );
51 |
52 | /**
53 | * Pass the validator after splitted the string as an array.
54 | */
55 | export const toSplit = (
56 | separator: string,
57 | validator?: Validator,
58 | ): Validator => (
59 | convert((val: string) => val.split(separator), validator)
60 | );
61 |
62 | /**
63 | * Pass the validator after converted to keys of an object.
64 | */
65 | export const toKeys = (validator?: Validator<(keyof T)[]>): Validator => (
66 | convert(keysConverter, validator)
67 | );
68 |
69 | /**
70 | * Pass the validator after converted to values of an object.
71 | */
72 | export const toValues = (validator?: Validator<(T[keyof T])[]>): Validator => (
73 | convert(valuesConverter, validator)
74 | );
75 |
76 | /**
77 | * Pass the validator after converted to a date object.
78 | */
79 | export const toDate = (validator?: Validator): Validator => (
80 | convert(dateConverter, validator)
81 | );
82 |
--------------------------------------------------------------------------------
/src/lib/op/index.ts:
--------------------------------------------------------------------------------
1 | import * as basic from './basic';
2 | import * as converter from './converter';
3 |
4 | const op = {
5 | ...basic,
6 | ...converter,
7 | };
8 |
9 | export default op;
10 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * enhancedObjectEntries
3 | * Refer from [issue](https://github.com/Microsoft/TypeScript/issues/21826#issuecomment-479851685).
4 | */
5 | export const enhancedObjectEntries = Object.entries as (o: T) => [keyof T, T[keyof T]][];
6 |
7 | /**
8 | * isSameValueZero
9 | * Refer from [repo](https://github.com/domenic/especially/blob/master/abstract-operations.js#L126).
10 | */
11 | export const isSameValueZero = (x: any, y: any): boolean => (
12 | ((x === 0) && (y === 0)) || Object.is(x, y)
13 | );
14 |
15 | export const uniqueArray = (list: T[]): T[] => Array.from(new Set(list));
16 |
17 | export const haveSameObjectKeys = (base: object, compare: object): boolean => (true
18 | && Object.keys(base).every(key => Object.prototype.propertyIsEnumerable.call(compare, key))
19 | && Object.keys(compare).every(key => Object.prototype.propertyIsEnumerable.call(base, key))
20 | );
21 |
22 | export const isShapeOrExactValidation = (validation: any): validation is object => (true
23 | && (typeof validation === 'object')
24 | && (validation !== null)
25 | && Object.values(validation).every(validator => typeof validator === 'function')
26 | );
27 |
--------------------------------------------------------------------------------
/src/lib/validators.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SomeFunction,
3 | } from './definition';
4 | import {
5 | uniqueArray,
6 | } from './utils';
7 |
8 | export const trulyValidator = (
9 | (): boolean => true
10 | );
11 |
12 | export const falsyValidator = (
13 | (): boolean => false
14 | );
15 |
16 | export const definedValidator = (
17 | (val: any): val is undefined => (typeof val !== 'undefined')
18 | );
19 |
20 | export const notDefinedValidator = (
21 | (val: any): boolean => (typeof val === 'undefined')
22 | );
23 |
24 | export const nulValidator = (
25 | (val: any): val is null => (val === null)
26 | );
27 |
28 | export const nilValidator = (
29 | (val: any): val is (undefined | null) => ((typeof val === 'undefined') || (val === null))
30 | );
31 |
32 | export const booleanValidator = (
33 | (val: any): val is boolean => (typeof val === 'boolean')
34 | );
35 |
36 | export const numberValidator = (
37 | (val: any): val is number => (typeof val === 'number')
38 | );
39 |
40 | export const stringValidator = (
41 | (val: any): val is string => (typeof val === 'string')
42 | );
43 |
44 | export const objectValidator = (
45 | (val: any): val is object => ((typeof val === 'object') && (val !== null))
46 | );
47 |
48 | export const functionValidator = (
49 | (val: any): val is SomeFunction => (typeof val === 'function')
50 | );
51 |
52 | export const symbolValidator = (
53 | (val: any): val is symbol => (typeof val === 'symbol')
54 | );
55 |
56 | export const floatValidator = (
57 | (val: any): val is number => Number.isFinite(val)
58 | );
59 |
60 | export const integerValidator = (
61 | (val: any): val is number => Number.isInteger(val)
62 | );
63 |
64 | export const arrayValidator = (
65 | (val: any): val is any[] => (Array.isArray(val))
66 | );
67 |
68 | export const dateValidator = (
69 | (val: any): val is Date => ((val instanceof Date) && !Number.isNaN(val.getTime()))
70 | );
71 |
72 | export const distinctValidator = (
73 | (val: any[]): val is any[] => (Array.isArray(val) && (val.length === uniqueArray(val).length))
74 | );
75 |
--------------------------------------------------------------------------------
/test/cases/some-simple-posts.ts:
--------------------------------------------------------------------------------
1 | import { op, is } from 'semantic-validator';
2 |
3 | describe('normal case 01', (): void => {
4 | const validator = op.every(
5 | op.shape({
6 | id: is.integer(),
7 | content: is.string(),
8 | attachement: op.or(
9 | is.nul(),
10 | op.exact({
11 | type: is.same('image'),
12 | url: is.string(),
13 | }),
14 | op.exact({
15 | type: is.same('location'),
16 | coordinates: op.exact({
17 | lat: op.and(
18 | is.float(),
19 | is.atLeast(-90),
20 | is.atMost(90),
21 | ),
22 | lng: op.and(
23 | is.float(),
24 | is.atLeast(-180),
25 | is.atMost(180),
26 | ),
27 | }),
28 | }),
29 | ),
30 | createdAt: is.date(),
31 | }),
32 | );
33 |
34 | it('should return true when posts are valid', (): void => {
35 | const posts = [
36 | {
37 | id: 10000001,
38 | content: 'some-content',
39 | author: 'User One',
40 | attachement: {
41 | type: 'image',
42 | url: 'http://image.com/image-0001.jpg',
43 | },
44 | createdAt: new Date('2019-06-23T08:24:00Z'),
45 | },
46 | {
47 | id: 10000002,
48 | content: 'some-content',
49 | author: 'User Two',
50 | attachement: {
51 | type: 'location',
52 | coordinates: {
53 | lat: 25.061027,
54 | lng: 121.5289492,
55 | },
56 | },
57 | createdAt: new Date('2019-06-23T08:25:00Z'),
58 | notImportantProp: 'not-important-value',
59 | },
60 | {
61 | id: 10000003,
62 | content: 'some-content',
63 | author: 'User Three',
64 | attachement: null,
65 | createdAt: new Date('2019-06-23T08:26:00Z'),
66 | notImportantProp: 'not-important-value',
67 | },
68 | ];
69 |
70 | expect(validator(posts)).toBe(true);
71 | });
72 |
73 | it('should return false when posts are invalid', (): void => {
74 | const posts = [
75 | {
76 | id: 10000001,
77 | content: 'some-content',
78 | author: 'User One',
79 | attachement: {
80 | type: 'location',
81 | url: 'http://image.com/image-0001.jpg',
82 | },
83 | createdAt: new Date('2019-06-23T08:24:00Z'),
84 | },
85 | ];
86 |
87 | expect(validator(posts)).toBe(false);
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/test/is/basic.ts:
--------------------------------------------------------------------------------
1 | import {
2 | same,
3 | oneOf,
4 | defined,
5 | notDefined,
6 | nul,
7 | nil,
8 | bool,
9 | number,
10 | string,
11 | object,
12 | func,
13 | symbol,
14 | instanceOf,
15 | float,
16 | integer,
17 | array,
18 | date,
19 | } from 'semantic-validator/lib/is/basic';
20 |
21 | describe('basic validator creators', () => {
22 | describe('is: same', () => {
23 | it('should return true when values are the same', () => {
24 | expect(same(undefined)(undefined)).toBe(true);
25 | expect(same(null)(null)).toBe(true);
26 | expect(same(true)(true)).toBe(true);
27 | expect(same(123)(123)).toBe(true);
28 | expect(same(123.456)(123.456)).toBe(true);
29 | expect(same(NaN)(NaN)).toBe(true);
30 | expect(same(+0)(-0)).toBe(true);
31 | expect(same('abc')('abc')).toBe(true);
32 | const list = [1, 2, 3];
33 | expect(same(list)(list)).toBe(true);
34 | const obj = { key: 'value' };
35 | expect(same(obj)(obj)).toBe(true);
36 | });
37 | it('should return false when values are not the same', () => {
38 | expect(same(undefined)(null)).toBe(false);
39 | expect(same(true)(false)).toBe(false);
40 | expect(same(123)(123.456)).toBe(false);
41 | expect(same('abc')('def')).toBe(false);
42 | expect(same([1, 2, 3])([1, 2, 3])).toBe(false);
43 | expect(same({ key: 'value' })({ key: 'value' })).toBe(false);
44 | });
45 | });
46 |
47 | describe('is: one of', () => {
48 | it('should return true when the value is one of target', () => {
49 | expect(oneOf(1, 2, 3)(2)).toBe(true);
50 | expect(oneOf('a', 'b', 'c')('b')).toBe(true);
51 | });
52 | it('should return false when the value is not one of target', () => {
53 | expect(oneOf(1, 2, 3)(4)).toBe(false);
54 | expect(oneOf('a', 'b', 'c')('d')).toBe(false);
55 | });
56 | });
57 |
58 | describe('is: defined', () => {
59 | it('should return true when the value is defined', () => {
60 | expect(defined()(null)).toBe(true);
61 | expect(defined()(true)).toBe(true);
62 | expect(defined()(123)).toBe(true);
63 | expect(defined()('abc')).toBe(true);
64 | });
65 | it('should return false when the value is not defined', () => {
66 | expect(defined()(undefined)).toBe(false);
67 | });
68 | });
69 |
70 | describe('is: not defined', () => {
71 | it('should return true when the value is not defined', () => {
72 | expect(notDefined()(undefined)).toBe(true);
73 | });
74 | it('should return false when the value is defined', () => {
75 | expect(notDefined()(null)).toBe(false);
76 | expect(notDefined()(true)).toBe(false);
77 | expect(notDefined()(123)).toBe(false);
78 | expect(notDefined()('abc')).toBe(false);
79 | });
80 | });
81 |
82 | describe('is: nul', () => {
83 | it('should return true when the value is nul', () => {
84 | expect(nul()(null)).toBe(true);
85 | });
86 | it('should return false when the value is not nul', () => {
87 | expect(nul()(undefined)).toBe(false);
88 | expect(nul()(true)).toBe(false);
89 | });
90 | });
91 |
92 | describe('is: nil', () => {
93 | it('should return true when the value is nil', () => {
94 | expect(nil()(null)).toBe(true);
95 | expect(nil()(undefined)).toBe(true);
96 | });
97 | it('should return false when the value is not nil', () => {
98 | expect(nil()(true)).toBe(false);
99 | });
100 | });
101 |
102 | describe('is: bool', () => {
103 | it('should return true when the value is a bool', () => {
104 | expect(bool()(false)).toBe(true);
105 | });
106 | it('should return false when the value is not a bool', () => {
107 | expect(bool()(123)).toBe(false);
108 | });
109 | });
110 |
111 | describe('is: number', () => {
112 | it('should return true when the value is a number', () => {
113 | expect(number()(123)).toBe(true);
114 | });
115 | it('should return false when the value is not a number', () => {
116 | expect(number()('abc')).toBe(false);
117 | });
118 | });
119 |
120 | describe('is: string', () => {
121 | it('should return true when the value is a string', () => {
122 | expect(string()('abc')).toBe(true);
123 | });
124 | it('should return false when the value is not a string', () => {
125 | expect(string()(123)).toBe(false);
126 | });
127 | });
128 |
129 | describe('is: object', () => {
130 | it('should return true when the value is a object', () => {
131 | expect(object()({ key: 'value' })).toBe(true);
132 | expect(object()({})).toBe(true);
133 | });
134 | it('should return false when the value is not a object', () => {
135 | expect(object()(null)).toBe(false);
136 | expect(object()(123)).toBe(false);
137 | });
138 | });
139 |
140 | describe('is: func', () => {
141 | it('should return true when the value is a func', () => {
142 | expect(func()(() => {})).toBe(true);
143 | });
144 | it('should return false when the value is not a func', () => {
145 | expect(func()(123)).toBe(false);
146 | });
147 | });
148 |
149 | describe('is: symbol', () => {
150 | it('should return true when the value is a symbol', () => {
151 | expect(symbol()(Symbol('abc'))).toBe(true);
152 | });
153 | it('should return false when the value is not a symbol', () => {
154 | expect(symbol()('abc')).toBe(false);
155 | });
156 | });
157 |
158 | describe('is: instance of', () => {
159 | it('should return true when the value is the instance of a constructor', () => {
160 | expect(instanceOf(Date)(new Date())).toBe(true);
161 | });
162 | it('should return false when the value is not the instance of a constructor', () => {
163 | expect(instanceOf(Date)(123)).toBe(false);
164 | });
165 | });
166 |
167 | describe('is: float', () => {
168 | it('should return true when the value is a float', () => {
169 | expect(float()(123.456)).toBe(true);
170 | expect(float()(123)).toBe(true);
171 | });
172 | it('should return false when the value is not a float', () => {
173 | expect(float()('123')).toBe(false);
174 | });
175 | });
176 |
177 | describe('is: integer', () => {
178 | it('should return true when the value is a integer', () => {
179 | expect(integer()(123)).toBe(true);
180 | });
181 | it('should return false when the value is not a integer', () => {
182 | expect(integer()(123.456)).toBe(false);
183 | });
184 | });
185 |
186 | describe('is: array', () => {
187 | it('should return true when the value is an array', () => {
188 | expect(array()([1, 2, 3])).toBe(true);
189 | });
190 | it('should return false when the value is not an array', () => {
191 | expect(array()({})).toBe(false);
192 | });
193 | });
194 |
195 | describe('is: date', () => {
196 | it('should return true when the value is a valid date object', () => {
197 | expect(date()(new Date())).toBe(true);
198 | });
199 | it('should return false when the value is not a valid date object', () => {
200 | expect(date()(new Date('invalid date'))).toBe(false);
201 | expect(date()(new Date('invalid date'))).toBe(false);
202 | });
203 | });
204 | });
205 |
--------------------------------------------------------------------------------
/test/is/list.ts:
--------------------------------------------------------------------------------
1 | import {
2 | includes,
3 | excludes,
4 | restrictedBy,
5 | distinct,
6 | } from 'semantic-validator/lib/is/list';
7 |
8 | describe('list validator creators', () => {
9 | describe('is: includes', () => {
10 | it('should return true when the array value includes all includings', () => {
11 | expect(includes(10, 20, 30)([0, 10, 20, 30, 40])).toBe(true);
12 | });
13 | it('should return false when the array value not includes all includings', () => {
14 | expect(includes(10, 20, 30)([0, 20, 40])).toBe(false);
15 | expect(includes(10, 20, 30)(123 as any)).toBe(false);
16 | });
17 | });
18 |
19 | describe('is: excludes', () => {
20 | it('should return true when the array value excludes all includings', () => {
21 | expect(excludes(5, 15, 25)([0, 10, 20, 30])).toBe(true);
22 | });
23 | it('should return false when the array value not excludes all includings', () => {
24 | expect(excludes(5, 15, 25)([5, 10])).toBe(false);
25 | expect(excludes(5, 15, 25)(123 as any)).toBe(false);
26 | });
27 | });
28 |
29 | describe('is: restricted by', () => {
30 | it('should return true when the array value is restricted by allowed items', () => {
31 | expect(restrictedBy(5, 10, 15, 20)([10, 20])).toBe(true);
32 | });
33 | it('should return false when the array value is not restricted by allowed items', () => {
34 | expect(restrictedBy(5, 10, 15, 20)([5, 10, 15, 20, 25, 30])).toBe(false);
35 | expect(restrictedBy(5, 10, 15, 20)(123 as any)).toBe(false);
36 | });
37 | });
38 |
39 | describe('is: distinct', () => {
40 | it('should return true when the array value are all different', () => {
41 | expect(distinct()([1, 2, 3])).toBe(true);
42 | });
43 | it('should return false when the array value are not all different', () => {
44 | expect(distinct()([1, 2, 3, 3])).toBe(false);
45 | expect(distinct()(123 as any)).toBe(false);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/is/math.ts:
--------------------------------------------------------------------------------
1 | import {
2 | equalTo,
3 | greaterThan,
4 | atLeast,
5 | lessThan,
6 | atMost,
7 | between,
8 | fromTo,
9 | } from 'semantic-validator/lib/is/math';
10 |
11 | describe('math validator creators', () => {
12 | describe('is: equal to', () => {
13 | it('should return true when the value is equal to the number', () => {
14 | expect(equalTo(100)(100)).toBe(true);
15 | });
16 | it('should return false when the value is not equal to the number', () => {
17 | expect(equalTo(Infinity)(100)).toBe(false);
18 | expect(equalTo(100)(200)).toBe(false);
19 | expect(equalTo(100)(-100)).toBe(false);
20 | });
21 | });
22 |
23 | describe('is: greater than', () => {
24 | it('should return true when the value is greater than the number', () => {
25 | expect(greaterThan(100)(101)).toBe(true);
26 | });
27 | it('should return false when the value is not greater than the number', () => {
28 | expect(greaterThan(Infinity)(100)).toBe(false);
29 | expect(greaterThan(100)(100)).toBe(false);
30 | expect(greaterThan(100)(99)).toBe(false);
31 | });
32 | });
33 |
34 | describe('is: at least', () => {
35 | it('should return true when the value is at least the number', () => {
36 | expect(atLeast(100)(101)).toBe(true);
37 | expect(atLeast(100)(100)).toBe(true);
38 | });
39 | it('should return false when the value is not at least the number', () => {
40 | expect(atLeast(Infinity)(100)).toBe(false);
41 | expect(atLeast(100)(99)).toBe(false);
42 | });
43 | });
44 |
45 | describe('is: less than', () => {
46 | it('should return true when the value is less than the number', () => {
47 | expect(lessThan(200)(199)).toBe(true);
48 | });
49 | it('should return false when the value is not less than the number', () => {
50 | expect(lessThan(Infinity)(100)).toBe(false);
51 | expect(lessThan(200)(200)).toBe(false);
52 | expect(lessThan(200)(201)).toBe(false);
53 | });
54 | });
55 |
56 | describe('is: at most', () => {
57 | it('should return true when the value is at most the number', () => {
58 | expect(atMost(200)(199)).toBe(true);
59 | expect(atMost(200)(200)).toBe(true);
60 | });
61 | it('should return false when the value is not at most the number', () => {
62 | expect(atMost(Infinity)(100)).toBe(false);
63 | expect(atMost(200)(201)).toBe(false);
64 | });
65 | });
66 |
67 | describe('is: between', () => {
68 | it('should return true when the value is between the numbers', () => {
69 | expect(between(1, 10)(5)).toBe(true);
70 | });
71 | it('should return false when the value is not between the numbers', () => {
72 | expect(between(1, Infinity)(100)).toBe(false);
73 | expect(between(1, 10)(0)).toBe(false);
74 | expect(between(1, 10)(1)).toBe(false);
75 | expect(between(1, 10)(10)).toBe(false);
76 | expect(between(1, 10)(11)).toBe(false);
77 | });
78 | });
79 |
80 | describe('is: from to', () => {
81 | it('should return true when the value is from to the numbers', () => {
82 | expect(fromTo(1, 10)(1)).toBe(true);
83 | expect(fromTo(0, 10)(5)).toBe(true);
84 | expect(fromTo(1, 10)(10)).toBe(true);
85 | });
86 | it('should return false when the value is not from to the numbers', () => {
87 | expect(fromTo(1, Infinity)(100)).toBe(false);
88 | expect(fromTo(1, 10)(0)).toBe(false);
89 | expect(fromTo(1, 10)(11)).toBe(false);
90 | });
91 | });
92 | });
93 |
--------------------------------------------------------------------------------
/test/is/text.ts:
--------------------------------------------------------------------------------
1 | import {
2 | match,
3 | startsWith,
4 | endsWith,
5 | contains,
6 | } from 'semantic-validator/lib/is/text';
7 |
8 | describe('text validator creators', () => {
9 | describe('is: match', () => {
10 | it('should return true when the string value matches the regular expression', () => {
11 | expect(match(/^b[aeiou]t$/)('bat')).toBe(true);
12 | });
13 | it('should return false when the string value not matches the regular expression', () => {
14 | expect(match(123 as any)('never')).toBe(false);
15 | expect(match(/^b[aeiou]t$/)('brt')).toBe(false);
16 | expect(match(/^b[aeiou]t$/)(123 as any)).toBe(false);
17 | });
18 | });
19 |
20 | describe('is: starts with', () => {
21 | it('should return true when the string value starts with the wording', () => {
22 | expect(startsWith('net')('network')).toBe(true);
23 | });
24 | it('should return false when the string value not starts with the wording', () => {
25 | expect(startsWith(123 as any)('never')).toBe(false);
26 | expect(startsWith('net')('artwork')).toBe(false);
27 | expect(startsWith('net')(123 as any)).toBe(false);
28 | });
29 | });
30 |
31 | describe('is: ends with', () => {
32 | it('should return true when the string value ends with the wording', () => {
33 | expect(endsWith('fox')('firefox')).toBe(true);
34 | });
35 | it('should return false when the string value not ends with the wording', () => {
36 | expect(endsWith(123 as any)('never')).toBe(false);
37 | expect(endsWith('fox')('firewall')).toBe(false);
38 | expect(endsWith('fox')(123 as any)).toBe(false);
39 | });
40 | });
41 |
42 | describe('is: contains', () => {
43 | it('should return true when the string value contains the wording', () => {
44 | expect(contains('lie')('believe')).toBe(true);
45 | });
46 | it('should return false when the string value not contains the wording', () => {
47 | expect(contains(123 as any)('never')).toBe(false);
48 | expect(contains('lie')('behave')).toBe(false);
49 | expect(contains('lie')(123 as any)).toBe(false);
50 | });
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/test/is/time.ts:
--------------------------------------------------------------------------------
1 | import {
2 | moment,
3 | laterThan,
4 | atEarliest,
5 | earlierThan,
6 | atLatest,
7 | } from 'semantic-validator/lib/is/time';
8 |
9 | describe('time validator creators', () => {
10 | describe('is: moment', () => {
11 | it('should return true when the date object is at the moment of the base', () => {
12 | expect(moment(new Date(1500000000000))(new Date(1500000000000))).toBe(true);
13 | });
14 | it('should return false when the date object is not at the moment of the base', () => {
15 | expect(moment(new Date(1500000000000))(new Date(1400000000000))).toBe(false);
16 | expect(moment(new Date('invalid date'))(new Date(1500000000000))).toBe(false);
17 | expect(moment(new Date(1500000000000))(new Date('invalid date'))).toBe(false);
18 | expect(moment(123 as any)(new Date(1500000000000))).toBe(false);
19 | expect(moment(new Date(1500000000000))(123 as any)).toBe(false);
20 | });
21 | });
22 |
23 | describe('is: later than', () => {
24 | it('should return true when the date object is later than the base', () => {
25 | expect(laterThan(new Date(1500000000000))(new Date(1500000000001))).toBe(true);
26 | });
27 | it('should return false when the date object is not later than the base', () => {
28 | expect(laterThan(new Date(1500000000000))(new Date(1500000000000))).toBe(false);
29 | expect(laterThan(new Date(1500000000000))(new Date(1499999999999))).toBe(false);
30 | expect(laterThan(new Date('invalid date'))(new Date(1500000000000))).toBe(false);
31 | expect(laterThan(new Date(1500000000000))(new Date('invalid date'))).toBe(false);
32 | expect(laterThan(new Date(1500000000000))(123 as any)).toBe(false);
33 | });
34 | });
35 |
36 | describe('is: at earliest', () => {
37 | it('should return true when the date object is at earliest the base', () => {
38 | expect(atEarliest(new Date(1500000000000))(new Date(1500000000000))).toBe(true);
39 | expect(atEarliest(new Date(1500000000000))(new Date(1500000000001))).toBe(true);
40 | });
41 | it('should return false when the date object is not at earliest the base', () => {
42 | expect(atEarliest(new Date(1500000000000))(new Date(1499999999999))).toBe(false);
43 | expect(atEarliest(new Date('invalid date'))(new Date(1500000000000))).toBe(false);
44 | expect(atEarliest(new Date(1500000000000))(new Date('invalid date'))).toBe(false);
45 | expect(atEarliest(new Date(1500000000000))(123 as any)).toBe(false);
46 | });
47 | });
48 |
49 | describe('is: earlier than', () => {
50 | it('should return true when the date object is earlier than the base', () => {
51 | expect(earlierThan(new Date(1500000000000))(new Date(1499999999999))).toBe(true);
52 | });
53 | it('should return false when the date object is not earlier than the base', () => {
54 | expect(earlierThan(new Date(1500000000000))(new Date(1500000000000))).toBe(false);
55 | expect(earlierThan(new Date(1500000000000))(new Date(1500000000001))).toBe(false);
56 | expect(earlierThan(new Date('invalid date'))(new Date(1500000000000))).toBe(false);
57 | expect(earlierThan(new Date(1500000000000))(new Date('invalid date'))).toBe(false);
58 | expect(earlierThan(new Date(1500000000000))(123 as any)).toBe(false);
59 | });
60 | });
61 |
62 | describe('is: at latest', () => {
63 | it('should return true when the date object is at latest the base', () => {
64 | expect(atLatest(new Date(1500000000000))(new Date(1500000000000))).toBe(true);
65 | expect(atLatest(new Date(1500000000000))(new Date(1499999999999))).toBe(true);
66 | });
67 | it('should return false when the date object is not at latest the base', () => {
68 | expect(atLatest(new Date(1500000000000))(new Date(1500000000001))).toBe(false);
69 | expect(atLatest(new Date('invalid date'))(new Date(1500000000000))).toBe(false);
70 | expect(atLatest(new Date(1500000000000))(new Date('invalid date'))).toBe(false);
71 | expect(atLatest(new Date(1500000000000))(123 as any)).toBe(false);
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/test/op/basic.ts:
--------------------------------------------------------------------------------
1 | import {
2 | so,
3 | not,
4 | and,
5 | or,
6 | every,
7 | some,
8 | shape,
9 | exact,
10 | } from 'semantic-validator/lib/op/basic';
11 |
12 | describe('basic validator operators', () => {
13 | describe('op: so', () => {
14 | const is = {
15 | same: (target: string) => (val: string) => (target === val),
16 | };
17 | it('should return true when the validator returns true', () => {
18 | expect(so(is.same('hello'))('hello')).toBe(true);
19 | });
20 | it('should return false when the validator not returns true', () => {
21 | expect(so(is.same('hello'))('bye')).toBe(false);
22 | });
23 | });
24 |
25 | describe('op: not', () => {
26 | const is = {
27 | same: (target: string) => (val: string) => (target === val),
28 | };
29 | it('should return true when the validator returns false', () => {
30 | expect(not(is.same('hello'))('bye')).toBe(true);
31 | });
32 | it('should return false when the validator not returns false', () => {
33 | expect(not(is.same('hello'))('hello')).toBe(false);
34 | });
35 | });
36 |
37 | describe('op: and', () => {
38 | const is = {
39 | integer: () => (val: any) => Number.isInteger(val),
40 | greaterThan: (num: number) => (val: any) => (val > num),
41 | };
42 | it('should return true when all validators return true', () => {
43 | expect(and(is.integer(), is.greaterThan(100))(120)).toBe(true);
44 | });
45 | it('should return false when not all validators return true', () => {
46 | expect(and(is.integer(), is.greaterThan(100))(80)).toBe(false);
47 | expect(and(is.integer(), is.greaterThan(100))(123.456)).toBe(false);
48 | });
49 | });
50 |
51 | describe('op: or', () => {
52 | const is = {
53 | nul: () => (val: any) => (val === null),
54 | integer: () => (val: any) => Number.isInteger(val),
55 | };
56 | it('should return true when any validators return true', () => {
57 | expect(or(is.nul(), is.integer())(null)).toBe(true);
58 | expect(or(is.nul(), is.integer())(123)).toBe(true);
59 | });
60 | it('should return false when not any validators return true', () => {
61 | expect(or(is.nul(), is.integer())('abc')).toBe(false);
62 | });
63 | });
64 |
65 | describe('op: every', () => {
66 | const is = {
67 | integer: () => (val: any) => Number.isInteger(val),
68 | };
69 | it('should return true when the validator returns true on all elements', () => {
70 | expect(every(is.integer())([1, 2, 3])).toBe(true);
71 | });
72 | it('should return false when the validator not returns true on all elements', () => {
73 | expect(every(is.integer())([1, 2.22, 3])).toBe(false);
74 | });
75 | });
76 |
77 | describe('op: some', () => {
78 | const is = {
79 | integer: () => (val: any) => Number.isInteger(val),
80 | };
81 | it('should return true when the validator returns true on all elements', () => {
82 | expect(some(is.integer())([1, 2, 3])).toBe(true);
83 | expect(some(is.integer())([1.11, 2, 3.33])).toBe(true);
84 | });
85 | it('should return false when the validator not returns true on all elements', () => {
86 | expect(some(is.integer())([1.11, 2.22, 3.33])).toBe(false);
87 | });
88 | });
89 |
90 | describe('op: shape', () => {
91 | const is = {
92 | integer: () => (val: any) => Number.isInteger(val),
93 | string: () => (val: any) => (typeof val === 'string'),
94 | };
95 | it('should return true when all key validators returns true', () => {
96 | expect(
97 | shape({
98 | id: is.integer(),
99 | name: is.string(),
100 | })({
101 | id: 123,
102 | name: 'Mr. Sandman',
103 | }),
104 | ).toBe(true);
105 | expect(
106 | shape({
107 | id: is.integer(),
108 | name: is.string(),
109 | })({
110 | id: 123,
111 | name: 'Mr. Sandman',
112 | active: true,
113 | }),
114 | ).toBe(true);
115 | });
116 | it('should return false when not all key validators returns true', () => {
117 | expect(
118 | shape(123 as any)({
119 | id: 123,
120 | name: 'Mr. Sandman',
121 | }),
122 | ).toBe(false);
123 | expect(
124 | shape({
125 | id: is.integer(),
126 | name: is.string(),
127 | })({
128 | id: 123,
129 | } as any),
130 | ).toBe(false);
131 | expect(
132 | shape({
133 | id: is.integer(),
134 | name: is.string(),
135 | })({
136 | id: 123,
137 | name: 456,
138 | } as any),
139 | ).toBe(false);
140 | });
141 | });
142 |
143 | describe('op: exact', () => {
144 | const is = {
145 | integer: () => (val: any) => Number.isInteger(val),
146 | string: () => (val: any) => (typeof val === 'string'),
147 | };
148 | it('should return true when all key validators returns true on exact object', () => {
149 | expect(
150 | exact({
151 | id: is.integer(),
152 | name: is.string(),
153 | })({
154 | id: 123,
155 | name: 'Mr. Sandman',
156 | }),
157 | ).toBe(true);
158 | });
159 | it('should return false when not all key validators returns true on exact object', () => {
160 | expect(
161 | exact(123 as any)({
162 | id: 123,
163 | name: 'Mr. Sandman',
164 | }),
165 | ).toBe(false);
166 | expect(
167 | exact({
168 | id: is.integer(),
169 | name: is.string(),
170 | })({
171 | id: 123,
172 | name: 'Mr. Sandman',
173 | active: true,
174 | } as any),
175 | ).toBe(false);
176 | expect(
177 | exact({
178 | id: is.integer(),
179 | name: is.string(),
180 | })({
181 | id: 123,
182 | } as any),
183 | ).toBe(false);
184 | expect(
185 | exact({
186 | id: is.integer(),
187 | name: is.string(),
188 | })({
189 | id: 123,
190 | name: 456,
191 | } as any),
192 | ).toBe(false);
193 | });
194 | });
195 | });
196 |
--------------------------------------------------------------------------------
/test/op/converter.ts:
--------------------------------------------------------------------------------
1 | import {
2 | convert,
3 | toFloat,
4 | toInteger,
5 | toLength,
6 | toSplit,
7 | toKeys,
8 | toValues,
9 | toDate,
10 | } from 'semantic-validator/lib/op/converter';
11 |
12 | describe('converter validator operators', () => {
13 | describe('op: convert', () => {
14 | const is = {
15 | greaterThan: (num: number) => (val: any) => (val > num),
16 | };
17 | it('should return true when successfully convert the value and validate.', () => {
18 | expect(
19 | convert(
20 | (numeric: string) => parseFloat(numeric),
21 | is.greaterThan(100),
22 | )('150'),
23 | ).toBe(true);
24 | expect(
25 | convert(
26 | (numeric: string) => parseFloat(numeric),
27 | )('150'),
28 | ).toBe(true);
29 | });
30 | it('should return true when not successfully convert the value and validate', () => {
31 | expect(
32 | convert(
33 | (numeric: string) => parseFloat(numeric),
34 | is.greaterThan(100),
35 | )('50'),
36 | ).toBe(false);
37 | });
38 | });
39 |
40 | describe('op: to float', () => {
41 | const is = {
42 | greaterThan: (num: number) => (val: any) => (val > num),
43 | };
44 | it('should return true when successfully convert to a float and validate', () => {
45 | expect(toFloat(is.greaterThan(1.2))('1.5')).toBe(true);
46 | });
47 | it('should return true when not successfully convert to a float and validate', () => {
48 | expect(toFloat(is.greaterThan(1.2))('two')).toBe(false);
49 | expect(toFloat(is.greaterThan(1.2))('0.8')).toBe(false);
50 | });
51 | });
52 |
53 | describe('op: to integer', () => {
54 | const is = {
55 | greaterThan: (num: number) => (val: any) => (val > num),
56 | };
57 | it('should return true when successfully convert to an integer and validate', () => {
58 | expect(toInteger(is.greaterThan(100))('150')).toBe(true);
59 | });
60 | it('should return true when not successfully convert to an integer and validate', () => {
61 | expect(toInteger(is.greaterThan(100))('two')).toBe(false);
62 | expect(toInteger(is.greaterThan(100))('123.456')).toBe(false);
63 | expect(toInteger(is.greaterThan(100))('50')).toBe(false);
64 | });
65 | });
66 |
67 | describe('op: to length', () => {
68 | const is = {
69 | atLeast: (num: number) => (val: any) => (val >= num),
70 | };
71 | it('should return true when successfully get the length and validate', () => {
72 | expect(toLength(is.atLeast(3))(['a', 'b', 'c'])).toBe(true);
73 | expect(toLength(is.atLeast(3))('abc')).toBe(true);
74 | });
75 | it('should return true when not successfully get the length and validate', () => {
76 | expect(toLength(is.atLeast(3))(['a', 'b'])).toBe(false);
77 | expect(toLength(is.atLeast(3))('ab')).toBe(false);
78 | expect(toLength(is.atLeast(3))(3)).toBe(false);
79 | });
80 | });
81 |
82 | describe('op: to split', () => {
83 | const every = (validator: (val: any) => boolean): (list: any[]) => boolean => (
84 | (list: any[]) => (Array.isArray(list) && list.every(item => validator(item)))
85 | );
86 | const is = {
87 | startsWith: (wording: string) => (val: string) => (val.startsWith(wording)),
88 | };
89 | it('should return true when successfully split to the array and validate', () => {
90 | expect(
91 | toSplit(
92 | ',',
93 | every(is.startsWith('c')),
94 | )('candy,cookie,coffee'),
95 | ).toBe(true);
96 | });
97 | it('should return true when not successfully split to the array and validate', () => {
98 | expect(
99 | toSplit(
100 | ',',
101 | every(is.startsWith('c')),
102 | )('candy,cookie,tea'),
103 | ).toBe(false);
104 | expect(
105 | toSplit(
106 | ',',
107 | every(is.startsWith('c')),
108 | )(123),
109 | ).toBe(false);
110 | });
111 | });
112 |
113 | describe('op: to keys', () => {
114 | const every = (validator: (val: any) => boolean): (list: any[]) => boolean => (
115 | (list: any[]) => (Array.isArray(list) && list.every(item => validator(item)))
116 | );
117 | const is = {
118 | oneOf: (...items: any[]) => (val: any) => (items.includes(val)),
119 | };
120 | it('should return true when successfully get keys of an object and validate', () => {
121 | expect(
122 | toKeys(
123 | every(is.oneOf('id', 'name')),
124 | )({ id: 123, name: 'Mario' }),
125 | ).toBe(true);
126 | });
127 | it('should return true when not successfully get keys of an object and validate', () => {
128 | expect(
129 | toKeys(
130 | every(is.oneOf('id', 'name')),
131 | )({ id: 123, name: 'Mario', age: 20 }),
132 | ).toBe(false);
133 | });
134 | });
135 |
136 | describe('op: to values', () => {
137 | const every = (validator: (val: any) => boolean): (list: any[]) => boolean => (
138 | (list: any[]) => (Array.isArray(list) && list.every(item => validator(item)))
139 | );
140 | const is = {
141 | integer: () => (val: any) => Number.isInteger(val),
142 | };
143 | it('should return true when successfully get values of an object and validate', () => {
144 | expect(
145 | toValues(
146 | every(is.integer()),
147 | )({ people: 64, seats: 80 }),
148 | ).toBe(true);
149 | });
150 | it('should return true when not successfully get values of an object and validate', () => {
151 | expect(
152 | toValues(
153 | every(is.integer()),
154 | )({ people: 640, seats: 'many' }),
155 | ).toBe(false);
156 | });
157 | });
158 |
159 | describe('op: to date', () => {
160 | const is = {
161 | moment: (base: Date) => (date: Date) => (date.getTime() === base.getTime()),
162 | };
163 | it('should return true when successfully convert to a date object and validate', () => {
164 | expect(toDate(is.moment(new Date(1561889036718)))(1561889036718)).toBe(true);
165 | });
166 | it('should return true when not successfully convert to a date object and validate', () => {
167 | expect(toDate(is.moment(new Date(1561889036718)))(1500000000000)).toBe(false);
168 | expect(toDate(is.moment(new Date(1561889036718)))('invalid date')).toBe(false);
169 | expect(toDate(is.moment(new Date(1561889036718)))(123 as any)).toBe(false);
170 | });
171 | });
172 | });
173 |
--------------------------------------------------------------------------------
/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "strict": true,
7 | "strictNullChecks": true,
8 | "strictPropertyInitialization": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "paths": {
13 | "semantic-validator": [
14 | "../src"
15 | ],
16 | "semantic-validator/*": [
17 | "../src/*"
18 | ]
19 | },
20 | },
21 | "include": [
22 | "**/*.ts"
23 | ]
24 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "module": "commonjs",
5 | "moduleResolution": "node",
6 | "esModuleInterop": true,
7 | "sourceMap": true,
8 | "declaration": true,
9 | "strict": true,
10 | "strictNullChecks": true,
11 | "strictPropertyInitialization": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "outDir": "dist",
16 | "target": "es5",
17 | "lib": [
18 | "es2015",
19 | ]
20 | },
21 | "include": [
22 | "src/**/*.ts"
23 | ],
24 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | mode: 'production',
5 | entry: './src/index.ts',
6 | output: {
7 | path: path.resolve(__dirname, './dist'),
8 | filename: 'bundle.js',
9 | library: {
10 | root: 'semanticValidator',
11 | commonjs: 'semantic-validator',
12 | amd: 'semantic-validator'
13 | },
14 | libraryTarget: 'umd'
15 | },
16 | resolve: {
17 | extensions: ['.ts', '.js'],
18 | },
19 | devtool: "source-map",
20 | module: {
21 | rules: [
22 | {
23 | test: /\.ts$/,
24 | use: [
25 | {
26 | loader: 'ts-loader',
27 | options: {
28 | compilerOptions: {
29 | baseUrl: "./",
30 | module: "commonjs",
31 | moduleResolution: "node",
32 | sourceMap: true,
33 | target: "es5",
34 | lib: [
35 | "es2015",
36 | ]
37 | },
38 | },
39 | },
40 | ],
41 | },
42 | ],
43 | },
44 | };
45 |
--------------------------------------------------------------------------------