@pipeline-builder/pipeline-events 4.0.10 → 4.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/auth.d.ts +14 -0
- package/lib/auth.js +99 -0
- package/lib/health.d.ts +13 -0
- package/lib/health.js +191 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.js +678 -930
- package/lib/parse.d.ts +40 -0
- package/lib/parse.js +150 -0
- package/lib/scm.d.ts +21 -0
- package/lib/scm.js +286 -0
- package/lib/tags.d.ts +18 -0
- package/lib/tags.js +125 -0
- package/lib/testing/exports.json +8 -0
- package/lib/util.d.ts +21 -0
- package/lib/util.js +43 -0
- package/package.json +16 -6
package/lib/index.js
CHANGED
|
@@ -1,968 +1,716 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
// ../pipeline-core/lib/handlers/platform-credential.js
|
|
2
|
+
import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
|
|
3
|
+
var ACCESS_KEY_PREFIXES = ["pb_sa_", "pb_pat_"];
|
|
4
|
+
function assertAccessKey(value, source) {
|
|
5
|
+
if (ACCESS_KEY_PREFIXES.some((p) => value.startsWith(p)))
|
|
6
|
+
return;
|
|
7
|
+
const looksLikeJwt = value.split(".").length === 3;
|
|
8
|
+
throw new Error(`${source} does not hold an opaque access key (expected a "pb_sa_\u2026" value)` + (looksLikeJwt ? ' \u2014 it holds a JWT, not a service-account key. Re-run "pipeline-manager infra store-token" to issue one.' : "."));
|
|
9
|
+
}
|
|
10
|
+
async function readCredentialSecret(secretName, client = new SecretsManagerClient({})) {
|
|
11
|
+
const response = await client.send(new GetSecretValueCommand({ SecretId: secretName }));
|
|
12
|
+
if (!response.SecretString)
|
|
13
|
+
throw new Error(`Secret "${secretName}" is empty`);
|
|
14
|
+
return JSON.parse(response.SecretString);
|
|
15
|
+
}
|
|
16
|
+
async function readAccessKeyFromSecret(secretName) {
|
|
17
|
+
const secret = await readCredentialSecret(secretName);
|
|
18
|
+
const key = typeof secret.password === "string" ? secret.password : "";
|
|
19
|
+
if (!key) {
|
|
20
|
+
throw new Error(`Secret "${secretName}" missing password \u2014 run "pipeline-manager infra store-token" to provision a service-account key`);
|
|
21
|
+
}
|
|
22
|
+
assertAccessKey(key, `secret "${secretName}"`);
|
|
23
|
+
return key;
|
|
24
|
+
}
|
|
25
|
+
function createPlatformCredential(opts) {
|
|
26
|
+
let cached = null;
|
|
27
|
+
let fromEnv = false;
|
|
28
|
+
return {
|
|
29
|
+
async getKey() {
|
|
30
|
+
if (cached)
|
|
31
|
+
return cached;
|
|
32
|
+
const direct = opts.envKeyVar ? process.env[opts.envKeyVar] : void 0;
|
|
33
|
+
if (direct) {
|
|
34
|
+
assertAccessKey(direct, opts.envKeyVar);
|
|
35
|
+
cached = direct;
|
|
36
|
+
fromEnv = true;
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
const secretName = typeof opts.secretName === "function" ? opts.secretName() : opts.secretName;
|
|
40
|
+
if (!secretName) {
|
|
41
|
+
throw new Error(`${opts.envKeyVar ? `${opts.envKeyVar} or ` : ""}PLATFORM_SECRET_NAME environment variable is required`);
|
|
42
|
+
}
|
|
43
|
+
cached = await readAccessKeyFromSecret(secretName);
|
|
44
|
+
fromEnv = false;
|
|
45
|
+
return cached;
|
|
46
|
+
},
|
|
47
|
+
invalidate() {
|
|
48
|
+
if (!fromEnv)
|
|
49
|
+
cached = null;
|
|
50
|
+
},
|
|
51
|
+
reset() {
|
|
52
|
+
cached = null;
|
|
53
|
+
fromEnv = false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/util.ts
|
|
59
|
+
var log = {
|
|
60
|
+
info: (msg, data) => console.log(JSON.stringify({ level: "INFO", message: msg, data, ts: (/* @__PURE__ */ new Date()).toISOString() })),
|
|
61
|
+
warn: (msg, data) => console.log(JSON.stringify({ level: "WARN", message: msg, data, ts: (/* @__PURE__ */ new Date()).toISOString() })),
|
|
62
|
+
error: (msg, data) => console.error(JSON.stringify({ level: "ERROR", message: msg, data, ts: (/* @__PURE__ */ new Date()).toISOString() }))
|
|
63
63
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
64
|
+
var BoundedMap = class extends Map {
|
|
65
|
+
constructor(max) {
|
|
66
|
+
super();
|
|
67
|
+
this.max = max;
|
|
68
|
+
}
|
|
69
|
+
max;
|
|
70
|
+
set(key, value) {
|
|
71
|
+
if (super.has(key)) {
|
|
72
|
+
super.delete(key);
|
|
73
|
+
} else if (this.size >= this.max) {
|
|
74
|
+
const oldest = this.keys().next().value;
|
|
75
|
+
if (oldest !== void 0) super.delete(oldest);
|
|
76
|
+
}
|
|
77
|
+
return super.set(key, value);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var CACHE_MAX_ENTRIES = 5e3;
|
|
81
|
+
async function loadSdk(pkg) {
|
|
82
|
+
return await import(pkg);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/auth.ts
|
|
86
|
+
var TOKEN_REFRESH_FRACTION = 0.8;
|
|
87
|
+
var MIN_TOKEN_CACHE_MS = 3e4;
|
|
88
|
+
var EXCHANGE_TIMEOUT_MS = 5e3;
|
|
89
|
+
var credential = createPlatformCredential({
|
|
90
|
+
secretName: () => process.env.PLATFORM_SECRET_NAME,
|
|
91
|
+
envKeyVar: "PLATFORM_ACCESS_KEY"
|
|
92
|
+
});
|
|
93
|
+
var cachedToken = null;
|
|
94
|
+
var cachedTokenExpiresAt = 0;
|
|
95
|
+
async function getAuthToken() {
|
|
96
|
+
if (cachedToken && Date.now() < cachedTokenExpiresAt) return cachedToken;
|
|
97
|
+
const baseUrl = (process.env.PLATFORM_BASE_URL || "").replace(/\/+$/, "");
|
|
98
|
+
if (!baseUrl) throw new Error("PLATFORM_BASE_URL environment variable is required");
|
|
99
|
+
const key = await credential.getKey();
|
|
100
|
+
const controller = new AbortController();
|
|
101
|
+
const timer = setTimeout(() => controller.abort(), EXCHANGE_TIMEOUT_MS);
|
|
102
|
+
let res;
|
|
103
|
+
try {
|
|
104
|
+
res = await fetch(`${baseUrl}/api/auth/token/exchange`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { "Content-Type": "application/json", "Accept": "application/json" },
|
|
107
|
+
body: JSON.stringify({ key }),
|
|
108
|
+
signal: controller.signal
|
|
109
|
+
});
|
|
110
|
+
} finally {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
if (!res.ok) {
|
|
114
|
+
if (res.status === 401 || res.status === 403) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Access-key exchange refused (${res.status}) \u2014 the stored key is unknown, revoked, expired, its service account is disabled, or this address is outside the key\u2019s IP allowlist`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`Access-key exchange failed: ${res.status}`);
|
|
120
|
+
}
|
|
121
|
+
const body = await res.json().catch(() => ({}));
|
|
122
|
+
const accessToken = body.data?.accessToken;
|
|
123
|
+
if (!accessToken) throw new Error("Access-key exchange returned no access token");
|
|
124
|
+
const ttlMs = (typeof body.data?.expiresIn === "number" && body.data.expiresIn > 0 ? body.data.expiresIn : 300) * 1e3;
|
|
125
|
+
cachedToken = accessToken;
|
|
126
|
+
cachedTokenExpiresAt = Date.now() + Math.max(MIN_TOKEN_CACHE_MS, ttlMs * TOKEN_REFRESH_FRACTION);
|
|
127
|
+
log.info("Exchanged the stored service-account key for a platform token");
|
|
128
|
+
return accessToken;
|
|
112
129
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
// Transient/infra errors (throttling, timeouts, 5xx) MUST propagate so the SQS
|
|
155
|
-
// batch is retried rather than silently dropping the event — an event dropped
|
|
156
|
-
// BEFORE it reaches the ingest is lost permanently (the ingest's partial-unique
|
|
157
|
-
// index only dedupes events that were actually POSTed). Only swallow when the
|
|
158
|
-
// failure is a definitive answer that this ARN has no usable tag.
|
|
159
|
-
const e = err;
|
|
160
|
-
const status = e.$metadata?.httpStatusCode;
|
|
161
|
-
const retryable = e.$retryable != null
|
|
162
|
-
|| (typeof status === 'number' && status >= 500)
|
|
163
|
-
|| /throttl|timeout|toomanyrequests|serviceunavailable|econnreset|networkingerror/i.test(name ?? '');
|
|
164
|
-
if (retryable) {
|
|
165
|
-
log.error('Transient error resolving pb.pipeline-id tag — will retry the batch', { arn, error: name ?? String(err) });
|
|
166
|
-
throw err;
|
|
167
|
-
}
|
|
168
|
-
log.error('Failed to resolve pb.pipeline-id tag', { arn, error: name ?? String(err) });
|
|
169
|
-
return { pipelineId: null, orgId: null, deploys: new Map() };
|
|
170
|
-
}
|
|
130
|
+
function invalidateCredential() {
|
|
131
|
+
cachedToken = null;
|
|
132
|
+
cachedTokenExpiresAt = 0;
|
|
133
|
+
credential.invalidate();
|
|
134
|
+
}
|
|
135
|
+
function _resetAuthForTests() {
|
|
136
|
+
cachedToken = null;
|
|
137
|
+
cachedTokenExpiresAt = 0;
|
|
138
|
+
credential.reset();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/health.ts
|
|
142
|
+
var sqsMod;
|
|
143
|
+
var sqsClientsByRegion;
|
|
144
|
+
async function sqsClient(region) {
|
|
145
|
+
if (!sqsMod) sqsMod = await loadSdk("@aws-sdk/client-sqs");
|
|
146
|
+
if (!sqsClientsByRegion) sqsClientsByRegion = /* @__PURE__ */ new Map();
|
|
147
|
+
let client = sqsClientsByRegion.get(region);
|
|
148
|
+
if (!client) {
|
|
149
|
+
client = new sqsMod.SQSClient({ region });
|
|
150
|
+
sqsClientsByRegion.set(region, client);
|
|
151
|
+
}
|
|
152
|
+
return client;
|
|
153
|
+
}
|
|
154
|
+
var forwardedByOrg = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
155
|
+
var lastEventAtByOrg = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
156
|
+
var lastHealthAt = 0;
|
|
157
|
+
var lastRedriveAt = 0;
|
|
158
|
+
var HEALTH_THROTTLE_MS = 60 * 1e3;
|
|
159
|
+
var REDRIVE_THROTTLE_MS = 5 * 60 * 1e3;
|
|
160
|
+
var MAX_DLQ_REDRIVE_AGE_SEC = 6 * 60 * 60;
|
|
161
|
+
function deriveQueues(mainQueueArn) {
|
|
162
|
+
if (!mainQueueArn) return void 0;
|
|
163
|
+
const parts = mainQueueArn.split(":");
|
|
164
|
+
if (parts.length < 6 || parts[2] !== "sqs") return void 0;
|
|
165
|
+
const [, , , region, account, name] = parts;
|
|
166
|
+
const dlqArn = process.env.EVENT_DLQ_ARN || `arn:aws:sqs:${region}:${account}:${name}-dlq`;
|
|
167
|
+
const dlqName = dlqArn.split(":").pop() ?? `${name}-dlq`;
|
|
168
|
+
const dlqUrl = `https://sqs.${region}.amazonaws.com/${account}/${dlqName}`;
|
|
169
|
+
return { region, mainArn: mainQueueArn, dlqArn, dlqUrl };
|
|
171
170
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
async function getAccessKey() {
|
|
209
|
-
if (cachedKey)
|
|
210
|
-
return cachedKey;
|
|
211
|
-
const direct = process.env.PLATFORM_ACCESS_KEY;
|
|
212
|
-
if (direct) {
|
|
213
|
-
assertAccessKey(direct, 'PLATFORM_ACCESS_KEY');
|
|
214
|
-
cachedKey = direct;
|
|
215
|
-
log.info('Using PLATFORM_ACCESS_KEY from environment');
|
|
216
|
-
return cachedKey;
|
|
217
|
-
}
|
|
218
|
-
const secretName = process.env.PLATFORM_SECRET_NAME;
|
|
219
|
-
if (!secretName) {
|
|
220
|
-
throw new Error('PLATFORM_ACCESS_KEY or PLATFORM_SECRET_NAME environment variable is required');
|
|
221
|
-
}
|
|
222
|
-
log.info(`Fetching access key from Secrets Manager: ${secretName}`);
|
|
223
|
-
const client = new SecretsManagerClient({});
|
|
224
|
-
const response = await client.send(new GetSecretValueCommand({ SecretId: secretName }));
|
|
225
|
-
if (!response.SecretString)
|
|
226
|
-
throw new Error(`Secret "${secretName}" is empty`);
|
|
227
|
-
const secret = JSON.parse(response.SecretString);
|
|
228
|
-
// `password` stays the canonical field (CodeBuild's secretsManagerCredentials
|
|
229
|
-
// and the plugin-lookup Lambda read the same one) — only its VALUE changed,
|
|
230
|
-
// from a JWT to an opaque key.
|
|
231
|
-
const key = secret.password;
|
|
232
|
-
if (!key) {
|
|
233
|
-
throw new Error(`Secret "${secretName}" missing password — run "pipeline-manager infra store-token" to provision a service-account key`);
|
|
171
|
+
async function dlqStats(region, dlqUrl) {
|
|
172
|
+
if (!sqsMod) sqsMod = await loadSdk("@aws-sdk/client-sqs");
|
|
173
|
+
const client = await sqsClient(region);
|
|
174
|
+
const out = await client.send(new sqsMod.GetQueueAttributesCommand({
|
|
175
|
+
QueueUrl: dlqUrl,
|
|
176
|
+
AttributeNames: ["ApproximateNumberOfMessages", "ApproximateAgeOfOldestMessage"]
|
|
177
|
+
}));
|
|
178
|
+
const n = Number(out.Attributes?.ApproximateNumberOfMessages ?? "0");
|
|
179
|
+
const age = Number(out.Attributes?.ApproximateAgeOfOldestMessage ?? "0");
|
|
180
|
+
return {
|
|
181
|
+
depth: Number.isFinite(n) ? n : 0,
|
|
182
|
+
oldestAgeSec: Number.isFinite(age) ? age : 0
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async function hasActiveMoveTask(region, dlqArn) {
|
|
186
|
+
if (!sqsMod) sqsMod = await loadSdk("@aws-sdk/client-sqs");
|
|
187
|
+
const client = await sqsClient(region);
|
|
188
|
+
const out = await client.send(new sqsMod.ListMessageMoveTasksCommand({ SourceArn: dlqArn }));
|
|
189
|
+
return (out.Results ?? []).some((t) => t.Status === "RUNNING");
|
|
190
|
+
}
|
|
191
|
+
async function startMoveTask(region, dlqArn, mainArn) {
|
|
192
|
+
if (!sqsMod) sqsMod = await loadSdk("@aws-sdk/client-sqs");
|
|
193
|
+
const client = await sqsClient(region);
|
|
194
|
+
await client.send(new sqsMod.StartMessageMoveTaskCommand({ SourceArn: dlqArn, DestinationArn: mainArn }));
|
|
195
|
+
}
|
|
196
|
+
async function postIngestHealth(baseUrl, body) {
|
|
197
|
+
try {
|
|
198
|
+
const token = await getAuthToken();
|
|
199
|
+
const res = await fetch(`${baseUrl}/api/reports/ingest-health`, {
|
|
200
|
+
method: "POST",
|
|
201
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
|
|
202
|
+
body: JSON.stringify(body)
|
|
203
|
+
});
|
|
204
|
+
if (!res.ok) {
|
|
205
|
+
log.warn("ingest-health POST returned non-2xx (ignored)", { status: res.status, orgId: body.orgId });
|
|
206
|
+
return false;
|
|
234
207
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
log.
|
|
238
|
-
return
|
|
208
|
+
return true;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
log.warn("ingest-health POST failed (ignored)", { error: String(err), orgId: body.orgId });
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
239
213
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
214
|
+
async function reportHealthAndRedrive(baseUrl, mainQueueArn) {
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
if (now - lastHealthAt < HEALTH_THROTTLE_MS) return;
|
|
217
|
+
lastHealthAt = now;
|
|
218
|
+
try {
|
|
219
|
+
const q = deriveQueues(mainQueueArn);
|
|
220
|
+
let depth = 0;
|
|
221
|
+
let oldestAgeSec = 0;
|
|
222
|
+
if (q) {
|
|
223
|
+
try {
|
|
224
|
+
const stats = await dlqStats(q.region, q.dlqUrl);
|
|
225
|
+
depth = stats.depth;
|
|
226
|
+
oldestAgeSec = stats.oldestAgeSec;
|
|
227
|
+
} catch (err) {
|
|
228
|
+
log.warn("DLQ stats check failed (ignored)", { error: err?.name ?? String(err) });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const orgKeys = /* @__PURE__ */ new Set([...forwardedByOrg.keys(), ...lastEventAtByOrg.keys()]);
|
|
232
|
+
if (orgKeys.size === 0) orgKeys.add("");
|
|
233
|
+
for (const key of orgKeys) {
|
|
234
|
+
const forwarded = forwardedByOrg.get(key) ?? 0;
|
|
235
|
+
const lastEventAt = lastEventAtByOrg.get(key);
|
|
236
|
+
const ok = await postIngestHealth(baseUrl, {
|
|
237
|
+
...key ? { orgId: key } : {},
|
|
238
|
+
forwarded,
|
|
239
|
+
dropped: depth,
|
|
240
|
+
lastEventAt
|
|
241
|
+
});
|
|
242
|
+
if (ok) forwardedByOrg.set(key, 0);
|
|
243
|
+
}
|
|
244
|
+
if (q && depth > 0 && now - lastRedriveAt >= REDRIVE_THROTTLE_MS) {
|
|
245
|
+
if (oldestAgeSec > MAX_DLQ_REDRIVE_AGE_SEC) {
|
|
246
|
+
log.warn("DLQ oldest message exceeds poison-age ceiling \u2014 skipping redrive (needs manual attention)", {
|
|
247
|
+
dlqArn: q.dlqArn,
|
|
248
|
+
dropped: depth,
|
|
249
|
+
oldestAgeSec,
|
|
250
|
+
ceilingSec: MAX_DLQ_REDRIVE_AGE_SEC
|
|
271
251
|
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
throw new Error(`Access-key exchange refused (${res.status}) — the stored key is unknown, revoked, expired, `
|
|
282
|
-
+ 'its service account is disabled, or this address is outside the key’s IP allowlist');
|
|
252
|
+
} else {
|
|
253
|
+
try {
|
|
254
|
+
if (!await hasActiveMoveTask(q.region, q.dlqArn)) {
|
|
255
|
+
lastRedriveAt = now;
|
|
256
|
+
await startMoveTask(q.region, q.dlqArn, q.mainArn);
|
|
257
|
+
log.info("Started self-healing DLQ\u2192main redrive", { dlqArn: q.dlqArn, dropped: depth, oldestAgeSec });
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
log.warn("DLQ redrive failed (ignored)", { error: err?.name ?? String(err) });
|
|
283
261
|
}
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// Per-sha CodeCommit metadata cache (date + first-parent) for range walks.
|
|
319
|
-
const commitMetaCache = new Map();
|
|
320
|
-
// SCM rate-limit cooldown: after a 403/429 we stop calling the SCM for a while.
|
|
321
|
-
let scmCooldownUntil = 0;
|
|
322
|
-
const SCM_COOLDOWN_MS = 2 * 60 * 1000;
|
|
323
|
-
// Cap the CodeCommit first-parent walk so a long range can't blow the Lambda timeout.
|
|
324
|
-
const MAX_COMMIT_WALK = 50;
|
|
325
|
-
// Per-request timeout for external SCM calls (defensive against slow/hung hosts).
|
|
326
|
-
const SCM_FETCH_TIMEOUT_MS = 3000;
|
|
327
|
-
// The forwarder is a fleet Lambda: one warm container resolves events for MANY
|
|
328
|
-
// orgs, so the GitHub token MUST be keyed by the event's resolved org (not a single
|
|
329
|
-
// container-global token). Each org's token lives at `<prefix>/<orgId>/github-token`,
|
|
330
|
-
// derived from PLATFORM_SECRET_NAME (`<prefix>/<containerOrg>/platform`). null caches
|
|
331
|
-
// a definitive miss (no secret / read failed) → unauthenticated best-effort.
|
|
332
|
-
const githubTokenByOrg = new Map();
|
|
333
|
-
/**
|
|
334
|
-
* Derive a specific org's github-token secret name from PLATFORM_SECRET_NAME
|
|
335
|
-
* (`<prefix>/<containerOrg>/platform`). With an `orgId` we swap the org segment
|
|
336
|
-
* (`<prefix>/<orgId>/github-token`); without one (pipeline carried no OrgId tag) we
|
|
337
|
-
* fall back to the container's own sibling secret. Returns undefined when
|
|
338
|
-
* PLATFORM_SECRET_NAME isn't the expected `.../platform` shape.
|
|
339
|
-
*/
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch (err) {
|
|
265
|
+
log.warn("health/redrive step failed (ignored)", { error: String(err) });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function recordForwarded(events) {
|
|
269
|
+
for (const e of events) {
|
|
270
|
+
const key = e.orgId ?? "";
|
|
271
|
+
forwardedByOrg.set(key, (forwardedByOrg.get(key) ?? 0) + 1);
|
|
272
|
+
const at = e.completedAt || e.startedAt;
|
|
273
|
+
if (at) {
|
|
274
|
+
const prev = lastEventAtByOrg.get(key);
|
|
275
|
+
if (!prev || at > prev) lastEventAtByOrg.set(key, at);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function _resetHealthForTests() {
|
|
280
|
+
forwardedByOrg.clear();
|
|
281
|
+
lastEventAtByOrg.clear();
|
|
282
|
+
lastHealthAt = 0;
|
|
283
|
+
lastRedriveAt = 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/scm.ts
|
|
287
|
+
import { GetSecretValueCommand as GetSecretValueCommand2, SecretsManagerClient as SecretsManagerClient2 } from "@aws-sdk/client-secrets-manager";
|
|
288
|
+
var commitResultCache = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
289
|
+
var lastShaByPipeline = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
290
|
+
var commitMetaCache = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
291
|
+
var scmCooldownUntil = 0;
|
|
292
|
+
var SCM_COOLDOWN_MS = 2 * 60 * 1e3;
|
|
293
|
+
var MAX_COMMIT_WALK = 50;
|
|
294
|
+
var SCM_FETCH_TIMEOUT_MS = 3e3;
|
|
295
|
+
var githubTokenByOrg = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
340
296
|
function githubTokenSecretName(orgId) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return `${base}/github-token`;
|
|
297
|
+
const name = process.env.PLATFORM_SECRET_NAME;
|
|
298
|
+
if (!name || !name.endsWith("/platform")) return void 0;
|
|
299
|
+
const base = name.slice(0, -"/platform".length);
|
|
300
|
+
if (orgId) {
|
|
301
|
+
const slash = base.lastIndexOf("/");
|
|
302
|
+
const prefix = slash > 0 ? base.slice(0, slash) : base;
|
|
303
|
+
return `${prefix}/${orgId}/github-token`;
|
|
304
|
+
}
|
|
305
|
+
return `${base}/github-token`;
|
|
351
306
|
}
|
|
352
|
-
/** Read (and cache, per org) the org github token; null → unauthenticated best-effort. */
|
|
353
307
|
async function getGitHubToken(orgId) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
token = null;
|
|
374
|
-
}
|
|
375
|
-
githubTokenByOrg.set(key, token);
|
|
376
|
-
return token;
|
|
308
|
+
const key = orgId ?? "";
|
|
309
|
+
const cached = githubTokenByOrg.get(key);
|
|
310
|
+
if (cached !== void 0) return cached;
|
|
311
|
+
const secretName = githubTokenSecretName(orgId);
|
|
312
|
+
if (!secretName) {
|
|
313
|
+
githubTokenByOrg.set(key, null);
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
let token;
|
|
317
|
+
try {
|
|
318
|
+
const client = new SecretsManagerClient2({});
|
|
319
|
+
const res = await client.send(new GetSecretValueCommand2({ SecretId: secretName }));
|
|
320
|
+
token = res.SecretString && res.SecretString.length > 0 ? res.SecretString.trim() : null;
|
|
321
|
+
} catch (err) {
|
|
322
|
+
log.warn("No github-token secret \u2014 GitHub commit resolution will be unauthenticated", { orgId, error: err?.name });
|
|
323
|
+
token = null;
|
|
324
|
+
}
|
|
325
|
+
githubTokenByOrg.set(key, token);
|
|
326
|
+
return token;
|
|
377
327
|
}
|
|
378
328
|
async function fetchWithTimeout(url, opts) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
329
|
+
const controller = new AbortController();
|
|
330
|
+
const timer = setTimeout(() => controller.abort(), SCM_FETCH_TIMEOUT_MS);
|
|
331
|
+
try {
|
|
332
|
+
return await fetch(url, { ...opts, signal: controller.signal });
|
|
333
|
+
} finally {
|
|
334
|
+
clearTimeout(timer);
|
|
335
|
+
}
|
|
387
336
|
}
|
|
388
|
-
/** Normalize a commit date to ISO 8601. Accepts ISO strings and CodeCommit's
|
|
389
|
-
* "<epoch-seconds> +ZZZZ" form. Returns undefined when unparseable. */
|
|
390
337
|
function toIso(value) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
return Number.isNaN(d.getTime()) ? undefined : d.toISOString();
|
|
338
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
339
|
+
const epoch = /^(\d{9,})\b/.exec(value.trim());
|
|
340
|
+
if (epoch) {
|
|
341
|
+
const d2 = new Date(Number(epoch[1]) * 1e3);
|
|
342
|
+
return Number.isNaN(d2.getTime()) ? void 0 : d2.toISOString();
|
|
343
|
+
}
|
|
344
|
+
const d = new Date(value);
|
|
345
|
+
return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
|
|
400
346
|
}
|
|
401
|
-
/** Classify a source-revision URL into a provider + repo coordinates. */
|
|
402
347
|
function classifySource(url) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
// not a URL → unknown
|
|
426
|
-
}
|
|
427
|
-
return { provider: 'unknown' };
|
|
428
|
-
}
|
|
429
|
-
let codecommitClientsByRegion;
|
|
430
|
-
let codecommitMod;
|
|
348
|
+
if (!url) return { provider: "unknown" };
|
|
349
|
+
try {
|
|
350
|
+
const u = new URL(url);
|
|
351
|
+
const host = u.hostname.toLowerCase();
|
|
352
|
+
if (host === "github.com" || host.endsWith(".github.com")) {
|
|
353
|
+
const parts = u.pathname.split("/").filter(Boolean);
|
|
354
|
+
if (parts.length >= 2) return { provider: "github", owner: parts[0], repo: parts[1].replace(/\.git$/, "") };
|
|
355
|
+
}
|
|
356
|
+
if (host === "bitbucket.org") {
|
|
357
|
+
const parts = u.pathname.split("/").filter(Boolean);
|
|
358
|
+
if (parts.length >= 2) return { provider: "bitbucket", owner: parts[0], repo: parts[1].replace(/\.git$/, "") };
|
|
359
|
+
}
|
|
360
|
+
if (u.pathname.toLowerCase().includes("codecommit") || u.hash.toLowerCase().includes("codecommit") || u.pathname.includes("/repositories/")) {
|
|
361
|
+
const m = /repositor(?:y|ies)\/([^/#?]+)/.exec(u.pathname + u.hash);
|
|
362
|
+
if (m) return { provider: "codecommit", repositoryName: decodeURIComponent(m[1]) };
|
|
363
|
+
}
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
return { provider: "unknown" };
|
|
367
|
+
}
|
|
368
|
+
var codecommitClientsByRegion;
|
|
369
|
+
var codecommitMod;
|
|
431
370
|
async function codeCommitClient(region) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
return client;
|
|
371
|
+
if (!codecommitMod) codecommitMod = await loadSdk("@aws-sdk/client-codecommit");
|
|
372
|
+
if (!codecommitClientsByRegion) codecommitClientsByRegion = /* @__PURE__ */ new Map();
|
|
373
|
+
let client = codecommitClientsByRegion.get(region);
|
|
374
|
+
if (!client) {
|
|
375
|
+
client = new codecommitMod.CodeCommitClient({ region });
|
|
376
|
+
codecommitClientsByRegion.set(region, client);
|
|
377
|
+
}
|
|
378
|
+
return client;
|
|
442
379
|
}
|
|
443
|
-
/** Fetch (cached) a single CodeCommit commit's date + first parents via GetCommit. */
|
|
444
380
|
async function codeCommitMeta(region, repositoryName, commitId) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
commitMetaCache.set(commitId, meta);
|
|
455
|
-
return meta;
|
|
381
|
+
const cached = commitMetaCache.get(commitId);
|
|
382
|
+
if (cached) return cached;
|
|
383
|
+
if (!codecommitMod) codecommitMod = await loadSdk("@aws-sdk/client-codecommit");
|
|
384
|
+
const client = await codeCommitClient(region);
|
|
385
|
+
const out = await client.send(new codecommitMod.GetCommitCommand({ repositoryName, commitId }));
|
|
386
|
+
const commit = out.commit;
|
|
387
|
+
const meta = { date: toIso(commit?.committer?.date ?? commit?.author?.date), parents: commit?.parents };
|
|
388
|
+
commitMetaCache.set(commitId, meta);
|
|
389
|
+
return meta;
|
|
456
390
|
}
|
|
457
391
|
async function resolveCodeCommit(region, repositoryName, sha, sinceSha) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
cur = meta.parents?.[0];
|
|
479
|
-
}
|
|
480
|
-
// A truncated walk yields a commit NEWER than the true oldest-unshipped commit,
|
|
481
|
-
// which would UNDERSTATE lead time — omit the fields entirely rather than report a
|
|
482
|
-
// wrong (too-small) value. Reporting falls back to `unknown`.
|
|
483
|
-
if (truncated)
|
|
484
|
-
return {};
|
|
485
|
-
return oldest ? { commitTimestamp: oldest, commitCount: count } : {};
|
|
392
|
+
let cur = sha;
|
|
393
|
+
let count = 0;
|
|
394
|
+
let oldest;
|
|
395
|
+
let truncated = false;
|
|
396
|
+
for (let i = 0; ; i++) {
|
|
397
|
+
if (!cur) break;
|
|
398
|
+
if (sinceSha && cur === sinceSha) break;
|
|
399
|
+
if (i >= MAX_COMMIT_WALK) {
|
|
400
|
+
truncated = true;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
const meta = await codeCommitMeta(region, repositoryName, cur);
|
|
404
|
+
if (!meta?.date) break;
|
|
405
|
+
count++;
|
|
406
|
+
oldest = meta.date;
|
|
407
|
+
if (!sinceSha) break;
|
|
408
|
+
cur = meta.parents?.[0];
|
|
409
|
+
}
|
|
410
|
+
if (truncated) return {};
|
|
411
|
+
return oldest ? { commitTimestamp: oldest, commitCount: count } : {};
|
|
486
412
|
}
|
|
487
413
|
async function resolveGitHub(owner, repo, sha, sinceSha, orgId) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
return {};
|
|
502
|
-
}
|
|
503
|
-
if (r.ok) {
|
|
504
|
-
const j = await r.json();
|
|
505
|
-
const commits = Array.isArray(j.commits) ? j.commits : [];
|
|
506
|
-
if (commits.length > 0) {
|
|
507
|
-
const oldest = toIso(commits[0]?.commit?.committer?.date ?? commits[0]?.commit?.author?.date);
|
|
508
|
-
if (oldest)
|
|
509
|
-
return { commitTimestamp: oldest, commitCount: commits.length };
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
// fall through to single-commit resolution
|
|
513
|
-
}
|
|
514
|
-
const r2 = await fetchWithTimeout(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}`, { headers });
|
|
515
|
-
if (rateLimited(r2)) {
|
|
516
|
-
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
517
|
-
return {};
|
|
518
|
-
}
|
|
519
|
-
if (r2.ok) {
|
|
520
|
-
const j = await r2.json();
|
|
521
|
-
const d = toIso(j.commit?.committer?.date ?? j.commit?.author?.date);
|
|
522
|
-
if (d)
|
|
523
|
-
return { commitTimestamp: d, commitCount: 1 };
|
|
414
|
+
const token = await getGitHubToken(orgId);
|
|
415
|
+
const headers = {
|
|
416
|
+
"Accept": "application/vnd.github+json",
|
|
417
|
+
"User-Agent": "pipeline-builder-forwarder",
|
|
418
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
419
|
+
};
|
|
420
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
421
|
+
const rateLimited = (r) => r.status === 403 || r.status === 429;
|
|
422
|
+
if (sinceSha) {
|
|
423
|
+
const r = await fetchWithTimeout(`https://api.github.com/repos/${owner}/${repo}/compare/${sinceSha}...${sha}`, { headers });
|
|
424
|
+
if (rateLimited(r)) {
|
|
425
|
+
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
426
|
+
return {};
|
|
524
427
|
}
|
|
428
|
+
if (r.ok) {
|
|
429
|
+
const j = await r.json();
|
|
430
|
+
const commits = Array.isArray(j.commits) ? j.commits : [];
|
|
431
|
+
if (commits.length > 0) {
|
|
432
|
+
const oldest = toIso(commits[0]?.commit?.committer?.date ?? commits[0]?.commit?.author?.date);
|
|
433
|
+
if (oldest) return { commitTimestamp: oldest, commitCount: commits.length };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const r2 = await fetchWithTimeout(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}`, { headers });
|
|
438
|
+
if (rateLimited(r2)) {
|
|
439
|
+
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
525
440
|
return {};
|
|
441
|
+
}
|
|
442
|
+
if (r2.ok) {
|
|
443
|
+
const j = await r2.json();
|
|
444
|
+
const d = toIso(j.commit?.committer?.date ?? j.commit?.author?.date);
|
|
445
|
+
if (d) return { commitTimestamp: d, commitCount: 1 };
|
|
446
|
+
}
|
|
447
|
+
return {};
|
|
526
448
|
}
|
|
527
449
|
async function resolveBitbucket(owner, repo, sha) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
return {};
|
|
534
|
-
}
|
|
535
|
-
if (r.ok) {
|
|
536
|
-
const j = await r.json();
|
|
537
|
-
const d = toIso(j.date);
|
|
538
|
-
if (d)
|
|
539
|
-
return { commitTimestamp: d, commitCount: 1 };
|
|
540
|
-
}
|
|
450
|
+
const r = await fetchWithTimeout(`https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/commit/${sha}`, {
|
|
451
|
+
headers: { "User-Agent": "pipeline-builder-forwarder", "Accept": "application/json" }
|
|
452
|
+
});
|
|
453
|
+
if (r.status === 429) {
|
|
454
|
+
scmCooldownUntil = Date.now() + SCM_COOLDOWN_MS;
|
|
541
455
|
return {};
|
|
456
|
+
}
|
|
457
|
+
if (r.ok) {
|
|
458
|
+
const j = await r.json();
|
|
459
|
+
const d = toIso(j.date);
|
|
460
|
+
if (d) return { commitTimestamp: d, commitCount: 1 };
|
|
461
|
+
}
|
|
462
|
+
return {};
|
|
542
463
|
}
|
|
543
|
-
/**
|
|
544
|
-
* Resolve `commitTimestamp` + `commitCount` for a source/deploy event, in-account,
|
|
545
|
-
* best-effort. Computed once per (pipelineId, sha); advances the pipeline's range
|
|
546
|
-
* boundary only on success. Never throws — returns {} on any failure.
|
|
547
|
-
*/
|
|
548
464
|
async function resolveCommitInfo(pipelineId, orgId, region, src) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
catch (err) {
|
|
572
|
-
log.warn('Commit resolution failed (omitting commitTimestamp/commitCount)', { provider: src.provider, error: err?.name ?? String(err) });
|
|
573
|
-
info = {};
|
|
574
|
-
}
|
|
465
|
+
if (src.provider === "unknown") return {};
|
|
466
|
+
const rkey = `${pipelineId}|${src.sha}`;
|
|
467
|
+
const cached = commitResultCache.get(rkey);
|
|
468
|
+
if (cached) return cached;
|
|
469
|
+
const prev = lastShaByPipeline.get(pipelineId);
|
|
470
|
+
const sinceSha = prev && prev !== src.sha ? prev : void 0;
|
|
471
|
+
let info = {};
|
|
472
|
+
try {
|
|
473
|
+
if (src.provider === "codecommit" && src.repositoryName) {
|
|
474
|
+
info = await resolveCodeCommit(region, src.repositoryName, src.sha, sinceSha);
|
|
475
|
+
} else if (src.provider === "github" && src.owner && src.repo) {
|
|
476
|
+
if (Date.now() >= scmCooldownUntil) info = await resolveGitHub(src.owner, src.repo, src.sha, sinceSha, orgId);
|
|
477
|
+
} else if (src.provider === "bitbucket" && src.owner && src.repo) {
|
|
478
|
+
if (Date.now() >= scmCooldownUntil) info = await resolveBitbucket(src.owner, src.repo, src.sha);
|
|
479
|
+
}
|
|
480
|
+
} catch (err) {
|
|
481
|
+
log.warn("Commit resolution failed (omitting commitTimestamp/commitCount)", { provider: src.provider, error: err?.name ?? String(err) });
|
|
482
|
+
info = {};
|
|
483
|
+
}
|
|
484
|
+
if (info.commitTimestamp) {
|
|
575
485
|
commitResultCache.set(rkey, info);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
486
|
+
lastShaByPipeline.set(pipelineId, src.sha);
|
|
487
|
+
}
|
|
488
|
+
return info;
|
|
489
|
+
}
|
|
490
|
+
function _resetScmForTests() {
|
|
491
|
+
commitResultCache.clear();
|
|
492
|
+
lastShaByPipeline.clear();
|
|
493
|
+
commitMetaCache.clear();
|
|
494
|
+
scmCooldownUntil = 0;
|
|
495
|
+
githubTokenByOrg.clear();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/tags.ts
|
|
499
|
+
var codepipelineMod;
|
|
500
|
+
async function loadCodePipeline() {
|
|
501
|
+
if (!codepipelineMod) codepipelineMod = await import("@aws-sdk/client-codepipeline");
|
|
502
|
+
return codepipelineMod;
|
|
503
|
+
}
|
|
504
|
+
var PIPELINE_ID_TAG = "pb.pipeline-id";
|
|
505
|
+
var DEPLOYS_TAG = "pb.deploys";
|
|
506
|
+
var ORG_ID_TAG = "OrgId";
|
|
507
|
+
var clientsByRegion = /* @__PURE__ */ new Map();
|
|
508
|
+
var resolvedByArn = new BoundedMap(CACHE_MAX_ENTRIES);
|
|
509
|
+
var NEG_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
510
|
+
var POS_CACHE_TTL_MS = 15 * 60 * 1e3;
|
|
511
|
+
var MAX_ATTR = 255;
|
|
512
|
+
function parseDeploys(value) {
|
|
513
|
+
const map = /* @__PURE__ */ new Map();
|
|
514
|
+
if (!value) return map;
|
|
515
|
+
for (const pair of value.split("+")) {
|
|
516
|
+
const idx = pair.indexOf(":");
|
|
517
|
+
if (idx <= 0) continue;
|
|
518
|
+
const stage = pair.slice(0, idx).trim();
|
|
519
|
+
const env = pair.slice(idx + 1).trim();
|
|
520
|
+
if (stage && env) map.set(stage, env.slice(0, MAX_ATTR));
|
|
521
|
+
}
|
|
522
|
+
return map;
|
|
523
|
+
}
|
|
524
|
+
async function pipelineClient(region) {
|
|
525
|
+
let client = clientsByRegion.get(region);
|
|
526
|
+
if (!client) {
|
|
527
|
+
const { CodePipelineClient } = await loadCodePipeline();
|
|
528
|
+
client = new CodePipelineClient({ region });
|
|
529
|
+
clientsByRegion.set(region, client);
|
|
530
|
+
}
|
|
531
|
+
return client;
|
|
579
532
|
}
|
|
580
|
-
|
|
581
|
-
const
|
|
582
|
-
|
|
533
|
+
async function resolvePipeline(arn, region) {
|
|
534
|
+
const cached = resolvedByArn.get(arn);
|
|
535
|
+
if (cached && Date.now() - cached.ts < (cached.pipelineId !== null ? POS_CACHE_TTL_MS : NEG_CACHE_TTL_MS)) {
|
|
536
|
+
return { pipelineId: cached.pipelineId, orgId: cached.orgId, deploys: cached.deploys };
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
const client = await pipelineClient(region);
|
|
540
|
+
const { ListTagsForResourceCommand } = await loadCodePipeline();
|
|
541
|
+
const out = await client.send(new ListTagsForResourceCommand({ resourceArn: arn }));
|
|
542
|
+
const pipelineId = out.tags?.find((t) => t.key === PIPELINE_ID_TAG)?.value ?? null;
|
|
543
|
+
const orgId = out.tags?.find((t) => t.key === ORG_ID_TAG)?.value ?? null;
|
|
544
|
+
const deploys = parseDeploys(out.tags?.find((t) => t.key === DEPLOYS_TAG)?.value);
|
|
545
|
+
if (!pipelineId) log.warn("Pipeline missing pb.pipeline-id tag \u2014 skipping (register it?)", { arn });
|
|
546
|
+
resolvedByArn.set(arn, { pipelineId, orgId, deploys, ts: Date.now() });
|
|
547
|
+
return { pipelineId, orgId, deploys };
|
|
548
|
+
} catch (err) {
|
|
549
|
+
const name = err?.name;
|
|
550
|
+
if (name === "AccessDeniedException") {
|
|
551
|
+
log.error("AccessDenied calling codepipeline:ListTagsForResource \u2014 grant it to the Lambda role", { arn, error: name });
|
|
552
|
+
throw err;
|
|
553
|
+
}
|
|
554
|
+
const e = err;
|
|
555
|
+
const status = e.$metadata?.httpStatusCode;
|
|
556
|
+
const retryable = e.$retryable != null || typeof status === "number" && status >= 500 || /throttl|timeout|toomanyrequests|serviceunavailable|econnreset|networkingerror/i.test(name ?? "");
|
|
557
|
+
if (retryable) {
|
|
558
|
+
log.error("Transient error resolving pb.pipeline-id tag \u2014 will retry the batch", { arn, error: name ?? String(err) });
|
|
559
|
+
throw err;
|
|
560
|
+
}
|
|
561
|
+
log.error("Failed to resolve pb.pipeline-id tag", { arn, error: name ?? String(err) });
|
|
562
|
+
return { pipelineId: null, orgId: null, deploys: /* @__PURE__ */ new Map() };
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function _resetTagsForTests() {
|
|
566
|
+
resolvedByArn.clear();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// src/parse.ts
|
|
570
|
+
var MAX_ERROR_MESSAGE = 4e3;
|
|
583
571
|
function firstAttr(...vals) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* Best-effort extraction of the source commit id + ref (and the revision URL, used
|
|
592
|
-
* only in-account to classify the source type for commit-range resolution) from a
|
|
593
|
-
* CodePipeline event detail. CodePipeline surfaces the source revision under a few
|
|
594
|
-
* shapes depending on the event type / source-action; we check the known ones and
|
|
595
|
-
* leave the fields undefined when none are present (NULL downstream). Never pulls an
|
|
596
|
-
* ARN/account id: only revision/branch/url string fields are read; the revisionUrl
|
|
597
|
-
* is consumed in-account (source classification) and never forwarded on its own.
|
|
598
|
-
*/
|
|
572
|
+
for (const v of vals) {
|
|
573
|
+
if (typeof v === "string" && v.length > 0) return v.slice(0, MAX_ATTR);
|
|
574
|
+
}
|
|
575
|
+
return void 0;
|
|
576
|
+
}
|
|
599
577
|
function extractCommit(detail) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
578
|
+
const revs = detail["source-revisions"] ?? detail.sourceRevisions;
|
|
579
|
+
const firstRev = Array.isArray(revs) ? revs[0] : void 0;
|
|
580
|
+
const execResult = detail["execution-result"];
|
|
581
|
+
const commitSha = firstAttr(
|
|
582
|
+
detail.commitId,
|
|
583
|
+
detail.revisionId,
|
|
584
|
+
firstRev?.revisionId,
|
|
585
|
+
execResult?.revisionId
|
|
586
|
+
);
|
|
587
|
+
const commitRef = firstAttr(
|
|
588
|
+
detail.commitRef,
|
|
589
|
+
detail.branch,
|
|
590
|
+
detail.sourceBranch,
|
|
591
|
+
firstRev?.branchName,
|
|
592
|
+
firstRev?.branch
|
|
593
|
+
);
|
|
594
|
+
const revisionUrl = typeof firstRev?.revisionUrl === "string" ? firstRev.revisionUrl : void 0;
|
|
595
|
+
return { ...commitSha && { commitSha }, ...commitRef && { commitRef }, ...revisionUrl && { revisionUrl } };
|
|
609
596
|
}
|
|
610
597
|
function classifyEvent(detailType) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
return { eventType: 'ACTION', eventSource: 'codepipeline' };
|
|
617
|
-
// CodeBuild "Build State" events identify a build *project*, not a pipeline, so
|
|
618
|
-
// parseRecord drops them on the `eventSource !== 'codepipeline'` check below —
|
|
619
|
-
// the 'BUILD' eventType is never persisted here (plugin BUILD events are
|
|
620
|
-
// recorded directly by the plugin service). The 'codebuild' source is what
|
|
621
|
-
// routes the drop; keep this branch only for that.
|
|
622
|
-
if (detailType.includes('Build State'))
|
|
623
|
-
return { eventType: 'BUILD', eventSource: 'codebuild' };
|
|
624
|
-
return { eventType: 'PIPELINE', eventSource: 'codepipeline' };
|
|
625
|
-
}
|
|
626
|
-
/** Parse + resolve one record to a reportable event, or null to skip it. */
|
|
627
|
-
async function parseRecord(record) {
|
|
628
|
-
let event;
|
|
629
|
-
// A malformed body is a dead message (retrying won't help) — log + skip it so
|
|
630
|
-
// it can't fail the whole batch. Resolution errors below (e.g. AccessDenied)
|
|
631
|
-
// are NOT caught here: those are transient/infra and must propagate to retry.
|
|
632
|
-
try {
|
|
633
|
-
event = JSON.parse(record.body);
|
|
634
|
-
}
|
|
635
|
-
catch (err) {
|
|
636
|
-
log.warn('Skipping SQS record with unparseable body', { messageId: record.messageId, error: String(err) });
|
|
637
|
-
return null;
|
|
638
|
-
}
|
|
639
|
-
const { eventType, eventSource } = classifyEvent(event['detail-type']);
|
|
640
|
-
const detail = { ...event.detail };
|
|
641
|
-
// The raw account never needs to leave AWS now — drop it from the payload.
|
|
642
|
-
delete detail.account;
|
|
643
|
-
// CodeBuild "Build State" events identify a build *project*, not a pipeline,
|
|
644
|
-
// and a project can be shared across pipelines — there's no clean 1:1 mapping
|
|
645
|
-
// to a pipeline id, so skip them.
|
|
646
|
-
if (eventSource !== 'codepipeline') {
|
|
647
|
-
log.warn('Skipping non-CodePipeline event (no pipeline tag to resolve)', { detailType: event['detail-type'] });
|
|
648
|
-
return null;
|
|
649
|
-
}
|
|
650
|
-
const pipelineName = detail.pipeline;
|
|
651
|
-
if (!pipelineName) {
|
|
652
|
-
log.warn('CodePipeline event missing pipeline name — skipping');
|
|
653
|
-
return null;
|
|
654
|
-
}
|
|
655
|
-
// Resolve the pipeline's pb.pipeline-id (= platform pipelineId) + pb.deploys map.
|
|
656
|
-
// The ARN is only a transient handle for the tag lookup; it is never stored.
|
|
657
|
-
const arn = `arn:aws:codepipeline:${event.region}:${event.account}:${pipelineName}`;
|
|
658
|
-
const { pipelineId, orgId, deploys } = await resolvePipeline(arn, event.region);
|
|
659
|
-
if (!pipelineId)
|
|
660
|
-
return null; // untagged / unregistered → skip
|
|
661
|
-
const startedAt = detail['start-time'] || event.time;
|
|
662
|
-
const state = detail.state;
|
|
663
|
-
let durationMs;
|
|
664
|
-
let completedAt;
|
|
665
|
-
if (['SUCCEEDED', 'FAILED', 'CANCELED', 'STOPPED'].includes(state)) {
|
|
666
|
-
completedAt = event.time;
|
|
667
|
-
if (startedAt && completedAt) {
|
|
668
|
-
const ms = new Date(completedAt).getTime() - new Date(startedAt).getTime();
|
|
669
|
-
if (ms >= 0)
|
|
670
|
-
durationMs = ms;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
// Promote the failure reason to a typed field. On Action events AWS puts the
|
|
674
|
-
// human-readable summary (and a log URL + error-code, which stay in `detail`)
|
|
675
|
-
// under `execution-result`.
|
|
676
|
-
const result = detail['execution-result'];
|
|
677
|
-
const summary = result?.['external-execution-summary'];
|
|
678
|
-
const errorMessage = typeof summary === 'string' && summary.length > 0
|
|
679
|
-
? summary.slice(0, MAX_ERROR_MESSAGE)
|
|
680
|
-
: undefined;
|
|
681
|
-
// DORA deploy attribution — all optional: omitted (undefined) when the
|
|
682
|
-
// event/pipeline doesn't carry them.
|
|
683
|
-
const { commitSha, commitRef, revisionUrl } = extractCommit(detail);
|
|
684
|
-
const executionId = detail['execution-id'];
|
|
685
|
-
const stageName = detail.stage;
|
|
686
|
-
const actionName = detail.action;
|
|
687
|
-
// Environment (⇒ isDeploy) is set ONLY when this event's stage is listed in the
|
|
688
|
-
// pipeline's pb.deploys tag. PIPELINE events have no stage → never a deploy.
|
|
689
|
-
const environment = stageName ? deploys.get(stageName) : undefined;
|
|
690
|
-
// Phase 4 — resolve the commit range (oldest-unshipped timestamp + count) for any
|
|
691
|
-
// event carrying a commit. Best-effort/in-account; omitted on any failure.
|
|
692
|
-
// GATED on `DORA_ENABLED` (set by `setup-events --with-dora`): DORA lead time is an
|
|
693
|
-
// advanced_reporting add-on, so orgs without it skip the SCM/secret cost entirely —
|
|
694
|
-
// standard reporting (commitSha + all other fields) still forwards regardless.
|
|
695
|
-
let commitTimestamp;
|
|
696
|
-
let commitCount;
|
|
697
|
-
if (commitSha && process.env.DORA_ENABLED === 'true') {
|
|
698
|
-
const src = classifySource(revisionUrl);
|
|
699
|
-
const info = await resolveCommitInfo(pipelineId, orgId, event.region, { sha: commitSha, ...src });
|
|
700
|
-
commitTimestamp = info.commitTimestamp;
|
|
701
|
-
commitCount = info.commitCount;
|
|
702
|
-
}
|
|
703
|
-
return {
|
|
704
|
-
orgId,
|
|
705
|
-
pipelineId,
|
|
706
|
-
eventSource,
|
|
707
|
-
eventType,
|
|
708
|
-
status: state,
|
|
709
|
-
executionId,
|
|
710
|
-
stageName,
|
|
711
|
-
actionName,
|
|
712
|
-
errorMessage,
|
|
713
|
-
startedAt,
|
|
714
|
-
completedAt,
|
|
715
|
-
durationMs,
|
|
716
|
-
...(commitSha && { commitSha }),
|
|
717
|
-
...(commitRef && { commitRef }),
|
|
718
|
-
...(commitTimestamp && { commitTimestamp }),
|
|
719
|
-
...(commitCount && { commitCount }),
|
|
720
|
-
...(environment && { environment }),
|
|
721
|
-
detail,
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
let sqsMod;
|
|
725
|
-
let sqsClientsByRegion;
|
|
726
|
-
async function sqsClient(region) {
|
|
727
|
-
if (!sqsMod)
|
|
728
|
-
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
729
|
-
if (!sqsClientsByRegion)
|
|
730
|
-
sqsClientsByRegion = new Map();
|
|
731
|
-
let client = sqsClientsByRegion.get(region);
|
|
732
|
-
if (!client) {
|
|
733
|
-
client = new sqsMod.SQSClient({ region });
|
|
734
|
-
sqsClientsByRegion.set(region, client);
|
|
735
|
-
}
|
|
736
|
-
return client;
|
|
737
|
-
}
|
|
738
|
-
// Delivery-health counters are attributed PER RESOLVED ORG (the forwarder is a fleet
|
|
739
|
-
// Lambda serving many orgs; a single global counter would mis-attribute one org's
|
|
740
|
-
// throughput to another). Keyed by orgId ('' bucket = pipeline with no OrgId tag).
|
|
741
|
-
// The DLQ depth (`dropped`) is genuinely fleet-global — one shared DLQ — so it is a
|
|
742
|
-
// single snapshot posted alongside each org's health, documented as the shared depth.
|
|
743
|
-
const forwardedByOrg = new Map();
|
|
744
|
-
const lastEventAtByOrg = new Map();
|
|
745
|
-
let lastHealthAt = 0;
|
|
746
|
-
let lastRedriveAt = 0;
|
|
747
|
-
const HEALTH_THROTTLE_MS = 60 * 1000;
|
|
748
|
-
const REDRIVE_THROTTLE_MS = 5 * 60 * 1000;
|
|
749
|
-
// Poison-message ceiling: if the oldest DLQ message is older than this, we STOP
|
|
750
|
-
// redriving it (DLQ→main→fail→DLQ would loop forever). Such messages are left for an
|
|
751
|
-
// alarm / manual inspection instead of churning the fleet indefinitely.
|
|
752
|
-
const MAX_DLQ_REDRIVE_AGE_SEC = 6 * 60 * 60; // 6 hours
|
|
753
|
-
/** Parse the SQS trigger ARN and derive the main + DLQ ARNs and the DLQ URL. */
|
|
754
|
-
function deriveQueues(mainQueueArn) {
|
|
755
|
-
if (!mainQueueArn)
|
|
756
|
-
return undefined;
|
|
757
|
-
// arn:aws:sqs:{region}:{account}:{name}
|
|
758
|
-
const parts = mainQueueArn.split(':');
|
|
759
|
-
if (parts.length < 6 || parts[2] !== 'sqs')
|
|
760
|
-
return undefined;
|
|
761
|
-
const [, , , region, account, name] = parts;
|
|
762
|
-
const dlqArn = process.env.EVENT_DLQ_ARN || `arn:aws:sqs:${region}:${account}:${name}-dlq`;
|
|
763
|
-
const dlqName = dlqArn.split(':').pop() ?? `${name}-dlq`;
|
|
764
|
-
const dlqUrl = `https://sqs.${region}.amazonaws.com/${account}/${dlqName}`;
|
|
765
|
-
return { region, mainArn: mainQueueArn, dlqArn, dlqUrl };
|
|
766
|
-
}
|
|
767
|
-
/** Snapshot the DLQ depth + the age (seconds) of its oldest message (for the
|
|
768
|
-
* poison-message redrive ceiling). Missing/NaN attributes fall back to 0. */
|
|
769
|
-
async function dlqStats(region, dlqUrl) {
|
|
770
|
-
if (!sqsMod)
|
|
771
|
-
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
772
|
-
const client = await sqsClient(region);
|
|
773
|
-
const out = await client.send(new sqsMod.GetQueueAttributesCommand({
|
|
774
|
-
QueueUrl: dlqUrl,
|
|
775
|
-
AttributeNames: ['ApproximateNumberOfMessages', 'ApproximateAgeOfOldestMessage'],
|
|
776
|
-
}));
|
|
777
|
-
const n = Number(out.Attributes?.ApproximateNumberOfMessages ?? '0');
|
|
778
|
-
const age = Number(out.Attributes?.ApproximateAgeOfOldestMessage ?? '0');
|
|
779
|
-
return {
|
|
780
|
-
depth: Number.isFinite(n) ? n : 0,
|
|
781
|
-
oldestAgeSec: Number.isFinite(age) ? age : 0,
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
async function hasActiveMoveTask(region, dlqArn) {
|
|
785
|
-
if (!sqsMod)
|
|
786
|
-
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
787
|
-
const client = await sqsClient(region);
|
|
788
|
-
const out = await client.send(new sqsMod.ListMessageMoveTasksCommand({ SourceArn: dlqArn }));
|
|
789
|
-
return (out.Results ?? []).some(t => t.Status === 'RUNNING');
|
|
790
|
-
}
|
|
791
|
-
async function startMoveTask(region, dlqArn, mainArn) {
|
|
792
|
-
if (!sqsMod)
|
|
793
|
-
sqsMod = await loadSdk('@aws-sdk/client-sqs');
|
|
794
|
-
const client = await sqsClient(region);
|
|
795
|
-
await client.send(new sqsMod.StartMessageMoveTaskCommand({ SourceArn: dlqArn, DestinationArn: mainArn }));
|
|
796
|
-
}
|
|
797
|
-
/** POST one org's ingest-health signal. Returns true only on a confirmed 2xx so the
|
|
798
|
-
* caller resets that org's `forwarded` counter ONLY when the signal was accepted
|
|
799
|
-
* (a dropped/failed POST must not silently zero the accumulated count). */
|
|
800
|
-
async function postIngestHealth(baseUrl, body) {
|
|
801
|
-
try {
|
|
802
|
-
const token = await getAuthToken();
|
|
803
|
-
const res = await fetch(`${baseUrl}/api/reports/ingest-health`, {
|
|
804
|
-
method: 'POST',
|
|
805
|
-
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
|
|
806
|
-
body: JSON.stringify(body),
|
|
807
|
-
});
|
|
808
|
-
if (!res.ok) {
|
|
809
|
-
log.warn('ingest-health POST returned non-2xx (ignored)', { status: res.status, orgId: body.orgId });
|
|
810
|
-
return false;
|
|
811
|
-
}
|
|
812
|
-
return true;
|
|
813
|
-
}
|
|
814
|
-
catch (err) {
|
|
815
|
-
log.warn('ingest-health POST failed (ignored)', { error: String(err), orgId: body.orgId });
|
|
816
|
-
return false;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Throttled, best-effort post-success work: emit per-org ingest-health and self-heal
|
|
821
|
-
* the DLQ. Never throws. `forwarded`/`lastEventAt` accumulate PER ORG across batches
|
|
822
|
-
* between health posts; `dropped` is the shared fleet DLQ depth snapshot. A per-org
|
|
823
|
-
* counter is zeroed only after that org's health POST is confirmed 2xx.
|
|
824
|
-
*/
|
|
825
|
-
async function reportHealthAndRedrive(baseUrl, mainQueueArn) {
|
|
826
|
-
const now = Date.now();
|
|
827
|
-
if (now - lastHealthAt < HEALTH_THROTTLE_MS)
|
|
828
|
-
return;
|
|
829
|
-
lastHealthAt = now;
|
|
830
|
-
try {
|
|
831
|
-
const q = deriveQueues(mainQueueArn);
|
|
832
|
-
let depth = 0;
|
|
833
|
-
let oldestAgeSec = 0;
|
|
834
|
-
if (q) {
|
|
835
|
-
try {
|
|
836
|
-
const stats = await dlqStats(q.region, q.dlqUrl);
|
|
837
|
-
depth = stats.depth;
|
|
838
|
-
oldestAgeSec = stats.oldestAgeSec;
|
|
839
|
-
}
|
|
840
|
-
catch (err) {
|
|
841
|
-
log.warn('DLQ stats check failed (ignored)', { error: err?.name ?? String(err) });
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
// Post one health signal per resolved org, attributing its own forwarded count +
|
|
845
|
-
// last event time. `dropped` (the shared fleet DLQ depth) rides along on each.
|
|
846
|
-
// Snapshot the keys first so we don't mutate the map while iterating.
|
|
847
|
-
const orgKeys = new Set([...forwardedByOrg.keys(), ...lastEventAtByOrg.keys()]);
|
|
848
|
-
if (orgKeys.size === 0)
|
|
849
|
-
orgKeys.add(''); // nothing forwarded yet → still report depth
|
|
850
|
-
for (const key of orgKeys) {
|
|
851
|
-
const forwarded = forwardedByOrg.get(key) ?? 0;
|
|
852
|
-
const lastEventAt = lastEventAtByOrg.get(key);
|
|
853
|
-
const ok = await postIngestHealth(baseUrl, {
|
|
854
|
-
...(key ? { orgId: key } : {}),
|
|
855
|
-
forwarded,
|
|
856
|
-
dropped: depth,
|
|
857
|
-
lastEventAt,
|
|
858
|
-
});
|
|
859
|
-
// Reset the forwarded counter ONLY on a confirmed 2xx — a failed/dropped POST
|
|
860
|
-
// keeps the count so the next tick re-reports it instead of losing it.
|
|
861
|
-
if (ok)
|
|
862
|
-
forwardedByOrg.set(key, 0);
|
|
863
|
-
}
|
|
864
|
-
if (q && depth > 0 && now - lastRedriveAt >= REDRIVE_THROTTLE_MS) {
|
|
865
|
-
// Poison-message ceiling: never redrive a DLQ whose oldest message has aged
|
|
866
|
-
// past the ceiling — that message keeps failing and would loop DLQ→main→DLQ.
|
|
867
|
-
if (oldestAgeSec > MAX_DLQ_REDRIVE_AGE_SEC) {
|
|
868
|
-
log.warn('DLQ oldest message exceeds poison-age ceiling — skipping redrive (needs manual attention)', {
|
|
869
|
-
dlqArn: q.dlqArn, dropped: depth, oldestAgeSec, ceilingSec: MAX_DLQ_REDRIVE_AGE_SEC,
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
else {
|
|
873
|
-
try {
|
|
874
|
-
if (!(await hasActiveMoveTask(q.region, q.dlqArn))) {
|
|
875
|
-
lastRedriveAt = now;
|
|
876
|
-
await startMoveTask(q.region, q.dlqArn, q.mainArn);
|
|
877
|
-
log.info('Started self-healing DLQ→main redrive', { dlqArn: q.dlqArn, dropped: depth, oldestAgeSec });
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
catch (err) {
|
|
881
|
-
log.warn('DLQ redrive failed (ignored)', { error: err?.name ?? String(err) });
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
catch (err) {
|
|
887
|
-
log.warn('health/redrive step failed (ignored)', { error: String(err) });
|
|
888
|
-
}
|
|
598
|
+
if (detailType.includes("Pipeline Execution")) return { eventType: "PIPELINE", eventSource: "codepipeline" };
|
|
599
|
+
if (detailType.includes("Stage Execution")) return { eventType: "STAGE", eventSource: "codepipeline" };
|
|
600
|
+
if (detailType.includes("Action Execution")) return { eventType: "ACTION", eventSource: "codepipeline" };
|
|
601
|
+
if (detailType.includes("Build State")) return { eventType: "BUILD", eventSource: "codebuild" };
|
|
602
|
+
return { eventType: "PIPELINE", eventSource: "codepipeline" };
|
|
889
603
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
if (
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
604
|
+
async function parseRecord(record) {
|
|
605
|
+
let event;
|
|
606
|
+
try {
|
|
607
|
+
event = JSON.parse(record.body);
|
|
608
|
+
} catch (err) {
|
|
609
|
+
log.warn("Skipping SQS record with unparseable body", { messageId: record.messageId, error: String(err) });
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
const { eventType, eventSource } = classifyEvent(event["detail-type"]);
|
|
613
|
+
const detail = { ...event.detail };
|
|
614
|
+
delete detail.account;
|
|
615
|
+
if (eventSource !== "codepipeline") {
|
|
616
|
+
log.warn("Skipping non-CodePipeline event (no pipeline tag to resolve)", { detailType: event["detail-type"] });
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
const pipelineName = detail.pipeline;
|
|
620
|
+
if (!pipelineName) {
|
|
621
|
+
log.warn("CodePipeline event missing pipeline name \u2014 skipping");
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
const arn = `arn:aws:codepipeline:${event.region}:${event.account}:${pipelineName}`;
|
|
625
|
+
const { pipelineId, orgId, deploys } = await resolvePipeline(arn, event.region);
|
|
626
|
+
if (!pipelineId) return null;
|
|
627
|
+
const startedAt = detail["start-time"] || event.time;
|
|
628
|
+
const state = detail.state;
|
|
629
|
+
let durationMs;
|
|
630
|
+
let completedAt;
|
|
631
|
+
if (["SUCCEEDED", "FAILED", "CANCELED", "STOPPED"].includes(state)) {
|
|
632
|
+
completedAt = event.time;
|
|
633
|
+
if (startedAt && completedAt) {
|
|
634
|
+
const ms = new Date(completedAt).getTime() - new Date(startedAt).getTime();
|
|
635
|
+
if (ms >= 0) durationMs = ms;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const result = detail["execution-result"];
|
|
639
|
+
const summary = result?.["external-execution-summary"];
|
|
640
|
+
const errorMessage = typeof summary === "string" && summary.length > 0 ? summary.slice(0, MAX_ERROR_MESSAGE) : void 0;
|
|
641
|
+
const { commitSha, commitRef, revisionUrl } = extractCommit(detail);
|
|
642
|
+
const executionId = detail["execution-id"];
|
|
643
|
+
const stageName = detail.stage;
|
|
644
|
+
const actionName = detail.action;
|
|
645
|
+
const environment = stageName ? deploys.get(stageName) : void 0;
|
|
646
|
+
let commitTimestamp;
|
|
647
|
+
let commitCount;
|
|
648
|
+
if (commitSha && process.env.DORA_ENABLED === "true") {
|
|
649
|
+
const src = classifySource(revisionUrl);
|
|
650
|
+
const info = await resolveCommitInfo(pipelineId, orgId, event.region, { sha: commitSha, ...src });
|
|
651
|
+
commitTimestamp = info.commitTimestamp;
|
|
652
|
+
commitCount = info.commitCount;
|
|
653
|
+
}
|
|
654
|
+
return {
|
|
655
|
+
orgId,
|
|
656
|
+
pipelineId,
|
|
657
|
+
eventSource,
|
|
658
|
+
eventType,
|
|
659
|
+
status: state,
|
|
660
|
+
executionId,
|
|
661
|
+
stageName,
|
|
662
|
+
actionName,
|
|
663
|
+
errorMessage,
|
|
664
|
+
startedAt,
|
|
665
|
+
completedAt,
|
|
666
|
+
durationMs,
|
|
667
|
+
...commitSha && { commitSha },
|
|
668
|
+
...commitRef && { commitRef },
|
|
669
|
+
...commitTimestamp && { commitTimestamp },
|
|
670
|
+
...commitCount && { commitCount },
|
|
671
|
+
...environment && { environment },
|
|
672
|
+
detail
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/index.ts
|
|
677
|
+
var handler = async (event) => {
|
|
678
|
+
const baseUrl = process.env.PLATFORM_BASE_URL;
|
|
679
|
+
if (!baseUrl) throw new Error("PLATFORM_BASE_URL environment variable is required");
|
|
680
|
+
const events = (await Promise.all(event.Records.map(parseRecord))).filter((e) => e !== null);
|
|
681
|
+
if (events.length === 0) {
|
|
682
|
+
log.info("No resolvable CodePipeline events in batch");
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const payloadEvents = events.map(({ orgId: _orgId, ...rest }) => rest);
|
|
686
|
+
const post = (token) => fetch(`${baseUrl}/api/reports/events`, {
|
|
687
|
+
method: "POST",
|
|
688
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
|
|
689
|
+
body: JSON.stringify({ events: payloadEvents })
|
|
690
|
+
});
|
|
691
|
+
let res = await post(await getAuthToken());
|
|
692
|
+
if (res.status === 401 || res.status === 403) {
|
|
693
|
+
log.warn("Reporting API auth failed; re-reading the stored key and retrying once", { status: res.status });
|
|
694
|
+
invalidateCredential();
|
|
695
|
+
res = await post(await getAuthToken());
|
|
696
|
+
}
|
|
697
|
+
if (!res.ok) {
|
|
698
|
+
const body = await res.text().catch(() => "");
|
|
699
|
+
log.error(`Reporting API returned ${res.status}`, { body });
|
|
700
|
+
throw new Error(`Reporting API failed: ${res.status}`);
|
|
701
|
+
}
|
|
702
|
+
const result = await res.json().catch(() => ({}));
|
|
703
|
+
log.info(`Ingested ${events.length} events`, { inserted: result.data });
|
|
704
|
+
recordForwarded(events);
|
|
705
|
+
await reportHealthAndRedrive(baseUrl, event.Records[0]?.eventSourceARN);
|
|
706
|
+
};
|
|
707
|
+
function _resetForTests() {
|
|
708
|
+
_resetTagsForTests();
|
|
709
|
+
_resetScmForTests();
|
|
710
|
+
_resetHealthForTests();
|
|
711
|
+
_resetAuthForTests();
|
|
712
|
+
}
|
|
713
|
+
export {
|
|
714
|
+
_resetForTests,
|
|
715
|
+
handler
|
|
948
716
|
};
|
|
949
|
-
/**
|
|
950
|
-
* @internal Test-only: reset all module-level caches/throttle state so tests are
|
|
951
|
-
* isolated from one another. Stripped from the published .d.ts (stripInternal).
|
|
952
|
-
*/
|
|
953
|
-
export function _resetForTests() {
|
|
954
|
-
resolvedByArn.clear();
|
|
955
|
-
commitResultCache.clear();
|
|
956
|
-
lastShaByPipeline.clear();
|
|
957
|
-
commitMetaCache.clear();
|
|
958
|
-
scmCooldownUntil = 0;
|
|
959
|
-
githubTokenByOrg.clear();
|
|
960
|
-
forwardedByOrg.clear();
|
|
961
|
-
lastEventAtByOrg.clear();
|
|
962
|
-
lastHealthAt = 0;
|
|
963
|
-
lastRedriveAt = 0;
|
|
964
|
-
cachedKey = null;
|
|
965
|
-
cachedToken = null;
|
|
966
|
-
cachedTokenExpiresAt = 0;
|
|
967
|
-
}
|
|
968
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFFSCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQWtCOUYsSUFBSSxlQUErQyxDQUFDO0FBQ3BELEtBQUssVUFBVSxnQkFBZ0I7SUFDN0IsSUFBSSxDQUFDLGVBQWU7UUFBRSxlQUFlLEdBQUcsQ0FBQyxNQUFNLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxDQUFrQyxDQUFDO0lBQ3hILE9BQU8sZUFBZSxDQUFDO0FBQ3pCLENBQUM7QUFFRCxtRkFBbUY7QUFDbkYsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSxLQUFLLFVBQVUsT0FBTyxDQUFJLEdBQVc7SUFDbkMsT0FBTyxDQUFDLE1BQU0sTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFNLENBQUM7QUFDbEMsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F3Qkc7QUFFSCxNQUFNLEdBQUcsR0FBRztJQUNWLElBQUksRUFBRSxDQUFDLEdBQVcsRUFBRSxJQUFjLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZJLElBQUksRUFBRSxDQUFDLEdBQVcsRUFBRSxJQUFjLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFB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QUFTLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLFdBQVcsR0FBRyxJQUFJLENBQUM7SUFDbkIsb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO0FBQzNCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBTSU5HTEUtRklMRSBCWSBERVBMT1lNRU5UIENPTlNUUkFJTlQg4oCUIGRvIG5vdCBzcGxpdCB0aGlzIG1vZHVsZS5cbiAqXG4gKiBgcGlwZWxpbmUtbWFuYWdlcmAncyBgc2V0dXAtZXZlbnRzYCBjb21tYW5kIGRlcGxveXMgdGhpcyBMYW1iZGEgYnkgY29weWluZ1xuICogdGhlIGNvbXBpbGVkIGBsaWIvaW5kZXguanNgICoqYXMgb25lIGZpbGUqKiBpbnRvIHRoZSB6aXBcbiAqIChgY29tbWFuZHMvc2V0dXAtZXZlbnRzLnRzYDogYGNvcHlGaWxlU3luYyhoYW5kbGVyU3JjLCBpbmRleC5tanMpYCDihpIgYHppcCAtamApLlxuICogTm90aGluZyBidW5kbGVzIGl0LiBTbyB0aGUgbW9tZW50IHRoaXMgZmlsZSBnYWlucyBhIHJlbGF0aXZlIGltcG9ydCBvZiBhXG4gKiBzaWJsaW5nIG1vZHVsZSwgdGhlIGVtaXR0ZWQgYGxpYi9pbmRleC5qc2AgcmVmZXJlbmNlcyBhIGZpbGUgdGhhdCBpcyBub3QgaW5cbiAqIHRoZSB6aXAgYW5kIHRoZSBMYW1iZGEgZGllcyBhdCBpbml0IHdpdGggXCJDYW5ub3QgZmluZCBtb2R1bGUgJy4v4oCmJ1wiLlxuICpcbiAqIFRoYXQgbWFrZXMgdGhlIHVzdWFsIGh5Z2llbmUgaW5zdGluY3Qg4oCUIFwiOTczIGxpbmVzIGFuZCBuaW5lIG1vZHVsZS1sZXZlbFxuICogY2FjaGVzLCBzcGxpdCBpdCBpbnRvIHNjbS1yZXNvbHZlcnMgLyBwYXJzZS1yZWNvcmQgLyBkbHEtaGVhbHRoXCIg4oCUIGFcbiAqIHByb2R1Y3Rpb24gb3V0YWdlIHJhdGhlciB0aGFuIGEgY2xlYW51cC4gSXQgaXMgYWxzbyB3aHkgYF9yZXNldEZvclRlc3RzKClgXG4gKiBleGlzdHM6IHdpdGggZXZlcnkgY2FjaGUgaW4gb25lIG1vZHVsZSwgdGVzdHMgaGF2ZSBubyBvdGhlciBzZWFtLlxuICpcbiAqIElmIHRoaXMgZ2VudWluZWx5IG5lZWRzIHNwbGl0dGluZywgYWRkIGEgYnVuZGxpbmcgc3RlcCAoZXNidWlsZCDihpIgb25lXG4gKiBgbGliL2luZGV4LmpzYCkgdG8gdGhlIGJ1aWxkIEZJUlNULCBhbmQgb25seSB0aGVuIGJyZWFrIHRoZSBmaWxlIHVwLlxuICovXG5cbmltcG9ydCB7IEdldFNlY3JldFZhbHVlQ29tbWFuZCwgU2VjcmV0c01hbmFnZXJDbGllbnQgfSBmcm9tICdAYXdzLXNkay9jbGllbnQtc2VjcmV0cy1tYW5hZ2VyJztcbmltcG9ydCB0eXBlIHsgU1FTRXZlbnQsIFNRU1JlY29yZCB9IGZyb20gJ2F3cy1sYW1iZGEnO1xuXG4vLyBBV1MgTGFtYmRhJ3MgTm9kZSBydW50aW1lIGJ1bmRsZXMgQGF3cy1zZGsgdjMsIHNvIHRoZSBDb2RlUGlwZWxpbmUvQ29kZUNvbW1pdC9TUVNcbi8vIGNsaWVudHMgYXJlIGxvYWRlZCBsYXppbHkgdmlhIGR5bmFtaWMgaW1wb3J0KCkg4oCUIGEgc3RhdGljIHdvcmtzcGFjZSBkZXBlbmRlbmN5IG9uXG4vLyB0aGVtIHdvdWxkIHBlcnR1cmIgdGhlIHNoYXJlZCBAYXdzLXNkayB2ZXJzaW9uIHRyZWUgYW5kIGJyZWFrIG90aGVyIHBhY2thZ2VzJ1xuLy8gdHlwaW5ncy4gVGhlIGR5bmFtaWMgaW1wb3J0IGtlZXBzIHRoZW0gb3V0IG9mIHRoZSBzdGF0aWMgZ3JhcGggd2hpbGUgc3RheWluZ1xuLy8gRVNNLWNvcnJlY3QgKG5vIGByZXF1aXJlYCBpbiBhIHR5cGU6bW9kdWxlIHBhY2thZ2UpIGFuZCBtb2NrYWJsZS4gTWluaW1hbCBsb2NhbFxuLy8gdHlwZXMga2VlcCB0aGUgY2FsbCBzaXRlcyB0eXBlLWNoZWNrZWQuIGBAYXdzLXNkay9jbGllbnQtY29kZWNvbW1pdGAgYW5kXG4vLyBgQGF3cy1zZGsvY2xpZW50LXNxc2AgYXJlIE5PVCBkZXZEZXBlbmRlbmNpZXMgb2YgdGhpcyBwYWNrYWdlICh0aGV5IHJlc29sdmUgb25seVxuLy8gaW5zaWRlIHRoZSBMYW1iZGEgcnVudGltZSAvIGplc3QgbW9ja3MpLCBzbyB0aGV5IGFyZSBpbXBvcnRlZCB0aHJvdWdoIGFcbi8vIHZhcmlhYmxlLXNwZWNpZmllciBoZWxwZXIgYmVsb3cgc28gYHRzY2AgZG9lcyBub3QgdHJ5IHRvIHJlc29sdmUgdGhlbS5cbmludGVyZmFjZSBMaXN0VGFnc091dHB1dCB7IHRhZ3M/OiBBcnJheTx7IGtleT86IHN0cmluZzsgdmFsdWU/OiBzdHJpbmcgfT4gfVxuaW50ZXJmYWNlIENvZGVQaXBlbGluZUNsaWVudExpa2UgeyBzZW5kKGNvbW1hbmQ6IHVua25vd24pOiBQcm9taXNlPExpc3RUYWdzT3V0cHV0PiB9XG5pbnRlcmZhY2UgQ29kZVBpcGVsaW5lTW9kdWxlIHtcbiAgQ29kZVBpcGVsaW5lQ2xpZW50OiBuZXcgKGNvbmZpZzogeyByZWdpb246IHN0cmluZyB9KSA9PiBDb2RlUGlwZWxpbmVDbGllbnRMaWtlO1xuICBMaXN0VGFnc0ZvclJlc291cmNlQ29tbWFuZDogbmV3IChpbnB1dDogeyByZXNvdXJjZUFybjogc3RyaW5nIH0pID0+IHVua25vd247XG59XG5sZXQgY29kZXBpcGVsaW5lTW9kOiBDb2RlUGlwZWxpbmVNb2R1bGUgfCB1bmRlZmluZWQ7XG5hc3luYyBmdW5jdGlvbiBsb2FkQ29kZVBpcGVsaW5lKCk6IFByb21pc2U8Q29kZVBpcGVsaW5lTW9kdWxlPiB7XG4gIGlmICghY29kZXBpcGVsaW5lTW9kKSBjb2RlcGlwZWxpbmVNb2QgPSAoYXdhaXQgaW1wb3J0KCdAYXdzLXNkay9jbGllbnQtY29kZXBpcGVsaW5lJykpIGFzIHVua25vd24gYXMgQ29kZVBpcGVsaW5lTW9kdWxlO1xuICByZXR1cm4gY29kZXBpcGVsaW5lTW9kO1xufVxuXG4vLyBWYXJpYWJsZS1zcGVjaWZpZXIgbG9hZGVyIGZvciBTREsgY2xpZW50cyB0aGF0IGFyZSBOT1Qgc3RhdGljIGRldkRlcGVuZGVuY2llcyBvZlxuLy8gdGhpcyBwYWNrYWdlIChwcmVzZW50IG9ubHkgaW4gdGhlIExhbWJkYSBydW50aW1lIC8gamVzdCBtb2NrcykuIFBhc3NpbmcgdGhlXG4vLyBzcGVjaWZpZXIgYXMgYSBgc3RyaW5nYCBrZWVwcyBgdHNjYCBmcm9tIHJlc29sdmluZyB0aGUgbW9kdWxlIGF0IGJ1aWxkIHRpbWUuXG5hc3luYyBmdW5jdGlvbiBsb2FkU2RrPFQ+KHBrZzogc3RyaW5nKTogUHJvbWlzZTxUPiB7XG4gIHJldHVybiAoYXdhaXQgaW1wb3J0KHBrZykpIGFzIFQ7XG59XG5cbi8qKlxuICogUGlwZWxpbmUgZXZlbnQgaW5nZXN0aW9uIExhbWJkYSBoYW5kbGVyLlxuICpcbiAqIFJlY2VpdmVzIENvZGVQaXBlbGluZS9Db2RlQnVpbGQgZXZlbnRzIGZyb20gU1FTIChzb3VyY2VkIGJ5IEV2ZW50QnJpZGdlKSxcbiAqIHBhcnNlcyB0aGVtIGludG8gYSBub3JtYWxpemVkIGZvcm1hdCwgYW5kIFBPU1RzIHRoZW0gdG8gdGhlIHJlcG9ydGluZyBzZXJ2aWNlXG4gKiB2aWEgUExBVEZPUk1fQkFTRV9VUkwuXG4gKlxuICogQXV0aGVudGljYXRpb24gKCNOMiDigJQgc2VydmljZS1hY2NvdW50IGtleXMpOlxuICogVGhlIExhbWJkYSBob2xkcyBhbiBvcGFxdWUgU0VSVklDRS1BQ0NPVU5UIEtFWSAoYHBiX3NhX+KApmApLCBuZXZlciBhIEpXVCwgYW5kXG4gKiB0cmFkZXMgaXQgYXQgcGxhdGZvcm0ncyBgL2F1dGgvdG9rZW4vZXhjaGFuZ2VgIGZvciBhIDUtbWludXRlIHRva2VuIHBlciBiYXRjaC5cbiAqIEl0IHZlcmlmaWVzIE5PVEhJTkcgbG9jYWxseTogYW4gb3BhcXVlIGtleSBjYXJyaWVzIG5vIGNsYWltcyB0byBjaGVjaywgYW5kIHRoZVxuICogZXhjaGFuZ2UgaXMgdGhlIGF1dGhvcml0eSAoYSByZXZva2VkIGtleSBzdG9wcyB3b3JraW5nIHdpdGhpbiBvbmUgdG9rZW5cbiAqIGxpZmV0aW1lLCB3aGljaCBpcyB0aGUgZW50aXJlIHBvaW50IG9mIHRoZSBzaGFwZSkuIFRoZSBrZXkgaXMgbWludGVkIGJ5XG4gKiBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlbmAgYW5kIHJvdGF0ZWQgaW4gcGxhY2UgYnkgdGhlIHRva2VuLXJlbmV3XG4gKiBMYW1iZGEsIHNvIHRoZSBzZWNyZXQncyB2YWx1ZSBjaGFuZ2VzIHVuZGVybmVhdGggYSB3YXJtIGNvbnRhaW5lciDigJQgd2hpY2ggaXNcbiAqIHdoeSBhbiBhdXRoIGZhaWx1cmUgcmUtcmVhZHMgdGhlIFNFQ1JFVCwgbm90IGp1c3QgdGhlIHRva2VuLlxuICpcbiAqIEVudmlyb25tZW50IHZhcmlhYmxlczpcbiAqIC0gUExBVEZPUk1fQkFTRV9VUkwg4oCUIEJhc2UgVVJMIG9mIHRoZSBwbGF0Zm9ybVxuICogLSBQTEFURk9STV9BQ0NFU1NfS0VZIOKAlCB0aGUgYHBiX3NhX+KApmAga2V5IHNldCBkaXJlY3RseSAobm8gU2VjcmV0cyBNYW5hZ2VyIGNhbGwpLCBvclxuICogLSBQTEFURk9STV9TRUNSRVRfTkFNRSDigJQgU2VjcmV0cyBNYW5hZ2VyIHNlY3JldCBjb250YWluaW5nIHsgcGFzc3dvcmQ6IDxwYl9zYV8ga2V5PiB9XG4gKiAtIEVWRU5UX0RMUV9BUk4g4oCUIChvcHRpb25hbCkgb3ZlcnJpZGUgZm9yIHRoZSBkZWFkLWxldHRlciBxdWV1ZSBBUk4gdXNlZCBieSB0aGVcbiAqICAgc2VsZi1oZWFsaW5nIHJlZHJpdmU7IGRlZmF1bHRzIHRvIGA8bWFpbi1xdWV1ZS1hcm4+LWRscWAgZGVyaXZlZCBmcm9tIHRoZSBTUVNcbiAqICAgdHJpZ2dlcidzIGV2ZW50U291cmNlQVJOLlxuICovXG5cbmNvbnN0IGxvZyA9IHtcbiAgaW5mbzogKG1zZzogc3RyaW5nLCBkYXRhPzogdW5rbm93bikgPT4gY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyBsZXZlbDogJ0lORk8nLCBtZXNzYWdlOiBtc2csIGRhdGEsIHRzOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkgfSkpLFxuICB3YXJuOiAobXNnOiBzdHJpbmcsIGRhdGE/OiB1bmtub3duKSA9PiBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeSh7IGxldmVsOiAnV0FSTicsIG1lc3NhZ2U6IG1zZywgZGF0YSwgdHM6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSB9KSksXG4gIGVycm9yOiAobXNnOiBzdHJpbmcsIGRhdGE/OiB1bmtub3duKSA9PiBjb25zb2xlLmVycm9yKEpTT04uc3RyaW5naWZ5KHsgbGV2ZWw6ICdFUlJPUicsIG1lc3NhZ2U6IG1zZywgZGF0YSwgdHM6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSB9KSksXG59O1xuXG4vLyDilIDilIAgVGFnIHJlc29sdXRpb246IHBiLnBpcGVsaW5lLWlkICsgcGIuZGVwbG95cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbi8vIFBpcGVsaW5lcyBhcmUgdGFnZ2VkIGF0IENESyBzeW50aCB3aXRoOlxuLy8gICBwYi5waXBlbGluZS1pZCA9IDxwbGF0Zm9ybSBwaXBlbGluZUlkPlxuLy8gICBwYi5kZXBsb3lzICAgICA9IDxzdGFnZT46PGVudj4gcGFpcnMgam9pbmVkIGJ5ICcrJywgZS5nLlxuLy8gICAgICAgICAgICAgICAgICAgIFwiRGVwbG95LXN0ZzpzdGFnaW5nK0RlcGxveS1wcm9kOnByb2R1Y3Rpb25cIlxuLy8gV2UgcmVzb2x2ZSB0aG9zZSB0YWdzIGZyb20gdGhlIGxpdmUgQ29kZVBpcGVsaW5lIChyZWdpb24tYXdhcmUpLiBUaGUgQVJOL2FjY291bnRcbi8vIG5ldmVyIGxlYXZlIEFXUywgc28gdGhlcmUncyBubyBtYXNraW5nLiBDYWNoZWQgcGVyIEFSTiAod2FybS1jb250YWluZXIpIHRvIGF2b2lkXG4vLyBoYW1tZXJpbmcgdGhlIExpc3RUYWdzIEFQSS5cbi8vXG4vLyBSZXF1aXJlcyB0aGUgTGFtYmRhIGV4ZWN1dGlvbiByb2xlIHRvIGFsbG93IGBjb2RlcGlwZWxpbmU6TGlzdFRhZ3NGb3JSZXNvdXJjZWAuXG4vLyBBY2Nlc3NEZW5pZWQgaXMgYSBjb25maWcgZXJyb3IgKGFsZXJ0IGxvdWRseSk7IGEgbWlzc2luZyBwYi5waXBlbGluZS1pZCB0YWcgaXNcbi8vIHRyZWF0ZWQgYXMgXCJub3QgeWV0IHJlZ2lzdGVyZWRcIiAoc2tpcCkuXG5cbmNvbnN0IFBJUEVMSU5FX0lEX1RBRyA9ICdwYi5waXBlbGluZS1pZCc7XG5jb25zdCBERVBMT1lTX1RBRyA9ICdwYi5kZXBsb3lzJztcbi8vIENhbm9uaWNhbCBwZXItcGlwZWxpbmUgb3JnIHRhZyAoYXBwbGllZCBhdCBzeW50aCBieSBwaXBlbGluZS1jb3JlKS4gSXQgaXMgdGhlXG4vLyBSRVNPTFZFRCBvcmcgZm9yIGV2ZXJ5IGV2ZW50IG9mIHRoaXMgcGlwZWxpbmUg4oCUIHVzZWQgdG8ga2V5IHRoZSBwZXItb3JnIEdpdEh1YlxuLy8gdG9rZW4gYW5kIHRvIGF0dHJpYnV0ZSBpbmdlc3QtaGVhbHRoLiBvcmdJZCBpcyBhIHBsYXRmb3JtIGlkLCBuZXZlciBhbiBBV1Ncbi8vIGFjY291bnQgaWQsIHNvIGl0IGlzIHNhZmUgdG8gcmVhZC9mb3J3YXJkLlxuY29uc3QgT1JHX0lEX1RBRyA9ICdPcmdJZCc7XG5jb25zdCBjbGllbnRzQnlSZWdpb24gPSBuZXcgTWFwPHN0cmluZywgQ29kZVBpcGVsaW5lQ2xpZW50TGlrZT4oKTtcbi8vIHBpcGVsaW5lSWQ9bnVsbCBpcyBhIG5lZ2F0aXZlIGNhY2hlIChyZXNvbHZlZC1idXQtdW50YWdnZWQpOyBgdHNgIGxldHMgbmVnYXRpdmVzXG4vLyBleHBpcmUgc28gYSBwaXBlbGluZSB0YWdnZWQgQUZURVIgaXRzIGZpcnN0IGV2ZW50IGJlY29tZXMgcmVzb2x2YWJsZSB3aXRob3V0XG4vLyByZWN5Y2xpbmcgdGhlIHdhcm0gY29udGFpbmVyLiBQb3NpdGl2ZSByZXN1bHRzIHN0YXkgY2FjaGVkIGZvciB0aGUgbGlmZXRpbWUuXG5jb25zdCByZXNvbHZlZEJ5QXJuID0gbmV3IE1hcDxzdHJpbmcsIHsgcGlwZWxpbmVJZDogc3RyaW5nIHwgbnVsbDsgb3JnSWQ6IHN0cmluZyB8IG51bGw7IGRlcGxveXM6IE1hcDxzdHJpbmcsIHN0cmluZz47IHRzOiBudW1iZXIgfT4oKTtcbmNvbnN0IE5FR19DQUNIRV9UVExfTVMgPSA1ICogNjAgKiAxMDAwO1xuXG4vKiogUmVzb2x2ZWQgcGlwZWxpbmUgbWV0YWRhdGEgcmVhZCBmcm9tIHRoZSBDb2RlUGlwZWxpbmUgcmVzb3VyY2UgdGFncy4gKi9cbmludGVyZmFjZSBSZXNvbHZlZFBpcGVsaW5lIHtcbiAgLyoqIFBsYXRmb3JtIHBpcGVsaW5lSWQgKHBiLnBpcGVsaW5lLWlkIHRhZyk7IG51bGwgd2hlbiB1bnRhZ2dlZC91bnJlZ2lzdGVyZWQuICovXG4gIHBpcGVsaW5lSWQ6IHN0cmluZyB8IG51bGw7XG4gIC8qKiBQbGF0Zm9ybSBvcmdJZCAoT3JnSWQgdGFnKTsgbnVsbCB3aGVuIHRoZSBwaXBlbGluZSBjYXJyaWVzIG5vIG9yZyB0YWcuICovXG4gIG9yZ0lkOiBzdHJpbmcgfCBudWxsO1xuICAvKiogTWFwIG9mIGRlcGxveS1zdGFnZSBuYW1lIOKGkiBlbnZpcm9ubWVudCAocGFyc2VkIGZyb20gcGIuZGVwbG95cykuIEVtcHR5IHdoZW4gbm9uZS4gKi9cbiAgZGVwbG95czogTWFwPHN0cmluZywgc3RyaW5nPjtcbn1cblxuLyoqIENhcCBvbiBjb21taXQvcmVmL2Vudmlyb25tZW50IGZpZWxkcyDigJQgdGhlIGluZ2VzdCBjb250cmFjdCBhbGxvd3MgbWF4IDI1NS4gKi9cbmNvbnN0IE1BWF9BVFRSID0gMjU1O1xuXG4vKipcbiAqIFBhcnNlIHRoZSBgcGIuZGVwbG95c2AgdGFnIHZhbHVlIGludG8gYSBzdGFnZeKGkmVudmlyb25tZW50IG1hcC4gVGhlIHZhbHVlIGlzXG4gKiBgPHN0YWdlPjo8ZW52PmAgcGFpcnMgam9pbmVkIGJ5IGArYCAoQ29kZVBpcGVsaW5lLXRhZy1zYWZlOyBubyBKU09OKS4gQSBzdGFnZVxuICogcHJlc2VudCBoZXJlIGlzIGEgZGVwbG95OyBpdHMgbWFwcGVkIGVudmlyb25tZW50IGlzIHdoYXQgdGhlIGZvcndhcmRlciBzdGFtcHMgb25cbiAqIHRoYXQgc3RhZ2UncyBTVEFHRS9BQ1RJT04gZXZlbnRzIChET1JBIGRlcGxveSBhdHRyaWJ1dGlvbikuIE1hbGZvcm1lZCBwYWlycyBhcmVcbiAqIHNraXBwZWQgZGVmZW5zaXZlbHkuXG4gKi9cbmZ1bmN0aW9uIHBhcnNlRGVwbG95cyh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogTWFwPHN0cmluZywgc3RyaW5nPiB7XG4gIGNvbnN0IG1hcCA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gIGlmICghdmFsdWUpIHJldHVybiBtYXA7XG4gIGZvciAoY29uc3QgcGFpciBvZiB2YWx1ZS5zcGxpdCgnKycpKSB7XG4gICAgY29uc3QgaWR4ID0gcGFpci5pbmRleE9mKCc6Jyk7XG4gICAgaWYgKGlkeCA8PSAwKSBjb250aW51ZTtcbiAgICBjb25zdCBzdGFnZSA9IHBhaXIuc2xpY2UoMCwgaWR4KS50cmltKCk7XG4gICAgY29uc3QgZW52ID0gcGFpci5zbGljZShpZHggKyAxKS50cmltKCk7XG4gICAgaWYgKHN0YWdlICYmIGVudikgbWFwLnNldChzdGFnZSwgZW52LnNsaWNlKDAsIE1BWF9BVFRSKSk7XG4gIH1cbiAgcmV0dXJuIG1hcDtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcGlwZWxpbmVDbGllbnQocmVnaW9uOiBzdHJpbmcpOiBQcm9taXNlPENvZGVQaXBlbGluZUNsaWVudExpa2U+IHtcbiAgbGV0IGNsaWVudCA9IGNsaWVudHNCeVJlZ2lvbi5nZXQocmVnaW9uKTtcbiAgaWYgKCFjbGllbnQpIHtcbiAgICBjb25zdCB7IENvZGVQaXBlbGluZUNsaWVudCB9ID0gYXdhaXQgbG9hZENvZGVQaXBlbGluZSgpO1xuICAgIGNsaWVudCA9IG5ldyBDb2RlUGlwZWxpbmVDbGllbnQoeyByZWdpb24gfSk7XG4gICAgY2xpZW50c0J5UmVnaW9uLnNldChyZWdpb24sIGNsaWVudCk7XG4gIH1cbiAgcmV0dXJuIGNsaWVudDtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIGEgQ29kZVBpcGVsaW5lJ3MgdGFncyDihpIgdGhlIHBsYXRmb3JtIHBpcGVsaW5lSWQgKHBiLnBpcGVsaW5lLWlkKSBwbHVzIHRoZVxuICogZGVwbG95LXN0YWdl4oaSZW52aXJvbm1lbnQgbWFwIChwYi5kZXBsb3lzKS4gYHBpcGVsaW5lSWRgIGlzIG51bGwgd2hlbiB0aGUgcGlwZWxpbmVcbiAqIGhhcyBubyBwYi5waXBlbGluZS1pZCB0YWcgKHVucmVnaXN0ZXJlZCkuIFRocm93cyBvbiBBY2Nlc3NEZW5pZWQgc28gYSBtaXNzaW5nIElBTVxuICogZ3JhbnQgc3VyZmFjZXMgbG91ZGx5IGluc3RlYWQgb2Ygc2lsZW50bHkgZHJvcHBpbmcgZXZlcnkgZXZlbnQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVQaXBlbGluZShhcm46IHN0cmluZywgcmVnaW9uOiBzdHJpbmcpOiBQcm9taXNlPFJlc29sdmVkUGlwZWxpbmU+IHtcbiAgY29uc3QgY2FjaGVkID0gcmVzb2x2ZWRCeUFybi5nZXQoYXJuKTtcbiAgLy8gU2VydmUgYSBwb3NpdGl2ZSBoaXQgZm9yIHRoZSBjb250YWluZXIgbGlmZXRpbWU7IHNlcnZlIGEgbmVnYXRpdmUgaGl0IG9ubHlcbiAgLy8gdW50aWwgaXQgZXhwaXJlcyAodGhlbiByZS1yZXNvbHZlLCBpbiBjYXNlIHRoZSBwaXBlbGluZSB3YXMgc2luY2UgdGFnZ2VkKS5cbiAgaWYgKGNhY2hlZCAmJiAoY2FjaGVkLnBpcGVsaW5lSWQgIT09IG51bGwgfHwgRGF0ZS5ub3coKSAtIGNhY2hlZC50cyA8IE5FR19DQUNIRV9UVExfTVMpKSB7XG4gICAgcmV0dXJuIHsgcGlwZWxpbmVJZDogY2FjaGVkLnBpcGVsaW5lSWQsIG9yZ0lkOiBjYWNoZWQub3JnSWQsIGRlcGxveXM6IGNhY2hlZC5kZXBsb3lzIH07XG4gIH1cbiAgdHJ5IHtcbiAgICBjb25zdCBjbGllbnQgPSBhd2FpdCBwaXBlbGluZUNsaWVudChyZWdpb24pO1xuICAgIGNvbnN0IHsgTGlzdFRhZ3NGb3JSZXNvdXJjZUNvbW1hbmQgfSA9IGF3YWl0IGxvYWRDb2RlUGlwZWxpbmUoKTtcbiAgICBjb25zdCBvdXQgPSBhd2FpdCBjbGllbnQuc2VuZChuZXcgTGlzdFRhZ3NGb3JSZXNvdXJjZUNvbW1hbmQoeyByZXNvdXJjZUFybjogYXJuIH0pKTtcbiAgICBjb25zdCBwaXBlbGluZUlkID0gb3V0LnRhZ3M/LmZpbmQodCA9PiB0LmtleSA9PT0gUElQRUxJTkVfSURfVEFHKT8udmFsdWUgPz8gbnVsbDtcbiAgICBjb25zdCBvcmdJZCA9IG91dC50YWdzPy5maW5kKHQgPT4gdC5rZXkgPT09IE9SR19JRF9UQUcpPy52YWx1ZSA/PyBudWxsO1xuICAgIGNvbnN0IGRlcGxveXMgPSBwYXJzZURlcGxveXMob3V0LnRhZ3M/LmZpbmQodCA9PiB0LmtleSA9PT0gREVQTE9ZU19UQUcpPy52YWx1ZSk7XG4gICAgaWYgKCFwaXBlbGluZUlkKSBsb2cud2FybignUGlwZWxpbmUgbWlzc2luZyBwYi5waXBlbGluZS1pZCB0YWcg4oCUIHNraXBwaW5nIChyZWdpc3RlciBpdD8pJywgeyBhcm4gfSk7XG4gICAgcmVzb2x2ZWRCeUFybi5zZXQoYXJuLCB7IHBpcGVsaW5lSWQsIG9yZ0lkLCBkZXBsb3lzLCB0czogRGF0ZS5ub3coKSB9KTtcbiAgICByZXR1cm4geyBwaXBlbGluZUlkLCBvcmdJZCwgZGVwbG95cyB9O1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBjb25zdCBuYW1lID0gKGVyciBhcyB7IG5hbWU/OiBzdHJpbmcgfSk/Lm5hbWU7XG4gICAgaWYgKG5hbWUgPT09ICdBY2Nlc3NEZW5pZWRFeGNlcHRpb24nKSB7XG4gICAgICAvLyBEb24ndCBjYWNoZSDigJQgdGhpcyBpcyBhIGZpeGFibGUgbWlzY29uZmlnLCBub3QgYSBwcm9wZXJ0eSBvZiB0aGUgcGlwZWxpbmUuXG4gICAgICBsb2cuZXJyb3IoJ0FjY2Vzc0RlbmllZCBjYWxsaW5nIGNvZGVwaXBlbGluZTpMaXN0VGFnc0ZvclJlc291cmNlIOKAlCBncmFudCBpdCB0byB0aGUgTGFtYmRhIHJvbGUnLCB7IGFybiwgZXJyb3I6IG5hbWUgfSk7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICAgIC8vIFRyYW5zaWVudC9pbmZyYSBlcnJvcnMgKHRocm90dGxpbmcsIHRpbWVvdXRzLCA1eHgpIE1VU1QgcHJvcGFnYXRlIHNvIHRoZSBTUVNcbiAgICAvLyBiYXRjaCBpcyByZXRyaWVkIHJhdGhlciB0aGFuIHNpbGVudGx5IGRyb3BwaW5nIHRoZSBldmVudCDigJQgYW4gZXZlbnQgZHJvcHBlZFxuICAgIC8vIEJFRk9SRSBpdCByZWFjaGVzIHRoZSBpbmdlc3QgaXMgbG9zdCBwZXJtYW5lbnRseSAodGhlIGluZ2VzdCdzIHBhcnRpYWwtdW5pcXVlXG4gICAgLy8gaW5kZXggb25seSBkZWR1cGVzIGV2ZW50cyB0aGF0IHdlcmUgYWN0dWFsbHkgUE9TVGVkKS4gT25seSBzd2FsbG93IHdoZW4gdGhlXG4gICAgLy8gZmFpbHVyZSBpcyBhIGRlZmluaXRpdmUgYW5zd2VyIHRoYXQgdGhpcyBBUk4gaGFzIG5vIHVzYWJsZSB0YWcuXG4gICAgY29uc3QgZSA9IGVyciBhcyB7IG5hbWU/OiBzdHJpbmc7ICRyZXRyeWFibGU/OiB1bmtub3duOyAkbWV0YWRhdGE/OiB7IGh0dHBTdGF0dXNDb2RlPzogbnVtYmVyIH0gfTtcbiAgICBjb25zdCBzdGF0dXMgPSBlLiRtZXRhZGF0YT8uaHR0cFN0YXR1c0NvZGU7XG4gICAgY29uc3QgcmV0cnlhYmxlID0gZS4kcmV0cnlhYmxlICE9IG51bGxcbiAgICAgIHx8ICh0eXBlb2Ygc3RhdHVzID09PSAnbnVtYmVyJyAmJiBzdGF0dXMgPj0gNTAwKVxuICAgICAgfHwgL3Rocm90dGx8dGltZW91dHx0b29tYW55cmVxdWVzdHN8c2VydmljZXVuYXZhaWxhYmxlfGVjb25ucmVzZXR8bmV0d29ya2luZ2Vycm9yL2kudGVzdChuYW1lID8/ICcnKTtcbiAgICBpZiAocmV0cnlhYmxlKSB7XG4gICAgICBsb2cuZXJyb3IoJ1RyYW5zaWVudCBlcnJvciByZXNvbHZpbmcgcGIucGlwZWxpbmUtaWQgdGFnIOKAlCB3aWxsIHJldHJ5IHRoZSBiYXRjaCcsIHsgYXJuLCBlcnJvcjogbmFtZSA/PyBTdHJpbmcoZXJyKSB9KTtcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gICAgbG9nLmVycm9yKCdGYWlsZWQgdG8gcmVzb2x2ZSBwYi5waXBlbGluZS1pZCB0YWcnLCB7IGFybiwgZXJyb3I6IG5hbWUgPz8gU3RyaW5nKGVycikgfSk7XG4gICAgcmV0dXJuIHsgcGlwZWxpbmVJZDogbnVsbCwgb3JnSWQ6IG51bGwsIGRlcGxveXM6IG5ldyBNYXAoKSB9O1xuICB9XG59XG5cbi8vIOKUgOKUgOKUgCBBdXRoIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuLy9cbi8vIFRoZSBzdG9yZWQgY3JlZGVudGlhbCBpcyBhbiBPUEFRVUUgc2VydmljZS1hY2NvdW50IGtleSAoYHBiX3NhX+KApmApLCB0cmFkZWQgYXRcbi8vIHBsYXRmb3JtIGZvciBhIHNob3J0LWxpdmVkIEpXVC4gTm90aGluZyBpcyB2ZXJpZmllZCBoZXJlOiBhbiBvcGFxdWUga2V5IGhhcyBub1xuLy8gY2xhaW1zIGFuZCBubyBzaWduYXR1cmUsIHNvIHRoZXJlIGlzIG5vdGhpbmcgYSBsb2NhbCBjaGVjayBjb3VsZCB0ZWxsIHlvdSB0aGF0XG4vLyB0aGUgZXhjaGFuZ2UgZG9lcyBub3QgdGVsbCB5b3UgYmV0dGVyIOKAlCBhbmQgdGhlIGV4Y2hhbmdlIHJlLXJlYWRzIHRoZSBhY2NvdW50LFxuLy8gaXRzIG9yZyBhbmQgdGhlIGtleSdzIG93biBzdGF0ZSBldmVyeSB0aW1lLCB3aGljaCBpcyB3aGF0IG1ha2VzIGEgcmV2b2NhdGlvblxuLy8gdGFrZSBlZmZlY3Qgd2l0aGluIG9uZSB0b2tlbiBsaWZldGltZS5cbi8vXG4vLyBUaGlzIGNvZGUgaXMgZGVsaWJlcmF0ZWx5IHNlbGYtY29udGFpbmVkIChzZWUgdGhlIGZpbGUgaGVhZGVyOiB0aGUgTGFtYmRhIHNoaXBzXG4vLyBhcyBPTkUgZmlsZSksIHNvIGl0IGRvZXMgbm90IHVzZSBhcGktY29yZSdzIGV4Y2hhbmdlIGNsaWVudC4gSXQga2VlcHMgdGhlIHNhbWVcbi8vIGNvbnRyYWN0OiBjYWNoZSB0aGUgdG9rZW4gaW4gdGhlIHdhcm0gY29udGFpbmVyLCByZWZyZXNoIEVBUkxZIChiZWZvcmUgZXhwaXJ5KVxuLy8gc28gYSBiYXRjaCBuZXZlciBwcmVzZW50cyBhIHRva2VuIHRoYXQgZGllcyBtaWQtZmxpZ2h0LCBhbmQgZmFpbCB0aGUgYmF0Y2ggb25cbi8vIGFuIGV4Y2hhbmdlIGZhaWx1cmUgcmF0aGVyIHRoYW4gc2hpcHBpbmcgZXZlbnRzIHVuYXV0aGVudGljYXRlZC5cblxuLyoqIFJlZnJlc2ggYXQgdGhpcyBmcmFjdGlvbiBvZiB0aGUgdG9rZW4ncyBsaWZlIOKAlCBuZXZlciBhdCB0aGUgbGFzdCBtb21lbnQuICovXG5jb25zdCBUT0tFTl9SRUZSRVNIX0ZSQUNUSU9OID0gMC44O1xuLyoqIEZsb29yIG9uIHRoZSBjYWNoZWQgbGlmZXRpbWUsIHNvIGEgc3VycHJpc2luZ2x5IHNob3J0IFRUTCBjYW4ndCBjYXVzZSBhXG4gKiAgcmVmcmVzaCBzdG9ybSAob25lIGV4Y2hhbmdlIHBlciBiYXRjaCBhdCB3b3JzdCkuICovXG5jb25zdCBNSU5fVE9LRU5fQ0FDSEVfTVMgPSAzMF8wMDA7XG4vKiogUGVyLXJlcXVlc3QgdGltZW91dCBmb3IgdGhlIGV4Y2hhbmdlIGNhbGwuICovXG5jb25zdCBFWENIQU5HRV9USU1FT1VUX01TID0gNTAwMDtcblxuLyoqIEFjY2Vzcy1rZXkgcHJlZml4ZXMgcGxhdGZvcm0gaXNzdWVzLiBBIHN0b3JlZCBKV1QgaXMgTk9UIG9uZSBvZiB0aGVzZS4gKi9cbmNvbnN0IEFDQ0VTU19LRVlfUFJFRklYRVMgPSBbJ3BiX3NhXycsICdwYl9wYXRfJ107XG5cbmxldCBjYWNoZWRLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xubGV0IGNhY2hlZFRva2VuOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbmxldCBjYWNoZWRUb2tlbkV4cGlyZXNBdCA9IDA7XG5cbi8qKlxuICogUmVhZCAoYW5kIGNhY2hlKSB0aGUgc2VydmljZS1hY2NvdW50IGtleTogYFBMQVRGT1JNX0FDQ0VTU19LRVlgIGlmIHNldCxcbiAqIG90aGVyd2lzZSB0aGUgYHBhc3N3b3JkYCBmaWVsZCBvZiB0aGUgU2VjcmV0cyBNYW5hZ2VyIHNlY3JldCBuYW1lZCBieVxuICogYFBMQVRGT1JNX1NFQ1JFVF9OQU1FYC5cbiAqXG4gKiBGYWlscyBDTE9TRUQgYW5kIExPVURMWSBvbiBhIHN0b3JlZCBKV1QuIEFmdGVyIHRoZSAjTjIgY3V0b3ZlciB0aGUgc2VjcmV0IGhvbGRzXG4gKiBhIGtleSwgYW5kIGEgZGVwbG95bWVudCBzdGlsbCBjYXJyeWluZyB0aGUgb2xkIG1hY2hpbmUtc2Vzc2lvbiBKV1Qgd291bGRcbiAqIG90aGVyd2lzZSBmYWlsIGxhdGVyIGFzIGFuIG9wYXF1ZSA0MDEgZnJvbSB0aGUgcmVwb3J0aW5nIEFQSSDigJQgdGhyZWUgbGF5ZXJzXG4gKiBhd2F5IGZyb20gdGhlIHRoaW5nIGFuIG9wZXJhdG9yIGhhcyB0byBmaXguXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldEFjY2Vzc0tleSgpOiBQcm9taXNlPHN0cmluZz4ge1xuICBpZiAoY2FjaGVkS2V5KSByZXR1cm4gY2FjaGVkS2V5O1xuXG4gIGNvbnN0IGRpcmVjdCA9IHByb2Nlc3MuZW52LlBMQVRGT1JNX0FDQ0VTU19LRVk7XG4gIGlmIChkaXJlY3QpIHtcbiAgICBhc3NlcnRBY2Nlc3NLZXkoZGlyZWN0LCAnUExBVEZPUk1fQUNDRVNTX0tFWScpO1xuICAgIGNhY2hlZEtleSA9IGRpcmVjdDtcbiAgICBsb2cuaW5mbygnVXNpbmcgUExBVEZPUk1fQUNDRVNTX0tFWSBmcm9tIGVudmlyb25tZW50Jyk7XG4gICAgcmV0dXJuIGNhY2hlZEtleTtcbiAgfVxuXG4gIGNvbnN0IHNlY3JldE5hbWUgPSBwcm9jZXNzLmVudi5QTEFURk9STV9TRUNSRVRfTkFNRTtcbiAgaWYgKCFzZWNyZXROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdQTEFURk9STV9BQ0NFU1NfS0VZIG9yIFBMQVRGT1JNX1NFQ1JFVF9OQU1FIGVudmlyb25tZW50IHZhcmlhYmxlIGlzIHJlcXVpcmVkJyk7XG4gIH1cblxuICBsb2cuaW5mbyhgRmV0Y2hpbmcgYWNjZXNzIGtleSBmcm9tIFNlY3JldHMgTWFuYWdlcjogJHtzZWNyZXROYW1lfWApO1xuICBjb25zdCBjbGllbnQgPSBuZXcgU2VjcmV0c01hbmFnZXJDbGllbnQoe30pO1xuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5zZW5kKG5ldyBHZXRTZWNyZXRWYWx1ZUNvbW1hbmQoeyBTZWNyZXRJZDogc2VjcmV0TmFtZSB9KSk7XG4gIGlmICghcmVzcG9uc2UuU2VjcmV0U3RyaW5nKSB0aHJvdyBuZXcgRXJyb3IoYFNlY3JldCBcIiR7c2VjcmV0TmFtZX1cIiBpcyBlbXB0eWApO1xuXG4gIGNvbnN0IHNlY3JldCA9IEpTT04ucGFyc2UocmVzcG9uc2UuU2VjcmV0U3RyaW5nKSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICAvLyBgcGFzc3dvcmRgIHN0YXlzIHRoZSBjYW5vbmljYWwgZmllbGQgKENvZGVCdWlsZCdzIHNlY3JldHNNYW5hZ2VyQ3JlZGVudGlhbHNcbiAgLy8gYW5kIHRoZSBwbHVnaW4tbG9va3VwIExhbWJkYSByZWFkIHRoZSBzYW1lIG9uZSkg4oCUIG9ubHkgaXRzIFZBTFVFIGNoYW5nZWQsXG4gIC8vIGZyb20gYSBKV1QgdG8gYW4gb3BhcXVlIGtleS5cbiAgY29uc3Qga2V5ID0gc2VjcmV0LnBhc3N3b3JkO1xuICBpZiAoIWtleSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgU2VjcmV0IFwiJHtzZWNyZXROYW1lfVwiIG1pc3NpbmcgcGFzc3dvcmQg4oCUIHJ1biBcInBpcGVsaW5lLW1hbmFnZXIgaW5mcmEgc3RvcmUtdG9rZW5cIiB0byBwcm92aXNpb24gYSBzZXJ2aWNlLWFjY291bnQga2V5YCk7XG4gIH1cbiAgYXNzZXJ0QWNjZXNzS2V5KGtleSwgYHNlY3JldCBcIiR7c2VjcmV0TmFtZX1cImApO1xuXG4gIGNhY2hlZEtleSA9IGtleTtcbiAgbG9nLmluZm8oJ1VzaW5nIHN0b3JlZCBzZXJ2aWNlLWFjY291bnQga2V5IGZyb20gU2VjcmV0cyBNYW5hZ2VyJyk7XG4gIHJldHVybiBjYWNoZWRLZXk7XG59XG5cbi8qKiBSZWZ1c2UgYW55dGhpbmcgdGhhdCBpcyBub3QgYW4gb3BhcXVlIGFjY2VzcyBrZXksIG5hbWluZyB3aGVyZSBpdCBjYW1lIGZyb20uICovXG5mdW5jdGlvbiBhc3NlcnRBY2Nlc3NLZXkodmFsdWU6IHN0cmluZywgc291cmNlOiBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKEFDQ0VTU19LRVlfUFJFRklYRVMuc29tZSgocCkgPT4gdmFsdWUuc3RhcnRzV2l0aChwKSkpIHJldHVybjtcbiAgY29uc3QgbG9va3NMaWtlSnd0ID0gdmFsdWUuc3BsaXQoJy4nKS5sZW5ndGggPT09IDM7XG4gIHRocm93IG5ldyBFcnJvcihcbiAgICBgJHtzb3VyY2V9IGRvZXMgbm90IGhvbGQgYW4gb3BhcXVlIGFjY2VzcyBrZXkgKGV4cGVjdGVkIGEgXCJwYl9zYV/igKZcIiB2YWx1ZSlgXG4gICAgKyAobG9va3NMaWtlSnd0XG4gICAgICA/ICcg4oCUIGl0IHN0aWxsIGhvbGRzIGEgSldUIGZyb20gYmVmb3JlIHRoZSBzZXJ2aWNlLWFjY291bnQgY3V0b3Zlci4gUmUtcnVuIFwicGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlblwiIHRvIHJlaXNzdWUgaXQgYXMgYSBrZXkuJ1xuICAgICAgOiAnLicpLFxuICApO1xufVxuXG4vKipcbiAqIFRyYWRlIHRoZSBzdG9yZWQga2V5IGZvciBhIHNob3J0LWxpdmVkIHBsYXRmb3JtIHRva2VuLCBjYWNoZWQgcGVyIHdhcm1cbiAqIGNvbnRhaW5lciB1bnRpbCBpdCBpcyA4MCUgdGhyb3VnaCBpdHMgbGlmZS4gVGhyb3dzIG9uIGFueSBmYWlsdXJlLCB3aGljaCBmYWlsc1xuICogdGhlIGJhdGNoIHNvIFNRUyByZXRyaWVzIGl0IOKAlCBhbiB1bmF1dGhlbnRpY2F0ZWQgc2hpcCBpcyBuZXZlciBhbiBvcHRpb24uXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldEF1dGhUb2tlbigpOiBQcm9taXNlPHN0cmluZz4ge1xuICBpZiAoY2FjaGVkVG9rZW4gJiYgRGF0ZS5ub3coKSA8IGNhY2hlZFRva2VuRXhwaXJlc0F0KSByZXR1cm4gY2FjaGVkVG9rZW47XG5cbiAgY29uc3QgYmFzZVVybCA9IChwcm9jZXNzLmVudi5QTEFURk9STV9CQVNFX1VSTCB8fCAnJykucmVwbGFjZSgvXFwvKyQvLCAnJyk7XG4gIGlmICghYmFzZVVybCkgdGhyb3cgbmV3IEVycm9yKCdQTEFURk9STV9CQVNFX1VSTCBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZCcpO1xuICBjb25zdCBrZXkgPSBhd2FpdCBnZXRBY2Nlc3NLZXkoKTtcblxuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCBFWENIQU5HRV9USU1FT1VUX01TKTtcbiAgbGV0IHJlczogUmVzcG9uc2U7XG4gIHRyeSB7XG4gICAgcmVzID0gYXdhaXQgZmV0Y2goYCR7YmFzZVVybH0vYXBpL2F1dGgvdG9rZW4vZXhjaGFuZ2VgLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBrZXkgfSksXG4gICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pO1xuICB9IGZpbmFsbHkge1xuICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gIH1cblxuICBpZiAoIXJlcy5vaykge1xuICAgIC8vIEEgcmVqZWN0ZWQga2V5IGlzIHRoZSBvbmUgZmFpbHVyZSBhbiBvcGVyYXRvciBoYXMgdG8gYWN0IG9uLCBhbmQgcGxhdGZvcm1cbiAgICAvLyBkZWxpYmVyYXRlbHkgYW5zd2VycyBldmVyeSByZWZ1c2FsIHRoZSBzYW1lIHdheSDigJQgc28gc2F5IHdoYXQgdG8gY2hlY2tcbiAgICAvLyByYXRoZXIgdGhhbiBlY2hvaW5nIGFuIHVuZGlmZmVyZW50aWF0ZWQgNDAxLlxuICAgIGlmIChyZXMuc3RhdHVzID09PSA0MDEgfHwgcmVzLnN0YXR1cyA9PT0gNDAzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBBY2Nlc3Mta2V5IGV4Y2hhbmdlIHJlZnVzZWQgKCR7cmVzLnN0YXR1c30pIOKAlCB0aGUgc3RvcmVkIGtleSBpcyB1bmtub3duLCByZXZva2VkLCBleHBpcmVkLCBgXG4gICAgICAgICsgJ2l0cyBzZXJ2aWNlIGFjY291bnQgaXMgZGlzYWJsZWQsIG9yIHRoaXMgYWRkcmVzcyBpcyBvdXRzaWRlIHRoZSBrZXnigJlzIElQIGFsbG93bGlzdCcsXG4gICAgICApO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYEFjY2Vzcy1rZXkgZXhjaGFuZ2UgZmFpbGVkOiAke3Jlcy5zdGF0dXN9YCk7XG4gIH1cblxuICBjb25zdCBib2R5ID0gYXdhaXQgcmVzLmpzb24oKS5jYXRjaCgoKSA9PiAoe30pKSBhcyB7IGRhdGE/OiB7IGFjY2Vzc1Rva2VuPzogc3RyaW5nOyBleHBpcmVzSW4/OiBudW1iZXIgfSB9O1xuICBjb25zdCBhY2Nlc3NUb2tlbiA9IGJvZHkuZGF0YT8uYWNjZXNzVG9rZW47XG4gIGlmICghYWNjZXNzVG9rZW4pIHRocm93IG5ldyBFcnJvcignQWNjZXNzLWtleSBleGNoYW5nZSByZXR1cm5lZCBubyBhY2Nlc3MgdG9rZW4nKTtcblxuICBjb25zdCB0dGxNcyA9ICh0eXBlb2YgYm9keS5kYXRhPy5leHBpcmVzSW4gPT09ICdudW1iZXInICYmIGJvZHkuZGF0YS5leHBpcmVzSW4gPiAwID8gYm9keS5kYXRhLmV4cGlyZXNJbiA6IDMwMCkgKiAxMDAwO1xuICBjYWNoZWRUb2tlbiA9IGFjY2Vzc1Rva2VuO1xuICBjYWNoZWRUb2tlbkV4cGlyZXNBdCA9IERhdGUubm93KCkgKyBNYXRoLm1heChNSU5fVE9LRU5fQ0FDSEVfTVMsIHR0bE1zICogVE9LRU5fUkVGUkVTSF9GUkFDVElPTik7XG4gIGxvZy5pbmZvKCdFeGNoYW5nZWQgdGhlIHN0b3JlZCBzZXJ2aWNlLWFjY291bnQga2V5IGZvciBhIHBsYXRmb3JtIHRva2VuJyk7XG4gIHJldHVybiBhY2Nlc3NUb2tlbjtcbn1cblxuLyoqXG4gKiBEcm9wIGV2ZXJ5dGhpbmcgdGhlIGNvbnRhaW5lciBpcyBob2xkaW5nIGFib3V0IGl0cyBjcmVkZW50aWFsIGFmdGVyIGEgNDAxLzQwM1xuICogZnJvbSB0aGUgQVBJLiBUaGUgdG9rZW4gaXMgZHJvcHBlZCBmb3IgdGhlIG9idmlvdXMgcmVhc29uOyB0aGUgS0VZIGlzIGRyb3BwZWRcbiAqIGJlY2F1c2UgdGhlIHRva2VuLXJlbmV3IExhbWJkYSByb3RhdGVzIHRoZSBzZWNyZXQgdW5kZXJuZWF0aCB1cyDigJQgYSB3YXJtXG4gKiBjb250YWluZXIgdGhhdCBvbmx5IHJlLWV4Y2hhbmdlZCB3b3VsZCBrZWVwIHByZXNlbnRpbmcgdGhlIHJldGlyZWQga2V5XG4gKiBmb3JldmVyLiBUaGUgbmV4dCBjYWxsIHJlLXJlYWRzIHRoZSBzZWNyZXQgYW5kIHBpY2tzIHVwIHRoZSByZXBsYWNlbWVudC5cbiAqL1xuZnVuY3Rpb24gaW52YWxpZGF0ZUNyZWRlbnRpYWwoKTogdm9pZCB7XG4gIGNhY2hlZFRva2VuID0gbnVsbDtcbiAgY2FjaGVkVG9rZW5FeHBpcmVzQXQgPSAwO1xuICAvLyBPbmx5IHRoZSBTRUNSRVQgcGF0aCBjYW4gYmUgcmVmcmVzaGVkOyBhbiBlbnYtcHJvdmlkZWQga2V5IGNhbm5vdCBjaGFuZ2VcbiAgLy8gd2l0aG91dCBhIG5ldyBjb250YWluZXIsIHNvIGtlZXAgaXQgcmF0aGVyIHRoYW4gcmUtcmVhZGluZyBhbiBlbnYgdmFyLlxuICBpZiAoIXByb2Nlc3MuZW52LlBMQVRGT1JNX0FDQ0VTU19LRVkpIGNhY2hlZEtleSA9IG51bGw7XG59XG5cbi8vIOKUgOKUgOKUgCBQaGFzZSA0OiBpbi1hY2NvdW50IGNvbW1pdC1yYW5nZSByZXNvbHV0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuLy8gRm9yIGEgc291cmNlL2RlcGxveSBldmVudCBjYXJyeWluZyBhIGNvbW1pdFNoYSB3ZSByZXNvbHZlIHRoZSBjb21taXQgdGltZXN0YW1wXG4vLyAoYW5kIGhvdyBtYW55IGNvbW1pdHMgc2hpcHBlZCkgZm9yIHRoZSByYW5nZSBzaW5jZSB0aGUgbGFzdCByZXNvbHZlZCBzaGEgb2YgdGhlXG4vLyBzYW1lIHBpcGVsaW5lIOKAlCBlbnRpcmVseSBJTi1BQ0NPVU5ULCBwZXIgc291cmNlIHR5cGU6XG4vLyAgIOKAoiBDb2RlQ29tbWl0ICDihpIgQVdTIFNESyBHZXRDb21taXQgKHdhbGsgZmlyc3QtcGFyZW50cyBiYWNrIHRvIHRoZSBib3VuZGFyeSkuXG4vLyAgIOKAoiBHaXRIdWIvQ29kZUNvbm5lY3Rpb25zLUdpdEh1YiDihpIgR2l0SHViIFJFU1QgY29tcGFyZS9jb21taXRzIEFQSS4gQXV0aGVudGljYXRlZFxuLy8gICAgIHdpdGggdGhlIG9yZydzIGBwaXBlbGluZS1idWlsZGVyL3tvcmdJZH0vZ2l0aHViLXRva2VuYCBzZWNyZXQgd2hlbiBwcmVzZW50XG4vLyAgICAgKGRlcml2ZWQgZnJvbSBQTEFURk9STV9TRUNSRVRfTkFNRSksIG90aGVyd2lzZSBiZXN0LWVmZm9ydCB1bmF1dGhlbnRpY2F0ZWQuXG4vLyAgIOKAoiBCaXRidWNrZXQgICDihpIgQml0YnVja2V0IFJFU1QgY29tbWl0IEFQSSAoc2luZ2xlLWNvbW1pdDsgcmFuZ2Ugd2FsayBvbWl0dGVkKS5cbi8vIFRoZSBvbGRlc3QgdW5zaGlwcGVkIGNvbW1pdCdzIHRpbWVzdGFtcCDihpIgYGNvbW1pdFRpbWVzdGFtcGA7IHRoZSBudW1iZXIgb2YgY29tbWl0c1xuLy8gaW4gdGhlIHJhbmdlIOKGkiBgY29tbWl0Q291bnRgLiBFdmVyeXRoaW5nIGhlcmUgaXMgQkVTVC1FRkZPUlQ6IG9uIGFueSBmYWlsdXJlIHRoZVxuLy8gZmllbGRzIGFyZSBzaW1wbHkgb21pdHRlZCAocmVwb3J0aW5nIGZhbGxzIGJhY2sgdG8gYHVua25vd25gKS4gSXQgbXVzdCBORVZFUiBmYWlsXG4vLyB0aGUgYmF0Y2guXG4vL1xuLy8gSUFNIHJlcXVpcmVkIGJ5IHRoaXMgcGF0aCAoZG9jdW1lbnQgb25seSDigJQgZ3JhbnRlZCBvbiB0aGUgZXZlbnRzIHN0YWNrKTpcbi8vICAg4oCiIGNvZGVjb21taXQ6R2V0Q29tbWl0ICAgICAgICAgICAgKENvZGVDb21taXQgc291cmNlcylcbi8vICAg4oCiIHNlY3JldHNtYW5hZ2VyOkdldFNlY3JldFZhbHVlIG9uIGBwaXBlbGluZS1idWlsZGVyLyovZ2l0aHViLXRva2VuYCAodG9rZW4gU0NNIHNvdXJjZXMpXG5cbnR5cGUgU291cmNlUHJvdmlkZXIgPSAnZ2l0aHViJyB8ICdiaXRidWNrZXQnIHwgJ2NvZGVjb21taXQnIHwgJ3Vua25vd24nO1xuaW50ZXJmYWNlIFNvdXJjZVJlZiB7XG4gIHNoYTogc3RyaW5nO1xuICBwcm92aWRlcjogU291cmNlUHJvdmlkZXI7XG4gIG93bmVyPzogc3RyaW5nO1xuICByZXBvPzogc3RyaW5nO1xuICByZXBvc2l0b3J5TmFtZT86IHN0cmluZztcbn1cbmludGVyZmFjZSBDb21taXRJbmZvIHsgY29tbWl0VGltZXN0YW1wPzogc3RyaW5nOyBjb21taXRDb3VudD86IG51bWJlciB9XG5cbi8vIFBlci0ocGlwZWxpbmUsc2hhKSByZXNvbHZlZCByZXN1bHQg4oCUIGNvbXB1dGVkIG9uY2UsIHJldXNlZCBhY3Jvc3MgdGhlIG1hbnkgZXZlbnRzXG4vLyAoUElQRUxJTkUvU1RBR0UvQUNUSU9OKSB0aGF0IHNoYXJlIGEgc291cmNlIHJldmlzaW9uIGFuZCBhY3Jvc3MgcmVkZWxpdmVyaWVzLlxuY29uc3QgY29tbWl0UmVzdWx0Q2FjaGUgPSBuZXcgTWFwPHN0cmluZywgQ29tbWl0SW5mbz4oKTtcbi8vIExhc3Qgc2hhIHdlIHN1Y2Nlc3NmdWxseSByZXNvbHZlZCBwZXIgcGlwZWxpbmUg4oCUIHRoZSBleGNsdXNpdmUgbG93ZXIgYm91bmQgZm9yIHRoZVxuLy8gbmV4dCByYW5nZSAoXCJjb21taXRzIHNpbmNlIHRoZSBsYXN0IGRlcGxveVwiKS4gSW4tbWVtb3J5IG9ubHk6IGEgY29sZCBzdGFydCByZXNldHNcbi8vIGl0LCBzbyB0aGUgZmlyc3QgcG9zdC1jb2xkIGV2ZW50IHJlc29sdmVzIGFzIGEgc2luZ2xlIGNvbW1pdCAoZXhwZWN0ZWQsIGRlZmVuc2l2ZSkuXG5jb25zdCBsYXN0U2hhQnlQaXBlbGluZSA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+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+IHtcbiAgY29uc3QgYmFzZVVybCA9IHByb2Nlc3MuZW52LlBMQVRGT1JNX0JBU0VfVVJMO1xuICBpZiAoIWJhc2VVcmwpIHRocm93IG5ldyBFcnJvcignUExBVEZPUk1fQkFTRV9VUkwgZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgcmVxdWlyZWQnKTtcblxuICAvLyBQYXJzZSArIHJlc29sdmUgYWxsIHJlY29yZHMgKHRhZyArIGNvbW1pdCBsb29rdXBzIHJ1biBjb25jdXJyZW50bHkpOyBkcm9wXG4gIC8vIGludGVudGlvbmFsIHNraXBzIChudWxsKS4gQSBtYWxmb3JtZWQgcmVjb3JkIEJPRFkgaXMgaGFuZGxlZCBpbnNpZGUgcGFyc2VSZWNvcmRcbiAgLy8gKGxvZ2dlZCArIHNraXBwZWQpIHNvIG9uZSBiYWQgbWVzc2FnZSBjYW4ndCBmYWlsIHRoZSB3aG9sZSBiYXRjaCDigJQgd2hpbGUgZ2VudWluZVxuICAvLyBpbmZyYSBlcnJvcnMgKGUuZy4gQWNjZXNzRGVuaWVkIG9uIHRoZSB0YWcgbG9va3VwKSBzdGlsbCBwcm9wYWdhdGUgc28gYSBtaXNzaW5nXG4gIC8vIElBTSBncmFudCBzdXJmYWNlcyBhbmQgU1FTIHJldHJpZXMgdGhlIGJhdGNoLlxuICBjb25zdCBldmVudHMgPSAoYXdhaXQgUHJvbWlzZS5hbGwoZXZlbnQuUmVjb3Jkcy5tYXAocGFyc2VSZWNvcmQpKSlcbiAgICAuZmlsdGVyKChlKTogZSBpcyBQYXJzZWRFdmVudCA9PiBlICE9PSBudWxsKTtcbiAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApIHtcbiAgICBsb2cuaW5mbygnTm8gcmVzb2x2YWJsZSBDb2RlUGlwZWxpbmUgZXZlbnRzIGluIGJhdGNoJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gUE9TVCBiYXRjaCB0byByZXBvcnRpbmcgc2VydmljZS4gT24gYSA0MDEvNDAzIHRoZSBjcmVkZW50aWFsIHRoaXMgY29udGFpbmVyXG4gIC8vIGhvbGRzIGlzIHN0YWxlIOKAlCBtb3N0IG9mdGVuIGJlY2F1c2UgdGhlIHRva2VuLXJlbmV3IExhbWJkYSByb3RhdGVkIHRoZSBrZXkgaW5cbiAgLy8gU2VjcmV0cyBNYW5hZ2VyIOKAlCBzbyBkcm9wIEJPVEggdGhlIGNhY2hlZCB0b2tlbiBhbmQgdGhlIGNhY2hlZCBrZXksIHJlLXJlYWQsXG4gIC8vIGFuZCByZXRyeSBPTkNFLiBgb3JnSWRgIGlzIElOVEVSTkFMICh0b2tlbi9oZWFsdGgga2V5aW5nKSDigJQgc3RyaXAgaXQgaGVyZSBzb1xuICAvLyBpdCBuZXZlciByaWRlcyB0aGUgd2lyZTsgdGhlIGluZ2VzdCByZXNvbHZlcyBvcmcgZnJvbSB0aGUgcGlwZWxpbmUgcmVnaXN0cnksXG4gIC8vIG5vdCB0aGUgYm9keS5cbiAgY29uc3QgcGF5bG9hZEV2ZW50cyA9IGV2ZW50cy5tYXAoKHsgb3JnSWQ6IF9vcmdJZCwgLi4ucmVzdCB9KSA9PiByZXN0KTtcbiAgY29uc3QgcG9zdCA9ICh0b2tlbjogc3RyaW5nKSA9PiBmZXRjaChgJHtiYXNlVXJsfS9hcGkvcmVwb3J0cy9ldmVudHNgLCB7XG4gICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLCAnQXV0aG9yaXphdGlvbic6IGBCZWFyZXIgJHt0b2tlbn1gIH0sXG4gICAgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBldmVudHM6IHBheWxvYWRFdmVudHMgfSksXG4gIH0pO1xuICBsZXQgcmVzID0gYXdhaXQgcG9zdChhd2FpdCBnZXRBdXRoVG9rZW4oKSk7XG4gIGlmIChyZXMuc3RhdHVzID09PSA0MDEgfHwgcmVzLnN0YXR1cyA9PT0gNDAzKSB7XG4gICAgbG9nLndhcm4oJ1JlcG9ydGluZyBBUEkgYXV0aCBmYWlsZWQ7IHJlLXJlYWRpbmcgdGhlIHN0b3JlZCBrZXkgYW5kIHJldHJ5aW5nIG9uY2UnLCB7IHN0YXR1czogcmVzLnN0YXR1cyB9KTtcbiAgICBpbnZhbGlkYXRlQ3JlZGVudGlhbCgpO1xuICAgIHJlcyA9IGF3YWl0IHBvc3QoYXdhaXQgZ2V0QXV0aFRva2VuKCkpO1xuICB9XG5cbiAgaWYgKCFyZXMub2spIHtcbiAgICBjb25zdCBib2R5ID0gYXdhaXQgcmVzLnRleHQoKS5jYXRjaCgoKSA9PiAnJyk7XG4gICAgbG9nLmVycm9yKGBSZXBvcnRpbmcgQVBJIHJldHVybmVkICR7cmVzLnN0YXR1c31gLCB7IGJvZHkgfSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBSZXBvcnRpbmcgQVBJIGZhaWxlZDogJHtyZXMuc3RhdHVzfWApO1xuICB9XG5cbiAgLy8gVGhlIGluc2VydCBhbHJlYWR5IHN1Y2NlZWRlZCAoMnh4KS4gVGhlIHBhcnNlZCBib2R5IGlzIG9ubHkgdXNlZCBmb3IgYSBsb2dcbiAgLy8gbGluZSwgc28gYSBub24tSlNPTi9lbXB0eSBib2R5IG11c3Qgbm90IHRocm93IGFuZCBmYWlsIHRoZSB3aG9sZSBiYXRjaCDigJRcbiAgLy8gdGhhdCB3b3VsZCB0cmlnZ2VyIGFuIFNRUyByZWRlbGl2ZXJ5IGFuZCByZS1QT1NUIChkdXBsaWNhdGUpIGFscmVhZHktaW5zZXJ0ZWRcbiAgLy8gZXZlbnRzLiBUaGUgaW5nZXN0J3MgcGFydGlhbC11bmlxdWUgaW5kZXggZGVkdXBlcywgYnV0IGF2b2lkIHRoZSBjaHVybiBhbnl3YXkuXG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJlcy5qc29uKCkuY2F0Y2goKCkgPT4gKHt9KSkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gIGxvZy5pbmZvKGBJbmdlc3RlZCAke2V2ZW50cy5sZW5ndGh9IGV2ZW50c2AsIHsgaW5zZXJ0ZWQ6IHJlc3VsdC5kYXRhIH0pO1xuXG4gIC8vIEFjY3VtdWxhdGUgZGVsaXZlcnktaGVhbHRoIGNvdW50ZXJzIFBFUiBSRVNPTFZFRCBPUkcsIHRoZW4gcnVuIHRoZSB0aHJvdHRsZWRcbiAgLy8gcG9zdC1zdWNjZXNzIGhlYWx0aCArIHNlbGYtaGVhbGluZyBETFEgcmVkcml2ZSAoYmVzdC1lZmZvcnQsIG5ldmVyIGZhaWxzIGJhdGNoKS5cbiAgZm9yIChjb25zdCBlIG9mIGV2ZW50cykge1xuICAgIGNvbnN0IGtleSA9IGUub3JnSWQgPz8gJyc7XG4gICAgZm9yd2FyZGVkQnlPcmcuc2V0KGtleSwgKGZvcndhcmRlZEJ5T3JnLmdldChrZXkpID8/IDApICsgMSk7XG4gICAgY29uc3QgYXQgPSBlLmNvbXBsZXRlZEF0IHx8IGUuc3RhcnRlZEF0O1xuICAgIGlmIChhdCkge1xuICAgICAgY29uc3QgcHJldiA9IGxhc3RFdmVudEF0QnlPcmcuZ2V0KGtleSk7XG4gICAgICBpZiAoIXByZXYgfHwgYXQgPiBwcmV2KSBsYXN0RXZlbnRBdEJ5T3JnLnNldChrZXksIGF0KTtcbiAgICB9XG4gIH1cbiAgYXdhaXQgcmVwb3J0SGVhbHRoQW5kUmVkcml2ZShiYXNlVXJsLCBldmVudC5SZWNvcmRzWzBdPy5ldmVudFNvdXJjZUFSTik7XG59O1xuXG4vKipcbiAqIEBpbnRlcm5hbCBUZXN0LW9ubHk6IHJlc2V0IGFsbCBtb2R1bGUtbGV2ZWwgY2FjaGVzL3Rocm90dGxlIHN0YXRlIHNvIHRlc3RzIGFyZVxuICogaXNvbGF0ZWQgZnJvbSBvbmUgYW5vdGhlci4gU3RyaXBwZWQgZnJvbSB0aGUgcHVibGlzaGVkIC5kLnRzIChzdHJpcEludGVybmFsKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIF9yZXNldEZvclRlc3RzKCk6IHZvaWQge1xuICByZXNvbHZlZEJ5QXJuLmNsZWFyKCk7XG4gIGNvbW1pdFJlc3VsdENhY2hlLmNsZWFyKCk7XG4gIGxhc3RTaGFCeVBpcGVsaW5lLmNsZWFyKCk7XG4gIGNvbW1pdE1ldGFDYWNoZS5jbGVhcigpO1xuICBzY21Db29sZG93blVudGlsID0gMDtcbiAgZ2l0aHViVG9rZW5CeU9yZy5jbGVhcigpO1xuICBmb3J3YXJkZWRCeU9yZy5jbGVhcigpO1xuICBsYXN0RXZlbnRBdEJ5T3JnLmNsZWFyKCk7XG4gIGxhc3RIZWFsdGhBdCA9IDA7XG4gIGxhc3RSZWRyaXZlQXQgPSAwO1xuICBjYWNoZWRLZXkgPSBudWxsO1xuICBjYWNoZWRUb2tlbiA9IG51bGw7XG4gIGNhY2hlZFRva2VuRXhwaXJlc0F0ID0gMDtcbn1cbiJdfQ==
|