n4s 3.0.0 → 4.0.0-dev-1aae50

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/LICENSE +1 -2
  3. package/README.md +2 -5
  4. package/compose/package.json +7 -0
  5. package/compounds/package.json +7 -0
  6. package/dist/cjs/compose.development.js +139 -0
  7. package/dist/cjs/compose.js +7 -0
  8. package/dist/cjs/compose.production.js +1 -0
  9. package/dist/cjs/compounds.development.js +132 -0
  10. package/dist/cjs/compounds.js +7 -0
  11. package/dist/cjs/compounds.production.js +1 -0
  12. package/dist/cjs/n4s.development.js +602 -0
  13. package/dist/cjs/n4s.js +7 -0
  14. package/dist/cjs/n4s.production.js +1 -0
  15. package/dist/cjs/package.json +1 -0
  16. package/dist/cjs/schema.development.js +144 -0
  17. package/dist/cjs/schema.js +7 -0
  18. package/dist/cjs/schema.production.js +1 -0
  19. package/dist/es/compose.development.js +137 -0
  20. package/dist/es/compose.production.js +1 -0
  21. package/dist/es/compounds.development.js +130 -0
  22. package/dist/es/compounds.production.js +1 -0
  23. package/dist/es/n4s.development.js +597 -0
  24. package/dist/es/n4s.production.js +1 -0
  25. package/dist/es/package.json +1 -0
  26. package/dist/es/schema.development.js +140 -0
  27. package/dist/es/schema.production.js +1 -0
  28. package/dist/umd/compose.development.js +143 -0
  29. package/dist/umd/compose.production.js +1 -0
  30. package/dist/umd/compounds.development.js +136 -0
  31. package/dist/umd/compounds.production.js +1 -0
  32. package/dist/umd/n4s.development.js +606 -0
  33. package/dist/umd/n4s.production.js +1 -0
  34. package/dist/umd/schema.development.js +148 -0
  35. package/dist/umd/schema.production.js +1 -0
  36. package/docs/README.md +2 -5
  37. package/docs/_sidebar.md +1 -2
  38. package/docs/external.md +1 -28
  39. package/docs/rules.md +28 -0
  40. package/package.json +129 -42
  41. package/schema/package.json +7 -0
  42. package/tsconfig.json +8 -0
  43. package/types/compose.d.ts +134 -0
  44. package/types/compounds.d.ts +146 -0
  45. package/types/n4s.d.ts +167 -0
  46. package/types/schema.d.ts +151 -0
  47. package/config/jest/jest.setup.js +0 -14
  48. package/config/rollup/enforce.js +0 -8
  49. package/config/rollup/rollup.config.js +0 -3
  50. package/docs/compound.md +0 -187
  51. package/docs/custom.md +0 -52
  52. package/docs/template.md +0 -53
  53. package/esm/n4s.mjs.development.js +0 -1101
  54. package/esm/n4s.mjs.production.js +0 -1101
  55. package/esm/n4s.mjs.production.min.js +0 -1
  56. package/esm/package.json +0 -1
  57. package/jest.config.js +0 -3
  58. package/n4s.cjs.development.js +0 -1103
  59. package/n4s.cjs.production.js +0 -1103
  60. package/n4s.cjs.production.min.js +0 -1
  61. package/n4s.js +0 -7
  62. package/n4s.umd.development.js +0 -1109
  63. package/n4s.umd.production.js +0 -1192
  64. package/n4s.umd.production.min.js +0 -1
@@ -1,1109 +0,0 @@
1
- (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
- typeof define === 'function' && define.amd ? define(factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.n4s = factory());
5
- }(this, (function () { 'use strict';
6
-
7
- /**
8
- * Stores values and configuration passed down to compound rules.
9
- *
10
- * @param {Object} content
11
- */
12
- function EnforceContext(content) {
13
- Object.assign(this, content);
14
- }
15
- /**
16
- * Sets an EnforceContext config `failFast`
17
- *
18
- * @param {Boolean} failFast
19
- * @return {EnforceContext}
20
- */
21
-
22
- EnforceContext.prototype.setFailFast = function (failFast) {
23
- this.failFast = !!failFast;
24
- return this;
25
- };
26
- /**
27
- * Extracts the literal value from an EnforceContext object
28
- * @param {*} value
29
- * @return {*}
30
- */
31
-
32
-
33
- EnforceContext.unwrap = function unwrap(value) {
34
- return EnforceContext.is(value) ? value.value : value;
35
- };
36
- /**
37
- * Wraps a literal value within a context.
38
- * @param {*} value
39
- * @return {EnforceContext}
40
- */
41
-
42
-
43
- EnforceContext.wrap = function wrap(value) {
44
- return EnforceContext.is(value) ? value : new EnforceContext({
45
- value
46
- });
47
- };
48
- /**
49
- * Checks whether a given value is an EnforceContext instance
50
- *
51
- * @param {*} value
52
- * @returns {boolean}
53
- */
54
-
55
-
56
- EnforceContext.is = function is(value) {
57
- return value instanceof EnforceContext;
58
- };
59
-
60
- /**
61
- * A safe hasOwnProperty access
62
- */
63
- function hasOwnProperty(obj, key) {
64
- return Object.prototype.hasOwnProperty.call(obj, key);
65
- }
66
-
67
- function bindNot(fn) {
68
- return function () {
69
- return !fn.apply(this, arguments);
70
- };
71
- }
72
-
73
- function isBoolean(value) {
74
- return !!value === value;
75
- }
76
-
77
- const isNotBoolean = bindNot(isBoolean);
78
-
79
- function isNumeric(value) {
80
- const result = !isNaN(parseFloat(value)) && !isNaN(Number(value)) && isFinite(value);
81
- return Boolean(result);
82
- }
83
- const isNotNumeric = bindNot(isNumeric);
84
-
85
- function lengthEquals(value, arg1) {
86
- return value.length === Number(arg1);
87
- }
88
- const lengthNotEquals = bindNot(lengthEquals);
89
-
90
- function isEmpty(value) {
91
- if (!value) {
92
- return true;
93
- } else if (isNumeric(value)) {
94
- return value === 0;
95
- } else if (hasOwnProperty(value, 'length')) {
96
- return lengthEquals(value, 0);
97
- } else if (typeof value === 'object') {
98
- return lengthEquals(Object.keys(value), 0);
99
- }
100
-
101
- return true;
102
- }
103
- const isNotEmpty = bindNot(isEmpty);
104
-
105
- function isUndefined(value) {
106
- return value === undefined;
107
- }
108
- const isNotUndefined = bindNot(isUndefined);
109
-
110
- const HAS_WARNINGS = 'hasWarnings';
111
- const HAS_ERRORS = 'hasErrors';
112
-
113
- /**
114
- * Stores a rule result in an easy to inspect and manipulate structure.
115
- *
116
- * @param {boolean|RuleResult} ruleRunResult
117
- */
118
-
119
- function RuleResult(ruleRunResult) {
120
- if (isUndefined(ruleRunResult)) {
121
- return;
122
- }
123
-
124
- if (isBoolean(ruleRunResult)) {
125
- this.setFailed(!ruleRunResult);
126
- } else {
127
- this.extend(ruleRunResult);
128
- }
129
- }
130
- /**
131
- * Determines whether a given value is a RuleResult instance
132
- * @param {*} res
133
- * @return {boolean}
134
- */
135
-
136
- RuleResult.is = function (res) {
137
- return res instanceof RuleResult;
138
- };
139
- /**
140
- * Marks the current result object as an array
141
- */
142
-
143
-
144
- RuleResult.prototype.asArray = function () {
145
- this.isArray = true;
146
- return this;
147
- };
148
- /**
149
- * @param {string} key
150
- * @param {value} value
151
- * @return {RuleResult} current instance
152
- */
153
-
154
-
155
- RuleResult.prototype.setAttribute = function (key, value) {
156
- this[key] = value;
157
- return this;
158
- };
159
- /**
160
- * @param {boolean} failed
161
- * @return {RuleResult} current instance
162
- */
163
-
164
-
165
- RuleResult.prototype.setFailed = function (failed) {
166
- this.setAttribute(this.warn ? HAS_WARNINGS : HAS_ERRORS, failed);
167
- return this.setAttribute('failed', failed);
168
- };
169
- /**
170
- * Adds a nested result object
171
- *
172
- * @param {string} key
173
- * @param {RuleResult} child
174
- */
175
-
176
-
177
- RuleResult.prototype.setChild = function (key, child) {
178
- const isWarning = this[HAS_WARNINGS] || child[HAS_WARNINGS] || child.warn || this.warn;
179
- this.setAttribute(HAS_WARNINGS, isWarning && child.failed || false);
180
- this.setAttribute(HAS_ERRORS, this[HAS_ERRORS] || child[HAS_ERRORS] || !isWarning && child.failed || false);
181
- this.setFailed(this.failed || child.failed);
182
- this.children = this.children || {};
183
- this.children[key] = child;
184
- return child;
185
- };
186
- /**
187
- * Retrieves a child by its key
188
- *
189
- * @param {string} key
190
- * @return {RuleResult|undefined}
191
- */
192
-
193
-
194
- RuleResult.prototype.getChild = function (key) {
195
- return (this.children || {})[key];
196
- };
197
- /**
198
- * Extends current instance with a new provided result
199
- * @param {Boolean|RuleResult} newRes
200
- */
201
-
202
-
203
- RuleResult.prototype.extend = function (newRes) {
204
- const res = RuleResult.is(newRes) ? newRes : new RuleResult().setAttribute('warn', !!this.warn).setFailed(!newRes);
205
- const failed = this.failed || res.failed;
206
- const children = mergeChildren(res, this).children;
207
- Object.assign(this, res);
208
-
209
- if (!isEmpty(children)) {
210
- this.children = children;
211
- }
212
-
213
- this.setFailed(failed);
214
- this.setAttribute(HAS_WARNINGS, !!(this[HAS_WARNINGS] || res[HAS_WARNINGS]));
215
- this.setAttribute(HAS_ERRORS, !!(this[HAS_ERRORS] || res[HAS_ERRORS]));
216
- };
217
-
218
- Object.defineProperty(RuleResult.prototype, 'pass', {
219
- get() {
220
- return !this.failed;
221
- }
222
-
223
- });
224
- /**
225
- * Deeply merge the nested children of compound rules
226
- *
227
- * @param {?RuleResult} base
228
- * @param {?RuleResult} add
229
- * @return {RuleResult}
230
- */
231
-
232
- function mergeChildren(base, add) {
233
- const isRuleResultBase = RuleResult.is(base);
234
- const isRuleResultAdd = RuleResult.is(add); // If both base and add are result objects
235
-
236
- if (isRuleResultBase && isRuleResultAdd) {
237
- // Use failed if either is failing
238
- base.setFailed(base.failed || add.failed); // If neither has a children object, or the children object is
239
-
240
- if (isEmpty(base.children) && isEmpty(add.children)) {
241
- return base;
242
- } // If both have a children object
243
-
244
-
245
- if (base.children && add.children) {
246
- // Merge all the "right side" children back to base
247
- for (const key in base.children) {
248
- mergeChildren(base.children[key], add.children[key]);
249
- } // If a child exists in "add" but not in "base", just copy the child as is
250
-
251
-
252
- for (const key in add.children) {
253
- if (!hasOwnProperty(base.children, key)) {
254
- base.setChild(key, add.children[key]);
255
- }
256
- } // Return the modified base object
257
-
258
-
259
- return base; // If base has no children (but add does)
260
- } else if (!base.children) {
261
- // Use add's children
262
- base.children = add.children; // If add has no children
263
- } else if (!add.children) {
264
- // return base as is
265
- return base;
266
- } // If only base is `RuleResult`
267
-
268
- } else if (isRuleResultBase) {
269
- // Return base as is
270
- return base; // If only add is RuleResult
271
- } else if (isRuleResultAdd) {
272
- // Return add as is
273
- return add;
274
- } // That's a weird case. Let's fail. Very unlikely.
275
-
276
-
277
- return new RuleResult(false);
278
- }
279
-
280
- const RUN_RULE = 'run';
281
- const TEST_RULE = 'test';
282
- const MODE_ALL = 'all';
283
- const MODE_ONE = 'one';
284
- const MODE_ANY = 'any';
285
-
286
- function isFunction (v) {
287
- return typeof v === 'function';
288
- }
289
-
290
- function asArray(possibleArg) {
291
- return [].concat(possibleArg);
292
- }
293
-
294
- /**
295
- * Determines whether we should bail out of an enforcement.
296
- *
297
- * @param {EnforceContext} ctx
298
- * @param {RuleResult} result
299
- */
300
-
301
- function shouldFailFast(ctx, result) {
302
- if (result.pass || result.warn) {
303
- return false;
304
- }
305
-
306
- return !!EnforceContext.is(ctx) && ctx.failFast;
307
- }
308
-
309
- /**
310
- * Runs multiple enforce rules that are passed to compounds.
311
- * Example: enforce.allOf(enforce.ruleOne(), enforce.ruleTwo().ruleThree())
312
- *
313
- * @param {{run: Function}[]} ruleGroups
314
- * @param {*} value
315
- * @return {RuleResult}
316
- */
317
-
318
- function runLazyRules(ruleGroups, value) {
319
- const result = new RuleResult(true);
320
-
321
- for (const chain of asArray(ruleGroups)) {
322
- if (shouldFailFast(value, result)) {
323
- break;
324
- }
325
-
326
- result.extend(runLazyRule(chain, value));
327
- }
328
-
329
- return result;
330
- }
331
- /**
332
- * Runs a single lazy rule
333
- *
334
- * @param {{run: Function}} ruleGroup
335
- * @param {*} value
336
- * @return {boolean|RuleResult}
337
- */
338
-
339
- function runLazyRule(ruleGroup, value) {
340
- return ruleGroup[RUN_RULE](value);
341
- }
342
-
343
- /**
344
- * Runs chains of rules
345
- *
346
- * @param {EnforceContext} value
347
- * @param {[{test: Function, run: Function}]} rules
348
- * @param {RuleResult} options
349
- */
350
-
351
- function runCompoundChain(value, rules, options) {
352
- const result = new RuleResult(true);
353
-
354
- if (isEmpty(rules)) {
355
- result.setFailed(true);
356
- }
357
-
358
- const failedResults = [];
359
- let count = 0;
360
-
361
- for (const chain of rules) {
362
- // Inner result for each iteration
363
- const currentResult = runLazyRule(chain, value);
364
- const pass = currentResult.pass;
365
-
366
- if (pass) {
367
- count++;
368
- } else {
369
- failedResults.push(currentResult);
370
- }
371
-
372
- if (options) {
373
- // "anyOf" is a special case.
374
- // It shouldn't extend with failed results,
375
- // that's why we continue
376
- if (options.mode === MODE_ANY) {
377
- if (pass) {
378
- result.extend(currentResult);
379
- break;
380
- }
381
-
382
- continue;
383
- }
384
-
385
- result.extend(currentResult); // MODE_ALL: All must pass.
386
- // If one failed, exit.
387
-
388
- if (options.mode === MODE_ALL) {
389
- if (!pass) {
390
- break;
391
- }
392
- } // MODE_ONE: only one must pass.
393
- // If more than one passed, exit.
394
-
395
-
396
- if (options.mode === MODE_ONE) {
397
- result.setFailed(count !== 1);
398
-
399
- if (count > 1) {
400
- break;
401
- }
402
- }
403
- } else {
404
- result.extend(currentResult);
405
-
406
- if (pass) {
407
- break;
408
- }
409
- }
410
- }
411
-
412
- if (result.pass && count === 0) {
413
- result.setFailed(true); // In some cases we do not want to extend failures, for example - in ANY
414
- // when there is a valid response, so we do it before returning
415
-
416
- failedResults.forEach(failedResult => result.extend(failedResult));
417
- }
418
-
419
- return result;
420
- }
421
-
422
- function setFnName(fn, value) {
423
- return Object.defineProperty(fn, 'name', {
424
- value
425
- });
426
- }
427
-
428
- /**
429
- * ES5 Transpilation increases the size of spread arguments by a lot.
430
- * Wraps a function and passes its spread params as an array.
431
- *
432
- * @param {Function} cb
433
- * @param {String} [fnName]
434
- * @return {Function}
435
- */
436
-
437
- function withArgs(cb, fnName) {
438
- return setFnName((...args) => {
439
- const right = args.splice(cb.length - 1);
440
- return cb.apply(null, args.concat([right]));
441
- }, fnName || cb.name);
442
- }
443
-
444
- /**
445
- * Runs a chaen of rules, making sure that all assertions pass
446
- *
447
- * @param {EnforceContext} value
448
- * @param {[{test: Function, run: Function}]} ruleChains
449
- * @return {RuleResult}
450
- */
451
-
452
- function allOf(value, rules) {
453
- return runCompoundChain(value, rules, {
454
- mode: MODE_ALL
455
- });
456
- }
457
-
458
- var allOf$1 = withArgs(allOf);
459
-
460
- /**
461
- * Runs chains of rules, making sure
462
- * that at least one assertion passes
463
- *
464
- * @param {EnforceContext} value
465
- * @param {[{test: Function, run: Function}]} ruleChains
466
- * @return {RuleResult}
467
- */
468
-
469
- function anyOf(value, ruleChains) {
470
- return runCompoundChain(value, ruleChains, {
471
- mode: MODE_ANY
472
- });
473
- }
474
-
475
- var anyOf$1 = withArgs(anyOf);
476
-
477
- function isArray(value) {
478
- return Boolean(Array.isArray(value));
479
- }
480
- const isNotArray = bindNot(isArray);
481
-
482
- /**
483
- * Asserts that each element in an array passes
484
- * at least one of the provided rule chain
485
- *
486
- * @param {EnforceContext} value
487
- * @param {[{test: Function, run: Function}]} ruleChains
488
- * @return {RuleResult}
489
- */
490
-
491
- function isArrayOf(value, ruleChains) {
492
- const plainValue = EnforceContext.unwrap(value);
493
- const result = new RuleResult(true).asArray(); // Fails if current value is not an array
494
-
495
- if (isNotArray(plainValue)) {
496
- return result.setFailed(true);
497
- }
498
-
499
- for (let i = 0; i < plainValue.length; i++) {
500
- // Set result per each item in the array|
501
- result.setChild(i, runCompoundChain(EnforceContext.wrap(plainValue[i]).setFailFast(value.failFast), ruleChains, {
502
- mode: MODE_ANY
503
- }));
504
- }
505
-
506
- return result;
507
- }
508
-
509
- var isArrayOf$1 = withArgs(isArrayOf);
510
-
511
- /**
512
- * @param {EnforceContext} value
513
- * @param {[{test: Function, run: Function}]} ruleChains
514
- * @return {RuleResult}
515
- */
516
-
517
- function oneOf(value, rules) {
518
- return runCompoundChain(value, rules, {
519
- mode: MODE_ONE
520
- });
521
- }
522
-
523
- var oneOf$1 = withArgs(oneOf);
524
-
525
- function isNull(value) {
526
- return value === null;
527
- }
528
- const isNotNull = bindNot(isNull);
529
-
530
- /**
531
- * @param {Array} ObjectEntry Object and key leading to current value
532
- * @param {Function[]} rules Rules to validate the value with
533
- */
534
-
535
- function optional(inputObject, ruleGroups) {
536
- const {
537
- obj,
538
- key
539
- } = inputObject; // If current value is not defined, undefined or null
540
- // Pass without further assertions
541
-
542
- if (!hasOwnProperty(obj, key) || isUndefined(obj[key] || isNull(obj[key]))) {
543
- return true;
544
- } // Pass if exists but no assertions
545
-
546
-
547
- if (isEmpty(ruleGroups)) {
548
- return true;
549
- } // Run chain with `all` mode
550
-
551
-
552
- return runCompoundChain(obj[key], ruleGroups, {
553
- mode: MODE_ALL
554
- });
555
- }
556
-
557
- var optional$1 = withArgs(optional);
558
-
559
- /**
560
- * @param {EnforceContext} inputObject Data object that gets validated
561
- * @param {Object} shapeObj Shape definition
562
- * @param {Object} options
563
- * @param {boolean} options.loose Ignore extra keys not defined in shapeObj
564
- */
565
-
566
- function shape(inputObject, shapeObj, options) {
567
- // Extract the object from context
568
- const obj = EnforceContext.unwrap(inputObject); // Create a new result object
569
-
570
- const result = new RuleResult(true); // Iterate over the shape keys
571
-
572
- for (const key in shapeObj) {
573
- const current = shapeObj[key];
574
- const value = obj[key];
575
-
576
- if (shouldFailFast(value, result)) {
577
- break;
578
- } // Set each key in the result object
579
-
580
-
581
- result.setChild(key, runLazyRule(current, new EnforceContext({
582
- value,
583
- obj,
584
- key
585
- }).setFailFast(inputObject.failFast)));
586
- } // If mode is not loose
587
-
588
-
589
- if (!(options || {}).loose) {
590
- // Check that each key in the input object exists in the shape
591
- for (const key in obj) {
592
- if (!hasOwnProperty(shapeObj, key)) {
593
- return result.setFailed(true);
594
- }
595
- }
596
- }
597
-
598
- return result;
599
- }
600
- const loose = (obj, shapeObj) => shape(obj, shapeObj, {
601
- loose: true
602
- });
603
-
604
- var compounds = {
605
- allOf: allOf$1,
606
- anyOf: anyOf$1,
607
- isArrayOf: isArrayOf$1,
608
- loose,
609
- oneOf: oneOf$1,
610
- optional: optional$1,
611
- shape
612
- };
613
-
614
- /**
615
- * Takes a value. If it is a function, runs it and returns the result.
616
- * Otherwise, returns the value as is.
617
- *
618
- * @param {Function|*} value Value to return. Run it if a function.
619
- * @param {Any[]} [args] Arguments to pass if a function
620
- * @return {Any}
621
- */
622
-
623
- function optionalFunctionValue(value, args) {
624
- return isFunction(value) ? value.apply(null, args) : value;
625
- }
626
-
627
- function message(value, msg) {
628
- return optionalFunctionValue(msg, [EnforceContext.unwrap(value)]);
629
- }
630
-
631
- function warn(_, isWarn = true) {
632
- return isWarn;
633
- }
634
-
635
- var ruleMeta = {
636
- warn,
637
- message
638
- };
639
-
640
- function isString (v) {
641
- return String(v) === v;
642
- }
643
-
644
- function endsWith(value, arg1) {
645
- return isString(value) && isString(arg1) && value.endsWith(arg1);
646
- }
647
- const doesNotEndWith = bindNot(endsWith);
648
-
649
- function equals(value, arg1) {
650
- return value === arg1;
651
- }
652
- const notEquals = bindNot(equals);
653
-
654
- function greaterThan(value, arg1) {
655
- return isNumeric(value) && isNumeric(arg1) && Number(value) > Number(arg1);
656
- }
657
-
658
- function greaterThanOrEquals(value, arg1) {
659
- return isNumeric(value) && isNumeric(arg1) && Number(value) >= Number(arg1);
660
- }
661
-
662
- function inside(value, arg1) {
663
- if (Array.isArray(arg1) && /^[s|n|b]/.test(typeof value)) {
664
- return arg1.indexOf(value) !== -1;
665
- } // both value and arg1 are strings
666
-
667
-
668
- if (isString(arg1) && isString(value)) {
669
- return arg1.indexOf(value) !== -1;
670
- }
671
-
672
- return false;
673
- }
674
- const notInside = bindNot(inside);
675
-
676
- function lessThanOrEquals(value, arg1) {
677
- return isNumeric(value) && isNumeric(arg1) && Number(value) <= Number(arg1);
678
- }
679
-
680
- function isBetween(value, min, max) {
681
- return greaterThanOrEquals(value, min) && lessThanOrEquals(value, max);
682
- }
683
- const isNotBetween = bindNot(isBetween);
684
-
685
- /**
686
- * Validates that a given value is an even number
687
- * @param {Number|String} value Value to be validated
688
- * @return {Boolean}
689
- */
690
-
691
- const isEven = value => {
692
- if (isNumeric(value)) {
693
- return value % 2 === 0;
694
- }
695
-
696
- return false;
697
- };
698
-
699
- function isNaN$1(value) {
700
- return Number.isNaN(value);
701
- }
702
- const isNotNaN = bindNot(isNaN$1);
703
-
704
- function isNegative(value) {
705
- if (isNumeric(value)) {
706
- return Number(value) < 0;
707
- }
708
-
709
- return false;
710
- }
711
- const isPositive = bindNot(isNegative);
712
-
713
- function isNumber(value) {
714
- return Boolean(typeof value === 'number');
715
- }
716
- const isNotNumber = bindNot(isNumber);
717
-
718
- /**
719
- * Validates that a given value is an odd number
720
- * @param {Number|String} value Value to be validated
721
- * @return {Boolean}
722
- */
723
-
724
- const isOdd = value => {
725
- if (isNumeric(value)) {
726
- return value % 2 !== 0;
727
- }
728
-
729
- return false;
730
- };
731
-
732
- const isNotString = bindNot(isString);
733
-
734
- function isTruthy(value) {
735
- return !!value;
736
- }
737
- const isFalsy = bindNot(isTruthy);
738
-
739
- function lessThan(value, arg1) {
740
- return isNumeric(value) && isNumeric(arg1) && Number(value) < Number(arg1);
741
- }
742
-
743
- function longerThan(value, arg1) {
744
- return value.length > Number(arg1);
745
- }
746
-
747
- function longerThanOrEquals(value, arg1) {
748
- return value.length >= Number(arg1);
749
- }
750
-
751
- function matches(value, regex) {
752
- if (regex instanceof RegExp) {
753
- return regex.test(value);
754
- } else if (isString(regex)) {
755
- return new RegExp(regex).test(value);
756
- } else {
757
- return false;
758
- }
759
- }
760
- const notMatches = bindNot(matches);
761
-
762
- function numberEquals(value, arg1) {
763
- return isNumeric(value) && isNumeric(arg1) && Number(value) === Number(arg1);
764
- }
765
- const numberNotEquals = bindNot(numberEquals);
766
-
767
- function shorterThan(value, arg1) {
768
- return value.length < Number(arg1);
769
- }
770
-
771
- function shorterThanOrEquals(value, arg1) {
772
- return value.length <= Number(arg1);
773
- }
774
-
775
- function startsWith(value, arg1) {
776
- return isString(value) && isString(arg1) && value.startsWith(arg1);
777
- }
778
- const doesNotStartWith = bindNot(startsWith);
779
-
780
- function rules() {
781
- return {
782
- doesNotEndWith,
783
- doesNotStartWith,
784
- endsWith,
785
- equals,
786
- greaterThan,
787
- greaterThanOrEquals,
788
- gt: greaterThan,
789
- gte: greaterThanOrEquals,
790
- inside,
791
- isArray,
792
- isBetween,
793
- isBoolean,
794
- isEmpty,
795
- isEven,
796
- isFalsy,
797
- isNaN: isNaN$1,
798
- isNegative,
799
- isNotArray,
800
- isNotBetween,
801
- isNotBoolean,
802
- isNotEmpty,
803
- isNotNaN,
804
- isNotNull,
805
- isNotNumber,
806
- isNotNumeric,
807
- isNotString,
808
- isNotUndefined,
809
- isNull,
810
- isNumber,
811
- isNumeric,
812
- isOdd,
813
- isPositive,
814
- isString,
815
- isTruthy,
816
- isUndefined,
817
- lengthEquals,
818
- lengthNotEquals,
819
- lessThan,
820
- lessThanOrEquals,
821
- longerThan,
822
- longerThanOrEquals,
823
- lt: lessThan,
824
- lte: lessThanOrEquals,
825
- matches,
826
- notEquals,
827
- notInside,
828
- notMatches,
829
- numberEquals,
830
- numberNotEquals,
831
- shorterThan,
832
- shorterThanOrEquals,
833
- startsWith
834
- };
835
- }
836
-
837
- var runtimeRules = Object.assign(rules(), compounds, ruleMeta);
838
-
839
- /**
840
- * Determines whether a given string is a name of a rule
841
- *
842
- * @param {string} name
843
- * @return {boolean}
844
- */
845
-
846
- const isRule = name => hasOwnProperty(runtimeRules, name) && isFunction(runtimeRules[name]);
847
-
848
- const GLOBAL_OBJECT = Function('return this')();
849
-
850
- const proxySupported = () => isFunction(GLOBAL_OBJECT.Proxy);
851
-
852
- function genRuleProxy(target, output) {
853
- if (proxySupported()) {
854
- return new Proxy(target, {
855
- get: (target, fnName) => {
856
- // A faster bailout when we access `run` and `test`
857
- if (hasOwnProperty(target, fnName)) {
858
- return target[fnName];
859
- }
860
-
861
- if (isRule(fnName)) {
862
- return output(fnName);
863
- }
864
-
865
- return target[fnName];
866
- }
867
- });
868
- } else {
869
- /**
870
- * This method is REALLY not recommended as it is slow and iterates over
871
- * all the rules for each direct enforce reference. We only use it as a
872
- * lightweight alternative for the much faster proxy interface
873
- */
874
- for (const fnName in runtimeRules) {
875
- if (!isFunction(target[fnName])) {
876
- Object.defineProperties(target, {
877
- [fnName]: {
878
- get: () => output(fnName)
879
- }
880
- });
881
- }
882
- }
883
-
884
- return target;
885
- }
886
- }
887
-
888
- /**
889
- * Determines whether a given rule is a compound.
890
- *
891
- * @param {Function} rule
892
- * @return {boolean}
893
- */
894
-
895
- function isCompound(rule) {
896
- return hasOwnProperty(compounds, rule.name);
897
- }
898
-
899
- /**
900
- * Creates a rule of lazily called rules.
901
- * Each rule gets added a `.run()` property
902
- * which runs all the accumulated rules in
903
- * the chain against the supplied value
904
- *
905
- * @param {string} ruleName
906
- * @return {{run: Function}}
907
- */
908
-
909
- function bindLazyRule(ruleName) {
910
- const registeredRules = []; // Chained rules
911
-
912
- const meta = []; // Meta properties to add onto the rule context
913
- // Appends a function to the registeredRules array.
914
- // It gets called every time the consumer usess chaining
915
- // so, for example - enforce.isArray() <- this calles addFn
916
-
917
- const addFn = ruleName => {
918
- return withArgs(args => {
919
- const rule = runtimeRules[ruleName]; // Add a meta function
920
-
921
- if (ruleMeta[rule.name] === rule) {
922
- meta.push((value, ruleResult) => {
923
- ruleResult.setAttribute(rule.name, rule(value, args[0]));
924
- });
925
- } else {
926
- // Register a rule
927
- registeredRules.push(setFnName(value => {
928
- return rule.apply(null, [// If the rule is compound - wraps the value with context
929
- // Otherwise - unwraps it
930
- isCompound(rule) ? EnforceContext.wrap(value) : EnforceContext.unwrap(value)].concat(args));
931
- }, ruleName));
932
- } // set addFn as the proxy handler
933
-
934
-
935
- const returnvalue = genRuleProxy({}, addFn);
936
-
937
- returnvalue[RUN_RULE] = value => {
938
- const result = new RuleResult(true); // Run meta chains
939
-
940
- meta.forEach(fn => {
941
- fn(value, result);
942
- }); // Iterate over all the registered rules
943
- // This runs the function that's inside `addFn`
944
-
945
- for (const fn of registeredRules) {
946
- try {
947
- result.extend(fn(value)); // If a chained rule fails, exit. We failed.
948
-
949
- if (!result.pass) {
950
- break;
951
- }
952
- } catch (e) {
953
- result.setFailed(true);
954
- break;
955
- }
956
- }
957
-
958
- return result;
959
- };
960
-
961
- returnvalue[TEST_RULE] = value => returnvalue[RUN_RULE](EnforceContext.wrap(value).setFailFast(true)).pass;
962
-
963
- return returnvalue;
964
- }, ruleName);
965
- }; // Returns the initial rule in the chain
966
-
967
-
968
- return addFn(ruleName);
969
- }
970
-
971
- function bindExtend(enforce, Enforce) {
972
- enforce.extend = customRules => {
973
- Object.assign(runtimeRules, customRules);
974
-
975
- if (!proxySupported()) {
976
- genRuleProxy(Enforce, bindLazyRule);
977
- }
978
-
979
- return enforce;
980
- };
981
- }
982
-
983
- /**
984
- * Throws a timed out error.
985
- * @param {String} message Error message to display.
986
- * @param {Error} [type] Alternative Error type.
987
- */
988
- const throwError = (message, type = Error) => {
989
- throw new type(`[${"n4s"}]: ${message}`);
990
- };
991
-
992
- function validateResult(result, rule) {
993
- // if result is boolean, or if result.pass is boolean
994
- if (isBoolean(result) || result && isBoolean(result.pass)) {
995
- return;
996
- }
997
-
998
- throwError( `${rule.name} wrong return value for the rule please check that the return is valid` );
999
- } // for easier testing and mocking
1000
-
1001
- function getDefaultResult(value, rule) {
1002
- return {
1003
- message: formatResultMessage(rule, `invalid ${typeof value} value`)
1004
- };
1005
- }
1006
- function formatResultMessage(rule, msg) {
1007
- return `[${"n4s"}]:${rule.name} ${msg}`;
1008
- }
1009
- /**
1010
- * Transform the result of a rule into a standard format
1011
- * @param {string} interfaceName to be used in the messages
1012
- * @param {*} result of the rule
1013
- * @param {Object} options
1014
- * @param {function} options.rule
1015
- * @param {*} options.value
1016
- * @returns {Object} result
1017
- * @returns {string} result.message
1018
- * @returns {boolean} result.pass indicates if the test passes or not
1019
- */
1020
-
1021
- function transformResult(result, {
1022
- rule,
1023
- value
1024
- }) {
1025
- const defaultResult = getDefaultResult(value, rule);
1026
- validateResult(result, rule); // if result is boolean
1027
-
1028
- if (isBoolean(result)) {
1029
- return defaultResult.pass = result, defaultResult;
1030
- } else {
1031
- defaultResult.pass = result.pass;
1032
-
1033
- if (result.message) {
1034
- defaultResult.message = formatResultMessage(rule, optionalFunctionValue(result.message));
1035
- }
1036
-
1037
- return defaultResult;
1038
- }
1039
- }
1040
-
1041
- /**
1042
- * Run a single rule against enforced value (e.g. `isNumber()`)
1043
- *
1044
- * @param {Function} rule - rule to run
1045
- * @param {Any} value
1046
- * @param {Array} args list of arguments sent from consumer
1047
- * @throws
1048
- */
1049
-
1050
- function runner(rule, value, args = []) {
1051
- let result;
1052
- const isCompoundRule = isCompound(rule);
1053
- const ruleValue = isCompoundRule ? EnforceContext.wrap(value).setFailFast(true) : EnforceContext.unwrap(value);
1054
- result = rule.apply(null, [ruleValue].concat(args));
1055
-
1056
- if (!isCompoundRule) {
1057
- result = transformResult(result, {
1058
- rule,
1059
- value
1060
- });
1061
- }
1062
-
1063
- if (!result.pass) {
1064
- throw new Error(result.message);
1065
- }
1066
- }
1067
-
1068
- /**
1069
- * Adds `template` property to enforce.
1070
- *
1071
- * @param {Function} enforce
1072
- */
1073
-
1074
- function bindTemplate(enforce) {
1075
- enforce.template = withArgs(rules => {
1076
- const template = value => {
1077
- runner(runLazyRules.bind(null, rules), value);
1078
- const proxy = genRuleProxy({}, ruleName => withArgs(args => {
1079
- runner(runtimeRules[ruleName], value, args);
1080
- return proxy;
1081
- }));
1082
- return proxy;
1083
- }; // `run` returns a deep ResultObject
1084
-
1085
-
1086
- template[RUN_RULE] = value => runLazyRules(rules, value); // `test` returns a boolean
1087
-
1088
-
1089
- template[TEST_RULE] = value => runLazyRules(rules, EnforceContext.wrap(value).setFailFast(true)).pass;
1090
-
1091
- return template;
1092
- });
1093
- }
1094
-
1095
- const Enforce = value => {
1096
- const proxy = genRuleProxy({}, ruleName => withArgs(args => {
1097
- runner(runtimeRules[ruleName], value, args);
1098
- return proxy;
1099
- }));
1100
- return proxy;
1101
- };
1102
-
1103
- const enforce = genRuleProxy(Enforce, bindLazyRule);
1104
- bindExtend(enforce, Enforce);
1105
- bindTemplate(enforce);
1106
-
1107
- return enforce;
1108
-
1109
- })));