@ng-forge/dynamic-forms 1.3.0-next.2 → 1.3.0-next.3

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 (92) hide show
  1. package/fesm2022/ng-forge-dynamic-forms-active-page-initialized.event-CiKN-mcc.mjs +12 -0
  2. package/fesm2022/ng-forge-dynamic-forms-active-page-initialized.event-CiKN-mcc.mjs.map +1 -0
  3. package/fesm2022/ng-forge-dynamic-forms-array-field.component-Dv9mPnJ3.mjs +770 -0
  4. package/fesm2022/ng-forge-dynamic-forms-array-field.component-Dv9mPnJ3.mjs.map +1 -0
  5. package/fesm2022/ng-forge-dynamic-forms-array-item-registry.service-D-AxVLdk.mjs +83 -0
  6. package/fesm2022/ng-forge-dynamic-forms-array-item-registry.service-D-AxVLdk.mjs.map +1 -0
  7. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-EuYCbquo.mjs +1887 -0
  8. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-EuYCbquo.mjs.map +1 -0
  9. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-2wQK_wPp.mjs +954 -0
  10. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-2wQK_wPp.mjs.map +1 -0
  11. package/fesm2022/ng-forge-dynamic-forms-container-field-processors-C_JRvaVt.mjs +117 -0
  12. package/fesm2022/ng-forge-dynamic-forms-container-field-processors-C_JRvaVt.mjs.map +1 -0
  13. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Bg7Win9n.mjs +228 -0
  14. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Bg7Win9n.mjs.map +1 -0
  15. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-R2yc-H0T.mjs +102 -0
  16. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-R2yc-H0T.mjs.map +1 -0
  17. package/fesm2022/ng-forge-dynamic-forms-debounce-CG3uP5B-.mjs +4 -0
  18. package/fesm2022/{ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs.map → ng-forge-dynamic-forms-debounce-CG3uP5B-.mjs.map} +1 -1
  19. package/fesm2022/ng-forge-dynamic-forms-default-value-D5M4MNE-.mjs +77 -0
  20. package/fesm2022/ng-forge-dynamic-forms-default-value-D5M4MNE-.mjs.map +1 -0
  21. package/fesm2022/ng-forge-dynamic-forms-dev-mode-4rbYBBo_.mjs +4 -0
  22. package/fesm2022/ng-forge-dynamic-forms-dev-mode-4rbYBBo_.mjs.map +1 -0
  23. package/fesm2022/ng-forge-dynamic-forms-df-field-outlet.directive-CQsDMV-s.mjs +644 -0
  24. package/fesm2022/ng-forge-dynamic-forms-df-field-outlet.directive-CQsDMV-s.mjs.map +1 -0
  25. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-DKgkpIpG.mjs +56 -0
  26. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-DKgkpIpG.mjs.map +1 -0
  27. package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dd-gHJ_7.mjs +38 -0
  28. package/fesm2022/{ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs.map → ng-forge-dynamic-forms-form-schema.service-Dd-gHJ_7.mjs.map} +1 -1
  29. package/fesm2022/ng-forge-dynamic-forms-form-state-manager-DcOXRFRL.mjs +1522 -0
  30. package/fesm2022/ng-forge-dynamic-forms-form-state-manager-DcOXRFRL.mjs.map +1 -0
  31. package/fesm2022/ng-forge-dynamic-forms-group-field.component-BjZi0bhq.mjs +292 -0
  32. package/fesm2022/ng-forge-dynamic-forms-group-field.component-BjZi0bhq.mjs.map +1 -0
  33. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +1318 -843
  34. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  35. package/fesm2022/ng-forge-dynamic-forms-internal.mjs +3326 -3641
  36. package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
  37. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BRjQmWC9.mjs +295 -0
  38. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BRjQmWC9.mjs.map +1 -0
  39. package/fesm2022/ng-forge-dynamic-forms-resolve-field-ByGa5cpo.mjs +329 -0
  40. package/fesm2022/ng-forge-dynamic-forms-resolve-field-ByGa5cpo.mjs.map +1 -0
  41. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CuFycQb-.mjs +81 -0
  42. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CuFycQb-.mjs.map +1 -0
  43. package/fesm2022/ng-forge-dynamic-forms-schema-builder-C62afItJ.mjs +400 -0
  44. package/fesm2022/ng-forge-dynamic-forms-schema-builder-C62afItJ.mjs.map +1 -0
  45. package/fesm2022/ng-forge-dynamic-forms-schema-registry.service-axBV9FBH.mjs +45 -0
  46. package/fesm2022/ng-forge-dynamic-forms-schema-registry.service-axBV9FBH.mjs.map +1 -0
  47. package/fesm2022/ng-forge-dynamic-forms-schema.mjs +8 -14
  48. package/fesm2022/ng-forge-dynamic-forms-schema.mjs.map +1 -1
  49. package/fesm2022/ng-forge-dynamic-forms-testing.mjs +68 -73
  50. package/fesm2022/ng-forge-dynamic-forms-testing.mjs.map +1 -1
  51. package/fesm2022/ng-forge-dynamic-forms-text-field.component--MAh_h4Q.mjs +257 -0
  52. package/fesm2022/ng-forge-dynamic-forms-text-field.component--MAh_h4Q.mjs.map +1 -0
  53. package/fesm2022/ng-forge-dynamic-forms-validation-execution.token-BFyySKtp.mjs +8 -0
  54. package/fesm2022/ng-forge-dynamic-forms-validation-execution.token-BFyySKtp.mjs.map +1 -0
  55. package/fesm2022/ng-forge-dynamic-forms-with-logger-config-BAATqYY0.mjs +94 -0
  56. package/fesm2022/ng-forge-dynamic-forms-with-logger-config-BAATqYY0.mjs.map +1 -0
  57. package/fesm2022/ng-forge-dynamic-forms-wrapper-chain-BnntfNU1.mjs +115 -0
  58. package/fesm2022/ng-forge-dynamic-forms-wrapper-chain-BnntfNU1.mjs.map +1 -0
  59. package/fesm2022/ng-forge-dynamic-forms.mjs +2136 -3
  60. package/fesm2022/ng-forge-dynamic-forms.mjs.map +1 -1
  61. package/package.json +1 -1
  62. package/types/ng-forge-dynamic-forms-integration.d.ts +543 -415
  63. package/types/ng-forge-dynamic-forms-internal.d.ts +2319 -2436
  64. package/types/ng-forge-dynamic-forms-schema.d.ts +13 -16
  65. package/types/ng-forge-dynamic-forms-testing.d.ts +34 -37
  66. package/types/ng-forge-dynamic-forms.d.ts +735 -722
  67. package/fesm2022/ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs +0 -750
  68. package/fesm2022/ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs.map +0 -1
  69. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs +0 -2394
  70. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs.map +0 -1
  71. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs +0 -1054
  72. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs.map +0 -1
  73. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs +0 -86
  74. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs.map +0 -1
  75. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs +0 -33
  76. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs.map +0 -1
  77. package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs +0 -17
  78. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs +0 -25
  79. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs.map +0 -1
  80. package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs +0 -24
  81. package/fesm2022/ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs +0 -177
  82. package/fesm2022/ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs.map +0 -1
  83. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs +0 -5133
  84. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs.map +0 -1
  85. package/fesm2022/ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs +0 -176
  86. package/fesm2022/ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs.map +0 -1
  87. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs +0 -19
  88. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs.map +0 -1
  89. package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs +0 -570
  90. package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs.map +0 -1
  91. package/fesm2022/ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs +0 -126
  92. package/fesm2022/ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs.map +0 -1
@@ -0,0 +1,1887 @@
1
+ import { c as traverseFieldsWithContext, o as DERIVATION_WARNING_TRACKER, t as DERIVATION_LOG_CONFIG } from "./ng-forge-dynamic-forms-with-logger-config-BAATqYY0.mjs";
2
+ import { n as FormStateManager } from "./ng-forge-dynamic-forms-form-state-manager-DcOXRFRL.mjs";
3
+ import { c as PROPERTY_OVERRIDE_STORE } from "./ng-forge-dynamic-forms-resolve-field-ByGa5cpo.mjs";
4
+ import { t as DEV_MODE } from "./ng-forge-dynamic-forms-dev-mode-4rbYBBo_.mjs";
5
+ import "./ng-forge-dynamic-forms-debounce-CG3uP5B-.mjs";
6
+ import "./ng-forge-dynamic-forms-schema-registry.service-axBV9FBH.mjs";
7
+ import { DestroyRef, Injector, computed, inject, isSignal, isWritableSignal, untracked } from "@angular/core";
8
+ import { explicitEffect } from "ngxtension/explicit-effect";
9
+ import { EMPTY, Observable, auditTime, catchError, combineLatestWith, debounceTime, distinctUntilChanged, exhaustMap, filter, from, map, merge, of, pairwise, queueScheduler, scheduled, startWith, switchMap, timer } from "rxjs";
10
+ import { DynamicFormError, DynamicFormLogger, EXTERNAL_DATA, ExpressionParser, FORM_OPTIONS, FunctionRegistryService as FunctionRegistryService$1, createDefaultDerivationLogConfig, createFormFieldStateMap, evaluateCondition, extractExpressionDependencies, extractStringDependencies, getChangedKeys, getChangedKeysWithin, getNestedValue, hasTargetProperty, isDerivationLogicConfig, isEqual, readFieldStateInfo, resolveHttpRequest, shouldLog } from "@ng-forge/dynamic-forms/internal";
11
+ import { takeUntilDestroyed, toObservable } from "@angular/core/rxjs-interop";
12
+ import { HttpClient } from "@angular/common/http";
13
+ import { catchError as catchError$1 } from "rxjs/operators";
14
+ var ActiveDerivationLogger = class {
15
+ logger;
16
+ config = createDefaultDerivationLogConfig();
17
+ setConfig(config) {
18
+ this.config = config;
19
+ }
20
+ cycleStart(trigger, entryCount) {
21
+ if (!shouldLog(this.config, "verbose")) return;
22
+ this.logger.debug(`Derivation - Starting cycle (${trigger}) with ${entryCount} derivation(s)`);
23
+ }
24
+ iteration(iterationNumber) {
25
+ if (!shouldLog(this.config, "verbose")) return;
26
+ this.logger.debug(`Derivation - Iteration ${iterationNumber}`);
27
+ }
28
+ evaluation(entry) {
29
+ if (!shouldLog(this.config, "verbose")) return;
30
+ const name = entry.debugName ? `"${entry.debugName}"` : entry.fieldKey;
31
+ switch (entry.result) {
32
+ case "applied":
33
+ this.logger.debug(`Derivation - Applied ${name}`, {
34
+ field: entry.fieldKey,
35
+ previousValue: entry.previousValue,
36
+ newValue: entry.newValue,
37
+ ...entry.durationMs !== void 0 && { durationMs: entry.durationMs }
38
+ });
39
+ break;
40
+ case "skipped":
41
+ this.logger.debug(`Derivation - Skipped ${name} (${this.formatSkipReason(entry.skipReason)})`, {
42
+ field: entry.fieldKey,
43
+ reason: entry.skipReason
44
+ });
45
+ break;
46
+ case "error": this.logger.debug(`Derivation - Error ${name}`, {
47
+ field: entry.fieldKey,
48
+ error: entry.error
49
+ });
50
+ }
51
+ }
52
+ summary(result, trigger) {
53
+ if (!shouldLog(this.config, "summary")) return;
54
+ if (result.appliedCount === 0 && result.errorCount === 0) return;
55
+ this.logger.debug(`Derivation - Cycle complete (${trigger})`, {
56
+ applied: result.appliedCount,
57
+ skipped: result.skippedCount,
58
+ errors: result.errorCount,
59
+ iterations: result.iterations
60
+ });
61
+ }
62
+ maxIterationsReached(result, trigger) {
63
+ this.logger.warn(`Derivation - Max iterations reached (${trigger}). This may indicate a loop in derivation logic. Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
64
+ }
65
+ formatSkipReason(reason) {
66
+ switch (reason) {
67
+ case "condition-false": return "condition not met";
68
+ case "value-unchanged": return "value unchanged";
69
+ case "already-applied": return "already applied this cycle";
70
+ case "user-override": return "user override active";
71
+ case "target-not-found": return "target not found";
72
+ default: return "unknown";
73
+ }
74
+ }
75
+ };
76
+ var NoopDerivationLogger = class {
77
+ cycleStart() {}
78
+ iteration() {}
79
+ evaluation() {}
80
+ summary() {}
81
+ maxIterationsReached() {}
82
+ };
83
+ const NOOP_INSTANCE = new NoopDerivationLogger();
84
+ function createDerivationLogger(config, logger) {
85
+ const effectiveConfig = config ?? createDefaultDerivationLogConfig();
86
+ if (effectiveConfig.level === "none") return NOOP_INSTANCE;
87
+ const service = new ActiveDerivationLogger();
88
+ service.logger = logger;
89
+ service.setConfig(effectiveConfig);
90
+ return service;
91
+ }
92
+ function parseArrayPath(path) {
93
+ const placeholderIndex = path.indexOf(".$.");
94
+ if (placeholderIndex === -1) return {
95
+ arrayPath: "",
96
+ relativePath: "",
97
+ isArrayPath: false
98
+ };
99
+ return {
100
+ arrayPath: path.substring(0, placeholderIndex),
101
+ relativePath: path.substring(placeholderIndex + 3),
102
+ isArrayPath: true
103
+ };
104
+ }
105
+ function resolveArrayPath(path, index) {
106
+ const info = parseArrayPath(path);
107
+ if (!info.isArrayPath) return path;
108
+ return `${info.arrayPath}.${index}.${info.relativePath}`;
109
+ }
110
+ function isArrayPlaceholderPath(path) {
111
+ return path.includes(".$.");
112
+ }
113
+ function createDerivationChainContext() {
114
+ return {
115
+ appliedDerivations: /* @__PURE__ */ new Set(),
116
+ iteration: 0
117
+ };
118
+ }
119
+ function createDerivationKey(fieldKey) {
120
+ return fieldKey;
121
+ }
122
+ function isThenableOrObservable(value) {
123
+ if (value === null || typeof value !== "object") return false;
124
+ const candidate = value;
125
+ return typeof candidate.then === "function" || typeof candidate.subscribe === "function";
126
+ }
127
+ function computeValueFromEntry(entry, evalContext, options) {
128
+ if (entry.value !== void 0) return entry.value;
129
+ if (entry.expression) return ExpressionParser.evaluate(entry.expression, evalContext);
130
+ if (entry.fn || entry.functionName) {
131
+ if (entry.fn && entry.functionName) evalContext.logger.warn(`Both "fn" and "functionName" are set on derivation for '${options.subject}'. Inline "fn" takes precedence; "functionName" is ignored.`);
132
+ const fn = entry.fn ?? (entry.functionName ? options.derivationFunctions?.[entry.functionName] : void 0);
133
+ if (!fn) {
134
+ const kind = options.functionKind ?? "Derivation function";
135
+ throw new DynamicFormError(`${kind} '${entry.functionName}' not found in customFnConfig.derivations`);
136
+ }
137
+ const result = fn(evalContext);
138
+ if (isThenableOrObservable(result)) {
139
+ evalContext.logger.warn(`Derivation for '${options.subject}' returned a Promise or Observable. Synchronous derivations must return a value; use 'asyncDerivations' or an HTTP derivation for asynchronous logic. Skipping.`);
140
+ return;
141
+ }
142
+ return result;
143
+ }
144
+ throw new DynamicFormError(`Derivation for ${options.subject} has no value source. Specify 'value', 'expression', 'functionName', or 'fn'.`);
145
+ }
146
+ function getParentPathInScope(fieldKey) {
147
+ const lastDot = fieldKey.lastIndexOf(".");
148
+ if (lastDot <= 0) return void 0;
149
+ return fieldKey.slice(0, lastDot);
150
+ }
151
+ function resolveArrayItemScope(pathInfo, arrayItem) {
152
+ const relativePath = pathInfo.isArrayPath ? pathInfo.relativePath : "";
153
+ const innerParentPath = relativePath.includes(".") ? relativePath.slice(0, relativePath.lastIndexOf(".")) : "";
154
+ return {
155
+ relativePath,
156
+ groupValue: innerParentPath ? getNestedValue(arrayItem, innerParentPath) : arrayItem,
157
+ fieldValue: relativePath ? getNestedValue(arrayItem, relativePath) : arrayItem
158
+ };
159
+ }
160
+ function resolveFieldInstance(rootForm, fieldPath) {
161
+ const parts = fieldPath.split(".");
162
+ let current = rootForm;
163
+ for (let i = 0; i < parts.length - 1; i++) {
164
+ const next = current[parts[i]];
165
+ if (!next) return void 0;
166
+ current = next;
167
+ }
168
+ const fieldAccessor = current[parts[parts.length - 1]];
169
+ if (!fieldAccessor) return void 0;
170
+ return untracked(fieldAccessor);
171
+ }
172
+ function readFieldDirty(rootForm, fieldPath) {
173
+ const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
174
+ if (!fieldInstance) return void 0;
175
+ return untracked(fieldInstance.dirty);
176
+ }
177
+ function resetFieldState(rootForm, fieldPath) {
178
+ const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
179
+ if (!fieldInstance) return;
180
+ fieldInstance.reset();
181
+ }
182
+ function applyValueToForm(targetPath, value, rootForm, logger, warningTracker) {
183
+ if (!targetPath.includes(".")) return setFieldValue(rootForm, targetPath, value, logger, warningTracker);
184
+ const parts = targetPath.split(".");
185
+ let current = rootForm;
186
+ for (let i = 0; i < parts.length - 1; i++) {
187
+ const part = parts[i];
188
+ if (part === "$") return false;
189
+ const next = current[part];
190
+ if (!next) {
191
+ warnMissingField(targetPath, logger, warningTracker);
192
+ return false;
193
+ }
194
+ current = next;
195
+ }
196
+ const finalPart = parts[parts.length - 1];
197
+ return setFieldValue(current, finalPart, value, logger, warningTracker);
198
+ }
199
+ function setFieldValue(parent, fieldKey, value, logger, warningTracker) {
200
+ const fieldAccessor = parent[fieldKey];
201
+ if (!fieldAccessor) {
202
+ warnMissingField(fieldKey, logger, warningTracker);
203
+ return false;
204
+ }
205
+ const fieldInstance = untracked(fieldAccessor);
206
+ if (!fieldInstance || typeof fieldInstance !== "object") {
207
+ warnMissingField(fieldKey, logger, warningTracker);
208
+ return false;
209
+ }
210
+ const valueSignal = fieldInstance.value;
211
+ if (isWritableSignal(valueSignal)) {
212
+ valueSignal.set(value);
213
+ return true;
214
+ } else {
215
+ warnMissingField(fieldKey, logger, warningTracker);
216
+ return false;
217
+ }
218
+ }
219
+ function warnMissingField(fieldKey, logger, warningTracker) {
220
+ if (warningTracker) {
221
+ if (warningTracker.warnedKeys.has(fieldKey)) return;
222
+ warningTracker.warnedKeys.add(fieldKey);
223
+ }
224
+ logger?.warn(`Derivation: target field '${fieldKey}' not found in form. Ensure the field is defined in your form configuration. This warning is shown once per field.`);
225
+ }
226
+ function applyDerivations(collection, context, changedFields) {
227
+ const chainContext = createDerivationChainContext();
228
+ chainContext.changedFields = changedFields;
229
+ chainContext.formFieldState = createFormFieldStateMap(context.rootForm, false);
230
+ const { derivationLogger } = context;
231
+ const maxIterations = context.maxIterations ?? 10;
232
+ let appliedCount = 0;
233
+ let skippedCount = 0;
234
+ let errorCount = 0;
235
+ let warnCount = 0;
236
+ const entriesToProcess = changedFields ? getEntriesForChangedFields$1(collection.entries, changedFields) : collection.entries;
237
+ derivationLogger.cycleStart("onChange", entriesToProcess.length);
238
+ let hasChanges = true;
239
+ while (hasChanges && chainContext.iteration < maxIterations) {
240
+ chainContext.iteration++;
241
+ hasChanges = false;
242
+ derivationLogger.iteration(chainContext.iteration);
243
+ for (const entry of entriesToProcess) {
244
+ const result = tryApplyDerivation(entry, context, chainContext);
245
+ if (result.applied) {
246
+ appliedCount++;
247
+ hasChanges = true;
248
+ } else if (result.error) errorCount++;
249
+ else if (result.warning) {
250
+ warnCount++;
251
+ skippedCount++;
252
+ } else skippedCount++;
253
+ }
254
+ }
255
+ const processingResult = {
256
+ appliedCount,
257
+ skippedCount,
258
+ errorCount,
259
+ warnCount,
260
+ iterations: chainContext.iteration,
261
+ maxIterationsReached: chainContext.iteration >= maxIterations
262
+ };
263
+ if (processingResult.maxIterationsReached) derivationLogger.maxIterationsReached(processingResult, "onChange");
264
+ derivationLogger.summary(processingResult, "onChange");
265
+ return processingResult;
266
+ }
267
+ function getEntriesForChangedFields$1(entries, changedFields) {
268
+ return entries.filter((entry) => {
269
+ if (entry.dependsOn.length === 0 || entry.dependsOn.includes("*")) return true;
270
+ if (entry.dependsOn.some((dep) => changedFields.has(dep))) return true;
271
+ for (const changed of changedFields) {
272
+ if (entry.fieldKey.startsWith(changed + ".")) return true;
273
+ if (entry.dependsOn.some((dep) => dep.startsWith(changed + "."))) return true;
274
+ }
275
+ return false;
276
+ });
277
+ }
278
+ function tryApplyDerivation(entry, context, chainContext) {
279
+ const { derivationLogger } = context;
280
+ if (entry.http || entry.asyncFunctionName || entry.asyncFn) return {
281
+ applied: false,
282
+ fieldKey: entry.fieldKey
283
+ };
284
+ if (isArrayPlaceholderPath(entry.fieldKey)) return tryApplyArrayDerivation(entry, context, chainContext);
285
+ const derivationKey = createDerivationKey(entry.fieldKey);
286
+ if (chainContext.appliedDerivations.has(derivationKey)) {
287
+ derivationLogger.evaluation({
288
+ debugName: entry.debugName,
289
+ fieldKey: entry.fieldKey,
290
+ result: "skipped",
291
+ skipReason: "already-applied"
292
+ });
293
+ return {
294
+ applied: false,
295
+ fieldKey: entry.fieldKey
296
+ };
297
+ }
298
+ if (shouldSkipForUserOverride(entry, entry.fieldKey, context, chainContext)) {
299
+ derivationLogger.evaluation({
300
+ debugName: entry.debugName,
301
+ fieldKey: entry.fieldKey,
302
+ result: "skipped",
303
+ skipReason: "user-override"
304
+ });
305
+ return {
306
+ applied: false,
307
+ fieldKey: entry.fieldKey
308
+ };
309
+ }
310
+ const formValue = untracked(() => context.formValue());
311
+ const evalContext = createEvaluationContext$1(entry, formValue, context, chainContext);
312
+ if (!evaluateDerivationCondition(entry.condition, evalContext)) {
313
+ derivationLogger.evaluation({
314
+ debugName: entry.debugName,
315
+ fieldKey: entry.fieldKey,
316
+ result: "skipped",
317
+ skipReason: "condition-false"
318
+ });
319
+ return {
320
+ applied: false,
321
+ fieldKey: entry.fieldKey
322
+ };
323
+ }
324
+ let newValue;
325
+ try {
326
+ newValue = computeDerivedValue(entry, evalContext, context);
327
+ } catch (error) {
328
+ const errorMessage = error instanceof Error ? error.message : String(error);
329
+ context.logger.error(formatDerivationError(entry, "compute", errorMessage));
330
+ derivationLogger.evaluation({
331
+ debugName: entry.debugName,
332
+ fieldKey: entry.fieldKey,
333
+ result: "error",
334
+ error: errorMessage
335
+ });
336
+ return {
337
+ applied: false,
338
+ fieldKey: entry.fieldKey,
339
+ error: errorMessage
340
+ };
341
+ }
342
+ const currentValue = getNestedValue(formValue, entry.fieldKey);
343
+ if (isEqual(currentValue, newValue)) {
344
+ derivationLogger.evaluation({
345
+ debugName: entry.debugName,
346
+ fieldKey: entry.fieldKey,
347
+ result: "skipped",
348
+ skipReason: "value-unchanged"
349
+ });
350
+ return {
351
+ applied: false,
352
+ fieldKey: entry.fieldKey
353
+ };
354
+ }
355
+ try {
356
+ if (applyValueToForm(entry.fieldKey, newValue, context.rootForm, context.logger, context.warningTracker)) {
357
+ chainContext.appliedDerivations.add(derivationKey);
358
+ derivationLogger.evaluation({
359
+ debugName: entry.debugName,
360
+ fieldKey: entry.fieldKey,
361
+ result: "applied",
362
+ previousValue: currentValue,
363
+ newValue
364
+ });
365
+ return {
366
+ applied: true,
367
+ fieldKey: entry.fieldKey,
368
+ newValue
369
+ };
370
+ } else {
371
+ derivationLogger.evaluation({
372
+ debugName: entry.debugName,
373
+ fieldKey: entry.fieldKey,
374
+ result: "skipped",
375
+ skipReason: "target-not-found"
376
+ });
377
+ return {
378
+ applied: false,
379
+ fieldKey: entry.fieldKey,
380
+ warning: `Field '${entry.fieldKey}' not found`
381
+ };
382
+ }
383
+ } catch (error) {
384
+ const errorMessage = error instanceof Error ? error.message : String(error);
385
+ context.logger.error(formatDerivationError(entry, "apply", errorMessage));
386
+ derivationLogger.evaluation({
387
+ debugName: entry.debugName,
388
+ fieldKey: entry.fieldKey,
389
+ result: "error",
390
+ error: errorMessage
391
+ });
392
+ return {
393
+ applied: false,
394
+ fieldKey: entry.fieldKey,
395
+ error: errorMessage
396
+ };
397
+ }
398
+ }
399
+ function tryApplyArrayDerivation(entry, context, chainContext) {
400
+ const formValue = untracked(() => context.formValue());
401
+ const pathInfo = parseArrayPath(entry.fieldKey);
402
+ if (!pathInfo.isArrayPath) return {
403
+ applied: false,
404
+ fieldKey: entry.fieldKey,
405
+ error: "Invalid array derivation path"
406
+ };
407
+ const { arrayPath } = pathInfo;
408
+ const arrayValue = getNestedValue(formValue, arrayPath);
409
+ if (!Array.isArray(arrayValue)) return {
410
+ applied: false,
411
+ fieldKey: entry.fieldKey
412
+ };
413
+ let appliedAny = false;
414
+ for (let i = 0; i < arrayValue.length; i++) {
415
+ const resolvedPath = resolveArrayPath(entry.fieldKey, i);
416
+ const derivationKey = createDerivationKey(resolvedPath);
417
+ if (chainContext.appliedDerivations.has(derivationKey)) continue;
418
+ if (shouldSkipForUserOverride(entry, resolvedPath, context, chainContext)) {
419
+ context.derivationLogger.evaluation({
420
+ debugName: entry.debugName,
421
+ fieldKey: resolvedPath,
422
+ result: "skipped",
423
+ skipReason: "user-override"
424
+ });
425
+ continue;
426
+ }
427
+ const arrayItem = arrayValue[i];
428
+ const evalContext = createArrayItemEvaluationContext$1(entry, arrayItem, formValue, i, arrayPath, context, chainContext);
429
+ if (!evaluateDerivationCondition(entry.condition, evalContext)) continue;
430
+ let newValue;
431
+ try {
432
+ newValue = computeDerivedValue(entry, evalContext, context);
433
+ } catch (error) {
434
+ const errorMessage = error instanceof Error ? error.message : String(error);
435
+ context.logger.error(formatDerivationError(entry, "compute", `[index ${i}] ${errorMessage}`));
436
+ continue;
437
+ }
438
+ const currentValue = getNestedValue(formValue, resolvedPath);
439
+ if (isEqual(currentValue, newValue)) continue;
440
+ try {
441
+ applyValueToForm(resolvedPath, newValue, context.rootForm, context.logger, context.warningTracker);
442
+ chainContext.appliedDerivations.add(derivationKey);
443
+ appliedAny = true;
444
+ } catch (error) {
445
+ const errorMessage = error instanceof Error ? error.message : String(error);
446
+ context.logger.error(formatDerivationError(entry, "apply", `[index ${i}] ${errorMessage}`));
447
+ }
448
+ }
449
+ return {
450
+ applied: appliedAny,
451
+ fieldKey: entry.fieldKey
452
+ };
453
+ }
454
+ function createEvaluationContext$1(entry, formValue, context, chainContext) {
455
+ const fieldValue = getNestedValue(formValue, entry.fieldKey);
456
+ const fieldAccessor = getFieldTreeByPath(context.rootForm, entry.fieldKey);
457
+ const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : void 0;
458
+ const parentPath = getParentPathInScope(entry.fieldKey);
459
+ const groupValue = parentPath ? getNestedValue(formValue, parentPath) : void 0;
460
+ return {
461
+ fieldValue,
462
+ formValue: parentPath !== void 0 ? groupValue ?? {} : formValue,
463
+ fieldPath: entry.fieldKey,
464
+ groupValue,
465
+ customFunctions: context.customFunctions,
466
+ externalData: context.externalData,
467
+ logger: context.logger,
468
+ rootFormValue: formValue,
469
+ fieldState,
470
+ formFieldState: chainContext.formFieldState
471
+ };
472
+ }
473
+ function getFieldTreeByPath(root, path) {
474
+ let current = root;
475
+ for (const segment of path.split(".")) {
476
+ if (current === void 0 || current === null) return void 0;
477
+ current = current[segment];
478
+ }
479
+ return current;
480
+ }
481
+ function createArrayItemEvaluationContext$1(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context, chainContext) {
482
+ const pathInfo = parseArrayPath(entry.fieldKey);
483
+ let fieldState;
484
+ if (pathInfo.isArrayPath && pathInfo.relativePath) {
485
+ const arrayAccessor = context.rootForm[arrayPath];
486
+ if (arrayAccessor) {
487
+ const itemAccessor = arrayAccessor[String(itemIndex)];
488
+ if (itemAccessor) {
489
+ const fieldAccessor = itemAccessor[pathInfo.relativePath];
490
+ if (fieldAccessor) fieldState = readFieldStateInfo(fieldAccessor, false);
491
+ }
492
+ }
493
+ }
494
+ const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
495
+ return {
496
+ fieldValue,
497
+ formValue: arrayItem,
498
+ fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
499
+ groupValue,
500
+ customFunctions: context.customFunctions,
501
+ externalData: context.externalData,
502
+ logger: context.logger,
503
+ rootFormValue,
504
+ arrayIndex: itemIndex,
505
+ arrayPath,
506
+ fieldState,
507
+ formFieldState: chainContext.formFieldState
508
+ };
509
+ }
510
+ function evaluateDerivationCondition(condition, context) {
511
+ if (typeof condition === "boolean") return condition;
512
+ return evaluateCondition(condition, context);
513
+ }
514
+ function computeDerivedValue(entry, evalContext, applicatorContext) {
515
+ return computeValueFromEntry(entry, evalContext, {
516
+ derivationFunctions: applicatorContext.derivationFunctions,
517
+ subject: entry.fieldKey
518
+ });
519
+ }
520
+ const ERROR_PREFIX$1 = "[Derivation]";
521
+ function formatDerivationError(entry, phase, message) {
522
+ const source = entry.expression ? `expression: "${entry.expression.substring(0, 50)}${entry.expression.length > 50 ? "..." : ""}"` : entry.functionName ? `function: "${entry.functionName}"` : entry.fn ? "inline fn" : entry.value !== void 0 ? "static value" : "unknown source";
523
+ return `${ERROR_PREFIX$1} Failed to ${phase} derivation (field: ${entry.fieldKey}, ${source}): ${message}`;
524
+ }
525
+ function hasDependencyChanged(entry, changedFields) {
526
+ return entry.dependsOn.some((dep) => dep === "*" || changedFields.has(dep));
527
+ }
528
+ function shouldSkipForUserOverride(entry, resolvedFieldKey, context, chainContext) {
529
+ if (!entry.stopOnUserOverride) return false;
530
+ if (!(readFieldDirty(context.rootForm, resolvedFieldKey) ?? false)) return false;
531
+ if (entry.reEngageOnDependencyChange && chainContext.changedFields) {
532
+ if (hasDependencyChanged(entry, chainContext.changedFields)) {
533
+ resetFieldState(context.rootForm, resolvedFieldKey);
534
+ return false;
535
+ }
536
+ }
537
+ return true;
538
+ }
539
+ function applyDerivationsForTrigger(collection, trigger, context, changedFields) {
540
+ return applyDerivations({ entries: collection.entries.filter((entry) => {
541
+ if (entry.http || entry.asyncFunctionName || entry.asyncFn) return false;
542
+ if (trigger === "onChange") return !entry.trigger || entry.trigger === "onChange";
543
+ return entry.trigger === trigger;
544
+ }) }, context, changedFields);
545
+ }
546
+ function topologicalSort(entries) {
547
+ if (entries.length <= 1) return [...entries];
548
+ const graph = /* @__PURE__ */ new Map();
549
+ const inDegree = /* @__PURE__ */ new Map();
550
+ for (const entry of entries) {
551
+ graph.set(entry, /* @__PURE__ */ new Set());
552
+ inDegree.set(entry, 0);
553
+ }
554
+ const derivationsByField = /* @__PURE__ */ new Map();
555
+ for (const entry of entries) {
556
+ const fieldEntries = derivationsByField.get(entry.fieldKey) ?? [];
557
+ fieldEntries.push(entry);
558
+ derivationsByField.set(entry.fieldKey, fieldEntries);
559
+ if (entry.fieldKey.includes(".$.")) {
560
+ const relativePath = entry.fieldKey.split(".$.")[1];
561
+ if (relativePath) {
562
+ const relativeEntries = derivationsByField.get(relativePath) ?? [];
563
+ relativeEntries.push(entry);
564
+ derivationsByField.set(relativePath, relativeEntries);
565
+ }
566
+ }
567
+ }
568
+ const wildcardDependents = [];
569
+ for (const entry of entries) if (entry.dependsOn.includes("*")) wildcardDependents.push(entry);
570
+ for (const entryB of entries) {
571
+ const groupPath = entryB.fieldKey.includes(".") && !entryB.fieldKey.includes(".$.") ? entryB.fieldKey.slice(0, entryB.fieldKey.lastIndexOf(".")) : void 0;
572
+ for (const dep of entryB.dependsOn) {
573
+ if (dep === "*") continue;
574
+ const producers = [...derivationsByField.get(dep) ?? [], ...groupPath && !dep.includes(".") ? derivationsByField.get(`${groupPath}.${dep}`) ?? [] : []];
575
+ for (const entryA of producers) {
576
+ const entryAEdges = graph.get(entryA);
577
+ if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
578
+ entryAEdges.add(entryB);
579
+ inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
580
+ }
581
+ }
582
+ }
583
+ }
584
+ for (const entryB of wildcardDependents) for (const entryA of entries) {
585
+ const entryAEdges = graph.get(entryA);
586
+ if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
587
+ entryAEdges.add(entryB);
588
+ inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
589
+ }
590
+ }
591
+ const queue = [];
592
+ const sorted = [];
593
+ for (const entry of entries) if (inDegree.get(entry) === 0) queue.push(entry);
594
+ while (queue.length > 0) {
595
+ const current = queue.shift();
596
+ if (!current) break;
597
+ sorted.push(current);
598
+ const neighbors = graph.get(current) ?? /* @__PURE__ */ new Set();
599
+ for (const neighbor of neighbors) {
600
+ const newDegree = (inDegree.get(neighbor) ?? 0) - 1;
601
+ inDegree.set(neighbor, newDegree);
602
+ if (newDegree === 0) queue.push(neighbor);
603
+ }
604
+ }
605
+ if (sorted.length !== entries.length) {
606
+ const sortedSet = new Set(sorted);
607
+ for (const entry of entries) if (!sortedSet.has(entry)) sorted.push(entry);
608
+ }
609
+ return sorted;
610
+ }
611
+ function extractDependenciesFromConfig(config) {
612
+ const deps = /* @__PURE__ */ new Set();
613
+ if (config.dependsOn && config.dependsOn.length > 0) config.dependsOn.forEach((dep) => deps.add(dep));
614
+ else {
615
+ if (config.expression) extractStringDependencies(config.expression).forEach((dep) => deps.add(dep));
616
+ if (config.functionName || config.fn) deps.add("*");
617
+ }
618
+ if (config.condition && config.condition !== true) extractExpressionDependencies(config.condition).forEach((dep) => deps.add(dep));
619
+ return Array.from(deps);
620
+ }
621
+ function extractInlineFn(config) {
622
+ return config.fn;
623
+ }
624
+ function extractInlineAsyncFn(config) {
625
+ return config.asyncFn;
626
+ }
627
+ const GROUP_DEPENDENCY_TOKEN = "$group";
628
+ function buildEffectiveFieldKey(fieldKey, context) {
629
+ if (context.arrayPath && context.groupPath) return `${context.arrayPath}.$.${context.groupPath}.${fieldKey}`;
630
+ if (context.arrayPath) return `${context.arrayPath}.$.${fieldKey}`;
631
+ if (context.groupPath) return `${context.groupPath}.${fieldKey}`;
632
+ return fieldKey;
633
+ }
634
+ function buildNearestGroupPath(context) {
635
+ if (context.arrayPath && context.groupPath) return `${context.arrayPath}.$.${context.groupPath}`;
636
+ return context.arrayPath ?? context.groupPath;
637
+ }
638
+ function resolveTokenDependencies(deps, effectiveFieldKey, context) {
639
+ return deps.map((dep) => {
640
+ if (dep === "$self") return effectiveFieldKey;
641
+ if (dep.startsWith(`\$self.`)) return effectiveFieldKey + dep.slice(5);
642
+ if (dep === "$group" || dep.startsWith(`\$group.`)) {
643
+ const groupPath = buildNearestGroupPath(context);
644
+ if (!groupPath) throw new DynamicFormError(`Derivation for '${effectiveFieldKey}' uses '${dep}' but the field has no parent group or array. '${GROUP_DEPENDENCY_TOKEN}' is only valid for fields nested inside a group or array container.`);
645
+ if (dep === "$group") return groupPath;
646
+ return groupPath + dep.slice(6);
647
+ }
648
+ return dep;
649
+ });
650
+ }
651
+ const CONTEXT_TRAVERSAL_OPTIONS = {
652
+ onArrayChild: (_parent, field) => ({
653
+ arrayPath: field.key,
654
+ groupPath: void 0
655
+ }),
656
+ onGroupChild: (parent, field) => {
657
+ if (!field.key) return {};
658
+ return { groupPath: parent.groupPath ? `${parent.groupPath}.${field.key}` : field.key };
659
+ }
660
+ };
661
+ function collectDerivations(fields) {
662
+ const entries = [];
663
+ traverseFieldsWithContext(fields, {}, (field, context) => collectValueEntriesFromField(field, entries, context), CONTEXT_TRAVERSAL_OPTIONS);
664
+ return { entries: topologicalSort(entries) };
665
+ }
666
+ function collectAllDerivations(fields) {
667
+ const valueEntries = [];
668
+ const propertyEntries = [];
669
+ traverseFieldsWithContext(fields, {}, (field, context) => {
670
+ collectValueEntriesFromField(field, valueEntries, context);
671
+ collectPropertyEntriesFromField(field, propertyEntries, context);
672
+ }, CONTEXT_TRAVERSAL_OPTIONS);
673
+ return {
674
+ value: { entries: topologicalSort(valueEntries) },
675
+ property: { entries: propertyEntries }
676
+ };
677
+ }
678
+ function collectValueEntriesFromField(field, entries, context) {
679
+ const fieldKey = field.key;
680
+ if (!fieldKey) return;
681
+ const validationField = field;
682
+ if (validationField.derivation) entries.push(createShorthandEntry(fieldKey, validationField.derivation, context));
683
+ if (validationField.logic) for (const logicConfig of validationField.logic) {
684
+ if (!isDerivationLogicConfig(logicConfig)) continue;
685
+ if (hasTargetProperty(logicConfig)) continue;
686
+ entries.push(createValueLogicEntry(fieldKey, logicConfig, context));
687
+ }
688
+ }
689
+ function collectPropertyEntriesFromField(field, entries, context) {
690
+ const fieldKey = field.key;
691
+ if (!fieldKey) return;
692
+ const validationField = field;
693
+ if (!validationField.logic) return;
694
+ for (const logicConfig of validationField.logic) {
695
+ if (!isDerivationLogicConfig(logicConfig)) continue;
696
+ if (!hasTargetProperty(logicConfig)) continue;
697
+ entries.push(createPropertyLogicEntry(fieldKey, logicConfig, context));
698
+ }
699
+ }
700
+ function createShorthandEntry(fieldKey, expression, context) {
701
+ return {
702
+ fieldKey: buildEffectiveFieldKey(fieldKey, context),
703
+ dependsOn: extractStringDependencies(expression),
704
+ condition: true,
705
+ expression,
706
+ trigger: "onChange",
707
+ isShorthand: true
708
+ };
709
+ }
710
+ function createValueLogicEntry(fieldKey, config, context) {
711
+ const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
712
+ if (config.source === "http") {
713
+ if (config.dependsOn.length === 0) throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. Wildcard dependencies would trigger HTTP requests on every form change.`);
714
+ if (config.dependsOn.includes("*")) throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
715
+ }
716
+ if (config.source === "asyncFunction") {
717
+ if (config.dependsOn.length === 0) throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. Wildcard dependencies would trigger async functions on every form change.`);
718
+ if (config.dependsOn.includes("*")) throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
719
+ }
720
+ const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
721
+ const condition = config.condition ?? true;
722
+ const trigger = config.trigger ?? "onChange";
723
+ const debounceMs = trigger === "debounced" ? config.debounceMs : void 0;
724
+ return {
725
+ fieldKey: effectiveFieldKey,
726
+ dependsOn,
727
+ condition,
728
+ value: config.value,
729
+ expression: config.expression,
730
+ functionName: config.functionName,
731
+ fn: extractInlineFn(config),
732
+ http: config.http,
733
+ responseExpression: config.responseExpression,
734
+ asyncFunctionName: config.asyncFunctionName,
735
+ asyncFn: extractInlineAsyncFn(config),
736
+ trigger,
737
+ debounceMs,
738
+ isShorthand: false,
739
+ originalConfig: config,
740
+ debugName: config.debugName,
741
+ stopOnUserOverride: config.stopOnUserOverride,
742
+ reEngageOnDependencyChange: config.reEngageOnDependencyChange
743
+ };
744
+ }
745
+ function createPropertyLogicEntry(fieldKey, config, context) {
746
+ const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
747
+ const errorLocation = `'${effectiveFieldKey}.${config.targetProperty}'`;
748
+ const untypedConfig = config;
749
+ if (untypedConfig.source === "http" || untypedConfig.source === "asyncFunction") {
750
+ const conflictingSyncSources = [];
751
+ if (untypedConfig.value !== void 0) conflictingSyncSources.push("value");
752
+ if (untypedConfig.expression !== void 0) conflictingSyncSources.push("expression");
753
+ if (untypedConfig.functionName !== void 0) conflictingSyncSources.push("functionName");
754
+ if (untypedConfig.fn !== void 0) conflictingSyncSources.push("fn");
755
+ if (conflictingSyncSources.length > 0) throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: ${JSON.stringify(untypedConfig.source)}' alongside sync value source(s) [${conflictingSyncSources.join(", ")}]. These are mutually exclusive — remove either the sync fields or the async source.`);
756
+ }
757
+ if (untypedConfig.source === "http" && (untypedConfig.asyncFunctionName !== void 0 || untypedConfig.asyncFn !== void 0)) throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "http"' alongside an async function — these are mutually exclusive.`);
758
+ if (untypedConfig.source === "asyncFunction" && (untypedConfig.http !== void 0 || untypedConfig.responseExpression !== void 0)) throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "asyncFunction"' alongside HTTP fields — these are mutually exclusive.`);
759
+ if (untypedConfig.source === "http") {
760
+ if (!untypedConfig.http) throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires an 'http' config object.`);
761
+ if (typeof untypedConfig.responseExpression !== "string" || untypedConfig.responseExpression.trim() === "") throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires a non-empty 'responseExpression'.`);
762
+ if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires explicit 'dependsOn'. Wildcard dependencies would trigger HTTP requests on every form change.`);
763
+ if (untypedConfig.dependsOn.includes("*")) throw new DynamicFormError(`HTTP property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
764
+ }
765
+ if (untypedConfig.source === "asyncFunction") {
766
+ if (!untypedConfig.asyncFunctionName && !untypedConfig.asyncFn) throw new DynamicFormError(`Async property derivation for ${errorLocation} requires either 'asyncFunctionName' or 'asyncFn'.`);
767
+ if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) throw new DynamicFormError(`Async property derivation for ${errorLocation} requires explicit 'dependsOn'. Wildcard dependencies would trigger async functions on every form change.`);
768
+ if (untypedConfig.dependsOn.includes("*")) throw new DynamicFormError(`Async property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
769
+ }
770
+ const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
771
+ const condition = config.condition ?? true;
772
+ const trigger = config.trigger ?? "onChange";
773
+ const debounceMs = trigger === "debounced" ? config.debounceMs : void 0;
774
+ return {
775
+ fieldKey: effectiveFieldKey,
776
+ targetProperty: config.targetProperty,
777
+ dependsOn,
778
+ condition,
779
+ value: config.value,
780
+ expression: config.expression,
781
+ functionName: config.functionName,
782
+ fn: extractInlineFn(config),
783
+ http: config.http,
784
+ responseExpression: config.responseExpression,
785
+ asyncFunctionName: config.asyncFunctionName,
786
+ asyncFn: extractInlineAsyncFn(config),
787
+ trigger,
788
+ debounceMs,
789
+ debugName: config.debugName,
790
+ originalConfig: config
791
+ };
792
+ }
793
+ function collectPropertyDerivations(fields) {
794
+ return collectAllDerivations(fields).property;
795
+ }
796
+ function detectCycles(collection) {
797
+ const graph = buildDependencyGraph(collection);
798
+ if (graph.size === 0) return { hasCycle: false };
799
+ const bidirectionalPairs = detectBidirectionalPairs(collection);
800
+ const result = detectCyclesWithDFS(graph, bidirectionalPairs);
801
+ if (bidirectionalPairs.size > 0) result.bidirectionalPairs = Array.from(bidirectionalPairs);
802
+ return result;
803
+ }
804
+ function detectBidirectionalPairs(collection) {
805
+ const pairs = /* @__PURE__ */ new Set();
806
+ const dependencyMap = /* @__PURE__ */ new Map();
807
+ for (const entry of collection.entries) {
808
+ const deps = dependencyMap.get(entry.fieldKey) ?? /* @__PURE__ */ new Set();
809
+ for (const dep of entry.dependsOn) if (dep !== "*" && dep !== entry.fieldKey) deps.add(dep);
810
+ dependencyMap.set(entry.fieldKey, deps);
811
+ }
812
+ for (const [fieldA, depsA] of dependencyMap) for (const fieldB of depsA) if (dependencyMap.get(fieldB)?.has(fieldA)) {
813
+ const pairKey = fieldA < fieldB ? `${fieldA}↔${fieldB}` : `${fieldB}↔${fieldA}`;
814
+ pairs.add(pairKey);
815
+ }
816
+ return pairs;
817
+ }
818
+ function isBidirectionalCycle(cyclePath, bidirectionalPairs) {
819
+ if (cyclePath.length !== 3) return false;
820
+ const [first, second, third] = cyclePath;
821
+ if (first !== third) return false;
822
+ const pairKey = first < second ? `${first}↔${second}` : `${second}↔${first}`;
823
+ return bidirectionalPairs.has(pairKey);
824
+ }
825
+ function buildDependencyGraph(collection) {
826
+ const graph = /* @__PURE__ */ new Map();
827
+ const ensureNode = (fieldKey) => {
828
+ let node = graph.get(fieldKey);
829
+ if (!node) {
830
+ node = {
831
+ fieldKey,
832
+ dependsOn: /* @__PURE__ */ new Set(),
833
+ dependedOnBy: /* @__PURE__ */ new Set()
834
+ };
835
+ graph.set(fieldKey, node);
836
+ }
837
+ return node;
838
+ };
839
+ for (const entry of collection.entries) {
840
+ const fieldNode = ensureNode(entry.fieldKey);
841
+ for (const dep of entry.dependsOn) if (dep !== "*" && dep !== entry.fieldKey) {
842
+ const depNode = ensureNode(dep);
843
+ fieldNode.dependsOn.add(dep);
844
+ depNode.dependedOnBy.add(entry.fieldKey);
845
+ }
846
+ }
847
+ return graph;
848
+ }
849
+ function detectCyclesWithDFS(graph, bidirectionalPairs) {
850
+ const visitState = /* @__PURE__ */ new Map();
851
+ const parent = /* @__PURE__ */ new Map();
852
+ for (const fieldKey of graph.keys()) visitState.set(fieldKey, 0);
853
+ for (const fieldKey of graph.keys()) if (visitState.get(fieldKey) === 0) {
854
+ const cycleResult = dfsVisit(fieldKey, graph, visitState, parent, [], bidirectionalPairs);
855
+ if (cycleResult) return cycleResult;
856
+ }
857
+ return { hasCycle: false };
858
+ }
859
+ function dfsVisit(fieldKey, graph, visitState, parent, path, bidirectionalPairs) {
860
+ visitState.set(fieldKey, 1);
861
+ path.push(fieldKey);
862
+ const node = graph.get(fieldKey);
863
+ if (!node) {
864
+ visitState.set(fieldKey, 2);
865
+ path.pop();
866
+ return null;
867
+ }
868
+ for (const targetFieldKey of node.dependedOnBy) {
869
+ const targetState = visitState.get(targetFieldKey);
870
+ if (targetState === 1) {
871
+ const cyclePath = extractCyclePath(path, targetFieldKey);
872
+ if (isBidirectionalCycle(cyclePath, bidirectionalPairs)) continue;
873
+ return {
874
+ hasCycle: true,
875
+ cyclePath,
876
+ errorMessage: formatCycleError(cyclePath)
877
+ };
878
+ }
879
+ if (targetState === 0) {
880
+ parent.set(targetFieldKey, fieldKey);
881
+ const result = dfsVisit(targetFieldKey, graph, visitState, parent, path, bidirectionalPairs);
882
+ if (result) return result;
883
+ }
884
+ }
885
+ visitState.set(fieldKey, 2);
886
+ path.pop();
887
+ return null;
888
+ }
889
+ function extractCyclePath(path, cycleStart) {
890
+ const cycleStartIndex = path.indexOf(cycleStart);
891
+ if (cycleStartIndex === -1) return [...path, cycleStart];
892
+ const cyclePath = path.slice(cycleStartIndex);
893
+ cyclePath.push(cycleStart);
894
+ return cyclePath;
895
+ }
896
+ function formatCycleError(cyclePath) {
897
+ return `Derivation cycle detected: ${cyclePath.join(" -> ")}\nThis would cause an infinite loop at runtime. Remove one of the derivations to break the cycle.`;
898
+ }
899
+ function validateNoCycles(collection, logger) {
900
+ const result = detectCycles(collection);
901
+ if (result.hasCycle) throw new Error(result.errorMessage);
902
+ if (DEV_MODE && result.bidirectionalPairs && result.bidirectionalPairs.length > 0 && logger) logger.warn("Derivation: bidirectional derivation patterns detected. These patterns stabilize via equality checks, but may oscillate with floating-point values (e.g., currency conversions with rounding). Consider adding tolerance-based comparisons for numeric values.", result.bidirectionalPairs);
903
+ }
904
+ const LOG_PREFIX$3 = "HTTP Derivation -";
905
+ const DEFAULT_HTTP_DEBOUNCE_MS$1 = 300;
906
+ function createHttpDerivationStream(entry, formValue$, context) {
907
+ if (isArrayPlaceholderPath(entry.fieldKey)) {
908
+ context.logger.warn(`${LOG_PREFIX$3} HTTP derivation for array field '${entry.fieldKey}' is not supported. Array HTTP derivations will be supported in a future release.`);
909
+ return EMPTY;
910
+ }
911
+ if (!entry.http || !entry.responseExpression) return EMPTY;
912
+ const responseExpression = entry.responseExpression;
913
+ const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS$1;
914
+ return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
915
+ current,
916
+ changedFields: getChangedKeys(previous, current)
917
+ })), filter(({ changedFields }) => {
918
+ if (changedFields.size === 0) return false;
919
+ return entry.dependsOn.some((dep) => changedFields.has(dep));
920
+ }), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
921
+ return new Observable((subscriber) => {
922
+ const formAccessor = untracked(() => context.form());
923
+ if (entry.stopOnUserOverride) {
924
+ if (readFieldDirty(formAccessor, entry.fieldKey)) {
925
+ if (entry.reEngageOnDependencyChange && changedFields.size > 0) {
926
+ if (entry.dependsOn.some((dep) => changedFields.has(dep))) resetFieldState(formAccessor, entry.fieldKey);
927
+ else {
928
+ untracked(() => context.derivationLogger()).evaluation({
929
+ debugName: entry.debugName,
930
+ fieldKey: entry.fieldKey,
931
+ result: "skipped",
932
+ skipReason: "user-override"
933
+ });
934
+ subscriber.complete();
935
+ return;
936
+ }
937
+ } else {
938
+ untracked(() => context.derivationLogger()).evaluation({
939
+ debugName: entry.debugName,
940
+ fieldKey: entry.fieldKey,
941
+ result: "skipped",
942
+ skipReason: "user-override"
943
+ });
944
+ subscriber.complete();
945
+ return;
946
+ }
947
+ }
948
+ }
949
+ const customFunctions = context.customFunctions?.();
950
+ const externalData = context.externalData?.();
951
+ const fieldValue = getNestedValue(current, entry.fieldKey);
952
+ const fieldAccessor = formAccessor[entry.fieldKey];
953
+ const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : void 0;
954
+ const evalContext = {
955
+ fieldValue,
956
+ formValue: current,
957
+ fieldPath: entry.fieldKey,
958
+ customFunctions,
959
+ externalData,
960
+ logger: context.logger,
961
+ fieldState,
962
+ formFieldState: createFormFieldStateMap(formAccessor, false)
963
+ };
964
+ if (entry.condition !== true) {
965
+ if (!(typeof entry.condition === "boolean" ? entry.condition : evaluateCondition(entry.condition, evalContext))) {
966
+ untracked(() => context.derivationLogger()).evaluation({
967
+ debugName: entry.debugName,
968
+ fieldKey: entry.fieldKey,
969
+ result: "skipped",
970
+ skipReason: "condition-false"
971
+ });
972
+ subscriber.complete();
973
+ return;
974
+ }
975
+ }
976
+ const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
977
+ if (!resolvedRequest) {
978
+ untracked(() => context.derivationLogger()).evaluation({
979
+ debugName: entry.debugName,
980
+ fieldKey: entry.fieldKey,
981
+ result: "skipped",
982
+ skipReason: "condition-false"
983
+ });
984
+ subscriber.complete();
985
+ return;
986
+ }
987
+ const method = (resolvedRequest.method ?? "GET").toUpperCase();
988
+ const httpSub = context.httpClient.request(method, resolvedRequest.url, {
989
+ body: resolvedRequest.body,
990
+ headers: resolvedRequest.headers
991
+ }).subscribe({
992
+ next: (response) => {
993
+ try {
994
+ const newValue = ExpressionParser.evaluate(responseExpression, { response });
995
+ const currentFormValue = untracked(() => context.formValue());
996
+ const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
997
+ if (isEqual(currentValue, newValue)) {
998
+ untracked(() => context.derivationLogger()).evaluation({
999
+ debugName: entry.debugName,
1000
+ fieldKey: entry.fieldKey,
1001
+ result: "skipped",
1002
+ skipReason: "value-unchanged"
1003
+ });
1004
+ subscriber.complete();
1005
+ return;
1006
+ }
1007
+ const currentForm = untracked(() => context.form());
1008
+ if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
1009
+ untracked(() => context.derivationLogger()).evaluation({
1010
+ debugName: entry.debugName,
1011
+ fieldKey: entry.fieldKey,
1012
+ result: "skipped",
1013
+ skipReason: "user-override"
1014
+ });
1015
+ subscriber.complete();
1016
+ return;
1017
+ }
1018
+ applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
1019
+ untracked(() => context.derivationLogger()).evaluation({
1020
+ debugName: entry.debugName,
1021
+ fieldKey: entry.fieldKey,
1022
+ result: "applied",
1023
+ previousValue: currentValue,
1024
+ newValue
1025
+ });
1026
+ subscriber.complete();
1027
+ } catch (error) {
1028
+ const message = error instanceof Error ? error.message : String(error);
1029
+ context.logger.warn(`${LOG_PREFIX$3} Failed to process response for '${entry.fieldKey}': ${message}`);
1030
+ subscriber.complete();
1031
+ }
1032
+ },
1033
+ error: (error) => {
1034
+ const message = error instanceof Error ? error.message : String(error);
1035
+ context.logger.warn(`${LOG_PREFIX$3} HTTP request failed for '${entry.fieldKey}': ${message}`);
1036
+ subscriber.complete();
1037
+ }
1038
+ });
1039
+ return () => {
1040
+ httpSub.unsubscribe();
1041
+ };
1042
+ });
1043
+ }), catchError$1((error) => {
1044
+ const message = error instanceof Error ? error.message : String(error);
1045
+ context.logger.warn(`${LOG_PREFIX$3} Unexpected stream error for '${entry.fieldKey}': ${message}`);
1046
+ return EMPTY;
1047
+ }));
1048
+ }
1049
+ const LOG_PREFIX$2 = "Async Derivation -";
1050
+ const DEFAULT_ASYNC_DEBOUNCE_MS$1 = 300;
1051
+ function createAsyncDerivationStream(entry, formValue$, context) {
1052
+ if (isArrayPlaceholderPath(entry.fieldKey)) {
1053
+ context.logger.warn(`${LOG_PREFIX$2} Async derivation for array field '${entry.fieldKey}' is not supported. Array async derivations will be supported in a future release.`);
1054
+ return EMPTY;
1055
+ }
1056
+ if (!entry.asyncFunctionName && !entry.asyncFn) {
1057
+ context.logger.warn(`${LOG_PREFIX$2} Derivation for '${entry.fieldKey}' has neither "asyncFunctionName" nor "asyncFn". One of the two is required for source: 'asyncFunction'.`);
1058
+ return EMPTY;
1059
+ }
1060
+ if (entry.asyncFn && entry.asyncFunctionName) context.logger.warn(`${LOG_PREFIX$2} Both "asyncFn" and "asyncFunctionName" are set on derivation for '${entry.fieldKey}'. Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
1061
+ const inlineAsyncFn = entry.asyncFn;
1062
+ const asyncFunctionName = entry.asyncFunctionName;
1063
+ const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS$1;
1064
+ return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
1065
+ current,
1066
+ changedFields: getChangedKeys(previous, current)
1067
+ })), filter(({ changedFields }) => {
1068
+ if (changedFields.size === 0) return false;
1069
+ return entry.dependsOn.some((dep) => changedFields.has(dep));
1070
+ }), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
1071
+ return new Observable((subscriber) => {
1072
+ const formAccessor = untracked(() => context.form());
1073
+ if (entry.stopOnUserOverride) {
1074
+ if (readFieldDirty(formAccessor, entry.fieldKey)) {
1075
+ if (entry.reEngageOnDependencyChange) resetFieldState(formAccessor, entry.fieldKey);
1076
+ else {
1077
+ untracked(() => context.derivationLogger()).evaluation({
1078
+ debugName: entry.debugName,
1079
+ fieldKey: entry.fieldKey,
1080
+ result: "skipped",
1081
+ skipReason: "user-override"
1082
+ });
1083
+ subscriber.complete();
1084
+ return;
1085
+ }
1086
+ }
1087
+ }
1088
+ const customFunctions = context.customFunctions?.();
1089
+ const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
1090
+ const externalData = context.externalData?.();
1091
+ const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : void 0);
1092
+ if (!asyncFn) {
1093
+ context.logger.warn(`${LOG_PREFIX$2} Async derivation function '${asyncFunctionName}' not found for field '${entry.fieldKey}'. Register it in customFnConfig.asyncDerivations.`);
1094
+ subscriber.complete();
1095
+ return;
1096
+ }
1097
+ const fieldValue = getNestedValue(current, entry.fieldKey);
1098
+ const fieldAccessor = formAccessor[entry.fieldKey];
1099
+ const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : void 0;
1100
+ const evalContext = {
1101
+ fieldValue,
1102
+ formValue: current,
1103
+ fieldPath: entry.fieldKey,
1104
+ customFunctions,
1105
+ externalData,
1106
+ logger: context.logger,
1107
+ fieldState,
1108
+ formFieldState: createFormFieldStateMap(formAccessor, false)
1109
+ };
1110
+ if (entry.condition !== true) {
1111
+ if (!(typeof entry.condition === "boolean" ? entry.condition : evaluateCondition(entry.condition, evalContext))) {
1112
+ untracked(() => context.derivationLogger()).evaluation({
1113
+ debugName: entry.debugName,
1114
+ fieldKey: entry.fieldKey,
1115
+ result: "skipped",
1116
+ skipReason: "condition-false"
1117
+ });
1118
+ subscriber.complete();
1119
+ return;
1120
+ }
1121
+ }
1122
+ const asyncSub = from(asyncFn(evalContext)).subscribe({
1123
+ next: (newValue) => {
1124
+ try {
1125
+ const currentFormValue = untracked(() => context.formValue());
1126
+ const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
1127
+ if (isEqual(currentValue, newValue)) {
1128
+ untracked(() => context.derivationLogger()).evaluation({
1129
+ debugName: entry.debugName,
1130
+ fieldKey: entry.fieldKey,
1131
+ result: "skipped",
1132
+ skipReason: "value-unchanged"
1133
+ });
1134
+ subscriber.complete();
1135
+ return;
1136
+ }
1137
+ const currentForm = untracked(() => context.form());
1138
+ if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
1139
+ untracked(() => context.derivationLogger()).evaluation({
1140
+ debugName: entry.debugName,
1141
+ fieldKey: entry.fieldKey,
1142
+ result: "skipped",
1143
+ skipReason: "user-override"
1144
+ });
1145
+ subscriber.complete();
1146
+ return;
1147
+ }
1148
+ applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
1149
+ untracked(() => context.derivationLogger()).evaluation({
1150
+ debugName: entry.debugName,
1151
+ fieldKey: entry.fieldKey,
1152
+ result: "applied",
1153
+ previousValue: currentValue,
1154
+ newValue
1155
+ });
1156
+ subscriber.complete();
1157
+ } catch (error) {
1158
+ const message = error instanceof Error ? error.message : String(error);
1159
+ context.logger.warn(`${LOG_PREFIX$2} Failed to process result for '${entry.fieldKey}': ${message}`);
1160
+ subscriber.complete();
1161
+ }
1162
+ },
1163
+ error: (error) => {
1164
+ const message = error instanceof Error ? error.message : String(error);
1165
+ context.logger.warn(`${LOG_PREFIX$2} Async function failed for '${entry.fieldKey}': ${message}`);
1166
+ subscriber.complete();
1167
+ },
1168
+ complete: () => {
1169
+ subscriber.complete();
1170
+ }
1171
+ });
1172
+ return () => {
1173
+ asyncSub.unsubscribe();
1174
+ };
1175
+ });
1176
+ }), catchError$1((error) => {
1177
+ const message = error instanceof Error ? error.message : String(error);
1178
+ context.logger.warn(`${LOG_PREFIX$2} Unexpected stream error for '${entry.fieldKey}': ${message}`);
1179
+ return EMPTY;
1180
+ }));
1181
+ }
1182
+ function resolveExternalData(externalDataSignal) {
1183
+ const externalDataRecord = untracked(() => externalDataSignal?.());
1184
+ if (!externalDataRecord) return void 0;
1185
+ const resolved = {};
1186
+ for (const [key, value] of Object.entries(externalDataRecord)) if (isSignal(value)) resolved[key] = untracked(() => value());
1187
+ else resolved[key] = value;
1188
+ return resolved;
1189
+ }
1190
+ function getDebouncePeriods(entries) {
1191
+ const periods = /* @__PURE__ */ new Set();
1192
+ for (const entry of entries) if (entry.trigger === "debounced") periods.add(entry.debounceMs ?? 500);
1193
+ return Array.from(periods);
1194
+ }
1195
+ function entrySetsEqual(prev, next, sig) {
1196
+ if (prev.length !== next.length) return false;
1197
+ const counts = /* @__PURE__ */ new Map();
1198
+ for (const item of prev) {
1199
+ const key = sig(item);
1200
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1201
+ }
1202
+ for (const item of next) {
1203
+ const key = sig(item);
1204
+ const remaining = counts.get(key);
1205
+ if (!remaining) return false;
1206
+ if (remaining === 1) counts.delete(key);
1207
+ else counts.set(key, remaining - 1);
1208
+ }
1209
+ return counts.size === 0;
1210
+ }
1211
+ function buildEntryStreamPipeline(config) {
1212
+ return config.collection$.pipe(map((collection) => config.selectEntries(collection)), distinctUntilChanged((prev, next) => entrySetsEqual(prev, next, config.entrySignature)), switchMap((entries) => {
1213
+ if (entries.length === 0) return EMPTY;
1214
+ const streams = entries.map((entry) => config.createStream(entry)).filter((stream) => stream !== null);
1215
+ if (streams.length === 0) return EMPTY;
1216
+ return merge(...streams);
1217
+ }));
1218
+ }
1219
+ function rootSegment(path) {
1220
+ const dot = path.indexOf(".");
1221
+ return dot === -1 ? path : path.slice(0, dot);
1222
+ }
1223
+ function watchedRootKeys(collection) {
1224
+ const watched = /* @__PURE__ */ new Set();
1225
+ for (const entry of collection.entries) {
1226
+ const dependsOn = entry.dependsOn ?? [];
1227
+ if (dependsOn.length === 0 || dependsOn.includes("*")) continue;
1228
+ for (const dep of dependsOn) watched.add(rootSegment(dep));
1229
+ if (entry.fieldKey) watched.add(rootSegment(entry.fieldKey));
1230
+ }
1231
+ return watched;
1232
+ }
1233
+ function setupOnChangeStream(opts) {
1234
+ const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
1235
+ const formValue$ = toObservable(opts.formValueSignal, { injector: opts.injector });
1236
+ const extra$ = opts.additionalAwakeners?.length ? opts.additionalAwakeners.map((s) => toObservable(s, { injector: opts.injector })) : [];
1237
+ collection$.pipe(filter((collection) => collection !== null), map((collection) => [collection, watchedRootKeys(collection)]), combineLatestWith(formValue$, ...extra$), auditTime(0), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => {
1238
+ const [collection, watched] = current[0];
1239
+ return {
1240
+ collection,
1241
+ changedFields: getChangedKeysWithin(previous?.[1] ?? null, current[1], watched)
1242
+ };
1243
+ }), exhaustMap(({ collection, changedFields }) => {
1244
+ opts.applyOnChange(collection, changedFields);
1245
+ return scheduled([null], queueScheduler);
1246
+ }), takeUntilDestroyed(opts.destroyRef)).subscribe({ error: (err) => opts.logger.error(opts.errorLabel, err) });
1247
+ }
1248
+ function setupDebouncedStream(opts) {
1249
+ toObservable(opts.formValueSignal, { injector: opts.injector }).pipe(debounceTime(500), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({
1250
+ current,
1251
+ changedFields: getChangedKeys(previous, current)
1252
+ })), filter(({ changedFields }) => changedFields.size > 0), switchMap(({ changedFields }) => {
1253
+ const collection = untracked(() => opts.collectionSignal());
1254
+ if (!collection) return of(null);
1255
+ const debouncePeriods = getDebouncePeriods(collection.entries);
1256
+ if (debouncePeriods.length === 0) return of(null);
1257
+ const periodStreams = debouncePeriods.map((debounceMs) => {
1258
+ const additionalWait = Math.max(0, debounceMs - 500);
1259
+ if (additionalWait === 0) return of(null).pipe(map(() => {
1260
+ opts.applyDebouncedForPeriod(debounceMs, collection, changedFields);
1261
+ }));
1262
+ return timer(additionalWait).pipe(map(() => {
1263
+ const fresh = untracked(() => opts.collectionSignal()) ?? collection;
1264
+ opts.applyDebouncedForPeriod(debounceMs, fresh, changedFields);
1265
+ }));
1266
+ });
1267
+ return merge(...periodStreams);
1268
+ }), takeUntilDestroyed(opts.destroyRef)).subscribe({ error: (err) => opts.logger.error(opts.errorLabel, err) });
1269
+ }
1270
+ function setupEntryAsyncStream(opts) {
1271
+ buildEntryStreamPipeline({
1272
+ collection$: toObservable(opts.collectionSignal, { injector: opts.injector }),
1273
+ selectEntries: opts.selectEntries,
1274
+ entrySignature: opts.entrySignature,
1275
+ createStream: opts.createStream
1276
+ }).pipe(catchError((err) => {
1277
+ opts.logger.error(opts.errorLabel, err);
1278
+ return EMPTY;
1279
+ }), takeUntilDestroyed(opts.destroyRef)).subscribe();
1280
+ }
1281
+ function isAsyncPropertyDerivationEntry(entry) {
1282
+ return Boolean(entry.http) || Boolean(entry.asyncFunctionName) || Boolean(entry.asyncFn);
1283
+ }
1284
+ const ERROR_PREFIX = "[PropertyDerivation]";
1285
+ function applyPropertyDerivations(collection, context, changedFields) {
1286
+ let appliedCount = 0;
1287
+ let skippedCount = 0;
1288
+ let errorCount = 0;
1289
+ const entriesToProcess = (changedFields ? getEntriesForChangedFields(collection.entries, changedFields) : collection.entries).filter((entry) => !isAsyncPropertyDerivationEntry(entry));
1290
+ for (const entry of entriesToProcess) try {
1291
+ if (tryApplyPropertyDerivation(entry, context)) appliedCount++;
1292
+ else skippedCount++;
1293
+ } catch (error) {
1294
+ errorCount++;
1295
+ const errorMessage = error instanceof Error ? error.message : String(error);
1296
+ context.logger.error(`${ERROR_PREFIX} Failed to process property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}): ${errorMessage}`);
1297
+ }
1298
+ return {
1299
+ appliedCount,
1300
+ skippedCount,
1301
+ errorCount
1302
+ };
1303
+ }
1304
+ function applyPropertyDerivationsForTrigger(collection, trigger, context, changedFields) {
1305
+ return applyPropertyDerivations({ entries: collection.entries.filter((entry) => {
1306
+ if (trigger === "onChange") return !entry.trigger || entry.trigger === "onChange";
1307
+ return entry.trigger === trigger;
1308
+ }) }, context, changedFields);
1309
+ }
1310
+ function getEntriesForChangedFields(entries, changedFields) {
1311
+ return entries.filter((entry) => {
1312
+ if (entry.dependsOn.includes("*")) return true;
1313
+ if (entry.dependsOn.some((dep) => changedFields.has(dep))) return true;
1314
+ for (const changed of changedFields) {
1315
+ if (entry.fieldKey.startsWith(changed + ".")) return true;
1316
+ if (entry.dependsOn.some((dep) => dep.startsWith(changed + "."))) return true;
1317
+ }
1318
+ return false;
1319
+ });
1320
+ }
1321
+ function tryApplyPropertyDerivation(entry, context) {
1322
+ if (isArrayPlaceholderPath(entry.fieldKey)) return tryApplyArrayPropertyDerivation(entry, context);
1323
+ const evalContext = createEvaluationContext(entry, untracked(() => context.formValue()), context);
1324
+ if (!evaluatePropertyCondition(entry.condition, evalContext)) return false;
1325
+ const newValue = computePropertyValue(entry, evalContext, context);
1326
+ context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue);
1327
+ return true;
1328
+ }
1329
+ function tryApplyArrayPropertyDerivation(entry, context) {
1330
+ const formValue = untracked(() => context.formValue());
1331
+ const pathInfo = parseArrayPath(entry.fieldKey);
1332
+ if (!pathInfo.isArrayPath) return false;
1333
+ const { arrayPath } = pathInfo;
1334
+ const arrayValue = getNestedValue(formValue, arrayPath);
1335
+ if (!Array.isArray(arrayValue)) return false;
1336
+ let appliedAny = false;
1337
+ for (let i = 0; i < arrayValue.length; i++) {
1338
+ const resolvedPath = resolveArrayPath(entry.fieldKey, i);
1339
+ const arrayItem = arrayValue[i];
1340
+ const evalContext = createArrayItemEvaluationContext(entry, arrayItem, formValue, i, arrayPath, context);
1341
+ if (!evaluatePropertyCondition(entry.condition, evalContext)) continue;
1342
+ try {
1343
+ const newValue = computePropertyValue(entry, evalContext, context);
1344
+ context.store.setOverride(resolvedPath, entry.targetProperty, newValue);
1345
+ appliedAny = true;
1346
+ } catch (error) {
1347
+ const errorMessage = error instanceof Error ? error.message : String(error);
1348
+ context.logger.error(`${ERROR_PREFIX} Failed to compute property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}, index: ${i}): ${errorMessage}`);
1349
+ }
1350
+ }
1351
+ return appliedAny;
1352
+ }
1353
+ function createEvaluationContext(entry, formValue, context) {
1354
+ const fieldValue = getNestedValue(formValue, entry.fieldKey);
1355
+ const parentPath = getParentPathInScope(entry.fieldKey);
1356
+ const groupValue = parentPath ? getNestedValue(formValue, parentPath) : void 0;
1357
+ return {
1358
+ fieldValue,
1359
+ formValue: parentPath !== void 0 ? groupValue ?? {} : formValue,
1360
+ fieldPath: entry.fieldKey,
1361
+ groupValue,
1362
+ customFunctions: context.customFunctions,
1363
+ externalData: context.externalData,
1364
+ logger: context.logger,
1365
+ rootFormValue: formValue
1366
+ };
1367
+ }
1368
+ function createArrayItemEvaluationContext(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context) {
1369
+ const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(parseArrayPath(entry.fieldKey), arrayItem);
1370
+ return {
1371
+ fieldValue,
1372
+ formValue: arrayItem,
1373
+ fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
1374
+ groupValue,
1375
+ customFunctions: context.customFunctions,
1376
+ externalData: context.externalData,
1377
+ logger: context.logger,
1378
+ rootFormValue,
1379
+ arrayIndex: itemIndex,
1380
+ arrayPath
1381
+ };
1382
+ }
1383
+ function evaluatePropertyCondition(condition, context) {
1384
+ if (typeof condition === "boolean") return condition;
1385
+ return evaluateCondition(condition, context);
1386
+ }
1387
+ function computePropertyValue(entry, evalContext, applicatorContext) {
1388
+ return computeValueFromEntry(entry, evalContext, {
1389
+ derivationFunctions: applicatorContext.derivationFunctions,
1390
+ subject: `${entry.fieldKey}.${entry.targetProperty}`,
1391
+ functionKind: "Property derivation function"
1392
+ });
1393
+ }
1394
+ const LOG_PREFIX$1 = "HTTP Property Derivation -";
1395
+ const DEFAULT_HTTP_DEBOUNCE_MS = 300;
1396
+ function createHttpPropertyDerivationStream(entry, formValue$, context) {
1397
+ if (isArrayPlaceholderPath(entry.fieldKey)) {
1398
+ context.logger.warn(`${LOG_PREFIX$1} HTTP property derivation for array field '${entry.fieldKey}' is not supported. Array HTTP property derivations will be supported in a future release.`);
1399
+ return EMPTY;
1400
+ }
1401
+ if (!entry.http || !entry.responseExpression) return EMPTY;
1402
+ const responseExpression = entry.responseExpression;
1403
+ const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS;
1404
+ return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
1405
+ current,
1406
+ changedFields: getChangedKeys(previous, current)
1407
+ })), filter(({ changedFields }) => {
1408
+ if (changedFields.size === 0) return false;
1409
+ return entry.dependsOn.some((dep) => changedFields.has(dep));
1410
+ }), debounceTime(debounceMs), switchMap(({ current }) => {
1411
+ return new Observable((subscriber) => {
1412
+ const customFunctions = context.customFunctions?.();
1413
+ const externalData = context.externalData?.();
1414
+ const evalContext = {
1415
+ fieldValue: getNestedValue(current, entry.fieldKey),
1416
+ formValue: current,
1417
+ fieldPath: entry.fieldKey,
1418
+ customFunctions,
1419
+ externalData,
1420
+ logger: context.logger
1421
+ };
1422
+ if (entry.condition !== true) {
1423
+ if (!(typeof entry.condition === "boolean" ? entry.condition : evaluateCondition(entry.condition, evalContext))) {
1424
+ subscriber.complete();
1425
+ return;
1426
+ }
1427
+ }
1428
+ const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
1429
+ if (!resolvedRequest) {
1430
+ subscriber.complete();
1431
+ return;
1432
+ }
1433
+ const method = (resolvedRequest.method ?? "GET").toUpperCase();
1434
+ const httpSub = context.httpClient.request(method, resolvedRequest.url, {
1435
+ body: resolvedRequest.body,
1436
+ headers: resolvedRequest.headers
1437
+ }).subscribe({
1438
+ next: (response) => {
1439
+ try {
1440
+ const newValue = ExpressionParser.evaluate(responseExpression, { response });
1441
+ untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
1442
+ subscriber.complete();
1443
+ } catch (error) {
1444
+ const message = error instanceof Error ? error.message : String(error);
1445
+ context.logger.warn(`${LOG_PREFIX$1} Failed to process response for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
1446
+ subscriber.complete();
1447
+ }
1448
+ },
1449
+ error: (error) => {
1450
+ const message = error instanceof Error ? error.message : String(error);
1451
+ context.logger.warn(`${LOG_PREFIX$1} HTTP request failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
1452
+ subscriber.complete();
1453
+ }
1454
+ });
1455
+ return () => {
1456
+ httpSub.unsubscribe();
1457
+ };
1458
+ });
1459
+ }), catchError$1((error) => {
1460
+ const message = error instanceof Error ? error.message : String(error);
1461
+ context.logger.warn(`${LOG_PREFIX$1} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
1462
+ return EMPTY;
1463
+ }));
1464
+ }
1465
+ const LOG_PREFIX = "Async Property Derivation -";
1466
+ const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
1467
+ function createAsyncPropertyDerivationStream(entry, formValue$, context) {
1468
+ if (isArrayPlaceholderPath(entry.fieldKey)) {
1469
+ context.logger.warn(`${LOG_PREFIX} Async property derivation for array field '${entry.fieldKey}' is not supported. Array async property derivations will be supported in a future release.`);
1470
+ return EMPTY;
1471
+ }
1472
+ if (!entry.asyncFunctionName && !entry.asyncFn) {
1473
+ context.logger.warn(`${LOG_PREFIX} Property derivation for '${entry.fieldKey}.${entry.targetProperty}' has neither "asyncFunctionName" nor "asyncFn". One of the two is required for source: 'asyncFunction'.`);
1474
+ return EMPTY;
1475
+ }
1476
+ if (entry.asyncFn && entry.asyncFunctionName) context.logger.warn(`${LOG_PREFIX} Both "asyncFn" and "asyncFunctionName" are set on property derivation for '${entry.fieldKey}.${entry.targetProperty}'. Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
1477
+ const inlineAsyncFn = entry.asyncFn;
1478
+ const asyncFunctionName = entry.asyncFunctionName;
1479
+ const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS;
1480
+ return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
1481
+ current,
1482
+ changedFields: getChangedKeys(previous, current)
1483
+ })), filter(({ changedFields }) => {
1484
+ if (changedFields.size === 0) return false;
1485
+ return entry.dependsOn.some((dep) => changedFields.has(dep));
1486
+ }), debounceTime(debounceMs), switchMap(({ current }) => {
1487
+ return new Observable((subscriber) => {
1488
+ const customFunctions = context.customFunctions?.();
1489
+ const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
1490
+ const externalData = context.externalData?.();
1491
+ const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : void 0);
1492
+ if (!asyncFn) {
1493
+ context.logger.warn(`${LOG_PREFIX} Async derivation function '${asyncFunctionName}' not found for '${entry.fieldKey}.${entry.targetProperty}'. Register it in customFnConfig.asyncDerivations.`);
1494
+ subscriber.complete();
1495
+ return;
1496
+ }
1497
+ const evalContext = {
1498
+ fieldValue: getNestedValue(current, entry.fieldKey),
1499
+ formValue: current,
1500
+ fieldPath: entry.fieldKey,
1501
+ customFunctions,
1502
+ externalData,
1503
+ logger: context.logger
1504
+ };
1505
+ if (entry.condition !== true) {
1506
+ if (!(typeof entry.condition === "boolean" ? entry.condition : evaluateCondition(entry.condition, evalContext))) {
1507
+ subscriber.complete();
1508
+ return;
1509
+ }
1510
+ }
1511
+ const asyncSub = from(asyncFn(evalContext)).subscribe({
1512
+ next: (newValue) => {
1513
+ try {
1514
+ untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
1515
+ subscriber.complete();
1516
+ } catch (error) {
1517
+ const message = error instanceof Error ? error.message : String(error);
1518
+ context.logger.warn(`${LOG_PREFIX} Failed to process result for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
1519
+ subscriber.complete();
1520
+ }
1521
+ },
1522
+ error: (error) => {
1523
+ const message = error instanceof Error ? error.message : String(error);
1524
+ context.logger.warn(`${LOG_PREFIX} Async function failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
1525
+ subscriber.complete();
1526
+ },
1527
+ complete: () => {
1528
+ subscriber.complete();
1529
+ }
1530
+ });
1531
+ return () => {
1532
+ asyncSub.unsubscribe();
1533
+ };
1534
+ });
1535
+ }), catchError$1((error) => {
1536
+ const message = error instanceof Error ? error.message : String(error);
1537
+ context.logger.warn(`${LOG_PREFIX} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
1538
+ return EMPTY;
1539
+ }));
1540
+ }
1541
+ var DerivationOrchestrator = class {
1542
+ config;
1543
+ injector = inject(Injector);
1544
+ destroyRef = inject(DestroyRef);
1545
+ logger = inject(DynamicFormLogger);
1546
+ warningTracker = inject(DERIVATION_WARNING_TRACKER);
1547
+ functionRegistry = inject(FunctionRegistryService$1);
1548
+ httpClient = inject(HttpClient, { optional: true });
1549
+ formOptions;
1550
+ derivationCollection;
1551
+ propertyDerivationCollection;
1552
+ constructor(config) {
1553
+ this.config = config;
1554
+ const valueEnabled = !!(config.form && config.derivationLogger);
1555
+ const propertyStore = config.propertyStore;
1556
+ const sharedAllDerivations = valueEnabled && !!propertyStore ? computed(() => {
1557
+ const fields = config.schemaFields();
1558
+ if (!fields || fields.length === 0) return null;
1559
+ return collectAllDerivations(fields);
1560
+ }) : null;
1561
+ this.derivationCollection = valueEnabled ? this.setupValuePipeline(sharedAllDerivations) : computed(() => null);
1562
+ this.propertyDerivationCollection = propertyStore ? this.setupPropertyPipeline(propertyStore, sharedAllDerivations) : computed(() => null);
1563
+ }
1564
+ setupValuePipeline(sharedAllDerivations) {
1565
+ this.formOptions = inject(FORM_OPTIONS);
1566
+ const collection = computed(() => {
1567
+ const fields = this.config.schemaFields();
1568
+ if (!fields || fields.length === 0) return null;
1569
+ const collected = sharedAllDerivations ? sharedAllDerivations()?.value ?? null : collectDerivations(fields);
1570
+ if (!collected || collected.entries.length === 0) return null;
1571
+ validateNoCycles(collected, this.logger);
1572
+ if (DEV_MODE) {
1573
+ warnAboutWildcardDependencies(this.logger, collected.entries, fields.length);
1574
+ warnAboutMisconfiguredReEngagement(this.logger, collected.entries);
1575
+ }
1576
+ return collected;
1577
+ }, ...ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []);
1578
+ setupOnChangeStream({
1579
+ injector: this.injector,
1580
+ destroyRef: this.destroyRef,
1581
+ logger: this.logger,
1582
+ errorLabel: "Derivation onChange stream error",
1583
+ collectionSignal: collection,
1584
+ formValueSignal: this.config.formValue,
1585
+ additionalAwakeners: [this.config.form],
1586
+ applyOnChange: (current, changedFields) => {
1587
+ this.applyOnChangeDerivations(current, untracked(() => this.config.form()), changedFields);
1588
+ }
1589
+ });
1590
+ setupDebouncedStream({
1591
+ injector: this.injector,
1592
+ destroyRef: this.destroyRef,
1593
+ logger: this.logger,
1594
+ errorLabel: "Derivation debounced stream error",
1595
+ collectionSignal: collection,
1596
+ formValueSignal: this.config.formValue,
1597
+ applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
1598
+ const formAccessor = untracked(() => this.config.form());
1599
+ if (!formAccessor) return;
1600
+ this.applyDebouncedEntriesForPeriod(debounceMs, current, formAccessor, changedFields);
1601
+ }
1602
+ });
1603
+ this.setupValueHttpStreams(collection);
1604
+ this.setupValueAsyncFunctionStreams(collection);
1605
+ return collection;
1606
+ }
1607
+ setupPropertyPipeline(propertyStore, sharedAllDerivations) {
1608
+ const collection = computed(() => {
1609
+ const fields = this.config.schemaFields();
1610
+ if (!fields || fields.length === 0) return null;
1611
+ const collected = sharedAllDerivations ? sharedAllDerivations()?.property ?? null : collectPropertyDerivations(fields);
1612
+ if (!collected || collected.entries.length === 0) return null;
1613
+ return collected;
1614
+ }, ...ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []);
1615
+ explicitEffect([collection, this.config.schemaFields], ([current, fields]) => {
1616
+ propertyStore.clear();
1617
+ if (!current) return;
1618
+ for (const entry of current.entries) propertyStore.registerField(entry.fieldKey);
1619
+ if (DEV_MODE) warnAboutWildcardPropertyDependencies(this.logger, current.entries, fields?.length ?? 0);
1620
+ });
1621
+ setupOnChangeStream({
1622
+ injector: this.injector,
1623
+ destroyRef: this.destroyRef,
1624
+ logger: this.logger,
1625
+ errorLabel: "Property derivation onChange stream error",
1626
+ collectionSignal: collection,
1627
+ formValueSignal: this.config.formValue,
1628
+ applyOnChange: (current) => this.applyOnChangePropertyDerivations(current, propertyStore)
1629
+ });
1630
+ setupDebouncedStream({
1631
+ injector: this.injector,
1632
+ destroyRef: this.destroyRef,
1633
+ logger: this.logger,
1634
+ errorLabel: "Property derivation debounced stream error",
1635
+ collectionSignal: collection,
1636
+ formValueSignal: this.config.formValue,
1637
+ applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
1638
+ this.applyDebouncedPropertyEntriesForPeriod(debounceMs, current, propertyStore, changedFields);
1639
+ }
1640
+ });
1641
+ this.setupPropertyHttpStreams(collection, propertyStore);
1642
+ this.setupPropertyAsyncFunctionStreams(collection, propertyStore);
1643
+ return collection;
1644
+ }
1645
+ applyOnChangeDerivations(collection, formAccessor, changedFields) {
1646
+ const result = applyDerivationsForTrigger(collection, "onChange", {
1647
+ formValue: this.config.formValue,
1648
+ rootForm: formAccessor,
1649
+ derivationFunctions: this.functionRegistry.getDerivationFunctions(),
1650
+ customFunctions: this.functionRegistry.getCustomFunctions(),
1651
+ externalData: resolveExternalData(this.config.externalData),
1652
+ logger: this.logger,
1653
+ warningTracker: this.warningTracker,
1654
+ derivationLogger: untracked(() => this.config.derivationLogger()),
1655
+ maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations)
1656
+ }, changedFields);
1657
+ if (result.maxIterationsReached) this.logger.warn(`Derivation processing reached max iterations. This may indicate a loop in derivation logic that wasn't caught at build time. Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
1658
+ }
1659
+ applyDebouncedEntriesForPeriod(debounceMs, collection, formAccessor, changedFields) {
1660
+ const debouncedEntries = collection.entries.filter((entry) => entry.trigger === "debounced" && (entry.debounceMs ?? 500) === debounceMs);
1661
+ if (debouncedEntries.length === 0) return;
1662
+ const result = applyDerivationsForTrigger({ entries: debouncedEntries }, "debounced", {
1663
+ formValue: this.config.formValue,
1664
+ rootForm: formAccessor,
1665
+ derivationFunctions: this.functionRegistry.getDerivationFunctions(),
1666
+ customFunctions: this.functionRegistry.getCustomFunctions(),
1667
+ externalData: resolveExternalData(this.config.externalData),
1668
+ logger: this.logger,
1669
+ warningTracker: this.warningTracker,
1670
+ derivationLogger: untracked(() => this.config.derivationLogger()),
1671
+ maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations)
1672
+ }, changedFields);
1673
+ if (result.maxIterationsReached) this.logger.warn(`Debounced derivation processing reached max iterations (${debounceMs}ms). Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
1674
+ }
1675
+ setupValueHttpStreams(collectionSignal) {
1676
+ const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
1677
+ setupEntryAsyncStream({
1678
+ injector: this.injector,
1679
+ destroyRef: this.destroyRef,
1680
+ logger: this.logger,
1681
+ errorLabel: "HTTP Derivation: outer stream error",
1682
+ collectionSignal,
1683
+ selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
1684
+ entrySignature: valueHttpEntrySignature,
1685
+ createStream: (entry) => {
1686
+ if (!this.httpClient) {
1687
+ this.logger.error("HTTP Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.");
1688
+ return null;
1689
+ }
1690
+ const context = {
1691
+ formValue: this.config.formValue,
1692
+ form: this.config.form,
1693
+ httpClient: this.httpClient,
1694
+ logger: this.logger,
1695
+ derivationLogger: this.config.derivationLogger,
1696
+ customFunctions: () => this.functionRegistry.getCustomFunctions(),
1697
+ externalData: () => resolveExternalData(this.config.externalData),
1698
+ warningTracker: this.warningTracker
1699
+ };
1700
+ return createHttpDerivationStream(entry, formValue$, context);
1701
+ }
1702
+ });
1703
+ }
1704
+ setupValueAsyncFunctionStreams(collectionSignal) {
1705
+ const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
1706
+ setupEntryAsyncStream({
1707
+ injector: this.injector,
1708
+ destroyRef: this.destroyRef,
1709
+ logger: this.logger,
1710
+ errorLabel: "Async Derivation: outer stream error",
1711
+ collectionSignal,
1712
+ selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
1713
+ entrySignature: valueAsyncEntrySignature,
1714
+ createStream: (entry) => createAsyncDerivationStream(entry, formValue$, {
1715
+ formValue: this.config.formValue,
1716
+ form: this.config.form,
1717
+ logger: this.logger,
1718
+ derivationLogger: this.config.derivationLogger,
1719
+ customFunctions: () => this.functionRegistry.getCustomFunctions(),
1720
+ asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
1721
+ externalData: () => resolveExternalData(this.config.externalData),
1722
+ warningTracker: this.warningTracker
1723
+ })
1724
+ });
1725
+ }
1726
+ applyOnChangePropertyDerivations(collection, store) {
1727
+ applyPropertyDerivationsForTrigger(collection, "onChange", {
1728
+ formValue: this.config.formValue,
1729
+ store,
1730
+ derivationFunctions: this.functionRegistry.getDerivationFunctions(),
1731
+ customFunctions: this.functionRegistry.getCustomFunctions(),
1732
+ externalData: resolveExternalData(this.config.externalData),
1733
+ logger: this.logger,
1734
+ warningTracker: this.warningTracker
1735
+ });
1736
+ }
1737
+ applyDebouncedPropertyEntriesForPeriod(debounceMs, collection, store, changedFields) {
1738
+ const debouncedEntries = collection.entries.filter((entry) => entry.trigger === "debounced" && (entry.debounceMs ?? 500) === debounceMs);
1739
+ if (debouncedEntries.length === 0) return;
1740
+ applyPropertyDerivationsForTrigger({ entries: debouncedEntries }, "debounced", {
1741
+ formValue: this.config.formValue,
1742
+ store,
1743
+ derivationFunctions: this.functionRegistry.getDerivationFunctions(),
1744
+ customFunctions: this.functionRegistry.getCustomFunctions(),
1745
+ externalData: resolveExternalData(this.config.externalData),
1746
+ logger: this.logger,
1747
+ warningTracker: this.warningTracker
1748
+ }, changedFields);
1749
+ }
1750
+ setupPropertyHttpStreams(collectionSignal, store) {
1751
+ const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
1752
+ setupEntryAsyncStream({
1753
+ injector: this.injector,
1754
+ destroyRef: this.destroyRef,
1755
+ logger: this.logger,
1756
+ errorLabel: "HTTP Property Derivation: outer stream error",
1757
+ collectionSignal,
1758
+ selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
1759
+ entrySignature: propertyHttpEntrySignature,
1760
+ createStream: (entry) => {
1761
+ if (!this.httpClient) {
1762
+ this.logger.error("HTTP Property Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.");
1763
+ return null;
1764
+ }
1765
+ return createHttpPropertyDerivationStream(entry, formValue$, {
1766
+ formValue: this.config.formValue,
1767
+ store,
1768
+ httpClient: this.httpClient,
1769
+ logger: this.logger,
1770
+ customFunctions: () => this.functionRegistry.getCustomFunctions(),
1771
+ externalData: () => resolveExternalData(this.config.externalData)
1772
+ });
1773
+ }
1774
+ });
1775
+ }
1776
+ setupPropertyAsyncFunctionStreams(collectionSignal, store) {
1777
+ const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
1778
+ setupEntryAsyncStream({
1779
+ injector: this.injector,
1780
+ destroyRef: this.destroyRef,
1781
+ logger: this.logger,
1782
+ errorLabel: "Async Property Derivation: outer stream error",
1783
+ collectionSignal,
1784
+ selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
1785
+ entrySignature: propertyAsyncEntrySignature,
1786
+ createStream: (entry) => createAsyncPropertyDerivationStream(entry, formValue$, {
1787
+ formValue: this.config.formValue,
1788
+ store,
1789
+ logger: this.logger,
1790
+ customFunctions: () => this.functionRegistry.getCustomFunctions(),
1791
+ asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
1792
+ externalData: () => resolveExternalData(this.config.externalData)
1793
+ })
1794
+ });
1795
+ }
1796
+ };
1797
+ function valueHttpEntrySignature(entry) {
1798
+ const config = {
1799
+ http: entry.http,
1800
+ responseExpression: entry.responseExpression,
1801
+ dependsOn: entry.dependsOn,
1802
+ debounceMs: entry.debounceMs,
1803
+ trigger: entry.trigger,
1804
+ condition: entry.condition,
1805
+ stopOnUserOverride: entry.stopOnUserOverride,
1806
+ reEngageOnDependencyChange: entry.reEngageOnDependencyChange
1807
+ };
1808
+ return `${entry.fieldKey}:${JSON.stringify(config)}`;
1809
+ }
1810
+ function valueAsyncEntrySignature(entry) {
1811
+ const config = {
1812
+ asyncFunctionName: entry.asyncFunctionName,
1813
+ asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}` : void 0,
1814
+ dependsOn: entry.dependsOn,
1815
+ debounceMs: entry.debounceMs,
1816
+ trigger: entry.trigger,
1817
+ condition: entry.condition,
1818
+ stopOnUserOverride: entry.stopOnUserOverride,
1819
+ reEngageOnDependencyChange: entry.reEngageOnDependencyChange
1820
+ };
1821
+ return `${entry.fieldKey}:${JSON.stringify(config)}`;
1822
+ }
1823
+ function propertyHttpEntrySignature(entry) {
1824
+ const config = {
1825
+ http: entry.http,
1826
+ responseExpression: entry.responseExpression,
1827
+ dependsOn: entry.dependsOn,
1828
+ debounceMs: entry.debounceMs,
1829
+ trigger: entry.trigger,
1830
+ condition: entry.condition
1831
+ };
1832
+ return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
1833
+ }
1834
+ function propertyAsyncEntrySignature(entry) {
1835
+ const config = {
1836
+ asyncFunctionName: entry.asyncFunctionName,
1837
+ asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}.${entry.targetProperty}` : void 0,
1838
+ dependsOn: entry.dependsOn,
1839
+ debounceMs: entry.debounceMs,
1840
+ trigger: entry.trigger,
1841
+ condition: entry.condition
1842
+ };
1843
+ return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
1844
+ }
1845
+ function createDerivationOrchestrator(config) {
1846
+ return new DerivationOrchestrator(config);
1847
+ }
1848
+ function warnAboutWildcardDependencies(logger, entries, fieldCount) {
1849
+ const wildcardEntries = entries.filter((entry) => entry.dependsOn.includes("*"));
1850
+ if (wildcardEntries.length === 0) return;
1851
+ const implicitWildcards = wildcardEntries.filter((entry) => !entry.http && !entry.asyncFunctionName && !entry.asyncFn && (entry.functionName || entry.fn) && (!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
1852
+ if (implicitWildcards.length > 0) {
1853
+ const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey} (${e.functionName ?? "<inline fn>"})`);
1854
+ logger.warn(`Derivation: custom functions without explicit dependsOn detected. These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). Consider specifying explicit dependsOn arrays for better performance.`, derivationDescs);
1855
+ }
1856
+ }
1857
+ function warnAboutMisconfiguredReEngagement(logger, entries) {
1858
+ const misconfigured = entries.filter((entry) => entry.reEngageOnDependencyChange && !entry.stopOnUserOverride);
1859
+ if (misconfigured.length === 0) return;
1860
+ const fieldKeys = misconfigured.map((e) => e.debugName ?? e.fieldKey);
1861
+ logger.warn("Derivation: reEngageOnDependencyChange without stopOnUserOverride detected. reEngageOnDependencyChange only takes effect when stopOnUserOverride is true. Either add stopOnUserOverride: true or remove reEngageOnDependencyChange.", fieldKeys);
1862
+ }
1863
+ function warnAboutWildcardPropertyDependencies(logger, entries, fieldCount) {
1864
+ const implicitWildcards = entries.filter((entry) => entry.dependsOn.includes("*") && (entry.functionName || entry.fn) && (!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
1865
+ if (implicitWildcards.length > 0) {
1866
+ const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey}.${e.targetProperty} (${e.functionName ?? "<inline fn>"})`);
1867
+ logger.warn(`PropertyDerivation: custom functions without explicit dependsOn detected. These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). Consider specifying explicit dependsOn arrays for better performance.`, derivationDescs);
1868
+ }
1869
+ }
1870
+ function bootstrapDerivationOrchestrator() {
1871
+ const stateManager = inject(FormStateManager);
1872
+ const logger = inject(DynamicFormLogger);
1873
+ const logConfig = inject(DERIVATION_LOG_CONFIG);
1874
+ const externalData = inject(EXTERNAL_DATA);
1875
+ const propertyStore = inject(PROPERTY_OVERRIDE_STORE);
1876
+ createDerivationOrchestrator({
1877
+ schemaFields: computed(() => stateManager.formSetup()?.schemaFields),
1878
+ formValue: stateManager.formValue,
1879
+ form: computed(() => stateManager.form()),
1880
+ derivationLogger: computed(() => createDerivationLogger(logConfig, logger)),
1881
+ propertyStore,
1882
+ externalData
1883
+ });
1884
+ }
1885
+ export { bootstrapDerivationOrchestrator };
1886
+
1887
+ //# sourceMappingURL=ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-EuYCbquo.mjs.map