@ng-forge/dynamic-forms 0.9.0-next.12 → 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 (31) hide show
  1. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-BgCrTME1.mjs → ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs} +9 -9
  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-VHaBk_1l.mjs → ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs} +6 -6
  4. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-VHaBk_1l.mjs.map → ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs.map} +1 -1
  5. package/fesm2022/{ng-forge-dynamic-forms-container-utils-CGJiiKBY.mjs → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs} +2 -2
  6. package/fesm2022/{ng-forge-dynamic-forms-container-utils-CGJiiKBY.mjs.map → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs.map} +1 -1
  7. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-BOdw40ZF.mjs → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs} +5 -5
  8. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-BOdw40ZF.mjs.map → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs.map} +1 -1
  9. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-D72mQxlX.mjs → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs} +2 -2
  10. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-D72mQxlX.mjs.map → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs.map} +1 -1
  11. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-BvTQwoLg.mjs → ng-forge-dynamic-forms-group-field.component-C9xUHrXp.mjs} +9 -9
  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-BAPGCjUs.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BbgkzKuI.mjs} +1572 -186
  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-DDzmhJNn.mjs.map → ng-forge-dynamic-forms-page-field.component-BwNIx3R1.mjs.map} +1 -1
  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-Bo4zNJXY.mjs → ng-forge-dynamic-forms-text-field.component-CpgtswdO.mjs} +5 -5
  22. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-Bo4zNJXY.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 +1671 -531
  27. package/fesm2022/ng-forge-dynamic-forms-array-field.component-BgCrTME1.mjs.map +0 -1
  28. package/fesm2022/ng-forge-dynamic-forms-group-field.component-BvTQwoLg.mjs.map +0 -1
  29. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BAPGCjUs.mjs.map +0 -1
  30. package/fesm2022/ng-forge-dynamic-forms-page-field.component-DDzmhJNn.mjs +0 -114
  31. 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
  };
@@ -2098,8 +2240,6 @@ interface SchemaDefinition {
2098
2240
  subSchemas?: SchemaApplicationConfig[];
2099
2241
  }
2100
2242
 
2101
- type DynamicText = string | Observable<string> | Signal<string>;
2102
-
2103
2243
  /**
2104
2244
  * Re-export Angular's ValidationError for consistency
2105
2245
  * This replaces our custom ValidationError interface
@@ -4195,6 +4335,24 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
4195
4335
  * and any field-level `wrappers`.
4196
4336
  */
4197
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>;
4198
4356
  }
4199
4357
  type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
4200
4358
  /**
@@ -4289,6 +4447,103 @@ interface GroupContext {
4289
4447
  */
4290
4448
  type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
4291
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
+
4292
4547
  /**
4293
4548
  * Defines how a field type handles form values and data collection.
4294
4549
  *
@@ -4382,6 +4637,23 @@ interface FieldTypeDefinition<T extends FieldDef<any> = any> {
4382
4637
  scope?: FieldScope | FieldScope[];
4383
4638
  /** Mapped component inputs that must exist before the renderer instantiates the component */
4384
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;
4385
4657
  }
4386
4658
  /**
4387
4659
  * Injection token for the global field type registry.
@@ -4626,12 +4898,22 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
4626
4898
  formOptions: _angular_core.InputSignal<FormOptions | undefined>;
4627
4899
  /** Form values for two-way data binding. */
4628
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">;
4629
4908
  private destroyRef;
4630
4909
  private injector;
4631
4910
  protected environmentInjector: EnvironmentInjector;
4632
4911
  private eventBus;
4633
4912
  private logger;
4634
4913
  private dispatcher;
4914
+ private templateRegistry;
4915
+ /** All `<ng-template dfTemplate="...">` instances projected into this form. */
4916
+ private readonly _projectedTemplates;
4635
4917
  /** State manager that owns all form state. Initialized via connectDeps() to guarantee
4636
4918
  * FORM_STATE_DEPS is populated before FormStateManager is injected. */
4637
4919
  private readonly stateManager;
@@ -4745,7 +5027,7 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
4745
5027
  private connectDeps;
4746
5028
  private setupEventHandlers;
4747
5029
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
4748
- 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>;
4749
5031
  }
4750
5032
 
4751
5033
  declare class EventBus {
@@ -4920,178 +5202,99 @@ interface DynamicFormFeature<TKind extends string = string> {
4920
5202
  ɵproviders: Provider[];
4921
5203
  }
4922
5204
 
4923
- interface FieldOption<T = unknown> {
4924
- label: DynamicText;
4925
- value: T;
4926
- disabled?: boolean;
4927
- [key: string]: unknown;
4928
- }
4929
-
4930
5205
  /**
4931
- * Injection token for form-level default wrappers.
4932
- *
4933
- * Provides a Signal of the `defaultWrappers` array from FormConfig. Consumed by
4934
- * `DfFieldOutlet` / `ContainerFieldComponent` to merge into each field's
4935
- * effective wrapper chain (lowest priority after auto-associations, higher
4936
- * than field-level `wrappers`).
5206
+ * User-facing wrapper registration shape used with `createWrappers(...)`.
4937
5207
  *
4938
- * Provided once at the DynamicForm level and inherited via Angular's
4939
- * hierarchical injector.
5208
+ * Extends `WrapperTypeDefinition` with an optional `props` field that
5209
+ * carries the wrapper's config type (via `wrapperProps`) for later
5210
+ * inference by `InferWrapperRegistry`.
4940
5211
  */
4941
- declare const DEFAULT_WRAPPERS: InjectionToken<Signal<readonly WrapperConfig[] | undefined>>;
5212
+ interface WrapperRegistration<TName extends string = string, TConfig = unknown> {
5213
+ /** Unique identifier for the wrapper type */
5214
+ readonly wrapperName: TName;
5215
+ /** Lazy-loader for the wrapper component */
5216
+ readonly loadComponent: LazyComponentLoader;
5217
+ /** Field types this wrapper auto-applies to (merged lowest-priority during wrapper resolution) */
5218
+ readonly types?: readonly string[];
5219
+ /**
5220
+ * Type carrier only — runtime value is always `undefined`.
5221
+ * Use `wrapperProps<YourConfig>()` to thread the config type.
5222
+ */
5223
+ readonly props?: TConfig;
5224
+ }
4942
5225
  /**
4943
- * Injection token for providing field signal context to mappers and components.
4944
- *
4945
- * The field signal context is the "nervous system" of the dynamic form library,
4946
- * providing access to:
4947
- * - The form instance and structure
4948
- * - Current form values (as signals)
4949
- * - Default values
4950
- * - Validation messages
4951
- * - The injector for creating components
5226
+ * Branded bundle returned by `createWrappers(...)`.
4952
5227
  *
4953
- * This token is provided by the DynamicForm component and can be scoped
4954
- * for nested forms (Groups, Arrays) via child injectors.
5228
+ * Carries:
5229
+ * - `ɵkind: 'wrappers'` — discriminant recognised by `provideDynamicForm(...)`
5230
+ * - `ɵregistrations` — the original registrations (type-level use for `InferWrapperRegistry`)
5231
+ * - `ɵdefinitions` — the `WrapperTypeDefinition[]` extracted for `WRAPPER_REGISTRY`
5232
+ */
5233
+ interface WrappersBundle<T extends readonly WrapperRegistration[] = readonly WrapperRegistration[]> {
5234
+ readonly ɵkind: 'wrappers';
5235
+ readonly ɵregistrations: T;
5236
+ readonly ɵdefinitions: readonly WrapperTypeDefinition[];
5237
+ }
5238
+ /**
5239
+ * Bundle wrapper registrations into a single object that can be passed to
5240
+ * `provideDynamicForm(...)`.
4955
5241
  *
4956
5242
  * @example
4957
5243
  * ```typescript
4958
- * // In a mapper function
4959
- * export function mapValueField(fieldDef: BaseValueField<any, any>): Binding[] {
4960
- * const context = inject(FIELD_SIGNAL_CONTEXT);
4961
- * const form = context.form();
4962
- * // ... use context
4963
- * }
5244
+ * const appWrappers = createWrappers(
5245
+ * {
5246
+ * wrapperName: 'section',
5247
+ * loadComponent: () => import('./section-wrapper'),
5248
+ * props: wrapperProps<SectionWrapper>(),
5249
+ * },
5250
+ * {
5251
+ * wrapperName: 'highlight',
5252
+ * loadComponent: () => import('./highlight-wrapper'),
5253
+ * types: ['input', 'select'],
5254
+ * props: wrapperProps<HighlightWrapper>(),
5255
+ * },
5256
+ * );
4964
5257
  *
4965
- * // In a component
4966
- * export class MyFieldComponent {
4967
- * private context = inject(FIELD_SIGNAL_CONTEXT);
5258
+ * declare module '@ng-forge/dynamic-forms' {
5259
+ * interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
4968
5260
  * }
5261
+ *
5262
+ * bootstrapApplication(AppComponent, {
5263
+ * providers: [provideDynamicForm(appWrappers)],
5264
+ * });
4969
5265
  * ```
4970
5266
  */
4971
- declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
5267
+ declare function createWrappers<const T extends readonly WrapperRegistration[]>(...registrations: T): WrappersBundle<T>;
5268
+ /** Type guard for a `WrappersBundle`. */
5269
+ declare function isWrappersBundle(value: unknown): value is WrappersBundle;
4972
5270
  /**
4973
- * Injection token for providing array context metadata to mappers and components.
4974
- *
4975
- * This token is optionally provided by ArrayFieldComponent when creating injectors
4976
- * for array items. It contains metadata about the array item's position and parent.
5271
+ * Derive the `FieldRegistryWrappers` augmentation shape from a wrapper bundle.
4977
5272
  *
4978
- * Mappers can inject this token with {optional: true} to access array context:
5273
+ * Maps each registration's `wrapperName` to its `props` type (the config carried
5274
+ * by `wrapperProps<T>()`). If a registration has no `props`, falls back to the
5275
+ * minimal discriminant shape `{ readonly type: wrapperName }`.
4979
5276
  *
4980
5277
  * @example
4981
5278
  * ```typescript
4982
- * // In a mapper function
4983
- * export function buttonMapper(fieldDef: FieldDef<any>): Binding[] {
4984
- * const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
4985
- * if (arrayContext) {
4986
- * // Use arrayContext.index, arrayContext.arrayKey, etc.
4987
- * }
5279
+ * declare module '@ng-forge/dynamic-forms' {
5280
+ * interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
4988
5281
  * }
4989
5282
  * ```
4990
5283
  */
4991
- declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
5284
+ type InferWrapperRegistry<T> = T extends WrappersBundle<infer R> ? {
5285
+ [Reg in R[number] as Reg['wrapperName']]: Reg extends {
5286
+ props: infer P;
5287
+ } ? Exclude<P, undefined> : {
5288
+ readonly type: Reg['wrapperName'];
5289
+ };
5290
+ } : never;
5291
+
4992
5292
  /**
4993
- * Injection token for providing group ancestry to mappers and components.
4994
- *
4995
- * Re-provided in two places:
4996
- * 1. `GroupFieldComponent` provides a context whose `groupPath()` is the
4997
- * parent path extended by its own key (e.g. `'address'` or `'user.address'`).
4998
- * 2. `createArrayItemInjector` provides a sentinel with an empty `groupPath`,
4999
- * resetting the chain at array boundaries — descendants of an array item
5000
- * that re-enter a group compose paths INSIDE the array item, matching the
5001
- * property-derivation collector's keying.
5002
- *
5003
- * `mapFieldToInputs` reads this to scope DOM IDs and override-store keys so
5004
- * the same leaf key can appear in different groups without colliding (#401).
5005
- *
5006
- * Inject with `{ optional: true }` because top-level fields with no group
5007
- * ancestor (and no enclosing array) won't have the token provided. The token
5008
- * IS present for fields inside an array even when no inner group exists — the
5009
- * array-boundary sentinel makes injection succeed with an empty `groupPath`.
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.
5010
5296
  */
5011
- declare const GROUP_CONTEXT: InjectionToken<GroupContext>;
5012
- /**
5013
- * Injection token for form-level default props.
5014
- *
5015
- * Default props are form-wide property defaults that are merged with field-level props.
5016
- * Field props take precedence over default props.
5017
- *
5018
- * Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
5019
- * DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
5020
- * via Angular's hierarchical injector.
5021
- *
5022
- * The token provides a Signal to enable reactivity - when config changes, mappers
5023
- * that read the signal inside their computed() will automatically update.
5024
- *
5025
- * @example
5026
- * ```typescript
5027
- * // In a mapper function
5028
- * const defaultPropsSignal = inject(DEFAULT_PROPS);
5029
- *
5030
- * return computed(() => {
5031
- * const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
5032
- * const baseInputs = buildBaseInputs(fieldDef, defaultProps);
5033
- * // ...
5034
- * });
5035
- * ```
5036
- */
5037
- declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
5038
- /**
5039
- * Injection token for form-level default validation messages.
5040
- *
5041
- * Default validation messages act as fallbacks when fields have validation
5042
- * errors but no field-level `validationMessages` defined.
5043
- *
5044
- * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5045
- * and inherited by all children via Angular's hierarchical injector.
5046
- *
5047
- * The token provides a Signal to enable reactivity - when config changes, mappers
5048
- * that read the signal inside their computed() will automatically update.
5049
- *
5050
- * @example
5051
- * ```typescript
5052
- * // In a mapper or component
5053
- * const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
5054
- *
5055
- * return computed(() => {
5056
- * const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
5057
- * const message = defaultMessages?.required ?? 'Field is required';
5058
- * // ...
5059
- * });
5060
- * ```
5061
- */
5062
- declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
5063
- /**
5064
- * Injection token for form-level options.
5065
- *
5066
- * Form options control form-wide behavior including button disabled states.
5067
- * Used by button mappers to determine default disabled behavior.
5068
- *
5069
- * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5070
- * and inherited by all children via Angular's hierarchical injector.
5071
- *
5072
- * The token provides a Signal to enable reactivity - when config changes, mappers
5073
- * that read the signal inside their computed() will automatically update.
5074
- *
5075
- * @example
5076
- * ```typescript
5077
- * // In a button mapper
5078
- * const formOptionsSignal = inject(FORM_OPTIONS);
5079
- *
5080
- * return computed(() => {
5081
- * const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
5082
- * const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
5083
- * // ...
5084
- * });
5085
- * ```
5086
- */
5087
- declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
5088
-
5089
- /**
5090
- * Type guard to check if a field is a container field (page, row, group, or array)
5091
- * Container fields have a 'fields' property and don't contribute values directly.
5092
- * This overload works with RegisteredFieldTypes for full type narrowing.
5093
- */
5094
- declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
5297
+ declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
5095
5298
  /**
5096
5299
  * Type guard to check if a field is a container field (page, row, group, or array).
5097
5300
  * This overload works with any FieldDef for looser type checking.
@@ -5128,93 +5331,6 @@ type FieldPathAccess<TValue> = {
5128
5331
  [K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
5129
5332
  };
5130
5333
 
5131
- /**
5132
- * User-facing wrapper registration shape used with `createWrappers(...)`.
5133
- *
5134
- * Extends `WrapperTypeDefinition` with an optional `props` field that
5135
- * carries the wrapper's config type (via `wrapperProps`) for later
5136
- * inference by `InferWrapperRegistry`.
5137
- */
5138
- interface WrapperRegistration<TName extends string = string, TConfig = unknown> {
5139
- /** Unique identifier for the wrapper type */
5140
- readonly wrapperName: TName;
5141
- /** Lazy-loader for the wrapper component */
5142
- readonly loadComponent: LazyComponentLoader;
5143
- /** Field types this wrapper auto-applies to (merged lowest-priority during wrapper resolution) */
5144
- readonly types?: readonly string[];
5145
- /**
5146
- * Type carrier only — runtime value is always `undefined`.
5147
- * Use `wrapperProps<YourConfig>()` to thread the config type.
5148
- */
5149
- readonly props?: TConfig;
5150
- }
5151
- /**
5152
- * Branded bundle returned by `createWrappers(...)`.
5153
- *
5154
- * Carries:
5155
- * - `ɵkind: 'wrappers'` — discriminant recognised by `provideDynamicForm(...)`
5156
- * - `ɵregistrations` — the original registrations (type-level use for `InferWrapperRegistry`)
5157
- * - `ɵdefinitions` — the `WrapperTypeDefinition[]` extracted for `WRAPPER_REGISTRY`
5158
- */
5159
- interface WrappersBundle<T extends readonly WrapperRegistration[] = readonly WrapperRegistration[]> {
5160
- readonly ɵkind: 'wrappers';
5161
- readonly ɵregistrations: T;
5162
- readonly ɵdefinitions: readonly WrapperTypeDefinition[];
5163
- }
5164
- /**
5165
- * Bundle wrapper registrations into a single object that can be passed to
5166
- * `provideDynamicForm(...)`.
5167
- *
5168
- * @example
5169
- * ```typescript
5170
- * const appWrappers = createWrappers(
5171
- * {
5172
- * wrapperName: 'section',
5173
- * loadComponent: () => import('./section-wrapper'),
5174
- * props: wrapperProps<SectionWrapper>(),
5175
- * },
5176
- * {
5177
- * wrapperName: 'highlight',
5178
- * loadComponent: () => import('./highlight-wrapper'),
5179
- * types: ['input', 'select'],
5180
- * props: wrapperProps<HighlightWrapper>(),
5181
- * },
5182
- * );
5183
- *
5184
- * declare module '@ng-forge/dynamic-forms' {
5185
- * interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
5186
- * }
5187
- *
5188
- * bootstrapApplication(AppComponent, {
5189
- * providers: [provideDynamicForm(appWrappers)],
5190
- * });
5191
- * ```
5192
- */
5193
- declare function createWrappers<const T extends readonly WrapperRegistration[]>(...registrations: T): WrappersBundle<T>;
5194
- /** Type guard for a `WrappersBundle`. */
5195
- declare function isWrappersBundle(value: unknown): value is WrappersBundle;
5196
- /**
5197
- * Derive the `FieldRegistryWrappers` augmentation shape from a wrapper bundle.
5198
- *
5199
- * Maps each registration's `wrapperName` to its `props` type (the config carried
5200
- * by `wrapperProps<T>()`). If a registration has no `props`, falls back to the
5201
- * minimal discriminant shape `{ readonly type: wrapperName }`.
5202
- *
5203
- * @example
5204
- * ```typescript
5205
- * declare module '@ng-forge/dynamic-forms' {
5206
- * interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
5207
- * }
5208
- * ```
5209
- */
5210
- type InferWrapperRegistry<T> = T extends WrappersBundle<infer R> ? {
5211
- [Reg in R[number] as Reg['wrapperName']]: Reg extends {
5212
- props: infer P;
5213
- } ? Exclude<P, undefined> : {
5214
- readonly type: Reg['wrapperName'];
5215
- };
5216
- } : never;
5217
-
5218
5334
  /**
5219
5335
  * Extract FieldDef type from FieldTypeDefinition
5220
5336
  */
@@ -5307,336 +5423,1425 @@ type ExtractFormValue<T> = T extends {
5307
5423
  } ? V : never;
5308
5424
 
5309
5425
  /**
5310
- * Built-in field types provided by the dynamic form library.
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.
5311
5438
  *
5312
- * Each field type is validated at compile time using satisfies, ensuring
5313
- * type safety of the mapper function while allowing the array to be typed
5314
- * as FieldTypeDefinition[] for consumer flexibility.
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.
5315
5446
  */
5316
- declare const BUILT_IN_FIELDS: FieldTypeDefinition[];
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;
5317
5473
 
5318
5474
  /**
5319
- * Configuration options for the logger feature.
5320
- *
5321
- * @public
5475
+ * Fields every addon-action context carries, regardless of whether the
5476
+ * addon is bound to a host field or rendered orphaned.
5322
5477
  */
5323
- interface LoggerConfigOptions {
5478
+ interface AddonActionContextBase<TValue> {
5324
5479
  /**
5325
- * Whether general logging is enabled.
5326
- *
5327
- * @default true
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.
5328
5484
  */
5329
- enabled?: boolean;
5485
+ readonly field: {
5486
+ readonly key: string;
5487
+ readonly type: string;
5488
+ };
5330
5489
  /**
5331
- * Derivation logging level.
5332
- *
5333
- * - `'none'`: No derivation debug logging (default)
5334
- * - `'summary'`: Log cycle completion with counts
5335
- * - `'verbose'`: Log individual derivation evaluations with details
5336
- *
5337
- * @default 'none'
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.
5338
5493
  */
5339
- derivations?: DerivationLogLevel;
5494
+ readonly value: TValue | undefined;
5340
5495
  }
5341
5496
  /**
5342
- * Configure logging for dynamic forms.
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.
5343
5500
  *
5344
- * By default, general logging is enabled (ConsoleLogger) and derivation
5345
- * logging is disabled ('none'). Use this feature to enable derivation debugging.
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.
5346
5526
  *
5347
5527
  * @example
5348
5528
  * ```typescript
5349
- * // Disable all logging
5350
- * provideDynamicForm(
5351
- * ...withMaterialFields(),
5352
- * withLoggerConfig(false)
5353
- * )
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.
5354
5543
  *
5355
- * // Enable verbose derivation logging
5356
- * provideDynamicForm(
5357
- * ...withMaterialFields(),
5358
- * withLoggerConfig({ derivations: 'verbose' })
5359
- * )
5544
+ * Adapter kinds that accept a `preset` field render the corresponding
5545
+ * behaviour:
5360
5546
  *
5361
- * // Disable derivation logging but keep general logging
5362
- * provideDynamicForm(
5363
- * ...withMaterialFields(),
5364
- * withLoggerConfig({ derivations: 'none' })
5365
- * )
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`).
5366
5556
  *
5367
- * // Conditional logging (e.g., only in dev mode)
5368
- * provideDynamicForm(
5369
- * ...withMaterialFields(),
5370
- * withLoggerConfig(() => isDevMode())
5371
- * )
5372
- * ```
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.
5373
5563
  *
5374
- * @param config - Boolean to enable/disable logging, a function returning boolean, or a config object
5375
- * @returns A DynamicFormFeature that configures logging
5564
+ * Adapters add their canonical actions (e.g., Material's
5565
+ * `'mat-datepicker-open'`) without forking the `AddonActionPreset` union.
5376
5566
  *
5377
- * @public
5378
- */
5379
- declare function withLoggerConfig(config?: boolean | (() => boolean) | LoggerConfigOptions): DynamicFormFeature<'logger'>;
5380
-
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
+ }
5381
5578
  /**
5382
- * Injection token for the dynamic forms logger.
5579
+ * Union of every preset name an addon may reference — universal presets
5580
+ * plus any contributed via {@link DynamicFormAddonActionPresetRegistry}.
5383
5581
  *
5384
- * Provided by provideDynamicForm() with ConsoleLogger as default.
5385
- * Override with withLoggerConfig(false) to disable logging.
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.
5386
5588
  */
5387
- declare const DynamicFormLogger: InjectionToken<Logger>;
5388
-
5589
+ type AddonActionPreset = CommonAddonActionPreset | keyof DynamicFormAddonActionPresetRegistry | (string & {});
5389
5590
  /**
5390
- * Console-based logger implementation.
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
+ * ```
5391
5605
  */
5392
- declare class ConsoleLogger implements Logger {
5393
- private readonly prefix;
5394
- debug(message: string, ...args: unknown[]): void;
5395
- info(message: string, ...args: unknown[]): void;
5396
- warn(message: string, ...args: unknown[]): void;
5397
- error(message: string, ...args: unknown[]): void;
5606
+ interface DynamicFormActionRegistry {
5398
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;
5399
5614
 
5400
5615
  /**
5401
- * No-operation logger implementation.
5402
- * All methods are no-ops - used in production by default.
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.
5403
5628
  */
5404
- declare class NoopLogger implements Logger {
5405
- debug(): void;
5406
- info(): void;
5407
- warn(): void;
5408
- error(): void;
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;
5409
5636
  }
5410
5637
 
5411
5638
  /**
5412
- * Enables automatic form value attachment to all events dispatched through the EventBus.
5639
+ * Injection token for form-level default wrappers.
5413
5640
  *
5414
- * When this feature is enabled, events dispatched via `eventBus.dispatch()` will include
5415
- * the current form value in the `formValue` property. This is useful for event handlers
5416
- * that need access to the complete form state at the time of the event.
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`).
5417
5645
  *
5418
- * @example Global opt-in
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
5419
5665
  * ```typescript
5420
- * provideDynamicForm(
5421
- * ...withMaterialFields(),
5422
- * withEventFormValue()
5423
- * )
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
+ * }
5424
5678
  * ```
5679
+ */
5680
+ declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
5681
+ /**
5682
+ * Throwing accessor for {@link FIELD_SIGNAL_CONTEXT}.
5425
5683
  *
5426
- * @example Consumer usage
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
5427
5702
  * ```typescript
5428
- * eventBus.on<SubmitEvent>('submit').subscribe(event => {
5429
- * if (hasFormValue(event)) {
5430
- * console.log('Form value:', event.formValue);
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.
5431
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
+ * // ...
5432
5755
  * });
5433
5756
  * ```
5757
+ */
5758
+ declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
5759
+ /**
5760
+ * Injection token for form-level default validation messages.
5434
5761
  *
5435
- * @example Per-form disable (when globally enabled)
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
5436
5772
  * ```typescript
5437
- * const config: FormConfig = {
5438
- * fields: [...],
5439
- * options: { emitFormValueOnEvents: false }
5440
- * };
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
+ * });
5441
5781
  * ```
5782
+ */
5783
+ declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
5784
+ /**
5785
+ * Injection token for form-level options.
5442
5786
  *
5443
- * @remarks
5444
- * **Precedence rules:**
5445
- * 1. Per-form `false` - Disables for this form, regardless of global setting
5446
- * 2. Per-form `true` - Enables for this form, regardless of global setting
5447
- * 3. Per-form `undefined` - Uses global setting
5448
- * 4. Global `withEventFormValue()` - Enables globally
5449
- * 5. No global feature - Disabled globally (default)
5787
+ * Form options control form-wide behavior including button disabled states.
5788
+ * Used by button mappers to determine default disabled behavior.
5450
5789
  *
5451
- * @returns A DynamicFormFeature that enables form value attachment to events
5790
+ * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
5791
+ * and inherited by all children via Angular's hierarchical injector.
5452
5792
  *
5453
- * @public
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
+ * ```
5454
5807
  */
5455
- declare function withEventFormValue(): DynamicFormFeature<'event-form-value'>;
5808
+ declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
5456
5809
 
5457
5810
  /**
5458
- * Configuration for excluding field values from form submission output
5459
- * based on their reactive state (hidden, disabled, readonly).
5811
+ * Built-in field types provided by the dynamic form library.
5460
5812
  *
5461
- * Supports a 3-tier configuration hierarchy: Field > Form > Global.
5462
- * The most specific level wins for each property.
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.
5463
5821
  *
5464
5822
  * @public
5465
5823
  */
5466
- interface ValueExclusionConfig {
5824
+ interface LoggerConfigOptions {
5467
5825
  /**
5468
- * Whether to exclude the value of hidden fields from submission output.
5469
- *
5470
- * When `true`, fields whose `hidden()` state is `true` will have their
5471
- * values omitted from the submitted form value.
5826
+ * Whether general logging is enabled.
5472
5827
  *
5473
- * @remarks
5474
- * This does NOT affect HiddenField (`type: 'hidden'`) — those fields
5475
- * store values without UI and their `hidden()` state is always `false`.
5828
+ * @default true
5476
5829
  */
5477
- readonly excludeValueIfHidden?: boolean;
5830
+ enabled?: boolean;
5478
5831
  /**
5479
- * Whether to exclude the value of disabled fields from submission output.
5832
+ * Derivation logging level.
5480
5833
  *
5481
- * When `true`, fields whose `disabled()` state is `true` will have their
5482
- * values omitted from the submitted form value.
5483
- */
5484
- readonly excludeValueIfDisabled?: boolean;
5485
- /**
5486
- * Whether to exclude the value of readonly fields from submission output.
5834
+ * - `'none'`: No derivation debug logging (default)
5835
+ * - `'summary'`: Log cycle completion with counts
5836
+ * - `'verbose'`: Log individual derivation evaluations with details
5487
5837
  *
5488
- * When `true`, fields whose `readonly()` state is `true` will have their
5489
- * values omitted from the submitted form value.
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.
5490
5991
  */
5491
5992
  readonly excludeValueIfReadonly?: boolean;
5492
5993
  }
5493
5994
  /**
5494
- * Fully resolved exclusion config with all properties required.
5495
- * Produced by the 3-tier resolution: `field ?? form ?? global`.
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.
6125
+ *
6126
+ * @example Opt in once at the application root
6127
+ * ```typescript
6128
+ * bootstrapApplication(AppComponent, {
6129
+ * providers: [
6130
+ * provideDynamicForm(
6131
+ * ...withMaterialFields(),
6132
+ * withLegacyStatusClasses()
6133
+ * )
6134
+ * ]
6135
+ * });
6136
+ * ```
6137
+ *
6138
+ * @returns A DynamicFormFeature that enables the legacy `ng-*` class strategy
6139
+ *
6140
+ * @public
6141
+ */
6142
+ declare function withLegacyStatusClasses(): DynamicFormFeature<'legacy-status-classes'>;
6143
+
6144
+ /**
6145
+ * Zero-cost type carrier used in wrapper registrations.
6146
+ *
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
+ * ```
6166
+ */
6167
+ declare function wrapperProps<T>(): T;
6168
+
6169
+ /**
6170
+ * Input shape passed to a wrapper component via the `fieldInputs` input.
6171
+ *
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.
6180
+ */
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;
6192
+ /**
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.
6196
+ */
6197
+ readonly setValue?: (next: unknown) => void;
6198
+ readonly [key: string]: unknown;
6199
+ }
6200
+
6201
+ /**
6202
+ * Dispatcher component that renders an addon by looking up its `kind` in
6203
+ * `ADDON_KIND_REGISTRY` and instantiating the registered component.
6204
+ *
6205
+ * Used by every adapter's field component to render slot content:
6206
+ *
6207
+ * ```html
6208
+ * \@for (a of addonsAt('prefix'); track $index) {
6209
+ * <df-addon-slot [addon]="a" />
6210
+ * }
6211
+ * ```
6212
+ *
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.
6308
+ *
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.
6312
+ *
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>
6320
+ * ```
6321
+ *
6322
+ * In the field config:
6323
+ * ```ts
6324
+ * { slot: 'prefix', kind: 'template', templateKey: 'searchIcon' }
6325
+ * ```
6326
+ */
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
+ }
6335
+
6336
+ /**
6337
+ * Renderer for the universal `text` addon kind.
6338
+ *
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.
6342
+ */
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>;
6349
+ }
6350
+
6351
+ /**
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.
6362
+ */
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>;
6377
+ }
6378
+
6379
+ /**
6380
+ * Renderer for the universal `component` addon kind.
6381
+ *
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.
6387
+ *
6388
+ * @codeOnly The `component` loader is a function; this kind is dropped in
6389
+ * lenient validation when the config originated from JSON.
6390
+ */
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
+ }
6411
+
6412
+ /**
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.
6415
+ *
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.
6423
+ */
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;
6429
+ /**
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.
6434
+ */
6435
+ readonly fieldDefaultValueGetter?: () => unknown;
6436
+ /**
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.
6441
+ */
6442
+ readonly baselineType?: () => string | undefined;
6443
+ /**
6444
+ * Logger for warnings about presets that can't be fulfilled in this context.
6445
+ *
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)`.
6452
+ */
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>;
6498
+ }
6499
+ /**
6500
+ * Injection function for accessing the addon kind registry.
5496
6501
  *
5497
- * @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
+ * ```
5498
6510
  */
5499
- type ResolvedValueExclusionConfig = Required<ValueExclusionConfig>;
6511
+ declare function injectAddonKindRegistry(): AddonKindRegistryRef;
5500
6512
 
5501
6513
  /**
5502
- * Configures global value exclusion defaults for form submission output.
6514
+ * One entry per field type that declares addon support.
5503
6515
  *
5504
- * Value exclusion is **enabled by default** — field values are excluded from the
5505
- * `(submitted)` output based on their reactive state. Use this feature to
5506
- * override those defaults. This does NOT affect two-way binding (`value` model /
5507
- * `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`).
5508
6527
  *
5509
- * @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
5510
6535
  * ```typescript
5511
- * provideDynamicForm(
5512
- * ...withMaterialFields(),
5513
- * withValueExclusionDefaults()
5514
- * )
6536
+ * @Component({ ... })
6537
+ * class FormBuilder {
6538
+ * protected readonly addonable = injectFieldsSupportingAddons();
6539
+ *
6540
+ * constructor() {
6541
+ * // -> [{ name: 'prime-input', slots: ['prefix', 'suffix'], allowedKinds: [...] }, ...]
6542
+ * }
6543
+ * }
5515
6544
  * ```
6545
+ */
6546
+ declare function injectFieldsSupportingAddons(): FieldAddonSupportEntry[];
6547
+
6548
+ /**
6549
+ * Normalises a {@link DynamicValue} into a `Signal<T>`.
5516
6550
  *
5517
- * @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
5518
6565
  * ```typescript
5519
- * provideDynamicForm(
5520
- * ...withMaterialFields(),
5521
- * withValueExclusionDefaults({ excludeValueIfReadonly: false })
5522
- * )
6566
+ * const isHidden = resolveDynamicValue(addon().hidden, false);
6567
+ * // template: \@if (isHidden()) { ... }
5523
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.
5524
6574
  *
5525
- * @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
5526
6581
  * ```typescript
5527
6582
  * provideDynamicForm(
5528
6583
  * ...withMaterialFields(),
5529
- * withValueExclusionDefaults({
5530
- * excludeValueIfHidden: false,
5531
- * excludeValueIfDisabled: false,
5532
- * excludeValueIfReadonly: false,
5533
- * })
5534
- * )
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
+ * );
5535
6596
  * ```
5536
6597
  *
5537
- * @remarks
5538
- * **Precedence rules:**
5539
- * 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
5540
- * 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
5541
- * 3. Global `withValueExclusionDefaults()` — baseline default
5542
- * 4. No global feature — uses token default (all enabled)
5543
- *
5544
- * @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
5545
- * @returns A DynamicFormFeature that configures value exclusion defaults
5546
- *
5547
- * @public
6598
+ * Multiple `withCustomAddon(...)` calls are supported; later registrations
6599
+ * for the same kind override earlier ones (with a warning logged).
5548
6600
  */
5549
- declare function withValueExclusionDefaults(config?: Partial<ValueExclusionConfig>): DynamicFormFeature<'value-exclusion'>;
6601
+ declare function withCustomAddon<T extends BaseAddon>(definition: AddonKindDefinition<T>): DynamicFormFeature<'addons'>;
5550
6602
 
5551
6603
  /**
5552
- * Configuration for when validation runs based on a field's reactive state.
5553
- *
5554
- * Currently controls whether hidden fields are validated. Resolved through a
5555
- * tree-aware hierarchy: per-field overrides inherit down to descendants, with
5556
- * per-form `FormOptions` and global `withValidationExecutionDefaults()` providing
5557
- * the root inherited value.
6604
+ * Handler signature for user-registered addon actions.
5558
6605
  *
5559
- * @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.
5560
6609
  */
5561
- interface ValidationExecutionConfig {
5562
- /**
5563
- * Whether validators run while a field is hidden (statically, via `hidden`
5564
- * logic, or because any ancestor is hidden).
5565
- *
5566
- * When `false`, all validators on a hidden field are skipped — the field is
5567
- * effectively inert until shown. State logic (hidden/disabled/readonly) and
5568
- * value derivations continue to apply.
5569
- */
5570
- readonly validateWhenHidden?: boolean;
5571
- }
6610
+ type AddonActionHandler<TValue = unknown> = (ctx: AddonActionContext<TValue>) => void;
5572
6611
  /**
5573
- * 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.
5574
6614
  *
5575
- * @public
6615
+ * @internal
5576
6616
  */
5577
- 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>>>;
5578
6624
 
5579
6625
  /**
5580
- * Configures global validation execution defaults — when validators should run
5581
- * 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.
5582
6630
  *
5583
- * By default, validators are **skipped** for hidden fields (`validateWhenHidden: false`),
5584
- * matching what most users expect: a hidden field is inert. Use this feature to
5585
- * 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.
5586
6640
  *
5587
- * @example Default behavior — no feature needed
5588
- * ```typescript
5589
- * provideDynamicForm(...withMaterialFields())
5590
- * // hidden fields skip validation
5591
- * ```
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:
5592
6646
  *
5593
- * @example Restore legacy validation-on-hidden behavior
5594
6647
  * ```typescript
5595
- * provideDynamicForm(
5596
- * ...withMaterialFields(),
5597
- * withValidationExecutionDefaults({ validateWhenHidden: true })
5598
- * )
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);
5599
6660
  * ```
5600
6661
  *
5601
- * @remarks
5602
- * **Precedence rules:**
5603
- * 1. Per-field `FieldDef.validateWhenHidden` — wins for that field; inherits down to descendants
5604
- * that don't override.
5605
- * 2. Per-form `FormOptions.validateWhenHidden` — root inherited value for the form.
5606
- * 3. Global `withValidationExecutionDefaults()` — fallback when no form/field setting.
5607
- * 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.
5608
6671
  *
5609
- * @param config - Partial override. Unspecified properties keep their defaults.
5610
- * @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.
5611
6678
  *
5612
- * @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).
5613
6682
  */
5614
- 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;
5615
6735
 
5616
6736
  /**
5617
- * 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.
5618
6739
  *
5619
- * Returns `undefined` at runtime but is typed as `T`, so TypeScript can
5620
- * thread a wrapper's config type through `createWrappers(...)` into the
5621
- * `InferWrapperRegistry<typeof ...>` utility without requiring users to
5622
- * 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.
5623
6813
  *
5624
6814
  * @example
5625
6815
  * ```typescript
5626
- * const wrappers = createWrappers(
5627
- * {
5628
- * wrapperName: 'section',
5629
- * loadComponent: () => import('./section-wrapper'),
5630
- * props: wrapperProps<SectionWrapper>(),
5631
- * },
5632
- * );
5633
- *
5634
- * declare module '@ng-forge/dynamic-forms' {
5635
- * 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
+ * };
5636
6825
  * }
5637
6826
  * ```
5638
6827
  */
5639
- 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;
5640
6845
 
5641
6846
  interface FieldWithValidation {
5642
6847
  readonly required?: boolean;
@@ -5722,7 +6927,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TN
5722
6927
  nullable?: TNullable;
5723
6928
  }
5724
6929
  declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
5725
- 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';
5726
6931
  type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
5727
6932
 
5728
6933
  interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends FieldDef<TProps, TMeta>, FieldWithValidation {
@@ -5735,7 +6940,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends
5735
6940
  required?: boolean;
5736
6941
  }
5737
6942
  declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta>;
5738
- 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';
5739
6944
  type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
5740
6945
 
5741
6946
  declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
@@ -6852,71 +8057,6 @@ declare class ContainerFieldComponent {
6852
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>;
6853
8058
  }
6854
8059
 
6855
- /**
6856
- * Type representing a FieldTree for an array.
6857
- * Extends FieldTree to add numeric indexing for accessing item FieldTrees.
6858
- */
6859
- type ArrayFieldTree<T> = FieldTree<T[]> & {
6860
- readonly [index: number]: FieldTree<T> | undefined;
6861
- };
6862
- /**
6863
- * Get the length of an array FieldTree.
6864
- */
6865
- declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
6866
- /**
6867
- * Read-only view of a single field's observable state.
6868
- *
6869
- * Whitelisted read signals copied from Angular Signal Forms' `FieldState` so wrappers
6870
- * can observe a field without being able to mutate it. New write-capable members
6871
- * added in future Angular versions are excluded by default (the `Pick` list stays
6872
- * the source of truth).
6873
- *
6874
- * `value` is narrowed from `WritableSignal<TValue>` to `Signal<TValue>` so consumers
6875
- * cannot write through it.
6876
- */
6877
- interface ReadonlyFieldTree<TValue = unknown> {
6878
- readonly value: Signal<TValue>;
6879
- readonly valid: Signal<boolean>;
6880
- readonly invalid: Signal<boolean>;
6881
- readonly touched: Signal<boolean>;
6882
- readonly dirty: Signal<boolean>;
6883
- readonly required: Signal<boolean>;
6884
- readonly disabled: Signal<boolean>;
6885
- readonly hidden: Signal<boolean>;
6886
- readonly errors: Signal<readonly unknown[]>;
6887
- }
6888
- /**
6889
- * Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
6890
- * Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
6891
- * so consumers only see the narrow surface and Angular's `WritableSignal` capability
6892
- * on `value` is hidden.
6893
- */
6894
- declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
6895
-
6896
- /**
6897
- * Input shape passed to a wrapper component via the `fieldInputs` input.
6898
- *
6899
- * Carries the wrapped field's mapper outputs plus an optional `field`
6900
- * read-only view. Wrappers reached through a container path (which has
6901
- * no FieldTree of its own) receive `fieldInputs === undefined`, so any
6902
- * wrapper that reads `fieldInputs.field` must guard for it.
6903
- *
6904
- * Mappers must emit new rawInputs objects per tick rather than mutating
6905
- * the previous one — this bag is a shallow spread, so mutations to shared
6906
- * nested values leak to downstream wrappers.
6907
- */
6908
- interface WrapperFieldInputs {
6909
- readonly key: string;
6910
- readonly label?: string;
6911
- readonly placeholder?: string;
6912
- readonly className?: string;
6913
- readonly props?: Record<string, unknown>;
6914
- readonly validationMessages?: Record<string, string>;
6915
- readonly defaultValidationMessages?: Record<string, string>;
6916
- readonly field?: ReadonlyFieldTree;
6917
- readonly [key: string]: unknown;
6918
- }
6919
-
6920
8060
  /**
6921
8061
  * Native JavaScript utility functions to replace lodash-es
6922
8062
  * These are simple implementations for common object manipulation patterns
@@ -6995,5 +8135,5 @@ declare class RootFormRegistryService {
6995
8135
  */
6996
8136
  declare const INITIALIZATION_TIMEOUT_MS: InjectionToken<number>;
6997
8137
 
6998
- 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 };
6999
- 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 };