velocious 1.0.606 → 1.0.608
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 +45 -2
- package/build/background-jobs/job-semantics.js +145 -0
- package/build/background-jobs/local-adapter.js +161 -0
- package/build/background-jobs/local-dispatcher.js +418 -0
- package/build/background-jobs/local-job-registry.js +59 -0
- package/build/background-jobs/local-store.js +1220 -0
- package/build/background-jobs/main.js +2 -1
- package/build/background-jobs/store.js +97 -100
- package/build/background-jobs/types.js +28 -1
- package/build/background-jobs/worker.js +19 -14
- package/build/configuration-types.js +3 -1
- package/build/configuration.js +16 -1
- package/build/database/drivers/sqlite/index.js +46 -41
- package/build/environment-handlers/browser.js +10 -0
- package/build/src/background-jobs/job-semantics.d.ts +65 -0
- package/build/src/background-jobs/job-semantics.d.ts.map +1 -0
- package/build/src/background-jobs/job-semantics.js +125 -0
- package/build/src/background-jobs/local-adapter.d.ts +161 -0
- package/build/src/background-jobs/local-adapter.d.ts.map +1 -0
- package/build/src/background-jobs/local-adapter.js +141 -0
- package/build/src/background-jobs/local-dispatcher.d.ts +185 -0
- package/build/src/background-jobs/local-dispatcher.d.ts.map +1 -0
- package/build/src/background-jobs/local-dispatcher.js +383 -0
- package/build/src/background-jobs/local-job-registry.d.ts +26 -0
- package/build/src/background-jobs/local-job-registry.d.ts.map +1 -0
- package/build/src/background-jobs/local-job-registry.js +51 -0
- package/build/src/background-jobs/local-store.d.ts +364 -0
- package/build/src/background-jobs/local-store.d.ts.map +1 -0
- package/build/src/background-jobs/local-store.js +1079 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +3 -2
- package/build/src/background-jobs/store.d.ts +17 -9
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +75 -91
- package/build/src/background-jobs/types.d.ts +107 -2
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +29 -2
- package/build/src/background-jobs/worker.d.ts +15 -7
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +20 -15
- package/build/src/configuration-types.d.ts +9 -2
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +4 -2
- package/build/src/configuration.d.ts +5 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +15 -2
- package/build/src/database/drivers/sqlite/index.d.ts +8 -0
- package/build/src/database/drivers/sqlite/index.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.js +47 -41
- package/build/src/environment-handlers/browser.d.ts +9 -0
- package/build/src/environment-handlers/browser.d.ts.map +1 -1
- package/build/src/environment-handlers/browser.js +10 -1
- package/build/src/testing/test-runner.d.ts +42 -10
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +135 -35
- package/build/testing/test-runner.js +141 -33
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/test-browser.js +5 -1
- package/src/background-jobs/job-semantics.js +145 -0
- package/src/background-jobs/local-adapter.js +161 -0
- package/src/background-jobs/local-dispatcher.js +418 -0
- package/src/background-jobs/local-job-registry.js +59 -0
- package/src/background-jobs/local-store.js +1220 -0
- package/src/background-jobs/main.js +2 -1
- package/src/background-jobs/store.js +97 -100
- package/src/background-jobs/types.js +28 -1
- package/src/background-jobs/worker.js +19 -14
- package/src/configuration-types.js +3 -1
- package/src/configuration.js +16 -1
- package/src/database/drivers/sqlite/index.js +46 -41
- package/src/environment-handlers/browser.js +10 -0
- package/src/testing/test-runner.js +141 -33
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import UUID from "pure-uuid";
|
|
3
|
+
import TableData from "../database/table-data/index.js";
|
|
4
|
+
import TableIndex from "../database/table-data/table-index.js";
|
|
5
|
+
import sha256Hex from "../utils/sha256-hex.js";
|
|
6
|
+
import normalizeBackgroundJobError from "./normalize-error.js";
|
|
7
|
+
import { DEFAULT_BACKGROUND_JOB_QUEUE, QUEUE_CONCURRENCY_KEY_PREFIX, normalizeBackgroundJobConcurrency, normalizeBackgroundJobExecutionMode, normalizeBackgroundJobMaxRetries, normalizeBackgroundJobQueue, normalizeBackgroundJobScheduledAtMs, rescheduledBackgroundJobAtMs, retryDelayMs } from "./job-semantics.js";
|
|
8
|
+
export const LOCAL_BACKGROUND_JOBS_TABLE = "velocious_local_background_jobs";
|
|
9
|
+
export const LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE = "velocious_local_background_job_concurrency";
|
|
10
|
+
const MIGRATIONS_TABLE = "velocious_internal_migrations";
|
|
11
|
+
const MIGRATION_SCOPE = "local_background_jobs";
|
|
12
|
+
const MIGRATION_VERSION = "1";
|
|
13
|
+
const LOCAL_EXECUTION_MODES = [/** @type {const} */ ("inline")];
|
|
14
|
+
export const LOCAL_BACKGROUND_JOBS_INDEX_NAMES = [
|
|
15
|
+
"index_velocious_local_background_jobs_due",
|
|
16
|
+
"index_velocious_local_background_jobs_queue_status",
|
|
17
|
+
"index_velocious_local_background_jobs_deduplication",
|
|
18
|
+
"index_velocious_local_background_jobs_concurrency"
|
|
19
|
+
];
|
|
20
|
+
const EXPECTED_JOB_COLUMNS = [
|
|
21
|
+
"id",
|
|
22
|
+
"job_name",
|
|
23
|
+
"args_json",
|
|
24
|
+
"args_digest",
|
|
25
|
+
"execution_mode",
|
|
26
|
+
"queue",
|
|
27
|
+
"max_retries",
|
|
28
|
+
"attempts",
|
|
29
|
+
"status",
|
|
30
|
+
"scheduled_at_ms",
|
|
31
|
+
"created_at_ms",
|
|
32
|
+
"handed_off_at_ms",
|
|
33
|
+
"handoff_id",
|
|
34
|
+
"worker_id",
|
|
35
|
+
"completed_at_ms",
|
|
36
|
+
"failed_at_ms",
|
|
37
|
+
"last_error",
|
|
38
|
+
"concurrency_key",
|
|
39
|
+
"max_concurrency"
|
|
40
|
+
];
|
|
41
|
+
const EXPECTED_CONCURRENCY_COLUMNS = ["concurrency_key", "max_concurrency", "active_count"];
|
|
42
|
+
/** @type {WeakMap<import("../configuration.js").default, Map<string, Promise<void>>>} */
|
|
43
|
+
const deduplicatedEnqueueChains = new WeakMap();
|
|
44
|
+
/**
|
|
45
|
+
* Creates the production clock used by local dispatch.
|
|
46
|
+
* @returns {import("./types.js").LocalBackgroundJobsClock} - Production clock.
|
|
47
|
+
*/
|
|
48
|
+
export function localBackgroundJobsClock() {
|
|
49
|
+
return {
|
|
50
|
+
clearTimeout: (timerId) => globalThis.clearTimeout(timerId),
|
|
51
|
+
now: () => Date.now(),
|
|
52
|
+
setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Namespaced portable SQLite persistence for local background jobs. */
|
|
56
|
+
export default class LocalBackgroundJobsStore {
|
|
57
|
+
/**
|
|
58
|
+
* Creates a store for one configuration and local database.
|
|
59
|
+
* @param {object} args - Store options.
|
|
60
|
+
* @param {import("../configuration.js").default} args.configuration - Owning configuration.
|
|
61
|
+
* @param {import("./types.js").LocalBackgroundJobsClock} [args.clock] - Persistence clock.
|
|
62
|
+
* @param {string} [args.databaseIdentifier] - Configured local database identifier.
|
|
63
|
+
* @param {() => void} [args.onCommittedEnqueue] - Commit-aware dispatcher wake.
|
|
64
|
+
*/
|
|
65
|
+
constructor({ configuration, clock = localBackgroundJobsClock(), databaseIdentifier, onCommittedEnqueue }) {
|
|
66
|
+
this.clock = clock;
|
|
67
|
+
this.configuration = configuration;
|
|
68
|
+
this.databaseIdentifier = databaseIdentifier;
|
|
69
|
+
this.onCommittedEnqueue = onCommittedEnqueue;
|
|
70
|
+
this._isReady = false;
|
|
71
|
+
/** @type {Promise<void> | null} */
|
|
72
|
+
this._readyPromise = null;
|
|
73
|
+
/** @type {WeakMap<import("../database/drivers/base.js").default, {completion: Promise<void>, promise: Promise<void>}>} */
|
|
74
|
+
this._transactionReadyPromises = new WeakMap();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolves the configured local database identifier.
|
|
78
|
+
* @returns {string} - Database identifier.
|
|
79
|
+
*/
|
|
80
|
+
getDatabaseIdentifier() {
|
|
81
|
+
return this.databaseIdentifier || this.configuration.getBackgroundJobsConfig().databaseIdentifier;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Ensures the versioned physical schema exists.
|
|
85
|
+
* @returns {Promise<void>} - Resolves when ready.
|
|
86
|
+
*/
|
|
87
|
+
async ensureReady() {
|
|
88
|
+
if (this._isReady)
|
|
89
|
+
return;
|
|
90
|
+
await this._withDb(async (db) => await this._ensureReadyWithDb(db));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Clears the per-instance readiness latch for a deliberate adapter reopen.
|
|
94
|
+
* @returns {void} - No return value.
|
|
95
|
+
*/
|
|
96
|
+
resetReadiness() {
|
|
97
|
+
this._isReady = false;
|
|
98
|
+
this._readyPromise = null;
|
|
99
|
+
this._transactionReadyPromises = new WeakMap();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Coordinates physical and transaction-local schema readiness.
|
|
103
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
104
|
+
* @returns {Promise<void>} - Resolves when this caller can use the schema.
|
|
105
|
+
*/
|
|
106
|
+
async _ensureReadyWithDb(db) {
|
|
107
|
+
if (this._isReady)
|
|
108
|
+
return;
|
|
109
|
+
const transactionCompletion = db.insideTransaction() ? db.transactionCompletion() : null;
|
|
110
|
+
const transactionReady = this._transactionReadyPromises.get(db);
|
|
111
|
+
if (transactionCompletion && transactionReady?.completion === transactionCompletion) {
|
|
112
|
+
await transactionReady.promise;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (this._readyPromise) {
|
|
116
|
+
const readyPromise = this._readyPromise;
|
|
117
|
+
await readyPromise;
|
|
118
|
+
if (this._readyPromise === readyPromise)
|
|
119
|
+
this._readyPromise = null;
|
|
120
|
+
if (this._isReady)
|
|
121
|
+
return;
|
|
122
|
+
await this.ensureReady();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (transactionCompletion) {
|
|
126
|
+
const schemaReadyPromise = this._applySchema(db);
|
|
127
|
+
const transactionReadyPromise = schemaReadyPromise.then(() => undefined);
|
|
128
|
+
const transactionReady = { completion: transactionCompletion, promise: transactionReadyPromise };
|
|
129
|
+
const durableReadyPromise = schemaReadyPromise.then(async (changed) => {
|
|
130
|
+
if (!changed) {
|
|
131
|
+
this._isReady = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
await transactionCompletion;
|
|
135
|
+
}, () => {
|
|
136
|
+
// The transaction-local caller below owns and rethrows this same schema error.
|
|
137
|
+
// This branch only settles the shared durability barrier so it cannot become
|
|
138
|
+
// an independent unhandled rejection while failed ownership is cleared.
|
|
139
|
+
});
|
|
140
|
+
this._transactionReadyPromises.set(db, transactionReady);
|
|
141
|
+
this._readyPromise = durableReadyPromise;
|
|
142
|
+
try {
|
|
143
|
+
await transactionReadyPromise;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (this._transactionReadyPromises.get(db) === transactionReady)
|
|
147
|
+
this._transactionReadyPromises.delete(db);
|
|
148
|
+
if (this._readyPromise === durableReadyPromise)
|
|
149
|
+
this._readyPromise = null;
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this._readyPromise = this._transactionResult(db, async () => await this._applySchema(db)).then(() => {
|
|
155
|
+
this._isReady = true;
|
|
156
|
+
});
|
|
157
|
+
try {
|
|
158
|
+
await this._readyPromise;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
if (!this._isReady)
|
|
162
|
+
this._readyPromise = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Creates or repairs version-one tables and indexes.
|
|
167
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
168
|
+
* @returns {Promise<boolean>} - Whether schema state changed.
|
|
169
|
+
*/
|
|
170
|
+
async _applySchema(db) {
|
|
171
|
+
let changed = false;
|
|
172
|
+
if (!(await db.tableExists(MIGRATIONS_TABLE))) {
|
|
173
|
+
await db.createTable(this._migrationsTableData());
|
|
174
|
+
changed = true;
|
|
175
|
+
}
|
|
176
|
+
if (!(await db.tableExists(LOCAL_BACKGROUND_JOBS_TABLE))) {
|
|
177
|
+
await db.createTable(this._jobsTableData());
|
|
178
|
+
changed = true;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
await this._assertColumns(db, LOCAL_BACKGROUND_JOBS_TABLE, EXPECTED_JOB_COLUMNS);
|
|
182
|
+
}
|
|
183
|
+
if (!(await db.tableExists(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE))) {
|
|
184
|
+
await db.createTable(this._concurrencyTableData());
|
|
185
|
+
changed = true;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
await this._assertColumns(db, LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE, EXPECTED_CONCURRENCY_COLUMNS);
|
|
189
|
+
}
|
|
190
|
+
if (await this._ensureIndexes(db))
|
|
191
|
+
changed = true;
|
|
192
|
+
if (!(await this._hasMigration(db))) {
|
|
193
|
+
await db.upsert({
|
|
194
|
+
tableName: MIGRATIONS_TABLE,
|
|
195
|
+
data: {
|
|
196
|
+
applied_at_ms: this.clock.now(),
|
|
197
|
+
key: this._migrationKey(),
|
|
198
|
+
scope: MIGRATION_SCOPE,
|
|
199
|
+
version: MIGRATION_VERSION
|
|
200
|
+
},
|
|
201
|
+
conflictColumns: ["key"],
|
|
202
|
+
updateColumns: ["scope", "version", "applied_at_ms"]
|
|
203
|
+
});
|
|
204
|
+
changed = true;
|
|
205
|
+
}
|
|
206
|
+
return changed;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Builds the migration ledger table definition.
|
|
210
|
+
* @returns {TableData} - Migration ledger table.
|
|
211
|
+
*/
|
|
212
|
+
_migrationsTableData() {
|
|
213
|
+
const table = new TableData(MIGRATIONS_TABLE, { ifNotExists: true });
|
|
214
|
+
table.string("key", { null: false, primaryKey: true });
|
|
215
|
+
table.string("scope", { null: false });
|
|
216
|
+
table.string("version", { null: false });
|
|
217
|
+
table.bigint("applied_at_ms", { null: false });
|
|
218
|
+
return table;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Builds the local jobs table definition.
|
|
222
|
+
* @returns {TableData} - Local jobs table definition.
|
|
223
|
+
*/
|
|
224
|
+
_jobsTableData() {
|
|
225
|
+
const table = new TableData(LOCAL_BACKGROUND_JOBS_TABLE, { ifNotExists: true });
|
|
226
|
+
table.string("id", { null: false, primaryKey: true });
|
|
227
|
+
table.string("job_name", { null: false });
|
|
228
|
+
table.text("args_json", { null: false });
|
|
229
|
+
table.string("args_digest", { maxLength: 64, null: false });
|
|
230
|
+
table.string("execution_mode", { null: false });
|
|
231
|
+
table.string("queue", { null: false });
|
|
232
|
+
table.integer("max_retries", { null: false });
|
|
233
|
+
table.integer("attempts", { null: false });
|
|
234
|
+
table.string("status", { null: false });
|
|
235
|
+
table.bigint("scheduled_at_ms", { null: false });
|
|
236
|
+
table.bigint("created_at_ms", { null: false });
|
|
237
|
+
table.bigint("handed_off_at_ms", { null: true });
|
|
238
|
+
table.string("handoff_id", { null: true });
|
|
239
|
+
table.string("worker_id", { null: true });
|
|
240
|
+
table.bigint("completed_at_ms", { null: true });
|
|
241
|
+
table.bigint("failed_at_ms", { null: true });
|
|
242
|
+
table.text("last_error", { null: true });
|
|
243
|
+
table.string("concurrency_key", { null: true });
|
|
244
|
+
table.integer("max_concurrency", { null: true });
|
|
245
|
+
table.addIndex(new TableIndex(["status", "scheduled_at_ms", "created_at_ms", "id"], { name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[0] }));
|
|
246
|
+
table.addIndex(new TableIndex(["queue", "status", "created_at_ms"], { name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[1] }));
|
|
247
|
+
table.addIndex(new TableIndex(["args_digest"], { name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[2] }));
|
|
248
|
+
table.addIndex(new TableIndex(["status", "concurrency_key", "scheduled_at_ms"], { name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[3] }));
|
|
249
|
+
return table;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Builds the local concurrency counter table definition.
|
|
253
|
+
* @returns {TableData} - Concurrency counter table definition.
|
|
254
|
+
*/
|
|
255
|
+
_concurrencyTableData() {
|
|
256
|
+
const table = new TableData(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE, { ifNotExists: true });
|
|
257
|
+
table.string("concurrency_key", { null: false, primaryKey: true });
|
|
258
|
+
table.integer("max_concurrency", { null: false });
|
|
259
|
+
table.integer("active_count", { null: false });
|
|
260
|
+
return table;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Rejects an incompatible current-version table rather than rebuilding data.
|
|
264
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
265
|
+
* @param {string} tableName - Table name.
|
|
266
|
+
* @param {string[]} expectedColumns - Required columns.
|
|
267
|
+
* @returns {Promise<void>} - Resolves when compatible.
|
|
268
|
+
*/
|
|
269
|
+
async _assertColumns(db, tableName, expectedColumns) {
|
|
270
|
+
const table = await db.getTableByNameOrFail(tableName);
|
|
271
|
+
const columns = await table.getColumns();
|
|
272
|
+
const names = new Set(columns.map((column) => column.getName()));
|
|
273
|
+
const missing = expectedColumns.filter((columnName) => !names.has(columnName));
|
|
274
|
+
if (missing.length === 0)
|
|
275
|
+
return;
|
|
276
|
+
const error = new Error(`Incompatible local background-jobs schema for ${tableName}; missing columns: ${missing.join(", ")}`);
|
|
277
|
+
this._reportFrameworkError({ error, stage: "local-background-jobs-schema" });
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Recreates missing indexes declared by the current schema.
|
|
282
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
283
|
+
* @returns {Promise<boolean>} - Whether an index was created.
|
|
284
|
+
*/
|
|
285
|
+
async _ensureIndexes(db) {
|
|
286
|
+
db.clearSchemaCache();
|
|
287
|
+
const jobsTable = await db.getTableByNameOrFail(LOCAL_BACKGROUND_JOBS_TABLE);
|
|
288
|
+
const existingNames = new Set((await jobsTable.getIndexes()).map((index) => index.getName()));
|
|
289
|
+
let changed = false;
|
|
290
|
+
for (const index of this._jobsTableData().getIndexes()) {
|
|
291
|
+
const indexName = index.getName();
|
|
292
|
+
if (!indexName || existingNames.has(indexName))
|
|
293
|
+
continue;
|
|
294
|
+
const sqls = await db.createIndexSQLs({
|
|
295
|
+
columns: index.getColumns(),
|
|
296
|
+
ifNotExists: true,
|
|
297
|
+
name: indexName,
|
|
298
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE,
|
|
299
|
+
unique: index.getUnique()
|
|
300
|
+
});
|
|
301
|
+
for (const sql of sqls)
|
|
302
|
+
await db.query(sql);
|
|
303
|
+
changed = true;
|
|
304
|
+
}
|
|
305
|
+
if (changed)
|
|
306
|
+
db.clearSchemaCache();
|
|
307
|
+
return changed;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Checks whether the current local schema version is recorded.
|
|
311
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
312
|
+
* @returns {Promise<boolean>} - Whether version one is recorded.
|
|
313
|
+
*/
|
|
314
|
+
async _hasMigration(db) {
|
|
315
|
+
const rows = await db
|
|
316
|
+
.newQuery()
|
|
317
|
+
.from(MIGRATIONS_TABLE)
|
|
318
|
+
.where({ key: this._migrationKey() })
|
|
319
|
+
.limit(1)
|
|
320
|
+
.results();
|
|
321
|
+
return rows.length > 0;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Builds the scoped migration key.
|
|
325
|
+
* @returns {string} - Scoped migration key.
|
|
326
|
+
*/
|
|
327
|
+
_migrationKey() { return `${MIGRATION_SCOPE}:${MIGRATION_VERSION}`; }
|
|
328
|
+
/**
|
|
329
|
+
* Enqueues a local job in the caller's active transaction when present.
|
|
330
|
+
* @param {object} args - Enqueue request.
|
|
331
|
+
* @param {string} args.jobName - Registered job name.
|
|
332
|
+
* @param {Array<ReturnType<typeof JSON.parse>>} args.args - Serialized job arguments.
|
|
333
|
+
* @param {import("./types.js").BackgroundJobOptions} [args.options] - Job options.
|
|
334
|
+
* @returns {Promise<string>} - Durable job id.
|
|
335
|
+
*/
|
|
336
|
+
async enqueue({ jobName, args, options = {} }) {
|
|
337
|
+
await this.ensureReady();
|
|
338
|
+
const preparedJob = this._prepareJob({ args, jobName, options });
|
|
339
|
+
const mutate = async (holdUntil = (/** @type {Promise<void>} */ _completion) => { }) => await this._withDb(async (connection) => {
|
|
340
|
+
if (connection.insideTransaction())
|
|
341
|
+
holdUntil(connection.transactionCompletion());
|
|
342
|
+
return await this._mutate(connection, async (db) => {
|
|
343
|
+
let jobId = preparedJob.jobId;
|
|
344
|
+
if (preparedJob.concurrency)
|
|
345
|
+
await this._ensureConcurrency(db, preparedJob.concurrency);
|
|
346
|
+
if (options.deduplicateWhileQueued) {
|
|
347
|
+
const existing = await db
|
|
348
|
+
.newQuery()
|
|
349
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
350
|
+
.select("id")
|
|
351
|
+
.where({
|
|
352
|
+
args_digest: preparedJob.argsDigest,
|
|
353
|
+
args_json: preparedJob.argsJson,
|
|
354
|
+
job_name: preparedJob.jobName,
|
|
355
|
+
queue: preparedJob.queue,
|
|
356
|
+
status: "queued"
|
|
357
|
+
})
|
|
358
|
+
.where(`scheduled_at_ms <= ${db.quote(preparedJob.scheduledAtMs)}`)
|
|
359
|
+
.order("scheduled_at_ms ASC")
|
|
360
|
+
.order("created_at_ms ASC")
|
|
361
|
+
.limit(1)
|
|
362
|
+
.results();
|
|
363
|
+
const existingRow = /** @type {{id: string | number} | undefined} */ (existing[0]);
|
|
364
|
+
if (existingRow)
|
|
365
|
+
jobId = String(existingRow.id);
|
|
366
|
+
}
|
|
367
|
+
if (jobId === preparedJob.jobId)
|
|
368
|
+
await this._insertPreparedJob(db, preparedJob);
|
|
369
|
+
if (this.onCommittedEnqueue)
|
|
370
|
+
await db.afterCommit(this.onCommittedEnqueue);
|
|
371
|
+
return jobId;
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
if (options.deduplicateWhileQueued)
|
|
375
|
+
return await this._serializeDeduplicatedEnqueue(preparedJob, mutate);
|
|
376
|
+
return await mutate();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Serializes matching in-process deduplication checks through commit while
|
|
380
|
+
* leaving unrelated job identities independent.
|
|
381
|
+
* @template T
|
|
382
|
+
* @param {import("./types.js").PreparedLocalBackgroundJob} preparedJob - Prepared job identity.
|
|
383
|
+
* @param {(holdUntil: (completion: Promise<void>) => void) => Promise<T>} callback - Deduplication mutation.
|
|
384
|
+
* @returns {Promise<T>} - Mutation result.
|
|
385
|
+
*/
|
|
386
|
+
async _serializeDeduplicatedEnqueue(preparedJob, callback) {
|
|
387
|
+
let chains = deduplicatedEnqueueChains.get(this.configuration);
|
|
388
|
+
if (!chains) {
|
|
389
|
+
chains = new Map();
|
|
390
|
+
deduplicatedEnqueueChains.set(this.configuration, chains);
|
|
391
|
+
}
|
|
392
|
+
const key = sha256Hex(JSON.stringify([
|
|
393
|
+
this.getDatabaseIdentifier(),
|
|
394
|
+
preparedJob.jobName,
|
|
395
|
+
preparedJob.argsDigest,
|
|
396
|
+
preparedJob.queue
|
|
397
|
+
]));
|
|
398
|
+
const previous = chains.get(key) || Promise.resolve();
|
|
399
|
+
let release = () => { };
|
|
400
|
+
const running = new Promise((resolve) => { release = () => resolve(undefined); });
|
|
401
|
+
const chain = previous.then(() => running);
|
|
402
|
+
/** @type {Promise<void> | undefined} */
|
|
403
|
+
let completion;
|
|
404
|
+
const finish = () => {
|
|
405
|
+
release();
|
|
406
|
+
if (chains.get(key) === chain)
|
|
407
|
+
chains.delete(key);
|
|
408
|
+
if (chains.size === 0)
|
|
409
|
+
deduplicatedEnqueueChains.delete(this.configuration);
|
|
410
|
+
};
|
|
411
|
+
chains.set(key, chain);
|
|
412
|
+
await previous;
|
|
413
|
+
try {
|
|
414
|
+
const result = await callback((transactionCompletion) => { completion = transactionCompletion; });
|
|
415
|
+
if (completion) {
|
|
416
|
+
completion.then(finish, finish);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
finish();
|
|
420
|
+
}
|
|
421
|
+
return result;
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
finish();
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Prepares validated local job data for insertion.
|
|
430
|
+
* @param {{args: Array<ReturnType<typeof JSON.parse>>, jobName: string, options: import("./types.js").BackgroundJobOptions}} args - Job request.
|
|
431
|
+
* @returns {import("./types.js").PreparedLocalBackgroundJob} - Prepared row data.
|
|
432
|
+
*/
|
|
433
|
+
_prepareJob({ args, jobName, options }) {
|
|
434
|
+
if (options.idempotencyKey !== undefined) {
|
|
435
|
+
throw new Error("idempotencyKey is not supported by the local background-jobs adapter");
|
|
436
|
+
}
|
|
437
|
+
const createdAtMs = this.clock.now();
|
|
438
|
+
const queue = normalizeBackgroundJobQueue(options);
|
|
439
|
+
const queues = this.configuration.getBackgroundJobsConfig().queues;
|
|
440
|
+
const argsJson = JSON.stringify(args || []);
|
|
441
|
+
const executionMode = normalizeBackgroundJobExecutionMode(options, "inline", LOCAL_EXECUTION_MODES);
|
|
442
|
+
if (typeof argsJson !== "string")
|
|
443
|
+
throw new TypeError("Local background job arguments must be JSON serializable");
|
|
444
|
+
if (executionMode !== "inline")
|
|
445
|
+
throw new Error("Local background job execution mode invariant was violated");
|
|
446
|
+
return {
|
|
447
|
+
argsDigest: sha256Hex(argsJson),
|
|
448
|
+
argsJson,
|
|
449
|
+
concurrency: normalizeBackgroundJobConcurrency({ options, queue, queues }),
|
|
450
|
+
createdAtMs,
|
|
451
|
+
executionMode,
|
|
452
|
+
jobId: new UUID(4).format(),
|
|
453
|
+
jobName,
|
|
454
|
+
maxRetries: normalizeBackgroundJobMaxRetries(options.maxRetries),
|
|
455
|
+
queue,
|
|
456
|
+
scheduledAtMs: normalizeBackgroundJobScheduledAtMs(options.scheduledAtMs, createdAtMs)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Inserts one prepared local job row and its concurrency metadata.
|
|
461
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
462
|
+
* @param {import("./types.js").PreparedLocalBackgroundJob} preparedJob - Prepared row data.
|
|
463
|
+
* @returns {Promise<void>} - Resolves after insertion.
|
|
464
|
+
*/
|
|
465
|
+
async _insertPreparedJob(db, preparedJob) {
|
|
466
|
+
await db.insert({
|
|
467
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE,
|
|
468
|
+
data: {
|
|
469
|
+
args_digest: preparedJob.argsDigest,
|
|
470
|
+
args_json: preparedJob.argsJson,
|
|
471
|
+
attempts: 0,
|
|
472
|
+
completed_at_ms: null,
|
|
473
|
+
concurrency_key: preparedJob.concurrency?.concurrencyKey || null,
|
|
474
|
+
created_at_ms: preparedJob.createdAtMs,
|
|
475
|
+
execution_mode: preparedJob.executionMode,
|
|
476
|
+
failed_at_ms: null,
|
|
477
|
+
handed_off_at_ms: null,
|
|
478
|
+
handoff_id: null,
|
|
479
|
+
id: preparedJob.jobId,
|
|
480
|
+
job_name: preparedJob.jobName,
|
|
481
|
+
last_error: null,
|
|
482
|
+
max_concurrency: preparedJob.concurrency?.maxConcurrency || null,
|
|
483
|
+
max_retries: preparedJob.maxRetries,
|
|
484
|
+
queue: preparedJob.queue,
|
|
485
|
+
scheduled_at_ms: preparedJob.scheduledAtMs,
|
|
486
|
+
status: "queued",
|
|
487
|
+
worker_id: null
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Reconciles configured queue-derived caps and durable counters.
|
|
493
|
+
* @returns {Promise<void>} - Resolves after reconciliation.
|
|
494
|
+
*/
|
|
495
|
+
async reconcileQueueConcurrency() {
|
|
496
|
+
await this.ensureReady();
|
|
497
|
+
await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
498
|
+
const queues = this.configuration.getBackgroundJobsConfig().queues;
|
|
499
|
+
const rows = await db.newQuery().from(LOCAL_BACKGROUND_JOBS_TABLE).where({ status: "queued" }).results();
|
|
500
|
+
for (const rawRow of rows) {
|
|
501
|
+
await this._reconcileQueuedJobConcurrency(db, this._normalizeRow(rawRow), queues);
|
|
502
|
+
}
|
|
503
|
+
await this._rebuildConcurrencyCounts(db);
|
|
504
|
+
}));
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Applies current queue-derived concurrency policy to one queued row.
|
|
508
|
+
* Explicit concurrency keys remain owned by the enqueue contract.
|
|
509
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
510
|
+
* @param {import("./types.js").BackgroundJobRow} job - Queued job snapshot.
|
|
511
|
+
* @param {Record<string, {maxConcurrent?: number, priority?: number}>} queues - Current queue policy snapshot.
|
|
512
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow>} - Reconciled snapshot.
|
|
513
|
+
*/
|
|
514
|
+
async _reconcileQueuedJobConcurrency(db, job, queues) {
|
|
515
|
+
const currentIsQueueDerived = Boolean(job.concurrencyKey?.startsWith(QUEUE_CONCURRENCY_KEY_PREFIX));
|
|
516
|
+
if (job.concurrencyKey && !currentIsQueueDerived)
|
|
517
|
+
return job;
|
|
518
|
+
const concurrency = normalizeBackgroundJobConcurrency({
|
|
519
|
+
options: {},
|
|
520
|
+
queue: job.queue,
|
|
521
|
+
queues
|
|
522
|
+
});
|
|
523
|
+
if (!concurrency) {
|
|
524
|
+
if (currentIsQueueDerived) {
|
|
525
|
+
await db.update({
|
|
526
|
+
conditions: { id: job.id, status: "queued" },
|
|
527
|
+
data: { concurrency_key: null, max_concurrency: null },
|
|
528
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
return { ...job, concurrencyKey: null, maxConcurrency: null };
|
|
532
|
+
}
|
|
533
|
+
await this._ensureConcurrency(db, concurrency);
|
|
534
|
+
if (job.concurrencyKey !== concurrency.concurrencyKey || job.maxConcurrency !== concurrency.maxConcurrency) {
|
|
535
|
+
await db.update({
|
|
536
|
+
conditions: { id: job.id, status: "queued" },
|
|
537
|
+
data: { concurrency_key: concurrency.concurrencyKey, max_concurrency: concurrency.maxConcurrency },
|
|
538
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return { ...job, concurrencyKey: concurrency.concurrencyKey, maxConcurrency: concurrency.maxConcurrency };
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Finds the next eligible local job.
|
|
545
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Next eligible local job.
|
|
546
|
+
*/
|
|
547
|
+
async nextAvailableJob() {
|
|
548
|
+
await this.ensureReady();
|
|
549
|
+
return await this._withDb(async (db) => {
|
|
550
|
+
const jobsTable = db.quoteTable(LOCAL_BACKGROUND_JOBS_TABLE);
|
|
551
|
+
const concurrencyTable = db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE);
|
|
552
|
+
const priorityOrder = this._queuePriorityOrderSql(db);
|
|
553
|
+
let query = db
|
|
554
|
+
.newQuery()
|
|
555
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
556
|
+
.where({ status: "queued" })
|
|
557
|
+
.where(`scheduled_at_ms <= ${db.quote(this.clock.now())}`)
|
|
558
|
+
.where(`(${jobsTable}.${db.quoteColumn("concurrency_key")} IS NULL OR EXISTS (` +
|
|
559
|
+
`SELECT 1 FROM ${concurrencyTable} WHERE ` +
|
|
560
|
+
`${concurrencyTable}.${db.quoteColumn("concurrency_key")} = ${jobsTable}.${db.quoteColumn("concurrency_key")} AND ` +
|
|
561
|
+
`${concurrencyTable}.${db.quoteColumn("active_count")} < ${concurrencyTable}.${db.quoteColumn("max_concurrency")}))`);
|
|
562
|
+
if (priorityOrder)
|
|
563
|
+
query = query.order(`${priorityOrder} DESC`);
|
|
564
|
+
const rows = await query
|
|
565
|
+
.order("scheduled_at_ms ASC")
|
|
566
|
+
.order("created_at_ms ASC")
|
|
567
|
+
.order("id ASC")
|
|
568
|
+
.limit(1)
|
|
569
|
+
.results();
|
|
570
|
+
return rows[0] ? this._normalizeRow(rows[0]) : null;
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Finds the soonest future queued job.
|
|
575
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Soonest future queued job.
|
|
576
|
+
*/
|
|
577
|
+
async nextScheduledJob() {
|
|
578
|
+
await this.ensureReady();
|
|
579
|
+
return await this._withDb(async (db) => {
|
|
580
|
+
const rows = await db
|
|
581
|
+
.newQuery()
|
|
582
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
583
|
+
.where({ status: "queued" })
|
|
584
|
+
.where(`scheduled_at_ms > ${db.quote(this.clock.now())}`)
|
|
585
|
+
.order("scheduled_at_ms ASC")
|
|
586
|
+
.order("created_at_ms ASC")
|
|
587
|
+
.order("id ASC")
|
|
588
|
+
.limit(1)
|
|
589
|
+
.results();
|
|
590
|
+
return rows[0] ? this._normalizeRow(rows[0]) : null;
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Finds a persisted local job by id.
|
|
595
|
+
* @param {string} jobId - Job id.
|
|
596
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Persisted job.
|
|
597
|
+
*/
|
|
598
|
+
async getJob(jobId) {
|
|
599
|
+
await this.ensureReady();
|
|
600
|
+
return await this._withDb(async (db) => await this._getJob(db, jobId));
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Lists local jobs in creation order.
|
|
604
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow[]>} - All local jobs in creation order.
|
|
605
|
+
*/
|
|
606
|
+
async listJobs() {
|
|
607
|
+
await this.ensureReady();
|
|
608
|
+
return await this._withDb(async (db) => {
|
|
609
|
+
const rows = await db
|
|
610
|
+
.newQuery()
|
|
611
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
612
|
+
.order("created_at_ms ASC")
|
|
613
|
+
.order("id ASC")
|
|
614
|
+
.results();
|
|
615
|
+
return rows.map((row) => this._normalizeRow(row));
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Atomically reserves concurrency and claims one queued job.
|
|
620
|
+
* @param {{jobId: string, workerId?: string}} args - Claim request.
|
|
621
|
+
* @returns {Promise<import("./types.js").BackgroundJobHandoff | null>} - Fenced claim.
|
|
622
|
+
*/
|
|
623
|
+
async markHandedOff({ jobId, workerId }) {
|
|
624
|
+
await this.ensureReady();
|
|
625
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
626
|
+
const job = await this._getJob(db, jobId);
|
|
627
|
+
if (!job || job.status !== "queued" || Number(job.scheduledAtMs) > this.clock.now())
|
|
628
|
+
return null;
|
|
629
|
+
if (job.concurrencyKey && !(await this._reserveConcurrency(db, job.concurrencyKey)))
|
|
630
|
+
return null;
|
|
631
|
+
const handedOffAtMs = this.clock.now();
|
|
632
|
+
const handoffId = new UUID(4).format();
|
|
633
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
634
|
+
conditions: { id: jobId, status: "queued" },
|
|
635
|
+
data: { handed_off_at_ms: handedOffAtMs, handoff_id: handoffId, status: "handed_off", worker_id: workerId || "local" },
|
|
636
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
637
|
+
});
|
|
638
|
+
if (affectedRows !== 1) {
|
|
639
|
+
await this._releaseConcurrency(db, job.concurrencyKey);
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
return { handedOffAtMs, handoffId };
|
|
643
|
+
}));
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Finds active local handoffs owned by one worker.
|
|
647
|
+
* @param {{workerId: string}} args - Worker identity.
|
|
648
|
+
* @returns {Promise<Array<{jobId: string, handoffId: string}>>} - Active worker handoffs.
|
|
649
|
+
*/
|
|
650
|
+
async handedOffJobsForWorker({ workerId }) {
|
|
651
|
+
await this.ensureReady();
|
|
652
|
+
const rows = await this._withDb(async (db) => await db
|
|
653
|
+
.newQuery()
|
|
654
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
655
|
+
.where({ status: "handed_off", worker_id: workerId })
|
|
656
|
+
.results());
|
|
657
|
+
/** @type {Array<{jobId: string, handoffId: string}>} */
|
|
658
|
+
const handoffs = [];
|
|
659
|
+
for (const rawRow of rows) {
|
|
660
|
+
const job = this._normalizeRow(rawRow);
|
|
661
|
+
if (job.handoffId)
|
|
662
|
+
handoffs.push({ jobId: job.id, handoffId: job.handoffId });
|
|
663
|
+
}
|
|
664
|
+
return handoffs;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Returns an exact active handoff to the queue.
|
|
668
|
+
* @param {{jobId: string, handoffId: string}} args - Handoff release.
|
|
669
|
+
* @returns {Promise<void>} - Resolves after the fenced release.
|
|
670
|
+
*/
|
|
671
|
+
async markReturnedToQueue({ jobId, handoffId }) {
|
|
672
|
+
await this.ensureReady();
|
|
673
|
+
await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
674
|
+
const job = await this._getJob(db, jobId);
|
|
675
|
+
if (!this._acceptsHandoff(job, handoffId))
|
|
676
|
+
return;
|
|
677
|
+
await this._lockConcurrencyRow(db, job.concurrencyKey);
|
|
678
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
679
|
+
conditions: { handoff_id: handoffId, id: jobId, status: "handed_off" },
|
|
680
|
+
data: {
|
|
681
|
+
handed_off_at_ms: null,
|
|
682
|
+
handoff_id: null,
|
|
683
|
+
scheduled_at_ms: this.clock.now(),
|
|
684
|
+
status: "queued",
|
|
685
|
+
worker_id: null
|
|
686
|
+
},
|
|
687
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
688
|
+
});
|
|
689
|
+
if (affectedRows === 1)
|
|
690
|
+
await this._releaseConcurrency(db, job.concurrencyKey);
|
|
691
|
+
}));
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Applies a fenced successful acknowledgement.
|
|
695
|
+
* @param {{jobId: string, handoffId?: string}} args - Completion report.
|
|
696
|
+
* @returns {Promise<boolean>} - Whether the lease won.
|
|
697
|
+
*/
|
|
698
|
+
async markCompleted({ jobId, handoffId }) {
|
|
699
|
+
await this.ensureReady();
|
|
700
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
701
|
+
const job = await this._getJob(db, jobId);
|
|
702
|
+
if (!this._acceptsHandoff(job, handoffId))
|
|
703
|
+
return false;
|
|
704
|
+
await this._lockConcurrencyRow(db, job.concurrencyKey);
|
|
705
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
706
|
+
conditions: { handoff_id: handoffId, id: jobId, status: "handed_off" },
|
|
707
|
+
data: { completed_at_ms: this.clock.now(), status: "completed" },
|
|
708
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
709
|
+
});
|
|
710
|
+
if (affectedRows !== 1)
|
|
711
|
+
return false;
|
|
712
|
+
await this._releaseConcurrency(db, job.concurrencyKey);
|
|
713
|
+
return true;
|
|
714
|
+
}));
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Applies a fenced reschedule without consuming an attempt.
|
|
718
|
+
* @param {{jobId: string, handoffId?: string, delayMs: number}} args - Reschedule report.
|
|
719
|
+
* @returns {Promise<boolean>} - Whether the lease won.
|
|
720
|
+
*/
|
|
721
|
+
async markRescheduled({ jobId, handoffId, delayMs }) {
|
|
722
|
+
await this.ensureReady();
|
|
723
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
724
|
+
const job = await this._getJob(db, jobId);
|
|
725
|
+
if (!this._acceptsHandoff(job, handoffId))
|
|
726
|
+
return false;
|
|
727
|
+
await this._lockConcurrencyRow(db, job.concurrencyKey);
|
|
728
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
729
|
+
conditions: { handoff_id: handoffId, id: jobId, status: "handed_off" },
|
|
730
|
+
data: {
|
|
731
|
+
handed_off_at_ms: null,
|
|
732
|
+
handoff_id: null,
|
|
733
|
+
scheduled_at_ms: rescheduledBackgroundJobAtMs(delayMs, this.clock.now()),
|
|
734
|
+
status: "queued",
|
|
735
|
+
worker_id: null
|
|
736
|
+
},
|
|
737
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
738
|
+
});
|
|
739
|
+
if (affectedRows !== 1)
|
|
740
|
+
return false;
|
|
741
|
+
await this._releaseConcurrency(db, job.concurrencyKey);
|
|
742
|
+
return true;
|
|
743
|
+
}));
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Applies a fenced failure, retry, or terminal transition.
|
|
747
|
+
* @param {{jobId: string, handoffId?: string, error: ReturnType<typeof JSON.parse>}} args - Failure report.
|
|
748
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Accepted transition snapshot.
|
|
749
|
+
*/
|
|
750
|
+
async markFailed({ jobId, handoffId, error }) {
|
|
751
|
+
await this.ensureReady();
|
|
752
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
753
|
+
const job = await this._getJob(db, jobId);
|
|
754
|
+
if (!this._acceptsHandoff(job, handoffId))
|
|
755
|
+
return null;
|
|
756
|
+
return await this._applyFailure(db, job, error);
|
|
757
|
+
}));
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Turns every abandoned local handoff into the normal failure/retry path.
|
|
761
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow[]>} - Recovered transitions.
|
|
762
|
+
*/
|
|
763
|
+
async recoverHandedOffJobs() {
|
|
764
|
+
await this.ensureReady();
|
|
765
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
766
|
+
const queues = this.configuration.getBackgroundJobsConfig().queues;
|
|
767
|
+
const rows = await db.newQuery().from(LOCAL_BACKGROUND_JOBS_TABLE).where({ status: "handed_off" }).results();
|
|
768
|
+
/** @type {import("./types.js").BackgroundJobRow[]} */
|
|
769
|
+
const recovered = [];
|
|
770
|
+
for (const rawRow of rows) {
|
|
771
|
+
const job = this._normalizeRow(rawRow);
|
|
772
|
+
const updated = await this._applyFailure(db, job, new Error("Local background job recovered after an interrupted dispatcher"));
|
|
773
|
+
if (!updated)
|
|
774
|
+
continue;
|
|
775
|
+
const reconciled = updated.status === "queued"
|
|
776
|
+
? await this._reconcileQueuedJobConcurrency(db, updated, queues)
|
|
777
|
+
: updated;
|
|
778
|
+
recovered.push(reconciled);
|
|
779
|
+
}
|
|
780
|
+
await this._rebuildConcurrencyCounts(db);
|
|
781
|
+
return recovered;
|
|
782
|
+
}));
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Deletes local queue state for focused tests.
|
|
786
|
+
* @returns {Promise<void>} - Resolves after deletion.
|
|
787
|
+
*/
|
|
788
|
+
async clearAll() {
|
|
789
|
+
await this.ensureReady();
|
|
790
|
+
await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
791
|
+
await db.query(`DELETE FROM ${db.quoteTable(LOCAL_BACKGROUND_JOBS_TABLE)}`);
|
|
792
|
+
await db.query(`DELETE FROM ${db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE)}`);
|
|
793
|
+
}));
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Applies the common retry or exhausted failure transition.
|
|
797
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
798
|
+
* @param {import("./types.js").BackgroundJobRow} job - Active handoff.
|
|
799
|
+
* @param {ReturnType<typeof JSON.parse>} error - Performance error.
|
|
800
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Transition snapshot.
|
|
801
|
+
*/
|
|
802
|
+
async _applyFailure(db, job, error) {
|
|
803
|
+
const attempts = (job.attempts || 0) + 1;
|
|
804
|
+
const maxRetries = normalizeBackgroundJobMaxRetries(job.maxRetries);
|
|
805
|
+
const willRetry = attempts <= maxRetries;
|
|
806
|
+
const nowMs = this.clock.now();
|
|
807
|
+
/** @type {Record<string, ReturnType<typeof JSON.parse>>} */
|
|
808
|
+
const data = {
|
|
809
|
+
attempts,
|
|
810
|
+
handed_off_at_ms: null,
|
|
811
|
+
handoff_id: null,
|
|
812
|
+
last_error: normalizeBackgroundJobError(error),
|
|
813
|
+
status: willRetry ? "queued" : "failed",
|
|
814
|
+
worker_id: null
|
|
815
|
+
};
|
|
816
|
+
if (willRetry) {
|
|
817
|
+
Object.assign(data, { scheduled_at_ms: nowMs + retryDelayMs(attempts) });
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
Object.assign(data, { failed_at_ms: nowMs });
|
|
821
|
+
}
|
|
822
|
+
await this._lockConcurrencyRow(db, job.concurrencyKey);
|
|
823
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
824
|
+
conditions: { handoff_id: job.handoffId, id: job.id, status: "handed_off" },
|
|
825
|
+
data,
|
|
826
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
827
|
+
});
|
|
828
|
+
if (affectedRows !== 1)
|
|
829
|
+
return null;
|
|
830
|
+
await this._releaseConcurrency(db, job.concurrencyKey);
|
|
831
|
+
return {
|
|
832
|
+
...job,
|
|
833
|
+
attempts,
|
|
834
|
+
failedAtMs: willRetry ? job.failedAtMs : nowMs,
|
|
835
|
+
handedOffAtMs: null,
|
|
836
|
+
handoffId: null,
|
|
837
|
+
lastError: data.last_error,
|
|
838
|
+
scheduledAtMs: willRetry ? Number(data.scheduled_at_ms) : job.scheduledAtMs,
|
|
839
|
+
status: data.status,
|
|
840
|
+
workerId: null
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Ensures that a durable concurrency counter exists with the required cap.
|
|
845
|
+
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
846
|
+
* @param {import("./types.js").ResolvedBackgroundJobConcurrency} concurrency - Desired counter.
|
|
847
|
+
* @returns {Promise<void>} - Resolves when ensured.
|
|
848
|
+
*/
|
|
849
|
+
async _ensureConcurrency(db, concurrency) {
|
|
850
|
+
await db.upsert({
|
|
851
|
+
conflictColumns: ["concurrency_key"],
|
|
852
|
+
data: { active_count: 0, concurrency_key: concurrency.concurrencyKey, max_concurrency: concurrency.maxConcurrency },
|
|
853
|
+
tableName: LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE,
|
|
854
|
+
updateColumns: ["concurrency_key"]
|
|
855
|
+
});
|
|
856
|
+
const rows = await db
|
|
857
|
+
.newQuery()
|
|
858
|
+
.from(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE)
|
|
859
|
+
.where({ concurrency_key: concurrency.concurrencyKey })
|
|
860
|
+
.limit(1)
|
|
861
|
+
.results();
|
|
862
|
+
const existingRow = /** @type {{max_concurrency: number | string}} */ (rows[0]);
|
|
863
|
+
const existingCap = Number(existingRow.max_concurrency);
|
|
864
|
+
if (existingCap === concurrency.maxConcurrency)
|
|
865
|
+
return;
|
|
866
|
+
if (!concurrency.queueDerived)
|
|
867
|
+
throw new Error(`Conflicting maxConcurrency for background job concurrencyKey: ${concurrency.concurrencyKey}`);
|
|
868
|
+
await db.update({
|
|
869
|
+
conditions: { concurrency_key: concurrency.concurrencyKey },
|
|
870
|
+
data: { max_concurrency: concurrency.maxConcurrency },
|
|
871
|
+
tableName: LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Atomically reserves one slot for a concurrency key.
|
|
876
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
877
|
+
* @param {string} concurrencyKey - Concurrency key.
|
|
878
|
+
* @returns {Promise<boolean>} - Whether a slot was reserved.
|
|
879
|
+
*/
|
|
880
|
+
async _reserveConcurrency(db, concurrencyKey) {
|
|
881
|
+
const table = db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE);
|
|
882
|
+
const count = db.quoteColumn("active_count");
|
|
883
|
+
const affectedRows = await db.affectedRows(`UPDATE ${table} SET ${count} = ${count} + 1 ` +
|
|
884
|
+
`WHERE ${db.quoteColumn("concurrency_key")} = ${db.quote(concurrencyKey)} ` +
|
|
885
|
+
`AND ${count} < ${db.quoteColumn("max_concurrency")}`);
|
|
886
|
+
return affectedRows === 1;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Releases one slot for a concurrency key.
|
|
890
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
891
|
+
* @param {string | null} concurrencyKey - Concurrency key.
|
|
892
|
+
* @returns {Promise<void>} - Resolves after release.
|
|
893
|
+
*/
|
|
894
|
+
async _releaseConcurrency(db, concurrencyKey) {
|
|
895
|
+
if (!concurrencyKey)
|
|
896
|
+
return;
|
|
897
|
+
const table = db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE);
|
|
898
|
+
const count = db.quoteColumn("active_count");
|
|
899
|
+
await db.affectedRows(`UPDATE ${table} SET ${count} = ${count} - 1 ` +
|
|
900
|
+
`WHERE ${db.quoteColumn("concurrency_key")} = ${db.quote(concurrencyKey)} AND ${count} > 0`);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Acquires the transaction's write lock for a concurrency counter row.
|
|
904
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
905
|
+
* @param {string | null} concurrencyKey - Concurrency key.
|
|
906
|
+
* @returns {Promise<void>} - Resolves after locking.
|
|
907
|
+
*/
|
|
908
|
+
async _lockConcurrencyRow(db, concurrencyKey) {
|
|
909
|
+
if (!concurrencyKey)
|
|
910
|
+
return;
|
|
911
|
+
const table = db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE);
|
|
912
|
+
const count = db.quoteColumn("active_count");
|
|
913
|
+
await db.query(`UPDATE ${table} SET ${count} = ${count} ` +
|
|
914
|
+
`WHERE ${db.quoteColumn("concurrency_key")} = ${db.quote(concurrencyKey)}`);
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Rebuilds active counters from durable handed-off jobs.
|
|
918
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
919
|
+
* @returns {Promise<void>} - Resolves after counter rebuild.
|
|
920
|
+
*/
|
|
921
|
+
async _rebuildConcurrencyCounts(db) {
|
|
922
|
+
const concurrencyTable = db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE);
|
|
923
|
+
const jobsTable = db.quoteTable(LOCAL_BACKGROUND_JOBS_TABLE);
|
|
924
|
+
await db.query(`UPDATE ${concurrencyTable} SET ${db.quoteColumn("active_count")} = (` +
|
|
925
|
+
`SELECT COUNT(*) FROM ${jobsTable} WHERE ${jobsTable}.${db.quoteColumn("status")} = ${db.quote("handed_off")} AND ` +
|
|
926
|
+
`${jobsTable}.${db.quoteColumn("concurrency_key")} = ${concurrencyTable}.${db.quoteColumn("concurrency_key")})`);
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Builds the configured queue-priority ordering expression.
|
|
930
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
931
|
+
* @returns {string | null} - Queue priority expression.
|
|
932
|
+
*/
|
|
933
|
+
_queuePriorityOrderSql(db) {
|
|
934
|
+
const queues = this.configuration.getBackgroundJobsConfig().queues;
|
|
935
|
+
const prioritized = Object.entries(queues)
|
|
936
|
+
.filter(([, queue]) => Number.isFinite(queue?.priority) && Number(queue.priority) !== 0)
|
|
937
|
+
.map(([queueName, queue]) => [queueName, Number(queue.priority)]);
|
|
938
|
+
if (prioritized.length === 0)
|
|
939
|
+
return null;
|
|
940
|
+
const whens = prioritized
|
|
941
|
+
.map(([queue, priority]) => `WHEN ${db.quote(queue)} THEN ${priority}`)
|
|
942
|
+
.join(" ");
|
|
943
|
+
return `CASE COALESCE(${db.quoteColumn("queue")}, ${db.quote(DEFAULT_BACKGROUND_JOB_QUEUE)}) ${whens} ELSE 0 END`;
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Checks whether a persisted handoff owns the supplied acknowledgement fence.
|
|
947
|
+
* @param {import("./types.js").BackgroundJobRow | null} job - Persisted job.
|
|
948
|
+
* @param {string | undefined} handoffId - Handoff fence.
|
|
949
|
+
* @returns {job is import("./types.js").BackgroundJobRow} - Whether accepted.
|
|
950
|
+
*/
|
|
951
|
+
_acceptsHandoff(job, handoffId) {
|
|
952
|
+
return Boolean(job && job.status === "handed_off" && job.handoffId && job.handoffId === handoffId);
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Finds a persisted local job using the current connection.
|
|
956
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
957
|
+
* @param {string} jobId - Job id.
|
|
958
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Persisted row.
|
|
959
|
+
*/
|
|
960
|
+
async _getJob(db, jobId) {
|
|
961
|
+
const rows = await db.newQuery().from(LOCAL_BACKGROUND_JOBS_TABLE).where({ id: jobId }).limit(1).results();
|
|
962
|
+
return rows[0] ? this._normalizeRow(rows[0]) : null;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Normalizes one raw local database row.
|
|
966
|
+
* @param {Record<string, ReturnType<typeof JSON.parse>>} row - Raw row.
|
|
967
|
+
* @returns {import("./types.js").BackgroundJobRow} - Normalized row.
|
|
968
|
+
*/
|
|
969
|
+
_normalizeRow(row) {
|
|
970
|
+
const parsedArgs = JSON.parse(String(row.args_json));
|
|
971
|
+
const executionMode = normalizeBackgroundJobExecutionMode({ executionMode: String(row.execution_mode) }, "inline", LOCAL_EXECUTION_MODES);
|
|
972
|
+
if (!Array.isArray(parsedArgs))
|
|
973
|
+
throw new Error(`Invalid local background job args_json for job: ${String(row.id)}`);
|
|
974
|
+
if (executionMode !== "inline")
|
|
975
|
+
throw new Error("Local background job execution mode invariant was violated");
|
|
976
|
+
return {
|
|
977
|
+
args: parsedArgs,
|
|
978
|
+
attempts: this._numberOrNull(row.attempts),
|
|
979
|
+
completedAtMs: this._numberOrNull(row.completed_at_ms),
|
|
980
|
+
concurrencyKey: row.concurrency_key === null || row.concurrency_key === undefined ? null : String(row.concurrency_key),
|
|
981
|
+
createdAtMs: this._numberOrNull(row.created_at_ms),
|
|
982
|
+
executionMode,
|
|
983
|
+
failedAtMs: this._numberOrNull(row.failed_at_ms),
|
|
984
|
+
handedOffAtMs: this._numberOrNull(row.handed_off_at_ms),
|
|
985
|
+
handoffId: row.handoff_id === null || row.handoff_id === undefined ? null : String(row.handoff_id),
|
|
986
|
+
id: String(row.id),
|
|
987
|
+
jobName: String(row.job_name),
|
|
988
|
+
lastError: row.last_error === null || row.last_error === undefined ? null : String(row.last_error),
|
|
989
|
+
maxConcurrency: this._numberOrNull(row.max_concurrency),
|
|
990
|
+
maxRetries: this._numberOrNull(row.max_retries),
|
|
991
|
+
orphanedAtMs: null,
|
|
992
|
+
queue: row.queue ? String(row.queue) : DEFAULT_BACKGROUND_JOB_QUEUE,
|
|
993
|
+
scheduleKey: null,
|
|
994
|
+
scheduledAtMs: this._numberOrNull(row.scheduled_at_ms),
|
|
995
|
+
status: row.status ? String(row.status) : "queued",
|
|
996
|
+
timeoutMs: null,
|
|
997
|
+
workerId: row.worker_id === null || row.worker_id === undefined ? null : String(row.worker_id)
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Normalizes one nullable database number.
|
|
1002
|
+
* @param {ReturnType<typeof JSON.parse>} value - Database number.
|
|
1003
|
+
* @returns {number | null} - Normalized number.
|
|
1004
|
+
*/
|
|
1005
|
+
_numberOrNull(value) {
|
|
1006
|
+
if (value === null || value === undefined || value === "")
|
|
1007
|
+
return null;
|
|
1008
|
+
const number = Number(value);
|
|
1009
|
+
return Number.isNaN(number) ? null : number;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Executes a structured update and reports its affected-row count.
|
|
1013
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
1014
|
+
* @param {import("../database/drivers/base.js").UpdateSqlArgsType} args - Update arguments.
|
|
1015
|
+
* @returns {Promise<number>} - Affected rows.
|
|
1016
|
+
*/
|
|
1017
|
+
async _updateAffectedRows(db, args) { return await db.affectedRows(db.updateSql(args)); }
|
|
1018
|
+
/**
|
|
1019
|
+
* Joins an ambient app transaction or uses the database's scoped operation lease.
|
|
1020
|
+
* @template T
|
|
1021
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
1022
|
+
* @param {(db: import("../database/drivers/base.js").default) => Promise<T>} callback - Mutation.
|
|
1023
|
+
* @returns {Promise<T>} - Mutation result.
|
|
1024
|
+
*/
|
|
1025
|
+
async _mutate(db, callback) {
|
|
1026
|
+
if (db.insideTransaction())
|
|
1027
|
+
return await this._transactionResult(db, async () => await callback(db));
|
|
1028
|
+
return await this.configuration.withTransaction({
|
|
1029
|
+
databaseIdentifier: this.getDatabaseIdentifier(),
|
|
1030
|
+
name: "Local background jobs mutation"
|
|
1031
|
+
}, async (operation) => await callback(operation.connection()));
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Runs a callback in a transaction and returns its captured result.
|
|
1035
|
+
* @template T
|
|
1036
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
1037
|
+
* @param {() => Promise<T>} callback - Transaction callback.
|
|
1038
|
+
* @returns {Promise<T>} - Callback result.
|
|
1039
|
+
*/
|
|
1040
|
+
async _transactionResult(db, callback) {
|
|
1041
|
+
let completed = false;
|
|
1042
|
+
/** @type {T | undefined} */
|
|
1043
|
+
let result;
|
|
1044
|
+
await db.transaction(async () => {
|
|
1045
|
+
result = await callback();
|
|
1046
|
+
completed = true;
|
|
1047
|
+
});
|
|
1048
|
+
if (!completed)
|
|
1049
|
+
throw new Error("Local background jobs transaction callback was not invoked");
|
|
1050
|
+
return /** @type {T} */ (result);
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Runs a callback with the configured local database connection.
|
|
1054
|
+
* @template T
|
|
1055
|
+
* @param {(db: import("../database/drivers/base.js").default) => Promise<T>} callback - Connection callback.
|
|
1056
|
+
* @returns {Promise<T>} - Callback result.
|
|
1057
|
+
*/
|
|
1058
|
+
async _withDb(callback) {
|
|
1059
|
+
const databaseIdentifier = this.getDatabaseIdentifier();
|
|
1060
|
+
return await this.configuration.ensureConnections({ databaseIdentifiers: [databaseIdentifier], name: "Local background jobs store" }, async (dbs) => {
|
|
1061
|
+
const db = dbs[databaseIdentifier];
|
|
1062
|
+
if (!db)
|
|
1063
|
+
throw new Error(`No local background-jobs database connection available for identifier: ${databaseIdentifier}`);
|
|
1064
|
+
return await callback(db);
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Reports an unexpected local-store failure through framework channels.
|
|
1069
|
+
* @param {{error: Error, stage: string}} args - Error report.
|
|
1070
|
+
* @returns {void} - No return value.
|
|
1071
|
+
*/
|
|
1072
|
+
_reportFrameworkError({ error, stage }) {
|
|
1073
|
+
const payload = { context: { stage }, error };
|
|
1074
|
+
const errorEvents = this.configuration.getErrorEvents();
|
|
1075
|
+
errorEvents.emit("framework-error", payload);
|
|
1076
|
+
errorEvents.emit("all-error", { ...payload, errorType: "framework-error" });
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwtc3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYmFja2dyb3VuZC1qb2JzL2xvY2FsLXN0b3JlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLElBQUksTUFBTSxXQUFXLENBQUE7QUFFNUIsT0FBTyxTQUFTLE1BQU0saUNBQWlDLENBQUE7QUFDdkQsT0FBTyxVQUFVLE1BQU0sdUNBQXVDLENBQUE7QUFDOUQsT0FBTyxTQUFTLE1BQU0sd0JBQXdCLENBQUE7QUFDOUMsT0FBTywyQkFBMkIsTUFBTSxzQkFBc0IsQ0FBQTtBQUM5RCxPQUFPLEVBQ0wsNEJBQTRCLEVBQzVCLDRCQUE0QixFQUM1QixpQ0FBaUMsRUFDakMsbUNBQW1DLEVBQ25DLGdDQUFnQyxFQUNoQywyQkFBMkIsRUFDM0IsbUNBQW1DLEVBQ25DLDRCQUE0QixFQUM1QixZQUFZLEVBQ2IsTUFBTSxvQkFBb0IsQ0FBQTtBQUUzQixNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRyxpQ0FBaUMsQ0FBQTtBQUM1RSxNQUFNLENBQUMsTUFBTSxzQ0FBc0MsR0FBRyw0Q0FBNEMsQ0FBQTtBQUNsRyxNQUFNLGdCQUFnQixHQUFHLCtCQUErQixDQUFBO0FBQ3hELE1BQU0sZUFBZSxHQUFHLHVCQUF1QixDQUFBO0FBQy9DLE1BQU0saUJBQWlCLEdBQUcsR0FBRyxDQUFBO0FBQzdCLE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUE7QUFDL0QsTUFBTSxDQUFDLE1BQU0saUNBQWlDLEdBQUc7SUFDL0MsMkNBQTJDO0lBQzNDLG9EQUFvRDtJQUNwRCxxREFBcUQ7SUFDckQsbURBQW1EO0NBQ3BELENBQUE7QUFDRCxNQUFNLG9CQUFvQixHQUFHO0lBQzNCLElBQUk7SUFDSixVQUFVO0lBQ1YsV0FBVztJQUNYLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsT0FBTztJQUNQLGFB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RztJQUNILEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUTtRQUNwQixNQUFNLGtCQUFrQixHQUFHLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFBO1FBRXZELE9BQU8sTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLGlCQUFpQixDQUFDLEVBQUMsbUJBQW1CLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLElBQUksRUFBRSw2QkFBNkIsRUFBQyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsRUFBRTtZQUNoSixNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtZQUVsQyxJQUFJLENBQUMsRUFBRTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDBFQUEwRSxrQkFBa0IsRUFBRSxDQUFDLENBQUE7WUFFeEgsT0FBTyxNQUFNLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUMzQixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gscUJBQXFCLENBQUMsRUFBQyxLQUFLLEVBQUUsS0FBSyxFQUFDO1FBQ2xDLE1BQU0sT0FBTyxHQUFHLEVBQUMsT0FBTyxFQUFFLEVBQUMsS0FBSyxFQUFDLEVBQUUsS0FBSyxFQUFDLENBQUE7UUFDekMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxjQUFjLEVBQUUsQ0FBQTtRQUV2RCxXQUFXLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxDQUFBO1FBQzVDLFdBQVcsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUMsR0FBRyxPQUFPLEVBQUUsU0FBUyxFQUFFLGlCQUFpQixFQUFDLENBQUMsQ0FBQTtJQUMzRSxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IFVVSUQgZnJvbSBcInB1cmUtdXVpZFwiXG5cbmltcG9ydCBUYWJsZURhdGEgZnJvbSBcIi4uL2RhdGFiYXNlL3RhYmxlLWRhdGEvaW5kZXguanNcIlxuaW1wb3J0IFRhYmxlSW5kZXggZnJvbSBcIi4uL2RhdGFiYXNlL3RhYmxlLWRhdGEvdGFibGUtaW5kZXguanNcIlxuaW1wb3J0IHNoYTI1NkhleCBmcm9tIFwiLi4vdXRpbHMvc2hhMjU2LWhleC5qc1wiXG5pbXBvcnQgbm9ybWFsaXplQmFja2dyb3VuZEpvYkVycm9yIGZyb20gXCIuL25vcm1hbGl6ZS1lcnJvci5qc1wiXG5pbXBvcnQge1xuICBERUZBVUxUX0JBQ0tHUk9VTkRfSk9CX1FVRVVFLFxuICBRVUVVRV9DT05DVVJSRU5DWV9LRVlfUFJFRklYLFxuICBub3JtYWxpemVCYWNrZ3JvdW5kSm9iQ29uY3VycmVuY3ksXG4gIG5vcm1hbGl6ZUJhY2tncm91bmRKb2JFeGVjdXRpb25Nb2RlLFxuICBub3JtYWxpemVCYWNrZ3JvdW5kSm9iTWF4UmV0cmllcyxcbiAgbm9ybWFsaXplQmFja2dyb3VuZEpvYlF1ZXVlLFxuICBub3JtYWxpemVCYWNrZ3JvdW5kSm9iU2NoZWR1bGVkQXRNcyxcbiAgcmVzY2hlZHVsZWRCYWNrZ3JvdW5kSm9iQXRNcyxcbiAgcmV0cnlEZWxheU1zXG59IGZyb20gXCIuL2pvYi1zZW1hbnRpY3MuanNcIlxuXG5leHBvcnQgY29uc3QgTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFID0gXCJ2ZWxvY2lvdXNfbG9jYWxfYmFja2dyb3VuZF9qb2JzXCJcbmV4cG9ydCBjb25zdCBMT0NBTF9CQUNLR1JPVU5EX0pPQl9DT05DVVJSRU5DWV9UQUJMRSA9IFwidmVsb2Npb3VzX2xvY2FsX2JhY2tncm91bmRfam9iX2NvbmN1cnJlbmN5XCJcbmNvbnN0IE1JR1JBVElPTlNfVEFCTEUgPSBcInZlbG9jaW91c19pbnRlcm5hbF9taWdyYXRpb25zXCJcbmNvbnN0IE1JR1JBVElPTl9TQ09QRSA9IFwibG9jYWxfYmFja2dyb3VuZF9qb2JzXCJcbmNvbnN0IE1JR1JBVElPTl9WRVJTSU9OID0gXCIxXCJcbmNvbnN0IExPQ0FMX0VYRUNVVElPTl9NT0RFUyA9IFsvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJpbmxpbmVcIildXG5leHBvcnQgY29uc3QgTE9DQUxfQkFDS0dST1VORF9KT0JTX0lOREVYX05BTUVTID0gW1xuICBcImluZGV4X3ZlbG9jaW91c19sb2NhbF9iYWNrZ3JvdW5kX2pvYnNfZHVlXCIsXG4gIFwiaW5kZXhfdmVsb2Npb3VzX2xvY2FsX2JhY2tncm91bmRfam9ic19xdWV1ZV9zdGF0dXNcIixcbiAgXCJpbmRleF92ZWxvY2lvdXNfbG9jYWxfYmFja2dyb3VuZF9qb2JzX2RlZHVwbGljYXRpb25cIixcbiAgXCJpbmRleF92ZWxvY2lvdXNfbG9jYWxfYmFja2dyb3VuZF9qb2JzX2NvbmN1cnJlbmN5XCJcbl1cbmNvbnN0IEVYUEVDVEVEX0pPQl9DT0xVTU5TID0gW1xuICBcImlkXCIsXG4gIFwiam9iX25hbWVcIixcbiAgXCJhcmdzX2pzb25cIixcbiAgXCJhcmdzX2RpZ2VzdFwiLFxuICBcImV4ZWN1dGlvbl9tb2RlXCIsXG4gIFwicXVldWVcIixcbiAgXCJtYXhfcmV0cmllc1wiLFxuICBcImF0dGVtcHRzXCIsXG4gIFwic3RhdHVzXCIsXG4gIFwic2NoZWR1bGVkX2F0X21zXCIsXG4gIFwiY3JlYXRlZF9hdF9tc1wiLFxuICBcImhhbmRlZF9vZmZfYXRfbXNcIixcbiAgXCJoYW5kb2ZmX2lkXCIsXG4gIFwid29ya2VyX2lkXCIsXG4gIFwiY29tcGxldGVkX2F0X21zXCIsXG4gIFwiZmFpbGVkX2F0X21zXCIsXG4gIFwibGFzdF9lcnJvclwiLFxuICBcImNvbmN1cnJlbmN5X2tleVwiLFxuICBcIm1heF9jb25jdXJyZW5jeVwiXG5dXG5jb25zdCBFWFBFQ1RFRF9DT05DVVJSRU5DWV9DT0xVTU5TID0gW1wiY29uY3VycmVuY3lfa2V5XCIsIFwibWF4X2NvbmN1cnJlbmN5XCIsIFwiYWN0aXZlX2NvdW50XCJdXG4vKiogQHR5cGUge1dlYWtNYXA8aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0LCBNYXA8c3RyaW5nLCBQcm9taXNlPHZvaWQ+Pj59ICovXG5jb25zdCBkZWR1cGxpY2F0ZWRFbnF1ZXVlQ2hhaW5zID0gbmV3IFdlYWtNYXAoKVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIHByb2R1Y3Rpb24gY2xvY2sgdXNlZCBieSBsb2NhbCBkaXNwYXRjaC5cbiAqIEByZXR1cm5zIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkxvY2FsQmFja2dyb3VuZEpvYnNDbG9ja30gLSBQcm9kdWN0aW9uIGNsb2NrLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9jYWxCYWNrZ3JvdW5kSm9ic0Nsb2NrKCkge1xuICByZXR1cm4ge1xuICAgIGNsZWFyVGltZW91dDogKHRpbWVySWQpID0+IGdsb2JhbFRoaXMuY2xlYXJUaW1lb3V0KHRpbWVySWQpLFxuICAgIG5vdzogKCkgPT4gRGF0ZS5ub3coKSxcbiAgICBzZXRUaW1lb3V0OiAoY2FsbGJhY2ssIGRlbGF5TXMpID0+IGdsb2JhbFRoaXMuc2V0VGltZW91dChjYWxsYmFjaywgZGVsYXlNcylcbiAgfVxufVxuXG4vKiogTmFtZXNwYWNlZCBwb3J0YWJsZSBTUUxpdGUgcGVyc2lzdGVuY2UgZm9yIGxvY2FsIGJhY2tncm91bmQgam9icy4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIExvY2FsQmFja2dyb3VuZEpvYnNTdG9yZSB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc3RvcmUgZm9yIG9uZSBjb25maWd1cmF0aW9uIGFuZCBsb2NhbCBkYXRhYmFzZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBTdG9yZSBvcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gYXJncy5jb25maWd1cmF0aW9uIC0gT3duaW5nIGNvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5Mb2NhbEJhY2tncm91bmRKb2JzQ2xvY2t9IFthcmdzLmNsb2NrXSAtIFBlcnNpc3RlbmNlIGNsb2NrLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MuZGF0YWJhc2VJZGVudGlmaWVyXSAtIENvbmZpZ3VyZWQgbG9jYWwgZGF0YWJhc2UgaWRlbnRpZmllci5cbiAgICogQHBhcmFtIHsoKSA9PiB2b2lkfSBbYXJncy5vbkNvbW1pdHRlZEVucXVldWVdIC0gQ29tbWl0LWF3YXJlIGRpc3BhdGNoZXIgd2FrZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHtjb25maWd1cmF0aW9uLCBjbG9jayA9IGxvY2FsQmFja2dyb3VuZEpvYnNDbG9jaygpLCBkYXRhYmFzZUlkZW50aWZpZXIsIG9uQ29tbWl0dGVkRW5xdWV1ZX0pIHtcbiAgICB0aGlzLmNsb2NrID0gY2xvY2tcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uXG4gICAgdGhpcy5kYXRhYmFzZUlkZW50aWZpZXIgPSBkYXRhYmFzZUlkZW50aWZpZXJcbiAgICB0aGlzLm9uQ29tbWl0dGVkRW5xdWV1ZSA9IG9uQ29tbWl0dGVkRW5xdWV1ZVxuICAgIHRoaXMuX2lzUmVhZHkgPSBmYWxzZVxuICAgIC8qKiBAdHlwZSB7UHJvbWlzZTx2b2lkPiB8IG51bGx9ICovXG4gICAgdGhpcy5fcmVhZHlQcm9taXNlID0gbnVsbFxuICAgIC8qKiBAdHlwZSB7V2Vha01hcDxpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdCwge2NvbXBsZXRpb246IFByb21pc2U8dm9pZD4sIHByb21pc2U6IFByb21pc2U8dm9pZD59Pn0gKi9cbiAgICB0aGlzLl90cmFuc2FjdGlvblJlYWR5UHJvbWlzZXMgPSBuZXcgV2Vha01hcCgpXG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZXMgdGhlIGNvbmZpZ3VyZWQgbG9jYWwgZGF0YWJhc2UgaWRlbnRpZmllci5cbiAgICogQHJldHVybnMge3N0cmluZ30gLSBEYXRhYmFzZSBpZGVudGlmaWVyLlxuICAgKi9cbiAgZ2V0RGF0YWJhc2VJZGVudGlmaWVyKCkge1xuICAgIHJldHVybiB0aGlzLmRhdGFiYXNlSWRlbnRpZmllciB8fCB0aGlzLmNvbmZpZ3VyYXRpb24uZ2V0QmFja2dyb3VuZEpvYnNDb25maWcoKS5kYXRhYmFzZUlkZW50aWZpZXJcbiAgfVxuXG4gIC8qKlxuICAgKiBFbnN1cmVzIHRoZSB2ZXJzaW9uZWQgcGh5c2ljYWwgc2NoZW1hIGV4aXN0cy5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiByZWFkeS5cbiAgICovXG4gIGFzeW5jIGVuc3VyZVJlYWR5KCkge1xuICAgIGlmICh0aGlzLl9pc1JlYWR5KSByZXR1cm5cblxuICAgIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IGF3YWl0IHRoaXMuX2Vuc3VyZVJlYWR5V2l0aERiKGRiKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhcnMgdGhlIHBlci1pbnN0YW5jZSByZWFkaW5lc3MgbGF0Y2ggZm9yIGEgZGVsaWJlcmF0ZSBhZGFwdGVyIHJlb3Blbi5cbiAgICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgcmVzZXRSZWFkaW5lc3MoKSB7XG4gICAgdGhpcy5faXNSZWFkeSA9IGZhbHNlXG4gICAgdGhpcy5fcmVhZHlQcm9taXNlID0gbnVsbFxuICAgIHRoaXMuX3RyYW5zYWN0aW9uUmVhZHlQcm9taXNlcyA9IG5ldyBXZWFrTWFwKClcbiAgfVxuXG4gIC8qKlxuICAgKiBDb29yZGluYXRlcyBwaHlzaWNhbCBhbmQgdHJhbnNhY3Rpb24tbG9jYWwgc2NoZW1hIHJlYWRpbmVzcy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBMb2NhbCBTUUxpdGUgY29ubmVjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiB0aGlzIGNhbGxlciBjYW4gdXNlIHRoZSBzY2hlbWEuXG4gICAqL1xuICBhc3luYyBfZW5zdXJlUmVhZHlXaXRoRGIoZGIpIHtcbiAgICBpZiAodGhpcy5faXNSZWFkeSkgcmV0dXJuXG5cbiAgICBjb25zdCB0cmFuc2FjdGlvbkNvbXBsZXRpb24gPSBkYi5pbnNpZGVUcmFuc2FjdGlvbigpID8gZGIudHJhbnNhY3Rpb25Db21wbGV0aW9uKCkgOiBudWxsXG4gICAgY29uc3QgdHJhbnNhY3Rpb25SZWFkeSA9IHRoaXMuX3RyYW5zYWN0aW9uUmVhZHlQcm9taXNlcy5nZXQoZGIpXG5cbiAgICBpZiAodHJhbnNhY3Rpb25Db21wbGV0aW9uICYmIHRyYW5zYWN0aW9uUmVhZHk/LmNvbXBsZXRpb24gPT09IHRyYW5zYWN0aW9uQ29tcGxldGlvbikge1xuICAgICAgYXdhaXQgdHJhbnNhY3Rpb25SZWFkeS5wcm9taXNlXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAodGhpcy5fcmVhZHlQcm9taXNlKSB7XG4gICAgICBjb25zdCByZWFkeVByb21pc2UgPSB0aGlzLl9yZWFkeVByb21pc2VcblxuICAgICAgYXdhaXQgcmVhZHlQcm9taXNlXG4gICAgICBpZiAodGhpcy5fcmVhZHlQcm9taXNlID09PSByZWFkeVByb21pc2UpIHRoaXMuX3JlYWR5UHJvbWlzZSA9IG51bGxcbiAgICAgIGlmICh0aGlzLl9pc1JlYWR5KSByZXR1cm5cblxuICAgICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAodHJhbnNhY3Rpb25Db21wbGV0aW9uKSB7XG4gICAgICBjb25zdCBzY2hlbWFSZWFkeVByb21pc2UgPSB0aGlzLl9hcHBseVNjaGVtYShkYilcbiAgICAgIGNvbnN0IHRyYW5zYWN0aW9uUmVhZHlQcm9taXNlID0gc2NoZW1hUmVhZHlQcm9taXNlLnRoZW4oKCkgPT4gdW5kZWZpbmVkKVxuICAgICAgY29uc3QgdHJhbnNhY3Rpb25SZWFkeSA9IHtjb21wbGV0aW9uOiB0cmFuc2FjdGlvbkNvbXBsZXRpb24sIHByb21pc2U6IHRyYW5zYWN0aW9uUmVhZHlQcm9taXNlfVxuICAgICAgY29uc3QgZHVyYWJsZVJlYWR5UHJvbWlzZSA9IHNjaGVtYVJlYWR5UHJvbWlzZS50aGVuKGFzeW5jIChjaGFuZ2VkKSA9PiB7XG4gICAgICAgIGlmICghY2hhbmdlZCkge1xuICAgICAgICAgIHRoaXMuX2lzUmVhZHkgPSB0cnVlXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICBhd2FpdCB0cmFuc2FjdGlvbkNvbXBsZXRpb25cbiAgICAgIH0sICgpID0+IHtcbiAgICAgICAgLy8gVGhlIHRyYW5zYWN0aW9uLWxvY2FsIGNhbGxlciBiZWxvdyBvd25zIGFuZCByZXRocm93cyB0aGlzIHNhbWUgc2NoZW1hIGVycm9yLlxuICAgICAgICAvLyBUaGlzIGJyYW5jaCBvbmx5IHNldHRsZXMgdGhlIHNoYXJlZCBkdXJhYmlsaXR5IGJhcnJpZXIgc28gaXQgY2Fubm90IGJlY29tZVxuICAgICAgICAvLyBhbiBpbmRlcGVuZGVudCB1bmhhbmRsZWQgcmVqZWN0aW9uIHdoaWxlIGZhaWxlZCBvd25lcnNoaXAgaXMgY2xlYXJlZC5cbiAgICAgIH0pXG5cbiAgICAgIHRoaXMuX3RyYW5zYWN0aW9uUmVhZHlQcm9taXNlcy5zZXQoZGIsIHRyYW5zYWN0aW9uUmVhZHkpXG4gICAgICB0aGlzLl9yZWFkeVByb21pc2UgPSBkdXJhYmxlUmVhZHlQcm9taXNlXG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRyYW5zYWN0aW9uUmVhZHlQcm9taXNlXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAodGhpcy5fdHJhbnNhY3Rpb25SZWFkeVByb21pc2VzLmdldChkYikgPT09IHRyYW5zYWN0aW9uUmVhZHkpIHRoaXMuX3RyYW5zYWN0aW9uUmVhZHlQcm9taXNlcy5kZWxldGUoZGIpXG4gICAgICAgIGlmICh0aGlzLl9yZWFkeVByb21pc2UgPT09IGR1cmFibGVSZWFkeVByb21pc2UpIHRoaXMuX3JlYWR5UHJvbWlzZSA9IG51bGxcbiAgICAgICAgdGhyb3cgZXJyb3JcbiAgICAgIH1cbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHRoaXMuX3JlYWR5UHJvbWlzZSA9IHRoaXMuX3RyYW5zYWN0aW9uUmVzdWx0KGRiLCBhc3luYyAoKSA9PiBhd2FpdCB0aGlzLl9hcHBseVNjaGVtYShkYikpLnRoZW4oKCkgPT4ge1xuICAgICAgdGhpcy5faXNSZWFkeSA9IHRydWVcbiAgICB9KVxuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuX3JlYWR5UHJvbWlzZVxuICAgIH0gZmluYWxseSB7XG4gICAgICBpZiAoIXRoaXMuX2lzUmVhZHkpIHRoaXMuX3JlYWR5UHJvbWlzZSA9IG51bGxcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBvciByZXBhaXJzIHZlcnNpb24tb25lIHRhYmxlcyBhbmQgaW5kZXhlcy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBMb2NhbCBTUUxpdGUgY29ubmVjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IC0gV2hldGhlciBzY2hlbWEgc3RhdGUgY2hhbmdlZC5cbiAgICovXG4gIGFzeW5jIF9hcHBseVNjaGVtYShkYikge1xuICAgIGxldCBjaGFuZ2VkID0gZmFsc2VcblxuICAgIGlmICghKGF3YWl0IGRiLnRhYmxlRXhpc3RzKE1JR1JBVElPTlNfVEFCTEUpKSkge1xuICAgICAgYXdhaXQgZGIuY3JlYXRlVGFibGUodGhpcy5fbWlncmF0aW9uc1RhYmxlRGF0YSgpKVxuICAgICAgY2hhbmdlZCA9IHRydWVcbiAgICB9XG5cbiAgICBpZiAoIShhd2FpdCBkYi50YWJsZUV4aXN0cyhMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEUpKSkge1xuICAgICAgYXdhaXQgZGIuY3JlYXRlVGFibGUodGhpcy5fam9ic1RhYmxlRGF0YSgpKVxuICAgICAgY2hhbmdlZCA9IHRydWVcbiAgICB9IGVsc2Uge1xuICAgICAgYXdhaXQgdGhpcy5fYXNzZXJ0Q29sdW1ucyhkYiwgTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFLCBFWFBFQ1RFRF9KT0JfQ09MVU1OUylcbiAgICB9XG5cbiAgICBpZiAoIShhd2FpdCBkYi50YWJsZUV4aXN0cyhMT0NBTF9CQUNLR1JPVU5EX0pPQl9DT05DVVJSRU5DWV9UQUJMRSkpKSB7XG4gICAgICBhd2FpdCBkYi5jcmVhdGVUYWJsZSh0aGlzLl9jb25jdXJyZW5jeVRhYmxlRGF0YSgpKVxuICAgICAgY2hhbmdlZCA9IHRydWVcbiAgICB9IGVsc2Uge1xuICAgICAgYXdhaXQgdGhpcy5fYXNzZXJ0Q29sdW1ucyhkYiwgTE9DQUxfQkFDS0dST1VORF9KT0JfQ09OQ1VSUkVOQ1lfVEFCTEUsIEVYUEVDVEVEX0NPTkNVUlJFTkNZX0NPTFVNTlMpXG4gICAgfVxuXG4gICAgaWYgKGF3YWl0IHRoaXMuX2Vuc3VyZUluZGV4ZXMoZGIpKSBjaGFuZ2VkID0gdHJ1ZVxuXG4gICAgaWYgKCEoYXdhaXQgdGhpcy5faGFzTWlncmF0aW9uKGRiKSkpIHtcbiAgICAgIGF3YWl0IGRiLnVwc2VydCh7XG4gICAgICAgIHRhYmxlTmFtZTogTUlHUkFUSU9OU19UQUJMRSxcbiAgICAgICAgZGF0YToge1xuICAgICAgICAgIGFwcGxpZWRfYXRfbXM6IHRoaXMuY2xvY2subm93KCksXG4gICAgICAgICAga2V5OiB0aGlzLl9taWdyYXRpb25LZXkoKSxcbiAgICAgICAgICBzY29wZTogTUlHUkFUSU9OX1NDT1BFLFxuICAgICAgICAgIHZlcnNpb246IE1JR1JBVElPTl9WRVJTSU9OXG4gICAgICAgIH0sXG4gICAgICAgIGNvbmZsaWN0Q29sdW1uczogW1wia2V5XCJdLFxuICAgICAgICB1cGRhdGVDb2x1bW5zOiBbXCJzY29wZVwiLCBcInZlcnNpb25cIiwgXCJhcHBsaWVkX2F0X21zXCJdXG4gICAgICB9KVxuICAgICAgY2hhbmdlZCA9IHRydWVcbiAgICB9XG5cbiAgICByZXR1cm4gY2hhbmdlZFxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyB0aGUgbWlncmF0aW9uIGxlZGdlciB0YWJsZSBkZWZpbml0aW9uLlxuICAgKiBAcmV0dXJucyB7VGFibGVEYXRhfSAtIE1pZ3JhdGlvbiBsZWRnZXIgdGFibGUuXG4gICAqL1xuICBfbWlncmF0aW9uc1RhYmxlRGF0YSgpIHtcbiAgICBjb25zdCB0YWJsZSA9IG5ldyBUYWJsZURhdGEoTUlHUkFUSU9OU19UQUJMRSwge2lmTm90RXhpc3RzOiB0cnVlfSlcblxuICAgIHRhYmxlLnN0cmluZyhcImtleVwiLCB7bnVsbDogZmFsc2UsIHByaW1hcnlLZXk6IHRydWV9KVxuICAgIHRhYmxlLnN0cmluZyhcInNjb3BlXCIsIHtudWxsOiBmYWxzZX0pXG4gICAgdGFibGUuc3RyaW5nKFwidmVyc2lvblwiLCB7bnVsbDogZmFsc2V9KVxuICAgIHRhYmxlLmJpZ2ludChcImFwcGxpZWRfYXRfbXNcIiwge251bGw6IGZhbHNlfSlcbiAgICByZXR1cm4gdGFibGVcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIGxvY2FsIGpvYnMgdGFibGUgZGVmaW5pdGlvbi5cbiAgICogQHJldHVybnMge1RhYmxlRGF0YX0gLSBMb2NhbCBqb2JzIHRhYmxlIGRlZmluaXRpb24uXG4gICAqL1xuICBfam9ic1RhYmxlRGF0YSgpIHtcbiAgICBjb25zdCB0YWJsZSA9IG5ldyBUYWJsZURhdGEoTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFLCB7aWZOb3RFeGlzdHM6IHRydWV9KVxuXG4gICAgdGFibGUuc3RyaW5nKFwiaWRcIiwge251bGw6IGZhbHNlLCBwcmltYXJ5S2V5OiB0cnVlfSlcbiAgICB0YWJsZS5zdHJpbmcoXCJqb2JfbmFtZVwiLCB7bnVsbDogZmFsc2V9KVxuICAgIHRhYmxlLnRleHQoXCJhcmdzX2pzb25cIiwge251bGw6IGZhbHNlfSlcbiAgICB0YWJsZS5zdHJpbmcoXCJhcmdzX2RpZ2VzdFwiLCB7bWF4TGVuZ3RoOiA2NCwgbnVsbDogZmFsc2V9KVxuICAgIHRhYmxlLnN0cmluZyhcImV4ZWN1dGlvbl9tb2RlXCIsIHtudWxsOiBmYWxzZX0pXG4gICAgdGFibGUuc3RyaW5nKFwicXVldWVcIiwge251bGw6IGZhbHNlfSlcbiAgICB0YWJsZS5pbnRlZ2VyKFwibWF4X3JldHJpZXNcIiwge251bGw6IGZhbHNlfSlcbiAgICB0YWJsZS5pbnRlZ2VyKFwiYXR0ZW1wdHNcIiwge251bGw6IGZhbHNlfSlcbiAgICB0YWJsZS5zdHJpbmcoXCJzdGF0dXNcIiwge251bGw6IGZhbHNlfSlcbiAgICB0YWJsZS5iaWdpbnQoXCJzY2hlZHVsZWRfYXRfbXNcIiwge251bGw6IGZhbHNlfSlcbiAgICB0YWJsZS5iaWdpbnQoXCJjcmVhdGVkX2F0X21zXCIsIHtudWxsOiBmYWxzZX0pXG4gICAgdGFibGUuYmlnaW50KFwiaGFuZGVkX29mZl9hdF9tc1wiLCB7bnVsbDogdHJ1ZX0pXG4gICAgdGFibGUuc3RyaW5nKFwiaGFuZG9mZl9pZFwiLCB7bnVsbDogdHJ1ZX0pXG4gICAgdGFibGUuc3RyaW5nKFwid29ya2VyX2lkXCIsIHtudWxsOiB0cnVlfSlcbiAgICB0YWJsZS5iaWdpbnQoXCJjb21wbGV0ZWRfYXRfbXNcIiwge251bGw6IHRydWV9KVxuICAgIHRhYmxlLmJpZ2ludChcImZhaWxlZF9hdF9tc1wiLCB7bnVsbDogdHJ1ZX0pXG4gICAgdGFibGUudGV4dChcImxhc3RfZXJyb3JcIiwge251bGw6IHRydWV9KVxuICAgIHRhYmxlLnN0cmluZyhcImNvbmN1cnJlbmN5X2tleVwiLCB7bnVsbDogdHJ1ZX0pXG4gICAgdGFibGUuaW50ZWdlcihcIm1heF9jb25jdXJyZW5jeVwiLCB7bnVsbDogdHJ1ZX0pXG4gICAgdGFibGUuYWRkSW5kZXgobmV3IFRhYmxlSW5kZXgoW1wic3RhdHVzXCIsIFwic2NoZWR1bGVkX2F0X21zXCIsIFwiY3JlYXRlZF9hdF9tc1wiLCBcImlkXCJdLCB7bmFtZTogTE9DQUxfQkFDS0dST1VORF9KT0JTX0lOREVYX05BTUVTWzBdfSkpXG4gICAgdGFibGUuYWRkSW5kZXgobmV3IFRhYmxlSW5kZXgoW1wicXVldWVcIiwgXCJzdGF0dXNcIiwgXCJjcmVhdGVkX2F0X21zXCJdLCB7bmFtZTogTE9DQUxfQkFDS0dST1VORF9KT0JTX0lOREVYX05BTUVTWzFdfSkpXG4gICAgdGFibGUuYWRkSW5kZXgobmV3IFRhYmxlSW5kZXgoW1wiYXJnc19kaWdlc3RcIl0sIHtuYW1lOiBMT0NBTF9CQUNLR1JPVU5EX0pPQlNfSU5ERVhfTkFNRVNbMl19KSlcbiAgICB0YWJsZS5hZGRJbmRleChuZXcgVGFibGVJbmRleChbXCJzdGF0dXNcIiwgXCJjb25jdXJyZW5jeV9rZXlcIiwgXCJzY2hlZHVsZWRfYXRfbXNcIl0sIHtuYW1lOiBMT0NBTF9CQUNLR1JPVU5EX0pPQlNfSU5ERVhfTkFNRVNbM119KSlcbiAgICByZXR1cm4gdGFibGVcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIGxvY2FsIGNvbmN1cnJlbmN5IGNvdW50ZXIgdGFibGUgZGVmaW5pdGlvbi5cbiAgICogQHJldHVybnMge1RhYmxlRGF0YX0gLSBDb25jdXJyZW5jeSBjb3VudGVyIHRhYmxlIGRlZmluaXRpb24uXG4gICAqL1xuICBfY29uY3VycmVuY3lUYWJsZURhdGEoKSB7XG4gICAgY29uc3QgdGFibGUgPSBuZXcgVGFibGVEYXRhKExPQ0FMX0JBQ0tHUk9VTkRfSk9CX0NPTkNVUlJFTkNZX1RBQkxFLCB7aWZOb3RFeGlzdHM6IHRydWV9KVxuXG4gICAgdGFibGUuc3RyaW5nKFwiY29uY3VycmVuY3lfa2V5XCIsIHtudWxsOiBmYWxzZSwgcHJpbWFyeUtleTogdHJ1ZX0pXG4gICAgdGFibGUuaW50ZWdlcihcIm1heF9jb25jdXJyZW5jeVwiLCB7bnVsbDogZmFsc2V9KVxuICAgIHRhYmxlLmludGVnZXIoXCJhY3RpdmVfY291bnRcIiwge251bGw6IGZhbHNlfSlcbiAgICByZXR1cm4gdGFibGVcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWplY3RzIGFuIGluY29tcGF0aWJsZSBjdXJyZW50LXZlcnNpb24gdGFibGUgcmF0aGVyIHRoYW4gcmVidWlsZGluZyBkYXRhLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIExvY2FsIFNRTGl0ZSBjb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGFibGVOYW1lIC0gVGFibGUgbmFtZS5cbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gZXhwZWN0ZWRDb2x1bW5zIC0gUmVxdWlyZWQgY29sdW1ucy5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wYXRpYmxlLlxuICAgKi9cbiAgYXN5bmMgX2Fzc2VydENvbHVtbnMoZGIsIHRhYmxlTmFtZSwgZXhwZWN0ZWRDb2x1bW5zKSB7XG4gICAgY29uc3QgdGFibGUgPSBhd2FpdCBkYi5nZXRUYWJsZUJ5TmFtZU9yRmFpbCh0YWJsZU5hbWUpXG4gICAgY29uc3QgY29sdW1ucyA9IGF3YWl0IHRhYmxlLmdldENvbHVtbnMoKVxuICAgIGNvbnN0IG5hbWVzID0gbmV3IFNldChjb2x1bW5zLm1hcCgoY29sdW1uKSA9PiBjb2x1bW4uZ2V0TmFtZSgpKSlcbiAgICBjb25zdCBtaXNzaW5nID0gZXhwZWN0ZWRDb2x1bW5zLmZpbHRlcigoY29sdW1uTmFtZSkgPT4gIW5hbWVzLmhhcyhjb2x1bW5OYW1lKSlcblxuICAgIGlmIChtaXNzaW5nLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihgSW5jb21wYXRpYmxlIGxvY2FsIGJhY2tncm91bmQtam9icyBzY2hlbWEgZm9yICR7dGFibGVOYW1lfTsgbWlzc2luZyBjb2x1bW5zOiAke21pc3Npbmcuam9pbihcIiwgXCIpfWApXG5cbiAgICB0aGlzLl9yZXBvcnRGcmFtZXdvcmtFcnJvcih7ZXJyb3IsIHN0YWdlOiBcImxvY2FsLWJhY2tncm91bmQtam9icy1zY2hlbWFcIn0pXG4gICAgdGhyb3cgZXJyb3JcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNyZWF0ZXMgbWlzc2luZyBpbmRleGVzIGRlY2xhcmVkIGJ5IHRoZSBjdXJyZW50IHNjaGVtYS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBMb2NhbCBTUUxpdGUgY29ubmVjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IC0gV2hldGhlciBhbiBpbmRleCB3YXMgY3JlYXRlZC5cbiAgICovXG4gIGFzeW5jIF9lbnN1cmVJbmRleGVzKGRiKSB7XG4gICAgZGIuY2xlYXJTY2hlbWFDYWNoZSgpXG4gICAgY29uc3Qgam9ic1RhYmxlID0gYXdhaXQgZGIuZ2V0VGFibGVCeU5hbWVPckZhaWwoTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFKVxuICAgIGNvbnN0IGV4aXN0aW5nTmFtZXMgPSBuZXcgU2V0KChhd2FpdCBqb2JzVGFibGUuZ2V0SW5kZXhlcygpKS5tYXAoKGluZGV4KSA9PiBpbmRleC5nZXROYW1lKCkpKVxuICAgIGxldCBjaGFuZ2VkID0gZmFsc2VcblxuICAgIGZvciAoY29uc3QgaW5kZXggb2YgdGhpcy5fam9ic1RhYmxlRGF0YSgpLmdldEluZGV4ZXMoKSkge1xuICAgICAgY29uc3QgaW5kZXhOYW1lID0gaW5kZXguZ2V0TmFtZSgpXG5cbiAgICAgIGlmICghaW5kZXhOYW1lIHx8IGV4aXN0aW5nTmFtZXMuaGFzKGluZGV4TmFtZSkpIGNvbnRpbnVlXG5cbiAgICAgIGNvbnN0IHNxbHMgPSBhd2FpdCBkYi5jcmVhdGVJbmRleFNRTHMoe1xuICAgICAgICBjb2x1bW5zOiBpbmRleC5nZXRDb2x1bW5zKCksXG4gICAgICAgIGlmTm90RXhpc3RzOiB0cnVlLFxuICAgICAgICBuYW1lOiBpbmRleE5hbWUsXG4gICAgICAgIHRhYmxlTmFtZTogTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFLFxuICAgICAgICB1bmlxdWU6IGluZGV4LmdldFVuaXF1ZSgpXG4gICAgICB9KVxuXG4gICAgICBmb3IgKGNvbnN0IHNxbCBvZiBzcWxzKSBhd2FpdCBkYi5xdWVyeShzcWwpXG4gICAgICBjaGFuZ2VkID0gdHJ1ZVxuICAgIH1cblxuICAgIGlmIChjaGFuZ2VkKSBkYi5jbGVhclNjaGVtYUNhY2hlKClcbiAgICByZXR1cm4gY2hhbmdlZFxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyB3aGV0aGVyIHRoZSBjdXJyZW50IGxvY2FsIHNjaGVtYSB2ZXJzaW9uIGlzIHJlY29yZGVkLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIENvbm5lY3Rpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgdmVyc2lvbiBvbmUgaXMgcmVjb3JkZWQuXG4gICAqL1xuICBhc3luYyBfaGFzTWlncmF0aW9uKGRiKSB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiXG4gICAgICAubmV3UXVlcnkoKVxuICAgICAgLmZyb20oTUlHUkFUSU9OU19UQUJMRSlcbiAgICAgIC53aGVyZSh7a2V5OiB0aGlzLl9taWdyYXRpb25LZXkoKX0pXG4gICAgICAubGltaXQoMSlcbiAgICAgIC5yZXN1bHRzKClcblxuICAgIHJldHVybiByb3dzLmxlbmd0aCA+IDBcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIHNjb3BlZCBtaWdyYXRpb24ga2V5LlxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSAtIFNjb3BlZCBtaWdyYXRpb24ga2V5LlxuICAgKi9cbiAgX21pZ3JhdGlvbktleSgpIHsgcmV0dXJuIGAke01JR1JBVElPTl9TQ09QRX06JHtNSUdSQVRJT05fVkVSU0lPTn1gIH1cblxuICAvKipcbiAgICogRW5xdWV1ZXMgYSBsb2NhbCBqb2IgaW4gdGhlIGNhbGxlcidzIGFjdGl2ZSB0cmFuc2FjdGlvbiB3aGVuIHByZXNlbnQuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gRW5xdWV1ZSByZXF1ZXN0LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5qb2JOYW1lIC0gUmVnaXN0ZXJlZCBqb2IgbmFtZS5cbiAgICogQHBhcmFtIHtBcnJheTxSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59IGFyZ3MuYXJncyAtIFNlcmlhbGl6ZWQgam9iIGFyZ3VtZW50cy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JPcHRpb25zfSBbYXJncy5vcHRpb25zXSAtIEpvYiBvcHRpb25zLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSAtIER1cmFibGUgam9iIGlkLlxuICAgKi9cbiAgYXN5bmMgZW5xdWV1ZSh7am9iTmFtZSwgYXJncywgb3B0aW9ucyA9IHt9fSkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuXG4gICAgY29uc3QgcHJlcGFyZWRKb2IgPSB0aGlzLl9wcmVwYXJlSm9iKHthcmdzLCBqb2JOYW1lLCBvcHRpb25zfSlcbiAgICBjb25zdCBtdXRhdGUgPSBhc3luYyAoaG9sZFVudGlsID0gKC8qKiBAdHlwZSB7UHJvbWlzZTx2b2lkPn0gKi8gX2NvbXBsZXRpb24pID0+IHt9KSA9PiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGNvbm5lY3Rpb24pID0+IHtcbiAgICAgIGlmIChjb25uZWN0aW9uLmluc2lkZVRyYW5zYWN0aW9uKCkpIGhvbGRVbnRpbChjb25uZWN0aW9uLnRyYW5zYWN0aW9uQ29tcGxldGlvbigpKVxuXG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5fbXV0YXRlKGNvbm5lY3Rpb24sIGFzeW5jIChkYikgPT4ge1xuICAgICAgICBsZXQgam9iSWQgPSBwcmVwYXJlZEpvYi5qb2JJZFxuXG4gICAgICAgIGlmIChwcmVwYXJlZEpvYi5jb25jdXJyZW5jeSkgYXdhaXQgdGhpcy5fZW5zdXJlQ29uY3VycmVuY3koZGIsIHByZXBhcmVkSm9iLmNvbmN1cnJlbmN5KVxuXG4gICAgICAgIGlmIChvcHRpb25zLmRlZHVwbGljYXRlV2hpbGVRdWV1ZWQpIHtcbiAgICAgICAgICBjb25zdCBleGlzdGluZyA9IGF3YWl0IGRiXG4gICAgICAgICAgICAubmV3UXVlcnkoKVxuICAgICAgICAgICAgLmZyb20oTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFKVxuICAgICAgICAgICAgLnNlbGVjdChcImlkXCIpXG4gICAgICAgICAgICAud2hlcmUoe1xuICAgICAgICAgICAgICBhcmdzX2RpZ2VzdDogcHJlcGFyZWRKb2IuYXJnc0RpZ2VzdCxcbiAgICAgICAgICAgICAgYXJnc19qc29uOiBwcmVwYXJlZEpvYi5hcmdzSnNvbixcbiAgICAgICAgICAgICAgam9iX25hbWU6IHByZXBhcmVkSm9iLmpvYk5hbWUsXG4gICAgICAgICAgICAgIHF1ZXVlOiBwcmVwYXJlZEpvYi5xdWV1ZSxcbiAgICAgICAgICAgICAgc3RhdHVzOiBcInF1ZXVlZFwiXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLndoZXJlKGBzY2hlZHVsZWRfYXRfbXMgPD0gJHtkYi5xdW90ZShwcmVwYXJlZEpvYi5zY2hlZHVsZWRBdE1zKX1gKVxuICAgICAgICAgICAgLm9yZGVyKFwic2NoZWR1bGVkX2F0X21zIEFTQ1wiKVxuICAgICAgICAgICAgLm9yZGVyKFwiY3JlYXRlZF9hdF9tcyBBU0NcIilcbiAgICAgICAgICAgIC5saW1pdCgxKVxuICAgICAgICAgICAgLnJlc3VsdHMoKVxuXG4gICAgICAgICAgY29uc3QgZXhpc3RpbmdSb3cgPSAvKiogQHR5cGUge3tpZDogc3RyaW5nIHwgbnVtYmVyfSB8IHVuZGVmaW5lZH0gKi8gKGV4aXN0aW5nWzBdKVxuXG4gICAgICAgICAgaWYgKGV4aXN0aW5nUm93KSBqb2JJZCA9IFN0cmluZyhleGlzdGluZ1Jvdy5pZClcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChqb2JJZCA9PT0gcHJlcGFyZWRKb2Iuam9iSWQpIGF3YWl0IHRoaXMuX2luc2VydFByZXBhcmVkSm9iKGRiLCBwcmVwYXJlZEpvYilcbiAgICAgICAgaWYgKHRoaXMub25Db21taXR0ZWRFbnF1ZXVlKSBhd2FpdCBkYi5hZnRlckNvbW1pdCh0aGlzLm9uQ29tbWl0dGVkRW5xdWV1ZSlcblxuICAgICAgICByZXR1cm4gam9iSWRcbiAgICAgIH0pXG4gICAgfSlcblxuICAgIGlmIChvcHRpb25zLmRlZHVwbGljYXRlV2hpbGVRdWV1ZWQpIHJldHVybiBhd2FpdCB0aGlzLl9zZXJpYWxpemVEZWR1cGxpY2F0ZWRFbnF1ZXVlKHByZXBhcmVkSm9iLCBtdXRhdGUpXG4gICAgcmV0dXJuIGF3YWl0IG11dGF0ZSgpXG4gIH1cblxuICAvKipcbiAgICogU2VyaWFsaXplcyBtYXRjaGluZyBpbi1wcm9jZXNzIGRlZHVwbGljYXRpb24gY2hlY2tzIHRocm91Z2ggY29tbWl0IHdoaWxlXG4gICAqIGxlYXZpbmcgdW5yZWxhdGVkIGpvYiBpZGVudGl0aWVzIGluZGVwZW5kZW50LlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuUHJlcGFyZWRMb2NhbEJhY2tncm91bmRKb2J9IHByZXBhcmVkSm9iIC0gUHJlcGFyZWQgam9iIGlkZW50aXR5LlxuICAgKiBAcGFyYW0geyhob2xkVW50aWw6IChjb21wbGV0aW9uOiBQcm9taXNlPHZvaWQ+KSA9PiB2b2lkKSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIERlZHVwbGljYXRpb24gbXV0YXRpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFQ+fSAtIE11dGF0aW9uIHJlc3VsdC5cbiAgICovXG4gIGFzeW5jIF9zZXJpYWxpemVEZWR1cGxpY2F0ZWRFbnF1ZXVlKHByZXBhcmVkSm9iLCBjYWxsYmFjaykge1xuICAgIGxldCBjaGFpbnMgPSBkZWR1cGxpY2F0ZWRFbnF1ZXVlQ2hhaW5zLmdldCh0aGlzLmNvbmZpZ3VyYXRpb24pXG5cbiAgICBpZiAoIWNoYWlucykge1xuICAgICAgY2hhaW5zID0gbmV3IE1hcCgpXG4gICAgICBkZWR1cGxpY2F0ZWRFbnF1ZXVlQ2hhaW5zLnNldCh0aGlzLmNvbmZpZ3VyYXRpb24sIGNoYWlucylcbiAgICB9XG5cbiAgICBjb25zdCBrZXkgPSBzaGEyNTZIZXgoSlNPTi5zdHJpbmdpZnkoW1xuICAgICAgdGhpcy5nZXREYXRhYmFzZUlkZW50aWZpZXIoKSxcbiAgICAgIHByZXBhcmVkSm9iLmpvYk5hbWUsXG4gICAgICBwcmVwYXJlZEpvYi5hcmdzRGlnZXN0LFxuICAgICAgcHJlcGFyZWRKb2IucXVldWVcbiAgICBdKSlcbiAgICBjb25zdCBwcmV2aW91cyA9IGNoYWlucy5nZXQoa2V5KSB8fCBQcm9taXNlLnJlc29sdmUoKVxuICAgIGxldCByZWxlYXNlID0gKCkgPT4ge31cbiAgICBjb25zdCBydW5uaW5nID0gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHsgcmVsZWFzZSA9ICgpID0+IHJlc29sdmUodW5kZWZpbmVkKSB9KVxuICAgIGNvbnN0IGNoYWluID0gcHJldmlvdXMudGhlbigoKSA9PiBydW5uaW5nKVxuICAgIC8qKiBAdHlwZSB7UHJvbWlzZTx2b2lkPiB8IHVuZGVmaW5lZH0gKi9cbiAgICBsZXQgY29tcGxldGlvblxuICAgIGNvbnN0IGZpbmlzaCA9ICgpID0+IHtcbiAgICAgIHJlbGVhc2UoKVxuICAgICAgaWYgKGNoYWlucy5nZXQoa2V5KSA9PT0gY2hhaW4pIGNoYWlucy5kZWxldGUoa2V5KVxuICAgICAgaWYgKGNoYWlucy5zaXplID09PSAwKSBkZWR1cGxpY2F0ZWRFbnF1ZXVlQ2hhaW5zLmRlbGV0ZSh0aGlzLmNvbmZpZ3VyYXRpb24pXG4gICAgfVxuXG4gICAgY2hhaW5zLnNldChrZXksIGNoYWluKVxuICAgIGF3YWl0IHByZXZpb3VzXG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY2FsbGJhY2soKHRyYW5zYWN0aW9uQ29tcGxldGlvbikgPT4geyBjb21wbGV0aW9uID0gdHJhbnNhY3Rpb25Db21wbGV0aW9uIH0pXG5cbiAgICAgIGlmIChjb21wbGV0aW9uKSB7XG4gICAgICAgIGNvbXBsZXRpb24udGhlbihmaW5pc2gsIGZpbmlzaClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGZpbmlzaCgpXG4gICAgICB9XG5cbiAgICAgIHJldHVybiByZXN1bHRcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgZmluaXNoKClcbiAgICAgIHRocm93IGVycm9yXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFByZXBhcmVzIHZhbGlkYXRlZCBsb2NhbCBqb2IgZGF0YSBmb3IgaW5zZXJ0aW9uLlxuICAgKiBAcGFyYW0ge3thcmdzOiBBcnJheTxSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj4sIGpvYk5hbWU6IHN0cmluZywgb3B0aW9uczogaW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iT3B0aW9uc319IGFyZ3MgLSBKb2IgcmVxdWVzdC5cbiAgICogQHJldHVybnMge2ltcG9ydChcIi4vdHlwZXMuanNcIikuUHJlcGFyZWRMb2NhbEJhY2tncm91bmRKb2J9IC0gUHJlcGFyZWQgcm93IGRhdGEuXG4gICAqL1xuICBfcHJlcGFyZUpvYih7YXJncywgam9iTmFtZSwgb3B0aW9uc30pIHtcbiAgICBpZiAob3B0aW9ucy5pZGVtcG90ZW5jeUtleSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJpZGVtcG90ZW5jeUtleSBpcyBub3Qgc3VwcG9ydGVkIGJ5IHRoZSBsb2NhbCBiYWNrZ3JvdW5kLWpvYnMgYWRhcHRlclwiKVxuICAgIH1cblxuICAgIGNvbnN0IGNyZWF0ZWRBdE1zID0gdGhpcy5jbG9jay5ub3coKVxuICAgIGNvbnN0IHF1ZXVlID0gbm9ybWFsaXplQmFja2dyb3VuZEpvYlF1ZXVlKG9wdGlvbnMpXG4gICAgY29uc3QgcXVldWVzID0gdGhpcy5jb25maWd1cmF0aW9uLmdldEJhY2tncm91bmRKb2JzQ29uZmlnKCkucXVldWVzXG4gICAgY29uc3QgYXJnc0pzb24gPSBKU09OLnN0cmluZ2lmeShhcmdzIHx8IFtdKVxuICAgIGNvbnN0IGV4ZWN1dGlvbk1vZGUgPSBub3JtYWxpemVCYWNrZ3JvdW5kSm9iRXhlY3V0aW9uTW9kZShvcHRpb25zLCBcImlubGluZVwiLCBMT0NBTF9FWEVDVVRJT05fTU9ERVMpXG5cbiAgICBpZiAodHlwZW9mIGFyZ3NKc29uICE9PSBcInN0cmluZ1wiKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiTG9jYWwgYmFja2dyb3VuZCBqb2IgYXJndW1lbnRzIG11c3QgYmUgSlNPTiBzZXJpYWxpemFibGVcIilcbiAgICBpZiAoZXhlY3V0aW9uTW9kZSAhPT0gXCJpbmxpbmVcIikgdGhyb3cgbmV3IEVycm9yKFwiTG9jYWwgYmFja2dyb3VuZCBqb2IgZXhlY3V0aW9uIG1vZGUgaW52YXJpYW50IHdhcyB2aW9sYXRlZFwiKVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGFyZ3NEaWdlc3Q6IHNoYTI1NkhleChhcmdzSnNvbiksXG4gICAgICBhcmdzSnNvbixcbiAgICAgIGNvbmN1cnJlbmN5OiBub3JtYWxpemVCYWNrZ3JvdW5kSm9iQ29uY3VycmVuY3koe29wdGlvbnMsIHF1ZXVlLCBxdWV1ZXN9KSxcbiAgICAgIGNyZWF0ZWRBdE1zLFxuICAgICAgZXhlY3V0aW9uTW9kZSxcbiAgICAgIGpvYklkOiBuZXcgVVVJRCg0KS5mb3JtYXQoKSxcbiAgICAgIGpvYk5hbWUsXG4gICAgICBtYXhSZXRyaWVzOiBub3JtYWxpemVCYWNrZ3JvdW5kSm9iTWF4UmV0cmllcyhvcHRpb25zLm1heFJldHJpZXMpLFxuICAgICAgcXVldWUsXG4gICAgICBzY2hlZHVsZWRBdE1zOiBub3JtYWxpemVCYWNrZ3JvdW5kSm9iU2NoZWR1bGVkQXRNcyhvcHRpb25zLnNjaGVkdWxlZEF0TXMsIGNyZWF0ZWRBdE1zKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbnNlcnRzIG9uZSBwcmVwYXJlZCBsb2NhbCBqb2Igcm93IGFuZCBpdHMgY29uY3VycmVuY3kgbWV0YWRhdGEuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gTG9jYWwgU1FMaXRlIGNvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5QcmVwYXJlZExvY2FsQmFja2dyb3VuZEpvYn0gcHJlcGFyZWRKb2IgLSBQcmVwYXJlZCByb3cgZGF0YS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgaW5zZXJ0aW9uLlxuICAgKi9cbiAgYXN5bmMgX2luc2VydFByZXBhcmVkSm9iKGRiLCBwcmVwYXJlZEpvYikge1xuICAgIGF3YWl0IGRiLmluc2VydCh7XG4gICAgICB0YWJsZU5hbWU6IExPQ0FMX0JBQ0tHUk9VTkRfSk9CU19UQUJMRSxcbiAgICAgIGRhdGE6IHtcbiAgICAgICAgYXJnc19kaWdlc3Q6IHByZXBhcmVkSm9iLmFyZ3NEaWdlc3QsXG4gICAgICAgIGFyZ3NfanNvbjogcHJlcGFyZWRKb2IuYXJnc0pzb24sXG4gICAgICAgIGF0dGVtcHRzOiAwLFxuICAgICAgICBjb21wbGV0ZWRfYXRfbXM6IG51bGwsXG4gICAgICAgIGNvbmN1cnJlbmN5X2tleTogcHJlcGFyZWRKb2IuY29uY3VycmVuY3k/LmNvbmN1cnJlbmN5S2V5IHx8IG51bGwsXG4gICAgICAgIGNyZWF0ZWRfYXRfbXM6IHByZXBhcmVkSm9iLmNyZWF0ZWRBdE1zLFxuICAgICAgICBleGVjdXRpb25fbW9kZTogcHJlcGFyZWRKb2IuZXhlY3V0aW9uTW9kZSxcbiAgICAgICAgZmFpbGVkX2F0X21zOiBudWxsLFxuICAgICAgICBoYW5kZWRfb2ZmX2F0X21zOiBudWxsLFxuICAgICAgICBoYW5kb2ZmX2lkOiBudWxsLFxuICAgICAgICBpZDogcHJlcGFyZWRKb2Iuam9iSWQsXG4gICAgICAgIGpvYl9uYW1lOiBwcmVwYXJlZEpvYi5qb2JOYW1lLFxuICAgICAgICBsYXN0X2Vycm9yOiBudWxsLFxuICAgICAgICBtYXhfY29uY3VycmVuY3k6IHByZXBhcmVkSm9iLmNvbmN1cnJlbmN5Py5tYXhDb25jdXJyZW5jeSB8fCBudWxsLFxuICAgICAgICBtYXhfcmV0cmllczogcHJlcGFyZWRKb2IubWF4UmV0cmllcyxcbiAgICAgICAgcXVldWU6IHByZXBhcmVkSm9iLnF1ZXVlLFxuICAgICAgICBzY2hlZHVsZWRfYXRfbXM6IHByZXBhcmVkSm9iLnNjaGVkdWxlZEF0TXMsXG4gICAgICAgIHN0YXR1czogXCJxdWV1ZWRcIixcbiAgICAgICAgd29ya2VyX2lkOiBudWxsXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvbmNpbGVzIGNvbmZpZ3VyZWQgcXVldWUtZGVyaXZlZCBjYXBzIGFuZCBkdXJhYmxlIGNvdW50ZXJzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciByZWNvbmNpbGlhdGlvbi5cbiAgICovXG4gIGFzeW5jIHJlY29uY2lsZVF1ZXVlQ29uY3VycmVuY3koKSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGNvbm5lY3Rpb24pID0+IGF3YWl0IHRoaXMuX211dGF0ZShjb25uZWN0aW9uLCBhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IHF1ZXVlcyA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXRCYWNrZ3JvdW5kSm9ic0NvbmZpZygpLnF1ZXVlc1xuICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiLm5ld1F1ZXJ5KCkuZnJvbShMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEUpLndoZXJlKHtzdGF0dXM6IFwicXVldWVkXCJ9KS5yZXN1bHRzKClcblxuICAgICAgZm9yIChjb25zdCByYXdSb3cgb2Ygcm93cykge1xuICAgICAgICBhd2FpdCB0aGlzLl9yZWNvbmNpbGVRdWV1ZWRKb2JDb25jdXJyZW5jeShkYiwgdGhpcy5fbm9ybWFsaXplUm93KHJhd1JvdyksIHF1ZXVlcylcbiAgICAgIH1cblxuICAgICAgYXdhaXQgdGhpcy5fcmVidWlsZENvbmN1cnJlbmN5Q291bnRzKGRiKVxuICAgIH0pKVxuICB9XG5cbiAgLyoqXG4gICAqIEFwcGxpZXMgY3VycmVudCBxdWV1ZS1kZXJpdmVkIGNvbmN1cnJlbmN5IHBvbGljeSB0byBvbmUgcXVldWVkIHJvdy5cbiAgICogRXhwbGljaXQgY29uY3VycmVuY3kga2V5cyByZW1haW4gb3duZWQgYnkgdGhlIGVucXVldWUgY29udHJhY3QuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gTG9jYWwgU1FMaXRlIGNvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUm93fSBqb2IgLSBRdWV1ZWQgam9iIHNuYXBzaG90LlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHttYXhDb25jdXJyZW50PzogbnVtYmVyLCBwcmlvcml0eT86IG51bWJlcn0+fSBxdWV1ZXMgLSBDdXJyZW50IHF1ZXVlIHBvbGljeSBzbmFwc2hvdC5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUm93Pn0gLSBSZWNvbmNpbGVkIHNuYXBzaG90LlxuICAgKi9cbiAgYXN5bmMgX3JlY29uY2lsZVF1ZXVlZEpvYkNvbmN1cnJlbmN5KGRiLCBqb2IsIHF1ZXVlcykge1xuICAgIGNvbnN0IGN1cnJlbnRJc1F1ZXVlRGVyaXZlZCA9IEJvb2xlYW4oam9iLmNvbmN1cnJlbmN5S2V5Py5zdGFydHNXaXRoKFFVRVVFX0NPTkNVUlJFTkNZX0tFWV9QUkVGSVgpKVxuXG4gICAgaWYgKGpvYi5jb25jdXJyZW5jeUtleSAmJiAhY3VycmVudElzUXVldWVEZXJpdmVkKSByZXR1cm4gam9iXG5cbiAgICBjb25zdCBjb25jdXJyZW5jeSA9IG5vcm1hbGl6ZUJhY2tncm91bmRKb2JDb25jdXJyZW5jeSh7XG4gICAgICBvcHRpb25zOiB7fSxcbiAgICAgIHF1ZXVlOiBqb2IucXVldWUsXG4gICAgICBxdWV1ZXNcbiAgICB9KVxuXG4gICAgaWYgKCFjb25jdXJyZW5jeSkge1xuICAgICAgaWYgKGN1cnJlbnRJc1F1ZXVlRGVyaXZlZCkge1xuICAgICAgICBhd2FpdCBkYi51cGRhdGUoe1xuICAgICAgICAgIGNvbmRpdGlvbnM6IHtpZDogam9iLmlkLCBzdGF0dXM6IFwicXVldWVkXCJ9LFxuICAgICAgICAgIGRhdGE6IHtjb25jdXJyZW5jeV9rZXk6IG51bGwsIG1heF9jb25jdXJyZW5jeTogbnVsbH0sXG4gICAgICAgICAgdGFibGVOYW1lOiBMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEVcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHsuLi5qb2IsIGNvbmN1cnJlbmN5S2V5OiBudWxsLCBtYXhDb25jdXJyZW5jeTogbnVsbH1cbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLl9lbnN1cmVDb25jdXJyZW5jeShkYiwgY29uY3VycmVuY3kpXG4gICAgaWYgKGpvYi5jb25jdXJyZW5jeUtleSAhPT0gY29uY3VycmVuY3kuY29uY3VycmVuY3lLZXkgfHwgam9iLm1heENvbmN1cnJlbmN5ICE9PSBjb25jdXJyZW5jeS5tYXhDb25jdXJyZW5jeSkge1xuICAgICAgYXdhaXQgZGIudXBkYXRlKHtcbiAgICAgICAgY29uZGl0aW9uczoge2lkOiBqb2IuaWQsIHN0YXR1czogXCJxdWV1ZWRcIn0sXG4gICAgICAgIGRhdGE6IHtjb25jdXJyZW5jeV9rZXk6IGNvbmN1cnJlbmN5LmNvbmN1cnJlbmN5S2V5LCBtYXhfY29uY3VycmVuY3k6IGNvbmN1cnJlbmN5Lm1heENvbmN1cnJlbmN5fSxcbiAgICAgICAgdGFibGVOYW1lOiBMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEVcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgcmV0dXJuIHsuLi5qb2IsIGNvbmN1cnJlbmN5S2V5OiBjb25jdXJyZW5jeS5jb25jdXJyZW5jeUtleSwgbWF4Q29uY3VycmVuY3k6IGNvbmN1cnJlbmN5Lm1heENvbmN1cnJlbmN5fVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIHRoZSBuZXh0IGVsaWdpYmxlIGxvY2FsIGpvYi5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUm93IHwgbnVsbD59IC0gTmV4dCBlbGlnaWJsZSBsb2NhbCBqb2IuXG4gICAqL1xuICBhc3luYyBuZXh0QXZhaWxhYmxlSm9iKCkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuXG4gICAgcmV0dXJuIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IGpvYnNUYWJsZSA9IGRiLnF1b3RlVGFibGUoTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFKVxuICAgICAgY29uc3QgY29uY3VycmVuY3lUYWJsZSA9IGRiLnF1b3RlVGFibGUoTE9DQUxfQkFDS0dST1VORF9KT0JfQ09OQ1VSUkVOQ1lfVEFCTEUpXG4gICAgICBjb25zdCBwcmlvcml0eU9yZGVyID0gdGhpcy5fcXVldWVQcmlvcml0eU9yZGVyU3FsKGRiKVxuICAgICAgbGV0IHF1ZXJ5ID0gZGJcbiAgICAgICAgLm5ld1F1ZXJ5KClcbiAgICAgICAgLmZyb20oTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFKVxuICAgICAgICAud2hlcmUoe3N0YXR1czogXCJxdWV1ZWRcIn0pXG4gICAgICAgIC53aGVyZShgc2NoZWR1bGVkX2F0X21zIDw9ICR7ZGIucXVvdGUodGhpcy5jbG9jay5ub3coKSl9YClcbiAgICAgICAgLndoZXJlKFxuICAgICAgICAgIGAoJHtqb2JzVGFibGV9LiR7ZGIucXVvdGVDb2x1bW4oXCJjb25jdXJyZW5jeV9rZXlcIil9IElTIE5VTEwgT1IgRVhJU1RTIChgICtcbiAgICAgICAgICBgU0VMRUNUIDEgRlJPTSAke2NvbmN1cnJlbmN5VGFibGV9IFdIRVJFIGAgK1xuICAgICAgICAgIGAke2NvbmN1cnJlbmN5VGFibGV9LiR7ZGIucXVvdGVDb2x1bW4oXCJjb25jdXJyZW5jeV9rZXlcIil9ID0gJHtqb2JzVGFibGV9LiR7ZGIucXVvdGVDb2x1bW4oXCJjb25jdXJyZW5jeV9rZXlcIil9IEFORCBgICtcbiAgICAgICAgICBgJHtjb25jdXJyZW5jeVRhYmxlfS4ke2RiLnF1b3RlQ29sdW1uKFwiYWN0aXZlX2NvdW50XCIpfSA8ICR7Y29uY3VycmVuY3lUYWJsZX0uJHtkYi5xdW90ZUNvbHVtbihcIm1heF9jb25jdXJyZW5jeVwiKX0pKWBcbiAgICAgICAgKVxuXG4gICAgICBpZiAocHJpb3JpdHlPcmRlcikgcXVlcnkgPSBxdWVyeS5vcmRlcihgJHtwcmlvcml0eU9yZGVyfSBERVNDYClcblxuICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IHF1ZXJ5XG4gICAgICAgIC5vcmRlcihcInNjaGVkdWxlZF9hdF9tcyBBU0NcIilcbiAgICAgICAgLm9yZGVyKFwiY3JlYXRlZF9hdF9tcyBBU0NcIilcbiAgICAgICAgLm9yZGVyKFwiaWQgQVNDXCIpXG4gICAgICAgIC5saW1pdCgxKVxuICAgICAgICAucmVzdWx0cygpXG5cbiAgICAgIHJldHVybiByb3dzWzBdID8gdGhpcy5fbm9ybWFsaXplUm93KHJvd3NbMF0pIDogbnVsbFxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogRmluZHMgdGhlIHNvb25lc3QgZnV0dXJlIHF1ZXVlZCBqb2IuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvdyB8IG51bGw+fSAtIFNvb25lc3QgZnV0dXJlIHF1ZXVlZCBqb2IuXG4gICAqL1xuICBhc3luYyBuZXh0U2NoZWR1bGVkSm9iKCkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuXG4gICAgcmV0dXJuIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IHJvd3MgPSBhd2FpdCBkYlxuICAgICAgICAubmV3UXVlcnkoKVxuICAgICAgICAuZnJvbShMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEUpXG4gICAgICAgIC53aGVyZSh7c3RhdHVzOiBcInF1ZXVlZFwifSlcbiAgICAgICAgLndoZXJlKGBzY2hlZHVsZWRfYXRfbXMgPiAke2RiLnF1b3RlKHRoaXMuY2xvY2subm93KCkpfWApXG4gICAgICAgIC5vcmRlcihcInNjaGVkdWxlZF9hdF9tcyBBU0NcIilcbiAgICAgICAgLm9yZGVyKFwiY3JlYXRlZF9hdF9tcyBBU0NcIilcbiAgICAgICAgLm9yZGVyKFwiaWQgQVNDXCIpXG4gICAgICAgIC5saW1pdCgxKVxuICAgICAgICAucmVzdWx0cygpXG5cbiAgICAgIHJldHVybiByb3dzWzBdID8gdGhpcy5fbm9ybWFsaXplUm93KHJvd3NbMF0pIDogbnVsbFxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogRmluZHMgYSBwZXJzaXN0ZWQgbG9jYWwgam9iIGJ5IGlkLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gam9iSWQgLSBKb2IgaWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvdyB8IG51bGw+fSAtIFBlcnNpc3RlZCBqb2IuXG4gICAqL1xuICBhc3luYyBnZXRKb2Ioam9iSWQpIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiBhd2FpdCB0aGlzLl9nZXRKb2IoZGIsIGpvYklkKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXN0cyBsb2NhbCBqb2JzIGluIGNyZWF0aW9uIG9yZGVyLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JSb3dbXT59IC0gQWxsIGxvY2FsIGpvYnMgaW4gY3JlYXRpb24gb3JkZXIuXG4gICAqL1xuICBhc3luYyBsaXN0Sm9icygpIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiB7XG4gICAgICBjb25zdCByb3dzID0gYXdhaXQgZGJcbiAgICAgICAgLm5ld1F1ZXJ5KClcbiAgICAgICAgLmZyb20oTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFKVxuICAgICAgICAub3JkZXIoXCJjcmVhdGVkX2F0X21zIEFTQ1wiKVxuICAgICAgICAub3JkZXIoXCJpZCBBU0NcIilcbiAgICAgICAgLnJlc3VsdHMoKVxuXG4gICAgICByZXR1cm4gcm93cy5tYXAoKHJvdykgPT4gdGhpcy5fbm9ybWFsaXplUm93KHJvdykpXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBdG9taWNhbGx5IHJlc2VydmVzIGNvbmN1cnJlbmN5IGFuZCBjbGFpbXMgb25lIHF1ZXVlZCBqb2IuXG4gICAqIEBwYXJhbSB7e2pvYklkOiBzdHJpbmcsIHdvcmtlcklkPzogc3RyaW5nfX0gYXJncyAtIENsYWltIHJlcXVlc3QuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYkhhbmRvZmYgfCBudWxsPn0gLSBGZW5jZWQgY2xhaW0uXG4gICAqL1xuICBhc3luYyBtYXJrSGFuZGVkT2ZmKHtqb2JJZCwgd29ya2VySWR9KSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChjb25uZWN0aW9uKSA9PiBhd2FpdCB0aGlzLl9tdXRhdGUoY29ubmVjdGlvbiwgYXN5bmMgKGRiKSA9PiB7XG4gICAgICBjb25zdCBqb2IgPSBhd2FpdCB0aGlzLl9nZXRKb2IoZGIsIGpvYklkKVxuXG4gICAgICBpZiAoIWpvYiB8fCBqb2Iuc3RhdHVzICE9PSBcInF1ZXVlZFwiIHx8IE51bWJlcihqb2Iuc2NoZWR1bGVkQXRNcykgPiB0aGlzLmNsb2NrLm5vdygpKSByZXR1cm4gbnVsbFxuICAgICAgaWYgKGpvYi5jb25jdXJyZW5jeUtleSAmJiAhKGF3YWl0IHRoaXMuX3Jlc2VydmVDb25jdXJyZW5jeShkYiwgam9iLmNvbmN1cnJlbmN5S2V5KSkpIHJldHVybiBudWxsXG5cbiAgICAgIGNvbnN0IGhhbmRlZE9mZkF0TXMgPSB0aGlzLmNsb2NrLm5vdygpXG4gICAgICBjb25zdCBoYW5kb2ZmSWQgPSBuZXcgVVVJRCg0KS5mb3JtYXQoKVxuICAgICAgY29uc3QgYWZmZWN0ZWRSb3dzID0gYXdhaXQgdGhpcy5fdXBkYXRlQWZmZWN0ZWRSb3dzKGRiLCB7XG4gICAgICAgIGNvbmRpdGlvbnM6IHtpZDogam9iSWQsIHN0YXR1czogXCJxdWV1ZWRcIn0sXG4gICAgICAgIGRhdGE6IHtoYW5kZWRfb2ZmX2F0X21zOiBoYW5kZWRPZmZBdE1zLCBoYW5kb2ZmX2lkOiBoYW5kb2ZmSWQsIHN0YXR1czogXCJoYW5kZWRfb2ZmXCIsIHdvcmtlcl9pZDogd29ya2VySWQgfHwgXCJsb2NhbFwifSxcbiAgICAgICAgdGFibGVOYW1lOiBMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEVcbiAgICAgIH0pXG5cbiAgICAgIGlmIChhZmZlY3RlZFJvd3MgIT09IDEpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fcmVsZWFzZUNvbmN1cnJlbmN5KGRiLCBqb2IuY29uY3VycmVuY3lLZXkpXG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7aGFuZGVkT2ZmQXRNcywgaGFuZG9mZklkfVxuICAgIH0pKVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIGFjdGl2ZSBsb2NhbCBoYW5kb2ZmcyBvd25lZCBieSBvbmUgd29ya2VyLlxuICAgKiBAcGFyYW0ge3t3b3JrZXJJZDogc3RyaW5nfX0gYXJncyAtIFdvcmtlciBpZGVudGl0eS5cbiAgICogQHJldHVybnMge1Byb21pc2U8QXJyYXk8e2pvYklkOiBzdHJpbmcsIGhhbmRvZmZJZDogc3RyaW5nfT4+fSAtIEFjdGl2ZSB3b3JrZXIgaGFuZG9mZnMuXG4gICAqL1xuICBhc3luYyBoYW5kZWRPZmZKb2JzRm9yV29ya2VyKHt3b3JrZXJJZH0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiBhd2FpdCBkYlxuICAgICAgLm5ld1F1ZXJ5KClcbiAgICAgIC5mcm9tKExPQ0FMX0JBQ0tHUk9VTkRfSk9CU19UQUJMRSlcbiAgICAgIC53aGVyZSh7c3RhdHVzOiBcImhhbmRlZF9vZmZcIiwgd29ya2VyX2lkOiB3b3JrZXJJZH0pXG4gICAgICAucmVzdWx0cygpKVxuICAgIC8qKiBAdHlwZSB7QXJyYXk8e2pvYklkOiBzdHJpbmcsIGhhbmRvZmZJZDogc3RyaW5nfT59ICovXG4gICAgY29uc3QgaGFuZG9mZnMgPSBbXVxuXG4gICAgZm9yIChjb25zdCByYXdSb3cgb2Ygcm93cykge1xuICAgICAgY29uc3Qgam9iID0gdGhpcy5fbm9ybWFsaXplUm93KHJhd1JvdylcblxuICAgICAgaWYgKGpvYi5oYW5kb2ZmSWQpIGhhbmRvZmZzLnB1c2goe2pvYklkOiBqb2IuaWQsIGhhbmRvZmZJZDogam9iLmhhbmRvZmZJZH0pXG4gICAgfVxuXG4gICAgcmV0dXJuIGhhbmRvZmZzXG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhbiBleGFjdCBhY3RpdmUgaGFuZG9mZiB0byB0aGUgcXVldWUuXG4gICAqIEBwYXJhbSB7e2pvYklkOiBzdHJpbmcsIGhhbmRvZmZJZDogc3RyaW5nfX0gYXJncyAtIEhhbmRvZmYgcmVsZWFzZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgdGhlIGZlbmNlZCByZWxlYXNlLlxuICAgKi9cbiAgYXN5bmMgbWFya1JldHVybmVkVG9RdWV1ZSh7am9iSWQsIGhhbmRvZmZJZH0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoY29ubmVjdGlvbikgPT4gYXdhaXQgdGhpcy5fbXV0YXRlKGNvbm5lY3Rpb24sIGFzeW5jIChkYikgPT4ge1xuICAgICAgY29uc3Qgam9iID0gYXdhaXQgdGhpcy5fZ2V0Sm9iKGRiLCBqb2JJZClcblxuICAgICAgaWYgKCF0aGlzLl9hY2NlcHRzSGFuZG9mZihqb2IsIGhhbmRvZmZJZCkpIHJldHVyblxuICAgICAgYXdhaXQgdGhpcy5fbG9ja0NvbmN1cnJlbmN5Um93KGRiLCBqb2IuY29uY3VycmVuY3lLZXkpXG5cbiAgICAgIGNvbnN0IGFmZmVjdGVkUm93cyA9IGF3YWl0IHRoaXMuX3VwZGF0ZUFmZmVjdGVkUm93cyhkYiwge1xuICAgICAgICBjb25kaXRpb25zOiB7aGFuZG9mZl9pZDogaGFuZG9mZklkLCBpZDogam9iSWQsIHN0YXR1czogXCJoYW5kZWRfb2ZmXCJ9LFxuICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgaGFuZGVkX29mZl9hdF9tczogbnVsbCxcbiAgICAgICAgICBoYW5kb2ZmX2lkOiBudWxsLFxuICAgICAgICAgIHNjaGVkdWxlZF9hdF9tczogdGhpcy5jbG9jay5ub3coKSxcbiAgICAgICAgICBzdGF0dXM6IFwicXVldWVkXCIsXG4gICAgICAgICAgd29ya2VyX2lkOiBudWxsXG4gICAgICAgIH0sXG4gICAgICAgIHRhYmxlTmFtZTogTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFXG4gICAgICB9KVxuXG4gICAgICBpZiAoYWZmZWN0ZWRSb3dzID09PSAxKSBhd2FpdCB0aGlzLl9yZWxlYXNlQ29uY3VycmVuY3koZGIsIGpvYi5jb25jdXJyZW5jeUtleSlcbiAgICB9KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBsaWVzIGEgZmVuY2VkIHN1Y2Nlc3NmdWwgYWNrbm93bGVkZ2VtZW50LlxuICAgKiBAcGFyYW0ge3tqb2JJZDogc3RyaW5nLCBoYW5kb2ZmSWQ/OiBzdHJpbmd9fSBhcmdzIC0gQ29tcGxldGlvbiByZXBvcnQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgdGhlIGxlYXNlIHdvbi5cbiAgICovXG4gIGFzeW5jIG1hcmtDb21wbGV0ZWQoe2pvYklkLCBoYW5kb2ZmSWR9KSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChjb25uZWN0aW9uKSA9PiBhd2FpdCB0aGlzLl9tdXRhdGUoY29ubmVjdGlvbiwgYXN5bmMgKGRiKSA9PiB7XG4gICAgICBjb25zdCBqb2IgPSBhd2FpdCB0aGlzLl9nZXRKb2IoZGIsIGpvYklkKVxuXG4gICAgICBpZiAoIXRoaXMuX2FjY2VwdHNIYW5kb2ZmKGpvYiwgaGFuZG9mZklkKSkgcmV0dXJuIGZhbHNlXG4gICAgICBhd2FpdCB0aGlzLl9sb2NrQ29uY3VycmVuY3lSb3coZGIsIGpvYi5jb25jdXJyZW5jeUtleSlcblxuICAgICAgY29uc3QgYWZmZWN0ZWRSb3dzID0gYXdhaXQgdGhpcy5fdXBkYXRlQWZmZWN0ZWRSb3dzKGRiLCB7XG4gICAgICAgIGNvbmRpdGlvbnM6IHtoYW5kb2ZmX2lkOiBoYW5kb2ZmSWQsIGlkOiBqb2JJZCwgc3RhdHVzOiBcImhhbmRlZF9vZmZcIn0sXG4gICAgICAgIGRhdGE6IHtjb21wbGV0ZWRfYXRfbXM6IHRoaXMuY2xvY2subm93KCksIHN0YXR1czogXCJjb21wbGV0ZWRcIn0sXG4gICAgICAgIHRhYmxlTmFtZTogTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFXG4gICAgICB9KVxuXG4gICAgICBpZiAoYWZmZWN0ZWRSb3dzICE9PSAxKSByZXR1cm4gZmFsc2VcbiAgICAgIGF3YWl0IHRoaXMuX3JlbGVhc2VDb25jdXJyZW5jeShkYiwgam9iLmNvbmN1cnJlbmN5S2V5KVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBsaWVzIGEgZmVuY2VkIHJlc2NoZWR1bGUgd2l0aG91dCBjb25zdW1pbmcgYW4gYXR0ZW1wdC5cbiAgICogQHBhcmFtIHt7am9iSWQ6IHN0cmluZywgaGFuZG9mZklkPzogc3RyaW5nLCBkZWxheU1zOiBudW1iZXJ9fSBhcmdzIC0gUmVzY2hlZHVsZSByZXBvcnQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgdGhlIGxlYXNlIHdvbi5cbiAgICovXG4gIGFzeW5jIG1hcmtSZXNjaGVkdWxlZCh7am9iSWQsIGhhbmRvZmZJZCwgZGVsYXlNc30pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGNvbm5lY3Rpb24pID0+IGF3YWl0IHRoaXMuX211dGF0ZShjb25uZWN0aW9uLCBhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IGpvYiA9IGF3YWl0IHRoaXMuX2dldEpvYihkYiwgam9iSWQpXG5cbiAgICAgIGlmICghdGhpcy5fYWNjZXB0c0hhbmRvZmYoam9iLCBoYW5kb2ZmSWQpKSByZXR1cm4gZmFsc2VcbiAgICAgIGF3YWl0IHRoaXMuX2xvY2tDb25jdXJyZW5jeVJvdyhkYiwgam9iLmNvbmN1cnJlbmN5S2V5KVxuXG4gICAgICBjb25zdCBhZmZlY3RlZFJvd3MgPSBhd2FpdCB0aGlzLl91cGRhdGVBZmZlY3RlZFJvd3MoZGIsIHtcbiAgICAgICAgY29uZGl0aW9uczoge2hhbmRvZmZfaWQ6IGhhbmRvZmZJZCwgaWQ6IGpvYklkLCBzdGF0dXM6IFwiaGFuZGVkX29mZlwifSxcbiAgICAgICAgZGF0YToge1xuICAgICAgICAgIGhhbmRlZF9vZmZfYXRfbXM6IG51bGwsXG4gICAgICAgICAgaGFuZG9mZl9pZDogbnVsbCxcbiAgICAgICAgICBzY2hlZHVsZWRfYXRfbXM6IHJlc2NoZWR1bGVkQmFja2dyb3VuZEpvYkF0TXMoZGVsYXlNcywgdGhpcy5jbG9jay5ub3coKSksXG4gICAgICAgICAgc3RhdHVzOiBcInF1ZXVlZFwiLFxuICAgICAgICAgIHdvcmtlcl9pZDogbnVsbFxuICAgICAgICB9LFxuICAgICAgICB0YWJsZU5hbWU6IExPQ0FMX0JBQ0tHUk9VTkRfSk9CU19UQUJMRVxuICAgICAgfSlcblxuICAgICAgaWYgKGFmZmVjdGVkUm93cyAhPT0gMSkgcmV0dXJuIGZhbHNlXG4gICAgICBhd2FpdCB0aGlzLl9yZWxlYXNlQ29uY3VycmVuY3koZGIsIGpvYi5jb25jdXJyZW5jeUtleSlcbiAgICAgIHJldHVybiB0cnVlXG4gICAgfSkpXG4gIH1cblxuICAvKipcbiAgICogQXBwbGllcyBhIGZlbmNlZCBmYWlsdXJlLCByZXRyeSwgb3IgdGVybWluYWwgdHJhbnNpdGlvbi5cbiAgICogQHBhcmFtIHt7am9iSWQ6IHN0cmluZywgaGFuZG9mZklkPzogc3RyaW5nLCBlcnJvcjogUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT59fSBhcmdzIC0gRmFpbHVyZSByZXBvcnQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvdyB8IG51bGw+fSAtIEFjY2VwdGVkIHRyYW5zaXRpb24gc25hcHNob3QuXG4gICAqL1xuICBhc3luYyBtYXJrRmFpbGVkKHtqb2JJZCwgaGFuZG9mZklkLCBlcnJvcn0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGNvbm5lY3Rpb24pID0+IGF3YWl0IHRoaXMuX211dGF0ZShjb25uZWN0aW9uLCBhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IGpvYiA9IGF3YWl0IHRoaXMuX2dldEpvYihkYiwgam9iSWQpXG5cbiAgICAgIGlmICghdGhpcy5fYWNjZXB0c0hhbmRvZmYoam9iLCBoYW5kb2ZmSWQpKSByZXR1cm4gbnVsbFxuXG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5fYXBwbHlGYWlsdXJlKGRiLCBqb2IsIGVycm9yKVxuICAgIH0pKVxuICB9XG5cbiAgLyoqXG4gICAqIFR1cm5zIGV2ZXJ5IGFiYW5kb25lZCBsb2NhbCBoYW5kb2ZmIGludG8gdGhlIG5vcm1hbCBmYWlsdXJlL3JldHJ5IHBhdGguXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvd1tdPn0gLSBSZWNvdmVyZWQgdHJhbnNpdGlvbnMuXG4gICAqL1xuICBhc3luYyByZWNvdmVySGFuZGVkT2ZmSm9icygpIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGNvbm5lY3Rpb24pID0+IGF3YWl0IHRoaXMuX211dGF0ZShjb25uZWN0aW9uLCBhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IHF1ZXVlcyA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXRCYWNrZ3JvdW5kSm9ic0NvbmZpZygpLnF1ZXVlc1xuICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiLm5ld1F1ZXJ5KCkuZnJvbShMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEUpLndoZXJlKHtzdGF0dXM6IFwiaGFuZGVkX29mZlwifSkucmVzdWx0cygpXG4gICAgICAvKiogQHR5cGUge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvd1tdfSAqL1xuICAgICAgY29uc3QgcmVjb3ZlcmVkID0gW11cblxuICAgICAgZm9yIChjb25zdCByYXdSb3cgb2Ygcm93cykge1xuICAgICAgICBjb25zdCBqb2IgPSB0aGlzLl9ub3JtYWxpemVSb3cocmF3Um93KVxuICAgICAgICBjb25zdCB1cGRhdGVkID0gYXdhaXQgdGhpcy5fYXBwbHlGYWlsdXJlKGRiLCBqb2IsIG5ldyBFcnJvcihcIkxvY2FsIGJhY2tncm91bmQgam9iIHJlY292ZXJlZCBhZnRlciBhbiBpbnRlcnJ1cHRlZCBkaXNwYXRjaGVyXCIpKVxuXG4gICAgICAgIGlmICghdXBkYXRlZCkgY29udGludWVcblxuICAgICAgICBjb25zdCByZWNvbmNpbGVkID0gdXBkYXRlZC5zdGF0dXMgPT09IFwicXVldWVkXCJcbiAgICAgICAgICA/IGF3YWl0IHRoaXMuX3JlY29uY2lsZVF1ZXVlZEpvYkNvbmN1cnJlbmN5KGRiLCB1cGRhdGVkLCBxdWV1ZXMpXG4gICAgICAgICAgOiB1cGRhdGVkXG5cbiAgICAgICAgcmVjb3ZlcmVkLnB1c2gocmVjb25jaWxlZClcbiAgICAgIH1cblxuICAgICAgYXdhaXQgdGhpcy5fcmVidWlsZENvbmN1cnJlbmN5Q291bnRzKGRiKVxuICAgICAgcmV0dXJuIHJlY292ZXJlZFxuICAgIH0pKVxuICB9XG5cbiAgLyoqXG4gICAqIERlbGV0ZXMgbG9jYWwgcXVldWUgc3RhdGUgZm9yIGZvY3VzZWQgdGVzdHMuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIGRlbGV0aW9uLlxuICAgKi9cbiAgYXN5bmMgY2xlYXJBbGwoKSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG4gICAgYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChjb25uZWN0aW9uKSA9PiBhd2FpdCB0aGlzLl9tdXRhdGUoY29ubmVjdGlvbiwgYXN5bmMgKGRiKSA9PiB7XG4gICAgICBhd2FpdCBkYi5xdWVyeShgREVMRVRFIEZST00gJHtkYi5xdW90ZVRhYmxlKExPQ0FMX0JBQ0tHUk9VTkRfSk9CU19UQUJMRSl9YClcbiAgICAgIGF3YWl0IGRiLnF1ZXJ5KGBERUxFVEUgRlJPTSAke2RiLnF1b3RlVGFibGUoTE9DQUxfQkFDS0dST1VORF9KT0JfQ09OQ1VSUkVOQ1lfVEFCTEUpfWApXG4gICAgfSkpXG4gIH1cblxuICAvKipcbiAgICogQXBwbGllcyB0aGUgY29tbW9uIHJldHJ5IG9yIGV4aGF1c3RlZCBmYWlsdXJlIHRyYW5zaXRpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gTG9jYWwgU1FMaXRlIGNvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUm93fSBqb2IgLSBBY3RpdmUgaGFuZG9mZi5cbiAgICogQHBhcmFtIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gZXJyb3IgLSBQZXJmb3JtYW5jZSBlcnJvci5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUm93IHwgbnVsbD59IC0gVHJhbnNpdGlvbiBzbmFwc2hvdC5cbiAgICovXG4gIGFzeW5jIF9hcHBseUZhaWx1cmUoZGIsIGpvYiwgZXJyb3IpIHtcbiAgICBjb25zdCBhdHRlbXB0cyA9IChqb2IuYXR0ZW1wdHMgfHwgMCkgKyAxXG4gICAgY29uc3QgbWF4UmV0cmllcyA9IG5vcm1hbGl6ZUJhY2tncm91bmRKb2JNYXhSZXRyaWVzKGpvYi5tYXhSZXRyaWVzKVxuICAgIGNvbnN0IHdpbGxSZXRyeSA9IGF0dGVtcHRzIDw9IG1heFJldHJpZXNcbiAgICBjb25zdCBub3dNcyA9IHRoaXMuY2xvY2subm93KClcbiAgICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gKi9cbiAgICBjb25zdCBkYXRhID0ge1xuICAgICAgYXR0ZW1wdHMsXG4gICAgICBoYW5kZWRfb2ZmX2F0X21zOiBudWxsLFxuICAgICAgaGFuZG9mZl9pZDogbnVsbCxcbiAgICAgIGxhc3RfZXJyb3I6IG5vcm1hbGl6ZUJhY2tncm91bmRKb2JFcnJvcihlcnJvciksXG4gICAgICBzdGF0dXM6IHdpbGxSZXRyeSA/IFwicXVldWVkXCIgOiBcImZhaWxlZFwiLFxuICAgICAgd29ya2VyX2lkOiBudWxsXG4gICAgfVxuXG4gICAgaWYgKHdpbGxSZXRyeSkge1xuICAgICAgT2JqZWN0LmFzc2lnbihkYXRhLCB7c2NoZWR1bGVkX2F0X21zOiBub3dNcyArIHJldHJ5RGVsYXlNcyhhdHRlbXB0cyl9KVxuICAgIH0gZWxzZSB7XG4gICAgICBPYmplY3QuYXNzaWduKGRhdGEsIHtmYWlsZWRfYXRfbXM6IG5vd01zfSlcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLl9sb2NrQ29uY3VycmVuY3lSb3coZGIsIGpvYi5jb25jdXJyZW5jeUtleSlcbiAgICBjb25zdCBhZmZlY3RlZFJvd3MgPSBhd2FpdCB0aGlzLl91cGRhdGVBZmZlY3RlZFJvd3MoZGIsIHtcbiAgICAgIGNvbmRpdGlvbnM6IHtoYW5kb2ZmX2lkOiBqb2IuaGFuZG9mZklkLCBpZDogam9iLmlkLCBzdGF0dXM6IFwiaGFuZGVkX29mZlwifSxcbiAgICAgIGRhdGEsXG4gICAgICB0YWJsZU5hbWU6IExPQ0FMX0JBQ0tHUk9VTkRfSk9CU19UQUJMRVxuICAgIH0pXG5cbiAgICBpZiAoYWZmZWN0ZWRSb3dzICE9PSAxKSByZXR1cm4gbnVsbFxuICAgIGF3YWl0IHRoaXMuX3JlbGVhc2VDb25jdXJyZW5jeShkYiwgam9iLmNvbmN1cnJlbmN5S2V5KVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLmpvYixcbiAgICAgIGF0dGVtcHRzLFxuICAgICAgZmFpbGVkQXRNczogd2lsbFJldHJ5ID8gam9iLmZhaWxlZEF0TXMgOiBub3dNcyxcbiAgICAgIGhhbmRlZE9mZkF0TXM6IG51bGwsXG4gICAgICBoYW5kb2ZmSWQ6IG51bGwsXG4gICAgICBsYXN0RXJyb3I6IGRhdGEubGFzdF9lcnJvcixcbiAgICAgIHNjaGVkdWxlZEF0TXM6IHdpbGxSZXRyeSA/IE51bWJlcihkYXRhLnNjaGVkdWxlZF9hdF9tcykgOiBqb2Iuc2NoZWR1bGVkQXRNcyxcbiAgICAgIHN0YXR1czogZGF0YS5zdGF0dXMsXG4gICAgICB3b3JrZXJJZDogbnVsbFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFbnN1cmVzIHRoYXQgYSBkdXJhYmxlIGNvbmN1cnJlbmN5IGNvdW50ZXIgZXhpc3RzIHdpdGggdGhlIHJlcXVpcmVkIGNhcC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBMb2NhbCBTUUxpdGUgY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLlJlc29sdmVkQmFja2dyb3VuZEpvYkNvbmN1cnJlbmN5fSBjb25jdXJyZW5jeSAtIERlc2lyZWQgY291bnRlci5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBlbnN1cmVkLlxuICAgKi9cbiAgYXN5bmMgX2Vuc3VyZUNvbmN1cnJlbmN5KGRiLCBjb25jdXJyZW5jeSkge1xuICAgIGF3YWl0IGRiLnVwc2VydCh7XG4gICAgICBjb25mbGljdENvbHVtbnM6IFtcImNvbmN1cnJlbmN5X2tleVwiXSxcbiAgICAgIGRhdGE6IHthY3RpdmVfY291bnQ6IDAsIGNvbmN1cnJlbmN5X2tleTogY29uY3VycmVuY3kuY29uY3VycmVuY3lLZXksIG1heF9jb25jdXJyZW5jeTogY29uY3VycmVuY3kubWF4Q29uY3VycmVuY3l9LFxuICAgICAgdGFibGVOYW1lOiBMT0NBTF9CQUNLR1JPVU5EX0pPQl9DT05DVVJSRU5DWV9UQUJMRSxcbiAgICAgIHVwZGF0ZUNvbHVtbnM6IFtcImNvbmN1cnJlbmN5X2tleVwiXVxuICAgIH0pXG5cbiAgICBjb25zdCByb3dzID0gYXdhaXQgZGJcbiAgICAgIC5uZXdRdWVyeSgpXG4gICAgICAuZnJvbShMT0NBTF9CQUNLR1JPVU5EX0pPQl9DT05DVVJSRU5DWV9UQUJMRSlcbiAgICAgIC53aGVyZSh7Y29uY3VycmVuY3lfa2V5OiBjb25jdXJyZW5jeS5jb25jdXJyZW5jeUtleX0pXG4gICAgICAubGltaXQoMSlcbiAgICAgIC5yZXN1bHRzKClcblxuICAgIGNvbnN0IGV4aXN0aW5nUm93ID0gLyoqIEB0eXBlIHt7bWF4X2NvbmN1cnJlbmN5OiBudW1iZXIgfCBzdHJpbmd9fSAqLyAocm93c1swXSlcbiAgICBjb25zdCBleGlzdGluZ0NhcCA9IE51bWJlcihleGlzdGluZ1Jvdy5tYXhfY29uY3VycmVuY3kpXG5cbiAgICBpZiAoZXhpc3RpbmdDYXAgPT09IGNvbmN1cnJlbmN5Lm1heENvbmN1cnJlbmN5KSByZXR1cm5cbiAgICBpZiAoIWNvbmN1cnJlbmN5LnF1ZXVlRGVyaXZlZCkgdGhyb3cgbmV3IEVycm9yKGBDb25mbGljdGluZyBtYXhDb25jdXJyZW5jeSBmb3IgYmFja2dyb3VuZCBqb2IgY29uY3VycmVuY3lLZXk6ICR7Y29uY3VycmVuY3kuY29uY3VycmVuY3lLZXl9YClcblxuICAgIGF3YWl0IGRiLnVwZGF0ZSh7XG4gICAgICBjb25kaXRpb25zOiB7Y29uY3VycmVuY3lfa2V5OiBjb25jdXJyZW5jeS5jb25jdXJyZW5jeUtleX0sXG4gICAgICBkYXRhOiB7bWF4X2NvbmN1cnJlbmN5OiBjb25jdXJyZW5jeS5tYXhDb25jdXJyZW5jeX0sXG4gICAgICB0YWJsZU5hbWU6IExPQ0FMX0JBQ0tHUk9VTkRfSk9CX0NPTkNVUlJFTkNZX1RBQkxFXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBBdG9taWNhbGx5IHJlc2VydmVzIG9uZSBzbG90IGZvciBhIGNvbmN1cnJlbmN5IGtleS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBDb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gY29uY3VycmVuY3lLZXkgLSBDb25jdXJyZW5jeSBrZXkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgYSBzbG90IHdhcyByZXNlcnZlZC5cbiAgICovXG4gIGFzeW5jIF9yZXNlcnZlQ29uY3VycmVuY3koZGIsIGNvbmN1cnJlbmN5S2V5KSB7XG4gICAgY29uc3QgdGFibGUgPSBkYi5xdW90ZVRhYmxlKExPQ0FMX0JBQ0tHUk9VTkRfSk9CX0NPTkNVUlJFTkNZX1RBQkxFKVxuICAgIGNvbnN0IGNvdW50ID0gZGIucXVvdGVDb2x1bW4oXCJhY3RpdmVfY291bnRcIilcbiAgICBjb25zdCBhZmZlY3RlZFJvd3MgPSBhd2FpdCBkYi5hZmZlY3RlZFJvd3MoXG4gICAgICBgVVBEQVRFICR7dGFibGV9IFNFVCAke2NvdW50fSA9ICR7Y291bnR9ICsgMSBgICtcbiAgICAgIGBXSEVSRSAke2RiLnF1b3RlQ29sdW1uKFwiY29uY3VycmVuY3lfa2V5XCIpfSA9ICR7ZGIucXVvdGUoY29uY3VycmVuY3lLZXkpfSBgICtcbiAgICAgIGBBTkQgJHtjb3VudH0gPCAke2RiLnF1b3RlQ29sdW1uKFwibWF4X2NvbmN1cnJlbmN5XCIpfWBcbiAgICApXG5cbiAgICByZXR1cm4gYWZmZWN0ZWRSb3dzID09PSAxXG4gIH1cblxuICAvKipcbiAgICogUmVsZWFzZXMgb25lIHNsb3QgZm9yIGEgY29uY3VycmVuY3kga2V5LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIENvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgbnVsbH0gY29uY3VycmVuY3lLZXkgLSBDb25jdXJyZW5jeSBrZXkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIHJlbGVhc2UuXG4gICAqL1xuICBhc3luYyBfcmVsZWFzZUNvbmN1cnJlbmN5KGRiLCBjb25jdXJyZW5jeUtleSkge1xuICAgIGlmICghY29uY3VycmVuY3lLZXkpIHJldHVyblxuXG4gICAgY29uc3QgdGFibGUgPSBkYi5xdW90ZVRhYmxlKExPQ0FMX0JBQ0tHUk9VTkRfSk9CX0NPTkNVUlJFTkNZX1RBQkxFKVxuICAgIGNvbnN0IGNvdW50ID0gZGIucXVvdGVDb2x1bW4oXCJhY3RpdmVfY291bnRcIilcblxuICAgIGF3YWl0IGRiLmFmZmVjdGVkUm93cyhcbiAgICAgIGBVUERBVEUgJHt0YWJsZX0gU0VUICR7Y291bnR9ID0gJHtjb3VudH0gLSAxIGAgK1xuICAgICAgYFdIRVJFICR7ZGIucXVvdGVDb2x1bW4oXCJjb25jdXJyZW5jeV9rZXlcIil9ID0gJHtkYi5xdW90ZShjb25jdXJyZW5jeUtleSl9IEFORCAke2NvdW50fSA+IDBgXG4gICAgKVxuICB9XG5cbiAgLyoqXG4gICAqIEFjcXVpcmVzIHRoZSB0cmFuc2FjdGlvbidzIHdyaXRlIGxvY2sgZm9yIGEgY29uY3VycmVuY3kgY291bnRlciByb3cuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gQ29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmcgfCBudWxsfSBjb25jdXJyZW5jeUtleSAtIENvbmN1cnJlbmN5IGtleS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgbG9ja2luZy5cbiAgICovXG4gIGFzeW5jIF9sb2NrQ29uY3VycmVuY3lSb3coZGIsIGNvbmN1cnJlbmN5S2V5KSB7XG4gICAgaWYgKCFjb25jdXJyZW5jeUtleSkgcmV0dXJuXG5cbiAgICBjb25zdCB0YWJsZSA9IGRiLnF1b3RlVGFibGUoTE9DQUxfQkFDS0dST1VORF9KT0JfQ09OQ1VSUkVOQ1lfVEFCTEUpXG4gICAgY29uc3QgY291bnQgPSBkYi5xdW90ZUNvbHVtbihcImFjdGl2ZV9jb3VudFwiKVxuXG4gICAgYXdhaXQgZGIucXVlcnkoXG4gICAgICBgVVBEQVRFICR7dGFibGV9IFNFVCAke2NvdW50fSA9ICR7Y291bnR9IGAgK1xuICAgICAgYFdIRVJFICR7ZGIucXVvdGVDb2x1bW4oXCJjb25jdXJyZW5jeV9rZXlcIil9ID0gJHtkYi5xdW90ZShjb25jdXJyZW5jeUtleSl9YFxuICAgIClcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWJ1aWxkcyBhY3RpdmUgY291bnRlcnMgZnJvbSBkdXJhYmxlIGhhbmRlZC1vZmYgam9icy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBDb25uZWN0aW9uLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciBjb3VudGVyIHJlYnVpbGQuXG4gICAqL1xuICBhc3luYyBfcmVidWlsZENvbmN1cnJlbmN5Q291bnRzKGRiKSB7XG4gICAgY29uc3QgY29uY3VycmVuY3lUYWJsZSA9IGRiLnF1b3RlVGFibGUoTE9DQUxfQkFDS0dST1VORF9KT0JfQ09OQ1VSUkVOQ1lfVEFCTEUpXG4gICAgY29uc3Qgam9ic1RhYmxlID0gZGIucXVvdGVUYWJsZShMT0NBTF9CQUNLR1JPVU5EX0pPQlNfVEFCTEUpXG5cbiAgICBhd2FpdCBkYi5xdWVyeShcbiAgICAgIGBVUERBVEUgJHtjb25jdXJyZW5jeVRhYmxlfSBTRVQgJHtkYi5xdW90ZUNvbHVtbihcImFjdGl2ZV9jb3VudFwiKX0gPSAoYCArXG4gICAgICBgU0VMRUNUIENPVU5UKCopIEZST00gJHtqb2JzVGFibGV9IFdIRVJFICR7am9ic1RhYmxlfS4ke2RiLnF1b3RlQ29sdW1uKFwic3RhdHVzXCIpfSA9ICR7ZGIucXVvdGUoXCJoYW5kZWRfb2ZmXCIpfSBBTkQgYCArXG4gICAgICBgJHtqb2JzVGFibGV9LiR7ZGIucXVvdGVDb2x1bW4oXCJjb25jdXJyZW5jeV9rZXlcIil9ID0gJHtjb25jdXJyZW5jeVRhYmxlfS4ke2RiLnF1b3RlQ29sdW1uKFwiY29uY3VycmVuY3lfa2V5XCIpfSlgXG4gICAgKVxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyB0aGUgY29uZmlndXJlZCBxdWV1ZS1wcmlvcml0eSBvcmRlcmluZyBleHByZXNzaW9uLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIENvbm5lY3Rpb24uXG4gICAqIEByZXR1cm5zIHtzdHJpbmcgfCBudWxsfSAtIFF1ZXVlIHByaW9yaXR5IGV4cHJlc3Npb24uXG4gICAqL1xuICBfcXVldWVQcmlvcml0eU9yZGVyU3FsKGRiKSB7XG4gICAgY29uc3QgcXVldWVzID0gdGhpcy5jb25maWd1cmF0aW9uLmdldEJhY2tncm91bmRKb2JzQ29uZmlnKCkucXVldWVzXG4gICAgY29uc3QgcHJpb3JpdGl6ZWQgPSBPYmplY3QuZW50cmllcyhxdWV1ZXMpXG4gICAgICAuZmlsdGVyKChbLCBxdWV1ZV0pID0+IE51bWJlci5pc0Zpbml0ZShxdWV1ZT8ucHJpb3JpdHkpICYmIE51bWJlcihxdWV1ZS5wcmlvcml0eSkgIT09IDApXG4gICAgICAubWFwKChbcXVldWVOYW1lLCBxdWV1ZV0pID0+IFtxdWV1ZU5hbWUsIE51bWJlcihxdWV1ZS5wcmlvcml0eSldKVxuXG4gICAgaWYgKHByaW9yaXRpemVkLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG51bGxcblxuICAgIGNvbnN0IHdoZW5zID0gcHJpb3JpdGl6ZWRcbiAgICAgIC5tYXAoKFtxdWV1ZSwgcHJpb3JpdHldKSA9PiBgV0hFTiAke2RiLnF1b3RlKHF1ZXVlKX0gVEhFTiAke3ByaW9yaXR5fWApXG4gICAgICAuam9pbihcIiBcIilcblxuICAgIHJldHVybiBgQ0FTRSBDT0FMRVNDRSgke2RiLnF1b3RlQ29sdW1uKFwicXVldWVcIil9LCAke2RiLnF1b3RlKERFRkFVTFRfQkFDS0dST1VORF9KT0JfUVVFVUUpfSkgJHt3aGVuc30gRUxTRSAwIEVORGBcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3Mgd2hldGhlciBhIHBlcnNpc3RlZCBoYW5kb2ZmIG93bnMgdGhlIHN1cHBsaWVkIGFja25vd2xlZGdlbWVudCBmZW5jZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JSb3cgfCBudWxsfSBqb2IgLSBQZXJzaXN0ZWQgam9iLlxuICAgKiBAcGFyYW0ge3N0cmluZyB8IHVuZGVmaW5lZH0gaGFuZG9mZklkIC0gSGFuZG9mZiBmZW5jZS5cbiAgICogQHJldHVybnMge2pvYiBpcyBpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JSb3d9IC0gV2hldGhlciBhY2NlcHRlZC5cbiAgICovXG4gIF9hY2NlcHRzSGFuZG9mZihqb2IsIGhhbmRvZmZJZCkge1xuICAgIHJldHVybiBCb29sZWFuKGpvYiAmJiBqb2Iuc3RhdHVzID09PSBcImhhbmRlZF9vZmZcIiAmJiBqb2IuaGFuZG9mZklkICYmIGpvYi5oYW5kb2ZmSWQgPT09IGhhbmRvZmZJZClcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBhIHBlcnNpc3RlZCBsb2NhbCBqb2IgdXNpbmcgdGhlIGN1cnJlbnQgY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBDb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gam9iSWQgLSBKb2IgaWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvdyB8IG51bGw+fSAtIFBlcnNpc3RlZCByb3cuXG4gICAqL1xuICBhc3luYyBfZ2V0Sm9iKGRiLCBqb2JJZCkge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCBkYi5uZXdRdWVyeSgpLmZyb20oTE9DQUxfQkFDS0dST1VORF9KT0JTX1RBQkxFKS53aGVyZSh7aWQ6IGpvYklkfSkubGltaXQoMSkucmVzdWx0cygpXG5cbiAgICByZXR1cm4gcm93c1swXSA/IHRoaXMuX25vcm1hbGl6ZVJvdyhyb3dzWzBdKSA6IG51bGxcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpemVzIG9uZSByYXcgbG9jYWwgZGF0YWJhc2Ugcm93LlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gcm93IC0gUmF3IHJvdy5cbiAgICogQHJldHVybnMge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvd30gLSBOb3JtYWxpemVkIHJvdy5cbiAgICovXG4gIF9ub3JtYWxpemVSb3cocm93KSB7XG4gICAgY29uc3QgcGFyc2VkQXJncyA9IEpTT04ucGFyc2UoU3RyaW5nKHJvdy5hcmdzX2pzb24pKVxuICAgIGNvbnN0IGV4ZWN1dGlvbk1vZGUgPSBub3JtYWxpemVCYWNrZ3JvdW5kSm9iRXhlY3V0aW9uTW9kZSh7ZXhlY3V0aW9uTW9kZTogU3RyaW5nKHJvdy5leGVjdXRpb25fbW9kZSl9LCBcImlubGluZVwiLCBMT0NBTF9FWEVDVVRJT05fTU9ERVMpXG5cbiAgICBpZiAoIUFycmF5LmlzQXJyYXkocGFyc2VkQXJncykpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBsb2NhbCBiYWNrZ3JvdW5kIGpvYiBhcmdzX2pzb24gZm9yIGpvYjogJHtTdHJpbmcocm93LmlkKX1gKVxuICAgIGlmIChleGVjdXRpb25Nb2RlICE9PSBcImlubGluZVwiKSB0aHJvdyBuZXcgRXJyb3IoXCJMb2NhbCBiYWNrZ3JvdW5kIGpvYiBleGVjdXRpb24gbW9kZSBpbnZhcmlhbnQgd2FzIHZpb2xhdGVkXCIpXG5cbiAgICByZXR1cm4ge1xuICAgICAgYXJnczogcGFyc2VkQXJncyxcbiAgICAgIGF0dGVtcHRzOiB0aGlzLl9udW1iZXJPck51bGwocm93LmF0dGVtcHRzKSxcbiAgICAgIGNvbXBsZXRlZEF0TXM6IHRoaXMuX251bWJlck9yTnVsbChyb3cuY29tcGxldGVkX2F0X21zKSxcbiAgICAgIGNvbmN1cnJlbmN5S2V5OiByb3cuY29uY3VycmVuY3lfa2V5ID09PSBudWxsIHx8IHJvdy5jb25jdXJyZW5jeV9rZXkgPT09IHVuZGVmaW5lZCA/IG51bGwgOiBTdHJpbmcocm93LmNvbmN1cnJlbmN5X2tleSksXG4gICAgICBjcmVhdGVkQXRNczogdGhpcy5fbnVtYmVyT3JOdWxsKHJvdy5jcmVhdGVkX2F0X21zKSxcbiAgICAgIGV4ZWN1dGlvbk1vZGUsXG4gICAgICBmYWlsZWRBdE1zOiB0aGlzLl9udW1iZXJPck51bGwocm93LmZhaWxlZF9hdF9tcyksXG4gICAgICBoYW5kZWRPZmZBdE1zOiB0aGlzLl9udW1iZXJPck51bGwocm93LmhhbmRlZF9vZmZfYXRfbXMpLFxuICAgICAgaGFuZG9mZklkOiByb3cuaGFuZG9mZl9pZCA9PT0gbnVsbCB8fCByb3cuaGFuZG9mZl9pZCA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IFN0cmluZyhyb3cuaGFuZG9mZl9pZCksXG4gICAgICBpZDogU3RyaW5nKHJvdy5pZCksXG4gICAgICBqb2JOYW1lOiBTdHJpbmcocm93LmpvYl9uYW1lKSxcbiAgICAgIGxhc3RFcnJvcjogcm93Lmxhc3RfZXJyb3IgPT09IG51bGwgfHwgcm93Lmxhc3RfZXJyb3IgPT09IHVuZGVmaW5lZCA/IG51bGwgOiBTdHJpbmcocm93Lmxhc3RfZXJyb3IpLFxuICAgICAgbWF4Q29uY3VycmVuY3k6IHRoaXMuX251bWJlck9yTnVsbChyb3cubWF4X2NvbmN1cnJlbmN5KSxcbiAgICAgIG1heFJldHJpZXM6IHRoaXMuX251bWJlck9yTnVsbChyb3cubWF4X3JldHJpZXMpLFxuICAgICAgb3JwaGFuZWRBdE1zOiBudWxsLFxuICAgICAgcXVldWU6IHJvdy5xdWV1ZSA/IFN0cmluZyhyb3cucXVldWUpIDogREVGQVVMVF9CQUNLR1JPVU5EX0pPQl9RVUVVRSxcbiAgICAgIHNjaGVkdWxlS2V5OiBudWxsLFxuICAgICAgc2NoZWR1bGVkQXRNczogdGhpcy5fbnVtYmVyT3JOdWxsKHJvdy5zY2hlZHVsZWRfYXRfbXMpLFxuICAgICAgc3RhdHVzOiByb3cuc3RhdHVzID8gU3RyaW5nKHJvdy5zdGF0dXMpIDogXCJxdWV1ZWRcIixcbiAgICAgIHRpbWVvdXRNczogbnVsbCxcbiAgICAgIHdvcmtlcklkOiByb3cud29ya2VyX2lkID09PSBudWxsIHx8IHJvdy53b3JrZXJfaWQgPT09IHVuZGVmaW5lZCA/IG51bGwgOiBTdHJpbmcocm93Lndvcmtlcl9pZClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTm9ybWFsaXplcyBvbmUgbnVsbGFibGUgZGF0YWJhc2UgbnVtYmVyLlxuICAgKiBAcGFyYW0ge1JldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSB2YWx1ZSAtIERhdGFiYXNlIG51bWJlci5cbiAgICogQHJldHVybnMge251bWJlciB8IG51bGx9IC0gTm9ybWFsaXplZCBudW1iZXIuXG4gICAqL1xuICBfbnVtYmVyT3JOdWxsKHZhbHVlKSB7XG4gICAgaWYgKHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSB1bmRlZmluZWQgfHwgdmFsdWUgPT09IFwiXCIpIHJldHVybiBudWxsXG5cbiAgICBjb25zdCBudW1iZXIgPSBOdW1iZXIodmFsdWUpXG5cbiAgICByZXR1cm4gTnVtYmVyLmlzTmFOKG51bWJlcikgPyBudWxsIDogbnVtYmVyXG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZXMgYSBzdHJ1Y3R1cmVkIHVwZGF0ZSBhbmQgcmVwb3J0cyBpdHMgYWZmZWN0ZWQtcm93IGNvdW50LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIENvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLlVwZGF0ZVNxbEFyZ3NUeXBlfSBhcmdzIC0gVXBkYXRlIGFyZ3VtZW50cy5cbiAgICogQHJldHVybnMge1Byb21pc2U8bnVtYmVyPn0gLSBBZmZlY3RlZCByb3dzLlxuICAgKi9cbiAgYXN5bmMgX3VwZGF0ZUFmZmVjdGVkUm93cyhkYiwgYXJncykgeyByZXR1cm4gYXdhaXQgZGIuYWZmZWN0ZWRSb3dzKGRiLnVwZGF0ZVNxbChhcmdzKSkgfVxuXG4gIC8qKlxuICAgKiBKb2lucyBhbiBhbWJpZW50IGFwcCB0cmFuc2FjdGlvbiBvciB1c2VzIHRoZSBkYXRhYmFzZSdzIHNjb3BlZCBvcGVyYXRpb24gbGVhc2UuXG4gICAqIEB0ZW1wbGF0ZSBUXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gQ29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHsoZGI6IGltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0KSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIE11dGF0aW9uLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUPn0gLSBNdXRhdGlvbiByZXN1bHQuXG4gICAqL1xuICBhc3luYyBfbXV0YXRlKGRiLCBjYWxsYmFjaykge1xuICAgIGlmIChkYi5pbnNpZGVUcmFuc2FjdGlvbigpKSByZXR1cm4gYXdhaXQgdGhpcy5fdHJhbnNhY3Rpb25SZXN1bHQoZGIsIGFzeW5jICgpID0+IGF3YWl0IGNhbGxiYWNrKGRiKSlcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24ud2l0aFRyYW5zYWN0aW9uKHtcbiAgICAgIGRhdGFiYXNlSWRlbnRpZmllcjogdGhpcy5nZXREYXRhYmFzZUlkZW50aWZpZXIoKSxcbiAgICAgIG5hbWU6IFwiTG9jYWwgYmFja2dyb3VuZCBqb2JzIG11dGF0aW9uXCJcbiAgICB9LCBhc3luYyAob3BlcmF0aW9uKSA9PiBhd2FpdCBjYWxsYmFjayhvcGVyYXRpb24uY29ubmVjdGlvbigpKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGEgY2FsbGJhY2sgaW4gYSB0cmFuc2FjdGlvbiBhbmQgcmV0dXJucyBpdHMgY2FwdHVyZWQgcmVzdWx0LlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIENvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7KCkgPT4gUHJvbWlzZTxUPn0gY2FsbGJhY2sgLSBUcmFuc2FjdGlvbiBjYWxsYmFjay5cbiAgICogQHJldHVybnMge1Byb21pc2U8VD59IC0gQ2FsbGJhY2sgcmVzdWx0LlxuICAgKi9cbiAgYXN5bmMgX3RyYW5zYWN0aW9uUmVzdWx0KGRiLCBjYWxsYmFjaykge1xuICAgIGxldCBjb21wbGV0ZWQgPSBmYWxzZVxuICAgIC8qKiBAdHlwZSB7VCB8IHVuZGVmaW5lZH0gKi9cbiAgICBsZXQgcmVzdWx0XG5cbiAgICBhd2FpdCBkYi50cmFuc2FjdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICByZXN1bHQgPSBhd2FpdCBjYWxsYmFjaygpXG4gICAgICBjb21wbGV0ZWQgPSB0cnVlXG4gICAgfSlcblxuICAgIGlmICghY29tcGxldGVkKSB0aHJvdyBuZXcgRXJyb3IoXCJMb2NhbCBiYWNrZ3JvdW5kIGpvYnMgdHJhbnNhY3Rpb24gY2FsbGJhY2sgd2FzIG5vdCBpbnZva2VkXCIpXG4gICAgcmV0dXJuIC8qKiBAdHlwZSB7VH0gKi8gKHJlc3VsdClcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGEgY2FsbGJhY2sgd2l0aCB0aGUgY29uZmlndXJlZCBsb2NhbCBkYXRhYmFzZSBjb25uZWN0aW9uLlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0geyhkYjogaW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHQpID0+IFByb21pc2U8VD59IGNhbGxiYWNrIC0gQ29ubmVjdGlvbiBjYWxsYmFjay5cbiAgICogQHJldHVybnMge1Byb21pc2U8VD59IC0gQ2FsbGJhY2sgcmVzdWx0LlxuICAgKi9cbiAgYXN5bmMgX3dpdGhEYihjYWxsYmFjaykge1xuICAgIGNvbnN0IGRhdGFiYXNlSWRlbnRpZmllciA9IHRoaXMuZ2V0RGF0YWJhc2VJZGVudGlmaWVyKClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uZW5zdXJlQ29ubmVjdGlvbnMoe2RhdGFiYXNlSWRlbnRpZmllcnM6IFtkYXRhYmFzZUlkZW50aWZpZXJdLCBuYW1lOiBcIkxvY2FsIGJhY2tncm91bmQgam9icyBzdG9yZVwifSwgYXN5bmMgKGRicykgPT4ge1xuICAgICAgY29uc3QgZGIgPSBkYnNbZGF0YWJhc2VJZGVudGlmaWVyXVxuXG4gICAgICBpZiAoIWRiKSB0aHJvdyBuZXcgRXJyb3IoYE5vIGxvY2FsIGJhY2tncm91bmQtam9icyBkYXRhYmFzZSBjb25uZWN0aW9uIGF2YWlsYWJsZSBmb3IgaWRlbnRpZmllcjogJHtkYXRhYmFzZUlkZW50aWZpZXJ9YClcblxuICAgICAgcmV0dXJuIGF3YWl0IGNhbGxiYWNrKGRiKVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogUmVwb3J0cyBhbiB1bmV4cGVjdGVkIGxvY2FsLXN0b3JlIGZhaWx1cmUgdGhyb3VnaCBmcmFtZXdvcmsgY2hhbm5lbHMuXG4gICAqIEBwYXJhbSB7e2Vycm9yOiBFcnJvciwgc3RhZ2U6IHN0cmluZ319IGFyZ3MgLSBFcnJvciByZXBvcnQuXG4gICAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAgICovXG4gIF9yZXBvcnRGcmFtZXdvcmtFcnJvcih7ZXJyb3IsIHN0YWdlfSkge1xuICAgIGNvbnN0IHBheWxvYWQgPSB7Y29udGV4dDoge3N0YWdlfSwgZXJyb3J9XG4gICAgY29uc3QgZXJyb3JFdmVudHMgPSB0aGlzLmNvbmZpZ3VyYXRpb24uZ2V0RXJyb3JFdmVudHMoKVxuXG4gICAgZXJyb3JFdmVudHMuZW1pdChcImZyYW1ld29yay1lcnJvclwiLCBwYXlsb2FkKVxuICAgIGVycm9yRXZlbnRzLmVtaXQoXCJhbGwtZXJyb3JcIiwgey4uLnBheWxvYWQsIGVycm9yVHlwZTogXCJmcmFtZXdvcmstZXJyb3JcIn0pXG4gIH1cbn1cbiJdfQ==
|