@regle/rules 1.2.3 → 1.3.0-beta.1

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.
@@ -0,0 +1,1210 @@
1
+ import { InternalRuleType, createRule, unwrapRuleParameters } from "@regle/core";
2
+ import { computed, toValue, unref } from "vue";
3
+
4
+ //#region src/helpers/withMessage.ts
5
+ function withMessage(rule, newMessage) {
6
+ let _type;
7
+ let validator;
8
+ let _active;
9
+ let _params;
10
+ if (typeof rule === "function" && !("_validator" in rule)) {
11
+ _type = InternalRuleType.Inline;
12
+ validator = rule;
13
+ } else ({_type, validator, _active, _params} = rule);
14
+ const newRule = createRule({
15
+ type: _type,
16
+ validator,
17
+ active: _active,
18
+ message: newMessage
19
+ });
20
+ const newParams = [..._params ?? []];
21
+ newRule._params = newParams;
22
+ newRule._message_patched = true;
23
+ if (typeof newRule === "function") {
24
+ if (_params != null) {
25
+ const executedRule = newRule(...newParams);
26
+ executedRule._message_patched = true;
27
+ return executedRule;
28
+ }
29
+ return newRule;
30
+ } else return newRule;
31
+ }
32
+
33
+ //#endregion
34
+ //#region src/helpers/withTooltip.ts
35
+ function withTooltip(rule, newTooltip) {
36
+ let _type;
37
+ let validator;
38
+ let _active;
39
+ let _params;
40
+ let _message;
41
+ if (typeof rule === "function" && !("_validator" in rule)) {
42
+ _type = InternalRuleType.Inline;
43
+ validator = rule;
44
+ } else ({_type, validator, _active, _params, _message} = rule);
45
+ const newRule = createRule({
46
+ type: _type,
47
+ validator,
48
+ active: _active,
49
+ message: _message,
50
+ tooltip: newTooltip
51
+ });
52
+ const newParams = [..._params ?? []];
53
+ newRule._params = newParams;
54
+ newRule._tooltip_patched = true;
55
+ if (typeof newRule === "function") {
56
+ const executedRule = newRule(...newParams);
57
+ newRule._tooltip_patched = true;
58
+ return executedRule;
59
+ } else return newRule;
60
+ }
61
+
62
+ //#endregion
63
+ //#region src/helpers/withAsync.ts
64
+ function withAsync(rule, depsArray) {
65
+ let _type;
66
+ let validator;
67
+ let _params = [];
68
+ let _message = "";
69
+ if (typeof rule === "function") {
70
+ _type = InternalRuleType.Inline;
71
+ validator = async (value, ...params) => {
72
+ return rule(value, ...params);
73
+ };
74
+ _params = [depsArray];
75
+ } else {
76
+ ({_type, _message} = rule);
77
+ _params = _params = rule._params?.concat(depsArray);
78
+ validator = async (...args) => rule.validator(args);
79
+ }
80
+ const newRule = createRule({
81
+ type: InternalRuleType.Async,
82
+ validator,
83
+ message: _message
84
+ });
85
+ newRule._params = newRule._params?.concat(_params);
86
+ return newRule(...depsArray ?? []);
87
+ }
88
+
89
+ //#endregion
90
+ //#region src/helpers/withParams.ts
91
+ function withParams(rule, depsArray) {
92
+ let _type;
93
+ let validator;
94
+ let _params = [];
95
+ let _message = "";
96
+ if (typeof rule === "function") {
97
+ _type = InternalRuleType.Inline;
98
+ validator = (value, ...params) => {
99
+ return rule(value, ...params);
100
+ };
101
+ _params = [depsArray];
102
+ } else {
103
+ ({_type, validator, _message} = rule);
104
+ _params = _params = rule._params?.concat(depsArray);
105
+ }
106
+ const newRule = createRule({
107
+ type: InternalRuleType.Inline,
108
+ validator,
109
+ message: _message
110
+ });
111
+ newRule._params = newRule._params?.concat(_params);
112
+ return newRule(...depsArray);
113
+ }
114
+
115
+ //#endregion
116
+ //#region src/helpers/applyIf.ts
117
+ /**
118
+ * The applyIf operator is similar to requiredIf, but it can be used with any rule. It simplifies conditional rule declarations.
119
+ */
120
+ function applyIf(_condition, rule) {
121
+ let _type;
122
+ let validator;
123
+ let _params = [];
124
+ let _message = "";
125
+ if (typeof rule === "function") {
126
+ _type = InternalRuleType.Inline;
127
+ validator = rule;
128
+ _params = [_condition];
129
+ } else {
130
+ ({_type, validator, _message} = rule);
131
+ _params = rule._params?.concat([_condition]);
132
+ }
133
+ function newValidator(value, ...args) {
134
+ const [condition] = unwrapRuleParameters([_condition]);
135
+ if (condition) return validator(value, ...args);
136
+ return true;
137
+ }
138
+ function newActive(metadata) {
139
+ const [condition] = unwrapRuleParameters([_condition]);
140
+ return condition;
141
+ }
142
+ const newRule = createRule({
143
+ type: _type,
144
+ validator: newValidator,
145
+ active: newActive,
146
+ message: _message
147
+ });
148
+ const newParams = [..._params ?? []];
149
+ newRule._params = newParams;
150
+ if (typeof newRule === "function") {
151
+ const executedRule = newRule(...newParams);
152
+ return executedRule;
153
+ } else return newRule;
154
+ }
155
+
156
+ //#endregion
157
+ //#region ../shared/utils/isFile.ts
158
+ /**
159
+ * Server side friendly way of checking for a File
160
+ */
161
+ function isFile(value) {
162
+ return value?.constructor.name == "File" || value?.constructor.name == "FileList";
163
+ }
164
+
165
+ //#endregion
166
+ //#region ../shared/utils/isEmpty.ts
167
+ /**
168
+ * This is the inverse of isFilled. It will check if the value is in any way empty (including arrays and objects)
169
+ *
170
+ * isEmpty also acts as a type guard.
171
+ *
172
+ * @param value - the target value
173
+ * @param [considerEmptyArrayInvalid=true] - will return false if set to `false`. (default: `true`)
174
+ */
175
+ function isEmpty(value, considerEmptyArrayInvalid = true) {
176
+ if (value === void 0 || value === null) return true;
177
+ if (value instanceof Date) return isNaN(value.getTime());
178
+ else if (isFile(value)) return value.size <= 0;
179
+ else if (Array.isArray(value)) {
180
+ if (considerEmptyArrayInvalid) return value.length === 0;
181
+ return false;
182
+ } else if (typeof value === "object" && value != null) return Object.keys(value).length === 0;
183
+ return !String(value).length;
184
+ }
185
+
186
+ //#endregion
187
+ //#region ../shared/utils/symbol.ts
188
+ const RegleRuleSymbol = Symbol("regle-rule");
189
+
190
+ //#endregion
191
+ //#region ../shared/utils/isDate.ts
192
+ /**
193
+ * This is a useful helper that can check if the provided value is a Date, it is used internally for date rules. This can also check strings.
194
+ */
195
+ function isDate(value) {
196
+ if (isEmpty(value)) return false;
197
+ try {
198
+ let possibleDate = null;
199
+ if (value instanceof Date) possibleDate = value;
200
+ else if (typeof value === "string") {
201
+ const date$1 = new Date(value);
202
+ if (date$1.toString() === "Invalid Date") return false;
203
+ possibleDate = date$1;
204
+ }
205
+ return !!possibleDate;
206
+ } catch (e) {
207
+ return false;
208
+ }
209
+ }
210
+
211
+ //#endregion
212
+ //#region ../shared/utils/toDate.ts
213
+ /**
214
+ * This utility will coerce any string, number or Date value into a Date using the Date constructor.
215
+ */
216
+ function toDate(argument) {
217
+ const argStr = Object.prototype.toString.call(argument);
218
+ if (argument == null) return new Date(NaN);
219
+ else if (argument instanceof Date || typeof argument === "object" && argStr === "[object Date]") return new Date(argument.getTime());
220
+ else if (typeof argument === "number" || argStr === "[object Number]") return new Date(argument);
221
+ else if (typeof argument === "string" || argStr === "[object String]") return new Date(argument);
222
+ else return new Date(NaN);
223
+ }
224
+
225
+ //#endregion
226
+ //#region src/helpers/ruleHelpers/isFilled.ts
227
+ /**
228
+ * This is almost a must have for optional fields. It checks if any value you provided is defined (including arrays and objects). You can base your validator result on this.
229
+ *
230
+ * isFilled also acts as a type guard.
231
+ *
232
+ * @param value - the target value
233
+ * @param [considerEmptyArrayInvalid=true] - will return true if set to `false`. (default: `true`)
234
+ */
235
+ function isFilled(value, considerEmptyArrayInvalid = true) {
236
+ return !isEmpty(typeof value === "string" ? value.trim() : value, considerEmptyArrayInvalid);
237
+ }
238
+
239
+ //#endregion
240
+ //#region src/helpers/ruleHelpers/isNumber.ts
241
+ /**
242
+ * This is a type guard that will check if the passed value is a real Number. This also returns false for NaN, so this is better than typeof value === "number".
243
+ */
244
+ function isNumber(value) {
245
+ if (value == null) return false;
246
+ else if (typeof value !== "number") return false;
247
+ else if (isNaN(value)) return false;
248
+ else return true;
249
+ }
250
+
251
+ //#endregion
252
+ //#region src/helpers/ruleHelpers/matchRegex.ts
253
+ /**
254
+ * This utility can take multiple regular expressions as arguments. It checks the input's validity and tests it against the provided regex patterns.
255
+ */
256
+ function matchRegex(_value, ...expr) {
257
+ if (isEmpty(_value)) return true;
258
+ const value = typeof _value === "number" ? _value.toString() : _value;
259
+ return expr.every((reg) => {
260
+ reg.lastIndex = 0;
261
+ return reg.test(value);
262
+ });
263
+ }
264
+
265
+ //#endregion
266
+ //#region src/helpers/ruleHelpers/getSize.ts
267
+ /**
268
+ * This helper will return the length of any data type you pass. It works with strings, arrays, objects and numbers.
269
+ */
270
+ function getSize(value) {
271
+ const _value = unref(value);
272
+ if (Array.isArray(_value)) return _value.length;
273
+ if (typeof _value === "object") return Object.keys(_value).length;
274
+ if (typeof _value === "number") {
275
+ if (isNaN(_value)) return 0;
276
+ return _value;
277
+ }
278
+ return String(_value).length;
279
+ }
280
+
281
+ //#endregion
282
+ //#region src/helpers/ruleHelpers/toNumber.ts
283
+ /**
284
+ * This utility converts any string (or number) into a number using the Number constructor.
285
+ *
286
+ * @returns ⚠️ Warning, returned value can be NaN
287
+ */
288
+ function toNumber(argument) {
289
+ if (typeof argument === "number") return argument;
290
+ else if (argument != null) {
291
+ if (typeof argument === "string") {
292
+ const isPadded = argument.trim() !== argument;
293
+ if (isPadded) return NaN;
294
+ return +argument;
295
+ }
296
+ return NaN;
297
+ }
298
+ return NaN;
299
+ }
300
+
301
+ //#endregion
302
+ //#region src/helpers/and.ts
303
+ /**
304
+ * The and operator combines multiple rules and validates successfully only if all provided rules are valid.
305
+ */
306
+ function and(...rules) {
307
+ const isAnyRuleAsync = rules.some((rule) => {
308
+ if (typeof rule === "function") return rule.constructor.name === "AsyncFunction";
309
+ else return rule._async;
310
+ });
311
+ const _params = rules.map((rule) => {
312
+ if (typeof rule === "function") return null;
313
+ else {
314
+ const $params = rule._params;
315
+ if (!$params?.length) return [];
316
+ else return $params;
317
+ }
318
+ }).flat().filter((param) => !!param);
319
+ function computeRules(rules$1, value, ...params) {
320
+ const $rules = [];
321
+ let paramIndex = 0;
322
+ for (let rule of rules$1) if (typeof rule === "function") {
323
+ $rules.push(rule(value));
324
+ paramIndex++;
325
+ } else {
326
+ const paramsLength = rule._params?.length ?? 0;
327
+ $rules.push(rule.validator(value, ...params.slice(paramIndex, paramsLength)));
328
+ if (paramsLength) paramIndex += paramsLength;
329
+ }
330
+ return $rules;
331
+ }
332
+ function computeMetadata(results) {
333
+ const isAnyResultMetaData = results?.some((s) => typeof s !== "boolean");
334
+ if (isAnyResultMetaData) return {
335
+ $valid: results.every((result) => {
336
+ if (typeof result === "boolean") return !!result;
337
+ return result.$valid;
338
+ }),
339
+ ...results.reduce((acc, result) => {
340
+ if (typeof result === "boolean") return acc;
341
+ const { $valid,...rest } = result;
342
+ return {
343
+ ...acc,
344
+ ...rest
345
+ };
346
+ }, {})
347
+ };
348
+ else return results.every((result) => !!result);
349
+ }
350
+ let validator;
351
+ if (!!rules.length) validator = isAnyRuleAsync ? async (value, ...params) => {
352
+ const results = await Promise.all(computeRules(rules, value, ...params));
353
+ return computeMetadata(results);
354
+ } : (value, ...params) => {
355
+ const $rules = computeRules(rules, value, ...params);
356
+ return computeMetadata($rules);
357
+ };
358
+ else validator = (value) => {
359
+ return false;
360
+ };
361
+ const newRule = createRule({
362
+ type: "and",
363
+ validator,
364
+ message: "The value does not match all of the provided validators"
365
+ });
366
+ const newParams = [..._params ?? []];
367
+ newRule._params = newParams;
368
+ if (typeof newRule === "function") {
369
+ const executedRule = newRule(...newParams);
370
+ return executedRule;
371
+ } else return newRule;
372
+ }
373
+
374
+ //#endregion
375
+ //#region src/helpers/or.ts
376
+ /**
377
+ * The or operator validates successfully if at least one of the provided rules is valid.
378
+ */
379
+ function or(...rules) {
380
+ const isAnyRuleAsync = rules.some((rule) => {
381
+ if (typeof rule === "function") return rule.constructor.name === "AsyncFunction";
382
+ else return rule._async;
383
+ });
384
+ const _params = rules.map((rule) => {
385
+ if (typeof rule === "function") return null;
386
+ else return rule._params;
387
+ }).flat().filter((param) => !!param);
388
+ function computeRules(rules$1, value, ...params) {
389
+ const $rules = [];
390
+ let paramIndex = 0;
391
+ for (let rule of rules$1) if (typeof rule === "function") {
392
+ $rules.push(rule(value));
393
+ paramIndex++;
394
+ } else {
395
+ const paramsLength = rule._params?.length ?? 0;
396
+ $rules.push(rule.validator(value, ...params.slice(paramIndex, paramsLength)));
397
+ if (paramsLength) paramIndex += paramsLength;
398
+ }
399
+ return $rules;
400
+ }
401
+ function computeMetadata(results) {
402
+ const isAnyResultMetaData = results.some((s) => typeof s !== "boolean");
403
+ if (isAnyResultMetaData) return {
404
+ $valid: results.some((result) => {
405
+ if (typeof result === "boolean") return !!result;
406
+ return result.$valid;
407
+ }),
408
+ ...results.reduce((acc, result) => {
409
+ if (typeof result === "boolean") return acc;
410
+ const { $valid,...rest } = result;
411
+ return {
412
+ ...acc,
413
+ ...rest
414
+ };
415
+ }, {})
416
+ };
417
+ else return results.some((result) => !!result);
418
+ }
419
+ const validator = isAnyRuleAsync ? async (value, ...params) => {
420
+ const results = await Promise.all(computeRules(rules, value, ...params));
421
+ return computeMetadata(results);
422
+ } : (value, ...params) => {
423
+ const $rules = computeRules(rules, value, ...params);
424
+ return computeMetadata($rules);
425
+ };
426
+ const newRule = createRule({
427
+ type: "or",
428
+ validator,
429
+ message: "The value does not match any of the provided validators"
430
+ });
431
+ const newParams = [..._params ?? []];
432
+ newRule._params = newParams;
433
+ if (typeof newRule === "function") {
434
+ const executedRule = newRule(...newParams);
435
+ return executedRule;
436
+ } else return newRule;
437
+ }
438
+
439
+ //#endregion
440
+ //#region src/helpers/not.ts
441
+ /**
442
+ * The not operator passes when the provided rule fails and fails when the rule passes. It can be combined with other rules.
443
+ */
444
+ function not(rule, message) {
445
+ let _type;
446
+ let validator;
447
+ let newValidator;
448
+ let _params;
449
+ let _async;
450
+ if (typeof rule === "function") {
451
+ validator = rule;
452
+ _async = rule.constructor.name === "AsyncFunction";
453
+ } else {
454
+ ({_type, validator, _params} = rule);
455
+ _async = rule._async;
456
+ }
457
+ if (_async) newValidator = async (value, ...params) => {
458
+ if (isFilled(value)) {
459
+ const result = await validator(value, ...params);
460
+ return !result;
461
+ }
462
+ return true;
463
+ };
464
+ else newValidator = (value, ...params) => {
465
+ if (isFilled(value)) return !validator(value, ...params);
466
+ return true;
467
+ };
468
+ const newRule = createRule({
469
+ type: "not",
470
+ validator: newValidator,
471
+ message: message ?? "Error"
472
+ });
473
+ const newParams = [..._params ?? []];
474
+ newRule._params = newParams;
475
+ if (typeof newRule === "function") {
476
+ const executedRule = newRule(...newParams);
477
+ return executedRule;
478
+ } else return newRule;
479
+ }
480
+
481
+ //#endregion
482
+ //#region src/rules/alpha.ts
483
+ const alphaRegex = /^[a-zA-Z]*$/;
484
+ const alphaSymbolRegex = /^[\w.]+$/;
485
+ /**
486
+ * Allows only alphabetic characters.
487
+ *
488
+ * @param [options] - Alpha rules options
489
+ * */
490
+ const alpha = createRule({
491
+ type: "alpha",
492
+ validator(value, options) {
493
+ if (isEmpty(value)) return true;
494
+ if (options?.allowSymbols) return matchRegex(value, alphaSymbolRegex);
495
+ return matchRegex(value, alphaRegex);
496
+ },
497
+ message: "The value is not alphabetical"
498
+ });
499
+
500
+ //#endregion
501
+ //#region src/rules/alphaNum.ts
502
+ const alphaNumRegex = /^[a-zA-Z0-9]*$/;
503
+ const alphaNumSymbolRegex = /^[a-zA-Z0-9_]*$/;
504
+ /**
505
+ * Allows only alphanumeric characters.
506
+ *
507
+ * @param [options] - Alpha rules options
508
+ */
509
+ const alphaNum = createRule({
510
+ type: "alphaNum",
511
+ validator(value, options) {
512
+ if (isEmpty(value)) return true;
513
+ if (options?.allowSymbols) return matchRegex(value, alphaNumSymbolRegex);
514
+ return matchRegex(value, alphaNumRegex);
515
+ },
516
+ message: "The value must be alpha-numeric"
517
+ });
518
+
519
+ //#endregion
520
+ //#region src/rules/between.ts
521
+ /**
522
+ * Checks if a number is in specified bounds. min and max are both inclusive.
523
+ *
524
+ * @param min - the minimum limit
525
+ * @param max - the maximum limit
526
+ */
527
+ const between = createRule({
528
+ type: "between",
529
+ validator: (value, min, max, options) => {
530
+ const { allowEqual = true } = options ?? {};
531
+ if (isFilled(value) && isFilled(min) && isFilled(max)) {
532
+ const tValue = toNumber(value);
533
+ const tMin = toNumber(min);
534
+ const tMax = toNumber(max);
535
+ if (isNumber(tValue) && isNumber(tMin) && isNumber(tMax)) if (allowEqual) return tValue >= tMin && tValue <= tMax;
536
+ else return tValue > tMin && tValue < tMax;
537
+ console.warn(`[between] Value or parameters aren't numbers, got value: ${value}, min: ${min}, max: ${max}`);
538
+ return false;
539
+ }
540
+ return true;
541
+ },
542
+ message: ({ $params: [min, max] }) => {
543
+ return `The value must be between ${min} and ${max}`;
544
+ }
545
+ });
546
+
547
+ //#endregion
548
+ //#region src/rules/boolean.ts
549
+ /**
550
+ * Requires a value to be a native boolean type
551
+ *
552
+ * Mainly used for typing
553
+ */
554
+ const boolean = createRule({
555
+ type: "boolean",
556
+ validator: (value) => {
557
+ if (isFilled(value)) return typeof value === "boolean";
558
+ return true;
559
+ },
560
+ message: "The value must be a native boolean"
561
+ });
562
+
563
+ //#endregion
564
+ //#region src/rules/checked.ts
565
+ /**
566
+ * Requires a boolean value to be true. This is useful for checkbox inputs.
567
+ */
568
+ const checked = createRule({
569
+ type: "checked",
570
+ validator: (value) => {
571
+ if (isFilled(value)) return value === true;
572
+ return true;
573
+ },
574
+ message: "The field must be checked"
575
+ });
576
+
577
+ //#endregion
578
+ //#region src/rules/contains.ts
579
+ /**
580
+ * Checks if the string contains the specified substring.
581
+ *
582
+ * @param part - the part the value needs to contain
583
+ */
584
+ const contains = createRule({
585
+ type: "contains",
586
+ validator(value, part) {
587
+ if (isFilled(value) && isFilled(part)) return value.includes(part);
588
+ return true;
589
+ },
590
+ message({ $params: [part] }) {
591
+ return `The value must contain ${part}`;
592
+ }
593
+ });
594
+
595
+ //#endregion
596
+ //#region src/rules/date.ts
597
+ /**
598
+ * Requires a value to be a native Date constructor
599
+ *
600
+ * Mainly used for typing
601
+ */
602
+ const date = createRule({
603
+ type: "date",
604
+ validator: (value) => {
605
+ if (isFilled(value)) return value instanceof Date;
606
+ return true;
607
+ },
608
+ message: "The value must be a native Date constructor"
609
+ });
610
+
611
+ //#endregion
612
+ //#region src/utils/getLocale.util.ts
613
+ function getUserLocale() {
614
+ if (navigator.languages != void 0) return navigator.languages[0];
615
+ return navigator.language;
616
+ }
617
+ function formatLocaleDate(date$1) {
618
+ if (date$1) return new Intl.DateTimeFormat(getUserLocale(), { dateStyle: "short" }).format(new Date(date$1));
619
+ return "?";
620
+ }
621
+
622
+ //#endregion
623
+ //#region src/rules/dateAfter.ts
624
+ /**
625
+ * Checks if the date is after the given parameter.
626
+ *
627
+ * @param after - the date to compare to
628
+ * @param options - comparison options
629
+ */
630
+ const dateAfter = createRule({
631
+ type: "dateAfter",
632
+ validator: (value, after, options) => {
633
+ const { allowEqual = true } = options ?? {};
634
+ if (isFilled(value) && isFilled(after)) {
635
+ if (isDate(value) && isDate(after)) {
636
+ const result = allowEqual ? toDate(value).getTime() >= toDate(after).getTime() : toDate(value).getTime() > toDate(after).getTime();
637
+ if (result) return true;
638
+ return {
639
+ $valid: false,
640
+ error: "date-not-after"
641
+ };
642
+ }
643
+ return {
644
+ $valid: false,
645
+ error: "value-or-parameter-not-a-date"
646
+ };
647
+ }
648
+ return true;
649
+ },
650
+ message: ({ $params: [after], error }) => {
651
+ if (error === "value-or-parameter-not-a-date") return "The values must be dates";
652
+ return `The date must be after ${formatLocaleDate(after)}`;
653
+ }
654
+ });
655
+
656
+ //#endregion
657
+ //#region src/rules/dateBefore.ts
658
+ /**
659
+ * Checks if the date is before the given parameter.
660
+ *
661
+ * @param before - the date to compare to
662
+ * @param options - comparison options
663
+ */
664
+ const dateBefore = createRule({
665
+ type: "dateBefore",
666
+ validator: (value, before, options) => {
667
+ const { allowEqual = true } = options ?? {};
668
+ if (isFilled(value) && isFilled(before)) {
669
+ if (isDate(value) && isDate(before)) {
670
+ const result = allowEqual ? toDate(value).getTime() <= toDate(before).getTime() : toDate(value).getTime() < toDate(before).getTime();
671
+ if (result) return true;
672
+ return {
673
+ $valid: false,
674
+ error: "date-not-before"
675
+ };
676
+ }
677
+ return {
678
+ $valid: false,
679
+ error: "value-or-parameter-not-a-date"
680
+ };
681
+ }
682
+ return true;
683
+ },
684
+ message: ({ $params: [before], error }) => {
685
+ if (error === "value-or-parameter-not-a-date") return "The values must be dates";
686
+ return `The date must be before ${formatLocaleDate(before)}`;
687
+ }
688
+ });
689
+
690
+ //#endregion
691
+ //#region src/rules/dateBetween.ts
692
+ /**
693
+ * Checks if the date falls between the specified bounds.
694
+ *
695
+ * @param before - the minimum limit
696
+ * @param after - the maximum limit
697
+ * @param options - comparison options
698
+ */
699
+ const dateBetween = createRule({
700
+ type: "dateBetween",
701
+ validator: (value, before, after, options) => {
702
+ const { allowEqual = true } = options ?? {};
703
+ if (isDate(value) && isDate(before) && isDate(after)) if (allowEqual) return toDate(value).getTime() >= toDate(before).getTime() && toDate(value).getTime() <= toDate(after).getTime();
704
+ else return toDate(value).getTime() > toDate(before).getTime() && toDate(value).getTime() < toDate(after).getTime();
705
+ return true;
706
+ },
707
+ message: ({ $params: [before, after] }) => {
708
+ return `The date must be between ${formatLocaleDate(before)} and ${formatLocaleDate(after)}`;
709
+ }
710
+ });
711
+
712
+ //#endregion
713
+ //#region src/rules/decimal.ts
714
+ const decimalRegex = /^[-]?\d*(\.\d+)?$/;
715
+ /**
716
+ * Allows positive and negative decimal numbers.
717
+ */
718
+ const decimal = createRule({
719
+ type: "decimal",
720
+ validator(value) {
721
+ if (isEmpty(value)) return true;
722
+ return matchRegex(value, decimalRegex);
723
+ },
724
+ message: "The value must be decimal"
725
+ });
726
+
727
+ //#endregion
728
+ //#region src/rules/email.ts
729
+ const emailRegex = /^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i;
730
+ /**
731
+ * Validates email addresses. Always verify on the server to ensure the address is real and not already in use.
732
+ */
733
+ const email = createRule({
734
+ type: "email",
735
+ validator(value) {
736
+ if (isEmpty(value)) return true;
737
+ return matchRegex(value, emailRegex);
738
+ },
739
+ message: "The value must be an valid email address"
740
+ });
741
+
742
+ //#endregion
743
+ //#region src/rules/endsWith.ts
744
+ /**
745
+ * Checks if the string ends with the specified substring.
746
+ *
747
+ * @param part - the value the field must end with
748
+ */
749
+ const endsWith = createRule({
750
+ type: "endsWith",
751
+ validator(value, part) {
752
+ if (isFilled(value) && isFilled(part)) return value.endsWith(part);
753
+ return true;
754
+ },
755
+ message({ $params: [part] }) {
756
+ return `The value must end with ${part}`;
757
+ }
758
+ });
759
+
760
+ //#endregion
761
+ //#region src/rules/exactLength.ts
762
+ /**
763
+ * Requires the input value to have a strict specified length, inclusive. Works with arrays, objects and strings.
764
+ *
765
+ * @param count - the required length
766
+ */
767
+ const exactLength = createRule({
768
+ type: "exactLength",
769
+ validator: (value, count) => {
770
+ if (isFilled(value, false) && isFilled(count)) {
771
+ if (isNumber(count)) return getSize(value) === count;
772
+ console.warn(`[minLength] Parameter isn't a number, got parameter: ${count}`);
773
+ return false;
774
+ }
775
+ return true;
776
+ },
777
+ message: ({ $params: [count] }) => {
778
+ return `The value should be exactly ${count} characters long`;
779
+ }
780
+ });
781
+
782
+ //#endregion
783
+ //#region src/rules/exactValue.ts
784
+ /**
785
+ * Requires a field to have a strict numeric value.
786
+ */
787
+ const exactValue = createRule({
788
+ type: "exactValue",
789
+ validator: (value, count) => {
790
+ if (isFilled(value) && isFilled(count)) {
791
+ if (isNumber(count) && !isNaN(toNumber(value))) return toNumber(value) === count;
792
+ console.warn(`[exactValue] Value or parameter isn't a number, got value: ${value}, parameter: ${count}`);
793
+ return true;
794
+ }
795
+ return true;
796
+ },
797
+ message: ({ $params: [count] }) => {
798
+ return `The value must be equal to ${count}`;
799
+ }
800
+ });
801
+
802
+ //#endregion
803
+ //#region src/rules/hexadecimal.ts
804
+ const hexadecimalRegex = /^[a-fA-F0-9]*$/;
805
+ /**
806
+ * Allows only hexadecimal values.
807
+ */
808
+ const hexadecimal = createRule({
809
+ type: "hexadecimal",
810
+ validator(value) {
811
+ if (isEmpty(value)) return true;
812
+ return matchRegex(value, hexadecimalRegex);
813
+ },
814
+ message: "The value must be hexadecimal"
815
+ });
816
+
817
+ //#endregion
818
+ //#region src/rules/integer.ts
819
+ const integerRegex = /(^[0-9]*$)|(^-[0-9]+$)/;
820
+ /**
821
+ * Allows only integers (positive and negative).
822
+ */
823
+ const integer = createRule({
824
+ type: "integer",
825
+ validator(value) {
826
+ if (isEmpty(value)) return true;
827
+ return matchRegex(value, integerRegex);
828
+ },
829
+ message: "The value must be an integer"
830
+ });
831
+
832
+ //#endregion
833
+ //#region src/rules/ipv4Address.ts
834
+ function nibbleValid(nibble) {
835
+ if (nibble.length > 3 || nibble.length === 0) return false;
836
+ if (nibble[0] === "0" && nibble !== "0") return false;
837
+ if (!nibble.match(/^\d+$/)) return false;
838
+ const numeric$1 = +nibble | 0;
839
+ return numeric$1 >= 0 && numeric$1 <= 255;
840
+ }
841
+ /**
842
+ * Validates IPv4 addresses in dotted decimal notation 127.0.0.1.
843
+ */
844
+ const ipv4Address = createRule({
845
+ type: "ipv4Address",
846
+ validator(value) {
847
+ if (isEmpty(value)) return true;
848
+ if (typeof value !== "string") return false;
849
+ const nibbles = value.split(".");
850
+ return nibbles.length === 4 && nibbles.every(nibbleValid);
851
+ },
852
+ message: "The value is not a valid IPv4 address"
853
+ });
854
+
855
+ //#endregion
856
+ //#region src/rules/literal.ts
857
+ /**
858
+ * Allow only one possible literal value
859
+ */
860
+ function literal(literal$1) {
861
+ const params = computed(() => toValue(literal$1));
862
+ const rule = withMessage(withParams((value, literal$2) => {
863
+ if (isFilled(value) && isFilled(literal$2)) return literal$2 === value;
864
+ return true;
865
+ }, [params]), ({ $params: [literal$2] }) => `Value should be ${literal$2}.`);
866
+ return rule;
867
+ }
868
+
869
+ //#endregion
870
+ //#region src/rules/macAddress.ts
871
+ /**
872
+ * Validates MAC addresses. Call as a function to specify a custom separator (e.g., ':' or an empty string for 00ff1122334455).
873
+ *
874
+ * @param separator - the custom separator
875
+ */
876
+ const macAddress = createRule({
877
+ type: "macAddress",
878
+ validator(value, separator = ":") {
879
+ if (isEmpty(value)) return true;
880
+ if (typeof value !== "string") return false;
881
+ const parts = typeof separator === "string" && separator !== "" ? value.split(separator) : value.length === 12 || value.length === 16 ? value.match(/.{2}/g) : null;
882
+ return parts !== null && (parts.length === 6 || parts.length === 8) && parts.every(hexValid);
883
+ },
884
+ message: "The value is not a valid MAC Address"
885
+ });
886
+ const hexValid = (hex) => hex.toLowerCase().match(/^[0-9a-f]{2}$/);
887
+
888
+ //#endregion
889
+ //#region src/rules/maxLength.ts
890
+ /**
891
+ * Requires the input value to have a maximum specified length, inclusive. Works with arrays, objects and strings.
892
+ *
893
+ * @param max - the maximum length
894
+ * @param options - comparison options
895
+ */
896
+ const maxLength = createRule({
897
+ type: "maxLength",
898
+ validator: (value, count, options) => {
899
+ const { allowEqual = true } = options ?? {};
900
+ if (isFilled(value, false) && isFilled(count)) {
901
+ if (isNumber(count)) if (allowEqual) return getSize(value) <= count;
902
+ else return getSize(value) < count;
903
+ console.warn(`[maxLength] Value or parameter isn't a number, got value: ${value}, parameter: ${count}`);
904
+ return false;
905
+ }
906
+ return true;
907
+ },
908
+ message: ({ $value, $params: [count] }) => {
909
+ if (Array.isArray($value)) return `This list should have maximum ${count} items`;
910
+ return `The value length should not exceed ${count}`;
911
+ }
912
+ });
913
+
914
+ //#endregion
915
+ //#region src/rules/maxValue.ts
916
+ /**
917
+ * Requires a field to have a specified maximum numeric value.
918
+ *
919
+ * @param max - the maximum value
920
+ * @param options - comparison options
921
+ */
922
+ const maxValue = createRule({
923
+ type: "maxValue",
924
+ validator: (value, count, options) => {
925
+ const { allowEqual = true } = options ?? {};
926
+ if (isFilled(value) && isFilled(count)) {
927
+ if (isNumber(count) && !isNaN(toNumber(value))) if (allowEqual) return toNumber(value) <= count;
928
+ else return toNumber(value) < count;
929
+ console.warn(`[maxValue] Value or parameter isn't a number, got value: ${value}, parameter: ${count}`);
930
+ return true;
931
+ }
932
+ return true;
933
+ },
934
+ message: ({ $params: [count, options] }) => {
935
+ const { allowEqual = true } = options ?? {};
936
+ if (allowEqual) return `The value must be less than or equal to ${count}`;
937
+ else return `The value must be less than ${count}`;
938
+ }
939
+ });
940
+
941
+ //#endregion
942
+ //#region src/rules/minLength.ts
943
+ /**
944
+ * Requires the input value to have a minimum specified length, inclusive. Works with arrays, objects and strings.
945
+ *
946
+ * @param min - the minimum value
947
+ * @param options - comparison options
948
+ */
949
+ const minLength = createRule({
950
+ type: "minLength",
951
+ validator: (value, count, options) => {
952
+ const { allowEqual = true } = options ?? {};
953
+ if (isFilled(value, false) && isFilled(count)) {
954
+ if (isNumber(count)) if (allowEqual) return getSize(value) >= count;
955
+ else return getSize(value) > count;
956
+ console.warn(`[minLength] Parameter isn't a number, got parameter: ${count}`);
957
+ return false;
958
+ }
959
+ return true;
960
+ },
961
+ message: ({ $value, $params: [count] }) => {
962
+ if (Array.isArray($value)) return `The list should have at least ${count} items`;
963
+ return `The value length should be at least ${count}`;
964
+ }
965
+ });
966
+
967
+ //#endregion
968
+ //#region src/rules/minValue.ts
969
+ /**
970
+ * Requires a field to have a specified minimum numeric value.
971
+ *
972
+ * @param count - the minimum count
973
+ * @param options - comparison options
974
+ */
975
+ const minValue = createRule({
976
+ type: "minValue",
977
+ validator: (value, count, options) => {
978
+ const { allowEqual = true } = options ?? {};
979
+ if (isFilled(value) && isFilled(count)) {
980
+ if (isNumber(count) && !isNaN(toNumber(value))) if (allowEqual) return toNumber(value) >= count;
981
+ else return toNumber(value) > count;
982
+ console.warn(`[minValue] Value or parameter isn't a number, got value: ${value}, parameter: ${count}`);
983
+ return true;
984
+ }
985
+ return true;
986
+ },
987
+ message: ({ $params: [count, options] }) => {
988
+ const { allowEqual = true } = options ?? {};
989
+ if (allowEqual) return `The value must be greater than or equal to ${count}`;
990
+ else return `The value must be greater than ${count}`;
991
+ }
992
+ });
993
+
994
+ //#endregion
995
+ //#region src/rules/nativeEnum.ts
996
+ function getValidEnumValues(obj) {
997
+ const validKeys = Object.keys(obj).filter((k) => typeof obj[obj[k]] !== "number");
998
+ const filtered = {};
999
+ for (const k of validKeys) filtered[k] = obj[k];
1000
+ return Object.values(filtered);
1001
+ }
1002
+ /**
1003
+ * Validate against a native Typescript enum value.
1004
+ */
1005
+ function nativeEnum(enumLike) {
1006
+ const params = computed(() => toValue(enumLike));
1007
+ const rule = withMessage(withParams((value, enumLike$1) => {
1008
+ if (isFilled(value) && !isEmpty(enumLike$1)) {
1009
+ const validValues = getValidEnumValues(enumLike$1);
1010
+ return validValues.includes(value);
1011
+ }
1012
+ return true;
1013
+ }, [params]), ({ $params: [enumLike$1] }) => `The value should be one of those options: ${Object.values(enumLike$1).join(", ")}.`);
1014
+ return rule;
1015
+ }
1016
+
1017
+ //#endregion
1018
+ //#region src/rules/number.ts
1019
+ /**
1020
+ * Requires a value to be a native number type
1021
+ *
1022
+ * Mainly used for typing
1023
+ */
1024
+ const number = createRule({
1025
+ type: "number",
1026
+ validator: (value) => {
1027
+ if (isFilled(value)) return isNumber(value);
1028
+ return true;
1029
+ },
1030
+ message: "The value must be a native number"
1031
+ });
1032
+
1033
+ //#endregion
1034
+ //#region src/rules/numeric.ts
1035
+ const numericRegex = /^\d*(\.\d+)?$/;
1036
+ /**
1037
+ * Allows only numeric values (including numeric strings).
1038
+ */
1039
+ const numeric = createRule({
1040
+ type: "numeric",
1041
+ validator(value) {
1042
+ if (isEmpty(value)) return true;
1043
+ return matchRegex(value, numericRegex);
1044
+ },
1045
+ message: "The value must be numeric"
1046
+ });
1047
+
1048
+ //#endregion
1049
+ //#region src/rules/oneOf.ts
1050
+ /**
1051
+ * Allow only one of the values from a fixed Array of possible entries.
1052
+ */
1053
+ function oneOf(options) {
1054
+ const params = computed(() => toValue(options));
1055
+ const rule = withMessage(withParams((value, options$1) => {
1056
+ if (isFilled(value) && isFilled(options$1, false)) return options$1.includes(value);
1057
+ return true;
1058
+ }, [params]), ({ $params: [options$1] }) => `The value should be one of those options: ${options$1.join(", ")}.`);
1059
+ return rule;
1060
+ }
1061
+
1062
+ //#endregion
1063
+ //#region src/rules/regex.ts
1064
+ /**
1065
+ * Checks if the value matches one or more regular expressions.
1066
+ */
1067
+ const regex = createRule({
1068
+ type: "regex",
1069
+ validator(value, regexp) {
1070
+ if (isFilled(value)) {
1071
+ const filteredRegexp = Array.isArray(regexp) ? regexp : [regexp];
1072
+ return matchRegex(value, ...filteredRegexp);
1073
+ }
1074
+ return true;
1075
+ },
1076
+ message: "The value does not match the required pattern"
1077
+ });
1078
+
1079
+ //#endregion
1080
+ //#region src/rules/required.ts
1081
+ /**
1082
+ * Requires non-empty data. Checks for empty arrays and strings containing only whitespaces.
1083
+ */
1084
+ const required = createRule({
1085
+ type: "required",
1086
+ validator: (value) => {
1087
+ return isFilled(value);
1088
+ },
1089
+ message: "This field is required"
1090
+ });
1091
+
1092
+ //#endregion
1093
+ //#region src/rules/requiredIf.ts
1094
+ /**
1095
+ * Requires non-empty data, only if provided data property, ref, or a function resolves to true.
1096
+ *
1097
+ * @param condition - the condition to enable the required rule
1098
+ */
1099
+ const requiredIf = createRule({
1100
+ type: "required",
1101
+ validator(value, condition) {
1102
+ if (condition) return isFilled(value);
1103
+ return true;
1104
+ },
1105
+ message: "This field is required",
1106
+ active({ $params: [condition] }) {
1107
+ return condition;
1108
+ }
1109
+ });
1110
+
1111
+ //#endregion
1112
+ //#region src/rules/requiredUnless.ts
1113
+ /**
1114
+ * Requires non-empty data, only if provided data property, ref, or a function resolves to false.
1115
+ *
1116
+ * @param condition - the condition to disable the required rule
1117
+ */
1118
+ const requiredUnless = createRule({
1119
+ type: "required",
1120
+ validator(value, condition) {
1121
+ if (!condition) return isFilled(value);
1122
+ return true;
1123
+ },
1124
+ message: "This field is required",
1125
+ active({ $params: [condition] }) {
1126
+ return !condition;
1127
+ }
1128
+ });
1129
+
1130
+ //#endregion
1131
+ //#region src/rules/sameAs.ts
1132
+ /**
1133
+ * Checks if the value matches the specified property or ref.
1134
+ */
1135
+ const sameAs = createRule({
1136
+ type: "sameAs",
1137
+ validator(value, target, otherName) {
1138
+ if (isEmpty(value)) return true;
1139
+ return value === target;
1140
+ },
1141
+ message({ $params: [_, otherName = "other"] }) {
1142
+ return `The value must be equal to the ${otherName} value`;
1143
+ }
1144
+ });
1145
+
1146
+ //#endregion
1147
+ //#region src/rules/startsWith.ts
1148
+ /**
1149
+ * Checks if the string starts with the specified substring.
1150
+ *
1151
+ * @private part - the value the field must start with
1152
+ */
1153
+ const startsWith = createRule({
1154
+ type: "startsWith",
1155
+ validator(value, part) {
1156
+ if (isFilled(value) && isFilled(part)) return value.startsWith(part);
1157
+ return true;
1158
+ },
1159
+ message({ $params: [part] }) {
1160
+ return `The value must end with ${part}`;
1161
+ }
1162
+ });
1163
+
1164
+ //#endregion
1165
+ //#region src/rules/string.ts
1166
+ /**
1167
+ * Requires a value to be a native string type
1168
+ *
1169
+ * Mainly used for typing
1170
+ */
1171
+ const string = createRule({
1172
+ type: "string",
1173
+ validator: (value) => {
1174
+ if (isFilled(value)) return typeof value === "string";
1175
+ return true;
1176
+ },
1177
+ message: "The value must be a string"
1178
+ });
1179
+
1180
+ //#endregion
1181
+ //#region src/rules/type.ts
1182
+ /**
1183
+ * Define the input type of a rule. No runtime validation.
1184
+ *
1185
+ * Override any input type set by other rules.
1186
+ */
1187
+ function type() {
1188
+ return () => true;
1189
+ }
1190
+
1191
+ //#endregion
1192
+ //#region src/rules/url.ts
1193
+ /**
1194
+ * Regex taken from {@link https://gist.github.com/dperini/729294}
1195
+ */
1196
+ const urlRegex = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i;
1197
+ /**
1198
+ * Validates URLs.
1199
+ */
1200
+ const url = createRule({
1201
+ type: "url",
1202
+ validator(value) {
1203
+ if (isEmpty(value)) return true;
1204
+ return matchRegex(value, urlRegex);
1205
+ },
1206
+ message: "The value is not a valid URL address"
1207
+ });
1208
+
1209
+ //#endregion
1210
+ export { alpha, alphaNum, and, applyIf, between, boolean, checked, contains, date, dateAfter, dateBefore, dateBetween, decimal, email, endsWith, exactLength, exactValue, getSize, hexadecimal, integer, ipv4Address, isDate, isEmpty, isFilled, isNumber, literal, macAddress, matchRegex, maxLength, maxValue, minLength, minValue, nativeEnum, not, number, numeric, oneOf, or, regex, required, requiredIf, requiredUnless, sameAs, startsWith, string, toDate, toNumber, type, url, withAsync, withMessage, withParams, withTooltip };