├── .gitignore
├── LICENSE.md
├── assertions
├── assertAccepted.js
├── assertDate.js
├── assertEmpty.js
├── assertRequired.js
├── assertUniqueArray.js
└── index.js
├── index.js
├── lib
├── attributeToWildcard.js
├── bytesToMb.js
├── capitalize.js
├── getMessage.js
├── index.js
├── initializeAndGatherData.js
├── intus.js
├── objectSet.js
├── objectToDot.js
├── parseData.js
├── parseRules.js
├── snakeToWords.js
├── validation.js
└── wildcardToAttribute.js
├── package-lock.json
├── package.json
├── prettier.config.js
├── readme.md
├── rules
├── index.js
├── isAccepted.js
├── isAcceptedIf.js
├── isAfter.js
├── isAfterOrEqual.js
├── isArray.js
├── isBefore.js
├── isBeforeOrEqual.js
├── isBetween.js
├── isBoolean.js
├── isDate.js
├── isDistinct.js
├── isEmail.js
├── isExtension.js
├── isGt.js
├── isGte.js
├── isImage.js
├── isIn.js
├── isInteger.js
├── isIp.js
├── isJSON.js
├── isLt.js
├── isLte.js
├── isMax.js
├── isMime.js
├── isMin.js
├── isNotIn.js
├── isNotRegex.js
├── isNumeric.js
├── isRegex.js
├── isRequired.js
├── isRequiredIf.js
├── isRequiredIfAccepted.js
├── isRequiredUnless.js
├── isRequiredWith.js
├── isRequiredWithAll.js
├── isRequiredWithout.js
├── isRequiredWithoutAll.js
├── isSame.js
└── isUrl.js
├── test
├── helpers
│ ├── file.js
│ └── index.js
├── isAccepted.test.js
├── isAcceptedIf.test.js
├── isAfter.test.js
├── isAfterOrEqual.test.js
├── isArray.test.js
├── isBefore.test.js
├── isBeforeOrEqual.test.js
├── isBetween.test.js
├── isBoolean.test.js
├── isDate.test.js
├── isDistinct.test.js
├── isEmail.test.js
├── isExtension.test.js
├── isGt.test.js
├── isGte.test.js
├── isImage.test.js
├── isIn.test.js
├── isInteger.test.js
├── isIp.test.js
├── isJSON.test.js
├── isLt.test.js
├── isLte.test.js
├── isMax.test.js
├── isMime.test.js
├── isMin.test.js
├── isNotIn.test.js
├── isNotRegex.test.js
├── isNumeric.test.js
├── isRegex.test.js
├── isRequired.test.js
├── isRequiredIf.test.js
├── isRequiredIfAccepted.test.js
├── isRequiredUnless.test.js
├── isRequiredWith.test.js
├── isRequiredWithAll.test.js
├── isRequiredWithout.test.js
├── isRequiredWithoutAll.test.js
├── isSame.test.js
├── isUrl.test.js
└── validation.test.js
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © Constantin Druc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/assertions/assertAccepted.js:
--------------------------------------------------------------------------------
1 | export default function assertAccepted(value) {
2 | return [true, "on", 1, "yes"].includes(value);
3 | }
4 |
--------------------------------------------------------------------------------
/assertions/assertDate.js:
--------------------------------------------------------------------------------
1 | export default function assertDate(value) {
2 | return value && Object.prototype.toString.call(value) === "[object Date]" && !isNaN(value);
3 | }
4 |
--------------------------------------------------------------------------------
/assertions/assertEmpty.js:
--------------------------------------------------------------------------------
1 | export default function assertEmpty(value) {
2 | if (value === null || value === undefined || value === "") {
3 | return true;
4 | }
5 |
6 | return Array.isArray(value) && value.length === 0;
7 | }
8 |
--------------------------------------------------------------------------------
/assertions/assertRequired.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "./index";
2 |
3 | export default function assertRequired(value) {
4 | return !assertEmpty(value);
5 | }
6 |
--------------------------------------------------------------------------------
/assertions/assertUniqueArray.js:
--------------------------------------------------------------------------------
1 | export default function assertUniqueArray(arr) {
2 | let map = {};
3 | let i;
4 | let size;
5 | for (i = 0, size = arr.length; i < size; i++) {
6 | if (map[arr[i]]) {
7 | return false;
8 | }
9 | map[arr[i]] = true;
10 | }
11 | return true;
12 | }
13 |
--------------------------------------------------------------------------------
/assertions/index.js:
--------------------------------------------------------------------------------
1 | export {default as assertEmpty} from "./assertEmpty";
2 | export {default as assertDate} from "./assertDate";
3 | export {default as assertAccepted} from "./assertAccepted";
4 | export {default as assertUniqueArray} from "./assertUniqueArray";
5 | export {default as assertRequired} from "./assertRequired";
6 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import Intus from "./lib/intus.js";
2 |
3 | const intus = new Intus();
4 |
5 | intus.messages = {};
6 |
7 | export default intus;
8 |
--------------------------------------------------------------------------------
/lib/attributeToWildcard.js:
--------------------------------------------------------------------------------
1 | export default function attributeToWildcard(attribute) {
2 | return attribute.replaceAll(/\d+/g, "*");
3 | }
4 |
--------------------------------------------------------------------------------
/lib/bytesToMb.js:
--------------------------------------------------------------------------------
1 | export default function bytesToMb(bytes) {
2 | return bytes / 1024 ** 2;
3 | }
4 |
--------------------------------------------------------------------------------
/lib/capitalize.js:
--------------------------------------------------------------------------------
1 | export default function capitalize(string) {
2 | return string.charAt(0).toUpperCase() + string.slice(1);
3 | }
4 |
--------------------------------------------------------------------------------
/lib/getMessage.js:
--------------------------------------------------------------------------------
1 | export default function getMessage(key, messages, defaultMessage = null) {
2 | if (messages[key]) return messages[key];
3 |
4 | for (let msg in messages) {
5 | if (msg.includes("*")) {
6 | let pattern = new RegExp(msg.replaceAll("*", "[^.]*") + "$");
7 | if (pattern.test(key)) {
8 | return messages[msg];
9 | }
10 | }
11 | }
12 |
13 | return defaultMessage ? defaultMessage : key;
14 | }
15 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | export {default as arraySet} from "./objectSet";
2 | export {default as arrayToDot} from "./objectToDot";
3 | export {default as getMessage} from "./getMessage";
4 | export {default as snakeToWords} from "./snakeToWords";
5 | export {default as intus} from "./intus";
6 | export {default as bytesToMb} from "./bytesToMb";
7 | export {default as parseRules} from "./parseRules";
8 | export {default as parseData} from "./parseData";
9 | export {default as capitalize} from "./capitalize";
10 | export {default as attributeToWildcard} from "./attributeToWildcard";
11 | export {default as replaceWildcard} from "./wildcardToAttribute";
12 | export {default as validation} from "./validation";
13 | export {default as initializeAndGatherData} from "./initializeAndGatherData";
14 |
--------------------------------------------------------------------------------
/lib/initializeAndGatherData.js:
--------------------------------------------------------------------------------
1 | import {arraySet, arrayToDot} from "./index";
2 |
3 | function getFromObject(object, path) {
4 | let keys = path.split(".").reverse();
5 |
6 | while (
7 | keys.length &&
8 | (object = object[keys.pop()]) !== undefined &&
9 | object !== null
10 | );
11 |
12 | return object;
13 | }
14 |
15 | function extractValuesForWildcards(masterData, data, attribute) {
16 | let result = {...masterData};
17 |
18 | let pattern = new RegExp(attribute.replaceAll("*", "[^.]*") + "$");
19 |
20 | for (let attr in data) {
21 | if (pattern.test(attr)) {
22 | result[attr] = getFromObject(masterData, attr);
23 | }
24 | }
25 |
26 | return result;
27 | }
28 |
29 | function initializeAttributeOnData(attribute, masterData) {
30 | return arraySet({...masterData}, attribute, null, true);
31 | }
32 |
33 | export default function initializeAndGatherData(attribute, data) {
34 | let result = arrayToDot(initializeAttributeOnData(attribute, data));
35 | return extractValuesForWildcards(data, result, attribute);
36 | }
37 |
--------------------------------------------------------------------------------
/lib/intus.js:
--------------------------------------------------------------------------------
1 | import Validation from "./validation";
2 | import {arrayToDot} from "./index";
3 |
4 | export default class Intus {
5 | constructor(messages = {}) {
6 | this.messages = messages;
7 | }
8 |
9 | validate(data, rules, messages = {}) {
10 | return new Validation(
11 | data,
12 | rules,
13 | arrayToDot({
14 | ...this.messages,
15 | ...messages,
16 | })
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/objectSet.js:
--------------------------------------------------------------------------------
1 | export default function objectSet(target, key, value, overwrite) {
2 | let result = Array.isArray(target) ? [...target] : {...target};
3 | let segments = [...(Array.isArray(key) ? key : key.split("."))];
4 | let segment = segments.shift();
5 |
6 | if (segment === "*") {
7 | if (segments.length > 0) {
8 | for (let inner in result) {
9 | result[inner] = objectSet(result[inner], segments, value, overwrite);
10 | }
11 | } else if (overwrite) {
12 | for (let inner in result) {
13 | result[inner] = value;
14 | }
15 | }
16 | } else {
17 | if (segments.length > 0) {
18 | result[segment] = objectSet(result[segment], segments, value, overwrite);
19 | } else {
20 | if (
21 | overwrite ||
22 | result[segment] === null ||
23 | typeof result[segment] === "undefined"
24 | ) {
25 | result[segment] = value;
26 | }
27 | }
28 | }
29 |
30 | return result;
31 | }
32 |
--------------------------------------------------------------------------------
/lib/objectToDot.js:
--------------------------------------------------------------------------------
1 | export default function objectToDot(data, prepend = "") {
2 | let result = {};
3 |
4 | for (let key in data) {
5 | if (data[key] !== null && typeof data[key] === "object") {
6 | result = {
7 | ...result,
8 | ...objectToDot(data[key], prepend + key + "."),
9 | };
10 | } else {
11 | result[prepend + key] = data[key];
12 | }
13 | }
14 |
15 | return result;
16 | }
17 |
--------------------------------------------------------------------------------
/lib/parseData.js:
--------------------------------------------------------------------------------
1 | import {initializeAndGatherData} from "./index";
2 |
3 | export default function parseData(data, rules) {
4 | let result = {};
5 |
6 | for (let attribute in rules) {
7 | result = {
8 | ...result,
9 | ...initializeAndGatherData(attribute, data),
10 | };
11 | }
12 |
13 | return result;
14 | }
15 |
--------------------------------------------------------------------------------
/lib/parseRules.js:
--------------------------------------------------------------------------------
1 | export default function parseRules(parsedData, rules) {
2 | rules = {...rules};
3 |
4 | for (let attribute in rules) {
5 | if (attribute.includes("*")) {
6 | rules = {
7 | ...rules,
8 | ...parsedWildcardRules(parsedData, attribute, rules[attribute]),
9 | };
10 |
11 | delete rules[attribute];
12 | }
13 | }
14 |
15 | return rules;
16 | }
17 |
18 | function parsedWildcardRules(parsedData, attribute, rules) {
19 | let parsedRules = {};
20 | let pattern = new RegExp(attribute.replaceAll("*", "[^.]*") + "$");
21 |
22 | for (let attr in parsedData) {
23 | if (pattern.test(attr)) {
24 | rules.forEach(rule => {
25 | parsedRules[attr] = parsedRules[attr] || [];
26 | parsedRules[attr].push(rule);
27 | });
28 | }
29 | }
30 |
31 | return parsedRules;
32 | }
33 |
--------------------------------------------------------------------------------
/lib/snakeToWords.js:
--------------------------------------------------------------------------------
1 | export default function snakeToWords(str) {
2 | return str.replace(/_/g, " ");
3 | }
4 |
--------------------------------------------------------------------------------
/lib/validation.js:
--------------------------------------------------------------------------------
1 | import {
2 | attributeToWildcard,
3 | capitalize,
4 | parseData,
5 | parseRules,
6 | snakeToWords,
7 | } from "./index";
8 |
9 | export default class Validation {
10 | /**
11 | * @type {*}
12 | * @private
13 | */
14 | _errors = undefined;
15 |
16 | constructor(masterData, masterRules, messages = {}) {
17 | this.messages = messages;
18 | this.data = parseData(masterData, masterRules);
19 | this.rules = parseRules(this.data, masterRules);
20 | }
21 |
22 | passes() {
23 | this._errors = {};
24 |
25 | for (let attribute in this.rules) {
26 | for (let rule in this.rules[attribute]) {
27 | let validation = this.rules[attribute][rule]({
28 | value: this.data[attribute],
29 | data: this.data,
30 | attribute,
31 | messages: this.messages,
32 | });
33 |
34 | if (!validation.passes()) {
35 | let message = this.overwrittenMessage(attribute, rule);
36 | this._errors[attribute] = capitalize(
37 | snakeToWords(validation.message(message))
38 | );
39 | break;
40 | }
41 | }
42 | }
43 |
44 | return Object.keys(this._errors).length === 0;
45 | }
46 |
47 | /**
48 | * @private
49 | */
50 | overwrittenMessage(attribute, rule) {
51 | let message =
52 | this.messages[
53 | `${attributeToWildcard(attribute)}.${this.rules[attribute][rule].name}`
54 | ];
55 |
56 | if (!message) {
57 | message = this.messages[this.rules[attribute][rule].name];
58 | }
59 |
60 | return message;
61 | }
62 |
63 | errors() {
64 | if (typeof this._errors === "undefined") {
65 | this.passes();
66 | }
67 |
68 | return this._errors;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/wildcardToAttribute.js:
--------------------------------------------------------------------------------
1 | export default function wildcardToAttribute(wildcardKey, modelKey) {
2 | let segments = modelKey.split(".");
3 |
4 | return wildcardKey
5 | .split(".")
6 | .reduce((c, segment, index) => {
7 | return [...c, segment === "*" ? segments[index] : segment];
8 | }, [])
9 | .join(".");
10 | }
11 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "intus",
3 | "version": "0.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "intus",
9 | "version": "0.0.0",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "prettier": "2.7",
13 | "vite": "^3.1.0",
14 | "vitest": "^0.23.4"
15 | }
16 | },
17 | "node_modules/@esbuild/android-arm": {
18 | "version": "0.15.10",
19 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.10.tgz",
20 | "integrity": "sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==",
21 | "cpu": [
22 | "arm"
23 | ],
24 | "dev": true,
25 | "optional": true,
26 | "os": [
27 | "android"
28 | ],
29 | "engines": {
30 | "node": ">=12"
31 | }
32 | },
33 | "node_modules/@esbuild/linux-loong64": {
34 | "version": "0.15.10",
35 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz",
36 | "integrity": "sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==",
37 | "cpu": [
38 | "loong64"
39 | ],
40 | "dev": true,
41 | "optional": true,
42 | "os": [
43 | "linux"
44 | ],
45 | "engines": {
46 | "node": ">=12"
47 | }
48 | },
49 | "node_modules/@types/chai": {
50 | "version": "4.3.3",
51 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz",
52 | "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==",
53 | "dev": true
54 | },
55 | "node_modules/@types/chai-subset": {
56 | "version": "1.3.3",
57 | "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
58 | "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
59 | "dev": true,
60 | "dependencies": {
61 | "@types/chai": "*"
62 | }
63 | },
64 | "node_modules/@types/node": {
65 | "version": "18.8.2",
66 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.2.tgz",
67 | "integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==",
68 | "dev": true
69 | },
70 | "node_modules/acorn": {
71 | "version": "8.8.0",
72 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
73 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
74 | "dev": true,
75 | "bin": {
76 | "acorn": "bin/acorn"
77 | },
78 | "engines": {
79 | "node": ">=0.4.0"
80 | }
81 | },
82 | "node_modules/assertion-error": {
83 | "version": "1.1.0",
84 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
85 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
86 | "dev": true,
87 | "engines": {
88 | "node": "*"
89 | }
90 | },
91 | "node_modules/chai": {
92 | "version": "4.3.6",
93 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
94 | "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
95 | "dev": true,
96 | "dependencies": {
97 | "assertion-error": "^1.1.0",
98 | "check-error": "^1.0.2",
99 | "deep-eql": "^3.0.1",
100 | "get-func-name": "^2.0.0",
101 | "loupe": "^2.3.1",
102 | "pathval": "^1.1.1",
103 | "type-detect": "^4.0.5"
104 | },
105 | "engines": {
106 | "node": ">=4"
107 | }
108 | },
109 | "node_modules/check-error": {
110 | "version": "1.0.2",
111 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
112 | "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
113 | "dev": true,
114 | "engines": {
115 | "node": "*"
116 | }
117 | },
118 | "node_modules/debug": {
119 | "version": "4.3.4",
120 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
121 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
122 | "dev": true,
123 | "dependencies": {
124 | "ms": "2.1.2"
125 | },
126 | "engines": {
127 | "node": ">=6.0"
128 | },
129 | "peerDependenciesMeta": {
130 | "supports-color": {
131 | "optional": true
132 | }
133 | }
134 | },
135 | "node_modules/deep-eql": {
136 | "version": "3.0.1",
137 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
138 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
139 | "dev": true,
140 | "dependencies": {
141 | "type-detect": "^4.0.0"
142 | },
143 | "engines": {
144 | "node": ">=0.12"
145 | }
146 | },
147 | "node_modules/esbuild": {
148 | "version": "0.15.10",
149 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.10.tgz",
150 | "integrity": "sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==",
151 | "dev": true,
152 | "hasInstallScript": true,
153 | "bin": {
154 | "esbuild": "bin/esbuild"
155 | },
156 | "engines": {
157 | "node": ">=12"
158 | },
159 | "optionalDependencies": {
160 | "@esbuild/android-arm": "0.15.10",
161 | "@esbuild/linux-loong64": "0.15.10",
162 | "esbuild-android-64": "0.15.10",
163 | "esbuild-android-arm64": "0.15.10",
164 | "esbuild-darwin-64": "0.15.10",
165 | "esbuild-darwin-arm64": "0.15.10",
166 | "esbuild-freebsd-64": "0.15.10",
167 | "esbuild-freebsd-arm64": "0.15.10",
168 | "esbuild-linux-32": "0.15.10",
169 | "esbuild-linux-64": "0.15.10",
170 | "esbuild-linux-arm": "0.15.10",
171 | "esbuild-linux-arm64": "0.15.10",
172 | "esbuild-linux-mips64le": "0.15.10",
173 | "esbuild-linux-ppc64le": "0.15.10",
174 | "esbuild-linux-riscv64": "0.15.10",
175 | "esbuild-linux-s390x": "0.15.10",
176 | "esbuild-netbsd-64": "0.15.10",
177 | "esbuild-openbsd-64": "0.15.10",
178 | "esbuild-sunos-64": "0.15.10",
179 | "esbuild-windows-32": "0.15.10",
180 | "esbuild-windows-64": "0.15.10",
181 | "esbuild-windows-arm64": "0.15.10"
182 | }
183 | },
184 | "node_modules/esbuild-android-64": {
185 | "version": "0.15.10",
186 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz",
187 | "integrity": "sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==",
188 | "cpu": [
189 | "x64"
190 | ],
191 | "dev": true,
192 | "optional": true,
193 | "os": [
194 | "android"
195 | ],
196 | "engines": {
197 | "node": ">=12"
198 | }
199 | },
200 | "node_modules/esbuild-android-arm64": {
201 | "version": "0.15.10",
202 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz",
203 | "integrity": "sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==",
204 | "cpu": [
205 | "arm64"
206 | ],
207 | "dev": true,
208 | "optional": true,
209 | "os": [
210 | "android"
211 | ],
212 | "engines": {
213 | "node": ">=12"
214 | }
215 | },
216 | "node_modules/esbuild-darwin-64": {
217 | "version": "0.15.10",
218 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz",
219 | "integrity": "sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==",
220 | "cpu": [
221 | "x64"
222 | ],
223 | "dev": true,
224 | "optional": true,
225 | "os": [
226 | "darwin"
227 | ],
228 | "engines": {
229 | "node": ">=12"
230 | }
231 | },
232 | "node_modules/esbuild-darwin-arm64": {
233 | "version": "0.15.10",
234 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz",
235 | "integrity": "sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==",
236 | "cpu": [
237 | "arm64"
238 | ],
239 | "dev": true,
240 | "optional": true,
241 | "os": [
242 | "darwin"
243 | ],
244 | "engines": {
245 | "node": ">=12"
246 | }
247 | },
248 | "node_modules/esbuild-freebsd-64": {
249 | "version": "0.15.10",
250 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz",
251 | "integrity": "sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==",
252 | "cpu": [
253 | "x64"
254 | ],
255 | "dev": true,
256 | "optional": true,
257 | "os": [
258 | "freebsd"
259 | ],
260 | "engines": {
261 | "node": ">=12"
262 | }
263 | },
264 | "node_modules/esbuild-freebsd-arm64": {
265 | "version": "0.15.10",
266 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz",
267 | "integrity": "sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==",
268 | "cpu": [
269 | "arm64"
270 | ],
271 | "dev": true,
272 | "optional": true,
273 | "os": [
274 | "freebsd"
275 | ],
276 | "engines": {
277 | "node": ">=12"
278 | }
279 | },
280 | "node_modules/esbuild-linux-32": {
281 | "version": "0.15.10",
282 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz",
283 | "integrity": "sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==",
284 | "cpu": [
285 | "ia32"
286 | ],
287 | "dev": true,
288 | "optional": true,
289 | "os": [
290 | "linux"
291 | ],
292 | "engines": {
293 | "node": ">=12"
294 | }
295 | },
296 | "node_modules/esbuild-linux-64": {
297 | "version": "0.15.10",
298 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz",
299 | "integrity": "sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==",
300 | "cpu": [
301 | "x64"
302 | ],
303 | "dev": true,
304 | "optional": true,
305 | "os": [
306 | "linux"
307 | ],
308 | "engines": {
309 | "node": ">=12"
310 | }
311 | },
312 | "node_modules/esbuild-linux-arm": {
313 | "version": "0.15.10",
314 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz",
315 | "integrity": "sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==",
316 | "cpu": [
317 | "arm"
318 | ],
319 | "dev": true,
320 | "optional": true,
321 | "os": [
322 | "linux"
323 | ],
324 | "engines": {
325 | "node": ">=12"
326 | }
327 | },
328 | "node_modules/esbuild-linux-arm64": {
329 | "version": "0.15.10",
330 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz",
331 | "integrity": "sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==",
332 | "cpu": [
333 | "arm64"
334 | ],
335 | "dev": true,
336 | "optional": true,
337 | "os": [
338 | "linux"
339 | ],
340 | "engines": {
341 | "node": ">=12"
342 | }
343 | },
344 | "node_modules/esbuild-linux-mips64le": {
345 | "version": "0.15.10",
346 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz",
347 | "integrity": "sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==",
348 | "cpu": [
349 | "mips64el"
350 | ],
351 | "dev": true,
352 | "optional": true,
353 | "os": [
354 | "linux"
355 | ],
356 | "engines": {
357 | "node": ">=12"
358 | }
359 | },
360 | "node_modules/esbuild-linux-ppc64le": {
361 | "version": "0.15.10",
362 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz",
363 | "integrity": "sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==",
364 | "cpu": [
365 | "ppc64"
366 | ],
367 | "dev": true,
368 | "optional": true,
369 | "os": [
370 | "linux"
371 | ],
372 | "engines": {
373 | "node": ">=12"
374 | }
375 | },
376 | "node_modules/esbuild-linux-riscv64": {
377 | "version": "0.15.10",
378 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz",
379 | "integrity": "sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==",
380 | "cpu": [
381 | "riscv64"
382 | ],
383 | "dev": true,
384 | "optional": true,
385 | "os": [
386 | "linux"
387 | ],
388 | "engines": {
389 | "node": ">=12"
390 | }
391 | },
392 | "node_modules/esbuild-linux-s390x": {
393 | "version": "0.15.10",
394 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz",
395 | "integrity": "sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==",
396 | "cpu": [
397 | "s390x"
398 | ],
399 | "dev": true,
400 | "optional": true,
401 | "os": [
402 | "linux"
403 | ],
404 | "engines": {
405 | "node": ">=12"
406 | }
407 | },
408 | "node_modules/esbuild-netbsd-64": {
409 | "version": "0.15.10",
410 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz",
411 | "integrity": "sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==",
412 | "cpu": [
413 | "x64"
414 | ],
415 | "dev": true,
416 | "optional": true,
417 | "os": [
418 | "netbsd"
419 | ],
420 | "engines": {
421 | "node": ">=12"
422 | }
423 | },
424 | "node_modules/esbuild-openbsd-64": {
425 | "version": "0.15.10",
426 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz",
427 | "integrity": "sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==",
428 | "cpu": [
429 | "x64"
430 | ],
431 | "dev": true,
432 | "optional": true,
433 | "os": [
434 | "openbsd"
435 | ],
436 | "engines": {
437 | "node": ">=12"
438 | }
439 | },
440 | "node_modules/esbuild-sunos-64": {
441 | "version": "0.15.10",
442 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz",
443 | "integrity": "sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==",
444 | "cpu": [
445 | "x64"
446 | ],
447 | "dev": true,
448 | "optional": true,
449 | "os": [
450 | "sunos"
451 | ],
452 | "engines": {
453 | "node": ">=12"
454 | }
455 | },
456 | "node_modules/esbuild-windows-32": {
457 | "version": "0.15.10",
458 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz",
459 | "integrity": "sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==",
460 | "cpu": [
461 | "ia32"
462 | ],
463 | "dev": true,
464 | "optional": true,
465 | "os": [
466 | "win32"
467 | ],
468 | "engines": {
469 | "node": ">=12"
470 | }
471 | },
472 | "node_modules/esbuild-windows-64": {
473 | "version": "0.15.10",
474 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz",
475 | "integrity": "sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==",
476 | "cpu": [
477 | "x64"
478 | ],
479 | "dev": true,
480 | "optional": true,
481 | "os": [
482 | "win32"
483 | ],
484 | "engines": {
485 | "node": ">=12"
486 | }
487 | },
488 | "node_modules/esbuild-windows-arm64": {
489 | "version": "0.15.10",
490 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz",
491 | "integrity": "sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==",
492 | "cpu": [
493 | "arm64"
494 | ],
495 | "dev": true,
496 | "optional": true,
497 | "os": [
498 | "win32"
499 | ],
500 | "engines": {
501 | "node": ">=12"
502 | }
503 | },
504 | "node_modules/fsevents": {
505 | "version": "2.3.2",
506 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
507 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
508 | "dev": true,
509 | "hasInstallScript": true,
510 | "optional": true,
511 | "os": [
512 | "darwin"
513 | ],
514 | "engines": {
515 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
516 | }
517 | },
518 | "node_modules/function-bind": {
519 | "version": "1.1.1",
520 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
521 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
522 | "dev": true
523 | },
524 | "node_modules/get-func-name": {
525 | "version": "2.0.0",
526 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
527 | "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
528 | "dev": true,
529 | "engines": {
530 | "node": "*"
531 | }
532 | },
533 | "node_modules/has": {
534 | "version": "1.0.3",
535 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
536 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
537 | "dev": true,
538 | "dependencies": {
539 | "function-bind": "^1.1.1"
540 | },
541 | "engines": {
542 | "node": ">= 0.4.0"
543 | }
544 | },
545 | "node_modules/is-core-module": {
546 | "version": "2.10.0",
547 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
548 | "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
549 | "dev": true,
550 | "dependencies": {
551 | "has": "^1.0.3"
552 | },
553 | "funding": {
554 | "url": "https://github.com/sponsors/ljharb"
555 | }
556 | },
557 | "node_modules/local-pkg": {
558 | "version": "0.4.2",
559 | "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz",
560 | "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==",
561 | "dev": true,
562 | "engines": {
563 | "node": ">=14"
564 | },
565 | "funding": {
566 | "url": "https://github.com/sponsors/antfu"
567 | }
568 | },
569 | "node_modules/loupe": {
570 | "version": "2.3.4",
571 | "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
572 | "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
573 | "dev": true,
574 | "dependencies": {
575 | "get-func-name": "^2.0.0"
576 | }
577 | },
578 | "node_modules/ms": {
579 | "version": "2.1.2",
580 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
581 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
582 | "dev": true
583 | },
584 | "node_modules/nanoid": {
585 | "version": "3.3.4",
586 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
587 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
588 | "dev": true,
589 | "bin": {
590 | "nanoid": "bin/nanoid.cjs"
591 | },
592 | "engines": {
593 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
594 | }
595 | },
596 | "node_modules/path-parse": {
597 | "version": "1.0.7",
598 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
599 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
600 | "dev": true
601 | },
602 | "node_modules/pathval": {
603 | "version": "1.1.1",
604 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
605 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
606 | "dev": true,
607 | "engines": {
608 | "node": "*"
609 | }
610 | },
611 | "node_modules/picocolors": {
612 | "version": "1.0.0",
613 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
614 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
615 | "dev": true
616 | },
617 | "node_modules/postcss": {
618 | "version": "8.4.17",
619 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
620 | "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
621 | "dev": true,
622 | "funding": [
623 | {
624 | "type": "opencollective",
625 | "url": "https://opencollective.com/postcss/"
626 | },
627 | {
628 | "type": "tidelift",
629 | "url": "https://tidelift.com/funding/github/npm/postcss"
630 | }
631 | ],
632 | "dependencies": {
633 | "nanoid": "^3.3.4",
634 | "picocolors": "^1.0.0",
635 | "source-map-js": "^1.0.2"
636 | },
637 | "engines": {
638 | "node": "^10 || ^12 || >=14"
639 | }
640 | },
641 | "node_modules/prettier": {
642 | "version": "2.7.1",
643 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
644 | "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
645 | "dev": true,
646 | "bin": {
647 | "prettier": "bin-prettier.js"
648 | },
649 | "engines": {
650 | "node": ">=10.13.0"
651 | },
652 | "funding": {
653 | "url": "https://github.com/prettier/prettier?sponsor=1"
654 | }
655 | },
656 | "node_modules/resolve": {
657 | "version": "1.22.1",
658 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
659 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
660 | "dev": true,
661 | "dependencies": {
662 | "is-core-module": "^2.9.0",
663 | "path-parse": "^1.0.7",
664 | "supports-preserve-symlinks-flag": "^1.0.0"
665 | },
666 | "bin": {
667 | "resolve": "bin/resolve"
668 | },
669 | "funding": {
670 | "url": "https://github.com/sponsors/ljharb"
671 | }
672 | },
673 | "node_modules/rollup": {
674 | "version": "2.78.1",
675 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
676 | "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
677 | "dev": true,
678 | "bin": {
679 | "rollup": "dist/bin/rollup"
680 | },
681 | "engines": {
682 | "node": ">=10.0.0"
683 | },
684 | "optionalDependencies": {
685 | "fsevents": "~2.3.2"
686 | }
687 | },
688 | "node_modules/source-map-js": {
689 | "version": "1.0.2",
690 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
691 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
692 | "dev": true,
693 | "engines": {
694 | "node": ">=0.10.0"
695 | }
696 | },
697 | "node_modules/strip-literal": {
698 | "version": "0.4.2",
699 | "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-0.4.2.tgz",
700 | "integrity": "sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==",
701 | "dev": true,
702 | "dependencies": {
703 | "acorn": "^8.8.0"
704 | },
705 | "funding": {
706 | "url": "https://github.com/sponsors/antfu"
707 | }
708 | },
709 | "node_modules/supports-preserve-symlinks-flag": {
710 | "version": "1.0.0",
711 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
712 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
713 | "dev": true,
714 | "engines": {
715 | "node": ">= 0.4"
716 | },
717 | "funding": {
718 | "url": "https://github.com/sponsors/ljharb"
719 | }
720 | },
721 | "node_modules/tinybench": {
722 | "version": "2.3.0",
723 | "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.0.tgz",
724 | "integrity": "sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==",
725 | "dev": true
726 | },
727 | "node_modules/tinypool": {
728 | "version": "0.3.0",
729 | "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.0.tgz",
730 | "integrity": "sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==",
731 | "dev": true,
732 | "engines": {
733 | "node": ">=14.0.0"
734 | }
735 | },
736 | "node_modules/tinyspy": {
737 | "version": "1.0.2",
738 | "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
739 | "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
740 | "dev": true,
741 | "engines": {
742 | "node": ">=14.0.0"
743 | }
744 | },
745 | "node_modules/type-detect": {
746 | "version": "4.0.8",
747 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
748 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
749 | "dev": true,
750 | "engines": {
751 | "node": ">=4"
752 | }
753 | },
754 | "node_modules/vite": {
755 | "version": "3.1.6",
756 | "resolved": "https://registry.npmjs.org/vite/-/vite-3.1.6.tgz",
757 | "integrity": "sha512-qMXIwnehvvcK5XfJiXQUiTxoYAEMKhM+jqCY6ZSTKFBKu1hJnAKEzP3AOcnTerI0cMZYAaJ4wpW1wiXLMDt4mA==",
758 | "dev": true,
759 | "dependencies": {
760 | "esbuild": "^0.15.9",
761 | "postcss": "^8.4.16",
762 | "resolve": "^1.22.1",
763 | "rollup": "~2.78.0"
764 | },
765 | "bin": {
766 | "vite": "bin/vite.js"
767 | },
768 | "engines": {
769 | "node": "^14.18.0 || >=16.0.0"
770 | },
771 | "optionalDependencies": {
772 | "fsevents": "~2.3.2"
773 | },
774 | "peerDependencies": {
775 | "less": "*",
776 | "sass": "*",
777 | "stylus": "*",
778 | "terser": "^5.4.0"
779 | },
780 | "peerDependenciesMeta": {
781 | "less": {
782 | "optional": true
783 | },
784 | "sass": {
785 | "optional": true
786 | },
787 | "stylus": {
788 | "optional": true
789 | },
790 | "terser": {
791 | "optional": true
792 | }
793 | }
794 | },
795 | "node_modules/vitest": {
796 | "version": "0.23.4",
797 | "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.23.4.tgz",
798 | "integrity": "sha512-iukBNWqQAv8EKDBUNntspLp9SfpaVFbmzmM0sNcnTxASQZMzRw3PsM6DMlsHiI+I6GeO5/sYDg3ecpC+SNFLrQ==",
799 | "dev": true,
800 | "dependencies": {
801 | "@types/chai": "^4.3.3",
802 | "@types/chai-subset": "^1.3.3",
803 | "@types/node": "*",
804 | "chai": "^4.3.6",
805 | "debug": "^4.3.4",
806 | "local-pkg": "^0.4.2",
807 | "strip-literal": "^0.4.1",
808 | "tinybench": "^2.1.5",
809 | "tinypool": "^0.3.0",
810 | "tinyspy": "^1.0.2",
811 | "vite": "^2.9.12 || ^3.0.0-0"
812 | },
813 | "bin": {
814 | "vitest": "vitest.mjs"
815 | },
816 | "engines": {
817 | "node": ">=v14.16.0"
818 | },
819 | "funding": {
820 | "url": "https://github.com/sponsors/antfu"
821 | },
822 | "peerDependencies": {
823 | "@edge-runtime/vm": "*",
824 | "@vitest/browser": "*",
825 | "@vitest/ui": "*",
826 | "happy-dom": "*",
827 | "jsdom": "*"
828 | },
829 | "peerDependenciesMeta": {
830 | "@edge-runtime/vm": {
831 | "optional": true
832 | },
833 | "@vitest/browser": {
834 | "optional": true
835 | },
836 | "@vitest/ui": {
837 | "optional": true
838 | },
839 | "happy-dom": {
840 | "optional": true
841 | },
842 | "jsdom": {
843 | "optional": true
844 | }
845 | }
846 | }
847 | },
848 | "dependencies": {
849 | "@esbuild/android-arm": {
850 | "version": "0.15.10",
851 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.10.tgz",
852 | "integrity": "sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==",
853 | "dev": true,
854 | "optional": true
855 | },
856 | "@esbuild/linux-loong64": {
857 | "version": "0.15.10",
858 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz",
859 | "integrity": "sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==",
860 | "dev": true,
861 | "optional": true
862 | },
863 | "@types/chai": {
864 | "version": "4.3.3",
865 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz",
866 | "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==",
867 | "dev": true
868 | },
869 | "@types/chai-subset": {
870 | "version": "1.3.3",
871 | "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
872 | "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
873 | "dev": true,
874 | "requires": {
875 | "@types/chai": "*"
876 | }
877 | },
878 | "@types/node": {
879 | "version": "18.8.2",
880 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.2.tgz",
881 | "integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==",
882 | "dev": true
883 | },
884 | "acorn": {
885 | "version": "8.8.0",
886 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
887 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
888 | "dev": true
889 | },
890 | "assertion-error": {
891 | "version": "1.1.0",
892 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
893 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
894 | "dev": true
895 | },
896 | "chai": {
897 | "version": "4.3.6",
898 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
899 | "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
900 | "dev": true,
901 | "requires": {
902 | "assertion-error": "^1.1.0",
903 | "check-error": "^1.0.2",
904 | "deep-eql": "^3.0.1",
905 | "get-func-name": "^2.0.0",
906 | "loupe": "^2.3.1",
907 | "pathval": "^1.1.1",
908 | "type-detect": "^4.0.5"
909 | }
910 | },
911 | "check-error": {
912 | "version": "1.0.2",
913 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
914 | "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
915 | "dev": true
916 | },
917 | "debug": {
918 | "version": "4.3.4",
919 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
920 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
921 | "dev": true,
922 | "requires": {
923 | "ms": "2.1.2"
924 | }
925 | },
926 | "deep-eql": {
927 | "version": "3.0.1",
928 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
929 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
930 | "dev": true,
931 | "requires": {
932 | "type-detect": "^4.0.0"
933 | }
934 | },
935 | "esbuild": {
936 | "version": "0.15.10",
937 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.10.tgz",
938 | "integrity": "sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==",
939 | "dev": true,
940 | "requires": {
941 | "@esbuild/android-arm": "0.15.10",
942 | "@esbuild/linux-loong64": "0.15.10",
943 | "esbuild-android-64": "0.15.10",
944 | "esbuild-android-arm64": "0.15.10",
945 | "esbuild-darwin-64": "0.15.10",
946 | "esbuild-darwin-arm64": "0.15.10",
947 | "esbuild-freebsd-64": "0.15.10",
948 | "esbuild-freebsd-arm64": "0.15.10",
949 | "esbuild-linux-32": "0.15.10",
950 | "esbuild-linux-64": "0.15.10",
951 | "esbuild-linux-arm": "0.15.10",
952 | "esbuild-linux-arm64": "0.15.10",
953 | "esbuild-linux-mips64le": "0.15.10",
954 | "esbuild-linux-ppc64le": "0.15.10",
955 | "esbuild-linux-riscv64": "0.15.10",
956 | "esbuild-linux-s390x": "0.15.10",
957 | "esbuild-netbsd-64": "0.15.10",
958 | "esbuild-openbsd-64": "0.15.10",
959 | "esbuild-sunos-64": "0.15.10",
960 | "esbuild-windows-32": "0.15.10",
961 | "esbuild-windows-64": "0.15.10",
962 | "esbuild-windows-arm64": "0.15.10"
963 | }
964 | },
965 | "esbuild-android-64": {
966 | "version": "0.15.10",
967 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz",
968 | "integrity": "sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==",
969 | "dev": true,
970 | "optional": true
971 | },
972 | "esbuild-android-arm64": {
973 | "version": "0.15.10",
974 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz",
975 | "integrity": "sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==",
976 | "dev": true,
977 | "optional": true
978 | },
979 | "esbuild-darwin-64": {
980 | "version": "0.15.10",
981 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz",
982 | "integrity": "sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==",
983 | "dev": true,
984 | "optional": true
985 | },
986 | "esbuild-darwin-arm64": {
987 | "version": "0.15.10",
988 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz",
989 | "integrity": "sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==",
990 | "dev": true,
991 | "optional": true
992 | },
993 | "esbuild-freebsd-64": {
994 | "version": "0.15.10",
995 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz",
996 | "integrity": "sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==",
997 | "dev": true,
998 | "optional": true
999 | },
1000 | "esbuild-freebsd-arm64": {
1001 | "version": "0.15.10",
1002 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz",
1003 | "integrity": "sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==",
1004 | "dev": true,
1005 | "optional": true
1006 | },
1007 | "esbuild-linux-32": {
1008 | "version": "0.15.10",
1009 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz",
1010 | "integrity": "sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==",
1011 | "dev": true,
1012 | "optional": true
1013 | },
1014 | "esbuild-linux-64": {
1015 | "version": "0.15.10",
1016 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz",
1017 | "integrity": "sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==",
1018 | "dev": true,
1019 | "optional": true
1020 | },
1021 | "esbuild-linux-arm": {
1022 | "version": "0.15.10",
1023 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz",
1024 | "integrity": "sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==",
1025 | "dev": true,
1026 | "optional": true
1027 | },
1028 | "esbuild-linux-arm64": {
1029 | "version": "0.15.10",
1030 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz",
1031 | "integrity": "sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==",
1032 | "dev": true,
1033 | "optional": true
1034 | },
1035 | "esbuild-linux-mips64le": {
1036 | "version": "0.15.10",
1037 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz",
1038 | "integrity": "sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==",
1039 | "dev": true,
1040 | "optional": true
1041 | },
1042 | "esbuild-linux-ppc64le": {
1043 | "version": "0.15.10",
1044 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz",
1045 | "integrity": "sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==",
1046 | "dev": true,
1047 | "optional": true
1048 | },
1049 | "esbuild-linux-riscv64": {
1050 | "version": "0.15.10",
1051 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz",
1052 | "integrity": "sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==",
1053 | "dev": true,
1054 | "optional": true
1055 | },
1056 | "esbuild-linux-s390x": {
1057 | "version": "0.15.10",
1058 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz",
1059 | "integrity": "sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==",
1060 | "dev": true,
1061 | "optional": true
1062 | },
1063 | "esbuild-netbsd-64": {
1064 | "version": "0.15.10",
1065 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz",
1066 | "integrity": "sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==",
1067 | "dev": true,
1068 | "optional": true
1069 | },
1070 | "esbuild-openbsd-64": {
1071 | "version": "0.15.10",
1072 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz",
1073 | "integrity": "sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==",
1074 | "dev": true,
1075 | "optional": true
1076 | },
1077 | "esbuild-sunos-64": {
1078 | "version": "0.15.10",
1079 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz",
1080 | "integrity": "sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==",
1081 | "dev": true,
1082 | "optional": true
1083 | },
1084 | "esbuild-windows-32": {
1085 | "version": "0.15.10",
1086 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz",
1087 | "integrity": "sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==",
1088 | "dev": true,
1089 | "optional": true
1090 | },
1091 | "esbuild-windows-64": {
1092 | "version": "0.15.10",
1093 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz",
1094 | "integrity": "sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==",
1095 | "dev": true,
1096 | "optional": true
1097 | },
1098 | "esbuild-windows-arm64": {
1099 | "version": "0.15.10",
1100 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz",
1101 | "integrity": "sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==",
1102 | "dev": true,
1103 | "optional": true
1104 | },
1105 | "fsevents": {
1106 | "version": "2.3.2",
1107 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
1108 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
1109 | "dev": true,
1110 | "optional": true
1111 | },
1112 | "function-bind": {
1113 | "version": "1.1.1",
1114 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
1115 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
1116 | "dev": true
1117 | },
1118 | "get-func-name": {
1119 | "version": "2.0.0",
1120 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
1121 | "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
1122 | "dev": true
1123 | },
1124 | "has": {
1125 | "version": "1.0.3",
1126 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
1127 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
1128 | "dev": true,
1129 | "requires": {
1130 | "function-bind": "^1.1.1"
1131 | }
1132 | },
1133 | "is-core-module": {
1134 | "version": "2.10.0",
1135 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
1136 | "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
1137 | "dev": true,
1138 | "requires": {
1139 | "has": "^1.0.3"
1140 | }
1141 | },
1142 | "local-pkg": {
1143 | "version": "0.4.2",
1144 | "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz",
1145 | "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==",
1146 | "dev": true
1147 | },
1148 | "loupe": {
1149 | "version": "2.3.4",
1150 | "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
1151 | "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
1152 | "dev": true,
1153 | "requires": {
1154 | "get-func-name": "^2.0.0"
1155 | }
1156 | },
1157 | "ms": {
1158 | "version": "2.1.2",
1159 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1160 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
1161 | "dev": true
1162 | },
1163 | "nanoid": {
1164 | "version": "3.3.4",
1165 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
1166 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
1167 | "dev": true
1168 | },
1169 | "path-parse": {
1170 | "version": "1.0.7",
1171 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1172 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1173 | "dev": true
1174 | },
1175 | "pathval": {
1176 | "version": "1.1.1",
1177 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
1178 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
1179 | "dev": true
1180 | },
1181 | "picocolors": {
1182 | "version": "1.0.0",
1183 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
1184 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
1185 | "dev": true
1186 | },
1187 | "postcss": {
1188 | "version": "8.4.17",
1189 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
1190 | "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
1191 | "dev": true,
1192 | "requires": {
1193 | "nanoid": "^3.3.4",
1194 | "picocolors": "^1.0.0",
1195 | "source-map-js": "^1.0.2"
1196 | }
1197 | },
1198 | "prettier": {
1199 | "version": "2.7.1",
1200 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
1201 | "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
1202 | "dev": true
1203 | },
1204 | "resolve": {
1205 | "version": "1.22.1",
1206 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
1207 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
1208 | "dev": true,
1209 | "requires": {
1210 | "is-core-module": "^2.9.0",
1211 | "path-parse": "^1.0.7",
1212 | "supports-preserve-symlinks-flag": "^1.0.0"
1213 | }
1214 | },
1215 | "rollup": {
1216 | "version": "2.78.1",
1217 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
1218 | "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
1219 | "dev": true,
1220 | "requires": {
1221 | "fsevents": "~2.3.2"
1222 | }
1223 | },
1224 | "source-map-js": {
1225 | "version": "1.0.2",
1226 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1227 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
1228 | "dev": true
1229 | },
1230 | "strip-literal": {
1231 | "version": "0.4.2",
1232 | "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-0.4.2.tgz",
1233 | "integrity": "sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==",
1234 | "dev": true,
1235 | "requires": {
1236 | "acorn": "^8.8.0"
1237 | }
1238 | },
1239 | "supports-preserve-symlinks-flag": {
1240 | "version": "1.0.0",
1241 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
1242 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
1243 | "dev": true
1244 | },
1245 | "tinybench": {
1246 | "version": "2.3.0",
1247 | "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.0.tgz",
1248 | "integrity": "sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==",
1249 | "dev": true
1250 | },
1251 | "tinypool": {
1252 | "version": "0.3.0",
1253 | "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.0.tgz",
1254 | "integrity": "sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==",
1255 | "dev": true
1256 | },
1257 | "tinyspy": {
1258 | "version": "1.0.2",
1259 | "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
1260 | "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
1261 | "dev": true
1262 | },
1263 | "type-detect": {
1264 | "version": "4.0.8",
1265 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
1266 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
1267 | "dev": true
1268 | },
1269 | "vite": {
1270 | "version": "3.1.6",
1271 | "resolved": "https://registry.npmjs.org/vite/-/vite-3.1.6.tgz",
1272 | "integrity": "sha512-qMXIwnehvvcK5XfJiXQUiTxoYAEMKhM+jqCY6ZSTKFBKu1hJnAKEzP3AOcnTerI0cMZYAaJ4wpW1wiXLMDt4mA==",
1273 | "dev": true,
1274 | "requires": {
1275 | "esbuild": "^0.15.9",
1276 | "fsevents": "~2.3.2",
1277 | "postcss": "^8.4.16",
1278 | "resolve": "^1.22.1",
1279 | "rollup": "~2.78.0"
1280 | }
1281 | },
1282 | "vitest": {
1283 | "version": "0.23.4",
1284 | "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.23.4.tgz",
1285 | "integrity": "sha512-iukBNWqQAv8EKDBUNntspLp9SfpaVFbmzmM0sNcnTxASQZMzRw3PsM6DMlsHiI+I6GeO5/sYDg3ecpC+SNFLrQ==",
1286 | "dev": true,
1287 | "requires": {
1288 | "@types/chai": "^4.3.3",
1289 | "@types/chai-subset": "^1.3.3",
1290 | "@types/node": "*",
1291 | "chai": "^4.3.6",
1292 | "debug": "^4.3.4",
1293 | "local-pkg": "^0.4.2",
1294 | "strip-literal": "^0.4.1",
1295 | "tinybench": "^2.1.5",
1296 | "tinypool": "^0.3.0",
1297 | "tinyspy": "^1.0.2",
1298 | "vite": "^2.9.12 || ^3.0.0-0"
1299 | }
1300 | }
1301 | }
1302 | }
1303 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "intus",
3 | "version": "0.0.2",
4 | "description": "Intus is a framework agnostic client-side validation library.",
5 | "main": "index.js",
6 | "files": [
7 | "assertions",
8 | "lib",
9 | "rules",
10 | "index.js",
11 | "readme.md",
12 | "LICENSE.md"
13 | ],
14 | "scripts": {
15 | "test": "vitest",
16 | "coverage": "vitest run --coverage"
17 | },
18 | "devDependencies": {
19 | "prettier": "2.7",
20 | "vite": "^3.1.0",
21 | "vitest": "^0.23.4"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/druc/intus.git"
26 | },
27 | "keywords": [
28 | "validation"
29 | ],
30 | "author": "Constantin Druc",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/druc/intus/issues"
34 | },
35 | "homepage": "https://github.com/druc/intus#readme"
36 | }
37 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: "avoid",
3 | semi: true,
4 | tabWidth: 2,
5 | vueIndentScriptAndStyle: false,
6 | bracketSpacing: false,
7 | bracketSameLine: false,
8 | endOfLine: "lf",
9 | singleAttributePerLine: true,
10 | };
11 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # intus
2 |
3 | > **Note:** This project is at the very early stages of development and IS NOT yet intended for production
4 | > applications.
5 |
6 | **Intus** is a framework agnostic client-side validation library.
7 |
8 | Apart from its ease of use and having a gazillion [rules](#rules) to pick from, what makes intus great is having rules based on
9 | other fields, for example `requiredIf('otherField', 10)`, and the ability to validate nested fields and array items using
10 | wildcards: `{"lines.*.qty": [isMin(1)]}`.
11 |
12 | ## Installation
13 | ```bash
14 | npm install intus
15 | ```
16 |
17 | ## Usage
18 | Import, provide data, rules, and messages (optional). Profit!
19 |
20 | ```js
21 | import intus from "intus";
22 |
23 | const validation = intus.validate(
24 | // data
25 | {
26 | name: "",
27 | email: "druc@pinsmile.com",
28 | password: "secret",
29 | password_confirmation: "not-secret",
30 | avatar: File,
31 | roles: ['moderator', 'supervisor']
32 | },
33 | // rules
34 | {
35 | name: [isRequired()],
36 | email: [isRequired(), isEmail()],
37 | password: [isRequired(), isMin(8)],
38 | password_confirmation: [isRequired(), isSame('password')],
39 | avatar: [isImage()],
40 | "roles.*": [isIn("admin", "moderator", "supervisor")]
41 | },
42 | // overwrite error messages and field names
43 | {
44 | "name.isRequired": "Name is super required.",
45 | "password_confirmation": "password confirmation"
46 | }
47 | );
48 |
49 | validation.passes(); // true/false
50 | validation.errors(); // object with fieldName: firstErrorMessage
51 | // {
52 | // "name": "Name is super required.",
53 | // "password_confirmation": "The password confirmation must be the same as password.",
54 | // "avatar": "The avatar must be an image."
55 | // }
56 | ```
57 |
58 | ## Available validation rules
59 | *Most* rules are optional by default. If the field is empty (null/undefined/empty string), the validation will pass; so make sure you add in `isRequired()`.
60 |
61 | | Rules | | |
62 | |-------------------------------------|-------------------------|-----------------------------------------------|
63 | | [isAccepted](#isAccepted) | [isGt](#isGt) | [isNotRegex](#isNotRegex) |
64 | | [isAcceptedIf](#isAcceptedIf) | [isGte](#isGte) | [isNumeric](#isNumeric) |
65 | | [isAfter](#isAfter) | [isImage](#isImage) | [isRegex](#isRegex) |
66 | | [isAfterOrEqual](#isAfterOrEqual) | [isIn](#isIn) | [isRequired](#isRequired) |
67 | | [isArray](#isArray) | [isInteger](#isInteger) | [isRequiredIf](#isRequiredIf) |
68 | | [isBefore](#isBefore) | [isIp](#isIp) | [isRequiredIfAccepted](#isRequiredIfAccepted) |
69 | | [isBeforeOrEqual](#isBeforeOrEqual) | [isJSON](#isJSON) | [isRequiredUnless](#isRequiredUnless) |
70 | | [isBetween](#isBetween) | [isLt](#isLt) | [isRequiredWith](#isRequiredWith) |
71 | | [isBoolean](#isBoolean) | [isLte](#isLte) | [isRequiredWithAll](#isRequiredWithAll) |
72 | | [isDate](#isDate) | [isMax](#isMax) | [isRequiredWithout](#isRequiredWithout) |
73 | | [isDistinct](#isDistinct) | [isMime](#isMime) | [isRequiredWithoutAll](#isRequiredWithoutAll) |
74 | | [isEmail](#isEmail) | [isMin](#isMin) | [isSame](#isSame) |
75 | | [isExtension](#isExtension) | [isNotIn](#isNotIn) | [isUrl](#isUrl) |
76 |
77 | #### isAccepted()
78 | The field under validation must be `"yes"`, `"on"`, `1`, or `true`. This is useful for validating "Terms of Service" acceptance or
79 | similar fields.
80 |
81 | #### isAcceptedIf(anotherfield, value)
82 | The field under validation must be `"yes"`, `"on"`, `1`, or `true` if another field is equal to a specified value. This
83 | is useful for validating "Terms of Service" acceptance or similar fields.
84 |
85 | #### isAfter(date)
86 | The field under validation must be a value after a given date/another field that is itself a `Date` object.
87 |
88 | #### isAfterOrEqual(date)
89 | The field under validation must be a value after or equal to a given date/another field that is itself a `Date` object.
90 |
91 | #### isArray()
92 | The field under validation must be a valid array.
93 |
94 | #### isBefore(date)
95 | The field under validation must be a value preceding a given date/another field that is itself a `Date` object.
96 |
97 | #### isBeforeOrEqual(date)
98 | The field under validation must be a value preceding or equal to a given date/another field that is itself a `Date` object.
99 |
100 | #### isBetween(min, max)
101 | The field under validation must be a number between or equal to the given `min` and `max`.
102 |
103 | #### isBoolean()
104 | The field under validation must be one of: `true`, `false`, `1`, `0`.
105 |
106 | #### isDate()
107 | The field under validation must be a valid `Date` object.
108 |
109 | #### isDistinct()
110 | When validating arrays, the field must not have any duplicate values:
111 |
112 | ```js
113 | {
114 | "foo.*.id": [isDistinct()]
115 | }
116 | ```
117 |
118 | `isDistinct()` uses loose variable comparisons by default. To use strict comparisons, you may pass `strict` as
119 | parameter:
120 |
121 | ```js
122 | {
123 | "foo.*.id": [isDistinct("strict")]
124 | }
125 | ```
126 |
127 | You may also pass `ignoreCase` to make the rule ignore capitalization differences:
128 |
129 | ```js
130 | {
131 | "foo.*.id": [isDistinct("strict", "ignoreCase")]
132 | }
133 | ```
134 |
135 | #### isEmail()
136 | The field under validation must be formatted as an email address.
137 |
138 | #### isExtension(...extensions)
139 | The file must have an extension present in the provided list.
140 |
141 | ```js
142 | {
143 | "document": [isExtension("pdf", "txt", "xls")]
144 | }
145 | ```
146 |
147 | #### isGt(anotherfield)
148 | The field under validation must be a number greater than the given field.
149 |
150 | #### isGte(anotherfield)
151 | The field under validation must be a number greater or equal to the given field.
152 |
153 | #### isImage()
154 | The file must have one of the following extensions: jpg|svg|jpeg|png|bmp|gif|webp.
155 |
156 | #### isIn(...options)
157 | The field under validation must be a value present in the given options.
158 |
159 | ```js
160 | {
161 | "status": [isIn("draft", "published")]
162 | }
163 | ```
164 |
165 | #### isInteger()
166 | The field under validation must be a valid integer. Strings like `"1"` work as well.
167 |
168 | #### isIp()
169 | The field under validation must be an IP (version 4 or 6).
170 |
171 | #### isJSON()
172 | The field under validation must be a valid JSON string.
173 |
174 | #### isLt(anotherField)
175 | The field under validation must be a number smaller than the given field.
176 |
177 | #### isLte(anotherField)
178 | The field under validation must be a number smaller or equal to the given field.
179 |
180 | #### isMax(max)
181 | The field under validation must be less than or equal to a maximum value. This works with numerics by comparing their value, strings and arrays by comparing their length, and files by comparing their size in MB.
182 |
183 | #### isMime(...mimes)
184 | The file must match one of the given mime types:
185 | ```js
186 | {
187 | "document": [isMime("text/*", "application/pdf")]
188 | }
189 | ```
190 |
191 | #### isMin(min)
192 | The field under validation must have a minimum value. This works with numerics by comparing their value, strings and arrays by comparing their length, and files by comparing their size in MB.
193 |
194 | #### isNotIn(...options)
195 | The field under validation must be a value present in the given options.
196 |
197 | #### isNotRegex(regex)
198 | The field under validation must not match the given regular expression.
199 |
200 | #### isNumeric()
201 | The field under validation must be numeric.
202 |
203 | #### isRegex(regex)
204 | The field under validation must match the given regular expression.
205 |
206 | #### isRequired()
207 | The field under validation must be present in the input data and not empty. A field is considered "empty" if one of the following conditions are true:
208 | - The value is `null`.
209 | - The value is an empty string.
210 | - The value is an empty array.
211 |
212 | #### isRequiredIf(anotherField, value)
213 | The field under validation must be present and not empty if the `anotherfield` field is equal to `value`.
214 |
215 | #### isRequiredIfAccepted(anotherField)
216 | The field under validation must be present and not empty if the `anotherfield` is (accepted)[#isAccepted].
217 |
218 | #### isRequiredUnless(anotherField, value)
219 | The field under validation must be present and not empty unless the `anotherfield` is equal to `value`.
220 |
221 | #### isRequiredWith(...otherFields)
222 | The field under validation must be present and not empty only if any of the `otherFields` are present and not empty.
223 |
224 | #### isRequiredWithAll(...otherFields)
225 | The field under validation must be present and not empty only if all of the `otherFields` are present and not empty.
226 |
227 | #### isRequiredWithout(...otherFields)
228 | The field under validation must be present and not empty only when any of the `otherFields` are empty or not present.
229 |
230 | #### isRequiredWithoutAll(...otherFields)
231 | The field under validation must be present and not empty only when all of the `otherFields` are empty or not present.
232 |
233 | #### isSame(anotherField)
234 | The given field must match the `anotherField` under validation.
235 |
236 | #### isUrl()
237 | The field under validation must be a valid URL.
238 |
239 | ## Translations / overwriting error messages
240 | You can overwrite messages globally, or per individual validation.
241 |
242 | For a global overwrite, ideally you would create a wrapper around intus and set its `message` property to an object having keys as rule or field names, and values as the desired messages.
243 |
244 | The following snippet overwrites *all* rule messages, but you can, of course, overwrite only the ones you want.
245 | ```js
246 | // myIntus.js
247 | import intus from "intus";
248 |
249 | intus.messages = {
250 | "isAccepted": ":attribute must be accepted.",
251 | "isAcceptedIf": ":attribute must be accepted if :other is :otherValue.",
252 | "isAfter": ":attribute must be a date after :otherValue.",
253 | "isAfterOrEqual": ":attribute must be a date after or equal to :otherValue.",
254 | "isArray": ":attribute must be a valid array.",
255 | "isBefore": ":attribute must be a date before :otherValue.",
256 | "isBeforeOrEqual": ":attribute must be a date before or equal to :otherValue.",
257 | "isBetween": ":attribute must be between or equal to :min and :max.",
258 | "isBoolean": ":attribute must be a boolean.",
259 | "isDate": ":attribute must be a valid date.",
260 | "isDistinct": ":attribute must be distinct.",
261 | "isEmail": ":attribute must be a valid email.",
262 | "isExtension": ":attribute must be a file of type :extensions.",
263 | "isGt": ":attribute must be greater than :otherValue.",
264 | "isGte": ":attribute must be greater or equal to :otherValue.",
265 | "isImage": ":attribute must be an image.",
266 | "isIn": ":attribute must be one of :options.",
267 | "isInteger": ":attribute must be an integer.",
268 | "isIp": ":attribute must be a valid IP address.",
269 | "isJSON": ":attribute must be a valid JSON.",
270 | "isLt": ":attribute must be less than :otherValue.",
271 | "isLte": ":attribute must be less or equal to :otherValue.",
272 | "isMax": ":attribute must be at most :max.",
273 | "isMax.string": ":attribute must be at most :max characters long.",
274 | "isMax.array": ":attribute must have at most :max items.",
275 | "isMax.file": ":attribute must be at most :maxMB.",
276 | "isMime": ":attribute must be a file of type :mimes.",
277 | "isMin": ":attribute must be at least :min.",
278 | "isMin.string": ":attribute must be at least :min characters long.",
279 | "isMin.array": ":attribute must have at least :min items.",
280 | "isMin.file": ":attribute must be at least :minMB.",
281 | "isNotIn": ":attribute must not be one of :options.",
282 | "isNotRegex": ":attribute must not match regex :regex.",
283 | "isNumeric": ":attribute must be a number.",
284 | "isRegex": ":attribute must match regex :regex.",
285 | "isRequired": ":attribute is required.",
286 | "isRequiredIf": ":attribute is required if :other is :otherValue.",
287 | "isRequiredIfAccepted": ":attribute is required if :other is accepted.",
288 | "isRequiredUnless": ":attribute is required if :other is :otherValue.",
289 | "isRequiredWith": ":attribute is required when :others is present.",
290 | "isRequiredWithAll": ":attribute is required when :others are present.",
291 | "isRequiredWithout": ":attribute is required when :others is missing.",
292 | "isRequiredWithoutAll": ":attribute is required when :others are missing.",
293 | "isSame": ":attribute must be the same as :other.",
294 | "isUrl": ":attribute must be a valid URL.",
295 |
296 | // you can add field names as well
297 | "first_name": "prénom",
298 | "last_name": "nom de famille",
299 | }
300 |
301 | export default intus;
302 |
303 |
304 | // Then import and use the customized version of intus..
305 | import intus from "./myIntus"
306 | ```
307 |
308 | To overwrite messages and field names per individual validation, you can pass an object as the third parameter to the `validate` function:
309 | ```js
310 | let validation = intus.validate(
311 | {
312 | cart_items: [{id: 1, qty: 2}]
313 | },
314 | {
315 | "cart_items.*.id": [isRequired()],
316 | "cart_items.*.qty": [isRequired()],
317 | },
318 | {
319 | "cart_items.*.id": "product", // overwrites "cart_items.0.id" to "product"
320 | "cart_items.*.qty.isRequired": "Qty is absolutely required", // overwrites the entire isRequired message
321 | }
322 | );
323 | ```
324 |
325 | ## Most rules are optional
326 | If fields have an empty value (null/undefined/empty string), the validation will pass.
327 |
328 | For example:
329 | ```js
330 | let validation = intus.validate(
331 | {email: ""},
332 | {email: [isEmail()]}
333 | );
334 |
335 | validation.passes(); // true. Think of it as the rule saying "hey, you haven't actually passed me anything to validate."
336 | ```
337 |
338 | What you probably want is:
339 |
340 | ```js
341 | let validation = intus.validate(
342 | {email: ""},
343 | {email: [isRequired(), isEmail()]}
344 | );
345 |
346 | validation.passes(); // false because isRequired fails
347 |
348 | let validation = intus.validate(
349 | {email: "abc"},
350 | {email: [isRequired(), isEmail()]} // false because isEmail fails
351 | );
352 | ```
353 |
354 |
355 | ## Custom validation rules
356 | To create a custom rule, create a new file called `/rules/my-rule.js`, and use the following snippet as your starting point.
357 |
358 | ```js
359 | // my-rules/isLowercase.js
360 | import {getMessage} from "intus";
361 |
362 | export default function () {
363 | return function isLowercase({value, attribute, messages}) {
364 | return {
365 | passes() {
366 | return value === value.toLowerCase();
367 | },
368 | message(msg = ":attribute must be lowercase.") {
369 | return msg.replaceAll(":attribute", getMessage(attribute, messages));
370 | },
371 | };
372 | };
373 | }
374 | ```
375 | The `passes()` method should return `true/false` to indicate wether or not the rule passed, while the `message()` method should return a string indicating the error.
376 |
377 | Then you can use your custom rule as you would use any other intus rules.
378 | ```js
379 | import isLowercase from "my-rules/isLowercase.js";
380 |
381 | let validation = intus.validate(
382 | {username: "AbC"},
383 | {username: [isLowercase()]}
384 | );
385 |
386 | validation.validate(); // false
387 | ```
388 |
389 | For more complex rules, browse the [rules directory](https://github.com/druc/intus/tree/master/rules) for inspiration.
390 |
391 | ## Credits
392 | This library stands on the shoulders of giants. Many ideas have come from the following projects:
393 | - [laravel/laravel](https://github.com/laravel/laravel)
394 | - [validator.js](https://github.com/validatorjs/validator.js)
395 | - [vee-validate](https://github.com/logaretm/vee-validate)
396 | - [@kingshott/iodine](https://github.com/mattkingshott/iodine)
397 | - [dottie](https://github.com/mickhansen/dottie.js)
398 |
--------------------------------------------------------------------------------
/rules/index.js:
--------------------------------------------------------------------------------
1 | export {default as isAccepted} from "./isAccepted";
2 | export {default as isAcceptedIf} from "./isAcceptedIf";
3 | export {default as isRegex} from "./isRegex";
4 | export {default as isNotRegex} from "./isNotRegex";
5 | export {default as isArray} from "./isArray";
6 | export {default as isDistinct} from "./isDistinct";
7 | export {default as isBoolean} from "./isBoolean";
8 | export {default as isDate} from "./isDate";
9 | export {default as isEmail} from "./isEmail";
10 | export {default as isExtension} from "./isExtension";
11 | export {default as isGt} from "./isGt";
12 | export {default as isGte} from "./isGte";
13 | export {default as isImage} from "./isImage";
14 | export {default as isIn} from "./isIn";
15 | export {default as isJSON} from "./isJSON";
16 | export {default as isInteger} from "./isInteger";
17 | export {default as isIp} from "./isIp";
18 | export {default as isLt} from "./isLt";
19 | export {default as isLte} from "./isLte";
20 | export {default as isMax} from "./isMax";
21 | export {default as isMime} from "./isMime";
22 | export {default as isMin} from "./isMin";
23 | export {default as isBetween} from "./isBetween";
24 | export {default as isSame} from "./isSame";
25 | export {default as isNotIn} from "./isNotIn";
26 | export {default as isNumeric} from "./isNumeric";
27 | export {default as isRequired} from "./isRequired";
28 | export {default as isRequiredIf} from "./isRequiredIf";
29 | export {default as isRequiredIfAccepted} from "./isRequiredIfAccepted";
30 | export {default as isRequiredUnless} from "./isRequiredUnless";
31 | export {default as isRequiredWith} from "./isRequiredWith";
32 | export {default as isRequiredWithAll} from "./isRequiredWithAll";
33 | export {default as isRequiredWithout} from "./isRequiredWithout";
34 | export {default as isRequiredWithoutAll} from "./isRequiredWithoutAll";
35 | export {default as isUrl} from "./isUrl";
36 | export {default as isBeforeOrEqual} from "./isBeforeOrEqual";
37 |
--------------------------------------------------------------------------------
/rules/isAccepted.js:
--------------------------------------------------------------------------------
1 | import {assertAccepted} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isAccepted({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | return assertAccepted(value);
9 | },
10 | message(msg = ":attribute must be accepted.") {
11 | return msg
12 | .replaceAll(":value", value)
13 | .replaceAll(":attribute", getMessage(attribute, messages));
14 | },
15 | };
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/rules/isAcceptedIf.js:
--------------------------------------------------------------------------------
1 | import {assertAccepted} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other, otherValue) {
5 | return function isAcceptedIf({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertAccepted(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (data[other] === otherValue) {
16 | return assertAccepted(value);
17 | }
18 |
19 | return true;
20 | },
21 | message(msg = ":attribute must be accepted if :other is :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", otherValue)
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", attribute);
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isAfter.js:
--------------------------------------------------------------------------------
1 | import {assertDate, assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (otherValue) {
5 | return function isAfter({value, attribute, messages, data}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | if (typeof otherValue === "string") {
13 | otherValue = replaceWildcard(otherValue, attribute);
14 | data = initializeAndGatherData(otherValue, data);
15 | otherValue = data[otherValue];
16 | }
17 |
18 | if (!assertDate(value) || !assertDate(otherValue)) {
19 | return true;
20 | }
21 |
22 | return value.getTime() > otherValue.getTime();
23 | },
24 | message(msg = ":attribute must be a date after :otherValue.") {
25 | return msg
26 | .replaceAll(":value", value)
27 | .replaceAll(":attribute", getMessage(attribute, messages))
28 | .replaceAll(":otherValue", getMessage(otherValue, messages));
29 | },
30 | };
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/rules/isAfterOrEqual.js:
--------------------------------------------------------------------------------
1 | import {assertDate, assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (otherValue) {
5 | return function isAfterOrEqual({value, attribute, messages, data}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | if (typeof otherValue === "string") {
13 | otherValue = replaceWildcard(otherValue, attribute);
14 | data = initializeAndGatherData(otherValue, data);
15 | otherValue = data[otherValue];
16 | }
17 |
18 | if (!assertDate(value) || !assertDate(otherValue)) {
19 | return true;
20 | }
21 |
22 | return value.getTime() >= otherValue.getTime();
23 | },
24 | message(
25 | msg = ":attribute must be a date after or equal to :otherValue."
26 | ) {
27 | return msg
28 | .replaceAll(":value", value)
29 | .replaceAll(":attribute", getMessage(attribute, messages))
30 | .replaceAll(":otherValue", getMessage(otherValue, messages));
31 | },
32 | };
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/rules/isArray.js:
--------------------------------------------------------------------------------
1 | import {getMessage} from "../lib";
2 | import {assertEmpty} from "../assertions";
3 |
4 | export default function () {
5 | return function isArray({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return Array.isArray(value);
13 | },
14 | message(msg = ":attribute must be a valid array.") {
15 | return msg
16 | .replaceAll(":value", value)
17 | .replaceAll(":attribute", getMessage(attribute, messages));
18 | },
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/rules/isBefore.js:
--------------------------------------------------------------------------------
1 | import {assertDate, assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (otherValue) {
5 | return function isBefore({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | if (typeof otherValue === "string") {
13 | otherValue = replaceWildcard(otherValue, attribute);
14 | data = initializeAndGatherData(otherValue, data);
15 | otherValue = data[otherValue];
16 | }
17 |
18 | if (!assertDate(value) || !assertDate(otherValue)) {
19 | return true;
20 | }
21 |
22 | return value.getTime() < otherValue.getTime();
23 | },
24 | message(msg = ":attribute must be a date before :otherValue.") {
25 | return msg
26 | .replaceAll(":value", value)
27 | .replaceAll(":attribute", getMessage(attribute, messages))
28 | .replaceAll(":otherValue", getMessage(otherValue, messages));
29 | },
30 | };
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/rules/isBeforeOrEqual.js:
--------------------------------------------------------------------------------
1 | import {assertDate, assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (otherValue) {
5 | return function isBeforeOrEqual({value, attribute, messages, data}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | if (typeof otherValue === "string") {
13 | otherValue = replaceWildcard(otherValue, attribute);
14 | data = initializeAndGatherData(otherValue, data);
15 | otherValue = data[otherValue];
16 | }
17 |
18 | if (!assertDate(value) || !assertDate(otherValue)) {
19 | return true;
20 | }
21 |
22 | return value.getTime() <= otherValue.getTime();
23 | },
24 | message(
25 | msg = ":attribute must be a date before or equal to :otherValue."
26 | ) {
27 | return msg
28 | .replaceAll(":value", value)
29 | .replaceAll(":attribute", getMessage(attribute, messages))
30 | .replaceAll(":otherValue", getMessage(otherValue, messages));
31 | },
32 | };
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/rules/isBetween.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (min, max) {
5 | return function isBetween({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return Number(value) >= Number(min) && Number(value) <= Number(max);
13 | },
14 | message(msg = ":attribute must be between or equal to :min and :max.") {
15 | return msg
16 | .replaceAll(":min", min)
17 | .replaceAll(":max", max)
18 | .replaceAll(":value", value)
19 | .replaceAll(":attribute", getMessage(attribute, messages));
20 | },
21 | };
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/rules/isBoolean.js:
--------------------------------------------------------------------------------
1 | import {getMessage} from "../lib";
2 |
3 | export default function () {
4 | return function isBoolean({value, attribute, messages}) {
5 | return {
6 | passes() {
7 | return [true, false, 1, 0].includes(value);
8 | },
9 | message(msg = ":attribute must be a boolean.") {
10 | return msg
11 | .replaceAll(":value", value)
12 | .replaceAll(":attribute", getMessage(attribute, messages));
13 | },
14 | };
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/rules/isDate.js:
--------------------------------------------------------------------------------
1 | import {assertDate, assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isDate({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return assertDate(value);
13 | },
14 | message(msg = ":attribute must be a valid date.") {
15 | return msg
16 | .replaceAll(":value", value)
17 | .replaceAll(":attribute", getMessage(attribute, messages));
18 | },
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/rules/isDistinct.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty, assertUniqueArray} from "../assertions";
2 | import {attributeToWildcard, getMessage} from "../lib";
3 |
4 | export default function (...options) {
5 | return function isDistinct({value, data, attribute, messages, masterRules}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | let wildcardAttribute = attributeToWildcard(attribute);
13 | let strict = options.includes("strict");
14 | let ignoreCase = options.includes("ignoreCase");
15 | let values = [];
16 |
17 | let pattern = new RegExp(
18 | wildcardAttribute.replaceAll("*", "[^.]*") + "$"
19 | );
20 | for (let key in data) {
21 | if (pattern.test(key)) {
22 | if (ignoreCase) {
23 | values.push(data[key]?.toString()?.toLowerCase());
24 | } else {
25 | values.push(data[key]);
26 | }
27 | }
28 | }
29 |
30 | if (strict) {
31 | return new Set(values).size === values.length;
32 | }
33 |
34 | return assertUniqueArray(values);
35 | },
36 | message(msg = ":attribute must be distinct.") {
37 | return msg
38 | .replaceAll(":value", value)
39 | .replaceAll(":attribute", getMessage(attribute, messages));
40 | },
41 | };
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/rules/isEmail.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isEmail({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return new RegExp(
13 | "^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
14 | ).test(String(value).toLowerCase());
15 | },
16 | message(msg = ":attribute must be a valid email.") {
17 | return msg
18 | .replaceAll(":value", value)
19 | .replaceAll(":attribute", getMessage(attribute, messages));
20 | },
21 | };
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/rules/isExtension.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (...extensions) {
5 | return function isExtension({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return new RegExp(`.(${extensions.join("|")})$`, "i").test(value.name);
13 | },
14 | message(msg = ":attribute must be a file of type :extensions.") {
15 | return msg
16 | .replaceAll(":extensions", extensions.join(" / "))
17 | .replaceAll(":attribute", getMessage(attribute, messages));
18 | },
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/rules/isGt.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other) {
5 | return function isGt({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (assertEmpty(data[other])) {
16 | return true;
17 | }
18 |
19 | return Number(value) > Number(data[other]);
20 | },
21 | message(msg = ":attribute must be greater than :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", data[other])
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isGte.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other) {
5 | return function isGte({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (assertEmpty(data[other])) {
16 | return true;
17 | }
18 |
19 | return Number(value) >= Number(data[other]);
20 | },
21 | message(msg = ":attribute must be greater or equal to :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", data[other])
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isImage.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isImage({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 | return new RegExp(/\.(jpg|svg|jpeg|png|bmp|gif|webp)$/i).test(
12 | value.name
13 | );
14 | },
15 | message(msg = ":attribute must be an image.") {
16 | return msg
17 | .replaceAll(":value", value)
18 | .replaceAll(":attribute", getMessage(attribute, messages));
19 | },
20 | };
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/rules/isIn.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (...options) {
5 | return function isIn({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 | return options.includes(value.toString());
12 | },
13 | message(msg = ":attribute must be one of :options.") {
14 | return msg
15 | .replaceAll(":value", value)
16 | .replaceAll(
17 | ":options",
18 | options.map(o => getMessage(o, messages)).join(" / ")
19 | )
20 | .replaceAll(":attribute", getMessage(attribute, messages));
21 | },
22 | };
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/rules/isInteger.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isInteger({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return new RegExp("^-?[0-9]+$").test(String(value));
13 | },
14 | message(msg = ":attribute must be an integer.") {
15 | return msg
16 | .replaceAll(":value", value)
17 | .replaceAll(":attribute", getMessage(attribute, messages));
18 | },
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/rules/isIp.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isIp({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return new RegExp(
13 | "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
14 | ).test(String(value));
15 | },
16 | message(msg = ":attribute must be a valid IP address.") {
17 | return msg
18 | .replaceAll(":value", value)
19 | .replaceAll(":attribute", getMessage(attribute, messages));
20 | },
21 | };
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/rules/isJSON.js:
--------------------------------------------------------------------------------
1 | import {getMessage} from "../lib";
2 | import {assertEmpty} from "../assertions";
3 |
4 | export default function () {
5 | return function isJSON({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | try {
13 | const obj = JSON.parse(value);
14 | return !!obj && typeof obj === "object";
15 | } catch (e) {}
16 | return false;
17 | },
18 | message(msg = ":attribute must be a valid JSON.") {
19 | return msg
20 | .replaceAll(":value", value)
21 | .replaceAll(":attribute", getMessage(attribute, messages));
22 | },
23 | };
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/rules/isLt.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other) {
5 | return function isLt({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (assertEmpty(data[other])) {
16 | return true;
17 | }
18 |
19 | return Number(value) < Number(data[other]);
20 | },
21 | message(msg = ":attribute must be less than :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", data[other])
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isLte.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other) {
5 | return function isLte({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (assertEmpty(data[other])) {
16 | return true;
17 | }
18 |
19 | return Number(value) <= Number(data[other]);
20 | },
21 | message(msg = ":attribute must be less or equal to :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", data[other])
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isMax.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {bytesToMb, getMessage} from "../lib";
3 |
4 | export default function (max) {
5 | return function isMax({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | if (typeof value === "string") {
13 | return value.length <= Number(max);
14 | }
15 |
16 | if (Array.isArray(value)) {
17 | return value.length <= Number(max);
18 | }
19 |
20 | if (value.constructor?.name === "File") {
21 | return bytesToMb(value.size) <= Number(max);
22 | }
23 |
24 | return Number(value) <= Number(max);
25 | },
26 | message() {
27 | let segment = `${attribute}.isMax`;
28 |
29 | let msg = getMessage(
30 | segment,
31 | messages,
32 | ":attribute must be at most :max."
33 | );
34 |
35 | if (typeof value === "string") {
36 | msg = getMessage(
37 | `${segment}.string`,
38 | messages,
39 | ":attribute must be at most :max characters long."
40 | );
41 | }
42 |
43 | if (Array.isArray(value)) {
44 | msg = getMessage(
45 | `${segment}.array`,
46 | messages,
47 | ":attribute must have at most :max items."
48 | );
49 | }
50 |
51 | if (value.constructor?.name === "File") {
52 | msg = getMessage(
53 | `${segment}.file`,
54 | messages,
55 | ":attribute must be at most :maxMB."
56 | );
57 | }
58 |
59 | return msg
60 | .replaceAll(":max", max)
61 | .replaceAll(":value", value)
62 | .replaceAll(":attribute", getMessage(attribute, messages));
63 | },
64 | };
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/rules/isMime.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (...mimes) {
5 | return function isMime({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 | return new RegExp(`${mimes.join("|").replace("*", ".+")}$`, "i").test(
12 | value?.type
13 | );
14 | },
15 | message(msg = ":attribute must be a file of type :mimes.") {
16 | return msg
17 | .replaceAll(":mimes", mimes.join(" / "))
18 | .replaceAll(":attribute", getMessage(attribute, messages));
19 | },
20 | };
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/rules/isMin.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {bytesToMb, getMessage} from "../lib";
3 |
4 | export default function (min) {
5 | return function isMin({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | if (typeof value === "string") {
13 | return value.length >= Number(min);
14 | }
15 |
16 | if (Array.isArray(value)) {
17 | return value.length >= Number(min);
18 | }
19 |
20 | if (value.constructor?.name === "File") {
21 | return bytesToMb(value.size) >= Number(min);
22 | }
23 |
24 | return Number(value) >= Number(min);
25 | },
26 | message() {
27 | let segment = `${attribute}.isMin`;
28 |
29 | let msg = getMessage(
30 | segment,
31 | messages,
32 | ":attribute must be at least :min."
33 | );
34 |
35 | if (typeof value === "string") {
36 | msg = getMessage(
37 | `${segment}.string`,
38 | messages,
39 | ":attribute must be at least :min characters long."
40 | );
41 | }
42 |
43 | if (Array.isArray(value)) {
44 | msg = getMessage(
45 | `${segment}.array`,
46 | messages,
47 | ":attribute must have at least :min items."
48 | );
49 | }
50 |
51 | if (value.constructor?.name === "File") {
52 | msg = getMessage(
53 | `${segment}.file`,
54 | messages,
55 | ":attribute must be at least :minMB."
56 | );
57 | }
58 |
59 | return msg
60 | .replaceAll(":min", min)
61 | .replaceAll(":value", value)
62 | .replaceAll(":attribute", getMessage(attribute, messages));
63 | },
64 | };
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/rules/isNotIn.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (...options) {
5 | return function isNotIn({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return !options.includes(value?.toString());
13 | },
14 | message(msg = ":attribute must not be one of :options.") {
15 | return msg
16 | .replaceAll(":value", value)
17 | .replaceAll(
18 | ":options",
19 | options.map(o => getMessage(o, messages)).join(" / ")
20 | )
21 | .replaceAll(":attribute", getMessage(attribute, messages));
22 | },
23 | };
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/rules/isNotRegex.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (regex) {
5 | return function isNotRegex({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return !regex.test(value);
13 | },
14 | message(msg = ":attribute must not match regex :regex.") {
15 | return msg
16 | .replaceAll(":regex", regex)
17 | .replaceAll(":value", value)
18 | .replaceAll(":attribute", getMessage(attribute, messages));
19 | },
20 | };
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/rules/isNumeric.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isNumeric({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return !isNaN(Number(value)) && isFinite(value);
13 | },
14 | message(msg = ":attribute must be a number.") {
15 | return msg
16 | .replaceAll(":value", value)
17 | .replaceAll(":attribute", getMessage(attribute, messages));
18 | },
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/rules/isRegex.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function (regex) {
5 | return function isRegex({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | return regex.test(value);
13 | },
14 | message(msg = ":attribute must match regex :regex.") {
15 | return msg
16 | .replaceAll(":regex", regex)
17 | .replaceAll(":value", value)
18 | .replaceAll(":attribute", getMessage(attribute, messages));
19 | },
20 | };
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/rules/isRequired.js:
--------------------------------------------------------------------------------
1 | import {assertRequired} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isRequired({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | return assertRequired(value);
9 | },
10 | message(msg = ":attribute is required.") {
11 | return msg
12 | .replaceAll(":value", getMessage(value, messages))
13 | .replaceAll(":attribute", getMessage(attribute, messages));
14 | },
15 | };
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/rules/isRequiredIf.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty, assertRequired} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other, otherValue) {
5 | return function isRequiredIf({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (data[other] === otherValue) {
16 | return assertRequired(value);
17 | }
18 |
19 | return true;
20 | },
21 | message(msg = ":attribute is required if :other is :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", otherValue)
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isRequiredIfAccepted.js:
--------------------------------------------------------------------------------
1 | import {assertAccepted, assertEmpty, assertRequired} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other) {
5 | return function isRequiredIfAccepted({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (!assertAccepted(data[other])) {
16 | return true;
17 | }
18 |
19 | return assertRequired(value);
20 | },
21 | message(msg = ":attribute is required if :other is accepted.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", data[other])
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isRequiredUnless.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (other, otherValue) {
5 | return function isRequiredUnless({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | other = replaceWildcard(other, attribute);
13 | data = initializeAndGatherData(other, data);
14 |
15 | if (data[other] === otherValue) {
16 | return true;
17 | }
18 |
19 | return false;
20 | },
21 | message(msg = ":attribute is required if :other is :otherValue.") {
22 | return msg
23 | .replaceAll(":value", value)
24 | .replaceAll(":otherValue", otherValue)
25 | .replaceAll(":other", getMessage(other, messages))
26 | .replaceAll(":attribute", getMessage(attribute, messages));
27 | },
28 | };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/rules/isRequiredWith.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (...others) {
5 | return function isRequiredWith({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | others = others.map(o => replaceWildcard(o, attribute));
13 | others.forEach(o => (data = initializeAndGatherData(o, data)));
14 |
15 | return !!others.every(other => assertEmpty(data[other]));
16 | },
17 | message(msg = ":attribute is required when :others is present.") {
18 | return msg
19 | .replaceAll(":value", value)
20 | .replaceAll(
21 | ":others",
22 | others.map(o => getMessage(o, messages)).join(" / ")
23 | )
24 | .replaceAll(":attribute", getMessage(attribute, messages));
25 | },
26 | };
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/rules/isRequiredWithAll.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (...others) {
5 | return function isRequiredWithAll({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | others = others.map(o => replaceWildcard(o, attribute));
13 | others.forEach(o => (data = initializeAndGatherData(o, data)));
14 |
15 | return !others.every(other => !assertEmpty(data[other]));
16 | },
17 | message(msg = ":attribute is required when :others are present.") {
18 | return msg
19 | .replaceAll(":value", value)
20 | .replaceAll(
21 | ":others",
22 | others.map(o => getMessage(o, messages)).join(" / ")
23 | )
24 | .replaceAll(":attribute", getMessage(attribute, messages));
25 | },
26 | };
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/rules/isRequiredWithout.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (...others) {
5 | return function isRequiredWithout({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | others = others.map(o => replaceWildcard(o, attribute));
13 | others.forEach(o => (data = initializeAndGatherData(o, data)));
14 |
15 | return others.some(other => !assertEmpty(data[other]));
16 | },
17 | message(msg = ":attribute is required when :others is missing.") {
18 | return msg
19 | .replaceAll(":value", value)
20 | .replaceAll(
21 | ":others",
22 | others.map(o => getMessage(o, messages)).join(" / ")
23 | )
24 | .replaceAll(":attribute", getMessage(attribute, messages));
25 | },
26 | };
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/rules/isRequiredWithoutAll.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
3 |
4 | export default function (...others) {
5 | return function isRequiredWithoutAll({value, data, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (!assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | others = others.map(o => replaceWildcard(o, attribute));
13 | others.forEach(o => (data = initializeAndGatherData(o, data)));
14 |
15 | return !others.every(other => assertEmpty(data[other]));
16 | },
17 | message(msg = ":attribute is required when :others are missing.") {
18 | return msg
19 | .replaceAll(":value", value)
20 | .replaceAll(
21 | ":others",
22 | others.map(o => getMessage(o, messages)).join(" / ")
23 | )
24 | .replaceAll(":attribute", getMessage(attribute, messages));
25 | },
26 | };
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/rules/isSame.js:
--------------------------------------------------------------------------------
1 | import {getMessage, initializeAndGatherData, replaceWildcard} from "../lib";
2 |
3 | export default function (other) {
4 | return function isSame({value, data, attribute, messages}) {
5 | return {
6 | passes() {
7 | other = replaceWildcard(other, attribute);
8 | data = initializeAndGatherData(other, data);
9 | return value?.toString() === data[other]?.toString();
10 | },
11 | message(msg = ":attribute must be the same as :other.") {
12 | return msg
13 | .replaceAll(":value", value)
14 | .replaceAll(":otherValue", data[other])
15 | .replaceAll(":other", getMessage(other, messages))
16 | .replaceAll(":attribute", getMessage(attribute, messages));
17 | },
18 | };
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/rules/isUrl.js:
--------------------------------------------------------------------------------
1 | import {assertEmpty} from "../assertions";
2 | import {getMessage} from "../lib";
3 |
4 | export default function () {
5 | return function isUrl({value, attribute, messages}) {
6 | return {
7 | passes() {
8 | if (assertEmpty(value)) {
9 | return true;
10 | }
11 |
12 | let regex =
13 | "^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$";
14 |
15 | return new RegExp(regex).test(String(value).toLowerCase());
16 | },
17 | message(msg = ":attribute must be a valid URL.") {
18 | return msg
19 | .replaceAll(":value", value)
20 | .replaceAll(":attribute", getMessage(attribute, messages));
21 | },
22 | };
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/test/helpers/file.js:
--------------------------------------------------------------------------------
1 | export class File {
2 | constructor(name, type, size) {
3 | this.name = name;
4 | this.type = type;
5 | this.size = size * 1024 ** 2;
6 | }
7 | }
8 |
9 | export default function makeFile(name, type, size = 1) {
10 | return new File(name, type, size);
11 | }
12 |
--------------------------------------------------------------------------------
/test/helpers/index.js:
--------------------------------------------------------------------------------
1 | export {default as makeFile} from "./file";
2 |
--------------------------------------------------------------------------------
/test/isAccepted.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import {isAccepted} from "../rules";
3 | import intus from "../index";
4 |
5 | it("isAccepted", function () {
6 | let invalid = [undefined, "", "z", false];
7 | let valid = [1, "yes", "on", true];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isAccepted()]});
11 | expect(v.passes()).toBe(false);
12 | });
13 | valid.forEach(t =>
14 | expect(intus.validate({t}, {t: [isAccepted()]}).passes()).toBe(true)
15 | );
16 | });
17 |
18 | it("isAccepted nested", function () {
19 | let v = intus.validate(
20 | {nested: {field: "on"}},
21 | {"nested.field": [isAccepted()]}
22 | );
23 | expect(v.passes()).toBe(true);
24 |
25 | v = intus.validate(
26 | {nested: {field: "not-accepted"}},
27 | {"nested.field": [isAccepted()]}
28 | );
29 | expect(v.passes()).toBe(false);
30 | expect(v.errors()).toMatchObject({
31 | "nested.field": "Nested.field must be accepted.",
32 | });
33 | });
34 |
35 | it("isAccepted nested star", function () {
36 | let v = intus.validate(
37 | {nested: [{field: "on"}]},
38 | {"nested.*.field": [isAccepted()]}
39 | );
40 | expect(v.passes()).toBe(true);
41 |
42 | v = intus.validate(
43 | {nested: [{field: "not-accepted"}]},
44 | {"nested.*.field": [isAccepted()]}
45 | );
46 | expect(v.passes()).toBe(false);
47 | expect(v.errors()).toMatchObject({
48 | "nested.0.field": "Nested.0.field must be accepted.",
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/test/isAcceptedIf.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import {isAcceptedIf} from "../rules";
3 | import intus from "../index";
4 |
5 | it("isAcceptedIf", function () {
6 | let v = intus.validate(
7 | {terms: "on", age: 12},
8 | {terms: [isAcceptedIf("age", 12)]}
9 | );
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({age: 12}, {terms: [isAcceptedIf("age", 12)]});
13 | expect(v.passes()).toBe(false);
14 | expect(v.errors()).toMatchObject({
15 | terms: "Terms must be accepted if age is 12.",
16 | });
17 | });
18 |
19 | it("isAcceptedIf nested", function () {
20 | let v = intus.validate(
21 | {nested: {field: "on", age: 12}},
22 | {"nested.field": [isAcceptedIf("nested.age", 12)]}
23 | );
24 | expect(v.passes()).toBe(true);
25 |
26 | v = intus.validate(
27 | {nested: {field: "off"}},
28 | {"nested.field": [isAcceptedIf("nested.age", 12)]}
29 | );
30 | expect(v.passes()).toBe(true);
31 |
32 | v = intus.validate(
33 | {nested: {age: 12}},
34 | {"nested.field": [isAcceptedIf("nested.age", 12)]}
35 | );
36 | expect(v.passes()).toBe(false);
37 | expect(v.errors()).toMatchObject({
38 | "nested.field": "Nested.field must be accepted if nested.age is 12.",
39 | });
40 | });
41 |
42 | it("isAcceptedIf nested star", function () {
43 | let v = intus.validate(
44 | {nested: [{field: "on", age: 12}]},
45 | {"nested.*.field": [isAcceptedIf("nested.*.age", 12)]}
46 | );
47 | expect(v.passes()).toBe(true);
48 |
49 | v = intus.validate(
50 | {nested: [{field: "off"}]},
51 | {"nested.*.field": [isAcceptedIf("nested.*.age", 12)]}
52 | );
53 | expect(v.passes()).toBe(true);
54 |
55 | v = intus.validate(
56 | {nested: [{age: 12}]},
57 | {"nested.*.field": [isAcceptedIf("nested.*.age", 12)]}
58 | );
59 | expect(v.passes()).toBe(false);
60 | expect(v.errors()).toMatchObject({
61 | "nested.0.field": "Nested.0.field must be accepted if nested.0.age is 12.",
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/test/isAfter.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import isAfter from "../rules/isAfter";
3 | import intus from "../index";
4 |
5 | it("isAfter", function () {
6 | let afterDate = new Date(2022, 12, 18);
7 | let v = intus.validate(
8 | {date: new Date(2022, 12, 19)},
9 | {date: [isAfter(afterDate)]}
10 | );
11 | expect(v.passes()).toBe(true);
12 |
13 | v = intus.validate(
14 | {date: new Date(2022, 12, 18)},
15 | {date: [isAfter(afterDate)]}
16 | );
17 | expect(v.passes()).toBe(false);
18 | expect(v.errors()).toMatchObject({
19 | date: `Date must be a date after ${afterDate}.`,
20 | });
21 | });
22 |
23 | it("isAfter passes if value is not a valid date", function () {
24 | let v = intus.validate(
25 | {date: "some random value"},
26 | {date: [isAfter(new Date(2022, 12, 18))]}
27 | );
28 | expect(v.passes()).toBe(true);
29 | });
30 |
31 | it("isAfter passes if after date is not a valid date", function () {
32 | let v = intus.validate(
33 | {date: new Date(2022, 12, 18)},
34 | {date: [isAfter("random")]}
35 | );
36 | expect(v.passes()).toBe(true);
37 | });
38 |
39 | it("isAfter works with attributes", function () {
40 | let v = intus.validate(
41 | {
42 | start: new Date(2022, 12, 18),
43 | end: new Date(2022, 12, 18),
44 | },
45 | {start: [isAfter("end")]}
46 | );
47 | expect(v.passes()).toBe(false);
48 | });
49 |
50 | it("isAfter nested attributes", function () {
51 | let v = intus.validate(
52 | {
53 | nested: {
54 | start: new Date(2022, 12, 18),
55 | end: new Date(2022, 12, 18),
56 | },
57 | },
58 | {"nested.start": [isAfter("nested.end")]}
59 | );
60 |
61 | expect(v.passes()).toBe(false);
62 | });
63 |
64 | it("isAfter nested star attributes", function () {
65 | let v = intus.validate(
66 | {
67 | nested: [
68 | {
69 | start: new Date(2022, 12, 18),
70 | end: new Date(2022, 12, 18),
71 | },
72 | ],
73 | },
74 | {"nested.*.start": [isAfter("nested.*.end")]}
75 | );
76 |
77 | expect(v.passes()).toBe(false);
78 | });
79 |
80 | it("isAfter nested", function () {
81 | let afterDate = new Date(2022, 12, 18);
82 | let v = intus.validate(
83 | {nested: {date: new Date(2022, 12, 19)}},
84 | {"nested.date": [isAfter(afterDate)]}
85 | );
86 | expect(v.passes()).toBe(true);
87 |
88 | v = intus.validate(
89 | {nested: {date: new Date(2022, 12, 18)}},
90 | {"nested.date": [isAfter(afterDate)]}
91 | );
92 | expect(v.passes()).toBe(false);
93 | expect(v.errors()).toMatchObject({
94 | "nested.date": `Nested.date must be a date after ${afterDate}.`,
95 | });
96 | });
97 |
98 | it("isAfter nested star", function () {
99 | let afterDate = new Date(2022, 12, 18);
100 | let v = intus.validate(
101 | {nested: [{date: new Date(2022, 12, 19)}]},
102 | {"nested.*.date": [isAfter(afterDate)]}
103 | );
104 | expect(v.passes()).toBe(true);
105 |
106 | v = intus.validate(
107 | {nested: [{date: new Date(2022, 12, 18)}]},
108 | {"nested.*.date": [isAfter(afterDate)]}
109 | );
110 | expect(v.passes()).toBe(false);
111 | expect(v.errors()).toMatchObject({
112 | "nested.0.date": `Nested.0.date must be a date after ${afterDate}.`,
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/test/isAfterOrEqual.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import isAfterOrEqual from "../rules/isAfterOrEqual";
4 | import isAfter from "../rules/isAfter";
5 |
6 | it("isAfterOrEqual", function () {
7 | let dateAfter = new Date(2022, 12, 18);
8 | let v = intus.validate(
9 | {date: new Date(2022, 12, 19)},
10 | {date: [isAfterOrEqual(dateAfter)]}
11 | );
12 | expect(v.passes()).toBe(true);
13 |
14 | v = intus.validate(
15 | {date: new Date(2022, 12, 18)},
16 | {date: [isAfterOrEqual(dateAfter)]}
17 | );
18 | expect(v.passes()).toBe(true);
19 |
20 | v = intus.validate(
21 | {date: new Date(2022, 12, 17)},
22 | {date: [isAfterOrEqual(dateAfter)]}
23 | );
24 | expect(v.passes()).toBe(false);
25 | expect(v.errors()).toMatchObject({
26 | date: `Date must be a date after or equal to ${dateAfter}.`,
27 | });
28 | });
29 |
30 | it("isAfterOrEqual passes if value is not a valid date", function () {
31 | let v = intus.validate(
32 | {date: "some random value"},
33 | {date: [isAfterOrEqual(new Date(2022, 12, 18))]}
34 | );
35 | expect(v.passes()).toBe(true);
36 | });
37 |
38 | it("isAfterOrEqual passes if after date is not a valid date", function () {
39 | let v = intus.validate(
40 | {date: new Date(2022, 12, 18)},
41 | {date: [isAfterOrEqual("random")]}
42 | );
43 | expect(v.passes()).toBe(true);
44 | });
45 |
46 | it("isAfterOrEqual attributes", function () {
47 | let v = intus.validate(
48 | {
49 | start: new Date(2022, 12, 18),
50 | end: new Date(2022, 12, 19),
51 | },
52 | {start: [isAfterOrEqual("end")]}
53 | );
54 | expect(v.passes()).toBe(false);
55 | });
56 |
57 | it("isAfterOrEqual nested attributes", function () {
58 | let v = intus.validate(
59 | {
60 | nested: {
61 | start: new Date(2022, 12, 18),
62 | end: new Date(2022, 12, 19),
63 | },
64 | },
65 | {"nested.start": [isAfterOrEqual("nested.end")]}
66 | );
67 |
68 | expect(v.passes()).toBe(false);
69 | });
70 |
71 | it("isAfterOrEqual nested star attributes", function () {
72 | let v = intus.validate(
73 | {
74 | nested: [
75 | {
76 | start: new Date(2022, 12, 18),
77 | end: new Date(2022, 12, 19),
78 | },
79 | ],
80 | },
81 | {"nested.*.start": [isAfter("nested.*.end")]}
82 | );
83 |
84 | expect(v.passes()).toBe(false);
85 | });
86 |
87 | it("isAfterOrEqual nested", function () {
88 | let dateAfter = new Date(2022, 12, 18);
89 | let v = intus.validate(
90 | {nested: {date: new Date(2022, 12, 18)}},
91 | {"nested.date": [isAfterOrEqual(dateAfter)]}
92 | );
93 | expect(v.passes()).toBe(true);
94 |
95 | v = intus.validate(
96 | {nested: {date: new Date(2022, 12, 17)}},
97 | {"nested.date": [isAfterOrEqual(dateAfter)]}
98 | );
99 | expect(v.passes()).toBe(false);
100 | expect(v.errors()).toMatchObject({
101 | "nested.date": `Nested.date must be a date after or equal to ${dateAfter}.`,
102 | });
103 | });
104 |
105 | it("isAfterOrEqual nested star", function () {
106 | let dateAfter = new Date(2022, 12, 18);
107 | let v = intus.validate(
108 | {nested: [{date: new Date(2022, 12, 18)}]},
109 | {"nested.*.date": [isAfterOrEqual(dateAfter)]}
110 | );
111 | expect(v.passes()).toBe(true);
112 |
113 | v = intus.validate(
114 | {nested: [{date: new Date(2022, 12, 17)}]},
115 | {"nested.*.date": [isAfterOrEqual(dateAfter)]}
116 | );
117 | expect(v.passes()).toBe(false);
118 | expect(v.errors()).toMatchObject({
119 | "nested.0.date": `Nested.0.date must be a date after or equal to ${dateAfter}.`,
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/test/isArray.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isArray} from "../rules";
4 |
5 | it("isArray", function () {
6 | let invalid = ["a", 1, {}];
7 | let valid = [[], undefined, "", null];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isArray()]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must be a valid array."});
13 | });
14 |
15 | valid.forEach(t =>
16 | expect(intus.validate({t}, {t: [isArray()]}).passes()).toBe(true)
17 | );
18 | });
19 |
20 | it("isArray nested", function () {
21 | let validation = intus.validate(
22 | {nested: {field: "not-array"}},
23 | {"nested.field": [isArray()]}
24 | );
25 | expect(validation.passes()).toBe(false);
26 | expect(validation.errors()).toMatchObject({
27 | "nested.field": "Nested.field must be a valid array.",
28 | });
29 | });
30 |
31 | it("isArray nested star", function () {
32 | let validation = intus.validate(
33 | {nested: [{field: "not-array"}]},
34 | {"nested.*.field": [isArray()]}
35 | );
36 | expect(validation.passes()).toBe(false);
37 | expect(validation.errors()).toMatchObject({
38 | "nested.0.field": "Nested.0.field must be a valid array.",
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/test/isBefore.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import isBefore from "../rules/isBefore";
4 |
5 | it("isBefore", function () {
6 | let beforeDate = new Date(2022, 12, 18);
7 | let v = intus.validate(
8 | {date: new Date(2021, 12, 18)},
9 | {date: [isBefore(beforeDate)]}
10 | );
11 | expect(v.passes()).toBe(true);
12 |
13 | v = intus.validate(
14 | {date: new Date(2022, 12, 19)},
15 | {date: [isBefore(beforeDate)]}
16 | );
17 | expect(v.passes()).toBe(false);
18 | expect(v.errors()).toMatchObject({
19 | date: `Date must be a date before ${beforeDate}.`,
20 | });
21 | });
22 |
23 | it("isBefore passes if value is not a valid date", function () {
24 | let v = intus.validate(
25 | {date: "some random value"},
26 | {date: [isBefore(new Date(2022, 12, 18))]}
27 | );
28 | expect(v.passes()).toBe(true);
29 | });
30 |
31 | it("isBefore passes if before date is not a valid date", function () {
32 | let v = intus.validate(
33 | {date: new Date(2022, 12, 18)},
34 | {date: [isBefore("random")]}
35 | );
36 | expect(v.passes()).toBe(true);
37 | });
38 |
39 | it("isBefore nested", function () {
40 | let beforeDate = new Date(2022, 12, 22);
41 | let v = intus.validate(
42 | {nested: {date: new Date(2022, 12, 19)}},
43 | {"nested.date": [isBefore(beforeDate)]}
44 | );
45 | expect(v.passes()).toBe(true);
46 |
47 | v = intus.validate(
48 | {nested: {date: new Date(2022, 12, 23)}},
49 | {"nested.date": [isBefore(beforeDate)]}
50 | );
51 | expect(v.passes()).toBe(false);
52 | expect(v.errors()).toMatchObject({
53 | "nested.date": `Nested.date must be a date before ${beforeDate}.`,
54 | });
55 | });
56 |
57 | it("isBefore nested star", function () {
58 | let beforeDate = new Date(2022, 12, 22);
59 | let v = intus.validate(
60 | {nested: [{date: new Date(2022, 12, 19)}]},
61 | {"nested.*.date": [isBefore(beforeDate)]}
62 | );
63 | expect(v.passes()).toBe(true);
64 |
65 | v = intus.validate(
66 | {nested: [{date: new Date(2022, 12, 23)}]},
67 | {"nested.*.date": [isBefore(beforeDate)]}
68 | );
69 | expect(v.passes()).toBe(false);
70 | expect(v.errors()).toMatchObject({
71 | "nested.0.date": `Nested.0.date must be a date before ${beforeDate}.`,
72 | });
73 | });
74 |
75 | it("isBefore attributes", function () {
76 | let beforeDate = new Date(2022, 12, 18);
77 | let v = intus.validate(
78 | {
79 | start: new Date(2022, 12, 17),
80 | end: beforeDate,
81 | },
82 | {start: [isBefore("end")]}
83 | );
84 | expect(v.passes()).toBe(true);
85 |
86 | v = intus.validate(
87 | {
88 | start: new Date(2022, 12, 23),
89 | end: beforeDate,
90 | },
91 | {start: [isBefore("end")]}
92 | );
93 | expect(v.passes()).toBe(false);
94 | expect(v.errors()).toMatchObject({
95 | start: `Start must be a date before ${beforeDate}.`,
96 | });
97 | });
98 |
99 | it("isBefore nested attributes", function () {
100 | let beforeDate = new Date(2022, 12, 18);
101 | let v = intus.validate(
102 | {
103 | nested: {
104 | start: new Date(2022, 12, 17),
105 | end: beforeDate,
106 | },
107 | },
108 | {"nested.start": [isBefore("nested.end")]}
109 | );
110 | expect(v.passes()).toBe(true);
111 |
112 | v = intus.validate(
113 | {
114 | nested: {
115 | start: new Date(2022, 12, 23),
116 | end: beforeDate,
117 | },
118 | },
119 | {"nested.start": [isBefore("nested.end")]}
120 | );
121 | expect(v.passes()).toBe(false);
122 | expect(v.errors()).toMatchObject({
123 | "nested.start": `Nested.start must be a date before ${beforeDate}.`,
124 | });
125 | });
126 |
127 | it("isBefore nested star attributes", function () {
128 | let beforeDate = new Date(2022, 12, 18);
129 | let v = intus.validate(
130 | {
131 | nested: [
132 | {
133 | start: new Date(2022, 12, 17),
134 | end: beforeDate,
135 | },
136 | ],
137 | },
138 | {"nested.*.start": [isBefore("nested.*.end")]}
139 | );
140 | expect(v.passes()).toBe(true);
141 |
142 | v = intus.validate(
143 | {
144 | nested: [
145 | {
146 | start: new Date(2022, 12, 23),
147 | end: beforeDate,
148 | },
149 | ],
150 | },
151 | {"nested.*.start": [isBefore("nested.*.end")]}
152 | );
153 | expect(v.passes()).toBe(false);
154 | expect(v.errors()).toMatchObject({
155 | "nested.0.start": `Nested.0.start must be a date before ${beforeDate}.`,
156 | });
157 | });
158 |
--------------------------------------------------------------------------------
/test/isBeforeOrEqual.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isBeforeOrEqual} from "../rules";
4 |
5 | it("isBeforeOrEqual", function () {
6 | let dateAfter = new Date(2022, 12, 19);
7 | let v = intus.validate(
8 | {date: new Date(2022, 12, 19)},
9 | {date: [isBeforeOrEqual(dateAfter)]}
10 | );
11 | expect(v.passes()).toBe(true);
12 |
13 | v = intus.validate(
14 | {date: new Date(2022, 12, 18)},
15 | {date: [isBeforeOrEqual(dateAfter)]}
16 | );
17 | expect(v.passes()).toBe(true);
18 |
19 | v = intus.validate(
20 | {date: new Date(2022, 12, 20)},
21 | {date: [isBeforeOrEqual(dateAfter)]}
22 | );
23 | expect(v.passes()).toBe(false);
24 | expect(v.errors()).toMatchObject({
25 | date: `Date must be a date before or equal to ${dateAfter}.`,
26 | });
27 | });
28 |
29 | it("isBeforeOrEqual passes if value is not a valid date", function () {
30 | let v = intus.validate(
31 | {date: "some random value"},
32 | {date: [isBeforeOrEqual(new Date(2022, 12, 18))]}
33 | );
34 | expect(v.passes()).toBe(true);
35 | });
36 |
37 | it("isBeforeOrEqual passes if before date is not a valid date", function () {
38 | let v = intus.validate(
39 | {date: new Date(2022, 12, 18)},
40 | {date: [isBeforeOrEqual("random")]}
41 | );
42 | expect(v.passes()).toBe(true);
43 | });
44 |
45 | it("isBeforeOrEqual attributes", function () {
46 | let v = intus.validate(
47 | {
48 | start: new Date(2022, 12, 20),
49 | end: new Date(2022, 12, 19),
50 | },
51 | {start: [isBeforeOrEqual("end")]}
52 | );
53 | expect(v.passes()).toBe(false);
54 | });
55 |
56 | it("isBeforeOrEqual nested attributes", function () {
57 | let v = intus.validate(
58 | {
59 | nested: {
60 | start: new Date(2022, 12, 20),
61 | end: new Date(2022, 12, 19),
62 | },
63 | },
64 | {"nested.start": [isBeforeOrEqual("nested.end")]}
65 | );
66 |
67 | expect(v.passes()).toBe(false);
68 | });
69 |
70 | it("isBeforeOrEqual nested star attributes", function () {
71 | let v = intus.validate(
72 | {
73 | nested: [
74 | {
75 | start: new Date(2022, 12, 20),
76 | end: new Date(2022, 12, 19),
77 | },
78 | ],
79 | },
80 | {"nested.*.start": [isBeforeOrEqual("nested.*.end")]}
81 | );
82 |
83 | expect(v.passes()).toBe(false);
84 | });
85 |
86 | it("isBeforeOrEqual nested", function () {
87 | let dateAfter = new Date(2022, 12, 18);
88 | let v = intus.validate(
89 | {nested: {date: new Date(2022, 12, 18)}},
90 | {"nested.date": [isBeforeOrEqual(dateAfter)]}
91 | );
92 | expect(v.passes()).toBe(true);
93 |
94 | v = intus.validate(
95 | {nested: {date: new Date(2022, 12, 19)}},
96 | {"nested.date": [isBeforeOrEqual(dateAfter)]}
97 | );
98 | expect(v.passes()).toBe(false);
99 | expect(v.errors()).toMatchObject({
100 | "nested.date": `Nested.date must be a date before or equal to ${dateAfter}.`,
101 | });
102 | });
103 |
104 | it("isBeforeOrEqual nested star", function () {
105 | let dateAfter = new Date(2022, 12, 19);
106 | let v = intus.validate(
107 | {nested: [{date: new Date(2022, 12, 18)}]},
108 | {"nested.*.date": [isBeforeOrEqual(dateAfter)]}
109 | );
110 | expect(v.passes()).toBe(true);
111 |
112 | v = intus.validate(
113 | {nested: [{date: new Date(2022, 12, 20)}]},
114 | {"nested.*.date": [isBeforeOrEqual(dateAfter)]}
115 | );
116 | expect(v.passes()).toBe(false);
117 | expect(v.errors()).toMatchObject({
118 | "nested.0.date": `Nested.0.date must be a date before or equal to ${dateAfter}.`,
119 | });
120 | });
121 |
--------------------------------------------------------------------------------
/test/isBetween.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isBetween} from "../rules";
4 |
5 | it("isBetween", function () {
6 | let invalid = [16, "16"];
7 | let valid = [17, 18, "18", 19, "", undefined, null];
8 |
9 | invalid.forEach(t => {
10 | let validation = intus.validate({t}, {t: [isBetween(17, 20)]});
11 | expect(validation.passes()).toBe(false);
12 | expect(validation.errors()).toMatchObject({
13 | t: "T must be between or equal to 17 and 20.",
14 | });
15 | });
16 |
17 | valid.forEach(t =>
18 | expect(intus.validate({t}, {t: [isBetween(17, 20)]}).passes()).toBe(true)
19 | );
20 | });
21 |
22 | it("isBetween nested", function () {
23 | let validation = intus.validate(
24 | {nested: {field: 16}},
25 | {"nested.field": [isBetween(17, 20)]}
26 | );
27 | expect(validation.passes()).toBe(false);
28 | expect(validation.errors()).toMatchObject({
29 | "nested.field": "Nested.field must be between or equal to 17 and 20.",
30 | });
31 | });
32 |
33 | it("isBetween nested star", function () {
34 | let validation = intus.validate(
35 | {nested: [{field: 16}]},
36 | {"nested.*.field": [isBetween(17, 20)]}
37 | );
38 | expect(validation.passes()).toBe(false);
39 | expect(validation.errors()).toMatchObject({
40 | "nested.0.field": "Nested.0.field must be between or equal to 17 and 20.",
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/test/isBoolean.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isBoolean} from "../rules";
4 |
5 | it("isBoolean", function () {
6 | let invalid = ["a", undefined];
7 | let valid = [true, false, 1, 0];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isBoolean()]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must be a boolean."});
13 | });
14 | valid.forEach(t =>
15 | expect(intus.validate({t}, {t: [isBoolean()]}).passes()).toBe(true)
16 | );
17 | });
18 |
19 | it("isBoolean nested", function () {
20 | let v = intus.validate(
21 | {nested: {field: true}},
22 | {"nested.field": [isBoolean()]}
23 | );
24 | expect(v.passes()).toBe(true);
25 |
26 | v = intus.validate(
27 | {nested: {field: "something"}},
28 | {"nested.field": [isBoolean()]}
29 | );
30 | expect(v.passes()).toBe(false);
31 | expect(v.errors()).toMatchObject({
32 | "nested.field": "Nested.field must be a boolean.",
33 | });
34 | });
35 |
36 | it("isBoolean nested star", function () {
37 | let v = intus.validate(
38 | {nested: [{field: true}]},
39 | {"nested.*.field": [isBoolean()]}
40 | );
41 | expect(v.passes()).toBe(true);
42 |
43 | v = intus.validate(
44 | {nested: [{field: "something"}]},
45 | {"nested.*.field": [isBoolean()]}
46 | );
47 | expect(v.passes()).toBe(false);
48 | expect(v.errors()).toMatchObject({
49 | "nested.0.field": "Nested.0.field must be a boolean.",
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/test/isDate.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isDate} from "../rules";
4 |
5 | it("isDate", function () {
6 | let invalid = ["1", 1, {}];
7 | let valid = [
8 | "",
9 | undefined,
10 | null,
11 | new Date(1995, 12, 17),
12 | new Date("December 17, 1995 03:24:00"),
13 | ];
14 |
15 | invalid.forEach(t => {
16 | let v = intus.validate({t}, {t: [isDate()]});
17 | expect(v.passes()).toBe(false);
18 | expect(v.errors()).toMatchObject({t: "T must be a valid date."});
19 | });
20 |
21 | valid.forEach(t =>
22 | expect(intus.validate({t}, {t: [isDate()]}).passes()).toBe(true)
23 | );
24 | });
25 |
26 | it("isDate nested", function () {
27 | let v = intus.validate(
28 | {nested: {field: new Date()}},
29 | {"nested.field": [isDate()]}
30 | );
31 | expect(v.passes()).toBe(true);
32 |
33 | v = intus.validate(
34 | {nested: {field: "not-date"}},
35 | {"nested.field": [isDate()]}
36 | );
37 | expect(v.passes()).toBe(false);
38 | expect(v.errors()).toMatchObject({
39 | "nested.field": "Nested.field must be a valid date.",
40 | });
41 | });
42 |
43 | it("isDate nested star", function () {
44 | let v = intus.validate(
45 | {nested: [{field: new Date()}]},
46 | {"nested.*.field": [isDate()]}
47 | );
48 | expect(v.passes()).toBe(true);
49 |
50 | v = intus.validate(
51 | {nested: [{field: "not-a-date"}]},
52 | {"nested.*.field": [isDate()]}
53 | );
54 | expect(v.passes()).toBe(false);
55 | expect(v.errors()).toMatchObject({
56 | "nested.0.field": "Nested.0.field must be a valid date.",
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/isDistinct.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isDistinct} from "../rules";
4 |
5 | it("isDistinct", function () {
6 | let v = intus.validate({users: [1, "1"]}, {"users.*": [isDistinct()]});
7 | expect(v.passes()).toBe(false);
8 | expect(v.errors()).toMatchObject({
9 | "users.0": "Users.0 must be distinct.",
10 | "users.1": "Users.1 must be distinct.",
11 | });
12 | });
13 |
14 | it("isDistinct strict", function () {
15 | let v = intus.validate(
16 | {users: [1, "1"]},
17 | {"users.*": [isDistinct("strict")]}
18 | );
19 | expect(v.passes()).toBe(true);
20 | });
21 |
22 | it("isDistinct ignoreCase", function () {
23 | let v = intus.validate(
24 | {users: ["a", "A"]},
25 | {"users.*": [isDistinct("ignoreCase")]}
26 | );
27 | expect(v.passes()).toBe(false);
28 |
29 | v = intus.validate({users: ["a", "A"]}, {"users.*": [isDistinct()]});
30 | expect(v.passes()).toBe(true);
31 | });
32 |
--------------------------------------------------------------------------------
/test/isEmail.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isEmail} from "../rules";
4 |
5 | it("isEmail", function () {
6 | let invalid = [
7 | "😃@i.com",
8 | "john@example.com ",
9 | "john@example.com extra",
10 | "45454.com",
11 | "sdfsf@",
12 | ];
13 | let valid = [
14 | undefined,
15 | null,
16 | "",
17 | "john@example.com",
18 | "m@i.com",
19 | "m@i.de",
20 | "m@i.co.uk",
21 | ];
22 |
23 | invalid.forEach(t => {
24 | let v = intus.validate({t}, {t: [isEmail()]});
25 | expect(v.passes()).toBe(false);
26 | expect(v.errors()).toMatchObject({t: "T must be a valid email."});
27 | });
28 | valid.forEach(t =>
29 | expect(intus.validate({t}, {t: [isEmail()]}).passes()).toBe(true)
30 | );
31 | });
32 |
33 | it("isEmail nested", function () {
34 | let v = intus.validate(
35 | {nested: {field: "druc@gmail.com"}},
36 | {"nested.field": [isEmail()]}
37 | );
38 | expect(v.passes()).toBe(true);
39 |
40 | v = intus.validate(
41 | {nested: {field: "something"}},
42 | {"nested.field": [isEmail()]}
43 | );
44 | expect(v.passes()).toBe(false);
45 | expect(v.errors()).toMatchObject({
46 | "nested.field": "Nested.field must be a valid email.",
47 | });
48 | });
49 |
50 | it("isEmail nested star", function () {
51 | let v = intus.validate(
52 | {nested: [{field: "druc@gmail.com"}]},
53 | {"nested.*.field": [isEmail()]}
54 | );
55 | expect(v.passes()).toBe(true);
56 |
57 | v = intus.validate(
58 | {nested: [{field: "something"}]},
59 | {"nested.*.field": [isEmail()]}
60 | );
61 | expect(v.passes()).toBe(false);
62 | expect(v.errors()).toMatchObject({
63 | "nested.0.field": "Nested.0.field must be a valid email.",
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/test/isExtension.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isExtension} from "../rules";
4 | import {makeFile} from "./helpers";
5 |
6 | it("isExtension", function () {
7 | let file = makeFile("file.txt", "text/plain");
8 |
9 | expect(intus.validate({file}, {file: [isExtension("txt")]}).passes()).toBe(
10 | true
11 | );
12 |
13 | let validation = intus.validate({file}, {file: [isExtension("pdf", "jpg")]});
14 | expect(validation.passes()).toBe(false);
15 | expect(validation.errors()).toMatchObject({
16 | file: "File must be a file of type pdf / jpg.",
17 | });
18 | });
19 |
20 | it("isExtension nested", function () {
21 | let file = makeFile("file.txt", "text/plain");
22 |
23 | expect(
24 | intus
25 | .validate({nested: {file}}, {"nested.file": [isExtension("txt")]})
26 | .passes()
27 | ).toBe(true);
28 |
29 | let validation = intus.validate(
30 | {nested: {file}},
31 | {"nested.file": [isExtension("pdf", "jpg")]}
32 | );
33 | expect(validation.passes()).toBe(false);
34 | expect(validation.errors()).toMatchObject({
35 | "nested.file": "Nested.file must be a file of type pdf / jpg.",
36 | });
37 | });
38 |
39 | it("isExtension nested star", function () {
40 | let file = makeFile("file.txt", "text/plain");
41 |
42 | expect(
43 | intus
44 | .validate({nested: [{file}]}, {"nested.*.file": [isExtension("txt")]})
45 | .passes()
46 | ).toBe(true);
47 |
48 | let validation = intus.validate(
49 | {nested: [{file}]},
50 | {"nested.*.file": [isExtension("pdf", "jpg")]}
51 | );
52 | expect(validation.passes()).toBe(false);
53 | expect(validation.errors()).toMatchObject({
54 | "nested.0.file": "Nested.0.file must be a file of type pdf / jpg.",
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/isGt.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isGt} from "../rules";
4 |
5 | it("isGt", function () {
6 | let v = intus.validate({a: 3, b: 2}, {a: [isGt("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: 2}, {a: [isGt("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: "", b: 2}, {a: [isGt("b")]});
13 | expect(v.passes()).toBe(true);
14 |
15 | v = intus.validate({a: 3}, {a: [isGt("b")]});
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate({a: 3, b: "string"}, {a: [isGt("b")]});
19 | expect(v.passes()).toBe(false);
20 |
21 | v = intus.validate({a: 1, b: 2}, {a: [isGt("b")]});
22 | expect(v.passes()).toBe(false);
23 | expect(v.errors()).toMatchObject({a: "A must be greater than 2."});
24 | });
25 |
26 | it("isGt nested", function () {
27 | let v = intus.validate(
28 | {nested: {a: 3, b: 2}},
29 | {"nested.a": [isGt("nested.b")]}
30 | );
31 | expect(v.passes()).toBe(true);
32 |
33 | v = intus.validate({nested: {a: 1, b: 2}}, {"nested.a": [isGt("nested.b")]});
34 | expect(v.passes()).toBe(false);
35 | expect(v.errors()).toMatchObject({
36 | "nested.a": "Nested.a must be greater than 2.",
37 | });
38 | });
39 |
40 | it("isGt nested star", function () {
41 | let v = intus.validate(
42 | {nested: [{a: 3, b: 2}]},
43 | {"nested.*.a": [isGt("nested.*.b")]}
44 | );
45 | expect(v.passes()).toBe(true);
46 |
47 | v = intus.validate(
48 | {nested: [{a: 1, b: 2}]},
49 | {"nested.*.a": [isGt("nested.*.b")]}
50 | );
51 | expect(v.passes()).toBe(false);
52 | expect(v.errors()).toMatchObject({
53 | "nested.0.a": "Nested.0.a must be greater than 2.",
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/isGte.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isGte} from "../rules";
4 |
5 | it("isGte", function () {
6 | let v = intus.validate({a: 2, b: 2}, {a: [isGte("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: 3, b: 2}, {a: [isGte("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: null, b: 2}, {a: [isGte("b")]});
13 | expect(v.passes()).toBe(true);
14 |
15 | v = intus.validate({a: "", b: 2}, {a: [isGte("b")]});
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate({a: 3}, {a: [isGte("b")]});
19 | expect(v.passes()).toBe(true);
20 |
21 | v = intus.validate({a: 3, b: "string"}, {a: [isGte("b")]});
22 | expect(v.passes()).toBe(false);
23 |
24 | v = intus.validate({a: 1, b: 2}, {a: [isGte("b")]});
25 | expect(v.passes()).toBe(false);
26 | expect(v.errors()).toMatchObject({
27 | a: "A must be greater or equal to 2.",
28 | });
29 | });
30 |
31 | it("isGte nested", function () {
32 | let v = intus.validate(
33 | {nested: {a: 2, b: 2}},
34 | {"nested.a": [isGte("nested.b")]}
35 | );
36 | expect(v.passes()).toBe(true);
37 |
38 | v = intus.validate({nested: {a: 1, b: 2}}, {"nested.a": [isGte("nested.b")]});
39 | expect(v.passes()).toBe(false);
40 | expect(v.errors()).toMatchObject({
41 | "nested.a": "Nested.a must be greater or equal to 2.",
42 | });
43 | });
44 |
45 | it("isGte nested star", function () {
46 | let v = intus.validate(
47 | {nested: [{a: 2, b: 2}]},
48 | {"nested.*.a": [isGte("nested.*.b")]}
49 | );
50 | expect(v.passes()).toBe(true);
51 |
52 | v = intus.validate(
53 | {nested: [{a: 1, b: 2}]},
54 | {"nested.*.a": [isGte("nested.*.b")]}
55 | );
56 | expect(v.passes()).toBe(false);
57 | expect(v.errors()).toMatchObject({
58 | "nested.0.a": "Nested.0.a must be greater or equal to 2.",
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/test/isImage.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isImage} from "../rules";
4 | import {makeFile} from "./helpers";
5 |
6 | it("isImage", function () {
7 | const validFiles = [
8 | makeFile("file.gif", "image/gif"),
9 | makeFile("file.jpg", "image/jpeg"),
10 | makeFile("file.jpeg", "image/jpeg"),
11 | makeFile("file.svg", "image/svg"),
12 | makeFile("file.bmp", "image/bmp"),
13 | makeFile("file.png", "image/png"),
14 | makeFile("file.png", "image/webp"),
15 | ];
16 |
17 | validFiles.forEach(file => {
18 | expect(intus.validate({file: file}, {file: [isImage()]}).passes()).toBe(
19 | true
20 | );
21 | });
22 |
23 | let pdf = makeFile("file.pdf", "application/pdf");
24 |
25 | let v = intus.validate({file: pdf}, {file: [isImage()]});
26 | expect(v.passes()).toBe(false);
27 | expect(v.errors()).toMatchObject({
28 | file: "File must be an image.",
29 | });
30 | });
31 |
32 | it("isImage nested", function () {
33 | let v = intus.validate(
34 | {nested: {file: makeFile("file.gif", "image/gif")}},
35 | {"nested.file": [isImage()]}
36 | );
37 | expect(v.passes()).toBe(true);
38 |
39 | v = intus.validate(
40 | {nested: {file: makeFile("file.pdf", "application/pdf")}},
41 | {"nested.file": [isImage()]}
42 | );
43 | expect(v.passes()).toBe(false);
44 | expect(v.errors()).toMatchObject({
45 | "nested.file": "Nested.file must be an image.",
46 | });
47 | });
48 |
49 | it("isImage nested star", function () {
50 | let v = intus.validate(
51 | {nested: [{file: makeFile("file.gif", "image/gif")}]},
52 | {"nested.*.file": [isImage()]}
53 | );
54 | expect(v.passes()).toBe(true);
55 |
56 | v = intus.validate(
57 | {nested: [{file: makeFile("file.pdf", "application/pdf")}]},
58 | {"nested.*.file": [isImage()]}
59 | );
60 | expect(v.passes()).toBe(false);
61 | expect(v.errors()).toMatchObject({
62 | "nested.0.file": "Nested.0.file must be an image.",
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/test/isIn.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isIn} from "../rules";
4 |
5 | it("isIn", function () {
6 | let invalid = [1, "2", "this"];
7 | let valid = ["", undefined, null, 1, "2", "this"];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isIn("hey", "no", "ok")]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must be one of hey / no / ok."});
13 | });
14 |
15 | valid.forEach(t =>
16 | expect(intus.validate({t}, {t: [isIn("1", "2", "this")]}).passes()).toBe(
17 | true
18 | )
19 | );
20 | });
21 |
22 | it("isIn nested", function () {
23 | let v = intus.validate(
24 | {nested: {field: "this"}},
25 | {"nested.field": [isIn("this", "and", "that")]}
26 | );
27 | expect(v.passes()).toBe(true);
28 |
29 | v = intus.validate(
30 | {nested: {field: "not"}},
31 | {"nested.field": [isIn("this", "and", "that")]}
32 | );
33 | expect(v.passes()).toBe(false);
34 | expect(v.errors()).toMatchObject({
35 | "nested.field": "Nested.field must be one of this / and / that.",
36 | });
37 | });
38 |
39 | it("isIn nested star", function () {
40 | let v = intus.validate(
41 | {nested: [{field: "and"}]},
42 | {"nested.*.field": [isIn("this", "and", "that")]}
43 | );
44 | expect(v.passes()).toBe(true);
45 |
46 | v = intus.validate(
47 | {nested: [{field: "not"}]},
48 | {"nested.*.field": [isIn("this", "and", "that")]}
49 | );
50 | expect(v.passes()).toBe(false);
51 | expect(v.errors()).toMatchObject({
52 | "nested.0.field": "Nested.0.field must be one of this / and / that.",
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/isInteger.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isInteger} from "../rules";
4 |
5 | it("isInteger", function () {
6 | let invalid = ["a", 1.5, "1.5"];
7 | let valid = [1, undefined, null, "", "1"];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isInteger()]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must be an integer."});
13 | });
14 | valid.forEach(t =>
15 | expect(intus.validate({t}, {t: [isInteger()]}).passes()).toBe(true)
16 | );
17 | });
18 |
19 | it("isInteger nested", function () {
20 | let v = intus.validate({nested: {field: 3}}, {"nested.field": [isInteger()]});
21 | expect(v.passes()).toBe(true);
22 |
23 | v = intus.validate(
24 | {nested: {field: "not-int"}},
25 | {"nested.field": [isInteger()]}
26 | );
27 | expect(v.passes()).toBe(false);
28 | expect(v.errors()).toMatchObject({
29 | "nested.field": "Nested.field must be an integer.",
30 | });
31 | });
32 |
33 | it("isInteger nested star", function () {
34 | let v = intus.validate({nested: {field: 3}}, {"nested.field": [isInteger()]});
35 | expect(v.passes()).toBe(true);
36 |
37 | v = intus.validate(
38 | {nested: [{field: "not-int"}]},
39 | {"nested.*.field": [isInteger()]}
40 | );
41 | expect(v.passes()).toBe(false);
42 | expect(v.errors()).toMatchObject({
43 | "nested.0.field": "Nested.0.field must be an integer.",
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/isIp.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isIp} from "../rules";
4 |
5 | it("validates isIp", function () {
6 | let invalid = ["aslsdlks"];
7 | let valid = ["", null, undefined, "127.0.0.1"];
8 |
9 | invalid.forEach(t => {
10 | let validation = intus.validate({t}, {t: [isIp()]});
11 | expect(validation.passes()).toBe(false);
12 | expect(validation.errors()).toMatchObject({
13 | t: "T must be a valid IP address.",
14 | });
15 | });
16 |
17 | valid.forEach(t =>
18 | expect(intus.validate({t}, {t: [isIp()]}).passes()).toBe(true)
19 | );
20 | });
21 |
22 | it("validates isIp nested", function () {
23 | expect(
24 | intus
25 | .validate({nested: {field: "127.0.0.1"}}, {"nested.field": [isIp()]})
26 | .passes()
27 | ).toBe(true);
28 |
29 | let validation = intus.validate(
30 | {nested: {field: "127.0.0"}},
31 | {"nested.field": [isIp()]}
32 | );
33 | expect(validation.passes()).toBe(false);
34 | expect(validation.errors()).toMatchObject({
35 | "nested.field": "Nested.field must be a valid IP address.",
36 | });
37 | });
38 |
39 | it("validates isIp nested star", function () {
40 | expect(
41 | intus
42 | .validate({nested: [{field: "127.0.0.1"}]}, {"nested.*.field": [isIp()]})
43 | .passes()
44 | ).toBe(true);
45 |
46 | let validation = intus.validate(
47 | {nested: [{field: "127.0.0"}]},
48 | {"nested.*.field": [isIp()]}
49 | );
50 | expect(validation.passes()).toBe(false);
51 | expect(validation.errors()).toMatchObject({
52 | "nested.0.field": "Nested.0.field must be a valid IP address.",
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/isJSON.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isJSON} from "../rules";
4 |
5 | it("isJSON", function () {
6 | let invalid = [1, "nojson"];
7 | let valid = ["[]", "{}", '{"a": 1}', undefined, null, ""];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t: t}, {t: [isJSON()]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must be a valid JSON."});
13 | });
14 | valid.forEach(t =>
15 | expect(intus.validate({t: t}, {t: [isJSON()]}).passes()).toBe(true)
16 | );
17 | });
18 |
19 | it("isJSON nested", function () {
20 | let v = intus.validate({nested: {field: "{}"}}, {"nested.field": [isJSON()]});
21 | expect(v.passes()).toBe(true);
22 |
23 | v = intus.validate({nested: {field: "not"}}, {"nested.field": [isJSON()]});
24 | expect(v.passes()).toBe(false);
25 | expect(v.errors()).toMatchObject({
26 | "nested.field": "Nested.field must be a valid JSON.",
27 | });
28 | });
29 |
30 | it("isJSON nested star", function () {
31 | let v = intus.validate(
32 | {nested: [{field: "{}"}]},
33 | {"nested.*.field": [isJSON()]}
34 | );
35 | expect(v.passes()).toBe(true);
36 |
37 | v = intus.validate(
38 | {nested: [{field: "not"}]},
39 | {"nested.*.field": [isJSON()]}
40 | );
41 | expect(v.passes()).toBe(false);
42 | expect(v.errors()).toMatchObject({
43 | "nested.0.field": "Nested.0.field must be a valid JSON.",
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/isLt.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isLt} from "../rules";
4 |
5 | it("isLt", function () {
6 | let v = intus.validate({a: 1, b: 2}, {a: [isLt("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: 2}, {a: [isLt("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: "", b: 2}, {a: [isLt("b")]});
13 | expect(v.passes()).toBe(true);
14 |
15 | v = intus.validate({a: 3}, {a: [isLt("b")]});
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate({a: 3, b: "string"}, {a: [isLt("b")]});
19 | expect(v.passes()).toBe(false);
20 |
21 | v = intus.validate({a: 2, b: 2}, {a: [isLt("b")]});
22 | expect(v.passes()).toBe(false);
23 | expect(v.errors()).toMatchObject({a: "A must be less than 2."});
24 | });
25 |
26 | it("isLt nested", function () {
27 | let v = intus.validate(
28 | {nested: {a: 1, b: 2}},
29 | {"nested.a": [isLt("nested.b")]}
30 | );
31 | expect(v.passes()).toBe(true);
32 |
33 | v = intus.validate({nested: {a: 2, b: 2}}, {"nested.a": [isLt("nested.b")]});
34 | expect(v.passes()).toBe(false);
35 | expect(v.errors()).toMatchObject({
36 | "nested.a": "Nested.a must be less than 2.",
37 | });
38 | });
39 |
40 | it("isLt nested star", function () {
41 | let v = intus.validate(
42 | {nested: [{a: 1, b: 2}]},
43 | {"nested.*.a": [isLt("nested.*.b")]}
44 | );
45 | expect(v.passes()).toBe(true);
46 |
47 | v = intus.validate(
48 | {nested: [{a: 2, b: 2}]},
49 | {"nested.*.a": [isLt("nested.*.b")]}
50 | );
51 | expect(v.passes()).toBe(false);
52 | expect(v.errors()).toMatchObject({
53 | "nested.0.a": "Nested.0.a must be less than 2.",
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/isLte.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isLte} from "../rules";
4 |
5 | it("isLte", function () {
6 | let v = intus.validate({a: 2, b: 2}, {a: [isLte("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: 1, b: 2}, {a: [isLte("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: null, b: 2}, {a: [isLte("b")]});
13 | expect(v.passes()).toBe(true);
14 |
15 | v = intus.validate({a: "", b: 2}, {a: [isLte("b")]});
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate({a: 3}, {a: [isLte("b")]});
19 | expect(v.passes()).toBe(true);
20 |
21 | v = intus.validate({a: 3, b: "string"}, {a: [isLte("b")]});
22 | expect(v.passes()).toBe(false);
23 |
24 | v = intus.validate({a: 3, b: 2}, {a: [isLte("b")]});
25 | expect(v.passes()).toBe(false);
26 | expect(v.errors()).toMatchObject({
27 | a: "A must be less or equal to 2.",
28 | });
29 | });
30 |
31 | it("isLte nested", function () {
32 | let v = intus.validate(
33 | {nested: {a: 2, b: 2}},
34 | {"nested.a": [isLte("nested.b")]}
35 | );
36 | expect(v.passes()).toBe(true);
37 |
38 | v = intus.validate({nested: {a: 3, b: 2}}, {"nested.a": [isLte("nested.b")]});
39 | expect(v.passes()).toBe(false);
40 | expect(v.errors()).toMatchObject({
41 | "nested.a": "Nested.a must be less or equal to 2.",
42 | });
43 | });
44 |
45 | it("isLte nested star", function () {
46 | let v = intus.validate(
47 | {nested: [{a: 2, b: 2}]},
48 | {"nested.*.a": [isLte("nested.*.b")]}
49 | );
50 | expect(v.passes()).toBe(true);
51 |
52 | v = intus.validate(
53 | {nested: [{a: 3, b: 2}]},
54 | {"nested.*.a": [isLte("nested.*.b")]}
55 | );
56 | expect(v.passes()).toBe(false);
57 | expect(v.errors()).toMatchObject({
58 | "nested.0.a": "Nested.0.a must be less or equal to 2.",
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/test/isMax.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isMax} from "../rules";
4 | import {makeFile} from "./helpers";
5 |
6 | it("isMax", function () {
7 | let invalid = [100];
8 | let valid = [98, 99, "", undefined, null];
9 |
10 | invalid.forEach(t => {
11 | let validation = intus.validate({t}, {t: [isMax(99)]});
12 | expect(validation.passes()).toBe(false);
13 | expect(validation.errors()).toMatchObject({t: "T must be at most 99."});
14 | });
15 |
16 | valid.forEach(t =>
17 | expect(intus.validate({t}, {t: [isMax(99)]}).passes()).toBe(true)
18 | );
19 | });
20 |
21 | it("isMax takes length into account when value is a string", function () {
22 | let v = intus.validate({name: "abc"}, {name: [isMax(3)]});
23 | expect(v.passes()).toBe(true);
24 |
25 | v = intus.validate({name: "abcd"}, {name: [isMax(3)]});
26 | expect(v.passes()).toBe(false);
27 | expect(v.errors()).toMatchObject({
28 | name: "Name must be at most 3 characters long.",
29 | });
30 | });
31 |
32 | it("isMax takes length into account when value is an array", function () {
33 | let v = intus.validate({items: [1, 2, 3]}, {items: [isMax(3)]});
34 | expect(v.passes()).toBe(true);
35 |
36 | v = intus.validate({items: [1, 2, 3, 4]}, {items: [isMax(3)]});
37 | expect(v.passes()).toBe(false);
38 | expect(v.errors()).toMatchObject({
39 | items: "Items must have at most 3 items.",
40 | });
41 | });
42 |
43 | it("isMax takes size into account when value is a file", function () {
44 | let v = intus.validate(
45 | {file: makeFile("file.txt", "text/plain", 1)},
46 | {file: [isMax(1024)]}
47 | );
48 | expect(v.passes()).toBe(true);
49 |
50 | v = intus.validate(
51 | {file: makeFile("file.txt", "text/plain", 2)},
52 | {file: [isMax(1)]}
53 | );
54 | expect(v.passes()).toBe(false);
55 | expect(v.errors()).toMatchObject({
56 | file: "File must be at most 1MB.",
57 | });
58 | });
59 |
60 | it("isMax nested", function () {
61 | let validation = intus.validate(
62 | {nested: {field: 100}},
63 | {"nested.field": [isMax(99)]}
64 | );
65 | expect(validation.passes()).toBe(false);
66 | expect(validation.errors()).toMatchObject({
67 | "nested.field": "Nested.field must be at most 99.",
68 | });
69 | });
70 |
71 | it("isMax nested star", function () {
72 | let v = intus.validate(
73 | {nested: [{field: 100}]},
74 | {"nested.*.field": [isMax(99)]}
75 | );
76 | expect(v.passes()).toBe(false);
77 | expect(v.errors()).toMatchObject({
78 | "nested.0.field": "Nested.0.field must be at most 99.",
79 | });
80 | });
81 |
82 | it("isMax message based on type", function () {
83 | let v = intus.validate(
84 | {
85 | nested: [
86 | {field: 100},
87 | {field: "abc"},
88 | {field: makeFile("a.jpg", "image/jpg", 2)},
89 | {field: [1, 2, 3]},
90 | ],
91 | },
92 | {"nested.*.field": [isMax(1)]}
93 | );
94 | expect(v.passes()).toBe(false);
95 |
96 | expect(v.errors()).toMatchObject({
97 | "nested.0.field": "Nested.0.field must be at most 1.",
98 | "nested.1.field": "Nested.1.field must be at most 1 characters long.",
99 | "nested.2.field": "Nested.2.field must be at most 1MB.",
100 | "nested.3.field": "Nested.3.field must have at most 1 items.",
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/test/isMime.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isMime} from "../rules";
4 | import {makeFile} from "./helpers";
5 |
6 | it("isMime", function () {
7 | let file = makeFile("file.txt", "text/plain");
8 |
9 | let v = intus.validate({file}, {file: [isMime("text/*")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({file}, {file: [isMime("text/plain")]});
13 | expect(v.passes()).toBe(true);
14 |
15 | v = intus.validate(
16 | {file},
17 | {file: [isMime("application/pdf", "application/json")]}
18 | );
19 | expect(v.passes()).toBe(false);
20 | expect(v.errors()).toMatchObject({
21 | file: "File must be a file of type application/pdf / application/json.",
22 | });
23 | });
24 |
25 | it("isMime nested", function () {
26 | let file = makeFile("file.txt", "text/plain");
27 |
28 | let v = intus.validate(
29 | {nested: {file}},
30 | {"nested.file": [isMime("text/plain")]}
31 | );
32 | expect(v.passes()).toBe(true);
33 |
34 | v = intus.validate({nested: {file}}, {"nested.file": [isMime("image/jpeg")]});
35 | expect(v.passes()).toBe(false);
36 | expect(v.errors()).toMatchObject({
37 | "nested.file": "Nested.file must be a file of type image/jpeg.",
38 | });
39 | });
40 |
41 | it("isMime nested star", function () {
42 | let file = makeFile("file.txt", "text/plain");
43 |
44 | let v = intus.validate(
45 | {nested: [{file}]},
46 | {"nested.*.file": [isMime("text/plain")]}
47 | );
48 | expect(v.passes()).toBe(true);
49 |
50 | v = intus.validate(
51 | {nested: [{file}]},
52 | {"nested.*.file": [isMime("image/jpeg")]}
53 | );
54 | expect(v.passes()).toBe(false);
55 | expect(v.errors()).toMatchObject({
56 | "nested.0.file": "Nested.0.file must be a file of type image/jpeg.",
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/isMin.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isMin} from "../rules";
4 | import {makeFile} from "./helpers";
5 |
6 | it("isMin", function () {
7 | let invalid = [17];
8 | let valid = [18, 19, "", undefined, null];
9 |
10 | invalid.forEach(t => {
11 | let validation = intus.validate({t}, {t: [isMin(18)]});
12 | expect(validation.passes()).toBe(false);
13 | expect(validation.errors()).toMatchObject({t: "T must be at least 18."});
14 | });
15 | valid.forEach(t =>
16 | expect(intus.validate({t}, {t: [isMin(18)]}).passes()).toBe(true)
17 | );
18 | });
19 |
20 | it("isMin takes length into account when value is a string", function () {
21 | let v = intus.validate({name: "abc"}, {name: [isMin(3)]});
22 | expect(v.passes()).toBe(true);
23 |
24 | v = intus.validate({name: "ab"}, {name: [isMin(3)]});
25 | expect(v.passes()).toBe(false);
26 | expect(v.errors()).toMatchObject({
27 | name: "Name must be at least 3 characters long.",
28 | });
29 | });
30 |
31 | it("isMin takes length into account when value is an array", function () {
32 | let v = intus.validate({items: [1, 2, 3]}, {items: [isMin(3)]});
33 | expect(v.passes()).toBe(true);
34 |
35 | v = intus.validate({items: [1, 2]}, {items: [isMin(3)]});
36 | expect(v.passes()).toBe(false);
37 | expect(v.errors()).toMatchObject({
38 | items: "Items must have at least 3 items.",
39 | });
40 | });
41 |
42 | it("isMin takes size into account when value is a file", function () {
43 | let v = intus.validate(
44 | {file: makeFile("file.txt", "text/plain", 10)},
45 | {file: [isMin(3)]}
46 | );
47 | expect(v.passes()).toBe(true);
48 |
49 | v = intus.validate(
50 | {file: makeFile("file.txt", "text/plain", 1)},
51 | {file: [isMin(2)]}
52 | );
53 | expect(v.passes()).toBe(false);
54 | expect(v.errors()).toMatchObject({
55 | file: "File must be at least 2MB.",
56 | });
57 | });
58 |
59 | it("isMin nested", function () {
60 | expect(
61 | intus
62 | .validate({nested: {field: 17}}, {"nested.field": [isMin(17)]})
63 | .passes()
64 | ).toBe(true);
65 |
66 | let validation = intus.validate(
67 | {nested: {field: 17}},
68 | {"nested.field": [isMin(18)]}
69 | );
70 | expect(validation.passes()).toBe(false);
71 | expect(validation.errors()).toMatchObject({
72 | "nested.field": "Nested.field must be at least 18.",
73 | });
74 | });
75 |
76 | it("isMin nested star", function () {
77 | let validation = intus.validate(
78 | {nested: [{field: 17}]},
79 | {"nested.*.field": [isMin(18)]}
80 | );
81 | expect(validation.passes()).toBe(false);
82 | expect(validation.errors()).toMatchObject({
83 | "nested.0.field": "Nested.0.field must be at least 18.",
84 | });
85 | });
86 |
87 | it("isMin message based on type", function () {
88 | let v = intus.validate(
89 | {
90 | nested: [
91 | {field: 9},
92 | {field: "abc"},
93 | {field: makeFile("a.jpg", "image/jpg", 1)},
94 | {field: [1, 2, 3]},
95 | ],
96 | },
97 | {"nested.*.field": [isMin(10)]}
98 | );
99 | expect(v.passes()).toBe(false);
100 |
101 | expect(v.errors()).toMatchObject({
102 | "nested.0.field": "Nested.0.field must be at least 10.",
103 | "nested.1.field": "Nested.1.field must be at least 10 characters long.",
104 | "nested.2.field": "Nested.2.field must be at least 10MB.",
105 | "nested.3.field": "Nested.3.field must have at least 10 items.",
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/test/isNotIn.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isNotIn} from "../rules";
4 |
5 | it("isNotIn", function () {
6 | let invalid = [1, "2", "this"];
7 | let valid = ["", undefined, null, 1, "2", "this"];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isNotIn("1", "2", "this")]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must not be one of 1 / 2 / this."});
13 | });
14 | valid.forEach(t =>
15 | expect(
16 | intus.validate({t}, {t: [isNotIn("hey", "no", "ok")]}).passes()
17 | ).toBe(true)
18 | );
19 | });
20 |
21 | it("isNotIn nested", function () {
22 | let v = intus.validate(
23 | {nested: {field: "ok"}},
24 | {"nested.field": [isNotIn("this", "and", "that")]}
25 | );
26 | expect(v.passes()).toBe(true);
27 |
28 | v = intus.validate(
29 | {nested: {field: "this"}},
30 | {"nested.field": [isNotIn("this", "and", "that")]}
31 | );
32 | expect(v.passes()).toBe(false);
33 | expect(v.errors()).toMatchObject({
34 | "nested.field": "Nested.field must not be one of this / and / that.",
35 | });
36 | });
37 |
38 | it("isNotIn nested star", function () {
39 | let v = intus.validate(
40 | {nested: [{field: "ok"}]},
41 | {"nested.*.field": [isNotIn("this", "and", "that")]}
42 | );
43 | expect(v.passes()).toBe(true);
44 |
45 | v = intus.validate(
46 | {nested: [{field: "this"}]},
47 | {"nested.*.field": [isNotIn("this", "and", "that")]}
48 | );
49 | expect(v.passes()).toBe(false);
50 | expect(v.errors()).toMatchObject({
51 | "nested.0.field": "Nested.0.field must not be one of this / and / that.",
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/test/isNotRegex.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isNotRegex} from "../rules";
4 |
5 | it("isNotRegex", function () {
6 | let invalid = ["1234567890", 123];
7 | let valid = ["", null, undefined, "string"];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isNotRegex(/^[0-9]+$/)]});
11 | expect(v.passes()).toBe(false);
12 | });
13 |
14 | valid.forEach(t =>
15 | expect(intus.validate({t}, {t: [isNotRegex(/^[0-9]+$/)]}).passes()).toBe(
16 | true
17 | )
18 | );
19 | });
20 |
21 | it("isNotRegex nested", function () {
22 | let v = intus.validate(
23 | {nested: {field: "abc"}},
24 | {"nested.field": [isNotRegex(/^[0-9]+$/)]}
25 | );
26 | expect(v.passes()).toBe(true);
27 |
28 | v = intus.validate(
29 | {nested: {field: "321"}},
30 | {"nested.field": [isNotRegex(/^[0-9]+$/)]}
31 | );
32 | expect(v.passes()).toBe(false);
33 | expect(v.errors()).toMatchObject({
34 | "nested.field": "Nested.field must not match regex /^[0-9]+$/.",
35 | });
36 | });
37 |
38 | it("isNotRegex nested star", function () {
39 | let v = intus.validate(
40 | {nested: [{field: "abc"}]},
41 | {"nested.*.field": [isNotRegex(/^[0-9]+$/)]}
42 | );
43 | expect(v.passes()).toBe(true);
44 |
45 | v = intus.validate(
46 | {nested: [{field: "321"}]},
47 | {"nested.*.field": [isNotRegex(/^[0-9]+$/)]}
48 | );
49 | expect(v.passes()).toBe(false);
50 | expect(v.errors()).toMatchObject({
51 | "nested.0.field": "Nested.0.field must not match regex /^[0-9]+$/.",
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/test/isNumeric.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isNumeric} from "../rules";
4 |
5 | it("isNumeric", function () {
6 | let invalid = ["a"];
7 | let valid = [1, "1", 1.5, "2.55", "2.0", "", null, undefined];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isNumeric()]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must be a number."});
13 | });
14 |
15 | valid.forEach(t =>
16 | expect(intus.validate({t}, {t: [isNumeric()]}).passes()).toBe(true)
17 | );
18 | });
19 |
20 | it("isNumeric nested", function () {
21 | let validation = intus.validate(
22 | {nested: {field: "NaN"}},
23 | {"nested.field": [isNumeric()]}
24 | );
25 | expect(validation.passes()).toBe(false);
26 | expect(validation.errors()).toMatchObject({
27 | "nested.field": "Nested.field must be a number.",
28 | });
29 | });
30 |
31 | it("isNumeric nested star", function () {
32 | let validation = intus.validate(
33 | {nested: [{field: "NaN"}]},
34 | {"nested.*.field": [isNumeric()]}
35 | );
36 | expect(validation.passes()).toBe(false);
37 | expect(validation.errors()).toMatchObject({
38 | "nested.0.field": "Nested.0.field must be a number.",
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/test/isRegex.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRegex} from "../rules";
4 |
5 | it("isRegex", function () {
6 | let invalid = ["a"];
7 | let valid = ["", null, undefined, "1234567890", 123];
8 |
9 | invalid.forEach(t => {
10 | let v = intus.validate({t}, {t: [isRegex(/^[0-9]+$/)]});
11 | expect(v.passes()).toBe(false);
12 | expect(v.errors()).toMatchObject({t: "T must match regex /^[0-9]+$/."});
13 | });
14 |
15 | valid.forEach(t =>
16 | expect(intus.validate({t}, {t: [isRegex(/^[0-9]+$/)]}).passes()).toBe(true)
17 | );
18 | });
19 |
20 | it("isRegex nested", function () {
21 | let v = intus.validate(
22 | {nested: {field: "0123"}},
23 | {"nested.field": [isRegex(/^[0-9]+$/)]}
24 | );
25 | expect(v.passes()).toBe(true);
26 |
27 | v = intus.validate(
28 | {nested: {field: "NaN"}},
29 | {"nested.field": [isRegex(/^[0-9]+$/)]}
30 | );
31 |
32 | expect(v.passes()).toBe(false);
33 | expect(v.errors()).toMatchObject({
34 | "nested.field": "Nested.field must match regex /^[0-9]+$/.",
35 | });
36 | });
37 |
38 | it("isRegex nested star", function () {
39 | let v = intus.validate(
40 | {nested: [{field: "0123"}]},
41 | {"nested.*.field": [isRegex(/^[0-9]+$/)]}
42 | );
43 | expect(v.passes()).toBe(true);
44 |
45 | v = intus.validate(
46 | {nested: [{field: "NaN"}]},
47 | {"nested.*.field": [isRegex(/^[0-9]+$/)]}
48 | );
49 |
50 | expect(v.passes()).toBe(false);
51 | expect(v.errors()).toMatchObject({
52 | "nested.0.field": "Nested.0.field must match regex /^[0-9]+$/.",
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/isRequired.test.js:
--------------------------------------------------------------------------------
1 | import intus from "../index";
2 | import {isRequired} from "../rules";
3 | import {makeFile} from "./helpers";
4 |
5 | it("validates isRequired", function () {
6 | let invalid = ["", null, undefined];
7 | let valid = [
8 | false,
9 | true,
10 | 0,
11 | "1",
12 | 1,
13 | "Something",
14 | makeFile("file.txt", "text/plain"),
15 | ];
16 |
17 | invalid.forEach(t => {
18 | let validation = intus.validate({t}, {t: [isRequired()]});
19 | expect(validation.passes()).toBe(false);
20 | expect(validation.errors()).toMatchObject({t: "T is required."});
21 | });
22 |
23 | valid.forEach(t =>
24 | expect(intus.validate({t}, {t: [isRequired()]}).passes()).toBe(true)
25 | );
26 | });
27 |
28 | it("validates isRequired nested", function () {
29 | expect(
30 | intus
31 | .validate({nested: {field: 123}}, {"nested.field": [isRequired()]})
32 | .passes()
33 | ).toBe(true);
34 |
35 | let validation = intus.validate({}, {"nested.field": [isRequired()]});
36 | expect(validation.passes()).toBe(false);
37 | expect(validation.errors()).toMatchObject({
38 | "nested.field": "Nested.field is required.",
39 | });
40 | });
41 |
42 | it("validates isRequired nested star", function () {
43 | expect(
44 | intus
45 | .validate({nested: [{field: 123}]}, {"nested.*.field": [isRequired()]})
46 | .passes()
47 | ).toBe(true);
48 |
49 | let validation = intus.validate(
50 | {nested: [{}]},
51 | {"nested.*.field": [isRequired()]}
52 | );
53 | expect(validation.passes()).toBe(false);
54 | expect(validation.errors()).toMatchObject({
55 | "nested.0.field": "Nested.0.field is required.",
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/isRequiredIf.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredIf} from "../rules";
4 |
5 | it("isRequiredIf", function () {
6 | let v = intus.validate({age: 12}, {name: [isRequiredIf("age", 12)]});
7 | expect(v.passes()).toBe(false);
8 | expect(v.errors()).toMatchObject({
9 | name: "Name is required if age is 12.",
10 | });
11 |
12 | v = intus.validate(
13 | {name: "test", age: 12},
14 | {name: [isRequiredIf("age", 12)]}
15 | );
16 | expect(v.passes()).toBe(true);
17 | });
18 |
19 | it("isRequiredIf nested", function () {
20 | let v = intus.validate(
21 | {nested: {}, age: 12},
22 | {"nested.field": [isRequiredIf("age", 12)]}
23 | );
24 | expect(v.passes()).toBe(false);
25 | expect(v.errors()).toMatchObject({
26 | "nested.field": "Nested.field is required if age is 12.",
27 | });
28 | });
29 |
30 | it("isRequiredIf nested star", function () {
31 | let v = intus.validate(
32 | {nested: [{}], age: 12},
33 | {"nested.*.field": [isRequiredIf("age", 12)]}
34 | );
35 | expect(v.passes()).toBe(false);
36 | expect(v.errors()).toMatchObject({
37 | "nested.0.field": "Nested.0.field is required if age is 12.",
38 | });
39 | });
40 |
41 | it("isRequiredIf overwrite field name", function () {
42 | let v = intus.validate(
43 | {nested: [{}], age: 12},
44 | {"nested.*.field": [isRequiredIf("age", 12)]},
45 | {age: "varsta", "nested.*.field": "something"}
46 | );
47 | expect(v.passes()).toBe(false);
48 | expect(v.errors()).toMatchObject({
49 | "nested.0.field": "Something is required if varsta is 12.",
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/test/isRequiredIfAccepted.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredIfAccepted} from "../rules";
4 |
5 | it("isRequiredIfAccepted", function () {
6 | let v = intus.validate(
7 | {name: "Constantin", terms: true},
8 | {name: [isRequiredIfAccepted("terms")]}
9 | );
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({terms: "on"}, {name: [isRequiredIfAccepted("terms")]});
13 | expect(v.passes()).toBe(false);
14 | expect(v.errors()).toMatchObject({
15 | name: "Name is required if terms is accepted.",
16 | });
17 | });
18 |
19 | it("isRequiredIfAccepted nested", function () {
20 | let v = intus.validate(
21 | {terms: true, nested: {field: 123}},
22 | {"nested.field": [isRequiredIfAccepted("terms")]}
23 | );
24 | expect(v.passes()).toBe(true);
25 |
26 | v = intus.validate(
27 | {terms: true, nested: {}},
28 | {"nested.field": [isRequiredIfAccepted("terms")]}
29 | );
30 | expect(v.passes()).toBe(false);
31 | expect(v.errors()).toMatchObject({
32 | "nested.field": "Nested.field is required if terms is accepted.",
33 | });
34 | });
35 |
36 | it("isRequiredIfAccepted nested star", function () {
37 | let v = intus.validate(
38 | {terms: true, nested: [{field: 123}]},
39 | {"nested.*.field": [isRequiredIfAccepted("terms")]}
40 | );
41 | expect(v.passes()).toBe(true);
42 |
43 | v = intus.validate(
44 | {sub: {terms: true}, nested: [{}]},
45 | {"nested.*.field": [isRequiredIfAccepted("sub.terms")]}
46 | );
47 | expect(v.passes()).toBe(false);
48 | expect(v.errors()).toMatchObject({
49 | "nested.0.field": "Nested.0.field is required if sub.terms is accepted.",
50 | });
51 | });
52 |
53 | it("isRequiredIfAccepted nested overwrite attribute translation", function () {
54 | let v = intus.validate(
55 | {sub: {terms: true}, nested: [{}]},
56 | {"nested.*.field": [isRequiredIfAccepted("sub.terms")]},
57 | {"nested.*.field": "something", "sub.terms": "abc"}
58 | );
59 | expect(v.passes()).toBe(false);
60 | expect(v.errors()).toMatchObject({
61 | "nested.0.field": "Something is required if abc is accepted.",
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/test/isRequiredUnless.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredUnless} from "../rules";
4 |
5 | it("isRequiredUnless", function () {
6 | let v = intus.validate({a: null, b: 2}, {a: [isRequiredUnless("b", 2)]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: false}, {a: [isRequiredUnless("b", false)]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: null, b: 1}, {a: [isRequiredUnless("b", 2)]});
13 | expect(v.passes()).toBe(false);
14 | expect(v.errors()).toMatchObject({a: "A is required if b is 2."});
15 | });
16 |
17 | it("isRequiredUnless nested", function () {
18 | let v = intus.validate(
19 | {nested: {a: 1, b: 2}},
20 | {"nested.a": [isRequiredUnless("nested.b", 2)]}
21 | );
22 | expect(v.passes()).toBe(true);
23 |
24 | v = intus.validate(
25 | {nested: {a: null, b: 1}},
26 | {"nested.a": [isRequiredUnless("nested.b", 2)]}
27 | );
28 | expect(v.passes()).toBe(false);
29 | expect(v.errors()).toMatchObject({
30 | "nested.a": "Nested.a is required if nested.b is 2.",
31 | });
32 | });
33 |
34 | it("isRequiredUnless nested star", function () {
35 | let v = intus.validate(
36 | {nested: [{a: null, b: 2}]},
37 | {"nested.*.a": [isRequiredUnless("nested.*.b", 2)]}
38 | );
39 | expect(v.passes()).toBe(true);
40 |
41 | v = intus.validate(
42 | {nested: [{a: null, b: 1}]},
43 | {"nested.*.a": [isRequiredUnless("nested.*.b", 2)]}
44 | );
45 | expect(v.passes()).toBe(false);
46 | expect(v.errors()).toMatchObject({
47 | "nested.0.a": "Nested.0.a is required if nested.0.b is 2.",
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/isRequiredWith.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredWith} from "../rules";
4 |
5 | it("isRequiredWith", function () {
6 | let v = intus.validate({a: 1, b: 2}, {a: [isRequiredWith("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: null}, {a: [isRequiredWith("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: null, b: 2}, {a: [isRequiredWith("b")]});
13 | expect(v.passes()).toBe(false);
14 | expect(v.errors()).toMatchObject({a: "A is required when b is present."});
15 | });
16 |
17 | it("isRequiredWith many", function () {
18 | let v = intus.validate(
19 | {a: null, e: 2},
20 | {a: [isRequiredWith("b", "c", "d", "e")]}
21 | );
22 | expect(v.passes()).toBe(false);
23 | expect(v.errors()).toMatchObject({
24 | a: "A is required when b / c / d / e is present.",
25 | });
26 | });
27 |
28 | it("isRequiredWith nested", function () {
29 | let v = intus.validate(
30 | {a: {b: null}, c: {d: 2}},
31 | {"a.b": [isRequiredWith("c.d")]}
32 | );
33 | expect(v.passes()).toBe(false);
34 | expect(v.errors()).toMatchObject({
35 | "a.b": "A.b is required when c.d is present.",
36 | });
37 | });
38 |
39 | it("isRequiredWith nested star", function () {
40 | let v = intus.validate(
41 | {nested: [{a: {b: null}, c: {d: 2}}]},
42 | {"nested.*.a.b": [isRequiredWith("nested.*.c.d")]}
43 | );
44 | expect(v.passes()).toBe(false);
45 | expect(v.errors()).toMatchObject({
46 | "nested.0.a.b": "Nested.0.a.b is required when nested.0.c.d is present.",
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/isRequiredWithAll.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredWithAll} from "../rules";
4 |
5 | it("isRequiredWithAll", function () {
6 | let v = intus.validate(
7 | {a: 1, b: 2, c: 2},
8 | {a: [isRequiredWithAll("b", "c")]}
9 | );
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate(
13 | {a: null, b: null, c: null},
14 | {a: [isRequiredWithAll("b", "c")]}
15 | );
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate(
19 | {a: null, b: 2, c: null},
20 | {a: [isRequiredWithAll("b", "c")]}
21 | );
22 | expect(v.passes()).toBe(true);
23 |
24 | v = intus.validate({a: null, b: 2, c: 2}, {a: [isRequiredWithAll("b", "c")]});
25 | expect(v.passes()).toBe(false);
26 | expect(v.errors()).toMatchObject({
27 | a: "A is required when b / c are present.",
28 | });
29 | });
30 |
31 | it("isRequiredWithAll nested", function () {
32 | let v = intus.validate(
33 | {nested: {a: 1, b: 2, c: 2}},
34 | {"nested.a": [isRequiredWithAll("nested.b", "nested.c")]}
35 | );
36 | expect(v.passes()).toBe(true);
37 |
38 | v = intus.validate(
39 | {nested: {a: null, b: 2, c: 2}},
40 | {"nested.a": [isRequiredWithAll("nested.b", "nested.c")]}
41 | );
42 | expect(v.passes()).toBe(false);
43 | expect(v.errors()).toMatchObject({
44 | "nested.a": "Nested.a is required when nested.b / nested.c are present.",
45 | });
46 | });
47 |
48 | it("isRequiredWithAll nested star", function () {
49 | let v = intus.validate(
50 | {
51 | nested: [
52 | {
53 | a: 1,
54 | b: 2,
55 | c: 2,
56 | },
57 | ],
58 | },
59 | {"nested.*.a": [isRequiredWithAll("nested.*.b", "nested.*.c")]}
60 | );
61 | expect(v.passes()).toBe(true);
62 |
63 | v = intus.validate(
64 | {
65 | nested: [
66 | {
67 | a: null,
68 | b: 2,
69 | c: 2,
70 | },
71 | ],
72 | },
73 | {"nested.*.a": [isRequiredWithAll("nested.*.b", "nested.*.c")]}
74 | );
75 | expect(v.passes()).toBe(false);
76 | expect(v.errors()).toMatchObject({
77 | "nested.0.a":
78 | "Nested.0.a is required when nested.0.b / nested.0.c are present.",
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/test/isRequiredWithout.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredWithout} from "../rules";
4 |
5 | it("isRequiredWithout", function () {
6 | let v = intus.validate({a: 1, b: 2}, {a: [isRequiredWithout("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: 2}, {a: [isRequiredWithout("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate(
13 | {a: null, b: 2},
14 | {a: [isRequiredWithout("b", "e", "c", "d")]}
15 | );
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate({a: null}, {a: [isRequiredWithout("b")]});
19 | expect(v.passes()).toBe(false);
20 | expect(v.errors()).toMatchObject({a: "A is required when b is missing."});
21 | });
22 |
23 | it("isRequiredWithout multiple", function () {
24 | let v = intus.validate(
25 | {a: null, b: 2},
26 | {a: [isRequiredWithout("b", "c", "d")]}
27 | );
28 | expect(v.passes()).toBe(true);
29 |
30 | v = intus.validate(
31 | {a: null, b: null},
32 | {a: [isRequiredWithout("b", "c", "d")]}
33 | );
34 | expect(v.passes()).toBe(false);
35 | expect(v.errors()).toMatchObject({
36 | a: "A is required when b / c / d is missing.",
37 | });
38 | });
39 |
40 | it("isRequiredWithout nested", function () {
41 | let v = intus.validate(
42 | {nested: {a: null, b: 2}},
43 | {"nested.a": [isRequiredWithout("nested.b")]}
44 | );
45 | expect(v.passes()).toBe(true);
46 |
47 | v = intus.validate(
48 | {nested: {a: null, b: null}},
49 | {"nested.a": [isRequiredWithout("nested.b")]}
50 | );
51 | expect(v.passes()).toBe(false);
52 | expect(v.errors()).toMatchObject({
53 | "nested.a": "Nested.a is required when nested.b is missing.",
54 | });
55 | });
56 |
57 | it("isRequiredWithout nested star", function () {
58 | let v = intus.validate(
59 | {nested: [{a: null, b: 2}]},
60 | {"nested.*.a": [isRequiredWithout("nested.*.b")]}
61 | );
62 | expect(v.passes()).toBe(true);
63 |
64 | v = intus.validate(
65 | {nested: [{a: null, b: null}]},
66 | {"nested.*.a": [isRequiredWithout("nested.*.b")]}
67 | );
68 | expect(v.passes()).toBe(false);
69 | expect(v.errors()).toMatchObject({
70 | "nested.0.a": "Nested.0.a is required when nested.0.b is missing.",
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/test/isRequiredWithoutAll.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isRequiredWithoutAll} from "../rules";
4 |
5 | it("isRequiredWithoutAll", function () {
6 | let v = intus.validate({a: 1, b: 2}, {a: [isRequiredWithoutAll("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: 2}, {a: [isRequiredWithoutAll("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate(
13 | {a: null, b: 2},
14 | {a: [isRequiredWithoutAll("b", "c", "d", "e")]}
15 | );
16 | expect(v.passes()).toBe(true);
17 |
18 | v = intus.validate({a: null}, {a: [isRequiredWithoutAll("b", "c", "d")]});
19 | expect(v.passes()).toBe(false);
20 | expect(v.errors()).toMatchObject({
21 | a: "A is required when b / c / d are missing.",
22 | });
23 | });
24 |
25 | it("isRequiredWithoutAll nested", function () {
26 | let v = intus.validate(
27 | {nested: {a: 1, b: 2}},
28 | {"nested.a": [isRequiredWithoutAll("nested.b")]}
29 | );
30 | expect(v.passes()).toBe(true);
31 |
32 | v = intus.validate(
33 | {nested: {a: null}},
34 | {"nested.a": [isRequiredWithoutAll("nested.b")]}
35 | );
36 | expect(v.passes()).toBe(false);
37 | expect(v.errors()).toMatchObject({
38 | "nested.a": "Nested.a is required when nested.b are missing.",
39 | });
40 | });
41 |
42 | it("isRequiredWithoutAll nested star", function () {
43 | let v = intus.validate(
44 | {nested: [{a: 1, b: 2}]},
45 | {"nested.*.a": [isRequiredWithoutAll("nested.*.b")]}
46 | );
47 | expect(v.passes()).toBe(true);
48 |
49 | v = intus.validate(
50 | {nested: [{a: null}]},
51 | {"nested.*.a": [isRequiredWithoutAll("nested.*.b")]}
52 | );
53 | expect(v.passes()).toBe(false);
54 | expect(v.errors()).toMatchObject({
55 | "nested.0.a": "Nested.0.a is required when nested.0.b are missing.",
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/isSame.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isSame} from "../rules";
4 |
5 | it("should validate isSame", function () {
6 | let v = intus.validate({a: "2", b: 2}, {a: [isSame("b")]});
7 | expect(v.passes()).toBe(true);
8 |
9 | v = intus.validate({a: null, b: null}, {a: [isSame("b")]});
10 | expect(v.passes()).toBe(true);
11 |
12 | v = intus.validate({a: 1, b: 2}, {a: [isSame("b")]});
13 | expect(v.passes()).toBe(false);
14 | expect(v.errors()).toMatchObject({a: "A must be the same as b."});
15 |
16 | v = intus.validate({a: null, b: 2}, {a: [isSame("b")]});
17 | expect(v.passes()).toBe(false);
18 | expect(v.errors()).toMatchObject({
19 | a: "A must be the same as b.",
20 | });
21 | });
22 |
23 | it("should validate isSame nested", function () {
24 | let v = intus.validate(
25 | {nested: {a: 2, b: 2}},
26 | {"nested.a": [isSame("nested.b")]}
27 | );
28 | expect(v.passes()).toBe(true);
29 |
30 | v = intus.validate(
31 | {nested: {a: 2, b: 3}},
32 | {"nested.a": [isSame("nested.b")]}
33 | );
34 | expect(v.passes()).toBe(false);
35 | expect(v.errors()).toMatchObject({
36 | "nested.a": "Nested.a must be the same as nested.b.",
37 | });
38 | });
39 |
40 | it("should validate isSame nested star", function () {
41 | let v = intus.validate(
42 | {nested: [{a: 2, b: 2}]},
43 | {"nested.*.a": [isSame("nested.*.b")]}
44 | );
45 | expect(v.passes()).toBe(true);
46 |
47 | v = intus.validate(
48 | {nested: [{a: 2, b: 3}]},
49 | {"nested.*.a": [isSame("nested.*.b")]}
50 | );
51 | expect(v.passes()).toBe(false);
52 | expect(v.errors()).toMatchObject({
53 | "nested.0.a": "Nested.0.a must be the same as nested.0.b.",
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/isUrl.test.js:
--------------------------------------------------------------------------------
1 | import {expect} from "vitest";
2 | import intus from "../index";
3 | import {isUrl} from "../rules";
4 |
5 | it("isUrl", function () {
6 | let invalid = [123, "http:", "ww.x"];
7 | let valid = [
8 | undefined,
9 | null,
10 | "",
11 | "https://tallpad.com",
12 | "https://cdruc.com/",
13 | "cdruc.com",
14 | ];
15 |
16 | invalid.forEach(t => {
17 | let v = intus.validate({t}, {t: [isUrl()]});
18 | expect(v.passes()).toBe(false);
19 | expect(v.errors()).toMatchObject({t: "T must be a valid URL."});
20 | });
21 | valid.forEach(t =>
22 | expect(intus.validate({t}, {t: [isUrl()]}).passes()).toBe(true)
23 | );
24 | });
25 |
26 | it("isUrl nested", function () {
27 | let v = intus.validate(
28 | {nested: {field: "http://tallpad.com"}},
29 | {"nested.field": [isUrl()]}
30 | );
31 | expect(v.passes()).toBe(true);
32 |
33 | v = intus.validate({nested: {field: "not-url"}}, {"nested.field": [isUrl()]});
34 | expect(v.passes()).toBe(false);
35 | expect(v.errors()).toMatchObject({
36 | "nested.field": "Nested.field must be a valid URL.",
37 | });
38 | });
39 |
40 | it("isUrl nested star", function () {
41 | let v = intus.validate(
42 | {nested: [{field: "http://tallpad.com"}]},
43 | {"nested.*.field": [isUrl()]}
44 | );
45 | expect(v.passes()).toBe(true);
46 |
47 | v = intus.validate(
48 | {nested: [{field: "not-url"}]},
49 | {"nested.*.field": [isUrl()]}
50 | );
51 | expect(v.passes()).toBe(false);
52 | expect(v.errors()).toMatchObject({
53 | "nested.0.field": "Nested.0.field must be a valid URL.",
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/validation.test.js:
--------------------------------------------------------------------------------
1 | import {describe} from "vitest";
2 | import {
3 | isDate,
4 | isEmail,
5 | isImage,
6 | isIn,
7 | isMax,
8 | isMin,
9 | isRequired,
10 | isRequiredIf,
11 | isRequiredWithout,
12 | } from "../rules";
13 | import intus from "../index";
14 | import Intus from "../lib/intus";
15 | import {makeFile} from "./helpers";
16 |
17 | describe("validation", () => {
18 | it("XXX should glass", function () {
19 | let v = intus.validate(
20 | {
21 | name: "cdruc",
22 | frame: makeFile("plm.jpeg", "image/jpeg"),
23 | photos: [makeFile("plm.jpeg", "image/jpeg")],
24 | },
25 | {
26 | name: [isRequired(), isMin(5)],
27 | frame: [isImage()],
28 | "photos.*": [isImage()],
29 | }
30 | );
31 | expect(v.passes()).toBe(true);
32 | });
33 | it("XXX should order request", function () {
34 | let v = intus.validate(
35 | {
36 | billing_address_same_as_delivery: "on",
37 | delivery_address: {
38 | street: "ss",
39 | country_code: "DS",
40 | postal_code: "dsq",
41 | house_number: "11",
42 | city: "ddd",
43 | },
44 | billing_address: {
45 | street: "",
46 | country_code: "",
47 | postal_code: "",
48 | house_number: "",
49 | city: "",
50 | },
51 | payment_method: "stripe",
52 | date_of_birth: new Date(),
53 | },
54 | {
55 | delivery_address: [isRequired()],
56 | "delivery_address.street": [isRequired()],
57 | "delivery_address.country_code": [isRequired(), isMax(2)],
58 | "delivery_address.postal_code": [isRequired(), isMax(12)],
59 | "delivery_address.house_number": [isRequired()],
60 | "delivery_address.city": [isRequired()],
61 | billing_address: [
62 | isRequiredWithout("billing_address_same_as_delivery"),
63 | ],
64 | "billing_address.street": [
65 | isRequiredWithout("billing_address_same_as_delivery"),
66 | ],
67 | "billing_address.country_code": [
68 | isRequiredWithout("billing_address_same_as_delivery"),
69 | isMax(2),
70 | ],
71 | "billing_address.postal_code": [
72 | isRequiredWithout("billing_address_same_as_delivery"),
73 | isMax(12),
74 | ],
75 | "billing_address.house_number": [
76 | isRequiredWithout("billing_address_same_as_delivery"),
77 | ],
78 | "billing_address.city": [
79 | isRequiredWithout("billing_address_same_as_delivery"),
80 | ],
81 | payment_method: [isRequired(), isIn("paypal", "stripe")],
82 | date_of_birth: [isRequiredIf("payment_method", "stripe"), isDate()],
83 | }
84 | );
85 |
86 | expect(v.passes()).toBe(true);
87 | });
88 |
89 | it("XXX should user request", function () {
90 | let v = intus.validate(
91 | {
92 | first_name: "",
93 | last_name: Array.from({length: 257}).join("."),
94 | email: "not-mail",
95 | phone: "0749878321",
96 | },
97 | {
98 | first_name: [isRequired(), isMax(255)],
99 | last_name: [isRequired(), isMax(255)],
100 | email: [isRequired(), isEmail(), isMax(255)],
101 | }
102 | );
103 |
104 | expect(v.passes()).toBe(false);
105 | });
106 |
107 | it("should convert snake_case attributes to words when getting erros", function () {
108 | let v = intus.validate(
109 | {
110 | first_name: "",
111 | },
112 | {
113 | first_name: [isRequired()],
114 | }
115 | );
116 |
117 | expect(v.passes()).toBe(false);
118 | expect(v.errors()).toMatchObject({
119 | first_name: "First name is required.",
120 | });
121 | });
122 |
123 | it("nested error messages are overwritten", function () {
124 | let validation = intus.validate(
125 | {
126 | users: [{name: "", posts: [{title: ""}]}],
127 | },
128 | {
129 | "users.*.name": [isRequired()],
130 | "users.*.posts.*.title": [isRequired()],
131 | },
132 | {
133 | "users.*.name": "user name",
134 | "users.*.posts.*.title.isRequired": "post title is required",
135 | }
136 | );
137 |
138 | expect(validation.passes()).toBe(false);
139 | expect(validation.errors()).toMatchObject({
140 | "users.0.name": "User name is required.",
141 | "users.0.posts.0.title": "Post title is required",
142 | });
143 | });
144 |
145 | it("scares me to test this", function () {
146 | let validation = intus.validate(
147 | {
148 | users: [
149 | {
150 | name: "Constantin",
151 | posts: [{title: "Too short", ps: [{words: 3}]}],
152 | },
153 | ],
154 | },
155 | {
156 | "users.*.name": [isRequired()],
157 | "users.*.posts.*.title": [isMin(20)],
158 | },
159 | {}
160 | );
161 |
162 | expect(validation.errors()).toMatchObject({
163 | "users.0.posts.0.title":
164 | "Users.0.posts.0.title must be at least 20 characters long.",
165 | });
166 | });
167 |
168 | it("nested object error messages are overwritten ", function () {
169 | let validation = intus.validate(
170 | {
171 | users: [{name: "Constantin", posts: [{title: ""}]}],
172 | },
173 | {
174 | "users.*.name": [isRequired()],
175 | "users.*.posts.*.title": [isRequired()],
176 | },
177 | {
178 | "users.*.name.isRequired": "User name is required",
179 | "users.*.posts.*.title": {
180 | isRequired: "Post title is required",
181 | },
182 | }
183 | );
184 |
185 | expect(validation.passes()).toBe(false);
186 | expect(validation.errors()).toMatchObject({
187 | "users.0.posts.0.title": "Post title is required",
188 | });
189 | });
190 |
191 | it("should validate asterisks", function () {
192 | let validation = intus.validate(
193 | {
194 | quantities: [1, 2, 3],
195 | },
196 | {
197 | "quantities.*": [isMin(4)],
198 | }
199 | );
200 |
201 | expect(validation.passes()).toBe(false);
202 | });
203 |
204 | it("should replace error messages with asterisks", function () {
205 | let validation = intus.validate(
206 | {
207 | quantities: [1, 2],
208 | },
209 | {
210 | "quantities.*": [isMin(4)],
211 | },
212 | {
213 | "quantities.*": {
214 | isMin: "Should be min 4.",
215 | },
216 | }
217 | );
218 |
219 | expect(validation.passes()).toBe(false);
220 | expect(validation.errors()).toMatchObject({
221 | "quantities.0": "Should be min 4.",
222 | "quantities.1": "Should be min 4.",
223 | });
224 | });
225 |
226 | it("should add rule function on the fly", function () {
227 | let validation = intus.validate(
228 | {
229 | age: 17,
230 | },
231 | {
232 | age: [
233 | function isNotUnderage({value}) {
234 | function passes() {
235 | return value > 18;
236 | }
237 |
238 | function message(msg = "this will be replaced") {
239 | return msg.replaceAll(":value", value);
240 | }
241 |
242 | return {passes, message};
243 | },
244 | ],
245 | },
246 | {
247 | isNotUnderage: ":value is considered to be underage.",
248 | }
249 | );
250 |
251 | expect(validation.passes()).toBe(false);
252 | expect(validation.errors()).toMatchObject({
253 | age: "17 is considered to be underage.",
254 | });
255 | });
256 |
257 | it("allows replacing default error messages ", function () {
258 | let intus = new Intus({
259 | isRequired: "The :attribute field is required",
260 | "name.isRequired": "Name is definitely required",
261 | });
262 |
263 | let validation = intus.validate(
264 | {
265 | email: "not-valid",
266 | },
267 | {
268 | name: [isRequired()],
269 | age: [isRequired()],
270 | email: [isRequired(), isEmail()],
271 | }
272 | );
273 |
274 | expect(validation.errors()).toMatchObject({
275 | name: "Name is definitely required",
276 | age: "The age field is required",
277 | email: "Email must be a valid email.",
278 | });
279 | });
280 | });
281 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('vite').UserConfig} */
2 | export default {
3 | test: {
4 | globals: true,
5 | },
6 | };
7 |
--------------------------------------------------------------------------------