kanun 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +264 -58
- package/dist/index.d.ts +7 -2
- package/dist/index.js +264 -58
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
let node_async_hooks = require("node:async_hooks");
|
|
30
|
+
let node_url = require("node:url");
|
|
31
|
+
let node_net = require("node:net");
|
|
30
32
|
let dayjs_plugin_customParseFormat_js = require("dayjs/plugin/customParseFormat.js");
|
|
31
33
|
dayjs_plugin_customParseFormat_js = __toESM(dayjs_plugin_customParseFormat_js);
|
|
32
34
|
let dayjs = require("dayjs");
|
|
@@ -103,10 +105,12 @@ var en_default = {
|
|
|
103
105
|
declined: "The :attribute must be declined.",
|
|
104
106
|
declined_if: "The :attribute must be declined when :other is :value.",
|
|
105
107
|
different: "The :attribute and :other must be different.",
|
|
108
|
+
distinct: "The :attribute field has a duplicate value.",
|
|
106
109
|
digits: "The :attribute must be :digits digits.",
|
|
107
110
|
digits_between: "The :attribute must be between :min and :max digits.",
|
|
108
111
|
email: "The :attribute must be a valid email address.",
|
|
109
112
|
ends_with: "The :attribute must end with one of the following: :values.",
|
|
113
|
+
filled: "The :attribute field must have a value.",
|
|
110
114
|
exists: "The selected :attribute is invalid.",
|
|
111
115
|
gt: {
|
|
112
116
|
number: "The :attribute must be greater than :value.",
|
|
@@ -124,6 +128,9 @@ var en_default = {
|
|
|
124
128
|
in: "The :attribute must be one of the following :values.",
|
|
125
129
|
includes: "The :attribute must include one of the following values: :values.",
|
|
126
130
|
integer: "The :attribute must be an integer.",
|
|
131
|
+
ip: "The :attribute must be a valid IP address.",
|
|
132
|
+
ipv4: "The :attribute must be a valid IPv4 address.",
|
|
133
|
+
ipv6: "The :attribute must be a valid IPv6 address.",
|
|
127
134
|
json: "The :attribute must be a valid JSON string.",
|
|
128
135
|
lt: {
|
|
129
136
|
number: "The :attribute must be less than :value.",
|
|
@@ -149,6 +156,7 @@ var en_default = {
|
|
|
149
156
|
array: "The :attribute must have at least :min items.",
|
|
150
157
|
object: "The :attribute must have at least :min items."
|
|
151
158
|
},
|
|
159
|
+
mac_address: "The :attribute must be a valid MAC address.",
|
|
152
160
|
not_in: "The selected :attribute is invalid.",
|
|
153
161
|
not_regex: "The :attribute format is invalid.",
|
|
154
162
|
not_includes: "The :attribute must not include any of the following values: :values.",
|
|
@@ -167,6 +175,10 @@ var en_default = {
|
|
|
167
175
|
upper_cases: "The :attribute must contain at least :amount uppercase letters."
|
|
168
176
|
},
|
|
169
177
|
present: "The :attribute field must be present.",
|
|
178
|
+
presentsame: "The :attribute field must be present.",
|
|
179
|
+
prohibited: "The :attribute field is prohibited.",
|
|
180
|
+
prohibited_unless: "The :attribute field is prohibited unless :other is in :values.",
|
|
181
|
+
prohibits: "The :attribute field prohibits :values from being present.",
|
|
170
182
|
regex: "The :attribute format is invalid.",
|
|
171
183
|
required: "The :attribute field is required.",
|
|
172
184
|
required_if: "The :attribute field is required when :other is :value.",
|
|
@@ -184,8 +196,10 @@ var en_default = {
|
|
|
184
196
|
object: "The :attribute must contain :size items."
|
|
185
197
|
},
|
|
186
198
|
string: "The :attribute must be a string.",
|
|
199
|
+
timezone: "The :attribute must be a valid timezone.",
|
|
187
200
|
unique: "The :attribute has already been taken.",
|
|
188
|
-
url: "The :attribute must have a valid URL format."
|
|
201
|
+
url: "The :attribute must have a valid URL format.",
|
|
202
|
+
multiple_of: "The :attribute must be a multiple of :value."
|
|
189
203
|
};
|
|
190
204
|
|
|
191
205
|
//#endregion
|
|
@@ -654,6 +668,12 @@ const replaceAttributes = {
|
|
|
654
668
|
};
|
|
655
669
|
return message.replace(/:other|:value/gi, (matched) => values[matched]);
|
|
656
670
|
},
|
|
671
|
+
replaceProhibitedUnless: function(payload) {
|
|
672
|
+
return this.replaceRequiredUnless(payload);
|
|
673
|
+
},
|
|
674
|
+
replaceProhibits: function({ message, parameters, getDisplayableAttribute }) {
|
|
675
|
+
return message.replace(":values", parameters.map((attribute) => getDisplayableAttribute(attribute)).join(", "));
|
|
676
|
+
},
|
|
657
677
|
replaceRequiredUnless: function({ message, parameters, getDisplayableAttribute }) {
|
|
658
678
|
const [other] = parameters;
|
|
659
679
|
const values = {
|
|
@@ -668,40 +688,14 @@ const replaceAttributes = {
|
|
|
668
688
|
replaceSize: function({ message, parameters }) {
|
|
669
689
|
return message.replace(":size", parameters[0]);
|
|
670
690
|
},
|
|
691
|
+
replaceMultipleOf: function({ message, parameters }) {
|
|
692
|
+
return message.replace(":value", parameters[0]);
|
|
693
|
+
},
|
|
671
694
|
replaceUnique: function({ message, parameters, data }) {
|
|
672
695
|
return message.replace(":value", data[parameters[1]]);
|
|
673
696
|
}
|
|
674
697
|
};
|
|
675
698
|
|
|
676
|
-
//#endregion
|
|
677
|
-
//#region src/Rules/closureValidationRule.ts
|
|
678
|
-
var ClosureValidationRule = class extends IRuleContract {
|
|
679
|
-
/**
|
|
680
|
-
* The callback that validates the attribute
|
|
681
|
-
*/
|
|
682
|
-
callback;
|
|
683
|
-
/**
|
|
684
|
-
* Indicates if the validation callback failed.
|
|
685
|
-
*/
|
|
686
|
-
failed = false;
|
|
687
|
-
constructor(callback) {
|
|
688
|
-
super();
|
|
689
|
-
this.callback = callback;
|
|
690
|
-
}
|
|
691
|
-
/**
|
|
692
|
-
* Determine if the validation rule passes.
|
|
693
|
-
*/
|
|
694
|
-
passes(value, attribute) {
|
|
695
|
-
this.failed = false;
|
|
696
|
-
const result = this.callback(value, (message) => {
|
|
697
|
-
this.failed = true;
|
|
698
|
-
this.message = message;
|
|
699
|
-
}, attribute);
|
|
700
|
-
if (result instanceof Promise) return result.then(() => !this.failed);
|
|
701
|
-
return !this.failed;
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
|
|
705
699
|
//#endregion
|
|
706
700
|
//#region src/validators/validationData.ts
|
|
707
701
|
const validationData = {
|
|
@@ -742,6 +736,35 @@ const validationData = {
|
|
|
742
736
|
}
|
|
743
737
|
};
|
|
744
738
|
|
|
739
|
+
//#endregion
|
|
740
|
+
//#region src/Rules/closureValidationRule.ts
|
|
741
|
+
var ClosureValidationRule = class extends IRuleContract {
|
|
742
|
+
/**
|
|
743
|
+
* The callback that validates the attribute
|
|
744
|
+
*/
|
|
745
|
+
callback;
|
|
746
|
+
/**
|
|
747
|
+
* Indicates if the validation callback failed.
|
|
748
|
+
*/
|
|
749
|
+
failed = false;
|
|
750
|
+
constructor(callback) {
|
|
751
|
+
super();
|
|
752
|
+
this.callback = callback;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Determine if the validation rule passes.
|
|
756
|
+
*/
|
|
757
|
+
passes(value, attribute) {
|
|
758
|
+
this.failed = false;
|
|
759
|
+
const result = this.callback(value, (message) => {
|
|
760
|
+
this.failed = true;
|
|
761
|
+
this.message = message;
|
|
762
|
+
}, attribute);
|
|
763
|
+
if (result instanceof Promise) return result.then(() => !this.failed);
|
|
764
|
+
return !this.failed;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
745
768
|
//#endregion
|
|
746
769
|
//#region src/validators/validationRuleParser.ts
|
|
747
770
|
const validationRuleParser = {
|
|
@@ -989,7 +1012,7 @@ var validateAttributes = class {
|
|
|
989
1012
|
*/
|
|
990
1013
|
validateDeclinedIf(value, parameters) {
|
|
991
1014
|
this.requireParameterCount(2, parameters, "declined_if");
|
|
992
|
-
const other =
|
|
1015
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
993
1016
|
if (!other) return true;
|
|
994
1017
|
if (parameters.slice(1).indexOf(other) !== -1) return this.validateDeclined(value);
|
|
995
1018
|
return true;
|
|
@@ -999,7 +1022,7 @@ var validateAttributes = class {
|
|
|
999
1022
|
*/
|
|
1000
1023
|
validateDifferent(value, parameters) {
|
|
1001
1024
|
this.requireParameterCount(1, parameters, "different");
|
|
1002
|
-
const other =
|
|
1025
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
1003
1026
|
if (!sameType(value, other)) return true;
|
|
1004
1027
|
if (value !== null && typeof value === "object") return !deepEqual(value, other);
|
|
1005
1028
|
return value !== other;
|
|
@@ -1034,13 +1057,23 @@ var validateAttributes = class {
|
|
|
1034
1057
|
/**
|
|
1035
1058
|
* Validate that an attribute is a valid email address.
|
|
1036
1059
|
*/
|
|
1037
|
-
validateEmail(value) {
|
|
1060
|
+
validateEmail(value, parameters = []) {
|
|
1038
1061
|
if (typeof value !== "string") return false;
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1062
|
+
const normalized = parameters.map((parameter) => parameter.replace(/[{}]/g, "").toLowerCase());
|
|
1063
|
+
if (normalized.length === 0 || normalized.includes("filter") || normalized.includes("rfc")) {
|
|
1064
|
+
if (!this.isEmailByFilter(value)) return false;
|
|
1065
|
+
}
|
|
1066
|
+
if (normalized.includes("strict") && !this.isStrictEmail(value)) return false;
|
|
1067
|
+
if (normalized.includes("dns") && !this.hasResolvableEmailDomain(value)) return false;
|
|
1068
|
+
if (normalized.includes("spoof") && !this.isSpoofSafeEmail(value)) return false;
|
|
1069
|
+
return true;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Validate that a present attribute is not empty.
|
|
1073
|
+
*/
|
|
1074
|
+
validateFilled(value) {
|
|
1075
|
+
if (typeof value === "undefined") return true;
|
|
1076
|
+
return this.validateRequired(value);
|
|
1044
1077
|
}
|
|
1045
1078
|
/**
|
|
1046
1079
|
* Validate the attribute ends with a given substring.
|
|
@@ -1100,7 +1133,7 @@ var validateAttributes = class {
|
|
|
1100
1133
|
*/
|
|
1101
1134
|
validateRequiredIf(value, parameters) {
|
|
1102
1135
|
this.requireParameterCount(2, parameters, "required_if");
|
|
1103
|
-
const other =
|
|
1136
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
1104
1137
|
if (typeof other === "undefined") return true;
|
|
1105
1138
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) !== -1) return this.validateRequired(value);
|
|
1106
1139
|
return true;
|
|
@@ -1110,7 +1143,7 @@ var validateAttributes = class {
|
|
|
1110
1143
|
*/
|
|
1111
1144
|
validateRequiredUnless(value, parameters) {
|
|
1112
1145
|
this.requireParameterCount(2, parameters, "required_unless");
|
|
1113
|
-
let other =
|
|
1146
|
+
let other = this.getAttributeValue(parameters[0]);
|
|
1114
1147
|
other = typeof other === "undefined" ? null : other;
|
|
1115
1148
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) === -1) return this.validateRequired(value);
|
|
1116
1149
|
return true;
|
|
@@ -1147,14 +1180,14 @@ var validateAttributes = class {
|
|
|
1147
1180
|
* Determine if any of the given attributes fail the required test.
|
|
1148
1181
|
*/
|
|
1149
1182
|
anyFailingRequired(attributes) {
|
|
1150
|
-
for (let i = 0; i < attributes.length; i++) if (!this.validateRequired(
|
|
1183
|
+
for (let i = 0; i < attributes.length; i++) if (!this.validateRequired(this.getAttributeValue(attributes[i]))) return true;
|
|
1151
1184
|
return false;
|
|
1152
1185
|
}
|
|
1153
1186
|
/**
|
|
1154
1187
|
* Determine if all of the given attributes fail the required test.
|
|
1155
1188
|
*/
|
|
1156
1189
|
allFailingRequired(attributes) {
|
|
1157
|
-
for (let i = 0; i < attributes.length; i++) if (this.validateRequired(
|
|
1190
|
+
for (let i = 0; i < attributes.length; i++) if (this.validateRequired(this.getAttributeValue(attributes[i]))) return false;
|
|
1158
1191
|
return true;
|
|
1159
1192
|
}
|
|
1160
1193
|
/**
|
|
@@ -1196,7 +1229,13 @@ var validateAttributes = class {
|
|
|
1196
1229
|
* Validate that an attribute exists even if not filled.
|
|
1197
1230
|
*/
|
|
1198
1231
|
validatePresent(value, parameters, attribute) {
|
|
1199
|
-
return typeof
|
|
1232
|
+
return typeof this.getAttributeValue(attribute) !== "undefined";
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Alias of the present rule kept for compatibility with requested rule naming.
|
|
1236
|
+
*/
|
|
1237
|
+
validatePresentsame(value, parameters, attribute) {
|
|
1238
|
+
return this.validatePresent(value, parameters, attribute);
|
|
1200
1239
|
}
|
|
1201
1240
|
/**
|
|
1202
1241
|
* Validate that an attribute is an integer.
|
|
@@ -1218,12 +1257,103 @@ var validateAttributes = class {
|
|
|
1218
1257
|
return true;
|
|
1219
1258
|
}
|
|
1220
1259
|
/**
|
|
1260
|
+
* Validate that an attribute is prohibited.
|
|
1261
|
+
*/
|
|
1262
|
+
validateProhibited(value) {
|
|
1263
|
+
return !this.validateRequired(value);
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Validate that an attribute is prohibited unless another field matches one of the given values.
|
|
1267
|
+
*/
|
|
1268
|
+
validateProhibitedUnless(value, parameters) {
|
|
1269
|
+
this.requireParameterCount(2, parameters, "prohibited_unless");
|
|
1270
|
+
let other = this.getAttributeValue(parameters[0]);
|
|
1271
|
+
other = typeof other === "undefined" ? null : other;
|
|
1272
|
+
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) !== -1) return true;
|
|
1273
|
+
return this.validateProhibited(value);
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Validate that present attributes prohibit other attributes from being present.
|
|
1277
|
+
*/
|
|
1278
|
+
validateProhibits(value, parameters) {
|
|
1279
|
+
this.requireParameterCount(1, parameters, "prohibits");
|
|
1280
|
+
if (!this.validateRequired(value)) return true;
|
|
1281
|
+
return parameters.every((attribute) => this.validateProhibited(this.getAttributeValue(attribute)));
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Validate that an attribute is a valid IP address.
|
|
1285
|
+
*/
|
|
1286
|
+
validateIp(value) {
|
|
1287
|
+
return typeof value === "string" && (0, node_net.isIP)(value) !== 0;
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Validate that an attribute is a valid IPv4 address.
|
|
1291
|
+
*/
|
|
1292
|
+
validateIpv4(value) {
|
|
1293
|
+
return typeof value === "string" && (0, node_net.isIP)(value) === 4;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Validate that an attribute is a valid IPv6 address.
|
|
1297
|
+
*/
|
|
1298
|
+
validateIpv6(value) {
|
|
1299
|
+
return typeof value === "string" && (0, node_net.isIP)(value) === 6;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Validate that an attribute is a valid MAC address.
|
|
1303
|
+
*/
|
|
1304
|
+
validateMacAddress(value) {
|
|
1305
|
+
return typeof value === "string" && /^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i.test(value);
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Validate that an attribute is a valid timezone identifier.
|
|
1309
|
+
*/
|
|
1310
|
+
validateTimezone(value) {
|
|
1311
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
1312
|
+
if (typeof Intl.supportedValuesOf === "function" && Intl.supportedValuesOf("timeZone").includes(value)) return true;
|
|
1313
|
+
try {
|
|
1314
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value });
|
|
1315
|
+
return true;
|
|
1316
|
+
} catch {
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Validate that an attribute is a multiple of the given value.
|
|
1322
|
+
*/
|
|
1323
|
+
validateMultipleOf(value, parameters) {
|
|
1324
|
+
this.requireParameterCount(1, parameters, "multiple_of");
|
|
1325
|
+
const numericValue = Number(value);
|
|
1326
|
+
const divisor = Number(parameters[0]);
|
|
1327
|
+
if (!Number.isFinite(numericValue) || !Number.isFinite(divisor) || divisor === 0) return false;
|
|
1328
|
+
const scale = 10 ** Math.max(this.getDecimalPlaces(value), this.getDecimalPlaces(parameters[0]));
|
|
1329
|
+
const scaledValue = Math.round(numericValue * scale);
|
|
1330
|
+
const scaledDivisor = Math.round(divisor * scale);
|
|
1331
|
+
return scaledDivisor !== 0 && scaledValue % scaledDivisor === 0;
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Validate that values matched by a wildcard attribute are distinct.
|
|
1335
|
+
*/
|
|
1336
|
+
validateDistinct(value, parameters, attribute, primaryAttribute = attribute) {
|
|
1337
|
+
if (Array.isArray(value)) return new Set(value.map((entry) => this.normalizeDistinctValue(entry, parameters))).size === value.length;
|
|
1338
|
+
if (primaryAttribute.indexOf("*") === -1) return true;
|
|
1339
|
+
const strict = parameters.includes("strict");
|
|
1340
|
+
const values = this.getDistinctValues(primaryAttribute);
|
|
1341
|
+
const current = this.normalizeDistinctValue(value, parameters);
|
|
1342
|
+
let matches = 0;
|
|
1343
|
+
for (const candidate of values) {
|
|
1344
|
+
const normalized = this.normalizeDistinctValue(candidate, parameters);
|
|
1345
|
+
if (strict ? normalized === current : normalized == current) matches += 1;
|
|
1346
|
+
if (matches > 1) return false;
|
|
1347
|
+
}
|
|
1348
|
+
return true;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1221
1351
|
* Validate that an attribute is greater than another attribute.
|
|
1222
1352
|
*/
|
|
1223
1353
|
validateGt(value, parameters, attribute) {
|
|
1224
1354
|
this.requireParameterCount(1, parameters, "gt");
|
|
1225
1355
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1226
|
-
const compartedToValue =
|
|
1356
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1227
1357
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) > compartedToValue;
|
|
1228
1358
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1229
1359
|
return getSize(value) > getSize(compartedToValue);
|
|
@@ -1234,7 +1364,7 @@ var validateAttributes = class {
|
|
|
1234
1364
|
validateGte(value, parameters, attribute) {
|
|
1235
1365
|
this.requireParameterCount(1, parameters, "gte");
|
|
1236
1366
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1237
|
-
const compartedToValue =
|
|
1367
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1238
1368
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) >= compartedToValue;
|
|
1239
1369
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1240
1370
|
return getSize(value) >= getSize(compartedToValue);
|
|
@@ -1245,7 +1375,7 @@ var validateAttributes = class {
|
|
|
1245
1375
|
validateLt(value, parameters, attribute) {
|
|
1246
1376
|
this.requireParameterCount(1, parameters, "lt");
|
|
1247
1377
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1248
|
-
const compartedToValue =
|
|
1378
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1249
1379
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) < compartedToValue;
|
|
1250
1380
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1251
1381
|
return getSize(value) < getSize(compartedToValue);
|
|
@@ -1256,7 +1386,7 @@ var validateAttributes = class {
|
|
|
1256
1386
|
validateLte(value, parameters, attribute) {
|
|
1257
1387
|
this.requireParameterCount(1, parameters, "lte");
|
|
1258
1388
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1259
|
-
const compartedToValue =
|
|
1389
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1260
1390
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) <= compartedToValue;
|
|
1261
1391
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1262
1392
|
return getSize(value) <= getSize(compartedToValue);
|
|
@@ -1314,7 +1444,7 @@ var validateAttributes = class {
|
|
|
1314
1444
|
compareDates(value, parameter, operator, rule) {
|
|
1315
1445
|
value = toDate(value);
|
|
1316
1446
|
if (!value) throw `Validation rule ${rule} requires the field under valation to be a date.`;
|
|
1317
|
-
const compartedToValue = toDate(
|
|
1447
|
+
const compartedToValue = toDate(this.getAttributeValue(parameter) || parameter);
|
|
1318
1448
|
if (!compartedToValue) throw `Validation rule ${rule} requires the parameter to be a date.`;
|
|
1319
1449
|
return compare(value.getTime(), compartedToValue.getTime(), operator);
|
|
1320
1450
|
}
|
|
@@ -1326,6 +1456,10 @@ var validateAttributes = class {
|
|
|
1326
1456
|
}
|
|
1327
1457
|
/**
|
|
1328
1458
|
* Prepare the values for validation
|
|
1459
|
+
*
|
|
1460
|
+
* @param other The value to compare against
|
|
1461
|
+
* @param parameters The parameters for the validation rule
|
|
1462
|
+
* @returns The prepared values for validation
|
|
1329
1463
|
*/
|
|
1330
1464
|
parseDependentRuleParameters(other, parameters) {
|
|
1331
1465
|
let values = parameters.slice(1);
|
|
@@ -1334,6 +1468,55 @@ var validateAttributes = class {
|
|
|
1334
1468
|
if (typeof other === "boolean") values = convertValuesToBoolean(values);
|
|
1335
1469
|
return values;
|
|
1336
1470
|
}
|
|
1471
|
+
getAttributeValue(attribute) {
|
|
1472
|
+
const dataValue = deepFind(this.data, attribute);
|
|
1473
|
+
if (typeof dataValue !== "undefined") return dataValue;
|
|
1474
|
+
return deepFind(this.context.requestFiles ?? {}, attribute);
|
|
1475
|
+
}
|
|
1476
|
+
getDistinctValues(primaryAttribute) {
|
|
1477
|
+
const gathered = validationData.initializeAndGatherData(primaryAttribute, this.data);
|
|
1478
|
+
const pattern = new RegExp(`^${primaryAttribute.replace(/\./g, "\\.").replace(/\*/g, "[^.]+")}$`);
|
|
1479
|
+
return Object.keys(gathered).filter((attribute) => pattern.test(attribute)).map((attribute) => deepFind(this.data, attribute)).filter((value) => typeof value !== "undefined");
|
|
1480
|
+
}
|
|
1481
|
+
normalizeDistinctValue(value, parameters) {
|
|
1482
|
+
if (parameters.includes("ignore_case") && typeof value === "string") return value.toLowerCase();
|
|
1483
|
+
return value;
|
|
1484
|
+
}
|
|
1485
|
+
isEmailByFilter(value) {
|
|
1486
|
+
return value.toLowerCase().match(/^[^\s@]+@[^\s@]+\.[^\s@]{2,24}$/) !== null;
|
|
1487
|
+
}
|
|
1488
|
+
isStrictEmail(value) {
|
|
1489
|
+
if (!this.isEmailByFilter(value)) return false;
|
|
1490
|
+
const [localPart, domain] = value.split("@");
|
|
1491
|
+
if (!localPart || !domain || localPart.length > 64 || domain.length > 255) return false;
|
|
1492
|
+
if (localPart.startsWith(".") || localPart.endsWith(".") || localPart.includes("..")) return false;
|
|
1493
|
+
return domain.split(".").every((label) => {
|
|
1494
|
+
return label.length > 0 && label.length <= 63 && /^[a-z0-9-]+$/i.test(label) && !label.startsWith("-") && !label.endsWith("-");
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
hasResolvableEmailDomain(value) {
|
|
1498
|
+
if (!this.isStrictEmail(value)) return false;
|
|
1499
|
+
const domain = value.split("@")[1];
|
|
1500
|
+
const asciiDomain = (0, node_url.domainToASCII)(domain);
|
|
1501
|
+
return asciiDomain.length > 0 && asciiDomain.includes(".");
|
|
1502
|
+
}
|
|
1503
|
+
isSpoofSafeEmail(value) {
|
|
1504
|
+
if (!this.isEmailByFilter(value)) return false;
|
|
1505
|
+
const [localPart, domain] = value.split("@");
|
|
1506
|
+
return (0, node_url.domainToASCII)(domain).length > 0 && !this.hasAsciiControlCharacters(value) && localPart.normalize("NFKC") === localPart && domain.normalize("NFKC") === domain;
|
|
1507
|
+
}
|
|
1508
|
+
hasAsciiControlCharacters(value) {
|
|
1509
|
+
for (let index = 0; index < value.length; index++) {
|
|
1510
|
+
const codePoint = value.charCodeAt(index);
|
|
1511
|
+
if (codePoint <= 31 || codePoint === 127) return true;
|
|
1512
|
+
}
|
|
1513
|
+
return false;
|
|
1514
|
+
}
|
|
1515
|
+
getDecimalPlaces(value) {
|
|
1516
|
+
const stringValue = String(value).toLowerCase();
|
|
1517
|
+
if (!stringValue.includes(".")) return 0;
|
|
1518
|
+
return stringValue.split(".")[1]?.length ?? 0;
|
|
1519
|
+
}
|
|
1337
1520
|
};
|
|
1338
1521
|
|
|
1339
1522
|
//#endregion
|
|
@@ -1402,6 +1585,7 @@ const implicitRues = [
|
|
|
1402
1585
|
"declined_if",
|
|
1403
1586
|
"filled",
|
|
1404
1587
|
"present",
|
|
1588
|
+
"presentsame",
|
|
1405
1589
|
"required",
|
|
1406
1590
|
"required_if",
|
|
1407
1591
|
"required_unless",
|
|
@@ -2030,6 +2214,7 @@ var replaceAttributePayload = class {
|
|
|
2030
2214
|
//#endregion
|
|
2031
2215
|
//#region src/BaseValidator.ts
|
|
2032
2216
|
var BaseValidator = class BaseValidator {
|
|
2217
|
+
excludedAttributes = /* @__PURE__ */ new Set();
|
|
2033
2218
|
/**
|
|
2034
2219
|
* The lang used to return error messages
|
|
2035
2220
|
*/
|
|
@@ -2174,6 +2359,9 @@ var BaseValidator = class BaseValidator {
|
|
|
2174
2359
|
errors() {
|
|
2175
2360
|
return this.messages;
|
|
2176
2361
|
}
|
|
2362
|
+
getExcludedAttributes() {
|
|
2363
|
+
return [...this.excludedAttributes];
|
|
2364
|
+
}
|
|
2177
2365
|
/**
|
|
2178
2366
|
* Clear the error messages for the given keys.
|
|
2179
2367
|
* If no keys are provided, all error messages will be cleared.
|
|
@@ -2290,6 +2478,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2290
2478
|
runAllValidations() {
|
|
2291
2479
|
this.messages = new ErrorBag();
|
|
2292
2480
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2481
|
+
this.excludedAttributes.clear();
|
|
2293
2482
|
for (const property in this.rules) if (this.runValidation(property) === false) break;
|
|
2294
2483
|
}
|
|
2295
2484
|
/**
|
|
@@ -2298,6 +2487,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2298
2487
|
async runAllValidationsAsync() {
|
|
2299
2488
|
this.messages = new ErrorBag();
|
|
2300
2489
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2490
|
+
this.excludedAttributes.clear();
|
|
2301
2491
|
for (const property in this.rules) if (await this.runValidationAsync(property) === false) break;
|
|
2302
2492
|
}
|
|
2303
2493
|
/**
|
|
@@ -2320,26 +2510,39 @@ var BaseValidator = class BaseValidator {
|
|
|
2320
2510
|
* Run validation rules for the specified property and stop validation if needed
|
|
2321
2511
|
*/
|
|
2322
2512
|
runValidation(property) {
|
|
2323
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2513
|
+
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property])) {
|
|
2514
|
+
if (validationRuleParser.hasRule(property, ["exclude"], this.rules)) {
|
|
2515
|
+
this.excludedAttributes.add(property);
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
for (let i = 0; i < this.rules[property].length; i++) {
|
|
2519
|
+
this.validateAttribute(property, this.rules[property][i]);
|
|
2520
|
+
if (this.messages.keys().length > 0 && this.stopOnFirstFailureFlag === true) return false;
|
|
2521
|
+
if (this.shouldStopValidating(property)) break;
|
|
2522
|
+
}
|
|
2327
2523
|
}
|
|
2328
2524
|
}
|
|
2329
2525
|
/**
|
|
2330
2526
|
* Run validation rules for the specified property asynchronously and stop validation if needed
|
|
2331
2527
|
*/
|
|
2332
2528
|
async runValidationAsync(property) {
|
|
2333
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2529
|
+
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property])) {
|
|
2530
|
+
if (validationRuleParser.hasRule(property, ["exclude"], this.rules)) {
|
|
2531
|
+
this.excludedAttributes.add(property);
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
for (let i = 0; i < this.rules[property].length; i++) {
|
|
2535
|
+
await this.validateAttribute(property, this.rules[property][i]);
|
|
2536
|
+
if (this.messages.keys().length > 0 && this.stopOnFirstFailureFlag === true) return false;
|
|
2537
|
+
if (this.shouldStopValidating(property)) break;
|
|
2538
|
+
}
|
|
2337
2539
|
}
|
|
2338
2540
|
}
|
|
2339
2541
|
/**
|
|
2340
2542
|
* Check if we should stop further validations on a given attribute.
|
|
2341
2543
|
*/
|
|
2342
2544
|
shouldStopValidating(attribute) {
|
|
2545
|
+
if (this.excludedAttributes.has(attribute)) return true;
|
|
2343
2546
|
return this.messages.has(attribute) && validationRuleParser.hasRule(attribute, ["bail"], this.rules);
|
|
2344
2547
|
}
|
|
2345
2548
|
/**
|
|
@@ -2364,7 +2567,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2364
2567
|
const method = `validate${buildValidationMethodName(rule)}`;
|
|
2365
2568
|
if (rule !== "" && typeof this.validateAttributes[method] === "undefined") throw `Rule ${rule} is not valid`;
|
|
2366
2569
|
if (!validatable) return;
|
|
2367
|
-
const validation = this.validateAttributes[method](value, parameters, attribute);
|
|
2570
|
+
const validation = this.validateAttributes[method](value, parameters, attribute, this.getPrimaryAttribute(attribute));
|
|
2368
2571
|
if (validation instanceof Promise) return validation.then((result) => {
|
|
2369
2572
|
if (!result) this.addFailure(attribute, rule, value, parameters);
|
|
2370
2573
|
});
|
|
@@ -3129,8 +3332,10 @@ var Validator = class Validator {
|
|
|
3129
3332
|
*/
|
|
3130
3333
|
validatedData() {
|
|
3131
3334
|
const validKeys = Object.keys(this.rules);
|
|
3335
|
+
const excluded = new Set(this.instance?.getExcludedAttributes() ?? []);
|
|
3132
3336
|
const clean = {};
|
|
3133
3337
|
for (const key of validKeys) {
|
|
3338
|
+
if (excluded.has(key)) continue;
|
|
3134
3339
|
const value = deepFind(this.data, key);
|
|
3135
3340
|
const resolvedValue = typeof value !== "undefined" ? value : deepFind(this.getContext().requestFiles ?? {}, key);
|
|
3136
3341
|
if (typeof resolvedValue !== "undefined") deepSet(clean, key, resolvedValue);
|
|
@@ -3141,7 +3346,8 @@ var Validator = class Validator {
|
|
|
3141
3346
|
* Return all validated input.
|
|
3142
3347
|
*/
|
|
3143
3348
|
validated() {
|
|
3144
|
-
|
|
3349
|
+
const excluded = new Set(this.instance?.getExcludedAttributes() ?? []);
|
|
3350
|
+
return Object.fromEntries(Object.entries(this.data).filter(([key]) => key in this.rules && !excluded.has(key)));
|
|
3145
3351
|
}
|
|
3146
3352
|
/**
|
|
3147
3353
|
* Return a portion of validated input
|
package/dist/index.d.ts
CHANGED
|
@@ -127,8 +127,8 @@ type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>);
|
|
|
127
127
|
type PluginValidationRuleNameMap = ValidationRuleAutocompleteMap & CustomValidationRuleNameMap;
|
|
128
128
|
type CustomParamableValidationRuleName = Extract<{ [K in keyof PluginValidationRuleNameMap]: PluginValidationRuleNameMap[K] extends 'paramable' ? K : never }[keyof PluginValidationRuleNameMap], string>;
|
|
129
129
|
type CustomPlainRuleName = Extract<{ [K in keyof PluginValidationRuleNameMap]: PluginValidationRuleNameMap[K] extends 'plain' ? K : never }[keyof PluginValidationRuleNameMap], string>;
|
|
130
|
-
type ParamableValidationRuleName = 'accepted_if' | 'after' | 'after_or_equal' | 'before' | 'before_or_equal' | 'between' | 'date_equals' | 'datetime' | 'declined_if' | 'digits_between' | 'different' | 'exists' | 'ends_with' | 'gt' | 'gte' | 'in' | 'includes' | 'lt' | 'lte' | 'max' | 'min' | 'not_in' | 'not_includes' | 'required_if' | 'required_unless' | 'required_with' | 'required_with_all' | 'required_without' | 'required_without_all' | 'same' | 'size' | 'starts_with' | 'unique' | CustomParamableValidationRuleName;
|
|
131
|
-
type PlainRuleName = 'accepted' | 'alpha' | 'alpha_dash' | 'alpha_num' | 'array' | 'array_unique' | 'bail' | 'boolean' | 'confirmed' | 'date' | 'declined' | 'digits' | 'email' | 'integer' | 'json' | 'not_regex' | 'nullable' | 'numeric' | 'object' | 'present' | 'regex' | 'required' | 'sometimes' | 'string' | 'url' | 'hex' | 'uuid' | CustomPlainRuleName;
|
|
130
|
+
type ParamableValidationRuleName = 'accepted_if' | 'after' | 'after_or_equal' | 'before' | 'before_or_equal' | 'between' | 'date_equals' | 'datetime' | 'declined_if' | 'digits_between' | 'different' | 'email' | 'exists' | 'ends_with' | 'gt' | 'gte' | 'in' | 'includes' | 'lt' | 'lte' | 'max' | 'min' | 'not_in' | 'not_includes' | 'multiple_of' | 'prohibited_unless' | 'prohibits' | 'required_if' | 'required_unless' | 'required_with' | 'required_with_all' | 'required_without' | 'required_without_all' | 'same' | 'size' | 'starts_with' | 'unique' | CustomParamableValidationRuleName;
|
|
131
|
+
type PlainRuleName = 'accepted' | 'alpha' | 'alpha_dash' | 'alpha_num' | 'array' | 'array_unique' | 'bail' | 'boolean' | 'confirmed' | 'date' | 'declined' | 'distinct' | 'digits' | 'email' | 'exclude' | 'filled' | 'ip' | 'ipv4' | 'ipv6' | 'integer' | 'json' | 'mac_address' | 'not_regex' | 'nullable' | 'numeric' | 'object' | 'present' | 'presentsame' | 'prohibited' | 'regex' | 'required' | 'sometimes' | 'string' | 'timezone' | 'url' | 'hex' | 'uuid' | CustomPlainRuleName;
|
|
132
132
|
type ValidationRuleName = ParamableValidationRuleName | PlainRuleName;
|
|
133
133
|
type MethodRules = Regex | In | NotIn | RequiredIf;
|
|
134
134
|
type ParamableRuleString = `${ParamableValidationRuleName}:${string}`;
|
|
@@ -308,10 +308,13 @@ interface ReplaceAttributeInterface {
|
|
|
308
308
|
replaceGte: (payload: replaceAttributePayload) => string;
|
|
309
309
|
replaceLte: (payload: replaceAttributePayload) => string;
|
|
310
310
|
replaceRequiredIf: (payload: replaceAttributePayload) => string;
|
|
311
|
+
replaceProhibitedUnless: (payload: replaceAttributePayload) => string;
|
|
312
|
+
replaceProhibits: (payload: replaceAttributePayload) => string;
|
|
311
313
|
replaceStartsWith: (payload: replaceAttributePayload) => string;
|
|
312
314
|
replaceRequiredUnless: (payload: replaceAttributePayload) => string;
|
|
313
315
|
replaceSame: (payload: replaceAttributePayload) => string;
|
|
314
316
|
replaceSize: (payload: replaceAttributePayload) => string;
|
|
317
|
+
replaceMultipleOf: (payload: replaceAttributePayload) => string;
|
|
315
318
|
replaceUnique: (payload: replaceAttributePayload) => string;
|
|
316
319
|
}
|
|
317
320
|
type ValidationCallback = (value: any, fail: (message: string) => void, attribute: string) => void;
|
|
@@ -402,6 +405,7 @@ declare function getValidationSize(value: any, hasNumericRule?: boolean): number
|
|
|
402
405
|
//#endregion
|
|
403
406
|
//#region src/BaseValidator.d.ts
|
|
404
407
|
declare class BaseValidator<D extends GenericObject = GenericObject> {
|
|
408
|
+
private excludedAttributes;
|
|
405
409
|
/**
|
|
406
410
|
* The lang used to return error messages
|
|
407
411
|
*/
|
|
@@ -497,6 +501,7 @@ declare class BaseValidator<D extends GenericObject = GenericObject> {
|
|
|
497
501
|
* @returns
|
|
498
502
|
*/
|
|
499
503
|
errors(): ErrorBag;
|
|
504
|
+
getExcludedAttributes(): string[];
|
|
500
505
|
/**
|
|
501
506
|
* Clear the error messages for the given keys.
|
|
502
507
|
* If no keys are provided, all error messages will be cleared.
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { domainToASCII } from "node:url";
|
|
3
|
+
import { isIP } from "node:net";
|
|
2
4
|
import customParseFormat from "dayjs/plugin/customParseFormat.js";
|
|
3
5
|
import dayjs from "dayjs";
|
|
4
6
|
|
|
@@ -73,10 +75,12 @@ var en_default = {
|
|
|
73
75
|
declined: "The :attribute must be declined.",
|
|
74
76
|
declined_if: "The :attribute must be declined when :other is :value.",
|
|
75
77
|
different: "The :attribute and :other must be different.",
|
|
78
|
+
distinct: "The :attribute field has a duplicate value.",
|
|
76
79
|
digits: "The :attribute must be :digits digits.",
|
|
77
80
|
digits_between: "The :attribute must be between :min and :max digits.",
|
|
78
81
|
email: "The :attribute must be a valid email address.",
|
|
79
82
|
ends_with: "The :attribute must end with one of the following: :values.",
|
|
83
|
+
filled: "The :attribute field must have a value.",
|
|
80
84
|
exists: "The selected :attribute is invalid.",
|
|
81
85
|
gt: {
|
|
82
86
|
number: "The :attribute must be greater than :value.",
|
|
@@ -94,6 +98,9 @@ var en_default = {
|
|
|
94
98
|
in: "The :attribute must be one of the following :values.",
|
|
95
99
|
includes: "The :attribute must include one of the following values: :values.",
|
|
96
100
|
integer: "The :attribute must be an integer.",
|
|
101
|
+
ip: "The :attribute must be a valid IP address.",
|
|
102
|
+
ipv4: "The :attribute must be a valid IPv4 address.",
|
|
103
|
+
ipv6: "The :attribute must be a valid IPv6 address.",
|
|
97
104
|
json: "The :attribute must be a valid JSON string.",
|
|
98
105
|
lt: {
|
|
99
106
|
number: "The :attribute must be less than :value.",
|
|
@@ -119,6 +126,7 @@ var en_default = {
|
|
|
119
126
|
array: "The :attribute must have at least :min items.",
|
|
120
127
|
object: "The :attribute must have at least :min items."
|
|
121
128
|
},
|
|
129
|
+
mac_address: "The :attribute must be a valid MAC address.",
|
|
122
130
|
not_in: "The selected :attribute is invalid.",
|
|
123
131
|
not_regex: "The :attribute format is invalid.",
|
|
124
132
|
not_includes: "The :attribute must not include any of the following values: :values.",
|
|
@@ -137,6 +145,10 @@ var en_default = {
|
|
|
137
145
|
upper_cases: "The :attribute must contain at least :amount uppercase letters."
|
|
138
146
|
},
|
|
139
147
|
present: "The :attribute field must be present.",
|
|
148
|
+
presentsame: "The :attribute field must be present.",
|
|
149
|
+
prohibited: "The :attribute field is prohibited.",
|
|
150
|
+
prohibited_unless: "The :attribute field is prohibited unless :other is in :values.",
|
|
151
|
+
prohibits: "The :attribute field prohibits :values from being present.",
|
|
140
152
|
regex: "The :attribute format is invalid.",
|
|
141
153
|
required: "The :attribute field is required.",
|
|
142
154
|
required_if: "The :attribute field is required when :other is :value.",
|
|
@@ -154,8 +166,10 @@ var en_default = {
|
|
|
154
166
|
object: "The :attribute must contain :size items."
|
|
155
167
|
},
|
|
156
168
|
string: "The :attribute must be a string.",
|
|
169
|
+
timezone: "The :attribute must be a valid timezone.",
|
|
157
170
|
unique: "The :attribute has already been taken.",
|
|
158
|
-
url: "The :attribute must have a valid URL format."
|
|
171
|
+
url: "The :attribute must have a valid URL format.",
|
|
172
|
+
multiple_of: "The :attribute must be a multiple of :value."
|
|
159
173
|
};
|
|
160
174
|
|
|
161
175
|
//#endregion
|
|
@@ -624,6 +638,12 @@ const replaceAttributes = {
|
|
|
624
638
|
};
|
|
625
639
|
return message.replace(/:other|:value/gi, (matched) => values[matched]);
|
|
626
640
|
},
|
|
641
|
+
replaceProhibitedUnless: function(payload) {
|
|
642
|
+
return this.replaceRequiredUnless(payload);
|
|
643
|
+
},
|
|
644
|
+
replaceProhibits: function({ message, parameters, getDisplayableAttribute }) {
|
|
645
|
+
return message.replace(":values", parameters.map((attribute) => getDisplayableAttribute(attribute)).join(", "));
|
|
646
|
+
},
|
|
627
647
|
replaceRequiredUnless: function({ message, parameters, getDisplayableAttribute }) {
|
|
628
648
|
const [other] = parameters;
|
|
629
649
|
const values = {
|
|
@@ -638,40 +658,14 @@ const replaceAttributes = {
|
|
|
638
658
|
replaceSize: function({ message, parameters }) {
|
|
639
659
|
return message.replace(":size", parameters[0]);
|
|
640
660
|
},
|
|
661
|
+
replaceMultipleOf: function({ message, parameters }) {
|
|
662
|
+
return message.replace(":value", parameters[0]);
|
|
663
|
+
},
|
|
641
664
|
replaceUnique: function({ message, parameters, data }) {
|
|
642
665
|
return message.replace(":value", data[parameters[1]]);
|
|
643
666
|
}
|
|
644
667
|
};
|
|
645
668
|
|
|
646
|
-
//#endregion
|
|
647
|
-
//#region src/Rules/closureValidationRule.ts
|
|
648
|
-
var ClosureValidationRule = class extends IRuleContract {
|
|
649
|
-
/**
|
|
650
|
-
* The callback that validates the attribute
|
|
651
|
-
*/
|
|
652
|
-
callback;
|
|
653
|
-
/**
|
|
654
|
-
* Indicates if the validation callback failed.
|
|
655
|
-
*/
|
|
656
|
-
failed = false;
|
|
657
|
-
constructor(callback) {
|
|
658
|
-
super();
|
|
659
|
-
this.callback = callback;
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Determine if the validation rule passes.
|
|
663
|
-
*/
|
|
664
|
-
passes(value, attribute) {
|
|
665
|
-
this.failed = false;
|
|
666
|
-
const result = this.callback(value, (message) => {
|
|
667
|
-
this.failed = true;
|
|
668
|
-
this.message = message;
|
|
669
|
-
}, attribute);
|
|
670
|
-
if (result instanceof Promise) return result.then(() => !this.failed);
|
|
671
|
-
return !this.failed;
|
|
672
|
-
}
|
|
673
|
-
};
|
|
674
|
-
|
|
675
669
|
//#endregion
|
|
676
670
|
//#region src/validators/validationData.ts
|
|
677
671
|
const validationData = {
|
|
@@ -712,6 +706,35 @@ const validationData = {
|
|
|
712
706
|
}
|
|
713
707
|
};
|
|
714
708
|
|
|
709
|
+
//#endregion
|
|
710
|
+
//#region src/Rules/closureValidationRule.ts
|
|
711
|
+
var ClosureValidationRule = class extends IRuleContract {
|
|
712
|
+
/**
|
|
713
|
+
* The callback that validates the attribute
|
|
714
|
+
*/
|
|
715
|
+
callback;
|
|
716
|
+
/**
|
|
717
|
+
* Indicates if the validation callback failed.
|
|
718
|
+
*/
|
|
719
|
+
failed = false;
|
|
720
|
+
constructor(callback) {
|
|
721
|
+
super();
|
|
722
|
+
this.callback = callback;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Determine if the validation rule passes.
|
|
726
|
+
*/
|
|
727
|
+
passes(value, attribute) {
|
|
728
|
+
this.failed = false;
|
|
729
|
+
const result = this.callback(value, (message) => {
|
|
730
|
+
this.failed = true;
|
|
731
|
+
this.message = message;
|
|
732
|
+
}, attribute);
|
|
733
|
+
if (result instanceof Promise) return result.then(() => !this.failed);
|
|
734
|
+
return !this.failed;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
|
|
715
738
|
//#endregion
|
|
716
739
|
//#region src/validators/validationRuleParser.ts
|
|
717
740
|
const validationRuleParser = {
|
|
@@ -959,7 +982,7 @@ var validateAttributes = class {
|
|
|
959
982
|
*/
|
|
960
983
|
validateDeclinedIf(value, parameters) {
|
|
961
984
|
this.requireParameterCount(2, parameters, "declined_if");
|
|
962
|
-
const other =
|
|
985
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
963
986
|
if (!other) return true;
|
|
964
987
|
if (parameters.slice(1).indexOf(other) !== -1) return this.validateDeclined(value);
|
|
965
988
|
return true;
|
|
@@ -969,7 +992,7 @@ var validateAttributes = class {
|
|
|
969
992
|
*/
|
|
970
993
|
validateDifferent(value, parameters) {
|
|
971
994
|
this.requireParameterCount(1, parameters, "different");
|
|
972
|
-
const other =
|
|
995
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
973
996
|
if (!sameType(value, other)) return true;
|
|
974
997
|
if (value !== null && typeof value === "object") return !deepEqual(value, other);
|
|
975
998
|
return value !== other;
|
|
@@ -1004,13 +1027,23 @@ var validateAttributes = class {
|
|
|
1004
1027
|
/**
|
|
1005
1028
|
* Validate that an attribute is a valid email address.
|
|
1006
1029
|
*/
|
|
1007
|
-
validateEmail(value) {
|
|
1030
|
+
validateEmail(value, parameters = []) {
|
|
1008
1031
|
if (typeof value !== "string") return false;
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1032
|
+
const normalized = parameters.map((parameter) => parameter.replace(/[{}]/g, "").toLowerCase());
|
|
1033
|
+
if (normalized.length === 0 || normalized.includes("filter") || normalized.includes("rfc")) {
|
|
1034
|
+
if (!this.isEmailByFilter(value)) return false;
|
|
1035
|
+
}
|
|
1036
|
+
if (normalized.includes("strict") && !this.isStrictEmail(value)) return false;
|
|
1037
|
+
if (normalized.includes("dns") && !this.hasResolvableEmailDomain(value)) return false;
|
|
1038
|
+
if (normalized.includes("spoof") && !this.isSpoofSafeEmail(value)) return false;
|
|
1039
|
+
return true;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Validate that a present attribute is not empty.
|
|
1043
|
+
*/
|
|
1044
|
+
validateFilled(value) {
|
|
1045
|
+
if (typeof value === "undefined") return true;
|
|
1046
|
+
return this.validateRequired(value);
|
|
1014
1047
|
}
|
|
1015
1048
|
/**
|
|
1016
1049
|
* Validate the attribute ends with a given substring.
|
|
@@ -1070,7 +1103,7 @@ var validateAttributes = class {
|
|
|
1070
1103
|
*/
|
|
1071
1104
|
validateRequiredIf(value, parameters) {
|
|
1072
1105
|
this.requireParameterCount(2, parameters, "required_if");
|
|
1073
|
-
const other =
|
|
1106
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
1074
1107
|
if (typeof other === "undefined") return true;
|
|
1075
1108
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) !== -1) return this.validateRequired(value);
|
|
1076
1109
|
return true;
|
|
@@ -1080,7 +1113,7 @@ var validateAttributes = class {
|
|
|
1080
1113
|
*/
|
|
1081
1114
|
validateRequiredUnless(value, parameters) {
|
|
1082
1115
|
this.requireParameterCount(2, parameters, "required_unless");
|
|
1083
|
-
let other =
|
|
1116
|
+
let other = this.getAttributeValue(parameters[0]);
|
|
1084
1117
|
other = typeof other === "undefined" ? null : other;
|
|
1085
1118
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) === -1) return this.validateRequired(value);
|
|
1086
1119
|
return true;
|
|
@@ -1117,14 +1150,14 @@ var validateAttributes = class {
|
|
|
1117
1150
|
* Determine if any of the given attributes fail the required test.
|
|
1118
1151
|
*/
|
|
1119
1152
|
anyFailingRequired(attributes) {
|
|
1120
|
-
for (let i = 0; i < attributes.length; i++) if (!this.validateRequired(
|
|
1153
|
+
for (let i = 0; i < attributes.length; i++) if (!this.validateRequired(this.getAttributeValue(attributes[i]))) return true;
|
|
1121
1154
|
return false;
|
|
1122
1155
|
}
|
|
1123
1156
|
/**
|
|
1124
1157
|
* Determine if all of the given attributes fail the required test.
|
|
1125
1158
|
*/
|
|
1126
1159
|
allFailingRequired(attributes) {
|
|
1127
|
-
for (let i = 0; i < attributes.length; i++) if (this.validateRequired(
|
|
1160
|
+
for (let i = 0; i < attributes.length; i++) if (this.validateRequired(this.getAttributeValue(attributes[i]))) return false;
|
|
1128
1161
|
return true;
|
|
1129
1162
|
}
|
|
1130
1163
|
/**
|
|
@@ -1166,7 +1199,13 @@ var validateAttributes = class {
|
|
|
1166
1199
|
* Validate that an attribute exists even if not filled.
|
|
1167
1200
|
*/
|
|
1168
1201
|
validatePresent(value, parameters, attribute) {
|
|
1169
|
-
return typeof
|
|
1202
|
+
return typeof this.getAttributeValue(attribute) !== "undefined";
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Alias of the present rule kept for compatibility with requested rule naming.
|
|
1206
|
+
*/
|
|
1207
|
+
validatePresentsame(value, parameters, attribute) {
|
|
1208
|
+
return this.validatePresent(value, parameters, attribute);
|
|
1170
1209
|
}
|
|
1171
1210
|
/**
|
|
1172
1211
|
* Validate that an attribute is an integer.
|
|
@@ -1188,12 +1227,103 @@ var validateAttributes = class {
|
|
|
1188
1227
|
return true;
|
|
1189
1228
|
}
|
|
1190
1229
|
/**
|
|
1230
|
+
* Validate that an attribute is prohibited.
|
|
1231
|
+
*/
|
|
1232
|
+
validateProhibited(value) {
|
|
1233
|
+
return !this.validateRequired(value);
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Validate that an attribute is prohibited unless another field matches one of the given values.
|
|
1237
|
+
*/
|
|
1238
|
+
validateProhibitedUnless(value, parameters) {
|
|
1239
|
+
this.requireParameterCount(2, parameters, "prohibited_unless");
|
|
1240
|
+
let other = this.getAttributeValue(parameters[0]);
|
|
1241
|
+
other = typeof other === "undefined" ? null : other;
|
|
1242
|
+
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) !== -1) return true;
|
|
1243
|
+
return this.validateProhibited(value);
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Validate that present attributes prohibit other attributes from being present.
|
|
1247
|
+
*/
|
|
1248
|
+
validateProhibits(value, parameters) {
|
|
1249
|
+
this.requireParameterCount(1, parameters, "prohibits");
|
|
1250
|
+
if (!this.validateRequired(value)) return true;
|
|
1251
|
+
return parameters.every((attribute) => this.validateProhibited(this.getAttributeValue(attribute)));
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Validate that an attribute is a valid IP address.
|
|
1255
|
+
*/
|
|
1256
|
+
validateIp(value) {
|
|
1257
|
+
return typeof value === "string" && isIP(value) !== 0;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Validate that an attribute is a valid IPv4 address.
|
|
1261
|
+
*/
|
|
1262
|
+
validateIpv4(value) {
|
|
1263
|
+
return typeof value === "string" && isIP(value) === 4;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Validate that an attribute is a valid IPv6 address.
|
|
1267
|
+
*/
|
|
1268
|
+
validateIpv6(value) {
|
|
1269
|
+
return typeof value === "string" && isIP(value) === 6;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Validate that an attribute is a valid MAC address.
|
|
1273
|
+
*/
|
|
1274
|
+
validateMacAddress(value) {
|
|
1275
|
+
return typeof value === "string" && /^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i.test(value);
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Validate that an attribute is a valid timezone identifier.
|
|
1279
|
+
*/
|
|
1280
|
+
validateTimezone(value) {
|
|
1281
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
1282
|
+
if (typeof Intl.supportedValuesOf === "function" && Intl.supportedValuesOf("timeZone").includes(value)) return true;
|
|
1283
|
+
try {
|
|
1284
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value });
|
|
1285
|
+
return true;
|
|
1286
|
+
} catch {
|
|
1287
|
+
return false;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Validate that an attribute is a multiple of the given value.
|
|
1292
|
+
*/
|
|
1293
|
+
validateMultipleOf(value, parameters) {
|
|
1294
|
+
this.requireParameterCount(1, parameters, "multiple_of");
|
|
1295
|
+
const numericValue = Number(value);
|
|
1296
|
+
const divisor = Number(parameters[0]);
|
|
1297
|
+
if (!Number.isFinite(numericValue) || !Number.isFinite(divisor) || divisor === 0) return false;
|
|
1298
|
+
const scale = 10 ** Math.max(this.getDecimalPlaces(value), this.getDecimalPlaces(parameters[0]));
|
|
1299
|
+
const scaledValue = Math.round(numericValue * scale);
|
|
1300
|
+
const scaledDivisor = Math.round(divisor * scale);
|
|
1301
|
+
return scaledDivisor !== 0 && scaledValue % scaledDivisor === 0;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Validate that values matched by a wildcard attribute are distinct.
|
|
1305
|
+
*/
|
|
1306
|
+
validateDistinct(value, parameters, attribute, primaryAttribute = attribute) {
|
|
1307
|
+
if (Array.isArray(value)) return new Set(value.map((entry) => this.normalizeDistinctValue(entry, parameters))).size === value.length;
|
|
1308
|
+
if (primaryAttribute.indexOf("*") === -1) return true;
|
|
1309
|
+
const strict = parameters.includes("strict");
|
|
1310
|
+
const values = this.getDistinctValues(primaryAttribute);
|
|
1311
|
+
const current = this.normalizeDistinctValue(value, parameters);
|
|
1312
|
+
let matches = 0;
|
|
1313
|
+
for (const candidate of values) {
|
|
1314
|
+
const normalized = this.normalizeDistinctValue(candidate, parameters);
|
|
1315
|
+
if (strict ? normalized === current : normalized == current) matches += 1;
|
|
1316
|
+
if (matches > 1) return false;
|
|
1317
|
+
}
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1191
1321
|
* Validate that an attribute is greater than another attribute.
|
|
1192
1322
|
*/
|
|
1193
1323
|
validateGt(value, parameters, attribute) {
|
|
1194
1324
|
this.requireParameterCount(1, parameters, "gt");
|
|
1195
1325
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1196
|
-
const compartedToValue =
|
|
1326
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1197
1327
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) > compartedToValue;
|
|
1198
1328
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1199
1329
|
return getSize(value) > getSize(compartedToValue);
|
|
@@ -1204,7 +1334,7 @@ var validateAttributes = class {
|
|
|
1204
1334
|
validateGte(value, parameters, attribute) {
|
|
1205
1335
|
this.requireParameterCount(1, parameters, "gte");
|
|
1206
1336
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1207
|
-
const compartedToValue =
|
|
1337
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1208
1338
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) >= compartedToValue;
|
|
1209
1339
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1210
1340
|
return getSize(value) >= getSize(compartedToValue);
|
|
@@ -1215,7 +1345,7 @@ var validateAttributes = class {
|
|
|
1215
1345
|
validateLt(value, parameters, attribute) {
|
|
1216
1346
|
this.requireParameterCount(1, parameters, "lt");
|
|
1217
1347
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1218
|
-
const compartedToValue =
|
|
1348
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1219
1349
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) < compartedToValue;
|
|
1220
1350
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1221
1351
|
return getSize(value) < getSize(compartedToValue);
|
|
@@ -1226,7 +1356,7 @@ var validateAttributes = class {
|
|
|
1226
1356
|
validateLte(value, parameters, attribute) {
|
|
1227
1357
|
this.requireParameterCount(1, parameters, "lte");
|
|
1228
1358
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1229
|
-
const compartedToValue =
|
|
1359
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1230
1360
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) <= compartedToValue;
|
|
1231
1361
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1232
1362
|
return getSize(value) <= getSize(compartedToValue);
|
|
@@ -1284,7 +1414,7 @@ var validateAttributes = class {
|
|
|
1284
1414
|
compareDates(value, parameter, operator, rule) {
|
|
1285
1415
|
value = toDate(value);
|
|
1286
1416
|
if (!value) throw `Validation rule ${rule} requires the field under valation to be a date.`;
|
|
1287
|
-
const compartedToValue = toDate(
|
|
1417
|
+
const compartedToValue = toDate(this.getAttributeValue(parameter) || parameter);
|
|
1288
1418
|
if (!compartedToValue) throw `Validation rule ${rule} requires the parameter to be a date.`;
|
|
1289
1419
|
return compare(value.getTime(), compartedToValue.getTime(), operator);
|
|
1290
1420
|
}
|
|
@@ -1296,6 +1426,10 @@ var validateAttributes = class {
|
|
|
1296
1426
|
}
|
|
1297
1427
|
/**
|
|
1298
1428
|
* Prepare the values for validation
|
|
1429
|
+
*
|
|
1430
|
+
* @param other The value to compare against
|
|
1431
|
+
* @param parameters The parameters for the validation rule
|
|
1432
|
+
* @returns The prepared values for validation
|
|
1299
1433
|
*/
|
|
1300
1434
|
parseDependentRuleParameters(other, parameters) {
|
|
1301
1435
|
let values = parameters.slice(1);
|
|
@@ -1304,6 +1438,55 @@ var validateAttributes = class {
|
|
|
1304
1438
|
if (typeof other === "boolean") values = convertValuesToBoolean(values);
|
|
1305
1439
|
return values;
|
|
1306
1440
|
}
|
|
1441
|
+
getAttributeValue(attribute) {
|
|
1442
|
+
const dataValue = deepFind(this.data, attribute);
|
|
1443
|
+
if (typeof dataValue !== "undefined") return dataValue;
|
|
1444
|
+
return deepFind(this.context.requestFiles ?? {}, attribute);
|
|
1445
|
+
}
|
|
1446
|
+
getDistinctValues(primaryAttribute) {
|
|
1447
|
+
const gathered = validationData.initializeAndGatherData(primaryAttribute, this.data);
|
|
1448
|
+
const pattern = new RegExp(`^${primaryAttribute.replace(/\./g, "\\.").replace(/\*/g, "[^.]+")}$`);
|
|
1449
|
+
return Object.keys(gathered).filter((attribute) => pattern.test(attribute)).map((attribute) => deepFind(this.data, attribute)).filter((value) => typeof value !== "undefined");
|
|
1450
|
+
}
|
|
1451
|
+
normalizeDistinctValue(value, parameters) {
|
|
1452
|
+
if (parameters.includes("ignore_case") && typeof value === "string") return value.toLowerCase();
|
|
1453
|
+
return value;
|
|
1454
|
+
}
|
|
1455
|
+
isEmailByFilter(value) {
|
|
1456
|
+
return value.toLowerCase().match(/^[^\s@]+@[^\s@]+\.[^\s@]{2,24}$/) !== null;
|
|
1457
|
+
}
|
|
1458
|
+
isStrictEmail(value) {
|
|
1459
|
+
if (!this.isEmailByFilter(value)) return false;
|
|
1460
|
+
const [localPart, domain] = value.split("@");
|
|
1461
|
+
if (!localPart || !domain || localPart.length > 64 || domain.length > 255) return false;
|
|
1462
|
+
if (localPart.startsWith(".") || localPart.endsWith(".") || localPart.includes("..")) return false;
|
|
1463
|
+
return domain.split(".").every((label) => {
|
|
1464
|
+
return label.length > 0 && label.length <= 63 && /^[a-z0-9-]+$/i.test(label) && !label.startsWith("-") && !label.endsWith("-");
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
hasResolvableEmailDomain(value) {
|
|
1468
|
+
if (!this.isStrictEmail(value)) return false;
|
|
1469
|
+
const domain = value.split("@")[1];
|
|
1470
|
+
const asciiDomain = domainToASCII(domain);
|
|
1471
|
+
return asciiDomain.length > 0 && asciiDomain.includes(".");
|
|
1472
|
+
}
|
|
1473
|
+
isSpoofSafeEmail(value) {
|
|
1474
|
+
if (!this.isEmailByFilter(value)) return false;
|
|
1475
|
+
const [localPart, domain] = value.split("@");
|
|
1476
|
+
return domainToASCII(domain).length > 0 && !this.hasAsciiControlCharacters(value) && localPart.normalize("NFKC") === localPart && domain.normalize("NFKC") === domain;
|
|
1477
|
+
}
|
|
1478
|
+
hasAsciiControlCharacters(value) {
|
|
1479
|
+
for (let index = 0; index < value.length; index++) {
|
|
1480
|
+
const codePoint = value.charCodeAt(index);
|
|
1481
|
+
if (codePoint <= 31 || codePoint === 127) return true;
|
|
1482
|
+
}
|
|
1483
|
+
return false;
|
|
1484
|
+
}
|
|
1485
|
+
getDecimalPlaces(value) {
|
|
1486
|
+
const stringValue = String(value).toLowerCase();
|
|
1487
|
+
if (!stringValue.includes(".")) return 0;
|
|
1488
|
+
return stringValue.split(".")[1]?.length ?? 0;
|
|
1489
|
+
}
|
|
1307
1490
|
};
|
|
1308
1491
|
|
|
1309
1492
|
//#endregion
|
|
@@ -1372,6 +1555,7 @@ const implicitRues = [
|
|
|
1372
1555
|
"declined_if",
|
|
1373
1556
|
"filled",
|
|
1374
1557
|
"present",
|
|
1558
|
+
"presentsame",
|
|
1375
1559
|
"required",
|
|
1376
1560
|
"required_if",
|
|
1377
1561
|
"required_unless",
|
|
@@ -2000,6 +2184,7 @@ var replaceAttributePayload = class {
|
|
|
2000
2184
|
//#endregion
|
|
2001
2185
|
//#region src/BaseValidator.ts
|
|
2002
2186
|
var BaseValidator = class BaseValidator {
|
|
2187
|
+
excludedAttributes = /* @__PURE__ */ new Set();
|
|
2003
2188
|
/**
|
|
2004
2189
|
* The lang used to return error messages
|
|
2005
2190
|
*/
|
|
@@ -2144,6 +2329,9 @@ var BaseValidator = class BaseValidator {
|
|
|
2144
2329
|
errors() {
|
|
2145
2330
|
return this.messages;
|
|
2146
2331
|
}
|
|
2332
|
+
getExcludedAttributes() {
|
|
2333
|
+
return [...this.excludedAttributes];
|
|
2334
|
+
}
|
|
2147
2335
|
/**
|
|
2148
2336
|
* Clear the error messages for the given keys.
|
|
2149
2337
|
* If no keys are provided, all error messages will be cleared.
|
|
@@ -2260,6 +2448,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2260
2448
|
runAllValidations() {
|
|
2261
2449
|
this.messages = new ErrorBag();
|
|
2262
2450
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2451
|
+
this.excludedAttributes.clear();
|
|
2263
2452
|
for (const property in this.rules) if (this.runValidation(property) === false) break;
|
|
2264
2453
|
}
|
|
2265
2454
|
/**
|
|
@@ -2268,6 +2457,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2268
2457
|
async runAllValidationsAsync() {
|
|
2269
2458
|
this.messages = new ErrorBag();
|
|
2270
2459
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2460
|
+
this.excludedAttributes.clear();
|
|
2271
2461
|
for (const property in this.rules) if (await this.runValidationAsync(property) === false) break;
|
|
2272
2462
|
}
|
|
2273
2463
|
/**
|
|
@@ -2290,26 +2480,39 @@ var BaseValidator = class BaseValidator {
|
|
|
2290
2480
|
* Run validation rules for the specified property and stop validation if needed
|
|
2291
2481
|
*/
|
|
2292
2482
|
runValidation(property) {
|
|
2293
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2483
|
+
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property])) {
|
|
2484
|
+
if (validationRuleParser.hasRule(property, ["exclude"], this.rules)) {
|
|
2485
|
+
this.excludedAttributes.add(property);
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
2488
|
+
for (let i = 0; i < this.rules[property].length; i++) {
|
|
2489
|
+
this.validateAttribute(property, this.rules[property][i]);
|
|
2490
|
+
if (this.messages.keys().length > 0 && this.stopOnFirstFailureFlag === true) return false;
|
|
2491
|
+
if (this.shouldStopValidating(property)) break;
|
|
2492
|
+
}
|
|
2297
2493
|
}
|
|
2298
2494
|
}
|
|
2299
2495
|
/**
|
|
2300
2496
|
* Run validation rules for the specified property asynchronously and stop validation if needed
|
|
2301
2497
|
*/
|
|
2302
2498
|
async runValidationAsync(property) {
|
|
2303
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2499
|
+
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property])) {
|
|
2500
|
+
if (validationRuleParser.hasRule(property, ["exclude"], this.rules)) {
|
|
2501
|
+
this.excludedAttributes.add(property);
|
|
2502
|
+
return;
|
|
2503
|
+
}
|
|
2504
|
+
for (let i = 0; i < this.rules[property].length; i++) {
|
|
2505
|
+
await this.validateAttribute(property, this.rules[property][i]);
|
|
2506
|
+
if (this.messages.keys().length > 0 && this.stopOnFirstFailureFlag === true) return false;
|
|
2507
|
+
if (this.shouldStopValidating(property)) break;
|
|
2508
|
+
}
|
|
2307
2509
|
}
|
|
2308
2510
|
}
|
|
2309
2511
|
/**
|
|
2310
2512
|
* Check if we should stop further validations on a given attribute.
|
|
2311
2513
|
*/
|
|
2312
2514
|
shouldStopValidating(attribute) {
|
|
2515
|
+
if (this.excludedAttributes.has(attribute)) return true;
|
|
2313
2516
|
return this.messages.has(attribute) && validationRuleParser.hasRule(attribute, ["bail"], this.rules);
|
|
2314
2517
|
}
|
|
2315
2518
|
/**
|
|
@@ -2334,7 +2537,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2334
2537
|
const method = `validate${buildValidationMethodName(rule)}`;
|
|
2335
2538
|
if (rule !== "" && typeof this.validateAttributes[method] === "undefined") throw `Rule ${rule} is not valid`;
|
|
2336
2539
|
if (!validatable) return;
|
|
2337
|
-
const validation = this.validateAttributes[method](value, parameters, attribute);
|
|
2540
|
+
const validation = this.validateAttributes[method](value, parameters, attribute, this.getPrimaryAttribute(attribute));
|
|
2338
2541
|
if (validation instanceof Promise) return validation.then((result) => {
|
|
2339
2542
|
if (!result) this.addFailure(attribute, rule, value, parameters);
|
|
2340
2543
|
});
|
|
@@ -3099,8 +3302,10 @@ var Validator = class Validator {
|
|
|
3099
3302
|
*/
|
|
3100
3303
|
validatedData() {
|
|
3101
3304
|
const validKeys = Object.keys(this.rules);
|
|
3305
|
+
const excluded = new Set(this.instance?.getExcludedAttributes() ?? []);
|
|
3102
3306
|
const clean = {};
|
|
3103
3307
|
for (const key of validKeys) {
|
|
3308
|
+
if (excluded.has(key)) continue;
|
|
3104
3309
|
const value = deepFind(this.data, key);
|
|
3105
3310
|
const resolvedValue = typeof value !== "undefined" ? value : deepFind(this.getContext().requestFiles ?? {}, key);
|
|
3106
3311
|
if (typeof resolvedValue !== "undefined") deepSet(clean, key, resolvedValue);
|
|
@@ -3111,7 +3316,8 @@ var Validator = class Validator {
|
|
|
3111
3316
|
* Return all validated input.
|
|
3112
3317
|
*/
|
|
3113
3318
|
validated() {
|
|
3114
|
-
|
|
3319
|
+
const excluded = new Set(this.instance?.getExcludedAttributes() ?? []);
|
|
3320
|
+
return Object.fromEntries(Object.entries(this.data).filter(([key]) => key in this.rules && !excluded.has(key)));
|
|
3115
3321
|
}
|
|
3116
3322
|
/**
|
|
3117
3323
|
* Return a portion of validated input
|