@pipeline-builder/pipeline-events 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.
- package/LICENSE +202 -0
- package/README.md +126 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +834 -0
- package/package.json +159 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { GetSecretValueCommand, SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
|
|
4
|
+
let codepipelineMod;
|
|
5
|
+
async function loadCodePipeline() {
|
|
6
|
+
if (!codepipelineMod)
|
|
7
|
+
codepipelineMod = (await import('@aws-sdk/client-codepipeline'));
|
|
8
|
+
return codepipelineMod;
|
|
9
|
+
}
|
|
10
|
+
// Variable-specifier loader for SDK clients that are NOT static devDependencies of
|
|
11
|
+
// this package (present only in the Lambda runtime / jest mocks). Passing the
|
|
12
|
+
// specifier as a `string` keeps `tsc` from resolving the module at build time.
|
|
13
|
+
async function loadSdk(pkg) {
|
|
14
|
+
return (await import(pkg));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Pipeline event ingestion Lambda handler.
|
|
18
|
+
*
|
|
19
|
+
* Receives CodePipeline/CodeBuild events from SQS (sourced by EventBridge),
|
|
20
|
+
* parses them into a normalized format, and POSTs them to the reporting service
|
|
21
|
+
* via PLATFORM_BASE_URL.
|
|
22
|
+
*
|
|
23
|
+
* Authentication:
|
|
24
|
+
* - PLATFORM_TOKEN env var (preferred — no Secrets Manager call)
|
|
25
|
+
* - PLATFORM_SECRET_NAME env var → reads the JWT (password) from Secrets Manager
|
|
26
|
+
*
|
|
27
|
+
* Environment variables:
|
|
28
|
+
* - PLATFORM_BASE_URL — Base URL of the platform
|
|
29
|
+
* - PLATFORM_TOKEN — JWT token (set directly, or)
|
|
30
|
+
* - PLATFORM_SECRET_NAME — Secrets Manager secret containing { password: <JWT> }
|
|
31
|
+
* - EVENT_DLQ_ARN — (optional) override for the dead-letter queue ARN used by the
|
|
32
|
+
* self-healing redrive; defaults to `<main-queue-arn>-dlq` derived from the SQS
|
|
33
|
+
* trigger's eventSourceARN.
|
|
34
|
+
*/
|
|
35
|
+
const log = {
|
|
36
|
+
info: (msg, data) => console.log(JSON.stringify({ level: 'INFO', message: msg, data, ts: new Date().toISOString() })),
|
|
37
|
+
warn: (msg, data) => console.log(JSON.stringify({ level: 'WARN', message: msg, data, ts: new Date().toISOString() })),
|
|
38
|
+
error: (msg, data) => console.error(JSON.stringify({ level: 'ERROR', message: msg, data, ts: new Date().toISOString() })),
|
|
39
|
+
};
|
|
40
|
+
// ── Tag resolution: pb.pipeline-id + pb.deploys ──────────
|
|
41
|
+
// Pipelines are tagged at CDK synth with:
|
|
42
|
+
// pb.pipeline-id = <platform pipelineId>
|
|
43
|
+
// pb.deploys = <stage>:<env> pairs joined by '+', e.g.
|
|
44
|
+
// "Deploy-stg:staging+Deploy-prod:production"
|
|
45
|
+
// We resolve those tags from the live CodePipeline (region-aware). The ARN/account
|
|
46
|
+
// never leave AWS, so there's no masking. Cached per ARN (warm-container) to avoid
|
|
47
|
+
// hammering the ListTags API.
|
|
48
|
+
//
|
|
49
|
+
// Requires the Lambda execution role to allow `codepipeline:ListTagsForResource`.
|
|
50
|
+
// AccessDenied is a config error (alert loudly); a missing pb.pipeline-id tag is
|
|
51
|
+
// treated as "not yet registered" (skip).
|
|
52
|
+
const PIPELINE_ID_TAG = 'pb.pipeline-id';
|
|
53
|
+
const DEPLOYS_TAG = 'pb.deploys';
|
|
54
|
+
// Canonical per-pipeline org tag (applied at synth by pipeline-core). It is the
|
|
55
|
+
// RESOLVED org for every event of this pipeline — used to key the per-org GitHub
|
|
56
|
+
// token and to attribute ingest-health. orgId is a platform id, never an AWS
|
|
57
|
+
// account id, so it is safe to read/forward.
|
|
58
|
+
const ORG_ID_TAG = 'OrgId';
|
|
59
|
+
const clientsByRegion = new Map();
|
|
60
|
+
// pipelineId=null is a negative cache (resolved-but-untagged); `ts` lets negatives
|
|
61
|
+
// expire so a pipeline tagged AFTER its first event becomes resolvable without
|
|
62
|
+
// recycling the warm container. Positive results stay cached for the lifetime.
|
|
63
|
+
const resolvedByArn = new Map();
|
|
64
|
+
const NEG_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
65
|
+
/** Cap on commit/ref/environment fields — the ingest contract allows max 255. */
|
|
66
|
+
const MAX_ATTR = 255;
|
|
67
|
+
/**
|
|
68
|
+
* Parse the `pb.deploys` tag value into a stage→environment map. The value is
|
|
69
|
+
* `<stage>:<env>` pairs joined by `+` (CodePipeline-tag-safe; no JSON). A stage
|
|
70
|
+
* present here is a deploy; its mapped environment is what the forwarder stamps on
|
|
71
|
+
* that stage's STAGE/ACTION events (DORA deploy attribution). Malformed pairs are
|
|
72
|
+
* skipped defensively.
|
|
73
|
+
*/
|
|
74
|
+
function parseDeploys(value) {
|
|
75
|
+
const map = new Map();
|
|
76
|
+
if (!value)
|
|
77
|
+
return map;
|
|
78
|
+
for (const pair of value.split('+')) {
|
|
79
|
+
const idx = pair.indexOf(':');
|
|
80
|
+
if (idx <= 0)
|
|
81
|
+
continue;
|
|
82
|
+
const stage = pair.slice(0, idx).trim();
|
|
83
|
+
const env = pair.slice(idx + 1).trim();
|
|
84
|
+
if (stage && env)
|
|
85
|
+
map.set(stage, env.slice(0, MAX_ATTR));
|
|
86
|
+
}
|
|
87
|
+
return map;
|
|
88
|
+
}
|
|
89
|
+
async function pipelineClient(region) {
|
|
90
|
+
let client = clientsByRegion.get(region);
|
|
91
|
+
if (!client) {
|
|
92
|
+
const { CodePipelineClient } = await loadCodePipeline();
|
|
93
|
+
client = new CodePipelineClient({ region });
|
|
94
|
+
clientsByRegion.set(region, client);
|
|
95
|
+
}
|
|
96
|
+
return client;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve a CodePipeline's tags → the platform pipelineId (pb.pipeline-id) plus the
|
|
100
|
+
* deploy-stage→environment map (pb.deploys). `pipelineId` is null when the pipeline
|
|
101
|
+
* has no pb.pipeline-id tag (unregistered). Throws on AccessDenied so a missing IAM
|
|
102
|
+
* grant surfaces loudly instead of silently dropping every event.
|
|
103
|
+
*/
|
|
104
|
+
async function resolvePipeline(arn, region) {
|
|
105
|
+
const cached = resolvedByArn.get(arn);
|
|
106
|
+
// Serve a positive hit for the container lifetime; serve a negative hit only
|
|
107
|
+
// until it expires (then re-resolve, in case the pipeline was since tagged).
|
|
108
|
+
if (cached && (cached.pipelineId !== null || Date.now() - cached.ts < NEG_CACHE_TTL_MS)) {
|
|
109
|
+
return { pipelineId: cached.pipelineId, orgId: cached.orgId, deploys: cached.deploys };
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const client = await pipelineClient(region);
|
|
113
|
+
const { ListTagsForResourceCommand } = await loadCodePipeline();
|
|
114
|
+
const out = await client.send(new ListTagsForResourceCommand({ resourceArn: arn }));
|
|
115
|
+
const pipelineId = out.tags?.find(t => t.key === PIPELINE_ID_TAG)?.value ?? null;
|
|
116
|
+
const orgId = out.tags?.find(t => t.key === ORG_ID_TAG)?.value ?? null;
|
|
117
|
+
const deploys = parseDeploys(out.tags?.find(t => t.key === DEPLOYS_TAG)?.value);
|
|
118
|
+
if (!pipelineId)
|
|
119
|
+
log.warn('Pipeline missing pb.pipeline-id tag — skipping (register it?)', { arn });
|
|
120
|
+
resolvedByArn.set(arn, { pipelineId, orgId, deploys, ts: Date.now() });
|
|
121
|
+
return { pipelineId, orgId, deploys };
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const name = err?.name;
|
|
125
|
+
if (name === 'AccessDeniedException') {
|
|
126
|
+
// Don't cache — this is a fixable misconfig, not a property of the pipeline.
|
|
127
|
+
log.error('AccessDenied calling codepipeline:ListTagsForResource — grant it to the Lambda role', { arn, error: name });
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
// Transient/infra errors (throttling, timeouts, 5xx) MUST propagate so the SQS
|
|
131
|
+
// batch is retried rather than silently dropping the event — an event dropped
|
|
132
|
+
// BEFORE it reaches the ingest is lost permanently (the ingest's partial-unique
|
|
133
|
+
// index only dedupes events that were actually POSTed). Only swallow when the
|
|
134
|
+
// failure is a definitive answer that this ARN has no usable tag.
|
|
135
|
+
const e = err;
|
|
136
|
+
const status = e.$metadata?.httpStatusCode;
|
|
137
|
+
const retryable = e.$retryable != null
|
|
138
|
+
|| (typeof status === 'number' && status >= 500)
|
|
139
|
+
|| /throttl|timeout|toomanyrequests|serviceunavailable|econnreset|networkingerror/i.test(name ?? '');
|
|
140
|
+
if (retryable) {
|
|
141
|
+
log.error('Transient error resolving pb.pipeline-id tag — will retry the batch', { arn, error: name ?? String(err) });
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
log.error('Failed to resolve pb.pipeline-id tag', { arn, error: name ?? String(err) });
|
|
145
|
+
return { pipelineId: null, orgId: null, deploys: new Map() };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// ─── Auth ───────────────────────────────────────────────
|
|
149
|
+
let cachedToken = null;
|
|
150
|
+
async function getAuthToken() {
|
|
151
|
+
if (cachedToken)
|
|
152
|
+
return cachedToken;
|
|
153
|
+
// Path 1: PLATFORM_TOKEN env var (no Secrets Manager call)
|
|
154
|
+
if (process.env.PLATFORM_TOKEN) {
|
|
155
|
+
cachedToken = process.env.PLATFORM_TOKEN;
|
|
156
|
+
log.info('Using PLATFORM_TOKEN from environment');
|
|
157
|
+
return cachedToken;
|
|
158
|
+
}
|
|
159
|
+
// Path 2: PLATFORM_SECRET_NAME → read from Secrets Manager
|
|
160
|
+
const secretName = process.env.PLATFORM_SECRET_NAME;
|
|
161
|
+
if (!secretName) {
|
|
162
|
+
throw new Error('PLATFORM_TOKEN or PLATFORM_SECRET_NAME environment variable is required');
|
|
163
|
+
}
|
|
164
|
+
log.info(`Fetching token from Secrets Manager: ${secretName}`);
|
|
165
|
+
const client = new SecretsManagerClient({});
|
|
166
|
+
const response = await client.send(new GetSecretValueCommand({ SecretId: secretName }));
|
|
167
|
+
if (!response.SecretString)
|
|
168
|
+
throw new Error(`Secret "${secretName}" is empty`);
|
|
169
|
+
const secret = JSON.parse(response.SecretString);
|
|
170
|
+
// infra store-token writes the JWT in `password` (the canonical field — also used by
|
|
171
|
+
// CodeBuild secretsManagerCredentials, the plugin-lookup Lambda, and token-renew).
|
|
172
|
+
const token = secret.password;
|
|
173
|
+
if (!token) {
|
|
174
|
+
throw new Error('Secret missing JWT (password) — run "pipeline-manager infra store-token" to generate');
|
|
175
|
+
}
|
|
176
|
+
cachedToken = token;
|
|
177
|
+
log.info('Using stored JWT token from Secrets Manager');
|
|
178
|
+
return cachedToken;
|
|
179
|
+
}
|
|
180
|
+
// Per-(pipeline,sha) resolved result — computed once, reused across the many events
|
|
181
|
+
// (PIPELINE/STAGE/ACTION) that share a source revision and across redeliveries.
|
|
182
|
+
const commitResultCache = new Map();
|
|
183
|
+
// Last sha we successfully resolved per pipeline — the exclusive lower bound for the
|
|
184
|
+
// next range ("commits since the last deploy"). In-memory only: a cold start resets
|
|
185
|
+
// it, so the first post-cold event resolves as a single commit (expected, defensive).
|
|
186
|
+
const lastShaByPipeline = new Map();
|
|
187
|
+
// Per-sha CodeCommit metadata cache (date + first-parent) for range walks.
|
|
188
|
+
const commitMetaCache = new Map();
|
|
189
|
+
// SCM rate-limit cooldown: after a 403/429 we stop calling the SCM for a while.
|
|
190
|
+
let scmCooldownUntil = 0;
|
|
191
|
+
const SCM_COOLDOWN_MS = 2 * 60 * 1000;
|
|
192
|
+
// Cap the CodeCommit first-parent walk so a long range can't blow the Lambda timeout.
|
|
193
|
+
const MAX_COMMIT_WALK = 50;
|
|
194
|
+
// Per-request timeout for external SCM calls (defensive against slow/hung hosts).
|
|
195
|
+
const SCM_FETCH_TIMEOUT_MS = 3000;
|
|
196
|
+
// The forwarder is a fleet Lambda: one warm container resolves events for MANY
|
|
197
|
+
// orgs, so the GitHub token MUST be keyed by the event's resolved org (not a single
|
|
198
|
+
// container-global token). Each org's token lives at `<prefix>/<orgId>/github-token`,
|
|
199
|
+
// derived from PLATFORM_SECRET_NAME (`<prefix>/<containerOrg>/platform`). null caches
|
|
200
|
+
// a definitive miss (no secret / read failed) → unauthenticated best-effort.
|
|
201
|
+
const githubTokenByOrg = new Map();
|
|
202
|
+
/**
|
|
203
|
+
* Derive a specific org's github-token secret name from PLATFORM_SECRET_NAME
|
|
204
|
+
* (`<prefix>/<containerOrg>/platform`). With an `orgId` we swap the org segment
|
|
205
|
+
* (`<prefix>/<orgId>/github-token`); without one (pipeline carried no OrgId tag) we
|
|
206
|
+
* fall back to the container's own sibling secret. Returns undefined when
|
|
207
|
+
* PLATFORM_SECRET_NAME isn't the expected `.../platform` shape.
|
|
208
|
+
*/
|
|
209
|
+
function githubTokenSecretName(orgId) {
|
|
210
|
+
const name = process.env.PLATFORM_SECRET_NAME;
|
|
211
|
+
if (!name || !name.endsWith('/platform'))
|
|
212
|
+
return undefined;
|
|
213
|
+
const base = name.slice(0, -'/platform'.length); // <prefix>/<containerOrg>
|
|
214
|
+
if (orgId) {
|
|
215
|
+
const slash = base.lastIndexOf('/');
|
|
216
|
+
const prefix = slash > 0 ? base.slice(0, slash) : base;
|
|
217
|
+
return `${prefix}/${orgId}/github-token`;
|
|
218
|
+
}
|
|
219
|
+
return `${base}/github-token`;
|
|
220
|
+
}
|
|
221
|
+
/** Read (and cache, per org) the org github token; null → unauthenticated best-effort. */
|
|
222
|
+
async function getGitHubToken(orgId) {
|
|
223
|
+
const key = orgId ?? '';
|
|
224
|
+
const cached = githubTokenByOrg.get(key);
|
|
225
|
+
if (cached !== undefined)
|
|
226
|
+
return cached;
|
|
227
|
+
const secretName = githubTokenSecretName(orgId);
|
|
228
|
+
if (!secretName) {
|
|
229
|
+
githubTokenByOrg.set(key, null);
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
let token;
|
|
233
|
+
try {
|
|
234
|
+
const client = new SecretsManagerClient({});
|
|
235
|
+
const res = await client.send(new GetSecretValueCommand({ SecretId: secretName }));
|
|
236
|
+
// The github-token secret is a RAW token string (not JSON — CDK resolves it via
|
|
237
|
+
// the default SecretString with no key).
|
|
238
|
+
token = res.SecretString && res.SecretString.length > 0 ? res.SecretString.trim() : null;
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
log.warn('No github-token secret — GitHub commit resolution will be unauthenticated', { orgId, error: err?.name });
|
|
242
|
+
token = null;
|
|
243
|
+
}
|
|
244
|
+
githubTokenByOrg.set(key, token);
|
|
245
|
+
return token;
|
|
246
|
+
}
|
|
247
|
+
async function fetchWithTimeout(url, opts) {
|
|
248
|
+
const controller = new AbortController();
|
|
249
|
+
const timer = setTimeout(() => controller.abort(), SCM_FETCH_TIMEOUT_MS);
|
|
250
|
+
try {
|
|
251
|
+
return await fetch(url, { ...opts, signal: controller.signal });
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
clearTimeout(timer);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Normalize a commit date to ISO 8601. Accepts ISO strings and CodeCommit's
|
|
258
|
+
* "<epoch-seconds> +ZZZZ" form. Returns undefined when unparseable. */
|
|
259
|
+
function toIso(value) {
|
|
260
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
261
|
+
return undefined;
|
|
262
|
+
const epoch = /^(\d{9,})\b/.exec(value.trim());
|
|
263
|
+
if (epoch) {
|
|
264
|
+
const d = new Date(Number(epoch[1]) * 1000);
|
|
265
|
+
return Number.isNaN(d.getTime()) ? undefined : d.toISOString();
|
|
266
|
+
}
|
|
267
|
+
const d = new Date(value);
|
|
268
|
+
return Number.isNaN(d.getTime()) ? undefined : d.toISOString();
|
|
269
|
+
}
|
|
270
|
+
/** Classify a source-revision URL into a provider + repo coordinates. */
|
|
271
|
+
function classifySource(url) {
|
|
272
|
+
if (!url)
|
|
273
|
+
return { provider: 'unknown' };
|
|
274
|
+
try {
|
|
275
|
+
const u = new URL(url);
|
|
276
|
+
const host = u.hostname.toLowerCase();
|
|
277
|
+
if (host === 'github.com' || host.endsWith('.github.com')) {
|
|
278
|
+
const parts = u.pathname.split('/').filter(Boolean);
|
|
279
|
+
if (parts.length >= 2)
|
|
280
|
+
return { provider: 'github', owner: parts[0], repo: parts[1].replace(/\.git$/, '') };
|
|
281
|
+
}
|
|
282
|
+
if (host === 'bitbucket.org') {
|
|
283
|
+
const parts = u.pathname.split('/').filter(Boolean);
|
|
284
|
+
if (parts.length >= 2)
|
|
285
|
+
return { provider: 'bitbucket', owner: parts[0], repo: parts[1].replace(/\.git$/, '') };
|
|
286
|
+
}
|
|
287
|
+
if (u.pathname.toLowerCase().includes('codecommit') || u.hash.toLowerCase().includes('codecommit') || u.pathname.includes('/repositories/')) {
|
|
288
|
+
const m = /repositor(?:y|ies)\/([^/#?]+)/.exec(u.pathname + u.hash);
|
|
289
|
+
if (m)
|
|
290
|
+
return { provider: 'codecommit', repositoryName: decodeURIComponent(m[1]) };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// not a URL → unknown
|
|
295
|
+
}
|
|
296
|
+
return { provider: 'unknown' };
|
|
297
|
+
}
|
|
298
|
+
let codecommitClientsByRegion;
|
|
299
|
+
let codecommitMod;
|
|
300
|
+
async function codeCommitClient(region) {
|
|
301
|
+
if (!codecommitMod)
|
|
302
|
+
codecommitMod = await loadSdk('@aws-sdk/client-codecommit');
|
|
303
|
+
if (!codecommitClientsByRegion)
|
|
304
|
+
codecommitClientsByRegion = new Map();
|
|
305
|
+
let client = codecommitClientsByRegion.get(region);
|
|
306
|
+
if (!client) {
|
|
307
|
+
client = new codecommitMod.CodeCommitClient({ region });
|
|
308
|
+
codecommitClientsByRegion.set(region, client);
|
|
309
|
+
}
|
|
310
|
+
return client;
|
|
311
|
+
}
|
|
312
|
+
/** Fetch (cached) a single CodeCommit commit's date + first parents via GetCommit. */
|
|
313
|
+
async function codeCommitMeta(region, repositoryName, commitId) {
|
|
314
|
+
const cached = commitMetaCache.get(commitId);
|
|
315
|
+
if (cached)
|
|
316
|
+
return cached;
|
|
317
|
+
if (!codecommitMod)
|
|
318
|
+
codecommitMod = await loadSdk('@aws-sdk/client-codecommit');
|
|
319
|
+
const client = await codeCommitClient(region);
|
|
320
|
+
const out = await client.send(new codecommitMod.GetCommitCommand({ repositoryName, commitId }));
|
|
321
|
+
const commit = out.commit;
|
|
322
|
+
const meta = { date: toIso(commit?.committer?.date ?? commit?.author?.date), parents: commit?.parents };
|
|
323
|
+
commitMetaCache.set(commitId, meta);
|
|
324
|
+
return meta;
|
|
325
|
+
}
|
|
326
|
+
async function resolveCodeCommit(region, repositoryName, sha, sinceSha) {
|
|
327
|
+
let cur = sha;
|
|
328
|
+
let count = 0;
|
|
329
|
+
let oldest;
|
|
330
|
+
let truncated = false;
|
|
331
|
+
for (let i = 0;; i++) {
|
|
332
|
+
if (!cur)
|
|
333
|
+
break; // reached history root — the whole range is covered
|
|
334
|
+
if (sinceSha && cur === sinceSha)
|
|
335
|
+
break; // reached the exclusive lower bound
|
|
336
|
+
if (i >= MAX_COMMIT_WALK) {
|
|
337
|
+
truncated = true;
|
|
338
|
+
break;
|
|
339
|
+
} // hit the cap before the boundary
|
|
340
|
+
const meta = await codeCommitMeta(region, repositoryName, cur);
|
|
341
|
+
if (!meta?.date)
|
|
342
|
+
break;
|
|
343
|
+
count++;
|
|
344
|
+
oldest = meta.date;
|
|
345
|
+
if (!sinceSha)
|
|
346
|
+
break; // no range → single commit
|
|
347
|
+
cur = meta.parents?.[0];
|
|
348
|
+
}
|
|
349
|
+
// A truncated walk yields a commit NEWER than the true oldest-unshipped commit,
|
|
350
|
+
// which would UNDERSTATE lead time — omit the fields entirely rather than report a
|
|
351
|
+
// wrong (too-small) value. Reporting falls back to `unknown`.
|
|
352
|
+
if (truncated)
|
|
353
|
+
return {};
|
|
354
|
+
return oldest ? { commitTimestamp: oldest, commitCount: count } : {};
|
|
355
|
+
}
|
|
356
|
+
async function resolveGitHub(owner, repo, sha, sinceSha, orgId) {
|
|
357
|
+
const token = await getGitHubToken(orgId);
|
|
358
|
+
const headers = {
|
|
359
|
+
'Accept': 'application/vnd.github+json',
|
|
360
|
+
'User-Agent': 'pipeline-builder-forwarder',
|
|
361
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
362
|
+
};
|
|
363
|
+
if (token)
|
|
364
|
+
headers.Authorization = `Bearer ${token}`;
|
|
365
|
+
const rateLimited = (r) => r.status === 403 || r.status === 429;
|
|
366
|
+
if (sinceSha) {
|
|
367
|
+
const r = await fetchWithTimeout(`https://api.github.com/repos/${owner}/${repo}/compare/${sinceSha}...${sha}`, { headers });
|
|
368
|
+
if (rateLimited(r)) {
|
|
369
|
+
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
370
|
+
return {};
|
|
371
|
+
}
|
|
372
|
+
if (r.ok) {
|
|
373
|
+
const j = await r.json();
|
|
374
|
+
const commits = Array.isArray(j.commits) ? j.commits : [];
|
|
375
|
+
if (commits.length > 0) {
|
|
376
|
+
const oldest = toIso(commits[0]?.commit?.committer?.date ?? commits[0]?.commit?.author?.date);
|
|
377
|
+
if (oldest)
|
|
378
|
+
return { commitTimestamp: oldest, commitCount: commits.length };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// fall through to single-commit resolution
|
|
382
|
+
}
|
|
383
|
+
const r2 = await fetchWithTimeout(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}`, { headers });
|
|
384
|
+
if (rateLimited(r2)) {
|
|
385
|
+
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
386
|
+
return {};
|
|
387
|
+
}
|
|
388
|
+
if (r2.ok) {
|
|
389
|
+
const j = await r2.json();
|
|
390
|
+
const d = toIso(j.commit?.committer?.date ?? j.commit?.author?.date);
|
|
391
|
+
if (d)
|
|
392
|
+
return { commitTimestamp: d, commitCount: 1 };
|
|
393
|
+
}
|
|
394
|
+
return {};
|
|
395
|
+
}
|
|
396
|
+
async function resolveBitbucket(owner, repo, sha) {
|
|
397
|
+
const r = await fetchWithTimeout(`https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/commit/${sha}`, {
|
|
398
|
+
headers: { 'User-Agent': 'pipeline-builder-forwarder', 'Accept': 'application/json' },
|
|
399
|
+
});
|
|
400
|
+
if (r.status === 429) {
|
|
401
|
+
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
402
|
+
return {};
|
|
403
|
+
}
|
|
404
|
+
if (r.ok) {
|
|
405
|
+
const j = await r.json();
|
|
406
|
+
const d = toIso(j.date);
|
|
407
|
+
if (d)
|
|
408
|
+
return { commitTimestamp: d, commitCount: 1 };
|
|
409
|
+
}
|
|
410
|
+
return {};
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Resolve `commitTimestamp` + `commitCount` for a source/deploy event, in-account,
|
|
414
|
+
* best-effort. Computed once per (pipelineId, sha); advances the pipeline's range
|
|
415
|
+
* boundary only on success. Never throws — returns {} on any failure.
|
|
416
|
+
*/
|
|
417
|
+
async function resolveCommitInfo(pipelineId, orgId, region, src) {
|
|
418
|
+
if (src.provider === 'unknown')
|
|
419
|
+
return {};
|
|
420
|
+
const rkey = `${pipelineId}|${src.sha}`;
|
|
421
|
+
const cached = commitResultCache.get(rkey);
|
|
422
|
+
if (cached)
|
|
423
|
+
return cached;
|
|
424
|
+
const prev = lastShaByPipeline.get(pipelineId);
|
|
425
|
+
const sinceSha = prev && prev !== src.sha ? prev : undefined;
|
|
426
|
+
let info = {};
|
|
427
|
+
try {
|
|
428
|
+
if (src.provider === 'codecommit' && src.repositoryName) {
|
|
429
|
+
info = await resolveCodeCommit(region, src.repositoryName, src.sha, sinceSha);
|
|
430
|
+
}
|
|
431
|
+
else if (src.provider === 'github' && src.owner && src.repo) {
|
|
432
|
+
if (Date.now() >= scmCooldownUntil)
|
|
433
|
+
info = await resolveGitHub(src.owner, src.repo, src.sha, sinceSha, orgId);
|
|
434
|
+
}
|
|
435
|
+
else if (src.provider === 'bitbucket' && src.owner && src.repo) {
|
|
436
|
+
if (Date.now() >= scmCooldownUntil)
|
|
437
|
+
info = await resolveBitbucket(src.owner, src.repo, src.sha);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
log.warn('Commit resolution failed (omitting commitTimestamp/commitCount)', { provider: src.provider, error: err?.name ?? String(err) });
|
|
442
|
+
info = {};
|
|
443
|
+
}
|
|
444
|
+
commitResultCache.set(rkey, info);
|
|
445
|
+
if (info.commitTimestamp)
|
|
446
|
+
lastShaByPipeline.set(pipelineId, src.sha);
|
|
447
|
+
return info;
|
|
448
|
+
}
|
|
449
|
+
/** Cap on the stored failure summary — CodeBuild/Deploy summaries can be long. */
|
|
450
|
+
const MAX_ERROR_MESSAGE = 4000;
|
|
451
|
+
/** First non-empty string among the candidates, capped to the ingest max. */
|
|
452
|
+
function firstAttr(...vals) {
|
|
453
|
+
for (const v of vals) {
|
|
454
|
+
if (typeof v === 'string' && v.length > 0)
|
|
455
|
+
return v.slice(0, MAX_ATTR);
|
|
456
|
+
}
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Best-effort extraction of the source commit id + ref (and the revision URL, used
|
|
461
|
+
* only in-account to classify the source type for commit-range resolution) from a
|
|
462
|
+
* CodePipeline event detail. CodePipeline surfaces the source revision under a few
|
|
463
|
+
* shapes depending on the event type / source-action; we check the known ones and
|
|
464
|
+
* leave the fields undefined when none are present (NULL downstream). Never pulls an
|
|
465
|
+
* ARN/account id: only revision/branch/url string fields are read; the revisionUrl
|
|
466
|
+
* is consumed in-account (source classification) and never forwarded on its own.
|
|
467
|
+
*/
|
|
468
|
+
function extractCommit(detail) {
|
|
469
|
+
// `source-revisions` (kebab, EventBridge) / `sourceRevisions` (camel) — first entry.
|
|
470
|
+
const revs = (detail['source-revisions'] ?? detail.sourceRevisions);
|
|
471
|
+
const firstRev = Array.isArray(revs) ? revs[0] : undefined;
|
|
472
|
+
// On source actions the revision id can also ride on `execution-result`.
|
|
473
|
+
const execResult = detail['execution-result'];
|
|
474
|
+
const commitSha = firstAttr(detail.commitId, detail.revisionId, firstRev?.revisionId, execResult?.revisionId);
|
|
475
|
+
const commitRef = firstAttr(detail.commitRef, detail.branch, detail.sourceBranch, firstRev?.branchName, firstRev?.branch);
|
|
476
|
+
const revisionUrl = typeof firstRev?.revisionUrl === 'string' ? firstRev.revisionUrl : undefined;
|
|
477
|
+
return { ...(commitSha && { commitSha }), ...(commitRef && { commitRef }), ...(revisionUrl && { revisionUrl }) };
|
|
478
|
+
}
|
|
479
|
+
function classifyEvent(detailType) {
|
|
480
|
+
if (detailType.includes('Pipeline Execution'))
|
|
481
|
+
return { eventType: 'PIPELINE', eventSource: 'codepipeline' };
|
|
482
|
+
if (detailType.includes('Stage Execution'))
|
|
483
|
+
return { eventType: 'STAGE', eventSource: 'codepipeline' };
|
|
484
|
+
if (detailType.includes('Action Execution'))
|
|
485
|
+
return { eventType: 'ACTION', eventSource: 'codepipeline' };
|
|
486
|
+
// CodeBuild "Build State" events identify a build *project*, not a pipeline, so
|
|
487
|
+
// parseRecord drops them on the `eventSource !== 'codepipeline'` check below —
|
|
488
|
+
// the 'BUILD' eventType is never persisted here (plugin BUILD events are
|
|
489
|
+
// recorded directly by the plugin service). The 'codebuild' source is what
|
|
490
|
+
// routes the drop; keep this branch only for that.
|
|
491
|
+
if (detailType.includes('Build State'))
|
|
492
|
+
return { eventType: 'BUILD', eventSource: 'codebuild' };
|
|
493
|
+
return { eventType: 'PIPELINE', eventSource: 'codepipeline' };
|
|
494
|
+
}
|
|
495
|
+
/** Parse + resolve one record to a reportable event, or null to skip it. */
|
|
496
|
+
async function parseRecord(record) {
|
|
497
|
+
let event;
|
|
498
|
+
// A malformed body is a dead message (retrying won't help) — log + skip it so
|
|
499
|
+
// it can't fail the whole batch. Resolution errors below (e.g. AccessDenied)
|
|
500
|
+
// are NOT caught here: those are transient/infra and must propagate to retry.
|
|
501
|
+
try {
|
|
502
|
+
event = JSON.parse(record.body);
|
|
503
|
+
}
|
|
504
|
+
catch (err) {
|
|
505
|
+
log.warn('Skipping SQS record with unparseable body', { messageId: record.messageId, error: String(err) });
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
const { eventType, eventSource } = classifyEvent(event['detail-type']);
|
|
509
|
+
const detail = { ...event.detail };
|
|
510
|
+
// The raw account never needs to leave AWS now — drop it from the payload.
|
|
511
|
+
delete detail.account;
|
|
512
|
+
// CodeBuild "Build State" events identify a build *project*, not a pipeline,
|
|
513
|
+
// and a project can be shared across pipelines — there's no clean 1:1 mapping
|
|
514
|
+
// to a pipeline id, so skip them.
|
|
515
|
+
if (eventSource !== 'codepipeline') {
|
|
516
|
+
log.warn('Skipping non-CodePipeline event (no pipeline tag to resolve)', { detailType: event['detail-type'] });
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
const pipelineName = detail.pipeline;
|
|
520
|
+
if (!pipelineName) {
|
|
521
|
+
log.warn('CodePipeline event missing pipeline name — skipping');
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
// Resolve the pipeline's pb.pipeline-id (= platform pipelineId) + pb.deploys map.
|
|
525
|
+
// The ARN is only a transient handle for the tag lookup; it is never stored.
|
|
526
|
+
const arn = `arn:aws:codepipeline:${event.region}:${event.account}:${pipelineName}`;
|
|
527
|
+
const { pipelineId, orgId, deploys } = await resolvePipeline(arn, event.region);
|
|
528
|
+
if (!pipelineId)
|
|
529
|
+
return null; // untagged / unregistered → skip
|
|
530
|
+
const startedAt = detail['start-time'] || event.time;
|
|
531
|
+
const state = detail.state;
|
|
532
|
+
let durationMs;
|
|
533
|
+
let completedAt;
|
|
534
|
+
if (['SUCCEEDED', 'FAILED', 'CANCELED', 'STOPPED'].includes(state)) {
|
|
535
|
+
completedAt = event.time;
|
|
536
|
+
if (startedAt && completedAt) {
|
|
537
|
+
const ms = new Date(completedAt).getTime() - new Date(startedAt).getTime();
|
|
538
|
+
if (ms >= 0)
|
|
539
|
+
durationMs = ms;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// Promote the failure reason to a typed field. On Action events AWS puts the
|
|
543
|
+
// human-readable summary (and a log URL + error-code, which stay in `detail`)
|
|
544
|
+
// under `execution-result`.
|
|
545
|
+
const result = detail['execution-result'];
|
|
546
|
+
const summary = result?.['external-execution-summary'];
|
|
547
|
+
const errorMessage = typeof summary === 'string' && summary.length > 0
|
|
548
|
+
? summary.slice(0, MAX_ERROR_MESSAGE)
|
|
549
|
+
: undefined;
|
|
550
|
+
// DORA deploy attribution — all optional: omitted (undefined) when the
|
|
551
|
+
// event/pipeline doesn't carry them.
|
|
552
|
+
const { commitSha, commitRef, revisionUrl } = extractCommit(detail);
|
|
553
|
+
const executionId = detail['execution-id'];
|
|
554
|
+
const stageName = detail.stage;
|
|
555
|
+
const actionName = detail.action;
|
|
556
|
+
// Environment (⇒ isDeploy) is set ONLY when this event's stage is listed in the
|
|
557
|
+
// pipeline's pb.deploys tag. PIPELINE events have no stage → never a deploy.
|
|
558
|
+
const environment = stageName ? deploys.get(stageName) : undefined;
|
|
559
|
+
// Phase 4 — resolve the commit range (oldest-unshipped timestamp + count) for any
|
|
560
|
+
// event carrying a commit. Best-effort/in-account; omitted on any failure.
|
|
561
|
+
// GATED on `DORA_ENABLED` (set by `setup-events --with-dora`): DORA lead time is an
|
|
562
|
+
// advanced_reporting add-on, so orgs without it skip the SCM/secret cost entirely —
|
|
563
|
+
// standard reporting (commitSha + all other fields) still forwards regardless.
|
|
564
|
+
let commitTimestamp;
|
|
565
|
+
let commitCount;
|
|
566
|
+
if (commitSha && process.env.DORA_ENABLED === 'true') {
|
|
567
|
+
const src = classifySource(revisionUrl);
|
|
568
|
+
const info = await resolveCommitInfo(pipelineId, orgId, event.region, { sha: commitSha, ...src });
|
|
569
|
+
commitTimestamp = info.commitTimestamp;
|
|
570
|
+
commitCount = info.commitCount;
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
orgId,
|
|
574
|
+
pipelineId,
|
|
575
|
+
eventSource,
|
|
576
|
+
eventType,
|
|
577
|
+
status: state,
|
|
578
|
+
executionId,
|
|
579
|
+
stageName,
|
|
580
|
+
actionName,
|
|
581
|
+
errorMessage,
|
|
582
|
+
startedAt,
|
|
583
|
+
completedAt,
|
|
584
|
+
durationMs,
|
|
585
|
+
...(commitSha && { commitSha }),
|
|
586
|
+
...(commitRef && { commitRef }),
|
|
587
|
+
...(commitTimestamp && { commitTimestamp }),
|
|
588
|
+
...(commitCount && { commitCount }),
|
|
589
|
+
...(environment && { environment }),
|
|
590
|
+
detail,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
let sqsMod;
|
|
594
|
+
let sqsClientsByRegion;
|
|
595
|
+
async function sqsClient(region) {
|
|
596
|
+
if (!sqsMod)
|
|
597
|
+
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
598
|
+
if (!sqsClientsByRegion)
|
|
599
|
+
sqsClientsByRegion = new Map();
|
|
600
|
+
let client = sqsClientsByRegion.get(region);
|
|
601
|
+
if (!client) {
|
|
602
|
+
client = new sqsMod.SQSClient({ region });
|
|
603
|
+
sqsClientsByRegion.set(region, client);
|
|
604
|
+
}
|
|
605
|
+
return client;
|
|
606
|
+
}
|
|
607
|
+
// Delivery-health counters are attributed PER RESOLVED ORG (the forwarder is a fleet
|
|
608
|
+
// Lambda serving many orgs; a single global counter would mis-attribute one org's
|
|
609
|
+
// throughput to another). Keyed by orgId ('' bucket = pipeline with no OrgId tag).
|
|
610
|
+
// The DLQ depth (`dropped`) is genuinely fleet-global — one shared DLQ — so it is a
|
|
611
|
+
// single snapshot posted alongside each org's health, documented as the shared depth.
|
|
612
|
+
const forwardedByOrg = new Map();
|
|
613
|
+
const lastEventAtByOrg = new Map();
|
|
614
|
+
let lastHealthAt = 0;
|
|
615
|
+
let lastRedriveAt = 0;
|
|
616
|
+
const HEALTH_THROTTLE_MS = 60 * 1000;
|
|
617
|
+
const REDRIVE_THROTTLE_MS = 5 * 60 * 1000;
|
|
618
|
+
// Poison-message ceiling: if the oldest DLQ message is older than this, we STOP
|
|
619
|
+
// redriving it (DLQ→main→fail→DLQ would loop forever). Such messages are left for an
|
|
620
|
+
// alarm / manual inspection instead of churning the fleet indefinitely.
|
|
621
|
+
const MAX_DLQ_REDRIVE_AGE_SEC = 6 * 60 * 60; // 6 hours
|
|
622
|
+
/** Parse the SQS trigger ARN and derive the main + DLQ ARNs and the DLQ URL. */
|
|
623
|
+
function deriveQueues(mainQueueArn) {
|
|
624
|
+
if (!mainQueueArn)
|
|
625
|
+
return undefined;
|
|
626
|
+
// arn:aws:sqs:{region}:{account}:{name}
|
|
627
|
+
const parts = mainQueueArn.split(':');
|
|
628
|
+
if (parts.length < 6 || parts[2] !== 'sqs')
|
|
629
|
+
return undefined;
|
|
630
|
+
const [, , , region, account, name] = parts;
|
|
631
|
+
const dlqArn = process.env.EVENT_DLQ_ARN || `arn:aws:sqs:${region}:${account}:${name}-dlq`;
|
|
632
|
+
const dlqName = dlqArn.split(':').pop() ?? `${name}-dlq`;
|
|
633
|
+
const dlqUrl = `https://sqs.${region}.amazonaws.com/${account}/${dlqName}`;
|
|
634
|
+
return { region, mainArn: mainQueueArn, dlqArn, dlqUrl };
|
|
635
|
+
}
|
|
636
|
+
/** Snapshot the DLQ depth + the age (seconds) of its oldest message (for the
|
|
637
|
+
* poison-message redrive ceiling). Missing/NaN attributes fall back to 0. */
|
|
638
|
+
async function dlqStats(region, dlqUrl) {
|
|
639
|
+
if (!sqsMod)
|
|
640
|
+
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
641
|
+
const client = await sqsClient(region);
|
|
642
|
+
const out = await client.send(new sqsMod.GetQueueAttributesCommand({
|
|
643
|
+
QueueUrl: dlqUrl,
|
|
644
|
+
AttributeNames: ['ApproximateNumberOfMessages', 'ApproximateAgeOfOldestMessage'],
|
|
645
|
+
}));
|
|
646
|
+
const n = Number(out.Attributes?.ApproximateNumberOfMessages ?? '0');
|
|
647
|
+
const age = Number(out.Attributes?.ApproximateAgeOfOldestMessage ?? '0');
|
|
648
|
+
return {
|
|
649
|
+
depth: Number.isFinite(n) ? n : 0,
|
|
650
|
+
oldestAgeSec: Number.isFinite(age) ? age : 0,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
async function hasActiveMoveTask(region, dlqArn) {
|
|
654
|
+
if (!sqsMod)
|
|
655
|
+
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
656
|
+
const client = await sqsClient(region);
|
|
657
|
+
const out = await client.send(new sqsMod.ListMessageMoveTasksCommand({ SourceArn: dlqArn }));
|
|
658
|
+
return (out.Results ?? []).some(t => t.Status === 'RUNNING');
|
|
659
|
+
}
|
|
660
|
+
async function startMoveTask(region, dlqArn, mainArn) {
|
|
661
|
+
if (!sqsMod)
|
|
662
|
+
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
663
|
+
const client = await sqsClient(region);
|
|
664
|
+
await client.send(new sqsMod.StartMessageMoveTaskCommand({ SourceArn: dlqArn, DestinationArn: mainArn }));
|
|
665
|
+
}
|
|
666
|
+
/** POST one org's ingest-health signal. Returns true only on a confirmed 2xx so the
|
|
667
|
+
* caller resets that org's `forwarded` counter ONLY when the signal was accepted
|
|
668
|
+
* (a dropped/failed POST must not silently zero the accumulated count). */
|
|
669
|
+
async function postIngestHealth(baseUrl, body) {
|
|
670
|
+
try {
|
|
671
|
+
const token = await getAuthToken();
|
|
672
|
+
const res = await fetch(`${baseUrl}/api/reports/ingest-health`, {
|
|
673
|
+
method: 'POST',
|
|
674
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
|
|
675
|
+
body: JSON.stringify(body),
|
|
676
|
+
});
|
|
677
|
+
if (!res.ok) {
|
|
678
|
+
log.warn('ingest-health POST returned non-2xx (ignored)', { status: res.status, orgId: body.orgId });
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
log.warn('ingest-health POST failed (ignored)', { error: String(err), orgId: body.orgId });
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Throttled, best-effort post-success work: emit per-org ingest-health and self-heal
|
|
690
|
+
* the DLQ. Never throws. `forwarded`/`lastEventAt` accumulate PER ORG across batches
|
|
691
|
+
* between health posts; `dropped` is the shared fleet DLQ depth snapshot. A per-org
|
|
692
|
+
* counter is zeroed only after that org's health POST is confirmed 2xx.
|
|
693
|
+
*/
|
|
694
|
+
async function reportHealthAndRedrive(baseUrl, mainQueueArn) {
|
|
695
|
+
const now = Date.now();
|
|
696
|
+
if (now - lastHealthAt < HEALTH_THROTTLE_MS)
|
|
697
|
+
return;
|
|
698
|
+
lastHealthAt = now;
|
|
699
|
+
try {
|
|
700
|
+
const q = deriveQueues(mainQueueArn);
|
|
701
|
+
let depth = 0;
|
|
702
|
+
let oldestAgeSec = 0;
|
|
703
|
+
if (q) {
|
|
704
|
+
try {
|
|
705
|
+
const stats = await dlqStats(q.region, q.dlqUrl);
|
|
706
|
+
depth = stats.depth;
|
|
707
|
+
oldestAgeSec = stats.oldestAgeSec;
|
|
708
|
+
}
|
|
709
|
+
catch (err) {
|
|
710
|
+
log.warn('DLQ stats check failed (ignored)', { error: err?.name ?? String(err) });
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Post one health signal per resolved org, attributing its own forwarded count +
|
|
714
|
+
// last event time. `dropped` (the shared fleet DLQ depth) rides along on each.
|
|
715
|
+
// Snapshot the keys first so we don't mutate the map while iterating.
|
|
716
|
+
const orgKeys = new Set([...forwardedByOrg.keys(), ...lastEventAtByOrg.keys()]);
|
|
717
|
+
if (orgKeys.size === 0)
|
|
718
|
+
orgKeys.add(''); // nothing forwarded yet → still report depth
|
|
719
|
+
for (const key of orgKeys) {
|
|
720
|
+
const forwarded = forwardedByOrg.get(key) ?? 0;
|
|
721
|
+
const lastEventAt = lastEventAtByOrg.get(key);
|
|
722
|
+
const ok = await postIngestHealth(baseUrl, {
|
|
723
|
+
...(key ? { orgId: key } : {}),
|
|
724
|
+
forwarded,
|
|
725
|
+
dropped: depth,
|
|
726
|
+
lastEventAt,
|
|
727
|
+
});
|
|
728
|
+
// Reset the forwarded counter ONLY on a confirmed 2xx — a failed/dropped POST
|
|
729
|
+
// keeps the count so the next tick re-reports it instead of losing it.
|
|
730
|
+
if (ok)
|
|
731
|
+
forwardedByOrg.set(key, 0);
|
|
732
|
+
}
|
|
733
|
+
if (q && depth > 0 && now - lastRedriveAt >= REDRIVE_THROTTLE_MS) {
|
|
734
|
+
// Poison-message ceiling: never redrive a DLQ whose oldest message has aged
|
|
735
|
+
// past the ceiling — that message keeps failing and would loop DLQ→main→DLQ.
|
|
736
|
+
if (oldestAgeSec > MAX_DLQ_REDRIVE_AGE_SEC) {
|
|
737
|
+
log.warn('DLQ oldest message exceeds poison-age ceiling — skipping redrive (needs manual attention)', {
|
|
738
|
+
dlqArn: q.dlqArn, dropped: depth, oldestAgeSec, ceilingSec: MAX_DLQ_REDRIVE_AGE_SEC,
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
try {
|
|
743
|
+
if (!(await hasActiveMoveTask(q.region, q.dlqArn))) {
|
|
744
|
+
lastRedriveAt = now;
|
|
745
|
+
await startMoveTask(q.region, q.dlqArn, q.mainArn);
|
|
746
|
+
log.info('Started self-healing DLQ→main redrive', { dlqArn: q.dlqArn, dropped: depth, oldestAgeSec });
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
catch (err) {
|
|
750
|
+
log.warn('DLQ redrive failed (ignored)', { error: err?.name ?? String(err) });
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
catch (err) {
|
|
756
|
+
log.warn('health/redrive step failed (ignored)', { error: String(err) });
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
// ─── Handler ────────────────────────────────────────────
|
|
760
|
+
export const handler = async (event) => {
|
|
761
|
+
const baseUrl = process.env.PLATFORM_BASE_URL;
|
|
762
|
+
if (!baseUrl)
|
|
763
|
+
throw new Error('PLATFORM_BASE_URL environment variable is required');
|
|
764
|
+
// Parse + resolve all records (tag + commit lookups run concurrently); drop
|
|
765
|
+
// intentional skips (null). A malformed record BODY is handled inside parseRecord
|
|
766
|
+
// (logged + skipped) so one bad message can't fail the whole batch — while genuine
|
|
767
|
+
// infra errors (e.g. AccessDenied on the tag lookup) still propagate so a missing
|
|
768
|
+
// IAM grant surfaces and SQS retries the batch.
|
|
769
|
+
const events = (await Promise.all(event.Records.map(parseRecord)))
|
|
770
|
+
.filter((e) => e !== null);
|
|
771
|
+
if (events.length === 0) {
|
|
772
|
+
log.info('No resolvable CodePipeline events in batch');
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
// POST batch to reporting service. On a 401/403 the cached JWT has likely
|
|
776
|
+
// expired — drop it, re-fetch, and retry ONCE so an expired token doesn't
|
|
777
|
+
// permanently brick a warm container. `orgId` is INTERNAL (token/health keying) —
|
|
778
|
+
// strip it here so it never rides the wire; the ingest resolves org from the
|
|
779
|
+
// pipeline registry, not the body.
|
|
780
|
+
const payloadEvents = events.map(({ orgId: _orgId, ...rest }) => rest);
|
|
781
|
+
const post = (token) => fetch(`${baseUrl}/api/reports/events`, {
|
|
782
|
+
method: 'POST',
|
|
783
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
|
|
784
|
+
body: JSON.stringify({ events: payloadEvents }),
|
|
785
|
+
});
|
|
786
|
+
let res = await post(await getAuthToken());
|
|
787
|
+
if (res.status === 401 || res.status === 403) {
|
|
788
|
+
log.warn('Reporting API auth failed; refreshing token and retrying once', { status: res.status });
|
|
789
|
+
cachedToken = null;
|
|
790
|
+
res = await post(await getAuthToken());
|
|
791
|
+
}
|
|
792
|
+
if (!res.ok) {
|
|
793
|
+
const body = await res.text().catch(() => '');
|
|
794
|
+
log.error(`Reporting API returned ${res.status}`, { body });
|
|
795
|
+
throw new Error(`Reporting API failed: ${res.status}`);
|
|
796
|
+
}
|
|
797
|
+
// The insert already succeeded (2xx). The parsed body is only used for a log
|
|
798
|
+
// line, so a non-JSON/empty body must not throw and fail the whole batch —
|
|
799
|
+
// that would trigger an SQS redelivery and re-POST (duplicate) already-inserted
|
|
800
|
+
// events. The ingest's partial-unique index dedupes, but avoid the churn anyway.
|
|
801
|
+
const result = await res.json().catch(() => ({}));
|
|
802
|
+
log.info(`Ingested ${events.length} events`, { inserted: result.data });
|
|
803
|
+
// Accumulate delivery-health counters PER RESOLVED ORG, then run the throttled
|
|
804
|
+
// post-success health + self-healing DLQ redrive (best-effort, never fails batch).
|
|
805
|
+
for (const e of events) {
|
|
806
|
+
const key = e.orgId ?? '';
|
|
807
|
+
forwardedByOrg.set(key, (forwardedByOrg.get(key) ?? 0) + 1);
|
|
808
|
+
const at = e.completedAt || e.startedAt;
|
|
809
|
+
if (at) {
|
|
810
|
+
const prev = lastEventAtByOrg.get(key);
|
|
811
|
+
if (!prev || at > prev)
|
|
812
|
+
lastEventAtByOrg.set(key, at);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
await reportHealthAndRedrive(baseUrl, event.Records[0]?.eventSourceARN);
|
|
816
|
+
};
|
|
817
|
+
/**
|
|
818
|
+
* @internal Test-only: reset all module-level caches/throttle state so tests are
|
|
819
|
+
* isolated from one another. Stripped from the published .d.ts (stripInternal).
|
|
820
|
+
*/
|
|
821
|
+
export function _resetForTests() {
|
|
822
|
+
resolvedByArn.clear();
|
|
823
|
+
commitResultCache.clear();
|
|
824
|
+
lastShaByPipeline.clear();
|
|
825
|
+
commitMetaCache.clear();
|
|
826
|
+
scmCooldownUntil = 0;
|
|
827
|
+
githubTokenByOrg.clear();
|
|
828
|
+
forwardedByOrg.clear();
|
|
829
|
+
lastEventAtByOrg.clear();
|
|
830
|
+
lastHealthAt = 0;
|
|
831
|
+
lastRedriveAt = 0;
|
|
832
|
+
cachedToken = null;
|
|
833
|
+
}
|
|
834
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQWtCOUYsSUFBSSxlQUErQyxDQUFDO0FBQ3BELEtBQUssVUFBVSxnQkFBZ0I7SUFDN0IsSUFBSSxDQUFDLGVBQWU7UUFBRSxlQUFlLEdBQUcsQ0FBQyxNQUFNLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxDQUFrQyxDQUFDO0lBQ3hILE9BQU8sZUFBZSxDQUFDO0FBQ3pCLENBQUM7QUFFRCxtRkFBbUY7QUFDbkYsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSxLQUFLLFVBQVUsT0FBTyxDQUFJLEdBQVc7SUFDbkMsT0FBTyxDQUFDLE1BQU0sTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFNLENBQUM7QUFDbEMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFFSCxNQUFNLEdBQUcsR0FBRztJQUNWLElBQUksRUFBRSxDQUFDLEdBQVcsRUFBRSxJQUFjLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZJLElBQUksRUFBRSxDQUFDLEdBQVcsRUFBRSxJQUFjLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZJLEtBQUssRUFBRSxDQUFDLEdBQVcsRUFBRSxJQUFjLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0NBQzVJLENBQUM7QUFFRiw0REFBNEQ7QUFDNUQsMENBQTBDO0FBQzFDLDJDQUEyQztBQUMzQyw2REFBNkQ7QUFDN0QsaUVBQWlFO0FBQ2pFLG1GQUFtRjtBQUNuRixtRkFBbUY7QUFDbkYsOEJBQThCO0FBQzlCLEVBQUU7QUFDRixrRkFBa0Y7QUFDbEYsaUZBQWlGO0FBQ2pGLDBDQUEwQztBQUUxQyxNQUFNLGVBQWUsR0FBRyxnQkFBZ0IsQ0FBQztBQUN6QyxNQUFNLFdBQVcsR0FBRyxZQUFZLENBQUM7QUFDakMsZ0ZBQWdGO0FBQ2hGLGlGQUFpRjtBQUNqRiw2RUFBNkU7QUFDN0UsNkNBQTZDO0FBQzdDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQztBQUMzQixNQUFNLGVBQWUsR0FBRyxJQUFJLEdBQUcsRUFBa0MsQ0FBQztBQUNsRSxtRkFBbUY7QUFDbkYsK0VBQStFO0FBQy9FLCtFQUErRTtBQUMvRSxNQUFNLGFBQWEsR0FBRyxJQUFJLEdBQUcsRUFBeUcsQ0FBQztBQUN2SSxNQUFNLGdCQUFnQixHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBWXZDLGlGQUFpRjtBQUNqRixNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUM7QUFFckI7Ozs7OztHQU1HO0FBQ0gsU0FBUyxZQUFZLENBQUMsS0FBeUI7SUFDN0MsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDdEMsSUFBSSxDQUFDLEtBQUs7UUFBRSxPQUFPLEdBQUcsQ0FBQztJQUN2QixLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNwQyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLElBQUksR0FBRyxJQUFJLENBQUM7WUFBRSxTQUFTO1FBQ3ZCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3hDLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3ZDLElBQUksS0FBSyxJQUFJLEdBQUc7WUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRCxLQUFLLFVBQVUsY0FBYyxDQUFDLE1BQWM7SUFDMUMsSUFBSSxNQUFNLEdBQUcsZUFBZSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN6QyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDWixNQUFNLEVBQUUsa0JBQWtCLEVBQUUsR0FBRyxNQUFNLGdCQUFnQixFQUFFLENBQUM7UUFDeEQsTUFBTSxHQUFHLElBQUksa0JBQWtCLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQzVDLGVBQWUsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxLQUFLLFVBQVUsZUFBZSxDQUFDLEdBQVcsRUFBRSxNQUFjO0lBQ3hELE1BQU0sTUFBTSxHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdEMsNkVBQTZFO0lBQzdFLDZFQUE2RTtJQUM3RSxJQUFJLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEtBQUssSUFBSSxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxNQUFNLENBQUMsRUFBRSxHQUFHLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztRQUN4RixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUN6RixDQUFDO0lBQ0QsSUFBSSxDQUFDO1FBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDNUMsTUFBTSxFQUFFLDBCQUEwQixFQUFFLEdBQUcsTUFBTSxnQkFBZ0IsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sR0FBRyxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLDBCQUEwQixDQUFDLEVBQUUsV0FBVyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNwRixNQUFNLFVBQVUsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLEtBQUssZUFBZSxDQUFDLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQztRQUNqRixNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLEtBQUssVUFBVSxDQUFDLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQztRQUN2RSxNQUFNLE9BQU8sR0FBRyxZQUFZLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLFdBQVcsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ2hGLElBQUksQ0FBQyxVQUFVO1lBQUUsR0FBRyxDQUFDLElBQUksQ0FBQywrREFBK0QsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDcEcsYUFBYSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUN2RSxPQUFPLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQztJQUN4QyxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE1BQU0sSUFBSSxHQUFJLEdBQXlCLEVBQUUsSUFBSSxDQUFDO1FBQzlDLElBQUksSUFBSSxLQUFLLHVCQUF1QixFQUFFLENBQUM7WUFDckMsNkVBQTZFO1lBQzdFLEdBQUcsQ0FBQyxLQUFLLENBQUMscUZBQXFGLEVBQUUsRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFDdkgsTUFBTSxHQUFHLENBQUM7UUFDWixDQUFDO1FBQ0QsK0VBQStFO1FBQy9FLDhFQUE4RTtRQUM5RSxnRkFBZ0Y7UUFDaEYsOEVBQThFO1FBQzlFLGtFQUFrRTtRQUNsRSxNQUFNLENBQUMsR0FBRyxHQUF1RixDQUFDO1FBQ2xHLE1BQU0sTUFBTSxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsY0FBYyxDQUFDO1FBQzNDLE1BQU0sU0FBUyxHQUFHLENBQUMsQ0FBQyxVQUFVLElBQUksSUFBSTtlQUNqQyxDQUFDLE9BQU8sTUFBTSxLQUFLLFFBQVEsSUFBSSxNQUFNLElBQUksR0FBRyxDQUFDO2VBQzdDLGdGQUFnRixDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDLENBQUM7UUFDdkcsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLEdBQUcsQ0FBQyxLQUFLLENBQUMscUVBQXFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLElBQUksSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3RILE1BQU0sR0FBRyxDQUFDO1FBQ1osQ0FBQztRQUNELEdBQUcsQ0FBQyxLQUFLLENBQUMsc0NBQXNDLEVBQUUsRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLElBQUksSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZGLE9BQU8sRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksR0FBRyxFQUFFLEVBQUUsQ0FBQztJQUMvRCxDQUFDO0FBQ0gsQ0FBQztBQUVELDJEQUEyRDtBQUUzRCxJQUFJLFdBQVcsR0FBa0IsSUFBSSxDQUFDO0FBRXRDLEtBQUssVUFBVSxZQUFZO0lBQ3pCLElBQUksV0FBVztRQUFFLE9BQU8sV0FBVyxDQUFDO0lBRXBDLDJEQUEyRDtJQUMzRCxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDL0IsV0FBVyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDO1FBQ3pDLEdBQUcsQ0FBQyxJQUFJLENBQUMsdUNBQXVDLENBQUMsQ0FBQztRQUNsRCxPQUFPLFdBQVcsQ0FBQztJQUNyQixDQUFDO0lBRUQsMkRBQTJEO0lBQzNELE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLENBQUM7SUFDcEQsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2hCLE1BQU0sSUFBSSxLQUFLLENBQUMseUVBQXlFLENBQUMsQ0FBQztJQUM3RixDQUFDO0lBRUQsR0FBRyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUMvRCxNQUFNLE1BQU0sR0FBRyxJQUFJLG9CQUFvQixDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVDLE1BQU0sUUFBUSxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLHFCQUFxQixDQUFDLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUV4RixJQUFJLENBQUMsUUFBUSxDQUFDLFlBQVk7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLFdBQVcsVUFBVSxZQUFZLENBQUMsQ0FBQztJQUUvRSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQTJCLENBQUM7SUFDM0UscUZBQXFGO0lBQ3JGLG1GQUFtRjtJQUNuRixNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDO0lBQzlCLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNYLE1BQU0sSUFBSSxLQUFLLENBQUMsc0ZBQXNGLENBQUMsQ0FBQztJQUMxRyxDQUFDO0lBRUQsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUNwQixHQUFHLENBQUMsSUFBSSxDQUFDLDZDQUE2QyxDQUFDLENBQUM7SUFDeEQsT0FBTyxXQUFXLENBQUM7QUFDckIsQ0FBQztBQThCRCxvRkFBb0Y7QUFDcEYsZ0ZBQWdGO0FBQ2hGLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQXNCLENBQUM7QUFDeEQscUZBQXFGO0FBQ3JGLG9GQUFvRjtBQUNwRixzRkFBc0Y7QUFDdEYsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsRUFBa0IsQ0FBQztBQUNwRCwyRUFBMkU7QUFDM0UsTUFBTSxlQUFlLEdBQUcsSUFBSSxHQUFHLEVBQWlELENBQUM7QUFDakYsZ0ZBQWdGO0FBQ2hGLElBQUksZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDO0FBQ3pCLE1BQU0sZUFBZSxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3RDLHNGQUFzRjtBQUN0RixNQUFNLGVBQWUsR0FBRyxFQUFFLENBQUM7QUFDM0Isa0ZBQWtGO0FBQ2xGLE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxDQUFDO0FBRWxDLCtFQUErRTtBQUMvRSxvRkFBb0Y7QUFDcEYsc0ZBQXNGO0FBQ3RGLHNGQUFzRjtBQUN0Riw2RUFBNkU7QUFDN0UsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLEdBQUcsRUFBeUIsQ0FBQztBQUUxRDs7Ozs7O0dBTUc7QUFDSCxTQUFTLHFCQUFxQixDQUFDLEtBQW9CO0lBQ2pELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLENBQUM7SUFDOUMsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDM0QsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQywwQkFBMEI7SUFDM0UsSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUNWLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDcEMsTUFBTSxNQUFNLEdBQUcsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUN2RCxPQUFPLEdBQUcsTUFBTSxJQUFJLEtBQUssZUFBZSxDQUFDO0lBQzNDLENBQUM7SUFDRCxPQUFPLEdBQUcsSUFBSSxlQUFlLENBQUM7QUFDaEMsQ0FBQztBQUVELDBGQUEwRjtBQUMxRixLQUFLLFVBQVUsY0FBYyxDQUFDLEtBQW9CO0lBQ2hELE1BQU0sR0FBRyxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUM7SUFDeEIsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3pDLElBQUksTUFBTSxLQUFLLFNBQVM7UUFBRSxPQUFPLE1BQU0sQ0FBQztJQUN4QyxNQUFNLFVBQVUsR0FBRyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNoRCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQUMsT0FBTyxJQUFJLENBQUM7SUFBQyxDQUFDO0lBQ2xFLElBQUksS0FBb0IsQ0FBQztJQUN6QixJQUFJLENBQUM7UUFDSCxNQUFNLE1BQU0sR0FBRyxJQUFJLG9CQUFvQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sR0FBRyxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLHFCQUFxQixDQUFDLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNuRixnRkFBZ0Y7UUFDaEYseUNBQXlDO1FBQ3pDLEtBQUssR0FBRyxHQUFHLENBQUMsWUFBWSxJQUFJLEdBQUcsQ0FBQyxZQUFZLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQzNGLENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ2IsR0FBRyxDQUFDLElBQUksQ0FBQywyRUFBMkUsRUFBRSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUcsR0FBeUIsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzFJLEtBQUssR0FBRyxJQUFJLENBQUM7SUFDZixDQUFDO0lBQ0QsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNqQyxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCxLQUFLLFVBQVUsZ0JBQWdCLENBQUMsR0FBVyxFQUFFLElBQTZCO0lBQ3hFLE1BQU0sVUFBVSxHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7SUFDekMsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO0lBQ3pFLElBQUksQ0FBQztRQUNILE9BQU8sTUFBTSxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7WUFBUyxDQUFDO1FBQ1QsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3RCLENBQUM7QUFDSCxDQUFDO0FBRUQ7d0VBQ3dFO0FBQ3hFLFNBQVMsS0FBSyxDQUFDLEtBQWM7SUFDM0IsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDdEUsTUFBTSxLQUFLLEdBQUcsYUFBYSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMvQyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQ1YsTUFBTSxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQzVDLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDakUsQ0FBQztJQUNELE1BQU0sQ0FBQyxHQUFHLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzFCLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7QUFDakUsQ0FBQztBQUVELHlFQUF5RTtBQUN6RSxTQUFTLGNBQWMsQ0FBQyxHQUF1QjtJQUM3QyxJQUFJLENBQUMsR0FBRztRQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDekMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdkIsTUFBTSxJQUFJLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN0QyxJQUFJLElBQUksS0FBSyxZQUFZLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO1lBQzFELE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNwRCxJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksQ0FBQztnQkFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQzlHLENBQUM7UUFDRCxJQUFJLElBQUksS0FBSyxlQUFlLEVBQUUsQ0FBQztZQUM3QixNQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDcEQsSUFBSSxLQUFLLENBQUMsTUFBTSxJQUFJLENBQUM7Z0JBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUNqSCxDQUFDO1FBQ0QsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLENBQUM7WUFDNUksTUFBTSxDQUFDLEdBQUcsK0JBQStCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxRQUFRLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3BFLElBQUksQ0FBQztnQkFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxjQUFjLEVBQUUsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNyRixDQUFDO0lBQ0gsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLHNCQUFzQjtJQUN4QixDQUFDO0lBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNqQyxDQUFDO0FBRUQsSUFBSSx5QkFBZ0csQ0FBQztBQUtyRyxJQUFJLGFBQTJDLENBQUM7QUFDaEQsS0FBSyxVQUFVLGdCQUFnQixDQUFDLE1BQWM7SUFDNUMsSUFBSSxDQUFDLGFBQWE7UUFBRSxhQUFhLEdBQUcsTUFBTSxPQUFPLENBQW1CLDRCQUE0QixDQUFDLENBQUM7SUFDbEcsSUFBSSxDQUFDLHlCQUF5QjtRQUFFLHlCQUF5QixHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7SUFDdEUsSUFBSSxNQUFNLEdBQUcseUJBQXlCLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ25ELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE1BQU0sR0FBRyxJQUFJLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDeEQseUJBQXlCLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNoRCxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELHNGQUFzRjtBQUN0RixLQUFLLFVBQVUsY0FBYyxDQUFDLE1BQWMsRUFBRSxjQUFzQixFQUFFLFFBQWdCO0lBQ3BGLE1BQU0sTUFBTSxHQUFHLGVBQWUsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDN0MsSUFBSSxNQUFNO1FBQUUsT0FBTyxNQUFNLENBQUM7SUFDMUIsSUFBSSxDQUFDLGFBQWE7UUFBRSxhQUFhLEdBQUcsTUFBTSxPQUFPLENBQW1CLDRCQUE0QixDQUFDLENBQUM7SUFDbEcsTUFBTSxNQUFNLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM5QyxNQUFNLEdBQUcsR0FBRyxNQUFNLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxhQUFhLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxjQUFjLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FFN0YsQ0FBQztJQUNGLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUM7SUFDMUIsTUFBTSxJQUFJLEdBQUcsRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxTQUFTLEVBQUUsSUFBSSxJQUFJLE1BQU0sRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsQ0FBQztJQUN4RyxlQUFlLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNwQyxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxLQUFLLFVBQVUsaUJBQWlCLENBQUMsTUFBYyxFQUFFLGNBQXNCLEVBQUUsR0FBVyxFQUFFLFFBQWlCO0lBQ3JHLElBQUksR0FBRyxHQUF1QixHQUFHLENBQUM7SUFDbEMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2QsSUFBSSxNQUEwQixDQUFDO0lBQy9CLElBQUksU0FBUyxHQUFHLEtBQUssQ0FBQztJQUN0QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxHQUFHO1lBQUUsTUFBTSxDQUFDLG9EQUFvRDtRQUNyRSxJQUFJLFFBQVEsSUFBSSxHQUFHLEtBQUssUUFBUTtZQUFFLE1BQU0sQ0FBQyxvQ0FBb0M7UUFDN0UsSUFBSSxDQUFDLElBQUksZUFBZSxFQUFFLENBQUM7WUFBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1lBQUMsTUFBTTtRQUFDLENBQUMsQ0FBQyxrQ0FBa0M7UUFDekYsTUFBTSxJQUFJLEdBQUcsTUFBTSxjQUFjLENBQUMsTUFBTSxFQUFFLGNBQWMsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUk7WUFBRSxNQUFNO1FBQ3ZCLEtBQUssRUFBRSxDQUFDO1FBQ1IsTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDLFFBQVE7WUFBRSxNQUFNLENBQUMsMkJBQTJCO1FBQ2pELEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUNELGdGQUFnRjtJQUNoRixtRkFBbUY7SUFDbkYsOERBQThEO0lBQzlELElBQUksU0FBUztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBQ3pCLE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7QUFDdkUsQ0FBQztBQUVELEtBQUssVUFBVSxhQUFhLENBQUMsS0FBYSxFQUFFLElBQVksRUFBRSxHQUFXLEVBQUUsUUFBNEIsRUFBRSxLQUFvQjtJQUN2SCxNQUFNLEtBQUssR0FBRyxNQUFNLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMxQyxNQUFNLE9BQU8sR0FBMkI7UUFDdEMsUUFBUSxFQUFFLDZCQUE2QjtRQUN2QyxZQUFZLEVBQUUsNEJBQTRCO1FBQzFDLHNCQUFzQixFQUFFLFlBQVk7S0FDckMsQ0FBQztJQUNGLElBQUksS0FBSztRQUFFLE9BQU8sQ0FBQyxhQUFhLEdBQUcsVUFBVSxLQUFLLEVBQUUsQ0FBQztJQUNyRCxNQUFNLFdBQVcsR0FBRyxDQUFDLENBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxDQUFDLE1BQU0sS0FBSyxHQUFHLENBQUM7SUFFMUUsSUFBSSxRQUFRLEVBQUUsQ0FBQztRQUNiLE1BQU0sQ0FBQyxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsZ0NBQWdDLEtBQUssSUFBSSxJQUFJLFlBQVksUUFBUSxNQUFNLEdBQUcsRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM1SCxJQUFJLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLGVBQWUsQ0FBQztZQUFDLE9BQU8sRUFBRSxDQUFDO1FBQUMsQ0FBQztRQUNuRixJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNULE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBcUcsQ0FBQztZQUM1SCxNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFELElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDdkIsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztnQkFDOUYsSUFBSSxNQUFNO29CQUFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDOUUsQ0FBQztRQUNILENBQUM7UUFDRCwyQ0FBMkM7SUFDN0MsQ0FBQztJQUNELE1BQU0sRUFBRSxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsZ0NBQWdDLEtBQUssSUFBSSxJQUFJLFlBQVksR0FBRyxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQy9HLElBQUksV0FBVyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsZUFBZSxDQUFDO1FBQUMsT0FBTyxFQUFFLENBQUM7SUFBQyxDQUFDO0lBQ3BGLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ1YsTUFBTSxDQUFDLEdBQUcsTUFBTSxFQUFFLENBQUMsSUFBSSxFQUFnRixDQUFDO1FBQ3hHLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxJQUFJLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDckUsSUFBSSxDQUFDO1lBQUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxDQUFDLEVBQUUsV0FBVyxFQUFFLENBQUMsRUFBRSxDQUFDO0lBQ3ZELENBQUM7SUFDRCxPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUM7QUFFRCxLQUFLLFVBQVUsZ0JBQWdCLENBQUMsS0FBYSxFQUFFLElBQVksRUFBRSxHQUFXO0lBQ3RFLE1BQU0sQ0FBQyxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsOENBQThDLEtBQUssSUFBSSxJQUFJLFdBQVcsR0FBRyxFQUFFLEVBQUU7UUFDNUcsT0FBTyxFQUFFLEVBQUUsWUFBWSxFQUFFLDRCQUE0QixFQUFFLFFBQVEsRUFBRSxrQkFBa0IsRUFBRTtLQUN0RixDQUFDLENBQUM7SUFDSCxJQUFJLENBQUMsQ0FBQyxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUM7UUFBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsZUFBZSxDQUFDO1FBQUMsT0FBTyxFQUFFLENBQUM7SUFBQyxDQUFDO0lBQ3JGLElBQUksQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ1QsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUF1QixDQUFDO1FBQzlDLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsSUFBSSxDQUFDO1lBQUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxDQUFDLEVBQUUsV0FBVyxFQUFFLENBQUMsRUFBRSxDQUFDO0lBQ3ZELENBQUM7SUFDRCxPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsS0FBSyxVQUFVLGlCQUFpQixDQUFDLFVBQWtCLEVBQUUsS0FBb0IsRUFBRSxNQUFjLEVBQUUsR0FBYztJQUN2RyxJQUFJLEdBQUcsQ0FBQyxRQUFRLEtBQUssU0FBUztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBQzFDLE1BQU0sSUFBSSxHQUFHLEdBQUcsVUFBVSxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN4QyxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0MsSUFBSSxNQUFNO1FBQUUsT0FBTyxNQUFNLENBQUM7SUFFMUIsTUFBTSxJQUFJLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sUUFBUSxHQUFHLElBQUksSUFBSSxJQUFJLEtBQUssR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFFN0QsSUFBSSxJQUFJLEdBQWUsRUFBRSxDQUFDO0lBQzFCLElBQUksQ0FBQztRQUNILElBQUksR0FBRyxDQUFDLFFBQVEsS0FBSyxZQUFZLElBQUksR0FBRyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3hELElBQUksR0FBRyxNQUFNLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsY0FBYyxFQUFFLEdBQUcsQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDaEYsQ0FBQzthQUFNLElBQUksR0FBRyxDQUFDLFFBQVEsS0FBSyxRQUFRLElBQUksR0FBRyxDQUFDLEtBQUssSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDOUQsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksZ0JBQWdCO2dCQUFFLElBQUksR0FBRyxNQUFNLGFBQWEsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxRQUFRLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDaEgsQ0FBQzthQUFNLElBQUksR0FBRyxDQUFDLFFBQVEsS0FBSyxXQUFXLElBQUksR0FBRyxDQUFDLEtBQUssSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDakUsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksZ0JBQWdCO2dCQUFFLElBQUksR0FBRyxNQUFNLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbEcsQ0FBQztJQUNILENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ2IsR0FBRyxDQUFDLElBQUksQ0FBQyxpRUFBaUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxHQUFHLENBQUMsUUFBUSxFQUFFLEtBQUssRUFBRyxHQUF5QixFQUFFLElBQUksSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ2hLLElBQUksR0FBRyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQsaUJBQWlCLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNsQyxJQUFJLElBQUksQ0FBQyxlQUFlO1FBQUUsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDckUsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBMENELGtGQUFrRjtBQUNsRixNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQztBQUUvQiw2RUFBNkU7QUFDN0UsU0FBUyxTQUFTLENBQUMsR0FBRyxJQUFlO0lBQ25DLEtBQUssTUFBTSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7UUFDckIsSUFBSSxPQUFPLENBQUMsS0FBSyxRQUFRLElBQUksQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDO1lBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsU0FBUyxhQUFhLENBQUMsTUFBK0I7SUFDcEQscUZBQXFGO0lBQ3JGLE1BQU0sSUFBSSxHQUFHLENBQUMsTUFBTSxDQUFDLGtCQUFrQixDQUFDLElBQUksTUFBTSxDQUFDLGVBQWUsQ0FDcEIsQ0FBQztJQUMvQyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUMzRCx5RUFBeUU7SUFDekUsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLGtCQUFrQixDQUF3QyxDQUFDO0lBRXJGLE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FDekIsTUFBTSxDQUFDLFFBQVEsRUFDZixNQUFNLENBQUMsVUFBVSxFQUNqQixRQUFRLEVBQUUsVUFBVSxFQUNwQixVQUFVLEVBQUUsVUFBVSxDQUN2QixDQUFDO0lBQ0YsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUN6QixNQUFNLENBQUMsU0FBUyxFQUNoQixNQUFNLENBQUMsTUFBTSxFQUNiLE1BQU0sQ0FBQyxZQUFZLEVBQ25CLFFBQVEsRUFBRSxVQUFVLEVBQ3BCLFFBQVEsRUFBRSxNQUFNLENBQ2pCLENBQUM7SUFDRixNQUFNLFdBQVcsR0FBRyxPQUFPLFFBQVEsRUFBRSxXQUFXLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFFakcsT0FBTyxFQUFFLEdBQUcsQ0FBQyxTQUFTLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxTQUFTLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxXQUFXLElBQUksRUFBRSxXQUFXLEVBQUUsQ0FBQyxFQUFFLENBQUM7QUFDbkgsQ0FBQztBQUVELFNBQVMsYUFBYSxDQUFDLFVBQWtCO0lBQ3ZDLElBQUksVUFBVSxDQUFDLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQztRQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsQ0FBQztJQUM3RyxJQUFJLFVBQVUsQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUM7UUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLENBQUM7SUFDdkcsSUFBSSxVQUFVLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFDO1FBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLGNBQWMsRUFBRSxDQUFDO0lBQ3pHLGdGQUFnRjtJQUNoRiwrRUFBK0U7SUFDL0UseUVBQXlFO0lBQ3pFLDJFQUEyRTtJQUMzRSxtREFBbUQ7SUFDbkQsSUFBSSxVQUFVLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQztRQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsQ0FBQztJQUNoRyxPQUFPLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLENBQUM7QUFDaEUsQ0FBQztBQUVELDRFQUE0RTtBQUM1RSxLQUFLLFVBQVUsV0FBVyxDQUFDLE1BQWlCO0lBQzFDLElBQUksS0FPSCxDQUFDO0lBQ0YsOEVBQThFO0lBQzlFLDZFQUE2RTtJQUM3RSw4RUFBOEU7SUFDOUUsSUFBSSxDQUFDO1FBQ0gsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ2IsR0FBRyxDQUFDLElBQUksQ0FBQywyQ0FBMkMsRUFBRSxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzNHLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE1BQU0sRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLEdBQUcsYUFBYSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO0lBQ3ZFLE1BQU0sTUFBTSxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7SUFDbkMsMkVBQTJFO0lBQzNFLE9BQU8sTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUV0Qiw2RUFBNkU7SUFDN0UsOEVBQThFO0lBQzlFLGtDQUFrQztJQUNsQyxJQUFJLFdBQVcsS0FBSyxjQUFjLEVBQUUsQ0FBQztRQUNuQyxHQUFHLENBQUMsSUFBSSxDQUFDLDhEQUE4RCxFQUFFLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDL0csT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLFFBQThCLENBQUM7SUFDM0QsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ2xCLEdBQUcsQ0FBQyxJQUFJLENBQUMscURBQXFELENBQUMsQ0FBQztRQUNoRSxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxrRkFBa0Y7SUFDbEYsNkVBQTZFO0lBQzdFLE1BQU0sR0FBRyxHQUFHLHdCQUF3QixLQUFLLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxPQUFPLElBQUksWUFBWSxFQUFFLENBQUM7SUFDcEYsTUFBTSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLEdBQUcsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNoRixJQUFJLENBQUMsVUFBVTtRQUFFLE9BQU8sSUFBSSxDQUFDLENBQUMsaUNBQWlDO0lBRS9ELE1BQU0sU0FBUyxHQUFJLE1BQU0sQ0FBQyxZQUFZLENBQVksSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDO0lBQ2pFLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxLQUFlLENBQUM7SUFFckMsSUFBSSxVQUE4QixDQUFDO0lBQ25DLElBQUksV0FBK0IsQ0FBQztJQUVwQyxJQUFJLENBQUMsV0FBVyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDbkUsV0FBVyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUM7UUFDekIsSUFBSSxTQUFTLElBQUksV0FBVyxFQUFFLENBQUM7WUFDN0IsTUFBTSxFQUFFLEdBQUcsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDM0UsSUFBSSxFQUFFLElBQUksQ0FBQztnQkFBRSxVQUFVLEdBQUcsRUFBRSxDQUFDO1FBQy9CLENBQUM7SUFDSCxDQUFDO0lBRUQsNkVBQTZFO0lBQzdFLDhFQUE4RTtJQUM5RSw0QkFBNEI7SUFDNUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLGtCQUFrQixDQUEyRCxDQUFDO0lBQ3BHLE1BQU0sT0FBTyxHQUFHLE1BQU0sRUFBRSxDQUFDLDRCQUE0QixDQUFDLENBQUM7SUFDdkQsTUFBTSxZQUFZLEdBQUcsT0FBTyxPQUFPLEtBQUssUUFBUSxJQUFJLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUNwRSxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsaUJBQWlCLENBQUM7UUFDckMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUVkLHVFQUF1RTtJQUN2RSxxQ0FBcUM7SUFDckMsTUFBTSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRXBFLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQXVCLENBQUM7SUFDakUsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLEtBQTJCLENBQUM7SUFDckQsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLE1BQTRCLENBQUM7SUFFdkQsZ0ZBQWdGO0lBQ2hGLDZFQUE2RTtJQUM3RSxNQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUVuRSxrRkFBa0Y7SUFDbEYsMkVBQTJFO0lBQzNFLG9GQUFvRjtJQUNwRixvRkFBb0Y7SUFDcEYsK0VBQStFO0lBQy9FLElBQUksZUFBbUMsQ0FBQztJQUN4QyxJQUFJLFdBQStCLENBQUM7SUFDcEMsSUFBSSxTQUFTLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLEtBQUssTUFBTSxFQUFFLENBQUM7UUFDckQsTUFBTSxHQUFHLEdBQUcsY0FBYyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3hDLE1BQU0sSUFBSSxHQUFHLE1BQU0saUJBQWlCLENBQUMsVUFBVSxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDbEcsZUFBZSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUM7UUFDdkMsV0FBVyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7SUFDakMsQ0FBQztJQUVELE9BQU87UUFDTCxLQUFLO1FBQ0wsVUFBVTtRQUNWLFdBQVc7UUFDWCxTQUFTO1FBQ1QsTUFBTSxFQUFFLEtBQUs7UUFDYixXQUFXO1FBQ1gsU0FBUztRQUNULFVBQVU7UUFDVixZQUFZO1FBQ1osU0FBUztRQUNULFdBQVc7UUFDWCxVQUFVO1FBQ1YsR0FBRyxDQUFDLFNBQVMsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQy9CLEdBQUcsQ0FBQyxTQUFTLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQztRQUMvQixHQUFHLENBQUMsZUFBZSxJQUFJLEVBQUUsZUFBZSxFQUFFLENBQUM7UUFDM0MsR0FBRyxDQUFDLFdBQVcsSUFBSSxFQUFFLFdBQVcsRUFBRSxDQUFDO1FBQ25DLEdBQUcsQ0FBQyxXQUFXLElBQUksRUFBRSxXQUFXLEVBQUUsQ0FBQztRQUNuQyxNQUFNO0tBQ1AsQ0FBQztBQUNKLENBQUM7QUF5QkQsSUFBSSxNQUE2QixDQUFDO0FBQ2xDLElBQUksa0JBQTBELENBQUM7QUFDL0QsS0FBSyxVQUFVLFNBQVMsQ0FBQyxNQUFjO0lBQ3JDLElBQUksQ0FBQyxNQUFNO1FBQUUsTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFZLHFCQUFxQixDQUFDLENBQUM7SUFDdEUsSUFBSSxDQUFDLGtCQUFrQjtRQUFFLGtCQUFrQixHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7SUFDeEQsSUFBSSxNQUFNLEdBQUcsa0JBQWtCLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzVDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQzFDLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRCxxRkFBcUY7QUFDckYsa0ZBQWtGO0FBQ2xGLG1GQUFtRjtBQUNuRixvRkFBb0Y7QUFDcEYsc0ZBQXNGO0FBQ3RGLE1BQU0sY0FBYyxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0FBQ2pELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7QUFDbkQsSUFBSSxZQUFZLEdBQUcsQ0FBQyxDQUFDO0FBQ3JCLElBQUksYUFBYSxHQUFHLENBQUMsQ0FBQztBQUN0QixNQUFNLGtCQUFrQixHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFDckMsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUMxQyxnRkFBZ0Y7QUFDaEYscUZBQXFGO0FBQ3JGLHdFQUF3RTtBQUN4RSxNQUFNLHVCQUF1QixHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsVUFBVTtBQUV2RCxnRkFBZ0Y7QUFDaEYsU0FBUyxZQUFZLENBQUMsWUFBZ0M7SUFDcEQsSUFBSSxDQUFDLFlBQVk7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUNwQyx3Q0FBd0M7SUFDeEMsTUFBTSxLQUFLLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN0QyxJQUFJLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDN0QsTUFBTSxDQUFDLEVBQUUsQUFBRCxFQUFHLEFBQUQsRUFBRyxNQUFNLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLEtBQUssQ0FBQztJQUM1QyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsSUFBSSxlQUFlLE1BQU0sSUFBSSxPQUFPLElBQUksSUFBSSxNQUFNLENBQUM7SUFDM0YsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsSUFBSSxHQUFHLElBQUksTUFBTSxDQUFDO0lBQ3pELE1BQU0sTUFBTSxHQUFHLGVBQWUsTUFBTSxrQkFBa0IsT0FBTyxJQUFJLE9BQU8sRUFBRSxDQUFDO0lBQzNFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUM7QUFDM0QsQ0FBQztBQUVEOzhFQUM4RTtBQUM5RSxLQUFLLFVBQVUsUUFBUSxDQUFDLE1BQWMsRUFBRSxNQUFjO0lBQ3BELElBQUksQ0FBQyxNQUFNO1FBQUUsTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFZLHFCQUFxQixDQUFDLENBQUM7SUFDdEUsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkMsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksTUFBTSxDQUFDLHlCQUF5QixDQUFDO1FBQ2pFLFFBQVEsRUFBRSxNQUFNO1FBQ2hCLGNBQWMsRUFBRSxDQUFDLDZCQUE2QixFQUFFLCtCQUErQixDQUFDO0tBQ2pGLENBQUMsQ0FBc0csQ0FBQztJQUN6RyxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSwyQkFBMkIsSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNyRSxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSw2QkFBNkIsSUFBSSxHQUFHLENBQUMsQ0FBQztJQUN6RSxPQUFPO1FBQ0wsS0FBSyxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNqQyxZQUFZLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQzdDLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLGlCQUFpQixDQUFDLE1BQWMsRUFBRSxNQUFjO0lBQzdELElBQUksQ0FBQyxNQUFNO1FBQUUsTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFZLHFCQUFxQixDQUFDLENBQUM7SUFDdEUsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkMsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksTUFBTSxDQUFDLDJCQUEyQixDQUFDLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBRTFGLENBQUM7SUFDRixPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxLQUFLLFNBQVMsQ0FBQyxDQUFDO0FBQy9ELENBQUM7QUFFRCxLQUFLLFVBQVUsYUFBYSxDQUFDLE1BQWMsRUFBRSxNQUFjLEVBQUUsT0FBZTtJQUMxRSxJQUFJLENBQUMsTUFBTTtRQUFFLE1BQU0sR0FBRyxNQUFNLE9BQU8sQ0FBWSxxQkFBcUIsQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sTUFBTSxHQUFHLE1BQU0sU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3ZDLE1BQU0sTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQywyQkFBMkIsQ0FBQyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztBQUM1RyxDQUFDO0FBRUQ7OzRFQUU0RTtBQUM1RSxLQUFLLFVBQVUsZ0JBQWdCLENBQUMsT0FBZSxFQUFFLElBQWtGO0lBQ2pJLElBQUksQ0FBQztRQUNILE1BQU0sS0FBSyxHQUFHLE1BQU0sWUFBWSxFQUFFLENBQUM7UUFDbkMsTUFBTSxHQUFHLEdBQUcsTUFBTSxLQUFLLENBQUMsR0FBRyxPQUFPLDRCQUE0QixFQUFFO1lBQzlELE1BQU0sRUFBRSxNQUFNO1lBQ2QsT0FBTyxFQUFFLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFLGVBQWUsRUFBRSxVQUFVLEtBQUssRUFBRSxFQUFFO1lBQ25GLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztTQUMzQixDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ1osR0FBRyxDQUFDLElBQUksQ0FBQywrQ0FBK0MsRUFBRSxFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUNyRyxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ2IsR0FBRyxDQUFDLElBQUksQ0FBQyxxQ0FBcUMsRUFBRSxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQzNGLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUFlLEVBQUUsWUFBZ0M7SUFDckYsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLElBQUksR0FBRyxHQUFHLFlBQVksR0FBRyxrQkFBa0I7UUFBRSxPQUFPO0lBQ3BELFlBQVksR0FBRyxHQUFHLENBQUM7SUFDbkIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxDQUFDLEdBQUcsWUFBWSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ3JDLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztRQUNkLElBQUksWUFBWSxHQUFHLENBQUMsQ0FBQztRQUNyQixJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ04sSUFBSSxDQUFDO2dCQUNILE1BQU0sS0FBSyxHQUFHLE1BQU0sUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNqRCxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQztnQkFDcEIsWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUM7WUFDcEMsQ0FBQztZQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7Z0JBQ2IsR0FBRyxDQUFDLElBQUksQ0FBQyxrQ0FBa0MsRUFBRSxFQUFFLEtBQUssRUFBRyxHQUF5QixFQUFFLElBQUksSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzNHLENBQUM7UUFDSCxDQUFDO1FBRUQsaUZBQWlGO1FBQ2pGLCtFQUErRTtRQUMvRSxzRUFBc0U7UUFDdEUsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLGNBQWMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFHLGdCQUFnQixDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNoRixJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQztZQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyw2Q0FBNkM7UUFDdEYsS0FBSyxNQUFNLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUMxQixNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMvQyxNQUFNLFdBQVcsR0FBRyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDOUMsTUFBTSxFQUFFLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxPQUFPLEVBQUU7Z0JBQ3pDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQzlCLFNBQVM7Z0JBQ1QsT0FBTyxFQUFFLEtBQUs7Z0JBQ2QsV0FBVzthQUNaLENBQUMsQ0FBQztZQUNILDhFQUE4RTtZQUM5RSx1RUFBdUU7WUFDdkUsSUFBSSxFQUFFO2dCQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLENBQUM7UUFFRCxJQUFJLENBQUMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxJQUFJLEdBQUcsR0FBRyxhQUFhLElBQUksbUJBQW1CLEVBQUUsQ0FBQztZQUNqRSw0RUFBNEU7WUFDNUUsNkVBQTZFO1lBQzdFLElBQUksWUFBWSxHQUFHLHVCQUF1QixFQUFFLENBQUM7Z0JBQzNDLEdBQUcsQ0FBQyxJQUFJLENBQUMsMkZBQTJGLEVBQUU7b0JBQ3BHLE1BQU0sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSx1QkFBdUI7aUJBQ3BGLENBQUMsQ0FBQztZQUNMLENBQUM7aUJBQU0sQ0FBQztnQkFDTixJQUFJLENBQUM7b0JBQ0gsSUFBSSxDQUFDLENBQUMsTUFBTSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUM7d0JBQ25ELGFBQWEsR0FBRyxHQUFHLENBQUM7d0JBQ3BCLE1BQU0sYUFBYSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUM7d0JBQ25ELEdBQUcsQ0FBQyxJQUFJLENBQUMsdUNBQXVDLEVBQUUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUM7b0JBQ3hHLENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO29CQUNiLEdBQUcsQ0FBQyxJQUFJLENBQUMsOEJBQThCLEVBQUUsRUFBRSxLQUFLLEVBQUcsR0FBeUIsRUFBRSxJQUFJLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDdkcsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixHQUFHLENBQUMsSUFBSSxDQUFDLHNDQUFzQyxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDM0UsQ0FBQztBQUNILENBQUM7QUFFRCwyREFBMkQ7QUFFM0QsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLEtBQUssRUFBRSxLQUFlLEVBQWlCLEVBQUU7SUFDOUQsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQztJQUM5QyxJQUFJLENBQUMsT0FBTztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsb0RBQW9ELENBQUMsQ0FBQztJQUVwRiw0RUFBNEU7SUFDNUUsa0ZBQWtGO0lBQ2xGLG1GQUFtRjtJQUNuRixrRkFBa0Y7SUFDbEYsZ0RBQWdEO0lBQ2hELE1BQU0sTUFBTSxHQUFHLENBQUMsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7U0FDL0QsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFvQixFQUFFLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyxDQUFDO0lBQy9DLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4QixHQUFHLENBQUMsSUFBSSxDQUFDLDRDQUE0QyxDQUFDLENBQUM7UUFDdkQsT0FBTztJQUNULENBQUM7SUFFRCwwRUFBMEU7SUFDMUUsMEVBQTBFO0lBQzFFLGtGQUFrRjtJQUNsRiw2RUFBNkU7SUFDN0UsbUNBQW1DO0lBQ25DLE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdkUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxLQUFhLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLE9BQU8scUJBQXFCLEVBQUU7UUFDckUsTUFBTSxFQUFFLE1BQU07UUFDZCxPQUFPLEVBQUUsRUFBRSxjQUFjLEVBQUUsa0JBQWtCLEVBQUUsZUFBZSxFQUFFLFVBQVUsS0FBSyxFQUFFLEVBQUU7UUFDbkYsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLENBQUM7S0FDaEQsQ0FBQyxDQUFDO0lBQ0gsSUFBSSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxZQUFZLEVBQUUsQ0FBQyxDQUFDO0lBQzNDLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUM3QyxHQUFHLENBQUMsSUFBSSxDQUFDLCtEQUErRCxFQUFFLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ2xHLFdBQVcsR0FBRyxJQUFJLENBQUM7UUFDbkIsR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sWUFBWSxFQUFFLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNaLE1BQU0sSUFBSSxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUM5QyxHQUFHLENBQUMsS0FBSyxDQUFDLDBCQUEwQixHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFRCw2RUFBNkU7SUFDN0UsMkVBQTJFO0lBQzNFLGdGQUFnRjtJQUNoRixpRkFBaUY7SUFDakYsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQTRCLENBQUM7SUFDN0UsR0FBRyxDQUFDLElBQUksQ0FBQyxZQUFZLE1BQU0sQ0FBQyxNQUFNLFNBQVMsRUFBRSxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUV4RSwrRUFBK0U7SUFDL0UsbUZBQW1GO0lBQ25GLEtBQUssTUFBTSxDQUFDLElBQUksTUFBTSxFQUFFLENBQUM7UUFDdkIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDMUIsY0FBYyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQzVELE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxXQUFXLElBQUksQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUN4QyxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQ1AsTUFBTSxJQUFJLEdBQUcsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3ZDLElBQUksQ0FBQyxJQUFJLElBQUksRUFBRSxHQUFHLElBQUk7Z0JBQUUsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUN4RCxDQUFDO0lBQ0gsQ0FBQztJQUNELE1BQU0sc0JBQXNCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsY0FBYyxDQUFDLENBQUM7QUFDMUUsQ0FBQyxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLGNBQWM7SUFDNUIsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3RCLGlCQUFpQixDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzFCLGlCQUFpQixDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzFCLGVBQWUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixnQkFBZ0IsR0FBRyxDQUFDLENBQUM7SUFDckIsZ0JBQWdCLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDekIsY0FBYyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3ZCLGdCQUFnQixDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3pCLFlBQVksR0FBRyxDQUFDLENBQUM7SUFDakIsYUFBYSxHQUFHLENBQUMsQ0FBQztJQUNsQixXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ3JCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgR2V0U2VjcmV0VmFsdWVDb21tYW5kLCBTZWNyZXRzTWFuYWdlckNsaWVudCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXInO1xuaW1wb3J0IHR5cGUgeyBTUVNFdmVudCwgU1FTUmVjb3JkIH0gZnJvbSAnYXdzLWxhbWJkYSc7XG5cbi8vIEFXUyBMYW1iZGEncyBOb2RlIHJ1bnRpbWUgYnVuZGxlcyBAYXdzLXNkayB2Mywgc28gdGhlIENvZGVQaXBlbGluZS9Db2RlQ29tbWl0L1NRU1xuLy8gY2xpZW50cyBhcmUgbG9hZGVkIGxhemlseSB2aWEgZHluYW1pYyBpbXBvcnQoKSDigJQgYSBzdGF0aWMgd29ya3NwYWNlIGRlcGVuZGVuY3kgb25cbi8vIHRoZW0gd291bGQgcGVydHVyYiB0aGUgc2hhcmVkIEBhd3Mtc2RrIHZlcnNpb24gdHJlZSBhbmQgYnJlYWsgb3RoZXIgcGFja2FnZXMnXG4vLyB0eXBpbmdzLiBUaGUgZHluYW1pYyBpbXBvcnQga2VlcHMgdGhlbSBvdXQgb2YgdGhlIHN0YXRpYyBncmFwaCB3aGlsZSBzdGF5aW5nXG4vLyBFU00tY29ycmVjdCAobm8gYHJlcXVpcmVgIGluIGEgdHlwZTptb2R1bGUgcGFja2FnZSkgYW5kIG1vY2thYmxlLiBNaW5pbWFsIGxvY2FsXG4vLyB0eXBlcyBrZWVwIHRoZSBjYWxsIHNpdGVzIHR5cGUtY2hlY2tlZC4gYEBhd3Mtc2RrL2NsaWVudC1jb2RlY29tbWl0YCBhbmRcbi8vIGBAYXdzLXNkay9jbGllbnQtc3FzYCBhcmUgTk9UIGRldkRlcGVuZGVuY2llcyBvZiB0aGlzIHBhY2thZ2UgKHRoZXkgcmVzb2x2ZSBvbmx5XG4vLyBpbnNpZGUgdGhlIExhbWJkYSBydW50aW1lIC8gamVzdCBtb2NrcyksIHNvIHRoZXkgYXJlIGltcG9ydGVkIHRocm91Z2ggYVxuLy8gdmFyaWFibGUtc3BlY2lmaWVyIGhlbHBlciBiZWxvdyBzbyBgdHNjYCBkb2VzIG5vdCB0cnkgdG8gcmVzb2x2ZSB0aGVtLlxuaW50ZXJmYWNlIExpc3RUYWdzT3V0cHV0IHsgdGFncz86IEFycmF5PHsga2V5Pzogc3RyaW5nOyB2YWx1ZT86IHN0cmluZyB9PiB9XG5pbnRlcmZhY2UgQ29kZVBpcGVsaW5lQ2xpZW50TGlrZSB7IHNlbmQoY29tbWFuZDogdW5rbm93bik6IFByb21pc2U8TGlzdFRhZ3NPdXRwdXQ+IH1cbmludGVyZmFjZSBDb2RlUGlwZWxpbmVNb2R1bGUge1xuICBDb2RlUGlwZWxpbmVDbGllbnQ6IG5ldyAoY29uZmlnOiB7IHJlZ2lvbjogc3RyaW5nIH0pID0+IENvZGVQaXBlbGluZUNsaWVudExpa2U7XG4gIExpc3RUYWdzRm9yUmVzb3VyY2VDb21tYW5kOiBuZXcgKGlucHV0OiB7IHJlc291cmNlQXJuOiBzdHJpbmcgfSkgPT4gdW5rbm93bjtcbn1cbmxldCBjb2RlcGlwZWxpbmVNb2Q6IENvZGVQaXBlbGluZU1vZHVsZSB8IHVuZGVmaW5lZDtcbmFzeW5jIGZ1bmN0aW9uIGxvYWRDb2RlUGlwZWxpbmUoKTogUHJvbWlzZTxDb2RlUGlwZWxpbmVNb2R1bGU+IHtcbiAgaWYgKCFjb2RlcGlwZWxpbmVNb2QpIGNvZGVwaXBlbGluZU1vZCA9IChhd2FpdCBpbXBvcnQoJ0Bhd3Mtc2RrL2NsaWVudC1jb2RlcGlwZWxpbmUnKSkgYXMgdW5rbm93biBhcyBDb2RlUGlwZWxpbmVNb2R1bGU7XG4gIHJldHVybiBjb2RlcGlwZWxpbmVNb2Q7XG59XG5cbi8vIFZhcmlhYmxlLXNwZWNpZmllciBsb2FkZXIgZm9yIFNESyBjbGllbnRzIHRoYXQgYXJlIE5PVCBzdGF0aWMgZGV2RGVwZW5kZW5jaWVzIG9mXG4vLyB0aGlzIHBhY2thZ2UgKHByZXNlbnQgb25seSBpbiB0aGUgTGFtYmRhIHJ1bnRpbWUgLyBqZXN0IG1vY2tzKS4gUGFzc2luZyB0aGVcbi8vIHNwZWNpZmllciBhcyBhIGBzdHJpbmdgIGtlZXBzIGB0c2NgIGZyb20gcmVzb2x2aW5nIHRoZSBtb2R1bGUgYXQgYnVpbGQgdGltZS5cbmFzeW5jIGZ1bmN0aW9uIGxvYWRTZGs8VD4ocGtnOiBzdHJpbmcpOiBQcm9taXNlPFQ+IHtcbiAgcmV0dXJuIChhd2FpdCBpbXBvcnQocGtnKSkgYXMgVDtcbn1cblxuLyoqXG4gKiBQaXBlbGluZSBldmVudCBpbmdlc3Rpb24gTGFtYmRhIGhhbmRsZXIuXG4gKlxuICogUmVjZWl2ZXMgQ29kZVBpcGVsaW5lL0NvZGVCdWlsZCBldmVudHMgZnJvbSBTUVMgKHNvdXJjZWQgYnkgRXZlbnRCcmlkZ2UpLFxuICogcGFyc2VzIHRoZW0gaW50byBhIG5vcm1hbGl6ZWQgZm9ybWF0LCBhbmQgUE9TVHMgdGhlbSB0byB0aGUgcmVwb3J0aW5nIHNlcnZpY2VcbiAqIHZpYSBQTEFURk9STV9CQVNFX1VSTC5cbiAqXG4gKiBBdXRoZW50aWNhdGlvbjpcbiAqIC0gUExBVEZPUk1fVE9LRU4gZW52IHZhciAocHJlZmVycmVkIOKAlCBubyBTZWNyZXRzIE1hbmFnZXIgY2FsbClcbiAqIC0gUExBVEZPUk1fU0VDUkVUX05BTUUgZW52IHZhciDihpIgcmVhZHMgdGhlIEpXVCAocGFzc3dvcmQpIGZyb20gU2VjcmV0cyBNYW5hZ2VyXG4gKlxuICogRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBQTEFURk9STV9CQVNFX1VSTCDigJQgQmFzZSBVUkwgb2YgdGhlIHBsYXRmb3JtXG4gKiAtIFBMQVRGT1JNX1RPS0VOIOKAlCBKV1QgdG9rZW4gKHNldCBkaXJlY3RseSwgb3IpXG4gKiAtIFBMQVRGT1JNX1NFQ1JFVF9OQU1FIOKAlCBTZWNyZXRzIE1hbmFnZXIgc2VjcmV0IGNvbnRhaW5pbmcgeyBwYXNzd29yZDogPEpXVD4gfVxuICogLSBFVkVOVF9ETFFfQVJOIOKAlCAob3B0aW9uYWwpIG92ZXJyaWRlIGZvciB0aGUgZGVhZC1sZXR0ZXIgcXVldWUgQVJOIHVzZWQgYnkgdGhlXG4gKiAgIHNlbGYtaGVhbGluZyByZWRyaXZlOyBkZWZhdWx0cyB0byBgPG1haW4tcXVldWUtYXJuPi1kbHFgIGRlcml2ZWQgZnJvbSB0aGUgU1FTXG4gKiAgIHRyaWdnZXIncyBldmVudFNvdXJjZUFSTi5cbiAqL1xuXG5jb25zdCBsb2cgPSB7XG4gIGluZm86IChtc2c6IHN0cmluZywgZGF0YT86IHVua25vd24pID0+IGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgbGV2ZWw6ICdJTkZPJywgbWVzc2FnZTogbXNnLCBkYXRhLCB0czogbmV3IERhdGUoKS50b0lTT1N0cmluZygpIH0pKSxcbiAgd2FybjogKG1zZzogc3RyaW5nLCBkYXRhPzogdW5rbm93bikgPT4gY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ1dBUk4nLCBtZXNzYWdlOiBtc2csIGRhdGEsIHRzOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkgfSkpLFxuICBlcnJvcjogKG1zZzogc3RyaW5nLCBkYXRhPzogdW5rbm93bikgPT4gY29uc29sZS5lcnJvcihKU09OLnN0cmluZ2lmeSh7IGxldmVsOiAnRVJST1InLCBtZXNzYWdlOiBtc2csIGRhdGEsIHRzOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkgfSkpLFxufTtcblxuLy8g4pSA4pSAIFRhZyByZXNvbHV0aW9uOiBwYi5waXBlbGluZS1pZCArIHBiLmRlcGxveXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4vLyBQaXBlbGluZXMgYXJlIHRhZ2dlZCBhdCBDREsgc3ludGggd2l0aDpcbi8vICAgcGIucGlwZWxpbmUtaWQgPSA8cGxhdGZvcm0gcGlwZWxpbmVJZD5cbi8vICAgcGIuZGVwbG95cyAgICAgPSA8c3RhZ2U+OjxlbnY+IHBhaXJzIGpvaW5lZCBieSAnKycsIGUuZy5cbi8vICAgICAgICAgICAgICAgICAgICBcIkRlcGxveS1zdGc6c3RhZ2luZytEZXBsb3ktcHJvZDpwcm9kdWN0aW9uXCJcbi8vIFdlIHJlc29sdmUgdGhvc2UgdGFncyBmcm9tIHRoZSBsaXZlIENvZGVQaXBlbGluZSAocmVnaW9uLWF3YXJlKS4gVGhlIEFSTi9hY2NvdW50XG4vLyBuZXZlciBsZWF2ZSBBV1MsIHNvIHRoZXJlJ3Mgbm8gbWFza2luZy4gQ2FjaGVkIHBlciBBUk4gKHdhcm0tY29udGFpbmVyKSB0byBhdm9pZFxuLy8gaGFtbWVyaW5nIHRoZSBMaXN0VGFncyBBUEkuXG4vL1xuLy8gUmVxdWlyZXMgdGhlIExhbWJkYSBleGVjdXRpb24gcm9sZSB0byBhbGxvdyBgY29kZXBpcGVsaW5lOkxpc3RUYWdzRm9yUmVzb3VyY2VgLlxuLy8gQWNjZXNzRGVuaWVkIGlzIGEgY29uZmlnIGVycm9yIChhbGVydCBsb3VkbHkpOyBhIG1pc3NpbmcgcGIucGlwZWxpbmUtaWQgdGFnIGlzXG4vLyB0cmVhdGVkIGFzIFwibm90IHlldCByZWdpc3RlcmVkXCIgKHNraXApLlxuXG5jb25zdCBQSVBFTElORV9JRF9UQUcgPSAncGIucGlwZWxpbmUtaWQnO1xuY29uc3QgREVQTE9ZU19UQUcgPSAncGIuZGVwbG95cyc7XG4vLyBDYW5vbmljYWwgcGVyLXBpcGVsaW5lIG9yZyB0YWcgKGFwcGxpZWQgYXQgc3ludGggYnkgcGlwZWxpbmUtY29yZSkuIEl0IGlzIHRoZVxuLy8gUkVTT0xWRUQgb3JnIGZvciBldmVyeSBldmVudCBvZiB0aGlzIHBpcGVsaW5lIOKAlCB1c2VkIHRvIGtleSB0aGUgcGVyLW9yZyBHaXRIdWJcbi8vIHRva2VuIGFuZCB0byBhdHRyaWJ1dGUgaW5nZXN0LWhlYWx0aC4gb3JnSWQgaXMgYSBwbGF0Zm9ybSBpZCwgbmV2ZXIgYW4gQVdTXG4vLyBhY2NvdW50IGlkLCBzbyBpdCBpcyBzYWZlIHRvIHJlYWQvZm9yd2FyZC5cbmNvbnN0IE9SR19JRF9UQUcgPSAnT3JnSWQnO1xuY29uc3QgY2xpZW50c0J5UmVnaW9uID0gbmV3IE1hcDxzdHJpbmcsIENvZGVQaXBlbGluZUNsaWVudExpa2U+KCk7XG4vLyBwaXBlbGluZUlkPW51bGwgaXMgYSBuZWdhdGl2ZSBjYWNoZSAocmVzb2x2ZWQtYnV0LXVudGFnZ2VkKTsgYHRzYCBsZXRzIG5lZ2F0aXZlc1xuLy8gZXhwaXJlIHNvIGEgcGlwZWxpbmUgdGFnZ2VkIEFGVEVSIGl0cyBmaXJzdCBldmVudCBiZWNvbWVzIHJlc29sdmFibGUgd2l0aG91dFxuLy8gcmVjeWNsaW5nIHRoZSB3YXJtIGNvbnRhaW5lci4gUG9zaXRpdmUgcmVzdWx0cyBzdGF5IGNhY2hlZCBmb3IgdGhlIGxpZmV0aW1lLlxuY29uc3QgcmVzb2x2ZWRCeUFybiA9IG5ldyBNYXA8c3RyaW5nLCB7IHBpcGVsaW5lSWQ6IHN0cmluZyB8IG51bGw7IG9yZ0lkOiBzdHJpbmcgfCBudWxsOyBkZXBsb3lzOiBNYXA8c3RyaW5nLCBzdHJpbmc+OyB0czogbnVtYmVyIH0+KCk7XG5jb25zdCBORUdfQ0FDSEVfVFRMX01TID0gNSAqIDYwICogMTAwMDtcblxuLyoqIFJlc29sdmVkIHBpcGVsaW5lIG1ldGFkYXRhIHJlYWQgZnJvbSB0aGUgQ29kZVBpcGVsaW5lIHJlc291cmNlIHRhZ3MuICovXG5pbnRlcmZhY2UgUmVzb2x2ZWRQaXBlbGluZSB7XG4gIC8qKiBQbGF0Zm9ybSBwaXBlbGluZUlkIChwYi5waXBlbGluZS1pZCB0YWcpOyBudWxsIHdoZW4gdW50YWdnZWQvdW5yZWdpc3RlcmVkLiAqL1xuICBwaXBlbGluZUlkOiBzdHJpbmcgfCBudWxsO1xuICAvKiogUGxhdGZvcm0gb3JnSWQgKE9yZ0lkIHRhZyk7IG51bGwgd2hlbiB0aGUgcGlwZWxpbmUgY2FycmllcyBubyBvcmcgdGFnLiAqL1xuICBvcmdJZDogc3RyaW5nIHwgbnVsbDtcbiAgLyoqIE1hcCBvZiBkZXBsb3ktc3RhZ2UgbmFtZSDihpIgZW52aXJvbm1lbnQgKHBhcnNlZCBmcm9tIHBiLmRlcGxveXMpLiBFbXB0eSB3aGVuIG5vbmUuICovXG4gIGRlcGxveXM6IE1hcDxzdHJpbmcsIHN0cmluZz47XG59XG5cbi8qKiBDYXAgb24gY29tbWl0L3JlZi9lbnZpcm9ubWVudCBmaWVsZHMg4oCUIHRoZSBpbmdlc3QgY29udHJhY3QgYWxsb3dzIG1heCAyNTUuICovXG5jb25zdCBNQVhfQVRUUiA9IDI1NTtcblxuLyoqXG4gKiBQYXJzZSB0aGUgYHBiLmRlcGxveXNgIHRhZyB2YWx1ZSBpbnRvIGEgc3RhZ2XihpJlbnZpcm9ubWVudCBtYXAuIFRoZSB2YWx1ZSBpc1xuICogYDxzdGFnZT46PGVudj5gIHBhaXJzIGpvaW5lZCBieSBgK2AgKENvZGVQaXBlbGluZS10YWctc2FmZTsgbm8gSlNPTikuIEEgc3RhZ2VcbiAqIHByZXNlbnQgaGVyZSBpcyBhIGRlcGxveTsgaXRzIG1hcHBlZCBlbnZpcm9ubWVudCBpcyB3aGF0IHRoZSBmb3J3YXJkZXIgc3RhbXBzIG9uXG4gKiB0aGF0IHN0YWdlJ3MgU1RBR0UvQUNUSU9OIGV2ZW50cyAoRE9SQSBkZXBsb3kgYXR0cmlidXRpb24pLiBNYWxmb3JtZWQgcGFpcnMgYXJlXG4gKiBza2lwcGVkIGRlZmVuc2l2ZWx5LlxuICovXG5mdW5jdGlvbiBwYXJzZURlcGxveXModmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZCk6IE1hcDxzdHJpbmcsIHN0cmluZz4ge1xuICBjb25zdCBtYXAgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICBpZiAoIXZhbHVlKSByZXR1cm4gbWFwO1xuICBmb3IgKGNvbnN0IHBhaXIgb2YgdmFsdWUuc3BsaXQoJysnKSkge1xuICAgIGNvbnN0IGlkeCA9IHBhaXIuaW5kZXhPZignOicpO1xuICAgIGlmIChpZHggPD0gMCkgY29udGludWU7XG4gICAgY29uc3Qgc3RhZ2UgPSBwYWlyLnNsaWNlKDAsIGlkeCkudHJpbSgpO1xuICAgIGNvbnN0IGVudiA9IHBhaXIuc2xpY2UoaWR4ICsgMSkudHJpbSgpO1xuICAgIGlmIChzdGFnZSAmJiBlbnYpIG1hcC5zZXQoc3RhZ2UsIGVudi5zbGljZSgwLCBNQVhfQVRUUikpO1xuICB9XG4gIHJldHVybiBtYXA7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHBpcGVsaW5lQ2xpZW50KHJlZ2lvbjogc3RyaW5nKTogUHJvbWlzZTxDb2RlUGlwZWxpbmVDbGllbnRMaWtlPiB7XG4gIGxldCBjbGllbnQgPSBjbGllbnRzQnlSZWdpb24uZ2V0KHJlZ2lvbik7XG4gIGlmICghY2xpZW50KSB7XG4gICAgY29uc3QgeyBDb2RlUGlwZWxpbmVDbGllbnQgfSA9IGF3YWl0IGxvYWRDb2RlUGlwZWxpbmUoKTtcbiAgICBjbGllbnQgPSBuZXcgQ29kZVBpcGVsaW5lQ2xpZW50KHsgcmVnaW9uIH0pO1xuICAgIGNsaWVudHNCeVJlZ2lvbi5zZXQocmVnaW9uLCBjbGllbnQpO1xuICB9XG4gIHJldHVybiBjbGllbnQ7XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIENvZGVQaXBlbGluZSdzIHRhZ3Mg4oaSIHRoZSBwbGF0Zm9ybSBwaXBlbGluZUlkIChwYi5waXBlbGluZS1pZCkgcGx1cyB0aGVcbiAqIGRlcGxveS1zdGFnZeKGkmVudmlyb25tZW50IG1hcCAocGIuZGVwbG95cykuIGBwaXBlbGluZUlkYCBpcyBudWxsIHdoZW4gdGhlIHBpcGVsaW5lXG4gKiBoYXMgbm8gcGIucGlwZWxpbmUtaWQgdGFnICh1bnJlZ2lzdGVyZWQpLiBUaHJvd3Mgb24gQWNjZXNzRGVuaWVkIHNvIGEgbWlzc2luZyBJQU1cbiAqIGdyYW50IHN1cmZhY2VzIGxvdWRseSBpbnN0ZWFkIG9mIHNpbGVudGx5IGRyb3BwaW5nIGV2ZXJ5IGV2ZW50LlxuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlUGlwZWxpbmUoYXJuOiBzdHJpbmcsIHJlZ2lvbjogc3RyaW5nKTogUHJvbWlzZTxSZXNvbHZlZFBpcGVsaW5lPiB7XG4gIGNvbnN0IGNhY2hlZCA9IHJlc29sdmVkQnlBcm4uZ2V0KGFybik7XG4gIC8vIFNlcnZlIGEgcG9zaXRpdmUgaGl0IGZvciB0aGUgY29udGFpbmVyIGxpZmV0aW1lOyBzZXJ2ZSBhIG5lZ2F0aXZlIGhpdCBvbmx5XG4gIC8vIHVudGlsIGl0IGV4cGlyZXMgKHRoZW4gcmUtcmVzb2x2ZSwgaW4gY2FzZSB0aGUgcGlwZWxpbmUgd2FzIHNpbmNlIHRhZ2dlZCkuXG4gIGlmIChjYWNoZWQgJiYgKGNhY2hlZC5waXBlbGluZUlkICE9PSBudWxsIHx8IERhdGUubm93KCkgLSBjYWNoZWQudHMgPCBORUdfQ0FDSEVfVFRMX01TKSkge1xuICAgIHJldHVybiB7IHBpcGVsaW5lSWQ6IGNhY2hlZC5waXBlbGluZUlkLCBvcmdJZDogY2FjaGVkLm9yZ0lkLCBkZXBsb3lzOiBjYWNoZWQuZGVwbG95cyB9O1xuICB9XG4gIHRyeSB7XG4gICAgY29uc3QgY2xpZW50ID0gYXdhaXQgcGlwZWxpbmVDbGllbnQocmVnaW9uKTtcbiAgICBjb25zdCB7IExpc3RUYWdzRm9yUmVzb3VyY2VDb21tYW5kIH0gPSBhd2FpdCBsb2FkQ29kZVBpcGVsaW5lKCk7XG4gICAgY29uc3Qgb3V0ID0gYXdhaXQgY2xpZW50LnNlbmQobmV3IExpc3RUYWdzRm9yUmVzb3VyY2VDb21tYW5kKHsgcmVzb3VyY2VBcm46IGFybiB9KSk7XG4gICAgY29uc3QgcGlwZWxpbmVJZCA9IG91dC50YWdzPy5maW5kKHQgPT4gdC5rZXkgPT09IFBJUEVMSU5FX0lEX1RBRyk/LnZhbHVlID8/IG51bGw7XG4gICAgY29uc3Qgb3JnSWQgPSBvdXQudGFncz8uZmluZCh0ID0+IHQua2V5ID09PSBPUkdfSURfVEFHKT8udmFsdWUgPz8gbnVsbDtcbiAgICBjb25zdCBkZXBsb3lzID0gcGFyc2VEZXBsb3lzKG91dC50YWdzPy5maW5kKHQgPT4gdC5rZXkgPT09IERFUExPWVNfVEFHKT8udmFsdWUpO1xuICAgIGlmICghcGlwZWxpbmVJZCkgbG9nLndhcm4oJ1BpcGVsaW5lIG1pc3NpbmcgcGIucGlwZWxpbmUtaWQgdGFnIOKAlCBza2lwcGluZyAocmVnaXN0ZXIgaXQ/KScsIHsgYXJuIH0pO1xuICAgIHJlc29sdmVkQnlBcm4uc2V0KGFybiwgeyBwaXBlbGluZUlkLCBvcmdJZCwgZGVwbG95cywgdHM6IERhdGUubm93KCkgfSk7XG4gICAgcmV0dXJuIHsgcGlwZWxpbmVJZCwgb3JnSWQsIGRlcGxveXMgfTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgY29uc3QgbmFtZSA9IChlcnIgYXMgeyBuYW1lPzogc3RyaW5nIH0pPy5uYW1lO1xuICAgIGlmIChuYW1lID09PSAnQWNjZXNzRGVuaWVkRXhjZXB0aW9uJykge1xuICAgICAgLy8gRG9uJ3QgY2FjaGUg4oCUIHRoaXMgaXMgYSBmaXhhYmxlIG1pc2NvbmZpZywgbm90IGEgcHJvcGVydHkgb2YgdGhlIHBpcGVsaW5lLlxuICAgICAgbG9nLmVycm9yKCdBY2Nlc3NEZW5pZWQgY2FsbGluZyBjb2RlcGlwZWxpbmU6TGlzdFRhZ3NGb3JSZXNvdXJjZSDigJQgZ3JhbnQgaXQgdG8gdGhlIExhbWJkYSByb2xlJywgeyBhcm4sIGVycm9yOiBuYW1lIH0pO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgICAvLyBUcmFuc2llbnQvaW5mcmEgZXJyb3JzICh0aHJvdHRsaW5nLCB0aW1lb3V0cywgNXh4KSBNVVNUIHByb3BhZ2F0ZSBzbyB0aGUgU1FTXG4gICAgLy8gYmF0Y2ggaXMgcmV0cmllZCByYXRoZXIgdGhhbiBzaWxlbnRseSBkcm9wcGluZyB0aGUgZXZlbnQg4oCUIGFuIGV2ZW50IGRyb3BwZWRcbiAgICAvLyBCRUZPUkUgaXQgcmVhY2hlcyB0aGUgaW5nZXN0IGlzIGxvc3QgcGVybWFuZW50bHkgKHRoZSBpbmdlc3QncyBwYXJ0aWFsLXVuaXF1ZVxuICAgIC8vIGluZGV4IG9ubHkgZGVkdXBlcyBldmVudHMgdGhhdCB3ZXJlIGFjdHVhbGx5IFBPU1RlZCkuIE9ubHkgc3dhbGxvdyB3aGVuIHRoZVxuICAgIC8vIGZhaWx1cmUgaXMgYSBkZWZpbml0aXZlIGFuc3dlciB0aGF0IHRoaXMgQVJOIGhhcyBubyB1c2FibGUgdGFnLlxuICAgIGNvbnN0IGUgPSBlcnIgYXMgeyBuYW1lPzogc3RyaW5nOyAkcmV0cnlhYmxlPzogdW5rbm93bjsgJG1ldGFkYXRhPzogeyBodHRwU3RhdHVzQ29kZT86IG51bWJlciB9IH07XG4gICAgY29uc3Qgc3RhdHVzID0gZS4kbWV0YWRhdGE/Lmh0dHBTdGF0dXNDb2RlO1xuICAgIGNvbnN0IHJldHJ5YWJsZSA9IGUuJHJldHJ5YWJsZSAhPSBudWxsXG4gICAgICB8fCAodHlwZW9mIHN0YXR1cyA9PT0gJ251bWJlcicgJiYgc3RhdHVzID49IDUwMClcbiAgICAgIHx8IC90aHJvdHRsfHRpbWVvdXR8dG9vbWFueXJlcXVlc3RzfHNlcnZpY2V1bmF2YWlsYWJsZXxlY29ubnJlc2V0fG5ldHdvcmtpbmdlcnJvci9pLnRlc3QobmFtZSA/PyAnJyk7XG4gICAgaWYgKHJldHJ5YWJsZSkge1xuICAgICAgbG9nLmVycm9yKCdUcmFuc2llbnQgZXJyb3IgcmVzb2x2aW5nIHBiLnBpcGVsaW5lLWlkIHRhZyDigJQgd2lsbCByZXRyeSB0aGUgYmF0Y2gnLCB7IGFybiwgZXJyb3I6IG5hbWUgPz8gU3RyaW5nKGVycikgfSk7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICAgIGxvZy5lcnJvcignRmFpbGVkIHRvIHJlc29sdmUgcGIucGlwZWxpbmUtaWQgdGFnJywgeyBhcm4sIGVycm9yOiBuYW1lID8/IFN0cmluZyhlcnIpIH0pO1xuICAgIHJldHVybiB7IHBpcGVsaW5lSWQ6IG51bGwsIG9yZ0lkOiBudWxsLCBkZXBsb3lzOiBuZXcgTWFwKCkgfTtcbiAgfVxufVxuXG4vLyDilIDilIDilIAgQXV0aCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxubGV0IGNhY2hlZFRva2VuOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuYXN5bmMgZnVuY3Rpb24gZ2V0QXV0aFRva2VuKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGlmIChjYWNoZWRUb2tlbikgcmV0dXJuIGNhY2hlZFRva2VuO1xuXG4gIC8vIFBhdGggMTogUExBVEZPUk1fVE9LRU4gZW52IHZhciAobm8gU2VjcmV0cyBNYW5hZ2VyIGNhbGwpXG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9UT0tFTikge1xuICAgIGNhY2hlZFRva2VuID0gcHJvY2Vzcy5lbnYuUExBVEZPUk1fVE9LRU47XG4gICAgbG9nLmluZm8oJ1VzaW5nIFBMQVRGT1JNX1RPS0VOIGZyb20gZW52aXJvbm1lbnQnKTtcbiAgICByZXR1cm4gY2FjaGVkVG9rZW47XG4gIH1cblxuICAvLyBQYXRoIDI6IFBMQVRGT1JNX1NFQ1JFVF9OQU1FIOKGkiByZWFkIGZyb20gU2VjcmV0cyBNYW5hZ2VyXG4gIGNvbnN0IHNlY3JldE5hbWUgPSBwcm9jZXNzLmVudi5QTEFURk9STV9TRUNSRVRfTkFNRTtcbiAgaWYgKCFzZWNyZXROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdQTEFURk9STV9UT0tFTiBvciBQTEFURk9STV9TRUNSRVRfTkFNRSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZCcpO1xuICB9XG5cbiAgbG9nLmluZm8oYEZldGNoaW5nIHRva2VuIGZyb20gU2VjcmV0cyBNYW5hZ2VyOiAke3NlY3JldE5hbWV9YCk7XG4gIGNvbnN0IGNsaWVudCA9IG5ldyBTZWNyZXRzTWFuYWdlckNsaWVudCh7fSk7XG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LnNlbmQobmV3IEdldFNlY3JldFZhbHVlQ29tbWFuZCh7IFNlY3JldElkOiBzZWNyZXROYW1lIH0pKTtcblxuICBpZiAoIXJlc3BvbnNlLlNlY3JldFN0cmluZykgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgXCIke3NlY3JldE5hbWV9XCIgaXMgZW1wdHlgKTtcblxuICBjb25zdCBzZWNyZXQgPSBKU09OLnBhcnNlKHJlc3BvbnNlLlNlY3JldFN0cmluZykgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgLy8gaW5mcmEgc3RvcmUtdG9rZW4gd3JpdGVzIHRoZSBKV1QgaW4gYHBhc3N3b3JkYCAodGhlIGNhbm9uaWNhbCBmaWVsZCDigJQgYWxzbyB1c2VkIGJ5XG4gIC8vIENvZGVCdWlsZCBzZWNyZXRzTWFuYWdlckNyZWRlbnRpYWxzLCB0aGUgcGx1Z2luLWxvb2t1cCBMYW1iZGEsIGFuZCB0b2tlbi1yZW5ldykuXG4gIGNvbnN0IHRva2VuID0gc2VjcmV0LnBhc3N3b3JkO1xuICBpZiAoIXRva2VuKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdTZWNyZXQgbWlzc2luZyBKV1QgKHBhc3N3b3JkKSDigJQgcnVuIFwicGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlblwiIHRvIGdlbmVyYXRlJyk7XG4gIH1cblxuICBjYWNoZWRUb2tlbiA9IHRva2VuO1xuICBsb2cuaW5mbygnVXNpbmcgc3RvcmVkIEpXVCB0b2tlbiBmcm9tIFNlY3JldHMgTWFuYWdlcicpO1xuICByZXR1cm4gY2FjaGVkVG9rZW47XG59XG5cbi8vIOKUgOKUgOKUgCBQaGFzZSA0OiBpbi1hY2NvdW50IGNvbW1pdC1yYW5nZSByZXNvbHV0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuLy8gRm9yIGEgc291cmNlL2RlcGxveSBldmVudCBjYXJyeWluZyBhIGNvbW1pdFNoYSB3ZSByZXNvbHZlIHRoZSBjb21taXQgdGltZXN0YW1wXG4vLyAoYW5kIGhvdyBtYW55IGNvbW1pdHMgc2hpcHBlZCkgZm9yIHRoZSByYW5nZSBzaW5jZSB0aGUgbGFzdCByZXNvbHZlZCBzaGEgb2YgdGhlXG4vLyBzYW1lIHBpcGVsaW5lIOKAlCBlbnRpcmVseSBJTi1BQ0NPVU5ULCBwZXIgc291cmNlIHR5cGU6XG4vLyAgIOKAoiBDb2RlQ29tbWl0ICDihpIgQVdTIFNESyBHZXRDb21taXQgKHdhbGsgZmlyc3QtcGFyZW50cyBiYWNrIHRvIHRoZSBib3VuZGFyeSkuXG4vLyAgIOKAoiBHaXRIdWIvQ29kZUNvbm5lY3Rpb25zLUdpdEh1YiDihpIgR2l0SHViIFJFU1QgY29tcGFyZS9jb21taXRzIEFQSS4gQXV0aGVudGljYXRlZFxuLy8gICAgIHdpdGggdGhlIG9yZydzIGBwaXBlbGluZS1idWlsZGVyL3tvcmdJZH0vZ2l0aHViLXRva2VuYCBzZWNyZXQgd2hlbiBwcmVzZW50XG4vLyAgICAgKGRlcml2ZWQgZnJvbSBQTEFURk9STV9TRUNSRVRfTkFNRSksIG90aGVyd2lzZSBiZXN0LWVmZm9ydCB1bmF1dGhlbnRpY2F0ZWQuXG4vLyAgIOKAoiBCaXRidWNrZXQgICDihpIgQml0YnVja2V0IFJFU1QgY29tbWl0IEFQSSAoc2luZ2xlLWNvbW1pdDsgcmFuZ2Ugd2FsayBvbWl0dGVkKS5cbi8vIFRoZSBvbGRlc3QgdW5zaGlwcGVkIGNvbW1pdCdzIHRpbWVzdGFtcCDihpIgYGNvbW1pdFRpbWVzdGFtcGA7IHRoZSBudW1iZXIgb2YgY29tbWl0c1xuLy8gaW4gdGhlIHJhbmdlIOKGkiBgY29tbWl0Q291bnRgLiBFdmVyeXRoaW5nIGhlcmUgaXMgQkVTVC1FRkZPUlQ6IG9uIGFueSBmYWlsdXJlIHRoZVxuLy8gZmllbGRzIGFyZSBzaW1wbHkgb21pdHRlZCAocmVwb3J0aW5nIGZhbGxzIGJhY2sgdG8gYHVua25vd25gKS4gSXQgbXVzdCBORVZFUiBmYWlsXG4vLyB0aGUgYmF0Y2guXG4vL1xuLy8gSUFNIHJlcXVpcmVkIGJ5IHRoaXMgcGF0aCAoZG9jdW1lbnQgb25seSDigJQgZ3JhbnRlZCBvbiB0aGUgZXZlbnRzIHN0YWNrKTpcbi8vICAg4oCiIGNvZGVjb21taXQ6R2V0Q29tbWl0ICAgICAgICAgICAgKENvZGVDb21taXQgc291cmNlcylcbi8vICAg4oCiIHNlY3JldHNtYW5hZ2VyOkdldFNlY3JldFZhbHVlIG9uIGBwaXBlbGluZS1idWlsZGVyLyovZ2l0aHViLXRva2VuYCAodG9rZW4gU0NNIHNvdXJjZXMpXG5cbnR5cGUgU291cmNlUHJvdmlkZXIgPSAnZ2l0aHViJyB8ICdiaXRidWNrZXQnIHwgJ2NvZGVjb21taXQnIHwgJ3Vua25vd24nO1xuaW50ZXJmYWNlIFNvdXJjZVJlZiB7XG4gIHNoYTogc3RyaW5nO1xuICBwcm92aWRlcjogU291cmNlUHJvdmlkZXI7XG4gIG93bmVyPzogc3RyaW5nO1xuICByZXBvPzogc3RyaW5nO1xuICByZXBvc2l0b3J5TmFtZT86IHN0cmluZztcbn1cbmludGVyZmFjZSBDb21taXRJbmZvIHsgY29tbWl0VGltZXN0YW1wPzogc3RyaW5nOyBjb21taXRDb3VudD86IG51bWJlciB9XG5cbi8vIFBlci0ocGlwZWxpbmUsc2hhKSByZXNvbHZlZCByZXN1bHQg4oCUIGNvbXB1dGVkIG9uY2UsIHJldXNlZCBhY3Jvc3MgdGhlIG1hbnkgZXZlbnRzXG4vLyAoUElQRUxJTkUvU1RBR0UvQUNUSU9OKSB0aGF0IHNoYXJlIGEgc291cmNlIHJldmlzaW9uIGFuZCBhY3Jvc3MgcmVkZWxpdmVyaWVzLlxuY29uc3QgY29tbWl0UmVzdWx0Q2FjaGUgPSBuZXcgTWFwPHN0cmluZywgQ29tbWl0SW5mbz4oKTtcbi8vIExhc3Qgc2hhIHdlIHN1Y2Nlc3NmdWxseSByZXNvbHZlZCBwZXIgcGlwZWxpbmUg4oCUIHRoZSBleGNsdXNpdmUgbG93ZXIgYm91bmQgZm9yIHRoZVxuLy8gbmV4dCByYW5nZSAoXCJjb21taXRzIHNpbmNlIHRoZSBsYXN0IGRlcGxveVwiKS4gSW4tbWVtb3J5IG9ubHk6IGEgY29sZCBzdGFydCByZXNldHNcbi8vIGl0LCBzbyB0aGUgZmlyc3QgcG9zdC1jb2xkIGV2ZW50IHJlc29sdmVzIGFzIGEgc2luZ2xlIGNvbW1pdCAoZXhwZWN0ZWQsIGRlZmVuc2l2ZSkuXG5jb25zdCBsYXN0U2hhQnlQaXBlbGluZSA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4vLyBQZXItc2hhIENvZGVDb21taXQgbWV0YWRhdGEgY2FjaGUgKGRhdGUgKyBmaXJzdC1wYXJlbnQpIGZvciByYW5nZSB3YWxrcy5cbmNvbnN0IGNvbW1pdE1ldGFDYWNoZSA9IG5ldyBNYXA8c3RyaW5nLCB7IGRhdGU/OiBzdHJpbmc7IHBhcmVudHM/OiBzdHJpbmdbXSB9PigpO1xuLy8gU0NNIHJhdGUtbGltaXQgY29vbGRvd246IGFmdGVyIGEgNDAzLzQyOSB3ZSBzdG9wIGNhbGxpbmcgdGhlIFNDTSBmb3IgYSB3aGlsZS5cbmxldCBzY21Db29sZG93blVudGlsID0gMDtcbmNvbnN0IFNDTV9DT09MRE9XTl9NUyA9IDIgKiA2MCAqIDEwMDA7XG4vLyBDYXAgdGhlIENvZGVDb21taXQgZmlyc3QtcGFyZW50IHdhbGsgc28gYSBsb25nIHJhbmdlIGNhbid0IGJsb3cgdGhlIExhbWJkYSB0aW1lb3V0LlxuY29uc3QgTUFYX0NPTU1JVF9XQUxLID0gNTA7XG4vLyBQZXItcmVxdWVzdCB0aW1lb3V0IGZvciBleHRlcm5hbCBTQ00gY2FsbHMgKGRlZmVuc2l2ZSBhZ2FpbnN0IHNsb3cvaHVuZyBob3N0cykuXG5jb25zdCBTQ01fRkVUQ0hfVElNRU9VVF9NUyA9IDMwMDA7XG5cbi8vIFRoZSBmb3J3YXJkZXIgaXMgYSBmbGVldCBMYW1iZGE6IG9uZSB3YXJtIGNvbnRhaW5lciByZXNvbHZlcyBldmVudHMgZm9yIE1BTllcbi8vIG9yZ3MsIHNvIHRoZSBHaXRIdWIgdG9rZW4gTVVTVCBiZSBrZXllZCBieSB0aGUgZXZlbnQncyByZXNvbHZlZCBvcmcgKG5vdCBhIHNpbmdsZVxuLy8gY29udGFpbmVyLWdsb2JhbCB0b2tlbikuIEVhY2ggb3JnJ3MgdG9rZW4gbGl2ZXMgYXQgYDxwcmVmaXg+LzxvcmdJZD4vZ2l0aHViLXRva2VuYCxcbi8vIGRlcml2ZWQgZnJvbSBQTEFURk9STV9TRUNSRVRfTkFNRSAoYDxwcmVmaXg+Lzxjb250YWluZXJPcmc+L3BsYXRmb3JtYCkuIG51bGwgY2FjaGVzXG4vLyBhIGRlZmluaXRpdmUgbWlzcyAobm8gc2VjcmV0IC8gcmVhZCBmYWlsZWQpIOKGkiB1bmF1dGhlbnRpY2F0ZWQgYmVzdC1lZmZvcnQuXG5jb25zdCBnaXRodWJUb2tlbkJ5T3JnID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZyB8IG51bGw+KCk7XG5cbi8qKlxuICogRGVyaXZlIGEgc3BlY2lmaWMgb3JnJ3MgZ2l0aHViLXRva2VuIHNlY3JldCBuYW1lIGZyb20gUExBVEZPUk1fU0VDUkVUX05BTUVcbiAqIChgPHByZWZpeD4vPGNvbnRhaW5lck9yZz4vcGxhdGZvcm1gKS4gV2l0aCBhbiBgb3JnSWRgIHdlIHN3YXAgdGhlIG9yZyBzZWdtZW50XG4gKiAoYDxwcmVmaXg+LzxvcmdJZD4vZ2l0aHViLXRva2VuYCk7IHdpdGhvdXQgb25lIChwaXBlbGluZSBjYXJyaWVkIG5vIE9yZ0lkIHRhZykgd2VcbiAqIGZhbGwgYmFjayB0byB0aGUgY29udGFpbmVyJ3Mgb3duIHNpYmxpbmcgc2VjcmV0LiBSZXR1cm5zIHVuZGVmaW5lZCB3aGVuXG4gKiBQTEFURk9STV9TRUNSRVRfTkFNRSBpc24ndCB0aGUgZXhwZWN0ZWQgYC4uLi9wbGF0Zm9ybWAgc2hhcGUuXG4gKi9cbmZ1bmN0aW9uIGdpdGh1YlRva2VuU2VjcmV0TmFtZShvcmdJZDogc3RyaW5nIHwgbnVsbCk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IG5hbWUgPSBwcm9jZXNzLmVudi5QTEFURk9STV9TRUNSRVRfTkFNRTtcbiAgaWYgKCFuYW1lIHx8ICFuYW1lLmVuZHNXaXRoKCcvcGxhdGZvcm0nKSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgYmFzZSA9IG5hbWUuc2xpY2UoMCwgLScvcGxhdGZvcm0nLmxlbmd0aCk7IC8vIDxwcmVmaXg+Lzxjb250YWluZXJPcmc+XG4gIGlmIChvcmdJZCkge1xuICAgIGNvbnN0IHNsYXNoID0gYmFzZS5sYXN0SW5kZXhPZignLycpO1xuICAgIGNvbnN0IHByZWZpeCA9IHNsYXNoID4gMCA/IGJhc2Uuc2xpY2UoMCwgc2xhc2gpIDogYmFzZTtcbiAgICByZXR1cm4gYCR7cHJlZml4fS8ke29yZ0lkfS9naXRodWItdG9rZW5gO1xuICB9XG4gIHJldHVybiBgJHtiYXNlfS9naXRodWItdG9rZW5gO1xufVxuXG4vKiogUmVhZCAoYW5kIGNhY2hlLCBwZXIgb3JnKSB0aGUgb3JnIGdpdGh1YiB0b2tlbjsgbnVsbCDihpIgdW5hdXRoZW50aWNhdGVkIGJlc3QtZWZmb3J0LiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0R2l0SHViVG9rZW4ob3JnSWQ6IHN0cmluZyB8IG51bGwpOiBQcm9taXNlPHN0cmluZyB8IG51bGw+IHtcbiAgY29uc3Qga2V5ID0gb3JnSWQgPz8gJyc7XG4gIGNvbnN0IGNhY2hlZCA9IGdpdGh1YlRva2VuQnlPcmcuZ2V0KGtleSk7XG4gIGlmIChjYWNoZWQgIT09IHVuZGVmaW5lZCkgcmV0dXJuIGNhY2hlZDtcbiAgY29uc3Qgc2VjcmV0TmFtZSA9IGdpdGh1YlRva2VuU2VjcmV0TmFtZShvcmdJZCk7XG4gIGlmICghc2VjcmV0TmFtZSkgeyBnaXRodWJUb2tlbkJ5T3JnLnNldChrZXksIG51bGwpOyByZXR1cm4gbnVsbDsgfVxuICBsZXQgdG9rZW46IHN0cmluZyB8IG51bGw7XG4gIHRyeSB7XG4gICAgY29uc3QgY2xpZW50ID0gbmV3IFNlY3JldHNNYW5hZ2VyQ2xpZW50KHt9KTtcbiAgICBjb25zdCByZXMgPSBhd2FpdCBjbGllbnQuc2VuZChuZXcgR2V0U2VjcmV0VmFsdWVDb21tYW5kKHsgU2VjcmV0SWQ6IHNlY3JldE5hbWUgfSkpO1xuICAgIC8vIFRoZSBnaXRodWItdG9rZW4gc2VjcmV0IGlzIGEgUkFXIHRva2VuIHN0cmluZyAobm90IEpTT04g4oCUIENESyByZXNvbHZlcyBpdCB2aWFcbiAgICAvLyB0aGUgZGVmYXVsdCBTZWNyZXRTdHJpbmcgd2l0aCBubyBrZXkpLlxuICAgIHRva2VuID0gcmVzLlNlY3JldFN0cmluZyAmJiByZXMuU2VjcmV0U3RyaW5nLmxlbmd0aCA+IDAgPyByZXMuU2VjcmV0U3RyaW5nLnRyaW0oKSA6IG51bGw7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGxvZy53YXJuKCdObyBnaXRodWItdG9rZW4gc2VjcmV0IOKAlCBHaXRIdWIgY29tbWl0IHJlc29sdXRpb24gd2lsbCBiZSB1bmF1dGhlbnRpY2F0ZWQnLCB7IG9yZ0lkLCBlcnJvcjogKGVyciBhcyB7IG5hbWU/OiBzdHJpbmcgfSk/Lm5hbWUgfSk7XG4gICAgdG9rZW4gPSBudWxsO1xuICB9XG4gIGdpdGh1YlRva2VuQnlPcmcuc2V0KGtleSwgdG9rZW4pO1xuICByZXR1cm4gdG9rZW47XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZldGNoV2l0aFRpbWVvdXQodXJsOiBzdHJpbmcsIG9wdHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogUHJvbWlzZTxSZXNwb25zZT4ge1xuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCBTQ01fRkVUQ0hfVElNRU9VVF9NUyk7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IGZldGNoKHVybCwgeyAuLi5vcHRzLCBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsIH0pO1xuICB9IGZpbmFsbHkge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gIH1cbn1cblxuLyoqIE5vcm1hbGl6ZSBhIGNvbW1pdCBkYXRlIHRvIElTTyA4NjAxLiBBY2NlcHRzIElTTyBzdHJpbmdzIGFuZCBDb2RlQ29tbWl0J3NcbiAqICBcIjxlcG9jaC1zZWNvbmRzPiArWlpaWlwiIGZvcm0uIFJldHVybnMgdW5kZWZpbmVkIHdoZW4gdW5wYXJzZWFibGUuICovXG5mdW5jdGlvbiB0b0lzbyh2YWx1ZTogdW5rbm93bik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmICh0eXBlb2YgdmFsdWUgIT09ICdzdHJpbmcnIHx8IHZhbHVlLmxlbmd0aCA9PT0gMCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgZXBvY2ggPSAvXihcXGR7OSx9KVxcYi8uZXhlYyh2YWx1ZS50cmltKCkpO1xuICBpZiAoZXBvY2gpIHtcbiAgICBjb25zdCBkID0gbmV3IERhdGUoTnVtYmVyKGVwb2NoWzFdKSAqIDEwMDApO1xuICAgIHJldHVybiBOdW1iZXIuaXNOYU4oZC5nZXRUaW1lKCkpID8gdW5kZWZpbmVkIDogZC50b0lTT1N0cmluZygpO1xuICB9XG4gIGNvbnN0IGQgPSBuZXcgRGF0ZSh2YWx1ZSk7XG4gIHJldHVybiBOdW1iZXIuaXNOYU4oZC5nZXRUaW1lKCkpID8gdW5kZWZpbmVkIDogZC50b0lTT1N0cmluZygpO1xufVxuXG4vKiogQ2xhc3NpZnkgYSBzb3VyY2UtcmV2aXNpb24gVVJMIGludG8gYSBwcm92aWRlciArIHJlcG8gY29vcmRpbmF0ZXMuICovXG5mdW5jdGlvbiBjbGFzc2lmeVNvdXJjZSh1cmw6IHN0cmluZyB8IHVuZGVmaW5lZCk6IE9taXQ8U291cmNlUmVmLCAnc2hhJz4ge1xuICBpZiAoIXVybCkgcmV0dXJuIHsgcHJvdmlkZXI6ICd1bmtub3duJyB9O1xuICB0cnkge1xuICAgIGNvbnN0IHUgPSBuZXcgVVJMKHVybCk7XG4gICAgY29uc3QgaG9zdCA9IHUuaG9zdG5hbWUudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAoaG9zdCA9PT0gJ2dpdGh1Yi5jb20nIHx8IGhvc3QuZW5kc1dpdGgoJy5naXRodWIuY29tJykpIHtcbiAgICAgIGNvbnN0IHBhcnRzID0gdS5wYXRobmFtZS5zcGxpdCgnLycpLmZpbHRlcihCb29sZWFuKTtcbiAgICAgIGlmIChwYXJ0cy5sZW5ndGggPj0gMikgcmV0dXJuIHsgcHJvdmlkZXI6ICdnaXRodWInLCBvd25lcjogcGFydHNbMF0sIHJlcG86IHBhcnRzWzFdLnJlcGxhY2UoL1xcLmdpdCQvLCAnJykgfTtcbiAgICB9XG4gICAgaWYgKGhvc3QgPT09ICdiaXRidWNrZXQub3JnJykge1xuICAgICAgY29uc3QgcGFydHMgPSB1LnBhdGhuYW1lLnNwbGl0KCcvJykuZmlsdGVyKEJvb2xlYW4pO1xuICAgICAgaWYgKHBhcnRzLmxlbmd0aCA+PSAyKSByZXR1cm4geyBwcm92aWRlcjogJ2JpdGJ1Y2tldCcsIG93bmVyOiBwYXJ0c1swXSwgcmVwbzogcGFydHNbMV0ucmVwbGFjZSgvXFwuZ2l0JC8sICcnKSB9O1xuICAgIH1cbiAgICBpZiAodS5wYXRobmFtZS50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKCdjb2RlY29tbWl0JykgfHwgdS5oYXNoLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ2NvZGVjb21taXQnKSB8fCB1LnBhdGhuYW1lLmluY2x1ZGVzKCcvcmVwb3NpdG9yaWVzLycpKSB7XG4gICAgICBjb25zdCBtID0gL3JlcG9zaXRvcig/Onl8aWVzKVxcLyhbXi8jP10rKS8uZXhlYyh1LnBhdGhuYW1lICsgdS5oYXNoKTtcbiAgICAgIGlmIChtKSByZXR1cm4geyBwcm92aWRlcjogJ2NvZGVjb21taXQnLCByZXBvc2l0b3J5TmFtZTogZGVjb2RlVVJJQ29tcG9uZW50KG1bMV0pIH07XG4gICAgfVxuICB9IGNhdGNoIHtcbiAgICAvLyBub3QgYSBVUkwg4oaSIHVua25vd25cbiAgfVxuICByZXR1cm4geyBwcm92aWRlcjogJ3Vua25vd24nIH07XG59XG5cbmxldCBjb2RlY29tbWl0Q2xpZW50c0J5UmVnaW9uOiBNYXA8c3RyaW5nLCB7IHNlbmQoY29tbWFuZDogdW5rbm93bik6IFByb21pc2U8dW5rbm93bj4gfT4gfCB1bmRlZmluZWQ7XG5pbnRlcmZhY2UgQ29kZUNvbW1pdE1vZHVsZSB7XG4gIENvZGVDb21taXRDbGllbnQ6IG5ldyAoY29uZmlnOiB7IHJlZ2lvbjogc3RyaW5nIH0pID0+IHsgc2VuZChjb21tYW5kOiB1bmtub3duKTogUHJvbWlzZTx1bmtub3duPiB9O1xuICBHZXRDb21taXRDb21tYW5kOiBuZXcgKGlucHV0OiB7IHJlcG9zaXRvcnlOYW1lOiBzdHJpbmc7IGNvbW1pdElkOiBzdHJpbmcgfSkgPT4gdW5rbm93bjtcbn1cbmxldCBjb2RlY29tbWl0TW9kOiBDb2RlQ29tbWl0TW9kdWxlIHwgdW5kZWZpbmVkO1xuYXN5bmMgZnVuY3Rpb24gY29kZUNvbW1pdENsaWVudChyZWdpb246IHN0cmluZyk6IFByb21pc2U8eyBzZW5kKGNvbW1hbmQ6IHVua25vd24pOiBQcm9taXNlPHVua25vd24+IH0+IHtcbiAgaWYgKCFjb2RlY29tbWl0TW9kKSBjb2RlY29tbWl0TW9kID0gYXdhaXQgbG9hZFNkazxDb2RlQ29tbWl0TW9kdWxlPignQGF3cy1zZGsvY2xpZW50LWNvZGVjb21taXQnKTtcbiAgaWYgKCFjb2RlY29tbWl0Q2xpZW50c0J5UmVnaW9uKSBjb2RlY29tbWl0Q2xpZW50c0J5UmVnaW9uID0gbmV3IE1hcCgpO1xuICBsZXQgY2xpZW50ID0gY29kZWNvbW1pdENsaWVudHNCeVJlZ2lvbi5nZXQocmVnaW9uKTtcbiAgaWYgKCFjbGllbnQpIHtcbiAgICBjbGllbnQgPSBuZXcgY29kZWNvbW1pdE1vZC5Db2RlQ29tbWl0Q2xpZW50KHsgcmVnaW9uIH0pO1xuICAgIGNvZGVjb21taXRDbGllbnRzQnlSZWdpb24uc2V0KHJlZ2lvbiwgY2xpZW50KTtcbiAgfVxuICByZXR1cm4gY2xpZW50O1xufVxuXG4vKiogRmV0Y2ggKGNhY2hlZCkgYSBzaW5nbGUgQ29kZUNvbW1pdCBjb21taXQncyBkYXRlICsgZmlyc3QgcGFyZW50cyB2aWEgR2V0Q29tbWl0LiAqL1xuYXN5bmMgZnVuY3Rpb24gY29kZUNvbW1pdE1ldGEocmVnaW9uOiBzdHJpbmcsIHJlcG9zaXRvcnlOYW1lOiBzdHJpbmcsIGNvbW1pdElkOiBzdHJpbmcpOiBQcm9taXNlPHsgZGF0ZT86IHN0cmluZzsgcGFyZW50cz86IHN0cmluZ1tdIH0gfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgY2FjaGVkID0gY29tbWl0TWV0YUNhY2hlLmdldChjb21taXRJZCk7XG4gIGlmIChjYWNoZWQpIHJldHVybiBjYWNoZWQ7XG4gIGlmICghY29kZWNvbW1pdE1vZCkgY29kZWNvbW1pdE1vZCA9IGF3YWl0IGxvYWRTZGs8Q29kZUNvbW1pdE1vZHVsZT4oJ0Bhd3Mtc2RrL2NsaWVudC1jb2RlY29tbWl0Jyk7XG4gIGNvbnN0IGNsaWVudCA9IGF3YWl0IGNvZGVDb21taXRDbGllbnQocmVnaW9uKTtcbiAgY29uc3Qgb3V0ID0gYXdhaXQgY2xpZW50LnNlbmQobmV3IGNvZGVjb21taXRNb2QuR2V0Q29tbWl0Q29tbWFuZCh7IHJlcG9zaXRvcnlOYW1lLCBjb21taXRJZCB9KSkgYXMge1xuICAgIGNvbW1pdD86IHsgY29tbWl0dGVyPzogeyBkYXRlPzogc3RyaW5nIH07IGF1dGhvcj86IHsgZGF0ZT86IHN0cmluZyB9OyBwYXJlbnRzPzogc3RyaW5nW10gfTtcbiAgfTtcbiAgY29uc3QgY29tbWl0ID0gb3V0LmNvbW1pdDtcbiAgY29uc3QgbWV0YSA9IHsgZGF0ZTogdG9Jc28oY29tbWl0Py5jb21taXR0ZXI/LmRhdGUgPz8gY29tbWl0Py5hdXRob3I/LmRhdGUpLCBwYXJlbnRzOiBjb21taXQ/LnBhcmVudHMgfTtcbiAgY29tbWl0TWV0YUNhY2hlLnNldChjb21taXRJZCwgbWV0YSk7XG4gIHJldHVybiBtZXRhO1xufVxuXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlQ29kZUNvbW1pdChyZWdpb246IHN0cmluZywgcmVwb3NpdG9yeU5hbWU6IHN0cmluZywgc2hhOiBzdHJpbmcsIHNpbmNlU2hhPzogc3RyaW5nKTogUHJvbWlzZTxDb21taXRJbmZvPiB7XG4gIGxldCBjdXI6IHN0cmluZyB8IHVuZGVmaW5lZCA9IHNoYTtcbiAgbGV0IGNvdW50ID0gMDtcbiAgbGV0IG9sZGVzdDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBsZXQgdHJ1bmNhdGVkID0gZmFsc2U7XG4gIGZvciAobGV0IGkgPSAwOyA7IGkrKykge1xuICAgIGlmICghY3VyKSBicmVhazsgLy8gcmVhY2hlZCBoaXN0b3J5IHJvb3Qg4oCUIHRoZSB3aG9sZSByYW5nZSBpcyBjb3ZlcmVkXG4gICAgaWYgKHNpbmNlU2hhICYmIGN1ciA9PT0gc2luY2VTaGEpIGJyZWFrOyAvLyByZWFjaGVkIHRoZSBleGNsdXNpdmUgbG93ZXIgYm91bmRcbiAgICBpZiAoaSA+PSBNQVhfQ09NTUlUX1dBTEspIHsgdHJ1bmNhdGVkID0gdHJ1ZTsgYnJlYWs7IH0gLy8gaGl0IHRoZSBjYXAgYmVmb3JlIHRoZSBib3VuZGFyeVxuICAgIGNvbnN0IG1ldGEgPSBhd2FpdCBjb2RlQ29tbWl0TWV0YShyZWdpb24sIHJlcG9zaXRvcnlOYW1lLCBjdXIpO1xuICAgIGlmICghbWV0YT8uZGF0ZSkgYnJlYWs7XG4gICAgY291bnQrKztcbiAgICBvbGRlc3QgPSBtZXRhLmRhdGU7XG4gICAgaWYgKCFzaW5jZVNoYSkgYnJlYWs7IC8vIG5vIHJhbmdlIOKGkiBzaW5nbGUgY29tbWl0XG4gICAgY3VyID0gbWV0YS5wYXJlbnRzPy5bMF07XG4gIH1cbiAgLy8gQSB0cnVuY2F0ZWQgd2FsayB5aWVsZHMgYSBjb21taXQgTkVXRVIgdGhhbiB0aGUgdHJ1ZSBvbGRlc3QtdW5zaGlwcGVkIGNvbW1pdCxcbiAgLy8gd2hpY2ggd291bGQgVU5ERVJTVEFURSBsZWFkIHRpbWUg4oCUIG9taXQgdGhlIGZpZWxkcyBlbnRpcmVseSByYXRoZXIgdGhhbiByZXBvcnQgYVxuICAvLyB3cm9uZyAodG9vLXNtYWxsKSB2YWx1ZS4gUmVwb3J0aW5nIGZhbGxzIGJhY2sgdG8gYHVua25vd25gLlxuICBpZiAodHJ1bmNhdGVkKSByZXR1cm4ge307XG4gIHJldHVybiBvbGRlc3QgPyB7IGNvbW1pdFRpbWVzdGFtcDogb2xkZXN0LCBjb21taXRDb3VudDogY291bnQgfSA6IHt9O1xufVxuXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlR2l0SHViKG93bmVyOiBzdHJpbmcsIHJlcG86IHN0cmluZywgc2hhOiBzdHJpbmcsIHNpbmNlU2hhOiBzdHJpbmcgfCB1bmRlZmluZWQsIG9yZ0lkOiBzdHJpbmcgfCBudWxsKTogUHJvbWlzZTxDb21taXRJbmZvPiB7XG4gIGNvbnN0IHRva2VuID0gYXdhaXQgZ2V0R2l0SHViVG9rZW4ob3JnSWQpO1xuICBjb25zdCBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vdm5kLmdpdGh1Yitqc29uJyxcbiAgICAnVXNlci1BZ2VudCc6ICdwaXBlbGluZS1idWlsZGVyLWZvcndhcmRlcicsXG4gICAgJ1gtR2l0SHViLUFwaS1WZXJzaW9uJzogJzIwMjItMTEtMjgnLFxuICB9O1xuICBpZiAodG9rZW4pIGhlYWRlcnMuQXV0aG9yaXphdGlvbiA9IGBCZWFyZXIgJHt0b2tlbn1gO1xuICBjb25zdCByYXRlTGltaXRlZCA9IChyOiBSZXNwb25zZSkgPT4gci5zdGF0dXMgPT09IDQwMyB8fCByLnN0YXR1cyA9PT0gNDI5O1xuXG4gIGlmIChzaW5jZVNoYSkge1xuICAgIGNvbnN0IHIgPSBhd2FpdCBmZXRjaFdpdGhUaW1lb3V0KGBodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zLyR7b3duZXJ9LyR7cmVwb30vY29tcGFyZS8ke3NpbmNlU2hhfS4uLiR7c2hhfWAsIHsgaGVhZGVycyB9KTtcbiAgICBpZiAocmF0ZUxpbWl0ZWQocikpIHsgc2NtQ29vbGRvd25VbnRpbCA9IERhdGUubm93KCkgKyBTQ01fQ09PTERPV05fTVM7IHJldHVybiB7fTsgfVxuICAgIGlmIChyLm9rKSB7XG4gICAgICBjb25zdCBqID0gYXdhaXQgci5qc29uKCkgYXMgeyBjb21taXRzPzogQXJyYXk8eyBjb21taXQ/OiB7IGNvbW1pdHRlcj86IHsgZGF0ZT86IHN0cmluZyB9OyBhdXRob3I/OiB7IGRhdGU/OiBzdHJpbmcgfSB9IH0+IH07XG4gICAgICBjb25zdCBjb21taXRzID0gQXJyYXkuaXNBcnJheShqLmNvbW1pdHMpID8gai5jb21taXRzIDogW107XG4gICAgICBpZiAoY29tbWl0cy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IG9sZGVzdCA9IHRvSXNvKGNvbW1pdHNbMF0/LmNvbW1pdD8uY29tbWl0dGVyPy5kYXRlID8/IGNvbW1pdHNbMF0/LmNvbW1pdD8uYXV0aG9yPy5kYXRlKTtcbiAgICAgICAgaWYgKG9sZGVzdCkgcmV0dXJuIHsgY29tbWl0VGltZXN0YW1wOiBvbGRlc3QsIGNvbW1pdENvdW50OiBjb21taXRzLmxlbmd0aCB9O1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBmYWxsIHRocm91Z2ggdG8gc2luZ2xlLWNvbW1pdCByZXNvbHV0aW9uXG4gIH1cbiAgY29uc3QgcjIgPSBhd2FpdCBmZXRjaFdpdGhUaW1lb3V0KGBodHRwczovL2FwaS5naXRodWIuY29tL3JlcG9zLyR7b3duZXJ9LyR7cmVwb30vY29tbWl0cy8ke3NoYX1gLCB7IGhlYWRlcnMgfSk7XG4gIGlmIChyYXRlTGltaXRlZChyMikpIHsgc2NtQ29vbGRvd25VbnRpbCA9IERhdGUubm93KCkgKyBTQ01fQ09PTERPV05fTVM7IHJldHVybiB7fTsgfVxuICBpZiAocjIub2spIHtcbiAgICBjb25zdCBqID0gYXdhaXQgcjIuanNvbigpIGFzIHsgY29tbWl0PzogeyBjb21taXR0ZXI/OiB7IGRhdGU/OiBzdHJpbmcgfTsgYXV0aG9yPzogeyBkYXRlPzogc3RyaW5nIH0gfSB9O1xuICAgIGNvbnN0IGQgPSB0b0lzbyhqLmNvbW1pdD8uY29tbWl0dGVyPy5kYXRlID8/IGouY29tbWl0Py5hdXRob3I/LmRhdGUpO1xuICAgIGlmIChkKSByZXR1cm4geyBjb21taXRUaW1lc3RhbXA6IGQsIGNvbW1pdENvdW50OiAxIH07XG4gIH1cbiAgcmV0dXJuIHt9O1xufVxuXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlQml0YnVja2V0KG93bmVyOiBzdHJpbmcsIHJlcG86IHN0cmluZywgc2hhOiBzdHJpbmcpOiBQcm9taXNlPENvbW1pdEluZm8+IHtcbiAgY29uc3QgciA9IGF3YWl0IGZldGNoV2l0aFRpbWVvdXQoYGh0dHBzOi8vYXBpLmJpdGJ1Y2tldC5vcmcvMi4wL3JlcG9zaXRvcmllcy8ke293bmVyfS8ke3JlcG99L2NvbW1pdC8ke3NoYX1gLCB7XG4gICAgaGVhZGVyczogeyAnVXNlci1BZ2VudCc6ICdwaXBlbGluZS1idWlsZGVyLWZvcndhcmRlcicsICdBY2NlcHQnOiAnYXBwbGljYXRpb24vanNvbicgfSxcbiAgfSk7XG4gIGlmIChyLnN0YXR1cyA9PT0gNDI5KSB7IHNjbUNvb2xkb3duVW50aWwgPSBEYXRlLm5vdygpICsgU0NNX0NPT0xET1dOX01TOyByZXR1cm4ge307IH1cbiAgaWYgKHIub2spIHtcbiAgICBjb25zdCBqID0gYXdhaXQgci5qc29uKCkgYXMgeyBkYXRlPzogc3RyaW5nIH07XG4gICAgY29uc3QgZCA9IHRvSXNvKGouZGF0ZSk7XG4gICAgaWYgKGQpIHJldHVybiB7IGNvbW1pdFRpbWVzdGFtcDogZCwgY29tbWl0Q291bnQ6IDEgfTtcbiAgfVxuICByZXR1cm4ge307XG59XG5cbi8qKlxuICogUmVzb2x2ZSBgY29tbWl0VGltZXN0YW1wYCArIGBjb21taXRDb3VudGAgZm9yIGEgc291cmNlL2RlcGxveSBldmVudCwgaW4tYWNjb3VudCxcbiAqIGJlc3QtZWZmb3J0LiBDb21wdXRlZCBvbmNlIHBlciAocGlwZWxpbmVJZCwgc2hhKTsgYWR2YW5jZXMgdGhlIHBpcGVsaW5lJ3MgcmFuZ2VcbiAqIGJvdW5kYXJ5IG9ubHkgb24gc3VjY2Vzcy4gTmV2ZXIgdGhyb3dzIOKAlCByZXR1cm5zIHt9IG9uIGFueSBmYWlsdXJlLlxuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlQ29tbWl0SW5mbyhwaXBlbGluZUlkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcgfCBudWxsLCByZWdpb246IHN0cmluZywgc3JjOiBTb3VyY2VSZWYpOiBQcm9taXNlPENvbW1pdEluZm8+IHtcbiAgaWYgKHNyYy5wcm92aWRlciA9PT0gJ3Vua25vd24nKSByZXR1cm4ge307XG4gIGNvbnN0IHJrZXkgPSBgJHtwaXBlbGluZUlkfXwke3NyYy5zaGF9YDtcbiAgY29uc3QgY2FjaGVkID0gY29tbWl0UmVzdWx0Q2FjaGUuZ2V0KHJrZXkpO1xuICBpZiAoY2FjaGVkKSByZXR1cm4gY2FjaGVkO1xuXG4gIGNvbnN0IHByZXYgPSBsYXN0U2hhQnlQaXBlbGluZS5nZXQocGlwZWxpbmVJZCk7XG4gIGNvbnN0IHNpbmNlU2hhID0gcHJldiAmJiBwcmV2ICE9PSBzcmMuc2hhID8gcHJldiA6IHVuZGVmaW5lZDtcblxuICBsZXQgaW5mbzogQ29tbWl0SW5mbyA9IHt9O1xuICB0cnkge1xuICAgIGlmIChzcmMucHJvdmlkZXIgPT09ICdjb2RlY29tbWl0JyAmJiBzcmMucmVwb3NpdG9yeU5hbWUpIHtcbiAgICAgIGluZm8gPSBhd2FpdCByZXNvbHZlQ29kZUNvbW1pdChyZWdpb24sIHNyYy5yZXBvc2l0b3J5TmFtZSwgc3JjLnNoYSwgc2luY2VTaGEpO1xuICAgIH0gZWxzZSBpZiAoc3JjLnByb3ZpZGVyID09PSAnZ2l0aHViJyAmJiBzcmMub3duZXIgJiYgc3JjLnJlcG8pIHtcbiAgICAgIGlmIChEYXRlLm5vdygpID49IHNjbUNvb2xkb3duVW50aWwpIGluZm8gPSBhd2FpdCByZXNvbHZlR2l0SHViKHNyYy5vd25lciwgc3JjLnJlcG8sIHNyYy5zaGEsIHNpbmNlU2hhLCBvcmdJZCk7XG4gICAgfSBlbHNlIGlmIChzcmMucHJvdmlkZXIgPT09ICdiaXRidWNrZXQnICYmIHNyYy5vd25lciAmJiBzcmMucmVwbykge1xuICAgICAgaWYgKERhdGUubm93KCkgPj0gc2NtQ29vbGRvd25VbnRpbCkgaW5mbyA9IGF3YWl0IHJlc29sdmVCaXRidWNrZXQoc3JjLm93bmVyLCBzcmMucmVwbywgc3JjLnNoYSk7XG4gICAgfVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2cud2FybignQ29tbWl0IHJlc29sdXRpb24gZmFpbGVkIChvbWl0dGluZyBjb21taXRUaW1lc3RhbXAvY29tbWl0Q291bnQpJywgeyBwcm92aWRlcjogc3JjLnByb3ZpZGVyLCBlcnJvcjogKGVyciBhcyB7IG5hbWU/OiBzdHJpbmcgfSk/Lm5hbWUgPz8gU3RyaW5nKGVycikgfSk7XG4gICAgaW5mbyA9IHt9O1xuICB9XG5cbiAgY29tbWl0UmVzdWx0Q2FjaGUuc2V0KHJrZXksIGluZm8pO1xuICBpZiAoaW5mby5jb21taXRUaW1lc3RhbXApIGxhc3RTaGFCeVBpcGVsaW5lLnNldChwaXBlbGluZUlkLCBzcmMuc2hhKTtcbiAgcmV0dXJuIGluZm87XG59XG5cbi8vIOKUgOKUgOKUgCBFdmVudCBQYXJzaW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5pbnRlcmZhY2UgUGFyc2VkRXZlbnQge1xuICAvKipcbiAgICogUmVzb2x2ZWQgcGxhdGZvcm0gb3JnSWQgKGZyb20gdGhlIHBpcGVsaW5lJ3MgT3JnSWQgdGFnOyBudWxsIHdoZW4gdW50YWdnZWQpLlxuICAgKiBJTlRFUk5BTCDigJQgdXNlZCBvbmx5IHRvIGtleSB0aGUgcGVyLW9yZyBHaXRIdWIgdG9rZW4gYW5kIHRvIGF0dHJpYnV0ZVxuICAgKiBpbmdlc3QtaGVhbHRoLiBTdHJpcHBlZCBmcm9tIHRoZSBwYXlsb2FkIFBPU1RlZCB0byAvcmVwb3J0cy9ldmVudHMgKHdoaWNoXG4gICAqIHJlc29sdmVzIG9yZyBmcm9tIHRoZSBwaXBlbGluZSByZWdpc3RyeSwgbm90IHRoZSBib2R5KS5cbiAgICovXG4gIG9yZ0lkOiBzdHJpbmcgfCBudWxsO1xuICBwaXBlbGluZUlkOiBzdHJpbmc7XG4gIGV2ZW50U291cmNlOiBzdHJpbmc7XG4gIGV2ZW50VHlwZTogc3RyaW5nO1xuICBzdGF0dXM6IHN0cmluZztcbiAgZXhlY3V0aW9uSWQ/OiBzdHJpbmc7XG4gIHN0YWdlTmFtZT86IHN0cmluZztcbiAgYWN0aW9uTmFtZT86IHN0cmluZztcbiAgLyoqIEh1bWFuLXJlYWRhYmxlIGZhaWx1cmUgcmVhc29uLCBmcm9tIGFuIEFjdGlvbiBldmVudCdzXG4gICAqICBgZXhlY3V0aW9uLXJlc3VsdC5leHRlcm5hbC1leGVjdXRpb24tc3VtbWFyeWAuIFRoZSBsb2cgVVJMICsgZXJyb3ItY29kZVxuICAgKiAgc3RheSBpbiBgZGV0YWlsYCBmb3IgZHJpbGwtZG93bi4gKi9cbiAgZXJyb3JNZXNzYWdlPzogc3RyaW5nO1xuICBzdGFydGVkQXQ/OiBzdHJpbmc7XG4gIGNvbXBsZXRlZEF0Pzogc3RyaW5nO1xuICBkdXJhdGlvbk1zPzogbnVtYmVyO1xuICAvKiogU291cmNlIGNvbW1pdCBpZCAoRE9SQSBkZXBsb3kgYXR0cmlidXRpb24pLiBVbmRlZmluZWQgd2hlbiB0aGUgZXZlbnRcbiAgICogIGNhcnJpZXMgbm8gc291cmNlIHJldmlzaW9uIOKAlCBzdG9yZWQgTlVMTCBkb3duc3RyZWFtLiAqL1xuICBjb21taXRTaGE/OiBzdHJpbmc7XG4gIC8qKiBTb3VyY2UgYnJhbmNoL3JlZiAoRE9SQSBkZXBsb3kgYXR0cmlidXRpb24pLiBVbmRlZmluZWQgd2hlbiB1bmF2YWlsYWJsZS4gKi9cbiAgY29tbWl0UmVmPzogc3RyaW5nO1xuICAvKiogT2xkZXN0LXVuc2hpcHBlZCBjb21taXQgdGltZXN0YW1wIChJU08gODYwMSkgZm9yIHRoZSByYW5nZSBzaW5jZSB0aGUgbGFzdFxuICAgKiAgZGVwbG95IOKAlCByZXNvbHZlZCBpbi1hY2NvdW50IChQaGFzZSA0KS4gVW5kZWZpbmVkIHdoZW4gdW5yZXNvbHZhYmxlLiAqL1xuICBjb21taXRUaW1lc3RhbXA/OiBzdHJpbmc7XG4gIC8qKiBOdW1iZXIgb2YgY29tbWl0cyBpbiB0aGUgcmVzb2x2ZWQgcmFuZ2UgKOKJpTEpLiBVbmRlZmluZWQgd2hlbiB1bnJlc29sdmFibGUuICovXG4gIGNvbW1pdENvdW50PzogbnVtYmVyO1xuICAvKiogRGVwbG95IGVudmlyb25tZW50IOKAlCBzZXQgb25seSB3aGVuIHRoZSBldmVudCdzIHN0YWdlIGlzIGxpc3RlZCBpbiB0aGVcbiAgICogIHBpcGVsaW5lJ3MgYHBiLmRlcGxveXNgIHRhZy4gVW5kZWZpbmVkIG90aGVyd2lzZSAo4oeSIG5vdCBhIGRlcGxveSkuICovXG4gIGVudmlyb25tZW50Pzogc3RyaW5nO1xuICBkZXRhaWw6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xufVxuXG4vKiogQ2FwIG9uIHRoZSBzdG9yZWQgZmFpbHVyZSBzdW1tYXJ5IOKAlCBDb2RlQnVpbGQvRGVwbG95IHN1bW1hcmllcyBjYW4gYmUgbG9uZy4gKi9cbmNvbnN0IE1BWF9FUlJPUl9NRVNTQUdFID0gNDAwMDtcblxuLyoqIEZpcnN0IG5vbi1lbXB0eSBzdHJpbmcgYW1vbmcgdGhlIGNhbmRpZGF0ZXMsIGNhcHBlZCB0byB0aGUgaW5nZXN0IG1heC4gKi9cbmZ1bmN0aW9uIGZpcnN0QXR0ciguLi52YWxzOiB1bmtub3duW10pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBmb3IgKGNvbnN0IHYgb2YgdmFscykge1xuICAgIGlmICh0eXBlb2YgdiA9PT0gJ3N0cmluZycgJiYgdi5sZW5ndGggPiAwKSByZXR1cm4gdi5zbGljZSgwLCBNQVhfQVRUUik7XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBCZXN0LWVmZm9ydCBleHRyYWN0aW9uIG9mIHRoZSBzb3VyY2UgY29tbWl0IGlkICsgcmVmIChhbmQgdGhlIHJldmlzaW9uIFVSTCwgdXNlZFxuICogb25seSBpbi1hY2NvdW50IHRvIGNsYXNzaWZ5IHRoZSBzb3VyY2UgdHlwZSBmb3IgY29tbWl0LXJhbmdlIHJlc29sdXRpb24pIGZyb20gYVxuICogQ29kZVBpcGVsaW5lIGV2ZW50IGRldGFpbC4gQ29kZVBpcGVsaW5lIHN1cmZhY2VzIHRoZSBzb3VyY2UgcmV2aXNpb24gdW5kZXIgYSBmZXdcbiAqIHNoYXBlcyBkZXBlbmRpbmcgb24gdGhlIGV2ZW50IHR5cGUgLyBzb3VyY2UtYWN0aW9uOyB3ZSBjaGVjayB0aGUga25vd24gb25lcyBhbmRcbiAqIGxlYXZlIHRoZSBmaWVsZHMgdW5kZWZpbmVkIHdoZW4gbm9uZSBhcmUgcHJlc2VudCAoTlVMTCBkb3duc3RyZWFtKS4gTmV2ZXIgcHVsbHMgYW5cbiAqIEFSTi9hY2NvdW50IGlkOiBvbmx5IHJldmlzaW9uL2JyYW5jaC91cmwgc3RyaW5nIGZpZWxkcyBhcmUgcmVhZDsgdGhlIHJldmlzaW9uVXJsXG4gKiBpcyBjb25zdW1lZCBpbi1hY2NvdW50IChzb3VyY2UgY2xhc3NpZmljYXRpb24pIGFuZCBuZXZlciBmb3J3YXJkZWQgb24gaXRzIG93bi5cbiAqL1xuZnVuY3Rpb24gZXh0cmFjdENvbW1pdChkZXRhaWw6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogeyBjb21taXRTaGE/OiBzdHJpbmc7IGNvbW1pdFJlZj86IHN0cmluZzsgcmV2aXNpb25Vcmw/OiBzdHJpbmcgfSB7XG4gIC8vIGBzb3VyY2UtcmV2aXNpb25zYCAoa2ViYWIsIEV2ZW50QnJpZGdlKSAvIGBzb3VyY2VSZXZpc2lvbnNgIChjYW1lbCkg4oCUIGZpcnN0IGVudHJ5LlxuICBjb25zdCByZXZzID0gKGRldGFpbFsnc291cmNlLXJldmlzaW9ucyddID8/IGRldGFpbC5zb3VyY2VSZXZpc2lvbnMpIGFzXG4gICAgfCBBcnJheTxSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4gfCB1bmRlZmluZWQ7XG4gIGNvbnN0IGZpcnN0UmV2ID0gQXJyYXkuaXNBcnJheShyZXZzKSA/IHJldnNbMF0gOiB1bmRlZmluZWQ7XG4gIC8vIE9uIHNvdXJjZSBhY3Rpb25zIHRoZSByZXZpc2lvbiBpZCBjYW4gYWxzbyByaWRlIG9uIGBleGVjdXRpb24tcmVzdWx0YC5cbiAgY29uc3QgZXhlY1Jlc3VsdCA9IGRldGFpbFsnZXhlY3V0aW9uLXJlc3VsdCddIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkO1xuXG4gIGNvbnN0IGNvbW1pdFNoYSA9IGZpcnN0QXR0cihcbiAgICBkZXRhaWwuY29tbWl0SWQsXG4gICAgZGV0YWlsLnJldmlzaW9uSWQsXG4gICAgZmlyc3RSZXY/LnJldmlzaW9uSWQsXG4gICAgZXhlY1Jlc3VsdD8ucmV2aXNpb25JZCxcbiAgKTtcbiAgY29uc3QgY29tbWl0UmVmID0gZmlyc3RBdHRyKFxuICAgIGRldGFpbC5jb21taXRSZWYsXG4gICAgZGV0YWlsLmJyYW5jaCxcbiAgICBkZXRhaWwuc291cmNlQnJhbmNoLFxuICAgIGZpcnN0UmV2Py5icmFuY2hOYW1lLFxuICAgIGZpcnN0UmV2Py5icmFuY2gsXG4gICk7XG4gIGNvbnN0IHJldmlzaW9uVXJsID0gdHlwZW9mIGZpcnN0UmV2Py5yZXZpc2lvblVybCA9PT0gJ3N0cmluZycgPyBmaXJzdFJldi5yZXZpc2lvblVybCA6IHVuZGVmaW5lZDtcblxuICByZXR1cm4geyAuLi4oY29tbWl0U2hhICYmIHsgY29tbWl0U2hhIH0pLCAuLi4oY29tbWl0UmVmICYmIHsgY29tbWl0UmVmIH0pLCAuLi4ocmV2aXNpb25VcmwgJiYgeyByZXZpc2lvblVybCB9KSB9O1xufVxuXG5mdW5jdGlvbiBjbGFzc2lmeUV2ZW50KGRldGFpbFR5cGU6IHN0cmluZyk6IHsgZXZlbnRUeXBlOiBzdHJpbmc7IGV2ZW50U291cmNlOiBzdHJpbmcgfSB7XG4gIGlmIChkZXRhaWxUeXBlLmluY2x1ZGVzKCdQaXBlbGluZSBFeGVjdXRpb24nKSkgcmV0dXJuIHsgZXZlbnRUeXBlOiAnUElQRUxJTkUnLCBldmVudFNvdXJjZTogJ2NvZGVwaXBlbGluZScgfTtcbiAgaWYgKGRldGFpbFR5cGUuaW5jbHVkZXMoJ1N0YWdlIEV4ZWN1dGlvbicpKSByZXR1cm4geyBldmVudFR5cGU6ICdTVEFHRScsIGV2ZW50U291cmNlOiAnY29kZXBpcGVsaW5lJyB9O1xuICBpZiAoZGV0YWlsVHlwZS5pbmNsdWRlcygnQWN0aW9uIEV4ZWN1dGlvbicpKSByZXR1cm4geyBldmVudFR5cGU6ICdBQ1RJT04nLCBldmVudFNvdXJjZTogJ2NvZGVwaXBlbGluZScgfTtcbiAgLy8gQ29kZUJ1aWxkIFwiQnVpbGQgU3RhdGVcIiBldmVudHMgaWRlbnRpZnkgYSBidWlsZCAqcHJvamVjdCosIG5vdCBhIHBpcGVsaW5lLCBzb1xuICAvLyBwYXJzZVJlY29yZCBkcm9wcyB0aGVtIG9uIHRoZSBgZXZlbnRTb3VyY2UgIT09ICdjb2RlcGlwZWxpbmUnYCBjaGVjayBiZWxvdyDigJRcbiAgLy8gdGhlICdCVUlMRCcgZXZlbnRUeXBlIGlzIG5ldmVyIHBlcnNpc3RlZCBoZXJlIChwbHVnaW4gQlVJTEQgZXZlbnRzIGFyZVxuICAvLyByZWNvcmRlZCBkaXJlY3RseSBieSB0aGUgcGx1Z2luIHNlcnZpY2UpLiBUaGUgJ2NvZGVidWlsZCcgc291cmNlIGlzIHdoYXRcbiAgLy8gcm91dGVzIHRoZSBkcm9wOyBrZWVwIHRoaXMgYnJhbmNoIG9ubHkgZm9yIHRoYXQuXG4gIGlmIChkZXRhaWxUeXBlLmluY2x1ZGVzKCdCdWlsZCBTdGF0ZScpKSByZXR1cm4geyBldmVudFR5cGU6ICdCVUlMRCcsIGV2ZW50U291cmNlOiAnY29kZWJ1aWxkJyB9O1xuICByZXR1cm4geyBldmVudFR5cGU6ICdQSVBFTElORScsIGV2ZW50U291cmNlOiAnY29kZXBpcGVsaW5lJyB9O1xufVxuXG4vKiogUGFyc2UgKyByZXNvbHZlIG9uZSByZWNvcmQgdG8gYSByZXBvcnRhYmxlIGV2ZW50LCBvciBudWxsIHRvIHNraXAgaXQuICovXG5hc3luYyBmdW5jdGlvbiBwYXJzZVJlY29yZChyZWNvcmQ6IFNRU1JlY29yZCk6IFByb21pc2U8UGFyc2VkRXZlbnQgfCBudWxsPiB7XG4gIGxldCBldmVudDoge1xuICAgICdkZXRhaWwtdHlwZSc6IHN0cmluZztcbiAgICAnc291cmNlJzogc3RyaW5nO1xuICAgICdkZXRhaWwnOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAndGltZSc6IHN0cmluZztcbiAgICAncmVnaW9uJzogc3RyaW5nO1xuICAgICdhY2NvdW50Jzogc3RyaW5nO1xuICB9O1xuICAvLyBBIG1hbGZvcm1lZCBib2R5IGlzIGEgZGVhZCBtZXNzYWdlIChyZXRyeWluZyB3b24ndCBoZWxwKSDigJQgbG9nICsgc2tpcCBpdCBzb1xuICAvLyBpdCBjYW4ndCBmYWlsIHRoZSB3aG9sZSBiYXRjaC4gUmVzb2x1dGlvbiBlcnJvcnMgYmVsb3cgKGUuZy4gQWNjZXNzRGVuaWVkKVxuICAvLyBhcmUgTk9UIGNhdWdodCBoZXJlOiB0aG9zZSBhcmUgdHJhbnNpZW50L2luZnJhIGFuZCBtdXN0IHByb3BhZ2F0ZSB0byByZXRyeS5cbiAgdHJ5IHtcbiAgICBldmVudCA9IEpTT04ucGFyc2UocmVjb3JkLmJvZHkpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2cud2FybignU2tpcHBpbmcgU1FTIHJlY29yZCB3aXRoIHVucGFyc2VhYmxlIGJvZHknLCB7IG1lc3NhZ2VJZDogcmVjb3JkLm1lc3NhZ2VJZCwgZXJyb3I6IFN0cmluZyhlcnIpIH0pO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgY29uc3QgeyBldmVudFR5cGUsIGV2ZW50U291cmNlIH0gPSBjbGFzc2lmeUV2ZW50KGV2ZW50WydkZXRhaWwtdHlwZSddKTtcbiAgY29uc3QgZGV0YWlsID0geyAuLi5ldmVudC5kZXRhaWwgfTtcbiAgLy8gVGhlIHJhdyBhY2NvdW50IG5ldmVyIG5lZWRzIHRvIGxlYXZlIEFXUyBub3cg4oCUIGRyb3AgaXQgZnJvbSB0aGUgcGF5bG9hZC5cbiAgZGVsZXRlIGRldGFpbC5hY2NvdW50O1xuXG4gIC8vIENvZGVCdWlsZCBcIkJ1aWxkIFN0YXRlXCIgZXZlbnRzIGlkZW50aWZ5IGEgYnVpbGQgKnByb2plY3QqLCBub3QgYSBwaXBlbGluZSxcbiAgLy8gYW5kIGEgcHJvamVjdCBjYW4gYmUgc2hhcmVkIGFjcm9zcyBwaXBlbGluZXMg4oCUIHRoZXJlJ3Mgbm8gY2xlYW4gMToxIG1hcHBpbmdcbiAgLy8gdG8gYSBwaXBlbGluZSBpZCwgc28gc2tpcCB0aGVtLlxuICBpZiAoZXZlbnRTb3VyY2UgIT09ICdjb2RlcGlwZWxpbmUnKSB7XG4gICAgbG9nLndhcm4oJ1NraXBwaW5nIG5vbi1Db2RlUGlwZWxpbmUgZXZlbnQgKG5vIHBpcGVsaW5lIHRhZyB0byByZXNvbHZlKScsIHsgZGV0YWlsVHlwZTogZXZlbnRbJ2RldGFpbC10eXBlJ10gfSk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBjb25zdCBwaXBlbGluZU5hbWUgPSBkZXRhaWwucGlwZWxpbmUgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBpZiAoIXBpcGVsaW5lTmFtZSkge1xuICAgIGxvZy53YXJuKCdDb2RlUGlwZWxpbmUgZXZlbnQgbWlzc2luZyBwaXBlbGluZSBuYW1lIOKAlCBza2lwcGluZycpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gUmVzb2x2ZSB0aGUgcGlwZWxpbmUncyBwYi5waXBlbGluZS1pZCAoPSBwbGF0Zm9ybSBwaXBlbGluZUlkKSArIHBiLmRlcGxveXMgbWFwLlxuICAvLyBUaGUgQVJOIGlzIG9ubHkgYSB0cmFuc2llbnQgaGFuZGxlIGZvciB0aGUgdGFnIGxvb2t1cDsgaXQgaXMgbmV2ZXIgc3RvcmVkLlxuICBjb25zdCBhcm4gPSBgYXJuOmF3czpjb2RlcGlwZWxpbmU6JHtldmVudC5yZWdpb259OiR7ZXZlbnQuYWNjb3VudH06JHtwaXBlbGluZU5hbWV9YDtcbiAgY29uc3QgeyBwaXBlbGluZUlkLCBvcmdJZCwgZGVwbG95cyB9ID0gYXdhaXQgcmVzb2x2ZVBpcGVsaW5lKGFybiwgZXZlbnQucmVnaW9uKTtcbiAgaWYgKCFwaXBlbGluZUlkKSByZXR1cm4gbnVsbDsgLy8gdW50YWdnZWQgLyB1bnJlZ2lzdGVyZWQg4oaSIHNraXBcblxuICBjb25zdCBzdGFydGVkQXQgPSAoZGV0YWlsWydzdGFydC10aW1lJ10gYXMgc3RyaW5nKSB8fCBldmVudC50aW1lO1xuICBjb25zdCBzdGF0ZSA9IGRldGFpbC5zdGF0ZSBhcyBzdHJpbmc7XG5cbiAgbGV0IGR1cmF0aW9uTXM6IG51bWJlciB8IHVuZGVmaW5lZDtcbiAgbGV0IGNvbXBsZXRlZEF0OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgaWYgKFsnU1VDQ0VFREVEJywgJ0ZBSUxFRCcsICdDQU5DRUxFRCcsICdTVE9QUEVEJ10uaW5jbHVkZXMoc3RhdGUpKSB7XG4gICAgY29tcGxldGVkQXQgPSBldmVudC50aW1lO1xuICAgIGlmIChzdGFydGVkQXQgJiYgY29tcGxldGVkQXQpIHtcbiAgICAgIGNvbnN0IG1zID0gbmV3IERhdGUoY29tcGxldGVkQXQpLmdldFRpbWUoKSAtIG5ldyBEYXRlKHN0YXJ0ZWRBdCkuZ2V0VGltZSgpO1xuICAgICAgaWYgKG1zID49IDApIGR1cmF0aW9uTXMgPSBtcztcbiAgICB9XG4gIH1cblxuICAvLyBQcm9tb3RlIHRoZSBmYWlsdXJlIHJlYXNvbiB0byBhIHR5cGVkIGZpZWxkLiBPbiBBY3Rpb24gZXZlbnRzIEFXUyBwdXRzIHRoZVxuICAvLyBodW1hbi1yZWFkYWJsZSBzdW1tYXJ5IChhbmQgYSBsb2cgVVJMICsgZXJyb3ItY29kZSwgd2hpY2ggc3RheSBpbiBgZGV0YWlsYClcbiAgLy8gdW5kZXIgYGV4ZWN1dGlvbi1yZXN1bHRgLlxuICBjb25zdCByZXN1bHQgPSBkZXRhaWxbJ2V4ZWN1dGlvbi1yZXN1bHQnXSBhcyB7ICdleHRlcm5hbC1leGVjdXRpb24tc3VtbWFyeSc/OiB1bmtub3duIH0gfCB1bmRlZmluZWQ7XG4gIGNvbnN0IHN1bW1hcnkgPSByZXN1bHQ/LlsnZXh0ZXJuYWwtZXhlY3V0aW9uLXN1bW1hcnknXTtcbiAgY29uc3QgZXJyb3JNZXNzYWdlID0gdHlwZW9mIHN1bW1hcnkgPT09ICdzdHJpbmcnICYmIHN1bW1hcnkubGVuZ3RoID4gMFxuICAgID8gc3VtbWFyeS5zbGljZSgwLCBNQVhfRVJST1JfTUVTU0FHRSlcbiAgICA6IHVuZGVmaW5lZDtcblxuICAvLyBET1JBIGRlcGxveSBhdHRyaWJ1dGlvbiDigJQgYWxsIG9wdGlvbmFsOiBvbWl0dGVkICh1bmRlZmluZWQpIHdoZW4gdGhlXG4gIC8vIGV2ZW50L3BpcGVsaW5lIGRvZXNuJ3QgY2FycnkgdGhlbS5cbiAgY29uc3QgeyBjb21taXRTaGEsIGNvbW1pdFJlZiwgcmV2aXNpb25VcmwgfSA9IGV4dHJhY3RDb21taXQoZGV0YWlsKTtcblxuICBjb25zdCBleGVjdXRpb25JZCA9IGRldGFpbFsnZXhlY3V0aW9uLWlkJ10gYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBjb25zdCBzdGFnZU5hbWUgPSBkZXRhaWwuc3RhZ2UgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBjb25zdCBhY3Rpb25OYW1lID0gZGV0YWlsLmFjdGlvbiBhcyBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgLy8gRW52aXJvbm1lbnQgKOKHkiBpc0RlcGxveSkgaXMgc2V0IE9OTFkgd2hlbiB0aGlzIGV2ZW50J3Mgc3RhZ2UgaXMgbGlzdGVkIGluIHRoZVxuICAvLyBwaXBlbGluZSdzIHBiLmRlcGxveXMgdGFnLiBQSVBFTElORSBldmVudHMgaGF2ZSBubyBzdGFnZSDihpIgbmV2ZXIgYSBkZXBsb3kuXG4gIGNvbnN0IGVudmlyb25tZW50ID0gc3RhZ2VOYW1lID8gZGVwbG95cy5nZXQoc3RhZ2VOYW1lKSA6IHVuZGVmaW5lZDtcblxuICAvLyBQaGFzZSA0IOKAlCByZXNvbHZlIHRoZSBjb21taXQgcmFuZ2UgKG9sZGVzdC11bnNoaXBwZWQgdGltZXN0YW1wICsgY291bnQpIGZvciBhbnlcbiAgLy8gZXZlbnQgY2FycnlpbmcgYSBjb21taXQuIEJlc3QtZWZmb3J0L2luLWFjY291bnQ7IG9taXR0ZWQgb24gYW55IGZhaWx1cmUuXG4gIC8vIEdBVEVEIG9uIGBET1JBX0VOQUJMRURgIChzZXQgYnkgYHNldHVwLWV2ZW50cyAtLXdpdGgtZG9yYWApOiBET1JBIGxlYWQgdGltZSBpcyBhblxuICAvLyBhZHZhbmNlZF9yZXBvcnRpbmcgYWRkLW9uLCBzbyBvcmdzIHdpdGhvdXQgaXQgc2tpcCB0aGUgU0NNL3NlY3JldCBjb3N0IGVudGlyZWx5IOKAlFxuICAvLyBzdGFuZGFyZCByZXBvcnRpbmcgKGNvbW1pdFNoYSArIGFsbCBvdGhlciBmaWVsZHMpIHN0aWxsIGZvcndhcmRzIHJlZ2FyZGxlc3MuXG4gIGxldCBjb21taXRUaW1lc3RhbXA6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgbGV0IGNvbW1pdENvdW50OiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gIGlmIChjb21taXRTaGEgJiYgcHJvY2Vzcy5lbnYuRE9SQV9FTkFCTEVEID09PSAndHJ1ZScpIHtcbiAgICBjb25zdCBzcmMgPSBjbGFzc2lmeVNvdXJjZShyZXZpc2lvblVybCk7XG4gICAgY29uc3QgaW5mbyA9IGF3YWl0IHJlc29sdmVDb21taXRJbmZvKHBpcGVsaW5lSWQsIG9yZ0lkLCBldmVudC5yZWdpb24sIHsgc2hhOiBjb21taXRTaGEsIC4uLnNyYyB9KTtcbiAgICBjb21taXRUaW1lc3RhbXAgPSBpbmZvLmNvbW1pdFRpbWVzdGFtcDtcbiAgICBjb21taXRDb3VudCA9IGluZm8uY29tbWl0Q291bnQ7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG9yZ0lkLFxuICAgIHBpcGVsaW5lSWQsXG4gICAgZXZlbnRTb3VyY2UsXG4gICAgZXZlbnRUeXBlLFxuICAgIHN0YXR1czogc3RhdGUsXG4gICAgZXhlY3V0aW9uSWQsXG4gICAgc3RhZ2VOYW1lLFxuICAgIGFjdGlvbk5hbWUsXG4gICAgZXJyb3JNZXNzYWdlLFxuICAgIHN0YXJ0ZWRBdCxcbiAgICBjb21wbGV0ZWRBdCxcbiAgICBkdXJhdGlvbk1zLFxuICAgIC4uLihjb21taXRTaGEgJiYgeyBjb21taXRTaGEgfSksXG4gICAgLi4uKGNvbW1pdFJlZiAmJiB7IGNvbW1pdFJlZiB9KSxcbiAgICAuLi4oY29tbWl0VGltZXN0YW1wICYmIHsgY29tbWl0VGltZXN0YW1wIH0pLFxuICAgIC4uLihjb21taXRDb3VudCAmJiB7IGNvbW1pdENvdW50IH0pLFxuICAgIC4uLihlbnZpcm9ubWVudCAmJiB7IGVudmlyb25tZW50IH0pLFxuICAgIGRldGFpbCxcbiAgfTtcbn1cblxuLy8g4pSA4pSA4pSAIFBoYXNlIDM6IGRlbGl2ZXJ5IGhlYWx0aCArIHNlbGYtaGVhbGluZyBETFEgcmVkcml2ZSDilIBcbi8vIEFmdGVyIGEgU1VDQ0VTU0ZVTCBiYXRjaCBQT1NUIHdlICh0aHJvdHRsZWQsIGJlc3QtZWZmb3J0KTpcbi8vICAgMS4gUE9TVCBhbiBpbmdlc3QtaGVhbHRoIHNpZ25hbCB0byAvYXBpL3JlcG9ydHMvaW5nZXN0LWhlYWx0aCBzbyB0aGUgUmVwb3J0cyBVSVxuLy8gICAgICBjYW4gc2hvdyBmbG93aW5nIC8gc3RhbGUgLyBkcm9wcGluZy5cbi8vICAgMi4gSWYgdGhlIERMUSBpcyBub24tZW1wdHksIG5vIG1vdmUgdGFzayBpcyBhbHJlYWR5IHJ1bm5pbmcsIEFORCB0aGUgb2xkZXN0IERMUVxuLy8gICAgICBtZXNzYWdlIGlzIHlvdW5nZXIgdGhhbiB0aGUgcG9pc29uLWFnZSBjZWlsaW5nLCBraWNrIG9mZiBhbiBTUVNcbi8vICAgICAgTWVzc2FnZU1vdmVUYXNrIChETFEg4oaSIG1haW4gcXVldWUpIHNvIHJldHJ5YWJsZSBmYWlsdXJlcyBzZWxmLWhlYWwgd2l0aG91dCBhXG4vLyAgICAgIG5ldyBBV1Mgc2VydmljZS4gVGhlIHJlcG9ydGluZyBpbmdlc3QncyBwYXJ0aWFsLXVuaXF1ZSBpbmRleCBkZWR1cGVzIGFueVxuLy8gICAgICByZWRlbGl2ZXJlZCBldmVudCwgc28gYSByZWRyaXZlIGNhbid0IGRvdWJsZS1jb3VudC5cbi8vIEd1YXJkczogb25seSBvbiBzdWNjZXNzLCBvbmUtYXQtYS10aW1lIChMaXN0TWVzc2FnZU1vdmVUYXNrcyksIGEgcG9pc29uLWFnZSBjZWlsaW5nXG4vLyAoc28gYSBtZXNzYWdlIHRoYXQgYWx3YXlzIGZhaWxzIGNhbid0IGxvb3AgRExR4oaSbWFpbuKGkkRMUSBmb3JldmVyKSwgYW5kIHRocm90dGxlZCB2aWFcbi8vIG1vZHVsZSB0aW1lc3RhbXBzLiBORVZFUiB0aHJvd3Mg4oCUIGEgZmFpbHVyZSBoZXJlIG11c3Qgbm90IGZhaWwgdGhlIGJhdGNoLlxuLy9cbi8vIElBTSByZXF1aXJlZCAoZG9jdW1lbnQgb25seSDigJQgZ3JhbnRlZCBvbiB0aGUgZXZlbnRzIHN0YWNrKTpcbi8vICAgc3FzOkdldFF1ZXVlQXR0cmlidXRlcywgc3FzOkxpc3RNZXNzYWdlTW92ZVRhc2tzLCBzcXM6U3RhcnRNZXNzYWdlTW92ZVRhc2sgKG9uIHRoZSBETFEpXG5cbmludGVyZmFjZSBTcXNDbGllbnRMaWtlIHsgc2VuZChjb21tYW5kOiB1bmtub3duKTogUHJvbWlzZTx1bmtub3duPiB9XG5pbnRlcmZhY2UgU3FzTW9kdWxlIHtcbiAgU1FTQ2xpZW50OiBuZXcgKGNvbmZpZzogeyByZWdpb246IHN0cmluZyB9KSA9PiBTcXNDbGllbnRMaWtlO1xuICBHZXRRdWV1ZUF0dHJpYnV0ZXNDb21tYW5kOiBuZXcgKGlucHV0OiB7IFF1ZXVlVXJsOiBzdHJpbmc7IEF0dHJpYnV0ZU5hbWVzOiBzdHJpbmdbXSB9KSA9PiB1bmtub3duO1xuICBMaXN0TWVzc2FnZU1vdmVUYXNrc0NvbW1hbmQ6IG5ldyAoaW5wdXQ6IHsgU291cmNlQXJuOiBzdHJpbmcgfSkgPT4gdW5rbm93bjtcbiAgU3RhcnRNZXNzYWdlTW92ZVRhc2tDb21tYW5kOiBuZXcgKGlucHV0OiB7IFNvdXJjZUFybjogc3RyaW5nOyBEZXN0aW5hdGlvbkFybj86IHN0cmluZyB9KSA9PiB1bmtub3duO1xufVxubGV0IHNxc01vZDogU3FzTW9kdWxlIHwgdW5kZWZpbmVkO1xubGV0IHNxc0NsaWVudHNCeVJlZ2lvbjogTWFwPHN0cmluZywgU3FzQ2xpZW50TGlrZT4gfCB1bmRlZmluZWQ7XG5hc3luYyBmdW5jdGlvbiBzcXNDbGllbnQocmVnaW9uOiBzdHJpbmcpOiBQcm9taXNlPFNxc0NsaWVudExpa2U+IHtcbiAgaWYgKCFzcXNNb2QpIHNxc01vZCA9IGF3YWl0IGxvYWRTZGs8U3FzTW9kdWxlPignQGF3cy1zZGsvY2xpZW50LXNxcycpO1xuICBpZiAoIXNxc0NsaWVudHNCeVJlZ2lvbikgc3FzQ2xpZW50c0J5UmVnaW9uID0gbmV3IE1hcCgpO1xuICBsZXQgY2xpZW50ID0gc3FzQ2xpZW50c0J5UmVnaW9uLmdldChyZWdpb24pO1xuICBpZiAoIWNsaWVudCkge1xuICAgIGNsaWVudCA9IG5ldyBzcXNNb2QuU1FTQ2xpZW50KHsgcmVnaW9uIH0pO1xuICAgIHNxc0NsaWVudHNCeVJlZ2lvbi5zZXQocmVnaW9uLCBjbGllbnQpO1xuICB9XG4gIHJldHVybiBjbGllbnQ7XG59XG5cbi8vIERlbGl2ZXJ5LWhlYWx0aCBjb3VudGVycyBhcmUgYXR0cmlidXRlZCBQRVIgUkVTT0xWRUQgT1JHICh0aGUgZm9yd2FyZGVyIGlzIGEgZmxlZXRcbi8vIExhbWJkYSBzZXJ2aW5nIG1hbnkgb3JnczsgYSBzaW5nbGUgZ2xvYmFsIGNvdW50ZXIgd291bGQgbWlzLWF0dHJpYnV0ZSBvbmUgb3JnJ3Ncbi8vIHRocm91Z2hwdXQgdG8gYW5vdGhlcikuIEtleWVkIGJ5IG9yZ0lkICgnJyBidWNrZXQgPSBwaXBlbGluZSB3aXRoIG5vIE9yZ0lkIHRhZykuXG4vLyBUaGUgRExRIGRlcHRoIChgZHJvcHBlZGApIGlzIGdlbnVpbmVseSBmbGVldC1nbG9iYWwg4oCUIG9uZSBzaGFyZWQgRExRIOKAlCBzbyBpdCBpcyBhXG4vLyBzaW5nbGUgc25hcHNob3QgcG9zdGVkIGFsb25nc2lkZSBlYWNoIG9yZydzIGhlYWx0aCwgZG9jdW1lbnRlZCBhcyB0aGUgc2hhcmVkIGRlcHRoLlxuY29uc3QgZm9yd2FyZGVkQnlPcmcgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xuY29uc3QgbGFzdEV2ZW50QXRCeU9yZyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG5sZXQgbGFzdEhlYWx0aEF0ID0gMDtcbmxldCBsYXN0UmVkcml2ZUF0ID0gMDtcbmNvbnN0IEhFQUxUSF9USFJPVFRMRV9NUyA9IDYwICogMTAwMDtcbmNvbnN0IFJFRFJJVkVfVEhST1RUTEVfTVMgPSA1ICogNjAgKiAxMDAwO1xuLy8gUG9pc29uLW1lc3NhZ2UgY2VpbGluZzogaWYgdGhlIG9sZGVzdCBETFEgbWVzc2FnZSBpcyBvbGRlciB0aGFuIHRoaXMsIHdlIFNUT1Bcbi8vIHJlZHJpdmluZyBpdCAoRExR4oaSbWFpbuKGkmZhaWzihpJETFEgd291bGQgbG9vcCBmb3JldmVyKS4gU3VjaCBtZXNzYWdlcyBhcmUgbGVmdCBmb3IgYW5cbi8vIGFsYXJtIC8gbWFudWFsIGluc3BlY3Rpb24gaW5zdGVhZCBvZiBjaHVybmluZyB0aGUgZmxlZXQgaW5kZWZpbml0ZWx5LlxuY29uc3QgTUFYX0RMUV9SRURSSVZFX0FHRV9TRUMgPSA2ICogNjAgKiA2MDsgLy8gNiBob3Vyc1xuXG4vKiogUGFyc2UgdGhlIFNRUyB0cmlnZ2VyIEFSTiBhbmQgZGVyaXZlIHRoZSBtYWluICsgRExRIEFSTnMgYW5kIHRoZSBETFEgVVJMLiAqL1xuZnVuY3Rpb24gZGVyaXZlUXVldWVzKG1haW5RdWV1ZUFybjogc3RyaW5nIHwgdW5kZWZpbmVkKTogeyByZWdpb246IHN0cmluZzsgbWFpbkFybjogc3RyaW5nOyBkbHFBcm46IHN0cmluZzsgZGxxVXJsOiBzdHJpbmcgfSB8IHVuZGVmaW5lZCB7XG4gIGlmICghbWFpblF1ZXVlQXJuKSByZXR1cm4gdW5kZWZpbmVkO1xuICAvLyBhcm46YXdzOnNxczp7cmVnaW9ufTp7YWNjb3VudH06e25hbWV9XG4gIGNvbnN0IHBhcnRzID0gbWFpblF1ZXVlQXJuLnNwbGl0KCc6Jyk7XG4gIGlmIChwYXJ0cy5sZW5ndGggPCA2IHx8IHBhcnRzWzJdICE9PSAnc3FzJykgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgWywgLCAsIHJlZ2lvbiwgYWNjb3VudCwgbmFtZV0gPSBwYXJ0cztcbiAgY29uc3QgZGxxQXJuID0gcHJvY2Vzcy5lbnYuRVZFTlRfRExRX0FSTiB8fCBgYXJuOmF3czpzcXM6JHtyZWdpb259OiR7YWNjb3VudH06JHtuYW1lfS1kbHFgO1xuICBjb25zdCBkbHFOYW1lID0gZGxxQXJuLnNwbGl0KCc6JykucG9wKCkgPz8gYCR7bmFtZX0tZGxxYDtcbiAgY29uc3QgZGxxVXJsID0gYGh0dHBzOi8vc3FzLiR7cmVnaW9ufS5hbWF6b25hd3MuY29tLyR7YWNjb3VudH0vJHtkbHFOYW1lfWA7XG4gIHJldHVybiB7IHJlZ2lvbiwgbWFpbkFybjogbWFpblF1ZXVlQXJuLCBkbHFBcm4sIGRscVVybCB9O1xufVxuXG4vKiogU25hcHNob3QgdGhlIERMUSBkZXB0aCArIHRoZSBhZ2UgKHNlY29uZHMpIG9mIGl0cyBvbGRlc3QgbWVzc2FnZSAoZm9yIHRoZVxuICogIHBvaXNvbi1tZXNzYWdlIHJlZHJpdmUgY2VpbGluZykuIE1pc3NpbmcvTmFOIGF0dHJpYnV0ZXMgZmFsbCBiYWNrIHRvIDAuICovXG5hc3luYyBmdW5jdGlvbiBkbHFTdGF0cyhyZWdpb246IHN0cmluZywgZGxxVXJsOiBzdHJpbmcpOiBQcm9taXNlPHsgZGVwdGg6IG51bWJlcjsgb2xkZXN0QWdlU2VjOiBudW1iZXIgfT4ge1xuICBpZiAoIXNxc01vZCkgc3FzTW9kID0gYXdhaXQgbG9hZFNkazxTcXNNb2R1bGU+KCdAYXdzLXNkay9jbGllbnQtc3FzJyk7XG4gIGNvbnN0IGNsaWVudCA9IGF3YWl0IHNxc0NsaWVudChyZWdpb24pO1xuICBjb25zdCBvdXQgPSBhd2FpdCBjbGllbnQuc2VuZChuZXcgc3FzTW9kLkdldFF1ZXVlQXR0cmlidXRlc0NvbW1hbmQoe1xuICAgIFF1ZXVlVXJsOiBkbHFVcmwsXG4gICAgQXR0cmlidXRlTmFtZXM6IFsnQXBwcm94aW1hdGVOdW1iZXJPZk1lc3NhZ2VzJywgJ0FwcHJveGltYXRlQWdlT2ZPbGRlc3RNZXNzYWdlJ10sXG4gIH0pKSBhcyB7IEF0dHJpYnV0ZXM/OiB7IEFwcHJveGltYXRlTnVtYmVyT2ZNZXNzYWdlcz86IHN0cmluZzsgQXBwcm94aW1hdGVBZ2VPZk9sZGVzdE1lc3NhZ2U/OiBzdHJpbmcgfSB9O1xuICBjb25zdCBuID0gTnVtYmVyKG91dC5BdHRyaWJ1dGVzPy5BcHByb3hpbWF0ZU51bWJlck9mTWVzc2FnZXMgPz8gJzAnKTtcbiAgY29uc3QgYWdlID0gTnVtYmVyKG91dC5BdHRyaWJ1dGVzPy5BcHByb3hpbWF0ZUFnZU9mT2xkZXN0TWVzc2FnZSA/PyAnMCcpO1xuICByZXR1cm4ge1xuICAgIGRlcHRoOiBOdW1iZXIuaXNGaW5pdGUobikgPyBuIDogMCxcbiAgICBvbGRlc3RBZ2VTZWM6IE51bWJlci5pc0Zpbml0ZShhZ2UpID8gYWdlIDogMCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaGFzQWN0aXZlTW92ZVRhc2socmVnaW9uOiBzdHJpbmcsIGRscUFybjogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGlmICghc3FzTW9kKSBzcXNNb2QgPSBhd2FpdCBsb2FkU2RrPFNxc01vZHVsZT4oJ0Bhd3Mtc2RrL2NsaWVudC1zcXMnKTtcbiAgY29uc3QgY2xpZW50ID0gYXdhaXQgc3FzQ2xpZW50KHJlZ2lvbik7XG4gIGNvbnN0IG91dCA9IGF3YWl0IGNsaWVudC5zZW5kKG5ldyBzcXNNb2QuTGlzdE1lc3NhZ2VNb3ZlVGFza3NDb21tYW5kKHsgU291cmNlQXJuOiBkbHFBcm4gfSkpIGFzIHtcbiAgICBSZXN1bHRzPzogQXJyYXk8eyBTdGF0dXM/OiBzdHJpbmcgfT47XG4gIH07XG4gIHJldHVybiAob3V0LlJlc3VsdHMgPz8gW10pLnNvbWUodCA9PiB0LlN0YXR1cyA9PT0gJ1JVTk5JTkcnKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3RhcnRNb3ZlVGFzayhyZWdpb246IHN0cmluZywgZGxxQXJuOiBzdHJpbmcsIG1haW5Bcm46IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBpZiAoIXNxc01vZCkgc3FzTW9kID0gYXdhaXQgbG9hZFNkazxTcXNNb2R1bGU+KCdAYXdzLXNkay9jbGllbnQtc3FzJyk7XG4gIGNvbnN0IGNsaWVudCA9IGF3YWl0IHNxc0NsaWVudChyZWdpb24pO1xuICBhd2FpdCBjbGllbnQuc2VuZChuZXcgc3FzTW9kLlN0YXJ0TWVzc2FnZU1vdmVUYXNrQ29tbWFuZCh7IFNvdXJjZUFybjogZGxxQXJuLCBEZXN0aW5hdGlvbkFybjogbWFpbkFybiB9KSk7XG59XG5cbi8qKiBQT1NUIG9uZSBvcmcncyBpbmdlc3QtaGVhbHRoIHNpZ25hbC4gUmV0dXJucyB0cnVlIG9ubHkgb24gYSBjb25maXJtZWQgMnh4IHNvIHRoZVxuICogIGNhbGxlciByZXNldHMgdGhhdCBvcmcncyBgZm9yd2FyZGVkYCBjb3VudGVyIE9OTFkgd2hlbiB0aGUgc2lnbmFsIHdhcyBhY2NlcHRlZFxuICogIChhIGRyb3BwZWQvZmFpbGVkIFBPU1QgbXVzdCBub3Qgc2lsZW50bHkgemVybyB0aGUgYWNjdW11bGF0ZWQgY291bnQpLiAqL1xuYXN5bmMgZnVuY3Rpb24gcG9zdEluZ2VzdEhlYWx0aChiYXNlVXJsOiBzdHJpbmcsIGJvZHk6IHsgb3JnSWQ/OiBzdHJpbmc7IGZvcndhcmRlZDogbnVtYmVyOyBkcm9wcGVkOiBudW1iZXI7IGxhc3RFdmVudEF0Pzogc3RyaW5nIH0pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCB0b2tlbiA9IGF3YWl0IGdldEF1dGhUb2tlbigpO1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGAke2Jhc2VVcmx9L2FwaS9yZXBvcnRzL2luZ2VzdC1oZWFsdGhgLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywgJ0F1dGhvcml6YXRpb24nOiBgQmVhcmVyICR7dG9rZW59YCB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoYm9keSksXG4gICAgfSk7XG4gICAgaWYgKCFyZXMub2spIHtcbiAgICAgIGxvZy53YXJuKCdpbmdlc3QtaGVhbHRoIFBPU1QgcmV0dXJuZWQgbm9uLTJ4eCAoaWdub3JlZCknLCB7IHN0YXR1czogcmVzLnN0YXR1cywgb3JnSWQ6IGJvZHkub3JnSWQgfSk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2cud2FybignaW5nZXN0LWhlYWx0aCBQT1NUIGZhaWxlZCAoaWdub3JlZCknLCB7IGVycm9yOiBTdHJpbmcoZXJyKSwgb3JnSWQ6IGJvZHkub3JnSWQgfSk7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogVGhyb3R0bGVkLCBiZXN0LWVmZm9ydCBwb3N0LXN1Y2Nlc3Mgd29yazogZW1pdCBwZXItb3JnIGluZ2VzdC1oZWFsdGggYW5kIHNlbGYtaGVhbFxuICogdGhlIERMUS4gTmV2ZXIgdGhyb3dzLiBgZm9yd2FyZGVkYC9gbGFzdEV2ZW50QXRgIGFjY3VtdWxhdGUgUEVSIE9SRyBhY3Jvc3MgYmF0Y2hlc1xuICogYmV0d2VlbiBoZWFsdGggcG9zdHM7IGBkcm9wcGVkYCBpcyB0aGUgc2hhcmVkIGZsZWV0IERMUSBkZXB0aCBzbmFwc2hvdC4gQSBwZXItb3JnXG4gKiBjb3VudGVyIGlzIHplcm9lZCBvbmx5IGFmdGVyIHRoYXQgb3JnJ3MgaGVhbHRoIFBPU1QgaXMgY29uZmlybWVkIDJ4eC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVwb3J0SGVhbHRoQW5kUmVkcml2ZShiYXNlVXJsOiBzdHJpbmcsIG1haW5RdWV1ZUFybjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gIGlmIChub3cgLSBsYXN0SGVhbHRoQXQgPCBIRUFMVEhfVEhST1RUTEVfTVMpIHJldHVybjtcbiAgbGFzdEhlYWx0aEF0ID0gbm93O1xuICB0cnkge1xuICAgIGNvbnN0IHEgPSBkZXJpdmVRdWV1ZXMobWFpblF1ZXVlQXJuKTtcbiAgICBsZXQgZGVwdGggPSAwO1xuICAgIGxldCBvbGRlc3RBZ2VTZWMgPSAwO1xuICAgIGlmIChxKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBzdGF0cyA9IGF3YWl0IGRscVN0YXRzKHEucmVnaW9uLCBxLmRscVVybCk7XG4gICAgICAgIGRlcHRoID0gc3RhdHMuZGVwdGg7XG4gICAgICAgIG9sZGVzdEFnZVNlYyA9IHN0YXRzLm9sZGVzdEFnZVNlYztcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsb2cud2FybignRExRIHN0YXRzIGNoZWNrIGZhaWxlZCAoaWdub3JlZCknLCB7IGVycm9yOiAoZXJyIGFzIHsgbmFtZT86IHN0cmluZyB9KT8ubmFtZSA/PyBTdHJpbmcoZXJyKSB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBQb3N0IG9uZSBoZWFsdGggc2lnbmFsIHBlciByZXNvbHZlZCBvcmcsIGF0dHJpYnV0aW5nIGl0cyBvd24gZm9yd2FyZGVkIGNvdW50ICtcbiAgICAvLyBsYXN0IGV2ZW50IHRpbWUuIGBkcm9wcGVkYCAodGhlIHNoYXJlZCBmbGVldCBETFEgZGVwdGgpIHJpZGVzIGFsb25nIG9uIGVhY2guXG4gICAgLy8gU25hcHNob3QgdGhlIGtleXMgZmlyc3Qgc28gd2UgZG9uJ3QgbXV0YXRlIHRoZSBtYXAgd2hpbGUgaXRlcmF0aW5nLlxuICAgIGNvbnN0IG9yZ0tleXMgPSBuZXcgU2V0KFsuLi5mb3J3YXJkZWRCeU9yZy5rZXlzKCksIC4uLmxhc3RFdmVudEF0QnlPcmcua2V5cygpXSk7XG4gICAgaWYgKG9yZ0tleXMuc2l6ZSA9PT0gMCkgb3JnS2V5cy5hZGQoJycpOyAvLyBub3RoaW5nIGZvcndhcmRlZCB5ZXQg4oaSIHN0aWxsIHJlcG9ydCBkZXB0aFxuICAgIGZvciAoY29uc3Qga2V5IG9mIG9yZ0tleXMpIHtcbiAgICAgIGNvbnN0IGZvcndhcmRlZCA9IGZvcndhcmRlZEJ5T3JnLmdldChrZXkpID8/IDA7XG4gICAgICBjb25zdCBsYXN0RXZlbnRBdCA9IGxhc3RFdmVudEF0QnlPcmcuZ2V0KGtleSk7XG4gICAgICBjb25zdCBvayA9IGF3YWl0IHBvc3RJbmdlc3RIZWFsdGgoYmFzZVVybCwge1xuICAgICAgICAuLi4oa2V5ID8geyBvcmdJZDoga2V5IH0gOiB7fSksXG4gICAgICAgIGZvcndhcmRlZCxcbiAgICAgICAgZHJvcHBlZDogZGVwdGgsXG4gICAgICAgIGxhc3RFdmVudEF0LFxuICAgICAgfSk7XG4gICAgICAvLyBSZXNldCB0aGUgZm9yd2FyZGVkIGNvdW50ZXIgT05MWSBvbiBhIGNvbmZpcm1lZCAyeHgg4oCUIGEgZmFpbGVkL2Ryb3BwZWQgUE9TVFxuICAgICAgLy8ga2VlcHMgdGhlIGNvdW50IHNvIHRoZSBuZXh0IHRpY2sgcmUtcmVwb3J0cyBpdCBpbnN0ZWFkIG9mIGxvc2luZyBpdC5cbiAgICAgIGlmIChvaykgZm9yd2FyZGVkQnlPcmcuc2V0KGtleSwgMCk7XG4gICAgfVxuXG4gICAgaWYgKHEgJiYgZGVwdGggPiAwICYmIG5vdyAtIGxhc3RSZWRyaXZlQXQgPj0gUkVEUklWRV9USFJPVFRMRV9NUykge1xuICAgICAgLy8gUG9pc29uLW1lc3NhZ2UgY2VpbGluZzogbmV2ZXIgcmVkcml2ZSBhIERMUSB3aG9zZSBvbGRlc3QgbWVzc2FnZSBoYXMgYWdlZFxuICAgICAgLy8gcGFzdCB0aGUgY2VpbGluZyDigJQgdGhhdCBtZXNzYWdlIGtlZXBzIGZhaWxpbmcgYW5kIHdvdWxkIGxvb3AgRExR4oaSbWFpbuKGkkRMUS5cbiAgICAgIGlmIChvbGRlc3RBZ2VTZWMgPiBNQVhfRExRX1JFRFJJVkVfQUdFX1NFQykge1xuICAgICAgICBsb2cud2FybignRExRIG9sZGVzdCBtZXNzYWdlIGV4Y2VlZHMgcG9pc29uLWFnZSBjZWlsaW5nIOKAlCBza2lwcGluZyByZWRyaXZlIChuZWVkcyBtYW51YWwgYXR0ZW50aW9uKScsIHtcbiAgICAgICAgICBkbHFBcm46IHEuZGxxQXJuLCBkcm9wcGVkOiBkZXB0aCwgb2xkZXN0QWdlU2VjLCBjZWlsaW5nU2VjOiBNQVhfRExRX1JFRFJJVkVfQUdFX1NFQyxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmICghKGF3YWl0IGhhc0FjdGl2ZU1vdmVUYXNrKHEucmVnaW9uLCBxLmRscUFybikpKSB7XG4gICAgICAgICAgICBsYXN0UmVkcml2ZUF0ID0gbm93O1xuICAgICAgICAgICAgYXdhaXQgc3RhcnRNb3ZlVGFzayhxLnJlZ2lvbiwgcS5kbHFBcm4sIHEubWFpbkFybik7XG4gICAgICAgICAgICBsb2cuaW5mbygnU3RhcnRlZCBzZWxmLWhlYWxpbmcgRExR4oaSbWFpbiByZWRyaXZlJywgeyBkbHFBcm46IHEuZGxxQXJuLCBkcm9wcGVkOiBkZXB0aCwgb2xkZXN0QWdlU2VjIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbG9nLndhcm4oJ0RMUSByZWRyaXZlIGZhaWxlZCAoaWdub3JlZCknLCB7IGVycm9yOiAoZXJyIGFzIHsgbmFtZT86IHN0cmluZyB9KT8ubmFtZSA/PyBTdHJpbmcoZXJyKSB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nLndhcm4oJ2hlYWx0aC9yZWRyaXZlIHN0ZXAgZmFpbGVkIChpZ25vcmVkKScsIHsgZXJyb3I6IFN0cmluZyhlcnIpIH0pO1xuICB9XG59XG5cbi8vIOKUgOKUgOKUgCBIYW5kbGVyIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5leHBvcnQgY29uc3QgaGFuZGxlciA9IGFzeW5jIChldmVudDogU1FTRXZlbnQpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgY29uc3QgYmFzZVVybCA9IHByb2Nlc3MuZW52LlBMQVRGT1JNX0JBU0VfVVJMO1xuICBpZiAoIWJhc2VVcmwpIHRocm93IG5ldyBFcnJvcignUExBVEZPUk1fQkFTRV9VUkwgZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgcmVxdWlyZWQnKTtcblxuICAvLyBQYXJzZSArIHJlc29sdmUgYWxsIHJlY29yZHMgKHRhZyArIGNvbW1pdCBsb29rdXBzIHJ1biBjb25jdXJyZW50bHkpOyBkcm9wXG4gIC8vIGludGVudGlvbmFsIHNraXBzIChudWxsKS4gQSBtYWxmb3JtZWQgcmVjb3JkIEJPRFkgaXMgaGFuZGxlZCBpbnNpZGUgcGFyc2VSZWNvcmRcbiAgLy8gKGxvZ2dlZCArIHNraXBwZWQpIHNvIG9uZSBiYWQgbWVzc2FnZSBjYW4ndCBmYWlsIHRoZSB3aG9sZSBiYXRjaCDigJQgd2hpbGUgZ2VudWluZVxuICAvLyBpbmZyYSBlcnJvcnMgKGUuZy4gQWNjZXNzRGVuaWVkIG9uIHRoZSB0YWcgbG9va3VwKSBzdGlsbCBwcm9wYWdhdGUgc28gYSBtaXNzaW5nXG4gIC8vIElBTSBncmFudCBzdXJmYWNlcyBhbmQgU1FTIHJldHJpZXMgdGhlIGJhdGNoLlxuICBjb25zdCBldmVudHMgPSAoYXdhaXQgUHJvbWlzZS5hbGwoZXZlbnQuUmVjb3Jkcy5tYXAocGFyc2VSZWNvcmQpKSlcbiAgICAuZmlsdGVyKChlKTogZSBpcyBQYXJzZWRFdmVudCA9PiBlICE9PSBudWxsKTtcbiAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApIHtcbiAgICBsb2cuaW5mbygnTm8gcmVzb2x2YWJsZSBDb2RlUGlwZWxpbmUgZXZlbnRzIGluIGJhdGNoJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gUE9TVCBiYXRjaCB0byByZXBvcnRpbmcgc2VydmljZS4gT24gYSA0MDEvNDAzIHRoZSBjYWNoZWQgSldUIGhhcyBsaWtlbHlcbiAgLy8gZXhwaXJlZCDigJQgZHJvcCBpdCwgcmUtZmV0Y2gsIGFuZCByZXRyeSBPTkNFIHNvIGFuIGV4cGlyZWQgdG9rZW4gZG9lc24ndFxuICAvLyBwZXJtYW5lbnRseSBicmljayBhIHdhcm0gY29udGFpbmVyLiBgb3JnSWRgIGlzIElOVEVSTkFMICh0b2tlbi9oZWFsdGgga2V5aW5nKSDigJRcbiAgLy8gc3RyaXAgaXQgaGVyZSBzbyBpdCBuZXZlciByaWRlcyB0aGUgd2lyZTsgdGhlIGluZ2VzdCByZXNvbHZlcyBvcmcgZnJvbSB0aGVcbiAgLy8gcGlwZWxpbmUgcmVnaXN0cnksIG5vdCB0aGUgYm9keS5cbiAgY29uc3QgcGF5bG9hZEV2ZW50cyA9IGV2ZW50cy5tYXAoKHsgb3JnSWQ6IF9vcmdJZCwgLi4ucmVzdCB9KSA9PiByZXN0KTtcbiAgY29uc3QgcG9zdCA9ICh0b2tlbjogc3RyaW5nKSA9PiBmZXRjaChgJHtiYXNlVXJsfS9hcGkvcmVwb3J0cy9ldmVudHNgLCB7XG4gICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLCAnQXV0aG9yaXphdGlvbic6IGBCZWFyZXIgJHt0b2tlbn1gIH0sXG4gICAgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBldmVudHM6IHBheWxvYWRFdmVudHMgfSksXG4gIH0pO1xuICBsZXQgcmVzID0gYXdhaXQgcG9zdChhd2FpdCBnZXRBdXRoVG9rZW4oKSk7XG4gIGlmIChyZXMuc3RhdHVzID09PSA0MDEgfHwgcmVzLnN0YXR1cyA9PT0gNDAzKSB7XG4gICAgbG9nLndhcm4oJ1JlcG9ydGluZyBBUEkgYXV0aCBmYWlsZWQ7IHJlZnJlc2hpbmcgdG9rZW4gYW5kIHJldHJ5aW5nIG9uY2UnLCB7IHN0YXR1czogcmVzLnN0YXR1cyB9KTtcbiAgICBjYWNoZWRUb2tlbiA9IG51bGw7XG4gICAgcmVzID0gYXdhaXQgcG9zdChhd2FpdCBnZXRBdXRoVG9rZW4oKSk7XG4gIH1cblxuICBpZiAoIXJlcy5vaykge1xuICAgIGNvbnN0IGJvZHkgPSBhd2FpdCByZXMudGV4dCgpLmNhdGNoKCgpID0+ICcnKTtcbiAgICBsb2cuZXJyb3IoYFJlcG9ydGluZyBBUEkgcmV0dXJuZWQgJHtyZXMuc3RhdHVzfWAsIHsgYm9keSB9KTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFJlcG9ydGluZyBBUEkgZmFpbGVkOiAke3Jlcy5zdGF0dXN9YCk7XG4gIH1cblxuICAvLyBUaGUgaW5zZXJ0IGFscmVhZHkgc3VjY2VlZGVkICgyeHgpLiBUaGUgcGFyc2VkIGJvZHkgaXMgb25seSB1c2VkIGZvciBhIGxvZ1xuICAvLyBsaW5lLCBzbyBhIG5vbi1KU09OL2VtcHR5IGJvZHkgbXVzdCBub3QgdGhyb3cgYW5kIGZhaWwgdGhlIHdob2xlIGJhdGNoIOKAlFxuICAvLyB0aGF0IHdvdWxkIHRyaWdnZXIgYW4gU1FTIHJlZGVsaXZlcnkgYW5kIHJlLVBPU1QgKGR1cGxpY2F0ZSkgYWxyZWFkeS1pbnNlcnRlZFxuICAvLyBldmVudHMuIFRoZSBpbmdlc3QncyBwYXJ0aWFsLXVuaXF1ZSBpbmRleCBkZWR1cGVzLCBidXQgYXZvaWQgdGhlIGNodXJuIGFueXdheS5cbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcmVzLmpzb24oKS5jYXRjaCgoKSA9PiAoe30pKSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgbG9nLmluZm8oYEluZ2VzdGVkICR7ZXZlbnRzLmxlbmd0aH0gZXZlbnRzYCwgeyBpbnNlcnRlZDogcmVzdWx0LmRhdGEgfSk7XG5cbiAgLy8gQWNjdW11bGF0ZSBkZWxpdmVyeS1oZWFsdGggY291bnRlcnMgUEVSIFJFU09MVkVEIE9SRywgdGhlbiBydW4gdGhlIHRocm90dGxlZFxuICAvLyBwb3N0LXN1Y2Nlc3MgaGVhbHRoICsgc2VsZi1oZWFsaW5nIERMUSByZWRyaXZlIChiZXN0LWVmZm9ydCwgbmV2ZXIgZmFpbHMgYmF0Y2gpLlxuICBmb3IgKGNvbnN0IGUgb2YgZXZlbnRzKSB7XG4gICAgY29uc3Qga2V5ID0gZS5vcmdJZCA/PyAnJztcbiAgICBmb3J3YXJkZWRCeU9yZy5zZXQoa2V5LCAoZm9yd2FyZGVkQnlPcmcuZ2V0KGtleSkgPz8gMCkgKyAxKTtcbiAgICBjb25zdCBhdCA9IGUuY29tcGxldGVkQXQgfHwgZS5zdGFydGVkQXQ7XG4gICAgaWYgKGF0KSB7XG4gICAgICBjb25zdCBwcmV2ID0gbGFzdEV2ZW50QXRCeU9yZy5nZXQoa2V5KTtcbiAgICAgIGlmICghcHJldiB8fCBhdCA+IHByZXYpIGxhc3RFdmVudEF0QnlPcmcuc2V0KGtleSwgYXQpO1xuICAgIH1cbiAgfVxuICBhd2FpdCByZXBvcnRIZWFsdGhBbmRSZWRyaXZlKGJhc2VVcmwsIGV2ZW50LlJlY29yZHNbMF0/LmV2ZW50U291cmNlQVJOKTtcbn07XG5cbi8qKlxuICogQGludGVybmFsIFRlc3Qtb25seTogcmVzZXQgYWxsIG1vZHVsZS1sZXZlbCBjYWNoZXMvdGhyb3R0bGUgc3RhdGUgc28gdGVzdHMgYXJlXG4gKiBpc29sYXRlZCBmcm9tIG9uZSBhbm90aGVyLiBTdHJpcHBlZCBmcm9tIHRoZSBwdWJsaXNoZWQgLmQudHMgKHN0cmlwSW50ZXJuYWwpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gX3Jlc2V0Rm9yVGVzdHMoKTogdm9pZCB7XG4gIHJlc29sdmVkQnlBcm4uY2xlYXIoKTtcbiAgY29tbWl0UmVzdWx0Q2FjaGUuY2xlYXIoKTtcbiAgbGFzdFNoYUJ5UGlwZWxpbmUuY2xlYXIoKTtcbiAgY29tbWl0TWV0YUNhY2hlLmNsZWFyKCk7XG4gIHNjbUNvb2xkb3duVW50aWwgPSAwO1xuICBnaXRodWJUb2tlbkJ5T3JnLmNsZWFyKCk7XG4gIGZvcndhcmRlZEJ5T3JnLmNsZWFyKCk7XG4gIGxhc3RFdmVudEF0QnlPcmcuY2xlYXIoKTtcbiAgbGFzdEhlYWx0aEF0ID0gMDtcbiAgbGFzdFJlZHJpdmVBdCA9IDA7XG4gIGNhY2hlZFRva2VuID0gbnVsbDtcbn1cbiJdfQ==
|