@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/src/index.js
CHANGED
|
@@ -22,7 +22,7 @@ var FIELD_TYPES = [
|
|
|
22
22
|
},
|
|
23
23
|
validationSupported: true,
|
|
24
24
|
source: "adapter",
|
|
25
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
25
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
26
26
|
example: `{
|
|
27
27
|
key: 'email',
|
|
28
28
|
type: 'input',
|
|
@@ -57,7 +57,7 @@ var FIELD_TYPES = [
|
|
|
57
57
|
},
|
|
58
58
|
validationSupported: true,
|
|
59
59
|
source: "adapter",
|
|
60
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
60
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
61
61
|
example: `{
|
|
62
62
|
key: 'description',
|
|
63
63
|
type: 'textarea',
|
|
@@ -91,7 +91,7 @@ var FIELD_TYPES = [
|
|
|
91
91
|
},
|
|
92
92
|
validationSupported: true,
|
|
93
93
|
source: "adapter",
|
|
94
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
94
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
95
95
|
example: `{
|
|
96
96
|
key: 'country',
|
|
97
97
|
type: 'select',
|
|
@@ -113,7 +113,7 @@ var FIELD_TYPES = [
|
|
|
113
113
|
props: {},
|
|
114
114
|
validationSupported: true,
|
|
115
115
|
source: "adapter",
|
|
116
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
116
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
117
117
|
example: `{
|
|
118
118
|
key: 'acceptTerms',
|
|
119
119
|
type: 'checkbox',
|
|
@@ -138,7 +138,7 @@ var FIELD_TYPES = [
|
|
|
138
138
|
},
|
|
139
139
|
validationSupported: true,
|
|
140
140
|
source: "adapter",
|
|
141
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
141
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
142
142
|
example: `{
|
|
143
143
|
key: 'interests',
|
|
144
144
|
type: 'multi-checkbox',
|
|
@@ -168,7 +168,7 @@ var FIELD_TYPES = [
|
|
|
168
168
|
},
|
|
169
169
|
validationSupported: true,
|
|
170
170
|
source: "adapter",
|
|
171
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
171
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
172
172
|
example: `{
|
|
173
173
|
key: 'gender',
|
|
174
174
|
type: 'radio',
|
|
@@ -203,7 +203,7 @@ var FIELD_TYPES = [
|
|
|
203
203
|
},
|
|
204
204
|
validationSupported: true,
|
|
205
205
|
source: "adapter",
|
|
206
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
206
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
207
207
|
example: `{
|
|
208
208
|
key: 'birthDate',
|
|
209
209
|
type: 'datepicker',
|
|
@@ -222,7 +222,7 @@ var FIELD_TYPES = [
|
|
|
222
222
|
props: {},
|
|
223
223
|
validationSupported: true,
|
|
224
224
|
source: "adapter",
|
|
225
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
225
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
226
226
|
example: `{
|
|
227
227
|
key: 'notifications',
|
|
228
228
|
type: 'toggle',
|
|
@@ -261,7 +261,7 @@ var FIELD_TYPES = [
|
|
|
261
261
|
},
|
|
262
262
|
validationSupported: true,
|
|
263
263
|
source: "adapter",
|
|
264
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
264
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
265
265
|
example: `{
|
|
266
266
|
key: 'volume',
|
|
267
267
|
type: 'slider',
|
|
@@ -282,7 +282,7 @@ var FIELD_TYPES = [
|
|
|
282
282
|
props: {},
|
|
283
283
|
validationSupported: false,
|
|
284
284
|
source: "core",
|
|
285
|
-
allowedIn: ["top-level (single-page forms only)", "page.fields", "group.fields", "array.fields"],
|
|
285
|
+
allowedIn: ["top-level (single-page forms only)", "page.fields", "group.fields", "array.fields", "container"],
|
|
286
286
|
notAllowedIn: ["row", "top-level when using pages"],
|
|
287
287
|
example: `// Hidden field - value is REQUIRED!
|
|
288
288
|
{
|
|
@@ -319,7 +319,7 @@ var FIELD_TYPES = [
|
|
|
319
319
|
},
|
|
320
320
|
validationSupported: false,
|
|
321
321
|
source: "core",
|
|
322
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
322
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
323
323
|
example: `{
|
|
324
324
|
key: 'sectionTitle',
|
|
325
325
|
type: 'text',
|
|
@@ -422,11 +422,12 @@ var FIELD_TYPES = [
|
|
|
422
422
|
},
|
|
423
423
|
validationSupported: true,
|
|
424
424
|
source: "core",
|
|
425
|
-
allowedIn: ["top-level", "page", "row", "array"],
|
|
425
|
+
allowedIn: ["top-level", "page", "row", "array", "container"],
|
|
426
426
|
notAllowedIn: ["group"],
|
|
427
427
|
canContain: [
|
|
428
428
|
"row",
|
|
429
429
|
"array",
|
|
430
|
+
"container",
|
|
430
431
|
"input",
|
|
431
432
|
"textarea",
|
|
432
433
|
"select",
|
|
@@ -520,11 +521,12 @@ var FIELD_TYPES = [
|
|
|
520
521
|
},
|
|
521
522
|
validationSupported: true,
|
|
522
523
|
source: "core",
|
|
523
|
-
allowedIn: ["top-level", "page", "row", "group"],
|
|
524
|
+
allowedIn: ["top-level", "page", "row", "group", "container"],
|
|
524
525
|
notAllowedIn: ["array"],
|
|
525
526
|
canContain: [
|
|
526
527
|
"row",
|
|
527
528
|
"group",
|
|
529
|
+
"container",
|
|
528
530
|
"input",
|
|
529
531
|
"textarea",
|
|
530
532
|
"select",
|
|
@@ -586,6 +588,74 @@ var FIELD_TYPES = [
|
|
|
586
588
|
// ? { kind: 'periodOrder' } : null`,
|
|
587
589
|
minimalExample: `{ key: 'items', type: 'array', fields: [...] }`
|
|
588
590
|
},
|
|
591
|
+
{
|
|
592
|
+
type: "container",
|
|
593
|
+
category: "container",
|
|
594
|
+
description: "Wraps its children in UI chrome via a chain of wrapper components. Containers do NOT have a label property and do NOT create a nesting level in form values \u2014 child values are flattened into the parent, exactly like rows. The 'wrappers' property is REQUIRED (use [] for no chrome). A container is NOT a group with chrome and the two are not substitutable: a container flattens child values into the parent (like a row), while a group nests them under its own key and owns a schema path, so swapping one for the other changes the submitted value shape and which validators can run. Wrappers are applied outermost-first, so the first entry in the array is the outermost element. Supports only 'hidden' logic type for conditional visibility. Like `row` and `page`, a container flattens into its parent and has no schema path of its own, so it does NOT support container-level `validators`, `required` or `validationMessages` \u2014 use `group` or `array` for cross-field rules.",
|
|
595
|
+
valueType: void 0,
|
|
596
|
+
baseInterface: "FieldDef",
|
|
597
|
+
props: {
|
|
598
|
+
fields: {
|
|
599
|
+
name: "fields",
|
|
600
|
+
type: "ContainerAllowedChildren[]",
|
|
601
|
+
description: "Child fields to render inside the wrapper chain. Allowed: rows, groups, arrays, nested containers, hidden fields, and value fields (input, select, etc). NOT ALLOWED: pages",
|
|
602
|
+
required: true
|
|
603
|
+
},
|
|
604
|
+
wrappers: {
|
|
605
|
+
name: "wrappers",
|
|
606
|
+
type: "WrapperConfig[]",
|
|
607
|
+
description: "REQUIRED. Wrapper components chained around the children, outermost first. Use [] for no chrome. Each entry needs a 'type'; the built-in wrapper is 'css', which takes 'cssClasses'. Custom wrapper types can be registered.",
|
|
608
|
+
required: true
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
validationSupported: false,
|
|
612
|
+
source: "core",
|
|
613
|
+
allowedIn: ["top-level", "page", "group", "array", "row", "container"],
|
|
614
|
+
notAllowedIn: [],
|
|
615
|
+
canContain: [
|
|
616
|
+
"row",
|
|
617
|
+
"group",
|
|
618
|
+
"array",
|
|
619
|
+
"container",
|
|
620
|
+
"hidden",
|
|
621
|
+
"input",
|
|
622
|
+
"textarea",
|
|
623
|
+
"select",
|
|
624
|
+
"checkbox",
|
|
625
|
+
"multi-checkbox",
|
|
626
|
+
"radio",
|
|
627
|
+
"datepicker",
|
|
628
|
+
"toggle",
|
|
629
|
+
"slider",
|
|
630
|
+
"text",
|
|
631
|
+
"button",
|
|
632
|
+
"submit",
|
|
633
|
+
"next",
|
|
634
|
+
"previous",
|
|
635
|
+
"add-array-item",
|
|
636
|
+
"prepend-array-item",
|
|
637
|
+
"insert-array-item",
|
|
638
|
+
"remove-array-item",
|
|
639
|
+
"pop-array-item",
|
|
640
|
+
"shift-array-item"
|
|
641
|
+
],
|
|
642
|
+
cannotContain: ["page"],
|
|
643
|
+
example: `{
|
|
644
|
+
key: 'billingChrome',
|
|
645
|
+
type: 'container',
|
|
646
|
+
// NOTE: Containers do NOT have a label property
|
|
647
|
+
// wrappers is REQUIRED \u2014 use [] when no chrome is needed
|
|
648
|
+
wrappers: [{ type: 'css', cssClasses: 'card p-3' }],
|
|
649
|
+
fields: [
|
|
650
|
+
{ key: 'street', type: 'input', label: 'Street' },
|
|
651
|
+
{ key: 'city', type: 'input', label: 'City' }
|
|
652
|
+
]
|
|
653
|
+
}
|
|
654
|
+
// Values flatten: { street: '', city: '' } \u2014 no 'billingChrome' key
|
|
655
|
+
// NOT ALLOWED in containers:
|
|
656
|
+
// - type: 'page' (pages are top-level only)`,
|
|
657
|
+
minimalExample: `{ key: 'chrome', type: 'container', wrappers: [], fields: [...] }`
|
|
658
|
+
},
|
|
589
659
|
{
|
|
590
660
|
type: "page",
|
|
591
661
|
category: "container",
|
|
@@ -603,11 +673,12 @@ var FIELD_TYPES = [
|
|
|
603
673
|
validationSupported: false,
|
|
604
674
|
source: "core",
|
|
605
675
|
allowedIn: ["top-level"],
|
|
606
|
-
notAllowedIn: ["page", "row", "group", "array"],
|
|
676
|
+
notAllowedIn: ["page", "row", "group", "array", "container"],
|
|
607
677
|
canContain: [
|
|
608
678
|
"row",
|
|
609
679
|
"group",
|
|
610
680
|
"array",
|
|
681
|
+
"container",
|
|
611
682
|
"input",
|
|
612
683
|
"textarea",
|
|
613
684
|
"select",
|
|
@@ -647,7 +718,7 @@ var FIELD_TYPES = [
|
|
|
647
718
|
props: {},
|
|
648
719
|
validationSupported: false,
|
|
649
720
|
source: "adapter",
|
|
650
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
721
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
651
722
|
example: `{
|
|
652
723
|
key: 'next',
|
|
653
724
|
type: 'next',
|
|
@@ -664,7 +735,7 @@ var FIELD_TYPES = [
|
|
|
664
735
|
props: {},
|
|
665
736
|
validationSupported: false,
|
|
666
737
|
source: "adapter",
|
|
667
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
738
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
668
739
|
example: `{
|
|
669
740
|
key: 'back',
|
|
670
741
|
type: 'previous',
|
|
@@ -688,7 +759,7 @@ var FIELD_TYPES = [
|
|
|
688
759
|
},
|
|
689
760
|
validationSupported: false,
|
|
690
761
|
source: "adapter",
|
|
691
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
762
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
692
763
|
example: `// RECOMMENDED: Use pre-defined button types for common actions
|
|
693
764
|
{ key: 'submit', type: 'submit', label: 'Submit Form' }
|
|
694
765
|
{ key: 'next', type: 'next', label: 'Next' }
|
|
@@ -715,7 +786,7 @@ var FIELD_TYPES = [
|
|
|
715
786
|
props: {},
|
|
716
787
|
validationSupported: false,
|
|
717
788
|
source: "adapter",
|
|
718
|
-
allowedIn: ["top-level", "page", "row", "group", "array"],
|
|
789
|
+
allowedIn: ["top-level", "page", "row", "group", "array", "container"],
|
|
719
790
|
example: `{
|
|
720
791
|
key: 'submit',
|
|
721
792
|
type: 'submit',
|
|
@@ -732,7 +803,7 @@ var FIELD_TYPES = [
|
|
|
732
803
|
props: {},
|
|
733
804
|
validationSupported: false,
|
|
734
805
|
source: "adapter",
|
|
735
|
-
allowedIn: ["array", "row", "group"],
|
|
806
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
736
807
|
example: `{
|
|
737
808
|
key: 'addContact',
|
|
738
809
|
type: 'add-array-item',
|
|
@@ -749,7 +820,7 @@ var FIELD_TYPES = [
|
|
|
749
820
|
props: {},
|
|
750
821
|
validationSupported: false,
|
|
751
822
|
source: "adapter",
|
|
752
|
-
allowedIn: ["array", "row", "group"],
|
|
823
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
753
824
|
example: `{
|
|
754
825
|
key: 'prependContact',
|
|
755
826
|
type: 'prepend-array-item',
|
|
@@ -766,7 +837,7 @@ var FIELD_TYPES = [
|
|
|
766
837
|
props: {},
|
|
767
838
|
validationSupported: false,
|
|
768
839
|
source: "adapter",
|
|
769
|
-
allowedIn: ["array", "row", "group"],
|
|
840
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
770
841
|
example: `{
|
|
771
842
|
key: 'insertContact',
|
|
772
843
|
type: 'insert-array-item',
|
|
@@ -784,7 +855,7 @@ var FIELD_TYPES = [
|
|
|
784
855
|
props: {},
|
|
785
856
|
validationSupported: false,
|
|
786
857
|
source: "adapter",
|
|
787
|
-
allowedIn: ["array", "row", "group"],
|
|
858
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
788
859
|
example: `{
|
|
789
860
|
key: 'removeContact',
|
|
790
861
|
type: 'remove-array-item',
|
|
@@ -801,7 +872,7 @@ var FIELD_TYPES = [
|
|
|
801
872
|
props: {},
|
|
802
873
|
validationSupported: false,
|
|
803
874
|
source: "adapter",
|
|
804
|
-
allowedIn: ["array", "row", "group"],
|
|
875
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
805
876
|
example: `{
|
|
806
877
|
key: 'popContact',
|
|
807
878
|
type: 'pop-array-item',
|
|
@@ -819,7 +890,7 @@ var FIELD_TYPES = [
|
|
|
819
890
|
props: {},
|
|
820
891
|
validationSupported: false,
|
|
821
892
|
source: "adapter",
|
|
822
|
-
allowedIn: ["array", "row", "group"],
|
|
893
|
+
allowedIn: ["array", "row", "group", "container"],
|
|
823
894
|
example: `{
|
|
824
895
|
key: 'shiftContact',
|
|
825
896
|
type: 'shift-array-item',
|
|
@@ -4856,15 +4927,23 @@ var ContainerLogicSchema = z13.object({
|
|
|
4856
4927
|
type: z13.literal("hidden"),
|
|
4857
4928
|
condition: z13.union([ConditionalExpressionSchema, z13.boolean()])
|
|
4858
4929
|
});
|
|
4930
|
+
var SIMPLIFIED_ONLY_PROPS = ["template", "value", "addButton", "removeButton"];
|
|
4931
|
+
var SIMPLIFIED_ONLY_HINTS = {
|
|
4932
|
+
template: 'Use "fields" for the full API, or "template" for the simplified one.',
|
|
4933
|
+
value: 'Set initial values on the field definitions inside "fields".',
|
|
4934
|
+
addButton: 'Add an "add-array-item" or "insert-array-item" button as a field.',
|
|
4935
|
+
removeButton: 'Add a "remove-array-item" button to the item definition.'
|
|
4936
|
+
};
|
|
4859
4937
|
function createContainerSchemas(options) {
|
|
4860
4938
|
const { leafFieldSchema } = options;
|
|
4861
4939
|
const AnyFieldSchema = z13.lazy(
|
|
4862
|
-
() => z13.
|
|
4940
|
+
() => z13.discriminatedUnion("type", [
|
|
4863
4941
|
leafFieldSchema,
|
|
4864
4942
|
PageFieldSchema,
|
|
4865
4943
|
RowFieldSchema,
|
|
4866
4944
|
GroupFieldSchema,
|
|
4867
|
-
ArrayFieldSchema
|
|
4945
|
+
ArrayFieldSchema,
|
|
4946
|
+
ContainerFieldSchema
|
|
4868
4947
|
])
|
|
4869
4948
|
);
|
|
4870
4949
|
const ContainerBaseSchema = BaseFieldDefSchema.omit({
|
|
@@ -4876,6 +4955,18 @@ function createContainerSchemas(options) {
|
|
|
4876
4955
|
label: z13.never().optional(),
|
|
4877
4956
|
meta: z13.never().optional()
|
|
4878
4957
|
});
|
|
4958
|
+
const ContainerValidationShape = {
|
|
4959
|
+
required: z13.boolean().optional(),
|
|
4960
|
+
validators: ValidatorsArraySchema.optional(),
|
|
4961
|
+
validationMessages: ValidationMessagesSchema.optional()
|
|
4962
|
+
};
|
|
4963
|
+
const WrapperConfigSchema = z13.object({ type: z13.string() }).passthrough();
|
|
4964
|
+
const ContainerFieldSchema = ContainerBaseSchema.extend({
|
|
4965
|
+
type: z13.literal("container"),
|
|
4966
|
+
fields: z13.array(AnyFieldSchema),
|
|
4967
|
+
wrappers: z13.array(WrapperConfigSchema),
|
|
4968
|
+
logic: z13.array(ContainerLogicSchema).optional()
|
|
4969
|
+
});
|
|
4879
4970
|
const PageFieldSchema = ContainerBaseSchema.extend({
|
|
4880
4971
|
type: z13.literal("page"),
|
|
4881
4972
|
fields: z13.array(AnyFieldSchema),
|
|
@@ -4892,33 +4983,27 @@ function createContainerSchemas(options) {
|
|
|
4892
4983
|
const GroupFieldSchema = ContainerBaseSchema.extend({
|
|
4893
4984
|
type: z13.literal("group"),
|
|
4894
4985
|
fields: z13.array(AnyFieldSchema),
|
|
4986
|
+
...ContainerValidationShape,
|
|
4895
4987
|
// Container logic - only 'hidden' type allowed (same as pages)
|
|
4896
4988
|
logic: z13.array(ContainerLogicSchema).optional()
|
|
4897
4989
|
});
|
|
4898
4990
|
const ArrayButtonConfigSchema = z13.object({
|
|
4899
4991
|
label: z13.string().optional(),
|
|
4900
|
-
props: z13.record(z13.unknown()).optional()
|
|
4901
|
-
});
|
|
4902
|
-
const FullArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4903
|
-
type: z13.literal("array"),
|
|
4904
|
-
fields: z13.array(AnyFieldSchema),
|
|
4905
|
-
logic: z13.array(ContainerLogicSchema).optional(),
|
|
4906
|
-
// Full API does not use template
|
|
4907
|
-
template: z13.never().optional(),
|
|
4908
|
-
// Array length validation
|
|
4909
|
-
minLength: z13.number().int().min(0).optional(),
|
|
4910
|
-
maxLength: z13.number().int().min(0).optional()
|
|
4992
|
+
props: z13.record(z13.string(), z13.unknown()).optional()
|
|
4911
4993
|
});
|
|
4912
4994
|
const ArrayAllowedChildSchema = z13.lazy(
|
|
4913
4995
|
() => AnyFieldSchema.refine((field) => field.type !== "page" && field.type !== "array", {
|
|
4914
4996
|
message: "Array templates cannot contain page or array fields. Only leaf fields, rows, and groups are allowed."
|
|
4915
4997
|
})
|
|
4916
4998
|
);
|
|
4917
|
-
const
|
|
4999
|
+
const ArrayFieldSchema = ContainerBaseSchema.extend({
|
|
4918
5000
|
type: z13.literal("array"),
|
|
4919
|
-
|
|
5001
|
+
...ContainerValidationShape,
|
|
5002
|
+
// Full API: explicit item definitions.
|
|
5003
|
+
fields: z13.array(AnyFieldSchema).optional(),
|
|
5004
|
+
// Simplified API: single field (primitive array) or array of fields (object array).
|
|
4920
5005
|
// Only ArrayAllowedChildren (leaf fields, rows, groups) are valid — no pages or nested arrays.
|
|
4921
|
-
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]),
|
|
5006
|
+
template: z13.union([ArrayAllowedChildSchema, z13.array(ArrayAllowedChildSchema)]).optional(),
|
|
4922
5007
|
// Initial values for the array
|
|
4923
5008
|
value: z13.array(z13.unknown()).optional(),
|
|
4924
5009
|
// Button customization or opt-out (false to disable)
|
|
@@ -4928,18 +5013,64 @@ function createContainerSchemas(options) {
|
|
|
4928
5013
|
// Array length validation
|
|
4929
5014
|
minLength: z13.number().int().min(0).optional(),
|
|
4930
5015
|
maxLength: z13.number().int().min(0).optional(),
|
|
4931
|
-
//
|
|
4932
|
-
|
|
5016
|
+
// The array size properties are minLength/maxLength. These are the common
|
|
5017
|
+
// wrong spelling, and naming them keeps the mistake from being silently
|
|
5018
|
+
// stripped. Previously only the simplified API rejected them.
|
|
4933
5019
|
minItems: z13.never().optional(),
|
|
4934
5020
|
maxItems: z13.never().optional()
|
|
5021
|
+
}).superRefine((field, ctx) => {
|
|
5022
|
+
const hasFields = field.fields !== void 0;
|
|
5023
|
+
const hasTemplate = field.template !== void 0;
|
|
5024
|
+
if (hasFields && hasTemplate) {
|
|
5025
|
+
ctx.addIssue({
|
|
5026
|
+
code: "custom",
|
|
5027
|
+
message: 'Array has BOTH "fields" and "template". These are mutually exclusive: use "fields" for the full API, or "template" + "value" for the simplified API.'
|
|
5028
|
+
});
|
|
5029
|
+
return;
|
|
5030
|
+
}
|
|
5031
|
+
if (!hasFields && !hasTemplate) {
|
|
5032
|
+
ctx.addIssue({
|
|
5033
|
+
code: "custom",
|
|
5034
|
+
message: 'Array is MISSING both "fields" and "template". Use "fields" (full API) or "template" + "value" (simplified API).'
|
|
5035
|
+
});
|
|
5036
|
+
return;
|
|
5037
|
+
}
|
|
5038
|
+
if (hasFields) {
|
|
5039
|
+
for (const prop of SIMPLIFIED_ONLY_PROPS) {
|
|
5040
|
+
if (prop === "template" || field[prop] === void 0) continue;
|
|
5041
|
+
ctx.addIssue({
|
|
5042
|
+
code: "custom",
|
|
5043
|
+
path: [prop],
|
|
5044
|
+
message: `Array uses the full API ("fields"), so "${prop}" is not allowed: it belongs to the simplified API. ${SIMPLIFIED_ONLY_HINTS[prop]}`
|
|
5045
|
+
});
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
}).meta({
|
|
5049
|
+
// The refinement above is invisible to JSON Schema generation, and the
|
|
5050
|
+
// generated schema is authoring guidance for a model. Two optional
|
|
5051
|
+
// properties with no stated relationship reads as "pass either, both or
|
|
5052
|
+
// neither", which is exactly the mistake the refinement rejects at
|
|
5053
|
+
// runtime. Restating the rule as `oneOf` keeps the published schema
|
|
5054
|
+
// saying what the validator enforces — the union of two array schemas
|
|
5055
|
+
// used to express this, and collapsing them to one schema lost it.
|
|
5056
|
+
description: 'Array field. Use EXACTLY ONE of "fields" (full API: explicit item definitions) or "template" (simplified API: one field, or an array of fields, repeated per item). Never both, never neither. "value", "addButton" and "removeButton" belong to the simplified API only: with "fields", put initial values on the item definitions and add buttons as fields.',
|
|
5057
|
+
oneOf: [
|
|
5058
|
+
{
|
|
5059
|
+
required: ["fields"],
|
|
5060
|
+
// `not` wraps an `anyOf`, not one multi-name `required`: the latter
|
|
5061
|
+
// would only forbid all of them being present at once.
|
|
5062
|
+
not: { anyOf: SIMPLIFIED_ONLY_PROPS.map((prop) => ({ required: [prop] })) }
|
|
5063
|
+
},
|
|
5064
|
+
{ required: ["template"], not: { required: ["fields"] } }
|
|
5065
|
+
]
|
|
4935
5066
|
});
|
|
4936
|
-
const ArrayFieldSchema = z13.union([FullArrayFieldSchema, SimplifiedArrayFieldSchema]);
|
|
4937
5067
|
const AllFieldsSchema = AnyFieldSchema;
|
|
4938
5068
|
return {
|
|
4939
5069
|
PageFieldSchema,
|
|
4940
5070
|
RowFieldSchema,
|
|
4941
5071
|
GroupFieldSchema,
|
|
4942
5072
|
ArrayFieldSchema,
|
|
5073
|
+
ContainerFieldSchema,
|
|
4943
5074
|
AllFieldsSchema
|
|
4944
5075
|
};
|
|
4945
5076
|
}
|
|
@@ -5149,7 +5280,7 @@ var BaseFormConfigSchema = z18.object({
|
|
|
5149
5280
|
});
|
|
5150
5281
|
|
|
5151
5282
|
// internal/dynamic-forms-validation/validate/src/json-schema/form-config-json-schema.ts
|
|
5152
|
-
import
|
|
5283
|
+
import { z as z55 } from "zod";
|
|
5153
5284
|
|
|
5154
5285
|
// internal/dynamic-forms-validation/material/src/props/mat-common-props.schema.ts
|
|
5155
5286
|
import { z as z19 } from "zod";
|
|
@@ -6647,24 +6778,20 @@ var formConfigSchemas = {
|
|
|
6647
6778
|
primeng: PrimeFormConfigSchema,
|
|
6648
6779
|
ionic: IonicFormConfigSchema
|
|
6649
6780
|
};
|
|
6781
|
+
function toJsonSchema(schema, name, basePath) {
|
|
6782
|
+
const jsonSchema = z55.toJSONSchema(schema, { target: "draft-7", io: "input", unrepresentable: "any" });
|
|
6783
|
+
jsonSchema["$id"] = basePath ? `${basePath.replace(/\/$/, "")}/${name}` : name;
|
|
6784
|
+
return jsonSchema;
|
|
6785
|
+
}
|
|
6650
6786
|
function getFormConfigJsonSchema(uiIntegration, options = {}) {
|
|
6651
6787
|
const schema = formConfigSchemas[uiIntegration];
|
|
6652
6788
|
if (!schema) {
|
|
6653
6789
|
throw new Error(`Unknown UI integration: ${uiIntegration}. Valid options: material, bootstrap, primeng, ionic`);
|
|
6654
6790
|
}
|
|
6655
|
-
|
|
6656
|
-
const jsonSchema = zodToJsonSchema(schema, {
|
|
6657
|
-
name: name ?? `${uiIntegration}FormConfig`,
|
|
6658
|
-
basePath: basePath ? basePath.split("/") : void 0,
|
|
6659
|
-
$refStrategy: "none",
|
|
6660
|
-
// Inline all definitions for MCP compatibility
|
|
6661
|
-
errorMessages: true
|
|
6662
|
-
});
|
|
6663
|
-
return jsonSchema;
|
|
6791
|
+
return toJsonSchema(schema, options.name ?? `${uiIntegration}FormConfig`, options.basePath);
|
|
6664
6792
|
}
|
|
6665
6793
|
|
|
6666
6794
|
// internal/dynamic-forms-validation/validate/src/json-schema/field-type-json-schemas.ts
|
|
6667
|
-
import zodToJsonSchema2 from "zod-to-json-schema";
|
|
6668
6795
|
var leafFieldSchemas = {
|
|
6669
6796
|
material: MatLeafFieldSchema,
|
|
6670
6797
|
bootstrap: BsLeafFieldSchema,
|
|
@@ -6759,53 +6886,41 @@ function getFieldTypeJsonSchema(uiIntegration, fieldType) {
|
|
|
6759
6886
|
if (!schema) {
|
|
6760
6887
|
throw new Error(`Unknown field type: ${fieldType}. Valid: ${getSupportedFieldTypes().join(", ")}`);
|
|
6761
6888
|
}
|
|
6762
|
-
|
|
6763
|
-
name: `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`,
|
|
6764
|
-
$refStrategy: "none",
|
|
6765
|
-
errorMessages: true
|
|
6766
|
-
});
|
|
6767
|
-
return jsonSchema;
|
|
6889
|
+
return toJsonSchema(schema, `${uiIntegration}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}Field`);
|
|
6768
6890
|
}
|
|
6769
6891
|
function getLeafFieldJsonSchema(uiIntegration) {
|
|
6770
6892
|
const schema = leafFieldSchemas[uiIntegration];
|
|
6771
6893
|
if (!schema) {
|
|
6772
6894
|
throw new Error(`Unknown UI integration: ${uiIntegration}`);
|
|
6773
6895
|
}
|
|
6774
|
-
|
|
6775
|
-
name: `${uiIntegration}LeafField`,
|
|
6776
|
-
$refStrategy: "none",
|
|
6777
|
-
errorMessages: true
|
|
6778
|
-
});
|
|
6779
|
-
return jsonSchema;
|
|
6896
|
+
return toJsonSchema(schema, `${uiIntegration}LeafField`);
|
|
6780
6897
|
}
|
|
6781
6898
|
|
|
6782
6899
|
// internal/dynamic-forms-validation/src/lib/schemas/field-type-names.ts
|
|
6783
|
-
import { z as
|
|
6900
|
+
import { z as z56 } from "zod";
|
|
6784
6901
|
var MAX_DEPTH = 20;
|
|
6785
6902
|
function collectDiscriminator(schema, into) {
|
|
6786
|
-
if (schema instanceof
|
|
6787
|
-
|
|
6903
|
+
if (schema instanceof z56.ZodLiteral) {
|
|
6904
|
+
for (const value of schema.def.values) {
|
|
6905
|
+
if (typeof value === "string") into.add(value);
|
|
6906
|
+
}
|
|
6788
6907
|
return;
|
|
6789
6908
|
}
|
|
6790
|
-
if (schema instanceof
|
|
6909
|
+
if (schema instanceof z56.ZodEnum) {
|
|
6791
6910
|
for (const value of schema.options) into.add(value);
|
|
6792
6911
|
}
|
|
6793
6912
|
}
|
|
6794
6913
|
function visit(schema, into, depth) {
|
|
6795
6914
|
if (depth > MAX_DEPTH) return;
|
|
6796
|
-
if (schema instanceof
|
|
6797
|
-
visit(schema.
|
|
6798
|
-
return;
|
|
6799
|
-
}
|
|
6800
|
-
if (schema instanceof z55.ZodEffects) {
|
|
6801
|
-
visit(schema.innerType(), into, depth + 1);
|
|
6915
|
+
if (schema instanceof z56.ZodLazy) {
|
|
6916
|
+
visit(schema.unwrap(), into, depth + 1);
|
|
6802
6917
|
return;
|
|
6803
6918
|
}
|
|
6804
|
-
if (schema instanceof
|
|
6919
|
+
if (schema instanceof z56.ZodUnion) {
|
|
6805
6920
|
for (const option of schema.options) visit(option, into, depth + 1);
|
|
6806
6921
|
return;
|
|
6807
6922
|
}
|
|
6808
|
-
if (schema instanceof
|
|
6923
|
+
if (schema instanceof z56.ZodObject) {
|
|
6809
6924
|
collectDiscriminator(schema.shape["type"], into);
|
|
6810
6925
|
return;
|
|
6811
6926
|
}
|
|
@@ -6878,14 +6993,14 @@ var DID_YOU_MEAN = {
|
|
|
6878
6993
|
validation: "Did you mean to use validator shorthand (`required`, `email`, `min`, etc.) or `validators: [...]` array?"
|
|
6879
6994
|
};
|
|
6880
6995
|
function formatZodError(error, uiIntegration, config) {
|
|
6881
|
-
const errors = error.
|
|
6996
|
+
const errors = error.issues.map((err) => {
|
|
6882
6997
|
const path = err.path.join(".") || "root";
|
|
6883
6998
|
let message = err.message;
|
|
6884
6999
|
let expected;
|
|
6885
7000
|
let received;
|
|
6886
7001
|
if ("expected" in err) expected = String(err.expected);
|
|
6887
7002
|
if ("received" in err) received = String(err.received);
|
|
6888
|
-
if (
|
|
7003
|
+
if (err.code === "invalid_union") {
|
|
6889
7004
|
const fieldMatch = path.match(/fields\.(\d+)/);
|
|
6890
7005
|
if (fieldMatch && config && typeof config === "object" && "fields" in config) {
|
|
6891
7006
|
const fields = config.fields;
|
|
@@ -7016,123 +7131,31 @@ var FORBIDDEN_HIDDEN_PROPS = [
|
|
|
7016
7131
|
"pattern",
|
|
7017
7132
|
"placeholder"
|
|
7018
7133
|
];
|
|
7019
|
-
var CONTAINER_FORBIDDEN_PROPS = ["
|
|
7134
|
+
var CONTAINER_FORBIDDEN_PROPS = ["email", "min", "max", "pattern", "value"];
|
|
7135
|
+
var VALIDATING_CONTAINERS = ["group", "array"];
|
|
7136
|
+
var CONTAINER_VALIDATION_PROPS = ["validators", "required", "validationMessages"];
|
|
7137
|
+
var ARRAY_SIZE_PROPS = ["minLength", "maxLength"];
|
|
7020
7138
|
var FIELDS_REQUIRING_OPTIONS = ["select", "radio", "multi-checkbox"];
|
|
7021
7139
|
var NESTING_RULES = {
|
|
7022
7140
|
page: {
|
|
7023
|
-
allowed: [
|
|
7024
|
-
"row",
|
|
7025
|
-
"group",
|
|
7026
|
-
"array",
|
|
7027
|
-
"input",
|
|
7028
|
-
"textarea",
|
|
7029
|
-
"select",
|
|
7030
|
-
"checkbox",
|
|
7031
|
-
"multi-checkbox",
|
|
7032
|
-
"radio",
|
|
7033
|
-
"datepicker",
|
|
7034
|
-
"toggle",
|
|
7035
|
-
"slider",
|
|
7036
|
-
"hidden",
|
|
7037
|
-
"text",
|
|
7038
|
-
"button",
|
|
7039
|
-
"submit",
|
|
7040
|
-
"next",
|
|
7041
|
-
"previous",
|
|
7042
|
-
"add-array-item",
|
|
7043
|
-
"prepend-array-item",
|
|
7044
|
-
"insert-array-item",
|
|
7045
|
-
"remove-array-item",
|
|
7046
|
-
"pop-array-item",
|
|
7047
|
-
"shift-array-item"
|
|
7048
|
-
],
|
|
7049
7141
|
forbidden: ["page"],
|
|
7050
7142
|
message: "Pages cannot be nested inside other containers. ALL top-level fields must be pages if using multi-page mode."
|
|
7051
7143
|
},
|
|
7052
7144
|
row: {
|
|
7053
|
-
allowed: [
|
|
7054
|
-
"group",
|
|
7055
|
-
"array",
|
|
7056
|
-
"input",
|
|
7057
|
-
"textarea",
|
|
7058
|
-
"select",
|
|
7059
|
-
"checkbox",
|
|
7060
|
-
"multi-checkbox",
|
|
7061
|
-
"radio",
|
|
7062
|
-
"datepicker",
|
|
7063
|
-
"toggle",
|
|
7064
|
-
"slider",
|
|
7065
|
-
"text",
|
|
7066
|
-
"button",
|
|
7067
|
-
"submit",
|
|
7068
|
-
"next",
|
|
7069
|
-
"previous",
|
|
7070
|
-
"add-array-item",
|
|
7071
|
-
"prepend-array-item",
|
|
7072
|
-
"insert-array-item",
|
|
7073
|
-
"remove-array-item",
|
|
7074
|
-
"pop-array-item",
|
|
7075
|
-
"shift-array-item"
|
|
7076
|
-
],
|
|
7077
7145
|
forbidden: ["page", "row", "hidden"],
|
|
7078
7146
|
message: "Rows cannot contain pages, other rows, or hidden fields. Hidden fields should be at page or form level."
|
|
7079
7147
|
},
|
|
7080
7148
|
group: {
|
|
7081
|
-
allowed: [
|
|
7082
|
-
"row",
|
|
7083
|
-
"input",
|
|
7084
|
-
"textarea",
|
|
7085
|
-
"select",
|
|
7086
|
-
"checkbox",
|
|
7087
|
-
"multi-checkbox",
|
|
7088
|
-
"radio",
|
|
7089
|
-
"datepicker",
|
|
7090
|
-
"toggle",
|
|
7091
|
-
"slider",
|
|
7092
|
-
"hidden",
|
|
7093
|
-
"text",
|
|
7094
|
-
"button",
|
|
7095
|
-
"submit",
|
|
7096
|
-
"next",
|
|
7097
|
-
"previous",
|
|
7098
|
-
"add-array-item",
|
|
7099
|
-
"prepend-array-item",
|
|
7100
|
-
"insert-array-item",
|
|
7101
|
-
"remove-array-item",
|
|
7102
|
-
"pop-array-item",
|
|
7103
|
-
"shift-array-item"
|
|
7104
|
-
],
|
|
7105
7149
|
forbidden: ["page", "group"],
|
|
7106
7150
|
message: "Groups cannot contain pages or other groups (no nested groups)."
|
|
7107
7151
|
},
|
|
7108
7152
|
array: {
|
|
7109
|
-
allowed: [
|
|
7110
|
-
"row",
|
|
7111
|
-
"group",
|
|
7112
|
-
"input",
|
|
7113
|
-
"textarea",
|
|
7114
|
-
"select",
|
|
7115
|
-
"checkbox",
|
|
7116
|
-
"multi-checkbox",
|
|
7117
|
-
"radio",
|
|
7118
|
-
"datepicker",
|
|
7119
|
-
"toggle",
|
|
7120
|
-
"slider",
|
|
7121
|
-
"hidden",
|
|
7122
|
-
"text",
|
|
7123
|
-
"button",
|
|
7124
|
-
"submit",
|
|
7125
|
-
"next",
|
|
7126
|
-
"previous",
|
|
7127
|
-
"add-array-item",
|
|
7128
|
-
"prepend-array-item",
|
|
7129
|
-
"insert-array-item",
|
|
7130
|
-
"remove-array-item",
|
|
7131
|
-
"pop-array-item",
|
|
7132
|
-
"shift-array-item"
|
|
7133
|
-
],
|
|
7134
7153
|
forbidden: ["page", "array"],
|
|
7135
7154
|
message: "Arrays cannot contain pages or other arrays (no nested arrays)."
|
|
7155
|
+
},
|
|
7156
|
+
container: {
|
|
7157
|
+
forbidden: ["page"],
|
|
7158
|
+
message: "Containers cannot contain pages. Pages are top-level fields only."
|
|
7136
7159
|
}
|
|
7137
7160
|
};
|
|
7138
7161
|
var EXPECTED_STRUCTURE = {
|
|
@@ -7144,7 +7167,8 @@ var EXPECTED_STRUCTURE = {
|
|
|
7144
7167
|
row: `{ key: 'rowKey', type: 'row', fields: [...childFields] }`,
|
|
7145
7168
|
group: `{ key: 'groupKey', type: 'group', fields: [...childFields] }`,
|
|
7146
7169
|
array: `Full API: { key: 'arrayKey', type: 'array', fields: [...itemDefs] } OR Simplified API: { key: 'arrayKey', type: 'array', template: { type: 'input', label: 'Item' }, value: [] }`,
|
|
7147
|
-
page: `{ key: 'pageKey', type: 'page', fields: [...childFields, { key: 'next', type: 'next', label: 'Next' }] }
|
|
7170
|
+
page: `{ key: 'pageKey', type: 'page', fields: [...childFields, { key: 'next', type: 'next', label: 'Next' }] }`,
|
|
7171
|
+
container: `{ key: 'containerKey', type: 'container', wrappers: [{ type: 'css', cssClasses: 'card' }], fields: [...childFields] }`
|
|
7148
7172
|
};
|
|
7149
7173
|
function preValidateConfig(config) {
|
|
7150
7174
|
const errors = [];
|
|
@@ -7189,7 +7213,7 @@ function preValidateConfig(config) {
|
|
|
7189
7213
|
}
|
|
7190
7214
|
});
|
|
7191
7215
|
}
|
|
7192
|
-
if (["row", "group", "array"].includes(fieldType || "")) {
|
|
7216
|
+
if (["row", "group", "array", "container"].includes(fieldType || "")) {
|
|
7193
7217
|
for (const prop of INVALID_CONTAINER_PROPS) {
|
|
7194
7218
|
if (prop in f) {
|
|
7195
7219
|
errors.push({
|
|
@@ -7206,10 +7230,30 @@ function preValidateConfig(config) {
|
|
|
7206
7230
|
const expected = EXPECTED_STRUCTURE[fieldType || ""] || "";
|
|
7207
7231
|
errors.push({
|
|
7208
7232
|
path: `${path}.${prop}`,
|
|
7209
|
-
message: `"${fieldType}" containers do NOT support "${prop}". Containers don't hold values
|
|
7233
|
+
message: `"${fieldType}" containers do NOT support "${prop}". Containers don't hold values - they are purely for layout/grouping.${expected ? ` Expected structure: ${expected}` : ""}`
|
|
7210
7234
|
});
|
|
7211
7235
|
}
|
|
7212
7236
|
}
|
|
7237
|
+
if (!VALIDATING_CONTAINERS.includes(fieldType || "")) {
|
|
7238
|
+
for (const prop of CONTAINER_VALIDATION_PROPS) {
|
|
7239
|
+
if (prop in f) {
|
|
7240
|
+
errors.push({
|
|
7241
|
+
path: `${path}.${prop}`,
|
|
7242
|
+
message: `"${fieldType}" containers do NOT support "${prop}". They flatten into their parent and have no schema path, so a container-level validator would have no value to run against. Use "group" or "array" for cross-field rules, or move the rule onto a child field.`
|
|
7243
|
+
});
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
if (fieldType !== "array") {
|
|
7248
|
+
for (const prop of ARRAY_SIZE_PROPS) {
|
|
7249
|
+
if (prop in f) {
|
|
7250
|
+
errors.push({
|
|
7251
|
+
path: `${path}.${prop}`,
|
|
7252
|
+
message: `"${fieldType}" containers do NOT support "${prop}". Array size constraints apply to "array" fields only.`
|
|
7253
|
+
});
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
}
|
|
7213
7257
|
if ("logic" in f && Array.isArray(f["logic"])) {
|
|
7214
7258
|
const nonHiddenLogic = f["logic"].filter(
|
|
7215
7259
|
(l) => l && typeof l === "object" && l["type"] !== "hidden"
|
|
@@ -7240,6 +7284,14 @@ function preValidateConfig(config) {
|
|
|
7240
7284
|
});
|
|
7241
7285
|
}
|
|
7242
7286
|
}
|
|
7287
|
+
for (const prop of [...CONTAINER_VALIDATION_PROPS, ...ARRAY_SIZE_PROPS]) {
|
|
7288
|
+
if (prop in f) {
|
|
7289
|
+
errors.push({
|
|
7290
|
+
path: `${path}.${prop}`,
|
|
7291
|
+
message: `"page" containers do NOT support "${prop}". Pages flatten into the form and have no schema path of their own.`
|
|
7292
|
+
});
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7243
7295
|
}
|
|
7244
7296
|
if (parentType && NESTING_RULES[parentType]) {
|
|
7245
7297
|
const rules = NESTING_RULES[parentType];
|
|
@@ -7320,7 +7372,7 @@ function preValidateConfig(config) {
|
|
|
7320
7372
|
});
|
|
7321
7373
|
}
|
|
7322
7374
|
}
|
|
7323
|
-
if (["row", "group", "page"].includes(fieldType || "")) {
|
|
7375
|
+
if (["row", "group", "page", "container"].includes(fieldType || "")) {
|
|
7324
7376
|
if (!("fields" in f)) {
|
|
7325
7377
|
errors.push({
|
|
7326
7378
|
path: `${path}.fields`,
|
|
@@ -7333,6 +7385,19 @@ function preValidateConfig(config) {
|
|
|
7333
7385
|
});
|
|
7334
7386
|
}
|
|
7335
7387
|
}
|
|
7388
|
+
if (fieldType === "container") {
|
|
7389
|
+
if (!("wrappers" in f)) {
|
|
7390
|
+
errors.push({
|
|
7391
|
+
path: `${path}.wrappers`,
|
|
7392
|
+
message: `"container" container "${fieldKey || "unknown"}" is MISSING required "wrappers" property. Add a wrappers array naming the UI chrome to wrap the children in, or [] for no chrome. Do NOT switch to a "group" instead: a container flattens its children into the parent value, while a group nests them under its own key and owns a schema path, so the submitted value shape and validation behaviour both change. Expected structure: ${EXPECTED_STRUCTURE["container"]}`
|
|
7393
|
+
});
|
|
7394
|
+
} else if (!Array.isArray(f["wrappers"])) {
|
|
7395
|
+
errors.push({
|
|
7396
|
+
path: `${path}.wrappers`,
|
|
7397
|
+
message: `"container" container "${fieldKey || "unknown"}" has invalid "wrappers" - must be an array of wrapper objects, not ${typeof f["wrappers"]}.`
|
|
7398
|
+
});
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7336
7401
|
if (fieldType === "array") {
|
|
7337
7402
|
const hasFields = "fields" in f;
|
|
7338
7403
|
const hasTemplate = "template" in f;
|
|
@@ -8170,7 +8235,7 @@ function registerResources(server) {
|
|
|
8170
8235
|
}
|
|
8171
8236
|
|
|
8172
8237
|
// packages/dynamic-form-mcp/src/tools/lookup.tool.ts
|
|
8173
|
-
import { z as
|
|
8238
|
+
import { z as z57 } from "zod";
|
|
8174
8239
|
|
|
8175
8240
|
// packages/dynamic-form-mcp/src/tools/data/lookup-topics.ts
|
|
8176
8241
|
var TOPICS = {
|
|
@@ -9905,25 +9970,42 @@ provideDynamicForm({
|
|
|
9905
9970
|
| FieldContext | \`ctx.value()\` | \`ctx.valueOf('path' as never)\` | custom validators |`
|
|
9906
9971
|
},
|
|
9907
9972
|
containers: {
|
|
9908
|
-
brief: `| Container | Label? | Logic? | Notes |
|
|
9909
|
-
|
|
9910
|
-
| page | NO | hidden only | Nav buttons INSIDE |
|
|
9911
|
-
| group | NO | hidden only | Creates nested object |
|
|
9912
|
-
| array | NO | hidden only | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9913
|
-
| row | NO | hidden only | Layout only
|
|
9973
|
+
brief: `| Container | Label? | Logic? | Value | Notes |
|
|
9974
|
+
|-----------|--------|--------|-------|-------|
|
|
9975
|
+
| page | NO | hidden only | flattened | Nav buttons INSIDE |
|
|
9976
|
+
| group | NO | hidden only | nested under its key | Creates nested object |
|
|
9977
|
+
| array | NO | hidden only | nested under its key | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9978
|
+
| row | NO | hidden only | flattened | Layout only |
|
|
9979
|
+
| container | NO | hidden only | flattened | Layout + REQUIRED \`wrappers\` chain |`,
|
|
9914
9980
|
full: `# Container Rules
|
|
9915
9981
|
|
|
9916
|
-
| Container | Label? | Logic? | Allowed Children | Notes |
|
|
9917
|
-
|
|
9918
|
-
| page | NO | YES (hidden only) | rows, groups, arrays, leaf fields, buttons | Nav buttons go INSIDE |
|
|
9919
|
-
| group | NO | YES (hidden only) | rows, arrays, leaf fields (NOT pages, groups) | Creates nested object |
|
|
9920
|
-
| array | NO | YES (hidden only) | rows, groups, leaf fields (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9921
|
-
| row | NO | YES (hidden only) | groups, arrays, containers,
|
|
9982
|
+
| Container | Label? | Logic? | Value Handling | Allowed Children | Notes |
|
|
9983
|
+
|-----------|--------|--------|----------------|------------------|-------|
|
|
9984
|
+
| page | NO | YES (hidden only) | flattened | rows, groups, arrays, containers, hidden, leaf fields, buttons (NOT pages) | Nav buttons go INSIDE |
|
|
9985
|
+
| group | NO | YES (hidden only) | nested under its key | rows, arrays, containers, hidden, leaf fields, buttons (NOT pages, groups) | Creates nested object |
|
|
9986
|
+
| array | NO | YES (hidden only) | nested under its key | rows, groups, containers, hidden, leaf fields, buttons (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
|
|
9987
|
+
| row | NO | YES (hidden only) | flattened | groups, arrays, containers, leaf fields, buttons (NOT pages, nested rows, hidden) | Layout only |
|
|
9988
|
+
| container | NO | YES (hidden only) | flattened | rows, groups, arrays, nested containers, hidden, leaf fields, buttons (NOT pages) | \`wrappers\` is REQUIRED (use [] for none) |
|
|
9989
|
+
|
|
9990
|
+
**Value handling decides which container you want.** \`page\`, \`row\` and \`container\` flatten: their children land directly in the parent value, and the container key never appears in the submitted data. \`group\` and \`array\` nest their children under their own key.
|
|
9991
|
+
|
|
9992
|
+
That difference is not cosmetic, so these are not interchangeable:
|
|
9993
|
+
|
|
9994
|
+
\`\`\`typescript
|
|
9995
|
+
// container \u2192 { street: '', city: '' } (chrome only, key not in the value)
|
|
9996
|
+
{ key: 'billing', type: 'container', wrappers: [{ type: 'css', cssClasses: 'card' }], fields: [street, city] }
|
|
9997
|
+
|
|
9998
|
+
// group \u2192 { billing: { street: '', city: '' } } (adds a level to the value)
|
|
9999
|
+
{ key: 'billing', type: 'group', fields: [street, city] }
|
|
10000
|
+
\`\`\`
|
|
10001
|
+
|
|
10002
|
+
Only \`group\` and \`array\` own a schema path, so only they take container-level \`validators\`, \`required\` and \`validationMessages\`. Reach for \`container\` when you want chrome around fields without changing the data shape, and for \`group\` when you want the nesting or the cross-field validation.
|
|
9922
10003
|
|
|
9923
10004
|
**CRITICAL:**
|
|
9924
|
-
- All containers (page, group, row, array) support only the \`hidden\` logic type for conditional visibility.
|
|
10005
|
+
- All containers (page, group, row, array, container) support only the \`hidden\` logic type for conditional visibility.
|
|
9925
10006
|
- For other logic types (disabled, required, readonly, derivation), apply them to child fields instead.
|
|
9926
|
-
- Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode
|
|
10007
|
+
- Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode.
|
|
10008
|
+
- Rows reject nested rows and hidden fields; put hidden fields at page or form level.`
|
|
9927
10009
|
},
|
|
9928
10010
|
"field-placement": {
|
|
9929
10011
|
brief: `**Multi-page:** ALL root fields must be pages. Hidden fields go INSIDE first page.
|
|
@@ -10717,7 +10799,8 @@ var TOPIC_DESCRIPTIONS = {
|
|
|
10717
10799
|
"field-placement": "Which fields can go where \u2014 nesting and placement rules",
|
|
10718
10800
|
"logic-matrix": "Which logic types are supported on which field types",
|
|
10719
10801
|
"context-api": "FieldContext API \u2014 valueOf(), formValue, parent, arrayIndex",
|
|
10720
|
-
containers: "Overview of all container types (group, row, array, page)",
|
|
10802
|
+
containers: "Overview of all container types (group, row, array, container, page) and what each may hold",
|
|
10803
|
+
container: "Wraps children in a wrapper chain; flattens their values into the parent like a row",
|
|
10721
10804
|
"array-buttons": "Add/remove array item button configuration",
|
|
10722
10805
|
"custom-validators": "Custom validator functions via validators array",
|
|
10723
10806
|
conditions: "Condition operators and types for logic rules",
|
|
@@ -10977,7 +11060,7 @@ function formatTopicItem(key) {
|
|
|
10977
11060
|
}
|
|
10978
11061
|
function getTopicList() {
|
|
10979
11062
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
10980
|
-
const containers = ["
|
|
11063
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
10981
11064
|
const wrappers = ["wrappers", ...getWrappers().map((w) => w.type)];
|
|
10982
11065
|
const addons = ["addons", ...getAddonTypes().map((k) => k.type)];
|
|
10983
11066
|
const concepts = [
|
|
@@ -11033,7 +11116,7 @@ ${patterns.map(formatTopicItem).join("\n")}
|
|
|
11033
11116
|
}
|
|
11034
11117
|
function formatFieldInfoFull(field, uiFieldType) {
|
|
11035
11118
|
const lines = [];
|
|
11036
|
-
const isContainer =
|
|
11119
|
+
const isContainer = field.category === "container";
|
|
11037
11120
|
const isHidden = field.type === "hidden";
|
|
11038
11121
|
lines.push(`## ${field.type} field`);
|
|
11039
11122
|
lines.push("");
|
|
@@ -11462,11 +11545,11 @@ Patterns: field-placement, logic-matrix, context-api, containers, multi-page-got
|
|
|
11462
11545
|
Use topic="list" to see all available topics with descriptions.
|
|
11463
11546
|
Use ngforge_search query="your question" to find topics by keyword.`,
|
|
11464
11547
|
{
|
|
11465
|
-
topic:
|
|
11466
|
-
'Topic to look up: field types (input, select, hidden, group, row, array, page), wrappers (wrappers, css, section, arraySection), concepts (validation, conditional, derivation, property-derivation, options-format), patterns (golden-path, pitfalls, multi-page-gotchas), or "list" to see all topics'
|
|
11548
|
+
topic: z57.string().describe(
|
|
11549
|
+
'Topic to look up: field types (input, select, hidden, group, row, array, container, page), wrappers (wrappers, css, section, arraySection), concepts (validation, conditional, derivation, property-derivation, options-format), patterns (golden-path, pitfalls, multi-page-gotchas), or "list" to see all topics'
|
|
11467
11550
|
),
|
|
11468
|
-
depth:
|
|
11469
|
-
uiIntegration:
|
|
11551
|
+
depth: z57.enum(["brief", "full", "schema"]).default("full").describe("brief=quick syntax (~20 lines), full=complete docs with examples, schema=include JSON schema"),
|
|
11552
|
+
uiIntegration: z57.enum(UI_INTEGRATIONS2).optional().describe("Filter UI-specific info (only with depth=schema). material, bootstrap, primeng, ionic")
|
|
11470
11553
|
},
|
|
11471
11554
|
async ({ topic, depth, uiIntegration }) => {
|
|
11472
11555
|
const normalizedTopic = topic.toLowerCase().trim();
|
|
@@ -11514,7 +11597,7 @@ ${nextSteps}`;
|
|
|
11514
11597
|
}
|
|
11515
11598
|
|
|
11516
11599
|
// packages/dynamic-form-mcp/src/tools/examples.tool.ts
|
|
11517
|
-
import { z as
|
|
11600
|
+
import { z as z58 } from "zod";
|
|
11518
11601
|
var PATTERNS = {
|
|
11519
11602
|
// ========== MINIMAL PATTERNS (NEW) ==========
|
|
11520
11603
|
"minimal-multipage": {
|
|
@@ -13779,10 +13862,10 @@ Standard patterns: derivation, property-derivation, multi-page, conditional, val
|
|
|
13779
13862
|
Use pattern="list" to see all available patterns.
|
|
13780
13863
|
Use depth="minimal" for code-only output (no markdown).`,
|
|
13781
13864
|
{
|
|
13782
|
-
pattern:
|
|
13865
|
+
pattern: z58.string().optional().describe(
|
|
13783
13866
|
'Pattern to get: minimal-multipage, minimal-array, minimal-simplified-array, minimal-conditional, minimal-validation, minimal-hidden, derivation, property-derivation, multi-page, conditional, validation, complete, mega. Use "list" to see all.'
|
|
13784
13867
|
),
|
|
13785
|
-
depth:
|
|
13868
|
+
depth: z58.enum(["minimal", "brief", "full", "explained"]).default("full").describe(
|
|
13786
13869
|
"minimal=code only (~20-50 lines), brief=code+summary, full=code+comments (default), explained=code+detailed explanation"
|
|
13787
13870
|
)
|
|
13788
13871
|
},
|
|
@@ -13847,7 +13930,7 @@ function extractCode(markdown) {
|
|
|
13847
13930
|
}
|
|
13848
13931
|
|
|
13849
13932
|
// packages/dynamic-form-mcp/src/tools/validate.tool.ts
|
|
13850
|
-
import { z as
|
|
13933
|
+
import { z as z59 } from "zod";
|
|
13851
13934
|
var UI_INTEGRATIONS3 = ["material", "bootstrap", "primeng", "ionic"];
|
|
13852
13935
|
var ERROR_TOPIC_HINTS = [
|
|
13853
13936
|
{ pattern: /options/i, hint: '`ngforge_lookup topic="options-format"` \u2014 correct options syntax' },
|
|
@@ -13900,10 +13983,10 @@ Example errors you'll see:
|
|
|
13900
13983
|
- "options MUST be at FIELD level, NOT inside props"
|
|
13901
13984
|
- "containers only support 'hidden' logic type"`,
|
|
13902
13985
|
{
|
|
13903
|
-
config:
|
|
13986
|
+
config: z59.union([z59.string(), z59.object({}).passthrough()]).describe(
|
|
13904
13987
|
"File path (.ts/.js) OR JSON string OR JSON object. Auto-detects: paths ending in .ts/.js or starting with / are treated as files."
|
|
13905
13988
|
),
|
|
13906
|
-
uiIntegration:
|
|
13989
|
+
uiIntegration: z59.enum(UI_INTEGRATIONS3).default("material").describe("UI library to validate against (material, bootstrap, primeng, ionic). Defaults to material.")
|
|
13907
13990
|
},
|
|
13908
13991
|
async ({ config, uiIntegration }) => {
|
|
13909
13992
|
try {
|
|
@@ -13995,7 +14078,7 @@ If validating JSON, ensure:
|
|
|
13995
14078
|
}
|
|
13996
14079
|
|
|
13997
14080
|
// packages/dynamic-form-mcp/src/tools/scaffold.tool.ts
|
|
13998
|
-
import { z as
|
|
14081
|
+
import { z as z60 } from "zod";
|
|
13999
14082
|
var UI_INTEGRATIONS4 = ["material", "bootstrap", "primeng", "ionic"];
|
|
14000
14083
|
function parseFieldDefs(fields) {
|
|
14001
14084
|
return fields.map((f) => {
|
|
@@ -14250,12 +14333,12 @@ Examples:
|
|
|
14250
14333
|
- With groups: pages=0 groups=["address","billing"]
|
|
14251
14334
|
- With hidden: hidden=["userId:abc123","source:web"]`,
|
|
14252
14335
|
{
|
|
14253
|
-
pages:
|
|
14254
|
-
arrays:
|
|
14255
|
-
groups:
|
|
14256
|
-
hidden:
|
|
14257
|
-
fields:
|
|
14258
|
-
uiIntegration:
|
|
14336
|
+
pages: z60.number().min(0).max(10).default(0).describe("Number of pages (0 = single-page form, 1-10 = multi-page wizard)"),
|
|
14337
|
+
arrays: z60.array(z60.string()).optional().describe('Array field names for dynamic lists, e.g., ["contacts", "addresses"]'),
|
|
14338
|
+
groups: z60.array(z60.string()).optional().describe('Group field names for nested objects, e.g., ["address", "billing"]'),
|
|
14339
|
+
hidden: z60.array(z60.string()).optional().describe('Hidden fields as "name:value" pairs, e.g., ["userId:abc", "version:1"]'),
|
|
14340
|
+
fields: z60.array(z60.string()).optional().describe('Basic fields as "name:type" pairs, e.g., ["email:input", "country:select", "age:slider"]'),
|
|
14341
|
+
uiIntegration: z60.enum(UI_INTEGRATIONS4).default("material").describe("UI library (for future UI-specific props). Defaults to material.")
|
|
14259
14342
|
},
|
|
14260
14343
|
async ({ pages, arrays = [], groups = [], hidden = [], fields = [] }) => {
|
|
14261
14344
|
const fieldDefs = parseFieldDefs(fields);
|
|
@@ -14297,11 +14380,11 @@ Examples:
|
|
|
14297
14380
|
}
|
|
14298
14381
|
|
|
14299
14382
|
// packages/dynamic-form-mcp/src/tools/search.tool.ts
|
|
14300
|
-
import { z as
|
|
14383
|
+
import { z as z61 } from "zod";
|
|
14301
14384
|
var searchIndex = null;
|
|
14302
14385
|
function getTopicSubcategory(key) {
|
|
14303
14386
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
14304
|
-
const containers = ["
|
|
14387
|
+
const containers = [...getFieldTypesByCategory("container").map((f) => f.type), "simplified-array"];
|
|
14305
14388
|
const concepts = [
|
|
14306
14389
|
"validation",
|
|
14307
14390
|
"validation-messages",
|
|
@@ -14341,6 +14424,19 @@ function buildIndex() {
|
|
|
14341
14424
|
toolCall: `ngforge_lookup topic="${key}"`
|
|
14342
14425
|
});
|
|
14343
14426
|
}
|
|
14427
|
+
for (const field of getFieldTypes()) {
|
|
14428
|
+
if (TOPICS[field.type]) continue;
|
|
14429
|
+
const description = TOPIC_DESCRIPTIONS[field.type] || field.description;
|
|
14430
|
+
entries.push({
|
|
14431
|
+
id: field.type,
|
|
14432
|
+
category: "topic",
|
|
14433
|
+
subcategory: getTopicSubcategory(field.type),
|
|
14434
|
+
description,
|
|
14435
|
+
aliases: aliasMap[field.type] || [],
|
|
14436
|
+
content: `${description} ${field.description}`.toLowerCase(),
|
|
14437
|
+
toolCall: `ngforge_lookup topic="${field.type}"`
|
|
14438
|
+
});
|
|
14439
|
+
}
|
|
14344
14440
|
for (const [key, pattern] of Object.entries(PATTERNS)) {
|
|
14345
14441
|
entries.push({
|
|
14346
14442
|
id: key,
|
|
@@ -14445,7 +14541,7 @@ Use this when you don't know the exact topic name.
|
|
|
14445
14541
|
|
|
14446
14542
|
New to ng-forge? Start with: ngforge_lookup topic="workflow"`,
|
|
14447
14543
|
{
|
|
14448
|
-
query:
|
|
14544
|
+
query: z61.string().describe("Natural language or keywords to search for")
|
|
14449
14545
|
},
|
|
14450
14546
|
async ({ query }) => {
|
|
14451
14547
|
const index = buildIndex();
|