@ng-forge/dynamic-forms 0.10.0-next.3 → 0.10.0-next.4

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 (56) hide show
  1. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-D6GEsH72.mjs → ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs} +4 -3
  2. package/fesm2022/ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs.map +1 -0
  3. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-BluTT44A.mjs → ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs} +4 -3
  4. package/fesm2022/ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs.map +1 -0
  5. package/fesm2022/{ng-forge-dynamic-forms-container-utils-diYAWMa_.mjs → ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs} +2 -2
  6. package/fesm2022/ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs.map +1 -0
  7. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-CgMQOaG7.mjs → ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs} +2 -2
  8. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs.map +1 -0
  9. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-CB86IMsQ.mjs → ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs} +2 -2
  10. package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs.map +1 -0
  11. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-2bWHql4u.mjs → ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs} +4 -3
  12. package/fesm2022/ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs.map +1 -0
  13. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +14 -10
  14. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  15. package/fesm2022/ng-forge-dynamic-forms-internal.mjs +3372 -0
  16. package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -0
  17. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CPkVjU_N.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs} +635 -3887
  18. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map +1 -0
  19. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-zF-vEv5V.mjs → ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs} +4 -3
  20. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map +1 -0
  21. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map +1 -1
  22. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-_8_vVeH0.mjs → ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs} +2 -2
  23. package/fesm2022/ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map +1 -0
  24. package/fesm2022/ng-forge-dynamic-forms.mjs +2 -1
  25. package/fesm2022/ng-forge-dynamic-forms.mjs.map +1 -1
  26. package/package.json +14 -1
  27. package/schematics/collection.json +10 -0
  28. package/schematics/generators.json +12 -0
  29. package/schematics/ng-add/index.js +87 -0
  30. package/schematics/ng-add/index.js.map +1 -0
  31. package/schematics/ng-add/nx-wrapper.js +5 -0
  32. package/schematics/ng-add/nx-wrapper.js.map +1 -0
  33. package/schematics/ng-add/recipes.js +60 -0
  34. package/schematics/ng-add/recipes.js.map +1 -0
  35. package/schematics/ng-add/rules/add-packages.js +73 -0
  36. package/schematics/ng-add/rules/add-packages.js.map +1 -0
  37. package/schematics/ng-add/rules/add-providers.js +132 -0
  38. package/schematics/ng-add/rules/add-providers.js.map +1 -0
  39. package/schematics/ng-add/rules/add-styles.js +123 -0
  40. package/schematics/ng-add/rules/add-styles.js.map +1 -0
  41. package/schematics/ng-add/schema.json +48 -0
  42. package/schematics/package.json +1 -0
  43. package/schematics/versions.js +33 -0
  44. package/schematics/versions.js.map +1 -0
  45. package/types/ng-forge-dynamic-forms-integration.d.ts +4 -2
  46. package/types/ng-forge-dynamic-forms-internal.d.ts +3508 -0
  47. package/types/ng-forge-dynamic-forms.d.ts +14 -2733
  48. package/fesm2022/ng-forge-dynamic-forms-array-field.component-D6GEsH72.mjs.map +0 -1
  49. package/fesm2022/ng-forge-dynamic-forms-container-field.component-BluTT44A.mjs.map +0 -1
  50. package/fesm2022/ng-forge-dynamic-forms-container-utils-diYAWMa_.mjs.map +0 -1
  51. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-CgMQOaG7.mjs.map +0 -1
  52. package/fesm2022/ng-forge-dynamic-forms-emit-initialization-CB86IMsQ.mjs.map +0 -1
  53. package/fesm2022/ng-forge-dynamic-forms-group-field.component-2bWHql4u.mjs.map +0 -1
  54. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CPkVjU_N.mjs.map +0 -1
  55. package/fesm2022/ng-forge-dynamic-forms-page-field.component-zF-vEv5V.mjs.map +0 -1
  56. package/fesm2022/ng-forge-dynamic-forms-text-field.component-_8_vVeH0.mjs.map +0 -1
@@ -0,0 +1,3372 @@
1
+ import * as i0 from '@angular/core';
2
+ import { isSignal, untracked, InjectionToken, inject, Injectable, linkedSignal, resourceFromSnapshots, signal, Injector, computed, Pipe } from '@angular/core';
3
+ import { defer, combineLatest, pipe, debounceTime, distinctUntilChanged, of, from, map, catchError, tap, startWith, Subject, filter, isObservable, TimeoutError, throwError } from 'rxjs';
4
+ import { toObservable, toSignal, rxResource } from '@angular/core/rxjs-interop';
5
+ import { HttpClient } from '@angular/common/http';
6
+ import { email, min, max, minLength, maxLength, pattern, required, validate, validateAsync, validateHttp } from '@angular/forms/signals';
7
+ import { scan, map as map$1, filter as filter$1, take, switchMap, timeout, catchError as catchError$1, shareReplay } from 'rxjs/operators';
8
+
9
+ function createDefaultDerivationLogConfig() {
10
+ return {
11
+ level: 'none',
12
+ };
13
+ }
14
+ function shouldLog(config, minLevel) {
15
+ if (config.level === 'none')
16
+ return false;
17
+ if (minLevel === 'summary')
18
+ return config.level === 'summary' || config.level === 'verbose';
19
+ return config.level === 'verbose';
20
+ }
21
+ function isFormStateCondition(condition) {
22
+ return typeof condition === 'string' && ['formInvalid', 'formSubmitting', 'pageInvalid'].includes(condition);
23
+ }
24
+ function isStateLogicConfig(config) {
25
+ return config.type === 'hidden' || config.type === 'readonly' || config.type === 'disabled' || config.type === 'required';
26
+ }
27
+ function isDerivationLogicConfig(config) {
28
+ return config.type === 'derivation';
29
+ }
30
+ function hasTargetProperty(config) {
31
+ return 'targetProperty' in config && typeof config.targetProperty === 'string' && config.targetProperty.length > 0;
32
+ }
33
+
34
+ const FORM_VALUE_ACCESS_PATTERN = /\bformValue\s*(?:\.|\[)/;
35
+ const FORM_VALUE_PATTERN = /\bformValue\s*(?:\.([\w.]+)|\[\s*'([\w.-]+)'\s*\]|\[\s*"([\w.-]+)"\s*\])/g;
36
+ function isCrossFieldExpression(expr, context) {
37
+ if (expr === undefined || typeof expr === 'boolean' || isFormStateCondition(expr)) {
38
+ return false;
39
+ }
40
+ switch (expr.type) {
41
+ case 'fieldValue':
42
+ return !!expr.fieldPath;
43
+ case 'javascript':
44
+ return FORM_VALUE_ACCESS_PATTERN.test(expr.expression || '');
45
+ case 'custom': {
46
+ const functionName = expr.functionName;
47
+ if (context?.getFunctionScope && functionName) {
48
+ const scope = context.getFunctionScope(functionName);
49
+ if (scope === 'field') {
50
+ return false;
51
+ }
52
+ }
53
+ return true;
54
+ }
55
+ case 'async':
56
+ return false;
57
+ case 'and':
58
+ case 'or':
59
+ return (expr.conditions || []).some((c) => isCrossFieldExpression(c, context));
60
+ default:
61
+ return false;
62
+ }
63
+ }
64
+ function extractExpressionDependencies(expr) {
65
+ if (expr === undefined || typeof expr === 'boolean') {
66
+ return [];
67
+ }
68
+ const deps = new Set();
69
+ switch (expr.type) {
70
+ case 'fieldValue':
71
+ if (expr.fieldPath) {
72
+ deps.add(expr.fieldPath.split('.')[0]);
73
+ }
74
+ break;
75
+ case 'javascript':
76
+ extractFromExpressionString(expr.expression || '', deps);
77
+ break;
78
+ case 'custom':
79
+ deps.add('*');
80
+ break;
81
+ case 'async':
82
+ break;
83
+ case 'and':
84
+ case 'or':
85
+ for (const condition of expr.conditions || []) {
86
+ extractExpressionDependencies(condition).forEach((d) => deps.add(d));
87
+ }
88
+ break;
89
+ }
90
+ return Array.from(deps);
91
+ }
92
+ function extractStringDependencies(expression) {
93
+ const deps = new Set();
94
+ extractFromExpressionString(expression, deps);
95
+ return Array.from(deps);
96
+ }
97
+ function extractFromExpressionString(expression, deps) {
98
+ for (const match of expression.matchAll(FORM_VALUE_PATTERN)) {
99
+ const fullPath = match[1] ?? match[2] ?? match[3];
100
+ if (!fullPath)
101
+ continue;
102
+ const rootField = fullPath.split('.')[0];
103
+ deps.add(rootField);
104
+ if (fullPath.includes('.')) {
105
+ deps.add(fullPath);
106
+ }
107
+ }
108
+ }
109
+ function isCrossFieldValidator(config) {
110
+ if (!config.expression) {
111
+ return false;
112
+ }
113
+ return FORM_VALUE_ACCESS_PATTERN.test(config.expression);
114
+ }
115
+ function isCrossFieldBuiltInValidator(config) {
116
+ if (config.type === 'custom') {
117
+ return false; }
118
+ if ('expression' in config && typeof config.expression === 'string') {
119
+ return FORM_VALUE_ACCESS_PATTERN.test(config.expression);
120
+ }
121
+ return false;
122
+ }
123
+ function isResourceBasedValidator(config) {
124
+ return config.type === 'async' || config.type === 'http';
125
+ }
126
+ function hasCrossFieldWhenCondition(config, context) {
127
+ if (!('when' in config) || !config.when) {
128
+ return false;
129
+ }
130
+ return isCrossFieldExpression(config.when, context);
131
+ }
132
+ function isCrossFieldStateLogic(config, context) {
133
+ return isCrossFieldExpression(config.condition, context);
134
+ }
135
+ function isCrossFieldSchema(config, context) {
136
+ if (config.type !== 'applyWhen' || !config.condition) {
137
+ return false;
138
+ }
139
+ return isCrossFieldExpression(config.condition, context);
140
+ }
141
+
142
+ const STATE_PROPERTIES = ['touched', 'dirty', 'valid', 'invalid', 'pending', 'hidden', 'readonly', 'disabled'];
143
+ function readStateSignal(fieldState, prop, reactive) {
144
+ const signalFn = fieldState[prop];
145
+ if (!signalFn || !isSignal(signalFn))
146
+ return false;
147
+ return reactive ? !!signalFn() : !!untracked(signalFn);
148
+ }
149
+ function resolveFieldState(fieldSource, reactive) {
150
+ if (!fieldSource)
151
+ return undefined;
152
+ if (typeof fieldSource === 'function') {
153
+ return reactive ? fieldSource() : untracked(fieldSource);
154
+ }
155
+ if (typeof fieldSource === 'object') {
156
+ return fieldSource;
157
+ }
158
+ return undefined;
159
+ }
160
+ function readFieldStateInfo(fieldSource, reactive) {
161
+ const state = resolveFieldState(fieldSource, reactive);
162
+ if (!state || typeof state !== 'object')
163
+ return undefined;
164
+ const knownProperties = new Set([...STATE_PROPERTIES, 'pristine']);
165
+ return new Proxy({}, {
166
+ get(_target, prop) {
167
+ if (prop === 'pristine') {
168
+ return !readStateSignal(state, 'dirty', reactive);
169
+ }
170
+ if (STATE_PROPERTIES.includes(prop)) {
171
+ return readStateSignal(state, prop, reactive);
172
+ }
173
+ return false;
174
+ },
175
+ has(_target, prop) {
176
+ return knownProperties.has(prop);
177
+ },
178
+ });
179
+ }
180
+ function createFormFieldStateMap(rootForm, reactive) {
181
+ const cache = new Map();
182
+ return new Proxy({}, {
183
+ get(_target, key) {
184
+ if (cache.has(key)) {
185
+ return cache.get(key);
186
+ }
187
+ const fieldAccessor = navigateToFieldAccessor(rootForm, key);
188
+ const snapshot = readFieldStateInfo(fieldAccessor, reactive);
189
+ cache.set(key, snapshot);
190
+ return snapshot;
191
+ },
192
+ has(_target, key) {
193
+ return navigateToFieldAccessor(rootForm, key) !== undefined;
194
+ },
195
+ });
196
+ }
197
+ function navigateToFieldAccessor(rootForm, keyPath) {
198
+ if (!keyPath)
199
+ return undefined;
200
+ const parts = keyPath.split('.');
201
+ let current = rootForm;
202
+ for (let i = 0; i < parts.length - 1; i++) {
203
+ const next = current[parts[i]];
204
+ if (!next)
205
+ return undefined;
206
+ current = next;
207
+ }
208
+ return current[parts[parts.length - 1]];
209
+ }
210
+
211
+ class AsyncConditionFunctionCacheService {
212
+ asyncConditionFunctionCache = new Map();
213
+ }
214
+
215
+ function stableStringify(value) {
216
+ if (value === null || value === undefined) {
217
+ return String(value);
218
+ }
219
+ if (typeof value !== 'object') {
220
+ return JSON.stringify(value);
221
+ }
222
+ if (Array.isArray(value)) {
223
+ return '[' + value.map(stableStringify).join(',') + ']';
224
+ }
225
+ const obj = value;
226
+ const sortedKeys = Object.keys(obj).sort();
227
+ const pairs = sortedKeys.map((key) => JSON.stringify(key) + ':' + stableStringify(obj[key]));
228
+ return '{' + pairs.join(',') + '}';
229
+ }
230
+
231
+ class DynamicFormError extends Error {
232
+ static PREFIX = '[Dynamic Forms]';
233
+ constructor(message) {
234
+ super(`${DynamicFormError.PREFIX} ${message}`);
235
+ this.name = 'DynamicFormError';
236
+ }
237
+ }
238
+
239
+ const DEFAULT_WRAPPERS = new InjectionToken('DEFAULT_WRAPPERS');
240
+ const FIELD_SIGNAL_CONTEXT = new InjectionToken('FIELD_SIGNAL_CONTEXT');
241
+ function injectFieldSignalContext() {
242
+ const ctx = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
243
+ if (!ctx) {
244
+ throw new DynamicFormError('FIELD_SIGNAL_CONTEXT is not provided. This consumer must run inside a <df-dynamic-form> component tree.');
245
+ }
246
+ return ctx;
247
+ }
248
+ const ARRAY_CONTEXT = new InjectionToken('ARRAY_CONTEXT');
249
+ const GROUP_CONTEXT = new InjectionToken('GROUP_CONTEXT');
250
+ const DEFAULT_PROPS = new InjectionToken('DEFAULT_PROPS');
251
+ const DEFAULT_VALIDATION_MESSAGES = new InjectionToken('DEFAULT_VALIDATION_MESSAGES');
252
+ const FORM_OPTIONS = new InjectionToken('FORM_OPTIONS');
253
+ const EXTERNAL_DATA = new InjectionToken('EXTERNAL_DATA');
254
+ const FORM_ID_PREFIX = new InjectionToken('FORM_ID_PREFIX');
255
+
256
+ class RootFormRegistryService {
257
+ formValue;
258
+ rootForm;
259
+ constructor(formValue, rootForm) {
260
+ this.formValue = formValue;
261
+ this.rootForm = rootForm;
262
+ }
263
+ }
264
+
265
+ const DynamicFormLogger = new InjectionToken('DynamicFormLogger');
266
+
267
+ const DEPRECATION_WARNING_TRACKER = new InjectionToken('DeprecationWarningTracker');
268
+
269
+ function compareValues(actual, expected, operator) {
270
+ switch (operator) {
271
+ case 'equals':
272
+ return actual === expected;
273
+ case 'notEquals':
274
+ return actual !== expected;
275
+ case 'greater':
276
+ return Number(actual) > Number(expected);
277
+ case 'less':
278
+ return Number(actual) < Number(expected);
279
+ case 'greaterOrEqual':
280
+ return Number(actual) >= Number(expected);
281
+ case 'lessOrEqual':
282
+ return Number(actual) <= Number(expected);
283
+ case 'contains':
284
+ return String(actual).includes(String(expected));
285
+ case 'startsWith':
286
+ return String(actual).startsWith(String(expected));
287
+ case 'endsWith':
288
+ return String(actual).endsWith(String(expected));
289
+ case 'matches':
290
+ try {
291
+ return toRegExp(String(expected)).test(String(actual));
292
+ }
293
+ catch {
294
+ return false;
295
+ }
296
+ default:
297
+ return false;
298
+ }
299
+ }
300
+ function toRegExp(value) {
301
+ const literal = /^\/(.+)\/([dgimsuvy]*)$/.exec(value);
302
+ return literal ? new RegExp(literal[1], literal[2]) : new RegExp(value);
303
+ }
304
+ function getNestedValue(obj, path) {
305
+ return path.split('.').reduce((current, key) => {
306
+ return current && typeof current === 'object' ? current[key] : undefined;
307
+ }, obj);
308
+ }
309
+ function hasNestedProperty(obj, path) {
310
+ const keys = path.split('.');
311
+ let current = obj;
312
+ for (let i = 0; i < keys.length - 1; i++) {
313
+ if (!current || typeof current !== 'object')
314
+ return false;
315
+ current = current[keys[i]];
316
+ }
317
+ if (!current || typeof current !== 'object')
318
+ return false;
319
+ return keys[keys.length - 1] in current;
320
+ }
321
+
322
+ function safeReadPathKeys(fieldContext) {
323
+ if (!('pathKeys' in fieldContext) || typeof fieldContext.pathKeys !== 'function') {
324
+ return [];
325
+ }
326
+ return untracked(() => fieldContext.pathKeys());
327
+ }
328
+
329
+ function isChildFieldContext(context) {
330
+ return 'key' in context && isSignal(context.key);
331
+ }
332
+ function extractFieldState(fieldContext) {
333
+ return untracked(() => {
334
+ if (!fieldContext || !('state' in fieldContext))
335
+ return undefined;
336
+ return fieldContext.state;
337
+ });
338
+ }
339
+ function detectArrayScope(pathKeys) {
340
+ if (pathKeys.length < 3)
341
+ return undefined;
342
+ for (let i = pathKeys.length - 2; i >= 1; i--) {
343
+ const maybeIndex = Number(pathKeys[i]);
344
+ if (Number.isInteger(maybeIndex) && maybeIndex >= 0) {
345
+ return {
346
+ arrayKey: pathKeys.slice(0, i).join('.'),
347
+ index: maybeIndex,
348
+ localKey: pathKeys[pathKeys.length - 1],
349
+ };
350
+ }
351
+ }
352
+ return undefined;
353
+ }
354
+ class FieldContextRegistryService {
355
+ rootFormRegistry = inject(RootFormRegistryService);
356
+ logger = inject(DynamicFormLogger);
357
+ deprecationTracker = inject(DEPRECATION_WARNING_TRACKER, { optional: true });
358
+ externalDataSignal = inject(EXTERNAL_DATA, { optional: true });
359
+ createEvaluationContext(fieldContext, customFunctions) {
360
+ const fieldValue = untracked(() => fieldContext.value());
361
+ const rootFormValue = untracked(() => this.rootFormRegistry.formValue());
362
+ const localKey = this.extractFieldPath(fieldContext);
363
+ const pathKeys = safeReadPathKeys(fieldContext);
364
+ const arrayScope = detectArrayScope(pathKeys);
365
+ if (arrayScope) {
366
+ return this.buildArrayScopedContext(rootFormValue, arrayScope, fieldValue, customFunctions, false, fieldContext);
367
+ }
368
+ const rootFormSignal = this.rootFormRegistry.rootForm;
369
+ return {
370
+ fieldValue,
371
+ formValue: rootFormValue,
372
+ fieldPath: localKey,
373
+ customFunctions: customFunctions || {},
374
+ externalData: this.resolveExternalData(false),
375
+ logger: this.logger,
376
+ deprecationTracker: this.deprecationTracker ?? undefined,
377
+ get fieldState() {
378
+ return readFieldStateInfo(extractFieldState(fieldContext), false);
379
+ },
380
+ get formFieldState() {
381
+ return createFormFieldStateMap(untracked(rootFormSignal), false);
382
+ },
383
+ };
384
+ }
385
+ extractFieldPath(fieldContext) {
386
+ if (isChildFieldContext(fieldContext)) {
387
+ try {
388
+ return String(fieldContext.key());
389
+ }
390
+ catch (error) {
391
+ this.logger.warn('Unable to extract field key:', error);
392
+ }
393
+ }
394
+ return '';
395
+ }
396
+ buildArrayScopedContext(rootFormValue, arrayScope, fieldValue, customFunctions, reactive, fieldContext) {
397
+ const { arrayKey, index, localKey } = arrayScope;
398
+ const arrayData = getNestedValue(rootFormValue, arrayKey);
399
+ let scopedFormValue;
400
+ if (Array.isArray(arrayData) && index >= 0 && index < arrayData.length) {
401
+ const item = arrayData[index];
402
+ if (item != null && typeof item === 'object') {
403
+ scopedFormValue = item;
404
+ }
405
+ }
406
+ const rootFormSignal = this.rootFormRegistry.rootForm;
407
+ const fieldStateGetter = fieldContext ? () => readFieldStateInfo(extractFieldState(fieldContext), reactive) : () => undefined;
408
+ const formFieldStateGetter = () => createFormFieldStateMap((reactive ? rootFormSignal() : untracked(rootFormSignal)), reactive);
409
+ if (!scopedFormValue) {
410
+ return {
411
+ fieldValue,
412
+ formValue: rootFormValue,
413
+ fieldPath: localKey,
414
+ customFunctions: customFunctions || {},
415
+ externalData: this.resolveExternalData(reactive),
416
+ logger: this.logger,
417
+ deprecationTracker: this.deprecationTracker ?? undefined,
418
+ get fieldState() {
419
+ return fieldStateGetter();
420
+ },
421
+ get formFieldState() {
422
+ return formFieldStateGetter();
423
+ },
424
+ };
425
+ }
426
+ return {
427
+ fieldValue,
428
+ formValue: scopedFormValue,
429
+ rootFormValue,
430
+ arrayIndex: index,
431
+ arrayPath: arrayKey,
432
+ fieldPath: `${arrayKey}.${index}.${localKey}`,
433
+ customFunctions: customFunctions || {},
434
+ externalData: this.resolveExternalData(reactive),
435
+ logger: this.logger,
436
+ deprecationTracker: this.deprecationTracker ?? undefined,
437
+ get fieldState() {
438
+ return fieldStateGetter();
439
+ },
440
+ get formFieldState() {
441
+ return formFieldStateGetter();
442
+ },
443
+ };
444
+ }
445
+ resolveExternalData(reactive) {
446
+ const externalDataSignal = this.externalDataSignal;
447
+ if (!externalDataSignal)
448
+ return undefined;
449
+ const externalDataRecord = reactive ? externalDataSignal() : untracked(() => externalDataSignal());
450
+ if (!externalDataRecord) {
451
+ return undefined;
452
+ }
453
+ const resolved = {};
454
+ for (const [key, value] of Object.entries(externalDataRecord)) {
455
+ if (!isSignal(value)) {
456
+ throw new DynamicFormError(`externalData["${key}"] must be a Signal. Got: ${typeof value}. Wrap it with signal(yourValue).`);
457
+ }
458
+ resolved[key] = reactive ? value() : untracked(() => value());
459
+ }
460
+ return resolved;
461
+ }
462
+ createReactiveEvaluationContext(fieldContext, customFunctions) {
463
+ const fieldValue = fieldContext.value();
464
+ const rootFormValue = this.rootFormRegistry.formValue();
465
+ const pathKeys = safeReadPathKeys(fieldContext);
466
+ const arrayScope = detectArrayScope(pathKeys);
467
+ if (arrayScope) {
468
+ return this.buildArrayScopedContext(rootFormValue, arrayScope, fieldValue, customFunctions, true, fieldContext);
469
+ }
470
+ const localKey = this.extractFieldPath(fieldContext);
471
+ const rootFormSignal = this.rootFormRegistry.rootForm;
472
+ return {
473
+ fieldValue,
474
+ formValue: rootFormValue,
475
+ fieldPath: localKey,
476
+ customFunctions: customFunctions || {},
477
+ externalData: this.resolveExternalData(true),
478
+ logger: this.logger,
479
+ deprecationTracker: this.deprecationTracker ?? undefined,
480
+ get fieldState() {
481
+ return readFieldStateInfo(extractFieldState(fieldContext), true);
482
+ },
483
+ get formFieldState() {
484
+ return createFormFieldStateMap(rootFormSignal(), true);
485
+ },
486
+ };
487
+ }
488
+ createDisplayOnlyContext(fieldPath, customFunctions) {
489
+ const formValue = this.rootFormRegistry.formValue();
490
+ const rootFormSignal = this.rootFormRegistry.rootForm;
491
+ return {
492
+ fieldValue: undefined,
493
+ formValue,
494
+ fieldPath,
495
+ customFunctions: customFunctions || {},
496
+ externalData: this.resolveExternalData(true),
497
+ logger: this.logger,
498
+ deprecationTracker: this.deprecationTracker ?? undefined,
499
+ get formFieldState() {
500
+ return createFormFieldStateMap(rootFormSignal(), true);
501
+ },
502
+ };
503
+ }
504
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldContextRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
505
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldContextRegistryService });
506
+ }
507
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldContextRegistryService, decorators: [{
508
+ type: Injectable
509
+ }] });
510
+
511
+ class FunctionRegistryService {
512
+ customFunctions = new Map();
513
+ customFunctionScopes = new Map();
514
+ derivationFunctions = new Map();
515
+ asyncDerivationFunctions = new Map();
516
+ asyncConditionFunctions = new Map();
517
+ validators = new Map();
518
+ asyncValidators = new Map();
519
+ httpValidators = new Map();
520
+ registerCustomFunction(name, fn, options) {
521
+ this.customFunctions.set(name, fn);
522
+ this.customFunctionScopes.set(name, options?.scope ?? 'form');
523
+ }
524
+ getCustomFunctionScope(name) {
525
+ return this.customFunctionScopes.get(name);
526
+ }
527
+ isFieldScopedFunction(name) {
528
+ return this.customFunctionScopes.get(name) === 'field';
529
+ }
530
+ getCustomFunctions() {
531
+ return Object.fromEntries(this.customFunctions);
532
+ }
533
+ clearCustomFunctions() {
534
+ this.customFunctions.clear();
535
+ this.customFunctionScopes.clear();
536
+ }
537
+ registerDerivationFunction(name, fn) {
538
+ this.derivationFunctions.set(name, fn);
539
+ }
540
+ getDerivationFunction(name) {
541
+ return this.derivationFunctions.get(name);
542
+ }
543
+ getDerivationFunctions() {
544
+ return Object.fromEntries(this.derivationFunctions);
545
+ }
546
+ setDerivationFunctions(derivations) {
547
+ this.setRegistryIfChanged(this.derivationFunctions, derivations);
548
+ }
549
+ clearDerivationFunctions() {
550
+ this.derivationFunctions.clear();
551
+ }
552
+ registerAsyncDerivationFunction(name, fn) {
553
+ this.asyncDerivationFunctions.set(name, fn);
554
+ }
555
+ getAsyncDerivationFunction(name) {
556
+ return this.asyncDerivationFunctions.get(name);
557
+ }
558
+ getAsyncDerivationFunctions() {
559
+ return Object.fromEntries(this.asyncDerivationFunctions);
560
+ }
561
+ setAsyncDerivationFunctions(fns) {
562
+ this.setRegistryIfChanged(this.asyncDerivationFunctions, fns);
563
+ }
564
+ clearAsyncDerivationFunctions() {
565
+ this.asyncDerivationFunctions.clear();
566
+ }
567
+ registerAsyncConditionFunction(name, fn) {
568
+ this.asyncConditionFunctions.set(name, fn);
569
+ }
570
+ getAsyncConditionFunction(name) {
571
+ return this.asyncConditionFunctions.get(name);
572
+ }
573
+ getAsyncConditionFunctions() {
574
+ return Object.fromEntries(this.asyncConditionFunctions);
575
+ }
576
+ setAsyncConditionFunctions(fns) {
577
+ this.setRegistryIfChanged(this.asyncConditionFunctions, fns);
578
+ }
579
+ clearAsyncConditionFunctions() {
580
+ this.asyncConditionFunctions.clear();
581
+ }
582
+ registerValidator(name, fn) {
583
+ this.validators.set(name, fn);
584
+ }
585
+ getValidator(name) {
586
+ return this.validators.get(name);
587
+ }
588
+ clearValidators() {
589
+ this.validators.clear();
590
+ }
591
+ registerAsyncValidator(name, fn) {
592
+ this.asyncValidators.set(name, fn);
593
+ }
594
+ getAsyncValidator(name) {
595
+ return this.asyncValidators.get(name);
596
+ }
597
+ clearAsyncValidators() {
598
+ this.asyncValidators.clear();
599
+ }
600
+ registerHttpValidator(name, config) {
601
+ this.httpValidators.set(name, config);
602
+ }
603
+ getHttpValidator(name) {
604
+ return this.httpValidators.get(name);
605
+ }
606
+ clearHttpValidators() {
607
+ this.httpValidators.clear();
608
+ }
609
+ setRegistryIfChanged(registry, values) {
610
+ if (!values)
611
+ return;
612
+ const entries = Object.entries(values);
613
+ const hasChanges = entries.some(([name, value]) => registry.get(name) !== value);
614
+ if (hasChanges) {
615
+ entries.forEach(([name, value]) => registry.set(name, value));
616
+ }
617
+ }
618
+ setValidators(validators) {
619
+ this.setRegistryIfChanged(this.validators, validators);
620
+ }
621
+ setAsyncValidators(asyncValidators) {
622
+ this.setRegistryIfChanged(this.asyncValidators, asyncValidators);
623
+ }
624
+ setHttpValidators(httpValidators) {
625
+ this.setRegistryIfChanged(this.httpValidators, httpValidators);
626
+ }
627
+ clearAll() {
628
+ this.clearCustomFunctions();
629
+ this.clearDerivationFunctions();
630
+ this.clearAsyncDerivationFunctions();
631
+ this.clearAsyncConditionFunctions();
632
+ this.clearValidators();
633
+ this.clearAsyncValidators();
634
+ this.clearHttpValidators();
635
+ }
636
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FunctionRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
637
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FunctionRegistryService });
638
+ }
639
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FunctionRegistryService, decorators: [{
640
+ type: Injectable
641
+ }] });
642
+
643
+ function isSignalArray(value) {
644
+ return Array.isArray(value) && value.every((item) => isSignal(item));
645
+ }
646
+ function isSignalRecord(value) {
647
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.values(value).every((item) => isSignal(item));
648
+ }
649
+ function derivedFromDeferred(source, operator, options) {
650
+ const deferred$ = defer(() => {
651
+ if (isSignal(source)) {
652
+ return toObservable(source, { injector: options.injector });
653
+ }
654
+ if (isSignalArray(source)) {
655
+ const observables = source.map((s) => toObservable(s, { injector: options.injector }));
656
+ return combineLatest(observables);
657
+ }
658
+ if (isSignalRecord(source)) {
659
+ const keys = Object.keys(source);
660
+ const observables = keys.map((key) => toObservable(source[key], { injector: options.injector }));
661
+ return combineLatest(observables, (...values) => {
662
+ const result = {};
663
+ keys.forEach((key, index) => {
664
+ result[key] = values[index];
665
+ });
666
+ return result;
667
+ });
668
+ }
669
+ throw new Error('Invalid source type. Expected a Signal, array of Signals, or object of Signals.');
670
+ }).pipe(operator);
671
+ return toSignal(deferred$, { initialValue: options.initialValue, injector: options.injector });
672
+ }
673
+
674
+ function withPreviousValue(input) {
675
+ const derived = linkedSignal({ ...(ngDevMode ? { debugName: "derived" } : /* istanbul ignore next */ {}), source: input.snapshot,
676
+ computation: (snapshot, previous) => {
677
+ if ((snapshot.status === 'loading' || snapshot.status === 'reloading') &&
678
+ previous !== undefined &&
679
+ previous.value.status !== 'error') {
680
+ return { status: snapshot.status, value: previous.value.value };
681
+ }
682
+ return snapshot;
683
+ } });
684
+ return resourceFromSnapshots(derived);
685
+ }
686
+
687
+ function createDebouncedResourceLogicFn(config) {
688
+ const perFunctionSignalStore = new Map();
689
+ return (ctx) => {
690
+ const contextKey = safeReadPathKeys(ctx).join('.');
691
+ let handle = perFunctionSignalStore.get(contextKey);
692
+ if (!handle) {
693
+ const trigger = signal(undefined, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
694
+ const resultResource = untracked(() => {
695
+ const debouncedTrigger = derivedFromDeferred(trigger, pipe(debounceTime(config.debounceMs),
696
+ distinctUntilChanged((a, b) => a?.key === b?.key)), { initialValue: undefined, injector: config.injector });
697
+ const resource = rxResource({
698
+ params: () => debouncedTrigger(),
699
+ stream: ({ params }) => config.buildStream(params.payload),
700
+ defaultValue: config.pendingValue,
701
+ injector: config.injector,
702
+ });
703
+ return withPreviousValue(resource);
704
+ });
705
+ handle = { trigger, resultResource, refetchVersion: 0 };
706
+ perFunctionSignalStore.set(contextKey, handle);
707
+ }
708
+ const resolved = config.resolve(ctx);
709
+ if (resolved.kind === 'returnEarly') {
710
+ handle.refetchVersion++;
711
+ return resolved.value;
712
+ }
713
+ if (resolved.kind === 'holdPrevious') {
714
+ return handle.resultResource.value();
715
+ }
716
+ const dedupKey = `${resolved.key}::${handle.refetchVersion}`;
717
+ const newTrigger = { key: dedupKey, payload: resolved.payload };
718
+ untracked(() => {
719
+ const current = handle.trigger();
720
+ if (current?.key !== newTrigger.key) {
721
+ handle.trigger.set(newTrigger);
722
+ }
723
+ });
724
+ return handle.resultResource.value();
725
+ };
726
+ }
727
+
728
+ function createAsyncConditionLogicFunction(condition) {
729
+ const fieldContextRegistry = inject(FieldContextRegistryService);
730
+ const functionRegistry = inject(FunctionRegistryService);
731
+ const injector = inject(Injector);
732
+ const logger = inject(DynamicFormLogger);
733
+ const cacheService = inject(AsyncConditionFunctionCacheService);
734
+ const pendingValue = condition.pendingValue ?? false;
735
+ const debounceMs = condition.debounceMs ?? 300;
736
+ if (condition.asyncFn && condition.asyncFunctionName) {
737
+ logger.warn('Both "asyncFn" and "asyncFunctionName" are set on async condition. Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.');
738
+ }
739
+ const cacheKey = condition.asyncFn ? undefined : stableStringify(condition);
740
+ if (cacheKey !== undefined) {
741
+ const cached = cacheService.asyncConditionFunctionCache.get(cacheKey);
742
+ if (cached) {
743
+ return cached;
744
+ }
745
+ }
746
+ const fn = createDebouncedResourceLogicFn({
747
+ pendingValue,
748
+ debounceMs,
749
+ injector,
750
+ resolve: (ctx) => {
751
+ const evaluationContext = fieldContextRegistry.createReactiveEvaluationContext(ctx, functionRegistry.getCustomFunctions());
752
+ const key = stableStringify({
753
+ formValue: evaluationContext.formValue,
754
+ fieldValue: evaluationContext.fieldValue,
755
+ externalData: evaluationContext.externalData,
756
+ });
757
+ return { kind: 'trigger', key, payload: ctx };
758
+ },
759
+ buildStream: (capturedCtx) => {
760
+ const asyncFn = condition.asyncFn ??
761
+ (condition.asyncFunctionName ? functionRegistry.getAsyncConditionFunction(condition.asyncFunctionName) : undefined);
762
+ if (!asyncFn) {
763
+ logger.warn(`Async Condition - function '${condition.asyncFunctionName}' not found. ` + `Register it in customFnConfig.asyncConditions.`);
764
+ return of(pendingValue);
765
+ }
766
+ const evaluationContext = fieldContextRegistry.createReactiveEvaluationContext(capturedCtx, functionRegistry.getCustomFunctions());
767
+ return from(asyncFn(evaluationContext)).pipe(map((result) => !!result), catchError((error) => {
768
+ logger.warn('Async Condition - function failed:', error);
769
+ return of(pendingValue);
770
+ }));
771
+ },
772
+ });
773
+ if (cacheKey !== undefined) {
774
+ cacheService.asyncConditionFunctionCache.set(cacheKey, fn);
775
+ }
776
+ return fn;
777
+ }
778
+
779
+ var TokenType;
780
+ (function (TokenType) {
781
+ TokenType["NUMBER"] = "NUMBER";
782
+ TokenType["STRING"] = "STRING";
783
+ TokenType["TRUE"] = "TRUE";
784
+ TokenType["FALSE"] = "FALSE";
785
+ TokenType["NULL"] = "NULL";
786
+ TokenType["UNDEFINED"] = "UNDEFINED";
787
+ TokenType["IDENTIFIER"] = "IDENTIFIER";
788
+ TokenType["PLUS"] = "PLUS";
789
+ TokenType["MINUS"] = "MINUS";
790
+ TokenType["MULTIPLY"] = "MULTIPLY";
791
+ TokenType["DIVIDE"] = "DIVIDE";
792
+ TokenType["MODULO"] = "MODULO";
793
+ TokenType["EQUAL"] = "EQUAL";
794
+ TokenType["NOT_EQUAL"] = "NOT_EQUAL";
795
+ TokenType["GREATER"] = "GREATER";
796
+ TokenType["LESS"] = "LESS";
797
+ TokenType["GREATER_EQUAL"] = "GREATER_EQUAL";
798
+ TokenType["LESS_EQUAL"] = "LESS_EQUAL";
799
+ TokenType["AND"] = "AND";
800
+ TokenType["OR"] = "OR";
801
+ TokenType["NOT"] = "NOT";
802
+ TokenType["DOT"] = "DOT";
803
+ TokenType["COMMA"] = "COMMA";
804
+ TokenType["COLON"] = "COLON";
805
+ TokenType["LPAREN"] = "LPAREN";
806
+ TokenType["RPAREN"] = "RPAREN";
807
+ TokenType["LBRACKET"] = "LBRACKET";
808
+ TokenType["RBRACKET"] = "RBRACKET";
809
+ TokenType["LBRACE"] = "LBRACE";
810
+ TokenType["RBRACE"] = "RBRACE";
811
+ TokenType["ARROW"] = "ARROW";
812
+ TokenType["QUESTION"] = "QUESTION";
813
+ TokenType["OPTIONAL_DOT"] = "OPTIONAL_DOT";
814
+ TokenType["EOF"] = "EOF";
815
+ })(TokenType || (TokenType = {}));
816
+ class ExpressionParserError extends DynamicFormError {
817
+ position;
818
+ expression;
819
+ constructor(message, position, expression) {
820
+ super(`${message} at position ${position} in expression: ${expression}`);
821
+ this.position = position;
822
+ this.expression = expression;
823
+ this.name = 'ExpressionParserError';
824
+ }
825
+ }
826
+
827
+ class Tokenizer {
828
+ position = 0;
829
+ expression;
830
+ constructor(expression) {
831
+ this.expression = expression;
832
+ }
833
+ tokenize() {
834
+ const tokens = [];
835
+ while (this.position < this.expression.length) {
836
+ this.skipWhitespace();
837
+ if (this.position >= this.expression.length) {
838
+ break;
839
+ }
840
+ const token = this.nextToken();
841
+ tokens.push(token);
842
+ }
843
+ tokens.push({ type: TokenType.EOF, value: '', position: this.position });
844
+ return tokens;
845
+ }
846
+ skipWhitespace() {
847
+ while (this.position < this.expression.length && /\s/.test(this.expression[this.position])) {
848
+ this.position++;
849
+ }
850
+ }
851
+ nextToken() {
852
+ const char = this.expression[this.position];
853
+ if (char === '"' || char === "'") {
854
+ return this.readString(char);
855
+ }
856
+ if (/[0-9]/.test(char)) {
857
+ return this.readNumber();
858
+ }
859
+ if (/[a-zA-Z_$]/.test(char)) {
860
+ return this.readIdentifierOrKeyword();
861
+ }
862
+ return this.readOperatorOrPunctuation();
863
+ }
864
+ readString(quote) {
865
+ const start = this.position;
866
+ this.position++; let value = '';
867
+ while (this.position < this.expression.length) {
868
+ const char = this.expression[this.position];
869
+ if (char === quote) {
870
+ this.position++; return { type: TokenType.STRING, value, position: start };
871
+ }
872
+ if (char === '\\' && this.position + 1 < this.expression.length) {
873
+ this.position++;
874
+ const nextChar = this.expression[this.position];
875
+ switch (nextChar) {
876
+ case 'n':
877
+ value += '\n';
878
+ break;
879
+ case 't':
880
+ value += '\t';
881
+ break;
882
+ case 'r':
883
+ value += '\r';
884
+ break;
885
+ case '\\':
886
+ value += '\\';
887
+ break;
888
+ case quote:
889
+ value += quote;
890
+ break;
891
+ default:
892
+ value += nextChar;
893
+ }
894
+ this.position++;
895
+ }
896
+ else {
897
+ value += char;
898
+ this.position++;
899
+ }
900
+ }
901
+ throw new ExpressionParserError('Unterminated string literal', start, this.expression);
902
+ }
903
+ readNumber() {
904
+ const start = this.position;
905
+ let value = '';
906
+ while (this.position < this.expression.length && /[0-9.]/.test(this.expression[this.position])) {
907
+ value += this.expression[this.position];
908
+ this.position++;
909
+ }
910
+ if (!/^\d+(\.\d+)?$/.test(value)) {
911
+ throw new ExpressionParserError(`Invalid number: ${value}`, start, this.expression);
912
+ }
913
+ return { type: TokenType.NUMBER, value, position: start };
914
+ }
915
+ readIdentifierOrKeyword() {
916
+ const start = this.position;
917
+ let value = '';
918
+ while (this.position < this.expression.length && /[a-zA-Z0-9_$]/.test(this.expression[this.position])) {
919
+ value += this.expression[this.position];
920
+ this.position++;
921
+ }
922
+ const type = this.getKeywordType(value);
923
+ return { type, value, position: start };
924
+ }
925
+ getKeywordType(value) {
926
+ switch (value) {
927
+ case 'true':
928
+ return TokenType.TRUE;
929
+ case 'false':
930
+ return TokenType.FALSE;
931
+ case 'null':
932
+ return TokenType.NULL;
933
+ case 'undefined':
934
+ return TokenType.UNDEFINED;
935
+ default:
936
+ return TokenType.IDENTIFIER;
937
+ }
938
+ }
939
+ readOperatorOrPunctuation() {
940
+ const start = this.position;
941
+ const char = this.expression[this.position];
942
+ const nextChar = this.position + 1 < this.expression.length ? this.expression[this.position + 1] : '';
943
+ const thirdChar = this.position + 2 < this.expression.length ? this.expression[this.position + 2] : '';
944
+ const threeChar = char + nextChar + thirdChar;
945
+ if (threeChar === '===') {
946
+ this.position += 3;
947
+ return { type: TokenType.EQUAL, value: '===', position: start };
948
+ }
949
+ if (threeChar === '!==') {
950
+ this.position += 3;
951
+ return { type: TokenType.NOT_EQUAL, value: '!==', position: start };
952
+ }
953
+ const twoChar = char + nextChar;
954
+ switch (twoChar) {
955
+ case '==':
956
+ this.position += 2;
957
+ return { type: TokenType.EQUAL, value: '==', position: start };
958
+ case '!=':
959
+ this.position += 2;
960
+ return { type: TokenType.NOT_EQUAL, value: '!=', position: start };
961
+ case '>=':
962
+ this.position += 2;
963
+ return { type: TokenType.GREATER_EQUAL, value: '>=', position: start };
964
+ case '<=':
965
+ this.position += 2;
966
+ return { type: TokenType.LESS_EQUAL, value: '<=', position: start };
967
+ case '&&':
968
+ this.position += 2;
969
+ return { type: TokenType.AND, value: '&&', position: start };
970
+ case '||':
971
+ this.position += 2;
972
+ return { type: TokenType.OR, value: '||', position: start };
973
+ case '=>':
974
+ this.position += 2;
975
+ return { type: TokenType.ARROW, value: '=>', position: start };
976
+ case '?.':
977
+ this.position += 2;
978
+ return { type: TokenType.OPTIONAL_DOT, value: '?.', position: start };
979
+ }
980
+ this.position++;
981
+ switch (char) {
982
+ case '+':
983
+ return { type: TokenType.PLUS, value: '+', position: start };
984
+ case '-':
985
+ return { type: TokenType.MINUS, value: '-', position: start };
986
+ case '*':
987
+ return { type: TokenType.MULTIPLY, value: '*', position: start };
988
+ case '/':
989
+ return { type: TokenType.DIVIDE, value: '/', position: start };
990
+ case '%':
991
+ return { type: TokenType.MODULO, value: '%', position: start };
992
+ case '>':
993
+ return { type: TokenType.GREATER, value: '>', position: start };
994
+ case '<':
995
+ return { type: TokenType.LESS, value: '<', position: start };
996
+ case '!':
997
+ return { type: TokenType.NOT, value: '!', position: start };
998
+ case '.':
999
+ return { type: TokenType.DOT, value: '.', position: start };
1000
+ case ',':
1001
+ return { type: TokenType.COMMA, value: ',', position: start };
1002
+ case ':':
1003
+ return { type: TokenType.COLON, value: ':', position: start };
1004
+ case '(':
1005
+ return { type: TokenType.LPAREN, value: '(', position: start };
1006
+ case ')':
1007
+ return { type: TokenType.RPAREN, value: ')', position: start };
1008
+ case '[':
1009
+ return { type: TokenType.LBRACKET, value: '[', position: start };
1010
+ case ']':
1011
+ return { type: TokenType.RBRACKET, value: ']', position: start };
1012
+ case '{':
1013
+ return { type: TokenType.LBRACE, value: '{', position: start };
1014
+ case '}':
1015
+ return { type: TokenType.RBRACE, value: '}', position: start };
1016
+ case '?':
1017
+ return { type: TokenType.QUESTION, value: '?', position: start };
1018
+ default:
1019
+ throw new ExpressionParserError(`Unexpected character: ${char}`, start, this.expression);
1020
+ }
1021
+ }
1022
+ }
1023
+
1024
+ class Parser {
1025
+ tokens = [];
1026
+ current = 0;
1027
+ expression;
1028
+ constructor(expression) {
1029
+ this.expression = expression;
1030
+ }
1031
+ parse() {
1032
+ const tokenizer = new Tokenizer(this.expression);
1033
+ this.tokens = tokenizer.tokenize();
1034
+ this.current = 0;
1035
+ if (this.isAtEnd()) {
1036
+ throw new ExpressionParserError('Empty expression', 0, this.expression);
1037
+ }
1038
+ const result = this.parseExpression();
1039
+ if (!this.isAtEnd()) {
1040
+ const token = this.peek();
1041
+ throw new ExpressionParserError(`Unexpected token '${token.value}' at position ${token.position} — expression has trailing content`, token.position, this.expression);
1042
+ }
1043
+ return result;
1044
+ }
1045
+ parseExpression() {
1046
+ const arrow = this.tryParseArrowFunction();
1047
+ if (arrow)
1048
+ return arrow;
1049
+ return this.parseConditional();
1050
+ }
1051
+ parseConditional() {
1052
+ const test = this.parseLogicalOr();
1053
+ if (!this.match(TokenType.QUESTION))
1054
+ return test;
1055
+ const consequent = this.parseExpression();
1056
+ this.consume(TokenType.COLON, 'Expected ":" between ternary branches');
1057
+ const alternate = this.parseExpression();
1058
+ return { type: 'Conditional', test, consequent, alternate };
1059
+ }
1060
+ tryParseArrowFunction() {
1061
+ const savedPos = this.current;
1062
+ if (this.check(TokenType.IDENTIFIER) && this.checkAt(this.current + 1, TokenType.ARROW)) {
1063
+ const param = this.advance().value;
1064
+ this.advance(); const body = this.parseExpression();
1065
+ return { type: 'ArrowFunction', params: [param], body };
1066
+ }
1067
+ if (this.check(TokenType.LPAREN)) {
1068
+ this.advance(); const params = [];
1069
+ if (this.check(TokenType.RPAREN)) {
1070
+ this.advance(); }
1071
+ else if (this.check(TokenType.IDENTIFIER)) {
1072
+ params.push(this.advance().value);
1073
+ while (this.check(TokenType.COMMA)) {
1074
+ this.advance(); if (!this.check(TokenType.IDENTIFIER)) {
1075
+ this.current = savedPos;
1076
+ return null;
1077
+ }
1078
+ params.push(this.advance().value);
1079
+ }
1080
+ if (!this.check(TokenType.RPAREN)) {
1081
+ this.current = savedPos;
1082
+ return null;
1083
+ }
1084
+ this.advance(); }
1085
+ else {
1086
+ this.current = savedPos;
1087
+ return null;
1088
+ }
1089
+ if (!this.check(TokenType.ARROW)) {
1090
+ this.current = savedPos;
1091
+ return null;
1092
+ }
1093
+ this.advance(); const body = this.parseExpression();
1094
+ return { type: 'ArrowFunction', params, body };
1095
+ }
1096
+ return null;
1097
+ }
1098
+ checkAt(index, type) {
1099
+ if (index < 0 || index >= this.tokens.length)
1100
+ return false;
1101
+ return this.tokens[index].type === type;
1102
+ }
1103
+ parseLogicalOr() {
1104
+ let node = this.parseLogicalAnd();
1105
+ while (this.match(TokenType.OR)) {
1106
+ const operator = this.previous().value;
1107
+ const right = this.parseLogicalAnd();
1108
+ node = { type: 'BinaryOp', operator, left: node, right };
1109
+ }
1110
+ return node;
1111
+ }
1112
+ parseLogicalAnd() {
1113
+ let node = this.parseEquality();
1114
+ while (this.match(TokenType.AND)) {
1115
+ const operator = this.previous().value;
1116
+ const right = this.parseEquality();
1117
+ node = { type: 'BinaryOp', operator, left: node, right };
1118
+ }
1119
+ return node;
1120
+ }
1121
+ parseEquality() {
1122
+ let node = this.parseComparison();
1123
+ while (this.match(TokenType.EQUAL, TokenType.NOT_EQUAL)) {
1124
+ const operator = this.previous().value;
1125
+ const right = this.parseComparison();
1126
+ node = { type: 'BinaryOp', operator, left: node, right };
1127
+ }
1128
+ return node;
1129
+ }
1130
+ parseComparison() {
1131
+ let node = this.parseAddition();
1132
+ while (this.match(TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.LESS_EQUAL)) {
1133
+ const operator = this.previous().value;
1134
+ const right = this.parseAddition();
1135
+ node = { type: 'BinaryOp', operator, left: node, right };
1136
+ }
1137
+ return node;
1138
+ }
1139
+ parseAddition() {
1140
+ let node = this.parseMultiplication();
1141
+ while (this.match(TokenType.PLUS, TokenType.MINUS)) {
1142
+ const operator = this.previous().value;
1143
+ const right = this.parseMultiplication();
1144
+ node = { type: 'BinaryOp', operator, left: node, right };
1145
+ }
1146
+ return node;
1147
+ }
1148
+ parseMultiplication() {
1149
+ let node = this.parseUnary();
1150
+ while (this.match(TokenType.MULTIPLY, TokenType.DIVIDE, TokenType.MODULO)) {
1151
+ const operator = this.previous().value;
1152
+ const right = this.parseUnary();
1153
+ node = { type: 'BinaryOp', operator, left: node, right };
1154
+ }
1155
+ return node;
1156
+ }
1157
+ parseUnary() {
1158
+ if (this.match(TokenType.NOT, TokenType.MINUS, TokenType.PLUS)) {
1159
+ const operator = this.previous().value;
1160
+ const operand = this.parseUnary();
1161
+ return { type: 'UnaryOp', operator, operand };
1162
+ }
1163
+ return this.parsePostfix();
1164
+ }
1165
+ parsePostfix() {
1166
+ let node = this.parsePrimary();
1167
+ while (true) {
1168
+ if (this.match(TokenType.DOT)) {
1169
+ if (!this.check(TokenType.IDENTIFIER)) {
1170
+ throw new ExpressionParserError('Expected property name after "."', this.peek().position, this.expression);
1171
+ }
1172
+ const property = this.advance().value;
1173
+ node = { type: 'MemberAccess', object: node, property };
1174
+ }
1175
+ else if (this.match(TokenType.OPTIONAL_DOT)) {
1176
+ if (this.match(TokenType.LBRACKET)) {
1177
+ const property = this.parseExpression();
1178
+ this.consume(TokenType.RBRACKET, 'Expected "]" after computed property');
1179
+ node = { type: 'ComputedMemberAccess', object: node, property, optional: true };
1180
+ }
1181
+ else if (this.match(TokenType.LPAREN)) {
1182
+ const args = this.parseArgumentList();
1183
+ this.consume(TokenType.RPAREN, 'Expected ")" after arguments');
1184
+ node = { type: 'CallExpression', callee: node, arguments: args, optional: true };
1185
+ }
1186
+ else if (this.check(TokenType.IDENTIFIER)) {
1187
+ const property = this.advance().value;
1188
+ node = { type: 'MemberAccess', object: node, property, optional: true };
1189
+ }
1190
+ else {
1191
+ throw new ExpressionParserError('Expected property name, "[", or "(" after "?."', this.peek().position, this.expression);
1192
+ }
1193
+ }
1194
+ else if (this.match(TokenType.LBRACKET)) {
1195
+ const property = this.parseExpression();
1196
+ this.consume(TokenType.RBRACKET, 'Expected "]" after computed property');
1197
+ node = { type: 'ComputedMemberAccess', object: node, property };
1198
+ }
1199
+ else if (this.match(TokenType.LPAREN)) {
1200
+ const args = this.parseArgumentList();
1201
+ this.consume(TokenType.RPAREN, 'Expected ")" after arguments');
1202
+ node = { type: 'CallExpression', callee: node, arguments: args };
1203
+ }
1204
+ else {
1205
+ break;
1206
+ }
1207
+ }
1208
+ return node;
1209
+ }
1210
+ parsePrimary() {
1211
+ if (this.match(TokenType.TRUE)) {
1212
+ return { type: 'Literal', value: true };
1213
+ }
1214
+ if (this.match(TokenType.FALSE)) {
1215
+ return { type: 'Literal', value: false };
1216
+ }
1217
+ if (this.match(TokenType.NULL)) {
1218
+ return { type: 'Literal', value: null };
1219
+ }
1220
+ if (this.match(TokenType.UNDEFINED)) {
1221
+ return { type: 'Literal', value: undefined };
1222
+ }
1223
+ if (this.match(TokenType.NUMBER)) {
1224
+ return { type: 'Literal', value: parseFloat(this.previous().value) };
1225
+ }
1226
+ if (this.match(TokenType.STRING)) {
1227
+ return { type: 'Literal', value: this.previous().value };
1228
+ }
1229
+ if (this.match(TokenType.LBRACKET)) {
1230
+ const elements = this.parseArrayElements();
1231
+ this.consume(TokenType.RBRACKET, 'Expected "]" after array elements');
1232
+ return { type: 'ArrayLiteral', elements };
1233
+ }
1234
+ if (this.match(TokenType.LBRACE)) {
1235
+ const properties = this.parseObjectProperties();
1236
+ this.consume(TokenType.RBRACE, 'Expected "}" after object properties');
1237
+ return { type: 'ObjectLiteral', properties };
1238
+ }
1239
+ if (this.match(TokenType.IDENTIFIER)) {
1240
+ return { type: 'Identifier', name: this.previous().value };
1241
+ }
1242
+ if (this.match(TokenType.LPAREN)) {
1243
+ const expr = this.parseExpression();
1244
+ this.consume(TokenType.RPAREN, 'Expected ")" after expression');
1245
+ return expr;
1246
+ }
1247
+ throw new ExpressionParserError(`Unexpected token: ${this.peek().value}`, this.peek().position, this.expression);
1248
+ }
1249
+ parseArgumentList() {
1250
+ const args = [];
1251
+ if (!this.check(TokenType.RPAREN)) {
1252
+ do {
1253
+ args.push(this.parseExpression());
1254
+ } while (this.match(TokenType.COMMA));
1255
+ }
1256
+ return args;
1257
+ }
1258
+ parseArrayElements() {
1259
+ const elements = [];
1260
+ if (!this.check(TokenType.RBRACKET)) {
1261
+ do {
1262
+ elements.push(this.parseExpression());
1263
+ } while (this.match(TokenType.COMMA));
1264
+ }
1265
+ return elements;
1266
+ }
1267
+ parseObjectProperties() {
1268
+ const properties = [];
1269
+ if (this.check(TokenType.RBRACE)) {
1270
+ return properties;
1271
+ }
1272
+ do {
1273
+ if (this.check(TokenType.RBRACE))
1274
+ break;
1275
+ let key;
1276
+ if (this.check(TokenType.IDENTIFIER) || this.check(TokenType.STRING)) {
1277
+ key = this.advance().value;
1278
+ }
1279
+ else {
1280
+ throw new ExpressionParserError(`Expected property name (identifier or string) in object literal, got '${this.peek().value}'`, this.peek().position, this.expression);
1281
+ }
1282
+ this.consume(TokenType.COLON, 'Expected ":" after property name in object literal');
1283
+ const value = this.parseExpression();
1284
+ properties.push({ key, value });
1285
+ } while (this.match(TokenType.COMMA));
1286
+ return properties;
1287
+ }
1288
+ match(...types) {
1289
+ for (const type of types) {
1290
+ if (this.check(type)) {
1291
+ this.advance();
1292
+ return true;
1293
+ }
1294
+ }
1295
+ return false;
1296
+ }
1297
+ check(type) {
1298
+ if (this.isAtEnd())
1299
+ return false;
1300
+ return this.peek().type === type;
1301
+ }
1302
+ advance() {
1303
+ if (!this.isAtEnd())
1304
+ this.current++;
1305
+ return this.previous();
1306
+ }
1307
+ isAtEnd() {
1308
+ return this.peek().type === TokenType.EOF;
1309
+ }
1310
+ peek() {
1311
+ return this.tokens[this.current];
1312
+ }
1313
+ previous() {
1314
+ return this.tokens[this.current - 1];
1315
+ }
1316
+ consume(type, message) {
1317
+ if (this.check(type))
1318
+ return this.advance();
1319
+ throw new ExpressionParserError(message, this.peek().position, this.expression);
1320
+ }
1321
+ }
1322
+
1323
+ const STRING_SAFE_METHODS = [
1324
+ 'charAt',
1325
+ 'charCodeAt',
1326
+ 'concat',
1327
+ 'endsWith',
1328
+ 'includes',
1329
+ 'indexOf',
1330
+ 'lastIndexOf',
1331
+ 'match',
1332
+ 'padEnd',
1333
+ 'padStart',
1334
+ 'repeat',
1335
+ 'replace',
1336
+ 'search',
1337
+ 'slice',
1338
+ 'split',
1339
+ 'startsWith',
1340
+ 'substring',
1341
+ 'toLowerCase',
1342
+ 'toUpperCase',
1343
+ 'trim',
1344
+ 'trimEnd',
1345
+ 'trimStart',
1346
+ 'toString',
1347
+ ];
1348
+ const NUMBER_SAFE_METHODS = ['toExponential', 'toFixed', 'toPrecision', 'toString'];
1349
+ const ARRAY_SAFE_METHODS = [
1350
+ 'concat',
1351
+ 'every',
1352
+ 'filter',
1353
+ 'find',
1354
+ 'findIndex',
1355
+ 'flat',
1356
+ 'flatMap',
1357
+ 'includes',
1358
+ 'indexOf',
1359
+ 'join',
1360
+ 'lastIndexOf',
1361
+ 'map',
1362
+ 'reduce',
1363
+ 'reduceRight',
1364
+ 'slice',
1365
+ 'some',
1366
+ 'toString',
1367
+ 'entries',
1368
+ 'keys',
1369
+ 'values',
1370
+ ];
1371
+ const DATE_SAFE_METHODS = [
1372
+ 'getDate',
1373
+ 'getDay',
1374
+ 'getFullYear',
1375
+ 'getHours',
1376
+ 'getMilliseconds',
1377
+ 'getMinutes',
1378
+ 'getMonth',
1379
+ 'getSeconds',
1380
+ 'getTime',
1381
+ 'getTimezoneOffset',
1382
+ 'getUTCDate',
1383
+ 'getUTCDay',
1384
+ 'getUTCFullYear',
1385
+ 'getUTCHours',
1386
+ 'getUTCMilliseconds',
1387
+ 'getUTCMinutes',
1388
+ 'getUTCMonth',
1389
+ 'getUTCSeconds',
1390
+ 'toDateString',
1391
+ 'toISOString',
1392
+ 'toJSON',
1393
+ 'toString',
1394
+ 'toTimeString',
1395
+ 'toUTCString',
1396
+ ];
1397
+ const SAFE_METHODS = {
1398
+ string: new Set(STRING_SAFE_METHODS),
1399
+ number: new Set(NUMBER_SAFE_METHODS),
1400
+ array: new Set(ARRAY_SAFE_METHODS),
1401
+ date: new Set(DATE_SAFE_METHODS),
1402
+ };
1403
+ const BLOCKED_PROPERTIES = new Set([
1404
+ 'constructor',
1405
+ '__proto__',
1406
+ 'prototype',
1407
+ '__defineGetter__',
1408
+ '__defineSetter__',
1409
+ '__lookupGetter__',
1410
+ '__lookupSetter__',
1411
+ ]);
1412
+ class Evaluator {
1413
+ scope;
1414
+ expression;
1415
+ constructor(scope, expression) {
1416
+ this.scope = scope;
1417
+ this.expression = expression;
1418
+ }
1419
+ evaluate(node) {
1420
+ switch (node.type) {
1421
+ case 'Literal':
1422
+ return node.value;
1423
+ case 'Identifier':
1424
+ return this.evaluateIdentifier(node.name);
1425
+ case 'MemberAccess':
1426
+ return this.evaluateMemberAccess(node);
1427
+ case 'ComputedMemberAccess':
1428
+ return this.evaluateComputedMemberAccess(node);
1429
+ case 'BinaryOp':
1430
+ return this.evaluateBinaryOp(node);
1431
+ case 'UnaryOp':
1432
+ return this.evaluateUnaryOp(node);
1433
+ case 'CallExpression':
1434
+ return this.evaluateCallExpression(node);
1435
+ case 'Conditional':
1436
+ return this.evaluate(node.test) ? this.evaluate(node.consequent) : this.evaluate(node.alternate);
1437
+ case 'ArrayLiteral':
1438
+ return this.evaluateArrayLiteral(node);
1439
+ case 'ObjectLiteral':
1440
+ return this.evaluateObjectLiteral(node);
1441
+ case 'ArrowFunction':
1442
+ return this.evaluateArrowFunction(node);
1443
+ default:
1444
+ throw new ExpressionParserError(`Unknown node type: ${node.type}`, 0, this.expression);
1445
+ }
1446
+ }
1447
+ evaluateIdentifier(name) {
1448
+ if (name in this.scope) {
1449
+ return this.scope[name];
1450
+ }
1451
+ return undefined;
1452
+ }
1453
+ evaluateMemberAccess(node) {
1454
+ const obj = this.evaluate(node.object);
1455
+ if (obj === null || obj === undefined) {
1456
+ return undefined;
1457
+ }
1458
+ if (BLOCKED_PROPERTIES.has(node.property)) {
1459
+ throw new ExpressionParserError(`Property "${node.property}" is not accessible for security reasons`, 0, this.expression);
1460
+ }
1461
+ if (typeof obj === 'object' || typeof obj === 'string' || typeof obj === 'number') {
1462
+ return obj[node.property];
1463
+ }
1464
+ return undefined;
1465
+ }
1466
+ evaluateComputedMemberAccess(node) {
1467
+ const obj = this.evaluate(node.object);
1468
+ if (obj === null || obj === undefined) {
1469
+ return undefined;
1470
+ }
1471
+ const rawKey = this.evaluate(node.property);
1472
+ if (rawKey === null || rawKey === undefined)
1473
+ return undefined;
1474
+ const key = String(rawKey);
1475
+ if (BLOCKED_PROPERTIES.has(key)) {
1476
+ throw new ExpressionParserError(`Property "${key}" is not accessible for security reasons`, 0, this.expression);
1477
+ }
1478
+ if (typeof obj === 'object' || typeof obj === 'string' || typeof obj === 'number') {
1479
+ return obj[key];
1480
+ }
1481
+ return undefined;
1482
+ }
1483
+ evaluateBinaryOp(node) {
1484
+ if (node.operator === '&&') {
1485
+ const left = this.evaluate(node.left);
1486
+ return left ? this.evaluate(node.right) : left;
1487
+ }
1488
+ if (node.operator === '||') {
1489
+ const left = this.evaluate(node.left);
1490
+ return left ? left : this.evaluate(node.right);
1491
+ }
1492
+ const left = this.evaluate(node.left);
1493
+ const right = this.evaluate(node.right);
1494
+ switch (node.operator) {
1495
+ case '+':
1496
+ return left + right;
1497
+ case '-':
1498
+ return left - right;
1499
+ case '*':
1500
+ return left * right;
1501
+ case '/':
1502
+ if (right === 0)
1503
+ return null;
1504
+ return left / right;
1505
+ case '%':
1506
+ if (right === 0)
1507
+ return null;
1508
+ return left % right;
1509
+ case '==':
1510
+ return left == right;
1511
+ case '===':
1512
+ return left === right;
1513
+ case '!=':
1514
+ return left != right;
1515
+ case '!==':
1516
+ return left !== right;
1517
+ case '>':
1518
+ return left > right;
1519
+ case '<':
1520
+ return left < right;
1521
+ case '>=':
1522
+ return left >= right;
1523
+ case '<=':
1524
+ return left <= right;
1525
+ default:
1526
+ throw new ExpressionParserError(`Unknown binary operator: ${node.operator}`, 0, this.expression);
1527
+ }
1528
+ }
1529
+ evaluateUnaryOp(node) {
1530
+ const operand = this.evaluate(node.operand);
1531
+ switch (node.operator) {
1532
+ case '!':
1533
+ return !operand;
1534
+ case '-':
1535
+ return -operand;
1536
+ case '+':
1537
+ return +operand;
1538
+ default:
1539
+ throw new ExpressionParserError(`Unknown unary operator: ${node.operator}`, 0, this.expression);
1540
+ }
1541
+ }
1542
+ evaluateCallExpression(node) {
1543
+ if (node.callee.type !== 'MemberAccess') {
1544
+ throw new ExpressionParserError('Only method calls are allowed, not arbitrary function calls', 0, this.expression);
1545
+ }
1546
+ const obj = this.evaluate(node.callee.object);
1547
+ if (obj === null || obj === undefined) {
1548
+ return undefined;
1549
+ }
1550
+ const methodName = node.callee.property;
1551
+ if (BLOCKED_PROPERTIES.has(methodName)) {
1552
+ throw new ExpressionParserError(`Property "${methodName}" is not accessible for security reasons`, 0, this.expression);
1553
+ }
1554
+ if (!this.isMethodSafe(obj, methodName)) {
1555
+ throw new ExpressionParserError(`Method "${methodName}" is not allowed for security reasons`, 0, this.expression);
1556
+ }
1557
+ const args = node.arguments.map((arg) => this.evaluate(arg));
1558
+ const method = obj[methodName];
1559
+ if (typeof method === 'function') {
1560
+ return method.call(obj, ...args);
1561
+ }
1562
+ return undefined;
1563
+ }
1564
+ evaluateArrayLiteral(node) {
1565
+ return node.elements.map((element) => this.evaluate(element));
1566
+ }
1567
+ evaluateObjectLiteral(node) {
1568
+ const result = {};
1569
+ for (const { key, value } of node.properties) {
1570
+ if (BLOCKED_PROPERTIES.has(key)) {
1571
+ throw new ExpressionParserError(`Property "${key}" is not allowed in object literals for security reasons`, 0, this.expression);
1572
+ }
1573
+ result[key] = this.evaluate(value);
1574
+ }
1575
+ return result;
1576
+ }
1577
+ evaluateArrowFunction(node) {
1578
+ const parentScope = this.scope;
1579
+ const expression = this.expression;
1580
+ return (...args) => {
1581
+ const childScope = { ...parentScope };
1582
+ for (let i = 0; i < node.params.length; i++) {
1583
+ childScope[node.params[i]] = args[i];
1584
+ }
1585
+ const childEvaluator = new Evaluator(childScope, expression);
1586
+ return childEvaluator.evaluate(node.body);
1587
+ };
1588
+ }
1589
+ isMethodSafe(obj, methodName) {
1590
+ if (obj === null || obj === undefined) {
1591
+ return false;
1592
+ }
1593
+ const type = Array.isArray(obj) ? 'array' : obj instanceof Date ? 'date' : typeof obj;
1594
+ const safeMethods = SAFE_METHODS[type];
1595
+ if (!safeMethods) {
1596
+ return false;
1597
+ }
1598
+ return safeMethods.has(methodName);
1599
+ }
1600
+ }
1601
+
1602
+ const MAX_AST_CACHE_SIZE = 1000;
1603
+ class LRUCache {
1604
+ cache = new Map();
1605
+ maxSize;
1606
+ constructor(maxSize) {
1607
+ this.maxSize = maxSize;
1608
+ }
1609
+ get(key) {
1610
+ const value = this.cache.get(key);
1611
+ if (value !== undefined) {
1612
+ this.cache.delete(key);
1613
+ this.cache.set(key, value);
1614
+ }
1615
+ return value;
1616
+ }
1617
+ set(key, value) {
1618
+ if (this.cache.has(key)) {
1619
+ this.cache.delete(key);
1620
+ }
1621
+ this.cache.set(key, value);
1622
+ if (this.cache.size > this.maxSize) {
1623
+ const firstKey = this.cache.keys().next().value;
1624
+ if (firstKey !== undefined) {
1625
+ this.cache.delete(firstKey);
1626
+ }
1627
+ }
1628
+ }
1629
+ clear() {
1630
+ this.cache.clear();
1631
+ }
1632
+ get size() {
1633
+ return this.cache.size;
1634
+ }
1635
+ }
1636
+ const astCache = new LRUCache(MAX_AST_CACHE_SIZE);
1637
+ class ExpressionParser {
1638
+ static parse(expression) {
1639
+ const cached = astCache.get(expression);
1640
+ if (cached) {
1641
+ return cached;
1642
+ }
1643
+ const parser = new Parser(expression);
1644
+ const ast = parser.parse();
1645
+ astCache.set(expression, ast);
1646
+ return ast;
1647
+ }
1648
+ static evaluate(expression, scope) {
1649
+ try {
1650
+ const ast = this.parse(expression);
1651
+ const evaluator = new Evaluator(scope, expression);
1652
+ return evaluator.evaluate(ast);
1653
+ }
1654
+ catch (error) {
1655
+ if (error instanceof ExpressionParserError) {
1656
+ throw error;
1657
+ }
1658
+ throw new ExpressionParserError(`Error evaluating expression: ${error instanceof Error ? error.message : String(error)}`, 0, expression);
1659
+ }
1660
+ }
1661
+ static clearCache() {
1662
+ astCache.clear();
1663
+ }
1664
+ static getCacheStats() {
1665
+ return {
1666
+ size: astCache.size,
1667
+ maxSize: MAX_AST_CACHE_SIZE,
1668
+ };
1669
+ }
1670
+ }
1671
+
1672
+ function isThenableOrObservable(value) {
1673
+ if (value === null || typeof value !== 'object')
1674
+ return false;
1675
+ const candidate = value;
1676
+ return typeof candidate.then === 'function' || typeof candidate.subscribe === 'function';
1677
+ }
1678
+ function evaluateCondition(expression, context) {
1679
+ switch (expression.type) {
1680
+ case 'fieldValue':
1681
+ return evaluateFieldValueCondition(expression, context);
1682
+ case 'javascript':
1683
+ return evaluateJavaScriptExpression(expression, context);
1684
+ case 'custom': {
1685
+ const bothSet = expression;
1686
+ const inlineFn = bothSet.fn;
1687
+ const registeredName = bothSet.functionName;
1688
+ if (inlineFn && registeredName) {
1689
+ context.logger.warn('Both "fn" and "functionName" are set on custom condition. Inline "fn" takes precedence; "functionName" is ignored.');
1690
+ }
1691
+ const customFn = inlineFn ?? (registeredName ? context.customFunctions?.[registeredName] : undefined);
1692
+ if (!customFn) {
1693
+ context.logger.error('Custom function not found:', registeredName);
1694
+ return false;
1695
+ }
1696
+ try {
1697
+ const result = customFn(context);
1698
+ if (isThenableOrObservable(result)) {
1699
+ context.logger.warn(`Custom condition '${registeredName ?? '<inline>'}' returned a Promise or Observable. ` +
1700
+ `Synchronous conditions must return a value; use an 'async' or 'http' condition type for asynchronous logic. Treating as false.`);
1701
+ return false;
1702
+ }
1703
+ return !!result;
1704
+ }
1705
+ catch (error) {
1706
+ context.logger.error('Error executing custom function:', registeredName ?? '<inline>', error);
1707
+ return false;
1708
+ }
1709
+ }
1710
+ case 'and':
1711
+ return evaluateAndCondition(expression, context);
1712
+ case 'or':
1713
+ return evaluateOrCondition(expression, context);
1714
+ case 'http':
1715
+ context.logger.warn('HTTP conditions are resolved asynchronously via createHttpConditionLogicFunction(). ' +
1716
+ 'When used inside and/or composites, the HTTP result is not available synchronously. Returning false.');
1717
+ return false;
1718
+ case 'async':
1719
+ context.logger.warn('Async Condition - resolved asynchronously via createAsyncConditionLogicFunction(). ' +
1720
+ 'When used inside and/or composites, the async result is not available synchronously. Returning false.');
1721
+ return false;
1722
+ default:
1723
+ return false;
1724
+ }
1725
+ }
1726
+ function evaluateFieldValueCondition(expression, context) {
1727
+ if (!expression.fieldPath)
1728
+ return false;
1729
+ const fieldValue = hasNestedProperty(context.formValue, expression.fieldPath)
1730
+ ? getNestedValue(context.formValue, expression.fieldPath)
1731
+ : context.rootFormValue
1732
+ ? getNestedValue(context.rootFormValue, expression.fieldPath)
1733
+ : undefined;
1734
+ return compareValues(fieldValue, expression.value, expression.operator);
1735
+ }
1736
+ function evaluateJavaScriptExpression(expression, context) {
1737
+ try {
1738
+ const result = ExpressionParser.evaluate(expression.expression, context);
1739
+ return !!result;
1740
+ }
1741
+ catch (error) {
1742
+ context.logger.error('Error evaluating JavaScript expression:', expression.expression, error);
1743
+ return false;
1744
+ }
1745
+ }
1746
+ function evaluateAndCondition(expression, context) {
1747
+ if (expression.conditions.length === 0)
1748
+ return false;
1749
+ return expression.conditions.every((condition) => evaluateCondition(condition, context));
1750
+ }
1751
+ function evaluateOrCondition(expression, context) {
1752
+ if (expression.conditions.length === 0)
1753
+ return false;
1754
+ return expression.conditions.some((condition) => evaluateCondition(condition, context));
1755
+ }
1756
+
1757
+ class HttpConditionFunctionCacheService {
1758
+ httpConditionFunctionCache = new Map();
1759
+ }
1760
+
1761
+ class HttpConditionCache {
1762
+ cache = new Map();
1763
+ maxEntries;
1764
+ constructor(maxEntries = 100) {
1765
+ this.maxEntries = Math.max(1, maxEntries);
1766
+ }
1767
+ get(key) {
1768
+ const entry = this.cache.get(key);
1769
+ if (!entry)
1770
+ return undefined;
1771
+ if (Date.now() > entry.expiresAt) {
1772
+ this.cache.delete(key);
1773
+ return undefined;
1774
+ }
1775
+ return entry.value;
1776
+ }
1777
+ set(key, value, ttlMs) {
1778
+ while (this.cache.size >= this.maxEntries && !this.cache.has(key)) {
1779
+ const oldestKey = this.cache.keys().next().value;
1780
+ if (oldestKey !== undefined) {
1781
+ this.cache.delete(oldestKey);
1782
+ }
1783
+ }
1784
+ this.cache.set(key, { value, expiresAt: Date.now() + ttlMs });
1785
+ }
1786
+ clear() {
1787
+ this.cache.clear();
1788
+ }
1789
+ }
1790
+ const HTTP_CONDITION_CACHE = new InjectionToken('HttpConditionCache');
1791
+
1792
+ function resolveHttpRequest(config, context) {
1793
+ let url = config.url;
1794
+ if (config.params) {
1795
+ for (const [key, expression] of Object.entries(config.params)) {
1796
+ const value = ExpressionParser.evaluate(expression, context);
1797
+ if (value == null) {
1798
+ context.logger.debug(`HTTP request suppressed: path param '${key}' resolved to ${String(value)} ` +
1799
+ `(expression: '${expression}'). The request will not be sent.`);
1800
+ return null;
1801
+ }
1802
+ url = url.replace(`:${key}`, encodeURIComponent(String(value)));
1803
+ }
1804
+ }
1805
+ if (config.queryParams) {
1806
+ const params = new URLSearchParams();
1807
+ for (const [key, expression] of Object.entries(config.queryParams)) {
1808
+ const value = ExpressionParser.evaluate(expression, context);
1809
+ params.set(key, value != null ? String(value) : '');
1810
+ }
1811
+ const queryString = params.toString();
1812
+ if (queryString) {
1813
+ url += (url.includes('?') ? '&' : '?') + queryString;
1814
+ }
1815
+ }
1816
+ const request = {
1817
+ url,
1818
+ method: config.method,
1819
+ };
1820
+ if (config.body) {
1821
+ if (config.evaluateBodyExpressions) {
1822
+ const resolvedBody = {};
1823
+ for (const [key, value] of Object.entries(config.body)) {
1824
+ if (typeof value === 'string') {
1825
+ resolvedBody[key] = ExpressionParser.evaluate(value, context);
1826
+ }
1827
+ else {
1828
+ resolvedBody[key] = value;
1829
+ }
1830
+ }
1831
+ request.body = resolvedBody;
1832
+ }
1833
+ else {
1834
+ request.body = config.body;
1835
+ }
1836
+ }
1837
+ if (config.headers) {
1838
+ request.headers = config.headers;
1839
+ }
1840
+ return request;
1841
+ }
1842
+
1843
+ function extractBoolean(response, responseExpression, pendingValue, logger) {
1844
+ if (responseExpression) {
1845
+ try {
1846
+ const result = ExpressionParser.evaluate(responseExpression, { response });
1847
+ if (typeof result !== 'boolean') {
1848
+ logger.warn(`responseExpression "${responseExpression}" returned non-boolean value:`, result, 'Consider returning a boolean explicitly.');
1849
+ }
1850
+ return !!result;
1851
+ }
1852
+ catch (error) {
1853
+ logger.warn(`Failed to evaluate responseExpression '${responseExpression}':`, error);
1854
+ return pendingValue;
1855
+ }
1856
+ }
1857
+ return !!response;
1858
+ }
1859
+ function createHttpConditionLogicFunction(condition) {
1860
+ const httpClient = inject(HttpClient, { optional: true });
1861
+ if (!httpClient) {
1862
+ throw new DynamicFormError('HttpClient is required for HTTP conditions. Add provideHttpClient() to your providers.');
1863
+ }
1864
+ const fieldContextRegistry = inject(FieldContextRegistryService);
1865
+ const functionRegistry = inject(FunctionRegistryService);
1866
+ const injector = inject(Injector);
1867
+ const cache = inject(HTTP_CONDITION_CACHE);
1868
+ const logger = inject(DynamicFormLogger);
1869
+ const cacheService = inject(HttpConditionFunctionCacheService);
1870
+ const pendingValue = condition.pendingValue ?? false;
1871
+ const cacheDurationMs = condition.cacheDurationMs ?? 30000;
1872
+ const debounceMs = condition.debounceMs ?? 300;
1873
+ const cacheKey = stableStringify(condition);
1874
+ const cached = cacheService.httpConditionFunctionCache.get(cacheKey);
1875
+ if (cached) {
1876
+ return cached;
1877
+ }
1878
+ const fn = createDebouncedResourceLogicFn({
1879
+ pendingValue,
1880
+ debounceMs,
1881
+ injector,
1882
+ resolve: (ctx) => {
1883
+ const evaluationContext = fieldContextRegistry.createReactiveEvaluationContext(ctx, functionRegistry.getCustomFunctions());
1884
+ const resolved = resolveHttpRequest(condition.http, evaluationContext);
1885
+ if (!resolved)
1886
+ return { kind: 'holdPrevious' };
1887
+ const requestKey = stableStringify(resolved);
1888
+ const cachedResult = cache.get(requestKey);
1889
+ if (cachedResult !== undefined)
1890
+ return { kind: 'returnEarly', value: cachedResult };
1891
+ return { kind: 'trigger', key: requestKey, payload: resolved };
1892
+ },
1893
+ buildStream: (request) => {
1894
+ const method = request.method ?? 'GET';
1895
+ const options = {};
1896
+ if (request.body !== undefined)
1897
+ options['body'] = request.body;
1898
+ if (request.headers)
1899
+ options['headers'] = request.headers;
1900
+ return httpClient.request(method, request.url, options).pipe(map((response) => extractBoolean(response, condition.responseExpression, pendingValue, logger)), tap((value) => {
1901
+ const requestKey = stableStringify(request);
1902
+ cache.set(requestKey, value, cacheDurationMs);
1903
+ }), catchError((error) => {
1904
+ logger.warn('HTTP condition request failed:', error);
1905
+ return of(pendingValue);
1906
+ }));
1907
+ },
1908
+ });
1909
+ cacheService.httpConditionFunctionCache.set(cacheKey, fn);
1910
+ return fn;
1911
+ }
1912
+
1913
+ class LogicFunctionCacheService {
1914
+ logicFunctionCache = new Map();
1915
+ debouncedLogicFunctionCache = new Map();
1916
+ debouncedSignalStore = new Map();
1917
+ }
1918
+
1919
+ function validateNoNestedHttpConditions(expression) {
1920
+ if (expression.type === 'and' || expression.type === 'or') {
1921
+ for (const sub of expression.conditions) {
1922
+ if (sub.type === 'http' || sub.type === 'async') {
1923
+ const label = sub.type === 'http' ? 'HTTP' : 'Async';
1924
+ throw new DynamicFormError(`${label} conditions cannot be nested inside '${expression.type}' composites. ` +
1925
+ `Move the ${label} condition to a separate logic entry on the field.`);
1926
+ }
1927
+ validateNoNestedHttpConditions(sub);
1928
+ }
1929
+ }
1930
+ }
1931
+ function createLogicFunction(expression) {
1932
+ if (expression?.type === 'async') {
1933
+ return createAsyncConditionLogicFunction(expression);
1934
+ }
1935
+ if (expression?.type === 'http') {
1936
+ return createHttpConditionLogicFunction(expression);
1937
+ }
1938
+ if (expression) {
1939
+ validateNoNestedHttpConditions(expression);
1940
+ }
1941
+ const functionRegistry = inject(FunctionRegistryService);
1942
+ const fieldContextRegistry = inject(FieldContextRegistryService);
1943
+ const cacheService = inject(LogicFunctionCacheService);
1944
+ const cacheKey = stableStringify(expression);
1945
+ const cached = cacheService.logicFunctionCache.get(cacheKey);
1946
+ if (cached) {
1947
+ return cached;
1948
+ }
1949
+ const fn = (ctx) => {
1950
+ const evaluationContext = fieldContextRegistry.createReactiveEvaluationContext(ctx, functionRegistry.getCustomFunctions());
1951
+ return evaluateCondition(expression, evaluationContext);
1952
+ };
1953
+ cacheService.logicFunctionCache.set(cacheKey, fn);
1954
+ return fn;
1955
+ }
1956
+ function createDebouncedLogicFunction(expression, debounceMs) {
1957
+ if (expression?.type === 'async') {
1958
+ return createAsyncConditionLogicFunction(expression);
1959
+ }
1960
+ if (expression?.type === 'http') {
1961
+ return createHttpConditionLogicFunction(expression);
1962
+ }
1963
+ if (expression) {
1964
+ validateNoNestedHttpConditions(expression);
1965
+ }
1966
+ const functionRegistry = inject(FunctionRegistryService);
1967
+ const fieldContextRegistry = inject(FieldContextRegistryService);
1968
+ const injector = inject(Injector);
1969
+ const cacheService = inject(LogicFunctionCacheService);
1970
+ const cacheKey = `${stableStringify(expression)}:${debounceMs}`;
1971
+ const cached = cacheService.debouncedLogicFunctionCache.get(cacheKey);
1972
+ if (cached) {
1973
+ return cached;
1974
+ }
1975
+ const fn = (ctx) => {
1976
+ const contextKey = safeReadPathKeys(ctx).join('.');
1977
+ let signalPair = cacheService.debouncedSignalStore.get(contextKey);
1978
+ if (!signalPair) {
1979
+ const immediateValue = signal(false, ...(ngDevMode ? [{ debugName: "immediateValue" }] : /* istanbul ignore next */ []));
1980
+ const debouncedValue = untracked(() => {
1981
+ const immediateValue$ = toObservable(immediateValue, { injector }).pipe(debounceTime(debounceMs), distinctUntilChanged(), startWith(false));
1982
+ return toSignal(immediateValue$, { injector, initialValue: false });
1983
+ });
1984
+ signalPair = { immediateValue, debouncedValue };
1985
+ cacheService.debouncedSignalStore.set(contextKey, signalPair);
1986
+ }
1987
+ const evaluationContext = fieldContextRegistry.createReactiveEvaluationContext(ctx, functionRegistry.getCustomFunctions());
1988
+ const result = evaluateCondition(expression, evaluationContext);
1989
+ const { immediateValue, debouncedValue } = signalPair;
1990
+ untracked(() => {
1991
+ if (immediateValue() !== result) {
1992
+ immediateValue.set(result);
1993
+ }
1994
+ });
1995
+ return debouncedValue() ?? false;
1996
+ };
1997
+ cacheService.debouncedLogicFunctionCache.set(cacheKey, fn);
1998
+ return fn;
1999
+ }
2000
+
2001
+ function getArrayLength(arrayFieldTree) {
2002
+ const value = arrayFieldTree().value();
2003
+ return Array.isArray(value) ? value.length : 0;
2004
+ }
2005
+ function toReadonlyFieldTree(field) {
2006
+ const state = field();
2007
+ return {
2008
+ value: state.value,
2009
+ valid: state.valid,
2010
+ invalid: state.invalid,
2011
+ touched: state.touched,
2012
+ dirty: state.dirty,
2013
+ required: state.required,
2014
+ disabled: state.disabled,
2015
+ hidden: state.hidden,
2016
+ errors: state.errors,
2017
+ };
2018
+ }
2019
+ function writeToFieldValue(value, next, logger) {
2020
+ const candidate = value;
2021
+ if (typeof candidate.set !== 'function') {
2022
+ logger?.warn('writeToFieldValue: FieldState.value is not a WritableSignal — Signal Forms contract changed.');
2023
+ return false;
2024
+ }
2025
+ candidate.set(next);
2026
+ return true;
2027
+ }
2028
+ const READONLY_FIELD_TREE_CACHE = new InjectionToken('READONLY_FIELD_TREE_CACHE', {
2029
+ providedIn: 'root',
2030
+ factory: () => new WeakMap(),
2031
+ });
2032
+ function toReadonlyFieldTreeCached(cache, field) {
2033
+ const key = field;
2034
+ const cached = cache.get(key);
2035
+ if (cached)
2036
+ return cached;
2037
+ const view = toReadonlyFieldTree(field);
2038
+ cache.set(key, view);
2039
+ return view;
2040
+ }
2041
+
2042
+ function evaluateHttpValidationResponse(response, mapping, logger) {
2043
+ const scope = { response };
2044
+ try {
2045
+ const result = ExpressionParser.evaluate(mapping.validWhen, scope);
2046
+ if (typeof result !== 'boolean') {
2047
+ logger.warn(`validWhen expression "${mapping.validWhen}" returned non-boolean value:`, result, 'Expected true or false.');
2048
+ }
2049
+ if (result === true) {
2050
+ return null;
2051
+ }
2052
+ const error = { kind: mapping.errorKind };
2053
+ if (mapping.errorParams) {
2054
+ for (const [key, expression] of Object.entries(mapping.errorParams)) {
2055
+ try {
2056
+ error[key] = ExpressionParser.evaluate(expression, scope);
2057
+ }
2058
+ catch (err) {
2059
+ logger.warn(`Error evaluating errorParam "${key}":`, expression, err);
2060
+ }
2061
+ }
2062
+ }
2063
+ return error;
2064
+ }
2065
+ catch (err) {
2066
+ logger.warn('Error evaluating validWhen expression:', mapping.validWhen, err);
2067
+ return { kind: mapping.errorKind };
2068
+ }
2069
+ }
2070
+
2071
+ const noOpLogger = {
2072
+ debug: () => { },
2073
+ info: () => { },
2074
+ warn: () => { },
2075
+ error: () => { },
2076
+ };
2077
+ const DEFAULT_SUBMIT_BUTTON_OPTIONS = {
2078
+ disableWhenInvalid: true,
2079
+ disableWhileSubmitting: true,
2080
+ };
2081
+ const DEFAULT_NEXT_BUTTON_OPTIONS = {
2082
+ disableWhenPageInvalid: true,
2083
+ disableWhileSubmitting: true,
2084
+ };
2085
+ function evaluateFormStateCondition(condition, ctx) {
2086
+ const form = ctx.form();
2087
+ switch (condition) {
2088
+ case 'formInvalid':
2089
+ return !form.valid();
2090
+ case 'formSubmitting':
2091
+ return form.submitting();
2092
+ case 'pageInvalid':
2093
+ return ctx.currentPageValid ? !ctx.currentPageValid() : false;
2094
+ default:
2095
+ return false;
2096
+ }
2097
+ }
2098
+ function evaluateLogicCondition(condition, ctx) {
2099
+ if (typeof condition === 'boolean') {
2100
+ return condition;
2101
+ }
2102
+ if (isFormStateCondition(condition)) {
2103
+ return evaluateFormStateCondition(condition, ctx);
2104
+ }
2105
+ const formValue = (ctx.formValue ?? ctx.form().value());
2106
+ const evaluationContext = {
2107
+ fieldValue: undefined,
2108
+ formValue,
2109
+ fieldPath: '',
2110
+ logger: ctx.logger ?? noOpLogger,
2111
+ };
2112
+ return evaluateCondition(condition, evaluationContext);
2113
+ }
2114
+ function hasFieldLevelDisabledCondition(fieldLogic, ctx) {
2115
+ if (!fieldLogic || fieldLogic.length === 0) {
2116
+ return false;
2117
+ }
2118
+ return fieldLogic.filter((logic) => logic.type === 'disabled').some((logic) => evaluateLogicCondition(logic.condition, ctx));
2119
+ }
2120
+ function hasCustomLogicOfType(fieldLogic, logicType) {
2121
+ if (!fieldLogic || fieldLogic.length === 0) {
2122
+ return false;
2123
+ }
2124
+ return fieldLogic.some((logic) => logic.type === logicType);
2125
+ }
2126
+ function evaluateLogicOfType(fieldLogic, logicType, ctx) {
2127
+ if (!fieldLogic || fieldLogic.length === 0) {
2128
+ return false;
2129
+ }
2130
+ const buttonCtx = {
2131
+ form: ctx.form,
2132
+ formValue: ctx.formValue,
2133
+ logger: ctx.logger,
2134
+ };
2135
+ return fieldLogic.filter((logic) => logic.type === logicType).some((logic) => evaluateLogicCondition(logic.condition, buttonCtx));
2136
+ }
2137
+ function resolveSubmitButtonDisabled(ctx) {
2138
+ return computed(() => {
2139
+ if (ctx.explicitlyDisabled) {
2140
+ return true;
2141
+ }
2142
+ if (hasCustomLogicOfType(ctx.fieldLogic, 'disabled')) {
2143
+ return hasFieldLevelDisabledCondition(ctx.fieldLogic, ctx);
2144
+ }
2145
+ const options = {
2146
+ ...DEFAULT_SUBMIT_BUTTON_OPTIONS,
2147
+ ...ctx.formOptions?.submitButton,
2148
+ };
2149
+ const form = ctx.form();
2150
+ if (options.disableWhenInvalid && !form.valid()) {
2151
+ return true;
2152
+ }
2153
+ if (options.disableWhileSubmitting && form.submitting()) {
2154
+ return true;
2155
+ }
2156
+ return false;
2157
+ });
2158
+ }
2159
+ function resolveNextButtonDisabled(ctx) {
2160
+ return computed(() => {
2161
+ if (ctx.explicitlyDisabled) {
2162
+ return true;
2163
+ }
2164
+ if (hasCustomLogicOfType(ctx.fieldLogic, 'disabled')) {
2165
+ return hasFieldLevelDisabledCondition(ctx.fieldLogic, ctx);
2166
+ }
2167
+ const options = {
2168
+ ...DEFAULT_NEXT_BUTTON_OPTIONS,
2169
+ ...ctx.formOptions?.nextButton,
2170
+ };
2171
+ const form = ctx.form();
2172
+ if (options.disableWhenPageInvalid && ctx.currentPageValid && !ctx.currentPageValid()) {
2173
+ return true;
2174
+ }
2175
+ if (options.disableWhileSubmitting && form.submitting()) {
2176
+ return true;
2177
+ }
2178
+ return false;
2179
+ });
2180
+ }
2181
+ function evaluateNonFieldHidden(ctx) {
2182
+ if (ctx.explicitValue) {
2183
+ return true;
2184
+ }
2185
+ if (hasCustomLogicOfType(ctx.fieldLogic, 'hidden')) {
2186
+ return evaluateLogicOfType(ctx.fieldLogic, 'hidden', {
2187
+ form: ctx.form,
2188
+ formValue: ctx.formValue,
2189
+ logger: ctx.logger,
2190
+ });
2191
+ }
2192
+ return false;
2193
+ }
2194
+ function resolveNonFieldHidden(ctx) {
2195
+ return computed(() => evaluateNonFieldHidden(ctx));
2196
+ }
2197
+ function evaluateNonFieldDisabled(ctx) {
2198
+ if (ctx.explicitValue) {
2199
+ return true;
2200
+ }
2201
+ if (hasCustomLogicOfType(ctx.fieldLogic, 'disabled')) {
2202
+ return evaluateLogicOfType(ctx.fieldLogic, 'disabled', {
2203
+ form: ctx.form,
2204
+ formValue: ctx.formValue,
2205
+ logger: ctx.logger,
2206
+ });
2207
+ }
2208
+ return false;
2209
+ }
2210
+ function resolveNonFieldDisabled(ctx) {
2211
+ return computed(() => evaluateNonFieldDisabled(ctx));
2212
+ }
2213
+
2214
+ function isFunctionHttpValidator(config) {
2215
+ const c = config;
2216
+ return typeof c.functionName === 'string' || (c.fn !== null && typeof c.fn === 'object');
2217
+ }
2218
+
2219
+ class DynamicValueFunctionCacheService {
2220
+ dynamicValueFunctionCache = new Map();
2221
+ }
2222
+
2223
+ function createDynamicValueFunction(expression) {
2224
+ const fieldContextRegistry = inject(FieldContextRegistryService);
2225
+ const logger = inject(DynamicFormLogger);
2226
+ const cacheService = inject(DynamicValueFunctionCacheService);
2227
+ const cached = cacheService.dynamicValueFunctionCache.get(expression);
2228
+ if (cached) {
2229
+ return cached;
2230
+ }
2231
+ const fn = (ctx) => {
2232
+ const evaluationContext = fieldContextRegistry.createEvaluationContext(ctx);
2233
+ try {
2234
+ return ExpressionParser.evaluate(expression, evaluationContext);
2235
+ }
2236
+ catch (error) {
2237
+ logger.error('Error evaluating dynamic expression:', expression, error);
2238
+ return undefined;
2239
+ }
2240
+ };
2241
+ cacheService.dynamicValueFunctionCache.set(expression, fn);
2242
+ return fn;
2243
+ }
2244
+
2245
+ function applyEmailValidator(path) {
2246
+ email(path);
2247
+ }
2248
+ function applyMinValidator(path, value, expression) {
2249
+ if (expression) {
2250
+ min(path, createDynamicValueFunction(expression));
2251
+ }
2252
+ else {
2253
+ min(path, value);
2254
+ }
2255
+ }
2256
+ function applyMaxValidator(path, value, expression) {
2257
+ if (expression) {
2258
+ max(path, createDynamicValueFunction(expression));
2259
+ }
2260
+ else {
2261
+ max(path, value);
2262
+ }
2263
+ }
2264
+ function applyMinLengthValidator(path, value, expression) {
2265
+ if (expression) {
2266
+ minLength(path, createDynamicValueFunction(expression));
2267
+ }
2268
+ else {
2269
+ minLength(path, value);
2270
+ }
2271
+ }
2272
+ function applyMaxLengthValidator(path, value, expression) {
2273
+ if (expression) {
2274
+ maxLength(path, createDynamicValueFunction(expression));
2275
+ }
2276
+ else {
2277
+ maxLength(path, value);
2278
+ }
2279
+ }
2280
+ function applyPatternValidator(path, value, expression) {
2281
+ if (expression) {
2282
+ pattern(path, createDynamicValueFunction(expression));
2283
+ }
2284
+ else {
2285
+ pattern(path, value);
2286
+ }
2287
+ }
2288
+ function createConditionalLogic(when) {
2289
+ return when ? createLogicFunction(when) : undefined;
2290
+ }
2291
+ function applyValidator(config, fieldPath) {
2292
+ const path = fieldPath;
2293
+ if (!isResourceBasedValidator(config) && (isCrossFieldBuiltInValidator(config) || hasCrossFieldWhenCondition(config))) {
2294
+ return;
2295
+ }
2296
+ switch (config.type) {
2297
+ case 'required':
2298
+ if (config.when) {
2299
+ required(path, { when: createLogicFunction(config.when) });
2300
+ }
2301
+ else {
2302
+ required(path);
2303
+ }
2304
+ break;
2305
+ case 'email':
2306
+ applyEmailValidator(fieldPath);
2307
+ break;
2308
+ case 'min':
2309
+ if (typeof config.value === 'number') {
2310
+ applyMinValidator(fieldPath, config.value, config.expression);
2311
+ }
2312
+ break;
2313
+ case 'max':
2314
+ if (typeof config.value === 'number') {
2315
+ applyMaxValidator(fieldPath, config.value, config.expression);
2316
+ }
2317
+ break;
2318
+ case 'minLength':
2319
+ if (typeof config.value === 'number') {
2320
+ applyMinLengthValidator(fieldPath, config.value, config.expression);
2321
+ }
2322
+ break;
2323
+ case 'maxLength':
2324
+ if (typeof config.value === 'number') {
2325
+ applyMaxLengthValidator(fieldPath, config.value, config.expression);
2326
+ }
2327
+ break;
2328
+ case 'pattern':
2329
+ if (config.value instanceof RegExp || typeof config.value === 'string') {
2330
+ let regexPattern;
2331
+ if (typeof config.value === 'string') {
2332
+ try {
2333
+ regexPattern = new RegExp(config.value);
2334
+ }
2335
+ catch (e) {
2336
+ throw new DynamicFormError(`Invalid regex pattern in validator: '${config.value}' — ${e instanceof Error ? e.message : String(e)}`);
2337
+ }
2338
+ }
2339
+ else {
2340
+ regexPattern = config.value;
2341
+ }
2342
+ applyPatternValidator(fieldPath, regexPattern, config.expression);
2343
+ }
2344
+ break;
2345
+ case 'custom':
2346
+ applyCustomValidator(config, path);
2347
+ break;
2348
+ case 'async':
2349
+ applyAsyncValidator(config, path);
2350
+ break;
2351
+ case 'http':
2352
+ applyUnifiedHttpValidator(config, path);
2353
+ break;
2354
+ }
2355
+ }
2356
+ function applyCustomValidator(config, fieldPath) {
2357
+ if (isCrossFieldValidator(config)) {
2358
+ return;
2359
+ }
2360
+ let validatorFn;
2361
+ if (config.expression) {
2362
+ validatorFn = createExpressionValidator(config);
2363
+ }
2364
+ else if (config.fn || config.functionName) {
2365
+ validatorFn = createFunctionValidator(config);
2366
+ }
2367
+ else {
2368
+ const logger = inject(DynamicFormLogger);
2369
+ logger.warn('Custom validator must have one of "expression", "functionName", or "fn"');
2370
+ return;
2371
+ }
2372
+ const whenLogic = createConditionalLogic(config.when);
2373
+ validate(fieldPath, (ctx) => {
2374
+ if (whenLogic && !whenLogic(ctx))
2375
+ return null;
2376
+ return validatorFn(ctx);
2377
+ });
2378
+ }
2379
+ function createFunctionValidator(config) {
2380
+ const logger = inject(DynamicFormLogger);
2381
+ if (config.fn && config.functionName) {
2382
+ logger.warn('Both "fn" and "functionName" are set on custom validator. Inline "fn" takes precedence; "functionName" is ignored.');
2383
+ }
2384
+ if (config.fn) {
2385
+ const inlineFn = config.fn;
2386
+ return (ctx) => inlineFn(ctx, config.params);
2387
+ }
2388
+ const functionName = config.functionName;
2389
+ if (!functionName) {
2390
+ logger.warn('Custom validator missing functionName');
2391
+ return () => null;
2392
+ }
2393
+ const registry = inject(FunctionRegistryService);
2394
+ const validatorFn = registry.getValidator(functionName);
2395
+ if (!validatorFn) {
2396
+ throw new DynamicFormError(`Custom validator "${functionName}" not found. Register it with customFnConfig.validators.`);
2397
+ }
2398
+ return (ctx) => validatorFn(ctx, config.params);
2399
+ }
2400
+ function createExpressionValidator(config) {
2401
+ const logger = inject(DynamicFormLogger);
2402
+ const expression = config.expression;
2403
+ if (!expression) {
2404
+ logger.warn('Custom validator missing expression');
2405
+ return () => null;
2406
+ }
2407
+ const fieldContextRegistry = inject(FieldContextRegistryService);
2408
+ const functionRegistry = inject(FunctionRegistryService);
2409
+ return (ctx) => {
2410
+ try {
2411
+ const evaluationContext = fieldContextRegistry.createEvaluationContext(ctx, functionRegistry.getCustomFunctions());
2412
+ const result = ExpressionParser.evaluate(expression, evaluationContext);
2413
+ if (result) {
2414
+ return null;
2415
+ }
2416
+ const kind = config.kind || 'custom';
2417
+ const validationError = { kind };
2418
+ if (config.errorParams) {
2419
+ Object.entries(config.errorParams).forEach(([key, expression]) => {
2420
+ try {
2421
+ validationError[key] = ExpressionParser.evaluate(expression, evaluationContext);
2422
+ }
2423
+ catch (err) {
2424
+ logger.warn(`Error evaluating errorParam "${key}":`, expression, err);
2425
+ }
2426
+ });
2427
+ }
2428
+ return validationError;
2429
+ }
2430
+ catch (error) {
2431
+ logger.error('Error evaluating custom validator expression:', expression, error);
2432
+ return { kind: config.kind || 'custom' };
2433
+ }
2434
+ };
2435
+ }
2436
+ function applyAsyncValidator(config, fieldPath) {
2437
+ if (config.fn && config.functionName) {
2438
+ const logger = inject(DynamicFormLogger);
2439
+ logger.warn('Both "fn" and "functionName" are set on async validator. Inline "fn" takes precedence; "functionName" is ignored.');
2440
+ }
2441
+ let validatorConfig;
2442
+ if (config.fn) {
2443
+ validatorConfig = config.fn;
2444
+ }
2445
+ else if (config.functionName) {
2446
+ const registry = inject(FunctionRegistryService);
2447
+ validatorConfig = registry.getAsyncValidator(config.functionName);
2448
+ if (!validatorConfig) {
2449
+ throw new DynamicFormError(`Async validator "${config.functionName}" not found. Register it with customFnConfig.asyncValidators.`);
2450
+ }
2451
+ }
2452
+ else {
2453
+ throw new DynamicFormError('Async validator requires one of "functionName" or "fn".');
2454
+ }
2455
+ const whenLogic = createConditionalLogic(config.when);
2456
+ const asyncOptions = {
2457
+ params: (ctx) => {
2458
+ if (whenLogic && !whenLogic(ctx))
2459
+ return undefined;
2460
+ return validatorConfig.params(ctx, config.params);
2461
+ },
2462
+ factory: validatorConfig.factory,
2463
+ onSuccess: validatorConfig.onSuccess,
2464
+ onError: validatorConfig.onError,
2465
+ };
2466
+ validateAsync(fieldPath, asyncOptions);
2467
+ }
2468
+ function applyUnifiedHttpValidator(config, fieldPath) {
2469
+ if (isFunctionHttpValidator(config)) {
2470
+ applyFunctionHttpValidator(config, fieldPath);
2471
+ return;
2472
+ }
2473
+ const declarative = config;
2474
+ if (!declarative.http || !declarative.responseMapping) {
2475
+ throw new DynamicFormError('HTTP validator requires one of "functionName" / "fn" (function-based) or "http" + "responseMapping" (declarative).');
2476
+ }
2477
+ applyDeclarativeHttpValidator(config, fieldPath);
2478
+ }
2479
+ function applyFunctionHttpValidator(config, fieldPath) {
2480
+ if (config.fn && config.functionName) {
2481
+ const logger = inject(DynamicFormLogger);
2482
+ logger.warn('Both "fn" and "functionName" are set on HTTP validator. Inline "fn" takes precedence; "functionName" is ignored.');
2483
+ }
2484
+ let httpValidatorConfig;
2485
+ if (config.fn) {
2486
+ httpValidatorConfig = config.fn;
2487
+ }
2488
+ else if (config.functionName) {
2489
+ const registry = inject(FunctionRegistryService);
2490
+ httpValidatorConfig = registry.getHttpValidator(config.functionName);
2491
+ if (!httpValidatorConfig) {
2492
+ throw new DynamicFormError(`HTTP validator "${config.functionName}" not found. Register it with customFnConfig.httpValidators.`);
2493
+ }
2494
+ }
2495
+ else {
2496
+ throw new DynamicFormError('HTTP validator requires one of "functionName" or "fn".');
2497
+ }
2498
+ const whenLogic = createConditionalLogic(config.when);
2499
+ const httpOptions = {
2500
+ request: (ctx) => {
2501
+ if (whenLogic && !whenLogic(ctx))
2502
+ return undefined;
2503
+ return httpValidatorConfig.request(ctx, config.params);
2504
+ },
2505
+ onSuccess: httpValidatorConfig.onSuccess,
2506
+ onError: httpValidatorConfig.onError,
2507
+ };
2508
+ validateHttp(fieldPath, httpOptions);
2509
+ }
2510
+ function applyDeclarativeHttpValidator(config, fieldPath) {
2511
+ const fieldContextRegistry = inject(FieldContextRegistryService);
2512
+ const functionRegistry = inject(FunctionRegistryService);
2513
+ const logger = inject(DynamicFormLogger);
2514
+ const whenLogic = createConditionalLogic(config.when);
2515
+ validateHttp(fieldPath, {
2516
+ request: (ctx) => {
2517
+ if (whenLogic && !whenLogic(ctx))
2518
+ return undefined;
2519
+ const evalCtx = fieldContextRegistry.createReactiveEvaluationContext(ctx, functionRegistry.getCustomFunctions());
2520
+ return resolveHttpRequest(config.http, evalCtx) ?? undefined;
2521
+ },
2522
+ onSuccess: (response) => {
2523
+ return evaluateHttpValidationResponse(response, config.responseMapping, logger);
2524
+ },
2525
+ onError: (error) => {
2526
+ logger.warn('HTTP validator request failed:', error);
2527
+ return { kind: config.responseMapping.errorKind };
2528
+ },
2529
+ });
2530
+ }
2531
+ function applyValidators(configs, fieldPath) {
2532
+ configs.forEach((config) => applyValidator(config, fieldPath));
2533
+ }
2534
+
2535
+ function isCheckedField(field) {
2536
+ return field.type === 'checkbox' || field.type === 'toggle';
2537
+ }
2538
+
2539
+ function isValueField(field) {
2540
+ return 'value' in field || 'nullable' in field;
2541
+ }
2542
+
2543
+ function isArrayField(field) {
2544
+ return field.type === 'array' && 'fields' in field && Array.isArray(field.fields);
2545
+ }
2546
+ function isSimplifiedArrayField(field) {
2547
+ return field.type === 'array' && 'template' in field;
2548
+ }
2549
+
2550
+ function isContainerTypedField(field) {
2551
+ return (field.type === 'container' &&
2552
+ 'wrappers' in field &&
2553
+ 'fields' in field &&
2554
+ Array.isArray(field.wrappers) &&
2555
+ Array.isArray(field.fields));
2556
+ }
2557
+
2558
+ function isGroupField(field) {
2559
+ return field.type === 'group' && 'fields' in field;
2560
+ }
2561
+
2562
+ function isHiddenField(field) {
2563
+ return field.type === 'hidden';
2564
+ }
2565
+
2566
+ function isRowField(field) {
2567
+ return field.type === 'row' && 'fields' in field && Array.isArray(field.fields);
2568
+ }
2569
+
2570
+ function isPageField(field) {
2571
+ return field.type === 'page' && 'fields' in field && Array.isArray(field.fields);
2572
+ }
2573
+ function validatePageNesting(pageField) {
2574
+ return !hasNestedPages(pageField.fields);
2575
+ }
2576
+ function isContainerWithFields$1(field) {
2577
+ return ((isRowField(field) || isGroupField(field) || isContainerTypedField(field)) &&
2578
+ 'fields' in field &&
2579
+ Array.isArray(field.fields));
2580
+ }
2581
+ function hasNestedPages(fields) {
2582
+ for (const field of fields) {
2583
+ if (isPageField(field)) {
2584
+ return true;
2585
+ }
2586
+ if (isContainerWithFields$1(field)) {
2587
+ if (hasNestedPages(field.fields)) {
2588
+ return true;
2589
+ }
2590
+ }
2591
+ }
2592
+ return false;
2593
+ }
2594
+
2595
+ class ComponentInitializedEvent {
2596
+ componentType;
2597
+ componentId;
2598
+ type = 'component-initialized';
2599
+ constructor(componentType, componentId) {
2600
+ this.componentType = componentType;
2601
+ this.componentId = componentId;
2602
+ }
2603
+ }
2604
+
2605
+ const EMIT_FORM_VALUE_ON_EVENTS = new InjectionToken('EMIT_FORM_VALUE_ON_EVENTS', {
2606
+ providedIn: 'root',
2607
+ factory: () => false,
2608
+ });
2609
+
2610
+ class NoopLogger {
2611
+ debug() {
2612
+ }
2613
+ info() {
2614
+ }
2615
+ warn() {
2616
+ }
2617
+ error() {
2618
+ }
2619
+ }
2620
+
2621
+ function safeInjectToken(token, defaultValue) {
2622
+ try {
2623
+ return inject(token, { optional: true }) ?? defaultValue;
2624
+ }
2625
+ catch {
2626
+ return defaultValue;
2627
+ }
2628
+ }
2629
+ function safeInjectClass(token, defaultValue) {
2630
+ try {
2631
+ return inject(token, { optional: true }) ?? defaultValue;
2632
+ }
2633
+ catch {
2634
+ return defaultValue;
2635
+ }
2636
+ }
2637
+ function attachFormValue(event, formValue) {
2638
+ const prototype = Object.getPrototypeOf(event);
2639
+ return Object.assign(Object.create(prototype), event, { formValue });
2640
+ }
2641
+ const FALLBACK_LOGGER = new NoopLogger();
2642
+ class EventBus {
2643
+ pipeline$ = new Subject();
2644
+ globalEmitFormValue = safeInjectToken(EMIT_FORM_VALUE_ON_EVENTS, false);
2645
+ rootFormRegistry = safeInjectClass(RootFormRegistryService, null);
2646
+ formOptions = safeInjectToken(FORM_OPTIONS, null);
2647
+ logger = safeInjectToken(DynamicFormLogger, FALLBACK_LOGGER);
2648
+ events$ = this.pipeline$.asObservable();
2649
+ dispatch(eventOrConstructor, ...args) {
2650
+ if (typeof eventOrConstructor !== 'function' && 'type' in eventOrConstructor) {
2651
+ this.emit(eventOrConstructor);
2652
+ }
2653
+ else {
2654
+ this.emit(new eventOrConstructor(...args));
2655
+ }
2656
+ }
2657
+ emitInstance(event) {
2658
+ this.emit(event);
2659
+ }
2660
+ emit(event) {
2661
+ if (this.shouldEmitFormValue()) {
2662
+ const formValue = this.rootFormRegistry?.formValue();
2663
+ if (formValue && Object.keys(formValue).length > 0) {
2664
+ this.safeEmit(attachFormValue(event, formValue));
2665
+ return;
2666
+ }
2667
+ }
2668
+ this.safeEmit(event);
2669
+ }
2670
+ safeEmit(event) {
2671
+ try {
2672
+ this.pipeline$.next(event);
2673
+ }
2674
+ catch (err) {
2675
+ this.logger.error('Exception in EventBus subscriber during dispatch', err);
2676
+ }
2677
+ }
2678
+ shouldEmitFormValue() {
2679
+ const formLevelSetting = this.formOptions?.()?.emitFormValueOnEvents;
2680
+ return formLevelSetting ?? this.globalEmitFormValue;
2681
+ }
2682
+ on(eventType) {
2683
+ if (Array.isArray(eventType)) {
2684
+ return this.pipeline$.pipe(filter((event) => eventType.includes(event.type)));
2685
+ }
2686
+ return this.pipeline$.pipe(filter((event) => event.type === eventType));
2687
+ }
2688
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2689
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventBus });
2690
+ }
2691
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventBus, decorators: [{
2692
+ type: Injectable
2693
+ }] });
2694
+
2695
+ function hasFormValue(event) {
2696
+ return 'formValue' in event && event.formValue !== undefined;
2697
+ }
2698
+
2699
+ function resolveTokens(args, context) {
2700
+ return args.map((arg) => {
2701
+ if (typeof arg !== 'string') {
2702
+ return arg;
2703
+ }
2704
+ switch (arg) {
2705
+ case '$key':
2706
+ return context.key;
2707
+ case '$index':
2708
+ return context.index;
2709
+ case '$arrayKey':
2710
+ return context.arrayKey;
2711
+ case '$template':
2712
+ return context.template;
2713
+ case 'formValue':
2714
+ return context.formValue;
2715
+ default:
2716
+ return arg;
2717
+ }
2718
+ });
2719
+ }
2720
+
2721
+ function applyHiddenLogic(inputs, fieldDef, rootFormRegistry) {
2722
+ const rootForm = rootFormRegistry.rootForm();
2723
+ if (rootForm && (fieldDef.hidden === true || fieldDef.logic?.some((l) => l.type === 'hidden'))) {
2724
+ inputs['hidden'] = evaluateNonFieldHidden({
2725
+ form: rootForm,
2726
+ fieldLogic: fieldDef.logic,
2727
+ explicitValue: fieldDef.hidden,
2728
+ formValue: rootFormRegistry.formValue(),
2729
+ });
2730
+ }
2731
+ }
2732
+
2733
+ function getGridClassString(fieldDef) {
2734
+ const col = fieldDef.col;
2735
+ if (typeof col === 'number' && Number.isInteger(col) && col > 0 && col <= 12) {
2736
+ return `df-col-${col}`;
2737
+ }
2738
+ return '';
2739
+ }
2740
+ function buildClassName(fieldDef) {
2741
+ const gridClass = getGridClassString(fieldDef);
2742
+ const userClass = fieldDef.className;
2743
+ const classes = [];
2744
+ if (gridClass) {
2745
+ classes.push(gridClass);
2746
+ }
2747
+ if (userClass) {
2748
+ classes.push(userClass);
2749
+ }
2750
+ return classes.length > 0 ? classes.join(' ') : undefined;
2751
+ }
2752
+
2753
+ function arrayFieldMapper(fieldDef) {
2754
+ const rootFormRegistry = inject(RootFormRegistryService);
2755
+ const className = buildClassName(fieldDef);
2756
+ return computed(() => {
2757
+ const inputs = {
2758
+ key: fieldDef.key,
2759
+ field: fieldDef,
2760
+ ...(className !== undefined && { className }),
2761
+ };
2762
+ applyHiddenLogic(inputs, fieldDef, rootFormRegistry);
2763
+ return inputs;
2764
+ });
2765
+ }
2766
+
2767
+ function buildBaseInputs(fieldDef, defaultProps) {
2768
+ const { key, label, className, tabIndex, props, meta, addons } = fieldDef;
2769
+ const inputs = {};
2770
+ inputs['key'] = key;
2771
+ if (label !== undefined) {
2772
+ inputs['label'] = label;
2773
+ }
2774
+ const gridClassString = getGridClassString(fieldDef);
2775
+ const allClasses = [];
2776
+ if (gridClassString) {
2777
+ allClasses.push(gridClassString);
2778
+ }
2779
+ if (className) {
2780
+ allClasses.push(className);
2781
+ }
2782
+ if (allClasses.length > 0) {
2783
+ inputs['className'] = allClasses.join(' ');
2784
+ }
2785
+ if (tabIndex !== undefined) {
2786
+ inputs['tabIndex'] = tabIndex;
2787
+ }
2788
+ const fieldProps = props;
2789
+ const mergedProps = fieldProps !== undefined || defaultProps !== undefined ? { ...(defaultProps ?? {}), ...(fieldProps ?? {}) } : undefined;
2790
+ if (mergedProps !== undefined) {
2791
+ inputs['props'] = mergedProps;
2792
+ }
2793
+ if (meta !== undefined) {
2794
+ inputs['meta'] = meta;
2795
+ }
2796
+ if (addons !== undefined) {
2797
+ inputs['addons'] = addons;
2798
+ }
2799
+ return inputs;
2800
+ }
2801
+ function baseFieldMapper(fieldDef) {
2802
+ return computed(() => buildBaseInputs(fieldDef));
2803
+ }
2804
+
2805
+ function containerFieldMapper(fieldDef) {
2806
+ const rootFormRegistry = inject(RootFormRegistryService);
2807
+ const className = buildClassName(fieldDef);
2808
+ return computed(() => {
2809
+ const containerField = { ...fieldDef, wrappers: undefined, skipAutoWrappers: undefined, skipDefaultWrappers: undefined };
2810
+ const inputs = {
2811
+ key: fieldDef.key,
2812
+ field: containerField,
2813
+ ...(className !== undefined && { className }),
2814
+ };
2815
+ applyHiddenLogic(inputs, fieldDef, rootFormRegistry);
2816
+ return inputs;
2817
+ });
2818
+ }
2819
+
2820
+ function groupFieldMapper(fieldDef) {
2821
+ const rootFormRegistry = inject(RootFormRegistryService);
2822
+ const className = buildClassName(fieldDef);
2823
+ return computed(() => {
2824
+ const inputs = {
2825
+ key: fieldDef.key,
2826
+ field: fieldDef,
2827
+ ...(className !== undefined && { className }),
2828
+ };
2829
+ applyHiddenLogic(inputs, fieldDef, rootFormRegistry);
2830
+ return inputs;
2831
+ });
2832
+ }
2833
+
2834
+ const ROW_WRAPPERS = [{ type: 'row' }];
2835
+ function rowFieldMapper(fieldDef) {
2836
+ const rootFormRegistry = inject(RootFormRegistryService);
2837
+ const className = buildClassName(fieldDef);
2838
+ return computed(() => {
2839
+ const containerField = { ...fieldDef, type: 'container', wrappers: ROW_WRAPPERS };
2840
+ const inputs = {
2841
+ key: fieldDef.key,
2842
+ field: containerField,
2843
+ ...(className !== undefined && { className }),
2844
+ };
2845
+ applyHiddenLogic(inputs, fieldDef, rootFormRegistry);
2846
+ return inputs;
2847
+ });
2848
+ }
2849
+
2850
+ function pageFieldMapper(fieldDef) {
2851
+ const className = buildClassName(fieldDef);
2852
+ return computed(() => ({
2853
+ key: fieldDef.key,
2854
+ field: fieldDef,
2855
+ ...(className !== undefined && { className }),
2856
+ }));
2857
+ }
2858
+
2859
+ function textFieldMapper(fieldDef) {
2860
+ const rootFormRegistry = inject(RootFormRegistryService);
2861
+ const defaultProps = inject(DEFAULT_PROPS);
2862
+ return computed(() => {
2863
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
2864
+ const inputs = { ...baseInputs };
2865
+ applyHiddenLogic(inputs, fieldDef, rootFormRegistry);
2866
+ return inputs;
2867
+ });
2868
+ }
2869
+
2870
+ function isFieldBoundContext(ctx) {
2871
+ return ctx.form !== null;
2872
+ }
2873
+
2874
+ const ADDON_KIND_REGISTRY = new InjectionToken('ADDON_KIND_REGISTRY', {
2875
+ providedIn: 'root',
2876
+ factory: () => new Map(),
2877
+ });
2878
+
2879
+ const DF_FIELD_TEMPLATES = new InjectionToken('DF_FIELD_TEMPLATES');
2880
+
2881
+ const FIELD_REGISTRY = new InjectionToken('FIELD_REGISTRY', {
2882
+ providedIn: 'root',
2883
+ factory: () => new Map(),
2884
+ });
2885
+ function getFieldValueHandling(fieldType, registry) {
2886
+ const definition = registry.get(fieldType);
2887
+ return definition?.valueHandling ?? 'include';
2888
+ }
2889
+
2890
+ function isWrapperTypeDefinition(value) {
2891
+ return typeof value === 'object' && value !== null && 'wrapperName' in value;
2892
+ }
2893
+ const WRAPPER_REGISTRY = new InjectionToken('WRAPPER_REGISTRY', {
2894
+ providedIn: 'root',
2895
+ factory: () => new Map(),
2896
+ });
2897
+ const WRAPPER_COMPONENT_CACHE = new InjectionToken('WRAPPER_COMPONENT_CACHE', {
2898
+ providedIn: 'root',
2899
+ factory: () => new Map(),
2900
+ });
2901
+ const WRAPPER_AUTO_ASSOCIATIONS = new InjectionToken('WRAPPER_AUTO_ASSOCIATIONS', {
2902
+ providedIn: 'root',
2903
+ factory: () => new Map(),
2904
+ });
2905
+
2906
+ function isContainerWithFields(field) {
2907
+ return ((field.type === 'row' || field.type === 'group' || field.type === 'page' || field.type === 'container') &&
2908
+ 'fields' in field &&
2909
+ Array.isArray(field.fields));
2910
+ }
2911
+ function isPagedForm(fields) {
2912
+ if (!fields || fields.length === 0) {
2913
+ return false;
2914
+ }
2915
+ return fields.every((field) => isPageField(field));
2916
+ }
2917
+ function isNonPagedForm(fields) {
2918
+ if (!fields || fields.length === 0) {
2919
+ return true; }
2920
+ return !hasAnyPageFields(fields);
2921
+ }
2922
+ function detectFormMode(fields) {
2923
+ if (!fields || fields.length === 0) {
2924
+ return {
2925
+ mode: 'non-paged',
2926
+ isValid: true,
2927
+ errors: [],
2928
+ };
2929
+ }
2930
+ const hasPageFields = fields.some((field) => isPageField(field));
2931
+ const allPagesAtRoot = fields.every((field) => isPageField(field));
2932
+ const hasNestedPages = hasNestedPageFields(fields);
2933
+ if (hasPageFields) {
2934
+ const errors = [];
2935
+ if (!allPagesAtRoot) {
2936
+ errors.push('Mixed page and non-page fields at root level. In paged forms, ALL root-level fields must be of type "page".');
2937
+ }
2938
+ if (hasNestedPages) {
2939
+ errors.push('Page fields cannot contain nested page fields at any level.');
2940
+ }
2941
+ return {
2942
+ mode: 'paged',
2943
+ isValid: errors.length === 0,
2944
+ errors,
2945
+ };
2946
+ }
2947
+ else {
2948
+ const errors = [];
2949
+ if (hasAnyPageFields(fields)) {
2950
+ errors.push('Page fields are not allowed in non-paged forms.');
2951
+ }
2952
+ return {
2953
+ mode: 'non-paged',
2954
+ isValid: errors.length === 0,
2955
+ errors,
2956
+ };
2957
+ }
2958
+ }
2959
+ function hasAnyPageFields(fields) {
2960
+ for (const field of fields) {
2961
+ if (isPageField(field)) {
2962
+ return true;
2963
+ }
2964
+ if (!isPageField(field) && isContainerWithFields(field)) {
2965
+ if (hasAnyPageFields(field.fields)) {
2966
+ return true;
2967
+ }
2968
+ }
2969
+ }
2970
+ return false;
2971
+ }
2972
+ function hasNestedPageFields(fields) {
2973
+ for (const field of fields) {
2974
+ if (isPageField(field) && isContainerWithFields(field)) {
2975
+ if (hasAnyPageFields(field.fields)) {
2976
+ return true;
2977
+ }
2978
+ }
2979
+ if (!isPageField(field) && isContainerWithFields(field)) {
2980
+ if (hasNestedPageFields(field.fields)) {
2981
+ return true;
2982
+ }
2983
+ }
2984
+ }
2985
+ return false;
2986
+ }
2987
+ function isValidPagedForm(fields) {
2988
+ const result = detectFormMode(fields);
2989
+ return result.mode === 'paged' && result.isValid;
2990
+ }
2991
+ function isValidNonPagedForm(fields) {
2992
+ const result = detectFormMode(fields);
2993
+ return result.mode === 'non-paged' && result.isValid;
2994
+ }
2995
+
2996
+ function hasChildFields(field) {
2997
+ return 'fields' in field && field.fields != null;
2998
+ }
2999
+ const CONTAINER_TYPES = new Set(['page', 'row', 'group', 'array', 'container']);
3000
+ function isContainerField(field) {
3001
+ return CONTAINER_TYPES.has(field.type);
3002
+ }
3003
+ function isLeafField(field) {
3004
+ return !isContainerField(field);
3005
+ }
3006
+ function isValueBearingField(field) {
3007
+ return 'value' in field;
3008
+ }
3009
+ function isDisplayOnlyField(field) {
3010
+ return field.type === 'text' || isContainerField(field);
3011
+ }
3012
+
3013
+
3014
+ class DynamicTextPipe {
3015
+ injector = inject(Injector);
3016
+ transform(value) {
3017
+ if (isObservable(value)) {
3018
+ return value;
3019
+ }
3020
+ if (isSignal(value)) {
3021
+ return toObservable(value, { injector: this.injector });
3022
+ }
3023
+ return of(value || '');
3024
+ }
3025
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicTextPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3026
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: DynamicTextPipe, isStandalone: true, name: "dynamicText" });
3027
+ }
3028
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicTextPipe, decorators: [{
3029
+ type: Pipe,
3030
+ args: [{
3031
+ name: 'dynamicText',
3032
+ }]
3033
+ }] });
3034
+
3035
+ function applyMetaToElement(element, meta, previouslyApplied) {
3036
+ const newApplied = new Set();
3037
+ for (const attr of previouslyApplied) {
3038
+ if (!meta || !(attr in meta) || meta[attr] === undefined) {
3039
+ element.removeAttribute(attr);
3040
+ }
3041
+ }
3042
+ if (!meta)
3043
+ return newApplied;
3044
+ for (const [key, value] of Object.entries(meta)) {
3045
+ if (value === undefined || value === null)
3046
+ continue;
3047
+ element.setAttribute(key, String(value));
3048
+ newApplied.add(key);
3049
+ }
3050
+ return newApplied;
3051
+ }
3052
+
3053
+ function dynamicTextToObservable(value, injector) {
3054
+ if (value === undefined) {
3055
+ return of('');
3056
+ }
3057
+ if (isObservable(value)) {
3058
+ return value;
3059
+ }
3060
+ if (isSignal(value)) {
3061
+ return toObservable(value, { injector });
3062
+ }
3063
+ return of(String(value));
3064
+ }
3065
+
3066
+ function resolveDynamicValue(value, fallback, injector) {
3067
+ if (value === undefined) {
3068
+ return computed(() => fallback);
3069
+ }
3070
+ if (isSignal(value)) {
3071
+ return value;
3072
+ }
3073
+ if (isObservable(value)) {
3074
+ return toSignal(value, { initialValue: fallback, injector });
3075
+ }
3076
+ return computed(() => value);
3077
+ }
3078
+
3079
+ const INITIALIZATION_TIMEOUT_MS = new InjectionToken('INITIALIZATION_TIMEOUT_MS', {
3080
+ providedIn: 'root',
3081
+ factory: () => 10_000,
3082
+ });
3083
+ function createInitializationTracker(eventBus, expectedCount) {
3084
+ return eventBus.on('component-initialized').pipe(scan((count) => count + 1, 0), map$1((currentCount) => currentCount >= expectedCount), filter$1((isComplete) => isComplete));
3085
+ }
3086
+ function setupInitializationTracking(options) {
3087
+ const { eventBus, totalComponentsCount, injector, componentId } = options;
3088
+ const timeoutMs = injector.get(INITIALIZATION_TIMEOUT_MS);
3089
+ const logger = injector.get(DynamicFormLogger);
3090
+ return toObservable(totalComponentsCount, { injector }).pipe(take(1), switchMap((count) => {
3091
+ const tracking$ = count === 1
3092
+ ? eventBus.on('component-initialized').pipe(filter$1((event) => event.componentType === 'dynamic-form' && event.componentId === componentId), map$1(() => true), take(1))
3093
+ : createInitializationTracker(eventBus, count);
3094
+ return tracking$.pipe(timeout(timeoutMs), catchError$1((error) => {
3095
+ if (error instanceof TimeoutError) {
3096
+ logger.warn(`Initialization timed out after ${timeoutMs}ms. ` +
3097
+ `Expected ${count} component(s) to initialize but not all reported in time. ` +
3098
+ 'This may indicate a container component threw during initialization. ' +
3099
+ 'Emitting (initialized) as best-effort.');
3100
+ return of(true);
3101
+ }
3102
+ return throwError(() => error);
3103
+ }));
3104
+ }), shareReplay({ bufferSize: 1, refCount: false }));
3105
+ }
3106
+
3107
+ function interpolateParams(message, error) {
3108
+ let result = message;
3109
+ const params = extractErrorParams(error);
3110
+ Object.entries(params).forEach(([key, value]) => {
3111
+ const placeholder = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
3112
+ result = result.replace(placeholder, safeToString(value));
3113
+ });
3114
+ return result;
3115
+ }
3116
+ function safeToString(value) {
3117
+ if (value === null || value === undefined) {
3118
+ return '';
3119
+ }
3120
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
3121
+ return String(value);
3122
+ }
3123
+ if (value instanceof RegExp) {
3124
+ return value.toString();
3125
+ }
3126
+ if (value instanceof Date) {
3127
+ return value.toISOString();
3128
+ }
3129
+ if (Array.isArray(value)) {
3130
+ return value.join(', ');
3131
+ }
3132
+ try {
3133
+ if (typeof value === 'object' && value.toString !== Object.prototype.toString) {
3134
+ return value.toString();
3135
+ }
3136
+ return JSON.stringify(value);
3137
+ }
3138
+ catch {
3139
+ return '[object]';
3140
+ }
3141
+ }
3142
+ function extractErrorParams(error) {
3143
+ const params = {};
3144
+ if ('min' in error)
3145
+ params.min = error.min;
3146
+ if ('max' in error)
3147
+ params.max = error.max;
3148
+ if ('pattern' in error)
3149
+ params.pattern = error.pattern;
3150
+ if ('actual' in error)
3151
+ params.actual = error.actual;
3152
+ if ('expected' in error)
3153
+ params.expected = error.expected;
3154
+ if ('requiredLength' in error) {
3155
+ params.requiredLength = error.requiredLength;
3156
+ }
3157
+ else if ('minLength' in error) {
3158
+ params.requiredLength = error.minLength;
3159
+ params.minLength = error.minLength;
3160
+ }
3161
+ else if ('maxLength' in error) {
3162
+ params.requiredLength = error.maxLength;
3163
+ params.maxLength = error.maxLength;
3164
+ }
3165
+ if ('actualLength' in error)
3166
+ params.actualLength = error.actualLength;
3167
+ Object.keys(error).forEach((key) => {
3168
+ if (key !== 'kind' && !(key in params)) {
3169
+ params[key] = error[key];
3170
+ }
3171
+ });
3172
+ return params;
3173
+ }
3174
+
3175
+ function isEqual(a, b) {
3176
+ return isEqualInternal(a, b, new WeakMap(), new WeakMap());
3177
+ }
3178
+ function isEqualInternal(a, b, seenA, seenB) {
3179
+ if (a === b)
3180
+ return true;
3181
+ if (typeof a === 'number' && typeof b === 'number') {
3182
+ return Object.is(a, b);
3183
+ }
3184
+ if (a == null || b == null)
3185
+ return false;
3186
+ if (typeof a !== typeof b)
3187
+ return false;
3188
+ if (typeof a !== 'object') {
3189
+ return Object.is(a, b);
3190
+ }
3191
+ const objA = a;
3192
+ const objB = b;
3193
+ if (seenA.has(objA) || seenB.has(objB)) {
3194
+ return seenA.get(objA) === objB && seenB.get(objB) === objA;
3195
+ }
3196
+ seenA.set(objA, objB);
3197
+ seenB.set(objB, objA);
3198
+ if (objA.constructor !== objB.constructor)
3199
+ return false;
3200
+ if (a instanceof Date && b instanceof Date) {
3201
+ return a.getTime() === b.getTime();
3202
+ }
3203
+ if (a instanceof RegExp && b instanceof RegExp) {
3204
+ return a.source === b.source && a.flags === b.flags;
3205
+ }
3206
+ if (a instanceof Map && b instanceof Map) {
3207
+ if (a.size !== b.size)
3208
+ return false;
3209
+ for (const [key, value] of a) {
3210
+ if (!b.has(key) || !isEqualInternal(value, b.get(key), seenA, seenB)) {
3211
+ return false;
3212
+ }
3213
+ }
3214
+ return true;
3215
+ }
3216
+ if (a instanceof Set && b instanceof Set) {
3217
+ if (a.size !== b.size)
3218
+ return false;
3219
+ for (const value of a) {
3220
+ if (!b.has(value)) {
3221
+ if (typeof value === 'object' && value !== null) {
3222
+ let found = false;
3223
+ for (const bValue of b) {
3224
+ if (typeof bValue === 'object' && bValue !== null && isEqualInternal(value, bValue, seenA, seenB)) {
3225
+ found = true;
3226
+ break;
3227
+ }
3228
+ }
3229
+ if (!found)
3230
+ return false;
3231
+ }
3232
+ else {
3233
+ return false;
3234
+ }
3235
+ }
3236
+ }
3237
+ return true;
3238
+ }
3239
+ if (Array.isArray(a) && Array.isArray(b)) {
3240
+ if (a.length !== b.length)
3241
+ return false;
3242
+ return a.every((item, index) => isEqualInternal(item, b[index], seenA, seenB));
3243
+ }
3244
+ const recordA = a;
3245
+ const recordB = b;
3246
+ const keysA = [...Object.keys(recordA), ...Object.getOwnPropertySymbols(recordA)];
3247
+ const keysB = [...Object.keys(recordB), ...Object.getOwnPropertySymbols(recordB)];
3248
+ if (keysA.length !== keysB.length)
3249
+ return false;
3250
+ const keySetB = new Set(keysB);
3251
+ return keysA.every((key) => keySetB.has(key) && isEqualInternal(recordA[key], recordB[key], seenA, seenB));
3252
+ }
3253
+ function omit(obj, keys) {
3254
+ const result = { ...obj };
3255
+ keys.forEach((key) => delete result[key]);
3256
+ return result;
3257
+ }
3258
+ function keyBy(array, key) {
3259
+ return array.reduce((acc, item) => {
3260
+ acc[String(item[key])] = item;
3261
+ return acc;
3262
+ }, {});
3263
+ }
3264
+ function mapValues(obj, fn) {
3265
+ return Object.entries(obj).reduce((acc, [key, value]) => {
3266
+ acc[key] = fn(value, key);
3267
+ return acc;
3268
+ }, {});
3269
+ }
3270
+ function deepMergeDefaults(defaults, value) {
3271
+ if (value == null)
3272
+ return { ...defaults };
3273
+ const result = { ...defaults };
3274
+ for (const key of Object.keys(value)) {
3275
+ if (key === '__proto__' || key === 'constructor' || key === 'prototype')
3276
+ continue;
3277
+ const incoming = value[key];
3278
+ const existing = result[key];
3279
+ if (isPlainObject(existing) && isPlainObject(incoming)) {
3280
+ result[key] = deepMergeDefaults(existing, incoming);
3281
+ }
3282
+ else if (Array.isArray(existing) && Array.isArray(incoming)) {
3283
+ result[key] = mergeArrayDefaults(existing, incoming);
3284
+ }
3285
+ else {
3286
+ result[key] = incoming;
3287
+ }
3288
+ }
3289
+ return result;
3290
+ }
3291
+ function mergeArrayDefaults(defaults, value) {
3292
+ if (defaults.length !== value.length)
3293
+ return value;
3294
+ if (!value.some((item, i) => isPlainObject(defaults[i]) && isPlainObject(item)))
3295
+ return value;
3296
+ return value.map((item, i) => {
3297
+ const defaultItem = defaults[i];
3298
+ if (isPlainObject(defaultItem) && isPlainObject(item)) {
3299
+ return deepMergeDefaults(defaultItem, item);
3300
+ }
3301
+ return item;
3302
+ });
3303
+ }
3304
+ function isPlainObject(value) {
3305
+ if (typeof value !== 'object' || value === null)
3306
+ return false;
3307
+ const proto = Object.getPrototypeOf(value);
3308
+ return proto === null || proto === Object.prototype;
3309
+ }
3310
+ const DEFAULT_MEMOIZE_MAX_SIZE = 100;
3311
+ function memoize(fn, resolverOrOptions) {
3312
+ const options = typeof resolverOrOptions === 'function' ? { resolver: resolverOrOptions } : (resolverOrOptions ?? {});
3313
+ const { resolver, maxSize = DEFAULT_MEMOIZE_MAX_SIZE } = options;
3314
+ const cache = new Map();
3315
+ return ((...args) => {
3316
+ const key = resolver ? resolver(...args) : JSON.stringify(args);
3317
+ const cachedValue = cache.get(key);
3318
+ if (cachedValue !== undefined) {
3319
+ cache.delete(key);
3320
+ cache.set(key, cachedValue);
3321
+ return cachedValue;
3322
+ }
3323
+ const result = fn(...args);
3324
+ if (cache.size >= maxSize) {
3325
+ const oldestKey = cache.keys().next().value;
3326
+ if (oldestKey !== undefined) {
3327
+ cache.delete(oldestKey);
3328
+ }
3329
+ }
3330
+ cache.set(key, result);
3331
+ return result;
3332
+ });
3333
+ }
3334
+ function normalizeFieldsArray(fields) {
3335
+ return Array.isArray(fields) ? [...fields] : Object.values(fields);
3336
+ }
3337
+ function getChangedKeys(previous, current) {
3338
+ const changedKeys = new Set();
3339
+ if (!previous && !current)
3340
+ return changedKeys;
3341
+ if (!previous) {
3342
+ return new Set(Object.keys(current ?? {}));
3343
+ }
3344
+ if (!current) {
3345
+ return new Set(Object.keys(previous));
3346
+ }
3347
+ const allKeys = new Set([...Object.keys(previous), ...Object.keys(current)]);
3348
+ for (const key of allKeys) {
3349
+ const prevValue = previous[key];
3350
+ const currValue = current[key];
3351
+ if (!(key in previous) || !(key in current)) {
3352
+ changedKeys.add(key);
3353
+ continue;
3354
+ }
3355
+ if (!isEqual(prevValue, currValue)) {
3356
+ changedKeys.add(key);
3357
+ }
3358
+ }
3359
+ return changedKeys;
3360
+ }
3361
+ function assertNever(value) {
3362
+ throw new DynamicFormError(`Unexpected value: ${JSON.stringify(value)}`);
3363
+ }
3364
+
3365
+ function createWarningTracker() {
3366
+ return { warnedKeys: new Set() };
3367
+ }
3368
+
3369
+
3370
+
3371
+ export { ADDON_KIND_REGISTRY, ARRAY_CONTEXT, AsyncConditionFunctionCacheService, ComponentInitializedEvent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DEPRECATION_WARNING_TRACKER, DF_FIELD_TEMPLATES, DynamicFormError, DynamicFormLogger, DynamicTextPipe, DynamicValueFunctionCacheService, EMIT_FORM_VALUE_ON_EVENTS, EXTERNAL_DATA, Evaluator, EventBus, ExpressionParser, ExpressionParserError, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_ID_PREFIX, FORM_OPTIONS, FieldContextRegistryService, FunctionRegistryService, GROUP_CONTEXT, HTTP_CONDITION_CACHE, HttpConditionCache, HttpConditionFunctionCacheService, INITIALIZATION_TIMEOUT_MS, LogicFunctionCacheService, NoopLogger, Parser, READONLY_FIELD_TREE_CACHE, RootFormRegistryService, TokenType, Tokenizer, WRAPPER_AUTO_ASSOCIATIONS, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, applyHiddenLogic, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, assertNever, baseFieldMapper, buildBaseInputs, buildClassName, compareValues, containerFieldMapper, createAsyncConditionLogicFunction, createDebouncedLogicFunction, createDebouncedResourceLogicFn, createDefaultDerivationLogConfig, createDynamicValueFunction, createFormFieldStateMap, createHttpConditionLogicFunction, createInitializationTracker, createLogicFunction, createWarningTracker, deepMergeDefaults, derivedFromDeferred, detectFormMode, dynamicTextToObservable, evaluateCondition, evaluateHttpValidationResponse, evaluateNonFieldDisabled, evaluateNonFieldHidden, extractExpressionDependencies, extractStringDependencies, getArrayLength, getChangedKeys, getFieldValueHandling, getGridClassString, getNestedValue, groupFieldMapper, hasChildFields, hasCrossFieldWhenCondition, hasFormValue, hasNestedProperty, hasTargetProperty, injectFieldSignalContext, interpolateParams, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isCrossFieldBuiltInValidator, isCrossFieldExpression, isCrossFieldSchema, isCrossFieldStateLogic, isCrossFieldValidator, isDerivationLogicConfig, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isFunctionHttpValidator, isGroupField, isHiddenField, isLeafField, isNonPagedForm, isPageField, isPagedForm, isResourceBasedValidator, isRowField, isSimplifiedArrayField, isStateLogicConfig, isValidNonPagedForm, isValidPagedForm, isValueBearingField, isValueField, isWrapperTypeDefinition, keyBy, mapValues, memoize, normalizeFieldsArray, omit, pageFieldMapper, readFieldStateInfo, resolveDynamicValue, resolveHttpRequest, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, safeReadPathKeys, setupInitializationTracking, shouldLog, stableStringify, textFieldMapper, toReadonlyFieldTree, toReadonlyFieldTreeCached, validatePageNesting, withPreviousValue, writeToFieldValue };
3372
+ //# sourceMappingURL=ng-forge-dynamic-forms-internal.mjs.map