@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.union([
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 SimplifiedArrayFieldSchema = ContainerBaseSchema.extend({
4999
+ const ArrayFieldSchema = ContainerBaseSchema.extend({
4918
5000
  type: z13.literal("array"),
4919
- // Template: single field (primitive array) or array of fields (object array)
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
- // Simplified API does not use fields
4932
- fields: z13.never().optional(),
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 zodToJsonSchema from "zod-to-json-schema";
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
- const { name, basePath } = options;
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
- const jsonSchema = zodToJsonSchema2(schema, {
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
- const jsonSchema = zodToJsonSchema2(schema, {
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 z55 } from "zod";
6900
+ import { z as z56 } from "zod";
6784
6901
  var MAX_DEPTH = 20;
6785
6902
  function collectDiscriminator(schema, into) {
6786
- if (schema instanceof z55.ZodLiteral) {
6787
- if (typeof schema.value === "string") into.add(schema.value);
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 z55.ZodEnum) {
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 z55.ZodLazy) {
6797
- visit(schema.schema, into, depth + 1);
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 z55.ZodDiscriminatedUnion || schema instanceof z55.ZodUnion) {
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 z55.ZodObject) {
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.errors.map((err) => {
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 (message === "Invalid input" || message === "Invalid union") {
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 = ["validators", "required", "email", "min", "max", "minLength", "maxLength", "pattern", "value"];
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 or validate - they are purely for layout/grouping.${expected ? ` Expected structure: ${expected}` : ""}`
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 z56 } from "zod";
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, nested rows, hidden fields, leaf fields (NOT pages) | Layout only |
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 = ["group", "row", "array", "simplified-array", "page"];
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 = ["row", "group", "array", "page"].includes(field.type);
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: z56.string().describe(
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: z56.enum(["brief", "full", "schema"]).default("full").describe("brief=quick syntax (~20 lines), full=complete docs with examples, schema=include JSON schema"),
11469
- uiIntegration: z56.enum(UI_INTEGRATIONS2).optional().describe("Filter UI-specific info (only with depth=schema). material, bootstrap, primeng, ionic")
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 z57 } from "zod";
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: z57.string().optional().describe(
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: z57.enum(["minimal", "brief", "full", "explained"]).default("full").describe(
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 z58 } from "zod";
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: z58.union([z58.string(), z58.object({}).passthrough()]).describe(
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: z58.enum(UI_INTEGRATIONS3).default("material").describe("UI library to validate against (material, bootstrap, primeng, ionic). Defaults to material.")
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 z59 } from "zod";
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: z59.number().min(0).max(10).default(0).describe("Number of pages (0 = single-page form, 1-10 = multi-page wizard)"),
14254
- arrays: z59.array(z59.string()).optional().describe('Array field names for dynamic lists, e.g., ["contacts", "addresses"]'),
14255
- groups: z59.array(z59.string()).optional().describe('Group field names for nested objects, e.g., ["address", "billing"]'),
14256
- hidden: z59.array(z59.string()).optional().describe('Hidden fields as "name:value" pairs, e.g., ["userId:abc", "version:1"]'),
14257
- fields: z59.array(z59.string()).optional().describe('Basic fields as "name:type" pairs, e.g., ["email:input", "country:select", "age:slider"]'),
14258
- uiIntegration: z59.enum(UI_INTEGRATIONS4).default("material").describe("UI library (for future UI-specific props). Defaults to material.")
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 z60 } from "zod";
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 = ["group", "row", "array", "simplified-array", "page"];
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: z60.string().describe("Natural language or keywords to search for")
14544
+ query: z61.string().describe("Natural language or keywords to search for")
14449
14545
  },
14450
14546
  async ({ query }) => {
14451
14547
  const index = buildIndex();