@firestitch/report 18.0.10 → 18.0.12

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 (23) hide show
  1. package/app/reports/data/report.data.d.ts +4 -0
  2. package/app/reports/dialogs/entity-functions/entity-functions.component.d.ts +22 -0
  3. package/app/reports/dialogs/global-settings/components/general/general.component.d.ts +8 -0
  4. package/app/reports/dialogs/global-settings/components/semantic-database/semantic-database.component.d.ts +21 -21
  5. package/app/reports/dialogs/global-settings/global-settings.component.d.ts +8 -4
  6. package/app/reports/dialogs/instruction-history/instruction-history.component.d.ts +16 -0
  7. package/app/reports/interfaces/report.interface.d.ts +11 -1
  8. package/esm2022/app/reports/data/report.data.mjs +15 -1
  9. package/esm2022/app/reports/dialogs/entity-functions/entity-functions.component.mjs +81 -0
  10. package/esm2022/app/reports/dialogs/global-settings/components/general/general.component.mjs +34 -0
  11. package/esm2022/app/reports/dialogs/global-settings/components/semantic-database/semantic-database.component.mjs +70 -75
  12. package/esm2022/app/reports/dialogs/global-settings/global-settings.component.mjs +58 -24
  13. package/esm2022/app/reports/dialogs/instruction-history/instruction-history.component.mjs +60 -0
  14. package/esm2022/app/reports/interfaces/report.interface.mjs +1 -1
  15. package/fesm2022/{firestitch-report-echarts-Cel1iHUg.mjs → firestitch-report-echarts-1VNsyco5.mjs} +2 -2
  16. package/fesm2022/{firestitch-report-echarts-Cel1iHUg.mjs.map → firestitch-report-echarts-1VNsyco5.mjs.map} +1 -1
  17. package/fesm2022/{firestitch-report-firestitch-report-Y1XG_Gms.mjs → firestitch-report-firestitch-report-BNHGwPj2.mjs} +274 -119
  18. package/fesm2022/firestitch-report-firestitch-report-BNHGwPj2.mjs.map +1 -0
  19. package/fesm2022/firestitch-report.mjs +1 -1
  20. package/package.json +1 -1
  21. package/app/reports/dialogs/global-settings/components/history/history.component.d.ts +0 -8
  22. package/esm2022/app/reports/dialogs/global-settings/components/history/history.component.mjs +0 -32
  23. package/fesm2022/firestitch-report-firestitch-report-Y1XG_Gms.mjs.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { ChangeDetectionStrategy, Component, DestroyRef, computed, inject, signal } from '@angular/core';
2
2
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { MatDialog } from '@angular/material/dialog';
3
4
  import { MatExpansionModule } from '@angular/material/expansion';
4
5
  import { MatTooltipModule } from '@angular/material/tooltip';
5
6
  import { StreamEventType } from '@firestitch/api';
@@ -9,8 +10,9 @@ import { FsMessage, FsMessageModule } from '@firestitch/message';
9
10
  import { FsProcess } from '@firestitch/process';
10
11
  import { FsPrompt } from '@firestitch/prompt';
11
12
  import { FsSkeletonModule } from '@firestitch/skeleton';
12
- import { filter, map, switchMap, take } from 'rxjs/operators';
13
+ import { filter, map, switchMap, take, tap } from 'rxjs/operators';
13
14
  import { ReportData } from '../../../../data/report.data';
15
+ import { EntityFunctionsComponent } from '../../../entity-functions/entity-functions.component';
14
16
  import * as i0 from "@angular/core";
15
17
  import * as i1 from "@angular/material/expansion";
16
18
  import * as i2 from "@angular/material/tooltip";
@@ -18,13 +20,13 @@ import * as i3 from "@firestitch/chip";
18
20
  import * as i4 from "@firestitch/date";
19
21
  import * as i5 from "@firestitch/message";
20
22
  import * as i6 from "@firestitch/skeleton";
21
- // The Semantic Database tab: the model scoreboard the report AI is built from.
22
- // Shows the last build's status, runs (or stops) a build — streamed into the
23
- // FsProcess dock so it survives the dialog closing — and drills into every
24
- // entity to reveal its shape, inlined enum values, computed fields and the
25
- // AI-classified code functions cached from its model class. Each entity's
26
- // functions load lazily on first expand, because the full set across all
27
- // entities is far too big to ship with the scoreboard.
23
+ // The Semantic Database tab: the entity scoreboard the report AI is built from
24
+ // (an entity = a table + a model + a handler). Shows the last build's status,
25
+ // runs (or stops) a build — streamed into the FsProcess dock so it survives the
26
+ // dialog closing — and drills into every entity to reveal its shape, inlined
27
+ // enum values, computed fields and its cached function counts, whose links open
28
+ // the functions dialog (the full function set across all entities is far too
29
+ // big to ship with the scoreboard).
28
30
  export class SemanticDatabaseComponent {
29
31
  // The build-in-flight states of the ff_processes row (queued/running/stopping).
30
32
  static ACTIVE_STATES = ['Q', 'R', 'X'];
@@ -41,17 +43,6 @@ export class SemanticDatabaseComponent {
41
43
  // failure.
42
44
  static DONE_COLOR = '#2e7d32';
43
45
  static PENDING_COLOR = '#9e9e9e';
44
- // One color per AI classification kind, so a function's nature reads at a glance.
45
- static KIND_COLORS = {
46
- 'calculation': '#1565c0',
47
- 'business-rule': '#6a1b9a',
48
- 'query': '#00695c',
49
- 'mutation': '#e65100',
50
- 'accessor': '#546e7a',
51
- 'other': '#757575',
52
- };
53
- // Interesting logic first: derived values and rules, then data access, then the rest.
54
- static KIND_ORDER = ['calculation', 'business-rule', 'query', 'mutation', 'accessor', 'other'];
55
46
  // The scoreboard's per-entity stage chips, left to right.
56
47
  stageKeys = ['introspected', 'enumsInlined', 'computed', 'functions'];
57
48
  stageLabels = {
@@ -67,9 +58,10 @@ export class SemanticDatabaseComponent {
67
58
  buildActive = computed(() => this.building()
68
59
  || SemanticDatabaseComponent.ACTIVE_STATES.includes(this.status()?.build?.state));
69
60
  buildStateLabel = computed(() => SemanticDatabaseComponent.STATE_LABELS[this.status()?.build?.state] ?? '');
70
- // Per-entity function caches, loaded on first expand. undefined = not
71
- // requested yet, null = request in flight (the skeleton window), object = loaded.
72
- _entityFunctions = signal({});
61
+ // Guards the outcome message to fire exactly once per run — whether the stream
62
+ // completes (build finished, possibly failed) or errors outright.
63
+ _buildFinalized = false;
64
+ _dialog = inject(MatDialog);
73
65
  _reportData = inject(ReportData);
74
66
  _process = inject(FsProcess);
75
67
  _prompt = inject(FsPrompt);
@@ -110,22 +102,59 @@ export class SemanticDatabaseComponent {
110
102
  // dismissing the dialog never interrupts a running build.
111
103
  _runBuild() {
112
104
  this.building.set(true);
105
+ this._buildFinalized = false;
113
106
  const target$ = this._reportData.semanticBuild()
114
- .pipe(filter((event) => event?.type === StreamEventType.Data
107
+ .pipe(
108
+ // A hard stream/transport failure (the build couldn't even start)
109
+ // finalizes here — completed$ won't fire in that case.
110
+ tap({ error: (error) => this._finalizeBuild(error) }), filter((event) => event?.type === StreamEventType.Data
115
111
  && event.data?.kind === 'log'
116
112
  && typeof event.data.line === 'string'), map((event) => event.data.line));
117
113
  this._process
118
114
  .run('Semantic database build', target$)
119
115
  .completed$
120
116
  .pipe(take(1), takeUntilDestroyed(this._destroyRef))
121
- .subscribe(() => {
122
- this.building.set(false);
123
- // The build rewrote every entity's function cache — drop the loaded
124
- // copies so the next expand refetches fresh data.
125
- this._entityFunctions.set({});
117
+ .subscribe(() => this._finalizeBuild(null));
118
+ }
119
+ /**
120
+ * Wrap up one build run exactly once. On a hard stream error there is no
121
+ * status to read, so say so directly; otherwise refresh the status and
122
+ * announce how the build actually ended — a build that failed (e.g. the AI
123
+ * couldn't be reached) reports 'Failed' with a reason, and that must surface
124
+ * instead of the run silently doing nothing.
125
+ */
126
+ _finalizeBuild(streamError) {
127
+ if (this._buildFinalized) {
128
+ return;
129
+ }
130
+ this._buildFinalized = true;
131
+ this.building.set(false);
132
+ if (streamError) {
133
+ this._message.error('The semantic database build could not run — check the AI connection and try again.');
126
134
  this._refreshStatus();
135
+ return;
136
+ }
137
+ this._reportData.semanticBuildStatus()
138
+ .pipe(takeUntilDestroyed(this._destroyRef))
139
+ .subscribe((status) => {
140
+ this.status.set(status);
141
+ this._announceOutcome(status);
127
142
  });
128
143
  }
144
+ /**
145
+ * Tell the operator how the build ended: a failed/stopped build surfaces its
146
+ * reason (so an AI connection problem is never silent), a completed one
147
+ * confirms success.
148
+ */
149
+ _announceOutcome(status) {
150
+ const build = status?.build;
151
+ if (build?.state === 'F' || build?.state === 'K') {
152
+ this._message.error(build.message || 'The semantic database build did not finish.');
153
+ }
154
+ else if (build?.state === 'C') {
155
+ this._message.success('Semantic database build complete.');
156
+ }
157
+ }
129
158
  stageColor(stage) {
130
159
  return stage === 'done'
131
160
  ? SemanticDatabaseComponent.DONE_COLOR
@@ -134,37 +163,18 @@ export class SemanticDatabaseComponent {
134
163
  stageTooltip(stage) {
135
164
  return stage === 'done' ? 'Built' : 'Needs building';
136
165
  }
137
- kindColor(kind) {
138
- return SemanticDatabaseComponent.KIND_COLORS[kind] ?? SemanticDatabaseComponent.KIND_COLORS.other;
139
- }
140
166
  /**
141
- * The loaded function cache for an entity: undefined until the panel is first
142
- * expanded, null while the request is in flight (the skeleton window), then
143
- * the loaded cache.
167
+ * Open the child dialog listing one group of an entity's cached functions —
168
+ * the model class's or the handler class's. The dialog fetches fresh on open,
169
+ * so the tab never holds function payloads.
144
170
  */
145
- entityFunctions(key) {
146
- return this._entityFunctions()[key];
147
- }
148
- /**
149
- * First expand of an entity: fetch its cached functions through the per-entity
150
- * endpoint. Subsequent expands reuse the loaded copy (a finished build clears
151
- * the copies so a re-expand refetches fresh data).
152
- */
153
- entityOpened(entity) {
154
- if (this._entityFunctions()[entity.key] !== undefined) {
155
- return;
156
- }
157
- this._entityFunctions.update((loaded) => ({ ...loaded, [entity.key]: null }));
158
- this._reportData.semanticEntityFunctions(entity.key)
159
- .pipe(takeUntilDestroyed(this._destroyRef))
160
- .subscribe((entityFunctions) => {
161
- this._entityFunctions.update((loaded) => ({
162
- ...loaded,
163
- [entity.key]: {
164
- ...entityFunctions,
165
- functions: this._sortFunctions(entityFunctions),
166
- },
167
- }));
171
+ openFunctions(entity, source) {
172
+ this._dialog.open(EntityFunctionsComponent, {
173
+ width: '700px',
174
+ maxWidth: '95vw',
175
+ maxHeight: '85vh',
176
+ autoFocus: false,
177
+ data: { entityKey: entity.key, source },
168
178
  });
169
179
  }
170
180
  _init() {
@@ -175,23 +185,8 @@ export class SemanticDatabaseComponent {
175
185
  .pipe(takeUntilDestroyed(this._destroyRef))
176
186
  .subscribe((status) => this.status.set(status));
177
187
  }
178
- /**
179
- * Order once at load time (kind priority, then method name) so the template
180
- * renders the list as-is.
181
- */
182
- _sortFunctions(entityFunctions) {
183
- return [...entityFunctions.functions]
184
- .sort((a, b) => {
185
- const kindDiff = this._kindRank(a.kind) - this._kindRank(b.kind);
186
- return kindDiff !== 0 ? kindDiff : a.method.localeCompare(b.method);
187
- });
188
- }
189
- _kindRank(kind) {
190
- const rank = SemanticDatabaseComponent.KIND_ORDER.indexOf(kind);
191
- return rank === -1 ? SemanticDatabaseComponent.KIND_ORDER.length : rank;
192
- }
193
188
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SemanticDatabaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
194
- 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 <!-- 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}.entities{display:block;padding:3px}.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: "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 });
189
+ 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 <!-- The entity scoreboard: every entity the AI knows about (an entity =\n a table + a model + a handler), its built-vs-needs-building stages,\n and a drill-down into its shape, enum values, computed fields and\n cached function counts \u2014 the counts link into the functions dialog. -->\n @if (buildStatus.detail.entities?.length) {\n <h2>Entities</h2>\n <mat-accordion class=\"entities\">\n @for (entity of buildStatus.detail.entities; track entity.key) {\n <mat-expansion-panel>\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 only exists once expanded. -->\n <ng-template matExpansionPanelContent>\n <div class=\"entity-detail\">\n <div class=\"stat\">\n <span class=\"label\">Table</span>\n <span>{{ entity.table }}</span>\n </div>\n <div class=\"stat\">\n <span class=\"label\">Columns</span>\n <span>{{ entity.columns }}</span>\n </div>\n <div class=\"stat\">\n <span class=\"label\">Relationships</span>\n <span>{{ entity.relationships }}</span>\n </div>\n @if (entity.enums.length) {\n <div class=\"stat\">\n <span class=\"label\">Enumeration values</span>\n <div class=\"enum-list\">\n @for (enumColumn of entity.enums; track enumColumn.column) {\n <div class=\"enum-row\">\n <span class=\"enum-column\">{{ enumColumn.column }}</span>\n <span>{{ enumColumn.values.join(', ') }}</span>\n </div>\n }\n </div>\n </div>\n }\n @if (entity.computedFields.length) {\n <div class=\"stat\">\n <span class=\"label\">Computed fields</span>\n <span>{{ entity.computedFields.join(', ') }}</span>\n </div>\n }\n <div class=\"stat\">\n <span class=\"label\">Model functions</span>\n @if (entity.modelFunctionCount) {\n <a\n class=\"count-link\"\n (click)=\"openFunctions(entity, 'model')\">\n {{ entity.modelFunctionCount }}\n </a>\n } @else {\n <span>0</span>\n }\n </div>\n <div class=\"stat\">\n <span class=\"label\">Handler functions</span>\n @if (entity.handlerFunctionCount) {\n <a\n class=\"count-link\"\n (click)=\"openFunctions(entity, 'handler')\">\n {{ entity.handlerFunctionCount }}\n </a>\n } @else {\n <span>0</span>\n }\n </div>\n </div>\n </ng-template>\n </mat-expansion-panel>\n }\n </mat-accordion>\n }\n }\n </div>\n</div>\n", styles: [".hint{margin:0 0 12px;color:#0009;font-size:13px;line-height:1.5}.stat{display:flex;align-items:baseline;gap:12px}.stat>.label{flex:0 0 150px;white-space:nowrap;color:#0009}.build-status{display:flex;flex-direction:column;gap:8px}.entities{display:block;padding:3px}.entities mat-panel-description{justify-content:flex-end;margin-right:8px}.entities .stage-chips{display:flex;gap:4px}.entities .entity-detail{display:flex;flex-direction:column;gap:8px}.entities .entity-detail .enum-list{display:flex;flex-direction:column;gap:3px}.entities .entity-detail .enum-list .enum-row{display:flex;gap:12px}.entities .entity-detail .enum-list .enum-row .enum-column{flex:0 0 140px;white-space:nowrap;color:#0009}\n"], dependencies: [{ 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 });
195
190
  }
196
191
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SemanticDatabaseComponent, decorators: [{
197
192
  type: Component,
@@ -202,6 +197,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
202
197
  FsDateModule,
203
198
  FsMessageModule,
204
199
  FsSkeletonModule,
205
- ], 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 <!-- 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}.entities{display:block;padding:3px}.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"] }]
200
+ ], 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 <!-- The entity scoreboard: every entity the AI knows about (an entity =\n a table + a model + a handler), its built-vs-needs-building stages,\n and a drill-down into its shape, enum values, computed fields and\n cached function counts \u2014 the counts link into the functions dialog. -->\n @if (buildStatus.detail.entities?.length) {\n <h2>Entities</h2>\n <mat-accordion class=\"entities\">\n @for (entity of buildStatus.detail.entities; track entity.key) {\n <mat-expansion-panel>\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 only exists once expanded. -->\n <ng-template matExpansionPanelContent>\n <div class=\"entity-detail\">\n <div class=\"stat\">\n <span class=\"label\">Table</span>\n <span>{{ entity.table }}</span>\n </div>\n <div class=\"stat\">\n <span class=\"label\">Columns</span>\n <span>{{ entity.columns }}</span>\n </div>\n <div class=\"stat\">\n <span class=\"label\">Relationships</span>\n <span>{{ entity.relationships }}</span>\n </div>\n @if (entity.enums.length) {\n <div class=\"stat\">\n <span class=\"label\">Enumeration values</span>\n <div class=\"enum-list\">\n @for (enumColumn of entity.enums; track enumColumn.column) {\n <div class=\"enum-row\">\n <span class=\"enum-column\">{{ enumColumn.column }}</span>\n <span>{{ enumColumn.values.join(', ') }}</span>\n </div>\n }\n </div>\n </div>\n }\n @if (entity.computedFields.length) {\n <div class=\"stat\">\n <span class=\"label\">Computed fields</span>\n <span>{{ entity.computedFields.join(', ') }}</span>\n </div>\n }\n <div class=\"stat\">\n <span class=\"label\">Model functions</span>\n @if (entity.modelFunctionCount) {\n <a\n class=\"count-link\"\n (click)=\"openFunctions(entity, 'model')\">\n {{ entity.modelFunctionCount }}\n </a>\n } @else {\n <span>0</span>\n }\n </div>\n <div class=\"stat\">\n <span class=\"label\">Handler functions</span>\n @if (entity.handlerFunctionCount) {\n <a\n class=\"count-link\"\n (click)=\"openFunctions(entity, 'handler')\">\n {{ entity.handlerFunctionCount }}\n </a>\n } @else {\n <span>0</span>\n }\n </div>\n </div>\n </ng-template>\n </mat-expansion-panel>\n }\n </mat-accordion>\n }\n }\n </div>\n</div>\n", styles: [".hint{margin:0 0 12px;color:#0009;font-size:13px;line-height:1.5}.stat{display:flex;align-items:baseline;gap:12px}.stat>.label{flex:0 0 150px;white-space:nowrap;color:#0009}.build-status{display:flex;flex-direction:column;gap:8px}.entities{display:block;padding:3px}.entities mat-panel-description{justify-content:flex-end;margin-right:8px}.entities .stage-chips{display:flex;gap:4px}.entities .entity-detail{display:flex;flex-direction:column;gap:8px}.entities .entity-detail .enum-list{display:flex;flex-direction:column;gap:3px}.entities .entity-detail .enum-list .enum-row{display:flex;gap:12px}.entities .entity-detail .enum-list .enum-row .enum-column{flex:0 0 140px;white-space:nowrap;color:#0009}\n"] }]
206
201
  }], ctorParameters: () => [] });
207
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2RpYWxvZ3MvZ2xvYmFsLXNldHRpbmdzL2NvbXBvbmVudHMvc2VtYW50aWMtZGF0YWJhc2Uvc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2RpYWxvZ3MvZ2xvYmFsLXNldHRpbmdzL2NvbXBvbmVudHMvc2VtYW50aWMtZGF0YWJhc2Uvc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDekcsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFaEUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDakUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFN0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxlQUFlLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FB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QXpCLHlCQUF5QiwwR0MvQ3RDLG1uTkFvSkEsKzdERDdHSSxrQkFBa0IscTBCQUNsQixnQkFBZ0IsNFRBQ2hCLFlBQVkscVhBQ1osWUFBWSw0SUFDWixlQUFlLHlIQUNmLGdCQUFnQjs7NEZBR1AseUJBQXlCO2tCQWZyQyxTQUFTOytCQUNFLGdEQUFnRCxtQkFHekMsdUJBQXVCLENBQUMsTUFBTSxjQUNuQyxJQUFJLFdBQ1A7d0JBQ1Asa0JBQWtCO3dCQUNsQixnQkFBZ0I7d0JBQ2hCLFlBQVk7d0JBQ1osWUFBWTt3QkFDWixlQUFlO3dCQUNmLGdCQUFnQjtxQkFDakIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ29tcG9uZW50LCBEZXN0cm95UmVmLCBjb21wdXRlZCwgaW5qZWN0LCBzaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IHRha2VVbnRpbERlc3Ryb3llZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUvcnhqcy1pbnRlcm9wJztcblxuaW1wb3J0IHsgTWF0RXhwYW5zaW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZXhwYW5zaW9uJztcbmltcG9ydCB7IE1hdFRvb2x0aXBNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC90b29sdGlwJztcblxuaW1wb3J0IHsgU3RyZWFtRXZlbnRUeXBlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvYXBpJztcbmltcG9ydCB7IEZzQ2hpcE1vZHVsZSB9IGZyb20gJ0BmaXJlc3RpdGNoL2NoaXAnO1xuaW1wb3J0IHsgRnNEYXRlTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvZGF0ZSc7XG5pbXBvcnQgeyBGc01lc3NhZ2UsIEZzTWVzc2FnZU1vZHVsZSB9IGZyb20gJ0BmaXJlc3RpdGNoL21lc3NhZ2UnO1xuaW1wb3J0IHsgRnNQcm9jZXNzIH0gZnJvbSAnQGZpcmVzdGl0Y2gvcHJvY2Vzcyc7XG5pbXBvcnQgeyBGc1Byb21wdCB9IGZyb20gJ0BmaXJlc3RpdGNoL3Byb21wdCc7XG5pbXBvcnQgeyBGc1NrZWxldG9uTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvc2tlbGV0b24nO1xuXG5pbXBvcnQgeyBmaWx0ZXIsIG1hcCwgc3dpdGNoTWFwLCB0YWtlIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQgeyBSZXBvcnREYXRhIH0gZnJvbSAnLi4vLi4vLi4vLi4vZGF0YS9yZXBvcnQuZGF0YSc7XG5pbXBvcnQge1xuICBTZW1hbnRpY0J1aWxkRW50aXR5LFxuICBTZW1hbnRpY0J1aWxkU3RhZ2UsXG4gIFNlbWFudGljQnVpbGRTdGF0dXMsXG4gIFNlbWFudGljRW50aXR5RnVuY3Rpb25zLFxufSBmcm9tICcuLi8uLi8uLi8uLi9pbnRlcmZhY2VzL3JlcG9ydC5pbnRlcmZhY2UnO1xuXG5cbi8vIFRoZSBTZW1hbnRpYyBEYXRhYmFzZSB0YWI6IHRoZSBtb2RlbCBzY29yZWJvYXJkIHRoZSByZXBvcnQgQUkgaXMgYnVpbHQgZnJvbS5cbi8vIFNob3dzIHRoZSBsYXN0IGJ1aWxkJ3Mgc3RhdHVzLCBydW5zIChvciBzdG9wcykgYSBidWlsZCDigJQgc3RyZWFtZWQgaW50byB0aGVcbi8vIEZzUHJvY2VzcyBkb2NrIHNvIGl0IHN1cnZpdmVzIHRoZSBkaWFsb2cgY2xvc2luZyDigJQgYW5kIGRyaWxscyBpbnRvIGV2ZXJ5XG4vLyBlbnRpdHkgdG8gcmV2ZWFsIGl0cyBzaGFwZSwgaW5saW5lZCBlbnVtIHZhbHVlcywgY29tcHV0ZWQgZmllbGRzIGFuZCB0aGVcbi8vIEFJLWNsYXNzaWZpZWQgY29kZSBmdW5jdGlvbnMgY2FjaGVkIGZyb20gaXRzIG1vZGVsIGNsYXNzLiBFYWNoIGVudGl0eSdzXG4vLyBmdW5jdGlvbnMgbG9hZCBsYXppbHkgb24gZmlyc3QgZXhwYW5kLCBiZWNhdXNlIHRoZSBmdWxsIHNldCBhY3Jvc3MgYWxsXG4vLyBlbnRpdGllcyBpcyBmYXIgdG9vIGJpZyB0byBzaGlwIHdpdGggdGhlIHNjb3JlYm9hcmQuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdmcy1haS1yZXBvcnQtZ2xvYmFsLXNldHRpbmdzLXNlbWFudGljLWRhdGFiYXNlJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3NlbWFudGljLWRhdGFiYXNlLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBNYXRFeHBhbnNpb25Nb2R1bGUsXG4gICAgTWF0VG9vbHRpcE1vZHVsZSxcbiAgICBGc0NoaXBNb2R1bGUsXG4gICAgRnNEYXRlTW9kdWxlLFxuICAgIEZzTWVzc2FnZU1vZHVsZSxcbiAgICBGc1NrZWxldG9uTW9kdWxlLFxuICBdLFxufSlcbmV4cG9ydCBjbGFzcyBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50IHtcblxuICAvLyBUaGUgYnVpbGQtaW4tZmxpZ2h0IHN0YXRlcyBvZiB0aGUgZmZfcHJvY2Vzc2VzIHJvdyAocXVldWVkL3J1bm5pbmcvc3RvcHBpbmcpLlxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBBQ1RJVkVfU1RBVEVTID0gWydRJywgJ1InLCAnWCddO1xuXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFNUQVRFX0xBQkVMUzogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICBROiAnUXVldWVkJyxcbiAgICBSOiAnUnVubmluZycsXG4gICAgWDogJ1N0b3BwaW5nJyxcbiAgICBDOiAnQ29tcGxldGVkJyxcbiAgICBLOiAnU3RvcHBlZCcsXG4gICAgRjogJ0ZhaWxlZCcsXG4gIH07XG5cbiAgLy8gU2NvcmVib2FyZCBjb21wbGV0aW9uIHBhbGV0dGU6IGJ1aWx0ID0gZ3JlZW4sIGV2ZXJ5dGhpbmcgZWxzZSAobmVlZHNcbiAgLy8gYnVpbGRpbmcpID0gZ3JleS4gTm8gZXJyb3IgcmVkIOKAlCBhIG1pc3Npbmcgc3RhZ2UgaXMgdW5idWlsdCBjb250ZW50LCBub3QgYVxuICAvLyBmYWlsdXJlLlxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBET05FX0NPTE9SID0gJyMyZTdkMzInO1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBQRU5ESU5HX0NPTE9SID0gJyM5ZTllOWUnO1xuXG4gIC8vIE9uZSBjb2xvciBwZXIgQUkgY2xhc3NpZmljYXRpb24ga2luZCwgc28gYSBmdW5jdGlvbidzIG5hdHVyZSByZWFkcyBhdCBhIGdsYW5jZS5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgS0lORF9DT0xPUlM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgJ2NhbGN1bGF0aW9uJzogJyMxNTY1YzAnLFxuICAgICdidXNpbmVzcy1ydWxlJzogJyM2YTFiOWEnLFxuICAgICdxdWVyeSc6ICcjMDA2OTVjJyxcbiAgICAnbXV0YXRpb24nOiAnI2U2NTEwMCcsXG4gICAgJ2FjY2Vzc29yJzogJyM1NDZlN2EnLFxuICAgICdvdGhlcic6ICcjNzU3NTc1JyxcbiAgfTtcblxuICAvLyBJbnRlcmVzdGluZyBsb2dpYyBmaXJzdDogZGVyaXZlZCB2YWx1ZXMgYW5kIHJ1bGVzLCB0aGVuIGRhdGEgYWNjZXNzLCB0aGVuIHRoZSByZXN0LlxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBLSU5EX09SREVSID0gWydjYWxjdWxhdGlvbicsICdidXNpbmVzcy1ydWxlJywgJ3F1ZXJ5JywgJ211dGF0aW9uJywgJ2FjY2Vzc29yJywgJ290aGVyJ107XG5cbiAgLy8gVGhlIHNjb3JlYm9hcmQncyBwZXItZW50aXR5IHN0YWdlIGNoaXBzLCBsZWZ0IHRvIHJpZ2h0LlxuICBwdWJsaWMgcmVhZG9ubHkgc3RhZ2VLZXlzID0gWydpbnRyb3NwZWN0ZWQnLCAnZW51bXNJbmxpbmVkJywgJ2NvbXB1dGVkJywgJ2Z1bmN0aW9ucyddO1xuXG4gIHB1YmxpYyByZWFkb25seSBzdGFnZUxhYmVsczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICBpbnRyb3NwZWN0ZWQ6ICdJbnRyb3NwZWN0ZWQnLFxuICAgIGVudW1zSW5saW5lZDogJ0VudW0gY2FzZXMnLFxuICAgIGNvbXB1dGVkOiAnQ29tcHV0ZWQnLFxuICAgIGZ1bmN0aW9uczogJ0Z1bmN0aW9ucycsXG4gIH07XG5cbiAgLy8gbnVsbCB1bnRpbCBsb2FkZWQg4oCUIGdhdGVzIHRoZSB0YWIgYmVoaW5kIHRoZSBza2VsZXRvbi5cbiAgcHVibGljIHJlYWRvbmx5IHN0YXR1cyA9IHNpZ25hbDxTZW1hbnRpY0J1aWxkU3RhdHVzIHwgbnVsbD4obnVsbCk7XG5cbiAgLy8gVHJ1ZSB3aGlsZSBhIGJ1aWxkIHN0YXJ0ZWQgZnJvbSB0aGlzIHRhYiBpcyBzdHJlYW1pbmcgaW50byB0aGUgZG9jay5cbiAgcHVibGljIHJlYWRvbmx5IGJ1aWxkaW5nID0gc2lnbmFsKGZhbHNlKTtcblxuICBwdWJsaWMgcmVhZG9ubHkgYnVpbGRBY3RpdmUgPSBjb21wdXRlZDxib29sZWFuPigoKSA9PlxuICAgIHRoaXMuYnVpbGRpbmcoKVxuICAgICAgfHwgU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5BQ1RJVkVfU1RBVEVTLmluY2x1ZGVzKHRoaXMuc3RhdHVzKCk/LmJ1aWxkPy5zdGF0ZSksXG4gICk7XG5cbiAgcHVibGljIHJlYWRvbmx5IGJ1aWxkU3RhdGVMYWJlbCA9IGNvbXB1dGVkPHN0cmluZz4oKCkgPT5cbiAgICBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LlNUQVRFX0xBQkVMU1t0aGlzLnN0YXR1cygpPy5idWlsZD8uc3RhdGVdID8/ICcnLFxuICApO1xuXG4gIC8vIFBlci1lbnRpdHkgZnVuY3Rpb24gY2FjaGVzLCBsb2FkZWQgb24gZmlyc3QgZXhwYW5kLiB1bmRlZmluZWQgPSBub3RcbiAgLy8gcmVxdWVzdGVkIHlldCwgbnVsbCA9IHJlcXVlc3QgaW4gZmxpZ2h0ICh0aGUgc2tlbGV0b24gd2luZG93KSwgb2JqZWN0ID0gbG9hZGVkLlxuICBwcml2YXRlIHJlYWRvbmx5IF9lbnRpdHlGdW5jdGlvbnMgPSBzaWduYWw8UmVjb3JkPHN0cmluZywgU2VtYW50aWNFbnRpdHlGdW5jdGlvbnMgfCBudWxsPj4oe30pO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX3JlcG9ydERhdGEgPSBpbmplY3QoUmVwb3J0RGF0YSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX3Byb2Nlc3MgPSBpbmplY3QoRnNQcm9jZXNzKTtcbiAgcHJpdmF0ZSByZWFkb25seSBfcHJvbXB0ID0gaW5qZWN0KEZzUHJvbXB0KTtcbiAgcHJpdmF0ZSByZWFkb25seSBfbWVzc2FnZSA9IGluamVjdChGc01lc3NhZ2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IF9kZXN0cm95UmVmID0gaW5qZWN0KERlc3Ryb3lSZWYpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuX2luaXQoKTtcbiAgfVxuXG4gIC8vIENvbmZpcm0gYmVmb3JlIGtpY2tpbmcgb2ZmIGEgYnVpbGQg4oCUIGEgcmVidWlsZCByZXBsYWNlcyB0aGUgY3VycmVudFxuICAvLyBzZW1hbnRpYyBkYXRhYmFzZSwgc28gaXQgc2hvdWxkIG5ldmVyIGZpcmUgb24gYSBzdHJheSBjbGljay5cbiAgcHVibGljIGJ1aWxkKCk6IHZvaWQge1xuICAgIGNvbnN0IHJlYnVpbGQgPSAhIXRoaXMuc3RhdHVzKCk/LmRldGFpbD8uZ2VuZXJhdGVkQXQ7XG5cbiAgICB0aGlzLl9wcm9tcHQuY29uZmlybSh7XG4gICAgICB0aXRsZTogcmVidWlsZCA/ICdSZWJ1aWxkIHNlbWFudGljIGRhdGFiYXNlJyA6ICdCdWlsZCBzZW1hbnRpYyBkYXRhYmFzZScsXG4gICAgICB0ZW1wbGF0ZTogcmVidWlsZFxuICAgICAgICA/ICdSZWJ1aWxkIHRoZSBzZW1hbnRpYyBkYXRhYmFzZT8gSXQgcnVucyBpbiB0aGUgYmFja2dyb3VuZCBhbmQgcmVwbGFjZXMgdGhlIGN1cnJlbnQgb25lIHdoZW4gaXQgZmluaXNoZXMuJ1xuICAgICAgICA6ICdCdWlsZCB0aGUgc2VtYW50aWMgZGF0YWJhc2U/IEl0IHJ1bnMgaW4gdGhlIGJhY2tncm91bmQuJyxcbiAgICAgIGNvbW1pdExhYmVsOiByZWJ1aWxkID8gJ1JlYnVpbGQnIDogJ0J1aWxkJyxcbiAgICB9KVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLl9ydW5CdWlsZCgpKTtcbiAgfVxuXG4gIHB1YmxpYyBzdG9wQnVpbGQoKTogdm9pZCB7XG4gICAgdGhpcy5fcHJvbXB0LmNvbmZpcm0oe1xuICAgICAgdGl0bGU6ICdTdG9wIGJ1aWxkJyxcbiAgICAgIHRlbXBsYXRlOiAnU3RvcCB0aGUgcnVubmluZyBidWlsZD8gVGhlIGN1cnJlbnQgc2VtYW50aWMgZGF0YWJhc2Ugc3RheXMgaW4gcGxhY2UgdW50aWwgYSBidWlsZCBmaW5pc2hlcy4nLFxuICAgICAgY29tbWl0TGFiZWw6ICdTdG9wJyxcbiAgICB9KVxuICAgICAgLnBpcGUoXG4gICAgICAgIHN3aXRjaE1hcCgoKSA9PiB0aGlzLl9yZXBvcnREYXRhLnNlbWFudGljQnVpbGRTdG9wKCkpLFxuICAgICAgICB0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZiksXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgdGhpcy5idWlsZGluZy5zZXQoZmFsc2UpO1xuICAgICAgICB0aGlzLl9tZXNzYWdlLnN1Y2Nlc3MoJ1N0b3BwaW5nIHRoZSBidWlsZOKApicpO1xuICAgICAgICB0aGlzLl9yZWZyZXNoU3RhdHVzKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIC8vIFJ1biB0aGUgc2VtYW50aWMtZGF0YWJhc2UgYnVpbGQuIFRoZSBsaXZlIGZlZWQgc3RyZWFtcyBpbnRvIHRoZSBGc1Byb2Nlc3NcbiAgLy8gZG9jayAoYm90dG9tLXJpZ2h0KSwgd2hvc2Ugc3Vic2NyaXB0aW9uIHN1cnZpdmVzIHRoaXMgZGlhbG9nIGNsb3Npbmcg4oCUIHNvXG4gIC8vIGRpc21pc3NpbmcgdGhlIGRpYWxvZyBuZXZlciBpbnRlcnJ1cHRzIGEgcnVubmluZyBidWlsZC5cbiAgcHJpdmF0ZSBfcnVuQnVpbGQoKTogdm9pZCB7XG4gICAgdGhpcy5idWlsZGluZy5zZXQodHJ1ZSk7XG5cbiAgICBjb25zdCB0YXJnZXQkID0gdGhpcy5fcmVwb3J0RGF0YS5zZW1hbnRpY0J1aWxkKClcbiAgICAgIC5waXBlKFxuICAgICAgICBmaWx0ZXIoKGV2ZW50KSA9PlxuICAgICAgICAgIGV2ZW50Py50eXBlID09PSBTdHJlYW1FdmVudFR5cGUuRGF0YVxuICAgICAgICAgICYmIGV2ZW50LmRhdGE/LmtpbmQgPT09ICdsb2cnXG4gICAgICAgICAgJiYgdHlwZW9mIGV2ZW50LmRhdGEubGluZSA9PT0gJ3N0cmluZycpLFxuICAgICAgICBtYXAoKGV2ZW50KSA9PiBldmVudC5kYXRhLmxpbmUgYXMgc3RyaW5nKSxcbiAgICAgICk7XG5cbiAgICB0aGlzLl9wcm9jZXNzXG4gICAgICAucnVuKCdTZW1hbnRpYyBkYXRhYmFzZSBidWlsZCcsIHRhcmdldCQpXG4gICAgICAuY29tcGxldGVkJFxuICAgICAgLnBpcGUoXG4gICAgICAgIHRha2UoMSksXG4gICAgICAgIHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICB0aGlzLmJ1aWxkaW5nLnNldChmYWxzZSk7XG4gICAgICAgIC8vIFRoZSBidWlsZCByZXdyb3RlIGV2ZXJ5IGVudGl0eSdzIGZ1bmN0aW9uIGNhY2hlIOKAlCBkcm9wIHRoZSBsb2FkZWRcbiAgICAgICAgLy8gY29waWVzIHNvIHRoZSBuZXh0IGV4cGFuZCByZWZldGNoZXMgZnJlc2ggZGF0YS5cbiAgICAgICAgdGhpcy5fZW50aXR5RnVuY3Rpb25zLnNldCh7fSk7XG4gICAgICAgIHRoaXMuX3JlZnJlc2hTdGF0dXMoKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgcHVibGljIHN0YWdlQ29sb3Ioc3RhZ2U6IFNlbWFudGljQnVpbGRTdGFnZSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHN0YWdlID09PSAnZG9uZSdcbiAgICAgID8gU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5ET05FX0NPTE9SXG4gICAgICA6IFNlbWFudGljRGF0YWJhc2VDb21wb25lbnQuUEVORElOR19DT0xPUjtcbiAgfVxuXG4gIHB1YmxpYyBzdGFnZVRvb2x0aXAoc3RhZ2U6IFNlbWFudGljQnVpbGRTdGFnZSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHN0YWdlID09PSAnZG9uZScgPyAnQnVpbHQnIDogJ05lZWRzIGJ1aWxkaW5nJztcbiAgfVxuXG4gIHB1YmxpYyBraW5kQ29sb3Ioa2luZDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5LSU5EX0NPTE9SU1traW5kXSA/PyBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LktJTkRfQ09MT1JTLm90aGVyO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBsb2FkZWQgZnVuY3Rpb24gY2FjaGUgZm9yIGFuIGVudGl0eTogdW5kZWZpbmVkIHVudGlsIHRoZSBwYW5lbCBpcyBmaXJzdFxuICAgKiBleHBhbmRlZCwgbnVsbCB3aGlsZSB0aGUgcmVxdWVzdCBpcyBpbiBmbGlnaHQgKHRoZSBza2VsZXRvbiB3aW5kb3cpLCB0aGVuXG4gICAqIHRoZSBsb2FkZWQgY2FjaGUuXG4gICAqL1xuICBwdWJsaWMgZW50aXR5RnVuY3Rpb25zKGtleTogc3RyaW5nKTogU2VtYW50aWNFbnRpdHlGdW5jdGlvbnMgfCBudWxsIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5fZW50aXR5RnVuY3Rpb25zKClba2V5XTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaXJzdCBleHBhbmQgb2YgYW4gZW50aXR5OiBmZXRjaCBpdHMgY2FjaGVkIGZ1bmN0aW9ucyB0aHJvdWdoIHRoZSBwZXItZW50aXR5XG4gICAqIGVuZHBvaW50LiBTdWJzZXF1ZW50IGV4cGFuZHMgcmV1c2UgdGhlIGxvYWRlZCBjb3B5IChhIGZpbmlzaGVkIGJ1aWxkIGNsZWFyc1xuICAgKiB0aGUgY29waWVzIHNvIGEgcmUtZXhwYW5kIHJlZmV0Y2hlcyBmcmVzaCBkYXRhKS5cbiAgICovXG4gIHB1YmxpYyBlbnRpdHlPcGVuZWQoZW50aXR5OiBTZW1hbnRpY0J1aWxkRW50aXR5KTogdm9pZCB7XG4gICAgaWYgKHRoaXMuX2VudGl0eUZ1bmN0aW9ucygpW2VudGl0eS5rZXldICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLl9lbnRpdHlGdW5jdGlvbnMudXBkYXRlKChsb2FkZWQpID0+ICh7IC4uLmxvYWRlZCwgW2VudGl0eS5rZXldOiBudWxsIH0pKTtcblxuICAgIHRoaXMuX3JlcG9ydERhdGEuc2VtYW50aWNFbnRpdHlGdW5jdGlvbnMoZW50aXR5LmtleSlcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKGVudGl0eUZ1bmN0aW9uczogU2VtYW50aWNFbnRpdHlGdW5jdGlvbnMpID0+IHtcbiAgICAgICAgdGhpcy5fZW50aXR5RnVuY3Rpb25zLnVwZGF0ZSgobG9hZGVkKSA9PiAoe1xuICAgICAgICAgIC4uLmxvYWRlZCxcbiAgICAgICAgICBbZW50aXR5LmtleV06IHtcbiAgICAgICAgICAgIC4uLmVudGl0eUZ1bmN0aW9ucyxcbiAgICAgICAgICAgIGZ1bmN0aW9uczogdGhpcy5fc29ydEZ1bmN0aW9ucyhlbnRpdHlGdW5jdGlvbnMpLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfaW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLl9yZWZyZXNoU3RhdHVzKCk7XG4gIH1cblxuICBwcml2YXRlIF9yZWZyZXNoU3RhdHVzKCk6IHZvaWQge1xuICAgIHRoaXMuX3JlcG9ydERhdGEuc2VtYW50aWNCdWlsZFN0YXR1cygpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChzdGF0dXM6IFNlbWFudGljQnVpbGRTdGF0dXMpID0+IHRoaXMuc3RhdHVzLnNldChzdGF0dXMpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPcmRlciBvbmNlIGF0IGxvYWQgdGltZSAoa2luZCBwcmlvcml0eSwgdGhlbiBtZXRob2QgbmFtZSkgc28gdGhlIHRlbXBsYXRlXG4gICAqIHJlbmRlcnMgdGhlIGxpc3QgYXMtaXMuXG4gICAqL1xuICBwcml2YXRlIF9zb3J0RnVuY3Rpb25zKGVudGl0eUZ1bmN0aW9uczogU2VtYW50aWNFbnRpdHlGdW5jdGlvbnMpOiBTZW1hbnRpY0VudGl0eUZ1bmN0aW9uc1snZnVuY3Rpb25zJ10ge1xuICAgIHJldHVybiBbLi4uZW50aXR5RnVuY3Rpb25zLmZ1bmN0aW9uc11cbiAgICAgIC5zb3J0KChhLCBiKSA9PiB7XG4gICAgICAgIGNvbnN0IGtpbmREaWZmID0gdGhpcy5fa2luZFJhbmsoYS5raW5kKSAtIHRoaXMuX2tpbmRSYW5rKGIua2luZCk7XG5cbiAgICAgICAgcmV0dXJuIGtpbmREaWZmICE9PSAwID8ga2luZERpZmYgOiBhLm1ldGhvZC5sb2NhbGVDb21wYXJlKGIubWV0aG9kKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfa2luZFJhbmsoa2luZDogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBjb25zdCByYW5rID0gU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5LSU5EX09SREVSLmluZGV4T2Yoa2luZCk7XG5cbiAgICByZXR1cm4gcmFuayA9PT0gLTEgPyBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LktJTkRfT1JERVIubGVuZ3RoIDogcmFuaztcbiAgfVxufVxuIiwiPGRpdiBjbGFzcz1cImZzLWNvbHVtbiB0YWItYm9keVwiPlxuICA8cCBjbGFzcz1cImhpbnRcIj5cbiAgICBUaGUgc2VtYW50aWMgZGF0YWJhc2UgaXMgd2hhdCB0aGUgcmVwb3J0IEFJIHVzZXMgdG8gdW5kZXJzdGFuZCB5b3VyIGRhdGEg4oCUXG4gICAgdGFibGVzLCByZWxhdGlvbnNoaXBzIGFuZCB0aGUgY2FsY3VsYXRpb25zIGluIGNvZGUuIFJlYnVpbGQgaXQgYWZ0ZXJcbiAgICBzaWduaWZpY2FudCBhcHBsaWNhdGlvbiB1cGRhdGVzLiBUaGUgYnVpbGQgcnVucyBpbiB0aGUgYmFja2dyb3VuZDsgcHJvZ3Jlc3NcbiAgICBhcHBlYXJzIGluIHRoZSBkb2NrIGF0IHRoZSBib3R0b20gcmlnaHQgYW5kIHRoaXMgZGlhbG9nIGNhbiBiZSBjbG9zZWQgd2hpbGVcbiAgICBpdCBydW5zLlxuICA8L3A+XG5cbiAgPGRpdiAqZnNTa2VsZXRvbj1cInN0YXR1cygpXCI+XG4gICAgQGlmIChzdGF0dXMoKTsgYXMgYnVpbGRTdGF0dXMpIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJidWlsZC1zdGF0dXNcIj5cbiAgICAgICAgQGlmIChidWlsZFN0YXR1cy5kZXRhaWwuZ2VuZXJhdGVkQXQpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhdFwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsYWJlbFwiPkxhc3QgYnVpbHQ8L3NwYW4+XG4gICAgICAgICAgICA8ZnMtZGF0ZVxuICAgICAgICAgICAgICAgIFtkYXRlXT1cImJ1aWxkU3RhdHVzLmRldGFpbC5nZW5lcmF0ZWRBdFwiXG4gICAgICAgICAgICAgICAgZm9ybWF0PVwiZGF0ZS10aW1lXCI+XG4gICAgICAgICAgICA8L2ZzLWRhdGU+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cInN0YXRcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5Db3ZlcmFnZTwvc3Bhbj5cbiAgICAgICAgICAgIDxzcGFuPlxuICAgICAgICAgICAgICB7eyBidWlsZFN0YXR1cy5kZXRhaWwuZW50aXR5Q291bnQgfX0gZW50aXRpZXMgwrdcbiAgICAgICAgICAgICAge3sgYnVpbGRTdGF0dXMuZGV0YWlsLmZ1bmN0aW9uQ291bnQgfX0gY29kZSBmdW5jdGlvbnNcbiAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgPGZzLW1lc3NhZ2Utd2FybmluZz5cbiAgICAgICAgICAgIFRoZSBzZW1hbnRpYyBkYXRhYmFzZSBoYXMgbm90IGJlZW4gYnVpbHQgeWV0IOKAlCB0aGUgcmVwb3J0IEFJIGNhbid0XG4gICAgICAgICAgICBhbnN3ZXIgZGF0YSBxdWVzdGlvbnMgdW50aWwgaXQgaXMuIFJ1biB0aGUgZmlyc3QgYnVpbGQuXG4gICAgICAgICAgPC9mcy1tZXNzYWdlLXdhcm5pbmc+XG4gICAgICAgIH1cbiAgICAgICAgQGlmIChidWlsZFN0YXR1cy5idWlsZCkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGF0XCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxhYmVsXCI+TGFzdCBidWlsZCBydW48L3NwYW4+XG4gICAgICAgICAgICA8c3Bhbj5cbiAgICAgICAgICAgICAge3sgYnVpbGRTdGF0ZUxhYmVsKCkgfX1cbiAgICAgICAgICAgICAgQGlmIChidWlsZFN0YXR1cy5idWlsZC5tZXNzYWdlKSB7XG4gICAgICAgICAgICAgICAg4oCUIHt7IGJ1aWxkU3RhdHVzLmJ1aWxkLm1lc3NhZ2UgfX1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cblxuICAgICAgPCEtLSBUaGUgbW9kZWwgc2NvcmVib2FyZDogZXZlcnkgZW50aXR5IHRoZSBBSSBrbm93cyBhYm91dCwgaXRzXG4gICAgICAgICAgIGJ1aWx0LXZzLW5lZWRzLWJ1aWxkaW5nIHN0YWdlcywgYW5kIGEgZHJpbGwtZG93biBpbnRvIGl0cyBzaGFwZSwgZW51bVxuICAgICAgICAgICB2YWx1ZXMsIGNvbXB1dGVkIGZpZWxkcyBhbmQgQUktY2xhc3NpZmllZCBjb2RlIGZ1bmN0aW9ucy4gLS0+XG4gICAgICBAaWYgKGJ1aWxkU3RhdHVzLmRldGFpbC5lbnRpdGllcz8ubGVuZ3RoKSB7XG4gICAgICAgIDxtYXQtYWNjb3JkaW9uIGNsYXNzPVwiZW50aXRpZXNcIj5cbiAgICAgICAgICBAZm9yIChlbnRpdHkgb2YgYnVpbGRTdGF0dXMuZGV0YWlsLmVudGl0aWVzOyB0cmFjayBlbnRpdHkua2V5KSB7XG4gICAgICAgICAgICA8bWF0LWV4cGFuc2lvbi1wYW5lbCAob3BlbmVkKT1cImVudGl0eU9wZW5lZChlbnRpdHkpXCI+XG4gICAgICAgICAgICAgIDxtYXQtZXhwYW5zaW9uLXBhbmVsLWhlYWRlcj5cbiAgICAgICAgICAgICAgICA8bWF0LXBhbmVsLXRpdGxlPnt7IGVudGl0eS5rZXkgfX08L21hdC1wYW5lbC10aXRsZT5cbiAgICAgICAgICAgICAgICA8bWF0LXBhbmVsLWRlc2NyaXB0aW9uPlxuICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInN0YWdlLWNoaXBzXCI+XG4gICAgICAgICAgICAgICAgICAgIEBmb3IgKHN0YWdlIG9mIHN0YWdlS2V5czsgdHJhY2sgc3RhZ2UpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8ZnMtY2hpcFxuICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPVwidGlueVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtiYWNrZ3JvdW5kQ29sb3JdPVwic3RhZ2VDb2xvcihlbnRpdHkuc3RhZ2VzW3N0YWdlXSlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1cIiNmZmZmZmZcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBbbWF0VG9vbHRpcF09XCJzdGFnZVRvb2x0aXAoZW50aXR5LnN0YWdlc1tzdGFnZV0pXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7eyBzdGFnZUxhYmVsc1tzdGFnZV0gfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2ZzLWNoaXA+XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvbWF0LXBhbmVsLWRlc2NyaXB0aW9uPlxuICAgICAgICAgICAgICA8L21hdC1leHBhbnNpb24tcGFuZWwtaGVhZGVyPlxuXG4gICAgICAgICAgICAgIDwhLS0gRGVmZXJyZWQ6IHRoZSBib2R5IChhbmQgaXRzIGZ1bmN0aW9ucyByZXF1ZXN0KSBvbmx5IGV4aXN0c1xuICAgICAgICAgICAgICAgICAgIG9uY2UgZXhwYW5kZWQuIC0tPlxuICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgbWF0RXhwYW5zaW9uUGFuZWxDb250ZW50PlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnRpdHktZGV0YWlsXCI+XG4gICAgICAgICAgICAgICAgICA8ZGl2PjxzdHJvbmc+VGFibGU6PC9zdHJvbmc+IHt7IGVudGl0eS50YWJsZSB9fTwvZGl2PlxuICAgICAgICAgICAgICAgICAgPGRpdj48c3Ryb25nPkNvbHVtbnM6PC9zdHJvbmc+IHt7IGVudGl0eS5jb2x1bW5zIH19PC9kaXY+XG4gICAgICAgICAgICAgICAgICA8ZGl2PjxzdHJvbmc+UmVsYXRpb25zaGlwczo8L3N0cm9uZz4ge3sgZW50aXR5LnJlbGF0aW9uc2hpcHMgfX08L2Rpdj5cbiAgICAgICAgICAgICAgICAgIEBpZiAoZW50aXR5LmVudW1zLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW51bS1saXN0XCI+XG4gICAgICAgICAgICAgICAgICAgICAgPHN0cm9uZz5FbnVtIHZhbHVlczo8L3N0cm9uZz5cbiAgICAgICAgICAgICAgICAgICAgICBAZm9yIChlbnVtQ29sdW1uIG9mIGVudGl0eS5lbnVtczsgdHJhY2sgZW51bUNvbHVtbi5jb2x1bW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnVtLXJvd1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImVudW0tY29sdW1uXCI+e3sgZW51bUNvbHVtbi5jb2x1bW4gfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZW51bS12YWx1ZXNcIj57eyBlbnVtQ29sdW1uLnZhbHVlcy5qb2luKCcsICcpIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIEBpZiAoZW50aXR5LmNvbXB1dGVkRmllbGRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICA8ZGl2PjxzdHJvbmc+Q29tcHV0ZWQgZmllbGRzOjwvc3Ryb25nPiB7eyBlbnRpdHkuY29tcHV0ZWRGaWVsZHMuam9pbignLCAnKSB9fTwvZGl2PlxuICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZnVuY3Rpb25zLXNlY3Rpb25cIj5cbiAgICAgICAgICAgICAgICAgICAgPGgzIGNsYXNzPVwiZnVuY3Rpb25zLWhlYWRpbmdcIj5cbiAgICAgICAgICAgICAgICAgICAgICBDb2RlIGZ1bmN0aW9uc1xuICAgICAgICAgICAgICAgICAgICAgIEBpZiAoZW50aXR5LmZ1bmN0aW9uQ291bnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZnVuY3Rpb25zLWNvdW50XCI+KHt7IGVudGl0eS5mdW5jdGlvbkNvdW50IH19KTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIDwvaDM+XG5cbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImZ1bmN0aW9ucy1ib2R5XCIgKmZzU2tlbGV0b249XCJlbnRpdHlGdW5jdGlvbnMoZW50aXR5LmtleSlcIj5cbiAgICAgICAgICAgICAgICAgICAgICBAaWYgKGVudGl0eUZ1bmN0aW9ucyhlbnRpdHkua2V5KTsgYXMgZm5zKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBAaWYgKGZucy5mdW5jdGlvbnMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIEBpZiAoZm5zLmdlbmVyYXRlZEF0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImhpbnQgZnVuY3Rpb25zLWdlbmVyYXRlZFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlzY292ZXJlZCBmcm9tIHt7IGZucy5jbGFzcyB9fSAmbWlkZG90O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGZzLWRhdGVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbZGF0ZV09XCJmbnMuZ2VuZXJhdGVkQXRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdD1cImRhdGUtdGltZVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9mcy1kYXRlPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmdW5jdGlvbnMtbGlzdFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEBmb3IgKGZuIG9mIGZucy5mdW5jdGlvbnM7IHRyYWNrIGZuLm1ldGhvZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImZ1bmN0aW9uLXJvd1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZnVuY3Rpb24taGVhZFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxmcy1jaGlwXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9XCJ0aW55XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2JhY2tncm91bmRDb2xvcl09XCJraW5kQ29sb3IoZm4ua2luZClcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1cIiNmZmZmZmZcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHt7IGZuLmtpbmQgfX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2ZzLWNoaXA+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvZGUgY2xhc3M9XCJmdW5jdGlvbi1tZXRob2RcIj57eyBmbi5tZXRob2QgfX08L2NvZGU+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZnVuY3Rpb24tc3VtbWFyeVwiPnt7IGZuLnN1bW1hcnkgfX08L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQGlmIChmbi5wcm9kdWNlcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmdW5jdGlvbi1wcm9kdWNlcyBoaW50XCI+UHJvZHVjZXM6IHt7IGZuLnByb2R1Y2VzIH19PC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImhpbnRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBObyBmdW5jdGlvbnMgY2FjaGVkIGZvciB0aGlzIGVudGl0eSB5ZXQg4oCUIHJ1biBhIGJ1aWxkIHRvIGRpc2NvdmVyIGl0cyBjb2RlLlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgICA8L21hdC1leHBhbnNpb24tcGFuZWw+XG4gICAgICAgICAgfVxuICAgICAgICA8L21hdC1hY2NvcmRpb24+XG4gICAgICB9XG4gICAgfVxuICA8L2Rpdj5cbjwvZGl2PlxuIl19
202
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2RpYWxvZ3MvZ2xvYmFsLXNldHRpbmdzL2NvbXBvbmVudHMvc2VtYW50aWMtZGF0YWJhc2Uvc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2RpYWxvZ3MvZ2xvYmFsLXNldHRpbmdzL2NvbXBvbmVudHMvc2VtYW50aWMtZGF0YWJhc2Uvc2VtYW50aWMtZGF0YWJhc2UuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDekcsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFaEUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRTdELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNsRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDakUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5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MkI7UUFDM0UsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsd0JBQXdCLEVBQUU7WUFDMUMsS0FBSyxFQUFFLE9BQU87WUFDZCxRQUFRLEVBQUUsTUFBTTtZQUNoQixTQUFTLEVBQUUsTUFBTTtZQUNqQixTQUFTLEVBQUUsS0FBSztZQUNoQixJQUFJLEVBQUUsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUU7U0FDeEMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVPLEtBQUs7UUFDWCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVPLGNBQWM7UUFDcEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxtQkFBbUIsRUFBRTthQUNuQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO2FBQzFDLFNBQVMsQ0FBQyxDQUFDLE1BQTJCLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekUsQ0FBQzt3R0F2TVUseUJBQXlCOzRGQUF6Qix5QkFBeUIsMEdDaER0Qyx1dkxBMklBLDJ2QkRuR0ksa0JBQWtCLHEwQkFDbEIsZ0JBQWdCLDRUQUNoQixZQUFZLHFYQUNaLFlBQVksNElBQ1osZUFBZSx5SEFDZixnQkFBZ0I7OzRGQUdQLHlCQUF5QjtrQkFmckMsU0FBUzsrQkFDRSxnREFBZ0QsbUJBR3pDLHVCQUF1QixDQUFDLE1BQU0sY0FDbkMsSUFBSSxXQUNQO3dCQUNQLGtCQUFrQjt3QkFDbEIsZ0JBQWdCO3dCQUNoQixZQUFZO3dCQUNaLFlBQVk7d0JBQ1osZUFBZTt3QkFDZixnQkFBZ0I7cUJBQ2pCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgRGVzdHJveVJlZiwgY29tcHV0ZWQsIGluamVjdCwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyB0YWtlVW50aWxEZXN0cm95ZWQgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XG5cbmltcG9ydCB7IE1hdERpYWxvZyB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2RpYWxvZyc7XG5pbXBvcnQgeyBNYXRFeHBhbnNpb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9leHBhbnNpb24nO1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuXG5pbXBvcnQgeyBTdHJlYW1FdmVudFR5cGUgfSBmcm9tICdAZmlyZXN0aXRjaC9hcGknO1xuaW1wb3J0IHsgRnNDaGlwTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvY2hpcCc7XG5pbXBvcnQgeyBGc0RhdGVNb2R1bGUgfSBmcm9tICdAZmlyZXN0aXRjaC9kYXRlJztcbmltcG9ydCB7IEZzTWVzc2FnZSwgRnNNZXNzYWdlTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvbWVzc2FnZSc7XG5pbXBvcnQgeyBGc1Byb2Nlc3MgfSBmcm9tICdAZmlyZXN0aXRjaC9wcm9jZXNzJztcbmltcG9ydCB7IEZzUHJvbXB0IH0gZnJvbSAnQGZpcmVzdGl0Y2gvcHJvbXB0JztcbmltcG9ydCB7IEZzU2tlbGV0b25Nb2R1bGUgfSBmcm9tICdAZmlyZXN0aXRjaC9za2VsZXRvbic7XG5cbmltcG9ydCB7IGZpbHRlciwgbWFwLCBzd2l0Y2hNYXAsIHRha2UsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgUmVwb3J0RGF0YSB9IGZyb20gJy4uLy4uLy4uLy4uL2RhdGEvcmVwb3J0LmRhdGEnO1xuaW1wb3J0IHtcbiAgU2VtYW50aWNCdWlsZEVudGl0eSxcbiAgU2VtYW50aWNCdWlsZFN0YWdlLFxuICBTZW1hbnRpY0J1aWxkU3RhdHVzLFxufSBmcm9tICcuLi8uLi8uLi8uLi9pbnRlcmZhY2VzL3JlcG9ydC5pbnRlcmZhY2UnO1xuaW1wb3J0IHsgRW50aXR5RnVuY3Rpb25zQ29tcG9uZW50IH0gZnJvbSAnLi4vLi4vLi4vZW50aXR5LWZ1bmN0aW9ucy9lbnRpdHktZnVuY3Rpb25zLmNvbXBvbmVudCc7XG5cblxuLy8gVGhlIFNlbWFudGljIERhdGFiYXNlIHRhYjogdGhlIGVudGl0eSBzY29yZWJvYXJkIHRoZSByZXBvcnQgQUkgaXMgYnVpbHQgZnJvbVxuLy8gKGFuIGVudGl0eSA9IGEgdGFibGUgKyBhIG1vZGVsICsgYSBoYW5kbGVyKS4gU2hvd3MgdGhlIGxhc3QgYnVpbGQncyBzdGF0dXMsXG4vLyBydW5zIChvciBzdG9wcykgYSBidWlsZCDigJQgc3RyZWFtZWQgaW50byB0aGUgRnNQcm9jZXNzIGRvY2sgc28gaXQgc3Vydml2ZXMgdGhlXG4vLyBkaWFsb2cgY2xvc2luZyDigJQgYW5kIGRyaWxscyBpbnRvIGV2ZXJ5IGVudGl0eSB0byByZXZlYWwgaXRzIHNoYXBlLCBpbmxpbmVkXG4vLyBlbnVtIHZhbHVlcywgY29tcHV0ZWQgZmllbGRzIGFuZCBpdHMgY2FjaGVkIGZ1bmN0aW9uIGNvdW50cywgd2hvc2UgbGlua3Mgb3BlblxuLy8gdGhlIGZ1bmN0aW9ucyBkaWFsb2cgKHRoZSBmdWxsIGZ1bmN0aW9uIHNldCBhY3Jvc3MgYWxsIGVudGl0aWVzIGlzIGZhciB0b29cbi8vIGJpZyB0byBzaGlwIHdpdGggdGhlIHNjb3JlYm9hcmQpLlxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnZnMtYWktcmVwb3J0LWdsb2JhbC1zZXR0aW5ncy1zZW1hbnRpYy1kYXRhYmFzZScsXG4gIHRlbXBsYXRlVXJsOiAnLi9zZW1hbnRpYy1kYXRhYmFzZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3NlbWFudGljLWRhdGFiYXNlLmNvbXBvbmVudC5zY3NzJ10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgTWF0RXhwYW5zaW9uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGUsXG4gICAgRnNDaGlwTW9kdWxlLFxuICAgIEZzRGF0ZU1vZHVsZSxcbiAgICBGc01lc3NhZ2VNb2R1bGUsXG4gICAgRnNTa2VsZXRvbk1vZHVsZSxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudCB7XG5cbiAgLy8gVGhlIGJ1aWxkLWluLWZsaWdodCBzdGF0ZXMgb2YgdGhlIGZmX3Byb2Nlc3NlcyByb3cgKHF1ZXVlZC9ydW5uaW5nL3N0b3BwaW5nKS5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQUNUSVZFX1NUQVRFUyA9IFsnUScsICdSJywgJ1gnXTtcblxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBTVEFURV9MQUJFTFM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgUTogJ1F1ZXVlZCcsXG4gICAgUjogJ1J1bm5pbmcnLFxuICAgIFg6ICdTdG9wcGluZycsXG4gICAgQzogJ0NvbXBsZXRlZCcsXG4gICAgSzogJ1N0b3BwZWQnLFxuICAgIEY6ICdGYWlsZWQnLFxuICB9O1xuXG4gIC8vIFNjb3JlYm9hcmQgY29tcGxldGlvbiBwYWxldHRlOiBidWlsdCA9IGdyZWVuLCBldmVyeXRoaW5nIGVsc2UgKG5lZWRzXG4gIC8vIGJ1aWxkaW5nKSA9IGdyZXkuIE5vIGVycm9yIHJlZCDigJQgYSBtaXNzaW5nIHN0YWdlIGlzIHVuYnVpbHQgY29udGVudCwgbm90IGFcbiAgLy8gZmFpbHVyZS5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgRE9ORV9DT0xPUiA9ICcjMmU3ZDMyJztcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgUEVORElOR19DT0xPUiA9ICcjOWU5ZTllJztcblxuICAvLyBUaGUgc2NvcmVib2FyZCdzIHBlci1lbnRpdHkgc3RhZ2UgY2hpcHMsIGxlZnQgdG8gcmlnaHQuXG4gIHB1YmxpYyByZWFkb25seSBzdGFnZUtleXMgPSBbJ2ludHJvc3BlY3RlZCcsICdlbnVtc0lubGluZWQnLCAnY29tcHV0ZWQnLCAnZnVuY3Rpb25zJ107XG5cbiAgcHVibGljIHJlYWRvbmx5IHN0YWdlTGFiZWxzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgIGludHJvc3BlY3RlZDogJ0ludHJvc3BlY3RlZCcsXG4gICAgZW51bXNJbmxpbmVkOiAnRW51bSBjYXNlcycsXG4gICAgY29tcHV0ZWQ6ICdDb21wdXRlZCcsXG4gICAgZnVuY3Rpb25zOiAnRnVuY3Rpb25zJyxcbiAgfTtcblxuICAvLyBudWxsIHVudGlsIGxvYWRlZCDigJQgZ2F0ZXMgdGhlIHRhYiBiZWhpbmQgdGhlIHNrZWxldG9uLlxuICBwdWJsaWMgcmVhZG9ubHkgc3RhdHVzID0gc2lnbmFsPFNlbWFudGljQnVpbGRTdGF0dXMgfCBudWxsPihudWxsKTtcblxuICAvLyBUcnVlIHdoaWxlIGEgYnVpbGQgc3RhcnRlZCBmcm9tIHRoaXMgdGFiIGlzIHN0cmVhbWluZyBpbnRvIHRoZSBkb2NrLlxuICBwdWJsaWMgcmVhZG9ubHkgYnVpbGRpbmcgPSBzaWduYWwoZmFsc2UpO1xuXG4gIHB1YmxpYyByZWFkb25seSBidWlsZEFjdGl2ZSA9IGNvbXB1dGVkPGJvb2xlYW4+KCgpID0+XG4gICAgdGhpcy5idWlsZGluZygpXG4gICAgICB8fCBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LkFDVElWRV9TVEFURVMuaW5jbHVkZXModGhpcy5zdGF0dXMoKT8uYnVpbGQ/LnN0YXRlKSxcbiAgKTtcblxuICBwdWJsaWMgcmVhZG9ubHkgYnVpbGRTdGF0ZUxhYmVsID0gY29tcHV0ZWQ8c3RyaW5nPigoKSA9PlxuICAgIFNlbWFudGljRGF0YWJhc2VDb21wb25lbnQuU1RBVEVfTEFCRUxTW3RoaXMuc3RhdHVzKCk/LmJ1aWxkPy5zdGF0ZV0gPz8gJycsXG4gICk7XG5cbiAgLy8gR3VhcmRzIHRoZSBvdXRjb21lIG1lc3NhZ2UgdG8gZmlyZSBleGFjdGx5IG9uY2UgcGVyIHJ1biDigJQgd2hldGhlciB0aGUgc3RyZWFtXG4gIC8vIGNvbXBsZXRlcyAoYnVpbGQgZmluaXNoZWQsIHBvc3NpYmx5IGZhaWxlZCkgb3IgZXJyb3JzIG91dHJpZ2h0LlxuICBwcml2YXRlIF9idWlsZEZpbmFsaXplZCA9IGZhbHNlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX2RpYWxvZyA9IGluamVjdChNYXREaWFsb2cpO1xuICBwcml2YXRlIHJlYWRvbmx5IF9yZXBvcnREYXRhID0gaW5qZWN0KFJlcG9ydERhdGEpO1xuICBwcml2YXRlIHJlYWRvbmx5IF9wcm9jZXNzID0gaW5qZWN0KEZzUHJvY2Vzcyk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX3Byb21wdCA9IGluamVjdChGc1Byb21wdCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX21lc3NhZ2UgPSBpbmplY3QoRnNNZXNzYWdlKTtcbiAgcHJpdmF0ZSByZWFkb25seSBfZGVzdHJveVJlZiA9IGluamVjdChEZXN0cm95UmVmKTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLl9pbml0KCk7XG4gIH1cblxuICAvLyBDb25maXJtIGJlZm9yZSBraWNraW5nIG9mZiBhIGJ1aWxkIOKAlCBhIHJlYnVpbGQgcmVwbGFjZXMgdGhlIGN1cnJlbnRcbiAgLy8gc2VtYW50aWMgZGF0YWJhc2UsIHNvIGl0IHNob3VsZCBuZXZlciBmaXJlIG9uIGEgc3RyYXkgY2xpY2suXG4gIHB1YmxpYyBidWlsZCgpOiB2b2lkIHtcbiAgICBjb25zdCByZWJ1aWxkID0gISF0aGlzLnN0YXR1cygpPy5kZXRhaWw/LmdlbmVyYXRlZEF0O1xuXG4gICAgdGhpcy5fcHJvbXB0LmNvbmZpcm0oe1xuICAgICAgdGl0bGU6IHJlYnVpbGQgPyAnUmVidWlsZCBzZW1hbnRpYyBkYXRhYmFzZScgOiAnQnVpbGQgc2VtYW50aWMgZGF0YWJhc2UnLFxuICAgICAgdGVtcGxhdGU6IHJlYnVpbGRcbiAgICAgICAgPyAnUmVidWlsZCB0aGUgc2VtYW50aWMgZGF0YWJhc2U/IEl0IHJ1bnMgaW4gdGhlIGJhY2tncm91bmQgYW5kIHJlcGxhY2VzIHRoZSBjdXJyZW50IG9uZSB3aGVuIGl0IGZpbmlzaGVzLidcbiAgICAgICAgOiAnQnVpbGQgdGhlIHNlbWFudGljIGRhdGFiYXNlPyBJdCBydW5zIGluIHRoZSBiYWNrZ3JvdW5kLicsXG4gICAgICBjb21taXRMYWJlbDogcmVidWlsZCA/ICdSZWJ1aWxkJyA6ICdCdWlsZCcsXG4gICAgfSlcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5fcnVuQnVpbGQoKSk7XG4gIH1cblxuICBwdWJsaWMgc3RvcEJ1aWxkKCk6IHZvaWQge1xuICAgIHRoaXMuX3Byb21wdC5jb25maXJtKHtcbiAgICAgIHRpdGxlOiAnU3RvcCBidWlsZCcsXG4gICAgICB0ZW1wbGF0ZTogJ1N0b3AgdGhlIHJ1bm5pbmcgYnVpbGQ/IFRoZSBjdXJyZW50IHNlbWFudGljIGRhdGFiYXNlIHN0YXlzIGluIHBsYWNlIHVudGlsIGEgYnVpbGQgZmluaXNoZXMuJyxcbiAgICAgIGNvbW1pdExhYmVsOiAnU3RvcCcsXG4gICAgfSlcbiAgICAgIC5waXBlKFxuICAgICAgICBzd2l0Y2hNYXAoKCkgPT4gdGhpcy5fcmVwb3J0RGF0YS5zZW1hbnRpY0J1aWxkU3RvcCgpKSxcbiAgICAgICAgdGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpLFxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgIHRoaXMuYnVpbGRpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgdGhpcy5fbWVzc2FnZS5zdWNjZXNzKCdTdG9wcGluZyB0aGUgYnVpbGTigKYnKTtcbiAgICAgICAgdGhpcy5fcmVmcmVzaFN0YXR1cygpO1xuICAgICAgfSk7XG4gIH1cblxuICAvLyBSdW4gdGhlIHNlbWFudGljLWRhdGFiYXNlIGJ1aWxkLiBUaGUgbGl2ZSBmZWVkIHN0cmVhbXMgaW50byB0aGUgRnNQcm9jZXNzXG4gIC8vIGRvY2sgKGJvdHRvbS1yaWdodCksIHdob3NlIHN1YnNjcmlwdGlvbiBzdXJ2aXZlcyB0aGlzIGRpYWxvZyBjbG9zaW5nIOKAlCBzb1xuICAvLyBkaXNtaXNzaW5nIHRoZSBkaWFsb2cgbmV2ZXIgaW50ZXJydXB0cyBhIHJ1bm5pbmcgYnVpbGQuXG4gIHByaXZhdGUgX3J1bkJ1aWxkKCk6IHZvaWQge1xuICAgIHRoaXMuYnVpbGRpbmcuc2V0KHRydWUpO1xuICAgIHRoaXMuX2J1aWxkRmluYWxpemVkID0gZmFsc2U7XG5cbiAgICBjb25zdCB0YXJnZXQkID0gdGhpcy5fcmVwb3J0RGF0YS5zZW1hbnRpY0J1aWxkKClcbiAgICAgIC5waXBlKFxuICAgICAgICAvLyBBIGhhcmQgc3RyZWFtL3RyYW5zcG9ydCBmYWlsdXJlICh0aGUgYnVpbGQgY291bGRuJ3QgZXZlbiBzdGFydClcbiAgICAgICAgLy8gZmluYWxpemVzIGhlcmUg4oCUIGNvbXBsZXRlZCQgd29uJ3QgZmlyZSBpbiB0aGF0IGNhc2UuXG4gICAgICAgIHRhcCh7IGVycm9yOiAoZXJyb3IpID0+IHRoaXMuX2ZpbmFsaXplQnVpbGQoZXJyb3IpIH0pLFxuICAgICAgICBmaWx0ZXIoKGV2ZW50KSA9PlxuICAgICAgICAgIGV2ZW50Py50eXBlID09PSBTdHJlYW1FdmVudFR5cGUuRGF0YVxuICAgICAgICAgICYmIGV2ZW50LmRhdGE/LmtpbmQgPT09ICdsb2cnXG4gICAgICAgICAgJiYgdHlwZW9mIGV2ZW50LmRhdGEubGluZSA9PT0gJ3N0cmluZycpLFxuICAgICAgICBtYXAoKGV2ZW50KSA9PiBldmVudC5kYXRhLmxpbmUgYXMgc3RyaW5nKSxcbiAgICAgICk7XG5cbiAgICB0aGlzLl9wcm9jZXNzXG4gICAgICAucnVuKCdTZW1hbnRpYyBkYXRhYmFzZSBidWlsZCcsIHRhcmdldCQpXG4gICAgICAuY29tcGxldGVkJFxuICAgICAgLnBpcGUoXG4gICAgICAgIHRha2UoMSksXG4gICAgICAgIHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5fZmluYWxpemVCdWlsZChudWxsKSk7XG4gIH1cblxuICAvKipcbiAgICogV3JhcCB1cCBvbmUgYnVpbGQgcnVuIGV4YWN0bHkgb25jZS4gT24gYSBoYXJkIHN0cmVhbSBlcnJvciB0aGVyZSBpcyBub1xuICAgKiBzdGF0dXMgdG8gcmVhZCwgc28gc2F5IHNvIGRpcmVjdGx5OyBvdGhlcndpc2UgcmVmcmVzaCB0aGUgc3RhdHVzIGFuZFxuICAgKiBhbm5vdW5jZSBob3cgdGhlIGJ1aWxkIGFjdHVhbGx5IGVuZGVkIOKAlCBhIGJ1aWxkIHRoYXQgZmFpbGVkIChlLmcuIHRoZSBBSVxuICAgKiBjb3VsZG4ndCBiZSByZWFjaGVkKSByZXBvcnRzICdGYWlsZWQnIHdpdGggYSByZWFzb24sIGFuZCB0aGF0IG11c3Qgc3VyZmFjZVxuICAgKiBpbnN0ZWFkIG9mIHRoZSBydW4gc2lsZW50bHkgZG9pbmcgbm90aGluZy5cbiAgICovXG4gIHByaXZhdGUgX2ZpbmFsaXplQnVpbGQoc3RyZWFtRXJyb3I6IHVua25vd24pOiB2b2lkIHtcbiAgICBpZiAodGhpcy5fYnVpbGRGaW5hbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5fYnVpbGRGaW5hbGl6ZWQgPSB0cnVlO1xuXG4gICAgdGhpcy5idWlsZGluZy5zZXQoZmFsc2UpO1xuXG4gICAgaWYgKHN0cmVhbUVycm9yKSB7XG4gICAgICB0aGlzLl9tZXNzYWdlLmVycm9yKCdUaGUgc2VtYW50aWMgZGF0YWJhc2UgYnVpbGQgY291bGQgbm90IHJ1biDigJQgY2hlY2sgdGhlIEFJIGNvbm5lY3Rpb24gYW5kIHRyeSBhZ2Fpbi4nKTtcbiAgICAgIHRoaXMuX3JlZnJlc2hTdGF0dXMoKTtcblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuX3JlcG9ydERhdGEuc2VtYW50aWNCdWlsZFN0YXR1cygpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChzdGF0dXM6IFNlbWFudGljQnVpbGRTdGF0dXMpID0+IHtcbiAgICAgICAgdGhpcy5zdGF0dXMuc2V0KHN0YXR1cyk7XG4gICAgICAgIHRoaXMuX2Fubm91bmNlT3V0Y29tZShzdGF0dXMpO1xuICAgICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVGVsbCB0aGUgb3BlcmF0b3IgaG93IHRoZSBidWlsZCBlbmRlZDogYSBmYWlsZWQvc3RvcHBlZCBidWlsZCBzdXJmYWNlcyBpdHNcbiAgICogcmVhc29uIChzbyBhbiBBSSBjb25uZWN0aW9uIHByb2JsZW0gaXMgbmV2ZXIgc2lsZW50KSwgYSBjb21wbGV0ZWQgb25lXG4gICAqIGNvbmZpcm1zIHN1Y2Nlc3MuXG4gICAqL1xuICBwcml2YXRlIF9hbm5vdW5jZU91dGNvbWUoc3RhdHVzOiBTZW1hbnRpY0J1aWxkU3RhdHVzKTogdm9pZCB7XG4gICAgY29uc3QgYnVpbGQgPSBzdGF0dXM/LmJ1aWxkO1xuICAgIGlmIChidWlsZD8uc3RhdGUgPT09ICdGJyB8fCBidWlsZD8uc3RhdGUgPT09ICdLJykge1xuICAgICAgdGhpcy5fbWVzc2FnZS5lcnJvcihidWlsZC5tZXNzYWdlIHx8ICdUaGUgc2VtYW50aWMgZGF0YWJhc2UgYnVpbGQgZGlkIG5vdCBmaW5pc2guJyk7XG4gICAgfSBlbHNlIGlmIChidWlsZD8uc3RhdGUgPT09ICdDJykge1xuICAgICAgdGhpcy5fbWVzc2FnZS5zdWNjZXNzKCdTZW1hbnRpYyBkYXRhYmFzZSBidWlsZCBjb21wbGV0ZS4nKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgc3RhZ2VDb2xvcihzdGFnZTogU2VtYW50aWNCdWlsZFN0YWdlKTogc3RyaW5nIHtcbiAgICByZXR1cm4gc3RhZ2UgPT09ICdkb25lJ1xuICAgICAgPyBTZW1hbnRpY0RhdGFiYXNlQ29tcG9uZW50LkRPTkVfQ09MT1JcbiAgICAgIDogU2VtYW50aWNEYXRhYmFzZUNvbXBvbmVudC5QRU5ESU5HX0NPTE9SO1xuICB9XG5cbiAgcHVibGljIHN0YWdlVG9vbHRpcChzdGFnZTogU2VtYW50aWNCdWlsZFN0YWdlKTogc3RyaW5nIHtcbiAgICByZXR1cm4gc3RhZ2UgPT09ICdkb25lJyA/ICdCdWlsdCcgOiAnTmVlZHMgYnVpbGRpbmcnO1xuICB9XG5cbiAgLyoqXG4gICAqIE9wZW4gdGhlIGNoaWxkIGRpYWxvZyBsaXN0aW5nIG9uZSBncm91cCBvZiBhbiBlbnRpdHkncyBjYWNoZWQgZnVuY3Rpb25zIOKAlFxuICAgKiB0aGUgbW9kZWwgY2xhc3MncyBvciB0aGUgaGFuZGxlciBjbGFzcydzLiBUaGUgZGlhbG9nIGZldGNoZXMgZnJlc2ggb24gb3BlbixcbiAgICogc28gdGhlIHRhYiBuZXZlciBob2xkcyBmdW5jdGlvbiBwYXlsb2Fkcy5cbiAgICovXG4gIHB1YmxpYyBvcGVuRnVuY3Rpb25zKGVudGl0eTogU2VtYW50aWNCdWlsZEVudGl0eSwgc291cmNlOiAnbW9kZWwnIHwgJ2hhbmRsZXInKTogdm9pZCB7XG4gICAgdGhpcy5fZGlhbG9nLm9wZW4oRW50aXR5RnVuY3Rpb25zQ29tcG9uZW50LCB7XG4gICAgICB3aWR0aDogJzcwMHB4JyxcbiAgICAgIG1heFdpZHRoOiAnOTV2dycsXG4gICAgICBtYXhIZWlnaHQ6ICc4NXZoJyxcbiAgICAgIGF1dG9Gb2N1czogZmFsc2UsXG4gICAgICBkYXRhOiB7IGVudGl0eUtleTogZW50aXR5LmtleSwgc291cmNlIH0sXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIF9pbml0KCk6IHZvaWQge1xuICAgIHRoaXMuX3JlZnJlc2hTdGF0dXMoKTtcbiAgfVxuXG4gIHByaXZhdGUgX3JlZnJlc2hTdGF0dXMoKTogdm9pZCB7XG4gICAgdGhpcy5fcmVwb3J0RGF0YS5zZW1hbnRpY0J1aWxkU3RhdHVzKClcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKHN0YXR1czogU2VtYW50aWNCdWlsZFN0YXR1cykgPT4gdGhpcy5zdGF0dXMuc2V0KHN0YXR1cykpO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiZnMtY29sdW1uIHRhYi1ib2R5XCI+XG4gIDxwIGNsYXNzPVwiaGludFwiPlxuICAgIFRoZSBzZW1hbnRpYyBkYXRhYmFzZSBpcyB3aGF0IHRoZSByZXBvcnQgQUkgdXNlcyB0byB1bmRlcnN0YW5kIHlvdXIgZGF0YSDigJRcbiAgICB0YWJsZXMsIHJlbGF0aW9uc2hpcHMgYW5kIHRoZSBjYWxjdWxhdGlvbnMgaW4gY29kZS4gUmVidWlsZCBpdCBhZnRlclxuICAgIHNpZ25pZmljYW50IGFwcGxpY2F0aW9uIHVwZGF0ZXMuIFRoZSBidWlsZCBydW5zIGluIHRoZSBiYWNrZ3JvdW5kOyBwcm9ncmVzc1xuICAgIGFwcGVhcnMgaW4gdGhlIGRvY2sgYXQgdGhlIGJvdHRvbSByaWdodCBhbmQgdGhpcyBkaWFsb2cgY2FuIGJlIGNsb3NlZCB3aGlsZVxuICAgIGl0IHJ1bnMuXG4gIDwvcD5cblxuICA8ZGl2ICpmc1NrZWxldG9uPVwic3RhdHVzKClcIj5cbiAgICBAaWYgKHN0YXR1cygpOyBhcyBidWlsZFN0YXR1cykge1xuICAgICAgPGRpdiBjbGFzcz1cImJ1aWxkLXN0YXR1c1wiPlxuICAgICAgICBAaWYgKGJ1aWxkU3RhdHVzLmRldGFpbC5nZW5lcmF0ZWRBdCkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGF0XCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxhYmVsXCI+TGFzdCBidWlsdDwvc3Bhbj5cbiAgICAgICAgICAgIDxmcy1kYXRlXG4gICAgICAgICAgICAgICAgW2RhdGVdPVwiYnVpbGRTdGF0dXMuZGV0YWlsLmdlbmVyYXRlZEF0XCJcbiAgICAgICAgICAgICAgICBmb3JtYXQ9XCJkYXRlLXRpbWVcIj5cbiAgICAgICAgICAgIDwvZnMtZGF0ZT5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhdFwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsYWJlbFwiPkNvdmVyYWdlPC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+XG4gICAgICAgICAgICAgIHt7IGJ1aWxkU3RhdHVzLmRldGFpbC5lbnRpdHlDb3VudCB9fSBlbnRpdGllcyDCt1xuICAgICAgICAgICAgICB7eyBidWlsZFN0YXR1cy5kZXRhaWwuZnVuY3Rpb25Db3VudCB9fSBjb2RlIGZ1bmN0aW9uc1xuICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICA8ZnMtbWVzc2FnZS13YXJuaW5nPlxuICAgICAgICAgICAgVGhlIHNlbWFudGljIGRhdGFiYXNlIGhhcyBub3QgYmVlbiBidWlsdCB5ZXQg4oCUIHRoZSByZXBvcnQgQUkgY2FuJ3RcbiAgICAgICAgICAgIGFuc3dlciBkYXRhIHF1ZXN0aW9ucyB1bnRpbCBpdCBpcy4gUnVuIHRoZSBmaXJzdCBidWlsZC5cbiAgICAgICAgICA8L2ZzLW1lc3NhZ2Utd2FybmluZz5cbiAgICAgICAgfVxuICAgICAgICBAaWYgKGJ1aWxkU3RhdHVzLmJ1aWxkKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cInN0YXRcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5MYXN0IGJ1aWxkIHJ1bjwvc3Bhbj5cbiAgICAgICAgICAgIDxzcGFuPlxuICAgICAgICAgICAgICB7eyBidWlsZFN0YXRlTGFiZWwoKSB9fVxuICAgICAgICAgICAgICBAaWYgKGJ1aWxkU3RhdHVzLmJ1aWxkLm1lc3NhZ2UpIHtcbiAgICAgICAgICAgICAgICDigJQge3sgYnVpbGRTdGF0dXMuYnVpbGQubWVzc2FnZSB9fVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8IS0tIFRoZSBlbnRpdHkgc2NvcmVib2FyZDogZXZlcnkgZW50aXR5IHRoZSBBSSBrbm93cyBhYm91dCAoYW4gZW50aXR5ID1cbiAgICAgICAgICAgYSB0YWJsZSArIGEgbW9kZWwgKyBhIGhhbmRsZXIpLCBpdHMgYnVpbHQtdnMtbmVlZHMtYnVpbGRpbmcgc3RhZ2VzLFxuICAgICAgICAgICBhbmQgYSBkcmlsbC1kb3duIGludG8gaXRzIHNoYXBlLCBlbnVtIHZhbHVlcywgY29tcHV0ZWQgZmllbGRzIGFuZFxuICAgICAgICAgICBjYWNoZWQgZnVuY3Rpb24gY291bnRzIOKAlCB0aGUgY291bnRzIGxpbmsgaW50byB0aGUgZnVuY3Rpb25zIGRpYWxvZy4gLS0+XG4gICAgICBAaWYgKGJ1aWxkU3RhdHVzLmRldGFpbC5lbnRpdGllcz8ubGVuZ3RoKSB7XG4gICAgICAgIDxoMj5FbnRpdGllczwvaDI+XG4gICAgICAgIDxtYXQtYWNjb3JkaW9uIGNsYXNzPVwiZW50aXRpZXNcIj5cbiAgICAgICAgICBAZm9yIChlbnRpdHkgb2YgYnVpbGRTdGF0dXMuZGV0YWlsLmVudGl0aWVzOyB0cmFjayBlbnRpdHkua2V5KSB7XG4gICAgICAgICAgICA8bWF0LWV4cGFuc2lvbi1wYW5lbD5cbiAgICAgICAgICAgICAgPG1hdC1leHBhbnNpb24tcGFuZWwtaGVhZGVyPlxuICAgICAgICAgICAgICAgIDxtYXQtcGFuZWwtdGl0bGU+e3sgZW50aXR5LmtleSB9fTwvbWF0LXBhbmVsLXRpdGxlPlxuICAgICAgICAgICAgICAgIDxtYXQtcGFuZWwtZGVzY3JpcHRpb24+XG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhZ2UtY2hpcHNcIj5cbiAgICAgICAgICAgICAgICAgICAgQGZvciAoc3RhZ2Ugb2Ygc3RhZ2VLZXlzOyB0cmFjayBzdGFnZSkge1xuICAgICAgICAgICAgICAgICAgICAgIDxmcy1jaGlwXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9XCJ0aW55XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW2JhY2tncm91bmRDb2xvcl09XCJzdGFnZUNvbG9yKGVudGl0eS5zdGFnZXNbc3RhZ2VdKVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPVwiI2ZmZmZmZlwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFttYXRUb29sdGlwXT1cInN0YWdlVG9vbHRpcChlbnRpdHkuc3RhZ2VzW3N0YWdlXSlcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHN0YWdlTGFiZWxzW3N0YWdlXSB9fVxuICAgICAgICAgICAgICAgICAgICAgIDwvZnMtY2hpcD5cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPC9tYXQtcGFuZWwtZGVzY3JpcHRpb24+XG4gICAgICAgICAgICAgIDwvbWF0LWV4cGFuc2lvbi1wYW5lbC1oZWFkZXI+XG5cbiAgICAgICAgICAgICAgPCEtLSBEZWZlcnJlZDogdGhlIGJvZHkgb25seSBleGlzdHMgb25jZSBleHBhbmRlZC4gLS0+XG4gICAgICAgICAgICAgIDxuZy10ZW1wbGF0ZSBtYXRFeHBhbnNpb25QYW5lbENvbnRlbnQ+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImVudGl0eS1kZXRhaWxcIj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGF0XCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5UYWJsZTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgZW50aXR5LnRhYmxlIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhdFwiPlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxhYmVsXCI+Q29sdW1uczwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgZW50aXR5LmNvbHVtbnMgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGF0XCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5SZWxhdGlvbnNoaXBzPC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICA8c3Bhbj57eyBlbnRpdHkucmVsYXRpb25zaGlwcyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgQGlmIChlbnRpdHkuZW51bXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdGF0XCI+XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsYWJlbFwiPkVudW1lcmF0aW9uIHZhbHVlczwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW51bS1saXN0XCI+XG4gICAgICAgICAgICAgICAgICAgICAgICBAZm9yIChlbnVtQ29sdW1uIG9mIGVudGl0eS5lbnVtczsgdHJhY2sgZW51bUNvbHVtbi5jb2x1bW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImVudW0tcm93XCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJlbnVtLWNvbHVtblwiPnt7IGVudW1Db2x1bW4uY29sdW1uIH19PC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuPnt7IGVudW1Db2x1bW4udmFsdWVzLmpvaW4oJywgJykgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIEBpZiAoZW50aXR5LmNvbXB1dGVkRmllbGRzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhdFwiPlxuICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5Db21wdXRlZCBmaWVsZHM8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgZW50aXR5LmNvbXB1dGVkRmllbGRzLmpvaW4oJywgJykgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInN0YXRcIj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsYWJlbFwiPk1vZGVsIGZ1bmN0aW9uczwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgQGlmIChlbnRpdHkubW9kZWxGdW5jdGlvbkNvdW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgPGFcbiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJjb3VudC1saW5rXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9wZW5GdW5jdGlvbnMoZW50aXR5LCAnbW9kZWwnKVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgZW50aXR5Lm1vZGVsRnVuY3Rpb25Db3VudCB9fVxuICAgICAgICAgICAgICAgICAgICAgIDwvYT5cbiAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4+MDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RhdFwiPlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxhYmVsXCI+SGFuZGxlciBmdW5jdGlvbnM8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIEBpZiAoZW50aXR5LmhhbmRsZXJGdW5jdGlvbkNvdW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgPGFcbiAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJjb3VudC1saW5rXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9wZW5GdW5jdGlvbnMoZW50aXR5LCAnaGFuZGxlcicpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7eyBlbnRpdHkuaGFuZGxlckZ1bmN0aW9uQ291bnQgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L2E+XG4gICAgICAgICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgIDxzcGFuPjA8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgICAgPC9tYXQtZXhwYW5zaW9uLXBhbmVsPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9tYXQtYWNjb3JkaW9uPlxuICAgICAgfVxuICAgIH1cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==