@ng-forge/dynamic-form-mcp 0.8.0-next.4 → 0.8.0

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.
@@ -49,6 +49,12 @@ var FIELD_TYPES = [
49
49
  description: "Number of visible text lines",
50
50
  required: false,
51
51
  default: 3
52
+ },
53
+ cols: {
54
+ name: "cols",
55
+ type: "number",
56
+ description: "Number of visible columns",
57
+ required: false
52
58
  }
53
59
  },
54
60
  validationSupported: true,
@@ -59,6 +65,7 @@ var FIELD_TYPES = [
59
65
  type: 'textarea',
60
66
  label: 'Description',
61
67
  placeholder: 'Enter description...',
68
+ maxLength: 500,
62
69
  props: { rows: 5 }
63
70
  }`,
64
71
  minimalExample: `{ key: 'bio', type: 'textarea', label: 'Bio' }`
@@ -333,17 +340,20 @@ var FIELD_TYPES = [
333
340
  fields: {
334
341
  name: "fields",
335
342
  type: "RowAllowedChildren[]",
336
- description: "Child fields to render horizontally. Allowed: groups, arrays, VALUE fields (input, select, etc). NOT ALLOWED: pages, rows, hidden fields",
343
+ description: "Child fields to render horizontally. Allowed: groups, arrays, containers, nested rows, hidden fields, and value fields (input, select, etc). NOT ALLOWED: pages",
337
344
  required: true
338
345
  }
339
346
  },
340
347
  validationSupported: false,
341
348
  source: "core",
342
- allowedIn: ["top-level", "page", "group", "array"],
343
- notAllowedIn: ["row"],
349
+ allowedIn: ["top-level", "page", "group", "array", "row", "container"],
350
+ notAllowedIn: [],
344
351
  canContain: [
345
352
  "group",
346
353
  "array",
354
+ "container",
355
+ "row",
356
+ "hidden",
347
357
  "input",
348
358
  "textarea",
349
359
  "select",
@@ -359,7 +369,7 @@ var FIELD_TYPES = [
359
369
  "next",
360
370
  "previous"
361
371
  ],
362
- cannotContain: ["page", "row", "hidden"],
372
+ cannotContain: ["page"],
363
373
  example: `{
364
374
  key: 'nameRow',
365
375
  type: 'row',
@@ -371,9 +381,7 @@ var FIELD_TYPES = [
371
381
  ]
372
382
  }
373
383
  // NOT ALLOWED in rows:
374
- // - type: 'hidden' (hidden fields cannot be inside rows)
375
- // - type: 'page' (pages are top-level only)
376
- // - type: 'row' (no nested rows)`,
384
+ // - type: 'page' (pages are top-level only)`,
377
385
  minimalExample: `{ key: 'row1', type: 'row', fields: [...] }`
378
386
  },
379
387
  {
@@ -1105,30 +1113,12 @@ var UI_ADAPTERS = [
1105
1113
  description: "Hint text below the field",
1106
1114
  required: false
1107
1115
  },
1108
- rows: {
1109
- name: "rows",
1110
- type: "number",
1111
- description: "Visible row count",
1112
- required: false
1113
- },
1114
- cols: {
1115
- name: "cols",
1116
- type: "number",
1117
- description: "Visible column count",
1118
- required: false
1119
- },
1120
1116
  resize: {
1121
1117
  name: "resize",
1122
1118
  type: "'none' | 'both' | 'horizontal' | 'vertical'",
1123
1119
  description: "Resize handle behavior",
1124
1120
  required: false,
1125
1121
  default: "vertical"
1126
- },
1127
- maxLength: {
1128
- name: "maxLength",
1129
- type: "number",
1130
- description: "Maximum character length",
1131
- required: false
1132
1122
  }
1133
1123
  }
1134
1124
  }
@@ -5339,7 +5329,6 @@ var PrimeDatepickerPropsSchema = PrimeCommonPropsSchema.extend({
5339
5329
  var PrimeTextareaPropsSchema = PrimeCommonPropsSchema.extend({
5340
5330
  rows: z47.number().positive().optional(),
5341
5331
  autoResize: z47.boolean().optional(),
5342
- maxlength: z47.number().positive().optional(),
5343
5332
  placeholder: DynamicTextSchema.optional()
5344
5333
  });
5345
5334
  var PrimeSliderPropsSchema = PrimeCommonPropsSchema.extend({
@@ -5524,9 +5513,8 @@ var IonicInputPropsSchema = z51.object({
5524
5513
  hint: DynamicTextSchema.optional(),
5525
5514
  errorText: DynamicTextSchema.optional(),
5526
5515
  counter: z51.boolean().optional(),
5527
- maxlength: z51.number().positive().optional(),
5528
5516
  clearInput: z51.boolean().optional(),
5529
- type: z51.enum(["text", "email", "password", "number", "tel", "url"]).optional(),
5517
+ type: z51.enum(["text", "email", "password", "number", "tel", "url", "search"]).optional(),
5530
5518
  placeholder: DynamicTextSchema.optional()
5531
5519
  });
5532
5520
  var IonicCheckboxPropsSchema = z51.object({
@@ -5591,7 +5579,6 @@ var IonicSelectPropsSchema = z51.object({
5591
5579
  var IonicTextareaPropsSchema = z51.object({
5592
5580
  rows: z51.number().positive().optional(),
5593
5581
  autoGrow: z51.boolean().optional(),
5594
- maxlength: z51.number().positive().optional(),
5595
5582
  counter: z51.boolean().optional(),
5596
5583
  fill: z51.enum(["solid", "outline"]).optional(),
5597
5584
  shape: z51.literal("round").optional(),
@@ -6933,10 +6920,8 @@ Included in form value but not rendered.
6933
6920
 
6934
6921
  Use \`col\` (1-12) on child fields for grid width. Row is purely for layout. Supports only \`hidden\` logic type for conditional visibility.
6935
6922
 
6936
- **Allowed children:** groups, arrays, value fields (input, select, checkbox, etc.)
6937
- **NOT allowed:** \`hidden\` fields, \`page\` containers, nested \`row\` containers
6938
-
6939
- \u26A0\uFE0F **Common mistake:** Putting hidden fields inside rows - hidden fields should be at page or form level, not in rows.`
6923
+ **Allowed children:** groups, arrays, containers, nested rows, hidden fields, value fields (input, select, checkbox, etc.)
6924
+ **NOT allowed:** \`page\` containers`
6940
6925
  },
6941
6926
  array: {
6942
6927
  brief: `**Two APIs available:**
@@ -7352,8 +7337,15 @@ logic: [{ type: 'derivation', expression: '...', stopOnUserOverride: true, reEng
7352
7337
 
7353
7338
  // HTTP derivation (server-driven values)
7354
7339
  logic: [{ type: 'derivation', source: 'http', http: { url: '/api/rate', queryParams: { from: 'formValue.currency' } }, responseExpression: 'response.rate', dependsOn: ['currency'] }]
7340
+
7341
+ // Self-dependency token: $self resolves to the field's own absolute path at collection time
7342
+ logic: [{ type: 'derivation', functionName: 'uppercase', dependsOn: ['$self'] }]
7343
+
7344
+ // Parent-container token: $group resolves to the field's nearest parent group/array path
7345
+ logic: [{ type: 'derivation', functionName: 'computeFromGroup', dependsOn: ['$group'] }]
7355
7346
  \`\`\`
7356
7347
  Derivation is always defined ON the field that receives the computed value (self-targeting).
7348
+ \`dependsOn\` paths are absolute from the form root (e.g. \`'address.state'\`). Use \`'$self'\` for the field's own absolute path and \`'$group'\` for its nearest parent group/array \u2014 both useful in factory-built fields where ancestor keys aren't known at config time.
7357
7349
  **Variables:** \`formValue\`, \`fieldValue\`, \`fieldState\`, \`formFieldState\`, \`externalData\` (see topic: expression-variables)
7358
7350
  **For deriving component properties** (minDate, options, label): use \`targetProperty\` (see topic \`property-derivation\`)`,
7359
7351
  full: `# Value Derivation (Computed Fields)
@@ -7417,6 +7409,98 @@ Derivations are always defined ON the field that receives the computed value (se
7417
7409
  }
7418
7410
  \`\`\`
7419
7411
 
7412
+ ## \`dependsOn\` Path Resolution
7413
+
7414
+ \`dependsOn\` paths are **absolute from the form root**, dotted notation. A field nested under a group has the group's key in its path:
7415
+
7416
+ \`\`\`typescript
7417
+ {
7418
+ type: 'group', key: 'address',
7419
+ fields: [
7420
+ { key: 'state', type: 'input', logic: [{
7421
+ type: 'derivation',
7422
+ functionName: 'uppercaseState',
7423
+ dependsOn: ['address.state'] // absolute path from root
7424
+ }]}
7425
+ ]
7426
+ }
7427
+ \`\`\`
7428
+
7429
+ Layout containers (\`page\`, \`row\`, \`container\`) do **not** contribute to the path \u2014 only \`group\` fields do. Array fields use the \`'arrayKey.$.fieldKey'\` placeholder convention.
7430
+
7431
+ ### \`$self\` token
7432
+
7433
+ Use \`'$self'\` in \`dependsOn\` to refer to the field's own absolute path. Useful for self-derivations on fields built by factory helpers, where the ancestor group keys aren't known at config-authoring time:
7434
+
7435
+ \`\`\`typescript
7436
+ {
7437
+ type: 'group', key: 'address',
7438
+ fields: [
7439
+ { key: 'state', type: 'input', logic: [{
7440
+ type: 'derivation',
7441
+ functionName: 'uppercaseState',
7442
+ dependsOn: ['$self'] // resolves to 'address.state' at collection time
7443
+ }]}
7444
+ ]
7445
+ }
7446
+ \`\`\`
7447
+
7448
+ \`$self\` works alongside other absolute deps and inside arrays (resolves to the array placeholder form, e.g. \`'items.$.name'\`).
7449
+
7450
+ ### \`$group\` token
7451
+
7452
+ Use \`'$group'\` in \`dependsOn\` to refer to the field's **nearest parent container** (group or array). The derivation fires whenever any sibling under the same parent changes \u2014 handy when you don't want to enumerate each sibling explicitly:
7453
+
7454
+ \`\`\`typescript
7455
+ {
7456
+ type: 'group', key: 'address',
7457
+ fields: [
7458
+ { key: 'country', type: 'input' },
7459
+ { key: 'state', type: 'input', logic: [{
7460
+ type: 'derivation',
7461
+ functionName: 'pickStateFromCountry',
7462
+ dependsOn: ['$group'] // resolves to 'address' at collection time
7463
+ }]}
7464
+ ]
7465
+ }
7466
+ \`\`\`
7467
+
7468
+ Resolution rules:
7469
+ - Inside group(s) only \u2192 the dotted ancestor group path (\`'address'\` or \`'org.address'\`).
7470
+ - Directly inside an array \u2192 the array key (\`'items'\`). Fires on any item change.
7471
+ - Inside group(s) inside an array \u2192 \`'items.$.address'\` form.
7472
+ - At form root \u2192 throws \`DynamicFormError\` (no parent container exists).
7473
+
7474
+ ### Token prefix substitution
7475
+
7476
+ Both \`$self\` and \`$group\` accept a dotted suffix that is appended to the resolved path:
7477
+
7478
+ \`\`\`typescript
7479
+ dependsOn: ['$group.country'] // resolves to 'address.country' inside group 'address'
7480
+ dependsOn: ['$group.contact.email'] // multi-segment suffix supported
7481
+ dependsOn: ['$self.flag'] // resolves to '<fieldKey>.flag' (object-valued fields)
7482
+ \`\`\`
7483
+
7484
+ Use this to target a specific sibling without naming the parent group key \u2014 handy in factory-built field shapes.
7485
+
7486
+ ### \`groupValue\` in evaluation context
7487
+
7488
+ Custom derivation functions and expressions receive a \`groupValue\` on the \`EvaluationContext\` that mirrors the \`$group\` token at evaluation time:
7489
+
7490
+ \`\`\`typescript
7491
+ deriveStateFromCountry: (ctx) => {
7492
+ const country = ctx.groupValue?.country; // no need to know the parent key
7493
+ return country === 'usa' ? 'NY' : '';
7494
+ }
7495
+ \`\`\`
7496
+
7497
+ Population rules:
7498
+ - Inside a group: the parent group's value object.
7499
+ - Inside nested groups: the innermost parent group's value.
7500
+ - Directly inside an array item (no inner group): the array item itself (same as \`formValue\` in array context).
7501
+ - Inside a group inside an array item: the inner group's value within the item.
7502
+ - Field at form root: \`undefined\`.
7503
+
7420
7504
  ## HTTP Derivation (Server-Driven Values)
7421
7505
 
7422
7506
  Fetch derived values from an HTTP endpoint. Requires \`dependsOn\` (explicit) and \`responseExpression\`.
@@ -8221,7 +8305,7 @@ provideDynamicForm({
8221
8305
  | page | NO | hidden only | Nav buttons INSIDE |
8222
8306
  | group | NO | hidden only | Creates nested object |
8223
8307
  | array | NO | hidden only | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
8224
- | row | NO | hidden only | Layout only, no hidden fields |`,
8308
+ | row | NO | hidden only | Layout only |`,
8225
8309
  full: `# Container Rules
8226
8310
 
8227
8311
  | Container | Label? | Logic? | Allowed Children | Notes |
@@ -8229,17 +8313,16 @@ provideDynamicForm({
8229
8313
  | page | NO | YES (hidden only) | rows, groups, arrays, leaf fields, buttons | Nav buttons go INSIDE |
8230
8314
  | group | NO | YES (hidden only) | rows, leaf fields (NOT pages, groups) | Creates nested object |
8231
8315
  | array | NO | YES (hidden only) | rows, groups, leaf fields (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
8232
- | row | NO | YES (hidden only) | groups, arrays, leaf fields (NOT hidden, pages, rows) | Layout only |
8316
+ | row | NO | YES (hidden only) | groups, arrays, containers, nested rows, hidden fields, leaf fields (NOT pages) | Layout only |
8233
8317
 
8234
8318
  **CRITICAL:**
8235
8319
  - All containers (page, group, row, array) support only the \`hidden\` logic type for conditional visibility.
8236
8320
  - For other logic types (disabled, required, readonly, derivation), apply them to child fields instead.
8237
- - Hidden fields cannot be inside rows - place them at page/form level.
8238
8321
  - Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode.`
8239
8322
  },
8240
8323
  "field-placement": {
8241
8324
  brief: `**Multi-page:** ALL root fields must be pages. Hidden fields go INSIDE first page.
8242
- **Rows:** NO hidden fields, NO nested rows, NO pages
8325
+ **Rows:** NO pages
8243
8326
  **Groups:** NO nested groups, NO pages`,
8244
8327
  full: `# Field Placement Rules
8245
8328
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-forge/dynamic-form-mcp",
3
- "version": "0.8.0-next.4",
3
+ "version": "0.8.0",
4
4
  "description": "MCP server for ng-forge dynamic forms - AI-assisted form schema generation",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -51,11 +51,11 @@
51
51
  "zod": "^3.25.76"
52
52
  },
53
53
  "devDependencies": {
54
- "@ng-forge/dynamic-forms": "^0.8.0-next.4",
55
- "@ng-forge/dynamic-forms-bootstrap": "^0.8.0-next.4",
56
- "@ng-forge/dynamic-forms-ionic": "^0.8.0-next.4",
57
- "@ng-forge/dynamic-forms-material": "^0.8.0-next.4",
58
- "@ng-forge/dynamic-forms-primeng": "^0.8.0-next.4",
54
+ "@ng-forge/dynamic-forms": "^0.8.0",
55
+ "@ng-forge/dynamic-forms-bootstrap": "^0.8.0",
56
+ "@ng-forge/dynamic-forms-ionic": "^0.8.0",
57
+ "@ng-forge/dynamic-forms-material": "^0.8.0",
58
+ "@ng-forge/dynamic-forms-primeng": "^0.8.0",
59
59
  "tsx": "^4.19.0"
60
60
  }
61
61
  }
package/src/index.js CHANGED
@@ -47,6 +47,12 @@ var FIELD_TYPES = [
47
47
  description: "Number of visible text lines",
48
48
  required: false,
49
49
  default: 3
50
+ },
51
+ cols: {
52
+ name: "cols",
53
+ type: "number",
54
+ description: "Number of visible columns",
55
+ required: false
50
56
  }
51
57
  },
52
58
  validationSupported: true,
@@ -57,6 +63,7 @@ var FIELD_TYPES = [
57
63
  type: 'textarea',
58
64
  label: 'Description',
59
65
  placeholder: 'Enter description...',
66
+ maxLength: 500,
60
67
  props: { rows: 5 }
61
68
  }`,
62
69
  minimalExample: `{ key: 'bio', type: 'textarea', label: 'Bio' }`
@@ -331,17 +338,20 @@ var FIELD_TYPES = [
331
338
  fields: {
332
339
  name: "fields",
333
340
  type: "RowAllowedChildren[]",
334
- description: "Child fields to render horizontally. Allowed: groups, arrays, VALUE fields (input, select, etc). NOT ALLOWED: pages, rows, hidden fields",
341
+ description: "Child fields to render horizontally. Allowed: groups, arrays, containers, nested rows, hidden fields, and value fields (input, select, etc). NOT ALLOWED: pages",
335
342
  required: true
336
343
  }
337
344
  },
338
345
  validationSupported: false,
339
346
  source: "core",
340
- allowedIn: ["top-level", "page", "group", "array"],
341
- notAllowedIn: ["row"],
347
+ allowedIn: ["top-level", "page", "group", "array", "row", "container"],
348
+ notAllowedIn: [],
342
349
  canContain: [
343
350
  "group",
344
351
  "array",
352
+ "container",
353
+ "row",
354
+ "hidden",
345
355
  "input",
346
356
  "textarea",
347
357
  "select",
@@ -357,7 +367,7 @@ var FIELD_TYPES = [
357
367
  "next",
358
368
  "previous"
359
369
  ],
360
- cannotContain: ["page", "row", "hidden"],
370
+ cannotContain: ["page"],
361
371
  example: `{
362
372
  key: 'nameRow',
363
373
  type: 'row',
@@ -369,9 +379,7 @@ var FIELD_TYPES = [
369
379
  ]
370
380
  }
371
381
  // NOT ALLOWED in rows:
372
- // - type: 'hidden' (hidden fields cannot be inside rows)
373
- // - type: 'page' (pages are top-level only)
374
- // - type: 'row' (no nested rows)`,
382
+ // - type: 'page' (pages are top-level only)`,
375
383
  minimalExample: `{ key: 'row1', type: 'row', fields: [...] }`
376
384
  },
377
385
  {
@@ -1103,30 +1111,12 @@ var UI_ADAPTERS = [
1103
1111
  description: "Hint text below the field",
1104
1112
  required: false
1105
1113
  },
1106
- rows: {
1107
- name: "rows",
1108
- type: "number",
1109
- description: "Visible row count",
1110
- required: false
1111
- },
1112
- cols: {
1113
- name: "cols",
1114
- type: "number",
1115
- description: "Visible column count",
1116
- required: false
1117
- },
1118
1114
  resize: {
1119
1115
  name: "resize",
1120
1116
  type: "'none' | 'both' | 'horizontal' | 'vertical'",
1121
1117
  description: "Resize handle behavior",
1122
1118
  required: false,
1123
1119
  default: "vertical"
1124
- },
1125
- maxLength: {
1126
- name: "maxLength",
1127
- type: "number",
1128
- description: "Maximum character length",
1129
- required: false
1130
1120
  }
1131
1121
  }
1132
1122
  }
@@ -5344,7 +5334,6 @@ var PrimeDatepickerPropsSchema = PrimeCommonPropsSchema.extend({
5344
5334
  var PrimeTextareaPropsSchema = PrimeCommonPropsSchema.extend({
5345
5335
  rows: z47.number().positive().optional(),
5346
5336
  autoResize: z47.boolean().optional(),
5347
- maxlength: z47.number().positive().optional(),
5348
5337
  placeholder: DynamicTextSchema.optional()
5349
5338
  });
5350
5339
  var PrimeSliderPropsSchema = PrimeCommonPropsSchema.extend({
@@ -5529,9 +5518,8 @@ var IonicInputPropsSchema = z51.object({
5529
5518
  hint: DynamicTextSchema.optional(),
5530
5519
  errorText: DynamicTextSchema.optional(),
5531
5520
  counter: z51.boolean().optional(),
5532
- maxlength: z51.number().positive().optional(),
5533
5521
  clearInput: z51.boolean().optional(),
5534
- type: z51.enum(["text", "email", "password", "number", "tel", "url"]).optional(),
5522
+ type: z51.enum(["text", "email", "password", "number", "tel", "url", "search"]).optional(),
5535
5523
  placeholder: DynamicTextSchema.optional()
5536
5524
  });
5537
5525
  var IonicCheckboxPropsSchema = z51.object({
@@ -5596,7 +5584,6 @@ var IonicSelectPropsSchema = z51.object({
5596
5584
  var IonicTextareaPropsSchema = z51.object({
5597
5585
  rows: z51.number().positive().optional(),
5598
5586
  autoGrow: z51.boolean().optional(),
5599
- maxlength: z51.number().positive().optional(),
5600
5587
  counter: z51.boolean().optional(),
5601
5588
  fill: z51.enum(["solid", "outline"]).optional(),
5602
5589
  shape: z51.literal("round").optional(),
@@ -6982,10 +6969,8 @@ Included in form value but not rendered.
6982
6969
 
6983
6970
  Use \`col\` (1-12) on child fields for grid width. Row is purely for layout. Supports only \`hidden\` logic type for conditional visibility.
6984
6971
 
6985
- **Allowed children:** groups, arrays, value fields (input, select, checkbox, etc.)
6986
- **NOT allowed:** \`hidden\` fields, \`page\` containers, nested \`row\` containers
6987
-
6988
- \u26A0\uFE0F **Common mistake:** Putting hidden fields inside rows - hidden fields should be at page or form level, not in rows.`
6972
+ **Allowed children:** groups, arrays, containers, nested rows, hidden fields, value fields (input, select, checkbox, etc.)
6973
+ **NOT allowed:** \`page\` containers`
6989
6974
  },
6990
6975
  array: {
6991
6976
  brief: `**Two APIs available:**
@@ -7401,8 +7386,15 @@ logic: [{ type: 'derivation', expression: '...', stopOnUserOverride: true, reEng
7401
7386
 
7402
7387
  // HTTP derivation (server-driven values)
7403
7388
  logic: [{ type: 'derivation', source: 'http', http: { url: '/api/rate', queryParams: { from: 'formValue.currency' } }, responseExpression: 'response.rate', dependsOn: ['currency'] }]
7389
+
7390
+ // Self-dependency token: $self resolves to the field's own absolute path at collection time
7391
+ logic: [{ type: 'derivation', functionName: 'uppercase', dependsOn: ['$self'] }]
7392
+
7393
+ // Parent-container token: $group resolves to the field's nearest parent group/array path
7394
+ logic: [{ type: 'derivation', functionName: 'computeFromGroup', dependsOn: ['$group'] }]
7404
7395
  \`\`\`
7405
7396
  Derivation is always defined ON the field that receives the computed value (self-targeting).
7397
+ \`dependsOn\` paths are absolute from the form root (e.g. \`'address.state'\`). Use \`'$self'\` for the field's own absolute path and \`'$group'\` for its nearest parent group/array \u2014 both useful in factory-built fields where ancestor keys aren't known at config time.
7406
7398
  **Variables:** \`formValue\`, \`fieldValue\`, \`fieldState\`, \`formFieldState\`, \`externalData\` (see topic: expression-variables)
7407
7399
  **For deriving component properties** (minDate, options, label): use \`targetProperty\` (see topic \`property-derivation\`)`,
7408
7400
  full: `# Value Derivation (Computed Fields)
@@ -7466,6 +7458,98 @@ Derivations are always defined ON the field that receives the computed value (se
7466
7458
  }
7467
7459
  \`\`\`
7468
7460
 
7461
+ ## \`dependsOn\` Path Resolution
7462
+
7463
+ \`dependsOn\` paths are **absolute from the form root**, dotted notation. A field nested under a group has the group's key in its path:
7464
+
7465
+ \`\`\`typescript
7466
+ {
7467
+ type: 'group', key: 'address',
7468
+ fields: [
7469
+ { key: 'state', type: 'input', logic: [{
7470
+ type: 'derivation',
7471
+ functionName: 'uppercaseState',
7472
+ dependsOn: ['address.state'] // absolute path from root
7473
+ }]}
7474
+ ]
7475
+ }
7476
+ \`\`\`
7477
+
7478
+ Layout containers (\`page\`, \`row\`, \`container\`) do **not** contribute to the path \u2014 only \`group\` fields do. Array fields use the \`'arrayKey.$.fieldKey'\` placeholder convention.
7479
+
7480
+ ### \`$self\` token
7481
+
7482
+ Use \`'$self'\` in \`dependsOn\` to refer to the field's own absolute path. Useful for self-derivations on fields built by factory helpers, where the ancestor group keys aren't known at config-authoring time:
7483
+
7484
+ \`\`\`typescript
7485
+ {
7486
+ type: 'group', key: 'address',
7487
+ fields: [
7488
+ { key: 'state', type: 'input', logic: [{
7489
+ type: 'derivation',
7490
+ functionName: 'uppercaseState',
7491
+ dependsOn: ['$self'] // resolves to 'address.state' at collection time
7492
+ }]}
7493
+ ]
7494
+ }
7495
+ \`\`\`
7496
+
7497
+ \`$self\` works alongside other absolute deps and inside arrays (resolves to the array placeholder form, e.g. \`'items.$.name'\`).
7498
+
7499
+ ### \`$group\` token
7500
+
7501
+ Use \`'$group'\` in \`dependsOn\` to refer to the field's **nearest parent container** (group or array). The derivation fires whenever any sibling under the same parent changes \u2014 handy when you don't want to enumerate each sibling explicitly:
7502
+
7503
+ \`\`\`typescript
7504
+ {
7505
+ type: 'group', key: 'address',
7506
+ fields: [
7507
+ { key: 'country', type: 'input' },
7508
+ { key: 'state', type: 'input', logic: [{
7509
+ type: 'derivation',
7510
+ functionName: 'pickStateFromCountry',
7511
+ dependsOn: ['$group'] // resolves to 'address' at collection time
7512
+ }]}
7513
+ ]
7514
+ }
7515
+ \`\`\`
7516
+
7517
+ Resolution rules:
7518
+ - Inside group(s) only \u2192 the dotted ancestor group path (\`'address'\` or \`'org.address'\`).
7519
+ - Directly inside an array \u2192 the array key (\`'items'\`). Fires on any item change.
7520
+ - Inside group(s) inside an array \u2192 \`'items.$.address'\` form.
7521
+ - At form root \u2192 throws \`DynamicFormError\` (no parent container exists).
7522
+
7523
+ ### Token prefix substitution
7524
+
7525
+ Both \`$self\` and \`$group\` accept a dotted suffix that is appended to the resolved path:
7526
+
7527
+ \`\`\`typescript
7528
+ dependsOn: ['$group.country'] // resolves to 'address.country' inside group 'address'
7529
+ dependsOn: ['$group.contact.email'] // multi-segment suffix supported
7530
+ dependsOn: ['$self.flag'] // resolves to '<fieldKey>.flag' (object-valued fields)
7531
+ \`\`\`
7532
+
7533
+ Use this to target a specific sibling without naming the parent group key \u2014 handy in factory-built field shapes.
7534
+
7535
+ ### \`groupValue\` in evaluation context
7536
+
7537
+ Custom derivation functions and expressions receive a \`groupValue\` on the \`EvaluationContext\` that mirrors the \`$group\` token at evaluation time:
7538
+
7539
+ \`\`\`typescript
7540
+ deriveStateFromCountry: (ctx) => {
7541
+ const country = ctx.groupValue?.country; // no need to know the parent key
7542
+ return country === 'usa' ? 'NY' : '';
7543
+ }
7544
+ \`\`\`
7545
+
7546
+ Population rules:
7547
+ - Inside a group: the parent group's value object.
7548
+ - Inside nested groups: the innermost parent group's value.
7549
+ - Directly inside an array item (no inner group): the array item itself (same as \`formValue\` in array context).
7550
+ - Inside a group inside an array item: the inner group's value within the item.
7551
+ - Field at form root: \`undefined\`.
7552
+
7469
7553
  ## HTTP Derivation (Server-Driven Values)
7470
7554
 
7471
7555
  Fetch derived values from an HTTP endpoint. Requires \`dependsOn\` (explicit) and \`responseExpression\`.
@@ -8270,7 +8354,7 @@ provideDynamicForm({
8270
8354
  | page | NO | hidden only | Nav buttons INSIDE |
8271
8355
  | group | NO | hidden only | Creates nested object |
8272
8356
  | array | NO | hidden only | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
8273
- | row | NO | hidden only | Layout only, no hidden fields |`,
8357
+ | row | NO | hidden only | Layout only |`,
8274
8358
  full: `# Container Rules
8275
8359
 
8276
8360
  | Container | Label? | Logic? | Allowed Children | Notes |
@@ -8278,17 +8362,16 @@ provideDynamicForm({
8278
8362
  | page | NO | YES (hidden only) | rows, groups, arrays, leaf fields, buttons | Nav buttons go INSIDE |
8279
8363
  | group | NO | YES (hidden only) | rows, leaf fields (NOT pages, groups) | Creates nested object |
8280
8364
  | array | NO | YES (hidden only) | rows, groups, leaf fields (NOT pages, arrays) | Two APIs: \`template\`+\`value\` (simplified) or \`fields\` (full) |
8281
- | row | NO | YES (hidden only) | groups, arrays, leaf fields (NOT hidden, pages, rows) | Layout only |
8365
+ | row | NO | YES (hidden only) | groups, arrays, containers, nested rows, hidden fields, leaf fields (NOT pages) | Layout only |
8282
8366
 
8283
8367
  **CRITICAL:**
8284
8368
  - All containers (page, group, row, array) support only the \`hidden\` logic type for conditional visibility.
8285
8369
  - For other logic types (disabled, required, readonly, derivation), apply them to child fields instead.
8286
- - Hidden fields cannot be inside rows - place them at page/form level.
8287
8370
  - Pages cannot be nested - ALL top-level fields must be pages if using multi-page mode.`
8288
8371
  },
8289
8372
  "field-placement": {
8290
8373
  brief: `**Multi-page:** ALL root fields must be pages. Hidden fields go INSIDE first page.
8291
- **Rows:** NO hidden fields, NO nested rows, NO pages
8374
+ **Rows:** NO pages
8292
8375
  **Groups:** NO nested groups, NO pages`,
8293
8376
  full: `# Field Placement Rules
8294
8377
 
@@ -1 +1 @@
1
- {"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../../../packages/dynamic-form-mcp/src/registry/field-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,WAAW,EAAE,aAAa,EAqqBtC,CAAC"}
1
+ {"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../../../packages/dynamic-form-mcp/src/registry/field-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,WAAW,EAAE,aAAa,EA6qBtC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ui-adapters.d.ts","sourceRoot":"","sources":["../../../../../packages/dynamic-form-mcp/src/registry/ui-adapters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,WAAW,EAAE,aAAa,EA4WtC,CAAC"}
1
+ {"version":3,"file":"ui-adapters.d.ts","sourceRoot":"","sources":["../../../../../packages/dynamic-form-mcp/src/registry/ui-adapters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,WAAW,EAAE,aAAa,EA0VtC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"lookup-topics.d.ts","sourceRoot":"","sources":["../../../../../../packages/dynamic-form-mcp/src/tools/data/lookup-topics.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAq5ElE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiDrD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA4IhD,CAAC"}
1
+ {"version":3,"file":"lookup-topics.d.ts","sourceRoot":"","sources":["../../../../../../packages/dynamic-form-mcp/src/tools/data/lookup-topics.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAq/ElE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiDrD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA4IhD,CAAC"}