@ng-forge/dynamic-form-mcp 1.2.0-next.7 → 1.2.0-next.9

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