@ng-forge/dynamic-form-mcp 1.2.0-next.8 → 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
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',
|
|
@@ -4811,15 +4882,23 @@ var ContainerLogicSchema = z13.object({
|
|
|
4811
4882
|
type: z13.literal("hidden"),
|
|
4812
4883
|
condition: z13.union([ConditionalExpressionSchema, z13.boolean()])
|
|
4813
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
|
+
};
|
|
4814
4892
|
function createContainerSchemas(options) {
|
|
4815
4893
|
const { leafFieldSchema } = options;
|
|
4816
4894
|
const AnyFieldSchema = z13.lazy(
|
|
4817
|
-
() => z13.
|
|
4895
|
+
() => z13.discriminatedUnion("type", [
|
|
4818
4896
|
leafFieldSchema,
|
|
4819
4897
|
PageFieldSchema,
|
|
4820
4898
|
RowFieldSchema,
|
|
4821
4899
|
GroupFieldSchema,
|
|
4822
|
-
ArrayFieldSchema
|
|
4900
|
+
ArrayFieldSchema,
|
|
4901
|
+
ContainerFieldSchema
|
|
4823
4902
|
])
|
|
4824
4903
|
);
|
|
4825
4904
|
const ContainerBaseSchema = BaseFieldDefSchema.omit({
|
|
@@ -4831,6 +4910,18 @@ function createContainerSchemas(options) {
|
|
|
4831
4910
|
label: z13.never().optional(),
|
|
4832
4911
|
meta: z13.never().optional()
|
|
4833
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
|
+
});
|
|
4834
4925
|
const PageFieldSchema = ContainerBaseSchema.extend({
|
|
4835
4926
|
type: z13.literal("page"),
|
|
4836
4927
|
fields: z13.array(AnyFieldSchema),
|
|
@@ -4847,33 +4938,27 @@ function createContainerSchemas(options) {
|
|
|
4847
4938
|
const GroupFieldSchema = ContainerBaseSchema.extend({
|
|
4848
4939
|
type: z13.literal("group"),
|
|
4849
4940
|
fields: z13.array(AnyFieldSchema),
|
|
4941
|
+
...ContainerValidationShape,
|
|
4850
4942
|
// Container logic - only 'hidden' type allowed (same as pages)
|
|
4851
4943
|
logic: z13.array(ContainerLogicSchema).optional()
|
|
4852
4944
|
});
|
|
4853
4945
|
const ArrayButtonConfigSchema = z13.object({
|
|
4854
4946
|
label: z13.string().optional(),
|
|
4855
|
-
props: z13.record(z13.unknown()).optional()
|
|
4856
|
-
});
|
|
4857
|
-
const FullArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4858
|
-
type: z13.literal("array"),
|
|
4859
|
-
fields: z13.array(AnyFieldSchema),
|
|
4860
|
-
logic: z13.array(ContainerLogicSchema).optional(),
|
|
4861
|
-
// Full API does not use template
|
|
4862
|
-
template: z13.never().optional(),
|
|
4863
|
-
// Array length validation
|
|
4864
|
-
minLength: z13.number().int().min(0).optional(),
|
|
4865
|
-
maxLength: z13.number().int().min(0).optional()
|
|
4947
|
+
props: z13.record(z13.string(), z13.unknown()).optional()
|
|
4866
4948
|
});
|
|
4867
4949
|
const ArrayAllowedChildSchema = z13.lazy(
|
|
4868
4950
|
() => AnyFieldSchema.refine((field) => field.type !== "page" && field.type !== "array", {
|
|
4869
4951
|
message: "Array templates cannot contain page or array fields. Only leaf fields, rows, and groups are allowed."
|
|
4870
4952
|
})
|
|
4871
4953
|
);
|
|
4872
|
-
const
|
|
4954
|
+
const ArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4873
4955
|
type: z13.literal("array"),
|
|
4874
|
-
|
|
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).
|
|
4875
4960
|
// Only ArrayAllowedChildren (leaf fields, rows, groups) are valid — no pages or nested arrays.
|
|
4876
|
-
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]),
|
|
4961
|
+
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]).optional(),
|
|
4877
4962
|
// Initial values for the array
|
|
4878
4963
|
value: z13.array(z13.unknown()).optional(),
|
|
4879
4964
|
// Button customization or opt-out (false to disable)
|
|
@@ -4883,18 +4968,64 @@ function createContainerSchemas(options) {
|
|
|
4883
4968
|
// Array length validation
|
|
4884
4969
|
minLength: z13.number().int().min(0).optional(),
|
|
4885
4970
|
maxLength: z13.number().int().min(0).optional(),
|
|
4886
|
-
//
|
|
4887
|
-
|
|
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.
|
|
4888
4974
|
minItems: z13.never().optional(),
|
|
4889
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
|
+
]
|
|
4890
5021
|
});
|
|
4891
|
-
const ArrayFieldSchema = z13.union([FullArrayFieldSchema, SimplifiedArrayFieldSchema]);
|
|
4892
5022
|
const AllFieldsSchema = AnyFieldSchema;
|
|
4893
5023
|
return {
|
|
4894
5024
|
PageFieldSchema,
|
|
4895
5025
|
RowFieldSchema,
|
|
4896
5026
|
GroupFieldSchema,
|
|
4897
5027
|
ArrayFieldSchema,
|
|
5028
|
+
ContainerFieldSchema,
|
|
4898
5029
|
AllFieldsSchema
|
|
4899
5030
|
};
|
|
4900
5031
|
}
|
|
@@ -5103,6 +5234,9 @@ var BaseFormConfigSchema = z18.object({
|
|
|
5103
5234
|
defaultValidationMessages: ValidationMessagesSchema.optional()
|
|
5104
5235
|
});
|
|
5105
5236
|
|
|
5237
|
+
// internal/dynamic-forms-validation/validate/src/json-schema/form-config-json-schema.ts
|
|
5238
|
+
import { z as z55 } from "zod";
|
|
5239
|
+
|
|
5106
5240
|
// internal/dynamic-forms-validation/material/src/props/mat-common-props.schema.ts
|
|
5107
5241
|
import { z as z19 } from "zod";
|
|
5108
5242
|
var MatFormFieldAppearanceSchema = z19.enum(["fill", "outline"]);
|
|
@@ -6592,8 +6726,14 @@ var IonicFormDefaultPropsSchema = z54.object({
|
|
|
6592
6726
|
});
|
|
6593
6727
|
var IonicFormConfigSchema = createFormConfigSchema(IonicFieldSchema, IonicFormDefaultPropsSchema);
|
|
6594
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
|
+
|
|
6595
6736
|
// internal/dynamic-forms-validation/validate/src/json-schema/field-type-json-schemas.ts
|
|
6596
|
-
import zodToJsonSchema from "zod-to-json-schema";
|
|
6597
6737
|
var fieldTypeSchemas = {
|
|
6598
6738
|
material: {
|
|
6599
6739
|
input: MatInputFieldSchema,
|
|
@@ -6682,41 +6822,34 @@ function getFieldTypeJsonSchema(uiIntegration, fieldType) {
|
|
|
6682
6822
|
if (!schema) {
|
|
6683
6823
|
throw new Error(`Unknown field type: ${fieldType}. Valid: ${getSupportedFieldTypes().join(", ")}`);
|
|
6684
6824
|
}
|
|
6685
|
-
|
|
6686
|
-
name: `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`,
|
|
6687
|
-
$refStrategy: "none",
|
|
6688
|
-
errorMessages: true
|
|
6689
|
-
});
|
|
6690
|
-
return jsonSchema;
|
|
6825
|
+
return toJsonSchema(schema, `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`);
|
|
6691
6826
|
}
|
|
6692
6827
|
|
|
6693
6828
|
// internal/dynamic-forms-validation/src/lib/schemas/field-type-names.ts
|
|
6694
|
-
import { z as
|
|
6829
|
+
import { z as z56 } from "zod";
|
|
6695
6830
|
var MAX_DEPTH = 20;
|
|
6696
6831
|
function collectDiscriminator(schema, into) {
|
|
6697
|
-
if (schema instanceof
|
|
6698
|
-
|
|
6832
|
+
if (schema instanceof z56.ZodLiteral) {
|
|
6833
|
+
for (const value of schema.def.values) {
|
|
6834
|
+
if (typeof value === "string") into.add(value);
|
|
6835
|
+
}
|
|
6699
6836
|
return;
|
|
6700
6837
|
}
|
|
6701
|
-
if (schema instanceof
|
|
6838
|
+
if (schema instanceof z56.ZodEnum) {
|
|
6702
6839
|
for (const value of schema.options) into.add(value);
|
|
6703
6840
|
}
|
|
6704
6841
|
}
|
|
6705
6842
|
function visit(schema, into, depth) {
|
|
6706
6843
|
if (depth > MAX_DEPTH) return;
|
|
6707
|
-
if (schema instanceof
|
|
6708
|
-
visit(schema.
|
|
6844
|
+
if (schema instanceof z56.ZodLazy) {
|
|
6845
|
+
visit(schema.unwrap(), into, depth + 1);
|
|
6709
6846
|
return;
|
|
6710
6847
|
}
|
|
6711
|
-
if (schema instanceof
|
|
6712
|
-
visit(schema.innerType(), into, depth + 1);
|
|
6713
|
-
return;
|
|
6714
|
-
}
|
|
6715
|
-
if (schema instanceof z55.ZodDiscriminatedUnion || schema instanceof z55.ZodUnion) {
|
|
6848
|
+
if (schema instanceof z56.ZodUnion) {
|
|
6716
6849
|
for (const option of schema.options) visit(option, into, depth + 1);
|
|
6717
6850
|
return;
|
|
6718
6851
|
}
|
|
6719
|
-
if (schema instanceof
|
|
6852
|
+
if (schema instanceof z56.ZodObject) {
|
|
6720
6853
|
collectDiscriminator(schema.shape["type"], into);
|
|
6721
6854
|
return;
|
|
6722
6855
|
}
|
|
@@ -6789,14 +6922,14 @@ var DID_YOU_MEAN = {
|
|
|
6789
6922
|
validation: "Did you mean to use validator shorthand (`required`, `email`, `min`, etc.) or `validators: [...]` array?"
|
|
6790
6923
|
};
|
|
6791
6924
|
function formatZodError(error, uiIntegration, config) {
|
|
6792
|
-
const errors = error.
|
|
6925
|
+
const errors = error.issues.map((err) => {
|
|
6793
6926
|
const path = err.path.join(".") || "root";
|
|
6794
6927
|
let message = err.message;
|
|
6795
6928
|
let expected;
|
|
6796
6929
|
let received;
|
|
6797
6930
|
if ("expected" in err) expected = String(err.expected);
|
|
6798
6931
|
if ("received" in err) received = String(err.received);
|
|
6799
|
-
if (
|
|
6932
|
+
if (err.code === "invalid_union") {
|
|
6800
6933
|
const fieldMatch = path.match(/fields\.(\d+)/);
|
|
6801
6934
|
if (fieldMatch && config && typeof config === "object" && "fields" in config) {
|
|
6802
6935
|
const fields = config.fields;
|
|
@@ -6927,123 +7060,31 @@ var FORBIDDEN_HIDDEN_PROPS = [
|
|
|
6927
7060
|
"pattern",
|
|
6928
7061
|
"placeholder"
|
|
6929
7062
|
];
|
|
6930
|
-
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"];
|
|
6931
7067
|
var FIELDS_REQUIRING_OPTIONS = ["select", "radio", "multi-checkbox"];
|
|
6932
7068
|
var NESTING_RULES = {
|
|
6933
7069
|
page: {
|
|
6934
|
-
allowed: [
|
|
6935
|
-
"row",
|
|
6936
|
-
"group",
|
|
6937
|
-
"array",
|
|
6938
|
-
"input",
|
|
6939
|
-
"textarea",
|
|
6940
|
-
"select",
|
|
6941
|
-
"checkbox",
|
|
6942
|
-
"multi-checkbox",
|
|
6943
|
-
"radio",
|
|
6944
|
-
"datepicker",
|
|
6945
|
-
"toggle",
|
|
6946
|
-
"slider",
|
|
6947
|
-
"hidden",
|
|
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
7070
|
forbidden: ["page"],
|
|
6961
7071
|
message: "Pages cannot be nested inside other containers. ALL top-level fields must be pages if using multi-page mode."
|
|
6962
7072
|
},
|
|
6963
7073
|
row: {
|
|
6964
|
-
allowed: [
|
|
6965
|
-
"group",
|
|
6966
|
-
"array",
|
|
6967
|
-
"input",
|
|
6968
|
-
"textarea",
|
|
6969
|
-
"select",
|
|
6970
|
-
"checkbox",
|
|
6971
|
-
"multi-checkbox",
|
|
6972
|
-
"radio",
|
|
6973
|
-
"datepicker",
|
|
6974
|
-
"toggle",
|
|
6975
|
-
"slider",
|
|
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
7074
|
forbidden: ["page", "row", "hidden"],
|
|
6989
7075
|
message: "Rows cannot contain pages, other rows, or hidden fields. Hidden fields should be at page or form level."
|
|
6990
7076
|
},
|
|
6991
7077
|
group: {
|
|
6992
|
-
allowed: [
|
|
6993
|
-
"row",
|
|
6994
|
-
"input",
|
|
6995
|
-
"textarea",
|
|
6996
|
-
"select",
|
|
6997
|
-
"checkbox",
|
|
6998
|
-
"multi-checkbox",
|
|
6999
|
-
"radio",
|
|
7000
|
-
"datepicker",
|
|
7001
|
-
"toggle",
|
|
7002
|
-
"slider",
|
|
7003
|
-
"hidden",
|
|
7004
|
-
"text",
|
|
7005
|
-
"button",
|
|
7006
|
-
"submit",
|
|
7007
|
-
"next",
|
|
7008
|
-
"previous",
|
|
7009
|
-
"add-array-item",
|
|
7010
|
-
"prepend-array-item",
|
|
7011
|
-
"insert-array-item",
|
|
7012
|
-
"remove-array-item",
|
|
7013
|
-
"pop-array-item",
|
|
7014
|
-
"shift-array-item"
|
|
7015
|
-
],
|
|
7016
7078
|
forbidden: ["page", "group"],
|
|
7017
7079
|
message: "Groups cannot contain pages or other groups (no nested groups)."
|
|
7018
7080
|
},
|
|
7019
7081
|
array: {
|
|
7020
|
-
allowed: [
|
|
7021
|
-
"row",
|
|
7022
|
-
"group",
|
|
7023
|
-
"input",
|
|
7024
|
-
"textarea",
|
|
7025
|
-
"select",
|
|
7026
|
-
"checkbox",
|
|
7027
|
-
"multi-checkbox",
|
|
7028
|
-
"radio",
|
|
7029
|
-
"datepicker",
|
|
7030
|
-
"toggle",
|
|
7031
|
-
"slider",
|
|
7032
|
-
"hidden",
|
|
7033
|
-
"text",
|
|
7034
|
-
"button",
|
|
7035
|
-
"submit",
|
|
7036
|
-
"next",
|
|
7037
|
-
"previous",
|
|
7038
|
-
"add-array-item",
|
|
7039
|
-
"prepend-array-item",
|
|
7040
|
-
"insert-array-item",
|
|
7041
|
-
"remove-array-item",
|
|
7042
|
-
"pop-array-item",
|
|
7043
|
-
"shift-array-item"
|
|
7044
|
-
],
|
|
7045
7082
|
forbidden: ["page", "array"],
|
|
7046
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."
|
|
7047
7088
|
}
|
|
7048
7089
|
};
|
|
7049
7090
|
var EXPECTED_STRUCTURE = {
|
|
@@ -7055,7 +7096,8 @@ var EXPECTED_STRUCTURE = {
|
|
|
7055
7096
|
row: `{ key: 'rowKey', type: 'row', fields: [...childFields] }`,
|
|
7056
7097
|
group: `{ key: 'groupKey', type: 'group', fields: [...childFields] }`,
|
|
7057
7098
|
array: `Full API: { key: 'arrayKey', type: 'array', fields: [...itemDefs] } OR Simplified API: { key: 'arrayKey', type: 'array', template: { type: 'input', label: 'Item' }, value: [] }`,
|
|
7058
|
-
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] }`
|
|
7059
7101
|
};
|
|
7060
7102
|
function preValidateConfig(config) {
|
|
7061
7103
|
const errors = [];
|
|
@@ -7100,7 +7142,7 @@ function preValidateConfig(config) {
|
|
|
7100
7142
|
}
|
|
7101
7143
|
});
|
|
7102
7144
|
}
|
|
7103
|
-
if (["row", "group", "array"].includes(fieldType || "")) {
|
|
7145
|
+
if (["row", "group", "array", "container"].includes(fieldType || "")) {
|
|
7104
7146
|
for (const prop of INVALID_CONTAINER_PROPS) {
|
|
7105
7147
|
if (prop in f) {
|
|
7106
7148
|
errors.push({
|
|
@@ -7117,10 +7159,30 @@ function preValidateConfig(config) {
|
|
|
7117
7159
|
const expected = EXPECTED_STRUCTURE[fieldType || ""] || "";
|
|
7118
7160
|
errors.push({
|
|
7119
7161
|
path: `${path}.${prop}`,
|
|
7120
|
-
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}` : ""}`
|
|
7121
7163
|
});
|
|
7122
7164
|
}
|
|
7123
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
|
+
}
|
|
7124
7186
|
if ("logic" in f && Array.isArray(f["logic"])) {
|
|
7125
7187
|
const nonHiddenLogic = f["logic"].filter(
|
|
7126
7188
|
(l) => l && typeof l === "object" && l["type"] !== "hidden"
|
|
@@ -7151,6 +7213,14 @@ function preValidateConfig(config) {
|
|
|
7151
7213
|
});
|
|
7152
7214
|
}
|
|
7153
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
|
+
}
|
|
7154
7224
|
}
|
|
7155
7225
|
if (parentType && NESTING_RULES[parentType]) {
|
|
7156
7226
|
const rules = NESTING_RULES[parentType];
|
|
@@ -7231,7 +7301,7 @@ function preValidateConfig(config) {
|
|
|
7231
7301
|
});
|
|
7232
7302
|
}
|
|
7233
7303
|
}
|
|
7234
|
-
if (["row", "group", "page"].includes(fieldType || "")) {
|
|
7304
|
+
if (["row", "group", "page", "container"].includes(fieldType || "")) {
|
|
7235
7305
|
if (!("fields" in f)) {
|
|
7236
7306
|
errors.push({
|
|
7237
7307
|
path: `${path}.fields`,
|
|
@@ -7244,6 +7314,19 @@ function preValidateConfig(config) {
|
|
|
7244
7314
|
});
|
|
7245
7315
|
}
|
|
7246
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
|
+
}
|
|
7247
7330
|
if (fieldType === "array") {
|
|
7248
7331
|
const hasFields = "fields" in f;
|
|
7249
7332
|
const hasTemplate = "template" in f;
|
|
@@ -8078,7 +8161,7 @@ function registerResources(server) {
|
|
|
8078
8161
|
}
|
|
8079
8162
|
|
|
8080
8163
|
// packages/dynamic-form-mcp/src/tools/lookup.tool.ts
|
|
8081
|
-
import { z as
|
|
8164
|
+
import { z as z57 } from "zod";
|
|
8082
8165
|
|
|
8083
8166
|
// packages/dynamic-form-mcp/src/tools/data/lookup-topics.ts
|
|
8084
8167
|
var TOPICS = {
|
|
@@ -9813,25 +9896,42 @@ provideDynamicForm({
|
|
|
9813
9896
|
| FieldContext | \`ctx.value()\` | \`ctx.valueOf('path' as never)\` | custom validators |`
|
|
9814
9897
|
},
|
|
9815
9898
|
containers: {
|
|
9816
|
-
brief: `| Container | Label? | Logic? | Notes |
|
|
9817
|
-
|
|
9818
|
-
| page | NO | hidden only | Nav buttons INSIDE |
|
|
9819
|
-
| group | NO | hidden only | Creates nested object |
|
|
9820
|
-
| array | NO | hidden only | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9821
|
-
| 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 |`,
|
|
9822
9906
|
full: `# Container Rules
|
|
9823
9907
|
|
|
9824
|
-
| Container | Label? | Logic? | Allowed Children | Notes |
|
|
9825
|
-
|
|
9826
|
-
| page | NO | YES (hidden only) | rows, groups, arrays, leaf fields, buttons | Nav buttons go INSIDE |
|
|
9827
|
-
| group | NO | YES (hidden only) | rows, arrays, leaf fields (NOT pages, groups) | Creates nested object |
|
|
9828
|
-
| array | NO | YES (hidden only) | rows, groups, leaf fields (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9829
|
-
| 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.
|
|
9830
9929
|
|
|
9831
9930
|
**CRITICAL:**
|
|
9832
|
-
- 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.
|
|
9833
9932
|
- For other logic types (disabled, required, readonly, derivation), apply them to child fields instead.
|
|
9834
|
-
- 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.`
|
|
9835
9935
|
},
|
|
9836
9936
|
"field-placement": {
|
|
9837
9937
|
brief: `**Multi-page:** ALL root fields must be pages. Hidden fields go INSIDE first page.
|
|
@@ -10625,7 +10725,8 @@ var TOPIC_DESCRIPTIONS = {
|
|
|
10625
10725
|
"field-placement": "Which fields can go where \u2014 nesting and placement rules",
|
|
10626
10726
|
"logic-matrix": "Which logic types are supported on which field types",
|
|
10627
10727
|
"context-api": "FieldContext API \u2014 valueOf(), formValue, parent, arrayIndex",
|
|
10628
|
-
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",
|
|
10629
10730
|
"array-buttons": "Add/remove array item button configuration",
|
|
10630
10731
|
"custom-validators": "Custom validator functions via validators array",
|
|
10631
10732
|
conditions: "Condition operators and types for logic rules",
|
|
@@ -10885,7 +10986,7 @@ function formatTopicItem(key) {
|
|
|
10885
10986
|
}
|
|
10886
10987
|
function getTopicList() {
|
|
10887
10988
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
10888
|
-
const containers = ["
|
|
10989
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
10889
10990
|
const wrappers = ["wrappers", ...getWrappers().map((w) => w.type)];
|
|
10890
10991
|
const addons = ["addons", ...getAddonTypes().map((k) => k.type)];
|
|
10891
10992
|
const concepts = [
|
|
@@ -10941,7 +11042,7 @@ ${patterns.map(formatTopicItem).join("\n")}
|
|
|
10941
11042
|
}
|
|
10942
11043
|
function formatFieldInfoFull(field, uiFieldType) {
|
|
10943
11044
|
const lines = [];
|
|
10944
|
-
const isContainer =
|
|
11045
|
+
const isContainer = field.category === "container";
|
|
10945
11046
|
const isHidden = field.type === "hidden";
|
|
10946
11047
|
lines.push(`## ${field.type} field`);
|
|
10947
11048
|
lines.push("");
|
|
@@ -11370,11 +11471,11 @@ Patterns: field-placement, logic-matrix, context-api, containers, multi-page-got
|
|
|
11370
11471
|
Use topic="list" to see all available topics with descriptions.
|
|
11371
11472
|
Use ngforge_search query="your question" to find topics by keyword.`,
|
|
11372
11473
|
{
|
|
11373
|
-
topic:
|
|
11374
|
-
'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'
|
|
11375
11476
|
),
|
|
11376
|
-
depth:
|
|
11377
|
-
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")
|
|
11378
11479
|
},
|
|
11379
11480
|
async ({ topic, depth, uiIntegration }) => {
|
|
11380
11481
|
const normalizedTopic = topic.toLowerCase().trim();
|
|
@@ -11422,7 +11523,7 @@ ${nextSteps}`;
|
|
|
11422
11523
|
}
|
|
11423
11524
|
|
|
11424
11525
|
// packages/dynamic-form-mcp/src/tools/examples.tool.ts
|
|
11425
|
-
import { z as
|
|
11526
|
+
import { z as z58 } from "zod";
|
|
11426
11527
|
var PATTERNS = {
|
|
11427
11528
|
// ========== MINIMAL PATTERNS (NEW) ==========
|
|
11428
11529
|
"minimal-multipage": {
|
|
@@ -13687,10 +13788,10 @@ Standard patterns: derivation, property-derivation, multi-page, conditional, val
|
|
|
13687
13788
|
Use pattern="list" to see all available patterns.
|
|
13688
13789
|
Use depth="minimal" for code-only output (no markdown).`,
|
|
13689
13790
|
{
|
|
13690
|
-
pattern:
|
|
13791
|
+
pattern: z58.string().optional().describe(
|
|
13691
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.'
|
|
13692
13793
|
),
|
|
13693
|
-
depth:
|
|
13794
|
+
depth: z58.enum(["minimal", "brief", "full", "explained"]).default("full").describe(
|
|
13694
13795
|
"minimal=code only (~20-50 lines), brief=code+summary, full=code+comments (default), explained=code+detailed explanation"
|
|
13695
13796
|
)
|
|
13696
13797
|
},
|
|
@@ -13755,7 +13856,7 @@ function extractCode(markdown) {
|
|
|
13755
13856
|
}
|
|
13756
13857
|
|
|
13757
13858
|
// packages/dynamic-form-mcp/src/tools/validate.tool.ts
|
|
13758
|
-
import { z as
|
|
13859
|
+
import { z as z59 } from "zod";
|
|
13759
13860
|
var UI_INTEGRATIONS3 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13760
13861
|
var ERROR_TOPIC_HINTS = [
|
|
13761
13862
|
{ pattern: /options/i, hint: '`ngforge_lookup topic="options-format"` \u2014 correct options syntax' },
|
|
@@ -13808,10 +13909,10 @@ Example errors you'll see:
|
|
|
13808
13909
|
- "options MUST be at FIELD level, NOT inside props"
|
|
13809
13910
|
- "containers only support 'hidden' logic type"`,
|
|
13810
13911
|
{
|
|
13811
|
-
config:
|
|
13912
|
+
config: z59.union([z59.string(), z59.object({}).passthrough()]).describe(
|
|
13812
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."
|
|
13813
13914
|
),
|
|
13814
|
-
uiIntegration:
|
|
13915
|
+
uiIntegration: z59.enum(UI_INTEGRATIONS3).default("material").describe("UI library to validate against (material, bootstrap, primeng, ionic). Defaults to material.")
|
|
13815
13916
|
},
|
|
13816
13917
|
async ({ config, uiIntegration }) => {
|
|
13817
13918
|
try {
|
|
@@ -13903,7 +14004,7 @@ If validating JSON, ensure:
|
|
|
13903
14004
|
}
|
|
13904
14005
|
|
|
13905
14006
|
// packages/dynamic-form-mcp/src/tools/scaffold.tool.ts
|
|
13906
|
-
import { z as
|
|
14007
|
+
import { z as z60 } from "zod";
|
|
13907
14008
|
var UI_INTEGRATIONS4 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13908
14009
|
function parseFieldDefs(fields) {
|
|
13909
14010
|
return fields.map((f) => {
|
|
@@ -14158,12 +14259,12 @@ Examples:
|
|
|
14158
14259
|
- With groups: pages=0 groups=["address","billing"]
|
|
14159
14260
|
- With hidden: hidden=["userId:abc123","source:web"]`,
|
|
14160
14261
|
{
|
|
14161
|
-
pages:
|
|
14162
|
-
arrays:
|
|
14163
|
-
groups:
|
|
14164
|
-
hidden:
|
|
14165
|
-
fields:
|
|
14166
|
-
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.")
|
|
14167
14268
|
},
|
|
14168
14269
|
async ({ pages, arrays = [], groups = [], hidden = [], fields = [] }) => {
|
|
14169
14270
|
const fieldDefs = parseFieldDefs(fields);
|
|
@@ -14205,11 +14306,11 @@ Examples:
|
|
|
14205
14306
|
}
|
|
14206
14307
|
|
|
14207
14308
|
// packages/dynamic-form-mcp/src/tools/search.tool.ts
|
|
14208
|
-
import { z as
|
|
14309
|
+
import { z as z61 } from "zod";
|
|
14209
14310
|
var searchIndex = null;
|
|
14210
14311
|
function getTopicSubcategory(key) {
|
|
14211
14312
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
14212
|
-
const containers = ["
|
|
14313
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
14213
14314
|
const concepts = [
|
|
14214
14315
|
"validation",
|
|
14215
14316
|
"validation-messages",
|
|
@@ -14249,6 +14350,19 @@ function buildIndex() {
|
|
|
14249
14350
|
toolCall: `ngforge_lookup topic="${key}"`
|
|
14250
14351
|
});
|
|
14251
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
|
+
}
|
|
14252
14366
|
for (const [key, pattern] of Object.entries(PATTERNS)) {
|
|
14253
14367
|
entries.push({
|
|
14254
14368
|
id: key,
|
|
@@ -14353,7 +14467,7 @@ Use this when you don't know the exact topic name.
|
|
|
14353
14467
|
|
|
14354
14468
|
New to ng-forge? Start with: ngforge_lookup topic="workflow"`,
|
|
14355
14469
|
{
|
|
14356
|
-
query:
|
|
14470
|
+
query: z61.string().describe("Natural language or keywords to search for")
|
|
14357
14471
|
},
|
|
14358
14472
|
async ({ query }) => {
|
|
14359
14473
|
const index = buildIndex();
|