ngx-dynamic-entity-builder 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -1
  2. package/fesm2022/ngx-dynamic-entity-builder.mjs +521 -105
  3. package/fesm2022/ngx-dynamic-entity-builder.mjs.map +1 -1
  4. package/package.json +7 -7
  5. package/types/ngx-dynamic-entity-builder.d.ts +582 -0
  6. package/esm2022/lib/builder-store.service.mjs +0 -1057
  7. package/esm2022/lib/clone.mjs +0 -14
  8. package/esm2022/lib/components/entity-builder-canvas.component.mjs +0 -84
  9. package/esm2022/lib/components/entity-builder-tree-node.component.mjs +0 -192
  10. package/esm2022/lib/components/entity-reference-config.component.mjs +0 -405
  11. package/esm2022/lib/components/field-inspector.component.mjs +0 -139
  12. package/esm2022/lib/components/field-palette.component.mjs +0 -57
  13. package/esm2022/lib/components/field-rules-list.component.mjs +0 -224
  14. package/esm2022/lib/components/referenced-field-config.component.mjs +0 -235
  15. package/esm2022/lib/components/rule-form.component.mjs +0 -201
  16. package/esm2022/lib/components/tab-manager.component.mjs +0 -354
  17. package/esm2022/lib/entity-builder.component.mjs +0 -159
  18. package/esm2022/lib/field-catalog.mjs +0 -6
  19. package/esm2022/ngx-dynamic-entity-builder.mjs +0 -5
  20. package/esm2022/public-api.mjs +0 -16
  21. package/index.d.ts +0 -5
  22. package/lib/builder-store.service.d.ts +0 -206
  23. package/lib/clone.d.ts +0 -9
  24. package/lib/components/entity-builder-canvas.component.d.ts +0 -9
  25. package/lib/components/entity-builder-tree-node.component.d.ts +0 -17
  26. package/lib/components/entity-reference-config.component.d.ts +0 -29
  27. package/lib/components/field-inspector.component.d.ts +0 -33
  28. package/lib/components/field-palette.component.d.ts +0 -13
  29. package/lib/components/field-rules-list.component.d.ts +0 -19
  30. package/lib/components/referenced-field-config.component.d.ts +0 -24
  31. package/lib/components/rule-form.component.d.ts +0 -16
  32. package/lib/components/tab-manager.component.d.ts +0 -28
  33. package/lib/entity-builder.component.d.ts +0 -45
  34. package/lib/field-catalog.d.ts +0 -5
  35. package/public-api.d.ts +0 -12
@@ -1,14 +0,0 @@
1
- /**
2
- * Deep clone with a JSON fallback.
3
- *
4
- * `structuredClone` is not universally available (older browsers, and jsdom before v22),
5
- * so it must never be called unguarded — an unguarded call throws a ReferenceError and the
6
- * caller silently does nothing. Config and rule objects are JSON-safe, so the fallback is
7
- * lossless here.
8
- */
9
- export function deepClone(value) {
10
- if (typeof structuredClone === 'function')
11
- return structuredClone(value);
12
- return JSON.parse(JSON.stringify(value));
13
- }
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2Nsb25lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFJLEtBQVE7SUFDbkMsSUFBSSxPQUFPLGVBQWUsS0FBSyxVQUFVO1FBQUUsT0FBTyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDekUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQU0sQ0FBQztBQUNoRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEZWVwIGNsb25lIHdpdGggYSBKU09OIGZhbGxiYWNrLlxuICpcbiAqIGBzdHJ1Y3R1cmVkQ2xvbmVgIGlzIG5vdCB1bml2ZXJzYWxseSBhdmFpbGFibGUgKG9sZGVyIGJyb3dzZXJzLCBhbmQganNkb20gYmVmb3JlIHYyMiksXG4gKiBzbyBpdCBtdXN0IG5ldmVyIGJlIGNhbGxlZCB1bmd1YXJkZWQg4oCUIGFuIHVuZ3VhcmRlZCBjYWxsIHRocm93cyBhIFJlZmVyZW5jZUVycm9yIGFuZCB0aGVcbiAqIGNhbGxlciBzaWxlbnRseSBkb2VzIG5vdGhpbmcuIENvbmZpZyBhbmQgcnVsZSBvYmplY3RzIGFyZSBKU09OLXNhZmUsIHNvIHRoZSBmYWxsYmFjayBpc1xuICogbG9zc2xlc3MgaGVyZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDbG9uZTxUPih2YWx1ZTogVCk6IFQge1xuICBpZiAodHlwZW9mIHN0cnVjdHVyZWRDbG9uZSA9PT0gJ2Z1bmN0aW9uJykgcmV0dXJuIHN0cnVjdHVyZWRDbG9uZSh2YWx1ZSk7XG4gIHJldHVybiBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KHZhbHVlKSkgYXMgVDtcbn1cbiJdfQ==
@@ -1,84 +0,0 @@
1
- import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { DragDropModule } from '@angular/cdk/drag-drop';
4
- import { MatCardModule } from '@angular/material/card';
5
- import { MatIconModule } from '@angular/material/icon';
6
- import { BuilderStore } from '../builder-store.service';
7
- import { EntityBuilderTreeNodeComponent } from './entity-builder-tree-node.component';
8
- import * as i0 from "@angular/core";
9
- import * as i1 from "@angular/cdk/drag-drop";
10
- import * as i2 from "@angular/material/card";
11
- import * as i3 from "@angular/material/icon";
12
- export class EntityBuilderCanvasComponent {
13
- store = inject(BuilderStore);
14
- onDrop(event) {
15
- this.store.reorderField(event.previousIndex, event.currentIndex);
16
- }
17
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EntityBuilderCanvasComponent, isStandalone: true, selector: "ngx-entity-builder-canvas", ngImport: i0, template: `
19
- <mat-card class="deb-canvas">
20
- <mat-card-header>
21
- <mat-card-title>Fields ({{ store.fields().length }})</mat-card-title>
22
- </mat-card-header>
23
- <mat-card-content>
24
- @if (store.fields().length === 0) {
25
- <div class="deb-empty">
26
- <mat-icon>widgets</mat-icon>
27
- <p>No fields yet. Pick a type from <strong>Add field</strong> to get started.</p>
28
- </div>
29
- }
30
-
31
- <div cdkDropList class="deb-field-list" data-testid="builder-field-list" (cdkDropListDropped)="onDrop($event)">
32
- @for (f of store.fields(); track f.id; let i = $index, count = $count) {
33
- <ngx-entity-builder-tree-node
34
- [field]="f"
35
- [index]="i"
36
- [totalCount]="count"
37
- />
38
- }
39
- </div>
40
- </mat-card-content>
41
- </mat-card>
42
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i2.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i2.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i2.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: EntityBuilderTreeNodeComponent, selector: "ngx-entity-builder-tree-node", inputs: ["field", "index", "totalCount"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
43
- }
44
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderCanvasComponent, decorators: [{
45
- type: Component,
46
- args: [{
47
- changeDetection: ChangeDetectionStrategy.OnPush,
48
- selector: 'ngx-entity-builder-canvas',
49
- standalone: true,
50
- imports: [
51
- CommonModule,
52
- DragDropModule,
53
- MatCardModule,
54
- MatIconModule,
55
- EntityBuilderTreeNodeComponent,
56
- ],
57
- template: `
58
- <mat-card class="deb-canvas">
59
- <mat-card-header>
60
- <mat-card-title>Fields ({{ store.fields().length }})</mat-card-title>
61
- </mat-card-header>
62
- <mat-card-content>
63
- @if (store.fields().length === 0) {
64
- <div class="deb-empty">
65
- <mat-icon>widgets</mat-icon>
66
- <p>No fields yet. Pick a type from <strong>Add field</strong> to get started.</p>
67
- </div>
68
- }
69
-
70
- <div cdkDropList class="deb-field-list" data-testid="builder-field-list" (cdkDropListDropped)="onDrop($event)">
71
- @for (f of store.fields(); track f.id; let i = $index, count = $count) {
72
- <ngx-entity-builder-tree-node
73
- [field]="f"
74
- [index]="i"
75
- [totalCount]="count"
76
- />
77
- }
78
- </div>
79
- </mat-card-content>
80
- </mat-card>
81
- `,
82
- }]
83
- }] });
84
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWJ1aWxkZXItY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9lbnRpdHktYnVpbGRlci1jYW52YXMuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQWUsY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLDhCQUE4QixFQUFFLE1BQU0sc0NBQXNDLENBQUM7Ozs7O0FBdUN0RixNQUFNLE9BQU8sNEJBQTRCO0lBQ3BCLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFFdEMsTUFBTSxDQUFDLEtBQTJCO1FBQzFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ25FLENBQUM7d0dBTFUsNEJBQTRCOzRGQUE1Qiw0QkFBNEIscUZBMUI3Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JULDJEQTlCQyxZQUFZLDhCQUNaLGNBQWMsMmdCQUNkLGFBQWEsa1pBQ2IsYUFBYSxvTEFDYiw4QkFBOEI7OzRGQTRCckIsNEJBQTRCO2tCQXJDeEMsU0FBUzttQkFBQztvQkFDVCxlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtvQkFDL0MsUUFBUSxFQUFFLDJCQUEyQjtvQkFDckMsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLE9BQU8sRUFBRTt3QkFDUCxZQUFZO3dCQUNaLGNBQWM7d0JBQ2QsYUFBYTt3QkFDYixhQUFhO3dCQUNiLDhCQUE4QjtxQkFDL0I7b0JBQ0QsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F3QlQ7aUJBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ29tcG9uZW50LCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBDZGtEcmFnRHJvcCwgRHJhZ0Ryb3BNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jZGsvZHJhZy1kcm9wJztcbmltcG9ydCB7IE1hdENhcmRNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9jYXJkJztcbmltcG9ydCB7IE1hdEljb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pY29uJztcbmltcG9ydCB7IEJ1aWxkZXJTdG9yZSB9IGZyb20gJy4uL2J1aWxkZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBFbnRpdHlCdWlsZGVyVHJlZU5vZGVDb21wb25lbnQgfSBmcm9tICcuL2VudGl0eS1idWlsZGVyLXRyZWUtbm9kZS5jb21wb25lbnQnO1xuXG5AQ29tcG9uZW50KHtcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHNlbGVjdG9yOiAnbmd4LWVudGl0eS1idWlsZGVyLWNhbnZhcycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBDb21tb25Nb2R1bGUsXG4gICAgRHJhZ0Ryb3BNb2R1bGUsXG4gICAgTWF0Q2FyZE1vZHVsZSxcbiAgICBNYXRJY29uTW9kdWxlLFxuICAgIEVudGl0eUJ1aWxkZXJUcmVlTm9kZUNvbXBvbmVudCxcbiAgXSxcbiAgdGVtcGxhdGU6IGBcbiAgICA8bWF0LWNhcmQgY2xhc3M9XCJkZWItY2FudmFzXCI+XG4gICAgICA8bWF0LWNhcmQtaGVhZGVyPlxuICAgICAgICA8bWF0LWNhcmQtdGl0bGU+RmllbGRzICh7eyBzdG9yZS5maWVsZHMoKS5sZW5ndGggfX0pPC9tYXQtY2FyZC10aXRsZT5cbiAgICAgIDwvbWF0LWNhcmQtaGVhZGVyPlxuICAgICAgPG1hdC1jYXJkLWNvbnRlbnQ+XG4gICAgICAgIEBpZiAoc3RvcmUuZmllbGRzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImRlYi1lbXB0eVwiPlxuICAgICAgICAgICAgPG1hdC1pY29uPndpZGdldHM8L21hdC1pY29uPlxuICAgICAgICAgICAgPHA+Tm8gZmllbGRzIHlldC4gUGljayBhIHR5cGUgZnJvbSA8c3Ryb25nPkFkZCBmaWVsZDwvc3Ryb25nPiB0byBnZXQgc3RhcnRlZC48L3A+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cblxuICAgICAgICA8ZGl2IGNka0Ryb3BMaXN0IGNsYXNzPVwiZGViLWZpZWxkLWxpc3RcIiBkYXRhLXRlc3RpZD1cImJ1aWxkZXItZmllbGQtbGlzdFwiIChjZGtEcm9wTGlzdERyb3BwZWQpPVwib25Ecm9wKCRldmVudClcIj5cbiAgICAgICAgICBAZm9yIChmIG9mIHN0b3JlLmZpZWxkcygpOyB0cmFjayBmLmlkOyBsZXQgaSA9ICRpbmRleCwgY291bnQgPSAkY291bnQpIHtcbiAgICAgICAgICAgIDxuZ3gtZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlXG4gICAgICAgICAgICAgIFtmaWVsZF09XCJmXCJcbiAgICAgICAgICAgICAgW2luZGV4XT1cImlcIlxuICAgICAgICAgICAgICBbdG90YWxDb3VudF09XCJjb3VudFwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L21hdC1jYXJkLWNvbnRlbnQ+XG4gICAgPC9tYXQtY2FyZD5cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgRW50aXR5QnVpbGRlckNhbnZhc0NvbXBvbmVudCB7XG4gIHByb3RlY3RlZCByZWFkb25seSBzdG9yZSA9IGluamVjdChCdWlsZGVyU3RvcmUpO1xuXG4gIHByb3RlY3RlZCBvbkRyb3AoZXZlbnQ6IENka0RyYWdEcm9wPHVua25vd24+KTogdm9pZCB7XG4gICAgdGhpcy5zdG9yZS5yZW9yZGVyRmllbGQoZXZlbnQucHJldmlvdXNJbmRleCwgZXZlbnQuY3VycmVudEluZGV4KTtcbiAgfVxufVxuIl19
@@ -1,192 +0,0 @@
1
- import { ChangeDetectionStrategy, Component, Input, inject } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { DragDropModule } from '@angular/cdk/drag-drop';
4
- import { MatButtonModule } from '@angular/material/button';
5
- import { MatIconModule } from '@angular/material/icon';
6
- import { MatTooltipModule } from '@angular/material/tooltip';
7
- import { resolveLabel } from '@dynamic-entity/core';
8
- import { BuilderStore } from '../builder-store.service';
9
- import { getFieldTypeMeta } from '../field-catalog';
10
- import * as i0 from "@angular/core";
11
- import * as i1 from "@angular/cdk/drag-drop";
12
- import * as i2 from "@angular/material/button";
13
- import * as i3 from "@angular/material/icon";
14
- import * as i4 from "@angular/material/tooltip";
15
- export class EntityBuilderTreeNodeComponent {
16
- store = inject(BuilderStore);
17
- field;
18
- index;
19
- totalCount;
20
- fieldTypeLabel(type) {
21
- return getFieldTypeMeta(type)?.label ?? type;
22
- }
23
- fieldTypeIcon(type) {
24
- return getFieldTypeMeta(type)?.icon ?? 'help_outline';
25
- }
26
- fieldLabel(field) {
27
- const lang = this.store.activeLanguage();
28
- return resolveLabel(field.label, lang) || field.id;
29
- }
30
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
31
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EntityBuilderTreeNodeComponent, isStandalone: true, selector: "ngx-entity-builder-tree-node", inputs: { field: "field", index: "index", totalCount: "totalCount" }, ngImport: i0, template: `
32
- <div
33
- cdkDrag
34
- class="deb-field-row"
35
- data-testid="builder-field-row"
36
- [class.deb-field-row--active]="store.selectedFieldId() === field.id"
37
- (click)="store.selectField(field.id)"
38
- (keydown.enter)="store.selectField(field.id)"
39
- (keydown.space)="store.selectField(field.id); $event.preventDefault()"
40
- role="button"
41
- tabindex="0"
42
- [attr.aria-pressed]="store.selectedFieldId() === field.id"
43
- [attr.aria-label]="'Select field ' + fieldLabel(field)"
44
- >
45
- <mat-icon class="deb-drag-handle" cdkDragHandle aria-hidden="true" title="Drag to reorder (or use the move buttons)">drag_indicator</mat-icon>
46
- <mat-icon class="deb-field-type" [matTooltip]="fieldTypeLabel(field.type)">{{ fieldTypeIcon(field.type) }}</mat-icon>
47
- <div class="deb-field-meta">
48
- <div class="deb-row" style="gap:6px">
49
- <span class="deb-field-label" [attr.data-testid]="'row-label-' + field.id">{{ fieldLabel(field) }}</span>
50
- <span class="deb-type-badge">{{ field.type }}</span>
51
- @if (field.isReferenced) {
52
- <mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #1976d2;" matTooltip="Referenced field (linked to {{ field.referencedEntityKey }})">link</mat-icon>
53
- }
54
- @if (field.hasDrift) {
55
- <mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #d32f2f;" data-testid="drift-warning-icon" matTooltip="Drift detected: source field has evolved">sync_problem</mat-icon>
56
- }
57
- </div>
58
- <span class="deb-field-id" [attr.data-testid]="'row-id-' + field.id">{{ field.id }}</span>
59
- </div>
60
- @if (field.validators?.required) { <span class="deb-req" title="Required">*</span> }
61
-
62
- <button mat-icon-button type="button" [disabled]="index === 0"
63
- [attr.data-testid]="'row-up-' + field.id"
64
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' up'"
65
- (click)="store.moveField(field.id, -1); $event.stopPropagation()" matTooltip="Move up">
66
- <mat-icon>arrow_upward</mat-icon>
67
- </button>
68
- <button mat-icon-button type="button" [disabled]="index === totalCount - 1"
69
- [attr.data-testid]="'row-down-' + field.id"
70
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' down'"
71
- (click)="store.moveField(field.id, 1); $event.stopPropagation()" matTooltip="Move down">
72
- <mat-icon>arrow_downward</mat-icon>
73
- </button>
74
- <button mat-icon-button type="button"
75
- [attr.data-testid]="'row-duplicate-' + field.id"
76
- [attr.aria-label]="'Duplicate ' + fieldLabel(field)"
77
- (click)="store.duplicateField(field.id); $event.stopPropagation()" matTooltip="Duplicate">
78
- <mat-icon>content_copy</mat-icon>
79
- </button>
80
- <button mat-icon-button type="button" color="warn"
81
- [attr.data-testid]="'row-delete-' + field.id"
82
- (click)="store.removeField(field.id); $event.stopPropagation()" matTooltip="Delete">
83
- <mat-icon>delete</mat-icon>
84
- </button>
85
- </div>
86
-
87
- @if (field.children && field.children.length > 0) {
88
- <div class="deb-tree-children" style="padding-left: 20px; border-left: 2px solid rgba(0,0,0,0.08); margin-left: 12px; margin-top: 4px;">
89
- @for (child of field.children; track child.id; let ci = $index, cCount = $count) {
90
- <ngx-entity-builder-tree-node
91
- [field]="child"
92
- [index]="ci"
93
- [totalCount]="cCount"
94
- />
95
- }
96
- </div>
97
- }
98
- `, isInline: true, dependencies: [{ kind: "component", type: EntityBuilderTreeNodeComponent, selector: "ngx-entity-builder-tree-node", inputs: ["field", "index", "totalCount"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
99
- }
100
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderTreeNodeComponent, decorators: [{
101
- type: Component,
102
- args: [{
103
- changeDetection: ChangeDetectionStrategy.OnPush,
104
- selector: 'ngx-entity-builder-tree-node',
105
- standalone: true,
106
- imports: [
107
- CommonModule,
108
- DragDropModule,
109
- MatButtonModule,
110
- MatIconModule,
111
- MatTooltipModule,
112
- ],
113
- template: `
114
- <div
115
- cdkDrag
116
- class="deb-field-row"
117
- data-testid="builder-field-row"
118
- [class.deb-field-row--active]="store.selectedFieldId() === field.id"
119
- (click)="store.selectField(field.id)"
120
- (keydown.enter)="store.selectField(field.id)"
121
- (keydown.space)="store.selectField(field.id); $event.preventDefault()"
122
- role="button"
123
- tabindex="0"
124
- [attr.aria-pressed]="store.selectedFieldId() === field.id"
125
- [attr.aria-label]="'Select field ' + fieldLabel(field)"
126
- >
127
- <mat-icon class="deb-drag-handle" cdkDragHandle aria-hidden="true" title="Drag to reorder (or use the move buttons)">drag_indicator</mat-icon>
128
- <mat-icon class="deb-field-type" [matTooltip]="fieldTypeLabel(field.type)">{{ fieldTypeIcon(field.type) }}</mat-icon>
129
- <div class="deb-field-meta">
130
- <div class="deb-row" style="gap:6px">
131
- <span class="deb-field-label" [attr.data-testid]="'row-label-' + field.id">{{ fieldLabel(field) }}</span>
132
- <span class="deb-type-badge">{{ field.type }}</span>
133
- @if (field.isReferenced) {
134
- <mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #1976d2;" matTooltip="Referenced field (linked to {{ field.referencedEntityKey }})">link</mat-icon>
135
- }
136
- @if (field.hasDrift) {
137
- <mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #d32f2f;" data-testid="drift-warning-icon" matTooltip="Drift detected: source field has evolved">sync_problem</mat-icon>
138
- }
139
- </div>
140
- <span class="deb-field-id" [attr.data-testid]="'row-id-' + field.id">{{ field.id }}</span>
141
- </div>
142
- @if (field.validators?.required) { <span class="deb-req" title="Required">*</span> }
143
-
144
- <button mat-icon-button type="button" [disabled]="index === 0"
145
- [attr.data-testid]="'row-up-' + field.id"
146
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' up'"
147
- (click)="store.moveField(field.id, -1); $event.stopPropagation()" matTooltip="Move up">
148
- <mat-icon>arrow_upward</mat-icon>
149
- </button>
150
- <button mat-icon-button type="button" [disabled]="index === totalCount - 1"
151
- [attr.data-testid]="'row-down-' + field.id"
152
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' down'"
153
- (click)="store.moveField(field.id, 1); $event.stopPropagation()" matTooltip="Move down">
154
- <mat-icon>arrow_downward</mat-icon>
155
- </button>
156
- <button mat-icon-button type="button"
157
- [attr.data-testid]="'row-duplicate-' + field.id"
158
- [attr.aria-label]="'Duplicate ' + fieldLabel(field)"
159
- (click)="store.duplicateField(field.id); $event.stopPropagation()" matTooltip="Duplicate">
160
- <mat-icon>content_copy</mat-icon>
161
- </button>
162
- <button mat-icon-button type="button" color="warn"
163
- [attr.data-testid]="'row-delete-' + field.id"
164
- (click)="store.removeField(field.id); $event.stopPropagation()" matTooltip="Delete">
165
- <mat-icon>delete</mat-icon>
166
- </button>
167
- </div>
168
-
169
- @if (field.children && field.children.length > 0) {
170
- <div class="deb-tree-children" style="padding-left: 20px; border-left: 2px solid rgba(0,0,0,0.08); margin-left: 12px; margin-top: 4px;">
171
- @for (child of field.children; track child.id; let ci = $index, cCount = $count) {
172
- <ngx-entity-builder-tree-node
173
- [field]="child"
174
- [index]="ci"
175
- [totalCount]="cCount"
176
- />
177
- }
178
- </div>
179
- }
180
- `,
181
- }]
182
- }], propDecorators: { field: [{
183
- type: Input,
184
- args: [{ required: true }]
185
- }], index: [{
186
- type: Input,
187
- args: [{ required: true }]
188
- }], totalCount: [{
189
- type: Input,
190
- args: [{ required: true }]
191
- }] } });
192
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9lbnRpdHktYnVpbGRlci10cmVlLW5vZGUuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNsRixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3hELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBMEIsTUFBTSxzQkFBc0IsQ0FBQztBQUM1RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7Ozs7OztBQWtGcEQsTUFBTSxPQUFPLDhCQUE4QjtJQUN0QixLQUFLLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRXJCLEtBQUssQ0FBcUI7SUFDMUIsS0FBSyxDQUFVO0lBQ2YsVUFBVSxDQUFVO0lBRXJDLGNBQWMsQ0FBQyxJQUFZO1FBQ25DLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQztJQUMvQyxDQUFDO0lBRVMsYUFBYSxDQUFDLElBQVk7UUFDbEMsT0FBTyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLElBQUksY0FBYyxDQUFDO0lBQ3hELENBQUM7SUFFUyxVQUFVLENBQUMsS0FBa0M7UUFDckQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN6QyxPQUFPLFlBQVksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUM7SUFDckQsQ0FBQzt3R0FsQlUsOEJBQThCOzRGQUE5Qiw4QkFBOEIsOEpBckUvQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1FVCw0REFFVSw4QkFBOEIsa0hBM0V2QyxZQUFZLDhCQUNaLGNBQWMsK2tCQUNkLGVBQWUsMklBQ2YsYUFBYSxtTEFDYixnQkFBZ0I7OzRGQXVFUCw4QkFBOEI7a0JBaEYxQyxTQUFTO21CQUFDO29CQUNULGVBQWUsRUFBRSx1QkFBdUIsQ0FBQyxNQUFNO29CQUMvQyxRQUFRLEVBQUUsOEJBQThCO29CQUN4QyxVQUFVLEVBQUUsSUFBSTtvQkFDaEIsT0FBTyxFQUFFO3dCQUNQLFlBQVk7d0JBQ1osY0FBYzt3QkFDZCxlQUFlO3dCQUNmLGFBQWE7d0JBQ2IsZ0JBQWdCO3FCQUNqQjtvQkFDRCxRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtRVQ7aUJBQ0Y7OEJBSTRCLEtBQUs7c0JBQS9CLEtBQUs7dUJBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO2dCQUNFLEtBQUs7c0JBQS9CLEtBQUs7dUJBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO2dCQUNFLFVBQVU7c0JBQXBDLEtBQUs7dUJBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgSW5wdXQsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IERyYWdEcm9wTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2RyYWctZHJvcCc7XG5pbXBvcnQgeyBNYXRCdXR0b25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9idXR0b24nO1xuaW1wb3J0IHsgTWF0SWNvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuaW1wb3J0IHsgcmVzb2x2ZUxhYmVsLCB0eXBlIE5lc3RlZEZpZWxkQ29uZmlnIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgQnVpbGRlclN0b3JlIH0gZnJvbSAnLi4vYnVpbGRlci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IGdldEZpZWxkVHlwZU1ldGEgfSBmcm9tICcuLi9maWVsZC1jYXRhbG9nJztcblxuQENvbXBvbmVudCh7XG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzZWxlY3RvcjogJ25neC1lbnRpdHktYnVpbGRlci10cmVlLW5vZGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIERyYWdEcm9wTW9kdWxlLFxuICAgIE1hdEJ1dHRvbk1vZHVsZSxcbiAgICBNYXRJY29uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGUsXG4gIF0sXG4gIHRlbXBsYXRlOiBgXG4gICAgPGRpdlxuICAgICAgY2RrRHJhZ1xuICAgICAgY2xhc3M9XCJkZWItZmllbGQtcm93XCJcbiAgICAgIGRhdGEtdGVzdGlkPVwiYnVpbGRlci1maWVsZC1yb3dcIlxuICAgICAgW2NsYXNzLmRlYi1maWVsZC1yb3ctLWFjdGl2ZV09XCJzdG9yZS5zZWxlY3RlZEZpZWxkSWQoKSA9PT0gZmllbGQuaWRcIlxuICAgICAgKGNsaWNrKT1cInN0b3JlLnNlbGVjdEZpZWxkKGZpZWxkLmlkKVwiXG4gICAgICAoa2V5ZG93bi5lbnRlcik9XCJzdG9yZS5zZWxlY3RGaWVsZChmaWVsZC5pZClcIlxuICAgICAgKGtleWRvd24uc3BhY2UpPVwic3RvcmUuc2VsZWN0RmllbGQoZmllbGQuaWQpOyAkZXZlbnQucHJldmVudERlZmF1bHQoKVwiXG4gICAgICByb2xlPVwiYnV0dG9uXCJcbiAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICBbYXR0ci5hcmlhLXByZXNzZWRdPVwic3RvcmUuc2VsZWN0ZWRGaWVsZElkKCkgPT09IGZpZWxkLmlkXCJcbiAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ1NlbGVjdCBmaWVsZCAnICsgZmllbGRMYWJlbChmaWVsZClcIlxuICAgID5cbiAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImRlYi1kcmFnLWhhbmRsZVwiIGNka0RyYWdIYW5kbGUgYXJpYS1oaWRkZW49XCJ0cnVlXCIgdGl0bGU9XCJEcmFnIHRvIHJlb3JkZXIgKG9yIHVzZSB0aGUgbW92ZSBidXR0b25zKVwiPmRyYWdfaW5kaWNhdG9yPC9tYXQtaWNvbj5cbiAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImRlYi1maWVsZC10eXBlXCIgW21hdFRvb2x0aXBdPVwiZmllbGRUeXBlTGFiZWwoZmllbGQudHlwZSlcIj57eyBmaWVsZFR5cGVJY29uKGZpZWxkLnR5cGUpIH19PC9tYXQtaWNvbj5cbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItZmllbGQtbWV0YVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiZGViLXJvd1wiIHN0eWxlPVwiZ2FwOjZweFwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLWZpZWxkLWxhYmVsXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy1sYWJlbC0nICsgZmllbGQuaWRcIj57eyBmaWVsZExhYmVsKGZpZWxkKSB9fTwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cImRlYi10eXBlLWJhZGdlXCI+e3sgZmllbGQudHlwZSB9fTwvc3Bhbj5cbiAgICAgICAgICBAaWYgKGZpZWxkLmlzUmVmZXJlbmNlZCkge1xuICAgICAgICAgICAgPG1hdC1pY29uIHN0eWxlPVwiZm9udC1zaXplOiAxNnB4OyB3aWR0aDogMTZweDsgaGVpZ2h0OiAxNnB4OyBjb2xvcjogIzE5NzZkMjtcIiBtYXRUb29sdGlwPVwiUmVmZXJlbmNlZCBmaWVsZCAobGlua2VkIHRvIHt7IGZpZWxkLnJlZmVyZW5jZWRFbnRpdHlLZXkgfX0pXCI+bGluazwvbWF0LWljb24+XG4gICAgICAgICAgfVxuICAgICAgICAgIEBpZiAoZmllbGQuaGFzRHJpZnQpIHtcbiAgICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZTogMTZweDsgd2lkdGg6IDE2cHg7IGhlaWdodDogMTZweDsgY29sb3I6ICNkMzJmMmY7XCIgZGF0YS10ZXN0aWQ9XCJkcmlmdC13YXJuaW5nLWljb25cIiBtYXRUb29sdGlwPVwiRHJpZnQgZGV0ZWN0ZWQ6IHNvdXJjZSBmaWVsZCBoYXMgZXZvbHZlZFwiPnN5bmNfcHJvYmxlbTwvbWF0LWljb24+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJkZWItZmllbGQtaWRcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LWlkLScgKyBmaWVsZC5pZFwiPnt7IGZpZWxkLmlkIH19PC9zcGFuPlxuICAgICAgPC9kaXY+XG4gICAgICBAaWYgKGZpZWxkLnZhbGlkYXRvcnM/LnJlcXVpcmVkKSB7IDxzcGFuIGNsYXNzPVwiZGViLXJlcVwiIHRpdGxlPVwiUmVxdWlyZWRcIj4qPC9zcGFuPiB9XG5cbiAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBbZGlzYWJsZWRdPVwiaW5kZXggPT09IDBcIlxuICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LXVwLScgKyBmaWVsZC5pZFwiXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ01vdmUgJyArIGZpZWxkTGFiZWwoZmllbGQpICsgJyB1cCdcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUubW92ZUZpZWxkKGZpZWxkLmlkLCAtMSk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiIG1hdFRvb2x0aXA9XCJNb3ZlIHVwXCI+XG4gICAgICAgIDxtYXQtaWNvbj5hcnJvd191cHdhcmQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgW2Rpc2FibGVkXT1cImluZGV4ID09PSB0b3RhbENvdW50IC0gMVwiXG4gICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidyb3ctZG93bi0nICsgZmllbGQuaWRcIlxuICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidNb3ZlICcgKyBmaWVsZExhYmVsKGZpZWxkKSArICcgZG93bidcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUubW92ZUZpZWxkKGZpZWxkLmlkLCAxKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCIgbWF0VG9vbHRpcD1cIk1vdmUgZG93blwiPlxuICAgICAgICA8bWF0LWljb24+YXJyb3dfZG93bndhcmQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy1kdXBsaWNhdGUtJyArIGZpZWxkLmlkXCJcbiAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInRHVwbGljYXRlICcgKyBmaWVsZExhYmVsKGZpZWxkKVwiXG4gICAgICAgIChjbGljayk9XCJzdG9yZS5kdXBsaWNhdGVGaWVsZChmaWVsZC5pZCk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiIG1hdFRvb2x0aXA9XCJEdXBsaWNhdGVcIj5cbiAgICAgICAgPG1hdC1pY29uPmNvbnRlbnRfY29weTwvbWF0LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBjb2xvcj1cIndhcm5cIlxuICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LWRlbGV0ZS0nICsgZmllbGQuaWRcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUucmVtb3ZlRmllbGQoZmllbGQuaWQpOyAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIiBtYXRUb29sdGlwPVwiRGVsZXRlXCI+XG4gICAgICAgIDxtYXQtaWNvbj5kZWxldGU8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgPC9kaXY+XG5cbiAgICBAaWYgKGZpZWxkLmNoaWxkcmVuICYmIGZpZWxkLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItdHJlZS1jaGlsZHJlblwiIHN0eWxlPVwicGFkZGluZy1sZWZ0OiAyMHB4OyBib3JkZXItbGVmdDogMnB4IHNvbGlkIHJnYmEoMCwwLDAsMC4wOCk7IG1hcmdpbi1sZWZ0OiAxMnB4OyBtYXJnaW4tdG9wOiA0cHg7XCI+XG4gICAgICAgIEBmb3IgKGNoaWxkIG9mIGZpZWxkLmNoaWxkcmVuOyB0cmFjayBjaGlsZC5pZDsgbGV0IGNpID0gJGluZGV4LCBjQ291bnQgPSAkY291bnQpIHtcbiAgICAgICAgICA8bmd4LWVudGl0eS1idWlsZGVyLXRyZWUtbm9kZVxuICAgICAgICAgICAgW2ZpZWxkXT1cImNoaWxkXCJcbiAgICAgICAgICAgIFtpbmRleF09XCJjaVwiXG4gICAgICAgICAgICBbdG90YWxDb3VudF09XCJjQ291bnRcIlxuICAgICAgICAgIC8+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgRW50aXR5QnVpbGRlclRyZWVOb2RlQ29tcG9uZW50IHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHN0b3JlID0gaW5qZWN0KEJ1aWxkZXJTdG9yZSk7XG5cbiAgQElucHV0KHsgcmVxdWlyZWQ6IHRydWUgfSkgZmllbGQhOiBOZXN0ZWRGaWVsZENvbmZpZztcbiAgQElucHV0KHsgcmVxdWlyZWQ6IHRydWUgfSkgaW5kZXghOiBudW1iZXI7XG4gIEBJbnB1dCh7IHJlcXVpcmVkOiB0cnVlIH0pIHRvdGFsQ291bnQhOiBudW1iZXI7XG5cbiAgcHJvdGVjdGVkIGZpZWxkVHlwZUxhYmVsKHR5cGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGdldEZpZWxkVHlwZU1ldGEodHlwZSk/LmxhYmVsID8/IHR5cGU7XG4gIH1cblxuICBwcm90ZWN0ZWQgZmllbGRUeXBlSWNvbih0eXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBnZXRGaWVsZFR5cGVNZXRhKHR5cGUpPy5pY29uID8/ICdoZWxwX291dGxpbmUnO1xuICB9XG5cbiAgcHJvdGVjdGVkIGZpZWxkTGFiZWwoZmllbGQ6IHsgaWQ6IHN0cmluZzsgbGFiZWw/OiBhbnkgfSk6IHN0cmluZyB7XG4gICAgY29uc3QgbGFuZyA9IHRoaXMuc3RvcmUuYWN0aXZlTGFuZ3VhZ2UoKTtcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCBsYW5nKSB8fCBmaWVsZC5pZDtcbiAgfVxufVxuIl19