velocious 1.0.576 → 1.0.577
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/README.md +46 -1
- package/build/background-jobs/main.js +13 -2
- package/build/background-jobs/scheduler.js +11 -7
- package/build/background-jobs/store.js +18 -13
- package/build/environment-handlers/node/cli/commands/test.js +2 -0
- package/build/environment-handlers/node/source-peer-package.js +172 -0
- package/build/routes/resolver.js +3 -50
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +13 -3
- package/build/src/background-jobs/scheduler.d.ts +2 -2
- package/build/src/background-jobs/scheduler.d.ts.map +1 -1
- package/build/src/background-jobs/scheduler.js +11 -8
- package/build/src/background-jobs/store.d.ts +5 -4
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +17 -12
- package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/test.js +3 -1
- package/build/src/environment-handlers/node/source-peer-package.d.ts +46 -0
- package/build/src/environment-handlers/node/source-peer-package.d.ts.map +1 -0
- package/build/src/environment-handlers/node/source-peer-package.js +148 -0
- package/build/src/routes/resolver.d.ts.map +1 -1
- package/build/src/routes/resolver.js +4 -45
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/scripts/run-tests.js +61 -20
- package/scripts/test-browser.js +6 -1
- package/src/background-jobs/main.js +13 -2
- package/src/background-jobs/scheduler.js +11 -7
- package/src/background-jobs/store.js +18 -13
- package/src/environment-handlers/node/cli/commands/test.js +2 -0
- package/src/environment-handlers/node/source-peer-package.js +172 -0
- package/src/routes/resolver.js +3 -50
- package/build/deployment-api/controller.js +0 -437
- package/build/deployment-api/index.js +0 -210
- package/build/deployment-api/path-matcher.js +0 -45
- package/build/deployment-api/registry.js +0 -84
- package/build/deployment-api/run-store.js +0 -798
- package/build/deployment-api/sanitize.js +0 -114
- package/build/src/deployment-api/controller.d.ts +0 -117
- package/build/src/deployment-api/controller.d.ts.map +0 -1
- package/build/src/deployment-api/controller.js +0 -384
- package/build/src/deployment-api/index.d.ts +0 -46
- package/build/src/deployment-api/index.d.ts.map +0 -1
- package/build/src/deployment-api/index.js +0 -178
- package/build/src/deployment-api/path-matcher.d.ts +0 -31
- package/build/src/deployment-api/path-matcher.d.ts.map +0 -1
- package/build/src/deployment-api/path-matcher.js +0 -39
- package/build/src/deployment-api/registry.d.ts +0 -106
- package/build/src/deployment-api/registry.d.ts.map +0 -1
- package/build/src/deployment-api/registry.js +0 -74
- package/build/src/deployment-api/run-store.d.ts +0 -402
- package/build/src/deployment-api/run-store.d.ts.map +0 -1
- package/build/src/deployment-api/run-store.js +0 -711
- package/build/src/deployment-api/sanitize.d.ts +0 -27
- package/build/src/deployment-api/sanitize.d.ts.map +0 -1
- package/build/src/deployment-api/sanitize.js +0 -100
- package/src/deployment-api/controller.js +0 -437
- package/src/deployment-api/index.js +0 -210
- package/src/deployment-api/path-matcher.js +0 -45
- package/src/deployment-api/registry.js +0 -84
- package/src/deployment-api/run-store.js +0 -798
- package/src/deployment-api/sanitize.js +0 -114
|
@@ -1,711 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import TableData from "../database/table-data/index.js";
|
|
3
|
-
import TableIndex from "../database/table-data/table-index.js";
|
|
4
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
-
/**
|
|
6
|
-
* DeploymentRunRow type.
|
|
7
|
-
* @typedef {object} DeploymentRunRow
|
|
8
|
-
* @property {string} id - Run id (UUID).
|
|
9
|
-
* @property {string} mountIdentifier - Stable identifier of the authenticated API mount that owns the run.
|
|
10
|
-
* @property {string} project - Allowlisted project identifier.
|
|
11
|
-
* @property {string} stage - Allowlisted stage identifier.
|
|
12
|
-
* @property {string} revision - Full immutable requested Git revision.
|
|
13
|
-
* @property {string} idempotencyKey - Caller-supplied idempotency key.
|
|
14
|
-
* @property {string} status - Run status: pending, running, succeeded, failed, interrupted, or reconciliation_required.
|
|
15
|
-
* @property {?} result - Sanitized adapter report, or null while unset.
|
|
16
|
-
* @property {?} error - Sanitized failure payload, or null while unset.
|
|
17
|
-
* @property {number} requestedAtMs - Creation time in ms epoch.
|
|
18
|
-
* @property {number | null} startedAtMs - Execution start in ms epoch.
|
|
19
|
-
* @property {number | null} finishedAtMs - Terminal time in ms epoch.
|
|
20
|
-
* @property {string | null} ownerToken - Ownership token of the executing process.
|
|
21
|
-
* @property {number | null} heartbeatAtMs - Last ownership lease heartbeat in ms epoch.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* DeploymentAuditEventRow type.
|
|
25
|
-
* @typedef {object} DeploymentAuditEventRow
|
|
26
|
-
* @property {string} id - Event id (UUID).
|
|
27
|
-
* @property {string | null} runId - Owning run id.
|
|
28
|
-
* @property {string} event - Event name (e.g. run_requested, run_started, run_succeeded, run_failed).
|
|
29
|
-
* @property {?} payload - Sanitized JSON payload.
|
|
30
|
-
* @property {number} createdAtMs - Creation time in ms epoch.
|
|
31
|
-
*/
|
|
32
|
-
/**
|
|
33
|
-
* CreateRunOutcome type.
|
|
34
|
-
* @typedef {object} CreateRunOutcome
|
|
35
|
-
* @property {"created" | "replay" | "conflict" | "in_progress" | "reconciliation_required"} outcome - What happened.
|
|
36
|
-
* @property {DeploymentRunRow | null} run - The created, replayed, conflicting, or active run; null when the deployment lock was held mid-creation by another process.
|
|
37
|
-
*/
|
|
38
|
-
const RUNS_TABLE = "velocious_deployment_runs";
|
|
39
|
-
const AUDIT_TABLE = "velocious_deployment_api_audit_events";
|
|
40
|
-
const BLOCKING_STATUSES = ["pending", "running", "reconciliation_required"];
|
|
41
|
-
const DEFAULT_STALE_RUN_TIMEOUT_MS = 60000;
|
|
42
|
-
const IDEMPOTENCY_INDEX_NAME = "index_deployment_runs_on_mount_and_key";
|
|
43
|
-
const LEGACY_UNSCOPED_MOUNT_IDENTIFIER = "legacy-unscoped";
|
|
44
|
-
const SCHEMA_LOCK_NAME = "velocious-deployment-api:schema-v2";
|
|
45
|
-
/**
|
|
46
|
-
* Ownership token identifying runs executed by this process. Combined with the
|
|
47
|
-
* heartbeat lease it lets reconciliation distinguish a genuinely active run
|
|
48
|
-
* (fresh lease, or running in this process) from an interrupted one left
|
|
49
|
-
* behind by a dead owner.
|
|
50
|
-
*/
|
|
51
|
-
const PROCESS_OWNER_TOKEN = randomUUID();
|
|
52
|
-
/**
|
|
53
|
-
* Runs currently executing in this process. A genuinely active local run is
|
|
54
|
-
* never reclaimed as stale, even if a blocked event loop delays its heartbeat.
|
|
55
|
-
* @type {Set<string>}
|
|
56
|
-
*/
|
|
57
|
-
const activeRunIdsInProcess = new Set();
|
|
58
|
-
/**
|
|
59
|
-
* Marks a run as executing in this process so lease reconciliation never
|
|
60
|
-
* reclaims it.
|
|
61
|
-
* @param {string} id - Run id.
|
|
62
|
-
* @returns {void} - No return value.
|
|
63
|
-
*/
|
|
64
|
-
export function registerActiveDeploymentRun(id) {
|
|
65
|
-
activeRunIdsInProcess.add(id);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Removes a run from the in-process execution registry.
|
|
69
|
-
* @param {string} id - Run id.
|
|
70
|
-
* @returns {void} - No return value.
|
|
71
|
-
*/
|
|
72
|
-
export function unregisterActiveDeploymentRun(id) {
|
|
73
|
-
activeRunIdsInProcess.delete(id);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Parses a stored JSON text column.
|
|
77
|
-
* @param {?} value - Raw column value.
|
|
78
|
-
* @returns {?} - Parsed value or null.
|
|
79
|
-
*/
|
|
80
|
-
function parseJsonColumn(value) {
|
|
81
|
-
if (typeof value !== "string" || value.length === 0)
|
|
82
|
-
return null;
|
|
83
|
-
return JSON.parse(value);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Persistence for deployment API runs and audit events. Owns its schema
|
|
87
|
-
* lazily (the record-attachments store precedent) so the tables exist in the
|
|
88
|
-
* consuming app's database without app-side migrations.
|
|
89
|
-
*/
|
|
90
|
-
export default class DeploymentRunStore {
|
|
91
|
-
/**
|
|
92
|
-
* Runs constructor.
|
|
93
|
-
* @param {object} args - Options.
|
|
94
|
-
* @param {import("../configuration.js").default} args.configuration - Configuration instance.
|
|
95
|
-
* @param {string} [args.databaseIdentifier] - Database identifier; defaults to the primary database.
|
|
96
|
-
* @param {string} args.mountIdentifier - Stable identifier of the authenticated API mount.
|
|
97
|
-
* @param {number} [args.staleRunTimeoutMs] - Lease timeout after which an active run without a heartbeat counts as interrupted.
|
|
98
|
-
*/
|
|
99
|
-
constructor({ configuration, databaseIdentifier, mountIdentifier, staleRunTimeoutMs = DEFAULT_STALE_RUN_TIMEOUT_MS }) {
|
|
100
|
-
if (typeof mountIdentifier !== "string" || mountIdentifier.length === 0) {
|
|
101
|
-
throw new Error("DeploymentRunStore requires a mountIdentifier");
|
|
102
|
-
}
|
|
103
|
-
this.configuration = configuration;
|
|
104
|
-
this.databaseIdentifier = databaseIdentifier || "default";
|
|
105
|
-
this.mountIdentifier = mountIdentifier;
|
|
106
|
-
this.staleRunTimeoutMs = staleRunTimeoutMs;
|
|
107
|
-
/** @type {Promise<void> | null} */
|
|
108
|
-
this._readyPromise = null;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Ensures the run and audit tables exist.
|
|
112
|
-
* @returns {Promise<void>} - Resolves when the schema is ready.
|
|
113
|
-
*/
|
|
114
|
-
async ensureReady() {
|
|
115
|
-
if (this._readyPromise) {
|
|
116
|
-
await this._readyPromise;
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
this._readyPromise = this._withDb(async (db) => {
|
|
120
|
-
const acquired = await db.acquireAdvisoryLock(SCHEMA_LOCK_NAME);
|
|
121
|
-
if (!acquired)
|
|
122
|
-
throw new Error("Failed to acquire deployment API schema lock");
|
|
123
|
-
try {
|
|
124
|
-
db.clearSchemaCache();
|
|
125
|
-
if (!(await db.tableExists(RUNS_TABLE))) {
|
|
126
|
-
const table = new TableData(RUNS_TABLE, { ifNotExists: true });
|
|
127
|
-
table.string("id", { null: false, primaryKey: true });
|
|
128
|
-
table.string("mount_identifier", { maxLength: 64, null: false });
|
|
129
|
-
table.string("project", { null: false, index: true });
|
|
130
|
-
table.string("stage", { null: false });
|
|
131
|
-
table.string("revision", { null: false });
|
|
132
|
-
table.string("idempotency_key", { null: false });
|
|
133
|
-
table.string("status", { null: false, index: true });
|
|
134
|
-
table.text("result_json", { null: true });
|
|
135
|
-
table.text("error_json", { null: true });
|
|
136
|
-
table.bigint("requested_at_ms", { null: false });
|
|
137
|
-
table.bigint("started_at_ms", { null: true });
|
|
138
|
-
table.bigint("finished_at_ms", { null: true });
|
|
139
|
-
table.string("owner_token", { null: true });
|
|
140
|
-
table.bigint("heartbeat_at_ms", { null: true });
|
|
141
|
-
table.addIndex(new TableIndex(["mount_identifier", "idempotency_key"], {
|
|
142
|
-
name: IDEMPOTENCY_INDEX_NAME,
|
|
143
|
-
unique: true
|
|
144
|
-
}));
|
|
145
|
-
await db.createTable(table);
|
|
146
|
-
}
|
|
147
|
-
await this._ensureRunColumns(db);
|
|
148
|
-
await this._ensureScopedIdempotencyIndex(db);
|
|
149
|
-
if (!(await db.tableExists(AUDIT_TABLE))) {
|
|
150
|
-
const table = new TableData(AUDIT_TABLE, { ifNotExists: true });
|
|
151
|
-
table.string("id", { null: false, primaryKey: true });
|
|
152
|
-
table.string("mount_identifier", { maxLength: 64, null: false });
|
|
153
|
-
table.string("run_id", { null: true, index: true });
|
|
154
|
-
table.string("event", { null: false, index: true });
|
|
155
|
-
table.text("payload_json", { null: true });
|
|
156
|
-
table.bigint("created_at_ms", { null: false });
|
|
157
|
-
await db.createTable(table);
|
|
158
|
-
}
|
|
159
|
-
await this._ensureMountIdentifierColumn(db, AUDIT_TABLE);
|
|
160
|
-
}
|
|
161
|
-
finally {
|
|
162
|
-
await db.releaseAdvisoryLock(SCHEMA_LOCK_NAME);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
try {
|
|
166
|
-
await this._readyPromise;
|
|
167
|
-
}
|
|
168
|
-
finally {
|
|
169
|
-
this._readyPromise = null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Finds a run by id.
|
|
174
|
-
* @param {string} id - Run id.
|
|
175
|
-
* @returns {Promise<DeploymentRunRow | null>} - The run or null.
|
|
176
|
-
*/
|
|
177
|
-
async findRunById(id) {
|
|
178
|
-
await this.ensureReady();
|
|
179
|
-
return await this._withDb(async (db) => await this._findRunById(db, id));
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Finds a run by idempotency key.
|
|
183
|
-
* @param {string} idempotencyKey - Idempotency key.
|
|
184
|
-
* @returns {Promise<DeploymentRunRow | null>} - The run or null.
|
|
185
|
-
*/
|
|
186
|
-
async findRunByKey(idempotencyKey) {
|
|
187
|
-
await this.ensureReady();
|
|
188
|
-
return await this._withDb(async (db) => await this._findRunByKey(db, idempotencyKey));
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Creates a run fenced by two advisory locks taken in a consistent global
|
|
192
|
-
* order — first the project/stage deployment lock, then the idempotency-key
|
|
193
|
-
* lock — so concurrent requests get a deterministic outcome instead of
|
|
194
|
-
* duplicate deployments or unique-constraint errors: an identical existing
|
|
195
|
-
* key replays, a different payload for an existing key conflicts, and an
|
|
196
|
-
* active run for the same project/stage blocks unless its ownership lease
|
|
197
|
-
* expired, in which case it is terminally reconciled as interrupted first.
|
|
198
|
-
* @param {object} args - Options.
|
|
199
|
-
* @param {string} args.idempotencyKey - Idempotency key.
|
|
200
|
-
* @param {string} args.project - Project identifier.
|
|
201
|
-
* @param {string} args.revision - Requested revision.
|
|
202
|
-
* @param {string} args.stage - Stage identifier.
|
|
203
|
-
* @returns {Promise<CreateRunOutcome>} - The outcome and the relevant run.
|
|
204
|
-
*/
|
|
205
|
-
async createRunIfPossible({ idempotencyKey, project, revision, stage }) {
|
|
206
|
-
await this.ensureReady();
|
|
207
|
-
return await this._withDb(async (db) => {
|
|
208
|
-
const lockName = this._deploymentLockName({ project, stage });
|
|
209
|
-
const acquired = await db.tryAcquireAdvisoryLock(lockName);
|
|
210
|
-
if (!acquired) {
|
|
211
|
-
// The lock being held proves another request is mid-creation for this
|
|
212
|
-
// project/stage, so this is a bounded conflict even before the active
|
|
213
|
-
// run row is visible.
|
|
214
|
-
return { outcome: "in_progress", run: await this._findBlockingRun(db, { project, stage }) };
|
|
215
|
-
}
|
|
216
|
-
try {
|
|
217
|
-
// The key lock serializes this mount's idempotency key across different
|
|
218
|
-
// project/stage pairs; always taken after the deployment lock so lock
|
|
219
|
-
// ordering can never deadlock.
|
|
220
|
-
const keyLockName = this._idempotencyLockName(idempotencyKey);
|
|
221
|
-
await db.acquireAdvisoryLock(keyLockName);
|
|
222
|
-
try {
|
|
223
|
-
const existing = await this._findRunByKey(db, idempotencyKey);
|
|
224
|
-
if (existing) {
|
|
225
|
-
const samePayload = existing.project === project && existing.stage === stage && existing.revision === revision;
|
|
226
|
-
return { outcome: samePayload ? "replay" : "conflict", run: existing };
|
|
227
|
-
}
|
|
228
|
-
const activeRun = await this._findBlockingRun(db, { project, stage });
|
|
229
|
-
if (activeRun) {
|
|
230
|
-
if (activeRun.status === "reconciliation_required") {
|
|
231
|
-
return { outcome: "reconciliation_required", run: activeRun };
|
|
232
|
-
}
|
|
233
|
-
if (!this._isRunInterrupted(activeRun))
|
|
234
|
-
return { outcome: "in_progress", run: activeRun };
|
|
235
|
-
await this._reconcileExpiredRun(db, activeRun);
|
|
236
|
-
if (activeRun.status === "running") {
|
|
237
|
-
const reconciledRun = await this._findRunById(db, activeRun.id);
|
|
238
|
-
if (!reconciledRun)
|
|
239
|
-
throw new Error(`Reconciled deployment run ${activeRun.id} could not be found`);
|
|
240
|
-
return { outcome: "reconciliation_required", run: reconciledRun };
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
const id = randomUUID();
|
|
244
|
-
await db.insert({
|
|
245
|
-
tableName: RUNS_TABLE,
|
|
246
|
-
data: {
|
|
247
|
-
id,
|
|
248
|
-
mount_identifier: this.mountIdentifier,
|
|
249
|
-
project,
|
|
250
|
-
stage,
|
|
251
|
-
revision,
|
|
252
|
-
idempotency_key: idempotencyKey,
|
|
253
|
-
status: "pending",
|
|
254
|
-
result_json: null,
|
|
255
|
-
error_json: null,
|
|
256
|
-
requested_at_ms: Date.now(),
|
|
257
|
-
started_at_ms: null,
|
|
258
|
-
finished_at_ms: null,
|
|
259
|
-
owner_token: PROCESS_OWNER_TOKEN,
|
|
260
|
-
heartbeat_at_ms: null
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
const run = await this._findRunById(db, id);
|
|
264
|
-
if (!run)
|
|
265
|
-
throw new Error(`Deployment run ${id} was not found right after insertion`);
|
|
266
|
-
return { outcome: "created", run };
|
|
267
|
-
}
|
|
268
|
-
finally {
|
|
269
|
-
await db.releaseAdvisoryLock(keyLockName);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
finally {
|
|
273
|
-
await db.releaseAdvisoryLock(lockName);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Marks a run as running.
|
|
279
|
-
* @param {object} args - Options.
|
|
280
|
-
* @param {string} args.id - Run id.
|
|
281
|
-
* @param {number} args.startedAtMs - Start time in ms epoch.
|
|
282
|
-
* @returns {Promise<void>} - Resolves when updated.
|
|
283
|
-
*/
|
|
284
|
-
async markRunning({ id, startedAtMs }) {
|
|
285
|
-
await this.ensureReady();
|
|
286
|
-
await this._withDb(async (db) => {
|
|
287
|
-
const affected = await db.affectedRows(db.updateSql({
|
|
288
|
-
tableName: RUNS_TABLE,
|
|
289
|
-
data: { heartbeat_at_ms: startedAtMs, started_at_ms: startedAtMs, status: "running" },
|
|
290
|
-
conditions: {
|
|
291
|
-
id,
|
|
292
|
-
mount_identifier: this.mountIdentifier,
|
|
293
|
-
owner_token: PROCESS_OWNER_TOKEN,
|
|
294
|
-
status: "pending"
|
|
295
|
-
}
|
|
296
|
-
}));
|
|
297
|
-
if (affected !== 1) {
|
|
298
|
-
throw new Error(`Expected to mark exactly one pending deployment run with id ${id} as running for this process owner, but updated ${affected}`);
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Renews the ownership lease of a run executing in this process. A no-op
|
|
304
|
-
* when the run already reached a terminal state.
|
|
305
|
-
* @param {object} args - Options.
|
|
306
|
-
* @param {string} args.id - Run id.
|
|
307
|
-
* @param {number} args.heartbeatAtMs - Heartbeat time in ms epoch.
|
|
308
|
-
* @returns {Promise<void>} - Resolves when updated.
|
|
309
|
-
*/
|
|
310
|
-
async heartbeat({ heartbeatAtMs, id }) {
|
|
311
|
-
await this.ensureReady();
|
|
312
|
-
await this._withDb(async (db) => {
|
|
313
|
-
await db.affectedRows(db.updateSql({
|
|
314
|
-
tableName: RUNS_TABLE,
|
|
315
|
-
data: { heartbeat_at_ms: heartbeatAtMs },
|
|
316
|
-
conditions: { id, mount_identifier: this.mountIdentifier, owner_token: PROCESS_OWNER_TOKEN, status: "running" }
|
|
317
|
-
}));
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Marks a run as succeeded with its sanitized result.
|
|
322
|
-
* @param {object} args - Options.
|
|
323
|
-
* @param {string} args.id - Run id.
|
|
324
|
-
* @param {string} args.ownerToken - Expected execution owner token.
|
|
325
|
-
* @param {number} args.finishedAtMs - Finish time in ms epoch.
|
|
326
|
-
* @param {?} args.result - Sanitized adapter report.
|
|
327
|
-
* @returns {Promise<void>} - Resolves when updated.
|
|
328
|
-
*/
|
|
329
|
-
async markSucceeded({ finishedAtMs, id, ownerToken, result }) {
|
|
330
|
-
await this._transitionRunningRun({
|
|
331
|
-
data: { finished_at_ms: finishedAtMs, result_json: JSON.stringify(result ?? null), status: "succeeded" },
|
|
332
|
-
id,
|
|
333
|
-
ownerToken
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Marks a run as failed with its sanitized failure payload.
|
|
338
|
-
* @param {object} args - Options.
|
|
339
|
-
* @param {string} args.id - Run id.
|
|
340
|
-
* @param {string} args.ownerToken - Expected execution owner token.
|
|
341
|
-
* @param {number} args.finishedAtMs - Finish time in ms epoch.
|
|
342
|
-
* @param {?} args.error - Sanitized failure payload.
|
|
343
|
-
* @returns {Promise<void>} - Resolves when updated.
|
|
344
|
-
*/
|
|
345
|
-
async markFailed({ error, finishedAtMs, id, ownerToken }) {
|
|
346
|
-
await this._transitionRunningRun({
|
|
347
|
-
data: { error_json: JSON.stringify(error ?? null), finished_at_ms: finishedAtMs, status: "failed" },
|
|
348
|
-
id,
|
|
349
|
-
ownerToken
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Fences a run after the adapter may have activated externally but its
|
|
354
|
-
* successful result could not be durably recorded. This state blocks future
|
|
355
|
-
* deployments until an operator reconciles the external outcome.
|
|
356
|
-
* @param {object} args - Options.
|
|
357
|
-
* @param {string} args.id - Run id.
|
|
358
|
-
* @param {string} args.ownerToken - Expected execution owner token.
|
|
359
|
-
* @param {number} args.finishedAtMs - Time reconciliation became necessary in ms epoch.
|
|
360
|
-
* @param {?} args.error - Sanitized operator-facing explanation.
|
|
361
|
-
* @returns {Promise<void>} - Resolves when updated.
|
|
362
|
-
*/
|
|
363
|
-
async markReconciliationRequired({ error, finishedAtMs, id, ownerToken }) {
|
|
364
|
-
await this._transitionRunningRun({
|
|
365
|
-
data: { error_json: JSON.stringify(error ?? null), finished_at_ms: finishedAtMs, status: "reconciliation_required" },
|
|
366
|
-
id,
|
|
367
|
-
ownerToken
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* Records a sanitized audit event.
|
|
372
|
-
* @param {object} args - Options.
|
|
373
|
-
* @param {string} args.event - Event name.
|
|
374
|
-
* @param {?} args.payload - Sanitized payload.
|
|
375
|
-
* @param {string | null} args.runId - Owning run id.
|
|
376
|
-
* @returns {Promise<void>} - Resolves when recorded.
|
|
377
|
-
*/
|
|
378
|
-
async addAuditEvent({ event, payload, runId }) {
|
|
379
|
-
await this.ensureReady();
|
|
380
|
-
await this._withDb(async (db) => {
|
|
381
|
-
await db.insert({
|
|
382
|
-
tableName: AUDIT_TABLE,
|
|
383
|
-
data: {
|
|
384
|
-
id: randomUUID(),
|
|
385
|
-
mount_identifier: this.mountIdentifier,
|
|
386
|
-
run_id: runId,
|
|
387
|
-
event,
|
|
388
|
-
payload_json: payload === undefined ? null : JSON.stringify(payload ?? null),
|
|
389
|
-
created_at_ms: Date.now()
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Lists audit events for a run in creation order.
|
|
396
|
-
* @param {object} args - Options.
|
|
397
|
-
* @param {string} args.runId - Run id.
|
|
398
|
-
* @returns {Promise<DeploymentAuditEventRow[]>} - Audit events.
|
|
399
|
-
*/
|
|
400
|
-
async listAuditEvents({ runId }) {
|
|
401
|
-
await this.ensureReady();
|
|
402
|
-
return await this._withDb(async (db) => {
|
|
403
|
-
const rows = await db
|
|
404
|
-
.newQuery()
|
|
405
|
-
.from(AUDIT_TABLE)
|
|
406
|
-
.where({ mount_identifier: this.mountIdentifier, run_id: runId })
|
|
407
|
-
.order("created_at_ms ASC")
|
|
408
|
-
.results();
|
|
409
|
-
return rows.map((row) => this._auditRowFromDb(row));
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Builds the advisory-lock name for a project/stage pair. Hashed so the name
|
|
414
|
-
* stays within driver limits (MySQL/MariaDB cap lock names at 64 chars)
|
|
415
|
-
* regardless of identifier length.
|
|
416
|
-
* @param {object} args - Options.
|
|
417
|
-
* @param {string} args.project - Project identifier.
|
|
418
|
-
* @param {string} args.stage - Stage identifier.
|
|
419
|
-
* @returns {string} - Bounded advisory-lock name.
|
|
420
|
-
*/
|
|
421
|
-
_deploymentLockName({ project, stage }) {
|
|
422
|
-
const hash = createHash("sha256").update(`${this.mountIdentifier}:${project}:${stage}`).digest("hex").slice(0, 32);
|
|
423
|
-
return `velocious-deployment-api:${hash}`;
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Builds the advisory-lock name serializing one idempotency key within this mount.
|
|
427
|
-
* @param {string} idempotencyKey - Idempotency key.
|
|
428
|
-
* @returns {string} - Bounded advisory-lock name.
|
|
429
|
-
*/
|
|
430
|
-
_idempotencyLockName(idempotencyKey) {
|
|
431
|
-
const hash = createHash("sha256").update(`${this.mountIdentifier}:${idempotencyKey}`).digest("hex").slice(0, 32);
|
|
432
|
-
return `velocious-deployment-api:key:${hash}`;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Decides whether an active run counts as interrupted: its owner lease
|
|
436
|
-
* (heartbeat, or the request time when it never started) is older than the
|
|
437
|
-
* configured timeout and it is not executing in this process. A run with a
|
|
438
|
-
* fresh lease owned by another worker is genuinely active and must never be
|
|
439
|
-
* reclaimed here.
|
|
440
|
-
* @param {DeploymentRunRow} run - Active run.
|
|
441
|
-
* @returns {boolean} - Whether the run is interrupted.
|
|
442
|
-
*/
|
|
443
|
-
_isRunInterrupted(run) {
|
|
444
|
-
if (activeRunIdsInProcess.has(run.id))
|
|
445
|
-
return false;
|
|
446
|
-
const leaseReferenceMs = run.heartbeatAtMs ?? run.requestedAtMs;
|
|
447
|
-
return Date.now() - leaseReferenceMs > this.staleRunTimeoutMs;
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Reconciles an expired ownership lease. Pending work is safely interrupted
|
|
451
|
-
* because external activation never began. Running work requires operator
|
|
452
|
-
* reconciliation because the external outcome may already have changed.
|
|
453
|
-
* Runs inside the project/stage deployment lock, so exactly one reconciler
|
|
454
|
-
* wins.
|
|
455
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
456
|
-
* @param {DeploymentRunRow} run - Interrupted run.
|
|
457
|
-
* @returns {Promise<void>} - Resolves when reconciled.
|
|
458
|
-
*/
|
|
459
|
-
async _reconcileExpiredRun(db, run) {
|
|
460
|
-
const finishedAtMs = Date.now();
|
|
461
|
-
const reconciliationRequired = run.status === "running";
|
|
462
|
-
const errorPayload = reconciliationRequired
|
|
463
|
-
? { message: "Deployment outcome requires operator reconciliation after the execution owner lease expired" }
|
|
464
|
-
: { message: "Deployment run was interrupted before execution began (ownership lease expired)" };
|
|
465
|
-
const status = reconciliationRequired ? "reconciliation_required" : "interrupted";
|
|
466
|
-
const event = reconciliationRequired ? "run_reconciliation_required" : "run_interrupted";
|
|
467
|
-
const data = { error_json: JSON.stringify(errorPayload), finished_at_ms: finishedAtMs, status };
|
|
468
|
-
const affected = await db.affectedRows(db.updateSql({
|
|
469
|
-
tableName: RUNS_TABLE,
|
|
470
|
-
data,
|
|
471
|
-
conditions: { id: run.id, mount_identifier: this.mountIdentifier, owner_token: run.ownerToken, status: run.status }
|
|
472
|
-
}));
|
|
473
|
-
if (affected !== 1) {
|
|
474
|
-
throw new Error(`Expected to reconcile exactly one expired deployment run with id ${run.id}, but updated ${affected}`);
|
|
475
|
-
}
|
|
476
|
-
await db.insert({
|
|
477
|
-
tableName: AUDIT_TABLE,
|
|
478
|
-
data: {
|
|
479
|
-
id: randomUUID(),
|
|
480
|
-
mount_identifier: this.mountIdentifier,
|
|
481
|
-
run_id: run.id,
|
|
482
|
-
event,
|
|
483
|
-
payload_json: JSON.stringify({ project: run.project, revision: run.revision, stage: run.stage }),
|
|
484
|
-
created_at_ms: Date.now()
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Adds the ownership lease columns to a runs table created before they
|
|
490
|
-
* existed (the background-jobs store column-upgrade precedent).
|
|
491
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
492
|
-
* @returns {Promise<void>} - Resolves when the columns exist.
|
|
493
|
-
*/
|
|
494
|
-
async _ensureRunColumns(db) {
|
|
495
|
-
const table = await db.getTableByNameOrFail(RUNS_TABLE);
|
|
496
|
-
const missingMountIdentifier = !(await table.getColumnByName("mount_identifier"));
|
|
497
|
-
const missingOwnerToken = !(await table.getColumnByName("owner_token"));
|
|
498
|
-
const missingHeartbeat = !(await table.getColumnByName("heartbeat_at_ms"));
|
|
499
|
-
if (missingMountIdentifier || missingOwnerToken || missingHeartbeat) {
|
|
500
|
-
const tableData = new TableData(RUNS_TABLE);
|
|
501
|
-
if (missingMountIdentifier)
|
|
502
|
-
tableData.string("mount_identifier", { maxLength: 64, null: true });
|
|
503
|
-
if (missingOwnerToken)
|
|
504
|
-
tableData.string("owner_token", { null: true });
|
|
505
|
-
if (missingHeartbeat)
|
|
506
|
-
tableData.bigint("heartbeat_at_ms", { null: true });
|
|
507
|
-
const sqls = await db.alterTableSQLs(tableData);
|
|
508
|
-
for (const sql of sqls) {
|
|
509
|
-
await db.query(sql);
|
|
510
|
-
}
|
|
511
|
-
db.clearSchemaCache();
|
|
512
|
-
}
|
|
513
|
-
await this._quarantineLegacyRows(db, RUNS_TABLE);
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Adds and safely backfills a mount identifier on an existing lazily-owned table.
|
|
517
|
-
* Legacy rows were not attributable to an authenticated mount, so they are
|
|
518
|
-
* quarantined in an unreachable scope instead of being exposed to whichever
|
|
519
|
-
* mount happens to initialize first.
|
|
520
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
521
|
-
* @param {string} tableName - Lazily-owned table name.
|
|
522
|
-
* @returns {Promise<void>} - Resolves when the column exists.
|
|
523
|
-
*/
|
|
524
|
-
async _ensureMountIdentifierColumn(db, tableName) {
|
|
525
|
-
const table = await db.getTableByNameOrFail(tableName);
|
|
526
|
-
if (!(await table.getColumnByName("mount_identifier"))) {
|
|
527
|
-
const tableData = new TableData(tableName);
|
|
528
|
-
tableData.string("mount_identifier", { maxLength: 64, null: true });
|
|
529
|
-
for (const sql of await db.alterTableSQLs(tableData))
|
|
530
|
-
await db.query(sql);
|
|
531
|
-
db.clearSchemaCache();
|
|
532
|
-
}
|
|
533
|
-
await this._quarantineLegacyRows(db, tableName);
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Assigns pre-scope rows to a reserved identifier no real mount can use.
|
|
537
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
538
|
-
* @param {string} tableName - Table containing legacy rows.
|
|
539
|
-
* @returns {Promise<void>} - Resolves when legacy rows are quarantined.
|
|
540
|
-
*/
|
|
541
|
-
async _quarantineLegacyRows(db, tableName) {
|
|
542
|
-
await db.affectedRows(db.updateSql({
|
|
543
|
-
tableName,
|
|
544
|
-
data: { mount_identifier: LEGACY_UNSCOPED_MOUNT_IDENTIFIER },
|
|
545
|
-
conditions: { mount_identifier: null }
|
|
546
|
-
}));
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Replaces the legacy global idempotency-key uniqueness with mount-scoped
|
|
550
|
-
* uniqueness. The scoped index is created before the old one is removed, so
|
|
551
|
-
* an interrupted lazy upgrade never leaves idempotency keys unfenced.
|
|
552
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
553
|
-
* @returns {Promise<void>} - Resolves when index ownership is scoped.
|
|
554
|
-
*/
|
|
555
|
-
async _ensureScopedIdempotencyIndex(db) {
|
|
556
|
-
db.clearSchemaCache();
|
|
557
|
-
const table = await db.getTableByNameOrFail(RUNS_TABLE);
|
|
558
|
-
const indexes = await table.getIndexes();
|
|
559
|
-
const scopedIndex = indexes.find((index) => {
|
|
560
|
-
return index.isUnique() && index.getColumnNames().join(",") === "mount_identifier,idempotency_key";
|
|
561
|
-
});
|
|
562
|
-
if (!scopedIndex) {
|
|
563
|
-
const sqls = await db.createIndexSQLs({
|
|
564
|
-
columns: ["mount_identifier", "idempotency_key"],
|
|
565
|
-
name: IDEMPOTENCY_INDEX_NAME,
|
|
566
|
-
tableName: RUNS_TABLE,
|
|
567
|
-
unique: true
|
|
568
|
-
});
|
|
569
|
-
for (const sql of sqls)
|
|
570
|
-
await db.query(sql);
|
|
571
|
-
db.clearSchemaCache();
|
|
572
|
-
}
|
|
573
|
-
const refreshedTable = await db.getTableByNameOrFail(RUNS_TABLE);
|
|
574
|
-
const legacyIndexes = (await refreshedTable.getIndexes()).filter((index) => {
|
|
575
|
-
return !index.isPrimaryKey() && index.isUnique() && index.getColumnNames().join(",") === "idempotency_key";
|
|
576
|
-
});
|
|
577
|
-
for (const legacyIndex of legacyIndexes) {
|
|
578
|
-
const sqls = await db.removeIndexSQLs({ name: legacyIndex.getName(), tableName: RUNS_TABLE });
|
|
579
|
-
for (const sql of sqls)
|
|
580
|
-
await db.query(sql);
|
|
581
|
-
}
|
|
582
|
-
if (legacyIndexes.length > 0)
|
|
583
|
-
db.clearSchemaCache();
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Applies a terminal transition only while the expected execution owner
|
|
587
|
-
* still owns a running row. A stale worker therefore cannot overwrite a
|
|
588
|
-
* state already written by lease reconciliation.
|
|
589
|
-
* @param {object} args - Options.
|
|
590
|
-
* @param {Record<string, ?>} args.data - Terminal column values.
|
|
591
|
-
* @param {string} args.id - Run id.
|
|
592
|
-
* @param {string} args.ownerToken - Expected execution owner token.
|
|
593
|
-
* @returns {Promise<void>} - Resolves when updated.
|
|
594
|
-
*/
|
|
595
|
-
async _transitionRunningRun({ data, id, ownerToken }) {
|
|
596
|
-
await this.ensureReady();
|
|
597
|
-
await this._withDb(async (db) => {
|
|
598
|
-
const affected = await db.affectedRows(db.updateSql({
|
|
599
|
-
tableName: RUNS_TABLE,
|
|
600
|
-
data,
|
|
601
|
-
conditions: { id, mount_identifier: this.mountIdentifier, owner_token: ownerToken, status: "running" }
|
|
602
|
-
}));
|
|
603
|
-
if (affected !== 1) {
|
|
604
|
-
throw new Error(`Expected to transition exactly one running deployment run with id ${id} for its execution owner, but updated ${affected}`);
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Finds a run by id on an open connection.
|
|
610
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
611
|
-
* @param {string} id - Run id.
|
|
612
|
-
* @returns {Promise<DeploymentRunRow | null>} - The run or null.
|
|
613
|
-
*/
|
|
614
|
-
async _findRunById(db, id) {
|
|
615
|
-
const rows = await db
|
|
616
|
-
.newQuery()
|
|
617
|
-
.from(RUNS_TABLE)
|
|
618
|
-
.where({ id, mount_identifier: this.mountIdentifier })
|
|
619
|
-
.limit(1)
|
|
620
|
-
.results();
|
|
621
|
-
return rows[0] ? this._runRowFromDb(rows[0]) : null;
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Finds a run by idempotency key on an open connection.
|
|
625
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
626
|
-
* @param {string} idempotencyKey - Idempotency key.
|
|
627
|
-
* @returns {Promise<DeploymentRunRow | null>} - The run or null.
|
|
628
|
-
*/
|
|
629
|
-
async _findRunByKey(db, idempotencyKey) {
|
|
630
|
-
const rows = await db
|
|
631
|
-
.newQuery()
|
|
632
|
-
.from(RUNS_TABLE)
|
|
633
|
-
.where({ idempotency_key: idempotencyKey, mount_identifier: this.mountIdentifier })
|
|
634
|
-
.limit(1)
|
|
635
|
-
.results();
|
|
636
|
-
return rows[0] ? this._runRowFromDb(rows[0]) : null;
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Finds the run blocking another deployment for a project/stage.
|
|
640
|
-
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
641
|
-
* @param {object} args - Options.
|
|
642
|
-
* @param {string} args.project - Project identifier.
|
|
643
|
-
* @param {string} args.stage - Stage identifier.
|
|
644
|
-
* @returns {Promise<DeploymentRunRow | null>} - The blocking run or null.
|
|
645
|
-
*/
|
|
646
|
-
async _findBlockingRun(db, { project, stage }) {
|
|
647
|
-
const rows = await db
|
|
648
|
-
.newQuery()
|
|
649
|
-
.from(RUNS_TABLE)
|
|
650
|
-
.where({ mount_identifier: this.mountIdentifier, project, stage, status: BLOCKING_STATUSES })
|
|
651
|
-
.order("requested_at_ms ASC")
|
|
652
|
-
.limit(1)
|
|
653
|
-
.results();
|
|
654
|
-
return rows[0] ? this._runRowFromDb(rows[0]) : null;
|
|
655
|
-
}
|
|
656
|
-
/**
|
|
657
|
-
* Normalizes a raw run row.
|
|
658
|
-
* @param {?} row - Raw database row.
|
|
659
|
-
* @returns {DeploymentRunRow} - Normalized run row.
|
|
660
|
-
*/
|
|
661
|
-
_runRowFromDb(row) {
|
|
662
|
-
const record = /** @type {Record<string, ?>} */ (row);
|
|
663
|
-
return {
|
|
664
|
-
id: /** @type {string} */ (record.id),
|
|
665
|
-
mountIdentifier: /** @type {string} */ (record.mount_identifier),
|
|
666
|
-
project: /** @type {string} */ (record.project),
|
|
667
|
-
stage: /** @type {string} */ (record.stage),
|
|
668
|
-
revision: /** @type {string} */ (record.revision),
|
|
669
|
-
idempotencyKey: /** @type {string} */ (record.idempotency_key),
|
|
670
|
-
status: /** @type {string} */ (record.status),
|
|
671
|
-
result: parseJsonColumn(record.result_json),
|
|
672
|
-
error: parseJsonColumn(record.error_json),
|
|
673
|
-
requestedAtMs: Number(record.requested_at_ms),
|
|
674
|
-
startedAtMs: record.started_at_ms === null || record.started_at_ms === undefined ? null : Number(record.started_at_ms),
|
|
675
|
-
finishedAtMs: record.finished_at_ms === null || record.finished_at_ms === undefined ? null : Number(record.finished_at_ms),
|
|
676
|
-
ownerToken: record.owner_token === null || record.owner_token === undefined ? null : /** @type {string} */ (record.owner_token),
|
|
677
|
-
heartbeatAtMs: record.heartbeat_at_ms === null || record.heartbeat_at_ms === undefined ? null : Number(record.heartbeat_at_ms)
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
/**
|
|
681
|
-
* Normalizes a raw audit event row.
|
|
682
|
-
* @param {?} row - Raw database row.
|
|
683
|
-
* @returns {DeploymentAuditEventRow} - Normalized audit event row.
|
|
684
|
-
*/
|
|
685
|
-
_auditRowFromDb(row) {
|
|
686
|
-
const record = /** @type {Record<string, ?>} */ (row);
|
|
687
|
-
return {
|
|
688
|
-
id: /** @type {string} */ (record.id),
|
|
689
|
-
runId: record.run_id === null || record.run_id === undefined ? null : /** @type {string} */ (record.run_id),
|
|
690
|
-
event: /** @type {string} */ (record.event),
|
|
691
|
-
payload: parseJsonColumn(record.payload_json),
|
|
692
|
-
createdAtMs: Number(record.created_at_ms)
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Runs a callback with a pooled connection.
|
|
697
|
-
* @template T
|
|
698
|
-
* @param {(db: import("../database/drivers/base.js").default) => Promise<T>} callback - Callback.
|
|
699
|
-
* @returns {Promise<T>} - Callback result.
|
|
700
|
-
*/
|
|
701
|
-
async _withDb(callback) {
|
|
702
|
-
const pool = this.configuration.getDatabasePool(this.databaseIdentifier);
|
|
703
|
-
/** @type {T | undefined} */
|
|
704
|
-
let result;
|
|
705
|
-
await pool.withConnection({ name: "Deployment run store" }, async (db) => {
|
|
706
|
-
result = await callback(db);
|
|
707
|
-
});
|
|
708
|
-
return /** @type {T} */ (result);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVuLXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2RlcGxveW1lbnQtYXBpL3J1bi1zdG9yZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxTQUFTLE1BQU0saUNBQWlDLENBQUE7QUFDdkQsT0FBTyxVQUFVLE1BQU0sdUNBQXVDLENBQUE7QUFDOUQsT0FBTyxFQUFDLFVBQVUsRUFBRSxVQUFVLEVBQUMsTUFBTSxhQUFhLENBQUE7QUFFbEQ7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0g7Ozs7Ozs7O0dBUUc7QUFDSDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxHQUFHLDJCQUEyQixDQUFBO0FBQzlDLE1BQU0sV0FBVyxHQUFHLHVDQUF1QyxDQUFBO0FBQzNELE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxTQUFTLEVBQUUsU0FBUyxFQUFFLHlCQUF5QixDQUFDLENBQUE7QUFDM0UsTUFBTSw0QkFBNEIsR0FBRyxLQUFLLENBQUE7QUFDMUMsTUFBTSxzQkFBc0IsR0FBRyx3Q0FBd0MsQ0FBQTtBQUN2RSxNQUFNLGdDQUFnQyxHQUFHLGlCQUFpQixDQUFBO0FBQzFELE1BQU0sZ0JBQWdCLEdBQUcsb0NBQW9DLENBQUE7QUFFN0Q7Ozs7O0dBS0c7QUFDSCxNQUFNLG1CQUFtQixHQUFHLFVBQVUsRUFBRSxDQUFBO0FBRXhDOzs7O0dBSUc7QUFDSCxNQUFNLHFCQUFxQixHQUFHLElBQUksR0FBRyxFQUFFLENBQUE7QUFFdkM7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsMkJBQTJCLENBQUMsRUFBRTtJQUM1QyxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUE7QUFDL0IsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsNkJBQTZCLENBQUMsRUFBRTtJQUM5QyxxQkFBcUIsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUE7QUFDbEMsQ0FBQztBQUVEOzs7O0dB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Q0FBQTtJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUTtRQUNwQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtRQUN4RSw0QkFBNEI7UUFDNUIsSUFBSSxNQUFNLENBQUE7UUFFVixNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsRUFBQyxJQUFJLEVBQUUsc0JBQXNCLEVBQUMsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7WUFDckUsTUFBTSxHQUFHLE1BQU0sUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBQzdCLENBQUMsQ0FBQyxDQUFBO1FBRUYsT0FBTyxnQkFBZ0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQ2xDLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgVGFibGVEYXRhIGZyb20gXCIuLi9kYXRhYmFzZS90YWJsZS1kYXRhL2luZGV4LmpzXCJcbmltcG9ydCBUYWJsZUluZGV4IGZyb20gXCIuLi9kYXRhYmFzZS90YWJsZS1kYXRhL3RhYmxlLWluZGV4LmpzXCJcbmltcG9ydCB7Y3JlYXRlSGFzaCwgcmFuZG9tVVVJRH0gZnJvbSBcIm5vZGU6Y3J5cHRvXCJcblxuLyoqXG4gKiBEZXBsb3ltZW50UnVuUm93IHR5cGUuXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBEZXBsb3ltZW50UnVuUm93XG4gKiBAcHJvcGVydHkge3N0cmluZ30gaWQgLSBSdW4gaWQgKFVVSUQpLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IG1vdW50SWRlbnRpZmllciAtIFN0YWJsZSBpZGVudGlmaWVyIG9mIHRoZSBhdXRoZW50aWNhdGVkIEFQSSBtb3VudCB0aGF0IG93bnMgdGhlIHJ1bi5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBwcm9qZWN0IC0gQWxsb3dsaXN0ZWQgcHJvamVjdCBpZGVudGlmaWVyLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHN0YWdlIC0gQWxsb3dsaXN0ZWQgc3RhZ2UgaWRlbnRpZmllci5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSByZXZpc2lvbiAtIEZ1bGwgaW1tdXRhYmxlIHJlcXVlc3RlZCBHaXQgcmV2aXNpb24uXG4gKiBAcHJvcGVydHkge3N0cmluZ30gaWRlbXBvdGVuY3lLZXkgLSBDYWxsZXItc3VwcGxpZWQgaWRlbXBvdGVuY3kga2V5LlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHN0YXR1cyAtIFJ1biBzdGF0dXM6IHBlbmRpbmcsIHJ1bm5pbmcsIHN1Y2NlZWRlZCwgZmFpbGVkLCBpbnRlcnJ1cHRlZCwgb3IgcmVjb25jaWxpYXRpb25fcmVxdWlyZWQuXG4gKiBAcHJvcGVydHkgez99IHJlc3VsdCAtIFNhbml0aXplZCBhZGFwdGVyIHJlcG9ydCwgb3IgbnVsbCB3aGlsZSB1bnNldC5cbiAqIEBwcm9wZXJ0eSB7P30gZXJyb3IgLSBTYW5pdGl6ZWQgZmFpbHVyZSBwYXlsb2FkLCBvciBudWxsIHdoaWxlIHVuc2V0LlxuICogQHByb3BlcnR5IHtudW1iZXJ9IHJlcXVlc3RlZEF0TXMgLSBDcmVhdGlvbiB0aW1lIGluIG1zIGVwb2NoLlxuICogQHByb3BlcnR5IHtudW1iZXIgfCBudWxsfSBzdGFydGVkQXRNcyAtIEV4ZWN1dGlvbiBzdGFydCBpbiBtcyBlcG9jaC5cbiAqIEBwcm9wZXJ0eSB7bnVtYmVyIHwgbnVsbH0gZmluaXNoZWRBdE1zIC0gVGVybWluYWwgdGltZSBpbiBtcyBlcG9jaC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nIHwgbnVsbH0gb3duZXJUb2tlbiAtIE93bmVyc2hpcCB0b2tlbiBvZiB0aGUgZXhlY3V0aW5nIHByb2Nlc3MuXG4gKiBAcHJvcGVydHkge251bWJlciB8IG51bGx9IGhlYXJ0YmVhdEF0TXMgLSBMYXN0IG93bmVyc2hpcCBsZWFzZSBoZWFydGJlYXQgaW4gbXMgZXBvY2guXG4gKi9cbi8qKlxuICogRGVwbG95bWVudEF1ZGl0RXZlbnRSb3cgdHlwZS5cbiAqIEB0eXBlZGVmIHtvYmplY3R9IERlcGxveW1lbnRBdWRpdEV2ZW50Um93XG4gKiBAcHJvcGVydHkge3N0cmluZ30gaWQgLSBFdmVudCBpZCAoVVVJRCkuXG4gKiBAcHJvcGVydHkge3N0cmluZyB8IG51bGx9IHJ1bklkIC0gT3duaW5nIHJ1biBpZC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBldmVudCAtIEV2ZW50IG5hbWUgKGUuZy4gcnVuX3JlcXVlc3RlZCwgcnVuX3N0YXJ0ZWQsIHJ1bl9zdWNjZWVkZWQsIHJ1bl9mYWlsZWQpLlxuICogQHByb3BlcnR5IHs/fSBwYXlsb2FkIC0gU2FuaXRpemVkIEpTT04gcGF5bG9hZC5cbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBjcmVhdGVkQXRNcyAtIENyZWF0aW9uIHRpbWUgaW4gbXMgZXBvY2guXG4gKi9cbi8qKlxuICogQ3JlYXRlUnVuT3V0Y29tZSB0eXBlLlxuICogQHR5cGVkZWYge29iamVjdH0gQ3JlYXRlUnVuT3V0Y29tZVxuICogQHByb3BlcnR5IHtcImNyZWF0ZWRcIiB8IFwicmVwbGF5XCIgfCBcImNvbmZsaWN0XCIgfCBcImluX3Byb2dyZXNzXCIgfCBcInJlY29uY2lsaWF0aW9uX3JlcXVpcmVkXCJ9IG91dGNvbWUgLSBXaGF0IGhhcHBlbmVkLlxuICogQHByb3BlcnR5IHtEZXBsb3ltZW50UnVuUm93IHwgbnVsbH0gcnVuIC0gVGhlIGNyZWF0ZWQsIHJlcGxheWVkLCBjb25mbGljdGluZywgb3IgYWN0aXZlIHJ1bjsgbnVsbCB3aGVuIHRoZSBkZXBsb3ltZW50IGxvY2sgd2FzIGhlbGQgbWlkLWNyZWF0aW9uIGJ5IGFub3RoZXIgcHJvY2Vzcy5cbiAqL1xuY29uc3QgUlVOU19UQUJMRSA9IFwidmVsb2Npb3VzX2RlcGxveW1lbnRfcnVuc1wiXG5jb25zdCBBVURJVF9UQUJMRSA9IFwidmVsb2Npb3VzX2RlcGxveW1lbnRfYXBpX2F1ZGl0X2V2ZW50c1wiXG5jb25zdCBCTE9DS0lOR19TVEFUVVNFUyA9IFtcInBlbmRpbmdcIiwgXCJydW5uaW5nXCIsIFwicmVjb25jaWxpYXRpb25fcmVxdWlyZWRcIl1cbmNvbnN0IERFRkFVTFRfU1RBTEVfUlVOX1RJTUVPVVRfTVMgPSA2MDAwMFxuY29uc3QgSURFTVBPVEVOQ1lfSU5ERVhfTkFNRSA9IFwiaW5kZXhfZGVwbG95bWVudF9ydW5zX29uX21vdW50X2FuZF9rZXlcIlxuY29uc3QgTEVHQUNZX1VOU0NPUEVEX01PVU5UX0lERU5USUZJRVIgPSBcImxlZ2FjeS11bnNjb3BlZFwiXG5jb25zdCBTQ0hFTUFfTE9DS19OQU1FID0gXCJ2ZWxvY2lvdXMtZGVwbG95bWVudC1hcGk6c2NoZW1hLXYyXCJcblxuLyoqXG4gKiBPd25lcnNoaXAgdG9rZW4gaWRlbnRpZnlpbmcgcnVucyBleGVjdXRlZCBieSB0aGlzIHByb2Nlc3MuIENvbWJpbmVkIHdpdGggdGhlXG4gKiBoZWFydGJlYXQgbGVhc2UgaXQgbGV0cyByZWNvbmNpbGlhdGlvbiBkaXN0aW5ndWlzaCBhIGdlbnVpbmVseSBhY3RpdmUgcnVuXG4gKiAoZnJlc2ggbGVhc2UsIG9yIHJ1bm5pbmcgaW4gdGhpcyBwcm9jZXNzKSBmcm9tIGFuIGludGVycnVwdGVkIG9uZSBsZWZ0XG4gKiBiZWhpbmQgYnkgYSBkZWFkIG93bmVyLlxuICovXG5jb25zdCBQUk9DRVNTX09XTkVSX1RPS0VOID0gcmFuZG9tVVVJRCgpXG5cbi8qKlxuICogUnVucyBjdXJyZW50bHkgZXhlY3V0aW5nIGluIHRoaXMgcHJvY2Vzcy4gQSBnZW51aW5lbHkgYWN0aXZlIGxvY2FsIHJ1biBpc1xuICogbmV2ZXIgcmVjbGFpbWVkIGFzIHN0YWxlLCBldmVuIGlmIGEgYmxvY2tlZCBldmVudCBsb29wIGRlbGF5cyBpdHMgaGVhcnRiZWF0LlxuICogQHR5cGUge1NldDxzdHJpbmc+fVxuICovXG5jb25zdCBhY3RpdmVSdW5JZHNJblByb2Nlc3MgPSBuZXcgU2V0KClcblxuLyoqXG4gKiBNYXJrcyBhIHJ1biBhcyBleGVjdXRpbmcgaW4gdGhpcyBwcm9jZXNzIHNvIGxlYXNlIHJlY29uY2lsaWF0aW9uIG5ldmVyXG4gKiByZWNsYWltcyBpdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBpZCAtIFJ1biBpZC5cbiAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZ2lzdGVyQWN0aXZlRGVwbG95bWVudFJ1bihpZCkge1xuICBhY3RpdmVSdW5JZHNJblByb2Nlc3MuYWRkKGlkKVxufVxuXG4vKipcbiAqIFJlbW92ZXMgYSBydW4gZnJvbSB0aGUgaW4tcHJvY2VzcyBleGVjdXRpb24gcmVnaXN0cnkuXG4gKiBAcGFyYW0ge3N0cmluZ30gaWQgLSBSdW4gaWQuXG4gKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1bnJlZ2lzdGVyQWN0aXZlRGVwbG95bWVudFJ1bihpZCkge1xuICBhY3RpdmVSdW5JZHNJblByb2Nlc3MuZGVsZXRlKGlkKVxufVxuXG4vKipcbiAqIFBhcnNlcyBhIHN0b3JlZCBKU09OIHRleHQgY29sdW1uLlxuICogQHBhcmFtIHs/fSB2YWx1ZSAtIFJhdyBjb2x1bW4gdmFsdWUuXG4gKiBAcmV0dXJucyB7P30gLSBQYXJzZWQgdmFsdWUgb3IgbnVsbC5cbiAqL1xuZnVuY3Rpb24gcGFyc2VKc29uQ29sdW1uKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgIT09IFwic3RyaW5nXCIgfHwgdmFsdWUubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbFxuXG4gIHJldHVybiBKU09OLnBhcnNlKHZhbHVlKVxufVxuXG4vKipcbiAqIFBlcnNpc3RlbmNlIGZvciBkZXBsb3ltZW50IEFQSSBydW5zIGFuZCBhdWRpdCBldmVudHMuIE93bnMgaXRzIHNjaGVtYVxuICogbGF6aWx5ICh0aGUgcmVjb3JkLWF0dGFjaG1lbnRzIHN0b3JlIHByZWNlZGVudCkgc28gdGhlIHRhYmxlcyBleGlzdCBpbiB0aGVcbiAqIGNvbnN1bWluZyBhcHAncyBkYXRhYmFzZSB3aXRob3V0IGFwcC1zaWRlIG1pZ3JhdGlvbnMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERlcGxveW1lbnRSdW5TdG9yZSB7XG4gIC8qKlxuICAgKiBSdW5zIGNvbnN0cnVjdG9yLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbmZpZ3VyYXRpb24gLSBDb25maWd1cmF0aW9uIGluc3RhbmNlLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MuZGF0YWJhc2VJZGVudGlmaWVyXSAtIERhdGFiYXNlIGlkZW50aWZpZXI7IGRlZmF1bHRzIHRvIHRoZSBwcmltYXJ5IGRhdGFiYXNlLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5tb3VudElkZW50aWZpZXIgLSBTdGFibGUgaWRlbnRpZmllciBvZiB0aGUgYXV0aGVudGljYXRlZCBBUEkgbW91bnQuXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbYXJncy5zdGFsZVJ1blRpbWVvdXRNc10gLSBMZWFzZSB0aW1lb3V0IGFmdGVyIHdoaWNoIGFuIGFjdGl2ZSBydW4gd2l0aG91dCBhIGhlYXJ0YmVhdCBjb3VudHMgYXMgaW50ZXJydXB0ZWQuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29uZmlndXJhdGlvbiwgZGF0YWJhc2VJZGVudGlmaWVyLCBtb3VudElkZW50aWZpZXIsIHN0YWxlUnVuVGltZW91dE1zID0gREVGQVVMVF9TVEFMRV9SVU5fVElNRU9VVF9NU30pIHtcbiAgICBpZiAodHlwZW9mIG1vdW50SWRlbnRpZmllciAhPT0gXCJzdHJpbmdcIiB8fCBtb3VudElkZW50aWZpZXIubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJEZXBsb3ltZW50UnVuU3RvcmUgcmVxdWlyZXMgYSBtb3VudElkZW50aWZpZXJcIilcbiAgICB9XG5cbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uXG4gICAgdGhpcy5kYXRhYmFzZUlkZW50aWZpZXIgPSBkYXRhYmFzZUlkZW50aWZpZXIgfHwgXCJkZWZhdWx0XCJcbiAgICB0aGlzLm1vdW50SWRlbnRpZmllciA9IG1vdW50SWRlbnRpZmllclxuICAgIHRoaXMuc3RhbGVSdW5UaW1lb3V0TXMgPSBzdGFsZVJ1blRpbWVvdXRNc1xuICAgIC8qKiBAdHlwZSB7UHJvbWlzZTx2b2lkPiB8IG51bGx9ICovXG4gICAgdGhpcy5fcmVhZHlQcm9taXNlID0gbnVsbFxuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZXMgdGhlIHJ1biBhbmQgYXVkaXQgdGFibGVzIGV4aXN0LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIHRoZSBzY2hlbWEgaXMgcmVhZHkuXG4gICAqL1xuICBhc3luYyBlbnN1cmVSZWFkeSgpIHtcbiAgICBpZiAodGhpcy5fcmVhZHlQcm9taXNlKSB7XG4gICAgICBhd2FpdCB0aGlzLl9yZWFkeVByb21pc2VcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHRoaXMuX3JlYWR5UHJvbWlzZSA9IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IGFjcXVpcmVkID0gYXdhaXQgZGIuYWNxdWlyZUFkdmlzb3J5TG9jayhTQ0hFTUFfTE9DS19OQU1FKVxuXG4gICAgICBpZiAoIWFjcXVpcmVkKSB0aHJvdyBuZXcgRXJyb3IoXCJGYWlsZWQgdG8gYWNxdWlyZSBkZXBsb3ltZW50IEFQSSBzY2hlbWEgbG9ja1wiKVxuXG4gICAgICB0cnkge1xuICAgICAgICBkYi5jbGVhclNjaGVtYUNhY2hlKClcblxuICAgICAgICBpZiAoIShhd2FpdCBkYi50YWJsZUV4aXN0cyhSVU5TX1RBQkxFKSkpIHtcbiAgICAgICAgICBjb25zdCB0YWJsZSA9IG5ldyBUYWJsZURhdGEoUlVOU19UQUJMRSwge2lmTm90RXhpc3RzOiB0cnVlfSlcblxuICAgICAgICAgIHRhYmxlLnN0cmluZyhcImlkXCIsIHtudWxsOiBmYWxzZSwgcHJpbWFyeUtleTogdHJ1ZX0pXG4gICAgICAgICAgdGFibGUuc3RyaW5nKFwibW91bnRfaWRlbnRpZmllclwiLCB7bWF4TGVuZ3RoOiA2NCwgbnVsbDogZmFsc2V9KVxuICAgICAgICAgIHRhYmxlLnN0cmluZyhcInByb2plY3RcIiwge251bGw6IGZhbHNlLCBpbmRleDogdHJ1ZX0pXG4gICAgICAgICAgdGFibGUuc3RyaW5nKFwic3RhZ2VcIiwge251bGw6IGZhbHNlfSlcbiAgICAgICAgICB0YWJsZS5zdHJpbmcoXCJyZXZpc2lvblwiLCB7bnVsbDogZmFsc2V9KVxuICAgICAgICAgIHRhYmxlLnN0cmluZyhcImlkZW1wb3RlbmN5X2tleVwiLCB7bnVsbDogZmFsc2V9KVxuICAgICAgICAgIHRhYmxlLnN0cmluZyhcInN0YXR1c1wiLCB7bnVsbDogZmFsc2UsIGluZGV4OiB0cnVlfSlcbiAgICAgICAgICB0YWJsZS50ZXh0KFwicmVzdWx0X2pzb25cIiwge251bGw6IHRydWV9KVxuICAgICAgICAgIHRhYmxlLnRleHQoXCJlcnJvcl9qc29uXCIsIHtudWxsOiB0cnVlfSlcbiAgICAgICAgICB0YWJsZS5iaWdpbnQoXCJyZXF1ZXN0ZWRfYXRfbXNcIiwge251bGw6IGZhbHNlfSlcbiAgICAgICAgICB0YWJsZS5iaWdpbnQoXCJzdGFydGVkX2F0X21zXCIsIHtudWxsOiB0cnVlfSlcbiAgICAgICAgICB0YWJsZS5iaWdpbnQoXCJmaW5pc2hlZF9hdF9tc1wiLCB7bnVsbDogdHJ1ZX0pXG4gICAgICAgICAgdGFibGUuc3RyaW5nKFwib3duZXJfdG9rZW5cIiwge251bGw6IHRydWV9KVxuICAgICAgICAgIHRhYmxlLmJpZ2ludChcImhlYXJ0YmVhdF9hdF9tc1wiLCB7bnVsbDogdHJ1ZX0pXG4gICAgICAgICAgdGFibGUuYWRkSW5kZXgobmV3IFRhYmxlSW5kZXgoW1wibW91bnRfaWRlbnRpZmllclwiLCBcImlkZW1wb3RlbmN5X2tleVwiXSwge1xuICAgICAgICAgICAgbmFtZTogSURFTVBPVEVOQ1lfSU5ERVhfTkFNRSxcbiAgICAgICAgICAgIHVuaXF1ZTogdHJ1ZVxuICAgICAgICAgIH0pKVxuXG4gICAgICAgICAgYXdhaXQgZGIuY3JlYXRlVGFibGUodGFibGUpXG4gICAgICAgIH1cblxuICAgICAgICBhd2FpdCB0aGlzLl9lbnN1cmVSdW5Db2x1bW5zKGRiKVxuICAgICAgICBhd2FpdCB0aGlzLl9lbnN1cmVTY29wZWRJZGVtcG90ZW5jeUluZGV4KGRiKVxuXG4gICAgICAgIGlmICghKGF3YWl0IGRiLnRhYmxlRXhpc3RzKEFVRElUX1RBQkxFKSkpIHtcbiAgICAgICAgICBjb25zdCB0YWJsZSA9IG5ldyBUYWJsZURhdGEoQVVESVRfVEFCTEUsIHtpZk5vdEV4aXN0czogdHJ1ZX0pXG5cbiAgICAgICAgICB0YWJsZS5zdHJpbmcoXCJpZFwiLCB7bnVsbDogZmFsc2UsIHByaW1hcnlLZXk6IHRydWV9KVxuICAgICAgICAgIHRhYmxlLnN0cmluZyhcIm1vdW50X2lkZW50aWZpZXJcIiwge21heExlbmd0aDogNjQsIG51bGw6IGZhbHNlfSlcbiAgICAgICAgICB0YWJsZS5zdHJpbmcoXCJydW5faWRcIiwge251bGw6IHRydWUsIGluZGV4OiB0cnVlfSlcbiAgICAgICAgICB0YWJsZS5zdHJpbmcoXCJldmVudFwiLCB7bnVsbDogZmFsc2UsIGluZGV4OiB0cnVlfSlcbiAgICAgICAgICB0YWJsZS50ZXh0KFwicGF5bG9hZF9qc29uXCIsIHtudWxsOiB0cnVlfSlcbiAgICAgICAgICB0YWJsZS5iaWdpbnQoXCJjcmVhdGVkX2F0X21zXCIsIHtudWxsOiBmYWxzZX0pXG5cbiAgICAgICAgICBhd2FpdCBkYi5jcmVhdGVUYWJsZSh0YWJsZSlcbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMuX2Vuc3VyZU1vdW50SWRlbnRpZmllckNvbHVtbihkYiwgQVVESVRfVEFCTEUpXG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBkYi5yZWxlYXNlQWR2aXNvcnlMb2NrKFNDSEVNQV9MT0NLX05BTUUpXG4gICAgICB9XG4gICAgfSlcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9yZWFkeVByb21pc2VcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5fcmVhZHlQcm9taXNlID0gbnVsbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBhIHJ1biBieSBpZC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIC0gUnVuIGlkLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxEZXBsb3ltZW50UnVuUm93IHwgbnVsbD59IC0gVGhlIHJ1biBvciBudWxsLlxuICAgKi9cbiAgYXN5bmMgZmluZFJ1bkJ5SWQoaWQpIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiBhd2FpdCB0aGlzLl9maW5kUnVuQnlJZChkYiwgaWQpKVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIGEgcnVuIGJ5IGlkZW1wb3RlbmN5IGtleS5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkZW1wb3RlbmN5S2V5IC0gSWRlbXBvdGVuY3kga2V5LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxEZXBsb3ltZW50UnVuUm93IHwgbnVsbD59IC0gVGhlIHJ1biBvciBudWxsLlxuICAgKi9cbiAgYXN5bmMgZmluZFJ1bkJ5S2V5KGlkZW1wb3RlbmN5S2V5KSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChkYikgPT4gYXdhaXQgdGhpcy5fZmluZFJ1bkJ5S2V5KGRiLCBpZGVtcG90ZW5jeUtleSkpXG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHJ1biBmZW5jZWQgYnkgdHdvIGFkdmlzb3J5IGxvY2tzIHRha2VuIGluIGEgY29uc2lzdGVudCBnbG9iYWxcbiAgICogb3JkZXIg4oCUIGZpcnN0IHRoZSBwcm9qZWN0L3N0YWdlIGRlcGxveW1lbnQgbG9jaywgdGhlbiB0aGUgaWRlbXBvdGVuY3kta2V5XG4gICAqIGxvY2sg4oCUIHNvIGNvbmN1cnJlbnQgcmVxdWVzdHMgZ2V0IGEgZGV0ZXJtaW5pc3RpYyBvdXRjb21lIGluc3RlYWQgb2ZcbiAgICogZHVwbGljYXRlIGRlcGxveW1lbnRzIG9yIHVuaXF1ZS1jb25zdHJhaW50IGVycm9yczogYW4gaWRlbnRpY2FsIGV4aXN0aW5nXG4gICAqIGtleSByZXBsYXlzLCBhIGRpZmZlcmVudCBwYXlsb2FkIGZvciBhbiBleGlzdGluZyBrZXkgY29uZmxpY3RzLCBhbmQgYW5cbiAgICogYWN0aXZlIHJ1biBmb3IgdGhlIHNhbWUgcHJvamVjdC9zdGFnZSBibG9ja3MgdW5sZXNzIGl0cyBvd25lcnNoaXAgbGVhc2VcbiAgICogZXhwaXJlZCwgaW4gd2hpY2ggY2FzZSBpdCBpcyB0ZXJtaW5hbGx5IHJlY29uY2lsZWQgYXMgaW50ZXJydXB0ZWQgZmlyc3QuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuaWRlbXBvdGVuY3lLZXkgLSBJZGVtcG90ZW5jeSBrZXkuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnByb2plY3QgLSBQcm9qZWN0IGlkZW50aWZpZXIuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnJldmlzaW9uIC0gUmVxdWVzdGVkIHJldmlzaW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5zdGFnZSAtIFN0YWdlIGlkZW50aWZpZXIuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPENyZWF0ZVJ1bk91dGNvbWU+fSAtIFRoZSBvdXRjb21lIGFuZCB0aGUgcmVsZXZhbnQgcnVuLlxuICAgKi9cbiAgYXN5bmMgY3JlYXRlUnVuSWZQb3NzaWJsZSh7aWRlbXBvdGVuY3lLZXksIHByb2plY3QsIHJldmlzaW9uLCBzdGFnZX0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiB7XG4gICAgICBjb25zdCBsb2NrTmFtZSA9IHRoaXMuX2RlcGxveW1lbnRMb2NrTmFtZSh7cHJvamVjdCwgc3RhZ2V9KVxuICAgICAgY29uc3QgYWNxdWlyZWQgPSBhd2FpdCBkYi50cnlBY3F1aXJlQWR2aXNvcnlMb2NrKGxvY2tOYW1lKVxuXG4gICAgICBpZiAoIWFjcXVpcmVkKSB7XG4gICAgICAgIC8vIFRoZSBsb2NrIGJlaW5nIGhlbGQgcHJvdmVzIGFub3RoZXIgcmVxdWVzdCBpcyBtaWQtY3JlYXRpb24gZm9yIHRoaXNcbiAgICAgICAgLy8gcHJvamVjdC9zdGFnZSwgc28gdGhpcyBpcyBhIGJvdW5kZWQgY29uZmxpY3QgZXZlbiBiZWZvcmUgdGhlIGFjdGl2ZVxuICAgICAgICAvLyBydW4gcm93IGlzIHZpc2libGUuXG4gICAgICAgIHJldHVybiB7b3V0Y29tZTogXCJpbl9wcm9ncmVzc1wiLCBydW46IGF3YWl0IHRoaXMuX2ZpbmRCbG9ja2luZ1J1bihkYiwge3Byb2plY3QsIHN0YWdlfSl9XG4gICAgICB9XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIFRoZSBrZXkgbG9jayBzZXJpYWxpemVzIHRoaXMgbW91bnQncyBpZGVtcG90ZW5jeSBrZXkgYWNyb3NzIGRpZmZlcmVudFxuICAgICAgICAvLyBwcm9qZWN0L3N0YWdlIHBhaXJzOyBhbHdheXMgdGFrZW4gYWZ0ZXIgdGhlIGRlcGxveW1lbnQgbG9jayBzbyBsb2NrXG4gICAgICAgIC8vIG9yZGVyaW5nIGNhbiBuZXZlciBkZWFkbG9jay5cbiAgICAgICAgY29uc3Qga2V5TG9ja05hbWUgPSB0aGlzLl9pZGVtcG90ZW5jeUxvY2tOYW1lKGlkZW1wb3RlbmN5S2V5KVxuXG4gICAgICAgIGF3YWl0IGRiLmFjcXVpcmVBZHZpc29yeUxvY2soa2V5TG9ja05hbWUpXG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBleGlzdGluZyA9IGF3YWl0IHRoaXMuX2ZpbmRSdW5CeUtleShkYiwgaWRlbXBvdGVuY3lLZXkpXG5cbiAgICAgICAgICBpZiAoZXhpc3RpbmcpIHtcbiAgICAgICAgICAgIGNvbnN0IHNhbWVQYXlsb2FkID0gZXhpc3RpbmcucHJvamVjdCA9PT0gcHJvamVjdCAmJiBleGlzdGluZy5zdGFnZSA9PT0gc3RhZ2UgJiYgZXhpc3RpbmcucmV2aXNpb24gPT09IHJldmlzaW9uXG5cbiAgICAgICAgICAgIHJldHVybiB7b3V0Y29tZTogc2FtZVBheWxvYWQgPyBcInJlcGxheVwiIDogXCJjb25mbGljdFwiLCBydW46IGV4aXN0aW5nfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGFjdGl2ZVJ1biA9IGF3YWl0IHRoaXMuX2ZpbmRCbG9ja2luZ1J1bihkYiwge3Byb2plY3QsIHN0YWdlfSlcblxuICAgICAgICAgIGlmIChhY3RpdmVSdW4pIHtcbiAgICAgICAgICAgIGlmIChhY3RpdmVSdW4uc3RhdHVzID09PSBcInJlY29uY2lsaWF0aW9uX3JlcXVpcmVkXCIpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHtvdXRjb21lOiBcInJlY29uY2lsaWF0aW9uX3JlcXVpcmVkXCIsIHJ1bjogYWN0aXZlUnVufVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIXRoaXMuX2lzUnVuSW50ZXJydXB0ZWQoYWN0aXZlUnVuKSkgcmV0dXJuIHtvdXRjb21lOiBcImluX3Byb2dyZXNzXCIsIHJ1bjogYWN0aXZlUnVufVxuXG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9yZWNvbmNpbGVFeHBpcmVkUnVuKGRiLCBhY3RpdmVSdW4pXG5cbiAgICAgICAgICAgIGlmIChhY3RpdmVSdW4uc3RhdHVzID09PSBcInJ1bm5pbmdcIikge1xuICAgICAgICAgICAgICBjb25zdCByZWNvbmNpbGVkUnVuID0gYXdhaXQgdGhpcy5fZmluZFJ1bkJ5SWQoZGIsIGFjdGl2ZVJ1bi5pZClcblxuICAgICAgICAgICAgICBpZiAoIXJlY29uY2lsZWRSdW4pIHRocm93IG5ldyBFcnJvcihgUmVjb25jaWxlZCBkZXBsb3ltZW50IHJ1biAke2FjdGl2ZVJ1bi5pZH0gY291bGQgbm90IGJlIGZvdW5kYClcblxuICAgICAgICAgICAgICByZXR1cm4ge291dGNvbWU6IFwicmVjb25jaWxpYXRpb25fcmVxdWlyZWRcIiwgcnVuOiByZWNvbmNpbGVkUnVufVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGlkID0gcmFuZG9tVVVJRCgpXG5cbiAgICAgICAgICBhd2FpdCBkYi5pbnNlcnQoe1xuICAgICAgICAgICAgdGFibGVOYW1lOiBSVU5TX1RBQkxFLFxuICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICBpZCxcbiAgICAgICAgICAgICAgbW91bnRfaWRlbnRpZmllcjogdGhpcy5tb3VudElkZW50aWZpZXIsXG4gICAgICAgICAgICAgIHByb2plY3QsXG4gICAgICAgICAgICAgIHN0YWdlLFxuICAgICAgICAgICAgICByZXZpc2lvbixcbiAgICAgICAgICAgICAgaWRlbXBvdGVuY3lfa2V5OiBpZGVtcG90ZW5jeUtleSxcbiAgICAgICAgICAgICAgc3RhdHVzOiBcInBlbmRpbmdcIixcbiAgICAgICAgICAgICAgcmVzdWx0X2pzb246IG51bGwsXG4gICAgICAgICAgICAgIGVycm9yX2pzb246IG51bGwsXG4gICAgICAgICAgICAgIHJlcXVlc3RlZF9hdF9tczogRGF0ZS5ub3coKSxcbiAgICAgICAgICAgICAgc3RhcnRlZF9hdF9tczogbnVsbCxcbiAgICAgICAgICAgICAgZmluaXNoZWRfYXRfbXM6IG51bGwsXG4gICAgICAgICAgICAgIG93bmVyX3Rva2VuOiBQUk9DRVNTX09XTkVSX1RPS0VOLFxuICAgICAgICAgICAgICBoZWFydGJlYXRfYXRfbXM6IG51bGxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgY29uc3QgcnVuID0gYXdhaXQgdGhpcy5fZmluZFJ1bkJ5SWQoZGIsIGlkKVxuXG4gICAgICAgICAgaWYgKCFydW4pIHRocm93IG5ldyBFcnJvcihgRGVwbG95bWVudCBydW4gJHtpZH0gd2FzIG5vdCBmb3VuZCByaWdodCBhZnRlciBpbnNlcnRpb25gKVxuXG4gICAgICAgICAgcmV0dXJuIHtvdXRjb21lOiBcImNyZWF0ZWRcIiwgcnVufVxuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIGF3YWl0IGRiLnJlbGVhc2VBZHZpc29yeUxvY2soa2V5TG9ja05hbWUpXG4gICAgICAgIH1cbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGRiLnJlbGVhc2VBZHZpc29yeUxvY2sobG9ja05hbWUpXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBNYXJrcyBhIHJ1biBhcyBydW5uaW5nLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmlkIC0gUnVuIGlkLlxuICAgKiBAcGFyYW0ge251bWJlcn0gYXJncy5zdGFydGVkQXRNcyAtIFN0YXJ0IHRpbWUgaW4gbXMgZXBvY2guXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gdXBkYXRlZC5cbiAgICovXG4gIGFzeW5jIG1hcmtSdW5uaW5nKHtpZCwgc3RhcnRlZEF0TXN9KSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG4gICAgYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChkYikgPT4ge1xuICAgICAgY29uc3QgYWZmZWN0ZWQgPSBhd2FpdCBkYi5hZmZlY3RlZFJvd3MoZGIudXBkYXRlU3FsKHtcbiAgICAgICAgdGFibGVOYW1lOiBSVU5TX1RBQkxFLFxuICAgICAgICBkYXRhOiB7aGVhcnRiZWF0X2F0X21zOiBzdGFydGVkQXRNcywgc3RhcnRlZF9hdF9tczogc3RhcnRlZEF0TXMsIHN0YXR1czogXCJydW5uaW5nXCJ9LFxuICAgICAgICBjb25kaXRpb25zOiB7XG4gICAgICAgICAgaWQsXG4gICAgICAgICAgbW91bnRfaWRlbnRpZmllcjogdGhpcy5tb3VudElkZW50aWZpZXIsXG4gICAgICAgICAgb3duZXJfdG9rZW46IFBST0NFU1NfT1dORVJfVE9LRU4sXG4gICAgICAgICAgc3RhdHVzOiBcInBlbmRpbmdcIlxuICAgICAgICB9XG4gICAgICB9KSlcblxuICAgICAgaWYgKGFmZmVjdGVkICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgdG8gbWFyayBleGFjdGx5IG9uZSBwZW5kaW5nIGRlcGxveW1lbnQgcnVuIHdpdGggaWQgJHtpZH0gYXMgcnVubmluZyBmb3IgdGhpcyBwcm9jZXNzIG93bmVyLCBidXQgdXBkYXRlZCAke2FmZmVjdGVkfWApXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW5ld3MgdGhlIG93bmVyc2hpcCBsZWFzZSBvZiBhIHJ1biBleGVjdXRpbmcgaW4gdGhpcyBwcm9jZXNzLiBBIG5vLW9wXG4gICAqIHdoZW4gdGhlIHJ1biBhbHJlYWR5IHJlYWNoZWQgYSB0ZXJtaW5hbCBzdGF0ZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5pZCAtIFJ1biBpZC5cbiAgICogQHBhcmFtIHtudW1iZXJ9IGFyZ3MuaGVhcnRiZWF0QXRNcyAtIEhlYXJ0YmVhdCB0aW1lIGluIG1zIGVwb2NoLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIHVwZGF0ZWQuXG4gICAqL1xuICBhc3luYyBoZWFydGJlYXQoe2hlYXJ0YmVhdEF0TXMsIGlkfSkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuICAgIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGF3YWl0IGRiLmFmZmVjdGVkUm93cyhkYi51cGRhdGVTcWwoe1xuICAgICAgICB0YWJsZU5hbWU6IFJVTlNfVEFCTEUsXG4gICAgICAgIGRhdGE6IHtoZWFydGJlYXRfYXRfbXM6IGhlYXJ0YmVhdEF0TXN9LFxuICAgICAgICBjb25kaXRpb25zOiB7aWQsIG1vdW50X2lkZW50aWZpZXI6IHRoaXMubW91bnRJZGVudGlmaWVyLCBvd25lcl90b2tlbjogUFJPQ0VTU19PV05FUl9UT0tFTiwgc3RhdHVzOiBcInJ1bm5pbmdcIn1cbiAgICAgIH0pKVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogTWFya3MgYSBydW4gYXMgc3VjY2VlZGVkIHdpdGggaXRzIHNhbml0aXplZCByZXN1bHQuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuaWQgLSBSdW4gaWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLm93bmVyVG9rZW4gLSBFeHBlY3RlZCBleGVjdXRpb24gb3duZXIgdG9rZW4uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBhcmdzLmZpbmlzaGVkQXRNcyAtIEZpbmlzaCB0aW1lIGluIG1zIGVwb2NoLlxuICAgKiBAcGFyYW0gez99IGFyZ3MucmVzdWx0IC0gU2FuaXRpemVkIGFkYXB0ZXIgcmVwb3J0LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIHVwZGF0ZWQuXG4gICAqL1xuICBhc3luYyBtYXJrU3VjY2VlZGVkKHtmaW5pc2hlZEF0TXMsIGlkLCBvd25lclRva2VuLCByZXN1bHR9KSB7XG4gICAgYXdhaXQgdGhpcy5fdHJhbnNpdGlvblJ1bm5pbmdSdW4oe1xuICAgICAgZGF0YToge2ZpbmlzaGVkX2F0X21zOiBmaW5pc2hlZEF0TXMsIHJlc3VsdF9qc29uOiBKU09OLnN0cmluZ2lmeShyZXN1bHQgPz8gbnVsbCksIHN0YXR1czogXCJzdWNjZWVkZWRcIn0sXG4gICAgICBpZCxcbiAgICAgIG93bmVyVG9rZW5cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIE1hcmtzIGEgcnVuIGFzIGZhaWxlZCB3aXRoIGl0cyBzYW5pdGl6ZWQgZmFpbHVyZSBwYXlsb2FkLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmlkIC0gUnVuIGlkLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5vd25lclRva2VuIC0gRXhwZWN0ZWQgZXhlY3V0aW9uIG93bmVyIHRva2VuLlxuICAgKiBAcGFyYW0ge251bWJlcn0gYXJncy5maW5pc2hlZEF0TXMgLSBGaW5pc2ggdGltZSBpbiBtcyBlcG9jaC5cbiAgICogQHBhcmFtIHs/fSBhcmdzLmVycm9yIC0gU2FuaXRpemVkIGZhaWx1cmUgcGF5bG9hZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiB1cGRhdGVkLlxuICAgKi9cbiAgYXN5bmMgbWFya0ZhaWxlZCh7ZXJyb3IsIGZpbmlzaGVkQXRNcywgaWQsIG93bmVyVG9rZW59KSB7XG4gICAgYXdhaXQgdGhpcy5fdHJhbnNpdGlvblJ1bm5pbmdSdW4oe1xuICAgICAgZGF0YToge2Vycm9yX2pzb246IEpTT04uc3RyaW5naWZ5KGVycm9yID8/IG51bGwpLCBmaW5pc2hlZF9hdF9tczogZmluaXNoZWRBdE1zLCBzdGF0dXM6IFwiZmFpbGVkXCJ9LFxuICAgICAgaWQsXG4gICAgICBvd25lclRva2VuXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBGZW5jZXMgYSBydW4gYWZ0ZXIgdGhlIGFkYXB0ZXIgbWF5IGhhdmUgYWN0aXZhdGVkIGV4dGVybmFsbHkgYnV0IGl0c1xuICAgKiBzdWNjZXNzZnVsIHJlc3VsdCBjb3VsZCBub3QgYmUgZHVyYWJseSByZWNvcmRlZC4gVGhpcyBzdGF0ZSBibG9ja3MgZnV0dXJlXG4gICAqIGRlcGxveW1lbnRzIHVudGlsIGFuIG9wZXJhdG9yIHJlY29uY2lsZXMgdGhlIGV4dGVybmFsIG91dGNvbWUuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuaWQgLSBSdW4gaWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLm93bmVyVG9rZW4gLSBFeHBlY3RlZCBleGVjdXRpb24gb3duZXIgdG9rZW4uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBhcmdzLmZpbmlzaGVkQXRNcyAtIFRpbWUgcmVjb25jaWxpYXRpb24gYmVjYW1lIG5lY2Vzc2FyeSBpbiBtcyBlcG9jaC5cbiAgICogQHBhcmFtIHs/fSBhcmdzLmVycm9yIC0gU2FuaXRpemVkIG9wZXJhdG9yLWZhY2luZyBleHBsYW5hdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiB1cGRhdGVkLlxuICAgKi9cbiAgYXN5bmMgbWFya1JlY29uY2lsaWF0aW9uUmVxdWlyZWQoe2Vycm9yLCBmaW5pc2hlZEF0TXMsIGlkLCBvd25lclRva2VufSkge1xuICAgIGF3YWl0IHRoaXMuX3RyYW5zaXRpb25SdW5uaW5nUnVuKHtcbiAgICAgIGRhdGE6IHtlcnJvcl9qc29uOiBKU09OLnN0cmluZ2lmeShlcnJvciA/PyBudWxsKSwgZmluaXNoZWRfYXRfbXM6IGZpbmlzaGVkQXRNcywgc3RhdHVzOiBcInJlY29uY2lsaWF0aW9uX3JlcXVpcmVkXCJ9LFxuICAgICAgaWQsXG4gICAgICBvd25lclRva2VuXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmRzIGEgc2FuaXRpemVkIGF1ZGl0IGV2ZW50LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmV2ZW50IC0gRXZlbnQgbmFtZS5cbiAgICogQHBhcmFtIHs/fSBhcmdzLnBheWxvYWQgLSBTYW5pdGl6ZWQgcGF5bG9hZC5cbiAgICogQHBhcmFtIHtzdHJpbmcgfCBudWxsfSBhcmdzLnJ1bklkIC0gT3duaW5nIHJ1biBpZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiByZWNvcmRlZC5cbiAgICovXG4gIGFzeW5jIGFkZEF1ZGl0RXZlbnQoe2V2ZW50LCBwYXlsb2FkLCBydW5JZH0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcbiAgICBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiB7XG4gICAgICBhd2FpdCBkYi5pbnNlcnQoe1xuICAgICAgICB0YWJsZU5hbWU6IEFVRElUX1RBQkxFLFxuICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgaWQ6IHJhbmRvbVVVSUQoKSxcbiAgICAgICAgICBtb3VudF9pZGVudGlmaWVyOiB0aGlzLm1vdW50SWRlbnRpZmllcixcbiAgICAgICAgICBydW5faWQ6IHJ1bklkLFxuICAgICAgICAgIGV2ZW50LFxuICAgICAgICAgIHBheWxvYWRfanNvbjogcGF5bG9hZCA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQgPz8gbnVsbCksXG4gICAgICAgICAgY3JlYXRlZF9hdF9tczogRGF0ZS5ub3coKVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogTGlzdHMgYXVkaXQgZXZlbnRzIGZvciBhIHJ1biBpbiBjcmVhdGlvbiBvcmRlci5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5ydW5JZCAtIFJ1biBpZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8RGVwbG95bWVudEF1ZGl0RXZlbnRSb3dbXT59IC0gQXVkaXQgZXZlbnRzLlxuICAgKi9cbiAgYXN5bmMgbGlzdEF1ZGl0RXZlbnRzKHtydW5JZH0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiB7XG4gICAgICBjb25zdCByb3dzID0gYXdhaXQgZGJcbiAgICAgICAgLm5ld1F1ZXJ5KClcbiAgICAgICAgLmZyb20oQVVESVRfVEFCTEUpXG4gICAgICAgIC53aGVyZSh7bW91bnRfaWRlbnRpZmllcjogdGhpcy5tb3VudElkZW50aWZpZXIsIHJ1bl9pZDogcnVuSWR9KVxuICAgICAgICAub3JkZXIoXCJjcmVhdGVkX2F0X21zIEFTQ1wiKVxuICAgICAgICAucmVzdWx0cygpXG5cbiAgICAgIHJldHVybiByb3dzLm1hcCgocm93KSA9PiB0aGlzLl9hdWRpdFJvd0Zyb21EYihyb3cpKVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIHRoZSBhZHZpc29yeS1sb2NrIG5hbWUgZm9yIGEgcHJvamVjdC9zdGFnZSBwYWlyLiBIYXNoZWQgc28gdGhlIG5hbWVcbiAgICogc3RheXMgd2l0aGluIGRyaXZlciBsaW1pdHMgKE15U1FML01hcmlhREIgY2FwIGxvY2sgbmFtZXMgYXQgNjQgY2hhcnMpXG4gICAqIHJlZ2FyZGxlc3Mgb2YgaWRlbnRpZmllciBsZW5ndGguXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MucHJvamVjdCAtIFByb2plY3QgaWRlbnRpZmllci5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3Muc3RhZ2UgLSBTdGFnZSBpZGVudGlmaWVyLlxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSAtIEJvdW5kZWQgYWR2aXNvcnktbG9jayBuYW1lLlxuICAgKi9cbiAgX2RlcGxveW1lbnRMb2NrTmFtZSh7cHJvamVjdCwgc3RhZ2V9KSB7XG4gICAgY29uc3QgaGFzaCA9IGNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKGAke3RoaXMubW91bnRJZGVudGlmaWVyfToke3Byb2plY3R9OiR7c3RhZ2V9YCkuZGlnZXN0KFwiaGV4XCIpLnNsaWNlKDAsIDMyKVxuXG4gICAgcmV0dXJuIGB2ZWxvY2lvdXMtZGVwbG95bWVudC1hcGk6JHtoYXNofWBcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIGFkdmlzb3J5LWxvY2sgbmFtZSBzZXJpYWxpemluZyBvbmUgaWRlbXBvdGVuY3kga2V5IHdpdGhpbiB0aGlzIG1vdW50LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRlbXBvdGVuY3lLZXkgLSBJZGVtcG90ZW5jeSBrZXkuXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IC0gQm91bmRlZCBhZHZpc29yeS1sb2NrIG5hbWUuXG4gICAqL1xuICBfaWRlbXBvdGVuY3lMb2NrTmFtZShpZGVtcG90ZW5jeUtleSkge1xuICAgIGNvbnN0IGhhc2ggPSBjcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShgJHt0aGlzLm1vdW50SWRlbnRpZmllcn06JHtpZGVtcG90ZW5jeUtleX1gKS5kaWdlc3QoXCJoZXhcIikuc2xpY2UoMCwgMzIpXG5cbiAgICByZXR1cm4gYHZlbG9jaW91cy1kZXBsb3ltZW50LWFwaTprZXk6JHtoYXNofWBcbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNpZGVzIHdoZXRoZXIgYW4gYWN0aXZlIHJ1biBjb3VudHMgYXMgaW50ZXJydXB0ZWQ6IGl0cyBvd25lciBsZWFzZVxuICAgKiAoaGVhcnRiZWF0LCBvciB0aGUgcmVxdWVzdCB0aW1lIHdoZW4gaXQgbmV2ZXIgc3RhcnRlZCkgaXMgb2xkZXIgdGhhbiB0aGVcbiAgICogY29uZmlndXJlZCB0aW1lb3V0IGFuZCBpdCBpcyBub3QgZXhlY3V0aW5nIGluIHRoaXMgcHJvY2Vzcy4gQSBydW4gd2l0aCBhXG4gICAqIGZyZXNoIGxlYXNlIG93bmVkIGJ5IGFub3RoZXIgd29ya2VyIGlzIGdlbnVpbmVseSBhY3RpdmUgYW5kIG11c3QgbmV2ZXIgYmVcbiAgICogcmVjbGFpbWVkIGhlcmUuXG4gICAqIEBwYXJhbSB7RGVwbG95bWVudFJ1blJvd30gcnVuIC0gQWN0aXZlIHJ1bi5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gV2hldGhlciB0aGUgcnVuIGlzIGludGVycnVwdGVkLlxuICAgKi9cbiAgX2lzUnVuSW50ZXJydXB0ZWQocnVuKSB7XG4gICAgaWYgKGFjdGl2ZVJ1bklkc0luUHJvY2Vzcy5oYXMocnVuLmlkKSkgcmV0dXJuIGZhbHNlXG5cbiAgICBjb25zdCBsZWFzZVJlZmVyZW5jZU1zID0gcnVuLmhlYXJ0YmVhdEF0TXMgPz8gcnVuLnJlcXVlc3RlZEF0TXNcblxuICAgIHJldHVybiBEYXRlLm5vdygpIC0gbGVhc2VSZWZlcmVuY2VNcyA+IHRoaXMuc3RhbGVSdW5UaW1lb3V0TXNcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvbmNpbGVzIGFuIGV4cGlyZWQgb3duZXJzaGlwIGxlYXNlLiBQZW5kaW5nIHdvcmsgaXMgc2FmZWx5IGludGVycnVwdGVkXG4gICAqIGJlY2F1c2UgZXh0ZXJuYWwgYWN0aXZhdGlvbiBuZXZlciBiZWdhbi4gUnVubmluZyB3b3JrIHJlcXVpcmVzIG9wZXJhdG9yXG4gICAqIHJlY29uY2lsaWF0aW9uIGJlY2F1c2UgdGhlIGV4dGVybmFsIG91dGNvbWUgbWF5IGFscmVhZHkgaGF2ZSBjaGFuZ2VkLlxuICAgKiBSdW5zIGluc2lkZSB0aGUgcHJvamVjdC9zdGFnZSBkZXBsb3ltZW50IGxvY2ssIHNvIGV4YWN0bHkgb25lIHJlY29uY2lsZXJcbiAgICogd2lucy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSBjb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge0RlcGxveW1lbnRSdW5Sb3d9IHJ1biAtIEludGVycnVwdGVkIHJ1bi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiByZWNvbmNpbGVkLlxuICAgKi9cbiAgYXN5bmMgX3JlY29uY2lsZUV4cGlyZWRSdW4oZGIsIHJ1bikge1xuICAgIGNvbnN0IGZpbmlzaGVkQXRNcyA9IERhdGUubm93KClcbiAgICBjb25zdCByZWNvbmNpbGlhdGlvblJlcXVpcmVkID0gcnVuLnN0YXR1cyA9PT0gXCJydW5uaW5nXCJcbiAgICBjb25zdCBlcnJvclBheWxvYWQgPSByZWNvbmNpbGlhdGlvblJlcXVpcmVkXG4gICAgICA/IHttZXNzYWdlOiBcIkRlcGxveW1lbnQgb3V0Y29tZSByZXF1aXJlcyBvcGVyYXRvciByZWNvbmNpbGlhdGlvbiBhZnRlciB0aGUgZXhlY3V0aW9uIG93bmVyIGxlYXNlIGV4cGlyZWRcIn1cbiAgICAgIDoge21lc3NhZ2U6IFwiRGVwbG95bWVudCBydW4gd2FzIGludGVycnVwdGVkIGJlZm9yZSBleGVjdXRpb24gYmVnYW4gKG93bmVyc2hpcCBsZWFzZSBleHBpcmVkKVwifVxuICAgIGNvbnN0IHN0YXR1cyA9IHJlY29uY2lsaWF0aW9uUmVxdWlyZWQgPyBcInJlY29uY2lsaWF0aW9uX3JlcXVpcmVkXCIgOiBcImludGVycnVwdGVkXCJcbiAgICBjb25zdCBldmVudCA9IHJlY29uY2lsaWF0aW9uUmVxdWlyZWQgPyBcInJ1bl9yZWNvbmNpbGlhdGlvbl9yZXF1aXJlZFwiIDogXCJydW5faW50ZXJydXB0ZWRcIlxuICAgIGNvbnN0IGRhdGEgPSB7ZXJyb3JfanNvbjogSlNPTi5zdHJpbmdpZnkoZXJyb3JQYXlsb2FkKSwgZmluaXNoZWRfYXRfbXM6IGZpbmlzaGVkQXRNcywgc3RhdHVzfVxuICAgIGNvbnN0IGFmZmVjdGVkID0gYXdhaXQgZGIuYWZmZWN0ZWRSb3dzKGRiLnVwZGF0ZVNxbCh7XG4gICAgICB0YWJsZU5hbWU6IFJVTlNfVEFCTEUsXG4gICAgICBkYXRhLFxuICAgICAgY29uZGl0aW9uczoge2lkOiBydW4uaWQsIG1vdW50X2lkZW50aWZpZXI6IHRoaXMubW91bnRJZGVudGlmaWVyLCBvd25lcl90b2tlbjogcnVuLm93bmVyVG9rZW4sIHN0YXR1czogcnVuLnN0YXR1c31cbiAgICB9KSlcblxuICAgIGlmIChhZmZlY3RlZCAhPT0gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCB0byByZWNvbmNpbGUgZXhhY3RseSBvbmUgZXhwaXJlZCBkZXBsb3ltZW50IHJ1biB3aXRoIGlkICR7cnVuLmlkfSwgYnV0IHVwZGF0ZWQgJHthZmZlY3RlZH1gKVxuICAgIH1cblxuICAgIGF3YWl0IGRiLmluc2VydCh7XG4gICAgICB0YWJsZU5hbWU6IEFVRElUX1RBQkxFLFxuICAgICAgZGF0YToge1xuICAgICAgICBpZDogcmFuZG9tVVVJRCgpLFxuICAgICAgICBtb3VudF9pZGVudGlmaWVyOiB0aGlzLm1vdW50SWRlbnRpZmllcixcbiAgICAgICAgcnVuX2lkOiBydW4uaWQsXG4gICAgICAgIGV2ZW50LFxuICAgICAgICBwYXlsb2FkX2pzb246IEpTT04uc3RyaW5naWZ5KHtwcm9qZWN0OiBydW4ucHJvamVjdCwgcmV2aXNpb246IHJ1bi5yZXZpc2lvbiwgc3RhZ2U6IHJ1bi5zdGFnZX0pLFxuICAgICAgICBjcmVhdGVkX2F0X21zOiBEYXRlLm5vdygpXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIHRoZSBvd25lcnNoaXAgbGVhc2UgY29sdW1ucyB0byBhIHJ1bnMgdGFibGUgY3JlYXRlZCBiZWZvcmUgdGhleVxuICAgKiBleGlzdGVkICh0aGUgYmFja2dyb3VuZC1qb2JzIHN0b3JlIGNvbHVtbi11cGdyYWRlIHByZWNlZGVudCkuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2UgY29ubmVjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiB0aGUgY29sdW1ucyBleGlzdC5cbiAgICovXG4gIGFzeW5jIF9lbnN1cmVSdW5Db2x1bW5zKGRiKSB7XG4gICAgY29uc3QgdGFibGUgPSBhd2FpdCBkYi5nZXRUYWJsZUJ5TmFtZU9yRmFpbChSVU5TX1RBQkxFKVxuICAgIGNvbnN0IG1pc3NpbmdNb3VudElkZW50aWZpZXIgPSAhKGF3YWl0IHRhYmxlLmdldENvbHVtbkJ5TmFtZShcIm1vdW50X2lkZW50aWZpZXJcIikpXG4gICAgY29uc3QgbWlzc2luZ093bmVyVG9rZW4gPSAhKGF3YWl0IHRhYmxlLmdldENvbHVtbkJ5TmFtZShcIm93bmVyX3Rva2VuXCIpKVxuICAgIGNvbnN0IG1pc3NpbmdIZWFydGJlYXQgPSAhKGF3YWl0IHRhYmxlLmdldENvbHVtbkJ5TmFtZShcImhlYXJ0YmVhdF9hdF9tc1wiKSlcblxuICAgIGlmIChtaXNzaW5nTW91bnRJZGVudGlmaWVyIHx8IG1pc3NpbmdPd25lclRva2VuIHx8IG1pc3NpbmdIZWFydGJlYXQpIHtcbiAgICAgIGNvbnN0IHRhYmxlRGF0YSA9IG5ldyBUYWJsZURhdGEoUlVOU19UQUJMRSlcblxuICAgICAgaWYgKG1pc3NpbmdNb3VudElkZW50aWZpZXIpIHRhYmxlRGF0YS5zdHJpbmcoXCJtb3VudF9pZGVudGlmaWVyXCIsIHttYXhMZW5ndGg6IDY0LCBudWxsOiB0cnVlfSlcbiAgICAgIGlmIChtaXNzaW5nT3duZXJUb2tlbikgdGFibGVEYXRhLnN0cmluZyhcIm93bmVyX3Rva2VuXCIsIHtudWxsOiB0cnVlfSlcbiAgICAgIGlmIChtaXNzaW5nSGVhcnRiZWF0KSB0YWJsZURhdGEuYmlnaW50KFwiaGVhcnRiZWF0X2F0X21zXCIsIHtudWxsOiB0cnVlfSlcblxuICAgICAgY29uc3Qgc3FscyA9IGF3YWl0IGRiLmFsdGVyVGFibGVTUUxzKHRhYmxlRGF0YSlcblxuICAgICAgZm9yIChjb25zdCBzcWwgb2Ygc3Fscykge1xuICAgICAgICBhd2FpdCBkYi5xdWVyeShzcWwpXG4gICAgICB9XG5cbiAgICAgIGRiLmNsZWFyU2NoZW1hQ2FjaGUoKVxuICAgIH1cblxuICAgIGF3YWl0IHRoaXMuX3F1YXJhbnRpbmVMZWdhY3lSb3dzKGRiLCBSVU5TX1RBQkxFKVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYW5kIHNhZmVseSBiYWNrZmlsbHMgYSBtb3VudCBpZGVudGlmaWVyIG9uIGFuIGV4aXN0aW5nIGxhemlseS1vd25lZCB0YWJsZS5cbiAgICogTGVnYWN5IHJvd3Mgd2VyZSBub3QgYXR0cmlidXRhYmxlIHRvIGFuIGF1dGhlbnRpY2F0ZWQgbW91bnQsIHNvIHRoZXkgYXJlXG4gICAqIHF1YXJhbnRpbmVkIGluIGFuIHVucmVhY2hhYmxlIHNjb3BlIGluc3RlYWQgb2YgYmVpbmcgZXhwb3NlZCB0byB3aGljaGV2ZXJcbiAgICogbW91bnQgaGFwcGVucyB0byBpbml0aWFsaXplIGZpcnN0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIERhdGFiYXNlIGNvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWJsZU5hbWUgLSBMYXppbHktb3duZWQgdGFibGUgbmFtZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiB0aGUgY29sdW1uIGV4aXN0cy5cbiAgICovXG4gIGFzeW5jIF9lbnN1cmVNb3VudElkZW50aWZpZXJDb2x1bW4oZGIsIHRhYmxlTmFtZSkge1xuICAgIGNvbnN0IHRhYmxlID0gYXdhaXQgZGIuZ2V0VGFibGVCeU5hbWVPckZhaWwodGFibGVOYW1lKVxuXG4gICAgaWYgKCEoYXdhaXQgdGFibGUuZ2V0Q29sdW1uQnlOYW1lKFwibW91bnRfaWRlbnRpZmllclwiKSkpIHtcbiAgICAgIGNvbnN0IHRhYmxlRGF0YSA9IG5ldyBUYWJsZURhdGEodGFibGVOYW1lKVxuXG4gICAgICB0YWJsZURhdGEuc3RyaW5nKFwibW91bnRfaWRlbnRpZmllclwiLCB7bWF4TGVuZ3RoOiA2NCwgbnVsbDogdHJ1ZX0pXG5cbiAgICAgIGZvciAoY29uc3Qgc3FsIG9mIGF3YWl0IGRiLmFsdGVyVGFibGVTUUxzKHRhYmxlRGF0YSkpIGF3YWl0IGRiLnF1ZXJ5KHNxbClcblxuICAgICAgZGIuY2xlYXJTY2hlbWFDYWNoZSgpXG4gICAgfVxuXG4gICAgYXdhaXQgdGhpcy5fcXVhcmFudGluZUxlZ2FjeVJvd3MoZGIsIHRhYmxlTmFtZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3NpZ25zIHByZS1zY29wZSByb3dzIHRvIGEgcmVzZXJ2ZWQgaWRlbnRpZmllciBubyByZWFsIG1vdW50IGNhbiB1c2UuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2UgY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHRhYmxlTmFtZSAtIFRhYmxlIGNvbnRhaW5pbmcgbGVnYWN5IHJvd3MuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gbGVnYWN5IHJvd3MgYXJlIHF1YXJhbnRpbmVkLlxuICAgKi9cbiAgYXN5bmMgX3F1YXJhbnRpbmVMZWdhY3lSb3dzKGRiLCB0YWJsZU5hbWUpIHtcbiAgICBhd2FpdCBkYi5hZmZlY3RlZFJvd3MoZGIudXBkYXRlU3FsKHtcbiAgICAgIHRhYmxlTmFtZSxcbiAgICAgIGRhdGE6IHttb3VudF9pZGVudGlmaWVyOiBMRUdBQ1lfVU5TQ09QRURfTU9VTlRfSURFTlRJRklFUn0sXG4gICAgICBjb25kaXRpb25zOiB7bW91bnRfaWRlbnRpZmllcjogbnVsbH1cbiAgICB9KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYWNlcyB0aGUgbGVnYWN5IGdsb2JhbCBpZGVtcG90ZW5jeS1rZXkgdW5pcXVlbmVzcyB3aXRoIG1vdW50LXNjb3BlZFxuICAgKiB1bmlxdWVuZXNzLiBUaGUgc2NvcGVkIGluZGV4IGlzIGNyZWF0ZWQgYmVmb3JlIHRoZSBvbGQgb25lIGlzIHJlbW92ZWQsIHNvXG4gICAqIGFuIGludGVycnVwdGVkIGxhenkgdXBncmFkZSBuZXZlciBsZWF2ZXMgaWRlbXBvdGVuY3kga2V5cyB1bmZlbmNlZC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSBjb25uZWN0aW9uLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGluZGV4IG93bmVyc2hpcCBpcyBzY29wZWQuXG4gICAqL1xuICBhc3luYyBfZW5zdXJlU2NvcGVkSWRlbXBvdGVuY3lJbmRleChkYikge1xuICAgIGRiLmNsZWFyU2NoZW1hQ2FjaGUoKVxuXG4gICAgY29uc3QgdGFibGUgPSBhd2FpdCBkYi5nZXRUYWJsZUJ5TmFtZU9yRmFpbChSVU5TX1RBQkxFKVxuICAgIGNvbnN0IGluZGV4ZXMgPSBhd2FpdCB0YWJsZS5nZXRJbmRleGVzKClcbiAgICBjb25zdCBzY29wZWRJbmRleCA9IGluZGV4ZXMuZmluZCgoaW5kZXgpID0+IHtcbiAgICAgIHJldHVybiBpbmRleC5pc1VuaXF1ZSgpICYmIGluZGV4LmdldENvbHVtbk5hbWVzKCkuam9pbihcIixcIikgPT09IFwibW91bnRfaWRlbnRpZmllcixpZGVtcG90ZW5jeV9rZXlcIlxuICAgIH0pXG5cbiAgICBpZiAoIXNjb3BlZEluZGV4KSB7XG4gICAgICBjb25zdCBzcWxzID0gYXdhaXQgZGIuY3JlYXRlSW5kZXhTUUxzKHtcbiAgICAgICAgY29sdW1uczogW1wibW91bnRfaWRlbnRpZmllclwiLCBcImlkZW1wb3RlbmN5X2tleVwiXSxcbiAgICAgICAgbmFtZTogSURFTVBPVEVOQ1lfSU5ERVhfTkFNRSxcbiAgICAgICAgdGFibGVOYW1lOiBSVU5TX1RBQkxFLFxuICAgICAgICB1bmlxdWU6IHRydWVcbiAgICAgIH0pXG5cbiAgICAgIGZvciAoY29uc3Qgc3FsIG9mIHNxbHMpIGF3YWl0IGRiLnF1ZXJ5KHNxbClcblxuICAgICAgZGIuY2xlYXJTY2hlbWFDYWNoZSgpXG4gICAgfVxuXG4gICAgY29uc3QgcmVmcmVzaGVkVGFibGUgPSBhd2FpdCBkYi5nZXRUYWJsZUJ5TmFtZU9yRmFpbChSVU5TX1RBQkxFKVxuICAgIGNvbnN0IGxlZ2FjeUluZGV4ZXMgPSAoYXdhaXQgcmVmcmVzaGVkVGFibGUuZ2V0SW5kZXhlcygpKS5maWx0ZXIoKGluZGV4KSA9PiB7XG4gICAgICByZXR1cm4gIWluZGV4LmlzUHJpbWFyeUtleSgpICYmIGluZGV4LmlzVW5pcXVlKCkgJiYgaW5kZXguZ2V0Q29sdW1uTmFtZXMoKS5qb2luKFwiLFwiKSA9PT0gXCJpZGVtcG90ZW5jeV9rZXlcIlxuICAgIH0pXG5cbiAgICBmb3IgKGNvbnN0IGxlZ2FjeUluZGV4IG9mIGxlZ2FjeUluZGV4ZXMpIHtcbiAgICAgIGNvbnN0IHNxbHMgPSBhd2FpdCBkYi5yZW1vdmVJbmRleFNRTHMoe25hbWU6IGxlZ2FjeUluZGV4LmdldE5hbWUoKSwgdGFibGVOYW1lOiBSVU5TX1RBQkxFfSlcblxuICAgICAgZm9yIChjb25zdCBzcWwgb2Ygc3FscykgYXdhaXQgZGIucXVlcnkoc3FsKVxuICAgIH1cblxuICAgIGlmIChsZWdhY3lJbmRleGVzLmxlbmd0aCA+IDApIGRiLmNsZWFyU2NoZW1hQ2FjaGUoKVxuICB9XG5cbiAgLyoqXG4gICAqIEFwcGxpZXMgYSB0ZXJtaW5hbCB0cmFuc2l0aW9uIG9ubHkgd2hpbGUgdGhlIGV4cGVjdGVkIGV4ZWN1dGlvbiBvd25lclxuICAgKiBzdGlsbCBvd25zIGEgcnVubmluZyByb3cuIEEgc3RhbGUgd29ya2VyIHRoZXJlZm9yZSBjYW5ub3Qgb3ZlcndyaXRlIGFcbiAgICogc3RhdGUgYWxyZWFkeSB3cml0dGVuIGJ5IGxlYXNlIHJlY29uY2lsaWF0aW9uLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgPz59IGFyZ3MuZGF0YSAtIFRlcm1pbmFsIGNvbHVtbiB2YWx1ZXMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmlkIC0gUnVuIGlkLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5vd25lclRva2VuIC0gRXhwZWN0ZWQgZXhlY3V0aW9uIG93bmVyIHRva2VuLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIHVwZGF0ZWQuXG4gICAqL1xuICBhc3luYyBfdHJhbnNpdGlvblJ1bm5pbmdSdW4oe2RhdGEsIGlkLCBvd25lclRva2VufSkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuICAgIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IGFmZmVjdGVkID0gYXdhaXQgZGIuYWZmZWN0ZWRSb3dzKGRiLnVwZGF0ZVNxbCh7XG4gICAgICAgIHRhYmxlTmFtZTogUlVOU19UQUJMRSxcbiAgICAgICAgZGF0YSxcbiAgICAgICAgY29uZGl0aW9uczoge2lkLCBtb3VudF9pZGVudGlmaWVyOiB0aGlzLm1vdW50SWRlbnRpZmllciwgb3duZXJfdG9rZW46IG93bmVyVG9rZW4sIHN0YXR1czogXCJydW5uaW5nXCJ9XG4gICAgICB9KSlcblxuICAgICAgaWYgKGFmZmVjdGVkICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgdG8gdHJhbnNpdGlvbiBleGFjdGx5IG9uZSBydW5uaW5nIGRlcGxveW1lbnQgcnVuIHdpdGggaWQgJHtpZH0gZm9yIGl0cyBleGVjdXRpb24gb3duZXIsIGJ1dCB1cGRhdGVkICR7YWZmZWN0ZWR9YClcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIGEgcnVuIGJ5IGlkIG9uIGFuIG9wZW4gY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSBjb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWQgLSBSdW4gaWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPERlcGxveW1lbnRSdW5Sb3cgfCBudWxsPn0gLSBUaGUgcnVuIG9yIG51bGwuXG4gICAqL1xuICBhc3luYyBfZmluZFJ1bkJ5SWQoZGIsIGlkKSB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiXG4gICAgICAubmV3UXVlcnkoKVxuICAgICAgLmZyb20oUlVOU19UQUJMRSlcbiAgICAgIC53aGVyZSh7aWQsIG1vdW50X2lkZW50aWZpZXI6IHRoaXMubW91bnRJZGVudGlmaWVyfSlcbiAgICAgIC5saW1pdCgxKVxuICAgICAgLnJlc3VsdHMoKVxuXG4gICAgcmV0dXJuIHJvd3NbMF0gPyB0aGlzLl9ydW5Sb3dGcm9tRGIocm93c1swXSkgOiBudWxsXG4gIH1cblxuICAvKipcbiAgICogRmluZHMgYSBydW4gYnkgaWRlbXBvdGVuY3kga2V5IG9uIGFuIG9wZW4gY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSBjb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWRlbXBvdGVuY3lLZXkgLSBJZGVtcG90ZW5jeSBrZXkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPERlcGxveW1lbnRSdW5Sb3cgfCBudWxsPn0gLSBUaGUgcnVuIG9yIG51bGwuXG4gICAqL1xuICBhc3luYyBfZmluZFJ1bkJ5S2V5KGRiLCBpZGVtcG90ZW5jeUtleSkge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCBkYlxuICAgICAgLm5ld1F1ZXJ5KClcbiAgICAgIC5mcm9tKFJVTlNfVEFCTEUpXG4gICAgICAud2hlcmUoe2lkZW1wb3RlbmN5X2tleTogaWRlbXBvdGVuY3lLZXksIG1vdW50X2lkZW50aWZpZXI6IHRoaXMubW91bnRJZGVudGlmaWVyfSlcbiAgICAgIC5saW1pdCgxKVxuICAgICAgLnJlc3VsdHMoKVxuXG4gICAgcmV0dXJuIHJvd3NbMF0gPyB0aGlzLl9ydW5Sb3dGcm9tRGIocm93c1swXSkgOiBudWxsXG4gIH1cblxuICAvKipcbiAgICogRmluZHMgdGhlIHJ1biBibG9ja2luZyBhbm90aGVyIGRlcGxveW1lbnQgZm9yIGEgcHJvamVjdC9zdGFnZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSBjb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnByb2plY3QgLSBQcm9qZWN0IGlkZW50aWZpZXIuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnN0YWdlIC0gU3RhZ2UgaWRlbnRpZmllci5cbiAgICogQHJldHVybnMge1Byb21pc2U8RGVwbG95bWVudFJ1blJvdyB8IG51bGw+fSAtIFRoZSBibG9ja2luZyBydW4gb3IgbnVsbC5cbiAgICovXG4gIGFzeW5jIF9maW5kQmxvY2tpbmdSdW4oZGIsIHtwcm9qZWN0LCBzdGFnZX0pIHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgZGJcbiAgICAgIC5uZXdRdWVyeSgpXG4gICAgICAuZnJvbShSVU5TX1RBQkxFKVxuICAgICAgLndoZXJlKHttb3VudF9pZGVudGlmaWVyOiB0aGlzLm1vdW50SWRlbnRpZmllciwgcHJvamVjdCwgc3RhZ2UsIHN0YXR1czogQkxPQ0tJTkdfU1RBVFVTRVN9KVxuICAgICAgLm9yZGVyKFwicmVxdWVzdGVkX2F0X21zIEFTQ1wiKVxuICAgICAgLmxpbWl0KDEpXG4gICAgICAucmVzdWx0cygpXG5cbiAgICByZXR1cm4gcm93c1swXSA/IHRoaXMuX3J1blJvd0Zyb21EYihyb3dzWzBdKSA6IG51bGxcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpemVzIGEgcmF3IHJ1biByb3cuXG4gICAqIEBwYXJhbSB7P30gcm93IC0gUmF3IGRhdGFiYXNlIHJvdy5cbiAgICogQHJldHVybnMge0RlcGxveW1lbnRSdW5Sb3d9IC0gTm9ybWFsaXplZCBydW4gcm93LlxuICAgKi9cbiAgX3J1blJvd0Zyb21EYihyb3cpIHtcbiAgICBjb25zdCByZWNvcmQgPSAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsID8+fSAqLyAocm93KVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlkOiAvKiogQHR5cGUge3N0cmluZ30gKi8gKHJlY29yZC5pZCksXG4gICAgICBtb3VudElkZW50aWZpZXI6IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAocmVjb3JkLm1vdW50X2lkZW50aWZpZXIpLFxuICAgICAgcHJvamVjdDogLyoqIEB0eXBlIHtzdHJpbmd9ICovIChyZWNvcmQucHJvamVjdCksXG4gICAgICBzdGFnZTogLyoqIEB0eXBlIHtzdHJpbmd9ICovIChyZWNvcmQuc3RhZ2UpLFxuICAgICAgcmV2aXNpb246IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAocmVjb3JkLnJldmlzaW9uKSxcbiAgICAgIGlkZW1wb3RlbmN5S2V5OiAvKiogQHR5cGUge3N0cmluZ30gKi8gKHJlY29yZC5pZGVtcG90ZW5jeV9rZXkpLFxuICAgICAgc3RhdHVzOiAvKiogQHR5cGUge3N0cmluZ30gKi8gKHJlY29yZC5zdGF0dXMpLFxuICAgICAgcmVzdWx0OiBwYXJzZUpzb25Db2x1bW4ocmVjb3JkLnJlc3VsdF9qc29uKSxcbiAgICAgIGVycm9yOiBwYXJzZUpzb25Db2x1bW4ocmVjb3JkLmVycm9yX2pzb24pLFxuICAgICAgcmVxdWVzdGVkQXRNczogTnVtYmVyKHJlY29yZC5yZXF1ZXN0ZWRfYXRfbXMpLFxuICAgICAgc3RhcnRlZEF0TXM6IHJlY29yZC5zdGFydGVkX2F0X21zID09PSBudWxsIHx8IHJlY29yZC5zdGFydGVkX2F0X21zID09PSB1bmRlZmluZWQgPyBudWxsIDogTnVtYmVyKHJlY29yZC5zdGFydGVkX2F0X21zKSxcbiAgICAgIGZpbmlzaGVkQXRNczogcmVjb3JkLmZpbmlzaGVkX2F0X21zID09PSBudWxsIHx8IHJlY29yZC5maW5pc2hlZF9hdF9tcyA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IE51bWJlcihyZWNvcmQuZmluaXNoZWRfYXRfbXMpLFxuICAgICAgb3duZXJUb2tlbjogcmVjb3JkLm93bmVyX3Rva2VuID09PSBudWxsIHx8IHJlY29yZC5vd25lcl90b2tlbiA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAocmVjb3JkLm93bmVyX3Rva2VuKSxcbiAgICAgIGhlYXJ0YmVhdEF0TXM6IHJlY29yZC5oZWFydGJlYXRfYXRfbXMgPT09IG51bGwgfHwgcmVjb3JkLmhlYXJ0YmVhdF9hdF9tcyA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IE51bWJlcihyZWNvcmQuaGVhcnRiZWF0X2F0X21zKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpemVzIGEgcmF3IGF1ZGl0IGV2ZW50IHJvdy5cbiAgICogQHBhcmFtIHs/fSByb3cgLSBSYXcgZGF0YWJhc2Ugcm93LlxuICAgKiBAcmV0dXJucyB7RGVwbG95bWVudEF1ZGl0RXZlbnRSb3d9IC0gTm9ybWFsaXplZCBhdWRpdCBldmVudCByb3cuXG4gICAqL1xuICBfYXVkaXRSb3dGcm9tRGIocm93KSB7XG4gICAgY29uc3QgcmVjb3JkID0gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gKi8gKHJvdylcblxuICAgIHJldHVybiB7XG4gICAgICBpZDogLyoqIEB0eXBlIHtzdHJpbmd9ICovIChyZWNvcmQuaWQpLFxuICAgICAgcnVuSWQ6IHJlY29yZC5ydW5faWQgPT09IG51bGwgfHwgcmVjb3JkLnJ1bl9pZCA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAocmVjb3JkLnJ1bl9pZCksXG4gICAgICBldmVudDogLyoqIEB0eXBlIHtzdHJpbmd9ICovIChyZWNvcmQuZXZlbnQpLFxuICAgICAgcGF5bG9hZDogcGFyc2VKc29uQ29sdW1uKHJlY29yZC5wYXlsb2FkX2pzb24pLFxuICAgICAgY3JlYXRlZEF0TXM6IE51bWJlcihyZWNvcmQuY3JlYXRlZF9hdF9tcylcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBhIGNhbGxiYWNrIHdpdGggYSBwb29sZWQgY29ubmVjdGlvbi5cbiAgICogQHRlbXBsYXRlIFRcbiAgICogQHBhcmFtIHsoZGI6IGltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0KSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIENhbGxiYWNrLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUPn0gLSBDYWxsYmFjayByZXN1bHQuXG4gICAqL1xuICBhc3luYyBfd2l0aERiKGNhbGxiYWNrKSB7XG4gICAgY29uc3QgcG9vbCA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXREYXRhYmFzZVBvb2wodGhpcy5kYXRhYmFzZUlkZW50aWZpZXIpXG4gICAgLyoqIEB0eXBlIHtUIHwgdW5kZWZpbmVkfSAqL1xuICAgIGxldCByZXN1bHRcblxuICAgIGF3YWl0IHBvb2wud2l0aENvbm5lY3Rpb24oe25hbWU6IFwiRGVwbG95bWVudCBydW4gc3RvcmVcIn0sIGFzeW5jIChkYikgPT4ge1xuICAgICAgcmVzdWx0ID0gYXdhaXQgY2FsbGJhY2soZGIpXG4gICAgfSlcblxuICAgIHJldHVybiAvKiogQHR5cGUge1R9ICovIChyZXN1bHQpXG4gIH1cbn1cbiJdfQ==
|