@firestitch/report 18.0.6 → 18.0.8

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 (20) hide show
  1. package/app/reports/data/report.data.d.ts +1 -0
  2. package/app/reports/dialogs/global-settings/components/ai-knowledge/ai-knowledge.component.d.ts +8 -0
  3. package/app/reports/dialogs/global-settings/components/history/history.component.d.ts +8 -0
  4. package/app/reports/dialogs/global-settings/components/semantic-database/semantic-database.component.d.ts +50 -0
  5. package/app/reports/dialogs/global-settings/global-settings.component.d.ts +6 -16
  6. package/app/reports/interfaces/report.interface.d.ts +29 -0
  7. package/esm2022/app/reports/data/report.data.mjs +10 -1
  8. package/esm2022/app/reports/dialogs/global-settings/components/ai-knowledge/ai-knowledge.component.mjs +31 -0
  9. package/esm2022/app/reports/dialogs/global-settings/components/history/history.component.mjs +32 -0
  10. package/esm2022/app/reports/dialogs/global-settings/components/semantic-database/semantic-database.component.mjs +195 -0
  11. package/esm2022/app/reports/dialogs/global-settings/global-settings.component.mjs +36 -97
  12. package/esm2022/app/reports/interfaces/report.interface.mjs +1 -1
  13. package/esm2022/app/reports/views/report/report.component.mjs +3 -3
  14. package/fesm2022/{firestitch-report-echarts-BLg3-O3v.mjs → firestitch-report-echarts-BMA8i0eA.mjs} +2 -2
  15. package/fesm2022/{firestitch-report-echarts-BLg3-O3v.mjs.map → firestitch-report-echarts-BMA8i0eA.mjs.map} +1 -1
  16. package/fesm2022/{firestitch-report-firestitch-report-DjYxPwAG.mjs → firestitch-report-firestitch-report-CrMT9lNQ.mjs} +277 -100
  17. package/fesm2022/firestitch-report-firestitch-report-CrMT9lNQ.mjs.map +1 -0
  18. package/fesm2022/firestitch-report.mjs +1 -1
  19. package/package.json +1 -1
  20. package/fesm2022/firestitch-report-firestitch-report-DjYxPwAG.mjs.map +0 -1
@@ -0,0 +1,195 @@
1
+ import { ChangeDetectionStrategy, Component, DestroyRef, computed, inject, signal } from '@angular/core';
2
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { MatButton } from '@angular/material/button';
4
+ import { MatExpansionModule } from '@angular/material/expansion';
5
+ import { MatTooltipModule } from '@angular/material/tooltip';
6
+ import { StreamEventType } from '@firestitch/api';
7
+ import { FsChipModule } from '@firestitch/chip';
8
+ import { FsDateModule } from '@firestitch/date';
9
+ import { FsMessage, FsMessageModule } from '@firestitch/message';
10
+ import { FsProcess } from '@firestitch/process';
11
+ import { FsPrompt } from '@firestitch/prompt';
12
+ import { FsSkeletonModule } from '@firestitch/skeleton';
13
+ import { filter, map, switchMap, take } from 'rxjs/operators';
14
+ import { ReportData } from '../../../../data/report.data';
15
+ import * as i0 from "@angular/core";
16
+ import * as i1 from "@angular/material/expansion";
17
+ import * as i2 from "@angular/material/tooltip";
18
+ import * as i3 from "@firestitch/chip";
19
+ import * as i4 from "@firestitch/date";
20
+ import * as i5 from "@firestitch/message";
21
+ import * as i6 from "@firestitch/skeleton";
22
+ // The Semantic Database tab: the model scoreboard the report AI is built from.
23
+ // Shows the last build's status, runs (or stops) a build — streamed into the
24
+ // FsProcess dock so it survives the dialog closing — and drills into every
25
+ // entity to reveal its shape, inlined enum values, computed fields and the
26
+ // AI-classified code functions cached from its model class. Each entity's
27
+ // functions load lazily on first expand, because the full set across all
28
+ // entities is far too big to ship with the scoreboard.
29
+ export class SemanticDatabaseComponent {
30
+ // The build-in-flight states of the ff_processes row (queued/running/stopping).
31
+ static ACTIVE_STATES = ['Q', 'R', 'X'];
32
+ static STATE_LABELS = {
33
+ Q: 'Queued',
34
+ R: 'Running',
35
+ X: 'Stopping',
36
+ C: 'Completed',
37
+ K: 'Stopped',
38
+ F: 'Failed',
39
+ };
40
+ // Scoreboard completion palette: built = green, everything else (needs
41
+ // building) = grey. No error red — a missing stage is unbuilt content, not a
42
+ // failure.
43
+ static DONE_COLOR = '#2e7d32';
44
+ static PENDING_COLOR = '#9e9e9e';
45
+ // One color per AI classification kind, so a function's nature reads at a glance.
46
+ static KIND_COLORS = {
47
+ 'calculation': '#1565c0',
48
+ 'business-rule': '#6a1b9a',
49
+ 'query': '#00695c',
50
+ 'mutation': '#e65100',
51
+ 'accessor': '#546e7a',
52
+ 'other': '#757575',
53
+ };
54
+ // Interesting logic first: derived values and rules, then data access, then the rest.
55
+ static KIND_ORDER = ['calculation', 'business-rule', 'query', 'mutation', 'accessor', 'other'];
56
+ // The scoreboard's per-entity stage chips, left to right.
57
+ stageKeys = ['introspected', 'enumsInlined', 'computed', 'functions'];
58
+ stageLabels = {
59
+ introspected: 'Introspected',
60
+ enumsInlined: 'Enum cases',
61
+ computed: 'Computed',
62
+ functions: 'Functions',
63
+ };
64
+ // null until loaded — gates the tab behind the skeleton.
65
+ status = signal(null);
66
+ // True while a build started from this tab is streaming into the dock.
67
+ building = signal(false);
68
+ buildActive = computed(() => this.building()
69
+ || SemanticDatabaseComponent.ACTIVE_STATES.includes(this.status()?.build?.state));
70
+ buildStateLabel = computed(() => SemanticDatabaseComponent.STATE_LABELS[this.status()?.build?.state] ?? '');
71
+ // Per-entity function caches, loaded on first expand. undefined = not
72
+ // requested yet, null = request in flight (the skeleton window), object = loaded.
73
+ _entityFunctions = signal({});
74
+ _reportData = inject(ReportData);
75
+ _process = inject(FsProcess);
76
+ _prompt = inject(FsPrompt);
77
+ _message = inject(FsMessage);
78
+ _destroyRef = inject(DestroyRef);
79
+ constructor() {
80
+ this._init();
81
+ }
82
+ // Run the semantic-database build. The live feed streams into the FsProcess
83
+ // dock (bottom-right), whose subscription survives this dialog closing — so
84
+ // dismissing the dialog never interrupts a running build.
85
+ build() {
86
+ this.building.set(true);
87
+ const target$ = this._reportData.semanticBuild()
88
+ .pipe(filter((event) => event?.type === StreamEventType.Data
89
+ && event.data?.kind === 'log'
90
+ && typeof event.data.line === 'string'), map((event) => event.data.line));
91
+ this._process
92
+ .run('Semantic database build', target$)
93
+ .completed$
94
+ .pipe(take(1), takeUntilDestroyed(this._destroyRef))
95
+ .subscribe(() => {
96
+ this.building.set(false);
97
+ // The build rewrote every entity's function cache — drop the loaded
98
+ // copies so the next expand refetches fresh data.
99
+ this._entityFunctions.set({});
100
+ this._refreshStatus();
101
+ });
102
+ }
103
+ stopBuild() {
104
+ this._prompt.confirm({
105
+ title: 'Stop build',
106
+ template: 'Stop the running build? The current semantic database stays in place until a build finishes.',
107
+ commitLabel: 'Stop',
108
+ })
109
+ .pipe(switchMap(() => this._reportData.semanticBuildStop()), takeUntilDestroyed(this._destroyRef))
110
+ .subscribe(() => {
111
+ this.building.set(false);
112
+ this._message.success('Stopping the build…');
113
+ this._refreshStatus();
114
+ });
115
+ }
116
+ stageColor(stage) {
117
+ return stage === 'done'
118
+ ? SemanticDatabaseComponent.DONE_COLOR
119
+ : SemanticDatabaseComponent.PENDING_COLOR;
120
+ }
121
+ stageTooltip(stage) {
122
+ return stage === 'done' ? 'Built' : 'Needs building';
123
+ }
124
+ kindColor(kind) {
125
+ return SemanticDatabaseComponent.KIND_COLORS[kind] ?? SemanticDatabaseComponent.KIND_COLORS.other;
126
+ }
127
+ /**
128
+ * The loaded function cache for an entity: undefined until the panel is first
129
+ * expanded, null while the request is in flight (the skeleton window), then
130
+ * the loaded cache.
131
+ */
132
+ entityFunctions(key) {
133
+ return this._entityFunctions()[key];
134
+ }
135
+ /**
136
+ * First expand of an entity: fetch its cached functions through the per-entity
137
+ * endpoint. Subsequent expands reuse the loaded copy (a finished build clears
138
+ * the copies so a re-expand refetches fresh data).
139
+ */
140
+ entityOpened(entity) {
141
+ if (this._entityFunctions()[entity.key] !== undefined) {
142
+ return;
143
+ }
144
+ this._entityFunctions.update((loaded) => ({ ...loaded, [entity.key]: null }));
145
+ this._reportData.semanticEntityFunctions(entity.key)
146
+ .pipe(takeUntilDestroyed(this._destroyRef))
147
+ .subscribe((entityFunctions) => {
148
+ this._entityFunctions.update((loaded) => ({
149
+ ...loaded,
150
+ [entity.key]: {
151
+ ...entityFunctions,
152
+ functions: this._sortFunctions(entityFunctions),
153
+ },
154
+ }));
155
+ });
156
+ }
157
+ _init() {
158
+ this._refreshStatus();
159
+ }
160
+ _refreshStatus() {
161
+ this._reportData.semanticBuildStatus()
162
+ .pipe(takeUntilDestroyed(this._destroyRef))
163
+ .subscribe((status) => this.status.set(status));
164
+ }
165
+ /**
166
+ * Order once at load time (kind priority, then method name) so the template
167
+ * renders the list as-is.
168
+ */
169
+ _sortFunctions(entityFunctions) {
170
+ return [...entityFunctions.functions]
171
+ .sort((a, b) => {
172
+ const kindDiff = this._kindRank(a.kind) - this._kindRank(b.kind);
173
+ return kindDiff !== 0 ? kindDiff : a.method.localeCompare(b.method);
174
+ });
175
+ }
176
+ _kindRank(kind) {
177
+ const rank = SemanticDatabaseComponent.KIND_ORDER.indexOf(kind);
178
+ return rank === -1 ? SemanticDatabaseComponent.KIND_ORDER.length : rank;
179
+ }
180
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SemanticDatabaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
181
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SemanticDatabaseComponent, isStandalone: true, selector: "fs-ai-report-global-settings-semantic-database", ngImport: i0, template: "<div class=\"fs-column tab-body\">\n <p class=\"hint\">\n The semantic database is what the report AI uses to understand your data \u2014\n tables, relationships and the calculations in code. Rebuild it after\n significant application updates. The build runs in the background; progress\n appears in the dock at the bottom right and this dialog can be closed while\n it runs.\n </p>\n\n <div *fsSkeleton=\"status()\">\n @if (status(); as buildStatus) {\n <div class=\"build-status\">\n @if (buildStatus.detail.generatedAt) {\n <div class=\"stat\">\n <span class=\"label\">Last built</span>\n <fs-date\n [date]=\"buildStatus.detail.generatedAt\"\n format=\"date-time\">\n </fs-date>\n </div>\n <div class=\"stat\">\n <span class=\"label\">Coverage</span>\n <span>\n {{ buildStatus.detail.entityCount }} entities \u00B7\n {{ buildStatus.detail.functionCount }} code functions\n </span>\n </div>\n } @else {\n <fs-message-warning>\n The semantic database has not been built yet \u2014 the report AI can't\n answer data questions until it is. Run the first build.\n </fs-message-warning>\n }\n @if (buildStatus.build) {\n <div class=\"stat\">\n <span class=\"label\">Last build run</span>\n <span>\n {{ buildStateLabel() }}\n @if (buildStatus.build.message) {\n \u2014 {{ buildStatus.build.message }}\n }\n </span>\n </div>\n }\n </div>\n\n <div class=\"build-actions\">\n @if (buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"stopBuild()\">\n Stop Build\n </button>\n } @else {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n (click)=\"build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n </div>\n\n <!-- The model scoreboard: every entity the AI knows about, its\n built-vs-needs-building stages, and a drill-down into its shape, enum\n values, computed fields and AI-classified code functions. -->\n @if (buildStatus.detail.entities?.length) {\n <mat-accordion class=\"entities\">\n @for (entity of buildStatus.detail.entities; track entity.key) {\n <mat-expansion-panel (opened)=\"entityOpened(entity)\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ entity.key }}</mat-panel-title>\n <mat-panel-description>\n <div class=\"stage-chips\">\n @for (stage of stageKeys; track stage) {\n <fs-chip\n size=\"tiny\"\n [backgroundColor]=\"stageColor(entity.stages[stage])\"\n color=\"#ffffff\"\n [matTooltip]=\"stageTooltip(entity.stages[stage])\">\n {{ stageLabels[stage] }}\n </fs-chip>\n }\n </div>\n </mat-panel-description>\n </mat-expansion-panel-header>\n\n <!-- Deferred: the body (and its functions request) only exists\n once expanded. -->\n <ng-template matExpansionPanelContent>\n <div class=\"entity-detail\">\n <div><strong>Table:</strong> {{ entity.table }}</div>\n <div><strong>Columns:</strong> {{ entity.columns }}</div>\n <div><strong>Relationships:</strong> {{ entity.relationships }}</div>\n @if (entity.enums.length) {\n <div class=\"enum-list\">\n <strong>Enum values:</strong>\n @for (enumColumn of entity.enums; track enumColumn.column) {\n <div class=\"enum-row\">\n <span class=\"enum-column\">{{ enumColumn.column }}</span>\n <span class=\"enum-values\">{{ enumColumn.values.join(', ') }}</span>\n </div>\n }\n </div>\n }\n @if (entity.computedFields.length) {\n <div><strong>Computed fields:</strong> {{ entity.computedFields.join(', ') }}</div>\n }\n\n <div class=\"functions-section\">\n <h3 class=\"functions-heading\">\n Code functions\n @if (entity.functionCount) {\n <span class=\"functions-count\">({{ entity.functionCount }})</span>\n }\n </h3>\n\n <div class=\"functions-body\" *fsSkeleton=\"entityFunctions(entity.key)\">\n @if (entityFunctions(entity.key); as fns) {\n @if (fns.functions.length) {\n @if (fns.generatedAt) {\n <div class=\"hint functions-generated\">\n Discovered from {{ fns.class }} &middot;\n <fs-date\n [date]=\"fns.generatedAt\"\n format=\"date-time\">\n </fs-date>\n </div>\n }\n <div class=\"functions-list\">\n @for (fn of fns.functions; track fn.method) {\n <div class=\"function-row\">\n <div class=\"function-head\">\n <fs-chip\n size=\"tiny\"\n [backgroundColor]=\"kindColor(fn.kind)\"\n color=\"#ffffff\">\n {{ fn.kind }}\n </fs-chip>\n <code class=\"function-method\">{{ fn.method }}</code>\n </div>\n <div class=\"function-summary\">{{ fn.summary }}</div>\n @if (fn.produces) {\n <div class=\"function-produces hint\">Produces: {{ fn.produces }}</div>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"hint\">\n No functions cached for this entity yet \u2014 run a build to discover its code.\n </div>\n }\n }\n </div>\n </div>\n </div>\n </ng-template>\n </mat-expansion-panel>\n }\n </mat-accordion>\n }\n }\n </div>\n</div>\n", styles: [".tab-body{padding-top:16px}.hint{margin:0 0 12px;color:#0009;font-size:13px;line-height:1.5}.build-status{display:flex;flex-direction:column;gap:8px}.build-status .stat{display:flex;gap:8px}.build-status .stat .label{min-width:110px;color:#0009}.build-actions{margin:12px 0}.entities{display:block}.entities .stage-chips{display:flex;flex-wrap:wrap;gap:4px}.entities .entity-detail{display:flex;flex-direction:column;gap:5px}.entities .entity-detail .enum-list{display:flex;flex-direction:column;gap:3px}.entities .entity-detail .enum-list .enum-row{display:flex;gap:10px;padding-left:10px}.entities .entity-detail .enum-list .enum-row .enum-column{min-width:140px;font-weight:600}.entities .entity-detail .enum-list .enum-row .enum-values{color:#0009}.entities .entity-detail .functions-section{margin-top:10px}.entities .entity-detail .functions-section .functions-heading{margin:0 0 5px;font-size:15px}.entities .entity-detail .functions-section .functions-heading .functions-count{font-weight:400;color:#0009}.entities .entity-detail .functions-section .functions-generated{margin-bottom:10px}.entities .entity-detail .functions-section .functions-list{display:flex;flex-direction:column}.entities .entity-detail .functions-section .functions-list .function-row{display:flex;flex-direction:column;gap:3px;padding:10px 0}.entities .entity-detail .functions-section .functions-list .function-row+.function-row{border-top:1px solid rgba(0,0,0,.08)}.entities .entity-detail .functions-section .functions-list .function-row .function-head{display:flex;align-items:center;gap:8px}.entities .entity-detail .functions-section .functions-list .function-row .function-method{font-family:monospace;font-size:13px;font-weight:600}.entities .entity-detail .functions-section .functions-list .function-row .function-summary{color:#000000bf}.entities .entity-detail .functions-section .functions-list .function-row .function-produces{padding-left:10px}\n"], dependencies: [{ kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i1.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "directive", type: i1.MatExpansionPanelContent, selector: "ng-template[matExpansionPanelContent]" }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: FsChipModule }, { kind: "component", type: i3.FsChipComponent, selector: "fs-chip", inputs: ["selectable", "removable", "value", "maxWidth", "width", "backgroundColor", "borderColor", "color", "shape", "outlined", "outlineDash", "icon", "image", "selected", "disabled", "padding", "contrastColor", "size"], outputs: ["selectedToggled", "removed", "click"] }, { kind: "ngmodule", type: FsDateModule }, { kind: "component", type: i4.FsDateComponent, selector: "fs-date", inputs: ["date", "format", "timezone"] }, { kind: "ngmodule", type: FsMessageModule }, { kind: "component", type: i5.FsMessageWarningComponent, selector: "fs-message-warning" }, { kind: "ngmodule", type: FsSkeletonModule }, { kind: "directive", type: i6.FsSkeletonContentDirective, selector: "[fsSkeleton]", inputs: ["fsSkeleton", "fsSkeletonPattern"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
182
+ }
183
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SemanticDatabaseComponent, decorators: [{
184
+ type: Component,
185
+ args: [{ selector: 'fs-ai-report-global-settings-semantic-database', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
186
+ MatButton,
187
+ MatExpansionModule,
188
+ MatTooltipModule,
189
+ FsChipModule,
190
+ FsDateModule,
191
+ FsMessageModule,
192
+ FsSkeletonModule,
193
+ ], template: "<div class=\"fs-column tab-body\">\n <p class=\"hint\">\n The semantic database is what the report AI uses to understand your data \u2014\n tables, relationships and the calculations in code. Rebuild it after\n significant application updates. The build runs in the background; progress\n appears in the dock at the bottom right and this dialog can be closed while\n it runs.\n </p>\n\n <div *fsSkeleton=\"status()\">\n @if (status(); as buildStatus) {\n <div class=\"build-status\">\n @if (buildStatus.detail.generatedAt) {\n <div class=\"stat\">\n <span class=\"label\">Last built</span>\n <fs-date\n [date]=\"buildStatus.detail.generatedAt\"\n format=\"date-time\">\n </fs-date>\n </div>\n <div class=\"stat\">\n <span class=\"label\">Coverage</span>\n <span>\n {{ buildStatus.detail.entityCount }} entities \u00B7\n {{ buildStatus.detail.functionCount }} code functions\n </span>\n </div>\n } @else {\n <fs-message-warning>\n The semantic database has not been built yet \u2014 the report AI can't\n answer data questions until it is. Run the first build.\n </fs-message-warning>\n }\n @if (buildStatus.build) {\n <div class=\"stat\">\n <span class=\"label\">Last build run</span>\n <span>\n {{ buildStateLabel() }}\n @if (buildStatus.build.message) {\n \u2014 {{ buildStatus.build.message }}\n }\n </span>\n </div>\n }\n </div>\n\n <div class=\"build-actions\">\n @if (buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"stopBuild()\">\n Stop Build\n </button>\n } @else {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n (click)=\"build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n </div>\n\n <!-- The model scoreboard: every entity the AI knows about, its\n built-vs-needs-building stages, and a drill-down into its shape, enum\n values, computed fields and AI-classified code functions. -->\n @if (buildStatus.detail.entities?.length) {\n <mat-accordion class=\"entities\">\n @for (entity of buildStatus.detail.entities; track entity.key) {\n <mat-expansion-panel (opened)=\"entityOpened(entity)\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ entity.key }}</mat-panel-title>\n <mat-panel-description>\n <div class=\"stage-chips\">\n @for (stage of stageKeys; track stage) {\n <fs-chip\n size=\"tiny\"\n [backgroundColor]=\"stageColor(entity.stages[stage])\"\n color=\"#ffffff\"\n [matTooltip]=\"stageTooltip(entity.stages[stage])\">\n {{ stageLabels[stage] }}\n </fs-chip>\n }\n </div>\n </mat-panel-description>\n </mat-expansion-panel-header>\n\n <!-- Deferred: the body (and its functions request) only exists\n once expanded. -->\n <ng-template matExpansionPanelContent>\n <div class=\"entity-detail\">\n <div><strong>Table:</strong> {{ entity.table }}</div>\n <div><strong>Columns:</strong> {{ entity.columns }}</div>\n <div><strong>Relationships:</strong> {{ entity.relationships }}</div>\n @if (entity.enums.length) {\n <div class=\"enum-list\">\n <strong>Enum values:</strong>\n @for (enumColumn of entity.enums; track enumColumn.column) {\n <div class=\"enum-row\">\n <span class=\"enum-column\">{{ enumColumn.column }}</span>\n <span class=\"enum-values\">{{ enumColumn.values.join(', ') }}</span>\n </div>\n }\n </div>\n }\n @if (entity.computedFields.length) {\n <div><strong>Computed fields:</strong> {{ entity.computedFields.join(', ') }}</div>\n }\n\n <div class=\"functions-section\">\n <h3 class=\"functions-heading\">\n Code functions\n @if (entity.functionCount) {\n <span class=\"functions-count\">({{ entity.functionCount }})</span>\n }\n </h3>\n\n <div class=\"functions-body\" *fsSkeleton=\"entityFunctions(entity.key)\">\n @if (entityFunctions(entity.key); as fns) {\n @if (fns.functions.length) {\n @if (fns.generatedAt) {\n <div class=\"hint functions-generated\">\n Discovered from {{ fns.class }} &middot;\n <fs-date\n [date]=\"fns.generatedAt\"\n format=\"date-time\">\n </fs-date>\n </div>\n }\n <div class=\"functions-list\">\n @for (fn of fns.functions; track fn.method) {\n <div class=\"function-row\">\n <div class=\"function-head\">\n <fs-chip\n size=\"tiny\"\n [backgroundColor]=\"kindColor(fn.kind)\"\n color=\"#ffffff\">\n {{ fn.kind }}\n </fs-chip>\n <code class=\"function-method\">{{ fn.method }}</code>\n </div>\n <div class=\"function-summary\">{{ fn.summary }}</div>\n @if (fn.produces) {\n <div class=\"function-produces hint\">Produces: {{ fn.produces }}</div>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"hint\">\n No functions cached for this entity yet \u2014 run a build to discover its code.\n </div>\n }\n }\n </div>\n </div>\n </div>\n </ng-template>\n </mat-expansion-panel>\n }\n </mat-accordion>\n }\n }\n </div>\n</div>\n", styles: [".tab-body{padding-top:16px}.hint{margin:0 0 12px;color:#0009;font-size:13px;line-height:1.5}.build-status{display:flex;flex-direction:column;gap:8px}.build-status .stat{display:flex;gap:8px}.build-status .stat .label{min-width:110px;color:#0009}.build-actions{margin:12px 0}.entities{display:block}.entities .stage-chips{display:flex;flex-wrap:wrap;gap:4px}.entities .entity-detail{display:flex;flex-direction:column;gap:5px}.entities .entity-detail .enum-list{display:flex;flex-direction:column;gap:3px}.entities .entity-detail .enum-list .enum-row{display:flex;gap:10px;padding-left:10px}.entities .entity-detail .enum-list .enum-row .enum-column{min-width:140px;font-weight:600}.entities .entity-detail .enum-list .enum-row .enum-values{color:#0009}.entities .entity-detail .functions-section{margin-top:10px}.entities .entity-detail .functions-section .functions-heading{margin:0 0 5px;font-size:15px}.entities .entity-detail .functions-section .functions-heading .functions-count{font-weight:400;color:#0009}.entities .entity-detail .functions-section .functions-generated{margin-bottom:10px}.entities .entity-detail .functions-section .functions-list{display:flex;flex-direction:column}.entities .entity-detail .functions-section .functions-list .function-row{display:flex;flex-direction:column;gap:3px;padding:10px 0}.entities .entity-detail .functions-section .functions-list .function-row+.function-row{border-top:1px solid rgba(0,0,0,.08)}.entities .entity-detail .functions-section .functions-list .function-row .function-head{display:flex;align-items:center;gap:8px}.entities .entity-detail .functions-section .functions-list .function-row .function-method{font-family:monospace;font-size:13px;font-weight:600}.entities .entity-detail .functions-section .functions-list .function-row .function-summary{color:#000000bf}.entities .entity-detail .functions-section .functions-list .function-row .function-produces{padding-left:10px}\n"] }]
194
+ }], ctorParameters: () => [] });
195
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2RpYWxvZ3MvZ2xvYmFsLXNldHRpbmdzL2NvbXBvbmVudHMvc2VtYW50aWMtZGF0YWJhc2Uvc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2RpYWxvZ3MvZ2xvYmFsLXNldHRpbmdzL2NvbXBvbmVudHMvc2VtYW50aWMtZGF0YWJhc2Uvc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDekcsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFaEUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRTdELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNsRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDakUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5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LE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBRWpFLE9BQU8sUUFBUSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDdEUsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRU8sU0FBUyxDQUFDLElBQVk7UUFDNUIsTUFBTSxJQUFJLEdBQUcseUJBQXlCLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUVoRSxPQUFPLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMseUJBQXlCLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQzFFLENBQUM7d0dBbk1VLHlCQUF5Qjs0RkFBekIseUJBQXlCLDBHQ2pEdEMsa3BPQXdLQSxpOUREaElJLFNBQVMsZ0xBQ1Qsa0JBQWtCLHEwQkFDbEIsZ0JBQWdCLDRUQUNoQixZQUFZLHFYQUNaLFlBQVksNElBQ1osZUFBZSx5SEFDZixnQkFBZ0I7OzRGQUdQLHlCQUF5QjtrQkFoQnJDLFNBQVM7K0JBQ0UsZ0RBQWdELG1CQUd6Qyx1QkFBdUIsQ0FBQyxNQUFNLGNBQ25DLElBQUksV0FDUDt3QkFDUCxTQUFTO3dCQUNULGtCQUFrQjt3QkFDbEIsZ0JBQWdCO3dCQUNoQixZQUFZO3dCQUNaLFlBQVk7d0JBQ1osZUFBZTt3QkFDZixnQkFBZ0I7cUJBQ2pCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgRGVzdHJveVJlZiwgY29tcHV0ZWQsIGluamVjdCwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyB0YWtlVW50aWxEZXN0cm95ZWQgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XG5cbmltcG9ydCB7IE1hdEJ1dHRvbiB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBNYXRFeHBhbnNpb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9leHBhbnNpb24nO1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuXG5pbXBvcnQgeyBTdHJlYW1FdmVudFR5cGUgfSBmcm9tICdAZmlyZXN0aXRjaC9hcGknO1xuaW1wb3J0IHsgRnNDaGlwTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvY2hpcCc7XG5pbXBvcnQgeyBGc0RhdGVNb2R1bGUgfSBmcm9tICdAZmlyZXN0aXRjaC9kYXRlJztcbmltcG9ydCB7IEZzTWVzc2FnZSwgRnNNZXNzYWdlTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvbWVzc2FnZSc7XG5pbXBvcnQgeyBGc1Byb2Nlc3MgfSBmcm9tICdAZmlyZXN0aXRjaC9wcm9jZXNzJztcbmltcG9ydCB7IEZzUHJvbXB0IH0gZnJvbSAnQGZpcmVzdGl0Y2gvcHJvbXB0JztcbmltcG9ydCB7IEZzU2tlbGV0b25Nb2R1bGUgfSBmcm9tICdAZmlyZXN0aXRjaC9za2VsZXRvbic7XG5cbmltcG9ydCB7IGZpbHRlciwgbWFwLCBzd2l0Y2hNYXAsIHRha2UgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IFJlcG9ydERhdGEgfSBmcm9tICcuLi8uLi8uLi8uLi9kYXRhL3JlcG9ydC5kYXRhJztcbmltcG9ydCB7XG4gIFNlbWFudGljQnVpbGRFbnRpdHksXG4gIFNlbWFudGljQnVpbGRTdGFnZSxcbiAgU2VtYW50aWNCdWlsZFN0YXR1cyxcbiAgU2VtYW50aWNFbnRpdHlGdW5jdGlvbnMsXG59IGZyb20gJy4uLy4uLy4uLy4uL2ludGVyZmFjZXMvcmVwb3J0LmludGVyZmFjZSc7XG5cblxuLy8gVGhlIFNlbWFudGljIERhdGFiYXNlIHRhYjogdGhlIG1vZGVsIHNjb3JlYm9hcmQgdGhlIHJlcG9ydCBBSSBpcyBidWlsdCBmcm9tLlxuLy8gU2hvd3MgdGhlIGxhc3QgYnVpbGQncyBzdGF0dXMsIHJ1bnMgKG9yIHN0b3BzKSBhIGJ1aWxkIOKAlCBzdHJlYW1lZCBpbnRvIHRoZVxuLy8gRnNQcm9jZXNzIGRvY2sgc28gaXQgc3Vydml2ZXMgdGhlIGRpYWxvZyBjbG9zaW5nIOKAlCBhbmQgZHJpbGxzIGludG8gZXZlcnlcbi8vIGVudGl0eSB0byByZXZlYWwgaXRzIHNoYXBlLCBpbmxpbmVkIGVudW0gdmFsdWVzLCBjb21wdXRlZCBmaWVsZHMgYW5kIHRoZVxuLy8gQUktY2xhc3NpZmllZCBjb2RlIGZ1bmN0aW9ucyBjYWNoZWQgZnJvbSBpdHMgbW9kZWwgY2xhc3MuIEVhY2ggZW50aXR5J3Ncbi8vIGZ1bmN0aW9ucyBsb2FkIGxhemlseSBvbiBmaXJzdCBleHBhbmQsIGJlY2F1c2UgdGhlIGZ1bGwgc2V0IGFjcm9zcyBhbGxcbi8vIGVudGl0aWVzIGlzIGZhciB0b28gYmlnIHRvIHNoaXAgd2l0aCB0aGUgc2NvcmVib2FyZC5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2ZzLWFpLXJlcG9ydC1nbG9iYWwtc2V0dGluZ3Mtc2VtYW50aWMtZGF0YWJhc2UnLFxuICB0ZW1wbGF0ZVVybDogJy4vc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9zZW1hbnRpYy1kYXRhYmFzZS5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIE1hdEJ1dHRvbixcbiAgICBNYXRFeHBhbnNpb25Nb2R1bGUsXG4gICAgTWF0VG9vbHRpcE1vZHVsZSxcbiAgICBGc0NoaXBNb2R1bGUsXG4gICAgRnNEYXRlTW9kdWxlLFxuICAgIEZzTWVzc2FnZU1vZHVsZSxcbiAgICBGc1NrZWxldG9uTW9kdWxlLFxuICBdLFxufSlcbmV4cG9ydCBjbGFzcyBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50IHtcblxuICAvLyBUaGUgYnVpbGQtaW4tZmxpZ2h0IHN0YXRlcyBvZiB0aGUgZmZfcHJvY2Vzc2VzIHJvdyAocXVldWVkL3J1bm5pbmcvc3RvcHBpbmcpLlxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBBQ1RJVkVfU1RBVEVTID0gWydRJywgJ1InLCAnWCddO1xuXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFNUQVRFX0xBQkVMUzogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICBROiAnUXVldWVkJyxcbiAgICBSOiAnUnVubmluZycsXG4gICAgWDogJ1N0b3BwaW5nJyxcbiAgICBDOiAnQ29tcGxldGVkJyxcbiAgICBLOiAnU3RvcHBlZCcsXG4gICAgRjogJ0ZhaWxlZCcsXG4gIH07XG5cbiAgLy8gU2NvcmVib2FyZCBjb21wbGV0aW9uIHBhbGV0dGU6IGJ1aWx0ID0gZ3JlZW4sIGV2ZXJ5dGhpbmcgZWxzZSAobmVlZHNcbiAgLy8gYnVpbGRpbmcpID0gZ3JleS4gTm8gZXJyb3IgcmVkIOKAlCBhIG1pc3Npbmcgc3RhZ2UgaXMgdW5idWlsdCBjb250ZW50LCBub3QgYVxuICAvLyBmYWlsdXJlLlxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBET05FX0NPTE9SID0gJyMyZTdkMzInO1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBQRU5ESU5HX0NPTE9SID0gJyM5ZTllOWUnO1xuXG4gIC8vIE9uZSBjb2xvciBwZXIgQUkgY2xhc3NpZmljYXRpb24ga2luZCwgc28gYSBmdW5jdGlvbidzIG5hdHVyZSByZWFkcyBhdCBhIGdsYW5jZS5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgS0lORF9DT0xPUlM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgJ2NhbGN1bGF0aW9uJzogJyMxNTY1YzAnLFxuICAgICdidXNpbmVzcy1ydWxlJzogJyM2YTFiOWEnLFxuICAgICdxdWVyeSc6ICcjMDA2OTVjJyxcbiAgICAnbXV0YXRpb24nOiAnI2U2NTEwMCcsXG4gICAgJ2FjY2Vzc29yJzogJyM1NDZlN2EnLFxuICAgICdvdGhlcic6ICcjNzU3NTc1JyxcbiAgfTtcblxuICAvLyBJbnRlcmVzdGluZyBsb2dpYyBmaXJzdDogZGVyaXZlZCB2YWx1ZXMgYW5kIHJ1bGVzLCB0aGVuIGRhdGEgYWNjZXNzLCB0aGVuIHRoZSByZXN0LlxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBLSU5EX09SREVSID0gWydjYWxjdWxhdGlvbicsICdidXNpbmVzcy1ydWxlJywgJ3F1ZXJ5JywgJ211dGF0aW9uJywgJ2FjY2Vzc29yJywgJ290aGVyJ107XG5cbiAgLy8gVGhlIHNjb3JlYm9hcmQncyBwZXItZW50aXR5IHN0YWdlIGNoaXBzLCBsZWZ0IHRvIHJpZ2h0LlxuICBwdWJsaWMgcmVhZG9ubHkgc3RhZ2VLZXlzID0gWydpbnRyb3NwZWN0ZWQnLCAnZW51bXNJbmxpbmVkJywgJ2NvbXB1dGVkJywgJ2Z1bmN0aW9ucyddO1xuXG4gIHB1YmxpYyByZWFkb25seSBzdGFnZUxhYmVsczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICBpbnRyb3NwZWN0ZWQ6ICdJbnRyb3NwZWN0ZWQnLFxuICAgIGVudW1zSW5saW5lZDogJ0VudW0gY2FzZXMnLFxuICAgIGNvbXB1dGVkOiAnQ29tcHV0ZWQnLFxuICAgIGZ1bmN0aW9uczogJ0Z1bmN0aW9ucycsXG4gIH07XG5cbiAgLy8gbnVsbCB1bnRpbCBsb2FkZWQg4oCUIGdhdGVzIHRoZSB0YWIgYmVoaW5kIHRoZSBza2VsZXRvbi5cbiAgcHVibGljIHJlYWRvbmx5IHN0YXR1cyA9IHNpZ25hbDxTZW1hbnRpY0J1aWxkU3RhdHVzIHwgbnVsbD4obnVsbCk7XG5cbiAgLy8gVHJ1ZSB3aGlsZSBhIGJ1aWxkIHN0YXJ0ZWQgZnJvbSB0aGlzIHRhYiBpcyBzdHJlYW1pbmcgaW50byB0aGUgZG9jay5cbiAgcHVibGljIHJlYWRvbmx5IGJ1aWxkaW5nID0gc2lnbmFsKGZhbHNlKTtcblxuICBwdWJsaWMgcmVhZG9ubHkgYnVpbGRBY3RpdmUgPSBjb21wdXRlZDxib29sZWFuPigoKSA9PlxuICAgIHRoaXMuYnVpbGRpbmcoKVxuICAgICAgfHwgU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5BQ1RJVkVfU1RBVEVTLmluY2x1ZGVzKHRoaXMuc3RhdHVzKCk/LmJ1aWxkPy5zdGF0ZSksXG4gICk7XG5cbiAgcHVibGljIHJlYWRvbmx5IGJ1aWxkU3RhdGVMYWJlbCA9IGNvbXB1dGVkPHN0cmluZz4oKCkgPT5cbiAgICBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LlNUQVRFX0xBQkVMU1t0aGlzLnN0YXR1cygpPy5idWlsZD8uc3RhdGVdID8/ICcnLFxuICApO1xuXG4gIC8vIFBlci1lbnRpdHkgZnVuY3Rpb24gY2FjaGVzLCBsb2FkZWQgb24gZmlyc3QgZXhwYW5kLiB1bmRlZmluZWQgPSBub3RcbiAgLy8gcmVxdWVzdGVkIHlldCwgbnVsbCA9IHJlcXVlc3QgaW4gZmxpZ2h0ICh0aGUgc2tlbGV0b24gd2luZG93KSwgb2JqZWN0ID0gbG9hZGVkLlxuICBwcml2YXRlIHJlYWRvbmx5IF9lbnRpdHlGdW5jdGlvbnMgPSBzaWduYWw8UmVjb3JkPHN0cmluZywgU2VtYW50aWNFbnRpdHlGdW5jdGlvbnMgfCBudWxsPj4oe30pO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX3JlcG9ydERhdGEgPSBpbmplY3QoUmVwb3J0RGF0YSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX3Byb2Nlc3MgPSBpbmplY3QoRnNQcm9jZXNzKTtcbiAgcHJpdmF0ZSByZWFkb25seSBfcHJvbXB0ID0gaW5qZWN0KEZzUHJvbXB0KTtcbiAgcHJpdmF0ZSByZWFkb25seSBfbWVzc2FnZSA9IGluamVjdChGc01lc3NhZ2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IF9kZXN0cm95UmVmID0gaW5qZWN0KERlc3Ryb3lSZWYpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuX2luaXQoKTtcbiAgfVxuXG4gIC8vIFJ1biB0aGUgc2VtYW50aWMtZGF0YWJhc2UgYnVpbGQuIFRoZSBsaXZlIGZlZWQgc3RyZWFtcyBpbnRvIHRoZSBGc1Byb2Nlc3NcbiAgLy8gZG9jayAoYm90dG9tLXJpZ2h0KSwgd2hvc2Ugc3Vic2NyaXB0aW9uIHN1cnZpdmVzIHRoaXMgZGlhbG9nIGNsb3Npbmcg4oCUIHNvXG4gIC8vIGRpc21pc3NpbmcgdGhlIGRpYWxvZyBuZXZlciBpbnRlcnJ1cHRzIGEgcnVubmluZyBidWlsZC5cbiAgcHVibGljIGJ1aWxkKCk6IHZvaWQge1xuICAgIHRoaXMuYnVpbGRpbmcuc2V0KHRydWUpO1xuXG4gICAgY29uc3QgdGFyZ2V0JCA9IHRoaXMuX3JlcG9ydERhdGEuc2VtYW50aWNCdWlsZCgpXG4gICAgICAucGlwZShcbiAgICAgICAgZmlsdGVyKChldmVudCkgPT5cbiAgICAgICAgICBldmVudD8udHlwZSA9PT0gU3RyZWFtRXZlbnRUeXBlLkRhdGFcbiAgICAgICAgICAmJiBldmVudC5kYXRhPy5raW5kID09PSAnbG9nJ1xuICAgICAgICAgICYmIHR5cGVvZiBldmVudC5kYXRhLmxpbmUgPT09ICdzdHJpbmcnKSxcbiAgICAgICAgbWFwKChldmVudCkgPT4gZXZlbnQuZGF0YS5saW5lIGFzIHN0cmluZyksXG4gICAgICApO1xuXG4gICAgdGhpcy5fcHJvY2Vzc1xuICAgICAgLnJ1bignU2VtYW50aWMgZGF0YWJhc2UgYnVpbGQnLCB0YXJnZXQkKVxuICAgICAgLmNvbXBsZXRlZCRcbiAgICAgIC5waXBlKFxuICAgICAgICB0YWtlKDEpLFxuICAgICAgICB0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZiksXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgdGhpcy5idWlsZGluZy5zZXQoZmFsc2UpO1xuICAgICAgICAvLyBUaGUgYnVpbGQgcmV3cm90ZSBldmVyeSBlbnRpdHkncyBmdW5jdGlvbiBjYWNoZSDigJQgZHJvcCB0aGUgbG9hZGVkXG4gICAgICAgIC8vIGNvcGllcyBzbyB0aGUgbmV4dCBleHBhbmQgcmVmZXRjaGVzIGZyZXNoIGRhdGEuXG4gICAgICAgIHRoaXMuX2VudGl0eUZ1bmN0aW9ucy5zZXQoe30pO1xuICAgICAgICB0aGlzLl9yZWZyZXNoU3RhdHVzKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBzdG9wQnVpbGQoKTogdm9pZCB7XG4gICAgdGhpcy5fcHJvbXB0LmNvbmZpcm0oe1xuICAgICAgdGl0bGU6ICdTdG9wIGJ1aWxkJyxcbiAgICAgIHRlbXBsYXRlOiAnU3RvcCB0aGUgcnVubmluZyBidWlsZD8gVGhlIGN1cnJlbnQgc2VtYW50aWMgZGF0YWJhc2Ugc3RheXMgaW4gcGxhY2UgdW50aWwgYSBidWlsZCBmaW5pc2hlcy4nLFxuICAgICAgY29tbWl0TGFiZWw6ICdTdG9wJyxcbiAgICB9KVxuICAgICAgLnBpcGUoXG4gICAgICAgIHN3aXRjaE1hcCgoKSA9PiB0aGlzLl9yZXBvcnREYXRhLnNlbWFudGljQnVpbGRTdG9wKCkpLFxuICAgICAgICB0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZiksXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgdGhpcy5idWlsZGluZy5zZXQoZmFsc2UpO1xuICAgICAgICB0aGlzLl9tZXNzYWdlLnN1Y2Nlc3MoJ1N0b3BwaW5nIHRoZSBidWlsZOKApicpO1xuICAgICAgICB0aGlzLl9yZWZyZXNoU3RhdHVzKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBzdGFnZUNvbG9yKHN0YWdlOiBTZW1hbnRpY0J1aWxkU3RhZ2UpOiBzdHJpbmcge1xuICAgIHJldHVybiBzdGFnZSA9PT0gJ2RvbmUnXG4gICAgICA/IFNlbWFudGljRGF0YWJhc2VDb21wb25lbnQuRE9ORV9DT0xPUlxuICAgICAgOiBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LlBFTkRJTkdfQ09MT1I7XG4gIH1cblxuICBwdWJsaWMgc3RhZ2VUb29sdGlwKHN0YWdlOiBTZW1hbnRpY0J1aWxkU3RhZ2UpOiBzdHJpbmcge1xuICAgIHJldHVybiBzdGFnZSA9PT0gJ2RvbmUnID8gJ0J1aWx0JyA6ICdOZWVkcyBidWlsZGluZyc7XG4gIH1cblxuICBwdWJsaWMga2luZENvbG9yKGtpbmQ6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIFNlbWFudGljRGF0YWJhc2VDb21wb25lbnQuS0lORF9DT0xPUlNba2luZF0gPz8gU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5LSU5EX0NPTE9SUy5vdGhlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgbG9hZGVkIGZ1bmN0aW9uIGNhY2hlIGZvciBhbiBlbnRpdHk6IHVuZGVmaW5lZCB1bnRpbCB0aGUgcGFuZWwgaXMgZmlyc3RcbiAgICogZXhwYW5kZWQsIG51bGwgd2hpbGUgdGhlIHJlcXVlc3QgaXMgaW4gZmxpZ2h0ICh0aGUgc2tlbGV0b24gd2luZG93KSwgdGhlblxuICAgKiB0aGUgbG9hZGVkIGNhY2hlLlxuICAgKi9cbiAgcHVibGljIGVudGl0eUZ1bmN0aW9ucyhrZXk6IHN0cmluZyk6IFNlbWFudGljRW50aXR5RnVuY3Rpb25zIHwgbnVsbCB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuX2VudGl0eUZ1bmN0aW9ucygpW2tleV07XG4gIH1cblxuICAvKipcbiAgICogRmlyc3QgZXhwYW5kIG9mIGFuIGVudGl0eTogZmV0Y2ggaXRzIGNhY2hlZCBmdW5jdGlvbnMgdGhyb3VnaCB0aGUgcGVyLWVudGl0eVxuICAgKiBlbmRwb2ludC4gU3Vic2VxdWVudCBleHBhbmRzIHJldXNlIHRoZSBsb2FkZWQgY29weSAoYSBmaW5pc2hlZCBidWlsZCBjbGVhcnNcbiAgICogdGhlIGNvcGllcyBzbyBhIHJlLWV4cGFuZCByZWZldGNoZXMgZnJlc2ggZGF0YSkuXG4gICAqL1xuICBwdWJsaWMgZW50aXR5T3BlbmVkKGVudGl0eTogU2VtYW50aWNCdWlsZEVudGl0eSk6IHZvaWQge1xuICAgIGlmICh0aGlzLl9lbnRpdHlGdW5jdGlvbnMoKVtlbnRpdHkua2V5XSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5fZW50aXR5RnVuY3Rpb25zLnVwZGF0ZSgobG9hZGVkKSA9PiAoeyAuLi5sb2FkZWQsIFtlbnRpdHkua2V5XTogbnVsbCB9KSk7XG5cbiAgICB0aGlzLl9yZXBvcnREYXRhLnNlbWFudGljRW50aXR5RnVuY3Rpb25zKGVudGl0eS5rZXkpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChlbnRpdHlGdW5jdGlvbnM6IFNlbWFudGljRW50aXR5RnVuY3Rpb25zKSA9PiB7XG4gICAgICAgIHRoaXMuX2VudGl0eUZ1bmN0aW9ucy51cGRhdGUoKGxvYWRlZCkgPT4gKHtcbiAgICAgICAgICAuLi5sb2FkZWQsXG4gICAgICAgICAgW2VudGl0eS5rZXldOiB7XG4gICAgICAgICAgICAuLi5lbnRpdHlGdW5jdGlvbnMsXG4gICAgICAgICAgICBmdW5jdGlvbnM6IHRoaXMuX3NvcnRGdW5jdGlvbnMoZW50aXR5RnVuY3Rpb25zKSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KSk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgX2luaXQoKTogdm9pZCB7XG4gICAgdGhpcy5fcmVmcmVzaFN0YXR1cygpO1xuICB9XG5cbiAgcHJpdmF0ZSBfcmVmcmVzaFN0YXR1cygpOiB2b2lkIHtcbiAgICB0aGlzLl9yZXBvcnREYXRhLnNlbWFudGljQnVpbGRTdGF0dXMoKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgoc3RhdHVzOiBTZW1hbnRpY0J1aWxkU3RhdHVzKSA9PiB0aGlzLnN0YXR1cy5zZXQoc3RhdHVzKSk7XG4gIH1cblxuICAvKipcbiAgICogT3JkZXIgb25jZSBhdCBsb2FkIHRpbWUgKGtpbmQgcHJpb3JpdHksIHRoZW4gbWV0aG9kIG5hbWUpIHNvIHRoZSB0ZW1wbGF0ZVxuICAgKiByZW5kZXJzIHRoZSBsaXN0IGFzLWlzLlxuICAgKi9cbiAgcHJpdmF0ZSBfc29ydEZ1bmN0aW9ucyhlbnRpdHlGdW5jdGlvbnM6IFNlbWFudGljRW50aXR5RnVuY3Rpb25zKTogU2VtYW50aWNFbnRpdHlGdW5jdGlvbnNbJ2Z1bmN0aW9ucyddIHtcbiAgICByZXR1cm4gWy4uLmVudGl0eUZ1bmN0aW9ucy5mdW5jdGlvbnNdXG4gICAgICAuc29ydCgoYSwgYikgPT4ge1xuICAgICAgICBjb25zdCBraW5kRGlmZiA9IHRoaXMuX2tpbmRSYW5rKGEua2luZCkgLSB0aGlzLl9raW5kUmFuayhiLmtpbmQpO1xuXG4gICAgICAgIHJldHVybiBraW5kRGlmZiAhPT0gMCA/IGtpbmREaWZmIDogYS5tZXRob2QubG9jYWxlQ29tcGFyZShiLm1ldGhvZCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgX2tpbmRSYW5rKGtpbmQ6IHN0cmluZyk6IG51bWJlciB7XG4gICAgY29uc3QgcmFuayA9IFNlbWFudGljRGF0YWJhc2VDb21wb25lbnQuS0lORF9PUkRFUi5pbmRleE9mKGtpbmQpO1xuXG4gICAgcmV0dXJuIHJhbmsgPT09IC0xID8gU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5LSU5EX09SREVSLmxlbmd0aCA6IHJhbms7XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJmcy1jb2x1bW4gdGFiLWJvZHlcIj5cbiAgPHAgY2xhc3M9XCJoaW50XCI+XG4gICAgVGhlIHNlbWFudGljIGRhdGFiYXNlIGlzIHdoYXQgdGhlIHJlcG9ydCBBSSB1c2VzIHRvIHVuZGVyc3RhbmQgeW91ciBkYXRhIOKAlFxuICAgIHRhYmxlcywgcmVsYXRpb25zaGlwcyBhbmQgdGhlIGNhbGN1bGF0aW9ucyBpbiBjb2RlLiBSZWJ1aWxkIGl0IGFmdGVyXG4gICAgc2lnbmlmaWNhbnQgYXBwbGljYXRpb24gdXBkYXRlcy4gVGhlIGJ1aWxkIHJ1bnMgaW4gdGhlIGJhY2tncm91bmQ7IHByb2dyZXNzXG4gICAgYXBwZWFycyBpbiB0aGUgZG9jayBhdCB0aGUgYm90dG9tIHJpZ2h0IGFuZCB0aGlzIGRpYWxvZyBjYW4gYmUgY2xvc2VkIHdoaWxlXG4gICAgaXQgcnVucy5cbiAgPC9wPlxuXG4gIDxkaXYgKmZzU2tlbGV0b249XCJzdGF0dXMoKVwiPlxuICAgIEBpZiAoc3RhdHVzKCk7IGFzIGJ1aWxkU3RhdHVzKSB7XG4gICAgICA8ZGl2IGNsYXNzPVwiYnVpbGQtc3RhdHVzXCI+XG4gICAgICAgIEBpZiAoYnVpbGRTdGF0dXMuZGV0YWlsLmdlbmVyYXRlZEF0KSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cInN0YXRcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5MYXN0IGJ1aWx0PC9zcGFuPlxuICAgICAgICAgICAgPGZzLWRhdGVcbiAgICAgICAgICAgICAgICBbZGF0ZV09XCJidWlsZFN0YXR1cy5kZXRhaWwuZ2VuZXJhdGVkQXRcIlxuICAgICAgICAgICAgICAgIGZvcm1hdD1cImRhdGUtdGltZVwiPlxuICAgICAgICAgICAgPC9mcy1kYXRlPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGF0XCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxhYmVsXCI+Q292ZXJhZ2U8L3NwYW4+XG4gICAgICAgICAgICA8c3Bhbj5cbiAgICAgICAgICAgICAge3sgYnVpbGRTdGF0dXMuZGV0YWlsLmVudGl0eUNvdW50IH19IGVudGl0aWVzIMK3XG4gICAgICAgICAgICAgIHt7IGJ1aWxkU3RhdHVzLmRldGFpbC5mdW5jdGlvbkNvdW50IH19IGNvZGUgZnVuY3Rpb25zXG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgIDxmcy1tZXNzYWdlLXdhcm5pbmc+XG4gICAgICAgICAgICBUaGUgc2VtYW50aWMgZGF0YWJhc2UgaGFzIG5vdCBiZWVuIGJ1aWx0IHlldCDigJQgdGhlIHJlcG9ydCBBSSBjYW4ndFxuICAgICAgICAgICAgYW5zd2VyIGRhdGEgcXVlc3Rpb25zIHVudGlsIGl0IGlzLiBSdW4gdGhlIGZpcnN0IGJ1aWxkLlxuICAgICAgICAgIDwvZnMtbWVzc2FnZS13YXJuaW5nPlxuICAgICAgICB9XG4gICAgICAgIEBpZiAoYnVpbGRTdGF0dXMuYnVpbGQpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhdFwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsYWJlbFwiPkxhc3QgYnVpbGQgcnVuPC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+XG4gICAgICAgICAgICAgIHt7IGJ1aWxkU3RhdGVMYWJlbCgpIH19XG4gICAgICAgICAgICAgIEBpZiAoYnVpbGRTdGF0dXMuYnVpbGQubWVzc2FnZSkge1xuICAgICAgICAgICAgICAgIOKAlCB7eyBidWlsZFN0YXR1cy5idWlsZC5tZXNzYWdlIH19XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJidWlsZC1hY3Rpb25zXCI+XG4gICAgICAgIEBpZiAoYnVpbGRBY3RpdmUoKSkge1xuICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgIG1hdC1idXR0b25cbiAgICAgICAgICAgICAgY29sb3I9XCJ3YXJuXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInN0b3BCdWlsZCgpXCI+XG4gICAgICAgICAgICBTdG9wIEJ1aWxkXG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgIG1hdC1idXR0b25cbiAgICAgICAgICAgICAgY29sb3I9XCJwcmltYXJ5XCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImJ1aWxkKClcIj5cbiAgICAgICAgICAgIHt7IGJ1aWxkU3RhdHVzLmRldGFpbC5nZW5lcmF0ZWRBdCA/ICdSZWJ1aWxkJyA6ICdCdWlsZCcgfX1cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG5cbiAgICAgIDwhLS0gVGhlIG1vZGVsIHNjb3JlYm9hcmQ6IGV2ZXJ5IGVudGl0eSB0aGUgQUkga25vd3MgYWJvdXQsIGl0c1xuICAgICAgICAgICBidWlsdC12cy1uZWVkcy1idWlsZGluZyBzdGFnZXMsIGFuZCBhIGRyaWxsLWRvd24gaW50byBpdHMgc2hhcGUsIGVudW1cbiAgICAgICAgICAgdmFsdWVzLCBjb21wdXRlZCBmaWVsZHMgYW5kIEFJLWNsYXNzaWZpZWQgY29kZSBmdW5jdGlvbnMuIC0tPlxuICAgICAgQGlmIChidWlsZFN0YXR1cy5kZXRhaWwuZW50aXRpZXM/Lmxlbmd0aCkge1xuICAgICAgICA8bWF0LWFjY29yZGlvbiBjbGFzcz1cImVudGl0aWVzXCI+XG4gICAgICAgICAgQGZvciAoZW50aXR5IG9mIGJ1aWxkU3RhdHVzLmRldGFpbC5lbnRpdGllczsgdHJhY2sgZW50aXR5LmtleSkge1xuICAgICAgICAgICAgPG1hdC1leHBhbnNpb24tcGFuZWwgKG9wZW5lZCk9XCJlbnRpdHlPcGVuZWQoZW50aXR5KVwiPlxuICAgICAgICAgICAgICA8bWF0LWV4cGFuc2lvbi1wYW5lbC1oZWFkZXI+XG4gICAgICAgICAgICAgICAgPG1hdC1wYW5lbC10aXRsZT57eyBlbnRpdHkua2V5IH19PC9tYXQtcGFuZWwtdGl0bGU+XG4gICAgICAgICAgICAgICAgPG1hdC1wYW5lbC1kZXNjcmlwdGlvbj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGFnZS1jaGlwc1wiPlxuICAgICAgICAgICAgICAgICAgICBAZm9yIChzdGFnZSBvZiBzdGFnZUtleXM7IHRyYWNrIHN0YWdlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgPGZzLWNoaXBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT1cInRpbnlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBbYmFja2dyb3VuZENvbG9yXT1cInN0YWdlQ29sb3IoZW50aXR5LnN0YWdlc1tzdGFnZV0pXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9XCIjZmZmZmZmXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW21hdFRvb2x0aXBdPVwic3RhZ2VUb29sdGlwKGVudGl0eS5zdGFnZXNbc3RhZ2VdKVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgc3RhZ2VMYWJlbHNbc3RhZ2VdIH19XG4gICAgICAgICAgICAgICAgICAgICAgPC9mcy1jaGlwPlxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8L21hdC1wYW5lbC1kZXNjcmlwdGlvbj5cbiAgICAgICAgICAgICAgPC9tYXQtZXhwYW5zaW9uLXBhbmVsLWhlYWRlcj5cblxuICAgICAgICAgICAgICA8IS0tIERlZmVycmVkOiB0aGUgYm9keSAoYW5kIGl0cyBmdW5jdGlvbnMgcmVxdWVzdCkgb25seSBleGlzdHNcbiAgICAgICAgICAgICAgICAgICBvbmNlIGV4cGFuZGVkLiAtLT5cbiAgICAgICAgICAgICAgPG5nLXRlbXBsYXRlIG1hdEV4cGFuc2lvblBhbmVsQ29udGVudD5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW50aXR5LWRldGFpbFwiPlxuICAgICAgICAgICAgICAgICAgPGRpdj48c3Ryb25nPlRhYmxlOjwvc3Ryb25nPiB7eyBlbnRpdHkudGFibGUgfX08L2Rpdj5cbiAgICAgICAgICAgICAgICAgIDxkaXY+PHN0cm9uZz5Db2x1bW5zOjwvc3Ryb25nPiB7eyBlbnRpdHkuY29sdW1ucyB9fTwvZGl2PlxuICAgICAgICAgICAgICAgICAgPGRpdj48c3Ryb25nPlJlbGF0aW9uc2hpcHM6PC9zdHJvbmc+IHt7IGVudGl0eS5yZWxhdGlvbnNoaXBzIH19PC9kaXY+XG4gICAgICAgICAgICAgICAgICBAaWYgKGVudGl0eS5lbnVtcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImVudW0tbGlzdFwiPlxuICAgICAgICAgICAgICAgICAgICAgIDxzdHJvbmc+RW51bSB2YWx1ZXM6PC9zdHJvbmc+XG4gICAgICAgICAgICAgICAgICAgICAgQGZvciAoZW51bUNvbHVtbiBvZiBlbnRpdHkuZW51bXM7IHRyYWNrIGVudW1Db2x1bW4uY29sdW1uKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW51bS1yb3dcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJlbnVtLWNvbHVtblwiPnt7IGVudW1Db2x1bW4uY29sdW1uIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImVudW0tdmFsdWVzXCI+e3sgZW51bUNvbHVtbi52YWx1ZXMuam9pbignLCAnKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBAaWYgKGVudGl0eS5jb21wdXRlZEZpZWxkcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgPGRpdj48c3Ryb25nPkNvbXB1dGVkIGZpZWxkczo8L3N0cm9uZz4ge3sgZW50aXR5LmNvbXB1dGVkRmllbGRzLmpvaW4oJywgJykgfX08L2Rpdj5cbiAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImZ1bmN0aW9ucy1zZWN0aW9uXCI+XG4gICAgICAgICAgICAgICAgICAgIDxoMyBjbGFzcz1cImZ1bmN0aW9ucy1oZWFkaW5nXCI+XG4gICAgICAgICAgICAgICAgICAgICAgQ29kZSBmdW5jdGlvbnNcbiAgICAgICAgICAgICAgICAgICAgICBAaWYgKGVudGl0eS5mdW5jdGlvbkNvdW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImZ1bmN0aW9ucy1jb3VudFwiPih7eyBlbnRpdHkuZnVuY3Rpb25Db3VudCB9fSk8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA8L2gzPlxuXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmdW5jdGlvbnMtYm9keVwiICpmc1NrZWxldG9uPVwiZW50aXR5RnVuY3Rpb25zKGVudGl0eS5rZXkpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgQGlmIChlbnRpdHlGdW5jdGlvbnMoZW50aXR5LmtleSk7IGFzIGZucykge1xuICAgICAgICAgICAgICAgICAgICAgICAgQGlmIChmbnMuZnVuY3Rpb25zLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICBAaWYgKGZucy5nZW5lcmF0ZWRBdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJoaW50IGZ1bmN0aW9ucy1nZW5lcmF0ZWRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpc2NvdmVyZWQgZnJvbSB7eyBmbnMuY2xhc3MgfX0gJm1pZGRvdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxmcy1kYXRlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2RhdGVdPVwiZm5zLmdlbmVyYXRlZEF0XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQ9XCJkYXRlLXRpbWVcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZnMtZGF0ZT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZnVuY3Rpb25zLWxpc3RcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBAZm9yIChmbiBvZiBmbnMuZnVuY3Rpb25zOyB0cmFjayBmbi5tZXRob2QpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmdW5jdGlvbi1yb3dcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImZ1bmN0aW9uLWhlYWRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZnMtY2hpcFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPVwidGlueVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtiYWNrZ3JvdW5kQ29sb3JdPVwia2luZENvbG9yKGZuLmtpbmQpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9XCIjZmZmZmZmXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7eyBmbi5raW5kIH19XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9mcy1jaGlwPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjb2RlIGNsYXNzPVwiZnVuY3Rpb24tbWV0aG9kXCI+e3sgZm4ubWV0aG9kIH19PC9jb2RlPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImZ1bmN0aW9uLXN1bW1hcnlcIj57eyBmbi5zdW1tYXJ5IH19PC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEBpZiAoZm4ucHJvZHVjZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZnVuY3Rpb24tcHJvZHVjZXMgaGludFwiPlByb2R1Y2VzOiB7eyBmbi5wcm9kdWNlcyB9fTwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJoaW50XCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgTm8gZnVuY3Rpb25zIGNhY2hlZCBmb3IgdGhpcyBlbnRpdHkgeWV0IOKAlCBydW4gYSBidWlsZCB0byBkaXNjb3ZlciBpdHMgY29kZS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgICAgPC9tYXQtZXhwYW5zaW9uLXBhbmVsPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9tYXQtYWNjb3JkaW9uPlxuICAgICAgfVxuICAgIH1cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==