@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/bin/ng-forge-mcp.js
CHANGED
|
@@ -24,7 +24,7 @@ var FIELD_TYPES = [
|
|
|
24
24
|
},
|
|
25
25
|
validationSupported: true,
|
|
26
26
|
source: "adapter",
|
|
27
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
27
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
28
28
|
example: `{
|
|
29
29
|
key: 'email',
|
|
30
30
|
type: 'input',
|
|
@@ -59,7 +59,7 @@ var FIELD_TYPES = [
|
|
|
59
59
|
},
|
|
60
60
|
validationSupported: true,
|
|
61
61
|
source: "adapter",
|
|
62
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
62
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
63
63
|
example: `{
|
|
64
64
|
key: 'description',
|
|
65
65
|
type: 'textarea',
|
|
@@ -93,7 +93,7 @@ var FIELD_TYPES = [
|
|
|
93
93
|
},
|
|
94
94
|
validationSupported: true,
|
|
95
95
|
source: "adapter",
|
|
96
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
96
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
97
97
|
example: `{
|
|
98
98
|
key: 'country',
|
|
99
99
|
type: 'select',
|
|
@@ -115,7 +115,7 @@ var FIELD_TYPES = [
|
|
|
115
115
|
props: {},
|
|
116
116
|
validationSupported: true,
|
|
117
117
|
source: "adapter",
|
|
118
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
118
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
119
119
|
example: `{
|
|
120
120
|
key: 'acceptTerms',
|
|
121
121
|
type: 'checkbox',
|
|
@@ -140,7 +140,7 @@ var FIELD_TYPES = [
|
|
|
140
140
|
},
|
|
141
141
|
validationSupported: true,
|
|
142
142
|
source: "adapter",
|
|
143
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
143
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
144
144
|
example: `{
|
|
145
145
|
key: 'interests',
|
|
146
146
|
type: 'multi-checkbox',
|
|
@@ -170,7 +170,7 @@ var FIELD_TYPES = [
|
|
|
170
170
|
},
|
|
171
171
|
validationSupported: true,
|
|
172
172
|
source: "adapter",
|
|
173
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
173
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
174
174
|
example: `{
|
|
175
175
|
key: 'gender',
|
|
176
176
|
type: 'radio',
|
|
@@ -205,7 +205,7 @@ var FIELD_TYPES = [
|
|
|
205
205
|
},
|
|
206
206
|
validationSupported: true,
|
|
207
207
|
source: "adapter",
|
|
208
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
208
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
209
209
|
example: `{
|
|
210
210
|
key: 'birthDate',
|
|
211
211
|
type: 'datepicker',
|
|
@@ -224,7 +224,7 @@ var FIELD_TYPES = [
|
|
|
224
224
|
props: {},
|
|
225
225
|
validationSupported: true,
|
|
226
226
|
source: "adapter",
|
|
227
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
227
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
228
228
|
example: `{
|
|
229
229
|
key: 'notifications',
|
|
230
230
|
type: 'toggle',
|
|
@@ -263,7 +263,7 @@ var FIELD_TYPES = [
|
|
|
263
263
|
},
|
|
264
264
|
validationSupported: true,
|
|
265
265
|
source: "adapter",
|
|
266
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
266
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
267
267
|
example: `{
|
|
268
268
|
key: 'volume',
|
|
269
269
|
type: 'slider',
|
|
@@ -284,7 +284,7 @@ var FIELD_TYPES = [
|
|
|
284
284
|
props: {},
|
|
285
285
|
validationSupported: false,
|
|
286
286
|
source: "core",
|
|
287
|
-
allowedIn: ["top-level (single-page forms only)", "page.fields", "group.fields", "array.fields"],
|
|
287
|
+
allowedIn: ["top-level (single-page forms only)", "page.fields", "group.fields", "array.fields", "container"],
|
|
288
288
|
notAllowedIn: ["row", "top-level when using pages"],
|
|
289
289
|
example: `// Hidden field - value is REQUIRED!
|
|
290
290
|
{
|
|
@@ -321,7 +321,7 @@ var FIELD_TYPES = [
|
|
|
321
321
|
},
|
|
322
322
|
validationSupported: false,
|
|
323
323
|
source: "core",
|
|
324
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
324
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
325
325
|
example: `{
|
|
326
326
|
key: 'sectionTitle',
|
|
327
327
|
type: 'text',
|
|
@@ -424,11 +424,12 @@ var FIELD_TYPES = [
|
|
|
424
424
|
},
|
|
425
425
|
validationSupported: true,
|
|
426
426
|
source: "core",
|
|
427
|
-
allowedIn: ["top-level", "page", "row", "array"],
|
|
427
|
+
allowedIn: ["top-level", "page", "row", "array", "container"],
|
|
428
428
|
notAllowedIn: ["group"],
|
|
429
429
|
canContain: [
|
|
430
430
|
"row",
|
|
431
431
|
"array",
|
|
432
|
+
"container",
|
|
432
433
|
"input",
|
|
433
434
|
"textarea",
|
|
434
435
|
"select",
|
|
@@ -522,11 +523,12 @@ var FIELD_TYPES = [
|
|
|
522
523
|
},
|
|
523
524
|
validationSupported: true,
|
|
524
525
|
source: "core",
|
|
525
|
-
allowedIn: ["top-level", "page", "row", "group"],
|
|
526
|
+
allowedIn: ["top-level", "page", "row", "group", "container"],
|
|
526
527
|
notAllowedIn: ["array"],
|
|
527
528
|
canContain: [
|
|
528
529
|
"row",
|
|
529
530
|
"group",
|
|
531
|
+
"container",
|
|
530
532
|
"input",
|
|
531
533
|
"textarea",
|
|
532
534
|
"select",
|
|
@@ -588,6 +590,74 @@ var FIELD_TYPES = [
|
|
|
588
590
|
// ? { kind: 'periodOrder' } : null`,
|
|
589
591
|
minimalExample: `{ key: 'items', type: 'array', fields: [...] }`
|
|
590
592
|
},
|
|
593
|
+
{
|
|
594
|
+
type: "container",
|
|
595
|
+
category: "container",
|
|
596
|
+
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.",
|
|
597
|
+
valueType: void 0,
|
|
598
|
+
baseInterface: "FieldDef",
|
|
599
|
+
props: {
|
|
600
|
+
fields: {
|
|
601
|
+
name: "fields",
|
|
602
|
+
type: "ContainerAllowedChildren[]",
|
|
603
|
+
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",
|
|
604
|
+
required: true
|
|
605
|
+
},
|
|
606
|
+
wrappers: {
|
|
607
|
+
name: "wrappers",
|
|
608
|
+
type: "WrapperConfig[]",
|
|
609
|
+
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.",
|
|
610
|
+
required: true
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
validationSupported: false,
|
|
614
|
+
source: "core",
|
|
615
|
+
allowedIn: ["top-level", "page", "group", "array", "row", "container"],
|
|
616
|
+
notAllowedIn: [],
|
|
617
|
+
canContain: [
|
|
618
|
+
"row",
|
|
619
|
+
"group",
|
|
620
|
+
"array",
|
|
621
|
+
"container",
|
|
622
|
+
"hidden",
|
|
623
|
+
"input",
|
|
624
|
+
"textarea",
|
|
625
|
+
"select",
|
|
626
|
+
"checkbox",
|
|
627
|
+
"multi-checkbox",
|
|
628
|
+
"radio",
|
|
629
|
+
"datepicker",
|
|
630
|
+
"toggle",
|
|
631
|
+
"slider",
|
|
632
|
+
"text",
|
|
633
|
+
"button",
|
|
634
|
+
"submit",
|
|
635
|
+
"next",
|
|
636
|
+
"previous",
|
|
637
|
+
"add-array-item",
|
|
638
|
+
"prepend-array-item",
|
|
639
|
+
"insert-array-item",
|
|
640
|
+
"remove-array-item",
|
|
641
|
+
"pop-array-item",
|
|
642
|
+
"shift-array-item"
|
|
643
|
+
],
|
|
644
|
+
cannotContain: ["page"],
|
|
645
|
+
example: `{
|
|
646
|
+
key: 'billingChrome',
|
|
647
|
+
type: 'container',
|
|
648
|
+
// NOTE: Containers do NOT have a label property
|
|
649
|
+
// wrappers is REQUIRED \u2014 use [] when no chrome is needed
|
|
650
|
+
wrappers: [{ type: 'css', cssClasses: 'card p-3' }],
|
|
651
|
+
fields: [
|
|
652
|
+
{ key: 'street', type: 'input', label: 'Street' },
|
|
653
|
+
{ key: 'city', type: 'input', label: 'City' }
|
|
654
|
+
]
|
|
655
|
+
}
|
|
656
|
+
// Values flatten: { street: '', city: '' } \u2014 no 'billingChrome' key
|
|
657
|
+
// NOT ALLOWED in containers:
|
|
658
|
+
// - type: 'page' (pages are top-level only)`,
|
|
659
|
+
minimalExample: `{ key: 'chrome', type: 'container', wrappers: [], fields: [...] }`
|
|
660
|
+
},
|
|
591
661
|
{
|
|
592
662
|
type: "page",
|
|
593
663
|
category: "container",
|
|
@@ -605,11 +675,12 @@ var FIELD_TYPES = [
|
|
|
605
675
|
validationSupported: false,
|
|
606
676
|
source: "core",
|
|
607
677
|
allowedIn: ["top-level"],
|
|
608
|
-
notAllowedIn: ["page", "row", "group", "array"],
|
|
678
|
+
notAllowedIn: ["page", "row", "group", "array", "container"],
|
|
609
679
|
canContain: [
|
|
610
680
|
"row",
|
|
611
681
|
"group",
|
|
612
682
|
"array",
|
|
683
|
+
"container",
|
|
613
684
|
"input",
|
|
614
685
|
"textarea",
|
|
615
686
|
"select",
|
|
@@ -649,7 +720,7 @@ var FIELD_TYPES = [
|
|
|
649
720
|
props: {},
|
|
650
721
|
validationSupported: false,
|
|
651
722
|
source: "adapter",
|
|
652
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
723
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
653
724
|
example: `{
|
|
654
725
|
key: 'next',
|
|
655
726
|
type: 'next',
|
|
@@ -666,7 +737,7 @@ var FIELD_TYPES = [
|
|
|
666
737
|
props: {},
|
|
667
738
|
validationSupported: false,
|
|
668
739
|
source: "adapter",
|
|
669
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
740
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
670
741
|
example: `{
|
|
671
742
|
key: 'back',
|
|
672
743
|
type: 'previous',
|
|
@@ -690,7 +761,7 @@ var FIELD_TYPES = [
|
|
|
690
761
|
},
|
|
691
762
|
validationSupported: false,
|
|
692
763
|
source: "adapter",
|
|
693
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
764
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
694
765
|
example: `// RECOMMENDED: Use pre-defined button types for common actions
|
|
695
766
|
{ key: 'submit', type: 'submit', label: 'Submit Form' }
|
|
696
767
|
{ key: 'next', type: 'next', label: 'Next' }
|
|
@@ -717,7 +788,7 @@ var FIELD_TYPES = [
|
|
|
717
788
|
props: {},
|
|
718
789
|
validationSupported: false,
|
|
719
790
|
source: "adapter",
|
|
720
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
791
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
721
792
|
example: `{
|
|
722
793
|
key: 'submit',
|
|
723
794
|
type: 'submit',
|
|
@@ -734,7 +805,7 @@ var FIELD_TYPES = [
|
|
|
734
805
|
props: {},
|
|
735
806
|
validationSupported: false,
|
|
736
807
|
source: "adapter",
|
|
737
|
-
allowedIn: ["array", "row", "group"],
|
|
808
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
738
809
|
example: `{
|
|
739
810
|
key: 'addContact',
|
|
740
811
|
type: 'add-array-item',
|
|
@@ -751,7 +822,7 @@ var FIELD_TYPES = [
|
|
|
751
822
|
props: {},
|
|
752
823
|
validationSupported: false,
|
|
753
824
|
source: "adapter",
|
|
754
|
-
allowedIn: ["array", "row", "group"],
|
|
825
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
755
826
|
example: `{
|
|
756
827
|
key: 'prependContact',
|
|
757
828
|
type: 'prepend-array-item',
|
|
@@ -768,7 +839,7 @@ var FIELD_TYPES = [
|
|
|
768
839
|
props: {},
|
|
769
840
|
validationSupported: false,
|
|
770
841
|
source: "adapter",
|
|
771
|
-
allowedIn: ["array", "row", "group"],
|
|
842
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
772
843
|
example: `{
|
|
773
844
|
key: 'insertContact',
|
|
774
845
|
type: 'insert-array-item',
|
|
@@ -786,7 +857,7 @@ var FIELD_TYPES = [
|
|
|
786
857
|
props: {},
|
|
787
858
|
validationSupported: false,
|
|
788
859
|
source: "adapter",
|
|
789
|
-
allowedIn: ["array", "row", "group"],
|
|
860
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
790
861
|
example: `{
|
|
791
862
|
key: 'removeContact',
|
|
792
863
|
type: 'remove-array-item',
|
|
@@ -803,7 +874,7 @@ var FIELD_TYPES = [
|
|
|
803
874
|
props: {},
|
|
804
875
|
validationSupported: false,
|
|
805
876
|
source: "adapter",
|
|
806
|
-
allowedIn: ["array", "row", "group"],
|
|
877
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
807
878
|
example: `{
|
|
808
879
|
key: 'popContact',
|
|
809
880
|
type: 'pop-array-item',
|
|
@@ -821,7 +892,7 @@ var FIELD_TYPES = [
|
|
|
821
892
|
props: {},
|
|
822
893
|
validationSupported: false,
|
|
823
894
|
source: "adapter",
|
|
824
|
-
allowedIn: ["array", "row", "group"],
|
|
895
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
825
896
|
example: `{
|
|
826
897
|
key: 'shiftContact',
|
|
827
898
|
type: 'shift-array-item',
|
|
@@ -1951,7 +2022,7 @@ var ADDON_TYPES = [
|
|
|
1951
2022
|
preset: {
|
|
1952
2023
|
name: "preset",
|
|
1953
2024
|
type: "'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility' | string",
|
|
1954
|
-
description: "Built-in preset action. Mutually exclusive with `actionRef` and `action`.",
|
|
2025
|
+
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`.",
|
|
1955
2026
|
required: false
|
|
1956
2027
|
},
|
|
1957
2028
|
actionRef: {
|
|
@@ -2891,6 +2962,34 @@ Paged forms may virtualize inactive pages. DynamicForm \`(initialized)\` emits a
|
|
|
2891
2962
|
the initially active page is ready. Use \`(activePageInitialized)\` when work must run
|
|
2892
2963
|
after every visible-page transition; it announces once per visit.
|
|
2893
2964
|
|
|
2965
|
+
### Rendering Performance
|
|
2966
|
+
|
|
2967
|
+
Use \`options.pagePreloadWindow\` for paged forms. It controls how many neighbouring
|
|
2968
|
+
pages are mounted and have their component chunks preloaded. \`0\` keeps only the
|
|
2969
|
+
active page in the render window; the default is \`1\`.
|
|
2970
|
+
|
|
2971
|
+
Use \`options.fieldWindowing\` for large flat forms:
|
|
2972
|
+
|
|
2973
|
+
\`\`\`typescript
|
|
2974
|
+
{
|
|
2975
|
+
options: {
|
|
2976
|
+
fieldWindowing: {
|
|
2977
|
+
eager: 20,
|
|
2978
|
+
placeholderHeight: '4rem',
|
|
2979
|
+
park: { margin: '100%' },
|
|
2980
|
+
},
|
|
2981
|
+
},
|
|
2982
|
+
fields: [ /* flat fields */ ],
|
|
2983
|
+
}
|
|
2984
|
+
\`\`\`
|
|
2985
|
+
|
|
2986
|
+
- \`eager\` mounts that many leading leaf fields immediately. Remaining fields mount near the viewport.
|
|
2987
|
+
- \`placeholderHeight\` reserves layout space before a field mounts.
|
|
2988
|
+
- \`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.
|
|
2989
|
+
- \`park.margin\` follows \`IntersectionObserver.rootMargin\`: use one to four \`px\` or \`%\` values. Unsupported units fall back to the inherited margin.
|
|
2990
|
+
- \`fieldWindowing: false\` disables inherited deferred mounting for that form.
|
|
2991
|
+
- A park-only object changes parking without changing the inherited mounting mode. Use \`{ park: false }\` to opt out or \`{ park: true }\` to opt in.
|
|
2992
|
+
|
|
2894
2993
|
## Validation
|
|
2895
2994
|
|
|
2896
2995
|
### Shorthand Validators (Preferred)
|
|
@@ -4783,15 +4882,23 @@ var ContainerLogicSchema = z13.object({
|
|
|
4783
4882
|
type: z13.literal("hidden"),
|
|
4784
4883
|
condition: z13.union([ConditionalExpressionSchema, z13.boolean()])
|
|
4785
4884
|
});
|
|
4885
|
+
var SIMPLIFIED_ONLY_PROPS = ["template", "value", "addButton", "removeButton"];
|
|
4886
|
+
var SIMPLIFIED_ONLY_HINTS = {
|
|
4887
|
+
template: 'Use "fields" for the full API, or "template" for the simplified one.',
|
|
4888
|
+
value: 'Set initial values on the field definitions inside "fields".',
|
|
4889
|
+
addButton: 'Add an "add-array-item" or "insert-array-item" button as a field.',
|
|
4890
|
+
removeButton: 'Add a "remove-array-item" button to the item definition.'
|
|
4891
|
+
};
|
|
4786
4892
|
function createContainerSchemas(options) {
|
|
4787
4893
|
const { leafFieldSchema } = options;
|
|
4788
4894
|
const AnyFieldSchema = z13.lazy(
|
|
4789
|
-
() => z13.
|
|
4895
|
+
() => z13.discriminatedUnion("type", [
|
|
4790
4896
|
leafFieldSchema,
|
|
4791
4897
|
PageFieldSchema,
|
|
4792
4898
|
RowFieldSchema,
|
|
4793
4899
|
GroupFieldSchema,
|
|
4794
|
-
ArrayFieldSchema
|
|
4900
|
+
ArrayFieldSchema,
|
|
4901
|
+
ContainerFieldSchema
|
|
4795
4902
|
])
|
|
4796
4903
|
);
|
|
4797
4904
|
const ContainerBaseSchema = BaseFieldDefSchema.omit({
|
|
@@ -4803,6 +4910,18 @@ function createContainerSchemas(options) {
|
|
|
4803
4910
|
label: z13.never().optional(),
|
|
4804
4911
|
meta: z13.never().optional()
|
|
4805
4912
|
});
|
|
4913
|
+
const ContainerValidationShape = {
|
|
4914
|
+
required: z13.boolean().optional(),
|
|
4915
|
+
validators: ValidatorsArraySchema.optional(),
|
|
4916
|
+
validationMessages: ValidationMessagesSchema.optional()
|
|
4917
|
+
};
|
|
4918
|
+
const WrapperConfigSchema = z13.object({ type: z13.string() }).passthrough();
|
|
4919
|
+
const ContainerFieldSchema = ContainerBaseSchema.extend({
|
|
4920
|
+
type: z13.literal("container"),
|
|
4921
|
+
fields: z13.array(AnyFieldSchema),
|
|
4922
|
+
wrappers: z13.array(WrapperConfigSchema),
|
|
4923
|
+
logic: z13.array(ContainerLogicSchema).optional()
|
|
4924
|
+
});
|
|
4806
4925
|
const PageFieldSchema = ContainerBaseSchema.extend({
|
|
4807
4926
|
type: z13.literal("page"),
|
|
4808
4927
|
fields: z13.array(AnyFieldSchema),
|
|
@@ -4819,33 +4938,27 @@ function createContainerSchemas(options) {
|
|
|
4819
4938
|
const GroupFieldSchema = ContainerBaseSchema.extend({
|
|
4820
4939
|
type: z13.literal("group"),
|
|
4821
4940
|
fields: z13.array(AnyFieldSchema),
|
|
4941
|
+
...ContainerValidationShape,
|
|
4822
4942
|
// Container logic - only 'hidden' type allowed (same as pages)
|
|
4823
4943
|
logic: z13.array(ContainerLogicSchema).optional()
|
|
4824
4944
|
});
|
|
4825
4945
|
const ArrayButtonConfigSchema = z13.object({
|
|
4826
4946
|
label: z13.string().optional(),
|
|
4827
|
-
props: z13.record(z13.unknown()).optional()
|
|
4828
|
-
});
|
|
4829
|
-
const FullArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4830
|
-
type: z13.literal("array"),
|
|
4831
|
-
fields: z13.array(AnyFieldSchema),
|
|
4832
|
-
logic: z13.array(ContainerLogicSchema).optional(),
|
|
4833
|
-
// Full API does not use template
|
|
4834
|
-
template: z13.never().optional(),
|
|
4835
|
-
// Array length validation
|
|
4836
|
-
minLength: z13.number().int().min(0).optional(),
|
|
4837
|
-
maxLength: z13.number().int().min(0).optional()
|
|
4947
|
+
props: z13.record(z13.string(), z13.unknown()).optional()
|
|
4838
4948
|
});
|
|
4839
4949
|
const ArrayAllowedChildSchema = z13.lazy(
|
|
4840
4950
|
() => AnyFieldSchema.refine((field) => field.type !== "page" && field.type !== "array", {
|
|
4841
4951
|
message: "Array templates cannot contain page or array fields. Only leaf fields, rows, and groups are allowed."
|
|
4842
4952
|
})
|
|
4843
4953
|
);
|
|
4844
|
-
const
|
|
4954
|
+
const ArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4845
4955
|
type: z13.literal("array"),
|
|
4846
|
-
|
|
4956
|
+
...ContainerValidationShape,
|
|
4957
|
+
// Full API: explicit item definitions.
|
|
4958
|
+
fields: z13.array(AnyFieldSchema).optional(),
|
|
4959
|
+
// Simplified API: single field (primitive array) or array of fields (object array).
|
|
4847
4960
|
// Only ArrayAllowedChildren (leaf fields, rows, groups) are valid — no pages or nested arrays.
|
|
4848
|
-
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]),
|
|
4961
|
+
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]).optional(),
|
|
4849
4962
|
// Initial values for the array
|
|
4850
4963
|
value: z13.array(z13.unknown()).optional(),
|
|
4851
4964
|
// Button customization or opt-out (false to disable)
|
|
@@ -4855,18 +4968,64 @@ function createContainerSchemas(options) {
|
|
|
4855
4968
|
// Array length validation
|
|
4856
4969
|
minLength: z13.number().int().min(0).optional(),
|
|
4857
4970
|
maxLength: z13.number().int().min(0).optional(),
|
|
4858
|
-
//
|
|
4859
|
-
|
|
4971
|
+
// The array size properties are minLength/maxLength. These are the common
|
|
4972
|
+
// wrong spelling, and naming them keeps the mistake from being silently
|
|
4973
|
+
// stripped. Previously only the simplified API rejected them.
|
|
4860
4974
|
minItems: z13.never().optional(),
|
|
4861
4975
|
maxItems: z13.never().optional()
|
|
4976
|
+
}).superRefine((field, ctx) => {
|
|
4977
|
+
const hasFields = field.fields !== void 0;
|
|
4978
|
+
const hasTemplate = field.template !== void 0;
|
|
4979
|
+
if (hasFields && hasTemplate) {
|
|
4980
|
+
ctx.addIssue({
|
|
4981
|
+
code: "custom",
|
|
4982
|
+
message: 'Array has BOTH "fields" and "template". These are mutually exclusive: use "fields" for the full API, or "template" + "value" for the simplified API.'
|
|
4983
|
+
});
|
|
4984
|
+
return;
|
|
4985
|
+
}
|
|
4986
|
+
if (!hasFields && !hasTemplate) {
|
|
4987
|
+
ctx.addIssue({
|
|
4988
|
+
code: "custom",
|
|
4989
|
+
message: 'Array is MISSING both "fields" and "template". Use "fields" (full API) or "template" + "value" (simplified API).'
|
|
4990
|
+
});
|
|
4991
|
+
return;
|
|
4992
|
+
}
|
|
4993
|
+
if (hasFields) {
|
|
4994
|
+
for (const prop of SIMPLIFIED_ONLY_PROPS) {
|
|
4995
|
+
if (prop === "template" || field[prop] === void 0) continue;
|
|
4996
|
+
ctx.addIssue({
|
|
4997
|
+
code: "custom",
|
|
4998
|
+
path: [prop],
|
|
4999
|
+
message: `Array uses the full API ("fields"), so "${prop}" is not allowed: it belongs to the simplified API. ${SIMPLIFIED_ONLY_HINTS[prop]}`
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
}).meta({
|
|
5004
|
+
// The refinement above is invisible to JSON Schema generation, and the
|
|
5005
|
+
// generated schema is authoring guidance for a model. Two optional
|
|
5006
|
+
// properties with no stated relationship reads as "pass either, both or
|
|
5007
|
+
// neither", which is exactly the mistake the refinement rejects at
|
|
5008
|
+
// runtime. Restating the rule as `oneOf` keeps the published schema
|
|
5009
|
+
// saying what the validator enforces — the union of two array schemas
|
|
5010
|
+
// used to express this, and collapsing them to one schema lost it.
|
|
5011
|
+
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.',
|
|
5012
|
+
oneOf: [
|
|
5013
|
+
{
|
|
5014
|
+
required: ["fields"],
|
|
5015
|
+
// `not` wraps an `anyOf`, not one multi-name `required`: the latter
|
|
5016
|
+
// would only forbid all of them being present at once.
|
|
5017
|
+
not: { anyOf: SIMPLIFIED_ONLY_PROPS.map((prop) => ({ required: [prop] })) }
|
|
5018
|
+
},
|
|
5019
|
+
{ required: ["template"], not: { required: ["fields"] } }
|
|
5020
|
+
]
|
|
4862
5021
|
});
|
|
4863
|
-
const ArrayFieldSchema = z13.union([FullArrayFieldSchema, SimplifiedArrayFieldSchema]);
|
|
4864
5022
|
const AllFieldsSchema = AnyFieldSchema;
|
|
4865
5023
|
return {
|
|
4866
5024
|
PageFieldSchema,
|
|
4867
5025
|
RowFieldSchema,
|
|
4868
5026
|
GroupFieldSchema,
|
|
4869
5027
|
ArrayFieldSchema,
|
|
5028
|
+
ContainerFieldSchema,
|
|
4870
5029
|
AllFieldsSchema
|
|
4871
5030
|
};
|
|
4872
5031
|
}
|
|
@@ -5075,6 +5234,9 @@ var BaseFormConfigSchema = z18.object({
|
|
|
5075
5234
|
defaultValidationMessages: ValidationMessagesSchema.optional()
|
|
5076
5235
|
});
|
|
5077
5236
|
|
|
5237
|
+
// internal/dynamic-forms-validation/validate/src/json-schema/form-config-json-schema.ts
|
|
5238
|
+
import { z as z55 } from "zod";
|
|
5239
|
+
|
|
5078
5240
|
// internal/dynamic-forms-validation/material/src/props/mat-common-props.schema.ts
|
|
5079
5241
|
import { z as z19 } from "zod";
|
|
5080
5242
|
var MatFormFieldAppearanceSchema = z19.enum(["fill", "outline"]);
|
|
@@ -6564,8 +6726,14 @@ var IonicFormDefaultPropsSchema = z54.object({
|
|
|
6564
6726
|
});
|
|
6565
6727
|
var IonicFormConfigSchema = createFormConfigSchema(IonicFieldSchema, IonicFormDefaultPropsSchema);
|
|
6566
6728
|
|
|
6729
|
+
// internal/dynamic-forms-validation/validate/src/json-schema/form-config-json-schema.ts
|
|
6730
|
+
function toJsonSchema(schema, name, basePath) {
|
|
6731
|
+
const jsonSchema = z55.toJSONSchema(schema, { target: "draft-7", io: "input", unrepresentable: "any" });
|
|
6732
|
+
jsonSchema["$id"] = basePath ? `${basePath.replace(/\/$/, "")}/${name}` : name;
|
|
6733
|
+
return jsonSchema;
|
|
6734
|
+
}
|
|
6735
|
+
|
|
6567
6736
|
// internal/dynamic-forms-validation/validate/src/json-schema/field-type-json-schemas.ts
|
|
6568
|
-
import zodToJsonSchema from "zod-to-json-schema";
|
|
6569
6737
|
var fieldTypeSchemas = {
|
|
6570
6738
|
material: {
|
|
6571
6739
|
input: MatInputFieldSchema,
|
|
@@ -6654,41 +6822,34 @@ function getFieldTypeJsonSchema(uiIntegration, fieldType) {
|
|
|
6654
6822
|
if (!schema) {
|
|
6655
6823
|
throw new Error(`Unknown field type: ${fieldType}. Valid: ${getSupportedFieldTypes().join(", ")}`);
|
|
6656
6824
|
}
|
|
6657
|
-
|
|
6658
|
-
name: `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`,
|
|
6659
|
-
$refStrategy: "none",
|
|
6660
|
-
errorMessages: true
|
|
6661
|
-
});
|
|
6662
|
-
return jsonSchema;
|
|
6825
|
+
return toJsonSchema(schema, `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`);
|
|
6663
6826
|
}
|
|
6664
6827
|
|
|
6665
6828
|
// internal/dynamic-forms-validation/src/lib/schemas/field-type-names.ts
|
|
6666
|
-
import { z as
|
|
6829
|
+
import { z as z56 } from "zod";
|
|
6667
6830
|
var MAX_DEPTH = 20;
|
|
6668
6831
|
function collectDiscriminator(schema, into) {
|
|
6669
|
-
if (schema instanceof
|
|
6670
|
-
|
|
6832
|
+
if (schema instanceof z56.ZodLiteral) {
|
|
6833
|
+
for (const value of schema.def.values) {
|
|
6834
|
+
if (typeof value === "string") into.add(value);
|
|
6835
|
+
}
|
|
6671
6836
|
return;
|
|
6672
6837
|
}
|
|
6673
|
-
if (schema instanceof
|
|
6838
|
+
if (schema instanceof z56.ZodEnum) {
|
|
6674
6839
|
for (const value of schema.options) into.add(value);
|
|
6675
6840
|
}
|
|
6676
6841
|
}
|
|
6677
6842
|
function visit(schema, into, depth) {
|
|
6678
6843
|
if (depth > MAX_DEPTH) return;
|
|
6679
|
-
if (schema instanceof
|
|
6680
|
-
visit(schema.
|
|
6844
|
+
if (schema instanceof z56.ZodLazy) {
|
|
6845
|
+
visit(schema.unwrap(), into, depth + 1);
|
|
6681
6846
|
return;
|
|
6682
6847
|
}
|
|
6683
|
-
if (schema instanceof
|
|
6684
|
-
visit(schema.innerType(), into, depth + 1);
|
|
6685
|
-
return;
|
|
6686
|
-
}
|
|
6687
|
-
if (schema instanceof z55.ZodDiscriminatedUnion || schema instanceof z55.ZodUnion) {
|
|
6848
|
+
if (schema instanceof z56.ZodUnion) {
|
|
6688
6849
|
for (const option of schema.options) visit(option, into, depth + 1);
|
|
6689
6850
|
return;
|
|
6690
6851
|
}
|
|
6691
|
-
if (schema instanceof
|
|
6852
|
+
if (schema instanceof z56.ZodObject) {
|
|
6692
6853
|
collectDiscriminator(schema.shape["type"], into);
|
|
6693
6854
|
return;
|
|
6694
6855
|
}
|
|
@@ -6761,14 +6922,14 @@ var DID_YOU_MEAN = {
|
|
|
6761
6922
|
validation: "Did you mean to use validator shorthand (`required`, `email`, `min`, etc.) or `validators: [...]` array?"
|
|
6762
6923
|
};
|
|
6763
6924
|
function formatZodError(error, uiIntegration, config) {
|
|
6764
|
-
const errors = error.
|
|
6925
|
+
const errors = error.issues.map((err) => {
|
|
6765
6926
|
const path = err.path.join(".") || "root";
|
|
6766
6927
|
let message = err.message;
|
|
6767
6928
|
let expected;
|
|
6768
6929
|
let received;
|
|
6769
6930
|
if ("expected" in err) expected = String(err.expected);
|
|
6770
6931
|
if ("received" in err) received = String(err.received);
|
|
6771
|
-
if (
|
|
6932
|
+
if (err.code === "invalid_union") {
|
|
6772
6933
|
const fieldMatch = path.match(/fields\.(\d+)/);
|
|
6773
6934
|
if (fieldMatch && config && typeof config === "object" && "fields" in config) {
|
|
6774
6935
|
const fields = config.fields;
|
|
@@ -6899,123 +7060,31 @@ var FORBIDDEN_HIDDEN_PROPS = [
|
|
|
6899
7060
|
"pattern",
|
|
6900
7061
|
"placeholder"
|
|
6901
7062
|
];
|
|
6902
|
-
var CONTAINER_FORBIDDEN_PROPS = ["
|
|
7063
|
+
var CONTAINER_FORBIDDEN_PROPS = ["email", "min", "max", "pattern", "value"];
|
|
7064
|
+
var VALIDATING_CONTAINERS = ["group", "array"];
|
|
7065
|
+
var CONTAINER_VALIDATION_PROPS = ["validators", "required", "validationMessages"];
|
|
7066
|
+
var ARRAY_SIZE_PROPS = ["minLength", "maxLength"];
|
|
6903
7067
|
var FIELDS_REQUIRING_OPTIONS = ["select", "radio", "multi-checkbox"];
|
|
6904
7068
|
var NESTING_RULES = {
|
|
6905
7069
|
page: {
|
|
6906
|
-
allowed: [
|
|
6907
|
-
"row",
|
|
6908
|
-
"group",
|
|
6909
|
-
"array",
|
|
6910
|
-
"input",
|
|
6911
|
-
"textarea",
|
|
6912
|
-
"select",
|
|
6913
|
-
"checkbox",
|
|
6914
|
-
"multi-checkbox",
|
|
6915
|
-
"radio",
|
|
6916
|
-
"datepicker",
|
|
6917
|
-
"toggle",
|
|
6918
|
-
"slider",
|
|
6919
|
-
"hidden",
|
|
6920
|
-
"text",
|
|
6921
|
-
"button",
|
|
6922
|
-
"submit",
|
|
6923
|
-
"next",
|
|
6924
|
-
"previous",
|
|
6925
|
-
"add-array-item",
|
|
6926
|
-
"prepend-array-item",
|
|
6927
|
-
"insert-array-item",
|
|
6928
|
-
"remove-array-item",
|
|
6929
|
-
"pop-array-item",
|
|
6930
|
-
"shift-array-item"
|
|
6931
|
-
],
|
|
6932
7070
|
forbidden: ["page"],
|
|
6933
7071
|
message: "Pages cannot be nested inside other containers. ALL top-level fields must be pages if using multi-page mode."
|
|
6934
7072
|
},
|
|
6935
7073
|
row: {
|
|
6936
|
-
allowed: [
|
|
6937
|
-
"group",
|
|
6938
|
-
"array",
|
|
6939
|
-
"input",
|
|
6940
|
-
"textarea",
|
|
6941
|
-
"select",
|
|
6942
|
-
"checkbox",
|
|
6943
|
-
"multi-checkbox",
|
|
6944
|
-
"radio",
|
|
6945
|
-
"datepicker",
|
|
6946
|
-
"toggle",
|
|
6947
|
-
"slider",
|
|
6948
|
-
"text",
|
|
6949
|
-
"button",
|
|
6950
|
-
"submit",
|
|
6951
|
-
"next",
|
|
6952
|
-
"previous",
|
|
6953
|
-
"add-array-item",
|
|
6954
|
-
"prepend-array-item",
|
|
6955
|
-
"insert-array-item",
|
|
6956
|
-
"remove-array-item",
|
|
6957
|
-
"pop-array-item",
|
|
6958
|
-
"shift-array-item"
|
|
6959
|
-
],
|
|
6960
7074
|
forbidden: ["page", "row", "hidden"],
|
|
6961
7075
|
message: "Rows cannot contain pages, other rows, or hidden fields. Hidden fields should be at page or form level."
|
|
6962
7076
|
},
|
|
6963
7077
|
group: {
|
|
6964
|
-
allowed: [
|
|
6965
|
-
"row",
|
|
6966
|
-
"input",
|
|
6967
|
-
"textarea",
|
|
6968
|
-
"select",
|
|
6969
|
-
"checkbox",
|
|
6970
|
-
"multi-checkbox",
|
|
6971
|
-
"radio",
|
|
6972
|
-
"datepicker",
|
|
6973
|
-
"toggle",
|
|
6974
|
-
"slider",
|
|
6975
|
-
"hidden",
|
|
6976
|
-
"text",
|
|
6977
|
-
"button",
|
|
6978
|
-
"submit",
|
|
6979
|
-
"next",
|
|
6980
|
-
"previous",
|
|
6981
|
-
"add-array-item",
|
|
6982
|
-
"prepend-array-item",
|
|
6983
|
-
"insert-array-item",
|
|
6984
|
-
"remove-array-item",
|
|
6985
|
-
"pop-array-item",
|
|
6986
|
-
"shift-array-item"
|
|
6987
|
-
],
|
|
6988
7078
|
forbidden: ["page", "group"],
|
|
6989
7079
|
message: "Groups cannot contain pages or other groups (no nested groups)."
|
|
6990
7080
|
},
|
|
6991
7081
|
array: {
|
|
6992
|
-
allowed: [
|
|
6993
|
-
"row",
|
|
6994
|
-
"group",
|
|
6995
|
-
"input",
|
|
6996
|
-
"textarea",
|
|
6997
|
-
"select",
|
|
6998
|
-
"checkbox",
|
|
6999
|
-
"multi-checkbox",
|
|
7000
|
-
"radio",
|
|
7001
|
-
"datepicker",
|
|
7002
|
-
"toggle",
|
|
7003
|
-
"slider",
|
|
7004
|
-
"hidden",
|
|
7005
|
-
"text",
|
|
7006
|
-
"button",
|
|
7007
|
-
"submit",
|
|
7008
|
-
"next",
|
|
7009
|
-
"previous",
|
|
7010
|
-
"add-array-item",
|
|
7011
|
-
"prepend-array-item",
|
|
7012
|
-
"insert-array-item",
|
|
7013
|
-
"remove-array-item",
|
|
7014
|
-
"pop-array-item",
|
|
7015
|
-
"shift-array-item"
|
|
7016
|
-
],
|
|
7017
7082
|
forbidden: ["page", "array"],
|
|
7018
7083
|
message: "Arrays cannot contain pages or other arrays (no nested arrays)."
|
|
7084
|
+
},
|
|
7085
|
+
container: {
|
|
7086
|
+
forbidden: ["page"],
|
|
7087
|
+
message: "Containers cannot contain pages. Pages are top-level fields only."
|
|
7019
7088
|
}
|
|
7020
7089
|
};
|
|
7021
7090
|
var EXPECTED_STRUCTURE = {
|
|
@@ -7027,7 +7096,8 @@ var EXPECTED_STRUCTURE = {
|
|
|
7027
7096
|
row: `{ key: 'rowKey', type: 'row', fields: [...childFields] }`,
|
|
7028
7097
|
group: `{ key: 'groupKey', type: 'group', fields: [...childFields] }`,
|
|
7029
7098
|
array: `Full API: { key: 'arrayKey', type: 'array', fields: [...itemDefs] } OR Simplified API: { key: 'arrayKey', type: 'array', template: { type: 'input', label: 'Item' }, value: [] }`,
|
|
7030
|
-
page: `{ key: 'pageKey', type: 'page', fields: [...childFields, { key: 'next', type: 'next', label: 'Next' }] }
|
|
7099
|
+
page: `{ key: 'pageKey', type: 'page', fields: [...childFields, { key: 'next', type: 'next', label: 'Next' }] }`,
|
|
7100
|
+
container: `{ key: 'containerKey', type: 'container', wrappers: [{ type: 'css', cssClasses: 'card' }], fields: [...childFields] }`
|
|
7031
7101
|
};
|
|
7032
7102
|
function preValidateConfig(config) {
|
|
7033
7103
|
const errors = [];
|
|
@@ -7072,7 +7142,7 @@ function preValidateConfig(config) {
|
|
|
7072
7142
|
}
|
|
7073
7143
|
});
|
|
7074
7144
|
}
|
|
7075
|
-
if (["row", "group", "array"].includes(fieldType || "")) {
|
|
7145
|
+
if (["row", "group", "array", "container"].includes(fieldType || "")) {
|
|
7076
7146
|
for (const prop of INVALID_CONTAINER_PROPS) {
|
|
7077
7147
|
if (prop in f) {
|
|
7078
7148
|
errors.push({
|
|
@@ -7089,10 +7159,30 @@ function preValidateConfig(config) {
|
|
|
7089
7159
|
const expected = EXPECTED_STRUCTURE[fieldType || ""] || "";
|
|
7090
7160
|
errors.push({
|
|
7091
7161
|
path: `${path}.${prop}`,
|
|
7092
|
-
message: `"${fieldType}" containers do NOT support "${prop}". Containers don't hold values
|
|
7162
|
+
message: `"${fieldType}" containers do NOT support "${prop}". Containers don't hold values - they are purely for layout/grouping.${expected ? ` Expected structure: ${expected}` : ""}`
|
|
7093
7163
|
});
|
|
7094
7164
|
}
|
|
7095
7165
|
}
|
|
7166
|
+
if (!VALIDATING_CONTAINERS.includes(fieldType || "")) {
|
|
7167
|
+
for (const prop of CONTAINER_VALIDATION_PROPS) {
|
|
7168
|
+
if (prop in f) {
|
|
7169
|
+
errors.push({
|
|
7170
|
+
path: `${path}.${prop}`,
|
|
7171
|
+
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.`
|
|
7172
|
+
});
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
}
|
|
7176
|
+
if (fieldType !== "array") {
|
|
7177
|
+
for (const prop of ARRAY_SIZE_PROPS) {
|
|
7178
|
+
if (prop in f) {
|
|
7179
|
+
errors.push({
|
|
7180
|
+
path: `${path}.${prop}`,
|
|
7181
|
+
message: `"${fieldType}" containers do NOT support "${prop}". Array size constraints apply to "array" fields only.`
|
|
7182
|
+
});
|
|
7183
|
+
}
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
7096
7186
|
if ("logic" in f && Array.isArray(f["logic"])) {
|
|
7097
7187
|
const nonHiddenLogic = f["logic"].filter(
|
|
7098
7188
|
(l) => l && typeof l === "object" && l["type"] !== "hidden"
|
|
@@ -7123,6 +7213,14 @@ function preValidateConfig(config) {
|
|
|
7123
7213
|
});
|
|
7124
7214
|
}
|
|
7125
7215
|
}
|
|
7216
|
+
for (const prop of [...CONTAINER_VALIDATION_PROPS, ...ARRAY_SIZE_PROPS]) {
|
|
7217
|
+
if (prop in f) {
|
|
7218
|
+
errors.push({
|
|
7219
|
+
path: `${path}.${prop}`,
|
|
7220
|
+
message: `"page" containers do NOT support "${prop}". Pages flatten into the form and have no schema path of their own.`
|
|
7221
|
+
});
|
|
7222
|
+
}
|
|
7223
|
+
}
|
|
7126
7224
|
}
|
|
7127
7225
|
if (parentType && NESTING_RULES[parentType]) {
|
|
7128
7226
|
const rules = NESTING_RULES[parentType];
|
|
@@ -7203,7 +7301,7 @@ function preValidateConfig(config) {
|
|
|
7203
7301
|
});
|
|
7204
7302
|
}
|
|
7205
7303
|
}
|
|
7206
|
-
if (["row", "group", "page"].includes(fieldType || "")) {
|
|
7304
|
+
if (["row", "group", "page", "container"].includes(fieldType || "")) {
|
|
7207
7305
|
if (!("fields" in f)) {
|
|
7208
7306
|
errors.push({
|
|
7209
7307
|
path: `${path}.fields`,
|
|
@@ -7216,6 +7314,19 @@ function preValidateConfig(config) {
|
|
|
7216
7314
|
});
|
|
7217
7315
|
}
|
|
7218
7316
|
}
|
|
7317
|
+
if (fieldType === "container") {
|
|
7318
|
+
if (!("wrappers" in f)) {
|
|
7319
|
+
errors.push({
|
|
7320
|
+
path: `${path}.wrappers`,
|
|
7321
|
+
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"]}`
|
|
7322
|
+
});
|
|
7323
|
+
} else if (!Array.isArray(f["wrappers"])) {
|
|
7324
|
+
errors.push({
|
|
7325
|
+
path: `${path}.wrappers`,
|
|
7326
|
+
message: `"container" container "${fieldKey || "unknown"}" has invalid "wrappers" - must be an array of wrapper objects, not ${typeof f["wrappers"]}.`
|
|
7327
|
+
});
|
|
7328
|
+
}
|
|
7329
|
+
}
|
|
7219
7330
|
if (fieldType === "array") {
|
|
7220
7331
|
const hasFields = "fields" in f;
|
|
7221
7332
|
const hasTemplate = "template" in f;
|
|
@@ -8050,7 +8161,7 @@ function registerResources(server) {
|
|
|
8050
8161
|
}
|
|
8051
8162
|
|
|
8052
8163
|
// packages/dynamic-form-mcp/src/tools/lookup.tool.ts
|
|
8053
|
-
import { z as
|
|
8164
|
+
import { z as z57 } from "zod";
|
|
8054
8165
|
|
|
8055
8166
|
// packages/dynamic-form-mcp/src/tools/data/lookup-topics.ts
|
|
8056
8167
|
var TOPICS = {
|
|
@@ -9785,25 +9896,42 @@ provideDynamicForm({
|
|
|
9785
9896
|
| FieldContext | \`ctx.value()\` | \`ctx.valueOf('path' as never)\` | custom validators |`
|
|
9786
9897
|
},
|
|
9787
9898
|
containers: {
|
|
9788
|
-
brief: `| Container | Label? | Logic? | Notes |
|
|
9789
|
-
|
|
9790
|
-
| page | NO | hidden only | Nav buttons INSIDE |
|
|
9791
|
-
| group | NO | hidden only | Creates nested object |
|
|
9792
|
-
| array | NO | hidden only | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9793
|
-
| row | NO | hidden only | Layout only
|
|
9899
|
+
brief: `| Container | Label? | Logic? | Value | Notes |
|
|
9900
|
+
|-----------|--------|--------|-------|-------|
|
|
9901
|
+
| page | NO | hidden only | flattened | Nav buttons INSIDE |
|
|
9902
|
+
| group | NO | hidden only | nested under its key | Creates nested object |
|
|
9903
|
+
| array | NO | hidden only | nested under its key | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9904
|
+
| row | NO | hidden only | flattened | Layout only |
|
|
9905
|
+
| container | NO | hidden only | flattened | Layout + REQUIRED \`wrappers\` chain |`,
|
|
9794
9906
|
full: `# Container Rules
|
|
9795
9907
|
|
|
9796
|
-
| Container | Label? | Logic? | Allowed Children | Notes |
|
|
9797
|
-
|
|
9798
|
-
| page | NO | YES (hidden only) | rows, groups, arrays, leaf fields, buttons | Nav buttons go INSIDE |
|
|
9799
|
-
| group | NO | YES (hidden only) | rows, arrays, leaf fields (NOT pages, groups) | Creates nested object |
|
|
9800
|
-
| array | NO | YES (hidden only) | rows, groups, leaf fields (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9801
|
-
| row | NO | YES (hidden only) | groups, arrays, containers,
|
|
9908
|
+
| Container | Label? | Logic? | Value Handling | Allowed Children | Notes |
|
|
9909
|
+
|-----------|--------|--------|----------------|------------------|-------|
|
|
9910
|
+
| page | NO | YES (hidden only) | flattened | rows, groups, arrays, containers, hidden, leaf fields, buttons (NOT pages) | Nav buttons go INSIDE |
|
|
9911
|
+
| group | NO | YES (hidden only) | nested under its key | rows, arrays, containers, hidden, leaf fields, buttons (NOT pages, groups) | Creates nested object |
|
|
9912
|
+
| 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) |
|
|
9913
|
+
| row | NO | YES (hidden only) | flattened | groups, arrays, containers, leaf fields, buttons (NOT pages, nested rows, hidden) | Layout only |
|
|
9914
|
+
| container | NO | YES (hidden only) | flattened | rows, groups, arrays, nested containers, hidden, leaf fields, buttons (NOT pages) | \`wrappers\` is REQUIRED (use [] for none) |
|
|
9915
|
+
|
|
9916
|
+
**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.
|
|
9917
|
+
|
|
9918
|
+
That difference is not cosmetic, so these are not interchangeable:
|
|
9919
|
+
|
|
9920
|
+
\`\`\`typescript
|
|
9921
|
+
// container \u2192 { street: '', city: '' } (chrome only, key not in the value)
|
|
9922
|
+
{ key: 'billing', type: 'container', wrappers: [{ type: 'css', cssClasses: 'card' }], fields: [street, city] }
|
|
9923
|
+
|
|
9924
|
+
// group \u2192 { billing: { street: '', city: '' } } (adds a level to the value)
|
|
9925
|
+
{ key: 'billing', type: 'group', fields: [street, city] }
|
|
9926
|
+
\`\`\`
|
|
9927
|
+
|
|
9928
|
+
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.
|
|
9802
9929
|
|
|
9803
9930
|
**CRITICAL:**
|
|
9804
|
-
- All containers (page, group, row, array) support only the \`hidden\` logic type for conditional visibility.
|
|
9931
|
+
- All containers (page, group, row, array, container) support only the \`hidden\` logic type for conditional visibility.
|
|
9805
9932
|
- For other logic types (disabled, required, readonly, derivation), apply them to child fields instead.
|
|
9806
|
-
- Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode
|
|
9933
|
+
- Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode.
|
|
9934
|
+
- Rows reject nested rows and hidden fields; put hidden fields at page or form level.`
|
|
9807
9935
|
},
|
|
9808
9936
|
"field-placement": {
|
|
9809
9937
|
brief: `**Multi-page:** ALL root fields must be pages. Hidden fields go INSIDE first page.
|
|
@@ -10597,7 +10725,8 @@ var TOPIC_DESCRIPTIONS = {
|
|
|
10597
10725
|
"field-placement": "Which fields can go where \u2014 nesting and placement rules",
|
|
10598
10726
|
"logic-matrix": "Which logic types are supported on which field types",
|
|
10599
10727
|
"context-api": "FieldContext API \u2014 valueOf(), formValue, parent, arrayIndex",
|
|
10600
|
-
containers: "Overview of all container types (group, row, array, page)",
|
|
10728
|
+
containers: "Overview of all container types (group, row, array, container, page) and what each may hold",
|
|
10729
|
+
container: "Wraps children in a wrapper chain; flattens their values into the parent like a row",
|
|
10601
10730
|
"array-buttons": "Add/remove array item button configuration",
|
|
10602
10731
|
"custom-validators": "Custom validator functions via validators array",
|
|
10603
10732
|
conditions: "Condition operators and types for logic rules",
|
|
@@ -10857,7 +10986,7 @@ function formatTopicItem(key) {
|
|
|
10857
10986
|
}
|
|
10858
10987
|
function getTopicList() {
|
|
10859
10988
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
10860
|
-
const containers = ["
|
|
10989
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
10861
10990
|
const wrappers = ["wrappers", ...getWrappers().map((w) => w.type)];
|
|
10862
10991
|
const addons = ["addons", ...getAddonTypes().map((k) => k.type)];
|
|
10863
10992
|
const concepts = [
|
|
@@ -10913,7 +11042,7 @@ ${patterns.map(formatTopicItem).join("\n")}
|
|
|
10913
11042
|
}
|
|
10914
11043
|
function formatFieldInfoFull(field, uiFieldType) {
|
|
10915
11044
|
const lines = [];
|
|
10916
|
-
const isContainer =
|
|
11045
|
+
const isContainer = field.category === "container";
|
|
10917
11046
|
const isHidden = field.type === "hidden";
|
|
10918
11047
|
lines.push(`## ${field.type} field`);
|
|
10919
11048
|
lines.push("");
|
|
@@ -11342,11 +11471,11 @@ Patterns: field-placement, logic-matrix, context-api, containers, multi-page-got
|
|
|
11342
11471
|
Use topic="list" to see all available topics with descriptions.
|
|
11343
11472
|
Use ngforge_search query="your question" to find topics by keyword.`,
|
|
11344
11473
|
{
|
|
11345
|
-
topic:
|
|
11346
|
-
'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'
|
|
11474
|
+
topic: z57.string().describe(
|
|
11475
|
+
'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'
|
|
11347
11476
|
),
|
|
11348
|
-
depth:
|
|
11349
|
-
uiIntegration:
|
|
11477
|
+
depth: z57.enum(["brief", "full", "schema"]).default("full").describe("brief=quick syntax (~20 lines), full=complete docs with examples, schema=include JSON schema"),
|
|
11478
|
+
uiIntegration: z57.enum(UI_INTEGRATIONS2).optional().describe("Filter UI-specific info (only with depth=schema). material, bootstrap, primeng, ionic")
|
|
11350
11479
|
},
|
|
11351
11480
|
async ({ topic, depth, uiIntegration }) => {
|
|
11352
11481
|
const normalizedTopic = topic.toLowerCase().trim();
|
|
@@ -11394,7 +11523,7 @@ ${nextSteps}`;
|
|
|
11394
11523
|
}
|
|
11395
11524
|
|
|
11396
11525
|
// packages/dynamic-form-mcp/src/tools/examples.tool.ts
|
|
11397
|
-
import { z as
|
|
11526
|
+
import { z as z58 } from "zod";
|
|
11398
11527
|
var PATTERNS = {
|
|
11399
11528
|
// ========== MINIMAL PATTERNS (NEW) ==========
|
|
11400
11529
|
"minimal-multipage": {
|
|
@@ -13659,10 +13788,10 @@ Standard patterns: derivation, property-derivation, multi-page, conditional, val
|
|
|
13659
13788
|
Use pattern="list" to see all available patterns.
|
|
13660
13789
|
Use depth="minimal" for code-only output (no markdown).`,
|
|
13661
13790
|
{
|
|
13662
|
-
pattern:
|
|
13791
|
+
pattern: z58.string().optional().describe(
|
|
13663
13792
|
'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.'
|
|
13664
13793
|
),
|
|
13665
|
-
depth:
|
|
13794
|
+
depth: z58.enum(["minimal", "brief", "full", "explained"]).default("full").describe(
|
|
13666
13795
|
"minimal=code only (~20-50 lines), brief=code+summary, full=code+comments (default), explained=code+detailed explanation"
|
|
13667
13796
|
)
|
|
13668
13797
|
},
|
|
@@ -13727,7 +13856,7 @@ function extractCode(markdown) {
|
|
|
13727
13856
|
}
|
|
13728
13857
|
|
|
13729
13858
|
// packages/dynamic-form-mcp/src/tools/validate.tool.ts
|
|
13730
|
-
import { z as
|
|
13859
|
+
import { z as z59 } from "zod";
|
|
13731
13860
|
var UI_INTEGRATIONS3 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13732
13861
|
var ERROR_TOPIC_HINTS = [
|
|
13733
13862
|
{ pattern: /options/i, hint: '`ngforge_lookup topic="options-format"` \u2014 correct options syntax' },
|
|
@@ -13780,10 +13909,10 @@ Example errors you'll see:
|
|
|
13780
13909
|
- "options MUST be at FIELD level, NOT inside props"
|
|
13781
13910
|
- "containers only support 'hidden' logic type"`,
|
|
13782
13911
|
{
|
|
13783
|
-
config:
|
|
13912
|
+
config: z59.union([z59.string(), z59.object({}).passthrough()]).describe(
|
|
13784
13913
|
"File path (.ts/.js) OR JSON string OR JSON object. Auto-detects: paths ending in .ts/.js or starting with / are treated as files."
|
|
13785
13914
|
),
|
|
13786
|
-
uiIntegration:
|
|
13915
|
+
uiIntegration: z59.enum(UI_INTEGRATIONS3).default("material").describe("UI library to validate against (material, bootstrap, primeng, ionic). Defaults to material.")
|
|
13787
13916
|
},
|
|
13788
13917
|
async ({ config, uiIntegration }) => {
|
|
13789
13918
|
try {
|
|
@@ -13875,7 +14004,7 @@ If validating JSON, ensure:
|
|
|
13875
14004
|
}
|
|
13876
14005
|
|
|
13877
14006
|
// packages/dynamic-form-mcp/src/tools/scaffold.tool.ts
|
|
13878
|
-
import { z as
|
|
14007
|
+
import { z as z60 } from "zod";
|
|
13879
14008
|
var UI_INTEGRATIONS4 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13880
14009
|
function parseFieldDefs(fields) {
|
|
13881
14010
|
return fields.map((f) => {
|
|
@@ -14130,12 +14259,12 @@ Examples:
|
|
|
14130
14259
|
- With groups: pages=0 groups=["address","billing"]
|
|
14131
14260
|
- With hidden: hidden=["userId:abc123","source:web"]`,
|
|
14132
14261
|
{
|
|
14133
|
-
pages:
|
|
14134
|
-
arrays:
|
|
14135
|
-
groups:
|
|
14136
|
-
hidden:
|
|
14137
|
-
fields:
|
|
14138
|
-
uiIntegration:
|
|
14262
|
+
pages: z60.number().min(0).max(10).default(0).describe("Number of pages (0 = single-page form, 1-10 = multi-page wizard)"),
|
|
14263
|
+
arrays: z60.array(z60.string()).optional().describe('Array field names for dynamic lists, e.g., ["contacts", "addresses"]'),
|
|
14264
|
+
groups: z60.array(z60.string()).optional().describe('Group field names for nested objects, e.g., ["address", "billing"]'),
|
|
14265
|
+
hidden: z60.array(z60.string()).optional().describe('Hidden fields as "name:value" pairs, e.g., ["userId:abc", "version:1"]'),
|
|
14266
|
+
fields: z60.array(z60.string()).optional().describe('Basic fields as "name:type" pairs, e.g., ["email:input", "country:select", "age:slider"]'),
|
|
14267
|
+
uiIntegration: z60.enum(UI_INTEGRATIONS4).default("material").describe("UI library (for future UI-specific props). Defaults to material.")
|
|
14139
14268
|
},
|
|
14140
14269
|
async ({ pages, arrays = [], groups = [], hidden = [], fields = [] }) => {
|
|
14141
14270
|
const fieldDefs = parseFieldDefs(fields);
|
|
@@ -14177,11 +14306,11 @@ Examples:
|
|
|
14177
14306
|
}
|
|
14178
14307
|
|
|
14179
14308
|
// packages/dynamic-form-mcp/src/tools/search.tool.ts
|
|
14180
|
-
import { z as
|
|
14309
|
+
import { z as z61 } from "zod";
|
|
14181
14310
|
var searchIndex = null;
|
|
14182
14311
|
function getTopicSubcategory(key) {
|
|
14183
14312
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
14184
|
-
const containers = ["
|
|
14313
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
14185
14314
|
const concepts = [
|
|
14186
14315
|
"validation",
|
|
14187
14316
|
"validation-messages",
|
|
@@ -14221,6 +14350,19 @@ function buildIndex() {
|
|
|
14221
14350
|
toolCall: `ngforge_lookup topic="${key}"`
|
|
14222
14351
|
});
|
|
14223
14352
|
}
|
|
14353
|
+
for (const field of getFieldTypes()) {
|
|
14354
|
+
if (TOPICS[field.type]) continue;
|
|
14355
|
+
const description = TOPIC_DESCRIPTIONS[field.type] || field.description;
|
|
14356
|
+
entries.push({
|
|
14357
|
+
id: field.type,
|
|
14358
|
+
category: "topic",
|
|
14359
|
+
subcategory: getTopicSubcategory(field.type),
|
|
14360
|
+
description,
|
|
14361
|
+
aliases: aliasMap[field.type] || [],
|
|
14362
|
+
content: `${description} ${field.description}`.toLowerCase(),
|
|
14363
|
+
toolCall: `ngforge_lookup topic="${field.type}"`
|
|
14364
|
+
});
|
|
14365
|
+
}
|
|
14224
14366
|
for (const [key, pattern] of Object.entries(PATTERNS)) {
|
|
14225
14367
|
entries.push({
|
|
14226
14368
|
id: key,
|
|
@@ -14325,7 +14467,7 @@ Use this when you don't know the exact topic name.
|
|
|
14325
14467
|
|
|
14326
14468
|
New to ng-forge? Start with: ngforge_lookup topic="workflow"`,
|
|
14327
14469
|
{
|
|
14328
|
-
query:
|
|
14470
|
+
query: z61.string().describe("Natural language or keywords to search for")
|
|
14329
14471
|
},
|
|
14330
14472
|
async ({ query }) => {
|
|
14331
14473
|
const index = buildIndex();
|