ngx-dynamic-entity 0.0.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/esm2022/lib/field-types/checkbox-field.component.mjs +73 -0
- package/esm2022/lib/field-types/date-field.component.mjs +82 -0
- package/esm2022/lib/field-types/dropdown-field.component.mjs +83 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +117 -0
- package/esm2022/lib/field-types/multi-select-field.component.mjs +91 -0
- package/esm2022/lib/field-types/number-field.component.mjs +73 -0
- package/esm2022/lib/field-types/text-field.component.mjs +74 -0
- package/esm2022/lib/field-types/textarea-field.component.mjs +73 -0
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +86 -0
- package/esm2022/lib/form/dynamic-form.component.mjs +167 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +23 -0
- package/esm2022/lib/services/config.service.mjs +58 -0
- package/esm2022/lib/services/entity-ref-registry.service.mjs +31 -0
- package/esm2022/lib/services/field-registry.service.mjs +50 -0
- package/esm2022/lib/services/hook-registry.service.mjs +26 -0
- package/esm2022/lib/services/rbac.service.mjs +52 -0
- package/esm2022/lib/services/validator-registry.service.mjs +49 -0
- package/esm2022/lib/services/version.service.mjs +40 -0
- package/esm2022/lib/table/dynamic-table.component.mjs +160 -0
- package/esm2022/lib/tokens/injection-tokens.mjs +16 -0
- package/esm2022/ngx-dynamic-entity.mjs +5 -0
- package/esm2022/public-api.mjs +26 -0
- package/fesm2022/ngx-dynamic-entity.mjs +1352 -0
- package/fesm2022/ngx-dynamic-entity.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/field-types/checkbox-field.component.d.ts +12 -0
- package/lib/field-types/date-field.component.d.ts +14 -0
- package/lib/field-types/dropdown-field.component.d.ts +13 -0
- package/lib/field-types/entity-ref-field.component.d.ts +26 -0
- package/lib/field-types/multi-select-field.component.d.ts +13 -0
- package/lib/field-types/number-field.component.d.ts +12 -0
- package/lib/field-types/text-field.component.d.ts +13 -0
- package/lib/field-types/textarea-field.component.d.ts +12 -0
- package/lib/form/dynamic-field/dynamic-field.component.d.ts +30 -0
- package/lib/form/dynamic-form.component.d.ts +52 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +29 -0
- package/lib/services/config.service.d.ts +25 -0
- package/lib/services/entity-ref-registry.service.d.ts +20 -0
- package/lib/services/field-registry.service.d.ts +22 -0
- package/lib/services/hook-registry.service.d.ts +12 -0
- package/lib/services/rbac.service.d.ts +32 -0
- package/lib/services/validator-registry.service.d.ts +19 -0
- package/lib/services/version.service.d.ts +26 -0
- package/lib/table/dynamic-table.component.d.ts +56 -0
- package/lib/tokens/injection-tokens.d.ts +16 -0
- package/package.json +37 -0
- package/public-api.d.ts +21 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Component, Input } from '@angular/core';
|
|
2
|
+
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/forms";
|
|
5
|
+
/** ADR-008: Shared field component contract — all 8 built-in types implement this. */
|
|
6
|
+
export class TextFieldComponent {
|
|
7
|
+
field;
|
|
8
|
+
control;
|
|
9
|
+
language = 'en';
|
|
10
|
+
readonly = false;
|
|
11
|
+
masked = false;
|
|
12
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: TextFieldComponent, isStandalone: true, selector: "ngx-text-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
14
|
+
<div class="ngx-field ngx-field--text" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
15
|
+
<label class="ngx-field__label">{{ field.label[language] || field.label['en'] }}</label>
|
|
16
|
+
@if (masked) {
|
|
17
|
+
<span class="ngx-field__value ngx-field__value--masked">XXXXXXXXX</span>
|
|
18
|
+
} @else if (readonly) {
|
|
19
|
+
<span class="ngx-field__value">{{ control.value }}</span>
|
|
20
|
+
} @else {
|
|
21
|
+
<input
|
|
22
|
+
class="ngx-field__input"
|
|
23
|
+
type="text"
|
|
24
|
+
[formControl]="$any(control)"
|
|
25
|
+
[placeholder]="field.placeholder?.[language] || field.placeholder?.['en'] || ''"
|
|
26
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
27
|
+
/>
|
|
28
|
+
@if (control.invalid && control.touched) {
|
|
29
|
+
<span class="ngx-field__error">This field has an error</span>
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
</div>
|
|
33
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
34
|
+
}
|
|
35
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextFieldComponent, decorators: [{
|
|
36
|
+
type: Component,
|
|
37
|
+
args: [{
|
|
38
|
+
selector: 'ngx-text-field',
|
|
39
|
+
standalone: true,
|
|
40
|
+
imports: [ReactiveFormsModule],
|
|
41
|
+
template: `
|
|
42
|
+
<div class="ngx-field ngx-field--text" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
43
|
+
<label class="ngx-field__label">{{ field.label[language] || field.label['en'] }}</label>
|
|
44
|
+
@if (masked) {
|
|
45
|
+
<span class="ngx-field__value ngx-field__value--masked">XXXXXXXXX</span>
|
|
46
|
+
} @else if (readonly) {
|
|
47
|
+
<span class="ngx-field__value">{{ control.value }}</span>
|
|
48
|
+
} @else {
|
|
49
|
+
<input
|
|
50
|
+
class="ngx-field__input"
|
|
51
|
+
type="text"
|
|
52
|
+
[formControl]="$any(control)"
|
|
53
|
+
[placeholder]="field.placeholder?.[language] || field.placeholder?.['en'] || ''"
|
|
54
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
55
|
+
/>
|
|
56
|
+
@if (control.invalid && control.touched) {
|
|
57
|
+
<span class="ngx-field__error">This field has an error</span>
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</div>
|
|
61
|
+
`,
|
|
62
|
+
}]
|
|
63
|
+
}], propDecorators: { field: [{
|
|
64
|
+
type: Input
|
|
65
|
+
}], control: [{
|
|
66
|
+
type: Input
|
|
67
|
+
}], language: [{
|
|
68
|
+
type: Input
|
|
69
|
+
}], readonly: [{
|
|
70
|
+
type: Input
|
|
71
|
+
}], masked: [{
|
|
72
|
+
type: Input
|
|
73
|
+
}] } });
|
|
74
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dC1maWVsZC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2ZpZWxkLXR5cGVzL3RleHQtZmllbGQuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pELE9BQU8sRUFBbUIsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7O0FBR3RFLHNGQUFzRjtBQTJCdEYsTUFBTSxPQUFPLGtCQUFrQjtJQUNwQixLQUFLLENBQWU7SUFDcEIsT0FBTyxDQUFtQjtJQUMxQixRQUFRLEdBQVcsSUFBSSxDQUFDO0lBQ3hCLFFBQVEsR0FBWSxLQUFLLENBQUM7SUFDMUIsTUFBTSxHQUFZLEtBQUssQ0FBQzt3R0FMdEIsa0JBQWtCOzRGQUFsQixrQkFBa0Isd0xBdEJuQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQlQsMkRBckJTLG1CQUFtQjs7NEZBdUJsQixrQkFBa0I7a0JBMUI5QixTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSxnQkFBZ0I7b0JBQzFCLFVBQVUsRUFBRSxJQUFJO29CQUNoQixPQUFPLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQztvQkFDOUIsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CVDtpQkFDRjs4QkFFVSxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxNQUFNO3NCQUFkLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBYnN0cmFjdENvbnRyb2wsIFJlYWN0aXZlRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgdHlwZSB7IEZpZWxkQ29uZmlnIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuXG4vKiogQURSLTAwODogU2hhcmVkIGZpZWxkIGNvbXBvbmVudCBjb250cmFjdCDigJQgYWxsIDggYnVpbHQtaW4gdHlwZXMgaW1wbGVtZW50IHRoaXMuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICduZ3gtdGV4dC1maWVsZCcsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtSZWFjdGl2ZUZvcm1zTW9kdWxlXSxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2IGNsYXNzPVwibmd4LWZpZWxkIG5neC1maWVsZC0tdGV4dFwiIFtjbGFzcy5uZ3gtZmllbGQtLXJlYWRvbmx5XT1cInJlYWRvbmx5XCIgW2NsYXNzLm5neC1maWVsZC0tbWFza2VkXT1cIm1hc2tlZFwiPlxuICAgICAgPGxhYmVsIGNsYXNzPVwibmd4LWZpZWxkX19sYWJlbFwiPnt7IGZpZWxkLmxhYmVsW2xhbmd1YWdlXSB8fCBmaWVsZC5sYWJlbFsnZW4nXSB9fTwvbGFiZWw+XG4gICAgICBAaWYgKG1hc2tlZCkge1xuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fdmFsdWUgbmd4LWZpZWxkX192YWx1ZS0tbWFza2VkXCI+WFhYWFhYWFhYPC9zcGFuPlxuICAgICAgfSBAZWxzZSBpZiAocmVhZG9ubHkpIHtcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlXCI+e3sgY29udHJvbC52YWx1ZSB9fTwvc3Bhbj5cbiAgICAgIH0gQGVsc2Uge1xuICAgICAgICA8aW5wdXRcbiAgICAgICAgICBjbGFzcz1cIm5neC1maWVsZF9faW5wdXRcIlxuICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICBbZm9ybUNvbnRyb2xdPVwiJGFueShjb250cm9sKVwiXG4gICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyPy5bbGFuZ3VhZ2VdIHx8IGZpZWxkLnBsYWNlaG9sZGVyPy5bJ2VuJ10gfHwgJydcIlxuICAgICAgICAgIFthdHRyLmRpc2FibGVkXT1cImZpZWxkLmRpc2FibGVkID8gdHJ1ZSA6IG51bGxcIlxuICAgICAgICAvPlxuICAgICAgICBAaWYgKGNvbnRyb2wuaW52YWxpZCAmJiBjb250cm9sLnRvdWNoZWQpIHtcbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fZXJyb3JcIj5UaGlzIGZpZWxkIGhhcyBhbiBlcnJvcjwvc3Bhbj5cbiAgICAgICAgfVxuICAgICAgfVxuICAgIDwvZGl2PlxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBUZXh0RmllbGRDb21wb25lbnQge1xuICBASW5wdXQoKSBmaWVsZCE6IEZpZWxkQ29uZmlnO1xuICBASW5wdXQoKSBjb250cm9sITogQWJzdHJhY3RDb250cm9sO1xuICBASW5wdXQoKSBsYW5ndWFnZTogc3RyaW5nID0gJ2VuJztcbiAgQElucHV0KCkgcmVhZG9ubHk6IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgbWFza2VkOiBib29sZWFuID0gZmFsc2U7XG59XG4iXX0=
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Component, Input } from '@angular/core';
|
|
2
|
+
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/forms";
|
|
5
|
+
export class TextareaFieldComponent {
|
|
6
|
+
field;
|
|
7
|
+
control;
|
|
8
|
+
language = 'en';
|
|
9
|
+
readonly = false;
|
|
10
|
+
masked = false;
|
|
11
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: TextareaFieldComponent, isStandalone: true, selector: "ngx-textarea-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
13
|
+
<div class="ngx-field ngx-field--textarea" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
14
|
+
<label class="ngx-field__label">{{ field.label[language] || field.label['en'] }}</label>
|
|
15
|
+
@if (masked) {
|
|
16
|
+
<span class="ngx-field__value ngx-field__value--masked">XXXXXXXXX</span>
|
|
17
|
+
} @else if (readonly) {
|
|
18
|
+
<span class="ngx-field__value">{{ control.value }}</span>
|
|
19
|
+
} @else {
|
|
20
|
+
<textarea
|
|
21
|
+
class="ngx-field__input"
|
|
22
|
+
[formControl]="$any(control)"
|
|
23
|
+
[placeholder]="field.placeholder?.[language] || field.placeholder?.['en'] || ''"
|
|
24
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
25
|
+
rows="3"
|
|
26
|
+
></textarea>
|
|
27
|
+
@if (control.invalid && control.touched) {
|
|
28
|
+
<span class="ngx-field__error">This field has an error</span>
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
32
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
33
|
+
}
|
|
34
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextareaFieldComponent, decorators: [{
|
|
35
|
+
type: Component,
|
|
36
|
+
args: [{
|
|
37
|
+
selector: 'ngx-textarea-field',
|
|
38
|
+
standalone: true,
|
|
39
|
+
imports: [ReactiveFormsModule],
|
|
40
|
+
template: `
|
|
41
|
+
<div class="ngx-field ngx-field--textarea" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
42
|
+
<label class="ngx-field__label">{{ field.label[language] || field.label['en'] }}</label>
|
|
43
|
+
@if (masked) {
|
|
44
|
+
<span class="ngx-field__value ngx-field__value--masked">XXXXXXXXX</span>
|
|
45
|
+
} @else if (readonly) {
|
|
46
|
+
<span class="ngx-field__value">{{ control.value }}</span>
|
|
47
|
+
} @else {
|
|
48
|
+
<textarea
|
|
49
|
+
class="ngx-field__input"
|
|
50
|
+
[formControl]="$any(control)"
|
|
51
|
+
[placeholder]="field.placeholder?.[language] || field.placeholder?.['en'] || ''"
|
|
52
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
53
|
+
rows="3"
|
|
54
|
+
></textarea>
|
|
55
|
+
@if (control.invalid && control.touched) {
|
|
56
|
+
<span class="ngx-field__error">This field has an error</span>
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
</div>
|
|
60
|
+
`,
|
|
61
|
+
}]
|
|
62
|
+
}], propDecorators: { field: [{
|
|
63
|
+
type: Input
|
|
64
|
+
}], control: [{
|
|
65
|
+
type: Input
|
|
66
|
+
}], language: [{
|
|
67
|
+
type: Input
|
|
68
|
+
}], readonly: [{
|
|
69
|
+
type: Input
|
|
70
|
+
}], masked: [{
|
|
71
|
+
type: Input
|
|
72
|
+
}] } });
|
|
73
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dGFyZWEtZmllbGQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9maWVsZC10eXBlcy90ZXh0YXJlYS1maWVsZC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDakQsT0FBTyxFQUFtQixtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDOzs7QUE2QnRFLE1BQU0sT0FBTyxzQkFBc0I7SUFDeEIsS0FBSyxDQUFlO0lBQ3BCLE9BQU8sQ0FBbUI7SUFDMUIsUUFBUSxHQUFXLElBQUksQ0FBQztJQUN4QixRQUFRLEdBQVksS0FBSyxDQUFDO0lBQzFCLE1BQU0sR0FBWSxLQUFLLENBQUM7d0dBTHRCLHNCQUFzQjs0RkFBdEIsc0JBQXNCLDRMQXRCdkI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JULDJEQXJCUyxtQkFBbUI7OzRGQXVCbEIsc0JBQXNCO2tCQTFCbEMsU0FBUzttQkFBQztvQkFDVCxRQUFRLEVBQUUsb0JBQW9CO29CQUM5QixVQUFVLEVBQUUsSUFBSTtvQkFDaEIsT0FBTyxFQUFFLENBQUMsbUJBQW1CLENBQUM7b0JBQzlCLFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQlQ7aUJBQ0Y7OEJBRVUsS0FBSztzQkFBYixLQUFLO2dCQUNHLE9BQU87c0JBQWYsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csTUFBTTtzQkFBZCxLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBJbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBSZWFjdGl2ZUZvcm1zTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHR5cGUgeyBGaWVsZENvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LXRleHRhcmVhLWZpZWxkJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1JlYWN0aXZlRm9ybXNNb2R1bGVdLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXYgY2xhc3M9XCJuZ3gtZmllbGQgbmd4LWZpZWxkLS10ZXh0YXJlYVwiIFtjbGFzcy5uZ3gtZmllbGQtLXJlYWRvbmx5XT1cInJlYWRvbmx5XCIgW2NsYXNzLm5neC1maWVsZC0tbWFza2VkXT1cIm1hc2tlZFwiPlxuICAgICAgPGxhYmVsIGNsYXNzPVwibmd4LWZpZWxkX19sYWJlbFwiPnt7IGZpZWxkLmxhYmVsW2xhbmd1YWdlXSB8fCBmaWVsZC5sYWJlbFsnZW4nXSB9fTwvbGFiZWw+XG4gICAgICBAaWYgKG1hc2tlZCkge1xuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fdmFsdWUgbmd4LWZpZWxkX192YWx1ZS0tbWFza2VkXCI+WFhYWFhYWFhYPC9zcGFuPlxuICAgICAgfSBAZWxzZSBpZiAocmVhZG9ubHkpIHtcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlXCI+e3sgY29udHJvbC52YWx1ZSB9fTwvc3Bhbj5cbiAgICAgIH0gQGVsc2Uge1xuICAgICAgICA8dGV4dGFyZWFcbiAgICAgICAgICBjbGFzcz1cIm5neC1maWVsZF9faW5wdXRcIlxuICAgICAgICAgIFtmb3JtQ29udHJvbF09XCIkYW55KGNvbnRyb2wpXCJcbiAgICAgICAgICBbcGxhY2Vob2xkZXJdPVwiZmllbGQucGxhY2Vob2xkZXI/LltsYW5ndWFnZV0gfHwgZmllbGQucGxhY2Vob2xkZXI/LlsnZW4nXSB8fCAnJ1wiXG4gICAgICAgICAgW2F0dHIuZGlzYWJsZWRdPVwiZmllbGQuZGlzYWJsZWQgPyB0cnVlIDogbnVsbFwiXG4gICAgICAgICAgcm93cz1cIjNcIlxuICAgICAgICA+PC90ZXh0YXJlYT5cbiAgICAgICAgQGlmIChjb250cm9sLmludmFsaWQgJiYgY29udHJvbC50b3VjaGVkKSB7XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX2Vycm9yXCI+VGhpcyBmaWVsZCBoYXMgYW4gZXJyb3I8L3NwYW4+XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgVGV4dGFyZWFGaWVsZENvbXBvbmVudCB7XG4gIEBJbnB1dCgpIGZpZWxkITogRmllbGRDb25maWc7XG4gIEBJbnB1dCgpIGNvbnRyb2whOiBBYnN0cmFjdENvbnRyb2w7XG4gIEBJbnB1dCgpIGxhbmd1YWdlOiBzdHJpbmcgPSAnZW4nO1xuICBASW5wdXQoKSByZWFkb25seTogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBtYXNrZWQ6IGJvb2xlYW4gPSBmYWxzZTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Component, Input, ViewChild, ViewContainerRef, inject, } from '@angular/core';
|
|
2
|
+
import { FieldRegistryService } from '../../services/field-registry.service';
|
|
3
|
+
import { RbacService } from '../../services/rbac.service';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* DynamicFieldComponent — mounts the correct field component for a given FieldConfig.
|
|
7
|
+
* Uses ViewContainerRef.createComponent() and passes inputs via setInput() (ADR-008).
|
|
8
|
+
* Never add field-type-specific logic here — this component must remain generic.
|
|
9
|
+
*
|
|
10
|
+
* The 5 inputs (field, control, language, readonly, masked) are passed to every mounted
|
|
11
|
+
* component uniformly — no special-casing per type.
|
|
12
|
+
*/
|
|
13
|
+
export class DynamicFieldComponent {
|
|
14
|
+
field;
|
|
15
|
+
control;
|
|
16
|
+
config;
|
|
17
|
+
language = 'en';
|
|
18
|
+
readonly = false;
|
|
19
|
+
userRoles = [];
|
|
20
|
+
fieldHost;
|
|
21
|
+
fieldRegistry = inject(FieldRegistryService);
|
|
22
|
+
rbacService = inject(RbacService);
|
|
23
|
+
mountedComponent = null;
|
|
24
|
+
ngOnChanges(_changes) {
|
|
25
|
+
this.mountField();
|
|
26
|
+
}
|
|
27
|
+
mountField() {
|
|
28
|
+
if (!this.field || !this.control)
|
|
29
|
+
return;
|
|
30
|
+
const ComponentClass = this.fieldRegistry.resolve(this.field.type);
|
|
31
|
+
if (!ComponentClass)
|
|
32
|
+
return; // Unknown field type — render nothing
|
|
33
|
+
// Determine masking for this specific field
|
|
34
|
+
const tab = (this.config?.tabs || []).find(t => t.id === this.field.tab);
|
|
35
|
+
const masked = this.rbacService.shouldMaskField(this.field, tab, this.config, this.userRoles);
|
|
36
|
+
// Re-use existing component if same type, otherwise recreate
|
|
37
|
+
if (this.mountedComponent && this.mountedComponent instanceof ComponentClass) {
|
|
38
|
+
this.setInputs(this.mountedComponent, masked);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.fieldHost.clear();
|
|
42
|
+
const ref = this.fieldHost.createComponent(ComponentClass);
|
|
43
|
+
this.mountedComponent = ref.instance;
|
|
44
|
+
this.setInputs(ref.instance, masked);
|
|
45
|
+
ref.changeDetectorRef.markForCheck();
|
|
46
|
+
}
|
|
47
|
+
/** Pass all 5 contract inputs via setInput() — uniform for all types (ADR-008) */
|
|
48
|
+
setInputs(instance, masked) {
|
|
49
|
+
if ('field' in instance)
|
|
50
|
+
instance.field = this.field;
|
|
51
|
+
if ('control' in instance)
|
|
52
|
+
instance.control = this.control;
|
|
53
|
+
if ('language' in instance)
|
|
54
|
+
instance.language = this.language;
|
|
55
|
+
if ('readonly' in instance)
|
|
56
|
+
instance.readonly = this.readonly || !!this.field.readonly;
|
|
57
|
+
if ('masked' in instance)
|
|
58
|
+
instance.masked = masked;
|
|
59
|
+
}
|
|
60
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
61
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DynamicFieldComponent, isStandalone: true, selector: "ngx-dynamic-field", inputs: { field: "field", control: "control", config: "config", language: "language", readonly: "readonly", userRoles: "userRoles" }, viewQueries: [{ propertyName: "fieldHost", first: true, predicate: ["fieldHost"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: `<ng-container #fieldHost></ng-container>`, isInline: true });
|
|
62
|
+
}
|
|
63
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicFieldComponent, decorators: [{
|
|
64
|
+
type: Component,
|
|
65
|
+
args: [{
|
|
66
|
+
selector: 'ngx-dynamic-field',
|
|
67
|
+
standalone: true,
|
|
68
|
+
template: `<ng-container #fieldHost></ng-container>`,
|
|
69
|
+
}]
|
|
70
|
+
}], propDecorators: { field: [{
|
|
71
|
+
type: Input
|
|
72
|
+
}], control: [{
|
|
73
|
+
type: Input
|
|
74
|
+
}], config: [{
|
|
75
|
+
type: Input
|
|
76
|
+
}], language: [{
|
|
77
|
+
type: Input
|
|
78
|
+
}], readonly: [{
|
|
79
|
+
type: Input
|
|
80
|
+
}], userRoles: [{
|
|
81
|
+
type: Input
|
|
82
|
+
}], fieldHost: [{
|
|
83
|
+
type: ViewChild,
|
|
84
|
+
args: ['fieldHost', { read: ViewContainerRef, static: true }]
|
|
85
|
+
}] } });
|
|
86
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1maWVsZC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1maWVsZC9keW5hbWljLWZpZWxkLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULEtBQUssRUFHTCxTQUFTLEVBQ1QsZ0JBQWdCLEVBQ2hCLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUd2QixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUM3RSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sNkJBQTZCLENBQUM7O0FBRTFEOzs7Ozs7O0dBT0c7QUFNSCxNQUFNLE9BQU8scUJBQXFCO0lBQ3ZCLEtBQUssQ0FBZTtJQUNwQixPQUFPLENBQW1CO0lBQzFCLE1BQU0sQ0FBZ0I7SUFDdEIsUUFBUSxHQUFXLElBQUksQ0FBQztJQUN4QixRQUFRLEdBQVksS0FBSyxDQUFDO0lBQzFCLFNBQVMsR0FBYSxFQUFFLENBQUM7SUFHakIsU0FBUyxDQUFvQjtJQUU3QixhQUFhLEdBQUcsTUFBTSxDQUFDLG9CQUFvQixDQUFDLENBQUM7SUFDN0MsV0FBVyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUUzQyxnQkFBZ0IsR0FBUSxJQUFJLENBQUM7SUFFckMsV0FBVyxDQUFDLFFBQXVCO1FBQ2pDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNwQixDQUFDO0lBRU8sVUFBVTtRQUNoQixJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTztRQUV6QyxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ25FLElBQUksQ0FBQyxjQUFjO1lBQUUsT0FBTyxDQUFDLHNDQUFzQztRQUVuRSw0Q0FBNEM7UUFDNUMsTUFBTSxHQUFHLEdBQTBCLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ2hHLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRTlGLDZEQUE2RDtRQUM3RCxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLFlBQVksY0FBYyxFQUFFLENBQUM7WUFDN0UsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDOUMsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDO1FBQ3JDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNyQyxHQUFHLENBQUMsaUJBQWlCLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDdkMsQ0FBQztJQUVELGtGQUFrRjtJQUMxRSxTQUFTLENBQUMsUUFBYSxFQUFFLE1BQWU7UUFDOUMsSUFBSSxPQUFPLElBQUksUUFBUTtZQUFFLFFBQVEsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUNyRCxJQUFJLFNBQVMsSUFBSSxRQUFRO1lBQUUsUUFBUSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQzNELElBQUksVUFBVSxJQUFJLFFBQVE7WUFBRSxRQUFRLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7UUFDOUQsSUFBSSxVQUFVLElBQUksUUFBUTtZQUFFLFFBQVEsQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUM7UUFDdkYsSUFBSSxRQUFRLElBQUksUUFBUTtZQUFFLFFBQVEsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3JELENBQUM7d0dBbERVLHFCQUFxQjs0RkFBckIscUJBQXFCLHNTQVFBLGdCQUFnQixnRUFWdEMsMENBQTBDOzs0RkFFekMscUJBQXFCO2tCQUxqQyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSxtQkFBbUI7b0JBQzdCLFVBQVUsRUFBRSxJQUFJO29CQUNoQixRQUFRLEVBQUUsMENBQTBDO2lCQUNyRDs4QkFFVSxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNHLE1BQU07c0JBQWQsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csU0FBUztzQkFBakIsS0FBSztnQkFHVyxTQUFTO3NCQUR6QixTQUFTO3VCQUFDLFdBQVcsRUFBRSxFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBJbnB1dCxcbiAgT25DaGFuZ2VzLFxuICBTaW1wbGVDaGFuZ2VzLFxuICBWaWV3Q2hpbGQsXG4gIFZpZXdDb250YWluZXJSZWYsXG4gIGluamVjdCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBYnN0cmFjdENvbnRyb2wgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgdHlwZSB7IEZpZWxkQ29uZmlnLCBFbnRpdHlDb25maWcsIFRhYkNvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IEZpZWxkUmVnaXN0cnlTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvZmllbGQtcmVnaXN0cnkuc2VydmljZSc7XG5pbXBvcnQgeyBSYmFjU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3JiYWMuc2VydmljZSc7XG5cbi8qKlxuICogRHluYW1pY0ZpZWxkQ29tcG9uZW50IOKAlCBtb3VudHMgdGhlIGNvcnJlY3QgZmllbGQgY29tcG9uZW50IGZvciBhIGdpdmVuIEZpZWxkQ29uZmlnLlxuICogVXNlcyBWaWV3Q29udGFpbmVyUmVmLmNyZWF0ZUNvbXBvbmVudCgpIGFuZCBwYXNzZXMgaW5wdXRzIHZpYSBzZXRJbnB1dCgpIChBRFItMDA4KS5cbiAqIE5ldmVyIGFkZCBmaWVsZC10eXBlLXNwZWNpZmljIGxvZ2ljIGhlcmUg4oCUIHRoaXMgY29tcG9uZW50IG11c3QgcmVtYWluIGdlbmVyaWMuXG4gKlxuICogVGhlIDUgaW5wdXRzIChmaWVsZCwgY29udHJvbCwgbGFuZ3VhZ2UsIHJlYWRvbmx5LCBtYXNrZWQpIGFyZSBwYXNzZWQgdG8gZXZlcnkgbW91bnRlZFxuICogY29tcG9uZW50IHVuaWZvcm1seSDigJQgbm8gc3BlY2lhbC1jYXNpbmcgcGVyIHR5cGUuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1keW5hbWljLWZpZWxkJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgdGVtcGxhdGU6IGA8bmctY29udGFpbmVyICNmaWVsZEhvc3Q+PC9uZy1jb250YWluZXI+YCxcbn0pXG5leHBvcnQgY2xhc3MgRHluYW1pY0ZpZWxkQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzIHtcbiAgQElucHV0KCkgZmllbGQhOiBGaWVsZENvbmZpZztcbiAgQElucHV0KCkgY29udHJvbCE6IEFic3RyYWN0Q29udHJvbDtcbiAgQElucHV0KCkgY29uZmlnITogRW50aXR5Q29uZmlnO1xuICBASW5wdXQoKSBsYW5ndWFnZTogc3RyaW5nID0gJ2VuJztcbiAgQElucHV0KCkgcmVhZG9ubHk6IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgdXNlclJvbGVzOiBzdHJpbmdbXSA9IFtdO1xuXG4gIEBWaWV3Q2hpbGQoJ2ZpZWxkSG9zdCcsIHsgcmVhZDogVmlld0NvbnRhaW5lclJlZiwgc3RhdGljOiB0cnVlIH0pXG4gIHByaXZhdGUgcmVhZG9ubHkgZmllbGRIb3N0ITogVmlld0NvbnRhaW5lclJlZjtcblxuICBwcml2YXRlIHJlYWRvbmx5IGZpZWxkUmVnaXN0cnkgPSBpbmplY3QoRmllbGRSZWdpc3RyeVNlcnZpY2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IHJiYWNTZXJ2aWNlID0gaW5qZWN0KFJiYWNTZXJ2aWNlKTtcblxuICBwcml2YXRlIG1vdW50ZWRDb21wb25lbnQ6IGFueSA9IG51bGw7XG5cbiAgbmdPbkNoYW5nZXMoX2NoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICB0aGlzLm1vdW50RmllbGQoKTtcbiAgfVxuXG4gIHByaXZhdGUgbW91bnRGaWVsZCgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuZmllbGQgfHwgIXRoaXMuY29udHJvbCkgcmV0dXJuO1xuXG4gICAgY29uc3QgQ29tcG9uZW50Q2xhc3MgPSB0aGlzLmZpZWxkUmVnaXN0cnkucmVzb2x2ZSh0aGlzLmZpZWxkLnR5cGUpO1xuICAgIGlmICghQ29tcG9uZW50Q2xhc3MpIHJldHVybjsgLy8gVW5rbm93biBmaWVsZCB0eXBlIOKAlCByZW5kZXIgbm90aGluZ1xuXG4gICAgLy8gRGV0ZXJtaW5lIG1hc2tpbmcgZm9yIHRoaXMgc3BlY2lmaWMgZmllbGRcbiAgICBjb25zdCB0YWI6IFRhYkNvbmZpZyB8IHVuZGVmaW5lZCA9ICh0aGlzLmNvbmZpZz8udGFicyB8fCBbXSkuZmluZCh0ID0+IHQuaWQgPT09IHRoaXMuZmllbGQudGFiKTtcbiAgICBjb25zdCBtYXNrZWQgPSB0aGlzLnJiYWNTZXJ2aWNlLnNob3VsZE1hc2tGaWVsZCh0aGlzLmZpZWxkLCB0YWIsIHRoaXMuY29uZmlnLCB0aGlzLnVzZXJSb2xlcyk7XG5cbiAgICAvLyBSZS11c2UgZXhpc3RpbmcgY29tcG9uZW50IGlmIHNhbWUgdHlwZSwgb3RoZXJ3aXNlIHJlY3JlYXRlXG4gICAgaWYgKHRoaXMubW91bnRlZENvbXBvbmVudCAmJiB0aGlzLm1vdW50ZWRDb21wb25lbnQgaW5zdGFuY2VvZiBDb21wb25lbnRDbGFzcykge1xuICAgICAgdGhpcy5zZXRJbnB1dHModGhpcy5tb3VudGVkQ29tcG9uZW50LCBtYXNrZWQpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuZmllbGRIb3N0LmNsZWFyKCk7XG4gICAgY29uc3QgcmVmID0gdGhpcy5maWVsZEhvc3QuY3JlYXRlQ29tcG9uZW50KENvbXBvbmVudENsYXNzKTtcbiAgICB0aGlzLm1vdW50ZWRDb21wb25lbnQgPSByZWYuaW5zdGFuY2U7XG4gICAgdGhpcy5zZXRJbnB1dHMocmVmLmluc3RhbmNlLCBtYXNrZWQpO1xuICAgIHJlZi5jaGFuZ2VEZXRlY3RvclJlZi5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIC8qKiBQYXNzIGFsbCA1IGNvbnRyYWN0IGlucHV0cyB2aWEgc2V0SW5wdXQoKSDigJQgdW5pZm9ybSBmb3IgYWxsIHR5cGVzIChBRFItMDA4KSAqL1xuICBwcml2YXRlIHNldElucHV0cyhpbnN0YW5jZTogYW55LCBtYXNrZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoJ2ZpZWxkJyBpbiBpbnN0YW5jZSkgaW5zdGFuY2UuZmllbGQgPSB0aGlzLmZpZWxkO1xuICAgIGlmICgnY29udHJvbCcgaW4gaW5zdGFuY2UpIGluc3RhbmNlLmNvbnRyb2wgPSB0aGlzLmNvbnRyb2w7XG4gICAgaWYgKCdsYW5ndWFnZScgaW4gaW5zdGFuY2UpIGluc3RhbmNlLmxhbmd1YWdlID0gdGhpcy5sYW5ndWFnZTtcbiAgICBpZiAoJ3JlYWRvbmx5JyBpbiBpbnN0YW5jZSkgaW5zdGFuY2UucmVhZG9ubHkgPSB0aGlzLnJlYWRvbmx5IHx8ICEhdGhpcy5maWVsZC5yZWFkb25seTtcbiAgICBpZiAoJ21hc2tlZCcgaW4gaW5zdGFuY2UpIGluc3RhbmNlLm1hc2tlZCA9IG1hc2tlZDtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal, inject, } from '@angular/core';
|
|
2
|
+
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import { DynamicFieldComponent } from './dynamic-field/dynamic-field.component';
|
|
4
|
+
import { ValidatorRegistryService } from '../services/validator-registry.service';
|
|
5
|
+
import { HookRegistryService } from '../services/hook-registry.service';
|
|
6
|
+
import { VersionService } from '../services/version.service';
|
|
7
|
+
import { RbacService } from '../services/rbac.service';
|
|
8
|
+
import * as i0 from "@angular/core";
|
|
9
|
+
import * as i1 from "@angular/forms";
|
|
10
|
+
/**
|
|
11
|
+
* DynamicFormComponent — the main form component.
|
|
12
|
+
* Renders a reactive form from EntityConfig with tab support, dependencies,
|
|
13
|
+
* migration banner, and RBAC-gated submission.
|
|
14
|
+
*
|
|
15
|
+
* ONEHERMES pattern: isSaving state lock prevents subscription cyclones (ADR-002).
|
|
16
|
+
* ONEHERMES bug fix: visibility rules re-evaluated after initialData loads (form-fixes doc).
|
|
17
|
+
* ONEHERMES bug fix: use @for + @if separately, not both on same element (form-fixes doc).
|
|
18
|
+
*/
|
|
19
|
+
export class DynamicFormComponent {
|
|
20
|
+
// ─── Inputs ───────────────────────────────────────────────────────────────
|
|
21
|
+
config;
|
|
22
|
+
initialData;
|
|
23
|
+
userRoles = [];
|
|
24
|
+
language = 'en';
|
|
25
|
+
readonly = false;
|
|
26
|
+
loading = false;
|
|
27
|
+
error = null;
|
|
28
|
+
// ─── Outputs ──────────────────────────────────────────────────────────────
|
|
29
|
+
formSubmit = new EventEmitter();
|
|
30
|
+
formChange = new EventEmitter();
|
|
31
|
+
formReset = new EventEmitter();
|
|
32
|
+
// ─── Services ─────────────────────────────────────────────────────────────
|
|
33
|
+
fb = inject(FormBuilder);
|
|
34
|
+
validatorRegistry = inject(ValidatorRegistryService);
|
|
35
|
+
hookRegistry = inject(HookRegistryService);
|
|
36
|
+
versionService = inject(VersionService);
|
|
37
|
+
rbacService = inject(RbacService);
|
|
38
|
+
// ─── Signals (local reactive state) ───────────────────────────────────────
|
|
39
|
+
activeTab = signal('');
|
|
40
|
+
isSaving = signal(false); // ADR-002: isSaving lock prevents API spam
|
|
41
|
+
// ─── Form ─────────────────────────────────────────────────────────────────
|
|
42
|
+
form;
|
|
43
|
+
// ─── Computed ─────────────────────────────────────────────────────────────
|
|
44
|
+
get tabs() {
|
|
45
|
+
return (this.config?.tabs || []).sort((a, b) => a.order - b.order);
|
|
46
|
+
}
|
|
47
|
+
get fieldsForActiveTab() {
|
|
48
|
+
const tabId = this.activeTab();
|
|
49
|
+
return (this.config?.fields || []).filter(f => {
|
|
50
|
+
if (!this.tabs.length)
|
|
51
|
+
return true; // No tabs — show all
|
|
52
|
+
return f.tab === tabId || (!f.tab && tabId === this.tabs[0]?.id);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
get permissions() {
|
|
56
|
+
return this.rbacService.getPermissions(this.config, this.userRoles);
|
|
57
|
+
}
|
|
58
|
+
get needsMigration() {
|
|
59
|
+
return this.versionService.needsMigration(this.initialData || {}, this.config);
|
|
60
|
+
}
|
|
61
|
+
get canSubmit() {
|
|
62
|
+
return (this.permissions.canEdit &&
|
|
63
|
+
!this.readonly &&
|
|
64
|
+
!this.isSaving() &&
|
|
65
|
+
!this.versionService.shouldBlockSubmit(this.initialData || {}, this.config));
|
|
66
|
+
}
|
|
67
|
+
// ─── Lifecycle ────────────────────────────────────────────────────────────
|
|
68
|
+
ngOnChanges(changes) {
|
|
69
|
+
if (changes['config'] && this.config) {
|
|
70
|
+
this.buildForm();
|
|
71
|
+
}
|
|
72
|
+
// ONEHERMES fix: re-apply data after initialData loads (form-fixes doc)
|
|
73
|
+
if (changes['initialData'] && this.form && this.initialData) {
|
|
74
|
+
this.patchForm(this.initialData);
|
|
75
|
+
}
|
|
76
|
+
if (changes['config'] && this.tabs.length) {
|
|
77
|
+
this.activeTab.set(this.tabs[0]?.id || '');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
buildForm() {
|
|
81
|
+
const controls = {};
|
|
82
|
+
for (const field of this.config.fields || []) {
|
|
83
|
+
if (field.isSystem)
|
|
84
|
+
continue; // System fields not in form controls
|
|
85
|
+
const validators = this.validatorRegistry.resolveAll(field.validators);
|
|
86
|
+
controls[field.id] = [field.defaultValue ?? null, validators];
|
|
87
|
+
}
|
|
88
|
+
this.form = this.fb.group(controls);
|
|
89
|
+
// Emit changes via formChange output
|
|
90
|
+
this.form.valueChanges.subscribe(value => this.formChange.emit(value));
|
|
91
|
+
// Apply initial data after form builds
|
|
92
|
+
if (this.initialData) {
|
|
93
|
+
this.patchForm(this.initialData);
|
|
94
|
+
}
|
|
95
|
+
if (this.tabs.length && !this.activeTab()) {
|
|
96
|
+
this.activeTab.set(this.tabs[0].id);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
patchForm(data) {
|
|
100
|
+
// Filter empty objects (ONEHERMES array field empty row bug fix)
|
|
101
|
+
const cleanData = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
|
|
102
|
+
this.form.patchValue(cleanData, { emitEvent: false });
|
|
103
|
+
}
|
|
104
|
+
// ─── Actions ──────────────────────────────────────────────────────────────
|
|
105
|
+
setActiveTab(tabId) {
|
|
106
|
+
this.activeTab.set(tabId);
|
|
107
|
+
}
|
|
108
|
+
async submit() {
|
|
109
|
+
if (!this.canSubmit || this.form.invalid) {
|
|
110
|
+
this.form.markAllAsTouched();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// ADR-002: isSaving lock — prevents subscription cyclones / duplicate API calls
|
|
114
|
+
if (this.isSaving())
|
|
115
|
+
return;
|
|
116
|
+
this.isSaving.set(true);
|
|
117
|
+
try {
|
|
118
|
+
let data = { ...this.form.value };
|
|
119
|
+
// Run pre-submit hook (Angular-side)
|
|
120
|
+
data = await this.hookRegistry.run(`${this.config.entity}:beforeSave`, data, {
|
|
121
|
+
config: this.config,
|
|
122
|
+
userRoles: this.userRoles,
|
|
123
|
+
});
|
|
124
|
+
this.formSubmit.emit(data);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
// Reset saving lock in the consumer's success/error handler — here we just unblock
|
|
128
|
+
this.isSaving.set(false);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
reset() {
|
|
132
|
+
this.form.reset();
|
|
133
|
+
if (this.initialData)
|
|
134
|
+
this.patchForm(this.initialData);
|
|
135
|
+
this.formReset.emit();
|
|
136
|
+
}
|
|
137
|
+
getControl(fieldId) {
|
|
138
|
+
return this.form?.get(fieldId);
|
|
139
|
+
}
|
|
140
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
141
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: DynamicFormComponent, isStandalone: true, selector: "ngx-dynamic-form", inputs: { config: "config", initialData: "initialData", userRoles: "userRoles", language: "language", readonly: "readonly", loading: "loading", error: "error" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset" }, usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" novalidate>\n\n <!-- Migration banner \u2014 visible in graceful mode when record is stale -->\n @if (needsMigration) {\n <div class=\"ngx-form__migration-banner\" role=\"alert\">\n \u26A0\uFE0F This record was created with an older configuration version.\n @if (versionService.shouldBlockSubmit(initialData || {}, config)) {\n Please run migration before editing.\n } @else {\n It will be updated to the latest version on save.\n }\n </div>\n }\n\n <!-- Error message -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\">{{ error }}</div>\n }\n\n <!-- Tab bar \u2014 only if tabs defined -->\n @if (tabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\">\n <!-- ONEHERMES fix: @for and @if on separate elements, not same (form-fixes doc) -->\n @for (tab of tabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ tab.label[language] || tab.label['en'] }}\n </button>\n }\n </div>\n }\n\n <!-- Fields for active tab -->\n <div class=\"ngx-form__panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n @if (field.visible !== false) {\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"readonly || !!field.readonly\"\n [userRoles]=\"userRoles\"\n />\n }\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\">\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n (click)=\"reset()\"\n [disabled]=\"isSaving()\"\n >\n Reset\n </button>\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n [disabled]=\"!canSubmit || form.invalid\"\n >\n @if (isSaving() || loading) {\n Saving\u2026\n } @else {\n Save\n }\n </button>\n </div>\n }\n\n</form>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "language", "readonly", "userRoles"] }] });
|
|
142
|
+
}
|
|
143
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicFormComponent, decorators: [{
|
|
144
|
+
type: Component,
|
|
145
|
+
args: [{ selector: 'ngx-dynamic-form', standalone: true, imports: [ReactiveFormsModule, DynamicFieldComponent], template: "<form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" novalidate>\n\n <!-- Migration banner \u2014 visible in graceful mode when record is stale -->\n @if (needsMigration) {\n <div class=\"ngx-form__migration-banner\" role=\"alert\">\n \u26A0\uFE0F This record was created with an older configuration version.\n @if (versionService.shouldBlockSubmit(initialData || {}, config)) {\n Please run migration before editing.\n } @else {\n It will be updated to the latest version on save.\n }\n </div>\n }\n\n <!-- Error message -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\">{{ error }}</div>\n }\n\n <!-- Tab bar \u2014 only if tabs defined -->\n @if (tabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\">\n <!-- ONEHERMES fix: @for and @if on separate elements, not same (form-fixes doc) -->\n @for (tab of tabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ tab.label[language] || tab.label['en'] }}\n </button>\n }\n </div>\n }\n\n <!-- Fields for active tab -->\n <div class=\"ngx-form__panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n @if (field.visible !== false) {\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"readonly || !!field.readonly\"\n [userRoles]=\"userRoles\"\n />\n }\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\">\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n (click)=\"reset()\"\n [disabled]=\"isSaving()\"\n >\n Reset\n </button>\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n [disabled]=\"!canSubmit || form.invalid\"\n >\n @if (isSaving() || loading) {\n Saving\u2026\n } @else {\n Save\n }\n </button>\n </div>\n }\n\n</form>\n" }]
|
|
146
|
+
}], propDecorators: { config: [{
|
|
147
|
+
type: Input
|
|
148
|
+
}], initialData: [{
|
|
149
|
+
type: Input
|
|
150
|
+
}], userRoles: [{
|
|
151
|
+
type: Input
|
|
152
|
+
}], language: [{
|
|
153
|
+
type: Input
|
|
154
|
+
}], readonly: [{
|
|
155
|
+
type: Input
|
|
156
|
+
}], loading: [{
|
|
157
|
+
type: Input
|
|
158
|
+
}], error: [{
|
|
159
|
+
type: Input
|
|
160
|
+
}], formSubmit: [{
|
|
161
|
+
type: Output
|
|
162
|
+
}], formChange: [{
|
|
163
|
+
type: Output
|
|
164
|
+
}], formReset: [{
|
|
165
|
+
type: Output
|
|
166
|
+
}] } });
|
|
167
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1mb3JtLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvZm9ybS9keW5hbWljLWZvcm0uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vc3JjL2xpYi9mb3JtL2R5bmFtaWMtZm9ybS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBQ04sWUFBWSxFQUdaLE1BQU0sRUFFTixNQUFNLEdBQ1AsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBYSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTdFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBQ2xGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7OztBQUV2RDs7Ozs7Ozs7R0FRRztBQU9ILE1BQU0sT0FBTyxvQkFBb0I7SUFDL0IsNkVBQTZFO0lBQ3BFLE1BQU0sQ0FBZ0I7SUFDdEIsV0FBVyxDQUF1QjtJQUNsQyxTQUFTLEdBQWEsRUFBRSxDQUFDO0lBQ3pCLFFBQVEsR0FBVyxJQUFJLENBQUM7SUFDeEIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixPQUFPLEdBQVksS0FBSyxDQUFDO0lBQ3pCLEtBQUssR0FBa0IsSUFBSSxDQUFDO0lBRXJDLDZFQUE2RTtJQUNuRSxVQUFVLEdBQUcsSUFBSSxZQUFZLEVBQXVCLENBQUM7SUFDckQsVUFBVSxHQUFHLElBQUksWUFBWSxFQUF1QixDQUFDO0lBQ3JELFNBQVMsR0FBRyxJQUFJ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XG4gIEV2ZW50RW1pdHRlcixcbiAgT25DaGFuZ2VzLFxuICBTaW1wbGVDaGFuZ2VzLFxuICBzaWduYWwsXG4gIGNvbXB1dGVkLFxuICBpbmplY3QsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRm9ybUJ1aWxkZXIsIEZvcm1Hcm91cCwgUmVhY3RpdmVGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB0eXBlIHsgRW50aXR5Q29uZmlnLCBWZXJzaW9uZWRSZWNvcmQgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQgeyBEeW5hbWljRmllbGRDb21wb25lbnQgfSBmcm9tICcuL2R5bmFtaWMtZmllbGQvZHluYW1pYy1maWVsZC5jb21wb25lbnQnO1xuaW1wb3J0IHsgVmFsaWRhdG9yUmVnaXN0cnlTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvdmFsaWRhdG9yLXJlZ2lzdHJ5LnNlcnZpY2UnO1xuaW1wb3J0IHsgSG9va1JlZ2lzdHJ5U2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL2hvb2stcmVnaXN0cnkuc2VydmljZSc7XG5pbXBvcnQgeyBWZXJzaW9uU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL3ZlcnNpb24uc2VydmljZSc7XG5pbXBvcnQgeyBSYmFjU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL3JiYWMuc2VydmljZSc7XG5cbi8qKlxuICogRHluYW1pY0Zvcm1Db21wb25lbnQg4oCUIHRoZSBtYWluIGZvcm0gY29tcG9uZW50LlxuICogUmVuZGVycyBhIHJlYWN0aXZlIGZvcm0gZnJvbSBFbnRpdHlDb25maWcgd2l0aCB0YWIgc3VwcG9ydCwgZGVwZW5kZW5jaWVzLFxuICogbWlncmF0aW9uIGJhbm5lciwgYW5kIFJCQUMtZ2F0ZWQgc3VibWlzc2lvbi5cbiAqXG4gKiBPTkVIRVJNRVMgcGF0dGVybjogaXNTYXZpbmcgc3RhdGUgbG9jayBwcmV2ZW50cyBzdWJzY3JpcHRpb24gY3ljbG9uZXMgKEFEUi0wMDIpLlxuICogT05FSEVSTUVTIGJ1ZyBmaXg6IHZpc2liaWxpdHkgcnVsZXMgcmUtZXZhbHVhdGVkIGFmdGVyIGluaXRpYWxEYXRhIGxvYWRzIChmb3JtLWZpeGVzIGRvYykuXG4gKiBPTkVIRVJNRVMgYnVnIGZpeDogdXNlIEBmb3IgKyBAaWYgc2VwYXJhdGVseSwgbm90IGJvdGggb24gc2FtZSBlbGVtZW50IChmb3JtLWZpeGVzIGRvYykuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1keW5hbWljLWZvcm0nLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbUmVhY3RpdmVGb3Jtc01vZHVsZSwgRHluYW1pY0ZpZWxkQ29tcG9uZW50XSxcbiAgdGVtcGxhdGVVcmw6ICcuL2R5bmFtaWMtZm9ybS5jb21wb25lbnQuaHRtbCcsXG59KVxuZXhwb3J0IGNsYXNzIER5bmFtaWNGb3JtQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzIHtcbiAgLy8g4pSA4pSA4pSAIElucHV0cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgQElucHV0KCkgY29uZmlnITogRW50aXR5Q29uZmlnO1xuICBASW5wdXQoKSBpbml0aWFsRGF0YT86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gIEBJbnB1dCgpIHVzZXJSb2xlczogc3RyaW5nW10gPSBbXTtcbiAgQElucHV0KCkgbGFuZ3VhZ2U6IHN0cmluZyA9ICdlbic7XG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XG4gIEBJbnB1dCgpIGxvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgZXJyb3I6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8vIOKUgOKUgOKUgCBPdXRwdXRzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBAT3V0cHV0KCkgZm9ybVN1Ym1pdCA9IG5ldyBFdmVudEVtaXR0ZXI8UmVjb3JkPHN0cmluZywgYW55Pj4oKTtcbiAgQE91dHB1dCgpIGZvcm1DaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPFJlY29yZDxzdHJpbmcsIGFueT4+KCk7XG4gIEBPdXRwdXQoKSBmb3JtUmVzZXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG5cbiAgLy8g4pSA4pSA4pSAIFNlcnZpY2VzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBwcml2YXRlIHJlYWRvbmx5IGZiID0gaW5qZWN0KEZvcm1CdWlsZGVyKTtcbiAgcHJpdmF0ZSByZWFkb25seSB2YWxpZGF0b3JSZWdpc3RyeSA9IGluamVjdChWYWxpZGF0b3JSZWdpc3RyeVNlcnZpY2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IGhvb2tSZWdpc3RyeSA9IGluamVjdChIb29rUmVnaXN0cnlTZXJ2aWNlKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHZlcnNpb25TZXJ2aWNlID0gaW5qZWN0KFZlcnNpb25TZXJ2aWNlKTtcbiAgcHJpdmF0ZSByZWFkb25seSByYmFjU2VydmljZSA9IGluamVjdChSYmFjU2VydmljZSk7XG5cbiAgLy8g4pSA4pSA4pSAIFNpZ25hbHMgKGxvY2FsIHJlYWN0aXZlIHN0YXRlKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgcmVhZG9ubHkgYWN0aXZlVGFiID0gc2lnbmFsPHN0cmluZz4oJycpO1xuICByZWFkb25seSBpc1NhdmluZyA9IHNpZ25hbChmYWxzZSk7IC8vIEFEUi0wMDI6IGlzU2F2aW5nIGxvY2sgcHJldmVudHMgQVBJIHNwYW1cblxuICAvLyDilIDilIDilIAgRm9ybSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgZm9ybSE6IEZvcm1Hcm91cDtcblxuICAvLyDilIDilIDilIAgQ29tcHV0ZWQg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIGdldCB0YWJzKCkge1xuICAgIHJldHVybiAodGhpcy5jb25maWc/LnRhYnMgfHwgW10pLnNvcnQoKGEsIGIpID0+IGEub3JkZXIgLSBiLm9yZGVyKTtcbiAgfVxuXG4gIGdldCBmaWVsZHNGb3JBY3RpdmVUYWIoKSB7XG4gICAgY29uc3QgdGFiSWQgPSB0aGlzLmFjdGl2ZVRhYigpO1xuICAgIHJldHVybiAodGhpcy5jb25maWc/LmZpZWxkcyB8fCBbXSkuZmlsdGVyKGYgPT4ge1xuICAgICAgaWYgKCF0aGlzLnRhYnMubGVuZ3RoKSByZXR1cm4gdHJ1ZTsgLy8gTm8gdGFicyDigJQgc2hvdyBhbGxcbiAgICAgIHJldHVybiBmLnRhYiA9PT0gdGFiSWQgfHwgKCFmLnRhYiAmJiB0YWJJZCA9PT0gdGhpcy50YWJzWzBdPy5pZCk7XG4gICAgfSk7XG4gIH1cblxuICBnZXQgcGVybWlzc2lvbnMoKSB7XG4gICAgcmV0dXJuIHRoaXMucmJhY1NlcnZpY2UuZ2V0UGVybWlzc2lvbnModGhpcy5jb25maWcsIHRoaXMudXNlclJvbGVzKTtcbiAgfVxuXG4gIGdldCBuZWVkc01pZ3JhdGlvbigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy52ZXJzaW9uU2VydmljZS5uZWVkc01pZ3JhdGlvbih0aGlzLmluaXRpYWxEYXRhIHx8IHt9LCB0aGlzLmNvbmZpZyk7XG4gIH1cblxuICBnZXQgY2FuU3VibWl0KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLnBlcm1pc3Npb25zLmNhbkVkaXQgJiZcbiAgICAgICF0aGlzLnJlYWRvbmx5ICYmXG4gICAgICAhdGhpcy5pc1NhdmluZygpICYmXG4gICAgICAhdGhpcy52ZXJzaW9uU2VydmljZS5zaG91bGRCbG9ja1N1Ym1pdCh0aGlzLmluaXRpYWxEYXRhIHx8IHt9LCB0aGlzLmNvbmZpZylcbiAgICApO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIExpZmVjeWNsZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ2NvbmZpZyddICYmIHRoaXMuY29uZmlnKSB7XG4gICAgICB0aGlzLmJ1aWxkRm9ybSgpO1xuICAgIH1cbiAgICAvLyBPTkVIRVJNRVMgZml4OiByZS1hcHBseSBkYXRhIGFmdGVyIGluaXRpYWxEYXRhIGxvYWRzIChmb3JtLWZpeGVzIGRvYylcbiAgICBpZiAoY2hhbmdlc1snaW5pdGlhbERhdGEnXSAmJiB0aGlzLmZvcm0gJiYgdGhpcy5pbml0aWFsRGF0YSkge1xuICAgICAgdGhpcy5wYXRjaEZvcm0odGhpcy5pbml0aWFsRGF0YSk7XG4gICAgfVxuICAgIGlmIChjaGFuZ2VzWydjb25maWcnXSAmJiB0aGlzLnRhYnMubGVuZ3RoKSB7XG4gICAgICB0aGlzLmFjdGl2ZVRhYi5zZXQodGhpcy50YWJzWzBdPy5pZCB8fCAnJyk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBidWlsZEZvcm0oKTogdm9pZCB7XG4gICAgY29uc3QgY29udHJvbHM6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIHRoaXMuY29uZmlnLmZpZWxkcyB8fCBbXSkge1xuICAgICAgaWYgKGZpZWxkLmlzU3lzdGVtKSBjb250aW51ZTsgLy8gU3lzdGVtIGZpZWxkcyBub3QgaW4gZm9ybSBjb250cm9sc1xuICAgICAgY29uc3QgdmFsaWRhdG9ycyA9IHRoaXMudmFsaWRhdG9yUmVnaXN0cnkucmVzb2x2ZUFsbChmaWVsZC52YWxpZGF0b3JzKTtcbiAgICAgIGNvbnRyb2xzW2ZpZWxkLmlkXSA9IFtmaWVsZC5kZWZhdWx0VmFsdWUgPz8gbnVsbCwgdmFsaWRhdG9yc107XG4gICAgfVxuICAgIHRoaXMuZm9ybSA9IHRoaXMuZmIuZ3JvdXAoY29udHJvbHMpO1xuXG4gICAgLy8gRW1pdCBjaGFuZ2VzIHZpYSBmb3JtQ2hhbmdlIG91dHB1dFxuICAgIHRoaXMuZm9ybS52YWx1ZUNoYW5nZXMuc3Vic2NyaWJlKHZhbHVlID0+IHRoaXMuZm9ybUNoYW5nZS5lbWl0KHZhbHVlKSk7XG5cbiAgICAvLyBBcHBseSBpbml0aWFsIGRhdGEgYWZ0ZXIgZm9ybSBidWlsZHNcbiAgICBpZiAodGhpcy5pbml0aWFsRGF0YSkge1xuICAgICAgdGhpcy5wYXRjaEZvcm0odGhpcy5pbml0aWFsRGF0YSk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMudGFicy5sZW5ndGggJiYgIXRoaXMuYWN0aXZlVGFiKCkpIHtcbiAgICAgIHRoaXMuYWN0aXZlVGFiLnNldCh0aGlzLnRhYnNbMF0uaWQpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcGF0Y2hGb3JtKGRhdGE6IFJlY29yZDxzdHJpbmcsIGFueT4pOiB2b2lkIHtcbiAgICAvLyBGaWx0ZXIgZW1wdHkgb2JqZWN0cyAoT05FSEVSTUVTIGFycmF5IGZpZWxkIGVtcHR5IHJvdyBidWcgZml4KVxuICAgIGNvbnN0IGNsZWFuRGF0YSA9IE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKGRhdGEpLmZpbHRlcigoWywgdl0pID0+IHYgIT09IHVuZGVmaW5lZCksXG4gICAgKTtcbiAgICB0aGlzLmZvcm0ucGF0Y2hWYWx1ZShjbGVhbkRhdGEsIHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcbiAgfVxuXG4gIC8vIOKUgOKUgOKUgCBBY3Rpb25zIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIHNldEFjdGl2ZVRhYih0YWJJZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5hY3RpdmVUYWIuc2V0KHRhYklkKTtcbiAgfVxuXG4gIGFzeW5jIHN1Ym1pdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuY2FuU3VibWl0IHx8IHRoaXMuZm9ybS5pbnZhbGlkKSB7XG4gICAgICB0aGlzLmZvcm0ubWFya0FsbEFzVG91Y2hlZCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIEFEUi0wMDI6IGlzU2F2aW5nIGxvY2sg4oCUIHByZXZlbnRzIHN1YnNjcmlwdGlvbiBjeWNsb25lcyAvIGR1cGxpY2F0ZSBBUEkgY2FsbHNcbiAgICBpZiAodGhpcy5pc1NhdmluZygpKSByZXR1cm47XG4gICAgdGhpcy5pc1NhdmluZy5zZXQodHJ1ZSk7XG5cbiAgICB0cnkge1xuICAgICAgbGV0IGRhdGEgPSB7IC4uLnRoaXMuZm9ybS52YWx1ZSB9O1xuXG4gICAgICAvLyBSdW4gcHJlLXN1Ym1pdCBob29rIChBbmd1bGFyLXNpZGUpXG4gICAgICBkYXRhID0gYXdhaXQgdGhpcy5ob29rUmVnaXN0cnkucnVuKGAke3RoaXMuY29uZmlnLmVudGl0eX06YmVmb3JlU2F2ZWAsIGRhdGEsIHtcbiAgICAgICAgY29uZmlnOiB0aGlzLmNvbmZpZyxcbiAgICAgICAgdXNlclJvbGVzOiB0aGlzLnVzZXJSb2xlcyxcbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLmZvcm1TdWJtaXQuZW1pdChkYXRhKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgLy8gUmVzZXQgc2F2aW5nIGxvY2sgaW4gdGhlIGNvbnN1bWVyJ3Mgc3VjY2Vzcy9lcnJvciBoYW5kbGVyIOKAlCBoZXJlIHdlIGp1c3QgdW5ibG9ja1xuICAgICAgdGhpcy5pc1NhdmluZy5zZXQoZmFsc2UpO1xuICAgIH1cbiAgfVxuXG4gIHJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMuZm9ybS5yZXNldCgpO1xuICAgIGlmICh0aGlzLmluaXRpYWxEYXRhKSB0aGlzLnBhdGNoRm9ybSh0aGlzLmluaXRpYWxEYXRhKTtcbiAgICB0aGlzLmZvcm1SZXNldC5lbWl0KCk7XG4gIH1cblxuICBnZXRDb250cm9sKGZpZWxkSWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLmZvcm0/LmdldChmaWVsZElkKTtcbiAgfVxufVxuIiwiPGZvcm0gW2Zvcm1Hcm91cF09XCJmb3JtXCIgKG5nU3VibWl0KT1cInN1Ym1pdCgpXCIgY2xhc3M9XCJuZ3gtZm9ybVwiIG5vdmFsaWRhdGU+XG5cbiAgPCEtLSBNaWdyYXRpb24gYmFubmVyIOKAlCB2aXNpYmxlIGluIGdyYWNlZnVsIG1vZGUgd2hlbiByZWNvcmQgaXMgc3RhbGUgLS0+XG4gIEBpZiAobmVlZHNNaWdyYXRpb24pIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX21pZ3JhdGlvbi1iYW5uZXJcIiByb2xlPVwiYWxlcnRcIj5cbiAgICAgIOKaoO+4jyBUaGlzIHJlY29yZCB3YXMgY3JlYXRlZCB3aXRoIGFuIG9sZGVyIGNvbmZpZ3VyYXRpb24gdmVyc2lvbi5cbiAgICAgIEBpZiAodmVyc2lvblNlcnZpY2Uuc2hvdWxkQmxvY2tTdWJtaXQoaW5pdGlhbERhdGEgfHwge30sIGNvbmZpZykpIHtcbiAgICAgICAgUGxlYXNlIHJ1biBtaWdyYXRpb24gYmVmb3JlIGVkaXRpbmcuXG4gICAgICB9IEBlbHNlIHtcbiAgICAgICAgSXQgd2lsbCBiZSB1cGRhdGVkIHRvIHRoZSBsYXRlc3QgdmVyc2lvbiBvbiBzYXZlLlxuICAgICAgfVxuICAgIDwvZGl2PlxuICB9XG5cbiAgPCEtLSBFcnJvciBtZXNzYWdlIC0tPlxuICBAaWYgKGVycm9yKSB7XG4gICAgPGRpdiBjbGFzcz1cIm5neC1mb3JtX19lcnJvci1iYW5uZXJcIiByb2xlPVwiYWxlcnRcIj57eyBlcnJvciB9fTwvZGl2PlxuICB9XG5cbiAgPCEtLSBUYWIgYmFyIOKAlCBvbmx5IGlmIHRhYnMgZGVmaW5lZCAtLT5cbiAgQGlmICh0YWJzLmxlbmd0aCA+IDApIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX3RhYnNcIiByb2xlPVwidGFibGlzdFwiPlxuICAgICAgPCEtLSBPTkVIRVJNRVMgZml4OiBAZm9yIGFuZCBAaWYgb24gc2VwYXJhdGUgZWxlbWVudHMsIG5vdCBzYW1lIChmb3JtLWZpeGVzIGRvYykgLS0+XG4gICAgICBAZm9yICh0YWIgb2YgdGFiczsgdHJhY2sgdGFiLmlkKSB7XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBjbGFzcz1cIm5neC1mb3JtX190YWJcIlxuICAgICAgICAgIFtjbGFzcy5uZ3gtZm9ybV9fdGFiLS1hY3RpdmVdPVwiYWN0aXZlVGFiKCkgPT09IHRhYi5pZFwiXG4gICAgICAgICAgKGNsaWNrKT1cInNldEFjdGl2ZVRhYih0YWIuaWQpXCJcbiAgICAgICAgICByb2xlPVwidGFiXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLXNlbGVjdGVkXT1cImFjdGl2ZVRhYigpID09PSB0YWIuaWRcIlxuICAgICAgICA+XG4gICAgICAgICAge3sgdGFiLmxhYmVsW2xhbmd1YWdlXSB8fCB0YWIubGFiZWxbJ2VuJ10gfX1cbiAgICAgICAgPC9idXR0b24+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIH1cblxuICA8IS0tIEZpZWxkcyBmb3IgYWN0aXZlIHRhYiAtLT5cbiAgPGRpdiBjbGFzcz1cIm5neC1mb3JtX19wYW5lbFwiPlxuICAgIEBmb3IgKGZpZWxkIG9mIGZpZWxkc0ZvckFjdGl2ZVRhYjsgdHJhY2sgZmllbGQuaWQpIHtcbiAgICAgIEBpZiAoZmllbGQudmlzaWJsZSAhPT0gZmFsc2UpIHtcbiAgICAgICAgPG5neC1keW5hbWljLWZpZWxkXG4gICAgICAgICAgW2ZpZWxkXT1cImZpZWxkXCJcbiAgICAgICAgICBbY29udHJvbF09XCJnZXRDb250cm9sKGZpZWxkLmlkKSFcIlxuICAgICAgICAgIFtjb25maWddPVwiY29uZmlnXCJcbiAgICAgICAgICBbbGFuZ3VhZ2VdPVwibGFuZ3VhZ2VcIlxuICAgICAgICAgIFtyZWFkb25seV09XCJyZWFkb25seSB8fCAhIWZpZWxkLnJlYWRvbmx5XCJcbiAgICAgICAgICBbdXNlclJvbGVzXT1cInVzZXJSb2xlc1wiXG4gICAgICAgIC8+XG4gICAgICB9XG4gICAgfVxuICA8L2Rpdj5cblxuICA8IS0tIFN1Ym1pdCBidXR0b24g4oCUIGdhdGVkIGJ5IFJCQUMgYW5kIGlzU2F2aW5nIGxvY2sgLS0+XG4gIEBpZiAoIXJlYWRvbmx5ICYmIHBlcm1pc3Npb25zLmNhbkVkaXQpIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX2FjdGlvbnNcIj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwibmd4LWZvcm1fX3Jlc2V0XCJcbiAgICAgICAgKGNsaWNrKT1cInJlc2V0KClcIlxuICAgICAgICBbZGlzYWJsZWRdPVwiaXNTYXZpbmcoKVwiXG4gICAgICA+XG4gICAgICAgIFJlc2V0XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cInN1Ym1pdFwiXG4gICAgICAgIGNsYXNzPVwibmd4LWZvcm1fX3N1Ym1pdFwiXG4gICAgICAgIFtkaXNhYmxlZF09XCIhY2FuU3VibWl0IHx8IGZvcm0uaW52YWxpZFwiXG4gICAgICA+XG4gICAgICAgIEBpZiAoaXNTYXZpbmcoKSB8fCBsb2FkaW5nKSB7XG4gICAgICAgICAgU2F2aW5n4oCmXG4gICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgIFNhdmVcbiAgICAgICAgfVxuICAgICAgPC9idXR0b24+XG4gICAgPC9kaXY+XG4gIH1cblxuPC9mb3JtPlxuIl19
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { makeEnvironmentProviders } from '@angular/core';
|
|
2
|
+
import { DYNAMIC_ENTITY_API_URL, ENTITY_REF_REGISTRY, FIELD_TYPE_REGISTRY, HOOK_REGISTRY, MASKED_ROLES, MIGRATION_STRATEGY, VALIDATOR_REGISTRY, } from '../tokens/injection-tokens';
|
|
3
|
+
/**
|
|
4
|
+
* Provider function for ngx-dynamic-entity.
|
|
5
|
+
* All options are optional — consumers provide only what they use (ISP).
|
|
6
|
+
*
|
|
7
|
+
* Usage in app.config.ts:
|
|
8
|
+
* provideNgxDynamicEntity({
|
|
9
|
+
* apiUrl: 'http://localhost:3000/api/entities',
|
|
10
|
+
* maskedRoles: ['IT_SUPPORT'],
|
|
11
|
+
* entityRefs: { clients: () => clientService.getOptions() },
|
|
12
|
+
* })
|
|
13
|
+
*/
|
|
14
|
+
export const provideNgxDynamicEntity = (config = {}) => makeEnvironmentProviders([
|
|
15
|
+
{ provide: DYNAMIC_ENTITY_API_URL, useValue: config.apiUrl ?? '' },
|
|
16
|
+
{ provide: MASKED_ROLES, useValue: config.maskedRoles ?? [] },
|
|
17
|
+
{ provide: MIGRATION_STRATEGY, useValue: config.migrationStrategy ?? 'graceful' },
|
|
18
|
+
{ provide: FIELD_TYPE_REGISTRY, useValue: new Map(Object.entries(config.fieldTypes ?? {})) },
|
|
19
|
+
{ provide: ENTITY_REF_REGISTRY, useValue: new Map(Object.entries(config.entityRefs ?? {})) },
|
|
20
|
+
{ provide: VALIDATOR_REGISTRY, useValue: new Map(Object.entries(config.validators ?? {})) },
|
|
21
|
+
{ provide: HOOK_REGISTRY, useValue: new Map(Object.entries(config.hooks ?? {})) },
|
|
22
|
+
]);
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZS1uZ3gtZHluYW1pYy1lbnRpdHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Byb3ZpZGVycy9wcm92aWRlLW5neC1keW5hbWljLWVudGl0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQXdCLHdCQUF3QixFQUFRLE1BQU0sZUFBZSxDQUFDO0FBQ3JGLE9BQU8sRUFDTCxzQkFBc0IsRUFDdEIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNuQixhQUFhLEVBQ2IsWUFBWSxFQUNaLGtCQUFrQixFQUNsQixrQkFBa0IsR0FDbkIsTUFBTSw0QkFBNEIsQ0FBQztBQW1CcEM7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFHLENBQUMsU0FBaUMsRUFBRSxFQUF3QixFQUFFLENBQ25HLHdCQUF3QixDQUFDO0lBQ3ZCLEVBQUUsT0FBTyxFQUFFLHNCQUFzQixFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsTUFBTSxJQUFJLEVBQUUsRUFBRTtJQUNsRSxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsUUFBUSxFQUFFLE1BQU0sQ0FBQyxXQUFXLElBQUksRUFBRSxFQUFFO0lBQzdELEVBQUUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsaUJBQWlCLElBQUksVUFBVSxFQUFFO0lBQ2pGLEVBQUUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLFFBQVEsRUFBRSxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRTtJQUM1RixFQUFFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxRQUFRLEVBQUUsSUFBSSxHQUFHLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUU7SUFDNUYsRUFBRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsUUFBUSxFQUFFLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFO0lBQzNGLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxRQUFRLEVBQUUsSUFBSSxHQUFHLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUU7Q0FDbEYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRW52aXJvbm1lbnRQcm92aWRlcnMsIG1ha2VFbnZpcm9ubWVudFByb3ZpZGVycywgVHlwZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgRFlOQU1JQ19FTlRJVFlfQVBJX1VSTCxcbiAgRU5USVRZX1JFRl9SRUdJU1RSWSxcbiAgRklFTERfVFlQRV9SRUdJU1RSWSxcbiAgSE9PS19SRUdJU1RSWSxcbiAgTUFTS0VEX1JPTEVTLFxuICBNSUdSQVRJT05fU1RSQVRFR1ksXG4gIFZBTElEQVRPUl9SRUdJU1RSWSxcbn0gZnJvbSAnLi4vdG9rZW5zL2luamVjdGlvbi10b2tlbnMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE5neER5bmFtaWNFbnRpdHlDb25maWcge1xuICAvKiogQmFzZSBVUkwgb2YgdGhlIGR5bmFtaWMtZW50aXR5LXNlcnZlciBBUEkuIEV4YW1wbGU6ICdodHRwOi8vbG9jYWxob3N0OjMwMDAvYXBpL2VudGl0aWVzJyAqL1xuICBhcGlVcmw/OiBzdHJpbmc7XG4gIC8qKiBSb2xlcyB0aGF0IHNlZSBtYXNrZWQgZmllbGQgdmFsdWVzIGFzIFhYWFhYWFhYWCAqL1xuICBtYXNrZWRSb2xlcz86IHN0cmluZ1tdO1xuICAvKiogTWlncmF0aW9uIHN0cmF0ZWd5OiAnc3RyaWN0JyByZWplY3RzIHN0YWxlIHJlY29yZHMsICdncmFjZWZ1bCcgZmxhZ3MgdGhlbS4gTmV2ZXIgJ2F1dG8nIChBRFItMDA1KS4gKi9cbiAgbWlncmF0aW9uU3RyYXRlZ3k/OiAnc3RyaWN0JyB8ICdncmFjZWZ1bCc7XG4gIC8qKiBDdXN0b20gZmllbGQgdHlwZSBjb21wb25lbnRzIGtleWVkIGJ5IHR5cGUgc3RyaW5nICovXG4gIGZpZWxkVHlwZXM/OiBSZWNvcmQ8c3RyaW5nLCBUeXBlPGFueT4+O1xuICAvKiogRW50aXR5LXJlZiBvcHRpb24gbG9hZGVycyBrZXllZCBieSBlbnRpdHkga2V5IChBRFItMDA2KSAqL1xuICBlbnRpdHlSZWZzPzogUmVjb3JkPHN0cmluZywgKCkgPT4gUHJvbWlzZTxhbnlbXT4+O1xuICAvKiogQ3VzdG9tIHZhbGlkYXRvciBmdW5jdGlvbnMga2V5ZWQgYnkgdmFsaWRhdG9yIG5hbWUgKi9cbiAgdmFsaWRhdG9ycz86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gIC8qKiBIb29rIGZ1bmN0aW9ucyBrZXllZCBieSBob29rIG5hbWUgKi9cbiAgaG9va3M/OiBSZWNvcmQ8c3RyaW5nLCBGdW5jdGlvbj47XG59XG5cbi8qKlxuICogUHJvdmlkZXIgZnVuY3Rpb24gZm9yIG5neC1keW5hbWljLWVudGl0eS5cbiAqIEFsbCBvcHRpb25zIGFyZSBvcHRpb25hbCDigJQgY29uc3VtZXJzIHByb3ZpZGUgb25seSB3aGF0IHRoZXkgdXNlIChJU1ApLlxuICpcbiAqIFVzYWdlIGluIGFwcC5jb25maWcudHM6XG4gKiAgIHByb3ZpZGVOZ3hEeW5hbWljRW50aXR5KHtcbiAqICAgICBhcGlVcmw6ICdodHRwOi8vbG9jYWxob3N0OjMwMDAvYXBpL2VudGl0aWVzJyxcbiAqICAgICBtYXNrZWRSb2xlczogWydJVF9TVVBQT1JUJ10sXG4gKiAgICAgZW50aXR5UmVmczogeyBjbGllbnRzOiAoKSA9PiBjbGllbnRTZXJ2aWNlLmdldE9wdGlvbnMoKSB9LFxuICogICB9KVxuICovXG5leHBvcnQgY29uc3QgcHJvdmlkZU5neER5bmFtaWNFbnRpdHkgPSAoY29uZmlnOiBOZ3hEeW5hbWljRW50aXR5Q29uZmlnID0ge30pOiBFbnZpcm9ubWVudFByb3ZpZGVycyA9PlxuICBtYWtlRW52aXJvbm1lbnRQcm92aWRlcnMoW1xuICAgIHsgcHJvdmlkZTogRFlOQU1JQ19FTlRJVFlfQVBJX1VSTCwgdXNlVmFsdWU6IGNvbmZpZy5hcGlVcmwgPz8gJycgfSxcbiAgICB7IHByb3ZpZGU6IE1BU0tFRF9ST0xFUywgdXNlVmFsdWU6IGNvbmZpZy5tYXNrZWRSb2xlcyA/PyBbXSB9LFxuICAgIHsgcHJvdmlkZTogTUlHUkFUSU9OX1NUUkFURUdZLCB1c2VWYWx1ZTogY29uZmlnLm1pZ3JhdGlvblN0cmF0ZWd5ID8/ICdncmFjZWZ1bCcgfSxcbiAgICB7IHByb3ZpZGU6IEZJRUxEX1RZUEVfUkVHSVNUUlksIHVzZVZhbHVlOiBuZXcgTWFwKE9iamVjdC5lbnRyaWVzKGNvbmZpZy5maWVsZFR5cGVzID8/IHt9KSkgfSxcbiAgICB7IHByb3ZpZGU6IEVOVElUWV9SRUZfUkVHSVNUUlksIHVzZVZhbHVlOiBuZXcgTWFwKE9iamVjdC5lbnRyaWVzKGNvbmZpZy5lbnRpdHlSZWZzID8/IHt9KSkgfSxcbiAgICB7IHByb3ZpZGU6IFZBTElEQVRPUl9SRUdJU1RSWSwgdXNlVmFsdWU6IG5ldyBNYXAoT2JqZWN0LmVudHJpZXMoY29uZmlnLnZhbGlkYXRvcnMgPz8ge30pKSB9LFxuICAgIHsgcHJvdmlkZTogSE9PS19SRUdJU1RSWSwgdXNlVmFsdWU6IG5ldyBNYXAoT2JqZWN0LmVudHJpZXMoY29uZmlnLmhvb2tzID8/IHt9KSkgfSxcbiAgXSk7XG4iXX0=
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import { map, shareReplay } from 'rxjs/operators';
|
|
4
|
+
import { DYNAMIC_ENTITY_API_URL } from '../tokens/injection-tokens';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* ConfigService — fetches entity configs from the dynamic-entity-server API.
|
|
8
|
+
* Uses shareReplay(1) to cache per config key — avoids duplicate HTTP requests.
|
|
9
|
+
* Following ONEHERMES smart service pattern: Observable$ + shareReplay.
|
|
10
|
+
*/
|
|
11
|
+
export class ConfigService {
|
|
12
|
+
http = inject(HttpClient);
|
|
13
|
+
apiUrl = inject(DYNAMIC_ENTITY_API_URL, { optional: true }) ?? '';
|
|
14
|
+
cache = new Map();
|
|
15
|
+
/** Get a config by entity key. Cached via shareReplay(1). */
|
|
16
|
+
getConfig(entity) {
|
|
17
|
+
if (!this.cache.has(entity)) {
|
|
18
|
+
const config$ = this.http
|
|
19
|
+
.get(`${this.apiUrl}/config/${entity}`)
|
|
20
|
+
.pipe(map(res => res.data), // ADR-004: Success guarantees data
|
|
21
|
+
shareReplay(1));
|
|
22
|
+
this.cache.set(entity, config$);
|
|
23
|
+
}
|
|
24
|
+
return this.cache.get(entity);
|
|
25
|
+
}
|
|
26
|
+
/** List all configs */
|
|
27
|
+
listConfigs() {
|
|
28
|
+
return this.http
|
|
29
|
+
.get(`${this.apiUrl}/config`)
|
|
30
|
+
.pipe(map(res => res.data)); // ADR-004: Success guarantees data
|
|
31
|
+
}
|
|
32
|
+
/** Create a new config */
|
|
33
|
+
saveConfig(config) {
|
|
34
|
+
return this.http
|
|
35
|
+
.post(`${this.apiUrl}/config`, config)
|
|
36
|
+
.pipe(map(res => res.data));
|
|
37
|
+
}
|
|
38
|
+
/** Update an existing config */
|
|
39
|
+
updateConfig(entity, updates) {
|
|
40
|
+
return this.http
|
|
41
|
+
.put(`${this.apiUrl}/config/${entity}`, updates)
|
|
42
|
+
.pipe(map(res => {
|
|
43
|
+
this.invalidate(entity);
|
|
44
|
+
return res.data;
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
/** Invalidate cache for an entity (call after config update) */
|
|
48
|
+
invalidate(entity) {
|
|
49
|
+
this.cache.delete(entity);
|
|
50
|
+
}
|
|
51
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
52
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConfigService, providedIn: 'root' });
|
|
53
|
+
}
|
|
54
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConfigService, decorators: [{
|
|
55
|
+
type: Injectable,
|
|
56
|
+
args: [{ providedIn: 'root' }]
|
|
57
|
+
}] });
|
|
58
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3NlcnZpY2VzL2NvbmZpZy5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUVsRCxPQUFPLEVBQUUsR0FBRyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWxELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDOztBQUVwRTs7OztHQUlHO0FBRUgsTUFBTSxPQUFPLGFBQWE7SUFDUCxJQUFJLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQzFCLE1BQU0sR0FBRyxNQUFNLENBQUMsc0JBQXNCLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDbEUsS0FBSyxHQUFHLElBQUksR0FBRyxFQUFvQyxDQUFDO0lBRXJFLDZEQUE2RDtJQUM3RCxTQUFTLENBQUMsTUFBYztRQUN0QixJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUM1QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSTtpQkFDdEIsR0FBRyxDQUEyQyxHQUFHLElBQUksQ0FBQyxNQUFNLFdBQVcsTUFBTSxFQUFFLENBQUM7aUJBQ2hGLElBQUksQ0FDSCxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSyxDQUFDLEVBQUUsbUNBQW1DO1lBQzFELFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FDZixDQUFDO1lBQ0osSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ2xDLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBRSxDQUFDO0lBQ2pDLENBQUM7SUFFRCx1QkFBdUI7SUFDdkIsV0FBVztRQUNULE9BQU8sSUFBSSxDQUFDLElBQUk7YUFDYixHQUFHLENBQTZDLEdBQUcsSUFBSSxDQUFDLE1BQU0sU0FBUyxDQUFDO2FBQ3hFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLG1DQUFtQztJQUNyRSxDQUFDO0lBRUQsMEJBQTBCO0lBQzFCLFVBQVUsQ0FBQyxNQUFvQjtRQUM3QixPQUFPLElBQUksQ0FBQyxJQUFJO2FBQ2IsSUFBSSxDQUEyQyxHQUFHLElBQUksQ0FBQyxNQUFNLFNBQVMsRUFBRSxNQUFNLENBQUM7YUFDL0UsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFLLENBQUMsQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFFRCxnQ0FBZ0M7SUFDaEMsWUFBWSxDQUFDLE1BQWMsRUFBRSxPQUE4QjtRQUN6RCxPQUFPLElBQUksQ0FBQyxJQUFJO2FBQ2IsR0FBRyxDQUEyQyxHQUFHLElBQUksQ0FBQyxNQUFNLFdBQVcsTUFBTSxFQUFFLEVBQUUsT0FBTyxDQUFDO2FBQ3pGLElBQUksQ0FDSCxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDUixJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3hCLE9BQU8sR0FBRyxDQUFDLElBQUssQ0FBQztRQUNuQixDQUFDLENBQUMsQ0FDSCxDQUFDO0lBQ04sQ0FBQztJQUVELGdFQUFnRTtJQUNoRSxVQUFVLENBQUMsTUFBYztRQUN2QixJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM1QixDQUFDO3dHQWhEVSxhQUFhOzRHQUFiLGFBQWEsY0FEQSxNQUFNOzs0RkFDbkIsYUFBYTtrQkFEekIsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEh0dHBDbGllbnQgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBtYXAsIHNoYXJlUmVwbGF5IH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHR5cGUgeyBFbnRpdHlDb25maWcgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQgeyBEWU5BTUlDX0VOVElUWV9BUElfVVJMIH0gZnJvbSAnLi4vdG9rZW5zL2luamVjdGlvbi10b2tlbnMnO1xuXG4vKipcbiAqIENvbmZpZ1NlcnZpY2Ug4oCUIGZldGNoZXMgZW50aXR5IGNvbmZpZ3MgZnJvbSB0aGUgZHluYW1pYy1lbnRpdHktc2VydmVyIEFQSS5cbiAqIFVzZXMgc2hhcmVSZXBsYXkoMSkgdG8gY2FjaGUgcGVyIGNvbmZpZyBrZXkg4oCUIGF2b2lkcyBkdXBsaWNhdGUgSFRUUCByZXF1ZXN0cy5cbiAqIEZvbGxvd2luZyBPTkVIRVJNRVMgc21hcnQgc2VydmljZSBwYXR0ZXJuOiBPYnNlcnZhYmxlJCArIHNoYXJlUmVwbGF5LlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIENvbmZpZ1NlcnZpY2Uge1xuICBwcml2YXRlIHJlYWRvbmx5IGh0dHAgPSBpbmplY3QoSHR0cENsaWVudCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXBpVXJsID0gaW5qZWN0KERZTkFNSUNfRU5USVRZX0FQSV9VUkwsIHsgb3B0aW9uYWw6IHRydWUgfSkgPz8gJyc7XG4gIHByaXZhdGUgcmVhZG9ubHkgY2FjaGUgPSBuZXcgTWFwPHN0cmluZywgT2JzZXJ2YWJsZTxFbnRpdHlDb25maWc+PigpO1xuXG4gIC8qKiBHZXQgYSBjb25maWcgYnkgZW50aXR5IGtleS4gQ2FjaGVkIHZpYSBzaGFyZVJlcGxheSgxKS4gKi9cbiAgZ2V0Q29uZmlnKGVudGl0eTogc3RyaW5nKTogT2JzZXJ2YWJsZTxFbnRpdHlDb25maWc+IHtcbiAgICBpZiAoIXRoaXMuY2FjaGUuaGFzKGVudGl0eSkpIHtcbiAgICAgIGNvbnN0IGNvbmZpZyQgPSB0aGlzLmh0dHBcbiAgICAgICAgLmdldDx7IHN1Y2Nlc3M6IGJvb2xlYW47IGRhdGE6IEVudGl0eUNvbmZpZyB9PihgJHt0aGlzLmFwaVVybH0vY29uZmlnLyR7ZW50aXR5fWApXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIG1hcChyZXMgPT4gcmVzLmRhdGEhKSwgLy8gQURSLTAwNDogU3VjY2VzcyBndWFyYW50ZWVzIGRhdGFcbiAgICAgICAgICBzaGFyZVJlcGxheSgxKSxcbiAgICAgICAgKTtcbiAgICAgIHRoaXMuY2FjaGUuc2V0KGVudGl0eSwgY29uZmlnJCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmNhY2hlLmdldChlbnRpdHkpITtcbiAgfVxuXG4gIC8qKiBMaXN0IGFsbCBjb25maWdzICovXG4gIGxpc3RDb25maWdzKCk6IE9ic2VydmFibGU8RW50aXR5Q29uZmlnW10+IHtcbiAgICByZXR1cm4gdGhpcy5odHRwXG4gICAgICAuZ2V0PHsgc3VjY2VzczogYm9vbGVhbjsgZGF0YTogRW50aXR5Q29uZmlnW10gfT4oYCR7dGhpcy5hcGlVcmx9L2NvbmZpZ2ApXG4gICAgICAucGlwZShtYXAocmVzID0+IHJlcy5kYXRhISkpOyAvLyBBRFItMDA0OiBTdWNjZXNzIGd1YXJhbnRlZXMgZGF0YVxuICB9XG5cbiAgLyoqIENyZWF0ZSBhIG5ldyBjb25maWcgKi9cbiAgc2F2ZUNvbmZpZyhjb25maWc6IEVudGl0eUNvbmZpZyk6IE9ic2VydmFibGU8RW50aXR5Q29uZmlnPiB7XG4gICAgcmV0dXJuIHRoaXMuaHR0cFxuICAgICAgLnBvc3Q8eyBzdWNjZXNzOiBib29sZWFuOyBkYXRhOiBFbnRpdHlDb25maWcgfT4oYCR7dGhpcy5hcGlVcmx9L2NvbmZpZ2AsIGNvbmZpZylcbiAgICAgIC5waXBlKG1hcChyZXMgPT4gcmVzLmRhdGEhKSk7XG4gIH1cblxuICAvKiogVXBkYXRlIGFuIGV4aXN0aW5nIGNvbmZpZyAqL1xuICB1cGRhdGVDb25maWcoZW50aXR5OiBzdHJpbmcsIHVwZGF0ZXM6IFBhcnRpYWw8RW50aXR5Q29uZmlnPik6IE9ic2VydmFibGU8RW50aXR5Q29uZmlnPiB7XG4gICAgcmV0dXJuIHRoaXMuaHR0cFxuICAgICAgLnB1dDx7IHN1Y2Nlc3M6IGJvb2xlYW47IGRhdGE6IEVudGl0eUNvbmZpZyB9PihgJHt0aGlzLmFwaVVybH0vY29uZmlnLyR7ZW50aXR5fWAsIHVwZGF0ZXMpXG4gICAgICAucGlwZShcbiAgICAgICAgbWFwKHJlcyA9PiB7XG4gICAgICAgICAgdGhpcy5pbnZhbGlkYXRlKGVudGl0eSk7XG4gICAgICAgICAgcmV0dXJuIHJlcy5kYXRhITtcbiAgICAgICAgfSlcbiAgICAgICk7XG4gIH1cblxuICAvKiogSW52YWxpZGF0ZSBjYWNoZSBmb3IgYW4gZW50aXR5IChjYWxsIGFmdGVyIGNvbmZpZyB1cGRhdGUpICovXG4gIGludmFsaWRhdGUoZW50aXR5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmNhY2hlLmRlbGV0ZShlbnRpdHkpO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { ENTITY_REF_REGISTRY } from '../tokens/injection-tokens';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* EntityRefRegistryService — resolves entity-ref option loader functions.
|
|
6
|
+
* ADR-006: Entity-ref options come ONLY from this registry.
|
|
7
|
+
* Never pass loaders via @Input() on field components.
|
|
8
|
+
*
|
|
9
|
+
* Consumers register loaders via provideNgxDynamicEntity({ entityRefs: { clients: () => [...] } }).
|
|
10
|
+
* EntityRefFieldComponent injects this service and calls resolve(field.component ?? field.id).
|
|
11
|
+
*/
|
|
12
|
+
export class EntityRefRegistryService {
|
|
13
|
+
registry = inject(ENTITY_REF_REGISTRY, { optional: true }) ?? new Map();
|
|
14
|
+
/**
|
|
15
|
+
* Get the option loader function for an entity key.
|
|
16
|
+
* @returns Async function that returns options array, or null if not registered.
|
|
17
|
+
*/
|
|
18
|
+
resolve(entityKey) {
|
|
19
|
+
return this.registry.get(entityKey) ?? null;
|
|
20
|
+
}
|
|
21
|
+
has(entityKey) {
|
|
22
|
+
return this.registry.has(entityKey);
|
|
23
|
+
}
|
|
24
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityRefRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
25
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityRefRegistryService, providedIn: 'root' });
|
|
26
|
+
}
|
|
27
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityRefRegistryService, decorators: [{
|
|
28
|
+
type: Injectable,
|
|
29
|
+
args: [{ providedIn: 'root' }]
|
|
30
|
+
}] });
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LXJlZi1yZWdpc3RyeS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9zZXJ2aWNlcy9lbnRpdHktcmVmLXJlZ2lzdHJ5LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7O0FBRWpFOzs7Ozs7O0dBT0c7QUFFSCxNQUFNLE9BQU8sd0JBQXdCO0lBQ2xCLFFBQVEsR0FBRyxNQUFNLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsSUFBSSxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRXpGOzs7T0FHRztJQUNILE9BQU8sQ0FBQyxTQUFpQjtRQUN2QixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUM5QyxDQUFDO0lBRUQsR0FBRyxDQUFDLFNBQWlCO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDdEMsQ0FBQzt3R0FiVSx3QkFBd0I7NEdBQXhCLHdCQUF3QixjQURYLE1BQU07OzRGQUNuQix3QkFBd0I7a0JBRHBDLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBFTlRJVFlfUkVGX1JFR0lTVFJZIH0gZnJvbSAnLi4vdG9rZW5zL2luamVjdGlvbi10b2tlbnMnO1xuXG4vKipcbiAqIEVudGl0eVJlZlJlZ2lzdHJ5U2VydmljZSDigJQgcmVzb2x2ZXMgZW50aXR5LXJlZiBvcHRpb24gbG9hZGVyIGZ1bmN0aW9ucy5cbiAqIEFEUi0wMDY6IEVudGl0eS1yZWYgb3B0aW9ucyBjb21lIE9OTFkgZnJvbSB0aGlzIHJlZ2lzdHJ5LlxuICogTmV2ZXIgcGFzcyBsb2FkZXJzIHZpYSBASW5wdXQoKSBvbiBmaWVsZCBjb21wb25lbnRzLlxuICpcbiAqIENvbnN1bWVycyByZWdpc3RlciBsb2FkZXJzIHZpYSBwcm92aWRlTmd4RHluYW1pY0VudGl0eSh7IGVudGl0eVJlZnM6IHsgY2xpZW50czogKCkgPT4gWy4uLl0gfSB9KS5cbiAqIEVudGl0eVJlZkZpZWxkQ29tcG9uZW50IGluamVjdHMgdGhpcyBzZXJ2aWNlIGFuZCBjYWxscyByZXNvbHZlKGZpZWxkLmNvbXBvbmVudCA/PyBmaWVsZC5pZCkuXG4gKi9cbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXG5leHBvcnQgY2xhc3MgRW50aXR5UmVmUmVnaXN0cnlTZXJ2aWNlIHtcbiAgcHJpdmF0ZSByZWFkb25seSByZWdpc3RyeSA9IGluamVjdChFTlRJVFlfUkVGX1JFR0lTVFJZLCB7IG9wdGlvbmFsOiB0cnVlIH0pID8/IG5ldyBNYXAoKTtcblxuICAvKipcbiAgICogR2V0IHRoZSBvcHRpb24gbG9hZGVyIGZ1bmN0aW9uIGZvciBhbiBlbnRpdHkga2V5LlxuICAgKiBAcmV0dXJucyBBc3luYyBmdW5jdGlvbiB0aGF0IHJldHVybnMgb3B0aW9ucyBhcnJheSwgb3IgbnVsbCBpZiBub3QgcmVnaXN0ZXJlZC5cbiAgICovXG4gIHJlc29sdmUoZW50aXR5S2V5OiBzdHJpbmcpOiAoKCkgPT4gUHJvbWlzZTxhbnlbXT4pIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucmVnaXN0cnkuZ2V0KGVudGl0eUtleSkgPz8gbnVsbDtcbiAgfVxuXG4gIGhhcyhlbnRpdHlLZXk6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lzdHJ5LmhhcyhlbnRpdHlLZXkpO1xuICB9XG59XG4iXX0=
|