kanun 1.0.3 → 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 +271 -62
- package/dist/index.d.ts +7 -2
- package/dist/index.js +271 -62
- 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");
|
|
@@ -49,10 +51,12 @@ function getValidatorContext() {
|
|
|
49
51
|
* @returns
|
|
50
52
|
*/
|
|
51
53
|
function useValidatorContext(context = {}) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const currentContext = validatorContextStorage.getStore();
|
|
55
|
+
if (currentContext) {
|
|
56
|
+
Object.assign(currentContext, context);
|
|
57
|
+
return currentContext;
|
|
58
|
+
}
|
|
59
|
+
const nextContext = { ...context };
|
|
56
60
|
validatorContextStorage.enterWith(nextContext);
|
|
57
61
|
return nextContext;
|
|
58
62
|
}
|
|
@@ -101,10 +105,12 @@ var en_default = {
|
|
|
101
105
|
declined: "The :attribute must be declined.",
|
|
102
106
|
declined_if: "The :attribute must be declined when :other is :value.",
|
|
103
107
|
different: "The :attribute and :other must be different.",
|
|
108
|
+
distinct: "The :attribute field has a duplicate value.",
|
|
104
109
|
digits: "The :attribute must be :digits digits.",
|
|
105
110
|
digits_between: "The :attribute must be between :min and :max digits.",
|
|
106
111
|
email: "The :attribute must be a valid email address.",
|
|
107
112
|
ends_with: "The :attribute must end with one of the following: :values.",
|
|
113
|
+
filled: "The :attribute field must have a value.",
|
|
108
114
|
exists: "The selected :attribute is invalid.",
|
|
109
115
|
gt: {
|
|
110
116
|
number: "The :attribute must be greater than :value.",
|
|
@@ -122,6 +128,9 @@ var en_default = {
|
|
|
122
128
|
in: "The :attribute must be one of the following :values.",
|
|
123
129
|
includes: "The :attribute must include one of the following values: :values.",
|
|
124
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.",
|
|
125
134
|
json: "The :attribute must be a valid JSON string.",
|
|
126
135
|
lt: {
|
|
127
136
|
number: "The :attribute must be less than :value.",
|
|
@@ -147,6 +156,7 @@ var en_default = {
|
|
|
147
156
|
array: "The :attribute must have at least :min items.",
|
|
148
157
|
object: "The :attribute must have at least :min items."
|
|
149
158
|
},
|
|
159
|
+
mac_address: "The :attribute must be a valid MAC address.",
|
|
150
160
|
not_in: "The selected :attribute is invalid.",
|
|
151
161
|
not_regex: "The :attribute format is invalid.",
|
|
152
162
|
not_includes: "The :attribute must not include any of the following values: :values.",
|
|
@@ -165,6 +175,10 @@ var en_default = {
|
|
|
165
175
|
upper_cases: "The :attribute must contain at least :amount uppercase letters."
|
|
166
176
|
},
|
|
167
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.",
|
|
168
182
|
regex: "The :attribute format is invalid.",
|
|
169
183
|
required: "The :attribute field is required.",
|
|
170
184
|
required_if: "The :attribute field is required when :other is :value.",
|
|
@@ -182,8 +196,10 @@ var en_default = {
|
|
|
182
196
|
object: "The :attribute must contain :size items."
|
|
183
197
|
},
|
|
184
198
|
string: "The :attribute must be a string.",
|
|
199
|
+
timezone: "The :attribute must be a valid timezone.",
|
|
185
200
|
unique: "The :attribute has already been taken.",
|
|
186
|
-
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."
|
|
187
203
|
};
|
|
188
204
|
|
|
189
205
|
//#endregion
|
|
@@ -652,6 +668,12 @@ const replaceAttributes = {
|
|
|
652
668
|
};
|
|
653
669
|
return message.replace(/:other|:value/gi, (matched) => values[matched]);
|
|
654
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
|
+
},
|
|
655
677
|
replaceRequiredUnless: function({ message, parameters, getDisplayableAttribute }) {
|
|
656
678
|
const [other] = parameters;
|
|
657
679
|
const values = {
|
|
@@ -666,40 +688,14 @@ const replaceAttributes = {
|
|
|
666
688
|
replaceSize: function({ message, parameters }) {
|
|
667
689
|
return message.replace(":size", parameters[0]);
|
|
668
690
|
},
|
|
691
|
+
replaceMultipleOf: function({ message, parameters }) {
|
|
692
|
+
return message.replace(":value", parameters[0]);
|
|
693
|
+
},
|
|
669
694
|
replaceUnique: function({ message, parameters, data }) {
|
|
670
695
|
return message.replace(":value", data[parameters[1]]);
|
|
671
696
|
}
|
|
672
697
|
};
|
|
673
698
|
|
|
674
|
-
//#endregion
|
|
675
|
-
//#region src/Rules/closureValidationRule.ts
|
|
676
|
-
var ClosureValidationRule = class extends IRuleContract {
|
|
677
|
-
/**
|
|
678
|
-
* The callback that validates the attribute
|
|
679
|
-
*/
|
|
680
|
-
callback;
|
|
681
|
-
/**
|
|
682
|
-
* Indicates if the validation callback failed.
|
|
683
|
-
*/
|
|
684
|
-
failed = false;
|
|
685
|
-
constructor(callback) {
|
|
686
|
-
super();
|
|
687
|
-
this.callback = callback;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Determine if the validation rule passes.
|
|
691
|
-
*/
|
|
692
|
-
passes(value, attribute) {
|
|
693
|
-
this.failed = false;
|
|
694
|
-
const result = this.callback(value, (message) => {
|
|
695
|
-
this.failed = true;
|
|
696
|
-
this.message = message;
|
|
697
|
-
}, attribute);
|
|
698
|
-
if (result instanceof Promise) return result.then(() => !this.failed);
|
|
699
|
-
return !this.failed;
|
|
700
|
-
}
|
|
701
|
-
};
|
|
702
|
-
|
|
703
699
|
//#endregion
|
|
704
700
|
//#region src/validators/validationData.ts
|
|
705
701
|
const validationData = {
|
|
@@ -740,6 +736,35 @@ const validationData = {
|
|
|
740
736
|
}
|
|
741
737
|
};
|
|
742
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
|
+
|
|
743
768
|
//#endregion
|
|
744
769
|
//#region src/validators/validationRuleParser.ts
|
|
745
770
|
const validationRuleParser = {
|
|
@@ -987,7 +1012,7 @@ var validateAttributes = class {
|
|
|
987
1012
|
*/
|
|
988
1013
|
validateDeclinedIf(value, parameters) {
|
|
989
1014
|
this.requireParameterCount(2, parameters, "declined_if");
|
|
990
|
-
const other =
|
|
1015
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
991
1016
|
if (!other) return true;
|
|
992
1017
|
if (parameters.slice(1).indexOf(other) !== -1) return this.validateDeclined(value);
|
|
993
1018
|
return true;
|
|
@@ -997,7 +1022,7 @@ var validateAttributes = class {
|
|
|
997
1022
|
*/
|
|
998
1023
|
validateDifferent(value, parameters) {
|
|
999
1024
|
this.requireParameterCount(1, parameters, "different");
|
|
1000
|
-
const other =
|
|
1025
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
1001
1026
|
if (!sameType(value, other)) return true;
|
|
1002
1027
|
if (value !== null && typeof value === "object") return !deepEqual(value, other);
|
|
1003
1028
|
return value !== other;
|
|
@@ -1032,13 +1057,23 @@ var validateAttributes = class {
|
|
|
1032
1057
|
/**
|
|
1033
1058
|
* Validate that an attribute is a valid email address.
|
|
1034
1059
|
*/
|
|
1035
|
-
validateEmail(value) {
|
|
1060
|
+
validateEmail(value, parameters = []) {
|
|
1036
1061
|
if (typeof value !== "string") return false;
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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);
|
|
1042
1077
|
}
|
|
1043
1078
|
/**
|
|
1044
1079
|
* Validate the attribute ends with a given substring.
|
|
@@ -1087,6 +1122,7 @@ var validateAttributes = class {
|
|
|
1087
1122
|
*/
|
|
1088
1123
|
validateRequired(value) {
|
|
1089
1124
|
if (value === null || typeof value === "undefined") return false;
|
|
1125
|
+
else if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
1090
1126
|
else if (typeof value === "string" && value.trim() === "") return false;
|
|
1091
1127
|
else if (Array.isArray(value) && value.length < 1) return false;
|
|
1092
1128
|
else if (typeof value === "object" && Object.keys(value).length < 1) return false;
|
|
@@ -1097,7 +1133,7 @@ var validateAttributes = class {
|
|
|
1097
1133
|
*/
|
|
1098
1134
|
validateRequiredIf(value, parameters) {
|
|
1099
1135
|
this.requireParameterCount(2, parameters, "required_if");
|
|
1100
|
-
const other =
|
|
1136
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
1101
1137
|
if (typeof other === "undefined") return true;
|
|
1102
1138
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) !== -1) return this.validateRequired(value);
|
|
1103
1139
|
return true;
|
|
@@ -1107,7 +1143,7 @@ var validateAttributes = class {
|
|
|
1107
1143
|
*/
|
|
1108
1144
|
validateRequiredUnless(value, parameters) {
|
|
1109
1145
|
this.requireParameterCount(2, parameters, "required_unless");
|
|
1110
|
-
let other =
|
|
1146
|
+
let other = this.getAttributeValue(parameters[0]);
|
|
1111
1147
|
other = typeof other === "undefined" ? null : other;
|
|
1112
1148
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) === -1) return this.validateRequired(value);
|
|
1113
1149
|
return true;
|
|
@@ -1144,14 +1180,14 @@ var validateAttributes = class {
|
|
|
1144
1180
|
* Determine if any of the given attributes fail the required test.
|
|
1145
1181
|
*/
|
|
1146
1182
|
anyFailingRequired(attributes) {
|
|
1147
|
-
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;
|
|
1148
1184
|
return false;
|
|
1149
1185
|
}
|
|
1150
1186
|
/**
|
|
1151
1187
|
* Determine if all of the given attributes fail the required test.
|
|
1152
1188
|
*/
|
|
1153
1189
|
allFailingRequired(attributes) {
|
|
1154
|
-
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;
|
|
1155
1191
|
return true;
|
|
1156
1192
|
}
|
|
1157
1193
|
/**
|
|
@@ -1193,7 +1229,13 @@ var validateAttributes = class {
|
|
|
1193
1229
|
* Validate that an attribute exists even if not filled.
|
|
1194
1230
|
*/
|
|
1195
1231
|
validatePresent(value, parameters, attribute) {
|
|
1196
|
-
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);
|
|
1197
1239
|
}
|
|
1198
1240
|
/**
|
|
1199
1241
|
* Validate that an attribute is an integer.
|
|
@@ -1215,12 +1257,103 @@ var validateAttributes = class {
|
|
|
1215
1257
|
return true;
|
|
1216
1258
|
}
|
|
1217
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
|
+
/**
|
|
1218
1351
|
* Validate that an attribute is greater than another attribute.
|
|
1219
1352
|
*/
|
|
1220
1353
|
validateGt(value, parameters, attribute) {
|
|
1221
1354
|
this.requireParameterCount(1, parameters, "gt");
|
|
1222
1355
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1223
|
-
const compartedToValue =
|
|
1356
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1224
1357
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) > compartedToValue;
|
|
1225
1358
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1226
1359
|
return getSize(value) > getSize(compartedToValue);
|
|
@@ -1231,7 +1364,7 @@ var validateAttributes = class {
|
|
|
1231
1364
|
validateGte(value, parameters, attribute) {
|
|
1232
1365
|
this.requireParameterCount(1, parameters, "gte");
|
|
1233
1366
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1234
|
-
const compartedToValue =
|
|
1367
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1235
1368
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) >= compartedToValue;
|
|
1236
1369
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1237
1370
|
return getSize(value) >= getSize(compartedToValue);
|
|
@@ -1242,7 +1375,7 @@ var validateAttributes = class {
|
|
|
1242
1375
|
validateLt(value, parameters, attribute) {
|
|
1243
1376
|
this.requireParameterCount(1, parameters, "lt");
|
|
1244
1377
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1245
|
-
const compartedToValue =
|
|
1378
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1246
1379
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) < compartedToValue;
|
|
1247
1380
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1248
1381
|
return getSize(value) < getSize(compartedToValue);
|
|
@@ -1253,7 +1386,7 @@ var validateAttributes = class {
|
|
|
1253
1386
|
validateLte(value, parameters, attribute) {
|
|
1254
1387
|
this.requireParameterCount(1, parameters, "lte");
|
|
1255
1388
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1256
|
-
const compartedToValue =
|
|
1389
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1257
1390
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) <= compartedToValue;
|
|
1258
1391
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1259
1392
|
return getSize(value) <= getSize(compartedToValue);
|
|
@@ -1311,7 +1444,7 @@ var validateAttributes = class {
|
|
|
1311
1444
|
compareDates(value, parameter, operator, rule) {
|
|
1312
1445
|
value = toDate(value);
|
|
1313
1446
|
if (!value) throw `Validation rule ${rule} requires the field under valation to be a date.`;
|
|
1314
|
-
const compartedToValue = toDate(
|
|
1447
|
+
const compartedToValue = toDate(this.getAttributeValue(parameter) || parameter);
|
|
1315
1448
|
if (!compartedToValue) throw `Validation rule ${rule} requires the parameter to be a date.`;
|
|
1316
1449
|
return compare(value.getTime(), compartedToValue.getTime(), operator);
|
|
1317
1450
|
}
|
|
@@ -1323,6 +1456,10 @@ var validateAttributes = class {
|
|
|
1323
1456
|
}
|
|
1324
1457
|
/**
|
|
1325
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
|
|
1326
1463
|
*/
|
|
1327
1464
|
parseDependentRuleParameters(other, parameters) {
|
|
1328
1465
|
let values = parameters.slice(1);
|
|
@@ -1331,6 +1468,55 @@ var validateAttributes = class {
|
|
|
1331
1468
|
if (typeof other === "boolean") values = convertValuesToBoolean(values);
|
|
1332
1469
|
return values;
|
|
1333
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
|
+
}
|
|
1334
1520
|
};
|
|
1335
1521
|
|
|
1336
1522
|
//#endregion
|
|
@@ -1399,6 +1585,7 @@ const implicitRues = [
|
|
|
1399
1585
|
"declined_if",
|
|
1400
1586
|
"filled",
|
|
1401
1587
|
"present",
|
|
1588
|
+
"presentsame",
|
|
1402
1589
|
"required",
|
|
1403
1590
|
"required_if",
|
|
1404
1591
|
"required_unless",
|
|
@@ -2027,6 +2214,7 @@ var replaceAttributePayload = class {
|
|
|
2027
2214
|
//#endregion
|
|
2028
2215
|
//#region src/BaseValidator.ts
|
|
2029
2216
|
var BaseValidator = class BaseValidator {
|
|
2217
|
+
excludedAttributes = /* @__PURE__ */ new Set();
|
|
2030
2218
|
/**
|
|
2031
2219
|
* The lang used to return error messages
|
|
2032
2220
|
*/
|
|
@@ -2171,6 +2359,9 @@ var BaseValidator = class BaseValidator {
|
|
|
2171
2359
|
errors() {
|
|
2172
2360
|
return this.messages;
|
|
2173
2361
|
}
|
|
2362
|
+
getExcludedAttributes() {
|
|
2363
|
+
return [...this.excludedAttributes];
|
|
2364
|
+
}
|
|
2174
2365
|
/**
|
|
2175
2366
|
* Clear the error messages for the given keys.
|
|
2176
2367
|
* If no keys are provided, all error messages will be cleared.
|
|
@@ -2287,6 +2478,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2287
2478
|
runAllValidations() {
|
|
2288
2479
|
this.messages = new ErrorBag();
|
|
2289
2480
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2481
|
+
this.excludedAttributes.clear();
|
|
2290
2482
|
for (const property in this.rules) if (this.runValidation(property) === false) break;
|
|
2291
2483
|
}
|
|
2292
2484
|
/**
|
|
@@ -2295,6 +2487,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2295
2487
|
async runAllValidationsAsync() {
|
|
2296
2488
|
this.messages = new ErrorBag();
|
|
2297
2489
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2490
|
+
this.excludedAttributes.clear();
|
|
2298
2491
|
for (const property in this.rules) if (await this.runValidationAsync(property) === false) break;
|
|
2299
2492
|
}
|
|
2300
2493
|
/**
|
|
@@ -2317,26 +2510,39 @@ var BaseValidator = class BaseValidator {
|
|
|
2317
2510
|
* Run validation rules for the specified property and stop validation if needed
|
|
2318
2511
|
*/
|
|
2319
2512
|
runValidation(property) {
|
|
2320
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
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
|
+
}
|
|
2324
2523
|
}
|
|
2325
2524
|
}
|
|
2326
2525
|
/**
|
|
2327
2526
|
* Run validation rules for the specified property asynchronously and stop validation if needed
|
|
2328
2527
|
*/
|
|
2329
2528
|
async runValidationAsync(property) {
|
|
2330
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
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
|
+
}
|
|
2334
2539
|
}
|
|
2335
2540
|
}
|
|
2336
2541
|
/**
|
|
2337
2542
|
* Check if we should stop further validations on a given attribute.
|
|
2338
2543
|
*/
|
|
2339
2544
|
shouldStopValidating(attribute) {
|
|
2545
|
+
if (this.excludedAttributes.has(attribute)) return true;
|
|
2340
2546
|
return this.messages.has(attribute) && validationRuleParser.hasRule(attribute, ["bail"], this.rules);
|
|
2341
2547
|
}
|
|
2342
2548
|
/**
|
|
@@ -2361,7 +2567,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2361
2567
|
const method = `validate${buildValidationMethodName(rule)}`;
|
|
2362
2568
|
if (rule !== "" && typeof this.validateAttributes[method] === "undefined") throw `Rule ${rule} is not valid`;
|
|
2363
2569
|
if (!validatable) return;
|
|
2364
|
-
const validation = this.validateAttributes[method](value, parameters, attribute);
|
|
2570
|
+
const validation = this.validateAttributes[method](value, parameters, attribute, this.getPrimaryAttribute(attribute));
|
|
2365
2571
|
if (validation instanceof Promise) return validation.then((result) => {
|
|
2366
2572
|
if (!result) this.addFailure(attribute, rule, value, parameters);
|
|
2367
2573
|
});
|
|
@@ -3126,8 +3332,10 @@ var Validator = class Validator {
|
|
|
3126
3332
|
*/
|
|
3127
3333
|
validatedData() {
|
|
3128
3334
|
const validKeys = Object.keys(this.rules);
|
|
3335
|
+
const excluded = new Set(this.instance?.getExcludedAttributes() ?? []);
|
|
3129
3336
|
const clean = {};
|
|
3130
3337
|
for (const key of validKeys) {
|
|
3338
|
+
if (excluded.has(key)) continue;
|
|
3131
3339
|
const value = deepFind(this.data, key);
|
|
3132
3340
|
const resolvedValue = typeof value !== "undefined" ? value : deepFind(this.getContext().requestFiles ?? {}, key);
|
|
3133
3341
|
if (typeof resolvedValue !== "undefined") deepSet(clean, key, resolvedValue);
|
|
@@ -3138,7 +3346,8 @@ var Validator = class Validator {
|
|
|
3138
3346
|
* Return all validated input.
|
|
3139
3347
|
*/
|
|
3140
3348
|
validated() {
|
|
3141
|
-
|
|
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)));
|
|
3142
3351
|
}
|
|
3143
3352
|
/**
|
|
3144
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
|
|
|
@@ -19,10 +21,12 @@ function getValidatorContext() {
|
|
|
19
21
|
* @returns
|
|
20
22
|
*/
|
|
21
23
|
function useValidatorContext(context = {}) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const currentContext = validatorContextStorage.getStore();
|
|
25
|
+
if (currentContext) {
|
|
26
|
+
Object.assign(currentContext, context);
|
|
27
|
+
return currentContext;
|
|
28
|
+
}
|
|
29
|
+
const nextContext = { ...context };
|
|
26
30
|
validatorContextStorage.enterWith(nextContext);
|
|
27
31
|
return nextContext;
|
|
28
32
|
}
|
|
@@ -71,10 +75,12 @@ var en_default = {
|
|
|
71
75
|
declined: "The :attribute must be declined.",
|
|
72
76
|
declined_if: "The :attribute must be declined when :other is :value.",
|
|
73
77
|
different: "The :attribute and :other must be different.",
|
|
78
|
+
distinct: "The :attribute field has a duplicate value.",
|
|
74
79
|
digits: "The :attribute must be :digits digits.",
|
|
75
80
|
digits_between: "The :attribute must be between :min and :max digits.",
|
|
76
81
|
email: "The :attribute must be a valid email address.",
|
|
77
82
|
ends_with: "The :attribute must end with one of the following: :values.",
|
|
83
|
+
filled: "The :attribute field must have a value.",
|
|
78
84
|
exists: "The selected :attribute is invalid.",
|
|
79
85
|
gt: {
|
|
80
86
|
number: "The :attribute must be greater than :value.",
|
|
@@ -92,6 +98,9 @@ var en_default = {
|
|
|
92
98
|
in: "The :attribute must be one of the following :values.",
|
|
93
99
|
includes: "The :attribute must include one of the following values: :values.",
|
|
94
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.",
|
|
95
104
|
json: "The :attribute must be a valid JSON string.",
|
|
96
105
|
lt: {
|
|
97
106
|
number: "The :attribute must be less than :value.",
|
|
@@ -117,6 +126,7 @@ var en_default = {
|
|
|
117
126
|
array: "The :attribute must have at least :min items.",
|
|
118
127
|
object: "The :attribute must have at least :min items."
|
|
119
128
|
},
|
|
129
|
+
mac_address: "The :attribute must be a valid MAC address.",
|
|
120
130
|
not_in: "The selected :attribute is invalid.",
|
|
121
131
|
not_regex: "The :attribute format is invalid.",
|
|
122
132
|
not_includes: "The :attribute must not include any of the following values: :values.",
|
|
@@ -135,6 +145,10 @@ var en_default = {
|
|
|
135
145
|
upper_cases: "The :attribute must contain at least :amount uppercase letters."
|
|
136
146
|
},
|
|
137
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.",
|
|
138
152
|
regex: "The :attribute format is invalid.",
|
|
139
153
|
required: "The :attribute field is required.",
|
|
140
154
|
required_if: "The :attribute field is required when :other is :value.",
|
|
@@ -152,8 +166,10 @@ var en_default = {
|
|
|
152
166
|
object: "The :attribute must contain :size items."
|
|
153
167
|
},
|
|
154
168
|
string: "The :attribute must be a string.",
|
|
169
|
+
timezone: "The :attribute must be a valid timezone.",
|
|
155
170
|
unique: "The :attribute has already been taken.",
|
|
156
|
-
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."
|
|
157
173
|
};
|
|
158
174
|
|
|
159
175
|
//#endregion
|
|
@@ -622,6 +638,12 @@ const replaceAttributes = {
|
|
|
622
638
|
};
|
|
623
639
|
return message.replace(/:other|:value/gi, (matched) => values[matched]);
|
|
624
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
|
+
},
|
|
625
647
|
replaceRequiredUnless: function({ message, parameters, getDisplayableAttribute }) {
|
|
626
648
|
const [other] = parameters;
|
|
627
649
|
const values = {
|
|
@@ -636,40 +658,14 @@ const replaceAttributes = {
|
|
|
636
658
|
replaceSize: function({ message, parameters }) {
|
|
637
659
|
return message.replace(":size", parameters[0]);
|
|
638
660
|
},
|
|
661
|
+
replaceMultipleOf: function({ message, parameters }) {
|
|
662
|
+
return message.replace(":value", parameters[0]);
|
|
663
|
+
},
|
|
639
664
|
replaceUnique: function({ message, parameters, data }) {
|
|
640
665
|
return message.replace(":value", data[parameters[1]]);
|
|
641
666
|
}
|
|
642
667
|
};
|
|
643
668
|
|
|
644
|
-
//#endregion
|
|
645
|
-
//#region src/Rules/closureValidationRule.ts
|
|
646
|
-
var ClosureValidationRule = class extends IRuleContract {
|
|
647
|
-
/**
|
|
648
|
-
* The callback that validates the attribute
|
|
649
|
-
*/
|
|
650
|
-
callback;
|
|
651
|
-
/**
|
|
652
|
-
* Indicates if the validation callback failed.
|
|
653
|
-
*/
|
|
654
|
-
failed = false;
|
|
655
|
-
constructor(callback) {
|
|
656
|
-
super();
|
|
657
|
-
this.callback = callback;
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Determine if the validation rule passes.
|
|
661
|
-
*/
|
|
662
|
-
passes(value, attribute) {
|
|
663
|
-
this.failed = false;
|
|
664
|
-
const result = this.callback(value, (message) => {
|
|
665
|
-
this.failed = true;
|
|
666
|
-
this.message = message;
|
|
667
|
-
}, attribute);
|
|
668
|
-
if (result instanceof Promise) return result.then(() => !this.failed);
|
|
669
|
-
return !this.failed;
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
|
|
673
669
|
//#endregion
|
|
674
670
|
//#region src/validators/validationData.ts
|
|
675
671
|
const validationData = {
|
|
@@ -710,6 +706,35 @@ const validationData = {
|
|
|
710
706
|
}
|
|
711
707
|
};
|
|
712
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
|
+
|
|
713
738
|
//#endregion
|
|
714
739
|
//#region src/validators/validationRuleParser.ts
|
|
715
740
|
const validationRuleParser = {
|
|
@@ -957,7 +982,7 @@ var validateAttributes = class {
|
|
|
957
982
|
*/
|
|
958
983
|
validateDeclinedIf(value, parameters) {
|
|
959
984
|
this.requireParameterCount(2, parameters, "declined_if");
|
|
960
|
-
const other =
|
|
985
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
961
986
|
if (!other) return true;
|
|
962
987
|
if (parameters.slice(1).indexOf(other) !== -1) return this.validateDeclined(value);
|
|
963
988
|
return true;
|
|
@@ -967,7 +992,7 @@ var validateAttributes = class {
|
|
|
967
992
|
*/
|
|
968
993
|
validateDifferent(value, parameters) {
|
|
969
994
|
this.requireParameterCount(1, parameters, "different");
|
|
970
|
-
const other =
|
|
995
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
971
996
|
if (!sameType(value, other)) return true;
|
|
972
997
|
if (value !== null && typeof value === "object") return !deepEqual(value, other);
|
|
973
998
|
return value !== other;
|
|
@@ -1002,13 +1027,23 @@ var validateAttributes = class {
|
|
|
1002
1027
|
/**
|
|
1003
1028
|
* Validate that an attribute is a valid email address.
|
|
1004
1029
|
*/
|
|
1005
|
-
validateEmail(value) {
|
|
1030
|
+
validateEmail(value, parameters = []) {
|
|
1006
1031
|
if (typeof value !== "string") return false;
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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);
|
|
1012
1047
|
}
|
|
1013
1048
|
/**
|
|
1014
1049
|
* Validate the attribute ends with a given substring.
|
|
@@ -1057,6 +1092,7 @@ var validateAttributes = class {
|
|
|
1057
1092
|
*/
|
|
1058
1093
|
validateRequired(value) {
|
|
1059
1094
|
if (value === null || typeof value === "undefined") return false;
|
|
1095
|
+
else if (typeof Blob !== "undefined" && value instanceof Blob) return true;
|
|
1060
1096
|
else if (typeof value === "string" && value.trim() === "") return false;
|
|
1061
1097
|
else if (Array.isArray(value) && value.length < 1) return false;
|
|
1062
1098
|
else if (typeof value === "object" && Object.keys(value).length < 1) return false;
|
|
@@ -1067,7 +1103,7 @@ var validateAttributes = class {
|
|
|
1067
1103
|
*/
|
|
1068
1104
|
validateRequiredIf(value, parameters) {
|
|
1069
1105
|
this.requireParameterCount(2, parameters, "required_if");
|
|
1070
|
-
const other =
|
|
1106
|
+
const other = this.getAttributeValue(parameters[0]);
|
|
1071
1107
|
if (typeof other === "undefined") return true;
|
|
1072
1108
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) !== -1) return this.validateRequired(value);
|
|
1073
1109
|
return true;
|
|
@@ -1077,7 +1113,7 @@ var validateAttributes = class {
|
|
|
1077
1113
|
*/
|
|
1078
1114
|
validateRequiredUnless(value, parameters) {
|
|
1079
1115
|
this.requireParameterCount(2, parameters, "required_unless");
|
|
1080
|
-
let other =
|
|
1116
|
+
let other = this.getAttributeValue(parameters[0]);
|
|
1081
1117
|
other = typeof other === "undefined" ? null : other;
|
|
1082
1118
|
if (this.parseDependentRuleParameters(other, parameters).indexOf(other) === -1) return this.validateRequired(value);
|
|
1083
1119
|
return true;
|
|
@@ -1114,14 +1150,14 @@ var validateAttributes = class {
|
|
|
1114
1150
|
* Determine if any of the given attributes fail the required test.
|
|
1115
1151
|
*/
|
|
1116
1152
|
anyFailingRequired(attributes) {
|
|
1117
|
-
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;
|
|
1118
1154
|
return false;
|
|
1119
1155
|
}
|
|
1120
1156
|
/**
|
|
1121
1157
|
* Determine if all of the given attributes fail the required test.
|
|
1122
1158
|
*/
|
|
1123
1159
|
allFailingRequired(attributes) {
|
|
1124
|
-
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;
|
|
1125
1161
|
return true;
|
|
1126
1162
|
}
|
|
1127
1163
|
/**
|
|
@@ -1163,7 +1199,13 @@ var validateAttributes = class {
|
|
|
1163
1199
|
* Validate that an attribute exists even if not filled.
|
|
1164
1200
|
*/
|
|
1165
1201
|
validatePresent(value, parameters, attribute) {
|
|
1166
|
-
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);
|
|
1167
1209
|
}
|
|
1168
1210
|
/**
|
|
1169
1211
|
* Validate that an attribute is an integer.
|
|
@@ -1185,12 +1227,103 @@ var validateAttributes = class {
|
|
|
1185
1227
|
return true;
|
|
1186
1228
|
}
|
|
1187
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
|
+
/**
|
|
1188
1321
|
* Validate that an attribute is greater than another attribute.
|
|
1189
1322
|
*/
|
|
1190
1323
|
validateGt(value, parameters, attribute) {
|
|
1191
1324
|
this.requireParameterCount(1, parameters, "gt");
|
|
1192
1325
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1193
|
-
const compartedToValue =
|
|
1326
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1194
1327
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) > compartedToValue;
|
|
1195
1328
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1196
1329
|
return getSize(value) > getSize(compartedToValue);
|
|
@@ -1201,7 +1334,7 @@ var validateAttributes = class {
|
|
|
1201
1334
|
validateGte(value, parameters, attribute) {
|
|
1202
1335
|
this.requireParameterCount(1, parameters, "gte");
|
|
1203
1336
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1204
|
-
const compartedToValue =
|
|
1337
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1205
1338
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) >= compartedToValue;
|
|
1206
1339
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1207
1340
|
return getSize(value) >= getSize(compartedToValue);
|
|
@@ -1212,7 +1345,7 @@ var validateAttributes = class {
|
|
|
1212
1345
|
validateLt(value, parameters, attribute) {
|
|
1213
1346
|
this.requireParameterCount(1, parameters, "lt");
|
|
1214
1347
|
if (typeof value !== "number" && typeof value !== "string" && typeof value !== "object") throw "The field under validation must be a number, string, array or object";
|
|
1215
|
-
const compartedToValue =
|
|
1348
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1216
1349
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) < compartedToValue;
|
|
1217
1350
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1218
1351
|
return getSize(value) < getSize(compartedToValue);
|
|
@@ -1223,7 +1356,7 @@ var validateAttributes = class {
|
|
|
1223
1356
|
validateLte(value, parameters, attribute) {
|
|
1224
1357
|
this.requireParameterCount(1, parameters, "lte");
|
|
1225
1358
|
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 =
|
|
1359
|
+
const compartedToValue = this.getAttributeValue(parameters[0]) || parameters[0];
|
|
1227
1360
|
if (!Array.isArray(compartedToValue) && isNaN(compartedToValue) === false) return getSize(value, validationRuleParser.hasRule(attribute, getNumericRules(), this.rules)) <= compartedToValue;
|
|
1228
1361
|
if (sameType(value, compartedToValue) === false) throw "The fields under validation must be of the same type";
|
|
1229
1362
|
return getSize(value) <= getSize(compartedToValue);
|
|
@@ -1281,7 +1414,7 @@ var validateAttributes = class {
|
|
|
1281
1414
|
compareDates(value, parameter, operator, rule) {
|
|
1282
1415
|
value = toDate(value);
|
|
1283
1416
|
if (!value) throw `Validation rule ${rule} requires the field under valation to be a date.`;
|
|
1284
|
-
const compartedToValue = toDate(
|
|
1417
|
+
const compartedToValue = toDate(this.getAttributeValue(parameter) || parameter);
|
|
1285
1418
|
if (!compartedToValue) throw `Validation rule ${rule} requires the parameter to be a date.`;
|
|
1286
1419
|
return compare(value.getTime(), compartedToValue.getTime(), operator);
|
|
1287
1420
|
}
|
|
@@ -1293,6 +1426,10 @@ var validateAttributes = class {
|
|
|
1293
1426
|
}
|
|
1294
1427
|
/**
|
|
1295
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
|
|
1296
1433
|
*/
|
|
1297
1434
|
parseDependentRuleParameters(other, parameters) {
|
|
1298
1435
|
let values = parameters.slice(1);
|
|
@@ -1301,6 +1438,55 @@ var validateAttributes = class {
|
|
|
1301
1438
|
if (typeof other === "boolean") values = convertValuesToBoolean(values);
|
|
1302
1439
|
return values;
|
|
1303
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
|
+
}
|
|
1304
1490
|
};
|
|
1305
1491
|
|
|
1306
1492
|
//#endregion
|
|
@@ -1369,6 +1555,7 @@ const implicitRues = [
|
|
|
1369
1555
|
"declined_if",
|
|
1370
1556
|
"filled",
|
|
1371
1557
|
"present",
|
|
1558
|
+
"presentsame",
|
|
1372
1559
|
"required",
|
|
1373
1560
|
"required_if",
|
|
1374
1561
|
"required_unless",
|
|
@@ -1997,6 +2184,7 @@ var replaceAttributePayload = class {
|
|
|
1997
2184
|
//#endregion
|
|
1998
2185
|
//#region src/BaseValidator.ts
|
|
1999
2186
|
var BaseValidator = class BaseValidator {
|
|
2187
|
+
excludedAttributes = /* @__PURE__ */ new Set();
|
|
2000
2188
|
/**
|
|
2001
2189
|
* The lang used to return error messages
|
|
2002
2190
|
*/
|
|
@@ -2141,6 +2329,9 @@ var BaseValidator = class BaseValidator {
|
|
|
2141
2329
|
errors() {
|
|
2142
2330
|
return this.messages;
|
|
2143
2331
|
}
|
|
2332
|
+
getExcludedAttributes() {
|
|
2333
|
+
return [...this.excludedAttributes];
|
|
2334
|
+
}
|
|
2144
2335
|
/**
|
|
2145
2336
|
* Clear the error messages for the given keys.
|
|
2146
2337
|
* If no keys are provided, all error messages will be cleared.
|
|
@@ -2257,6 +2448,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2257
2448
|
runAllValidations() {
|
|
2258
2449
|
this.messages = new ErrorBag();
|
|
2259
2450
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2451
|
+
this.excludedAttributes.clear();
|
|
2260
2452
|
for (const property in this.rules) if (this.runValidation(property) === false) break;
|
|
2261
2453
|
}
|
|
2262
2454
|
/**
|
|
@@ -2265,6 +2457,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2265
2457
|
async runAllValidationsAsync() {
|
|
2266
2458
|
this.messages = new ErrorBag();
|
|
2267
2459
|
this.validateAttributes = new validateAttributes(this.data, this.rules, this.getContext());
|
|
2460
|
+
this.excludedAttributes.clear();
|
|
2268
2461
|
for (const property in this.rules) if (await this.runValidationAsync(property) === false) break;
|
|
2269
2462
|
}
|
|
2270
2463
|
/**
|
|
@@ -2287,26 +2480,39 @@ var BaseValidator = class BaseValidator {
|
|
|
2287
2480
|
* Run validation rules for the specified property and stop validation if needed
|
|
2288
2481
|
*/
|
|
2289
2482
|
runValidation(property) {
|
|
2290
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
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
|
+
}
|
|
2294
2493
|
}
|
|
2295
2494
|
}
|
|
2296
2495
|
/**
|
|
2297
2496
|
* Run validation rules for the specified property asynchronously and stop validation if needed
|
|
2298
2497
|
*/
|
|
2299
2498
|
async runValidationAsync(property) {
|
|
2300
|
-
if (Object.prototype.hasOwnProperty.call(this.rules, property) && Array.isArray(this.rules[property]))
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
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
|
+
}
|
|
2304
2509
|
}
|
|
2305
2510
|
}
|
|
2306
2511
|
/**
|
|
2307
2512
|
* Check if we should stop further validations on a given attribute.
|
|
2308
2513
|
*/
|
|
2309
2514
|
shouldStopValidating(attribute) {
|
|
2515
|
+
if (this.excludedAttributes.has(attribute)) return true;
|
|
2310
2516
|
return this.messages.has(attribute) && validationRuleParser.hasRule(attribute, ["bail"], this.rules);
|
|
2311
2517
|
}
|
|
2312
2518
|
/**
|
|
@@ -2331,7 +2537,7 @@ var BaseValidator = class BaseValidator {
|
|
|
2331
2537
|
const method = `validate${buildValidationMethodName(rule)}`;
|
|
2332
2538
|
if (rule !== "" && typeof this.validateAttributes[method] === "undefined") throw `Rule ${rule} is not valid`;
|
|
2333
2539
|
if (!validatable) return;
|
|
2334
|
-
const validation = this.validateAttributes[method](value, parameters, attribute);
|
|
2540
|
+
const validation = this.validateAttributes[method](value, parameters, attribute, this.getPrimaryAttribute(attribute));
|
|
2335
2541
|
if (validation instanceof Promise) return validation.then((result) => {
|
|
2336
2542
|
if (!result) this.addFailure(attribute, rule, value, parameters);
|
|
2337
2543
|
});
|
|
@@ -3096,8 +3302,10 @@ var Validator = class Validator {
|
|
|
3096
3302
|
*/
|
|
3097
3303
|
validatedData() {
|
|
3098
3304
|
const validKeys = Object.keys(this.rules);
|
|
3305
|
+
const excluded = new Set(this.instance?.getExcludedAttributes() ?? []);
|
|
3099
3306
|
const clean = {};
|
|
3100
3307
|
for (const key of validKeys) {
|
|
3308
|
+
if (excluded.has(key)) continue;
|
|
3101
3309
|
const value = deepFind(this.data, key);
|
|
3102
3310
|
const resolvedValue = typeof value !== "undefined" ? value : deepFind(this.getContext().requestFiles ?? {}, key);
|
|
3103
3311
|
if (typeof resolvedValue !== "undefined") deepSet(clean, key, resolvedValue);
|
|
@@ -3108,7 +3316,8 @@ var Validator = class Validator {
|
|
|
3108
3316
|
* Return all validated input.
|
|
3109
3317
|
*/
|
|
3110
3318
|
validated() {
|
|
3111
|
-
|
|
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)));
|
|
3112
3321
|
}
|
|
3113
3322
|
/**
|
|
3114
3323
|
* Return a portion of validated input
|