@pipeline-builder/pipeline-data 4.1.24 → 4.1.26

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 (64) hide show
  1. package/lib/api/alert-destinations.d.ts +17 -0
  2. package/lib/api/alert-destinations.js +36 -0
  3. package/lib/api/reporting/analytics-queries.d.ts +274 -0
  4. package/lib/api/reporting/analytics-queries.js +598 -0
  5. package/lib/api/reporting/daily-rollup.d.ts +41 -0
  6. package/lib/api/reporting/daily-rollup.js +173 -0
  7. package/lib/api/reporting/deploy-rollup.d.ts +78 -0
  8. package/lib/api/reporting/deploy-rollup.js +203 -0
  9. package/lib/api/reporting/dora-scoring.d.ts +14 -0
  10. package/lib/api/reporting/dora-scoring.js +25 -1
  11. package/lib/api/reporting/dora.js +30 -6
  12. package/lib/api/reporting/failure-classifier.d.ts +54 -0
  13. package/lib/api/reporting/failure-classifier.js +179 -0
  14. package/lib/api/reporting/ingest.d.ts +2 -2
  15. package/lib/api/reporting/ingest.js +129 -4
  16. package/lib/api/reporting/last-deploy.d.ts +31 -0
  17. package/lib/api/reporting/last-deploy.js +93 -0
  18. package/lib/api/reporting/needs-attention.d.ts +93 -0
  19. package/lib/api/reporting/needs-attention.js +152 -0
  20. package/lib/api/reporting/plugin-exposure.d.ts +129 -0
  21. package/lib/api/reporting/plugin-exposure.js +312 -0
  22. package/lib/api/reporting/retention-sweep.js +9 -1
  23. package/lib/api/reporting/sql-helpers.d.ts +35 -2
  24. package/lib/api/reporting/sql-helpers.js +39 -1
  25. package/lib/api/reporting/stakeholder/compose.d.ts +115 -0
  26. package/lib/api/reporting/stakeholder/compose.js +234 -0
  27. package/lib/api/reporting/stakeholder/index.d.ts +14 -0
  28. package/lib/api/reporting/stakeholder/index.js +17 -0
  29. package/lib/api/reporting/stakeholder/period.d.ts +102 -0
  30. package/lib/api/reporting/stakeholder/period.js +273 -0
  31. package/lib/api/reporting/stakeholder/report-html.d.ts +93 -0
  32. package/lib/api/reporting/stakeholder/report-html.js +208 -0
  33. package/lib/api/reporting/stakeholder/sections.d.ts +117 -0
  34. package/lib/api/reporting/stakeholder/sections.js +249 -0
  35. package/lib/api/reporting/stakeholder/store.d.ts +450 -0
  36. package/lib/api/reporting/stakeholder/store.js +981 -0
  37. package/lib/api/reporting/stakeholder/templates.d.ts +21 -0
  38. package/lib/api/reporting/stakeholder/templates.js +104 -0
  39. package/lib/api/reporting/types.d.ts +73 -0
  40. package/lib/api/reporting/types.js +1 -1
  41. package/lib/api/reporting-service.d.ts +94 -5
  42. package/lib/api/reporting-service.js +169 -10
  43. package/lib/api/soft-delete-sweep.d.ts +11 -2
  44. package/lib/api/soft-delete-sweep.js +17 -6
  45. package/lib/database/drizzle-schema.d.ts +2244 -385
  46. package/lib/database/drizzle-schema.js +18 -7
  47. package/lib/database/index.d.ts +1 -1
  48. package/lib/database/index.js +4 -2
  49. package/lib/database/schema/compliance.d.ts +0 -682
  50. package/lib/database/schema/compliance.js +6 -66
  51. package/lib/database/schema/ecosystem.d.ts +20 -157
  52. package/lib/database/schema/ecosystem.js +23 -16
  53. package/lib/database/schema/pipeline.d.ts +200 -0
  54. package/lib/database/schema/pipeline.js +100 -1
  55. package/lib/database/schema/plugin.d.ts +20 -1
  56. package/lib/database/schema/plugin.js +13 -1
  57. package/lib/database/schema/reporting-analytics.d.ts +1212 -0
  58. package/lib/database/schema/reporting-analytics.js +250 -0
  59. package/lib/database/schema/reporting-stakeholder.d.ts +1373 -0
  60. package/lib/database/schema/reporting-stakeholder.js +251 -0
  61. package/lib/index.d.ts +10 -1
  62. package/lib/index.js +15 -1
  63. package/lib/testing/exports.json +67 -4
  64. 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