@ng-forge/dynamic-forms 0.10.0-next.4 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +16 -0
  2. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs → ng-forge-dynamic-forms-array-field.component-8rodySeE.mjs} +47 -26
  3. package/fesm2022/ng-forge-dynamic-forms-array-field.component-8rodySeE.mjs.map +1 -0
  4. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-C8kpF0tw.mjs +2347 -0
  5. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-C8kpF0tw.mjs.map +1 -0
  6. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs → ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs} +22 -14
  7. package/fesm2022/ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs.map +1 -0
  8. package/fesm2022/{ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs → ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs} +3 -2
  9. package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs.map +1 -0
  10. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs → ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs} +8 -6
  11. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs.map +1 -0
  12. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs → ng-forge-dynamic-forms-group-field.component-Ca4AqdZu.mjs} +42 -24
  13. package/fesm2022/ng-forge-dynamic-forms-group-field.component-Ca4AqdZu.mjs.map +1 -0
  14. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +122 -81
  15. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  16. package/fesm2022/ng-forge-dynamic-forms-internal.mjs +297 -56
  17. package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
  18. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BJi_rtey.mjs} +535 -3055
  19. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BJi_rtey.mjs.map +1 -0
  20. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs → ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs} +26 -16
  21. package/fesm2022/ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs.map +1 -0
  22. package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs → ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs} +6 -5
  23. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs.map +1 -0
  24. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs → ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs} +18 -11
  25. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map → ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs.map} +1 -1
  26. package/fesm2022/ng-forge-dynamic-forms.mjs +2 -2
  27. package/package.json +4 -4
  28. package/types/ng-forge-dynamic-forms-integration.d.ts +5 -5
  29. package/types/ng-forge-dynamic-forms-internal.d.ts +1943 -1765
  30. package/types/ng-forge-dynamic-forms.d.ts +59 -267
  31. package/fesm2022/ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs.map +0 -1
  32. package/fesm2022/ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs.map +0 -1
  33. package/fesm2022/ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs.map +0 -1
  34. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs.map +0 -1
  35. package/fesm2022/ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs.map +0 -1
  36. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map +0 -1
  37. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map +0 -1
  38. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map +0 -1
@@ -1,14 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { reflectComponentType, Injector, inject, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, linkedSignal, ChangeDetectionStrategy, Component, InjectionToken, Injectable, isSignal, runInInjectionContext, afterNextRender, untracked, ElementRef, PLATFORM_ID, isDevMode, isWritableSignal, TemplateRef, model, contentChildren, makeEnvironmentProviders } from '@angular/core';
2
+ import { reflectComponentType, Injector, inject, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, linkedSignal, ChangeDetectionStrategy, Component, Injectable, InjectionToken, isSignal, runInInjectionContext, afterNextRender, untracked, PendingTasks, ElementRef, PLATFORM_ID, isDevMode, TemplateRef, model, contentChildren, makeEnvironmentProviders } from '@angular/core';
3
3
  import { explicitEffect } from 'ngxtension/explicit-effect';
4
- import { DynamicFormLogger, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, FieldContextRegistryService, FunctionRegistryService, FORM_OPTIONS, evaluateCondition, FIELD_SIGNAL_CONTEXT, isRowField, isContainerField, isArrayField, hasChildFields, isSimplifiedArrayField, DynamicFormError, isStateLogicConfig, isDerivationLogicConfig, createDebouncedLogicFunction, createLogicFunction, ExpressionParser, applyValidator, isPageField, isContainerTypedField, isGroupField, getFieldValueHandling, normalizeFieldsArray, getNestedValue, isCrossFieldValidator, extractStringDependencies, isCrossFieldBuiltInValidator, hasCrossFieldWhenCondition, extractExpressionDependencies, isCrossFieldStateLogic, isCrossFieldSchema, keyBy, mapValues, isEqual, hasTargetProperty, ARRAY_CONTEXT, GROUP_CONTEXT, FORM_ID_PREFIX, baseFieldMapper, detectFormMode, validatePageNesting, FIELD_REGISTRY, assertNever, ADDON_KIND_REGISTRY, deepMergeDefaults, derivedFromDeferred, createWarningTracker, createFormFieldStateMap, readFieldStateInfo, getChangedKeys, resolveHttpRequest, createDefaultDerivationLogConfig, shouldLog, NoopLogger, RootFormRegistryService, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, EXTERNAL_DATA, DEPRECATION_WARNING_TRACKER, HttpConditionCache, HTTP_CONDITION_CACHE, HttpConditionFunctionCacheService, AsyncConditionFunctionCacheService, DynamicValueFunctionCacheService, LogicFunctionCacheService, setupInitializationTracking, ComponentInitializedEvent, DF_FIELD_TEMPLATES, rowFieldMapper, groupFieldMapper, arrayFieldMapper, pageFieldMapper, textFieldMapper, containerFieldMapper, isWrapperTypeDefinition, EMIT_FORM_VALUE_ON_EVENTS, resolveDynamicValue, DynamicTextPipe } from '@ng-forge/dynamic-forms/internal';
4
+ import { DynamicFormLogger, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, FieldContextRegistryService, FunctionRegistryService, FORM_OPTIONS, evaluateCondition, FIELD_SIGNAL_CONTEXT, isRowField, isContainerField, isArrayField, hasChildFields, isSimplifiedArrayField, DynamicFormError, isStateLogicConfig, isDerivationLogicConfig, createDebouncedLogicFunction, createLogicFunction, ExpressionParser, applyValidator, isPageField, isGenericContainerField, isGroupField, getFieldValueHandling, normalizeFieldsArray, getNestedValue, isCrossFieldValidator, extractStringDependencies, isCrossFieldBuiltInValidator, hasCrossFieldWhenCondition, extractExpressionDependencies, isCrossFieldStateLogic, isCrossFieldSchema, keyBy, mapValues, isEqual, hasTargetProperty, ARRAY_CONTEXT, GROUP_CONTEXT, FORM_ID_PREFIX, baseFieldMapper, detectFormMode, validatePageNesting, FIELD_REGISTRY, assertNever, ADDON_TYPE_REGISTRY, deepMergeDefaults, derivedFromDeferred, createWarningTracker, RootFormRegistryService, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, EXTERNAL_DATA, DEPRECATION_WARNING_TRACKER, createAddonActionRegistry, ADDON_ACTION_REGISTRY, HttpConditionCache, HTTP_CONDITION_CACHE, HttpConditionFunctionCacheService, AsyncConditionFunctionCacheService, DynamicValueFunctionCacheService, LogicFunctionCacheService, setupInitializationTracking, ComponentInitializedEvent, DF_FIELD_TEMPLATES, rowFieldMapper, groupFieldMapper, arrayFieldMapper, pageFieldMapper, textFieldMapper, containerFieldMapper, isWrapperTypeDefinition, ADDON_TYPE_COMPONENT_CACHE, ADDON_TYPE_DEFINITIONS, createDefaultDerivationLogConfig, NoopLogger, EMIT_FORM_VALUE_ON_EVENTS, DynamicTextPipe, ADDON_ACTION_HANDLERS } from '@ng-forge/dynamic-forms/internal';
5
5
  import { takeUntilDestroyed, toObservable, toSignal, outputFromObservable } from '@angular/core/rxjs-interop';
6
- import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, debounceTime, distinctUntilChanged, tap, pipe, scan, Observable, Subject, concatMap, filter, take, startWith, pairwise, merge, combineLatestWith, auditTime, scheduled, queueScheduler, timer } from 'rxjs';
6
+ import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, debounceTime, distinctUntilChanged, tap, pipe, scan, Observable, Subject, concatMap, filter, take } from 'rxjs';
7
7
  import { submit, required, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, email, minLength, maxLength, pattern, min, max, schema, validateStandardSchema, validateTree, form, provideSignalFormsConfig } from '@angular/forms/signals';
8
8
  import { isStandardSchemaMarker } from '@ng-forge/dynamic-forms/schema';
9
- import { isPlatformBrowser, NgComponentOutlet, AsyncPipe, NgTemplateOutlet } from '@angular/common';
10
- import { HttpClient } from '@angular/common/http';
11
- import { catchError as catchError$1 } from 'rxjs/operators';
9
+ import { isPlatformBrowser, AsyncPipe, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';
12
10
  import { NG_STATUS_CLASSES } from '@angular/forms/signals/compat';
13
11
 
14
12
  const EMPTY_WRAPPERS = Object.freeze([]);
@@ -258,7 +256,8 @@ function buildFieldInputs(rawInputs, cache, fieldType) {
258
256
 
259
257
  const EMPTY_STATE = Object.freeze({ phase: 'empty' });
260
258
  class FieldComponentSlot {
261
- state = signal(EMPTY_STATE, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
259
+ state = signal(EMPTY_STATE, /* @ts-ignore */
260
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
262
261
  lastInputs = null;
263
262
  phase = () => this.state().phase;
264
263
  snapshot = () => this.state();
@@ -342,8 +341,10 @@ class FieldComponentSlot {
342
341
  }
343
342
 
344
343
  class DfFieldOutlet {
345
- dfFieldOutlet = input.required(...(ngDevMode ? [{ debugName: "dfFieldOutlet" }] : /* istanbul ignore next */ []));
346
- dfFieldOutletEnvironmentInjector = input(undefined, ...(ngDevMode ? [{ debugName: "dfFieldOutletEnvironmentInjector" }] : /* istanbul ignore next */ []));
344
+ dfFieldOutlet = input.required(/* @ts-ignore */
345
+ ...(ngDevMode ? [{ debugName: "dfFieldOutlet" }] : /* istanbul ignore next */ []));
346
+ dfFieldOutletEnvironmentInjector = input(undefined, /* @ts-ignore */
347
+ ...(ngDevMode ? [{ debugName: "dfFieldOutletEnvironmentInjector" }] : /* istanbul ignore next */ []));
347
348
  vcrRef = inject(ViewContainerRef);
348
349
  vcr = signal(this.vcrRef).asReadonly();
349
350
  destroyRef = inject(DestroyRef);
@@ -351,14 +352,20 @@ class DfFieldOutlet {
351
352
  defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
352
353
  readonlyFieldCache = inject(READONLY_FIELD_TREE_CACHE);
353
354
  fieldComponent = new FieldComponentSlot();
354
- componentIdentity = computed(() => this.dfFieldOutlet().component, ...(ngDevMode ? [{ debugName: "componentIdentity" }] : /* istanbul ignore next */ []));
355
- renderReady = computed(() => this.dfFieldOutlet().renderReady() && !this.dfFieldOutlet().hidden(), ...(ngDevMode ? [{ debugName: "renderReady" }] : /* istanbul ignore next */ []));
356
- rawInputs = computed(() => this.dfFieldOutlet().inputs(), ...(ngDevMode ? [{ debugName: "rawInputs" }] : /* istanbul ignore next */ []));
355
+ componentIdentity = computed(() => this.dfFieldOutlet().component, /* @ts-ignore */
356
+ ...(ngDevMode ? [{ debugName: "componentIdentity" }] : /* istanbul ignore next */ []));
357
+ renderReady = computed(() => this.dfFieldOutlet().renderReady() && !this.dfFieldOutlet().hidden(), /* @ts-ignore */
358
+ ...(ngDevMode ? [{ debugName: "renderReady" }] : /* istanbul ignore next */ []));
359
+ rawInputs = computed(() => this.dfFieldOutlet().inputs(), /* @ts-ignore */
360
+ ...(ngDevMode ? [{ debugName: "rawInputs" }] : /* istanbul ignore next */ []));
357
361
  wrappers = computed(() => resolveWrappers(this.dfFieldOutlet().fieldDef, this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
358
- fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type), ...(ngDevMode ? [{ debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
362
+ fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type), /* @ts-ignore */
363
+ ...(ngDevMode ? [{ debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
359
364
  defaultEnvInjector = inject(EnvironmentInjector);
360
- fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector, ...(ngDevMode ? [{ debugName: "fieldEnvInjector" }] : /* istanbul ignore next */ []));
361
- fieldInjector = computed(() => this.dfFieldOutlet().injector, ...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
365
+ fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector, /* @ts-ignore */
366
+ ...(ngDevMode ? [{ debugName: "fieldEnvInjector" }] : /* istanbul ignore next */ []));
367
+ fieldInjector = computed(() => this.dfFieldOutlet().injector, /* @ts-ignore */
368
+ ...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
362
369
  constructor() {
363
370
  createWrapperChainController({
364
371
  vcr: this.vcr,
@@ -376,10 +383,10 @@ class DfFieldOutlet {
376
383
  explicitEffect([this.rawInputs, this.fieldInputs], ([rawInputs, fieldInputs]) => this.fieldComponent.pushInputs(rawInputs, fieldInputs));
377
384
  this.destroyRef.onDestroy(() => this.fieldComponent.destroyOnTeardown());
378
385
  }
379
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive });
380
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DfFieldOutlet, isStandalone: true, selector: "[dfFieldOutlet]", inputs: { dfFieldOutlet: { classPropertyName: "dfFieldOutlet", publicName: "dfFieldOutlet", isSignal: true, isRequired: true, transformFunction: null }, dfFieldOutletEnvironmentInjector: { classPropertyName: "dfFieldOutletEnvironmentInjector", publicName: "dfFieldOutletEnvironmentInjector", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
386
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive });
387
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: DfFieldOutlet, isStandalone: true, selector: "[dfFieldOutlet]", inputs: { dfFieldOutlet: { classPropertyName: "dfFieldOutlet", publicName: "dfFieldOutlet", isSignal: true, isRequired: true, transformFunction: null }, dfFieldOutletEnvironmentInjector: { classPropertyName: "dfFieldOutletEnvironmentInjector", publicName: "dfFieldOutletEnvironmentInjector", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
381
388
  }
382
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldOutlet, decorators: [{
389
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldOutlet, decorators: [{
383
390
  type: Directive,
384
391
  args: [{
385
392
  selector: '[dfFieldOutlet]',
@@ -415,7 +422,7 @@ function createSubmissionHandler(options) {
415
422
  }));
416
423
  }
417
424
 
418
- class SubmitEvent {
425
+ class FormSubmitEvent {
419
426
  type = 'submit';
420
427
  }
421
428
 
@@ -547,26 +554,32 @@ class PageOrchestratorComponent {
547
554
  fieldContextRegistry = inject(FieldContextRegistryService);
548
555
  functionRegistry = inject(FunctionRegistryService);
549
556
  formOptions = inject(FORM_OPTIONS, { optional: true });
550
- pageFields = input.required(...(ngDevMode ? [{ debugName: "pageFields" }] : /* istanbul ignore next */ []));
551
- form = input.required(...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
552
- fieldSignalContext = input.required(...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
557
+ pageFields = input.required(/* @ts-ignore */
558
+ ...(ngDevMode ? [{ debugName: "pageFields" }] : /* istanbul ignore next */ []));
559
+ form = input.required(/* @ts-ignore */
560
+ ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
561
+ fieldSignalContext = input.required(/* @ts-ignore */
562
+ ...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
553
563
  pageHiddenStates = computed(() => {
554
564
  const pages = this.pageFields();
555
565
  return pages.map((page) => this.evaluatePageHidden(page));
556
- }, ...(ngDevMode ? [{ debugName: "pageHiddenStates" }] : /* istanbul ignore next */ []));
566
+ }, /* @ts-ignore */
567
+ ...(ngDevMode ? [{ debugName: "pageHiddenStates" }] : /* istanbul ignore next */ []));
557
568
  visiblePageIndices = computed(() => {
558
569
  const hiddenStates = this.pageHiddenStates();
559
570
  return hiddenStates
560
571
  .map((hidden, index) => ({ index, hidden }))
561
572
  .filter((item) => !item.hidden)
562
573
  .map((item) => item.index);
563
- }, ...(ngDevMode ? [{ debugName: "visiblePageIndices" }] : /* istanbul ignore next */ []));
574
+ }, /* @ts-ignore */
575
+ ...(ngDevMode ? [{ debugName: "visiblePageIndices" }] : /* istanbul ignore next */ []));
564
576
  currentPageIndex = linkedSignal(() => {
565
577
  const totalPages = this.pageFields().length;
566
578
  if (totalPages === 0)
567
579
  return 0;
568
580
  return 0;
569
- }, ...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
581
+ }, /* @ts-ignore */
582
+ ...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
570
583
  state = computed(() => {
571
584
  const currentIndex = this.currentPageIndex();
572
585
  const totalPages = this.pageFields().length;
@@ -581,7 +594,8 @@ class PageOrchestratorComponent {
581
594
  isLastPage: isLastVisiblePage,
582
595
  navigationDisabled: false,
583
596
  };
584
- }, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
597
+ }, /* @ts-ignore */
598
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
585
599
  currentPageValid = computed(() => {
586
600
  const currentIndex = this.currentPageIndex();
587
601
  const pages = this.pageFields();
@@ -602,11 +616,13 @@ class PageOrchestratorComponent {
602
616
  }
603
617
  }
604
618
  return true;
605
- }, ...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
619
+ }, /* @ts-ignore */
620
+ ...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
606
621
  extendedFieldSignalContext = computed(() => ({
607
622
  ...this.fieldSignalContext(),
608
623
  currentPageValid: this.currentPageValid,
609
- }), ...(ngDevMode ? [{ debugName: "extendedFieldSignalContext" }] : /* istanbul ignore next */ []));
624
+ }), /* @ts-ignore */
625
+ ...(ngDevMode ? [{ debugName: "extendedFieldSignalContext" }] : /* istanbul ignore next */ []));
610
626
  constructor() {
611
627
  this.setupEventListeners();
612
628
  explicitEffect([this.state, this.visiblePageIndices], ([state, visibleIndices]) => {
@@ -765,8 +781,8 @@ class PageOrchestratorComponent {
765
781
  }
766
782
  return false;
767
783
  }
768
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
769
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PageOrchestratorComponent, isStandalone: true, selector: "div[page-orchestrator]", inputs: { pageFields: { classPropertyName: "pageFields", publicName: "pageFields", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, fieldSignalContext: { classPropertyName: "fieldSignalContext", publicName: "fieldSignalContext", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-current-page": "state().currentPageIndex", "attr.data-total-pages": "state().totalPages" }, classAttribute: "df-page-orchestrator" }, providers: [
784
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
785
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: PageOrchestratorComponent, isStandalone: true, selector: "div[page-orchestrator]", inputs: { pageFields: { classPropertyName: "pageFields", publicName: "pageFields", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, fieldSignalContext: { classPropertyName: "fieldSignalContext", publicName: "fieldSignalContext", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-current-page": "state().currentPageIndex", "attr.data-total-pages": "state().totalPages" }, classAttribute: "df-page-orchestrator" }, providers: [
770
786
  {
771
787
  provide: FIELD_SIGNAL_CONTEXT,
772
788
  useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
@@ -805,9 +821,12 @@ class PageOrchestratorComponent {
805
821
  }
806
822
  }
807
823
  }
808
- `, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs').then(m => m.default)], () => [import('./ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs').then(m => m.default)]] });
824
+ `, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
825
+ import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs').then(m => m.default)], () => [/* @ts-ignore */
826
+ import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs').then(m => m.default)]] });
809
827
  }
810
- i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.14", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
828
+ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.0", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [/* @ts-ignore */
829
+ import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
811
830
  type: Component,
812
831
  args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
813
832
  @for (pageField of pageFields(); track pageField.key; let i = $index) {
@@ -867,8 +886,6 @@ function collectLeafFieldKeys(fields) {
867
886
  return keys;
868
887
  }
869
888
 
870
- const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
871
-
872
889
  const NORMALIZED_ARRAY_METADATA = Symbol('normalizedArrayMetadata');
873
890
  function setNormalizedArrayMetadata(arrayField, metadata) {
874
891
  arrayField[NORMALIZED_ARRAY_METADATA] = metadata;
@@ -1008,7 +1025,8 @@ function createSlot() {
1008
1025
  let counter = 0;
1009
1026
  const templates = new Map();
1010
1027
  const itemOrder = [];
1011
- const primitiveFieldKey = signal(undefined, ...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
1028
+ const primitiveFieldKey = signal(undefined, /* @ts-ignore */
1029
+ ...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
1012
1030
  return {
1013
1031
  templates,
1014
1032
  itemOrder,
@@ -1064,10 +1082,10 @@ class ArrayItemRegistryService {
1064
1082
  slot.clear();
1065
1083
  this.slots.clear();
1066
1084
  }
1067
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayItemRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1068
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayItemRegistryService });
1085
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1086
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService });
1069
1087
  }
1070
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayItemRegistryService, decorators: [{
1088
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService, decorators: [{
1071
1089
  type: Injectable
1072
1090
  }] });
1073
1091
 
@@ -1091,10 +1109,10 @@ class SchemaRegistryService {
1091
1109
  }
1092
1110
  return schema;
1093
1111
  }
1094
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1095
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SchemaRegistryService });
1112
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1113
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService });
1096
1114
  }
1097
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SchemaRegistryService, decorators: [{
1115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService, decorators: [{
1098
1116
  type: Injectable
1099
1117
  }] });
1100
1118
 
@@ -1349,7 +1367,7 @@ function getNumberValidationConfig(fieldDef) {
1349
1367
  }
1350
1368
  function mapFieldToForm(fieldDef, fieldPath, inheritedContext = DEFAULT_FIELD_TREE_MAPPING_CONTEXT) {
1351
1369
  const ownContext = resolveFieldOwnContext(fieldDef, inheritedContext);
1352
- if (isPageField(fieldDef) || isRowField(fieldDef) || isContainerTypedField(fieldDef)) {
1370
+ if (isPageField(fieldDef) || isRowField(fieldDef) || isGenericContainerField(fieldDef)) {
1353
1371
  const descendantContext = resolveDescendantContext(fieldDef, ownContext);
1354
1372
  mapContainerChildren(fieldDef.fields, fieldPath, descendantContext);
1355
1373
  return;
@@ -1371,7 +1389,7 @@ function mapContainerChildren(fields, parentPath, inheritedContext) {
1371
1389
  return;
1372
1390
  const pathRecord = parentPath;
1373
1391
  for (const field of fields) {
1374
- if (isPageField(field) || isRowField(field) || isContainerTypedField(field)) {
1392
+ if (isPageField(field) || isRowField(field) || isGenericContainerField(field)) {
1375
1393
  const layoutOwn = resolveFieldOwnContext(field, inheritedContext);
1376
1394
  const layoutDescendant = resolveDescendantContext(field, layoutOwn);
1377
1395
  mapContainerChildren(field.fields, parentPath, layoutDescendant);
@@ -1492,7 +1510,7 @@ function mapArrayFieldToForm(arrayField, fieldPath, context) {
1492
1510
  }
1493
1511
  else if (isPageField(itemDef) ||
1494
1512
  isRowField(itemDef) ||
1495
- isContainerTypedField(itemDef)) {
1513
+ isGenericContainerField(itemDef)) {
1496
1514
  collectFieldsFromObjectItem([itemDef], allObjectFields);
1497
1515
  }
1498
1516
  else {
@@ -1508,7 +1526,7 @@ function mapArrayFieldToForm(arrayField, fieldPath, context) {
1508
1526
  const itemSchema = schema((itemPath) => {
1509
1527
  const pathRecord = itemPath;
1510
1528
  for (const templateField of allObjectFields) {
1511
- if (isRowField(templateField) || isPageField(templateField) || isContainerTypedField(templateField)) {
1529
+ if (isRowField(templateField) || isPageField(templateField) || isGenericContainerField(templateField)) {
1512
1530
  const layoutOwn = resolveFieldOwnContext(templateField, context);
1513
1531
  const layoutDescendant = resolveDescendantContext(templateField, layoutOwn);
1514
1532
  mapContainerChildren(templateField.fields, itemPath, layoutDescendant);
@@ -1543,7 +1561,7 @@ function mapArrayFieldToForm(arrayField, fieldPath, context) {
1543
1561
  function collectFieldsFromObjectItem(itemFields, allFields) {
1544
1562
  const seenKeys = new Set(allFields.map((f) => f.key).filter(Boolean));
1545
1563
  for (const field of itemFields) {
1546
- if (isRowField(field) || isPageField(field) || isContainerTypedField(field)) {
1564
+ if (isRowField(field) || isPageField(field) || isGenericContainerField(field)) {
1547
1565
  const containerKey = `__container_${field.type}_${JSON.stringify(field.fields?.map((f) => f.key))}`;
1548
1566
  if (!seenKeys.has(containerKey)) {
1549
1567
  seenKeys.add(containerKey);
@@ -2120,7 +2138,7 @@ function flattenFields(fields, registry, options = {}) {
2120
2138
  let autoKeyCounter = 0;
2121
2139
  for (const field of fields) {
2122
2140
  const valueHandling = getFieldValueHandling(field.type, registry);
2123
- if (options.preserveRows && (isRowField(field) || isContainerTypedField(field))) {
2141
+ if (options.preserveRows && (isRowField(field) || isGenericContainerField(field))) {
2124
2142
  if (field.fields) {
2125
2143
  const flattenedChildren = flattenFields(normalizeFieldsArray(field.fields), registry, options);
2126
2144
  const autoPrefix = isRowField(field) ? 'auto_row' : 'auto_container';
@@ -2783,7 +2801,7 @@ function validateFormConfig(fields, registry, logger) {
2783
2801
  }
2784
2802
  }
2785
2803
 
2786
- function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2804
+ function validateFieldAddons(field, fieldRegistry, typeRegistry, source) {
2787
2805
  const warnings = [];
2788
2806
  const incoming = field.addons;
2789
2807
  if (!incoming?.length) {
@@ -2801,8 +2819,8 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2801
2819
  return { addons: [], warnings };
2802
2820
  }
2803
2821
  const allowedSlots = addonSupport.slots;
2804
- const allowedKinds = addonSupport.allowedKinds;
2805
- const registeredKinds = Array.from(kindRegistry.keys());
2822
+ const allowedTypes = addonSupport.allowedTypes;
2823
+ const registeredTypes = Array.from(typeRegistry.keys());
2806
2824
  const survivors = [];
2807
2825
  for (const addon of incoming) {
2808
2826
  if (!allowedSlots.includes(addon.slot)) {
@@ -2815,31 +2833,31 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2815
2833
  });
2816
2834
  continue;
2817
2835
  }
2818
- const kindDef = kindRegistry.get(addon.kind);
2819
- if (!kindDef) {
2836
+ const typeDef = typeRegistry.get(addon.type);
2837
+ if (!typeDef) {
2820
2838
  warnings.push({
2821
- type: 'unknown-kind',
2839
+ type: 'unknown-type',
2822
2840
  fieldKey: field.key,
2823
- kind: addon.kind,
2824
- registeredKinds,
2841
+ addonType: addon.type,
2842
+ registeredTypes,
2825
2843
  });
2826
2844
  continue;
2827
2845
  }
2828
- if (allowedKinds && !allowedKinds.includes(kindDef.kind)) {
2846
+ if (allowedTypes && !allowedTypes.includes(typeDef.type)) {
2829
2847
  warnings.push({
2830
- type: 'kind-not-allowed',
2848
+ type: 'type-not-allowed',
2831
2849
  fieldKey: field.key,
2832
2850
  fieldType,
2833
- kind: kindDef.kind,
2834
- allowedKinds,
2851
+ addonType: typeDef.type,
2852
+ allowedTypes,
2835
2853
  });
2836
2854
  continue;
2837
2855
  }
2838
- if (source === 'json' && kindDef.jsonSafe === false) {
2856
+ if (source === 'json' && typeDef.jsonSafe === false) {
2839
2857
  warnings.push({
2840
- type: 'code-only-kind-in-json',
2858
+ type: 'code-only-type-in-json',
2841
2859
  fieldKey: field.key,
2842
- kind: kindDef.kind,
2860
+ addonType: typeDef.type,
2843
2861
  });
2844
2862
  continue;
2845
2863
  }
@@ -2859,7 +2877,7 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2859
2877
  warnings.push({
2860
2878
  type: 'code-only-action-in-json',
2861
2879
  fieldKey: field.key,
2862
- reason: `function '${key}' on kind '${kindDef.kind}' (use a signal/observable in code, or omit in JSON)`,
2880
+ reason: `function '${key}' on type '${typeDef.type}' (use a signal/observable in code, or omit in JSON)`,
2863
2881
  });
2864
2882
  }
2865
2883
  };
@@ -2869,7 +2887,7 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2869
2887
  warnings.push({
2870
2888
  type: 'code-only-action-in-json',
2871
2889
  fieldKey: field.key,
2872
- reason: `inline 'action' function on kind '${kindDef.kind}'`,
2890
+ reason: `inline 'action' function on type '${typeDef.type}'`,
2873
2891
  });
2874
2892
  }
2875
2893
  dropReactiveFn('hidden');
@@ -2883,22 +2901,22 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2883
2901
  warnings.push({
2884
2902
  type: 'code-only-action-in-json',
2885
2903
  fieldKey: field.key,
2886
- reason: `multiple click variants on kind '${kindDef.kind}' (${declaredVariants.join(', ')}); kept '${declaredVariants[0]}'`,
2904
+ reason: `multiple click variants on type '${typeDef.type}' (${declaredVariants.join(', ')}); kept '${declaredVariants[0]}'`,
2887
2905
  });
2888
2906
  }
2889
2907
  if (stripped) {
2890
2908
  survivors.push(sanitized);
2891
2909
  continue;
2892
2910
  }
2893
- if (kindDef.validate) {
2911
+ if (typeDef.validate) {
2894
2912
  try {
2895
- kindDef.validate(addon, field.key);
2913
+ typeDef.validate(addon, field.key);
2896
2914
  }
2897
2915
  catch (error) {
2898
2916
  warnings.push({
2899
2917
  type: 'shape-violation',
2900
2918
  fieldKey: field.key,
2901
- kind: kindDef.kind,
2919
+ addonType: typeDef.type,
2902
2920
  reason: error instanceof Error ? error.message : String(error),
2903
2921
  });
2904
2922
  continue;
@@ -2908,18 +2926,18 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
2908
2926
  }
2909
2927
  return { addons: survivors, warnings };
2910
2928
  }
2911
- function walkAndValidateAddons(fields, fieldRegistry, kindRegistry, source) {
2929
+ function walkAndValidateAddons(fields, fieldRegistry, typeRegistry, source) {
2912
2930
  const allWarnings = [];
2913
2931
  const sanitized = fields.map((field) => {
2914
2932
  let next = field;
2915
2933
  if (field.addons?.length) {
2916
- const { addons, warnings } = validateFieldAddons(field, fieldRegistry, kindRegistry, source);
2934
+ const { addons, warnings } = validateFieldAddons(field, fieldRegistry, typeRegistry, source);
2917
2935
  allWarnings.push(...warnings);
2918
2936
  next = { ...next, addons };
2919
2937
  }
2920
2938
  const children = next.fields;
2921
2939
  if (Array.isArray(children)) {
2922
- const { fields: visited, warnings } = walkAndValidateAddons(children, fieldRegistry, kindRegistry, source);
2940
+ const { fields: visited, warnings } = walkAndValidateAddons(children, fieldRegistry, typeRegistry, source);
2923
2941
  allWarnings.push(...warnings);
2924
2942
  next = { ...next, fields: visited };
2925
2943
  }
@@ -2936,14 +2954,14 @@ function addonWarningKey(w) {
2936
2954
  return `${w.type}|${w.fieldKey}|${w.fieldType}`;
2937
2955
  case 'unknown-slot':
2938
2956
  return `${w.type}|${w.fieldKey}|${w.fieldType}|${w.slot}`;
2939
- case 'unknown-kind':
2940
- return `${w.type}|${w.fieldKey}|${w.kind}`;
2941
- case 'kind-not-allowed':
2942
- return `${w.type}|${w.fieldKey}|${w.fieldType}|${w.kind}`;
2957
+ case 'unknown-type':
2958
+ return `${w.type}|${w.fieldKey}|${w.addonType}`;
2959
+ case 'type-not-allowed':
2960
+ return `${w.type}|${w.fieldKey}|${w.fieldType}|${w.addonType}`;
2943
2961
  case 'shape-violation':
2944
- return `${w.type}|${w.fieldKey}|${w.kind}|${w.reason}`;
2945
- case 'code-only-kind-in-json':
2946
- return `${w.type}|${w.fieldKey}|${w.kind}`;
2962
+ return `${w.type}|${w.fieldKey}|${w.addonType}|${w.reason}`;
2963
+ case 'code-only-type-in-json':
2964
+ return `${w.type}|${w.fieldKey}|${w.addonType}`;
2947
2965
  case 'code-only-action-in-json':
2948
2966
  return `${w.type}|${w.fieldKey}|${w.reason}`;
2949
2967
  }
@@ -2958,17 +2976,17 @@ function formatAddonWarning(w) {
2958
2976
  case 'unknown-slot':
2959
2977
  return (`Field '${w.fieldKey}' (type: '${w.fieldType}'): slot '${w.slot}' is not supported. ` +
2960
2978
  `Allowed: ${w.allowedSlots.join(', ')}. Addon dropped.`);
2961
- case 'unknown-kind':
2962
- return (`Field '${w.fieldKey}': addon kind '${w.kind}' is not registered. ` +
2963
- `Did you forget withCustomAddon({ kind: '${w.kind}', ... })? ` +
2964
- `Currently registered: ${w.registeredKinds.join(', ') || '(none)'}. Addon dropped.`);
2965
- case 'kind-not-allowed':
2966
- return (`Field '${w.fieldKey}' (type: '${w.fieldType}'): addon kind '${w.kind}' is not in the allowed list. ` +
2967
- `Allowed: ${w.allowedKinds.join(', ')}. Addon dropped.`);
2979
+ case 'unknown-type':
2980
+ return (`Field '${w.fieldKey}': addon type '${w.addonType}' is not registered. ` +
2981
+ `Did you forget withCustomAddon({ type: '${w.addonType}', ... })? ` +
2982
+ `Currently registered: ${w.registeredTypes.join(', ') || '(none)'}. Addon dropped.`);
2983
+ case 'type-not-allowed':
2984
+ return (`Field '${w.fieldKey}' (type: '${w.fieldType}'): addon type '${w.addonType}' is not in the allowed list. ` +
2985
+ `Allowed: ${w.allowedTypes.join(', ')}. Addon dropped.`);
2968
2986
  case 'shape-violation':
2969
- return `Field '${w.fieldKey}': addon kind '${w.kind}' shape invalid: ${w.reason}. Addon dropped.`;
2970
- case 'code-only-kind-in-json':
2971
- return `Field '${w.fieldKey}': addon kind '${w.kind}' is code-only and cannot be used with JSON-derived configs. Addon dropped.`;
2987
+ return `Field '${w.fieldKey}': addon type '${w.addonType}' shape invalid: ${w.reason}. Addon dropped.`;
2988
+ case 'code-only-type-in-json':
2989
+ return `Field '${w.fieldKey}': addon type '${w.addonType}' is code-only and cannot be used with JSON-derived configs. Addon dropped.`;
2972
2990
  case 'code-only-action-in-json':
2973
2991
  return `Field '${w.fieldKey}': ${w.reason}. Use 'preset' or 'actionRef' instead. Addon dropped.`;
2974
2992
  }
@@ -3208,7 +3226,8 @@ class FormStateMachine {
3208
3226
  }
3209
3227
  constructor(config) {
3210
3228
  this.config = config;
3211
- this._state = signal(createUninitializedState(), ...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
3229
+ this._state = signal(createUninitializedState(), /* @ts-ignore */
3230
+ ...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
3212
3231
  this.state = this._state.asReadonly();
3213
3232
  this.state$ = toObservable(this._state, { injector: config.injector });
3214
3233
  this.setupActionProcessing();
@@ -3518,12 +3537,13 @@ class FormStateManager {
3518
3537
  })();
3519
3538
  valueExclusionDefaults = inject(VALUE_EXCLUSION_DEFAULTS);
3520
3539
  fieldRegistry = injectFieldRegistry();
3521
- addonKindRegistry = inject(ADDON_KIND_REGISTRY);
3540
+ addonKindRegistry = inject(ADDON_TYPE_REGISTRY);
3522
3541
  lastAddonWarningKeys = new Set();
3523
3542
  scheduler = createSideEffectScheduler(this.injector);
3524
3543
  machine;
3525
3544
  fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);
3526
- fieldLoadingErrors = signal([], ...(ngDevMode ? [{ debugName: "fieldLoadingErrors" }] : /* istanbul ignore next */ []));
3545
+ fieldLoadingErrors = signal([], /* @ts-ignore */
3546
+ ...(ngDevMode ? [{ debugName: "fieldLoadingErrors" }] : /* istanbul ignore next */ []));
3527
3547
  _formCache = { current: undefined };
3528
3548
  activeConfig = computed(() => {
3529
3549
  const state = this.machine.state();
@@ -3540,33 +3560,39 @@ class FormStateManager {
3540
3560
  if (state.type === LifecycleState.Initializing)
3541
3561
  return state.config;
3542
3562
  return undefined;
3543
- }, ...(ngDevMode ? [{ debugName: "activeConfig" }] : /* istanbul ignore next */ []));
3563
+ }, /* @ts-ignore */
3564
+ ...(ngDevMode ? [{ debugName: "activeConfig" }] : /* istanbul ignore next */ []));
3544
3565
  renderPhase = computed(() => {
3545
3566
  const state = this.machine.state();
3546
3567
  if (isTransitioningState(state) && state.phase === Phase.Teardown)
3547
3568
  return Phase.Teardown;
3548
3569
  return 'render';
3549
- }, ...(ngDevMode ? [{ debugName: "renderPhase" }] : /* istanbul ignore next */ []));
3550
- shouldRender = computed(() => this.activeConfig() !== undefined, ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
3570
+ }, /* @ts-ignore */
3571
+ ...(ngDevMode ? [{ debugName: "renderPhase" }] : /* istanbul ignore next */ []));
3572
+ shouldRender = computed(() => this.activeConfig() !== undefined, /* @ts-ignore */
3573
+ ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
3551
3574
  formModeDetection = computed(() => {
3552
3575
  const config = this.activeConfig();
3553
3576
  if (!config) {
3554
3577
  return { mode: 'non-paged', isValid: true, errors: [] };
3555
3578
  }
3556
3579
  return detectFormMode(config.fields || []);
3557
- }, ...(ngDevMode ? [{ debugName: "formModeDetection" }] : /* istanbul ignore next */ []));
3580
+ }, /* @ts-ignore */
3581
+ ...(ngDevMode ? [{ debugName: "formModeDetection" }] : /* istanbul ignore next */ []));
3558
3582
  formConfigValidation = computed(() => {
3559
3583
  const config = this.activeConfig();
3560
3584
  if (!config)
3561
3585
  return { isValid: true, errors: [], warnings: [] };
3562
3586
  return FormModeValidator.validateFormConfiguration(config.fields || []);
3563
- }, ...(ngDevMode ? [{ debugName: "formConfigValidation" }] : /* istanbul ignore next */ []));
3587
+ }, /* @ts-ignore */
3588
+ ...(ngDevMode ? [{ debugName: "formConfigValidation" }] : /* istanbul ignore next */ []));
3564
3589
  effectiveFormOptions = computed(() => {
3565
3590
  const config = this.activeConfig();
3566
3591
  const configOptions = config?.options || {};
3567
3592
  const inputOptions = this.deps.formOptions() ?? undefined;
3568
3593
  return { ...configOptions, ...inputOptions };
3569
- }, ...(ngDevMode ? [{ debugName: "effectiveFormOptions" }] : /* istanbul ignore next */ []));
3594
+ }, /* @ts-ignore */
3595
+ ...(ngDevMode ? [{ debugName: "effectiveFormOptions" }] : /* istanbul ignore next */ []));
3570
3596
  pageFieldDefinitions = computed(() => {
3571
3597
  const config = this.activeConfig();
3572
3598
  const mode = this.formModeDetection().mode;
@@ -3574,8 +3600,10 @@ class FormStateManager {
3574
3600
  return config.fields.filter(isPageField);
3575
3601
  }
3576
3602
  return [];
3577
- }, ...(ngDevMode ? [{ debugName: "pageFieldDefinitions" }] : /* istanbul ignore next */ []));
3578
- rawFieldRegistry = computed(() => this.fieldRegistry.raw, ...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
3603
+ }, /* @ts-ignore */
3604
+ ...(ngDevMode ? [{ debugName: "pageFieldDefinitions" }] : /* istanbul ignore next */ []));
3605
+ rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
3606
+ ...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
3579
3607
  formSetup = computed(() => {
3580
3608
  const registry = this.rawFieldRegistry();
3581
3609
  const state = this.machine.state();
@@ -3600,14 +3628,17 @@ class FormStateManager {
3600
3628
  return this.createFormSetupFromConfig(config.fields, modeDetection.mode, registry);
3601
3629
  }
3602
3630
  return this.createEmptyFormSetup(registry);
3603
- }, ...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
3604
- defaultValues = linkedSignal(() => this.formSetup().defaultValues, ...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
3631
+ }, /* @ts-ignore */
3632
+ ...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
3633
+ defaultValues = linkedSignal(() => this.formSetup().defaultValues, /* @ts-ignore */
3634
+ ...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
3605
3635
  validKeys = computed(() => {
3606
3636
  const schemaFields = this.formSetup().schemaFields;
3607
3637
  if (!schemaFields || schemaFields.length === 0)
3608
3638
  return undefined;
3609
3639
  return new Set(schemaFields.map((f) => f.key).filter((key) => key !== undefined));
3610
- }, ...(ngDevMode ? [{ debugName: "validKeys" }] : /* istanbul ignore next */ []));
3640
+ }, /* @ts-ignore */
3641
+ ...(ngDevMode ? [{ debugName: "validKeys" }] : /* istanbul ignore next */ []));
3611
3642
  fieldStateSnapshot = computed(() => {
3612
3643
  const setup = this.formSetup();
3613
3644
  if (!setup.schemaFields || setup.schemaFields.length === 0)
@@ -3615,9 +3646,12 @@ class FormStateManager {
3615
3646
  const snapshot = {};
3616
3647
  collectFieldStateSnapshot(setup.schemaFields, asFieldTreeRecord(this.form()), [], snapshot);
3617
3648
  return snapshot;
3618
- }, ...(ngDevMode ? [{ debugName: "fieldStateSnapshot" }] : /* istanbul ignore next */ []));
3619
- excludedValueStore = signal({}, ...(ngDevMode ? [{ debugName: "excludedValueStore" }] : /* istanbul ignore next */ []));
3620
- prevFieldStateSnapshot = signal({}, ...(ngDevMode ? [{ debugName: "prevFieldStateSnapshot" }] : /* istanbul ignore next */ []));
3649
+ }, /* @ts-ignore */
3650
+ ...(ngDevMode ? [{ debugName: "fieldStateSnapshot" }] : /* istanbul ignore next */ []));
3651
+ excludedValueStore = signal({}, /* @ts-ignore */
3652
+ ...(ngDevMode ? [{ debugName: "excludedValueStore" }] : /* istanbul ignore next */ []));
3653
+ prevFieldStateSnapshot = signal({}, /* @ts-ignore */
3654
+ ...(ngDevMode ? [{ debugName: "prevFieldStateSnapshot" }] : /* istanbul ignore next */ []));
3621
3655
  entity = linkedSignal(() => {
3622
3656
  const inputValue = this.deps.value();
3623
3657
  const defaults = this.defaultValues();
@@ -3656,12 +3690,14 @@ class FormStateManager {
3656
3690
  return createFormLevelSchema(config.schema);
3657
3691
  }
3658
3692
  return undefined;
3659
- }), ...(ngDevMode ? [{ debugName: "formSchema" }] : /* istanbul ignore next */ []));
3693
+ }), /* @ts-ignore */
3694
+ ...(ngDevMode ? [{ debugName: "formSchema" }] : /* istanbul ignore next */ []));
3660
3695
  form = computed(() => {
3661
3696
  const schema = this.formSchema();
3662
3697
  const injector = this.injector;
3663
3698
  return untracked(() => (schema ? form(this.entity, schema, { injector }) : form(this.entity, { injector })));
3664
- }, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
3699
+ }, /* @ts-ignore */
3700
+ ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
3665
3701
  isFieldPipelineSettled;
3666
3702
  fieldSignalContext = computed(() => {
3667
3703
  const formSignal = this.form;
@@ -3681,9 +3717,12 @@ class FormStateManager {
3681
3717
  return currentForm;
3682
3718
  },
3683
3719
  };
3684
- }, ...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
3685
- formInstance = computed(() => this.form()(), ...(ngDevMode ? [{ debugName: "formInstance" }] : /* istanbul ignore next */ []));
3686
- formValue = computed(() => this.formInstance().value(), ...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
3720
+ }, /* @ts-ignore */
3721
+ ...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
3722
+ formInstance = computed(() => this.form()(), /* @ts-ignore */
3723
+ ...(ngDevMode ? [{ debugName: "formInstance" }] : /* istanbul ignore next */ []));
3724
+ formValue = computed(() => this.formInstance().value(), /* @ts-ignore */
3725
+ ...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
3687
3726
  filteredFormValue = computed(() => {
3688
3727
  const rawValue = this.formValue();
3689
3728
  const setup = this.formSetup();
@@ -3699,7 +3738,8 @@ class FormStateManager {
3699
3738
  };
3700
3739
  const fieldTreeRecord = asFieldTreeRecord(formTree);
3701
3740
  return filterFormValue(rawValue, setup.schemaFields, fieldTreeRecord, setup.registry, this.valueExclusionDefaults, formOptions);
3702
- }, ...(ngDevMode ? [{ debugName: "filteredFormValue" }] : /* istanbul ignore next */ []));
3741
+ }, /* @ts-ignore */
3742
+ ...(ngDevMode ? [{ debugName: "filteredFormValue" }] : /* istanbul ignore next */ []));
3703
3743
  boundFormValue = computed(() => {
3704
3744
  const rawValue = this.formValue();
3705
3745
  const setup = this.formSetup();
@@ -3715,18 +3755,26 @@ class FormStateManager {
3715
3755
  };
3716
3756
  const fieldTreeRecord = asFieldTreeRecord(formTree);
3717
3757
  return filterFormValue(rawValue, setup.schemaFields, fieldTreeRecord, setup.registry, BOUND_VALUE_EXCLUSION_BASELINE, formOptions);
3718
- }, ...(ngDevMode ? [{ debugName: "boundFormValue" }] : /* istanbul ignore next */ []));
3719
- valid = computed(() => this.formInstance().valid(), ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
3720
- invalid = computed(() => this.formInstance().invalid(), ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
3721
- dirty = computed(() => this.formInstance().dirty(), ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
3722
- touched = computed(() => this.formInstance().touched(), ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
3723
- errors = computed(() => this.formInstance().errors(), ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
3758
+ }, /* @ts-ignore */
3759
+ ...(ngDevMode ? [{ debugName: "boundFormValue" }] : /* istanbul ignore next */ []));
3760
+ valid = computed(() => this.formInstance().valid(), /* @ts-ignore */
3761
+ ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
3762
+ invalid = computed(() => this.formInstance().invalid(), /* @ts-ignore */
3763
+ ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
3764
+ dirty = computed(() => this.formInstance().dirty(), /* @ts-ignore */
3765
+ ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
3766
+ touched = computed(() => this.formInstance().touched(), /* @ts-ignore */
3767
+ ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
3768
+ errors = computed(() => this.formInstance().errors(), /* @ts-ignore */
3769
+ ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
3724
3770
  disabled = computed(() => {
3725
3771
  const optionsDisabled = this.effectiveFormOptions().disabled;
3726
3772
  const formDisabled = this.formInstance().disabled();
3727
3773
  return optionsDisabled ?? formDisabled;
3728
- }, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3729
- submitting = computed(() => this.formInstance().submitting(), ...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
3774
+ }, /* @ts-ignore */
3775
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3776
+ submitting = computed(() => this.formInstance().submitting(), /* @ts-ignore */
3777
+ ...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
3730
3778
  fieldsSource = computed(() => {
3731
3779
  const state = this.machine.state();
3732
3780
  if (isTransitioningState(state)) {
@@ -3751,7 +3799,8 @@ class FormStateManager {
3751
3799
  fieldInjector = computed(() => Injector.create({
3752
3800
  parent: this.injector,
3753
3801
  providers: [{ provide: FIELD_SIGNAL_CONTEXT, useValue: this.fieldSignalContext() }],
3754
- }), ...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
3802
+ }), /* @ts-ignore */
3803
+ ...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
3755
3804
  resolvedFields;
3756
3805
  submitted$;
3757
3806
  reset$;
@@ -3817,7 +3866,8 @@ class FormStateManager {
3817
3866
  }), map((fields) => fields.filter((f) => f !== undefined)), scan(reconcileFields, [])), { initialValue: [], injector: this.injector });
3818
3867
  this.isFieldPipelineSettled = computed(() => {
3819
3868
  return this.fieldsSource().length === this.resolvedFields().length;
3820
- }, ...(ngDevMode ? [{ debugName: "isFieldPipelineSettled" }] : /* istanbul ignore next */ []));
3869
+ }, /* @ts-ignore */
3870
+ ...(ngDevMode ? [{ debugName: "isFieldPipelineSettled" }] : /* istanbul ignore next */ []));
3821
3871
  this.submitted$ = this.eventBus.on('submit').pipe(filter(() => {
3822
3872
  if (!this.valid()) {
3823
3873
  this.logger.debug('Form submitted while invalid, not emitting to (submitted) output');
@@ -3894,7 +3944,7 @@ class FormStateManager {
3894
3944
  this.deps.value.set(emptyValue);
3895
3945
  }
3896
3946
  submit() {
3897
- this.eventBus.dispatch(SubmitEvent);
3947
+ this.eventBus.dispatch(FormSubmitEvent);
3898
3948
  }
3899
3949
  setupEffects() {
3900
3950
  explicitEffect([this.deps.config], ([config]) => {
@@ -4038,17 +4088,115 @@ class FormStateManager {
4038
4088
  ngOnDestroy() {
4039
4089
  this.machine.dispatch({ type: Action.Destroy });
4040
4090
  }
4041
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FormStateManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4042
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FormStateManager });
4091
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4092
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager });
4043
4093
  }
4044
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FormStateManager, decorators: [{
4094
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager, decorators: [{
4045
4095
  type: Injectable
4046
4096
  }], ctorParameters: () => [] });
4047
4097
 
4098
+ function traverseFieldsWithContext(fields, context, visitor, hooks) {
4099
+ for (const field of fields) {
4100
+ visitor(field, context);
4101
+ if (!hasChildFields(field))
4102
+ continue;
4103
+ if (field.type === 'array') {
4104
+ const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
4105
+ const arrayChildren = collectArrayChildren(field);
4106
+ traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
4107
+ continue;
4108
+ }
4109
+ const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
4110
+ const childContext = mergeContext(context, overrides);
4111
+ traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
4112
+ }
4113
+ }
4114
+ function someField(fields, predicate) {
4115
+ return fields.some((field) => {
4116
+ if (predicate(field))
4117
+ return true;
4118
+ if (!hasChildFields(field))
4119
+ return false;
4120
+ const children = field.type === 'array' ? collectArrayChildren(field) : normalizeFieldsArray(field.fields);
4121
+ return someField(children, predicate);
4122
+ });
4123
+ }
4124
+ function collectArrayChildren(field) {
4125
+ let arrayItems = normalizeFieldsArray(field.fields);
4126
+ if (arrayItems.length === 0) {
4127
+ const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
4128
+ if (metadataTemplate) {
4129
+ arrayItems = [
4130
+ Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
4131
+ ];
4132
+ }
4133
+ }
4134
+ const flattened = [];
4135
+ for (const item of arrayItems) {
4136
+ if (Array.isArray(item)) {
4137
+ flattened.push(...item);
4138
+ }
4139
+ else {
4140
+ flattened.push(item);
4141
+ }
4142
+ }
4143
+ return flattened;
4144
+ }
4145
+ function mergeContext(base, overrides) {
4146
+ return overrides ? { ...base, ...overrides } : { ...base };
4147
+ }
4148
+
4149
+ function configHasDerivations(fields) {
4150
+ return someField(fields, (field) => {
4151
+ if (!field.key)
4152
+ return false;
4153
+ const validationField = field;
4154
+ return !!validationField.derivation || !!validationField.logic?.some(isDerivationLogicConfig);
4155
+ });
4156
+ }
4157
+
4158
+ const DERIVATION_RENDER_GATE = new InjectionToken('DERIVATION_RENDER_GATE');
4159
+ function createDerivationRenderGate() {
4160
+ const stateManager = inject(FormStateManager);
4161
+ const injector = inject(Injector);
4162
+ const pendingTasks = inject(PendingTasks);
4163
+ const logger = inject(DynamicFormLogger);
4164
+ const hasDerivations = computed(() => {
4165
+ const setup = stateManager.formSetup();
4166
+ return !!setup && configHasDerivations(setup.schemaFields);
4167
+ }, /* @ts-ignore */
4168
+ ...(ngDevMode ? [{ debugName: "hasDerivations" }] : /* istanbul ignore next */ []));
4169
+ const engine = signal('idle', /* @ts-ignore */
4170
+ ...(ngDevMode ? [{ debugName: "engine" }] : /* istanbul ignore next */ []));
4171
+ explicitEffect([hasDerivations], ([has]) => {
4172
+ if (!has || engine() !== 'idle')
4173
+ return;
4174
+ engine.set('loading');
4175
+ pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-C8kpF0tw.mjs')
4176
+ .then(({ bootstrapDerivationOrchestrator }) => {
4177
+ runInInjectionContext(injector, () => bootstrapDerivationOrchestrator());
4178
+ engine.set('wired');
4179
+ })
4180
+ .catch((error) => {
4181
+ engine.set('failed');
4182
+ logger.error('[Dynamic Forms] Failed to load the derivation engine; derivations will not apply.', error);
4183
+ }));
4184
+ });
4185
+ return computed(() => {
4186
+ if (!hasDerivations())
4187
+ return true;
4188
+ const state = engine();
4189
+ return state === 'wired' || state === 'failed';
4190
+ });
4191
+ }
4192
+
4193
+ const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
4194
+
4048
4195
  const AUTO_ID_PREFIX = 'df-';
4049
4196
  class DynamicFormInstanceRegistry {
4050
4197
  slots = new Map();
4051
- _membership = signal(0, ...(ngDevMode ? [{ debugName: "_membership" }] : /* istanbul ignore next */ []));
4198
+ _membership = signal(0, /* @ts-ignore */
4199
+ ...(ngDevMode ? [{ debugName: "_membership" }] : /* istanbul ignore next */ []));
4052
4200
  multiplePresent = computed(() => {
4053
4201
  this._membership();
4054
4202
  let visible = 0;
@@ -4060,7 +4208,8 @@ class DynamicFormInstanceRegistry {
4060
4208
  }
4061
4209
  }
4062
4210
  return false;
4063
- }, ...(ngDevMode ? [{ debugName: "multiplePresent" }] : /* istanbul ignore next */ []));
4211
+ }, /* @ts-ignore */
4212
+ ...(ngDevMode ? [{ debugName: "multiplePresent" }] : /* istanbul ignore next */ []));
4064
4213
  register(visible) {
4065
4214
  let slot = 1;
4066
4215
  while (this.slots.has(slot))
@@ -4073,10 +4222,10 @@ class DynamicFormInstanceRegistry {
4073
4222
  this.slots.delete(Number(id.slice(AUTO_ID_PREFIX.length)));
4074
4223
  this._membership.update((m) => m + 1);
4075
4224
  }
4076
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormInstanceRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4077
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormInstanceRegistry, providedIn: 'root' });
4225
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4226
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, providedIn: 'root' });
4078
4227
  }
4079
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormInstanceRegistry, decorators: [{
4228
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, decorators: [{
4080
4229
  type: Injectable,
4081
4230
  args: [{ providedIn: 'root' }]
4082
4231
  }] });
@@ -4098,15 +4247,18 @@ class FormIdPrefixService {
4098
4247
  options = inject(FORM_OPTIONS, { optional: true });
4099
4248
  logger = inject(DynamicFormLogger, { optional: true });
4100
4249
  host = inject((ElementRef)).nativeElement;
4101
- visible = signal(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
4250
+ visible = signal(true, /* @ts-ignore */
4251
+ ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
4102
4252
  autoId = this.registry.register(this.visible);
4103
- rawPrefix = computed(() => this.options?.()?.idPrefix?.trim() || undefined, ...(ngDevMode ? [{ debugName: "rawPrefix" }] : /* istanbul ignore next */ []));
4253
+ rawPrefix = computed(() => this.options?.()?.idPrefix?.trim() || undefined, /* @ts-ignore */
4254
+ ...(ngDevMode ? [{ debugName: "rawPrefix" }] : /* istanbul ignore next */ []));
4104
4255
  prefix = computed(() => {
4105
4256
  const explicit = this.rawPrefix();
4106
4257
  if (explicit)
4107
4258
  return sanitizeIdPrefix(explicit);
4108
4259
  return this.registry.multiplePresent() ? this.autoId : '';
4109
- }, ...(ngDevMode ? [{ debugName: "prefix" }] : /* istanbul ignore next */ []));
4260
+ }, /* @ts-ignore */
4261
+ ...(ngDevMode ? [{ debugName: "prefix" }] : /* istanbul ignore next */ []));
4110
4262
  constructor() {
4111
4263
  const destroyRef = inject(DestroyRef);
4112
4264
  destroyRef.onDestroy(() => this.registry.unregister(this.autoId));
@@ -4124,10 +4276,10 @@ class FormIdPrefixService {
4124
4276
  });
4125
4277
  }
4126
4278
  }
4127
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FormIdPrefixService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4128
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FormIdPrefixService });
4279
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4280
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService });
4129
4281
  }
4130
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FormIdPrefixService, decorators: [{
4282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService, decorators: [{
4131
4283
  type: Injectable
4132
4284
  }], ctorParameters: () => [] });
4133
4285
 
@@ -4136,2588 +4288,146 @@ const DERIVATION_WARNING_TRACKER = new InjectionToken('DerivationWarningTracker'
4136
4288
  factory: createWarningTracker,
4137
4289
  });
4138
4290
 
4139
- function parseArrayPath(path) {
4140
- const ARRAY_PLACEHOLDER = '.$.';
4141
- const placeholderIndex = path.indexOf(ARRAY_PLACEHOLDER);
4142
- if (placeholderIndex === -1) {
4143
- return {
4144
- arrayPath: '',
4145
- relativePath: '',
4146
- isArrayPath: false,
4147
- };
4148
- }
4149
- return {
4150
- arrayPath: path.substring(0, placeholderIndex),
4151
- relativePath: path.substring(placeholderIndex + ARRAY_PLACEHOLDER.length),
4152
- isArrayPath: true,
4153
- };
4154
- }
4155
- function resolveArrayPath(path, index) {
4156
- const info = parseArrayPath(path);
4157
- if (!info.isArrayPath) {
4158
- return path;
4159
- }
4160
- return `${info.arrayPath}.${index}.${info.relativePath}`;
4161
- }
4162
- function isArrayPlaceholderPath(path) {
4163
- return path.includes('.$.');
4164
- }
4165
- function extractArrayPath(path) {
4166
- return parseArrayPath(path).arrayPath;
4291
+ function coreProviders() {
4292
+ return [
4293
+ EventBus,
4294
+ { provide: CONTAINER_FIELD_PROCESSORS, useFactory: createContainerFieldProcessors },
4295
+ SchemaRegistryService,
4296
+ FunctionRegistryService,
4297
+ {
4298
+ provide: FORM_STATE_DEPS,
4299
+ useFactory: () => ({ config: null, formOptions: null, value: null, source: null }),
4300
+ },
4301
+ FormStateManager,
4302
+ {
4303
+ provide: RootFormRegistryService,
4304
+ useFactory: (stateManager) => new RootFormRegistryService(stateManager.formValue, computed(() => stateManager.form())),
4305
+ deps: [FormStateManager],
4306
+ },
4307
+ FieldContextRegistryService,
4308
+ ArrayItemRegistryService,
4309
+ {
4310
+ provide: DEFAULT_PROPS,
4311
+ useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultProps),
4312
+ deps: [FormStateManager],
4313
+ },
4314
+ {
4315
+ provide: DEFAULT_WRAPPERS,
4316
+ useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultWrappers),
4317
+ deps: [FormStateManager],
4318
+ },
4319
+ {
4320
+ provide: DEFAULT_VALIDATION_MESSAGES,
4321
+ useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultValidationMessages),
4322
+ deps: [FormStateManager],
4323
+ },
4324
+ {
4325
+ provide: FORM_OPTIONS,
4326
+ useFactory: (stateManager) => stateManager.effectiveFormOptions,
4327
+ deps: [FormStateManager],
4328
+ },
4329
+ {
4330
+ provide: EXTERNAL_DATA,
4331
+ useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.externalData),
4332
+ deps: [FormStateManager],
4333
+ },
4334
+ FormIdPrefixService,
4335
+ {
4336
+ provide: FORM_ID_PREFIX,
4337
+ useFactory: (svc) => svc.prefix,
4338
+ deps: [FormIdPrefixService],
4339
+ },
4340
+ { provide: DEPRECATION_WARNING_TRACKER, useFactory: createWarningTracker },
4341
+ { provide: ADDON_ACTION_REGISTRY, useFactory: createAddonActionRegistry },
4342
+ ];
4167
4343
  }
4168
- function splitPath(path) {
4169
- if (!path)
4170
- return [];
4171
- return path
4172
- .replace(/\[(\d+)\]/g, '.$1')
4173
- .split('.')
4174
- .filter(Boolean);
4175
- }
4176
- function joinPath(segments) {
4177
- return segments.join('.');
4178
- }
4179
- function getParentPath(path) {
4180
- const segments = splitPath(path);
4181
- if (segments.length <= 1)
4182
- return '';
4183
- return joinPath(segments.slice(0, -1));
4184
- }
4185
- function getLeafPath(path) {
4186
- const segments = splitPath(path);
4187
- return segments[segments.length - 1] ?? '';
4188
- }
4189
- function parseMultiArrayPath(path) {
4190
- const parts = path.split('.');
4191
- const segments = [];
4192
- const placeholderPositions = [];
4193
- for (let i = 0; i < parts.length; i++) {
4194
- if (parts[i] === '$') {
4195
- placeholderPositions.push(i);
4196
- }
4197
- else {
4198
- segments.push(parts[i]);
4199
- }
4200
- }
4201
- return {
4202
- isArrayPath: placeholderPositions.length > 0,
4203
- placeholderCount: placeholderPositions.length,
4204
- segments,
4205
- placeholderPositions,
4206
- };
4344
+ function derivationProviders() {
4345
+ return [
4346
+ { provide: DERIVATION_WARNING_TRACKER, useFactory: createWarningTracker },
4347
+ { provide: DERIVATION_RENDER_GATE, useFactory: createDerivationRenderGate },
4348
+ ];
4207
4349
  }
4208
- function resolveMultiArrayPath(path, indices) {
4209
- const parts = path.split('.');
4210
- let indexPointer = 0;
4211
- const resolved = parts.map((part) => {
4212
- if (part === '$') {
4213
- if (indexPointer >= indices.length) {
4214
- throw new Error(`Not enough indices provided for path '${path}'. ` +
4215
- `Expected ${parts.filter((p) => p === '$').length} indices, got ${indices.length}.`);
4216
- }
4217
- return String(indices[indexPointer++]);
4218
- }
4219
- return part;
4220
- });
4221
- if (indexPointer < indices.length) {
4222
- throw new Error(`Too many indices provided for path '${path}'. ` + `Expected ${indexPointer} indices, got ${indices.length}.`);
4223
- }
4224
- return resolved.join('.');
4350
+ function propertyDerivationProviders() {
4351
+ return [{ provide: PROPERTY_OVERRIDE_STORE, useFactory: createPropertyOverrideStore }];
4225
4352
  }
4226
- function countArrayPlaceholders(path) {
4227
- return path.split('.').filter((part) => part === '$').length;
4353
+ function httpExpressionProviders() {
4354
+ return [{ provide: HTTP_CONDITION_CACHE, useFactory: () => new HttpConditionCache(100) }, HttpConditionFunctionCacheService];
4228
4355
  }
4229
-
4230
- function createEmptyDerivationCollection() {
4231
- return { entries: [] };
4356
+ function asyncExpressionProviders() {
4357
+ return [AsyncConditionFunctionCacheService];
4232
4358
  }
4233
- function createDerivationChainContext() {
4234
- return {
4235
- appliedDerivations: new Set(),
4236
- iteration: 0,
4237
- };
4359
+ function dynamicValueProviders() {
4360
+ return [DynamicValueFunctionCacheService];
4238
4361
  }
4239
- function createDerivationKey(fieldKey) {
4240
- return fieldKey;
4362
+ function logicProviders() {
4363
+ return [LogicFunctionCacheService];
4241
4364
  }
4242
- function parseDerivationKey(key) {
4243
- return { fieldKey: key };
4365
+ function provideDynamicFormDI() {
4366
+ return [
4367
+ ...coreProviders(),
4368
+ ...logicProviders(),
4369
+ ...dynamicValueProviders(),
4370
+ ...httpExpressionProviders(),
4371
+ ...asyncExpressionProviders(),
4372
+ ...propertyDerivationProviders(),
4373
+ ...derivationProviders(),
4374
+ ];
4244
4375
  }
4245
4376
 
4246
- function isThenableOrObservable(value) {
4247
- if (value === null || typeof value !== 'object')
4248
- return false;
4249
- const candidate = value;
4250
- return typeof candidate.then === 'function' || typeof candidate.subscribe === 'function';
4251
- }
4252
- function computeValueFromEntry(entry, evalContext, options) {
4253
- if (entry.value !== undefined) {
4254
- return entry.value;
4377
+ class EventDispatcher {
4378
+ bus;
4379
+ dispatch(event) {
4380
+ this.bus?.emitInstance(event);
4255
4381
  }
4256
- if (entry.expression) {
4257
- return ExpressionParser.evaluate(entry.expression, evalContext);
4382
+ connect(bus) {
4383
+ this.bus = bus;
4258
4384
  }
4259
- if (entry.fn || entry.functionName) {
4260
- if (entry.fn && entry.functionName) {
4261
- evalContext.logger.warn(`Both "fn" and "functionName" are set on derivation for '${options.subject}'. Inline "fn" takes precedence; "functionName" is ignored.`);
4262
- }
4263
- const fn = entry.fn ?? (entry.functionName ? options.derivationFunctions?.[entry.functionName] : undefined);
4264
- if (!fn) {
4265
- const kind = options.functionKind ?? 'Derivation function';
4266
- throw new DynamicFormError(`${kind} '${entry.functionName}' not found in customFnConfig.derivations`);
4267
- }
4268
- const result = fn(evalContext);
4269
- if (isThenableOrObservable(result)) {
4270
- evalContext.logger.warn(`Derivation for '${options.subject}' returned a Promise or Observable. ` +
4271
- `Synchronous derivations must return a value; use 'asyncDerivations' or an HTTP derivation for asynchronous logic. Skipping.`);
4272
- return undefined;
4273
- }
4274
- return result;
4385
+ disconnect() {
4386
+ this.bus = undefined;
4275
4387
  }
4276
- throw new DynamicFormError(`Derivation for ${options.subject} has no value source. Specify 'value', 'expression', 'functionName', or 'fn'.`);
4388
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4389
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher });
4277
4390
  }
4391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher, decorators: [{
4392
+ type: Injectable
4393
+ }] });
4278
4394
 
4279
- function getParentPathInScope(fieldKey) {
4280
- const lastDot = fieldKey.lastIndexOf('.');
4281
- if (lastDot <= 0)
4282
- return undefined;
4283
- return fieldKey.slice(0, lastDot);
4284
- }
4285
- function resolveArrayItemScope(pathInfo, arrayItem) {
4286
- const relativePath = pathInfo.isArrayPath ? pathInfo.relativePath : '';
4287
- const innerParentPath = relativePath.includes('.') ? relativePath.slice(0, relativePath.lastIndexOf('.')) : '';
4288
- const groupValue = innerParentPath ? getNestedValue(arrayItem, innerParentPath) : arrayItem;
4289
- const fieldValue = relativePath ? getNestedValue(arrayItem, relativePath) : arrayItem;
4290
- return { relativePath, groupValue, fieldValue };
4291
- }
4292
-
4293
- const MAX_DERIVATION_ITERATIONS = 10;
4294
- const ARRAY_PLACEHOLDER = '.$.';
4295
- const MAX_AST_CACHE_SIZE = 1000;
4296
- const DERIVATION_KEY_DELIMITER = '\x00';
4297
-
4298
- function resolveFieldInstance(rootForm, fieldPath) {
4299
- const parts = fieldPath.split('.');
4300
- let current = rootForm;
4301
- for (let i = 0; i < parts.length - 1; i++) {
4302
- const next = current[parts[i]];
4303
- if (!next)
4304
- return undefined;
4305
- current = next;
4306
- }
4307
- const fieldAccessor = current[parts[parts.length - 1]];
4308
- if (!fieldAccessor)
4309
- return undefined;
4310
- return untracked(fieldAccessor);
4311
- }
4312
- function readFieldDirty(rootForm, fieldPath) {
4313
- const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
4314
- if (!fieldInstance)
4315
- return undefined;
4316
- return untracked(fieldInstance.dirty);
4317
- }
4318
- function resetFieldState(rootForm, fieldPath) {
4319
- const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
4320
- if (!fieldInstance)
4321
- return;
4322
- fieldInstance.reset();
4323
- }
4324
- function applyValueToForm(targetPath, value, rootForm, logger, warningTracker) {
4325
- if (!targetPath.includes('.')) {
4326
- return setFieldValue(rootForm, targetPath, value, logger, warningTracker);
4327
- }
4328
- const parts = targetPath.split('.');
4329
- let current = rootForm;
4330
- for (let i = 0; i < parts.length - 1; i++) {
4331
- const part = parts[i];
4332
- if (part === '$') {
4333
- return false;
4334
- }
4335
- const next = current[part];
4336
- if (!next) {
4337
- warnMissingField(targetPath, logger, warningTracker);
4338
- return false; }
4339
- current = next;
4340
- }
4341
- const finalPart = parts[parts.length - 1];
4342
- return setFieldValue(current, finalPart, value, logger, warningTracker);
4343
- }
4344
- function setFieldValue(parent, fieldKey, value, logger, warningTracker) {
4345
- const fieldAccessor = parent[fieldKey];
4346
- if (!fieldAccessor) {
4347
- warnMissingField(fieldKey, logger, warningTracker);
4348
- return false;
4349
- }
4350
- const fieldInstance = untracked(fieldAccessor);
4351
- if (!fieldInstance || typeof fieldInstance !== 'object') {
4352
- warnMissingField(fieldKey, logger, warningTracker);
4353
- return false;
4354
- }
4355
- const valueSignal = fieldInstance.value;
4356
- if (isWritableSignal(valueSignal)) {
4357
- valueSignal.set(value);
4358
- return true;
4359
- }
4360
- else {
4361
- warnMissingField(fieldKey, logger, warningTracker);
4362
- return false;
4363
- }
4395
+ class DfTemplate {
4396
+ dfTemplate = input.required(/* @ts-ignore */
4397
+ ...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
4398
+ templateRef = inject(TemplateRef);
4399
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4400
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
4364
4401
  }
4365
- function warnMissingField(fieldKey, logger, warningTracker) {
4366
- if (warningTracker) {
4367
- if (warningTracker.warnedKeys.has(fieldKey)) {
4368
- return;
4369
- }
4370
- warningTracker.warnedKeys.add(fieldKey);
4402
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfTemplate, decorators: [{
4403
+ type: Directive,
4404
+ args: [{
4405
+ selector: 'ng-template[dfTemplate]',
4406
+ exportAs: 'dfTemplate',
4407
+ }]
4408
+ }], propDecorators: { dfTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfTemplate", required: true }] }] } });
4409
+ class DfFieldTemplateRegistry {
4410
+ _map = signal(new Map(), /* @ts-ignore */
4411
+ ...(ngDevMode ? [{ debugName: "_map" }] : /* istanbul ignore next */ []));
4412
+ map = this._map.asReadonly();
4413
+ set(value) {
4414
+ this._map.set(value);
4371
4415
  }
4372
- logger?.warn(`Derivation: target field '${fieldKey}' not found in form. ` +
4373
- `Ensure the field is defined in your form configuration. ` +
4374
- `This warning is shown once per field.`);
4416
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4417
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry });
4375
4418
  }
4376
-
4377
- function applyDerivations(collection, context, changedFields) {
4378
- const chainContext = createDerivationChainContext();
4379
- chainContext.changedFields = changedFields;
4380
- chainContext.formFieldState = createFormFieldStateMap(context.rootForm, false);
4381
- const { derivationLogger } = context;
4382
- const maxIterations = context.maxIterations ?? MAX_DERIVATION_ITERATIONS;
4383
- let appliedCount = 0;
4384
- let skippedCount = 0;
4385
- let errorCount = 0;
4386
- let warnCount = 0;
4387
- const entriesToProcess = changedFields ? getEntriesForChangedFields$1(collection.entries, changedFields) : collection.entries;
4388
- derivationLogger.cycleStart('onChange', entriesToProcess.length);
4389
- let hasChanges = true;
4390
- while (hasChanges && chainContext.iteration < maxIterations) {
4391
- chainContext.iteration++;
4392
- hasChanges = false;
4393
- derivationLogger.iteration(chainContext.iteration);
4394
- for (const entry of entriesToProcess) {
4395
- const result = tryApplyDerivation(entry, context, chainContext);
4396
- if (result.applied) {
4397
- appliedCount++;
4398
- hasChanges = true;
4399
- }
4400
- else if (result.error) {
4401
- errorCount++;
4402
- }
4403
- else if (result.warning) {
4404
- warnCount++;
4405
- skippedCount++;
4406
- }
4407
- else {
4408
- skippedCount++;
4409
- }
4410
- }
4411
- }
4412
- const processingResult = {
4413
- appliedCount,
4414
- skippedCount,
4415
- errorCount,
4416
- warnCount,
4417
- iterations: chainContext.iteration,
4418
- maxIterationsReached: chainContext.iteration >= maxIterations,
4419
- };
4420
- if (processingResult.maxIterationsReached) {
4421
- derivationLogger.maxIterationsReached(processingResult, 'onChange');
4422
- }
4423
- derivationLogger.summary(processingResult, 'onChange');
4424
- return processingResult;
4425
- }
4426
- function getEntriesForChangedFields$1(entries, changedFields) {
4427
- return entries.filter((entry) => {
4428
- if (entry.dependsOn.length === 0 || entry.dependsOn.includes('*')) {
4429
- return true;
4430
- }
4431
- if (entry.dependsOn.some((dep) => changedFields.has(dep))) {
4432
- return true;
4433
- }
4434
- for (const changed of changedFields) {
4435
- if (entry.fieldKey.startsWith(changed + '.'))
4436
- return true;
4437
- if (entry.dependsOn.some((dep) => dep.startsWith(changed + '.')))
4438
- return true;
4439
- }
4440
- return false;
4441
- });
4442
- }
4443
- function tryApplyDerivation(entry, context, chainContext) {
4444
- const { derivationLogger } = context;
4445
- if (entry.http || entry.asyncFunctionName || entry.asyncFn) {
4446
- return { applied: false, fieldKey: entry.fieldKey };
4447
- }
4448
- if (isArrayPlaceholderPath(entry.fieldKey)) {
4449
- return tryApplyArrayDerivation(entry, context, chainContext);
4450
- }
4451
- const derivationKey = createDerivationKey(entry.fieldKey);
4452
- if (chainContext.appliedDerivations.has(derivationKey)) {
4453
- derivationLogger.evaluation({
4454
- debugName: entry.debugName,
4455
- fieldKey: entry.fieldKey,
4456
- result: 'skipped',
4457
- skipReason: 'already-applied',
4458
- });
4459
- return { applied: false, fieldKey: entry.fieldKey };
4460
- }
4461
- if (shouldSkipForUserOverride(entry, entry.fieldKey, context, chainContext)) {
4462
- derivationLogger.evaluation({
4463
- debugName: entry.debugName,
4464
- fieldKey: entry.fieldKey,
4465
- result: 'skipped',
4466
- skipReason: 'user-override',
4467
- });
4468
- return { applied: false, fieldKey: entry.fieldKey };
4469
- }
4470
- const formValue = untracked(() => context.formValue());
4471
- const evalContext = createEvaluationContext$1(entry, formValue, context, chainContext);
4472
- if (!evaluateDerivationCondition(entry.condition, evalContext)) {
4473
- derivationLogger.evaluation({
4474
- debugName: entry.debugName,
4475
- fieldKey: entry.fieldKey,
4476
- result: 'skipped',
4477
- skipReason: 'condition-false',
4478
- });
4479
- return { applied: false, fieldKey: entry.fieldKey };
4480
- }
4481
- let newValue;
4482
- try {
4483
- newValue = computeDerivedValue(entry, evalContext, context);
4484
- }
4485
- catch (error) {
4486
- const errorMessage = error instanceof Error ? error.message : String(error);
4487
- context.logger.error(formatDerivationError(entry, 'compute', errorMessage));
4488
- derivationLogger.evaluation({
4489
- debugName: entry.debugName,
4490
- fieldKey: entry.fieldKey,
4491
- result: 'error',
4492
- error: errorMessage,
4493
- });
4494
- return {
4495
- applied: false,
4496
- fieldKey: entry.fieldKey,
4497
- error: errorMessage,
4498
- };
4499
- }
4500
- const currentValue = getNestedValue(formValue, entry.fieldKey);
4501
- if (isEqual(currentValue, newValue)) {
4502
- derivationLogger.evaluation({
4503
- debugName: entry.debugName,
4504
- fieldKey: entry.fieldKey,
4505
- result: 'skipped',
4506
- skipReason: 'value-unchanged',
4507
- });
4508
- return { applied: false, fieldKey: entry.fieldKey };
4509
- }
4510
- try {
4511
- const wasApplied = applyValueToForm(entry.fieldKey, newValue, context.rootForm, context.logger, context.warningTracker);
4512
- if (wasApplied) {
4513
- chainContext.appliedDerivations.add(derivationKey);
4514
- derivationLogger.evaluation({
4515
- debugName: entry.debugName,
4516
- fieldKey: entry.fieldKey,
4517
- result: 'applied',
4518
- previousValue: currentValue,
4519
- newValue,
4520
- });
4521
- return { applied: true, fieldKey: entry.fieldKey, newValue };
4522
- }
4523
- else {
4524
- derivationLogger.evaluation({
4525
- debugName: entry.debugName,
4526
- fieldKey: entry.fieldKey,
4527
- result: 'skipped',
4528
- skipReason: 'target-not-found',
4529
- });
4530
- return {
4531
- applied: false,
4532
- fieldKey: entry.fieldKey,
4533
- warning: `Field '${entry.fieldKey}' not found`,
4534
- };
4535
- }
4536
- }
4537
- catch (error) {
4538
- const errorMessage = error instanceof Error ? error.message : String(error);
4539
- context.logger.error(formatDerivationError(entry, 'apply', errorMessage));
4540
- derivationLogger.evaluation({
4541
- debugName: entry.debugName,
4542
- fieldKey: entry.fieldKey,
4543
- result: 'error',
4544
- error: errorMessage,
4545
- });
4546
- return {
4547
- applied: false,
4548
- fieldKey: entry.fieldKey,
4549
- error: errorMessage,
4550
- };
4551
- }
4552
- }
4553
- function tryApplyArrayDerivation(entry, context, chainContext) {
4554
- const formValue = untracked(() => context.formValue());
4555
- const pathInfo = parseArrayPath(entry.fieldKey);
4556
- if (!pathInfo.isArrayPath) {
4557
- return { applied: false, fieldKey: entry.fieldKey, error: 'Invalid array derivation path' };
4558
- }
4559
- const { arrayPath } = pathInfo;
4560
- const arrayValue = getNestedValue(formValue, arrayPath);
4561
- if (!Array.isArray(arrayValue)) {
4562
- return { applied: false, fieldKey: entry.fieldKey };
4563
- }
4564
- let appliedAny = false;
4565
- for (let i = 0; i < arrayValue.length; i++) {
4566
- const resolvedPath = resolveArrayPath(entry.fieldKey, i);
4567
- const derivationKey = createDerivationKey(resolvedPath);
4568
- if (chainContext.appliedDerivations.has(derivationKey)) {
4569
- continue;
4570
- }
4571
- if (shouldSkipForUserOverride(entry, resolvedPath, context, chainContext)) {
4572
- context.derivationLogger.evaluation({
4573
- debugName: entry.debugName,
4574
- fieldKey: resolvedPath,
4575
- result: 'skipped',
4576
- skipReason: 'user-override',
4577
- });
4578
- continue;
4579
- }
4580
- const arrayItem = arrayValue[i];
4581
- const evalContext = createArrayItemEvaluationContext$1(entry, arrayItem, formValue, i, arrayPath, context, chainContext);
4582
- if (!evaluateDerivationCondition(entry.condition, evalContext)) {
4583
- continue;
4584
- }
4585
- let newValue;
4586
- try {
4587
- newValue = computeDerivedValue(entry, evalContext, context);
4588
- }
4589
- catch (error) {
4590
- const errorMessage = error instanceof Error ? error.message : String(error);
4591
- context.logger.error(formatDerivationError(entry, 'compute', `[index ${i}] ${errorMessage}`));
4592
- continue;
4593
- }
4594
- const currentValue = getNestedValue(formValue, resolvedPath);
4595
- if (isEqual(currentValue, newValue)) {
4596
- continue;
4597
- }
4598
- try {
4599
- applyValueToForm(resolvedPath, newValue, context.rootForm, context.logger, context.warningTracker);
4600
- chainContext.appliedDerivations.add(derivationKey);
4601
- appliedAny = true;
4602
- }
4603
- catch (error) {
4604
- const errorMessage = error instanceof Error ? error.message : String(error);
4605
- context.logger.error(formatDerivationError(entry, 'apply', `[index ${i}] ${errorMessage}`));
4606
- }
4607
- }
4608
- return { applied: appliedAny, fieldKey: entry.fieldKey };
4609
- }
4610
- function createEvaluationContext$1(entry, formValue, context, chainContext) {
4611
- const fieldValue = getNestedValue(formValue, entry.fieldKey);
4612
- const fieldAccessor = context.rootForm[entry.fieldKey];
4613
- const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
4614
- const parentPath = getParentPathInScope(entry.fieldKey);
4615
- const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
4616
- return {
4617
- fieldValue,
4618
- formValue,
4619
- fieldPath: entry.fieldKey,
4620
- groupValue,
4621
- customFunctions: context.customFunctions,
4622
- externalData: context.externalData,
4623
- logger: context.logger,
4624
- fieldState,
4625
- formFieldState: chainContext.formFieldState,
4626
- };
4627
- }
4628
- function createArrayItemEvaluationContext$1(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context, chainContext) {
4629
- const pathInfo = parseArrayPath(entry.fieldKey);
4630
- let fieldState;
4631
- if (pathInfo.isArrayPath && pathInfo.relativePath) {
4632
- const rootFormRecord = context.rootForm;
4633
- const arrayAccessor = rootFormRecord[arrayPath];
4634
- if (arrayAccessor) {
4635
- const arrayItems = arrayAccessor;
4636
- const itemAccessor = arrayItems[String(itemIndex)];
4637
- if (itemAccessor) {
4638
- const itemFields = itemAccessor;
4639
- const fieldAccessor = itemFields[pathInfo.relativePath];
4640
- if (fieldAccessor) {
4641
- fieldState = readFieldStateInfo(fieldAccessor, false);
4642
- }
4643
- }
4644
- }
4645
- }
4646
- const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
4647
- return {
4648
- fieldValue,
4649
- formValue: arrayItem,
4650
- fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
4651
- groupValue,
4652
- customFunctions: context.customFunctions,
4653
- externalData: context.externalData,
4654
- logger: context.logger,
4655
- rootFormValue,
4656
- arrayIndex: itemIndex,
4657
- arrayPath,
4658
- fieldState,
4659
- formFieldState: chainContext.formFieldState,
4660
- };
4661
- }
4662
- function evaluateDerivationCondition(condition, context) {
4663
- if (typeof condition === 'boolean') {
4664
- return condition;
4665
- }
4666
- return evaluateCondition(condition, context);
4667
- }
4668
- function computeDerivedValue(entry, evalContext, applicatorContext) {
4669
- return computeValueFromEntry(entry, evalContext, {
4670
- derivationFunctions: applicatorContext.derivationFunctions,
4671
- subject: entry.fieldKey,
4672
- });
4673
- }
4674
- const ERROR_PREFIX$1 = '[Derivation]';
4675
- function formatDerivationError(entry, phase, message) {
4676
- const source = entry.expression
4677
- ? `expression: "${entry.expression.substring(0, 50)}${entry.expression.length > 50 ? '...' : ''}"`
4678
- : entry.functionName
4679
- ? `function: "${entry.functionName}"`
4680
- : entry.fn
4681
- ? 'inline fn'
4682
- : entry.value !== undefined
4683
- ? 'static value'
4684
- : 'unknown source';
4685
- return `${ERROR_PREFIX$1} Failed to ${phase} derivation (field: ${entry.fieldKey}, ${source}): ${message}`;
4686
- }
4687
- function hasDependencyChanged(entry, changedFields) {
4688
- return entry.dependsOn.some((dep) => dep === '*' || changedFields.has(dep));
4689
- }
4690
- function shouldSkipForUserOverride(entry, resolvedFieldKey, context, chainContext) {
4691
- if (!entry.stopOnUserOverride)
4692
- return false;
4693
- const isDirty = readFieldDirty(context.rootForm, resolvedFieldKey) ?? false;
4694
- if (!isDirty)
4695
- return false;
4696
- if (entry.reEngageOnDependencyChange && chainContext.changedFields) {
4697
- if (hasDependencyChanged(entry, chainContext.changedFields)) {
4698
- resetFieldState(context.rootForm, resolvedFieldKey);
4699
- return false;
4700
- }
4701
- }
4702
- return true;
4703
- }
4704
- function applyDerivationsForTrigger(collection, trigger, context, changedFields) {
4705
- const filteredEntries = collection.entries.filter((entry) => {
4706
- if (entry.http || entry.asyncFunctionName || entry.asyncFn)
4707
- return false;
4708
- if (trigger === 'onChange') {
4709
- return !entry.trigger || entry.trigger === 'onChange';
4710
- }
4711
- return entry.trigger === trigger;
4712
- });
4713
- const filteredCollection = {
4714
- entries: filteredEntries,
4715
- };
4716
- return applyDerivations(filteredCollection, context, changedFields);
4717
- }
4718
- function getDebouncedDerivationEntries(collection) {
4719
- return collection.entries.filter((entry) => entry.trigger === 'debounced');
4720
- }
4721
-
4722
- function topologicalSort(entries) {
4723
- if (entries.length <= 1) {
4724
- return [...entries];
4725
- }
4726
- const graph = new Map();
4727
- const inDegree = new Map();
4728
- for (const entry of entries) {
4729
- graph.set(entry, new Set());
4730
- inDegree.set(entry, 0);
4731
- }
4732
- const derivationsByField = new Map();
4733
- for (const entry of entries) {
4734
- const fieldEntries = derivationsByField.get(entry.fieldKey) ?? [];
4735
- fieldEntries.push(entry);
4736
- derivationsByField.set(entry.fieldKey, fieldEntries);
4737
- if (entry.fieldKey.includes('.$.')) {
4738
- const relativePath = entry.fieldKey.split('.$.')[1];
4739
- if (relativePath) {
4740
- const relativeEntries = derivationsByField.get(relativePath) ?? [];
4741
- relativeEntries.push(entry);
4742
- derivationsByField.set(relativePath, relativeEntries);
4743
- }
4744
- }
4745
- }
4746
- const wildcardDependents = [];
4747
- for (const entry of entries) {
4748
- if (entry.dependsOn.includes('*')) {
4749
- wildcardDependents.push(entry);
4750
- }
4751
- }
4752
- for (const entryB of entries) {
4753
- for (const dep of entryB.dependsOn) {
4754
- if (dep === '*')
4755
- continue;
4756
- const producers = derivationsByField.get(dep);
4757
- if (producers) {
4758
- for (const entryA of producers) {
4759
- const entryAEdges = graph.get(entryA);
4760
- if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
4761
- entryAEdges.add(entryB);
4762
- inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
4763
- }
4764
- }
4765
- }
4766
- }
4767
- }
4768
- for (const entryB of wildcardDependents) {
4769
- for (const entryA of entries) {
4770
- const entryAEdges = graph.get(entryA);
4771
- if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
4772
- entryAEdges.add(entryB);
4773
- inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
4774
- }
4775
- }
4776
- }
4777
- const queue = [];
4778
- const sorted = [];
4779
- for (const entry of entries) {
4780
- if (inDegree.get(entry) === 0) {
4781
- queue.push(entry);
4782
- }
4783
- }
4784
- while (queue.length > 0) {
4785
- const current = queue.shift();
4786
- if (!current)
4787
- break; sorted.push(current);
4788
- const neighbors = graph.get(current) ?? new Set();
4789
- for (const neighbor of neighbors) {
4790
- const newDegree = (inDegree.get(neighbor) ?? 0) - 1;
4791
- inDegree.set(neighbor, newDegree);
4792
- if (newDegree === 0) {
4793
- queue.push(neighbor);
4794
- }
4795
- }
4796
- }
4797
- if (sorted.length !== entries.length) {
4798
- const sortedSet = new Set(sorted);
4799
- for (const entry of entries) {
4800
- if (!sortedSet.has(entry)) {
4801
- sorted.push(entry);
4802
- }
4803
- }
4804
- }
4805
- return sorted;
4806
- }
4807
-
4808
- function extractDependenciesFromConfig(config) {
4809
- const deps = new Set();
4810
- if (config.dependsOn && config.dependsOn.length > 0) {
4811
- config.dependsOn.forEach((dep) => deps.add(dep));
4812
- }
4813
- else {
4814
- if (config.expression) {
4815
- extractStringDependencies(config.expression).forEach((dep) => deps.add(dep));
4816
- }
4817
- if (config.functionName || config.fn) {
4818
- deps.add('*');
4819
- }
4820
- }
4821
- if (config.condition && config.condition !== true) {
4822
- extractExpressionDependencies(config.condition).forEach((dep) => deps.add(dep));
4823
- }
4824
- return Array.from(deps);
4825
- }
4826
-
4827
- function extractInlineFn(config) {
4828
- return config.fn;
4829
- }
4830
- function extractInlineAsyncFn(config) {
4831
- return config.asyncFn;
4832
- }
4833
-
4834
- function traverseFieldsWithContext(fields, context, visitor, hooks) {
4835
- for (const field of fields) {
4836
- visitor(field, context);
4837
- if (!hasChildFields(field))
4838
- continue;
4839
- if (field.type === 'array') {
4840
- const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
4841
- const arrayChildren = collectArrayChildren(field);
4842
- traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
4843
- continue;
4844
- }
4845
- const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
4846
- const childContext = mergeContext(context, overrides);
4847
- traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
4848
- }
4849
- }
4850
- function collectArrayChildren(field) {
4851
- let arrayItems = normalizeFieldsArray(field.fields);
4852
- if (arrayItems.length === 0) {
4853
- const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
4854
- if (metadataTemplate) {
4855
- arrayItems = [
4856
- Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
4857
- ];
4858
- }
4859
- }
4860
- const flattened = [];
4861
- for (const item of arrayItems) {
4862
- if (Array.isArray(item)) {
4863
- flattened.push(...item);
4864
- }
4865
- else {
4866
- flattened.push(item);
4867
- }
4868
- }
4869
- return flattened;
4870
- }
4871
- function mergeContext(base, overrides) {
4872
- return overrides ? { ...base, ...overrides } : { ...base };
4873
- }
4874
-
4875
- const SELF_DEPENDENCY_TOKEN = '$self';
4876
- const GROUP_DEPENDENCY_TOKEN = '$group';
4877
- function buildEffectiveFieldKey(fieldKey, context) {
4878
- if (context.arrayPath && context.groupPath) {
4879
- return `${context.arrayPath}.$.${context.groupPath}.${fieldKey}`;
4880
- }
4881
- if (context.arrayPath) {
4882
- return `${context.arrayPath}.$.${fieldKey}`;
4883
- }
4884
- if (context.groupPath) {
4885
- return `${context.groupPath}.${fieldKey}`;
4886
- }
4887
- return fieldKey;
4888
- }
4889
- function buildNearestGroupPath(context) {
4890
- if (context.arrayPath && context.groupPath) {
4891
- return `${context.arrayPath}.$.${context.groupPath}`;
4892
- }
4893
- return context.arrayPath ?? context.groupPath;
4894
- }
4895
- function resolveTokenDependencies(deps, effectiveFieldKey, context) {
4896
- return deps.map((dep) => {
4897
- if (dep === SELF_DEPENDENCY_TOKEN) {
4898
- return effectiveFieldKey;
4899
- }
4900
- if (dep.startsWith(`${SELF_DEPENDENCY_TOKEN}.`)) {
4901
- return effectiveFieldKey + dep.slice(SELF_DEPENDENCY_TOKEN.length);
4902
- }
4903
- if (dep === GROUP_DEPENDENCY_TOKEN || dep.startsWith(`${GROUP_DEPENDENCY_TOKEN}.`)) {
4904
- const groupPath = buildNearestGroupPath(context);
4905
- if (!groupPath) {
4906
- throw new DynamicFormError(`Derivation for '${effectiveFieldKey}' uses '${dep}' but the field has no parent group or array. ` +
4907
- `'${GROUP_DEPENDENCY_TOKEN}' is only valid for fields nested inside a group or array container.`);
4908
- }
4909
- if (dep === GROUP_DEPENDENCY_TOKEN)
4910
- return groupPath;
4911
- return groupPath + dep.slice(GROUP_DEPENDENCY_TOKEN.length);
4912
- }
4913
- return dep;
4914
- });
4915
- }
4916
- const CONTEXT_TRAVERSAL_OPTIONS = {
4917
- onArrayChild: (_parent, field) => ({ arrayPath: field.key, groupPath: undefined }),
4918
- onGroupChild: (parent, field) => {
4919
- if (!field.key)
4920
- return {};
4921
- return { groupPath: parent.groupPath ? `${parent.groupPath}.${field.key}` : field.key };
4922
- },
4923
- };
4924
-
4925
- function collectDerivations(fields) {
4926
- const entries = [];
4927
- traverseFieldsWithContext(fields, {}, (field, context) => collectValueEntriesFromField(field, entries, context), CONTEXT_TRAVERSAL_OPTIONS);
4928
- return { entries: topologicalSort(entries) };
4929
- }
4930
- function collectAllDerivations(fields) {
4931
- const valueEntries = [];
4932
- const propertyEntries = [];
4933
- traverseFieldsWithContext(fields, {}, (field, context) => {
4934
- collectValueEntriesFromField(field, valueEntries, context);
4935
- collectPropertyEntriesFromField(field, propertyEntries, context);
4936
- }, CONTEXT_TRAVERSAL_OPTIONS);
4937
- return {
4938
- value: { entries: topologicalSort(valueEntries) },
4939
- property: { entries: propertyEntries },
4940
- };
4941
- }
4942
- function collectValueEntriesFromField(field, entries, context) {
4943
- const fieldKey = field.key;
4944
- if (!fieldKey)
4945
- return;
4946
- const validationField = field;
4947
- if (validationField.derivation) {
4948
- entries.push(createShorthandEntry(fieldKey, validationField.derivation, context));
4949
- }
4950
- if (validationField.logic) {
4951
- for (const logicConfig of validationField.logic) {
4952
- if (!isDerivationLogicConfig(logicConfig))
4953
- continue;
4954
- if (hasTargetProperty(logicConfig))
4955
- continue; entries.push(createValueLogicEntry(fieldKey, logicConfig, context));
4956
- }
4957
- }
4958
- }
4959
- function collectPropertyEntriesFromField(field, entries, context) {
4960
- const fieldKey = field.key;
4961
- if (!fieldKey)
4962
- return;
4963
- const validationField = field;
4964
- if (!validationField.logic)
4965
- return;
4966
- for (const logicConfig of validationField.logic) {
4967
- if (!isDerivationLogicConfig(logicConfig))
4968
- continue;
4969
- if (!hasTargetProperty(logicConfig))
4970
- continue;
4971
- entries.push(createPropertyLogicEntry(fieldKey, logicConfig, context));
4972
- }
4973
- }
4974
- function createShorthandEntry(fieldKey, expression, context) {
4975
- const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
4976
- return {
4977
- fieldKey: effectiveFieldKey,
4978
- dependsOn: extractStringDependencies(expression),
4979
- condition: true,
4980
- expression,
4981
- trigger: 'onChange',
4982
- isShorthand: true,
4983
- };
4984
- }
4985
- function createValueLogicEntry(fieldKey, config, context) {
4986
- const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
4987
- if (config.source === 'http') {
4988
- if (config.dependsOn.length === 0) {
4989
- throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
4990
- `Wildcard dependencies would trigger HTTP requests on every form change.`);
4991
- }
4992
- if (config.dependsOn.includes('*')) {
4993
- throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. ` +
4994
- `Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
4995
- }
4996
- }
4997
- if (config.source === 'asyncFunction') {
4998
- if (config.dependsOn.length === 0) {
4999
- throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
5000
- `Wildcard dependencies would trigger async functions on every form change.`);
5001
- }
5002
- if (config.dependsOn.includes('*')) {
5003
- throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. ` +
5004
- `Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
5005
- }
5006
- }
5007
- const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
5008
- const condition = config.condition ?? true;
5009
- const trigger = config.trigger ?? 'onChange';
5010
- const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
5011
- return {
5012
- fieldKey: effectiveFieldKey,
5013
- dependsOn,
5014
- condition,
5015
- value: config.value,
5016
- expression: config.expression,
5017
- functionName: config.functionName,
5018
- fn: extractInlineFn(config),
5019
- http: config.http,
5020
- responseExpression: config.responseExpression,
5021
- asyncFunctionName: config.asyncFunctionName,
5022
- asyncFn: extractInlineAsyncFn(config),
5023
- trigger,
5024
- debounceMs,
5025
- isShorthand: false,
5026
- originalConfig: config,
5027
- debugName: config.debugName,
5028
- stopOnUserOverride: config.stopOnUserOverride,
5029
- reEngageOnDependencyChange: config.reEngageOnDependencyChange,
5030
- };
5031
- }
5032
- function createPropertyLogicEntry(fieldKey, config, context) {
5033
- const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
5034
- const errorLocation = `'${effectiveFieldKey}.${config.targetProperty}'`;
5035
- const untypedConfig = config;
5036
- if (untypedConfig.source === 'http' || untypedConfig.source === 'asyncFunction') {
5037
- const conflictingSyncSources = [];
5038
- if (untypedConfig.value !== undefined)
5039
- conflictingSyncSources.push('value');
5040
- if (untypedConfig.expression !== undefined)
5041
- conflictingSyncSources.push('expression');
5042
- if (untypedConfig.functionName !== undefined)
5043
- conflictingSyncSources.push('functionName');
5044
- if (untypedConfig.fn !== undefined)
5045
- conflictingSyncSources.push('fn');
5046
- if (conflictingSyncSources.length > 0) {
5047
- throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: ${JSON.stringify(untypedConfig.source)}' alongside ` +
5048
- `sync value source(s) [${conflictingSyncSources.join(', ')}]. These are mutually exclusive — remove either ` +
5049
- `the sync fields or the async source.`);
5050
- }
5051
- }
5052
- if (untypedConfig.source === 'http' && (untypedConfig.asyncFunctionName !== undefined || untypedConfig.asyncFn !== undefined)) {
5053
- throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "http"' alongside an async function — these are mutually exclusive.`);
5054
- }
5055
- if (untypedConfig.source === 'asyncFunction' && (untypedConfig.http !== undefined || untypedConfig.responseExpression !== undefined)) {
5056
- throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "asyncFunction"' alongside HTTP fields — these are mutually exclusive.`);
5057
- }
5058
- if (untypedConfig.source === 'http') {
5059
- if (!untypedConfig.http) {
5060
- throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires an 'http' config object.`);
5061
- }
5062
- if (typeof untypedConfig.responseExpression !== 'string' || untypedConfig.responseExpression.trim() === '') {
5063
- throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires a non-empty 'responseExpression'.`);
5064
- }
5065
- if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
5066
- throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
5067
- `Wildcard dependencies would trigger HTTP requests on every form change.`);
5068
- }
5069
- if (untypedConfig.dependsOn.includes('*')) {
5070
- throw new DynamicFormError(`HTTP property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
5071
- `Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
5072
- }
5073
- }
5074
- if (untypedConfig.source === 'asyncFunction') {
5075
- if (!untypedConfig.asyncFunctionName && !untypedConfig.asyncFn) {
5076
- throw new DynamicFormError(`Async property derivation for ${errorLocation} requires either 'asyncFunctionName' or 'asyncFn'.`);
5077
- }
5078
- if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
5079
- throw new DynamicFormError(`Async property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
5080
- `Wildcard dependencies would trigger async functions on every form change.`);
5081
- }
5082
- if (untypedConfig.dependsOn.includes('*')) {
5083
- throw new DynamicFormError(`Async property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
5084
- `Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
5085
- }
5086
- }
5087
- const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
5088
- const condition = config.condition ?? true;
5089
- const trigger = config.trigger ?? 'onChange';
5090
- const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
5091
- return {
5092
- fieldKey: effectiveFieldKey,
5093
- targetProperty: config.targetProperty,
5094
- dependsOn,
5095
- condition,
5096
- value: config.value,
5097
- expression: config.expression,
5098
- functionName: config.functionName,
5099
- fn: extractInlineFn(config),
5100
- http: config.http,
5101
- responseExpression: config.responseExpression,
5102
- asyncFunctionName: config.asyncFunctionName,
5103
- asyncFn: extractInlineAsyncFn(config),
5104
- trigger,
5105
- debounceMs,
5106
- debugName: config.debugName,
5107
- originalConfig: config,
5108
- };
5109
- }
5110
-
5111
- function collectPropertyDerivations(fields) {
5112
- return collectAllDerivations(fields).property;
5113
- }
5114
-
5115
- function detectCycles(collection) {
5116
- const graph = buildDependencyGraph(collection);
5117
- if (graph.size === 0) {
5118
- return { hasCycle: false };
5119
- }
5120
- const bidirectionalPairs = detectBidirectionalPairs(collection);
5121
- const result = detectCyclesWithDFS(graph, bidirectionalPairs);
5122
- if (bidirectionalPairs.size > 0) {
5123
- result.bidirectionalPairs = Array.from(bidirectionalPairs);
5124
- }
5125
- return result;
5126
- }
5127
- function detectBidirectionalPairs(collection) {
5128
- const pairs = new Set();
5129
- const dependencyMap = new Map();
5130
- for (const entry of collection.entries) {
5131
- const deps = dependencyMap.get(entry.fieldKey) ?? new Set();
5132
- for (const dep of entry.dependsOn) {
5133
- if (dep !== '*' && dep !== entry.fieldKey) {
5134
- deps.add(dep);
5135
- }
5136
- }
5137
- dependencyMap.set(entry.fieldKey, deps);
5138
- }
5139
- for (const [fieldA, depsA] of dependencyMap) {
5140
- for (const fieldB of depsA) {
5141
- const depsB = dependencyMap.get(fieldB);
5142
- if (depsB?.has(fieldA)) {
5143
- const pairKey = fieldA < fieldB ? `${fieldA}↔${fieldB}` : `${fieldB}↔${fieldA}`;
5144
- pairs.add(pairKey);
5145
- }
5146
- }
5147
- }
5148
- return pairs;
5149
- }
5150
- function isBidirectionalCycle(cyclePath, bidirectionalPairs) {
5151
- if (cyclePath.length !== 3) {
5152
- return false;
5153
- }
5154
- const [first, second, third] = cyclePath;
5155
- if (first !== third) {
5156
- return false;
5157
- }
5158
- const pairKey = first < second ? `${first}↔${second}` : `${second}↔${first}`;
5159
- return bidirectionalPairs.has(pairKey);
5160
- }
5161
- function buildDependencyGraph(collection) {
5162
- const graph = new Map();
5163
- const ensureNode = (fieldKey) => {
5164
- let node = graph.get(fieldKey);
5165
- if (!node) {
5166
- node = {
5167
- fieldKey,
5168
- dependsOn: new Set(),
5169
- dependedOnBy: new Set(),
5170
- };
5171
- graph.set(fieldKey, node);
5172
- }
5173
- return node;
5174
- };
5175
- for (const entry of collection.entries) {
5176
- const fieldNode = ensureNode(entry.fieldKey);
5177
- for (const dep of entry.dependsOn) {
5178
- if (dep !== '*' && dep !== entry.fieldKey) {
5179
- const depNode = ensureNode(dep);
5180
- fieldNode.dependsOn.add(dep);
5181
- depNode.dependedOnBy.add(entry.fieldKey);
5182
- }
5183
- }
5184
- }
5185
- return graph;
5186
- }
5187
- function detectCyclesWithDFS(graph, bidirectionalPairs) {
5188
- const visitState = new Map();
5189
- const parent = new Map();
5190
- for (const fieldKey of graph.keys()) {
5191
- visitState.set(fieldKey, 0 /* VisitState.Unvisited */);
5192
- }
5193
- for (const fieldKey of graph.keys()) {
5194
- if (visitState.get(fieldKey) === 0 /* VisitState.Unvisited */) {
5195
- const cycleResult = dfsVisit(fieldKey, graph, visitState, parent, [], bidirectionalPairs);
5196
- if (cycleResult) {
5197
- return cycleResult;
5198
- }
5199
- }
5200
- }
5201
- return { hasCycle: false };
5202
- }
5203
- function dfsVisit(fieldKey, graph, visitState, parent, path, bidirectionalPairs) {
5204
- visitState.set(fieldKey, 1 /* VisitState.InProgress */);
5205
- path.push(fieldKey);
5206
- const node = graph.get(fieldKey);
5207
- if (!node) {
5208
- visitState.set(fieldKey, 2 /* VisitState.Completed */);
5209
- path.pop();
5210
- return null;
5211
- }
5212
- for (const targetFieldKey of node.dependedOnBy) {
5213
- const targetState = visitState.get(targetFieldKey);
5214
- if (targetState === 1 /* VisitState.InProgress */) {
5215
- const cyclePath = extractCyclePath(path, targetFieldKey);
5216
- if (isBidirectionalCycle(cyclePath, bidirectionalPairs)) {
5217
- continue;
5218
- }
5219
- return {
5220
- hasCycle: true,
5221
- cyclePath,
5222
- errorMessage: formatCycleError(cyclePath),
5223
- };
5224
- }
5225
- if (targetState === 0 /* VisitState.Unvisited */) {
5226
- parent.set(targetFieldKey, fieldKey);
5227
- const result = dfsVisit(targetFieldKey, graph, visitState, parent, path, bidirectionalPairs);
5228
- if (result) {
5229
- return result;
5230
- }
5231
- }
5232
- }
5233
- visitState.set(fieldKey, 2 /* VisitState.Completed */);
5234
- path.pop();
5235
- return null;
5236
- }
5237
- function extractCyclePath(path, cycleStart) {
5238
- const cycleStartIndex = path.indexOf(cycleStart);
5239
- if (cycleStartIndex === -1) {
5240
- return [...path, cycleStart];
5241
- }
5242
- const cyclePath = path.slice(cycleStartIndex);
5243
- cyclePath.push(cycleStart); return cyclePath;
5244
- }
5245
- function formatCycleError(cyclePath) {
5246
- const pathStr = cyclePath.join(' -> ');
5247
- return (`Derivation cycle detected: ${pathStr}\n` +
5248
- `This would cause an infinite loop at runtime. ` +
5249
- `Remove one of the derivations to break the cycle.`);
5250
- }
5251
- function validateNoCycles(collection, logger) {
5252
- const result = detectCycles(collection);
5253
- if (result.hasCycle) {
5254
- throw new Error(result.errorMessage);
5255
- }
5256
- if (DEV_MODE && result.bidirectionalPairs && result.bidirectionalPairs.length > 0 && logger) {
5257
- logger.warn('Derivation: bidirectional derivation patterns detected. ' +
5258
- 'These patterns stabilize via equality checks, but may oscillate with floating-point values ' +
5259
- '(e.g., currency conversions with rounding). ' +
5260
- 'Consider adding tolerance-based comparisons for numeric values.', result.bidirectionalPairs);
5261
- }
5262
- }
5263
-
5264
- const LOG_PREFIX$3 = 'HTTP Derivation -';
5265
- const DEFAULT_HTTP_DEBOUNCE_MS$1 = 300;
5266
- function createHttpDerivationStream(entry, formValue$, context) {
5267
- if (isArrayPlaceholderPath(entry.fieldKey)) {
5268
- context.logger.warn(`${LOG_PREFIX$3} HTTP derivation for array field '${entry.fieldKey}' is not supported. ` +
5269
- `Array HTTP derivations will be supported in a future release.`);
5270
- return EMPTY;
5271
- }
5272
- if (!entry.http || !entry.responseExpression) {
5273
- return EMPTY;
5274
- }
5275
- const responseExpression = entry.responseExpression;
5276
- const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS$1;
5277
- return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
5278
- current: current,
5279
- changedFields: getChangedKeys(previous, current),
5280
- })),
5281
- filter(({ changedFields }) => {
5282
- if (changedFields.size === 0)
5283
- return false;
5284
- return entry.dependsOn.some((dep) => changedFields.has(dep));
5285
- }), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
5286
- return new Observable((subscriber) => {
5287
- const formAccessor = untracked(() => context.form());
5288
- if (entry.stopOnUserOverride) {
5289
- const isDirty = readFieldDirty(formAccessor, entry.fieldKey);
5290
- if (isDirty) {
5291
- if (entry.reEngageOnDependencyChange && changedFields.size > 0) {
5292
- if (entry.dependsOn.some((dep) => changedFields.has(dep))) {
5293
- resetFieldState(formAccessor, entry.fieldKey);
5294
- }
5295
- else {
5296
- const derivationLogger = untracked(() => context.derivationLogger());
5297
- derivationLogger.evaluation({
5298
- debugName: entry.debugName,
5299
- fieldKey: entry.fieldKey,
5300
- result: 'skipped',
5301
- skipReason: 'user-override',
5302
- });
5303
- subscriber.complete();
5304
- return;
5305
- }
5306
- }
5307
- else {
5308
- const derivationLogger = untracked(() => context.derivationLogger());
5309
- derivationLogger.evaluation({
5310
- debugName: entry.debugName,
5311
- fieldKey: entry.fieldKey,
5312
- result: 'skipped',
5313
- skipReason: 'user-override',
5314
- });
5315
- subscriber.complete();
5316
- return;
5317
- }
5318
- }
5319
- }
5320
- const customFunctions = context.customFunctions?.();
5321
- const externalData = context.externalData?.();
5322
- const fieldValue = getNestedValue(current, entry.fieldKey);
5323
- const fieldAccessor = formAccessor[entry.fieldKey];
5324
- const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
5325
- const evalContext = {
5326
- fieldValue,
5327
- formValue: current,
5328
- fieldPath: entry.fieldKey,
5329
- customFunctions,
5330
- externalData,
5331
- logger: context.logger,
5332
- fieldState,
5333
- formFieldState: createFormFieldStateMap(formAccessor, false),
5334
- };
5335
- if (entry.condition !== true) {
5336
- const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
5337
- if (!conditionMet) {
5338
- const derivationLogger = untracked(() => context.derivationLogger());
5339
- derivationLogger.evaluation({
5340
- debugName: entry.debugName,
5341
- fieldKey: entry.fieldKey,
5342
- result: 'skipped',
5343
- skipReason: 'condition-false',
5344
- });
5345
- subscriber.complete();
5346
- return;
5347
- }
5348
- }
5349
- const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
5350
- if (!resolvedRequest) {
5351
- const derivationLogger = untracked(() => context.derivationLogger());
5352
- derivationLogger.evaluation({
5353
- debugName: entry.debugName,
5354
- fieldKey: entry.fieldKey,
5355
- result: 'skipped',
5356
- skipReason: 'condition-false',
5357
- });
5358
- subscriber.complete();
5359
- return;
5360
- }
5361
- const method = (resolvedRequest.method ?? 'GET').toUpperCase();
5362
- const httpSub = context.httpClient
5363
- .request(method, resolvedRequest.url, {
5364
- body: resolvedRequest.body,
5365
- headers: resolvedRequest.headers,
5366
- })
5367
- .subscribe({
5368
- next: (response) => {
5369
- try {
5370
- const newValue = ExpressionParser.evaluate(responseExpression, { response });
5371
- const currentFormValue = untracked(() => context.formValue());
5372
- const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
5373
- if (isEqual(currentValue, newValue)) {
5374
- const derivationLogger = untracked(() => context.derivationLogger());
5375
- derivationLogger.evaluation({
5376
- debugName: entry.debugName,
5377
- fieldKey: entry.fieldKey,
5378
- result: 'skipped',
5379
- skipReason: 'value-unchanged',
5380
- });
5381
- subscriber.complete();
5382
- return;
5383
- }
5384
- const currentForm = untracked(() => context.form());
5385
- if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
5386
- const derivationLogger = untracked(() => context.derivationLogger());
5387
- derivationLogger.evaluation({
5388
- debugName: entry.debugName,
5389
- fieldKey: entry.fieldKey,
5390
- result: 'skipped',
5391
- skipReason: 'user-override',
5392
- });
5393
- subscriber.complete();
5394
- return;
5395
- }
5396
- applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
5397
- const derivationLogger = untracked(() => context.derivationLogger());
5398
- derivationLogger.evaluation({
5399
- debugName: entry.debugName,
5400
- fieldKey: entry.fieldKey,
5401
- result: 'applied',
5402
- previousValue: currentValue,
5403
- newValue,
5404
- });
5405
- subscriber.complete();
5406
- }
5407
- catch (error) {
5408
- const message = error instanceof Error ? error.message : String(error);
5409
- context.logger.warn(`${LOG_PREFIX$3} Failed to process response for '${entry.fieldKey}': ${message}`);
5410
- subscriber.complete();
5411
- }
5412
- },
5413
- error: (error) => {
5414
- const message = error instanceof Error ? error.message : String(error);
5415
- context.logger.warn(`${LOG_PREFIX$3} HTTP request failed for '${entry.fieldKey}': ${message}`);
5416
- subscriber.complete();
5417
- },
5418
- });
5419
- return () => {
5420
- httpSub.unsubscribe();
5421
- };
5422
- });
5423
- }), catchError$1((error) => {
5424
- const message = error instanceof Error ? error.message : String(error);
5425
- context.logger.warn(`${LOG_PREFIX$3} Unexpected stream error for '${entry.fieldKey}': ${message}`);
5426
- return EMPTY;
5427
- }));
5428
- }
5429
-
5430
- const LOG_PREFIX$2 = 'Async Derivation -';
5431
- const DEFAULT_ASYNC_DEBOUNCE_MS$1 = 300;
5432
- function createAsyncDerivationStream(entry, formValue$, context) {
5433
- if (isArrayPlaceholderPath(entry.fieldKey)) {
5434
- context.logger.warn(`${LOG_PREFIX$2} Async derivation for array field '${entry.fieldKey}' is not supported. ` +
5435
- `Array async derivations will be supported in a future release.`);
5436
- return EMPTY;
5437
- }
5438
- if (!entry.asyncFunctionName && !entry.asyncFn) {
5439
- context.logger.warn(`${LOG_PREFIX$2} Derivation for '${entry.fieldKey}' has neither "asyncFunctionName" nor "asyncFn". ` +
5440
- `One of the two is required for source: 'asyncFunction'.`);
5441
- return EMPTY;
5442
- }
5443
- if (entry.asyncFn && entry.asyncFunctionName) {
5444
- context.logger.warn(`${LOG_PREFIX$2} Both "asyncFn" and "asyncFunctionName" are set on derivation for '${entry.fieldKey}'. ` +
5445
- `Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
5446
- }
5447
- const inlineAsyncFn = entry.asyncFn;
5448
- const asyncFunctionName = entry.asyncFunctionName;
5449
- const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS$1;
5450
- return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
5451
- current: current,
5452
- changedFields: getChangedKeys(previous, current),
5453
- })),
5454
- filter(({ changedFields }) => {
5455
- if (changedFields.size === 0)
5456
- return false;
5457
- return entry.dependsOn.some((dep) => changedFields.has(dep));
5458
- }), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
5459
- return new Observable((subscriber) => {
5460
- const formAccessor = untracked(() => context.form());
5461
- if (entry.stopOnUserOverride) {
5462
- const isDirty = readFieldDirty(formAccessor, entry.fieldKey);
5463
- if (isDirty) {
5464
- if (entry.reEngageOnDependencyChange) {
5465
- resetFieldState(formAccessor, entry.fieldKey);
5466
- }
5467
- else {
5468
- const derivationLogger = untracked(() => context.derivationLogger());
5469
- derivationLogger.evaluation({
5470
- debugName: entry.debugName,
5471
- fieldKey: entry.fieldKey,
5472
- result: 'skipped',
5473
- skipReason: 'user-override',
5474
- });
5475
- subscriber.complete();
5476
- return;
5477
- }
5478
- }
5479
- }
5480
- const customFunctions = context.customFunctions?.();
5481
- const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
5482
- const externalData = context.externalData?.();
5483
- const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
5484
- if (!asyncFn) {
5485
- context.logger.warn(`${LOG_PREFIX$2} Async derivation function '${asyncFunctionName}' not found for field '${entry.fieldKey}'. ` +
5486
- `Register it in customFnConfig.asyncDerivations.`);
5487
- subscriber.complete();
5488
- return;
5489
- }
5490
- const fieldValue = getNestedValue(current, entry.fieldKey);
5491
- const fieldAccessor = formAccessor[entry.fieldKey];
5492
- const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
5493
- const evalContext = {
5494
- fieldValue,
5495
- formValue: current,
5496
- fieldPath: entry.fieldKey,
5497
- customFunctions,
5498
- externalData,
5499
- logger: context.logger,
5500
- fieldState,
5501
- formFieldState: createFormFieldStateMap(formAccessor, false),
5502
- };
5503
- if (entry.condition !== true) {
5504
- const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
5505
- if (!conditionMet) {
5506
- const derivationLogger = untracked(() => context.derivationLogger());
5507
- derivationLogger.evaluation({
5508
- debugName: entry.debugName,
5509
- fieldKey: entry.fieldKey,
5510
- result: 'skipped',
5511
- skipReason: 'condition-false',
5512
- });
5513
- subscriber.complete();
5514
- return;
5515
- }
5516
- }
5517
- const result$ = from(asyncFn(evalContext));
5518
- const asyncSub = result$.subscribe({
5519
- next: (newValue) => {
5520
- try {
5521
- const currentFormValue = untracked(() => context.formValue());
5522
- const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
5523
- if (isEqual(currentValue, newValue)) {
5524
- const derivationLogger = untracked(() => context.derivationLogger());
5525
- derivationLogger.evaluation({
5526
- debugName: entry.debugName,
5527
- fieldKey: entry.fieldKey,
5528
- result: 'skipped',
5529
- skipReason: 'value-unchanged',
5530
- });
5531
- subscriber.complete();
5532
- return;
5533
- }
5534
- const currentForm = untracked(() => context.form());
5535
- if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
5536
- const derivationLogger = untracked(() => context.derivationLogger());
5537
- derivationLogger.evaluation({
5538
- debugName: entry.debugName,
5539
- fieldKey: entry.fieldKey,
5540
- result: 'skipped',
5541
- skipReason: 'user-override',
5542
- });
5543
- subscriber.complete();
5544
- return;
5545
- }
5546
- applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
5547
- const derivationLogger = untracked(() => context.derivationLogger());
5548
- derivationLogger.evaluation({
5549
- debugName: entry.debugName,
5550
- fieldKey: entry.fieldKey,
5551
- result: 'applied',
5552
- previousValue: currentValue,
5553
- newValue,
5554
- });
5555
- subscriber.complete();
5556
- }
5557
- catch (error) {
5558
- const message = error instanceof Error ? error.message : String(error);
5559
- context.logger.warn(`${LOG_PREFIX$2} Failed to process result for '${entry.fieldKey}': ${message}`);
5560
- subscriber.complete();
5561
- }
5562
- },
5563
- error: (error) => {
5564
- const message = error instanceof Error ? error.message : String(error);
5565
- context.logger.warn(`${LOG_PREFIX$2} Async function failed for '${entry.fieldKey}': ${message}`);
5566
- subscriber.complete();
5567
- },
5568
- complete: () => {
5569
- subscriber.complete();
5570
- },
5571
- });
5572
- return () => {
5573
- asyncSub.unsubscribe();
5574
- };
5575
- });
5576
- }), catchError$1((error) => {
5577
- const message = error instanceof Error ? error.message : String(error);
5578
- context.logger.warn(`${LOG_PREFIX$2} Unexpected stream error for '${entry.fieldKey}': ${message}`);
5579
- return EMPTY;
5580
- }));
5581
- }
5582
-
5583
- function resolveExternalData(externalDataSignal) {
5584
- const externalDataRecord = untracked(() => externalDataSignal?.());
5585
- if (!externalDataRecord)
5586
- return undefined;
5587
- const resolved = {};
5588
- for (const [key, value] of Object.entries(externalDataRecord)) {
5589
- if (isSignal(value)) {
5590
- resolved[key] = untracked(() => value());
5591
- }
5592
- else {
5593
- resolved[key] = value;
5594
- }
5595
- }
5596
- return resolved;
5597
- }
5598
-
5599
- function getDebouncePeriods(entries) {
5600
- const periods = new Set();
5601
- for (const entry of entries) {
5602
- if (entry.trigger === 'debounced') {
5603
- periods.add(entry.debounceMs ?? DEFAULT_DEBOUNCE_MS);
5604
- }
5605
- }
5606
- return Array.from(periods);
5607
- }
5608
- function filterEntriesByDebouncePeriod(entries, period) {
5609
- return entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === period);
5610
- }
5611
-
5612
- function entrySetsEqual(prev, next, sig) {
5613
- if (prev.length !== next.length)
5614
- return false;
5615
- const counts = new Map();
5616
- for (const item of prev) {
5617
- const key = sig(item);
5618
- counts.set(key, (counts.get(key) ?? 0) + 1);
5619
- }
5620
- for (const item of next) {
5621
- const key = sig(item);
5622
- const remaining = counts.get(key);
5623
- if (!remaining)
5624
- return false;
5625
- if (remaining === 1)
5626
- counts.delete(key);
5627
- else
5628
- counts.set(key, remaining - 1);
5629
- }
5630
- return counts.size === 0;
5631
- }
5632
- function buildEntryStreamPipeline(config) {
5633
- return config.collection$.pipe(map((collection) => config.selectEntries(collection)), distinctUntilChanged((prev, next) => entrySetsEqual(prev, next, config.entrySignature)), switchMap((entries) => {
5634
- if (entries.length === 0)
5635
- return EMPTY;
5636
- const streams = entries.map((entry) => config.createStream(entry)).filter((stream) => stream !== null);
5637
- if (streams.length === 0)
5638
- return EMPTY;
5639
- return merge(...streams);
5640
- }));
5641
- }
5642
-
5643
- function setupOnChangeStream(opts) {
5644
- const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
5645
- const formValue$ = toObservable(opts.formValueSignal, { injector: opts.injector });
5646
- const extra$ = opts.additionalAwakeners?.length ? opts.additionalAwakeners.map((s) => toObservable(s, { injector: opts.injector })) : [];
5647
- collection$
5648
- .pipe(filter((collection) => collection !== null), combineLatestWith(formValue$, ...extra$), auditTime(0), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({
5649
- collection: current[0],
5650
- changedFields: getChangedKeys(previous?.[1] ?? null, current[1]),
5651
- })), exhaustMap(({ collection, changedFields }) => {
5652
- opts.applyOnChange(collection, changedFields);
5653
- return scheduled([null], queueScheduler);
5654
- }), takeUntilDestroyed(opts.destroyRef))
5655
- .subscribe({
5656
- error: (err) => opts.logger.error(opts.errorLabel, err),
5657
- });
5658
- }
5659
- function setupDebouncedStream(opts) {
5660
- toObservable(opts.formValueSignal, { injector: opts.injector })
5661
- .pipe(debounceTime(DEFAULT_DEBOUNCE_MS), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({ current, changedFields: getChangedKeys(previous, current) })), filter(({ changedFields }) => changedFields.size > 0), switchMap(({ changedFields }) => {
5662
- const collection = untracked(() => opts.collectionSignal());
5663
- if (!collection)
5664
- return of(null);
5665
- const debouncePeriods = getDebouncePeriods(collection.entries);
5666
- if (debouncePeriods.length === 0)
5667
- return of(null);
5668
- const periodStreams = debouncePeriods.map((debounceMs) => {
5669
- const additionalWait = Math.max(0, debounceMs - DEFAULT_DEBOUNCE_MS);
5670
- if (additionalWait === 0) {
5671
- return of(null).pipe(map(() => {
5672
- opts.applyDebouncedForPeriod(debounceMs, collection, changedFields);
5673
- }));
5674
- }
5675
- return timer(additionalWait).pipe(map(() => {
5676
- const fresh = untracked(() => opts.collectionSignal()) ?? collection;
5677
- opts.applyDebouncedForPeriod(debounceMs, fresh, changedFields);
5678
- }));
5679
- });
5680
- return merge(...periodStreams);
5681
- }), takeUntilDestroyed(opts.destroyRef))
5682
- .subscribe({
5683
- error: (err) => opts.logger.error(opts.errorLabel, err),
5684
- });
5685
- }
5686
- function setupEntryAsyncStream(opts) {
5687
- const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
5688
- buildEntryStreamPipeline({
5689
- collection$,
5690
- selectEntries: opts.selectEntries,
5691
- entrySignature: opts.entrySignature,
5692
- createStream: opts.createStream,
5693
- })
5694
- .pipe(catchError((err) => {
5695
- opts.logger.error(opts.errorLabel, err);
5696
- return EMPTY;
5697
- }), takeUntilDestroyed(opts.destroyRef))
5698
- .subscribe();
5699
- }
5700
-
5701
- function createEmptyPropertyDerivationCollection() {
5702
- return { entries: [] };
5703
- }
5704
- function isAsyncPropertyDerivationEntry(entry) {
5705
- return Boolean(entry.http) || Boolean(entry.asyncFunctionName) || Boolean(entry.asyncFn);
5706
- }
5707
-
5708
- const ERROR_PREFIX = '[PropertyDerivation]';
5709
- function applyPropertyDerivations(collection, context, changedFields) {
5710
- let appliedCount = 0;
5711
- let skippedCount = 0;
5712
- let errorCount = 0;
5713
- const baseEntries = changedFields ? getEntriesForChangedFields(collection.entries, changedFields) : collection.entries;
5714
- const entriesToProcess = baseEntries.filter((entry) => !isAsyncPropertyDerivationEntry(entry));
5715
- for (const entry of entriesToProcess) {
5716
- try {
5717
- const applied = tryApplyPropertyDerivation(entry, context);
5718
- if (applied) {
5719
- appliedCount++;
5720
- }
5721
- else {
5722
- skippedCount++;
5723
- }
5724
- }
5725
- catch (error) {
5726
- errorCount++;
5727
- const errorMessage = error instanceof Error ? error.message : String(error);
5728
- context.logger.error(`${ERROR_PREFIX} Failed to process property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}): ${errorMessage}`);
5729
- }
5730
- }
5731
- return { appliedCount, skippedCount, errorCount };
5732
- }
5733
- function applyPropertyDerivationsForTrigger(collection, trigger, context, changedFields) {
5734
- const filteredEntries = collection.entries.filter((entry) => {
5735
- if (trigger === 'onChange') {
5736
- return !entry.trigger || entry.trigger === 'onChange';
5737
- }
5738
- return entry.trigger === trigger;
5739
- });
5740
- const filteredCollection = { entries: filteredEntries };
5741
- return applyPropertyDerivations(filteredCollection, context, changedFields);
5742
- }
5743
- function getEntriesForChangedFields(entries, changedFields) {
5744
- return entries.filter((entry) => {
5745
- if (entry.dependsOn.includes('*'))
5746
- return true;
5747
- return entry.dependsOn.some((dep) => changedFields.has(dep));
5748
- });
5749
- }
5750
- function tryApplyPropertyDerivation(entry, context) {
5751
- if (isArrayPlaceholderPath(entry.fieldKey)) {
5752
- return tryApplyArrayPropertyDerivation(entry, context);
5753
- }
5754
- const formValue = untracked(() => context.formValue());
5755
- const evalContext = createEvaluationContext(entry, formValue, context);
5756
- if (!evaluatePropertyCondition(entry.condition, evalContext)) {
5757
- return false;
5758
- }
5759
- const newValue = computePropertyValue(entry, evalContext, context);
5760
- context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue);
5761
- return true;
5762
- }
5763
- function tryApplyArrayPropertyDerivation(entry, context) {
5764
- const formValue = untracked(() => context.formValue());
5765
- const pathInfo = parseArrayPath(entry.fieldKey);
5766
- if (!pathInfo.isArrayPath) {
5767
- return false;
5768
- }
5769
- const { arrayPath } = pathInfo;
5770
- const arrayValue = getNestedValue(formValue, arrayPath);
5771
- if (!Array.isArray(arrayValue)) {
5772
- return false;
5773
- }
5774
- let appliedAny = false;
5775
- for (let i = 0; i < arrayValue.length; i++) {
5776
- const resolvedPath = resolveArrayPath(entry.fieldKey, i);
5777
- const arrayItem = arrayValue[i];
5778
- const evalContext = createArrayItemEvaluationContext(entry, arrayItem, formValue, i, arrayPath, context);
5779
- if (!evaluatePropertyCondition(entry.condition, evalContext)) {
5780
- continue;
5781
- }
5782
- try {
5783
- const newValue = computePropertyValue(entry, evalContext, context);
5784
- context.store.setOverride(resolvedPath, entry.targetProperty, newValue);
5785
- appliedAny = true;
5786
- }
5787
- catch (error) {
5788
- const errorMessage = error instanceof Error ? error.message : String(error);
5789
- context.logger.error(`${ERROR_PREFIX} Failed to compute property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}, index: ${i}): ${errorMessage}`);
5790
- }
5791
- }
5792
- return appliedAny;
5793
- }
5794
- function createEvaluationContext(entry, formValue, context) {
5795
- const fieldValue = getNestedValue(formValue, entry.fieldKey);
5796
- const parentPath = getParentPathInScope(entry.fieldKey);
5797
- const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
5798
- return {
5799
- fieldValue,
5800
- formValue,
5801
- fieldPath: entry.fieldKey,
5802
- groupValue,
5803
- customFunctions: context.customFunctions,
5804
- externalData: context.externalData,
5805
- logger: context.logger,
5806
- };
5807
- }
5808
- function createArrayItemEvaluationContext(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context) {
5809
- const pathInfo = parseArrayPath(entry.fieldKey);
5810
- const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
5811
- return {
5812
- fieldValue,
5813
- formValue: arrayItem,
5814
- fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
5815
- groupValue,
5816
- customFunctions: context.customFunctions,
5817
- externalData: context.externalData,
5818
- logger: context.logger,
5819
- rootFormValue,
5820
- arrayIndex: itemIndex,
5821
- arrayPath,
5822
- };
5823
- }
5824
- function evaluatePropertyCondition(condition, context) {
5825
- if (typeof condition === 'boolean') {
5826
- return condition;
5827
- }
5828
- return evaluateCondition(condition, context);
5829
- }
5830
- function computePropertyValue(entry, evalContext, applicatorContext) {
5831
- return computeValueFromEntry(entry, evalContext, {
5832
- derivationFunctions: applicatorContext.derivationFunctions,
5833
- subject: `${entry.fieldKey}.${entry.targetProperty}`,
5834
- functionKind: 'Property derivation function',
5835
- });
5836
- }
5837
-
5838
- const LOG_PREFIX$1 = 'HTTP Property Derivation -';
5839
- const DEFAULT_HTTP_DEBOUNCE_MS = 300;
5840
- function createHttpPropertyDerivationStream(entry, formValue$, context) {
5841
- if (isArrayPlaceholderPath(entry.fieldKey)) {
5842
- context.logger.warn(`${LOG_PREFIX$1} HTTP property derivation for array field '${entry.fieldKey}' is not supported. ` +
5843
- `Array HTTP property derivations will be supported in a future release.`);
5844
- return EMPTY;
5845
- }
5846
- if (!entry.http || !entry.responseExpression) {
5847
- return EMPTY;
5848
- }
5849
- const responseExpression = entry.responseExpression;
5850
- const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS;
5851
- return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
5852
- current: current,
5853
- changedFields: getChangedKeys(previous, current),
5854
- })), filter(({ changedFields }) => {
5855
- if (changedFields.size === 0)
5856
- return false;
5857
- return entry.dependsOn.some((dep) => changedFields.has(dep));
5858
- }), debounceTime(debounceMs), switchMap(({ current }) => {
5859
- return new Observable((subscriber) => {
5860
- const customFunctions = context.customFunctions?.();
5861
- const externalData = context.externalData?.();
5862
- const fieldValue = getNestedValue(current, entry.fieldKey);
5863
- const evalContext = {
5864
- fieldValue,
5865
- formValue: current,
5866
- fieldPath: entry.fieldKey,
5867
- customFunctions,
5868
- externalData,
5869
- logger: context.logger,
5870
- };
5871
- if (entry.condition !== true) {
5872
- const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
5873
- if (!conditionMet) {
5874
- subscriber.complete();
5875
- return;
5876
- }
5877
- }
5878
- const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
5879
- if (!resolvedRequest) {
5880
- subscriber.complete();
5881
- return;
5882
- }
5883
- const method = (resolvedRequest.method ?? 'GET').toUpperCase();
5884
- const httpSub = context.httpClient
5885
- .request(method, resolvedRequest.url, {
5886
- body: resolvedRequest.body,
5887
- headers: resolvedRequest.headers,
5888
- })
5889
- .subscribe({
5890
- next: (response) => {
5891
- try {
5892
- const newValue = ExpressionParser.evaluate(responseExpression, { response });
5893
- untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
5894
- subscriber.complete();
5895
- }
5896
- catch (error) {
5897
- const message = error instanceof Error ? error.message : String(error);
5898
- context.logger.warn(`${LOG_PREFIX$1} Failed to process response for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
5899
- subscriber.complete();
5900
- }
5901
- },
5902
- error: (error) => {
5903
- const message = error instanceof Error ? error.message : String(error);
5904
- context.logger.warn(`${LOG_PREFIX$1} HTTP request failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
5905
- subscriber.complete();
5906
- },
5907
- });
5908
- return () => {
5909
- httpSub.unsubscribe();
5910
- };
5911
- });
5912
- }), catchError$1((error) => {
5913
- const message = error instanceof Error ? error.message : String(error);
5914
- context.logger.warn(`${LOG_PREFIX$1} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
5915
- return EMPTY;
5916
- }));
5917
- }
5918
-
5919
- const LOG_PREFIX = 'Async Property Derivation -';
5920
- const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
5921
- function createAsyncPropertyDerivationStream(entry, formValue$, context) {
5922
- if (isArrayPlaceholderPath(entry.fieldKey)) {
5923
- context.logger.warn(`${LOG_PREFIX} Async property derivation for array field '${entry.fieldKey}' is not supported. ` +
5924
- `Array async property derivations will be supported in a future release.`);
5925
- return EMPTY;
5926
- }
5927
- if (!entry.asyncFunctionName && !entry.asyncFn) {
5928
- context.logger.warn(`${LOG_PREFIX} Property derivation for '${entry.fieldKey}.${entry.targetProperty}' has neither "asyncFunctionName" nor "asyncFn". ` +
5929
- `One of the two is required for source: 'asyncFunction'.`);
5930
- return EMPTY;
5931
- }
5932
- if (entry.asyncFn && entry.asyncFunctionName) {
5933
- context.logger.warn(`${LOG_PREFIX} Both "asyncFn" and "asyncFunctionName" are set on property derivation for '${entry.fieldKey}.${entry.targetProperty}'. ` +
5934
- `Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
5935
- }
5936
- const inlineAsyncFn = entry.asyncFn;
5937
- const asyncFunctionName = entry.asyncFunctionName;
5938
- const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS;
5939
- return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
5940
- current: current,
5941
- changedFields: getChangedKeys(previous, current),
5942
- })), filter(({ changedFields }) => {
5943
- if (changedFields.size === 0)
5944
- return false;
5945
- return entry.dependsOn.some((dep) => changedFields.has(dep));
5946
- }), debounceTime(debounceMs), switchMap(({ current }) => {
5947
- return new Observable((subscriber) => {
5948
- const customFunctions = context.customFunctions?.();
5949
- const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
5950
- const externalData = context.externalData?.();
5951
- const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
5952
- if (!asyncFn) {
5953
- context.logger.warn(`${LOG_PREFIX} Async derivation function '${asyncFunctionName}' not found for '${entry.fieldKey}.${entry.targetProperty}'. ` +
5954
- `Register it in customFnConfig.asyncDerivations.`);
5955
- subscriber.complete();
5956
- return;
5957
- }
5958
- const fieldValue = getNestedValue(current, entry.fieldKey);
5959
- const evalContext = {
5960
- fieldValue,
5961
- formValue: current,
5962
- fieldPath: entry.fieldKey,
5963
- customFunctions,
5964
- externalData,
5965
- logger: context.logger,
5966
- };
5967
- if (entry.condition !== true) {
5968
- const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
5969
- if (!conditionMet) {
5970
- subscriber.complete();
5971
- return;
5972
- }
5973
- }
5974
- const result$ = from(asyncFn(evalContext));
5975
- const asyncSub = result$.subscribe({
5976
- next: (newValue) => {
5977
- try {
5978
- untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
5979
- subscriber.complete();
5980
- }
5981
- catch (error) {
5982
- const message = error instanceof Error ? error.message : String(error);
5983
- context.logger.warn(`${LOG_PREFIX} Failed to process result for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
5984
- subscriber.complete();
5985
- }
5986
- },
5987
- error: (error) => {
5988
- const message = error instanceof Error ? error.message : String(error);
5989
- context.logger.warn(`${LOG_PREFIX} Async function failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
5990
- subscriber.complete();
5991
- },
5992
- complete: () => {
5993
- subscriber.complete();
5994
- },
5995
- });
5996
- return () => {
5997
- asyncSub.unsubscribe();
5998
- };
5999
- });
6000
- }), catchError$1((error) => {
6001
- const message = error instanceof Error ? error.message : String(error);
6002
- context.logger.warn(`${LOG_PREFIX} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
6003
- return EMPTY;
6004
- }));
6005
- }
6006
-
6007
- class DerivationOrchestrator {
6008
- config;
6009
- injector = inject(Injector);
6010
- destroyRef = inject(DestroyRef);
6011
- logger = inject(DynamicFormLogger);
6012
- warningTracker = inject(DERIVATION_WARNING_TRACKER);
6013
- functionRegistry = inject(FunctionRegistryService);
6014
- httpClient = inject(HttpClient, { optional: true });
6015
- formOptions;
6016
- derivationCollection;
6017
- propertyDerivationCollection;
6018
- constructor(config) {
6019
- this.config = config;
6020
- const valueEnabled = !!(config.form && config.derivationLogger);
6021
- const propertyStore = config.propertyStore;
6022
- const propertyEnabled = !!propertyStore;
6023
- const sharedAllDerivations = valueEnabled && propertyEnabled
6024
- ? computed(() => {
6025
- const fields = config.schemaFields();
6026
- if (!fields || fields.length === 0)
6027
- return null;
6028
- return collectAllDerivations(fields);
6029
- })
6030
- : null;
6031
- this.derivationCollection = valueEnabled
6032
- ? this.setupValuePipeline(sharedAllDerivations)
6033
- : computed(() => null);
6034
- this.propertyDerivationCollection = propertyStore
6035
- ? this.setupPropertyPipeline(propertyStore, sharedAllDerivations)
6036
- : computed(() => null);
6037
- }
6038
- setupValuePipeline(sharedAllDerivations) {
6039
- this.formOptions = inject(FORM_OPTIONS);
6040
- const collection = computed(() => {
6041
- const fields = this.config.schemaFields();
6042
- if (!fields || fields.length === 0) {
6043
- return null;
6044
- }
6045
- const collected = sharedAllDerivations ? (sharedAllDerivations()?.value ?? null) : collectDerivations(fields);
6046
- if (!collected || collected.entries.length === 0) {
6047
- return null;
6048
- }
6049
- validateNoCycles(collected, this.logger);
6050
- if (DEV_MODE) {
6051
- warnAboutWildcardDependencies(this.logger, collected.entries, fields.length);
6052
- warnAboutMisconfiguredReEngagement(this.logger, collected.entries);
6053
- }
6054
- return collected;
6055
- }, ...(ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []));
6056
- setupOnChangeStream({
6057
- injector: this.injector,
6058
- destroyRef: this.destroyRef,
6059
- logger: this.logger,
6060
- errorLabel: 'Derivation onChange stream error',
6061
- collectionSignal: collection,
6062
- formValueSignal: this.config.formValue,
6063
- additionalAwakeners: [this.config.form],
6064
- applyOnChange: (current, changedFields) => {
6065
- this.applyOnChangeDerivations(current, untracked(() => this.config.form()), changedFields);
6066
- },
6067
- });
6068
- setupDebouncedStream({
6069
- injector: this.injector,
6070
- destroyRef: this.destroyRef,
6071
- logger: this.logger,
6072
- errorLabel: 'Derivation debounced stream error',
6073
- collectionSignal: collection,
6074
- formValueSignal: this.config.formValue,
6075
- applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
6076
- const formAccessor = untracked(() => this.config.form());
6077
- if (!formAccessor)
6078
- return;
6079
- this.applyDebouncedEntriesForPeriod(debounceMs, current, formAccessor, changedFields);
6080
- },
6081
- });
6082
- this.setupValueHttpStreams(collection);
6083
- this.setupValueAsyncFunctionStreams(collection);
6084
- return collection;
6085
- }
6086
- setupPropertyPipeline(propertyStore, sharedAllDerivations) {
6087
- const collection = computed(() => {
6088
- const fields = this.config.schemaFields();
6089
- if (!fields || fields.length === 0) {
6090
- return null;
6091
- }
6092
- const collected = sharedAllDerivations ? (sharedAllDerivations()?.property ?? null) : collectPropertyDerivations(fields);
6093
- if (!collected || collected.entries.length === 0) {
6094
- return null;
6095
- }
6096
- return collected;
6097
- }, ...(ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []));
6098
- explicitEffect([collection, this.config.schemaFields], ([current, fields]) => {
6099
- propertyStore.clear();
6100
- if (!current)
6101
- return;
6102
- for (const entry of current.entries) {
6103
- propertyStore.registerField(entry.fieldKey);
6104
- }
6105
- if (DEV_MODE) {
6106
- warnAboutWildcardPropertyDependencies(this.logger, current.entries, fields?.length ?? 0);
6107
- }
6108
- });
6109
- setupOnChangeStream({
6110
- injector: this.injector,
6111
- destroyRef: this.destroyRef,
6112
- logger: this.logger,
6113
- errorLabel: 'Property derivation onChange stream error',
6114
- collectionSignal: collection,
6115
- formValueSignal: this.config.formValue,
6116
- applyOnChange: (current) => this.applyOnChangePropertyDerivations(current, propertyStore),
6117
- });
6118
- setupDebouncedStream({
6119
- injector: this.injector,
6120
- destroyRef: this.destroyRef,
6121
- logger: this.logger,
6122
- errorLabel: 'Property derivation debounced stream error',
6123
- collectionSignal: collection,
6124
- formValueSignal: this.config.formValue,
6125
- applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
6126
- this.applyDebouncedPropertyEntriesForPeriod(debounceMs, current, propertyStore, changedFields);
6127
- },
6128
- });
6129
- this.setupPropertyHttpStreams(collection, propertyStore);
6130
- this.setupPropertyAsyncFunctionStreams(collection, propertyStore);
6131
- return collection;
6132
- }
6133
- applyOnChangeDerivations(collection, formAccessor, changedFields) {
6134
- const applicatorContext = {
6135
- formValue: this.config.formValue,
6136
- rootForm: formAccessor,
6137
- derivationFunctions: this.functionRegistry.getDerivationFunctions(),
6138
- customFunctions: this.functionRegistry.getCustomFunctions(),
6139
- externalData: resolveExternalData(this.config.externalData),
6140
- logger: this.logger,
6141
- warningTracker: this.warningTracker,
6142
- derivationLogger: untracked(() => this.config.derivationLogger()),
6143
- maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations),
6144
- };
6145
- const result = applyDerivationsForTrigger(collection, 'onChange', applicatorContext, changedFields);
6146
- if (result.maxIterationsReached) {
6147
- this.logger.warn(`Derivation processing reached max iterations. ` +
6148
- `This may indicate a loop in derivation logic that wasn't caught at build time. ` +
6149
- `Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
6150
- }
6151
- }
6152
- applyDebouncedEntriesForPeriod(debounceMs, collection, formAccessor, changedFields) {
6153
- const debouncedEntries = collection.entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === debounceMs);
6154
- if (debouncedEntries.length === 0) {
6155
- return;
6156
- }
6157
- const filteredCollection = {
6158
- entries: debouncedEntries,
6159
- };
6160
- const applicatorContext = {
6161
- formValue: this.config.formValue,
6162
- rootForm: formAccessor,
6163
- derivationFunctions: this.functionRegistry.getDerivationFunctions(),
6164
- customFunctions: this.functionRegistry.getCustomFunctions(),
6165
- externalData: resolveExternalData(this.config.externalData),
6166
- logger: this.logger,
6167
- warningTracker: this.warningTracker,
6168
- derivationLogger: untracked(() => this.config.derivationLogger()),
6169
- maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations),
6170
- };
6171
- const result = applyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
6172
- if (result.maxIterationsReached) {
6173
- this.logger.warn(`Debounced derivation processing reached max iterations (${debounceMs}ms). ` +
6174
- `Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
6175
- }
6176
- }
6177
- setupValueHttpStreams(collectionSignal) {
6178
- const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
6179
- setupEntryAsyncStream({
6180
- injector: this.injector,
6181
- destroyRef: this.destroyRef,
6182
- logger: this.logger,
6183
- errorLabel: 'HTTP Derivation: outer stream error',
6184
- collectionSignal,
6185
- selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
6186
- entrySignature: valueHttpEntrySignature,
6187
- createStream: (entry) => {
6188
- if (!this.httpClient) {
6189
- this.logger.error('HTTP Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
6190
- return null;
6191
- }
6192
- const context = {
6193
- formValue: this.config.formValue,
6194
- form: this.config.form,
6195
- httpClient: this.httpClient,
6196
- logger: this.logger,
6197
- derivationLogger: this.config.derivationLogger,
6198
- customFunctions: () => this.functionRegistry.getCustomFunctions(),
6199
- externalData: () => resolveExternalData(this.config.externalData),
6200
- warningTracker: this.warningTracker,
6201
- };
6202
- return createHttpDerivationStream(entry, formValue$, context);
6203
- },
6204
- });
6205
- }
6206
- setupValueAsyncFunctionStreams(collectionSignal) {
6207
- const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
6208
- setupEntryAsyncStream({
6209
- injector: this.injector,
6210
- destroyRef: this.destroyRef,
6211
- logger: this.logger,
6212
- errorLabel: 'Async Derivation: outer stream error',
6213
- collectionSignal,
6214
- selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
6215
- entrySignature: valueAsyncEntrySignature,
6216
- createStream: (entry) => createAsyncDerivationStream(entry, formValue$, {
6217
- formValue: this.config.formValue,
6218
- form: this.config.form,
6219
- logger: this.logger,
6220
- derivationLogger: this.config.derivationLogger,
6221
- customFunctions: () => this.functionRegistry.getCustomFunctions(),
6222
- asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
6223
- externalData: () => resolveExternalData(this.config.externalData),
6224
- warningTracker: this.warningTracker,
6225
- }),
6226
- });
6227
- }
6228
- applyOnChangePropertyDerivations(collection, store) {
6229
- const applicatorContext = {
6230
- formValue: this.config.formValue,
6231
- store,
6232
- derivationFunctions: this.functionRegistry.getDerivationFunctions(),
6233
- customFunctions: this.functionRegistry.getCustomFunctions(),
6234
- externalData: resolveExternalData(this.config.externalData),
6235
- logger: this.logger,
6236
- warningTracker: this.warningTracker,
6237
- };
6238
- applyPropertyDerivationsForTrigger(collection, 'onChange', applicatorContext);
6239
- }
6240
- applyDebouncedPropertyEntriesForPeriod(debounceMs, collection, store, changedFields) {
6241
- const debouncedEntries = collection.entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === debounceMs);
6242
- if (debouncedEntries.length === 0)
6243
- return;
6244
- const filteredCollection = { entries: debouncedEntries };
6245
- const applicatorContext = {
6246
- formValue: this.config.formValue,
6247
- store,
6248
- derivationFunctions: this.functionRegistry.getDerivationFunctions(),
6249
- customFunctions: this.functionRegistry.getCustomFunctions(),
6250
- externalData: resolveExternalData(this.config.externalData),
6251
- logger: this.logger,
6252
- warningTracker: this.warningTracker,
6253
- };
6254
- applyPropertyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
6255
- }
6256
- setupPropertyHttpStreams(collectionSignal, store) {
6257
- const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
6258
- setupEntryAsyncStream({
6259
- injector: this.injector,
6260
- destroyRef: this.destroyRef,
6261
- logger: this.logger,
6262
- errorLabel: 'HTTP Property Derivation: outer stream error',
6263
- collectionSignal,
6264
- selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
6265
- entrySignature: propertyHttpEntrySignature,
6266
- createStream: (entry) => {
6267
- if (!this.httpClient) {
6268
- this.logger.error('HTTP Property Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
6269
- return null;
6270
- }
6271
- return createHttpPropertyDerivationStream(entry, formValue$, {
6272
- formValue: this.config.formValue,
6273
- store,
6274
- httpClient: this.httpClient,
6275
- logger: this.logger,
6276
- customFunctions: () => this.functionRegistry.getCustomFunctions(),
6277
- externalData: () => resolveExternalData(this.config.externalData),
6278
- });
6279
- },
6280
- });
6281
- }
6282
- setupPropertyAsyncFunctionStreams(collectionSignal, store) {
6283
- const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
6284
- setupEntryAsyncStream({
6285
- injector: this.injector,
6286
- destroyRef: this.destroyRef,
6287
- logger: this.logger,
6288
- errorLabel: 'Async Property Derivation: outer stream error',
6289
- collectionSignal,
6290
- selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
6291
- entrySignature: propertyAsyncEntrySignature,
6292
- createStream: (entry) => createAsyncPropertyDerivationStream(entry, formValue$, {
6293
- formValue: this.config.formValue,
6294
- store,
6295
- logger: this.logger,
6296
- customFunctions: () => this.functionRegistry.getCustomFunctions(),
6297
- asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
6298
- externalData: () => resolveExternalData(this.config.externalData),
6299
- }),
6300
- });
6301
- }
6302
- }
6303
- function valueHttpEntrySignature(entry) {
6304
- const config = {
6305
- http: entry.http,
6306
- responseExpression: entry.responseExpression,
6307
- dependsOn: entry.dependsOn,
6308
- debounceMs: entry.debounceMs,
6309
- trigger: entry.trigger,
6310
- condition: entry.condition,
6311
- stopOnUserOverride: entry.stopOnUserOverride,
6312
- reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
6313
- };
6314
- return `${entry.fieldKey}:${JSON.stringify(config)}`;
6315
- }
6316
- function valueAsyncEntrySignature(entry) {
6317
- const config = {
6318
- asyncFunctionName: entry.asyncFunctionName,
6319
- asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}` : undefined,
6320
- dependsOn: entry.dependsOn,
6321
- debounceMs: entry.debounceMs,
6322
- trigger: entry.trigger,
6323
- condition: entry.condition,
6324
- stopOnUserOverride: entry.stopOnUserOverride,
6325
- reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
6326
- };
6327
- return `${entry.fieldKey}:${JSON.stringify(config)}`;
6328
- }
6329
- function propertyHttpEntrySignature(entry) {
6330
- const config = {
6331
- http: entry.http,
6332
- responseExpression: entry.responseExpression,
6333
- dependsOn: entry.dependsOn,
6334
- debounceMs: entry.debounceMs,
6335
- trigger: entry.trigger,
6336
- condition: entry.condition,
6337
- };
6338
- return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
6339
- }
6340
- function propertyAsyncEntrySignature(entry) {
6341
- const config = {
6342
- asyncFunctionName: entry.asyncFunctionName,
6343
- asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}.${entry.targetProperty}` : undefined,
6344
- dependsOn: entry.dependsOn,
6345
- debounceMs: entry.debounceMs,
6346
- trigger: entry.trigger,
6347
- condition: entry.condition,
6348
- };
6349
- return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
6350
- }
6351
- function createDerivationOrchestrator(config) {
6352
- return new DerivationOrchestrator(config);
6353
- }
6354
- const DERIVATION_ORCHESTRATOR = new InjectionToken('DERIVATION_ORCHESTRATOR');
6355
- const PROPERTY_DERIVATION_ORCHESTRATOR = new InjectionToken('PROPERTY_DERIVATION_ORCHESTRATOR');
6356
- function injectDerivationOrchestrator() {
6357
- return inject(DERIVATION_ORCHESTRATOR);
6358
- }
6359
- function warnAboutWildcardDependencies(logger, entries, fieldCount) {
6360
- const wildcardEntries = entries.filter((entry) => entry.dependsOn.includes('*'));
6361
- if (wildcardEntries.length === 0)
6362
- return;
6363
- const implicitWildcards = wildcardEntries.filter((entry) => !entry.http &&
6364
- !entry.asyncFunctionName &&
6365
- !entry.asyncFn &&
6366
- (entry.functionName || entry.fn) &&
6367
- (!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
6368
- if (implicitWildcards.length > 0) {
6369
- const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey} (${e.functionName ?? '<inline fn>'})`);
6370
- logger.warn('Derivation: custom functions without explicit dependsOn detected. ' +
6371
- `These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). ` +
6372
- 'Consider specifying explicit dependsOn arrays for better performance.', derivationDescs);
6373
- }
6374
- }
6375
- function warnAboutMisconfiguredReEngagement(logger, entries) {
6376
- const misconfigured = entries.filter((entry) => entry.reEngageOnDependencyChange && !entry.stopOnUserOverride);
6377
- if (misconfigured.length === 0)
6378
- return;
6379
- const fieldKeys = misconfigured.map((e) => e.debugName ?? e.fieldKey);
6380
- logger.warn('Derivation: reEngageOnDependencyChange without stopOnUserOverride detected. ' +
6381
- 'reEngageOnDependencyChange only takes effect when stopOnUserOverride is true. ' +
6382
- 'Either add stopOnUserOverride: true or remove reEngageOnDependencyChange.', fieldKeys);
6383
- }
6384
- function warnAboutWildcardPropertyDependencies(logger, entries, fieldCount) {
6385
- const implicitWildcards = entries.filter((entry) => entry.dependsOn.includes('*') &&
6386
- (entry.functionName || entry.fn) &&
6387
- (!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
6388
- if (implicitWildcards.length > 0) {
6389
- const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey}.${e.targetProperty} (${e.functionName ?? '<inline fn>'})`);
6390
- logger.warn('PropertyDerivation: custom functions without explicit dependsOn detected. ' +
6391
- `These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). ` +
6392
- 'Consider specifying explicit dependsOn arrays for better performance.', derivationDescs);
6393
- }
6394
- }
6395
-
6396
- class ActiveDerivationLogger {
6397
- logger;
6398
- config = createDefaultDerivationLogConfig();
6399
- setConfig(config) {
6400
- this.config = config;
6401
- }
6402
- cycleStart(trigger, entryCount) {
6403
- if (!shouldLog(this.config, 'verbose'))
6404
- return;
6405
- this.logger.debug(`Derivation - Starting cycle (${trigger}) with ${entryCount} derivation(s)`);
6406
- }
6407
- iteration(iterationNumber) {
6408
- if (!shouldLog(this.config, 'verbose'))
6409
- return;
6410
- this.logger.debug(`Derivation - Iteration ${iterationNumber}`);
6411
- }
6412
- evaluation(entry) {
6413
- if (!shouldLog(this.config, 'verbose'))
6414
- return;
6415
- const name = entry.debugName ? `"${entry.debugName}"` : entry.fieldKey;
6416
- switch (entry.result) {
6417
- case 'applied':
6418
- this.logger.debug(`Derivation - Applied ${name}`, {
6419
- field: entry.fieldKey,
6420
- previousValue: entry.previousValue,
6421
- newValue: entry.newValue,
6422
- ...(entry.durationMs !== undefined && { durationMs: entry.durationMs }),
6423
- });
6424
- break;
6425
- case 'skipped':
6426
- this.logger.debug(`Derivation - Skipped ${name} (${this.formatSkipReason(entry.skipReason)})`, {
6427
- field: entry.fieldKey,
6428
- reason: entry.skipReason,
6429
- });
6430
- break;
6431
- case 'error':
6432
- this.logger.debug(`Derivation - Error ${name}`, {
6433
- field: entry.fieldKey,
6434
- error: entry.error,
6435
- });
6436
- break;
6437
- }
6438
- }
6439
- summary(result, trigger) {
6440
- if (!shouldLog(this.config, 'summary'))
6441
- return;
6442
- if (result.appliedCount === 0 && result.errorCount === 0)
6443
- return;
6444
- this.logger.debug(`Derivation - Cycle complete (${trigger})`, {
6445
- applied: result.appliedCount,
6446
- skipped: result.skippedCount,
6447
- errors: result.errorCount,
6448
- iterations: result.iterations,
6449
- });
6450
- }
6451
- maxIterationsReached(result, trigger) {
6452
- this.logger.warn(`Derivation - Max iterations reached (${trigger}). ` +
6453
- `This may indicate a loop in derivation logic. ` +
6454
- `Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
6455
- }
6456
- formatSkipReason(reason) {
6457
- switch (reason) {
6458
- case 'condition-false':
6459
- return 'condition not met';
6460
- case 'value-unchanged':
6461
- return 'value unchanged';
6462
- case 'already-applied':
6463
- return 'already applied this cycle';
6464
- case 'user-override':
6465
- return 'user override active';
6466
- case 'target-not-found':
6467
- return 'target not found';
6468
- default:
6469
- return 'unknown';
6470
- }
6471
- }
6472
- }
6473
- class NoopDerivationLogger {
6474
- cycleStart() {
6475
- /* no-op */
6476
- }
6477
- iteration() {
6478
- /* no-op */
6479
- }
6480
- evaluation() {
6481
- /* no-op */
6482
- }
6483
- summary() {
6484
- /* no-op */
6485
- }
6486
- maxIterationsReached() {
6487
- /* no-op */
6488
- }
6489
- }
6490
- const NOOP_INSTANCE = new NoopDerivationLogger();
6491
- function createDerivationLogger(config, logger) {
6492
- const effectiveConfig = config ?? createDefaultDerivationLogConfig();
6493
- if (effectiveConfig.level === 'none') {
6494
- return NOOP_INSTANCE;
6495
- }
6496
- const service = new ActiveDerivationLogger();
6497
- service.logger = logger;
6498
- service.setConfig(effectiveConfig);
6499
- return service;
6500
- }
6501
-
6502
- function isDynamicFormFeature(value) {
6503
- return (typeof value === 'object' &&
6504
- value !== null &&
6505
- 'ɵkind' in value &&
6506
- typeof value.ɵkind === 'string' &&
6507
- 'ɵproviders' in value &&
6508
- Array.isArray(value.ɵproviders));
6509
- }
6510
- function createFeature(kind, providers) {
6511
- return {
6512
- ɵkind: kind,
6513
- ɵproviders: providers,
6514
- };
6515
- }
6516
-
6517
- class ConsoleLogger {
6518
- prefix = '[Dynamic Forms]';
6519
- debug(message, ...args) {
6520
- console.debug(this.prefix, message, ...args);
6521
- }
6522
- info(message, ...args) {
6523
- console.info(this.prefix, message, ...args);
6524
- }
6525
- warn(message, ...args) {
6526
- console.warn(this.prefix, message, ...args);
6527
- }
6528
- error(message, ...args) {
6529
- console.error(this.prefix, message, ...args);
6530
- }
6531
- }
6532
-
6533
- const DERIVATION_LOG_CONFIG = new InjectionToken('DERIVATION_LOG_CONFIG', {
6534
- providedIn: 'root',
6535
- factory: createDefaultDerivationLogConfig,
6536
- });
6537
- function withLoggerConfig(config = true) {
6538
- if (typeof config === 'boolean' || typeof config === 'function') {
6539
- const isEnabled = typeof config === 'function' ? config() : config;
6540
- const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
6541
- return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
6542
- }
6543
- const isEnabled = config.enabled !== false;
6544
- const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
6545
- const providers = [{ provide: DynamicFormLogger, useValue: logger }];
6546
- if (config.derivations !== undefined) {
6547
- providers.push({
6548
- provide: DERIVATION_LOG_CONFIG,
6549
- useValue: { level: config.derivations },
6550
- });
6551
- }
6552
- return createFeature('logger', providers);
6553
- }
6554
-
6555
- const ADDON_ACTION_HANDLERS = new InjectionToken('ADDON_ACTION_HANDLERS');
6556
- const ADDON_ACTION_REGISTRY = new InjectionToken('ADDON_ACTION_REGISTRY');
6557
- function createAddonActionRegistry() {
6558
- const sources = inject(ADDON_ACTION_HANDLERS, { optional: true }) ?? [];
6559
- const map = new Map();
6560
- for (const handlers of sources) {
6561
- for (const [name, fn] of Object.entries(handlers)) {
6562
- map.set(name, fn);
6563
- }
6564
- }
6565
- return map;
6566
- }
6567
-
6568
- function coreProviders() {
6569
- return [
6570
- EventBus,
6571
- { provide: CONTAINER_FIELD_PROCESSORS, useFactory: createContainerFieldProcessors },
6572
- SchemaRegistryService,
6573
- FunctionRegistryService,
6574
- {
6575
- provide: FORM_STATE_DEPS,
6576
- useFactory: () => ({ config: null, formOptions: null, value: null, source: null }),
6577
- },
6578
- FormStateManager,
6579
- {
6580
- provide: RootFormRegistryService,
6581
- useFactory: (stateManager) => new RootFormRegistryService(stateManager.formValue, computed(() => stateManager.form())),
6582
- deps: [FormStateManager],
6583
- },
6584
- FieldContextRegistryService,
6585
- ArrayItemRegistryService,
6586
- {
6587
- provide: DEFAULT_PROPS,
6588
- useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultProps),
6589
- deps: [FormStateManager],
6590
- },
6591
- {
6592
- provide: DEFAULT_WRAPPERS,
6593
- useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultWrappers),
6594
- deps: [FormStateManager],
6595
- },
6596
- {
6597
- provide: DEFAULT_VALIDATION_MESSAGES,
6598
- useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultValidationMessages),
6599
- deps: [FormStateManager],
6600
- },
6601
- {
6602
- provide: FORM_OPTIONS,
6603
- useFactory: (stateManager) => stateManager.effectiveFormOptions,
6604
- deps: [FormStateManager],
6605
- },
6606
- {
6607
- provide: EXTERNAL_DATA,
6608
- useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.externalData),
6609
- deps: [FormStateManager],
6610
- },
6611
- FormIdPrefixService,
6612
- {
6613
- provide: FORM_ID_PREFIX,
6614
- useFactory: (svc) => svc.prefix,
6615
- deps: [FormIdPrefixService],
6616
- },
6617
- { provide: DEPRECATION_WARNING_TRACKER, useFactory: createWarningTracker },
6618
- { provide: ADDON_ACTION_REGISTRY, useFactory: createAddonActionRegistry },
6619
- ];
6620
- }
6621
- function derivationProviders() {
6622
- return [
6623
- { provide: DERIVATION_WARNING_TRACKER, useFactory: createWarningTracker },
6624
- {
6625
- provide: DERIVATION_ORCHESTRATOR,
6626
- useFactory: (stateManager, logger, logConfig, externalData, propertyStore) => {
6627
- const config = {
6628
- schemaFields: computed(() => stateManager.formSetup()?.schemaFields),
6629
- formValue: stateManager.formValue,
6630
- form: computed(() => stateManager.form()),
6631
- derivationLogger: computed(() => createDerivationLogger(logConfig, logger)),
6632
- propertyStore,
6633
- externalData,
6634
- };
6635
- return createDerivationOrchestrator(config);
6636
- },
6637
- deps: [FormStateManager, DynamicFormLogger, DERIVATION_LOG_CONFIG, EXTERNAL_DATA, PROPERTY_OVERRIDE_STORE],
6638
- },
6639
- { provide: FORM_INITIALIZER, useExisting: DERIVATION_ORCHESTRATOR, multi: true },
6640
- ];
6641
- }
6642
- function propertyDerivationProviders() {
6643
- return [
6644
- { provide: PROPERTY_OVERRIDE_STORE, useFactory: createPropertyOverrideStore },
6645
- { provide: PROPERTY_DERIVATION_ORCHESTRATOR, useExisting: DERIVATION_ORCHESTRATOR },
6646
- ];
6647
- }
6648
- function httpExpressionProviders() {
6649
- return [{ provide: HTTP_CONDITION_CACHE, useFactory: () => new HttpConditionCache(100) }, HttpConditionFunctionCacheService];
6650
- }
6651
- function asyncExpressionProviders() {
6652
- return [AsyncConditionFunctionCacheService];
6653
- }
6654
- function dynamicValueProviders() {
6655
- return [DynamicValueFunctionCacheService];
6656
- }
6657
- function logicProviders() {
6658
- return [LogicFunctionCacheService];
6659
- }
6660
- function provideDynamicFormDI() {
6661
- return [
6662
- ...coreProviders(),
6663
- ...logicProviders(),
6664
- ...dynamicValueProviders(),
6665
- ...httpExpressionProviders(),
6666
- ...asyncExpressionProviders(),
6667
- ...propertyDerivationProviders(),
6668
- ...derivationProviders(),
6669
- ];
6670
- }
6671
-
6672
- class EventDispatcher {
6673
- bus;
6674
- dispatch(event) {
6675
- this.bus?.emitInstance(event);
6676
- }
6677
- connect(bus) {
6678
- this.bus = bus;
6679
- }
6680
- disconnect() {
6681
- this.bus = undefined;
6682
- }
6683
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6684
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher });
6685
- }
6686
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher, decorators: [{
6687
- type: Injectable
6688
- }] });
6689
-
6690
- class DfTemplate {
6691
- dfTemplate = input.required(...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
6692
- templateRef = inject(TemplateRef);
6693
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
6694
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
6695
- }
6696
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfTemplate, decorators: [{
6697
- type: Directive,
6698
- args: [{
6699
- selector: 'ng-template[dfTemplate]',
6700
- exportAs: 'dfTemplate',
6701
- }]
6702
- }], propDecorators: { dfTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfTemplate", required: true }] }] } });
6703
- class DfFieldTemplateRegistry {
6704
- _map = signal(new Map(), ...(ngDevMode ? [{ debugName: "_map" }] : /* istanbul ignore next */ []));
6705
- map = this._map.asReadonly();
6706
- set(value) {
6707
- this._map.set(value);
6708
- }
6709
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6710
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry });
6711
- }
6712
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry, decorators: [{
6713
- type: Injectable
6714
- }] });
4419
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry, decorators: [{
4420
+ type: Injectable
4421
+ }] });
6715
4422
 
6716
4423
  class DynamicForm {
6717
4424
  config = input.required({ ...(ngDevMode ? { debugName: "config" } : /* istanbul ignore next */ {}), alias: 'dynamic-form' });
6718
- formOptions = input(undefined, ...(ngDevMode ? [{ debugName: "formOptions" }] : /* istanbul ignore next */ []));
6719
- value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
6720
- source = input('inline', ...(ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []));
4425
+ formOptions = input(undefined, /* @ts-ignore */
4426
+ ...(ngDevMode ? [{ debugName: "formOptions" }] : /* istanbul ignore next */ []));
4427
+ value = model(undefined, /* @ts-ignore */
4428
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4429
+ source = input('inline', /* @ts-ignore */
4430
+ ...(ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []));
6721
4431
  destroyRef = inject(DestroyRef);
6722
4432
  injector = inject(Injector);
6723
4433
  environmentInjector = inject(EnvironmentInjector);
@@ -6725,7 +4435,8 @@ class DynamicForm {
6725
4435
  logger = inject(DynamicFormLogger);
6726
4436
  dispatcher = inject(EventDispatcher, { optional: true });
6727
4437
  templateRegistry = inject(DfFieldTemplateRegistry);
6728
- _projectedTemplates = contentChildren(DfTemplate, ...(ngDevMode ? [{ debugName: "_projectedTemplates" }] : /* istanbul ignore next */ []));
4438
+ _projectedTemplates = contentChildren(DfTemplate, /* @ts-ignore */
4439
+ ...(ngDevMode ? [{ debugName: "_projectedTemplates" }] : /* istanbul ignore next */ []));
6729
4440
  stateManager = this.connectDeps();
6730
4441
  formIdPrefix = inject(FormIdPrefixService);
6731
4442
  idPrefix = this.formIdPrefix.prefix;
@@ -6747,12 +4458,15 @@ class DynamicForm {
6747
4458
  disabled = this.stateManager.disabled;
6748
4459
  submitting = this.stateManager.submitting;
6749
4460
  fieldLoadingErrors = this.stateManager.fieldLoadingErrors;
6750
- shouldRender = this.stateManager.shouldRender;
4461
+ derivationReady = inject(DERIVATION_RENDER_GATE);
4462
+ shouldRender = computed(() => this.stateManager.shouldRender() && this.derivationReady(), /* @ts-ignore */
4463
+ ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
6751
4464
  resolvedFields = this.stateManager.resolvedFields;
6752
4465
  totalComponentsCount = computed(() => {
6753
4466
  const fields = this.stateManager.formSetup()?.fields ?? [];
6754
4467
  return countContainersRecursive(fields) + 1;
6755
- }, ...(ngDevMode ? [{ debugName: "totalComponentsCount" }] : /* istanbul ignore next */ []));
4468
+ }, /* @ts-ignore */
4469
+ ...(ngDevMode ? [{ debugName: "totalComponentsCount" }] : /* istanbul ignore next */ []));
6756
4470
  initialized$ = setupInitializationTracking({
6757
4471
  eventBus: this.eventBus,
6758
4472
  totalComponentsCount: this.totalComponentsCount,
@@ -6777,7 +4491,7 @@ class DynamicForm {
6777
4491
  }
6778
4492
  onNativeSubmit(event) {
6779
4493
  event.preventDefault();
6780
- this.eventBus.dispatch(SubmitEvent);
4494
+ this.eventBus.dispatch(FormSubmitEvent);
6781
4495
  }
6782
4496
  setupEffects() {
6783
4497
  explicitEffect([this.formModeDetection, this.pageFieldDefinitions], ([{ mode }, pages]) => {
@@ -6821,8 +4535,8 @@ class DynamicForm {
6821
4535
  error: (err) => this.logger.error('Submission handler error', err),
6822
4536
  });
6823
4537
  }
6824
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
6825
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicForm, isStandalone: true, selector: "form[dynamic-form]", inputs: { config: { classPropertyName: "config", publicName: "dynamic-form", isSignal: true, isRequired: true, transformFunction: null }, formOptions: { classPropertyName: "formOptions", publicName: "formOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted", reset: "reset", cleared: "cleared", events: "events", initialized: "initialized", onPageChange: "onPageChange", onPageNavigationStateChange: "onPageNavigationStateChange" }, host: { attributes: { "novalidate": "" }, listeners: { "submit": "onNativeSubmit($event)" }, properties: { "class.disabled": "disabled()", "class.df-form-paged": "formModeDetection().mode === \"paged\"", "class.df-form-non-paged": "formModeDetection().mode === \"non-paged\"", "attr.data-form-mode": "formModeDetection().mode", "attr.id": "idPrefix() || null" }, classAttribute: "df-dynamic-form df-form" }, providers: [
4538
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
4539
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: DynamicForm, isStandalone: true, selector: "form[dynamic-form]", inputs: { config: { classPropertyName: "config", publicName: "dynamic-form", isSignal: true, isRequired: true, transformFunction: null }, formOptions: { classPropertyName: "formOptions", publicName: "formOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted", reset: "reset", cleared: "cleared", events: "events", initialized: "initialized", onPageChange: "onPageChange", onPageNavigationStateChange: "onPageNavigationStateChange" }, host: { attributes: { "novalidate": "" }, listeners: { "submit": "onNativeSubmit($event)" }, properties: { "class.disabled": "disabled()", "class.df-form-paged": "formModeDetection().mode === \"paged\"", "class.df-form-non-paged": "formModeDetection().mode === \"non-paged\"", "attr.data-form-mode": "formModeDetection().mode", "attr.id": "idPrefix() || null" }, classAttribute: "df-dynamic-form df-form" }, providers: [
6826
4540
  provideDynamicFormDI(),
6827
4541
  DfFieldTemplateRegistry,
6828
4542
  { provide: DF_FIELD_TEMPLATES, useFactory: () => inject(DfFieldTemplateRegistry).map },
@@ -6850,7 +4564,7 @@ class DynamicForm {
6850
4564
  }
6851
4565
  `, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--df-form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{gap:0}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }, { kind: "component", type: PageOrchestratorComponent, selector: "div[page-orchestrator]", inputs: ["pageFields", "form", "fieldSignalContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6852
4566
  }
6853
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicForm, decorators: [{
4567
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicForm, decorators: [{
6854
4568
  type: Component,
6855
4569
  args: [{ selector: 'form[dynamic-form]', imports: [DfFieldOutlet, PageOrchestratorComponent], template: `
6856
4570
  @if (shouldRender()) {
@@ -6911,87 +4625,38 @@ function countContainersRecursive(fields) {
6911
4625
  return count;
6912
4626
  }
6913
4627
 
6914
- const ADDON_KIND_COMPONENT_CACHE = new InjectionToken('ADDON_KIND_COMPONENT_CACHE');
6915
- function injectAddonKindRegistry() {
6916
- const registry = inject(ADDON_KIND_REGISTRY);
6917
- const componentCache = inject(ADDON_KIND_COMPONENT_CACHE);
6918
- return {
6919
- getKind(kind) {
6920
- return registry.get(kind);
6921
- },
6922
- hasKind(kind) {
6923
- return registry.has(kind);
6924
- },
6925
- getKindNames() {
6926
- return Array.from(registry.keys());
6927
- },
6928
- async loadKindComponent(kind) {
6929
- const definition = registry.get(kind);
6930
- if (!definition) {
6931
- const known = Array.from(registry.keys()).join(', ') || '(none)';
6932
- throw new DynamicFormError(`Addon kind '${kind}' is not registered. Currently registered kinds: ${known}.`);
6933
- }
6934
- const cached = componentCache.get(kind);
6935
- if (cached) {
6936
- return cached;
6937
- }
6938
- try {
6939
- const component = resolveDefaultExport(await definition.loadComponent());
6940
- if (component) {
6941
- componentCache.set(kind, component);
6942
- return component;
6943
- }
6944
- throw new DynamicFormError(`Addon kind '${kind}' loadComponent resolved to null.`);
6945
- }
6946
- catch (error) {
6947
- if (error instanceof DynamicFormError)
6948
- throw error;
6949
- throw new DynamicFormError(`Failed to load component for addon kind '${kind}': ${error instanceof Error ? error.message : String(error)}`);
6950
- }
6951
- },
6952
- getLoadedKindComponent(kind) {
6953
- if (!registry.has(kind))
6954
- return undefined;
6955
- return componentCache.get(kind);
6956
- },
6957
- get raw() {
6958
- return registry;
6959
- },
6960
- };
6961
- }
6962
-
6963
- const BUILT_IN_ADDON_KINDS = [
4628
+ const BUILT_IN_ADDON_TYPES = [
6964
4629
  {
6965
- kind: 'text',
4630
+ type: 'text',
6966
4631
  loadComponent: () => Promise.resolve().then(function () { return textAddon_component; }).then((m) => m.TextAddonComponent),
6967
4632
  validate: (addon, fieldKey) => {
6968
4633
  const text = addon.text;
6969
4634
  if (text === undefined || text === null) {
6970
- throw new DynamicFormError(`Addon kind 'text' requires a 'text' field (field: '${fieldKey}').`);
4635
+ throw new DynamicFormError(`Addon type 'text' requires a 'text' field (field: '${fieldKey}').`);
6971
4636
  }
6972
4637
  if (typeof text === 'string' && text.length === 0) {
6973
- throw new DynamicFormError(`Addon kind 'text' requires a non-empty 'text' string (field: '${fieldKey}').`);
4638
+ throw new DynamicFormError(`Addon type 'text' requires a non-empty 'text' string (field: '${fieldKey}').`);
6974
4639
  }
6975
4640
  },
6976
4641
  },
6977
4642
  {
6978
- kind: 'template',
4643
+ type: 'template',
6979
4644
  loadComponent: () => Promise.resolve().then(function () { return templateAddon_component; }).then((m) => m.TemplateAddonComponent),
6980
4645
  validate: (addon, fieldKey) => {
6981
4646
  const key = addon.templateKey;
6982
4647
  if (typeof key !== 'string' || key.length === 0) {
6983
- throw new DynamicFormError(`Addon kind 'template' requires a non-empty 'templateKey' string (field: '${fieldKey}').`);
4648
+ throw new DynamicFormError(`Addon type 'template' requires a non-empty 'templateKey' string (field: '${fieldKey}').`);
6984
4649
  }
6985
4650
  },
6986
4651
  },
6987
4652
  {
6988
- kind: 'component',
4653
+ type: 'component',
6989
4654
  jsonSafe: false,
6990
4655
  loadComponent: () => Promise.resolve().then(function () { return componentAddon_component; }).then((m) => m.ComponentAddonComponent),
6991
4656
  validate: (addon, fieldKey) => {
6992
4657
  const loader = addon.component;
6993
4658
  if (typeof loader !== 'function') {
6994
- throw new DynamicFormError(`Addon kind 'component' requires a 'component' loader function (field: '${fieldKey}').`);
4659
+ throw new DynamicFormError(`Addon type 'component' requires a 'component' loader function (field: '${fieldKey}').`);
6995
4660
  }
6996
4661
  },
6997
4662
  },
@@ -7006,35 +4671,35 @@ const DISPLAY_FIELD_TYPES_BASE = {
7006
4671
  const BUILT_IN_FIELDS = [
7007
4672
  {
7008
4673
  name: 'row',
7009
- loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs'),
4674
+ loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs'),
7010
4675
  mapper: rowFieldMapper,
7011
4676
  valueHandling: 'flatten',
7012
4677
  ...CONTAINER_FIELD_TYPES_BASE,
7013
4678
  },
7014
4679
  {
7015
4680
  name: 'group',
7016
- loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs'),
4681
+ loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-Ca4AqdZu.mjs'),
7017
4682
  mapper: groupFieldMapper,
7018
4683
  valueHandling: 'include',
7019
4684
  ...CONTAINER_FIELD_TYPES_BASE,
7020
4685
  },
7021
4686
  {
7022
4687
  name: 'array',
7023
- loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs'),
4688
+ loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-8rodySeE.mjs'),
7024
4689
  mapper: arrayFieldMapper,
7025
4690
  valueHandling: 'include',
7026
4691
  ...CONTAINER_FIELD_TYPES_BASE,
7027
4692
  },
7028
4693
  {
7029
4694
  name: 'page',
7030
- loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs'),
4695
+ loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs'),
7031
4696
  mapper: pageFieldMapper,
7032
4697
  valueHandling: 'flatten',
7033
4698
  ...CONTAINER_FIELD_TYPES_BASE,
7034
4699
  },
7035
4700
  {
7036
4701
  name: 'text',
7037
- loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs'),
4702
+ loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs'),
7038
4703
  mapper: textFieldMapper,
7039
4704
  valueHandling: 'exclude',
7040
4705
  ...DISPLAY_FIELD_TYPES_BASE,
@@ -7045,7 +4710,7 @@ const BUILT_IN_FIELDS = [
7045
4710
  },
7046
4711
  {
7047
4712
  name: 'container',
7048
- loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs'),
4713
+ loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs'),
7049
4714
  mapper: containerFieldMapper,
7050
4715
  valueHandling: 'flatten',
7051
4716
  ...CONTAINER_FIELD_TYPES_BASE,
@@ -7054,15 +4719,44 @@ const BUILT_IN_FIELDS = [
7054
4719
  const BUILT_IN_WRAPPERS = [
7055
4720
  {
7056
4721
  wrapperName: 'css',
7057
- loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs'),
4722
+ loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs'),
7058
4723
  },
7059
4724
  {
7060
4725
  wrapperName: 'row',
7061
- loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs'),
4726
+ loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs'),
7062
4727
  },
7063
4728
  ];
7064
4729
 
7065
- const ADDON_KIND_DEFINITIONS = new InjectionToken('ADDON_KIND_DEFINITIONS');
4730
+ function isDynamicFormFeature(value) {
4731
+ return (typeof value === 'object' &&
4732
+ value !== null &&
4733
+ 'ɵkind' in value &&
4734
+ typeof value.ɵkind === 'string' &&
4735
+ 'ɵproviders' in value &&
4736
+ Array.isArray(value.ɵproviders));
4737
+ }
4738
+ function createFeature(kind, providers) {
4739
+ return {
4740
+ ɵkind: kind,
4741
+ ɵproviders: providers,
4742
+ };
4743
+ }
4744
+
4745
+ class ConsoleLogger {
4746
+ prefix = '[Dynamic Forms]';
4747
+ debug(message, ...args) {
4748
+ console.debug(this.prefix, message, ...args);
4749
+ }
4750
+ info(message, ...args) {
4751
+ console.info(this.prefix, message, ...args);
4752
+ }
4753
+ warn(message, ...args) {
4754
+ console.warn(this.prefix, message, ...args);
4755
+ }
4756
+ error(message, ...args) {
4757
+ console.error(this.prefix, message, ...args);
4758
+ }
4759
+ }
7066
4760
 
7067
4761
  function createWrappers(...registrations) {
7068
4762
  return {
@@ -7144,28 +4838,50 @@ function provideDynamicForm(...items) {
7144
4838
  },
7145
4839
  },
7146
4840
  {
7147
- provide: ADDON_KIND_REGISTRY,
4841
+ provide: ADDON_TYPE_REGISTRY,
7148
4842
  useFactory: () => {
7149
4843
  const logger = inject(DynamicFormLogger);
7150
- const contributed = inject(ADDON_KIND_DEFINITIONS, { optional: true }) ?? [];
4844
+ const contributed = inject(ADDON_TYPE_DEFINITIONS, { optional: true }) ?? [];
7151
4845
  const registry = new Map();
7152
- for (const def of BUILT_IN_ADDON_KINDS) {
7153
- registry.set(def.kind, def);
4846
+ for (const def of BUILT_IN_ADDON_TYPES) {
4847
+ registry.set(def.type, def);
7154
4848
  }
7155
4849
  for (const def of contributed) {
7156
- if (registry.has(def.kind)) {
7157
- logger.warn(`Addon kind "${def.kind}" is already registered. Overwriting.`);
4850
+ if (registry.has(def.type)) {
4851
+ logger.warn(`Addon type "${def.type}" is already registered. Overwriting.`);
7158
4852
  }
7159
- registry.set(def.kind, def);
4853
+ registry.set(def.type, def);
7160
4854
  }
7161
4855
  return registry;
7162
4856
  },
7163
4857
  },
7164
- { provide: ADDON_KIND_COMPONENT_CACHE, useFactory: () => new Map() },
4858
+ { provide: ADDON_TYPE_COMPONENT_CACHE, useFactory: () => new Map() },
7165
4859
  ...featureProviders,
7166
4860
  ]);
7167
4861
  }
7168
4862
 
4863
+ const DERIVATION_LOG_CONFIG = new InjectionToken('DERIVATION_LOG_CONFIG', {
4864
+ providedIn: 'root',
4865
+ factory: createDefaultDerivationLogConfig,
4866
+ });
4867
+ function withLoggerConfig(config = true) {
4868
+ if (typeof config === 'boolean' || typeof config === 'function') {
4869
+ const isEnabled = typeof config === 'function' ? config() : config;
4870
+ const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
4871
+ return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
4872
+ }
4873
+ const isEnabled = config.enabled !== false;
4874
+ const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
4875
+ const providers = [{ provide: DynamicFormLogger, useValue: logger }];
4876
+ if (config.derivations !== undefined) {
4877
+ providers.push({
4878
+ provide: DERIVATION_LOG_CONFIG,
4879
+ useValue: { level: config.derivations },
4880
+ });
4881
+ }
4882
+ return createFeature('logger', providers);
4883
+ }
4884
+
7169
4885
  function withEventFormValue() {
7170
4886
  return createFeature('event-form-value', [{ provide: EMIT_FORM_VALUE_ON_EVENTS, useValue: true }]);
7171
4887
  }
@@ -7194,94 +4910,15 @@ function wrapperProps() {
7194
4910
  return undefined;
7195
4911
  }
7196
4912
 
7197
- class DfAddonSlot {
7198
- registry = injectAddonKindRegistry();
7199
- logger = inject(DynamicFormLogger);
7200
- hostInjector = inject(Injector);
7201
- addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
7202
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
7203
- hidden = input(undefined, ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
7204
- slotAttr = computed(() => this.addon().slot, ...(ngDevMode ? [{ debugName: "slotAttr" }] : /* istanbul ignore next */ []));
7205
- className = computed(() => this.addon().className ?? null, ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
7206
- _hiddenByValue = new WeakMap();
7207
- _resolvedHidden = signal(undefined, ...(ngDevMode ? [{ debugName: "_resolvedHidden" }] : /* istanbul ignore next */ []));
7208
- isHidden = computed(() => {
7209
- const pre = this.hidden();
7210
- if (pre)
7211
- return pre();
7212
- const local = this._resolvedHidden();
7213
- return local ? local() : false;
7214
- }, ...(ngDevMode ? [{ debugName: "isHidden" }] : /* istanbul ignore next */ []));
7215
- asyncLoaded = signal(undefined, ...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
7216
- resolvedComponent = computed(() => {
7217
- const kind = this.addon().kind;
7218
- const cached = this.registry.getLoadedKindComponent(kind);
7219
- if (cached)
7220
- return cached;
7221
- const loaded = this.asyncLoaded();
7222
- return loaded?.kind === kind ? loaded.component : undefined;
7223
- }, ...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
7224
- kindInputs = computed(() => ({
7225
- addon: this.addon(),
7226
- fieldInputs: this.fieldInputs(),
7227
- }), ...(ngDevMode ? [{ debugName: "kindInputs" }] : /* istanbul ignore next */ []));
7228
- constructor() {
7229
- explicitEffect([this.addon], ([addon]) => {
7230
- const h = addon.hidden;
7231
- const valueKey = typeof h === 'object' && h !== null ? h : null;
7232
- let resolved = valueKey ? this._hiddenByValue.get(valueKey) : undefined;
7233
- if (!resolved) {
7234
- resolved = resolveDynamicValue(addon.hidden, false, this.hostInjector);
7235
- if (valueKey)
7236
- this._hiddenByValue.set(valueKey, resolved);
7237
- }
7238
- this._resolvedHidden.set(resolved);
7239
- });
7240
- explicitEffect([this.addon], ([addon]) => {
7241
- const kind = addon.kind;
7242
- if (this.registry.getLoadedKindComponent(kind))
7243
- return; this.registry
7244
- .loadKindComponent(kind)
7245
- .then((component) => this.asyncLoaded.set({ kind, component }))
7246
- .catch((error) => {
7247
- this.logger.warn(`Failed to load addon kind '${kind}': ${error instanceof Error ? error.message : String(error)}. ` +
7248
- `Registered kinds: ${this.registry.getKindNames().join(', ') || '(none)'}.`);
7249
- });
7250
- });
7251
- }
7252
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfAddonSlot, deps: [], target: i0.ɵɵFactoryTarget.Component });
7253
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DfAddonSlot, isStandalone: true, selector: "df-addon-slot", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.slot": "slotAttr()", "class": "className()", "style.display": "isHidden() ? \"none\" : null" } }, ngImport: i0, template: `
7254
- @if (resolvedComponent(); as cmp) {
7255
- <ng-container *ngComponentOutlet="cmp; inputs: kindInputs()" />
7256
- }
7257
- `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7258
- }
7259
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfAddonSlot, decorators: [{
7260
- type: Component,
7261
- args: [{
7262
- selector: 'df-addon-slot',
7263
- imports: [NgComponentOutlet],
7264
- template: `
7265
- @if (resolvedComponent(); as cmp) {
7266
- <ng-container *ngComponentOutlet="cmp; inputs: kindInputs()" />
7267
- }
7268
- `,
7269
- host: {
7270
- '[attr.slot]': 'slotAttr()',
7271
- '[class]': 'className()',
7272
- '[style.display]': 'isHidden() ? "none" : null',
7273
- },
7274
- changeDetection: ChangeDetectionStrategy.OnPush,
7275
- }]
7276
- }], ctorParameters: () => [], propDecorators: { addon: [{ type: i0.Input, args: [{ isSignal: true, alias: "addon", required: true }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }] } });
7277
-
7278
4913
  class TextAddonComponent {
7279
- addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
7280
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
7281
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TextAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7282
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", type: TextAddonComponent, isStandalone: true, selector: "df-text-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "\"true\"" } }, ngImport: i0, template: `<span>{{ addon().text | dynamicText | async }}</span>`, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7283
- }
7284
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TextAddonComponent, decorators: [{
4914
+ addon = input.required(/* @ts-ignore */
4915
+ ...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
4916
+ fieldInputs = input(/* @ts-ignore */
4917
+ ...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
4918
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TextAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4919
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", type: TextAddonComponent, isStandalone: true, selector: "df-text-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "\"true\"" } }, ngImport: i0, template: `<span>{{ addon().text | dynamicText | async }}</span>`, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4920
+ }
4921
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TextAddonComponent, decorators: [{
7285
4922
  type: Component,
7286
4923
  args: [{
7287
4924
  selector: 'df-text-addon',
@@ -7302,14 +4939,18 @@ var textAddon_component = /*#__PURE__*/Object.freeze({
7302
4939
  class TemplateAddonComponent {
7303
4940
  templates = inject(DF_FIELD_TEMPLATES, { optional: true });
7304
4941
  logger = inject(DynamicFormLogger);
7305
- addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
7306
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
4942
+ addon = input.required(/* @ts-ignore */
4943
+ ...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
4944
+ fieldInputs = input(/* @ts-ignore */
4945
+ ...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
7307
4946
  template = computed(() => {
7308
4947
  const map = this.templates?.();
7309
4948
  const key = this.addon().templateKey;
7310
4949
  return map?.get(key) ?? null;
7311
- }, ...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
7312
- outletContext = computed(() => ({ $implicit: this.fieldInputs() }), ...(ngDevMode ? [{ debugName: "outletContext" }] : /* istanbul ignore next */ []));
4950
+ }, /* @ts-ignore */
4951
+ ...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
4952
+ outletContext = computed(() => ({ $implicit: this.fieldInputs() }), /* @ts-ignore */
4953
+ ...(ngDevMode ? [{ debugName: "outletContext" }] : /* istanbul ignore next */ []));
7313
4954
  constructor() {
7314
4955
  explicitEffect([this.template, this.addon], ([tpl, addon]) => {
7315
4956
  if (!tpl) {
@@ -7317,14 +4958,14 @@ class TemplateAddonComponent {
7317
4958
  }
7318
4959
  });
7319
4960
  }
7320
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TemplateAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7321
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: TemplateAddonComponent, isStandalone: true, selector: "df-template-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4961
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TemplateAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4962
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: TemplateAddonComponent, isStandalone: true, selector: "df-template-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7322
4963
  @if (template(); as tpl) {
7323
4964
  <ng-container *ngTemplateOutlet="tpl; context: outletContext()" />
7324
4965
  }
7325
4966
  `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7326
4967
  }
7327
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TemplateAddonComponent, decorators: [{
4968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TemplateAddonComponent, decorators: [{
7328
4969
  type: Component,
7329
4970
  args: [{
7330
4971
  selector: 'df-template-addon',
@@ -7345,15 +4986,20 @@ var templateAddon_component = /*#__PURE__*/Object.freeze({
7345
4986
 
7346
4987
  class ComponentAddonComponent {
7347
4988
  logger = inject(DynamicFormLogger);
7348
- addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
7349
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
7350
- inputs = computed(() => (this.addon().inputs ?? {}), ...(ngDevMode ? [{ debugName: "inputs" }] : /* istanbul ignore next */ []));
7351
- asyncLoaded = signal(undefined, ...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
4989
+ addon = input.required(/* @ts-ignore */
4990
+ ...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
4991
+ fieldInputs = input(/* @ts-ignore */
4992
+ ...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
4993
+ inputs = computed(() => (this.addon().inputs ?? {}), /* @ts-ignore */
4994
+ ...(ngDevMode ? [{ debugName: "inputs" }] : /* istanbul ignore next */ []));
4995
+ asyncLoaded = signal(undefined, /* @ts-ignore */
4996
+ ...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
7352
4997
  resolvedComponent = computed(() => {
7353
4998
  const current = this.addon();
7354
4999
  const loaded = this.asyncLoaded();
7355
5000
  return loaded?.addon === current ? loaded.component : undefined;
7356
- }, ...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
5001
+ }, /* @ts-ignore */
5002
+ ...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
7357
5003
  constructor() {
7358
5004
  explicitEffect([this.addon], ([addon]) => {
7359
5005
  Promise.resolve(addon.component())
@@ -7367,14 +5013,14 @@ class ComponentAddonComponent {
7367
5013
  });
7368
5014
  });
7369
5015
  }
7370
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7371
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ComponentAddonComponent, isStandalone: true, selector: "df-component-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
5016
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ComponentAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5017
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: ComponentAddonComponent, isStandalone: true, selector: "df-component-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7372
5018
  @if (resolvedComponent(); as cmp) {
7373
5019
  <ng-container *ngComponentOutlet="cmp; inputs: inputs()" />
7374
5020
  }
7375
5021
  `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7376
5022
  }
7377
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentAddonComponent, decorators: [{
5023
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ComponentAddonComponent, decorators: [{
7378
5024
  type: Component,
7379
5025
  args: [{
7380
5026
  selector: 'df-component-addon',
@@ -7393,96 +5039,17 @@ var componentAddon_component = /*#__PURE__*/Object.freeze({
7393
5039
  ComponentAddonComponent: ComponentAddonComponent
7394
5040
  });
7395
5041
 
7396
- async function runPresetAction(preset, ctx, collaborators, adapterLabel, fieldLabel) {
7397
- if (ctx.form === null && preset !== 'copy' && preset !== 'paste') {
7398
- collaborators.logger.warn(`preset '${String(preset)}' fired without a host field context — ignoring.`);
7399
- return;
7400
- }
7401
- if (ctx.form !== null && (preset === 'clear' || preset === 'reset' || preset === 'paste') && ctx.form.disabled()) {
7402
- collaborators.logger.warn(`preset '${String(preset)}' skipped — host '${fieldLabel}' is disabled.`);
7403
- return;
7404
- }
7405
- switch (preset) {
7406
- case 'clear': {
7407
- const next = typeof ctx.value === 'string' ? '' : undefined;
7408
- collaborators.fieldValueSetter?.(next);
7409
- return;
7410
- }
7411
- case 'reset': {
7412
- const defaultValue = collaborators.fieldDefaultValueGetter?.();
7413
- collaborators.fieldValueSetter?.(defaultValue !== undefined ? defaultValue : typeof ctx.value === 'string' ? '' : undefined);
7414
- return;
7415
- }
7416
- case 'paste': {
7417
- if (typeof navigator === 'undefined' || !navigator.clipboard) {
7418
- collaborators.logger.warn(`preset 'paste' requires a browser clipboard API (not available in this environment).`);
7419
- return;
7420
- }
7421
- try {
7422
- const text = await navigator.clipboard.readText();
7423
- collaborators.fieldValueSetter?.(text);
7424
- }
7425
- catch (error) {
7426
- collaborators.logger.warn(`preset 'paste' clipboard read failed: ${error instanceof Error ? error.message : String(error)}`);
7427
- }
7428
- return;
7429
- }
7430
- case 'copy': {
7431
- if (typeof navigator === 'undefined' || !navigator.clipboard) {
7432
- collaborators.logger.warn(`preset 'copy' requires a browser clipboard API (not available in this environment).`);
7433
- return;
7434
- }
7435
- try {
7436
- const v = ctx.value;
7437
- await navigator.clipboard.writeText(v == null ? '' : String(v));
7438
- }
7439
- catch (error) {
7440
- collaborators.logger.warn(`preset 'copy' clipboard write failed: ${error instanceof Error ? error.message : String(error)}`);
7441
- }
7442
- return;
7443
- }
7444
- case 'toggle-password-visibility': {
7445
- const override = collaborators.typeOverride;
7446
- if (!override) {
7447
- collaborators.logger.warn(`preset 'toggle-password-visibility' has no effect outside a '${fieldLabel}' field.`);
7448
- return;
7449
- }
7450
- const baseline = collaborators.baselineType?.();
7451
- if (baseline !== undefined && baseline !== 'password') {
7452
- collaborators.logger.warn(`preset 'toggle-password-visibility' refused: host '${fieldLabel}' has type '${baseline}', not 'password'.`);
7453
- return;
7454
- }
7455
- const current = override();
7456
- override.set(current === 'text' ? 'password' : 'text');
7457
- return;
7458
- }
7459
- default:
7460
- collaborators.logger.warn(`preset '${String(preset)}' is not implemented in the ${adapterLabel} adapter.`);
7461
- }
7462
- }
7463
-
7464
- function injectFieldsSupportingAddons() {
7465
- const registry = inject(FIELD_REGISTRY);
7466
- const entries = [];
7467
- for (const [name, def] of registry.entries()) {
7468
- if (def.addons === undefined)
7469
- continue;
7470
- entries.push({ name, slots: def.addons.slots, allowedKinds: def.addons.allowedKinds });
7471
- }
7472
- return entries;
7473
- }
7474
-
7475
5042
  function withCustomAddon(definition) {
7476
5043
  return createFeature('addons', [
7477
5044
  {
7478
- provide: ADDON_KIND_DEFINITIONS,
5045
+ provide: ADDON_TYPE_DEFINITIONS,
7479
5046
  useValue: definition,
7480
5047
  multi: true,
7481
5048
  },
7482
5049
  ]);
7483
5050
  }
7484
5051
 
7485
- function provideAddonActions(handlers) {
5052
+ function withAddonActions(handlers) {
7486
5053
  return createFeature('addon-actions', [
7487
5054
  {
7488
5055
  provide: ADDON_ACTION_HANDLERS,
@@ -7494,9 +5061,9 @@ function provideAddonActions(handlers) {
7494
5061
 
7495
5062
  function sanitizeFormConfig(config, options = {}) {
7496
5063
  const fieldRegistry = inject(FIELD_REGISTRY);
7497
- const kindRegistry = inject(ADDON_KIND_REGISTRY);
5064
+ const typeRegistry = inject(ADDON_TYPE_REGISTRY);
7498
5065
  const source = options.source ?? 'inline';
7499
- const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry, kindRegistry, source);
5066
+ const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry, typeRegistry, source);
7500
5067
  return {
7501
5068
  sanitized: { ...config, fields: fields },
7502
5069
  warnings,
@@ -7512,9 +5079,9 @@ function logAddonWarnings(warnings, logger) {
7512
5079
  }
7513
5080
  }
7514
5081
  }
7515
- function sanitizeFormConfigPure(config, fieldRegistry, kindRegistry, options = {}) {
5082
+ function sanitizeFormConfigPure(config, fieldRegistry, typeRegistry, options = {}) {
7516
5083
  const source = options.source ?? 'inline';
7517
- const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry, kindRegistry, source);
5084
+ const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry, typeRegistry, source);
7518
5085
  return { sanitized: { ...config, fields: fields }, warnings };
7519
5086
  }
7520
5087
 
@@ -7531,94 +5098,7 @@ function arrayEvent(arrayKey) {
7531
5098
  }
7532
5099
 
7533
5100
 
7534
- function formConfig(config) {
7535
- return config;
7536
- }
7537
-
7538
- const CONTAINER_TYPES = ['group', 'row', 'array', 'container'];
7539
- const HIDDEN_ONLY_LOGIC_TYPES = ['group', 'row', 'array', 'container', 'page'];
7540
- const OPTION_FIELD_TYPES = ['select', 'radio', 'multi-checkbox'];
7541
- const SLIDER_TYPE = 'slider';
7542
- const HIDDEN_TYPE = 'hidden';
7543
- function createField(type, config) {
7544
- const configWithProps = config;
7545
- if (!configWithProps['key']) {
7546
- throw new DynamicFormError(`createField('${type}'): 'key' property is required`);
7547
- }
7548
- if (CONTAINER_TYPES.includes(type)) {
7549
- if ('label' in configWithProps && configWithProps['label'] !== undefined) {
7550
- throw new DynamicFormError(`createField('${type}'): Container fields (${CONTAINER_TYPES.join(', ')}) do NOT support 'label'. ` +
7551
- `Labels go on the child fields inside the container.`);
7552
- }
7553
- }
7554
- if (HIDDEN_ONLY_LOGIC_TYPES.includes(type)) {
7555
- if ('logic' in configWithProps && configWithProps['logic'] !== undefined) {
7556
- const logic = configWithProps['logic'];
7557
- if (Array.isArray(logic)) {
7558
- const nonHiddenLogic = logic.filter((l) => l.type !== 'hidden');
7559
- if (nonHiddenLogic.length > 0) {
7560
- throw new DynamicFormError(`createField('${type}'): Only 'hidden' logic type is supported. ` +
7561
- `Found unsupported logic types: ${nonHiddenLogic.map((l) => l.type).join(', ')}. ` +
7562
- `For other logic types, apply them to child fields instead.`);
7563
- }
7564
- }
7565
- }
7566
- }
7567
- if (OPTION_FIELD_TYPES.includes(type)) {
7568
- const props = configWithProps['props'];
7569
- if (props && 'options' in props) {
7570
- throw new DynamicFormError(`createField('${type}'): 'options' must be at FIELD level, NOT inside 'props'. ` +
7571
- `Move 'options' from props to the field root: { type: '${type}', key: '...', options: [...] }`);
7572
- }
7573
- }
7574
- if (type === SLIDER_TYPE) {
7575
- const props = configWithProps['props'];
7576
- if (props) {
7577
- if ('min' in props || 'max' in props) {
7578
- throw new DynamicFormError(`createField('slider'): Use 'minValue' and 'maxValue' at FIELD level, NOT 'min'/'max' in props. ` +
7579
- `Example: { type: 'slider', key: '...', minValue: 0, maxValue: 100, step: 1 }`);
7580
- }
7581
- if ('step' in props) {
7582
- throw new DynamicFormError(`createField('slider'): Use 'step' at FIELD level, NOT in props. ` +
7583
- `Example: { type: 'slider', key: '...', minValue: 0, maxValue: 100, step: 1 }`);
7584
- }
7585
- }
7586
- }
7587
- if (type === HIDDEN_TYPE) {
7588
- if ('logic' in configWithProps && configWithProps['logic'] !== undefined) {
7589
- throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'logic'. ` + `Hidden fields are purely for passing values through the form.`);
7590
- }
7591
- if ('validators' in configWithProps && configWithProps['validators'] !== undefined) {
7592
- throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'validators'. ` +
7593
- `Hidden fields are purely for passing values through the form.`);
7594
- }
7595
- if ('required' in configWithProps && configWithProps['required']) {
7596
- throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'required' validation. ` +
7597
- `Hidden fields are purely for passing values through the form.`);
7598
- }
7599
- if ('label' in configWithProps && configWithProps['label'] !== undefined) {
7600
- throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'label'. ` +
7601
- `Hidden fields are not rendered and have no visual representation.`);
7602
- }
7603
- }
7604
- if (type === 'array') {
7605
- if ('template' in configWithProps) {
7606
- throw new DynamicFormError(`createField('array'): Use 'fields' (NOT 'template') to define the array item structure. ` +
7607
- `Example: { type: 'array', key: '...', fields: [{ type: 'group', key: 'item', fields: [...] }] }`);
7608
- }
7609
- }
7610
- if (type === 'button') {
7611
- if (!('event' in configWithProps) || configWithProps['event'] === undefined) {
7612
- throw new DynamicFormError(`createField('button'): Generic buttons REQUIRE the 'event' property with a FormEventConstructor. ` +
7613
- `For common actions, use 'submit', 'next', or 'previous' instead which don't require event configuration. ` +
7614
- `Example: { type: 'submit', key: 'submit', label: 'Submit' }`);
7615
- }
7616
- }
7617
- return { type, ...config };
7618
- }
7619
- const field = createField;
7620
-
7621
5101
 
7622
5102
 
7623
- export { sanitizeFormConfig as $, ADDON_ACTION_HANDLERS as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfAddonSlot as D, EventDispatcher as E, FormClearEvent as F, formatAddonWarning as G, getFieldDefaultValue as H, InsertArrayItemEvent as I, getNormalizedArrayMetadata as J, injectAddonKindRegistry as K, injectFieldRegistry as L, MoveArrayItemEvent as M, NextPageEvent as N, injectFieldsSupportingAddons as O, PageChangeEvent as P, isSameWrapperChain as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, isWrappersBundle as U, logAddonWarnings as V, mapFieldToInputs as W, provideAddonActions as X, provideDynamicForm as Y, resolveWrappers as Z, runPresetAction as _, ADDON_ACTION_REGISTRY as a, sanitizeFormConfigPure as a0, validateFieldAddons as a1, walkAndValidateAddons as a2, withCustomAddon as a3, withEventFormValue as a4, withLegacyStatusClasses as a5, withLoggerConfig as a6, withValidationExecutionDefaults as a7, withValueExclusionDefaults as a8, wrapperProps as a9, ADDON_KIND_COMPONENT_CACHE as b, ADDON_KIND_DEFINITIONS as c, AppendArrayItemEvent as d, ArrayItemRegistryService as e, ComponentAddonComponent as f, ConsoleLogger as g, DfFieldOutlet as h, DfTemplate as i, DynamicForm as j, FormResetEvent as k, PopArrayItemEvent as l, PrependArrayItemEvent as m, PreviousPageEvent as n, SubmitEvent as o, TextAddonComponent as p, arrayEvent as q, createField as r, createFieldResolutionPipe as s, createHiddenSignal as t, createRenderReadySignal as u, createSchemaFromFields as v, createWrapperChainController as w, createWrappers as x, field as y, formConfig as z };
7624
- //# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map
5103
+ export { withValueExclusionDefaults as $, ArrayItemRegistryService as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfFieldOutlet as D, EventDispatcher as E, FormStateManager as F, PrependArrayItemEvent as G, PreviousPageEvent as H, InsertArrayItemEvent as I, TextAddonComponent as J, arrayEvent as K, createWrappers as L, MoveArrayItemEvent as M, NextPageEvent as N, isWrappersBundle as O, PROPERTY_OVERRIDE_STORE as P, provideDynamicForm as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, sanitizeFormConfig as U, withAddonActions as V, withCustomAddon as W, withEventFormValue as X, withLegacyStatusClasses as Y, withLoggerConfig as Z, withValidationExecutionDefaults as _, isSameWrapperChain as a, wrapperProps as a0, createWrapperChainController as b, createFieldResolutionPipe as c, createSchemaFromFields as d, createHiddenSignal as e, createRenderReadySignal as f, getNormalizedArrayMetadata as g, getFieldDefaultValue as h, injectFieldRegistry as i, DEV_MODE as j, DEFAULT_DEBOUNCE_MS as k, DERIVATION_WARNING_TRACKER as l, mapFieldToInputs as m, DERIVATION_LOG_CONFIG as n, AppendArrayItemEvent as o, ComponentAddonComponent as p, ConsoleLogger as q, resolveWrappers as r, DfTemplate as s, traverseFieldsWithContext as t, DynamicForm as u, FormClearEvent as v, FormResetEvent as w, FormSubmitEvent as x, PageChangeEvent as y, PopArrayItemEvent as z };
5104
+ //# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-BJi_rtey.mjs.map