@pipeline-builder/pipeline-data 4.1.23 → 4.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/alert-destinations.d.ts +17 -0
- package/lib/api/alert-destinations.js +36 -0
- package/lib/api/reporting/analytics-queries.d.ts +274 -0
- package/lib/api/reporting/analytics-queries.js +598 -0
- package/lib/api/reporting/daily-rollup.d.ts +41 -0
- package/lib/api/reporting/daily-rollup.js +173 -0
- package/lib/api/reporting/deploy-rollup.d.ts +78 -0
- package/lib/api/reporting/deploy-rollup.js +203 -0
- package/lib/api/reporting/dora-scoring.d.ts +14 -0
- package/lib/api/reporting/dora-scoring.js +25 -1
- package/lib/api/reporting/dora.js +30 -6
- package/lib/api/reporting/failure-classifier.d.ts +54 -0
- package/lib/api/reporting/failure-classifier.js +179 -0
- package/lib/api/reporting/ingest.d.ts +2 -2
- package/lib/api/reporting/ingest.js +129 -4
- package/lib/api/reporting/last-deploy.d.ts +31 -0
- package/lib/api/reporting/last-deploy.js +93 -0
- package/lib/api/reporting/needs-attention.d.ts +93 -0
- package/lib/api/reporting/needs-attention.js +152 -0
- package/lib/api/reporting/plugin-exposure.d.ts +129 -0
- package/lib/api/reporting/plugin-exposure.js +312 -0
- package/lib/api/reporting/retention-sweep.js +9 -1
- package/lib/api/reporting/sql-helpers.d.ts +35 -2
- package/lib/api/reporting/sql-helpers.js +39 -1
- package/lib/api/reporting/stakeholder/compose.d.ts +115 -0
- package/lib/api/reporting/stakeholder/compose.js +234 -0
- package/lib/api/reporting/stakeholder/index.d.ts +14 -0
- package/lib/api/reporting/stakeholder/index.js +17 -0
- package/lib/api/reporting/stakeholder/period.d.ts +102 -0
- package/lib/api/reporting/stakeholder/period.js +273 -0
- package/lib/api/reporting/stakeholder/report-html.d.ts +93 -0
- package/lib/api/reporting/stakeholder/report-html.js +208 -0
- package/lib/api/reporting/stakeholder/sections.d.ts +117 -0
- package/lib/api/reporting/stakeholder/sections.js +249 -0
- package/lib/api/reporting/stakeholder/store.d.ts +450 -0
- package/lib/api/reporting/stakeholder/store.js +981 -0
- package/lib/api/reporting/stakeholder/templates.d.ts +21 -0
- package/lib/api/reporting/stakeholder/templates.js +104 -0
- package/lib/api/reporting/types.d.ts +73 -0
- package/lib/api/reporting/types.js +1 -1
- package/lib/api/reporting-service.d.ts +94 -5
- package/lib/api/reporting-service.js +169 -10
- package/lib/api/soft-delete-sweep.d.ts +11 -2
- package/lib/api/soft-delete-sweep.js +17 -6
- package/lib/database/drizzle-schema.d.ts +2263 -385
- package/lib/database/drizzle-schema.js +18 -7
- package/lib/database/index.d.ts +1 -1
- package/lib/database/index.js +4 -2
- package/lib/database/schema/compliance.d.ts +0 -682
- package/lib/database/schema/compliance.js +6 -66
- package/lib/database/schema/ecosystem.d.ts +20 -157
- package/lib/database/schema/ecosystem.js +23 -16
- package/lib/database/schema/pipeline.d.ts +200 -0
- package/lib/database/schema/pipeline.js +100 -1
- package/lib/database/schema/plugin.d.ts +20 -1
- package/lib/database/schema/plugin.js +13 -1
- package/lib/database/schema/reporting-analytics.d.ts +1212 -0
- package/lib/database/schema/reporting-analytics.js +250 -0
- package/lib/database/schema/reporting-stakeholder.d.ts +1405 -0
- package/lib/database/schema/reporting-stakeholder.js +251 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +15 -1
- package/lib/testing/exports.json +67 -4
- package/package.json +4 -3
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* The queries the bespoke report sections read.
|
|
5
|
+
*
|
|
6
|
+
* All of them go through `execution_daily_rollups`, `dora_deployments`,
|
|
7
|
+
* `pipeline_plugin_resolution` or `plugin_vuln_exposure` — never raw
|
|
8
|
+
* `pipeline_events`. That is the whole point of those tables: a quarterly report
|
|
9
|
+
* over a busy org costs a few hundred rows, and keeps working after retention has
|
|
10
|
+
* swept the events.
|
|
11
|
+
*
|
|
12
|
+
* TWO RULES EVERY QUERY HERE FOLLOWS:
|
|
13
|
+
*
|
|
14
|
+
* - NOTHING IS PER-PERSON. Every aggregate is per pipeline, per stage, per
|
|
15
|
+
* category or per environment. The section registry refuses a per-person id at
|
|
16
|
+
* registration; these queries make sure there is nothing to register.
|
|
17
|
+
* - A ZERO AND A BLIND SPOT ARE DIFFERENT. Where a number can be absent for lack
|
|
18
|
+
* of data rather than lack of activity, the shape carries that (`null`, a
|
|
19
|
+
* `coverage` count, an explicit reason) instead of collapsing to 0.
|
|
20
|
+
*/
|
|
21
|
+
import { and, eq, gte, inArray, lt, sql } from 'drizzle-orm';
|
|
22
|
+
import { FAILURE_CATEGORY_LABELS, isTeamActionable } from './failure-classifier.js';
|
|
23
|
+
import { schema } from '../../database/drizzle-schema.js';
|
|
24
|
+
import { withTenantTx } from '../../database/tenancy.js';
|
|
25
|
+
/** The org predicate: one org, or a rollup set. */
|
|
26
|
+
function orgPredicate(col, scope) {
|
|
27
|
+
return scope.orgIds && scope.orgIds.length > 0
|
|
28
|
+
? inArray(col, scope.orgIds)
|
|
29
|
+
: eq(col, scope.orgId);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Per-pipeline delivery, worst first.
|
|
33
|
+
*
|
|
34
|
+
* WORST FIRST, and capped, because the point of the section is "where should
|
|
35
|
+
* attention go". A list sorted by name, or an unbounded one, is a data dump: the
|
|
36
|
+
* lead still has to find the problem, which is the work the report was supposed to
|
|
37
|
+
* do. Ties break on volume, so a 50%-success pipeline that ran twice does not
|
|
38
|
+
* outrank one that ran two hundred times.
|
|
39
|
+
*
|
|
40
|
+
* `successPct` is NULL for a pipeline with no runs in the window — not 0. A
|
|
41
|
+
* pipeline nobody ran is not a pipeline that failed.
|
|
42
|
+
*/
|
|
43
|
+
export async function getPipelineBreakdown(scope, limit = 10) {
|
|
44
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
45
|
+
WITH totals AS (
|
|
46
|
+
SELECT
|
|
47
|
+
r.pipeline_id,
|
|
48
|
+
SUM(r.runs)::int AS runs,
|
|
49
|
+
SUM(r.succeeded)::int AS succeeded,
|
|
50
|
+
SUM(r.failed)::int AS failed,
|
|
51
|
+
MAX(r.p95_ms)::int AS p95_ms,
|
|
52
|
+
SUM(r.build_seconds)::int AS build_seconds
|
|
53
|
+
FROM execution_daily_rollups r
|
|
54
|
+
WHERE r.org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
55
|
+
AND r.day >= ${scope.from}::timestamptz AND r.day < ${scope.to}::timestamptz
|
|
56
|
+
-- The pipeline-level grain only: summing the stage rows too would count
|
|
57
|
+
-- every run once per stage.
|
|
58
|
+
AND r.stage_name = ''
|
|
59
|
+
GROUP BY r.pipeline_id
|
|
60
|
+
),
|
|
61
|
+
deploys AS (
|
|
62
|
+
SELECT pipeline_id, COUNT(*)::int AS deploys
|
|
63
|
+
FROM dora_deployments
|
|
64
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
65
|
+
AND deployed_at >= ${scope.from}::timestamptz AND deployed_at < ${scope.to}::timestamptz
|
|
66
|
+
AND succeeded = true
|
|
67
|
+
GROUP BY pipeline_id
|
|
68
|
+
),
|
|
69
|
+
last_fail AS (
|
|
70
|
+
-- The most recent failing STAGE row per pipeline: what to look at first.
|
|
71
|
+
SELECT DISTINCT ON (pipeline_id) pipeline_id, day, stage_name
|
|
72
|
+
FROM execution_daily_rollups
|
|
73
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
74
|
+
AND day >= ${scope.from}::timestamptz AND day < ${scope.to}::timestamptz
|
|
75
|
+
AND stage_name <> '' AND failed > 0
|
|
76
|
+
ORDER BY pipeline_id, day DESC, failed DESC
|
|
77
|
+
)
|
|
78
|
+
SELECT
|
|
79
|
+
t.pipeline_id,
|
|
80
|
+
p.pipeline_name,
|
|
81
|
+
p.project,
|
|
82
|
+
t.runs, t.succeeded, t.failed, t.p95_ms, t.build_seconds,
|
|
83
|
+
COALESCE(d.deploys, 0) AS deploys,
|
|
84
|
+
f.day AS last_failure_at,
|
|
85
|
+
f.stage_name AS last_failure_stage,
|
|
86
|
+
CASE WHEN t.runs > 0 THEN ROUND((t.succeeded::numeric / t.runs) * 100, 1) ELSE NULL END AS success_pct
|
|
87
|
+
FROM totals t
|
|
88
|
+
LEFT JOIN deploys d ON d.pipeline_id = t.pipeline_id
|
|
89
|
+
LEFT JOIN last_fail f ON f.pipeline_id = t.pipeline_id
|
|
90
|
+
LEFT JOIN pipelines p ON p.id::text = t.pipeline_id
|
|
91
|
+
-- Worst first: lowest success rate, then most runs, so a 50% pipeline that ran
|
|
92
|
+
-- twice does not outrank one that ran two hundred times.
|
|
93
|
+
ORDER BY success_pct ASC NULLS LAST, t.runs DESC
|
|
94
|
+
LIMIT ${limit}
|
|
95
|
+
`));
|
|
96
|
+
return rowsOf(rows).map((r) => ({
|
|
97
|
+
pipelineId: String(r.pipeline_id),
|
|
98
|
+
pipelineName: r.pipeline_name ?? null,
|
|
99
|
+
project: r.project ?? null,
|
|
100
|
+
runs: num(r.runs),
|
|
101
|
+
succeeded: num(r.succeeded),
|
|
102
|
+
failed: num(r.failed),
|
|
103
|
+
successPct: r.success_pct === null ? null : Number(r.success_pct),
|
|
104
|
+
p95Ms: r.p95_ms === null ? null : num(r.p95_ms),
|
|
105
|
+
deploys: num(r.deploys),
|
|
106
|
+
lastFailureAt: isoOrNull(r.last_failure_at),
|
|
107
|
+
lastFailureStage: r.last_failure_stage ?? null,
|
|
108
|
+
buildSeconds: num(r.build_seconds),
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Failures by category, with the previous period's counts for the trend.
|
|
113
|
+
*
|
|
114
|
+
* SPLITS ACTIONABLE FROM NOT. A week whose failures were all cloud throttling is a
|
|
115
|
+
* different conversation from one whose failures were all broken tests, and
|
|
116
|
+
* presenting them in one ranked list invites exactly the wrong conclusion — a team
|
|
117
|
+
* being asked to fix an AWS capacity problem.
|
|
118
|
+
*/
|
|
119
|
+
export async function getFailureAnalysis(scope, previous) {
|
|
120
|
+
const counts = await categoryCounts(scope);
|
|
121
|
+
const prior = previous ? await categoryCounts({ ...scope, ...previous }) : null;
|
|
122
|
+
const total = [...counts.values()].reduce((s, v) => s + v.count, 0);
|
|
123
|
+
const topByCategory = await topPipelinesByCategory(scope);
|
|
124
|
+
const categories = [...counts.entries()]
|
|
125
|
+
.map(([category, v]) => ({
|
|
126
|
+
category,
|
|
127
|
+
label: FAILURE_CATEGORY_LABELS[category] ?? category,
|
|
128
|
+
count: v.count,
|
|
129
|
+
sharePct: total > 0 ? Number(((v.count / total) * 100).toFixed(1)) : 0,
|
|
130
|
+
teamActionable: isTeamActionable(category),
|
|
131
|
+
change: prior ? v.count - (prior.get(category)?.count ?? 0) : null,
|
|
132
|
+
topPipelines: topByCategory.get(category) ?? [],
|
|
133
|
+
}))
|
|
134
|
+
.sort((a, b) => b.count - a.count);
|
|
135
|
+
return {
|
|
136
|
+
categories,
|
|
137
|
+
total,
|
|
138
|
+
actionableTotal: categories.filter((c) => c.teamActionable).reduce((s, c) => s + c.count, 0),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function categoryCounts(scope) {
|
|
142
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
143
|
+
SELECT key AS category, SUM(value::int)::int AS count
|
|
144
|
+
FROM execution_daily_rollups r, jsonb_each(r.failures_by_category)
|
|
145
|
+
WHERE r.org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
146
|
+
AND r.day >= ${scope.from}::timestamptz AND r.day < ${scope.to}::timestamptz
|
|
147
|
+
AND r.stage_name = ''
|
|
148
|
+
GROUP BY key
|
|
149
|
+
`));
|
|
150
|
+
const out = new Map();
|
|
151
|
+
for (const r of rowsOf(rows))
|
|
152
|
+
out.set(r.category, { count: num(r.count) });
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
async function topPipelinesByCategory(scope, perCategory = 3) {
|
|
156
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
157
|
+
SELECT category, pipeline_id, pipeline_name, count FROM (
|
|
158
|
+
SELECT
|
|
159
|
+
e.key AS category,
|
|
160
|
+
r.pipeline_id,
|
|
161
|
+
p.pipeline_name,
|
|
162
|
+
SUM(e.value::int)::int AS count,
|
|
163
|
+
ROW_NUMBER() OVER (PARTITION BY e.key ORDER BY SUM(e.value::int) DESC) AS rn
|
|
164
|
+
-- CROSS JOIN LATERAL, not a comma join. With FROM a, f(a.x) e LEFT JOIN b,
|
|
165
|
+
-- the LEFT JOIN binds to e alone and r falls out of scope in its ON clause
|
|
166
|
+
-- ("invalid reference to FROM-clause entry"). The explicit lateral keeps all
|
|
167
|
+
-- three in one join tree.
|
|
168
|
+
FROM execution_daily_rollups r
|
|
169
|
+
CROSS JOIN LATERAL jsonb_each(r.failures_by_category) e
|
|
170
|
+
LEFT JOIN pipelines p ON p.id::text = r.pipeline_id
|
|
171
|
+
WHERE r.org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
172
|
+
AND r.day >= ${scope.from}::timestamptz AND r.day < ${scope.to}::timestamptz
|
|
173
|
+
AND r.stage_name = ''
|
|
174
|
+
GROUP BY e.key, r.pipeline_id, p.pipeline_name
|
|
175
|
+
) ranked
|
|
176
|
+
WHERE rn <= ${perCategory}
|
|
177
|
+
`));
|
|
178
|
+
const out = new Map();
|
|
179
|
+
for (const r of rowsOf(rows)) {
|
|
180
|
+
const key = r.category;
|
|
181
|
+
const list = out.get(key) ?? [];
|
|
182
|
+
list.push({
|
|
183
|
+
pipelineId: String(r.pipeline_id),
|
|
184
|
+
pipelineName: r.pipeline_name ?? null,
|
|
185
|
+
count: num(r.count),
|
|
186
|
+
});
|
|
187
|
+
out.set(key, list);
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Per-stage timings for one pipeline, slowest first.
|
|
193
|
+
*
|
|
194
|
+
* The section a lead opens after the breakdown says a pipeline got slower: "which
|
|
195
|
+
* stage" is the next question, and answering it from raw events would mean a
|
|
196
|
+
* second scan of the same window.
|
|
197
|
+
*/
|
|
198
|
+
export async function getStagePerformance(scope, pipelineId, previous) {
|
|
199
|
+
const current = await stageRows(scope, pipelineId);
|
|
200
|
+
const prior = previous
|
|
201
|
+
? new Map((await stageRows({ ...scope, ...previous }, pipelineId)).map((s) => [s.stageName, s]))
|
|
202
|
+
: new Map();
|
|
203
|
+
return current.map((s) => {
|
|
204
|
+
const before = prior.get(s.stageName)?.p95Ms ?? null;
|
|
205
|
+
return {
|
|
206
|
+
...s,
|
|
207
|
+
p95ChangePct: before && before > 0 && s.p95Ms !== null
|
|
208
|
+
? Number((((s.p95Ms - before) / before) * 100).toFixed(1))
|
|
209
|
+
: null,
|
|
210
|
+
};
|
|
211
|
+
}).sort((a, b) => (b.p95Ms ?? 0) - (a.p95Ms ?? 0));
|
|
212
|
+
}
|
|
213
|
+
async function stageRows(scope, pipelineId) {
|
|
214
|
+
const rows = await withTenantTx((tx) => tx.select({
|
|
215
|
+
stageName: schema.executionDailyRollup.stageName,
|
|
216
|
+
runs: sql `SUM(${schema.executionDailyRollup.runs})::int`,
|
|
217
|
+
failed: sql `SUM(${schema.executionDailyRollup.failed})::int`,
|
|
218
|
+
// MAX of the daily p95s, not an average of percentiles: averaging
|
|
219
|
+
// percentiles is not a percentile of anything, and the worst day is the
|
|
220
|
+
// number somebody would act on.
|
|
221
|
+
p50Ms: sql `MAX(${schema.executionDailyRollup.p50Ms})::int`,
|
|
222
|
+
p95Ms: sql `MAX(${schema.executionDailyRollup.p95Ms})::int`,
|
|
223
|
+
}).from(schema.executionDailyRollup)
|
|
224
|
+
.where(and(orgPredicate(schema.executionDailyRollup.orgId, scope), eq(schema.executionDailyRollup.pipelineId, pipelineId), gte(schema.executionDailyRollup.day, new Date(scope.from)), lt(schema.executionDailyRollup.day, new Date(scope.to)), sql `${schema.executionDailyRollup.stageName} <> ''`))
|
|
225
|
+
.groupBy(schema.executionDailyRollup.stageName));
|
|
226
|
+
const map = new Map();
|
|
227
|
+
for (const r of rows) {
|
|
228
|
+
map.set(String(r.stageName), {
|
|
229
|
+
stageName: String(r.stageName),
|
|
230
|
+
runs: num(r.runs),
|
|
231
|
+
failed: num(r.failed),
|
|
232
|
+
p50Ms: r.p50Ms === null ? null : num(r.p50Ms),
|
|
233
|
+
p95Ms: r.p95Ms === null ? null : num(r.p95Ms),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return [...map.values()];
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Where the build time went.
|
|
240
|
+
*
|
|
241
|
+
* SECONDS, NOT DOLLARS. A cost figure needs the instance types, the spot/on-demand
|
|
242
|
+
* mix and the account's own discounts, none of which this platform knows — so a
|
|
243
|
+
* dollar number here would be a guess presented as an invoice. Build time is the
|
|
244
|
+
* thing actually measured.
|
|
245
|
+
*/
|
|
246
|
+
export async function getResourceConsumption(scope, limit = 20) {
|
|
247
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
248
|
+
SELECT
|
|
249
|
+
p.project,
|
|
250
|
+
r.pipeline_id,
|
|
251
|
+
p.pipeline_name,
|
|
252
|
+
SUM(r.build_seconds)::int AS build_seconds,
|
|
253
|
+
SUM(r.runs)::int AS runs
|
|
254
|
+
FROM execution_daily_rollups r
|
|
255
|
+
LEFT JOIN pipelines p ON p.id::text = r.pipeline_id
|
|
256
|
+
WHERE r.org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
257
|
+
AND r.day >= ${scope.from}::timestamptz AND r.day < ${scope.to}::timestamptz
|
|
258
|
+
AND r.stage_name = ''
|
|
259
|
+
GROUP BY p.project, r.pipeline_id, p.pipeline_name
|
|
260
|
+
ORDER BY build_seconds DESC
|
|
261
|
+
LIMIT ${limit}
|
|
262
|
+
`));
|
|
263
|
+
const list = rowsOf(rows);
|
|
264
|
+
const total = list.reduce((s, r) => s + num(r.build_seconds), 0);
|
|
265
|
+
return {
|
|
266
|
+
totalBuildSeconds: total,
|
|
267
|
+
pipelines: list.map((r) => ({
|
|
268
|
+
project: r.project ?? null,
|
|
269
|
+
pipelineId: String(r.pipeline_id),
|
|
270
|
+
pipelineName: r.pipeline_name ?? null,
|
|
271
|
+
buildSeconds: num(r.build_seconds),
|
|
272
|
+
runs: num(r.runs),
|
|
273
|
+
sharePct: total > 0 ? Number(((num(r.build_seconds) / total) * 100).toFixed(1)) : 0,
|
|
274
|
+
})),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Where commits got to, and where they stopped.
|
|
279
|
+
*
|
|
280
|
+
* "Stuck" is the number worth reading. A commit that reached staging four days ago
|
|
281
|
+
* and never went further is work that is finished and not delivered — which does
|
|
282
|
+
* not appear in deployment frequency, success rate or lead time, because by every
|
|
283
|
+
* one of those measures nothing happened.
|
|
284
|
+
*/
|
|
285
|
+
export async function getPromotionView(scope, productionEnv = 'production', limit = 25) {
|
|
286
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
287
|
+
SELECT commit_sha, environment, MIN(deployed_at) AS deployed_at
|
|
288
|
+
FROM dora_deployments
|
|
289
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
290
|
+
AND deployed_at >= ${scope.from}::timestamptz AND deployed_at < ${scope.to}::timestamptz
|
|
291
|
+
AND succeeded = true
|
|
292
|
+
AND commit_sha IS NOT NULL
|
|
293
|
+
GROUP BY commit_sha, environment
|
|
294
|
+
ORDER BY MIN(deployed_at) DESC
|
|
295
|
+
`));
|
|
296
|
+
const byCommit = new Map();
|
|
297
|
+
for (const r of rowsOf(rows)) {
|
|
298
|
+
const sha = String(r.commit_sha);
|
|
299
|
+
const list = byCommit.get(sha) ?? [];
|
|
300
|
+
list.push({ environment: String(r.environment), deployedAt: new Date(String(r.deployed_at)) });
|
|
301
|
+
byCommit.set(sha, list);
|
|
302
|
+
}
|
|
303
|
+
const commits = [...byCommit.entries()].map(([commitSha, raw]) => {
|
|
304
|
+
const ordered = [...raw].sort((a, b) => a.deployedAt.getTime() - b.deployedAt.getTime());
|
|
305
|
+
const stops = ordered.map((stop, i) => ({
|
|
306
|
+
environment: stop.environment,
|
|
307
|
+
deployedAt: stop.deployedAt.toISOString(),
|
|
308
|
+
hoursFromPrevious: i === 0
|
|
309
|
+
? null
|
|
310
|
+
: Number(((stop.deployedAt.getTime() - ordered[i - 1].deployedAt.getTime()) / 3600_000).toFixed(1)),
|
|
311
|
+
}));
|
|
312
|
+
const furthest = ordered[ordered.length - 1]?.environment ?? '';
|
|
313
|
+
return { commitSha, stops, furthest, stuck: furthest !== productionEnv };
|
|
314
|
+
})
|
|
315
|
+
.sort((a, b) => {
|
|
316
|
+
const aLast = a.stops[a.stops.length - 1]?.deployedAt ?? '';
|
|
317
|
+
const bLast = b.stops[b.stops.length - 1]?.deployedAt ?? '';
|
|
318
|
+
return bLast.localeCompare(aLast);
|
|
319
|
+
})
|
|
320
|
+
.slice(0, limit);
|
|
321
|
+
return { commits, stuckCount: commits.filter((c) => c.stuck).length };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Which pipelines are behind on a plugin, worst gap first.
|
|
325
|
+
*
|
|
326
|
+
* Reads the DECLARED side (`pipeline_plugin_resolution`). A pipeline's declared
|
|
327
|
+
* range and what it last actually ran can disagree — which is why the vulnerability
|
|
328
|
+
* section below reads the deployed side and says which it is showing.
|
|
329
|
+
*/
|
|
330
|
+
export async function getOutdatedPlugins(scope, limit = 50) {
|
|
331
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
332
|
+
SELECT
|
|
333
|
+
r.pipeline_id, p.pipeline_name, r.stage_name, r.step_name,
|
|
334
|
+
COALESCE(r.plugin_publisher || '/', '') || r.plugin_name AS plugin,
|
|
335
|
+
r.resolved_version, r.latest_version, r.version_gap, r.within_policy
|
|
336
|
+
FROM pipeline_plugin_resolution r
|
|
337
|
+
LEFT JOIN pipelines p ON p.id::text = r.pipeline_id
|
|
338
|
+
WHERE r.org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
339
|
+
AND (r.version_gap IN ('major', 'minor', 'patch') OR r.within_policy = false)
|
|
340
|
+
ORDER BY CASE r.version_gap WHEN 'major' THEN 0 WHEN 'minor' THEN 1 WHEN 'patch' THEN 2 ELSE 3 END,
|
|
341
|
+
r.pipeline_id
|
|
342
|
+
LIMIT ${limit}
|
|
343
|
+
`));
|
|
344
|
+
const list = rowsOf(rows).map((r) => ({
|
|
345
|
+
pipelineId: String(r.pipeline_id),
|
|
346
|
+
pipelineName: r.pipeline_name ?? null,
|
|
347
|
+
stageName: String(r.stage_name),
|
|
348
|
+
stepName: String(r.step_name),
|
|
349
|
+
plugin: String(r.plugin),
|
|
350
|
+
inUse: r.resolved_version ?? null,
|
|
351
|
+
latest: r.latest_version ?? null,
|
|
352
|
+
gap: r.version_gap ?? null,
|
|
353
|
+
withinPolicy: r.within_policy !== false,
|
|
354
|
+
}));
|
|
355
|
+
return {
|
|
356
|
+
rows: list,
|
|
357
|
+
behindPipelines: new Set(list.map((r) => r.pipelineId)).size,
|
|
358
|
+
majorBehind: list.filter((r) => r.gap === 'major').length,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Plugin vulnerabilities, per pipeline, Criticals first.
|
|
363
|
+
*
|
|
364
|
+
* SAYS WHETHER IT IS DECLARED OR DEPLOYED. A pipeline whose config was updated to a
|
|
365
|
+
* fixed version but which has not re-synthed is still running the vulnerable image,
|
|
366
|
+
* and a report that collapsed the two would tell a manager the problem is solved
|
|
367
|
+
* while the old image keeps running.
|
|
368
|
+
*/
|
|
369
|
+
export async function getPluginVulnerabilities(scope, limit = 50) {
|
|
370
|
+
const now = new Date();
|
|
371
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
372
|
+
SELECT
|
|
373
|
+
v.pipeline_id, p.pipeline_name,
|
|
374
|
+
COALESCE(v.plugin_publisher || '/', '') || v.plugin_name AS plugin,
|
|
375
|
+
v.plugin_version, v.source, v.critical_count, v.high_count, v.top_findings, v.flagged_at
|
|
376
|
+
FROM plugin_vuln_exposure v
|
|
377
|
+
LEFT JOIN pipelines p ON p.id::text = v.pipeline_id
|
|
378
|
+
WHERE v.org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
379
|
+
AND v.fixed_at IS NULL
|
|
380
|
+
-- An acceptance past its deadline is not an acceptance.
|
|
381
|
+
AND (v.triage_state NOT IN ('accepted', 'false_positive')
|
|
382
|
+
OR (v.triage_state = 'accepted' AND v.accepted_until IS NOT NULL AND v.accepted_until <= ${now}))
|
|
383
|
+
ORDER BY v.critical_count DESC, v.high_count DESC
|
|
384
|
+
LIMIT ${limit}
|
|
385
|
+
`));
|
|
386
|
+
const list = rowsOf(rows).map((r) => ({
|
|
387
|
+
pipelineId: String(r.pipeline_id),
|
|
388
|
+
pipelineName: r.pipeline_name ?? null,
|
|
389
|
+
plugin: String(r.plugin),
|
|
390
|
+
version: String(r.plugin_version),
|
|
391
|
+
source: String(r.source),
|
|
392
|
+
critical: num(r.critical_count),
|
|
393
|
+
high: num(r.high_count),
|
|
394
|
+
findings: r.top_findings ?? [],
|
|
395
|
+
flaggedAt: isoOrNull(r.flagged_at) ?? new Date(0).toISOString(),
|
|
396
|
+
}));
|
|
397
|
+
return {
|
|
398
|
+
rows: list,
|
|
399
|
+
exposedPipelines: new Set(list.map((r) => r.pipelineId)).size,
|
|
400
|
+
criticalPipelines: new Set(list.filter((r) => r.critical > 0).map((r) => r.pipelineId)).size,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Adoption, coverage and creation source.
|
|
405
|
+
*
|
|
406
|
+
* `deployTrackedPipelines` is the honest denominator for everything DORA says: a
|
|
407
|
+
* pipeline that has not re-synthed with deploy tags produces no deploy rows, so it
|
|
408
|
+
* is invisible to deployment frequency — and reporting "4 deploys" without saying
|
|
409
|
+
* that 20 of 30 pipelines cannot be seen is the single most misleading thing this
|
|
410
|
+
* feature could do.
|
|
411
|
+
*/
|
|
412
|
+
export async function getAdoption(scope, timeSavedMinutes) {
|
|
413
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
414
|
+
WITH scoped AS (
|
|
415
|
+
SELECT id::text AS id, org_id, creation_source
|
|
416
|
+
FROM pipelines
|
|
417
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
418
|
+
AND deleted_at IS NULL
|
|
419
|
+
),
|
|
420
|
+
active AS (
|
|
421
|
+
SELECT DISTINCT pipeline_id, org_id
|
|
422
|
+
FROM execution_daily_rollups
|
|
423
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
424
|
+
AND day >= ${scope.from}::timestamptz AND day < ${scope.to}::timestamptz
|
|
425
|
+
AND stage_name = '' AND runs > 0
|
|
426
|
+
),
|
|
427
|
+
tracked AS (
|
|
428
|
+
SELECT DISTINCT pipeline_id
|
|
429
|
+
FROM dora_deployments
|
|
430
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
431
|
+
AND deployed_at >= ${scope.from}::timestamptz AND deployed_at < ${scope.to}::timestamptz
|
|
432
|
+
)
|
|
433
|
+
SELECT
|
|
434
|
+
(SELECT COUNT(*) FROM scoped)::int AS total_pipelines,
|
|
435
|
+
(SELECT COUNT(*) FROM active)::int AS active_pipelines,
|
|
436
|
+
(SELECT COUNT(DISTINCT org_id) FROM active)::int AS active_teams,
|
|
437
|
+
(SELECT COUNT(*) FROM tracked)::int AS deploy_tracked,
|
|
438
|
+
(SELECT COALESCE(jsonb_object_agg(COALESCE(creation_source, 'unknown'), n), '{}'::jsonb)
|
|
439
|
+
FROM (SELECT creation_source, COUNT(*)::int AS n FROM scoped GROUP BY creation_source) s
|
|
440
|
+
) AS by_source
|
|
441
|
+
`));
|
|
442
|
+
const r = rowsOf(rows)[0] ?? {};
|
|
443
|
+
const created = num(r.total_pipelines);
|
|
444
|
+
return {
|
|
445
|
+
totalPipelines: created,
|
|
446
|
+
activePipelines: num(r.active_pipelines),
|
|
447
|
+
activeTeams: Math.max(1, num(r.active_teams)),
|
|
448
|
+
deployTrackedPipelines: num(r.deploy_tracked),
|
|
449
|
+
bySource: r.by_source ?? {},
|
|
450
|
+
// Only when the ORG configured the assumption — a platform-supplied number
|
|
451
|
+
// here would be the platform marking its own homework.
|
|
452
|
+
estimatedMinutesSaved: timeSavedMinutes && timeSavedMinutes > 0 ? created * timeSavedMinutes : null,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Failure streaks and last-success dates, for the needs-attention rules.
|
|
457
|
+
*
|
|
458
|
+
* Reads the daily rollup, not `pipeline_events` — the same reason as everything
|
|
459
|
+
* else here: a streak that ended three weeks ago must still be readable after
|
|
460
|
+
* retention has swept the events that produced it.
|
|
461
|
+
*
|
|
462
|
+
* `lookbackDays` extends PAST the window on purpose. "Last succeeded 12 days ago"
|
|
463
|
+
* is the finding a weekly report most needs, and a query bounded by a 7-day window
|
|
464
|
+
* could never produce it.
|
|
465
|
+
*/
|
|
466
|
+
export async function getPipelineStreaks(scope, lookbackDays = 90) {
|
|
467
|
+
const since = new Date(new Date(scope.to).getTime() - lookbackDays * 86_400_000).toISOString();
|
|
468
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
469
|
+
WITH days AS (
|
|
470
|
+
SELECT pipeline_id, day,
|
|
471
|
+
SUM(succeeded)::int AS succeeded,
|
|
472
|
+
SUM(failed)::int AS failed
|
|
473
|
+
FROM execution_daily_rollups
|
|
474
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
475
|
+
AND day >= ${since}::timestamptz AND day < ${scope.to}::timestamptz
|
|
476
|
+
GROUP BY pipeline_id, day
|
|
477
|
+
),
|
|
478
|
+
last_ok AS (
|
|
479
|
+
SELECT pipeline_id, MAX(day) AS last_success_day
|
|
480
|
+
FROM days WHERE succeeded > 0
|
|
481
|
+
GROUP BY pipeline_id
|
|
482
|
+
)
|
|
483
|
+
SELECT d.pipeline_id,
|
|
484
|
+
l.last_success_day,
|
|
485
|
+
COALESCE(SUM(d.failed) FILTER (
|
|
486
|
+
WHERE l.last_success_day IS NULL OR d.day > l.last_success_day
|
|
487
|
+
), 0)::int AS streak
|
|
488
|
+
FROM days d
|
|
489
|
+
LEFT JOIN last_ok l ON l.pipeline_id = d.pipeline_id
|
|
490
|
+
GROUP BY d.pipeline_id, l.last_success_day
|
|
491
|
+
`));
|
|
492
|
+
return rowsOf(rows).map((r) => ({
|
|
493
|
+
pipelineId: String(r.pipeline_id),
|
|
494
|
+
consecutiveFailures: num(r.streak),
|
|
495
|
+
lastSuccessAt: r.last_success_day ? new Date(String(r.last_success_day)) : null,
|
|
496
|
+
}));
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Pipelines that RAN in the window but produced no deploy rows.
|
|
500
|
+
*
|
|
501
|
+
* A measurement gap, not a failure, and the reason a deployment-frequency number can
|
|
502
|
+
* be quietly wrong: a pipeline whose synth predates the `pb.deploys` tags deploys
|
|
503
|
+
* perfectly well and is invisible to every DORA aggregate. Nothing else in the
|
|
504
|
+
* report would say so.
|
|
505
|
+
*/
|
|
506
|
+
export async function getUntrackedPipelines(scope) {
|
|
507
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
508
|
+
WITH active AS (
|
|
509
|
+
SELECT pipeline_id
|
|
510
|
+
FROM execution_daily_rollups
|
|
511
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
512
|
+
AND day >= ${scope.from}::timestamptz AND day < ${scope.to}::timestamptz
|
|
513
|
+
AND stage_name = ''
|
|
514
|
+
GROUP BY pipeline_id
|
|
515
|
+
HAVING SUM(runs) > 0
|
|
516
|
+
),
|
|
517
|
+
tracked AS (
|
|
518
|
+
SELECT DISTINCT pipeline_id
|
|
519
|
+
FROM dora_deployments
|
|
520
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
521
|
+
AND deployed_at >= ${scope.from}::timestamptz AND deployed_at < ${scope.to}::timestamptz
|
|
522
|
+
)
|
|
523
|
+
SELECT a.pipeline_id FROM active a
|
|
524
|
+
WHERE NOT EXISTS (SELECT 1 FROM tracked t WHERE t.pipeline_id = a.pipeline_id)
|
|
525
|
+
`));
|
|
526
|
+
return rowsOf(rows).map((r) => String(r.pipeline_id));
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Pipelines running a plugin version with an OPEN Critical exposure.
|
|
530
|
+
*
|
|
531
|
+
* Point-in-time, not windowed: what matters is whether the pipeline is running one
|
|
532
|
+
* NOW. A Critical that was open last Tuesday and is fixed today is not something to
|
|
533
|
+
* put in front of a manager, and `fixed_at` is what says so.
|
|
534
|
+
*
|
|
535
|
+
* An ACCEPTED exposure is excluded. Someone with the authority to accept a risk has
|
|
536
|
+
* already decided about it in writing, with a reason and a deadline; re-raising it
|
|
537
|
+
* weekly is how a report teaches its reader to skip a section. The lapse of that
|
|
538
|
+
* acceptance is a separate finding (`lapsedAcceptances`).
|
|
539
|
+
*/
|
|
540
|
+
export async function getVulnerablePipelines(scope) {
|
|
541
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
542
|
+
SELECT DISTINCT pipeline_id
|
|
543
|
+
FROM plugin_vuln_exposure
|
|
544
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
545
|
+
AND fixed_at IS NULL
|
|
546
|
+
AND critical_count > 0
|
|
547
|
+
AND triage_state <> 'accepted'
|
|
548
|
+
`));
|
|
549
|
+
return rowsOf(rows).map((r) => String(r.pipeline_id));
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* When each pipeline in the window was last changed.
|
|
553
|
+
*
|
|
554
|
+
* `pipelines.updated_at`, not an audit query: the audit trail lives in another store
|
|
555
|
+
* with its own retention, and the one fact this needs — "was this touched shortly
|
|
556
|
+
* before it broke" — is already on the row, durably, in the same transaction the
|
|
557
|
+
* change was made in.
|
|
558
|
+
*
|
|
559
|
+
* The consequence is stated rather than hidden: `updated_at` moves for ANY write,
|
|
560
|
+
* including a rename. So the finding says "after a configuration change", which is
|
|
561
|
+
* true of a rename too, and does not claim to name the change.
|
|
562
|
+
*/
|
|
563
|
+
export async function getPipelineConfigChanges(scope) {
|
|
564
|
+
const rows = await withTenantTx((tx) => tx.execute(sql `
|
|
565
|
+
SELECT id::text AS id, updated_at
|
|
566
|
+
FROM pipelines
|
|
567
|
+
WHERE org_id ${scope.orgIds?.length ? sql `= ANY(${scope.orgIds})` : sql `= ${scope.orgId}`}
|
|
568
|
+
AND deleted_at IS NULL
|
|
569
|
+
AND updated_at IS NOT NULL
|
|
570
|
+
`));
|
|
571
|
+
const out = new Map();
|
|
572
|
+
for (const r of rowsOf(rows)) {
|
|
573
|
+
const at = r.updated_at ? new Date(String(r.updated_at)) : null;
|
|
574
|
+
if (at && !Number.isNaN(at.getTime()))
|
|
575
|
+
out.set(String(r.id), at);
|
|
576
|
+
}
|
|
577
|
+
return out;
|
|
578
|
+
}
|
|
579
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
580
|
+
/** Rows from a raw `execute`, whichever shape the driver returned. */
|
|
581
|
+
function rowsOf(result) {
|
|
582
|
+
if (Array.isArray(result))
|
|
583
|
+
return result;
|
|
584
|
+
const rows = result?.rows;
|
|
585
|
+
return Array.isArray(rows) ? rows : [];
|
|
586
|
+
}
|
|
587
|
+
/** A count from a driver that may hand back a string for a bigint. */
|
|
588
|
+
function num(v) {
|
|
589
|
+
const n = typeof v === 'number' ? v : Number(v ?? 0);
|
|
590
|
+
return Number.isFinite(n) ? n : 0;
|
|
591
|
+
}
|
|
592
|
+
function isoOrNull(v) {
|
|
593
|
+
if (!v)
|
|
594
|
+
return null;
|
|
595
|
+
const d = v instanceof Date ? v : new Date(String(v));
|
|
596
|
+
return Number.isNaN(d.getTime()) ? null : d.toISOString();
|
|
597
|
+
}
|
|
598
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5hbHl0aWNzLXF1ZXJpZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL3JlcG9ydGluZy9hbmFseXRpY3MtcXVlcmllcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUVILE9BQU8sRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUM3RCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNwRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFFMUQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBV3pELG1EQUFtRDtBQUNuRCxTQUFTLFlBQVksQ0FBQyxHQUFrQyxFQUFFLEtBQXFCO0lBQzdFLE9BQU8sS0FBSyxDQUFDLE1BQU0sSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQzVDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFDNUIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFZLEVBQUUsS0FBSyxDQUFDLEtBQWMsQ0FBQyxDQUFDO0FBQzdDLENBQUM7QUFtQkQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLG9CQUFvQixDQUFDLEtBQXFCLEVBQUUsS0FBSyxHQUFHLEVBQUU7SUFDMUUsTUFBTSxJQUFJLEdBQUcsTUFBTSxZQUFZLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFBOzs7Ozs7Ozs7O3VCQVVqQyxLQUFLLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFD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QUFNLENBQUM7UUFBRSxPQUFPLE1BQXdDLENBQUM7SUFDM0UsTUFBTSxJQUFJLEdBQUksTUFBNkIsRUFBRSxJQUFJLENBQUM7SUFDbEQsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFzQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7QUFDM0UsQ0FBQztBQUVELHNFQUFzRTtBQUN0RSxTQUFTLEdBQUcsQ0FBQyxDQUFVO0lBQ3JCLE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3JELE9BQU8sTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDcEMsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLENBQVU7SUFDM0IsSUFBSSxDQUFDLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNwQixNQUFNLENBQUMsR0FBRyxDQUFDLFlBQVksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3RELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDNUQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRoZSBxdWVyaWVzIHRoZSBiZXNwb2tlIHJlcG9ydCBzZWN0aW9ucyByZWFkLlxuICpcbiAqIEFsbCBvZiB0aGVtIGdvIHRocm91Z2ggYGV4ZWN1dGlvbl9kYWlseV9yb2xsdXBzYCwgYGRvcmFfZGVwbG95bWVudHNgLFxuICogYHBpcGVsaW5lX3BsdWdpbl9yZXNvbHV0aW9uYCBvciBgcGx1Z2luX3Z1bG5fZXhwb3N1cmVgIOKAlCBuZXZlciByYXdcbiAqIGBwaXBlbGluZV9ldmVudHNgLiBUaGF0IGlzIHRoZSB3aG9sZSBwb2ludCBvZiB0aG9zZSB0YWJsZXM6IGEgcXVhcnRlcmx5IHJlcG9ydFxuICogb3ZlciBhIGJ1c3kgb3JnIGNvc3RzIGEgZmV3IGh1bmRyZWQgcm93cywgYW5kIGtlZXBzIHdvcmtpbmcgYWZ0ZXIgcmV0ZW50aW9uIGhhc1xuICogc3dlcHQgdGhlIGV2ZW50cy5cbiAqXG4gKiBUV08gUlVMRVMgRVZFUlkgUVVFUlkgSEVSRSBGT0xMT1dTOlxuICpcbiAqICAtIE5PVEhJTkcgSVMgUEVSLVBFUlNPTi4gRXZlcnkgYWdncmVnYXRlIGlzIHBlciBwaXBlbGluZSwgcGVyIHN0YWdlLCBwZXJcbiAqICAgIGNhdGVnb3J5IG9yIHBlciBlbnZpcm9ubWVudC4gVGhlIHNlY3Rpb24gcmVnaXN0cnkgcmVmdXNlcyBhIHBlci1wZXJzb24gaWQgYXRcbiAqICAgIHJlZ2lzdHJhdGlvbjsgdGhlc2UgcXVlcmllcyBtYWtlIHN1cmUgdGhlcmUgaXMgbm90aGluZyB0byByZWdpc3Rlci5cbiAqICAtIEEgWkVSTyBBTkQgQSBCTElORCBTUE9UIEFSRSBESUZGRVJFTlQuIFdoZXJlIGEgbnVtYmVyIGNhbiBiZSBhYnNlbnQgZm9yIGxhY2tcbiAqICAgIG9mIGRhdGEgcmF0aGVyIHRoYW4gbGFjayBvZiBhY3Rpdml0eSwgdGhlIHNoYXBlIGNhcnJpZXMgdGhhdCAoYG51bGxgLCBhXG4gKiAgICBgY292ZXJhZ2VgIGNvdW50LCBhbiBleHBsaWNpdCByZWFzb24pIGluc3RlYWQgb2YgY29sbGFwc2luZyB0byAwLlxuICovXG5cbmltcG9ydCB7IGFuZCwgZXEsIGd0ZSwgaW5BcnJheSwgbHQsIHNxbCB9IGZyb20gJ2RyaXp6bGUtb3JtJztcbmltcG9ydCB7IEZBSUxVUkVfQ0FURUdPUllfTEFCRUxTLCBpc1RlYW1BY3Rpb25hYmxlIH0gZnJvbSAnLi9mYWlsdXJlLWNsYXNzaWZpZXIuanMnO1xuaW1wb3J0IHsgc2NoZW1hIH0gZnJvbSAnLi4vLi4vZGF0YWJhc2UvZHJpenpsZS1zY2hlbWEuanMnO1xuaW1wb3J0IHR5cGUgeyBGYWlsdXJlQ2F0ZWdvcnkgfSBmcm9tICcuLi8uLi9kYXRhYmFzZS9zY2hlbWEvcmVwb3J0aW5nLWFuYWx5dGljcy5qcyc7XG5pbXBvcnQgeyB3aXRoVGVuYW50VHggfSBmcm9tICcuLi8uLi9kYXRhYmFzZS90ZW5hbmN5LmpzJztcblxuLyoqIFRoZSB3aW5kb3cgYW5kIG9yZyBzY29wZSBldmVyeSBxdWVyeSB0YWtlcy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQW5hbHl0aWNzU2NvcGUge1xuICBvcmdJZDogc3RyaW5nO1xuICAvKiogYFtzZWxmLCAuLi5kZXNjZW5kYW50c11gIGZvciBhIHJvbGx1cDsgdW5kZWZpbmVkID0gdGhpcyBvcmcgb25seS4gKi9cbiAgb3JnSWRzPzogc3RyaW5nW107XG4gIGZyb206IHN0cmluZztcbiAgdG86IHN0cmluZztcbn1cblxuLyoqIFRoZSBvcmcgcHJlZGljYXRlOiBvbmUgb3JnLCBvciBhIHJvbGx1cCBzZXQuICovXG5mdW5jdGlvbiBvcmdQcmVkaWNhdGUoY29sOiBQYXJhbWV0ZXJzPHR5cGVvZiBpbkFycmF5PlswXSwgc2NvcGU6IEFuYWx5dGljc1Njb3BlKSB7XG4gIHJldHVybiBzY29wZS5vcmdJZHMgJiYgc2NvcGUub3JnSWRzLmxlbmd0aCA+IDBcbiAgICA/IGluQXJyYXkoY29sLCBzY29wZS5vcmdJZHMpXG4gICAgOiBlcShjb2wgYXMgbmV2ZXIsIHNjb3BlLm9yZ0lkIGFzIG5ldmVyKTtcbn1cblxuLyoqIE9uZSBwaXBlbGluZSdzIGxpbmUgaW4gdGhlIGJyZWFrZG93bi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGlwZWxpbmVCcmVha2Rvd25Sb3cge1xuICBwaXBlbGluZUlkOiBzdHJpbmc7XG4gIHBpcGVsaW5lTmFtZTogc3RyaW5nIHwgbnVsbDtcbiAgcHJvamVjdDogc3RyaW5nIHwgbnVsbDtcbiAgcnVuczogbnVtYmVyO1xuICBzdWNjZWVkZWQ6IG51bWJlcjtcbiAgZmFpbGVkOiBudW1iZXI7XG4gIHN1Y2Nlc3NQY3Q6IG51bWJlciB8IG51bGw7XG4gIHA5NU1zOiBudW1iZXIgfCBudWxsO1xuICBkZXBsb3lzOiBudW1iZXI7XG4gIC8qKiBUaGUgbW9zdCByZWNlbnQgZmFpbHVyZSdzIGRheSBhbmQgc3RhZ2UsIGZvciBcIndoYXQgdG8gbG9vayBhdCBmaXJzdFwiLiAqL1xuICBsYXN0RmFpbHVyZUF0OiBzdHJpbmcgfCBudWxsO1xuICBsYXN0RmFpbHVyZVN0YWdlOiBzdHJpbmcgfCBudWxsO1xuICBidWlsZFNlY29uZHM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBQZXItcGlwZWxpbmUgZGVsaXZlcnksIHdvcnN0IGZpcnN0LlxuICpcbiAqIFdPUlNUIEZJUlNULCBhbmQgY2FwcGVkLCBiZWNhdXNlIHRoZSBwb2ludCBvZiB0aGUgc2VjdGlvbiBpcyBcIndoZXJlIHNob3VsZFxuICogYXR0ZW50aW9uIGdvXCIuIEEgbGlzdCBzb3J0ZWQgYnkgbmFtZSwgb3IgYW4gdW5ib3VuZGVkIG9uZSwgaXMgYSBkYXRhIGR1bXA6IHRoZVxuICogbGVhZCBzdGlsbCBoYXMgdG8gZmluZCB0aGUgcHJvYmxlbSwgd2hpY2ggaXMgdGhlIHdvcmsgdGhlIHJlcG9ydCB3YXMgc3VwcG9zZWQgdG9cbiAqIGRvLiBUaWVzIGJyZWFrIG9uIHZvbHVtZSwgc28gYSA1MCUtc3VjY2VzcyBwaXBlbGluZSB0aGF0IHJhbiB0d2ljZSBkb2VzIG5vdFxuICogb3V0cmFuayBvbmUgdGhhdCByYW4gdHdvIGh1bmRyZWQgdGltZXMuXG4gKlxuICogYHN1Y2Nlc3NQY3RgIGlzIE5VTEwgZm9yIGEgcGlwZWxpbmUgd2l0aCBubyBydW5zIGluIHRoZSB3aW5kb3cg4oCUIG5vdCAwLiBBXG4gKiBwaXBlbGluZSBub2JvZHkgcmFuIGlzIG5vdCBhIHBpcGVsaW5lIHRoYXQgZmFpbGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0UGlwZWxpbmVCcmVha2Rvd24oc2NvcGU6IEFuYWx5dGljc1Njb3BlLCBsaW1pdCA9IDEwKTogUHJvbWlzZTxQaXBlbGluZUJyZWFrZG93blJvd1tdPiB7XG4gIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICBXSVRIIHRvdGFscyBBUyAoXG4gICAgICBTRUxFQ1RcbiAgICAgICAgci5waXBlbGluZV9pZCxcbiAgICAgICAgU1VNKHIucnVucyk6OmludCBBUyBydW5zLFxuICAgICAgICBTVU0oci5zdWNjZWVkZWQpOjppbnQgQVMgc3VjY2VlZGVkLFxuICAgICAgICBTVU0oci5mYWlsZWQpOjppbnQgQVMgZmFpbGVkLFxuICAgICAgICBNQVgoci5wOTVfbXMpOjppbnQgQVMgcDk1X21zLFxuICAgICAgICBTVU0oci5idWlsZF9zZWNvbmRzKTo6aW50IEFTIGJ1aWxkX3NlY29uZHNcbiAgICAgIEZST00gZXhlY3V0aW9uX2RhaWx5X3JvbGx1cHMgclxuICAgICAgV0hFUkUgci5vcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgICAgQU5EIHIuZGF5ID49ICR7c2NvcGUuZnJvbX06OnRpbWVzdGFtcHR6IEFORCByLmRheSA8ICR7c2NvcGUudG99Ojp0aW1lc3RhbXB0elxuICAgICAgICAtLSBUaGUgcGlwZWxpbmUtbGV2ZWwgZ3JhaW4gb25seTogc3VtbWluZyB0aGUgc3RhZ2Ugcm93cyB0b28gd291bGQgY291bnRcbiAgICAgICAgLS0gZXZlcnkgcnVuIG9uY2UgcGVyIHN0YWdlLlxuICAgICAgICBBTkQgci5zdGFnZV9uYW1lID0gJydcbiAgICAgIEdST1VQIEJZIHIucGlwZWxpbmVfaWRcbiAgICApLFxuICAgIGRlcGxveXMgQVMgKFxuICAgICAgU0VMRUNUIHBpcGVsaW5lX2lkLCBDT1VOVCgqKTo6aW50IEFTIGRlcGxveXNcbiAgICAgIEZST00gZG9yYV9kZXBsb3ltZW50c1xuICAgICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCBkZXBsb3llZF9hdCA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgZGVwbG95ZWRfYXQgPCAke3Njb3BlLnRvfTo6dGltZXN0YW1wdHpcbiAgICAgICAgQU5EIHN1Y2NlZWRlZCA9IHRydWVcbiAgICAgIEdST1VQIEJZIHBpcGVsaW5lX2lkXG4gICAgKSxcbiAgICBsYXN0X2ZhaWwgQVMgKFxuICAgICAgLS0gVGhlIG1vc3QgcmVjZW50IGZhaWxpbmcgU1RBR0Ugcm93IHBlciBwaXBlbGluZTogd2hhdCB0byBsb29rIGF0IGZpcnN0LlxuICAgICAgU0VMRUNUIERJU1RJTkNUIE9OIChwaXBlbGluZV9pZCkgcGlwZWxpbmVfaWQsIGRheSwgc3RhZ2VfbmFtZVxuICAgICAgRlJPTSBleGVjdXRpb25fZGFpbHlfcm9sbHVwc1xuICAgICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCBkYXkgPj0gJHtzY29wZS5mcm9tfTo6dGltZXN0YW1wdHogQU5EIGRheSA8ICR7c2NvcGUudG99Ojp0aW1lc3RhbXB0elxuICAgICAgICBBTkQgc3RhZ2VfbmFtZSA8PiAnJyBBTkQgZmFpbGVkID4gMFxuICAgICAgT1JERVIgQlkgcGlwZWxpbmVfaWQsIGRheSBERVNDLCBmYWlsZWQgREVTQ1xuICAgIClcbiAgICBTRUxFQ1RcbiAgICAgIHQucGlwZWxpbmVfaWQsXG4gICAgICBwLnBpcGVsaW5lX25hbWUsXG4gICAgICBwLnByb2plY3QsXG4gICAgICB0LnJ1bnMsIHQuc3VjY2VlZGVkLCB0LmZhaWxlZCwgdC5wOTVfbXMsIHQuYnVpbGRfc2Vjb25kcyxcbiAgICAgIENPQUxFU0NFKGQuZGVwbG95cywgMCkgQVMgZGVwbG95cyxcbiAgICAgIGYuZGF5IEFTIGxhc3RfZmFpbHVyZV9hdCxcbiAgICAgIGYuc3RhZ2VfbmFtZSBBUyBsYXN0X2ZhaWx1cmVfc3RhZ2UsXG4gICAgICBDQVNFIFdIRU4gdC5ydW5zID4gMCBUSEVOIFJPVU5EKCh0LnN1Y2NlZWRlZDo6bnVtZXJpYyAvIHQucnVucykgKiAxMDAsIDEpIEVMU0UgTlVMTCBFTkQgQVMgc3VjY2Vzc19wY3RcbiAgICBGUk9NIHRvdGFscyB0XG4gICAgTEVGVCBKT0lOIGRlcGxveXMgZCBPTiBkLnBpcGVsaW5lX2lkID0gdC5waXBlbGluZV9pZFxuICAgIExFRlQgSk9JTiBsYXN0X2ZhaWwgZiBPTiBmLnBpcGVsaW5lX2lkID0gdC5waXBlbGluZV9pZFxuICAgIExFRlQgSk9JTiBwaXBlbGluZXMgcCBPTiBwLmlkOjp0ZXh0ID0gdC5waXBlbGluZV9pZFxuICAgIC0tIFdvcnN0IGZpcnN0OiBsb3dlc3Qgc3VjY2VzcyByYXRlLCB0aGVuIG1vc3QgcnVucywgc28gYSA1MCUgcGlwZWxpbmUgdGhhdCByYW5cbiAgICAtLSB0d2ljZSBkb2VzIG5vdCBvdXRyYW5rIG9uZSB0aGF0IHJhbiB0d28gaHVuZHJlZCB0aW1lcy5cbiAgICBPUkRFUiBCWSBzdWNjZXNzX3BjdCBBU0MgTlVMTFMgTEFTVCwgdC5ydW5zIERFU0NcbiAgICBMSU1JVCAke2xpbWl0fVxuICBgKSk7XG4gIHJldHVybiByb3dzT2Yocm93cykubWFwKChyKSA9PiAoe1xuICAgIHBpcGVsaW5lSWQ6IFN0cmluZyhyLnBpcGVsaW5lX2lkKSxcbiAgICBwaXBlbGluZU5hbWU6IChyLnBpcGVsaW5lX25hbWUgYXMgc3RyaW5nIHwgbnVsbCkgPz8gbnVsbCxcbiAgICBwcm9qZWN0OiAoci5wcm9qZWN0IGFzIHN0cmluZyB8IG51bGwpID8/IG51bGwsXG4gICAgcnVuczogbnVtKHIucnVucyksXG4gICAgc3VjY2VlZGVkOiBudW0oci5zdWNjZWVkZWQpLFxuICAgIGZhaWxlZDogbnVtKHIuZmFpbGVkKSxcbiAgICBzdWNjZXNzUGN0OiByLnN1Y2Nlc3NfcGN0ID09PSBudWxsID8gbnVsbCA6IE51bWJlcihyLnN1Y2Nlc3NfcGN0KSxcbiAgICBwOTVNczogci5wOTVfbXMgPT09IG51bGwgPyBudWxsIDogbnVtKHIucDk1X21zKSxcbiAgICBkZXBsb3lzOiBudW0oci5kZXBsb3lzKSxcbiAgICBsYXN0RmFpbHVyZUF0OiBpc29Pck51bGwoci5sYXN0X2ZhaWx1cmVfYXQpLFxuICAgIGxhc3RGYWlsdXJlU3RhZ2U6IChyLmxhc3RfZmFpbHVyZV9zdGFnZSBhcyBzdHJpbmcgfCBudWxsKSA/PyBudWxsLFxuICAgIGJ1aWxkU2Vjb25kczogbnVtKHIuYnVpbGRfc2Vjb25kcyksXG4gIH0pKTtcbn1cblxuLyoqIE9uZSBmYWlsdXJlIGNhdGVnb3J5J3Mgc2hhcmUgb2YgdGhlIHBlcmlvZC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRmFpbHVyZUNhdGVnb3J5Um93IHtcbiAgY2F0ZWdvcnk6IEZhaWx1cmVDYXRlZ29yeTtcbiAgbGFiZWw6IHN0cmluZztcbiAgY291bnQ6IG51bWJlcjtcbiAgc2hhcmVQY3Q6IG51bWJlcjtcbiAgLyoqIFdoZXRoZXIgdGhlIHRlYW0gY2FuIGFjdCBvbiBpdCwgb3IgaXQgaGFwcGVuZWQgdG8gdGhlbS4gKi9cbiAgdGVhbUFjdGlvbmFibGU6IGJvb2xlYW47XG4gIC8qKiBDaGFuZ2UgdnMgdGhlIHByZXZpb3VzIHBlcmlvZCwgaW4gZmFpbHVyZXMuICovXG4gIGNoYW5nZTogbnVtYmVyIHwgbnVsbDtcbiAgLyoqIFRoZSBwaXBlbGluZXMgaXQgaGl0IGhhcmRlc3QuICovXG4gIHRvcFBpcGVsaW5lczogQXJyYXk8eyBwaXBlbGluZUlkOiBzdHJpbmc7IHBpcGVsaW5lTmFtZTogc3RyaW5nIHwgbnVsbDsgY291bnQ6IG51bWJlciB9Pjtcbn1cblxuLyoqXG4gKiBGYWlsdXJlcyBieSBjYXRlZ29yeSwgd2l0aCB0aGUgcHJldmlvdXMgcGVyaW9kJ3MgY291bnRzIGZvciB0aGUgdHJlbmQuXG4gKlxuICogU1BMSVRTIEFDVElPTkFCTEUgRlJPTSBOT1QuIEEgd2VlayB3aG9zZSBmYWlsdXJlcyB3ZXJlIGFsbCBjbG91ZCB0aHJvdHRsaW5nIGlzIGFcbiAqIGRpZmZlcmVudCBjb252ZXJzYXRpb24gZnJvbSBvbmUgd2hvc2UgZmFpbHVyZXMgd2VyZSBhbGwgYnJva2VuIHRlc3RzLCBhbmRcbiAqIHByZXNlbnRpbmcgdGhlbSBpbiBvbmUgcmFua2VkIGxpc3QgaW52aXRlcyBleGFjdGx5IHRoZSB3cm9uZyBjb25jbHVzaW9uIOKAlCBhIHRlYW1cbiAqIGJlaW5nIGFza2VkIHRvIGZpeCBhbiBBV1MgY2FwYWNpdHkgcHJvYmxlbS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldEZhaWx1cmVBbmFseXNpcyhcbiAgc2NvcGU6IEFuYWx5dGljc1Njb3BlLFxuICBwcmV2aW91cz86IHsgZnJvbTogc3RyaW5nOyB0bzogc3RyaW5nIH0sXG4pOiBQcm9taXNlPHsgY2F0ZWdvcmllczogRmFpbHVyZUNhdGVnb3J5Um93W107IHRvdGFsOiBudW1iZXI7IGFjdGlvbmFibGVUb3RhbDogbnVtYmVyIH0+IHtcbiAgY29uc3QgY291bnRzID0gYXdhaXQgY2F0ZWdvcnlDb3VudHMoc2NvcGUpO1xuICBjb25zdCBwcmlvciA9IHByZXZpb3VzID8gYXdhaXQgY2F0ZWdvcnlDb3VudHMoeyAuLi5zY29wZSwgLi4ucHJldmlvdXMgfSkgOiBudWxsO1xuICBjb25zdCB0b3RhbCA9IFsuLi5jb3VudHMudmFsdWVzKCldLnJlZHVjZSgocywgdikgPT4gcyArIHYuY291bnQsIDApO1xuICBjb25zdCB0b3BCeUNhdGVnb3J5ID0gYXdhaXQgdG9wUGlwZWxpbmVzQnlDYXRlZ29yeShzY29wZSk7XG5cbiAgY29uc3QgY2F0ZWdvcmllcyA9IFsuLi5jb3VudHMuZW50cmllcygpXVxuICAgIC5tYXAoKFtjYXRlZ29yeSwgdl0pID0+ICh7XG4gICAgICBjYXRlZ29yeSxcbiAgICAgIGxhYmVsOiBGQUlMVVJFX0NBVEVHT1JZX0xBQkVMU1tjYXRlZ29yeV0gPz8gY2F0ZWdvcnksXG4gICAgICBjb3VudDogdi5jb3VudCxcbiAgICAgIHNoYXJlUGN0OiB0b3RhbCA+IDAgPyBOdW1iZXIoKCh2LmNvdW50IC8gdG90YWwpICogMTAwKS50b0ZpeGVkKDEpKSA6IDAsXG4gICAgICB0ZWFtQWN0aW9uYWJsZTogaXNUZWFtQWN0aW9uYWJsZShjYXRlZ29yeSksXG4gICAgICBjaGFuZ2U6IHByaW9yID8gdi5jb3VudCAtIChwcmlvci5nZXQoY2F0ZWdvcnkpPy5jb3VudCA/PyAwKSA6IG51bGwsXG4gICAgICB0b3BQaXBlbGluZXM6IHRvcEJ5Q2F0ZWdvcnkuZ2V0KGNhdGVnb3J5KSA/PyBbXSxcbiAgICB9KSlcbiAgICAuc29ydCgoYSwgYikgPT4gYi5jb3VudCAtIGEuY291bnQpO1xuXG4gIHJldHVybiB7XG4gICAgY2F0ZWdvcmllcyxcbiAgICB0b3RhbCxcbiAgICBhY3Rpb25hYmxlVG90YWw6IGNhdGVnb3JpZXMuZmlsdGVyKChjKSA9PiBjLnRlYW1BY3Rpb25hYmxlKS5yZWR1Y2UoKHMsIGMpID0+IHMgKyBjLmNvdW50LCAwKSxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY2F0ZWdvcnlDb3VudHMoc2NvcGU6IEFuYWx5dGljc1Njb3BlKTogUHJvbWlzZTxNYXA8RmFpbHVyZUNhdGVnb3J5LCB7IGNvdW50OiBudW1iZXIgfT4+IHtcbiAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgIFNFTEVDVCBrZXkgQVMgY2F0ZWdvcnksIFNVTSh2YWx1ZTo6aW50KTo6aW50IEFTIGNvdW50XG4gICAgRlJPTSBleGVjdXRpb25fZGFpbHlfcm9sbHVwcyByLCBqc29uYl9lYWNoKHIuZmFpbHVyZXNfYnlfY2F0ZWdvcnkpXG4gICAgV0hFUkUgci5vcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgIEFORCByLmRheSA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgci5kYXkgPCAke3Njb3BlLnRvfTo6dGltZXN0YW1wdHpcbiAgICAgIEFORCByLnN0YWdlX25hbWUgPSAnJ1xuICAgIEdST1VQIEJZIGtleVxuICBgKSk7XG4gIGNvbnN0IG91dCA9IG5ldyBNYXA8RmFpbHVyZUNhdGVnb3J5LCB7IGNvdW50OiBudW1iZXIgfT4oKTtcbiAgZm9yIChjb25zdCByIG9mIHJvd3NPZihyb3dzKSkgb3V0LnNldChyLmNhdGVnb3J5IGFzIEZhaWx1cmVDYXRlZ29yeSwgeyBjb3VudDogbnVtKHIuY291bnQpIH0pO1xuICByZXR1cm4gb3V0O1xufVxuXG5hc3luYyBmdW5jdGlvbiB0b3BQaXBlbGluZXNCeUNhdGVnb3J5KFxuICBzY29wZTogQW5hbHl0aWNzU2NvcGUsXG4gIHBlckNhdGVnb3J5ID0gMyxcbik6IFByb21pc2U8TWFwPEZhaWx1cmVDYXRlZ29yeSwgQXJyYXk8eyBwaXBlbGluZUlkOiBzdHJpbmc7IHBpcGVsaW5lTmFtZTogc3RyaW5nIHwgbnVsbDsgY291bnQ6IG51bWJlciB9Pj4+IHtcbiAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgIFNFTEVDVCBjYXRlZ29yeSwgcGlwZWxpbmVfaWQsIHBpcGVsaW5lX25hbWUsIGNvdW50IEZST00gKFxuICAgICAgU0VMRUNUXG4gICAgICAgIGUua2V5IEFTIGNhdGVnb3J5LFxuICAgICAgICByLnBpcGVsaW5lX2lkLFxuICAgICAgICBwLnBpcGVsaW5lX25hbWUsXG4gICAgICAgIFNVTShlLnZhbHVlOjppbnQpOjppbnQgQVMgY291bnQsXG4gICAgICAgIFJPV19OVU1CRVIoKSBPVkVSIChQQVJUSVRJT04gQlkgZS5rZXkgT1JERVIgQlkgU1VNKGUudmFsdWU6OmludCkgREVTQykgQVMgcm5cbiAgICAgIC0tIENST1NTIEpPSU4gTEFURVJBTCwgbm90IGEgY29tbWEgam9pbi4gV2l0aCBGUk9NIGEsIGYoYS54KSBlIExFRlQgSk9JTiBiLFxuICAgICAgLS0gdGhlIExFRlQgSk9JTiBiaW5kcyB0byBlIGFsb25lIGFuZCByIGZhbGxzIG91dCBvZiBzY29wZSBpbiBpdHMgT04gY2xhdXNlXG4gICAgICAtLSAoXCJpbnZhbGlkIHJlZmVyZW5jZSB0byBGUk9NLWNsYXVzZSBlbnRyeVwiKS4gVGhlIGV4cGxpY2l0IGxhdGVyYWwga2VlcHMgYWxsXG4gICAgICAtLSB0aHJlZSBpbiBvbmUgam9pbiB0cmVlLlxuICAgICAgRlJPTSBleGVjdXRpb25fZGFpbHlfcm9sbHVwcyByXG4gICAgICBDUk9TUyBKT0lOIExBVEVSQUwganNvbmJfZWFjaChyLmZhaWx1cmVzX2J5X2NhdGVnb3J5KSBlXG4gICAgICBMRUZUIEpPSU4gcGlwZWxpbmVzIHAgT04gcC5pZDo6dGV4dCA9IHIucGlwZWxpbmVfaWRcbiAgICAgIFdIRVJFIHIub3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCByLmRheSA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgci5kYXkgPCAke3Njb3BlLnRvfTo6dGltZXN0YW1wdHpcbiAgICAgICAgQU5EIHIuc3RhZ2VfbmFtZSA9ICcnXG4gICAgICBHUk9VUCBCWSBlLmtleSwgci5waXBlbGluZV9pZCwgcC5waXBlbGluZV9uYW1lXG4gICAgKSByYW5rZWRcbiAgICBXSEVSRSBybiA8PSAke3BlckNhdGVnb3J5fVxuICBgKSk7XG4gIGNvbnN0IG91dCA9IG5ldyBNYXA8RmFpbHVyZUNhdGVnb3J5LCBBcnJheTx7IHBpcGVsaW5lSWQ6IHN0cmluZzsgcGlwZWxpbmVOYW1lOiBzdHJpbmcgfCBudWxsOyBjb3VudDogbnVtYmVyIH0+PigpO1xuICBmb3IgKGNvbnN0IHIgb2Ygcm93c09mKHJvd3MpKSB7XG4gICAgY29uc3Qga2V5ID0gci5jYXRlZ29yeSBhcyBGYWlsdXJlQ2F0ZWdvcnk7XG4gICAgY29uc3QgbGlzdCA9IG91dC5nZXQoa2V5KSA/PyBbXTtcbiAgICBsaXN0LnB1c2goe1xuICAgICAgcGlwZWxpbmVJZDogU3RyaW5nKHIucGlwZWxpbmVfaWQpLFxuICAgICAgcGlwZWxpbmVOYW1lOiAoci5waXBlbGluZV9uYW1lIGFzIHN0cmluZyB8IG51bGwpID8/IG51bGwsXG4gICAgICBjb3VudDogbnVtKHIuY291bnQpLFxuICAgIH0pO1xuICAgIG91dC5zZXQoa2V5LCBsaXN0KTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKiogT25lIHN0YWdlJ3MgdGltaW5nLCBmb3IgdGhlIGRyaWxsLWRvd24uICovXG5leHBvcnQgaW50ZXJmYWNlIFN0YWdlUGVyZm9ybWFuY2VSb3cge1xuICBzdGFnZU5hbWU6IHN0cmluZztcbiAgcnVuczogbnVtYmVyO1xuICBwNTBNczogbnVtYmVyIHwgbnVsbDtcbiAgcDk1TXM6IG51bWJlciB8IG51bGw7XG4gIC8qKiBDaGFuZ2UgaW4gcDk1IHZzIHRoZSBwcmV2aW91cyBwZXJpb2QsIGFzIGEgcGVyY2VudGFnZS4gKi9cbiAgcDk1Q2hhbmdlUGN0OiBudW1iZXIgfCBudWxsO1xuICBmYWlsZWQ6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBQZXItc3RhZ2UgdGltaW5ncyBmb3Igb25lIHBpcGVsaW5lLCBzbG93ZXN0IGZpcnN0LlxuICpcbiAqIFRoZSBzZWN0aW9uIGEgbGVhZCBvcGVucyBhZnRlciB0aGUgYnJlYWtkb3duIHNheXMgYSBwaXBlbGluZSBnb3Qgc2xvd2VyOiBcIndoaWNoXG4gKiBzdGFnZVwiIGlzIHRoZSBuZXh0IHF1ZXN0aW9uLCBhbmQgYW5zd2VyaW5nIGl0IGZyb20gcmF3IGV2ZW50cyB3b3VsZCBtZWFuIGFcbiAqIHNlY29uZCBzY2FuIG9mIHRoZSBzYW1lIHdpbmRvdy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldFN0YWdlUGVyZm9ybWFuY2UoXG4gIHNjb3BlOiBBbmFseXRpY3NTY29wZSxcbiAgcGlwZWxpbmVJZDogc3RyaW5nLFxuICBwcmV2aW91cz86IHsgZnJvbTogc3RyaW5nOyB0bzogc3RyaW5nIH0sXG4pOiBQcm9taXNlPFN0YWdlUGVyZm9ybWFuY2VSb3dbXT4ge1xuICBjb25zdCBjdXJyZW50ID0gYXdhaXQgc3RhZ2VSb3dzKHNjb3BlLCBwaXBlbGluZUlkKTtcbiAgY29uc3QgcHJpb3I6IE1hcDxzdHJpbmcsIHsgcDk1TXM6IG51bWJlciB8IG51bGwgfT4gPSBwcmV2aW91c1xuICAgID8gbmV3IE1hcCgoYXdhaXQgc3RhZ2VSb3dzKHsgLi4uc2NvcGUsIC4uLnByZXZpb3VzIH0sIHBpcGVsaW5lSWQpKS5tYXAoKHMpID0+IFtzLnN0YWdlTmFtZSwgc10pKVxuICAgIDogbmV3IE1hcCgpO1xuICByZXR1cm4gY3VycmVudC5tYXAoKHMpID0+IHtcbiAgICBjb25zdCBiZWZvcmUgPSBwcmlvci5nZXQocy5zdGFnZU5hbWUpPy5wOTVNcyA/PyBudWxsO1xuICAgIHJldHVybiB7XG4gICAgICAuLi5zLFxuICAgICAgcDk1Q2hhbmdlUGN0OiBiZWZvcmUgJiYgYmVmb3JlID4gMCAmJiBzLnA5NU1zICE9PSBudWxsXG4gICAgICAgID8gTnVtYmVyKCgoKHMucDk1TXMgLSBiZWZvcmUpIC8gYmVmb3JlKSAqIDEwMCkudG9GaXhlZCgxKSlcbiAgICAgICAgOiBudWxsLFxuICAgIH07XG4gIH0pLnNvcnQoKGEsIGIpID0+IChiLnA5NU1zID8/IDApIC0gKGEucDk1TXMgPz8gMCkpO1xufVxuXG5pbnRlcmZhY2UgU3RhZ2VSb3cgeyBzdGFnZU5hbWU6IHN0cmluZzsgcnVuczogbnVtYmVyOyBwNTBNczogbnVtYmVyIHwgbnVsbDsgcDk1TXM6IG51bWJlciB8IG51bGw7IGZhaWxlZDogbnVtYmVyIH1cblxuYXN5bmMgZnVuY3Rpb24gc3RhZ2VSb3dzKHNjb3BlOiBBbmFseXRpY3NTY29wZSwgcGlwZWxpbmVJZDogc3RyaW5nKTogUHJvbWlzZTxTdGFnZVJvd1tdPiB7XG4gIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3Qoe1xuICAgIHN0YWdlTmFtZTogc2NoZW1hLmV4ZWN1dGlvbkRhaWx5Um9sbHVwLnN0YWdlTmFtZSxcbiAgICBydW5zOiBzcWw8bnVtYmVyPmBTVU0oJHtzY2hlbWEuZXhlY3V0aW9uRGFpbHlSb2xsdXAucnVuc30pOjppbnRgLFxuICAgIGZhaWxlZDogc3FsPG51bWJlcj5gU1VNKCR7c2NoZW1hLmV4ZWN1dGlvbkRhaWx5Um9sbHVwLmZhaWxlZH0pOjppbnRgLFxuICAgIC8vIE1BWCBvZiB0aGUgZGFpbHkgcDk1cywgbm90IGFuIGF2ZXJhZ2Ugb2YgcGVyY2VudGlsZXM6IGF2ZXJhZ2luZ1xuICAgIC8vIHBlcmNlbnRpbGVzIGlzIG5vdCBhIHBlcmNlbnRpbGUgb2YgYW55dGhpbmcsIGFuZCB0aGUgd29yc3QgZGF5IGlzIHRoZVxuICAgIC8vIG51bWJlciBzb21lYm9keSB3b3VsZCBhY3Qgb24uXG4gICAgcDUwTXM6IHNxbDxudW1iZXIgfCBudWxsPmBNQVgoJHtzY2hlbWEuZXhlY3V0aW9uRGFpbHlSb2xsdXAucDUwTXN9KTo6aW50YCxcbiAgICBwOTVNczogc3FsPG51bWJlciB8IG51bGw+YE1BWCgke3NjaGVtYS5leGVjdXRpb25EYWlseVJvbGx1cC5wOTVNc30pOjppbnRgLFxuICB9KS5mcm9tKHNjaGVtYS5leGVjdXRpb25EYWlseVJvbGx1cClcbiAgICAud2hlcmUoYW5kKFxuICAgICAgb3JnUHJlZGljYXRlKHNjaGVtYS5leGVjdXRpb25EYWlseVJvbGx1cC5vcmdJZCwgc2NvcGUpLFxuICAgICAgZXEoc2NoZW1hLmV4ZWN1dGlvbkRhaWx5Um9sbHVwLnBpcGVsaW5lSWQsIHBpcGVsaW5lSWQpLFxuICAgICAgZ3RlKHNjaGVtYS5leGVjdXRpb25EYWlseVJvbGx1cC5kYXksIG5ldyBEYXRlKHNjb3BlLmZyb20pKSxcbiAgICAgIGx0KHNjaGVtYS5leGVjdXRpb25EYWlseVJvbGx1cC5kYXksIG5ldyBEYXRlKHNjb3BlLnRvKSksXG4gICAgICBzcWxgJHtzY2hlbWEuZXhlY3V0aW9uRGFpbHlSb2xsdXAuc3RhZ2VOYW1lfSA8PiAnJ2AsXG4gICAgKSlcbiAgICAuZ3JvdXBCeShzY2hlbWEuZXhlY3V0aW9uRGFpbHlSb2xsdXAuc3RhZ2VOYW1lKSk7XG4gIGNvbnN0IG1hcCA9IG5ldyBNYXA8c3RyaW5nLCBTdGFnZVJvdz4oKTtcbiAgZm9yIChjb25zdCByIG9mIHJvd3MgYXMgQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+KSB7XG4gICAgbWFwLnNldChTdHJpbmcoci5zdGFnZU5hbWUpLCB7XG4gICAgICBzdGFnZU5hbWU6IFN0cmluZyhyLnN0YWdlTmFtZSksXG4gICAgICBydW5zOiBudW0oci5ydW5zKSxcbiAgICAgIGZhaWxlZDogbnVtKHIuZmFpbGVkKSxcbiAgICAgIHA1ME1zOiByLnA1ME1zID09PSBudWxsID8gbnVsbCA6IG51bShyLnA1ME1zKSxcbiAgICAgIHA5NU1zOiByLnA5NU1zID09PSBudWxsID8gbnVsbCA6IG51bShyLnA5NU1zKSxcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gWy4uLm1hcC52YWx1ZXMoKV07XG59XG5cbi8qKiBCdWlsZCB0aW1lIGJ5IHByb2plY3QgYW5kIHBpcGVsaW5lLiBObyBtb25leSBhbnl3aGVyZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb3VyY2VSb3cge1xuICBwcm9qZWN0OiBzdHJpbmcgfCBudWxsO1xuICBwaXBlbGluZUlkOiBzdHJpbmc7XG4gIHBpcGVsaW5lTmFtZTogc3RyaW5nIHwgbnVsbDtcbiAgYnVpbGRTZWNvbmRzOiBudW1iZXI7XG4gIHJ1bnM6IG51bWJlcjtcbiAgc2hhcmVQY3Q6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBXaGVyZSB0aGUgYnVpbGQgdGltZSB3ZW50LlxuICpcbiAqIFNFQ09ORFMsIE5PVCBET0xMQVJTLiBBIGNvc3QgZmlndXJlIG5lZWRzIHRoZSBpbnN0YW5jZSB0eXBlcywgdGhlIHNwb3Qvb24tZGVtYW5kXG4gKiBtaXggYW5kIHRoZSBhY2NvdW50J3Mgb3duIGRpc2NvdW50cywgbm9uZSBvZiB3aGljaCB0aGlzIHBsYXRmb3JtIGtub3dzIOKAlCBzbyBhXG4gKiBkb2xsYXIgbnVtYmVyIGhlcmUgd291bGQgYmUgYSBndWVzcyBwcmVzZW50ZWQgYXMgYW4gaW52b2ljZS4gQnVpbGQgdGltZSBpcyB0aGVcbiAqIHRoaW5nIGFjdHVhbGx5IG1lYXN1cmVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0UmVzb3VyY2VDb25zdW1wdGlvbihzY29wZTogQW5hbHl0aWNzU2NvcGUsIGxpbWl0ID0gMjApOiBQcm9taXNlPHtcbiAgcGlwZWxpbmVzOiBSZXNvdXJjZVJvd1tdO1xuICB0b3RhbEJ1aWxkU2Vjb25kczogbnVtYmVyO1xufT4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgU0VMRUNUXG4gICAgICBwLnByb2plY3QsXG4gICAgICByLnBpcGVsaW5lX2lkLFxuICAgICAgcC5waXBlbGluZV9uYW1lLFxuICAgICAgU1VNKHIuYnVpbGRfc2Vjb25kcyk6OmludCBBUyBidWlsZF9zZWNvbmRzLFxuICAgICAgU1VNKHIucnVucyk6OmludCBBUyBydW5zXG4gICAgRlJPTSBleGVjdXRpb25fZGFpbHlfcm9sbHVwcyByXG4gICAgTEVGVCBKT0lOIHBpcGVsaW5lcyBwIE9OIHAuaWQ6OnRleHQgPSByLnBpcGVsaW5lX2lkXG4gICAgV0hFUkUgci5vcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgIEFORCByLmRheSA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgci5kYXkgPCAke3Njb3BlLnRvfTo6dGltZXN0YW1wdHpcbiAgICAgIEFORCByLnN0YWdlX25hbWUgPSAnJ1xuICAgIEdST1VQIEJZIHAucHJvamVjdCwgci5waXBlbGluZV9pZCwgcC5waXBlbGluZV9uYW1lXG4gICAgT1JERVIgQlkgYnVpbGRfc2Vjb25kcyBERVNDXG4gICAgTElNSVQgJHtsaW1pdH1cbiAgYCkpO1xuICBjb25zdCBsaXN0ID0gcm93c09mKHJvd3MpO1xuICBjb25zdCB0b3RhbCA9IGxpc3QucmVkdWNlKChzLCByKSA9PiBzICsgbnVtKHIuYnVpbGRfc2Vjb25kcyksIDApO1xuICByZXR1cm4ge1xuICAgIHRvdGFsQnVpbGRTZWNvbmRzOiB0b3RhbCxcbiAgICBwaXBlbGluZXM6IGxpc3QubWFwKChyKSA9PiAoe1xuICAgICAgcHJvamVjdDogKHIucHJvamVjdCBhcyBzdHJpbmcgfCBudWxsKSA/PyBudWxsLFxuICAgICAgcGlwZWxpbmVJZDogU3RyaW5nKHIucGlwZWxpbmVfaWQpLFxuICAgICAgcGlwZWxpbmVOYW1lOiAoci5waXBlbGluZV9uYW1lIGFzIHN0cmluZyB8IG51bGwpID8/IG51bGwsXG4gICAgICBidWlsZFNlY29uZHM6IG51bShyLmJ1aWxkX3NlY29uZHMpLFxuICAgICAgcnVuczogbnVtKHIucnVucyksXG4gICAgICBzaGFyZVBjdDogdG90YWwgPiAwID8gTnVtYmVyKCgobnVtKHIuYnVpbGRfc2Vjb25kcykgLyB0b3RhbCkgKiAxMDApLnRvRml4ZWQoMSkpIDogMCxcbiAgICB9KSksXG4gIH07XG59XG5cbi8qKiBPbmUgY29tbWl0J3Mgam91cm5leSBhY3Jvc3MgZW52aXJvbm1lbnRzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQcm9tb3Rpb25Sb3cge1xuICBjb21taXRTaGE6IHN0cmluZztcbiAgLyoqIEVudmlyb25tZW50IOKGkiB3aGVuIGl0IGxhbmRlZCB0aGVyZSwgaW4gdGhlIG9yZGVyIGl0IHJlYWNoZWQgdGhlbS4gKi9cbiAgc3RvcHM6IEFycmF5PHsgZW52aXJvbm1lbnQ6IHN0cmluZzsgZGVwbG95ZWRBdDogc3RyaW5nOyBob3Vyc0Zyb21QcmV2aW91czogbnVtYmVyIHwgbnVsbCB9PjtcbiAgLyoqIFRydWUgd2hlbiB0aGUgY29tbWl0IHJlYWNoZWQgYSBub24tcHJvZHVjdGlvbiBlbnZpcm9ubWVudCBhbmQgc3RvcHBlZC4gKi9cbiAgc3R1Y2s6IGJvb2xlYW47XG4gIGZ1cnRoZXN0OiBzdHJpbmc7XG59XG5cbi8qKlxuICogV2hlcmUgY29tbWl0cyBnb3QgdG8sIGFuZCB3aGVyZSB0aGV5IHN0b3BwZWQuXG4gKlxuICogXCJTdHVja1wiIGlzIHRoZSBudW1iZXIgd29ydGggcmVhZGluZy4gQSBjb21taXQgdGhhdCByZWFjaGVkIHN0YWdpbmcgZm91ciBkYXlzIGFnb1xuICogYW5kIG5ldmVyIHdlbnQgZnVydGhlciBpcyB3b3JrIHRoYXQgaXMgZmluaXNoZWQgYW5kIG5vdCBkZWxpdmVyZWQg4oCUIHdoaWNoIGRvZXNcbiAqIG5vdCBhcHBlYXIgaW4gZGVwbG95bWVudCBmcmVxdWVuY3ksIHN1Y2Nlc3MgcmF0ZSBvciBsZWFkIHRpbWUsIGJlY2F1c2UgYnkgZXZlcnlcbiAqIG9uZSBvZiB0aG9zZSBtZWFzdXJlcyBub3RoaW5nIGhhcHBlbmVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0UHJvbW90aW9uVmlldyhcbiAgc2NvcGU6IEFuYWx5dGljc1Njb3BlLFxuICBwcm9kdWN0aW9uRW52ID0gJ3Byb2R1Y3Rpb24nLFxuICBsaW1pdCA9IDI1LFxuKTogUHJvbWlzZTx7IGNvbW1pdHM6IFByb21vdGlvblJvd1tdOyBzdHVja0NvdW50OiBudW1iZXIgfT4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgU0VMRUNUIGNvbW1pdF9zaGEsIGVudmlyb25tZW50LCBNSU4oZGVwbG95ZWRfYXQpIEFTIGRlcGxveWVkX2F0XG4gICAgRlJPTSBkb3JhX2RlcGxveW1lbnRzXG4gICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICBBTkQgZGVwbG95ZWRfYXQgPj0gJHtzY29wZS5mcm9tfTo6dGltZXN0YW1wdHogQU5EIGRlcGxveWVkX2F0IDwgJHtzY29wZS50b306OnRpbWVzdGFtcHR6XG4gICAgICBBTkQgc3VjY2VlZGVkID0gdHJ1ZVxuICAgICAgQU5EIGNvbW1pdF9zaGEgSVMgTk9UIE5VTExcbiAgICBHUk9VUCBCWSBjb21taXRfc2hhLCBlbnZpcm9ubWVudFxuICAgIE9SREVSIEJZIE1JTihkZXBsb3llZF9hdCkgREVTQ1xuICBgKSk7XG5cbiAgY29uc3QgYnlDb21taXQgPSBuZXcgTWFwPHN0cmluZywgQXJyYXk8eyBlbnZpcm9ubWVudDogc3RyaW5nOyBkZXBsb3llZEF0OiBEYXRlIH0+PigpO1xuICBmb3IgKGNvbnN0IHIgb2Ygcm93c09mKHJvd3MpKSB7XG4gICAgY29uc3Qgc2hhID0gU3RyaW5nKHIuY29tbWl0X3NoYSk7XG4gICAgY29uc3QgbGlzdCA9IGJ5Q29tbWl0LmdldChzaGEpID8/IFtdO1xuICAgIGxpc3QucHVzaCh7IGVudmlyb25tZW50OiBTdHJpbmcoci5lbnZpcm9ubWVudCksIGRlcGxveWVkQXQ6IG5ldyBEYXRlKFN0cmluZyhyLmRlcGxveWVkX2F0KSkgfSk7XG4gICAgYnlDb21taXQuc2V0KHNoYSwgbGlzdCk7XG4gIH1cblxuICBjb25zdCBjb21taXRzOiBQcm9tb3Rpb25Sb3dbXSA9IFsuLi5ieUNvbW1pdC5lbnRyaWVzKCldLm1hcCgoW2NvbW1pdFNoYSwgcmF3XSkgPT4ge1xuICAgIGNvbnN0IG9yZGVyZWQgPSBbLi4ucmF3XS5zb3J0KChhLCBiKSA9PiBhLmRlcGxveWVkQXQuZ2V0VGltZSgpIC0gYi5kZXBsb3llZEF0LmdldFRpbWUoKSk7XG4gICAgY29uc3Qgc3RvcHMgPSBvcmRlcmVkLm1hcCgoc3RvcCwgaSkgPT4gKHtcbiAgICAgIGVudmlyb25tZW50OiBzdG9wLmVudmlyb25tZW50LFxuICAgICAgZGVwbG95ZWRBdDogc3RvcC5kZXBsb3llZEF0LnRvSVNPU3RyaW5nKCksXG4gICAgICBob3Vyc0Zyb21QcmV2aW91czogaSA9PT0gMFxuICAgICAgICA/IG51bGxcbiAgICAgICAgOiBOdW1iZXIoKChzdG9wLmRlcGxveWVkQXQuZ2V0VGltZSgpIC0gKG9yZGVyZWRbaSAtIDFdIGFzIHsgZGVwbG95ZWRBdDogRGF0ZSB9KS5kZXBsb3llZEF0LmdldFRpbWUoKSkgLyAzNjAwXzAwMCkudG9GaXhlZCgxKSksXG4gICAgfSkpO1xuICAgIGNvbnN0IGZ1cnRoZXN0ID0gb3JkZXJlZFtvcmRlcmVkLmxlbmd0aCAtIDFdPy5lbnZpcm9ubWVudCA/PyAnJztcbiAgICByZXR1cm4geyBjb21taXRTaGEsIHN0b3BzLCBmdXJ0aGVzdCwgc3R1Y2s6IGZ1cnRoZXN0ICE9PSBwcm9kdWN0aW9uRW52IH07XG4gIH0pXG4gICAgLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIGNvbnN0IGFMYXN0ID0gYS5zdG9wc1thLnN0b3BzLmxlbmd0aCAtIDFdPy5kZXBsb3llZEF0ID8/ICcnO1xuICAgICAgY29uc3QgYkxhc3QgPSBiLnN0b3BzW2Iuc3RvcHMubGVuZ3RoIC0gMV0/LmRlcGxveWVkQXQgPz8gJyc7XG4gICAgICByZXR1cm4gYkxhc3QubG9jYWxlQ29tcGFyZShhTGFzdCk7XG4gICAgfSlcbiAgICAuc2xpY2UoMCwgbGltaXQpO1xuXG4gIHJldHVybiB7IGNvbW1pdHMsIHN0dWNrQ291bnQ6IGNvbW1pdHMuZmlsdGVyKChjKSA9PiBjLnN0dWNrKS5sZW5ndGggfTtcbn1cblxuLyoqIE91dGRhdGVkIHBsdWdpbiB1c2FnZSwgcGVyIHBpcGVsaW5lIGFuZCBzdGVwLiAqL1xuZXhwb3J0IGludGVyZmFjZSBPdXRkYXRlZFBsdWdpblJvdyB7XG4gIHBpcGVsaW5lSWQ6IHN0cmluZztcbiAgcGlwZWxpbmVOYW1lOiBzdHJpbmcgfCBudWxsO1xuICBzdGFnZU5hbWU6IHN0cmluZztcbiAgc3RlcE5hbWU6IHN0cmluZztcbiAgcGx1Z2luOiBzdHJpbmc7XG4gIGluVXNlOiBzdHJpbmcgfCBudWxsO1xuICBsYXRlc3Q6IHN0cmluZyB8IG51bGw7XG4gIGdhcDogc3RyaW5nIHwgbnVsbDtcbiAgd2l0aGluUG9saWN5OiBib29sZWFuO1xufVxuXG4vKipcbiAqIFdoaWNoIHBpcGVsaW5lcyBhcmUgYmVoaW5kIG9uIGEgcGx1Z2luLCB3b3JzdCBnYXAgZmlyc3QuXG4gKlxuICogUmVhZHMgdGhlIERFQ0xBUkVEIHNpZGUgKGBwaXBlbGluZV9wbHVnaW5fcmVzb2x1dGlvbmApLiBBIHBpcGVsaW5lJ3MgZGVjbGFyZWRcbiAqIHJhbmdlIGFuZCB3aGF0IGl0IGxhc3QgYWN0dWFsbHkgcmFuIGNhbiBkaXNhZ3JlZSDigJQgd2hpY2ggaXMgd2h5IHRoZSB2dWxuZXJhYmlsaXR5XG4gKiBzZWN0aW9uIGJlbG93IHJlYWRzIHRoZSBkZXBsb3llZCBzaWRlIGFuZCBzYXlzIHdoaWNoIGl0IGlzIHNob3dpbmcuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRPdXRkYXRlZFBsdWdpbnMoc2NvcGU6IEFuYWx5dGljc1Njb3BlLCBsaW1pdCA9IDUwKTogUHJvbWlzZTx7XG4gIHJvd3M6IE91dGRhdGVkUGx1Z2luUm93W107XG4gIGJlaGluZFBpcGVsaW5lczogbnVtYmVyO1xuICBtYWpvckJlaGluZDogbnVtYmVyO1xufT4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgU0VMRUNUXG4gICAgICByLnBpcGVsaW5lX2lkLCBwLnBpcGVsaW5lX25hbWUsIHIuc3RhZ2VfbmFtZSwgci5zdGVwX25hbWUsXG4gICAgICBDT0FMRVNDRShyLnBsdWdpbl9wdWJsaXNoZXIgfHwgJy8nLCAnJykgfHwgci5wbHVnaW5fbmFtZSBBUyBwbHVnaW4sXG4gICAgICByLnJlc29sdmVkX3ZlcnNpb24sIHIubGF0ZXN0X3ZlcnNpb24sIHIudmVyc2lvbl9nYXAsIHIud2l0aGluX3BvbGljeVxuICAgIEZST00gcGlwZWxpbmVfcGx1Z2luX3Jlc29sdXRpb24gclxuICAgIExFRlQgSk9JTiBwaXBlbGluZXMgcCBPTiBwLmlkOjp0ZXh0ID0gci5waXBlbGluZV9pZFxuICAgIFdIRVJFIHIub3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICBBTkQgKHIudmVyc2lvbl9nYXAgSU4gKCdtYWpvcicsICdtaW5vcicsICdwYXRjaCcpIE9SIHIud2l0aGluX3BvbGljeSA9IGZhbHNlKVxuICAgIE9SREVSIEJZIENBU0Ugci52ZXJzaW9uX2dhcCBXSEVOICdtYWpvcicgVEhFTiAwIFdIRU4gJ21pbm9yJyBUSEVOIDEgV0hFTiAncGF0Y2gnIFRIRU4gMiBFTFNFIDMgRU5ELFxuICAgICAgICAgICAgIHIucGlwZWxpbmVfaWRcbiAgICBMSU1JVCAke2xpbWl0fVxuICBgKSk7XG4gIGNvbnN0IGxpc3QgPSByb3dzT2Yocm93cykubWFwKChyKSA9PiAoe1xuICAgIHBpcGVsaW5lSWQ6IFN0cmluZyhyLnBpcGVsaW5lX2lkKSxcbiAgICBwaXBlbGluZU5hbWU6IChyLnBpcGVsaW5lX25hbWUgYXMgc3RyaW5nIHwgbnVsbCkgPz8gbnVsbCxcbiAgICBzdGFnZU5hbWU6IFN0cmluZyhyLnN0YWdlX25hbWUpLFxuICAgIHN0ZXBOYW1lOiBTdHJpbmcoci5zdGVwX25hbWUpLFxuICAgIHBsdWdpbjogU3RyaW5nKHIucGx1Z2luKSxcbiAgICBpblVzZTogKHIucmVzb2x2ZWRfdmVyc2lvbiBhcyBzdHJpbmcgfCBudWxsKSA/PyBudWxsLFxuICAgIGxhdGVzdDogKHIubGF0ZXN0X3ZlcnNpb24gYXMgc3RyaW5nIHwgbnVsbCkgPz8gbnVsbCxcbiAgICBnYXA6IChyLnZlcnNpb25fZ2FwIGFzIHN0cmluZyB8IG51bGwpID8/IG51bGwsXG4gICAgd2l0aGluUG9saWN5OiByLndpdGhpbl9wb2xpY3kgIT09IGZhbHNlLFxuICB9KSk7XG4gIHJldHVybiB7XG4gICAgcm93czogbGlzdCxcbiAgICBiZWhpbmRQaXBlbGluZXM6IG5ldyBTZXQobGlzdC5tYXAoKHIpID0+IHIucGlwZWxpbmVJZCkpLnNpemUsXG4gICAgbWFqb3JCZWhpbmQ6IGxpc3QuZmlsdGVyKChyKSA9PiByLmdhcCA9PT0gJ21ham9yJykubGVuZ3RoLFxuICB9O1xufVxuXG4vKiogQSBwaXBlbGluZSdzIGV4cG9zdXJlIHRvIGEgdnVsbmVyYWJsZSBwbHVnaW4gdmVyc2lvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVnVsbmVyYWJpbGl0eVJvdyB7XG4gIHBpcGVsaW5lSWQ6IHN0cmluZztcbiAgcGlwZWxpbmVOYW1lOiBzdHJpbmcgfCBudWxsO1xuICBwbHVnaW46IHN0cmluZztcbiAgdmVyc2lvbjogc3RyaW5nO1xuICAvKiogYGRlcGxveWVkYCA9IHdoYXQgYWN0dWFsbHkgcmFuOyBgZGVjbGFyZWRgID0gd2hhdCB0aGUgY29uZmlnIGFza3MgZm9yLiAqL1xuICBzb3VyY2U6IHN0cmluZztcbiAgY3JpdGljYWw6IG51bWJlcjtcbiAgaGlnaDogbnVtYmVyO1xuICBmaW5kaW5nczogQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+O1xuICBmbGFnZ2VkQXQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBQbHVnaW4gdnVsbmVyYWJpbGl0aWVzLCBwZXIgcGlwZWxpbmUsIENyaXRpY2FscyBmaXJzdC5cbiAqXG4gKiBTQVlTIFdIRVRIRVIgSVQgSVMgREVDTEFSRUQgT1IgREVQTE9ZRUQuIEEgcGlwZWxpbmUgd2hvc2UgY29uZmlnIHdhcyB1cGRhdGVkIHRvIGFcbiAqIGZpeGVkIHZlcnNpb24gYnV0IHdoaWNoIGhhcyBub3QgcmUtc3ludGhlZCBpcyBzdGlsbCBydW5uaW5nIHRoZSB2dWxuZXJhYmxlIGltYWdlLFxuICogYW5kIGEgcmVwb3J0IHRoYXQgY29sbGFwc2VkIHRoZSB0d28gd291bGQgdGVsbCBhIG1hbmFnZXIgdGhlIHByb2JsZW0gaXMgc29sdmVkXG4gKiB3aGlsZSB0aGUgb2xkIGltYWdlIGtlZXBzIHJ1bm5pbmcuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRQbHVnaW5WdWxuZXJhYmlsaXRpZXMoc2NvcGU6IEFuYWx5dGljc1Njb3BlLCBsaW1pdCA9IDUwKTogUHJvbWlzZTx7XG4gIHJvd3M6IFZ1bG5lcmFiaWxpdHlSb3dbXTtcbiAgZXhwb3NlZFBpcGVsaW5lczogbnVtYmVyO1xuICBjcml0aWNhbFBpcGVsaW5lczogbnVtYmVyO1xufT4ge1xuICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgU0VMRUNUXG4gICAgICB2LnBpcGVsaW5lX2lkLCBwLnBpcGVsaW5lX25hbWUsXG4gICAgICBDT0FMRVNDRSh2LnBsdWdpbl9wdWJsaXNoZXIgfHwgJy8nLCAnJykgfHwgdi5wbHVnaW5fbmFtZSBBUyBwbHVnaW4sXG4gICAgICB2LnBsdWdpbl92ZXJzaW9uLCB2LnNvdXJjZSwgdi5jcml0aWNhbF9jb3VudCwgdi5oaWdoX2NvdW50LCB2LnRvcF9maW5kaW5ncywgdi5mbGFnZ2VkX2F0XG4gICAgRlJPTSBwbHVnaW5fdnVsbl9leHBvc3VyZSB2XG4gICAgTEVGVCBKT0lOIHBpcGVsaW5lcyBwIE9OIHAuaWQ6OnRleHQgPSB2LnBpcGVsaW5lX2lkXG4gICAgV0hFUkUgdi5vcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgIEFORCB2LmZpeGVkX2F0IElTIE5VTExcbiAgICAgIC0tIEFuIGFjY2VwdGFuY2UgcGFzdCBpdHMgZGVhZGxpbmUgaXMgbm90IGFuIGFjY2VwdGFuY2UuXG4gICAgICBBTkQgKHYudHJpYWdlX3N0YXRlIE5PVCBJTiAoJ2FjY2VwdGVkJywgJ2ZhbHNlX3Bvc2l0aXZlJylcbiAgICAgICAgICAgT1IgKHYudHJpYWdlX3N0YXRlID0gJ2FjY2VwdGVkJyBBTkQgdi5hY2NlcHRlZF91bnRpbCBJUyBOT1QgTlVMTCBBTkQgdi5hY2NlcHRlZF91bnRpbCA8PSAke25vd30pKVxuICAgIE9SREVSIEJZIHYuY3JpdGljYWxfY291bnQgREVTQywgdi5oaWdoX2NvdW50IERFU0NcbiAgICBMSU1JVCAke2xpbWl0fVxuICBgKSk7XG4gIGNvbnN0IGxpc3QgPSByb3dzT2Yocm93cykubWFwKChyKSA9PiAoe1xuICAgIHBpcGVsaW5lSWQ6IFN0cmluZyhyLnBpcGVsaW5lX2lkKSxcbiAgICBwaXBlbGluZU5hbWU6IChyLnBpcGVsaW5lX25hbWUgYXMgc3RyaW5nIHwgbnVsbCkgPz8gbnVsbCxcbiAgICBwbHVnaW46IFN0cmluZyhyLnBsdWdpbiksXG4gICAgdmVyc2lvbjogU3RyaW5nKHIucGx1Z2luX3ZlcnNpb24pLFxuICAgIHNvdXJjZTogU3RyaW5nKHIuc291cmNlKSxcbiAgICBjcml0aWNhbDogbnVtKHIuY3JpdGljYWxfY291bnQpLFxuICAgIGhpZ2g6IG51bShyLmhpZ2hfY291bnQpLFxuICAgIGZpbmRpbmdzOiAoci50b3BfZmluZGluZ3MgYXMgQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+KSA/PyBbXSxcbiAgICBmbGFnZ2VkQXQ6IGlzb09yTnVsbChyLmZsYWdnZWRfYXQpID8/IG5ldyBEYXRlKDApLnRvSVNPU3RyaW5nKCksXG4gIH0pKTtcbiAgcmV0dXJuIHtcbiAgICByb3dzOiBsaXN0LFxuICAgIGV4cG9zZWRQaXBlbGluZXM6IG5ldyBTZXQobGlzdC5tYXAoKHIpID0+IHIucGlwZWxpbmVJZCkpLnNpemUsXG4gICAgY3JpdGljYWxQaXBlbGluZXM6IG5ldyBTZXQobGlzdC5maWx0ZXIoKHIpID0+IHIuY3JpdGljYWwgPiAwKS5tYXAoKHIpID0+IHIucGlwZWxpbmVJZCkpLnNpemUsXG4gIH07XG59XG5cbi8qKiBBZG9wdGlvbiBhbmQgZGVwbG95LXRyYWNraW5nIGNvdmVyYWdlIHdpdGhpbiB0aGUgcmVwb3J0ZWQgc2NvcGUuICovXG5leHBvcnQgaW50ZXJmYWNlIEFkb3B0aW9uU3VtbWFyeSB7XG4gIGFjdGl2ZVBpcGVsaW5lczogbnVtYmVyO1xuICB0b3RhbFBpcGVsaW5lczogbnVtYmVyO1xuICAvKiogUGlwZWxpbmVzIHRoYXQgcHJvZHVjZWQgYSBkZXBsb3kgcm93IOKAlCBpLmUuIG9uZXMgRE9SQSBjYW4gc2VlLiAqL1xuICBkZXBsb3lUcmFja2VkUGlwZWxpbmVzOiBudW1iZXI7XG4gIC8qKiBCeSBgY3JlYXRpb25fc291cmNlYCwgZm9yIFwiYXJlIHRoZSBnb2xkZW4gcGF0aHMgYmVpbmcgdXNlZFwiLiAqL1xuICBieVNvdXJjZTogUmVjb3JkPHN0cmluZywgbnVtYmVyPjtcbiAgLyoqIFRlYW1zIGluIHRoZSByb2xsdXAgdGhhdCBoYWQgYWN0aXZpdHkuIDEgZm9yIGEgc2luZ2xlLW9yZyByZXBvcnQuICovXG4gIGFjdGl2ZVRlYW1zOiBudW1iZXI7XG4gIC8qKiBPbmx5IHdoZW4gdGhlIG9yZyBjb25maWd1cmVkIHRoZSBhc3N1bXB0aW9uLiBOZXZlciBwbGF0Zm9ybS1zdXBwbGllZC4gKi9cbiAgZXN0aW1hdGVkTWludXRlc1NhdmVkOiBudW1iZXIgfCBudWxsO1xufVxuXG4vKipcbiAqIEFkb3B0aW9uLCBjb3ZlcmFnZSBhbmQgY3JlYXRpb24gc291cmNlLlxuICpcbiAqIGBkZXBsb3lUcmFja2VkUGlwZWxpbmVzYCBpcyB0aGUgaG9uZXN0IGRlbm9taW5hdG9yIGZvciBldmVyeXRoaW5nIERPUkEgc2F5czogYVxuICogcGlwZWxpbmUgdGhhdCBoYXMgbm90IHJlLXN5bnRoZWQgd2l0aCBkZXBsb3kgdGFncyBwcm9kdWNlcyBubyBkZXBsb3kgcm93cywgc28gaXRcbiAqIGlzIGludmlzaWJsZSB0byBkZXBsb3ltZW50IGZyZXF1ZW5jeSDigJQgYW5kIHJlcG9ydGluZyBcIjQgZGVwbG95c1wiIHdpdGhvdXQgc2F5aW5nXG4gKiB0aGF0IDIwIG9mIDMwIHBpcGVsaW5lcyBjYW5ub3QgYmUgc2VlbiBpcyB0aGUgc2luZ2xlIG1vc3QgbWlzbGVhZGluZyB0aGluZyB0aGlzXG4gKiBmZWF0dXJlIGNvdWxkIGRvLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0QWRvcHRpb24oc2NvcGU6IEFuYWx5dGljc1Njb3BlLCB0aW1lU2F2ZWRNaW51dGVzPzogbnVtYmVyIHwgbnVsbCk6IFByb21pc2U8QWRvcHRpb25TdW1tYXJ5PiB7XG4gIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICBXSVRIIHNjb3BlZCBBUyAoXG4gICAgICBTRUxFQ1QgaWQ6OnRleHQgQVMgaWQsIG9yZ19pZCwgY3JlYXRpb25fc291cmNlXG4gICAgICBGUk9NIHBpcGVsaW5lc1xuICAgICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCBkZWxldGVkX2F0IElTIE5VTExcbiAgICApLFxuICAgIGFjdGl2ZSBBUyAoXG4gICAgICBTRUxFQ1QgRElTVElOQ1QgcGlwZWxpbmVfaWQsIG9yZ19pZFxuICAgICAgRlJPTSBleGVjdXRpb25fZGFpbHlfcm9sbHVwc1xuICAgICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCBkYXkgPj0gJHtzY29wZS5mcm9tfTo6dGltZXN0YW1wdHogQU5EIGRheSA8ICR7c2NvcGUudG99Ojp0aW1lc3RhbXB0elxuICAgICAgICBBTkQgc3RhZ2VfbmFtZSA9ICcnIEFORCBydW5zID4gMFxuICAgICksXG4gICAgdHJhY2tlZCBBUyAoXG4gICAgICBTRUxFQ1QgRElTVElOQ1QgcGlwZWxpbmVfaWRcbiAgICAgIEZST00gZG9yYV9kZXBsb3ltZW50c1xuICAgICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCBkZXBsb3llZF9hdCA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgZGVwbG95ZWRfYXQgPCAke3Njb3BlLnRvfTo6dGltZXN0YW1wdHpcbiAgICApXG4gICAgU0VMRUNUXG4gICAgICAoU0VMRUNUIENPVU5UKCopIEZST00gc2NvcGVkKTo6aW50IEFTIHRvdGFsX3BpcGVsaW5lcyxcbiAgICAgIChTRUxFQ1QgQ09VTlQoKikgRlJPTSBhY3RpdmUpOjppbnQgQVMgYWN0aXZlX3BpcGVsaW5lcyxcbiAgICAgIChTRUxFQ1QgQ09VTlQoRElTVElOQ1Qgb3JnX2lkKSBGUk9NIGFjdGl2ZSk6OmludCBBUyBhY3RpdmVfdGVhbXMsXG4gICAgICAoU0VMRUNUIENPVU5UKCopIEZST00gdHJhY2tlZCk6OmludCBBUyBkZXBsb3lfdHJhY2tlZCxcbiAgICAgIChTRUxFQ1QgQ09BTEVTQ0UoanNvbmJfb2JqZWN0X2FnZyhDT0FMRVNDRShjcmVhdGlvbl9zb3VyY2UsICd1bmtub3duJyksIG4pLCAne30nOjpqc29uYilcbiAgICAgICAgIEZST00gKFNFTEVDVCBjcmVhdGlvbl9zb3VyY2UsIENPVU5UKCopOjppbnQgQVMgbiBGUk9NIHNjb3BlZCBHUk9VUCBCWSBjcmVhdGlvbl9zb3VyY2UpIHNcbiAgICAgICkgQVMgYnlfc291cmNlXG4gIGApKTtcbiAgY29uc3QgciA9IHJvd3NPZihyb3dzKVswXSA/PyB7fTtcbiAgY29uc3QgY3JlYXRlZCA9IG51bShyLnRvdGFsX3BpcGVsaW5lcyk7XG4gIHJldHVybiB7XG4gICAgdG90YWxQaXBlbGluZXM6IGNyZWF0ZWQsXG4gICAgYWN0aXZlUGlwZWxpbmVzOiBudW0oci5hY3RpdmVfcGlwZWxpbmVzKSxcbiAgICBhY3RpdmVUZWFtczogTWF0aC5tYXgoMSwgbnVtKHIuYWN0aXZlX3RlYW1zKSksXG4gICAgZGVwbG95VHJhY2tlZFBpcGVsaW5lczogbnVtKHIuZGVwbG95X3RyYWNrZWQpLFxuICAgIGJ5U291cmNlOiAoci5ieV9zb3VyY2UgYXMgUmVjb3JkPHN0cmluZywgbnVtYmVyPikgPz8ge30sXG4gICAgLy8gT25seSB3aGVuIHRoZSBPUkcgY29uZmlndXJlZCB0aGUgYXNzdW1wdGlvbiDigJQgYSBwbGF0Zm9ybS1zdXBwbGllZCBudW1iZXJcbiAgICAvLyBoZXJlIHdvdWxkIGJlIHRoZSBwbGF0Zm9ybSBtYXJraW5nIGl0cyBvd24gaG9tZXdvcmsuXG4gICAgZXN0aW1hdGVkTWludXRlc1NhdmVkOiB0aW1lU2F2ZWRNaW51dGVzICYmIHRpbWVTYXZlZE1pbnV0ZXMgPiAwID8gY3JlYXRlZCAqIHRpbWVTYXZlZE1pbnV0ZXMgOiBudWxsLFxuICB9O1xufVxuXG4vKiogT25lIHBpcGVsaW5lJ3MgZmFpbHVyZSBzdHJlYWsgYW5kIGxhc3Qgc3VjY2Vzc2Z1bCBkYXkuICovXG5leHBvcnQgaW50ZXJmYWNlIFBpcGVsaW5lU3RyZWFrUm93IHtcbiAgcGlwZWxpbmVJZDogc3RyaW5nO1xuICAvKipcbiAgICogRmFpbHVyZXMgaW4gdGhlIHRyYWlsaW5nIHJ1biBvZiBkYXlzIG9uIHdoaWNoIE5PVEhJTkcgc3VjY2VlZGVkLlxuICAgKlxuICAgKiBBIExPV0VSIEJPVU5ELCBhbmQgZGVsaWJlcmF0ZWx5IHNvLiBUaGUgcm9sbHVwIGlzIHBlciBkYXksIHNvIHdpdGhpbiB0aGUgbW9zdFxuICAgKiByZWNlbnQgZGF5IHRoYXQgZGlkIGhhdmUgYSBzdWNjZXNzIHRoZXJlIGlzIG5vIHdheSB0byB0ZWxsIHdoZXRoZXIgdGhlIGZhaWx1cmVzXG4gICAqIGNhbWUgYmVmb3JlIG9yIGFmdGVyIGl0IOKAlCB0aGF0IGRheSBpcyBleGNsdWRlZCByYXRoZXIgdGhhbiBndWVzc2VkIGF0LiBXaGF0XG4gICAqIHN1cnZpdmVzIGlzIGEgcnVuIG9mIGZhaWx1cmVzIHdpdGggbm8gc3VjY2VzcyBhbnl3aGVyZSBiZXR3ZWVuIHRoZW0sIHdoaWNoIGlzXG4gICAqIGV4YWN0bHkgdGhlIGNsYWltIHRoZSBmaW5kaW5nIG1ha2VzLlxuICAgKi9cbiAgY29uc2VjdXRpdmVGYWlsdXJlczogbnVtYmVyO1xuICAvKiogVGhlIGxhc3QgZGF5IHdpdGggYXQgbGVhc3Qgb25lIHN1Y2Nlc3MsIG9yIG51bGwgd2l0aGluIHRoZSBsb29rYmFjay4gKi9cbiAgbGFzdFN1Y2Nlc3NBdDogRGF0ZSB8IG51bGw7XG59XG5cbi8qKlxuICogRmFpbHVyZSBzdHJlYWtzIGFuZCBsYXN0LXN1Y2Nlc3MgZGF0ZXMsIGZvciB0aGUgbmVlZHMtYXR0ZW50aW9uIHJ1bGVzLlxuICpcbiAqIFJlYWRzIHRoZSBkYWlseSByb2xsdXAsIG5vdCBgcGlwZWxpbmVfZXZlbnRzYCDigJQgdGhlIHNhbWUgcmVhc29uIGFzIGV2ZXJ5dGhpbmdcbiAqIGVsc2UgaGVyZTogYSBzdHJlYWsgdGhhdCBlbmRlZCB0aHJlZSB3ZWVrcyBhZ28gbXVzdCBzdGlsbCBiZSByZWFkYWJsZSBhZnRlclxuICogcmV0ZW50aW9uIGhhcyBzd2VwdCB0aGUgZXZlbnRzIHRoYXQgcHJvZHVjZWQgaXQuXG4gKlxuICogYGxvb2tiYWNrRGF5c2AgZXh0ZW5kcyBQQVNUIHRoZSB3aW5kb3cgb24gcHVycG9zZS4gXCJMYXN0IHN1Y2NlZWRlZCAxMiBkYXlzIGFnb1wiXG4gKiBpcyB0aGUgZmluZGluZyBhIHdlZWtseSByZXBvcnQgbW9zdCBuZWVkcywgYW5kIGEgcXVlcnkgYm91bmRlZCBieSBhIDctZGF5IHdpbmRvd1xuICogY291bGQgbmV2ZXIgcHJvZHVjZSBpdC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldFBpcGVsaW5lU3RyZWFrcyhcbiAgc2NvcGU6IEFuYWx5dGljc1Njb3BlLFxuICBsb29rYmFja0RheXMgPSA5MCxcbik6IFByb21pc2U8UGlwZWxpbmVTdHJlYWtSb3dbXT4ge1xuICBjb25zdCBzaW5jZSA9IG5ldyBEYXRlKG5ldyBEYXRlKHNjb3BlLnRvKS5nZXRUaW1lKCkgLSBsb29rYmFja0RheXMgKiA4Nl80MDBfMDAwKS50b0lTT1N0cmluZygpO1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgV0lUSCBkYXlzIEFTIChcbiAgICAgIFNFTEVDVCBwaXBlbGluZV9pZCwgZGF5LFxuICAgICAgICAgICAgIFNVTShzdWNjZWVkZWQpOjppbnQgQVMgc3VjY2VlZGVkLFxuICAgICAgICAgICAgIFNVTShmYWlsZWQpOjppbnQgICAgQVMgZmFpbGVkXG4gICAgICBGUk9NIGV4ZWN1dGlvbl9kYWlseV9yb2xsdXBzXG4gICAgICBXSEVSRSBvcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgICAgQU5EIGRheSA+PSAke3NpbmNlfTo6dGltZXN0YW1wdHogQU5EIGRheSA8ICR7c2NvcGUudG99Ojp0aW1lc3RhbXB0elxuICAgICAgR1JPVVAgQlkgcGlwZWxpbmVfaWQsIGRheVxuICAgICksXG4gICAgbGFzdF9vayBBUyAoXG4gICAgICBTRUxFQ1QgcGlwZWxpbmVfaWQsIE1BWChkYXkpIEFTIGxhc3Rfc3VjY2Vzc19kYXlcbiAgICAgIEZST00gZGF5cyBXSEVSRSBzdWNjZWVkZWQgPiAwXG4gICAgICBHUk9VUCBCWSBwaXBlbGluZV9pZFxuICAgIClcbiAgICBTRUxFQ1QgZC5waXBlbGluZV9pZCxcbiAgICAgICAgICAgbC5sYXN0X3N1Y2Nlc3NfZGF5LFxuICAgICAgICAgICBDT0FMRVNDRShTVU0oZC5mYWlsZWQpIEZJTFRFUiAoXG4gICAgICAgICAgICAgV0hFUkUgbC5sYXN0X3N1Y2Nlc3NfZGF5IElTIE5VTEwgT1IgZC5kYXkgPiBsLmxhc3Rfc3VjY2Vzc19kYXlcbiAgICAgICAgICAgKSwgMCk6OmludCBBUyBzdHJlYWtcbiAgICBGUk9NIGRheXMgZFxuICAgIExFRlQgSk9JTiBsYXN0X29rIGwgT04gbC5waXBlbGluZV9pZCA9IGQucGlwZWxpbmVfaWRcbiAgICBHUk9VUCBCWSBkLnBpcGVsaW5lX2lkLCBsLmxhc3Rfc3VjY2Vzc19kYXlcbiAgYCkpO1xuICByZXR1cm4gcm93c09mKHJvd3MpLm1hcCgocikgPT4gKHtcbiAgICBwaXBlbGluZUlkOiBTdHJpbmcoci5waXBlbGluZV9pZCksXG4gICAgY29uc2VjdXRpdmVGYWlsdXJlczogbnVtKHIuc3RyZWFrKSxcbiAgICBsYXN0U3VjY2Vzc0F0OiByLmxhc3Rfc3VjY2Vzc19kYXkgPyBuZXcgRGF0ZShTdHJpbmcoci5sYXN0X3N1Y2Nlc3NfZGF5KSkgOiBudWxsLFxuICB9KSk7XG59XG5cbi8qKlxuICogUGlwZWxpbmVzIHRoYXQgUkFOIGluIHRoZSB3aW5kb3cgYnV0IHByb2R1Y2VkIG5vIGRlcGxveSByb3dzLlxuICpcbiAqIEEgbWVhc3VyZW1lbnQgZ2FwLCBub3QgYSBmYWlsdXJlLCBhbmQgdGhlIHJlYXNvbiBhIGRlcGxveW1lbnQtZnJlcXVlbmN5IG51bWJlciBjYW5cbiAqIGJlIHF1aWV0bHkgd3Jvbmc6IGEgcGlwZWxpbmUgd2hvc2Ugc3ludGggcHJlZGF0ZXMgdGhlIGBwYi5kZXBsb3lzYCB0YWdzIGRlcGxveXNcbiAqIHBlcmZlY3RseSB3ZWxsIGFuZCBpcyBpbnZpc2libGUgdG8gZXZlcnkgRE9SQSBhZ2dyZWdhdGUuIE5vdGhpbmcgZWxzZSBpbiB0aGVcbiAqIHJlcG9ydCB3b3VsZCBzYXkgc28uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRVbnRyYWNrZWRQaXBlbGluZXMoc2NvcGU6IEFuYWx5dGljc1Njb3BlKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgV0lUSCBhY3RpdmUgQVMgKFxuICAgICAgU0VMRUNUIHBpcGVsaW5lX2lkXG4gICAgICBGUk9NIGV4ZWN1dGlvbl9kYWlseV9yb2xsdXBzXG4gICAgICBXSEVSRSBvcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgICAgQU5EIGRheSA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgZGF5IDwgJHtzY29wZS50b306OnRpbWVzdGFtcHR6XG4gICAgICAgIEFORCBzdGFnZV9uYW1lID0gJydcbiAgICAgIEdST1VQIEJZIHBpcGVsaW5lX2lkXG4gICAgICBIQVZJTkcgU1VNKHJ1bnMpID4gMFxuICAgICksXG4gICAgdHJhY2tlZCBBUyAoXG4gICAgICBTRUxFQ1QgRElTVElOQ1QgcGlwZWxpbmVfaWRcbiAgICAgIEZST00gZG9yYV9kZXBsb3ltZW50c1xuICAgICAgV0hFUkUgb3JnX2lkICR7c2NvcGUub3JnSWRzPy5sZW5ndGggPyBzcWxgPSBBTlkoJHtzY29wZS5vcmdJZHN9KWAgOiBzcWxgPSAke3Njb3BlLm9yZ0lkfWB9XG4gICAgICAgIEFORCBkZXBsb3llZF9hdCA+PSAke3Njb3BlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgZGVwbG95ZWRfYXQgPCAke3Njb3BlLnRvfTo6dGltZXN0YW1wdHpcbiAgICApXG4gICAgU0VMRUNUIGEucGlwZWxpbmVfaWQgRlJPTSBhY3RpdmUgYVxuICAgIFdIRVJFIE5PVCBFWElTVFMgKFNFTEVDVCAxIEZST00gdHJhY2tlZCB0IFdIRVJFIHQucGlwZWxpbmVfaWQgPSBhLnBpcGVsaW5lX2lkKVxuICBgKSk7XG4gIHJldHVybiByb3dzT2Yocm93cykubWFwKChyKSA9PiBTdHJpbmcoci5waXBlbGluZV9pZCkpO1xufVxuXG4vKipcbiAqIFBpcGVsaW5lcyBydW5uaW5nIGEgcGx1Z2luIHZlcnNpb24gd2l0aCBhbiBPUEVOIENyaXRpY2FsIGV4cG9zdXJlLlxuICpcbiAqIFBvaW50LWluLXRpbWUsIG5vdCB3aW5kb3dlZDogd2hhdCBtYXR0ZXJzIGlzIHdoZXRoZXIgdGhlIHBpcGVsaW5lIGlzIHJ1bm5pbmcgb25lXG4gKiBOT1cuIEEgQ3JpdGljYWwgdGhhdCB3YXMgb3BlbiBsYXN0IFR1ZXNkYXkgYW5kIGlzIGZpeGVkIHRvZGF5IGlzIG5vdCBzb21ldGhpbmcgdG9cbiAqIHB1dCBpbiBmcm9udCBvZiBhIG1hbmFnZXIsIGFuZCBgZml4ZWRfYXRgIGlzIHdoYXQgc2F5cyBzby5cbiAqXG4gKiBBbiBBQ0NFUFRFRCBleHBvc3VyZSBpcyBleGNsdWRlZC4gU29tZW9uZSB3aXRoIHRoZSBhdXRob3JpdHkgdG8gYWNjZXB0IGEgcmlzayBoYXNcbiAqIGFscmVhZHkgZGVjaWRlZCBhYm91dCBpdCBpbiB3cml0aW5nLCB3aXRoIGEgcmVhc29uIGFuZCBhIGRlYWRsaW5lOyByZS1yYWlzaW5nIGl0XG4gKiB3ZWVrbHkgaXMgaG93IGEgcmVwb3J0IHRlYWNoZXMgaXRzIHJlYWRlciB0byBza2lwIGEgc2VjdGlvbi4gVGhlIGxhcHNlIG9mIHRoYXRcbiAqIGFjY2VwdGFuY2UgaXMgYSBzZXBhcmF0ZSBmaW5kaW5nIChgbGFwc2VkQWNjZXB0YW5jZXNgKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldFZ1bG5lcmFibGVQaXBlbGluZXMoc2NvcGU6IEFuYWx5dGljc1Njb3BlKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgU0VMRUNUIERJU1RJTkNUIHBpcGVsaW5lX2lkXG4gICAgRlJPTSBwbHVnaW5fdnVsbl9leHBvc3VyZVxuICAgIFdIRVJFIG9yZ19pZCAke3Njb3BlLm9yZ0lkcz8ubGVuZ3RoID8gc3FsYD0gQU5ZKCR7c2NvcGUub3JnSWRzfSlgIDogc3FsYD0gJHtzY29wZS5vcmdJZH1gfVxuICAgICAgQU5EIGZpeGVkX2F0IElTIE5VTExcbiAgICAgIEFORCBjcml0aWNhbF9jb3VudCA+IDBcbiAgICAgIEFORCB0cmlhZ2Vfc3RhdGUgPD4gJ2FjY2VwdGVkJ1xuICBgKSk7XG4gIHJldHVybiByb3dzT2Yocm93cykubWFwKChyKSA9PiBTdHJpbmcoci5waXBlbGluZV9pZCkpO1xufVxuXG4vKipcbiAqIFdoZW4gZWFjaCBwaXBlbGluZSBpbiB0aGUgd2luZG93IHdhcyBsYXN0IGNoYW5nZWQuXG4gKlxuICogYHBpcGVsaW5lcy51cGRhdGVkX2F0YCwgbm90IGFuIGF1ZGl0IHF1ZXJ5OiB0aGUgYXVkaXQgdHJhaWwgbGl2ZXMgaW4gYW5vdGhlciBzdG9yZVxuICogd2l0aCBpdHMgb3duIHJldGVudGlvbiwgYW5kIHRoZSBvbmUgZmFjdCB0aGlzIG5lZWRzIOKAlCBcIndhcyB0aGlzIHRvdWNoZWQgc2hvcnRseVxuICogYmVmb3JlIGl0IGJyb2tlXCIg4oCUIGlzIGFscmVhZHkgb24gdGhlIHJvdywgZHVyYWJseSwgaW4gdGhlIHNhbWUgdHJhbnNhY3Rpb24gdGhlXG4gKiBjaGFuZ2Ugd2FzIG1hZGUgaW4uXG4gKlxuICogVGhlIGNvbnNlcXVlbmNlIGlzIHN0YXRlZCByYXRoZXIgdGhhbiBoaWRkZW46IGB1cGRhdGVkX2F0YCBtb3ZlcyBmb3IgQU5ZIHdyaXRlLFxuICogaW5jbHVkaW5nIGEgcmVuYW1lLiBTbyB0aGUgZmluZGluZyBzYXlzIFwiYWZ0ZXIgYSBjb25maWd1cmF0aW9uIGNoYW5nZVwiLCB3aGljaCBpc1xuICogdHJ1ZSBvZiBhIHJlbmFtZSB0b28sIGFuZCBkb2VzIG5vdCBjbGFpbSB0byBuYW1lIHRoZSBjaGFuZ2UuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRQaXBlbGluZUNvbmZpZ0NoYW5nZXMoc2NvcGU6IEFuYWx5dGljc1Njb3BlKTogUHJvbWlzZTxNYXA8c3RyaW5nLCBEYXRlPj4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgU0VMRUNUIGlkOjp0ZXh0IEFTIGlkLCB1cGRhdGVkX2F0XG4gICAgRlJPTSBwaXBlbGluZXNcbiAgICBXSEVSRSBvcmdfaWQgJHtzY29wZS5vcmdJZHM/Lmxlbmd0aCA/IHNxbGA9IEFOWSgke3Njb3BlLm9yZ0lkc30pYCA6IHNxbGA9ICR7c2NvcGUub3JnSWR9YH1cbiAgICAgIEFORCBkZWxldGVkX2F0IElTIE5VTExcbiAgICAgIEFORCB1cGRhdGVkX2F0IElTIE5PVCBOVUxMXG4gIGApKTtcbiAgY29uc3Qgb3V0ID0gbmV3IE1hcDxzdHJpbmcsIERhdGU+KCk7XG4gIGZvciAoY29uc3QgciBvZiByb3dzT2Yocm93cykpIHtcbiAgICBjb25zdCBhdCA9IHIudXBkYXRlZF9hdCA/IG5ldyBEYXRlKFN0cmluZyhyLnVwZGF0ZWRfYXQpKSA6IG51bGw7XG4gICAgaWYgKGF0ICYmICFOdW1iZXIuaXNOYU4oYXQuZ2V0VGltZSgpKSkgb3V0LnNldChTdHJpbmcoci5pZCksIGF0KTtcbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vLyDilIDilIAgSGVscGVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqIFJvd3MgZnJvbSBhIHJhdyBgZXhlY3V0ZWAsIHdoaWNoZXZlciBzaGFwZSB0aGUgZHJpdmVyIHJldHVybmVkLiAqL1xuZnVuY3Rpb24gcm93c09mKHJlc3VsdDogdW5rbm93bik6IEFycmF5PFJlY29yZDxzdHJpbmcsIHVua25vd24+PiB7XG4gIGlmIChBcnJheS5pc0FycmF5KHJlc3VsdCkpIHJldHVybiByZXN1bHQgYXMgQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+O1xuICBjb25zdCByb3dzID0gKHJlc3VsdCBhcyB7IHJvd3M/OiB1bmtub3duIH0pPy5yb3dzO1xuICByZXR1cm4gQXJyYXkuaXNBcnJheShyb3dzKSA/IHJvd3MgYXMgQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+IDogW107XG59XG5cbi8qKiBBIGNvdW50IGZyb20gYSBkcml2ZXIgdGhhdCBtYXkgaGFuZCBiYWNrIGEgc3RyaW5nIGZvciBhIGJpZ2ludC4gKi9cbmZ1bmN0aW9uIG51bSh2OiB1bmtub3duKTogbnVtYmVyIHtcbiAgY29uc3QgbiA9IHR5cGVvZiB2ID09PSAnbnVtYmVyJyA/IHYgOiBOdW1iZXIodiA/PyAwKTtcbiAgcmV0dXJuIE51bWJlci5pc0Zpbml0ZShuKSA/IG4gOiAwO1xufVxuXG5mdW5jdGlvbiBpc29Pck51bGwodjogdW5rbm93bik6IHN0cmluZyB8IG51bGwge1xuICBpZiAoIXYpIHJldHVybiBudWxsO1xuICBjb25zdCBkID0gdiBpbnN0YW5jZW9mIERhdGUgPyB2IDogbmV3IERhdGUoU3RyaW5nKHYpKTtcbiAgcmV0dXJuIE51bWJlci5pc05hTihkLmdldFRpbWUoKSkgPyBudWxsIDogZC50b0lTT1N0cmluZygpO1xufVxuIl19
|