@ng-forge/dynamic-forms 0.5.0 → 0.5.2

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.
@@ -1,11 +1,11 @@
1
1
  import * as rxjs from 'rxjs';
2
2
  import { Observable } from 'rxjs';
3
+ import * as _angular_core from '@angular/core';
4
+ import { Signal, Injector, WritableSignal, InjectionToken, Type, ResourceRef, Provider, EnvironmentProviders, PipeTransform } from '@angular/core';
3
5
  import * as _angular_forms_signals from '@angular/forms/signals';
4
6
  import { FieldTree, FieldContext, ValidationError as ValidationError$1, TreeValidationResult, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
5
7
  import * as _angular_forms from '@angular/forms';
6
8
  import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
7
- import * as _angular_core from '@angular/core';
8
- import { Signal, Injector, WritableSignal, InjectionToken, Type, ResourceRef, OnDestroy, InputSignal, Provider, EnvironmentProviders, PipeTransform } from '@angular/core';
9
9
  import { FormSchema } from '@ng-forge/dynamic-forms/schema';
10
10
 
11
11
  type WithInputSignals<T> = {
@@ -578,51 +578,91 @@ type GroupAllowedChildren = LeafFieldTypes | RowField;
578
578
  */
579
579
  type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField;
580
580
 
581
+ /**
582
+ * An array item template is an array of allowed children that defines one OBJECT array item's fields.
583
+ * Used for object arrays where each item is `{ key1: value1, key2: value2, ... }`.
584
+ */
585
+ type ArrayItemTemplate$1 = readonly ArrayAllowedChildren[];
586
+ /**
587
+ * An array item definition can be either:
588
+ * - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
589
+ * - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
590
+ *
591
+ * This allows support for:
592
+ * - Primitive arrays: `['tag1', 'tag2']`
593
+ * - Object arrays: `[{ name: 'Alice', email: '...' }]`
594
+ * - Heterogeneous arrays: `[{ value: 'x' }, 'y']` (mixed primitives and objects)
595
+ */
596
+ type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate$1;
581
597
  /**
582
598
  * Array field interface for creating dynamic field collections that map to array values.
583
599
  *
584
600
  * Key concepts:
585
- * - The `fields` array defines the TEMPLATE (single field definition), not instances
586
- * - Array items are created/removed dynamically at runtime via event bus
587
- * - For primitive arrays: Use a leaf field as template → `['value1', 'value2']`
588
- * - For object arrays: Use a group field as template → `[{key1: val1}, {key2: val2}]`
601
+ * - The outer `fields` array defines INITIAL ITEMS (each element is one array item to render)
602
+ * - Each element can be either:
603
+ * - A single FieldDef (primitive item) - the field's value is extracted directly
604
+ * - An array of FieldDefs (object item) - fields are merged into an object
605
+ * - Items can have different field configurations (heterogeneous arrays supported)
606
+ * - Empty `fields: []` means no initial items - user adds via buttons with explicit templates
607
+ * - Initial values are declared via `value` property on each field definition
589
608
  *
590
609
  * @example
591
- * // Primitive array (flat values)
610
+ * // Primitive array: ['angular', 'typescript']
592
611
  * {
593
612
  * key: 'tags',
594
613
  * type: 'array',
595
- * fields: [{ key: 'tag', type: 'input' }]
614
+ * fields: [
615
+ * { key: 'tag', type: 'input', value: 'angular' }, // Single field = primitive
616
+ * { key: 'tag', type: 'input', value: 'typescript' },
617
+ * ]
596
618
  * }
597
- * // Creates: { tags: ['tag1', 'tag2', 'tag3'] }
598
619
  *
599
620
  * @example
600
- * // Object array (nested groups)
621
+ * // Object array: [{ name: 'Alice', email: '...' }]
601
622
  * {
602
623
  * key: 'contacts',
603
624
  * type: 'array',
604
- * fields: [{
605
- * type: 'group',
606
- * fields: [
607
- * { key: 'name', type: 'input' },
608
- * { key: 'email', type: 'input' }
609
- * ]
610
- * }]
625
+ * fields: [
626
+ * [ // Array = object
627
+ * { key: 'name', type: 'input', value: 'Alice' },
628
+ * { key: 'email', type: 'input', value: 'alice@example.com' }
629
+ * ],
630
+ * ]
631
+ * }
632
+ *
633
+ * @example
634
+ * // Heterogeneous array: [{ label: 'Structured' }, 'Simple']
635
+ * {
636
+ * key: 'items',
637
+ * type: 'array',
638
+ * fields: [
639
+ * [{ key: 'label', type: 'input', value: 'Structured' }], // Object item
640
+ * { key: 'value', type: 'input', value: 'Simple' }, // Primitive item
641
+ * ]
642
+ * }
643
+ *
644
+ * @example
645
+ * // Empty array (add items via buttons)
646
+ * {
647
+ * key: 'tags',
648
+ * type: 'array',
649
+ * fields: [] // No initial items - user adds via button with template
611
650
  * }
612
- * // Creates: { contacts: [{name: '', email: ''}, {name: '', email: ''}] }
613
651
  *
614
652
  * Nesting constraints: Arrays can contain rows, leaf fields, or groups (for object arrays),
615
653
  * but NOT pages or other arrays. Runtime validation enforces these rules.
616
654
  *
617
655
  * Note: Arrays are container fields and do not support `meta` since they have no native form element.
618
656
  */
619
- interface ArrayField<TFields extends readonly ArrayAllowedChildren[] = readonly ArrayAllowedChildren[]> extends FieldDef<never> {
657
+ interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never> {
620
658
  type: 'array';
621
659
  /**
622
- * Template field definition(s) that will be cloned for each array item.
623
- * Typically contains a single field definition:
624
- * - For flat arrays: A leaf field (input, select, checkbox, etc.)
625
- * - For object arrays: A group field containing multiple child fields
660
+ * Array of item definitions. Each element defines one array item:
661
+ * - Single FieldDef = primitive item (field's value is extracted directly)
662
+ * - Array of FieldDefs = object item (fields are merged into an object)
663
+ * - Empty array `[]` = no initial items (add via buttons with explicit templates)
664
+ * - Items can have different configurations (heterogeneous arrays supported)
665
+ * - Initial values are set via `value` property on each field definition
626
666
  */
627
667
  readonly fields: TFields;
628
668
  /** Array fields do not have a label property **/
@@ -631,7 +671,9 @@ interface ArrayField<TFields extends readonly ArrayAllowedChildren[] = readonly
631
671
  readonly meta?: never;
632
672
  }
633
673
  /**
634
- * Type guard for ArrayField with proper type narrowing
674
+ * Type guard for ArrayField with proper type narrowing.
675
+ * Validates that the field is an array type with a fields property that is an array.
676
+ * Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
635
677
  */
636
678
  declare function isArrayField(field: FieldDef<any>): field is ArrayField;
637
679
 
@@ -876,6 +918,10 @@ type StateLogicConfig = ImmediateStateLogicConfig | DebouncedStateLogicConfig;
876
918
  /**
877
919
  * Base configuration for value derivation logic.
878
920
  *
921
+ * All derivations target the field they are defined on (self-targeting).
922
+ * This simplifies configuration by removing the need for `targetField` -
923
+ * derivation logic is placed on the field that should receive the computed value.
924
+ *
879
925
  * @internal
880
926
  */
881
927
  interface BaseDerivationLogicConfig {
@@ -892,29 +938,17 @@ interface BaseDerivationLogicConfig {
892
938
  * @example
893
939
  * ```typescript
894
940
  * {
895
- * type: 'derivation',
896
- * debugName: 'Calculate line total',
897
- * targetField: '$.lineTotal',
898
- * expression: 'formValue.quantity * formValue.unitPrice'
941
+ * key: 'lineTotal',
942
+ * type: 'input',
943
+ * logic: [{
944
+ * type: 'derivation',
945
+ * debugName: 'Calculate line total',
946
+ * expression: 'formValue.quantity * formValue.unitPrice'
947
+ * }]
899
948
  * }
900
949
  * ```
901
950
  */
902
951
  debugName?: string;
903
- /**
904
- * The field whose value will be modified.
905
- *
906
- * Can be:
907
- * - Absolute path: 'fieldName' or 'nested.field.path'
908
- * - Relative path for arrays: '$.siblingField' (same index as source)
909
- *
910
- * @example
911
- * ```typescript
912
- * targetField: 'phonePrefix' // Absolute path
913
- * targetField: 'address.country' // Nested path
914
- * targetField: '$.lineTotal' // Relative path (array sibling)
915
- * ```
916
- */
917
- targetField: string;
918
952
  /**
919
953
  * Condition that determines when this derivation applies.
920
954
  *
@@ -942,7 +976,7 @@ interface BaseDerivationLogicConfig {
942
976
  */
943
977
  condition?: ConditionalExpression | boolean;
944
978
  /**
945
- * Static value to set on the target field.
979
+ * Static value to set on this field.
946
980
  *
947
981
  * Use when the derived value is a constant.
948
982
  * Mutually exclusive with `expression` and `functionName`.
@@ -960,6 +994,7 @@ interface BaseDerivationLogicConfig {
960
994
  * JavaScript expression to evaluate for the derived value.
961
995
  *
962
996
  * Has access to `formValue` object containing all form values.
997
+ * For array fields, `formValue` is scoped to the current array item.
963
998
  * Uses the same secure AST-based parser as other expressions.
964
999
  * Mutually exclusive with `value` and `functionName`.
965
1000
  *
@@ -1001,18 +1036,22 @@ interface BaseDerivationLogicConfig {
1001
1036
  * ```typescript
1002
1037
  * // Only re-evaluate when country changes
1003
1038
  * {
1004
- * type: 'derivation',
1005
- * targetField: 'currency',
1006
- * functionName: 'getCurrencyForCountry',
1007
- * dependsOn: ['country']
1039
+ * key: 'currency',
1040
+ * logic: [{
1041
+ * type: 'derivation',
1042
+ * functionName: 'getCurrencyForCountry',
1043
+ * dependsOn: ['country']
1044
+ * }]
1008
1045
  * }
1009
1046
  *
1010
1047
  * // Override automatic detection for complex expressions
1011
1048
  * {
1012
- * type: 'derivation',
1013
- * targetField: 'total',
1014
- * expression: 'calculateTotal(formValue)',
1015
- * dependsOn: ['quantity', 'unitPrice', 'discount']
1049
+ * key: 'total',
1050
+ * logic: [{
1051
+ * type: 'derivation',
1052
+ * expression: 'calculateTotal(formValue)',
1053
+ * dependsOn: ['quantity', 'unitPrice', 'discount']
1054
+ * }]
1016
1055
  * }
1017
1056
  * ```
1018
1057
  */
@@ -1056,68 +1095,253 @@ interface DebouncedDerivationLogicConfig extends BaseDerivationLogicConfig {
1056
1095
  * Configuration for value derivation logic.
1057
1096
  *
1058
1097
  * Enables programmatic value derivation based on conditions.
1059
- * When the condition is met, the target field's value is set
1060
- * using a static value, expression, or custom function.
1098
+ * Derivations are self-targeting: the logic is placed on the field
1099
+ * that should receive the computed value.
1061
1100
  *
1062
1101
  * @example
1063
1102
  * ```typescript
1064
1103
  * // Set phone prefix based on country selection
1065
1104
  * {
1066
- * type: 'derivation',
1067
- * targetField: 'phonePrefix',
1068
- * value: '+1',
1069
- * condition: {
1070
- * type: 'fieldValue',
1071
- * fieldPath: 'country',
1072
- * operator: 'equals',
1073
- * value: 'USA'
1074
- * }
1105
+ * key: 'phonePrefix',
1106
+ * logic: [{
1107
+ * type: 'derivation',
1108
+ * value: '+1',
1109
+ * condition: {
1110
+ * type: 'fieldValue',
1111
+ * fieldPath: 'country',
1112
+ * operator: 'equals',
1113
+ * value: 'USA'
1114
+ * }
1115
+ * }]
1075
1116
  * }
1076
1117
  *
1077
1118
  * // Compute total from quantity and price
1078
1119
  * {
1079
- * type: 'derivation',
1080
- * targetField: 'total',
1081
- * expression: 'formValue.quantity * formValue.unitPrice'
1120
+ * key: 'total',
1121
+ * derivation: 'formValue.quantity * formValue.unitPrice'
1082
1122
  * }
1083
1123
  *
1084
1124
  * // Use custom function for complex logic
1085
1125
  * {
1086
- * type: 'derivation',
1087
- * targetField: 'currency',
1088
- * functionName: 'getCurrencyForCountry'
1126
+ * key: 'currency',
1127
+ * logic: [{
1128
+ * type: 'derivation',
1129
+ * functionName: 'getCurrencyForCountry'
1130
+ * }]
1089
1131
  * }
1090
1132
  *
1091
1133
  * // Self-transform with debounced trigger
1092
1134
  * // (applies after user stops typing)
1093
1135
  * {
1094
- * type: 'derivation',
1095
- * targetField: 'email',
1096
- * expression: 'formValue.email.toLowerCase()',
1097
- * trigger: 'debounced',
1098
- * debounceMs: 500
1136
+ * key: 'email',
1137
+ * logic: [{
1138
+ * type: 'derivation',
1139
+ * expression: 'formValue.email.toLowerCase()',
1140
+ * trigger: 'debounced',
1141
+ * debounceMs: 500
1142
+ * }]
1099
1143
  * }
1100
1144
  *
1101
- * // Relative path for array items
1145
+ * // Array item derivation (formValue is scoped to current item)
1102
1146
  * {
1103
- * type: 'derivation',
1104
- * targetField: '$.lineTotal', // Same array index as source field
1105
- * expression: 'formValue.quantity * formValue.unitPrice'
1147
+ * key: 'lineTotal', // Inside array field
1148
+ * derivation: 'formValue.quantity * formValue.unitPrice'
1106
1149
  * }
1107
1150
  * ```
1108
1151
  *
1109
1152
  * @public
1110
1153
  */
1111
1154
  type DerivationLogicConfig = OnChangeDerivationLogicConfig | DebouncedDerivationLogicConfig;
1155
+ /**
1156
+ * Base configuration for property derivation logic.
1157
+ *
1158
+ * Property derivations compute values for field properties (like `minDate`, `options`,
1159
+ * `label`, `placeholder`) based on form values and external data. Unlike value derivations
1160
+ * which set the field's value, property derivations update component input properties.
1161
+ *
1162
+ * All property derivations are self-targeting: the logic is placed on the field whose
1163
+ * property should be derived.
1164
+ *
1165
+ * @internal
1166
+ */
1167
+ interface BasePropertyDerivationLogicConfig {
1168
+ /**
1169
+ * Logic type identifier for property derivation.
1170
+ */
1171
+ type: 'propertyDerivation';
1172
+ /**
1173
+ * The target property to set on the field component.
1174
+ *
1175
+ * **Depth limit (max 2 levels):** Only simple and single-dot-nested paths are
1176
+ * supported. Paths with 2+ dots (e.g., `'a.b.c'`) will throw a `DynamicFormError`
1177
+ * at runtime. This is an architectural constraint of the override merging strategy.
1178
+ *
1179
+ * Supported formats:
1180
+ * - Simple: `'minDate'`, `'options'`, `'label'`, `'placeholder'`
1181
+ * - Nested (1 dot): `'props.appearance'`, `'meta.autocomplete'`
1182
+ *
1183
+ * **Note:** There is no compile-time validation that the property name matches
1184
+ * an actual input on the field component. Typos will silently write to the
1185
+ * override store with no visible effect. A dev-mode warning is emitted when
1186
+ * the override key doesn't match any existing input property.
1187
+ *
1188
+ * @example
1189
+ * ```typescript
1190
+ * targetProperty: 'minDate' // ✅ Simple property
1191
+ * targetProperty: 'options' // ✅ Simple property
1192
+ * targetProperty: 'props.appearance' // ✅ Single-nested property
1193
+ * targetProperty: 'a.b.c' // ❌ Throws DynamicFormError (too deep)
1194
+ * ```
1195
+ */
1196
+ targetProperty: string;
1197
+ /**
1198
+ * Optional name for this derivation for debugging purposes.
1199
+ *
1200
+ * When provided, this name appears in property derivation debug logs.
1201
+ */
1202
+ debugName?: string;
1203
+ /**
1204
+ * Condition that determines when this property derivation applies.
1205
+ *
1206
+ * Defaults to `true` (always apply).
1207
+ *
1208
+ * Note: FormStateCondition is not supported for property derivations.
1209
+ */
1210
+ condition?: ConditionalExpression | boolean;
1211
+ /**
1212
+ * Static value to set on the target property.
1213
+ *
1214
+ * Mutually exclusive with `expression` and `functionName`.
1215
+ */
1216
+ value?: unknown;
1217
+ /**
1218
+ * JavaScript expression to evaluate for the derived property value.
1219
+ *
1220
+ * Has access to `formValue` object containing all form values.
1221
+ * For array fields, `formValue` is scoped to the current array item.
1222
+ *
1223
+ * Mutually exclusive with `value` and `functionName`.
1224
+ *
1225
+ * @example
1226
+ * ```typescript
1227
+ * expression: 'formValue.startDate'
1228
+ * expression: 'formValue.quantity > 10 ? "bulk" : "standard"'
1229
+ * ```
1230
+ */
1231
+ expression?: string;
1232
+ /**
1233
+ * Name of a registered custom property derivation function.
1234
+ *
1235
+ * Register functions in `customFnConfig.propertyDerivations`.
1236
+ * Mutually exclusive with `value` and `expression`.
1237
+ */
1238
+ functionName?: string;
1239
+ /**
1240
+ * Explicit field dependencies for property derivations.
1241
+ *
1242
+ * When using `functionName`, you can optionally specify which fields
1243
+ * the function depends on. If not provided with `functionName`, defaults
1244
+ * to all fields ('*').
1245
+ * For `expression`, dependencies are automatically extracted.
1246
+ * For `value`, no dependencies are needed (static value).
1247
+ */
1248
+ dependsOn?: string[];
1249
+ }
1250
+ /**
1251
+ * Property derivation that evaluates immediately on change (default).
1252
+ *
1253
+ * @internal
1254
+ */
1255
+ interface OnChangePropertyDerivationLogicConfig extends BasePropertyDerivationLogicConfig {
1256
+ /**
1257
+ * Trigger for immediate evaluation.
1258
+ * @default 'onChange'
1259
+ */
1260
+ trigger?: 'onChange';
1261
+ /** Not allowed for onChange trigger */
1262
+ debounceMs?: never;
1263
+ }
1264
+ /**
1265
+ * Property derivation that evaluates after a debounce period.
1266
+ *
1267
+ * @internal
1268
+ */
1269
+ interface DebouncedPropertyDerivationLogicConfig extends BasePropertyDerivationLogicConfig {
1270
+ /**
1271
+ * Trigger for debounced evaluation.
1272
+ */
1273
+ trigger: 'debounced';
1274
+ /**
1275
+ * Debounce duration in milliseconds.
1276
+ * @default 500
1277
+ */
1278
+ debounceMs?: number;
1279
+ }
1280
+ /**
1281
+ * Configuration for property derivation logic.
1282
+ *
1283
+ * Enables reactive derivation of field properties (like `minDate`, `options`,
1284
+ * `label`, `placeholder`) based on form values and external data.
1285
+ *
1286
+ * Property derivations are self-targeting: the logic is placed on the field
1287
+ * whose property should be derived.
1288
+ *
1289
+ * @example
1290
+ * ```typescript
1291
+ * // Derive endDate's minDate from startDate value
1292
+ * {
1293
+ * key: 'endDate',
1294
+ * type: 'datepicker',
1295
+ * logic: [{
1296
+ * type: 'propertyDerivation',
1297
+ * targetProperty: 'minDate',
1298
+ * expression: 'formValue.startDate'
1299
+ * }]
1300
+ * }
1301
+ *
1302
+ * // Derive options from another field's value
1303
+ * {
1304
+ * key: 'city',
1305
+ * type: 'select',
1306
+ * logic: [{
1307
+ * type: 'propertyDerivation',
1308
+ * targetProperty: 'options',
1309
+ * functionName: 'getCitiesForCountry',
1310
+ * dependsOn: ['country']
1311
+ * }]
1312
+ * }
1313
+ *
1314
+ * // Derive label conditionally
1315
+ * {
1316
+ * key: 'phone',
1317
+ * type: 'input',
1318
+ * logic: [{
1319
+ * type: 'propertyDerivation',
1320
+ * targetProperty: 'label',
1321
+ * value: 'Mobile Phone',
1322
+ * condition: {
1323
+ * type: 'fieldValue',
1324
+ * fieldPath: 'contactType',
1325
+ * operator: 'equals',
1326
+ * value: 'mobile'
1327
+ * }
1328
+ * }]
1329
+ * }
1330
+ * ```
1331
+ *
1332
+ * @public
1333
+ */
1334
+ type PropertyDerivationLogicConfig = OnChangePropertyDerivationLogicConfig | DebouncedPropertyDerivationLogicConfig;
1112
1335
  /**
1113
1336
  * Union type for all logic configurations.
1114
1337
  *
1115
1338
  * - `StateLogicConfig`: For field state changes (hidden, readonly, disabled, required)
1116
1339
  * - `DerivationLogicConfig`: For value derivation
1340
+ * - `PropertyDerivationLogicConfig`: For property derivation
1117
1341
  *
1118
1342
  * @public
1119
1343
  */
1120
- type LogicConfig = StateLogicConfig | DerivationLogicConfig;
1344
+ type LogicConfig = StateLogicConfig | DerivationLogicConfig | PropertyDerivationLogicConfig;
1121
1345
  /**
1122
1346
  * Log level for derivation debug output.
1123
1347
  *
@@ -1137,71 +1361,306 @@ type DerivationLogLevel = 'none' | 'summary' | 'verbose';
1137
1361
  * @public
1138
1362
  */
1139
1363
  declare function isFormStateCondition(condition: StateLogicConfig['condition'] | DerivationLogicConfig['condition']): condition is FormStateCondition;
1140
-
1141
- /**
1142
- * Text element type for rendering different HTML text elements
1143
- */
1144
- type TextElementType = 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span';
1145
1364
  /**
1146
- * Properties for text field configuration
1365
+ * Type guard to check if a logic config is a PropertyDerivationLogicConfig.
1366
+ *
1367
+ * @param config - The logic config to check
1368
+ * @returns true if the config is for property derivation
1369
+ *
1370
+ * @public
1147
1371
  */
1148
- type TextProps = {
1149
- /** The HTML element type to render */
1150
- elementType: TextElementType;
1151
- };
1372
+ declare function isPropertyDerivationLogicConfig(config: LogicConfig): config is PropertyDerivationLogicConfig;
1373
+
1152
1374
  /**
1153
- * Text field definition for displaying translatable text content
1375
+ * Interface for dynamic forms logger implementations.
1376
+ * Allows custom logging integrations (Sentry, DataDog, etc.)
1154
1377
  */
1155
- interface TextField extends FieldDef<TextProps> {
1156
- type: 'text';
1157
- /** Logic rules for conditional visibility (hidden, readonly, disabled) */
1158
- readonly logic?: LogicConfig[];
1378
+ interface Logger {
1379
+ /**
1380
+ * Log a debug message.
1381
+ * Use for detailed diagnostic information during development.
1382
+ */
1383
+ debug(message: string, ...args: unknown[]): void;
1384
+ /**
1385
+ * Log an info message.
1386
+ * Use for general operational information.
1387
+ */
1388
+ info(message: string, ...args: unknown[]): void;
1389
+ /**
1390
+ * Log a warning message.
1391
+ * Use for potentially problematic situations that don't prevent operation.
1392
+ */
1393
+ warn(message: string, ...args: unknown[]): void;
1394
+ /**
1395
+ * Log an error message.
1396
+ * Use for error conditions that may affect functionality.
1397
+ */
1398
+ error(message: string, ...args: unknown[]): void;
1159
1399
  }
1160
1400
 
1161
1401
  /**
1162
- * Container fields registry - augment this interface to add custom container fields
1402
+ * Context for resolving button disabled state.
1163
1403
  *
1164
- * @example
1165
- * ```typescript
1166
- * declare module '@ng-forge/dynamic-forms' {
1167
- * interface FieldRegistryContainers {
1168
- * 'my-container': MyContainerFieldDef;
1169
- * }
1170
- * }
1171
- * ```
1404
+ * @public
1172
1405
  */
1173
- interface FieldRegistryContainers {
1174
- page: PageField;
1175
- row: RowField;
1176
- group: GroupField;
1177
- array: ArrayField;
1406
+ interface ButtonLogicContext {
1407
+ /** The form's FieldTree instance (supports both string and number keys for array indices) */
1408
+ form: FieldTree<unknown, string | number>;
1409
+ /** Form-level options */
1410
+ formOptions?: FormOptions;
1411
+ /** Field-level logic array (if provided) */
1412
+ fieldLogic?: LogicConfig[];
1413
+ /** Explicit disabled state from field definition */
1414
+ explicitlyDisabled?: boolean;
1415
+ /** Current page validity signal (for paged forms) */
1416
+ currentPageValid?: Signal<boolean>;
1417
+ /** Current form value for evaluating conditional expressions */
1418
+ formValue?: unknown;
1419
+ /** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
1420
+ logger?: Logger;
1178
1421
  }
1179
1422
  /**
1180
- * Leaf fields registry - augment this interface to add custom leaf fields
1423
+ * Allowed logic types for non-form-bound elements (buttons, text fields, etc.).
1424
+ * These elements only support hidden and disabled states - not readonly or required
1425
+ * since they don't participate in form validation.
1181
1426
  *
1182
- * @example
1183
- * ```typescript
1184
- * declare module '@ng-forge/dynamic-forms' {
1185
- * interface FieldRegistryLeaves {
1186
- * 'my-input': MyInputFieldDef;
1187
- * }
1188
- * }
1189
- * ```
1427
+ * @public
1190
1428
  */
1191
- interface FieldRegistryLeaves {
1192
- text: TextField;
1193
- hidden: HiddenField;
1194
- }
1429
+ type NonFieldLogicType = 'hidden' | 'disabled';
1195
1430
  /**
1196
- * Global interface for dynamic form field definitions with categorization
1197
- * This interface combines containers and leaves from their respective registries
1431
+ * Logic config restricted to types valid for non-form-bound elements.
1432
+ * This is a subset of LogicConfig that only includes hidden and disabled types.
1198
1433
  *
1199
- * Container fields: Layout fields that contain other fields (page, row, group, array)
1200
- * Leaf fields: Fields that can hold values or display content (input, text, etc.)
1434
+ * @public
1435
+ */
1436
+ type NonFieldLogicConfig = LogicConfig & {
1437
+ type: NonFieldLogicType;
1438
+ };
1439
+ /**
1440
+ * Context for resolving state (hidden/disabled) for non-form-bound elements.
1201
1441
  *
1202
- * To add custom fields, augment FieldRegistryContainers or FieldRegistryLeaves
1442
+ * This is a generalized context that can be used by any field mapper (buttons, text fields,
1443
+ * or any non-form-bound elements) to evaluate logic from a `logic` array.
1444
+ *
1445
+ * Note: While this context accepts the full `LogicConfig[]` for flexibility, the resolver
1446
+ * functions only process hidden and disabled types. Other logic types (readonly, required,
1447
+ * derivation) are ignored for non-form-bound elements.
1448
+ *
1449
+ * @public
1203
1450
  */
1204
- interface DynamicFormFieldRegistry {
1451
+ interface NonFieldLogicContext {
1452
+ /** The form's FieldTree instance (supports both string and number keys for array indices) */
1453
+ form: FieldTree<unknown, string | number>;
1454
+ /**
1455
+ * Field-level logic array containing conditions.
1456
+ * Accepts full LogicConfig[] for compatibility, but only hidden and disabled types are processed.
1457
+ */
1458
+ fieldLogic?: LogicConfig[];
1459
+ /** Explicit state from field definition (e.g., `hidden: true` or `disabled: true`) */
1460
+ explicitValue?: boolean;
1461
+ /** Current form value for evaluating conditional expressions */
1462
+ formValue?: unknown;
1463
+ /** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
1464
+ logger?: Logger;
1465
+ }
1466
+ /**
1467
+ * Resolves the disabled state for a submit button.
1468
+ *
1469
+ * The disabled state is determined by (in order of precedence):
1470
+ * 1. Explicit `disabled: true` on the field definition
1471
+ * 2. Field-level `logic` array (if present, overrides form-level defaults)
1472
+ * 3. Form-level `options.submitButton` defaults
1473
+ *
1474
+ * @param ctx - The button logic context
1475
+ * @returns A computed signal that returns true when the button should be disabled
1476
+ *
1477
+ * @example
1478
+ * ```typescript
1479
+ * const disabled = resolveSubmitButtonDisabled({
1480
+ * form: formInstance,
1481
+ * formOptions: config.options,
1482
+ * fieldLogic: buttonField.logic,
1483
+ * explicitlyDisabled: buttonField.disabled,
1484
+ * });
1485
+ *
1486
+ * // Use in template
1487
+ * <button [disabled]="disabled()">Submit</button>
1488
+ * ```
1489
+ *
1490
+ * @public
1491
+ */
1492
+ declare function resolveSubmitButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
1493
+ /**
1494
+ * Resolves the disabled state for a next page button.
1495
+ *
1496
+ * The disabled state is determined by (in order of precedence):
1497
+ * 1. Explicit `disabled: true` on the field definition
1498
+ * 2. Field-level `logic` array (if present, overrides form-level defaults)
1499
+ * 3. Form-level `options.nextButton` defaults
1500
+ *
1501
+ * @param ctx - The button logic context
1502
+ * @returns A computed signal that returns true when the button should be disabled
1503
+ *
1504
+ * @example
1505
+ * ```typescript
1506
+ * const disabled = resolveNextButtonDisabled({
1507
+ * form: formInstance,
1508
+ * formOptions: config.options,
1509
+ * fieldLogic: buttonField.logic,
1510
+ * explicitlyDisabled: buttonField.disabled,
1511
+ * currentPageValid: pageOrchestrator.currentPageValid,
1512
+ * });
1513
+ * ```
1514
+ *
1515
+ * @public
1516
+ */
1517
+ declare function resolveNextButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
1518
+ /**
1519
+ * Resolves the hidden state for non-form-bound elements (buttons, text fields, etc.).
1520
+ *
1521
+ * This is a generalized function that can be used by any field mapper (buttons, text fields,
1522
+ * or any non-form-bound elements) to evaluate hidden logic. It's particularly useful for
1523
+ * elements that don't have a FieldTree but still need conditional visibility.
1524
+ *
1525
+ * The hidden state is determined by (in order of precedence):
1526
+ * 1. Explicit `hidden: true` on the field definition
1527
+ * 2. Field-level `logic` array with `type: 'hidden'` conditions
1528
+ *
1529
+ * @param ctx - The context containing form, logic array, and explicit hidden state
1530
+ * @returns A computed signal that returns true when the element should be hidden
1531
+ *
1532
+ * @example
1533
+ * ```typescript
1534
+ * // In an array button mapper
1535
+ * const hiddenSignal = resolveNonFieldHidden({
1536
+ * form: rootFormRegistry.getRootForm()!,
1537
+ * fieldLogic: buttonField.logic,
1538
+ * explicitValue: buttonField.hidden,
1539
+ * formValue: rootFormRegistry.getFormValue(),
1540
+ * });
1541
+ *
1542
+ * return computed(() => ({
1543
+ * ...baseInputs,
1544
+ * hidden: hiddenSignal(),
1545
+ * }));
1546
+ * ```
1547
+ *
1548
+ * @example
1549
+ * ```typescript
1550
+ * // In a text field mapper
1551
+ * const hiddenSignal = resolveNonFieldHidden({
1552
+ * form: rootFormRegistry.getRootForm()!,
1553
+ * fieldLogic: textField.logic,
1554
+ * explicitValue: textField.hidden,
1555
+ * formValue: rootFormRegistry.getFormValue(),
1556
+ * });
1557
+ * ```
1558
+ *
1559
+ * @public
1560
+ */
1561
+ declare function resolveNonFieldHidden(ctx: NonFieldLogicContext): Signal<boolean>;
1562
+ /**
1563
+ * Resolves the disabled state for non-form-bound elements (buttons, text fields, etc.).
1564
+ *
1565
+ * This is a generalized function that can be used by any field mapper to evaluate
1566
+ * disabled logic. It handles elements that don't have form-level disabled defaults
1567
+ * (unlike submit/next buttons which have form-level config).
1568
+ *
1569
+ * The disabled state is determined by (in order of precedence):
1570
+ * 1. Explicit `disabled: true` on the field definition
1571
+ * 2. Field-level `logic` array with `type: 'disabled'` conditions
1572
+ *
1573
+ * @param ctx - The context containing form, logic array, and explicit disabled state
1574
+ * @returns A computed signal that returns true when the element should be disabled
1575
+ *
1576
+ * @example
1577
+ * ```typescript
1578
+ * // In an array button mapper
1579
+ * const disabledSignal = resolveNonFieldDisabled({
1580
+ * form: rootFormRegistry.getRootForm()!,
1581
+ * fieldLogic: buttonField.logic,
1582
+ * explicitValue: buttonField.disabled,
1583
+ * formValue: rootFormRegistry.getFormValue(),
1584
+ * });
1585
+ *
1586
+ * return computed(() => ({
1587
+ * ...baseInputs,
1588
+ * disabled: disabledSignal(),
1589
+ * }));
1590
+ * ```
1591
+ *
1592
+ * @public
1593
+ */
1594
+ declare function resolveNonFieldDisabled(ctx: NonFieldLogicContext): Signal<boolean>;
1595
+
1596
+ /**
1597
+ * Text element type for rendering different HTML text elements
1598
+ */
1599
+ type TextElementType = 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span';
1600
+ /**
1601
+ * Properties for text field configuration
1602
+ */
1603
+ type TextProps = {
1604
+ /** The HTML element type to render */
1605
+ elementType: TextElementType;
1606
+ };
1607
+ /**
1608
+ * Text field definition for displaying translatable text content
1609
+ */
1610
+ interface TextField extends FieldDef<TextProps> {
1611
+ type: 'text';
1612
+ /**
1613
+ * Logic rules for conditional visibility.
1614
+ * Text fields only support 'hidden' logic type since they are display-only
1615
+ * and don't participate in form validation (no disabled/readonly/required).
1616
+ */
1617
+ readonly logic?: NonFieldLogicConfig[];
1618
+ }
1619
+
1620
+ /**
1621
+ * Container fields registry - augment this interface to add custom container fields
1622
+ *
1623
+ * @example
1624
+ * ```typescript
1625
+ * declare module '@ng-forge/dynamic-forms' {
1626
+ * interface FieldRegistryContainers {
1627
+ * 'my-container': MyContainerFieldDef;
1628
+ * }
1629
+ * }
1630
+ * ```
1631
+ */
1632
+ interface FieldRegistryContainers {
1633
+ page: PageField;
1634
+ row: RowField;
1635
+ group: GroupField;
1636
+ array: ArrayField;
1637
+ }
1638
+ /**
1639
+ * Leaf fields registry - augment this interface to add custom leaf fields
1640
+ *
1641
+ * @example
1642
+ * ```typescript
1643
+ * declare module '@ng-forge/dynamic-forms' {
1644
+ * interface FieldRegistryLeaves {
1645
+ * 'my-input': MyInputFieldDef;
1646
+ * }
1647
+ * }
1648
+ * ```
1649
+ */
1650
+ interface FieldRegistryLeaves {
1651
+ text: TextField;
1652
+ hidden: HiddenField;
1653
+ }
1654
+ /**
1655
+ * Global interface for dynamic form field definitions with categorization
1656
+ * This interface combines containers and leaves from their respective registries
1657
+ *
1658
+ * Container fields: Layout fields that contain other fields (page, row, group, array)
1659
+ * Leaf fields: Fields that can hold values or display content (input, text, etc.)
1660
+ *
1661
+ * To add custom fields, augment FieldRegistryContainers or FieldRegistryLeaves
1662
+ */
1663
+ interface DynamicFormFieldRegistry {
1205
1664
  /**
1206
1665
  * Container fields that hold other fields (no value, have children)
1207
1666
  */
@@ -1315,8 +1774,13 @@ type InferValueType<T, V> = T extends {
1315
1774
  type: 'number';
1316
1775
  };
1317
1776
  } ? number : Widen<V>;
1318
- /** Make type optional if field is not required */
1777
+ /**
1778
+ * Make type optional if field is not required.
1779
+ * Hidden fields are never optional since they require a value property.
1780
+ */
1319
1781
  type MaybeOptional<T, V> = T extends {
1782
+ type: 'hidden';
1783
+ } ? V : T extends {
1320
1784
  required: true;
1321
1785
  } ? V : V | undefined;
1322
1786
  /**
@@ -1333,10 +1797,18 @@ type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string
1333
1797
  [P in K]: InferFormValueWithDepth<F, Depth[D]>;
1334
1798
  } : {
1335
1799
  [P in K]: Record<string, unknown>;
1800
+ } : never : T extends {
1801
+ type: 'array';
1802
+ key: infer K;
1803
+ fields: infer F;
1804
+ } ? K extends string ? F extends RegisteredFieldTypes[] ? {
1805
+ [P in K]: InferFormValueWithDepth<F, Depth[D]>[];
1806
+ } : {
1807
+ [P in K]: unknown[];
1336
1808
  } : never : T extends {
1337
1809
  type: 'text';
1338
1810
  } ? never : T extends {
1339
- type: 'submit' | 'button';
1811
+ type: 'submit' | 'button' | 'next' | 'previous' | 'addArrayItem' | 'removeArrayItem';
1340
1812
  } ? never : T extends {
1341
1813
  key: infer K;
1342
1814
  value: infer V;
@@ -1759,33 +2231,6 @@ interface HttpCustomValidator<TValue = unknown, TResult = unknown> {
1759
2231
  readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
1760
2232
  }
1761
2233
 
1762
- /**
1763
- * Interface for dynamic forms logger implementations.
1764
- * Allows custom logging integrations (Sentry, DataDog, etc.)
1765
- */
1766
- interface Logger {
1767
- /**
1768
- * Log a debug message.
1769
- * Use for detailed diagnostic information during development.
1770
- */
1771
- debug(message: string, ...args: unknown[]): void;
1772
- /**
1773
- * Log an info message.
1774
- * Use for general operational information.
1775
- */
1776
- info(message: string, ...args: unknown[]): void;
1777
- /**
1778
- * Log a warning message.
1779
- * Use for potentially problematic situations that don't prevent operation.
1780
- */
1781
- warn(message: string, ...args: unknown[]): void;
1782
- /**
1783
- * Log an error message.
1784
- * Use for error conditions that may affect functionality.
1785
- */
1786
- error(message: string, ...args: unknown[]): void;
1787
- }
1788
-
1789
2234
  interface EvaluationContext<TValue = unknown> {
1790
2235
  /** Current field value */
1791
2236
  fieldValue: TValue;
@@ -1813,13 +2258,13 @@ interface EvaluationContext<TValue = unknown> {
1813
2258
  *
1814
2259
  * @example
1815
2260
  * ```typescript
1816
- * // In an array item derivation (e.g., lineItems.$.lineTotal):
2261
+ * // In an array item derivation (on the lineTotal field inside lineItems array):
1817
2262
  * {
1818
- * type: 'derivation',
1819
- * targetField: '$.lineTotal',
2263
+ * key: 'lineTotal',
2264
+ * type: 'input',
1820
2265
  * // formValue = current array item { quantity: 2, unitPrice: 50 }
1821
2266
  * // rootFormValue = entire form { globalDiscount: 0.1, lineItems: [...] }
1822
- * expression: 'formValue.quantity * formValue.unitPrice * (1 - rootFormValue.globalDiscount)'
2267
+ * derivation: 'formValue.quantity * formValue.unitPrice * (1 - rootFormValue.globalDiscount)'
1823
2268
  * }
1824
2269
  * ```
1825
2270
  *
@@ -1835,6 +2280,28 @@ interface EvaluationContext<TValue = unknown> {
1835
2280
  * Path to the array field when inside an array derivation.
1836
2281
  */
1837
2282
  arrayPath?: string;
2283
+ /**
2284
+ * External data signals resolved to their current values.
2285
+ *
2286
+ * This allows forms to reference data from outside the form context
2287
+ * in conditional logic, derivations, and other expressions.
2288
+ *
2289
+ * @example
2290
+ * ```typescript
2291
+ * // In form config:
2292
+ * externalData: {
2293
+ * userRole: computed(() => this.userService.currentRole()),
2294
+ * permissions: computed(() => this.authService.permissions()),
2295
+ * }
2296
+ *
2297
+ * // In JavaScript expression:
2298
+ * condition: {
2299
+ * type: 'javascript',
2300
+ * expression: "externalData.userRole === 'admin'"
2301
+ * }
2302
+ * ```
2303
+ */
2304
+ externalData?: Record<string, unknown>;
1838
2305
  /** Allow additional properties for flexible expression evaluation */
1839
2306
  [key: string]: unknown;
1840
2307
  }
@@ -2202,6 +2669,54 @@ interface FormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = Reg
2202
2669
  * ```
2203
2670
  */
2204
2671
  defaultProps?: TProps;
2672
+ /**
2673
+ * External data signals available to conditional logic and derivations.
2674
+ *
2675
+ * Provides a way to inject external application state into form expressions.
2676
+ * Each property is a Signal that will be unwrapped and made available in the
2677
+ * `EvaluationContext` under `externalData`.
2678
+ *
2679
+ * The signals are read reactively in logic functions (when/readonly/disabled)
2680
+ * so changes to the external data will trigger re-evaluation of conditions.
2681
+ *
2682
+ * @example
2683
+ * ```typescript
2684
+ * const config: FormConfig = {
2685
+ * externalData: {
2686
+ * userRole: computed(() => this.authService.currentRole()),
2687
+ * permissions: computed(() => this.authService.permissions()),
2688
+ * featureFlags: computed(() => this.featureFlagService.flags()),
2689
+ * },
2690
+ * fields: [
2691
+ * {
2692
+ * type: 'input',
2693
+ * key: 'adminField',
2694
+ * label: 'Admin Only Field',
2695
+ * logic: [{
2696
+ * effect: 'show',
2697
+ * condition: {
2698
+ * type: 'javascript',
2699
+ * expression: "externalData.userRole === 'admin'"
2700
+ * }
2701
+ * }]
2702
+ * },
2703
+ * {
2704
+ * type: 'select',
2705
+ * key: 'advancedOption',
2706
+ * label: 'Advanced Option',
2707
+ * logic: [{
2708
+ * effect: 'show',
2709
+ * condition: {
2710
+ * type: 'javascript',
2711
+ * expression: "externalData.featureFlags.advancedMode === true"
2712
+ * }
2713
+ * }]
2714
+ * }
2715
+ * ]
2716
+ * };
2717
+ * ```
2718
+ */
2719
+ externalData?: Record<string, Signal<unknown>>;
2205
2720
  }
2206
2721
  /**
2207
2722
  * Signal forms adapter configuration for advanced form behavior.
@@ -2322,20 +2837,61 @@ interface CustomFnConfig {
2322
2837
  *
2323
2838
  * Field configuration using a derivation function:
2324
2839
  * ```typescript
2840
+ * // Derivation is defined on the target field (currency), not the source field (country)
2325
2841
  * {
2326
- * key: 'country',
2842
+ * key: 'currency',
2327
2843
  * type: 'select',
2328
2844
  * logic: [
2329
2845
  * {
2330
2846
  * type: 'derivation',
2331
- * targetField: 'currency',
2332
- * functionName: 'getCurrencyForCountry'
2847
+ * functionName: 'getCurrencyForCountry',
2848
+ * dependsOn: ['country']
2333
2849
  * }
2334
2850
  * ]
2335
2851
  * }
2336
2852
  * ```
2337
2853
  */
2338
2854
  derivations?: Record<string, CustomFunction>;
2855
+ /**
2856
+ * Custom property derivation functions for reactive property updates.
2857
+ *
2858
+ * These functions compute derived values for field properties (like `minDate`,
2859
+ * `options`, `label`, `placeholder`) and are called when a
2860
+ * `PropertyDerivationLogicConfig` references them by `functionName`.
2861
+ *
2862
+ * Property derivation functions:
2863
+ * - Receive an `EvaluationContext` with access to `formValue`
2864
+ * - Return the value to set on the target property
2865
+ * - Are called reactively when dependencies change
2866
+ *
2867
+ * @example
2868
+ * ```typescript
2869
+ * propertyDerivations: {
2870
+ * getCitiesForCountry: (context) => {
2871
+ * const countryCities: Record<string, Array<{ label: string; value: string }>> = {
2872
+ * 'USA': [{ label: 'New York', value: 'ny' }, { label: 'LA', value: 'la' }],
2873
+ * 'Germany': [{ label: 'Berlin', value: 'berlin' }, { label: 'Munich', value: 'munich' }],
2874
+ * };
2875
+ * return countryCities[context.formValue.country as string] ?? [];
2876
+ * },
2877
+ * }
2878
+ * ```
2879
+ *
2880
+ * Field configuration using a property derivation function:
2881
+ * ```typescript
2882
+ * {
2883
+ * key: 'city',
2884
+ * type: 'select',
2885
+ * logic: [{
2886
+ * type: 'propertyDerivation',
2887
+ * targetProperty: 'options',
2888
+ * functionName: 'getCitiesForCountry',
2889
+ * dependsOn: ['country']
2890
+ * }]
2891
+ * }
2892
+ * ```
2893
+ */
2894
+ propertyDerivations?: Record<string, CustomFunction>;
2339
2895
  /**
2340
2896
  * Custom validators using Angular's public FieldContext API
2341
2897
  *
@@ -2594,17 +3150,46 @@ interface FormOptions {
2594
3150
  * ```
2595
3151
  */
2596
3152
  nextButton?: NextButtonOptions;
2597
- }
2598
- /**
2599
- * Options for controlling submit button disabled behavior.
2600
- *
2601
- * @public
2602
- */
2603
- interface SubmitButtonOptions {
2604
3153
  /**
2605
- * Disable submit button when the form is invalid.
3154
+ * Whether to attach the current form value to all events dispatched through the EventBus.
2606
3155
  *
2607
- * @default true
3156
+ * This per-form setting overrides the global `withEventFormValue()` feature:
3157
+ * - `true` - Enable form value emission for this form (even if globally disabled)
3158
+ * - `false` - Disable form value emission for this form (even if globally enabled)
3159
+ * - `undefined` - Use global setting (default)
3160
+ *
3161
+ * When enabled, events will include a `formValue` property with the current form state.
3162
+ * Use the `hasFormValue()` type guard to safely access this property.
3163
+ *
3164
+ * @example
3165
+ * ```typescript
3166
+ * // Enable for this form only (no global withEventFormValue() needed)
3167
+ * const config: FormConfig = {
3168
+ * fields: [...],
3169
+ * options: { emitFormValueOnEvents: true }
3170
+ * };
3171
+ *
3172
+ * // Disable for this form (when globally enabled)
3173
+ * const config: FormConfig = {
3174
+ * fields: [...],
3175
+ * options: { emitFormValueOnEvents: false }
3176
+ * };
3177
+ * ```
3178
+ *
3179
+ * @default undefined (uses global setting)
3180
+ */
3181
+ emitFormValueOnEvents?: boolean;
3182
+ }
3183
+ /**
3184
+ * Options for controlling submit button disabled behavior.
3185
+ *
3186
+ * @public
3187
+ */
3188
+ interface SubmitButtonOptions {
3189
+ /**
3190
+ * Disable submit button when the form is invalid.
3191
+ *
3192
+ * @default true
2608
3193
  */
2609
3194
  disableWhenInvalid?: boolean;
2610
3195
  /**
@@ -2653,148 +3238,14 @@ interface FormModeDetectionResult {
2653
3238
  }
2654
3239
 
2655
3240
  /**
2656
- * Type guard to check if a field is a container field (page, row, group, or array)
2657
- * Container fields have a 'fields' property and don't contribute values directly.
2658
- * This overload works with RegisteredFieldTypes for full type narrowing.
2659
- */
2660
- declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
2661
- /**
2662
- * Type guard to check if a field is a container field (page, row, group, or array).
2663
- * This overload works with any FieldDef for looser type checking.
2664
- */
2665
- declare function isContainerField(field: FieldDef<unknown>): boolean;
2666
- /**
2667
- * Type guard to check if a field is a leaf field (value or display field)
2668
- * Leaf fields don't have children and either contribute values or display content
2669
- */
2670
- declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
2671
- /**
2672
- * Type guard to check if a field has a value property (value-bearing field)
2673
- * These fields contribute to the form value output
2674
- * Note: Using `unknown` in the Extract condition to match any value type
2675
- */
2676
- declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
2677
- value: unknown;
2678
- }>;
2679
- /**
2680
- * Type guard to check if a field is excluded from form values (display-only field)
2681
- * Currently this includes text fields and any other fields without a value property
2682
- */
2683
- declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
2684
-
2685
- /**
2686
- * Helper types for creating type-safe field configurations with proper nesting constraints
2687
- */
2688
-
2689
- /**
2690
- * Type helper for accessing nested field paths safely
2691
- * This allows accessing child paths while maintaining some type safety
2692
- */
2693
- type FieldPathAccess<TValue> = {
2694
- [K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
2695
- };
2696
-
2697
- /**
2698
- * Result of processing all pending derivations.
2699
- *
2700
- * @public
2701
- */
2702
- interface DerivationProcessingResult {
2703
- /** Number of derivations successfully applied */
2704
- appliedCount: number;
2705
- /** Number of derivations skipped (condition not met or value unchanged) */
2706
- skippedCount: number;
2707
- /** Number of derivations that failed with errors */
2708
- errorCount: number;
2709
- /**
2710
- * Number of derivations that encountered warnings (e.g., missing target field).
2711
- *
2712
- * These are not counted as errors because they may be intentional (conditional fields)
2713
- * but indicate potential configuration issues if unexpected.
2714
- */
2715
- warnCount: number;
2716
- /** Total iterations performed */
2717
- iterations: number;
2718
- /** Whether max iterations was reached (potential loop) */
2719
- maxIterationsReached: boolean;
2720
- }
2721
-
2722
- /**
2723
- * Information about a single derivation evaluation for logging.
3241
+ * Error from async field component loading.
2724
3242
  *
2725
3243
  * @public
2726
3244
  */
2727
- interface DerivationLogEntry {
2728
- /** Debug name if provided in the derivation config */
2729
- debugName?: string;
2730
- /** Source field key where the derivation is defined */
2731
- sourceFieldKey: string;
2732
- /** Target field key that will be modified */
2733
- targetFieldKey: string;
2734
- /** Result of the derivation evaluation */
2735
- result: 'applied' | 'skipped' | 'error';
2736
- /** Reason for skipping (if result is 'skipped') */
2737
- skipReason?: 'condition-false' | 'value-unchanged' | 'already-applied' | 'target-not-found';
2738
- /** The new value that was set (if result is 'applied') */
2739
- newValue?: unknown;
2740
- /** The previous value before the derivation (if result is 'applied') */
2741
- previousValue?: unknown;
2742
- /** Error message (if result is 'error') */
2743
- error?: string;
2744
- /** Duration in milliseconds (optional, for performance tracking) */
2745
- durationMs?: number;
2746
- }
2747
-
2748
- /**
2749
- * Service interface for derivation logging.
2750
- *
2751
- * Provides methods to log derivation processing events at different verbosity levels.
2752
- * Use `DERIVATION_LOGGER` injection token to get an instance.
2753
- *
2754
- * @public
2755
- */
2756
- interface DerivationLogger {
2757
- /**
2758
- * Logs the start of a derivation processing cycle.
2759
- * Only logs when config level is 'verbose'.
2760
- */
2761
- cycleStart(trigger: 'onChange' | 'debounced', entryCount: number): void;
2762
- /**
2763
- * Logs the start of a derivation iteration within a cycle.
2764
- * Only logs when config level is 'verbose'.
2765
- */
2766
- iteration(iterationNumber: number): void;
2767
- /**
2768
- * Logs a single derivation evaluation result.
2769
- * Only logs when config level is 'verbose'.
2770
- */
2771
- evaluation(entry: DerivationLogEntry): void;
2772
- /**
2773
- * Logs a summary of derivation processing results.
2774
- * Logs when config level is 'summary' or 'verbose' and something interesting happened.
2775
- */
2776
- summary(result: DerivationProcessingResult, trigger: 'onChange' | 'debounced'): void;
2777
- /**
2778
- * Logs when max iterations are reached (always logged as warning).
2779
- */
2780
- maxIterationsReached(result: DerivationProcessingResult, trigger: 'onChange' | 'debounced'): void;
2781
- }
2782
-
2783
- /**
2784
- * Minimal configuration for creating a DerivationOrchestrator.
2785
- * Only includes form-specific signals that cannot be injected.
2786
- *
2787
- * @internal
2788
- */
2789
- interface DerivationOrchestratorConfig {
2790
- /** Signal containing the flattened schema fields */
2791
- schemaFields: Signal<FieldDef<unknown>[] | undefined>;
2792
- /** Signal containing the current form value */
2793
- formValue: Signal<Record<string, unknown>>;
2794
- /** Signal containing the form accessor */
2795
- form: Signal<FieldTree<unknown>>;
2796
- /** Signal containing the derivation logger */
2797
- derivationLogger: Signal<DerivationLogger>;
3245
+ interface FieldLoadingError {
3246
+ readonly fieldType: string;
3247
+ readonly fieldKey: string;
3248
+ readonly error: Error;
2798
3249
  }
2799
3250
 
2800
3251
  /**
@@ -2819,7 +3270,38 @@ interface DerivationOrchestratorConfig {
2819
3270
  interface FormEvent {
2820
3271
  /** Unique identifier for the event type used for filtering and subscriptions */
2821
3272
  readonly type: string;
3273
+ /**
3274
+ * Optional form value attached to the event when `withEventFormValue()` is enabled
3275
+ * or `options.emitFormValueOnEvents` is set to `true` in the form config.
3276
+ *
3277
+ * Use the `hasFormValue()` type guard to check if this property is present.
3278
+ */
3279
+ readonly formValue?: unknown;
2822
3280
  }
3281
+ /**
3282
+ * Type guard to check if a form event has a form value attached.
3283
+ *
3284
+ * Use this to safely access the `formValue` property on events when
3285
+ * `withEventFormValue()` is enabled globally or `options.emitFormValueOnEvents`
3286
+ * is set to `true` in the form config.
3287
+ *
3288
+ * @example
3289
+ * ```typescript
3290
+ * eventBus.on<SubmitEvent>('submit').subscribe(event => {
3291
+ * if (hasFormValue(event)) {
3292
+ * console.log('Form value:', event.formValue);
3293
+ * }
3294
+ * });
3295
+ * ```
3296
+ *
3297
+ * @param event - The form event to check
3298
+ * @returns `true` if the event has a form value attached, narrowing the type
3299
+ *
3300
+ * @public
3301
+ */
3302
+ declare function hasFormValue<T extends FormEvent>(event: T): event is T & {
3303
+ formValue: unknown;
3304
+ };
2823
3305
  /**
2824
3306
  * Constructor type for form event classes.
2825
3307
  *
@@ -2929,128 +3411,110 @@ declare class PageNavigationStateChangeEvent implements FormEvent {
2929
3411
  }
2930
3412
 
2931
3413
  /**
2932
- * Dynamic form component that renders a complete form based on configuration.
2933
- *
2934
- * This is the main entry point for the dynamic form system. It handles form state management,
2935
- * validation, field rendering, and event coordination using Angular's signal-based reactive forms.
2936
- *
2937
- * @typeParam TFields - Array of registered field types available for this form
2938
- * @typeParam TModel - The strongly-typed interface for form values
3414
+ * Dynamic form component — renders a form based on configuration.
3415
+ * Delegates state management to `FormStateManager`.
2939
3416
  *
2940
3417
  * @example
2941
3418
  *```html
2942
- * <form
2943
- * [dynamic-form]="formConfig"
2944
- * [(value)]="formData"
2945
- * (submitted)="handleSubmit($event)"
2946
- * (validityChange)="handleValidityChange($event)">
2947
- * </form>
3419
+ * <form [dynamic-form]="formConfig" [(value)]="formData" (submitted)="handleSubmit($event)"></form>
2948
3420
  * ```
2949
3421
  */
2950
- declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFieldTypes[], TModel extends Record<string, unknown> = InferFormValue$1<TFields> & Record<string, unknown>> implements OnDestroy {
2951
- private readonly destroyRef;
2952
- private readonly fieldRegistry;
2953
- private readonly injector;
2954
- private readonly eventBus;
2955
- private readonly rootFormRegistry;
2956
- private readonly functionRegistry;
2957
- private readonly schemaRegistry;
2958
- private readonly logger;
2959
- private readonly derivationLogConfig;
3422
+ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFieldTypes[], TModel extends Record<string, unknown> = InferFormValue$1<TFields> & Record<string, unknown>> {
2960
3423
  /** Form configuration defining the structure, validation, and behavior. */
2961
- config: InputSignal<FormConfig<TFields>>;
3424
+ config: _angular_core.InputSignal<FormConfig<TFields, InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>, Record<string, unknown>, unknown>>;
2962
3425
  /** Runtime form options that override config options when provided. */
2963
- formOptions: InputSignal<FormOptions | undefined>;
3426
+ formOptions: _angular_core.InputSignal<FormOptions | undefined>;
2964
3427
  /** Form values for two-way data binding. */
2965
3428
  value: _angular_core.ModelSignal<Partial<TModel> | undefined>;
2966
- private readonly memoizedFlattenFields;
2967
- private readonly memoizedFlattenFieldsForRendering;
2968
- private readonly memoizedKeyBy;
2969
- private readonly memoizedDefaultValues;
2970
- private readonly rawFieldRegistry;
2971
- readonly formModeDetection: Signal<FormModeDetectionResult>;
2972
- private readonly formSetup;
2973
- readonly pageFieldDefinitions: Signal<PageField<_ng_forge_dynamic_forms.PageAllowedChildren[]>[]>;
2974
- readonly effectiveFormOptions: Signal<{
3429
+ private destroyRef;
3430
+ private injector;
3431
+ private eventBus;
3432
+ private logger;
3433
+ /**
3434
+ * Connect input signals to the shared deps holder BEFORE injecting FormStateManager.
3435
+ * Field initializers run in declaration order, so this runs after inputs are created
3436
+ * but before stateManager is injected. FormStateManager reads from these signals
3437
+ * in computeds/effects, which are all lazy and evaluated after construction.
3438
+ */
3439
+ private readonly _connectStateDeps;
3440
+ /** State manager that owns all form state. */
3441
+ private stateManager;
3442
+ private componentId;
3443
+ /** The currently active config used for form rendering */
3444
+ activeConfig: Signal<FormConfig<TFields, InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>, Record<string, unknown>, unknown> | undefined>;
3445
+ /** Current render phase: 'render' = showing form, 'teardown' = hiding old components */
3446
+ renderPhase: Signal<"teardown" | "render">;
3447
+ /** Computed form mode detection with validation */
3448
+ formModeDetection: Signal<_ng_forge_dynamic_forms.FormModeDetectionResult>;
3449
+ /** Page field definitions for paged forms */
3450
+ pageFieldDefinitions: Signal<_ng_forge_dynamic_forms.PageField<_ng_forge_dynamic_forms.PageAllowedChildren[]>[]>;
3451
+ /** Effective form options (merged from config and input) */
3452
+ effectiveFormOptions: Signal<{
2975
3453
  disabled?: boolean;
2976
3454
  maxDerivationIterations?: number;
2977
3455
  submitButton?: _ng_forge_dynamic_forms.SubmitButtonOptions;
2978
3456
  nextButton?: _ng_forge_dynamic_forms.NextButtonOptions;
3457
+ emitFormValueOnEvents?: boolean;
2979
3458
  }>;
2980
- /**
2981
- * Computed derivation logger based on injected config.
2982
- *
2983
- * Uses factory pattern to return no-op logger when logging is disabled,
2984
- * avoiding any logging overhead in production.
2985
- */
2986
- private readonly derivationLogger;
2987
- readonly fieldSignalContext: Signal<FieldSignalContext<TModel>>;
2988
- /**
2989
- * Injector for field components with FIELD_SIGNAL_CONTEXT.
2990
- * Other form-level tokens (DEFAULT_PROPS, etc.) are provided at component level.
2991
- */
2992
- private readonly fieldInjector;
2993
- readonly defaultValues: _angular_core.WritableSignal<TModel>;
2994
- private readonly entity;
2995
- readonly form: Signal<FieldTree<TModel, string | number>>;
2996
- private readonly componentId;
2997
- private readonly totalComponentsCount;
2998
- private readonly fieldsSource;
2999
- /** Collects errors from async field component loading for error boundary patterns. */
3000
- readonly fieldLoadingErrors: _angular_core.WritableSignal<{
3001
- fieldType: string;
3002
- fieldKey: string;
3003
- error: Error;
3004
- }[]>;
3005
- /** Current form values (reactive). */
3006
- readonly formValue: Signal<TModel | (TModel extends infer T ? T extends TModel ? T extends _angular_forms.AbstractControl<unknown, infer TValue extends unknown, any> ? TValue : never : never : never)>;
3007
- /** Whether the form is currently valid. */
3008
- readonly valid: Signal<boolean>;
3009
- /** Whether the form is currently invalid. */
3010
- readonly invalid: Signal<boolean>;
3011
- /** Whether any form field has been modified. */
3012
- readonly dirty: Signal<boolean>;
3013
- /** Whether any form field has been touched (blurred). */
3014
- readonly touched: Signal<boolean>;
3015
- /** Current validation errors from all fields. */
3016
- readonly errors: Signal<_angular_forms_signals.ValidationError.WithField[]>;
3017
- /** Whether the form is disabled (from options or form state). */
3018
- readonly disabled: Signal<boolean>;
3019
- /** Whether the form is currently submitting. */
3020
- readonly submitting: Signal<boolean>;
3021
- /**
3022
- * Configuration for the derivation orchestrator.
3023
- * Bundles the form-specific signals needed by the orchestrator.
3024
- *
3025
- * @internal Used by DERIVATION_ORCHESTRATOR provider.
3026
- */
3027
- readonly derivationOrchestratorConfig: DerivationOrchestratorConfig;
3028
- readonly initialized$: rxjs.Observable<boolean>;
3459
+ /** Field signal context for injection into child components */
3460
+ fieldSignalContext: Signal<_ng_forge_dynamic_forms.FieldSignalContext<TModel>>;
3461
+ /** Default values computed from field definitions */
3462
+ defaultValues: WritableSignal<TModel>;
3463
+ /** The Angular Signal Form instance */
3464
+ form: Signal<FieldTree<TModel, string | number>>;
3465
+ /** Current form values (reactive) */
3466
+ formValue: Signal<TModel | (TModel extends infer T ? T extends TModel ? T extends _angular_forms.AbstractControl<unknown, infer TValue extends unknown, any> ? TValue : never : never : never)>;
3467
+ /** Whether the form is currently valid */
3468
+ valid: Signal<boolean>;
3469
+ /** Whether the form is currently invalid */
3470
+ invalid: Signal<boolean>;
3471
+ /** Whether any form field has been modified */
3472
+ dirty: Signal<boolean>;
3473
+ /** Whether any form field has been touched (blurred) */
3474
+ touched: Signal<boolean>;
3475
+ /** Current validation errors from all fields */
3476
+ errors: Signal<_angular_forms_signals.ValidationError.WithField[]>;
3477
+ /** Whether the form is disabled (from options or form state) */
3478
+ disabled: Signal<boolean>;
3479
+ /** Whether the form is currently submitting */
3480
+ submitting: Signal<boolean>;
3481
+ /** Collects errors from async field component loading for error boundary patterns */
3482
+ fieldLoadingErrors: WritableSignal<FieldLoadingError[]>;
3483
+ /** Whether to render the form template */
3484
+ shouldRender: Signal<boolean>;
3485
+ /** Resolved fields ready for rendering */
3486
+ protected resolvedFields: Signal<ResolvedField[]>;
3487
+ private totalComponentsCount;
3488
+ initialized$: rxjs.Observable<boolean>;
3029
3489
  /** Emits when form validity changes. */
3030
- readonly validityChange: _angular_core.OutputRef<boolean>;
3490
+ validityChange: _angular_core.OutputRef<boolean>;
3031
3491
  /** Emits when form dirty state changes. */
3032
- readonly dirtyChange: _angular_core.OutputRef<boolean>;
3492
+ dirtyChange: _angular_core.OutputRef<boolean>;
3033
3493
  /**
3034
- * Emits form values when submitted (via SubmitEvent).
3494
+ * Emits form values when submitted (via SubmitEvent) and form is valid.
3495
+ *
3496
+ * **Important:** This output only emits when the form is valid. If you need to
3497
+ * handle submit events regardless of validity, use the `(events)` output and
3498
+ * filter for `'submit'` events.
3499
+ *
3035
3500
  * Note: Does not emit when `submission.action` is configured - use one or the other.
3036
3501
  */
3037
- readonly submitted: _angular_core.OutputRef<Partial<TModel> | undefined>;
3502
+ submitted: _angular_core.OutputRef<Partial<TModel>>;
3038
3503
  /** Emits when form is reset to default values. */
3039
- readonly reset: _angular_core.OutputRef<FormResetEvent>;
3504
+ reset: _angular_core.OutputRef<FormResetEvent>;
3040
3505
  /** Emits when form is cleared to empty state. */
3041
- readonly cleared: _angular_core.OutputRef<FormClearEvent>;
3506
+ cleared: _angular_core.OutputRef<FormClearEvent>;
3042
3507
  /** Emits all form events for custom event handling. */
3043
- readonly events: _angular_core.OutputRef<_ng_forge_dynamic_forms.FormEvent>;
3508
+ events: _angular_core.OutputRef<_ng_forge_dynamic_forms.FormEvent>;
3044
3509
  /**
3045
3510
  * Emits when all form components are initialized and ready for interaction.
3046
3511
  * Useful for E2E testing to ensure the form is fully rendered before interaction.
3047
3512
  */
3048
- readonly initialized: _angular_core.OutputRef<boolean>;
3513
+ initialized: _angular_core.OutputRef<boolean>;
3049
3514
  /** Emits when the current page changes in paged forms. */
3050
- readonly onPageChange: _angular_core.OutputRef<PageChangeEvent>;
3515
+ onPageChange: _angular_core.OutputRef<PageChangeEvent>;
3051
3516
  /** Emits when page navigation state changes (canGoNext, canGoPrevious, etc.). */
3052
- readonly onPageNavigationStateChange: _angular_core.OutputRef<PageNavigationStateChangeEvent>;
3053
- protected readonly resolvedFields: Signal<ResolvedField[]>;
3517
+ onPageNavigationStateChange: _angular_core.OutputRef<PageNavigationStateChangeEvent>;
3054
3518
  constructor();
3055
3519
  /**
3056
3520
  * Handles native form submission triggered by:
@@ -3066,12 +3530,6 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
3066
3530
  protected onNativeSubmit(event: Event): void;
3067
3531
  private setupEffects;
3068
3532
  private setupEventHandlers;
3069
- private registerValidatorsFromConfig;
3070
- private createFormSetupFromConfig;
3071
- private createEmptyFormSetup;
3072
- private onFormReset;
3073
- private onFormClear;
3074
- ngOnDestroy(): void;
3075
3533
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
3076
3534
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicForm<any, any>, "form[dynamic-form]", never, { "config": { "alias": "dynamic-form"; "required": true; "isSignal": true; }; "formOptions": { "alias": "formOptions"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; "reset": "reset"; "cleared": "cleared"; "events": "events"; "initialized": "initialized"; "onPageChange": "onPageChange"; "onPageNavigationStateChange": "onPageNavigationStateChange"; }, never, never, true, never>;
3077
3535
  }
@@ -3090,6 +3548,48 @@ interface DynamicFormFeature<TKind extends string = string> {
3090
3548
  ɵproviders: Provider[];
3091
3549
  }
3092
3550
 
3551
+ /**
3552
+ * Type guard to check if a field is a container field (page, row, group, or array)
3553
+ * Container fields have a 'fields' property and don't contribute values directly.
3554
+ * This overload works with RegisteredFieldTypes for full type narrowing.
3555
+ */
3556
+ declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
3557
+ /**
3558
+ * Type guard to check if a field is a container field (page, row, group, or array).
3559
+ * This overload works with any FieldDef for looser type checking.
3560
+ */
3561
+ declare function isContainerField(field: FieldDef<unknown>): boolean;
3562
+ /**
3563
+ * Type guard to check if a field is a leaf field (value or display field)
3564
+ * Leaf fields don't have children and either contribute values or display content
3565
+ */
3566
+ declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
3567
+ /**
3568
+ * Type guard to check if a field has a value property (value-bearing field)
3569
+ * These fields contribute to the form value output
3570
+ * Note: Using `unknown` in the Extract condition to match any value type
3571
+ */
3572
+ declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
3573
+ value: unknown;
3574
+ }>;
3575
+ /**
3576
+ * Type guard to check if a field is excluded from form values (display-only field)
3577
+ * Currently this includes text fields and any other fields without a value property
3578
+ */
3579
+ declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
3580
+
3581
+ /**
3582
+ * Helper types for creating type-safe field configurations with proper nesting constraints
3583
+ */
3584
+
3585
+ /**
3586
+ * Type helper for accessing nested field paths safely
3587
+ * This allows accessing child paths while maintaining some type safety
3588
+ */
3589
+ type FieldPathAccess<TValue> = {
3590
+ [K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
3591
+ };
3592
+
3093
3593
  /**
3094
3594
  * Extract FieldDef type from FieldTypeDefinition
3095
3595
  */
@@ -3099,13 +3599,9 @@ type ExtractFieldDef<T> = T extends FieldTypeDefinition<infer F> ? F : never;
3099
3599
  */
3100
3600
  type FieldDefUnion<T extends FieldTypeDefinition[]> = ExtractFieldDef<T[number]>;
3101
3601
  /**
3102
- * Infer form value type from field definitions
3103
- * Note: Full value type inference requires complex recursive type mapping.
3104
- * Using unknown as placeholder until full inference is implemented.
3602
+ * Infer form value type from field definitions using the real inference type.
3105
3603
  */
3106
- type InferFormValue<TFieldDefs extends FieldDef<unknown>[]> = {
3107
- [K in TFieldDefs[number]['key']]: unknown;
3108
- };
3604
+ type InferFormValue<TFieldDefs extends FieldDef<unknown>[]> = InferFormValue$1<TFieldDefs>;
3109
3605
  /**
3110
3606
  * Provider result with type inference
3111
3607
  */
@@ -3318,6 +3814,52 @@ declare class NoopLogger implements Logger {
3318
3814
  error(): void;
3319
3815
  }
3320
3816
 
3817
+ /**
3818
+ * Enables automatic form value attachment to all events dispatched through the EventBus.
3819
+ *
3820
+ * When this feature is enabled, events dispatched via `eventBus.dispatch()` will include
3821
+ * the current form value in the `formValue` property. This is useful for event handlers
3822
+ * that need access to the complete form state at the time of the event.
3823
+ *
3824
+ * @example Global opt-in
3825
+ * ```typescript
3826
+ * provideDynamicForm(
3827
+ * ...withMaterialFields(),
3828
+ * withEventFormValue()
3829
+ * )
3830
+ * ```
3831
+ *
3832
+ * @example Consumer usage
3833
+ * ```typescript
3834
+ * eventBus.on<SubmitEvent>('submit').subscribe(event => {
3835
+ * if (hasFormValue(event)) {
3836
+ * console.log('Form value:', event.formValue);
3837
+ * }
3838
+ * });
3839
+ * ```
3840
+ *
3841
+ * @example Per-form disable (when globally enabled)
3842
+ * ```typescript
3843
+ * const config: FormConfig = {
3844
+ * fields: [...],
3845
+ * options: { emitFormValueOnEvents: false }
3846
+ * };
3847
+ * ```
3848
+ *
3849
+ * @remarks
3850
+ * **Precedence rules:**
3851
+ * 1. Per-form `false` - Disables for this form, regardless of global setting
3852
+ * 2. Per-form `true` - Enables for this form, regardless of global setting
3853
+ * 3. Per-form `undefined` - Uses global setting
3854
+ * 4. Global `withEventFormValue()` - Enables globally
3855
+ * 5. No global feature - Disabled globally (default)
3856
+ *
3857
+ * @returns A DynamicFormFeature that enables form value attachment to events
3858
+ *
3859
+ * @public
3860
+ */
3861
+ declare function withEventFormValue(): DynamicFormFeature<'event-form-value'>;
3862
+
3321
3863
  interface FieldOption<T = unknown> {
3322
3864
  label: DynamicText;
3323
3865
  value: T;
@@ -3452,80 +3994,6 @@ declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessa
3452
3994
  */
3453
3995
  declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
3454
3996
 
3455
- /**
3456
- * Context for resolving button disabled state.
3457
- *
3458
- * @public
3459
- */
3460
- interface ButtonLogicContext {
3461
- /** The form's FieldTree instance (supports both string and number keys for array indices) */
3462
- form: FieldTree<unknown, string | number>;
3463
- /** Form-level options */
3464
- formOptions?: FormOptions;
3465
- /** Field-level logic array (if provided) */
3466
- fieldLogic?: LogicConfig[];
3467
- /** Explicit disabled state from field definition */
3468
- explicitlyDisabled?: boolean;
3469
- /** Current page validity signal (for paged forms) */
3470
- currentPageValid?: Signal<boolean>;
3471
- /** Current form value for evaluating conditional expressions */
3472
- formValue?: unknown;
3473
- /** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
3474
- logger?: Logger;
3475
- }
3476
- /**
3477
- * Resolves the disabled state for a submit button.
3478
- *
3479
- * The disabled state is determined by (in order of precedence):
3480
- * 1. Explicit `disabled: true` on the field definition
3481
- * 2. Field-level `logic` array (if present, overrides form-level defaults)
3482
- * 3. Form-level `options.submitButton` defaults
3483
- *
3484
- * @param ctx - The button logic context
3485
- * @returns A computed signal that returns true when the button should be disabled
3486
- *
3487
- * @example
3488
- * ```typescript
3489
- * const disabled = resolveSubmitButtonDisabled({
3490
- * form: formInstance,
3491
- * formOptions: config.options,
3492
- * fieldLogic: buttonField.logic,
3493
- * explicitlyDisabled: buttonField.disabled,
3494
- * });
3495
- *
3496
- * // Use in template
3497
- * <button [disabled]="disabled()">Submit</button>
3498
- * ```
3499
- *
3500
- * @public
3501
- */
3502
- declare function resolveSubmitButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
3503
- /**
3504
- * Resolves the disabled state for a next page button.
3505
- *
3506
- * The disabled state is determined by (in order of precedence):
3507
- * 1. Explicit `disabled: true` on the field definition
3508
- * 2. Field-level `logic` array (if present, overrides form-level defaults)
3509
- * 3. Form-level `options.nextButton` defaults
3510
- *
3511
- * @param ctx - The button logic context
3512
- * @returns A computed signal that returns true when the button should be disabled
3513
- *
3514
- * @example
3515
- * ```typescript
3516
- * const disabled = resolveNextButtonDisabled({
3517
- * form: formInstance,
3518
- * formOptions: config.options,
3519
- * fieldLogic: buttonField.logic,
3520
- * explicitlyDisabled: buttonField.disabled,
3521
- * currentPageValid: pageOrchestrator.currentPageValid,
3522
- * });
3523
- * ```
3524
- *
3525
- * @public
3526
- */
3527
- declare function resolveNextButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
3528
-
3529
3997
  interface FieldWithValidation {
3530
3998
  readonly required?: boolean;
3531
3999
  readonly email?: boolean;
@@ -3624,77 +4092,375 @@ declare class PreviousPageEvent implements FormEvent {
3624
4092
  }
3625
4093
 
3626
4094
  /**
3627
- * Event dispatched to add a new item to an array field.
4095
+ * Template type for OBJECT array items: array of field configurations.
4096
+ * Each field's value is merged into the item object.
4097
+ */
4098
+ type ArrayItemTemplate = ArrayAllowedChildren[];
4099
+ /**
4100
+ * Template type for array items that can be either:
4101
+ * - A single field (ArrayAllowedChildren) for PRIMITIVE items - extracts field value directly
4102
+ * - An array of fields (ArrayItemTemplate) for OBJECT items - merges fields into object
4103
+ *
4104
+ * Examples:
4105
+ * - Primitive: `{ key: 'tag', type: 'input', value: 'angular' }` → 'angular'
4106
+ * - Object: `[{ key: 'name', type: 'input' }, { key: 'email', type: 'input' }]` → { name: '', email: '' }
4107
+ */
4108
+ type ArrayItemDefinitionTemplate = ArrayAllowedChildren | ArrayItemTemplate;
4109
+ /**
4110
+ * Event dispatched to append a new item at the END of an array field.
3628
4111
  *
3629
- * This event is used to dynamically add items to array fields at runtime.
3630
- * The array field component listens for this event and adds a new item
3631
- * based on the provided field template, or uses the array's own template if not provided.
4112
+ * This is the most common array operation - adding items to the end.
4113
+ * For other positions, use {@link PrependArrayItemEvent} or {@link InsertArrayItemEvent}.
3632
4114
  *
3633
4115
  * @example
3634
4116
  * ```typescript
3635
- * // Add item using array's own template
3636
- * eventBus.dispatch(AddArrayItemEvent, 'contacts');
4117
+ * // Object item: append { name, email } object
4118
+ * eventBus.dispatch(arrayEvent('contacts').append([
4119
+ * { key: 'name', type: 'input', label: 'Name' },
4120
+ * { key: 'email', type: 'input', label: 'Email' }
4121
+ * ]));
4122
+ *
4123
+ * // Primitive item: append single value
4124
+ * eventBus.dispatch(arrayEvent('tags').append(
4125
+ * { key: 'tag', type: 'input', label: 'Tag' }
4126
+ * ));
4127
+ * ```
4128
+ *
4129
+ * @typeParam TTemplate - The type of the template (single field or array of fields)
4130
+ */
4131
+ declare class AppendArrayItemEvent<TTemplate extends ArrayItemDefinitionTemplate = ArrayItemDefinitionTemplate> implements FormEvent {
4132
+ /** The key of the array field to append an item to */
4133
+ readonly arrayKey: string;
4134
+ /**
4135
+ * Template for the new array item. REQUIRED.
4136
+ * - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
4137
+ * - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
4138
+ */
4139
+ readonly template: TTemplate;
4140
+ readonly type: "append-array-item";
4141
+ constructor(
4142
+ /** The key of the array field to append an item to */
4143
+ arrayKey: string,
4144
+ /**
4145
+ * Template for the new array item. REQUIRED.
4146
+ * - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
4147
+ * - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
4148
+ */
4149
+ template: TTemplate);
4150
+ }
4151
+
4152
+ /**
4153
+ * Event dispatched to prepend a new item at the BEGINNING of an array field.
4154
+ *
4155
+ * Use this when new items should appear at the start of the list.
4156
+ * For appending to the end, use {@link AppendArrayItemEvent}.
4157
+ *
4158
+ * @example
4159
+ * ```typescript
4160
+ * // Object item: prepend { name } object
4161
+ * eventBus.dispatch(arrayEvent('contacts').prepend([
4162
+ * { key: 'name', type: 'input', label: 'Name' }
4163
+ * ]));
4164
+ *
4165
+ * // Primitive item: prepend single value
4166
+ * eventBus.dispatch(arrayEvent('tags').prepend(
4167
+ * { key: 'tag', type: 'input', label: 'Tag' }
4168
+ * ));
4169
+ * ```
4170
+ *
4171
+ * @typeParam TTemplate - The type of the template (single field or array of fields)
4172
+ */
4173
+ declare class PrependArrayItemEvent<TTemplate extends ArrayItemDefinitionTemplate = ArrayItemDefinitionTemplate> implements FormEvent {
4174
+ /** The key of the array field to prepend an item to */
4175
+ readonly arrayKey: string;
4176
+ /**
4177
+ * Template for the new array item. REQUIRED.
4178
+ * - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
4179
+ * - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
4180
+ */
4181
+ readonly template: TTemplate;
4182
+ readonly type: "prepend-array-item";
4183
+ constructor(
4184
+ /** The key of the array field to prepend an item to */
4185
+ arrayKey: string,
4186
+ /**
4187
+ * Template for the new array item. REQUIRED.
4188
+ * - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
4189
+ * - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
4190
+ */
4191
+ template: TTemplate);
4192
+ }
4193
+
4194
+ /**
4195
+ * Event dispatched to insert a new item at a SPECIFIC INDEX in an array field.
3637
4196
  *
3638
- * // Add item with explicit field template
3639
- * const fieldTemplate = { key: 'email', type: 'input' };
3640
- * eventBus.dispatch(AddArrayItemEvent, 'contacts', fieldTemplate);
4197
+ * Use this when you need precise control over where the new item appears.
4198
+ * For simpler operations, use {@link AppendArrayItemEvent} or {@link PrependArrayItemEvent}.
3641
4199
  *
3642
- * // Add item at specific index
3643
- * eventBus.dispatch(AddArrayItemEvent, 'contacts', fieldTemplate, 2);
4200
+ * @example
4201
+ * ```typescript
4202
+ * // Object item: insert { name } object at index 2
4203
+ * eventBus.dispatch(arrayEvent('contacts').insertAt(2, [
4204
+ * { key: 'name', type: 'input', label: 'Name' }
4205
+ * ]));
4206
+ *
4207
+ * // Primitive item: insert single value at index 2
4208
+ * eventBus.dispatch(arrayEvent('tags').insertAt(2,
4209
+ * { key: 'tag', type: 'input', label: 'Tag' }
4210
+ * ));
3644
4211
  * ```
4212
+ *
4213
+ * @typeParam TTemplate - The type of the template (single field or array of fields)
3645
4214
  */
3646
- declare class AddArrayItemEvent implements FormEvent {
3647
- /** The key of the array field to add an item to */
4215
+ declare class InsertArrayItemEvent<TTemplate extends ArrayItemDefinitionTemplate = ArrayItemDefinitionTemplate> implements FormEvent {
4216
+ /** The key of the array field to insert an item into */
3648
4217
  readonly arrayKey: string;
4218
+ /** The index at which to insert the new item */
4219
+ readonly index: number;
3649
4220
  /**
3650
- * Optional field template definition to render for the new array item.
3651
- * If not provided, the array field component will use its own template.
4221
+ * Template for the new array item. REQUIRED.
4222
+ * - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
4223
+ * - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
3652
4224
  */
3653
- readonly field?: ArrayAllowedChildren | undefined;
3654
- /** Optional index where to insert the item (defaults to end of array) */
3655
- readonly index?: number | undefined;
3656
- readonly type: "add-array-item";
4225
+ readonly template: TTemplate;
4226
+ readonly type: "insert-array-item";
3657
4227
  constructor(
3658
- /** The key of the array field to add an item to */
4228
+ /** The key of the array field to insert an item into */
3659
4229
  arrayKey: string,
4230
+ /** The index at which to insert the new item */
4231
+ index: number,
3660
4232
  /**
3661
- * Optional field template definition to render for the new array item.
3662
- * If not provided, the array field component will use its own template.
4233
+ * Template for the new array item. REQUIRED.
4234
+ * - Single field (FieldDef): Creates a primitive item (field's value is extracted directly)
4235
+ * - Array of fields (FieldDef[]): Creates an object item (fields merged into object)
3663
4236
  */
3664
- field?: ArrayAllowedChildren | undefined,
3665
- /** Optional index where to insert the item (defaults to end of array) */
3666
- index?: number | undefined);
4237
+ template: TTemplate);
4238
+ }
4239
+
4240
+ /**
4241
+ * Event dispatched to remove the LAST item from an array field.
4242
+ *
4243
+ * Equivalent to JavaScript's `Array.pop()` - removes from the end.
4244
+ * For removing from the beginning, use {@link ShiftArrayItemEvent}.
4245
+ * For removing at a specific index, use {@link RemoveAtIndexEvent}.
4246
+ *
4247
+ * @example
4248
+ * ```typescript
4249
+ * // Use the builder API (recommended)
4250
+ * eventBus.dispatch(arrayEvent('contacts').pop());
4251
+ *
4252
+ * // Or instantiate directly
4253
+ * eventBus.dispatch(new PopArrayItemEvent('contacts'));
4254
+ * ```
4255
+ */
4256
+ declare class PopArrayItemEvent implements FormEvent {
4257
+ /** The key of the array field to remove the last item from */
4258
+ readonly arrayKey: string;
4259
+ readonly type: "pop-array-item";
4260
+ constructor(
4261
+ /** The key of the array field to remove the last item from */
4262
+ arrayKey: string);
4263
+ }
4264
+
4265
+ /**
4266
+ * Event dispatched to remove the FIRST item from an array field.
4267
+ *
4268
+ * Equivalent to JavaScript's `Array.shift()` - removes from the beginning.
4269
+ * For removing from the end, use {@link PopArrayItemEvent}.
4270
+ * For removing at a specific index, use {@link RemoveAtIndexEvent}.
4271
+ *
4272
+ * @example
4273
+ * ```typescript
4274
+ * // Use the builder API (recommended)
4275
+ * eventBus.dispatch(arrayEvent('contacts').shift());
4276
+ *
4277
+ * // Or instantiate directly
4278
+ * eventBus.dispatch(new ShiftArrayItemEvent('contacts'));
4279
+ * ```
4280
+ */
4281
+ declare class ShiftArrayItemEvent implements FormEvent {
4282
+ /** The key of the array field to remove the first item from */
4283
+ readonly arrayKey: string;
4284
+ readonly type: "shift-array-item";
4285
+ constructor(
4286
+ /** The key of the array field to remove the first item from */
4287
+ arrayKey: string);
3667
4288
  }
3668
4289
 
3669
4290
  /**
3670
- * Event dispatched to remove an item from an array field.
4291
+ * Event dispatched to remove an item at a SPECIFIC INDEX from an array field.
3671
4292
  *
3672
- * This event is used to dynamically remove items from array fields at runtime.
3673
- * The array field component listens for this event and removes the item
3674
- * at the specified index.
4293
+ * Use this when you need precise control over which item to remove.
4294
+ * For simpler operations, use {@link PopArrayItemEvent} or {@link ShiftArrayItemEvent}.
3675
4295
  *
3676
4296
  * @example
3677
4297
  * ```typescript
3678
- * // Remove item at index 1 from 'contacts' array
3679
- * eventBus.dispatch(RemoveArrayItemEvent, 'contacts', 1);
4298
+ * // Use the builder API (recommended)
4299
+ * eventBus.dispatch(arrayEvent('contacts').removeAt(2));
3680
4300
  *
3681
- * // Remove last item
3682
- * eventBus.dispatch(RemoveArrayItemEvent, 'contacts');
4301
+ * // Or instantiate directly
4302
+ * eventBus.dispatch(new RemoveAtIndexEvent('contacts', 2));
3683
4303
  * ```
3684
4304
  */
3685
- declare class RemoveArrayItemEvent implements FormEvent {
4305
+ declare class RemoveAtIndexEvent implements FormEvent {
3686
4306
  /** The key of the array field to remove an item from */
3687
4307
  readonly arrayKey: string;
3688
- /** Index of the item to remove (defaults to last item) */
3689
- readonly index?: number | undefined;
3690
- readonly type: "remove-array-item";
4308
+ /** The index of the item to remove */
4309
+ readonly index: number;
4310
+ readonly type: "remove-at-index";
3691
4311
  constructor(
3692
4312
  /** The key of the array field to remove an item from */
3693
4313
  arrayKey: string,
3694
- /** Index of the item to remove (defaults to last item) */
3695
- index?: number | undefined);
4314
+ /** The index of the item to remove */
4315
+ index: number);
3696
4316
  }
3697
4317
 
4318
+ /**
4319
+ * Builder for array manipulation events.
4320
+ *
4321
+ * Provides a fluent, discoverable API for array operations.
4322
+ * Type `arrayEvent('key').` in your IDE to see all available operations.
4323
+ *
4324
+ * **BREAKING CHANGE**: Template is now required for add operations.
4325
+ *
4326
+ * Supports both primitive and object array items:
4327
+ * - Primitive: Pass a single field definition → extracts field value directly
4328
+ * - Object: Pass an array of field definitions → merges fields into object
4329
+ *
4330
+ * @example
4331
+ * ```typescript
4332
+ * import { arrayEvent } from '@ng-forge/dynamic-forms';
4333
+ *
4334
+ * // Object item: append { name, email } object
4335
+ * eventBus.dispatch(arrayEvent('contacts').append([
4336
+ * { key: 'name', type: 'input', label: 'Name' },
4337
+ * { key: 'email', type: 'input', label: 'Email' }
4338
+ * ]));
4339
+ *
4340
+ * // Primitive item: append single value
4341
+ * eventBus.dispatch(arrayEvent('tags').append(
4342
+ * { key: 'tag', type: 'input', label: 'Tag' }
4343
+ * ));
4344
+ *
4345
+ * // Removing items (no template needed)
4346
+ * eventBus.dispatch(arrayEvent('contacts').pop()); // Remove last
4347
+ * eventBus.dispatch(arrayEvent('contacts').shift()); // Remove first
4348
+ * eventBus.dispatch(arrayEvent('contacts').removeAt(2)); // Remove at index
4349
+ * ```
4350
+ *
4351
+ * @param arrayKey - The key of the array field to operate on
4352
+ * @returns An object with methods for all 6 array operations
4353
+ */
4354
+ declare function arrayEvent(arrayKey: string): {
4355
+ /**
4356
+ * Append a new item at the END of the array.
4357
+ * This is the most common operation for adding items.
4358
+ *
4359
+ * @param template - Template for the new item (REQUIRED)
4360
+ * - Single field: Creates a primitive item (field's value is extracted directly)
4361
+ * - Array of fields: Creates an object item (fields merged into object)
4362
+ * @returns An AppendArrayItemEvent to dispatch
4363
+ *
4364
+ * @example
4365
+ * ```typescript
4366
+ * // Object item
4367
+ * eventBus.dispatch(arrayEvent('contacts').append([
4368
+ * { key: 'name', type: 'input', label: 'Name' }
4369
+ * ]));
4370
+ *
4371
+ * // Primitive item
4372
+ * eventBus.dispatch(arrayEvent('tags').append(
4373
+ * { key: 'tag', type: 'input', label: 'Tag' }
4374
+ * ));
4375
+ * ```
4376
+ */
4377
+ append: <T extends ArrayItemDefinitionTemplate>(template: T) => AppendArrayItemEvent<T>;
4378
+ /**
4379
+ * Prepend a new item at the BEGINNING of the array.
4380
+ * Use when new items should appear at the start.
4381
+ *
4382
+ * @param template - Template for the new item (REQUIRED)
4383
+ * - Single field: Creates a primitive item (field's value is extracted directly)
4384
+ * - Array of fields: Creates an object item (fields merged into object)
4385
+ * @returns A PrependArrayItemEvent to dispatch
4386
+ *
4387
+ * @example
4388
+ * ```typescript
4389
+ * // Object item
4390
+ * eventBus.dispatch(arrayEvent('contacts').prepend([
4391
+ * { key: 'name', type: 'input', label: 'Name' }
4392
+ * ]));
4393
+ *
4394
+ * // Primitive item
4395
+ * eventBus.dispatch(arrayEvent('tags').prepend(
4396
+ * { key: 'tag', type: 'input', label: 'Tag' }
4397
+ * ));
4398
+ * ```
4399
+ */
4400
+ prepend: <T extends ArrayItemDefinitionTemplate>(template: T) => PrependArrayItemEvent<T>;
4401
+ /**
4402
+ * Insert a new item at a SPECIFIC INDEX in the array.
4403
+ * Use when you need precise control over item placement.
4404
+ *
4405
+ * @param index - The position at which to insert the new item
4406
+ * @param template - Template for the new item (REQUIRED)
4407
+ * - Single field: Creates a primitive item (field's value is extracted directly)
4408
+ * - Array of fields: Creates an object item (fields merged into object)
4409
+ * @returns An InsertArrayItemEvent to dispatch
4410
+ *
4411
+ * @example
4412
+ * ```typescript
4413
+ * // Object item at index 2
4414
+ * eventBus.dispatch(arrayEvent('contacts').insertAt(2, [
4415
+ * { key: 'name', type: 'input', label: 'Name' }
4416
+ * ]));
4417
+ *
4418
+ * // Primitive item at index 2
4419
+ * eventBus.dispatch(arrayEvent('tags').insertAt(2,
4420
+ * { key: 'tag', type: 'input', label: 'Tag' }
4421
+ * ));
4422
+ * ```
4423
+ */
4424
+ insertAt: <T extends ArrayItemDefinitionTemplate>(index: number, template: T) => InsertArrayItemEvent<T>;
4425
+ /**
4426
+ * Remove the LAST item from the array.
4427
+ * Equivalent to JavaScript's `Array.pop()`.
4428
+ *
4429
+ * @returns A PopArrayItemEvent to dispatch
4430
+ *
4431
+ * @example
4432
+ * ```typescript
4433
+ * eventBus.dispatch(arrayEvent('contacts').pop());
4434
+ * ```
4435
+ */
4436
+ pop: () => PopArrayItemEvent;
4437
+ /**
4438
+ * Remove the FIRST item from the array.
4439
+ * Equivalent to JavaScript's `Array.shift()`.
4440
+ *
4441
+ * @returns A ShiftArrayItemEvent to dispatch
4442
+ *
4443
+ * @example
4444
+ * ```typescript
4445
+ * eventBus.dispatch(arrayEvent('contacts').shift());
4446
+ * ```
4447
+ */
4448
+ shift: () => ShiftArrayItemEvent;
4449
+ /**
4450
+ * Remove an item at a SPECIFIC INDEX from the array.
4451
+ * Use when you need to remove a specific item by position.
4452
+ *
4453
+ * @param index - The position of the item to remove
4454
+ * @returns A RemoveAtIndexEvent to dispatch
4455
+ *
4456
+ * @example
4457
+ * ```typescript
4458
+ * eventBus.dispatch(arrayEvent('contacts').removeAt(2));
4459
+ * ```
4460
+ */
4461
+ removeAt: (index: number) => RemoveAtIndexEvent;
4462
+ };
4463
+
3698
4464
  /**
3699
4465
  * Event bus service for form-wide event communication.
3700
4466
  *
@@ -3728,7 +4494,10 @@ declare class RemoveArrayItemEvent implements FormEvent {
3728
4494
  * ```
3729
4495
  */
3730
4496
  declare class EventBus {
3731
- private pipeline$;
4497
+ private readonly pipeline$;
4498
+ private readonly globalEmitFormValue;
4499
+ private readonly rootFormRegistry;
4500
+ private readonly formOptions;
3732
4501
  events$: Observable<FormEvent>;
3733
4502
  /**
3734
4503
  * Dispatches an event to all subscribers.
@@ -3736,6 +4505,10 @@ declare class EventBus {
3736
4505
  * Creates an instance of the provided event constructor and broadcasts it
3737
4506
  * through the event pipeline to all active subscribers.
3738
4507
  *
4508
+ * If `withEventFormValue()` is enabled globally or `options.emitFormValueOnEvents`
4509
+ * is set to `true` in the form config, the current form value will be attached
4510
+ * to the event's `formValue` property.
4511
+ *
3739
4512
  * @param eventConstructor - Constructor function for the event to dispatch
3740
4513
  *
3741
4514
  * @example
@@ -3748,6 +4521,14 @@ declare class EventBus {
3748
4521
  * ```
3749
4522
  */
3750
4523
  dispatch<T extends FormEventConstructor>(eventConstructor: T, ...args: ConstructorParameters<T>): void;
4524
+ /**
4525
+ * Determines whether form value should be attached to events.
4526
+ *
4527
+ * Precedence rules:
4528
+ * 1. Per-form setting (if defined) takes precedence
4529
+ * 2. Falls back to global setting
4530
+ */
4531
+ private shouldEmitFormValue;
3751
4532
  /**
3752
4533
  * Subscribes to events of a specific type.
3753
4534
  *
@@ -3778,6 +4559,8 @@ interface TokenContext {
3778
4559
  arrayKey?: string;
3779
4560
  /** Current form value for complex indexing */
3780
4561
  formValue?: unknown;
4562
+ /** Template for array item creation */
4563
+ template?: unknown;
3781
4564
  }
3782
4565
  /**
3783
4566
  * Resolves special tokens in event arguments to their actual values
@@ -3786,6 +4569,7 @@ interface TokenContext {
3786
4569
  * - $key: The current field key
3787
4570
  * - $index: The array index (if inside an array field)
3788
4571
  * - $arrayKey: The parent array field key (if inside an array field)
4572
+ * - $template: The template for array item creation
3789
4573
  * - formValue: Reference to the current form value for indexing
3790
4574
  *
3791
4575
  * @param args - Array of arguments that may contain tokens
@@ -3799,6 +4583,10 @@ interface TokenContext {
3799
4583
  * @example
3800
4584
  * resolveTokens(['$key', 'static'], { key: 'myField' })
3801
4585
  * // Returns: ['myField', 'static']
4586
+ *
4587
+ * @example
4588
+ * resolveTokens(['$arrayKey', '$template'], { arrayKey: 'contacts', template: [{ key: 'name', type: 'input' }] })
4589
+ * // Returns: ['contacts', [{ key: 'name', type: 'input' }]]
3802
4590
  */
3803
4591
  declare function resolveTokens(args: readonly (string | number | boolean | null | undefined)[], context: TokenContext): (string | number | boolean | null | undefined | unknown)[];
3804
4592
 
@@ -3873,6 +4661,64 @@ declare function formConfig<const TFields extends NarrowFields, TProps extends o
3873
4661
  schema?: FormSchema<InferFormValue$1<TFields>>;
3874
4662
  }): FormConfig<TFields, InferFormValue$1<TFields>, TProps>;
3875
4663
 
4664
+ /**
4665
+ * Creates a typed field configuration with helpful error messages for common mistakes.
4666
+ *
4667
+ * This helper function provides early validation and clear error messages
4668
+ * for common configuration pitfalls that would otherwise cause runtime errors.
4669
+ *
4670
+ * @example
4671
+ * ```typescript
4672
+ * // Basic usage
4673
+ * const nameField = createField('input', {
4674
+ * key: 'name',
4675
+ * label: 'Name',
4676
+ * value: ''
4677
+ * });
4678
+ *
4679
+ * // With validation
4680
+ * const emailField = createField('input', {
4681
+ * key: 'email',
4682
+ * label: 'Email',
4683
+ * required: true,
4684
+ * email: true,
4685
+ * props: { type: 'email' }
4686
+ * });
4687
+ *
4688
+ * // Select with options (at field level)
4689
+ * const countryField = createField('select', {
4690
+ * key: 'country',
4691
+ * label: 'Country',
4692
+ * options: [{ label: 'USA', value: 'us' }]
4693
+ * });
4694
+ *
4695
+ * // Slider with minValue/maxValue (at field level)
4696
+ * const ratingField = createField('slider', {
4697
+ * key: 'rating',
4698
+ * label: 'Rating',
4699
+ * minValue: 1,
4700
+ * maxValue: 10,
4701
+ * step: 1,
4702
+ * value: 5
4703
+ * });
4704
+ * ```
4705
+ *
4706
+ * @param type - The field type (e.g., 'input', 'select', 'group')
4707
+ * @param config - The field configuration (excluding the 'type' property)
4708
+ * @returns A properly typed field configuration
4709
+ * @throws DynamicFormError if the configuration contains common mistakes
4710
+ */
4711
+ declare function createField<T extends AvailableFieldTypes>(type: T, config: Omit<ExtractField<T>, 'type'>): ExtractField<T>;
4712
+ /**
4713
+ * Shorthand alias for createField
4714
+ *
4715
+ * @example
4716
+ * ```typescript
4717
+ * const nameField = field('input', { key: 'name', label: 'Name', value: '' });
4718
+ * ```
4719
+ */
4720
+ declare const field: typeof createField;
4721
+
3876
4722
  /**
3877
4723
  * Base error class for all Dynamic Forms errors.
3878
4724
  *
@@ -3957,8 +4803,14 @@ declare function pageFieldMapper(fieldDef: PageField): Signal<Record<string, unk
3957
4803
  * Maps a text field definition to component inputs.
3958
4804
  *
3959
4805
  * Text fields are display-only fields that don't participate in the form schema.
3960
- * This mapper handles the `logic` configuration by creating reactive signals
3961
- * that evaluate conditions using the form value from RootFormRegistryService.
4806
+ * This mapper handles the `logic` configuration by using the non-field-hidden resolver
4807
+ * to evaluate conditions against the form value from RootFormRegistryService.
4808
+ *
4809
+ * Hidden state is resolved using the non-field-hidden resolver which considers:
4810
+ * 1. Explicit `hidden: true` on the field definition
4811
+ * 2. Field-level `logic` array with `type: 'hidden'` conditions
4812
+ *
4813
+ * Note: Text fields don't support disabled logic since they are display-only.
3962
4814
  *
3963
4815
  * @param fieldDef The text field definition
3964
4816
  * @returns Signal containing Record of input names to values for ngComponentOutlet
@@ -4079,9 +4931,7 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
4079
4931
  private readonly injector;
4080
4932
  private readonly eventBus;
4081
4933
  private readonly logger;
4082
- private readonly memoizedFlattenFields;
4083
- private readonly memoizedKeyBy;
4084
- private readonly memoizedDefaultValues;
4934
+ private readonly fieldProcessors;
4085
4935
  field: _angular_core.InputSignal<GroupField<readonly _ng_forge_dynamic_forms.GroupAllowedChildren[]>>;
4086
4936
  key: _angular_core.InputSignal<string>;
4087
4937
  className: _angular_core.InputSignal<string | undefined>;
@@ -4089,6 +4939,11 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
4089
4939
  private readonly rawFieldRegistry;
4090
4940
  private readonly formSetup;
4091
4941
  readonly defaultValues: _angular_core.WritableSignal<Record<string, unknown>>;
4942
+ /**
4943
+ * Entity computed from parent value, group key, and defaults.
4944
+ * Uses deep equality check to prevent unnecessary updates when
4945
+ * object spread creates new references with identical values.
4946
+ */
4092
4947
  private readonly entity;
4093
4948
  private readonly form;
4094
4949
  readonly formValue: _angular_core.Signal<{}>;
@@ -4099,6 +4954,7 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
4099
4954
  readonly errors: _angular_core.Signal<_angular_forms_signals.ValidationError.WithField[]>;
4100
4955
  readonly disabled: _angular_core.Signal<boolean>;
4101
4956
  private readonly nestedFieldTree;
4957
+ private readonly groupFieldSignalContext;
4102
4958
  private readonly groupInjector;
4103
4959
  readonly validityChange: _angular_core.OutputRef<boolean>;
4104
4960
  readonly dirtyChange: _angular_core.OutputRef<boolean>;
@@ -4112,13 +4968,9 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
4112
4968
  }
4113
4969
 
4114
4970
  /**
4115
- * Resolved array item ready for declarative rendering with ngComponentOutlet.
4116
- * Each item has a stable unique ID and a linkedSignal-based index that
4117
- * automatically updates when items are added/removed.
4971
+ * A single field within a resolved array item.
4118
4972
  */
4119
- interface ResolvedArrayItem {
4120
- /** Unique identifier for this item (stable across position changes). */
4121
- id: string;
4973
+ interface ResolvedArrayItemField {
4122
4974
  /** The loaded component type. */
4123
4975
  component: Type<unknown>;
4124
4976
  /** Injector providing ARRAY_CONTEXT and FIELD_SIGNAL_CONTEXT. */
@@ -4126,13 +4978,26 @@ interface ResolvedArrayItem {
4126
4978
  /** Inputs signal for ngComponentOutlet - evaluated in template for reactivity. */
4127
4979
  inputs: Signal<Record<string, unknown>>;
4128
4980
  }
4981
+ /**
4982
+ * Resolved array item ready for declarative rendering with ngComponentOutlet.
4983
+ * Each item has a stable unique ID and a linkedSignal-based index that
4984
+ * automatically updates when items are added/removed.
4985
+ * Supports multiple fields per array item (e.g., name + email without a wrapper).
4986
+ */
4987
+ interface ResolvedArrayItem {
4988
+ /** Unique identifier for this item (stable across position changes). */
4989
+ id: string;
4990
+ /** All fields to render for this array item. */
4991
+ fields: ResolvedArrayItemField[];
4992
+ }
4129
4993
 
4130
4994
  /**
4131
4995
  * Container component for rendering dynamic arrays of fields.
4132
4996
  *
4133
- * Supports add/remove operations via AddArrayItemEvent and RemoveArrayItemEvent.
4997
+ * Supports add/remove operations via the arrayEvent() builder API.
4134
4998
  * Uses differential updates to optimize rendering - only recreates items when necessary.
4135
4999
  * Each item gets a scoped injector with ARRAY_CONTEXT for position-aware operations.
5000
+ * Supports multiple sibling fields per array item (e.g., name + email without a wrapper).
4136
5001
  */
4137
5002
  declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Record<string, unknown>> {
4138
5003
  private readonly destroyRef;
@@ -4141,25 +5006,62 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
4141
5006
  private readonly parentInjector;
4142
5007
  private readonly eventBus;
4143
5008
  private readonly logger;
4144
- field: _angular_core.InputSignal<ArrayField<readonly _ng_forge_dynamic_forms.ArrayAllowedChildren[]>>;
5009
+ private readonly templateRegistry;
5010
+ private readonly generateItemId;
5011
+ field: _angular_core.InputSignal<ArrayField<readonly ArrayItemDefinition[]>>;
4145
5012
  key: _angular_core.InputSignal<string>;
4146
5013
  className: _angular_core.InputSignal<string | undefined>;
4147
5014
  readonly hostClasses: _angular_core.Signal<string>;
4148
5015
  private readonly rawFieldRegistry;
4149
- private readonly fieldTemplate;
5016
+ /**
5017
+ * Gets the item templates (field definitions) for the array.
5018
+ * Each element can be either:
5019
+ * - A single FieldDef (primitive item) - normalized to [FieldDef]
5020
+ * - An array of FieldDefs (object item) - used as-is
5021
+ *
5022
+ * Returns normalized templates where all items are arrays for consistent handling.
5023
+ */
5024
+ private readonly itemTemplates;
4150
5025
  private readonly arrayFieldTrees;
4151
5026
  private readonly resolvedItemsSignal;
4152
5027
  private readonly updateVersion;
4153
- private readonly itemOrderSignal;
4154
- readonly resolvedItems: _angular_core.WritableSignal<ResolvedArrayItem[]>;
5028
+ /**
5029
+ * Map of item IDs to their current positions. O(1) lookup vs O(n) indexOf().
5030
+ * Used by child linkedSignals to reactively track their position in the array.
5031
+ */
5032
+ private readonly itemPositionMap;
5033
+ /** Read-only view of resolved items for template consumption. */
5034
+ readonly resolvedItems: _angular_core.Signal<ResolvedArrayItem[]>;
4155
5035
  constructor();
4156
5036
  private setupEffects;
4157
5037
  private setupEventHandlers;
5038
+ /**
5039
+ * Handles add operations from events (append, prepend, insert).
5040
+ * Creates resolved items FIRST, then updates form value.
5041
+ * This ensures prepend/insert work correctly - differential update sees "none"
5042
+ * because resolved items count already matches the new array length.
5043
+ *
5044
+ * Supports both primitive (single FieldDef) and object (FieldDef[]) templates.
5045
+ */
5046
+ private handleAddFromEvent;
4158
5047
  private performDifferentialUpdate;
4159
5048
  private resolveAllItems;
4160
5049
  private appendItems;
5050
+ /**
5051
+ * Creates an observable that resolves a single array item.
5052
+ *
5053
+ * Template resolution order:
5054
+ * 1. Use overrideTemplate if provided (from recreate with stored templates)
5055
+ * 2. Use itemTemplates[index] if within defined templates range
5056
+ * 3. Return undefined (item cannot be resolved without a template)
5057
+ */
4161
5058
  private createResolveItemObservable;
4162
- private addItem;
5059
+ /**
5060
+ * Handles remove operations from events (pop, shift, removeAt).
5061
+ * Updates resolvedItems FIRST, then form value - this ensures differential
5062
+ * update sees "none" (lengths match) and avoids unnecessary recreates.
5063
+ * Remaining items' linkedSignal indices auto-update via itemPositionMap.
5064
+ */
4163
5065
  private removeItem;
4164
5066
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArrayFieldComponent<any>, never>;
4165
5067
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArrayFieldComponent<any>, "array-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
@@ -4237,97 +5139,16 @@ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): O
4237
5139
  declare function interpolateParams(message: string, error: ValidationError): string;
4238
5140
 
4239
5141
  /**
4240
- * Registry service that maintains references to root form fields.
4241
- * This provides a type-safe way to access root form values from child fields
4242
- * without relying on internal Angular Forms API navigation.
4243
- *
4244
- * This service should be provided at the component level to ensure proper
4245
- * isolation between different form instances.
5142
+ * Registry service that provides access to the root form and its values.
4246
5143
  *
4247
- * IMPORTANT: This registry supports two registration patterns:
4248
- * 1. registerFormValueSignal() - Register the SOURCE signal BEFORE form creation
4249
- * This allows logic functions to read form values during schema evaluation.
4250
- * 2. registerRootForm() - Register the FieldTree AFTER form creation
4251
- * This provides access to the full form structure.
4252
- *
4253
- * For cross-field logic to work correctly, registerFormValueSignal() should be
4254
- * called BEFORE the form() call.
5144
+ * Constructed via factory in `provideDynamicFormDI` with signals from
5145
+ * `FormStateManager` — no DI injection, no Maps, no lifecycle.
4255
5146
  */
4256
5147
  declare class RootFormRegistryService {
4257
- private readonly _rootForms;
4258
- private readonly _formValueSignals;
4259
- private readonly formContexts;
4260
- /**
4261
- * Registers the form value signal BEFORE form creation.
4262
- *
4263
- * This is the recommended pattern for cross-field logic:
4264
- * 1. Create the formValue signal
4265
- * 2. Register it with registerFormValueSignal()
4266
- * 3. Create the form with form(formValue, schema(...))
4267
- *
4268
- * This allows logic functions to access form values during schema evaluation.
4269
- */
4270
- registerFormValueSignal(valueSignal: Signal<Record<string, unknown>>, formId?: string): void;
4271
- /**
4272
- * Gets the registered form value signal by id.
4273
- *
4274
- * This allows direct access to the source signal, which can be read
4275
- * reactively to get form values.
4276
- */
4277
- getFormValueSignal(formId?: string): Signal<Record<string, unknown>> | undefined;
4278
- /**
4279
- * Registers a root form field with an optional identifier.
4280
- * If no id is provided, uses 'default' as the key.
4281
- *
4282
- * This triggers reactive updates for any computed signals that
4283
- * depend on getRootForm().
4284
- */
4285
- registerRootForm(rootField: FieldTree<unknown>, formId?: string): void;
4286
- /**
4287
- * Gets a registered root form field by id.
4288
- *
4289
- * IMPORTANT: This method reads from a signal, which means any computed
4290
- * signal that calls this method will automatically re-evaluate when
4291
- * the form is registered or updated.
4292
- */
4293
- getRootForm(formId?: string): FieldTree<unknown> | undefined;
4294
- /**
4295
- * Gets the form value, preferring the direct signal if available.
4296
- *
4297
- * This method tries in order:
4298
- * 1. Form value signal (if registered) - reads directly from source signal
4299
- * 2. Root form's .value() (if registered) - reads from FieldTree
4300
- * 3. Returns empty object if nothing is registered
4301
- *
4302
- * Reading from signals creates reactive dependencies, so computeds will
4303
- * re-evaluate when form values change.
4304
- */
4305
- getFormValue(formId?: string): Record<string, unknown>;
4306
- /**
4307
- * Registers form context (field paths and metadata) for a specific form.
4308
- * This can be used to store additional context information.
4309
- */
4310
- registerFormContext(context: Record<string, unknown>, formId?: string): void;
4311
- /**
4312
- * Gets form context by id.
4313
- */
4314
- getFormContext(formId?: string): Record<string, unknown>;
4315
- /**
4316
- * Unregisters a form and its context.
4317
- * This should be called when a form component is destroyed to prevent memory leaks.
4318
- */
4319
- unregisterForm(formId?: string): void;
4320
- /**
4321
- * Gets all registered form IDs.
4322
- */
4323
- getRegisteredFormIds(): string[];
4324
- /**
4325
- * Clears all registered forms. Useful for testing.
4326
- */
4327
- clear(): void;
4328
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<RootFormRegistryService, never>;
4329
- static ɵprov: _angular_core.ɵɵInjectableDeclaration<RootFormRegistryService>;
5148
+ readonly formValue: Signal<Record<string, unknown>>;
5149
+ readonly rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>;
5150
+ constructor(formValue: Signal<Record<string, unknown>>, rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>);
4330
5151
  }
4331
5152
 
4332
- export { ARRAY_CONTEXT, AddArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GroupFieldComponent, NextPageEvent, NoopLogger, PageChangeEvent, PreviousPageEvent, RemoveArrayItemEvent, RootFormRegistryService, RowFieldComponent, SubmitEvent, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, baseFieldMapper, buildBaseInputs, dynamicTextToObservable, formConfig, getArrayLength, groupFieldMapper, interpolateParams, isArrayField, isCheckedField, isContainerField, isDisplayOnlyField, isEqual, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isValueBearingField, isValueField, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, withLoggerConfig };
4333
- export type { ArrayAllowedChildren, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, AsyncCustomValidator, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCustomValidator, HttpResourceRequest, HttpValidatorConfig, InferFormValue$1 as InferFormValue, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, PageAllowedChildren, PageField, Prettify, RegisteredFieldTypes, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };
5153
+ export { ARRAY_CONTEXT, AppendArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GroupFieldComponent, InsertArrayItemEvent, NextPageEvent, NoopLogger, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, RootFormRegistryService, RowFieldComponent, ShiftArrayItemEvent, SubmitEvent, applyMetaToElement, applyValidator, applyValidators, arrayEvent, arrayFieldMapper, baseFieldMapper, buildBaseInputs, createField, dynamicTextToObservable, field, formConfig, getArrayLength, groupFieldMapper, hasFormValue, interpolateParams, isArrayField, isCheckedField, isContainerField, isDisplayOnlyField, isEqual, isFormStateCondition, isGroupField, isLeafField, isPageField, isPropertyDerivationLogicConfig, isRowField, isValueBearingField, isValueField, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, withEventFormValue, withLoggerConfig };
5154
+ export type { ArrayAllowedChildren, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, ArrayItemDefinitionTemplate, ArrayItemTemplate, AsyncCustomValidator, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCustomValidator, HttpResourceRequest, HttpValidatorConfig, InferFormValue$1 as InferFormValue, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, PageAllowedChildren, PageField, Prettify, PropertyDerivationLogicConfig, RegisteredFieldTypes, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };