@whatsaaaa/form-builder-schema 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -3
- package/dist/index.d.ts +55 -2
- package/dist/index.js +398 -119
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -85,22 +85,39 @@ function buildFieldSchema(checked, messages, ctx) {
|
|
|
85
85
|
return required ? schema : v.optional(schema);
|
|
86
86
|
}
|
|
87
87
|
const pipe = (schema, items) => items.reduce((acc, item) => v.pipe(acc, item), schema);
|
|
88
|
-
// src/
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
88
|
+
// src/shared/reserved-keys.ts
|
|
89
|
+
/**
|
|
90
|
+
* Field keys this package owns. A field type's props can't use them.
|
|
91
|
+
*/
|
|
92
|
+
const RESERVED_FIELD_KEYS = [
|
|
93
|
+
"name",
|
|
94
|
+
"type",
|
|
95
|
+
"label",
|
|
96
|
+
"description",
|
|
97
|
+
"placeholder",
|
|
98
|
+
"default",
|
|
99
|
+
"required",
|
|
100
|
+
"rules",
|
|
101
|
+
"ui"
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* Rule keys this package owns. A rule's params can't use them.
|
|
105
|
+
*/
|
|
106
|
+
const RESERVED_RULE_KEYS = ["rule", "message"];
|
|
107
|
+
/**
|
|
108
|
+
* Form rule keys this package owns. A form rule's params can't use them.
|
|
109
|
+
*/
|
|
110
|
+
const RESERVED_FORM_RULE_KEYS = [
|
|
111
|
+
"rule",
|
|
112
|
+
"field",
|
|
113
|
+
"message"
|
|
114
|
+
];
|
|
115
|
+
const fieldKeys = new Set(RESERVED_FIELD_KEYS);
|
|
116
|
+
const ruleKeys = new Set(RESERVED_RULE_KEYS);
|
|
117
|
+
const formRuleKeys = new Set(RESERVED_FORM_RULE_KEYS);
|
|
118
|
+
const isReservedFieldKey = (key) => fieldKeys.has(key);
|
|
119
|
+
const isReservedRuleKey = (key) => ruleKeys.has(key);
|
|
120
|
+
const isReservedFormRuleKey = (key) => formRuleKeys.has(key);
|
|
104
121
|
// src/field-types/basic-types.ts
|
|
105
122
|
/** Types with one plain value and no props. Each maps to a native input. */
|
|
106
123
|
const basicTypes = {
|
|
@@ -331,7 +348,7 @@ const any = [
|
|
|
331
348
|
"string",
|
|
332
349
|
"boolean"
|
|
333
350
|
];
|
|
334
|
-
const meta$
|
|
351
|
+
const meta$7 = (label) => ({
|
|
335
352
|
label,
|
|
336
353
|
category: "comparison"
|
|
337
354
|
});
|
|
@@ -342,54 +359,54 @@ const comparisonRules = {
|
|
|
342
359
|
minValue: defineRule({
|
|
343
360
|
kinds: ordered,
|
|
344
361
|
params: valueParams,
|
|
345
|
-
meta: meta$
|
|
362
|
+
meta: meta$7("Minimum"),
|
|
346
363
|
create: (p, m) => v.minValue(p.value, m)
|
|
347
364
|
}),
|
|
348
365
|
maxValue: defineRule({
|
|
349
366
|
kinds: ordered,
|
|
350
367
|
params: valueParams,
|
|
351
|
-
meta: meta$
|
|
368
|
+
meta: meta$7("Maximum"),
|
|
352
369
|
create: (p, m) => v.maxValue(p.value, m)
|
|
353
370
|
}),
|
|
354
371
|
gtValue: defineRule({
|
|
355
372
|
kinds: ordered,
|
|
356
373
|
params: valueParams,
|
|
357
|
-
meta: meta$
|
|
374
|
+
meta: meta$7("Greater than"),
|
|
358
375
|
create: (p, m) => v.gtValue(p.value, m)
|
|
359
376
|
}),
|
|
360
377
|
ltValue: defineRule({
|
|
361
378
|
kinds: ordered,
|
|
362
379
|
params: valueParams,
|
|
363
|
-
meta: meta$
|
|
380
|
+
meta: meta$7("Less than"),
|
|
364
381
|
create: (p, m) => v.ltValue(p.value, m)
|
|
365
382
|
}),
|
|
366
383
|
value: defineRule({
|
|
367
384
|
kinds: any,
|
|
368
385
|
params: valueParams,
|
|
369
|
-
meta: meta$
|
|
386
|
+
meta: meta$7("Equals"),
|
|
370
387
|
create: (p, m) => v.value(p.value, m)
|
|
371
388
|
}),
|
|
372
389
|
notValue: defineRule({
|
|
373
390
|
kinds: any,
|
|
374
391
|
params: valueParams,
|
|
375
|
-
meta: meta$
|
|
392
|
+
meta: meta$7("Does not equal"),
|
|
376
393
|
create: (p, m) => v.notValue(p.value, m)
|
|
377
394
|
}),
|
|
378
395
|
values: defineRule({
|
|
379
396
|
kinds: ordered,
|
|
380
397
|
params: valuesParams,
|
|
381
|
-
meta: meta$
|
|
398
|
+
meta: meta$7("One of"),
|
|
382
399
|
create: (p, m) => v.values(p.values, m)
|
|
383
400
|
}),
|
|
384
401
|
notValues: defineRule({
|
|
385
402
|
kinds: ordered,
|
|
386
403
|
params: valuesParams,
|
|
387
|
-
meta: meta$
|
|
404
|
+
meta: meta$7("None of"),
|
|
388
405
|
create: (p, m) => v.notValues(p.values, m)
|
|
389
406
|
})
|
|
390
407
|
};
|
|
391
408
|
// src/rules/content-rules.ts
|
|
392
|
-
const meta$
|
|
409
|
+
const meta$6 = (label, description) => ({
|
|
393
410
|
label,
|
|
394
411
|
description,
|
|
395
412
|
category: "content"
|
|
@@ -418,31 +435,31 @@ const contentRules = {
|
|
|
418
435
|
regex: defineRule({
|
|
419
436
|
kinds: ["string"],
|
|
420
437
|
params: regexParams,
|
|
421
|
-
meta: meta$
|
|
438
|
+
meta: meta$6("Matches pattern", "A JavaScript regular expression, without the slashes."),
|
|
422
439
|
create: (p, m) => v.regex(new RegExp(p.pattern, p.flags?.replace(/[gy]/g, "")), m)
|
|
423
440
|
}),
|
|
424
441
|
startsWith: defineRule({
|
|
425
442
|
kinds: ["string"],
|
|
426
443
|
params: stringParams,
|
|
427
|
-
meta: meta$
|
|
444
|
+
meta: meta$6("Starts with"),
|
|
428
445
|
create: (p, m) => v.startsWith(p.value, m)
|
|
429
446
|
}),
|
|
430
447
|
endsWith: defineRule({
|
|
431
448
|
kinds: ["string"],
|
|
432
449
|
params: stringParams,
|
|
433
|
-
meta: meta$
|
|
450
|
+
meta: meta$6("Ends with"),
|
|
434
451
|
create: (p, m) => v.endsWith(p.value, m)
|
|
435
452
|
}),
|
|
436
453
|
includes: defineRule({
|
|
437
454
|
kinds: ["string", "array"],
|
|
438
455
|
params: contentParams,
|
|
439
|
-
meta: meta$
|
|
456
|
+
meta: meta$6("Contains"),
|
|
440
457
|
create: (p, m) => v.includes(p.value, m)
|
|
441
458
|
}),
|
|
442
459
|
excludes: defineRule({
|
|
443
460
|
kinds: ["string", "array"],
|
|
444
461
|
params: contentParams,
|
|
445
|
-
meta: meta$
|
|
462
|
+
meta: meta$6("Does not contain"),
|
|
446
463
|
create: (p, m) => v.excludes(p.value, m)
|
|
447
464
|
})
|
|
448
465
|
};
|
|
@@ -466,7 +483,7 @@ const dateRules = {
|
|
|
466
483
|
};
|
|
467
484
|
// src/rules/file-rules.ts
|
|
468
485
|
const kinds$3 = ["file"];
|
|
469
|
-
const meta$
|
|
486
|
+
const meta$5 = (label, description) => ({
|
|
470
487
|
label,
|
|
471
488
|
description,
|
|
472
489
|
category: "file"
|
|
@@ -478,31 +495,31 @@ const fileRules = {
|
|
|
478
495
|
minSize: defineRule({
|
|
479
496
|
kinds: kinds$3,
|
|
480
497
|
params: countParams,
|
|
481
|
-
meta: meta$
|
|
498
|
+
meta: meta$5("Minimum size", bytes),
|
|
482
499
|
create: (p, m) => v.minSize(p.value, m)
|
|
483
500
|
}),
|
|
484
501
|
maxSize: defineRule({
|
|
485
502
|
kinds: kinds$3,
|
|
486
503
|
params: countParams,
|
|
487
|
-
meta: meta$
|
|
504
|
+
meta: meta$5("Maximum size", bytes),
|
|
488
505
|
create: (p, m) => v.maxSize(p.value, m)
|
|
489
506
|
}),
|
|
490
507
|
size: defineRule({
|
|
491
508
|
kinds: kinds$3,
|
|
492
509
|
params: countParams,
|
|
493
|
-
meta: meta$
|
|
510
|
+
meta: meta$5("Exact size", bytes),
|
|
494
511
|
create: (p, m) => v.size(p.value, m)
|
|
495
512
|
}),
|
|
496
513
|
notSize: defineRule({
|
|
497
514
|
kinds: kinds$3,
|
|
498
515
|
params: countParams,
|
|
499
|
-
meta: meta$
|
|
516
|
+
meta: meta$5("Not this size", bytes),
|
|
500
517
|
create: (p, m) => v.notSize(p.value, m)
|
|
501
518
|
}),
|
|
502
519
|
mimeType: defineRule({
|
|
503
520
|
kinds: kinds$3,
|
|
504
521
|
params: mimeTypeParams,
|
|
505
|
-
meta: meta$
|
|
522
|
+
meta: meta$5("File type"),
|
|
506
523
|
create: (p, m) => v.mimeType(p.types, m)
|
|
507
524
|
})
|
|
508
525
|
};
|
|
@@ -573,7 +590,7 @@ const formatRules = {
|
|
|
573
590
|
};
|
|
574
591
|
// src/rules/length-rules.ts
|
|
575
592
|
const kinds$2 = ["string", "array"];
|
|
576
|
-
const meta$
|
|
593
|
+
const meta$4 = (label) => ({
|
|
577
594
|
label,
|
|
578
595
|
category: "length"
|
|
579
596
|
});
|
|
@@ -582,31 +599,31 @@ const lengthRules = {
|
|
|
582
599
|
length: defineRule({
|
|
583
600
|
kinds: kinds$2,
|
|
584
601
|
params: countParams,
|
|
585
|
-
meta: meta$
|
|
602
|
+
meta: meta$4("Exact length"),
|
|
586
603
|
create: (p, m) => v.length(p.value, m)
|
|
587
604
|
}),
|
|
588
605
|
minLength: defineRule({
|
|
589
606
|
kinds: kinds$2,
|
|
590
607
|
params: countParams,
|
|
591
|
-
meta: meta$
|
|
608
|
+
meta: meta$4("Minimum length"),
|
|
592
609
|
create: (p, m) => v.minLength(p.value, m)
|
|
593
610
|
}),
|
|
594
611
|
maxLength: defineRule({
|
|
595
612
|
kinds: kinds$2,
|
|
596
613
|
params: countParams,
|
|
597
|
-
meta: meta$
|
|
614
|
+
meta: meta$4("Maximum length"),
|
|
598
615
|
create: (p, m) => v.maxLength(p.value, m)
|
|
599
616
|
}),
|
|
600
617
|
notLength: defineRule({
|
|
601
618
|
kinds: kinds$2,
|
|
602
619
|
params: countParams,
|
|
603
|
-
meta: meta$
|
|
620
|
+
meta: meta$4("Not this length"),
|
|
604
621
|
create: (p, m) => v.notLength(p.value, m)
|
|
605
622
|
})
|
|
606
623
|
};
|
|
607
624
|
// src/rules/normalize-rules.ts
|
|
608
625
|
const stage = "normalize";
|
|
609
|
-
const meta$
|
|
626
|
+
const meta$3 = (label, description) => ({
|
|
610
627
|
label,
|
|
611
628
|
description,
|
|
612
629
|
category: "normalize"
|
|
@@ -620,31 +637,31 @@ const normalizeRules = {
|
|
|
620
637
|
trim: defineRule({
|
|
621
638
|
kinds: ["string"],
|
|
622
639
|
stage,
|
|
623
|
-
meta: meta$
|
|
640
|
+
meta: meta$3("Trim"),
|
|
624
641
|
create: () => v.trim()
|
|
625
642
|
}),
|
|
626
643
|
trimStart: defineRule({
|
|
627
644
|
kinds: ["string"],
|
|
628
645
|
stage,
|
|
629
|
-
meta: meta$
|
|
646
|
+
meta: meta$3("Trim start"),
|
|
630
647
|
create: () => v.trimStart()
|
|
631
648
|
}),
|
|
632
649
|
trimEnd: defineRule({
|
|
633
650
|
kinds: ["string"],
|
|
634
651
|
stage,
|
|
635
|
-
meta: meta$
|
|
652
|
+
meta: meta$3("Trim end"),
|
|
636
653
|
create: () => v.trimEnd()
|
|
637
654
|
}),
|
|
638
655
|
toLowerCase: defineRule({
|
|
639
656
|
kinds: ["string"],
|
|
640
657
|
stage,
|
|
641
|
-
meta: meta$
|
|
658
|
+
meta: meta$3("Lowercase"),
|
|
642
659
|
create: () => v.toLowerCase()
|
|
643
660
|
}),
|
|
644
661
|
toUpperCase: defineRule({
|
|
645
662
|
kinds: ["string"],
|
|
646
663
|
stage,
|
|
647
|
-
meta: meta$
|
|
664
|
+
meta: meta$3("Uppercase"),
|
|
648
665
|
create: () => v.toUpperCase()
|
|
649
666
|
}),
|
|
650
667
|
normalize: defineRule({
|
|
@@ -656,27 +673,27 @@ const normalizeRules = {
|
|
|
656
673
|
"NFKC",
|
|
657
674
|
"NFKD"
|
|
658
675
|
])) }),
|
|
659
|
-
meta: meta$
|
|
676
|
+
meta: meta$3("Unicode normalize", "Defaults to NFC."),
|
|
660
677
|
create: (p) => v.normalize(p.form)
|
|
661
678
|
}),
|
|
662
679
|
toMinValue: defineRule({
|
|
663
680
|
kinds: ["number"],
|
|
664
681
|
stage,
|
|
665
682
|
params: limit,
|
|
666
|
-
meta: meta$
|
|
683
|
+
meta: meta$3("Raise to minimum", "Replaces a smaller number with the minimum."),
|
|
667
684
|
create: (p) => v.toMinValue(p.value)
|
|
668
685
|
}),
|
|
669
686
|
toMaxValue: defineRule({
|
|
670
687
|
kinds: ["number"],
|
|
671
688
|
stage,
|
|
672
689
|
params: limit,
|
|
673
|
-
meta: meta$
|
|
690
|
+
meta: meta$3("Lower to maximum", "Replaces a larger number with the maximum."),
|
|
674
691
|
create: (p) => v.toMaxValue(p.value)
|
|
675
692
|
})
|
|
676
693
|
};
|
|
677
694
|
// src/rules/number-rules.ts
|
|
678
695
|
const kinds$1 = ["number"];
|
|
679
|
-
const meta$
|
|
696
|
+
const meta$2 = (label) => ({
|
|
680
697
|
label,
|
|
681
698
|
category: "number"
|
|
682
699
|
});
|
|
@@ -685,29 +702,29 @@ const divisor = v.pipe(v.number(), v.finite(), v.notValue(0, "Can't be 0"));
|
|
|
685
702
|
const numberRules = {
|
|
686
703
|
integer: defineRule({
|
|
687
704
|
kinds: kinds$1,
|
|
688
|
-
meta: meta$
|
|
705
|
+
meta: meta$2("Whole number"),
|
|
689
706
|
create: (_, m) => v.integer(m)
|
|
690
707
|
}),
|
|
691
708
|
safeInteger: defineRule({
|
|
692
709
|
kinds: kinds$1,
|
|
693
|
-
meta: meta$
|
|
710
|
+
meta: meta$2("Safe whole number"),
|
|
694
711
|
create: (_, m) => v.safeInteger(m)
|
|
695
712
|
}),
|
|
696
713
|
finite: defineRule({
|
|
697
714
|
kinds: kinds$1,
|
|
698
|
-
meta: meta$
|
|
715
|
+
meta: meta$2("Finite number"),
|
|
699
716
|
create: (_, m) => v.finite(m)
|
|
700
717
|
}),
|
|
701
718
|
multipleOf: defineRule({
|
|
702
719
|
kinds: kinds$1,
|
|
703
720
|
params: v.object({ value: divisor }),
|
|
704
|
-
meta: meta$
|
|
721
|
+
meta: meta$2("Multiple of"),
|
|
705
722
|
create: (p, m) => v.multipleOf(p.value, m)
|
|
706
723
|
})
|
|
707
724
|
};
|
|
708
725
|
// src/rules/text-unit-rules.ts
|
|
709
726
|
const kinds = ["string"];
|
|
710
|
-
const meta = (label, description) => ({
|
|
727
|
+
const meta$1 = (label, description) => ({
|
|
711
728
|
label,
|
|
712
729
|
description,
|
|
713
730
|
category: "text"
|
|
@@ -729,73 +746,73 @@ const textUnitRules = {
|
|
|
729
746
|
graphemes: defineRule({
|
|
730
747
|
kinds,
|
|
731
748
|
params: countParams,
|
|
732
|
-
meta: meta("Exact characters", graphemesInfo),
|
|
749
|
+
meta: meta$1("Exact characters", graphemesInfo),
|
|
733
750
|
create: (p, m) => v.graphemes(p.value, m)
|
|
734
751
|
}),
|
|
735
752
|
minGraphemes: defineRule({
|
|
736
753
|
kinds,
|
|
737
754
|
params: countParams,
|
|
738
|
-
meta: meta("Minimum characters", graphemesInfo),
|
|
755
|
+
meta: meta$1("Minimum characters", graphemesInfo),
|
|
739
756
|
create: (p, m) => v.minGraphemes(p.value, m)
|
|
740
757
|
}),
|
|
741
758
|
maxGraphemes: defineRule({
|
|
742
759
|
kinds,
|
|
743
760
|
params: countParams,
|
|
744
|
-
meta: meta("Maximum characters", graphemesInfo),
|
|
761
|
+
meta: meta$1("Maximum characters", graphemesInfo),
|
|
745
762
|
create: (p, m) => v.maxGraphemes(p.value, m)
|
|
746
763
|
}),
|
|
747
764
|
notGraphemes: defineRule({
|
|
748
765
|
kinds,
|
|
749
766
|
params: countParams,
|
|
750
|
-
meta: meta("Not this many characters", graphemesInfo),
|
|
767
|
+
meta: meta$1("Not this many characters", graphemesInfo),
|
|
751
768
|
create: (p, m) => v.notGraphemes(p.value, m)
|
|
752
769
|
}),
|
|
753
770
|
words: defineRule({
|
|
754
771
|
kinds,
|
|
755
772
|
params: wordsParams,
|
|
756
|
-
meta: meta("Exact words"),
|
|
773
|
+
meta: meta$1("Exact words"),
|
|
757
774
|
create: (p, m) => v.words(p.locale, p.value, m)
|
|
758
775
|
}),
|
|
759
776
|
minWords: defineRule({
|
|
760
777
|
kinds,
|
|
761
778
|
params: wordsParams,
|
|
762
|
-
meta: meta("Minimum words"),
|
|
779
|
+
meta: meta$1("Minimum words"),
|
|
763
780
|
create: (p, m) => v.minWords(p.locale, p.value, m)
|
|
764
781
|
}),
|
|
765
782
|
maxWords: defineRule({
|
|
766
783
|
kinds,
|
|
767
784
|
params: wordsParams,
|
|
768
|
-
meta: meta("Maximum words"),
|
|
785
|
+
meta: meta$1("Maximum words"),
|
|
769
786
|
create: (p, m) => v.maxWords(p.locale, p.value, m)
|
|
770
787
|
}),
|
|
771
788
|
notWords: defineRule({
|
|
772
789
|
kinds,
|
|
773
790
|
params: wordsParams,
|
|
774
|
-
meta: meta("Not this many words"),
|
|
791
|
+
meta: meta$1("Not this many words"),
|
|
775
792
|
create: (p, m) => v.notWords(p.locale, p.value, m)
|
|
776
793
|
}),
|
|
777
794
|
bytes: defineRule({
|
|
778
795
|
kinds,
|
|
779
796
|
params: countParams,
|
|
780
|
-
meta: meta("Exact bytes", bytesInfo),
|
|
797
|
+
meta: meta$1("Exact bytes", bytesInfo),
|
|
781
798
|
create: (p, m) => v.bytes(p.value, m)
|
|
782
799
|
}),
|
|
783
800
|
minBytes: defineRule({
|
|
784
801
|
kinds,
|
|
785
802
|
params: countParams,
|
|
786
|
-
meta: meta("Minimum bytes", bytesInfo),
|
|
803
|
+
meta: meta$1("Minimum bytes", bytesInfo),
|
|
787
804
|
create: (p, m) => v.minBytes(p.value, m)
|
|
788
805
|
}),
|
|
789
806
|
maxBytes: defineRule({
|
|
790
807
|
kinds,
|
|
791
808
|
params: countParams,
|
|
792
|
-
meta: meta("Maximum bytes", bytesInfo),
|
|
809
|
+
meta: meta$1("Maximum bytes", bytesInfo),
|
|
793
810
|
create: (p, m) => v.maxBytes(p.value, m)
|
|
794
811
|
}),
|
|
795
812
|
notBytes: defineRule({
|
|
796
813
|
kinds,
|
|
797
814
|
params: countParams,
|
|
798
|
-
meta: meta("Not this many bytes", bytesInfo),
|
|
815
|
+
meta: meta$1("Not this many bytes", bytesInfo),
|
|
799
816
|
create: (p, m) => v.notBytes(p.value, m)
|
|
800
817
|
})
|
|
801
818
|
};
|
|
@@ -812,10 +829,71 @@ const defaultRules = {
|
|
|
812
829
|
...fileRules,
|
|
813
830
|
...normalizeRules
|
|
814
831
|
};
|
|
832
|
+
// src/rules/define-form-rule.ts
|
|
833
|
+
const defineFormRule = (def) => def;
|
|
834
|
+
// src/rules/form-rules.ts
|
|
835
|
+
const meta = (label, description) => ({
|
|
836
|
+
label,
|
|
837
|
+
description,
|
|
838
|
+
category: "comparison"
|
|
839
|
+
});
|
|
840
|
+
const comparable = (value) => value instanceof Date ? value.getTime() : value;
|
|
841
|
+
const otherParams = v.object({ other: v.string() });
|
|
842
|
+
const operators = {
|
|
843
|
+
lt: (a, b) => a < b,
|
|
844
|
+
lte: (a, b) => a <= b,
|
|
845
|
+
gt: (a, b) => a > b,
|
|
846
|
+
gte: (a, b) => a >= b
|
|
847
|
+
};
|
|
848
|
+
/** Every built-in form rule. */
|
|
849
|
+
const defaultFormRules = {
|
|
850
|
+
sameAs: defineFormRule({
|
|
851
|
+
params: otherParams,
|
|
852
|
+
reads: (params) => [params.other],
|
|
853
|
+
kinds: [
|
|
854
|
+
"string",
|
|
855
|
+
"number",
|
|
856
|
+
"boolean",
|
|
857
|
+
"date"
|
|
858
|
+
],
|
|
859
|
+
sameKind: true,
|
|
860
|
+
meta: meta("Same as", "Equals another field, e.g. to confirm a password"),
|
|
861
|
+
check: ([value, other]) => comparable(value) === comparable(other)
|
|
862
|
+
}),
|
|
863
|
+
differentFrom: defineFormRule({
|
|
864
|
+
params: otherParams,
|
|
865
|
+
reads: (params) => [params.other],
|
|
866
|
+
kinds: [
|
|
867
|
+
"string",
|
|
868
|
+
"number",
|
|
869
|
+
"boolean",
|
|
870
|
+
"date"
|
|
871
|
+
],
|
|
872
|
+
sameKind: true,
|
|
873
|
+
meta: meta("Different from", "Differs from another field, e.g. a new password"),
|
|
874
|
+
check: ([value, other]) => comparable(value) !== comparable(other)
|
|
875
|
+
}),
|
|
876
|
+
compare: defineFormRule({
|
|
877
|
+
params: v.object({
|
|
878
|
+
op: v.picklist([
|
|
879
|
+
"lt",
|
|
880
|
+
"lte",
|
|
881
|
+
"gt",
|
|
882
|
+
"gte"
|
|
883
|
+
]),
|
|
884
|
+
other: v.string()
|
|
885
|
+
}),
|
|
886
|
+
reads: (params) => [params.other],
|
|
887
|
+
kinds: ["number", "date"],
|
|
888
|
+
sameKind: true,
|
|
889
|
+
meta: meta("Compare", "Less or greater than another field, e.g. an end date after the start"),
|
|
890
|
+
check: ([value, other], { op }) => operators[op](comparable(value), comparable(other))
|
|
891
|
+
})
|
|
892
|
+
};
|
|
815
893
|
// src/builder/registry.ts
|
|
816
894
|
/** Consumer entries replace defaults with the same key. */
|
|
817
895
|
function mergeRegistry(custom) {
|
|
818
|
-
const
|
|
896
|
+
const defaults = custom.includeDefaultRules !== false;
|
|
819
897
|
return {
|
|
820
898
|
kinds: Object.freeze({
|
|
821
899
|
...defaultKinds,
|
|
@@ -826,37 +904,34 @@ function mergeRegistry(custom) {
|
|
|
826
904
|
...custom.fieldTypes
|
|
827
905
|
}),
|
|
828
906
|
rules: Object.freeze({
|
|
829
|
-
...
|
|
907
|
+
...defaults ? defaultRules : {},
|
|
830
908
|
...custom.rules
|
|
909
|
+
}),
|
|
910
|
+
formRules: Object.freeze({
|
|
911
|
+
...defaults ? defaultFormRules : {},
|
|
912
|
+
...custom.formRules
|
|
831
913
|
})
|
|
832
914
|
};
|
|
833
915
|
}
|
|
834
916
|
function lookup(record, key) {
|
|
835
917
|
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
836
918
|
}
|
|
837
|
-
// src/
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
* Rule keys this package owns. A rule's params can't use them.
|
|
854
|
-
*/
|
|
855
|
-
const RESERVED_RULE_KEYS = ["rule", "message"];
|
|
856
|
-
const fieldKeys = new Set(RESERVED_FIELD_KEYS);
|
|
857
|
-
const ruleKeys = new Set(RESERVED_RULE_KEYS);
|
|
858
|
-
const isReservedFieldKey = (key) => fieldKeys.has(key);
|
|
859
|
-
const isReservedRuleKey = (key) => ruleKeys.has(key);
|
|
919
|
+
// src/rules/rule-params.ts
|
|
920
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
921
|
+
function paramsFor(def, kind) {
|
|
922
|
+
if (typeof def.params !== "function") return def.params;
|
|
923
|
+
let byKind = cache.get(def);
|
|
924
|
+
if (!byKind) {
|
|
925
|
+
byKind = /* @__PURE__ */ new Map();
|
|
926
|
+
cache.set(def, byKind);
|
|
927
|
+
}
|
|
928
|
+
let params = byKind.get(kind);
|
|
929
|
+
if (!params) {
|
|
930
|
+
params = def.params(kind);
|
|
931
|
+
byKind.set(kind, params);
|
|
932
|
+
}
|
|
933
|
+
return params;
|
|
934
|
+
}
|
|
860
935
|
// src/builder/validate-definition.ts
|
|
861
936
|
const fieldShape = v.looseObject({
|
|
862
937
|
name: v.optional(v.string()),
|
|
@@ -869,7 +944,7 @@ const fieldShape = v.looseObject({
|
|
|
869
944
|
rules: v.optional(v.array(v.unknown())),
|
|
870
945
|
ui: v.optional(v.record(v.string(), v.unknown()))
|
|
871
946
|
});
|
|
872
|
-
const ruleShape = v.looseObject({
|
|
947
|
+
const ruleShape$1 = v.looseObject({
|
|
873
948
|
rule: v.string(),
|
|
874
949
|
message: v.optional(v.string())
|
|
875
950
|
});
|
|
@@ -985,7 +1060,7 @@ function checkProps(field, type, path, issues) {
|
|
|
985
1060
|
issues.push(...toIssues("invalid_props", path, parsed.issues));
|
|
986
1061
|
}
|
|
987
1062
|
function checkRule(raw, { kind, itemKind }, path, registry, issues) {
|
|
988
|
-
const shape = v.safeParse(ruleShape, raw);
|
|
1063
|
+
const shape = v.safeParse(ruleShape$1, raw);
|
|
989
1064
|
if (!shape.success) {
|
|
990
1065
|
issues.push(...toIssues("invalid_shape", path, shape.issues));
|
|
991
1066
|
return;
|
|
@@ -1003,28 +1078,38 @@ function checkRule(raw, { kind, itemKind }, path, registry, issues) {
|
|
|
1003
1078
|
issues.push(issue("kind_mismatch", [...path, "rule"], message));
|
|
1004
1079
|
return;
|
|
1005
1080
|
}
|
|
1081
|
+
const params = checkParams(rule, paramsFor(def, each ? itemKind : kind), isReservedRuleKey, `Rule "${rule.rule}"`, path, issues);
|
|
1082
|
+
if (!params) return void 0;
|
|
1083
|
+
return {
|
|
1084
|
+
name: rule.rule,
|
|
1085
|
+
def,
|
|
1086
|
+
params,
|
|
1087
|
+
message: rule.message,
|
|
1088
|
+
each
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Checks the params of a rule or a form rule. Every key that isn't reserved must be one of
|
|
1093
|
+
* `params`, and together they must pass it. Returns the parsed params, or undefined when they
|
|
1094
|
+
* have issues. `at` names the rule in messages, e.g. `Rule "minLength"`.
|
|
1095
|
+
*/
|
|
1096
|
+
function checkParams(raw, params, isReserved, at, path, issues) {
|
|
1006
1097
|
const issueCount = issues.length;
|
|
1007
|
-
const params = paramsFor(def, each ? itemKind : kind);
|
|
1008
1098
|
const paramKeys = Object.keys(params?.entries ?? {});
|
|
1009
1099
|
const input = {};
|
|
1010
|
-
for (const key of Object.keys(
|
|
1011
|
-
if (
|
|
1012
|
-
if (paramKeys.includes(key)) input[key] =
|
|
1013
|
-
else issues.push(issue("unknown_param", [...path, key],
|
|
1100
|
+
for (const key of Object.keys(raw)) {
|
|
1101
|
+
if (isReserved(key)) continue;
|
|
1102
|
+
if (paramKeys.includes(key)) input[key] = raw[key];
|
|
1103
|
+
else issues.push(issue("unknown_param", [...path, key], `${at} has no param "${key}"`));
|
|
1014
1104
|
}
|
|
1015
1105
|
const parsed = v.safeParse(params ?? noParams, input);
|
|
1016
1106
|
if (!parsed.success) issues.push(...toIssues("invalid_params", path, parsed.issues));
|
|
1017
1107
|
if (!parsed.success || issues.length > issueCount) return void 0;
|
|
1018
|
-
return
|
|
1019
|
-
name: rule.rule,
|
|
1020
|
-
def,
|
|
1021
|
-
params: parsed.output,
|
|
1022
|
-
message: rule.message,
|
|
1023
|
-
each
|
|
1024
|
-
};
|
|
1108
|
+
return parsed.output;
|
|
1025
1109
|
}
|
|
1026
1110
|
function checkRegistry(registry) {
|
|
1027
1111
|
for (const [name, rule] of Object.entries(registry.rules)) for (const kind of rule.kinds) for (const key of Object.keys(paramsFor(rule, kind)?.entries ?? {})) if (isReservedRuleKey(key)) throw new TypeError(`Rule "${name}": params can't use the reserved key "${key}"`);
|
|
1112
|
+
for (const [name, rule] of Object.entries(registry.formRules)) for (const key of Object.keys(rule.params?.entries ?? {})) if (isReservedFormRuleKey(key)) throw new TypeError(`Form rule "${name}": params can't use the reserved key "${key}"`);
|
|
1028
1113
|
for (const [name, type] of Object.entries(registry.fieldTypes)) {
|
|
1029
1114
|
const at = `Field type "${name}"`;
|
|
1030
1115
|
for (const kind of [type.kind, type.itemKind]) if (typeof kind === "string" && !lookup(registry.kinds, kind)) throw new TypeError(`${at}: kind "${kind}" is not registered`);
|
|
@@ -1050,10 +1135,164 @@ function toIssues(code, path, valibotIssues) {
|
|
|
1050
1135
|
return issue(code, [...path, ...keys], valibotIssue.message);
|
|
1051
1136
|
});
|
|
1052
1137
|
}
|
|
1138
|
+
// src/builder/check-form-rules.ts
|
|
1139
|
+
const ruleShape = v.looseObject({
|
|
1140
|
+
rule: v.string(),
|
|
1141
|
+
field: v.string(),
|
|
1142
|
+
message: v.string()
|
|
1143
|
+
});
|
|
1144
|
+
/**
|
|
1145
|
+
* Checks the definition's form rules against the fields the compiler built, and adds their
|
|
1146
|
+
* issues to `issues`. Returns the rules that are ready to build.
|
|
1147
|
+
*/
|
|
1148
|
+
function checkFormRules(rules, registry, fieldsByPath, issues) {
|
|
1149
|
+
return rules.flatMap((raw, i) => checkFormRule(raw, ["formRules", i], registry, fieldsByPath, issues) ?? []);
|
|
1150
|
+
}
|
|
1151
|
+
function checkFormRule(raw, path, registry, fieldsByPath, issues) {
|
|
1152
|
+
const shape = v.safeParse(ruleShape, raw);
|
|
1153
|
+
if (!shape.success) {
|
|
1154
|
+
issues.push(...toIssues("invalid_shape", path, shape.issues));
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
const rule = shape.output;
|
|
1158
|
+
const at = `Form rule "${rule.rule}"`;
|
|
1159
|
+
const def = lookup(registry.formRules, rule.rule);
|
|
1160
|
+
if (!def) {
|
|
1161
|
+
issues.push(issue("unknown_rule", [...path, "rule"], `Unknown form rule "${rule.rule}"`));
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const params = checkParams(rule, def.params, isReservedFormRuleKey, at, path, issues);
|
|
1165
|
+
if (!params) return void 0;
|
|
1166
|
+
const dotPaths = [rule.field, ...def.reads(params)];
|
|
1167
|
+
const paths = dotPaths.map((dotPath) => dotPath.split("."));
|
|
1168
|
+
const fieldKeys = paths[0];
|
|
1169
|
+
const issueCount = issues.length;
|
|
1170
|
+
const fields = dotPaths.map((dotPath, i) => {
|
|
1171
|
+
const found = findField(dotPath, fieldsByPath);
|
|
1172
|
+
const message = typeof found === "string" ? found : i > 0 && outsideItem(paths[i], fieldKeys);
|
|
1173
|
+
if (!message) return found;
|
|
1174
|
+
issues.push(issue("unknown_field", i === 0 ? [...path, "field"] : path, message));
|
|
1175
|
+
return null;
|
|
1176
|
+
});
|
|
1177
|
+
if (issues.length > issueCount) return void 0;
|
|
1178
|
+
if (fields.some((field) => !field)) return void 0;
|
|
1179
|
+
const checked = fields;
|
|
1180
|
+
const kinds = checked.map((field) => field.valueKind);
|
|
1181
|
+
const wrong = kinds.find((kind) => def.kinds && !def.kinds.includes(kind));
|
|
1182
|
+
if (wrong !== void 0) {
|
|
1183
|
+
const message = `${at} can't be used on ${wrong} fields`;
|
|
1184
|
+
issues.push(issue("kind_mismatch", [...path, "rule"], message));
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
if (def.sameKind && new Set(kinds).size > 1) {
|
|
1188
|
+
const message = `${at} needs fields of one kind, not ${[...new Set(kinds)].join(" and ")}`;
|
|
1189
|
+
issues.push(issue("kind_mismatch", [...path, "rule"], message));
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
const field = checked[0].field;
|
|
1193
|
+
return {
|
|
1194
|
+
name: rule.rule,
|
|
1195
|
+
def,
|
|
1196
|
+
params,
|
|
1197
|
+
message: rule.message,
|
|
1198
|
+
path,
|
|
1199
|
+
field,
|
|
1200
|
+
paths
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* The field with a value at `dotPath`. Null when a field on the way has issues of its own, and
|
|
1205
|
+
* the issue's message when there is no such field.
|
|
1206
|
+
*/
|
|
1207
|
+
function findField(dotPath, fieldsByPath) {
|
|
1208
|
+
const keys = dotPath.split(".");
|
|
1209
|
+
let found;
|
|
1210
|
+
for (let i = 1; i <= keys.length; i++) {
|
|
1211
|
+
found = fieldsByPath.get(keys.slice(0, i).join("."));
|
|
1212
|
+
if (found === null) return null;
|
|
1213
|
+
if (found) continue;
|
|
1214
|
+
if (keys[i - 1] === "*" && i < keys.length) continue;
|
|
1215
|
+
if (fieldsByPath.get(keys.slice(0, i - 1).join("."))?.valueKind === "array" && keys[i - 1] !== "*") return `Unknown field "${dotPath}". A list's items are read with "*": "${[
|
|
1216
|
+
...keys.slice(0, i - 1),
|
|
1217
|
+
"*",
|
|
1218
|
+
...keys.slice(i - 1)
|
|
1219
|
+
].join(".")}"`;
|
|
1220
|
+
return `Unknown field "${dotPath}"`;
|
|
1221
|
+
}
|
|
1222
|
+
return found ?? `Unknown field "${dotPath}"`;
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* A path into the items of a list is read once per item, so `field` must be in the same items.
|
|
1226
|
+
* Returns the issue's message when it isn't.
|
|
1227
|
+
*/
|
|
1228
|
+
function outsideItem(keys, fieldKeys) {
|
|
1229
|
+
const star = keys.lastIndexOf("*");
|
|
1230
|
+
const item = (path) => path.slice(0, star + 1).join(".");
|
|
1231
|
+
if (star === -1 || item(keys) === item(fieldKeys)) return void 0;
|
|
1232
|
+
const list = keys.slice(0, star).join(".");
|
|
1233
|
+
return `"${keys.join(".")}" is read from each item of "${list}", so "${fieldKeys.join(".")}" must be too`;
|
|
1234
|
+
}
|
|
1235
|
+
// src/builder/form-rule-action.ts
|
|
1236
|
+
/**
|
|
1237
|
+
* Runs a form rule on the form's value and puts its issue on `field`. A rule on the fields of a
|
|
1238
|
+
* list's items runs once per item, with the item's index in place of each `'*'`.
|
|
1239
|
+
*
|
|
1240
|
+
* The rule only runs when none of the fields it reads has an issue. `v.partialCheck` can't do
|
|
1241
|
+
* this: it runs whenever the form has no schema issues, even when a field it reads failed a rule.
|
|
1242
|
+
*/
|
|
1243
|
+
function formRuleAction(rule, message) {
|
|
1244
|
+
const { def, params, paths } = rule;
|
|
1245
|
+
const fieldKeys = paths[0];
|
|
1246
|
+
return v.rawCheck(({ dataset, addIssue }) => {
|
|
1247
|
+
for (const indexes of itemIndexes(dataset.value, fieldKeys)) {
|
|
1248
|
+
const read = paths.map((keys) => withIndexes(keys, indexes));
|
|
1249
|
+
if (read.some((path) => hasIssue(dataset.issues, path))) continue;
|
|
1250
|
+
const values = read.map((path) => valueAt(dataset.value, path));
|
|
1251
|
+
if (values.includes(void 0) || def.check(values, params)) continue;
|
|
1252
|
+
const field = read[0];
|
|
1253
|
+
addIssue({
|
|
1254
|
+
message,
|
|
1255
|
+
input: values[0],
|
|
1256
|
+
path: issuePath(dataset.value, field)
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
/** Replaces each `'*'` in `keys` with the next index. */
|
|
1262
|
+
const withIndexes = (keys, indexes) => {
|
|
1263
|
+
let next = 0;
|
|
1264
|
+
return keys.map((key) => key === "*" ? indexes[next++] : key);
|
|
1265
|
+
};
|
|
1266
|
+
const valueAt = (input, path) => path.reduce((value, key) => typeof value === "object" && value !== null ? value[key] : void 0, input);
|
|
1267
|
+
/** True when an issue is on `path`, inside it, or on a value that holds it. */
|
|
1268
|
+
const hasIssue = (issues, path) => !!issues?.some((issue) => (issue.path ?? []).every((item, i) => i >= path.length || item.key === path[i]));
|
|
1269
|
+
/** Valibot's path to `path` in `input`, the way an object or array schema reports it. */
|
|
1270
|
+
const issuePath = (input, path) => path.map((key, i) => {
|
|
1271
|
+
const parent = valueAt(input, path.slice(0, i));
|
|
1272
|
+
return {
|
|
1273
|
+
type: Array.isArray(parent) ? "array" : "object",
|
|
1274
|
+
origin: "value",
|
|
1275
|
+
input: parent,
|
|
1276
|
+
key,
|
|
1277
|
+
value: valueAt(parent, [key])
|
|
1278
|
+
};
|
|
1279
|
+
});
|
|
1280
|
+
/** The indexes for each `'*'` in `keys`, for every item `value` has there. */
|
|
1281
|
+
function* itemIndexes(value, keys, indexes = []) {
|
|
1282
|
+
const star = keys.indexOf("*");
|
|
1283
|
+
if (star === -1) {
|
|
1284
|
+
yield indexes;
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const list = valueAt(value, keys.slice(0, star));
|
|
1288
|
+
if (!Array.isArray(list)) return;
|
|
1289
|
+
for (const [i, item] of list.entries()) yield* itemIndexes(item, keys.slice(star + 1), [...indexes, i]);
|
|
1290
|
+
}
|
|
1053
1291
|
// src/builder/compile-definition.ts
|
|
1054
1292
|
const formShape = v.object({
|
|
1055
1293
|
version: v.string(),
|
|
1056
|
-
fields: v.array(v.unknown())
|
|
1294
|
+
fields: v.array(v.unknown()),
|
|
1295
|
+
formRules: v.optional(v.array(v.unknown()))
|
|
1057
1296
|
});
|
|
1058
1297
|
/**
|
|
1059
1298
|
* Checks a definition and builds its schema in the same pass, so `validate()` and `build()`
|
|
@@ -1078,9 +1317,23 @@ function compileDefinition(json, registry, messages) {
|
|
|
1078
1317
|
schema: v.object({})
|
|
1079
1318
|
};
|
|
1080
1319
|
}
|
|
1320
|
+
const compiler = createCompiler(registry, messages, issues);
|
|
1321
|
+
const schema = compiler.fields(form.output.fields, ["fields"]);
|
|
1322
|
+
const rules = checkFormRules(form.output.formRules ?? [], registry, compiler.fieldsByPath, issues);
|
|
1323
|
+
if (issues.length || !rules.length) return {
|
|
1324
|
+
issues,
|
|
1325
|
+
schema
|
|
1326
|
+
};
|
|
1081
1327
|
return {
|
|
1082
1328
|
issues,
|
|
1083
|
-
schema:
|
|
1329
|
+
schema: rules.reduce((acc, rule) => {
|
|
1330
|
+
const ctx = {
|
|
1331
|
+
field: rule.field,
|
|
1332
|
+
rule: rule.name,
|
|
1333
|
+
path: rule.path
|
|
1334
|
+
};
|
|
1335
|
+
return v.pipe(acc, formRuleAction(rule, messages.resolve(rule.message, ctx)));
|
|
1336
|
+
}, schema)
|
|
1084
1337
|
};
|
|
1085
1338
|
}
|
|
1086
1339
|
/** Every field the compiler checks adds its issues to `issues`. */
|
|
@@ -1090,29 +1343,54 @@ function createCompiler(registry, messages, issues) {
|
|
|
1090
1343
|
buildFields: fields,
|
|
1091
1344
|
buildField: field
|
|
1092
1345
|
});
|
|
1346
|
+
const fieldsByPath = /* @__PURE__ */ new Map();
|
|
1347
|
+
const parents = [];
|
|
1348
|
+
const parentPath = () => parents.at(-1) ?? [];
|
|
1093
1349
|
function fields(list, path) {
|
|
1094
1350
|
const entries = {};
|
|
1095
1351
|
const names = /* @__PURE__ */ new Set();
|
|
1096
1352
|
list.forEach((raw, i) => {
|
|
1097
1353
|
const checked = checkField(raw, [...path, i], registry, names, issues);
|
|
1098
|
-
|
|
1354
|
+
const name = checked ? checked.name : nameOf(raw);
|
|
1355
|
+
if (name === void 0) return;
|
|
1356
|
+
const dataPath = [...parentPath(), name];
|
|
1357
|
+
remember(raw, checked, dataPath);
|
|
1358
|
+
if (checked) entries[name] = build(checked, dataPath);
|
|
1099
1359
|
});
|
|
1100
1360
|
return v.object(entries);
|
|
1101
1361
|
}
|
|
1102
1362
|
function field(raw, path) {
|
|
1103
1363
|
const checked = checkField(raw, path, registry, void 0, issues);
|
|
1104
|
-
|
|
1364
|
+
const dataPath = parentPath();
|
|
1365
|
+
remember(raw, checked, dataPath);
|
|
1366
|
+
return checked ? build(checked, dataPath) : v.never();
|
|
1105
1367
|
}
|
|
1106
|
-
function build(checked) {
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1368
|
+
function build(checked, dataPath) {
|
|
1369
|
+
parents.push(checked.valueKind === "array" ? [...dataPath, "*"] : dataPath);
|
|
1370
|
+
try {
|
|
1371
|
+
const schema = buildFieldSchema(checked, messages, context(checked.path));
|
|
1372
|
+
checkDefault(checked, schema, issues);
|
|
1373
|
+
return schema;
|
|
1374
|
+
} finally {
|
|
1375
|
+
parents.pop();
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
/** The first field at a path wins, as with duplicate names. Display elements are left out. */
|
|
1379
|
+
function remember(raw, checked, dataPath) {
|
|
1380
|
+
const key = dataPath.join(".");
|
|
1381
|
+
if (fieldsByPath.has(key) || !checked && !hasValue(raw, registry)) return;
|
|
1382
|
+
fieldsByPath.set(key, checked ?? null);
|
|
1110
1383
|
}
|
|
1111
1384
|
return {
|
|
1112
1385
|
fields,
|
|
1113
|
-
field
|
|
1386
|
+
field,
|
|
1387
|
+
fieldsByPath
|
|
1114
1388
|
};
|
|
1115
1389
|
}
|
|
1390
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
1391
|
+
const nameOf = (raw) => isRecord(raw) && typeof raw.name === "string" ? raw.name : void 0;
|
|
1392
|
+
/** An unknown type counts as one with a value, so a form rule that reads the field is fine. */
|
|
1393
|
+
const hasValue = (raw, registry) => isRecord(raw) && typeof raw.type === "string" && lookup(registry.fieldTypes, raw.type)?.kind !== "none";
|
|
1116
1394
|
/** A default must be a value the field accepts. An empty one is the same as none. */
|
|
1117
1395
|
function checkDefault({ field, kind, path }, schema, issues) {
|
|
1118
1396
|
if (field.default === void 0 || kind.isEmpty(field.default)) return;
|
|
@@ -1180,7 +1458,8 @@ function createSchemaBuilder(options = {}) {
|
|
|
1180
1458
|
rulesForKind,
|
|
1181
1459
|
kinds: registry.kinds,
|
|
1182
1460
|
fieldTypes: registry.fieldTypes,
|
|
1183
|
-
rules: registry.rules
|
|
1461
|
+
rules: registry.rules,
|
|
1462
|
+
formRules: registry.formRules
|
|
1184
1463
|
};
|
|
1185
1464
|
}
|
|
1186
1465
|
// src/definition/initial-input.ts
|
|
@@ -1239,6 +1518,6 @@ function* walkField(field, definitionPath, dataPath, depth) {
|
|
|
1239
1518
|
}
|
|
1240
1519
|
}
|
|
1241
1520
|
const isField = (value) => typeof value === "object" && value !== null && "type" in value;
|
|
1242
|
-
export { DefinitionError, RESERVED_FIELD_KEYS, RESERVED_RULE_KEYS, comparisonRules, contentRules, createBuildContext, createSchemaBuilder, dateRules, defaultFieldTypes, defaultKinds, defaultRules, defineFieldType, defineRule, fileRules, formatPath, formatRules, getInitialInput, lengthRules, normalizeRules, numberRules, textUnitRules, walkFields };
|
|
1521
|
+
export { DefinitionError, RESERVED_FIELD_KEYS, RESERVED_FORM_RULE_KEYS, RESERVED_RULE_KEYS, comparisonRules, contentRules, createBuildContext, createSchemaBuilder, dateRules, defaultFieldTypes, defaultFormRules, defaultKinds, defaultRules, defineFieldType, defineFormRule, defineRule, fileRules, formatPath, formatRules, getInitialInput, lengthRules, normalizeRules, numberRules, textUnitRules, walkFields };
|
|
1243
1522
|
|
|
1244
1523
|
//# sourceMappingURL=index.js.map
|