@kklab/fortress-validator 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,9 +92,13 @@ const result = validator
92
92
  | `fileMaxSize` | Passes if the field's value is not greater than the specified maximum file size. |
93
93
  | `fileMinSize` | Passes if the field's value is at least the specified minimum file size. |
94
94
  | `fileSize` | Passes if the field's value matches the specified file size. |
95
- | `http` | Passes if the field's value starts with "http://" or "https://". |
95
+ | `http` | Passes if the field's value starts with "http://". |
96
+ | `httpOrHttps` | Passes if the field's value starts with "http://" or "https://". |
96
97
  | `https` | Passes if the field's value starts with "https://". |
97
98
  | `integer` | Passes if the field's value is an integer. |
99
+ | `ip` | Passes if the field's value is a valid IP address. |
100
+ | `ipv4` | Passes if the field's value is a valid IPv4 address. |
101
+ | `ipv6` | Passes if the field's value is a valid IPv6 address. |
98
102
  | `iso8601` | Passes if the field's value is a valid ISO 8601 date. |
99
103
  | `json` | Passes if the field's value is a valid JSON string. |
100
104
  | `jsonSchema` | Passes if the field's value matches the specified JSON schema. |
@@ -111,6 +115,7 @@ const result = validator
111
115
  | `number` | Passes if the field's value is a number. |
112
116
  | `numeric` | Passes if the field's value contains only numeric characters. |
113
117
  | `oneOf` | Passes if the field's value is one of the specified values. |
118
+ | `protocol` | Passes if the field's value starts with the specified protocol. |
114
119
  | `regex` | Passes if the field's value matches the specified regular expression. |
115
120
  | `required` | Passes if the field's value is not empty. |
116
121
  | `requiredWhen` | Passes if the field's value is not empty when the specified condition is true. |
@@ -9,6 +9,7 @@ declare class FieldValidator {
9
9
  private ruleFunctions;
10
10
  private conditions;
11
11
  private shouldSkip;
12
+ private appliedProtocols;
12
13
  constructor({ name, locale, fallbackLocale, locales, rules, }: FieldValidatorArguments);
13
14
  get formattedName(): string;
14
15
  get messages(): Messages;
@@ -141,17 +142,33 @@ declare class FieldValidator {
141
142
  */
142
143
  fileSize(size: number): this;
143
144
  /**
144
- * Passes if the field's value starts with "http://" or "https://".
145
+ * Passes if the field's value starts with the "http://" protocol.
145
146
  */
146
147
  http(): this;
147
148
  /**
148
- * Passes if the field's value starts with "https://".
149
+ * Passes if the field's value starts with the "http://" or "https://" protocols.
150
+ */
151
+ httpOrHttps(): this;
152
+ /**
153
+ * Passes if the field's value starts with the "https://" protocol.
149
154
  */
150
155
  https(): this;
151
156
  /**
152
157
  * Passes if the field's value is an integer.
153
158
  */
154
159
  integer(): this;
160
+ /**
161
+ * Passes if the field's value is a valid IP address.
162
+ */
163
+ ip(): this;
164
+ /**
165
+ * Passes if the field's value is a valid IPv4 address.
166
+ */
167
+ ipv4(): this;
168
+ /**
169
+ * Passes if the field's value is a valid IPv6 address.
170
+ */
171
+ ipv6(): this;
155
172
  /**
156
173
  * Passes if the field's value is a valid ISO 8601 date.
157
174
  */
@@ -204,6 +221,10 @@ declare class FieldValidator {
204
221
  * Passes if the field's value is not one of the specified values.
205
222
  */
206
223
  notOneOf(values: string[]): this;
224
+ /**
225
+ * Passes if the field's value starts with the specified protocol.
226
+ */
227
+ protocol(protocol: string | string[]): this;
207
228
  /**
208
229
  * Passes if the field's value is a number.
209
230
  */
package/dist/index.js CHANGED
@@ -1,27 +1,31 @@
1
- var x = Object.defineProperty;
2
- var S = (t, e, r) => e in t ? x(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
3
- var l = (t, e, r) => (S(t, typeof e != "symbol" ? e + "" : e, r), r);
4
- const $ = (t) => t.replace(/([^\x20-\x7E])([\x20-\x7E])/gu, "$1 $2").replace(/([\x20-\x7E])([^\x20-\x7E])/gu, "$1 $2").replace(/ +/g, " "), n = (t, e = 0) => new Intl.NumberFormat(void 0, {
1
+ var q = Object.defineProperty;
2
+ var O = (t, e, r) => e in t ? q(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
3
+ var o = (t, e, r) => (O(t, typeof e != "symbol" ? e + "" : e, r), r);
4
+ const A = (t) => {
5
+ const e = ",、;:!?。()《》「」『』【】〔〕", r = "\\x20-\\x7E", s = new RegExp(`\\s*([${e}])`, "gu"), l = new RegExp(`([${e}])\\s*([${r}])`, "gu"), u = new RegExp(`([^${r}${e}])([${r}])`, "gu"), _ = new RegExp(`([${r}])([^${r}${e}])`, "gu");
6
+ return t.replace(s, "$1").replace(l, "$1$2").replace(u, "$1 $2").replace(_, "$1 $2").replace(/ +/g, " ");
7
+ }, n = (t, e = 0) => new Intl.NumberFormat(void 0, {
5
8
  minimumFractionDigits: e,
6
9
  maximumFractionDigits: e
7
- }).format(t), z = (t) => Object.prototype.toString.call(t).toLowerCase().slice(8, -1), s = (t) => t == null || t === "" || Array.isArray(t) && t.length < 1;
8
- class C {
10
+ }).format(t), M = (t) => Object.prototype.toString.call(t).toLowerCase().slice(8, -1), a = (t) => t == null || t === "" || Array.isArray(t) && t.length < 1, i = (t) => `"${t}"`;
11
+ class k {
9
12
  constructor({
10
13
  name: e,
11
14
  locale: r,
12
- fallbackLocale: i,
13
- locales: a,
15
+ fallbackLocale: s,
16
+ locales: l,
14
17
  rules: u
15
18
  }) {
16
- l(this, "name");
17
- l(this, "locale");
18
- l(this, "fallbackLocale");
19
- l(this, "locales");
20
- l(this, "rules");
21
- l(this, "ruleFunctions", []);
22
- l(this, "conditions", {});
23
- l(this, "shouldSkip", !1);
24
- this.name = e, this.locale = r, this.fallbackLocale = i, this.locales = a, this.rules = u;
19
+ o(this, "name");
20
+ o(this, "locale");
21
+ o(this, "fallbackLocale");
22
+ o(this, "locales");
23
+ o(this, "rules");
24
+ o(this, "ruleFunctions", {});
25
+ o(this, "conditions", {});
26
+ o(this, "shouldSkip", !1);
27
+ o(this, "appliedProtocols", {});
28
+ this.name = e, this.locale = r, this.fallbackLocale = s, this.locales = l, this.rules = u;
25
29
  }
26
30
  get formattedName() {
27
31
  return this.name.toLowerCase();
@@ -50,31 +54,31 @@ class C {
50
54
  return this.rules[e];
51
55
  }
52
56
  buildRuleFunction(e, r) {
53
- return (i) => {
54
- if (s(i) && !this.mandatoryRules.includes(e))
57
+ return (s) => {
58
+ if (a(s) && !this.mandatoryRules.includes(e))
55
59
  return !0;
56
- const a = this.getRule(e)(r)(i);
57
- return typeof a == "string" ? a : a === !0 ? !0 : this.buildRuleFunctionMessage(e, r, i);
60
+ const l = this.getRule(e)(r)(s);
61
+ return typeof l == "string" ? l : l === !0 ? !0 : this.buildRuleFunctionMessage(e, r, s);
58
62
  };
59
63
  }
60
- buildRuleFunctionMessage(e, r, i) {
61
- const a = this.getMessage(e)(this.formattedName, r);
62
- if (typeof a == "object") {
63
- const u = z(i);
64
- if (!(u in a))
64
+ buildRuleFunctionMessage(e, r, s) {
65
+ const l = this.getMessage(e)(this.formattedName, r);
66
+ if (typeof l == "object") {
67
+ const u = M(s);
68
+ if (!(u in l))
65
69
  throw new Error(`The message for the "${e}" rule of the "${u}" type is missing.`);
66
- return $(a[u]);
70
+ return A(l[u]);
67
71
  }
68
- return $(a);
72
+ return A(l);
69
73
  }
70
74
  pushRuleFunction(e, r) {
71
75
  if (e in this.conditions && !this.conditions[e])
72
76
  return this;
73
- const i = this.buildRuleFunction(e, r);
74
- return this.ruleFunctions.push(i), this;
77
+ const s = this.buildRuleFunction(e, r);
78
+ return this.ruleFunctions[e] = s, this;
75
79
  }
76
80
  getRuleFunctions() {
77
- return this.ruleFunctions;
81
+ return Object.values(this.ruleFunctions);
78
82
  }
79
83
  /**
80
84
  * Collects the rule functions.
@@ -88,10 +92,11 @@ class C {
88
92
  validate(e) {
89
93
  if (this.shouldSkip)
90
94
  return !0;
91
- for (const r of this.ruleFunctions) {
92
- const i = r(e);
93
- if (typeof i == "string")
94
- return i;
95
+ const r = this.getRuleFunctions();
96
+ for (const s of r) {
97
+ const l = s(e);
98
+ if (typeof l == "string")
99
+ return l;
95
100
  }
96
101
  return !0;
97
102
  }
@@ -110,8 +115,8 @@ class C {
110
115
  /**
111
116
  * Passes if the field's value is a date that occurs after the specified date.
112
117
  */
113
- after(e, r, i, a = !0) {
114
- return this.apply(this.after.name, { date: e, format: r, displayFormat: i, strict: a });
118
+ after(e, r, s, l = !0) {
119
+ return this.apply(this.after.name, { date: e, format: r, displayFormat: s, strict: l });
115
120
  }
116
121
  /**
117
122
  * Passes if the field's value contains only letters.
@@ -152,8 +157,8 @@ class C {
152
157
  /**
153
158
  * Passes if the field's value is a date that occurs before the specified date.
154
159
  */
155
- before(e, r, i, a = !0) {
156
- return this.apply(this.before.name, { date: e, format: r, displayFormat: i, strict: a });
160
+ before(e, r, s, l = !0) {
161
+ return this.apply(this.before.name, { date: e, format: r, displayFormat: s, strict: l });
157
162
  }
158
163
  /**
159
164
  * Passes if the field's value is between the specified minimum and maximum values.
@@ -264,16 +269,22 @@ class C {
264
269
  return this.apply(this.fileSize.name, { size: e });
265
270
  }
266
271
  /**
267
- * Passes if the field's value starts with "http://" or "https://".
272
+ * Passes if the field's value starts with the "http://" protocol.
268
273
  */
269
274
  http() {
270
- return this.apply(this.http.name);
275
+ return this.appliedProtocols[this.http.name] = !0, this.apply(this.http.name);
271
276
  }
272
277
  /**
273
- * Passes if the field's value starts with "https://".
278
+ * Passes if the field's value starts with the "http://" or "https://" protocols.
279
+ */
280
+ httpOrHttps() {
281
+ return this.appliedProtocols[this.http.name] = !0, this.appliedProtocols[this.https.name] = !0, this.apply(this.httpOrHttps.name);
282
+ }
283
+ /**
284
+ * Passes if the field's value starts with the "https://" protocol.
274
285
  */
275
286
  https() {
276
- return this.apply(this.https.name);
287
+ return this.appliedProtocols[this.https.name] = !0, this.apply(this.https.name);
277
288
  }
278
289
  /**
279
290
  * Passes if the field's value is an integer.
@@ -281,6 +292,27 @@ class C {
281
292
  integer() {
282
293
  return this.apply(this.integer.name);
283
294
  }
295
+ /**
296
+ * Passes if the field's value is a valid IP address.
297
+ */
298
+ ip() {
299
+ const e = Object.keys(this.appliedProtocols).length > 0;
300
+ return this.apply(this.ip.name, { includeProtocol: e });
301
+ }
302
+ /**
303
+ * Passes if the field's value is a valid IPv4 address.
304
+ */
305
+ ipv4() {
306
+ const e = Object.keys(this.appliedProtocols).length > 0;
307
+ return this.apply(this.ipv4.name, { includeProtocol: e });
308
+ }
309
+ /**
310
+ * Passes if the field's value is a valid IPv6 address.
311
+ */
312
+ ipv6() {
313
+ const e = Object.keys(this.appliedProtocols).length > 0;
314
+ return this.apply(this.ipv6.name, { includeProtocol: e });
315
+ }
284
316
  /**
285
317
  * Passes if the field's value is a valid ISO 8601 date.
286
318
  */
@@ -359,6 +391,13 @@ class C {
359
391
  notOneOf(e) {
360
392
  return this.apply(this.notOneOf.name, { values: e });
361
393
  }
394
+ /**
395
+ * Passes if the field's value starts with the specified protocol.
396
+ */
397
+ protocol(e) {
398
+ const r = Array.isArray(e) ? e : [e];
399
+ return r.forEach((s) => this.appliedProtocols[s] = !0), this.apply(this.protocol.name, { values: r });
400
+ }
362
401
  /**
363
402
  * Passes if the field's value is a number.
364
403
  */
@@ -492,7 +531,7 @@ class C {
492
531
  return typeof e == "object" ? (this.conditions = e, this) : (e || (this.shouldSkip = !0), this);
493
532
  }
494
533
  }
495
- const _ = {
534
+ const E = {
496
535
  accepted: (t) => `The ${t} field must be accepted.`,
497
536
  alpha: (t) => `The ${t} field must only contain letters.`,
498
537
  alphaDash: (t) => `The ${t} field must only contain letters, numbers, dashes and underscores.`,
@@ -501,24 +540,24 @@ const _ = {
501
540
  array: (t) => `The ${t} field must be an array.`,
502
541
  ascii: (t) => `The ${t} field must only contain ASCII characters and symbols.`,
503
542
  between: (t, e) => {
504
- const { min: r, max: i } = e;
543
+ const { min: r, max: s } = e;
505
544
  return {
506
- number: `The ${t} field must be between ${n(r)} and ${n(i)}.`,
507
- array: `The ${t} field must contain items where each item is between ${n(r)} and ${n(i)}.`
545
+ number: `The ${t} field must be between ${n(r)} and ${n(s)}.`,
546
+ array: `The ${t} field must contain items where each item is between ${n(r)} and ${n(s)}.`
508
547
  };
509
548
  },
510
549
  betweenLength: (t, e) => {
511
- const { min: r, max: i } = e;
512
- return `The ${t} field must be between ${n(r)} and ${n(i)} items.`;
550
+ const { min: r, max: s } = e;
551
+ return `The ${t} field must be between ${n(r)} and ${n(s)} items.`;
513
552
  },
514
553
  boolean: (t) => `The ${t} field must be a boolean value.`,
515
554
  containsAll: (t, e) => {
516
555
  const { values: r } = e;
517
- return `The ${t} field must contain all of the following values: ${r.join(", ")}.`;
556
+ return `The ${t} field must contain all of the following values: ${r.map(i).join(", ")}.`;
518
557
  },
519
558
  containsAny: (t, e) => {
520
559
  const { values: r } = e;
521
- return `The ${t} field must contain at least one of the following values: ${r.join(", ")}.`;
560
+ return `The ${t} field must contain at least one of the following values: ${r.map(i).join(", ")}.`;
522
561
  },
523
562
  declined: (t) => `The ${t} field must be declined.`,
524
563
  different: (t, e) => {
@@ -538,10 +577,10 @@ const _ = {
538
577
  },
539
578
  file: (t) => `The ${t} field must be a file.`,
540
579
  fileBetweenSize: (t, e) => {
541
- const { min: r, max: i } = e;
580
+ const { min: r, max: s } = e;
542
581
  return {
543
- file: `The ${t} field must be between ${n(r)} and ${n(i)} kilobytes.`,
544
- array: `The ${t} field must contain items where each item is between ${n(r)} and ${n(i)} kilobytes.`
582
+ file: `The ${t} field must be between ${n(r)} and ${n(s)} kilobytes.`,
583
+ array: `The ${t} field must contain items where each item is between ${n(r)} and ${n(s)} kilobytes.`
545
584
  };
546
585
  },
547
586
  fileMaxSize: (t, e) => {
@@ -565,9 +604,13 @@ const _ = {
565
604
  array: `The ${t} field must contain items where each item is ${n(r)} kilobytes.`
566
605
  };
567
606
  },
568
- http: (t) => `The ${t} field must start with either "http://" or "https://".`,
569
- https: (t) => `The ${t} field must start with "http://".`,
607
+ http: (t) => `The ${t} field must start with the "http://" protocol.`,
608
+ httpOrHttps: (t) => `The ${t} field must start with the "http://" or "https://" protocols.`,
609
+ https: (t) => `The ${t} field must start with the "https://" protocol.`,
570
610
  integer: (t) => `The ${t} field must be an integer.`,
611
+ ip: (t) => `The ${t} field must be a valid IP address.`,
612
+ ipv4: (t) => `The ${t} field must be a valid IPv4 address.`,
613
+ ipv6: (t) => `The ${t} field must be a valid IPv6 address.`,
571
614
  json: (t) => `The ${t} field must be a valid JSON string.`,
572
615
  length: (t, e) => {
573
616
  const { length: r } = e;
@@ -598,11 +641,11 @@ const _ = {
598
641
  },
599
642
  notContainsAll: (t, e) => {
600
643
  const { values: r } = e;
601
- return `The ${t} field must not contain all of the following values together: ${r.join(", ")}.`;
644
+ return `The ${t} field must not contain all of the following values together: ${r.map(i).join(", ")}.`;
602
645
  },
603
646
  notContainsAny: (t, e) => {
604
647
  const { values: r } = e;
605
- return `The ${t} field must not contain any of the following values: ${r.join(", ")}.`;
648
+ return `The ${t} field must not contain any of the following values: ${r.map(i).join(", ")}.`;
606
649
  },
607
650
  notEquals: (t, e) => {
608
651
  const { value: r } = e;
@@ -610,13 +653,21 @@ const _ = {
610
653
  },
611
654
  notOneOf: (t, e) => {
612
655
  const { values: r } = e;
613
- return `The ${t} field must not be one of the following values: ${r.join(", ")}.`;
656
+ return `The ${t} field must not be one of the following values: ${r.map(i).join(", ")}.`;
614
657
  },
615
658
  number: (t) => `The ${t} field must be a number.`,
616
659
  numeric: (t) => `The ${t} field must be a number.`,
617
660
  oneOf: (t, e) => {
618
661
  const { values: r } = e;
619
- return `The ${t} field must be one of the following values: ${r.join(", ")}.`;
662
+ return `The ${t} field must be one of the following values: ${r.map(i).join(", ")}.`;
663
+ },
664
+ protocol: (t, e) => {
665
+ const { values: r } = e;
666
+ if (r.length === 1) {
667
+ const [s] = r;
668
+ return `The ${t} field must start with the "${s}://" protocol.`;
669
+ }
670
+ return `The ${t} field must start with one of the following protocols: ${r.map((s) => `${s}://`).map(i).join(", ")}.`;
620
671
  },
621
672
  regex: (t) => `The ${t} field must match the required format.`,
622
673
  required: (t) => `The ${t} field is required.`,
@@ -637,19 +688,19 @@ const _ = {
637
688
  },
638
689
  string: (t) => `The ${t} field must be a string.`,
639
690
  stringBetweenLength: (t, e) => {
640
- const { min: r, max: i } = e;
691
+ const { min: r, max: s } = e;
641
692
  return {
642
- string: `The ${t} field must be between ${n(r)} and ${n(i)} characters.`,
643
- array: `The ${t} field must contain items where each item is between ${n(r)} and ${n(i)} characters.`
693
+ string: `The ${t} field must be between ${n(r)} and ${n(s)} characters.`,
694
+ array: `The ${t} field must contain items where each item is between ${n(r)} and ${n(s)} characters.`
644
695
  };
645
696
  },
646
697
  stringContainsAll: (t, e) => {
647
698
  const { values: r } = e;
648
- return `The ${t} field must contain all of the following text: ${r.join(", ")}.`;
699
+ return `The ${t} field must contain all of the following text: ${r.map(i).join(", ")}.`;
649
700
  },
650
701
  stringContainsAny: (t, e) => {
651
702
  const { values: r } = e;
652
- return `The ${t} field must contain at least one of the following text: ${r.join(", ")}.`;
703
+ return `The ${t} field must contain at least one of the following text: ${r.map(i).join(", ")}.`;
653
704
  },
654
705
  stringLength: (t, e) => {
655
706
  const { length: r } = e;
@@ -674,42 +725,42 @@ const _ = {
674
725
  },
675
726
  stringNotContainsAll: (t, e) => {
676
727
  const { values: r } = e;
677
- return `The ${t} field must not contain all of the following text together: ${r.join(", ")}.`;
728
+ return `The ${t} field must not contain all of the following text together: ${r.map(i).join(", ")}.`;
678
729
  },
679
730
  stringNotContainsAny: (t, e) => {
680
731
  const { values: r } = e;
681
- return `The ${t} field must not contain any of the following text: ${r.join(", ")}.`;
732
+ return `The ${t} field must not contain any of the following text: ${r.map(i).join(", ")}.`;
682
733
  },
683
734
  unique: (t) => `The ${t} field has already been taken.`,
684
735
  uppercase: (t) => `The ${t} field must be uppercase.`,
685
736
  url: (t) => `The ${t} field must be a valid URL.`
686
- }, q = {
737
+ }, B = {
687
738
  accepted: () => "此欄位必須被同意",
688
739
  alpha: () => "此欄位只能包含字母",
689
740
  alphaDash: () => "此欄位只能包含字母、數字、連接號和底線",
690
741
  alphaDashDot: () => "此欄位只能包含字母、數字、連接號、底線和點",
691
742
  alphaNum: () => "此欄位只能包含字母和數字",
692
743
  array: () => "此欄位必須是一個陣列",
693
- ascii: () => "此欄位只能包含ASCII字元和符號",
744
+ ascii: () => "此欄位只能包含 ASCII 字元和符號",
694
745
  between: (t, e) => {
695
- const { min: r, max: i } = e;
746
+ const { min: r, max: s } = e;
696
747
  return {
697
- number: `此欄位必須介於${n(r)}到${n(i)}`,
698
- array: `此欄位中的每個項目都必須介於${n(r)}到${n(i)}`
748
+ number: `此欄位必須介於${n(r)}到${n(s)}`,
749
+ array: `此欄位中的每個項目都必須介於${n(r)}到${n(s)}`
699
750
  };
700
751
  },
701
752
  betweenLength: (t, e) => {
702
- const { min: r, max: i } = e;
703
- return `此欄位必須介於${n(r)}到${n(i)}個項目之間`;
753
+ const { min: r, max: s } = e;
754
+ return `此欄位必須介於${n(r)}到${n(s)}個項目之間`;
704
755
  },
705
756
  boolean: () => "此欄位必須是一個布林值",
706
757
  containsAll: (t, e) => {
707
758
  const { values: r } = e;
708
- return `此欄位必須包含以下所有項目:${r.join(", ")}`;
759
+ return `此欄位必須包含以下所有項目:${r.map(i).join(", ")}`;
709
760
  },
710
761
  containsAny: (t, e) => {
711
762
  const { values: r } = e;
712
- return `此欄位必須包含以下其中一個項目:${r.join(", ")}`;
763
+ return `此欄位必須包含以下其中一個項目:${r.map(i).join(", ")}`;
713
764
  },
714
765
  declined: () => "此欄位必須被拒絕",
715
766
  different: (t, e) => {
@@ -729,10 +780,10 @@ const _ = {
729
780
  },
730
781
  file: () => "此欄位必須是檔案",
731
782
  fileBetweenSize: (t, e) => {
732
- const { min: r, max: i } = e;
783
+ const { min: r, max: s } = e;
733
784
  return {
734
- file: `此欄位必須介於${n(r)}到${n(i)} KB之間`,
735
- array: `此欄位中的每個項目都必須介於${n(r)}到${n(i)} KB之間`
785
+ file: `此欄位必須介於${n(r)}到${n(s)} KB 之間`,
786
+ array: `此欄位中的每個項目都必須介於${n(r)}到${n(s)} KB 之間`
736
787
  };
737
788
  },
738
789
  fileMaxSize: (t, e) => {
@@ -756,9 +807,13 @@ const _ = {
756
807
  array: `此欄位中的每個項目都必須是${n(r)} KB`
757
808
  };
758
809
  },
759
- http: () => "此欄位必須以 http:// 或 https:// 開頭",
760
- https: () => "此欄位必須以 https:// 開頭",
810
+ http: () => '此欄位必須以 "http://" 協議開頭',
811
+ httpOrHttps: () => '此欄位必須以 "http://" 或 "https://" 協議開頭',
812
+ https: () => '此欄位必須以 "https://" 協議開頭',
761
813
  integer: () => "此欄位必須是整數",
814
+ ip: () => "此欄位必須是有效的 IP 位址",
815
+ ipv4: () => "此欄位必須是有效的 IPv4 位址",
816
+ ipv6: () => "此欄位必須是有效的 IPv6 位址",
762
817
  json: () => "此欄位必須是有效的 JSON 字串",
763
818
  length: (t, e) => {
764
819
  const { length: r } = e;
@@ -789,11 +844,11 @@ const _ = {
789
844
  },
790
845
  notContainsAll: (t, e) => {
791
846
  const { values: r } = e;
792
- return `此欄位不能同時包含以下所有值:${r.join(", ")}`;
847
+ return `此欄位不能同時包含以下所有值:${r.map(i).join(", ")}`;
793
848
  },
794
849
  notContainsAny: (t, e) => {
795
850
  const { values: r } = e;
796
- return `此欄位不能包含以下任何值:${r.join(", ")}`;
851
+ return `此欄位不能包含以下任何值:${r.map(i).join(", ")}`;
797
852
  },
798
853
  notEquals: (t, e) => {
799
854
  const { value: r } = e;
@@ -801,13 +856,21 @@ const _ = {
801
856
  },
802
857
  notOneOf: (t, e) => {
803
858
  const { values: r } = e;
804
- return `此欄位不能是以下任何值:${r.join(", ")}`;
859
+ return `此欄位不能是以下任何值:${r.map(i).join(", ")}`;
805
860
  },
806
861
  number: () => "此欄位必須是數字",
807
862
  numeric: () => "此欄位必須是數字",
808
863
  oneOf: (t, e) => {
809
864
  const { values: r } = e;
810
- return `此欄位必須是以下任何值:${r.join(", ")}`;
865
+ return `此欄位必須是以下其中一個值:${r.map(i).join(", ")}`;
866
+ },
867
+ protocol: (t, e) => {
868
+ const { values: r } = e;
869
+ if (r.length === 1) {
870
+ const [s] = r;
871
+ return `此欄位必須以 "${s}://" 協議開頭`;
872
+ }
873
+ return `此欄位必須以以下其中一個協議開頭:${r.map((s) => `${s}://`).map(i).join(", ")}`;
811
874
  },
812
875
  regex: () => "此欄位必須符合所需的格式",
813
876
  required: () => "此欄位為必填",
@@ -828,19 +891,19 @@ const _ = {
828
891
  },
829
892
  string: () => "此欄位必須是字串",
830
893
  stringBetweenLength: (t, e) => {
831
- const { min: r, max: i } = e;
894
+ const { min: r, max: s } = e;
832
895
  return {
833
- string: `此欄位必須介於${n(r)}到${n(i)}個字元之間`,
834
- array: `此欄位中的每個項目都必須介於${n(r)}到${n(i)}個字元之間`
896
+ string: `此欄位必須介於${n(r)}到${n(s)}個字元之間`,
897
+ array: `此欄位中的每個項目都必須介於${n(r)}到${n(s)}個字元之間`
835
898
  };
836
899
  },
837
900
  stringContainsAll: (t, e) => {
838
901
  const { values: r } = e;
839
- return `此欄位必須包含以下所有文字:${r.join(", ")}`;
902
+ return `此欄位必須包含以下所有文字:${r.map(i).join(", ")}`;
840
903
  },
841
904
  stringContainsAny: (t, e) => {
842
905
  const { values: r } = e;
843
- return `此欄位必須包含以下其中一個文字:${r.join(", ")}`;
906
+ return `此欄位必須包含以下其中一個文字:${r.map(i).join(", ")}`;
844
907
  },
845
908
  stringLength: (t, e) => {
846
909
  const { length: r } = e;
@@ -865,111 +928,146 @@ const _ = {
865
928
  },
866
929
  stringNotContainsAll: (t, e) => {
867
930
  const { values: r } = e;
868
- return `此欄位不能同時包含以下所有文字:${r.join(", ")}`;
931
+ return `此欄位不能同時包含以下所有文字:${r.map(i).join(", ")}`;
869
932
  },
870
933
  stringNotContainsAny: (t, e) => {
871
934
  const { values: r } = e;
872
- return `此欄位不能包含以下任何文字:${r.join(", ")}`;
935
+ return `此欄位不能包含以下任何文字:${r.map(i).join(", ")}`;
873
936
  },
874
937
  unique: () => "此欄位已經存在",
875
938
  uppercase: () => "此欄位必須是大寫",
876
- url: () => "此欄位必須是有效的網址"
877
- }, j = {
878
- en: _,
879
- "zh-TW": q
880
- }, M = () => (t) => s(t) ? !1 : ["y", "yes", "on", "1", "true"].includes(String(t).toLowerCase()), F = () => (t) => s(t) ? !1 : /^[a-zA-Z]+$/.test(String(t)), k = () => (t) => s(t) ? !1 : /^[a-zA-Z0-9-_]+$/.test(String(t)), O = () => (t) => s(t) ? !1 : /^[a-zA-Z0-9-_.]+$/.test(String(t)), B = () => (t) => s(t) ? !1 : /^[a-zA-Z0-9]+$/.test(String(t)), D = () => (t) => Array.isArray(t), E = () => (t) => s(t) ? !1 : /^[\x20-\x7E]+$/.test(String(t)), h = ({ max: t }) => (e) => s(e) ? !1 : typeof e == "number" ? e <= t : Array.isArray(e) ? e.every((r) => h({ max: t })(r)) : !1, c = ({ min: t }) => (e) => s(e) ? !1 : typeof e == "number" ? e >= t : Array.isArray(e) ? e.every((r) => c({ min: t })(r)) : !1, N = ({ min: t, max: e }) => (r) => s(r) ? !1 : c({ min: t })(r) && h({ max: e })(r), A = ({ length: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? e.length <= t : !1, w = ({ length: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? e.length >= t : !1, W = ({ min: t, max: e }) => (r) => s(r) ? !1 : w({ length: t })(r) && A({ length: e })(r), Z = () => (t) => s(t) ? !1 : typeof t == "boolean", p = ({ values: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? t.every((r) => e.includes(r)) : !1, b = ({ values: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? t.some((r) => e.includes(r)) : !1, K = () => (t) => s(t) ? !1 : ["n", "no", "off", "0", "false"].includes(String(t).toLowerCase()), I = ({ value: t }) => (e) => s(e) ? !1 : e !== t, J = () => (t) => s(t) ? !1 : Array.isArray(t) ? new Set(t).size === t.length : !0, P = () => (t) => s(t) ? !1 : /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)), U = () => (t) => s(t) ? !1 : /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)), V = ({ value: t }) => (e) => s(e) ? !1 : String(e).endsWith(t), G = ({ value: t }) => (e) => e === t, H = () => (t) => t instanceof File, f = ({ size: t }) => (e) => s(e) ? !1 : e instanceof File ? e.size <= t * 1024 : Array.isArray(e) ? e.every((r) => f({ size: t })(r)) : !1, m = ({ size: t }) => (e) => s(e) ? !1 : e instanceof File ? e.size >= t * 1024 : Array.isArray(e) ? e.every((r) => m({ size: t })(r)) : !1, Q = ({ min: t, max: e }) => (r) => s(r) ? !1 : m({ size: t })(r) && f({ size: e })(r), T = ({ size: t }) => (e) => {
881
- if (s(e))
939
+ url: () => "此欄位必須是有效的 URL"
940
+ }, D = {
941
+ en: E,
942
+ "zh-TW": B
943
+ }, P = () => (t) => a(t) ? !1 : ["y", "yes", "on", "1", "true"].includes(String(t).toLowerCase()), N = () => (t) => a(t) ? !1 : /^[a-zA-Z]+$/.test(String(t)), W = () => (t) => a(t) ? !1 : /^[a-zA-Z0-9-_]+$/.test(String(t)), I = () => (t) => a(t) ? !1 : /^[a-zA-Z0-9-_.]+$/.test(String(t)), Z = () => (t) => a(t) ? !1 : /^[a-zA-Z0-9]+$/.test(String(t)), K = () => (t) => Array.isArray(t), H = () => (t) => a(t) ? !1 : /^[\x20-\x7E]+$/.test(String(t)), h = ({ max: t }) => (e) => a(e) ? !1 : typeof e == "number" ? e <= t : Array.isArray(e) ? e.every((r) => h({ max: t })(r)) : !1, c = ({ min: t }) => (e) => a(e) ? !1 : typeof e == "number" ? e >= t : Array.isArray(e) ? e.every((r) => c({ min: t })(r)) : !1, U = ({ min: t, max: e }) => (r) => a(r) ? !1 : c({ min: t })(r) && h({ max: e })(r), T = ({ length: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? e.length <= t : !1, v = ({ length: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? e.length >= t : !1, J = ({ min: t, max: e }) => (r) => a(r) ? !1 : v({ length: t })(r) && T({ length: e })(r), V = () => (t) => a(t) ? !1 : typeof t == "boolean", w = ({ values: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? t.every((r) => e.includes(r)) : !1, R = ({ values: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? t.some((r) => e.includes(r)) : !1, G = () => (t) => a(t) ? !1 : ["n", "no", "off", "0", "false"].includes(String(t).toLowerCase()), Q = ({ value: t }) => (e) => a(e) ? !1 : e !== t, X = () => (t) => a(t) ? !1 : Array.isArray(t) ? new Set(t).size === t.length : !0, Y = () => (t) => a(t) ? !1 : /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)), ee = () => (t) => a(t) ? !1 : /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)), te = ({ value: t }) => (e) => a(e) ? !1 : String(e).endsWith(t), re = ({ value: t }) => (e) => e === t, ne = () => (t) => t instanceof File, f = ({ size: t }) => (e) => a(e) ? !1 : e instanceof File ? e.size <= t * 1024 : Array.isArray(e) ? e.every((r) => f({ size: t })(r)) : !1, m = ({ size: t }) => (e) => a(e) ? !1 : e instanceof File ? e.size >= t * 1024 : Array.isArray(e) ? e.every((r) => m({ size: t })(r)) : !1, se = ({ min: t, max: e }) => (r) => a(r) ? !1 : m({ size: t })(r) && f({ size: e })(r), L = ({ size: t }) => (e) => {
944
+ if (a(e))
882
945
  return !1;
883
946
  if (e instanceof File) {
884
- const r = t * 1024, i = (t + 1) * 1024;
885
- return e.size >= r && e.size < i;
947
+ const r = t * 1024, s = (t + 1) * 1024;
948
+ return e.size >= r && e.size < s;
886
949
  }
887
- return Array.isArray(e) ? e.every((r) => T({ size: t })(r)) : !1;
888
- }, o = ({ value: t }) => (e) => s(e) ? !1 : String(e).startsWith(t), X = () => (t) => s(t) ? !1 : o({ value: "http://" })(t) || o({ value: "https://" })(t), Y = () => (t) => s(t) ? !1 : o({ value: "https://" })(t), g = () => (t) => s(t) ? !1 : typeof t == "number", ee = () => (t) => s(t) ? !1 : g()(t) && Number.isInteger(t), te = () => (t) => {
889
- if (s(t))
950
+ return Array.isArray(e) ? e.every((r) => L({ size: t })(r)) : !1;
951
+ }, S = ({ value: t }) => (e) => a(e) ? !1 : String(e).startsWith(t), g = ({ values: t }) => (e) => a(e) ? !1 : t.map((r) => `${r}://`).some((r) => S({ value: r })(e)), x = () => (t) => a(t) ? !1 : g({ values: ["http"] })(t), z = () => (t) => a(t) ? !1 : g({ values: ["https"] })(t), ae = () => (t) => a(t) ? !1 : x()(t) || z()(t), y = () => (t) => a(t) ? !1 : typeof t == "number", ie = () => (t) => a(t) ? !1 : y()(t) && Number.isInteger(t), p = ({ includeProtocol: t } = {}) => (e) => {
952
+ if (a(e))
953
+ return !1;
954
+ if (t)
955
+ try {
956
+ const { host: r } = new URL(String(e));
957
+ return p({})(r);
958
+ } catch {
959
+ return !1;
960
+ }
961
+ return /^((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]?)(:\d+)?$/.test(String(e));
962
+ }, d = ({ includeProtocol: t } = {}) => (e) => {
963
+ if (a(e))
964
+ return !1;
965
+ if (t)
966
+ try {
967
+ const { host: r } = new URL(String(e));
968
+ return d({})(r);
969
+ } catch {
970
+ return !1;
971
+ }
972
+ return /(([0-9a-fA-F]{1,4}:){7,7}[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})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(String(e));
973
+ }, le = ({ includeProtocol: t } = {}) => (e) => a(e) ? !1 : p({ includeProtocol: t })(e) || d({ includeProtocol: t })(e), oe = () => (t) => {
974
+ if (a(t))
890
975
  return !1;
891
976
  try {
892
977
  return JSON.parse(String(t)), !0;
893
978
  } catch {
894
979
  return !1;
895
980
  }
896
- }, re = ({ length: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? e.length === t : !1, ne = () => (t) => s(t) ? !1 : String(t) === String(t).toLowerCase(), se = ({ values: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? !t.every((r) => e.includes(r)) : !1, ie = ({ values: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? !t.some((r) => e.includes(r)) : !1, ae = ({ value: t }) => (e) => e !== t, le = ({ values: t }) => (e) => s(e) ? !1 : !t.includes(e), R = () => (t) => typeof t == "string", ue = () => (t) => s(t) ? !1 : R()(t) ? /^-?\d+(\.\d+)?$/.test(String(t)) : g()(t), oe = ({ values: t }) => (e) => s(e) ? !1 : t.includes(e), he = ({ expression: t }) => (e) => {
897
- if (s(e))
981
+ }, ue = ({ length: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? e.length === t : !1, he = () => (t) => a(t) ? !1 : String(t) === String(t).toLowerCase(), ce = ({ values: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? !t.every((r) => e.includes(r)) : !1, fe = ({ values: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? !t.some((r) => e.includes(r)) : !1, me = ({ value: t }) => (e) => e !== t, ge = ({ values: t }) => (e) => a(e) ? !1 : !t.includes(e), F = () => (t) => typeof t == "string", ye = () => (t) => a(t) ? !1 : F()(t) ? /^-?\d+(\.\d+)?$/.test(String(t)) : y()(t), pe = ({ values: t }) => (e) => a(e) ? !1 : t.includes(e), de = ({ expression: t }) => (e) => {
982
+ if (a(e))
898
983
  return !1;
899
984
  if (!(t instanceof RegExp))
900
985
  throw new TypeError("The expression provided is not a valid RegExp.");
901
986
  return t.test(String(e));
902
- }, ce = () => (t) => !s(t), fe = ({ value: t }) => (e) => s(e) ? !1 : Array.isArray(e) ? e.every((r) => r === t) : e === t, L = ({ size: t }) => (e) => s(e) ? !1 : typeof e == "number" ? e === t : Array.isArray(e) ? e.every((r) => L({ size: t })(r)) : !1, y = ({ length: t }) => (e) => s(e) ? !1 : typeof e == "string" ? e.length <= t : Array.isArray(e) ? e.every((r) => y({ length: t })(r)) : !1, d = ({ length: t }) => (e) => s(e) ? !1 : typeof e == "string" ? e.length >= t : Array.isArray(e) ? e.every((r) => d({ length: t })(r)) : !1, me = ({ min: t, max: e }) => (r) => s(r) ? !1 : d({ length: t })(r) && y({ length: e })(r), v = ({ length: t }) => (e) => s(e) ? !1 : typeof e == "string" ? e.length === t : Array.isArray(e) ? e.every((r) => v({ length: t })(r)) : !1, ge = ({ values: t }) => (e) => s(e) ? !1 : typeof e == "string" ? !t.every((r) => e.includes(r)) : !1, ye = ({ values: t }) => (e) => s(e) ? !1 : typeof e == "string" ? !t.some((r) => e.includes(r)) : !1, de = ({ values: t, ignored: e = [] }) => (r) => s(r) ? !1 : (Array.isArray(e) ? e : [e]).some((i) => i === r) ? !0 : !t.some((i) => i === r), $e = () => (t) => s(t) ? !1 : String(t) === String(t).toUpperCase(), pe = () => (t) => s(t) ? !1 : /^(https?):\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}.*$/.test(String(t)), be = {
903
- accepted: M,
904
- alpha: F,
905
- alphaDash: k,
906
- alphaDashDot: O,
907
- alphaNum: B,
908
- array: D,
909
- ascii: E,
910
- between: N,
911
- betweenLength: W,
912
- boolean: Z,
913
- containsAll: p,
914
- containsAny: b,
915
- declined: K,
916
- different: I,
917
- distinct: J,
918
- domain: P,
919
- email: U,
920
- endsWith: V,
921
- equals: G,
922
- file: H,
923
- fileBetweenSize: Q,
987
+ }, $e = () => (t) => !a(t), be = ({ value: t }) => (e) => a(e) ? !1 : Array.isArray(e) ? e.every((r) => r === t) : e === t, C = ({ size: t }) => (e) => a(e) ? !1 : typeof e == "number" ? e === t : Array.isArray(e) ? e.every((r) => C({ size: t })(r)) : !1, $ = ({ length: t }) => (e) => a(e) ? !1 : typeof e == "string" ? e.length <= t : Array.isArray(e) ? e.every((r) => $({ length: t })(r)) : !1, b = ({ length: t }) => (e) => a(e) ? !1 : typeof e == "string" ? e.length >= t : Array.isArray(e) ? e.every((r) => b({ length: t })(r)) : !1, Ae = ({ min: t, max: e }) => (r) => a(r) ? !1 : b({ length: t })(r) && $({ length: e })(r), j = ({ length: t }) => (e) => a(e) ? !1 : typeof e == "string" ? e.length === t : Array.isArray(e) ? e.every((r) => j({ length: t })(r)) : !1, we = ({ values: t }) => (e) => a(e) ? !1 : typeof e == "string" ? !t.every((r) => e.includes(r)) : !1, Re = ({ values: t }) => (e) => a(e) ? !1 : typeof e == "string" ? !t.some((r) => e.includes(r)) : !1, Te = ({ values: t, ignored: e = [] }) => (r) => a(r) ? !1 : (Array.isArray(e) ? e : [e]).some((s) => s === r) ? !0 : !t.some((s) => s === r), ve = () => (t) => a(t) ? !1 : String(t) === String(t).toUpperCase(), Le = () => (t) => {
988
+ if (a(t))
989
+ return !1;
990
+ try {
991
+ return new URL(String(t)), !0;
992
+ } catch {
993
+ return !1;
994
+ }
995
+ }, Se = {
996
+ accepted: P,
997
+ alpha: N,
998
+ alphaDash: W,
999
+ alphaDashDot: I,
1000
+ alphaNum: Z,
1001
+ array: K,
1002
+ ascii: H,
1003
+ between: U,
1004
+ betweenLength: J,
1005
+ boolean: V,
1006
+ containsAll: w,
1007
+ containsAny: R,
1008
+ declined: G,
1009
+ different: Q,
1010
+ distinct: X,
1011
+ domain: Y,
1012
+ email: ee,
1013
+ endsWith: te,
1014
+ equals: re,
1015
+ file: ne,
1016
+ fileBetweenSize: se,
924
1017
  fileMaxSize: f,
925
1018
  fileMinSize: m,
926
- fileSize: T,
927
- http: X,
928
- https: Y,
929
- integer: ee,
930
- json: te,
931
- length: re,
932
- lowercase: ne,
1019
+ fileSize: L,
1020
+ http: x,
1021
+ httpOrHttps: ae,
1022
+ https: z,
1023
+ integer: ie,
1024
+ ip: le,
1025
+ ipv4: p,
1026
+ ipv6: d,
1027
+ json: oe,
1028
+ length: ue,
1029
+ lowercase: he,
933
1030
  max: h,
934
- maxLength: A,
1031
+ maxLength: T,
935
1032
  min: c,
936
- minLength: w,
937
- notContainsAll: se,
938
- notContainsAny: ie,
939
- notEquals: ae,
940
- notOneOf: le,
941
- number: g,
942
- numeric: ue,
943
- oneOf: oe,
944
- regex: he,
945
- required: ce,
946
- same: fe,
947
- size: L,
948
- startsWith: o,
949
- string: R,
950
- stringBetweenLength: me,
951
- stringContainsAll: p,
952
- stringContainsAny: b,
953
- stringLength: v,
954
- stringMaxLength: y,
955
- stringMinLength: d,
956
- stringNotContainsAll: ge,
957
- stringNotContainsAny: ye,
958
- unique: de,
959
- uppercase: $e,
960
- url: pe
1033
+ minLength: v,
1034
+ notContainsAll: ce,
1035
+ notContainsAny: fe,
1036
+ notEquals: me,
1037
+ notOneOf: ge,
1038
+ number: y,
1039
+ numeric: ye,
1040
+ oneOf: pe,
1041
+ protocol: g,
1042
+ regex: de,
1043
+ required: $e,
1044
+ same: be,
1045
+ size: C,
1046
+ startsWith: S,
1047
+ string: F,
1048
+ stringBetweenLength: Ae,
1049
+ stringContainsAll: w,
1050
+ stringContainsAny: R,
1051
+ stringLength: j,
1052
+ stringMaxLength: $,
1053
+ stringMinLength: b,
1054
+ stringNotContainsAll: we,
1055
+ stringNotContainsAny: Re,
1056
+ unique: Te,
1057
+ uppercase: ve,
1058
+ url: Le
961
1059
  };
962
- class we {
1060
+ class ze {
963
1061
  constructor({
964
1062
  fallbackLocale: e,
965
1063
  locale: r,
966
- plugins: i
1064
+ plugins: s
967
1065
  } = {}) {
968
- l(this, "locale", "en");
969
- l(this, "fallbackLocale", "en");
970
- l(this, "locales", {});
971
- l(this, "rules", {});
972
- this.registerLocales(j), this.registerRules(be), e && this.setFallbackLocale(e), r && this.setLocale(r), i && i.forEach((a) => this.registerPlugin(a));
1066
+ o(this, "locale", "en");
1067
+ o(this, "fallbackLocale", "en");
1068
+ o(this, "locales", {});
1069
+ o(this, "rules", {});
1070
+ this.registerLocales(D), this.registerRules(Se), e && this.setFallbackLocale(e), r && this.setLocale(r), s && s.forEach((l) => this.registerPlugin(l));
973
1071
  }
974
1072
  getLocale() {
975
1073
  return this.locale;
@@ -988,7 +1086,7 @@ class we {
988
1086
  return this.fallbackLocale = e, this;
989
1087
  }
990
1088
  defineField(e) {
991
- return new C({
1089
+ return new k({
992
1090
  name: e,
993
1091
  locale: this.locale,
994
1092
  fallbackLocale: this.fallbackLocale,
@@ -998,7 +1096,7 @@ class we {
998
1096
  }
999
1097
  registerLocales(e) {
1000
1098
  return this.locales = Object.keys(e).reduce(
1001
- (r, i) => (r[i] = { ...this.locales[i], ...e[i] }, r),
1099
+ (r, s) => (r[s] = { ...this.locales[s], ...e[s] }, r),
1002
1100
  { ...this.locales }
1003
1101
  ), this;
1004
1102
  }
@@ -1012,6 +1110,6 @@ class we {
1012
1110
  }
1013
1111
  }
1014
1112
  export {
1015
- C as FieldValidator,
1016
- we as FormValidator
1113
+ k as FieldValidator,
1114
+ ze as FormValidator
1017
1115
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(u,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(u=typeof globalThis<"u"?globalThis:u||self,l(u.Fortress={}))})(this,function(u){"use strict";var me=Object.defineProperty;var ge=(u,l,n)=>l in u?me(u,l,{enumerable:!0,configurable:!0,writable:!0,value:n}):u[l]=n;var o=(u,l,n)=>(ge(u,typeof l!="symbol"?l+"":l,n),n);const l=t=>t.replace(/([^\x20-\x7E])([\x20-\x7E])/gu,"$1 $2").replace(/([\x20-\x7E])([^\x20-\x7E])/gu,"$1 $2").replace(/ +/g," "),n=(t,e=0)=>new Intl.NumberFormat(void 0,{minimumFractionDigits:e,maximumFractionDigits:e}).format(t),z=t=>Object.prototype.toString.call(t).toLowerCase().slice(8,-1),s=t=>t==null||t===""||Array.isArray(t)&&t.length<1;class b{constructor({name:e,locale:r,fallbackLocale:i,locales:a,rules:h}){o(this,"name");o(this,"locale");o(this,"fallbackLocale");o(this,"locales");o(this,"rules");o(this,"ruleFunctions",[]);o(this,"conditions",{});o(this,"shouldSkip",!1);this.name=e,this.locale=r,this.fallbackLocale=i,this.locales=a,this.rules=h}get formattedName(){return this.name.toLowerCase()}get messages(){return this.locales[this.locale]||{}}get fallbackMessages(){return this.locales[this.fallbackLocale]||{}}get mandatoryRules(){return[this.required.name,this.string.name,this.array.name,this.equals.name,this.notEquals.name]}getMessage(e){return this.messages[e]||this.fallbackMessages[e]||(r=>`The ${r} field is invalid.`)}getRule(e){if(!(e in this.rules))throw new Error(`The "${e}" rule is not registered.`);return this.rules[e]}buildRuleFunction(e,r){return i=>{if(s(i)&&!this.mandatoryRules.includes(e))return!0;const a=this.getRule(e)(r)(i);return typeof a=="string"?a:a===!0?!0:this.buildRuleFunctionMessage(e,r,i)}}buildRuleFunctionMessage(e,r,i){const a=this.getMessage(e)(this.formattedName,r);if(typeof a=="object"){const h=z(i);if(!(h in a))throw new Error(`The message for the "${e}" rule of the "${h}" type is missing.`);return l(a[h])}return l(a)}pushRuleFunction(e,r){if(e in this.conditions&&!this.conditions[e])return this;const i=this.buildRuleFunction(e,r);return this.ruleFunctions.push(i),this}getRuleFunctions(){return this.ruleFunctions}collect(){return this.shouldSkip?[]:this.getRuleFunctions()}validate(e){if(this.shouldSkip)return!0;for(const r of this.ruleFunctions){const i=r(e);if(typeof i=="string")return i}return!0}apply(e,r={}){return this.pushRuleFunction(e,r)}accepted(){return this.apply(this.accepted.name)}after(e,r,i,a=!0){return this.apply(this.after.name,{date:e,format:r,displayFormat:i,strict:a})}alpha(){return this.apply(this.alpha.name)}alphaDash(){return this.apply(this.alphaDash.name)}alphaDashDot(){return this.apply(this.alphaDashDot.name)}alphaNum(){return this.apply(this.alphaNum.name)}array(){return this.apply(this.array.name)}ascii(){return this.apply(this.ascii.name)}before(e,r,i,a=!0){return this.apply(this.before.name,{date:e,format:r,displayFormat:i,strict:a})}between(e,r){return this.apply(this.between.name,{min:e,max:r})}betweenLength(e,r){return this.apply(this.betweenLength.name,{min:e,max:r})}boolean(){return this.apply(this.boolean.name)}containsAll(e){return this.apply(this.containsAll.name,{values:e})}containsAny(e){return this.apply(this.containsAny.name,{values:e})}date(e,r=!0){return this.apply(this.date.name,{format:e,strict:r})}declined(){return this.apply(this.declined.name)}different(e,r){return this.apply(this.different.name,{field:e,value:r})}distinct(){return this.apply(this.distinct.name)}domain(){return this.apply(this.domain.name)}email(){return this.apply(this.email.name)}endsWith(e){return this.apply(this.endsWith.name,{value:e})}equals(e){return this.apply(this.equals.name,{value:e})}file(){return this.apply(this.file.name)}fileBetweenSize(e,r){return this.apply(this.fileBetweenSize.name,{min:e,max:r})}fileMaxSize(e){return this.apply(this.fileMaxSize.name,{size:e})}fileMinSize(e){return this.apply(this.fileMinSize.name,{size:e})}fileSize(e){return this.apply(this.fileSize.name,{size:e})}http(){return this.apply(this.http.name)}https(){return this.apply(this.https.name)}integer(){return this.apply(this.integer.name)}iso8601(){return this.apply(this.iso8601.name)}json(){return this.apply(this.json.name)}jsonSchema(e){return this.apply(this.jsonSchema.name,{schema:e,locale:this.locale,field:this.name})}length(e){return this.apply(this.length.name,{length:e})}lowercase(){return this.apply(this.lowercase.name)}max(e){return this.apply(this.max.name,{max:e})}maxLength(e){return this.apply(this.maxLength.name,{length:e})}min(e){return this.apply(this.min.name,{min:e})}minLength(e){return this.apply(this.minLength.name,{length:e})}notContainsAll(e){return this.apply(this.notContainsAll.name,{values:e})}notContainsAny(e){return this.apply(this.notContainsAny.name,{values:e})}notEquals(e){return this.apply(this.notEquals.name,{value:e})}notOneOf(e){return this.apply(this.notOneOf.name,{values:e})}number(){return this.apply(this.number.name)}numeric(){return this.apply(this.numeric.name)}oneOf(e){return this.apply(this.oneOf.name,{values:e})}regex(e){return this.apply(this.regex.name,{expression:e})}required(){return this.apply(this.required.name)}requiredWhen(e){return this.when({required:e}).required()}same(e,r){return this.apply(this.same.name,{field:e,value:r})}size(e){return this.apply(this.size.name,{size:e})}startsWith(e){return this.apply(this.startsWith.name,{value:e})}string(){return this.apply(this.string.name)}stringBetweenLength(e,r){return this.apply(this.stringBetweenLength.name,{min:e,max:r})}stringContainsAll(e){return this.apply(this.stringContainsAll.name,{values:e})}stringContainsAny(e){return this.apply(this.stringContainsAny.name,{values:e})}stringLength(e){return this.apply(this.stringLength.name,{length:e})}stringMaxLength(e){return this.apply(this.stringMaxLength.name,{length:e})}stringMinLength(e){return this.apply(this.stringMinLength.name,{length:e})}stringNotContainsAll(e){return this.apply(this.stringNotContainsAll.name,{values:e})}stringNotContainsAny(e){return this.apply(this.stringNotContainsAny.name,{values:e})}unique(e,r=[]){return this.apply(this.unique.name,{values:e,ignored:r})}uppercase(){return this.apply(this.uppercase.name)}url(){return this.apply(this.url.name)}when(e){return typeof e=="object"?(this.conditions=e,this):(e||(this.shouldSkip=!0),this)}}const C={en:{accepted:t=>`The ${t} field must be accepted.`,alpha:t=>`The ${t} field must only contain letters.`,alphaDash:t=>`The ${t} field must only contain letters, numbers, dashes and underscores.`,alphaDashDot:t=>`The ${t} field must only contain letters, numbers, dashes, underscores and dots.`,alphaNum:t=>`The ${t} field must only contain letters and numbers.`,array:t=>`The ${t} field must be an array.`,ascii:t=>`The ${t} field must only contain ASCII characters and symbols.`,between:(t,e)=>{const{min:r,max:i}=e;return{number:`The ${t} field must be between ${n(r)} and ${n(i)}.`,array:`The ${t} field must contain items where each item is between ${n(r)} and ${n(i)}.`}},betweenLength:(t,e)=>{const{min:r,max:i}=e;return`The ${t} field must be between ${n(r)} and ${n(i)} items.`},boolean:t=>`The ${t} field must be a boolean value.`,containsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must contain all of the following values: ${r.join(", ")}.`},containsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must contain at least one of the following values: ${r.join(", ")}.`},declined:t=>`The ${t} field must be declined.`,different:(t,e)=>{const{field:r}=e;return`The ${t} and ${r} fields must be different.`},distinct:t=>`The ${t} field must not contain duplicate values.`,domain:t=>`The ${t} field must be a valid domain.`,email:t=>`The ${t} field must be a valid email address.`,endsWith:(t,e)=>{const{value:r}=e;return`The ${t} field must end with ${r}.`},equals:(t,e)=>{const{value:r}=e;return`The ${t} field must be equal to ${r}.`},file:t=>`The ${t} field must be a file.`,fileBetweenSize:(t,e)=>{const{min:r,max:i}=e;return{file:`The ${t} field must be between ${n(r)} and ${n(i)} kilobytes.`,array:`The ${t} field must contain items where each item is between ${n(r)} and ${n(i)} kilobytes.`}},fileMaxSize:(t,e)=>{const{size:r}=e;return{file:`The ${t} field must not be greater than ${n(r)} kilobytes.`,array:`The ${t} field must contain items where each item is not greater than ${n(r)} kilobytes.`}},fileMinSize:(t,e)=>{const{size:r}=e;return{file:`The ${t} field must be at least ${n(r)} kilobytes.`,array:`The ${t} field must contain items where each item is at least ${n(r)} kilobytes.`}},fileSize:(t,e)=>{const{size:r}=e;return{file:`The ${t} field must be ${n(r)} kilobytes.`,array:`The ${t} field must contain items where each item is ${n(r)} kilobytes.`}},http:t=>`The ${t} field must start with either "http://" or "https://".`,https:t=>`The ${t} field must start with "http://".`,integer:t=>`The ${t} field must be an integer.`,json:t=>`The ${t} field must be a valid JSON string.`,length:(t,e)=>{const{length:r}=e;return`The ${t} field must be ${n(r)} items.`},lowercase:t=>`The ${t} field must be lowercase.`,max:(t,e)=>{const{max:r}=e;return{number:`The ${t} field must not be greater than ${n(r)}.`,array:`The ${t} field must contain items where each item is not greater than ${n(r)}.`}},maxLength:(t,e)=>{const{length:r}=e;return`The ${t} field must not be greater than ${n(r)} items.`},min:(t,e)=>{const{min:r}=e;return{number:`The ${t} field must be at least ${n(r)}.`,array:`The ${t} field must contain items where each item is at least ${n(r)}.`}},minLength:(t,e)=>{const{length:r}=e;return`The ${t} field must be at least ${n(r)} items.`},notContainsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain all of the following values together: ${r.join(", ")}.`},notContainsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain any of the following values: ${r.join(", ")}.`},notEquals:(t,e)=>{const{value:r}=e;return`The ${t} field must not be equal to ${r}.`},notOneOf:(t,e)=>{const{values:r}=e;return`The ${t} field must not be one of the following values: ${r.join(", ")}.`},number:t=>`The ${t} field must be a number.`,numeric:t=>`The ${t} field must be a number.`,oneOf:(t,e)=>{const{values:r}=e;return`The ${t} field must be one of the following values: ${r.join(", ")}.`},regex:t=>`The ${t} field must match the required format.`,required:t=>`The ${t} field is required.`,same:(t,e)=>{const{field:r}=e;return`The ${t} and ${r} fields must match.`},size:(t,e)=>{const{size:r}=e;return{number:`The ${t} field must be ${n(r)}.`,array:`The ${t} field must contain items where each item is ${n(r)}.`}},startsWith:(t,e)=>{const{value:r}=e;return`The ${t} field must start with ${r}.`},string:t=>`The ${t} field must be a string.`,stringBetweenLength:(t,e)=>{const{min:r,max:i}=e;return{string:`The ${t} field must be between ${n(r)} and ${n(i)} characters.`,array:`The ${t} field must contain items where each item is between ${n(r)} and ${n(i)} characters.`}},stringContainsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must contain all of the following text: ${r.join(", ")}.`},stringContainsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must contain at least one of the following text: ${r.join(", ")}.`},stringLength:(t,e)=>{const{length:r}=e;return{string:`The ${t} field must be ${n(r)} characters.`,array:`The ${t} field must contain items where each item is ${n(r)} characters.`}},stringMaxLength:(t,e)=>{const{length:r}=e;return{string:`The ${t} field must not be greater than ${n(r)} characters.`,array:`The ${t} field must contain items where each item is not greater than ${n(r)} characters.`}},stringMinLength:(t,e)=>{const{length:r}=e;return{string:`The ${t} field must be at least ${n(r)} characters.`,array:`The ${t} field must contain items where each item is at least ${n(r)} characters.`}},stringNotContainsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain all of the following text together: ${r.join(", ")}.`},stringNotContainsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain any of the following text: ${r.join(", ")}.`},unique:t=>`The ${t} field has already been taken.`,uppercase:t=>`The ${t} field must be uppercase.`,url:t=>`The ${t} field must be a valid URL.`},"zh-TW":{accepted:()=>"此欄位必須被同意",alpha:()=>"此欄位只能包含字母",alphaDash:()=>"此欄位只能包含字母、數字、連接號和底線",alphaDashDot:()=>"此欄位只能包含字母、數字、連接號、底線和點",alphaNum:()=>"此欄位只能包含字母和數字",array:()=>"此欄位必須是一個陣列",ascii:()=>"此欄位只能包含ASCII字元和符號",between:(t,e)=>{const{min:r,max:i}=e;return{number:`此欄位必須介於${n(r)}到${n(i)}`,array:`此欄位中的每個項目都必須介於${n(r)}到${n(i)}`}},betweenLength:(t,e)=>{const{min:r,max:i}=e;return`此欄位必須介於${n(r)}到${n(i)}個項目之間`},boolean:()=>"此欄位必須是一個布林值",containsAll:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下所有項目:${r.join(", ")}`},containsAny:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下其中一個項目:${r.join(", ")}`},declined:()=>"此欄位必須被拒絕",different:(t,e)=>{const{field:r}=e;return`此欄位必須和${r}欄位不同`},distinct:()=>"此欄位不能包含重複的值",domain:()=>"此欄位必須是有效的網域",email:()=>"此欄位必須是有效的電子郵件地址",endsWith:(t,e)=>{const{value:r}=e;return`此欄位必須以${r}結尾`},equals:(t,e)=>{const{value:r}=e;return`此欄位必須是${r}`},file:()=>"此欄位必須是檔案",fileBetweenSize:(t,e)=>{const{min:r,max:i}=e;return{file:`此欄位必須介於${n(r)}到${n(i)} KB之間`,array:`此欄位中的每個項目都必須介於${n(r)}到${n(i)} KB之間`}},fileMaxSize:(t,e)=>{const{size:r}=e;return{file:`此欄位不能大於${n(r)} KB`,array:`此欄位中的每個項目都不能大於${n(r)} KB`}},fileMinSize:(t,e)=>{const{size:r}=e;return{file:`此欄位不能小於${n(r)} KB`,array:`此欄位中的每個項目都不能小於${n(r)} KB`}},fileSize:(t,e)=>{const{size:r}=e;return{file:`此欄位必須是${n(r)} KB`,array:`此欄位中的每個項目都必須是${n(r)} KB`}},http:()=>"此欄位必須以 http:// 或 https:// 開頭",https:()=>"此欄位必須以 https:// 開頭",integer:()=>"此欄位必須是整數",json:()=>"此欄位必須是有效的 JSON 字串",length:(t,e)=>{const{length:r}=e;return`此欄位必須包含${n(r)}個項目`},lowercase:()=>"此欄位必須是小寫",max:(t,e)=>{const{max:r}=e;return{number:`此欄位不能大於${n(r)}`,array:`此欄位中的每個項目都不能大於${n(r)}`}},maxLength:(t,e)=>{const{length:r}=e;return`此欄位不能大於${n(r)}個項目`},min:(t,e)=>{const{min:r}=e;return{number:`此欄位不能小於${n(r)}`,array:`此欄位中的每個項目都不能小於${n(r)}`}},minLength:(t,e)=>{const{length:r}=e;return`此欄位不能小於${n(r)}個項目`},notContainsAll:(t,e)=>{const{values:r}=e;return`此欄位不能同時包含以下所有值:${r.join(", ")}`},notContainsAny:(t,e)=>{const{values:r}=e;return`此欄位不能包含以下任何值:${r.join(", ")}`},notEquals:(t,e)=>{const{value:r}=e;return`此欄位不能是${r}`},notOneOf:(t,e)=>{const{values:r}=e;return`此欄位不能是以下任何值:${r.join(", ")}`},number:()=>"此欄位必須是數字",numeric:()=>"此欄位必須是數字",oneOf:(t,e)=>{const{values:r}=e;return`此欄位必須是以下任何值:${r.join(", ")}`},regex:()=>"此欄位必須符合所需的格式",required:()=>"此欄位為必填",same:(t,e)=>{const{field:r}=e;return`此欄位必須與${r}欄位相同`},size:(t,e)=>{const{size:r}=e;return{number:`此欄位必須是${n(r)}`,array:`此欄位中的每個項目都必須是${n(r)}`}},startsWith:(t,e)=>{const{value:r}=e;return`此欄位必須以${r}開頭`},string:()=>"此欄位必須是字串",stringBetweenLength:(t,e)=>{const{min:r,max:i}=e;return{string:`此欄位必須介於${n(r)}到${n(i)}個字元之間`,array:`此欄位中的每個項目都必須介於${n(r)}到${n(i)}個字元之間`}},stringContainsAll:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下所有文字:${r.join(", ")}`},stringContainsAny:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下其中一個文字:${r.join(", ")}`},stringLength:(t,e)=>{const{length:r}=e;return{string:`此欄位必須是${n(r)}個字元`,array:`此欄位中的每個項目都必須是${n(r)}個字元`}},stringMaxLength:(t,e)=>{const{length:r}=e;return{string:`此欄位不能大於${n(r)}個字元`,array:`此欄位中的每個項目都不能大於${n(r)}個字元`}},stringMinLength:(t,e)=>{const{length:r}=e;return{string:`此欄位不能小於${n(r)}個字元`,array:`此欄位中的每個項目都不能小於${n(r)}個字元`}},stringNotContainsAll:(t,e)=>{const{values:r}=e;return`此欄位不能同時包含以下所有文字:${r.join(", ")}`},stringNotContainsAny:(t,e)=>{const{values:r}=e;return`此欄位不能包含以下任何文字:${r.join(", ")}`},unique:()=>"此欄位已經存在",uppercase:()=>"此欄位必須是大寫",url:()=>"此欄位必須是有效的網址"}},_=()=>t=>s(t)?!1:["y","yes","on","1","true"].includes(String(t).toLowerCase()),j=()=>t=>s(t)?!1:/^[a-zA-Z]+$/.test(String(t)),q=()=>t=>s(t)?!1:/^[a-zA-Z0-9-_]+$/.test(String(t)),M=()=>t=>s(t)?!1:/^[a-zA-Z0-9-_.]+$/.test(String(t)),F=()=>t=>s(t)?!1:/^[a-zA-Z0-9]+$/.test(String(t)),k=()=>t=>Array.isArray(t),O=()=>t=>s(t)?!1:/^[\x20-\x7E]+$/.test(String(t)),c=({max:t})=>e=>s(e)?!1:typeof e=="number"?e<=t:Array.isArray(e)?e.every(r=>c({max:t})(r)):!1,m=({min:t})=>e=>s(e)?!1:typeof e=="number"?e>=t:Array.isArray(e)?e.every(r=>m({min:t})(r)):!1,B=({min:t,max:e})=>r=>s(r)?!1:m({min:t})(r)&&c({max:e})(r),A=({length:t})=>e=>s(e)?!1:Array.isArray(e)?e.length<=t:!1,w=({length:t})=>e=>s(e)?!1:Array.isArray(e)?e.length>=t:!1,D=({min:t,max:e})=>r=>s(r)?!1:w({length:t})(r)&&A({length:e})(r),E=()=>t=>s(t)?!1:typeof t=="boolean",T=({values:t})=>e=>s(e)?!1:Array.isArray(e)?t.every(r=>e.includes(r)):!1,R=({values:t})=>e=>s(e)?!1:Array.isArray(e)?t.some(r=>e.includes(r)):!1,W=()=>t=>s(t)?!1:["n","no","off","0","false"].includes(String(t).toLowerCase()),N=({value:t})=>e=>s(e)?!1:e!==t,Z=()=>t=>s(t)?!1:Array.isArray(t)?new Set(t).size===t.length:!0,K=()=>t=>s(t)?!1:/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)),I=()=>t=>s(t)?!1:/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)),V=({value:t})=>e=>s(e)?!1:String(e).endsWith(t),J=({value:t})=>e=>e===t,P=()=>t=>t instanceof File,g=({size:t})=>e=>s(e)?!1:e instanceof File?e.size<=t*1024:Array.isArray(e)?e.every(r=>g({size:t})(r)):!1,y=({size:t})=>e=>s(e)?!1:e instanceof File?e.size>=t*1024:Array.isArray(e)?e.every(r=>y({size:t})(r)):!1,U=({min:t,max:e})=>r=>s(r)?!1:y({size:t})(r)&&g({size:e})(r),L=({size:t})=>e=>{if(s(e))return!1;if(e instanceof File){const r=t*1024,i=(t+1)*1024;return e.size>=r&&e.size<i}return Array.isArray(e)?e.every(r=>L({size:t})(r)):!1},f=({value:t})=>e=>s(e)?!1:String(e).startsWith(t),G=()=>t=>s(t)?!1:f({value:"http://"})(t)||f({value:"https://"})(t),H=()=>t=>s(t)?!1:f({value:"https://"})(t),d=()=>t=>s(t)?!1:typeof t=="number",Q=()=>t=>s(t)?!1:d()(t)&&Number.isInteger(t),X=()=>t=>{if(s(t))return!1;try{return JSON.parse(String(t)),!0}catch{return!1}},Y=({length:t})=>e=>s(e)?!1:Array.isArray(e)?e.length===t:!1,ee=()=>t=>s(t)?!1:String(t)===String(t).toLowerCase(),te=({values:t})=>e=>s(e)?!1:Array.isArray(e)?!t.every(r=>e.includes(r)):!1,re=({values:t})=>e=>s(e)?!1:Array.isArray(e)?!t.some(r=>e.includes(r)):!1,ne=({value:t})=>e=>e!==t,se=({values:t})=>e=>s(e)?!1:!t.includes(e),v=()=>t=>typeof t=="string",ie=()=>t=>s(t)?!1:v()(t)?/^-?\d+(\.\d+)?$/.test(String(t)):d()(t),ae=({values:t})=>e=>s(e)?!1:t.includes(e),le=({expression:t})=>e=>{if(s(e))return!1;if(!(t instanceof RegExp))throw new TypeError("The expression provided is not a valid RegExp.");return t.test(String(e))},ue=()=>t=>!s(t),oe=({value:t})=>e=>s(e)?!1:Array.isArray(e)?e.every(r=>r===t):e===t,S=({size:t})=>e=>s(e)?!1:typeof e=="number"?e===t:Array.isArray(e)?e.every(r=>S({size:t})(r)):!1,$=({length:t})=>e=>s(e)?!1:typeof e=="string"?e.length<=t:Array.isArray(e)?e.every(r=>$({length:t})(r)):!1,p=({length:t})=>e=>s(e)?!1:typeof e=="string"?e.length>=t:Array.isArray(e)?e.every(r=>p({length:t})(r)):!1,he=({min:t,max:e})=>r=>s(r)?!1:p({length:t})(r)&&$({length:e})(r),x=({length:t})=>e=>s(e)?!1:typeof e=="string"?e.length===t:Array.isArray(e)?e.every(r=>x({length:t})(r)):!1,fe={accepted:_,alpha:j,alphaDash:q,alphaDashDot:M,alphaNum:F,array:k,ascii:O,between:B,betweenLength:D,boolean:E,containsAll:T,containsAny:R,declined:W,different:N,distinct:Z,domain:K,email:I,endsWith:V,equals:J,file:P,fileBetweenSize:U,fileMaxSize:g,fileMinSize:y,fileSize:L,http:G,https:H,integer:Q,json:X,length:Y,lowercase:ee,max:c,maxLength:A,min:m,minLength:w,notContainsAll:te,notContainsAny:re,notEquals:ne,notOneOf:se,number:d,numeric:ie,oneOf:ae,regex:le,required:ue,same:oe,size:S,startsWith:f,string:v,stringBetweenLength:he,stringContainsAll:T,stringContainsAny:R,stringLength:x,stringMaxLength:$,stringMinLength:p,stringNotContainsAll:({values:t})=>e=>s(e)?!1:typeof e=="string"?!t.every(r=>e.includes(r)):!1,stringNotContainsAny:({values:t})=>e=>s(e)?!1:typeof e=="string"?!t.some(r=>e.includes(r)):!1,unique:({values:t,ignored:e=[]})=>r=>s(r)?!1:(Array.isArray(e)?e:[e]).some(i=>i===r)?!0:!t.some(i=>i===r),uppercase:()=>t=>s(t)?!1:String(t)===String(t).toUpperCase(),url:()=>t=>s(t)?!1:/^(https?):\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}.*$/.test(String(t))};class ce{constructor({fallbackLocale:e,locale:r,plugins:i}={}){o(this,"locale","en");o(this,"fallbackLocale","en");o(this,"locales",{});o(this,"rules",{});this.registerLocales(C),this.registerRules(fe),e&&this.setFallbackLocale(e),r&&this.setLocale(r),i&&i.forEach(a=>this.registerPlugin(a))}getLocale(){return this.locale}getFallbackLocale(){return this.fallbackLocale}setLocale(e){if(!(e in this.locales))throw new Error(`The "${e}" locale is not registered.`);return this.locale=e,this}setFallbackLocale(e){if(!(e in this.locales))throw new Error(`The "${e}" fallback locale is not registered.`);return this.fallbackLocale=e,this}defineField(e){return new b({name:e,locale:this.locale,fallbackLocale:this.fallbackLocale,locales:this.locales,rules:this.rules})}registerLocales(e){return this.locales=Object.keys(e).reduce((r,i)=>(r[i]={...this.locales[i],...e[i]},r),{...this.locales}),this}registerRules(e){return this.rules={...this.rules,...e},this}registerPlugin(e){if(!e||!e.locales||!e.rules)throw new Error('The plugin must have "locales" and "rules" properties.');return this.registerLocales(e.locales).registerRules(e.rules)}}u.FieldValidator=b,u.FormValidator=ce,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})});
1
+ (function(h,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(h=typeof globalThis<"u"?globalThis:h||self,o(h.Fortress={}))})(this,function(h){"use strict";var Ae=Object.defineProperty;var we=(h,o,n)=>o in h?Ae(h,o,{enumerable:!0,configurable:!0,writable:!0,value:n}):h[o]=n;var u=(h,o,n)=>(we(h,typeof o!="symbol"?o+"":o,n),n);const o=t=>{const e=",、;:!?。()《》「」『』【】〔〕",r="\\x20-\\x7E",s=new RegExp(`\\s*([${e}])`,"gu"),l=new RegExp(`([${e}])\\s*([${r}])`,"gu"),f=new RegExp(`([^${r}${e}])([${r}])`,"gu"),be=new RegExp(`([${r}])([^${r}${e}])`,"gu");return t.replace(s,"$1").replace(l,"$1$2").replace(f,"$1 $2").replace(be,"$1 $2").replace(/ +/g," ")},n=(t,e=0)=>new Intl.NumberFormat(void 0,{minimumFractionDigits:e,maximumFractionDigits:e}).format(t),O=t=>Object.prototype.toString.call(t).toLowerCase().slice(8,-1),a=t=>t==null||t===""||Array.isArray(t)&&t.length<1,i=t=>`"${t}"`;class R{constructor({name:e,locale:r,fallbackLocale:s,locales:l,rules:f}){u(this,"name");u(this,"locale");u(this,"fallbackLocale");u(this,"locales");u(this,"rules");u(this,"ruleFunctions",{});u(this,"conditions",{});u(this,"shouldSkip",!1);u(this,"appliedProtocols",{});this.name=e,this.locale=r,this.fallbackLocale=s,this.locales=l,this.rules=f}get formattedName(){return this.name.toLowerCase()}get messages(){return this.locales[this.locale]||{}}get fallbackMessages(){return this.locales[this.fallbackLocale]||{}}get mandatoryRules(){return[this.required.name,this.string.name,this.array.name,this.equals.name,this.notEquals.name]}getMessage(e){return this.messages[e]||this.fallbackMessages[e]||(r=>`The ${r} field is invalid.`)}getRule(e){if(!(e in this.rules))throw new Error(`The "${e}" rule is not registered.`);return this.rules[e]}buildRuleFunction(e,r){return s=>{if(a(s)&&!this.mandatoryRules.includes(e))return!0;const l=this.getRule(e)(r)(s);return typeof l=="string"?l:l===!0?!0:this.buildRuleFunctionMessage(e,r,s)}}buildRuleFunctionMessage(e,r,s){const l=this.getMessage(e)(this.formattedName,r);if(typeof l=="object"){const f=O(s);if(!(f in l))throw new Error(`The message for the "${e}" rule of the "${f}" type is missing.`);return o(l[f])}return o(l)}pushRuleFunction(e,r){if(e in this.conditions&&!this.conditions[e])return this;const s=this.buildRuleFunction(e,r);return this.ruleFunctions[e]=s,this}getRuleFunctions(){return Object.values(this.ruleFunctions)}collect(){return this.shouldSkip?[]:this.getRuleFunctions()}validate(e){if(this.shouldSkip)return!0;const r=this.getRuleFunctions();for(const s of r){const l=s(e);if(typeof l=="string")return l}return!0}apply(e,r={}){return this.pushRuleFunction(e,r)}accepted(){return this.apply(this.accepted.name)}after(e,r,s,l=!0){return this.apply(this.after.name,{date:e,format:r,displayFormat:s,strict:l})}alpha(){return this.apply(this.alpha.name)}alphaDash(){return this.apply(this.alphaDash.name)}alphaDashDot(){return this.apply(this.alphaDashDot.name)}alphaNum(){return this.apply(this.alphaNum.name)}array(){return this.apply(this.array.name)}ascii(){return this.apply(this.ascii.name)}before(e,r,s,l=!0){return this.apply(this.before.name,{date:e,format:r,displayFormat:s,strict:l})}between(e,r){return this.apply(this.between.name,{min:e,max:r})}betweenLength(e,r){return this.apply(this.betweenLength.name,{min:e,max:r})}boolean(){return this.apply(this.boolean.name)}containsAll(e){return this.apply(this.containsAll.name,{values:e})}containsAny(e){return this.apply(this.containsAny.name,{values:e})}date(e,r=!0){return this.apply(this.date.name,{format:e,strict:r})}declined(){return this.apply(this.declined.name)}different(e,r){return this.apply(this.different.name,{field:e,value:r})}distinct(){return this.apply(this.distinct.name)}domain(){return this.apply(this.domain.name)}email(){return this.apply(this.email.name)}endsWith(e){return this.apply(this.endsWith.name,{value:e})}equals(e){return this.apply(this.equals.name,{value:e})}file(){return this.apply(this.file.name)}fileBetweenSize(e,r){return this.apply(this.fileBetweenSize.name,{min:e,max:r})}fileMaxSize(e){return this.apply(this.fileMaxSize.name,{size:e})}fileMinSize(e){return this.apply(this.fileMinSize.name,{size:e})}fileSize(e){return this.apply(this.fileSize.name,{size:e})}http(){return this.appliedProtocols[this.http.name]=!0,this.apply(this.http.name)}httpOrHttps(){return this.appliedProtocols[this.http.name]=!0,this.appliedProtocols[this.https.name]=!0,this.apply(this.httpOrHttps.name)}https(){return this.appliedProtocols[this.https.name]=!0,this.apply(this.https.name)}integer(){return this.apply(this.integer.name)}ip(){const e=Object.keys(this.appliedProtocols).length>0;return this.apply(this.ip.name,{includeProtocol:e})}ipv4(){const e=Object.keys(this.appliedProtocols).length>0;return this.apply(this.ipv4.name,{includeProtocol:e})}ipv6(){const e=Object.keys(this.appliedProtocols).length>0;return this.apply(this.ipv6.name,{includeProtocol:e})}iso8601(){return this.apply(this.iso8601.name)}json(){return this.apply(this.json.name)}jsonSchema(e){return this.apply(this.jsonSchema.name,{schema:e,locale:this.locale,field:this.name})}length(e){return this.apply(this.length.name,{length:e})}lowercase(){return this.apply(this.lowercase.name)}max(e){return this.apply(this.max.name,{max:e})}maxLength(e){return this.apply(this.maxLength.name,{length:e})}min(e){return this.apply(this.min.name,{min:e})}minLength(e){return this.apply(this.minLength.name,{length:e})}notContainsAll(e){return this.apply(this.notContainsAll.name,{values:e})}notContainsAny(e){return this.apply(this.notContainsAny.name,{values:e})}notEquals(e){return this.apply(this.notEquals.name,{value:e})}notOneOf(e){return this.apply(this.notOneOf.name,{values:e})}protocol(e){const r=Array.isArray(e)?e:[e];return r.forEach(s=>this.appliedProtocols[s]=!0),this.apply(this.protocol.name,{values:r})}number(){return this.apply(this.number.name)}numeric(){return this.apply(this.numeric.name)}oneOf(e){return this.apply(this.oneOf.name,{values:e})}regex(e){return this.apply(this.regex.name,{expression:e})}required(){return this.apply(this.required.name)}requiredWhen(e){return this.when({required:e}).required()}same(e,r){return this.apply(this.same.name,{field:e,value:r})}size(e){return this.apply(this.size.name,{size:e})}startsWith(e){return this.apply(this.startsWith.name,{value:e})}string(){return this.apply(this.string.name)}stringBetweenLength(e,r){return this.apply(this.stringBetweenLength.name,{min:e,max:r})}stringContainsAll(e){return this.apply(this.stringContainsAll.name,{values:e})}stringContainsAny(e){return this.apply(this.stringContainsAny.name,{values:e})}stringLength(e){return this.apply(this.stringLength.name,{length:e})}stringMaxLength(e){return this.apply(this.stringMaxLength.name,{length:e})}stringMinLength(e){return this.apply(this.stringMinLength.name,{length:e})}stringNotContainsAll(e){return this.apply(this.stringNotContainsAll.name,{values:e})}stringNotContainsAny(e){return this.apply(this.stringNotContainsAny.name,{values:e})}unique(e,r=[]){return this.apply(this.unique.name,{values:e,ignored:r})}uppercase(){return this.apply(this.uppercase.name)}url(){return this.apply(this.url.name)}when(e){return typeof e=="object"?(this.conditions=e,this):(e||(this.shouldSkip=!0),this)}}const M={en:{accepted:t=>`The ${t} field must be accepted.`,alpha:t=>`The ${t} field must only contain letters.`,alphaDash:t=>`The ${t} field must only contain letters, numbers, dashes and underscores.`,alphaDashDot:t=>`The ${t} field must only contain letters, numbers, dashes, underscores and dots.`,alphaNum:t=>`The ${t} field must only contain letters and numbers.`,array:t=>`The ${t} field must be an array.`,ascii:t=>`The ${t} field must only contain ASCII characters and symbols.`,between:(t,e)=>{const{min:r,max:s}=e;return{number:`The ${t} field must be between ${n(r)} and ${n(s)}.`,array:`The ${t} field must contain items where each item is between ${n(r)} and ${n(s)}.`}},betweenLength:(t,e)=>{const{min:r,max:s}=e;return`The ${t} field must be between ${n(r)} and ${n(s)} items.`},boolean:t=>`The ${t} field must be a boolean value.`,containsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must contain all of the following values: ${r.map(i).join(", ")}.`},containsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must contain at least one of the following values: ${r.map(i).join(", ")}.`},declined:t=>`The ${t} field must be declined.`,different:(t,e)=>{const{field:r}=e;return`The ${t} and ${r} fields must be different.`},distinct:t=>`The ${t} field must not contain duplicate values.`,domain:t=>`The ${t} field must be a valid domain.`,email:t=>`The ${t} field must be a valid email address.`,endsWith:(t,e)=>{const{value:r}=e;return`The ${t} field must end with ${r}.`},equals:(t,e)=>{const{value:r}=e;return`The ${t} field must be equal to ${r}.`},file:t=>`The ${t} field must be a file.`,fileBetweenSize:(t,e)=>{const{min:r,max:s}=e;return{file:`The ${t} field must be between ${n(r)} and ${n(s)} kilobytes.`,array:`The ${t} field must contain items where each item is between ${n(r)} and ${n(s)} kilobytes.`}},fileMaxSize:(t,e)=>{const{size:r}=e;return{file:`The ${t} field must not be greater than ${n(r)} kilobytes.`,array:`The ${t} field must contain items where each item is not greater than ${n(r)} kilobytes.`}},fileMinSize:(t,e)=>{const{size:r}=e;return{file:`The ${t} field must be at least ${n(r)} kilobytes.`,array:`The ${t} field must contain items where each item is at least ${n(r)} kilobytes.`}},fileSize:(t,e)=>{const{size:r}=e;return{file:`The ${t} field must be ${n(r)} kilobytes.`,array:`The ${t} field must contain items where each item is ${n(r)} kilobytes.`}},http:t=>`The ${t} field must start with the "http://" protocol.`,httpOrHttps:t=>`The ${t} field must start with the "http://" or "https://" protocols.`,https:t=>`The ${t} field must start with the "https://" protocol.`,integer:t=>`The ${t} field must be an integer.`,ip:t=>`The ${t} field must be a valid IP address.`,ipv4:t=>`The ${t} field must be a valid IPv4 address.`,ipv6:t=>`The ${t} field must be a valid IPv6 address.`,json:t=>`The ${t} field must be a valid JSON string.`,length:(t,e)=>{const{length:r}=e;return`The ${t} field must be ${n(r)} items.`},lowercase:t=>`The ${t} field must be lowercase.`,max:(t,e)=>{const{max:r}=e;return{number:`The ${t} field must not be greater than ${n(r)}.`,array:`The ${t} field must contain items where each item is not greater than ${n(r)}.`}},maxLength:(t,e)=>{const{length:r}=e;return`The ${t} field must not be greater than ${n(r)} items.`},min:(t,e)=>{const{min:r}=e;return{number:`The ${t} field must be at least ${n(r)}.`,array:`The ${t} field must contain items where each item is at least ${n(r)}.`}},minLength:(t,e)=>{const{length:r}=e;return`The ${t} field must be at least ${n(r)} items.`},notContainsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain all of the following values together: ${r.map(i).join(", ")}.`},notContainsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain any of the following values: ${r.map(i).join(", ")}.`},notEquals:(t,e)=>{const{value:r}=e;return`The ${t} field must not be equal to ${r}.`},notOneOf:(t,e)=>{const{values:r}=e;return`The ${t} field must not be one of the following values: ${r.map(i).join(", ")}.`},number:t=>`The ${t} field must be a number.`,numeric:t=>`The ${t} field must be a number.`,oneOf:(t,e)=>{const{values:r}=e;return`The ${t} field must be one of the following values: ${r.map(i).join(", ")}.`},protocol:(t,e)=>{const{values:r}=e;if(r.length===1){const[s]=r;return`The ${t} field must start with the "${s}://" protocol.`}return`The ${t} field must start with one of the following protocols: ${r.map(s=>`${s}://`).map(i).join(", ")}.`},regex:t=>`The ${t} field must match the required format.`,required:t=>`The ${t} field is required.`,same:(t,e)=>{const{field:r}=e;return`The ${t} and ${r} fields must match.`},size:(t,e)=>{const{size:r}=e;return{number:`The ${t} field must be ${n(r)}.`,array:`The ${t} field must contain items where each item is ${n(r)}.`}},startsWith:(t,e)=>{const{value:r}=e;return`The ${t} field must start with ${r}.`},string:t=>`The ${t} field must be a string.`,stringBetweenLength:(t,e)=>{const{min:r,max:s}=e;return{string:`The ${t} field must be between ${n(r)} and ${n(s)} characters.`,array:`The ${t} field must contain items where each item is between ${n(r)} and ${n(s)} characters.`}},stringContainsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must contain all of the following text: ${r.map(i).join(", ")}.`},stringContainsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must contain at least one of the following text: ${r.map(i).join(", ")}.`},stringLength:(t,e)=>{const{length:r}=e;return{string:`The ${t} field must be ${n(r)} characters.`,array:`The ${t} field must contain items where each item is ${n(r)} characters.`}},stringMaxLength:(t,e)=>{const{length:r}=e;return{string:`The ${t} field must not be greater than ${n(r)} characters.`,array:`The ${t} field must contain items where each item is not greater than ${n(r)} characters.`}},stringMinLength:(t,e)=>{const{length:r}=e;return{string:`The ${t} field must be at least ${n(r)} characters.`,array:`The ${t} field must contain items where each item is at least ${n(r)} characters.`}},stringNotContainsAll:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain all of the following text together: ${r.map(i).join(", ")}.`},stringNotContainsAny:(t,e)=>{const{values:r}=e;return`The ${t} field must not contain any of the following text: ${r.map(i).join(", ")}.`},unique:t=>`The ${t} field has already been taken.`,uppercase:t=>`The ${t} field must be uppercase.`,url:t=>`The ${t} field must be a valid URL.`},"zh-TW":{accepted:()=>"此欄位必須被同意",alpha:()=>"此欄位只能包含字母",alphaDash:()=>"此欄位只能包含字母、數字、連接號和底線",alphaDashDot:()=>"此欄位只能包含字母、數字、連接號、底線和點",alphaNum:()=>"此欄位只能包含字母和數字",array:()=>"此欄位必須是一個陣列",ascii:()=>"此欄位只能包含 ASCII 字元和符號",between:(t,e)=>{const{min:r,max:s}=e;return{number:`此欄位必須介於${n(r)}到${n(s)}`,array:`此欄位中的每個項目都必須介於${n(r)}到${n(s)}`}},betweenLength:(t,e)=>{const{min:r,max:s}=e;return`此欄位必須介於${n(r)}到${n(s)}個項目之間`},boolean:()=>"此欄位必須是一個布林值",containsAll:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下所有項目:${r.map(i).join(", ")}`},containsAny:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下其中一個項目:${r.map(i).join(", ")}`},declined:()=>"此欄位必須被拒絕",different:(t,e)=>{const{field:r}=e;return`此欄位必須和${r}欄位不同`},distinct:()=>"此欄位不能包含重複的值",domain:()=>"此欄位必須是有效的網域",email:()=>"此欄位必須是有效的電子郵件地址",endsWith:(t,e)=>{const{value:r}=e;return`此欄位必須以${r}結尾`},equals:(t,e)=>{const{value:r}=e;return`此欄位必須是${r}`},file:()=>"此欄位必須是檔案",fileBetweenSize:(t,e)=>{const{min:r,max:s}=e;return{file:`此欄位必須介於${n(r)}到${n(s)} KB 之間`,array:`此欄位中的每個項目都必須介於${n(r)}到${n(s)} KB 之間`}},fileMaxSize:(t,e)=>{const{size:r}=e;return{file:`此欄位不能大於${n(r)} KB`,array:`此欄位中的每個項目都不能大於${n(r)} KB`}},fileMinSize:(t,e)=>{const{size:r}=e;return{file:`此欄位不能小於${n(r)} KB`,array:`此欄位中的每個項目都不能小於${n(r)} KB`}},fileSize:(t,e)=>{const{size:r}=e;return{file:`此欄位必須是${n(r)} KB`,array:`此欄位中的每個項目都必須是${n(r)} KB`}},http:()=>'此欄位必須以 "http://" 協議開頭',httpOrHttps:()=>'此欄位必須以 "http://" 或 "https://" 協議開頭',https:()=>'此欄位必須以 "https://" 協議開頭',integer:()=>"此欄位必須是整數",ip:()=>"此欄位必須是有效的 IP 位址",ipv4:()=>"此欄位必須是有效的 IPv4 位址",ipv6:()=>"此欄位必須是有效的 IPv6 位址",json:()=>"此欄位必須是有效的 JSON 字串",length:(t,e)=>{const{length:r}=e;return`此欄位必須包含${n(r)}個項目`},lowercase:()=>"此欄位必須是小寫",max:(t,e)=>{const{max:r}=e;return{number:`此欄位不能大於${n(r)}`,array:`此欄位中的每個項目都不能大於${n(r)}`}},maxLength:(t,e)=>{const{length:r}=e;return`此欄位不能大於${n(r)}個項目`},min:(t,e)=>{const{min:r}=e;return{number:`此欄位不能小於${n(r)}`,array:`此欄位中的每個項目都不能小於${n(r)}`}},minLength:(t,e)=>{const{length:r}=e;return`此欄位不能小於${n(r)}個項目`},notContainsAll:(t,e)=>{const{values:r}=e;return`此欄位不能同時包含以下所有值:${r.map(i).join(", ")}`},notContainsAny:(t,e)=>{const{values:r}=e;return`此欄位不能包含以下任何值:${r.map(i).join(", ")}`},notEquals:(t,e)=>{const{value:r}=e;return`此欄位不能是${r}`},notOneOf:(t,e)=>{const{values:r}=e;return`此欄位不能是以下任何值:${r.map(i).join(", ")}`},number:()=>"此欄位必須是數字",numeric:()=>"此欄位必須是數字",oneOf:(t,e)=>{const{values:r}=e;return`此欄位必須是以下其中一個值:${r.map(i).join(", ")}`},protocol:(t,e)=>{const{values:r}=e;if(r.length===1){const[s]=r;return`此欄位必須以 "${s}://" 協議開頭`}return`此欄位必須以以下其中一個協議開頭:${r.map(s=>`${s}://`).map(i).join(", ")}`},regex:()=>"此欄位必須符合所需的格式",required:()=>"此欄位為必填",same:(t,e)=>{const{field:r}=e;return`此欄位必須與${r}欄位相同`},size:(t,e)=>{const{size:r}=e;return{number:`此欄位必須是${n(r)}`,array:`此欄位中的每個項目都必須是${n(r)}`}},startsWith:(t,e)=>{const{value:r}=e;return`此欄位必須以${r}開頭`},string:()=>"此欄位必須是字串",stringBetweenLength:(t,e)=>{const{min:r,max:s}=e;return{string:`此欄位必須介於${n(r)}到${n(s)}個字元之間`,array:`此欄位中的每個項目都必須介於${n(r)}到${n(s)}個字元之間`}},stringContainsAll:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下所有文字:${r.map(i).join(", ")}`},stringContainsAny:(t,e)=>{const{values:r}=e;return`此欄位必須包含以下其中一個文字:${r.map(i).join(", ")}`},stringLength:(t,e)=>{const{length:r}=e;return{string:`此欄位必須是${n(r)}個字元`,array:`此欄位中的每個項目都必須是${n(r)}個字元`}},stringMaxLength:(t,e)=>{const{length:r}=e;return{string:`此欄位不能大於${n(r)}個字元`,array:`此欄位中的每個項目都不能大於${n(r)}個字元`}},stringMinLength:(t,e)=>{const{length:r}=e;return{string:`此欄位不能小於${n(r)}個字元`,array:`此欄位中的每個項目都不能小於${n(r)}個字元`}},stringNotContainsAll:(t,e)=>{const{values:r}=e;return`此欄位不能同時包含以下所有文字:${r.map(i).join(", ")}`},stringNotContainsAny:(t,e)=>{const{values:r}=e;return`此欄位不能包含以下任何文字:${r.map(i).join(", ")}`},unique:()=>"此欄位已經存在",uppercase:()=>"此欄位必須是大寫",url:()=>"此欄位必須是有效的 URL"}},k=()=>t=>a(t)?!1:["y","yes","on","1","true"].includes(String(t).toLowerCase()),E=()=>t=>a(t)?!1:/^[a-zA-Z]+$/.test(String(t)),B=()=>t=>a(t)?!1:/^[a-zA-Z0-9-_]+$/.test(String(t)),D=()=>t=>a(t)?!1:/^[a-zA-Z0-9-_.]+$/.test(String(t)),N=()=>t=>a(t)?!1:/^[a-zA-Z0-9]+$/.test(String(t)),P=()=>t=>Array.isArray(t),W=()=>t=>a(t)?!1:/^[\x20-\x7E]+$/.test(String(t)),c=({max:t})=>e=>a(e)?!1:typeof e=="number"?e<=t:Array.isArray(e)?e.every(r=>c({max:t})(r)):!1,m=({min:t})=>e=>a(e)?!1:typeof e=="number"?e>=t:Array.isArray(e)?e.every(r=>m({min:t})(r)):!1,I=({min:t,max:e})=>r=>a(r)?!1:m({min:t})(r)&&c({max:e})(r),T=({length:t})=>e=>a(e)?!1:Array.isArray(e)?e.length<=t:!1,v=({length:t})=>e=>a(e)?!1:Array.isArray(e)?e.length>=t:!1,Z=({min:t,max:e})=>r=>a(r)?!1:v({length:t})(r)&&T({length:e})(r),K=()=>t=>a(t)?!1:typeof t=="boolean",L=({values:t})=>e=>a(e)?!1:Array.isArray(e)?t.every(r=>e.includes(r)):!1,S=({values:t})=>e=>a(e)?!1:Array.isArray(e)?t.some(r=>e.includes(r)):!1,H=()=>t=>a(t)?!1:["n","no","off","0","false"].includes(String(t).toLowerCase()),U=({value:t})=>e=>a(e)?!1:e!==t,V=()=>t=>a(t)?!1:Array.isArray(t)?new Set(t).size===t.length:!0,J=()=>t=>a(t)?!1:/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)),G=()=>t=>a(t)?!1:/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(String(t)),Q=({value:t})=>e=>a(e)?!1:String(e).endsWith(t),X=({value:t})=>e=>e===t,Y=()=>t=>t instanceof File,g=({size:t})=>e=>a(e)?!1:e instanceof File?e.size<=t*1024:Array.isArray(e)?e.every(r=>g({size:t})(r)):!1,d=({size:t})=>e=>a(e)?!1:e instanceof File?e.size>=t*1024:Array.isArray(e)?e.every(r=>d({size:t})(r)):!1,ee=({min:t,max:e})=>r=>a(r)?!1:d({size:t})(r)&&g({size:e})(r),x=({size:t})=>e=>{if(a(e))return!1;if(e instanceof File){const r=t*1024,s=(t+1)*1024;return e.size>=r&&e.size<s}return Array.isArray(e)?e.every(r=>x({size:t})(r)):!1},z=({value:t})=>e=>a(e)?!1:String(e).startsWith(t),y=({values:t})=>e=>a(e)?!1:t.map(r=>`${r}://`).some(r=>z({value:r})(e)),F=()=>t=>a(t)?!1:y({values:["http"]})(t),C=()=>t=>a(t)?!1:y({values:["https"]})(t),te=()=>t=>a(t)?!1:F()(t)||C()(t),p=()=>t=>a(t)?!1:typeof t=="number",re=()=>t=>a(t)?!1:p()(t)&&Number.isInteger(t),$=({includeProtocol:t}={})=>e=>{if(a(e))return!1;if(t)try{const{host:r}=new URL(String(e));return $({})(r)}catch{return!1}return/^((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]?)(:\d+)?$/.test(String(e))},b=({includeProtocol:t}={})=>e=>{if(a(e))return!1;if(t)try{const{host:r}=new URL(String(e));return b({})(r)}catch{return!1}return/(([0-9a-fA-F]{1,4}:){7,7}[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})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(String(e))},ne=({includeProtocol:t}={})=>e=>a(e)?!1:$({includeProtocol:t})(e)||b({includeProtocol:t})(e),se=()=>t=>{if(a(t))return!1;try{return JSON.parse(String(t)),!0}catch{return!1}},ae=({length:t})=>e=>a(e)?!1:Array.isArray(e)?e.length===t:!1,ie=()=>t=>a(t)?!1:String(t)===String(t).toLowerCase(),le=({values:t})=>e=>a(e)?!1:Array.isArray(e)?!t.every(r=>e.includes(r)):!1,oe=({values:t})=>e=>a(e)?!1:Array.isArray(e)?!t.some(r=>e.includes(r)):!1,ue=({value:t})=>e=>e!==t,he=({values:t})=>e=>a(e)?!1:!t.includes(e),j=()=>t=>typeof t=="string",fe=()=>t=>a(t)?!1:j()(t)?/^-?\d+(\.\d+)?$/.test(String(t)):p()(t),ce=({values:t})=>e=>a(e)?!1:t.includes(e),me=({expression:t})=>e=>{if(a(e))return!1;if(!(t instanceof RegExp))throw new TypeError("The expression provided is not a valid RegExp.");return t.test(String(e))},ge=()=>t=>!a(t),de=({value:t})=>e=>a(e)?!1:Array.isArray(e)?e.every(r=>r===t):e===t,_=({size:t})=>e=>a(e)?!1:typeof e=="number"?e===t:Array.isArray(e)?e.every(r=>_({size:t})(r)):!1,A=({length:t})=>e=>a(e)?!1:typeof e=="string"?e.length<=t:Array.isArray(e)?e.every(r=>A({length:t})(r)):!1,w=({length:t})=>e=>a(e)?!1:typeof e=="string"?e.length>=t:Array.isArray(e)?e.every(r=>w({length:t})(r)):!1,ye=({min:t,max:e})=>r=>a(r)?!1:w({length:t})(r)&&A({length:e})(r),q=({length:t})=>e=>a(e)?!1:typeof e=="string"?e.length===t:Array.isArray(e)?e.every(r=>q({length:t})(r)):!1,pe={accepted:k,alpha:E,alphaDash:B,alphaDashDot:D,alphaNum:N,array:P,ascii:W,between:I,betweenLength:Z,boolean:K,containsAll:L,containsAny:S,declined:H,different:U,distinct:V,domain:J,email:G,endsWith:Q,equals:X,file:Y,fileBetweenSize:ee,fileMaxSize:g,fileMinSize:d,fileSize:x,http:F,httpOrHttps:te,https:C,integer:re,ip:ne,ipv4:$,ipv6:b,json:se,length:ae,lowercase:ie,max:c,maxLength:T,min:m,minLength:v,notContainsAll:le,notContainsAny:oe,notEquals:ue,notOneOf:he,number:p,numeric:fe,oneOf:ce,protocol:y,regex:me,required:ge,same:de,size:_,startsWith:z,string:j,stringBetweenLength:ye,stringContainsAll:L,stringContainsAny:S,stringLength:q,stringMaxLength:A,stringMinLength:w,stringNotContainsAll:({values:t})=>e=>a(e)?!1:typeof e=="string"?!t.every(r=>e.includes(r)):!1,stringNotContainsAny:({values:t})=>e=>a(e)?!1:typeof e=="string"?!t.some(r=>e.includes(r)):!1,unique:({values:t,ignored:e=[]})=>r=>a(r)?!1:(Array.isArray(e)?e:[e]).some(s=>s===r)?!0:!t.some(s=>s===r),uppercase:()=>t=>a(t)?!1:String(t)===String(t).toUpperCase(),url:()=>t=>{if(a(t))return!1;try{return new URL(String(t)),!0}catch{return!1}}};class $e{constructor({fallbackLocale:e,locale:r,plugins:s}={}){u(this,"locale","en");u(this,"fallbackLocale","en");u(this,"locales",{});u(this,"rules",{});this.registerLocales(M),this.registerRules(pe),e&&this.setFallbackLocale(e),r&&this.setLocale(r),s&&s.forEach(l=>this.registerPlugin(l))}getLocale(){return this.locale}getFallbackLocale(){return this.fallbackLocale}setLocale(e){if(!(e in this.locales))throw new Error(`The "${e}" locale is not registered.`);return this.locale=e,this}setFallbackLocale(e){if(!(e in this.locales))throw new Error(`The "${e}" fallback locale is not registered.`);return this.fallbackLocale=e,this}defineField(e){return new R({name:e,locale:this.locale,fallbackLocale:this.fallbackLocale,locales:this.locales,rules:this.rules})}registerLocales(e){return this.locales=Object.keys(e).reduce((r,s)=>(r[s]={...this.locales[s],...e[s]},r),{...this.locales}),this}registerRules(e){return this.rules={...this.rules,...e},this}registerPlugin(e){if(!e||!e.locales||!e.rules)throw new Error('The plugin must have "locales" and "rules" properties.');return this.registerLocales(e.locales).registerRules(e.rules)}}h.FieldValidator=R,h.FormValidator=$e,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,3 @@
1
+ import { Rule } from '@kklab/fortress-validator-types';
2
+ declare const httpOrHttpsRule: Rule;
3
+ export default httpOrHttpsRule;
@@ -0,0 +1,6 @@
1
+ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
+ export interface IpRuleArguments extends RuleArguments {
3
+ includeProtocol?: boolean;
4
+ }
5
+ declare const ipRule: Rule<IpRuleArguments>;
6
+ export default ipRule;
@@ -0,0 +1,6 @@
1
+ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
+ export interface Ipv4RuleArguments extends RuleArguments {
3
+ includeProtocol?: boolean;
4
+ }
5
+ declare const ipv4Rule: Rule<Ipv4RuleArguments>;
6
+ export default ipv4Rule;
@@ -0,0 +1,6 @@
1
+ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
+ export interface Ipv6RuleArguments extends RuleArguments {
3
+ includeProtocol?: boolean;
4
+ }
5
+ declare const ipv6Rule: Rule<Ipv6RuleArguments>;
6
+ export default ipv6Rule;
@@ -2,5 +2,5 @@ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
2
  export interface NotContainsAllRuleArguments extends RuleArguments {
3
3
  values: unknown[];
4
4
  }
5
- declare const containsAllRule: Rule<NotContainsAllRuleArguments>;
6
- export default containsAllRule;
5
+ declare const notContainsAllRule: Rule<NotContainsAllRuleArguments>;
6
+ export default notContainsAllRule;
@@ -2,5 +2,5 @@ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
2
  export interface NotContainsAnyRuleArguments extends RuleArguments {
3
3
  values: unknown[];
4
4
  }
5
- declare const containsAnyRule: Rule<NotContainsAnyRuleArguments>;
6
- export default containsAnyRule;
5
+ declare const notContainsAnyRule: Rule<NotContainsAnyRuleArguments>;
6
+ export default notContainsAnyRule;
@@ -0,0 +1,6 @@
1
+ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
+ export interface ProtocolRuleArguments extends RuleArguments {
3
+ values: string[];
4
+ }
5
+ declare const protocolRule: Rule<ProtocolRuleArguments>;
6
+ export default protocolRule;
@@ -2,5 +2,5 @@ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
2
  export interface StringNotContainsAllRuleArguments extends RuleArguments {
3
3
  values: string[];
4
4
  }
5
- declare const stringContainsAllRule: Rule<StringNotContainsAllRuleArguments>;
6
- export default stringContainsAllRule;
5
+ declare const stringNotContainsAllRule: Rule<StringNotContainsAllRuleArguments>;
6
+ export default stringNotContainsAllRule;
@@ -2,5 +2,5 @@ import { Rule, RuleArguments } from '@kklab/fortress-validator-types';
2
2
  export interface StringNotContainsAnyRuleArguments extends RuleArguments {
3
3
  values: string[];
4
4
  }
5
- declare const stringContainsAnyRule: Rule<StringNotContainsAnyRuleArguments>;
6
- export default stringContainsAnyRule;
5
+ declare const stringNotContainsAnyRule: Rule<StringNotContainsAnyRuleArguments>;
6
+ export default stringNotContainsAnyRule;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kklab/fortress-validator",
3
3
  "private": false,
4
- "version": "1.0.10",
4
+ "version": "1.0.12",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -14,7 +14,7 @@
14
14
  "release": "npm run test && npm run build && npm publish --access public"
15
15
  },
16
16
  "dependencies": {
17
- "@kklab/fortress-validator-utils": "^1.0.1"
17
+ "@kklab/fortress-validator-utils": "^1.0.3"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@kklab/fortress-validator-plugin-date": "^1.0.4",