@ng-forge/dynamic-forms 0.9.0-next.11 → 0.9.0-next.13

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.
Files changed (33) hide show
  1. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-DJ-LiLm_.mjs → ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs} +158 -73
  2. package/fesm2022/ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs.map +1 -0
  3. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-Pcxg5PQu.mjs → ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs} +12 -8
  4. package/fesm2022/ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs.map +1 -0
  5. package/fesm2022/{ng-forge-dynamic-forms-container-utils-a4RstR2Z.mjs → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs} +2 -2
  6. package/fesm2022/{ng-forge-dynamic-forms-container-utils-a4RstR2Z.mjs.map → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs.map} +1 -1
  7. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-CJACCIEK.mjs → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs} +5 -5
  8. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-CJACCIEK.mjs.map → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs.map} +1 -1
  9. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-Bjf_I0D_.mjs → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs} +2 -2
  10. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-Bjf_I0D_.mjs.map → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs.map} +1 -1
  11. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-fzKwPrQr.mjs → ng-forge-dynamic-forms-group-field.component-C9xUHrXp.mjs} +15 -11
  12. package/fesm2022/ng-forge-dynamic-forms-group-field.component-C9xUHrXp.mjs.map +1 -0
  13. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +334 -27
  14. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  15. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-o3rJX4vG.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BbgkzKuI.mjs} +1941 -407
  16. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BbgkzKuI.mjs.map +1 -0
  17. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BwNIx3R1.mjs +114 -0
  18. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BwNIx3R1.mjs.map +1 -0
  19. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs +34 -0
  20. package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-BIvEUoia.mjs.map → ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs.map} +1 -1
  21. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-B9SK8Vun.mjs → ng-forge-dynamic-forms-text-field.component-CpgtswdO.mjs} +5 -5
  22. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-B9SK8Vun.mjs.map → ng-forge-dynamic-forms-text-field.component-CpgtswdO.mjs.map} +1 -1
  23. package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
  24. package/package.json +1 -1
  25. package/types/ng-forge-dynamic-forms-integration.d.ts +241 -37
  26. package/types/ng-forge-dynamic-forms.d.ts +2320 -1160
  27. package/fesm2022/ng-forge-dynamic-forms-array-field.component-DJ-LiLm_.mjs.map +0 -1
  28. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Pcxg5PQu.mjs.map +0 -1
  29. package/fesm2022/ng-forge-dynamic-forms-group-field.component-fzKwPrQr.mjs.map +0 -1
  30. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-o3rJX4vG.mjs.map +0 -1
  31. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BX4R9GGa.mjs +0 -110
  32. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BX4R9GGa.mjs.map +0 -1
  33. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BIvEUoia.mjs +0 -34
@@ -1,13 +1,155 @@
1
1
  import * as rxjs from 'rxjs';
2
2
  import { Observable } from 'rxjs';
3
3
  import * as _angular_core from '@angular/core';
4
- import { Signal, ResourceRef, Type, ViewContainerRef, InjectionToken, Injector, WritableSignal, EnvironmentInjector, Provider, EnvironmentProviders, Resource, PipeTransform } from '@angular/core';
4
+ import { Signal, Type, ResourceRef, ViewContainerRef, InjectionToken, Injector, WritableSignal, EnvironmentInjector, Provider, EnvironmentProviders, TemplateRef, Resource, PipeTransform } from '@angular/core';
5
5
  import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
6
6
  import * as _angular_forms_signals from '@angular/forms/signals';
7
7
  import { FieldContext, TreeValidationResult, ValidationError as ValidationError$1, FieldTree, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
8
8
  import { FormSchema } from '@ng-forge/dynamic-forms/schema';
9
9
  import * as _angular_forms from '@angular/forms';
10
10
 
11
+ type DynamicText = string | Observable<string> | Signal<string>;
12
+
13
+ /**
14
+ * Generic reactive value — a static value, an Angular Signal, or an RxJS Observable.
15
+ *
16
+ * Generalises {@link DynamicText} for arbitrary value types. Used by
17
+ * cross-cutting properties that need to react to form state without forcing
18
+ * every consumer to deal with reactivity (e.g., `hidden` / `disabled` on
19
+ * addons).
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const a: DynamicValue<boolean> = true;
24
+ * const b: DynamicValue<boolean> = signal(false);
25
+ * const c: DynamicValue<boolean> = isValid$;
26
+ * ```
27
+ */
28
+ type DynamicValue<T> = T | Signal<T> | Observable<T>;
29
+
30
+ /**
31
+ * Universal addon slot names supported by every adapter.
32
+ *
33
+ * Adapters render `prefix` to the leading edge of a field and `suffix` to
34
+ * the trailing edge (mirrored in RTL). Additional slots may be contributed
35
+ * via {@link DynamicFormAddonSlotRegistry} module augmentation.
36
+ */
37
+ type CommonAddonSlot = 'prefix' | 'suffix';
38
+ /**
39
+ * Module-augmentable registry of adapter-specific addon slot names.
40
+ *
41
+ * Adapters that expose slots beyond the universal `prefix` / `suffix` (e.g.,
42
+ * an "error overlay" slot specific to one design system) declare them here.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * declare module '@ng-forge/dynamic-forms' {
47
+ * interface DynamicFormAddonSlotRegistry {
48
+ * 'mat-error-overlay': true;
49
+ * }
50
+ * }
51
+ * ```
52
+ */
53
+ interface DynamicFormAddonSlotRegistry {
54
+ }
55
+ /**
56
+ * Union of every valid addon slot — universal slots plus any registered via
57
+ * {@link DynamicFormAddonSlotRegistry} module augmentation.
58
+ */
59
+ type AddonSlot = CommonAddonSlot | keyof DynamicFormAddonSlotRegistry;
60
+
61
+ /**
62
+ * Base shape every addon kind extends.
63
+ *
64
+ * Public contract:
65
+ * - `slot` chooses where the addon renders (prefix / suffix / adapter-specific).
66
+ * - `hidden` / `disabled` are reactive — re-evaluated when their underlying
67
+ * signals or observables emit.
68
+ * - Multiple addons in the same slot render in array order, left-to-right
69
+ * within the slot (LTR; mirrored in RTL).
70
+ *
71
+ * Note: `meta` is intentionally absent — adapters declare addon-specific
72
+ * fields via module augmentation rather than an untyped escape hatch.
73
+ */
74
+ interface BaseAddon<TSlot extends AddonSlot = AddonSlot> {
75
+ /** Slot to render this addon in. */
76
+ readonly slot: TSlot;
77
+ /** CSS class applied to the addon host element. */
78
+ readonly className?: string;
79
+ /** Reactive visibility — when `true`, the addon is removed from layout. */
80
+ readonly hidden?: DynamicValue<boolean>;
81
+ /** Reactive disabled state — primarily relevant for interactive kinds. */
82
+ readonly disabled?: DynamicValue<boolean>;
83
+ }
84
+ /**
85
+ * Module-augmentable registry of addon kinds.
86
+ *
87
+ * Each key is the kind discriminant; each value is the addon's full shape
88
+ * (extending {@link BaseAddon}). Core ships `text`, `template`, and
89
+ * `component`; adapters add their own (e.g., `prime-icon`, `prime-button`).
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * declare module '@ng-forge/dynamic-forms' {
94
+ * interface DynamicFormAddonRegistry {
95
+ * 'prime-icon': PrimeIconAddon;
96
+ * 'prime-button': PrimeButtonAddon;
97
+ * }
98
+ * }
99
+ * ```
100
+ */
101
+ interface DynamicFormAddonRegistry {
102
+ text: TextAddon;
103
+ template: TemplateAddon;
104
+ component: ComponentAddon;
105
+ }
106
+ /**
107
+ * Union of every registered addon shape.
108
+ */
109
+ type AnyAddon = DynamicFormAddonRegistry[keyof DynamicFormAddonRegistry];
110
+ /**
111
+ * Renders text content inline.
112
+ *
113
+ * `text` is `DynamicText`, supporting plain strings, observables, and
114
+ * signals — so values like `'$'`, `'.com'`, or interpolated counters
115
+ * (`'42/100'`) all work uniformly.
116
+ */
117
+ interface TextAddon extends BaseAddon {
118
+ readonly kind: 'text';
119
+ readonly text: DynamicText;
120
+ }
121
+ /**
122
+ * Renders a named `<ng-template>` projected into the host `<df-dynamic-form>`.
123
+ *
124
+ * JSON-safe — backends ship the key, FE supplies the template definition.
125
+ * Use the `templateAddon(ref)` helper for rename-safe authoring in code.
126
+ */
127
+ interface TemplateAddon extends BaseAddon {
128
+ readonly kind: 'template';
129
+ readonly templateKey: string;
130
+ }
131
+ /**
132
+ * Renders an arbitrary Angular component as the addon body.
133
+ *
134
+ * The loader returns either a component class directly or a module with a
135
+ * `default` export. For non-default exports, unwrap with `.then(m => m.X)`:
136
+ *
137
+ * ```ts
138
+ * { kind: 'component', component: () => import('./my').then(m => m.MyComponent) }
139
+ * ```
140
+ *
141
+ * @codeOnly The `component` loader is a function and cannot survive
142
+ * `JSON.stringify`/`parse`. The lenient validator drops this kind when the
143
+ * config originated from JSON (set via `sanitizeFormConfig({ source: 'json' })`).
144
+ */
145
+ interface ComponentAddon extends BaseAddon {
146
+ readonly kind: 'component';
147
+ readonly component: () => Promise<Type<unknown> | {
148
+ default: Type<unknown>;
149
+ }>;
150
+ readonly inputs?: Record<string, unknown>;
151
+ }
152
+
11
153
  type WithInputSignals<T> = {
12
154
  readonly [K in keyof T]-?: Signal<T[K]>;
13
155
  };
@@ -16,8 +158,6 @@ type Prettify<T> = {
16
158
  [K in keyof T]: T[K];
17
159
  } & {};
18
160
 
19
- type DynamicText = string | Observable<string> | Signal<string>;
20
-
21
161
  /**
22
162
  * Interface for dynamic forms logger implementations.
23
163
  * Allows custom logging integrations (Sentry, DataDog, etc.)
@@ -589,351 +729,116 @@ interface ContainerLogicConfig {
589
729
  }
590
730
 
591
731
  /**
592
- * An array item template is an array of allowed children that defines one OBJECT array item's fields.
593
- * Used for object arrays where each item is `{ key1: value1, key2: value2, ... }`.
732
+ * Row field interface for creating horizontal layouts.
733
+ * A row is a synthetic field type that resolves to a Container at runtime,
734
+ * with a synthesized `{ type: 'row' }` wrapper applied for layout. The row
735
+ * itself does not hold a value — its children flatten into the parent form.
736
+ *
737
+ * Note: Rows do not support `meta` since they have no native form element.
594
738
  */
595
- type ArrayItemTemplate = readonly ArrayAllowedChildren[];
739
+ interface RowField<TFields extends readonly RowAllowedChildren[] = readonly RowAllowedChildren[]> extends FieldDef<never> {
740
+ type: 'row';
741
+ /** Child definitions to render within this row */
742
+ readonly fields: TFields;
743
+ /** Row fields do not have a label property **/
744
+ readonly label?: never;
745
+ /** Rows do not support meta - they have no native form element **/
746
+ readonly meta?: never;
747
+ /**
748
+ * Logic configurations for conditional row visibility.
749
+ * Only 'hidden' type logic is supported for rows.
750
+ */
751
+ readonly logic?: ContainerLogicConfig[];
752
+ }
596
753
  /**
597
- * An array item definition can be either:
598
- * - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
599
- * - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
600
- *
601
- * This allows support for:
602
- * - Primitive arrays: `['tag1', 'tag2']`
603
- * - Object arrays: `[{ name: 'Alice', email: '...' }]`
604
- * - Heterogeneous arrays: `[{ value: 'x' }, 'y']` (mixed primitives and objects)
754
+ * Type guard for RowField with proper type narrowing
605
755
  */
606
- type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate;
756
+ declare function isRowField(field: FieldDef<any>): field is RowField;
757
+
607
758
  /**
608
- * Array field interface for creating dynamic field collections that map to array values.
609
- *
610
- * Key concepts:
611
- * - The outer `fields` array defines INITIAL ITEMS (each element is one array item to render)
612
- * - Each element can be either:
613
- * - A single FieldDef (primitive item) - the field's value is extracted directly
614
- * - An array of FieldDefs (object item) - fields are merged into an object
615
- * - Items can have different field configurations (heterogeneous arrays supported)
616
- * - Empty `fields: []` means no initial items - user adds via buttons with explicit templates
617
- * - Initial values are declared via `value` property on each field definition
618
- *
619
- * @example
620
- * // Primitive array: ['angular', 'typescript']
621
- * {
622
- * key: 'tags',
623
- * type: 'array',
624
- * fields: [
625
- * { key: 'tag', type: 'input', value: 'angular' }, // Single field = primitive
626
- * { key: 'tag', type: 'input', value: 'typescript' },
627
- * ]
628
- * }
629
- *
630
- * @example
631
- * // Object array: [{ name: 'Alice', email: '...' }]
632
- * {
633
- * key: 'contacts',
634
- * type: 'array',
635
- * fields: [
636
- * [ // Array = object
637
- * { key: 'name', type: 'input', value: 'Alice' },
638
- * { key: 'email', type: 'input', value: 'alice@example.com' }
639
- * ],
640
- * ]
641
- * }
642
- *
643
- * @example
644
- * // Heterogeneous array: [{ label: 'Structured' }, 'Simple']
645
- * {
646
- * key: 'items',
647
- * type: 'array',
648
- * fields: [
649
- * [{ key: 'label', type: 'input', value: 'Structured' }], // Object item
650
- * { key: 'value', type: 'input', value: 'Simple' }, // Primitive item
651
- * ]
652
- * }
653
- *
654
- * @example
655
- * // Empty array (add items via buttons)
656
- * {
657
- * key: 'tags',
658
- * type: 'array',
659
- * fields: [] // No initial items - user adds via button with template
660
- * }
759
+ * Group field interface for creating logical field groupings that map to object values
760
+ * Groups create nested form structures where child field values are collected into an object
761
+ * This is a programmatic grouping only - users cannot customize this field type
661
762
  *
662
- * Nesting constraints: Arrays can contain rows, leaf fields, or groups (for object arrays),
663
- * but NOT pages or other arrays. Runtime validation enforces these rules.
763
+ * TypeScript cannot enforce field nesting rules due to circular dependency limitations.
764
+ * For documentation: Groups should contain rows and leaf fields, but NOT pages or other groups.
765
+ * Runtime validation enforces these rules.
664
766
  *
665
- * Note: Arrays are container fields and do not support `meta` since they have no native form element.
767
+ * Note: Groups are container fields and do not support `meta` since they have no native form element.
666
768
  */
667
- interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never> {
668
- type: 'array';
669
- /**
670
- * Array of item definitions. Each element defines one array item:
671
- * - Single FieldDef = primitive item (field's value is extracted directly)
672
- * - Array of FieldDefs = object item (fields are merged into an object)
673
- * - Empty array `[]` = no initial items (add via buttons with explicit templates)
674
- * - Items can have different configurations (heterogeneous arrays supported)
675
- * - Initial values are set via `value` property on each field definition
676
- */
769
+ interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never> {
770
+ type: 'group';
677
771
  readonly fields: TFields;
678
- /** Array fields do not have a label property **/
772
+ /** Groups do not have a label property - they are logical containers only **/
679
773
  readonly label?: never;
680
- /** Arrays do not support meta - they have no native form element **/
774
+ /** Groups do not support meta - they have no native form element **/
681
775
  readonly meta?: never;
682
776
  /**
683
- * Logic configurations for conditional array visibility.
684
- * Only 'hidden' type logic is supported for arrays.
777
+ * Logic configurations for conditional group visibility.
778
+ * Only 'hidden' type logic is supported for groups.
685
779
  */
686
780
  readonly logic?: ContainerLogicConfig[];
781
+ }
782
+ /**
783
+ * Type guard for GroupField with proper type narrowing
784
+ */
785
+ declare function isGroupField(field: FieldDef<any>): field is GroupField;
786
+
787
+ /**
788
+ * Page field interface for creating top-level page layouts
789
+ * This is a special field type that contains other field definitions
790
+ * The page itself doesn't have a value - it's a layout container like row
791
+ * Pages can only be used at the top level and cannot be nested
792
+ * This is a programmatic field type only - users cannot customize this field type
793
+ *
794
+ * TypeScript cannot enforce field nesting rules due to circular dependency limitations.
795
+ * For documentation: Pages should contain rows, groups, and leaf fields, but NOT other pages.
796
+ * Runtime validation enforces these rules.
797
+ *
798
+ * Note: Pages are container fields and do not support `meta` since they have no native form element.
799
+ */
800
+ interface PageField<TFields extends readonly PageAllowedChildren[] = PageAllowedChildren[]> extends FieldDef<never> {
801
+ type: 'page';
802
+ /** Child field definitions to render within this page */
803
+ readonly fields: TFields;
804
+ /** Page fields do not have a label property **/
805
+ readonly label?: never;
806
+ /** Pages do not support meta - they have no native form element **/
807
+ readonly meta?: never;
687
808
  /**
688
- * Minimum number of items required in the array.
689
- * Validation fails if the array has fewer items than this value.
690
- */
691
- readonly minLength?: number;
692
- /**
693
- * Maximum number of items allowed in the array.
694
- * Validation fails if the array has more items than this value.
809
+ * Logic configurations for conditional page visibility.
810
+ * Only 'hidden' type logic is supported for pages.
811
+ *
812
+ * @example
813
+ * ```typescript
814
+ * {
815
+ * key: 'businessPage',
816
+ * type: 'page',
817
+ * logic: [{
818
+ * type: 'hidden',
819
+ * condition: {
820
+ * type: 'fieldValue',
821
+ * fieldPath: 'accountType',
822
+ * operator: 'notEquals',
823
+ * value: 'business',
824
+ * },
825
+ * }],
826
+ * fields: [...]
827
+ * }
828
+ * ```
695
829
  */
696
- readonly maxLength?: number;
830
+ readonly logic?: ContainerLogicConfig[];
697
831
  }
698
832
  /**
699
- * Type guard for ArrayField with proper type narrowing.
700
- * Validates that the field is an array type with a fields property that is an array.
701
- * Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
833
+ * Type guard for PageField with proper type narrowing
702
834
  */
703
- declare function isArrayField(field: FieldDef<any>): field is ArrayField;
835
+ declare function isPageField(field: FieldDef<unknown>): field is PageField;
836
+
704
837
  /**
705
- * Configuration for auto-generated add/remove buttons in simplified array fields.
838
+ * Helper type to convert union to intersection.
839
+ * Uses `U extends U` to trigger distributive conditional type behavior.
706
840
  */
707
- interface ArrayButtonConfig {
708
- /** Custom label for the button */
709
- readonly label?: string;
710
- /** Additional properties passed to the button component */
711
- readonly props?: Record<string, unknown>;
712
- }
713
- /**
714
- * Simplified array field interface for common use cases.
715
- *
716
- * Instead of manually specifying each item in `fields`, provide a `template` that defines
717
- * the structure of a single item, and a `value` array with initial data.
718
- * The library auto-generates add/remove buttons.
719
- *
720
- * Discriminant: `template` presence → simplified API; `Array.isArray(template)` → object vs primitive.
721
- *
722
- * @example
723
- * // Primitive array: ['angular', 'typescript']
724
- * {
725
- * key: 'tags',
726
- * type: 'array',
727
- * template: { key: 'value', type: 'input', label: 'Tag', required: true },
728
- * value: ['angular', 'typescript']
729
- * }
730
- *
731
- * @example
732
- * // Object array: [{ name: 'Jane', phone: '555' }]
733
- * {
734
- * key: 'contacts',
735
- * type: 'array',
736
- * template: [
737
- * { key: 'name', type: 'input', label: 'Contact Name', required: true },
738
- * { key: 'phone', type: 'input', label: 'Phone Number' },
739
- * ],
740
- * value: [{ name: 'Jane', phone: '555' }]
741
- * }
742
- *
743
- * @example
744
- * // Button customization / opt-out
745
- * {
746
- * key: 'tags',
747
- * type: 'array',
748
- * template: { key: 'value', type: 'input', label: 'Tag' },
749
- * addButton: { label: 'Add Tag', props: { color: 'primary' } },
750
- * removeButton: false
751
- * }
752
- */
753
- interface SimplifiedArrayField extends FieldDef<never> {
754
- type: 'array';
755
- /**
756
- * Template defining the structure of a single array item.
757
- * - Single field (ArrayAllowedChildren) → primitive array (each item is a single value)
758
- * - Array of fields (readonly ArrayAllowedChildren[]) → object array (each item is an object)
759
- */
760
- readonly template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
761
- /** Initial values for the array. Each element creates one array item. */
762
- readonly value?: readonly unknown[];
763
- /**
764
- * Configuration for the auto-generated "Add" button, or `false` to disable it.
765
- * Defaults to a button with label "Add".
766
- */
767
- readonly addButton?: ArrayButtonConfig | false;
768
- /**
769
- * Configuration for the auto-generated "Remove" button on each item, or `false` to disable it.
770
- * Defaults to a button with label "Remove".
771
- */
772
- readonly removeButton?: ArrayButtonConfig | false;
773
- /** Simplified arrays do not support the label property */
774
- readonly label?: never;
775
- /** Simplified arrays do not support meta */
776
- readonly meta?: never;
777
- /**
778
- * Logic configurations for conditional array visibility.
779
- * Only 'hidden' type logic is supported for arrays.
780
- */
781
- readonly logic?: ContainerLogicConfig[];
782
- /**
783
- * Minimum number of items required in the array.
784
- * Validation fails if the array has fewer items than this value.
785
- */
786
- readonly minLength?: number;
787
- /**
788
- * Maximum number of items allowed in the array.
789
- * Validation fails if the array has more items than this value.
790
- */
791
- readonly maxLength?: number;
792
- /** Mutually exclusive with `template` — use `fields` for the full API instead */
793
- readonly fields?: never;
794
- }
795
- /**
796
- * Type guard for SimplifiedArrayField.
797
- * Checks for `type: 'array'` with a `template` property (discriminant from full ArrayField).
798
- */
799
- declare function isSimplifiedArrayField(field: FieldDef<any>): field is SimplifiedArrayField;
800
-
801
- /**
802
- * Group field interface for creating logical field groupings that map to object values
803
- * Groups create nested form structures where child field values are collected into an object
804
- * This is a programmatic grouping only - users cannot customize this field type
805
- *
806
- * TypeScript cannot enforce field nesting rules due to circular dependency limitations.
807
- * For documentation: Groups should contain rows and leaf fields, but NOT pages or other groups.
808
- * Runtime validation enforces these rules.
809
- *
810
- * Note: Groups are container fields and do not support `meta` since they have no native form element.
811
- */
812
- interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never> {
813
- type: 'group';
814
- readonly fields: TFields;
815
- /** Groups do not have a label property - they are logical containers only **/
816
- readonly label?: never;
817
- /** Groups do not support meta - they have no native form element **/
818
- readonly meta?: never;
819
- /**
820
- * Logic configurations for conditional group visibility.
821
- * Only 'hidden' type logic is supported for groups.
822
- */
823
- readonly logic?: ContainerLogicConfig[];
824
- }
825
- /**
826
- * Type guard for GroupField with proper type narrowing
827
- */
828
- declare function isGroupField(field: FieldDef<any>): field is GroupField;
829
-
830
- /**
831
- * Scalar types supported by hidden fields.
832
- * These are the primitive value types that can be stored.
833
- */
834
- type HiddenScalar = string | number | boolean;
835
- /**
836
- * All value types supported by hidden fields.
837
- * Supports both scalar values and arrays of scalars for storing
838
- * multiple IDs or similar data.
839
- */
840
- type HiddenValue = HiddenScalar | HiddenScalar[];
841
- /**
842
- * Hidden field definition for storing values without rendering UI.
843
- *
844
- * Hidden fields participate in the form schema and contribute to form values,
845
- * but do not render any visible component. They are useful for:
846
- * - Storing IDs when updating existing records
847
- * - Persisting metadata that shouldn't be user-editable
848
- * - Tracking computed values that need to be submitted
849
- *
850
- * Unlike the `hidden` property on other fields (which hides a rendered field),
851
- * this field type renders nothing at all - it's a componentless field.
852
- *
853
- * @example
854
- * ```typescript
855
- * // Store a record ID for updates
856
- * const idField: HiddenField<string> = {
857
- * type: 'hidden',
858
- * key: 'id',
859
- * value: '550e8400-e29b-41d4-a716-446655440000',
860
- * };
861
- *
862
- * // Store multiple tag IDs
863
- * const tagIdsField: HiddenField<number[]> = {
864
- * type: 'hidden',
865
- * key: 'tagIds',
866
- * value: [1, 2, 3],
867
- * };
868
- * ```
869
- *
870
- * @typeParam TValue - The type of value stored (must be HiddenValue compatible)
871
- */
872
- interface HiddenField<TValue extends HiddenValue = HiddenValue> extends FieldDef<never> {
873
- /** Discriminant for hidden field type */
874
- type: 'hidden';
875
- /**
876
- * The value to store in the form.
877
- * This is required - a hidden field without a value serves no purpose.
878
- */
879
- value: TValue;
880
- }
881
-
882
- /**
883
- * Page field interface for creating top-level page layouts
884
- * This is a special field type that contains other field definitions
885
- * The page itself doesn't have a value - it's a layout container like row
886
- * Pages can only be used at the top level and cannot be nested
887
- * This is a programmatic field type only - users cannot customize this field type
888
- *
889
- * TypeScript cannot enforce field nesting rules due to circular dependency limitations.
890
- * For documentation: Pages should contain rows, groups, and leaf fields, but NOT other pages.
891
- * Runtime validation enforces these rules.
892
- *
893
- * Note: Pages are container fields and do not support `meta` since they have no native form element.
894
- */
895
- interface PageField<TFields extends readonly PageAllowedChildren[] = PageAllowedChildren[]> extends FieldDef<never> {
896
- type: 'page';
897
- /** Child field definitions to render within this page */
898
- readonly fields: TFields;
899
- /** Page fields do not have a label property **/
900
- readonly label?: never;
901
- /** Pages do not support meta - they have no native form element **/
902
- readonly meta?: never;
903
- /**
904
- * Logic configurations for conditional page visibility.
905
- * Only 'hidden' type logic is supported for pages.
906
- *
907
- * @example
908
- * ```typescript
909
- * {
910
- * key: 'businessPage',
911
- * type: 'page',
912
- * logic: [{
913
- * type: 'hidden',
914
- * condition: {
915
- * type: 'fieldValue',
916
- * fieldPath: 'accountType',
917
- * operator: 'notEquals',
918
- * value: 'business',
919
- * },
920
- * }],
921
- * fields: [...]
922
- * }
923
- * ```
924
- */
925
- readonly logic?: ContainerLogicConfig[];
926
- }
927
- /**
928
- * Type guard for PageField with proper type narrowing
929
- */
930
- declare function isPageField(field: FieldDef<unknown>): field is PageField;
931
-
932
- /**
933
- * Helper type to convert union to intersection.
934
- * Uses `U extends U` to trigger distributive conditional type behavior.
935
- */
936
- type UnionToIntersection<U> = (U extends U ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
841
+ type UnionToIntersection<U> = (U extends U ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
937
842
  /**
938
843
  * Depth counter for recursion limiting (prevents infinite type instantiation)
939
844
  */
@@ -2420,7 +2325,6 @@ type SubmissionActionResult = Promise<TreeValidationResult> | Observable<TreeVal
2420
2325
  * @typeParam TValue - The form value type
2421
2326
  *
2422
2327
  * @public
2423
- * @experimental
2424
2328
  */
2425
2329
  interface SubmissionConfig<TValue = unknown> {
2426
2330
  /**
@@ -3434,18 +3338,70 @@ interface TextField extends FieldDef<TextProps> {
3434
3338
  }
3435
3339
 
3436
3340
  /**
3437
- * Configuration for the built-in 'css' wrapper type.
3341
+ * Scalar types supported by hidden fields.
3342
+ * These are the primitive value types that can be stored.
3343
+ */
3344
+ type HiddenScalar = string | number | boolean;
3345
+ /**
3346
+ * All value types supported by hidden fields.
3347
+ * Supports both scalar values and arrays of scalars for storing
3348
+ * multiple IDs or similar data.
3349
+ */
3350
+ type HiddenValue = HiddenScalar | HiddenScalar[];
3351
+ /**
3352
+ * Hidden field definition for storing values without rendering UI.
3438
3353
  *
3439
- * The CSS wrapper applies CSS classes around wrapped content via a DynamicText
3440
- * input that resolves to space-separated class names.
3354
+ * Hidden fields participate in the form schema and contribute to form values,
3355
+ * but do not render any visible component. They are useful for:
3356
+ * - Storing IDs when updating existing records
3357
+ * - Persisting metadata that shouldn't be user-editable
3358
+ * - Tracking computed values that need to be submitted
3359
+ *
3360
+ * Unlike the `hidden` property on other fields (which hides a rendered field),
3361
+ * this field type renders nothing at all - it's a componentless field.
3441
3362
  *
3442
3363
  * @example
3443
3364
  * ```typescript
3444
- * const wrapper: CssWrapper = {
3445
- * type: 'css',
3446
- * cssClasses: 'my-css-class another-class',
3365
+ * // Store a record ID for updates
3366
+ * const idField: HiddenField<string> = {
3367
+ * type: 'hidden',
3368
+ * key: 'id',
3369
+ * value: '550e8400-e29b-41d4-a716-446655440000',
3447
3370
  * };
3448
- * ```
3371
+ *
3372
+ * // Store multiple tag IDs
3373
+ * const tagIdsField: HiddenField<number[]> = {
3374
+ * type: 'hidden',
3375
+ * key: 'tagIds',
3376
+ * value: [1, 2, 3],
3377
+ * };
3378
+ * ```
3379
+ *
3380
+ * @typeParam TValue - The type of value stored (must be HiddenValue compatible)
3381
+ */
3382
+ interface HiddenField<TValue extends HiddenValue = HiddenValue> extends FieldDef<never> {
3383
+ /** Discriminant for hidden field type */
3384
+ type: 'hidden';
3385
+ /**
3386
+ * The value to store in the form.
3387
+ * This is required - a hidden field without a value serves no purpose.
3388
+ */
3389
+ value: TValue;
3390
+ }
3391
+
3392
+ /**
3393
+ * Configuration for the built-in 'css' wrapper type.
3394
+ *
3395
+ * The CSS wrapper applies CSS classes around wrapped content via a DynamicText
3396
+ * input that resolves to space-separated class names.
3397
+ *
3398
+ * @example
3399
+ * ```typescript
3400
+ * const wrapper: CssWrapper = {
3401
+ * type: 'css',
3402
+ * cssClasses: 'my-css-class another-class',
3403
+ * };
3404
+ * ```
3449
3405
  */
3450
3406
  interface CssWrapper {
3451
3407
  readonly type: 'css';
@@ -3472,6 +3428,118 @@ interface RowWrapper {
3472
3428
  readonly type: 'row';
3473
3429
  }
3474
3430
 
3431
+ /**
3432
+ * Resolves a wrapper type name to its registered config interface.
3433
+ *
3434
+ * When `TWrappers` is a specific registered key (e.g., `'css'`), resolves to
3435
+ * the full config type from `FieldRegistryWrappers` (e.g., `CssWrapper`),
3436
+ * providing type-safe access to wrapper-specific properties like `cssClasses`.
3437
+ *
3438
+ * When `TWrappers` is the full `RegisteredWrapperTypes` union, distributes
3439
+ * to produce a discriminated union of all registered wrapper configs.
3440
+ *
3441
+ * @example
3442
+ * ```typescript
3443
+ * // Resolves to CssWrapper — cssClasses is typed
3444
+ * type CssConfig = WrapperConfig<'css'>;
3445
+ *
3446
+ * // Union of all registered wrapper configs
3447
+ * type AnyConfig = WrapperConfig;
3448
+ * ```
3449
+ */
3450
+ type WrapperConfig<TWrappers extends RegisteredWrapperTypes = RegisteredWrapperTypes> = TWrappers extends keyof FieldRegistryWrappers ? FieldRegistryWrappers[TWrappers] : {
3451
+ readonly type: TWrappers;
3452
+ };
3453
+ /**
3454
+ * Signature of a lazy component loader — either a direct component class or
3455
+ * an ES module whose `default` export is the component. Shared by field and
3456
+ * wrapper registrations so the `loadComponent: () => import('./x.component')`
3457
+ * idiom types cleanly in both places.
3458
+ */
3459
+ type LazyComponentLoader<T = unknown> = () => Promise<Type<T> | {
3460
+ default: Type<T>;
3461
+ }>;
3462
+ /**
3463
+ * Configuration interface for registering wrapper types.
3464
+ *
3465
+ * Defines how a wrapper component is loaded and identified. Wrapper components
3466
+ * provide visual decoration around field content (sections, headers, styling)
3467
+ * without affecting the form data structure.
3468
+ *
3469
+ * @example
3470
+ * ```typescript
3471
+ * const SectionWrapper: WrapperTypeDefinition = {
3472
+ * name: 'section',
3473
+ * loadComponent: () => import('./section-wrapper.component').then(m => m.SectionWrapperComponent),
3474
+ * };
3475
+ * ```
3476
+ */
3477
+ interface WrapperTypeDefinition<T extends WrapperConfig = WrapperConfig> {
3478
+ /** Unique identifier for the wrapper type (also serves as discriminant from FieldTypeDefinition) */
3479
+ wrapperName: string;
3480
+ /** Wrapper definition type marker (internal use) */
3481
+ _wrapper?: T;
3482
+ /**
3483
+ * Function to load the wrapper component (supports lazy loading).
3484
+ * Returns a Promise that resolves to the component class or module with default export.
3485
+ */
3486
+ loadComponent: LazyComponentLoader;
3487
+ /**
3488
+ * Field types this wrapper should auto-apply to.
3489
+ *
3490
+ * When a field's `type` matches any entry, the wrapper is injected into that
3491
+ * field's effective wrapper chain at the lowest priority (can be overridden
3492
+ * by `FormConfig.defaultWrappers` or the field-level `wrappers` array, and
3493
+ * fully cleared with `wrappers: null`).
3494
+ */
3495
+ types?: readonly string[];
3496
+ }
3497
+ /**
3498
+ * Type guard for WrapperTypeDefinition.
3499
+ *
3500
+ * Discriminates via `wrapperName` — field types use `name`, wrapper types use `wrapperName`.
3501
+ */
3502
+ declare function isWrapperTypeDefinition(value: unknown): value is WrapperTypeDefinition;
3503
+ /**
3504
+ * Contract that wrapper components must satisfy.
3505
+ *
3506
+ * Each wrapper exposes a `#fieldComponent` ViewContainerRef where the inner
3507
+ * content (next wrapper, or the field) is rendered imperatively. Config
3508
+ * properties (minus `type`) are set on the component via `setInput`; a
3509
+ * wrapper opts into a key by declaring a matching `input()`, and unknown
3510
+ * keys are silently dropped. Wrappers may additionally declare a
3511
+ * `fieldInputs` input for the wrapped field's mapper outputs (see
3512
+ * `WrapperFieldInputs` for when that's undefined).
3513
+ *
3514
+ * @example
3515
+ * ```typescript
3516
+ * @Component({
3517
+ * template: `
3518
+ * <dbx-section [header]="header() ?? ''">
3519
+ * <ng-container #fieldComponent></ng-container>
3520
+ * </dbx-section>
3521
+ * `,
3522
+ * })
3523
+ * export class SectionWrapperComponent implements FieldWrapper {
3524
+ * readonly fieldComponent = viewChild.required('fieldComponent', { read: ViewContainerRef });
3525
+ * readonly header = input<string>();
3526
+ * readonly fieldInputs = input<WrapperFieldInputs>();
3527
+ * }
3528
+ * ```
3529
+ */
3530
+ interface FieldWrapper {
3531
+ /** ViewContainerRef slot where inner content is rendered */
3532
+ readonly fieldComponent: Signal<ViewContainerRef>;
3533
+ }
3534
+ /**
3535
+ * Injection token for the wrapper type registry.
3536
+ *
3537
+ * Provides access to the map of registered wrapper types. The registry is
3538
+ * populated via `provideDynamicForm()` and used by `ContainerFieldComponent` to
3539
+ * resolve wrapper types to their component implementations.
3540
+ */
3541
+ declare const WRAPPER_REGISTRY: InjectionToken<Map<string, WrapperTypeDefinition<RowWrapper | _ng_forge_dynamic_forms.CssWrapper>>>;
3542
+
3475
3543
  /**
3476
3544
  * Container field interface for wrapping child fields with UI chrome.
3477
3545
  *
@@ -3554,326 +3622,397 @@ interface ContainerField<TFields extends readonly ContainerAllowedChildren[] = r
3554
3622
  declare function isContainerTypedField(field: FieldDef<any>): field is ContainerField;
3555
3623
 
3556
3624
  /**
3557
- * Container fields registry - augment this interface to add custom container fields
3625
+ * Type constraints for field nesting rules
3626
+ * These ensure that container fields can only contain valid child field types
3558
3627
  *
3559
- * @example
3560
- * ```typescript
3561
- * declare module '@ng-forge/dynamic-forms' {
3562
- * interface FieldRegistryContainers {
3563
- * 'my-container': MyContainerFieldDef;
3564
- * }
3565
- * }
3566
- * ```
3628
+ * Note: We explicitly list types instead of using Exclude to avoid circular dependencies
3567
3629
  */
3568
- interface FieldRegistryContainers {
3569
- page: PageField;
3570
- row: RowField;
3571
- group: GroupField;
3572
- array: ArrayField | SimplifiedArrayField;
3573
- container: ContainerField;
3574
- }
3575
3630
  /**
3576
- * Leaf fields registry - augment this interface to add custom leaf fields
3577
- *
3578
- * @example
3579
- * ```typescript
3580
- * declare module '@ng-forge/dynamic-forms' {
3581
- * interface FieldRegistryLeaves {
3582
- * 'my-input': MyInputFieldDef;
3583
- * }
3584
- * }
3585
- * ```
3631
+ * Fields that are allowed as children of Page fields
3632
+ * Pages can contain: rows, groups, arrays, and leaf fields (but NOT other pages)
3586
3633
  */
3587
- interface FieldRegistryLeaves {
3588
- text: TextField;
3589
- hidden: HiddenField;
3590
- }
3634
+ type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
3591
3635
  /**
3592
- * Wrapper type registry for module augmentation.
3593
- *
3594
- * Augment this interface to add type-safe wrapper type definitions.
3595
- *
3596
- * @example
3597
- * ```typescript
3598
- * declare module '@ng-forge/dynamic-forms' {
3599
- * interface FieldRegistryWrappers {
3600
- * 'section': MySectionFieldDef
3601
- * }
3602
- * }
3603
- * ```
3636
+ * Fields that are allowed as children of Group fields
3637
+ * Groups can contain: rows and leaf fields (but NOT pages or other groups)
3604
3638
  */
3605
- interface FieldRegistryWrappers {
3606
- css: CssWrapper;
3607
- row: RowWrapper;
3608
- }
3639
+ type GroupAllowedChildren = LeafFieldTypes | RowField | ContainerField;
3609
3640
  /**
3610
- * Global interface for dynamic form field definitions with categorization
3611
- * This interface combines containers and leaves from their respective registries
3612
- *
3613
- * Container fields: Layout fields that contain other fields (page, row, group, array)
3614
- * Leaf fields: Fields that can hold values or display content (input, text, etc.)
3615
- *
3616
- * To add custom fields, augment FieldRegistryContainers or FieldRegistryLeaves
3641
+ * Fields that are allowed as children of Array fields
3642
+ * Arrays can contain: rows, groups, and leaf fields (but NOT pages or other arrays)
3643
+ * Groups are used for creating object arrays where each array item is an object
3617
3644
  */
3618
- interface DynamicFormFieldRegistry {
3619
- /**
3620
- * Container fields that hold other fields (no value, have children)
3621
- */
3622
- containers: FieldRegistryContainers;
3623
- /**
3624
- * Leaf fields that have values or display content
3625
- */
3626
- leaves: FieldRegistryLeaves;
3627
- /**
3628
- * Field wrappers types
3629
- */
3630
- wrappers: FieldRegistryWrappers;
3631
- }
3645
+ type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField | ContainerField;
3632
3646
  /**
3633
- * Union type of all registered container field definitions
3647
+ * Fields that are allowed as children of Container fields.
3648
+ * Containers are pure layout primitives that flatten their children into the
3649
+ * parent form, so most registered field types may appear inside — including
3650
+ * hidden fields and other containers — but NOT pages.
3634
3651
  */
3635
- type ContainerFieldTypes = DynamicFormFieldRegistry['containers'][keyof DynamicFormFieldRegistry['containers']];
3652
+ type ContainerAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
3636
3653
  /**
3637
- * Union type of all registered leaf field definitions
3654
+ * Row is a synthetic field type that resolves to a Container at runtime, so
3655
+ * it accepts the same children as a Container.
3638
3656
  */
3639
- type LeafFieldTypes = DynamicFormFieldRegistry['leaves'][keyof DynamicFormFieldRegistry['leaves']];
3657
+ type RowAllowedChildren = ContainerAllowedChildren;
3658
+
3640
3659
  /**
3641
- * Union type of all registered field definitions
3660
+ * An array item template is an array of allowed children that defines one OBJECT array item's fields.
3661
+ * Used for object arrays where each item is `{ key1: value1, key2: value2, ... }`.
3642
3662
  */
3643
- type RegisteredFieldTypes = ContainerFieldTypes | LeafFieldTypes;
3663
+ type ArrayItemTemplate = readonly ArrayAllowedChildren[];
3644
3664
  /**
3645
- * Extract field types that are available in the registry
3665
+ * An array item definition can be either:
3666
+ * - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
3667
+ * - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
3668
+ *
3669
+ * This allows support for:
3670
+ * - Primitive arrays: `['tag1', 'tag2']`
3671
+ * - Object arrays: `[{ name: 'Alice', email: '...' }]`
3672
+ * - Heterogeneous arrays: `[{ value: 'x' }, 'y']` (mixed primitives and objects)
3646
3673
  */
3647
- type AvailableFieldTypes = keyof DynamicFormFieldRegistry['containers'] | keyof DynamicFormFieldRegistry['leaves'];
3674
+ type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate;
3648
3675
  /**
3649
- * Extract wrapper types that are available in the registry
3650
- */
3651
- type RegisteredWrapperTypes = keyof DynamicFormFieldRegistry['wrappers'];
3652
- /**
3653
- * Combined registry mapping type names to field definitions.
3654
- * This flattens containers and leaves into a single mapping.
3655
- */
3656
- type FieldTypeMap = DynamicFormFieldRegistry['containers'] & DynamicFormFieldRegistry['leaves'];
3657
- /**
3658
- * Extract a specific field type from RegisteredFieldTypes based on the `type` discriminant.
3659
- * This enables proper type narrowing when defining fields.
3676
+ * Array field interface for creating dynamic field collections that map to array values.
3677
+ *
3678
+ * Key concepts:
3679
+ * - The outer `fields` array defines INITIAL ITEMS (each element is one array item to render)
3680
+ * - Each element can be either:
3681
+ * - A single FieldDef (primitive item) - the field's value is extracted directly
3682
+ * - An array of FieldDefs (object item) - fields are merged into an object
3683
+ * - Items can have different field configurations (heterogeneous arrays supported)
3684
+ * - Empty `fields: []` means no initial items - user adds via buttons with explicit templates
3685
+ * - Initial values are declared via `value` property on each field definition
3660
3686
  *
3661
3687
  * @example
3662
- * ```typescript
3663
- * // Extract a specific field type
3664
- * type MyInputField = ExtractField<'input'>;
3688
+ * // Primitive array: ['angular', 'typescript']
3689
+ * {
3690
+ * key: 'tags',
3691
+ * type: 'array',
3692
+ * fields: [
3693
+ * { key: 'tag', type: 'input', value: 'angular' }, // Single field = primitive
3694
+ * { key: 'tag', type: 'input', value: 'typescript' },
3695
+ * ]
3696
+ * }
3665
3697
  *
3666
- * // Use in field definitions for proper props inference
3667
- * const field: ExtractField<'input'> = {
3668
- * type: 'input',
3669
- * key: 'email',
3670
- * value: '',
3671
- * props: { type: 'email' } // Only input props allowed here
3672
- * };
3673
- * ```
3674
- */
3675
- type ExtractField<T extends AvailableFieldTypes> = T extends keyof FieldTypeMap ? FieldTypeMap[T] : never;
3676
- /**
3677
- * Narrow a field definition based on its `type` property.
3678
- * Use this to get proper type inference when working with field unions.
3698
+ * @example
3699
+ * // Object array: [{ name: 'Alice', email: '...' }]
3700
+ * {
3701
+ * key: 'contacts',
3702
+ * type: 'array',
3703
+ * fields: [
3704
+ * [ // Array = object
3705
+ * { key: 'name', type: 'input', value: 'Alice' },
3706
+ * { key: 'email', type: 'input', value: 'alice@example.com' }
3707
+ * ],
3708
+ * ]
3709
+ * }
3679
3710
  *
3680
3711
  * @example
3681
- * ```typescript
3682
- * function processField<T extends RegisteredFieldTypes>(field: T): NarrowField<T> {
3683
- * return field as NarrowField<T>;
3712
+ * // Heterogeneous array: [{ label: 'Structured' }, 'Simple']
3713
+ * {
3714
+ * key: 'items',
3715
+ * type: 'array',
3716
+ * fields: [
3717
+ * [{ key: 'label', type: 'input', value: 'Structured' }], // Object item
3718
+ * { key: 'value', type: 'input', value: 'Simple' }, // Primitive item
3719
+ * ]
3684
3720
  * }
3685
- * ```
3686
- */
3687
- type NarrowField<T> = T extends {
3688
- type: infer TType;
3689
- } ? (TType extends AvailableFieldTypes ? ExtractField<TType> : T) : T;
3690
- /**
3691
- * Narrow each field in an array based on its `type` property.
3692
- * Use with `satisfies` to get proper type inference for field arrays.
3693
3721
  *
3694
3722
  * @example
3695
- * ```typescript
3696
- * const fields = [
3697
- * { type: 'input', key: 'name', value: '', props: { type: 'text' } },
3698
- * { type: 'select', key: 'country', value: 'us', options: [...] },
3699
- * ] as const satisfies NarrowFields;
3700
- * ```
3701
- */
3702
- type NarrowFields = readonly NarrowField<RegisteredFieldTypes>[];
3703
-
3704
- /**
3705
- * Type constraints for field nesting rules
3706
- * These ensure that container fields can only contain valid child field types
3723
+ * // Empty array (add items via buttons)
3724
+ * {
3725
+ * key: 'tags',
3726
+ * type: 'array',
3727
+ * fields: [] // No initial items - user adds via button with template
3728
+ * }
3707
3729
  *
3708
- * Note: We explicitly list types instead of using Exclude to avoid circular dependencies
3709
- */
3710
- /**
3711
- * Fields that are allowed as children of Page fields
3712
- * Pages can contain: rows, groups, arrays, and leaf fields (but NOT other pages)
3713
- */
3714
- type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
3715
- /**
3716
- * Fields that are allowed as children of Group fields
3717
- * Groups can contain: rows and leaf fields (but NOT pages or other groups)
3718
- */
3719
- type GroupAllowedChildren = LeafFieldTypes | RowField | ContainerField;
3720
- /**
3721
- * Fields that are allowed as children of Array fields
3722
- * Arrays can contain: rows, groups, and leaf fields (but NOT pages or other arrays)
3723
- * Groups are used for creating object arrays where each array item is an object
3724
- */
3725
- type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField | ContainerField;
3726
- /**
3727
- * Fields that are allowed as children of Container fields.
3728
- * Containers are pure layout primitives that flatten their children into the
3729
- * parent form, so most registered field types may appear inside — including
3730
- * hidden fields and other containers — but NOT pages.
3731
- */
3732
- type ContainerAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
3733
- /**
3734
- * Row is a synthetic field type that resolves to a Container at runtime, so
3735
- * it accepts the same children as a Container.
3736
- */
3737
- type RowAllowedChildren = ContainerAllowedChildren;
3738
-
3739
- /**
3740
- * Row field interface for creating horizontal layouts.
3741
- * A row is a synthetic field type that resolves to a Container at runtime,
3742
- * with a synthesized `{ type: 'row' }` wrapper applied for layout. The row
3743
- * itself does not hold a value — its children flatten into the parent form.
3730
+ * Nesting constraints: Arrays can contain rows, leaf fields, or groups (for object arrays),
3731
+ * but NOT pages or other arrays. Runtime validation enforces these rules.
3744
3732
  *
3745
- * Note: Rows do not support `meta` since they have no native form element.
3733
+ * Note: Arrays are container fields and do not support `meta` since they have no native form element.
3746
3734
  */
3747
- interface RowField<TFields extends readonly RowAllowedChildren[] = readonly RowAllowedChildren[]> extends FieldDef<never> {
3748
- type: 'row';
3749
- /** Child definitions to render within this row */
3735
+ interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never> {
3736
+ type: 'array';
3737
+ /**
3738
+ * Array of item definitions. Each element defines one array item:
3739
+ * - Single FieldDef = primitive item (field's value is extracted directly)
3740
+ * - Array of FieldDefs = object item (fields are merged into an object)
3741
+ * - Empty array `[]` = no initial items (add via buttons with explicit templates)
3742
+ * - Items can have different configurations (heterogeneous arrays supported)
3743
+ * - Initial values are set via `value` property on each field definition
3744
+ */
3750
3745
  readonly fields: TFields;
3751
- /** Row fields do not have a label property **/
3746
+ /** Array fields do not have a label property **/
3752
3747
  readonly label?: never;
3753
- /** Rows do not support meta - they have no native form element **/
3748
+ /** Arrays do not support meta - they have no native form element **/
3754
3749
  readonly meta?: never;
3755
3750
  /**
3756
- * Logic configurations for conditional row visibility.
3757
- * Only 'hidden' type logic is supported for rows.
3751
+ * Logic configurations for conditional array visibility.
3752
+ * Only 'hidden' type logic is supported for arrays.
3758
3753
  */
3759
3754
  readonly logic?: ContainerLogicConfig[];
3755
+ /**
3756
+ * Minimum number of items required in the array.
3757
+ * Validation fails if the array has fewer items than this value.
3758
+ */
3759
+ readonly minLength?: number;
3760
+ /**
3761
+ * Maximum number of items allowed in the array.
3762
+ * Validation fails if the array has more items than this value.
3763
+ */
3764
+ readonly maxLength?: number;
3760
3765
  }
3761
3766
  /**
3762
- * Type guard for RowField with proper type narrowing
3767
+ * Type guard for ArrayField with proper type narrowing.
3768
+ * Validates that the field is an array type with a fields property that is an array.
3769
+ * Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
3763
3770
  */
3764
- declare function isRowField(field: FieldDef<any>): field is RowField;
3765
-
3771
+ declare function isArrayField(field: FieldDef<any>): field is ArrayField;
3766
3772
  /**
3767
- * Resolves a wrapper type name to its registered config interface.
3773
+ * Configuration for auto-generated add/remove buttons in simplified array fields.
3774
+ */
3775
+ interface ArrayButtonConfig {
3776
+ /** Custom label for the button */
3777
+ readonly label?: string;
3778
+ /** Additional properties passed to the button component */
3779
+ readonly props?: Record<string, unknown>;
3780
+ }
3781
+ /**
3782
+ * Simplified array field interface for common use cases.
3768
3783
  *
3769
- * When `TWrappers` is a specific registered key (e.g., `'css'`), resolves to
3770
- * the full config type from `FieldRegistryWrappers` (e.g., `CssWrapper`),
3771
- * providing type-safe access to wrapper-specific properties like `cssClasses`.
3784
+ * Instead of manually specifying each item in `fields`, provide a `template` that defines
3785
+ * the structure of a single item, and a `value` array with initial data.
3786
+ * The library auto-generates add/remove buttons.
3772
3787
  *
3773
- * When `TWrappers` is the full `RegisteredWrapperTypes` union, distributes
3774
- * to produce a discriminated union of all registered wrapper configs.
3788
+ * Discriminant: `template` presence → simplified API; `Array.isArray(template)` → object vs primitive.
3775
3789
  *
3776
3790
  * @example
3777
- * ```typescript
3778
- * // Resolves to CssWrapper — cssClasses is typed
3779
- * type CssConfig = WrapperConfig<'css'>;
3780
- *
3781
- * // Union of all registered wrapper configs
3782
- * type AnyConfig = WrapperConfig;
3783
- * ```
3784
- */
3785
- type WrapperConfig<TWrappers extends RegisteredWrapperTypes = RegisteredWrapperTypes> = TWrappers extends keyof FieldRegistryWrappers ? FieldRegistryWrappers[TWrappers] : {
3786
- readonly type: TWrappers;
3787
- };
3788
- /**
3789
- * Signature of a lazy component loader — either a direct component class or
3790
- * an ES module whose `default` export is the component. Shared by field and
3791
- * wrapper registrations so the `loadComponent: () => import('./x.component')`
3792
- * idiom types cleanly in both places.
3793
- */
3794
- type LazyComponentLoader<T = unknown> = () => Promise<Type<T> | {
3795
- default: Type<T>;
3796
- }>;
3797
- /**
3798
- * Configuration interface for registering wrapper types.
3791
+ * // Primitive array: ['angular', 'typescript']
3792
+ * {
3793
+ * key: 'tags',
3794
+ * type: 'array',
3795
+ * template: { key: 'value', type: 'input', label: 'Tag', required: true },
3796
+ * value: ['angular', 'typescript']
3797
+ * }
3799
3798
  *
3800
- * Defines how a wrapper component is loaded and identified. Wrapper components
3801
- * provide visual decoration around field content (sections, headers, styling)
3802
- * without affecting the form data structure.
3799
+ * @example
3800
+ * // Object array: [{ name: 'Jane', phone: '555' }]
3801
+ * {
3802
+ * key: 'contacts',
3803
+ * type: 'array',
3804
+ * template: [
3805
+ * { key: 'name', type: 'input', label: 'Contact Name', required: true },
3806
+ * { key: 'phone', type: 'input', label: 'Phone Number' },
3807
+ * ],
3808
+ * value: [{ name: 'Jane', phone: '555' }]
3809
+ * }
3803
3810
  *
3804
3811
  * @example
3805
- * ```typescript
3806
- * const SectionWrapper: WrapperTypeDefinition = {
3807
- * name: 'section',
3808
- * loadComponent: () => import('./section-wrapper.component').then(m => m.SectionWrapperComponent),
3809
- * };
3810
- * ```
3812
+ * // Button customization / opt-out
3813
+ * {
3814
+ * key: 'tags',
3815
+ * type: 'array',
3816
+ * template: { key: 'value', type: 'input', label: 'Tag' },
3817
+ * addButton: { label: 'Add Tag', props: { color: 'primary' } },
3818
+ * removeButton: false
3819
+ * }
3811
3820
  */
3812
- interface WrapperTypeDefinition<T extends WrapperConfig = WrapperConfig> {
3813
- /** Unique identifier for the wrapper type (also serves as discriminant from FieldTypeDefinition) */
3814
- wrapperName: string;
3815
- /** Wrapper definition type marker (internal use) */
3816
- _wrapper?: T;
3821
+ interface SimplifiedArrayField extends FieldDef<never> {
3822
+ type: 'array';
3817
3823
  /**
3818
- * Function to load the wrapper component (supports lazy loading).
3819
- * Returns a Promise that resolves to the component class or module with default export.
3824
+ * Template defining the structure of a single array item.
3825
+ * - Single field (ArrayAllowedChildren) → primitive array (each item is a single value)
3826
+ * - Array of fields (readonly ArrayAllowedChildren[]) → object array (each item is an object)
3820
3827
  */
3821
- loadComponent: LazyComponentLoader;
3828
+ readonly template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
3829
+ /** Initial values for the array. Each element creates one array item. */
3830
+ readonly value?: readonly unknown[];
3822
3831
  /**
3823
- * Field types this wrapper should auto-apply to.
3824
- *
3825
- * When a field's `type` matches any entry, the wrapper is injected into that
3826
- * field's effective wrapper chain at the lowest priority (can be overridden
3827
- * by `FormConfig.defaultWrappers` or the field-level `wrappers` array, and
3828
- * fully cleared with `wrappers: null`).
3832
+ * Configuration for the auto-generated "Add" button, or `false` to disable it.
3833
+ * Defaults to a button with label "Add".
3829
3834
  */
3830
- types?: readonly string[];
3835
+ readonly addButton?: ArrayButtonConfig | false;
3836
+ /**
3837
+ * Configuration for the auto-generated "Remove" button on each item, or `false` to disable it.
3838
+ * Defaults to a button with label "Remove".
3839
+ */
3840
+ readonly removeButton?: ArrayButtonConfig | false;
3841
+ /** Simplified arrays do not support the label property */
3842
+ readonly label?: never;
3843
+ /** Simplified arrays do not support meta */
3844
+ readonly meta?: never;
3845
+ /**
3846
+ * Logic configurations for conditional array visibility.
3847
+ * Only 'hidden' type logic is supported for arrays.
3848
+ */
3849
+ readonly logic?: ContainerLogicConfig[];
3850
+ /**
3851
+ * Minimum number of items required in the array.
3852
+ * Validation fails if the array has fewer items than this value.
3853
+ */
3854
+ readonly minLength?: number;
3855
+ /**
3856
+ * Maximum number of items allowed in the array.
3857
+ * Validation fails if the array has more items than this value.
3858
+ */
3859
+ readonly maxLength?: number;
3860
+ /** Mutually exclusive with `template` — use `fields` for the full API instead */
3861
+ readonly fields?: never;
3831
3862
  }
3832
3863
  /**
3833
- * Type guard for WrapperTypeDefinition.
3864
+ * Type guard for SimplifiedArrayField.
3865
+ * Checks for `type: 'array'` with a `template` property (discriminant from full ArrayField).
3866
+ */
3867
+ declare function isSimplifiedArrayField(field: FieldDef<any>): field is SimplifiedArrayField;
3868
+
3869
+ /**
3870
+ * Container fields registry - augment this interface to add custom container fields
3834
3871
  *
3835
- * Discriminates via `wrapperName` — field types use `name`, wrapper types use `wrapperName`.
3872
+ * @example
3873
+ * ```typescript
3874
+ * declare module '@ng-forge/dynamic-forms' {
3875
+ * interface FieldRegistryContainers {
3876
+ * 'my-container': MyContainerFieldDef;
3877
+ * }
3878
+ * }
3879
+ * ```
3836
3880
  */
3837
- declare function isWrapperTypeDefinition(value: unknown): value is WrapperTypeDefinition;
3881
+ interface FieldRegistryContainers {
3882
+ page: PageField;
3883
+ row: RowField;
3884
+ group: GroupField;
3885
+ array: ArrayField | SimplifiedArrayField;
3886
+ container: ContainerField;
3887
+ }
3838
3888
  /**
3839
- * Contract that wrapper components must satisfy.
3889
+ * Leaf fields registry - augment this interface to add custom leaf fields
3840
3890
  *
3841
- * Each wrapper exposes a `#fieldComponent` ViewContainerRef where the inner
3842
- * content (next wrapper, or the field) is rendered imperatively. Config
3843
- * properties (minus `type`) are set on the component via `setInput`; a
3844
- * wrapper opts into a key by declaring a matching `input()`, and unknown
3845
- * keys are silently dropped. Wrappers may additionally declare a
3846
- * `fieldInputs` input for the wrapped field's mapper outputs (see
3847
- * `WrapperFieldInputs` for when that's undefined).
3891
+ * @example
3892
+ * ```typescript
3893
+ * declare module '@ng-forge/dynamic-forms' {
3894
+ * interface FieldRegistryLeaves {
3895
+ * 'my-input': MyInputFieldDef;
3896
+ * }
3897
+ * }
3898
+ * ```
3899
+ */
3900
+ interface FieldRegistryLeaves {
3901
+ text: TextField;
3902
+ hidden: HiddenField;
3903
+ }
3904
+ /**
3905
+ * Wrapper type registry for module augmentation.
3906
+ *
3907
+ * Augment this interface to add type-safe wrapper type definitions.
3848
3908
  *
3849
3909
  * @example
3850
3910
  * ```typescript
3851
- * @Component({
3852
- * template: `
3853
- * <dbx-section [header]="header() ?? ''">
3854
- * <ng-container #fieldComponent></ng-container>
3855
- * </dbx-section>
3856
- * `,
3857
- * })
3858
- * export class SectionWrapperComponent implements FieldWrapper {
3859
- * readonly fieldComponent = viewChild.required('fieldComponent', { read: ViewContainerRef });
3860
- * readonly header = input<string>();
3861
- * readonly fieldInputs = input<WrapperFieldInputs>();
3911
+ * declare module '@ng-forge/dynamic-forms' {
3912
+ * interface FieldRegistryWrappers {
3913
+ * 'section': MySectionFieldDef
3914
+ * }
3862
3915
  * }
3863
3916
  * ```
3864
3917
  */
3865
- interface FieldWrapper {
3866
- /** ViewContainerRef slot where inner content is rendered */
3867
- readonly fieldComponent: Signal<ViewContainerRef>;
3918
+ interface FieldRegistryWrappers {
3919
+ css: CssWrapper;
3920
+ row: RowWrapper;
3868
3921
  }
3869
3922
  /**
3870
- * Injection token for the wrapper type registry.
3923
+ * Global interface for dynamic form field definitions with categorization
3924
+ * This interface combines containers and leaves from their respective registries
3871
3925
  *
3872
- * Provides access to the map of registered wrapper types. The registry is
3873
- * populated via `provideDynamicForm()` and used by `ContainerFieldComponent` to
3874
- * resolve wrapper types to their component implementations.
3926
+ * Container fields: Layout fields that contain other fields (page, row, group, array)
3927
+ * Leaf fields: Fields that can hold values or display content (input, text, etc.)
3928
+ *
3929
+ * To add custom fields, augment FieldRegistryContainers or FieldRegistryLeaves
3875
3930
  */
3876
- declare const WRAPPER_REGISTRY: InjectionToken<Map<string, WrapperTypeDefinition<RowWrapper | _ng_forge_dynamic_forms.CssWrapper>>>;
3931
+ interface DynamicFormFieldRegistry {
3932
+ /**
3933
+ * Container fields that hold other fields (no value, have children)
3934
+ */
3935
+ containers: FieldRegistryContainers;
3936
+ /**
3937
+ * Leaf fields that have values or display content
3938
+ */
3939
+ leaves: FieldRegistryLeaves;
3940
+ /**
3941
+ * Field wrappers types
3942
+ */
3943
+ wrappers: FieldRegistryWrappers;
3944
+ }
3945
+ /**
3946
+ * Union type of all registered container field definitions
3947
+ */
3948
+ type ContainerFieldTypes = DynamicFormFieldRegistry['containers'][keyof DynamicFormFieldRegistry['containers']];
3949
+ /**
3950
+ * Union type of all registered leaf field definitions
3951
+ */
3952
+ type LeafFieldTypes = DynamicFormFieldRegistry['leaves'][keyof DynamicFormFieldRegistry['leaves']];
3953
+ /**
3954
+ * Union type of all registered field definitions
3955
+ */
3956
+ type RegisteredFieldTypes = ContainerFieldTypes | LeafFieldTypes;
3957
+ /**
3958
+ * Extract field types that are available in the registry
3959
+ */
3960
+ type AvailableFieldTypes = keyof DynamicFormFieldRegistry['containers'] | keyof DynamicFormFieldRegistry['leaves'];
3961
+ /**
3962
+ * Extract wrapper types that are available in the registry
3963
+ */
3964
+ type RegisteredWrapperTypes = keyof DynamicFormFieldRegistry['wrappers'];
3965
+ /**
3966
+ * Combined registry mapping type names to field definitions.
3967
+ * This flattens containers and leaves into a single mapping.
3968
+ */
3969
+ type FieldTypeMap = DynamicFormFieldRegistry['containers'] & DynamicFormFieldRegistry['leaves'];
3970
+ /**
3971
+ * Extract a specific field type from RegisteredFieldTypes based on the `type` discriminant.
3972
+ * This enables proper type narrowing when defining fields.
3973
+ *
3974
+ * @example
3975
+ * ```typescript
3976
+ * // Extract a specific field type
3977
+ * type MyInputField = ExtractField<'input'>;
3978
+ *
3979
+ * // Use in field definitions for proper props inference
3980
+ * const field: ExtractField<'input'> = {
3981
+ * type: 'input',
3982
+ * key: 'email',
3983
+ * value: '',
3984
+ * props: { type: 'email' } // Only input props allowed here
3985
+ * };
3986
+ * ```
3987
+ */
3988
+ type ExtractField<T extends AvailableFieldTypes> = T extends keyof FieldTypeMap ? FieldTypeMap[T] : never;
3989
+ /**
3990
+ * Narrow a field definition based on its `type` property.
3991
+ * Use this to get proper type inference when working with field unions.
3992
+ *
3993
+ * @example
3994
+ * ```typescript
3995
+ * function processField<T extends RegisteredFieldTypes>(field: T): NarrowField<T> {
3996
+ * return field as NarrowField<T>;
3997
+ * }
3998
+ * ```
3999
+ */
4000
+ type NarrowField<T> = T extends {
4001
+ type: infer TType;
4002
+ } ? (TType extends AvailableFieldTypes ? ExtractField<TType> : T) : T;
4003
+ /**
4004
+ * Narrow each field in an array based on its `type` property.
4005
+ * Use with `satisfies` to get proper type inference for field arrays.
4006
+ *
4007
+ * @example
4008
+ * ```typescript
4009
+ * const fields = [
4010
+ * { type: 'input', key: 'name', value: '', props: { type: 'text' } },
4011
+ * { type: 'select', key: 'country', value: 'us', options: [...] },
4012
+ * ] as const satisfies NarrowFields;
4013
+ * ```
4014
+ */
4015
+ type NarrowFields = readonly NarrowField<RegisteredFieldTypes>[];
3877
4016
 
3878
4017
  /**
3879
4018
  * Base interface for native HTML attributes that can be passed to form field elements.
@@ -3974,18 +4113,26 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
3974
4113
  /**
3975
4114
  * Field type identifier for component selection.
3976
4115
  *
3977
- * Determines which component will be rendered for this field.
3978
- * Must match a registered field type name in the field registry.
4116
+ * Determines which component will be rendered for this field. Names
4117
+ * registered through module augmentation of `DynamicFormFieldRegistry`
4118
+ * are surfaced as IDE autocomplete suggestions via `RegisteredFieldTypes`;
4119
+ * the `(string & {})` branch keeps arbitrary custom identifiers accepted
4120
+ * as an escape hatch for fields that haven't been augmented into the
4121
+ * registry.
3979
4122
  *
3980
4123
  * @example
3981
4124
  * ```typescript
3982
- * type: 'input' // Text input field
3983
- * type: 'select' // Dropdown selection
3984
- * type: 'checkbox' // Boolean checkbox
3985
- * type: 'group' // Field group container
4125
+ * type: 'input' // Text input field (registered)
4126
+ * type: 'select' // Dropdown selection (registered)
4127
+ * type: 'checkbox' // Boolean checkbox (registered)
4128
+ * type: 'group' // Field group container (registered)
4129
+ * type: 'my-custom' // Unregistered — still accepted, no autocomplete
3986
4130
  * ```
4131
+ *
4132
+ * Wrapper types are intentionally excluded — wrappers are configured through
4133
+ * `wrappers` / `withFormWrappers()` rather than via the field-type discriminant.
3987
4134
  */
3988
- type: string;
4135
+ type: RegisteredFieldTypes['type'] | (string & {});
3989
4136
  /**
3990
4137
  * Human-readable field label displayed to users.
3991
4138
  *
@@ -4188,6 +4335,24 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
4188
4335
  * and any field-level `wrappers`.
4189
4336
  */
4190
4337
  skipDefaultWrappers?: boolean;
4338
+ /**
4339
+ * Addons rendered inside this field's slots (typically `prefix` / `suffix`).
4340
+ *
4341
+ * Each addon declares its kind, slot, and kind-specific configuration.
4342
+ * Whether a field type accepts addons — and which slots / kinds — is
4343
+ * declared at registration via `FieldTypeDefinition.addons`. Field types
4344
+ * without that registration ("Tier 3", e.g., toggle, radio) drop any
4345
+ * `addons` value with a lenient warning at config init.
4346
+ *
4347
+ * @example
4348
+ * ```typescript
4349
+ * addons: [
4350
+ * { slot: 'prefix', kind: 'prime-icon', icon: 'search' },
4351
+ * { slot: 'suffix', kind: 'prime-button', preset: 'clear', ariaLabel: 'Clear' },
4352
+ * ]
4353
+ * ```
4354
+ */
4355
+ addons?: ReadonlyArray<AnyAddon>;
4191
4356
  }
4192
4357
  type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
4193
4358
  /**
@@ -4282,6 +4447,103 @@ interface GroupContext {
4282
4447
  */
4283
4448
  type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
4284
4449
 
4450
+ /**
4451
+ * Minimal JSON Schema fragment used by the MCP server to surface an addon
4452
+ * kind's shape to AI form generators. Covers the common subset
4453
+ * (`type`/`properties`/`required`/`enum`/etc.) without pulling in a full JSON
4454
+ * Schema dependency. Extra keys are allowed via the `[key: string]` index so
4455
+ * vendor-specific annotations (`x-foo`, `markdownDescription`) still typecheck.
4456
+ *
4457
+ * Tooling-only: the runtime addon validator does NOT consult `schema` — it
4458
+ * relies on each kind's `validate?(addon, fieldKey)` callback for shape
4459
+ * enforcement. `schema` exists exclusively so the MCP server (and any other
4460
+ * AI-driven form authoring tool) can describe the kind to a model without
4461
+ * hand-maintaining a parallel registry.
4462
+ */
4463
+ interface AddonKindSchema {
4464
+ readonly $ref?: string;
4465
+ readonly $schema?: string;
4466
+ readonly title?: string;
4467
+ readonly description?: string;
4468
+ readonly type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null' | ReadonlyArray<'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'>;
4469
+ readonly enum?: ReadonlyArray<unknown>;
4470
+ readonly const?: unknown;
4471
+ readonly properties?: Readonly<Record<string, AddonKindSchema>>;
4472
+ readonly required?: ReadonlyArray<string>;
4473
+ readonly additionalProperties?: boolean | AddonKindSchema;
4474
+ readonly items?: AddonKindSchema | ReadonlyArray<AddonKindSchema>;
4475
+ readonly oneOf?: ReadonlyArray<AddonKindSchema>;
4476
+ readonly anyOf?: ReadonlyArray<AddonKindSchema>;
4477
+ readonly allOf?: ReadonlyArray<AddonKindSchema>;
4478
+ readonly not?: AddonKindSchema;
4479
+ readonly default?: unknown;
4480
+ readonly examples?: ReadonlyArray<unknown>;
4481
+ readonly minimum?: number;
4482
+ readonly maximum?: number;
4483
+ readonly minLength?: number;
4484
+ readonly maxLength?: number;
4485
+ readonly pattern?: string;
4486
+ readonly format?: string;
4487
+ /** Vendor extensions and unrecognised keywords pass through untyped. */
4488
+ readonly [key: string]: unknown;
4489
+ }
4490
+ /**
4491
+ * Optional shape validator a kind ships to enforce its required fields at
4492
+ * runtime. Throws are caught by the validator and converted into lenient
4493
+ * warnings; the offending addon is dropped and the form keeps rendering.
4494
+ */
4495
+ type AddonShapeValidator<T extends BaseAddon = BaseAddon> = (addon: T, fieldKey: string) => void;
4496
+ /**
4497
+ * Per-kind registration metadata.
4498
+ *
4499
+ * Registered via `withCustomAddon(...)` features (or adapter feature
4500
+ * helpers like `withPrimeNGAddons()`); resolved at render time by
4501
+ * `<df-addon-slot>`.
4502
+ */
4503
+ interface AddonKindDefinition<T extends BaseAddon = BaseAddon> {
4504
+ /** Discriminant matching `addon.kind`. */
4505
+ readonly kind: string;
4506
+ /** Lazy loader for the kind's renderer component. */
4507
+ readonly loadComponent: LazyComponentLoader;
4508
+ /** Optional shape validator — throw `DynamicFormError` to drop the addon with a warning. */
4509
+ readonly validate?: AddonShapeValidator<T>;
4510
+ /** Optional JSON Schema fragment for MCP / pre-flight tooling. */
4511
+ readonly schema?: AddonKindSchema;
4512
+ /**
4513
+ * Whether the kind survives `JSON.stringify` / `JSON.parse`. Kinds whose
4514
+ * shape includes a function payload (e.g., the built-in `'component'`
4515
+ * loader, inline `prime-button` `action` handlers) declare `jsonSafe: false`
4516
+ * so the validator drops them when the config was loaded from a JSON
4517
+ * source (`sanitizeFormConfig(config, { source: 'json' })`). Defaults to
4518
+ * `true` — kinds are JSON-safe unless explicitly opted out.
4519
+ */
4520
+ readonly jsonSafe?: boolean;
4521
+ }
4522
+ /**
4523
+ * Per-field-type addon-support metadata declared at field registration.
4524
+ *
4525
+ * Source of truth for the runtime validator: which slots are valid for a
4526
+ * field type, which kinds are accepted (optional whitelist).
4527
+ *
4528
+ * Field types that do not declare `addons` on their `FieldTypeDefinition`
4529
+ * are treated as Tier 3 — addons configured on such fields are dropped
4530
+ * with a warning at config init.
4531
+ */
4532
+ interface FieldAddonSupport {
4533
+ /** Slots a field of this type accepts. */
4534
+ readonly slots: readonly AddonSlot[];
4535
+ /** Optional whitelist of allowed addon kinds. Omitted = any registered kind. */
4536
+ readonly allowedKinds?: readonly string[];
4537
+ }
4538
+ /**
4539
+ * Global registry of addon kinds.
4540
+ *
4541
+ * Populated by `withCustomAddon(...)` features and adapter feature helpers
4542
+ * (e.g., `withPrimeNGAddons()`). Read by `<df-addon-slot>` to resolve a
4543
+ * kind discriminant to its renderer component.
4544
+ */
4545
+ declare const ADDON_KIND_REGISTRY: InjectionToken<Map<string, AddonKindDefinition<BaseAddon<AddonSlot>>>>;
4546
+
4285
4547
  /**
4286
4548
  * Defines how a field type handles form values and data collection.
4287
4549
  *
@@ -4374,7 +4636,24 @@ interface FieldTypeDefinition<T extends FieldDef<any> = any> {
4374
4636
  /** Semantic scope for tooling to discover interchangeable field alternatives */
4375
4637
  scope?: FieldScope | FieldScope[];
4376
4638
  /** Mapped component inputs that must exist before the renderer instantiates the component */
4377
- renderReadyWhen?: RenderReadyInput[];
4639
+ renderReadyWhen?: readonly RenderReadyInput[];
4640
+ /**
4641
+ * Declares which addon slots this field accepts and (optionally) the
4642
+ * subset of kinds allowed.
4643
+ *
4644
+ * Source of truth for the runtime addon validator. Field types that
4645
+ * omit this property are treated as Tier 3 — any `addons` configured
4646
+ * on such a field are dropped with a warning at config init.
4647
+ *
4648
+ * @example
4649
+ * ```typescript
4650
+ * {
4651
+ * name: 'prime-input',
4652
+ * addons: { slots: ['prefix', 'suffix'] },
4653
+ * }
4654
+ * ```
4655
+ */
4656
+ addons?: FieldAddonSupport;
4378
4657
  }
4379
4658
  /**
4380
4659
  * Injection token for the global field type registry.
@@ -4403,6 +4682,15 @@ interface ResolvedField {
4403
4682
  injector: Injector;
4404
4683
  inputs: Signal<Record<string, unknown>>;
4405
4684
  renderReady: Signal<boolean>;
4685
+ /**
4686
+ * Whether the field is currently hidden. Sourced from the FieldTree's
4687
+ * `state.hidden()` for value-bearing fields (set by Angular Signal Forms in
4688
+ * response to `hidden(path, …)` calls), or from the mapper's explicit
4689
+ * `hidden` input for non-value fields (button, text, container, …). Consumers
4690
+ * must `@if (!field.hidden())` around `*dfFieldOutlet` so hidden fields are
4691
+ * removed from the DOM — Angular Signal Forms warns (NG01916) otherwise.
4692
+ */
4693
+ hidden: Signal<boolean>;
4406
4694
  }
4407
4695
 
4408
4696
  /**
@@ -4610,12 +4898,22 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
4610
4898
  formOptions: _angular_core.InputSignal<FormOptions | undefined>;
4611
4899
  /** Form values for two-way data binding. */
4612
4900
  value: _angular_core.ModelSignal<Partial<TModel> | undefined>;
4901
+ /**
4902
+ * How lenient the addon validator is on `config.fields[...].addons`.
4903
+ * `'inline'` (default) keeps everything; `'json'` drops code-only kinds
4904
+ * with a warning. Read at form init and on every config swap — change
4905
+ * `source` AND the config to force re-validation mid-flight.
4906
+ */
4907
+ source: _angular_core.InputSignal<"json" | "inline">;
4613
4908
  private destroyRef;
4614
4909
  private injector;
4615
4910
  protected environmentInjector: EnvironmentInjector;
4616
4911
  private eventBus;
4617
4912
  private logger;
4618
4913
  private dispatcher;
4914
+ private templateRegistry;
4915
+ /** All `<ng-template dfTemplate="...">` instances projected into this form. */
4916
+ private readonly _projectedTemplates;
4619
4917
  /** State manager that owns all form state. Initialized via connectDeps() to guarantee
4620
4918
  * FORM_STATE_DEPS is populated before FormStateManager is injected. */
4621
4919
  private readonly stateManager;
@@ -4729,7 +5027,7 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
4729
5027
  private connectDeps;
4730
5028
  private setupEventHandlers;
4731
5029
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
4732
- 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>;
5030
+ 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; }; "source": { "alias": "source"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; "reset": "reset"; "cleared": "cleared"; "events": "events"; "initialized": "initialized"; "onPageChange": "onPageChange"; "onPageNavigationStateChange": "onPageNavigationStateChange"; }, ["_projectedTemplates"], never, true, never>;
4733
5031
  }
4734
5032
 
4735
5033
  declare class EventBus {
@@ -4904,214 +5202,6 @@ interface DynamicFormFeature<TKind extends string = string> {
4904
5202
  ɵproviders: Provider[];
4905
5203
  }
4906
5204
 
4907
- interface FieldOption<T = unknown> {
4908
- label: DynamicText;
4909
- value: T;
4910
- disabled?: boolean;
4911
- [key: string]: unknown;
4912
- }
4913
-
4914
- /**
4915
- * Injection token for form-level default wrappers.
4916
- *
4917
- * Provides a Signal of the `defaultWrappers` array from FormConfig. Consumed by
4918
- * `DfFieldOutlet` / `ContainerFieldComponent` to merge into each field's
4919
- * effective wrapper chain (lowest priority after auto-associations, higher
4920
- * than field-level `wrappers`).
4921
- *
4922
- * Provided once at the DynamicForm level and inherited via Angular's
4923
- * hierarchical injector.
4924
- */
4925
- declare const DEFAULT_WRAPPERS: InjectionToken<Signal<readonly WrapperConfig[] | undefined>>;
4926
- /**
4927
- * Injection token for providing field signal context to mappers and components.
4928
- *
4929
- * The field signal context is the "nervous system" of the dynamic form library,
4930
- * providing access to:
4931
- * - The form instance and structure
4932
- * - Current form values (as signals)
4933
- * - Default values
4934
- * - Validation messages
4935
- * - The injector for creating components
4936
- *
4937
- * This token is provided by the DynamicForm component and can be scoped
4938
- * for nested forms (Groups, Arrays) via child injectors.
4939
- *
4940
- * @example
4941
- * ```typescript
4942
- * // In a mapper function
4943
- * export function mapValueField(fieldDef: BaseValueField<any, any>): Binding[] {
4944
- * const context = inject(FIELD_SIGNAL_CONTEXT);
4945
- * const form = context.form();
4946
- * // ... use context
4947
- * }
4948
- *
4949
- * // In a component
4950
- * export class MyFieldComponent {
4951
- * private context = inject(FIELD_SIGNAL_CONTEXT);
4952
- * }
4953
- * ```
4954
- */
4955
- declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
4956
- /**
4957
- * Injection token for providing array context metadata to mappers and components.
4958
- *
4959
- * This token is optionally provided by ArrayFieldComponent when creating injectors
4960
- * for array items. It contains metadata about the array item's position and parent.
4961
- *
4962
- * Mappers can inject this token with {optional: true} to access array context:
4963
- *
4964
- * @example
4965
- * ```typescript
4966
- * // In a mapper function
4967
- * export function buttonMapper(fieldDef: FieldDef<any>): Binding[] {
4968
- * const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
4969
- * if (arrayContext) {
4970
- * // Use arrayContext.index, arrayContext.arrayKey, etc.
4971
- * }
4972
- * }
4973
- * ```
4974
- */
4975
- declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
4976
- /**
4977
- * Injection token for providing group ancestry to mappers and components.
4978
- *
4979
- * Re-provided in two places:
4980
- * 1. `GroupFieldComponent` provides a context whose `groupPath()` is the
4981
- * parent path extended by its own key (e.g. `'address'` or `'user.address'`).
4982
- * 2. `createArrayItemInjector` provides a sentinel with an empty `groupPath`,
4983
- * resetting the chain at array boundaries — descendants of an array item
4984
- * that re-enter a group compose paths INSIDE the array item, matching the
4985
- * property-derivation collector's keying.
4986
- *
4987
- * `mapFieldToInputs` reads this to scope DOM IDs and override-store keys so
4988
- * the same leaf key can appear in different groups without colliding (#401).
4989
- *
4990
- * Inject with `{ optional: true }` because top-level fields with no group
4991
- * ancestor (and no enclosing array) won't have the token provided. The token
4992
- * IS present for fields inside an array even when no inner group exists — the
4993
- * array-boundary sentinel makes injection succeed with an empty `groupPath`.
4994
- */
4995
- declare const GROUP_CONTEXT: InjectionToken<GroupContext>;
4996
- /**
4997
- * Injection token for form-level default props.
4998
- *
4999
- * Default props are form-wide property defaults that are merged with field-level props.
5000
- * Field props take precedence over default props.
5001
- *
5002
- * Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
5003
- * DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
5004
- * via Angular's hierarchical injector.
5005
- *
5006
- * The token provides a Signal to enable reactivity - when config changes, mappers
5007
- * that read the signal inside their computed() will automatically update.
5008
- *
5009
- * @example
5010
- * ```typescript
5011
- * // In a mapper function
5012
- * const defaultPropsSignal = inject(DEFAULT_PROPS);
5013
- *
5014
- * return computed(() => {
5015
- * const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
5016
- * const baseInputs = buildBaseInputs(fieldDef, defaultProps);
5017
- * // ...
5018
- * });
5019
- * ```
5020
- */
5021
- declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
5022
- /**
5023
- * Injection token for form-level default validation messages.
5024
- *
5025
- * Default validation messages act as fallbacks when fields have validation
5026
- * errors but no field-level `validationMessages` defined.
5027
- *
5028
- * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5029
- * and inherited by all children via Angular's hierarchical injector.
5030
- *
5031
- * The token provides a Signal to enable reactivity - when config changes, mappers
5032
- * that read the signal inside their computed() will automatically update.
5033
- *
5034
- * @example
5035
- * ```typescript
5036
- * // In a mapper or component
5037
- * const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
5038
- *
5039
- * return computed(() => {
5040
- * const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
5041
- * const message = defaultMessages?.required ?? 'Field is required';
5042
- * // ...
5043
- * });
5044
- * ```
5045
- */
5046
- declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
5047
- /**
5048
- * Injection token for form-level options.
5049
- *
5050
- * Form options control form-wide behavior including button disabled states.
5051
- * Used by button mappers to determine default disabled behavior.
5052
- *
5053
- * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5054
- * and inherited by all children via Angular's hierarchical injector.
5055
- *
5056
- * The token provides a Signal to enable reactivity - when config changes, mappers
5057
- * that read the signal inside their computed() will automatically update.
5058
- *
5059
- * @example
5060
- * ```typescript
5061
- * // In a button mapper
5062
- * const formOptionsSignal = inject(FORM_OPTIONS);
5063
- *
5064
- * return computed(() => {
5065
- * const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
5066
- * const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
5067
- * // ...
5068
- * });
5069
- * ```
5070
- */
5071
- declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
5072
-
5073
- /**
5074
- * Type guard to check if a field is a container field (page, row, group, or array)
5075
- * Container fields have a 'fields' property and don't contribute values directly.
5076
- * This overload works with RegisteredFieldTypes for full type narrowing.
5077
- */
5078
- declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
5079
- /**
5080
- * Type guard to check if a field is a container field (page, row, group, or array).
5081
- * This overload works with any FieldDef for looser type checking.
5082
- */
5083
- declare function isContainerField(field: FieldDef<unknown>): boolean;
5084
- /**
5085
- * Type guard to check if a field is a leaf field (value or display field)
5086
- * Leaf fields don't have children and either contribute values or display content
5087
- */
5088
- declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
5089
- /**
5090
- * Type guard to check if a field has a value property (value-bearing field)
5091
- * These fields contribute to the form value output
5092
- * Note: Using `unknown` in the Extract condition to match any value type
5093
- */
5094
- declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
5095
- value: unknown;
5096
- }>;
5097
- /**
5098
- * Type guard to check if a field is excluded from form values (display-only field)
5099
- * Currently this includes text fields and any other fields without a value property
5100
- */
5101
- declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
5102
-
5103
- /**
5104
- * Helper types for creating type-safe field configurations with proper nesting constraints
5105
- */
5106
-
5107
- /**
5108
- * Type helper for accessing nested field paths safely
5109
- * This allows accessing child paths while maintaining some type safety
5110
- */
5111
- type FieldPathAccess<TValue> = {
5112
- [K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
5113
- };
5114
-
5115
5205
  /**
5116
5206
  * User-facing wrapper registration shape used with `createWrappers(...)`.
5117
5207
  *
@@ -5199,6 +5289,48 @@ type InferWrapperRegistry<T> = T extends WrappersBundle<infer R> ? {
5199
5289
  };
5200
5290
  } : never;
5201
5291
 
5292
+ /**
5293
+ * Type guard to check if a field is a container field (page, row, group, or array)
5294
+ * Container fields have a 'fields' property and don't contribute values directly.
5295
+ * This overload works with RegisteredFieldTypes for full type narrowing.
5296
+ */
5297
+ declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
5298
+ /**
5299
+ * Type guard to check if a field is a container field (page, row, group, or array).
5300
+ * This overload works with any FieldDef for looser type checking.
5301
+ */
5302
+ declare function isContainerField(field: FieldDef<unknown>): boolean;
5303
+ /**
5304
+ * Type guard to check if a field is a leaf field (value or display field)
5305
+ * Leaf fields don't have children and either contribute values or display content
5306
+ */
5307
+ declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
5308
+ /**
5309
+ * Type guard to check if a field has a value property (value-bearing field)
5310
+ * These fields contribute to the form value output
5311
+ * Note: Using `unknown` in the Extract condition to match any value type
5312
+ */
5313
+ declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
5314
+ value: unknown;
5315
+ }>;
5316
+ /**
5317
+ * Type guard to check if a field is excluded from form values (display-only field)
5318
+ * Currently this includes text fields and any other fields without a value property
5319
+ */
5320
+ declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
5321
+
5322
+ /**
5323
+ * Helper types for creating type-safe field configurations with proper nesting constraints
5324
+ */
5325
+
5326
+ /**
5327
+ * Type helper for accessing nested field paths safely
5328
+ * This allows accessing child paths while maintaining some type safety
5329
+ */
5330
+ type FieldPathAccess<TValue> = {
5331
+ [K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
5332
+ };
5333
+
5202
5334
  /**
5203
5335
  * Extract FieldDef type from FieldTypeDefinition
5204
5336
  */
@@ -5237,390 +5369,1479 @@ type ExtractFieldTypes<T extends FieldTypeOrFeature[]> = {
5237
5369
  * @param items - Field type definitions and/or features (like withLoggerConfig)
5238
5370
  * @returns Environment providers for dependency injection with type inference
5239
5371
  *
5240
- * @example
5241
- * ```typescript
5242
- * // Application-level setup
5243
- * bootstrapApplication(AppComponent, {
5244
- * providers: [
5245
- * provideDynamicForm(...withMaterialFields())
5246
- * ]
5247
- * });
5248
- * ```
5372
+ * @example
5373
+ * ```typescript
5374
+ * // Application-level setup
5375
+ * bootstrapApplication(AppComponent, {
5376
+ * providers: [
5377
+ * provideDynamicForm(...withMaterialFields())
5378
+ * ]
5379
+ * });
5380
+ * ```
5381
+ *
5382
+ * @example
5383
+ * ```typescript
5384
+ * // Disable logging
5385
+ * bootstrapApplication(AppComponent, {
5386
+ * providers: [
5387
+ * provideDynamicForm(
5388
+ * ...withMaterialFields(),
5389
+ * withLoggerConfig(false)
5390
+ * )
5391
+ * ]
5392
+ * });
5393
+ * ```
5394
+ *
5395
+ * @example
5396
+ * ```typescript
5397
+ * // Custom field types with type inference
5398
+ * import { CustomFieldType, AnotherFieldType } from './custom-fields';
5399
+ *
5400
+ * export const appConfig: ApplicationConfig = {
5401
+ * providers: [
5402
+ * provideDynamicForm(CustomFieldType, AnotherFieldType)
5403
+ * ]
5404
+ * };
5405
+ * ```
5406
+ *
5407
+ * @typeParam T - Array of field type definitions for type inference
5408
+ *
5409
+ * @public
5410
+ */
5411
+ declare function provideDynamicForm<const T extends FieldTypeOrFeature[]>(...items: T): ProvideDynamicFormResult<ExtractFieldTypes<T> extends FieldTypeDefinition[] ? ExtractFieldTypes<T> : FieldTypeDefinition[]>;
5412
+ /**
5413
+ * Extract FieldDef types from provider result
5414
+ */
5415
+ type ExtractFieldDefs<T> = T extends {
5416
+ __fieldDefs?: infer F;
5417
+ } ? F : never;
5418
+ /**
5419
+ * Extract form value type from provider result
5420
+ */
5421
+ type ExtractFormValue<T> = T extends {
5422
+ __formValue?: infer V;
5423
+ } ? V : never;
5424
+
5425
+ /**
5426
+ * Type representing a FieldTree for an array.
5427
+ * Extends FieldTree to add numeric indexing for accessing item FieldTrees.
5428
+ */
5429
+ type ArrayFieldTree<T> = FieldTree<T[]> & {
5430
+ readonly [index: number]: FieldTree<T> | undefined;
5431
+ };
5432
+ /**
5433
+ * Get the length of an array FieldTree.
5434
+ */
5435
+ declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
5436
+ /**
5437
+ * Read-only view of a single field's observable state.
5438
+ *
5439
+ * Whitelisted read signals copied from Angular Signal Forms' `FieldState` so wrappers
5440
+ * can observe a field without being able to mutate it. New write-capable members
5441
+ * added in future Angular versions are excluded by default (the `Pick` list stays
5442
+ * the source of truth).
5443
+ *
5444
+ * `value` is narrowed from `WritableSignal<TValue>` to `Signal<TValue>` so consumers
5445
+ * cannot write through it.
5446
+ */
5447
+ interface ReadonlyFieldTree<TValue = unknown> {
5448
+ readonly value: Signal<TValue>;
5449
+ readonly valid: Signal<boolean>;
5450
+ readonly invalid: Signal<boolean>;
5451
+ readonly touched: Signal<boolean>;
5452
+ readonly dirty: Signal<boolean>;
5453
+ readonly required: Signal<boolean>;
5454
+ readonly disabled: Signal<boolean>;
5455
+ readonly hidden: Signal<boolean>;
5456
+ readonly errors: Signal<readonly unknown[]>;
5457
+ }
5458
+ /**
5459
+ * Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
5460
+ * Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
5461
+ * so consumers only see the narrow surface and Angular's `WritableSignal` capability
5462
+ * on `value` is hidden.
5463
+ */
5464
+ declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
5465
+ /**
5466
+ * Write a value into a FieldTree's underlying signal. Signal Forms stores
5467
+ * `FieldState.value` as a `WritableSignal`; this helper centralises the cast
5468
+ * and warns if that contract ever changes (vs a silent no-op).
5469
+ */
5470
+ declare function writeToFieldValue<T>(value: Signal<T>, next: T, logger?: {
5471
+ warn: (msg: string) => void;
5472
+ }): boolean;
5473
+
5474
+ /**
5475
+ * Fields every addon-action context carries, regardless of whether the
5476
+ * addon is bound to a host field or rendered orphaned.
5477
+ */
5478
+ interface AddonActionContextBase<TValue> {
5479
+ /**
5480
+ * Identity of the host field this addon is attached to. Empty strings
5481
+ * indicate the addon is rendered outside an active field context (rare).
5482
+ * Use {@link isFieldBoundContext} or the `form !== null` check to
5483
+ * narrow before relying on field identity.
5484
+ */
5485
+ readonly field: {
5486
+ readonly key: string;
5487
+ readonly type: string;
5488
+ };
5489
+ /**
5490
+ * Current value of the host field at the time the action fires.
5491
+ * `undefined` when the host field has no value yet (initial render before
5492
+ * defaultValue resolution) or when the addon is rendered outside a field.
5493
+ */
5494
+ readonly value: TValue | undefined;
5495
+ }
5496
+ /**
5497
+ * Field-bound variant — the addon is attached to a real field and has a
5498
+ * working value-writer. The common case for `pi-button` / `mat-button` /
5499
+ * `bs-button` / `ion-button` inside an adapter input field.
5500
+ *
5501
+ * `setValue` is non-optional here; the optional chain (`ctx.setValue?.()`)
5502
+ * is unnecessary noise once you've narrowed via {@link isFieldBoundContext}.
5503
+ */
5504
+ interface FieldBoundAddonActionContext<TValue = unknown> extends AddonActionContextBase<TValue> {
5505
+ /** Read-only view of the host field's tree — same view wrappers receive. */
5506
+ readonly form: ReadonlyFieldTree<TValue>;
5507
+ /** Push a new value into the host field. */
5508
+ readonly setValue: (next: TValue) => void;
5509
+ }
5510
+ /**
5511
+ * Orphan variant — the addon is rendered outside any field component (rare;
5512
+ * happens when consumers compose `<df-addon-slot>` directly without a host
5513
+ * field). No writer is reachable; reading `value` may also yield `undefined`.
5514
+ */
5515
+ interface OrphanAddonActionContext<TValue = unknown> extends AddonActionContextBase<TValue> {
5516
+ readonly form: null;
5517
+ /** Always absent in the orphan variant — distinguishes the union members. */
5518
+ readonly setValue?: undefined;
5519
+ }
5520
+ /**
5521
+ * Context handed to inline addon actions and registered handler functions.
5522
+ *
5523
+ * Discriminated by `form`: field-bound contexts have a non-null `form` and a
5524
+ * required `setValue`; orphan contexts have `form: null` and no `setValue`.
5525
+ * Use {@link isFieldBoundContext} when the handler needs to write back.
5526
+ *
5527
+ * @example
5528
+ * ```typescript
5529
+ * provideAddonActions({
5530
+ * submit: (ctx) => {
5531
+ * if (!isFieldBoundContext(ctx)) return;
5532
+ * // ctx.setValue is now `(next: TValue) => void` — no optional chain.
5533
+ * myService.send(ctx.field.key, ctx.value, ctx.setValue);
5534
+ * },
5535
+ * });
5536
+ * ```
5537
+ */
5538
+ type AddonActionContext<TValue = unknown> = FieldBoundAddonActionContext<TValue> | OrphanAddonActionContext<TValue>;
5539
+ /** Type guard narrowing {@link AddonActionContext} to its field-bound variant. */
5540
+ declare function isFieldBoundContext<TValue>(ctx: AddonActionContext<TValue>): ctx is FieldBoundAddonActionContext<TValue>;
5541
+ /**
5542
+ * Built-in preset actions shared across adapters.
5543
+ *
5544
+ * Adapter kinds that accept a `preset` field render the corresponding
5545
+ * behaviour:
5546
+ *
5547
+ * - `'clear'`: empties the field value.
5548
+ * - `'reset'`: restores the field's configured default value (resolved
5549
+ * from the form's `defaultValues` map at click time); falls back to
5550
+ * empty when no default is reachable.
5551
+ * - `'paste'`: reads from the system clipboard and writes the result.
5552
+ * - `'copy'`: writes the field's current value to the system clipboard.
5553
+ * - `'toggle-password-visibility'`: flips a host input's `type` between
5554
+ * `password` and `text` (requires the host field to provide a type
5555
+ * override token, e.g., `PRIME_INPUT_TYPE_OVERRIDE`).
5556
+ *
5557
+ * Form submission is intentionally NOT exposed as a button-addon preset —
5558
+ * use the dedicated `'submit'` field type instead.
5559
+ */
5560
+ type CommonAddonActionPreset = 'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility';
5561
+ /**
5562
+ * Module-augmentable registry of adapter-specific preset names.
5563
+ *
5564
+ * Adapters add their canonical actions (e.g., Material's
5565
+ * `'mat-datepicker-open'`) without forking the `AddonActionPreset` union.
5566
+ *
5567
+ * @example
5568
+ * ```typescript
5569
+ * declare module '@ng-forge/dynamic-forms' {
5570
+ * interface DynamicFormAddonActionPresetRegistry {
5571
+ * 'mat-datepicker-open': true;
5572
+ * }
5573
+ * }
5574
+ * ```
5575
+ */
5576
+ interface DynamicFormAddonActionPresetRegistry {
5577
+ }
5578
+ /**
5579
+ * Union of every preset name an addon may reference — universal presets
5580
+ * plus any contributed via {@link DynamicFormAddonActionPresetRegistry}.
5581
+ *
5582
+ * The `(string & {})` escape hatch keeps autocomplete on the known +
5583
+ * augmented presets while accepting arbitrary strings for one-off adapter
5584
+ * presets that don't warrant a module-augmentation. The runtime preset
5585
+ * runner already handles unknown names gracefully via its `default:` arm
5586
+ * (logs a warning, no throw). Mirrors `FieldDef.type`'s
5587
+ * `RegisteredFieldTypes['type'] | (string & {})` shape.
5588
+ */
5589
+ type AddonActionPreset = CommonAddonActionPreset | keyof DynamicFormAddonActionPresetRegistry | (string & {});
5590
+ /**
5591
+ * Module-augmentable registry of user-defined named action handlers.
5592
+ *
5593
+ * Users register handlers once at bootstrap with `provideAddonActions({...})`
5594
+ * and reference them from JSON-driven configs via `actionRef: 'handlerName'`.
5595
+ *
5596
+ * @example
5597
+ * ```typescript
5598
+ * declare module '@ng-forge/dynamic-forms' {
5599
+ * interface DynamicFormActionRegistry {
5600
+ * openProfile: true;
5601
+ * runWorkflow: true;
5602
+ * }
5603
+ * }
5604
+ * ```
5605
+ */
5606
+ interface DynamicFormActionRegistry {
5607
+ }
5608
+ /**
5609
+ * Typed string handle to a registered action handler. The empty registry
5610
+ * resolves to `never`; augmenting `DynamicFormActionRegistry` populates the
5611
+ * union and unlocks autocomplete.
5612
+ */
5613
+ type RegisteredActionRef = keyof DynamicFormActionRegistry;
5614
+
5615
+ /**
5616
+ * Map of named `TemplateRef`s collected by `<df-dynamic-form>` from
5617
+ * `<ng-template dfTemplate="...">` children projected into its content.
5618
+ * The directive selector is `dfTemplate` (see `DfTemplate`); the input
5619
+ * value is the name addons reference via `templateKey`.
5620
+ *
5621
+ * The `template` addon kind looks up its `templateKey` in this map at
5622
+ * render time. Exposed as a `Signal` so projections can be added or
5623
+ * removed reactively (e.g., when the host conditionally projects).
5624
+ *
5625
+ * Provided at the form-component level (not root) so multiple
5626
+ * `<df-dynamic-form>` instances on the same page have independent
5627
+ * template scopes.
5628
+ */
5629
+ declare const DF_FIELD_TEMPLATES: InjectionToken<Signal<ReadonlyMap<string, TemplateRef<unknown>>>>;
5630
+
5631
+ interface FieldOption<T = unknown> {
5632
+ label: DynamicText;
5633
+ value: T;
5634
+ disabled?: boolean;
5635
+ [key: string]: unknown;
5636
+ }
5637
+
5638
+ /**
5639
+ * Injection token for form-level default wrappers.
5640
+ *
5641
+ * Provides a Signal of the `defaultWrappers` array from FormConfig. Consumed by
5642
+ * `DfFieldOutlet` / `ContainerFieldComponent` to merge into each field's
5643
+ * effective wrapper chain (lowest priority after auto-associations, higher
5644
+ * than field-level `wrappers`).
5645
+ *
5646
+ * Provided once at the DynamicForm level and inherited via Angular's
5647
+ * hierarchical injector.
5648
+ */
5649
+ declare const DEFAULT_WRAPPERS: InjectionToken<Signal<readonly WrapperConfig[] | undefined>>;
5650
+ /**
5651
+ * Injection token for providing field signal context to mappers and components.
5652
+ *
5653
+ * The field signal context is the "nervous system" of the dynamic form library,
5654
+ * providing access to:
5655
+ * - The form instance and structure
5656
+ * - Current form values (as signals)
5657
+ * - Default values
5658
+ * - Validation messages
5659
+ * - The injector for creating components
5660
+ *
5661
+ * This token is provided by the DynamicForm component and can be scoped
5662
+ * for nested forms (Groups, Arrays) via child injectors.
5663
+ *
5664
+ * @example
5665
+ * ```typescript
5666
+ * // In a mapper function — prefer the helper for required access (descriptive error).
5667
+ * export function mapValueField(fieldDef: BaseValueField<any, any>): Binding[] {
5668
+ * const context = injectFieldSignalContext();
5669
+ * const form = context.form();
5670
+ * // ... use context
5671
+ * }
5672
+ *
5673
+ * // Optional access — token directly, returns null if not provided.
5674
+ * const ctx = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
5675
+ * if (ctx) {
5676
+ * // ...
5677
+ * }
5678
+ * ```
5679
+ */
5680
+ declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
5681
+ /**
5682
+ * Throwing accessor for {@link FIELD_SIGNAL_CONTEXT}.
5683
+ *
5684
+ * Use when the consumer requires the context — group/array containers and
5685
+ * mappers that depend on form structure. Throws a descriptive
5686
+ * `DynamicFormError` instead of Angular's generic `NullInjectorError` when
5687
+ * the token isn't provided.
5688
+ *
5689
+ * Consumers that may legitimately run outside a `DynamicForm` should keep
5690
+ * using `inject(FIELD_SIGNAL_CONTEXT, { optional: true })`.
5691
+ */
5692
+ declare function injectFieldSignalContext<TModel extends Record<string, unknown> = Record<string, unknown>>(): FieldSignalContext<TModel>;
5693
+ /**
5694
+ * Injection token for providing array context metadata to mappers and components.
5695
+ *
5696
+ * This token is optionally provided by ArrayFieldComponent when creating injectors
5697
+ * for array items. It contains metadata about the array item's position and parent.
5698
+ *
5699
+ * Mappers can inject this token with {optional: true} to access array context:
5700
+ *
5701
+ * @example
5702
+ * ```typescript
5703
+ * // In a mapper function
5704
+ * export function buttonMapper(fieldDef: FieldDef<any>): Binding[] {
5705
+ * const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
5706
+ * if (arrayContext) {
5707
+ * // Use arrayContext.index, arrayContext.arrayKey, etc.
5708
+ * }
5709
+ * }
5710
+ * ```
5711
+ */
5712
+ declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
5713
+ /**
5714
+ * Injection token for providing group ancestry to mappers and components.
5715
+ *
5716
+ * Re-provided in two places:
5717
+ * 1. `GroupFieldComponent` provides a context whose `groupPath()` is the
5718
+ * parent path extended by its own key (e.g. `'address'` or `'user.address'`).
5719
+ * 2. `createArrayItemInjector` provides a sentinel with an empty `groupPath`,
5720
+ * resetting the chain at array boundaries — descendants of an array item
5721
+ * that re-enter a group compose paths INSIDE the array item, matching the
5722
+ * property-derivation collector's keying.
5723
+ *
5724
+ * `mapFieldToInputs` reads this to scope DOM IDs and override-store keys so
5725
+ * the same leaf key can appear in different groups without colliding (#401).
5726
+ *
5727
+ * Inject with `{ optional: true }` because top-level fields with no group
5728
+ * ancestor (and no enclosing array) won't have the token provided. The token
5729
+ * IS present for fields inside an array even when no inner group exists — the
5730
+ * array-boundary sentinel makes injection succeed with an empty `groupPath`.
5731
+ */
5732
+ declare const GROUP_CONTEXT: InjectionToken<GroupContext>;
5733
+ /**
5734
+ * Injection token for form-level default props.
5735
+ *
5736
+ * Default props are form-wide property defaults that are merged with field-level props.
5737
+ * Field props take precedence over default props.
5738
+ *
5739
+ * Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
5740
+ * DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
5741
+ * via Angular's hierarchical injector.
5742
+ *
5743
+ * The token provides a Signal to enable reactivity - when config changes, mappers
5744
+ * that read the signal inside their computed() will automatically update.
5745
+ *
5746
+ * @example
5747
+ * ```typescript
5748
+ * // In a mapper function
5749
+ * const defaultPropsSignal = inject(DEFAULT_PROPS);
5750
+ *
5751
+ * return computed(() => {
5752
+ * const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
5753
+ * const baseInputs = buildBaseInputs(fieldDef, defaultProps);
5754
+ * // ...
5755
+ * });
5756
+ * ```
5757
+ */
5758
+ declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
5759
+ /**
5760
+ * Injection token for form-level default validation messages.
5761
+ *
5762
+ * Default validation messages act as fallbacks when fields have validation
5763
+ * errors but no field-level `validationMessages` defined.
5764
+ *
5765
+ * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5766
+ * and inherited by all children via Angular's hierarchical injector.
5767
+ *
5768
+ * The token provides a Signal to enable reactivity - when config changes, mappers
5769
+ * that read the signal inside their computed() will automatically update.
5770
+ *
5771
+ * @example
5772
+ * ```typescript
5773
+ * // In a mapper or component
5774
+ * const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
5775
+ *
5776
+ * return computed(() => {
5777
+ * const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
5778
+ * const message = defaultMessages?.required ?? 'Field is required';
5779
+ * // ...
5780
+ * });
5781
+ * ```
5782
+ */
5783
+ declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
5784
+ /**
5785
+ * Injection token for form-level options.
5786
+ *
5787
+ * Form options control form-wide behavior including button disabled states.
5788
+ * Used by button mappers to determine default disabled behavior.
5789
+ *
5790
+ * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5791
+ * and inherited by all children via Angular's hierarchical injector.
5792
+ *
5793
+ * The token provides a Signal to enable reactivity - when config changes, mappers
5794
+ * that read the signal inside their computed() will automatically update.
5795
+ *
5796
+ * @example
5797
+ * ```typescript
5798
+ * // In a button mapper
5799
+ * const formOptionsSignal = inject(FORM_OPTIONS);
5800
+ *
5801
+ * return computed(() => {
5802
+ * const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
5803
+ * const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
5804
+ * // ...
5805
+ * });
5806
+ * ```
5807
+ */
5808
+ declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
5809
+
5810
+ /**
5811
+ * Built-in field types provided by the dynamic form library.
5812
+ *
5813
+ * Each field type is validated at compile time using satisfies, ensuring
5814
+ * type safety of the mapper function while allowing the array to be typed
5815
+ * as FieldTypeDefinition[] for consumer flexibility.
5816
+ */
5817
+ declare const BUILT_IN_FIELDS: FieldTypeDefinition[];
5818
+
5819
+ /**
5820
+ * Configuration options for the logger feature.
5821
+ *
5822
+ * @public
5823
+ */
5824
+ interface LoggerConfigOptions {
5825
+ /**
5826
+ * Whether general logging is enabled.
5827
+ *
5828
+ * @default true
5829
+ */
5830
+ enabled?: boolean;
5831
+ /**
5832
+ * Derivation logging level.
5833
+ *
5834
+ * - `'none'`: No derivation debug logging (default)
5835
+ * - `'summary'`: Log cycle completion with counts
5836
+ * - `'verbose'`: Log individual derivation evaluations with details
5837
+ *
5838
+ * @default 'none'
5839
+ */
5840
+ derivations?: DerivationLogLevel;
5841
+ }
5842
+ /**
5843
+ * Configure logging for dynamic forms.
5844
+ *
5845
+ * By default, general logging is enabled (ConsoleLogger) and derivation
5846
+ * logging is disabled ('none'). Use this feature to enable derivation debugging.
5847
+ *
5848
+ * @example
5849
+ * ```typescript
5850
+ * // Disable all logging
5851
+ * provideDynamicForm(
5852
+ * ...withMaterialFields(),
5853
+ * withLoggerConfig(false)
5854
+ * )
5855
+ *
5856
+ * // Enable verbose derivation logging
5857
+ * provideDynamicForm(
5858
+ * ...withMaterialFields(),
5859
+ * withLoggerConfig({ derivations: 'verbose' })
5860
+ * )
5861
+ *
5862
+ * // Disable derivation logging but keep general logging
5863
+ * provideDynamicForm(
5864
+ * ...withMaterialFields(),
5865
+ * withLoggerConfig({ derivations: 'none' })
5866
+ * )
5867
+ *
5868
+ * // Conditional logging (e.g., only in dev mode)
5869
+ * provideDynamicForm(
5870
+ * ...withMaterialFields(),
5871
+ * withLoggerConfig(() => isDevMode())
5872
+ * )
5873
+ * ```
5874
+ *
5875
+ * @param config - Boolean to enable/disable logging, a function returning boolean, or a config object
5876
+ * @returns A DynamicFormFeature that configures logging
5877
+ *
5878
+ * @public
5879
+ */
5880
+ declare function withLoggerConfig(config?: boolean | (() => boolean) | LoggerConfigOptions): DynamicFormFeature<'logger'>;
5881
+
5882
+ /**
5883
+ * Injection token for the dynamic forms logger.
5884
+ *
5885
+ * Provided by provideDynamicForm() with ConsoleLogger as default.
5886
+ * Override with withLoggerConfig(false) to disable logging.
5887
+ */
5888
+ declare const DynamicFormLogger: InjectionToken<Logger>;
5889
+
5890
+ /**
5891
+ * Console-based logger implementation.
5892
+ */
5893
+ declare class ConsoleLogger implements Logger {
5894
+ private readonly prefix;
5895
+ debug(message: string, ...args: unknown[]): void;
5896
+ info(message: string, ...args: unknown[]): void;
5897
+ warn(message: string, ...args: unknown[]): void;
5898
+ error(message: string, ...args: unknown[]): void;
5899
+ }
5900
+
5901
+ /**
5902
+ * No-operation logger implementation.
5903
+ * All methods are no-ops - used in production by default.
5904
+ */
5905
+ declare class NoopLogger implements Logger {
5906
+ debug(): void;
5907
+ info(): void;
5908
+ warn(): void;
5909
+ error(): void;
5910
+ }
5911
+
5912
+ /**
5913
+ * Enables automatic form value attachment to all events dispatched through the EventBus.
5914
+ *
5915
+ * When this feature is enabled, events dispatched via `eventBus.dispatch()` will include
5916
+ * the current form value in the `formValue` property. This is useful for event handlers
5917
+ * that need access to the complete form state at the time of the event.
5918
+ *
5919
+ * @example Global opt-in
5920
+ * ```typescript
5921
+ * provideDynamicForm(
5922
+ * ...withMaterialFields(),
5923
+ * withEventFormValue()
5924
+ * )
5925
+ * ```
5926
+ *
5927
+ * @example Consumer usage
5928
+ * ```typescript
5929
+ * eventBus.on<SubmitEvent>('submit').subscribe(event => {
5930
+ * if (hasFormValue(event)) {
5931
+ * console.log('Form value:', event.formValue);
5932
+ * }
5933
+ * });
5934
+ * ```
5935
+ *
5936
+ * @example Per-form disable (when globally enabled)
5937
+ * ```typescript
5938
+ * const config: FormConfig = {
5939
+ * fields: [...],
5940
+ * options: { emitFormValueOnEvents: false }
5941
+ * };
5942
+ * ```
5943
+ *
5944
+ * @remarks
5945
+ * **Precedence rules:**
5946
+ * 1. Per-form `false` - Disables for this form, regardless of global setting
5947
+ * 2. Per-form `true` - Enables for this form, regardless of global setting
5948
+ * 3. Per-form `undefined` - Uses global setting
5949
+ * 4. Global `withEventFormValue()` - Enables globally
5950
+ * 5. No global feature - Disabled globally (default)
5951
+ *
5952
+ * @returns A DynamicFormFeature that enables form value attachment to events
5953
+ *
5954
+ * @public
5955
+ */
5956
+ declare function withEventFormValue(): DynamicFormFeature<'event-form-value'>;
5957
+
5958
+ /**
5959
+ * Configuration for excluding field values from form submission output
5960
+ * based on their reactive state (hidden, disabled, readonly).
5961
+ *
5962
+ * Supports a 3-tier configuration hierarchy: Field > Form > Global.
5963
+ * The most specific level wins for each property.
5964
+ *
5965
+ * @public
5966
+ */
5967
+ interface ValueExclusionConfig {
5968
+ /**
5969
+ * Whether to exclude the value of hidden fields from submission output.
5970
+ *
5971
+ * When `true`, fields whose `hidden()` state is `true` will have their
5972
+ * values omitted from the submitted form value.
5973
+ *
5974
+ * @remarks
5975
+ * This does NOT affect HiddenField (`type: 'hidden'`) — those fields
5976
+ * store values without UI and their `hidden()` state is always `false`.
5977
+ */
5978
+ readonly excludeValueIfHidden?: boolean;
5979
+ /**
5980
+ * Whether to exclude the value of disabled fields from submission output.
5981
+ *
5982
+ * When `true`, fields whose `disabled()` state is `true` will have their
5983
+ * values omitted from the submitted form value.
5984
+ */
5985
+ readonly excludeValueIfDisabled?: boolean;
5986
+ /**
5987
+ * Whether to exclude the value of readonly fields from submission output.
5988
+ *
5989
+ * When `true`, fields whose `readonly()` state is `true` will have their
5990
+ * values omitted from the submitted form value.
5991
+ */
5992
+ readonly excludeValueIfReadonly?: boolean;
5993
+ }
5994
+ /**
5995
+ * Fully resolved exclusion config with all properties required.
5996
+ * Produced by the 3-tier resolution: `field ?? form ?? global`.
5997
+ *
5998
+ * @public
5999
+ */
6000
+ type ResolvedValueExclusionConfig = Required<ValueExclusionConfig>;
6001
+
6002
+ /**
6003
+ * Configures global value exclusion defaults for form submission output.
6004
+ *
6005
+ * Value exclusion is **enabled by default** — field values are excluded from the
6006
+ * `(submitted)` output based on their reactive state. Use this feature to
6007
+ * override those defaults. This does NOT affect two-way binding (`value` model /
6008
+ * `entity`) — fields retain their values internally.
6009
+ *
6010
+ * @example Default behavior (all exclusions enabled)
6011
+ * ```typescript
6012
+ * provideDynamicForm(
6013
+ * ...withMaterialFields(),
6014
+ * withValueExclusionDefaults()
6015
+ * )
6016
+ * ```
6017
+ *
6018
+ * @example Disable specific exclusions
6019
+ * ```typescript
6020
+ * provideDynamicForm(
6021
+ * ...withMaterialFields(),
6022
+ * withValueExclusionDefaults({ excludeValueIfReadonly: false })
6023
+ * )
6024
+ * ```
6025
+ *
6026
+ * @example Disable all exclusions (restore pre-v1 behavior)
6027
+ * ```typescript
6028
+ * provideDynamicForm(
6029
+ * ...withMaterialFields(),
6030
+ * withValueExclusionDefaults({
6031
+ * excludeValueIfHidden: false,
6032
+ * excludeValueIfDisabled: false,
6033
+ * excludeValueIfReadonly: false,
6034
+ * })
6035
+ * )
6036
+ * ```
6037
+ *
6038
+ * @remarks
6039
+ * **Precedence rules:**
6040
+ * 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
6041
+ * 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
6042
+ * 3. Global `withValueExclusionDefaults()` — baseline default
6043
+ * 4. No global feature — uses token default (all enabled)
6044
+ *
6045
+ * @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
6046
+ * @returns A DynamicFormFeature that configures value exclusion defaults
6047
+ *
6048
+ * @public
6049
+ */
6050
+ declare function withValueExclusionDefaults(config?: Partial<ValueExclusionConfig>): DynamicFormFeature<'value-exclusion'>;
6051
+
6052
+ /**
6053
+ * Configuration for when validation runs based on a field's reactive state.
6054
+ *
6055
+ * Currently controls whether hidden fields are validated. Resolved through a
6056
+ * tree-aware hierarchy: per-field overrides inherit down to descendants, with
6057
+ * per-form `FormOptions` and global `withValidationExecutionDefaults()` providing
6058
+ * the root inherited value.
6059
+ *
6060
+ * @public
6061
+ */
6062
+ interface ValidationExecutionConfig {
6063
+ /**
6064
+ * Whether validators run while a field is hidden (statically, via `hidden`
6065
+ * logic, or because any ancestor is hidden).
6066
+ *
6067
+ * When `false`, all validators on a hidden field are skipped — the field is
6068
+ * effectively inert until shown. State logic (hidden/disabled/readonly) and
6069
+ * value derivations continue to apply.
6070
+ */
6071
+ readonly validateWhenHidden?: boolean;
6072
+ }
6073
+ /**
6074
+ * Fully resolved validation execution config — every property defined.
6075
+ *
6076
+ * @public
6077
+ */
6078
+ type ResolvedValidationExecutionConfig = Required<ValidationExecutionConfig>;
6079
+
6080
+ /**
6081
+ * Configures global validation execution defaults — when validators should run
6082
+ * relative to a field's reactive state.
6083
+ *
6084
+ * By default, validators are **skipped** for hidden fields (`validateWhenHidden: false`),
6085
+ * matching what most users expect: a hidden field is inert. Use this feature to
6086
+ * restore the legacy behavior where hidden fields still validate.
6087
+ *
6088
+ * @example Default behavior — no feature needed
6089
+ * ```typescript
6090
+ * provideDynamicForm(...withMaterialFields())
6091
+ * // hidden fields skip validation
6092
+ * ```
6093
+ *
6094
+ * @example Restore legacy validation-on-hidden behavior
6095
+ * ```typescript
6096
+ * provideDynamicForm(
6097
+ * ...withMaterialFields(),
6098
+ * withValidationExecutionDefaults({ validateWhenHidden: true })
6099
+ * )
6100
+ * ```
6101
+ *
6102
+ * @remarks
6103
+ * **Precedence rules:**
6104
+ * 1. Per-field `FieldDef.validateWhenHidden` — wins for that field; inherits down to descendants
6105
+ * that don't override.
6106
+ * 2. Per-form `FormOptions.validateWhenHidden` — root inherited value for the form.
6107
+ * 3. Global `withValidationExecutionDefaults()` — fallback when no form/field setting.
6108
+ * 4. No feature configured — uses token default (`validateWhenHidden: false`).
6109
+ *
6110
+ * @param config - Partial override. Unspecified properties keep their defaults.
6111
+ * @returns A `DynamicFormFeature` that configures validation execution defaults.
6112
+ *
6113
+ * @public
6114
+ */
6115
+ declare function withValidationExecutionDefaults(config?: Partial<ValidationExecutionConfig>): DynamicFormFeature<'validation-execution'>;
6116
+
6117
+ /**
6118
+ * Opt into Angular Signal Forms' legacy `.ng-touched` / `.ng-invalid` /
6119
+ * `.ng-dirty` / `.ng-pristine` / `.ng-untouched` / `.ng-pending` /
6120
+ * `.ng-valid` CSS classes on form-bound elements.
6121
+ *
6122
+ * Wires Angular's `@angular/forms/signals/compat` `NG_STATUS_CLASSES` strategy
6123
+ * via `provideSignalFormsConfig`. Without this feature the modern Signal Forms
6124
+ * class strategy is active, which omits the legacy `ng-*` classes.
5249
6125
  *
5250
- * @example
6126
+ * @example Opt in once at the application root
5251
6127
  * ```typescript
5252
- * // Disable logging
5253
6128
  * bootstrapApplication(AppComponent, {
5254
6129
  * providers: [
5255
6130
  * provideDynamicForm(
5256
6131
  * ...withMaterialFields(),
5257
- * withLoggerConfig(false)
6132
+ * withLegacyStatusClasses()
5258
6133
  * )
5259
6134
  * ]
5260
6135
  * });
5261
6136
  * ```
5262
6137
  *
5263
- * @example
5264
- * ```typescript
5265
- * // Custom field types with type inference
5266
- * import { CustomFieldType, AnotherFieldType } from './custom-fields';
5267
- *
5268
- * export const appConfig: ApplicationConfig = {
5269
- * providers: [
5270
- * provideDynamicForm(CustomFieldType, AnotherFieldType)
5271
- * ]
5272
- * };
5273
- * ```
5274
- *
5275
- * @typeParam T - Array of field type definitions for type inference
6138
+ * @returns A DynamicFormFeature that enables the legacy `ng-*` class strategy
5276
6139
  *
5277
6140
  * @public
5278
6141
  */
5279
- declare function provideDynamicForm<const T extends FieldTypeOrFeature[]>(...items: T): ProvideDynamicFormResult<ExtractFieldTypes<T> extends FieldTypeDefinition[] ? ExtractFieldTypes<T> : FieldTypeDefinition[]>;
5280
- /**
5281
- * Extract FieldDef types from provider result
5282
- */
5283
- type ExtractFieldDefs<T> = T extends {
5284
- __fieldDefs?: infer F;
5285
- } ? F : never;
5286
- /**
5287
- * Extract form value type from provider result
5288
- */
5289
- type ExtractFormValue<T> = T extends {
5290
- __formValue?: infer V;
5291
- } ? V : never;
6142
+ declare function withLegacyStatusClasses(): DynamicFormFeature<'legacy-status-classes'>;
5292
6143
 
5293
6144
  /**
5294
- * Built-in field types provided by the dynamic form library.
6145
+ * Zero-cost type carrier used in wrapper registrations.
5295
6146
  *
5296
- * Each field type is validated at compile time using satisfies, ensuring
5297
- * type safety of the mapper function while allowing the array to be typed
5298
- * as FieldTypeDefinition[] for consumer flexibility.
6147
+ * Returns `undefined` at runtime but is typed as `T`, so TypeScript can
6148
+ * thread a wrapper's config type through `createWrappers(...)` into the
6149
+ * `InferWrapperRegistry<typeof ...>` utility without requiring users to
6150
+ * hand-write the augmentation shape.
6151
+ *
6152
+ * @example
6153
+ * ```typescript
6154
+ * const wrappers = createWrappers(
6155
+ * {
6156
+ * wrapperName: 'section',
6157
+ * loadComponent: () => import('./section-wrapper'),
6158
+ * props: wrapperProps<SectionWrapper>(),
6159
+ * },
6160
+ * );
6161
+ *
6162
+ * declare module '@ng-forge/dynamic-forms' {
6163
+ * interface FieldRegistryWrappers extends InferWrapperRegistry<typeof wrappers> {}
6164
+ * }
6165
+ * ```
5299
6166
  */
5300
- declare const BUILT_IN_FIELDS: FieldTypeDefinition[];
6167
+ declare function wrapperProps<T>(): T;
5301
6168
 
5302
6169
  /**
5303
- * Configuration options for the logger feature.
6170
+ * Input shape passed to a wrapper component via the `fieldInputs` input.
5304
6171
  *
5305
- * @public
6172
+ * Carries the wrapped field's mapper outputs plus an optional `field`
6173
+ * read-only view. Wrappers reached through a container path (which has
6174
+ * no FieldTree of its own) receive `fieldInputs === undefined`, so any
6175
+ * wrapper that reads `fieldInputs.field` must guard for it.
6176
+ *
6177
+ * Mappers must emit new rawInputs objects per tick rather than mutating
6178
+ * the previous one — this bag is a shallow spread, so mutations to shared
6179
+ * nested values leak to downstream wrappers.
5306
6180
  */
5307
- interface LoggerConfigOptions {
5308
- /**
5309
- * Whether general logging is enabled.
5310
- *
5311
- * @default true
5312
- */
5313
- enabled?: boolean;
6181
+ interface WrapperFieldInputs {
6182
+ readonly key: string;
6183
+ /** Field-type discriminant (mirrors `FieldDef.type`) — useful for kind-aware addons. */
6184
+ readonly type?: string;
6185
+ readonly label?: string;
6186
+ readonly placeholder?: string;
6187
+ readonly className?: string;
6188
+ readonly props?: Record<string, unknown>;
6189
+ readonly validationMessages?: Record<string, string>;
6190
+ readonly defaultValidationMessages?: Record<string, string>;
6191
+ readonly field?: ReadonlyFieldTree;
5314
6192
  /**
5315
- * Derivation logging level.
5316
- *
5317
- * - `'none'`: No derivation debug logging (default)
5318
- * - `'summary'`: Log cycle completion with counts
5319
- * - `'verbose'`: Log individual derivation evaluations with details
5320
- *
5321
- * @default 'none'
6193
+ * Write a value into the wrapped field's tree. Provided by `buildFieldInputs`
6194
+ * when a `FieldTree` is present. Addons (e.g., `prime-button` presets) use this
6195
+ * to mutate the host field; wrappers should treat the field as read-only.
5322
6196
  */
5323
- derivations?: DerivationLogLevel;
6197
+ readonly setValue?: (next: unknown) => void;
6198
+ readonly [key: string]: unknown;
5324
6199
  }
6200
+
5325
6201
  /**
5326
- * Configure logging for dynamic forms.
6202
+ * Dispatcher component that renders an addon by looking up its `kind` in
6203
+ * `ADDON_KIND_REGISTRY` and instantiating the registered component.
5327
6204
  *
5328
- * By default, general logging is enabled (ConsoleLogger) and derivation
5329
- * logging is disabled ('none'). Use this feature to enable derivation debugging.
6205
+ * Used by every adapter's field component to render slot content:
5330
6206
  *
5331
- * @example
5332
- * ```typescript
5333
- * // Disable all logging
5334
- * provideDynamicForm(
5335
- * ...withMaterialFields(),
5336
- * withLoggerConfig(false)
5337
- * )
6207
+ * ```html
6208
+ * \@for (a of addonsAt('prefix'); track $index) {
6209
+ * <df-addon-slot [addon]="a" />
6210
+ * }
6211
+ * ```
5338
6212
  *
5339
- * // Enable verbose derivation logging
5340
- * provideDynamicForm(
5341
- * ...withMaterialFields(),
5342
- * withLoggerConfig({ derivations: 'verbose' })
5343
- * )
6213
+ * Behaviour:
6214
+ * - Resolves and caches the kind's component asynchronously on first render.
6215
+ * - Forwards the `slot` HTML attribute on the host so Ionic shadow-DOM
6216
+ * projection works (`<ion-input>` projects children with `slot="start"`
6217
+ * into its native start slot).
6218
+ * - Honours the addon's reactive `hidden` flag — when `true`, the host is
6219
+ * `display: none` so it occupies no layout but stays in DOM (cheaper than
6220
+ * tearing down the component).
6221
+ * - Forwards `className` to the host element.
6222
+ * - ARIA semantics are owned by individual kind components — decorative
6223
+ * kinds (icons, text) set `aria-hidden="true"` on themselves; interactive
6224
+ * kinds (buttons) handle their own `aria-label`.
6225
+ *
6226
+ * If the kind cannot be resolved (registry miss, load failure), the
6227
+ * dispatcher logs a warning and renders nothing — never throws. The
6228
+ * runtime addon validator should have caught unknown kinds at config init,
6229
+ * but this provides a defence-in-depth fallback for misconfigured registries.
6230
+ */
6231
+ declare class DfAddonSlot {
6232
+ private readonly registry;
6233
+ private readonly logger;
6234
+ private readonly hostInjector;
6235
+ readonly addon: _angular_core.InputSignal<AnyAddon>;
6236
+ /**
6237
+ * Optional read-only view of the host field's mapped inputs — same shape
6238
+ * wrappers receive (`field?: ReadonlyFieldTree`, `key`, `props`, etc.).
6239
+ * Forwarded to kind components so they can read field state without
6240
+ * re-injecting `FIELD_SIGNAL_CONTEXT` and rebuilding context themselves.
6241
+ *
6242
+ * `undefined` when the addon is rendered outside a field (rare).
6243
+ */
6244
+ readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
6245
+ /**
6246
+ * Pre-resolved `hidden` signal supplied by the host field. When provided,
6247
+ * the dispatcher reads this instead of re-resolving `addon.hidden` —
6248
+ * lets the host (e.g., `prime-input`) share a single subscription per
6249
+ * Observable-typed `hidden` between its own visibility filter and the
6250
+ * slot's `[style.display]` binding.
6251
+ */
6252
+ readonly hidden: _angular_core.InputSignal<Signal<boolean> | undefined>;
6253
+ /** `slot` HTML attribute forwarded to host — needed for Ionic shadow projection. */
6254
+ protected readonly slotAttr: Signal<_ng_forge_dynamic_forms.AddonSlot>;
6255
+ protected readonly className: Signal<string | null>;
6256
+ /**
6257
+ * Locally-resolved `hidden` signal — always reflects `addon.hidden`
6258
+ * regardless of whether the host supplied a pre-resolved signal.
6259
+ * Populated via `explicitEffect` so `resolveDynamicValue` (which may call
6260
+ * `toSignal` for Observable-typed values) does NOT run inside a reactive
6261
+ * context (NG0602). The WeakMap caches per `hidden`-value identity, so
6262
+ * swapping `addon` inputs that share the same Observable reuses the
6263
+ * subscription instead of leaking a fresh one per swap.
6264
+ *
6265
+ * Resolved unconditionally (not gated on host `hidden()` presence) so a
6266
+ * transition from `host hidden = Signal` → `undefined` doesn't expose a
6267
+ * brief window where neither value is current — the upstream effect
6268
+ * re-runs eventually, but the computed read can happen in between.
6269
+ */
6270
+ private readonly _hiddenByValue;
6271
+ private readonly _resolvedHidden;
6272
+ /**
6273
+ * `hidden` resolved from `DynamicValue<boolean>` to a flat `Signal<boolean>`.
6274
+ * Prefers the host-supplied signal when present (avoids duplicate `toSignal`
6275
+ * subscriptions for Observable-typed `hidden`); falls back to the locally-
6276
+ * resolved signal, which is always populated by the addon-tracking effect.
6277
+ */
6278
+ protected readonly isHidden: Signal<boolean>;
6279
+ /**
6280
+ * Latest async-loaded `{ kind, component }` pair. Tagging the result with
6281
+ * its kind lets `resolvedComponent` filter stale loads structurally — no
6282
+ * monotonic token needed. When `addon.kind` changes mid-load, the prior
6283
+ * load still completes and writes here, but the computed ignores it
6284
+ * because the recorded kind no longer matches the current one.
6285
+ */
6286
+ private readonly asyncLoaded;
6287
+ /**
6288
+ * Resolved kind component — composed declaratively from two sources of
6289
+ * truth: the registry's synchronous cache (warm hits, e.g., a second
6290
+ * `<df-addon-slot>` for an already-loaded kind) and the kind-tagged async
6291
+ * loader result. Reading `addon().kind` first makes the computed react to
6292
+ * addon swaps and naturally invalidate stale async-load results.
6293
+ */
6294
+ protected readonly resolvedComponent: Signal<Type<unknown> | undefined>;
6295
+ /** Inputs passed to the kind component — addon plus the wrapper-style host bag. */
6296
+ protected readonly kindInputs: Signal<{
6297
+ addon: AnyAddon;
6298
+ fieldInputs: WrapperFieldInputs | undefined;
6299
+ }>;
6300
+ constructor();
6301
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DfAddonSlot, never>;
6302
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DfAddonSlot, "df-addon-slot", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
6303
+ }
6304
+
6305
+ /**
6306
+ * Marks an `<ng-template>` projected into `<df-dynamic-form>` content as
6307
+ * available to template addons by name.
5344
6308
  *
5345
- * // Disable derivation logging but keep general logging
5346
- * provideDynamicForm(
5347
- * ...withMaterialFields(),
5348
- * withLoggerConfig({ derivations: 'none' })
5349
- * )
6309
+ * The host form collects all `DfTemplate` instances via `contentChildren`
6310
+ * and exposes them through {@link DF_FIELD_TEMPLATES}, where the template
6311
+ * addon kind looks them up by `templateKey` at render time.
5350
6312
  *
5351
- * // Conditional logging (e.g., only in dev mode)
5352
- * provideDynamicForm(
5353
- * ...withMaterialFields(),
5354
- * withLoggerConfig(() => isDevMode())
5355
- * )
6313
+ * @example
6314
+ * ```html
6315
+ * <df-dynamic-form [config]="config">
6316
+ * <ng-template dfTemplate="searchIcon" let-field>
6317
+ * <my-icon [field]="field" />
6318
+ * </ng-template>
6319
+ * </df-dynamic-form>
5356
6320
  * ```
5357
6321
  *
5358
- * @param config - Boolean to enable/disable logging, a function returning boolean, or a config object
5359
- * @returns A DynamicFormFeature that configures logging
5360
- *
5361
- * @public
6322
+ * In the field config:
6323
+ * ```ts
6324
+ * { slot: 'prefix', kind: 'template', templateKey: 'searchIcon' }
6325
+ * ```
5362
6326
  */
5363
- declare function withLoggerConfig(config?: boolean | (() => boolean) | LoggerConfigOptions): DynamicFormFeature<'logger'>;
6327
+ declare class DfTemplate {
6328
+ /** Name this template registers under — referenced from `TemplateAddon.templateKey`. */
6329
+ readonly dfTemplate: _angular_core.InputSignal<string>;
6330
+ /** The wrapped template, captured for registration and `NgTemplateOutlet` rendering. */
6331
+ readonly templateRef: TemplateRef<unknown>;
6332
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DfTemplate, never>;
6333
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<DfTemplate, "ng-template[dfTemplate]", ["dfTemplate"], { "dfTemplate": { "alias": "dfTemplate"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
6334
+ }
5364
6335
 
5365
6336
  /**
5366
- * Injection token for the dynamic forms logger.
6337
+ * Renderer for the universal `text` addon kind.
5367
6338
  *
5368
- * Provided by provideDynamicForm() with ConsoleLogger as default.
5369
- * Override with withLoggerConfig(false) to disable logging.
5370
- */
5371
- declare const DynamicFormLogger: InjectionToken<Logger>;
5372
-
5373
- /**
5374
- * Console-based logger implementation.
6339
+ * Renders the addon's `text` (a `DynamicText`) inside a span. Decorative —
6340
+ * sets `aria-hidden="true"` on the host so screen readers skip it; users
6341
+ * who need announcement should use a `*-button` kind with `ariaLabel` instead.
5375
6342
  */
5376
- declare class ConsoleLogger implements Logger {
5377
- private readonly prefix;
5378
- debug(message: string, ...args: unknown[]): void;
5379
- info(message: string, ...args: unknown[]): void;
5380
- warn(message: string, ...args: unknown[]): void;
5381
- error(message: string, ...args: unknown[]): void;
6343
+ declare class TextAddonComponent {
6344
+ readonly addon: _angular_core.InputSignal<TextAddon>;
6345
+ /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
6346
+ readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
6347
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TextAddonComponent, never>;
6348
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TextAddonComponent, "df-text-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
5382
6349
  }
5383
6350
 
5384
6351
  /**
5385
- * No-operation logger implementation.
5386
- * All methods are no-ops - used in production by default.
6352
+ * Renderer for the universal `template` addon kind.
6353
+ *
6354
+ * Resolves the addon's `templateKey` against {@link DF_FIELD_TEMPLATES}
6355
+ * (populated by `<df-dynamic-form>` from `<ng-template dfTemplate="...">`
6356
+ * children) and renders via `NgTemplateOutlet`. The wrapper-style host bag
6357
+ * (`fieldInputs`) is forwarded as the template's implicit context — author
6358
+ * the template as `<ng-template dfTemplate="..." let-fi>{{ fi.key }}</ng-template>`.
6359
+ *
6360
+ * If the key is unresolved, logs a warning and renders nothing — keeps the
6361
+ * form alive when a backend references a template the FE has not registered.
5387
6362
  */
5388
- declare class NoopLogger implements Logger {
5389
- debug(): void;
5390
- info(): void;
5391
- warn(): void;
5392
- error(): void;
6363
+ declare class TemplateAddonComponent {
6364
+ private readonly templates;
6365
+ private readonly logger;
6366
+ readonly addon: _angular_core.InputSignal<TemplateAddon>;
6367
+ /** Forwarded by `df-addon-slot`; surfaced to the template as its implicit `$implicit` context. */
6368
+ readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
6369
+ protected readonly template: _angular_core.Signal<_angular_core.TemplateRef<unknown> | null>;
6370
+ /** Implicit context payload — template authors bind via `let-fi`. */
6371
+ protected readonly outletContext: _angular_core.Signal<{
6372
+ $implicit: WrapperFieldInputs | undefined;
6373
+ }>;
6374
+ constructor();
6375
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TemplateAddonComponent, never>;
6376
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TemplateAddonComponent, "df-template-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
5393
6377
  }
5394
6378
 
5395
6379
  /**
5396
- * Enables automatic form value attachment to all events dispatched through the EventBus.
5397
- *
5398
- * When this feature is enabled, events dispatched via `eventBus.dispatch()` will include
5399
- * the current form value in the `formValue` property. This is useful for event handlers
5400
- * that need access to the complete form state at the time of the event.
5401
- *
5402
- * @example Global opt-in
5403
- * ```typescript
5404
- * provideDynamicForm(
5405
- * ...withMaterialFields(),
5406
- * withEventFormValue()
5407
- * )
5408
- * ```
5409
- *
5410
- * @example Consumer usage
5411
- * ```typescript
5412
- * eventBus.on<SubmitEvent>('submit').subscribe(event => {
5413
- * if (hasFormValue(event)) {
5414
- * console.log('Form value:', event.formValue);
5415
- * }
5416
- * });
5417
- * ```
5418
- *
5419
- * @example Per-form disable (when globally enabled)
5420
- * ```typescript
5421
- * const config: FormConfig = {
5422
- * fields: [...],
5423
- * options: { emitFormValueOnEvents: false }
5424
- * };
5425
- * ```
5426
- *
5427
- * @remarks
5428
- * **Precedence rules:**
5429
- * 1. Per-form `false` - Disables for this form, regardless of global setting
5430
- * 2. Per-form `true` - Enables for this form, regardless of global setting
5431
- * 3. Per-form `undefined` - Uses global setting
5432
- * 4. Global `withEventFormValue()` - Enables globally
5433
- * 5. No global feature - Disabled globally (default)
6380
+ * Renderer for the universal `component` addon kind.
5434
6381
  *
5435
- * @returns A DynamicFormFeature that enables form value attachment to events
6382
+ * Loads the user-supplied component lazily (`addon.component()`) and renders
6383
+ * it via `NgComponentOutlet` with `addon.inputs` forwarded as input bindings.
6384
+ * The wrapper-style `fieldInputs` bag is not propagated — user components
6385
+ * receive only what `addon.inputs` declares. Authors that need field state
6386
+ * should switch to the `template` kind or build a wrapper.
5436
6387
  *
5437
- * @public
6388
+ * @codeOnly The `component` loader is a function; this kind is dropped in
6389
+ * lenient validation when the config originated from JSON.
5438
6390
  */
5439
- declare function withEventFormValue(): DynamicFormFeature<'event-form-value'>;
6391
+ declare class ComponentAddonComponent {
6392
+ private readonly logger;
6393
+ readonly addon: _angular_core.InputSignal<ComponentAddon>;
6394
+ /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
6395
+ readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
6396
+ protected readonly inputs: _angular_core.Signal<Record<string, unknown>>;
6397
+ /**
6398
+ * Latest async-loaded `{ addon, component }` pair. Tagging the result with
6399
+ * its source addon lets `resolvedComponent` filter stale loads structurally
6400
+ * — same kind-tagging pattern `DfAddonSlot` uses. When `addon` swaps
6401
+ * mid-load, the prior loader still completes and writes here, but the
6402
+ * computed ignores it because the recorded addon no longer matches the
6403
+ * current one.
6404
+ */
6405
+ private readonly asyncLoaded;
6406
+ protected readonly resolvedComponent: _angular_core.Signal<Type<unknown> | undefined>;
6407
+ constructor();
6408
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ComponentAddonComponent, never>;
6409
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ComponentAddonComponent, "df-component-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
6410
+ }
5440
6411
 
5441
6412
  /**
5442
- * Configuration for excluding field values from form submission output
5443
- * based on their reactive state (hidden, disabled, readonly).
6413
+ * Optional collaborators a preset may need beyond the base context. Passed
6414
+ * by the button kind component which has access to its own DI scope.
5444
6415
  *
5445
- * Supports a 3-tier configuration hierarchy: Field > Form > Global.
5446
- * The most specific level wins for each property.
5447
- *
5448
- * @public
6416
+ * Adapter input components fill in the collaborators their preset semantics
6417
+ * require:
6418
+ * - `mat-input` / `bs-input` / `prime-input` / `ion-input` populate
6419
+ * `typeOverride` + `fieldValueSetter` + `fieldDefaultValueGetter` +
6420
+ * `baselineType` from their own DI scope.
6421
+ * - Addons rendered outside any input field receive `{ logger }` only and
6422
+ * the input-specific presets warn-and-no-op.
5449
6423
  */
5450
- interface ValueExclusionConfig {
6424
+ interface PresetCollaborators {
6425
+ /** Per-field input-type override signal — populated only inside an input field. */
6426
+ readonly typeOverride?: WritableSignal<string | undefined>;
6427
+ /** Writer that pushes a value into the host field's tree. */
6428
+ readonly fieldValueSetter?: (next: unknown) => void;
5451
6429
  /**
5452
- * Whether to exclude the value of hidden fields from submission output.
5453
- *
5454
- * When `true`, fields whose `hidden()` state is `true` will have their
5455
- * values omitted from the submitted form value.
5456
- *
5457
- * @remarks
5458
- * This does NOT affect HiddenField (`type: 'hidden'`) — those fields
5459
- * store values without UI and their `hidden()` state is always `false`.
6430
+ * Read the host field's configured default value (from the form's
6431
+ * `defaultValues` map keyed by `field.key`). Used by the `'reset'`
6432
+ * preset; absent when the addon is rendered outside an active field
6433
+ * context, in which case `'reset'` falls back to clearing.
5460
6434
  */
5461
- readonly excludeValueIfHidden?: boolean;
6435
+ readonly fieldDefaultValueGetter?: () => unknown;
5462
6436
  /**
5463
- * Whether to exclude the value of disabled fields from submission output.
5464
- *
5465
- * When `true`, fields whose `disabled()` state is `true` will have their
5466
- * values omitted from the submitted form value.
6437
+ * Baseline configured input type (from `props().type`), read at preset-run
6438
+ * time. Used by `toggle-password-visibility` to refuse to corrupt
6439
+ * `'email'`/`'number'`/etc. inputs into `'password'` when no override is
6440
+ * yet set.
5467
6441
  */
5468
- readonly excludeValueIfDisabled?: boolean;
6442
+ readonly baselineType?: () => string | undefined;
5469
6443
  /**
5470
- * Whether to exclude the value of readonly fields from submission output.
6444
+ * Logger for warnings about presets that can't be fulfilled in this context.
5471
6445
  *
5472
- * When `true`, fields whose `readonly()` state is `true` will have their
5473
- * values omitted from the submitted form value.
6446
+ * Passed via collaborators (rather than injected) because `runPresetAction`
6447
+ * is a DI-free pure function shared across adapters — each adapter's
6448
+ * `ADDON_PRESET_HANDLER` factory injects `DynamicFormLogger` itself and
6449
+ * forwards it here. The `[Dynamic Forms]` prefix comes from
6450
+ * `ConsoleLogger`'s implementation, so the final console line is identical
6451
+ * to one logged directly via `inject(DynamicFormLogger)`.
5474
6452
  */
5475
- readonly excludeValueIfReadonly?: boolean;
6453
+ readonly logger: Logger;
6454
+ }
6455
+ /**
6456
+ * Apply a built-in preset action against the addon's host field. Shared
6457
+ * across all adapters — each adapter's `ADDON_PRESET_HANDLER` provider
6458
+ * delegates here.
6459
+ *
6460
+ * Presets that require adapter-specific UI mechanics (clipboard, type
6461
+ * override) are best-effort — they no-op with a warning when the necessary
6462
+ * collaborator isn't available (e.g., toggle-password-visibility outside an
6463
+ * input field).
6464
+ *
6465
+ * @param adapterLabel Used in warnings — `'Material'` / `'Bootstrap'` / etc.
6466
+ * @param fieldLabel Used in warnings — `'mat-input'` / `'bs-input'` / etc.
6467
+ */
6468
+ declare function runPresetAction(preset: AddonActionPreset, ctx: AddonActionContext, collaborators: PresetCollaborators, adapterLabel: string, fieldLabel: string): Promise<void>;
6469
+
6470
+ /**
6471
+ * Cache for resolved addon-kind components.
6472
+ *
6473
+ * Provided per-form by `provideDynamicFormDI()` so two `<df-dynamic-form>`
6474
+ * instances with conflicting `loadComponent` factories for the same kind
6475
+ * never share a stale entry. Has no `providedIn: 'root'` factory — the
6476
+ * registry consumer is always inside a form scope, and a root fallback
6477
+ * would only hide misconfiguration (an SSR cross-request hit if a future
6478
+ * code path forgot to provide it locally).
6479
+ *
6480
+ * Mirrors `COMPONENT_CACHE` (field types) and `WRAPPER_COMPONENT_CACHE`
6481
+ * (wrappers).
6482
+ *
6483
+ * @internal
6484
+ */
6485
+ declare const ADDON_KIND_COMPONENT_CACHE: InjectionToken<Map<string, Type<unknown>>>;
6486
+ /**
6487
+ * Public shape returned by {@link injectAddonKindRegistry}. Pinned explicitly
6488
+ * so the API extractor doesn't churn between versions when the inner factory
6489
+ * gets refactored.
6490
+ */
6491
+ interface AddonKindRegistryRef {
6492
+ getKind(kind: string): AddonKindDefinition | undefined;
6493
+ hasKind(kind: string): boolean;
6494
+ getKindNames(): string[];
6495
+ loadKindComponent(kind: string): Promise<Type<unknown>>;
6496
+ getLoadedKindComponent(kind: string): Type<unknown> | undefined;
6497
+ readonly raw: ReadonlyMap<string, AddonKindDefinition>;
5476
6498
  }
5477
6499
  /**
5478
- * Fully resolved exclusion config with all properties required.
5479
- * Produced by the 3-tier resolution: `field ?? form ?? global`.
6500
+ * Injection function for accessing the addon kind registry.
5480
6501
  *
5481
- * @public
6502
+ * Usage parallels {@link injectFieldRegistry}: lookup, lazy load, cache.
6503
+ * Must be called within an injection context.
6504
+ *
6505
+ * @example
6506
+ * ```typescript
6507
+ * const addonKinds = injectAddonKindRegistry();
6508
+ * const Component = await addonKinds.loadKindComponent('prime-icon');
6509
+ * ```
5482
6510
  */
5483
- type ResolvedValueExclusionConfig = Required<ValueExclusionConfig>;
6511
+ declare function injectAddonKindRegistry(): AddonKindRegistryRef;
5484
6512
 
5485
6513
  /**
5486
- * Configures global value exclusion defaults for form submission output.
6514
+ * One entry per field type that declares addon support.
5487
6515
  *
5488
- * Value exclusion is **enabled by default** — field values are excluded from the
5489
- * `(submitted)` output based on their reactive state. Use this feature to
5490
- * override those defaults. This does NOT affect two-way binding (`value` model /
5491
- * `entity`) — fields retain their values internally.
6516
+ * `name` is the field-type discriminant; `slots` and `allowedKinds` mirror
6517
+ * the `FieldTypeDefinition.addons` registration.
6518
+ */
6519
+ interface FieldAddonSupportEntry {
6520
+ readonly name: string;
6521
+ readonly slots: FieldAddonSupport['slots'];
6522
+ readonly allowedKinds: FieldAddonSupport['allowedKinds'];
6523
+ }
6524
+ /**
6525
+ * Walk `FIELD_REGISTRY` and return every field type that opted into addons
6526
+ * (i.e., declared `addons` on its `FieldTypeDefinition`).
5492
6527
  *
5493
- * @example Default behavior (all exclusions enabled)
6528
+ * Useful for tooling — admin UIs surfacing "which fields can carry addons?",
6529
+ * docs generators, MCP server cross-checks. Field types without an `addons`
6530
+ * registration ("Tier 3" — toggle, checkbox, radio, slider) are excluded.
6531
+ *
6532
+ * Must be called within an injection context.
6533
+ *
6534
+ * @example
5494
6535
  * ```typescript
5495
- * provideDynamicForm(
5496
- * ...withMaterialFields(),
5497
- * withValueExclusionDefaults()
5498
- * )
6536
+ * @Component({ ... })
6537
+ * class FormBuilder {
6538
+ * protected readonly addonable = injectFieldsSupportingAddons();
6539
+ *
6540
+ * constructor() {
6541
+ * // -> [{ name: 'prime-input', slots: ['prefix', 'suffix'], allowedKinds: [...] }, ...]
6542
+ * }
6543
+ * }
5499
6544
  * ```
6545
+ */
6546
+ declare function injectFieldsSupportingAddons(): FieldAddonSupportEntry[];
6547
+
6548
+ /**
6549
+ * Normalises a {@link DynamicValue} into a `Signal<T>`.
5500
6550
  *
5501
- * @example Disable specific exclusions
6551
+ * Accepts a static value, a `Signal<T>`, or an `Observable<T>`. The result
6552
+ * is a `Signal<T>` that reactively reflects the current value, suitable
6553
+ * for direct use in Angular templates.
6554
+ *
6555
+ * Static values yield a `computed` that always returns the same constant —
6556
+ * preferable to wrapping in `signal()` because the computed never causes
6557
+ * change-detection invalidation.
6558
+ *
6559
+ * Pass an `injector` when calling outside an Angular injection context
6560
+ * (e.g., from inside a `computed`). `toSignal` needs a `DestroyRef` to
6561
+ * unsubscribe — without an injector it falls back to `inject(DestroyRef)`,
6562
+ * which throws when called outside injection context.
6563
+ *
6564
+ * @example
5502
6565
  * ```typescript
5503
- * provideDynamicForm(
5504
- * ...withMaterialFields(),
5505
- * withValueExclusionDefaults({ excludeValueIfReadonly: false })
5506
- * )
6566
+ * const isHidden = resolveDynamicValue(addon().hidden, false);
6567
+ * // template: \@if (isHidden()) { ... }
5507
6568
  * ```
6569
+ */
6570
+ declare function resolveDynamicValue<T>(value: DynamicValue<T> | undefined, fallback: T, injector?: Injector): Signal<T>;
6571
+
6572
+ /**
6573
+ * Register a custom addon kind with the form.
5508
6574
  *
5509
- * @example Disable all exclusions (restore pre-v1 behavior)
6575
+ * The provided definition is added to `ADDON_KIND_REGISTRY` at form
6576
+ * initialisation, alongside the built-in `text` / `template` / `component`
6577
+ * kinds and any kinds contributed by adapter feature helpers
6578
+ * (`withPrimeNGAddons()` etc.).
6579
+ *
6580
+ * @example
5510
6581
  * ```typescript
5511
6582
  * provideDynamicForm(
5512
6583
  * ...withMaterialFields(),
5513
- * withValueExclusionDefaults({
5514
- * excludeValueIfHidden: false,
5515
- * excludeValueIfDisabled: false,
5516
- * excludeValueIfReadonly: false,
5517
- * })
5518
- * )
6584
+ * withCustomAddon({
6585
+ * kind: 'lucide-icon',
6586
+ * // Match how field types register: a default-exported component lets
6587
+ * // `loadComponent` return the import promise directly.
6588
+ * loadComponent: () => import('./lucide-icon-addon.component'),
6589
+ * validate: (addon) => {
6590
+ * if (typeof addon.icon !== 'string') {
6591
+ * throw new Error('lucide-icon: icon string is required');
6592
+ * }
6593
+ * },
6594
+ * }),
6595
+ * );
5519
6596
  * ```
5520
6597
  *
5521
- * @remarks
5522
- * **Precedence rules:**
5523
- * 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
5524
- * 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
5525
- * 3. Global `withValueExclusionDefaults()` — baseline default
5526
- * 4. No global feature — uses token default (all enabled)
5527
- *
5528
- * @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
5529
- * @returns A DynamicFormFeature that configures value exclusion defaults
5530
- *
5531
- * @public
6598
+ * Multiple `withCustomAddon(...)` calls are supported; later registrations
6599
+ * for the same kind override earlier ones (with a warning logged).
5532
6600
  */
5533
- declare function withValueExclusionDefaults(config?: Partial<ValueExclusionConfig>): DynamicFormFeature<'value-exclusion'>;
6601
+ declare function withCustomAddon<T extends BaseAddon>(definition: AddonKindDefinition<T>): DynamicFormFeature<'addons'>;
5534
6602
 
5535
6603
  /**
5536
- * Configuration for when validation runs based on a field's reactive state.
5537
- *
5538
- * Currently controls whether hidden fields are validated. Resolved through a
5539
- * tree-aware hierarchy: per-field overrides inherit down to descendants, with
5540
- * per-form `FormOptions` and global `withValidationExecutionDefaults()` providing
5541
- * the root inherited value.
6604
+ * Handler signature for user-registered addon actions.
5542
6605
  *
5543
- * @public
6606
+ * Backends reference handlers by name via `actionRef: 'name'` on button-style
6607
+ * addons; the dispatcher invokes the registered function with the addon's
6608
+ * action context.
5544
6609
  */
5545
- interface ValidationExecutionConfig {
5546
- /**
5547
- * Whether validators run while a field is hidden (statically, via `hidden`
5548
- * logic, or because any ancestor is hidden).
5549
- *
5550
- * When `false`, all validators on a hidden field are skipped — the field is
5551
- * effectively inert until shown. State logic (hidden/disabled/readonly) and
5552
- * value derivations continue to apply.
5553
- */
5554
- readonly validateWhenHidden?: boolean;
5555
- }
6610
+ type AddonActionHandler<TValue = unknown> = (ctx: AddonActionContext<TValue>) => void;
5556
6611
  /**
5557
- * Fully resolved validation execution config — every property defined.
6612
+ * Multi-provider token populated by `provideAddonActions({...})` calls.
6613
+ * `ADDON_ACTION_REGISTRY` aggregates these into a single name → handler map.
5558
6614
  *
5559
- * @public
6615
+ * @internal
5560
6616
  */
5561
- type ResolvedValidationExecutionConfig = Required<ValidationExecutionConfig>;
6617
+ declare const ADDON_ACTION_HANDLERS: InjectionToken<readonly Record<string, AddonActionHandler<unknown>>[]>;
6618
+ /**
6619
+ * Form-scoped name → handler map. Read by adapter button kinds at click
6620
+ * time for `actionRef: 'name'` lookups. Provided at form scope (not root)
6621
+ * because `ADDON_ACTION_HANDLERS` is form-scoped via `provideAddonActions`.
6622
+ */
6623
+ declare const ADDON_ACTION_REGISTRY: InjectionToken<ReadonlyMap<string, AddonActionHandler<unknown>>>;
5562
6624
 
5563
6625
  /**
5564
- * Configures global validation execution defaults — when validators should run
5565
- * relative to a field's reactive state.
6626
+ * Typed feature returned by {@link provideAddonActions}. Carries the
6627
+ * registered handler keys as a phantom type so consumers can derive the
6628
+ * `DynamicFormActionRegistry` augmentation in one line instead of hand-typing
6629
+ * every key.
5566
6630
  *
5567
- * By default, validators are **skipped** for hidden fields (`validateWhenHidden: false`),
5568
- * matching what most users expect: a hidden field is inert. Use this feature to
5569
- * restore the legacy behavior where hidden fields still validate.
6631
+ * Phantom field is `__handlerKeys` (the leading underscores signal "internal,
6632
+ * type-only — not for runtime access").
6633
+ */
6634
+ type AddonActionsFeature<K extends string = string> = DynamicFormFeature<'addon-actions'> & {
6635
+ /** @internal Phantom field carrying the registered keys; never read at runtime. */
6636
+ readonly __handlerKeys?: K;
6637
+ };
6638
+ /**
6639
+ * Register named action handlers reachable via JSON-driven addon configs.
5570
6640
  *
5571
- * @example Default behavior — no feature needed
5572
- * ```typescript
5573
- * provideDynamicForm(...withMaterialFields())
5574
- * // hidden fields skip validation
5575
- * ```
6641
+ * Once registered, a backend can ship
6642
+ * `{ kind: 'prime-button', actionRef: 'openProfile' }` and the FE will invoke
6643
+ * the corresponding handler at click time.
6644
+ *
6645
+ * Define handlers once and augment the registry from the same object:
5576
6646
  *
5577
- * @example Restore legacy validation-on-hidden behavior
5578
6647
  * ```typescript
5579
- * provideDynamicForm(
5580
- * ...withMaterialFields(),
5581
- * withValidationExecutionDefaults({ validateWhenHidden: true })
5582
- * )
6648
+ * export const appActions = provideAddonActions({
6649
+ * openProfile: (ctx) => modalService.open(ProfileModal, { data: ctx.value }),
6650
+ * runWorkflow: (ctx) => workflowService.run(ctx.field.key, ctx.value),
6651
+ * });
6652
+ *
6653
+ * declare module '@ng-forge/dynamic-forms' {
6654
+ * // Pulls the keys straight off `appActions` — no manual list to keep in sync.
6655
+ * interface DynamicFormActionRegistry extends Record<NonNullable<typeof appActions['__handlerKeys']>, true> {}
6656
+ * }
6657
+ *
6658
+ * // Then in your bootstrap:
6659
+ * provideDynamicForm(...withPrimeNGFields(), appActions);
5583
6660
  * ```
5584
6661
  *
5585
- * @remarks
5586
- * **Precedence rules:**
5587
- * 1. Per-field `FieldDef.validateWhenHidden` — wins for that field; inherits down to descendants
5588
- * that don't override.
5589
- * 2. Per-form `FormOptions.validateWhenHidden` — root inherited value for the form.
5590
- * 3. Global `withValidationExecutionDefaults()` — fallback when no form/field setting.
5591
- * 4. No feature configured — uses token default (`validateWhenHidden: false`).
6662
+ * Multiple `provideAddonActions(...)` calls are merged; later names override
6663
+ * earlier ones for the same key.
6664
+ */
6665
+ declare function provideAddonActions<const H extends Record<string, AddonActionHandler>>(handlers: H): AddonActionsFeature<keyof H & string>;
6666
+
6667
+ /**
6668
+ * Multi-provider token used by `withCustomAddon(...)` features to contribute
6669
+ * addon kind definitions. `provideDynamicForm` aggregates all values into
6670
+ * the form-scoped `ADDON_KIND_REGISTRY` map.
5592
6671
  *
5593
- * @param config - Partial override. Unspecified properties keep their defaults.
5594
- * @returns A `DynamicFormFeature` that configures validation execution defaults.
6672
+ * @internal
6673
+ */
6674
+ declare const ADDON_KIND_DEFINITIONS: InjectionToken<readonly AddonKindDefinition<_ng_forge_dynamic_forms.BaseAddon<_ng_forge_dynamic_forms.AddonSlot>>[]>;
6675
+
6676
+ /**
6677
+ * Reason an addon was dropped during validation.
5595
6678
  *
5596
- * @public
6679
+ * Each variant carries its own context fields so log messages can be both
6680
+ * actionable (registered kinds enumerated, fix suggestion present) and
6681
+ * machine-readable (admin UIs surfacing per-error guidance).
5597
6682
  */
5598
- declare function withValidationExecutionDefaults(config?: Partial<ValidationExecutionConfig>): DynamicFormFeature<'validation-execution'>;
6683
+ type AddonWarning = {
6684
+ type: 'unknown-field-type';
6685
+ fieldKey: string;
6686
+ fieldType: string;
6687
+ } | {
6688
+ type: 'field-type-no-addon-support';
6689
+ fieldKey: string;
6690
+ fieldType: string;
6691
+ } | {
6692
+ type: 'unknown-slot';
6693
+ fieldKey: string;
6694
+ fieldType: string;
6695
+ slot: string;
6696
+ allowedSlots: readonly string[];
6697
+ } | {
6698
+ type: 'unknown-kind';
6699
+ fieldKey: string;
6700
+ kind: string;
6701
+ registeredKinds: readonly string[];
6702
+ } | {
6703
+ type: 'kind-not-allowed';
6704
+ fieldKey: string;
6705
+ fieldType: string;
6706
+ kind: string;
6707
+ allowedKinds: readonly string[];
6708
+ } | {
6709
+ type: 'shape-violation';
6710
+ fieldKey: string;
6711
+ kind: string;
6712
+ reason: string;
6713
+ } | {
6714
+ type: 'code-only-kind-in-json';
6715
+ fieldKey: string;
6716
+ kind: string;
6717
+ } | {
6718
+ type: 'code-only-action-in-json';
6719
+ fieldKey: string;
6720
+ reason: string;
6721
+ };
6722
+ /**
6723
+ * Render an `AddonWarning` to a developer-friendly message.
6724
+ *
6725
+ * Returns the bare message — the `[Dynamic Forms]` prefix is added by the
6726
+ * library's logger (`ConsoleLogger`) and by `DynamicFormError`. Pass the
6727
+ * result of this function directly into `logger.warn(...)` to get the
6728
+ * standard prefixed line; use `logAddonWarnings()` for direct
6729
+ * `console.warn` output (which prefixes manually).
6730
+ *
6731
+ * Messages are designed to be actionable: they enumerate the relevant set
6732
+ * (registered kinds, allowed slots) and suggest a likely fix.
6733
+ */
6734
+ declare function formatAddonWarning(w: AddonWarning): string;
5599
6735
 
5600
6736
  /**
5601
- * Zero-cost type carrier used in wrapper registrations.
6737
+ * Walk a field's addons; return the survivors plus a list of warnings for
6738
+ * anything dropped.
5602
6739
  *
5603
- * Returns `undefined` at runtime but is typed as `T`, so TypeScript can
5604
- * thread a wrapper's config type through `createWrappers(...)` into the
5605
- * `InferWrapperRegistry<typeof ...>` utility without requiring users to
5606
- * hand-write the augmentation shape.
6740
+ * Pure: receives the registries as arguments (so the function is reusable
6741
+ * outside Angular's DI — tests, server-side validation, etc.).
6742
+ *
6743
+ * @param field The field to validate.
6744
+ * @param fieldRegistry Snapshot of `FIELD_REGISTRY` map.
6745
+ * @param kindRegistry Snapshot of `ADDON_KIND_REGISTRY` map.
6746
+ * @param source Whether the config originated from JSON (lenient drop
6747
+ * of code-only kinds) or inline TypeScript.
6748
+ */
6749
+ declare function validateFieldAddons(field: FieldDef<unknown>, fieldRegistry: ReadonlyMap<string, FieldTypeDefinition>, kindRegistry: ReadonlyMap<string, AddonKindDefinition>, source: 'json' | 'inline'): {
6750
+ addons: AnyAddon[];
6751
+ warnings: AddonWarning[];
6752
+ };
6753
+ /**
6754
+ * Recursively walk a field tree, validating each field's addons. Returns a
6755
+ * sanitized copy of the input field array plus accumulated warnings.
6756
+ */
6757
+ declare function walkAndValidateAddons(fields: readonly FieldDef<unknown>[], fieldRegistry: ReadonlyMap<string, FieldTypeDefinition>, kindRegistry: ReadonlyMap<string, AddonKindDefinition>, source: 'json' | 'inline'): {
6758
+ fields: FieldDef<unknown>[];
6759
+ warnings: AddonWarning[];
6760
+ };
6761
+
6762
+ /**
6763
+ * Options accepted by {@link sanitizeFormConfig}.
6764
+ */
6765
+ interface SanitizeFormConfigOptions {
6766
+ /**
6767
+ * Whether the config originated from JSON (server-driven) or inline
6768
+ * TypeScript code.
6769
+ *
6770
+ * - `'inline'` (default): keeps everything; assumes the config is
6771
+ * authored in code and code-only constructs (inline `action`
6772
+ * functions, `component` kind) are intentional. The default avoids
6773
+ * silently stripping inline behaviour during in-process authoring.
6774
+ * - `'json'`: drops code-only addon kinds (`component`, inline
6775
+ * `action: function`) with a warning, since they cannot survive
6776
+ * serialization. Use this when sanitising configs received from a
6777
+ * backend / form-builder UI.
6778
+ */
6779
+ source?: 'json' | 'inline';
6780
+ }
6781
+ /**
6782
+ * Result of a `sanitizeFormConfig` call.
6783
+ */
6784
+ interface SanitizedFormConfig {
6785
+ /** Sanitized config — invalid addons stripped from the field tree. */
6786
+ sanitized: FormConfig;
6787
+ /** Structured list of every addon that was dropped, and why. */
6788
+ warnings: readonly AddonWarning[];
6789
+ }
6790
+ /**
6791
+ * Walk a `FormConfig`, validate every field's addons against the active
6792
+ * `FIELD_REGISTRY` and `ADDON_KIND_REGISTRY`, and return a sanitized copy
6793
+ * plus a list of dropped-addon warnings.
6794
+ *
6795
+ * Distinct from the INTERNAL `validateFormConfig` in
6796
+ * `utils/config-validation/config-validator.ts` (which throws on
6797
+ * structural errors like duplicate keys / unknown field types). This
6798
+ * helper is the PUBLIC, lenient addon-shape pass — renamed to
6799
+ * `sanitizeFormConfig` to avoid the name collision.
6800
+ *
6801
+ * Lenient by design: invalid addons are dropped (with warnings) rather than
6802
+ * thrown. The form keeps rendering even when the backend ships an addon the
6803
+ * FE doesn't understand.
6804
+ *
6805
+ * `<df-dynamic-form>` already runs this internally at form init — calling
6806
+ * the public helper AND passing the original config to the form will
6807
+ * double-emit warnings. Use this entry point only when you need the
6808
+ * sanitized config or warning list outside the form (e.g., admin-UI
6809
+ * diagnostics, build-time lints).
6810
+ *
6811
+ * Must be called within an Angular injection context — typically inside
6812
+ * an admin-UI component or a `runInInjectionContext()` block on the server.
5607
6813
  *
5608
6814
  * @example
5609
6815
  * ```typescript
5610
- * const wrappers = createWrappers(
5611
- * {
5612
- * wrapperName: 'section',
5613
- * loadComponent: () => import('./section-wrapper'),
5614
- * props: wrapperProps<SectionWrapper>(),
5615
- * },
5616
- * );
5617
- *
5618
- * declare module '@ng-forge/dynamic-forms' {
5619
- * interface FieldRegistryWrappers extends InferWrapperRegistry<typeof wrappers> {}
6816
+ * @Component({ ... })
6817
+ * class AdminFormBuilder {
6818
+ * private readonly check = (cfg: FormConfig) => {
6819
+ * const { sanitized, warnings } = sanitizeFormConfig(cfg, { source: 'json' });
6820
+ * if (warnings.length > 0) {
6821
+ * this.diagnostics.set(warnings);
6822
+ * }
6823
+ * return sanitized;
6824
+ * };
5620
6825
  * }
5621
6826
  * ```
5622
6827
  */
5623
- declare function wrapperProps<T>(): T;
6828
+ declare function sanitizeFormConfig(config: FormConfig, options?: SanitizeFormConfigOptions): SanitizedFormConfig;
6829
+ /**
6830
+ * Default warning sink. Pass a `DynamicFormLogger` to route through the
6831
+ * form's configured logger (respects `NoopLogger` opt-out); omit it to
6832
+ * fall back to `console.warn` for build-time / admin-UI use cases that
6833
+ * run outside an Angular injection context.
6834
+ */
6835
+ declare function logAddonWarnings(warnings: readonly AddonWarning[], logger?: Logger): void;
6836
+ /**
6837
+ * DI-free variant of {@link sanitizeFormConfig} for tooling that lives
6838
+ * outside Angular's injector — build-time linters, MCP-side checks, CLI
6839
+ * scripts.
6840
+ *
6841
+ * Caller passes the registries explicitly; the result has the same shape
6842
+ * as the DI-bound version.
6843
+ */
6844
+ declare function sanitizeFormConfigPure(config: FormConfig, fieldRegistry: ReadonlyMap<string, FieldTypeDefinition>, kindRegistry: ReadonlyMap<string, AddonKindDefinition>, options?: SanitizeFormConfigOptions): SanitizedFormConfig;
5624
6845
 
5625
6846
  interface FieldWithValidation {
5626
6847
  readonly required?: boolean;
@@ -5706,7 +6927,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TN
5706
6927
  nullable?: TNullable;
5707
6928
  }
5708
6929
  declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
5709
- type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'valueType' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas' | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers';
6930
+ type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'valueType' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas' | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
5710
6931
  type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
5711
6932
 
5712
6933
  interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends FieldDef<TProps, TMeta>, FieldWithValidation {
@@ -5719,7 +6940,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends
5719
6940
  required?: boolean;
5720
6941
  }
5721
6942
  declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta>;
5722
- type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers';
6943
+ type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
5723
6944
  type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
5724
6945
 
5725
6946
  declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
@@ -6495,8 +7716,6 @@ declare function applyMetaToElement(element: Element, meta: FieldMeta | undefine
6495
7716
  * );
6496
7717
  * // user.value() keeps the old user data during loading transitions
6497
7718
  * ```
6498
- *
6499
- * @experimental Uses Angular's experimental resource composition APIs.
6500
7719
  */
6501
7720
  declare function withPreviousValue<T>(input: Resource<T>): Resource<T>;
6502
7721
 
@@ -6615,6 +7834,12 @@ interface ResolvedArrayItemField {
6615
7834
  inputs: Signal<Record<string, unknown>>;
6616
7835
  /** Whether required mapped inputs are available for safe component instantiation. */
6617
7836
  renderReady: Signal<boolean>;
7837
+ /**
7838
+ * Whether the array-item field is currently hidden — mirrors `ResolvedField.hidden`
7839
+ * so the same `@if (!field.hidden())` template gate works for top-level and
7840
+ * array-item fields.
7841
+ */
7842
+ hidden: Signal<boolean>;
6618
7843
  }
6619
7844
  /**
6620
7845
  * Resolved array item ready for declarative rendering with ngComponentOutlet.
@@ -6645,7 +7870,9 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
6645
7870
  protected readonly environmentInjector: EnvironmentInjector;
6646
7871
  private readonly eventBus;
6647
7872
  private readonly logger;
6648
- private readonly templateRegistry;
7873
+ private readonly arrayItemRegistry;
7874
+ private readonly slot;
7875
+ /** Bound id-generator reference threaded to `resolveArrayItem` so callsites stay terse. */
6649
7876
  private readonly generateItemId;
6650
7877
  field: _angular_core.InputSignal<ArrayField<readonly ArrayItemDefinition[]>>;
6651
7878
  key: _angular_core.InputSignal<string>;
@@ -6710,9 +7937,7 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
6710
7937
  private readonly itemTemplates;
6711
7938
  private readonly arrayFieldTrees;
6712
7939
  private readonly resolvedItemsSignal;
6713
- private readonly updateVersion;
6714
- private readonly pendingInitializationCycle;
6715
- private readonly settledInitializationCycle;
7940
+ private readonly fsm;
6716
7941
  /**
6717
7942
  * Map of item IDs to their current positions. O(1) lookup vs O(n) indexOf().
6718
7943
  * Used by child linkedSignals to reactively track their position in the array.
@@ -6832,71 +8057,6 @@ declare class ContainerFieldComponent {
6832
8057
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContainerFieldComponent, "div[container-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
6833
8058
  }
6834
8059
 
6835
- /**
6836
- * Type representing a FieldTree for an array.
6837
- * Extends FieldTree to add numeric indexing for accessing item FieldTrees.
6838
- */
6839
- type ArrayFieldTree<T> = FieldTree<T[]> & {
6840
- readonly [index: number]: FieldTree<T> | undefined;
6841
- };
6842
- /**
6843
- * Get the length of an array FieldTree.
6844
- */
6845
- declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
6846
- /**
6847
- * Read-only view of a single field's observable state.
6848
- *
6849
- * Whitelisted read signals copied from Angular Signal Forms' `FieldState` so wrappers
6850
- * can observe a field without being able to mutate it. New write-capable members
6851
- * added in future Angular versions are excluded by default (the `Pick` list stays
6852
- * the source of truth).
6853
- *
6854
- * `value` is narrowed from `WritableSignal<TValue>` to `Signal<TValue>` so consumers
6855
- * cannot write through it.
6856
- */
6857
- interface ReadonlyFieldTree<TValue = unknown> {
6858
- readonly value: Signal<TValue>;
6859
- readonly valid: Signal<boolean>;
6860
- readonly invalid: Signal<boolean>;
6861
- readonly touched: Signal<boolean>;
6862
- readonly dirty: Signal<boolean>;
6863
- readonly required: Signal<boolean>;
6864
- readonly disabled: Signal<boolean>;
6865
- readonly hidden: Signal<boolean>;
6866
- readonly errors: Signal<readonly unknown[]>;
6867
- }
6868
- /**
6869
- * Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
6870
- * Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
6871
- * so consumers only see the narrow surface and Angular's `WritableSignal` capability
6872
- * on `value` is hidden.
6873
- */
6874
- declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
6875
-
6876
- /**
6877
- * Input shape passed to a wrapper component via the `fieldInputs` input.
6878
- *
6879
- * Carries the wrapped field's mapper outputs plus an optional `field`
6880
- * read-only view. Wrappers reached through a container path (which has
6881
- * no FieldTree of its own) receive `fieldInputs === undefined`, so any
6882
- * wrapper that reads `fieldInputs.field` must guard for it.
6883
- *
6884
- * Mappers must emit new rawInputs objects per tick rather than mutating
6885
- * the previous one — this bag is a shallow spread, so mutations to shared
6886
- * nested values leak to downstream wrappers.
6887
- */
6888
- interface WrapperFieldInputs {
6889
- readonly key: string;
6890
- readonly label?: string;
6891
- readonly placeholder?: string;
6892
- readonly className?: string;
6893
- readonly props?: Record<string, unknown>;
6894
- readonly validationMessages?: Record<string, string>;
6895
- readonly defaultValidationMessages?: Record<string, string>;
6896
- readonly field?: ReadonlyFieldTree;
6897
- readonly [key: string]: unknown;
6898
- }
6899
-
6900
8060
  /**
6901
8061
  * Native JavaScript utility functions to replace lodash-es
6902
8062
  * These are simple implementations for common object manipulation patterns
@@ -6975,5 +8135,5 @@ declare class RootFormRegistryService {
6975
8135
  */
6976
8136
  declare const INITIALIZATION_TIMEOUT_MS: InjectionToken<number>;
6977
8137
 
6978
- export { ARRAY_CONTEXT, AppendArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, ContainerFieldComponent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, EventDispatcher, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GROUP_CONTEXT, GroupFieldComponent, INITIALIZATION_TIMEOUT_MS, InsertArrayItemEvent, MoveArrayItemEvent, NextPageEvent, NoopLogger, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, RootFormRegistryService, ShiftArrayItemEvent, SubmitEvent, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayEvent, arrayFieldMapper, baseFieldMapper, buildBaseInputs, containerFieldMapper, createField, createWrappers, dynamicTextToObservable, evaluateNonFieldDisabled, evaluateNonFieldHidden, field, formConfig, getArrayLength, groupFieldMapper, hasFormValue, interpolateParams, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isDisplayOnlyField, isEqual, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isValueField, isWrapperTypeDefinition, isWrappersBundle, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, toReadonlyFieldTree, withEventFormValue, withLoggerConfig, withPreviousValue, withValidationExecutionDefaults, withValueExclusionDefaults, wrapperProps };
6979
- export type { ArrayAllowedChildren, ArrayButtonConfig, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, ArrayItemDefinitionTemplate, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerAllowedChildren, ContainerField, ContainerFieldTypes, CssWrapper, CustomFnConfig, CustomFunction, CustomValidator, CustomValidatorConfig, DeclarativeHttpValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldScope, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTypeDefinition, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, InferFormValue$1 as InferFormValue, InferWrapperRegistry, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, PageAllowedChildren, PageField, Prettify, ReadonlyFieldTree, RegisteredFieldTypes, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationExecutionConfig, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals, WrapperConfig, WrapperFieldInputs, WrapperRegistration, WrapperTypeDefinition, WrappersBundle };
8138
+ export { ADDON_ACTION_HANDLERS, ADDON_ACTION_REGISTRY, ADDON_KIND_COMPONENT_CACHE, ADDON_KIND_DEFINITIONS, ADDON_KIND_REGISTRY, ARRAY_CONTEXT, AppendArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ComponentAddonComponent, ConsoleLogger, ContainerFieldComponent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DF_FIELD_TEMPLATES, DfAddonSlot, DfTemplate, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, EventDispatcher, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GROUP_CONTEXT, GroupFieldComponent, INITIALIZATION_TIMEOUT_MS, InsertArrayItemEvent, MoveArrayItemEvent, NextPageEvent, NoopLogger, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, RootFormRegistryService, ShiftArrayItemEvent, SubmitEvent, TemplateAddonComponent, TextAddonComponent, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayEvent, arrayFieldMapper, baseFieldMapper, buildBaseInputs, containerFieldMapper, createField, createWrappers, dynamicTextToObservable, evaluateNonFieldDisabled, evaluateNonFieldHidden, field, formConfig, formatAddonWarning, getArrayLength, groupFieldMapper, hasFormValue, injectAddonKindRegistry, injectFieldSignalContext, injectFieldsSupportingAddons, interpolateParams, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isValueField, isWrapperTypeDefinition, isWrappersBundle, logAddonWarnings, omit, pageFieldMapper, provideAddonActions, provideDynamicForm, resolveDynamicValue, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, runPresetAction, sanitizeFormConfig, sanitizeFormConfigPure, textFieldMapper, toReadonlyFieldTree, validateFieldAddons, walkAndValidateAddons, withCustomAddon, withEventFormValue, withLegacyStatusClasses, withLoggerConfig, withPreviousValue, withValidationExecutionDefaults, withValueExclusionDefaults, wrapperProps, writeToFieldValue };
8139
+ export type { AddonActionContext, AddonActionHandler, AddonActionPreset, AddonActionsFeature, AddonKindDefinition, AddonKindSchema, AddonShapeValidator, AddonSlot, AddonWarning, AnyAddon, ArrayAllowedChildren, ArrayButtonConfig, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, ArrayItemDefinitionTemplate, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseAddon, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, CommonAddonActionPreset, CommonAddonSlot, ComponentAddon, ConditionalExpression, ContainerAllowedChildren, ContainerField, ContainerFieldTypes, CssWrapper, CustomFnConfig, CustomFunction, CustomValidator, CustomValidatorConfig, DeclarativeHttpValidatorConfig, DynamicFormActionRegistry, DynamicFormAddonActionPresetRegistry, DynamicFormAddonRegistry, DynamicFormAddonSlotRegistry, DynamicFormFieldRegistry, DynamicText, DynamicValue, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldAddonSupport, FieldAddonSupportEntry, FieldBoundAddonActionContext, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldScope, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTypeDefinition, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, InferFormValue$1 as InferFormValue, InferWrapperRegistry, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, OrphanAddonActionContext, PageAllowedChildren, PageField, PresetCollaborators, Prettify, ReadonlyFieldTree, RegisteredActionRef, RegisteredFieldTypes, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SanitizeFormConfigOptions, SanitizedFormConfig, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TemplateAddon, TextAddon, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationExecutionConfig, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals, WrapperConfig, WrapperFieldInputs, WrapperRegistration, WrapperTypeDefinition, WrappersBundle };