@ng-forge/dynamic-forms 0.9.0 → 0.10.0-next.1
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.
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-Tw49K5kz.mjs → ng-forge-dynamic-forms-array-field.component-GiaVHO9N.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-Tw49K5kz.mjs.map → ng-forge-dynamic-forms-array-field.component-GiaVHO9N.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-CrOIZEL_.mjs → ng-forge-dynamic-forms-container-field.component-C554hOxN.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-CrOIZEL_.mjs.map → ng-forge-dynamic-forms-container-field.component-C554hOxN.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-Bx1A5tg-.mjs → ng-forge-dynamic-forms-container-utils-BS4d0cCk.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-Bx1A5tg-.mjs.map → ng-forge-dynamic-forms-container-utils-BS4d0cCk.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-Q2g56KUM.mjs → ng-forge-dynamic-forms-css-wrapper.component-CnJgFWLB.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-Q2g56KUM.mjs.map → ng-forge-dynamic-forms-css-wrapper.component-CnJgFWLB.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-9l5gPNQc.mjs → ng-forge-dynamic-forms-emit-initialization-3PyXVwsn.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-9l5gPNQc.mjs.map → ng-forge-dynamic-forms-emit-initialization-3PyXVwsn.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-DqnhddRH.mjs → ng-forge-dynamic-forms-group-field.component-BJ8j7Sh0.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-DqnhddRH.mjs.map → ng-forge-dynamic-forms-group-field.component-BJ8j7Sh0.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-Be1Lk0qR.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms--PuL0AKc.mjs} +795 -193
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms--PuL0AKc.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-n4KTMOTm.mjs → ng-forge-dynamic-forms-page-field.component-Bz2RpleR.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-n4KTMOTm.mjs.map → ng-forge-dynamic-forms-page-field.component-Bz2RpleR.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C14mlROS.mjs → ng-forge-dynamic-forms-text-field.component-BGw6T-4h.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C14mlROS.mjs.map → ng-forge-dynamic-forms-text-field.component-BGw6T-4h.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +2 -2
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-Be1Lk0qR.mjs.map +0 -1
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, reflectComponentType, Injector, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, Injectable, isSignal, untracked, linkedSignal, ChangeDetectionStrategy, Component, resourceFromSnapshots, runInInjectionContext, afterNextRender, isWritableSignal, TemplateRef, model, contentChildren, makeEnvironmentProviders, Pipe } from '@angular/core';
|
|
3
3
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
4
4
|
import { takeUntilDestroyed, toObservable, toSignal, rxResource, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
5
|
-
import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, Subject, filter, TimeoutError, throwError, defer, combineLatest, pipe, debounceTime, distinctUntilChanged, tap, startWith, scan as scan$1, Observable, concatMap, take as take$1,
|
|
5
|
+
import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, Subject, filter, TimeoutError, throwError, defer, combineLatest, pipe, debounceTime, distinctUntilChanged, tap, startWith, scan as scan$1, Observable, concatMap, take as take$1, merge, pairwise, combineLatestWith, auditTime, scheduled, queueScheduler, timer } from 'rxjs';
|
|
6
6
|
import { submit, email, min, max, minLength, maxLength, pattern, required, validate, validateAsync, validateHttp, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, schema, validateStandardSchema, validateTree, form, provideSignalFormsConfig } from '@angular/forms/signals';
|
|
7
7
|
import { scan, map as map$1, filter as filter$1, take, switchMap as switchMap$1, timeout, catchError as catchError$1, shareReplay } from 'rxjs/operators';
|
|
8
8
|
import { HttpClient } from '@angular/common/http';
|
|
@@ -2010,10 +2010,16 @@ var TokenType;
|
|
|
2010
2010
|
// Punctuation
|
|
2011
2011
|
TokenType["DOT"] = "DOT";
|
|
2012
2012
|
TokenType["COMMA"] = "COMMA";
|
|
2013
|
+
TokenType["COLON"] = "COLON";
|
|
2013
2014
|
TokenType["LPAREN"] = "LPAREN";
|
|
2014
2015
|
TokenType["RPAREN"] = "RPAREN";
|
|
2015
2016
|
TokenType["LBRACKET"] = "LBRACKET";
|
|
2016
2017
|
TokenType["RBRACKET"] = "RBRACKET";
|
|
2018
|
+
TokenType["LBRACE"] = "LBRACE";
|
|
2019
|
+
TokenType["RBRACE"] = "RBRACE";
|
|
2020
|
+
TokenType["ARROW"] = "ARROW";
|
|
2021
|
+
TokenType["QUESTION"] = "QUESTION";
|
|
2022
|
+
TokenType["OPTIONAL_DOT"] = "OPTIONAL_DOT";
|
|
2017
2023
|
// Special
|
|
2018
2024
|
TokenType["EOF"] = "EOF";
|
|
2019
2025
|
})(TokenType || (TokenType = {}));
|
|
@@ -2193,6 +2199,12 @@ class Tokenizer {
|
|
|
2193
2199
|
case '||':
|
|
2194
2200
|
this.position += 2;
|
|
2195
2201
|
return { type: TokenType.OR, value: '||', position: start };
|
|
2202
|
+
case '=>':
|
|
2203
|
+
this.position += 2;
|
|
2204
|
+
return { type: TokenType.ARROW, value: '=>', position: start };
|
|
2205
|
+
case '?.':
|
|
2206
|
+
this.position += 2;
|
|
2207
|
+
return { type: TokenType.OPTIONAL_DOT, value: '?.', position: start };
|
|
2196
2208
|
}
|
|
2197
2209
|
// Single-character operators and punctuation
|
|
2198
2210
|
this.position++;
|
|
@@ -2217,6 +2229,8 @@ class Tokenizer {
|
|
|
2217
2229
|
return { type: TokenType.DOT, value: '.', position: start };
|
|
2218
2230
|
case ',':
|
|
2219
2231
|
return { type: TokenType.COMMA, value: ',', position: start };
|
|
2232
|
+
case ':':
|
|
2233
|
+
return { type: TokenType.COLON, value: ':', position: start };
|
|
2220
2234
|
case '(':
|
|
2221
2235
|
return { type: TokenType.LPAREN, value: '(', position: start };
|
|
2222
2236
|
case ')':
|
|
@@ -2225,6 +2239,12 @@ class Tokenizer {
|
|
|
2225
2239
|
return { type: TokenType.LBRACKET, value: '[', position: start };
|
|
2226
2240
|
case ']':
|
|
2227
2241
|
return { type: TokenType.RBRACKET, value: ']', position: start };
|
|
2242
|
+
case '{':
|
|
2243
|
+
return { type: TokenType.LBRACE, value: '{', position: start };
|
|
2244
|
+
case '}':
|
|
2245
|
+
return { type: TokenType.RBRACE, value: '}', position: start };
|
|
2246
|
+
case '?':
|
|
2247
|
+
return { type: TokenType.QUESTION, value: '?', position: start };
|
|
2228
2248
|
default:
|
|
2229
2249
|
throw new ExpressionParserError(`Unexpected character: ${char}`, start, this.expression);
|
|
2230
2250
|
}
|
|
@@ -2260,7 +2280,88 @@ class Parser {
|
|
|
2260
2280
|
return result;
|
|
2261
2281
|
}
|
|
2262
2282
|
parseExpression() {
|
|
2263
|
-
|
|
2283
|
+
// Try arrow function first — it has the lowest precedence and would otherwise
|
|
2284
|
+
// be ambiguous with grouped expressions / bare identifiers.
|
|
2285
|
+
const arrow = this.tryParseArrowFunction();
|
|
2286
|
+
if (arrow)
|
|
2287
|
+
return arrow;
|
|
2288
|
+
return this.parseConditional();
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Ternary `test ? consequent : alternate` — right-associative, sits between
|
|
2292
|
+
* the arrow-function check and logical-or in precedence.
|
|
2293
|
+
*/
|
|
2294
|
+
parseConditional() {
|
|
2295
|
+
const test = this.parseLogicalOr();
|
|
2296
|
+
if (!this.match(TokenType.QUESTION))
|
|
2297
|
+
return test;
|
|
2298
|
+
const consequent = this.parseExpression();
|
|
2299
|
+
this.consume(TokenType.COLON, 'Expected ":" between ternary branches');
|
|
2300
|
+
const alternate = this.parseExpression();
|
|
2301
|
+
return { type: 'Conditional', test, consequent, alternate };
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Attempts to parse an arrow function. Returns null and leaves position
|
|
2305
|
+
* unchanged if the next tokens don't form an arrow function head.
|
|
2306
|
+
*
|
|
2307
|
+
* Supported shapes:
|
|
2308
|
+
* x => body
|
|
2309
|
+
* () => body
|
|
2310
|
+
* (x) => body
|
|
2311
|
+
* (x, y, ...) => body
|
|
2312
|
+
*
|
|
2313
|
+
* Body returning an object literal must be wrapped in parens: `d => ({...})`.
|
|
2314
|
+
*/
|
|
2315
|
+
tryParseArrowFunction() {
|
|
2316
|
+
const savedPos = this.current;
|
|
2317
|
+
// Single unparenthesized param: IDENTIFIER =>
|
|
2318
|
+
if (this.check(TokenType.IDENTIFIER) && this.checkAt(this.current + 1, TokenType.ARROW)) {
|
|
2319
|
+
const param = this.advance().value;
|
|
2320
|
+
this.advance(); // consume =>
|
|
2321
|
+
const body = this.parseExpression();
|
|
2322
|
+
return { type: 'ArrowFunction', params: [param], body };
|
|
2323
|
+
}
|
|
2324
|
+
// Parenthesized params: ( ) => or ( id (, id)* ) =>
|
|
2325
|
+
if (this.check(TokenType.LPAREN)) {
|
|
2326
|
+
this.advance(); // tentatively consume (
|
|
2327
|
+
const params = [];
|
|
2328
|
+
if (this.check(TokenType.RPAREN)) {
|
|
2329
|
+
this.advance(); // )
|
|
2330
|
+
}
|
|
2331
|
+
else if (this.check(TokenType.IDENTIFIER)) {
|
|
2332
|
+
params.push(this.advance().value);
|
|
2333
|
+
while (this.check(TokenType.COMMA)) {
|
|
2334
|
+
this.advance(); // ,
|
|
2335
|
+
if (!this.check(TokenType.IDENTIFIER)) {
|
|
2336
|
+
this.current = savedPos;
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
params.push(this.advance().value);
|
|
2340
|
+
}
|
|
2341
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
2342
|
+
this.current = savedPos;
|
|
2343
|
+
return null;
|
|
2344
|
+
}
|
|
2345
|
+
this.advance(); // )
|
|
2346
|
+
}
|
|
2347
|
+
else {
|
|
2348
|
+
this.current = savedPos;
|
|
2349
|
+
return null;
|
|
2350
|
+
}
|
|
2351
|
+
if (!this.check(TokenType.ARROW)) {
|
|
2352
|
+
this.current = savedPos;
|
|
2353
|
+
return null;
|
|
2354
|
+
}
|
|
2355
|
+
this.advance(); // =>
|
|
2356
|
+
const body = this.parseExpression();
|
|
2357
|
+
return { type: 'ArrowFunction', params, body };
|
|
2358
|
+
}
|
|
2359
|
+
return null;
|
|
2360
|
+
}
|
|
2361
|
+
checkAt(index, type) {
|
|
2362
|
+
if (index < 0 || index >= this.tokens.length)
|
|
2363
|
+
return false;
|
|
2364
|
+
return this.tokens[index].type === type;
|
|
2264
2365
|
}
|
|
2265
2366
|
parseLogicalOr() {
|
|
2266
2367
|
let node = this.parseLogicalAnd();
|
|
@@ -2335,6 +2436,32 @@ class Parser {
|
|
|
2335
2436
|
const property = this.advance().value;
|
|
2336
2437
|
node = { type: 'MemberAccess', object: node, property };
|
|
2337
2438
|
}
|
|
2439
|
+
else if (this.match(TokenType.OPTIONAL_DOT)) {
|
|
2440
|
+
// Optional chain — supports `?.prop`, `?.[expr]`, and `?.(args)`.
|
|
2441
|
+
if (this.match(TokenType.LBRACKET)) {
|
|
2442
|
+
const property = this.parseExpression();
|
|
2443
|
+
this.consume(TokenType.RBRACKET, 'Expected "]" after computed property');
|
|
2444
|
+
node = { type: 'ComputedMemberAccess', object: node, property, optional: true };
|
|
2445
|
+
}
|
|
2446
|
+
else if (this.match(TokenType.LPAREN)) {
|
|
2447
|
+
const args = this.parseArgumentList();
|
|
2448
|
+
this.consume(TokenType.RPAREN, 'Expected ")" after arguments');
|
|
2449
|
+
node = { type: 'CallExpression', callee: node, arguments: args, optional: true };
|
|
2450
|
+
}
|
|
2451
|
+
else if (this.check(TokenType.IDENTIFIER)) {
|
|
2452
|
+
const property = this.advance().value;
|
|
2453
|
+
node = { type: 'MemberAccess', object: node, property, optional: true };
|
|
2454
|
+
}
|
|
2455
|
+
else {
|
|
2456
|
+
throw new ExpressionParserError('Expected property name, "[", or "(" after "?."', this.peek().position, this.expression);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
else if (this.match(TokenType.LBRACKET)) {
|
|
2460
|
+
// Computed member access: obj[expr]
|
|
2461
|
+
const property = this.parseExpression();
|
|
2462
|
+
this.consume(TokenType.RBRACKET, 'Expected "]" after computed property');
|
|
2463
|
+
node = { type: 'ComputedMemberAccess', object: node, property };
|
|
2464
|
+
}
|
|
2338
2465
|
else if (this.match(TokenType.LPAREN)) {
|
|
2339
2466
|
// Function call: func()
|
|
2340
2467
|
const args = this.parseArgumentList();
|
|
@@ -2373,6 +2500,12 @@ class Parser {
|
|
|
2373
2500
|
this.consume(TokenType.RBRACKET, 'Expected "]" after array elements');
|
|
2374
2501
|
return { type: 'ArrayLiteral', elements };
|
|
2375
2502
|
}
|
|
2503
|
+
// Object literal
|
|
2504
|
+
if (this.match(TokenType.LBRACE)) {
|
|
2505
|
+
const properties = this.parseObjectProperties();
|
|
2506
|
+
this.consume(TokenType.RBRACE, 'Expected "}" after object properties');
|
|
2507
|
+
return { type: 'ObjectLiteral', properties };
|
|
2508
|
+
}
|
|
2376
2509
|
// Identifier
|
|
2377
2510
|
if (this.match(TokenType.IDENTIFIER)) {
|
|
2378
2511
|
return { type: 'Identifier', name: this.previous().value };
|
|
@@ -2403,6 +2536,28 @@ class Parser {
|
|
|
2403
2536
|
}
|
|
2404
2537
|
return elements;
|
|
2405
2538
|
}
|
|
2539
|
+
parseObjectProperties() {
|
|
2540
|
+
const properties = [];
|
|
2541
|
+
if (this.check(TokenType.RBRACE)) {
|
|
2542
|
+
return properties;
|
|
2543
|
+
}
|
|
2544
|
+
do {
|
|
2545
|
+
// Trailing comma support: `{ a: 1, }`
|
|
2546
|
+
if (this.check(TokenType.RBRACE))
|
|
2547
|
+
break;
|
|
2548
|
+
let key;
|
|
2549
|
+
if (this.check(TokenType.IDENTIFIER) || this.check(TokenType.STRING)) {
|
|
2550
|
+
key = this.advance().value;
|
|
2551
|
+
}
|
|
2552
|
+
else {
|
|
2553
|
+
throw new ExpressionParserError(`Expected property name (identifier or string) in object literal, got '${this.peek().value}'`, this.peek().position, this.expression);
|
|
2554
|
+
}
|
|
2555
|
+
this.consume(TokenType.COLON, 'Expected ":" after property name in object literal');
|
|
2556
|
+
const value = this.parseExpression();
|
|
2557
|
+
properties.push({ key, value });
|
|
2558
|
+
} while (this.match(TokenType.COMMA));
|
|
2559
|
+
return properties;
|
|
2560
|
+
}
|
|
2406
2561
|
match(...types) {
|
|
2407
2562
|
for (const type of types) {
|
|
2408
2563
|
if (this.check(type)) {
|
|
@@ -2577,14 +2732,22 @@ class Evaluator {
|
|
|
2577
2732
|
return this.evaluateIdentifier(node.name);
|
|
2578
2733
|
case 'MemberAccess':
|
|
2579
2734
|
return this.evaluateMemberAccess(node);
|
|
2735
|
+
case 'ComputedMemberAccess':
|
|
2736
|
+
return this.evaluateComputedMemberAccess(node);
|
|
2580
2737
|
case 'BinaryOp':
|
|
2581
2738
|
return this.evaluateBinaryOp(node);
|
|
2582
2739
|
case 'UnaryOp':
|
|
2583
2740
|
return this.evaluateUnaryOp(node);
|
|
2584
2741
|
case 'CallExpression':
|
|
2585
2742
|
return this.evaluateCallExpression(node);
|
|
2743
|
+
case 'Conditional':
|
|
2744
|
+
return this.evaluate(node.test) ? this.evaluate(node.consequent) : this.evaluate(node.alternate);
|
|
2586
2745
|
case 'ArrayLiteral':
|
|
2587
2746
|
return this.evaluateArrayLiteral(node);
|
|
2747
|
+
case 'ObjectLiteral':
|
|
2748
|
+
return this.evaluateObjectLiteral(node);
|
|
2749
|
+
case 'ArrowFunction':
|
|
2750
|
+
return this.evaluateArrowFunction(node);
|
|
2588
2751
|
default:
|
|
2589
2752
|
throw new ExpressionParserError(`Unknown node type: ${node.type}`, 0, this.expression);
|
|
2590
2753
|
}
|
|
@@ -2610,6 +2773,27 @@ class Evaluator {
|
|
|
2610
2773
|
}
|
|
2611
2774
|
return undefined;
|
|
2612
2775
|
}
|
|
2776
|
+
evaluateComputedMemberAccess(node) {
|
|
2777
|
+
const obj = this.evaluate(node.object);
|
|
2778
|
+
if (obj === null || obj === undefined) {
|
|
2779
|
+
// Both `obj?.[expr]` and `obj[expr]` short-circuit on null/undefined —
|
|
2780
|
+
// matches the safe-access behavior of dotted member access above.
|
|
2781
|
+
return undefined;
|
|
2782
|
+
}
|
|
2783
|
+
const rawKey = this.evaluate(node.property);
|
|
2784
|
+
if (rawKey === null || rawKey === undefined)
|
|
2785
|
+
return undefined;
|
|
2786
|
+
const key = String(rawKey);
|
|
2787
|
+
// Same blocklist as dotted access — `obj['__proto__']` must be rejected
|
|
2788
|
+
// even though the property name was computed.
|
|
2789
|
+
if (BLOCKED_PROPERTIES.has(key)) {
|
|
2790
|
+
throw new ExpressionParserError(`Property "${key}" is not accessible for security reasons`, 0, this.expression);
|
|
2791
|
+
}
|
|
2792
|
+
if (typeof obj === 'object' || typeof obj === 'string' || typeof obj === 'number') {
|
|
2793
|
+
return obj[key];
|
|
2794
|
+
}
|
|
2795
|
+
return undefined;
|
|
2796
|
+
}
|
|
2613
2797
|
evaluateBinaryOp(node) {
|
|
2614
2798
|
// Handle short-circuit evaluation for logical operators
|
|
2615
2799
|
if (node.operator === '&&') {
|
|
@@ -2674,11 +2858,21 @@ class Evaluator {
|
|
|
2674
2858
|
}
|
|
2675
2859
|
}
|
|
2676
2860
|
evaluateCallExpression(node) {
|
|
2677
|
-
// Only allow method calls (member access)
|
|
2861
|
+
// Only allow method calls (member access). Computed access (`obj['method']()`)
|
|
2862
|
+
// is also rejected — the security model whitelists method names statically.
|
|
2678
2863
|
if (node.callee.type !== 'MemberAccess') {
|
|
2679
2864
|
throw new ExpressionParserError('Only method calls are allowed, not arbitrary function calls', 0, this.expression);
|
|
2680
2865
|
}
|
|
2681
2866
|
const obj = this.evaluate(node.callee.object);
|
|
2867
|
+
// Calling a method on a nullish receiver silently returns undefined —
|
|
2868
|
+
// consistent with how dotted member access (`obj.prop`) treats nullish
|
|
2869
|
+
// receivers above. This means `a?.b()`, `a?.b.c()`, and `nullable.method()`
|
|
2870
|
+
// all short-circuit to undefined rather than tripping the whitelist check
|
|
2871
|
+
// with a misleading "method not allowed" error. The whitelist still
|
|
2872
|
+
// enforces strictly on non-nullish receivers.
|
|
2873
|
+
if (obj === null || obj === undefined) {
|
|
2874
|
+
return undefined;
|
|
2875
|
+
}
|
|
2682
2876
|
const methodName = node.callee.property;
|
|
2683
2877
|
// Block access to dangerous properties (must check before method call)
|
|
2684
2878
|
if (BLOCKED_PROPERTIES.has(methodName)) {
|
|
@@ -2701,6 +2895,28 @@ class Evaluator {
|
|
|
2701
2895
|
evaluateArrayLiteral(node) {
|
|
2702
2896
|
return node.elements.map((element) => this.evaluate(element));
|
|
2703
2897
|
}
|
|
2898
|
+
evaluateObjectLiteral(node) {
|
|
2899
|
+
const result = {};
|
|
2900
|
+
for (const { key, value } of node.properties) {
|
|
2901
|
+
if (BLOCKED_PROPERTIES.has(key)) {
|
|
2902
|
+
throw new ExpressionParserError(`Property "${key}" is not allowed in object literals for security reasons`, 0, this.expression);
|
|
2903
|
+
}
|
|
2904
|
+
result[key] = this.evaluate(value);
|
|
2905
|
+
}
|
|
2906
|
+
return result;
|
|
2907
|
+
}
|
|
2908
|
+
evaluateArrowFunction(node) {
|
|
2909
|
+
const parentScope = this.scope;
|
|
2910
|
+
const expression = this.expression;
|
|
2911
|
+
return (...args) => {
|
|
2912
|
+
const childScope = { ...parentScope };
|
|
2913
|
+
for (let i = 0; i < node.params.length; i++) {
|
|
2914
|
+
childScope[node.params[i]] = args[i];
|
|
2915
|
+
}
|
|
2916
|
+
const childEvaluator = new Evaluator(childScope, expression);
|
|
2917
|
+
return childEvaluator.evaluate(node.body);
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2704
2920
|
isMethodSafe(obj, methodName) {
|
|
2705
2921
|
if (obj === null || obj === undefined) {
|
|
2706
2922
|
return false;
|
|
@@ -4241,9 +4457,9 @@ class PageOrchestratorComponent {
|
|
|
4241
4457
|
}
|
|
4242
4458
|
}
|
|
4243
4459
|
}
|
|
4244
|
-
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-
|
|
4460
|
+
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-Bz2RpleR.mjs').then(m => m.default)], () => [import('./ng-forge-dynamic-forms-page-field.component-Bz2RpleR.mjs').then(m => m.default)]] });
|
|
4245
4461
|
}
|
|
4246
|
-
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.14", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-
|
|
4462
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.14", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-Bz2RpleR.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
4247
4463
|
type: Component,
|
|
4248
4464
|
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
4249
4465
|
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
@@ -13413,21 +13629,67 @@ function validateNoCycles(collection, logger) {
|
|
|
13413
13629
|
}
|
|
13414
13630
|
|
|
13415
13631
|
/**
|
|
13416
|
-
*
|
|
13632
|
+
* Compares two entry lists as multisets of content signatures. Order-insensitive
|
|
13633
|
+
* — entries reordered with the same content compare equal, so topological-sort
|
|
13634
|
+
* reorderings triggered by unrelated entries don't churn downstream stream
|
|
13635
|
+
* lifecycles.
|
|
13636
|
+
*
|
|
13637
|
+
* Multiset (not set) semantics: two distinct entries with identical signatures
|
|
13638
|
+
* are counted separately, so `[A, B]` does NOT equal `[A, A]` even though both
|
|
13639
|
+
* have length 2 and both contain `A` as a signature.
|
|
13417
13640
|
*
|
|
13418
|
-
*
|
|
13419
|
-
*
|
|
13420
|
-
*
|
|
13641
|
+
* Used by the value-derivation and property-derivation orchestrators to gate
|
|
13642
|
+
* `distinctUntilChanged` over HTTP- and async-function-entry sets.
|
|
13643
|
+
*
|
|
13644
|
+
* @internal
|
|
13421
13645
|
*/
|
|
13422
|
-
function
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13646
|
+
function entrySetsEqual(prev, next, sig) {
|
|
13647
|
+
if (prev.length !== next.length)
|
|
13648
|
+
return false;
|
|
13649
|
+
const counts = new Map();
|
|
13650
|
+
for (const item of prev) {
|
|
13651
|
+
const key = sig(item);
|
|
13652
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
13653
|
+
}
|
|
13654
|
+
for (const item of next) {
|
|
13655
|
+
const key = sig(item);
|
|
13656
|
+
const remaining = counts.get(key);
|
|
13657
|
+
if (!remaining)
|
|
13658
|
+
return false;
|
|
13659
|
+
if (remaining === 1)
|
|
13660
|
+
counts.delete(key);
|
|
13661
|
+
else
|
|
13662
|
+
counts.set(key, remaining - 1);
|
|
13663
|
+
}
|
|
13664
|
+
return counts.size === 0;
|
|
13428
13665
|
}
|
|
13429
|
-
|
|
13430
|
-
|
|
13666
|
+
/**
|
|
13667
|
+
* Declarative pipeline shared by both derivation orchestrators:
|
|
13668
|
+
*
|
|
13669
|
+
* 1. Project the collection down to a typed entry list.
|
|
13670
|
+
* 2. Skip rebuilds when the entry set hasn't changed (multiset comparison).
|
|
13671
|
+
* 3. `switchMap` cancels the previous merged inner streams and creates fresh
|
|
13672
|
+
* ones from the new entries; in-flight HTTP / async work is cancelled via
|
|
13673
|
+
* ordinary RxJS unsubscribe semantics.
|
|
13674
|
+
*
|
|
13675
|
+
* No mutable subscription bookkeeping; the caller pipes through
|
|
13676
|
+
* `takeUntilDestroyed` for unmount cleanup.
|
|
13677
|
+
*
|
|
13678
|
+
* @internal
|
|
13679
|
+
*/
|
|
13680
|
+
function buildEntryStreamPipeline(config) {
|
|
13681
|
+
return config.collection$.pipe(map((collection) => config.selectEntries(collection)), distinctUntilChanged((prev, next) => entrySetsEqual(prev, next, config.entrySignature)), switchMap((entries) => {
|
|
13682
|
+
if (entries.length === 0)
|
|
13683
|
+
return EMPTY;
|
|
13684
|
+
const streams = entries.map((entry) => config.createStream(entry)).filter((stream) => stream !== null);
|
|
13685
|
+
if (streams.length === 0)
|
|
13686
|
+
return EMPTY;
|
|
13687
|
+
return merge(...streams);
|
|
13688
|
+
}));
|
|
13689
|
+
}
|
|
13690
|
+
|
|
13691
|
+
const LOG_PREFIX$3 = 'HTTP Derivation -';
|
|
13692
|
+
const DEFAULT_HTTP_DEBOUNCE_MS$1 = 300;
|
|
13431
13693
|
/**
|
|
13432
13694
|
* Creates an RxJS Observable stream that processes an HTTP derivation entry.
|
|
13433
13695
|
*
|
|
@@ -13451,7 +13713,7 @@ const DEFAULT_HTTP_DEBOUNCE_MS = 300;
|
|
|
13451
13713
|
function createHttpDerivationStream(entry, formValue$, context) {
|
|
13452
13714
|
// Guard: no array support for HTTP derivations
|
|
13453
13715
|
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
13454
|
-
context.logger.warn(`${LOG_PREFIX$
|
|
13716
|
+
context.logger.warn(`${LOG_PREFIX$3} HTTP derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
13455
13717
|
`Array HTTP derivations will be supported in a future release.`);
|
|
13456
13718
|
return EMPTY;
|
|
13457
13719
|
}
|
|
@@ -13460,7 +13722,7 @@ function createHttpDerivationStream(entry, formValue$, context) {
|
|
|
13460
13722
|
}
|
|
13461
13723
|
// Capture after the guard — TS narrows this to `string` here
|
|
13462
13724
|
const responseExpression = entry.responseExpression;
|
|
13463
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS;
|
|
13725
|
+
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS$1;
|
|
13464
13726
|
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
13465
13727
|
current: current,
|
|
13466
13728
|
changedFields: getChangedKeys(previous, current),
|
|
@@ -13554,15 +13816,16 @@ function createHttpDerivationStream(entry, formValue$, context) {
|
|
|
13554
13816
|
subscriber.complete();
|
|
13555
13817
|
return;
|
|
13556
13818
|
}
|
|
13557
|
-
//
|
|
13558
|
-
//
|
|
13819
|
+
// Cancellation is handled declaratively by the orchestrator's outer
|
|
13820
|
+
// switchMap: when the entry set changes, the outer stream unsubscribes
|
|
13821
|
+
// from this inner observable, the cleanup callback below runs, and
|
|
13822
|
+
// the in-flight HTTP request is cancelled.
|
|
13559
13823
|
const method = (resolvedRequest.method ?? 'GET').toUpperCase();
|
|
13560
13824
|
const httpSub = context.httpClient
|
|
13561
13825
|
.request(method, resolvedRequest.url, {
|
|
13562
13826
|
body: resolvedRequest.body,
|
|
13563
13827
|
headers: resolvedRequest.headers,
|
|
13564
13828
|
})
|
|
13565
|
-
.pipe(takeUntil(context.guard$))
|
|
13566
13829
|
.subscribe({
|
|
13567
13830
|
next: (response) => {
|
|
13568
13831
|
try {
|
|
@@ -13597,13 +13860,13 @@ function createHttpDerivationStream(entry, formValue$, context) {
|
|
|
13597
13860
|
}
|
|
13598
13861
|
catch (error) {
|
|
13599
13862
|
const message = error instanceof Error ? error.message : String(error);
|
|
13600
|
-
context.logger.warn(`${LOG_PREFIX$
|
|
13863
|
+
context.logger.warn(`${LOG_PREFIX$3} Failed to process response for '${entry.fieldKey}': ${message}`);
|
|
13601
13864
|
subscriber.complete();
|
|
13602
13865
|
}
|
|
13603
13866
|
},
|
|
13604
13867
|
error: (error) => {
|
|
13605
13868
|
const message = error instanceof Error ? error.message : String(error);
|
|
13606
|
-
context.logger.warn(`${LOG_PREFIX$
|
|
13869
|
+
context.logger.warn(`${LOG_PREFIX$3} HTTP request failed for '${entry.fieldKey}': ${message}`);
|
|
13607
13870
|
subscriber.complete();
|
|
13608
13871
|
},
|
|
13609
13872
|
});
|
|
@@ -13617,13 +13880,13 @@ function createHttpDerivationStream(entry, formValue$, context) {
|
|
|
13617
13880
|
}), catchError$1((error) => {
|
|
13618
13881
|
// Safety net — should not normally be reached since inner errors are caught
|
|
13619
13882
|
const message = error instanceof Error ? error.message : String(error);
|
|
13620
|
-
context.logger.warn(`${LOG_PREFIX$
|
|
13883
|
+
context.logger.warn(`${LOG_PREFIX$3} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
13621
13884
|
return EMPTY;
|
|
13622
13885
|
}));
|
|
13623
13886
|
}
|
|
13624
13887
|
|
|
13625
|
-
const LOG_PREFIX = 'Async Derivation -';
|
|
13626
|
-
const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
|
|
13888
|
+
const LOG_PREFIX$2 = 'Async Derivation -';
|
|
13889
|
+
const DEFAULT_ASYNC_DEBOUNCE_MS$1 = 300;
|
|
13627
13890
|
/**
|
|
13628
13891
|
* Creates an RxJS Observable stream that processes an async derivation entry.
|
|
13629
13892
|
*
|
|
@@ -13642,22 +13905,22 @@ const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
|
|
|
13642
13905
|
function createAsyncDerivationStream(entry, formValue$, context) {
|
|
13643
13906
|
// Guard: no array support for async derivations
|
|
13644
13907
|
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
13645
|
-
context.logger.warn(`${LOG_PREFIX} Async derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
13908
|
+
context.logger.warn(`${LOG_PREFIX$2} Async derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
13646
13909
|
`Array async derivations will be supported in a future release.`);
|
|
13647
13910
|
return EMPTY;
|
|
13648
13911
|
}
|
|
13649
13912
|
if (!entry.asyncFunctionName && !entry.asyncFn) {
|
|
13650
|
-
context.logger.warn(`${LOG_PREFIX} Derivation for '${entry.fieldKey}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
13913
|
+
context.logger.warn(`${LOG_PREFIX$2} Derivation for '${entry.fieldKey}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
13651
13914
|
`One of the two is required for source: 'asyncFunction'.`);
|
|
13652
13915
|
return EMPTY;
|
|
13653
13916
|
}
|
|
13654
13917
|
if (entry.asyncFn && entry.asyncFunctionName) {
|
|
13655
|
-
context.logger.warn(`${LOG_PREFIX} Both "asyncFn" and "asyncFunctionName" are set on derivation for '${entry.fieldKey}'. ` +
|
|
13918
|
+
context.logger.warn(`${LOG_PREFIX$2} Both "asyncFn" and "asyncFunctionName" are set on derivation for '${entry.fieldKey}'. ` +
|
|
13656
13919
|
`Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
|
|
13657
13920
|
}
|
|
13658
13921
|
const inlineAsyncFn = entry.asyncFn;
|
|
13659
13922
|
const asyncFunctionName = entry.asyncFunctionName;
|
|
13660
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS;
|
|
13923
|
+
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS$1;
|
|
13661
13924
|
// startWith(null) → pairwise() causes all fields to appear "changed" on the
|
|
13662
13925
|
// first emission (null vs actual form value), so every dependsOn field matches.
|
|
13663
13926
|
// This intentionally fires the async derivation on initial form load — same
|
|
@@ -13704,7 +13967,7 @@ function createAsyncDerivationStream(entry, formValue$, context) {
|
|
|
13704
13967
|
// Inline `asyncFn` wins; otherwise look up the registered function.
|
|
13705
13968
|
const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
|
|
13706
13969
|
if (!asyncFn) {
|
|
13707
|
-
context.logger.warn(`${LOG_PREFIX} Async derivation function '${asyncFunctionName}' not found for field '${entry.fieldKey}'. ` +
|
|
13970
|
+
context.logger.warn(`${LOG_PREFIX$2} Async derivation function '${asyncFunctionName}' not found for field '${entry.fieldKey}'. ` +
|
|
13708
13971
|
`Register it in customFnConfig.asyncDerivations.`);
|
|
13709
13972
|
subscriber.complete();
|
|
13710
13973
|
return;
|
|
@@ -13772,13 +14035,13 @@ function createAsyncDerivationStream(entry, formValue$, context) {
|
|
|
13772
14035
|
}
|
|
13773
14036
|
catch (error) {
|
|
13774
14037
|
const message = error instanceof Error ? error.message : String(error);
|
|
13775
|
-
context.logger.warn(`${LOG_PREFIX} Failed to process result for '${entry.fieldKey}': ${message}`);
|
|
14038
|
+
context.logger.warn(`${LOG_PREFIX$2} Failed to process result for '${entry.fieldKey}': ${message}`);
|
|
13776
14039
|
subscriber.complete();
|
|
13777
14040
|
}
|
|
13778
14041
|
},
|
|
13779
14042
|
error: (error) => {
|
|
13780
14043
|
const message = error instanceof Error ? error.message : String(error);
|
|
13781
|
-
context.logger.warn(`${LOG_PREFIX} Async function failed for '${entry.fieldKey}': ${message}`);
|
|
14044
|
+
context.logger.warn(`${LOG_PREFIX$2} Async function failed for '${entry.fieldKey}': ${message}`);
|
|
13782
14045
|
subscriber.complete();
|
|
13783
14046
|
},
|
|
13784
14047
|
complete: () => {
|
|
@@ -13795,7 +14058,7 @@ function createAsyncDerivationStream(entry, formValue$, context) {
|
|
|
13795
14058
|
}), catchError$1((error) => {
|
|
13796
14059
|
// Safety net — should not normally be reached since inner errors are caught
|
|
13797
14060
|
const message = error instanceof Error ? error.message : String(error);
|
|
13798
|
-
context.logger.warn(`${LOG_PREFIX} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
14061
|
+
context.logger.warn(`${LOG_PREFIX$2} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
13799
14062
|
return EMPTY;
|
|
13800
14063
|
}));
|
|
13801
14064
|
}
|
|
@@ -13873,20 +14136,6 @@ class DerivationOrchestrator {
|
|
|
13873
14136
|
functionRegistry = inject(FunctionRegistryService);
|
|
13874
14137
|
formOptions = inject(FORM_OPTIONS);
|
|
13875
14138
|
httpClient = inject(HttpClient, { optional: true });
|
|
13876
|
-
/** Active HTTP derivation stream subscriptions */
|
|
13877
|
-
httpSubscriptions = [];
|
|
13878
|
-
/** Identity keys of current HTTP entries for smart teardown comparison */
|
|
13879
|
-
lastHttpEntryKeys = null;
|
|
13880
|
-
/** Active async function derivation stream subscriptions */
|
|
13881
|
-
asyncFunctionSubscriptions = [];
|
|
13882
|
-
/** Identity keys of current async entries for smart teardown comparison */
|
|
13883
|
-
lastAsyncEntryKeys = null;
|
|
13884
|
-
/**
|
|
13885
|
-
* Guard for detecting stale HTTP responses. Call `invalidate()` on teardown to cancel
|
|
13886
|
-
* in-flight requests from the previous generation. Each HTTP stream subscribes via
|
|
13887
|
-
* `takeUntil(guard$)` — when `invalidate()` fires, all in-flight requests are discarded.
|
|
13888
|
-
*/
|
|
13889
|
-
streamGuard = createStreamGuard();
|
|
13890
14139
|
/**
|
|
13891
14140
|
* Computed signal containing the collected and validated derivations.
|
|
13892
14141
|
* Returns null if no derivations are defined.
|
|
@@ -14042,36 +14291,33 @@ class DerivationOrchestrator {
|
|
|
14042
14291
|
}
|
|
14043
14292
|
}
|
|
14044
14293
|
/**
|
|
14045
|
-
*
|
|
14294
|
+
* Builds a declarative stream that reacts to changes in the set of HTTP
|
|
14295
|
+
* derivation entries and emits per-entry streams merged together.
|
|
14046
14296
|
*
|
|
14047
|
-
*
|
|
14048
|
-
*
|
|
14297
|
+
* - `distinctUntilChanged` over the set of entry signatures skips rebuilds
|
|
14298
|
+
* when the HTTP entries haven't actually changed. The comparison is
|
|
14299
|
+
* order-insensitive — `topologicalSort` in the collector can reorder
|
|
14300
|
+
* existing entries when unrelated entries are added/removed, and we
|
|
14301
|
+
* don't want to tear down healthy streams in that case.
|
|
14302
|
+
* - `switchMap` cancels the previous merged stream when entries change,
|
|
14303
|
+
* which propagates into each `createHttpDerivationStream` and cancels
|
|
14304
|
+
* any in-flight HTTP requests via ordinary RxJS unsubscribe semantics.
|
|
14305
|
+
*
|
|
14306
|
+
* No mutable subscription bookkeeping or stream guard — `takeUntilDestroyed`
|
|
14307
|
+
* on the outer subscription handles unmount.
|
|
14049
14308
|
*/
|
|
14050
14309
|
setupHttpStreams() {
|
|
14051
|
-
toObservable(this.
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
|
|
14062
|
-
if (this.lastHttpEntryKeys && this.setsEqual(this.lastHttpEntryKeys, newKeys)) {
|
|
14063
|
-
return; // No change in HTTP entries — keep existing streams
|
|
14064
|
-
}
|
|
14065
|
-
// Validate HttpClient availability
|
|
14066
|
-
if (!this.httpClient) {
|
|
14067
|
-
this.logger.error('HTTP Derivation: HttpClient is not available. ' + 'Ensure provideHttpClient() is included in your application providers.');
|
|
14068
|
-
return;
|
|
14069
|
-
}
|
|
14070
|
-
// Tear down previous streams and create new ones
|
|
14071
|
-
this.teardownHttpStreams();
|
|
14072
|
-
this.lastHttpEntryKeys = newKeys;
|
|
14073
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
14074
|
-
for (const entry of httpEntries) {
|
|
14310
|
+
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
14311
|
+
const collection$ = toObservable(this.derivationCollection, { injector: this.injector });
|
|
14312
|
+
buildEntryStreamPipeline({
|
|
14313
|
+
collection$,
|
|
14314
|
+
selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
|
|
14315
|
+
entrySignature: httpEntrySignature$1,
|
|
14316
|
+
createStream: (entry) => {
|
|
14317
|
+
if (!this.httpClient) {
|
|
14318
|
+
this.logger.error('HTTP Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
|
|
14319
|
+
return null;
|
|
14320
|
+
}
|
|
14075
14321
|
const context = {
|
|
14076
14322
|
formValue: this.config.formValue,
|
|
14077
14323
|
form: this.config.form,
|
|
@@ -14081,119 +14327,77 @@ class DerivationOrchestrator {
|
|
|
14081
14327
|
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
14082
14328
|
externalData: () => resolveExternalData(this.config.externalData),
|
|
14083
14329
|
warningTracker: this.warningTracker,
|
|
14084
|
-
guard$: this.streamGuard.guard$,
|
|
14085
14330
|
};
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
* Tears down all active HTTP derivation streams. Invalidating the stream guard
|
|
14095
|
-
* cancels any in-flight HTTP responses from the old generation via `takeUntil`.
|
|
14096
|
-
*/
|
|
14097
|
-
teardownHttpStreams() {
|
|
14098
|
-
this.streamGuard.invalidate();
|
|
14099
|
-
for (const sub of this.httpSubscriptions) {
|
|
14100
|
-
sub.unsubscribe();
|
|
14101
|
-
}
|
|
14102
|
-
this.httpSubscriptions = [];
|
|
14103
|
-
}
|
|
14104
|
-
/**
|
|
14105
|
-
* Computes a set of identity keys for HTTP entries.
|
|
14106
|
-
* Used for smart teardown comparison.
|
|
14107
|
-
*/
|
|
14108
|
-
computeHttpEntryKeys(entries) {
|
|
14109
|
-
return new Set(entries.map((entry) => `${entry.fieldKey}:${JSON.stringify(entry.http, Object.keys(entry.http ?? {}).sort())}`));
|
|
14110
|
-
}
|
|
14111
|
-
/**
|
|
14112
|
-
* Compares two sets for equality.
|
|
14113
|
-
*/
|
|
14114
|
-
setsEqual(a, b) {
|
|
14115
|
-
if (a.size !== b.size)
|
|
14116
|
-
return false;
|
|
14117
|
-
for (const item of a) {
|
|
14118
|
-
if (!b.has(item))
|
|
14119
|
-
return false;
|
|
14120
|
-
}
|
|
14121
|
-
return true;
|
|
14331
|
+
return createHttpDerivationStream(entry, formValue$, context);
|
|
14332
|
+
},
|
|
14333
|
+
})
|
|
14334
|
+
.pipe(catchError((err) => {
|
|
14335
|
+
this.logger.error('HTTP Derivation: outer stream error', err);
|
|
14336
|
+
return EMPTY;
|
|
14337
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
14338
|
+
.subscribe();
|
|
14122
14339
|
}
|
|
14123
|
-
/**
|
|
14124
|
-
* Sets up reactive async function derivation streams that react to collection changes.
|
|
14125
|
-
*
|
|
14126
|
-
* Subscribes to derivationCollection changes and creates per-entry async streams.
|
|
14127
|
-
* Uses smart teardown: only recreates streams when async entries actually change.
|
|
14128
|
-
*/
|
|
14129
14340
|
setupAsyncFunctionStreams() {
|
|
14130
|
-
toObservable(this.
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
logger: this.logger,
|
|
14153
|
-
derivationLogger: this.config.derivationLogger,
|
|
14154
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
14155
|
-
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
14156
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
14157
|
-
warningTracker: this.warningTracker,
|
|
14158
|
-
};
|
|
14159
|
-
const stream = createAsyncDerivationStream(entry, formValue$, context).pipe(takeUntilDestroyed(this.destroyRef));
|
|
14160
|
-
this.asyncFunctionSubscriptions.push(stream.subscribe({
|
|
14161
|
-
error: (err) => this.logger.error(`Async Derivation: Stream error for '${entry.fieldKey}'`, err),
|
|
14162
|
-
}));
|
|
14163
|
-
}
|
|
14164
|
-
});
|
|
14165
|
-
}
|
|
14166
|
-
/**
|
|
14167
|
-
* Tears down all active async function derivation streams.
|
|
14168
|
-
*/
|
|
14169
|
-
teardownAsyncFunctionStreams() {
|
|
14170
|
-
for (const sub of this.asyncFunctionSubscriptions) {
|
|
14171
|
-
sub.unsubscribe();
|
|
14172
|
-
}
|
|
14173
|
-
this.asyncFunctionSubscriptions = [];
|
|
14174
|
-
}
|
|
14175
|
-
/**
|
|
14176
|
-
* Computes a set of identity keys for async entries.
|
|
14177
|
-
* Used for smart teardown comparison.
|
|
14178
|
-
*/
|
|
14179
|
-
computeAsyncEntryKeys(entries) {
|
|
14180
|
-
// For inline `asyncFn` entries we tag the key with the field path (the real
|
|
14181
|
-
// identity, since derivations are self-targeting) rather than the array
|
|
14182
|
-
// index. Index-based tagging churned identities whenever topologicalSort
|
|
14183
|
-
// reordered entries, causing unnecessary teardown + re-subscribe.
|
|
14184
|
-
return new Set(entries.map((entry) => {
|
|
14185
|
-
const config = {
|
|
14186
|
-
asyncFunctionName: entry.asyncFunctionName,
|
|
14187
|
-
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}` : undefined,
|
|
14188
|
-
dependsOn: entry.dependsOn,
|
|
14189
|
-
debounceMs: entry.debounceMs,
|
|
14190
|
-
stopOnUserOverride: entry.stopOnUserOverride,
|
|
14191
|
-
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
14192
|
-
};
|
|
14193
|
-
return `${entry.fieldKey}:${JSON.stringify(config, Object.keys(config).sort())}`;
|
|
14194
|
-
}));
|
|
14341
|
+
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
14342
|
+
const collection$ = toObservable(this.derivationCollection, { injector: this.injector });
|
|
14343
|
+
buildEntryStreamPipeline({
|
|
14344
|
+
collection$,
|
|
14345
|
+
selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
|
|
14346
|
+
entrySignature: asyncEntrySignature$1,
|
|
14347
|
+
createStream: (entry) => createAsyncDerivationStream(entry, formValue$, {
|
|
14348
|
+
formValue: this.config.formValue,
|
|
14349
|
+
form: this.config.form,
|
|
14350
|
+
logger: this.logger,
|
|
14351
|
+
derivationLogger: this.config.derivationLogger,
|
|
14352
|
+
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
14353
|
+
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
14354
|
+
externalData: () => resolveExternalData(this.config.externalData),
|
|
14355
|
+
warningTracker: this.warningTracker,
|
|
14356
|
+
}),
|
|
14357
|
+
})
|
|
14358
|
+
.pipe(catchError((err) => {
|
|
14359
|
+
this.logger.error('Async Derivation: outer stream error', err);
|
|
14360
|
+
return EMPTY;
|
|
14361
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
14362
|
+
.subscribe();
|
|
14195
14363
|
}
|
|
14196
14364
|
}
|
|
14365
|
+
/**
|
|
14366
|
+
* Identity signature for an HTTP derivation entry. Includes every field that
|
|
14367
|
+
* drives the inner stream's behavior — changing any of them must tear down
|
|
14368
|
+
* and rebuild the stream. Stable across topological reorderings because the
|
|
14369
|
+
* consumer ({@link entrySetsEqual}) compares as a multiset.
|
|
14370
|
+
*/
|
|
14371
|
+
function httpEntrySignature$1(entry) {
|
|
14372
|
+
const config = {
|
|
14373
|
+
http: entry.http,
|
|
14374
|
+
responseExpression: entry.responseExpression,
|
|
14375
|
+
dependsOn: entry.dependsOn,
|
|
14376
|
+
debounceMs: entry.debounceMs,
|
|
14377
|
+
trigger: entry.trigger,
|
|
14378
|
+
condition: entry.condition,
|
|
14379
|
+
stopOnUserOverride: entry.stopOnUserOverride,
|
|
14380
|
+
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
14381
|
+
};
|
|
14382
|
+
return `${entry.fieldKey}:${JSON.stringify(config)}`;
|
|
14383
|
+
}
|
|
14384
|
+
function asyncEntrySignature$1(entry) {
|
|
14385
|
+
// For inline `asyncFn` entries we tag the signature with the field path (the
|
|
14386
|
+
// real identity, since derivations are self-targeting) rather than the array
|
|
14387
|
+
// index. Index-based tagging churned identities whenever topologicalSort
|
|
14388
|
+
// reordered entries, causing unnecessary teardown + re-subscribe.
|
|
14389
|
+
const config = {
|
|
14390
|
+
asyncFunctionName: entry.asyncFunctionName,
|
|
14391
|
+
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}` : undefined,
|
|
14392
|
+
dependsOn: entry.dependsOn,
|
|
14393
|
+
debounceMs: entry.debounceMs,
|
|
14394
|
+
trigger: entry.trigger,
|
|
14395
|
+
condition: entry.condition,
|
|
14396
|
+
stopOnUserOverride: entry.stopOnUserOverride,
|
|
14397
|
+
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
14398
|
+
};
|
|
14399
|
+
return `${entry.fieldKey}:${JSON.stringify(config)}`;
|
|
14400
|
+
}
|
|
14197
14401
|
/**
|
|
14198
14402
|
* Creates a DerivationOrchestrator for a dynamic form.
|
|
14199
14403
|
* Must be called within an injection context.
|
|
@@ -14501,6 +14705,27 @@ function withLoggerConfig(config = true) {
|
|
|
14501
14705
|
return createFeature('logger', providers);
|
|
14502
14706
|
}
|
|
14503
14707
|
|
|
14708
|
+
/**
|
|
14709
|
+
* Creates an empty property derivation collection.
|
|
14710
|
+
*
|
|
14711
|
+
* @returns Empty collection
|
|
14712
|
+
*
|
|
14713
|
+
* @internal
|
|
14714
|
+
*/
|
|
14715
|
+
function createEmptyPropertyDerivationCollection() {
|
|
14716
|
+
return { entries: [] };
|
|
14717
|
+
}
|
|
14718
|
+
/**
|
|
14719
|
+
* Returns true when the entry's value source is async (HTTP or async function)
|
|
14720
|
+
* and therefore must be processed by a dedicated stream subscription rather
|
|
14721
|
+
* than the synchronous applicator pipeline.
|
|
14722
|
+
*
|
|
14723
|
+
* @internal
|
|
14724
|
+
*/
|
|
14725
|
+
function isAsyncPropertyDerivationEntry(entry) {
|
|
14726
|
+
return Boolean(entry.http) || Boolean(entry.asyncFunctionName) || Boolean(entry.asyncFn);
|
|
14727
|
+
}
|
|
14728
|
+
|
|
14504
14729
|
/** @internal */
|
|
14505
14730
|
const ERROR_PREFIX = '[PropertyDerivation]';
|
|
14506
14731
|
/**
|
|
@@ -14521,7 +14746,10 @@ function applyPropertyDerivations(collection, context, changedFields) {
|
|
|
14521
14746
|
let appliedCount = 0;
|
|
14522
14747
|
let skippedCount = 0;
|
|
14523
14748
|
let errorCount = 0;
|
|
14524
|
-
const
|
|
14749
|
+
const baseEntries = changedFields ? getEntriesForChangedFields(collection.entries, changedFields) : collection.entries;
|
|
14750
|
+
// Async entries (HTTP / asyncFn / asyncFunctionName) are processed by dedicated
|
|
14751
|
+
// stream subscriptions in the orchestrator, not by this synchronous applicator.
|
|
14752
|
+
const entriesToProcess = baseEntries.filter((entry) => !isAsyncPropertyDerivationEntry(entry));
|
|
14525
14753
|
for (const entry of entriesToProcess) {
|
|
14526
14754
|
try {
|
|
14527
14755
|
const applied = tryApplyPropertyDerivation(entry, context);
|
|
@@ -14766,6 +14994,78 @@ function collectFromField(field, entries, context) {
|
|
|
14766
14994
|
*/
|
|
14767
14995
|
function createPropertyDerivationEntryFromDerivation(fieldKey, config, context) {
|
|
14768
14996
|
const effectiveFieldKey = buildPropertyOverrideKey(context.arrayPath, context.arrayPath ? PLACEHOLDER_INDEX : undefined, fieldKey, context.groupPath);
|
|
14997
|
+
// HTTP and async function property derivations require explicit, non-wildcard
|
|
14998
|
+
// dependencies — mirrors the same guards on value-derivation HTTP/async sources.
|
|
14999
|
+
//
|
|
15000
|
+
// Type-level discriminants require these fields, but JSON-loaded configs can
|
|
15001
|
+
// bypass that — so we defensively guard against missing arrays and missing
|
|
15002
|
+
// HTTP fields here to fail fast with `DynamicFormError` instead of throwing
|
|
15003
|
+
// an opaque `TypeError` later.
|
|
15004
|
+
//
|
|
15005
|
+
// `targetProperty` is captured up front and `untypedConfig` is used inside
|
|
15006
|
+
// the source-narrowed blocks because TypeScript narrows `config` to `never`
|
|
15007
|
+
// once we negate a discriminant-required field (e.g. `!config.http` when
|
|
15008
|
+
// `source: 'http'` requires `http`).
|
|
15009
|
+
const errorLocation = `'${effectiveFieldKey}.${config.targetProperty}'`;
|
|
15010
|
+
const untypedConfig = config;
|
|
15011
|
+
// Reject configs that mix value-source fields with an explicit async source.
|
|
15012
|
+
// TypeScript's discriminated union already enforces this XOR for inline
|
|
15013
|
+
// configs; the runtime check guards JSON-loaded configs (API/OpenAPI/MCP)
|
|
15014
|
+
// where the wrong combination would silently degrade — async entries are
|
|
15015
|
+
// routed to the stream pipeline while sync fields would be ignored.
|
|
15016
|
+
if (untypedConfig.source === 'http' || untypedConfig.source === 'asyncFunction') {
|
|
15017
|
+
const conflictingSyncSources = [];
|
|
15018
|
+
if (untypedConfig.value !== undefined)
|
|
15019
|
+
conflictingSyncSources.push('value');
|
|
15020
|
+
if (untypedConfig.expression !== undefined)
|
|
15021
|
+
conflictingSyncSources.push('expression');
|
|
15022
|
+
if (untypedConfig.functionName !== undefined)
|
|
15023
|
+
conflictingSyncSources.push('functionName');
|
|
15024
|
+
if (untypedConfig.fn !== undefined)
|
|
15025
|
+
conflictingSyncSources.push('fn');
|
|
15026
|
+
if (conflictingSyncSources.length > 0) {
|
|
15027
|
+
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: ${JSON.stringify(untypedConfig.source)}' alongside ` +
|
|
15028
|
+
`sync value source(s) [${conflictingSyncSources.join(', ')}]. These are mutually exclusive — remove either ` +
|
|
15029
|
+
`the sync fields or the async source.`);
|
|
15030
|
+
}
|
|
15031
|
+
}
|
|
15032
|
+
// Reject HTTP + asyncFunction-field combinations as well (TS already
|
|
15033
|
+
// enforces this; runtime guard for JSON-loaded configs).
|
|
15034
|
+
if (untypedConfig.source === 'http' && (untypedConfig.asyncFunctionName !== undefined || untypedConfig.asyncFn !== undefined)) {
|
|
15035
|
+
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "http"' alongside an async function — these are mutually exclusive.`);
|
|
15036
|
+
}
|
|
15037
|
+
if (untypedConfig.source === 'asyncFunction' && (untypedConfig.http !== undefined || untypedConfig.responseExpression !== undefined)) {
|
|
15038
|
+
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "asyncFunction"' alongside HTTP fields — these are mutually exclusive.`);
|
|
15039
|
+
}
|
|
15040
|
+
if (untypedConfig.source === 'http') {
|
|
15041
|
+
if (!untypedConfig.http) {
|
|
15042
|
+
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires an 'http' config object.`);
|
|
15043
|
+
}
|
|
15044
|
+
if (typeof untypedConfig.responseExpression !== 'string' || untypedConfig.responseExpression.trim() === '') {
|
|
15045
|
+
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires a non-empty 'responseExpression'.`);
|
|
15046
|
+
}
|
|
15047
|
+
if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
|
|
15048
|
+
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
|
|
15049
|
+
`Wildcard dependencies would trigger HTTP requests on every form change.`);
|
|
15050
|
+
}
|
|
15051
|
+
if (untypedConfig.dependsOn.includes('*')) {
|
|
15052
|
+
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
15053
|
+
`Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
|
|
15054
|
+
}
|
|
15055
|
+
}
|
|
15056
|
+
if (untypedConfig.source === 'asyncFunction') {
|
|
15057
|
+
if (!untypedConfig.asyncFunctionName && !untypedConfig.asyncFn) {
|
|
15058
|
+
throw new DynamicFormError(`Async property derivation for ${errorLocation} requires either 'asyncFunctionName' or 'asyncFn'.`);
|
|
15059
|
+
}
|
|
15060
|
+
if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
|
|
15061
|
+
throw new DynamicFormError(`Async property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
|
|
15062
|
+
`Wildcard dependencies would trigger async functions on every form change.`);
|
|
15063
|
+
}
|
|
15064
|
+
if (untypedConfig.dependsOn.includes('*')) {
|
|
15065
|
+
throw new DynamicFormError(`Async property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
15066
|
+
`Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
|
|
15067
|
+
}
|
|
15068
|
+
}
|
|
14769
15069
|
const dependsOn = extractDependenciesFromConfig(config);
|
|
14770
15070
|
const condition = config.condition ?? true;
|
|
14771
15071
|
const trigger = config.trigger ?? 'onChange';
|
|
@@ -14779,6 +15079,10 @@ function createPropertyDerivationEntryFromDerivation(fieldKey, config, context)
|
|
|
14779
15079
|
expression: config.expression,
|
|
14780
15080
|
functionName: config.functionName,
|
|
14781
15081
|
fn: extractInlineFn(config),
|
|
15082
|
+
http: config.http,
|
|
15083
|
+
responseExpression: config.responseExpression,
|
|
15084
|
+
asyncFunctionName: config.asyncFunctionName,
|
|
15085
|
+
asyncFn: extractInlineAsyncFn(config),
|
|
14782
15086
|
trigger,
|
|
14783
15087
|
debounceMs,
|
|
14784
15088
|
debugName: config.debugName,
|
|
@@ -14786,6 +15090,204 @@ function createPropertyDerivationEntryFromDerivation(fieldKey, config, context)
|
|
|
14786
15090
|
};
|
|
14787
15091
|
}
|
|
14788
15092
|
|
|
15093
|
+
const LOG_PREFIX$1 = 'HTTP Property Derivation -';
|
|
15094
|
+
const DEFAULT_HTTP_DEBOUNCE_MS = 300;
|
|
15095
|
+
/**
|
|
15096
|
+
* Creates an RxJS Observable stream that processes an HTTP property-derivation entry.
|
|
15097
|
+
*
|
|
15098
|
+
* Mirrors `createHttpDerivationStream` (value pipeline) but writes the result via
|
|
15099
|
+
* `store.setOverride(fieldKey, targetProperty, value)` instead of patching the form.
|
|
15100
|
+
*
|
|
15101
|
+
* The stream uses `startWith(null) → pairwise()` so the first emission fires a
|
|
15102
|
+
* request for all `dependsOn` fields, populating the derived property on initial
|
|
15103
|
+
* form load. Subsequent emissions only fire when a `dependsOn` field changes.
|
|
15104
|
+
*
|
|
15105
|
+
* @internal
|
|
15106
|
+
*/
|
|
15107
|
+
function createHttpPropertyDerivationStream(entry, formValue$, context) {
|
|
15108
|
+
// Guard: no array support yet for HTTP property derivations (mirrors value pipeline).
|
|
15109
|
+
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
15110
|
+
context.logger.warn(`${LOG_PREFIX$1} HTTP property derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
15111
|
+
`Array HTTP property derivations will be supported in a future release.`);
|
|
15112
|
+
return EMPTY;
|
|
15113
|
+
}
|
|
15114
|
+
if (!entry.http || !entry.responseExpression) {
|
|
15115
|
+
return EMPTY;
|
|
15116
|
+
}
|
|
15117
|
+
const responseExpression = entry.responseExpression;
|
|
15118
|
+
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS;
|
|
15119
|
+
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
15120
|
+
current: current,
|
|
15121
|
+
changedFields: getChangedKeys(previous, current),
|
|
15122
|
+
})), filter(({ changedFields }) => {
|
|
15123
|
+
if (changedFields.size === 0)
|
|
15124
|
+
return false;
|
|
15125
|
+
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
15126
|
+
}), debounceTime(debounceMs), switchMap(({ current }) => {
|
|
15127
|
+
return new Observable((subscriber) => {
|
|
15128
|
+
const customFunctions = context.customFunctions?.();
|
|
15129
|
+
const externalData = context.externalData?.();
|
|
15130
|
+
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
15131
|
+
const evalContext = {
|
|
15132
|
+
fieldValue,
|
|
15133
|
+
formValue: current,
|
|
15134
|
+
fieldPath: entry.fieldKey,
|
|
15135
|
+
customFunctions,
|
|
15136
|
+
externalData,
|
|
15137
|
+
logger: context.logger,
|
|
15138
|
+
};
|
|
15139
|
+
// Evaluate condition — skip if false
|
|
15140
|
+
if (entry.condition !== true) {
|
|
15141
|
+
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
15142
|
+
if (!conditionMet) {
|
|
15143
|
+
subscriber.complete();
|
|
15144
|
+
return;
|
|
15145
|
+
}
|
|
15146
|
+
}
|
|
15147
|
+
// Returns null when a path param is undefined — suppress the request
|
|
15148
|
+
const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
|
|
15149
|
+
if (!resolvedRequest) {
|
|
15150
|
+
subscriber.complete();
|
|
15151
|
+
return;
|
|
15152
|
+
}
|
|
15153
|
+
const method = (resolvedRequest.method ?? 'GET').toUpperCase();
|
|
15154
|
+
// Inner cancellation is handled declaratively by the orchestrator's
|
|
15155
|
+
// outer switchMap: when the entry set changes, the outer stream
|
|
15156
|
+
// unsubscribes from this inner observable, which triggers the
|
|
15157
|
+
// cleanup callback below and cancels the in-flight HTTP request.
|
|
15158
|
+
const httpSub = context.httpClient
|
|
15159
|
+
.request(method, resolvedRequest.url, {
|
|
15160
|
+
body: resolvedRequest.body,
|
|
15161
|
+
headers: resolvedRequest.headers,
|
|
15162
|
+
})
|
|
15163
|
+
.subscribe({
|
|
15164
|
+
next: (response) => {
|
|
15165
|
+
try {
|
|
15166
|
+
const newValue = ExpressionParser.evaluate(responseExpression, { response });
|
|
15167
|
+
// `setOverride` performs its own deep-equality check, so we don't need to
|
|
15168
|
+
// compare with the current store value here.
|
|
15169
|
+
untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
|
|
15170
|
+
subscriber.complete();
|
|
15171
|
+
}
|
|
15172
|
+
catch (error) {
|
|
15173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15174
|
+
context.logger.warn(`${LOG_PREFIX$1} Failed to process response for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
15175
|
+
subscriber.complete();
|
|
15176
|
+
}
|
|
15177
|
+
},
|
|
15178
|
+
error: (error) => {
|
|
15179
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15180
|
+
context.logger.warn(`${LOG_PREFIX$1} HTTP request failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
15181
|
+
subscriber.complete();
|
|
15182
|
+
},
|
|
15183
|
+
});
|
|
15184
|
+
return () => {
|
|
15185
|
+
httpSub.unsubscribe();
|
|
15186
|
+
};
|
|
15187
|
+
});
|
|
15188
|
+
}), catchError$1((error) => {
|
|
15189
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15190
|
+
context.logger.warn(`${LOG_PREFIX$1} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
15191
|
+
return EMPTY;
|
|
15192
|
+
}));
|
|
15193
|
+
}
|
|
15194
|
+
|
|
15195
|
+
const LOG_PREFIX = 'Async Property Derivation -';
|
|
15196
|
+
const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
|
|
15197
|
+
/**
|
|
15198
|
+
* Creates an RxJS Observable stream that processes an async-function property-derivation entry.
|
|
15199
|
+
*
|
|
15200
|
+
* Mirrors `createAsyncDerivationStream` (value pipeline) but writes the result via
|
|
15201
|
+
* `store.setOverride(fieldKey, targetProperty, value)` instead of patching the form.
|
|
15202
|
+
*
|
|
15203
|
+
* @internal
|
|
15204
|
+
*/
|
|
15205
|
+
function createAsyncPropertyDerivationStream(entry, formValue$, context) {
|
|
15206
|
+
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
15207
|
+
context.logger.warn(`${LOG_PREFIX} Async property derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
15208
|
+
`Array async property derivations will be supported in a future release.`);
|
|
15209
|
+
return EMPTY;
|
|
15210
|
+
}
|
|
15211
|
+
if (!entry.asyncFunctionName && !entry.asyncFn) {
|
|
15212
|
+
context.logger.warn(`${LOG_PREFIX} Property derivation for '${entry.fieldKey}.${entry.targetProperty}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
15213
|
+
`One of the two is required for source: 'asyncFunction'.`);
|
|
15214
|
+
return EMPTY;
|
|
15215
|
+
}
|
|
15216
|
+
if (entry.asyncFn && entry.asyncFunctionName) {
|
|
15217
|
+
context.logger.warn(`${LOG_PREFIX} Both "asyncFn" and "asyncFunctionName" are set on property derivation for '${entry.fieldKey}.${entry.targetProperty}'. ` +
|
|
15218
|
+
`Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
|
|
15219
|
+
}
|
|
15220
|
+
const inlineAsyncFn = entry.asyncFn;
|
|
15221
|
+
const asyncFunctionName = entry.asyncFunctionName;
|
|
15222
|
+
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS;
|
|
15223
|
+
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
15224
|
+
current: current,
|
|
15225
|
+
changedFields: getChangedKeys(previous, current),
|
|
15226
|
+
})), filter(({ changedFields }) => {
|
|
15227
|
+
if (changedFields.size === 0)
|
|
15228
|
+
return false;
|
|
15229
|
+
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
15230
|
+
}), debounceTime(debounceMs), switchMap(({ current }) => {
|
|
15231
|
+
return new Observable((subscriber) => {
|
|
15232
|
+
const customFunctions = context.customFunctions?.();
|
|
15233
|
+
const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
|
|
15234
|
+
const externalData = context.externalData?.();
|
|
15235
|
+
const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
|
|
15236
|
+
if (!asyncFn) {
|
|
15237
|
+
context.logger.warn(`${LOG_PREFIX} Async derivation function '${asyncFunctionName}' not found for '${entry.fieldKey}.${entry.targetProperty}'. ` +
|
|
15238
|
+
`Register it in customFnConfig.asyncDerivations.`);
|
|
15239
|
+
subscriber.complete();
|
|
15240
|
+
return;
|
|
15241
|
+
}
|
|
15242
|
+
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
15243
|
+
const evalContext = {
|
|
15244
|
+
fieldValue,
|
|
15245
|
+
formValue: current,
|
|
15246
|
+
fieldPath: entry.fieldKey,
|
|
15247
|
+
customFunctions,
|
|
15248
|
+
externalData,
|
|
15249
|
+
logger: context.logger,
|
|
15250
|
+
};
|
|
15251
|
+
if (entry.condition !== true) {
|
|
15252
|
+
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
15253
|
+
if (!conditionMet) {
|
|
15254
|
+
subscriber.complete();
|
|
15255
|
+
return;
|
|
15256
|
+
}
|
|
15257
|
+
}
|
|
15258
|
+
const result$ = from(asyncFn(evalContext));
|
|
15259
|
+
const asyncSub = result$.subscribe({
|
|
15260
|
+
next: (newValue) => {
|
|
15261
|
+
try {
|
|
15262
|
+
untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
|
|
15263
|
+
subscriber.complete();
|
|
15264
|
+
}
|
|
15265
|
+
catch (error) {
|
|
15266
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15267
|
+
context.logger.warn(`${LOG_PREFIX} Failed to process result for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
15268
|
+
subscriber.complete();
|
|
15269
|
+
}
|
|
15270
|
+
},
|
|
15271
|
+
error: (error) => {
|
|
15272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15273
|
+
context.logger.warn(`${LOG_PREFIX} Async function failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
15274
|
+
subscriber.complete();
|
|
15275
|
+
},
|
|
15276
|
+
complete: () => {
|
|
15277
|
+
subscriber.complete();
|
|
15278
|
+
},
|
|
15279
|
+
});
|
|
15280
|
+
return () => {
|
|
15281
|
+
asyncSub.unsubscribe();
|
|
15282
|
+
};
|
|
15283
|
+
});
|
|
15284
|
+
}), catchError$1((error) => {
|
|
15285
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15286
|
+
context.logger.warn(`${LOG_PREFIX} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
15287
|
+
return EMPTY;
|
|
15288
|
+
}));
|
|
15289
|
+
}
|
|
15290
|
+
|
|
14789
15291
|
/**
|
|
14790
15292
|
* Orchestrates property derivation processing for a dynamic form.
|
|
14791
15293
|
*
|
|
@@ -14805,6 +15307,7 @@ class PropertyDerivationOrchestrator {
|
|
|
14805
15307
|
deprecationTracker = inject(DEPRECATION_WARNING_TRACKER);
|
|
14806
15308
|
functionRegistry = inject(FunctionRegistryService);
|
|
14807
15309
|
formOptions = inject(FORM_OPTIONS);
|
|
15310
|
+
httpClient = inject(HttpClient, { optional: true });
|
|
14808
15311
|
/**
|
|
14809
15312
|
* Computed signal containing the collected property derivations.
|
|
14810
15313
|
* Returns null if no property derivations are defined.
|
|
@@ -14840,6 +15343,8 @@ class PropertyDerivationOrchestrator {
|
|
|
14840
15343
|
});
|
|
14841
15344
|
this.setupOnChangeStream();
|
|
14842
15345
|
this.setupDebouncedStream();
|
|
15346
|
+
this.setupHttpStreams();
|
|
15347
|
+
this.setupAsyncFunctionStreams();
|
|
14843
15348
|
}
|
|
14844
15349
|
setupOnChangeStream() {
|
|
14845
15350
|
const collection$ = toObservable(this.propertyDerivationCollection, { injector: this.injector });
|
|
@@ -14909,6 +15414,103 @@ class PropertyDerivationOrchestrator {
|
|
|
14909
15414
|
};
|
|
14910
15415
|
applyPropertyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
|
|
14911
15416
|
}
|
|
15417
|
+
/**
|
|
15418
|
+
* Declarative pipeline for HTTP property-derivation entries. Delegates the
|
|
15419
|
+
* collection-watching / dedup / cancellation plumbing to
|
|
15420
|
+
* {@link buildEntryStreamPipeline} so both orchestrators share one
|
|
15421
|
+
* implementation.
|
|
15422
|
+
*
|
|
15423
|
+
* Errors inside `createStream` invocations are isolated via the inner
|
|
15424
|
+
* stream's own `catchError`; we additionally wrap the outer pipeline so a
|
|
15425
|
+
* synchronous throw during stream construction (e.g., evaluation of a
|
|
15426
|
+
* `condition`) doesn't tear the whole pipeline down for the lifetime of
|
|
15427
|
+
* the form.
|
|
15428
|
+
*/
|
|
15429
|
+
setupHttpStreams() {
|
|
15430
|
+
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
15431
|
+
const collection$ = toObservable(this.propertyDerivationCollection, { injector: this.injector });
|
|
15432
|
+
buildEntryStreamPipeline({
|
|
15433
|
+
collection$,
|
|
15434
|
+
selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
|
|
15435
|
+
entrySignature: httpEntrySignature,
|
|
15436
|
+
createStream: (entry) => {
|
|
15437
|
+
if (!this.httpClient) {
|
|
15438
|
+
this.logger.error('HTTP Property Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
|
|
15439
|
+
return null;
|
|
15440
|
+
}
|
|
15441
|
+
return createHttpPropertyDerivationStream(entry, formValue$, {
|
|
15442
|
+
formValue: this.config.formValue,
|
|
15443
|
+
store: this.config.store,
|
|
15444
|
+
httpClient: this.httpClient,
|
|
15445
|
+
logger: this.logger,
|
|
15446
|
+
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
15447
|
+
externalData: () => resolveExternalData(this.config.externalData),
|
|
15448
|
+
});
|
|
15449
|
+
},
|
|
15450
|
+
})
|
|
15451
|
+
.pipe(catchError((err) => {
|
|
15452
|
+
this.logger.error('HTTP Property Derivation: outer stream error', err);
|
|
15453
|
+
return EMPTY;
|
|
15454
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
15455
|
+
.subscribe();
|
|
15456
|
+
}
|
|
15457
|
+
setupAsyncFunctionStreams() {
|
|
15458
|
+
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
15459
|
+
const collection$ = toObservable(this.propertyDerivationCollection, { injector: this.injector });
|
|
15460
|
+
buildEntryStreamPipeline({
|
|
15461
|
+
collection$,
|
|
15462
|
+
selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
|
|
15463
|
+
entrySignature: asyncEntrySignature,
|
|
15464
|
+
createStream: (entry) => createAsyncPropertyDerivationStream(entry, formValue$, {
|
|
15465
|
+
formValue: this.config.formValue,
|
|
15466
|
+
store: this.config.store,
|
|
15467
|
+
logger: this.logger,
|
|
15468
|
+
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
15469
|
+
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
15470
|
+
externalData: () => resolveExternalData(this.config.externalData),
|
|
15471
|
+
}),
|
|
15472
|
+
})
|
|
15473
|
+
.pipe(catchError((err) => {
|
|
15474
|
+
this.logger.error('Async Property Derivation: outer stream error', err);
|
|
15475
|
+
return EMPTY;
|
|
15476
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
15477
|
+
.subscribe();
|
|
15478
|
+
}
|
|
15479
|
+
}
|
|
15480
|
+
/**
|
|
15481
|
+
* Identity signature for an HTTP property-derivation entry. Includes every
|
|
15482
|
+
* field that drives the inner stream's behavior — changing any of them must
|
|
15483
|
+
* tear down and rebuild the stream. Stable across topological reorderings
|
|
15484
|
+
* because the consumer ({@link entrySetsEqual}) compares as a multiset.
|
|
15485
|
+
*/
|
|
15486
|
+
function httpEntrySignature(entry) {
|
|
15487
|
+
const config = {
|
|
15488
|
+
http: entry.http,
|
|
15489
|
+
responseExpression: entry.responseExpression,
|
|
15490
|
+
dependsOn: entry.dependsOn,
|
|
15491
|
+
debounceMs: entry.debounceMs,
|
|
15492
|
+
trigger: entry.trigger,
|
|
15493
|
+
condition: entry.condition,
|
|
15494
|
+
};
|
|
15495
|
+
return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
|
|
15496
|
+
}
|
|
15497
|
+
/**
|
|
15498
|
+
* Identity signature for an async-function property-derivation entry. Same
|
|
15499
|
+
* principle as {@link httpEntrySignature}: include every field the inner
|
|
15500
|
+
* stream closes over so changes force a rebuild. Inline `asyncFn`s use the
|
|
15501
|
+
* `fieldKey.targetProperty` path as a stable identifier — function identity
|
|
15502
|
+
* isn't JSON-serializable.
|
|
15503
|
+
*/
|
|
15504
|
+
function asyncEntrySignature(entry) {
|
|
15505
|
+
const config = {
|
|
15506
|
+
asyncFunctionName: entry.asyncFunctionName,
|
|
15507
|
+
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}.${entry.targetProperty}` : undefined,
|
|
15508
|
+
dependsOn: entry.dependsOn,
|
|
15509
|
+
debounceMs: entry.debounceMs,
|
|
15510
|
+
trigger: entry.trigger,
|
|
15511
|
+
condition: entry.condition,
|
|
15512
|
+
};
|
|
15513
|
+
return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
|
|
14912
15514
|
}
|
|
14913
15515
|
/**
|
|
14914
15516
|
* Creates a PropertyDerivationOrchestrator for a dynamic form.
|
|
@@ -16326,35 +16928,35 @@ const BUILT_IN_FIELDS = [
|
|
|
16326
16928
|
// `row` is a virtual field type: it maps to ContainerFieldComponent, and
|
|
16327
16929
|
// the rowFieldMapper injects a `{ type: 'row' }` wrapper so the container
|
|
16328
16930
|
// renders the flex/grid layout via RowWrapperComponent.
|
|
16329
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
16931
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-C554hOxN.mjs'),
|
|
16330
16932
|
mapper: rowFieldMapper,
|
|
16331
16933
|
valueHandling: 'flatten',
|
|
16332
16934
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
16333
16935
|
},
|
|
16334
16936
|
{
|
|
16335
16937
|
name: 'group',
|
|
16336
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-
|
|
16938
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-BJ8j7Sh0.mjs'),
|
|
16337
16939
|
mapper: groupFieldMapper,
|
|
16338
16940
|
valueHandling: 'include',
|
|
16339
16941
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
16340
16942
|
},
|
|
16341
16943
|
{
|
|
16342
16944
|
name: 'array',
|
|
16343
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-
|
|
16945
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-GiaVHO9N.mjs'),
|
|
16344
16946
|
mapper: arrayFieldMapper,
|
|
16345
16947
|
valueHandling: 'include',
|
|
16346
16948
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
16347
16949
|
},
|
|
16348
16950
|
{
|
|
16349
16951
|
name: 'page',
|
|
16350
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-
|
|
16952
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-Bz2RpleR.mjs'),
|
|
16351
16953
|
mapper: pageFieldMapper,
|
|
16352
16954
|
valueHandling: 'flatten',
|
|
16353
16955
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
16354
16956
|
},
|
|
16355
16957
|
{
|
|
16356
16958
|
name: 'text',
|
|
16357
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-
|
|
16959
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-BGw6T-4h.mjs'),
|
|
16358
16960
|
mapper: textFieldMapper,
|
|
16359
16961
|
valueHandling: 'exclude',
|
|
16360
16962
|
...DISPLAY_FIELD_TYPES_BASE,
|
|
@@ -16366,7 +16968,7 @@ const BUILT_IN_FIELDS = [
|
|
|
16366
16968
|
},
|
|
16367
16969
|
{
|
|
16368
16970
|
name: 'container',
|
|
16369
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
16971
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-C554hOxN.mjs'),
|
|
16370
16972
|
mapper: containerFieldMapper,
|
|
16371
16973
|
valueHandling: 'flatten',
|
|
16372
16974
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
@@ -16375,7 +16977,7 @@ const BUILT_IN_FIELDS = [
|
|
|
16375
16977
|
const BUILT_IN_WRAPPERS = [
|
|
16376
16978
|
{
|
|
16377
16979
|
wrapperName: 'css',
|
|
16378
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-
|
|
16980
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-CnJgFWLB.mjs'),
|
|
16379
16981
|
},
|
|
16380
16982
|
{
|
|
16381
16983
|
wrapperName: 'row',
|
|
@@ -18168,4 +18770,4 @@ function extractErrorParams(error) {
|
|
|
18168
18770
|
*/
|
|
18169
18771
|
|
|
18170
18772
|
export { arrayFieldMapper as $, ADDON_ACTION_HANDLERS as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DEFAULT_PROPS as D, EventBus as E, FIELD_REGISTRY as F, GROUP_CONTEXT as G, NoopLogger as H, INITIALIZATION_TIMEOUT_MS as I, PopArrayItemEvent as J, PrependArrayItemEvent as K, PreviousPageEvent as L, MoveArrayItemEvent as M, NextPageEvent as N, RootFormRegistryService as O, PageChangeEvent as P, SubmitEvent as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, TextAddonComponent as U, WRAPPER_REGISTRY as V, WRAPPER_AUTO_ASSOCIATIONS as W, applyMetaToElement as X, applyValidator as Y, applyValidators as Z, arrayEvent as _, ADDON_ACTION_REGISTRY as a, textFieldMapper as a$, baseFieldMapper as a0, buildBaseInputs as a1, containerFieldMapper as a2, createField as a3, createFieldResolutionPipe as a4, createHiddenSignal as a5, createRenderReadySignal as a6, createSchemaFromFields as a7, createWrapperChainController as a8, createWrappers as a9, isGroupField as aA, isLeafField as aB, isPageField as aC, isRowField as aD, isSameWrapperChain as aE, isSimplifiedArrayField as aF, isValueBearingField as aG, isValueField as aH, isWrapperTypeDefinition as aI, isWrappersBundle as aJ, logAddonWarnings as aK, mapFieldToInputs as aL, omit as aM, pageFieldMapper as aN, provideAddonActions as aO, provideDynamicForm as aP, resolveDynamicValue as aQ, resolveNextButtonDisabled as aR, resolveNonFieldDisabled as aS, resolveNonFieldHidden as aT, resolveSubmitButtonDisabled as aU, resolveTokens as aV, resolveWrappers as aW, rowFieldMapper as aX, runPresetAction as aY, sanitizeFormConfig as aZ, sanitizeFormConfigPure as a_, derivedFromDeferred as aa, dynamicTextToObservable as ab, evaluateNonFieldDisabled as ac, evaluateNonFieldHidden as ad, field as ae, formConfig as af, formatAddonWarning as ag, getArrayLength as ah, getFieldDefaultValue as ai, getFieldValueHandling as aj, getNormalizedArrayMetadata as ak, groupFieldMapper as al, hasFormValue as am, injectAddonKindRegistry as an, injectFieldRegistry as ao, injectFieldSignalContext as ap, injectFieldsSupportingAddons as aq, interpolateParams as ar, isArrayField as as, isCheckedField as at, isContainerField as au, isContainerTypedField as av, isDisplayOnlyField as aw, isEqual as ax, isFieldBoundContext as ay, isFormStateCondition as az, ADDON_KIND_COMPONENT_CACHE as b, toReadonlyFieldTree as b0, validateFieldAddons as b1, validatePageNesting as b2, walkAndValidateAddons as b3, withCustomAddon as b4, withEventFormValue as b5, withLegacyStatusClasses as b6, withLoggerConfig as b7, withPreviousValue as b8, withValidationExecutionDefaults as b9, withValueExclusionDefaults as ba, wrapperProps as bb, writeToFieldValue as bc, ADDON_KIND_DEFINITIONS as c, ADDON_KIND_REGISTRY as d, ARRAY_CONTEXT as e, AppendArrayItemEvent as f, ArrayItemRegistryService as g, ComponentAddonComponent as h, ComponentInitializedEvent as i, ConsoleLogger as j, DEFAULT_VALIDATION_MESSAGES as k, DEFAULT_WRAPPERS as l, DF_FIELD_TEMPLATES as m, DfAddonSlot as n, DfFieldOutlet as o, DfTemplate as p, DynamicForm as q, DynamicFormError as r, DynamicFormLogger as s, DynamicTextPipe as t, EventDispatcher as u, FIELD_SIGNAL_CONTEXT as v, FORM_OPTIONS as w, FormClearEvent as x, FormResetEvent as y, InsertArrayItemEvent as z };
|
|
18171
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms
|
|
18773
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms--PuL0AKc.mjs.map
|