@pipeline-builder/pipeline-data 0.0.1

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 (48) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +61 -0
  3. package/drizzle.config.ts +17 -0
  4. package/lib/api/access-control-builder.d.ts +148 -0
  5. package/lib/api/access-control-builder.js +199 -0
  6. package/lib/api/crud-service.d.ts +343 -0
  7. package/lib/api/crud-service.js +849 -0
  8. package/lib/api/query-builders.d.ts +82 -0
  9. package/lib/api/query-builders.js +460 -0
  10. package/lib/api/reporting-service.d.ts +685 -0
  11. package/lib/api/reporting-service.js +1564 -0
  12. package/lib/api/soft-delete-sweep.d.ts +65 -0
  13. package/lib/api/soft-delete-sweep.js +124 -0
  14. package/lib/api/viewer-context.d.ts +29 -0
  15. package/lib/api/viewer-context.js +63 -0
  16. package/lib/core/query-filters.d.ts +263 -0
  17. package/lib/core/query-filters.js +4 -0
  18. package/lib/database/drizzle-schema.d.ts +7357 -0
  19. package/lib/database/drizzle-schema.js +66 -0
  20. package/lib/database/index.d.ts +5 -0
  21. package/lib/database/index.js +8 -0
  22. package/lib/database/migrator.d.ts +20 -0
  23. package/lib/database/migrator.js +80 -0
  24. package/lib/database/postgres-connection.d.ts +201 -0
  25. package/lib/database/postgres-connection.js +413 -0
  26. package/lib/database/retry-strategy.d.ts +68 -0
  27. package/lib/database/retry-strategy.js +122 -0
  28. package/lib/database/schema/alert.d.ts +588 -0
  29. package/lib/database/schema/alert.js +130 -0
  30. package/lib/database/schema/compliance.d.ts +3319 -0
  31. package/lib/database/schema/compliance.js +377 -0
  32. package/lib/database/schema/dashboard.d.ts +474 -0
  33. package/lib/database/schema/dashboard.js +129 -0
  34. package/lib/database/schema/message-attachment.d.ts +183 -0
  35. package/lib/database/schema/message-attachment.js +40 -0
  36. package/lib/database/schema/message.d.ts +411 -0
  37. package/lib/database/schema/message.js +114 -0
  38. package/lib/database/schema/pipeline-template.d.ts +466 -0
  39. package/lib/database/schema/pipeline-template.js +80 -0
  40. package/lib/database/schema/pipeline.d.ts +1613 -0
  41. package/lib/database/schema/pipeline.js +332 -0
  42. package/lib/database/schema/plugin.d.ts +749 -0
  43. package/lib/database/schema/plugin.js +166 -0
  44. package/lib/database/tenancy.d.ts +103 -0
  45. package/lib/database/tenancy.js +167 -0
  46. package/lib/index.d.ts +32 -0
  47. package/lib/index.js +38 -0
  48. package/package.json +161 -0
@@ -0,0 +1,1564 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { createCacheService, createLogger, errorMessage, REPORT_INTERVALS, scrubAwsIdentifiers, scrubAwsIdentifiersFromString } from '@pipeline-builder/api-core';
4
+ import { inArray, sql } from 'drizzle-orm';
5
+ import { drizzleRows } from './crud-service.js';
6
+ import { schema } from '../database/drizzle-schema.js';
7
+ import { withTenantTx, runWithTenantContext } from '../database/tenancy.js';
8
+ const logger = createLogger('reporting-service');
9
+ /**
10
+ * Defense-in-depth guard for the `DATE_TRUNC(${interval}, …)` bucket argument.
11
+ * The route layer (`parseReportInterval`) is the security boundary and already
12
+ * rejects unknown intervals; this assert makes an invalid value fail fast at the
13
+ * service boundary with a clear error instead of surfacing a raw Postgres error
14
+ * if a caller ever bypasses the route validation. Shares api-core's
15
+ * REPORT_INTERVALS allow-list so the two can't drift.
16
+ */
17
+ function assertReportInterval(interval) {
18
+ if (!REPORT_INTERVALS.includes(interval)) {
19
+ throw new Error(`Invalid report interval: ${interval}. Expected one of: ${REPORT_INTERVALS.join(', ')}`);
20
+ }
21
+ }
22
+ /**
23
+ * Scrub AWS identifiers from an optional free-form string, preserving `undefined`
24
+ * (so an absent field stays absent rather than becoming an empty scrubbed string).
25
+ * The DURABLE persistence boundary for the ingest paths — every user/AWS-derived
26
+ * string field is funneled through this before insert.
27
+ */
28
+ function scrubOptional(value) {
29
+ return value !== undefined ? scrubAwsIdentifiersFromString(value) : undefined;
30
+ }
31
+ /**
32
+ * Cache for reporting aggregations. Two tiers:
33
+ * - Inventory queries (plugin summary/distribution/versions): 5 min TTL — changes on plugin CRUD
34
+ * - Execution/build queries with date ranges: 2 min TTL — new events arrive continuously
35
+ */
36
+ const inventoryCache = createCacheService('report:inv:', parseInt(process.env.CACHE_TTL_REPORT_INVENTORY || '300', 10));
37
+ const timeseriesCache = createCacheService('report:ts:', parseInt(process.env.CACHE_TTL_REPORT_TIMESERIES || '120', 10));
38
+ /**
39
+ * The DORA headline environment. Deployment to this literal env name is the
40
+ * industry-standard DORA signal; MTTR is measured production-only, and this
41
+ * environment's card is the summary shown at the top of the panel.
42
+ */
43
+ const HEADLINE_ENV = 'production';
44
+ /**
45
+ * Incident→deploy correlation window (Phase 5). An incident is attributed to the
46
+ * most recent SUCCESSFUL deploy to its environment whose `completed_at` is within
47
+ * this many hours before the incident's `opened_at`. Default 24h; override via
48
+ * `DORA_INCIDENT_WINDOW_HOURS`. Guarded to a positive finite number.
49
+ */
50
+ const DORA_INCIDENT_WINDOW_HOURS = (() => {
51
+ const raw = Number(process.env.DORA_INCIDENT_WINDOW_HOURS);
52
+ return Number.isFinite(raw) && raw > 0 ? raw : 24;
53
+ })();
54
+ /**
55
+ * Resolve the effective incident→deploy correlation window (Phase 5b). A per-org
56
+ * override (from `dora_settings.incident_window_hours`, surfaced through the
57
+ * settings endpoint and threaded into {@link DoraOptions.incidentWindowHours})
58
+ * wins when it is a positive finite number; otherwise the global env default
59
+ * `DORA_INCIDENT_WINDOW_HOURS` applies. Centralized so the DORA correlation, the
60
+ * incidents list, and the wiring-test dry-run all agree on the same rule.
61
+ */
62
+ function resolveIncidentWindowHours(override) {
63
+ return override != null && Number.isFinite(override) && override > 0 ? override : DORA_INCIDENT_WINDOW_HOURS;
64
+ }
65
+ /**
66
+ * Reporting retention windows (Phase 7). Records in `pipeline_events`,
67
+ * `deployment_outcomes`, and `incidents` grow unbounded without a sweep, so a
68
+ * split, per-org retention purge (see {@link ReportingService.purgeExpiredReportingData})
69
+ * hard-deletes rows older than these windows, by `created_at`:
70
+ * - **Standard events** — `pipeline_events` with `environment IS NULL`
71
+ * (non-deploy STAGE/ACTION/build). High volume → short default (30 days).
72
+ * - **DORA source** — `pipeline_events` with `environment IS NOT NULL` (deploy
73
+ * stages) plus all of `deployment_outcomes` and `incidents`. Low volume →
74
+ * longer default (180 days). DORA history is therefore bounded by this window;
75
+ * the report query still hard-caps at 365 days (a longer per-org override only
76
+ * retains raw source rows, never widens a report).
77
+ * `ingest_health` and `dora_settings` are never purged. Env-overridable globals;
78
+ * a per-org override in `dora_settings` wins (see {@link resolveEventRetentionDays}).
79
+ */
80
+ const RETENTION_MIN_DAYS = 1;
81
+ const RETENTION_MAX_DAYS = 730;
82
+ /** Parse a positive-int env day-count, clamped to [MIN, MAX], else `fallback`. */
83
+ function retentionEnvDays(name, fallback) {
84
+ const raw = Number(process.env[name]);
85
+ if (!Number.isFinite(raw) || !Number.isInteger(raw) || raw < RETENTION_MIN_DAYS)
86
+ return fallback;
87
+ return Math.min(raw, RETENTION_MAX_DAYS);
88
+ }
89
+ const REPORTING_EVENT_RETENTION_DAYS = retentionEnvDays('REPORTING_EVENT_RETENTION_DAYS', 30);
90
+ const REPORTING_DORA_RETENTION_DAYS = retentionEnvDays('REPORTING_DORA_RETENTION_DAYS', 180);
91
+ /** Effective standard-event retention (days): a valid per-org override wins,
92
+ * else the global env default. Shared by the sweep + the settings surface so
93
+ * they can't drift. The `-1` unlimited sentinel (Phase 8) passes straight
94
+ * through — the sweep reads it as "keep forever, skip this org's standard
95
+ * events". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
96
+ * non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
97
+ * env default. */
98
+ export function resolveEventRetentionDays(override) {
99
+ if (override === -1)
100
+ return -1;
101
+ return override != null && Number.isInteger(override) && override >= RETENTION_MIN_DAYS
102
+ ? Math.min(override, RETENTION_MAX_DAYS) : REPORTING_EVENT_RETENTION_DAYS;
103
+ }
104
+ /** Effective DORA-source retention (days): a valid per-org override wins, else
105
+ * the global env default. The `-1` unlimited sentinel (Phase 8) passes straight
106
+ * through — the sweep reads it as "keep forever, skip this org's DORA-source
107
+ * windows". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
108
+ * non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
109
+ * env default. */
110
+ export function resolveDoraRetentionDays(override) {
111
+ if (override === -1)
112
+ return -1;
113
+ return override != null && Number.isInteger(override) && override >= RETENTION_MIN_DAYS
114
+ ? Math.min(override, RETENTION_MAX_DAYS) : REPORTING_DORA_RETENTION_DAYS;
115
+ }
116
+ /** The cutoff instant for a retention window: rows with `created_at` strictly
117
+ * before this (`< cutoff`) are expired. Deterministic for a fixed `now`. */
118
+ export function retentionCutoff(now, days) {
119
+ return new Date(now.getTime() - days * 86_400_000);
120
+ }
121
+ // DORA performance bands (thresholds from the DORA/Accelerate reports). Each
122
+ // helper returns `null` when there's no sample to classify.
123
+ /** Deployment frequency by deploys/day: elite ≥1/day, high ≥1/week, medium ≥1/month. */
124
+ function doraLevelForFrequency(perDay, deployments) {
125
+ if (deployments <= 0)
126
+ return null;
127
+ if (perDay >= 1)
128
+ return 'elite';
129
+ if (perDay >= 1 / 7)
130
+ return 'high';
131
+ if (perDay >= 1 / 30)
132
+ return 'medium';
133
+ return 'low';
134
+ }
135
+ /** Change failure rate by percent: elite ≤5%, high ≤10%, medium ≤15%. */
136
+ function doraLevelForChangeFailure(pct, total) {
137
+ if (total <= 0)
138
+ return null;
139
+ if (pct <= 5)
140
+ return 'elite';
141
+ if (pct <= 10)
142
+ return 'high';
143
+ if (pct <= 15)
144
+ return 'medium';
145
+ return 'low';
146
+ }
147
+ /** Time to restore by seconds: elite <1h, high <1 day, medium <1 week. */
148
+ function doraLevelForRestore(avgSeconds) {
149
+ if (avgSeconds == null)
150
+ return null;
151
+ if (avgSeconds < 3600)
152
+ return 'elite';
153
+ if (avgSeconds < 86400)
154
+ return 'high';
155
+ if (avgSeconds < 604800)
156
+ return 'medium';
157
+ return 'low';
158
+ }
159
+ /** Lead time by seconds: elite <1 day, high <1 week, medium <1 month. */
160
+ function doraLevelForLeadTime(medianSeconds) {
161
+ if (medianSeconds == null)
162
+ return null;
163
+ if (medianSeconds < 86400)
164
+ return 'elite';
165
+ if (medianSeconds < 604800)
166
+ return 'high';
167
+ if (medianSeconds < 2592000)
168
+ return 'medium';
169
+ return 'low';
170
+ }
171
+ /**
172
+ * Round `value` to `digits` decimal places, returning a real number. Small
173
+ * module-private helper so the DORA shaping doesn't repeat the
174
+ * `Number(Number(x).toFixed(n))` pattern (toFixed alone yields a string).
175
+ */
176
+ function round(value, digits) {
177
+ return Number(value.toFixed(digits));
178
+ }
179
+ /**
180
+ * Median of a numeric sample, or `null` when empty. Used for measured lead time
181
+ * and MTTR — both are cross-source gaps computed in JS from clamped deltas, so
182
+ * the median lives here rather than in SQL PERCENTILE_CONT (keeps the golden
183
+ * fixture tests able to prove the metric math without a live Postgres).
184
+ */
185
+ function median(values) {
186
+ if (values.length === 0)
187
+ return null;
188
+ const s = [...values].sort((a, b) => a - b);
189
+ const mid = Math.floor(s.length / 2);
190
+ return s.length % 2 === 1 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
191
+ }
192
+ /**
193
+ * Roll a group of deploy/stage events (grouped per execution) up to one terminal
194
+ * status: FAILED wins, then SUCCEEDED, else OTHER (still-running / non-terminal).
195
+ * Shared by the three per-(env|stage, execution) rollup CTEs — DORA metrics, DORA
196
+ * trend, and build health — so the precedence can't drift between them. Assumes the
197
+ * grouped event alias is `e`.
198
+ */
199
+ const terminalStatusRollup = sql `CASE
200
+ WHEN bool_or(e.status = 'FAILED') THEN 'FAILED'
201
+ WHEN bool_or(e.status = 'SUCCEEDED') THEN 'SUCCEEDED'
202
+ ELSE 'OTHER'
203
+ END`;
204
+ /** Optional `[from,to]` window on `e.started_at` for the execution reports —
205
+ * a no-op `sql` fragment when either bound is absent (all-time). Shared by the
206
+ * execution-count and per-pipeline-execution reports so they narrow identically. */
207
+ function optionalStartedAtRange(range) {
208
+ return range?.from && range?.to
209
+ ? sql `AND e.started_at >= ${range.from}::timestamptz AND e.started_at <= ${range.to}::timestamptz`
210
+ : sql ``;
211
+ }
212
+ // ─── Service ────────────────────────────────────────────
213
+ /**
214
+ * Read-only reporting service for pipeline execution and plugin inventory aggregations.
215
+ * Does not extend CrudService — reports are aggregate queries, not entity CRUD.
216
+ *
217
+ * All queries are cached in-memory to avoid repeated expensive SQL aggregations:
218
+ * - Inventory queries (plugin summary/distribution/versions): 5 min TTL
219
+ * - Timeseries queries (execution/build metrics with date ranges): 2 min TTL
220
+ */
221
+ export class ReportingService {
222
+ /** Invalidate all cached reports for an org (call after event ingest). */
223
+ async invalidateOrg(orgId) {
224
+ await Promise.all([
225
+ inventoryCache.invalidatePattern(`${orgId}:*`),
226
+ timeseriesCache.invalidatePattern(`${orgId}:*`),
227
+ ]);
228
+ }
229
+ /**
230
+ * Resolve incoming events against the pipeline registry, batch-insert the
231
+ * matched ones, and invalidate reporting caches for affected orgs.
232
+ * Events for unregistered pipeline ids are dropped (and logged at WARN
233
+ * with sample ids so an operator can see when EventBridge is delivering
234
+ * events for pipelines that haven't called POST /pipelines/registry yet).
235
+ *
236
+ * Returns counts + a sample of unregistered pipeline ids for observability.
237
+ */
238
+ async ingestEvents(events, onMetric) {
239
+ // Multi-org batch insert: the caller resolves to multiple orgs via the
240
+ // pipeline-registry lookup below, so the route layer MUST establish a
241
+ // `runWithTenantContext({ isSuperAdmin: true }, ...)` scope before calling
242
+ // this method. Under FORCE'd RLS, a single tx with `app.org_id = <one
243
+ // org>` could only write events for that org; bypass via sysadmin is
244
+ // the right gate for this server-internal cross-tenant endpoint. See
245
+ // api/reporting/src/routes/event-ingest.ts for the wrapper.
246
+ // Run insert inside the tx, but COLLECT affected orgs and invalidate
247
+ // caches AFTER the tx resolves. Keeping invalidation inside the tx held
248
+ // the pg locks open for the duration of the cache round-trips (Redis or
249
+ // in-memory invalidations are unrelated to the tx but still serialized
250
+ // its commit). Cache TTL is 2-5 min so fire-and-forget post-commit is
251
+ // an acceptable trade for tighter lock windows.
252
+ const { inserted, skipped, unregisteredPipelineIds, affectedOrgs } = await withTenantTx(async (tx) => {
253
+ // Batch-resolve all unique pipeline ids in one query
254
+ const uniqueIds = [...new Set(events.map(e => e.pipelineId))];
255
+ const registryRows = await tx
256
+ .select({
257
+ pipelineId: schema.pipelineRegistry.pipelineId,
258
+ orgId: schema.pipelineRegistry.orgId,
259
+ })
260
+ .from(schema.pipelineRegistry)
261
+ .where(inArray(schema.pipelineRegistry.pipelineId, uniqueIds));
262
+ const idMap = new Map(registryRows.map(r => [r.pipelineId, r]));
263
+ // Build insert batch (skip events whose pipeline isn't registered)
264
+ const rows = [];
265
+ let skippedLocal = 0;
266
+ const unregisteredLocal = [];
267
+ for (const event of events) {
268
+ const registry = idMap.get(event.pipelineId);
269
+ if (!registry) {
270
+ skippedLocal++;
271
+ unregisteredLocal.push(event.pipelineId);
272
+ continue;
273
+ }
274
+ rows.push({
275
+ // registry.pipelineId === event.pipelineId; use the registry's so the
276
+ // FK is always a row that exists, and pull orgId from the registry for
277
+ // tenancy (never trust the caller's claimed org).
278
+ pipelineId: registry.pipelineId,
279
+ orgId: registry.orgId,
280
+ eventSource: event.eventSource,
281
+ eventType: event.eventType,
282
+ status: event.status,
283
+ // executionId is an AWS-ASSIGNED identifier (a UUID), not free-form
284
+ // user text, so it can't carry an account id — and scrubbing a UUID
285
+ // that happens to contain a 12-digit run would corrupt the correlation
286
+ // key. Left intact by design.
287
+ executionId: event.executionId,
288
+ // stageName/actionName are USER-AUTHORED pipeline-structure names
289
+ // promoted from the CodePipeline detail — same untrusted-AWS-derived
290
+ // origin as errorMessage below, so scrub them at this persistence
291
+ // boundary too (a stage named with an ARN/12-digit id must not persist).
292
+ stageName: scrubOptional(event.stageName),
293
+ actionName: scrubOptional(event.actionName),
294
+ // HARD CONSTRAINT: an AWS account id must NEVER be persisted. This is
295
+ // the DURABLE persistence boundary and must not trust upstream:
296
+ // CodePipeline/CodeBuild failure detail & messages routinely carry
297
+ // ARNs (arn:aws:…:<account-id>:…) and bare 12-digit account ids. Scrub
298
+ // both free-form fields here before insert (per-event, non-mutating).
299
+ errorMessage: scrubOptional(event.errorMessage),
300
+ startedAt: event.startedAt ? new Date(event.startedAt) : undefined,
301
+ completedAt: event.completedAt ? new Date(event.completedAt) : undefined,
302
+ durationMs: event.durationMs,
303
+ // Deploy-attribution fields — scrubbed like the other free-form strings
304
+ // so an ARN/account id can never enter via a ref or environment name.
305
+ commitSha: scrubOptional(event.commitSha),
306
+ commitRef: scrubOptional(event.commitRef),
307
+ environment: scrubOptional(event.environment),
308
+ // Measured lead time (Phase 4): oldest unshipped commit time + count.
309
+ // commitTimestamp is an AWS/SCM-derived instant (never account-id
310
+ // shaped); commitCount is a plain integer. Neither needs scrubbing.
311
+ commitTimestamp: event.commitTimestamp ? new Date(event.commitTimestamp) : undefined,
312
+ commitCount: event.commitCount,
313
+ detail: event.detail !== undefined
314
+ ? scrubAwsIdentifiers(event.detail)
315
+ : undefined,
316
+ });
317
+ // Phase 3b: fan terminal STAGE outcomes into Prometheus counters via the
318
+ // route's hook (pipeline-data can't import api-server's registry). Emit
319
+ // per registered stage event with a terminal succeeded/failed result;
320
+ // a non-null environment additionally marks it a deploy result.
321
+ if (onMetric && event.eventType === 'STAGE' && (event.status === 'SUCCEEDED' || event.status === 'FAILED')) {
322
+ onMetric({
323
+ pipelineId: registry.pipelineId,
324
+ orgId: registry.orgId,
325
+ stage: scrubOptional(event.stageName) ?? '',
326
+ environment: scrubOptional(event.environment) ?? null,
327
+ result: event.status === 'SUCCEEDED' ? 'succeeded' : 'failed',
328
+ });
329
+ }
330
+ }
331
+ // SQS is at-least-once, so EventBridge can deliver the same state-change
332
+ // twice. `onConflictDoNothing` + the partial unique index on
333
+ // (pipeline_id, execution_id, event_type, status, stage_name, action_name)
334
+ // makes re-delivery idempotent. `returning` gives the REAL inserted set so
335
+ // counts + cache invalidation ignore duplicates.
336
+ const insertedRows = rows.length > 0
337
+ ? await tx.insert(schema.pipelineEvent).values(rows)
338
+ .onConflictDoNothing()
339
+ .returning({ orgId: schema.pipelineEvent.orgId })
340
+ : [];
341
+ return {
342
+ inserted: insertedRows.length,
343
+ skipped: skippedLocal,
344
+ unregisteredPipelineIds: unregisteredLocal,
345
+ affectedOrgs: [...new Set(insertedRows.map(r => r.orgId))],
346
+ };
347
+ });
348
+ // Surface the silent skip: an unregistered pipeline id usually means the
349
+ // pipeline hasn't called POST /pipelines/registry yet (or its
350
+ // pb.pipeline-id tag is missing/unreadable by the Lambda). Logging it
351
+ // makes a broken join visible instead of looking like "no activity".
352
+ if (unregisteredPipelineIds.length > 0) {
353
+ logger.warn('Pipeline events skipped: pipeline id not found in registry', {
354
+ count: unregisteredPipelineIds.length,
355
+ sample: unregisteredPipelineIds.slice(0, 3),
356
+ });
357
+ }
358
+ // Post-commit cache invalidation. Fire-and-forget with logging — TTL is
359
+ // short enough that a missed invalidation self-heals.
360
+ if (affectedOrgs.length > 0) {
361
+ void Promise.all(affectedOrgs.map((org) => this.invalidateOrg(org).catch((err) => {
362
+ logger.warn('Reporting cache invalidation failed', { orgId: org, error: errorMessage(err) });
363
+ })));
364
+ }
365
+ return { inserted, skipped, unregisteredPipelineIds };
366
+ }
367
+ // ── Category 1: Pipeline Execution & Performance ──
368
+ /**
369
+ * Build the org-scope predicate for a report query. With `orgIds` (the
370
+ * org → team rollup — a parent's `[self, ...descendants]`) it becomes an
371
+ * `IN (...)` over the subtree; otherwise the single-org `= $org`. Returns a
372
+ * `multi` flag so callers can run multi-org reads under sysadmin context
373
+ * (the subtree spans orgs outside the request's RLS scope) and salt the
374
+ * cache key.
375
+ */
376
+ orgScope(orgId, orgIds) {
377
+ // Defense-in-depth: the multi-org rollup runs under sysadmin context (RLS
378
+ // OFF, see runReport), so `orgIds` is the ONLY tenancy gate on the read. The
379
+ // route resolves it from the platform's authoritative descendants endpoint as
380
+ // `[self, ...descendants]`, but validate the subtree defensively here before
381
+ // the bypass: the requesting (anchor) org MUST appear in the set. A set that
382
+ // omits the caller's own org is a malformed/spoofed rollup — collapse to the
383
+ // safe single-org scope rather than reading an arbitrary org under sysadmin.
384
+ // (Full descendant-membership validation needs the org tree, which this
385
+ // service does not hold — see the report FLAG.)
386
+ const anchor = orgId.toLowerCase();
387
+ const provided = (orgIds ?? []).filter((id) => typeof id === 'string' && id.length > 0);
388
+ const anchored = provided.some((id) => id.toLowerCase() === anchor);
389
+ if (provided.length > 0 && !anchored) {
390
+ logger.warn('Reporting rollup rejected: requesting org absent from resolved subtree; using single-org scope', {
391
+ orgId: anchor, subtreeSize: provided.length,
392
+ });
393
+ }
394
+ const ids = provided.length > 0 && anchored ? provided : [orgId];
395
+ const multi = ids.length > 1;
396
+ const pred = multi
397
+ ? sql `IN (${sql.join(ids.map((id) => sql `${id}`), sql `, `)})`
398
+ : sql `= ${ids[0]}`;
399
+ return { pred, multi };
400
+ }
401
+ /**
402
+ * Run a report read. Single-org reads use the per-org cache (invalidated on
403
+ * that org's event ingest). Rollup (multi-org) reads **bypass the cache** and
404
+ * run under sysadmin context: they're admin-only and lower-frequency, and a
405
+ * child org's event ingest can't invalidate a parent's rollup entry (reporting
406
+ * has no org tree), so caching them would serve stale aggregates. Always fresh.
407
+ */
408
+ runReport(cacheKey, multi, exec) {
409
+ return multi
410
+ ? runWithTenantContext({ isSuperAdmin: true }, exec)
411
+ : timeseriesCache.getOrSet(cacheKey, exec);
412
+ }
413
+ /** 1.1 Execution count per pipeline with status breakdown. */
414
+ async getExecutionCount(orgId, orgIds, range) {
415
+ const { pred, multi } = this.orgScope(orgId, orgIds);
416
+ // Optional [from,to] window on the execution's started_at, mirroring the
417
+ // sibling timeseries reports so the dashboard date-range picker narrows the
418
+ // count too (an empty range = all-time, preserving the prior behavior). The
419
+ // range rides the JOIN so pipelines with no in-window events still list
420
+ // (LEFT semantics preserved via the inner-join filter — a pipeline with zero
421
+ // matching events drops from the count, same as before for its window).
422
+ const rangeClause = optionalStartedAtRange(range);
423
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
424
+ SELECT
425
+ p.id, p.project, p.organization, p.pipeline_name,
426
+ COUNT(*)::int AS total,
427
+ COUNT(*) FILTER (WHERE e.status = 'SUCCEEDED')::int AS succeeded,
428
+ COUNT(*) FILTER (WHERE e.status = 'FAILED')::int AS failed,
429
+ COUNT(*) FILTER (WHERE e.status = 'CANCELED')::int AS canceled,
430
+ MIN(e.started_at)::text AS first_execution,
431
+ MAX(e.started_at)::text AS last_execution
432
+ FROM ${schema.pipeline} p
433
+ JOIN ${schema.pipelineEvent} e ON e.pipeline_id = p.id
434
+ AND e.event_type = 'PIPELINE' AND e.status != 'STARTED'
435
+ ${rangeClause}
436
+ WHERE p.org_id ${pred} AND p.is_active = true
437
+ GROUP BY p.id
438
+ ORDER BY total DESC
439
+ `).then(r => drizzleRows(r.rows)));
440
+ return this.runReport(`${orgId}:exec-count:${range?.from ?? ''}:${range?.to ?? ''}`, multi, exec);
441
+ }
442
+ /**
443
+ * 1.1b Per-pipeline execution history — DISTINCT executions for one pipeline,
444
+ * newest first. Groups all events by `execution_id` in a single scan and
445
+ * rolls each execution up to one row:
446
+ * - status: derived from the PIPELINE-type events. FAILED wins, then
447
+ * SUCCEEDED, then CANCELED; an execution with no terminal PIPELINE event
448
+ * is still `in-progress`.
449
+ * - startedAt/endedAt/durationMs: from the PIPELINE lifecycle events.
450
+ * - failingStage/failingAction: the first FAILED STAGE/ACTION event (cheap —
451
+ * same scan, no extra query).
452
+ *
453
+ * ORG-SCOPING: identical to the sibling execution reports — joins the pipeline
454
+ * registry table (`pipeline`) and gates on `p.org_id ${pred}`, where `pred` is
455
+ * the single-org `= $org` or (with a rollup) an `IN (...)` over the org→team
456
+ * subtree. A pipelineId belonging to another org yields zero rows.
457
+ */
458
+ async listPipelineExecutions(orgId, pipelineId, orgIds, range, limit = 50) {
459
+ const { pred, multi } = this.orgScope(orgId, orgIds);
460
+ const rangeClause = optionalStartedAtRange(range);
461
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
462
+ SELECT
463
+ e.execution_id,
464
+ CASE
465
+ WHEN bool_or(e.event_type = 'PIPELINE' AND e.status = 'FAILED') THEN 'failed'
466
+ WHEN bool_or(e.event_type = 'PIPELINE' AND e.status = 'SUCCEEDED') THEN 'succeeded'
467
+ WHEN bool_or(e.event_type = 'PIPELINE' AND e.status = 'CANCELED') THEN 'canceled'
468
+ ELSE 'in-progress'
469
+ END AS status,
470
+ MIN(e.started_at) FILTER (WHERE e.event_type = 'PIPELINE')::text AS started_at,
471
+ MAX(e.completed_at) FILTER (WHERE e.event_type = 'PIPELINE')::text AS ended_at,
472
+ MAX(e.duration_ms) FILTER (WHERE e.event_type = 'PIPELINE')::int AS duration_ms,
473
+ (ARRAY_AGG(e.stage_name) FILTER (WHERE e.event_type = 'STAGE' AND e.status = 'FAILED'))[1] AS failing_stage,
474
+ (ARRAY_AGG(e.action_name) FILTER (WHERE e.event_type = 'ACTION' AND e.status = 'FAILED'))[1] AS failing_action
475
+ FROM ${schema.pipelineEvent} e
476
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
477
+ WHERE p.org_id ${pred} AND e.pipeline_id = ${pipelineId} AND e.execution_id IS NOT NULL
478
+ ${rangeClause}
479
+ GROUP BY e.execution_id
480
+ ORDER BY MAX(e.created_at) DESC
481
+ LIMIT ${limit}
482
+ `).then(r => drizzleRows(r.rows)));
483
+ return this.runReport(`${orgId}:pipeline-executions:${pipelineId}:${range?.from ?? ''}:${range?.to ?? ''}:${limit}`, multi, exec);
484
+ }
485
+ /** 1.2 Success rate over time for an org. */
486
+ async getSuccessRate(orgId, interval, from, to, orgIds) {
487
+ assertReportInterval(interval);
488
+ const { pred, multi } = this.orgScope(orgId, orgIds);
489
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
490
+ SELECT
491
+ DATE_TRUNC(${interval}, e.started_at)::text AS period,
492
+ COUNT(*) FILTER (WHERE e.status = 'SUCCEEDED')::int AS succeeded,
493
+ COUNT(*) FILTER (WHERE e.status = 'FAILED')::int AS failed,
494
+ COUNT(*) FILTER (WHERE e.status = 'CANCELED')::int AS canceled,
495
+ ROUND(COUNT(*) FILTER (WHERE e.status = 'SUCCEEDED')::numeric
496
+ / NULLIF(COUNT(*), 0) * 100, 1)::float AS success_pct
497
+ FROM ${schema.pipelineEvent} e
498
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
499
+ WHERE p.org_id ${pred} AND e.event_type = 'PIPELINE'
500
+ AND e.status IN ('SUCCEEDED', 'FAILED', 'CANCELED')
501
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
502
+ GROUP BY period ORDER BY period
503
+ `).then(r => drizzleRows(r.rows)));
504
+ return this.runReport(`${orgId}:success-rate:${interval}:${from}:${to}`, multi, exec);
505
+ }
506
+ /** 1.3 Average duration per pipeline. */
507
+ async getAverageDuration(orgId, from, to, orgIds) {
508
+ const { pred, multi } = this.orgScope(orgId, orgIds);
509
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
510
+ SELECT
511
+ p.id, p.project, p.pipeline_name,
512
+ AVG(e.duration_ms)::int AS avg_ms,
513
+ MIN(e.duration_ms)::int AS min_ms,
514
+ MAX(e.duration_ms)::int AS max_ms,
515
+ PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY e.duration_ms)::int AS p95_ms,
516
+ COUNT(*)::int AS executions
517
+ FROM ${schema.pipelineEvent} e
518
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
519
+ WHERE p.org_id ${pred} AND e.event_type = 'PIPELINE' AND e.duration_ms IS NOT NULL
520
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
521
+ GROUP BY p.id ORDER BY avg_ms DESC
522
+ `).then(r => drizzleRows(r.rows)));
523
+ return this.runReport(`${orgId}:avg-duration:${from}:${to}`, multi, exec);
524
+ }
525
+ /** 1.5 Stage failure heatmap — which stages fail most. */
526
+ async getStageFailures(orgId, from, to, orgIds) {
527
+ const { pred, multi } = this.orgScope(orgId, orgIds);
528
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
529
+ SELECT
530
+ e.stage_name,
531
+ COUNT(*) FILTER (WHERE e.status = 'FAILED')::int AS failures,
532
+ COUNT(*)::int AS total,
533
+ ROUND(COUNT(*) FILTER (WHERE e.status = 'FAILED')::numeric
534
+ / NULLIF(COUNT(*), 0) * 100, 1)::float AS failure_pct
535
+ FROM ${schema.pipelineEvent} e
536
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
537
+ WHERE p.org_id ${pred} AND e.event_type = 'STAGE' AND e.stage_name IS NOT NULL
538
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
539
+ GROUP BY e.stage_name ORDER BY failures DESC
540
+ `).then(r => drizzleRows(r.rows)));
541
+ return this.runReport(`${orgId}:stage-failures:${from}:${to}`, multi, exec);
542
+ }
543
+ /**
544
+ * Distinct deploy `environment` values observed in the window — powers the
545
+ * DORA environment-scope datalist. Org-scoped + rollup-aware like the sibling
546
+ * reports; only non-null environments (i.e. deploy-attributed executions).
547
+ */
548
+ async getReportEnvironments(orgId, from, to, orgIds) {
549
+ const { pred, multi } = this.orgScope(orgId, orgIds);
550
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
551
+ SELECT DISTINCT e.environment AS environment
552
+ FROM ${schema.pipelineEvent} e
553
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
554
+ WHERE p.org_id ${pred} AND e.environment IS NOT NULL
555
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
556
+ ORDER BY environment
557
+ `).then(r => drizzleRows(r.rows).map((row) => row.environment)));
558
+ return this.runReport(`${orgId}:report-envs:${from}:${to}`, multi, exec);
559
+ }
560
+ /** 1.6 Stage bottlenecks — slowest stages per pipeline. */
561
+ async getStageBottlenecks(orgId, from, to, orgIds) {
562
+ const { pred, multi } = this.orgScope(orgId, orgIds);
563
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
564
+ SELECT
565
+ p.id, p.pipeline_name, e.stage_name,
566
+ AVG(e.duration_ms)::int AS avg_ms,
567
+ MAX(e.duration_ms)::int AS max_ms
568
+ FROM ${schema.pipelineEvent} e
569
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
570
+ WHERE p.org_id ${pred} AND e.event_type = 'STAGE' AND e.duration_ms IS NOT NULL
571
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
572
+ GROUP BY p.id, e.stage_name ORDER BY avg_ms DESC
573
+ `).then(r => drizzleRows(r.rows)));
574
+ return this.runReport(`${orgId}:stage-bottlenecks:${from}:${to}`, multi, exec);
575
+ }
576
+ /** 1.7 Action failure rate — which plugin steps fail most. */
577
+ async getActionFailures(orgId, from, to, orgIds) {
578
+ const { pred, multi } = this.orgScope(orgId, orgIds);
579
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
580
+ SELECT
581
+ e.action_name,
582
+ COUNT(*) FILTER (WHERE e.status = 'FAILED')::int AS failures,
583
+ COUNT(*)::int AS total,
584
+ ROUND(COUNT(*) FILTER (WHERE e.status = 'FAILED')::numeric
585
+ / NULLIF(COUNT(*), 0) * 100, 1)::float AS failure_pct
586
+ FROM ${schema.pipelineEvent} e
587
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
588
+ WHERE p.org_id ${pred} AND e.event_type = 'ACTION' AND e.action_name IS NOT NULL
589
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
590
+ GROUP BY e.action_name ORDER BY failures DESC
591
+ `).then(r => drizzleRows(r.rows)));
592
+ return this.runReport(`${orgId}:action-failures:${from}:${to}`, multi, exec);
593
+ }
594
+ /**
595
+ * 1.8 Error categorization — group failure messages. Execution report, so
596
+ * rollup-aware exactly like the sibling execution reports: with `orgIds`
597
+ * (the org→team subtree) the org gate becomes an `IN (...)` and the read runs
598
+ * fresh under sysadmin via `runReport`; single-org reads keep the per-org cache.
599
+ */
600
+ async getErrors(orgId, from, to, limit = 20, orgIds) {
601
+ const { pred, multi } = this.orgScope(orgId, orgIds);
602
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
603
+ SELECT
604
+ SUBSTRING(e.error_message FROM 1 FOR 200) AS error_pattern,
605
+ COUNT(*)::int AS occurrences,
606
+ COUNT(DISTINCT e.pipeline_id)::int AS affected_pipelines,
607
+ MAX(e.started_at)::text AS last_seen
608
+ FROM ${schema.pipelineEvent} e
609
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
610
+ WHERE p.org_id ${pred} AND e.status = 'FAILED' AND e.error_message IS NOT NULL
611
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
612
+ GROUP BY error_pattern ORDER BY occurrences DESC
613
+ LIMIT ${limit}
614
+ `).then(r => drizzleRows(r.rows)));
615
+ return this.runReport(`${orgId}:errors:${from}:${to}:${limit}`, multi, exec);
616
+ }
617
+ /**
618
+ * 1.9 DORA metrics over a [from,to] deploy-completion window, org-scoped +
619
+ * rollup-aware. DEPLOY-BASIS ONLY — every metric derives from real deploy-stage
620
+ * executions (`event_type='STAGE'` with a non-null `environment`, set by the
621
+ * forwarder for the stages a user declared in `pb.deploys`). No run-based
622
+ * fallback: a pipeline with no deploy stage produces no DORA data.
623
+ *
624
+ * Four scoped SQL scans run inside one tx (each org-gated by `p.org_id ${pred}`
625
+ * / `o.org_id ${pred}` so a rollup passes the org→team subtree and a foreign
626
+ * org's rows never enter): (1) terminal deploy rows PER (execution, env) — one
627
+ * "deployment" = one execution reaching an env (D1) — → DF / deploy-time CFR /
628
+ * measured lead time (lead time joins the execution's earliest commit, D2);
629
+ * the scan also reaches back `windowHours` before `from` so incidents opened
630
+ * near the window start can correlate to a just-prior deploy; (2) `deployment_outcomes`
631
+ * in-window → post-deploy CFR component; (3) production restored/failed
632
+ * outcomes joined to their deploy → MTTR; (4) registry vs deploying → coverage.
633
+ * The cross-source medians (lead time, MTTR) are computed in JS from clamped
634
+ * (≥0) deltas.
635
+ *
636
+ * @param opts.pipelineId restrict to one pipeline (per-pipeline DORA)
637
+ * @param opts.environment restrict to one deploy environment
638
+ */
639
+ async getDoraMetrics(orgId, from, to, orgIds, opts = {}) {
640
+ const { pred, multi } = this.orgScope(orgId, orgIds);
641
+ const { pipelineId, environment } = opts;
642
+ const pipelineClause = pipelineId ? sql `AND e.pipeline_id = ${pipelineId}` : sql ``;
643
+ const pipelineClauseR = pipelineId ? sql `AND r.pipeline_id = ${pipelineId}` : sql ``;
644
+ const envClause = environment ? sql `AND e.environment = ${environment}` : sql ``;
645
+ const outcomeEnvClause = environment ? sql `AND o.environment = ${environment}` : sql ``;
646
+ const incidentEnvClause = environment ? sql `AND i.environment = ${environment}` : sql ``;
647
+ // Effective incident-correlation window (per-org override or the global
648
+ // default). Computed here (not just in shapeDora) because the deploy scan
649
+ // needs it for BOTH the correlation look-back lower bound AND the cache key.
650
+ const effectiveWindowHours = resolveIncidentWindowHours(opts.incidentWindowHours);
651
+ // Correlation look-back: incidents opened just inside `from` can attribute to
652
+ // a deploy that completed up to `windowHours` BEFORE `from` (mirrors the
653
+ // LATERAL look-back in listIncidents). Widen the deploy scan's lower bound to
654
+ // `from − windowHours`; DF/CFR/lead still count only the in-`from`..`to`
655
+ // subset (flagged by `in_window`), while correlation sees the wider set.
656
+ const fromMs = Date.parse(from);
657
+ const lookbackFrom = Number.isFinite(fromMs)
658
+ ? new Date(fromMs - effectiveWindowHours * 3600 * 1000).toISOString()
659
+ : from;
660
+ // (1) Terminal deploy rows: PER-EXECUTION (D1), one row per (environment,
661
+ // execution) — every deploy STAGE targeting that env within the execution is
662
+ // rolled up to a terminal status (FAILED wins, then SUCCEEDED). A single
663
+ // execution with two deploy stages to the same env is ONE deployment, not two.
664
+ // completed_at = the deploy's completion (MAX over its stages). `commit_ts`
665
+ // (D2) is the execution's EARLIEST commit time across ALL its events (commit
666
+ // enrichment rides the PIPELINE/source event where environment IS NULL, never
667
+ // the deploy STAGE row), joined by execution_id. `in_window` marks whether the
668
+ // deploy completed inside [from,to] (vs. a look-back-only row kept solely for
669
+ // incident correlation). execution_id is projected so post-deploy failures
670
+ // (manual outcomes + correlated incidents) can dedup against their deploy.
671
+ const deploySql = sql `
672
+ WITH deploy_stages AS (
673
+ SELECT
674
+ e.environment AS environment,
675
+ e.execution_id AS execution_id,
676
+ ${terminalStatusRollup} AS status,
677
+ MAX(e.completed_at) AS completed_at
678
+ FROM ${schema.pipelineEvent} e
679
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
680
+ WHERE p.org_id ${pred} AND e.event_type = 'STAGE' AND e.environment IS NOT NULL
681
+ ${pipelineClause} ${envClause}
682
+ AND e.completed_at >= ${lookbackFrom}::timestamptz AND e.completed_at <= ${to}::timestamptz
683
+ GROUP BY e.environment, e.execution_id
684
+ ),
685
+ exec_commits AS (
686
+ SELECT c.execution_id AS execution_id, MIN(c.commit_timestamp) AS commit_ts
687
+ FROM ${schema.pipelineEvent} c
688
+ JOIN ${schema.pipeline} p ON p.id = c.pipeline_id
689
+ WHERE p.org_id ${pred} AND c.commit_timestamp IS NOT NULL
690
+ AND c.execution_id IN (SELECT execution_id FROM deploy_stages)
691
+ GROUP BY c.execution_id
692
+ )
693
+ SELECT
694
+ ds.environment AS environment,
695
+ ds.execution_id AS execution_id,
696
+ ds.status AS status,
697
+ ds.completed_at::text AS completed_at,
698
+ (ds.completed_at >= ${from}::timestamptz) AS in_window,
699
+ ec.commit_ts::text AS commit_ts
700
+ FROM deploy_stages ds
701
+ LEFT JOIN exec_commits ec ON ec.execution_id = ds.execution_id`;
702
+ // (2) Post-deploy outcome markers in-window (per env). Post-deploy failures
703
+ // (outcome='failed') add to the CFR numerator without changing attempts.
704
+ // execution_id lets the CFR dedup a manual failure against an incident that
705
+ // correlates to the same deploy (no double-count).
706
+ const outcomeSql = sql `
707
+ SELECT o.environment AS environment, o.outcome AS outcome, o.execution_id AS execution_id
708
+ FROM ${schema.deploymentOutcome} o
709
+ WHERE o.org_id ${pred} ${outcomeEnvClause}
710
+ AND o.at >= ${from}::timestamptz AND o.at <= ${to}::timestamptz`;
711
+ // (3) MTTR — PRODUCTION-ONLY, independent of the env filter. Every production
712
+ // outcome in-window; for a 'restored' one, correlate to its deploy's
713
+ // completion via execution_id to measure restored.at − deployed.completed_at.
714
+ // execution_id is projected so an incident-sourced recovery for the same
715
+ // deploy takes precedence over the manual restored gap.
716
+ const mttrSql = sql `
717
+ SELECT
718
+ o.outcome AS outcome,
719
+ o.execution_id AS execution_id,
720
+ o.at::text AS restored_at,
721
+ (SELECT MAX(d.completed_at) FROM ${schema.pipelineEvent} d
722
+ JOIN ${schema.pipeline} p2 ON p2.id = d.pipeline_id
723
+ WHERE p2.org_id ${pred} AND d.execution_id = o.execution_id
724
+ AND d.event_type = 'STAGE' AND d.environment = ${HEADLINE_ENV})::text AS deployed_at
725
+ FROM ${schema.deploymentOutcome} o
726
+ WHERE o.org_id ${pred} AND o.environment = ${HEADLINE_ENV}
727
+ AND o.at >= ${from}::timestamptz AND o.at <= ${to}::timestamptz`;
728
+ // (5) Incidents opened in-window (per env). Correlated in JS to the most
729
+ // recent successful deploy → automated post-deploy failure (CFR) + real
730
+ // recovery time (resolved_at − opened_at) for MTTR (production).
731
+ const incidentSql = sql `
732
+ SELECT i.environment AS environment, i.opened_at::text AS opened_at, i.resolved_at::text AS resolved_at
733
+ FROM ${schema.incident} i
734
+ WHERE i.org_id ${pred} ${incidentEnvClause}
735
+ AND i.opened_at >= ${from}::timestamptz AND i.opened_at <= ${to}::timestamptz`;
736
+ // (4) Coverage: registered pipelines vs those that actually deployed in-window.
737
+ const coverageSql = sql `
738
+ SELECT
739
+ (SELECT COUNT(*)::int FROM ${schema.pipelineRegistry} r
740
+ WHERE r.org_id ${pred} ${pipelineClauseR}) AS registered,
741
+ (SELECT COUNT(DISTINCT e.pipeline_id)::int FROM ${schema.pipelineEvent} e
742
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
743
+ WHERE p.org_id ${pred} AND e.event_type = 'STAGE' AND e.environment IS NOT NULL
744
+ ${pipelineClause} ${envClause}
745
+ AND e.completed_at >= ${from}::timestamptz AND e.completed_at <= ${to}::timestamptz) AS deploying`;
746
+ const exec = () => withTenantTx(async (tx) => {
747
+ // Scan order is load-bearing for the generated-SQL tests: 0=deploy,
748
+ // 1=outcomes, 2=mttr, 3=coverage, 4=incidents (appended last so the
749
+ // earlier indices stay stable).
750
+ const deployRows = drizzleRows((await tx.execute(deploySql)).rows);
751
+ const outcomeRows = drizzleRows((await tx.execute(outcomeSql)).rows);
752
+ const mttrRows = drizzleRows((await tx.execute(mttrSql)).rows);
753
+ const coverageRow = drizzleRows((await tx.execute(coverageSql)).rows)[0];
754
+ const incidentRows = drizzleRows((await tx.execute(incidentSql)).rows);
755
+ return this.shapeDora(deployRows, outcomeRows, mttrRows, coverageRow, incidentRows, from, to, {
756
+ pipelineId, environment,
757
+ }, effectiveWindowHours);
758
+ });
759
+ // Cache key MUST include the effective incident window: the scorecard reads
760
+ // /dora with the org's default window while an explicit /dora call may pass an
761
+ // override — different windows yield different CFR/MTTR, so they must not
762
+ // collide on the same key.
763
+ const key = `${orgId}:dora:${from}:${to}:${pipelineId ?? ''}:${environment ?? ''}:${effectiveWindowHours}`;
764
+ return this.runReport(key, multi, exec);
765
+ }
766
+ /**
767
+ * Shape the raw DORA scan rows into the public {@link DoraMetrics}. Buckets the
768
+ * terminal deploy rows by environment (DF / deploy-time CFR / measured lead),
769
+ * folds in the post-deploy failure counts (manual outcomes + webhook-ingested
770
+ * incidents, deduped by deploy execution), computes production MTTR from both
771
+ * sources (incident `resolved_at − opened_at` taking precedence over the manual
772
+ * `restored − deployed`), and reconciles coverage — all cross-source medians
773
+ * over deltas clamped ≥0.
774
+ *
775
+ * Phase 5 incident correlation: each incident is attributed to the most recent
776
+ * SUCCESSFUL deploy to its environment with `completed_at ≤ opened_at` within
777
+ * {@link DORA_INCIDENT_WINDOW_HOURS}. That deploy is a post-deploy failure; an
778
+ * uncorrelated incident (no eligible deploy) contributes nothing.
779
+ */
780
+ shapeDora(deployRows, outcomeRows, mttrRows, coverageRow, incidentRows, from, to, filters, windowHours = DORA_INCIDENT_WINDOW_HOURS) {
781
+ // Window length in days (floored at 1 so a sub-day window yields the count as
782
+ // the rate, never /0 or an extrapolation). Guard unparseable dates → 1 day.
783
+ const spanDays = (Date.parse(to) - Date.parse(from)) / 86400000;
784
+ const days = Number.isFinite(spanDays) ? Math.max(spanDays, 1) : 1;
785
+ // Window end instant for MTTR right-censoring (a resolution after `to` is
786
+ // unobserved). +Infinity when `to` is unparseable ⇒ no censoring (fail-open).
787
+ const toParsed = Date.parse(to);
788
+ const toMs = Number.isFinite(toParsed) ? toParsed : Number.POSITIVE_INFINITY;
789
+ const envs = new Map();
790
+ const accFor = (env) => {
791
+ let a = envs.get(env);
792
+ if (!a) {
793
+ a = { deployments: 0, deployTimeFailures: 0, attempts: 0, leadGaps: [] };
794
+ envs.set(env, a);
795
+ }
796
+ return a;
797
+ };
798
+ // A deploy row counts toward DF/CFR/lead only if it completed inside [from,to].
799
+ // Look-back-only rows (in_window=false, kept for incident correlation) are
800
+ // skipped here. Absent in a fixture ⇒ in-window (unit tests omit the flag).
801
+ const isInWindow = (row) => row.in_window == null ? true : (row.in_window === true || row.in_window === 't');
802
+ for (const row of deployRows) {
803
+ const env = row.environment;
804
+ if (!env)
805
+ continue;
806
+ if (!isInWindow(row))
807
+ continue; // look-back-only row → correlation, not DF/CFR
808
+ const a = accFor(env);
809
+ if (row.status === 'SUCCEEDED') {
810
+ a.deployments++;
811
+ a.attempts++;
812
+ // Measured lead time: deploy completion − execution's earliest commit time.
813
+ if (row.commit_ts != null && row.completed_at != null) {
814
+ const gap = (Date.parse(row.completed_at) - Date.parse(row.commit_ts)) / 1000;
815
+ if (Number.isFinite(gap))
816
+ a.leadGaps.push(Math.max(gap, 0));
817
+ }
818
+ }
819
+ else if (row.status === 'FAILED') {
820
+ a.deployTimeFailures++;
821
+ a.attempts++;
822
+ }
823
+ }
824
+ // Incident→deploy correlation (Phase 5). Index the SUCCESSFUL deploys per env
825
+ // (execution id + completion instant), newest first, so each incident can be
826
+ // attributed to the most recent deploy that completed within the window
827
+ // before it opened. That execution is a post-deploy failure.
828
+ const windowMs = windowHours * 3600 * 1000;
829
+ const successfulByEnv = new Map();
830
+ for (const row of deployRows) {
831
+ if (row.status !== 'SUCCEEDED' || !row.environment || row.execution_id == null || row.completed_at == null)
832
+ continue;
833
+ const ms = Date.parse(row.completed_at);
834
+ if (!Number.isFinite(ms))
835
+ continue;
836
+ const list = successfulByEnv.get(row.environment) ?? [];
837
+ list.push({ exec: row.execution_id, completedMs: ms });
838
+ successfulByEnv.set(row.environment, list);
839
+ }
840
+ for (const list of successfulByEnv.values())
841
+ list.sort((a, b) => b.completedMs - a.completedMs);
842
+ /** Correlate an incident to the most recent in-window successful deploy execution. */
843
+ const correlate = (env, openedMs) => {
844
+ const list = successfulByEnv.get(env);
845
+ if (!list)
846
+ return null;
847
+ for (const d of list) {
848
+ if (d.completedMs <= openedMs && openedMs - d.completedMs <= windowMs)
849
+ return d.exec;
850
+ }
851
+ return null;
852
+ };
853
+ const correlatedIncidents = [];
854
+ for (const inc of incidentRows) {
855
+ if (!inc.environment || inc.opened_at == null)
856
+ continue;
857
+ const openedMs = Date.parse(inc.opened_at);
858
+ if (!Number.isFinite(openedMs))
859
+ continue;
860
+ const exec = correlate(inc.environment, openedMs);
861
+ if (exec == null)
862
+ continue; // uncorrelated → not attributable to a deploy
863
+ let resolvedGap = null;
864
+ if (inc.resolved_at != null) {
865
+ const resolvedMs = Date.parse(inc.resolved_at);
866
+ // Right-censoring: a resolution AFTER the window end (`to`) is not observed
867
+ // within the window — treat the incident as still open (gap stays null) so
868
+ // MTTR never reads unbounded past `to`. The incident still counts toward the
869
+ // incident total (unresolved), it just contributes no recovery time.
870
+ if (Number.isFinite(resolvedMs) && resolvedMs <= toMs) {
871
+ const g = (resolvedMs - openedMs) / 1000;
872
+ if (Number.isFinite(g))
873
+ resolvedGap = Math.max(g, 0);
874
+ }
875
+ }
876
+ correlatedIncidents.push({ environment: inc.environment, exec, resolvedGap });
877
+ }
878
+ // Post-deploy failures per env — the DISTINCT set of deploy executions flagged
879
+ // by a manual `failed` outcome OR a correlated incident (deduped by execution,
880
+ // so a deploy flagged by both counts once). A manual outcome without an
881
+ // execution id (legacy/unresolvable) still counts via a per-row sentinel.
882
+ const postDeployExecsByEnv = new Map();
883
+ let sentinel = 0;
884
+ const flagFor = (env) => {
885
+ let s = postDeployExecsByEnv.get(env);
886
+ if (!s) {
887
+ s = new Set();
888
+ postDeployExecsByEnv.set(env, s);
889
+ }
890
+ return s;
891
+ };
892
+ for (const o of outcomeRows) {
893
+ if (o.outcome !== 'failed' || !o.environment)
894
+ continue;
895
+ flagFor(o.environment).add(o.execution_id ?? `manual-${sentinel++}`);
896
+ accFor(o.environment); // ensure env appears even with no deploy events
897
+ }
898
+ for (const ci of correlatedIncidents) {
899
+ flagFor(ci.environment).add(ci.exec);
900
+ accFor(ci.environment); // ensure env appears even if the deploy is out-of-window
901
+ }
902
+ const postDeployByEnv = new Map();
903
+ for (const [env, set] of postDeployExecsByEnv)
904
+ postDeployByEnv.set(env, set.size);
905
+ const environments = [...envs.entries()]
906
+ .map(([environment, a]) => {
907
+ const postDeployFailures = postDeployByEnv.get(environment) ?? 0;
908
+ const rawPerDay = a.deployments / days;
909
+ const numerator = a.deployTimeFailures + postDeployFailures;
910
+ // Clamp to [0,100]: post-deploy failures can correlate to a deploy that
911
+ // completed just before the window (kept for correlation), so the numerator
912
+ // can transiently exceed in-window attempts — a CFR > 100% is never valid.
913
+ const rawRate = a.attempts > 0 ? Math.min(100, (numerator / a.attempts) * 100) : 0;
914
+ const ltMedian = median(a.leadGaps);
915
+ return {
916
+ environment,
917
+ deploymentFrequency: {
918
+ deployments: a.deployments,
919
+ perDay: round(rawPerDay, 2),
920
+ level: doraLevelForFrequency(rawPerDay, a.deployments),
921
+ },
922
+ changeFailureRate: {
923
+ rate: round(rawRate, 1),
924
+ deployTimeFailures: a.deployTimeFailures,
925
+ postDeployFailures,
926
+ attempts: a.attempts,
927
+ level: doraLevelForChangeFailure(rawRate, a.attempts),
928
+ },
929
+ leadTime: {
930
+ deployments: a.leadGaps.length,
931
+ medianSeconds: ltMedian != null ? round(ltMedian, 1) : null,
932
+ level: doraLevelForLeadTime(ltMedian),
933
+ },
934
+ };
935
+ })
936
+ .sort((x, y) => {
937
+ if (x.environment === HEADLINE_ENV)
938
+ return -1;
939
+ if (y.environment === HEADLINE_ENV)
940
+ return 1;
941
+ return x.environment.localeCompare(y.environment);
942
+ });
943
+ // MTTR — PRODUCTION-ONLY, from BOTH sources. Incidents take precedence: a
944
+ // production incident correlated to a deploy contributes the real recovery
945
+ // (`resolved_at − opened_at`), and any manual outcome on the SAME deploy is
946
+ // skipped. Manual outcomes on other deploys keep the Phase-2 behavior
947
+ // (`failed` → an incident; `restored` → a recovery gap of `restored − deployed`).
948
+ let incidents = 0, restored = 0;
949
+ const mttrGaps = [];
950
+ // Executions already attributed to a production incident (precedence guard).
951
+ const incidentExecs = new Set();
952
+ for (const ci of correlatedIncidents) {
953
+ if (ci.environment !== HEADLINE_ENV)
954
+ continue; // MTTR is production-only
955
+ incidents++;
956
+ incidentExecs.add(ci.exec);
957
+ if (ci.resolvedGap != null) {
958
+ restored++;
959
+ mttrGaps.push(ci.resolvedGap);
960
+ }
961
+ }
962
+ for (const m of mttrRows) {
963
+ // Incident precedence: don't double-count a deploy already flagged by an incident.
964
+ if (m.execution_id != null && incidentExecs.has(m.execution_id))
965
+ continue;
966
+ if (m.outcome === 'failed') {
967
+ incidents++;
968
+ continue;
969
+ }
970
+ if (m.outcome === 'restored') {
971
+ restored++;
972
+ if (m.restored_at != null && m.deployed_at != null) {
973
+ const gap = (Date.parse(m.restored_at) - Date.parse(m.deployed_at)) / 1000;
974
+ if (Number.isFinite(gap))
975
+ mttrGaps.push(Math.max(gap, 0));
976
+ }
977
+ }
978
+ }
979
+ const mttrMedian = median(mttrGaps);
980
+ const registered = Number(coverageRow?.registered) || 0;
981
+ const deploying = Number(coverageRow?.deploying) || 0;
982
+ return {
983
+ window: { from, to },
984
+ filters: { pipelineId: filters.pipelineId ?? null, environment: filters.environment ?? null },
985
+ headline: HEADLINE_ENV,
986
+ environments,
987
+ meanTimeToRestore: {
988
+ incidents,
989
+ restored,
990
+ medianSeconds: mttrMedian != null ? round(mttrMedian, 1) : null,
991
+ level: doraLevelForRestore(mttrMedian),
992
+ },
993
+ coverage: {
994
+ registered,
995
+ deploying,
996
+ withoutDeploys: Math.max(registered - deploying, 0),
997
+ },
998
+ };
999
+ }
1000
+ /**
1001
+ * 1.9b DORA trend — deployment frequency + deploy-time change-failure rate
1002
+ * bucketed by `interval` (day/week/month) for a sparkline. Deploy-basis like
1003
+ * getDoraMetrics: buckets terminal deploy-stage executions on their
1004
+ * `completed_at`. Shares the org/rollup + pipelineId/environment scoping. MTTR,
1005
+ * lead time, and post-deploy CFR are intentionally omitted (too heavy to bucket).
1006
+ */
1007
+ async getDoraTrend(orgId, interval, from, to, orgIds, opts = {}) {
1008
+ assertReportInterval(interval);
1009
+ const { pred, multi } = this.orgScope(orgId, orgIds);
1010
+ const { pipelineId, environment } = opts;
1011
+ const pipelineClause = pipelineId ? sql `AND e.pipeline_id = ${pipelineId}` : sql ``;
1012
+ const envClause = environment ? sql `AND e.environment = ${environment}` : sql ``;
1013
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
1014
+ WITH deploys AS (
1015
+ -- PER-EXECUTION deploy unit (D1): one row per (environment, execution),
1016
+ -- every deploy STAGE to that env within the execution rolled up to a
1017
+ -- single terminal status — NOT one row per stage.
1018
+ SELECT
1019
+ ${terminalStatusRollup} AS status,
1020
+ MAX(e.completed_at) AS completed_at
1021
+ FROM ${schema.pipelineEvent} e
1022
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
1023
+ WHERE p.org_id ${pred} AND e.event_type = 'STAGE' AND e.environment IS NOT NULL
1024
+ ${pipelineClause} ${envClause}
1025
+ AND e.completed_at >= ${from}::timestamptz AND e.completed_at <= ${to}::timestamptz
1026
+ GROUP BY e.environment, e.execution_id
1027
+ )
1028
+ SELECT
1029
+ DATE_TRUNC(${interval}, completed_at)::text AS period,
1030
+ COUNT(*) FILTER (WHERE status = 'SUCCEEDED')::int AS deployments,
1031
+ COUNT(*) FILTER (WHERE status = 'FAILED')::int AS failed,
1032
+ COUNT(*) FILTER (WHERE status IN ('SUCCEEDED', 'FAILED'))::int AS total,
1033
+ -- quoted alias so the raw row key is exactly the DoraTrendPoint field
1034
+ COALESCE(ROUND(COUNT(*) FILTER (WHERE status = 'FAILED')::numeric
1035
+ / NULLIF(COUNT(*) FILTER (WHERE status IN ('SUCCEEDED', 'FAILED')), 0) * 100, 1), 0)::float AS "changeFailurePct"
1036
+ FROM deploys
1037
+ GROUP BY period ORDER BY period
1038
+ `).then(r => drizzleRows(r.rows)));
1039
+ const key = `${orgId}:dora-trend:${interval}:${from}:${to}:${pipelineId ?? ''}:${environment ?? ''}`;
1040
+ return this.runReport(key, multi, exec);
1041
+ }
1042
+ /**
1043
+ * 1.10 Per-pipeline BUILD HEALTH (Phase 6) — a standard (NOT `advanced_reporting`)
1044
+ * per-stage breakdown for one pipeline over a [from,to] window. Aggregates the
1045
+ * existing `pipeline_events` STAGE rows: each stage is rolled up per execution
1046
+ * to a terminal status (FAILED wins, then SUCCEEDED) + its max duration, then
1047
+ * grouped per stage into run/success/failure counts, a success rate, and
1048
+ * duration percentiles (p50/p90/p99). Totals sum across stages. Org-scoped via
1049
+ * the pipeline join (`p.org_id ${pred}`) + rollup-aware like the sibling reports;
1050
+ * a pipelineId owned by another org returns an empty breakdown.
1051
+ */
1052
+ async getBuildHealth(orgId, pipelineId, from, to, orgIds) {
1053
+ const { pred, multi } = this.orgScope(orgId, orgIds);
1054
+ const exec = () => withTenantTx(async (tx) => {
1055
+ const rows = drizzleRows((await tx.execute(sql `
1056
+ WITH stage_runs AS (
1057
+ SELECT
1058
+ e.stage_name AS stage,
1059
+ ${terminalStatusRollup} AS status,
1060
+ MAX(e.duration_ms) AS duration_ms
1061
+ FROM ${schema.pipelineEvent} e
1062
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
1063
+ WHERE p.org_id ${pred} AND e.pipeline_id = ${pipelineId}
1064
+ AND e.event_type = 'STAGE' AND e.stage_name IS NOT NULL
1065
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
1066
+ GROUP BY e.stage_name, e.execution_id
1067
+ )
1068
+ SELECT
1069
+ stage,
1070
+ COUNT(*)::int AS runs,
1071
+ COUNT(*) FILTER (WHERE status = 'SUCCEEDED')::int AS successes,
1072
+ COUNT(*) FILTER (WHERE status = 'FAILED')::int AS failures,
1073
+ COALESCE(ROUND(COUNT(*) FILTER (WHERE status = 'SUCCEEDED')::numeric
1074
+ / NULLIF(COUNT(*), 0) * 100, 1), 0)::float AS "successRate",
1075
+ PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration_ms)::int AS "p50Ms",
1076
+ PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY duration_ms)::int AS "p90Ms",
1077
+ PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY duration_ms)::int AS "p99Ms"
1078
+ FROM stage_runs
1079
+ WHERE status IN ('SUCCEEDED', 'FAILED')
1080
+ GROUP BY stage
1081
+ ORDER BY runs DESC, stage
1082
+ `)).rows);
1083
+ // Totals sum across stages (total stage-runs / stage-failures in the window).
1084
+ let runs = 0, failures = 0;
1085
+ for (const s of rows) {
1086
+ runs += s.runs;
1087
+ failures += s.failures;
1088
+ }
1089
+ const failureRate = runs > 0 ? round((failures / runs) * 100, 1) : 0;
1090
+ return { stages: rows, totals: { runs, failures, failureRate } };
1091
+ });
1092
+ return this.runReport(`${orgId}:build-health:${pipelineId}:${from}:${to}`, multi, exec);
1093
+ }
1094
+ // ── Category 2: Plugin Inventory & Builds ──
1095
+ /** 2.1 Plugin summary — counts and breakdowns.
1096
+ * INTENTIONALLY SINGLE-ORG (no rollup): plugin inventory is an org-owned
1097
+ * asset count, not an execution/build activity report, so a parent's view is
1098
+ * its own plugins — teams manage their own inventory. Single `= $org` scope. */
1099
+ async getPluginSummary(orgId) {
1100
+ return inventoryCache.getOrSet(`${orgId}:plugin-summary`, async () => {
1101
+ const rows = await withTenantTx((tx) => tx.execute(sql `
1102
+ SELECT
1103
+ COUNT(*)::int AS total,
1104
+ COUNT(*) FILTER (WHERE ${schema.plugin.isActive})::int AS active,
1105
+ COUNT(*) FILTER (WHERE NOT ${schema.plugin.isActive})::int AS inactive,
1106
+ COUNT(*) FILTER (WHERE ${schema.plugin.visibility} = 'public')::int AS public,
1107
+ COUNT(*) FILTER (WHERE ${schema.plugin.visibility} = 'org')::int AS org,
1108
+ COUNT(*) FILTER (WHERE ${schema.plugin.visibility} = 'private')::int AS private,
1109
+ COUNT(DISTINCT ${schema.plugin.name})::int AS unique_names
1110
+ FROM ${schema.plugin}
1111
+ WHERE ${schema.plugin.orgId} = ${orgId}
1112
+ `));
1113
+ return (drizzleRows(rows.rows)[0] || { total: 0, active: 0, inactive: 0, public: 0, org: 0, private: 0, uniqueNames: 0 });
1114
+ });
1115
+ }
1116
+ /** 2.2 Type & compute distribution.
1117
+ * INTENTIONALLY SINGLE-ORG (no rollup): plugin inventory is per-org (see
1118
+ * getPluginSummary). Single `= $org` scope. */
1119
+ async getPluginDistribution(orgId) {
1120
+ return inventoryCache.getOrSet(`${orgId}:plugin-distribution`, () => withTenantTx((tx) => tx.execute(sql `
1121
+ SELECT
1122
+ ${schema.plugin.pluginType} AS plugin_type,
1123
+ ${schema.plugin.computeType} AS compute_type,
1124
+ COUNT(*)::int AS count
1125
+ FROM ${schema.plugin}
1126
+ WHERE ${schema.plugin.orgId} = ${orgId} AND ${schema.plugin.isActive} = true
1127
+ GROUP BY ${schema.plugin.pluginType}, ${schema.plugin.computeType}
1128
+ ORDER BY count DESC
1129
+ `).then(r => drizzleRows(r.rows))));
1130
+ }
1131
+ /** 2.3 Version counts per plugin name.
1132
+ * INTENTIONALLY SINGLE-ORG (no rollup): plugin inventory is per-org (see
1133
+ * getPluginSummary). Single `= $org` scope. */
1134
+ async getPluginVersions(orgId) {
1135
+ return inventoryCache.getOrSet(`${orgId}:plugin-versions`, () => withTenantTx((tx) => tx.execute(sql `
1136
+ SELECT
1137
+ ${schema.plugin.name},
1138
+ COUNT(*)::int AS version_count,
1139
+ -- Highest version by NUMERIC semver core (not lexical: '10.0.0' > '9.0.0').
1140
+ -- Strip any -prerelease/+build suffix before the int[] cast so it can't
1141
+ -- error on valid semver; keep the original string for display.
1142
+ -- GUARD: a non-numeric-dotted version ('latest', '', 'v1') would make the
1143
+ -- ::int[] cast throw and blow up the WHOLE org summary. Only cast rows
1144
+ -- whose stripped core matches a numeric-dotted shape; sort everything
1145
+ -- else to the bottom via a sentinel ARRAY[-1] so the row still counts.
1146
+ (array_agg(${schema.plugin.version}
1147
+ ORDER BY CASE
1148
+ WHEN regexp_replace(${schema.plugin.version}, '[-+].*$', '') ~ '^[0-9]+([.][0-9]+)*$'
1149
+ THEN string_to_array(regexp_replace(${schema.plugin.version}, '[-+].*$', ''), '.')::int[]
1150
+ ELSE ARRAY[-1]
1151
+ END DESC
1152
+ ))[1] AS latest_version,
1153
+ bool_or(${schema.plugin.isDefault}) AS has_default
1154
+ FROM ${schema.plugin}
1155
+ WHERE ${schema.plugin.orgId} = ${orgId} AND ${schema.plugin.isActive} = true
1156
+ GROUP BY ${schema.plugin.name}
1157
+ ORDER BY version_count DESC
1158
+ `).then(r => drizzleRows(r.rows))));
1159
+ }
1160
+ /**
1161
+ * 2.4 Build success rate over time.
1162
+ *
1163
+ * STATUS CASING NOTE: This query filters by `event_source = 'plugin-build'`
1164
+ * and uses lowercase status values (`'completed'`, `'failed'`), while
1165
+ * `getSuccessRate` (1.2) filters by `event_type = 'PIPELINE'` with
1166
+ * uppercase AWS-style statuses (`'SUCCEEDED'`, `'FAILED'`, `'CANCELED'`).
1167
+ * The casing drift is intentional and tracks the producer:
1168
+ * - `plugin-build` events come from our own build pipeline (lowercase)
1169
+ * - `PIPELINE` events come from AWS CodePipeline (uppercase)
1170
+ * The ingest Zod schema at api/reporting/src/routes/event-ingest.ts
1171
+ * SHOULD enum these per-eventSource so we catch drift at ingest rather
1172
+ * than silently producing zero rows here. See findings N71.
1173
+ */
1174
+ async getBuildSuccessRate(orgId, interval, from, to, orgIds) {
1175
+ assertReportInterval(interval);
1176
+ // Build activity report — rollup-aware like the execution reports. These
1177
+ // rows are gated on the pipeline_event `org_id` directly (no pipeline join),
1178
+ // so `pred` applies to `e.org_id`.
1179
+ const { pred, multi } = this.orgScope(orgId, orgIds);
1180
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
1181
+ SELECT
1182
+ DATE_TRUNC(${interval}, e.started_at)::text AS period,
1183
+ COUNT(*) FILTER (WHERE e.status = 'completed')::int AS succeeded,
1184
+ COUNT(*) FILTER (WHERE e.status = 'failed')::int AS failed,
1185
+ ROUND(COUNT(*) FILTER (WHERE e.status = 'completed')::numeric
1186
+ / NULLIF(COUNT(*), 0) * 100, 1)::float AS success_pct
1187
+ FROM ${schema.pipelineEvent} e
1188
+ WHERE e.org_id ${pred} AND e.event_source = 'plugin-build'
1189
+ AND e.status IN ('completed', 'failed')
1190
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
1191
+ GROUP BY period ORDER BY period
1192
+ `).then(r => drizzleRows(r.rows)));
1193
+ return this.runReport(`${orgId}:build-success:${interval}:${from}:${to}`, multi, exec);
1194
+ }
1195
+ /** 2.5 Build duration per plugin. Build activity report — rollup-aware. */
1196
+ async getBuildDuration(orgId, from, to, orgIds) {
1197
+ const { pred, multi } = this.orgScope(orgId, orgIds);
1198
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
1199
+ SELECT
1200
+ e.detail->>'pluginName' AS plugin_name,
1201
+ AVG(e.duration_ms)::int AS avg_ms,
1202
+ MAX(e.duration_ms)::int AS max_ms,
1203
+ COUNT(*)::int AS builds
1204
+ FROM ${schema.pipelineEvent} e
1205
+ WHERE e.org_id ${pred} AND e.event_source = 'plugin-build' AND e.duration_ms IS NOT NULL
1206
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
1207
+ GROUP BY plugin_name ORDER BY avg_ms DESC
1208
+ `).then(r => drizzleRows(r.rows)));
1209
+ return this.runReport(`${orgId}:build-duration:${from}:${to}`, multi, exec);
1210
+ }
1211
+ /** 2.6 Build failures — top error messages. Build activity report — rollup-aware. */
1212
+ async getBuildFailures(orgId, from, to, limit = 20, orgIds) {
1213
+ const { pred, multi } = this.orgScope(orgId, orgIds);
1214
+ const exec = () => withTenantTx((tx) => tx.execute(sql `
1215
+ SELECT
1216
+ e.detail->>'pluginName' AS plugin_name,
1217
+ e.error_message,
1218
+ COUNT(*)::int AS occurrences,
1219
+ MAX(e.started_at)::text AS last_seen
1220
+ FROM ${schema.pipelineEvent} e
1221
+ WHERE e.org_id ${pred} AND e.event_source = 'plugin-build' AND e.status = 'failed'
1222
+ AND e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz
1223
+ GROUP BY plugin_name, e.error_message
1224
+ ORDER BY occurrences DESC
1225
+ LIMIT ${limit}
1226
+ `).then(r => drizzleRows(r.rows)));
1227
+ return this.runReport(`${orgId}:build-failures:${from}:${to}:${limit}`, multi, exec);
1228
+ }
1229
+ // ── Category 3: DORA write paths (post-deploy outcomes + ingest health) ──
1230
+ /**
1231
+ * Record a manual post-deploy outcome marker (Phase 2): a user marks a
1232
+ * deployment `failed` (a production incident linked to the deploy) or
1233
+ * `restored` (recovered). Idempotent — keyed on (execution_id, outcome) so a
1234
+ * duplicate POST refreshes `at` rather than double-counting, while a
1235
+ * failed→restored pair stays two rows. Feeds the post-deploy CFR component and
1236
+ * the real MTTR. Runs under the caller's org context (RLS WITH CHECK).
1237
+ */
1238
+ async recordDeploymentOutcome(orgId, executionId, input) {
1239
+ await runWithTenantContext({ orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.insert(schema.deploymentOutcome).values({
1240
+ executionId,
1241
+ orgId,
1242
+ environment: scrubOptional(input.environment),
1243
+ outcome: input.outcome,
1244
+ at: new Date(input.at),
1245
+ }).onConflictDoUpdate({
1246
+ target: [schema.deploymentOutcome.executionId, schema.deploymentOutcome.outcome],
1247
+ set: {
1248
+ at: new Date(input.at),
1249
+ environment: scrubOptional(input.environment) ?? null,
1250
+ },
1251
+ })));
1252
+ // Outcome changes the DORA aggregate — drop the org's cached reports.
1253
+ await this.invalidateOrg(orgId).catch((err) => {
1254
+ logger.warn('Reporting cache invalidation failed', { orgId, error: errorMessage(err) });
1255
+ });
1256
+ }
1257
+ /**
1258
+ * Ingest a production incident (Phase 5) from the org's incident tooling
1259
+ * (PagerDuty / Datadog / Alertmanager webhook). Idempotent — keyed on
1260
+ * (org_id, incident_id) so a later resolve re-post upserts `resolved_at`
1261
+ * (and refreshes the other mutable fields) instead of inserting a duplicate.
1262
+ * DORA correlates each incident to the most recent successful deploy to its
1263
+ * `environment`, producing an automated post-deploy failure + a real MTTR.
1264
+ * Runs under the caller's org context (RLS WITH CHECK); free-form fields are
1265
+ * AWS-id scrubbed at this persistence boundary like the other ingest paths.
1266
+ */
1267
+ async recordIncident(orgId, input) {
1268
+ const incidentId = scrubAwsIdentifiersFromString(input.incidentId);
1269
+ const environment = scrubAwsIdentifiersFromString(input.environment);
1270
+ const severity = scrubAwsIdentifiersFromString(input.severity);
1271
+ const openedAt = new Date(input.openedAt);
1272
+ const resolvedAt = input.resolvedAt ? new Date(input.resolvedAt) : null;
1273
+ await runWithTenantContext({ orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.insert(schema.incident).values({
1274
+ incidentId,
1275
+ orgId,
1276
+ environment,
1277
+ openedAt,
1278
+ resolvedAt,
1279
+ severity,
1280
+ }).onConflictDoUpdate({
1281
+ target: [schema.incident.orgId, schema.incident.incidentId],
1282
+ set: { environment, openedAt, resolvedAt, severity },
1283
+ })));
1284
+ // A new/updated incident changes the DORA aggregate — drop cached reports.
1285
+ await this.invalidateOrg(orgId).catch((err) => {
1286
+ logger.warn('Reporting cache invalidation failed', { orgId, error: errorMessage(err) });
1287
+ });
1288
+ }
1289
+ /**
1290
+ * Read the per-org DORA settings (Phase 5b). Returns the stored
1291
+ * `incidentWindowHours` override (or `null` when unset) plus the global env
1292
+ * default, so the settings UI can show both. Runs under the caller's tenant
1293
+ * context (RLS-scoped); a single-org read.
1294
+ */
1295
+ async getIncidentSettings(orgId) {
1296
+ const rows = drizzleRows((await withTenantTx((tx) => tx.execute(sql `
1297
+ SELECT ${schema.doraSettings.incidentWindowHours} AS incident_window_hours,
1298
+ ${schema.doraSettings.eventRetentionDays} AS event_retention_days,
1299
+ ${schema.doraSettings.doraRetentionDays} AS dora_retention_days
1300
+ FROM ${schema.doraSettings}
1301
+ WHERE ${schema.doraSettings.orgId} = ${orgId}
1302
+ LIMIT 1
1303
+ `))).rows);
1304
+ const row = rows[0];
1305
+ return {
1306
+ incidentWindowHours: row?.incident_window_hours != null ? Number(row.incident_window_hours) : null,
1307
+ defaultWindowHours: DORA_INCIDENT_WINDOW_HOURS,
1308
+ eventRetentionDays: row?.event_retention_days != null ? Number(row.event_retention_days) : null,
1309
+ doraRetentionDays: row?.dora_retention_days != null ? Number(row.dora_retention_days) : null,
1310
+ defaultEventRetentionDays: REPORTING_EVENT_RETENTION_DAYS,
1311
+ defaultDoraRetentionDays: REPORTING_DORA_RETENTION_DAYS,
1312
+ };
1313
+ }
1314
+ /**
1315
+ * Upsert per-org reporting settings (Phase 5b incident window + Phase 7
1316
+ * retention overrides), idempotent on `org_id`. A partial write — only the
1317
+ * fields present in `patch` are set (so updating retention never clears the
1318
+ * incident window, and vice-versa). Set self-serve by an org admin via
1319
+ * `PUT /api/reports/settings/incidents`. Runs under the caller's org context
1320
+ * (RLS WITH CHECK); a changed value affects DORA aggregates so the org's
1321
+ * cached reports are dropped.
1322
+ */
1323
+ async setReportingSettings(orgId, patch) {
1324
+ const set = { updatedAt: new Date() };
1325
+ if (patch.incidentWindowHours !== undefined)
1326
+ set.incidentWindowHours = patch.incidentWindowHours;
1327
+ if (patch.eventRetentionDays !== undefined)
1328
+ set.eventRetentionDays = patch.eventRetentionDays;
1329
+ if (patch.doraRetentionDays !== undefined)
1330
+ set.doraRetentionDays = patch.doraRetentionDays;
1331
+ await runWithTenantContext({ orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.insert(schema.doraSettings).values({
1332
+ orgId,
1333
+ incidentWindowHours: patch.incidentWindowHours,
1334
+ eventRetentionDays: patch.eventRetentionDays,
1335
+ doraRetentionDays: patch.doraRetentionDays,
1336
+ updatedAt: new Date(),
1337
+ }).onConflictDoUpdate({
1338
+ target: schema.doraSettings.orgId,
1339
+ set,
1340
+ })));
1341
+ await this.invalidateOrg(orgId).catch((err) => {
1342
+ logger.warn('Reporting cache invalidation failed', { orgId, error: errorMessage(err) });
1343
+ });
1344
+ }
1345
+ /**
1346
+ * List recent incidents for an org (Phase 5b org-admin surface), newest first,
1347
+ * paginated. Each row carries its resolved state and its deploy correlation —
1348
+ * the most recent SUCCESSFUL deploy to the incident's `environment` whose
1349
+ * `completed_at` falls within the effective per-org correlation window before
1350
+ * `opened_at` (the same rule DORA's CFR/MTTR correlation applies). Single-org
1351
+ * (no rollup) — an org admin views their own org's incidents. RLS-scoped via
1352
+ * the `p.org_id`/`i.org_id` predicates + the tenant context.
1353
+ */
1354
+ async listIncidents(orgId, opts) {
1355
+ const { incidentWindowHours } = await this.getIncidentSettings(orgId);
1356
+ const windowHours = resolveIncidentWindowHours(incidentWindowHours);
1357
+ const limit = Math.max(1, Math.min(opts.limit, 200));
1358
+ const offset = Math.max(0, opts.offset);
1359
+ return withTenantTx((tx) => tx.execute(sql `
1360
+ SELECT
1361
+ i.incident_id AS "incidentId",
1362
+ i.environment AS environment,
1363
+ i.severity AS severity,
1364
+ i.opened_at::text AS "openedAt",
1365
+ i.resolved_at::text AS "resolvedAt",
1366
+ i.created_at::text AS "createdAt",
1367
+ (i.resolved_at IS NOT NULL) AS resolved,
1368
+ corr.execution_id AS "correlatedExecutionId",
1369
+ corr.completed_at::text AS "deployCompletedAt"
1370
+ FROM ${schema.incident} i
1371
+ LEFT JOIN LATERAL (
1372
+ SELECT e.execution_id, e.completed_at
1373
+ FROM ${schema.pipelineEvent} e
1374
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
1375
+ WHERE p.org_id = ${orgId} AND e.event_type = 'STAGE'
1376
+ AND e.environment = i.environment AND e.status = 'SUCCEEDED'
1377
+ AND e.completed_at <= i.opened_at
1378
+ AND e.completed_at >= i.opened_at - make_interval(hours => ${windowHours}::int)
1379
+ ORDER BY e.completed_at DESC
1380
+ LIMIT 1
1381
+ ) corr ON true
1382
+ WHERE i.org_id = ${orgId}
1383
+ ORDER BY i.opened_at DESC
1384
+ LIMIT ${limit} OFFSET ${offset}
1385
+ `).then((r) => drizzleRows(r.rows).map((row) => ({
1386
+ ...row,
1387
+ resolved: row.resolved === true || row.resolved === 't',
1388
+ correlatedExecutionId: row.correlatedExecutionId ?? null,
1389
+ deployCompletedAt: row.deployCompletedAt ?? null,
1390
+ }))));
1391
+ }
1392
+ /**
1393
+ * Wiring-test dry-run (Phase 5b): would a synthetic incident opening NOW for
1394
+ * `environment` correlate to a recent successful deploy under the org's
1395
+ * effective window? This is a NON-persisting correlation check — it verifies
1396
+ * the admin's environment naming + window line up with real deploy events
1397
+ * WITHOUT writing an incident (so a "test" never pollutes CFR/MTTR). RLS-scoped.
1398
+ */
1399
+ async testIncidentCorrelation(orgId, environment) {
1400
+ const env = scrubAwsIdentifiersFromString(environment);
1401
+ const openedAt = new Date().toISOString();
1402
+ const { incidentWindowHours } = await this.getIncidentSettings(orgId);
1403
+ const windowHours = resolveIncidentWindowHours(incidentWindowHours);
1404
+ const rows = drizzleRows((await withTenantTx((tx) => tx.execute(sql `
1405
+ SELECT e.execution_id AS execution_id, e.completed_at::text AS completed_at
1406
+ FROM ${schema.pipelineEvent} e
1407
+ JOIN ${schema.pipeline} p ON p.id = e.pipeline_id
1408
+ WHERE p.org_id = ${orgId} AND e.event_type = 'STAGE'
1409
+ AND e.environment = ${env} AND e.status = 'SUCCEEDED'
1410
+ AND e.completed_at <= ${openedAt}::timestamptz
1411
+ AND e.completed_at >= ${openedAt}::timestamptz - make_interval(hours => ${windowHours}::int)
1412
+ ORDER BY e.completed_at DESC
1413
+ LIMIT 1
1414
+ `))).rows);
1415
+ const hit = rows[0];
1416
+ return {
1417
+ environment: env,
1418
+ openedAt,
1419
+ windowHours,
1420
+ correlated: hit?.execution_id != null,
1421
+ executionId: hit?.execution_id ?? null,
1422
+ deployCompletedAt: hit?.completed_at ?? null,
1423
+ };
1424
+ }
1425
+ /**
1426
+ * Upsert per-org ingestion health (Phase 3): the AWS events Lambda periodically
1427
+ * reports forwarded/dropped counters + the last event timestamp so the Reports
1428
+ * UI can show flowing / stale / dropping. One row per org (upsert on org_id).
1429
+ */
1430
+ async recordIngestHealth(orgId, input) {
1431
+ const lastEventAt = input.lastEventAt ? new Date(input.lastEventAt) : undefined;
1432
+ await runWithTenantContext({ orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.insert(schema.ingestHealth).values({
1433
+ orgId,
1434
+ forwarded: input.forwarded,
1435
+ dropped: input.dropped,
1436
+ lastEventAt,
1437
+ updatedAt: new Date(),
1438
+ }).onConflictDoUpdate({
1439
+ target: schema.ingestHealth.orgId,
1440
+ set: {
1441
+ forwarded: input.forwarded,
1442
+ dropped: input.dropped,
1443
+ lastEventAt,
1444
+ updatedAt: new Date(),
1445
+ },
1446
+ })));
1447
+ }
1448
+ /**
1449
+ * Batched hard-DELETE of one reporting table's rows expired past a `created_at`
1450
+ * cutoff, scoped to an org and an optional row predicate (Phase 7). Uses a
1451
+ * `ctid`-in-subquery LIMIT so each statement touches at most `batchSize` rows
1452
+ * (short lock windows, no long table scan under lock); `RETURNING 1` lets us
1453
+ * count via `.rows.length` without depending on the driver's `rowCount`. Loops
1454
+ * until a short batch (drained) or the per-tick cap, then defers the rest.
1455
+ * MUST run inside a sysadmin tenant scope (see purgeExpiredReportingData) so it
1456
+ * bypasses RLS and can delete across the org's rows.
1457
+ */
1458
+ async #purgeReportingTableBatched(tableSql, predicate, batchSize, maxBatches) {
1459
+ let total = 0;
1460
+ for (let i = 0; i < maxBatches; i++) {
1461
+ const res = await withTenantTx((tx) => tx.execute(sql `
1462
+ DELETE FROM ${tableSql}
1463
+ WHERE ctid IN (
1464
+ SELECT ctid FROM ${tableSql}
1465
+ WHERE ${predicate}
1466
+ LIMIT ${batchSize}
1467
+ )
1468
+ RETURNING 1
1469
+ `));
1470
+ const purged = drizzleRows(res.rows).length;
1471
+ total += purged;
1472
+ if (purged < batchSize)
1473
+ break; // drained
1474
+ if (i === maxBatches - 1) {
1475
+ logger.warn('Reporting retention hit per-tick cap; remaining rows deferred', {
1476
+ purgedThisTick: total,
1477
+ });
1478
+ }
1479
+ }
1480
+ return total;
1481
+ }
1482
+ /**
1483
+ * Reporting retention sweep (Phase 7). Hard-deletes rows older than their
1484
+ * retention window, by `created_at`, across every org that has reporting data —
1485
+ * a **split** policy so high-volume standard events expire faster than the
1486
+ * low-volume DORA source:
1487
+ * - `pipeline_events WHERE environment IS NULL` → standard-event window.
1488
+ * - `pipeline_events WHERE environment IS NOT NULL` (deploy stages),
1489
+ * `deployment_outcomes`, and `incidents` → DORA-source window.
1490
+ * Each org's windows come from its `dora_settings` override, else the global
1491
+ * env defaults (see {@link resolveEventRetentionDays} / {@link resolveDoraRetentionDays}).
1492
+ * `ingest_health` and `dora_settings` are never purged. One `now` for the whole
1493
+ * tick (rows crossing the boundary mid-sweep wait for the next). Establishes a
1494
+ * sysadmin tenant scope so the deletes span all orgs / bypass RLS — this is a
1495
+ * cross-tenant housekeeping job. Returns (and logs) per-window purge tallies.
1496
+ */
1497
+ async purgeExpiredReportingData(opts = {}) {
1498
+ const batchSize = Math.max(1, opts.batchSize ?? 1000);
1499
+ const maxBatches = Math.max(1, opts.maxBatchesPerTable ?? 50);
1500
+ const now = opts.now ?? new Date();
1501
+ const counts = {
1502
+ orgs: 0, standardEvents: 0, doraEvents: 0, deploymentOutcomes: 0, incidents: 0,
1503
+ };
1504
+ return runWithTenantContext({ isSuperAdmin: true }, async () => {
1505
+ // Enumerate every org with reporting data (union across the three tables).
1506
+ const orgRows = drizzleRows((await withTenantTx((tx) => tx.execute(sql `
1507
+ SELECT DISTINCT org_id FROM (
1508
+ SELECT ${schema.pipelineEvent.orgId} AS org_id FROM ${schema.pipelineEvent}
1509
+ UNION
1510
+ SELECT ${schema.deploymentOutcome.orgId} AS org_id FROM ${schema.deploymentOutcome}
1511
+ UNION
1512
+ SELECT ${schema.incident.orgId} AS org_id FROM ${schema.incident}
1513
+ ) AS orgs
1514
+ `))).rows);
1515
+ if (orgRows.length === 0)
1516
+ return counts;
1517
+ // Per-org retention overrides (a single read; orgs without a row use defaults).
1518
+ const overrideRows = drizzleRows((await withTenantTx((tx) => tx.execute(sql `
1519
+ SELECT ${schema.doraSettings.orgId} AS org_id,
1520
+ ${schema.doraSettings.eventRetentionDays} AS event_retention_days,
1521
+ ${schema.doraSettings.doraRetentionDays} AS dora_retention_days
1522
+ FROM ${schema.doraSettings}
1523
+ `))).rows);
1524
+ const overrides = new Map(overrideRows.map((r) => [r.org_id, r]));
1525
+ const eventsTable = sql `${schema.pipelineEvent}`;
1526
+ const outcomesTable = sql `${schema.deploymentOutcome}`;
1527
+ const incidentsTable = sql `${schema.incident}`;
1528
+ for (const { org_id: orgId } of orgRows) {
1529
+ const ov = overrides.get(orgId);
1530
+ const eventDays = resolveEventRetentionDays(ov?.event_retention_days);
1531
+ const doraDays = resolveDoraRetentionDays(ov?.dora_retention_days);
1532
+ // `-1` = unlimited (Phase 8): keep forever, skip that window's deletes for
1533
+ // this org. Standard-event and DORA-source windows are independent; an org
1534
+ // with both `-1` is fully skipped. Log the skip so it's observable.
1535
+ const skipEvents = eventDays === -1;
1536
+ const skipDora = doraDays === -1;
1537
+ if (skipEvents || skipDora) {
1538
+ logger.info('Reporting retention sweep skipping unlimited window(s)', {
1539
+ orgId,
1540
+ standardEvents: skipEvents ? 'unlimited' : eventDays,
1541
+ doraSource: skipDora ? 'unlimited' : doraDays,
1542
+ });
1543
+ }
1544
+ if (!skipEvents) {
1545
+ const eventCutoff = retentionCutoff(now, eventDays);
1546
+ counts.standardEvents += await this.#purgeReportingTableBatched(eventsTable, sql `org_id = ${orgId} AND environment IS NULL AND created_at < ${eventCutoff}`, batchSize, maxBatches);
1547
+ }
1548
+ if (!skipDora) {
1549
+ const doraCutoff = retentionCutoff(now, doraDays);
1550
+ counts.doraEvents += await this.#purgeReportingTableBatched(eventsTable, sql `org_id = ${orgId} AND environment IS NOT NULL AND created_at < ${doraCutoff}`, batchSize, maxBatches);
1551
+ counts.deploymentOutcomes += await this.#purgeReportingTableBatched(outcomesTable, sql `org_id = ${orgId} AND created_at < ${doraCutoff}`, batchSize, maxBatches);
1552
+ counts.incidents += await this.#purgeReportingTableBatched(incidentsTable, sql `org_id = ${orgId} AND created_at < ${doraCutoff}`, batchSize, maxBatches);
1553
+ }
1554
+ counts.orgs += 1;
1555
+ }
1556
+ const purgedAny = counts.standardEvents + counts.doraEvents + counts.deploymentOutcomes + counts.incidents;
1557
+ if (purgedAny > 0)
1558
+ logger.info('Reporting retention sweep purged expired rows', { ...counts });
1559
+ return counts;
1560
+ });
1561
+ }
1562
+ }
1563
+ export const reportingService = new ReportingService();
1564
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwb3J0aW5nLXNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL3JlcG9ydGluZy1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsNkJBQTZCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNsSyxPQUFPLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDaEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxZQUFZLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUU1RSxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsbUJBQW1CLENBQUMsQ0FBQztBQUVqRDs7Ozs7OztHQU9HO0FBQ0gsU0FBUyxvQkFBb0IsQ0FBQyxRQUFnQjtJQUM1QyxJQUFJLENBQUUsZ0JBQXNDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDaEUsTUFBTSxJQUFJLEtBQUssQ0FBQyw0QkFBNEIsUUFBUSxzQkFBc0IsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMzRyxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxhQUFhLENBQUMsS0FBeUI7SUFDOUMsT0FBTyxLQUFLLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyw2QkFBNkIsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ2hGLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxjQUFjLEdBQUcsa0JBQWtCLENBQUMsYUFB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QUcsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUMvRixPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQUVELE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLElBQUksZ0JBQWdCLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBjcmVhdGVDYWNoZVNlcnZpY2UsIGNyZWF0ZUxvZ2dlciwgZXJyb3JNZXNzYWdlLCBSRVBPUlRfSU5URVJWQUxTLCBzY3J1YkF3c0lkZW50aWZpZXJzLCBzY3J1YkF3c0lkZW50aWZpZXJzRnJvbVN0cmluZyB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IGluQXJyYXksIHNxbCB9IGZyb20gJ2RyaXp6bGUtb3JtJztcbmltcG9ydCB7IGRyaXp6bGVSb3dzIH0gZnJvbSAnLi9jcnVkLXNlcnZpY2UuanMnO1xuaW1wb3J0IHsgc2NoZW1hIH0gZnJvbSAnLi4vZGF0YWJhc2UvZHJpenpsZS1zY2hlbWEuanMnO1xuaW1wb3J0IHsgd2l0aFRlbmFudFR4LCBydW5XaXRoVGVuYW50Q29udGV4dCB9IGZyb20gJy4uL2RhdGFiYXNlL3RlbmFuY3kuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3JlcG9ydGluZy1zZXJ2aWNlJyk7XG5cbi8qKlxuICogRGVmZW5zZS1pbi1kZXB0aCBndWFyZCBmb3IgdGhlIGBEQVRFX1RSVU5DKCR7aW50ZXJ2YWx9LCDigKYpYCBidWNrZXQgYXJndW1lbnQuXG4gKiBUaGUgcm91dGUgbGF5ZXIgKGBwYXJzZVJlcG9ydEludGVydmFsYCkgaXMgdGhlIHNlY3VyaXR5IGJvdW5kYXJ5IGFuZCBhbHJlYWR5XG4gKiByZWplY3RzIHVua25vd24gaW50ZXJ2YWxzOyB0aGlzIGFzc2VydCBtYWtlcyBhbiBpbnZhbGlkIHZhbHVlIGZhaWwgZmFzdCBhdCB0aGVcbiAqIHNlcnZpY2UgYm91bmRhcnkgd2l0aCBhIGNsZWFyIGVycm9yIGluc3RlYWQgb2Ygc3VyZmFjaW5nIGEgcmF3IFBvc3RncmVzIGVycm9yXG4gKiBpZiBhIGNhbGxlciBldmVyIGJ5cGFzc2VzIHRoZSByb3V0ZSB2YWxpZGF0aW9uLiBTaGFyZXMgYXBpLWNvcmUnc1xuICogUkVQT1JUX0lOVEVSVkFMUyBhbGxvdy1saXN0IHNvIHRoZSB0d28gY2FuJ3QgZHJpZnQuXG4gKi9cbmZ1bmN0aW9uIGFzc2VydFJlcG9ydEludGVydmFsKGludGVydmFsOiBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKCEoUkVQT1JUX0lOVEVSVkFMUyBhcyByZWFkb25seSBzdHJpbmdbXSkuaW5jbHVkZXMoaW50ZXJ2YWwpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHJlcG9ydCBpbnRlcnZhbDogJHtpbnRlcnZhbH0uIEV4cGVjdGVkIG9uZSBvZjogJHtSRVBPUlRfSU5URVJWQUxTLmpvaW4oJywgJyl9YCk7XG4gIH1cbn1cblxuLyoqXG4gKiBTY3J1YiBBV1MgaWRlbnRpZmllcnMgZnJvbSBhbiBvcHRpb25hbCBmcmVlLWZvcm0gc3RyaW5nLCBwcmVzZXJ2aW5nIGB1bmRlZmluZWRgXG4gKiAoc28gYW4gYWJzZW50IGZpZWxkIHN0YXlzIGFic2VudCByYXRoZXIgdGhhbiBiZWNvbWluZyBhbiBlbXB0eSBzY3J1YmJlZCBzdHJpbmcpLlxuICogVGhlIERVUkFCTEUgcGVyc2lzdGVuY2UgYm91bmRhcnkgZm9yIHRoZSBpbmdlc3QgcGF0aHMg4oCUIGV2ZXJ5IHVzZXIvQVdTLWRlcml2ZWRcbiAqIHN0cmluZyBmaWVsZCBpcyBmdW5uZWxlZCB0aHJvdWdoIHRoaXMgYmVmb3JlIGluc2VydC5cbiAqL1xuZnVuY3Rpb24gc2NydWJPcHRpb25hbCh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHZhbHVlICE9PSB1bmRlZmluZWQgPyBzY3J1YkF3c0lkZW50aWZpZXJzRnJvbVN0cmluZyh2YWx1ZSkgOiB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogQ2FjaGUgZm9yIHJlcG9ydGluZyBhZ2dyZWdhdGlvbnMuIFR3byB0aWVyczpcbiAqIC0gSW52ZW50b3J5IHF1ZXJpZXMgKHBsdWdpbiBzdW1tYXJ5L2Rpc3RyaWJ1dGlvbi92ZXJzaW9ucyk6IDUgbWluIFRUTCDigJQgY2hhbmdlcyBvbiBwbHVnaW4gQ1JVRFxuICogLSBFeGVjdXRpb24vYnVpbGQgcXVlcmllcyB3aXRoIGRhdGUgcmFuZ2VzOiAyIG1pbiBUVEwg4oCUIG5ldyBldmVudHMgYXJyaXZlIGNvbnRpbnVvdXNseVxuICovXG5jb25zdCBpbnZlbnRvcnlDYWNoZSA9IGNyZWF0ZUNhY2hlU2VydmljZSgncmVwb3J0OmludjonLCBwYXJzZUludChwcm9jZXNzLmVudi5DQUNIRV9UVExfUkVQT1JUX0lOVkVOVE9SWSB8fCAnMzAwJywgMTApKTtcbmNvbnN0IHRpbWVzZXJpZXNDYWNoZSA9IGNyZWF0ZUNhY2hlU2VydmljZSgncmVwb3J0OnRzOicsIHBhcnNlSW50KHByb2Nlc3MuZW52LkNBQ0hFX1RUTF9SRVBPUlRfVElNRVNFUklFUyB8fCAnMTIwJywgMTApKTtcblxuLy8g4pSA4pSA4pSAIFR5cGVzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5pbnRlcmZhY2UgRXhlY3V0aW9uQ291bnQge1xuICBpZDogc3RyaW5nO1xuICBwcm9qZWN0OiBzdHJpbmc7XG4gIG9yZ2FuaXphdGlvbjogc3RyaW5nO1xuICBwaXBlbGluZU5hbWU6IHN0cmluZyB8IG51bGw7XG4gIHRvdGFsOiBudW1iZXI7XG4gIHN1Y2NlZWRlZDogbnVtYmVyO1xuICBmYWlsZWQ6IG51bWJlcjtcbiAgY2FuY2VsZWQ6IG51bWJlcjtcbiAgZmlyc3RFeGVjdXRpb246IHN0cmluZyB8IG51bGw7XG4gIGxhc3RFeGVjdXRpb246IHN0cmluZyB8IG51bGw7XG59XG5cbmludGVyZmFjZSBUaW1lU2VyaWVzRW50cnkge1xuICBwZXJpb2Q6IHN0cmluZztcbiAgc3VjY2VlZGVkOiBudW1iZXI7XG4gIGZhaWxlZDogbnVtYmVyO1xuICBjYW5jZWxlZDogbnVtYmVyO1xuICBzdWNjZXNzUGN0OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBEdXJhdGlvblN0YXRzIHtcbiAgaWQ6IHN0cmluZztcbiAgcHJvamVjdDogc3RyaW5nO1xuICBwaXBlbGluZU5hbWU6IHN0cmluZyB8IG51bGw7XG4gIGF2Z01zOiBudW1iZXI7XG4gIG1pbk1zOiBudW1iZXI7XG4gIG1heE1zOiBudW1iZXI7XG4gIHA5NU1zOiBudW1iZXI7XG4gIGV4ZWN1dGlvbnM6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFBpcGVsaW5lRXhlY3V0aW9uIHtcbiAgZXhlY3V0aW9uSWQ6IHN0cmluZztcbiAgLyoqIFJvbGxlZC11cCB0ZXJtaW5hbCBzdGF0dXM6IHN1Y2NlZWRlZCB8IGZhaWxlZCB8IGNhbmNlbGVkIHwgaW4tcHJvZ3Jlc3MuICovXG4gIHN0YXR1czogc3RyaW5nO1xuICBzdGFydGVkQXQ6IHN0cmluZyB8IG51bGw7XG4gIGVuZGVkQXQ6IHN0cmluZyB8IG51bGw7XG4gIGR1cmF0aW9uTXM6IG51bWJlciB8IG51bGw7XG4gIC8qKiBGaXJzdCBmYWlsaW5nIHN0YWdlL2FjdGlvbiBmb3IgdGhlIGV4ZWN1dGlvbiAobnVsbCB3aGVuIGl0IGRpZG4ndCBmYWlsKS4gKi9cbiAgZmFpbGluZ1N0YWdlOiBzdHJpbmcgfCBudWxsO1xuICBmYWlsaW5nQWN0aW9uOiBzdHJpbmcgfCBudWxsO1xufVxuXG5pbnRlcmZhY2UgU3RhZ2VGYWlsdXJlIHtcbiAgc3RhZ2VOYW1lOiBzdHJpbmc7XG4gIGZhaWx1cmVzOiBudW1iZXI7XG4gIHRvdGFsOiBudW1iZXI7XG4gIGZhaWx1cmVQY3Q6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFN0YWdlQm90dGxlbmVjayB7XG4gIGlkOiBzdHJpbmc7XG4gIHBpcGVsaW5lTmFtZTogc3RyaW5nIHwgbnVsbDtcbiAgc3RhZ2VOYW1lOiBzdHJpbmc7XG4gIGF2Z01zOiBudW1iZXI7XG4gIG1heE1zOiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBBY3Rpb25GYWlsdXJlIHtcbiAgYWN0aW9uTmFtZTogc3RyaW5nO1xuICBmYWlsdXJlczogbnVtYmVyO1xuICB0b3RhbDogbnVtYmVyO1xuICBmYWlsdXJlUGN0OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBFcnJvckVudHJ5IHtcbiAgZXJyb3JQYXR0ZXJuOiBzdHJpbmc7XG4gIG9jY3VycmVuY2VzOiBudW1iZXI7XG4gIGFmZmVjdGVkUGlwZWxpbmVzOiBudW1iZXI7XG4gIGxhc3RTZWVuOiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBQbHVnaW5TdW1tYXJ5IHtcbiAgdG90YWw6IG51bWJlcjtcbiAgYWN0aXZlOiBudW1iZXI7XG4gIGluYWN0aXZlOiBudW1iZXI7XG4gIC8qKiBDb3VudHMgcGVyIHNoYXJpbmcgcnVuZyDigJQgb25lIGJ1Y2tldCBwZXIgYHZpc2liaWxpdHlgIHZhbHVlLiAqL1xuICBwdWJsaWM6IG51bWJlcjtcbiAgb3JnOiBudW1iZXI7XG4gIHByaXZhdGU6IG51bWJlcjtcbiAgdW5pcXVlTmFtZXM6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFR5cGVDb21wdXRlRGlzdHJpYnV0aW9uIHtcbiAgcGx1Z2luVHlwZTogc3RyaW5nO1xuICBjb21wdXRlVHlwZTogc3RyaW5nO1xuICBjb3VudDogbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgVmVyc2lvbkNvdW50IHtcbiAgbmFtZTogc3RyaW5nO1xuICB2ZXJzaW9uQ291bnQ6IG51bWJlcjtcbiAgbGF0ZXN0VmVyc2lvbjogc3RyaW5nO1xuICBoYXNEZWZhdWx0OiBib29sZWFuO1xufVxuXG5pbnRlcmZhY2UgQnVpbGRUaW1lU2VyaWVzRW50cnkge1xuICBwZXJpb2Q6IHN0cmluZztcbiAgc3VjY2VlZGVkOiBudW1iZXI7XG4gIGZhaWxlZDogbnVtYmVyO1xuICBzdWNjZXNzUGN0OiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBCdWlsZER1cmF0aW9uIHtcbiAgcGx1Z2luTmFtZTogc3RyaW5nO1xuICBhdmdNczogbnVtYmVyO1xuICBtYXhNczogbnVtYmVyO1xuICBidWlsZHM6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIEJ1aWxkRmFpbHVyZSB7XG4gIHBsdWdpbk5hbWU6IHN0cmluZztcbiAgZXJyb3JNZXNzYWdlOiBzdHJpbmc7XG4gIG9jY3VycmVuY2VzOiBudW1iZXI7XG4gIGxhc3RTZWVuOiBzdHJpbmc7XG59XG5cbi8qKlxuICogVGhlIERPUkEgaGVhZGxpbmUgZW52aXJvbm1lbnQuIERlcGxveW1lbnQgdG8gdGhpcyBsaXRlcmFsIGVudiBuYW1lIGlzIHRoZVxuICogaW5kdXN0cnktc3RhbmRhcmQgRE9SQSBzaWduYWw7IE1UVFIgaXMgbWVhc3VyZWQgcHJvZHVjdGlvbi1vbmx5LCBhbmQgdGhpc1xuICogZW52aXJvbm1lbnQncyBjYXJkIGlzIHRoZSBzdW1tYXJ5IHNob3duIGF0IHRoZSB0b3Agb2YgdGhlIHBhbmVsLlxuICovXG5jb25zdCBIRUFETElORV9FTlYgPSAncHJvZHVjdGlvbic7XG5cbi8qKlxuICogSW5jaWRlbnTihpJkZXBsb3kgY29ycmVsYXRpb24gd2luZG93IChQaGFzZSA1KS4gQW4gaW5jaWRlbnQgaXMgYXR0cmlidXRlZCB0byB0aGVcbiAqIG1vc3QgcmVjZW50IFNVQ0NFU1NGVUwgZGVwbG95IHRvIGl0cyBlbnZpcm9ubWVudCB3aG9zZSBgY29tcGxldGVkX2F0YCBpcyB3aXRoaW5cbiAqIHRoaXMgbWFueSBob3VycyBiZWZvcmUgdGhlIGluY2lkZW50J3MgYG9wZW5lZF9hdGAuIERlZmF1bHQgMjRoOyBvdmVycmlkZSB2aWFcbiAqIGBET1JBX0lOQ0lERU5UX1dJTkRPV19IT1VSU2AuIEd1YXJkZWQgdG8gYSBwb3NpdGl2ZSBmaW5pdGUgbnVtYmVyLlxuICovXG5jb25zdCBET1JBX0lOQ0lERU5UX1dJTkRPV19IT1VSUyA9ICgoKSA9PiB7XG4gIGNvbnN0IHJhdyA9IE51bWJlcihwcm9jZXNzLmVudi5ET1JBX0lOQ0lERU5UX1dJTkRPV19IT1VSUyk7XG4gIHJldHVybiBOdW1iZXIuaXNGaW5pdGUocmF3KSAmJiByYXcgPiAwID8gcmF3IDogMjQ7XG59KSgpO1xuXG4vKipcbiAqIFJlc29sdmUgdGhlIGVmZmVjdGl2ZSBpbmNpZGVudOKGkmRlcGxveSBjb3JyZWxhdGlvbiB3aW5kb3cgKFBoYXNlIDViKS4gQSBwZXItb3JnXG4gKiBvdmVycmlkZSAoZnJvbSBgZG9yYV9zZXR0aW5ncy5pbmNpZGVudF93aW5kb3dfaG91cnNgLCBzdXJmYWNlZCB0aHJvdWdoIHRoZVxuICogc2V0dGluZ3MgZW5kcG9pbnQgYW5kIHRocmVhZGVkIGludG8ge0BsaW5rIERvcmFPcHRpb25zLmluY2lkZW50V2luZG93SG91cnN9KVxuICogd2lucyB3aGVuIGl0IGlzIGEgcG9zaXRpdmUgZmluaXRlIG51bWJlcjsgb3RoZXJ3aXNlIHRoZSBnbG9iYWwgZW52IGRlZmF1bHRcbiAqIGBET1JBX0lOQ0lERU5UX1dJTkRPV19IT1VSU2AgYXBwbGllcy4gQ2VudHJhbGl6ZWQgc28gdGhlIERPUkEgY29ycmVsYXRpb24sIHRoZVxuICogaW5jaWRlbnRzIGxpc3QsIGFuZCB0aGUgd2lyaW5nLXRlc3QgZHJ5LXJ1biBhbGwgYWdyZWUgb24gdGhlIHNhbWUgcnVsZS5cbiAqL1xuZnVuY3Rpb24gcmVzb2x2ZUluY2lkZW50V2luZG93SG91cnMob3ZlcnJpZGU/OiBudW1iZXIgfCBudWxsKTogbnVtYmVyIHtcbiAgcmV0dXJuIG92ZXJyaWRlICE9IG51bGwgJiYgTnVtYmVyLmlzRmluaXRlKG92ZXJyaWRlKSAmJiBvdmVycmlkZSA+IDAgPyBvdmVycmlkZSA6IERPUkFfSU5DSURFTlRfV0lORE9XX0hPVVJTO1xufVxuXG4vKipcbiAqIFJlcG9ydGluZyByZXRlbnRpb24gd2luZG93cyAoUGhhc2UgNykuIFJlY29yZHMgaW4gYHBpcGVsaW5lX2V2ZW50c2AsXG4gKiBgZGVwbG95bWVudF9vdXRjb21lc2AsIGFuZCBgaW5jaWRlbnRzYCBncm93IHVuYm91bmRlZCB3aXRob3V0IGEgc3dlZXAsIHNvIGFcbiAqIHNwbGl0LCBwZXItb3JnIHJldGVudGlvbiBwdXJnZSAoc2VlIHtAbGluayBSZXBvcnRpbmdTZXJ2aWNlLnB1cmdlRXhwaXJlZFJlcG9ydGluZ0RhdGF9KVxuICogaGFyZC1kZWxldGVzIHJvd3Mgb2xkZXIgdGhhbiB0aGVzZSB3aW5kb3dzLCBieSBgY3JlYXRlZF9hdGA6XG4gKiAgLSAqKlN0YW5kYXJkIGV2ZW50cyoqIOKAlCBgcGlwZWxpbmVfZXZlbnRzYCB3aXRoIGBlbnZpcm9ubWVudCBJUyBOVUxMYFxuICogICAgKG5vbi1kZXBsb3kgU1RBR0UvQUNUSU9OL2J1aWxkKS4gSGlnaCB2b2x1bWUg4oaSIHNob3J0IGRlZmF1bHQgKDMwIGRheXMpLlxuICogIC0gKipET1JBIHNvdXJjZSoqIOKAlCBgcGlwZWxpbmVfZXZlbnRzYCB3aXRoIGBlbnZpcm9ubWVudCBJUyBOT1QgTlVMTGAgKGRlcGxveVxuICogICAgc3RhZ2VzKSBwbHVzIGFsbCBvZiBgZGVwbG95bWVudF9vdXRjb21lc2AgYW5kIGBpbmNpZGVudHNgLiBMb3cgdm9sdW1lIOKGklxuICogICAgbG9uZ2VyIGRlZmF1bHQgKDE4MCBkYXlzKS4gRE9SQSBoaXN0b3J5IGlzIHRoZXJlZm9yZSBib3VuZGVkIGJ5IHRoaXMgd2luZG93O1xuICogICAgdGhlIHJlcG9ydCBxdWVyeSBzdGlsbCBoYXJkLWNhcHMgYXQgMzY1IGRheXMgKGEgbG9uZ2VyIHBlci1vcmcgb3ZlcnJpZGUgb25seVxuICogICAgcmV0YWlucyByYXcgc291cmNlIHJvd3MsIG5ldmVyIHdpZGVucyBhIHJlcG9ydCkuXG4gKiBgaW5nZXN0X2hlYWx0aGAgYW5kIGBkb3JhX3NldHRpbmdzYCBhcmUgbmV2ZXIgcHVyZ2VkLiBFbnYtb3ZlcnJpZGFibGUgZ2xvYmFscztcbiAqIGEgcGVyLW9yZyBvdmVycmlkZSBpbiBgZG9yYV9zZXR0aW5nc2Agd2lucyAoc2VlIHtAbGluayByZXNvbHZlRXZlbnRSZXRlbnRpb25EYXlzfSkuXG4gKi9cbmNvbnN0IFJFVEVOVElPTl9NSU5fREFZUyA9IDE7XG5jb25zdCBSRVRFTlRJT05fTUFYX0RBWVMgPSA3MzA7XG5cbi8qKiBQYXJzZSBhIHBvc2l0aXZlLWludCBlbnYgZGF5LWNvdW50LCBjbGFtcGVkIHRvIFtNSU4sIE1BWF0sIGVsc2UgYGZhbGxiYWNrYC4gKi9cbmZ1bmN0aW9uIHJldGVudGlvbkVudkRheXMobmFtZTogc3RyaW5nLCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3QgcmF3ID0gTnVtYmVyKHByb2Nlc3MuZW52W25hbWVdKTtcbiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUocmF3KSB8fCAhTnVtYmVyLmlzSW50ZWdlcihyYXcpIHx8IHJhdyA8IFJFVEVOVElPTl9NSU5fREFZUykgcmV0dXJuIGZhbGxiYWNrO1xuICByZXR1cm4gTWF0aC5taW4ocmF3LCBSRVRFTlRJT05fTUFYX0RBWVMpO1xufVxuXG5jb25zdCBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMgPSByZXRlbnRpb25FbnZEYXlzKCdSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMnLCAzMCk7XG5jb25zdCBSRVBPUlRJTkdfRE9SQV9SRVRFTlRJT05fREFZUyA9IHJldGVudGlvbkVudkRheXMoJ1JFUE9SVElOR19ET1JBX1JFVEVOVElPTl9EQVlTJywgMTgwKTtcblxuLyoqIEVmZmVjdGl2ZSBzdGFuZGFyZC1ldmVudCByZXRlbnRpb24gKGRheXMpOiBhIHZhbGlkIHBlci1vcmcgb3ZlcnJpZGUgd2lucyxcbiAqICBlbHNlIHRoZSBnbG9iYWwgZW52IGRlZmF1bHQuIFNoYXJlZCBieSB0aGUgc3dlZXAgKyB0aGUgc2V0dGluZ3Mgc3VyZmFjZSBzb1xuICogIHRoZXkgY2FuJ3QgZHJpZnQuIFRoZSBgLTFgIHVubGltaXRlZCBzZW50aW5lbCAoUGhhc2UgOCkgcGFzc2VzIHN0cmFpZ2h0XG4gKiAgdGhyb3VnaCDigJQgdGhlIHN3ZWVwIHJlYWRzIGl0IGFzIFwia2VlcCBmb3JldmVyLCBza2lwIHRoaXMgb3JnJ3Mgc3RhbmRhcmRcbiAqICBldmVudHNcIi4gQSBwb3NpdGl2ZSBvdmVycmlkZSBjbGFtcHMgdG8gWzEsIFJFVEVOVElPTl9NQVhfREFZU107IGEgYG51bGxgLCBhXG4gKiAgbm9uLWludGVnZXIsIG9yIGFueSBvdGhlciBvdXQtb2YtcmFuZ2UgdmFsdWUgKGUuZy4gMCwgLTIpIGZhbGxzIGJhY2sgdG8gdGhlXG4gKiAgZW52IGRlZmF1bHQuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUV2ZW50UmV0ZW50aW9uRGF5cyhvdmVycmlkZT86IG51bWJlciB8IG51bGwpOiBudW1iZXIge1xuICBpZiAob3ZlcnJpZGUgPT09IC0xKSByZXR1cm4gLTE7XG4gIHJldHVybiBvdmVycmlkZSAhPSBudWxsICYmIE51bWJlci5pc0ludGVnZXIob3ZlcnJpZGUpICYmIG92ZXJyaWRlID49IFJFVEVOVElPTl9NSU5fREFZU1xuICAgID8gTWF0aC5taW4ob3ZlcnJpZGUsIFJFVEVOVElPTl9NQVhfREFZUykgOiBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVM7XG59XG5cbi8qKiBFZmZlY3RpdmUgRE9SQS1zb3VyY2UgcmV0ZW50aW9uIChkYXlzKTogYSB2YWxpZCBwZXItb3JnIG92ZXJyaWRlIHdpbnMsIGVsc2VcbiAqICB0aGUgZ2xvYmFsIGVudiBkZWZhdWx0LiBUaGUgYC0xYCB1bmxpbWl0ZWQgc2VudGluZWwgKFBoYXNlIDgpIHBhc3NlcyBzdHJhaWdodFxuICogIHRocm91Z2gg4oCUIHRoZSBzd2VlcCByZWFkcyBpdCBhcyBcImtlZXAgZm9yZXZlciwgc2tpcCB0aGlzIG9yZydzIERPUkEtc291cmNlXG4gKiAgd2luZG93c1wiLiBBIHBvc2l0aXZlIG92ZXJyaWRlIGNsYW1wcyB0byBbMSwgUkVURU5USU9OX01BWF9EQVlTXTsgYSBgbnVsbGAsIGFcbiAqICBub24taW50ZWdlciwgb3IgYW55IG90aGVyIG91dC1vZi1yYW5nZSB2YWx1ZSAoZS5nLiAwLCAtMikgZmFsbHMgYmFjayB0byB0aGVcbiAqICBlbnYgZGVmYXVsdC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRG9yYVJldGVudGlvbkRheXMob3ZlcnJpZGU/OiBudW1iZXIgfCBudWxsKTogbnVtYmVyIHtcbiAgaWYgKG92ZXJyaWRlID09PSAtMSkgcmV0dXJuIC0xO1xuICByZXR1cm4gb3ZlcnJpZGUgIT0gbnVsbCAmJiBOdW1iZXIuaXNJbnRlZ2VyKG92ZXJyaWRlKSAmJiBvdmVycmlkZSA+PSBSRVRFTlRJT05fTUlOX0RBWVNcbiAgICA/IE1hdGgubWluKG92ZXJyaWRlLCBSRVRFTlRJT05fTUFYX0RBWVMpIDogUkVQT1JUSU5HX0RPUkFfUkVURU5USU9OX0RBWVM7XG59XG5cbi8qKiBUaGUgY3V0b2ZmIGluc3RhbnQgZm9yIGEgcmV0ZW50aW9uIHdpbmRvdzogcm93cyB3aXRoIGBjcmVhdGVkX2F0YCBzdHJpY3RseVxuICogIGJlZm9yZSB0aGlzIChgPCBjdXRvZmZgKSBhcmUgZXhwaXJlZC4gRGV0ZXJtaW5pc3RpYyBmb3IgYSBmaXhlZCBgbm93YC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXRlbnRpb25DdXRvZmYobm93OiBEYXRlLCBkYXlzOiBudW1iZXIpOiBEYXRlIHtcbiAgcmV0dXJuIG5ldyBEYXRlKG5vdy5nZXRUaW1lKCkgLSBkYXlzICogODZfNDAwXzAwMCk7XG59XG5cbi8qKiBQZXItb3JnIHJldGVudGlvbiBvdmVycmlkZSByb3cgKFBoYXNlIDcpLiBCb3RoIG51bGxhYmxlIOKHkiBnbG9iYWwgZGVmYXVsdHMuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlcG9ydGluZ1JldGVudGlvblNldHRpbmdzIHtcbiAgZXZlbnRSZXRlbnRpb25EYXlzOiBudW1iZXIgfCBudWxsO1xuICBkb3JhUmV0ZW50aW9uRGF5czogbnVtYmVyIHwgbnVsbDtcbiAgZGVmYXVsdEV2ZW50UmV0ZW50aW9uRGF5czogbnVtYmVyO1xuICBkZWZhdWx0RG9yYVJldGVudGlvbkRheXM6IG51bWJlcjtcbn1cblxuLyoqIE9wdGlvbnMgZm9yIHRoZSByZXBvcnRpbmcgcmV0ZW50aW9uIHN3ZWVwLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXBvcnRpbmdSZXRlbnRpb25PcHRpb25zIHtcbiAgLyoqIFJvd3MgZGVsZXRlZCBwZXIgc3RhdGVtZW50IChkZWZhdWx0IDEwMDApLiAqL1xuICBiYXRjaFNpemU/OiBudW1iZXI7XG4gIC8qKiBNYXggYmF0Y2hlcyBwZXIgdGFibGUgcGVyIG9yZyBwZXIgdGljayAoZGVmYXVsdCA1MCk7IHRoZSByZXN0IGRlZmVycyB0b1xuICAgKiAgdGhlIG5leHQgdGljayBzbyBhIGh1Z2UgYmFja2xvZyBjYW4ndCBob2xkIGxvY2tzIHRvbyBsb25nLiAqL1xuICBtYXhCYXRjaGVzUGVyVGFibGU/OiBudW1iZXI7XG4gIC8qKiBGaXhlZCBcIm5vd1wiIGZvciB0aGUgd2hvbGUgc3dlZXAgKGRlZmF1bHQgYG5ldyBEYXRlKClgKS4gKi9cbiAgbm93PzogRGF0ZTtcbn1cblxuLyoqIFBlci1ydW4gcHVyZ2UgdGFsbGllcyAoYWxzbyBsb2dnZWQpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXBvcnRpbmdSZXRlbnRpb25Db3VudHMge1xuICAvKiogT3JncyBzd2VwdCB0aGlzIHJ1bi4gKi9cbiAgb3JnczogbnVtYmVyO1xuICAvKiogU3RhbmRhcmQgYHBpcGVsaW5lX2V2ZW50c2AgKGVudmlyb25tZW50IElTIE5VTEwpIHJvd3MgcHVyZ2VkLiAqL1xuICBzdGFuZGFyZEV2ZW50czogbnVtYmVyO1xuICAvKiogRE9SQS1zb3VyY2UgYHBpcGVsaW5lX2V2ZW50c2AgKGVudmlyb25tZW50IElTIE5PVCBOVUxMKSByb3dzIHB1cmdlZC4gKi9cbiAgZG9yYUV2ZW50czogbnVtYmVyO1xuICAvKiogYGRlcGxveW1lbnRfb3V0Y29tZXNgIHJvd3MgcHVyZ2VkLiAqL1xuICBkZXBsb3ltZW50T3V0Y29tZXM6IG51bWJlcjtcbiAgLyoqIGBpbmNpZGVudHNgIHJvd3MgcHVyZ2VkLiAqL1xuICBpbmNpZGVudHM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBET1JBIHBlcmZvcm1hbmNlIGJhbmQuIGBudWxsYCA9IGluc3VmZmljaWVudCBkYXRhIHRvIGNsYXNzaWZ5IChub1xuICogZGVwbG95bWVudHMvZmFpbHVyZXMvbGVhZC10aW1lIHNhbXBsZSBpbiB0aGUgd2luZG93KSwgc28gdGhlIFVJIHNob3dzIGFcbiAqIG5ldXRyYWwgc3RhdGUgcmF0aGVyIHRoYW4gbWlzbGFiZWxpbmcgYW4gZW1wdHkgd2luZG93IGFzIFwibG93XCIuXG4gKi9cbmV4cG9ydCB0eXBlIERvcmFMZXZlbCA9ICdlbGl0ZScgfCAnaGlnaCcgfCAnbWVkaXVtJyB8ICdsb3cnIHwgbnVsbDtcblxuLyoqIE9wdGlvbmFsIHNjb3BpbmcgZm9yIHtAbGluayBSZXBvcnRpbmdTZXJ2aWNlLmdldERvcmFNZXRyaWNzfS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRG9yYU9wdGlvbnMge1xuICAvKiogUmVzdHJpY3QgdG8gYSBzaW5nbGUgcGlwZWxpbmUgKHBlci1waXBlbGluZSBET1JBKS4gKi9cbiAgcGlwZWxpbmVJZD86IHN0cmluZztcbiAgLyoqIFJlc3RyaWN0IHRvIGEgc2luZ2xlIGRlcGxveSBlbnZpcm9ubWVudCAoZWxzZSBhbGwgZGVwbG95IGVudmlyb25tZW50cykuICovXG4gIGVudmlyb25tZW50Pzogc3RyaW5nO1xuICAvKipcbiAgICogUGVyLW9yZyBpbmNpZGVudOKGkmRlcGxveSBjb3JyZWxhdGlvbiB3aW5kb3cgb3ZlcnJpZGUgKGhvdXJzKS4gV2hlbiBzZXQgKGFcbiAgICogcG9zaXRpdmUgZmluaXRlIG51bWJlciksIHRoZSBpbmNpZGVudCBjb3JyZWxhdGlvbiB1c2VzIGl0IGluc3RlYWQgb2YgdGhlXG4gICAqIGdsb2JhbCBgRE9SQV9JTkNJREVOVF9XSU5ET1dfSE9VUlNgLiBUaGUgcm91dGUgcmVzb2x2ZXMgdGhpcyBmcm9tIHRoZSBvcmcnc1xuICAgKiBgZG9yYV9zZXR0aW5nc2Agcm93IChzZWUge0BsaW5rIFJlcG9ydGluZ1NlcnZpY2UuZ2V0SW5jaWRlbnRTZXR0aW5nc30pIGFuZFxuICAgKiBwYXNzZXMgaXQgaW47IHVuc2V0IGZhbGxzIGJhY2sgdG8gdGhlIGVudiBkZWZhdWx0LlxuICAgKi9cbiAgaW5jaWRlbnRXaW5kb3dIb3Vycz86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBQZXItb3JnIHJlcG9ydGluZyBzZXR0aW5ncyBzdXJmYWNlZCB0byB0aGUgb3JnLWFkbWluIHBhbmVsLiBgaW5jaWRlbnRXaW5kb3dIb3Vyc2BcbiAqIChQaGFzZSA1YikgaXMgdGhlIGluY2lkZW504oaSZGVwbG95IGNvcnJlbGF0aW9uIHdpbmRvdzsgYGV2ZW50UmV0ZW50aW9uRGF5c2AgL1xuICogYGRvcmFSZXRlbnRpb25EYXlzYCAoUGhhc2UgNykgYXJlIHRoZSB0d28gcmV0ZW50aW9uLXN3ZWVwIHdpbmRvd3MuIEVhY2ggb3ZlcnJpZGVcbiAqIGlzIGBudWxsYCB3aGVuIHVuc2V0ICh0aGUgcGFpcmVkIGBkZWZhdWx0KmAgZmllbGQgc2hvd3MgdGhlIGVudiBmYWxsYmFjayBhcHBsaWVkKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbmNpZGVudFNldHRpbmdzIHtcbiAgLyoqIFRoZSBvcmcncyBzdG9yZWQgY29ycmVsYXRpb24td2luZG93IG92ZXJyaWRlIGluIGhvdXJzLCBvciBgbnVsbGAgd2hlbiB1bnNldC4gKi9cbiAgaW5jaWRlbnRXaW5kb3dIb3VyczogbnVtYmVyIHwgbnVsbDtcbiAgLyoqIFRoZSBnbG9iYWwgZW52IGRlZmF1bHQgYXBwbGllZCB3aGVuIG5vIG92ZXJyaWRlIGlzIHN0b3JlZC4gKi9cbiAgZGVmYXVsdFdpbmRvd0hvdXJzOiBudW1iZXI7XG4gIC8qKiBTdGFuZGFyZC1ldmVudCByZXRlbnRpb24gb3ZlcnJpZGUgaW4gZGF5cywgb3IgYG51bGxgIHdoZW4gdW5zZXQgKFBoYXNlIDcpLiAqL1xuICBldmVudFJldGVudGlvbkRheXM6IG51bWJlciB8IG51bGw7XG4gIC8qKiBET1JBLXNvdXJjZSByZXRlbnRpb24gb3ZlcnJpZGUgaW4gZGF5cywgb3IgYG51bGxgIHdoZW4gdW5zZXQgKFBoYXNlIDcpLiAqL1xuICBkb3JhUmV0ZW50aW9uRGF5czogbnVtYmVyIHwgbnVsbDtcbiAgLyoqIEdsb2JhbCBzdGFuZGFyZC1ldmVudCByZXRlbnRpb24gZGVmYXVsdCBhcHBsaWVkIHdoZW4gdW5zZXQgKGRheXMpLiAqL1xuICBkZWZhdWx0RXZlbnRSZXRlbnRpb25EYXlzOiBudW1iZXI7XG4gIC8qKiBHbG9iYWwgRE9SQS1zb3VyY2UgcmV0ZW50aW9uIGRlZmF1bHQgYXBwbGllZCB3aGVuIHVuc2V0IChkYXlzKS4gKi9cbiAgZGVmYXVsdERvcmFSZXRlbnRpb25EYXlzOiBudW1iZXI7XG59XG5cbi8qKiBBIHBhcnRpYWwgcmVwb3J0aW5nLXNldHRpbmdzIHdyaXRlIChQaGFzZSA1YiArIDcpLiBPbmx5IHByb3ZpZGVkIGZpZWxkcyBhcmVcbiAqICB1cHNlcnRlZDsgb21pdHRlZCBmaWVsZHMgYXJlIGxlZnQgdW5jaGFuZ2VkIChhbiBvbWl0dGVkIHJldGVudGlvbiBmaWVsZCBrZWVwc1xuICogIGl0cyBzdG9yZWQgb3ZlcnJpZGUgLyB0aGUgZ2xvYmFsIGRlZmF1bHQpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXBvcnRpbmdTZXR0aW5nc1BhdGNoIHtcbiAgaW5jaWRlbnRXaW5kb3dIb3Vycz86IG51bWJlcjtcbiAgZXZlbnRSZXRlbnRpb25EYXlzPzogbnVtYmVyO1xuICBkb3JhUmV0ZW50aW9uRGF5cz86IG51bWJlcjtcbn1cblxuLyoqIE9uZSByb3cgb2YgdGhlIG9yZy1hZG1pbiBpbmNpZGVudHMgbGlzdCAocmVjZW50IGluY2lkZW50cyArIGRlcGxveSBjb3JyZWxhdGlvbikuICovXG5leHBvcnQgaW50ZXJmYWNlIEluY2lkZW50TGlzdEl0ZW0ge1xuICBpbmNpZGVudElkOiBzdHJpbmc7XG4gIGVudmlyb25tZW50OiBzdHJpbmc7XG4gIHNldmVyaXR5OiBzdHJpbmc7XG4gIG9wZW5lZEF0OiBzdHJpbmcgfCBudWxsO1xuICByZXNvbHZlZEF0OiBzdHJpbmcgfCBudWxsO1xuICBjcmVhdGVkQXQ6IHN0cmluZyB8IG51bGw7XG4gIC8qKiBUcnVlIG9uY2UgdGhlIGluY2lkZW50IGhhcyBhIGByZXNvbHZlZF9hdGAuICovXG4gIHJlc29sdmVkOiBib29sZWFuO1xuICAvKiogVGhlIGNvcnJlbGF0ZWQgZGVwbG95IGV4ZWN1dGlvbiBpZCAobW9zdCByZWNlbnQgc3VjY2Vzc2Z1bCBkZXBsb3kgaW4td2luZG93KSwgb3IgbnVsbC4gKi9cbiAgY29ycmVsYXRlZEV4ZWN1dGlvbklkOiBzdHJpbmcgfCBudWxsO1xuICAvKiogVGhhdCBkZXBsb3kncyBjb21wbGV0aW9uIGluc3RhbnQsIG9yIG51bGwgd2hlbiB1bmNvcnJlbGF0ZWQuICovXG4gIGRlcGxveUNvbXBsZXRlZEF0OiBzdHJpbmcgfCBudWxsO1xufVxuXG4vKiogUmVzdWx0IG9mIHRoZSB3aXJpbmctdGVzdCBkcnktcnVuIGNvcnJlbGF0aW9uICh7QGxpbmsgUmVwb3J0aW5nU2VydmljZS50ZXN0SW5jaWRlbnRDb3JyZWxhdGlvbn0pLiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbmNpZGVudFRlc3RSZXN1bHQge1xuICBlbnZpcm9ubWVudDogc3RyaW5nO1xuICAvKiogVGhlIHN5bnRoZXRpYyBpbmNpZGVudCdzIG9wZW5lZEF0IChub3csIElTTykuICovXG4gIG9wZW5lZEF0OiBzdHJpbmc7XG4gIC8qKiBUaGUgZWZmZWN0aXZlIGNvcnJlbGF0aW9uIHdpbmRvdyB1c2VkLiAqL1xuICB3aW5kb3dIb3VyczogbnVtYmVyO1xuICAvKiogV2hldGhlciBhIHJlY2VudCBzdWNjZXNzZnVsIGRlcGxveSB0byBgZW52aXJvbm1lbnRgIGNvcnJlbGF0ZWQuICovXG4gIGNvcnJlbGF0ZWQ6IGJvb2xlYW47XG4gIC8qKiBUaGUgY29ycmVsYXRlZCBkZXBsb3kgZXhlY3V0aW9uIGlkLCBvciBudWxsLiAqL1xuICBleGVjdXRpb25JZDogc3RyaW5nIHwgbnVsbDtcbiAgLyoqIFRoYXQgZGVwbG95J3MgY29tcGxldGlvbiBpbnN0YW50LCBvciBudWxsLiAqL1xuICBkZXBsb3lDb21wbGV0ZWRBdDogc3RyaW5nIHwgbnVsbDtcbn1cblxuLy8gRE9SQSBwZXJmb3JtYW5jZSBiYW5kcyAodGhyZXNob2xkcyBmcm9tIHRoZSBET1JBL0FjY2VsZXJhdGUgcmVwb3J0cykuIEVhY2hcbi8vIGhlbHBlciByZXR1cm5zIGBudWxsYCB3aGVuIHRoZXJlJ3Mgbm8gc2FtcGxlIHRvIGNsYXNzaWZ5LlxuLyoqIERlcGxveW1lbnQgZnJlcXVlbmN5IGJ5IGRlcGxveXMvZGF5OiBlbGl0ZSDiiaUxL2RheSwgaGlnaCDiiaUxL3dlZWssIG1lZGl1bSDiiaUxL21vbnRoLiAqL1xuZnVuY3Rpb24gZG9yYUxldmVsRm9yRnJlcXVlbmN5KHBlckRheTogbnVtYmVyLCBkZXBsb3ltZW50czogbnVtYmVyKTogRG9yYUxldmVsIHtcbiAgaWYgKGRlcGxveW1lbnRzIDw9IDApIHJldHVybiBudWxsO1xuICBpZiAocGVyRGF5ID49IDEpIHJldHVybiAnZWxpdGUnO1xuICBpZiAocGVyRGF5ID49IDEgLyA3KSByZXR1cm4gJ2hpZ2gnO1xuICBpZiAocGVyRGF5ID49IDEgLyAzMCkgcmV0dXJuICdtZWRpdW0nO1xuICByZXR1cm4gJ2xvdyc7XG59XG4vKiogQ2hhbmdlIGZhaWx1cmUgcmF0ZSBieSBwZXJjZW50OiBlbGl0ZSDiiaQ1JSwgaGlnaCDiiaQxMCUsIG1lZGl1bSDiiaQxNSUuICovXG5mdW5jdGlvbiBkb3JhTGV2ZWxGb3JDaGFuZ2VGYWlsdXJlKHBjdDogbnVtYmVyLCB0b3RhbDogbnVtYmVyKTogRG9yYUxldmVsIHtcbiAgaWYgKHRvdGFsIDw9IDApIHJldHVybiBudWxsO1xuICBpZiAocGN0IDw9IDUpIHJldHVybiAnZWxpdGUnO1xuICBpZiAocGN0IDw9IDEwKSByZXR1cm4gJ2hpZ2gnO1xuICBpZiAocGN0IDw9IDE1KSByZXR1cm4gJ21lZGl1bSc7XG4gIHJldHVybiAnbG93Jztcbn1cbi8qKiBUaW1lIHRvIHJlc3RvcmUgYnkgc2Vjb25kczogZWxpdGUgPDFoLCBoaWdoIDwxIGRheSwgbWVkaXVtIDwxIHdlZWsuICovXG5mdW5jdGlvbiBkb3JhTGV2ZWxGb3JSZXN0b3JlKGF2Z1NlY29uZHM6IG51bWJlciB8IG51bGwpOiBEb3JhTGV2ZWwge1xuICBpZiAoYXZnU2Vjb25kcyA9PSBudWxsKSByZXR1cm4gbnVsbDtcbiAgaWYgKGF2Z1NlY29uZHMgPCAzNjAwKSByZXR1cm4gJ2VsaXRlJztcbiAgaWYgKGF2Z1NlY29uZHMgPCA4NjQwMCkgcmV0dXJuICdoaWdoJztcbiAgaWYgKGF2Z1NlY29uZHMgPCA2MDQ4MDApIHJldHVybiAnbWVkaXVtJztcbiAgcmV0dXJuICdsb3cnO1xufVxuLyoqIExlYWQgdGltZSBieSBzZWNvbmRzOiBlbGl0ZSA8MSBkYXksIGhpZ2ggPDEgd2VlaywgbWVkaXVtIDwxIG1vbnRoLiAqL1xuZnVuY3Rpb24gZG9yYUxldmVsRm9yTGVhZFRpbWUobWVkaWFuU2Vjb25kczogbnVtYmVyIHwgbnVsbCk6IERvcmFMZXZlbCB7XG4gIGlmIChtZWRpYW5TZWNvbmRzID09IG51bGwpIHJldHVybiBudWxsO1xuICBpZiAobWVkaWFuU2Vjb25kcyA8IDg2NDAwKSByZXR1cm4gJ2VsaXRlJztcbiAgaWYgKG1lZGlhblNlY29uZHMgPCA2MDQ4MDApIHJldHVybiAnaGlnaCc7XG4gIGlmIChtZWRpYW5TZWNvbmRzIDwgMjU5MjAwMCkgcmV0dXJuICdtZWRpdW0nO1xuICByZXR1cm4gJ2xvdyc7XG59XG5cbi8qKlxuICogUm91bmQgYHZhbHVlYCB0byBgZGlnaXRzYCBkZWNpbWFsIHBsYWNlcywgcmV0dXJuaW5nIGEgcmVhbCBudW1iZXIuIFNtYWxsXG4gKiBtb2R1bGUtcHJpdmF0ZSBoZWxwZXIgc28gdGhlIERPUkEgc2hhcGluZyBkb2Vzbid0IHJlcGVhdCB0aGVcbiAqIGBOdW1iZXIoTnVtYmVyKHgpLnRvRml4ZWQobikpYCBwYXR0ZXJuICh0b0ZpeGVkIGFsb25lIHlpZWxkcyBhIHN0cmluZykuXG4gKi9cbmZ1bmN0aW9uIHJvdW5kKHZhbHVlOiBudW1iZXIsIGRpZ2l0czogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE51bWJlcih2YWx1ZS50b0ZpeGVkKGRpZ2l0cykpO1xufVxuXG4vKipcbiAqIE1lZGlhbiBvZiBhIG51bWVyaWMgc2FtcGxlLCBvciBgbnVsbGAgd2hlbiBlbXB0eS4gVXNlZCBmb3IgbWVhc3VyZWQgbGVhZCB0aW1lXG4gKiBhbmQgTVRUUiDigJQgYm90aCBhcmUgY3Jvc3Mtc291cmNlIGdhcHMgY29tcHV0ZWQgaW4gSlMgZnJvbSBjbGFtcGVkIGRlbHRhcywgc29cbiAqIHRoZSBtZWRpYW4gbGl2ZXMgaGVyZSByYXRoZXIgdGhhbiBpbiBTUUwgUEVSQ0VOVElMRV9DT05UIChrZWVwcyB0aGUgZ29sZGVuXG4gKiBmaXh0dXJlIHRlc3RzIGFibGUgdG8gcHJvdmUgdGhlIG1ldHJpYyBtYXRoIHdpdGhvdXQgYSBsaXZlIFBvc3RncmVzKS5cbiAqL1xuZnVuY3Rpb24gbWVkaWFuKHZhbHVlczogbnVtYmVyW10pOiBudW1iZXIgfCBudWxsIHtcbiAgaWYgKHZhbHVlcy5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuICBjb25zdCBzID0gWy4uLnZhbHVlc10uc29ydCgoYSwgYikgPT4gYSAtIGIpO1xuICBjb25zdCBtaWQgPSBNYXRoLmZsb29yKHMubGVuZ3RoIC8gMik7XG4gIHJldHVybiBzLmxlbmd0aCAlIDIgPT09IDEgPyBzW21pZF0gOiAoc1ttaWQgLSAxXSArIHNbbWlkXSkgLyAyO1xufVxuXG4vKipcbiAqIFJvbGwgYSBncm91cCBvZiBkZXBsb3kvc3RhZ2UgZXZlbnRzIChncm91cGVkIHBlciBleGVjdXRpb24pIHVwIHRvIG9uZSB0ZXJtaW5hbFxuICogc3RhdHVzOiBGQUlMRUQgd2lucywgdGhlbiBTVUNDRUVERUQsIGVsc2UgT1RIRVIgKHN0aWxsLXJ1bm5pbmcgLyBub24tdGVybWluYWwpLlxuICogU2hhcmVkIGJ5IHRoZSB0aHJlZSBwZXItKGVudnxzdGFnZSwgZXhlY3V0aW9uKSByb2xsdXAgQ1RFcyDigJQgRE9SQSBtZXRyaWNzLCBET1JBXG4gKiB0cmVuZCwgYW5kIGJ1aWxkIGhlYWx0aCDigJQgc28gdGhlIHByZWNlZGVuY2UgY2FuJ3QgZHJpZnQgYmV0d2VlbiB0aGVtLiBBc3N1bWVzIHRoZVxuICogZ3JvdXBlZCBldmVudCBhbGlhcyBpcyBgZWAuXG4gKi9cbmNvbnN0IHRlcm1pbmFsU3RhdHVzUm9sbHVwID0gc3FsYENBU0VcbiAgICAgICAgICAgICAgV0hFTiBib29sX29yKGUuc3RhdHVzID0gJ0ZBSUxFRCcpIFRIRU4gJ0ZBSUxFRCdcbiAgICAgICAgICAgICAgV0hFTiBib29sX29yKGUuc3RhdHVzID0gJ1NVQ0NFRURFRCcpIFRIRU4gJ1NVQ0NFRURFRCdcbiAgICAgICAgICAgICAgRUxTRSAnT1RIRVInXG4gICAgICAgICAgICBFTkRgO1xuXG4vKiogT3B0aW9uYWwgYFtmcm9tLHRvXWAgd2luZG93IG9uIGBlLnN0YXJ0ZWRfYXRgIGZvciB0aGUgZXhlY3V0aW9uIHJlcG9ydHMg4oCUXG4gKiAgYSBuby1vcCBgc3FsYCBmcmFnbWVudCB3aGVuIGVpdGhlciBib3VuZCBpcyBhYnNlbnQgKGFsbC10aW1lKS4gU2hhcmVkIGJ5IHRoZVxuICogIGV4ZWN1dGlvbi1jb3VudCBhbmQgcGVyLXBpcGVsaW5lLWV4ZWN1dGlvbiByZXBvcnRzIHNvIHRoZXkgbmFycm93IGlkZW50aWNhbGx5LiAqL1xuZnVuY3Rpb24gb3B0aW9uYWxTdGFydGVkQXRSYW5nZShyYW5nZT86IHsgZnJvbT86IHN0cmluZzsgdG8/OiBzdHJpbmcgfSk6IFJldHVyblR5cGU8dHlwZW9mIHNxbD4ge1xuICByZXR1cm4gcmFuZ2U/LmZyb20gJiYgcmFuZ2U/LnRvXG4gICAgPyBzcWxgQU5EIGUuc3RhcnRlZF9hdCA+PSAke3JhbmdlLmZyb219Ojp0aW1lc3RhbXB0eiBBTkQgZS5zdGFydGVkX2F0IDw9ICR7cmFuZ2UudG99Ojp0aW1lc3RhbXB0emBcbiAgICA6IHNxbGBgO1xufVxuXG4vKipcbiAqIFBlci1leGVjdXRpb24gdGVybWluYWwgZGVwbG95IHJvdyAoRDEg4oCUIG9uZSByb3cgcGVyIChlbnZpcm9ubWVudCwgZXhlY3V0aW9uKSxcbiAqIE5PVCBwZXIgc3RhZ2UpLiBUaGUgU1FMIHJvbGxzIGV2ZXJ5IGRlcGxveSBTVEFHRSB0YXJnZXRpbmcgYW4gZW52IHdpdGhpbiBhblxuICogZXhlY3V0aW9uIHVwIHRvIGEgc2luZ2xlIHRlcm1pbmFsIHN0YXR1czsgdGhlIEpTIHNoYXBpbmcgYnVja2V0cyB0aGVzZSBieVxuICogZW52aXJvbm1lbnQuIGBjb21taXRfdHNgIChEMikgaXMgdGhlIGV4ZWN1dGlvbidzIEVBUkxJRVNUIGNvbW1pdCB0aW1lIGFjcm9zc1xuICogYWxsIGl0cyBldmVudHMg4oCUIGNvbW1pdCBlbnJpY2htZW50IHJpZGVzIHRoZSBQSVBFTElORS9zb3VyY2UgZXZlbnQsIG5vdCB0aGVcbiAqIGRlcGxveSBTVEFHRSDigJQgam9pbmVkIGJ5IGV4ZWN1dGlvbl9pZCAobnVsbCB3aGVuIHVucmVzb2x2ZWQg4oaSIGxlYWQgYHVua25vd25gKS5cbiAqIGBpbl93aW5kb3dgIGlzIGZhbHNlIGZvciBhIGxvb2stYmFjay1vbmx5IHJvdyBrZXB0IHNvbGVseSBzbyBpbmNpZGVudHMgb3BlbmVkXG4gKiBuZWFyIGBmcm9tYCBjYW4gY29ycmVsYXRlIHRvIGEgZGVwbG95IHRoYXQgY29tcGxldGVkIGp1c3QgYmVmb3JlIHRoZSB3aW5kb3cuXG4gKi9cbmludGVyZmFjZSBEZXBsb3lSb3cge1xuICBlbnZpcm9ubWVudDogc3RyaW5nO1xuICBzdGF0dXM6ICdTVUNDRUVERUQnIHwgJ0ZBSUxFRCcgfCBzdHJpbmc7XG4gIC8qKiBEZXBsb3kgZXhlY3V0aW9uIGlkIOKAlCB0aGUgYXR0cmlidXRpb24ga2V5IGZvciBkZWR1cCBhZ2FpbnN0IG91dGNvbWVzL2luY2lkZW50cy4gKi9cbiAgZXhlY3V0aW9uX2lkOiBzdHJpbmcgfCBudWxsO1xuICBjb21wbGV0ZWRfYXQ6IHN0cmluZyB8IG51bGw7XG4gIGNvbW1pdF90czogc3RyaW5nIHwgbnVsbDtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGRlcGxveSBjb21wbGV0ZWQgaW5zaWRlIFtmcm9tLHRvXS4gQWJzZW50ICh1bmRlZmluZWQpIGluIHVuaXRcbiAgICogZml4dHVyZXMg4oeSIHRyZWF0ZWQgYXMgaW4td2luZG93LiBgdGAvYGZgIHRvbGVyYXRlZCAocGcgYm9vbGVhbiB0ZXh0IGZvcm0pLlxuICAgKi9cbiAgaW5fd2luZG93PzogYm9vbGVhbiB8IHN0cmluZyB8IG51bGw7XG59XG5cbi8qKiBSYXcgcG9zdC1kZXBsb3kgb3V0Y29tZSBtYXJrZXIgcm93IChmcm9tIGBkZXBsb3ltZW50X291dGNvbWVzYCwgaW4td2luZG93KS4gKi9cbmludGVyZmFjZSBPdXRjb21lUm93IHtcbiAgZW52aXJvbm1lbnQ6IHN0cmluZyB8IG51bGw7XG4gIG91dGNvbWU6ICdmYWlsZWQnIHwgJ3Jlc3RvcmVkJyB8IHN0cmluZztcbiAgLyoqIEV4ZWN1dGlvbiB0aGUgdXNlciBtYXJrZWQg4oCUIGRlZHVwIGtleSBhZ2FpbnN0IGluY2lkZW50LWNvcnJlbGF0ZWQgZGVwbG95cy4gKi9cbiAgZXhlY3V0aW9uX2lkOiBzdHJpbmcgfCBudWxsO1xufVxuXG4vKiogUHJvZHVjdGlvbiByZXN0b3JlZC1pbmNpZGVudCBwYWlyOiByZWNvdmVyeSB0aW1lICsgdGhlIGRlcGxveSdzIGNvbXBsZXRpb24uICovXG5pbnRlcmZhY2UgTXR0clBhaXJSb3cge1xuICByZXN0b3JlZF9hdDogc3RyaW5nIHwgbnVsbDtcbiAgZGVwbG95ZWRfYXQ6IHN0cmluZyB8IG51bGw7XG4gIC8qKiBFeGVjdXRpb24gaWQg4oCUIGxldHMgaW5jaWRlbnQgcHJlY2VkZW5jZSBvdmVycmlkZSB0aGUgbWFudWFsIG91dGNvbWUgcGVyIGRlcGxveS4gKi9cbiAgZXhlY3V0aW9uX2lkOiBzdHJpbmcgfCBudWxsO1xufVxuXG4vKipcbiAqIFJhdyBpbmNpZGVudCByb3cgKGZyb20gYGluY2lkZW50c2AsIG9wZW5lZCBpbi13aW5kb3cpLiBDb3JyZWxhdGVkIGluIEpTIHRvIHRoZVxuICogbW9zdCByZWNlbnQgc3VjY2Vzc2Z1bCBkZXBsb3kgdG8gYGVudmlyb25tZW50YCB3aXRoaW4gRE9SQV9JTkNJREVOVF9XSU5ET1dfSE9VUlNcbiAqIOKGkiBhbiBhdXRvbWF0ZWQgcG9zdC1kZXBsb3kgZmFpbHVyZSAoQ0ZSKSArIHJlYWwgcmVjb3ZlcnkgdGltZSAoTVRUUikuXG4gKi9cbmludGVyZmFjZSBJbmNpZGVudFJvdyB7XG4gIGVudmlyb25tZW50OiBzdHJpbmc7XG4gIG9wZW5lZF9hdDogc3RyaW5nIHwgbnVsbDtcbiAgcmVzb2x2ZWRfYXQ6IHN0cmluZyB8IG51bGw7XG59XG5cbi8qKiBDb3ZlcmFnZSBjb3VudHM6IHJlZ2lzdGVyZWQgcGlwZWxpbmVzIHZzIHRob3NlIHRoYXQgYWN0dWFsbHkgZGVwbG95ZWQuICovXG5pbnRlcmZhY2UgQ292ZXJhZ2VSb3cge1xuICByZWdpc3RlcmVkOiBudW1iZXIgfCBzdHJpbmcgfCBudWxsO1xuICBkZXBsb3lpbmc6IG51bWJlciB8IHN0cmluZyB8IG51bGw7XG59XG5cbi8qKlxuICogRE9SQSBtZXRyaWNzIG92ZXIgYSBbZnJvbSx0b10gd2luZG93LCBvcmctc2NvcGVkIChzaW5nbGUtb3JnIG9yIHJvbGx1cCBzdWJ0cmVlKS5cbiAqXG4gKiBERVBMT1ktQkFTSVMg4oCUIGV2ZXJ5IG1ldHJpYyBkZXJpdmVzIGZyb20gcmVhbCBERVBMT1ktU1RBR0UgZXhlY3V0aW9ucywgaS5lLlxuICogYHBpcGVsaW5lX2V2ZW50c2Agcm93cyB3aXRoIGBldmVudF90eXBlPSdTVEFHRSdgIGFuZCBhIG5vbi1udWxsIGBlbnZpcm9ubWVudGBcbiAqICh0aGUgZm9yd2FyZGVyIHNldHMgYGVudmlyb25tZW50YCBvbmx5IGZvciB0aGUgc3RhZ2VzIGEgdXNlciBkZWNsYXJlZCBpblxuICogYHBiLmRlcGxveXNgKS4gQSBDSS1vbmx5IGJ1aWxkL3Rlc3QgcGlwZWxpbmUgd2l0aCBubyBkZXBsb3kgc3RhZ2UgcHJvZHVjZXMgTk9cbiAqIERPUkEgZGF0YSDigJQgdGhlcmUgaXMgbm8gcnVuLWJhc2VkIGZhbGxiYWNrLiBUaGUgcGFuZWwgaXMgZW1wdHkgdW50aWwgZGVwbG95ZWRcbiAqIHBpcGVsaW5lcyByZS1zeW50aCB3aXRoIHRoZSBuZXcgZGVwbG95IHRhZ3MgYW5kIHN0YXJ0IGVtaXR0aW5nIGRlcGxveSBldmVudHMuXG4gKlxuICogYHByb2R1Y3Rpb25gIGlzIHRoZSBoZWFkbGluZSBlbnZpcm9ubWVudDsgTVRUUiBpcyBtZWFzdXJlZCBwcm9kdWN0aW9uLW9ubHkuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRG9yYU1ldHJpY3Mge1xuICAvKiogVGhlIFtmcm9tLHRvXSB3aW5kb3cgZWNob2VkIGJhY2sgKGRlcGxveSBgY29tcGxldGVkX2F0YCByYW5nZSkuICovXG4gIHdpbmRvdzogeyBmcm9tOiBzdHJpbmc7IHRvOiBzdHJpbmcgfTtcbiAgLyoqIFRoZSBzY29waW5nIGFwcGxpZWQgKGVjaG9lZCBmb3IgdGhlIFVJKTsgYG51bGxgIHdoZW4gdW5zY29wZWQuICovXG4gIGZpbHRlcnM6IHsgcGlwZWxpbmVJZDogc3RyaW5nIHwgbnVsbDsgZW52aXJvbm1lbnQ6IHN0cmluZyB8IG51bGwgfTtcbiAgLyoqIFRoZSBoZWFkbGluZSBlbnZpcm9ubWVudCBuYW1lIChgcHJvZHVjdGlvbmApLiAqL1xuICBoZWFkbGluZTogc3RyaW5nO1xuICAvKiogUGVyLWVudmlyb25tZW50IERGIC8gQ0ZSIC8gbGVhZCB0aW1lLiBTb3J0ZWQgaGVhZGxpbmUtZmlyc3QsIHRoZW4gQeKGklouICovXG4gIGVudmlyb25tZW50czogRG9yYUVudk1ldHJpY3NbXTtcbiAgLyoqXG4gICAqIE1lYW4gVGltZSBUbyBSZXN0b3JlIOKAlCBQUk9EVUNUSU9OLU9OTFksIGZyb20gYGRlcGxveW1lbnRfb3V0Y29tZXNgLlxuICAgKiBgbWVkaWFuKHJlc3RvcmVkLmF0IOKIkiBkZXBsb3llZC5jb21wbGV0ZWRfYXQpYCBvdmVyIHJlc3RvcmVkIHByb2R1Y3Rpb25cbiAgICogaW5jaWRlbnRzIChkZWx0YXMgY2xhbXBlZCDiiaUwKS4gYGluY2lkZW50c2AgY291bnRzIHByb2R1Y3Rpb24gZGVwbG95cyBtYXJrZWRcbiAgICogZmFpbGVkOyBgcmVzdG9yZWRgIGNvdW50cyB0aG9zZSB0aGF0IHJlY292ZXJlZDsgYG1lZGlhblNlY29uZHNgIGlzIG51bGwgd2hlblxuICAgKiBubyByZXN0b3JlZCBpbmNpZGVudCBoYXMgYSByZXNvbHZhYmxlIGRlcGxveSB0aW1lLlxuICAgKi9cbiAgbWVhblRpbWVUb1Jlc3RvcmU6IHtcbiAgICBpbmNpZGVudHM6IG51bWJlcjtcbiAgICByZXN0b3JlZDogbnVtYmVyO1xuICAgIG1lZGlhblNlY29uZHM6IG51bWJlciB8IG51bGw7XG4gICAgbGV2ZWw6IERvcmFMZXZlbDtcbiAgfTtcbiAgLyoqXG4gICAqIENvdmVyYWdlIHJlY29uY2lsaWF0aW9uIOKAlCByZWdpc3RlcmVkIHBpcGVsaW5lcyB3aXRoIG5vIG9ic2VydmVkIGRlcGxveSBpblxuICAgKiB0aGUgd2luZG93LiBBIGhpZ2ggYHdpdGhvdXREZXBsb3lzYCBjb3VudCBtZWFucyBET1JBIGlzIGJsaW5kIHRvIG1vc3Qgb2YgdGhlXG4gICAqIGZsZWV0IChwaXBlbGluZXMgdGhhdCBoYXZlbid0IHJlLXN5bnRoZWQgd2l0aCBkZXBsb3kgdGFncywgb3IgZG9uJ3QgZGVwbG95KS5cbiAgICovXG4gIGNvdmVyYWdlOiB7XG4gICAgLyoqIFBpcGVsaW5lcyBpbiB0aGUgcmVnaXN0cnkgKG9yZy1zY29wZWQpLiAqL1xuICAgIHJlZ2lzdGVyZWQ6IG51bWJlcjtcbiAgICAvKiogUmVnaXN0ZXJlZCBwaXBlbGluZXMgd2l0aCDiiaUxIGRlcGxveS1zdGFnZSBleGVjdXRpb24gaW4gdGhlIHdpbmRvdy4gKi9cbiAgICBkZXBsb3lpbmc6IG51bWJlcjtcbiAgICAvKiogcmVnaXN0ZXJlZCDiiJIgZGVwbG95aW5nIChjbGFtcGVkIOKJpTApLiAqL1xuICAgIHdpdGhvdXREZXBsb3lzOiBudW1iZXI7XG4gIH07XG59XG5cbi8qKiBQZXItZW52aXJvbm1lbnQgRE9SQSBtZXRyaWNzIChkZXBsb3ltZW50IGZyZXF1ZW5jeSwgQ0ZSLCBsZWFkIHRpbWUpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBEb3JhRW52TWV0cmljcyB7XG4gIGVudmlyb25tZW50OiBzdHJpbmc7XG4gIC8qKiBEZXBsb3ltZW50IEZyZXF1ZW5jeSDigJQgc3VjY2Vzc2Z1bCBkZXBsb3ktc3RhZ2UgZXhlY3V0aW9ucyBmb3IgdGhpcyBlbnYuICovXG4gIGRlcGxveW1lbnRGcmVxdWVuY3k6IHtcbiAgICBkZXBsb3ltZW50czogbnVtYmVyO1xuICAgIHBlckRheTogbnVtYmVyO1xuICAgIGxldmVsOiBEb3JhTGV2ZWw7XG4gIH07XG4gIC8qKlxuICAgKiBDaGFuZ2UgRmFpbHVyZSBSYXRlIOKAlCB0d28tY2xhc3M6XG4gICAqIGAoZGVwbG95VGltZUZhaWx1cmVzICsgcG9zdERlcGxveUZhaWx1cmVzKSAvIGF0dGVtcHRzYC5cbiAgICogLSBgZGVwbG95VGltZUZhaWx1cmVzYCDigJQgZGVwbG95IHN0YWdlIGByZXN1bHQ9ZmFpbGVkYCAoZnJvbSBldmVudHMpLlxuICAgKiAtIGBwb3N0RGVwbG95RmFpbHVyZXNgIOKAlCBhIHN1Y2Nlc3NmdWwgZGVwbG95IGxhdGVyIG1hcmtlZCBmYWlsZWQgaW4gcHJvZFxuICAgKiAgIChmcm9tIGBkZXBsb3ltZW50X291dGNvbWVzYCkuXG4gICAqIC0gYGF0dGVtcHRzYCDigJQgYWxsIHRlcm1pbmFsIGRlcGxveS1zdGFnZSBhdHRlbXB0cyAoc3VjY2VlZGVkICsgZmFpbGVkKS5cbiAgICovXG4gIGNoYW5nZUZhaWx1cmVSYXRlOiB7XG4gICAgcmF0ZTogbnVtYmVyO1xuICAgIGRlcGxveVRpbWVGYWlsdXJlczogbnVtYmVyO1xuICAgIHBvc3REZXBsb3lGYWlsdXJlczogbnVtYmVyO1xuICAgIGF0dGVtcHRzOiBudW1iZXI7XG4gICAgbGV2ZWw6IERvcmFMZXZlbDtcbiAgfTtcbiAgLyoqXG4gICAqIExlYWQgVGltZSDigJQgTUVBU1VSRUQ6IGBtZWRpYW4oZGVwbG95X2NvbXBsZXRlZCDiiJIgb2xkZXN0X2NvbW1pdF90aW1lKWAgb3ZlclxuICAgKiBzdWNjZXNzZnVsIGRlcGxveXMgd2l0aCBhIHJlc29sdmFibGUgY29tbWl0IHRpbWVzdGFtcCAoZGVsdGFzIGNsYW1wZWQg4omlMCkuXG4gICAqIGBtZWRpYW5TZWNvbmRzYCBpcyBgbnVsbGAgKD0gdW5rbm93bikgd2hlbiBubyBzdWNjZXNzZnVsIGRlcGxveSBpbiB0aGlzIGVudlxuICAgKiBjYXJyaWVkIGEgYGNvbW1pdF90aW1lc3RhbXBgLiBUaGUgb2xkIG1lZGlhbi1ydW4tZHVyYXRpb24gcHJveHkgaXMgcmVtb3ZlZC5cbiAgICovXG4gIGxlYWRUaW1lOiB7XG4gICAgLyoqIFN1Y2Nlc3NmdWwgZGVwbG95cyB3aXRoIGEgcmVzb2x2YWJsZSBjb21taXQgdGltZSAodGhlIG1lZGlhbiBzYW1wbGUpLiAqL1xuICAgIGRlcGxveW1lbnRzOiBudW1iZXI7XG4gICAgbWVkaWFuU2Vjb25kczogbnVtYmVyIHwgbnVsbDtcbiAgICBsZXZlbDogRG9yYUxldmVsO1xuICB9O1xufVxuXG4vKiogT25lIGludGVydmFsIGJ1Y2tldCBvZiB0aGUgRE9SQSB0cmVuZCAoZGVwbG95IGZyZXF1ZW5jeSArIGNoYW5nZSBmYWlsdXJlKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRG9yYVRyZW5kUG9pbnQge1xuICAvKiogQnVja2V0IHN0YXJ0IChEQVRFX1RSVU5DIG9mIHRoZSBkZXBsb3kgYGNvbXBsZXRlZF9hdGApLCBJU08gdGV4dC4gKi9cbiAgcGVyaW9kOiBzdHJpbmc7XG4gIC8qKiBTdWNjZXNzZnVsIGRlcGxveS1zdGFnZSBleGVjdXRpb25zIGluIHRoZSBidWNrZXQuICovXG4gIGRlcGxveW1lbnRzOiBudW1iZXI7XG4gIC8qKiBGYWlsZWQgZGVwbG95LXN0YWdlIGV4ZWN1dGlvbnMgaW4gdGhlIGJ1Y2tldC4gKi9cbiAgZmFpbGVkOiBudW1iZXI7XG4gIC8qKiBzdWNjZWVkZWQgKyBmYWlsZWQgaW4gdGhlIGJ1Y2tldCAoZGVwbG95IGF0dGVtcHRzKS4gKi9cbiAgdG90YWw6IG51bWJlcjtcbiAgLyoqIGZhaWxlZC90b3RhbCBhcyBhIHBlcmNlbnQgKDAgd2hlbiB0b3RhbCBpcyAwKS4gRGVwbG95LXRpbWUgQ0ZSIG9ubHkuICovXG4gIGNoYW5nZUZhaWx1cmVQY3Q6IG51bWJlcjtcbn1cblxuLyoqIFBlci1zdGFnZSBidWlsZC1oZWFsdGggbWV0cmljcyAoUGhhc2UgNikgZm9yIG9uZSBwaXBlbGluZSBvdmVyIGEgd2luZG93LiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWlsZEhlYWx0aFN0YWdlIHtcbiAgc3RhZ2U6IHN0cmluZztcbiAgLyoqIFRlcm1pbmFsIHN0YWdlIHJ1bnMgKHN1Y2NlZWRlZCArIGZhaWxlZCkgaW4gdGhlIHdpbmRvdy4gKi9cbiAgcnVuczogbnVtYmVyO1xuICBzdWNjZXNzZXM6IG51bWJlcjtcbiAgZmFpbHVyZXM6IG51bWJlcjtcbiAgLyoqIHN1Y2Nlc3NlcyAvIHJ1bnMgYXMgYSBwZXJjZW50ICgwIHdoZW4gcnVucyBpcyAwKS4gKi9cbiAgc3VjY2Vzc1JhdGU6IG51bWJlcjtcbiAgLyoqIER1cmF0aW9uIHBlcmNlbnRpbGVzIG92ZXIgdGhlIHN0YWdlJ3MgdGVybWluYWwgcnVuczsgbnVsbCB3aGVuIG5vIGR1cmF0aW9ucy4gKi9cbiAgcDUwTXM6IG51bWJlciB8IG51bGw7XG4gIHA5ME1zOiBudW1iZXIgfCBudWxsO1xuICBwOTlNczogbnVtYmVyIHwgbnVsbDtcbn1cblxuLyoqXG4gKiBQZXItcGlwZWxpbmUgYnVpbGQtaGVhbHRoIGJyZWFrZG93biAoUGhhc2UgNikuIFN0YW5kYXJkIHJlcG9ydGluZyAoTk9UXG4gKiBgYWR2YW5jZWRfcmVwb3J0aW5nYC1nYXRlZCkg4oCUIHBlci1zdGFnZSBzdWNjZXNzIHJhdGUgKyB0aW1pbmcgcGVyY2VudGlsZXMsIHdpdGhcbiAqIHRvdGFscyBzdW1tZWQgYWNyb3NzIHN0YWdlcy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWlsZEhlYWx0aCB7XG4gIHN0YWdlczogQnVpbGRIZWFsdGhTdGFnZVtdO1xuICB0b3RhbHM6IHsgcnVuczogbnVtYmVyOyBmYWlsdXJlczogbnVtYmVyOyBmYWlsdXJlUmF0ZTogbnVtYmVyIH07XG59XG5cbi8qKiBQb3N0LWRlcGxveSBpbmNpZGVudCBtYXJrZXIgKFBoYXNlIDUpIGFjY2VwdGVkIGJ5IGBSZXBvcnRpbmdTZXJ2aWNlLnJlY29yZEluY2lkZW50YC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5jaWRlbnRJbnB1dCB7XG4gIGluY2lkZW50SWQ6IHN0cmluZztcbiAgZW52aXJvbm1lbnQ6IHN0cmluZztcbiAgb3BlbmVkQXQ6IHN0cmluZztcbiAgcmVzb2x2ZWRBdD86IHN0cmluZztcbiAgc2V2ZXJpdHk6IHN0cmluZztcbn1cblxuLyoqIEV2ZW50IHBheWxvYWQgYWNjZXB0ZWQgYnkgYFJlcG9ydGluZ1NlcnZpY2UuaW5nZXN0RXZlbnRzYC4gTWlycm9ycyB0aGUgcm91dGUncyBab2Qgc2hhcGUuICovXG5leHBvcnQgaW50ZXJmYWNlIEluZ2VzdEV2ZW50IHtcbiAgLyoqIFN0YWJsZSBwaXBlbGluZSBpZCB0aGUgZXZlbnRzIExhbWJkYSByZWFkIGZyb20gdGhlIGBwYi5waXBlbGluZS1pZGBcbiAgICogIHRhZyAoPSB0aGUgcGxhdGZvcm0gcGlwZWxpbmVJZCkuIFRoZSByZWdpc3RyeSBqb2luIGtleS4gKi9cbiAgcGlwZWxpbmVJZDogc3RyaW5nO1xuICBldmVudFNvdXJjZTogJ2NvZGVwaXBlbGluZScgfCAnY29kZWJ1aWxkJyB8ICdwbHVnaW4tYnVpbGQnO1xuICBldmVudFR5cGU6ICdQSVBFTElORScgfCAnU1RBR0UnIHwgJ0FDVElPTicgfCAnQlVJTEQnO1xuICBzdGF0dXM6IHN0cmluZztcbiAgZXhlY3V0aW9uSWQ/OiBzdHJpbmc7XG4gIHN0YWdlTmFtZT86IHN0cmluZztcbiAgYWN0aW9uTmFtZT86IHN0cmluZztcbiAgLyoqIEh1bWFuLXJlYWRhYmxlIGZhaWx1cmUgcmVhc29uIChBY3Rpb24gZXZlbnRzKS4gKi9cbiAgZXJyb3JNZXNzYWdlPzogc3RyaW5nO1xuICBzdGFydGVkQXQ/OiBzdHJpbmc7XG4gIGNvbXBsZXRlZEF0Pzogc3RyaW5nO1xuICBkdXJhdGlvbk1zPzogbnVtYmVyO1xuICAvKiogU291cmNlIGNvbW1pdCBpZCBvZiB0aGUgY2hhbmdlIGJlaW5nIHNoaXBwZWQgKERPUkEgZGVwbG95IGF0dHJpYnV0aW9uKS4gKi9cbiAgY29tbWl0U2hhPzogc3RyaW5nO1xuICAvKiogU291cmNlIHJlZi9icmFuY2ggKERPUkEgZGVwbG95IGF0dHJpYnV0aW9uKS4gKi9cbiAgY29tbWl0UmVmPzogc3RyaW5nO1xuICAvKiogRGVwbG95IHRhcmdldCAoZS5nLiBcInByb2R1Y3Rpb25cIikuIEl0cyBwcmVzZW5jZSBvbiBhIFNUQUdFL0FDVElPTiBldmVudFxuICAgKiAgbWFya3MgYSByZWFsIGRlcGxveW1lbnQgKGRlcml2ZWQgc2VydmVyLXNpZGUg4oCUIHRoZXJlIGlzIG5vIGBpc0RlcGxveWApLiAqL1xuICBlbnZpcm9ubWVudD86IHN0cmluZztcbiAgLyoqIE9sZGVzdCB1bnNoaXBwZWQgY29tbWl0IHRpbWVzdGFtcCAoSVNPIDg2MDEpIGZvciBtZWFzdXJlZCBsZWFkIHRpbWUuICovXG4gIGNvbW1pdFRpbWVzdGFtcD86IHN0cmluZztcbiAgLyoqIE51bWJlciBvZiBjb21taXRzIHNoaXBwZWQgaW4gdGhpcyBjaGFuZ2UgKOKJpTEpLiAqL1xuICBjb21taXRDb3VudD86IG51bWJlcjtcbiAgZGV0YWlsPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG59XG5cbi8qKlxuICogUGVyLWV2ZW50IG1ldHJpYyBkZXNjcmlwdG9yIGVtaXR0ZWQgKHZpYSB0aGUge0BsaW5rIFJlcG9ydGluZ1NlcnZpY2UuaW5nZXN0RXZlbnRzfVxuICogYG9uTWV0cmljYCBob29rKSBmb3IgZWFjaCBSRUdJU1RFUkVEIHRlcm1pbmFsIGRlcGxveS9zdGFnZSBldmVudCwgc28gdGhlIHJvdXRlXG4gKiBsYXllciBjYW4gZmFuIHRoZW0gaW50byBQcm9tZXRoZXVzIGNvdW50ZXJzIHdpdGhvdXQgcGlwZWxpbmUtZGF0YSBpbXBvcnRpbmdcbiAqIGFwaS1zZXJ2ZXIncyBtZXRyaWNzIHJlZ2lzdHJ5LiBgcmVzdWx0YCBpcyB0aGUgdGVybWluYWwgb3V0Y29tZTsgYGVudmlyb25tZW50YFxuICogaXMgbm9uLW51bGwgb25seSBmb3IgYSBkZXBsb3ktc3RhZ2UgZXZlbnQuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5nZXN0TWV0cmljIHtcbiAgcGlwZWxpbmVJZDogc3RyaW5nO1xuICBvcmdJZDogc3RyaW5nO1xuICBzdGFnZTogc3RyaW5nO1xuICBlbnZpcm9ubWVudDogc3RyaW5nIHwgbnVsbDtcbiAgcmVzdWx0OiAnc3VjY2VlZGVkJyB8ICdmYWlsZWQnO1xufVxuXG4vKiogQ291bnRzICsgdGhlIChwb3NzaWJseSB0cnVuY2F0ZWQpIGxpc3Qgb2YgdW5yZWdpc3RlcmVkIHBpcGVsaW5lIGlkcyB0aGUgY2FsbGVyIGNhbiBsb2cuICovXG5leHBvcnQgaW50ZXJmYWNlIEluZ2VzdFJlc3VsdCB7XG4gIGluc2VydGVkOiBudW1iZXI7XG4gIHNraXBwZWQ6IG51bWJlcjtcbiAgdW5yZWdpc3RlcmVkUGlwZWxpbmVJZHM6IHN0cmluZ1tdO1xufVxuXG4vLyDilIDilIDilIAgU2VydmljZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqXG4gKiBSZWFkLW9ubHkgcmVwb3J0aW5nIHNlcnZpY2UgZm9yIHBpcGVsaW5lIGV4ZWN1dGlvbiBhbmQgcGx1Z2luIGludmVudG9yeSBhZ2dyZWdhdGlvbnMuXG4gKiBEb2VzIG5vdCBleHRlbmQgQ3J1ZFNlcnZpY2Ug4oCUIHJlcG9ydHMgYXJlIGFnZ3JlZ2F0ZSBxdWVyaWVzLCBub3QgZW50aXR5IENSVUQuXG4gKlxuICogQWxsIHF1ZXJpZXMgYXJlIGNhY2hlZCBpbi1tZW1vcnkgdG8gYXZvaWQgcmVwZWF0ZWQgZXhwZW5zaXZlIFNRTCBhZ2dyZWdhdGlvbnM6XG4gKiAtIEludmVudG9yeSBxdWVyaWVzIChwbHVnaW4gc3VtbWFyeS9kaXN0cmlidXRpb24vdmVyc2lvbnMpOiA1IG1pbiBUVExcbiAqIC0gVGltZXNlcmllcyBxdWVyaWVzIChleGVjdXRpb24vYnVpbGQgbWV0cmljcyB3aXRoIGRhdGUgcmFuZ2VzKTogMiBtaW4gVFRMXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXBvcnRpbmdTZXJ2aWNlIHtcblxuICAvKiogSW52YWxpZGF0ZSBhbGwgY2FjaGVkIHJlcG9ydHMgZm9yIGFuIG9yZyAoY2FsbCBhZnRlciBldmVudCBpbmdlc3QpLiAqL1xuICBhc3luYyBpbnZhbGlkYXRlT3JnKG9yZ0lkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICBpbnZlbnRvcnlDYWNoZS5pbnZhbGlkYXRlUGF0dGVybihgJHtvcmdJZH06KmApLFxuICAgICAgdGltZXNlcmllc0NhY2hlLmludmFsaWRhdGVQYXR0ZXJuKGAke29yZ0lkfToqYCksXG4gICAgXSk7XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZSBpbmNvbWluZyBldmVudHMgYWdhaW5zdCB0aGUgcGlwZWxpbmUgcmVnaXN0cnksIGJhdGNoLWluc2VydCB0aGVcbiAgICogbWF0Y2hlZCBvbmVzLCBhbmQgaW52YWxpZGF0ZSByZXBvcnRpbmcgY2FjaGVzIGZvciBhZmZlY3RlZCBvcmdzLlxuICAgKiBFdmVudHMgZm9yIHVucmVnaXN0ZXJlZCBwaXBlbGluZSBpZHMgYXJlIGRyb3BwZWQgKGFuZCBsb2dnZWQgYXQgV0FSTlxuICAgKiB3aXRoIHNhbXBsZSBpZHMgc28gYW4gb3BlcmF0b3IgY2FuIHNlZSB3aGVuIEV2ZW50QnJpZGdlIGlzIGRlbGl2ZXJpbmdcbiAgICogZXZlbnRzIGZvciBwaXBlbGluZXMgdGhhdCBoYXZlbid0IGNhbGxlZCBQT1NUIC9waXBlbGluZXMvcmVnaXN0cnkgeWV0KS5cbiAgICpcbiAgICogUmV0dXJucyBjb3VudHMgKyBhIHNhbXBsZSBvZiB1bnJlZ2lzdGVyZWQgcGlwZWxpbmUgaWRzIGZvciBvYnNlcnZhYmlsaXR5LlxuICAgKi9cbiAgYXN5bmMgaW5nZXN0RXZlbnRzKGV2ZW50czogSW5nZXN0RXZlbnRbXSwgb25NZXRyaWM/OiAobTogSW5nZXN0TWV0cmljKSA9PiB2b2lkKTogUHJvbWlzZTxJbmdlc3RSZXN1bHQ+IHtcbiAgICAvLyBNdWx0aS1vcmcgYmF0Y2ggaW5zZXJ0OiB0aGUgY2FsbGVyIHJlc29sdmVzIHRvIG11bHRpcGxlIG9yZ3MgdmlhIHRoZVxuICAgIC8vIHBpcGVsaW5lLXJlZ2lzdHJ5IGxvb2t1cCBiZWxvdywgc28gdGhlIHJvdXRlIGxheWVyIE1VU1QgZXN0YWJsaXNoIGFcbiAgICAvLyBgcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwgLi4uKWAgc2NvcGUgYmVmb3JlIGNhbGxpbmdcbiAgICAvLyB0aGlzIG1ldGhvZC4gVW5kZXIgRk9SQ0UnZCBSTFMsIGEgc2luZ2xlIHR4IHdpdGggYGFwcC5vcmdfaWQgPSA8b25lXG4gICAgLy8gb3JnPmAgY291bGQgb25seSB3cml0ZSBldmVudHMgZm9yIHRoYXQgb3JnOyBieXBhc3MgdmlhIHN5c2FkbWluIGlzXG4gICAgLy8gdGhlIHJpZ2h0IGdhdGUgZm9yIHRoaXMgc2VydmVyLWludGVybmFsIGNyb3NzLXRlbmFudCBlbmRwb2ludC4gU2VlXG4gICAgLy8gYXBpL3JlcG9ydGluZy9zcmMvcm91dGVzL2V2ZW50LWluZ2VzdC50cyBmb3IgdGhlIHdyYXBwZXIuXG4gICAgLy8gUnVuIGluc2VydCBpbnNpZGUgdGhlIHR4LCBidXQgQ09MTEVDVCBhZmZlY3RlZCBvcmdzIGFuZCBpbnZhbGlkYXRlXG4gICAgLy8gY2FjaGVzIEFGVEVSIHRoZSB0eCByZXNvbHZlcy4gS2VlcGluZyBpbnZhbGlkYXRpb24gaW5zaWRlIHRoZSB0eCBoZWxkXG4gICAgLy8gdGhlIHBnIGxvY2tzIG9wZW4gZm9yIHRoZSBkdXJhdGlvbiBvZiB0aGUgY2FjaGUgcm91bmQtdHJpcHMgKFJlZGlzIG9yXG4gICAgLy8gaW4tbWVtb3J5IGludmFsaWRhdGlvbnMgYXJlIHVucmVsYXRlZCB0byB0aGUgdHggYnV0IHN0aWxsIHNlcmlhbGl6ZWRcbiAgICAvLyBpdHMgY29tbWl0KS4gQ2FjaGUgVFRMIGlzIDItNSBtaW4gc28gZmlyZS1hbmQtZm9yZ2V0IHBvc3QtY29tbWl0IGlzXG4gICAgLy8gYW4gYWNjZXB0YWJsZSB0cmFkZSBmb3IgdGlnaHRlciBsb2NrIHdpbmRvd3MuXG4gICAgY29uc3QgeyBpbnNlcnRlZCwgc2tpcHBlZCwgdW5yZWdpc3RlcmVkUGlwZWxpbmVJZHMsIGFmZmVjdGVkT3JncyB9ID0gYXdhaXQgd2l0aFRlbmFudFR4KGFzeW5jICh0eCkgPT4ge1xuICAgICAgLy8gQmF0Y2gtcmVzb2x2ZSBhbGwgdW5pcXVlIHBpcGVsaW5lIGlkcyBpbiBvbmUgcXVlcnlcbiAgICAgIGNvbnN0IHVuaXF1ZUlkcyA9IFsuLi5uZXcgU2V0KGV2ZW50cy5tYXAoZSA9PiBlLnBpcGVsaW5lSWQpKV07XG4gICAgICBjb25zdCByZWdpc3RyeVJvd3MgPSBhd2FpdCB0eFxuICAgICAgICAuc2VsZWN0KHtcbiAgICAgICAgICBwaXBlbGluZUlkOiBzY2hlbWEucGlwZWxpbmVSZWdpc3RyeS5waXBlbGluZUlkLFxuICAgICAgICAgIG9yZ0lkOiBzY2hlbWEucGlwZWxpbmVSZWdpc3RyeS5vcmdJZCxcbiAgICAgICAgfSlcbiAgICAgICAgLmZyb20oc2NoZW1hLnBpcGVsaW5lUmVnaXN0cnkpXG4gICAgICAgIC53aGVyZShpbkFycmF5KHNjaGVtYS5waXBlbGluZVJlZ2lzdHJ5LnBpcGVsaW5lSWQsIHVuaXF1ZUlkcykpO1xuXG4gICAgICBjb25zdCBpZE1hcCA9IG5ldyBNYXAocmVnaXN0cnlSb3dzLm1hcChyID0+IFtyLnBpcGVsaW5lSWQsIHJdKSk7XG5cbiAgICAgIC8vIEJ1aWxkIGluc2VydCBiYXRjaCAoc2tpcCBldmVudHMgd2hvc2UgcGlwZWxpbmUgaXNuJ3QgcmVnaXN0ZXJlZClcbiAgICAgIGNvbnN0IHJvd3M6IEFycmF5PHR5cGVvZiBzY2hlbWEucGlwZWxpbmVFdmVudC4kaW5mZXJJbnNlcnQ+ID0gW107XG4gICAgICBsZXQgc2tpcHBlZExvY2FsID0gMDtcbiAgICAgIGNvbnN0IHVucmVnaXN0ZXJlZExvY2FsOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIGV2ZW50cykge1xuICAgICAgICBjb25zdCByZWdpc3RyeSA9IGlkTWFwLmdldChldmVudC5waXBlbGluZUlkKTtcbiAgICAgICAgaWYgKCFyZWdpc3RyeSkge1xuICAgICAgICAgIHNraXBwZWRMb2NhbCsrO1xuICAgICAgICAgIHVucmVnaXN0ZXJlZExvY2FsLnB1c2goZXZlbnQucGlwZWxpbmVJZCk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICByb3dzLnB1c2goe1xuICAgICAgICAgIC8vIHJlZ2lzdHJ5LnBpcGVsaW5lSWQgPT09IGV2ZW50LnBpcGVsaW5lSWQ7IHVzZSB0aGUgcmVnaXN0cnkncyBzbyB0aGVcbiAgICAgICAgICAvLyBGSyBpcyBhbHdheXMgYSByb3cgdGhhdCBleGlzdHMsIGFuZCBwdWxsIG9yZ0lkIGZyb20gdGhlIHJlZ2lzdHJ5IGZvclxuICAgICAgICAgIC8vIHRlbmFuY3kgKG5ldmVyIHRydXN0IHRoZSBjYWxsZXIncyBjbGFpbWVkIG9yZykuXG4gICAgICAgICAgcGlwZWxpbmVJZDogcmVnaXN0cnkucGlwZWxpbmVJZCxcbiAgICAgICAgICBvcmdJZDogcmVnaXN0cnkub3JnSWQsXG4gICAgICAgICAgZXZlbnRTb3VyY2U6IGV2ZW50LmV2ZW50U291cmNlLFxuICAgICAgICAgIGV2ZW50VHlwZTogZXZlbnQuZXZlbnRUeXBlLFxuICAgICAgICAgIHN0YXR1czogZXZlbnQuc3RhdHVzLFxuICAgICAgICAgIC8vIGV4ZWN1dGlvbklkIGlzIGFuIEFXUy1BU1NJR05FRCBpZGVudGlmaWVyIChhIFVVSUQpLCBub3QgZnJlZS1mb3JtXG4gICAgICAgICAgLy8gdXNlciB0ZXh0LCBzbyBpdCBjYW4ndCBjYXJyeSBhbiBhY2NvdW50IGlkIOKAlCBhbmQgc2NydWJiaW5nIGEgVVVJRFxuICAgICAgICAgIC8vIHRoYXQgaGFwcGVucyB0byBjb250YWluIGEgMTItZGlnaXQgcnVuIHdvdWxkIGNvcnJ1cHQgdGhlIGNvcnJlbGF0aW9uXG4gICAgICAgICAgLy8ga2V5LiBMZWZ0IGludGFjdCBieSBkZXNpZ24uXG4gICAgICAgICAgZXhlY3V0aW9uSWQ6IGV2ZW50LmV4ZWN1dGlvbklkLFxuICAgICAgICAgIC8vIHN0YWdlTmFtZS9hY3Rpb25OYW1lIGFyZSBVU0VSLUFVVEhPUkVEIHBpcGVsaW5lLXN0cnVjdHVyZSBuYW1lc1xuICAgICAgICAgIC8vIHByb21vdGVkIGZyb20gdGhlIENvZGVQaXBlbGluZSBkZXRhaWwg4oCUIHNhbWUgdW50cnVzdGVkLUFXUy1kZXJpdmVkXG4gICAgICAgICAgLy8gb3JpZ2luIGFzIGVycm9yTWVzc2FnZSBiZWxvdywgc28gc2NydWIgdGhlbSBhdCB0aGlzIHBlcnNpc3RlbmNlXG4gICAgICAgICAgLy8gYm91bmRhcnkgdG9vIChhIHN0YWdlIG5hbWVkIHdpdGggYW4gQVJOLzEyLWRpZ2l0IGlkIG11c3Qgbm90IHBlcnNpc3QpLlxuICAgICAgICAgIHN0YWdlTmFtZTogc2NydWJPcHRpb25hbChldmVudC5zdGFnZU5hbWUpLFxuICAgICAgICAgIGFjdGlvbk5hbWU6IHNjcnViT3B0aW9uYWwoZXZlbnQuYWN0aW9uTmFtZSksXG4gICAgICAgICAgLy8gSEFSRCBDT05TVFJBSU5UOiBhbiBBV1MgYWNjb3VudCBpZCBtdXN0IE5FVkVSIGJlIHBlcnNpc3RlZC4gVGhpcyBpc1xuICAgICAgICAgIC8vIHRoZSBEVVJBQkxFIHBlcnNpc3RlbmNlIGJvdW5kYXJ5IGFuZCBtdXN0IG5vdCB0cnVzdCB1cHN0cmVhbTpcbiAgICAgICAgICAvLyBDb2RlUGlwZWxpbmUvQ29kZUJ1aWxkIGZhaWx1cmUgZGV0YWlsICYgbWVzc2FnZXMgcm91dGluZWx5IGNhcnJ5XG4gICAgICAgICAgLy8gQVJOcyAoYXJuOmF3czrigKY6PGFjY291bnQtaWQ+OuKApikgYW5kIGJhcmUgMTItZGlnaXQgYWNjb3VudCBpZHMuIFNjcnViXG4gICAgICAgICAgLy8gYm90aCBmcmVlLWZvcm0gZmllbGRzIGhlcmUgYmVmb3JlIGluc2VydCAocGVyLWV2ZW50LCBub24tbXV0YXRpbmcpLlxuICAgICAgICAgIGVycm9yTWVzc2FnZTogc2NydWJPcHRpb25hbChldmVudC5lcnJvck1lc3NhZ2UpLFxuICAgICAgICAgIHN0YXJ0ZWRBdDogZXZlbnQuc3RhcnRlZEF0ID8gbmV3IERhdGUoZXZlbnQuc3RhcnRlZEF0KSA6IHVuZGVmaW5lZCxcbiAgICAgICAgICBjb21wbGV0ZWRBdDogZXZlbnQuY29tcGxldGVkQXQgPyBuZXcgRGF0ZShldmVudC5jb21wbGV0ZWRBdCkgOiB1bmRlZmluZWQsXG4gICAgICAgICAgZHVyYXRpb25NczogZXZlbnQuZHVyYXRpb25NcyxcbiAgICAgICAgICAvLyBEZXBsb3ktYXR0cmlidXRpb24gZmllbGRzIOKAlCBzY3J1YmJlZCBsaWtlIHRoZSBvdGhlciBmcmVlLWZvcm0gc3RyaW5nc1xuICAgICAgICAgIC8vIHNvIGFuIEFSTi9hY2NvdW50IGlkIGNhbiBuZXZlciBlbnRlciB2aWEgYSByZWYgb3IgZW52aXJvbm1lbnQgbmFtZS5cbiAgICAgICAgICBjb21taXRTaGE6IHNjcnViT3B0aW9uYWwoZXZlbnQuY29tbWl0U2hhKSxcbiAgICAgICAgICBjb21taXRSZWY6IHNjcnViT3B0aW9uYWwoZXZlbnQuY29tbWl0UmVmKSxcbiAgICAgICAgICBlbnZpcm9ubWVudDogc2NydWJPcHRpb25hbChldmVudC5lbnZpcm9ubWVudCksXG4gICAgICAgICAgLy8gTWVhc3VyZWQgbGVhZCB0aW1lIChQaGFzZSA0KTogb2xkZXN0IHVuc2hpcHBlZCBjb21taXQgdGltZSArIGNvdW50LlxuICAgICAgICAgIC8vIGNvbW1pdFRpbWVzdGFtcCBpcyBhbiBBV1MvU0NNLWRlcml2ZWQgaW5zdGFudCAobmV2ZXIgYWNjb3VudC1pZFxuICAgICAgICAgIC8vIHNoYXBlZCk7IGNvbW1pdENvdW50IGlzIGEgcGxhaW4gaW50ZWdlci4gTmVpdGhlciBuZWVkcyBzY3J1YmJpbmcuXG4gICAgICAgICAgY29tbWl0VGltZXN0YW1wOiBldmVudC5jb21taXRUaW1lc3RhbXAgPyBuZXcgRGF0ZShldmVudC5jb21taXRUaW1lc3RhbXApIDogdW5kZWZpbmVkLFxuICAgICAgICAgIGNvbW1pdENvdW50OiBldmVudC5jb21taXRDb3VudCxcbiAgICAgICAgICBkZXRhaWw6IGV2ZW50LmRldGFpbCAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICA/IHNjcnViQXdzSWRlbnRpZmllcnMoZXZlbnQuZGV0YWlsKVxuICAgICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIFBoYXNlIDNiOiBmYW4gdGVybWluYWwgU1RBR0Ugb3V0Y29tZXMgaW50byBQcm9tZXRoZXVzIGNvdW50ZXJzIHZpYSB0aGVcbiAgICAgICAgLy8gcm91dGUncyBob29rIChwaXBlbGluZS1kYXRhIGNhbid0IGltcG9ydCBhcGktc2VydmVyJ3MgcmVnaXN0cnkpLiBFbWl0XG4gICAgICAgIC8vIHBlciByZWdpc3RlcmVkIHN0YWdlIGV2ZW50IHdpdGggYSB0ZXJtaW5hbCBzdWNjZWVkZWQvZmFpbGVkIHJlc3VsdDtcbiAgICAgICAgLy8gYSBub24tbnVsbCBlbnZpcm9ubWVudCBhZGRpdGlvbmFsbHkgbWFya3MgaXQgYSBkZXBsb3kgcmVzdWx0LlxuICAgICAgICBpZiAob25NZXRyaWMgJiYgZXZlbnQuZXZlbnRUeXBlID09PSAnU1RBR0UnICYmIChldmVudC5zdGF0dXMgPT09ICdTVUNDRUVERUQnIHx8IGV2ZW50LnN0YXR1cyA9PT0gJ0ZBSUxFRCcpKSB7XG4gICAgICAgICAgb25NZXRyaWMoe1xuICAgICAgICAgICAgcGlwZWxpbmVJZDogcmVnaXN0cnkucGlwZWxpbmVJZCxcbiAgICAgICAgICAgIG9yZ0lkOiByZWdpc3RyeS5vcmdJZCxcbiAgICAgICAgICAgIHN0YWdlOiBzY3J1Yk9wdGlvbmFsKGV2ZW50LnN0YWdlTmFtZSkgPz8gJycsXG4gICAgICAgICAgICBlbnZpcm9ubWVudDogc2NydWJPcHRpb25hbChldmVudC5lbnZpcm9ubWVudCkgPz8gbnVsbCxcbiAgICAgICAgICAgIHJlc3VsdDogZXZlbnQuc3RhdHVzID09PSAnU1VDQ0VFREVEJyA/ICdzdWNjZWVkZWQnIDogJ2ZhaWxlZCcsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gU1FTIGlzIGF0LWxlYXN0LW9uY2UsIHNvIEV2ZW50QnJpZGdlIGNhbiBkZWxpdmVyIHRoZSBzYW1lIHN0YXRlLWNoYW5nZVxuICAgICAgLy8gdHdpY2UuIGBvbkNvbmZsaWN0RG9Ob3RoaW5nYCArIHRoZSBwYXJ0aWFsIHVuaXF1ZSBpbmRleCBvblxuICAgICAgLy8gKHBpcGVsaW5lX2lkLCBleGVjdXRpb25faWQsIGV2ZW50X3R5cGUsIHN0YXR1cywgc3RhZ2VfbmFtZSwgYWN0aW9uX25hbWUpXG4gICAgICAvLyBtYWtlcyByZS1kZWxpdmVyeSBpZGVtcG90ZW50LiBgcmV0dXJuaW5nYCBnaXZlcyB0aGUgUkVBTCBpbnNlcnRlZCBzZXQgc29cbiAgICAgIC8vIGNvdW50cyArIGNhY2hlIGludmFsaWRhdGlvbiBpZ25vcmUgZHVwbGljYXRlcy5cbiAgICAgIGNvbnN0IGluc2VydGVkUm93cyA9IHJvd3MubGVuZ3RoID4gMFxuICAgICAgICA/IGF3YWl0IHR4Lmluc2VydChzY2hlbWEucGlwZWxpbmVFdmVudCkudmFsdWVzKHJvd3MpXG4gICAgICAgICAgLm9uQ29uZmxpY3REb05vdGhpbmcoKVxuICAgICAgICAgIC5yZXR1cm5pbmcoeyBvcmdJZDogc2NoZW1hLnBpcGVsaW5lRXZlbnQub3JnSWQgfSlcbiAgICAgICAgOiBbXTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaW5zZXJ0ZWQ6IGluc2VydGVkUm93cy5sZW5ndGgsXG4gICAgICAgIHNraXBwZWQ6IHNraXBwZWRMb2NhbCxcbiAgICAgICAgdW5yZWdpc3RlcmVkUGlwZWxpbmVJZHM6IHVucmVnaXN0ZXJlZExvY2FsLFxuICAgICAgICBhZmZlY3RlZE9yZ3M6IFsuLi5uZXcgU2V0KGluc2VydGVkUm93cy5tYXAociA9PiByLm9yZ0lkKSldLFxuICAgICAgfTtcbiAgICB9KTtcblxuICAgIC8vIFN1cmZhY2UgdGhlIHNpbGVudCBza2lwOiBhbiB1bnJlZ2lzdGVyZWQgcGlwZWxpbmUgaWQgdXN1YWxseSBtZWFucyB0aGVcbiAgICAvLyBwaXBlbGluZSBoYXNuJ3QgY2FsbGVkIFBPU1QgL3BpcGVsaW5lcy9yZWdpc3RyeSB5ZXQgKG9yIGl0c1xuICAgIC8vIHBiLnBpcGVsaW5lLWlkIHRhZyBpcyBtaXNzaW5nL3VucmVhZGFibGUgYnkgdGhlIExhbWJkYSkuIExvZ2dpbmcgaXRcbiAgICAvLyBtYWtlcyBhIGJyb2tlbiBqb2luIHZpc2libGUgaW5zdGVhZCBvZiBsb29raW5nIGxpa2UgXCJubyBhY3Rpdml0eVwiLlxuICAgIGlmICh1bnJlZ2lzdGVyZWRQaXBlbGluZUlkcy5sZW5ndGggPiAwKSB7XG4gICAgICBsb2dnZXIud2FybignUGlwZWxpbmUgZXZlbnRzIHNraXBwZWQ6IHBpcGVsaW5lIGlkIG5vdCBmb3VuZCBpbiByZWdpc3RyeScsIHtcbiAgICAgICAgY291bnQ6IHVucmVnaXN0ZXJlZFBpcGVsaW5lSWRzLmxlbmd0aCxcbiAgICAgICAgc2FtcGxlOiB1bnJlZ2lzdGVyZWRQaXBlbGluZUlkcy5zbGljZSgwLCAzKSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIFBvc3QtY29tbWl0IGNhY2hlIGludmFsaWRhdGlvbi4gRmlyZS1hbmQtZm9yZ2V0IHdpdGggbG9nZ2luZyDigJQgVFRMIGlzXG4gICAgLy8gc2hvcnQgZW5vdWdoIHRoYXQgYSBtaXNzZWQgaW52YWxpZGF0aW9uIHNlbGYtaGVhbHMuXG4gICAgaWYgKGFmZmVjdGVkT3Jncy5sZW5ndGggPiAwKSB7XG4gICAgICB2b2lkIFByb21pc2UuYWxsKGFmZmVjdGVkT3Jncy5tYXAoKG9yZykgPT5cbiAgICAgICAgdGhpcy5pbnZhbGlkYXRlT3JnKG9yZykuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdSZXBvcnRpbmcgY2FjaGUgaW52YWxpZGF0aW9uIGZhaWxlZCcsIHsgb3JnSWQ6IG9yZywgZXJyb3I6IGVycm9yTWVzc2FnZShlcnIpIH0pO1xuICAgICAgICB9KSxcbiAgICAgICkpO1xuICAgIH1cblxuICAgIHJldHVybiB7IGluc2VydGVkLCBza2lwcGVkLCB1bnJlZ2lzdGVyZWRQaXBlbGluZUlkcyB9O1xuICB9XG5cbiAgLy8g4pSA4pSAIENhdGVnb3J5IDE6IFBpcGVsaW5lIEV4ZWN1dGlvbiAmIFBlcmZvcm1hbmNlIOKUgOKUgFxuXG4gIC8qKlxuICAgKiBCdWlsZCB0aGUgb3JnLXNjb3BlIHByZWRpY2F0ZSBmb3IgYSByZXBvcnQgcXVlcnkuIFdpdGggYG9yZ0lkc2AgKHRoZVxuICAgKiBvcmcg4oaSIHRlYW0gcm9sbHVwIOKAlCBhIHBhcmVudCdzIGBbc2VsZiwgLi4uZGVzY2VuZGFudHNdYCkgaXQgYmVjb21lcyBhblxuICAgKiBgSU4gKC4uLilgIG92ZXIgdGhlIHN1YnRyZWU7IG90aGVyd2lzZSB0aGUgc2luZ2xlLW9yZyBgPSAkb3JnYC4gUmV0dXJucyBhXG4gICAqIGBtdWx0aWAgZmxhZyBzbyBjYWxsZXJzIGNhbiBydW4gbXVsdGktb3JnIHJlYWRzIHVuZGVyIHN5c2FkbWluIGNvbnRleHRcbiAgICogKHRoZSBzdWJ0cmVlIHNwYW5zIG9yZ3Mgb3V0c2lkZSB0aGUgcmVxdWVzdCdzIFJMUyBzY29wZSkgYW5kIHNhbHQgdGhlXG4gICAqIGNhY2hlIGtleS5cbiAgICovXG4gIHByaXZhdGUgb3JnU2NvcGUob3JnSWQ6IHN0cmluZywgb3JnSWRzPzogc3RyaW5nW10pIHtcbiAgICAvLyBEZWZlbnNlLWluLWRlcHRoOiB0aGUgbXVsdGktb3JnIHJvbGx1cCBydW5zIHVuZGVyIHN5c2FkbWluIGNvbnRleHQgKFJMU1xuICAgIC8vIE9GRiwgc2VlIHJ1blJlcG9ydCksIHNvIGBvcmdJZHNgIGlzIHRoZSBPTkxZIHRlbmFuY3kgZ2F0ZSBvbiB0aGUgcmVhZC4gVGhlXG4gICAgLy8gcm91dGUgcmVzb2x2ZXMgaXQgZnJvbSB0aGUgcGxhdGZvcm0ncyBhdXRob3JpdGF0aXZlIGRlc2NlbmRhbnRzIGVuZHBvaW50IGFzXG4gICAgLy8gYFtzZWxmLCAuLi5kZXNjZW5kYW50c11gLCBidXQgdmFsaWRhdGUgdGhlIHN1YnRyZWUgZGVmZW5zaXZlbHkgaGVyZSBiZWZvcmVcbiAgICAvLyB0aGUgYnlwYXNzOiB0aGUgcmVxdWVzdGluZyAoYW5jaG9yKSBvcmcgTVVTVCBhcHBlYXIgaW4gdGhlIHNldC4gQSBzZXQgdGhhdFxuICAgIC8vIG9taXRzIHRoZSBjYWxsZXIncyBvd24gb3JnIGlzIGEgbWFsZm9ybWVkL3Nwb29mZWQgcm9sbHVwIOKAlCBjb2xsYXBzZSB0byB0aGVcbiAgICAvLyBzYWZlIHNpbmdsZS1vcmcgc2NvcGUgcmF0aGVyIHRoYW4gcmVhZGluZyBhbiBhcmJpdHJhcnkgb3JnIHVuZGVyIHN5c2FkbWluLlxuICAgIC8vIChGdWxsIGRlc2NlbmRhbnQtbWVtYmVyc2hpcCB2YWxpZGF0aW9uIG5lZWRzIHRoZSBvcmcgdHJlZSwgd2hpY2ggdGhpc1xuICAgIC8vIHNlcnZpY2UgZG9lcyBub3QgaG9sZCDigJQgc2VlIHRoZSByZXBvcnQgRkxBRy4pXG4gICAgY29uc3QgYW5jaG9yID0gb3JnSWQudG9Mb3dlckNhc2UoKTtcbiAgICBjb25zdCBwcm92aWRlZCA9IChvcmdJZHMgPz8gW10pLmZpbHRlcigoaWQpID0+IHR5cGVvZiBpZCA9PT0gJ3N0cmluZycgJiYgaWQubGVuZ3RoID4gMCk7XG4gICAgY29uc3QgYW5jaG9yZWQgPSBwcm92aWRlZC5zb21lKChpZCkgPT4gaWQudG9Mb3dlckNhc2UoKSA9PT0gYW5jaG9yKTtcbiAgICBpZiAocHJvdmlkZWQubGVuZ3RoID4gMCAmJiAhYW5jaG9yZWQpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdSZXBvcnRpbmcgcm9sbHVwIHJlamVjdGVkOiByZXF1ZXN0aW5nIG9yZyBhYnNlbnQgZnJvbSByZXNvbHZlZCBzdWJ0cmVlOyB1c2luZyBzaW5nbGUtb3JnIHNjb3BlJywge1xuICAgICAgICBvcmdJZDogYW5jaG9yLCBzdWJ0cmVlU2l6ZTogcHJvdmlkZWQubGVuZ3RoLFxuICAgICAgfSk7XG4gICAgfVxuICAgIGNvbnN0IGlkcyA9IHByb3ZpZGVkLmxlbmd0aCA+IDAgJiYgYW5jaG9yZWQgPyBwcm92aWRlZCA6IFtvcmdJZF07XG4gICAgY29uc3QgbXVsdGkgPSBpZHMubGVuZ3RoID4gMTtcbiAgICBjb25zdCBwcmVkID0gbXVsdGlcbiAgICAgID8gc3FsYElOICgke3NxbC5qb2luKGlkcy5tYXAoKGlkKSA9PiBzcWxgJHtpZH1gKSwgc3FsYCwgYCl9KWBcbiAgICAgIDogc3FsYD0gJHtpZHNbMF19YDtcbiAgICByZXR1cm4geyBwcmVkLCBtdWx0aSB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJ1biBhIHJlcG9ydCByZWFkLiBTaW5nbGUtb3JnIHJlYWRzIHVzZSB0aGUgcGVyLW9yZyBjYWNoZSAoaW52YWxpZGF0ZWQgb25cbiAgICogdGhhdCBvcmcncyBldmVudCBpbmdlc3QpLiBSb2xsdXAgKG11bHRpLW9yZykgcmVhZHMgKipieXBhc3MgdGhlIGNhY2hlKiogYW5kXG4gICAqIHJ1biB1bmRlciBzeXNhZG1pbiBjb250ZXh0OiB0aGV5J3JlIGFkbWluLW9ubHkgYW5kIGxvd2VyLWZyZXF1ZW5jeSwgYW5kIGFcbiAgICogY2hpbGQgb3JnJ3MgZXZlbnQgaW5nZXN0IGNhbid0IGludmFsaWRhdGUgYSBwYXJlbnQncyByb2xsdXAgZW50cnkgKHJlcG9ydGluZ1xuICAgKiBoYXMgbm8gb3JnIHRyZWUpLCBzbyBjYWNoaW5nIHRoZW0gd291bGQgc2VydmUgc3RhbGUgYWdncmVnYXRlcy4gQWx3YXlzIGZyZXNoLlxuICAgKi9cbiAgcHJpdmF0ZSBydW5SZXBvcnQ8VD4oY2FjaGVLZXk6IHN0cmluZywgbXVsdGk6IGJvb2xlYW4sIGV4ZWM6ICgpID0+IFByb21pc2U8VD4pOiBQcm9taXNlPFQ+IHtcbiAgICByZXR1cm4gbXVsdGlcbiAgICAgID8gcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwgZXhlYylcbiAgICAgIDogdGltZXNlcmllc0NhY2hlLmdldE9yU2V0KGNhY2hlS2V5LCBleGVjKTtcbiAgfVxuXG4gIC8qKiAxLjEgRXhlY3V0aW9uIGNvdW50IHBlciBwaXBlbGluZSB3aXRoIHN0YXR1cyBicmVha2Rvd24uICovXG4gIGFzeW5jIGdldEV4ZWN1dGlvbkNvdW50KG9yZ0lkOiBzdHJpbmcsIG9yZ0lkcz86IHN0cmluZ1tdLCByYW5nZT86IHsgZnJvbT86IHN0cmluZzsgdG8/OiBzdHJpbmcgfSk6IFByb21pc2U8RXhlY3V0aW9uQ291bnRbXT4ge1xuICAgIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IHRoaXMub3JnU2NvcGUob3JnSWQsIG9yZ0lkcyk7XG4gICAgLy8gT3B0aW9uYWwgW2Zyb20sdG9dIHdpbmRvdyBvbiB0aGUgZXhlY3V0aW9uJ3Mgc3RhcnRlZF9hdCwgbWlycm9yaW5nIHRoZVxuICAgIC8vIHNpYmxpbmcgdGltZXNlcmllcyByZXBvcnRzIHNvIHRoZSBkYXNoYm9hcmQgZGF0ZS1yYW5nZSBwaWNrZXIgbmFycm93cyB0aGVcbiAgICAvLyBjb3VudCB0b28gKGFuIGVtcHR5IHJhbmdlID0gYWxsLXRpbWUsIHByZXNlcnZpbmcgdGhlIHByaW9yIGJlaGF2aW9yKS4gVGhlXG4gICAgLy8gcmFuZ2UgcmlkZXMgdGhlIEpPSU4gc28gcGlwZWxpbmVzIHdpdGggbm8gaW4td2luZG93IGV2ZW50cyBzdGlsbCBsaXN0XG4gICAgLy8gKExFRlQgc2VtYW50aWNzIHByZXNlcnZlZCB2aWEgdGhlIGlubmVyLWpvaW4gZmlsdGVyIOKAlCBhIHBpcGVsaW5lIHdpdGggemVyb1xuICAgIC8vIG1hdGNoaW5nIGV2ZW50cyBkcm9wcyBmcm9tIHRoZSBjb3VudCwgc2FtZSBhcyBiZWZvcmUgZm9yIGl0cyB3aW5kb3cpLlxuICAgIGNvbnN0IHJhbmdlQ2xhdXNlID0gb3B0aW9uYWxTdGFydGVkQXRSYW5nZShyYW5nZSk7XG4gICAgY29uc3QgZXhlYyA9ICgpID0+IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1RcbiAgICAgICAgICBwLmlkLCBwLnByb2plY3QsIHAub3JnYW5pemF0aW9uLCBwLnBpcGVsaW5lX25hbWUsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyB0b3RhbCxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ1NVQ0NFRURFRCcpOjppbnQgQVMgc3VjY2VlZGVkLFxuICAgICAgICAgIENPVU5UKCopIEZJTFRFUiAoV0hFUkUgZS5zdGF0dXMgPSAnRkFJTEVEJyk6OmludCBBUyBmYWlsZWQsXG4gICAgICAgICAgQ09VTlQoKikgRklMVEVSIChXSEVSRSBlLnN0YXR1cyA9ICdDQU5DRUxFRCcpOjppbnQgQVMgY2FuY2VsZWQsXG4gICAgICAgICAgTUlOKGUuc3RhcnRlZF9hdCk6OnRleHQgQVMgZmlyc3RfZXhlY3V0aW9uLFxuICAgICAgICAgIE1BWChlLnN0YXJ0ZWRfYXQpOjp0ZXh0IEFTIGxhc3RfZXhlY3V0aW9uXG4gICAgICAgIEZST00gJHtzY2hlbWEucGlwZWxpbmV9IHBcbiAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlIE9OIGUucGlwZWxpbmVfaWQgPSBwLmlkXG4gICAgICAgICAgQU5EIGUuZXZlbnRfdHlwZSA9ICdQSVBFTElORScgQU5EIGUuc3RhdHVzICE9ICdTVEFSVEVEJ1xuICAgICAgICAgICR7cmFuZ2VDbGF1c2V9XG4gICAgICAgIFdIRVJFIHAub3JnX2lkICR7cHJlZH0gQU5EIHAuaXNfYWN0aXZlID0gdHJ1ZVxuICAgICAgICBHUk9VUCBCWSBwLmlkXG4gICAgICAgIE9SREVSIEJZIHRvdGFsIERFU0NcbiAgICAgIGApLnRoZW4ociA9PiBkcml6emxlUm93czxFeGVjdXRpb25Db3VudD4oci5yb3dzKSkpO1xuICAgIHJldHVybiB0aGlzLnJ1blJlcG9ydChgJHtvcmdJZH06ZXhlYy1jb3VudDoke3JhbmdlPy5mcm9tID8/ICcnfToke3JhbmdlPy50byA/PyAnJ31gLCBtdWx0aSwgZXhlYyk7XG4gIH1cblxuICAvKipcbiAgICogMS4xYiBQZXItcGlwZWxpbmUgZXhlY3V0aW9uIGhpc3Rvcnkg4oCUIERJU1RJTkNUIGV4ZWN1dGlvbnMgZm9yIG9uZSBwaXBlbGluZSxcbiAgICogbmV3ZXN0IGZpcnN0LiBHcm91cHMgYWxsIGV2ZW50cyBieSBgZXhlY3V0aW9uX2lkYCBpbiBhIHNpbmdsZSBzY2FuIGFuZFxuICAgKiByb2xscyBlYWNoIGV4ZWN1dGlvbiB1cCB0byBvbmUgcm93OlxuICAgKiAgIC0gc3RhdHVzOiBkZXJpdmVkIGZyb20gdGhlIFBJUEVMSU5FLXR5cGUgZXZlbnRzLiBGQUlMRUQgd2lucywgdGhlblxuICAgKiAgICAgU1VDQ0VFREVELCB0aGVuIENBTkNFTEVEOyBhbiBleGVjdXRpb24gd2l0aCBubyB0ZXJtaW5hbCBQSVBFTElORSBldmVudFxuICAgKiAgICAgaXMgc3RpbGwgYGluLXByb2dyZXNzYC5cbiAgICogICAtIHN0YXJ0ZWRBdC9lbmRlZEF0L2R1cmF0aW9uTXM6IGZyb20gdGhlIFBJUEVMSU5FIGxpZmVjeWNsZSBldmVudHMuXG4gICAqICAgLSBmYWlsaW5nU3RhZ2UvZmFpbGluZ0FjdGlvbjogdGhlIGZpcnN0IEZBSUxFRCBTVEFHRS9BQ1RJT04gZXZlbnQgKGNoZWFwIOKAlFxuICAgKiAgICAgc2FtZSBzY2FuLCBubyBleHRyYSBxdWVyeSkuXG4gICAqXG4gICAqIE9SRy1TQ09QSU5HOiBpZGVudGljYWwgdG8gdGhlIHNpYmxpbmcgZXhlY3V0aW9uIHJlcG9ydHMg4oCUIGpvaW5zIHRoZSBwaXBlbGluZVxuICAgKiByZWdpc3RyeSB0YWJsZSAoYHBpcGVsaW5lYCkgYW5kIGdhdGVzIG9uIGBwLm9yZ19pZCAke3ByZWR9YCwgd2hlcmUgYHByZWRgIGlzXG4gICAqIHRoZSBzaW5nbGUtb3JnIGA9ICRvcmdgIG9yICh3aXRoIGEgcm9sbHVwKSBhbiBgSU4gKC4uLilgIG92ZXIgdGhlIG9yZ+KGknRlYW1cbiAgICogc3VidHJlZS4gQSBwaXBlbGluZUlkIGJlbG9uZ2luZyB0byBhbm90aGVyIG9yZyB5aWVsZHMgemVybyByb3dzLlxuICAgKi9cbiAgYXN5bmMgbGlzdFBpcGVsaW5lRXhlY3V0aW9ucyhcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIHBpcGVsaW5lSWQ6IHN0cmluZyxcbiAgICBvcmdJZHM/OiBzdHJpbmdbXSxcbiAgICByYW5nZT86IHsgZnJvbT86IHN0cmluZzsgdG8/OiBzdHJpbmcgfSxcbiAgICBsaW1pdDogbnVtYmVyID0gNTAsXG4gICk6IFByb21pc2U8UGlwZWxpbmVFeGVjdXRpb25bXT4ge1xuICAgIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IHRoaXMub3JnU2NvcGUob3JnSWQsIG9yZ0lkcyk7XG4gICAgY29uc3QgcmFuZ2VDbGF1c2UgPSBvcHRpb25hbFN0YXJ0ZWRBdFJhbmdlKHJhbmdlKTtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIGUuZXhlY3V0aW9uX2lkLFxuICAgICAgICAgIENBU0VcbiAgICAgICAgICAgIFdIRU4gYm9vbF9vcihlLmV2ZW50X3R5cGUgPSAnUElQRUxJTkUnIEFORCBlLnN0YXR1cyA9ICdGQUlMRUQnKSBUSEVOICdmYWlsZWQnXG4gICAgICAgICAgICBXSEVOIGJvb2xfb3IoZS5ldmVudF90eXBlID0gJ1BJUEVMSU5FJyBBTkQgZS5zdGF0dXMgPSAnU1VDQ0VFREVEJykgVEhFTiAnc3VjY2VlZGVkJ1xuICAgICAgICAgICAgV0hFTiBib29sX29yKGUuZXZlbnRfdHlwZSA9ICdQSVBFTElORScgQU5EIGUuc3RhdHVzID0gJ0NBTkNFTEVEJykgVEhFTiAnY2FuY2VsZWQnXG4gICAgICAgICAgICBFTFNFICdpbi1wcm9ncmVzcydcbiAgICAgICAgICBFTkQgQVMgc3RhdHVzLFxuICAgICAgICAgIE1JTihlLnN0YXJ0ZWRfYXQpIEZJTFRFUiAoV0hFUkUgZS5ldmVudF90eXBlID0gJ1BJUEVMSU5FJyk6OnRleHQgQVMgc3RhcnRlZF9hdCxcbiAgICAgICAgICBNQVgoZS5jb21wbGV0ZWRfYXQpIEZJTFRFUiAoV0hFUkUgZS5ldmVudF90eXBlID0gJ1BJUEVMSU5FJyk6OnRleHQgQVMgZW5kZWRfYXQsXG4gICAgICAgICAgTUFYKGUuZHVyYXRpb25fbXMpIEZJTFRFUiAoV0hFUkUgZS5ldmVudF90eXBlID0gJ1BJUEVMSU5FJyk6OmludCBBUyBkdXJhdGlvbl9tcyxcbiAgICAgICAgICAoQVJSQVlfQUdHKGUuc3RhZ2VfbmFtZSkgRklMVEVSIChXSEVSRSBlLmV2ZW50X3R5cGUgPSAnU1RBR0UnIEFORCBlLnN0YXR1cyA9ICdGQUlMRUQnKSlbMV0gQVMgZmFpbGluZ19zdGFnZSxcbiAgICAgICAgICAoQVJSQVlfQUdHKGUuYWN0aW9uX25hbWUpIEZJTFRFUiAoV0hFUkUgZS5ldmVudF90eXBlID0gJ0FDVElPTicgQU5EIGUuc3RhdHVzID0gJ0ZBSUxFRCcpKVsxXSBBUyBmYWlsaW5nX2FjdGlvblxuICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICBXSEVSRSBwLm9yZ19pZCAke3ByZWR9IEFORCBlLnBpcGVsaW5lX2lkID0gJHtwaXBlbGluZUlkfSBBTkQgZS5leGVjdXRpb25faWQgSVMgTk9UIE5VTExcbiAgICAgICAgICAke3JhbmdlQ2xhdXNlfVxuICAgICAgICBHUk9VUCBCWSBlLmV4ZWN1dGlvbl9pZFxuICAgICAgICBPUkRFUiBCWSBNQVgoZS5jcmVhdGVkX2F0KSBERVNDXG4gICAgICAgIExJTUlUICR7bGltaXR9XG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8UGlwZWxpbmVFeGVjdXRpb24+KHIucm93cykpKTtcbiAgICByZXR1cm4gdGhpcy5ydW5SZXBvcnQoYCR7b3JnSWR9OnBpcGVsaW5lLWV4ZWN1dGlvbnM6JHtwaXBlbGluZUlkfToke3JhbmdlPy5mcm9tID8/ICcnfToke3JhbmdlPy50byA/PyAnJ306JHtsaW1pdH1gLCBtdWx0aSwgZXhlYyk7XG4gIH1cblxuICAvKiogMS4yIFN1Y2Nlc3MgcmF0ZSBvdmVyIHRpbWUgZm9yIGFuIG9yZy4gKi9cbiAgYXN5bmMgZ2V0U3VjY2Vzc1JhdGUob3JnSWQ6IHN0cmluZywgaW50ZXJ2YWw6IHN0cmluZywgZnJvbTogc3RyaW5nLCB0bzogc3RyaW5nLCBvcmdJZHM/OiBzdHJpbmdbXSk6IFByb21pc2U8VGltZVNlcmllc0VudHJ5W10+IHtcbiAgICBhc3NlcnRSZXBvcnRJbnRlcnZhbChpbnRlcnZhbCk7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIERBVEVfVFJVTkMoJHtpbnRlcnZhbH0sIGUuc3RhcnRlZF9hdCk6OnRleHQgQVMgcGVyaW9kLFxuICAgICAgICAgIENPVU5UKCopIEZJTFRFUiAoV0hFUkUgZS5zdGF0dXMgPSAnU1VDQ0VFREVEJyk6OmludCBBUyBzdWNjZWVkZWQsXG4gICAgICAgICAgQ09VTlQoKikgRklMVEVSIChXSEVSRSBlLnN0YXR1cyA9ICdGQUlMRUQnKTo6aW50IEFTIGZhaWxlZCxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ0NBTkNFTEVEJyk6OmludCBBUyBjYW5jZWxlZCxcbiAgICAgICAgICBST1VORChDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ1NVQ0NFRURFRCcpOjpudW1lcmljXG4gICAgICAgICAgICAvIE5VTExJRihDT1VOVCgqKSwgMCkgKiAxMDAsIDEpOjpmbG9hdCBBUyBzdWNjZXNzX3BjdFxuICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICBXSEVSRSBwLm9yZ19pZCAke3ByZWR9IEFORCBlLmV2ZW50X3R5cGUgPSAnUElQRUxJTkUnXG4gICAgICAgICAgQU5EIGUuc3RhdHVzIElOICgnU1VDQ0VFREVEJywgJ0ZBSUxFRCcsICdDQU5DRUxFRCcpXG4gICAgICAgICAgQU5EIGUuc3RhcnRlZF9hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgZS5zdGFydGVkX2F0IDw9ICR7dG99Ojp0aW1lc3RhbXB0elxuICAgICAgICBHUk9VUCBCWSBwZXJpb2QgT1JERVIgQlkgcGVyaW9kXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8VGltZVNlcmllc0VudHJ5PihyLnJvd3MpKSk7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGAke29yZ0lkfTpzdWNjZXNzLXJhdGU6JHtpbnRlcnZhbH06JHtmcm9tfToke3RvfWAsIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8qKiAxLjMgQXZlcmFnZSBkdXJhdGlvbiBwZXIgcGlwZWxpbmUuICovXG4gIGFzeW5jIGdldEF2ZXJhZ2VEdXJhdGlvbihvcmdJZDogc3RyaW5nLCBmcm9tOiBzdHJpbmcsIHRvOiBzdHJpbmcsIG9yZ0lkcz86IHN0cmluZ1tdKTogUHJvbWlzZTxEdXJhdGlvblN0YXRzW10+IHtcbiAgICBjb25zdCB7IHByZWQsIG11bHRpIH0gPSB0aGlzLm9yZ1Njb3BlKG9yZ0lkLCBvcmdJZHMpO1xuICAgIGNvbnN0IGV4ZWMgPSAoKSA9PiB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgcC5pZCwgcC5wcm9qZWN0LCBwLnBpcGVsaW5lX25hbWUsXG4gICAgICAgICAgQVZHKGUuZHVyYXRpb25fbXMpOjppbnQgQVMgYXZnX21zLFxuICAgICAgICAgIE1JTihlLmR1cmF0aW9uX21zKTo6aW50IEFTIG1pbl9tcyxcbiAgICAgICAgICBNQVgoZS5kdXJhdGlvbl9tcyk6OmludCBBUyBtYXhfbXMsXG4gICAgICAgICAgUEVSQ0VOVElMRV9DT05UKDAuOTUpIFdJVEhJTiBHUk9VUCAoT1JERVIgQlkgZS5kdXJhdGlvbl9tcyk6OmludCBBUyBwOTVfbXMsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBleGVjdXRpb25zXG4gICAgICAgIEZST00gJHtzY2hlbWEucGlwZWxpbmVFdmVudH0gZVxuICAgICAgICBKT0lOICR7c2NoZW1hLnBpcGVsaW5lfSBwIE9OIHAuaWQgPSBlLnBpcGVsaW5lX2lkXG4gICAgICAgIFdIRVJFIHAub3JnX2lkICR7cHJlZH0gQU5EIGUuZXZlbnRfdHlwZSA9ICdQSVBFTElORScgQU5EIGUuZHVyYXRpb25fbXMgSVMgTk9UIE5VTExcbiAgICAgICAgICBBTkQgZS5zdGFydGVkX2F0ID49ICR7ZnJvbX06OnRpbWVzdGFtcHR6IEFORCBlLnN0YXJ0ZWRfYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6XG4gICAgICAgIEdST1VQIEJZIHAuaWQgT1JERVIgQlkgYXZnX21zIERFU0NcbiAgICAgIGApLnRoZW4ociA9PiBkcml6emxlUm93czxEdXJhdGlvblN0YXRzPihyLnJvd3MpKSk7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGAke29yZ0lkfTphdmctZHVyYXRpb246JHtmcm9tfToke3RvfWAsIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8qKiAxLjUgU3RhZ2UgZmFpbHVyZSBoZWF0bWFwIOKAlCB3aGljaCBzdGFnZXMgZmFpbCBtb3N0LiAqL1xuICBhc3luYyBnZXRTdGFnZUZhaWx1cmVzKG9yZ0lkOiBzdHJpbmcsIGZyb206IHN0cmluZywgdG86IHN0cmluZywgb3JnSWRzPzogc3RyaW5nW10pOiBQcm9taXNlPFN0YWdlRmFpbHVyZVtdPiB7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIGUuc3RhZ2VfbmFtZSxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ0ZBSUxFRCcpOjppbnQgQVMgZmFpbHVyZXMsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyB0b3RhbCxcbiAgICAgICAgICBST1VORChDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ0ZBSUxFRCcpOjpudW1lcmljXG4gICAgICAgICAgICAvIE5VTExJRihDT1VOVCgqKSwgMCkgKiAxMDAsIDEpOjpmbG9hdCBBUyBmYWlsdXJlX3BjdFxuICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICBXSEVSRSBwLm9yZ19pZCAke3ByZWR9IEFORCBlLmV2ZW50X3R5cGUgPSAnU1RBR0UnIEFORCBlLnN0YWdlX25hbWUgSVMgTk9UIE5VTExcbiAgICAgICAgICBBTkQgZS5zdGFydGVkX2F0ID49ICR7ZnJvbX06OnRpbWVzdGFtcHR6IEFORCBlLnN0YXJ0ZWRfYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6XG4gICAgICAgIEdST1VQIEJZIGUuc3RhZ2VfbmFtZSBPUkRFUiBCWSBmYWlsdXJlcyBERVNDXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8U3RhZ2VGYWlsdXJlPihyLnJvd3MpKSk7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGAke29yZ0lkfTpzdGFnZS1mYWlsdXJlczoke2Zyb219OiR7dG99YCwgbXVsdGksIGV4ZWMpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc3RpbmN0IGRlcGxveSBgZW52aXJvbm1lbnRgIHZhbHVlcyBvYnNlcnZlZCBpbiB0aGUgd2luZG93IOKAlCBwb3dlcnMgdGhlXG4gICAqIERPUkEgZW52aXJvbm1lbnQtc2NvcGUgZGF0YWxpc3QuIE9yZy1zY29wZWQgKyByb2xsdXAtYXdhcmUgbGlrZSB0aGUgc2libGluZ1xuICAgKiByZXBvcnRzOyBvbmx5IG5vbi1udWxsIGVudmlyb25tZW50cyAoaS5lLiBkZXBsb3ktYXR0cmlidXRlZCBleGVjdXRpb25zKS5cbiAgICovXG4gIGFzeW5jIGdldFJlcG9ydEVudmlyb25tZW50cyhvcmdJZDogc3RyaW5nLCBmcm9tOiBzdHJpbmcsIHRvOiBzdHJpbmcsIG9yZ0lkcz86IHN0cmluZ1tdKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IHRoaXMub3JnU2NvcGUob3JnSWQsIG9yZ0lkcyk7XG4gICAgY29uc3QgZXhlYyA9ICgpID0+IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1QgRElTVElOQ1QgZS5lbnZpcm9ubWVudCBBUyBlbnZpcm9ubWVudFxuICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICBXSEVSRSBwLm9yZ19pZCAke3ByZWR9IEFORCBlLmVudmlyb25tZW50IElTIE5PVCBOVUxMXG4gICAgICAgICAgQU5EIGUuc3RhcnRlZF9hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgZS5zdGFydGVkX2F0IDw9ICR7dG99Ojp0aW1lc3RhbXB0elxuICAgICAgICBPUkRFUiBCWSBlbnZpcm9ubWVudFxuICAgICAgYCkudGhlbihyID0+IGRyaXp6bGVSb3dzPHsgZW52aXJvbm1lbnQ6IHN0cmluZyB9PihyLnJvd3MpLm1hcCgocm93KSA9PiByb3cuZW52aXJvbm1lbnQpKSk7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGAke29yZ0lkfTpyZXBvcnQtZW52czoke2Zyb219OiR7dG99YCwgbXVsdGksIGV4ZWMpO1xuICB9XG5cbiAgLyoqIDEuNiBTdGFnZSBib3R0bGVuZWNrcyDigJQgc2xvd2VzdCBzdGFnZXMgcGVyIHBpcGVsaW5lLiAqL1xuICBhc3luYyBnZXRTdGFnZUJvdHRsZW5lY2tzKG9yZ0lkOiBzdHJpbmcsIGZyb206IHN0cmluZywgdG86IHN0cmluZywgb3JnSWRzPzogc3RyaW5nW10pOiBQcm9taXNlPFN0YWdlQm90dGxlbmVja1tdPiB7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIHAuaWQsIHAucGlwZWxpbmVfbmFtZSwgZS5zdGFnZV9uYW1lLFxuICAgICAgICAgIEFWRyhlLmR1cmF0aW9uX21zKTo6aW50IEFTIGF2Z19tcyxcbiAgICAgICAgICBNQVgoZS5kdXJhdGlvbl9tcyk6OmludCBBUyBtYXhfbXNcbiAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgIEpPSU4gJHtzY2hlbWEucGlwZWxpbmV9IHAgT04gcC5pZCA9IGUucGlwZWxpbmVfaWRcbiAgICAgICAgV0hFUkUgcC5vcmdfaWQgJHtwcmVkfSBBTkQgZS5ldmVudF90eXBlID0gJ1NUQUdFJyBBTkQgZS5kdXJhdGlvbl9tcyBJUyBOT1QgTlVMTFxuICAgICAgICAgIEFORCBlLnN0YXJ0ZWRfYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIGUuc3RhcnRlZF9hdCA8PSAke3RvfTo6dGltZXN0YW1wdHpcbiAgICAgICAgR1JPVVAgQlkgcC5pZCwgZS5zdGFnZV9uYW1lIE9SREVSIEJZIGF2Z19tcyBERVNDXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8U3RhZ2VCb3R0bGVuZWNrPihyLnJvd3MpKSk7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGAke29yZ0lkfTpzdGFnZS1ib3R0bGVuZWNrczoke2Zyb219OiR7dG99YCwgbXVsdGksIGV4ZWMpO1xuICB9XG5cbiAgLyoqIDEuNyBBY3Rpb24gZmFpbHVyZSByYXRlIOKAlCB3aGljaCBwbHVnaW4gc3RlcHMgZmFpbCBtb3N0LiAqL1xuICBhc3luYyBnZXRBY3Rpb25GYWlsdXJlcyhvcmdJZDogc3RyaW5nLCBmcm9tOiBzdHJpbmcsIHRvOiBzdHJpbmcsIG9yZ0lkcz86IHN0cmluZ1tdKTogUHJvbWlzZTxBY3Rpb25GYWlsdXJlW10+IHtcbiAgICBjb25zdCB7IHByZWQsIG11bHRpIH0gPSB0aGlzLm9yZ1Njb3BlKG9yZ0lkLCBvcmdJZHMpO1xuICAgIGNvbnN0IGV4ZWMgPSAoKSA9PiB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgZS5hY3Rpb25fbmFtZSxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ0ZBSUxFRCcpOjppbnQgQVMgZmFpbHVyZXMsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyB0b3RhbCxcbiAgICAgICAgICBST1VORChDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ0ZBSUxFRCcpOjpudW1lcmljXG4gICAgICAgICAgICAvIE5VTExJRihDT1VOVCgqKSwgMCkgKiAxMDAsIDEpOjpmbG9hdCBBUyBmYWlsdXJlX3BjdFxuICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICBXSEVSRSBwLm9yZ19pZCAke3ByZWR9IEFORCBlLmV2ZW50X3R5cGUgPSAnQUNUSU9OJyBBTkQgZS5hY3Rpb25fbmFtZSBJUyBOT1QgTlVMTFxuICAgICAgICAgIEFORCBlLnN0YXJ0ZWRfYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIGUuc3RhcnRlZF9hdCA8PSAke3RvfTo6dGltZXN0YW1wdHpcbiAgICAgICAgR1JPVVAgQlkgZS5hY3Rpb25fbmFtZSBPUkRFUiBCWSBmYWlsdXJlcyBERVNDXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8QWN0aW9uRmFpbHVyZT4oci5yb3dzKSkpO1xuICAgIHJldHVybiB0aGlzLnJ1blJlcG9ydChgJHtvcmdJZH06YWN0aW9uLWZhaWx1cmVzOiR7ZnJvbX06JHt0b31gLCBtdWx0aSwgZXhlYyk7XG4gIH1cblxuICAvKipcbiAgICogMS44IEVycm9yIGNhdGVnb3JpemF0aW9uIOKAlCBncm91cCBmYWlsdXJlIG1lc3NhZ2VzLiBFeGVjdXRpb24gcmVwb3J0LCBzb1xuICAgKiByb2xsdXAtYXdhcmUgZXhhY3RseSBsaWtlIHRoZSBzaWJsaW5nIGV4ZWN1dGlvbiByZXBvcnRzOiB3aXRoIGBvcmdJZHNgXG4gICAqICh0aGUgb3Jn4oaSdGVhbSBzdWJ0cmVlKSB0aGUgb3JnIGdhdGUgYmVjb21lcyBhbiBgSU4gKC4uLilgIGFuZCB0aGUgcmVhZCBydW5zXG4gICAqIGZyZXNoIHVuZGVyIHN5c2FkbWluIHZpYSBgcnVuUmVwb3J0YDsgc2luZ2xlLW9yZyByZWFkcyBrZWVwIHRoZSBwZXItb3JnIGNhY2hlLlxuICAgKi9cbiAgYXN5bmMgZ2V0RXJyb3JzKG9yZ0lkOiBzdHJpbmcsIGZyb206IHN0cmluZywgdG86IHN0cmluZywgbGltaXQ6IG51bWJlciA9IDIwLCBvcmdJZHM/OiBzdHJpbmdbXSk6IFByb21pc2U8RXJyb3JFbnRyeVtdPiB7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIFNVQlNUUklORyhlLmVycm9yX21lc3NhZ2UgRlJPTSAxIEZPUiAyMDApIEFTIGVycm9yX3BhdHRlcm4sXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBvY2N1cnJlbmNlcyxcbiAgICAgICAgICBDT1VOVChESVNUSU5DVCBlLnBpcGVsaW5lX2lkKTo6aW50IEFTIGFmZmVjdGVkX3BpcGVsaW5lcyxcbiAgICAgICAgICBNQVgoZS5zdGFydGVkX2F0KTo6dGV4dCBBUyBsYXN0X3NlZW5cbiAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgIEpPSU4gJHtzY2hlbWEucGlwZWxpbmV9IHAgT04gcC5pZCA9IGUucGlwZWxpbmVfaWRcbiAgICAgICAgV0hFUkUgcC5vcmdfaWQgJHtwcmVkfSBBTkQgZS5zdGF0dXMgPSAnRkFJTEVEJyBBTkQgZS5lcnJvcl9tZXNzYWdlIElTIE5PVCBOVUxMXG4gICAgICAgICAgQU5EIGUuc3RhcnRlZF9hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgZS5zdGFydGVkX2F0IDw9ICR7dG99Ojp0aW1lc3RhbXB0elxuICAgICAgICBHUk9VUCBCWSBlcnJvcl9wYXR0ZXJuIE9SREVSIEJZIG9jY3VycmVuY2VzIERFU0NcbiAgICAgICAgTElNSVQgJHtsaW1pdH1cbiAgICAgIGApLnRoZW4ociA9PiBkcml6emxlUm93czxFcnJvckVudHJ5PihyLnJvd3MpKSk7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGAke29yZ0lkfTplcnJvcnM6JHtmcm9tfToke3RvfToke2xpbWl0fWAsIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8qKlxuICAgKiAxLjkgRE9SQSBtZXRyaWNzIG92ZXIgYSBbZnJvbSx0b10gZGVwbG95LWNvbXBsZXRpb24gd2luZG93LCBvcmctc2NvcGVkICtcbiAgICogcm9sbHVwLWF3YXJlLiBERVBMT1ktQkFTSVMgT05MWSDigJQgZXZlcnkgbWV0cmljIGRlcml2ZXMgZnJvbSByZWFsIGRlcGxveS1zdGFnZVxuICAgKiBleGVjdXRpb25zIChgZXZlbnRfdHlwZT0nU1RBR0UnYCB3aXRoIGEgbm9uLW51bGwgYGVudmlyb25tZW50YCwgc2V0IGJ5IHRoZVxuICAgKiBmb3J3YXJkZXIgZm9yIHRoZSBzdGFnZXMgYSB1c2VyIGRlY2xhcmVkIGluIGBwYi5kZXBsb3lzYCkuIE5vIHJ1bi1iYXNlZFxuICAgKiBmYWxsYmFjazogYSBwaXBlbGluZSB3aXRoIG5vIGRlcGxveSBzdGFnZSBwcm9kdWNlcyBubyBET1JBIGRhdGEuXG4gICAqXG4gICAqIEZvdXIgc2NvcGVkIFNRTCBzY2FucyBydW4gaW5zaWRlIG9uZSB0eCAoZWFjaCBvcmctZ2F0ZWQgYnkgYHAub3JnX2lkICR7cHJlZH1gXG4gICAqIC8gYG8ub3JnX2lkICR7cHJlZH1gIHNvIGEgcm9sbHVwIHBhc3NlcyB0aGUgb3Jn4oaSdGVhbSBzdWJ0cmVlIGFuZCBhIGZvcmVpZ25cbiAgICogb3JnJ3Mgcm93cyBuZXZlciBlbnRlcik6ICgxKSB0ZXJtaW5hbCBkZXBsb3kgcm93cyBQRVIgKGV4ZWN1dGlvbiwgZW52KSDigJQgb25lXG4gICAqIFwiZGVwbG95bWVudFwiID0gb25lIGV4ZWN1dGlvbiByZWFjaGluZyBhbiBlbnYgKEQxKSDigJQg4oaSIERGIC8gZGVwbG95LXRpbWUgQ0ZSIC9cbiAgICogbWVhc3VyZWQgbGVhZCB0aW1lIChsZWFkIHRpbWUgam9pbnMgdGhlIGV4ZWN1dGlvbidzIGVhcmxpZXN0IGNvbW1pdCwgRDIpO1xuICAgKiB0aGUgc2NhbiBhbHNvIHJlYWNoZXMgYmFjayBgd2luZG93SG91cnNgIGJlZm9yZSBgZnJvbWAgc28gaW5jaWRlbnRzIG9wZW5lZFxuICAgKiBuZWFyIHRoZSB3aW5kb3cgc3RhcnQgY2FuIGNvcnJlbGF0ZSB0byBhIGp1c3QtcHJpb3IgZGVwbG95OyAoMikgYGRlcGxveW1lbnRfb3V0Y29tZXNgXG4gICAqIGluLXdpbmRvdyDihpIgcG9zdC1kZXBsb3kgQ0ZSIGNvbXBvbmVudDsgKDMpIHByb2R1Y3Rpb24gcmVzdG9yZWQvZmFpbGVkXG4gICAqIG91dGNvbWVzIGpvaW5lZCB0byB0aGVpciBkZXBsb3kg4oaSIE1UVFI7ICg0KSByZWdpc3RyeSB2cyBkZXBsb3lpbmcg4oaSIGNvdmVyYWdlLlxuICAgKiBUaGUgY3Jvc3Mtc291cmNlIG1lZGlhbnMgKGxlYWQgdGltZSwgTVRUUikgYXJlIGNvbXB1dGVkIGluIEpTIGZyb20gY2xhbXBlZFxuICAgKiAo4omlMCkgZGVsdGFzLlxuICAgKlxuICAgKiBAcGFyYW0gb3B0cy5waXBlbGluZUlkICAgIHJlc3RyaWN0IHRvIG9uZSBwaXBlbGluZSAocGVyLXBpcGVsaW5lIERPUkEpXG4gICAqIEBwYXJhbSBvcHRzLmVudmlyb25tZW50ICAgcmVzdHJpY3QgdG8gb25lIGRlcGxveSBlbnZpcm9ubWVudFxuICAgKi9cbiAgYXN5bmMgZ2V0RG9yYU1ldHJpY3MoXG4gICAgb3JnSWQ6IHN0cmluZyxcbiAgICBmcm9tOiBzdHJpbmcsXG4gICAgdG86IHN0cmluZyxcbiAgICBvcmdJZHM/OiBzdHJpbmdbXSxcbiAgICBvcHRzOiBEb3JhT3B0aW9ucyA9IHt9LFxuICApOiBQcm9taXNlPERvcmFNZXRyaWNzPiB7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCB7IHBpcGVsaW5lSWQsIGVudmlyb25tZW50IH0gPSBvcHRzO1xuICAgIGNvbnN0IHBpcGVsaW5lQ2xhdXNlID0gcGlwZWxpbmVJZCA/IHNxbGBBTkQgZS5waXBlbGluZV9pZCA9ICR7cGlwZWxpbmVJZH1gIDogc3FsYGA7XG4gICAgY29uc3QgcGlwZWxpbmVDbGF1c2VSID0gcGlwZWxpbmVJZCA/IHNxbGBBTkQgci5waXBlbGluZV9pZCA9ICR7cGlwZWxpbmVJZH1gIDogc3FsYGA7XG4gICAgY29uc3QgZW52Q2xhdXNlID0gZW52aXJvbm1lbnQgPyBzcWxgQU5EIGUuZW52aXJvbm1lbnQgPSAke2Vudmlyb25tZW50fWAgOiBzcWxgYDtcbiAgICBjb25zdCBvdXRjb21lRW52Q2xhdXNlID0gZW52aXJvbm1lbnQgPyBzcWxgQU5EIG8uZW52aXJvbm1lbnQgPSAke2Vudmlyb25tZW50fWAgOiBzcWxgYDtcbiAgICBjb25zdCBpbmNpZGVudEVudkNsYXVzZSA9IGVudmlyb25tZW50ID8gc3FsYEFORCBpLmVudmlyb25tZW50ID0gJHtlbnZpcm9ubWVudH1gIDogc3FsYGA7XG5cbiAgICAvLyBFZmZlY3RpdmUgaW5jaWRlbnQtY29ycmVsYXRpb24gd2luZG93IChwZXItb3JnIG92ZXJyaWRlIG9yIHRoZSBnbG9iYWxcbiAgICAvLyBkZWZhdWx0KS4gQ29tcHV0ZWQgaGVyZSAobm90IGp1c3QgaW4gc2hhcGVEb3JhKSBiZWNhdXNlIHRoZSBkZXBsb3kgc2NhblxuICAgIC8vIG5lZWRzIGl0IGZvciBCT1RIIHRoZSBjb3JyZWxhdGlvbiBsb29rLWJhY2sgbG93ZXIgYm91bmQgQU5EIHRoZSBjYWNoZSBrZXkuXG4gICAgY29uc3QgZWZmZWN0aXZlV2luZG93SG91cnMgPSByZXNvbHZlSW5jaWRlbnRXaW5kb3dIb3VycyhvcHRzLmluY2lkZW50V2luZG93SG91cnMpO1xuICAgIC8vIENvcnJlbGF0aW9uIGxvb2stYmFjazogaW5jaWRlbnRzIG9wZW5lZCBqdXN0IGluc2lkZSBgZnJvbWAgY2FuIGF0dHJpYnV0ZSB0b1xuICAgIC8vIGEgZGVwbG95IHRoYXQgY29tcGxldGVkIHVwIHRvIGB3aW5kb3dIb3Vyc2AgQkVGT1JFIGBmcm9tYCAobWlycm9ycyB0aGVcbiAgICAvLyBMQVRFUkFMIGxvb2stYmFjayBpbiBsaXN0SW5jaWRlbnRzKS4gV2lkZW4gdGhlIGRlcGxveSBzY2FuJ3MgbG93ZXIgYm91bmQgdG9cbiAgICAvLyBgZnJvbSDiiJIgd2luZG93SG91cnNgOyBERi9DRlIvbGVhZCBzdGlsbCBjb3VudCBvbmx5IHRoZSBpbi1gZnJvbWAuLmB0b2BcbiAgICAvLyBzdWJzZXQgKGZsYWdnZWQgYnkgYGluX3dpbmRvd2ApLCB3aGlsZSBjb3JyZWxhdGlvbiBzZWVzIHRoZSB3aWRlciBzZXQuXG4gICAgY29uc3QgZnJvbU1zID0gRGF0ZS5wYXJzZShmcm9tKTtcbiAgICBjb25zdCBsb29rYmFja0Zyb20gPSBOdW1iZXIuaXNGaW5pdGUoZnJvbU1zKVxuICAgICAgPyBuZXcgRGF0ZShmcm9tTXMgLSBlZmZlY3RpdmVXaW5kb3dIb3VycyAqIDM2MDAgKiAxMDAwKS50b0lTT1N0cmluZygpXG4gICAgICA6IGZyb207XG5cbiAgICAvLyAoMSkgVGVybWluYWwgZGVwbG95IHJvd3M6IFBFUi1FWEVDVVRJT04gKEQxKSwgb25lIHJvdyBwZXIgKGVudmlyb25tZW50LFxuICAgIC8vIGV4ZWN1dGlvbikg4oCUIGV2ZXJ5IGRlcGxveSBTVEFHRSB0YXJnZXRpbmcgdGhhdCBlbnYgd2l0aGluIHRoZSBleGVjdXRpb24gaXNcbiAgICAvLyByb2xsZWQgdXAgdG8gYSB0ZXJtaW5hbCBzdGF0dXMgKEZBSUxFRCB3aW5zLCB0aGVuIFNVQ0NFRURFRCkuIEEgc2luZ2xlXG4gICAgLy8gZXhlY3V0aW9uIHdpdGggdHdvIGRlcGxveSBzdGFnZXMgdG8gdGhlIHNhbWUgZW52IGlzIE9ORSBkZXBsb3ltZW50LCBub3QgdHdvLlxuICAgIC8vIGNvbXBsZXRlZF9hdCA9IHRoZSBkZXBsb3kncyBjb21wbGV0aW9uIChNQVggb3ZlciBpdHMgc3RhZ2VzKS4gYGNvbW1pdF90c2BcbiAgICAvLyAoRDIpIGlzIHRoZSBleGVjdXRpb24ncyBFQVJMSUVTVCBjb21taXQgdGltZSBhY3Jvc3MgQUxMIGl0cyBldmVudHMgKGNvbW1pdFxuICAgIC8vIGVucmljaG1lbnQgcmlkZXMgdGhlIFBJUEVMSU5FL3NvdXJjZSBldmVudCB3aGVyZSBlbnZpcm9ubWVudCBJUyBOVUxMLCBuZXZlclxuICAgIC8vIHRoZSBkZXBsb3kgU1RBR0Ugcm93KSwgam9pbmVkIGJ5IGV4ZWN1dGlvbl9pZC4gYGluX3dpbmRvd2AgbWFya3Mgd2hldGhlciB0aGVcbiAgICAvLyBkZXBsb3kgY29tcGxldGVkIGluc2lkZSBbZnJvbSx0b10gKHZzLiBhIGxvb2stYmFjay1vbmx5IHJvdyBrZXB0IHNvbGVseSBmb3JcbiAgICAvLyBpbmNpZGVudCBjb3JyZWxhdGlvbikuIGV4ZWN1dGlvbl9pZCBpcyBwcm9qZWN0ZWQgc28gcG9zdC1kZXBsb3kgZmFpbHVyZXNcbiAgICAvLyAobWFudWFsIG91dGNvbWVzICsgY29ycmVsYXRlZCBpbmNpZGVudHMpIGNhbiBkZWR1cCBhZ2FpbnN0IHRoZWlyIGRlcGxveS5cbiAgICBjb25zdCBkZXBsb3lTcWwgPSBzcWxgXG4gICAgICAgIFdJVEggZGVwbG95X3N0YWdlcyBBUyAoXG4gICAgICAgICAgU0VMRUNUXG4gICAgICAgICAgICBlLmVudmlyb25tZW50IEFTIGVudmlyb25tZW50LFxuICAgICAgICAgICAgZS5leGVjdXRpb25faWQgQVMgZXhlY3V0aW9uX2lkLFxuICAgICAgICAgICAgJHt0ZXJtaW5hbFN0YXR1c1JvbGx1cH0gQVMgc3RhdHVzLFxuICAgICAgICAgICAgTUFYKGUuY29tcGxldGVkX2F0KSBBUyBjb21wbGV0ZWRfYXRcbiAgICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgICBKT0lOICR7c2NoZW1hLnBpcGVsaW5lfSBwIE9OIHAuaWQgPSBlLnBpcGVsaW5lX2lkXG4gICAgICAgICAgV0hFUkUgcC5vcmdfaWQgJHtwcmVkfSBBTkQgZS5ldmVudF90eXBlID0gJ1NUQUdFJyBBTkQgZS5lbnZpcm9ubWVudCBJUyBOT1QgTlVMTFxuICAgICAgICAgICAgJHtwaXBlbGluZUNsYXVzZX0gJHtlbnZDbGF1c2V9XG4gICAgICAgICAgICBBTkQgZS5jb21wbGV0ZWRfYXQgPj0gJHtsb29rYmFja0Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgZS5jb21wbGV0ZWRfYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6XG4gICAgICAgICAgR1JPVVAgQlkgZS5lbnZpcm9ubWVudCwgZS5leGVjdXRpb25faWRcbiAgICAgICAgKSxcbiAgICAgICAgZXhlY19jb21taXRzIEFTIChcbiAgICAgICAgICBTRUxFQ1QgYy5leGVjdXRpb25faWQgQVMgZXhlY3V0aW9uX2lkLCBNSU4oYy5jb21taXRfdGltZXN0YW1wKSBBUyBjb21taXRfdHNcbiAgICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGNcbiAgICAgICAgICBKT0lOICR7c2NoZW1hLnBpcGVsaW5lfSBwIE9OIHAuaWQgPSBjLnBpcGVsaW5lX2lkXG4gICAgICAgICAgV0hFUkUgcC5vcmdfaWQgJHtwcmVkfSBBTkQgYy5jb21taXRfdGltZXN0YW1wIElTIE5PVCBOVUxMXG4gICAgICAgICAgICBBTkQgYy5leGVjdXRpb25faWQgSU4gKFNFTEVDVCBleGVjdXRpb25faWQgRlJPTSBkZXBsb3lfc3RhZ2VzKVxuICAgICAgICAgIEdST1VQIEJZIGMuZXhlY3V0aW9uX2lkXG4gICAgICAgIClcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgZHMuZW52aXJvbm1lbnQgQVMgZW52aXJvbm1lbnQsXG4gICAgICAgICAgZHMuZXhlY3V0aW9uX2lkIEFTIGV4ZWN1dGlvbl9pZCxcbiAgICAgICAgICBkcy5zdGF0dXMgQVMgc3RhdHVzLFxuICAgICAgICAgIGRzLmNvbXBsZXRlZF9hdDo6dGV4dCBBUyBjb21wbGV0ZWRfYXQsXG4gICAgICAgICAgKGRzLmNvbXBsZXRlZF9hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eikgQVMgaW5fd2luZG93LFxuICAgICAgICAgIGVjLmNvbW1pdF90czo6dGV4dCBBUyBjb21taXRfdHNcbiAgICAgICAgRlJPTSBkZXBsb3lfc3RhZ2VzIGRzXG4gICAgICAgIExFRlQgSk9JTiBleGVjX2NvbW1pdHMgZWMgT04gZWMuZXhlY3V0aW9uX2lkID0gZHMuZXhlY3V0aW9uX2lkYDtcblxuICAgIC8vICgyKSBQb3N0LWRlcGxveSBvdXRjb21lIG1hcmtlcnMgaW4td2luZG93IChwZXIgZW52KS4gUG9zdC1kZXBsb3kgZmFpbHVyZXNcbiAgICAvLyAob3V0Y29tZT0nZmFpbGVkJykgYWRkIHRvIHRoZSBDRlIgbnVtZXJhdG9yIHdpdGhvdXQgY2hhbmdpbmcgYXR0ZW1wdHMuXG4gICAgLy8gZXhlY3V0aW9uX2lkIGxldHMgdGhlIENGUiBkZWR1cCBhIG1hbnVhbCBmYWlsdXJlIGFnYWluc3QgYW4gaW5jaWRlbnQgdGhhdFxuICAgIC8vIGNvcnJlbGF0ZXMgdG8gdGhlIHNhbWUgZGVwbG95IChubyBkb3VibGUtY291bnQpLlxuICAgIGNvbnN0IG91dGNvbWVTcWwgPSBzcWxgXG4gICAgICAgIFNFTEVDVCBvLmVudmlyb25tZW50IEFTIGVudmlyb25tZW50LCBvLm91dGNvbWUgQVMgb3V0Y29tZSwgby5leGVjdXRpb25faWQgQVMgZXhlY3V0aW9uX2lkXG4gICAgICAgIEZST00gJHtzY2hlbWEuZGVwbG95bWVudE91dGNvbWV9IG9cbiAgICAgICAgV0hFUkUgby5vcmdfaWQgJHtwcmVkfSAke291dGNvbWVFbnZDbGF1c2V9XG4gICAgICAgICAgQU5EIG8uYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIG8uYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6YDtcblxuICAgIC8vICgzKSBNVFRSIOKAlCBQUk9EVUNUSU9OLU9OTFksIGluZGVwZW5kZW50IG9mIHRoZSBlbnYgZmlsdGVyLiBFdmVyeSBwcm9kdWN0aW9uXG4gICAgLy8gb3V0Y29tZSBpbi13aW5kb3c7IGZvciBhICdyZXN0b3JlZCcgb25lLCBjb3JyZWxhdGUgdG8gaXRzIGRlcGxveSdzXG4gICAgLy8gY29tcGxldGlvbiB2aWEgZXhlY3V0aW9uX2lkIHRvIG1lYXN1cmUgcmVzdG9yZWQuYXQg4oiSIGRlcGxveWVkLmNvbXBsZXRlZF9hdC5cbiAgICAvLyBleGVjdXRpb25faWQgaXMgcHJvamVjdGVkIHNvIGFuIGluY2lkZW50LXNvdXJjZWQgcmVjb3ZlcnkgZm9yIHRoZSBzYW1lXG4gICAgLy8gZGVwbG95IHRha2VzIHByZWNlZGVuY2Ugb3ZlciB0aGUgbWFudWFsIHJlc3RvcmVkIGdhcC5cbiAgICBjb25zdCBtdHRyU3FsID0gc3FsYFxuICAgICAgICBTRUxFQ1RcbiAgICAgICAgICBvLm91dGNvbWUgQVMgb3V0Y29tZSxcbiAgICAgICAgICBvLmV4ZWN1dGlvbl9pZCBBUyBleGVjdXRpb25faWQsXG4gICAgICAgICAgby5hdDo6dGV4dCBBUyByZXN0b3JlZF9hdCxcbiAgICAgICAgICAoU0VMRUNUIE1BWChkLmNvbXBsZXRlZF9hdCkgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBkXG4gICAgICAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcDIgT04gcDIuaWQgPSBkLnBpcGVsaW5lX2lkXG4gICAgICAgICAgICAgV0hFUkUgcDIub3JnX2lkICR7cHJlZH0gQU5EIGQuZXhlY3V0aW9uX2lkID0gby5leGVjdXRpb25faWRcbiAgICAgICAgICAgICAgIEFORCBkLmV2ZW50X3R5cGUgPSAnU1RBR0UnIEFORCBkLmVudmlyb25tZW50ID0gJHtIRUFETElORV9FTlZ9KTo6dGV4dCBBUyBkZXBsb3llZF9hdFxuICAgICAgICBGUk9NICR7c2NoZW1hLmRlcGxveW1lbnRPdXRjb21lfSBvXG4gICAgICAgIFdIRVJFIG8ub3JnX2lkICR7cHJlZH0gQU5EIG8uZW52aXJvbm1lbnQgPSAke0hFQURMSU5FX0VOVn1cbiAgICAgICAgICBBTkQgby5hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgby5hdCA8PSAke3RvfTo6dGltZXN0YW1wdHpgO1xuXG4gICAgLy8gKDUpIEluY2lkZW50cyBvcGVuZWQgaW4td2luZG93IChwZXIgZW52KS4gQ29ycmVsYXRlZCBpbiBKUyB0byB0aGUgbW9zdFxuICAgIC8vIHJlY2VudCBzdWNjZXNzZnVsIGRlcGxveSDihpIgYXV0b21hdGVkIHBvc3QtZGVwbG95IGZhaWx1cmUgKENGUikgKyByZWFsXG4gICAgLy8gcmVjb3ZlcnkgdGltZSAocmVzb2x2ZWRfYXQg4oiSIG9wZW5lZF9hdCkgZm9yIE1UVFIgKHByb2R1Y3Rpb24pLlxuICAgIGNvbnN0IGluY2lkZW50U3FsID0gc3FsYFxuICAgICAgICBTRUxFQ1QgaS5lbnZpcm9ubWVudCBBUyBlbnZpcm9ubWVudCwgaS5vcGVuZWRfYXQ6OnRleHQgQVMgb3BlbmVkX2F0LCBpLnJlc29sdmVkX2F0Ojp0ZXh0IEFTIHJlc29sdmVkX2F0XG4gICAgICAgIEZST00gJHtzY2hlbWEuaW5jaWRlbnR9IGlcbiAgICAgICAgV0hFUkUgaS5vcmdfaWQgJHtwcmVkfSAke2luY2lkZW50RW52Q2xhdXNlfVxuICAgICAgICAgIEFORCBpLm9wZW5lZF9hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgaS5vcGVuZWRfYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6YDtcblxuICAgIC8vICg0KSBDb3ZlcmFnZTogcmVnaXN0ZXJlZCBwaXBlbGluZXMgdnMgdGhvc2UgdGhhdCBhY3R1YWxseSBkZXBsb3llZCBpbi13aW5kb3cuXG4gICAgY29uc3QgY292ZXJhZ2VTcWwgPSBzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIChTRUxFQ1QgQ09VTlQoKik6OmludCBGUk9NICR7c2NoZW1hLnBpcGVsaW5lUmVnaXN0cnl9IHJcbiAgICAgICAgICAgICBXSEVSRSByLm9yZ19pZCAke3ByZWR9ICR7cGlwZWxpbmVDbGF1c2VSfSkgQVMgcmVnaXN0ZXJlZCxcbiAgICAgICAgICAoU0VMRUNUIENPVU5UKERJU1RJTkNUIGUucGlwZWxpbmVfaWQpOjppbnQgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICAgICAgIFdIRVJFIHAub3JnX2lkICR7cHJlZH0gQU5EIGUuZXZlbnRfdHlwZSA9ICdTVEFHRScgQU5EIGUuZW52aXJvbm1lbnQgSVMgTk9UIE5VTExcbiAgICAgICAgICAgICAgICR7cGlwZWxpbmVDbGF1c2V9ICR7ZW52Q2xhdXNlfVxuICAgICAgICAgICAgICAgQU5EIGUuY29tcGxldGVkX2F0ID49ICR7ZnJvbX06OnRpbWVzdGFtcHR6IEFORCBlLmNvbXBsZXRlZF9hdCA8PSAke3RvfTo6dGltZXN0YW1wdHopIEFTIGRlcGxveWluZ2A7XG5cbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KGFzeW5jICh0eCkgPT4ge1xuICAgICAgLy8gU2NhbiBvcmRlciBpcyBsb2FkLWJlYXJpbmcgZm9yIHRoZSBnZW5lcmF0ZWQtU1FMIHRlc3RzOiAwPWRlcGxveSxcbiAgICAgIC8vIDE9b3V0Y29tZXMsIDI9bXR0ciwgMz1jb3ZlcmFnZSwgND1pbmNpZGVudHMgKGFwcGVuZGVkIGxhc3Qgc28gdGhlXG4gICAgICAvLyBlYXJsaWVyIGluZGljZXMgc3RheSBzdGFibGUpLlxuICAgICAgY29uc3QgZGVwbG95Um93cyA9IGRyaXp6bGVSb3dzPERlcGxveVJvdz4oKGF3YWl0IHR4LmV4ZWN1dGUoZGVwbG95U3FsKSkucm93cyk7XG4gICAgICBjb25zdCBvdXRjb21lUm93cyA9IGRyaXp6bGVSb3dzPE91dGNvbWVSb3c+KChhd2FpdCB0eC5leGVjdXRlKG91dGNvbWVTcWwpKS5yb3dzKTtcbiAgICAgIGNvbnN0IG10dHJSb3dzID0gZHJpenpsZVJvd3M8TXR0clBhaXJSb3cgJiB7IG91dGNvbWU6IHN0cmluZyB9PigoYXdhaXQgdHguZXhlY3V0ZShtdHRyU3FsKSkucm93cyk7XG4gICAgICBjb25zdCBjb3ZlcmFnZVJvdyA9IGRyaXp6bGVSb3dzPENvdmVyYWdlUm93PigoYXdhaXQgdHguZXhlY3V0ZShjb3ZlcmFnZVNxbCkpLnJvd3MpWzBdO1xuICAgICAgY29uc3QgaW5jaWRlbnRSb3dzID0gZHJpenpsZVJvd3M8SW5jaWRlbnRSb3c+KChhd2FpdCB0eC5leGVjdXRlKGluY2lkZW50U3FsKSkucm93cyk7XG4gICAgICByZXR1cm4gdGhpcy5zaGFwZURvcmEoZGVwbG95Um93cywgb3V0Y29tZVJvd3MsIG10dHJSb3dzLCBjb3ZlcmFnZVJvdywgaW5jaWRlbnRSb3dzLCBmcm9tLCB0bywge1xuICAgICAgICBwaXBlbGluZUlkLCBlbnZpcm9ubWVudCxcbiAgICAgIH0sIGVmZmVjdGl2ZVdpbmRvd0hvdXJzKTtcbiAgICB9KTtcbiAgICAvLyBDYWNoZSBrZXkgTVVTVCBpbmNsdWRlIHRoZSBlZmZlY3RpdmUgaW5jaWRlbnQgd2luZG93OiB0aGUgc2NvcmVjYXJkIHJlYWRzXG4gICAgLy8gL2RvcmEgd2l0aCB0aGUgb3JnJ3MgZGVmYXVsdCB3aW5kb3cgd2hpbGUgYW4gZXhwbGljaXQgL2RvcmEgY2FsbCBtYXkgcGFzcyBhblxuICAgIC8vIG92ZXJyaWRlIOKAlCBkaWZmZXJlbnQgd2luZG93cyB5aWVsZCBkaWZmZXJlbnQgQ0ZSL01UVFIsIHNvIHRoZXkgbXVzdCBub3RcbiAgICAvLyBjb2xsaWRlIG9uIHRoZSBzYW1lIGtleS5cbiAgICBjb25zdCBrZXkgPSBgJHtvcmdJZH06ZG9yYToke2Zyb219OiR7dG99OiR7cGlwZWxpbmVJZCA/PyAnJ306JHtlbnZpcm9ubWVudCA/PyAnJ306JHtlZmZlY3RpdmVXaW5kb3dIb3Vyc31gO1xuICAgIHJldHVybiB0aGlzLnJ1blJlcG9ydChrZXksIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTaGFwZSB0aGUgcmF3IERPUkEgc2NhbiByb3dzIGludG8gdGhlIHB1YmxpYyB7QGxpbmsgRG9yYU1ldHJpY3N9LiBCdWNrZXRzIHRoZVxuICAgKiB0ZXJtaW5hbCBkZXBsb3kgcm93cyBieSBlbnZpcm9ubWVudCAoREYgLyBkZXBsb3ktdGltZSBDRlIgLyBtZWFzdXJlZCBsZWFkKSxcbiAgICogZm9sZHMgaW4gdGhlIHBvc3QtZGVwbG95IGZhaWx1cmUgY291bnRzIChtYW51YWwgb3V0Y29tZXMgKyB3ZWJob29rLWluZ2VzdGVkXG4gICAqIGluY2lkZW50cywgZGVkdXBlZCBieSBkZXBsb3kgZXhlY3V0aW9uKSwgY29tcHV0ZXMgcHJvZHVjdGlvbiBNVFRSIGZyb20gYm90aFxuICAgKiBzb3VyY2VzIChpbmNpZGVudCBgcmVzb2x2ZWRfYXQg4oiSIG9wZW5lZF9hdGAgdGFraW5nIHByZWNlZGVuY2Ugb3ZlciB0aGUgbWFudWFsXG4gICAqIGByZXN0b3JlZCDiiJIgZGVwbG95ZWRgKSwgYW5kIHJlY29uY2lsZXMgY292ZXJhZ2Ug4oCUIGFsbCBjcm9zcy1zb3VyY2UgbWVkaWFuc1xuICAgKiBvdmVyIGRlbHRhcyBjbGFtcGVkIOKJpTAuXG4gICAqXG4gICAqIFBoYXNlIDUgaW5jaWRlbnQgY29ycmVsYXRpb246IGVhY2ggaW5jaWRlbnQgaXMgYXR0cmlidXRlZCB0byB0aGUgbW9zdCByZWNlbnRcbiAgICogU1VDQ0VTU0ZVTCBkZXBsb3kgdG8gaXRzIGVudmlyb25tZW50IHdpdGggYGNvbXBsZXRlZF9hdCDiiaQgb3BlbmVkX2F0YCB3aXRoaW5cbiAgICoge0BsaW5rIERPUkFfSU5DSURFTlRfV0lORE9XX0hPVVJTfS4gVGhhdCBkZXBsb3kgaXMgYSBwb3N0LWRlcGxveSBmYWlsdXJlOyBhblxuICAgKiB1bmNvcnJlbGF0ZWQgaW5jaWRlbnQgKG5vIGVsaWdpYmxlIGRlcGxveSkgY29udHJpYnV0ZXMgbm90aGluZy5cbiAgICovXG4gIHByaXZhdGUgc2hhcGVEb3JhKFxuICAgIGRlcGxveVJvd3M6IERlcGxveVJvd1tdLFxuICAgIG91dGNvbWVSb3dzOiBPdXRjb21lUm93W10sXG4gICAgbXR0clJvd3M6IEFycmF5PE10dHJQYWlyUm93ICYgeyBvdXRjb21lOiBzdHJpbmcgfT4sXG4gICAgY292ZXJhZ2VSb3c6IENvdmVyYWdlUm93IHwgdW5kZWZpbmVkLFxuICAgIGluY2lkZW50Um93czogSW5jaWRlbnRSb3dbXSxcbiAgICBmcm9tOiBzdHJpbmcsXG4gICAgdG86IHN0cmluZyxcbiAgICBmaWx0ZXJzOiB7IHBpcGVsaW5lSWQ/OiBzdHJpbmc7IGVudmlyb25tZW50Pzogc3RyaW5nIH0sXG4gICAgd2luZG93SG91cnM6IG51bWJlciA9IERPUkFfSU5DSURFTlRfV0lORE9XX0hPVVJTLFxuICApOiBEb3JhTWV0cmljcyB7XG4gICAgLy8gV2luZG93IGxlbmd0aCBpbiBkYXlzIChmbG9vcmVkIGF0IDEgc28gYSBzdWItZGF5IHdpbmRvdyB5aWVsZHMgdGhlIGNvdW50IGFzXG4gICAgLy8gdGhlIHJhdGUsIG5ldmVyIC8wIG9yIGFuIGV4dHJhcG9sYXRpb24pLiBHdWFyZCB1bnBhcnNlYWJsZSBkYXRlcyDihpIgMSBkYXkuXG4gICAgY29uc3Qgc3BhbkRheXMgPSAoRGF0ZS5wYXJzZSh0bykgLSBEYXRlLnBhcnNlKGZyb20pKSAvIDg2NDAwMDAwO1xuICAgIGNvbnN0IGRheXMgPSBOdW1iZXIuaXNGaW5pdGUoc3BhbkRheXMpID8gTWF0aC5tYXgoc3BhbkRheXMsIDEpIDogMTtcbiAgICAvLyBXaW5kb3cgZW5kIGluc3RhbnQgZm9yIE1UVFIgcmlnaHQtY2Vuc29yaW5nIChhIHJlc29sdXRpb24gYWZ0ZXIgYHRvYCBpc1xuICAgIC8vIHVub2JzZXJ2ZWQpLiArSW5maW5pdHkgd2hlbiBgdG9gIGlzIHVucGFyc2VhYmxlIOKHkiBubyBjZW5zb3JpbmcgKGZhaWwtb3BlbikuXG4gICAgY29uc3QgdG9QYXJzZWQgPSBEYXRlLnBhcnNlKHRvKTtcbiAgICBjb25zdCB0b01zID0gTnVtYmVyLmlzRmluaXRlKHRvUGFyc2VkKSA/IHRvUGFyc2VkIDogTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuXG4gICAgLy8gUGVyLWVudiBhY2N1bXVsYXRvci5cbiAgICBpbnRlcmZhY2UgQWNjIHtcbiAgICAgIGRlcGxveW1lbnRzOiBudW1iZXI7XG4gICAgICBkZXBsb3lUaW1lRmFpbHVyZXM6IG51bWJlcjtcbiAgICAgIGF0dGVtcHRzOiBudW1iZXI7XG4gICAgICBsZWFkR2FwczogbnVtYmVyW107XG4gICAgfVxuICAgIGNvbnN0IGVudnMgPSBuZXcgTWFwPHN0cmluZywgQWNjPigpO1xuICAgIGNvbnN0IGFjY0ZvciA9IChlbnY6IHN0cmluZyk6IEFjYyA9PiB7XG4gICAgICBsZXQgYSA9IGVudnMuZ2V0KGVudik7XG4gICAgICBpZiAoIWEpIHsgYSA9IHsgZGVwbG95bWVudHM6IDAsIGRlcGxveVRpbWVGYWlsdXJlczogMCwgYXR0ZW1wdHM6IDAsIGxlYWRHYXBzOiBbXSB9OyBlbnZzLnNldChlbnYsIGEpOyB9XG4gICAgICByZXR1cm4gYTtcbiAgICB9O1xuXG4gICAgLy8gQSBkZXBsb3kgcm93IGNvdW50cyB0b3dhcmQgREYvQ0ZSL2xlYWQgb25seSBpZiBpdCBjb21wbGV0ZWQgaW5zaWRlIFtmcm9tLHRvXS5cbiAgICAvLyBMb29rLWJhY2stb25seSByb3dzIChpbl93aW5kb3c9ZmFsc2UsIGtlcHQgZm9yIGluY2lkZW50IGNvcnJlbGF0aW9uKSBhcmVcbiAgICAvLyBza2lwcGVkIGhlcmUuIEFic2VudCBpbiBhIGZpeHR1cmUg4oeSIGluLXdpbmRvdyAodW5pdCB0ZXN0cyBvbWl0IHRoZSBmbGFnKS5cbiAgICBjb25zdCBpc0luV2luZG93ID0gKHJvdzogRGVwbG95Um93KTogYm9vbGVhbiA9PlxuICAgICAgcm93LmluX3dpbmRvdyA9PSBudWxsID8gdHJ1ZSA6IChyb3cuaW5fd2luZG93ID09PSB0cnVlIHx8IHJvdy5pbl93aW5kb3cgPT09ICd0Jyk7XG5cbiAgICBmb3IgKGNvbnN0IHJvdyBvZiBkZXBsb3lSb3dzKSB7XG4gICAgICBjb25zdCBlbnYgPSByb3cuZW52aXJvbm1lbnQ7XG4gICAgICBpZiAoIWVudikgY29udGludWU7XG4gICAgICBpZiAoIWlzSW5XaW5kb3cocm93KSkgY29udGludWU7IC8vIGxvb2stYmFjay1vbmx5IHJvdyDihpIgY29ycmVsYXRpb24sIG5vdCBERi9DRlJcbiAgICAgIGNvbnN0IGEgPSBhY2NGb3IoZW52KTtcbiAgICAgIGlmIChyb3cuc3RhdHVzID09PSAnU1VDQ0VFREVEJykge1xuICAgICAgICBhLmRlcGxveW1lbnRzKys7XG4gICAgICAgIGEuYXR0ZW1wdHMrKztcbiAgICAgICAgLy8gTWVhc3VyZWQgbGVhZCB0aW1lOiBkZXBsb3kgY29tcGxldGlvbiDiiJIgZXhlY3V0aW9uJ3MgZWFybGllc3QgY29tbWl0IHRpbWUuXG4gICAgICAgIGlmIChyb3cuY29tbWl0X3RzICE9IG51bGwgJiYgcm93LmNvbXBsZXRlZF9hdCAhPSBudWxsKSB7XG4gICAgICAgICAgY29uc3QgZ2FwID0gKERhdGUucGFyc2Uocm93LmNvbXBsZXRlZF9hdCkgLSBEYXRlLnBhcnNlKHJvdy5jb21taXRfdHMpKSAvIDEwMDA7XG4gICAgICAgICAgaWYgKE51bWJlci5pc0Zpbml0ZShnYXApKSBhLmxlYWRHYXBzLnB1c2goTWF0aC5tYXgoZ2FwLCAwKSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAocm93LnN0YXR1cyA9PT0gJ0ZBSUxFRCcpIHtcbiAgICAgICAgYS5kZXBsb3lUaW1lRmFpbHVyZXMrKztcbiAgICAgICAgYS5hdHRlbXB0cysrO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEluY2lkZW504oaSZGVwbG95IGNvcnJlbGF0aW9uIChQaGFzZSA1KS4gSW5kZXggdGhlIFNVQ0NFU1NGVUwgZGVwbG95cyBwZXIgZW52XG4gICAgLy8gKGV4ZWN1dGlvbiBpZCArIGNvbXBsZXRpb24gaW5zdGFudCksIG5ld2VzdCBmaXJzdCwgc28gZWFjaCBpbmNpZGVudCBjYW4gYmVcbiAgICAvLyBhdHRyaWJ1dGVkIHRvIHRoZSBtb3N0IHJlY2VudCBkZXBsb3kgdGhhdCBjb21wbGV0ZWQgd2l0aGluIHRoZSB3aW5kb3dcbiAgICAvLyBiZWZvcmUgaXQgb3BlbmVkLiBUaGF0IGV4ZWN1dGlvbiBpcyBhIHBvc3QtZGVwbG95IGZhaWx1cmUuXG4gICAgY29uc3Qgd2luZG93TXMgPSB3aW5kb3dIb3VycyAqIDM2MDAgKiAxMDAwO1xuICAgIGNvbnN0IHN1Y2Nlc3NmdWxCeUVudiA9IG5ldyBNYXA8c3RyaW5nLCBBcnJheTx7IGV4ZWM6IHN0cmluZzsgY29tcGxldGVkTXM6IG51bWJlciB9Pj4oKTtcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiBkZXBsb3lSb3dzKSB7XG4gICAgICBpZiAocm93LnN0YXR1cyAhPT0gJ1NVQ0NFRURFRCcgfHwgIXJvdy5lbnZpcm9ubWVudCB8fCByb3cuZXhlY3V0aW9uX2lkID09IG51bGwgfHwgcm93LmNvbXBsZXRlZF9hdCA9PSBudWxsKSBjb250aW51ZTtcbiAgICAgIGNvbnN0IG1zID0gRGF0ZS5wYXJzZShyb3cuY29tcGxldGVkX2F0KTtcbiAgICAgIGlmICghTnVtYmVyLmlzRmluaXRlKG1zKSkgY29udGludWU7XG4gICAgICBjb25zdCBsaXN0ID0gc3VjY2Vzc2Z1bEJ5RW52LmdldChyb3cuZW52aXJvbm1lbnQpID8/IFtdO1xuICAgICAgbGlzdC5wdXNoKHsgZXhlYzogcm93LmV4ZWN1dGlvbl9pZCwgY29tcGxldGVkTXM6IG1zIH0pO1xuICAgICAgc3VjY2Vzc2Z1bEJ5RW52LnNldChyb3cuZW52aXJvbm1lbnQsIGxpc3QpO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IGxpc3Qgb2Ygc3VjY2Vzc2Z1bEJ5RW52LnZhbHVlcygpKSBsaXN0LnNvcnQoKGEsIGIpID0+IGIuY29tcGxldGVkTXMgLSBhLmNvbXBsZXRlZE1zKTtcblxuICAgIC8qKiBDb3JyZWxhdGUgYW4gaW5jaWRlbnQgdG8gdGhlIG1vc3QgcmVjZW50IGluLXdpbmRvdyBzdWNjZXNzZnVsIGRlcGxveSBleGVjdXRpb24uICovXG4gICAgY29uc3QgY29ycmVsYXRlID0gKGVudjogc3RyaW5nLCBvcGVuZWRNczogbnVtYmVyKTogc3RyaW5nIHwgbnVsbCA9PiB7XG4gICAgICBjb25zdCBsaXN0ID0gc3VjY2Vzc2Z1bEJ5RW52LmdldChlbnYpO1xuICAgICAgaWYgKCFsaXN0KSByZXR1cm4gbnVsbDtcbiAgICAgIGZvciAoY29uc3QgZCBvZiBsaXN0KSB7XG4gICAgICAgIGlmIChkLmNvbXBsZXRlZE1zIDw9IG9wZW5lZE1zICYmIG9wZW5lZE1zIC0gZC5jb21wbGV0ZWRNcyA8PSB3aW5kb3dNcykgcmV0dXJuIGQuZXhlYztcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH07XG5cbiAgICAvLyBDb3JyZWxhdGVkIGluY2lkZW50cyAoYWxsIGVudnMg4oaSIENGUjsgcHJvZHVjdGlvbiBzdWJzZXQg4oaSIE1UVFIpLiBFYWNoIGNhcnJpZXNcbiAgICAvLyB0aGUgZGVwbG95IGV4ZWN1dGlvbiBpdCBhdHRyaWJ1dGVzIHRvICsgaXRzIHJlYWwgcmVjb3ZlcnkgZ2FwICh3aGVuIHJlc29sdmVkKS5cbiAgICBpbnRlcmZhY2UgQ29ycmVsYXRlZEluY2lkZW50IHsgZW52aXJvbm1lbnQ6IHN0cmluZzsgZXhlYzogc3RyaW5nOyByZXNvbHZlZEdhcDogbnVtYmVyIHwgbnVsbCB9XG4gICAgY29uc3QgY29ycmVsYXRlZEluY2lkZW50czogQ29ycmVsYXRlZEluY2lkZW50W10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGluYyBvZiBpbmNpZGVudFJvd3MpIHtcbiAgICAgIGlmICghaW5jLmVudmlyb25tZW50IHx8IGluYy5vcGVuZWRfYXQgPT0gbnVsbCkgY29udGludWU7XG4gICAgICBjb25zdCBvcGVuZWRNcyA9IERhdGUucGFyc2UoaW5jLm9wZW5lZF9hdCk7XG4gICAgICBpZiAoIU51bWJlci5pc0Zpbml0ZShvcGVuZWRNcykpIGNvbnRpbnVlO1xuICAgICAgY29uc3QgZXhlYyA9IGNvcnJlbGF0ZShpbmMuZW52aXJvbm1lbnQsIG9wZW5lZE1zKTtcbiAgICAgIGlmIChleGVjID09IG51bGwpIGNvbnRpbnVlOyAvLyB1bmNvcnJlbGF0ZWQg4oaSIG5vdCBhdHRyaWJ1dGFibGUgdG8gYSBkZXBsb3lcbiAgICAgIGxldCByZXNvbHZlZEdhcDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG4gICAgICBpZiAoaW5jLnJlc29sdmVkX2F0ICE9IG51bGwpIHtcbiAgICAgICAgY29uc3QgcmVzb2x2ZWRNcyA9IERhdGUucGFyc2UoaW5jLnJlc29sdmVkX2F0KTtcbiAgICAgICAgLy8gUmlnaHQtY2Vuc29yaW5nOiBhIHJlc29sdXRpb24gQUZURVIgdGhlIHdpbmRvdyBlbmQgKGB0b2ApIGlzIG5vdCBvYnNlcnZlZFxuICAgICAgICAvLyB3aXRoaW4gdGhlIHdpbmRvdyDigJQgdHJlYXQgdGhlIGluY2lkZW50IGFzIHN0aWxsIG9wZW4gKGdhcCBzdGF5cyBudWxsKSBzb1xuICAgICAgICAvLyBNVFRSIG5ldmVyIHJlYWRzIHVuYm91bmRlZCBwYXN0IGB0b2AuIFRoZSBpbmNpZGVudCBzdGlsbCBjb3VudHMgdG93YXJkIHRoZVxuICAgICAgICAvLyBpbmNpZGVudCB0b3RhbCAodW5yZXNvbHZlZCksIGl0IGp1c3QgY29udHJpYnV0ZXMgbm8gcmVjb3ZlcnkgdGltZS5cbiAgICAgICAgaWYgKE51bWJlci5pc0Zpbml0ZShyZXNvbHZlZE1zKSAmJiByZXNvbHZlZE1zIDw9IHRvTXMpIHtcbiAgICAgICAgICBjb25zdCBnID0gKHJlc29sdmVkTXMgLSBvcGVuZWRNcykgLyAxMDAwO1xuICAgICAgICAgIGlmIChOdW1iZXIuaXNGaW5pdGUoZykpIHJlc29sdmVkR2FwID0gTWF0aC5tYXgoZywgMCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvcnJlbGF0ZWRJbmNpZGVudHMucHVzaCh7IGVudmlyb25tZW50OiBpbmMuZW52aXJvbm1lbnQsIGV4ZWMsIHJlc29sdmVkR2FwIH0pO1xuICAgIH1cblxuICAgIC8vIFBvc3QtZGVwbG95IGZhaWx1cmVzIHBlciBlbnYg4oCUIHRoZSBESVNUSU5DVCBzZXQgb2YgZGVwbG95IGV4ZWN1dGlvbnMgZmxhZ2dlZFxuICAgIC8vIGJ5IGEgbWFudWFsIGBmYWlsZWRgIG91dGNvbWUgT1IgYSBjb3JyZWxhdGVkIGluY2lkZW50IChkZWR1cGVkIGJ5IGV4ZWN1dGlvbixcbiAgICAvLyBzbyBhIGRlcGxveSBmbGFnZ2VkIGJ5IGJvdGggY291bnRzIG9uY2UpLiBBIG1hbnVhbCBvdXRjb21lIHdpdGhvdXQgYW5cbiAgICAvLyBleGVjdXRpb24gaWQgKGxlZ2FjeS91bnJlc29sdmFibGUpIHN0aWxsIGNvdW50cyB2aWEgYSBwZXItcm93IHNlbnRpbmVsLlxuICAgIGNvbnN0IHBvc3REZXBsb3lFeGVjc0J5RW52ID0gbmV3IE1hcDxzdHJpbmcsIFNldDxzdHJpbmc+PigpO1xuICAgIGxldCBzZW50aW5lbCA9IDA7XG4gICAgY29uc3QgZmxhZ0ZvciA9IChlbnY6IHN0cmluZyk6IFNldDxzdHJpbmc+ID0+IHtcbiAgICAgIGxldCBzID0gcG9zdERlcGxveUV4ZWNzQnlFbnYuZ2V0KGVudik7XG4gICAgICBpZiAoIXMpIHsgcyA9IG5ldyBTZXQ8c3RyaW5nPigpOyBwb3N0RGVwbG95RXhlY3NCeUVudi5zZXQoZW52LCBzKTsgfVxuICAgICAgcmV0dXJuIHM7XG4gICAgfTtcbiAgICBmb3IgKGNvbnN0IG8gb2Ygb3V0Y29tZVJvd3MpIHtcbiAgICAgIGlmIChvLm91dGNvbWUgIT09ICdmYWlsZWQnIHx8ICFvLmVudmlyb25tZW50KSBjb250aW51ZTtcbiAgICAgIGZsYWdGb3Ioby5lbnZpcm9ubWVudCkuYWRkKG8uZXhlY3V0aW9uX2lkID8/IGBtYW51YWwtJHtzZW50aW5lbCsrfWApO1xuICAgICAgYWNjRm9yKG8uZW52aXJvbm1lbnQpOyAvLyBlbnN1cmUgZW52IGFwcGVhcnMgZXZlbiB3aXRoIG5vIGRlcGxveSBldmVudHNcbiAgICB9XG4gICAgZm9yIChjb25zdCBjaSBvZiBjb3JyZWxhdGVkSW5jaWRlbnRzKSB7XG4gICAgICBmbGFnRm9yKGNpLmVudmlyb25tZW50KS5hZGQoY2kuZXhlYyk7XG4gICAgICBhY2NGb3IoY2kuZW52aXJvbm1lbnQpOyAvLyBlbnN1cmUgZW52IGFwcGVhcnMgZXZlbiBpZiB0aGUgZGVwbG95IGlzIG91dC1vZi13aW5kb3dcbiAgICB9XG4gICAgY29uc3QgcG9zdERlcGxveUJ5RW52ID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgICBmb3IgKGNvbnN0IFtlbnYsIHNldF0gb2YgcG9zdERlcGxveUV4ZWNzQnlFbnYpIHBvc3REZXBsb3lCeUVudi5zZXQoZW52LCBzZXQuc2l6ZSk7XG5cbiAgICBjb25zdCBlbnZpcm9ubWVudHM6IERvcmFFbnZNZXRyaWNzW10gPSBbLi4uZW52cy5lbnRyaWVzKCldXG4gICAgICAubWFwKChbZW52aXJvbm1lbnQsIGFdKSA9PiB7XG4gICAgICAgIGNvbnN0IHBvc3REZXBsb3lGYWlsdXJlcyA9IHBvc3REZXBsb3lCeUVudi5nZXQoZW52aXJvbm1lbnQpID8/IDA7XG4gICAgICAgIGNvbnN0IHJhd1BlckRheSA9IGEuZGVwbG95bWVudHMgLyBkYXlzO1xuICAgICAgICBjb25zdCBudW1lcmF0b3IgPSBhLmRlcGxveVRpbWVGYWlsdXJlcyArIHBvc3REZXBsb3lGYWlsdXJlcztcbiAgICAgICAgLy8gQ2xhbXAgdG8gWzAsMTAwXTogcG9zdC1kZXBsb3kgZmFpbHVyZXMgY2FuIGNvcnJlbGF0ZSB0byBhIGRlcGxveSB0aGF0XG4gICAgICAgIC8vIGNvbXBsZXRlZCBqdXN0IGJlZm9yZSB0aGUgd2luZG93IChrZXB0IGZvciBjb3JyZWxhdGlvbiksIHNvIHRoZSBudW1lcmF0b3JcbiAgICAgICAgLy8gY2FuIHRyYW5zaWVudGx5IGV4Y2VlZCBpbi13aW5kb3cgYXR0ZW1wdHMg4oCUIGEgQ0ZSID4gMTAwJSBpcyBuZXZlciB2YWxpZC5cbiAgICAgICAgY29uc3QgcmF3UmF0ZSA9IGEuYXR0ZW1wdHMgPiAwID8gTWF0aC5taW4oMTAwLCAobnVtZXJhdG9yIC8gYS5hdHRlbXB0cykgKiAxMDApIDogMDtcbiAgICAgICAgY29uc3QgbHRNZWRpYW4gPSBtZWRpYW4oYS5sZWFkR2Fwcyk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgZW52aXJvbm1lbnQsXG4gICAgICAgICAgZGVwbG95bWVudEZyZXF1ZW5jeToge1xuICAgICAgICAgICAgZGVwbG95bWVudHM6IGEuZGVwbG95bWVudHMsXG4gICAgICAgICAgICBwZXJEYXk6IHJvdW5kKHJhd1BlckRheSwgMiksXG4gICAgICAgICAgICBsZXZlbDogZG9yYUxldmVsRm9yRnJlcXVlbmN5KHJhd1BlckRheSwgYS5kZXBsb3ltZW50cyksXG4gICAgICAgICAgfSxcbiAgICAgICAgICBjaGFuZ2VGYWlsdXJlUmF0ZToge1xuICAgICAgICAgICAgcmF0ZTogcm91bmQocmF3UmF0ZSwgMSksXG4gICAgICAgICAgICBkZXBsb3lUaW1lRmFpbHVyZXM6IGEuZGVwbG95VGltZUZhaWx1cmVzLFxuICAgICAgICAgICAgcG9zdERlcGxveUZhaWx1cmVzLFxuICAgICAgICAgICAgYXR0ZW1wdHM6IGEuYXR0ZW1wdHMsXG4gICAgICAgICAgICBsZXZlbDogZG9yYUxldmVsRm9yQ2hhbmdlRmFpbHVyZShyYXdSYXRlLCBhLmF0dGVtcHRzKSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIGxlYWRUaW1lOiB7XG4gICAgICAgICAgICBkZXBsb3ltZW50czogYS5sZWFkR2Fwcy5sZW5ndGgsXG4gICAgICAgICAgICBtZWRpYW5TZWNvbmRzOiBsdE1lZGlhbiAhPSBudWxsID8gcm91bmQobHRNZWRpYW4sIDEpIDogbnVsbCxcbiAgICAgICAgICAgIGxldmVsOiBkb3JhTGV2ZWxGb3JMZWFkVGltZShsdE1lZGlhbiksXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcbiAgICAgIH0pXG4gICAgICAuc29ydCgoeCwgeSkgPT4ge1xuICAgICAgICBpZiAoeC5lbnZpcm9ubWVudCA9PT0gSEVBRExJTkVfRU5WKSByZXR1cm4gLTE7XG4gICAgICAgIGlmICh5LmVudmlyb25tZW50ID09PSBIRUFETElORV9FTlYpIHJldHVybiAxO1xuICAgICAgICByZXR1cm4geC5lbnZpcm9ubWVudC5sb2NhbGVDb21wYXJlKHkuZW52aXJvbm1lbnQpO1xuICAgICAgfSk7XG5cbiAgICAvLyBNVFRSIOKAlCBQUk9EVUNUSU9OLU9OTFksIGZyb20gQk9USCBzb3VyY2VzLiBJbmNpZGVudHMgdGFrZSBwcmVjZWRlbmNlOiBhXG4gICAgLy8gcHJvZHVjdGlvbiBpbmNpZGVudCBjb3JyZWxhdGVkIHRvIGEgZGVwbG95IGNvbnRyaWJ1dGVzIHRoZSByZWFsIHJlY292ZXJ5XG4gICAgLy8gKGByZXNvbHZlZF9hdCDiiJIgb3BlbmVkX2F0YCksIGFuZCBhbnkgbWFudWFsIG91dGNvbWUgb24gdGhlIFNBTUUgZGVwbG95IGlzXG4gICAgLy8gc2tpcHBlZC4gTWFudWFsIG91dGNvbWVzIG9uIG90aGVyIGRlcGxveXMga2VlcCB0aGUgUGhhc2UtMiBiZWhhdmlvclxuICAgIC8vIChgZmFpbGVkYCDihpIgYW4gaW5jaWRlbnQ7IGByZXN0b3JlZGAg4oaSIGEgcmVjb3ZlcnkgZ2FwIG9mIGByZXN0b3JlZCDiiJIgZGVwbG95ZWRgKS5cbiAgICBsZXQgaW5jaWRlbnRzID0gMCwgcmVzdG9yZWQgPSAwO1xuICAgIGNvbnN0IG10dHJHYXBzOiBudW1iZXJbXSA9IFtdO1xuICAgIC8vIEV4ZWN1dGlvbnMgYWxyZWFkeSBhdHRyaWJ1dGVkIHRvIGEgcHJvZHVjdGlvbiBpbmNpZGVudCAocHJlY2VkZW5jZSBndWFyZCkuXG4gICAgY29uc3QgaW5jaWRlbnRFeGVjcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAgIGZvciAoY29uc3QgY2kgb2YgY29ycmVsYXRlZEluY2lkZW50cykge1xuICAgICAgaWYgKGNpLmVudmlyb25tZW50ICE9PSBIRUFETElORV9FTlYpIGNvbnRpbnVlOyAvLyBNVFRSIGlzIHByb2R1Y3Rpb24tb25seVxuICAgICAgaW5jaWRlbnRzKys7XG4gICAgICBpbmNpZGVudEV4ZWNzLmFkZChjaS5leGVjKTtcbiAgICAgIGlmIChjaS5yZXNvbHZlZEdhcCAhPSBudWxsKSB7IHJlc3RvcmVkKys7IG10dHJHYXBzLnB1c2goY2kucmVzb2x2ZWRHYXApOyB9XG4gICAgfVxuICAgIGZvciAoY29uc3QgbSBvZiBtdHRyUm93cykge1xuICAgICAgLy8gSW5jaWRlbnQgcHJlY2VkZW5jZTogZG9uJ3QgZG91YmxlLWNvdW50IGEgZGVwbG95IGFscmVhZHkgZmxhZ2dlZCBieSBhbiBpbmNpZGVudC5cbiAgICAgIGlmIChtLmV4ZWN1dGlvbl9pZCAhPSBudWxsICYmIGluY2lkZW50RXhlY3MuaGFzKG0uZXhlY3V0aW9uX2lkKSkgY29udGludWU7XG4gICAgICBpZiAobS5vdXRjb21lID09PSAnZmFpbGVkJykgeyBpbmNpZGVudHMrKzsgY29udGludWU7IH1cbiAgICAgIGlmIChtLm91dGNvbWUgPT09ICdyZXN0b3JlZCcpIHtcbiAgICAgICAgcmVzdG9yZWQrKztcbiAgICAgICAgaWYgKG0ucmVzdG9yZWRfYXQgIT0gbnVsbCAmJiBtLmRlcGxveWVkX2F0ICE9IG51bGwpIHtcbiAgICAgICAgICBjb25zdCBnYXAgPSAoRGF0ZS5wYXJzZShtLnJlc3RvcmVkX2F0KSAtIERhdGUucGFyc2UobS5kZXBsb3llZF9hdCkpIC8gMTAwMDtcbiAgICAgICAgICBpZiAoTnVtYmVyLmlzRmluaXRlKGdhcCkpIG10dHJHYXBzLnB1c2goTWF0aC5tYXgoZ2FwLCAwKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgbXR0ck1lZGlhbiA9IG1lZGlhbihtdHRyR2Fwcyk7XG5cbiAgICBjb25zdCByZWdpc3RlcmVkID0gTnVtYmVyKGNvdmVyYWdlUm93Py5yZWdpc3RlcmVkKSB8fCAwO1xuICAgIGNvbnN0IGRlcGxveWluZyA9IE51bWJlcihjb3ZlcmFnZVJvdz8uZGVwbG95aW5nKSB8fCAwO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHdpbmRvdzogeyBmcm9tLCB0byB9LFxuICAgICAgZmlsdGVyczogeyBwaXBlbGluZUlkOiBmaWx0ZXJzLnBpcGVsaW5lSWQgPz8gbnVsbCwgZW52aXJvbm1lbnQ6IGZpbHRlcnMuZW52aXJvbm1lbnQgPz8gbnVsbCB9LFxuICAgICAgaGVhZGxpbmU6IEhFQURMSU5FX0VOVixcbiAgICAgIGVudmlyb25tZW50cyxcbiAgICAgIG1lYW5UaW1lVG9SZXN0b3JlOiB7XG4gICAgICAgIGluY2lkZW50cyxcbiAgICAgICAgcmVzdG9yZWQsXG4gICAgICAgIG1lZGlhblNlY29uZHM6IG10dHJNZWRpYW4gIT0gbnVsbCA/IHJvdW5kKG10dHJNZWRpYW4sIDEpIDogbnVsbCxcbiAgICAgICAgbGV2ZWw6IGRvcmFMZXZlbEZvclJlc3RvcmUobXR0ck1lZGlhbiksXG4gICAgICB9LFxuICAgICAgY292ZXJhZ2U6IHtcbiAgICAgICAgcmVnaXN0ZXJlZCxcbiAgICAgICAgZGVwbG95aW5nLFxuICAgICAgICB3aXRob3V0RGVwbG95czogTWF0aC5tYXgocmVnaXN0ZXJlZCAtIGRlcGxveWluZywgMCksXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogMS45YiBET1JBIHRyZW5kIOKAlCBkZXBsb3ltZW50IGZyZXF1ZW5jeSArIGRlcGxveS10aW1lIGNoYW5nZS1mYWlsdXJlIHJhdGVcbiAgICogYnVja2V0ZWQgYnkgYGludGVydmFsYCAoZGF5L3dlZWsvbW9udGgpIGZvciBhIHNwYXJrbGluZS4gRGVwbG95LWJhc2lzIGxpa2VcbiAgICogZ2V0RG9yYU1ldHJpY3M6IGJ1Y2tldHMgdGVybWluYWwgZGVwbG95LXN0YWdlIGV4ZWN1dGlvbnMgb24gdGhlaXJcbiAgICogYGNvbXBsZXRlZF9hdGAuIFNoYXJlcyB0aGUgb3JnL3JvbGx1cCArIHBpcGVsaW5lSWQvZW52aXJvbm1lbnQgc2NvcGluZy4gTVRUUixcbiAgICogbGVhZCB0aW1lLCBhbmQgcG9zdC1kZXBsb3kgQ0ZSIGFyZSBpbnRlbnRpb25hbGx5IG9taXR0ZWQgKHRvbyBoZWF2eSB0byBidWNrZXQpLlxuICAgKi9cbiAgYXN5bmMgZ2V0RG9yYVRyZW5kKFxuICAgIG9yZ0lkOiBzdHJpbmcsXG4gICAgaW50ZXJ2YWw6IHN0cmluZyxcbiAgICBmcm9tOiBzdHJpbmcsXG4gICAgdG86IHN0cmluZyxcbiAgICBvcmdJZHM/OiBzdHJpbmdbXSxcbiAgICBvcHRzOiBEb3JhT3B0aW9ucyA9IHt9LFxuICApOiBQcm9taXNlPERvcmFUcmVuZFBvaW50W10+IHtcbiAgICBhc3NlcnRSZXBvcnRJbnRlcnZhbChpbnRlcnZhbCk7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCB7IHBpcGVsaW5lSWQsIGVudmlyb25tZW50IH0gPSBvcHRzO1xuICAgIGNvbnN0IHBpcGVsaW5lQ2xhdXNlID0gcGlwZWxpbmVJZCA/IHNxbGBBTkQgZS5waXBlbGluZV9pZCA9ICR7cGlwZWxpbmVJZH1gIDogc3FsYGA7XG4gICAgY29uc3QgZW52Q2xhdXNlID0gZW52aXJvbm1lbnQgPyBzcWxgQU5EIGUuZW52aXJvbm1lbnQgPSAke2Vudmlyb25tZW50fWAgOiBzcWxgYDtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFdJVEggZGVwbG95cyBBUyAoXG4gICAgICAgICAgLS0gUEVSLUVYRUNVVElPTiBkZXBsb3kgdW5pdCAoRDEpOiBvbmUgcm93IHBlciAoZW52aXJvbm1lbnQsIGV4ZWN1dGlvbiksXG4gICAgICAgICAgLS0gZXZlcnkgZGVwbG95IFNUQUdFIHRvIHRoYXQgZW52IHdpdGhpbiB0aGUgZXhlY3V0aW9uIHJvbGxlZCB1cCB0byBhXG4gICAgICAgICAgLS0gc2luZ2xlIHRlcm1pbmFsIHN0YXR1cyDigJQgTk9UIG9uZSByb3cgcGVyIHN0YWdlLlxuICAgICAgICAgIFNFTEVDVFxuICAgICAgICAgICAgJHt0ZXJtaW5hbFN0YXR1c1JvbGx1cH0gQVMgc3RhdHVzLFxuICAgICAgICAgICAgTUFYKGUuY29tcGxldGVkX2F0KSBBUyBjb21wbGV0ZWRfYXRcbiAgICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgICBKT0lOICR7c2NoZW1hLnBpcGVsaW5lfSBwIE9OIHAuaWQgPSBlLnBpcGVsaW5lX2lkXG4gICAgICAgICAgV0hFUkUgcC5vcmdfaWQgJHtwcmVkfSBBTkQgZS5ldmVudF90eXBlID0gJ1NUQUdFJyBBTkQgZS5lbnZpcm9ubWVudCBJUyBOT1QgTlVMTFxuICAgICAgICAgICAgJHtwaXBlbGluZUNsYXVzZX0gJHtlbnZDbGF1c2V9XG4gICAgICAgICAgICBBTkQgZS5jb21wbGV0ZWRfYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIGUuY29tcGxldGVkX2F0IDw9ICR7dG99Ojp0aW1lc3RhbXB0elxuICAgICAgICAgIEdST1VQIEJZIGUuZW52aXJvbm1lbnQsIGUuZXhlY3V0aW9uX2lkXG4gICAgICAgIClcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgREFURV9UUlVOQygke2ludGVydmFsfSwgY29tcGxldGVkX2F0KTo6dGV4dCBBUyBwZXJpb2QsXG4gICAgICAgICAgQ09VTlQoKikgRklMVEVSIChXSEVSRSBzdGF0dXMgPSAnU1VDQ0VFREVEJyk6OmludCBBUyBkZXBsb3ltZW50cyxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIHN0YXR1cyA9ICdGQUlMRUQnKTo6aW50IEFTIGZhaWxlZCxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIHN0YXR1cyBJTiAoJ1NVQ0NFRURFRCcsICdGQUlMRUQnKSk6OmludCBBUyB0b3RhbCxcbiAgICAgICAgICAtLSBxdW90ZWQgYWxpYXMgc28gdGhlIHJhdyByb3cga2V5IGlzIGV4YWN0bHkgdGhlIERvcmFUcmVuZFBvaW50IGZpZWxkXG4gICAgICAgICAgQ09BTEVTQ0UoUk9VTkQoQ09VTlQoKikgRklMVEVSIChXSEVSRSBzdGF0dXMgPSAnRkFJTEVEJyk6Om51bWVyaWNcbiAgICAgICAgICAgIC8gTlVMTElGKENPVU5UKCopIEZJTFRFUiAoV0hFUkUgc3RhdHVzIElOICgnU1VDQ0VFREVEJywgJ0ZBSUxFRCcpKSwgMCkgKiAxMDAsIDEpLCAwKTo6ZmxvYXQgQVMgXCJjaGFuZ2VGYWlsdXJlUGN0XCJcbiAgICAgICAgRlJPTSBkZXBsb3lzXG4gICAgICAgIEdST1VQIEJZIHBlcmlvZCBPUkRFUiBCWSBwZXJpb2RcbiAgICAgIGApLnRoZW4ociA9PiBkcml6emxlUm93czxEb3JhVHJlbmRQb2ludD4oci5yb3dzKSkpO1xuICAgIGNvbnN0IGtleSA9IGAke29yZ0lkfTpkb3JhLXRyZW5kOiR7aW50ZXJ2YWx9OiR7ZnJvbX06JHt0b306JHtwaXBlbGluZUlkID8/ICcnfToke2Vudmlyb25tZW50ID8/ICcnfWA7XG4gICAgcmV0dXJuIHRoaXMucnVuUmVwb3J0KGtleSwgbXVsdGksIGV4ZWMpO1xuICB9XG5cbiAgLyoqXG4gICAqIDEuMTAgUGVyLXBpcGVsaW5lIEJVSUxEIEhFQUxUSCAoUGhhc2UgNikg4oCUIGEgc3RhbmRhcmQgKE5PVCBgYWR2YW5jZWRfcmVwb3J0aW5nYClcbiAgICogcGVyLXN0YWdlIGJyZWFrZG93biBmb3Igb25lIHBpcGVsaW5lIG92ZXIgYSBbZnJvbSx0b10gd2luZG93LiBBZ2dyZWdhdGVzIHRoZVxuICAgKiBleGlzdGluZyBgcGlwZWxpbmVfZXZlbnRzYCBTVEFHRSByb3dzOiBlYWNoIHN0YWdlIGlzIHJvbGxlZCB1cCBwZXIgZXhlY3V0aW9uXG4gICAqIHRvIGEgdGVybWluYWwgc3RhdHVzIChGQUlMRUQgd2lucywgdGhlbiBTVUNDRUVERUQpICsgaXRzIG1heCBkdXJhdGlvbiwgdGhlblxuICAgKiBncm91cGVkIHBlciBzdGFnZSBpbnRvIHJ1bi9zdWNjZXNzL2ZhaWx1cmUgY291bnRzLCBhIHN1Y2Nlc3MgcmF0ZSwgYW5kXG4gICAqIGR1cmF0aW9uIHBlcmNlbnRpbGVzIChwNTAvcDkwL3A5OSkuIFRvdGFscyBzdW0gYWNyb3NzIHN0YWdlcy4gT3JnLXNjb3BlZCB2aWFcbiAgICogdGhlIHBpcGVsaW5lIGpvaW4gKGBwLm9yZ19pZCAke3ByZWR9YCkgKyByb2xsdXAtYXdhcmUgbGlrZSB0aGUgc2libGluZyByZXBvcnRzO1xuICAgKiBhIHBpcGVsaW5lSWQgb3duZWQgYnkgYW5vdGhlciBvcmcgcmV0dXJucyBhbiBlbXB0eSBicmVha2Rvd24uXG4gICAqL1xuICBhc3luYyBnZXRCdWlsZEhlYWx0aChcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIHBpcGVsaW5lSWQ6IHN0cmluZyxcbiAgICBmcm9tOiBzdHJpbmcsXG4gICAgdG86IHN0cmluZyxcbiAgICBvcmdJZHM/OiBzdHJpbmdbXSxcbiAgKTogUHJvbWlzZTxCdWlsZEhlYWx0aD4ge1xuICAgIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IHRoaXMub3JnU2NvcGUob3JnSWQsIG9yZ0lkcyk7XG4gICAgY29uc3QgZXhlYyA9ICgpID0+IHdpdGhUZW5hbnRUeChhc3luYyAodHgpID0+IHtcbiAgICAgIGNvbnN0IHJvd3MgPSBkcml6emxlUm93czxCdWlsZEhlYWx0aFN0YWdlPigoYXdhaXQgdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFdJVEggc3RhZ2VfcnVucyBBUyAoXG4gICAgICAgICAgU0VMRUNUXG4gICAgICAgICAgICBlLnN0YWdlX25hbWUgQVMgc3RhZ2UsXG4gICAgICAgICAgICAke3Rlcm1pbmFsU3RhdHVzUm9sbHVwfSBBUyBzdGF0dXMsXG4gICAgICAgICAgICBNQVgoZS5kdXJhdGlvbl9tcykgQVMgZHVyYXRpb25fbXNcbiAgICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgICBKT0lOICR7c2NoZW1hLnBpcGVsaW5lfSBwIE9OIHAuaWQgPSBlLnBpcGVsaW5lX2lkXG4gICAgICAgICAgV0hFUkUgcC5vcmdfaWQgJHtwcmVkfSBBTkQgZS5waXBlbGluZV9pZCA9ICR7cGlwZWxpbmVJZH1cbiAgICAgICAgICAgIEFORCBlLmV2ZW50X3R5cGUgPSAnU1RBR0UnIEFORCBlLnN0YWdlX25hbWUgSVMgTk9UIE5VTExcbiAgICAgICAgICAgIEFORCBlLnN0YXJ0ZWRfYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIGUuc3RhcnRlZF9hdCA8PSAke3RvfTo6dGltZXN0YW1wdHpcbiAgICAgICAgICBHUk9VUCBCWSBlLnN0YWdlX25hbWUsIGUuZXhlY3V0aW9uX2lkXG4gICAgICAgIClcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgc3RhZ2UsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBydW5zLFxuICAgICAgICAgIENPVU5UKCopIEZJTFRFUiAoV0hFUkUgc3RhdHVzID0gJ1NVQ0NFRURFRCcpOjppbnQgQVMgc3VjY2Vzc2VzLFxuICAgICAgICAgIENPVU5UKCopIEZJTFRFUiAoV0hFUkUgc3RhdHVzID0gJ0ZBSUxFRCcpOjppbnQgQVMgZmFpbHVyZXMsXG4gICAgICAgICAgQ09BTEVTQ0UoUk9VTkQoQ09VTlQoKikgRklMVEVSIChXSEVSRSBzdGF0dXMgPSAnU1VDQ0VFREVEJyk6Om51bWVyaWNcbiAgICAgICAgICAgIC8gTlVMTElGKENPVU5UKCopLCAwKSAqIDEwMCwgMSksIDApOjpmbG9hdCBBUyBcInN1Y2Nlc3NSYXRlXCIsXG4gICAgICAgICAgUEVSQ0VOVElMRV9DT05UKDAuNSkgV0lUSElOIEdST1VQIChPUkRFUiBCWSBkdXJhdGlvbl9tcyk6OmludCBBUyBcInA1ME1zXCIsXG4gICAgICAgICAgUEVSQ0VOVElMRV9DT05UKDAuOSkgV0lUSElOIEdST1VQIChPUkRFUiBCWSBkdXJhdGlvbl9tcyk6OmludCBBUyBcInA5ME1zXCIsXG4gICAgICAgICAgUEVSQ0VOVElMRV9DT05UKDAuOTkpIFdJVEhJTiBHUk9VUCAoT1JERVIgQlkgZHVyYXRpb25fbXMpOjppbnQgQVMgXCJwOTlNc1wiXG4gICAgICAgIEZST00gc3RhZ2VfcnVuc1xuICAgICAgICBXSEVSRSBzdGF0dXMgSU4gKCdTVUNDRUVERUQnLCAnRkFJTEVEJylcbiAgICAgICAgR1JPVVAgQlkgc3RhZ2VcbiAgICAgICAgT1JERVIgQlkgcnVucyBERVNDLCBzdGFnZVxuICAgICAgYCkpLnJvd3MpO1xuXG4gICAgICAvLyBUb3RhbHMgc3VtIGFjcm9zcyBzdGFnZXMgKHRvdGFsIHN0YWdlLXJ1bnMgLyBzdGFnZS1mYWlsdXJlcyBpbiB0aGUgd2luZG93KS5cbiAgICAgIGxldCBydW5zID0gMCwgZmFpbHVyZXMgPSAwO1xuICAgICAgZm9yIChjb25zdCBzIG9mIHJvd3MpIHsgcnVucyArPSBzLnJ1bnM7IGZhaWx1cmVzICs9IHMuZmFpbHVyZXM7IH1cbiAgICAgIGNvbnN0IGZhaWx1cmVSYXRlID0gcnVucyA+IDAgPyByb3VuZCgoZmFpbHVyZXMgLyBydW5zKSAqIDEwMCwgMSkgOiAwO1xuICAgICAgcmV0dXJuIHsgc3RhZ2VzOiByb3dzLCB0b3RhbHM6IHsgcnVucywgZmFpbHVyZXMsIGZhaWx1cmVSYXRlIH0gfTtcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5ydW5SZXBvcnQoYCR7b3JnSWR9OmJ1aWxkLWhlYWx0aDoke3BpcGVsaW5lSWR9OiR7ZnJvbX06JHt0b31gLCBtdWx0aSwgZXhlYyk7XG4gIH1cblxuICAvLyDilIDilIAgQ2F0ZWdvcnkgMjogUGx1Z2luIEludmVudG9yeSAmIEJ1aWxkcyDilIDilIBcblxuICAvKiogMi4xIFBsdWdpbiBzdW1tYXJ5IOKAlCBjb3VudHMgYW5kIGJyZWFrZG93bnMuXG4gICAqICBJTlRFTlRJT05BTExZIFNJTkdMRS1PUkcgKG5vIHJvbGx1cCk6IHBsdWdpbiBpbnZlbnRvcnkgaXMgYW4gb3JnLW93bmVkXG4gICAqICBhc3NldCBjb3VudCwgbm90IGFuIGV4ZWN1dGlvbi9idWlsZCBhY3Rpdml0eSByZXBvcnQsIHNvIGEgcGFyZW50J3MgdmlldyBpc1xuICAgKiAgaXRzIG93biBwbHVnaW5zIOKAlCB0ZWFtcyBtYW5hZ2UgdGhlaXIgb3duIGludmVudG9yeS4gU2luZ2xlIGA9ICRvcmdgIHNjb3BlLiAqL1xuICBhc3luYyBnZXRQbHVnaW5TdW1tYXJ5KG9yZ0lkOiBzdHJpbmcpOiBQcm9taXNlPFBsdWdpblN1bW1hcnk+IHtcbiAgICByZXR1cm4gaW52ZW50b3J5Q2FjaGUuZ2V0T3JTZXQoYCR7b3JnSWR9OnBsdWdpbi1zdW1tYXJ5YCwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1RcbiAgICAgICAgICBDT1VOVCgqKTo6aW50IEFTIHRvdGFsLFxuICAgICAgICAgIENPVU5UKCopIEZJTFRFUiAoV0hFUkUgJHtzY2hlbWEucGx1Z2luLmlzQWN0aXZlfSk6OmludCBBUyBhY3RpdmUsXG4gICAgICAgICAgQ09VTlQoKikgRklMVEVSIChXSEVSRSBOT1QgJHtzY2hlbWEucGx1Z2luLmlzQWN0aXZlfSk6OmludCBBUyBpbmFjdGl2ZSxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFICR7c2NoZW1hLnBsdWdpbi52aXNpYmlsaXR5fSA9ICdwdWJsaWMnKTo6aW50IEFTIHB1YmxpYyxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFICR7c2NoZW1hLnBsdWdpbi52aXNpYmlsaXR5fSA9ICdvcmcnKTo6aW50IEFTIG9yZyxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFICR7c2NoZW1hLnBsdWdpbi52aXNpYmlsaXR5fSA9ICdwcml2YXRlJyk6OmludCBBUyBwcml2YXRlLFxuICAgICAgICAgIENPVU5UKERJU1RJTkNUICR7c2NoZW1hLnBsdWdpbi5uYW1lfSk6OmludCBBUyB1bmlxdWVfbmFtZXNcbiAgICAgICAgRlJPTSAke3NjaGVtYS5wbHVnaW59XG4gICAgICAgIFdIRVJFICR7c2NoZW1hLnBsdWdpbi5vcmdJZH0gPSAke29yZ0lkfVxuICAgICAgYCkpO1xuICAgICAgcmV0dXJuIChkcml6emxlUm93czxQbHVnaW5TdW1tYXJ5Pihyb3dzLnJvd3MpWzBdIHx8IHsgdG90YWw6IDAsIGFjdGl2ZTogMCwgaW5hY3RpdmU6IDAsIHB1YmxpYzogMCwgb3JnOiAwLCBwcml2YXRlOiAwLCB1bmlxdWVOYW1lczogMCB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiAyLjIgVHlwZSAmIGNvbXB1dGUgZGlzdHJpYnV0aW9uLlxuICAgKiAgSU5URU5USU9OQUxMWSBTSU5HTEUtT1JHIChubyByb2xsdXApOiBwbHVnaW4gaW52ZW50b3J5IGlzIHBlci1vcmcgKHNlZVxuICAgKiAgZ2V0UGx1Z2luU3VtbWFyeSkuIFNpbmdsZSBgPSAkb3JnYCBzY29wZS4gKi9cbiAgYXN5bmMgZ2V0UGx1Z2luRGlzdHJpYnV0aW9uKG9yZ0lkOiBzdHJpbmcpOiBQcm9taXNlPFR5cGVDb21wdXRlRGlzdHJpYnV0aW9uW10+IHtcbiAgICByZXR1cm4gaW52ZW50b3J5Q2FjaGUuZ2V0T3JTZXQoYCR7b3JnSWR9OnBsdWdpbi1kaXN0cmlidXRpb25gLCAoKSA9PlxuICAgICAgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgICR7c2NoZW1hLnBsdWdpbi5wbHVnaW5UeXBlfSBBUyBwbHVnaW5fdHlwZSxcbiAgICAgICAgICAke3NjaGVtYS5wbHVnaW4uY29tcHV0ZVR5cGV9IEFTIGNvbXB1dGVfdHlwZSxcbiAgICAgICAgICBDT1VOVCgqKTo6aW50IEFTIGNvdW50XG4gICAgICAgIEZST00gJHtzY2hlbWEucGx1Z2lufVxuICAgICAgICBXSEVSRSAke3NjaGVtYS5wbHVnaW4ub3JnSWR9ID0gJHtvcmdJZH0gQU5EICR7c2NoZW1hLnBsdWdpbi5pc0FjdGl2ZX0gPSB0cnVlXG4gICAgICAgIEdST1VQIEJZICR7c2NoZW1hLnBsdWdpbi5wbHVnaW5UeXBlfSwgJHtzY2hlbWEucGx1Z2luLmNvbXB1dGVUeXBlfVxuICAgICAgICBPUkRFUiBCWSBjb3VudCBERVNDXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8VHlwZUNvbXB1dGVEaXN0cmlidXRpb24+KHIucm93cykpKSxcbiAgICApO1xuICB9XG5cbiAgLyoqIDIuMyBWZXJzaW9uIGNvdW50cyBwZXIgcGx1Z2luIG5hbWUuXG4gICAqICBJTlRFTlRJT05BTExZIFNJTkdMRS1PUkcgKG5vIHJvbGx1cCk6IHBsdWdpbiBpbnZlbnRvcnkgaXMgcGVyLW9yZyAoc2VlXG4gICAqICBnZXRQbHVnaW5TdW1tYXJ5KS4gU2luZ2xlIGA9ICRvcmdgIHNjb3BlLiAqL1xuICBhc3luYyBnZXRQbHVnaW5WZXJzaW9ucyhvcmdJZDogc3RyaW5nKTogUHJvbWlzZTxWZXJzaW9uQ291bnRbXT4ge1xuICAgIHJldHVybiBpbnZlbnRvcnlDYWNoZS5nZXRPclNldChgJHtvcmdJZH06cGx1Z2luLXZlcnNpb25zYCwgKCkgPT5cbiAgICAgIHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1RcbiAgICAgICAgICAke3NjaGVtYS5wbHVnaW4ubmFtZX0sXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyB2ZXJzaW9uX2NvdW50LFxuICAgICAgICAgIC0tIEhpZ2hlc3QgdmVyc2lvbiBieSBOVU1FUklDIHNlbXZlciBjb3JlIChub3QgbGV4aWNhbDogJzEwLjAuMCcgPiAnOS4wLjAnKS5cbiAgICAgICAgICAtLSBTdHJpcCBhbnkgLXByZXJlbGVhc2UvK2J1aWxkIHN1ZmZpeCBiZWZvcmUgdGhlIGludFtdIGNhc3Qgc28gaXQgY2FuJ3RcbiAgICAgICAgICAtLSBlcnJvciBvbiB2YWxpZCBzZW12ZXI7IGtlZXAgdGhlIG9yaWdpbmFsIHN0cmluZyBmb3IgZGlzcGxheS5cbiAgICAgICAgICAtLSBHVUFSRDogYSBub24tbnVtZXJpYy1kb3R0ZWQgdmVyc2lvbiAoJ2xhdGVzdCcsICcnLCAndjEnKSB3b3VsZCBtYWtlIHRoZVxuICAgICAgICAgIC0tIDo6aW50W10gY2FzdCB0aHJvdyBhbmQgYmxvdyB1cCB0aGUgV0hPTEUgb3JnIHN1bW1hcnkuIE9ubHkgY2FzdCByb3dzXG4gICAgICAgICAgLS0gd2hvc2Ugc3RyaXBwZWQgY29yZSBtYXRjaGVzIGEgbnVtZXJpYy1kb3R0ZWQgc2hhcGU7IHNvcnQgZXZlcnl0aGluZ1xuICAgICAgICAgIC0tIGVsc2UgdG8gdGhlIGJvdHRvbSB2aWEgYSBzZW50aW5lbCBBUlJBWVstMV0gc28gdGhlIHJvdyBzdGlsbCBjb3VudHMuXG4gICAgICAgICAgKGFycmF5X2FnZygke3NjaGVtYS5wbHVnaW4udmVyc2lvbn1cbiAgICAgICAgICAgICBPUkRFUiBCWSBDQVNFXG4gICAgICAgICAgICAgICBXSEVOIHJlZ2V4cF9yZXBsYWNlKCR7c2NoZW1hLnBsdWdpbi52ZXJzaW9ufSwgJ1stK10uKiQnLCAnJykgfiAnXlswLTldKyhbLl1bMC05XSspKiQnXG4gICAgICAgICAgICAgICAgIFRIRU4gc3RyaW5nX3RvX2FycmF5KHJlZ2V4cF9yZXBsYWNlKCR7c2NoZW1hLnBsdWdpbi52ZXJzaW9ufSwgJ1stK10uKiQnLCAnJyksICcuJyk6OmludFtdXG4gICAgICAgICAgICAgICBFTFNFIEFSUkFZWy0xXVxuICAgICAgICAgICAgIEVORCBERVNDXG4gICAgICAgICAgKSlbMV0gQVMgbGF0ZXN0X3ZlcnNpb24sXG4gICAgICAgICAgYm9vbF9vcigke3NjaGVtYS5wbHVnaW4uaXNEZWZhdWx0fSkgQVMgaGFzX2RlZmF1bHRcbiAgICAgICAgRlJPTSAke3NjaGVtYS5wbHVnaW59XG4gICAgICAgIFdIRVJFICR7c2NoZW1hLnBsdWdpbi5vcmdJZH0gPSAke29yZ0lkfSBBTkQgJHtzY2hlbWEucGx1Z2luLmlzQWN0aXZlfSA9IHRydWVcbiAgICAgICAgR1JPVVAgQlkgJHtzY2hlbWEucGx1Z2luLm5hbWV9XG4gICAgICAgIE9SREVSIEJZIHZlcnNpb25fY291bnQgREVTQ1xuICAgICAgYCkudGhlbihyID0+IGRyaXp6bGVSb3dzPFZlcnNpb25Db3VudD4oci5yb3dzKSkpLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogMi40IEJ1aWxkIHN1Y2Nlc3MgcmF0ZSBvdmVyIHRpbWUuXG4gICAqXG4gICAqIFNUQVRVUyBDQVNJTkcgTk9URTogVGhpcyBxdWVyeSBmaWx0ZXJzIGJ5IGBldmVudF9zb3VyY2UgPSAncGx1Z2luLWJ1aWxkJ2BcbiAgICogYW5kIHVzZXMgbG93ZXJjYXNlIHN0YXR1cyB2YWx1ZXMgKGAnY29tcGxldGVkJ2AsIGAnZmFpbGVkJ2ApLCB3aGlsZVxuICAgKiBgZ2V0U3VjY2Vzc1JhdGVgICgxLjIpIGZpbHRlcnMgYnkgYGV2ZW50X3R5cGUgPSAnUElQRUxJTkUnYCB3aXRoXG4gICAqIHVwcGVyY2FzZSBBV1Mtc3R5bGUgc3RhdHVzZXMgKGAnU1VDQ0VFREVEJ2AsIGAnRkFJTEVEJ2AsIGAnQ0FOQ0VMRUQnYCkuXG4gICAqIFRoZSBjYXNpbmcgZHJpZnQgaXMgaW50ZW50aW9uYWwgYW5kIHRyYWNrcyB0aGUgcHJvZHVjZXI6XG4gICAqICAgLSBgcGx1Z2luLWJ1aWxkYCBldmVudHMgY29tZSBmcm9tIG91ciBvd24gYnVpbGQgcGlwZWxpbmUgKGxvd2VyY2FzZSlcbiAgICogICAtIGBQSVBFTElORWAgZXZlbnRzIGNvbWUgZnJvbSBBV1MgQ29kZVBpcGVsaW5lICh1cHBlcmNhc2UpXG4gICAqIFRoZSBpbmdlc3QgWm9kIHNjaGVtYSBhdCBhcGkvcmVwb3J0aW5nL3NyYy9yb3V0ZXMvZXZlbnQtaW5nZXN0LnRzXG4gICAqIFNIT1VMRCBlbnVtIHRoZXNlIHBlci1ldmVudFNvdXJjZSBzbyB3ZSBjYXRjaCBkcmlmdCBhdCBpbmdlc3QgcmF0aGVyXG4gICAqIHRoYW4gc2lsZW50bHkgcHJvZHVjaW5nIHplcm8gcm93cyBoZXJlLiBTZWUgZmluZGluZ3MgTjcxLlxuICAgKi9cbiAgYXN5bmMgZ2V0QnVpbGRTdWNjZXNzUmF0ZShvcmdJZDogc3RyaW5nLCBpbnRlcnZhbDogc3RyaW5nLCBmcm9tOiBzdHJpbmcsIHRvOiBzdHJpbmcsIG9yZ0lkcz86IHN0cmluZ1tdKTogUHJvbWlzZTxCdWlsZFRpbWVTZXJpZXNFbnRyeVtdPiB7XG4gICAgYXNzZXJ0UmVwb3J0SW50ZXJ2YWwoaW50ZXJ2YWwpO1xuICAgIC8vIEJ1aWxkIGFjdGl2aXR5IHJlcG9ydCDigJQgcm9sbHVwLWF3YXJlIGxpa2UgdGhlIGV4ZWN1dGlvbiByZXBvcnRzLiBUaGVzZVxuICAgIC8vIHJvd3MgYXJlIGdhdGVkIG9uIHRoZSBwaXBlbGluZV9ldmVudCBgb3JnX2lkYCBkaXJlY3RseSAobm8gcGlwZWxpbmUgam9pbiksXG4gICAgLy8gc28gYHByZWRgIGFwcGxpZXMgdG8gYGUub3JnX2lkYC5cbiAgICBjb25zdCB7IHByZWQsIG11bHRpIH0gPSB0aGlzLm9yZ1Njb3BlKG9yZ0lkLCBvcmdJZHMpO1xuICAgIGNvbnN0IGV4ZWMgPSAoKSA9PiB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgREFURV9UUlVOQygke2ludGVydmFsfSwgZS5zdGFydGVkX2F0KTo6dGV4dCBBUyBwZXJpb2QsXG4gICAgICAgICAgQ09VTlQoKikgRklMVEVSIChXSEVSRSBlLnN0YXR1cyA9ICdjb21wbGV0ZWQnKTo6aW50IEFTIHN1Y2NlZWRlZCxcbiAgICAgICAgICBDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ2ZhaWxlZCcpOjppbnQgQVMgZmFpbGVkLFxuICAgICAgICAgIFJPVU5EKENPVU5UKCopIEZJTFRFUiAoV0hFUkUgZS5zdGF0dXMgPSAnY29tcGxldGVkJyk6Om51bWVyaWNcbiAgICAgICAgICAgIC8gTlVMTElGKENPVU5UKCopLCAwKSAqIDEwMCwgMSk6OmZsb2F0IEFTIHN1Y2Nlc3NfcGN0XG4gICAgICAgIEZST00gJHtzY2hlbWEucGlwZWxpbmVFdmVudH0gZVxuICAgICAgICBXSEVSRSBlLm9yZ19pZCAke3ByZWR9IEFORCBlLmV2ZW50X3NvdXJjZSA9ICdwbHVnaW4tYnVpbGQnXG4gICAgICAgICAgQU5EIGUuc3RhdHVzIElOICgnY29tcGxldGVkJywgJ2ZhaWxlZCcpXG4gICAgICAgICAgQU5EIGUuc3RhcnRlZF9hdCA+PSAke2Zyb219Ojp0aW1lc3RhbXB0eiBBTkQgZS5zdGFydGVkX2F0IDw9ICR7dG99Ojp0aW1lc3RhbXB0elxuICAgICAgICBHUk9VUCBCWSBwZXJpb2QgT1JERVIgQlkgcGVyaW9kXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8QnVpbGRUaW1lU2VyaWVzRW50cnk+KHIucm93cykpKTtcbiAgICByZXR1cm4gdGhpcy5ydW5SZXBvcnQoYCR7b3JnSWR9OmJ1aWxkLXN1Y2Nlc3M6JHtpbnRlcnZhbH06JHtmcm9tfToke3RvfWAsIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8qKiAyLjUgQnVpbGQgZHVyYXRpb24gcGVyIHBsdWdpbi4gQnVpbGQgYWN0aXZpdHkgcmVwb3J0IOKAlCByb2xsdXAtYXdhcmUuICovXG4gIGFzeW5jIGdldEJ1aWxkRHVyYXRpb24ob3JnSWQ6IHN0cmluZywgZnJvbTogc3RyaW5nLCB0bzogc3RyaW5nLCBvcmdJZHM/OiBzdHJpbmdbXSk6IFByb21pc2U8QnVpbGREdXJhdGlvbltdPiB7XG4gICAgY29uc3QgeyBwcmVkLCBtdWx0aSB9ID0gdGhpcy5vcmdTY29wZShvcmdJZCwgb3JnSWRzKTtcbiAgICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIGUuZGV0YWlsLT4+J3BsdWdpbk5hbWUnIEFTIHBsdWdpbl9uYW1lLFxuICAgICAgICAgIEFWRyhlLmR1cmF0aW9uX21zKTo6aW50IEFTIGF2Z19tcyxcbiAgICAgICAgICBNQVgoZS5kdXJhdGlvbl9tcyk6OmludCBBUyBtYXhfbXMsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBidWlsZHNcbiAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgIFdIRVJFIGUub3JnX2lkICR7cHJlZH0gQU5EIGUuZXZlbnRfc291cmNlID0gJ3BsdWdpbi1idWlsZCcgQU5EIGUuZHVyYXRpb25fbXMgSVMgTk9UIE5VTExcbiAgICAgICAgICBBTkQgZS5zdGFydGVkX2F0ID49ICR7ZnJvbX06OnRpbWVzdGFtcHR6IEFORCBlLnN0YXJ0ZWRfYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6XG4gICAgICAgIEdST1VQIEJZIHBsdWdpbl9uYW1lIE9SREVSIEJZIGF2Z19tcyBERVNDXG4gICAgICBgKS50aGVuKHIgPT4gZHJpenpsZVJvd3M8QnVpbGREdXJhdGlvbj4oci5yb3dzKSkpO1xuICAgIHJldHVybiB0aGlzLnJ1blJlcG9ydChgJHtvcmdJZH06YnVpbGQtZHVyYXRpb246JHtmcm9tfToke3RvfWAsIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8qKiAyLjYgQnVpbGQgZmFpbHVyZXMg4oCUIHRvcCBlcnJvciBtZXNzYWdlcy4gQnVpbGQgYWN0aXZpdHkgcmVwb3J0IOKAlCByb2xsdXAtYXdhcmUuICovXG4gIGFzeW5jIGdldEJ1aWxkRmFpbHVyZXMob3JnSWQ6IHN0cmluZywgZnJvbTogc3RyaW5nLCB0bzogc3RyaW5nLCBsaW1pdDogbnVtYmVyID0gMjAsIG9yZ0lkcz86IHN0cmluZ1tdKTogUHJvbWlzZTxCdWlsZEZhaWx1cmVbXT4ge1xuICAgIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IHRoaXMub3JnU2NvcGUob3JnSWQsIG9yZ0lkcyk7XG4gICAgY29uc3QgZXhlYyA9ICgpID0+IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1RcbiAgICAgICAgICBlLmRldGFpbC0+PidwbHVnaW5OYW1lJyBBUyBwbHVnaW5fbmFtZSxcbiAgICAgICAgICBlLmVycm9yX21lc3NhZ2UsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBvY2N1cnJlbmNlcyxcbiAgICAgICAgICBNQVgoZS5zdGFydGVkX2F0KTo6dGV4dCBBUyBsYXN0X3NlZW5cbiAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgIFdIRVJFIGUub3JnX2lkICR7cHJlZH0gQU5EIGUuZXZlbnRfc291cmNlID0gJ3BsdWdpbi1idWlsZCcgQU5EIGUuc3RhdHVzID0gJ2ZhaWxlZCdcbiAgICAgICAgICBBTkQgZS5zdGFydGVkX2F0ID49ICR7ZnJvbX06OnRpbWVzdGFtcHR6IEFORCBlLnN0YXJ0ZWRfYXQgPD0gJHt0b306OnRpbWVzdGFtcHR6XG4gICAgICAgIEdST1VQIEJZIHBsdWdpbl9uYW1lLCBlLmVycm9yX21lc3NhZ2VcbiAgICAgICAgT1JERVIgQlkgb2NjdXJyZW5jZXMgREVTQ1xuICAgICAgICBMSU1JVCAke2xpbWl0fVxuICAgICAgYCkudGhlbihyID0+IGRyaXp6bGVSb3dzPEJ1aWxkRmFpbHVyZT4oci5yb3dzKSkpO1xuICAgIHJldHVybiB0aGlzLnJ1blJlcG9ydChgJHtvcmdJZH06YnVpbGQtZmFpbHVyZXM6JHtmcm9tfToke3RvfToke2xpbWl0fWAsIG11bHRpLCBleGVjKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBDYXRlZ29yeSAzOiBET1JBIHdyaXRlIHBhdGhzIChwb3N0LWRlcGxveSBvdXRjb21lcyArIGluZ2VzdCBoZWFsdGgpIOKUgOKUgFxuXG4gIC8qKlxuICAgKiBSZWNvcmQgYSBtYW51YWwgcG9zdC1kZXBsb3kgb3V0Y29tZSBtYXJrZXIgKFBoYXNlIDIpOiBhIHVzZXIgbWFya3MgYVxuICAgKiBkZXBsb3ltZW50IGBmYWlsZWRgIChhIHByb2R1Y3Rpb24gaW5jaWRlbnQgbGlua2VkIHRvIHRoZSBkZXBsb3kpIG9yXG4gICAqIGByZXN0b3JlZGAgKHJlY292ZXJlZCkuIElkZW1wb3RlbnQg4oCUIGtleWVkIG9uIChleGVjdXRpb25faWQsIG91dGNvbWUpIHNvIGFcbiAgICogZHVwbGljYXRlIFBPU1QgcmVmcmVzaGVzIGBhdGAgcmF0aGVyIHRoYW4gZG91YmxlLWNvdW50aW5nLCB3aGlsZSBhXG4gICAqIGZhaWxlZOKGknJlc3RvcmVkIHBhaXIgc3RheXMgdHdvIHJvd3MuIEZlZWRzIHRoZSBwb3N0LWRlcGxveSBDRlIgY29tcG9uZW50IGFuZFxuICAgKiB0aGUgcmVhbCBNVFRSLiBSdW5zIHVuZGVyIHRoZSBjYWxsZXIncyBvcmcgY29udGV4dCAoUkxTIFdJVEggQ0hFQ0spLlxuICAgKi9cbiAgYXN5bmMgcmVjb3JkRGVwbG95bWVudE91dGNvbWUoXG4gICAgb3JnSWQ6IHN0cmluZyxcbiAgICBleGVjdXRpb25JZDogc3RyaW5nLFxuICAgIGlucHV0OiB7IG91dGNvbWU6ICdmYWlsZWQnIHwgJ3Jlc3RvcmVkJzsgYXQ6IHN0cmluZzsgZW52aXJvbm1lbnQ/OiBzdHJpbmcgfSxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgcnVuV2l0aFRlbmFudENvbnRleHQoeyBvcmdJZCwgaXNTdXBlckFkbWluOiBmYWxzZSB9LCAoKSA9PlxuICAgICAgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguaW5zZXJ0KHNjaGVtYS5kZXBsb3ltZW50T3V0Y29tZSkudmFsdWVzKHtcbiAgICAgICAgZXhlY3V0aW9uSWQsXG4gICAgICAgIG9yZ0lkLFxuICAgICAgICBlbnZpcm9ubWVudDogc2NydWJPcHRpb25hbChpbnB1dC5lbnZpcm9ubWVudCksXG4gICAgICAgIG91dGNvbWU6IGlucHV0Lm91dGNvbWUsXG4gICAgICAgIGF0OiBuZXcgRGF0ZShpbnB1dC5hdCksXG4gICAgICB9KS5vbkNvbmZsaWN0RG9VcGRhdGUoe1xuICAgICAgICB0YXJnZXQ6IFtzY2hlbWEuZGVwbG95bWVudE91dGNvbWUuZXhlY3V0aW9uSWQsIHNjaGVtYS5kZXBsb3ltZW50T3V0Y29tZS5vdXRjb21lXSxcbiAgICAgICAgc2V0OiB7XG4gICAgICAgICAgYXQ6IG5ldyBEYXRlKGlucHV0LmF0KSxcbiAgICAgICAgICBlbnZpcm9ubWVudDogc2NydWJPcHRpb25hbChpbnB1dC5lbnZpcm9ubWVudCkgPz8gbnVsbCxcbiAgICAgICAgfSxcbiAgICAgIH0pKSxcbiAgICApO1xuICAgIC8vIE91dGNvbWUgY2hhbmdlcyB0aGUgRE9SQSBhZ2dyZWdhdGUg4oCUIGRyb3AgdGhlIG9yZydzIGNhY2hlZCByZXBvcnRzLlxuICAgIGF3YWl0IHRoaXMuaW52YWxpZGF0ZU9yZyhvcmdJZCkuY2F0Y2goKGVycikgPT4ge1xuICAgICAgbG9nZ2VyLndhcm4oJ1JlcG9ydGluZyBjYWNoZSBpbnZhbGlkYXRpb24gZmFpbGVkJywgeyBvcmdJZCwgZXJyb3I6IGVycm9yTWVzc2FnZShlcnIpIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEluZ2VzdCBhIHByb2R1Y3Rpb24gaW5jaWRlbnQgKFBoYXNlIDUpIGZyb20gdGhlIG9yZydzIGluY2lkZW50IHRvb2xpbmdcbiAgICogKFBhZ2VyRHV0eSAvIERhdGFkb2cgLyBBbGVydG1hbmFnZXIgd2ViaG9vaykuIElkZW1wb3RlbnQg4oCUIGtleWVkIG9uXG4gICAqIChvcmdfaWQsIGluY2lkZW50X2lkKSBzbyBhIGxhdGVyIHJlc29sdmUgcmUtcG9zdCB1cHNlcnRzIGByZXNvbHZlZF9hdGBcbiAgICogKGFuZCByZWZyZXNoZXMgdGhlIG90aGVyIG11dGFibGUgZmllbGRzKSBpbnN0ZWFkIG9mIGluc2VydGluZyBhIGR1cGxpY2F0ZS5cbiAgICogRE9SQSBjb3JyZWxhdGVzIGVhY2ggaW5jaWRlbnQgdG8gdGhlIG1vc3QgcmVjZW50IHN1Y2Nlc3NmdWwgZGVwbG95IHRvIGl0c1xuICAgKiBgZW52aXJvbm1lbnRgLCBwcm9kdWNpbmcgYW4gYXV0b21hdGVkIHBvc3QtZGVwbG95IGZhaWx1cmUgKyBhIHJlYWwgTVRUUi5cbiAgICogUnVucyB1bmRlciB0aGUgY2FsbGVyJ3Mgb3JnIGNvbnRleHQgKFJMUyBXSVRIIENIRUNLKTsgZnJlZS1mb3JtIGZpZWxkcyBhcmVcbiAgICogQVdTLWlkIHNjcnViYmVkIGF0IHRoaXMgcGVyc2lzdGVuY2UgYm91bmRhcnkgbGlrZSB0aGUgb3RoZXIgaW5nZXN0IHBhdGhzLlxuICAgKi9cbiAgYXN5bmMgcmVjb3JkSW5jaWRlbnQob3JnSWQ6IHN0cmluZywgaW5wdXQ6IEluY2lkZW50SW5wdXQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBpbmNpZGVudElkID0gc2NydWJBd3NJZGVudGlmaWVyc0Zyb21TdHJpbmcoaW5wdXQuaW5jaWRlbnRJZCk7XG4gICAgY29uc3QgZW52aXJvbm1lbnQgPSBzY3J1YkF3c0lkZW50aWZpZXJzRnJvbVN0cmluZyhpbnB1dC5lbnZpcm9ubWVudCk7XG4gICAgY29uc3Qgc2V2ZXJpdHkgPSBzY3J1YkF3c0lkZW50aWZpZXJzRnJvbVN0cmluZyhpbnB1dC5zZXZlcml0eSk7XG4gICAgY29uc3Qgb3BlbmVkQXQgPSBuZXcgRGF0ZShpbnB1dC5vcGVuZWRBdCk7XG4gICAgY29uc3QgcmVzb2x2ZWRBdCA9IGlucHV0LnJlc29sdmVkQXQgPyBuZXcgRGF0ZShpbnB1dC5yZXNvbHZlZEF0KSA6IG51bGw7XG4gICAgYXdhaXQgcnVuV2l0aFRlbmFudENvbnRleHQoeyBvcmdJZCwgaXNTdXBlckFkbWluOiBmYWxzZSB9LCAoKSA9PlxuICAgICAgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguaW5zZXJ0KHNjaGVtYS5pbmNpZGVudCkudmFsdWVzKHtcbiAgICAgICAgaW5jaWRlbnRJZCxcbiAgICAgICAgb3JnSWQsXG4gICAgICAgIGVudmlyb25tZW50LFxuICAgICAgICBvcGVuZWRBdCxcbiAgICAgICAgcmVzb2x2ZWRBdCxcbiAgICAgICAgc2V2ZXJpdHksXG4gICAgICB9KS5vbkNvbmZsaWN0RG9VcGRhdGUoe1xuICAgICAgICB0YXJnZXQ6IFtzY2hlbWEuaW5jaWRlbnQub3JnSWQsIHNjaGVtYS5pbmNpZGVudC5pbmNpZGVudElkXSxcbiAgICAgICAgc2V0OiB7IGVudmlyb25tZW50LCBvcGVuZWRBdCwgcmVzb2x2ZWRBdCwgc2V2ZXJpdHkgfSxcbiAgICAgIH0pKSxcbiAgICApO1xuICAgIC8vIEEgbmV3L3VwZGF0ZWQgaW5jaWRlbnQgY2hhbmdlcyB0aGUgRE9SQSBhZ2dyZWdhdGUg4oCUIGRyb3AgY2FjaGVkIHJlcG9ydHMuXG4gICAgYXdhaXQgdGhpcy5pbnZhbGlkYXRlT3JnKG9yZ0lkKS5jYXRjaCgoZXJyKSA9PiB7XG4gICAgICBsb2dnZXIud2FybignUmVwb3J0aW5nIGNhY2hlIGludmFsaWRhdGlvbiBmYWlsZWQnLCB7IG9yZ0lkLCBlcnJvcjogZXJyb3JNZXNzYWdlKGVycikgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVhZCB0aGUgcGVyLW9yZyBET1JBIHNldHRpbmdzIChQaGFzZSA1YikuIFJldHVybnMgdGhlIHN0b3JlZFxuICAgKiBgaW5jaWRlbnRXaW5kb3dIb3Vyc2Agb3ZlcnJpZGUgKG9yIGBudWxsYCB3aGVuIHVuc2V0KSBwbHVzIHRoZSBnbG9iYWwgZW52XG4gICAqIGRlZmF1bHQsIHNvIHRoZSBzZXR0aW5ncyBVSSBjYW4gc2hvdyBib3RoLiBSdW5zIHVuZGVyIHRoZSBjYWxsZXIncyB0ZW5hbnRcbiAgICogY29udGV4dCAoUkxTLXNjb3BlZCk7IGEgc2luZ2xlLW9yZyByZWFkLlxuICAgKi9cbiAgYXN5bmMgZ2V0SW5jaWRlbnRTZXR0aW5ncyhvcmdJZDogc3RyaW5nKTogUHJvbWlzZTxJbmNpZGVudFNldHRpbmdzPiB7XG4gICAgY29uc3Qgcm93cyA9IGRyaXp6bGVSb3dzPHtcbiAgICAgIGluY2lkZW50X3dpbmRvd19ob3VyczogbnVtYmVyIHwgbnVsbDtcbiAgICAgIGV2ZW50X3JldGVudGlvbl9kYXlzOiBudW1iZXIgfCBudWxsO1xuICAgICAgZG9yYV9yZXRlbnRpb25fZGF5czogbnVtYmVyIHwgbnVsbDtcbiAgICB9PigoYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVCAke3NjaGVtYS5kb3JhU2V0dGluZ3MuaW5jaWRlbnRXaW5kb3dIb3Vyc30gQVMgaW5jaWRlbnRfd2luZG93X2hvdXJzLFxuICAgICAgICAgICAgICAgJHtzY2hlbWEuZG9yYVNldHRpbmdzLmV2ZW50UmV0ZW50aW9uRGF5c30gQVMgZXZlbnRfcmV0ZW50aW9uX2RheXMsXG4gICAgICAgICAgICAgICAke3NjaGVtYS5kb3JhU2V0dGluZ3MuZG9yYVJldGVudGlvbkRheXN9IEFTIGRvcmFfcmV0ZW50aW9uX2RheXNcbiAgICAgICAgRlJPTSAke3NjaGVtYS5kb3JhU2V0dGluZ3N9XG4gICAgICAgIFdIRVJFICR7c2NoZW1hLmRvcmFTZXR0aW5ncy5vcmdJZH0gPSAke29yZ0lkfVxuICAgICAgICBMSU1JVCAxXG4gICAgICBgKSkpLnJvd3MpO1xuICAgIGNvbnN0IHJvdyA9IHJvd3NbMF07XG4gICAgcmV0dXJuIHtcbiAgICAgIGluY2lkZW50V2luZG93SG91cnM6IHJvdz8uaW5jaWRlbnRfd2luZG93X2hvdXJzICE9IG51bGwgPyBOdW1iZXIocm93LmluY2lkZW50X3dpbmRvd19ob3VycykgOiBudWxsLFxuICAgICAgZGVmYXVsdFdpbmRvd0hvdXJzOiBET1JBX0lOQ0lERU5UX1dJTkRPV19IT1VSUyxcbiAgICAgIGV2ZW50UmV0ZW50aW9uRGF5czogcm93Py5ldmVudF9yZXRlbnRpb25fZGF5cyAhPSBudWxsID8gTnVtYmVyKHJvdy5ldmVudF9yZXRlbnRpb25fZGF5cykgOiBudWxsLFxuICAgICAgZG9yYVJldGVudGlvbkRheXM6IHJvdz8uZG9yYV9yZXRlbnRpb25fZGF5cyAhPSBudWxsID8gTnVtYmVyKHJvdy5kb3JhX3JldGVudGlvbl9kYXlzKSA6IG51bGwsXG4gICAgICBkZWZhdWx0RXZlbnRSZXRlbnRpb25EYXlzOiBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMsXG4gICAgICBkZWZhdWx0RG9yYVJldGVudGlvbkRheXM6IFJFUE9SVElOR19ET1JBX1JFVEVOVElPTl9EQVlTLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogVXBzZXJ0IHBlci1vcmcgcmVwb3J0aW5nIHNldHRpbmdzIChQaGFzZSA1YiBpbmNpZGVudCB3aW5kb3cgKyBQaGFzZSA3XG4gICAqIHJldGVudGlvbiBvdmVycmlkZXMpLCBpZGVtcG90ZW50IG9uIGBvcmdfaWRgLiBBIHBhcnRpYWwgd3JpdGUg4oCUIG9ubHkgdGhlXG4gICAqIGZpZWxkcyBwcmVzZW50IGluIGBwYXRjaGAgYXJlIHNldCAoc28gdXBkYXRpbmcgcmV0ZW50aW9uIG5ldmVyIGNsZWFycyB0aGVcbiAgICogaW5jaWRlbnQgd2luZG93LCBhbmQgdmljZS12ZXJzYSkuIFNldCBzZWxmLXNlcnZlIGJ5IGFuIG9yZyBhZG1pbiB2aWFcbiAgICogYFBVVCAvYXBpL3JlcG9ydHMvc2V0dGluZ3MvaW5jaWRlbnRzYC4gUnVucyB1bmRlciB0aGUgY2FsbGVyJ3Mgb3JnIGNvbnRleHRcbiAgICogKFJMUyBXSVRIIENIRUNLKTsgYSBjaGFuZ2VkIHZhbHVlIGFmZmVjdHMgRE9SQSBhZ2dyZWdhdGVzIHNvIHRoZSBvcmcnc1xuICAgKiBjYWNoZWQgcmVwb3J0cyBhcmUgZHJvcHBlZC5cbiAgICovXG4gIGFzeW5jIHNldFJlcG9ydGluZ1NldHRpbmdzKG9yZ0lkOiBzdHJpbmcsIHBhdGNoOiBSZXBvcnRpbmdTZXR0aW5nc1BhdGNoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgc2V0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHsgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpIH07XG4gICAgaWYgKHBhdGNoLmluY2lkZW50V2luZG93SG91cnMgIT09IHVuZGVmaW5lZCkgc2V0LmluY2lkZW50V2luZG93SG91cnMgPSBwYXRjaC5pbmNpZGVudFdpbmRvd0hvdXJzO1xuICAgIGlmIChwYXRjaC5ldmVudFJldGVudGlvbkRheXMgIT09IHVuZGVmaW5lZCkgc2V0LmV2ZW50UmV0ZW50aW9uRGF5cyA9IHBhdGNoLmV2ZW50UmV0ZW50aW9uRGF5cztcbiAgICBpZiAocGF0Y2guZG9yYVJldGVudGlvbkRheXMgIT09IHVuZGVmaW5lZCkgc2V0LmRvcmFSZXRlbnRpb25EYXlzID0gcGF0Y2guZG9yYVJldGVudGlvbkRheXM7XG4gICAgYXdhaXQgcnVuV2l0aFRlbmFudENvbnRleHQoeyBvcmdJZCwgaXNTdXBlckFkbWluOiBmYWxzZSB9LCAoKSA9PlxuICAgICAgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguaW5zZXJ0KHNjaGVtYS5kb3JhU2V0dGluZ3MpLnZhbHVlcyh7XG4gICAgICAgIG9yZ0lkLFxuICAgICAgICBpbmNpZGVudFdpbmRvd0hvdXJzOiBwYXRjaC5pbmNpZGVudFdpbmRvd0hvdXJzLFxuICAgICAgICBldmVudFJldGVudGlvbkRheXM6IHBhdGNoLmV2ZW50UmV0ZW50aW9uRGF5cyxcbiAgICAgICAgZG9yYVJldGVudGlvbkRheXM6IHBhdGNoLmRvcmFSZXRlbnRpb25EYXlzLFxuICAgICAgICB1cGRhdGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICB9KS5vbkNvbmZsaWN0RG9VcGRhdGUoe1xuICAgICAgICB0YXJnZXQ6IHNjaGVtYS5kb3JhU2V0dGluZ3Mub3JnSWQsXG4gICAgICAgIHNldCxcbiAgICAgIH0pKSxcbiAgICApO1xuICAgIGF3YWl0IHRoaXMuaW52YWxpZGF0ZU9yZyhvcmdJZCkuY2F0Y2goKGVycikgPT4ge1xuICAgICAgbG9nZ2VyLndhcm4oJ1JlcG9ydGluZyBjYWNoZSBpbnZhbGlkYXRpb24gZmFpbGVkJywgeyBvcmdJZCwgZXJyb3I6IGVycm9yTWVzc2FnZShlcnIpIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIExpc3QgcmVjZW50IGluY2lkZW50cyBmb3IgYW4gb3JnIChQaGFzZSA1YiBvcmctYWRtaW4gc3VyZmFjZSksIG5ld2VzdCBmaXJzdCxcbiAgICogcGFnaW5hdGVkLiBFYWNoIHJvdyBjYXJyaWVzIGl0cyByZXNvbHZlZCBzdGF0ZSBhbmQgaXRzIGRlcGxveSBjb3JyZWxhdGlvbiDigJRcbiAgICogdGhlIG1vc3QgcmVjZW50IFNVQ0NFU1NGVUwgZGVwbG95IHRvIHRoZSBpbmNpZGVudCdzIGBlbnZpcm9ubWVudGAgd2hvc2VcbiAgICogYGNvbXBsZXRlZF9hdGAgZmFsbHMgd2l0aGluIHRoZSBlZmZlY3RpdmUgcGVyLW9yZyBjb3JyZWxhdGlvbiB3aW5kb3cgYmVmb3JlXG4gICAqIGBvcGVuZWRfYXRgICh0aGUgc2FtZSBydWxlIERPUkEncyBDRlIvTVRUUiBjb3JyZWxhdGlvbiBhcHBsaWVzKS4gU2luZ2xlLW9yZ1xuICAgKiAobm8gcm9sbHVwKSDigJQgYW4gb3JnIGFkbWluIHZpZXdzIHRoZWlyIG93biBvcmcncyBpbmNpZGVudHMuIFJMUy1zY29wZWQgdmlhXG4gICAqIHRoZSBgcC5vcmdfaWRgL2BpLm9yZ19pZGAgcHJlZGljYXRlcyArIHRoZSB0ZW5hbnQgY29udGV4dC5cbiAgICovXG4gIGFzeW5jIGxpc3RJbmNpZGVudHMob3JnSWQ6IHN0cmluZywgb3B0czogeyBsaW1pdDogbnVtYmVyOyBvZmZzZXQ6IG51bWJlciB9KTogUHJvbWlzZTxJbmNpZGVudExpc3RJdGVtW10+IHtcbiAgICBjb25zdCB7IGluY2lkZW50V2luZG93SG91cnMgfSA9IGF3YWl0IHRoaXMuZ2V0SW5jaWRlbnRTZXR0aW5ncyhvcmdJZCk7XG4gICAgY29uc3Qgd2luZG93SG91cnMgPSByZXNvbHZlSW5jaWRlbnRXaW5kb3dIb3VycyhpbmNpZGVudFdpbmRvd0hvdXJzKTtcbiAgICBjb25zdCBsaW1pdCA9IE1hdGgubWF4KDEsIE1hdGgubWluKG9wdHMubGltaXQsIDIwMCkpO1xuICAgIGNvbnN0IG9mZnNldCA9IE1hdGgubWF4KDAsIG9wdHMub2Zmc2V0KTtcbiAgICByZXR1cm4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShzcWxgXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIGkuaW5jaWRlbnRfaWQgQVMgXCJpbmNpZGVudElkXCIsXG4gICAgICAgICAgaS5lbnZpcm9ubWVudCBBUyBlbnZpcm9ubWVudCxcbiAgICAgICAgICBpLnNldmVyaXR5IEFTIHNldmVyaXR5LFxuICAgICAgICAgIGkub3BlbmVkX2F0Ojp0ZXh0IEFTIFwib3BlbmVkQXRcIixcbiAgICAgICAgICBpLnJlc29sdmVkX2F0Ojp0ZXh0IEFTIFwicmVzb2x2ZWRBdFwiLFxuICAgICAgICAgIGkuY3JlYXRlZF9hdDo6dGV4dCBBUyBcImNyZWF0ZWRBdFwiLFxuICAgICAgICAgIChpLnJlc29sdmVkX2F0IElTIE5PVCBOVUxMKSBBUyByZXNvbHZlZCxcbiAgICAgICAgICBjb3JyLmV4ZWN1dGlvbl9pZCBBUyBcImNvcnJlbGF0ZWRFeGVjdXRpb25JZFwiLFxuICAgICAgICAgIGNvcnIuY29tcGxldGVkX2F0Ojp0ZXh0IEFTIFwiZGVwbG95Q29tcGxldGVkQXRcIlxuICAgICAgICBGUk9NICR7c2NoZW1hLmluY2lkZW50fSBpXG4gICAgICAgIExFRlQgSk9JTiBMQVRFUkFMIChcbiAgICAgICAgICBTRUxFQ1QgZS5leGVjdXRpb25faWQsIGUuY29tcGxldGVkX2F0XG4gICAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgICAgSk9JTiAke3NjaGVtYS5waXBlbGluZX0gcCBPTiBwLmlkID0gZS5waXBlbGluZV9pZFxuICAgICAgICAgIFdIRVJFIHAub3JnX2lkID0gJHtvcmdJZH0gQU5EIGUuZXZlbnRfdHlwZSA9ICdTVEFHRSdcbiAgICAgICAgICAgIEFORCBlLmVudmlyb25tZW50ID0gaS5lbnZpcm9ubWVudCBBTkQgZS5zdGF0dXMgPSAnU1VDQ0VFREVEJ1xuICAgICAgICAgICAgQU5EIGUuY29tcGxldGVkX2F0IDw9IGkub3BlbmVkX2F0XG4gICAgICAgICAgICBBTkQgZS5jb21wbGV0ZWRfYXQgPj0gaS5vcGVuZWRfYXQgLSBtYWtlX2ludGVydmFsKGhvdXJzID0+ICR7d2luZG93SG91cnN9OjppbnQpXG4gICAgICAgICAgT1JERVIgQlkgZS5jb21wbGV0ZWRfYXQgREVTQ1xuICAgICAgICAgIExJTUlUIDFcbiAgICAgICAgKSBjb3JyIE9OIHRydWVcbiAgICAgICAgV0hFUkUgaS5vcmdfaWQgPSAke29yZ0lkfVxuICAgICAgICBPUkRFUiBCWSBpLm9wZW5lZF9hdCBERVNDXG4gICAgICAgIExJTUlUICR7bGltaXR9IE9GRlNFVCAke29mZnNldH1cbiAgICAgIGApLnRoZW4oKHIpID0+IGRyaXp6bGVSb3dzPEluY2lkZW50TGlzdEl0ZW0+KHIucm93cykubWFwKChyb3cpID0+ICh7XG4gICAgICAuLi5yb3csXG4gICAgICByZXNvbHZlZDogcm93LnJlc29sdmVkID09PSB0cnVlIHx8IChyb3cucmVzb2x2ZWQgYXMgdW5rbm93bikgPT09ICd0JyxcbiAgICAgIGNvcnJlbGF0ZWRFeGVjdXRpb25JZDogcm93LmNvcnJlbGF0ZWRFeGVjdXRpb25JZCA/PyBudWxsLFxuICAgICAgZGVwbG95Q29tcGxldGVkQXQ6IHJvdy5kZXBsb3lDb21wbGV0ZWRBdCA/PyBudWxsLFxuICAgIH0pKSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFdpcmluZy10ZXN0IGRyeS1ydW4gKFBoYXNlIDViKTogd291bGQgYSBzeW50aGV0aWMgaW5jaWRlbnQgb3BlbmluZyBOT1cgZm9yXG4gICAqIGBlbnZpcm9ubWVudGAgY29ycmVsYXRlIHRvIGEgcmVjZW50IHN1Y2Nlc3NmdWwgZGVwbG95IHVuZGVyIHRoZSBvcmcnc1xuICAgKiBlZmZlY3RpdmUgd2luZG93PyBUaGlzIGlzIGEgTk9OLXBlcnNpc3RpbmcgY29ycmVsYXRpb24gY2hlY2sg4oCUIGl0IHZlcmlmaWVzXG4gICAqIHRoZSBhZG1pbidzIGVudmlyb25tZW50IG5hbWluZyArIHdpbmRvdyBsaW5lIHVwIHdpdGggcmVhbCBkZXBsb3kgZXZlbnRzXG4gICAqIFdJVEhPVVQgd3JpdGluZyBhbiBpbmNpZGVudCAoc28gYSBcInRlc3RcIiBuZXZlciBwb2xsdXRlcyBDRlIvTVRUUikuIFJMUy1zY29wZWQuXG4gICAqL1xuICBhc3luYyB0ZXN0SW5jaWRlbnRDb3JyZWxhdGlvbihvcmdJZDogc3RyaW5nLCBlbnZpcm9ubWVudDogc3RyaW5nKTogUHJvbWlzZTxJbmNpZGVudFRlc3RSZXN1bHQ+IHtcbiAgICBjb25zdCBlbnYgPSBzY3J1YkF3c0lkZW50aWZpZXJzRnJvbVN0cmluZyhlbnZpcm9ubWVudCk7XG4gICAgY29uc3Qgb3BlbmVkQXQgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gICAgY29uc3QgeyBpbmNpZGVudFdpbmRvd0hvdXJzIH0gPSBhd2FpdCB0aGlzLmdldEluY2lkZW50U2V0dGluZ3Mob3JnSWQpO1xuICAgIGNvbnN0IHdpbmRvd0hvdXJzID0gcmVzb2x2ZUluY2lkZW50V2luZG93SG91cnMoaW5jaWRlbnRXaW5kb3dIb3Vycyk7XG4gICAgY29uc3Qgcm93cyA9IGRyaXp6bGVSb3dzPHsgZXhlY3V0aW9uX2lkOiBzdHJpbmcgfCBudWxsOyBjb21wbGV0ZWRfYXQ6IHN0cmluZyB8IG51bGwgfT4oKGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1QgZS5leGVjdXRpb25faWQgQVMgZXhlY3V0aW9uX2lkLCBlLmNvbXBsZXRlZF9hdDo6dGV4dCBBUyBjb21wbGV0ZWRfYXRcbiAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgIEpPSU4gJHtzY2hlbWEucGlwZWxpbmV9IHAgT04gcC5pZCA9IGUucGlwZWxpbmVfaWRcbiAgICAgICAgV0hFUkUgcC5vcmdfaWQgPSAke29yZ0lkfSBBTkQgZS5ldmVudF90eXBlID0gJ1NUQUdFJ1xuICAgICAgICAgIEFORCBlLmVudmlyb25tZW50ID0gJHtlbnZ9IEFORCBlLnN0YXR1cyA9ICdTVUNDRUVERUQnXG4gICAgICAgICAgQU5EIGUuY29tcGxldGVkX2F0IDw9ICR7b3BlbmVkQXR9Ojp0aW1lc3RhbXB0elxuICAgICAgICAgIEFORCBlLmNvbXBsZXRlZF9hdCA+PSAke29wZW5lZEF0fTo6dGltZXN0YW1wdHogLSBtYWtlX2ludGVydmFsKGhvdXJzID0+ICR7d2luZG93SG91cnN9OjppbnQpXG4gICAgICAgIE9SREVSIEJZIGUuY29tcGxldGVkX2F0IERFU0NcbiAgICAgICAgTElNSVQgMVxuICAgICAgYCkpKS5yb3dzKTtcbiAgICBjb25zdCBoaXQgPSByb3dzWzBdO1xuICAgIHJldHVybiB7XG4gICAgICBlbnZpcm9ubWVudDogZW52LFxuICAgICAgb3BlbmVkQXQsXG4gICAgICB3aW5kb3dIb3VycyxcbiAgICAgIGNvcnJlbGF0ZWQ6IGhpdD8uZXhlY3V0aW9uX2lkICE9IG51bGwsXG4gICAgICBleGVjdXRpb25JZDogaGl0Py5leGVjdXRpb25faWQgPz8gbnVsbCxcbiAgICAgIGRlcGxveUNvbXBsZXRlZEF0OiBoaXQ/LmNvbXBsZXRlZF9hdCA/PyBudWxsLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogVXBzZXJ0IHBlci1vcmcgaW5nZXN0aW9uIGhlYWx0aCAoUGhhc2UgMyk6IHRoZSBBV1MgZXZlbnRzIExhbWJkYSBwZXJpb2RpY2FsbHlcbiAgICogcmVwb3J0cyBmb3J3YXJkZWQvZHJvcHBlZCBjb3VudGVycyArIHRoZSBsYXN0IGV2ZW50IHRpbWVzdGFtcCBzbyB0aGUgUmVwb3J0c1xuICAgKiBVSSBjYW4gc2hvdyBmbG93aW5nIC8gc3RhbGUgLyBkcm9wcGluZy4gT25lIHJvdyBwZXIgb3JnICh1cHNlcnQgb24gb3JnX2lkKS5cbiAgICovXG4gIGFzeW5jIHJlY29yZEluZ2VzdEhlYWx0aChcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIGlucHV0OiB7IGZvcndhcmRlZD86IG51bWJlcjsgZHJvcHBlZD86IG51bWJlcjsgbGFzdEV2ZW50QXQ/OiBzdHJpbmcgfSxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbGFzdEV2ZW50QXQgPSBpbnB1dC5sYXN0RXZlbnRBdCA/IG5ldyBEYXRlKGlucHV0Lmxhc3RFdmVudEF0KSA6IHVuZGVmaW5lZDtcbiAgICBhd2FpdCBydW5XaXRoVGVuYW50Q29udGV4dCh7IG9yZ0lkLCBpc1N1cGVyQWRtaW46IGZhbHNlIH0sICgpID0+XG4gICAgICB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5pbnNlcnQoc2NoZW1hLmluZ2VzdEhlYWx0aCkudmFsdWVzKHtcbiAgICAgICAgb3JnSWQsXG4gICAgICAgIGZvcndhcmRlZDogaW5wdXQuZm9yd2FyZGVkLFxuICAgICAgICBkcm9wcGVkOiBpbnB1dC5kcm9wcGVkLFxuICAgICAgICBsYXN0RXZlbnRBdCxcbiAgICAgICAgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpLFxuICAgICAgfSkub25Db25mbGljdERvVXBkYXRlKHtcbiAgICAgICAgdGFyZ2V0OiBzY2hlbWEuaW5nZXN0SGVhbHRoLm9yZ0lkLFxuICAgICAgICBzZXQ6IHtcbiAgICAgICAgICBmb3J3YXJkZWQ6IGlucHV0LmZvcndhcmRlZCxcbiAgICAgICAgICBkcm9wcGVkOiBpbnB1dC5kcm9wcGVkLFxuICAgICAgICAgIGxhc3RFdmVudEF0LFxuICAgICAgICAgIHVwZGF0ZWRBdDogbmV3IERhdGUoKSxcbiAgICAgICAgfSxcbiAgICAgIH0pKSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEJhdGNoZWQgaGFyZC1ERUxFVEUgb2Ygb25lIHJlcG9ydGluZyB0YWJsZSdzIHJvd3MgZXhwaXJlZCBwYXN0IGEgYGNyZWF0ZWRfYXRgXG4gICAqIGN1dG9mZiwgc2NvcGVkIHRvIGFuIG9yZyBhbmQgYW4gb3B0aW9uYWwgcm93IHByZWRpY2F0ZSAoUGhhc2UgNykuIFVzZXMgYVxuICAgKiBgY3RpZGAtaW4tc3VicXVlcnkgTElNSVQgc28gZWFjaCBzdGF0ZW1lbnQgdG91Y2hlcyBhdCBtb3N0IGBiYXRjaFNpemVgIHJvd3NcbiAgICogKHNob3J0IGxvY2sgd2luZG93cywgbm8gbG9uZyB0YWJsZSBzY2FuIHVuZGVyIGxvY2spOyBgUkVUVVJOSU5HIDFgIGxldHMgdXNcbiAgICogY291bnQgdmlhIGAucm93cy5sZW5ndGhgIHdpdGhvdXQgZGVwZW5kaW5nIG9uIHRoZSBkcml2ZXIncyBgcm93Q291bnRgLiBMb29wc1xuICAgKiB1bnRpbCBhIHNob3J0IGJhdGNoIChkcmFpbmVkKSBvciB0aGUgcGVyLXRpY2sgY2FwLCB0aGVuIGRlZmVycyB0aGUgcmVzdC5cbiAgICogTVVTVCBydW4gaW5zaWRlIGEgc3lzYWRtaW4gdGVuYW50IHNjb3BlIChzZWUgcHVyZ2VFeHBpcmVkUmVwb3J0aW5nRGF0YSkgc28gaXRcbiAgICogYnlwYXNzZXMgUkxTIGFuZCBjYW4gZGVsZXRlIGFjcm9zcyB0aGUgb3JnJ3Mgcm93cy5cbiAgICovXG4gIGFzeW5jICNwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICB0YWJsZVNxbDogUmV0dXJuVHlwZTx0eXBlb2Ygc3FsPixcbiAgICBwcmVkaWNhdGU6IFJldHVyblR5cGU8dHlwZW9mIHNxbD4sXG4gICAgYmF0Y2hTaXplOiBudW1iZXIsXG4gICAgbWF4QmF0Y2hlczogbnVtYmVyLFxuICApOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGxldCB0b3RhbCA9IDA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtYXhCYXRjaGVzOyBpKyspIHtcbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBERUxFVEUgRlJPTSAke3RhYmxlU3FsfVxuICAgICAgICBXSEVSRSBjdGlkIElOIChcbiAgICAgICAgICBTRUxFQ1QgY3RpZCBGUk9NICR7dGFibGVTcWx9XG4gICAgICAgICAgV0hFUkUgJHtwcmVkaWNhdGV9XG4gICAgICAgICAgTElNSVQgJHtiYXRjaFNpemV9XG4gICAgICAgIClcbiAgICAgICAgUkVUVVJOSU5HIDFcbiAgICAgIGApKTtcbiAgICAgIGNvbnN0IHB1cmdlZCA9IGRyaXp6bGVSb3dzPHVua25vd24+KHJlcy5yb3dzKS5sZW5ndGg7XG4gICAgICB0b3RhbCArPSBwdXJnZWQ7XG4gICAgICBpZiAocHVyZ2VkIDwgYmF0Y2hTaXplKSBicmVhazsgLy8gZHJhaW5lZFxuICAgICAgaWYgKGkgPT09IG1heEJhdGNoZXMgLSAxKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdSZXBvcnRpbmcgcmV0ZW50aW9uIGhpdCBwZXItdGljayBjYXA7IHJlbWFpbmluZyByb3dzIGRlZmVycmVkJywge1xuICAgICAgICAgIHB1cmdlZFRoaXNUaWNrOiB0b3RhbCxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0b3RhbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBvcnRpbmcgcmV0ZW50aW9uIHN3ZWVwIChQaGFzZSA3KS4gSGFyZC1kZWxldGVzIHJvd3Mgb2xkZXIgdGhhbiB0aGVpclxuICAgKiByZXRlbnRpb24gd2luZG93LCBieSBgY3JlYXRlZF9hdGAsIGFjcm9zcyBldmVyeSBvcmcgdGhhdCBoYXMgcmVwb3J0aW5nIGRhdGEg4oCUXG4gICAqIGEgKipzcGxpdCoqIHBvbGljeSBzbyBoaWdoLXZvbHVtZSBzdGFuZGFyZCBldmVudHMgZXhwaXJlIGZhc3RlciB0aGFuIHRoZVxuICAgKiBsb3ctdm9sdW1lIERPUkEgc291cmNlOlxuICAgKiAgLSBgcGlwZWxpbmVfZXZlbnRzIFdIRVJFIGVudmlyb25tZW50IElTIE5VTExgIOKGkiBzdGFuZGFyZC1ldmVudCB3aW5kb3cuXG4gICAqICAtIGBwaXBlbGluZV9ldmVudHMgV0hFUkUgZW52aXJvbm1lbnQgSVMgTk9UIE5VTExgIChkZXBsb3kgc3RhZ2VzKSxcbiAgICogICAgYGRlcGxveW1lbnRfb3V0Y29tZXNgLCBhbmQgYGluY2lkZW50c2Ag4oaSIERPUkEtc291cmNlIHdpbmRvdy5cbiAgICogRWFjaCBvcmcncyB3aW5kb3dzIGNvbWUgZnJvbSBpdHMgYGRvcmFfc2V0dGluZ3NgIG92ZXJyaWRlLCBlbHNlIHRoZSBnbG9iYWxcbiAgICogZW52IGRlZmF1bHRzIChzZWUge0BsaW5rIHJlc29sdmVFdmVudFJldGVudGlvbkRheXN9IC8ge0BsaW5rIHJlc29sdmVEb3JhUmV0ZW50aW9uRGF5c30pLlxuICAgKiBgaW5nZXN0X2hlYWx0aGAgYW5kIGBkb3JhX3NldHRpbmdzYCBhcmUgbmV2ZXIgcHVyZ2VkLiBPbmUgYG5vd2AgZm9yIHRoZSB3aG9sZVxuICAgKiB0aWNrIChyb3dzIGNyb3NzaW5nIHRoZSBib3VuZGFyeSBtaWQtc3dlZXAgd2FpdCBmb3IgdGhlIG5leHQpLiBFc3RhYmxpc2hlcyBhXG4gICAqIHN5c2FkbWluIHRlbmFudCBzY29wZSBzbyB0aGUgZGVsZXRlcyBzcGFuIGFsbCBvcmdzIC8gYnlwYXNzIFJMUyDigJQgdGhpcyBpcyBhXG4gICAqIGNyb3NzLXRlbmFudCBob3VzZWtlZXBpbmcgam9iLiBSZXR1cm5zIChhbmQgbG9ncykgcGVyLXdpbmRvdyBwdXJnZSB0YWxsaWVzLlxuICAgKi9cbiAgYXN5bmMgcHVyZ2VFeHBpcmVkUmVwb3J0aW5nRGF0YShvcHRzOiBSZXBvcnRpbmdSZXRlbnRpb25PcHRpb25zID0ge30pOiBQcm9taXNlPFJlcG9ydGluZ1JldGVudGlvbkNvdW50cz4ge1xuICAgIGNvbnN0IGJhdGNoU2l6ZSA9IE1hdGgubWF4KDEsIG9wdHMuYmF0Y2hTaXplID8/IDEwMDApO1xuICAgIGNvbnN0IG1heEJhdGNoZXMgPSBNYXRoLm1heCgxLCBvcHRzLm1heEJhdGNoZXNQZXJUYWJsZSA/PyA1MCk7XG4gICAgY29uc3Qgbm93ID0gb3B0cy5ub3cgPz8gbmV3IERhdGUoKTtcbiAgICBjb25zdCBjb3VudHM6IFJlcG9ydGluZ1JldGVudGlvbkNvdW50cyA9IHtcbiAgICAgIG9yZ3M6IDAsIHN0YW5kYXJkRXZlbnRzOiAwLCBkb3JhRXZlbnRzOiAwLCBkZXBsb3ltZW50T3V0Y29tZXM6IDAsIGluY2lkZW50czogMCxcbiAgICB9O1xuXG4gICAgcmV0dXJuIHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgaXNTdXBlckFkbWluOiB0cnVlIH0sIGFzeW5jICgpID0+IHtcbiAgICAgIC8vIEVudW1lcmF0ZSBldmVyeSBvcmcgd2l0aCByZXBvcnRpbmcgZGF0YSAodW5pb24gYWNyb3NzIHRoZSB0aHJlZSB0YWJsZXMpLlxuICAgICAgY29uc3Qgb3JnUm93cyA9IGRyaXp6bGVSb3dzPHsgb3JnX2lkOiBzdHJpbmcgfT4oKGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1QgRElTVElOQ1Qgb3JnX2lkIEZST00gKFxuICAgICAgICAgIFNFTEVDVCAke3NjaGVtYS5waXBlbGluZUV2ZW50Lm9yZ0lkfSBBUyBvcmdfaWQgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fVxuICAgICAgICAgIFVOSU9OXG4gICAgICAgICAgU0VMRUNUICR7c2NoZW1hLmRlcGxveW1lbnRPdXRjb21lLm9yZ0lkfSBBUyBvcmdfaWQgRlJPTSAke3NjaGVtYS5kZXBsb3ltZW50T3V0Y29tZX1cbiAgICAgICAgICBVTklPTlxuICAgICAgICAgIFNFTEVDVCAke3NjaGVtYS5pbmNpZGVudC5vcmdJZH0gQVMgb3JnX2lkIEZST00gJHtzY2hlbWEuaW5jaWRlbnR9XG4gICAgICAgICkgQVMgb3Jnc1xuICAgICAgYCkpKS5yb3dzKTtcbiAgICAgIGlmIChvcmdSb3dzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGNvdW50cztcblxuICAgICAgLy8gUGVyLW9yZyByZXRlbnRpb24gb3ZlcnJpZGVzIChhIHNpbmdsZSByZWFkOyBvcmdzIHdpdGhvdXQgYSByb3cgdXNlIGRlZmF1bHRzKS5cbiAgICAgIGNvbnN0IG92ZXJyaWRlUm93cyA9IGRyaXp6bGVSb3dzPHtcbiAgICAgICAgb3JnX2lkOiBzdHJpbmc7XG4gICAgICAgIGV2ZW50X3JldGVudGlvbl9kYXlzOiBudW1iZXIgfCBudWxsO1xuICAgICAgICBkb3JhX3JldGVudGlvbl9kYXlzOiBudW1iZXIgfCBudWxsO1xuICAgICAgfT4oKGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1QgJHtzY2hlbWEuZG9yYVNldHRpbmdzLm9yZ0lkfSBBUyBvcmdfaWQsXG4gICAgICAgICAgICAgICAke3NjaGVtYS5kb3JhU2V0dGluZ3MuZXZlbnRSZXRlbnRpb25EYXlzfSBBUyBldmVudF9yZXRlbnRpb25fZGF5cyxcbiAgICAgICAgICAgICAgICR7c2NoZW1hLmRvcmFTZXR0aW5ncy5kb3JhUmV0ZW50aW9uRGF5c30gQVMgZG9yYV9yZXRlbnRpb25fZGF5c1xuICAgICAgICBGUk9NICR7c2NoZW1hLmRvcmFTZXR0aW5nc31cbiAgICAgIGApKSkucm93cyk7XG4gICAgICBjb25zdCBvdmVycmlkZXMgPSBuZXcgTWFwKG92ZXJyaWRlUm93cy5tYXAoKHIpID0+IFtyLm9yZ19pZCwgcl0pKTtcblxuICAgICAgY29uc3QgZXZlbnRzVGFibGUgPSBzcWxgJHtzY2hlbWEucGlwZWxpbmVFdmVudH1gO1xuICAgICAgY29uc3Qgb3V0Y29tZXNUYWJsZSA9IHNxbGAke3NjaGVtYS5kZXBsb3ltZW50T3V0Y29tZX1gO1xuICAgICAgY29uc3QgaW5jaWRlbnRzVGFibGUgPSBzcWxgJHtzY2hlbWEuaW5jaWRlbnR9YDtcblxuICAgICAgZm9yIChjb25zdCB7IG9yZ19pZDogb3JnSWQgfSBvZiBvcmdSb3dzKSB7XG4gICAgICAgIGNvbnN0IG92ID0gb3ZlcnJpZGVzLmdldChvcmdJZCk7XG4gICAgICAgIGNvbnN0IGV2ZW50RGF5cyA9IHJlc29sdmVFdmVudFJldGVudGlvbkRheXMob3Y/LmV2ZW50X3JldGVudGlvbl9kYXlzKTtcbiAgICAgICAgY29uc3QgZG9yYURheXMgPSByZXNvbHZlRG9yYVJldGVudGlvbkRheXMob3Y/LmRvcmFfcmV0ZW50aW9uX2RheXMpO1xuICAgICAgICAvLyBgLTFgID0gdW5saW1pdGVkIChQaGFzZSA4KToga2VlcCBmb3JldmVyLCBza2lwIHRoYXQgd2luZG93J3MgZGVsZXRlcyBmb3JcbiAgICAgICAgLy8gdGhpcyBvcmcuIFN0YW5kYXJkLWV2ZW50IGFuZCBET1JBLXNvdXJjZSB3aW5kb3dzIGFyZSBpbmRlcGVuZGVudDsgYW4gb3JnXG4gICAgICAgIC8vIHdpdGggYm90aCBgLTFgIGlzIGZ1bGx5IHNraXBwZWQuIExvZyB0aGUgc2tpcCBzbyBpdCdzIG9ic2VydmFibGUuXG4gICAgICAgIGNvbnN0IHNraXBFdmVudHMgPSBldmVudERheXMgPT09IC0xO1xuICAgICAgICBjb25zdCBza2lwRG9yYSA9IGRvcmFEYXlzID09PSAtMTtcbiAgICAgICAgaWYgKHNraXBFdmVudHMgfHwgc2tpcERvcmEpIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbygnUmVwb3J0aW5nIHJldGVudGlvbiBzd2VlcCBza2lwcGluZyB1bmxpbWl0ZWQgd2luZG93KHMpJywge1xuICAgICAgICAgICAgb3JnSWQsXG4gICAgICAgICAgICBzdGFuZGFyZEV2ZW50czogc2tpcEV2ZW50cyA/ICd1bmxpbWl0ZWQnIDogZXZlbnREYXlzLFxuICAgICAgICAgICAgZG9yYVNvdXJjZTogc2tpcERvcmEgPyAndW5saW1pdGVkJyA6IGRvcmFEYXlzLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFza2lwRXZlbnRzKSB7XG4gICAgICAgICAgY29uc3QgZXZlbnRDdXRvZmYgPSByZXRlbnRpb25DdXRvZmYobm93LCBldmVudERheXMpO1xuICAgICAgICAgIGNvdW50cy5zdGFuZGFyZEV2ZW50cyArPSBhd2FpdCB0aGlzLiNwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICAgIGV2ZW50c1RhYmxlLFxuICAgICAgICAgICAgc3FsYG9yZ19pZCA9ICR7b3JnSWR9IEFORCBlbnZpcm9ubWVudCBJUyBOVUxMIEFORCBjcmVhdGVkX2F0IDwgJHtldmVudEN1dG9mZn1gLFxuICAgICAgICAgICAgYmF0Y2hTaXplLCBtYXhCYXRjaGVzLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXNraXBEb3JhKSB7XG4gICAgICAgICAgY29uc3QgZG9yYUN1dG9mZiA9IHJldGVudGlvbkN1dG9mZihub3csIGRvcmFEYXlzKTtcbiAgICAgICAgICBjb3VudHMuZG9yYUV2ZW50cyArPSBhd2FpdCB0aGlzLiNwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICAgIGV2ZW50c1RhYmxlLFxuICAgICAgICAgICAgc3FsYG9yZ19pZCA9ICR7b3JnSWR9IEFORCBlbnZpcm9ubWVudCBJUyBOT1QgTlVMTCBBTkQgY3JlYXRlZF9hdCA8ICR7ZG9yYUN1dG9mZn1gLFxuICAgICAgICAgICAgYmF0Y2hTaXplLCBtYXhCYXRjaGVzLFxuICAgICAgICAgICk7XG4gICAgICAgICAgY291bnRzLmRlcGxveW1lbnRPdXRjb21lcyArPSBhd2FpdCB0aGlzLiNwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICAgIG91dGNvbWVzVGFibGUsXG4gICAgICAgICAgICBzcWxgb3JnX2lkID0gJHtvcmdJZH0gQU5EIGNyZWF0ZWRfYXQgPCAke2RvcmFDdXRvZmZ9YCxcbiAgICAgICAgICAgIGJhdGNoU2l6ZSwgbWF4QmF0Y2hlcyxcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvdW50cy5pbmNpZGVudHMgKz0gYXdhaXQgdGhpcy4jcHVyZ2VSZXBvcnRpbmdUYWJsZUJhdGNoZWQoXG4gICAgICAgICAgICBpbmNpZGVudHNUYWJsZSxcbiAgICAgICAgICAgIHNxbGBvcmdfaWQgPSAke29yZ0lkfSBBTkQgY3JlYXRlZF9hdCA8ICR7ZG9yYUN1dG9mZn1gLFxuICAgICAgICAgICAgYmF0Y2hTaXplLCBtYXhCYXRjaGVzLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgY291bnRzLm9yZ3MgKz0gMTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcHVyZ2VkQW55ID0gY291bnRzLnN0YW5kYXJkRXZlbnRzICsgY291bnRzLmRvcmFFdmVudHMgKyBjb3VudHMuZGVwbG95bWVudE91dGNvbWVzICsgY291bnRzLmluY2lkZW50cztcbiAgICAgIGlmIChwdXJnZWRBbnkgPiAwKSBsb2dnZXIuaW5mbygnUmVwb3J0aW5nIHJldGVudGlvbiBzd2VlcCBwdXJnZWQgZXhwaXJlZCByb3dzJywgeyAuLi5jb3VudHMgfSk7XG4gICAgICByZXR1cm4gY291bnRzO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjb25zdCByZXBvcnRpbmdTZXJ2aWNlID0gbmV3IFJlcG9ydGluZ1NlcnZpY2UoKTtcbiJdfQ==