@zipbul/baker 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +236 -148
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +1 -10
  5. package/dist/src/baker.d.ts +26 -0
  6. package/dist/src/baker.js +1 -0
  7. package/dist/src/collect.js +1 -26
  8. package/dist/src/configure.d.ts +7 -1
  9. package/dist/src/configure.js +1 -43
  10. package/dist/src/create-rule.d.ts +2 -1
  11. package/dist/src/create-rule.js +1 -41
  12. package/dist/src/decorators/field.d.ts +2 -1
  13. package/dist/src/decorators/field.js +1 -277
  14. package/dist/src/decorators/index.js +1 -2
  15. package/dist/src/decorators/recipe.js +1 -23
  16. package/dist/src/enums.d.ts +51 -0
  17. package/dist/src/enums.js +1 -0
  18. package/dist/src/errors.js +1 -52
  19. package/dist/src/functions/check-call-options.js +1 -51
  20. package/dist/src/functions/deserialize.js +1 -57
  21. package/dist/src/functions/serialize.js +1 -52
  22. package/dist/src/functions/validate.js +1 -49
  23. package/dist/src/interfaces.js +0 -4
  24. package/dist/src/meta-access.js +1 -75
  25. package/dist/src/registry.js +1 -8
  26. package/dist/src/rule-metadata.js +1 -17
  27. package/dist/src/rule-plan.d.ts +5 -3
  28. package/dist/src/rule-plan.js +1 -117
  29. package/dist/src/rules/array.js +1 -96
  30. package/dist/src/rules/binary.js +3 -51
  31. package/dist/src/rules/combinators.js +1 -111
  32. package/dist/src/rules/common.js +1 -77
  33. package/dist/src/rules/date.js +1 -35
  34. package/dist/src/rules/index.js +1 -10
  35. package/dist/src/rules/locales.js +1 -249
  36. package/dist/src/rules/number.js +1 -79
  37. package/dist/src/rules/object.js +1 -49
  38. package/dist/src/rules/string.js +10 -2033
  39. package/dist/src/rules/typechecker.js +5 -171
  40. package/dist/src/seal/circular-analyzer.js +1 -63
  41. package/dist/src/seal/codegen-utils.js +1 -18
  42. package/dist/src/seal/deserialize-builder.js +265 -1564
  43. package/dist/src/seal/enums.d.ts +8 -0
  44. package/dist/src/seal/enums.js +1 -0
  45. package/dist/src/seal/expose-validator.js +1 -65
  46. package/dist/src/seal/seal-state.js +1 -18
  47. package/dist/src/seal/seal.d.ts +15 -1
  48. package/dist/src/seal/seal.js +1 -431
  49. package/dist/src/seal/serialize-builder.js +66 -370
  50. package/dist/src/seal/validate-meta.js +1 -61
  51. package/dist/src/symbols.js +1 -13
  52. package/dist/src/transformers/collection.transformer.js +1 -25
  53. package/dist/src/transformers/date.transformer.js +1 -18
  54. package/dist/src/transformers/index.js +1 -6
  55. package/dist/src/transformers/luxon.transformer.js +1 -34
  56. package/dist/src/transformers/moment.transformer.js +1 -32
  57. package/dist/src/transformers/number.transformer.js +1 -8
  58. package/dist/src/transformers/string.transformer.js +1 -12
  59. package/dist/src/types.d.ts +11 -10
  60. package/dist/src/types.js +0 -1
  61. package/dist/src/utils.js +1 -10
  62. package/package.json +2 -2
@@ -1,2035 +1,12 @@
1
- import { makePlannedRule, makeRule, planCompare, planLength, planOr } from '../rule-plan.js';
2
- // ─────────────────────────────────────────────────────────────────────────────
3
- // Helpers
4
- // ─────────────────────────────────────────────────────────────────────────────
5
- function makeStringRule(name, validate, buildEmit, requiresType = 'string', constraints = {}) {
6
- return makeRule({
7
- name,
8
- requiresType,
9
- constraints,
10
- validate: value => typeof value === 'string' && validate(value),
11
- emit: buildEmit,
12
- });
13
- }
14
- // ─────────────────────────────────────────────────────────────────────────────
15
- // Group A: Length / Range
16
- // ─────────────────────────────────────────────────────────────────────────────
17
- function minLength(min) {
18
- const plan = { cacheKey: 'length', failure: planCompare(planLength(), '<', min) };
19
- return makePlannedRule({
20
- name: 'minLength',
21
- requiresType: 'string',
22
- constraints: { min },
23
- plan,
24
- validate: value => typeof value === 'string' && value.length >= min,
25
- });
26
- }
27
- function maxLength(max) {
28
- const plan = { cacheKey: 'length', failure: planCompare(planLength(), '>', max) };
29
- return makePlannedRule({
30
- name: 'maxLength',
31
- requiresType: 'string',
32
- constraints: { max },
33
- plan,
34
- validate: value => typeof value === 'string' && value.length <= max,
35
- });
36
- }
37
- function length(minLen, maxLen) {
38
- const plan = {
39
- cacheKey: 'length',
40
- failure: planOr(planCompare(planLength(), '<', minLen), planCompare(planLength(), '>', maxLen)),
41
- };
42
- return makePlannedRule({
43
- name: 'length',
44
- requiresType: 'string',
45
- constraints: { min: minLen, max: maxLen },
46
- plan,
47
- validate: value => typeof value === 'string' && value.length >= minLen && value.length <= maxLen,
48
- });
49
- }
50
- function contains(seed) {
51
- return makeRule({
52
- name: 'contains',
53
- requiresType: 'string',
54
- constraints: { seed },
55
- validate: value => typeof value === 'string' && value.includes(seed),
56
- emit: (varName, ctx) => {
57
- const i = ctx.addRef(seed);
58
- return `if (!${varName}.includes(refs[${i}])) ${ctx.fail('contains')};`;
59
- },
60
- });
61
- }
62
- function notContains(seed) {
63
- return makeRule({
64
- name: 'notContains',
65
- requiresType: 'string',
66
- constraints: { seed },
67
- validate: value => typeof value === 'string' && !value.includes(seed),
68
- emit: (varName, ctx) => {
69
- const i = ctx.addRef(seed);
70
- return `if (${varName}.includes(refs[${i}])) ${ctx.fail('notContains')};`;
71
- },
72
- });
73
- }
74
- function matches(pattern, modifiers) {
75
- const re = pattern instanceof RegExp ? pattern : new RegExp(pattern, modifiers);
76
- return makeRule({
77
- name: 'matches',
78
- requiresType: 'string',
79
- constraints: { pattern: re.source },
80
- validate: value => typeof value === 'string' && re.test(value),
81
- emit: (varName, ctx) => {
82
- const i = ctx.addRegex(re);
83
- return `if (!re[${i}].test(${varName})) ${ctx.fail('matches')};`;
84
- },
85
- });
86
- }
87
- // ─────────────────────────────────────────────────────────────────────────────
88
- // Group B: Simple Boolean Checks
89
- // ─────────────────────────────────────────────────────────────────────────────
90
- const isLowercase = makeRule({
91
- name: 'isLowercase',
92
- requiresType: 'string',
93
- constraints: {},
94
- validate: value => typeof value === 'string' && value === value.toLowerCase(),
95
- emit: (varName, ctx) => `if (${varName} !== ${varName}.toLowerCase()) ${ctx.fail('isLowercase')};`,
96
- });
97
- const isUppercase = makeRule({
98
- name: 'isUppercase',
99
- requiresType: 'string',
100
- constraints: {},
101
- validate: value => typeof value === 'string' && value === value.toUpperCase(),
102
- emit: (varName, ctx) => `if (${varName} !== ${varName}.toUpperCase()) ${ctx.fail('isUppercase')};`,
103
- });
104
- // ASCII: all code points in [0x00, 0x7F]
105
- const ASCII_RE = new RegExp(`^[${String.fromCharCode(0)}-${String.fromCharCode(0x7f)}]*$`);
106
- const isAscii = makeStringRule('isAscii', v => ASCII_RE.test(v), (varName, ctx) => {
107
- const i = ctx.addRegex(ASCII_RE);
108
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isAscii')};`;
109
- });
110
- // Alpha — [a-zA-Z]+ singleton
111
- const ALPHA_DEFAULT_RE = /^[a-zA-Z]+$/;
112
- // length > 0 guard is dead — `+` quantifier requires ≥1 char so the regex returns false on empty.
113
- const isAlpha = makeStringRule('isAlpha', v => ALPHA_DEFAULT_RE.test(v), (varName, ctx) => {
114
- const i = ctx.addRegex(ALPHA_DEFAULT_RE);
115
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isAlpha')};`;
116
- });
117
- // Alphanumeric — [a-zA-Z0-9]+ singleton (same empty-input rationale as isAlpha)
118
- const ALNUM_DEFAULT_RE = /^[a-zA-Z0-9]+$/;
119
- const isAlphanumeric = makeStringRule('isAlphanumeric', v => ALNUM_DEFAULT_RE.test(v), (varName, ctx) => {
120
- const i = ctx.addRegex(ALNUM_DEFAULT_RE);
121
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isAlphanumeric')};`;
122
- });
123
- // HTTP token — RFC 9110 §5.6.2: token = 1*tchar.
124
- // tchar = "!"/"#"/"$"/"%"/"&"/"'"/"*"/"+"/"-"/"."/"^"/"_"/"`"/"|"/"~" / DIGIT / ALPHA.
125
- // Used for HTTP method names and header field-names (not field-values). The hyphen is
126
- // escaped so it stays literal — an unescaped `+-.` would form a range that admits ",".
127
- const HTTP_TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
128
- const isHttpToken = makeStringRule('isHttpToken', v => HTTP_TOKEN_RE.test(v), (varName, ctx) => {
129
- const i = ctx.addRegex(HTTP_TOKEN_RE);
130
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isHttpToken')};`;
131
- });
132
- // RFC 6454 §6.2 serialized origin — a string equal to WHATWG URL `.origin`.
133
- // The opaque-origin literal 'null' is matched explicitly because `new URL('null')` throws.
134
- // '*' (CORS wildcard) is rejected here; use isCorsOrigin for the CORS superset.
135
- const isOriginValue = (value) => {
136
- if (value === 'null') {
137
- return true;
138
- }
139
- try {
140
- return new URL(value).origin === value;
141
- }
142
- catch {
143
- return false;
144
- }
145
- };
146
- const isOrigin = makeRule({
147
- name: 'isOrigin',
148
- requiresType: 'string',
149
- constraints: { format: 'origin' },
150
- validate: value => typeof value === 'string' && isOriginValue(value),
151
- emit: (varName, ctx) => {
152
- const i = ctx.addRef(isOriginValue);
153
- return `if (!(refs[${i}](${varName}))) ${ctx.fail('isOrigin')};`;
154
- },
155
- });
156
- // CORS superset of isOrigin: additionally accepts the '*' wildcard literal
157
- // (Access-Control-Allow-Origin). Keep '*' out of the general isOrigin.
158
- const isCorsOriginValue = (value) => value === '*' || isOriginValue(value);
159
- const isCorsOrigin = makeRule({
160
- name: 'isCorsOrigin',
161
- requiresType: 'string',
162
- constraints: { format: 'origin', allowWildcard: true },
163
- validate: value => typeof value === 'string' && isCorsOriginValue(value),
164
- emit: (varName, ctx) => {
165
- const i = ctx.addRef(isCorsOriginValue);
166
- return `if (!(refs[${i}](${varName}))) ${ctx.fail('isCorsOrigin')};`;
167
- },
168
- });
169
- // BooleanString: 'true' | 'false' | '1' | '0'
170
- const isBooleanString = makeRule({
171
- name: 'isBooleanString',
172
- requiresType: 'string',
173
- constraints: {},
174
- validate: value => value === 'true' || value === 'false' || value === '1' || value === '0',
175
- emit: (varName, ctx) => `if (${varName} !== 'true' && ${varName} !== 'false' && ${varName} !== '1' && ${varName} !== '0') ${ctx.fail('isBooleanString')};`,
176
- });
177
- const NO_SYMBOLS_RE = /^[0-9]+$/;
178
- // A numeric string: optional sign, integer/decimal/leading-dot form. No whitespace, hex, or
179
- // exponent — `Number()` coercion accepted all of those (e.g. " ", "0x1A", "1e5"), which is far
180
- // looser than "is this string a number". Matches validator.js's default isNumeric behavior.
181
- const NUMERIC_STRING_RE = /^[+-]?(?:[0-9]*\.)?[0-9]+$/;
182
- function isNumberString(options) {
183
- const noSymbols = options?.no_symbols ?? false;
184
- const re = noSymbols ? NO_SYMBOLS_RE : NUMERIC_STRING_RE;
185
- return makeStringRule('isNumberString', (s) => re.test(s), (varName, ctx) => {
186
- const i = ctx.addRegex(re);
187
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isNumberString')};`;
188
- }, 'string', { no_symbols: noSymbols });
189
- }
190
- function isDecimal() {
191
- // Require a digit after the dot — `\d+(?:\.\d*)?` accepted a dangling "5.".
192
- const decimalRe = /^[-+]?(?:\d+(?:\.\d+)?|\.\d+)$/;
193
- return makeStringRule('isDecimal', v => decimalRe.test(v), (varName, ctx) => {
194
- const i = ctx.addRegex(decimalRe);
195
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isDecimal')};`;
196
- });
197
- }
198
- // Full-width characters (Unicode fullwidth forms)
199
- const FULLWIDTH_RE = /[^\u0020-\u007E\uFF61-\uFF9F]/;
200
- // Empty-string guard is redundant — non-anchored char-class regex returns false on empty input.
201
- const isFullWidth = makeStringRule('isFullWidth', v => FULLWIDTH_RE.test(v), (varName, ctx) => {
202
- const i = ctx.addRegex(FULLWIDTH_RE);
203
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isFullWidth')};`;
204
- });
205
- // Half-width characters
206
- const HALFWIDTH_RE = /[\u0020-\u007E\uFF61-\uFF9F]/;
207
- const isHalfWidth = makeStringRule('isHalfWidth', v => HALFWIDTH_RE.test(v), (varName, ctx) => {
208
- const i = ctx.addRegex(HALFWIDTH_RE);
209
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isHalfWidth')};`;
210
- });
211
- // Variable-width: must contain both full-width AND half-width
212
- const isVariableWidth = makeStringRule('isVariableWidth', v => FULLWIDTH_RE.test(v) && HALFWIDTH_RE.test(v), (varName, ctx) => {
213
- const i1 = ctx.addRegex(FULLWIDTH_RE);
214
- const i2 = ctx.addRegex(HALFWIDTH_RE);
215
- return `if (!re[${i1}].test(${varName}) || !re[${i2}].test(${varName})) ${ctx.fail('isVariableWidth')};`;
216
- });
217
- // Multibyte: any character outside Latin-1 / half-width range
218
- const MULTIBYTE_RE = new RegExp(`[^${String.fromCharCode(0)}-${String.fromCharCode(0xff)}]`);
219
- const isMultibyte = makeStringRule('isMultibyte', v => MULTIBYTE_RE.test(v), (varName, ctx) => {
220
- const i = ctx.addRegex(MULTIBYTE_RE);
221
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isMultibyte')};`;
222
- });
223
- // Surrogate pairs
224
- const SURROGATE_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
225
- const isSurrogatePair = makeStringRule('isSurrogatePair', v => SURROGATE_RE.test(v), (varName, ctx) => {
226
- const i = ctx.addRegex(SURROGATE_RE);
227
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isSurrogatePair')};`;
228
- });
229
- // Hexadecimal
230
- const HEX_RE = /^[0-9a-fA-F]+$/;
231
- const isHexadecimal = makeStringRule('isHexadecimal', v => HEX_RE.test(v), (varName, ctx) => {
232
- const i = ctx.addRegex(HEX_RE);
233
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isHexadecimal')};`;
234
- });
235
- // Octal
236
- const OCTAL_RE = /^(0[oO])?[0-7]+$/;
237
- const isOctal = makeStringRule('isOctal', v => OCTAL_RE.test(v), (varName, ctx) => {
238
- const i = ctx.addRegex(OCTAL_RE);
239
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isOctal')};`;
240
- });
241
- // ─────────────────────────────────────────────────────────────────────────────
242
- // Group C: Regex-based
243
- // ─────────────────────────────────────────────────────────────────────────────
244
- // Email — RFC 5322 simplified
245
- const EMAIL_RE = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;
246
- function isEmail() {
247
- return makeStringRule('isEmail', v => EMAIL_RE.test(v), (varName, ctx) => {
248
- const i = ctx.addRegex(EMAIL_RE);
249
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isEmail')};`;
250
- }, 'string', { format: 'email' });
251
- }
252
- const URL_PROTOCOLS_DEFAULT = ['http', 'https', 'ftp'];
253
- function isURL(options) {
254
- const protocols = options?.protocols ?? URL_PROTOCOLS_DEFAULT;
255
- const protocolPattern = protocols.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
256
- const re = new RegExp(`^(?:${protocolPattern}):\\/\\/(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)(?::(6553[0-5]|655[0-2]\\d|65[0-4]\\d{2}|6[0-4]\\d{3}|[1-5]\\d{4}|[1-9]\\d{0,3}|0))?(?:\\/[^\\s]*)?$`);
257
- return makeRule({
258
- name: 'isURL',
259
- requiresType: 'string',
260
- constraints: { format: 'uri', protocols },
261
- validate: value => typeof value === 'string' && re.test(value),
262
- emit: (varName, ctx) => {
263
- const i = ctx.addRegex(re);
264
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isURL')};`;
265
- },
266
- });
267
- }
268
- // UUID
269
- const UUID_RE = {
270
- all: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,
271
- 1: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-1[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,
272
- 2: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-2[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,
273
- 3: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,
274
- 4: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,
275
- 5: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,
276
- };
277
- function isUUID(version) {
278
- const re = version != null ? UUID_RE[version] : UUID_RE.all;
279
- return makeStringRule('isUUID', v => re.test(v), (varName, ctx) => {
280
- const i = ctx.addRegex(re);
281
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isUUID')};`;
282
- }, 'string', { format: 'uuid', version });
283
- }
284
- // IP
285
- const IPV4_RE = /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/;
286
- const IPV6_RE = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}$|^::$|^::1$|^::(?:ffff(?::0{1,4})?:)?(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$|^(?:[0-9a-fA-F]{1,4}:){1,4}:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
287
- function isIP(version) {
288
- return makeRule({
289
- name: 'isIP',
290
- requiresType: 'string',
291
- constraints: { version },
292
- validate: value => {
293
- if (typeof value !== 'string') {
294
- return false;
295
- }
296
- if (version === 4) {
297
- return IPV4_RE.test(value);
298
- }
299
- if (version === 6) {
300
- return IPV6_RE.test(value);
301
- }
302
- return IPV4_RE.test(value) || IPV6_RE.test(value);
303
- },
304
- emit: (varName, ctx) => {
305
- if (version === 4) {
306
- const i = ctx.addRegex(IPV4_RE);
307
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isIP')};`;
308
- }
309
- if (version === 6) {
310
- const i = ctx.addRegex(IPV6_RE);
311
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isIP')};`;
312
- }
313
- const i4 = ctx.addRegex(IPV4_RE);
314
- const i6 = ctx.addRegex(IPV6_RE);
315
- return `if (!re[${i4}].test(${varName}) && !re[${i6}].test(${varName})) ${ctx.fail('isIP')};`;
316
- },
317
- });
318
- }
319
- // HexColor: #RGB or #RRGGBB
320
- const HEX_COLOR_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
321
- const isHexColor = makeStringRule('isHexColor', v => HEX_COLOR_RE.test(v), (varName, ctx) => {
322
- const i = ctx.addRegex(HEX_COLOR_RE);
323
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isHexColor')};`;
324
- });
325
- // RgbColor
326
- const RGB_RE = /^rgb\(\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*\)$/;
327
- const RGBA_RE = /^rgba\(\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(0|0?\.\d+|1(\.0+)?)\s*\)$/;
328
- // Percent forms: rgb(...) must NOT have alpha; rgba(...) MUST have alpha.
329
- const RGB_PERCENT_NOALPHA_RE = /^rgb\(\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*\)$/;
330
- const RGBA_PERCENT_RE = /^rgba\(\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*,\s*(0|0?\.\d+|1(?:\.0+)?)\s*\)$/;
331
- function isRgbColor(includePercentValues = false) {
332
- return makeRule({
333
- name: 'isRgbColor',
334
- requiresType: 'string',
335
- constraints: { includePercentValues },
336
- validate: value => {
337
- if (typeof value !== 'string') {
338
- return false;
339
- }
340
- if (includePercentValues) {
341
- return RGB_PERCENT_NOALPHA_RE.test(value) || RGBA_PERCENT_RE.test(value) || RGB_RE.test(value) || RGBA_RE.test(value);
342
- }
343
- return RGB_RE.test(value) || RGBA_RE.test(value);
344
- },
345
- emit: (varName, ctx) => {
346
- if (includePercentValues) {
347
- const ip1 = ctx.addRegex(RGB_PERCENT_NOALPHA_RE);
348
- const ip2 = ctx.addRegex(RGBA_PERCENT_RE);
349
- const ip3 = ctx.addRegex(RGB_RE);
350
- const ip4 = ctx.addRegex(RGBA_RE);
351
- return `if (!re[${ip1}].test(${varName}) && !re[${ip2}].test(${varName}) && !re[${ip3}].test(${varName}) && !re[${ip4}].test(${varName})) ${ctx.fail('isRgbColor')};`;
352
- }
353
- const i1 = ctx.addRegex(RGB_RE);
354
- const i2 = ctx.addRegex(RGBA_RE);
355
- return `if (!re[${i1}].test(${varName}) && !re[${i2}].test(${varName})) ${ctx.fail('isRgbColor')};`;
356
- },
357
- });
358
- }
359
- // HSL: hsl(H, S%, L%) or hsla(H, S%, L%, A)
360
- // Alpha belongs to hsla() only — `hsla?(...)?` previously let hsl() carry alpha and hsla() omit it.
361
- const HSL_RE = /^(?:hsl\(\s*(?:360|3[0-5]\d|[12]\d{2}|[1-9]\d|\d)\s*,\s*(?:100|[1-9]\d|\d)%\s*,\s*(?:100|[1-9]\d|\d)%\s*\)|hsla\(\s*(?:360|3[0-5]\d|[12]\d{2}|[1-9]\d|\d)\s*,\s*(?:100|[1-9]\d|\d)%\s*,\s*(?:100|[1-9]\d|\d)%\s*,\s*(?:0|0?\.\d+|1(?:\.0+)?)\s*\))$/;
362
- const isHSL = makeStringRule('isHSL', v => HSL_RE.test(v), (varName, ctx) => {
363
- const i = ctx.addRegex(HSL_RE);
364
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isHSL')};`;
365
- });
366
- const MAC_COLON_RE = /^[0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5}$/;
367
- const MAC_HYPHEN_RE = /^[0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5}$/;
368
- const MAC_NO_SEP_RE = /^[0-9a-fA-F]{12}$/;
369
- function isMACAddress(options) {
370
- return makeRule({
371
- name: 'isMACAddress',
372
- requiresType: 'string',
373
- constraints: { no_separators: options?.no_separators },
374
- validate: value => {
375
- if (typeof value !== 'string') {
376
- return false;
377
- }
378
- if (options?.no_separators) {
379
- return MAC_NO_SEP_RE.test(value);
380
- }
381
- return MAC_COLON_RE.test(value) || MAC_HYPHEN_RE.test(value);
382
- },
383
- emit: (varName, ctx) => {
384
- if (options?.no_separators) {
385
- const i = ctx.addRegex(MAC_NO_SEP_RE);
386
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isMACAddress')};`;
387
- }
388
- const i1 = ctx.addRegex(MAC_COLON_RE);
389
- const i2 = ctx.addRegex(MAC_HYPHEN_RE);
390
- return `if (!re[${i1}].test(${varName}) && !re[${i2}].test(${varName})) ${ctx.fail('isMACAddress')};`;
391
- },
392
- });
393
- }
394
- // ISBN
395
- function validateISBN10(str) {
396
- const s = str.replace(/[-\s]/g, '');
397
- if (!/^\d{9}[\dX]$/.test(s)) {
398
- return false;
399
- }
400
- let sum = 0;
401
- for (let i = 0; i < 9; i++) {
402
- sum += (10 - i) * (s.charCodeAt(i) - 48);
403
- }
404
- const last = s[9] === 'X' ? 10 : s.charCodeAt(9) - 48;
405
- sum += last;
406
- return sum % 11 === 0;
407
- }
408
- function validateISBN13(str) {
409
- const s = str.replace(/[-\s]/g, '');
410
- if (!/^\d{13}$/.test(s)) {
411
- return false;
412
- }
413
- let sum = 0;
414
- for (let i = 0; i < 12; i++) {
415
- sum += (s.charCodeAt(i) - 48) * (i % 2 === 0 ? 1 : 3);
416
- }
417
- const check = (10 - (sum % 10)) % 10;
418
- return check === s.charCodeAt(12) - 48;
419
- }
420
- function isISBN(version) {
421
- const validateFn = (value) => {
422
- if (typeof value !== 'string') {
423
- return false;
424
- }
425
- if (version === 10) {
426
- return validateISBN10(value);
427
- }
428
- if (version === 13) {
429
- return validateISBN13(value);
430
- }
431
- return validateISBN10(value) || validateISBN13(value);
432
- };
433
- const emitISBN10 = (v) => `{var s=${v}.replace(/[-\\s]/g,'');` +
434
- `if(!/^\\d{9}[\\dX]$/.test(s)){%%FAIL%%}` +
435
- `else{var sm=0;for(var i=0;i<9;i++)sm+=(10-i)*(s.charCodeAt(i)-48);` +
436
- `var l=s[9]==='X'?10:(s.charCodeAt(9)-48);sm+=l;` +
437
- `if(sm%11!==0){%%FAIL%%}}}`;
438
- const emitISBN13 = (v) => `{var s=${v}.replace(/[-\\s]/g,'');` +
439
- `if(!/^\\d{13}$/.test(s)){%%FAIL%%}` +
440
- `else{var sm=0;for(var i=0;i<12;i++)sm+=(s.charCodeAt(i)-48)*(i%2===0?1:3);` +
441
- `var ck=(10-(sm%10))%10;` +
442
- `if(ck!==(s.charCodeAt(12)-48)){%%FAIL%%}}}`;
443
- return makeRule({
444
- name: 'isISBN',
445
- requiresType: 'string',
446
- constraints: { version },
447
- validate: validateFn,
448
- emit: (varName, ctx) => {
449
- const fail = ctx.fail('isISBN');
450
- if (version === 10) {
451
- return emitISBN10(varName).replace(/%%FAIL%%/g, fail);
452
- }
453
- if (version === 13) {
454
- return emitISBN13(varName).replace(/%%FAIL%%/g, fail);
455
- }
456
- const emit10 = emitISBN10(varName).replace(/%%FAIL%%/g, '__isbn_ok=false');
457
- const emit13 = emitISBN13(varName).replace(/%%FAIL%%/g, '__isbn_ok=false');
458
- return `{var __isbn_ok=true;${emit10} if(!__isbn_ok){__isbn_ok=true;${emit13}} if(!__isbn_ok)${fail};}`;
459
- },
460
- });
461
- }
462
- // ISIN — ISO 6166
463
- const ISIN_RE = /^[A-Z]{2}[A-Z0-9]{9}[0-9]$/;
464
- function validateISINStr(v) {
465
- if (!ISIN_RE.test(v)) {
466
- return false;
467
- }
468
- // Luhn mod10 on expanded digits — walk right-to-left, expanding letters as A=10..Z=35 on the fly.
469
- // No intermediate string/array allocations.
470
- let sum = 0;
471
- let alternate = false;
472
- for (let i = v.length - 1; i >= 0; i--) {
473
- const code = v.charCodeAt(i);
474
- if (code <= 57) {
475
- // ASCII digit '0'..'9'
476
- let n = code - 48;
477
- if (alternate) {
478
- n *= 2;
479
- if (n > 9) {
480
- n -= 9;
481
- }
482
- }
483
- sum += n;
484
- alternate = !alternate;
485
- }
486
- else {
487
- // ASCII letter 'A'..'Z' → two-digit value, ones first when walking right-to-left
488
- const value = code - 55;
489
- const ones = value % 10;
490
- let n = ones;
491
- if (alternate) {
492
- n *= 2;
493
- if (n > 9) {
494
- n -= 9;
495
- }
496
- }
497
- sum += n;
498
- alternate = !alternate;
499
- n = (value - ones) / 10;
500
- if (alternate) {
501
- n *= 2;
502
- if (n > 9) {
503
- n -= 9;
504
- }
505
- }
506
- sum += n;
507
- alternate = !alternate;
508
- }
509
- }
510
- return sum % 10 === 0;
511
- }
512
- const isISIN = makeStringRule('isISIN', validateISINStr, (varName, ctx) => {
513
- const i = ctx.addRegex(ISIN_RE);
514
- return (`if (!re[${i}].test(${varName})) ${ctx.fail('isISIN')};\n` +
515
- `else { var isSum=0,isAlt=false;\n` +
516
- `for(var isI=${varName}.length-1;isI>=0;isI--){var isCd=${varName}.charCodeAt(isI);` +
517
- `if(isCd<=57){var isN=isCd-48;if(isAlt){isN*=2;if(isN>9)isN-=9;}isSum+=isN;isAlt=!isAlt;}` +
518
- `else{var isVal=isCd-55;var isO=isVal%10;var isN=isO;if(isAlt){isN*=2;if(isN>9)isN-=9;}isSum+=isN;isAlt=!isAlt;` +
519
- `isN=(isVal-isO)/10;if(isAlt){isN*=2;if(isN>9)isN-=9;}isSum+=isN;isAlt=!isAlt;}}\n` +
520
- `if(isSum%10!==0)${ctx.fail('isISIN')}; }`);
521
- });
522
- // ISO 8601
523
- const ISO8601_RE = /^\d{4}(?:-\d{2}(?:-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?)?)?$/;
524
- // Strict ISO8601: requires month/day AND hour/minute/second to be valid values
525
- function validateISO8601Strict(v) {
526
- if (!ISO8601_RE.test(v)) {
527
- return false;
528
- }
529
- const m = v.match(/^(\d{4})-(\d{2})(?:-(\d{2}))?/);
530
- if (!m) {
531
- return true;
532
- } // year-only — no month/day to range-check
533
- const month = Number(m[2]);
534
- if (month < 1 || month > 12) {
535
- return false;
536
- }
537
- if (m[3] !== undefined) {
538
- const day = Number(m[3]);
539
- const maxDay = new Date(Number(m[1]), month, 0).getDate();
540
- if (day < 1 || day > maxDay) {
541
- return false;
542
- }
543
- }
544
- // Time component check: hour 0-23, minute 0-59, second 0-60 (leap second).
545
- const tm = v.match(/T(\d{2}):(\d{2}):(\d{2})/);
546
- if (!tm) {
547
- return true;
548
- }
549
- const hh = Number(tm[1]);
550
- const mm = Number(tm[2]);
551
- const ss = Number(tm[3]);
552
- return hh >= 0 && hh <= 23 && mm >= 0 && mm <= 59 && ss >= 0 && ss <= 60;
553
- }
554
- function isISO8601(options) {
555
- if (options?.strict) {
556
- const validateStrict = (v) => typeof v === 'string' && validateISO8601Strict(v);
557
- return makeRule({
558
- name: 'isISO8601',
559
- requiresType: 'string',
560
- constraints: { format: 'date-time', strict: true },
561
- validate: validateStrict,
562
- emit: (varName, ctx) => {
563
- const i = ctx.addRegex(ISO8601_RE);
564
- return (`if (!re[${i}].test(${varName})) ${ctx.fail('isISO8601')};\n` +
565
- `else { var dm=${varName}.match(/^(\\d{4})-(\\d{2})(?:-(\\d{2}))?/);` +
566
- `if(dm){var mo=Number(dm[2]);` +
567
- `if(mo<1||mo>12){${ctx.fail('isISO8601')}}` +
568
- `else if(dm[3]!==undefined){var da=Number(dm[3]),md=new Date(Number(dm[1]),mo,0).getDate();` +
569
- `if(da<1||da>md){${ctx.fail('isISO8601')}}}}` +
570
- `var tm=${varName}.match(/T(\\d{2}):(\\d{2}):(\\d{2})/);` +
571
- `if(tm){var hh=Number(tm[1]),mm=Number(tm[2]),ss=Number(tm[3]);` +
572
- `if(hh<0||hh>23||mm<0||mm>59||ss<0||ss>60)${ctx.fail('isISO8601')};} }`);
573
- },
574
- });
575
- }
576
- // non-strict: both validate and emit use same ISO8601_RE
577
- return makeStringRule('isISO8601', v => ISO8601_RE.test(v), (varName, ctx) => {
578
- const i = ctx.addRegex(ISO8601_RE);
579
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isISO8601')};`;
580
- }, 'string', { format: 'date-time', strict: false });
581
- }
582
- // ISRC — ISO 3901
583
- const ISRC_RE = /^[A-Z]{2}-[A-Z0-9]{3}-\d{2}-\d{5}$|^[A-Z]{2}[A-Z0-9]{3}\d{7}$/;
584
- const isISRC = makeStringRule('isISRC', v => ISRC_RE.test(v), (varName, ctx) => {
585
- const i = ctx.addRegex(ISRC_RE);
586
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isISRC')};`;
587
- });
588
- function validateISSN(value, options) {
589
- const requireHyphen = options?.requireHyphen !== false;
590
- const s = requireHyphen ? value : value.replace(/-/g, '');
591
- // Format with hyphen: NNNN-NNNX, without: NNNNNNXX
592
- const re = requireHyphen ? /^\d{4}-\d{3}[\dX]$/ : /^\d{7}[\dX]$/;
593
- if (!re.test(s)) {
594
- return false;
595
- }
596
- const digits = s.replace(/-/g, '');
597
- let sum = 0;
598
- for (let i = 0; i < 7; i++) {
599
- sum += (8 - i) * (digits.charCodeAt(i) - 48);
600
- }
601
- const last = digits[7] === 'X' ? 10 : digits.charCodeAt(7) - 48;
602
- sum += last;
603
- return sum % 11 === 0;
604
- }
605
- function isISSN(options) {
606
- const requireHyphen = options?.requireHyphen !== false;
607
- const validateIssn = (value) => typeof value === 'string' && validateISSN(value, options);
608
- const formatRe = requireHyphen ? /^\d{4}-\d{3}[\dX]$/ : /^\d{7}[\dX]$/;
609
- return makeRule({
610
- name: 'isISSN',
611
- requiresType: 'string',
612
- constraints: { requireHyphen: options?.requireHyphen },
613
- validate: validateIssn,
614
- emit: (varName, ctx) => {
615
- const ri = ctx.addRegex(formatRe);
616
- const strip = requireHyphen ? varName : `${varName}.replace(/-/g,'')`;
617
- return (`{var issn=${strip};` +
618
- `if(!re[${ri}].test(issn)){${ctx.fail('isISSN')}}` +
619
- `else{var id=issn.replace(/-/g,''),iss=0;` +
620
- `for(var ii=0;ii<7;ii++)iss+=(8-ii)*(id.charCodeAt(ii)-48);` +
621
- `var il=id[7]==='X'?10:(id.charCodeAt(7)-48);iss+=il;` +
622
- `if(iss%11!==0)${ctx.fail('isISSN')};}}`);
623
- },
624
- });
625
- }
626
- // JWT — 3-part dot-separated base64url
627
- const JWT_RE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
628
- const isJWT = makeStringRule('isJWT', v => JWT_RE.test(v), (varName, ctx) => {
629
- const i = ctx.addRegex(JWT_RE);
630
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isJWT')};`;
631
- });
632
- // LatLong
633
- const LAT_LONG_RE = /^[-+]?([1-8]?\d(?:\.\d+)?|90(?:\.0+)?),\s*[-+]?(180(?:\.0+)?|1[0-7]\d(?:\.\d+)?|\d{1,2}(?:\.\d+)?)$/;
634
- function isLatLong() {
635
- return makeStringRule('isLatLong', v => LAT_LONG_RE.test(v), (varName, ctx) => {
636
- const i = ctx.addRegex(LAT_LONG_RE);
637
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isLatLong')};`;
638
- });
639
- }
640
- // Locale — BCP 47 simplified
641
- const LOCALE_RE = /^[a-zA-Z]{2,3}(?:-[a-zA-Z]{4})?(?:-(?:[a-zA-Z]{2}|\d{3}))?(?:-[a-zA-Z\d]{5,8})*$/;
642
- const isLocale = makeStringRule('isLocale', v => LOCALE_RE.test(v), (varName, ctx) => {
643
- const i = ctx.addRegex(LOCALE_RE);
644
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isLocale')};`;
645
- });
646
- // DataURI
647
- const DATA_URI_RE = /^data:([a-zA-Z0-9!#$&\-^_]+\/[a-zA-Z0-9!#$&\-^_]+)(?:;[a-zA-Z0-9-]+=[a-zA-Z0-9-]+)*(?:;base64)?,[\s\S]*$/;
648
- const isDataURI = makeStringRule('isDataURI', v => DATA_URI_RE.test(v), (varName, ctx) => {
649
- const i = ctx.addRegex(DATA_URI_RE);
650
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isDataURI')};`;
651
- });
652
- function isFQDN(options) {
653
- const requireTld = options?.require_tld !== false;
654
- const allowUnderscores = options?.allow_underscores ?? false;
655
- const allowTrailingDot = options?.allow_trailing_dot ?? false;
656
- const partRe = allowUnderscores ? /^[a-zA-Z0-9_-]+$/ : /^[a-zA-Z0-9-]+$/;
657
- const validateFqdn = (value) => {
658
- if (typeof value !== 'string') {
659
- return false;
660
- }
661
- let str = value;
662
- if (allowTrailingDot && str.endsWith('.')) {
663
- str = str.slice(0, -1);
664
- }
665
- if (str.length === 0) {
666
- return false;
667
- }
668
- const parts = str.split('.');
669
- if (requireTld && parts.length < 2) {
670
- return false;
671
- }
672
- if (requireTld) {
673
- const tld = parts[parts.length - 1];
674
- if (!tld || tld.length < 2 || !/^[a-zA-Z]{2,}$/.test(tld)) {
675
- return false;
676
- }
677
- }
678
- return parts.every(part => {
679
- if (part.length === 0 || part.length > 63) {
680
- return false;
681
- }
682
- if (!partRe.test(part)) {
683
- return false;
684
- }
685
- if (!allowUnderscores && (part.startsWith('-') || part.endsWith('-'))) {
686
- return false;
687
- }
688
- return true;
689
- });
690
- };
691
- return makeRule({
692
- name: 'isFQDN',
693
- requiresType: 'string',
694
- constraints: {
695
- require_tld: options?.require_tld,
696
- allow_underscores: options?.allow_underscores,
697
- allow_trailing_dot: options?.allow_trailing_dot,
698
- },
699
- validate: validateFqdn,
700
- emit: (varName, ctx) => {
701
- const ri = ctx.addRegex(partRe);
702
- const tldRi = requireTld ? ctx.addRegex(/^[a-zA-Z]{2,}$/) : -1;
703
- // Inline for-loop instead of fp.every(function(p){...}) — avoids per-call closure
704
- // allocation inside the JIT executor.
705
- const partCheck = `if(p.length===0||p.length>63){fqOk=false;break;}` +
706
- `if(!re[${ri}].test(p)){fqOk=false;break;}` +
707
- (allowUnderscores ? '' : `if(p[0]==='-'||p[p.length-1]==='-'){fqOk=false;break;}`);
708
- const loopBlock = `var fqOk=true;for(var fi=0;fi<fp.length;fi++){var p=fp[fi];${partCheck}}if(!fqOk)${ctx.fail('isFQDN')};`;
709
- let code = `{var fq=${varName};`;
710
- if (allowTrailingDot) {
711
- code += `if(fq.endsWith('.'))fq=fq.slice(0,-1);`;
712
- }
713
- code += `if(fq.length===0)${ctx.fail('isFQDN')};`;
714
- code += `else{var fp=fq.split('.');`;
715
- if (requireTld) {
716
- code += `if(fp.length<2)${ctx.fail('isFQDN')};`;
717
- code += `else{var tld=fp[fp.length-1];`;
718
- code += `if(!tld||tld.length<2||!re[${tldRi}].test(tld))${ctx.fail('isFQDN')};`;
719
- code += `else{${loopBlock}}`; // close tld inner else block
720
- code += '}'; // close tld outer else block
721
- }
722
- else {
723
- code += loopBlock;
724
- }
725
- code += '}'; // close split else{
726
- code += '}'; // close outer {
727
- return code;
728
- },
729
- });
730
- }
731
- // Port — 0 to 65535
732
- const PORT_RE = /^(?:6553[0-5]|655[0-2]\d|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{1,3}|\d)$/;
733
- const isPort = makeStringRule('isPort', v => PORT_RE.test(v), (varName, ctx) => {
734
- const i = ctx.addRegex(PORT_RE);
735
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isPort')};`;
736
- });
737
- // EAN (EAN-8 and EAN-13 with checksum)
738
- function validateEAN(value) {
739
- if (!/^\d{8}$/.test(value) && !/^\d{13}$/.test(value)) {
740
- return false;
741
- }
742
- // Walk via charCodeAt — no split/map array allocations
743
- const len = value.length;
744
- let sum = 0;
745
- for (let i = 0; i < len - 1; i++) {
746
- const d = value.charCodeAt(i) - 48;
747
- sum += d * (len === 8 ? (i % 2 === 0 ? 3 : 1) : i % 2 === 0 ? 1 : 3);
748
- }
749
- const check = (10 - (sum % 10)) % 10;
750
- return check === value.charCodeAt(len - 1) - 48;
751
- }
752
- const isEAN = makeStringRule('isEAN', validateEAN, (varName, ctx) => {
753
- const re8 = ctx.addRegex(/^\d{8}$/);
754
- const re13 = ctx.addRegex(/^\d{13}$/);
755
- return (`{var ev=${varName};` +
756
- `if(!re[${re8}].test(ev)&&!re[${re13}].test(ev)){${ctx.fail('isEAN')}}` +
757
- `else{var el=ev.length,es=0;` +
758
- `for(var ei=0;ei<el-1;ei++){var ed=ev.charCodeAt(ei)-48;es+=ed*(el===8?(ei%2===0?3:1):(ei%2===0?1:3));}` +
759
- `var ec=(10-(es%10))%10;` +
760
- `if(ec!==(ev.charCodeAt(el-1)-48))${ctx.fail('isEAN')};}}`);
761
- });
762
- // ISO 3166-1 Alpha-2
763
- const ISO31661A2_CODES = new Set([
764
- 'AD',
765
- 'AE',
766
- 'AF',
767
- 'AG',
768
- 'AI',
769
- 'AL',
770
- 'AM',
771
- 'AO',
772
- 'AQ',
773
- 'AR',
774
- 'AS',
775
- 'AT',
776
- 'AU',
777
- 'AW',
778
- 'AX',
779
- 'AZ',
780
- 'BA',
781
- 'BB',
782
- 'BD',
783
- 'BE',
784
- 'BF',
785
- 'BG',
786
- 'BH',
787
- 'BI',
788
- 'BJ',
789
- 'BL',
790
- 'BM',
791
- 'BN',
792
- 'BO',
793
- 'BQ',
794
- 'BR',
795
- 'BS',
796
- 'BT',
797
- 'BV',
798
- 'BW',
799
- 'BY',
800
- 'BZ',
801
- 'CA',
802
- 'CC',
803
- 'CD',
804
- 'CF',
805
- 'CG',
806
- 'CH',
807
- 'CI',
808
- 'CK',
809
- 'CL',
810
- 'CM',
811
- 'CN',
812
- 'CO',
813
- 'CR',
814
- 'CU',
815
- 'CV',
816
- 'CW',
817
- 'CX',
818
- 'CY',
819
- 'CZ',
820
- 'DE',
821
- 'DJ',
822
- 'DK',
823
- 'DM',
824
- 'DO',
825
- 'DZ',
826
- 'EC',
827
- 'EE',
828
- 'EG',
829
- 'EH',
830
- 'ER',
831
- 'ES',
832
- 'ET',
833
- 'FI',
834
- 'FJ',
835
- 'FK',
836
- 'FM',
837
- 'FO',
838
- 'FR',
839
- 'GA',
840
- 'GB',
841
- 'GD',
842
- 'GE',
843
- 'GF',
844
- 'GG',
845
- 'GH',
846
- 'GI',
847
- 'GL',
848
- 'GM',
849
- 'GN',
850
- 'GP',
851
- 'GQ',
852
- 'GR',
853
- 'GS',
854
- 'GT',
855
- 'GU',
856
- 'GW',
857
- 'GY',
858
- 'HK',
859
- 'HM',
860
- 'HN',
861
- 'HR',
862
- 'HT',
863
- 'HU',
864
- 'ID',
865
- 'IE',
866
- 'IL',
867
- 'IM',
868
- 'IN',
869
- 'IO',
870
- 'IQ',
871
- 'IR',
872
- 'IS',
873
- 'IT',
874
- 'JE',
875
- 'JM',
876
- 'JO',
877
- 'JP',
878
- 'KE',
879
- 'KG',
880
- 'KH',
881
- 'KI',
882
- 'KM',
883
- 'KN',
884
- 'KP',
885
- 'KR',
886
- 'KW',
887
- 'KY',
888
- 'KZ',
889
- 'LA',
890
- 'LB',
891
- 'LC',
892
- 'LI',
893
- 'LK',
894
- 'LR',
895
- 'LS',
896
- 'LT',
897
- 'LU',
898
- 'LV',
899
- 'LY',
900
- 'MA',
901
- 'MC',
902
- 'MD',
903
- 'ME',
904
- 'MF',
905
- 'MG',
906
- 'MH',
907
- 'MK',
908
- 'ML',
909
- 'MM',
910
- 'MN',
911
- 'MO',
912
- 'MP',
913
- 'MQ',
914
- 'MR',
915
- 'MS',
916
- 'MT',
917
- 'MU',
918
- 'MV',
919
- 'MW',
920
- 'MX',
921
- 'MY',
922
- 'MZ',
923
- 'NA',
924
- 'NC',
925
- 'NE',
926
- 'NF',
927
- 'NG',
928
- 'NI',
929
- 'NL',
930
- 'NO',
931
- 'NP',
932
- 'NR',
933
- 'NU',
934
- 'NZ',
935
- 'OM',
936
- 'PA',
937
- 'PE',
938
- 'PF',
939
- 'PG',
940
- 'PH',
941
- 'PK',
942
- 'PL',
943
- 'PM',
944
- 'PN',
945
- 'PR',
946
- 'PS',
947
- 'PT',
948
- 'PW',
949
- 'PY',
950
- 'QA',
951
- 'RE',
952
- 'RO',
953
- 'RS',
954
- 'RU',
955
- 'RW',
956
- 'SA',
957
- 'SB',
958
- 'SC',
959
- 'SD',
960
- 'SE',
961
- 'SG',
962
- 'SH',
963
- 'SI',
964
- 'SJ',
965
- 'SK',
966
- 'SL',
967
- 'SM',
968
- 'SN',
969
- 'SO',
970
- 'SR',
971
- 'SS',
972
- 'ST',
973
- 'SV',
974
- 'SX',
975
- 'SY',
976
- 'SZ',
977
- 'TC',
978
- 'TD',
979
- 'TF',
980
- 'TG',
981
- 'TH',
982
- 'TJ',
983
- 'TK',
984
- 'TL',
985
- 'TM',
986
- 'TN',
987
- 'TO',
988
- 'TR',
989
- 'TT',
990
- 'TV',
991
- 'TW',
992
- 'TZ',
993
- 'UA',
994
- 'UG',
995
- 'UM',
996
- 'US',
997
- 'UY',
998
- 'UZ',
999
- 'VA',
1000
- 'VC',
1001
- 'VE',
1002
- 'VG',
1003
- 'VI',
1004
- 'VN',
1005
- 'VU',
1006
- 'WF',
1007
- 'WS',
1008
- 'YE',
1009
- 'YT',
1010
- 'ZA',
1011
- 'ZM',
1012
- 'ZW',
1013
- ]);
1014
- const isISO31661Alpha2 = makeRule({
1015
- name: 'isISO31661Alpha2',
1016
- requiresType: 'string',
1017
- constraints: {},
1018
- validate: value => typeof value === 'string' && ISO31661A2_CODES.has(value.toUpperCase()),
1019
- emit: (varName, ctx) => {
1020
- const i = ctx.addRef(ISO31661A2_CODES);
1021
- return `if (!refs[${i}].has(${varName}.toUpperCase())) ${ctx.fail('isISO31661Alpha2')};`;
1022
- },
1023
- });
1024
- // ISO 3166-1 Alpha-3
1025
- const ISO31661A3_CODES = new Set([
1026
- 'ABW',
1027
- 'AFG',
1028
- 'AGO',
1029
- 'AIA',
1030
- 'ALA',
1031
- 'ALB',
1032
- 'AND',
1033
- 'ANT',
1034
- 'ARE',
1035
- 'ARG',
1036
- 'ARM',
1037
- 'ASM',
1038
- 'ATA',
1039
- 'ATF',
1040
- 'ATG',
1041
- 'AUS',
1042
- 'AUT',
1043
- 'AZE',
1044
- 'BDI',
1045
- 'BEL',
1046
- 'BEN',
1047
- 'BES',
1048
- 'BFA',
1049
- 'BGD',
1050
- 'BGR',
1051
- 'BHR',
1052
- 'BHS',
1053
- 'BIH',
1054
- 'BLM',
1055
- 'BLR',
1056
- 'BLZ',
1057
- 'BMU',
1058
- 'BOL',
1059
- 'BRA',
1060
- 'BRB',
1061
- 'BRN',
1062
- 'BTN',
1063
- 'BVT',
1064
- 'BWA',
1065
- 'CAF',
1066
- 'CAN',
1067
- 'CCK',
1068
- 'CHE',
1069
- 'CHL',
1070
- 'CHN',
1071
- 'CIV',
1072
- 'CMR',
1073
- 'COD',
1074
- 'COG',
1075
- 'COK',
1076
- 'COL',
1077
- 'COM',
1078
- 'CPV',
1079
- 'CRI',
1080
- 'CUB',
1081
- 'CUW',
1082
- 'CXR',
1083
- 'CYM',
1084
- 'CYP',
1085
- 'CZE',
1086
- 'DEU',
1087
- 'DJI',
1088
- 'DMA',
1089
- 'DNK',
1090
- 'DOM',
1091
- 'DZA',
1092
- 'ECU',
1093
- 'EGY',
1094
- 'ERI',
1095
- 'ESH',
1096
- 'ESP',
1097
- 'EST',
1098
- 'ETH',
1099
- 'FIN',
1100
- 'FJI',
1101
- 'FLK',
1102
- 'FRA',
1103
- 'FRO',
1104
- 'FSM',
1105
- 'GAB',
1106
- 'GBR',
1107
- 'GEO',
1108
- 'GGY',
1109
- 'GHA',
1110
- 'GIB',
1111
- 'GIN',
1112
- 'GLP',
1113
- 'GMB',
1114
- 'GNB',
1115
- 'GNQ',
1116
- 'GRC',
1117
- 'GRD',
1118
- 'GRL',
1119
- 'GTM',
1120
- 'GUF',
1121
- 'GUM',
1122
- 'GUY',
1123
- 'HKG',
1124
- 'HMD',
1125
- 'HND',
1126
- 'HRV',
1127
- 'HTI',
1128
- 'HUN',
1129
- 'IDN',
1130
- 'IMN',
1131
- 'IND',
1132
- 'IOT',
1133
- 'IRL',
1134
- 'IRN',
1135
- 'IRQ',
1136
- 'ISL',
1137
- 'ISR',
1138
- 'ITA',
1139
- 'JAM',
1140
- 'JEY',
1141
- 'JOR',
1142
- 'JPN',
1143
- 'KAZ',
1144
- 'KEN',
1145
- 'KGZ',
1146
- 'KHM',
1147
- 'KIR',
1148
- 'KNA',
1149
- 'KOR',
1150
- 'KWT',
1151
- 'LAO',
1152
- 'LBN',
1153
- 'LBR',
1154
- 'LBY',
1155
- 'LCA',
1156
- 'LIE',
1157
- 'LKA',
1158
- 'LSO',
1159
- 'LTU',
1160
- 'LUX',
1161
- 'LVA',
1162
- 'MAC',
1163
- 'MAF',
1164
- 'MAR',
1165
- 'MCO',
1166
- 'MDA',
1167
- 'MDG',
1168
- 'MDV',
1169
- 'MEX',
1170
- 'MHL',
1171
- 'MKD',
1172
- 'MLI',
1173
- 'MLT',
1174
- 'MMR',
1175
- 'MNE',
1176
- 'MNG',
1177
- 'MNP',
1178
- 'MOZ',
1179
- 'MRT',
1180
- 'MSR',
1181
- 'MTQ',
1182
- 'MUS',
1183
- 'MWI',
1184
- 'MYS',
1185
- 'MYT',
1186
- 'NAM',
1187
- 'NCL',
1188
- 'NER',
1189
- 'NFK',
1190
- 'NGA',
1191
- 'NIC',
1192
- 'NIU',
1193
- 'NLD',
1194
- 'NOR',
1195
- 'NPL',
1196
- 'NRU',
1197
- 'NZL',
1198
- 'OMN',
1199
- 'PAK',
1200
- 'PAN',
1201
- 'PCN',
1202
- 'PER',
1203
- 'PHL',
1204
- 'PLW',
1205
- 'PNG',
1206
- 'POL',
1207
- 'PRI',
1208
- 'PRK',
1209
- 'PRT',
1210
- 'PRY',
1211
- 'PSE',
1212
- 'PYF',
1213
- 'QAT',
1214
- 'REU',
1215
- 'ROU',
1216
- 'RUS',
1217
- 'RWA',
1218
- 'SAU',
1219
- 'SDN',
1220
- 'SEN',
1221
- 'SGP',
1222
- 'SGS',
1223
- 'SHN',
1224
- 'SJM',
1225
- 'SLB',
1226
- 'SLE',
1227
- 'SLV',
1228
- 'SMR',
1229
- 'SOM',
1230
- 'SPM',
1231
- 'SRB',
1232
- 'SSD',
1233
- 'STP',
1234
- 'SUR',
1235
- 'SVK',
1236
- 'SVN',
1237
- 'SWE',
1238
- 'SWZ',
1239
- 'SXM',
1240
- 'SYC',
1241
- 'SYR',
1242
- 'TCA',
1243
- 'TCD',
1244
- 'TGO',
1245
- 'THA',
1246
- 'TJK',
1247
- 'TKL',
1248
- 'TKM',
1249
- 'TLS',
1250
- 'TON',
1251
- 'TTO',
1252
- 'TUN',
1253
- 'TUR',
1254
- 'TUV',
1255
- 'TWN',
1256
- 'TZA',
1257
- 'UGA',
1258
- 'UKR',
1259
- 'UMI',
1260
- 'URY',
1261
- 'USA',
1262
- 'UZB',
1263
- 'VAT',
1264
- 'VCT',
1265
- 'VEN',
1266
- 'VGB',
1267
- 'VIR',
1268
- 'VNM',
1269
- 'VUT',
1270
- 'WLF',
1271
- 'WSM',
1272
- 'YEM',
1273
- 'ZAF',
1274
- 'ZMB',
1275
- 'ZWE',
1276
- ]);
1277
- const isISO31661Alpha3 = makeRule({
1278
- name: 'isISO31661Alpha3',
1279
- requiresType: 'string',
1280
- constraints: {},
1281
- validate: value => typeof value === 'string' && ISO31661A3_CODES.has(value.toUpperCase()),
1282
- emit: (varName, ctx) => {
1283
- const i = ctx.addRef(ISO31661A3_CODES);
1284
- return `if (!refs[${i}].has(${varName}.toUpperCase())) ${ctx.fail('isISO31661Alpha3')};`;
1285
- },
1286
- });
1287
- // BIC / SWIFT code — case-insensitive via /i flag avoids per-call .toUpperCase() string allocation
1288
- const BIC_RE = /^[A-Z]{6}[A-Z0-9]{2}(?:[A-Z0-9]{3})?$/i;
1289
- const isBIC = makeStringRule('isBIC', v => BIC_RE.test(v), (varName, ctx) => {
1290
- const i = ctx.addRegex(BIC_RE);
1291
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isBIC')};`;
1292
- });
1293
- // Firebase Push ID — 20 chars, base64url charset (-0-9A-Za-z_)
1294
- const FIREBASE_RE = /^[a-zA-Z0-9_-]{20}$/;
1295
- const isFirebasePushId = makeStringRule('isFirebasePushId', v => FIREBASE_RE.test(v), (varName, ctx) => {
1296
- const i = ctx.addRegex(FIREBASE_RE);
1297
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isFirebasePushId')};`;
1298
- });
1299
- // SemVer — Semantic Versioning 2.0
1300
- const SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
1301
- const isSemVer = makeStringRule('isSemVer', v => SEMVER_RE.test(v), (varName, ctx) => {
1302
- const i = ctx.addRegex(SEMVER_RE);
1303
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isSemVer')};`;
1304
- });
1305
- // MongoDB ObjectId — 24-char hex
1306
- const MONGO_ID_RE = /^[0-9a-fA-F]{24}$/;
1307
- const isMongoId = makeStringRule('isMongoId', v => MONGO_ID_RE.test(v), (varName, ctx) => {
1308
- const i = ctx.addRegex(MONGO_ID_RE);
1309
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isMongoId')};`;
1310
- });
1311
- // JSON
1312
- const validateJsonString = (value) => {
1313
- if (typeof value !== 'string') {
1314
- return false;
1315
- }
1316
- try {
1317
- JSON.parse(value);
1318
- return true;
1319
- }
1320
- catch {
1321
- return false;
1322
- }
1323
- };
1324
- const isJSON = makeRule({
1325
- name: 'isJSON',
1326
- requiresType: 'string',
1327
- constraints: {},
1328
- validate: validateJsonString,
1329
- emit: (varName, ctx) => `try { JSON.parse(${varName}); } catch { ${ctx.fail('isJSON')}; }`,
1330
- });
1331
- // Base32
1332
- const BASE32_RE = /^[A-Z2-7]+=*$/i;
1333
- // Empty-string fails the `+`-quantified regex anyway, so the explicit length===0 check is dead.
1334
- function isBase32() {
1335
- return makeStringRule('isBase32', v => v.length % 8 === 0 && BASE32_RE.test(v), (varName, ctx) => {
1336
- const i = ctx.addRegex(BASE32_RE);
1337
- return `if (${varName}.length % 8 !== 0 || !re[${i}].test(${varName})) ${ctx.fail('isBase32')};`;
1338
- });
1339
- }
1340
- // Base58
1341
- const BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]+$/;
1342
- const isBase58 = makeStringRule('isBase58', v => BASE58_RE.test(v), (varName, ctx) => {
1343
- const i = ctx.addRegex(BASE58_RE);
1344
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isBase58')};`;
1345
- });
1346
- // Base64
1347
- const BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/;
1348
- const BASE64_URL_RE = /^[A-Za-z0-9_-]+={0,2}$/;
1349
- function isBase64(options) {
1350
- const re = options?.urlSafe ? BASE64_URL_RE : BASE64_RE;
1351
- // Empty-string check is redundant — both base64 regexes require ≥1 char and fail on empty input.
1352
- return makeStringRule('isBase64', v => re.test(v), (varName, ctx) => {
1353
- const i = ctx.addRegex(re);
1354
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isBase64')};`;
1355
- }, 'string', { urlSafe: options?.urlSafe });
1356
- }
1357
- // DateString — ISO 8601 date only (YYYY-MM-DD) with calendar validity (day must exist in month/year).
1358
- const DATE_STRING_RE = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
1359
- function isCalendarValidDate(v) {
1360
- if (!DATE_STRING_RE.test(v)) {
1361
- return false;
1362
- }
1363
- const y = Number(v.slice(0, 4));
1364
- const m = Number(v.slice(5, 7));
1365
- const d = Number(v.slice(8, 10));
1366
- const maxDay = new Date(y, m, 0).getDate();
1367
- return d >= 1 && d <= maxDay;
1368
- }
1369
- function isDateString() {
1370
- return makeStringRule('isDateString', isCalendarValidDate, (varName, ctx) => {
1371
- const i = ctx.addRegex(DATE_STRING_RE);
1372
- return (`if (!re[${i}].test(${varName})) ${ctx.fail('isDateString')};\n` +
1373
- `else { var y=Number(${varName}.slice(0,4)),m=Number(${varName}.slice(5,7)),d=Number(${varName}.slice(8,10));` +
1374
- `var md=new Date(y,m,0).getDate(); if(d<1||d>md)${ctx.fail('isDateString')}; }`);
1375
- });
1376
- }
1377
- // MimeType
1378
- const MIME_TYPE_RE = /^(application|audio|font|image|message|model|multipart|text|video)\/[a-zA-Z0-9][a-zA-Z0-9!#$&\-^_.+]*(?:;.+)?$/;
1379
- const isMimeType = makeStringRule('isMimeType', v => MIME_TYPE_RE.test(v), (varName, ctx) => {
1380
- const i = ctx.addRegex(MIME_TYPE_RE);
1381
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isMimeType')};`;
1382
- });
1383
- // Currency
1384
- // A single optional sign, either before the `$` (`-$5`, `+5`) or after it (`$-5`, `$+5`) — never
1385
- // both. The previous `[-+]?\$?-?` allowed two signs (e.g. `+-5`, `-$-5`).
1386
- const CURRENCY_RE = /^(?:[-+]?\$?|\$[-+]?)(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d{1,2})?$/;
1387
- function isCurrency() {
1388
- // Currency regex requires at least one digit; empty input fails the regex by itself.
1389
- return makeStringRule('isCurrency', v => CURRENCY_RE.test(v), (varName, ctx) => {
1390
- const i = ctx.addRegex(CURRENCY_RE);
1391
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isCurrency')};`;
1392
- });
1393
- }
1394
- // Magnet URI
1395
- const MAGNET_URI_RE = /^magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}(?:&[a-z][a-z0-9.]*=[^&\s]*)*$/i;
1396
- const isMagnetURI = makeStringRule('isMagnetURI', v => MAGNET_URI_RE.test(v), (varName, ctx) => {
1397
- const i = ctx.addRegex(MAGNET_URI_RE);
1398
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isMagnetURI')};`;
1399
- });
1400
- // ─────────────────────────────────────────────────────────────────────────────
1401
- // Group D: Algorithm-based
1402
- // ─────────────────────────────────────────────────────────────────────────────
1403
- // Credit Card — Luhn algorithm (§4.8 C)
1404
- function luhn(str) {
1405
- const s = str.replace(/[\s-]/g, '');
1406
- if (s.length === 0 || !/^\d+$/.test(s)) {
1407
- return false;
1408
- }
1409
- let sum = 0;
1410
- let alternate = false;
1411
- for (let i = s.length - 1; i >= 0; i--) {
1412
- let n = s.charCodeAt(i) - 48;
1413
- if (alternate) {
1414
- n *= 2;
1415
- if (n > 9) {
1416
- n -= 9;
1417
- }
1418
- }
1419
- sum += n;
1420
- alternate = !alternate;
1421
- }
1422
- return sum % 10 === 0;
1423
- }
1424
- const isCreditCard = makeRule({
1425
- name: 'isCreditCard',
1426
- requiresType: 'string',
1427
- constraints: {},
1428
- validate: value => typeof value === 'string' && luhn(value),
1429
- emit: (varName, ctx) => `{
1430
- var cs=${varName}.replace(/[\\s-]/g,'');
1431
- if(cs.length===0||!/^\\d+$/.test(cs)){${ctx.fail('isCreditCard')}}
1
+ import{CacheKey as k,RequiredType as f,RuleOp as h}from"../enums.js";import{makePlannedRule as g,makeRule as W,planCompare as B,planLength as y,planOr as Cj}from"../rule-plan.js";function X(j,Z,$,z=f.String,Q={}){return W({name:j,requiresType:z,constraints:Q,validate:(K)=>typeof K==="string"&&Z(K),emit:$})}function minLength(j){const Z={cacheKey:k.Length,failure:B(y(),h.Lt,j)};return g({name:"minLength",requiresType:f.String,constraints:{min:j},plan:Z,validate:($)=>typeof $==="string"&&$.length>=j})}function maxLength(j){const Z={cacheKey:k.Length,failure:B(y(),h.Gt,j)};return g({name:"maxLength",requiresType:f.String,constraints:{max:j},plan:Z,validate:($)=>typeof $==="string"&&$.length<=j})}function length(j,Z){const $={cacheKey:k.Length,failure:Cj(B(y(),h.Lt,j),B(y(),h.Gt,Z))};return g({name:"length",requiresType:f.String,constraints:{min:j,max:Z},plan:$,validate:(z)=>typeof z==="string"&&z.length>=j&&z.length<=Z})}function contains(j){return W({name:"contains",requiresType:f.String,constraints:{seed:j},validate:(Z)=>typeof Z==="string"&&Z.includes(j),emit:(Z,$)=>{const z=$.addRef(j);return`if (!${Z}.includes(refs[${z}])) ${$.fail("contains")};`}})}function notContains(j){return W({name:"notContains",requiresType:f.String,constraints:{seed:j},validate:(Z)=>typeof Z==="string"&&!Z.includes(j),emit:(Z,$)=>{const z=$.addRef(j);return`if (${Z}.includes(refs[${z}])) ${$.fail("notContains")};`}})}function matches(j,Z){const $=j instanceof RegExp?j:new RegExp(j,Z);return W({name:"matches",requiresType:f.String,constraints:{pattern:$.source},validate:(z)=>typeof z==="string"&&$.test(z),emit:(z,Q)=>{return`if (!re[${Q.addRegex($)}].test(${z})) ${Q.fail("matches")};`}})}const isLowercase=W({name:"isLowercase",requiresType:f.String,constraints:{},validate:(j)=>typeof j==="string"&&j===j.toLowerCase(),emit:(j,Z)=>`if (${j} !== ${j}.toLowerCase()) ${Z.fail("isLowercase")};`});const isUppercase=W({name:"isUppercase",requiresType:f.String,constraints:{},validate:(j)=>typeof j==="string"&&j===j.toUpperCase(),emit:(j,Z)=>`if (${j} !== ${j}.toUpperCase()) ${Z.fail("isUppercase")};`});const L=new RegExp(`^[${String.fromCharCode(0)}-${String.fromCharCode(127)}]*$`);const isAscii=X("isAscii",(j)=>L.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(L)}].test(${j})) ${Z.fail("isAscii")};`});const I=/^[a-zA-Z]+$/;const isAlpha=X("isAlpha",(j)=>I.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(I)}].test(${j})) ${Z.fail("isAlpha")};`});const T=/^[a-zA-Z0-9]+$/;const isAlphanumeric=X("isAlphanumeric",(j)=>T.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(T)}].test(${j})) ${Z.fail("isAlphanumeric")};`});const S=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;const isHttpToken=X("isHttpToken",(j)=>S.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(S)}].test(${j})) ${Z.fail("isHttpToken")};`});const A=(j)=>{if(j==="null")return!0;try{return new URL(j).origin===j}catch{return!1}};const isOrigin=W({name:"isOrigin",requiresType:f.String,constraints:{format:"origin"},validate:(j)=>typeof j==="string"&&A(j),emit:(j,Z)=>{return`if (!(refs[${Z.addRef(A)}](${j}))) ${Z.fail("isOrigin")};`}});const d=(j)=>j==="*"||A(j);const isCorsOrigin=W({name:"isCorsOrigin",requiresType:f.String,constraints:{format:"origin",allowWildcard:!0},validate:(j)=>typeof j==="string"&&d(j),emit:(j,Z)=>{return`if (!(refs[${Z.addRef(d)}](${j}))) ${Z.fail("isCorsOrigin")};`}});const isBooleanString=W({name:"isBooleanString",requiresType:f.String,constraints:{},validate:(j)=>j==="true"||j==="false"||j==="1"||j==="0",emit:(j,Z)=>`if (${j} !== 'true' && ${j} !== 'false' && ${j} !== '1' && ${j} !== '0') ${Z.fail("isBooleanString")};`});const Aj=/^[0-9]+$/;const kj=/^[+-]?(?:[0-9]*\.)?[0-9]+$/;function isNumberString(j){const Z=j?.no_symbols??!1,$=Z?Aj:kj;return X("isNumberString",(z)=>$.test(z),(z,Q)=>{return`if (!re[${Q.addRegex($)}].test(${z})) ${Q.fail("isNumberString")};`},f.String,{no_symbols:Z})}function isDecimal(){const j=/^[-+]?(?:\d+(?:\.\d+)?|\.\d+)$/;return X("isDecimal",(Z)=>j.test(Z),(Z,$)=>{return`if (!re[${$.addRegex(j)}].test(${Z})) ${$.fail("isDecimal")};`})}const O=/[^\u0020-\u007E\uFF61-\uFF9F]/;const isFullWidth=X("isFullWidth",(j)=>O.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(O)}].test(${j})) ${Z.fail("isFullWidth")};`});const C=/[\u0020-\u007E\uFF61-\uFF9F]/;const isHalfWidth=X("isHalfWidth",(j)=>C.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(C)}].test(${j})) ${Z.fail("isHalfWidth")};`});const isVariableWidth=X("isVariableWidth",(j)=>O.test(j)&&C.test(j),(j,Z)=>{const $=Z.addRegex(O),z=Z.addRegex(C);return`if (!re[${$}].test(${j}) || !re[${z}].test(${j})) ${Z.fail("isVariableWidth")};`});const p=new RegExp(`[^${String.fromCharCode(0)}-${String.fromCharCode(255)}]`);const isMultibyte=X("isMultibyte",(j)=>p.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(p)}].test(${j})) ${Z.fail("isMultibyte")};`});const _=/[\uD800-\uDBFF][\uDC00-\uDFFF]/;const isSurrogatePair=X("isSurrogatePair",(j)=>_.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(_)}].test(${j})) ${Z.fail("isSurrogatePair")};`});const E=/^[0-9a-fA-F]+$/;const isHexadecimal=X("isHexadecimal",(j)=>E.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(E)}].test(${j})) ${Z.fail("isHexadecimal")};`});const o=/^(0[oO])?[0-7]+$/;const isOctal=X("isOctal",(j)=>o.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(o)}].test(${j})) ${Z.fail("isOctal")};`});const N=/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;function isEmail(){return X("isEmail",(j)=>N.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(N)}].test(${j})) ${Z.fail("isEmail")};`},f.String,{format:"email"})}const gj=["http","https","ftp"];function isURL(j){const Z=j?.protocols??gj,$=Z.map((Q)=>Q.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|"),z=new RegExp(`^(?:${$}):\\/\\/(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)(?::(6553[0-5]|655[0-2]\\d|65[0-4]\\d{2}|6[0-4]\\d{3}|[1-5]\\d{4}|[1-9]\\d{0,3}|0))?(?:\\/[^\\s]*)?$`);return W({name:"isURL",requiresType:f.String,constraints:{format:"uri",protocols:Z},validate:(Q)=>typeof Q==="string"&&z.test(Q),emit:(Q,K)=>{return`if (!re[${K.addRegex(z)}].test(${Q})) ${K.fail("isURL")};`}})}const R={all:/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,1:/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-1[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,2:/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-2[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,3:/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,4:/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,5:/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/};function isUUID(j){const Z=j!=null?R[j]:R.all;return X("isUUID",($)=>Z.test($),($,z)=>{return`if (!re[${z.addRegex(Z)}].test(${$})) ${z.fail("isUUID")};`},f.String,{format:"uuid",version:j})}const q=/^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/;const D=/^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}$|^::$|^::1$|^::(?:ffff(?::0{1,4})?:)?(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$|^(?:[0-9a-fA-F]{1,4}:){1,4}:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;function isIP(j){return W({name:"isIP",requiresType:f.String,constraints:{version:j},validate:(Z)=>{if(typeof Z!=="string")return!1;if(j===4)return q.test(Z);if(j===6)return D.test(Z);return q.test(Z)||D.test(Z)},emit:(Z,$)=>{if(j===4)return`if (!re[${$.addRegex(q)}].test(${Z})) ${$.fail("isIP")};`;if(j===6)return`if (!re[${$.addRegex(D)}].test(${Z})) ${$.fail("isIP")};`;const z=$.addRegex(q),Q=$.addRegex(D);return`if (!re[${z}].test(${Z}) && !re[${Q}].test(${Z})) ${$.fail("isIP")};`}})}const s=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;const isHexColor=X("isHexColor",(j)=>s.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(s)}].test(${j})) ${Z.fail("isHexColor")};`});const U=/^rgb\(\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*\)$/;const P=/^rgba\(\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\s*,\s*(0|0?\.\d+|1(\.0+)?)\s*\)$/;const n=/^rgb\(\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*\)$/;const x=/^rgba\(\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*,\s*(\d{1,2}|100)%\s*,\s*(0|0?\.\d+|1(?:\.0+)?)\s*\)$/;function isRgbColor(j=!1){return W({name:"isRgbColor",requiresType:f.String,constraints:{includePercentValues:j},validate:(Z)=>{if(typeof Z!=="string")return!1;if(j)return n.test(Z)||x.test(Z)||U.test(Z)||P.test(Z);return U.test(Z)||P.test(Z)},emit:(Z,$)=>{if(j){const K=$.addRegex(n),Y=$.addRegex(x),J=$.addRegex(U),w=$.addRegex(P);return`if (!re[${K}].test(${Z}) && !re[${Y}].test(${Z}) && !re[${J}].test(${Z}) && !re[${w}].test(${Z})) ${$.fail("isRgbColor")};`}const z=$.addRegex(U),Q=$.addRegex(P);return`if (!re[${z}].test(${Z}) && !re[${Q}].test(${Z})) ${$.fail("isRgbColor")};`}})}const u=/^(?:hsl\(\s*(?:360|3[0-5]\d|[12]\d{2}|[1-9]\d|\d)\s*,\s*(?:100|[1-9]\d|\d)%\s*,\s*(?:100|[1-9]\d|\d)%\s*\)|hsla\(\s*(?:360|3[0-5]\d|[12]\d{2}|[1-9]\d|\d)\s*,\s*(?:100|[1-9]\d|\d)%\s*,\s*(?:100|[1-9]\d|\d)%\s*,\s*(?:0|0?\.\d+|1(?:\.0+)?)\s*\))$/;const isHSL=X("isHSL",(j)=>u.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(u)}].test(${j})) ${Z.fail("isHSL")};`});const l=/^[0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5}$/;const i=/^[0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5}$/;const m=/^[0-9a-fA-F]{12}$/;function isMACAddress(j){return W({name:"isMACAddress",requiresType:f.String,constraints:{no_separators:j?.no_separators},validate:(Z)=>{if(typeof Z!=="string")return!1;if(j?.no_separators)return m.test(Z);return l.test(Z)||i.test(Z)},emit:(Z,$)=>{if(j?.no_separators)return`if (!re[${$.addRegex(m)}].test(${Z})) ${$.fail("isMACAddress")};`;const z=$.addRegex(l),Q=$.addRegex(i);return`if (!re[${z}].test(${Z}) && !re[${Q}].test(${Z})) ${$.fail("isMACAddress")};`}})}function c(j){const Z=j.replace(/[-\s]/g,"");if(!/^\d{9}[\dX]$/.test(Z))return!1;let $=0;for(let Q=0;Q<9;Q++)$+=(10-Q)*(Z.charCodeAt(Q)-48);const z=Z[9]==="X"?10:Z.charCodeAt(9)-48;$+=z;return $%11===0}function r(j){const Z=j.replace(/[-\s]/g,"");if(!/^\d{13}$/.test(Z))return!1;let $=0;for(let Q=0;Q<12;Q++)$+=(Z.charCodeAt(Q)-48)*(Q%2===0?1:3);return(10-$%10)%10===Z.charCodeAt(12)-48}function isISBN(j){const Z=(Q)=>{if(typeof Q!=="string")return!1;if(j===10)return c(Q);if(j===13)return r(Q);return c(Q)||r(Q)},$=(Q)=>`{var s=${Q}.replace(/[-\\s]/g,'');if(!/^\\d{9}[\\dX]$/.test(s)){%%FAIL%%}else{var sm=0;for(var i=0;i<9;i++)sm+=(10-i)*(s.charCodeAt(i)-48);var l=s[9]==='X'?10:(s.charCodeAt(9)-48);sm+=l;if(sm%11!==0){%%FAIL%%}}}`,z=(Q)=>`{var s=${Q}.replace(/[-\\s]/g,'');if(!/^\\d{13}$/.test(s)){%%FAIL%%}else{var sm=0;for(var i=0;i<12;i++)sm+=(s.charCodeAt(i)-48)*(i%2===0?1:3);var ck=(10-(sm%10))%10;if(ck!==(s.charCodeAt(12)-48)){%%FAIL%%}}}`;return W({name:"isISBN",requiresType:f.String,constraints:{version:j},validate:Z,emit:(Q,K)=>{const Y=K.fail("isISBN");if(j===10)return $(Q).replace(/%%FAIL%%/g,Y);if(j===13)return z(Q).replace(/%%FAIL%%/g,Y);const J=$(Q).replace(/%%FAIL%%/g,"__isbn_ok=false"),w=z(Q).replace(/%%FAIL%%/g,"__isbn_ok=false");return`{var __isbn_ok=true;${J} if(!__isbn_ok){__isbn_ok=true;${w}} if(!__isbn_ok)${Y};}`}})}const Bj=/^[A-Z]{2}[A-Z0-9]{9}[0-9]$/;function Lj(j){if(!Bj.test(j))return!1;let Z=0,$=!1;for(let z=j.length-1;z>=0;z--){const Q=j.charCodeAt(z);if(Q<=57){let K=Q-48;if($){K*=2;if(K>9)K-=9}Z+=K;$=!$}else{const K=Q-55,Y=K%10;let J=Y;if($){J*=2;if(J>9)J-=9}Z+=J;$=!$;J=(K-Y)/10;if($){J*=2;if(J>9)J-=9}Z+=J;$=!$}}return Z%10===0}const isISIN=X("isISIN",Lj,(j,Z)=>{return`if (!re[${Z.addRegex(Bj)}].test(${j})) ${Z.fail("isISIN")};
2
+ else { var isSum=0,isAlt=false;
3
+ for(var isI=${j}.length-1;isI>=0;isI--){var isCd=${j}.charCodeAt(isI);if(isCd<=57){var isN=isCd-48;if(isAlt){isN*=2;if(isN>9)isN-=9;}isSum+=isN;isAlt=!isAlt;}else{var isVal=isCd-55;var isO=isVal%10;var isN=isO;if(isAlt){isN*=2;if(isN>9)isN-=9;}isSum+=isN;isAlt=!isAlt;isN=(isVal-isO)/10;if(isAlt){isN*=2;if(isN>9)isN-=9;}isSum+=isN;isAlt=!isAlt;}}
4
+ if(isSum%10!==0)${Z.fail("isISIN")}; }`});const H=/^\d{4}(?:-\d{2}(?:-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?)?)?$/;function Ij(j){if(!H.test(j))return!1;const Z=j.match(/^(\d{4})-(\d{2})(?:-(\d{2}))?/);if(!Z)return!0;const $=Number(Z[2]);if($<1||$>12)return!1;if(Z[3]!==void 0){const J=Number(Z[3]),w=new Date(Number(Z[1]),$,0).getDate();if(J<1||J>w)return!1}const z=j.match(/T(\d{2}):(\d{2}):(\d{2})/);if(!z)return!0;const Q=Number(z[1]),K=Number(z[2]),Y=Number(z[3]);return Q>=0&&Q<=23&&K>=0&&K<=59&&Y>=0&&Y<=60}function isISO8601(j){if(j?.strict){const Z=($)=>typeof $==="string"&&Ij($);return W({name:"isISO8601",requiresType:f.String,constraints:{format:"date-time",strict:!0},validate:Z,emit:($,z)=>{return`if (!re[${z.addRegex(H)}].test(${$})) ${z.fail("isISO8601")};
5
+ else { var dm=${$}.match(/^(\\d{4})-(\\d{2})(?:-(\\d{2}))?/);if(dm){var mo=Number(dm[2]);if(mo<1||mo>12){${z.fail("isISO8601")}}else if(dm[3]!==undefined){var da=Number(dm[3]),md=new Date(Number(dm[1]),mo,0).getDate();if(da<1||da>md){${z.fail("isISO8601")}}}}var tm=${$}.match(/T(\\d{2}):(\\d{2}):(\\d{2})/);if(tm){var hh=Number(tm[1]),mm=Number(tm[2]),ss=Number(tm[3]);if(hh<0||hh>23||mm<0||mm>59||ss<0||ss>60)${z.fail("isISO8601")};} }`}})}return X("isISO8601",(Z)=>H.test(Z),(Z,$)=>{return`if (!re[${$.addRegex(H)}].test(${Z})) ${$.fail("isISO8601")};`},f.String,{format:"date-time",strict:!1})}const v=/^[A-Z]{2}-[A-Z0-9]{3}-\d{2}-\d{5}$|^[A-Z]{2}[A-Z0-9]{3}\d{7}$/;const isISRC=X("isISRC",(j)=>v.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(v)}].test(${j})) ${Z.fail("isISRC")};`});function Tj(j,Z){const $=Z?.requireHyphen!==!1,z=$?j:j.replace(/-/g,"");if(!($?/^\d{4}-\d{3}[\dX]$/:/^\d{7}[\dX]$/).test(z))return!1;const K=z.replace(/-/g,"");let Y=0;for(let w=0;w<7;w++)Y+=(8-w)*(K.charCodeAt(w)-48);const J=K[7]==="X"?10:K.charCodeAt(7)-48;Y+=J;return Y%11===0}function isISSN(j){const Z=j?.requireHyphen!==!1,$=(Q)=>typeof Q==="string"&&Tj(Q,j),z=Z?/^\d{4}-\d{3}[\dX]$/:/^\d{7}[\dX]$/;return W({name:"isISSN",requiresType:f.String,constraints:{requireHyphen:j?.requireHyphen},validate:$,emit:(Q,K)=>{const Y=K.addRegex(z);return`{var issn=${Z?Q:`${Q}.replace(/-/g,'')`};if(!re[${Y}].test(issn)){${K.fail("isISSN")}}else{var id=issn.replace(/-/g,''),iss=0;for(var ii=0;ii<7;ii++)iss+=(8-ii)*(id.charCodeAt(ii)-48);var il=id[7]==='X'?10:(id.charCodeAt(7)-48);iss+=il;if(iss%11!==0)${K.fail("isISSN")};}}`}})}const t=/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;const isJWT=X("isJWT",(j)=>t.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(t)}].test(${j})) ${Z.fail("isJWT")};`});const a=/^[-+]?([1-8]?\d(?:\.\d+)?|90(?:\.0+)?),\s*[-+]?(180(?:\.0+)?|1[0-7]\d(?:\.\d+)?|\d{1,2}(?:\.\d+)?)$/;function isLatLong(){return X("isLatLong",(j)=>a.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(a)}].test(${j})) ${Z.fail("isLatLong")};`})}const e=/^[a-zA-Z]{2,3}(?:-[a-zA-Z]{4})?(?:-(?:[a-zA-Z]{2}|\d{3}))?(?:-[a-zA-Z\d]{5,8})*$/;const isLocale=X("isLocale",(j)=>e.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(e)}].test(${j})) ${Z.fail("isLocale")};`});const jj=/^data:([a-zA-Z0-9!#$&\-^_]+\/[a-zA-Z0-9!#$&\-^_]+)(?:;[a-zA-Z0-9-]+=[a-zA-Z0-9-]+)*(?:;base64)?,[\s\S]*$/;const isDataURI=X("isDataURI",(j)=>jj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(jj)}].test(${j})) ${Z.fail("isDataURI")};`});function isFQDN(j){const Z=j?.require_tld!==!1,$=j?.allow_underscores??!1,z=j?.allow_trailing_dot??!1,Q=$?/^[a-zA-Z0-9_-]+$/:/^[a-zA-Z0-9-]+$/,K=(Y)=>{if(typeof Y!=="string")return!1;let J=Y;if(z&&J.endsWith("."))J=J.slice(0,-1);if(J.length===0)return!1;const w=J.split(".");if(Z&&w.length<2)return!1;if(Z){const b=w[w.length-1];if(!b||b.length<2||!/^[a-zA-Z]{2,}$/.test(b))return!1}return w.every((b)=>{if(b.length===0||b.length>63)return!1;if(!Q.test(b))return!1;if(!$&&(b.startsWith("-")||b.endsWith("-")))return!1;return!0})};return W({name:"isFQDN",requiresType:f.String,constraints:{require_tld:j?.require_tld,allow_underscores:j?.allow_underscores,allow_trailing_dot:j?.allow_trailing_dot},validate:K,emit:(Y,J)=>{const w=J.addRegex(Q),b=Z?J.addRegex(/^[a-zA-Z]{2,}$/):-1,G=`var fqOk=true;for(var fi=0;fi<fp.length;fi++){var p=fp[fi];${`if(p.length===0||p.length>63){fqOk=false;break;}if(!re[${w}].test(p)){fqOk=false;break;}`+($?"":"if(p[0]==='-'||p[p.length-1]==='-'){fqOk=false;break;}")}}if(!fqOk)${J.fail("isFQDN")};`;let F=`{var fq=${Y};`;if(z)F+="if(fq.endsWith('.'))fq=fq.slice(0,-1);";F+=`if(fq.length===0)${J.fail("isFQDN")};`;F+="else{var fp=fq.split('.');";if(Z){F+=`if(fp.length<2)${J.fail("isFQDN")};`;F+="else{var tld=fp[fp.length-1];";F+=`if(!tld||tld.length<2||!re[${b}].test(tld))${J.fail("isFQDN")};`;F+=`else{${G}}`;F+="}"}else F+=G;F+="}";F+="}";return F}})}const Zj=/^(?:6553[0-5]|655[0-2]\d|65[0-4]\d{2}|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{1,3}|\d)$/;const isPort=X("isPort",(j)=>Zj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Zj)}].test(${j})) ${Z.fail("isPort")};`});function Sj(j){if(!/^\d{8}$/.test(j)&&!/^\d{13}$/.test(j))return!1;const Z=j.length;let $=0;for(let Q=0;Q<Z-1;Q++){const K=j.charCodeAt(Q)-48;$+=K*(Z===8?Q%2===0?3:1:Q%2===0?1:3)}return(10-$%10)%10===j.charCodeAt(Z-1)-48}const isEAN=X("isEAN",Sj,(j,Z)=>{const $=Z.addRegex(/^\d{8}$/),z=Z.addRegex(/^\d{13}$/);return`{var ev=${j};if(!re[${$}].test(ev)&&!re[${z}].test(ev)){${Z.fail("isEAN")}}else{var el=ev.length,es=0;for(var ei=0;ei<el-1;ei++){var ed=ev.charCodeAt(ei)-48;es+=ed*(el===8?(ei%2===0?3:1):(ei%2===0?1:3));}var ec=(10-(es%10))%10;if(ec!==(ev.charCodeAt(el-1)-48))${Z.fail("isEAN")};}}`});const $j=new Set(["AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","ZA","ZM","ZW"]);const isISO31661Alpha2=W({name:"isISO31661Alpha2",requiresType:f.String,constraints:{},validate:(j)=>typeof j==="string"&&$j.has(j.toUpperCase()),emit:(j,Z)=>{return`if (!refs[${Z.addRef($j)}].has(${j}.toUpperCase())) ${Z.fail("isISO31661Alpha2")};`}});const zj=new Set(["ABW","AFG","AGO","AIA","ALA","ALB","AND","ANT","ARE","ARG","ARM","ASM","ATA","ATF","ATG","AUS","AUT","AZE","BDI","BEL","BEN","BES","BFA","BGD","BGR","BHR","BHS","BIH","BLM","BLR","BLZ","BMU","BOL","BRA","BRB","BRN","BTN","BVT","BWA","CAF","CAN","CCK","CHE","CHL","CHN","CIV","CMR","COD","COG","COK","COL","COM","CPV","CRI","CUB","CUW","CXR","CYM","CYP","CZE","DEU","DJI","DMA","DNK","DOM","DZA","ECU","EGY","ERI","ESH","ESP","EST","ETH","FIN","FJI","FLK","FRA","FRO","FSM","GAB","GBR","GEO","GGY","GHA","GIB","GIN","GLP","GMB","GNB","GNQ","GRC","GRD","GRL","GTM","GUF","GUM","GUY","HKG","HMD","HND","HRV","HTI","HUN","IDN","IMN","IND","IOT","IRL","IRN","IRQ","ISL","ISR","ITA","JAM","JEY","JOR","JPN","KAZ","KEN","KGZ","KHM","KIR","KNA","KOR","KWT","LAO","LBN","LBR","LBY","LCA","LIE","LKA","LSO","LTU","LUX","LVA","MAC","MAF","MAR","MCO","MDA","MDG","MDV","MEX","MHL","MKD","MLI","MLT","MMR","MNE","MNG","MNP","MOZ","MRT","MSR","MTQ","MUS","MWI","MYS","MYT","NAM","NCL","NER","NFK","NGA","NIC","NIU","NLD","NOR","NPL","NRU","NZL","OMN","PAK","PAN","PCN","PER","PHL","PLW","PNG","POL","PRI","PRK","PRT","PRY","PSE","PYF","QAT","REU","ROU","RUS","RWA","SAU","SDN","SEN","SGP","SGS","SHN","SJM","SLB","SLE","SLV","SMR","SOM","SPM","SRB","SSD","STP","SUR","SVK","SVN","SWE","SWZ","SXM","SYC","SYR","TCA","TCD","TGO","THA","TJK","TKL","TKM","TLS","TON","TTO","TUN","TUR","TUV","TWN","TZA","UGA","UKR","UMI","URY","USA","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT","WLF","WSM","YEM","ZAF","ZMB","ZWE"]);const isISO31661Alpha3=W({name:"isISO31661Alpha3",requiresType:f.String,constraints:{},validate:(j)=>typeof j==="string"&&zj.has(j.toUpperCase()),emit:(j,Z)=>{return`if (!refs[${Z.addRef(zj)}].has(${j}.toUpperCase())) ${Z.fail("isISO31661Alpha3")};`}});const Qj=/^[A-Z]{6}[A-Z0-9]{2}(?:[A-Z0-9]{3})?$/i;const isBIC=X("isBIC",(j)=>Qj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Qj)}].test(${j})) ${Z.fail("isBIC")};`});const Jj=/^[a-zA-Z0-9_-]{20}$/;const isFirebasePushId=X("isFirebasePushId",(j)=>Jj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Jj)}].test(${j})) ${Z.fail("isFirebasePushId")};`});const Kj=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;const isSemVer=X("isSemVer",(j)=>Kj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Kj)}].test(${j})) ${Z.fail("isSemVer")};`});const Xj=/^[0-9a-fA-F]{24}$/;const isMongoId=X("isMongoId",(j)=>Xj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Xj)}].test(${j})) ${Z.fail("isMongoId")};`});const dj=(j)=>{if(typeof j!=="string")return!1;try{JSON.parse(j);return!0}catch{return!1}};const isJSON=W({name:"isJSON",requiresType:f.String,constraints:{},validate:dj,emit:(j,Z)=>`try { JSON.parse(${j}); } catch { ${Z.fail("isJSON")}; }`});const fj=/^[A-Z2-7]+=*$/i;function isBase32(){return X("isBase32",(j)=>j.length%8===0&&fj.test(j),(j,Z)=>{const $=Z.addRegex(fj);return`if (${j}.length % 8 !== 0 || !re[${$}].test(${j})) ${Z.fail("isBase32")};`})}const Yj=/^[1-9A-HJ-NP-Za-km-z]+$/;const isBase58=X("isBase58",(j)=>Yj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Yj)}].test(${j})) ${Z.fail("isBase58")};`});const pj=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/;const _j=/^[A-Za-z0-9_-]+={0,2}$/;function isBase64(j){const Z=j?.urlSafe?_j:pj;return X("isBase64",($)=>Z.test($),($,z)=>{return`if (!re[${z.addRegex(Z)}].test(${$})) ${z.fail("isBase64")};`},f.String,{urlSafe:j?.urlSafe})}const yj=/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;function Ej(j){if(!yj.test(j))return!1;const Z=Number(j.slice(0,4)),$=Number(j.slice(5,7)),z=Number(j.slice(8,10)),Q=new Date(Z,$,0).getDate();return z>=1&&z<=Q}function isDateString(){return X("isDateString",Ej,(j,Z)=>{return`if (!re[${Z.addRegex(yj)}].test(${j})) ${Z.fail("isDateString")};
6
+ else { var y=Number(${j}.slice(0,4)),m=Number(${j}.slice(5,7)),d=Number(${j}.slice(8,10));var md=new Date(y,m,0).getDate(); if(d<1||d>md)${Z.fail("isDateString")}; }`})}const Wj=/^(application|audio|font|image|message|model|multipart|text|video)\/[a-zA-Z0-9][a-zA-Z0-9!#$&\-^_.+]*(?:;.+)?$/;const isMimeType=X("isMimeType",(j)=>Wj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Wj)}].test(${j})) ${Z.fail("isMimeType")};`});const wj=/^(?:[-+]?\$?|\$[-+]?)(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d{1,2})?$/;function isCurrency(){return X("isCurrency",(j)=>wj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(wj)}].test(${j})) ${Z.fail("isCurrency")};`})}const bj=/^magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}(?:&[a-z][a-z0-9.]*=[^&\s]*)*$/i;const isMagnetURI=X("isMagnetURI",(j)=>bj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(bj)}].test(${j})) ${Z.fail("isMagnetURI")};`});function oj(j){const Z=j.replace(/[\s-]/g,"");if(Z.length===0||!/^\d+$/.test(Z))return!1;let $=0,z=!1;for(let Q=Z.length-1;Q>=0;Q--){let K=Z.charCodeAt(Q)-48;if(z){K*=2;if(K>9)K-=9}$+=K;z=!z}return $%10===0}const isCreditCard=W({name:"isCreditCard",requiresType:f.String,constraints:{},validate:(j)=>typeof j==="string"&&oj(j),emit:(j,Z)=>`{
7
+ var cs=${j}.replace(/[\\s-]/g,'');
8
+ if(cs.length===0||!/^\\d+$/.test(cs)){${Z.fail("isCreditCard")}}
1432
9
  else{var sum=0,alt=false;
1433
10
  for(var ci=cs.length-1;ci>=0;ci--){var cn=cs.charCodeAt(ci)-48;if(alt){cn*=2;if(cn>9)cn-=9;}sum+=cn;alt=!alt;}
1434
- if(sum%10!==0)${ctx.fail('isCreditCard')};}
1435
- }`,
1436
- });
1437
- const IBAN_COUNTRY_LENGTH = {
1438
- AD: 24,
1439
- AE: 23,
1440
- AL: 28,
1441
- AT: 20,
1442
- AZ: 28,
1443
- BA: 20,
1444
- BE: 16,
1445
- BG: 22,
1446
- BH: 22,
1447
- BR: 29,
1448
- CH: 21,
1449
- CR: 22,
1450
- CY: 28,
1451
- CZ: 24,
1452
- DE: 22,
1453
- DK: 18,
1454
- DO: 28,
1455
- EE: 20,
1456
- ES: 24,
1457
- FI: 18,
1458
- FO: 18,
1459
- FR: 27,
1460
- GB: 22,
1461
- GE: 22,
1462
- GI: 23,
1463
- GL: 18,
1464
- GR: 27,
1465
- GT: 28,
1466
- HR: 21,
1467
- HU: 28,
1468
- IE: 22,
1469
- IL: 23,
1470
- IS: 26,
1471
- IT: 27,
1472
- JO: 30,
1473
- KW: 30,
1474
- KZ: 20,
1475
- LB: 28,
1476
- LC: 32,
1477
- LI: 21,
1478
- LT: 20,
1479
- LU: 20,
1480
- LV: 21,
1481
- MC: 27,
1482
- MD: 24,
1483
- ME: 22,
1484
- MK: 19,
1485
- MR: 27,
1486
- MT: 31,
1487
- MU: 30,
1488
- NL: 18,
1489
- NO: 15,
1490
- PK: 24,
1491
- PL: 28,
1492
- PS: 29,
1493
- PT: 25,
1494
- QA: 29,
1495
- RO: 24,
1496
- RS: 22,
1497
- SA: 24,
1498
- SC: 31,
1499
- SE: 24,
1500
- SI: 19,
1501
- SK: 24,
1502
- SM: 27,
1503
- ST: 25,
1504
- SV: 28,
1505
- TL: 23,
1506
- TN: 24,
1507
- TR: 26,
1508
- UA: 29,
1509
- VA: 22,
1510
- VG: 24,
1511
- XK: 20,
1512
- };
1513
- function validateIBAN(value, options) {
1514
- let s = options?.allowSpaces ? value.replace(/\s/g, '') : value;
1515
- s = s.toUpperCase();
1516
- if (!/^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(s)) {
1517
- return false;
1518
- }
1519
- const country = s.slice(0, 2);
1520
- const expectedLength = IBAN_COUNTRY_LENGTH[country];
1521
- if (expectedLength !== undefined && s.length !== expectedLength) {
1522
- return false;
1523
- }
1524
- // Rearrange: move first 4 chars to end
1525
- const rearranged = s.slice(4) + s.slice(0, 4);
1526
- // Walk char-by-char accumulating mod 97 — no .replace/closure, no String() coercion,
1527
- // no parseInt() allocations.
1528
- let remainder = 0;
1529
- for (let i = 0; i < rearranged.length; i++) {
1530
- const code = rearranged.charCodeAt(i);
1531
- if (code <= 57) {
1532
- // digit
1533
- remainder = (remainder * 10 + (code - 48)) % 97;
1534
- }
1535
- else {
1536
- // letter A-Z → two digits (value = code - 55)
1537
- const value = code - 55;
1538
- remainder = (remainder * 100 + value) % 97;
1539
- }
1540
- }
1541
- return remainder === 1;
1542
- }
1543
- function isIBAN(options) {
1544
- const allowSpaces = options?.allowSpaces ?? false;
1545
- const validateIban = (value) => typeof value === 'string' && validateIBAN(value, options);
1546
- return makeRule({
1547
- name: 'isIBAN',
1548
- requiresType: 'string',
1549
- constraints: { allowSpaces: options?.allowSpaces },
1550
- validate: validateIban,
1551
- emit: (varName, ctx) => {
1552
- const baseRi = ctx.addRegex(/^[A-Z]{2}\d{2}[A-Z0-9]+$/);
1553
- const tableIdx = ctx.addRef(IBAN_COUNTRY_LENGTH);
1554
- let code = '{';
1555
- code += `var ib=${allowSpaces ? `${varName}.replace(/\\s/g,'')` : varName}.toUpperCase();`;
1556
- code += `if(!re[${baseRi}].test(ib)){${ctx.fail('isIBAN')}}`;
1557
- code += `else{var ic=ib.slice(0,2),il=refs[${tableIdx}][ic];`;
1558
- code += `if(il!==undefined&&ib.length!==il){${ctx.fail('isIBAN')}}`;
1559
- code += `else{var ir=ib.slice(4)+ib.slice(0,4);`;
1560
- // Walk char-by-char for mod 97 — no .replace closure, no parseInt allocation
1561
- code += `var im=0;for(var ii=0;ii<ir.length;ii++){var cc=ir.charCodeAt(ii);`;
1562
- code += `if(cc<=57){im=(im*10+(cc-48))%97;}else{im=(im*100+(cc-55))%97;}}`;
1563
- code += `if(im!==1)${ctx.fail('isIBAN')};}}}`;
1564
- return code;
1565
- },
1566
- });
1567
- }
1568
- // ByteLength — counts UTF-8 bytes via Buffer.byteLength
1569
- function isByteLength(min, max) {
1570
- const validateByteLength = (value) => {
1571
- if (typeof value !== 'string') {
1572
- return false;
1573
- }
1574
- const byteLen = Buffer.byteLength(value, 'utf8');
1575
- if (byteLen < min) {
1576
- return false;
1577
- }
1578
- if (max !== undefined && byteLen > max) {
1579
- return false;
1580
- }
1581
- return true;
1582
- };
1583
- return makeRule({
1584
- name: 'isByteLength',
1585
- requiresType: 'string',
1586
- constraints: { min, max },
1587
- validate: validateByteLength,
1588
- emit: (varName, ctx) => {
1589
- let code = `{var bl=Buffer.byteLength(${varName},'utf8');`;
1590
- code += `if(bl<${min})${ctx.fail('isByteLength')};`;
1591
- if (max !== undefined) {
1592
- code += `else if(bl>${max})${ctx.fail('isByteLength')};`;
1593
- }
1594
- code += '}';
1595
- return code;
1596
- },
1597
- });
1598
- }
1599
- // ─────────────────────────────────────────────────────────────────────────────
1600
- // Group E: New Validators
1601
- // ─────────────────────────────────────────────────────────────────────────────
1602
- // isHash — per-algorithm hex regex (§4.8 B: regex inline)
1603
- const HASH_REGEXES = {
1604
- md5: /^[a-f0-9]{32}$/i,
1605
- md4: /^[a-f0-9]{32}$/i,
1606
- md2: /^[a-f0-9]{32}$/i,
1607
- sha1: /^[a-f0-9]{40}$/i,
1608
- sha256: /^[a-f0-9]{64}$/i,
1609
- sha384: /^[a-f0-9]{96}$/i,
1610
- sha512: /^[a-f0-9]{128}$/i,
1611
- ripemd128: /^[a-f0-9]{32}$/i,
1612
- ripemd160: /^[a-f0-9]{40}$/i,
1613
- 'tiger128,3': /^[a-f0-9]{32}$/i,
1614
- 'tiger128,4': /^[a-f0-9]{32}$/i,
1615
- 'tiger160,3': /^[a-f0-9]{40}$/i,
1616
- 'tiger160,4': /^[a-f0-9]{40}$/i,
1617
- 'tiger192,3': /^[a-f0-9]{48}$/i,
1618
- 'tiger192,4': /^[a-f0-9]{48}$/i,
1619
- crc32: /^[a-f0-9]{8}$/i,
1620
- crc32b: /^[a-f0-9]{8}$/i,
1621
- };
1622
- function isHash(algorithm) {
1623
- const re = HASH_REGEXES[algorithm];
1624
- return makeRule({
1625
- name: 'isHash',
1626
- requiresType: 'string',
1627
- constraints: { algorithm },
1628
- validate: value => typeof value === 'string' && !!re && re.test(value),
1629
- emit: (varName, ctx) => {
1630
- if (!re) {
1631
- return ctx.fail('isHash') + ';';
1632
- }
1633
- const i = ctx.addRegex(re);
1634
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isHash')};`;
1635
- },
1636
- });
1637
- }
1638
- // isRFC3339 — RFC 3339 datetime (§4.8 B)
1639
- const RFC3339_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/i;
1640
- const isRFC3339 = makeStringRule('isRFC3339', v => RFC3339_RE.test(v), (varName, ctx) => {
1641
- const i = ctx.addRegex(RFC3339_RE);
1642
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isRFC3339')};`;
1643
- });
1644
- // isMilitaryTime — HH:MM 24-hour format (§4.8 B)
1645
- const MILITARY_TIME_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
1646
- const isMilitaryTime = makeStringRule('isMilitaryTime', v => MILITARY_TIME_RE.test(v), (varName, ctx) => {
1647
- const i = ctx.addRegex(MILITARY_TIME_RE);
1648
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isMilitaryTime')};`;
1649
- });
1650
- // isLatitude — string or number, -90 to 90 (requiresType none)
1651
- function checkLatitude(value) {
1652
- if (typeof value === 'number') {
1653
- return value >= -90 && value <= 90;
1654
- }
1655
- if (typeof value === 'string') {
1656
- const n = parseFloat(value);
1657
- if (isNaN(n)) {
1658
- return false;
1659
- }
1660
- // parseFloat('90abc') = 90 — strict regex check rejects trailing garbage
1661
- if (!/^-?\d+(\.\d+)?$/.test(value)) {
1662
- return false;
1663
- }
1664
- return n >= -90 && n <= 90;
1665
- }
1666
- return false;
1667
- }
1668
- const isLatitude = makeRule({
1669
- name: 'isLatitude',
1670
- constraints: {},
1671
- validate: checkLatitude,
1672
- emit: (varName, ctx) => {
1673
- const ri = ctx.addRegex(/^-?\d+(\.\d+)?$/);
1674
- return (`if(typeof ${varName}==='number'){if(${varName}<-90||${varName}>90)${ctx.fail('isLatitude')};}` +
1675
- `else if(typeof ${varName}==='string'){` +
1676
- // Regex catches non-numeric strings; if it matches, parseFloat is guaranteed valid (no isNaN check needed)
1677
- `if(!re[${ri}].test(${varName})){${ctx.fail('isLatitude')}}` +
1678
- `else{var lt=parseFloat(${varName});if(lt<-90||lt>90)${ctx.fail('isLatitude')};}}` +
1679
- `else{${ctx.fail('isLatitude')};}`);
1680
- },
1681
- });
1682
- // isLongitude — string or number, -180 to 180 (requiresType none)
1683
- function checkLongitude(value) {
1684
- if (typeof value === 'number') {
1685
- return value >= -180 && value <= 180;
1686
- }
1687
- if (typeof value === 'string') {
1688
- const n = parseFloat(value);
1689
- if (isNaN(n)) {
1690
- return false;
1691
- }
1692
- if (!/^-?\d+(\.\d+)?$/.test(value)) {
1693
- return false;
1694
- }
1695
- return n >= -180 && n <= 180;
1696
- }
1697
- return false;
1698
- }
1699
- const isLongitude = makeRule({
1700
- name: 'isLongitude',
1701
- constraints: {},
1702
- validate: checkLongitude,
1703
- emit: (varName, ctx) => {
1704
- const ri = ctx.addRegex(/^-?\d+(\.\d+)?$/);
1705
- return (`if(typeof ${varName}==='number'){if(${varName}<-180||${varName}>180)${ctx.fail('isLongitude')};}` +
1706
- `else if(typeof ${varName}==='string'){` +
1707
- `if(!re[${ri}].test(${varName})){${ctx.fail('isLongitude')}}` +
1708
- `else{var ln=parseFloat(${varName});if(ln<-180||ln>180)${ctx.fail('isLongitude')};}}` +
1709
- `else{${ctx.fail('isLongitude')};}`);
1710
- },
1711
- });
1712
- // isEthereumAddress — 0x + 40 hex chars (§4.8 B)
1713
- const ETH_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
1714
- const isEthereumAddress = makeStringRule('isEthereumAddress', v => ETH_ADDRESS_RE.test(v), (varName, ctx) => {
1715
- const i = ctx.addRegex(ETH_ADDRESS_RE);
1716
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isEthereumAddress')};`;
1717
- });
1718
- // isBtcAddress — P2PKH (1...), P2SH (3...), bech32 (bc1...) (§4.8 B)
1719
- const BTC_P2PKH_RE = /^1[a-km-zA-HJ-NP-Z1-9]{25,34}$/;
1720
- const BTC_P2SH_RE = /^3[a-km-zA-HJ-NP-Z1-9]{25,34}$/;
1721
- const BTC_BECH32_RE = /^(bc1)[a-z0-9]{6,87}$/;
1722
- const isBtcAddress = makeStringRule('isBtcAddress', v => BTC_P2PKH_RE.test(v) || BTC_P2SH_RE.test(v) || BTC_BECH32_RE.test(v), (varName, ctx) => {
1723
- const i1 = ctx.addRegex(BTC_P2PKH_RE);
1724
- const i2 = ctx.addRegex(BTC_P2SH_RE);
1725
- const i3 = ctx.addRegex(BTC_BECH32_RE);
1726
- return `if (!re[${i1}].test(${varName}) && !re[${i2}].test(${varName}) && !re[${i3}].test(${varName})) ${ctx.fail('isBtcAddress')};`;
1727
- });
1728
- // isISO4217CurrencyCode — ISO 4217 currency code set (§4.8 C: ref-based)
1729
- const ISO4217_CODES = new Set([
1730
- 'AED',
1731
- 'AFN',
1732
- 'ALL',
1733
- 'AMD',
1734
- 'ANG',
1735
- 'AOA',
1736
- 'ARS',
1737
- 'AUD',
1738
- 'AWG',
1739
- 'AZN',
1740
- 'BAM',
1741
- 'BBD',
1742
- 'BDT',
1743
- 'BGN',
1744
- 'BHD',
1745
- 'BIF',
1746
- 'BMD',
1747
- 'BND',
1748
- 'BOB',
1749
- 'BOV',
1750
- 'BRL',
1751
- 'BSD',
1752
- 'BTN',
1753
- 'BWP',
1754
- 'BYN',
1755
- 'BZD',
1756
- 'CAD',
1757
- 'CDF',
1758
- 'CHE',
1759
- 'CHF',
1760
- 'CHW',
1761
- 'CLF',
1762
- 'CLP',
1763
- 'CNY',
1764
- 'COP',
1765
- 'COU',
1766
- 'CRC',
1767
- 'CUC',
1768
- 'CUP',
1769
- 'CVE',
1770
- 'CZK',
1771
- 'DJF',
1772
- 'DKK',
1773
- 'DOP',
1774
- 'DZD',
1775
- 'EGP',
1776
- 'ERN',
1777
- 'ETB',
1778
- 'EUR',
1779
- 'FJD',
1780
- 'FKP',
1781
- 'GBP',
1782
- 'GEL',
1783
- 'GHS',
1784
- 'GIP',
1785
- 'GMD',
1786
- 'GNF',
1787
- 'GTQ',
1788
- 'GYD',
1789
- 'HKD',
1790
- 'HNL',
1791
- 'HRK',
1792
- 'HTG',
1793
- 'HUF',
1794
- 'IDR',
1795
- 'ILS',
1796
- 'INR',
1797
- 'IQD',
1798
- 'IRR',
1799
- 'ISK',
1800
- 'JMD',
1801
- 'JOD',
1802
- 'JPY',
1803
- 'KES',
1804
- 'KGS',
1805
- 'KHR',
1806
- 'KMF',
1807
- 'KPW',
1808
- 'KRW',
1809
- 'KWD',
1810
- 'KYD',
1811
- 'KZT',
1812
- 'LAK',
1813
- 'LBP',
1814
- 'LKR',
1815
- 'LRD',
1816
- 'LSL',
1817
- 'LYD',
1818
- 'MAD',
1819
- 'MDL',
1820
- 'MGA',
1821
- 'MKD',
1822
- 'MMK',
1823
- 'MNT',
1824
- 'MOP',
1825
- 'MRU',
1826
- 'MUR',
1827
- 'MVR',
1828
- 'MWK',
1829
- 'MXN',
1830
- 'MXV',
1831
- 'MYR',
1832
- 'MZN',
1833
- 'NAD',
1834
- 'NGN',
1835
- 'NIO',
1836
- 'NOK',
1837
- 'NPR',
1838
- 'NZD',
1839
- 'OMR',
1840
- 'PAB',
1841
- 'PEN',
1842
- 'PGK',
1843
- 'PHP',
1844
- 'PKR',
1845
- 'PLN',
1846
- 'PYG',
1847
- 'QAR',
1848
- 'RON',
1849
- 'RSD',
1850
- 'RUB',
1851
- 'RWF',
1852
- 'SAR',
1853
- 'SBD',
1854
- 'SCR',
1855
- 'SDG',
1856
- 'SEK',
1857
- 'SGD',
1858
- 'SHP',
1859
- 'SLE',
1860
- 'SLL',
1861
- 'SOS',
1862
- 'SRD',
1863
- 'SSP',
1864
- 'STN',
1865
- 'SVC',
1866
- 'SYP',
1867
- 'SZL',
1868
- 'THB',
1869
- 'TJS',
1870
- 'TMT',
1871
- 'TND',
1872
- 'TOP',
1873
- 'TRY',
1874
- 'TTD',
1875
- 'TWD',
1876
- 'TZS',
1877
- 'UAH',
1878
- 'UGX',
1879
- 'USD',
1880
- 'USN',
1881
- 'UYI',
1882
- 'UYU',
1883
- 'UYW',
1884
- 'UZS',
1885
- 'VED',
1886
- 'VES',
1887
- 'VND',
1888
- 'VUV',
1889
- 'WST',
1890
- 'XAF',
1891
- 'XAG',
1892
- 'XAU',
1893
- 'XBA',
1894
- 'XBB',
1895
- 'XBC',
1896
- 'XBD',
1897
- 'XCD',
1898
- 'XDR',
1899
- 'XOF',
1900
- 'XPD',
1901
- 'XPF',
1902
- 'XPT',
1903
- 'XSU',
1904
- 'XTS',
1905
- 'XUA',
1906
- 'YER',
1907
- 'ZAR',
1908
- 'ZMW',
1909
- 'ZWL',
1910
- ]);
1911
- const isISO4217CurrencyCode = makeStringRule('isISO4217CurrencyCode', v => ISO4217_CODES.has(v), (varName, ctx) => {
1912
- const i = ctx.addRef(ISO4217_CODES);
1913
- return `if (!refs[${i}].has(${varName})) ${ctx.fail('isISO4217CurrencyCode')};`;
1914
- });
1915
- // isPhoneNumber — E.164 international phone number (§4.8 B)
1916
- const PHONE_E164_RE = /^\+[1-9]\d{6,14}$/;
1917
- const isPhoneNumber = makeStringRule('isPhoneNumber', v => PHONE_E164_RE.test(v), (varName, ctx) => {
1918
- const i = ctx.addRegex(PHONE_E164_RE);
1919
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isPhoneNumber')};`;
1920
- });
1921
- function isStrongPassword(options) {
1922
- const minLength = options?.minLength ?? 8;
1923
- const minLower = options?.minLowercase ?? 1;
1924
- const minUpper = options?.minUppercase ?? 1;
1925
- const minNums = options?.minNumbers ?? 1;
1926
- const minSymbols = options?.minSymbols ?? 1;
1927
- // Single-pass character classification — counts all categories in one scan.
1928
- // Replaces 4× v.match(/.../g) which allocates 4 result arrays per call.
1929
- const validate = (v) => {
1930
- if (v.length < minLength) {
1931
- return false;
1932
- }
1933
- let lower = 0;
1934
- let upper = 0;
1935
- let nums = 0;
1936
- let symbols = 0;
1937
- for (let i = 0; i < v.length; i++) {
1938
- const c = v.charCodeAt(i);
1939
- if (c >= 97 && c <= 122) {
1940
- lower++;
1941
- }
1942
- else if (c >= 65 && c <= 90) {
1943
- upper++;
1944
- }
1945
- else if (c >= 48 && c <= 57) {
1946
- nums++;
1947
- }
1948
- else {
1949
- symbols++;
1950
- }
1951
- }
1952
- return lower >= minLower && upper >= minUpper && nums >= minNums && symbols >= minSymbols;
1953
- };
1954
- return makeRule({
1955
- name: 'isStrongPassword',
1956
- requiresType: 'string',
1957
- constraints: {},
1958
- validate: value => typeof value === 'string' && validate(value),
1959
- emit: (varName, ctx) => {
1960
- // Inline single-pass scan in the JIT executor — no regex match[] allocations
1961
- const failExpr = ctx.fail('isStrongPassword');
1962
- const checks = [];
1963
- if (minLower > 0) {
1964
- checks.push(`spLo<${minLower}`);
1965
- }
1966
- if (minUpper > 0) {
1967
- checks.push(`spUp<${minUpper}`);
1968
- }
1969
- if (minNums > 0) {
1970
- checks.push(`spNum<${minNums}`);
1971
- }
1972
- if (minSymbols > 0) {
1973
- checks.push(`spSym<${minSymbols}`);
1974
- }
1975
- const guard = checks.length === 0 ? '' : `if(${checks.join('||')}){${failExpr}}`;
1976
- return (`if(${varName}.length<${minLength}){${failExpr}}else{` +
1977
- `var spLo=0,spUp=0,spNum=0,spSym=0;` +
1978
- `for(var spI=0;spI<${varName}.length;spI++){var spC=${varName}.charCodeAt(spI);` +
1979
- `if(spC>=97&&spC<=122)spLo++;else if(spC>=65&&spC<=90)spUp++;else if(spC>=48&&spC<=57)spNum++;else spSym++;}` +
1980
- guard +
1981
- `}`);
1982
- },
1983
- });
1984
- }
1985
- // isTaxId — locale-specific tax identifier (§4.8 C: factory)
1986
- const TAX_ID_REGEXES = {
1987
- US: /^\d{2}-\d{7}$/, // EIN format: XX-XXXXXXX
1988
- KR: /^\d{3}-\d{2}-\d{5}$/, // Business Registration Number: XXX-XX-XXXXX
1989
- DE: /^\d{11}$/, // Steuernummer: 11 digits
1990
- FR: /^[0-9]{13}$/, // SIRET: 13 digits
1991
- GB: /^\d{10}$/, // UTR: 10 digits
1992
- IT: /^[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]$/i, // Codice Fiscale
1993
- ES: /^[0-9A-Z]\d{7}[0-9A-Z]$/i, // NIF/NIE/CIF
1994
- AU: /^\d{11}$/, // ABN: 11 digits
1995
- CA: /^\d{9}$/, // BN: 9 digits
1996
- IN: /^[A-Z]{5}\d{4}[A-Z]$/i, // PAN: XXXXX9999X
1997
- };
1998
- function isTaxId(locale) {
1999
- const re = TAX_ID_REGEXES[locale];
2000
- return makeRule({
2001
- name: 'isTaxId',
2002
- requiresType: 'string',
2003
- constraints: { locale },
2004
- validate: value => typeof value === 'string' && !!re && re.test(value),
2005
- emit: (varName, ctx) => {
2006
- if (!re) {
2007
- return ctx.fail('isTaxId') + ';';
2008
- }
2009
- const i = ctx.addRegex(re);
2010
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isTaxId')};`;
2011
- },
2012
- });
2013
- }
2014
- // ─────────────────────────────────────────────────────────────────────────────
2015
- // ULID
2016
- // ─────────────────────────────────────────────────────────────────────────────
2017
- const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
2018
- function isULID() {
2019
- return makeStringRule('isULID', v => ULID_RE.test(v), (varName, ctx) => {
2020
- const i = ctx.addRegex(ULID_RE);
2021
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isULID')};`;
2022
- }, 'string', { format: 'ulid' });
2023
- }
2024
- // ─────────────────────────────────────────────────────────────────────────────
2025
- // CUID2
2026
- // ─────────────────────────────────────────────────────────────────────────────
2027
- // CUID2 spec: length 24-32, lowercase alphanum, starts with a-z.
2028
- const CUID2_RE = /^[a-z][0-9a-z]{23,31}$/;
2029
- function isCUID2() {
2030
- return makeStringRule('isCUID2', v => CUID2_RE.test(v), (varName, ctx) => {
2031
- const i = ctx.addRegex(CUID2_RE);
2032
- return `if (!re[${i}].test(${varName})) ${ctx.fail('isCUID2')};`;
2033
- }, 'string', { format: 'cuid2' });
2034
- }
2035
- export { minLength, maxLength, length, contains, notContains, matches, isLowercase, isUppercase, isAscii, isAlpha, isAlphanumeric, isHttpToken, isOrigin, isCorsOrigin, isBooleanString, isNumberString, isDecimal, isFullWidth, isHalfWidth, isVariableWidth, isMultibyte, isSurrogatePair, isHexadecimal, isOctal, isEmail, isURL, isUUID, isIP, isHexColor, isRgbColor, isHSL, isMACAddress, isISBN, isISIN, isISO8601, isISRC, isISSN, isJWT, isLatLong, isLocale, isDataURI, isFQDN, isPort, isEAN, isISO31661Alpha2, isISO31661Alpha3, isBIC, isFirebasePushId, isSemVer, isMongoId, isJSON, isBase32, isBase58, isBase64, isDateString, isMimeType, isCurrency, isMagnetURI, isCreditCard, isIBAN, isByteLength, isHash, isRFC3339, isMilitaryTime, isLatitude, isLongitude, isEthereumAddress, isBtcAddress, isISO4217CurrencyCode, isPhoneNumber, isStrongPassword, isTaxId, isULID, isCUID2, };
11
+ if(sum%10!==0)${Z.fail("isCreditCard")};}
12
+ }`});const Oj={AD:24,AE:23,AL:28,AT:20,AZ:28,BA:20,BE:16,BG:22,BH:22,BR:29,CH:21,CR:22,CY:28,CZ:24,DE:22,DK:18,DO:28,EE:20,ES:24,FI:18,FO:18,FR:27,GB:22,GE:22,GI:23,GL:18,GR:27,GT:28,HR:21,HU:28,IE:22,IL:23,IS:26,IT:27,JO:30,KW:30,KZ:20,LB:28,LC:32,LI:21,LT:20,LU:20,LV:21,MC:27,MD:24,ME:22,MK:19,MR:27,MT:31,MU:30,NL:18,NO:15,PK:24,PL:28,PS:29,PT:25,QA:29,RO:24,RS:22,SA:24,SC:31,SE:24,SI:19,SK:24,SM:27,ST:25,SV:28,TL:23,TN:24,TR:26,UA:29,VA:22,VG:24,XK:20};function Nj(j,Z){let $=Z?.allowSpaces?j.replace(/\s/g,""):j;$=$.toUpperCase();if(!/^[A-Z]{2}\d{2}[A-Z0-9]+$/.test($))return!1;const z=$.slice(0,2),Q=Oj[z];if(Q!==void 0&&$.length!==Q)return!1;const K=$.slice(4)+$.slice(0,4);let Y=0;for(let J=0;J<K.length;J++){const w=K.charCodeAt(J);if(w<=57)Y=(Y*10+(w-48))%97;else{const b=w-55;Y=(Y*100+b)%97}}return Y===1}function isIBAN(j){const Z=j?.allowSpaces??!1,$=(z)=>typeof z==="string"&&Nj(z,j);return W({name:"isIBAN",requiresType:f.String,constraints:{allowSpaces:j?.allowSpaces},validate:$,emit:(z,Q)=>{const K=Q.addRegex(/^[A-Z]{2}\d{2}[A-Z0-9]+$/),Y=Q.addRef(Oj);let J="{";J+=`var ib=${Z?`${z}.replace(/\\s/g,'')`:z}.toUpperCase();`;J+=`if(!re[${K}].test(ib)){${Q.fail("isIBAN")}}`;J+=`else{var ic=ib.slice(0,2),il=refs[${Y}][ic];`;J+=`if(il!==undefined&&ib.length!==il){${Q.fail("isIBAN")}}`;J+="else{var ir=ib.slice(4)+ib.slice(0,4);";J+="var im=0;for(var ii=0;ii<ir.length;ii++){var cc=ir.charCodeAt(ii);";J+="if(cc<=57){im=(im*10+(cc-48))%97;}else{im=(im*100+(cc-55))%97;}}";J+=`if(im!==1)${Q.fail("isIBAN")};}}}`;return J}})}function isByteLength(j,Z){const $=(z)=>{if(typeof z!=="string")return!1;const Q=Buffer.byteLength(z,"utf8");if(Q<j)return!1;if(Z!==void 0&&Q>Z)return!1;return!0};return W({name:"isByteLength",requiresType:f.String,constraints:{min:j,max:Z},validate:$,emit:(z,Q)=>{let K=`{var bl=Buffer.byteLength(${z},'utf8');`;K+=`if(bl<${j})${Q.fail("isByteLength")};`;if(Z!==void 0)K+=`else if(bl>${Z})${Q.fail("isByteLength")};`;K+="}";return K}})}const Rj={md5:/^[a-f0-9]{32}$/i,md4:/^[a-f0-9]{32}$/i,md2:/^[a-f0-9]{32}$/i,sha1:/^[a-f0-9]{40}$/i,sha256:/^[a-f0-9]{64}$/i,sha384:/^[a-f0-9]{96}$/i,sha512:/^[a-f0-9]{128}$/i,ripemd128:/^[a-f0-9]{32}$/i,ripemd160:/^[a-f0-9]{40}$/i,"tiger128,3":/^[a-f0-9]{32}$/i,"tiger128,4":/^[a-f0-9]{32}$/i,"tiger160,3":/^[a-f0-9]{40}$/i,"tiger160,4":/^[a-f0-9]{40}$/i,"tiger192,3":/^[a-f0-9]{48}$/i,"tiger192,4":/^[a-f0-9]{48}$/i,crc32:/^[a-f0-9]{8}$/i,crc32b:/^[a-f0-9]{8}$/i};function isHash(j){const Z=Rj[j];return W({name:"isHash",requiresType:f.String,constraints:{algorithm:j},validate:($)=>typeof $==="string"&&!!Z&&Z.test($),emit:($,z)=>{if(!Z)return z.fail("isHash")+";";return`if (!re[${z.addRegex(Z)}].test(${$})) ${z.fail("isHash")};`}})}const Fj=/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/i;const isRFC3339=X("isRFC3339",(j)=>Fj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Fj)}].test(${j})) ${Z.fail("isRFC3339")};`});const Vj=/^([01]\d|2[0-3]):[0-5]\d$/;const isMilitaryTime=X("isMilitaryTime",(j)=>Vj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Vj)}].test(${j})) ${Z.fail("isMilitaryTime")};`});function sj(j){if(typeof j==="number")return j>=-90&&j<=90;if(typeof j==="string"){const Z=parseFloat(j);if(isNaN(Z))return!1;if(!/^-?\d+(\.\d+)?$/.test(j))return!1;return Z>=-90&&Z<=90}return!1}const isLatitude=W({name:"isLatitude",constraints:{},validate:sj,emit:(j,Z)=>{const $=Z.addRegex(/^-?\d+(\.\d+)?$/);return`if(typeof ${j}==='number'){if(${j}<-90||${j}>90)${Z.fail("isLatitude")};}else if(typeof ${j}==='string'){if(!re[${$}].test(${j})){${Z.fail("isLatitude")}}else{var lt=parseFloat(${j});if(lt<-90||lt>90)${Z.fail("isLatitude")};}}else{${Z.fail("isLatitude")};}`}});function nj(j){if(typeof j==="number")return j>=-180&&j<=180;if(typeof j==="string"){const Z=parseFloat(j);if(isNaN(Z))return!1;if(!/^-?\d+(\.\d+)?$/.test(j))return!1;return Z>=-180&&Z<=180}return!1}const isLongitude=W({name:"isLongitude",constraints:{},validate:nj,emit:(j,Z)=>{const $=Z.addRegex(/^-?\d+(\.\d+)?$/);return`if(typeof ${j}==='number'){if(${j}<-180||${j}>180)${Z.fail("isLongitude")};}else if(typeof ${j}==='string'){if(!re[${$}].test(${j})){${Z.fail("isLongitude")}}else{var ln=parseFloat(${j});if(ln<-180||ln>180)${Z.fail("isLongitude")};}}else{${Z.fail("isLongitude")};}`}});const Gj=/^0x[0-9a-fA-F]{40}$/;const isEthereumAddress=X("isEthereumAddress",(j)=>Gj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Gj)}].test(${j})) ${Z.fail("isEthereumAddress")};`});const Mj=/^1[a-km-zA-HJ-NP-Z1-9]{25,34}$/;const qj=/^3[a-km-zA-HJ-NP-Z1-9]{25,34}$/;const Dj=/^(bc1)[a-z0-9]{6,87}$/;const isBtcAddress=X("isBtcAddress",(j)=>Mj.test(j)||qj.test(j)||Dj.test(j),(j,Z)=>{const $=Z.addRegex(Mj),z=Z.addRegex(qj),Q=Z.addRegex(Dj);return`if (!re[${$}].test(${j}) && !re[${z}].test(${j}) && !re[${Q}].test(${j})) ${Z.fail("isBtcAddress")};`});const Uj=new Set(["AED","AFN","ALL","AMD","ANG","AOA","ARS","AUD","AWG","AZN","BAM","BBD","BDT","BGN","BHD","BIF","BMD","BND","BOB","BOV","BRL","BSD","BTN","BWP","BYN","BZD","CAD","CDF","CHE","CHF","CHW","CLF","CLP","CNY","COP","COU","CRC","CUC","CUP","CVE","CZK","DJF","DKK","DOP","DZD","EGP","ERN","ETB","EUR","FJD","FKP","GBP","GEL","GHS","GIP","GMD","GNF","GTQ","GYD","HKD","HNL","HRK","HTG","HUF","IDR","ILS","INR","IQD","IRR","ISK","JMD","JOD","JPY","KES","KGS","KHR","KMF","KPW","KRW","KWD","KYD","KZT","LAK","LBP","LKR","LRD","LSL","LYD","MAD","MDL","MGA","MKD","MMK","MNT","MOP","MRU","MUR","MVR","MWK","MXN","MXV","MYR","MZN","NAD","NGN","NIO","NOK","NPR","NZD","OMR","PAB","PEN","PGK","PHP","PKR","PLN","PYG","QAR","RON","RSD","RUB","RWF","SAR","SBD","SCR","SDG","SEK","SGD","SHP","SLE","SLL","SOS","SRD","SSP","STN","SVC","SYP","SZL","THB","TJS","TMT","TND","TOP","TRY","TTD","TWD","TZS","UAH","UGX","USD","USN","UYI","UYU","UYW","UZS","VED","VES","VND","VUV","WST","XAF","XAG","XAU","XBA","XBB","XBC","XBD","XCD","XDR","XOF","XPD","XPF","XPT","XSU","XTS","XUA","YER","ZAR","ZMW","ZWL"]);const isISO4217CurrencyCode=X("isISO4217CurrencyCode",(j)=>Uj.has(j),(j,Z)=>{return`if (!refs[${Z.addRef(Uj)}].has(${j})) ${Z.fail("isISO4217CurrencyCode")};`});const Pj=/^\+[1-9]\d{6,14}$/;const isPhoneNumber=X("isPhoneNumber",(j)=>Pj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Pj)}].test(${j})) ${Z.fail("isPhoneNumber")};`});function isStrongPassword(j){const Z=j?.minLength??8,$=j?.minLowercase??1,z=j?.minUppercase??1,Q=j?.minNumbers??1,K=j?.minSymbols??1,Y=(J)=>{if(J.length<Z)return!1;let w=0,b=0,V=0,G=0;for(let F=0;F<J.length;F++){const M=J.charCodeAt(F);if(M>=97&&M<=122)w++;else if(M>=65&&M<=90)b++;else if(M>=48&&M<=57)V++;else G++}return w>=$&&b>=z&&V>=Q&&G>=K};return W({name:"isStrongPassword",requiresType:f.String,constraints:{},validate:(J)=>typeof J==="string"&&Y(J),emit:(J,w)=>{const b=w.fail("isStrongPassword"),V=[];if($>0)V.push(`spLo<${$}`);if(z>0)V.push(`spUp<${z}`);if(Q>0)V.push(`spNum<${Q}`);if(K>0)V.push(`spSym<${K}`);const G=V.length===0?"":`if(${V.join("||")}){${b}}`;return`if(${J}.length<${Z}){${b}}else{var spLo=0,spUp=0,spNum=0,spSym=0;for(var spI=0;spI<${J}.length;spI++){var spC=${J}.charCodeAt(spI);if(spC>=97&&spC<=122)spLo++;else if(spC>=65&&spC<=90)spUp++;else if(spC>=48&&spC<=57)spNum++;else spSym++;}`+G+"}"}})}const xj={US:/^\d{2}-\d{7}$/,KR:/^\d{3}-\d{2}-\d{5}$/,DE:/^\d{11}$/,FR:/^[0-9]{13}$/,GB:/^\d{10}$/,IT:/^[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]$/i,ES:/^[0-9A-Z]\d{7}[0-9A-Z]$/i,AU:/^\d{11}$/,CA:/^\d{9}$/,IN:/^[A-Z]{5}\d{4}[A-Z]$/i};function isTaxId(j){const Z=xj[j];return W({name:"isTaxId",requiresType:f.String,constraints:{locale:j},validate:($)=>typeof $==="string"&&!!Z&&Z.test($),emit:($,z)=>{if(!Z)return z.fail("isTaxId")+";";return`if (!re[${z.addRegex(Z)}].test(${$})) ${z.fail("isTaxId")};`}})}const Hj=/^[0-9A-HJKMNP-TV-Z]{26}$/;function isULID(){return X("isULID",(j)=>Hj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(Hj)}].test(${j})) ${Z.fail("isULID")};`},f.String,{format:"ulid"})}const hj=/^[a-z][0-9a-z]{23,31}$/;function isCUID2(){return X("isCUID2",(j)=>hj.test(j),(j,Z)=>{return`if (!re[${Z.addRegex(hj)}].test(${j})) ${Z.fail("isCUID2")};`},f.String,{format:"cuid2"})}export{minLength,maxLength,length,contains,notContains,matches,isLowercase,isUppercase,isAscii,isAlpha,isAlphanumeric,isHttpToken,isOrigin,isCorsOrigin,isBooleanString,isNumberString,isDecimal,isFullWidth,isHalfWidth,isVariableWidth,isMultibyte,isSurrogatePair,isHexadecimal,isOctal,isEmail,isURL,isUUID,isIP,isHexColor,isRgbColor,isHSL,isMACAddress,isISBN,isISIN,isISO8601,isISRC,isISSN,isJWT,isLatLong,isLocale,isDataURI,isFQDN,isPort,isEAN,isISO31661Alpha2,isISO31661Alpha3,isBIC,isFirebasePushId,isSemVer,isMongoId,isJSON,isBase32,isBase58,isBase64,isDateString,isMimeType,isCurrency,isMagnetURI,isCreditCard,isIBAN,isByteLength,isHash,isRFC3339,isMilitaryTime,isLatitude,isLongitude,isEthereumAddress,isBtcAddress,isISO4217CurrencyCode,isPhoneNumber,isStrongPassword,isTaxId,isULID,isCUID2};