@ng-forge/dynamic-form-mcp 1.2.0-next.7 → 1.2.0-next.9
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/bin/ng-forge-mcp.js +349 -207
- package/package.json +7 -8
- package/src/index.js +362 -223
- package/src/registry/addons.d.ts.map +1 -1
- package/src/registry/field-types.d.ts.map +1 -1
- package/src/registry/instructions.d.ts +1 -1
- package/src/registry/instructions.d.ts.map +1 -1
- package/src/tools/data/lookup-topics.d.ts.map +1 -1
- package/src/tools/lookup.tool.d.ts.map +1 -1
- package/src/tools/search.tool.d.ts.map +1 -1
package/src/index.js
CHANGED
|
@@ -22,7 +22,7 @@ var FIELD_TYPES = [
|
|
|
22
22
|
},
|
|
23
23
|
validationSupported: true,
|
|
24
24
|
source: "adapter",
|
|
25
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
25
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
26
26
|
example: `{
|
|
27
27
|
key: 'email',
|
|
28
28
|
type: 'input',
|
|
@@ -57,7 +57,7 @@ var FIELD_TYPES = [
|
|
|
57
57
|
},
|
|
58
58
|
validationSupported: true,
|
|
59
59
|
source: "adapter",
|
|
60
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
60
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
61
61
|
example: `{
|
|
62
62
|
key: 'description',
|
|
63
63
|
type: 'textarea',
|
|
@@ -91,7 +91,7 @@ var FIELD_TYPES = [
|
|
|
91
91
|
},
|
|
92
92
|
validationSupported: true,
|
|
93
93
|
source: "adapter",
|
|
94
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
94
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
95
95
|
example: `{
|
|
96
96
|
key: 'country',
|
|
97
97
|
type: 'select',
|
|
@@ -113,7 +113,7 @@ var FIELD_TYPES = [
|
|
|
113
113
|
props: {},
|
|
114
114
|
validationSupported: true,
|
|
115
115
|
source: "adapter",
|
|
116
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
116
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
117
117
|
example: `{
|
|
118
118
|
key: 'acceptTerms',
|
|
119
119
|
type: 'checkbox',
|
|
@@ -138,7 +138,7 @@ var FIELD_TYPES = [
|
|
|
138
138
|
},
|
|
139
139
|
validationSupported: true,
|
|
140
140
|
source: "adapter",
|
|
141
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
141
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
142
142
|
example: `{
|
|
143
143
|
key: 'interests',
|
|
144
144
|
type: 'multi-checkbox',
|
|
@@ -168,7 +168,7 @@ var FIELD_TYPES = [
|
|
|
168
168
|
},
|
|
169
169
|
validationSupported: true,
|
|
170
170
|
source: "adapter",
|
|
171
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
171
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
172
172
|
example: `{
|
|
173
173
|
key: 'gender',
|
|
174
174
|
type: 'radio',
|
|
@@ -203,7 +203,7 @@ var FIELD_TYPES = [
|
|
|
203
203
|
},
|
|
204
204
|
validationSupported: true,
|
|
205
205
|
source: "adapter",
|
|
206
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
206
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
207
207
|
example: `{
|
|
208
208
|
key: 'birthDate',
|
|
209
209
|
type: 'datepicker',
|
|
@@ -222,7 +222,7 @@ var FIELD_TYPES = [
|
|
|
222
222
|
props: {},
|
|
223
223
|
validationSupported: true,
|
|
224
224
|
source: "adapter",
|
|
225
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
225
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
226
226
|
example: `{
|
|
227
227
|
key: 'notifications',
|
|
228
228
|
type: 'toggle',
|
|
@@ -261,7 +261,7 @@ var FIELD_TYPES = [
|
|
|
261
261
|
},
|
|
262
262
|
validationSupported: true,
|
|
263
263
|
source: "adapter",
|
|
264
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
264
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
265
265
|
example: `{
|
|
266
266
|
key: 'volume',
|
|
267
267
|
type: 'slider',
|
|
@@ -282,7 +282,7 @@ var FIELD_TYPES = [
|
|
|
282
282
|
props: {},
|
|
283
283
|
validationSupported: false,
|
|
284
284
|
source: "core",
|
|
285
|
-
allowedIn: ["top-level (single-page forms only)", "page.fields", "group.fields", "array.fields"],
|
|
285
|
+
allowedIn: ["top-level (single-page forms only)", "page.fields", "group.fields", "array.fields", "container"],
|
|
286
286
|
notAllowedIn: ["row", "top-level when using pages"],
|
|
287
287
|
example: `// Hidden field - value is REQUIRED!
|
|
288
288
|
{
|
|
@@ -319,7 +319,7 @@ var FIELD_TYPES = [
|
|
|
319
319
|
},
|
|
320
320
|
validationSupported: false,
|
|
321
321
|
source: "core",
|
|
322
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
322
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
323
323
|
example: `{
|
|
324
324
|
key: 'sectionTitle',
|
|
325
325
|
type: 'text',
|
|
@@ -422,11 +422,12 @@ var FIELD_TYPES = [
|
|
|
422
422
|
},
|
|
423
423
|
validationSupported: true,
|
|
424
424
|
source: "core",
|
|
425
|
-
allowedIn: ["top-level", "page", "row", "array"],
|
|
425
|
+
allowedIn: ["top-level", "page", "row", "array", "container"],
|
|
426
426
|
notAllowedIn: ["group"],
|
|
427
427
|
canContain: [
|
|
428
428
|
"row",
|
|
429
429
|
"array",
|
|
430
|
+
"container",
|
|
430
431
|
"input",
|
|
431
432
|
"textarea",
|
|
432
433
|
"select",
|
|
@@ -520,11 +521,12 @@ var FIELD_TYPES = [
|
|
|
520
521
|
},
|
|
521
522
|
validationSupported: true,
|
|
522
523
|
source: "core",
|
|
523
|
-
allowedIn: ["top-level", "page", "row", "group"],
|
|
524
|
+
allowedIn: ["top-level", "page", "row", "group", "container"],
|
|
524
525
|
notAllowedIn: ["array"],
|
|
525
526
|
canContain: [
|
|
526
527
|
"row",
|
|
527
528
|
"group",
|
|
529
|
+
"container",
|
|
528
530
|
"input",
|
|
529
531
|
"textarea",
|
|
530
532
|
"select",
|
|
@@ -586,6 +588,74 @@ var FIELD_TYPES = [
|
|
|
586
588
|
// ? { kind: 'periodOrder' } : null`,
|
|
587
589
|
minimalExample: `{ key: 'items', type: 'array', fields: [...] }`
|
|
588
590
|
},
|
|
591
|
+
{
|
|
592
|
+
type: "container",
|
|
593
|
+
category: "container",
|
|
594
|
+
description: "Wraps its children in UI chrome via a chain of wrapper components. Containers do NOT have a label property and do NOT create a nesting level in form values \u2014 child values are flattened into the parent, exactly like rows. The 'wrappers' property is REQUIRED (use [] for no chrome). A container is NOT a group with chrome and the two are not substitutable: a container flattens child values into the parent (like a row), while a group nests them under its own key and owns a schema path, so swapping one for the other changes the submitted value shape and which validators can run. Wrappers are applied outermost-first, so the first entry in the array is the outermost element. Supports only 'hidden' logic type for conditional visibility. Like `row` and `page`, a container flattens into its parent and has no schema path of its own, so it does NOT support container-level `validators`, `required` or `validationMessages` \u2014 use `group` or `array` for cross-field rules.",
|
|
595
|
+
valueType: void 0,
|
|
596
|
+
baseInterface: "FieldDef",
|
|
597
|
+
props: {
|
|
598
|
+
fields: {
|
|
599
|
+
name: "fields",
|
|
600
|
+
type: "ContainerAllowedChildren[]",
|
|
601
|
+
description: "Child fields to render inside the wrapper chain. Allowed: rows, groups, arrays, nested containers, hidden fields, and value fields (input, select, etc). NOT ALLOWED: pages",
|
|
602
|
+
required: true
|
|
603
|
+
},
|
|
604
|
+
wrappers: {
|
|
605
|
+
name: "wrappers",
|
|
606
|
+
type: "WrapperConfig[]",
|
|
607
|
+
description: "REQUIRED. Wrapper components chained around the children, outermost first. Use [] for no chrome. Each entry needs a 'type'; the built-in wrapper is 'css', which takes 'cssClasses'. Custom wrapper types can be registered.",
|
|
608
|
+
required: true
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
validationSupported: false,
|
|
612
|
+
source: "core",
|
|
613
|
+
allowedIn: ["top-level", "page", "group", "array", "row", "container"],
|
|
614
|
+
notAllowedIn: [],
|
|
615
|
+
canContain: [
|
|
616
|
+
"row",
|
|
617
|
+
"group",
|
|
618
|
+
"array",
|
|
619
|
+
"container",
|
|
620
|
+
"hidden",
|
|
621
|
+
"input",
|
|
622
|
+
"textarea",
|
|
623
|
+
"select",
|
|
624
|
+
"checkbox",
|
|
625
|
+
"multi-checkbox",
|
|
626
|
+
"radio",
|
|
627
|
+
"datepicker",
|
|
628
|
+
"toggle",
|
|
629
|
+
"slider",
|
|
630
|
+
"text",
|
|
631
|
+
"button",
|
|
632
|
+
"submit",
|
|
633
|
+
"next",
|
|
634
|
+
"previous",
|
|
635
|
+
"add-array-item",
|
|
636
|
+
"prepend-array-item",
|
|
637
|
+
"insert-array-item",
|
|
638
|
+
"remove-array-item",
|
|
639
|
+
"pop-array-item",
|
|
640
|
+
"shift-array-item"
|
|
641
|
+
],
|
|
642
|
+
cannotContain: ["page"],
|
|
643
|
+
example: `{
|
|
644
|
+
key: 'billingChrome',
|
|
645
|
+
type: 'container',
|
|
646
|
+
// NOTE: Containers do NOT have a label property
|
|
647
|
+
// wrappers is REQUIRED \u2014 use [] when no chrome is needed
|
|
648
|
+
wrappers: [{ type: 'css', cssClasses: 'card p-3' }],
|
|
649
|
+
fields: [
|
|
650
|
+
{ key: 'street', type: 'input', label: 'Street' },
|
|
651
|
+
{ key: 'city', type: 'input', label: 'City' }
|
|
652
|
+
]
|
|
653
|
+
}
|
|
654
|
+
// Values flatten: { street: '', city: '' } \u2014 no 'billingChrome' key
|
|
655
|
+
// NOT ALLOWED in containers:
|
|
656
|
+
// - type: 'page' (pages are top-level only)`,
|
|
657
|
+
minimalExample: `{ key: 'chrome', type: 'container', wrappers: [], fields: [...] }`
|
|
658
|
+
},
|
|
589
659
|
{
|
|
590
660
|
type: "page",
|
|
591
661
|
category: "container",
|
|
@@ -603,11 +673,12 @@ var FIELD_TYPES = [
|
|
|
603
673
|
validationSupported: false,
|
|
604
674
|
source: "core",
|
|
605
675
|
allowedIn: ["top-level"],
|
|
606
|
-
notAllowedIn: ["page", "row", "group", "array"],
|
|
676
|
+
notAllowedIn: ["page", "row", "group", "array", "container"],
|
|
607
677
|
canContain: [
|
|
608
678
|
"row",
|
|
609
679
|
"group",
|
|
610
680
|
"array",
|
|
681
|
+
"container",
|
|
611
682
|
"input",
|
|
612
683
|
"textarea",
|
|
613
684
|
"select",
|
|
@@ -647,7 +718,7 @@ var FIELD_TYPES = [
|
|
|
647
718
|
props: {},
|
|
648
719
|
validationSupported: false,
|
|
649
720
|
source: "adapter",
|
|
650
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
721
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
651
722
|
example: `{
|
|
652
723
|
key: 'next',
|
|
653
724
|
type: 'next',
|
|
@@ -664,7 +735,7 @@ var FIELD_TYPES = [
|
|
|
664
735
|
props: {},
|
|
665
736
|
validationSupported: false,
|
|
666
737
|
source: "adapter",
|
|
667
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
738
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
668
739
|
example: `{
|
|
669
740
|
key: 'back',
|
|
670
741
|
type: 'previous',
|
|
@@ -688,7 +759,7 @@ var FIELD_TYPES = [
|
|
|
688
759
|
},
|
|
689
760
|
validationSupported: false,
|
|
690
761
|
source: "adapter",
|
|
691
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
762
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
692
763
|
example: `// RECOMMENDED: Use pre-defined button types for common actions
|
|
693
764
|
{ key: 'submit', type: 'submit', label: 'Submit Form' }
|
|
694
765
|
{ key: 'next', type: 'next', label: 'Next' }
|
|
@@ -715,7 +786,7 @@ var FIELD_TYPES = [
|
|
|
715
786
|
props: {},
|
|
716
787
|
validationSupported: false,
|
|
717
788
|
source: "adapter",
|
|
718
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
789
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
719
790
|
example: `{
|
|
720
791
|
key: 'submit',
|
|
721
792
|
type: 'submit',
|
|
@@ -732,7 +803,7 @@ var FIELD_TYPES = [
|
|
|
732
803
|
props: {},
|
|
733
804
|
validationSupported: false,
|
|
734
805
|
source: "adapter",
|
|
735
|
-
allowedIn: ["array", "row", "group"],
|
|
806
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
736
807
|
example: `{
|
|
737
808
|
key: 'addContact',
|
|
738
809
|
type: 'add-array-item',
|
|
@@ -749,7 +820,7 @@ var FIELD_TYPES = [
|
|
|
749
820
|
props: {},
|
|
750
821
|
validationSupported: false,
|
|
751
822
|
source: "adapter",
|
|
752
|
-
allowedIn: ["array", "row", "group"],
|
|
823
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
753
824
|
example: `{
|
|
754
825
|
key: 'prependContact',
|
|
755
826
|
type: 'prepend-array-item',
|
|
@@ -766,7 +837,7 @@ var FIELD_TYPES = [
|
|
|
766
837
|
props: {},
|
|
767
838
|
validationSupported: false,
|
|
768
839
|
source: "adapter",
|
|
769
|
-
allowedIn: ["array", "row", "group"],
|
|
840
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
770
841
|
example: `{
|
|
771
842
|
key: 'insertContact',
|
|
772
843
|
type: 'insert-array-item',
|
|
@@ -784,7 +855,7 @@ var FIELD_TYPES = [
|
|
|
784
855
|
props: {},
|
|
785
856
|
validationSupported: false,
|
|
786
857
|
source: "adapter",
|
|
787
|
-
allowedIn: ["array", "row", "group"],
|
|
858
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
788
859
|
example: `{
|
|
789
860
|
key: 'removeContact',
|
|
790
861
|
type: 'remove-array-item',
|
|
@@ -801,7 +872,7 @@ var FIELD_TYPES = [
|
|
|
801
872
|
props: {},
|
|
802
873
|
validationSupported: false,
|
|
803
874
|
source: "adapter",
|
|
804
|
-
allowedIn: ["array", "row", "group"],
|
|
875
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
805
876
|
example: `{
|
|
806
877
|
key: 'popContact',
|
|
807
878
|
type: 'pop-array-item',
|
|
@@ -819,7 +890,7 @@ var FIELD_TYPES = [
|
|
|
819
890
|
props: {},
|
|
820
891
|
validationSupported: false,
|
|
821
892
|
source: "adapter",
|
|
822
|
-
allowedIn: ["array", "row", "group"],
|
|
893
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
823
894
|
example: `{
|
|
824
895
|
key: 'shiftContact',
|
|
825
896
|
type: 'shift-array-item',
|
|
@@ -1949,7 +2020,7 @@ var ADDON_TYPES = [
|
|
|
1949
2020
|
preset: {
|
|
1950
2021
|
name: "preset",
|
|
1951
2022
|
type: "'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility' | string",
|
|
1952
|
-
description: "Built-in preset action. Mutually exclusive with `actionRef` and `action`.",
|
|
2023
|
+
description: "Built-in preset action for Material input fields. Other Material addon-capable fields should use `actionRef` or `action`. Mutually exclusive with `actionRef` and `action`.",
|
|
1953
2024
|
required: false
|
|
1954
2025
|
},
|
|
1955
2026
|
actionRef: {
|
|
@@ -2140,6 +2211,21 @@ var FIELD_ADDON_SUPPORT = [
|
|
|
2140
2211
|
adapter: "material",
|
|
2141
2212
|
slots: ["prefix", "suffix"]
|
|
2142
2213
|
},
|
|
2214
|
+
{
|
|
2215
|
+
fieldType: "select",
|
|
2216
|
+
adapter: "material",
|
|
2217
|
+
slots: ["prefix", "suffix"]
|
|
2218
|
+
},
|
|
2219
|
+
{
|
|
2220
|
+
fieldType: "textarea",
|
|
2221
|
+
adapter: "material",
|
|
2222
|
+
slots: ["prefix", "suffix"]
|
|
2223
|
+
},
|
|
2224
|
+
{
|
|
2225
|
+
fieldType: "datepicker",
|
|
2226
|
+
adapter: "material",
|
|
2227
|
+
slots: ["prefix", "suffix"]
|
|
2228
|
+
},
|
|
2143
2229
|
{
|
|
2144
2230
|
fieldType: "input",
|
|
2145
2231
|
adapter: "bootstrap",
|
|
@@ -2921,6 +3007,34 @@ Paged forms may virtualize inactive pages. DynamicForm \`(initialized)\` emits a
|
|
|
2921
3007
|
the initially active page is ready. Use \`(activePageInitialized)\` when work must run
|
|
2922
3008
|
after every visible-page transition; it announces once per visit.
|
|
2923
3009
|
|
|
3010
|
+
### Rendering Performance
|
|
3011
|
+
|
|
3012
|
+
Use \`options.pagePreloadWindow\` for paged forms. It controls how many neighbouring
|
|
3013
|
+
pages are mounted and have their component chunks preloaded. \`0\` keeps only the
|
|
3014
|
+
active page in the render window; the default is \`1\`.
|
|
3015
|
+
|
|
3016
|
+
Use \`options.fieldWindowing\` for large flat forms:
|
|
3017
|
+
|
|
3018
|
+
\`\`\`typescript
|
|
3019
|
+
{
|
|
3020
|
+
options: {
|
|
3021
|
+
fieldWindowing: {
|
|
3022
|
+
eager: 20,
|
|
3023
|
+
placeholderHeight: '4rem',
|
|
3024
|
+
park: { margin: '100%' },
|
|
3025
|
+
},
|
|
3026
|
+
},
|
|
3027
|
+
fields: [ /* flat fields */ ],
|
|
3028
|
+
}
|
|
3029
|
+
\`\`\`
|
|
3030
|
+
|
|
3031
|
+
- \`eager\` mounts that many leading leaf fields immediately. Remaining fields mount near the viewport.
|
|
3032
|
+
- \`placeholderHeight\` reserves layout space before a field mounts.
|
|
3033
|
+
- \`park\` keeps mounted offscreen DOM in place but removes its view from routine change detection. Safety-related state such as disabled, readonly, required, and validation state stays current. Other model-to-DOM updates catch up when the field returns.
|
|
3034
|
+
- \`park.margin\` follows \`IntersectionObserver.rootMargin\`: use one to four \`px\` or \`%\` values. Unsupported units fall back to the inherited margin.
|
|
3035
|
+
- \`fieldWindowing: false\` disables inherited deferred mounting for that form.
|
|
3036
|
+
- A park-only object changes parking without changing the inherited mounting mode. Use \`{ park: false }\` to opt out or \`{ park: true }\` to opt in.
|
|
3037
|
+
|
|
2924
3038
|
## Validation
|
|
2925
3039
|
|
|
2926
3040
|
### Shorthand Validators (Preferred)
|
|
@@ -4813,15 +4927,23 @@ var ContainerLogicSchema = z13.object({
|
|
|
4813
4927
|
type: z13.literal("hidden"),
|
|
4814
4928
|
condition: z13.union([ConditionalExpressionSchema, z13.boolean()])
|
|
4815
4929
|
});
|
|
4930
|
+
var SIMPLIFIED_ONLY_PROPS = ["template", "value", "addButton", "removeButton"];
|
|
4931
|
+
var SIMPLIFIED_ONLY_HINTS = {
|
|
4932
|
+
template: 'Use "fields" for the full API, or "template" for the simplified one.',
|
|
4933
|
+
value: 'Set initial values on the field definitions inside "fields".',
|
|
4934
|
+
addButton: 'Add an "add-array-item" or "insert-array-item" button as a field.',
|
|
4935
|
+
removeButton: 'Add a "remove-array-item" button to the item definition.'
|
|
4936
|
+
};
|
|
4816
4937
|
function createContainerSchemas(options) {
|
|
4817
4938
|
const { leafFieldSchema } = options;
|
|
4818
4939
|
const AnyFieldSchema = z13.lazy(
|
|
4819
|
-
() => z13.
|
|
4940
|
+
() => z13.discriminatedUnion("type", [
|
|
4820
4941
|
leafFieldSchema,
|
|
4821
4942
|
PageFieldSchema,
|
|
4822
4943
|
RowFieldSchema,
|
|
4823
4944
|
GroupFieldSchema,
|
|
4824
|
-
ArrayFieldSchema
|
|
4945
|
+
ArrayFieldSchema,
|
|
4946
|
+
ContainerFieldSchema
|
|
4825
4947
|
])
|
|
4826
4948
|
);
|
|
4827
4949
|
const ContainerBaseSchema = BaseFieldDefSchema.omit({
|
|
@@ -4833,6 +4955,18 @@ function createContainerSchemas(options) {
|
|
|
4833
4955
|
label: z13.never().optional(),
|
|
4834
4956
|
meta: z13.never().optional()
|
|
4835
4957
|
});
|
|
4958
|
+
const ContainerValidationShape = {
|
|
4959
|
+
required: z13.boolean().optional(),
|
|
4960
|
+
validators: ValidatorsArraySchema.optional(),
|
|
4961
|
+
validationMessages: ValidationMessagesSchema.optional()
|
|
4962
|
+
};
|
|
4963
|
+
const WrapperConfigSchema = z13.object({ type: z13.string() }).passthrough();
|
|
4964
|
+
const ContainerFieldSchema = ContainerBaseSchema.extend({
|
|
4965
|
+
type: z13.literal("container"),
|
|
4966
|
+
fields: z13.array(AnyFieldSchema),
|
|
4967
|
+
wrappers: z13.array(WrapperConfigSchema),
|
|
4968
|
+
logic: z13.array(ContainerLogicSchema).optional()
|
|
4969
|
+
});
|
|
4836
4970
|
const PageFieldSchema = ContainerBaseSchema.extend({
|
|
4837
4971
|
type: z13.literal("page"),
|
|
4838
4972
|
fields: z13.array(AnyFieldSchema),
|
|
@@ -4849,33 +4983,27 @@ function createContainerSchemas(options) {
|
|
|
4849
4983
|
const GroupFieldSchema = ContainerBaseSchema.extend({
|
|
4850
4984
|
type: z13.literal("group"),
|
|
4851
4985
|
fields: z13.array(AnyFieldSchema),
|
|
4986
|
+
...ContainerValidationShape,
|
|
4852
4987
|
// Container logic - only 'hidden' type allowed (same as pages)
|
|
4853
4988
|
logic: z13.array(ContainerLogicSchema).optional()
|
|
4854
4989
|
});
|
|
4855
4990
|
const ArrayButtonConfigSchema = z13.object({
|
|
4856
4991
|
label: z13.string().optional(),
|
|
4857
|
-
props: z13.record(z13.unknown()).optional()
|
|
4858
|
-
});
|
|
4859
|
-
const FullArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4860
|
-
type: z13.literal("array"),
|
|
4861
|
-
fields: z13.array(AnyFieldSchema),
|
|
4862
|
-
logic: z13.array(ContainerLogicSchema).optional(),
|
|
4863
|
-
// Full API does not use template
|
|
4864
|
-
template: z13.never().optional(),
|
|
4865
|
-
// Array length validation
|
|
4866
|
-
minLength: z13.number().int().min(0).optional(),
|
|
4867
|
-
maxLength: z13.number().int().min(0).optional()
|
|
4992
|
+
props: z13.record(z13.string(), z13.unknown()).optional()
|
|
4868
4993
|
});
|
|
4869
4994
|
const ArrayAllowedChildSchema = z13.lazy(
|
|
4870
4995
|
() => AnyFieldSchema.refine((field) => field.type !== "page" && field.type !== "array", {
|
|
4871
4996
|
message: "Array templates cannot contain page or array fields. Only leaf fields, rows, and groups are allowed."
|
|
4872
4997
|
})
|
|
4873
4998
|
);
|
|
4874
|
-
const
|
|
4999
|
+
const ArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4875
5000
|
type: z13.literal("array"),
|
|
4876
|
-
|
|
5001
|
+
...ContainerValidationShape,
|
|
5002
|
+
// Full API: explicit item definitions.
|
|
5003
|
+
fields: z13.array(AnyFieldSchema).optional(),
|
|
5004
|
+
// Simplified API: single field (primitive array) or array of fields (object array).
|
|
4877
5005
|
// Only ArrayAllowedChildren (leaf fields, rows, groups) are valid — no pages or nested arrays.
|
|
4878
|
-
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]),
|
|
5006
|
+
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]).optional(),
|
|
4879
5007
|
// Initial values for the array
|
|
4880
5008
|
value: z13.array(z13.unknown()).optional(),
|
|
4881
5009
|
// Button customization or opt-out (false to disable)
|
|
@@ -4885,18 +5013,64 @@ function createContainerSchemas(options) {
|
|
|
4885
5013
|
// Array length validation
|
|
4886
5014
|
minLength: z13.number().int().min(0).optional(),
|
|
4887
5015
|
maxLength: z13.number().int().min(0).optional(),
|
|
4888
|
-
//
|
|
4889
|
-
|
|
5016
|
+
// The array size properties are minLength/maxLength. These are the common
|
|
5017
|
+
// wrong spelling, and naming them keeps the mistake from being silently
|
|
5018
|
+
// stripped. Previously only the simplified API rejected them.
|
|
4890
5019
|
minItems: z13.never().optional(),
|
|
4891
5020
|
maxItems: z13.never().optional()
|
|
5021
|
+
}).superRefine((field, ctx) => {
|
|
5022
|
+
const hasFields = field.fields !== void 0;
|
|
5023
|
+
const hasTemplate = field.template !== void 0;
|
|
5024
|
+
if (hasFields && hasTemplate) {
|
|
5025
|
+
ctx.addIssue({
|
|
5026
|
+
code: "custom",
|
|
5027
|
+
message: 'Array has BOTH "fields" and "template". These are mutually exclusive: use "fields" for the full API, or "template" + "value" for the simplified API.'
|
|
5028
|
+
});
|
|
5029
|
+
return;
|
|
5030
|
+
}
|
|
5031
|
+
if (!hasFields && !hasTemplate) {
|
|
5032
|
+
ctx.addIssue({
|
|
5033
|
+
code: "custom",
|
|
5034
|
+
message: 'Array is MISSING both "fields" and "template". Use "fields" (full API) or "template" + "value" (simplified API).'
|
|
5035
|
+
});
|
|
5036
|
+
return;
|
|
5037
|
+
}
|
|
5038
|
+
if (hasFields) {
|
|
5039
|
+
for (const prop of SIMPLIFIED_ONLY_PROPS) {
|
|
5040
|
+
if (prop === "template" || field[prop] === void 0) continue;
|
|
5041
|
+
ctx.addIssue({
|
|
5042
|
+
code: "custom",
|
|
5043
|
+
path: [prop],
|
|
5044
|
+
message: `Array uses the full API ("fields"), so "${prop}" is not allowed: it belongs to the simplified API. ${SIMPLIFIED_ONLY_HINTS[prop]}`
|
|
5045
|
+
});
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
}).meta({
|
|
5049
|
+
// The refinement above is invisible to JSON Schema generation, and the
|
|
5050
|
+
// generated schema is authoring guidance for a model. Two optional
|
|
5051
|
+
// properties with no stated relationship reads as "pass either, both or
|
|
5052
|
+
// neither", which is exactly the mistake the refinement rejects at
|
|
5053
|
+
// runtime. Restating the rule as `oneOf` keeps the published schema
|
|
5054
|
+
// saying what the validator enforces — the union of two array schemas
|
|
5055
|
+
// used to express this, and collapsing them to one schema lost it.
|
|
5056
|
+
description: 'Array field. Use EXACTLY ONE of "fields" (full API: explicit item definitions) or "template" (simplified API: one field, or an array of fields, repeated per item). Never both, never neither. "value", "addButton" and "removeButton" belong to the simplified API only: with "fields", put initial values on the item definitions and add buttons as fields.',
|
|
5057
|
+
oneOf: [
|
|
5058
|
+
{
|
|
5059
|
+
required: ["fields"],
|
|
5060
|
+
// `not` wraps an `anyOf`, not one multi-name `required`: the latter
|
|
5061
|
+
// would only forbid all of them being present at once.
|
|
5062
|
+
not: { anyOf: SIMPLIFIED_ONLY_PROPS.map((prop) => ({ required: [prop] })) }
|
|
5063
|
+
},
|
|
5064
|
+
{ required: ["template"], not: { required: ["fields"] } }
|
|
5065
|
+
]
|
|
4892
5066
|
});
|
|
4893
|
-
const ArrayFieldSchema = z13.union([FullArrayFieldSchema, SimplifiedArrayFieldSchema]);
|
|
4894
5067
|
const AllFieldsSchema = AnyFieldSchema;
|
|
4895
5068
|
return {
|
|
4896
5069
|
PageFieldSchema,
|
|
4897
5070
|
RowFieldSchema,
|
|
4898
5071
|
GroupFieldSchema,
|
|
4899
5072
|
ArrayFieldSchema,
|
|
5073
|
+
ContainerFieldSchema,
|
|
4900
5074
|
AllFieldsSchema
|
|
4901
5075
|
};
|
|
4902
5076
|
}
|
|
@@ -5106,7 +5280,7 @@ var BaseFormConfigSchema = z18.object({
|
|
|
5106
5280
|
});
|
|
5107
5281
|
|
|
5108
5282
|
// internal/dynamic-forms-validation/validate/src/json-schema/form-config-json-schema.ts
|
|
5109
|
-
import
|
|
5283
|
+
import { z as z55 } from "zod";
|
|
5110
5284
|
|
|
5111
5285
|
// internal/dynamic-forms-validation/material/src/props/mat-common-props.schema.ts
|
|
5112
5286
|
import { z as z19 } from "zod";
|
|
@@ -6604,24 +6778,20 @@ var formConfigSchemas = {
|
|
|
6604
6778
|
primeng: PrimeFormConfigSchema,
|
|
6605
6779
|
ionic: IonicFormConfigSchema
|
|
6606
6780
|
};
|
|
6781
|
+
function toJsonSchema(schema, name, basePath) {
|
|
6782
|
+
const jsonSchema = z55.toJSONSchema(schema, { target: "draft-7", io: "input", unrepresentable: "any" });
|
|
6783
|
+
jsonSchema["$id"] = basePath ? `${basePath.replace(/\/$/, "")}/${name}` : name;
|
|
6784
|
+
return jsonSchema;
|
|
6785
|
+
}
|
|
6607
6786
|
function getFormConfigJsonSchema(uiIntegration, options = {}) {
|
|
6608
6787
|
const schema = formConfigSchemas[uiIntegration];
|
|
6609
6788
|
if (!schema) {
|
|
6610
6789
|
throw new Error(`Unknown UI integration: ${uiIntegration}. Valid options: material, bootstrap, primeng, ionic`);
|
|
6611
6790
|
}
|
|
6612
|
-
|
|
6613
|
-
const jsonSchema = zodToJsonSchema(schema, {
|
|
6614
|
-
name: name ?? `${uiIntegration}FormConfig`,
|
|
6615
|
-
basePath: basePath ? basePath.split("/") : void 0,
|
|
6616
|
-
$refStrategy: "none",
|
|
6617
|
-
// Inline all definitions for MCP compatibility
|
|
6618
|
-
errorMessages: true
|
|
6619
|
-
});
|
|
6620
|
-
return jsonSchema;
|
|
6791
|
+
return toJsonSchema(schema, options.name ?? `${uiIntegration}FormConfig`, options.basePath);
|
|
6621
6792
|
}
|
|
6622
6793
|
|
|
6623
6794
|
// internal/dynamic-forms-validation/validate/src/json-schema/field-type-json-schemas.ts
|
|
6624
|
-
import zodToJsonSchema2 from "zod-to-json-schema";
|
|
6625
6795
|
var leafFieldSchemas = {
|
|
6626
6796
|
material: MatLeafFieldSchema,
|
|
6627
6797
|
bootstrap: BsLeafFieldSchema,
|
|
@@ -6716,53 +6886,41 @@ function getFieldTypeJsonSchema(uiIntegration, fieldType) {
|
|
|
6716
6886
|
if (!schema) {
|
|
6717
6887
|
throw new Error(`Unknown field type: ${fieldType}. Valid: ${getSupportedFieldTypes().join(", ")}`);
|
|
6718
6888
|
}
|
|
6719
|
-
|
|
6720
|
-
name: `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`,
|
|
6721
|
-
$refStrategy: "none",
|
|
6722
|
-
errorMessages: true
|
|
6723
|
-
});
|
|
6724
|
-
return jsonSchema;
|
|
6889
|
+
return toJsonSchema(schema, `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`);
|
|
6725
6890
|
}
|
|
6726
6891
|
function getLeafFieldJsonSchema(uiIntegration) {
|
|
6727
6892
|
const schema = leafFieldSchemas[uiIntegration];
|
|
6728
6893
|
if (!schema) {
|
|
6729
6894
|
throw new Error(`Unknown UI integration: ${uiIntegration}`);
|
|
6730
6895
|
}
|
|
6731
|
-
|
|
6732
|
-
name: `${uiIntegration}LeafField`,
|
|
6733
|
-
$refStrategy: "none",
|
|
6734
|
-
errorMessages: true
|
|
6735
|
-
});
|
|
6736
|
-
return jsonSchema;
|
|
6896
|
+
return toJsonSchema(schema, `${uiIntegration}LeafField`);
|
|
6737
6897
|
}
|
|
6738
6898
|
|
|
6739
6899
|
// internal/dynamic-forms-validation/src/lib/schemas/field-type-names.ts
|
|
6740
|
-
import { z as
|
|
6900
|
+
import { z as z56 } from "zod";
|
|
6741
6901
|
var MAX_DEPTH = 20;
|
|
6742
6902
|
function collectDiscriminator(schema, into) {
|
|
6743
|
-
if (schema instanceof
|
|
6744
|
-
|
|
6903
|
+
if (schema instanceof z56.ZodLiteral) {
|
|
6904
|
+
for (const value of schema.def.values) {
|
|
6905
|
+
if (typeof value === "string") into.add(value);
|
|
6906
|
+
}
|
|
6745
6907
|
return;
|
|
6746
6908
|
}
|
|
6747
|
-
if (schema instanceof
|
|
6909
|
+
if (schema instanceof z56.ZodEnum) {
|
|
6748
6910
|
for (const value of schema.options) into.add(value);
|
|
6749
6911
|
}
|
|
6750
6912
|
}
|
|
6751
6913
|
function visit(schema, into, depth) {
|
|
6752
6914
|
if (depth > MAX_DEPTH) return;
|
|
6753
|
-
if (schema instanceof
|
|
6754
|
-
visit(schema.
|
|
6915
|
+
if (schema instanceof z56.ZodLazy) {
|
|
6916
|
+
visit(schema.unwrap(), into, depth + 1);
|
|
6755
6917
|
return;
|
|
6756
6918
|
}
|
|
6757
|
-
if (schema instanceof
|
|
6758
|
-
visit(schema.innerType(), into, depth + 1);
|
|
6759
|
-
return;
|
|
6760
|
-
}
|
|
6761
|
-
if (schema instanceof z55.ZodDiscriminatedUnion || schema instanceof z55.ZodUnion) {
|
|
6919
|
+
if (schema instanceof z56.ZodUnion) {
|
|
6762
6920
|
for (const option of schema.options) visit(option, into, depth + 1);
|
|
6763
6921
|
return;
|
|
6764
6922
|
}
|
|
6765
|
-
if (schema instanceof
|
|
6923
|
+
if (schema instanceof z56.ZodObject) {
|
|
6766
6924
|
collectDiscriminator(schema.shape["type"], into);
|
|
6767
6925
|
return;
|
|
6768
6926
|
}
|
|
@@ -6835,14 +6993,14 @@ var DID_YOU_MEAN = {
|
|
|
6835
6993
|
validation: "Did you mean to use validator shorthand (`required`, `email`, `min`, etc.) or `validators: [...]` array?"
|
|
6836
6994
|
};
|
|
6837
6995
|
function formatZodError(error, uiIntegration, config) {
|
|
6838
|
-
const errors = error.
|
|
6996
|
+
const errors = error.issues.map((err) => {
|
|
6839
6997
|
const path = err.path.join(".") || "root";
|
|
6840
6998
|
let message = err.message;
|
|
6841
6999
|
let expected;
|
|
6842
7000
|
let received;
|
|
6843
7001
|
if ("expected" in err) expected = String(err.expected);
|
|
6844
7002
|
if ("received" in err) received = String(err.received);
|
|
6845
|
-
if (
|
|
7003
|
+
if (err.code === "invalid_union") {
|
|
6846
7004
|
const fieldMatch = path.match(/fields\.(\d+)/);
|
|
6847
7005
|
if (fieldMatch && config && typeof config === "object" && "fields" in config) {
|
|
6848
7006
|
const fields = config.fields;
|
|
@@ -6973,123 +7131,31 @@ var FORBIDDEN_HIDDEN_PROPS = [
|
|
|
6973
7131
|
"pattern",
|
|
6974
7132
|
"placeholder"
|
|
6975
7133
|
];
|
|
6976
|
-
var CONTAINER_FORBIDDEN_PROPS = ["
|
|
7134
|
+
var CONTAINER_FORBIDDEN_PROPS = ["email", "min", "max", "pattern", "value"];
|
|
7135
|
+
var VALIDATING_CONTAINERS = ["group", "array"];
|
|
7136
|
+
var CONTAINER_VALIDATION_PROPS = ["validators", "required", "validationMessages"];
|
|
7137
|
+
var ARRAY_SIZE_PROPS = ["minLength", "maxLength"];
|
|
6977
7138
|
var FIELDS_REQUIRING_OPTIONS = ["select", "radio", "multi-checkbox"];
|
|
6978
7139
|
var NESTING_RULES = {
|
|
6979
7140
|
page: {
|
|
6980
|
-
allowed: [
|
|
6981
|
-
"row",
|
|
6982
|
-
"group",
|
|
6983
|
-
"array",
|
|
6984
|
-
"input",
|
|
6985
|
-
"textarea",
|
|
6986
|
-
"select",
|
|
6987
|
-
"checkbox",
|
|
6988
|
-
"multi-checkbox",
|
|
6989
|
-
"radio",
|
|
6990
|
-
"datepicker",
|
|
6991
|
-
"toggle",
|
|
6992
|
-
"slider",
|
|
6993
|
-
"hidden",
|
|
6994
|
-
"text",
|
|
6995
|
-
"button",
|
|
6996
|
-
"submit",
|
|
6997
|
-
"next",
|
|
6998
|
-
"previous",
|
|
6999
|
-
"add-array-item",
|
|
7000
|
-
"prepend-array-item",
|
|
7001
|
-
"insert-array-item",
|
|
7002
|
-
"remove-array-item",
|
|
7003
|
-
"pop-array-item",
|
|
7004
|
-
"shift-array-item"
|
|
7005
|
-
],
|
|
7006
7141
|
forbidden: ["page"],
|
|
7007
7142
|
message: "Pages cannot be nested inside other containers. ALL top-level fields must be pages if using multi-page mode."
|
|
7008
7143
|
},
|
|
7009
7144
|
row: {
|
|
7010
|
-
allowed: [
|
|
7011
|
-
"group",
|
|
7012
|
-
"array",
|
|
7013
|
-
"input",
|
|
7014
|
-
"textarea",
|
|
7015
|
-
"select",
|
|
7016
|
-
"checkbox",
|
|
7017
|
-
"multi-checkbox",
|
|
7018
|
-
"radio",
|
|
7019
|
-
"datepicker",
|
|
7020
|
-
"toggle",
|
|
7021
|
-
"slider",
|
|
7022
|
-
"text",
|
|
7023
|
-
"button",
|
|
7024
|
-
"submit",
|
|
7025
|
-
"next",
|
|
7026
|
-
"previous",
|
|
7027
|
-
"add-array-item",
|
|
7028
|
-
"prepend-array-item",
|
|
7029
|
-
"insert-array-item",
|
|
7030
|
-
"remove-array-item",
|
|
7031
|
-
"pop-array-item",
|
|
7032
|
-
"shift-array-item"
|
|
7033
|
-
],
|
|
7034
7145
|
forbidden: ["page", "row", "hidden"],
|
|
7035
7146
|
message: "Rows cannot contain pages, other rows, or hidden fields. Hidden fields should be at page or form level."
|
|
7036
7147
|
},
|
|
7037
7148
|
group: {
|
|
7038
|
-
allowed: [
|
|
7039
|
-
"row",
|
|
7040
|
-
"input",
|
|
7041
|
-
"textarea",
|
|
7042
|
-
"select",
|
|
7043
|
-
"checkbox",
|
|
7044
|
-
"multi-checkbox",
|
|
7045
|
-
"radio",
|
|
7046
|
-
"datepicker",
|
|
7047
|
-
"toggle",
|
|
7048
|
-
"slider",
|
|
7049
|
-
"hidden",
|
|
7050
|
-
"text",
|
|
7051
|
-
"button",
|
|
7052
|
-
"submit",
|
|
7053
|
-
"next",
|
|
7054
|
-
"previous",
|
|
7055
|
-
"add-array-item",
|
|
7056
|
-
"prepend-array-item",
|
|
7057
|
-
"insert-array-item",
|
|
7058
|
-
"remove-array-item",
|
|
7059
|
-
"pop-array-item",
|
|
7060
|
-
"shift-array-item"
|
|
7061
|
-
],
|
|
7062
7149
|
forbidden: ["page", "group"],
|
|
7063
7150
|
message: "Groups cannot contain pages or other groups (no nested groups)."
|
|
7064
7151
|
},
|
|
7065
7152
|
array: {
|
|
7066
|
-
allowed: [
|
|
7067
|
-
"row",
|
|
7068
|
-
"group",
|
|
7069
|
-
"input",
|
|
7070
|
-
"textarea",
|
|
7071
|
-
"select",
|
|
7072
|
-
"checkbox",
|
|
7073
|
-
"multi-checkbox",
|
|
7074
|
-
"radio",
|
|
7075
|
-
"datepicker",
|
|
7076
|
-
"toggle",
|
|
7077
|
-
"slider",
|
|
7078
|
-
"hidden",
|
|
7079
|
-
"text",
|
|
7080
|
-
"button",
|
|
7081
|
-
"submit",
|
|
7082
|
-
"next",
|
|
7083
|
-
"previous",
|
|
7084
|
-
"add-array-item",
|
|
7085
|
-
"prepend-array-item",
|
|
7086
|
-
"insert-array-item",
|
|
7087
|
-
"remove-array-item",
|
|
7088
|
-
"pop-array-item",
|
|
7089
|
-
"shift-array-item"
|
|
7090
|
-
],
|
|
7091
7153
|
forbidden: ["page", "array"],
|
|
7092
7154
|
message: "Arrays cannot contain pages or other arrays (no nested arrays)."
|
|
7155
|
+
},
|
|
7156
|
+
container: {
|
|
7157
|
+
forbidden: ["page"],
|
|
7158
|
+
message: "Containers cannot contain pages. Pages are top-level fields only."
|
|
7093
7159
|
}
|
|
7094
7160
|
};
|
|
7095
7161
|
var EXPECTED_STRUCTURE = {
|
|
@@ -7101,7 +7167,8 @@ var EXPECTED_STRUCTURE = {
|
|
|
7101
7167
|
row: `{ key: 'rowKey', type: 'row', fields: [...childFields] }`,
|
|
7102
7168
|
group: `{ key: 'groupKey', type: 'group', fields: [...childFields] }`,
|
|
7103
7169
|
array: `Full API: { key: 'arrayKey', type: 'array', fields: [...itemDefs] } OR Simplified API: { key: 'arrayKey', type: 'array', template: { type: 'input', label: 'Item' }, value: [] }`,
|
|
7104
|
-
page: `{ key: 'pageKey', type: 'page', fields: [...childFields, { key: 'next', type: 'next', label: 'Next' }] }
|
|
7170
|
+
page: `{ key: 'pageKey', type: 'page', fields: [...childFields, { key: 'next', type: 'next', label: 'Next' }] }`,
|
|
7171
|
+
container: `{ key: 'containerKey', type: 'container', wrappers: [{ type: 'css', cssClasses: 'card' }], fields: [...childFields] }`
|
|
7105
7172
|
};
|
|
7106
7173
|
function preValidateConfig(config) {
|
|
7107
7174
|
const errors = [];
|
|
@@ -7146,7 +7213,7 @@ function preValidateConfig(config) {
|
|
|
7146
7213
|
}
|
|
7147
7214
|
});
|
|
7148
7215
|
}
|
|
7149
|
-
if (["row", "group", "array"].includes(fieldType || "")) {
|
|
7216
|
+
if (["row", "group", "array", "container"].includes(fieldType || "")) {
|
|
7150
7217
|
for (const prop of INVALID_CONTAINER_PROPS) {
|
|
7151
7218
|
if (prop in f) {
|
|
7152
7219
|
errors.push({
|
|
@@ -7163,10 +7230,30 @@ function preValidateConfig(config) {
|
|
|
7163
7230
|
const expected = EXPECTED_STRUCTURE[fieldType || ""] || "";
|
|
7164
7231
|
errors.push({
|
|
7165
7232
|
path: `${path}.${prop}`,
|
|
7166
|
-
message: `"${fieldType}" containers do NOT support "${prop}". Containers don't hold values
|
|
7233
|
+
message: `"${fieldType}" containers do NOT support "${prop}". Containers don't hold values - they are purely for layout/grouping.${expected ? ` Expected structure: ${expected}` : ""}`
|
|
7167
7234
|
});
|
|
7168
7235
|
}
|
|
7169
7236
|
}
|
|
7237
|
+
if (!VALIDATING_CONTAINERS.includes(fieldType || "")) {
|
|
7238
|
+
for (const prop of CONTAINER_VALIDATION_PROPS) {
|
|
7239
|
+
if (prop in f) {
|
|
7240
|
+
errors.push({
|
|
7241
|
+
path: `${path}.${prop}`,
|
|
7242
|
+
message: `"${fieldType}" containers do NOT support "${prop}". They flatten into their parent and have no schema path, so a container-level validator would have no value to run against. Use "group" or "array" for cross-field rules, or move the rule onto a child field.`
|
|
7243
|
+
});
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
if (fieldType !== "array") {
|
|
7248
|
+
for (const prop of ARRAY_SIZE_PROPS) {
|
|
7249
|
+
if (prop in f) {
|
|
7250
|
+
errors.push({
|
|
7251
|
+
path: `${path}.${prop}`,
|
|
7252
|
+
message: `"${fieldType}" containers do NOT support "${prop}". Array size constraints apply to "array" fields only.`
|
|
7253
|
+
});
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
}
|
|
7170
7257
|
if ("logic" in f && Array.isArray(f["logic"])) {
|
|
7171
7258
|
const nonHiddenLogic = f["logic"].filter(
|
|
7172
7259
|
(l) => l && typeof l === "object" && l["type"] !== "hidden"
|
|
@@ -7197,6 +7284,14 @@ function preValidateConfig(config) {
|
|
|
7197
7284
|
});
|
|
7198
7285
|
}
|
|
7199
7286
|
}
|
|
7287
|
+
for (const prop of [...CONTAINER_VALIDATION_PROPS, ...ARRAY_SIZE_PROPS]) {
|
|
7288
|
+
if (prop in f) {
|
|
7289
|
+
errors.push({
|
|
7290
|
+
path: `${path}.${prop}`,
|
|
7291
|
+
message: `"page" containers do NOT support "${prop}". Pages flatten into the form and have no schema path of their own.`
|
|
7292
|
+
});
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7200
7295
|
}
|
|
7201
7296
|
if (parentType && NESTING_RULES[parentType]) {
|
|
7202
7297
|
const rules = NESTING_RULES[parentType];
|
|
@@ -7277,7 +7372,7 @@ function preValidateConfig(config) {
|
|
|
7277
7372
|
});
|
|
7278
7373
|
}
|
|
7279
7374
|
}
|
|
7280
|
-
if (["row", "group", "page"].includes(fieldType || "")) {
|
|
7375
|
+
if (["row", "group", "page", "container"].includes(fieldType || "")) {
|
|
7281
7376
|
if (!("fields" in f)) {
|
|
7282
7377
|
errors.push({
|
|
7283
7378
|
path: `${path}.fields`,
|
|
@@ -7290,6 +7385,19 @@ function preValidateConfig(config) {
|
|
|
7290
7385
|
});
|
|
7291
7386
|
}
|
|
7292
7387
|
}
|
|
7388
|
+
if (fieldType === "container") {
|
|
7389
|
+
if (!("wrappers" in f)) {
|
|
7390
|
+
errors.push({
|
|
7391
|
+
path: `${path}.wrappers`,
|
|
7392
|
+
message: `"container" container "${fieldKey || "unknown"}" is MISSING required "wrappers" property. Add a wrappers array naming the UI chrome to wrap the children in, or [] for no chrome. Do NOT switch to a "group" instead: a container flattens its children into the parent value, while a group nests them under its own key and owns a schema path, so the submitted value shape and validation behaviour both change. Expected structure: ${EXPECTED_STRUCTURE["container"]}`
|
|
7393
|
+
});
|
|
7394
|
+
} else if (!Array.isArray(f["wrappers"])) {
|
|
7395
|
+
errors.push({
|
|
7396
|
+
path: `${path}.wrappers`,
|
|
7397
|
+
message: `"container" container "${fieldKey || "unknown"}" has invalid "wrappers" - must be an array of wrapper objects, not ${typeof f["wrappers"]}.`
|
|
7398
|
+
});
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7293
7401
|
if (fieldType === "array") {
|
|
7294
7402
|
const hasFields = "fields" in f;
|
|
7295
7403
|
const hasTemplate = "template" in f;
|
|
@@ -8127,7 +8235,7 @@ function registerResources(server) {
|
|
|
8127
8235
|
}
|
|
8128
8236
|
|
|
8129
8237
|
// packages/dynamic-form-mcp/src/tools/lookup.tool.ts
|
|
8130
|
-
import { z as
|
|
8238
|
+
import { z as z57 } from "zod";
|
|
8131
8239
|
|
|
8132
8240
|
// packages/dynamic-form-mcp/src/tools/data/lookup-topics.ts
|
|
8133
8241
|
var TOPICS = {
|
|
@@ -9862,25 +9970,42 @@ provideDynamicForm({
|
|
|
9862
9970
|
| FieldContext | \`ctx.value()\` | \`ctx.valueOf('path' as never)\` | custom validators |`
|
|
9863
9971
|
},
|
|
9864
9972
|
containers: {
|
|
9865
|
-
brief: `| Container | Label? | Logic? | Notes |
|
|
9866
|
-
|
|
9867
|
-
| page | NO | hidden only | Nav buttons INSIDE |
|
|
9868
|
-
| group | NO | hidden only | Creates nested object |
|
|
9869
|
-
| array | NO | hidden only | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9870
|
-
| row | NO | hidden only | Layout only
|
|
9973
|
+
brief: `| Container | Label? | Logic? | Value | Notes |
|
|
9974
|
+
|-----------|--------|--------|-------|-------|
|
|
9975
|
+
| page | NO | hidden only | flattened | Nav buttons INSIDE |
|
|
9976
|
+
| group | NO | hidden only | nested under its key | Creates nested object |
|
|
9977
|
+
| array | NO | hidden only | nested under its key | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9978
|
+
| row | NO | hidden only | flattened | Layout only |
|
|
9979
|
+
| container | NO | hidden only | flattened | Layout + REQUIRED \`wrappers\` chain |`,
|
|
9871
9980
|
full: `# Container Rules
|
|
9872
9981
|
|
|
9873
|
-
| Container | Label? | Logic? | Allowed Children | Notes |
|
|
9874
|
-
|
|
9875
|
-
| page | NO | YES (hidden only) | rows, groups, arrays, leaf fields, buttons | Nav buttons go INSIDE |
|
|
9876
|
-
| group | NO | YES (hidden only) | rows, arrays, leaf fields (NOT pages, groups) | Creates nested object |
|
|
9877
|
-
| array | NO | YES (hidden only) | rows, groups, leaf fields (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9878
|
-
| row | NO | YES (hidden only) | groups, arrays, containers,
|
|
9982
|
+
| Container | Label? | Logic? | Value Handling | Allowed Children | Notes |
|
|
9983
|
+
|-----------|--------|--------|----------------|------------------|-------|
|
|
9984
|
+
| page | NO | YES (hidden only) | flattened | rows, groups, arrays, containers, hidden, leaf fields, buttons (NOT pages) | Nav buttons go INSIDE |
|
|
9985
|
+
| group | NO | YES (hidden only) | nested under its key | rows, arrays, containers, hidden, leaf fields, buttons (NOT pages, groups) | Creates nested object |
|
|
9986
|
+
| array | NO | YES (hidden only) | nested under its key | rows, groups, containers, hidden, leaf fields, buttons (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9987
|
+
| row | NO | YES (hidden only) | flattened | groups, arrays, containers, leaf fields, buttons (NOT pages, nested rows, hidden) | Layout only |
|
|
9988
|
+
| container | NO | YES (hidden only) | flattened | rows, groups, arrays, nested containers, hidden, leaf fields, buttons (NOT pages) | \`wrappers\` is REQUIRED (use [] for none) |
|
|
9989
|
+
|
|
9990
|
+
**Value handling decides which container you want.** \`page\`, \`row\` and \`container\` flatten: their children land directly in the parent value, and the container key never appears in the submitted data. \`group\` and \`array\` nest their children under their own key.
|
|
9991
|
+
|
|
9992
|
+
That difference is not cosmetic, so these are not interchangeable:
|
|
9993
|
+
|
|
9994
|
+
\`\`\`typescript
|
|
9995
|
+
// container \u2192 { street: '', city: '' } (chrome only, key not in the value)
|
|
9996
|
+
{ key: 'billing', type: 'container', wrappers: [{ type: 'css', cssClasses: 'card' }], fields: [street, city] }
|
|
9997
|
+
|
|
9998
|
+
// group \u2192 { billing: { street: '', city: '' } } (adds a level to the value)
|
|
9999
|
+
{ key: 'billing', type: 'group', fields: [street, city] }
|
|
10000
|
+
\`\`\`
|
|
10001
|
+
|
|
10002
|
+
Only \`group\` and \`array\` own a schema path, so only they take container-level \`validators\`, \`required\` and \`validationMessages\`. Reach for \`container\` when you want chrome around fields without changing the data shape, and for \`group\` when you want the nesting or the cross-field validation.
|
|
9879
10003
|
|
|
9880
10004
|
**CRITICAL:**
|
|
9881
|
-
- All containers (page, group, row, array) support only the \`hidden\` logic type for conditional visibility.
|
|
10005
|
+
- All containers (page, group, row, array, container) support only the \`hidden\` logic type for conditional visibility.
|
|
9882
10006
|
- For other logic types (disabled, required, readonly, derivation), apply them to child fields instead.
|
|
9883
|
-
- Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode
|
|
10007
|
+
- Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode.
|
|
10008
|
+
- Rows reject nested rows and hidden fields; put hidden fields at page or form level.`
|
|
9884
10009
|
},
|
|
9885
10010
|
"field-placement": {
|
|
9886
10011
|
brief: `**Multi-page:** ALL root fields must be pages. Hidden fields go INSIDE first page.
|
|
@@ -10674,7 +10799,8 @@ var TOPIC_DESCRIPTIONS = {
|
|
|
10674
10799
|
"field-placement": "Which fields can go where \u2014 nesting and placement rules",
|
|
10675
10800
|
"logic-matrix": "Which logic types are supported on which field types",
|
|
10676
10801
|
"context-api": "FieldContext API \u2014 valueOf(), formValue, parent, arrayIndex",
|
|
10677
|
-
containers: "Overview of all container types (group, row, array, page)",
|
|
10802
|
+
containers: "Overview of all container types (group, row, array, container, page) and what each may hold",
|
|
10803
|
+
container: "Wraps children in a wrapper chain; flattens their values into the parent like a row",
|
|
10678
10804
|
"array-buttons": "Add/remove array item button configuration",
|
|
10679
10805
|
"custom-validators": "Custom validator functions via validators array",
|
|
10680
10806
|
conditions: "Condition operators and types for logic rules",
|
|
@@ -10934,7 +11060,7 @@ function formatTopicItem(key) {
|
|
|
10934
11060
|
}
|
|
10935
11061
|
function getTopicList() {
|
|
10936
11062
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
10937
|
-
const containers = ["
|
|
11063
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
10938
11064
|
const wrappers = ["wrappers", ...getWrappers().map((w) => w.type)];
|
|
10939
11065
|
const addons = ["addons", ...getAddonTypes().map((k) => k.type)];
|
|
10940
11066
|
const concepts = [
|
|
@@ -10990,7 +11116,7 @@ ${patterns.map(formatTopicItem).join("\n")}
|
|
|
10990
11116
|
}
|
|
10991
11117
|
function formatFieldInfoFull(field, uiFieldType) {
|
|
10992
11118
|
const lines = [];
|
|
10993
|
-
const isContainer =
|
|
11119
|
+
const isContainer = field.category === "container";
|
|
10994
11120
|
const isHidden = field.type === "hidden";
|
|
10995
11121
|
lines.push(`## ${field.type} field`);
|
|
10996
11122
|
lines.push("");
|
|
@@ -11419,11 +11545,11 @@ Patterns: field-placement, logic-matrix, context-api, containers, multi-page-got
|
|
|
11419
11545
|
Use topic="list" to see all available topics with descriptions.
|
|
11420
11546
|
Use ngforge_search query="your question" to find topics by keyword.`,
|
|
11421
11547
|
{
|
|
11422
|
-
topic:
|
|
11423
|
-
'Topic to look up: field types (input, select, hidden, group, row, array, page), wrappers (wrappers, css, section, arraySection), concepts (validation, conditional, derivation, property-derivation, options-format), patterns (golden-path, pitfalls, multi-page-gotchas), or "list" to see all topics'
|
|
11548
|
+
topic: z57.string().describe(
|
|
11549
|
+
'Topic to look up: field types (input, select, hidden, group, row, array, container, page), wrappers (wrappers, css, section, arraySection), concepts (validation, conditional, derivation, property-derivation, options-format), patterns (golden-path, pitfalls, multi-page-gotchas), or "list" to see all topics'
|
|
11424
11550
|
),
|
|
11425
|
-
depth:
|
|
11426
|
-
uiIntegration:
|
|
11551
|
+
depth: z57.enum(["brief", "full", "schema"]).default("full").describe("brief=quick syntax (~20 lines), full=complete docs with examples, schema=include JSON schema"),
|
|
11552
|
+
uiIntegration: z57.enum(UI_INTEGRATIONS2).optional().describe("Filter UI-specific info (only with depth=schema). material, bootstrap, primeng, ionic")
|
|
11427
11553
|
},
|
|
11428
11554
|
async ({ topic, depth, uiIntegration }) => {
|
|
11429
11555
|
const normalizedTopic = topic.toLowerCase().trim();
|
|
@@ -11471,7 +11597,7 @@ ${nextSteps}`;
|
|
|
11471
11597
|
}
|
|
11472
11598
|
|
|
11473
11599
|
// packages/dynamic-form-mcp/src/tools/examples.tool.ts
|
|
11474
|
-
import { z as
|
|
11600
|
+
import { z as z58 } from "zod";
|
|
11475
11601
|
var PATTERNS = {
|
|
11476
11602
|
// ========== MINIMAL PATTERNS (NEW) ==========
|
|
11477
11603
|
"minimal-multipage": {
|
|
@@ -13736,10 +13862,10 @@ Standard patterns: derivation, property-derivation, multi-page, conditional, val
|
|
|
13736
13862
|
Use pattern="list" to see all available patterns.
|
|
13737
13863
|
Use depth="minimal" for code-only output (no markdown).`,
|
|
13738
13864
|
{
|
|
13739
|
-
pattern:
|
|
13865
|
+
pattern: z58.string().optional().describe(
|
|
13740
13866
|
'Pattern to get: minimal-multipage, minimal-array, minimal-simplified-array, minimal-conditional, minimal-validation, minimal-hidden, derivation, property-derivation, multi-page, conditional, validation, complete, mega. Use "list" to see all.'
|
|
13741
13867
|
),
|
|
13742
|
-
depth:
|
|
13868
|
+
depth: z58.enum(["minimal", "brief", "full", "explained"]).default("full").describe(
|
|
13743
13869
|
"minimal=code only (~20-50 lines), brief=code+summary, full=code+comments (default), explained=code+detailed explanation"
|
|
13744
13870
|
)
|
|
13745
13871
|
},
|
|
@@ -13804,7 +13930,7 @@ function extractCode(markdown) {
|
|
|
13804
13930
|
}
|
|
13805
13931
|
|
|
13806
13932
|
// packages/dynamic-form-mcp/src/tools/validate.tool.ts
|
|
13807
|
-
import { z as
|
|
13933
|
+
import { z as z59 } from "zod";
|
|
13808
13934
|
var UI_INTEGRATIONS3 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13809
13935
|
var ERROR_TOPIC_HINTS = [
|
|
13810
13936
|
{ pattern: /options/i, hint: '`ngforge_lookup topic="options-format"` \u2014 correct options syntax' },
|
|
@@ -13857,10 +13983,10 @@ Example errors you'll see:
|
|
|
13857
13983
|
- "options MUST be at FIELD level, NOT inside props"
|
|
13858
13984
|
- "containers only support 'hidden' logic type"`,
|
|
13859
13985
|
{
|
|
13860
|
-
config:
|
|
13986
|
+
config: z59.union([z59.string(), z59.object({}).passthrough()]).describe(
|
|
13861
13987
|
"File path (.ts/.js) OR JSON string OR JSON object. Auto-detects: paths ending in .ts/.js or starting with / are treated as files."
|
|
13862
13988
|
),
|
|
13863
|
-
uiIntegration:
|
|
13989
|
+
uiIntegration: z59.enum(UI_INTEGRATIONS3).default("material").describe("UI library to validate against (material, bootstrap, primeng, ionic). Defaults to material.")
|
|
13864
13990
|
},
|
|
13865
13991
|
async ({ config, uiIntegration }) => {
|
|
13866
13992
|
try {
|
|
@@ -13952,7 +14078,7 @@ If validating JSON, ensure:
|
|
|
13952
14078
|
}
|
|
13953
14079
|
|
|
13954
14080
|
// packages/dynamic-form-mcp/src/tools/scaffold.tool.ts
|
|
13955
|
-
import { z as
|
|
14081
|
+
import { z as z60 } from "zod";
|
|
13956
14082
|
var UI_INTEGRATIONS4 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13957
14083
|
function parseFieldDefs(fields) {
|
|
13958
14084
|
return fields.map((f) => {
|
|
@@ -14207,12 +14333,12 @@ Examples:
|
|
|
14207
14333
|
- With groups: pages=0 groups=["address","billing"]
|
|
14208
14334
|
- With hidden: hidden=["userId:abc123","source:web"]`,
|
|
14209
14335
|
{
|
|
14210
|
-
pages:
|
|
14211
|
-
arrays:
|
|
14212
|
-
groups:
|
|
14213
|
-
hidden:
|
|
14214
|
-
fields:
|
|
14215
|
-
uiIntegration:
|
|
14336
|
+
pages: z60.number().min(0).max(10).default(0).describe("Number of pages (0 = single-page form, 1-10 = multi-page wizard)"),
|
|
14337
|
+
arrays: z60.array(z60.string()).optional().describe('Array field names for dynamic lists, e.g., ["contacts", "addresses"]'),
|
|
14338
|
+
groups: z60.array(z60.string()).optional().describe('Group field names for nested objects, e.g., ["address", "billing"]'),
|
|
14339
|
+
hidden: z60.array(z60.string()).optional().describe('Hidden fields as "name:value" pairs, e.g., ["userId:abc", "version:1"]'),
|
|
14340
|
+
fields: z60.array(z60.string()).optional().describe('Basic fields as "name:type" pairs, e.g., ["email:input", "country:select", "age:slider"]'),
|
|
14341
|
+
uiIntegration: z60.enum(UI_INTEGRATIONS4).default("material").describe("UI library (for future UI-specific props). Defaults to material.")
|
|
14216
14342
|
},
|
|
14217
14343
|
async ({ pages, arrays = [], groups = [], hidden = [], fields = [] }) => {
|
|
14218
14344
|
const fieldDefs = parseFieldDefs(fields);
|
|
@@ -14254,11 +14380,11 @@ Examples:
|
|
|
14254
14380
|
}
|
|
14255
14381
|
|
|
14256
14382
|
// packages/dynamic-form-mcp/src/tools/search.tool.ts
|
|
14257
|
-
import { z as
|
|
14383
|
+
import { z as z61 } from "zod";
|
|
14258
14384
|
var searchIndex = null;
|
|
14259
14385
|
function getTopicSubcategory(key) {
|
|
14260
14386
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
14261
|
-
const containers = ["
|
|
14387
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
14262
14388
|
const concepts = [
|
|
14263
14389
|
"validation",
|
|
14264
14390
|
"validation-messages",
|
|
@@ -14298,6 +14424,19 @@ function buildIndex() {
|
|
|
14298
14424
|
toolCall: `ngforge_lookup topic="${key}"`
|
|
14299
14425
|
});
|
|
14300
14426
|
}
|
|
14427
|
+
for (const field of getFieldTypes()) {
|
|
14428
|
+
if (TOPICS[field.type]) continue;
|
|
14429
|
+
const description = TOPIC_DESCRIPTIONS[field.type] || field.description;
|
|
14430
|
+
entries.push({
|
|
14431
|
+
id: field.type,
|
|
14432
|
+
category: "topic",
|
|
14433
|
+
subcategory: getTopicSubcategory(field.type),
|
|
14434
|
+
description,
|
|
14435
|
+
aliases: aliasMap[field.type] || [],
|
|
14436
|
+
content: `${description} ${field.description}`.toLowerCase(),
|
|
14437
|
+
toolCall: `ngforge_lookup topic="${field.type}"`
|
|
14438
|
+
});
|
|
14439
|
+
}
|
|
14301
14440
|
for (const [key, pattern] of Object.entries(PATTERNS)) {
|
|
14302
14441
|
entries.push({
|
|
14303
14442
|
id: key,
|
|
@@ -14402,7 +14541,7 @@ Use this when you don't know the exact topic name.
|
|
|
14402
14541
|
|
|
14403
14542
|
New to ng-forge? Start with: ngforge_lookup topic="workflow"`,
|
|
14404
14543
|
{
|
|
14405
|
-
query:
|
|
14544
|
+
query: z61.string().describe("Natural language or keywords to search for")
|
|
14406
14545
|
},
|
|
14407
14546
|
async ({ query }) => {
|
|
14408
14547
|
const index = buildIndex();
|