@workflow/core 5.0.0-beta.39 → 5.0.0-beta.40
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/dist/classify-error.d.ts.map +1 -1
- package/dist/classify-error.js +5 -2
- package/dist/correlation-id.d.ts +109 -0
- package/dist/correlation-id.d.ts.map +1 -0
- package/dist/correlation-id.js +139 -0
- package/dist/describe-error.d.ts.map +1 -1
- package/dist/describe-error.js +14 -1
- package/dist/events-consumer.d.ts +1 -0
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +11 -3
- package/dist/private.d.ts +17 -1
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +1 -1
- package/dist/runtime/constants.d.ts +19 -0
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +30 -1
- package/dist/runtime/deployment-guard.d.ts +99 -0
- package/dist/runtime/deployment-guard.d.ts.map +1 -0
- package/dist/runtime/deployment-guard.js +154 -0
- package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
- package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
- package/dist/runtime/quickjs-assets.generated.js +30 -0
- package/dist/runtime/quickjs-entrypoint.d.ts +124 -0
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
- package/dist/runtime/quickjs-entrypoint.js +1429 -0
- package/dist/runtime/quickjs-runtime.d.ts +193 -0
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
- package/dist/runtime/quickjs-runtime.js +1944 -0
- package/dist/runtime/run.d.ts +2 -0
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +9 -4
- package/dist/runtime/runs.d.ts +18 -1
- package/dist/runtime/runs.d.ts.map +1 -1
- package/dist/runtime/runs.js +96 -2
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +9 -1
- package/dist/runtime/suspension-handler.d.ts +7 -0
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +29 -2
- package/dist/runtime/vm-mode.d.ts +44 -0
- package/dist/runtime/vm-mode.d.ts.map +1 -0
- package/dist/runtime/vm-mode.js +62 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
- package/dist/runtime/vm-serde-bundle.generated.js +16 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +484 -47
- package/dist/serialization/codec-devalue-vm.d.ts +9 -0
- package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
- package/dist/serialization/codec-devalue-vm.js +137 -0
- package/dist/serialization/reducers/class-vm.d.ts +20 -0
- package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/class-vm.js +77 -0
- package/dist/serialization/reducers/common-vm.d.ts +15 -0
- package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/common-vm.js +579 -0
- package/dist/serialization/reducers/common.d.ts.map +1 -1
- package/dist/serialization/reducers/common.js +28 -6
- package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
- package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/step-function-vm.js +97 -0
- package/dist/serialization/vm-bundle-entry.d.ts +12 -0
- package/dist/serialization/vm-bundle-entry.d.ts.map +1 -0
- package/dist/serialization/vm-bundle-entry.js +58 -0
- package/dist/serialization/workflow-vm.d.ts +24 -0
- package/dist/serialization/workflow-vm.d.ts.map +1 -0
- package/dist/serialization/workflow-vm.js +66 -0
- package/dist/serialization.d.ts +2 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +26 -21
- package/dist/source-map.d.ts +20 -0
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +47 -1
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +7 -2
- package/dist/telemetry/semantic-conventions.d.ts +78 -0
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +56 -1
- package/dist/telemetry.d.ts +5 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +13 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/dist/workflow/abort-controller.d.ts.map +1 -1
- package/dist/workflow/abort-controller.js +6 -4
- package/dist/workflow/attribute-dispatcher.js +2 -2
- package/dist/workflow/hook.js +2 -2
- package/dist/workflow/sleep.js +2 -2
- package/dist/workflow.d.ts +49 -2
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +723 -569
- package/docs/api-reference/create-hook.mdx +1 -1
- package/docs/foundations/starting-workflows.mdx +74 -1
- package/package.json +8 -7
|
@@ -0,0 +1,1944 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuickJS WASM workflow VM.
|
|
3
|
+
*
|
|
4
|
+
* An alternative engine for the event-replay execution model: the workflow
|
|
5
|
+
* code runs inside a QuickJS WASM VM (via quickjs-wasi) instead of a
|
|
6
|
+
* `node:vm` context. Every invocation creates a fresh VM, re-executes the
|
|
7
|
+
* workflow function from the top, and replays the recorded event log to
|
|
8
|
+
* resolve awaited primitives — the same replay semantics as the `node:vm`
|
|
9
|
+
* engine.
|
|
10
|
+
*
|
|
11
|
+
* The workflow primitives (useStep, sleep, createHook) are implemented as
|
|
12
|
+
* JavaScript code running inside the QuickJS VM. The host communicates with
|
|
13
|
+
* the VM by evaluating small JS snippets to read pending operations and
|
|
14
|
+
* resolve/reject promises.
|
|
15
|
+
*
|
|
16
|
+
* The VM bootstrap is deliberately split into two phases:
|
|
17
|
+
* 1. Static initialization (`initWorkflowVM`) — run-independent setup:
|
|
18
|
+
* VM creation, the serde bundle, and the workflow primitives.
|
|
19
|
+
* 2. Per-run initialization (inline in `runQuickJSWorkflow`) — seeded
|
|
20
|
+
* PRNG/ULID host functions, workflow bundle evaluation, run metadata,
|
|
21
|
+
* workflow input, and start.
|
|
22
|
+
* Keeping the phases separate is groundwork for VM-memory snapshotting:
|
|
23
|
+
* a follow-up can persist/restore the VM at the phase boundary (e.g. a
|
|
24
|
+
* build-time initial snapshot) without restructuring this module. Note
|
|
25
|
+
* that bundle evaluation currently sits in the per-run phase so that
|
|
26
|
+
* module-scope user code observes the seeded `Math.random`, matching the
|
|
27
|
+
* `node:vm` engine's replay determinism.
|
|
28
|
+
*/
|
|
29
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
30
|
+
if (value !== null && value !== void 0) {
|
|
31
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
32
|
+
var dispose, inner;
|
|
33
|
+
if (async) {
|
|
34
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
35
|
+
dispose = value[Symbol.asyncDispose];
|
|
36
|
+
}
|
|
37
|
+
if (dispose === void 0) {
|
|
38
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
39
|
+
dispose = value[Symbol.dispose];
|
|
40
|
+
if (async) inner = dispose;
|
|
41
|
+
}
|
|
42
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
43
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
44
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
45
|
+
}
|
|
46
|
+
else if (async) {
|
|
47
|
+
env.stack.push({ async: true });
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
};
|
|
51
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
52
|
+
return function (env) {
|
|
53
|
+
function fail(e) {
|
|
54
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
55
|
+
env.hasError = true;
|
|
56
|
+
}
|
|
57
|
+
var r, s = 0;
|
|
58
|
+
function next() {
|
|
59
|
+
while (r = env.stack.pop()) {
|
|
60
|
+
try {
|
|
61
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
62
|
+
if (r.dispose) {
|
|
63
|
+
var result = r.dispose.call(r.value);
|
|
64
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
65
|
+
}
|
|
66
|
+
else s |= 1;
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
fail(e);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
73
|
+
if (env.hasError) throw env.error;
|
|
74
|
+
}
|
|
75
|
+
return next();
|
|
76
|
+
};
|
|
77
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
78
|
+
var e = new Error(message);
|
|
79
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
80
|
+
});
|
|
81
|
+
import * as nanoid from 'nanoid';
|
|
82
|
+
import { JSException, QuickJS, } from 'quickjs-wasi';
|
|
83
|
+
import seedrandom from 'seedrandom';
|
|
84
|
+
import { runtimeLogger } from '../logger.js';
|
|
85
|
+
import { decompress } from '../serialization/compression.js';
|
|
86
|
+
import { decrypt } from '../serialization/encryption.js';
|
|
87
|
+
import { getReplayTimeoutMs } from './constants.js';
|
|
88
|
+
import { quickjsExtensions, quickjsWasm } from './quickjs-assets.generated.js';
|
|
89
|
+
import { runIdCreatedAt } from './run-id-time.js';
|
|
90
|
+
import { VM_SERDE_BUNDLE } from './vm-serde-bundle.generated.js';
|
|
91
|
+
// ---- Host -> VM payload preparation ----
|
|
92
|
+
/**
|
|
93
|
+
* Prepare persisted payload bytes for consumption inside the VM: decrypt
|
|
94
|
+
* (when an encryption key is configured) and decompress (specVersion >= 5
|
|
95
|
+
* payloads may be gzip/zstd-compressed). The VM only understands plain
|
|
96
|
+
* format-prefixed 'devl' bytes — it has neither the key material nor zlib.
|
|
97
|
+
* The key is the run's full DecryptionKey capability (symmetric AES key +
|
|
98
|
+
* X25519 keypair) so sealed `encp` hook payloads from cross-deployment
|
|
99
|
+
* resumeHook() calls open here too, not just symmetric `encr` ones.
|
|
100
|
+
* Both stages are format-prefix dispatched, so plaintext/uncompressed
|
|
101
|
+
* data passes through unchanged. Mirrors `prepareReplayPayload` in
|
|
102
|
+
* serialization.ts (the node:vm engine's equivalent host-side stage).
|
|
103
|
+
*/
|
|
104
|
+
async function prepareBytesForVM(data, key) {
|
|
105
|
+
return (await decompress(await decrypt(data, key)));
|
|
106
|
+
}
|
|
107
|
+
// ---- VM Bootstrap Code ----
|
|
108
|
+
/**
|
|
109
|
+
* JavaScript code that runs inside the QuickJS VM to set up the workflow
|
|
110
|
+
* primitives. This sets up:
|
|
111
|
+
* - globalThis.__private_workflows (Map) - workflow registry
|
|
112
|
+
* - globalThis.__resolvers (Object) - pending promise resolve/reject functions
|
|
113
|
+
* - globalThis.__pending (Array) - metadata about pending operations
|
|
114
|
+
* - globalThis[Symbol.for("WORKFLOW_USE_STEP")] - step proxy factory
|
|
115
|
+
* - globalThis[Symbol.for("WORKFLOW_SLEEP")] - sleep function
|
|
116
|
+
*/
|
|
117
|
+
const VM_BOOTSTRAP = `
|
|
118
|
+
// Symbol.dispose / Symbol.asyncDispose polyfills for QuickJS
|
|
119
|
+
if (typeof Symbol.dispose === "undefined") {
|
|
120
|
+
Symbol.dispose = Symbol.for("Symbol.dispose");
|
|
121
|
+
}
|
|
122
|
+
if (typeof Symbol.asyncDispose === "undefined") {
|
|
123
|
+
Symbol.asyncDispose = Symbol.for("Symbol.asyncDispose");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
globalThis.__private_workflows = new Map();
|
|
127
|
+
globalThis.__resolvers = {};
|
|
128
|
+
globalThis.__pending = [];
|
|
129
|
+
globalThis.__workflowResult = undefined;
|
|
130
|
+
globalThis.__workflowError = undefined;
|
|
131
|
+
// Buffer for hook_received payloads that arrive before the hook is awaited.
|
|
132
|
+
// Keyed by correlationId → array of payloads (preserves delivery order).
|
|
133
|
+
// This mirrors the event-replay runtime's payloadsQueue in hook.ts.
|
|
134
|
+
globalThis.__hookPayloadBuffer = {};
|
|
135
|
+
|
|
136
|
+
// Buffer for step/wait/attr terminal outcomes that arrive before this VM
|
|
137
|
+
// has constructed the corresponding awaiting promise. In fresh-VM replay
|
|
138
|
+
// the multi-pass event scan makes this unreachable (awaits are
|
|
139
|
+
// reconstructed before their terminals are re-scanned), but the live
|
|
140
|
+
// continuation path (continueWithEvents) scans each delta exactly once —
|
|
141
|
+
// a concurrent invocation's terminal arriving before this VM reaches the
|
|
142
|
+
// await would otherwise be dropped on the floor and the await would
|
|
143
|
+
// never settle (the feed's seen-set means it is never re-delivered).
|
|
144
|
+
// Mirrors __hookPayloadBuffer, which exists for exactly this reason on
|
|
145
|
+
// the hook path. Keyed by correlationId → single terminal (steps, waits
|
|
146
|
+
// and attrs settle exactly once).
|
|
147
|
+
globalThis.__terminalBuffer = {};
|
|
148
|
+
|
|
149
|
+
// Registers a resolver for an awaited primitive, first draining any
|
|
150
|
+
// buffered terminal recorded for the correlationId. Entries are prepared
|
|
151
|
+
// host-side (bytes already decrypted; see processEvents).
|
|
152
|
+
globalThis.__registerResolver = function(correlationId, resolve, reject) {
|
|
153
|
+
var buffered = globalThis.__terminalBuffer[correlationId];
|
|
154
|
+
if (buffered) {
|
|
155
|
+
delete globalThis.__terminalBuffer[correlationId];
|
|
156
|
+
if (buffered.kind === "resolve_bytes") {
|
|
157
|
+
resolve(globalThis[Symbol.for("workflow-deserialize")](buffered.bytes));
|
|
158
|
+
} else if (buffered.kind === "resolve_value") {
|
|
159
|
+
resolve(buffered.value);
|
|
160
|
+
} else if (buffered.kind === "reject_bytes") {
|
|
161
|
+
reject(globalThis[Symbol.for("workflow-deserialize")](buffered.bytes));
|
|
162
|
+
} else if (buffered.kind === "reject_error") {
|
|
163
|
+
var e = new Error(buffered.message);
|
|
164
|
+
e.name = "FatalError";
|
|
165
|
+
e.fatal = true;
|
|
166
|
+
if (buffered.stack) e.stack = buffered.stack;
|
|
167
|
+
reject(e);
|
|
168
|
+
} else {
|
|
169
|
+
resolve(undefined);
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
globalThis.__resolvers[correlationId] = { resolve: resolve, reject: reject };
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Stubs for Web APIs that the workflow bundle may reference but are not
|
|
177
|
+
// available in QuickJS. Native C extensions (encoding, headers, url,
|
|
178
|
+
// structured-clone) provide the real implementations; these are minimal
|
|
179
|
+
// stubs for APIs that don't have native extensions yet. (btoa/atob and
|
|
180
|
+
// the Uint8Array base64/hex methods are built into quickjs-wasi >= 3.)
|
|
181
|
+
|
|
182
|
+
if (typeof ReadableStream === "undefined") {
|
|
183
|
+
// Minimal ReadableStream that stores body data for Response.json()/text()
|
|
184
|
+
globalThis.ReadableStream = function() {};
|
|
185
|
+
globalThis.ReadableStream.prototype.__bodyData = null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (typeof WritableStream === "undefined") {
|
|
189
|
+
globalThis.WritableStream = function() {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (typeof TransformStream === "undefined") {
|
|
193
|
+
globalThis.TransformStream = function() {};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (typeof console === "undefined") {
|
|
197
|
+
globalThis.console = { log: function(){}, error: function(){}, warn: function(){}, info: function(){} };
|
|
198
|
+
}
|
|
199
|
+
// Stub exports/module for CJS bundle format
|
|
200
|
+
globalThis.exports = {};
|
|
201
|
+
globalThis.module = { exports: globalThis.exports };
|
|
202
|
+
// NOTE: TextEncoder/TextDecoder are provided by the native encoding extension.
|
|
203
|
+
|
|
204
|
+
// ---- Deterministic \`crypto\` (parity with the node:vm engine) ----
|
|
205
|
+
// getRandomValues / randomUUID draw from Math.random, which the host
|
|
206
|
+
// replaces with the run's seeded PRNG before any user code runs — so the
|
|
207
|
+
// values replay deterministically and match the node engine, whose
|
|
208
|
+
// implementations draw from the same seeded sequence (see vm/index.ts).
|
|
209
|
+
// Every crypto.subtle method throws with the same guidance as the node
|
|
210
|
+
// engine's non-replayable methods; unlike node, \`digest\` is also
|
|
211
|
+
// unavailable here (no native hash in the VM yet).
|
|
212
|
+
(function() {
|
|
213
|
+
function getRandomValues(array) {
|
|
214
|
+
for (var i = 0; i < array.length; i++) {
|
|
215
|
+
array[i] = Math.floor(Math.random() * 256);
|
|
216
|
+
}
|
|
217
|
+
return array;
|
|
218
|
+
}
|
|
219
|
+
// Mirrors vm/uuid.ts createRandomUUID: identical draw pattern from the
|
|
220
|
+
// seeded PRNG, so both engines produce the same UUID at the same point
|
|
221
|
+
// in a replay.
|
|
222
|
+
function randomUUID() {
|
|
223
|
+
var chars = "0123456789abcdef";
|
|
224
|
+
var uuid = "";
|
|
225
|
+
for (var i = 0; i < 36; i++) {
|
|
226
|
+
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
227
|
+
uuid += "-";
|
|
228
|
+
} else if (i === 14) {
|
|
229
|
+
uuid += "4";
|
|
230
|
+
} else if (i === 19) {
|
|
231
|
+
uuid += chars[Math.floor(Math.random() * 4) + 8];
|
|
232
|
+
} else {
|
|
233
|
+
uuid += chars[Math.floor(Math.random() * 16)];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return uuid;
|
|
237
|
+
}
|
|
238
|
+
function subtleThrow(name) {
|
|
239
|
+
return function() {
|
|
240
|
+
var err = new Error("\`crypto.subtle." + name + "()\` is not available inside a workflow function. Move it to a step function where full Node.js crypto is available.");
|
|
241
|
+
err.name = "WorkflowRuntimeError";
|
|
242
|
+
throw err;
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
var subtle = {};
|
|
246
|
+
["encrypt","decrypt","sign","verify","digest","generateKey","deriveKey","deriveBits","importKey","exportKey","wrapKey","unwrapKey"].forEach(function(m) {
|
|
247
|
+
subtle[m] = subtleThrow(m);
|
|
248
|
+
});
|
|
249
|
+
globalThis.crypto = {
|
|
250
|
+
getRandomValues: getRandomValues,
|
|
251
|
+
randomUUID: randomUUID,
|
|
252
|
+
subtle: subtle,
|
|
253
|
+
};
|
|
254
|
+
})();
|
|
255
|
+
|
|
256
|
+
// ---- Loud Intl / locale guards ----
|
|
257
|
+
// QuickJS has no ICU: \`Intl\` is absent and toLocaleString-family methods
|
|
258
|
+
// silently ignore their locale argument. Silent divergence from the node
|
|
259
|
+
// engine would write different values into a durable event log with no
|
|
260
|
+
// error anywhere — so make the gap loud instead: Intl constructors throw,
|
|
261
|
+
// and toLocale* methods throw ONLY when called with an explicit locale
|
|
262
|
+
// (the no-argument forms keep QuickJS's default behavior).
|
|
263
|
+
(function() {
|
|
264
|
+
function intlThrow(name) {
|
|
265
|
+
return function() {
|
|
266
|
+
var err = new Error("\`Intl." + name + "\` is not available in the QuickJS workflow engine (no ICU). Perform locale-sensitive formatting in a step function, or use WORKFLOW_VM=node.");
|
|
267
|
+
err.name = "WorkflowRuntimeError";
|
|
268
|
+
throw err;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
if (typeof Intl === "undefined") {
|
|
272
|
+
var intl = {};
|
|
273
|
+
["Collator","DateTimeFormat","DisplayNames","DurationFormat","ListFormat","Locale","NumberFormat","PluralRules","RelativeTimeFormat","Segmenter"].forEach(function(n) {
|
|
274
|
+
intl[n] = intlThrow(n);
|
|
275
|
+
});
|
|
276
|
+
intl.getCanonicalLocales = intlThrow("getCanonicalLocales");
|
|
277
|
+
globalThis.Intl = intl;
|
|
278
|
+
}
|
|
279
|
+
function guardLocale(proto, method) {
|
|
280
|
+
var original = proto[method];
|
|
281
|
+
if (typeof original !== "function") return;
|
|
282
|
+
proto[method] = function(locales) {
|
|
283
|
+
if (locales !== undefined) {
|
|
284
|
+
var err = new Error("\`" + method + "(locales, ...)\` with an explicit locale is not supported in the QuickJS workflow engine (no ICU) — it would silently ignore the locale. Format in a step function, or call without arguments for the engine default.");
|
|
285
|
+
err.name = "WorkflowRuntimeError";
|
|
286
|
+
throw err;
|
|
287
|
+
}
|
|
288
|
+
return original.call(this);
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
guardLocale(Number.prototype, "toLocaleString");
|
|
292
|
+
guardLocale(Date.prototype, "toLocaleString");
|
|
293
|
+
guardLocale(Date.prototype, "toLocaleDateString");
|
|
294
|
+
guardLocale(Date.prototype, "toLocaleTimeString");
|
|
295
|
+
guardLocale(String.prototype, "toLocaleLowerCase");
|
|
296
|
+
guardLocale(String.prototype, "toLocaleUpperCase");
|
|
297
|
+
// localeCompare's locales argument is the SECOND parameter.
|
|
298
|
+
(function() {
|
|
299
|
+
var original = String.prototype.localeCompare;
|
|
300
|
+
String.prototype.localeCompare = function(that, locales) {
|
|
301
|
+
if (locales !== undefined) {
|
|
302
|
+
var err = new Error("\`localeCompare(that, locales, ...)\` with an explicit locale is not supported in the QuickJS workflow engine (no ICU). Compare in a step function, or call without a locale.");
|
|
303
|
+
err.name = "WorkflowRuntimeError";
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
return original.call(this, that);
|
|
307
|
+
};
|
|
308
|
+
})();
|
|
309
|
+
})();
|
|
310
|
+
|
|
311
|
+
globalThis[Symbol.for("WORKFLOW_USE_STEP")] = function(stepId, closureVarsFn) {
|
|
312
|
+
var fn = function() {
|
|
313
|
+
var args = Array.prototype.slice.call(arguments);
|
|
314
|
+
var correlationId = "step_" + globalThis.__generateUlid();
|
|
315
|
+
// Capture 'this' for method invocations (e.g., MyClass.method())
|
|
316
|
+
var thisVal = (this !== undefined && this !== null && this !== globalThis) ? this : undefined;
|
|
317
|
+
// Serialize step input using the host-provided devalue serializer.
|
|
318
|
+
// This produces a format-prefixed Uint8Array ("devl" + devalue.stringify).
|
|
319
|
+
var input = globalThis[Symbol.for("workflow-serialize")]({
|
|
320
|
+
args: args,
|
|
321
|
+
closureVars: closureVarsFn ? closureVarsFn() : undefined,
|
|
322
|
+
thisVal: thisVal,
|
|
323
|
+
});
|
|
324
|
+
globalThis.__pending.push({
|
|
325
|
+
type: "step",
|
|
326
|
+
correlationId: correlationId,
|
|
327
|
+
stepId: stepId,
|
|
328
|
+
input: input,
|
|
329
|
+
hasCreatedEvent: false,
|
|
330
|
+
});
|
|
331
|
+
return new Promise(function(resolve, reject) {
|
|
332
|
+
globalThis.__registerResolver(correlationId, resolve, reject);
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
// Set stepId on the proxy so the StepFunction reducer can detect and
|
|
336
|
+
// serialize step function references (e.g. when passed as arguments).
|
|
337
|
+
fn.stepId = stepId;
|
|
338
|
+
if (closureVarsFn) fn.__closureVarsFn = closureVarsFn;
|
|
339
|
+
// Override .bind so a bound step proxy (e.g. the SWC plugin's
|
|
340
|
+
// useStep(...).bind(this) for lexical-this arrow steps) keeps its
|
|
341
|
+
// stepId and records the bound receiver / prefilled args — the native
|
|
342
|
+
// bind drops own properties, which would make the StepFunction
|
|
343
|
+
// reducer fail to recognize the proxy when it crosses a serialization
|
|
344
|
+
// boundary. Mirrors the node:vm engine's override in step.ts.
|
|
345
|
+
fn.bind = function(thisArg) {
|
|
346
|
+
var partialArgs = Array.prototype.slice.call(arguments, 1);
|
|
347
|
+
var bound = Function.prototype.bind.apply(this, [thisArg].concat(partialArgs));
|
|
348
|
+
bound.stepId = stepId;
|
|
349
|
+
if (closureVarsFn) bound.__closureVarsFn = closureVarsFn;
|
|
350
|
+
bound.__boundThis = thisArg;
|
|
351
|
+
if (partialArgs.length > 0) bound.__boundArgs = partialArgs;
|
|
352
|
+
return bound;
|
|
353
|
+
};
|
|
354
|
+
return fn;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// Parses an "ms" library style duration string into milliseconds.
|
|
358
|
+
// Supports the same units as the replay runtime (which uses the "ms"
|
|
359
|
+
// package): ms / s / m / h / d / w / y, with verbose aliases
|
|
360
|
+
// (seconds, minutes, ...).
|
|
361
|
+
globalThis.__parseDurationMs = function(str) {
|
|
362
|
+
str = String(str);
|
|
363
|
+
if (str.length > 100) return undefined;
|
|
364
|
+
var match = str.match(
|
|
365
|
+
/^(-?(?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i
|
|
366
|
+
);
|
|
367
|
+
if (!match) return undefined;
|
|
368
|
+
var n = parseFloat(match[1]);
|
|
369
|
+
var type = (match[2] || "ms").toLowerCase();
|
|
370
|
+
var s = 1000, m = 60 * s, h = 60 * m, d = 24 * h, w = 7 * d, y = 365.25 * d;
|
|
371
|
+
switch (type) {
|
|
372
|
+
case "years": case "year": case "yrs": case "yr": case "y": return n * y;
|
|
373
|
+
case "weeks": case "week": case "w": return n * w;
|
|
374
|
+
case "days": case "day": case "d": return n * d;
|
|
375
|
+
case "hours": case "hour": case "hrs": case "hr": case "h": return n * h;
|
|
376
|
+
case "minutes": case "minute": case "mins": case "min": case "m": return n * m;
|
|
377
|
+
case "seconds": case "second": case "secs": case "sec": case "s": return n * s;
|
|
378
|
+
case "milliseconds": case "millisecond": case "msecs": case "msec": case "ms": return n;
|
|
379
|
+
default: return undefined;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
globalThis[Symbol.for("WORKFLOW_SLEEP")] = function(param) {
|
|
384
|
+
var correlationId = "wait_" + globalThis.__generateUlid();
|
|
385
|
+
var resumeAt;
|
|
386
|
+
if (typeof param === "number") {
|
|
387
|
+
resumeAt = new Date(Date.now() + param).toISOString();
|
|
388
|
+
} else if (typeof param === "string") {
|
|
389
|
+
var ms = globalThis.__parseDurationMs(param);
|
|
390
|
+
if (typeof ms === "number" && isFinite(ms)) {
|
|
391
|
+
resumeAt = new Date(Date.now() + ms).toISOString();
|
|
392
|
+
} else {
|
|
393
|
+
// Not a duration string — try as an absolute date string.
|
|
394
|
+
var date = new Date(param);
|
|
395
|
+
if (isNaN(date.getTime())) {
|
|
396
|
+
throw new Error("Invalid sleep parameter: " + param);
|
|
397
|
+
}
|
|
398
|
+
resumeAt = date.toISOString();
|
|
399
|
+
}
|
|
400
|
+
} else if (param instanceof Date) {
|
|
401
|
+
if (isNaN(param.getTime())) {
|
|
402
|
+
throw new Error("Invalid sleep parameter: " + param);
|
|
403
|
+
}
|
|
404
|
+
resumeAt = param.toISOString();
|
|
405
|
+
} else {
|
|
406
|
+
throw new Error("Invalid sleep parameter: " + param);
|
|
407
|
+
}
|
|
408
|
+
globalThis.__pending.push({
|
|
409
|
+
type: "wait",
|
|
410
|
+
correlationId: correlationId,
|
|
411
|
+
resumeAt: resumeAt,
|
|
412
|
+
hasCreatedEvent: false,
|
|
413
|
+
});
|
|
414
|
+
return new Promise(function(resolve, reject) {
|
|
415
|
+
globalThis.__registerResolver(correlationId, resolve, reject);
|
|
416
|
+
});
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Response/Request polyfills — .json()/.text()/.arrayBuffer() are useStep
|
|
420
|
+
// proxies that execute on the host side. The proxies are assigned directly
|
|
421
|
+
// to the prototypes so that 'this' (the Response/Request instance) is
|
|
422
|
+
// serialized as thisVal by WORKFLOW_USE_STEP, matching the event-replay
|
|
423
|
+
// runtime's approach (commit dcb0761).
|
|
424
|
+
if (typeof Response === "undefined") {
|
|
425
|
+
var __BODY_INIT = Symbol.for("BODY_INIT");
|
|
426
|
+
|
|
427
|
+
globalThis.Response = function(body, init) {
|
|
428
|
+
init = init || {};
|
|
429
|
+
this.status = init.status || 200;
|
|
430
|
+
this.statusText = init.statusText || "";
|
|
431
|
+
this.headers = new globalThis.Headers(init.headers || []);
|
|
432
|
+
this.type = "default";
|
|
433
|
+
this.url = "";
|
|
434
|
+
this.redirected = false;
|
|
435
|
+
if (body !== null && body !== undefined) {
|
|
436
|
+
this.body = Object.create(globalThis.ReadableStream.prototype);
|
|
437
|
+
this.body[__BODY_INIT] = body;
|
|
438
|
+
} else {
|
|
439
|
+
this.body = null;
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
Object.defineProperty(globalThis.Response.prototype, "ok", {
|
|
443
|
+
get: function() { return this.status >= 200 && this.status < 300; }
|
|
444
|
+
});
|
|
445
|
+
Object.defineProperty(globalThis.Response.prototype, "bodyUsed", {
|
|
446
|
+
get: function() { return false; }
|
|
447
|
+
});
|
|
448
|
+
// Assign useStep proxies directly — 'this' binding provides the
|
|
449
|
+
// Response instance, which gets serialized as thisVal by the proxy.
|
|
450
|
+
Object.defineProperties(globalThis.Response.prototype, {
|
|
451
|
+
arrayBuffer: { value: globalThis[Symbol.for("WORKFLOW_USE_STEP")]("__builtin_response_array_buffer"), writable: true, configurable: true },
|
|
452
|
+
json: { value: globalThis[Symbol.for("WORKFLOW_USE_STEP")]("__builtin_response_json"), writable: true, configurable: true },
|
|
453
|
+
text: { value: globalThis[Symbol.for("WORKFLOW_USE_STEP")]("__builtin_response_text"), writable: true, configurable: true },
|
|
454
|
+
});
|
|
455
|
+
globalThis.Response.prototype.bytes = function() {
|
|
456
|
+
return this.arrayBuffer().then(function(buf) { return new Uint8Array(buf); });
|
|
457
|
+
};
|
|
458
|
+
globalThis.Response.prototype.clone = function() {
|
|
459
|
+
var r = Object.create(globalThis.Response.prototype);
|
|
460
|
+
r.status = this.status; r.statusText = this.statusText;
|
|
461
|
+
r.headers = this.headers; r.type = this.type;
|
|
462
|
+
r.url = this.url; r.redirected = this.redirected; r.body = this.body;
|
|
463
|
+
return r;
|
|
464
|
+
};
|
|
465
|
+
globalThis.Response.json = function(data, init) {
|
|
466
|
+
var body = JSON.stringify(data);
|
|
467
|
+
var headers = new globalThis.Headers(init ? init.headers : []);
|
|
468
|
+
if (!headers.has("content-type")) { headers.set("content-type", "application/json"); }
|
|
469
|
+
return new globalThis.Response(body, { status: (init && init.status) || 200, statusText: (init && init.statusText) || "", headers: headers });
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (typeof Request === "undefined") {
|
|
473
|
+
globalThis.Request = function(input, init) {
|
|
474
|
+
init = init || {};
|
|
475
|
+
if (typeof input === "string") { this.url = input; }
|
|
476
|
+
else if (input && typeof input === "object") {
|
|
477
|
+
this.url = input.url || ""; this.method = input.method;
|
|
478
|
+
this.headers = input.headers; this.body = input.body;
|
|
479
|
+
}
|
|
480
|
+
if (init.method) this.method = init.method.toUpperCase();
|
|
481
|
+
if (!this.method) this.method = "GET";
|
|
482
|
+
if (init.headers) this.headers = new globalThis.Headers(init.headers);
|
|
483
|
+
if (!this.headers) this.headers = new globalThis.Headers();
|
|
484
|
+
if (init.body !== undefined) this.body = init.body;
|
|
485
|
+
if (!this.body) this.body = null;
|
|
486
|
+
this.duplex = init.duplex || "half";
|
|
487
|
+
};
|
|
488
|
+
Object.defineProperty(globalThis.Request.prototype, "bodyUsed", {
|
|
489
|
+
get: function() { return false; }
|
|
490
|
+
});
|
|
491
|
+
Object.defineProperties(globalThis.Request.prototype, {
|
|
492
|
+
arrayBuffer: { value: globalThis[Symbol.for("WORKFLOW_USE_STEP")]("__builtin_response_array_buffer"), writable: true, configurable: true },
|
|
493
|
+
json: { value: globalThis[Symbol.for("WORKFLOW_USE_STEP")]("__builtin_response_json"), writable: true, configurable: true },
|
|
494
|
+
text: { value: globalThis[Symbol.for("WORKFLOW_USE_STEP")]("__builtin_response_text"), writable: true, configurable: true },
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// createHook — returns a Hook object that is both a Thenable and AsyncIterable.
|
|
499
|
+
// Each await/yield creates a new promise keyed by the same correlationId.
|
|
500
|
+
// The promise is resolved when a hook_received event arrives.
|
|
501
|
+
globalThis[Symbol.for("WORKFLOW_CREATE_HOOK")] = function(options) {
|
|
502
|
+
options = options || {};
|
|
503
|
+
if (options.isWebhook === true && options.experimental_minRetention !== undefined) {
|
|
504
|
+
throw new Error('Webhook hooks do not support \`experimental_minRetention\`. Use a non-webhook \`createHook()\` with \`resumeHook()\`.');
|
|
505
|
+
}
|
|
506
|
+
if (options.experimental_minRetention !== undefined && globalThis.__worldCapabilities?.hookRetention?.active !== true) {
|
|
507
|
+
var unsupportedRetentionError = new Error('The configured World does not support \`experimental_minRetention\` for Hooks.');
|
|
508
|
+
unsupportedRetentionError.name = "FatalError";
|
|
509
|
+
unsupportedRetentionError.fatal = true;
|
|
510
|
+
throw unsupportedRetentionError;
|
|
511
|
+
}
|
|
512
|
+
var token = options.token || globalThis.__generateNanoid();
|
|
513
|
+
var correlationId = "hook_" + globalThis.__generateUlid();
|
|
514
|
+
var isDisposed = false;
|
|
515
|
+
var hasCreatedEvent = false;
|
|
516
|
+
var tokenRetentionUntil;
|
|
517
|
+
if (options.experimental_minRetention !== undefined) {
|
|
518
|
+
var minRetention = options.experimental_minRetention;
|
|
519
|
+
if (typeof minRetention === "number") {
|
|
520
|
+
if (minRetention < 0 || !isFinite(minRetention)) {
|
|
521
|
+
throw new Error("Invalid duration: " + minRetention + ". Expected a non-negative finite number of milliseconds.");
|
|
522
|
+
}
|
|
523
|
+
tokenRetentionUntil = Date.now() + minRetention;
|
|
524
|
+
} else if (typeof minRetention === "string") {
|
|
525
|
+
var retentionMs = globalThis.__parseDurationMs(minRetention);
|
|
526
|
+
if (typeof retentionMs !== "number" || retentionMs < 0 || !isFinite(retentionMs)) {
|
|
527
|
+
throw new Error('Invalid duration: "' + minRetention + '". Expected a valid duration string like "1s", "1m", "1h", etc.');
|
|
528
|
+
}
|
|
529
|
+
tokenRetentionUntil = Date.now() + retentionMs;
|
|
530
|
+
} else if (minRetention instanceof Date || (minRetention && typeof minRetention.getTime === "function")) {
|
|
531
|
+
// Accept Date-like objects (anything with getTime), matching
|
|
532
|
+
// parseDurationToDate: values that crossed the serde boundary may
|
|
533
|
+
// not be realm-native Date instances.
|
|
534
|
+
tokenRetentionUntil = minRetention.getTime();
|
|
535
|
+
} else {
|
|
536
|
+
throw new Error("Invalid duration parameter. Expected a duration string, number (milliseconds), or Date object.");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Register in pending operations.
|
|
541
|
+
// Serialize metadata inside the VM so Response/Request objects are
|
|
542
|
+
// properly handled by the devalue reducers before crossing the boundary.
|
|
543
|
+
var pendingOp = {
|
|
544
|
+
type: "hook",
|
|
545
|
+
correlationId: correlationId,
|
|
546
|
+
token: token,
|
|
547
|
+
tokenRetentionUntil: tokenRetentionUntil,
|
|
548
|
+
isWebhook: !!options.isWebhook,
|
|
549
|
+
metadata: options.metadata ? globalThis[Symbol.for("workflow-serialize")](options.metadata) : undefined,
|
|
550
|
+
hasCreatedEvent: false,
|
|
551
|
+
};
|
|
552
|
+
globalThis.__pending.push(pendingOp);
|
|
553
|
+
|
|
554
|
+
// Per-hook lifecycle state backing hook.getConflict(): resolves null
|
|
555
|
+
// once creation is confirmed (hook_created), or resolves with the
|
|
556
|
+
// conflicting Run handle / rejects with HookConflictError on
|
|
557
|
+
// hook_conflict. State transitions are driven by the host during event
|
|
558
|
+
// processing (see processEvents).
|
|
559
|
+
globalThis.__hooks = globalThis.__hooks || {};
|
|
560
|
+
globalThis.__hooks[correlationId] = {
|
|
561
|
+
token: token,
|
|
562
|
+
created: false,
|
|
563
|
+
conflict: null,
|
|
564
|
+
getConflictResolvers: [],
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// Each await creates a new promise for the next payload.
|
|
568
|
+
// The correlationId stays the same — the resolver is replaced each time.
|
|
569
|
+
function createHookPromise() {
|
|
570
|
+
// Check the payload buffer first — if a hook_received event arrived
|
|
571
|
+
// before this hook was awaited, the payload was buffered in the VM
|
|
572
|
+
// heap. Drain it immediately (matching event-replay payloadsQueue).
|
|
573
|
+
var buf = globalThis.__hookPayloadBuffer[correlationId];
|
|
574
|
+
if (buf && buf.length > 0) {
|
|
575
|
+
return Promise.resolve(buf.shift());
|
|
576
|
+
}
|
|
577
|
+
return new Promise(function(resolve, reject) {
|
|
578
|
+
globalThis.__resolvers[correlationId] = { resolve: resolve, reject: reject };
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function disposeHook() {
|
|
583
|
+
if (isDisposed) return;
|
|
584
|
+
isDisposed = true;
|
|
585
|
+
// A conflicted hook was never created (the world rejected its claim
|
|
586
|
+
// — the token belongs to another run), so there is no entity to
|
|
587
|
+
// dispose. Mirrors the node:vm engine, where hook_conflict removes
|
|
588
|
+
// the invocation-queue item before dispose can mark it. Emitting a
|
|
589
|
+
// hook_disposed here would be rejected by the world's
|
|
590
|
+
// hook-existence validation.
|
|
591
|
+
var state = globalThis.__hooks[correlationId];
|
|
592
|
+
if (!state || !state.conflict) {
|
|
593
|
+
// Signal to the entrypoint to create a hook_disposed event. The
|
|
594
|
+
// token is carried so the entrypoint can order same-token hook
|
|
595
|
+
// operations sequentially (a dispose must release the token before
|
|
596
|
+
// a later same-token hook's creation is validated).
|
|
597
|
+
globalThis.__pending.push({
|
|
598
|
+
type: "hook_dispose",
|
|
599
|
+
correlationId: correlationId,
|
|
600
|
+
token: token,
|
|
601
|
+
hasCreatedEvent: false,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
// If there's a pending resolver, resolve it with undefined to break the iterator
|
|
605
|
+
if (globalThis.__resolvers[correlationId]) {
|
|
606
|
+
globalThis.__resolvers[correlationId].resolve(undefined);
|
|
607
|
+
delete globalThis.__resolvers[correlationId];
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function getConflict() {
|
|
612
|
+
var state = globalThis.__hooks[correlationId];
|
|
613
|
+
if (state.conflict) {
|
|
614
|
+
return state.conflict.run
|
|
615
|
+
? Promise.resolve(state.conflict.run)
|
|
616
|
+
: Promise.reject(state.conflict.error);
|
|
617
|
+
}
|
|
618
|
+
if (state.created) {
|
|
619
|
+
return Promise.resolve(null);
|
|
620
|
+
}
|
|
621
|
+
// Creation not yet confirmed by the event log — park the awaiter and
|
|
622
|
+
// flag the pending op so the entrypoint re-invokes the workflow right
|
|
623
|
+
// after writing hook_created (nothing external resumes a getConflict
|
|
624
|
+
// awaiter; confirmation only comes from replaying the new event).
|
|
625
|
+
pendingOp.hasGetConflictAwaiter = true;
|
|
626
|
+
return new Promise(function(resolve, reject) {
|
|
627
|
+
state.getConflictResolvers.push({ resolve: resolve, reject: reject });
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
var hook = {
|
|
632
|
+
token: token,
|
|
633
|
+
then: function(onFulfilled, onRejected) {
|
|
634
|
+
return createHookPromise().then(onFulfilled, onRejected);
|
|
635
|
+
},
|
|
636
|
+
getConflict: getConflict,
|
|
637
|
+
dispose: disposeHook,
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// Symbol.dispose for explicit resource management
|
|
641
|
+
hook[Symbol.dispose] = disposeHook;
|
|
642
|
+
|
|
643
|
+
// AsyncIterable — yields payloads until disposed
|
|
644
|
+
hook[Symbol.asyncIterator] = function() {
|
|
645
|
+
return {
|
|
646
|
+
next: function() {
|
|
647
|
+
if (isDisposed) {
|
|
648
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
649
|
+
}
|
|
650
|
+
return createHookPromise().then(function(value) {
|
|
651
|
+
// If disposed while waiting, signal done
|
|
652
|
+
if (isDisposed) return { done: true, value: undefined };
|
|
653
|
+
return { done: false, value: value };
|
|
654
|
+
});
|
|
655
|
+
},
|
|
656
|
+
return: function() {
|
|
657
|
+
disposeHook();
|
|
658
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
659
|
+
},
|
|
660
|
+
};
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
return hook;
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
// setAttributes — attaches plaintext metadata to the current run.
|
|
667
|
+
// Validation happens in library code (normalizeAttributeChanges) before
|
|
668
|
+
// this dispatcher is invoked, so "changes" is already normalized. The
|
|
669
|
+
// returned promise resolves when the matching attr_set event is
|
|
670
|
+
// observed during event processing — mirroring the node:vm engine's
|
|
671
|
+
// createSetAttributes (attribute-dispatcher.ts).
|
|
672
|
+
globalThis[Symbol.for("WORKFLOW_SET_ATTRIBUTES")] = function(changes, options) {
|
|
673
|
+
var correlationId = "attr_" + globalThis.__generateUlid();
|
|
674
|
+
globalThis.__pending.push({
|
|
675
|
+
type: "attribute",
|
|
676
|
+
correlationId: correlationId,
|
|
677
|
+
changes: changes,
|
|
678
|
+
allowReservedAttributes: !!(options && options.allowReservedAttributes),
|
|
679
|
+
hasCreatedEvent: false,
|
|
680
|
+
});
|
|
681
|
+
return new Promise(function(resolve, reject) {
|
|
682
|
+
globalThis.__registerResolver(correlationId, resolve, reject);
|
|
683
|
+
});
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// ---- AbortController / AbortSignal (hook-backed) ----
|
|
687
|
+
// Port of workflow/abort-controller.ts to the VM pending-op model:
|
|
688
|
+
// the controller registers a system hook; abort() flips the signal
|
|
689
|
+
// synchronously and marks the pending op so the host records the abort
|
|
690
|
+
// (hook_received event + stream packet). On replay, the recorded
|
|
691
|
+
// hook_received event calls _setAborted during event processing and the
|
|
692
|
+
// workflow's own abort() call becomes a no-op.
|
|
693
|
+
var __ABORT_STREAM_NAME = Symbol.for("WORKFLOW_ABORT_STREAM_NAME");
|
|
694
|
+
var __ABORT_HOOK_TOKEN = Symbol.for("WORKFLOW_ABORT_HOOK_TOKEN");
|
|
695
|
+
|
|
696
|
+
function __makeAbortError() {
|
|
697
|
+
if (typeof DOMException !== "undefined") {
|
|
698
|
+
return new DOMException("The operation was aborted.", "AbortError");
|
|
699
|
+
}
|
|
700
|
+
var e = new Error("The operation was aborted.");
|
|
701
|
+
e.name = "AbortError";
|
|
702
|
+
return e;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function WorkflowAbortSignal(streamName, hookToken) {
|
|
706
|
+
this.aborted = false;
|
|
707
|
+
this.reason = undefined;
|
|
708
|
+
this[__ABORT_STREAM_NAME] = streamName;
|
|
709
|
+
this[__ABORT_HOOK_TOKEN] = hookToken;
|
|
710
|
+
this.__listeners = [];
|
|
711
|
+
this.__onabort = null;
|
|
712
|
+
}
|
|
713
|
+
Object.defineProperty(WorkflowAbortSignal.prototype, "onabort", {
|
|
714
|
+
get: function() { return this.__onabort; },
|
|
715
|
+
set: function(handler) {
|
|
716
|
+
this.__onabort = handler;
|
|
717
|
+
if (handler && this.aborted) handler.call(this);
|
|
718
|
+
},
|
|
719
|
+
});
|
|
720
|
+
WorkflowAbortSignal.prototype._setAborted = function(reason) {
|
|
721
|
+
if (this.aborted) return;
|
|
722
|
+
this.aborted = true;
|
|
723
|
+
this.reason = reason;
|
|
724
|
+
if (this.__onabort) this.__onabort.call(this);
|
|
725
|
+
var listeners = this.__listeners;
|
|
726
|
+
this.__listeners = [];
|
|
727
|
+
for (var i = 0; i < listeners.length; i++) listeners[i]();
|
|
728
|
+
};
|
|
729
|
+
WorkflowAbortSignal.prototype.addEventListener = function(type, listener) {
|
|
730
|
+
if (type !== "abort") return;
|
|
731
|
+
if (this.aborted) {
|
|
732
|
+
// Fire synchronously (not on a microtask) for deterministic replay —
|
|
733
|
+
// matches the node:vm engine's WorkflowAbortSignal.
|
|
734
|
+
listener();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
this.__listeners.push(listener);
|
|
738
|
+
};
|
|
739
|
+
WorkflowAbortSignal.prototype.removeEventListener = function(type, listener) {
|
|
740
|
+
if (type !== "abort") return;
|
|
741
|
+
this.__listeners = this.__listeners.filter(function(l) { return l !== listener; });
|
|
742
|
+
};
|
|
743
|
+
WorkflowAbortSignal.prototype.throwIfAborted = function() {
|
|
744
|
+
if (this.aborted) {
|
|
745
|
+
throw this.reason !== undefined && this.reason !== null
|
|
746
|
+
? this.reason
|
|
747
|
+
: __makeAbortError();
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
// Expose for the serde bundle's revivers (evaluated before this bootstrap;
|
|
751
|
+
// they look the class up lazily at revive time).
|
|
752
|
+
globalThis.__WorkflowAbortSignal = WorkflowAbortSignal;
|
|
753
|
+
|
|
754
|
+
// Registry of live abort signals keyed by their hook correlationId. The
|
|
755
|
+
// host delivers hook_received events for these ids as _setAborted calls.
|
|
756
|
+
globalThis.__abortSignals = {};
|
|
757
|
+
|
|
758
|
+
globalThis.AbortController = function WorkflowAbortController() {
|
|
759
|
+
var id = globalThis.__generateUlid();
|
|
760
|
+
var streamName = "strm_" + id + "_system_abort";
|
|
761
|
+
var hookToken = "abrt_" + id;
|
|
762
|
+
this[__ABORT_STREAM_NAME] = streamName;
|
|
763
|
+
this[__ABORT_HOOK_TOKEN] = hookToken;
|
|
764
|
+
this.signal = new WorkflowAbortSignal(streamName, hookToken);
|
|
765
|
+
var correlationId = "hook_" + globalThis.__generateUlid();
|
|
766
|
+
// Register an internal system hook. isSystem prevents token namespace
|
|
767
|
+
// conflicts with user hooks.
|
|
768
|
+
globalThis.__pending.push({
|
|
769
|
+
type: "hook",
|
|
770
|
+
correlationId: correlationId,
|
|
771
|
+
token: hookToken,
|
|
772
|
+
isWebhook: false,
|
|
773
|
+
isSystem: true,
|
|
774
|
+
hasCreatedEvent: false,
|
|
775
|
+
});
|
|
776
|
+
globalThis.__abortSignals[correlationId] = this.signal;
|
|
777
|
+
};
|
|
778
|
+
globalThis.AbortController.prototype.abort = function(reason) {
|
|
779
|
+
if (this.signal.aborted) return; // already aborted (e.g. from replay)
|
|
780
|
+
this.signal._setAborted(reason);
|
|
781
|
+
// Mark the pending hook op so the host records the abort. The payload
|
|
782
|
+
// is serialized in the VM so the reason crosses the boundary with
|
|
783
|
+
// type fidelity (Errors, DOMException, custom values).
|
|
784
|
+
var token = this[__ABORT_HOOK_TOKEN];
|
|
785
|
+
for (var i = 0; i < globalThis.__pending.length; i++) {
|
|
786
|
+
var item = globalThis.__pending[i];
|
|
787
|
+
if (item.type === "hook" && item.token === token) {
|
|
788
|
+
item.abortRequested = true;
|
|
789
|
+
item.abortPayload = globalThis[Symbol.for("workflow-serialize")]({
|
|
790
|
+
aborted: true,
|
|
791
|
+
reason: reason,
|
|
792
|
+
});
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
globalThis.AbortSignal = {
|
|
799
|
+
abort: function(reason) {
|
|
800
|
+
var s = new WorkflowAbortSignal("", "");
|
|
801
|
+
s._setAborted(reason !== undefined ? reason : __makeAbortError());
|
|
802
|
+
return s;
|
|
803
|
+
},
|
|
804
|
+
any: function(signals) {
|
|
805
|
+
var composite = new WorkflowAbortSignal("", "");
|
|
806
|
+
var arr = Array.from(signals);
|
|
807
|
+
for (var i = 0; i < arr.length; i++) {
|
|
808
|
+
if (arr[i].aborted) {
|
|
809
|
+
composite._setAborted(arr[i].reason);
|
|
810
|
+
return composite;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
var listeners = [];
|
|
814
|
+
var cleanup = function() {
|
|
815
|
+
for (var j = 0; j < listeners.length; j++) {
|
|
816
|
+
if (listeners[j].signal.removeEventListener) {
|
|
817
|
+
listeners[j].signal.removeEventListener("abort", listeners[j].listener);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
listeners.length = 0;
|
|
821
|
+
};
|
|
822
|
+
arr.forEach(function(signal) {
|
|
823
|
+
if (!signal.addEventListener) return;
|
|
824
|
+
var listener = function() {
|
|
825
|
+
if (!composite.aborted) {
|
|
826
|
+
composite._setAborted(signal.reason);
|
|
827
|
+
cleanup();
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
listeners.push({ signal: signal, listener: listener });
|
|
831
|
+
signal.addEventListener("abort", listener);
|
|
832
|
+
});
|
|
833
|
+
return composite;
|
|
834
|
+
},
|
|
835
|
+
timeout: function() {
|
|
836
|
+
throw new Error(
|
|
837
|
+
"AbortSignal.timeout() is not supported in workflow functions. " +
|
|
838
|
+
"Use sleep() with an AbortController instead. " +
|
|
839
|
+
"See: /docs/errors/abort-signal-timeout-in-workflow"
|
|
840
|
+
);
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
// WORKFLOW_GET_STREAM_ID — generates a stream ID for a workflow run.
|
|
845
|
+
// Replicates getWorkflowRunStreamId() from util.ts inside the QuickJS VM.
|
|
846
|
+
// Uses the built-in btoa() for base64url encoding.
|
|
847
|
+
globalThis[Symbol.for("WORKFLOW_GET_STREAM_ID")] = function(namespace) {
|
|
848
|
+
var runId = globalThis[Symbol.for("WORKFLOW_CONTEXT")]
|
|
849
|
+
? globalThis[Symbol.for("WORKFLOW_CONTEXT")].workflowRunId
|
|
850
|
+
: "";
|
|
851
|
+
var streamId = runId.replace("wrun_", "strm_") + "_user";
|
|
852
|
+
if (!namespace) return streamId;
|
|
853
|
+
// base64url: btoa then replace + with -, / with _, strip =
|
|
854
|
+
var b64 = btoa(namespace).replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");
|
|
855
|
+
return streamId + "_" + b64;
|
|
856
|
+
};
|
|
857
|
+
`;
|
|
858
|
+
// ---- Runtime ----
|
|
859
|
+
/**
|
|
860
|
+
* Phase 1 — static (run-independent) VM initialization.
|
|
861
|
+
*
|
|
862
|
+
* Creates a QuickJS VM and loads everything that does not depend on a
|
|
863
|
+
* specific workflow run: the serde bundle (devalue-based serialization
|
|
864
|
+
* used at the host/VM boundary) and the workflow-primitive bootstrap
|
|
865
|
+
* (useStep / sleep / createHook / Response-Request polyfills).
|
|
866
|
+
*
|
|
867
|
+
* `getNowMs` backs the VM's WASI clock (`Date.now()` / `new Date()`
|
|
868
|
+
* inside the VM). The callback itself is static — the per-run state it
|
|
869
|
+
* reads lives on the host and is advanced as events are consumed,
|
|
870
|
+
* matching the node:vm engine's deterministic replay clock.
|
|
871
|
+
*
|
|
872
|
+
* This phase is the future boundary for VM-memory snapshotting: a
|
|
873
|
+
* build-time snapshot can capture the VM right after this function and
|
|
874
|
+
* new runs can restore from it instead of paying VM creation + eval cost
|
|
875
|
+
* (`QuickJS.restore` accepts the same wasi override).
|
|
876
|
+
*/
|
|
877
|
+
/**
|
|
878
|
+
* Loosely-typed accessor for the `WebAssembly` global. The package
|
|
879
|
+
* tsconfig's `lib: ["es2022"]` does not include the DOM lib where the
|
|
880
|
+
* `WebAssembly` namespace types live; the runtime global is available on
|
|
881
|
+
* every WASM-capable platform this engine targets.
|
|
882
|
+
*/
|
|
883
|
+
const WebAssemblyGlobal = globalThis.WebAssembly;
|
|
884
|
+
/**
|
|
885
|
+
* Process-wide cache of the compiled `WebAssembly.Module`s for the main
|
|
886
|
+
* QuickJS runtime and its native extensions. `WebAssembly.compile` of the
|
|
887
|
+
* ~600 KB runtime binary is the most expensive part of VM creation and is
|
|
888
|
+
* pure (no per-VM state — instantiation binds the per-VM memory), so it
|
|
889
|
+
* only needs to happen once per process. The promise is cached (not the
|
|
890
|
+
* result) so concurrent first invocations share a single compilation.
|
|
891
|
+
*/
|
|
892
|
+
let compiledAssetsPromise;
|
|
893
|
+
function getCompiledAssets() {
|
|
894
|
+
if (!compiledAssetsPromise) {
|
|
895
|
+
compiledAssetsPromise = (async () => {
|
|
896
|
+
const [wasm, ...extensionModules] = await Promise.all([
|
|
897
|
+
WebAssemblyGlobal.compile(quickjsWasm),
|
|
898
|
+
...quickjsExtensions.map((ext) => WebAssemblyGlobal.compile(ext.wasm)),
|
|
899
|
+
]);
|
|
900
|
+
return {
|
|
901
|
+
wasm,
|
|
902
|
+
extensions: quickjsExtensions.map((ext, i) => ({
|
|
903
|
+
...ext,
|
|
904
|
+
wasm: extensionModules[i],
|
|
905
|
+
})),
|
|
906
|
+
};
|
|
907
|
+
})();
|
|
908
|
+
// On failure, clear the cache so a later invocation can retry rather
|
|
909
|
+
// than being stuck with a rejected promise forever.
|
|
910
|
+
compiledAssetsPromise.catch(() => {
|
|
911
|
+
compiledAssetsPromise = undefined;
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
return compiledAssetsPromise;
|
|
915
|
+
}
|
|
916
|
+
async function initWorkflowVM(getNowMs, interruptBudget) {
|
|
917
|
+
// Deterministic replay clock: Date.now() / new Date() inside the VM
|
|
918
|
+
// read the host-controlled clock instead of wall time. Replay
|
|
919
|
+
// re-executes the workflow from the top on every invocation, so the
|
|
920
|
+
// clock must be derived from the event log (not real time) for the
|
|
921
|
+
// workflow to observe stable timestamps across invocations.
|
|
922
|
+
const wasi = (memory) => ({
|
|
923
|
+
clock_time_get(_clockId, _precision, resultPtr) {
|
|
924
|
+
const timeNs = BigInt(Math.round(getNowMs())) * 1000000n;
|
|
925
|
+
new DataView(memory.buffer).setBigUint64(resultPtr, timeNs, true);
|
|
926
|
+
return 0;
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
const assets = await getCompiledAssets();
|
|
930
|
+
const vm = await QuickJS.create({
|
|
931
|
+
wasm: assets.wasm,
|
|
932
|
+
memoryLimit: 256 * 1024 * 1024,
|
|
933
|
+
interruptHandler: createInterruptHandler(interruptBudget),
|
|
934
|
+
extensions: assets.extensions,
|
|
935
|
+
wasi,
|
|
936
|
+
});
|
|
937
|
+
// Evaluate the VM serde bundle
|
|
938
|
+
vm.evalCode(VM_SERDE_BUNDLE, 'vm-serde.js').dispose();
|
|
939
|
+
// Bootstrap workflow primitives
|
|
940
|
+
vm.evalCode(VM_BOOTSTRAP, 'bootstrap.js').dispose();
|
|
941
|
+
return vm;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Run a workflow invocation to its first settled state and dispose the
|
|
945
|
+
* VM. Convenience wrapper over {@link startQuickJSWorkflow} for callers
|
|
946
|
+
* (and tests) that don't use live-VM continuation.
|
|
947
|
+
*/
|
|
948
|
+
export async function runQuickJSWorkflow(options) {
|
|
949
|
+
const session = await startQuickJSWorkflow(options);
|
|
950
|
+
session.dispose();
|
|
951
|
+
return session.result;
|
|
952
|
+
}
|
|
953
|
+
export async function startQuickJSWorkflow(options) {
|
|
954
|
+
const { workflowCode, workflowId, workflowRun, events } = options;
|
|
955
|
+
const startedAt = workflowRun.startedAt ? +workflowRun.startedAt : Date.now();
|
|
956
|
+
// Deterministic PRNG seed — identical for EVERY invocation of the same
|
|
957
|
+
// run. Full event replay requires this: each invocation re-executes the
|
|
958
|
+
// workflow from the top and must regenerate the exact same correlationId
|
|
959
|
+
// sequence so that pending operations re-created by replay match the
|
|
960
|
+
// events recorded by earlier invocations. Sequential operations within
|
|
961
|
+
// one execution still get distinct ids because the PRNG advances as the
|
|
962
|
+
// workflow draws from it. Identical seeding across CONCURRENT invocations
|
|
963
|
+
// of the same run is also load-bearing: both produce the same ids, and
|
|
964
|
+
// the world's per-(runId, correlationId) uniqueness turns the duplicate
|
|
965
|
+
// `events.create` into an EntityConflictError that the entrypoint
|
|
966
|
+
// swallows.
|
|
967
|
+
//
|
|
968
|
+
// The seed inputs MUST be stable across invocations. Notably
|
|
969
|
+
// `startedAt` is NOT: under turbo the first invocation runs against a
|
|
970
|
+
// synthesized run object whose timestamps differ from the durably
|
|
971
|
+
// stored ones that later invocations load. Matches the node:vm
|
|
972
|
+
// engine's seed (workflow.ts).
|
|
973
|
+
const seed = [
|
|
974
|
+
workflowRun.runId,
|
|
975
|
+
workflowRun.workflowName,
|
|
976
|
+
workflowRun.deploymentId,
|
|
977
|
+
].join(':');
|
|
978
|
+
const rng = seedrandom(seed);
|
|
979
|
+
// Seeded nanoid generator — uses the same nanoid package and seeded PRNG
|
|
980
|
+
// as the node:vm engine for consistent token generation.
|
|
981
|
+
const generateNanoid = nanoid.customRandom(nanoid.urlAlphabet, 21, (size) => new Uint8Array(size).map(() => 256 * rng()));
|
|
982
|
+
// Deterministic replay clock, mirroring the node:vm engine (see
|
|
983
|
+
// workflow.ts): the initial value is the run's creation time recovered
|
|
984
|
+
// from the ULID embedded in `runId` (falling back to `createdAt`), and
|
|
985
|
+
// it advances to each processed event's `createdAt` as the event log is
|
|
986
|
+
// replayed. Monotonic (Math.max) so the outer processEvents re-scan
|
|
987
|
+
// loop can't move the clock backwards mid-execution.
|
|
988
|
+
let vmNowMs = runIdCreatedAt(workflowRun.runId) ?? (+workflowRun.createdAt || startedAt);
|
|
989
|
+
const advanceClock = (ms) => {
|
|
990
|
+
if (Number.isFinite(ms))
|
|
991
|
+
vmNowMs = Math.max(vmNowMs, ms);
|
|
992
|
+
};
|
|
993
|
+
// ---- Phase 1: static initialization ----
|
|
994
|
+
const interruptBudget = { start: Date.now() };
|
|
995
|
+
const vm = await initWorkflowVM(() => vmNowMs, interruptBudget);
|
|
996
|
+
// Any throw between here and the terminal paths (which dispose the VM
|
|
997
|
+
// inside checkWorkflowState / extractError before RETURNING) would leak
|
|
998
|
+
// a live QuickJS instance and its WASM linear memory for the lifetime
|
|
999
|
+
// of the compute instance — which is reused. Dispose on the way out of
|
|
1000
|
+
// an exceptional exit and rethrow.
|
|
1001
|
+
try {
|
|
1002
|
+
return await runWorkflowInVM();
|
|
1003
|
+
}
|
|
1004
|
+
catch (err) {
|
|
1005
|
+
try {
|
|
1006
|
+
vm.dispose();
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
// Already disposed by a terminal path — ignore.
|
|
1010
|
+
}
|
|
1011
|
+
throw err;
|
|
1012
|
+
}
|
|
1013
|
+
// ---- Phase 2: per-run initialization ----
|
|
1014
|
+
async function runWorkflowInVM() {
|
|
1015
|
+
vm.evalCode(`globalThis.__worldCapabilities = ${JSON.stringify(options.worldCapabilities)};`).dispose();
|
|
1016
|
+
// Seeded Math.random
|
|
1017
|
+
{
|
|
1018
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
1019
|
+
try {
|
|
1020
|
+
const randomFn = __addDisposableResource(env_1, vm.newFunction('random', () => vm.newNumber(rng())), false);
|
|
1021
|
+
const math = __addDisposableResource(env_1, vm.global.getProp('Math'), false);
|
|
1022
|
+
math.setProp('random', randomFn);
|
|
1023
|
+
}
|
|
1024
|
+
catch (e_1) {
|
|
1025
|
+
env_1.error = e_1;
|
|
1026
|
+
env_1.hasError = true;
|
|
1027
|
+
}
|
|
1028
|
+
finally {
|
|
1029
|
+
__disposeResources(env_1);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
// Seeded nanoid generator
|
|
1033
|
+
{
|
|
1034
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
1035
|
+
try {
|
|
1036
|
+
const nanoidFn = __addDisposableResource(env_2, vm.newFunction('__generateNanoid', () => vm.newString(generateNanoid())), false);
|
|
1037
|
+
vm.setProp(vm.global, '__generateNanoid', nanoidFn);
|
|
1038
|
+
}
|
|
1039
|
+
catch (e_2) {
|
|
1040
|
+
env_2.error = e_2;
|
|
1041
|
+
env_2.hasError = true;
|
|
1042
|
+
}
|
|
1043
|
+
finally {
|
|
1044
|
+
__disposeResources(env_2);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
// Inject a deterministic timestamp for the VM's ULID factory. ULIDs
|
|
1048
|
+
// produced inside the VM use this as their time prefix instead of
|
|
1049
|
+
// Date.now(), so two concurrent workflow invocations of the same run
|
|
1050
|
+
// produce IDENTICAL correlationIds (the random portion also matches
|
|
1051
|
+
// because the PRNG is seeded the same way) and the world's
|
|
1052
|
+
// EntityConflictError on `events.create` dedups one of each pair.
|
|
1053
|
+
// Derived from the runId's embedded ULID (stable across invocations by
|
|
1054
|
+
// construction — unlike `startedAt`, which differs between turbo's
|
|
1055
|
+
// synthesized run object and the durably stored run).
|
|
1056
|
+
vm.evalCode(`globalThis.__ulidTimestamp = ${runIdCreatedAt(workflowRun.runId) ?? (+workflowRun.createdAt || startedAt)};`).dispose();
|
|
1057
|
+
// `process.env` — parity with the node:vm engine, which exposes a frozen
|
|
1058
|
+
// copy of the host env (vm/index.ts). Injected per run so the snapshot of
|
|
1059
|
+
// the env is taken at invocation time, same as node.
|
|
1060
|
+
{
|
|
1061
|
+
const envHandle = vm.newString(JSON.stringify(process.env));
|
|
1062
|
+
vm.setProp(vm.global, '__wdk_env', envHandle);
|
|
1063
|
+
envHandle.dispose();
|
|
1064
|
+
vm.evalCode('globalThis.process = { env: Object.freeze(JSON.parse(globalThis.__wdk_env)) };' +
|
|
1065
|
+
'delete globalThis.__wdk_env;').dispose();
|
|
1066
|
+
}
|
|
1067
|
+
// Execute the workflow bundle — use the workflowId as the eval filename
|
|
1068
|
+
// so QuickJS stack traces reference the workflow name, enabling source map
|
|
1069
|
+
// remapping by remapErrorStack (which matches frames by filename).
|
|
1070
|
+
// Evaluated in the per-run phase (after Math.random seeding) so that
|
|
1071
|
+
// module-scope user code draws from the seeded PRNG, matching the
|
|
1072
|
+
// node:vm engine's replay determinism.
|
|
1073
|
+
try {
|
|
1074
|
+
vm.evalCode(workflowCode, workflowId || 'workflow.js').dispose();
|
|
1075
|
+
}
|
|
1076
|
+
catch (err) {
|
|
1077
|
+
return makeSettledSession(extractError(vm, err, 'Workflow evaluation failed'));
|
|
1078
|
+
}
|
|
1079
|
+
// Extract workflow arguments. Prefer the run_created event; fall back
|
|
1080
|
+
// to the queue message's runInput if the event log is incomplete
|
|
1081
|
+
// (eventually-consistent read after start()). Failing to find input
|
|
1082
|
+
// for a first invocation is fatal — running the workflow function
|
|
1083
|
+
// with no args would silently turn typed arguments into `undefined`
|
|
1084
|
+
// and, for recursive workflows, produce exponential fan-out.
|
|
1085
|
+
const runCreatedEvent = events.find((e) => e.eventType === 'run_created');
|
|
1086
|
+
const runCreatedInput = runCreatedEvent && 'eventData' in runCreatedEvent
|
|
1087
|
+
? runCreatedEvent.eventData?.input
|
|
1088
|
+
: undefined;
|
|
1089
|
+
const runInput = runCreatedInput ?? options.runInput?.input;
|
|
1090
|
+
if (runInput instanceof Uint8Array) {
|
|
1091
|
+
const decryptedInput = await prepareBytesForVM(runInput, options.encryptionKey);
|
|
1092
|
+
runtimeLogger.debug('QuickJS runtime: run input format', {
|
|
1093
|
+
prefix: new TextDecoder().decode(decryptedInput.subarray(0, 4)),
|
|
1094
|
+
byteLength: decryptedInput.byteLength,
|
|
1095
|
+
source: runCreatedInput ? 'run_created' : 'queueMessage.runInput',
|
|
1096
|
+
});
|
|
1097
|
+
const inputHandle = vm.newUint8Array(decryptedInput);
|
|
1098
|
+
vm.setProp(vm.global, '__wdk_input', inputHandle);
|
|
1099
|
+
inputHandle.dispose();
|
|
1100
|
+
}
|
|
1101
|
+
else if (runInput === undefined && events.length > 0) {
|
|
1102
|
+
// The event log is non-empty (we got run_started or similar) but
|
|
1103
|
+
// no run_created event was found and no queue-provided runInput is
|
|
1104
|
+
// available. This is the race condition observed during the fib
|
|
1105
|
+
// incident — silently dropping arguments would turn `n` into
|
|
1106
|
+
// `undefined` and, for recursive workflows, cause exponential
|
|
1107
|
+
// fan-out. Fail loud: the throw escapes the entrypoint into the
|
|
1108
|
+
// replay loop's catch in runtime.ts (the QuickJS dispatch runs
|
|
1109
|
+
// inside that loop's try), which records run_failed. A visible
|
|
1110
|
+
// terminal failure is
|
|
1111
|
+
// preferred over silently executing with undefined arguments — the
|
|
1112
|
+
// queue-provided runInput fallback above makes this path rare.
|
|
1113
|
+
// Empty `events` is allowed because tests that bootstrap a workflow
|
|
1114
|
+
// with no arguments rely on the old permissive behavior.
|
|
1115
|
+
throw new Error(`Cannot start workflow run "${workflowRun.runId}": no run_created event found and no runInput in the queue payload, but other events are present (likely a read-after-write race during start()).`);
|
|
1116
|
+
}
|
|
1117
|
+
// Set workflow context metadata (for getWorkflowMetadata()).
|
|
1118
|
+
// Must match the shape that the node:vm engine produces (see
|
|
1119
|
+
// packages/core/src/workflow.ts: runWorkflow → ctx) so user code
|
|
1120
|
+
// that compares `getWorkflowMetadata()` values between a step
|
|
1121
|
+
// (server-side) and the workflow (VM-side) sees identical objects.
|
|
1122
|
+
{
|
|
1123
|
+
const metadata = {
|
|
1124
|
+
workflowName: workflowRun.workflowName,
|
|
1125
|
+
workflowRunId: workflowRun.runId,
|
|
1126
|
+
workflowStartedAt: workflowRun.startedAt
|
|
1127
|
+
? new Date(+workflowRun.startedAt)
|
|
1128
|
+
: new Date(),
|
|
1129
|
+
url: process.env.VERCEL_URL
|
|
1130
|
+
? `https://${process.env.VERCEL_URL}`
|
|
1131
|
+
: `http://localhost:${options.port ?? 3000}`,
|
|
1132
|
+
features: { encryption: !!options.encryptionKey },
|
|
1133
|
+
};
|
|
1134
|
+
vm.evalCode(`globalThis[Symbol.for("WORKFLOW_CONTEXT")] = ${JSON.stringify(metadata)};` +
|
|
1135
|
+
`globalThis[Symbol.for("WORKFLOW_CONTEXT")].workflowStartedAt = new Date(${JSON.stringify(metadata.workflowStartedAt.toISOString())});`).dispose();
|
|
1136
|
+
}
|
|
1137
|
+
// Start the workflow function. If the workflow isn't registered,
|
|
1138
|
+
// throw an error tagged with `name = "WorkflowNotRegisteredError"`
|
|
1139
|
+
// so the host-side entrypoint can reconstruct a real
|
|
1140
|
+
// WorkflowNotRegisteredError (a WorkflowRuntimeError subclass that
|
|
1141
|
+
// classifies as RUNTIME_ERROR) rather than a generic user error.
|
|
1142
|
+
// See quickjs-entrypoint.ts's run_failed branch.
|
|
1143
|
+
try {
|
|
1144
|
+
vm.evalCode(`
|
|
1145
|
+
var __wfn = globalThis.__private_workflows.get(${JSON.stringify(workflowId)});
|
|
1146
|
+
if (!__wfn) {
|
|
1147
|
+
var __wfnErr = new Error("Workflow \\"" + ${JSON.stringify(workflowId)} + "\\" is not registered in the current deployment.");
|
|
1148
|
+
__wfnErr.name = "WorkflowNotRegisteredError";
|
|
1149
|
+
throw __wfnErr;
|
|
1150
|
+
}
|
|
1151
|
+
var __args = globalThis.__wdk_input
|
|
1152
|
+
? globalThis[Symbol.for("workflow-deserialize")](globalThis.__wdk_input)
|
|
1153
|
+
: [];
|
|
1154
|
+
delete globalThis.__wdk_input;
|
|
1155
|
+
if (!Array.isArray(__args)) __args = [__args];
|
|
1156
|
+
__wfn.apply(null, __args).then(
|
|
1157
|
+
function(result) { globalThis.__workflowResult = globalThis[Symbol.for("workflow-serialize")](result); },
|
|
1158
|
+
function(error) {
|
|
1159
|
+
// Preserve display info on the host-side failed object
|
|
1160
|
+
// (matches the legacy host-visible shape) AND serialize the
|
|
1161
|
+
// entire thrown value so the host can dehydrate the original
|
|
1162
|
+
// type-identity, cause chain, or non-Error throws verbatim
|
|
1163
|
+
// through the standard error pipeline.
|
|
1164
|
+
globalThis.__workflowError = {
|
|
1165
|
+
message: error && error.message != null ? String(error.message) : String(error),
|
|
1166
|
+
stack: error && error.stack ? error.stack : "",
|
|
1167
|
+
name: error && error.name ? error.name : (error instanceof Error ? "Error" : typeof error),
|
|
1168
|
+
valueBytes: globalThis[Symbol.for("workflow-serialize")](error),
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
);
|
|
1172
|
+
`).dispose();
|
|
1173
|
+
}
|
|
1174
|
+
catch (err) {
|
|
1175
|
+
return makeSettledSession(extractError(vm, err, 'Failed to start workflow'));
|
|
1176
|
+
}
|
|
1177
|
+
// Process events and drain jobs in a loop. Events may resolve promises
|
|
1178
|
+
// that unblock workflow code, which then creates NEW resolvers for
|
|
1179
|
+
// subsequent events. Re-processing events matches these new resolvers
|
|
1180
|
+
// against events that were already delivered.
|
|
1181
|
+
{
|
|
1182
|
+
let maxIterations = 100;
|
|
1183
|
+
let madeProgress;
|
|
1184
|
+
do {
|
|
1185
|
+
madeProgress = await processEvents(vm, events, advanceClock, options.encryptionKey);
|
|
1186
|
+
let batch;
|
|
1187
|
+
do {
|
|
1188
|
+
batch = vm.executePendingJobs();
|
|
1189
|
+
if (batch > 0)
|
|
1190
|
+
madeProgress = true;
|
|
1191
|
+
} while (batch > 0);
|
|
1192
|
+
} while (madeProgress && --maxIterations > 0);
|
|
1193
|
+
if (madeProgress && maxIterations === 0) {
|
|
1194
|
+
// The drain loop hit its bound while still making progress —
|
|
1195
|
+
// proceeding as if it converged would present as a mysterious
|
|
1196
|
+
// suspension or replay divergence. Make the giving-up visible so
|
|
1197
|
+
// a wedge is attributable to this bound rather than a mystery.
|
|
1198
|
+
runtimeLogger.warn('QuickJS runtime: event drain loop hit its iteration bound before reaching a fixed point', {
|
|
1199
|
+
workflowRunId: workflowRun.runId,
|
|
1200
|
+
eventCount: events.length,
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
// ---- Check result ----
|
|
1205
|
+
return makeLiveSession(vm, interruptBudget, advanceClock, options.encryptionKey);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
/** Session wrapper for a result whose VM is already settled/disposed. */
|
|
1209
|
+
function makeSettledSession(result) {
|
|
1210
|
+
return {
|
|
1211
|
+
result,
|
|
1212
|
+
continueWithEvents: () => {
|
|
1213
|
+
throw new Error('QuickJS workflow session is settled — continueWithEvents is only valid while suspended');
|
|
1214
|
+
},
|
|
1215
|
+
dispose: () => { },
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Evaluate the VM's state and wrap it in a live session. While suspended,
|
|
1220
|
+
* the VM stays alive so `continueWithEvents` can resume it in place;
|
|
1221
|
+
* terminal states dispose the VM immediately (inside checkWorkflowState).
|
|
1222
|
+
*/
|
|
1223
|
+
function makeLiveSession(vm, interruptBudget, advanceClock, encryptionKey) {
|
|
1224
|
+
const result = checkWorkflowState(vm, { keepAliveOnSuspend: true });
|
|
1225
|
+
let alive = !!result.suspended;
|
|
1226
|
+
const session = {
|
|
1227
|
+
result,
|
|
1228
|
+
async continueWithEvents(newEvents) {
|
|
1229
|
+
if (!alive) {
|
|
1230
|
+
throw new Error('QuickJS workflow session is not alive — continueWithEvents is only valid while suspended');
|
|
1231
|
+
}
|
|
1232
|
+
// Fresh execution burst — the interrupt budget bounds VM compute,
|
|
1233
|
+
// not wall time spent waiting on inline steps between bursts.
|
|
1234
|
+
interruptBudget.start = Date.now();
|
|
1235
|
+
let maxIterations = 100;
|
|
1236
|
+
let madeProgress;
|
|
1237
|
+
do {
|
|
1238
|
+
madeProgress = await processEvents(vm, newEvents, advanceClock, encryptionKey);
|
|
1239
|
+
let batch;
|
|
1240
|
+
do {
|
|
1241
|
+
batch = vm.executePendingJobs();
|
|
1242
|
+
if (batch > 0)
|
|
1243
|
+
madeProgress = true;
|
|
1244
|
+
} while (batch > 0);
|
|
1245
|
+
} while (madeProgress && --maxIterations > 0);
|
|
1246
|
+
const next = checkWorkflowState(vm, { keepAliveOnSuspend: true });
|
|
1247
|
+
if (!next.suspended)
|
|
1248
|
+
alive = false;
|
|
1249
|
+
session.result = next;
|
|
1250
|
+
return next;
|
|
1251
|
+
},
|
|
1252
|
+
dispose() {
|
|
1253
|
+
if (alive) {
|
|
1254
|
+
alive = false;
|
|
1255
|
+
try {
|
|
1256
|
+
vm.dispose();
|
|
1257
|
+
}
|
|
1258
|
+
catch {
|
|
1259
|
+
// Already disposed — ignore.
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
};
|
|
1264
|
+
return session;
|
|
1265
|
+
}
|
|
1266
|
+
// ---- Event Processing ----
|
|
1267
|
+
async function processEvents(vm, events, advanceClock, encryptionKey) {
|
|
1268
|
+
let resolved = false;
|
|
1269
|
+
for (const event of events) {
|
|
1270
|
+
// Advance the VM's deterministic clock to this event's creation time
|
|
1271
|
+
// BEFORE resolving anything, so workflow code unblocked by this event
|
|
1272
|
+
// observes Date.now() at (or after — the clock is monotonic) the time
|
|
1273
|
+
// the event was recorded. Mirrors the node:vm engine's
|
|
1274
|
+
// `onConsumedEvent → updateTimestamp(+event.createdAt)`.
|
|
1275
|
+
advanceClock(+event.createdAt);
|
|
1276
|
+
const cid = event.correlationId;
|
|
1277
|
+
if (!cid)
|
|
1278
|
+
continue;
|
|
1279
|
+
// JSON.stringify handles quotes, backslashes and control characters;
|
|
1280
|
+
// correlation ids are host-generated ULIDs today, but the eval-string
|
|
1281
|
+
// safety shouldn't depend on that invariant being asserted nowhere.
|
|
1282
|
+
const cidJs = JSON.stringify(cid);
|
|
1283
|
+
const eventData = 'eventData' in event
|
|
1284
|
+
? event.eventData
|
|
1285
|
+
: undefined;
|
|
1286
|
+
// Log the event and whether the resolver exists
|
|
1287
|
+
switch (event.eventType) {
|
|
1288
|
+
case 'step_completed': {
|
|
1289
|
+
const hasResolver = vm.dump(vm.evalCode(`!!globalThis.__resolvers[${cidJs}]`));
|
|
1290
|
+
const rawOutput = eventData?.result ?? eventData?.output;
|
|
1291
|
+
if (hasResolver) {
|
|
1292
|
+
if (rawOutput instanceof Uint8Array) {
|
|
1293
|
+
// Decrypt if encrypted — the VM only understands 'devl' format
|
|
1294
|
+
runtimeLogger.debug('QuickJS runtime: step result raw', {
|
|
1295
|
+
correlationId: cid,
|
|
1296
|
+
rawPrefix: new TextDecoder().decode(rawOutput.subarray(0, 4)),
|
|
1297
|
+
rawByteLength: rawOutput.byteLength,
|
|
1298
|
+
isBuffer: Buffer.isBuffer(rawOutput),
|
|
1299
|
+
});
|
|
1300
|
+
const decryptedOutput = await prepareBytesForVM(rawOutput, encryptionKey);
|
|
1301
|
+
runtimeLogger.debug('QuickJS runtime: step result decrypted', {
|
|
1302
|
+
correlationId: cid,
|
|
1303
|
+
prefix: new TextDecoder().decode(decryptedOutput.subarray(0, 4)),
|
|
1304
|
+
byteLength: decryptedOutput.byteLength,
|
|
1305
|
+
});
|
|
1306
|
+
const bytesHandle = vm.newUint8Array(decryptedOutput);
|
|
1307
|
+
vm.setProp(vm.global, '__tmp_result', bytesHandle);
|
|
1308
|
+
bytesHandle.dispose();
|
|
1309
|
+
vm.evalCode(`globalThis.__resolvers[${cidJs}].resolve(globalThis[Symbol.for("workflow-deserialize")](globalThis.__tmp_result));` +
|
|
1310
|
+
`delete globalThis.__resolvers[${cidJs}];` +
|
|
1311
|
+
`delete globalThis.__tmp_result;`).dispose();
|
|
1312
|
+
}
|
|
1313
|
+
else {
|
|
1314
|
+
runtimeLogger.debug('QuickJS runtime: step result non-binary', {
|
|
1315
|
+
correlationId: cid,
|
|
1316
|
+
type: typeof rawOutput,
|
|
1317
|
+
isNull: rawOutput === null,
|
|
1318
|
+
isUndefined: rawOutput === undefined,
|
|
1319
|
+
constructor: rawOutput?.constructor?.name,
|
|
1320
|
+
});
|
|
1321
|
+
const serialized = rawOutput !== undefined ? JSON.stringify(rawOutput) : 'undefined';
|
|
1322
|
+
vm.evalCode(`globalThis.__resolvers[${cidJs}].resolve(${serialized});` +
|
|
1323
|
+
`delete globalThis.__resolvers[${cidJs}];`).dispose();
|
|
1324
|
+
}
|
|
1325
|
+
// Drain ALL microtasks after resolve
|
|
1326
|
+
{
|
|
1327
|
+
resolved = true;
|
|
1328
|
+
let b;
|
|
1329
|
+
do {
|
|
1330
|
+
b = vm.executePendingJobs();
|
|
1331
|
+
} while (b > 0);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
// No resolver yet — buffer the prepared outcome so the promise
|
|
1336
|
+
// settles the moment the VM constructs it (see __terminalBuffer
|
|
1337
|
+
// in the bootstrap). Without this, the live-continuation path
|
|
1338
|
+
// (which scans each delta exactly once) drops the terminal and
|
|
1339
|
+
// the await never settles.
|
|
1340
|
+
if (rawOutput instanceof Uint8Array) {
|
|
1341
|
+
const decryptedOutput = await prepareBytesForVM(rawOutput, encryptionKey);
|
|
1342
|
+
const bytesHandle = vm.newUint8Array(decryptedOutput);
|
|
1343
|
+
vm.setProp(vm.global, '__tmp_buf', bytesHandle);
|
|
1344
|
+
bytesHandle.dispose();
|
|
1345
|
+
vm.evalCode(`globalThis.__terminalBuffer[${cidJs}] = { kind: "resolve_bytes", bytes: globalThis.__tmp_buf };` +
|
|
1346
|
+
`delete globalThis.__tmp_buf;`).dispose();
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
const serialized = rawOutput !== undefined ? JSON.stringify(rawOutput) : 'undefined';
|
|
1350
|
+
vm.evalCode(`globalThis.__terminalBuffer[${cidJs}] = { kind: "resolve_value", value: ${serialized} };`).dispose();
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
markCreated(vm, cidJs);
|
|
1354
|
+
break;
|
|
1355
|
+
}
|
|
1356
|
+
case 'step_failed': {
|
|
1357
|
+
const hasResolver = vm.dump(vm.evalCode(`!!globalThis.__resolvers[${cidJs}]`));
|
|
1358
|
+
if (hasResolver) {
|
|
1359
|
+
const errorData = eventData?.error;
|
|
1360
|
+
if (errorData instanceof Uint8Array) {
|
|
1361
|
+
// Modern path (post-#1851): the step handler dehydrated the
|
|
1362
|
+
// thrown value through the first-class error pipeline. Decrypt
|
|
1363
|
+
// (if encrypted) and pass the bytes to the VM-side deserializer
|
|
1364
|
+
// so the workflow catch sees a properly typed Error subclass
|
|
1365
|
+
// (TypeError, FatalError with original cause chain, etc.) with
|
|
1366
|
+
// the original message and stack preserved.
|
|
1367
|
+
const decrypted = await prepareBytesForVM(errorData, encryptionKey);
|
|
1368
|
+
const bytesHandle = vm.newUint8Array(decrypted);
|
|
1369
|
+
vm.setProp(vm.global, '__tmp_error', bytesHandle);
|
|
1370
|
+
bytesHandle.dispose();
|
|
1371
|
+
vm.evalCode(`(function(){` +
|
|
1372
|
+
`var e=globalThis[Symbol.for("workflow-deserialize")](globalThis.__tmp_error);` +
|
|
1373
|
+
`globalThis.__resolvers[${cidJs}].reject(e);` +
|
|
1374
|
+
`delete globalThis.__resolvers[${cidJs}];` +
|
|
1375
|
+
`delete globalThis.__tmp_error;` +
|
|
1376
|
+
`})()`).dispose();
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
// Legacy path: pre-pipeline events stored error as
|
|
1380
|
+
// `{ message, stack, code }`. Reconstruct a FatalError so
|
|
1381
|
+
// workflow catch can detect it via FatalError.is(), matching
|
|
1382
|
+
// the original V1 step handler behavior.
|
|
1383
|
+
const isErrorObject = typeof errorData === 'object' && errorData !== null;
|
|
1384
|
+
const msg = isErrorObject
|
|
1385
|
+
? (errorData.message ??
|
|
1386
|
+
'Step failed')
|
|
1387
|
+
: typeof errorData === 'string'
|
|
1388
|
+
? errorData
|
|
1389
|
+
: 'Step failed';
|
|
1390
|
+
const errorStack = (isErrorObject
|
|
1391
|
+
? errorData.stack
|
|
1392
|
+
: undefined) ?? eventData?.stack;
|
|
1393
|
+
const stackAssignment = errorStack
|
|
1394
|
+
? `e.stack=${JSON.stringify(errorStack)};`
|
|
1395
|
+
: '';
|
|
1396
|
+
vm.evalCode(`(function(){var e=new Error(${JSON.stringify(msg)});e.name="FatalError";e.fatal=true;${stackAssignment}` +
|
|
1397
|
+
`globalThis.__resolvers[${cidJs}].reject(e);` +
|
|
1398
|
+
`delete globalThis.__resolvers[${cidJs}];})()`).dispose();
|
|
1399
|
+
}
|
|
1400
|
+
{
|
|
1401
|
+
resolved = true;
|
|
1402
|
+
let b;
|
|
1403
|
+
do {
|
|
1404
|
+
b = vm.executePendingJobs();
|
|
1405
|
+
} while (b > 0);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
else {
|
|
1409
|
+
// No resolver yet — buffer the prepared rejection (see the
|
|
1410
|
+
// step_completed branch above for the rationale).
|
|
1411
|
+
const errorData = eventData?.error;
|
|
1412
|
+
if (errorData instanceof Uint8Array) {
|
|
1413
|
+
const decrypted = await prepareBytesForVM(errorData, encryptionKey);
|
|
1414
|
+
const bytesHandle = vm.newUint8Array(decrypted);
|
|
1415
|
+
vm.setProp(vm.global, '__tmp_buf', bytesHandle);
|
|
1416
|
+
bytesHandle.dispose();
|
|
1417
|
+
vm.evalCode(`globalThis.__terminalBuffer[${cidJs}] = { kind: "reject_bytes", bytes: globalThis.__tmp_buf };` +
|
|
1418
|
+
`delete globalThis.__tmp_buf;`).dispose();
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
const isErrorObject = typeof errorData === 'object' && errorData !== null;
|
|
1422
|
+
const msg = isErrorObject
|
|
1423
|
+
? (errorData.message ??
|
|
1424
|
+
'Step failed')
|
|
1425
|
+
: typeof errorData === 'string'
|
|
1426
|
+
? errorData
|
|
1427
|
+
: 'Step failed';
|
|
1428
|
+
const errorStack = (isErrorObject
|
|
1429
|
+
? errorData.stack
|
|
1430
|
+
: undefined) ?? eventData?.stack;
|
|
1431
|
+
vm.evalCode(`globalThis.__terminalBuffer[${cidJs}] = { kind: "reject_error", message: ${JSON.stringify(msg)}, stack: ${errorStack ? JSON.stringify(errorStack) : 'undefined'} };`).dispose();
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
markCreated(vm, cidJs);
|
|
1435
|
+
break;
|
|
1436
|
+
}
|
|
1437
|
+
case 'wait_completed': {
|
|
1438
|
+
const hasResolver = vm.dump(vm.evalCode(`!!globalThis.__resolvers[${cidJs}]`));
|
|
1439
|
+
if (hasResolver) {
|
|
1440
|
+
vm.evalCode(`globalThis.__resolvers[${cidJs}].resolve();` +
|
|
1441
|
+
`delete globalThis.__resolvers[${cidJs}];`).dispose();
|
|
1442
|
+
{
|
|
1443
|
+
resolved = true;
|
|
1444
|
+
let b;
|
|
1445
|
+
do {
|
|
1446
|
+
b = vm.executePendingJobs();
|
|
1447
|
+
} while (b > 0);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
else {
|
|
1451
|
+
// No resolver yet — buffer (see step_completed above).
|
|
1452
|
+
vm.evalCode(`globalThis.__terminalBuffer[${cidJs}] = { kind: "resolve_undefined" };`).dispose();
|
|
1453
|
+
}
|
|
1454
|
+
markCreated(vm, cidJs);
|
|
1455
|
+
break;
|
|
1456
|
+
}
|
|
1457
|
+
case 'attr_set': {
|
|
1458
|
+
// Only workflow-written attribute events resolve a pending
|
|
1459
|
+
// setAttributes() promise; step/system writers share no
|
|
1460
|
+
// correlationIds with VM resolvers, so the guard is defensive.
|
|
1461
|
+
const writer = eventData?.writer
|
|
1462
|
+
?.type;
|
|
1463
|
+
if (writer !== 'workflow')
|
|
1464
|
+
break;
|
|
1465
|
+
const hasResolver = vm.dump(vm.evalCode(`!!globalThis.__resolvers[${cidJs}]`));
|
|
1466
|
+
if (!hasResolver) {
|
|
1467
|
+
// No resolver yet — buffer (see step_completed above).
|
|
1468
|
+
vm.evalCode(`globalThis.__terminalBuffer[${cidJs}] = { kind: "resolve_undefined" };`).dispose();
|
|
1469
|
+
}
|
|
1470
|
+
if (hasResolver) {
|
|
1471
|
+
vm.evalCode(`globalThis.__resolvers[${cidJs}].resolve();` +
|
|
1472
|
+
`delete globalThis.__resolvers[${cidJs}];`).dispose();
|
|
1473
|
+
{
|
|
1474
|
+
resolved = true;
|
|
1475
|
+
let b;
|
|
1476
|
+
do {
|
|
1477
|
+
b = vm.executePendingJobs();
|
|
1478
|
+
} while (b > 0);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
markCreated(vm, cidJs);
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1484
|
+
case 'hook_received': {
|
|
1485
|
+
// Check if this event was already processed (delivered or
|
|
1486
|
+
// buffered) within this invocation. Prevents double-delivery when
|
|
1487
|
+
// the outer loop re-scans events.
|
|
1488
|
+
const alreadyProcessed = event.eventId
|
|
1489
|
+
? vm.dump(vm.evalCode(`!!(globalThis.__hookPayloadBuffer.__processedEventIds && globalThis.__hookPayloadBuffer.__processedEventIds[${JSON.stringify(event.eventId)}])`))
|
|
1490
|
+
: false;
|
|
1491
|
+
if (alreadyProcessed) {
|
|
1492
|
+
runtimeLogger.debug('QuickJS runtime: hook_received already processed', {
|
|
1493
|
+
correlationId: cid,
|
|
1494
|
+
eventId: event.eventId,
|
|
1495
|
+
});
|
|
1496
|
+
markCreated(vm, cidJs);
|
|
1497
|
+
break;
|
|
1498
|
+
}
|
|
1499
|
+
// Resilient-resume dedup (parity with the node engine's
|
|
1500
|
+
// EventsConsumer in workflow/hook.ts): two hook_received rows for
|
|
1501
|
+
// ONE resume attempt share a client-minted `resumeId` (a duplicate
|
|
1502
|
+
// can be committed when the materialization fallback races a
|
|
1503
|
+
// delayed direct write — hook_received has no storage uniqueness
|
|
1504
|
+
// constraint). Deliver only the first-in-log occurrence. The seen
|
|
1505
|
+
// set lives in the VM heap so it is deterministic per replay and
|
|
1506
|
+
// survives event re-scans within the invocation. Events without a
|
|
1507
|
+
// resumeId (older SDKs) are never deduped.
|
|
1508
|
+
{
|
|
1509
|
+
// Top-level event.resumeId is the canonical location (the backend
|
|
1510
|
+
// hoists it to a first-class column); the nested
|
|
1511
|
+
// eventData.resumeId form is a deprecated legacy fallback —
|
|
1512
|
+
// mirrors the node engine's dedup in workflow/hook.ts.
|
|
1513
|
+
const resumeId = event.resumeId ??
|
|
1514
|
+
eventData?.resumeId;
|
|
1515
|
+
if (typeof resumeId === 'string') {
|
|
1516
|
+
const resumeIdJs = JSON.stringify(resumeId);
|
|
1517
|
+
const duplicate = vm.dump(vm.evalCode(`(globalThis.__hookSeenResumeIds = globalThis.__hookSeenResumeIds || {})[${resumeIdJs}] === true`));
|
|
1518
|
+
if (duplicate) {
|
|
1519
|
+
runtimeLogger.debug('QuickJS runtime: duplicate hook_received for the same resume attempt, dropping', { correlationId: cid, eventId: event.eventId, resumeId });
|
|
1520
|
+
if (event.eventId) {
|
|
1521
|
+
vm.evalCode(`(globalThis.__hookPayloadBuffer.__processedEventIds = globalThis.__hookPayloadBuffer.__processedEventIds || {})[${JSON.stringify(event.eventId)}] = true;`).dispose();
|
|
1522
|
+
}
|
|
1523
|
+
markCreated(vm, cidJs);
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
vm.evalCode(`globalThis.__hookSeenResumeIds[${resumeIdJs}] = true;`).dispose();
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
// Abort delivery: hook_received for an AbortController's system
|
|
1530
|
+
// hook flips the registered signal instead of resolving a promise.
|
|
1531
|
+
// The payload is the dehydrated `{ aborted: true, reason }` object.
|
|
1532
|
+
const isAbortHook = vm.dump(vm.evalCode(`!!(globalThis.__abortSignals && globalThis.__abortSignals[${cidJs}])`));
|
|
1533
|
+
if (isAbortHook) {
|
|
1534
|
+
const rawAbortPayload = eventData?.payload;
|
|
1535
|
+
if (rawAbortPayload instanceof Uint8Array) {
|
|
1536
|
+
const decrypted = await prepareBytesForVM(rawAbortPayload, encryptionKey);
|
|
1537
|
+
const bytesHandle = vm.newUint8Array(decrypted);
|
|
1538
|
+
vm.setProp(vm.global, '__tmp_abort', bytesHandle);
|
|
1539
|
+
bytesHandle.dispose();
|
|
1540
|
+
vm.evalCode(`(function(){` +
|
|
1541
|
+
`var p=globalThis[Symbol.for("workflow-deserialize")](globalThis.__tmp_abort);` +
|
|
1542
|
+
`delete globalThis.__tmp_abort;` +
|
|
1543
|
+
`globalThis.__abortSignals[${cidJs}]._setAborted(p&&typeof p==="object"?p.reason:undefined);` +
|
|
1544
|
+
`})()`).dispose();
|
|
1545
|
+
}
|
|
1546
|
+
else {
|
|
1547
|
+
vm.evalCode(`globalThis.__abortSignals[${cidJs}]._setAborted(undefined);`).dispose();
|
|
1548
|
+
}
|
|
1549
|
+
// The abort is durably recorded — clear the pending op's
|
|
1550
|
+
// abortRequested marker so the host doesn't re-record it (the
|
|
1551
|
+
// workflow's own abort() call can set the flag before this
|
|
1552
|
+
// event is processed when it happens later in replay order,
|
|
1553
|
+
// and hook_received events are not unique per correlationId).
|
|
1554
|
+
vm.evalCode(`(function(){` +
|
|
1555
|
+
`var p=globalThis.__pending.find(function(q){return q.correlationId===${JSON.stringify(cid)}&&q.type==="hook";});` +
|
|
1556
|
+
`if(p)p.abortRequested=false;` +
|
|
1557
|
+
`})()`).dispose();
|
|
1558
|
+
if (event.eventId) {
|
|
1559
|
+
vm.evalCode(`(globalThis.__hookPayloadBuffer.__processedEventIds = globalThis.__hookPayloadBuffer.__processedEventIds || {})[${JSON.stringify(event.eventId)}] = true;`).dispose();
|
|
1560
|
+
}
|
|
1561
|
+
{
|
|
1562
|
+
resolved = true;
|
|
1563
|
+
let b;
|
|
1564
|
+
do {
|
|
1565
|
+
b = vm.executePendingJobs();
|
|
1566
|
+
} while (b > 0);
|
|
1567
|
+
}
|
|
1568
|
+
markCreated(vm, cidJs);
|
|
1569
|
+
break;
|
|
1570
|
+
}
|
|
1571
|
+
const hasResolver = vm.dump(vm.evalCode(`!!globalThis.__resolvers[${cidJs}]`));
|
|
1572
|
+
const rawPayload = eventData?.payload ?? eventData?.result;
|
|
1573
|
+
runtimeLogger.debug('QuickJS runtime: processing hook_received', {
|
|
1574
|
+
correlationId: cid,
|
|
1575
|
+
eventId: event.eventId,
|
|
1576
|
+
hasResolver,
|
|
1577
|
+
payloadType: typeof rawPayload,
|
|
1578
|
+
payloadIsUint8Array: rawPayload instanceof Uint8Array,
|
|
1579
|
+
payloadKeys: rawPayload && typeof rawPayload === 'object'
|
|
1580
|
+
? Object.keys(rawPayload)
|
|
1581
|
+
: undefined,
|
|
1582
|
+
});
|
|
1583
|
+
if (hasResolver) {
|
|
1584
|
+
if (rawPayload instanceof Uint8Array) {
|
|
1585
|
+
// Decrypt if encrypted — the VM only understands 'devl' format
|
|
1586
|
+
const decryptedPayload = await prepareBytesForVM(rawPayload, encryptionKey);
|
|
1587
|
+
const bytesHandle = vm.newUint8Array(decryptedPayload);
|
|
1588
|
+
vm.setProp(vm.global, '__tmp_result', bytesHandle);
|
|
1589
|
+
bytesHandle.dispose();
|
|
1590
|
+
vm.evalCode(`globalThis.__resolvers[${cidJs}].resolve(globalThis[Symbol.for("workflow-deserialize")](globalThis.__tmp_result));` +
|
|
1591
|
+
`delete globalThis.__resolvers[${cidJs}];` +
|
|
1592
|
+
`delete globalThis.__tmp_result;`).dispose();
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
const serialized = rawPayload !== undefined
|
|
1596
|
+
? JSON.stringify(rawPayload)
|
|
1597
|
+
: 'undefined';
|
|
1598
|
+
vm.evalCode(`globalThis.__resolvers[${cidJs}].resolve(${serialized});` +
|
|
1599
|
+
`delete globalThis.__resolvers[${cidJs}];`).dispose();
|
|
1600
|
+
}
|
|
1601
|
+
// Mark this event as processed in the VM heap to prevent
|
|
1602
|
+
// double-delivery when the outer loop re-scans events.
|
|
1603
|
+
if (event.eventId) {
|
|
1604
|
+
vm.evalCode(`(globalThis.__hookPayloadBuffer.__processedEventIds = globalThis.__hookPayloadBuffer.__processedEventIds || {})[${JSON.stringify(event.eventId)}] = true;`).dispose();
|
|
1605
|
+
}
|
|
1606
|
+
{
|
|
1607
|
+
resolved = true;
|
|
1608
|
+
let b;
|
|
1609
|
+
do {
|
|
1610
|
+
b = vm.executePendingJobs();
|
|
1611
|
+
} while (b > 0);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1615
|
+
// No resolver yet — buffer the payload in the VM heap. When
|
|
1616
|
+
// createHookPromise() is called later, it will drain this buffer
|
|
1617
|
+
// first (matching the node:vm engine's payloadsQueue behavior).
|
|
1618
|
+
const eventIdJs = event.eventId
|
|
1619
|
+
? JSON.stringify(event.eventId)
|
|
1620
|
+
: 'null';
|
|
1621
|
+
const bufferAndTrack = `(globalThis.__hookPayloadBuffer[${cidJs}] = globalThis.__hookPayloadBuffer[${cidJs}] || [])` +
|
|
1622
|
+
`.push(%PAYLOAD%);` +
|
|
1623
|
+
(event.eventId
|
|
1624
|
+
? `(globalThis.__hookPayloadBuffer.__processedEventIds = globalThis.__hookPayloadBuffer.__processedEventIds || {})[${eventIdJs}] = true;`
|
|
1625
|
+
: '');
|
|
1626
|
+
if (rawPayload instanceof Uint8Array) {
|
|
1627
|
+
// Decrypt if encrypted — the VM only understands 'devl' format
|
|
1628
|
+
const decryptedPayload = await prepareBytesForVM(rawPayload, encryptionKey);
|
|
1629
|
+
const bytesHandle = vm.newUint8Array(decryptedPayload);
|
|
1630
|
+
vm.setProp(vm.global, '__tmp_result', bytesHandle);
|
|
1631
|
+
bytesHandle.dispose();
|
|
1632
|
+
// NOTE: replacement is a function so `$`-sequences in the
|
|
1633
|
+
// substituted JS never get interpreted as String.replace
|
|
1634
|
+
// special replacement patterns.
|
|
1635
|
+
vm.evalCode(bufferAndTrack.replace('%PAYLOAD%', () => 'globalThis[Symbol.for("workflow-deserialize")](globalThis.__tmp_result)') + 'delete globalThis.__tmp_result;').dispose();
|
|
1636
|
+
}
|
|
1637
|
+
else {
|
|
1638
|
+
const serialized = rawPayload !== undefined
|
|
1639
|
+
? JSON.stringify(rawPayload)
|
|
1640
|
+
: 'undefined';
|
|
1641
|
+
// Function replacement: a JSON-serialized payload can contain
|
|
1642
|
+
// `$&`, `$'`, `$\``, ... which String.replace would otherwise
|
|
1643
|
+
// expand, silently corrupting the injected code.
|
|
1644
|
+
vm.evalCode(bufferAndTrack.replace('%PAYLOAD%', () => serialized)).dispose();
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
markCreated(vm, cidJs);
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
case 'hook_conflict': {
|
|
1651
|
+
// Another workflow owns this hook token. Payload awaiters reject
|
|
1652
|
+
// with HookConflictError; getConflict() awaiters resolve with a
|
|
1653
|
+
// Run handle for the conflicting run (revived through the VM's
|
|
1654
|
+
// class registry so its methods are durable step proxies) or
|
|
1655
|
+
// reject with the error when no handle can be constructed —
|
|
1656
|
+
// mirroring the node:vm engine's hook.ts hook_conflict handling.
|
|
1657
|
+
const conflictToken = eventData?.token ?? 'unknown';
|
|
1658
|
+
const conflictingRunId = eventData?.conflictingRunId;
|
|
1659
|
+
const didSettle = vm.dump(vm.evalCode(`(function(){
|
|
1660
|
+
var cid = ${JSON.stringify(cid)};
|
|
1661
|
+
var token = ${JSON.stringify(conflictToken)};
|
|
1662
|
+
var conflictingRunId = ${JSON.stringify(conflictingRunId ?? null)};
|
|
1663
|
+
var ErrCls = globalThis[Symbol.for('@workflow/errors//HookConflictError')];
|
|
1664
|
+
var err;
|
|
1665
|
+
if (typeof ErrCls === 'function') {
|
|
1666
|
+
err = new ErrCls(token, conflictingRunId || undefined);
|
|
1667
|
+
} else {
|
|
1668
|
+
err = new Error('Hook token "' + token + '" is already in use by another workflow');
|
|
1669
|
+
err.name = 'HookConflictError';
|
|
1670
|
+
err.token = token;
|
|
1671
|
+
if (conflictingRunId) err.conflictingRunId = conflictingRunId;
|
|
1672
|
+
}
|
|
1673
|
+
var run = null;
|
|
1674
|
+
if (conflictingRunId) {
|
|
1675
|
+
var reg = globalThis[Symbol.for('workflow-class-registry')];
|
|
1676
|
+
var RunCls = reg && reg.get('class//workflow//Run');
|
|
1677
|
+
var des = RunCls && RunCls[Symbol.for('workflow-deserialize')];
|
|
1678
|
+
if (typeof des === 'function') {
|
|
1679
|
+
run = des.call(RunCls, { runId: conflictingRunId });
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
var settled = false;
|
|
1683
|
+
var state = globalThis.__hooks && globalThis.__hooks[cid];
|
|
1684
|
+
if (state && !state.conflict) {
|
|
1685
|
+
state.conflict = { error: err, run: run };
|
|
1686
|
+
var gc = state.getConflictResolvers;
|
|
1687
|
+
state.getConflictResolvers = [];
|
|
1688
|
+
for (var i = 0; i < gc.length; i++) {
|
|
1689
|
+
if (run) { gc[i].resolve(run); } else { gc[i].reject(err); }
|
|
1690
|
+
settled = true;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
if (globalThis.__resolvers[cid]) {
|
|
1694
|
+
globalThis.__resolvers[cid].reject(err);
|
|
1695
|
+
delete globalThis.__resolvers[cid];
|
|
1696
|
+
settled = true;
|
|
1697
|
+
}
|
|
1698
|
+
return settled;
|
|
1699
|
+
})()`));
|
|
1700
|
+
if (didSettle) {
|
|
1701
|
+
resolved = true;
|
|
1702
|
+
let b;
|
|
1703
|
+
do {
|
|
1704
|
+
b = vm.executePendingJobs();
|
|
1705
|
+
} while (b > 0);
|
|
1706
|
+
}
|
|
1707
|
+
markCreated(vm, cidJs);
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1710
|
+
case 'step_created':
|
|
1711
|
+
case 'step_started':
|
|
1712
|
+
case 'step_retrying':
|
|
1713
|
+
case 'wait_created': {
|
|
1714
|
+
markCreated(vm, cidJs);
|
|
1715
|
+
break;
|
|
1716
|
+
}
|
|
1717
|
+
case 'hook_created': {
|
|
1718
|
+
// Confirm creation for getConflict() awaiters: resolve them with
|
|
1719
|
+
// null (no conflict) once the event log proves the hook exists.
|
|
1720
|
+
const settledGetConflict = vm.dump(vm.evalCode(`(function(){
|
|
1721
|
+
var state = globalThis.__hooks && globalThis.__hooks[${JSON.stringify(cid)}];
|
|
1722
|
+
if (!state) return false;
|
|
1723
|
+
state.created = true;
|
|
1724
|
+
var gc = state.getConflictResolvers;
|
|
1725
|
+
state.getConflictResolvers = [];
|
|
1726
|
+
for (var i = 0; i < gc.length; i++) gc[i].resolve(null);
|
|
1727
|
+
return gc.length > 0;
|
|
1728
|
+
})()`));
|
|
1729
|
+
if (settledGetConflict) {
|
|
1730
|
+
resolved = true;
|
|
1731
|
+
let b;
|
|
1732
|
+
do {
|
|
1733
|
+
b = vm.executePendingJobs();
|
|
1734
|
+
} while (b > 0);
|
|
1735
|
+
}
|
|
1736
|
+
markCreated(vm, cidJs);
|
|
1737
|
+
break;
|
|
1738
|
+
}
|
|
1739
|
+
case 'hook_disposed': {
|
|
1740
|
+
// Disambiguate from the `hook` pending op with the same
|
|
1741
|
+
// correlationId — we want to mark the `hook_dispose` entry.
|
|
1742
|
+
markCreated(vm, cidJs, 'hook_dispose');
|
|
1743
|
+
break;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
return resolved;
|
|
1748
|
+
}
|
|
1749
|
+
function markCreated(vm, cidJs, opType) {
|
|
1750
|
+
// `cidJs` is the JSON.stringify-quoted correlation id (see processEvents).
|
|
1751
|
+
// `hook` and `hook_dispose` pending ops share the same correlationId,
|
|
1752
|
+
// so when processing `hook_disposed` events we must disambiguate by
|
|
1753
|
+
// type — otherwise `.find()` returns the original `hook` op and the
|
|
1754
|
+
// `hook_dispose` op is never marked, causing the entrypoint to keep
|
|
1755
|
+
// retrying a hook_disposed for an already-deleted entity.
|
|
1756
|
+
const predicate = opType
|
|
1757
|
+
? `function(p){return p.correlationId===${cidJs}&&p.type===${JSON.stringify(opType)};}`
|
|
1758
|
+
: `function(p){return p.correlationId===${cidJs};}`;
|
|
1759
|
+
vm.evalCode(`var __p=globalThis.__pending.find(${predicate});` +
|
|
1760
|
+
`if(__p)__p.hasCreatedEvent=true;`).dispose();
|
|
1761
|
+
}
|
|
1762
|
+
// ---- State Checking ----
|
|
1763
|
+
/**
|
|
1764
|
+
* Collect leftover pending operations that need durable side effects when
|
|
1765
|
+
* the workflow reaches a terminal state. Mirrors the node:vm engine's
|
|
1766
|
+
* drainPendingQueueItems (workflow.ts): still-alive system hooks
|
|
1767
|
+
* (AbortController) without an abort in flight are implicitly disposed so
|
|
1768
|
+
* they don't leak hook rows; ops without created events (fire-and-forget
|
|
1769
|
+
* attributes/hooks/steps/waits) and pending abort recordings are surfaced
|
|
1770
|
+
* for the entrypoint to flush.
|
|
1771
|
+
*/
|
|
1772
|
+
function collectDrainOperations(vm) {
|
|
1773
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
1774
|
+
try {
|
|
1775
|
+
const h = __addDisposableResource(env_3, vm.evalCode(`(function(){
|
|
1776
|
+
var toDispose = [];
|
|
1777
|
+
globalThis.__pending.forEach(function(p){
|
|
1778
|
+
if (p.type === "hook" && p.isSystem && !p.abortRequested && !p.disposed) {
|
|
1779
|
+
p.disposed = true;
|
|
1780
|
+
// Only dispose hooks that were durably created; a hook that never
|
|
1781
|
+
// reached storage has nothing to clean up.
|
|
1782
|
+
if (p.hasCreatedEvent) {
|
|
1783
|
+
toDispose.push({
|
|
1784
|
+
type: "hook_dispose",
|
|
1785
|
+
correlationId: p.correlationId,
|
|
1786
|
+
hasCreatedEvent: false,
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
toDispose.forEach(function(d){ globalThis.__pending.push(d); });
|
|
1792
|
+
return globalThis.__pending.filter(function(p){
|
|
1793
|
+
if (p.abortRequested) return true;
|
|
1794
|
+
if (p.hasCreatedEvent) return false;
|
|
1795
|
+
// Skip system hooks that were disposed before ever being created.
|
|
1796
|
+
if (p.type === "hook" && p.disposed) return false;
|
|
1797
|
+
return true;
|
|
1798
|
+
});
|
|
1799
|
+
})()`), false);
|
|
1800
|
+
return vm.dump(h);
|
|
1801
|
+
}
|
|
1802
|
+
catch (e_3) {
|
|
1803
|
+
env_3.error = e_3;
|
|
1804
|
+
env_3.hasError = true;
|
|
1805
|
+
}
|
|
1806
|
+
finally {
|
|
1807
|
+
__disposeResources(env_3);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
function checkWorkflowState(vm, opts = {}) {
|
|
1811
|
+
// Check completed — __workflowResult is a format-prefixed Uint8Array
|
|
1812
|
+
{
|
|
1813
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1814
|
+
try {
|
|
1815
|
+
const h = __addDisposableResource(env_4, vm.evalCode('globalThis.__workflowResult'), false);
|
|
1816
|
+
if (!h.isUndefined) {
|
|
1817
|
+
const resultBytes = h.toUint8Array();
|
|
1818
|
+
const drainOperations = collectDrainOperations(vm);
|
|
1819
|
+
vm.dispose();
|
|
1820
|
+
return {
|
|
1821
|
+
completed: {
|
|
1822
|
+
result: resultBytes,
|
|
1823
|
+
...(drainOperations.length > 0 ? { drainOperations } : {}),
|
|
1824
|
+
},
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
catch (e_4) {
|
|
1829
|
+
env_4.error = e_4;
|
|
1830
|
+
env_4.hasError = true;
|
|
1831
|
+
}
|
|
1832
|
+
finally {
|
|
1833
|
+
__disposeResources(env_4);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
// Check failed
|
|
1837
|
+
{
|
|
1838
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
1839
|
+
try {
|
|
1840
|
+
const h = __addDisposableResource(env_5, vm.evalCode('globalThis.__workflowError'), false);
|
|
1841
|
+
if (!h.isUndefined) {
|
|
1842
|
+
const errorObj = vm.dump(h);
|
|
1843
|
+
const failed = typeof errorObj === 'string'
|
|
1844
|
+
? { message: errorObj }
|
|
1845
|
+
: {
|
|
1846
|
+
message: errorObj.message,
|
|
1847
|
+
stack: errorObj.stack || undefined,
|
|
1848
|
+
name: errorObj.name || undefined,
|
|
1849
|
+
valueBytes: errorObj.valueBytes,
|
|
1850
|
+
};
|
|
1851
|
+
runtimeLogger.error('QuickJS runtime: workflow failed in VM', {
|
|
1852
|
+
errorMessage: failed.message,
|
|
1853
|
+
errorName: failed.name,
|
|
1854
|
+
errorStack: failed.stack,
|
|
1855
|
+
});
|
|
1856
|
+
const drainOperations = collectDrainOperations(vm);
|
|
1857
|
+
vm.dispose();
|
|
1858
|
+
return {
|
|
1859
|
+
failed: {
|
|
1860
|
+
...failed,
|
|
1861
|
+
...(drainOperations.length > 0 ? { drainOperations } : {}),
|
|
1862
|
+
},
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
catch (e_5) {
|
|
1867
|
+
env_5.error = e_5;
|
|
1868
|
+
env_5.hasError = true;
|
|
1869
|
+
}
|
|
1870
|
+
finally {
|
|
1871
|
+
__disposeResources(env_5);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
// Check suspended — the workflow is suspended if there are active resolvers
|
|
1875
|
+
// OR pending operations that haven't been created yet (e.g. hooks created
|
|
1876
|
+
// upfront but not yet awaited)
|
|
1877
|
+
{
|
|
1878
|
+
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
1879
|
+
try {
|
|
1880
|
+
const h = __addDisposableResource(env_6, vm.evalCode('Object.keys(globalThis.__resolvers).length > 0 || globalThis.__pending.some(function(p){return!p.hasCreatedEvent;})'), false);
|
|
1881
|
+
if (vm.dump(h)) {
|
|
1882
|
+
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
1883
|
+
try {
|
|
1884
|
+
const pendingH = __addDisposableResource(env_7, vm.evalCode(
|
|
1885
|
+
// Ops with an active resolver or without a created event are
|
|
1886
|
+
// pending; abort-requested hooks are also surfaced (even when
|
|
1887
|
+
// already created and unawaited) so the host records the abort.
|
|
1888
|
+
`globalThis.__pending.filter(function(p){return!!globalThis.__resolvers[p.correlationId] || !p.hasCreatedEvent || p.abortRequested;})`), false);
|
|
1889
|
+
const pendingOps = vm.dump(pendingH);
|
|
1890
|
+
if (!opts.keepAliveOnSuspend)
|
|
1891
|
+
vm.dispose();
|
|
1892
|
+
return {
|
|
1893
|
+
suspended: {
|
|
1894
|
+
pendingOperations: pendingOps,
|
|
1895
|
+
},
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
catch (e_6) {
|
|
1899
|
+
env_7.error = e_6;
|
|
1900
|
+
env_7.hasError = true;
|
|
1901
|
+
}
|
|
1902
|
+
finally {
|
|
1903
|
+
__disposeResources(env_7);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
catch (e_7) {
|
|
1908
|
+
env_6.error = e_7;
|
|
1909
|
+
env_6.hasError = true;
|
|
1910
|
+
}
|
|
1911
|
+
finally {
|
|
1912
|
+
__disposeResources(env_6);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
vm.dispose();
|
|
1916
|
+
return { failed: { message: 'Workflow ended in unknown state' } };
|
|
1917
|
+
}
|
|
1918
|
+
// ---- Helpers ----
|
|
1919
|
+
function extractError(vm, err, fallbackMessage) {
|
|
1920
|
+
let message = fallbackMessage;
|
|
1921
|
+
let stack;
|
|
1922
|
+
let name;
|
|
1923
|
+
if (err instanceof JSException) {
|
|
1924
|
+
const error = vm.dump(err.handle);
|
|
1925
|
+
err.handle.dispose();
|
|
1926
|
+
message = error?.message ?? err.message ?? fallbackMessage;
|
|
1927
|
+
stack = error?.stack ?? err.stack;
|
|
1928
|
+
name = error?.name ?? err.name;
|
|
1929
|
+
}
|
|
1930
|
+
else if (err instanceof Error) {
|
|
1931
|
+
message = err.message ?? fallbackMessage;
|
|
1932
|
+
stack = err.stack;
|
|
1933
|
+
name = err.name;
|
|
1934
|
+
}
|
|
1935
|
+
vm.dispose();
|
|
1936
|
+
return {
|
|
1937
|
+
failed: { message, stack, name },
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
function createInterruptHandler(budget) {
|
|
1941
|
+
const timeout = getReplayTimeoutMs();
|
|
1942
|
+
return () => Date.now() - budget.start > timeout;
|
|
1943
|
+
}
|
|
1944
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVpY2tqcy1ydW50aW1lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3J1bnRpbWUvcXVpY2tqcy1ydW50aW1lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQkc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBUUgsT0FBTyxLQUFLLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDakMsT0FBTyxFQUVMLFdBQVcsRUFDWCxPQUFPLEdBRVIsTUFBTSxjQUFjLENBQUM7QUFDdEIsT0FBTyxVQUFVLE1BQU0sWUFBWSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDN0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTdELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsV0FBVyxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDL0UsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUVqRSwyQ0FBMkM7QUFFM0M7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxLQUFLLFVBQVUsaUJBQWlCLENBQzlCLElBQWdCLEVBQ2hCLEdBQW1CO0lBRW5CLE9BQU8sQ0FBQyxNQUFNLFVBQVUsQ0FBQyxNQUFNLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBZSxDQUFDO0FBQ3BFLENBQUM7QUF3SkQsOEJBQThCO0FBRTlCOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxZQUFZLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7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LEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNiLE9BQU87UUFDTCxNQUFNLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRTtLQUNqQyxDQUFDO0FBQ0osQ0FBQztBQXFCRCxTQUFTLHNCQUFzQixDQUFDLE1BQXVCO0lBQ3JELE1BQU0sT0FBTyxHQUFHLGtCQUFrQixFQUFFLENBQUM7SUFDckMsT0FBTyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLEtBQUssR0FBRyxPQUFPLENBQUM7QUFDbkQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUXVpY2tKUyBXQVNNIHdvcmtmbG93IFZNLlxuICpcbiAqIEFuIGFsdGVybmF0aXZlIGVuZ2luZSBmb3IgdGhlIGV2ZW50LXJlcGxheSBleGVjdXRpb24gbW9kZWw6IHRoZSB3b3JrZmxvd1xuICogY29kZSBydW5zIGluc2lkZSBhIFF1aWNrSlMgV0FTTSBWTSAodmlhIHF1aWNranMtd2FzaSkgaW5zdGVhZCBvZiBhXG4gKiBgbm9kZTp2bWAgY29udGV4dC4gRXZlcnkgaW52b2NhdGlvbiBjcmVhdGVzIGEgZnJlc2ggVk0sIHJlLWV4ZWN1dGVzIHRoZVxuICogd29ya2Zsb3cgZnVuY3Rpb24gZnJvbSB0aGUgdG9wLCBhbmQgcmVwbGF5cyB0aGUgcmVjb3JkZWQgZXZlbnQgbG9nIHRvXG4gKiByZXNvbHZlIGF3YWl0ZWQgcHJpbWl0aXZlcyDigJQgdGhlIHNhbWUgcmVwbGF5IHNlbWFudGljcyBhcyB0aGUgYG5vZGU6dm1gXG4gKiBlbmdpbmUuXG4gKlxuICogVGhlIHdvcmtmbG93IHByaW1pdGl2ZXMgKHVzZVN0ZXAsIHNsZWVwLCBjcmVhdGVIb29rKSBhcmUgaW1wbGVtZW50ZWQgYXNcbiAqIEphdmFTY3JpcHQgY29kZSBydW5uaW5nIGluc2lkZSB0aGUgUXVpY2tKUyBWTS4gVGhlIGhvc3QgY29tbXVuaWNhdGVzIHdpdGhcbiAqIHRoZSBWTSBieSBldmFsdWF0aW5nIHNtYWxsIEpTIHNuaXBwZXRzIHRvIHJlYWQgcGVuZGluZyBvcGVyYXRpb25zIGFuZFxuICogcmVzb2x2ZS9yZWplY3QgcHJvbWlzZXMuXG4gKlxuICogVGhlIFZNIGJvb3RzdHJhcCBpcyBkZWxpYmVyYXRlbHkgc3BsaXQgaW50byB0d28gcGhhc2VzOlxuICogICAxLiBTdGF0aWMgaW5pdGlhbGl6YXRpb24gKGBpbml0V29ya2Zsb3dWTWApIOKAlCBydW4taW5kZXBlbmRlbnQgc2V0dXA6XG4gKiAgICAgIFZNIGNyZWF0aW9uLCB0aGUgc2VyZGUgYnVuZGxlLCBhbmQgdGhlIHdvcmtmbG93IHByaW1pdGl2ZXMuXG4gKiAgIDIuIFBlci1ydW4gaW5pdGlhbGl6YXRpb24gKGlubGluZSBpbiBgcnVuUXVpY2tKU1dvcmtmbG93YCkg4oCUIHNlZWRlZFxuICogICAgICBQUk5HL1VMSUQgaG9zdCBmdW5jdGlvbnMsIHdvcmtmbG93IGJ1bmRsZSBldmFsdWF0aW9uLCBydW4gbWV0YWRhdGEsXG4gKiAgICAgIHdvcmtmbG93IGlucHV0LCBhbmQgc3RhcnQuXG4gKiBLZWVwaW5nIHRoZSBwaGFzZXMgc2VwYXJhdGUgaXMgZ3JvdW5kd29yayBmb3IgVk0tbWVtb3J5IHNuYXBzaG90dGluZzpcbiAqIGEgZm9sbG93LXVwIGNhbiBwZXJzaXN0L3Jlc3RvcmUgdGhlIFZNIGF0IHRoZSBwaGFzZSBib3VuZGFyeSAoZS5nLiBhXG4gKiBidWlsZC10aW1lIGluaXRpYWwgc25hcHNob3QpIHdpdGhvdXQgcmVzdHJ1Y3R1cmluZyB0aGlzIG1vZHVsZS4gTm90ZVxuICogdGhhdCBidW5kbGUgZXZhbHVhdGlvbiBjdXJyZW50bHkgc2l0cyBpbiB0aGUgcGVyLXJ1biBwaGFzZSBzbyB0aGF0XG4gKiBtb2R1bGUtc2NvcGUgdXNlciBjb2RlIG9ic2VydmVzIHRoZSBzZWVkZWQgYE1hdGgucmFuZG9tYCwgbWF0Y2hpbmcgdGhlXG4gKiBgbm9kZTp2bWAgZW5naW5lJ3MgcmVwbGF5IGRldGVybWluaXNtLlxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgRXZlbnQsXG4gIFJ1bklucHV0LFxuICBXb3JrZmxvd1J1bixcbiAgV29ybGRDYXBhYmlsaXRpZXMsXG59IGZyb20gJ0B3b3JrZmxvdy93b3JsZCc7XG5pbXBvcnQgKiBhcyBuYW5vaWQgZnJvbSAnbmFub2lkJztcbmltcG9ydCB7XG4gIHR5cGUgRXh0ZW5zaW9uRGVzY3JpcHRvcixcbiAgSlNFeGNlcHRpb24sXG4gIFF1aWNrSlMsXG4gIHR5cGUgV2FzaU9wdGlvbnMsXG59IGZyb20gJ3F1aWNranMtd2FzaSc7XG5pbXBvcnQgc2VlZHJhbmRvbSBmcm9tICdzZWVkcmFuZG9tJztcbmltcG9ydCB7IHJ1bnRpbWVMb2dnZXIgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgZGVjb21wcmVzcyB9IGZyb20gJy4uL3NlcmlhbGl6YXRpb24vY29tcHJlc3Npb24uanMnO1xuaW1wb3J0IHR5cGUgeyBEZWNyeXB0aW9uS2V5IH0gZnJvbSAnLi4vc2VyaWFsaXphdGlvbi9lbmNyeXB0aW9uLmpzJztcbmltcG9ydCB7IGRlY3J5cHQgfSBmcm9tICcuLi9zZXJpYWxpemF0aW9uL2VuY3J5cHRpb24uanMnO1xuaW1wb3J0IHsgZ2V0UmVwbGF5VGltZW91dE1zIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgcXVpY2tqc0V4dGVuc2lvbnMsIHF1aWNranNXYXNtIH0gZnJvbSAnLi9xdWlja2pzLWFzc2V0cy5nZW5lcmF0ZWQuanMnO1xuaW1wb3J0IHsgcnVuSWRDcmVhdGVkQXQgfSBmcm9tICcuL3J1bi1pZC10aW1lLmpzJztcbmltcG9ydCB7IFZNX1NFUkRFX0JVTkRMRSB9IGZyb20gJy4vdm0tc2VyZGUtYnVuZGxlLmdlbmVyYXRlZC5qcyc7XG5cbi8vIC0tLS0gSG9zdCAtPiBWTSBwYXlsb2FkIHByZXBhcmF0aW9uIC0tLS1cblxuLyoqXG4gKiBQcmVwYXJlIHBlcnNpc3RlZCBwYXlsb2FkIGJ5dGVzIGZvciBjb25zdW1wdGlvbiBpbnNpZGUgdGhlIFZNOiBkZWNyeXB0XG4gKiAod2hlbiBhbiBlbmNyeXB0aW9uIGtleSBpcyBjb25maWd1cmVkKSBhbmQgZGVjb21wcmVzcyAoc3BlY1ZlcnNpb24gPj0gNVxuICogcGF5bG9hZHMgbWF5IGJlIGd6aXAvenN0ZC1jb21wcmVzc2VkKS4gVGhlIFZNIG9ubHkgdW5kZXJzdGFuZHMgcGxhaW5cbiAqIGZvcm1hdC1wcmVmaXhlZCAnZGV2bCcgYnl0ZXMg4oCUIGl0IGhhcyBuZWl0aGVyIHRoZSBrZXkgbWF0ZXJpYWwgbm9yIHpsaWIuXG4gKiBUaGUga2V5IGlzIHRoZSBydW4ncyBmdWxsIERlY3J5cHRpb25LZXkgY2FwYWJpbGl0eSAoc3ltbWV0cmljIEFFUyBrZXkgK1xuICogWDI1NTE5IGtleXBhaXIpIHNvIHNlYWxlZCBgZW5jcGAgaG9vayBwYXlsb2FkcyBmcm9tIGNyb3NzLWRlcGxveW1lbnRcbiAqIHJlc3VtZUhvb2soKSBjYWxscyBvcGVuIGhlcmUgdG9vLCBub3QganVzdCBzeW1tZXRyaWMgYGVuY3JgIG9uZXMuXG4gKiBCb3RoIHN0YWdlcyBhcmUgZm9ybWF0LXByZWZpeCBkaXNwYXRjaGVkLCBzbyBwbGFpbnRleHQvdW5jb21wcmVzc2VkXG4gKiBkYXRhIHBhc3NlcyB0aHJvdWdoIHVuY2hhbmdlZC4gTWlycm9ycyBgcHJlcGFyZVJlcGxheVBheWxvYWRgIGluXG4gKiBzZXJpYWxpemF0aW9uLnRzICh0aGUgbm9kZTp2bSBlbmdpbmUncyBlcXVpdmFsZW50IGhvc3Qtc2lkZSBzdGFnZSkuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHByZXBhcmVCeXRlc0ZvclZNKFxuICBkYXRhOiBVaW50OEFycmF5LFxuICBrZXk/OiBEZWNyeXB0aW9uS2V5XG4pOiBQcm9taXNlPFVpbnQ4QXJyYXk+IHtcbiAgcmV0dXJuIChhd2FpdCBkZWNvbXByZXNzKGF3YWl0IGRlY3J5cHQoZGF0YSwga2V5KSkpIGFzIFVpbnQ4QXJyYXk7XG59XG5cbi8vIC0tLS0gVHlwZXMgLS0tLVxuXG5leHBvcnQgaW50ZXJmYWNlIFBlbmRpbmdTdGVwIHtcbiAgdHlwZTogJ3N0ZXAnO1xuICBjb3JyZWxhdGlvbklkOiBzdHJpbmc7XG4gIHN0ZXBJZDogc3RyaW5nO1xuICAvKiogRm9ybWF0LXByZWZpeGVkIGRldmFsdWUtc2VyaWFsaXplZCBzdGVwIGlucHV0IChhcmdzICsgY2xvc3VyZVZhcnMpICovXG4gIGlucHV0OiBVaW50OEFycmF5O1xuICAvKiogV2hldGhlciBhIHN0ZXBfY3JlYXRlZCBldmVudCBhbHJlYWR5IGV4aXN0cyBmb3IgdGhpcyBzdGVwICovXG4gIGhhc0NyZWF0ZWRFdmVudDogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQZW5kaW5nV2FpdCB7XG4gIHR5cGU6ICd3YWl0JztcbiAgY29ycmVsYXRpb25JZDogc3RyaW5nO1xuICAvKiogSVNPIHN0cmluZyBvZiB3aGVuIHRvIHJlc3VtZSAqL1xuICByZXN1bWVBdDogc3RyaW5nO1xuICAvKiogV2hldGhlciBhIHdhaXRfY3JlYXRlZCBldmVudCBhbHJlYWR5IGV4aXN0cyBmb3IgdGhpcyB3YWl0ICovXG4gIGhhc0NyZWF0ZWRFdmVudDogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQZW5kaW5nSG9vayB7XG4gIHR5cGU6ICdob29rJztcbiAgY29ycmVsYXRpb25JZDogc3RyaW5nO1xuICB0b2tlbjogc3RyaW5nO1xuICAvKiogRWFybGllc3QgdG9rZW4gcmV1c2UgdGltZSwgYXMgbWlsbGlzZWNvbmRzIHNpbmNlIHRoZSBVbml4IGVwb2NoLiAqL1xuICB0b2tlblJldGVudGlvblVudGlsPzogbnVtYmVyO1xuICBpc1dlYmhvb2s6IGJvb2xlYW47XG4gIG1ldGFkYXRhPzogdW5rbm93bjtcbiAgaGFzQ3JlYXRlZEV2ZW50OiBib29sZWFuO1xuICAvKipcbiAgICogVHJ1ZSBmb3IgaW50ZXJuYWwgc3lzdGVtIGhvb2tzIChlLmcuIEFib3J0Q29udHJvbGxlcidzIGhvb2spLCB3aGljaFxuICAgKiBhcmUgZXhlbXB0IGZyb20gdXNlci1ob29rIHRva2VuIG5hbWVzcGFjZSBjb25mbGljdCBjaGVja3MuXG4gICAqL1xuICBpc1N5c3RlbT86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBTZXQgd2hlbiB0aGUgd29ya2Zsb3cgY2FsbGVkIEFib3J0Q29udHJvbGxlci5hYm9ydCgpIGR1cmluZyB0aGlzXG4gICAqIGludm9jYXRpb24uIFRoZSBob3N0IG11c3QgcmVjb3JkIHRoZSBhYm9ydDogY3JlYXRlIGEgaG9va19yZWNlaXZlZFxuICAgKiBldmVudCBjYXJyeWluZyBgYWJvcnRQYXlsb2FkYCBhbmQgd3JpdGUvY2xvc2UgdGhlIGFib3J0IHN0cmVhbS5cbiAgICovXG4gIGFib3J0UmVxdWVzdGVkPzogYm9vbGVhbjtcbiAgLyoqIFZNLXNlcmlhbGl6ZWQgYHsgYWJvcnRlZDogdHJ1ZSwgcmVhc29uIH1gIHBheWxvYWQgZm9yIHRoZSBhYm9ydC4gKi9cbiAgYWJvcnRQYXlsb2FkPzogVWludDhBcnJheTtcbiAgLyoqIFNldCBieSB0aGUgY29tcGxldGlvbiBkcmFpbiB3aGVuIGEgc3lzdGVtIGhvb2sgaXMgaW1wbGljaXRseSBkaXNwb3NlZC4gKi9cbiAgZGlzcG9zZWQ/OiBib29sZWFuO1xuICAvKipcbiAgICogVHJ1ZSB3aGVuIHRoZSB3b3JrZmxvdyBpcyBhd2FpdGluZyBob29rLmdldENvbmZsaWN0KCkgZm9yIHRoaXMgaG9vay5cbiAgICogVGhlIGVudHJ5cG9pbnQgcmUtaW52b2tlcyB0aGUgd29ya2Zsb3cgcmlnaHQgYWZ0ZXIgd3JpdGluZ1xuICAgKiBob29rX2NyZWF0ZWQgc28gcmVwbGF5IGNhbiBjb25maXJtIGNyZWF0aW9uIGFuZCByZXNvbHZlIHRoZSBhd2FpdGVyLlxuICAgKi9cbiAgaGFzR2V0Q29uZmxpY3RBd2FpdGVyPzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQZW5kaW5nQXR0cmlidXRlIHtcbiAgdHlwZTogJ2F0dHJpYnV0ZSc7XG4gIGNvcnJlbGF0aW9uSWQ6IHN0cmluZztcbiAgLyoqIE5vcm1hbGl6ZWQgYXR0cmlidXRlIGNoYW5nZXMgKHBsYWluIEpTT04tYWJsZSBvYmplY3RzKSAqL1xuICBjaGFuZ2VzOiB1bmtub3duW107XG4gIGFsbG93UmVzZXJ2ZWRBdHRyaWJ1dGVzPzogYm9vbGVhbjtcbiAgLyoqIFdoZXRoZXIgYW4gYXR0cl9zZXQgZXZlbnQgYWxyZWFkeSBleGlzdHMgZm9yIHRoaXMgd3JpdGUgKi9cbiAgaGFzQ3JlYXRlZEV2ZW50OiBib29sZWFuO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFBlbmRpbmdIb29rRGlzcG9zZSB7XG4gIHR5cGU6ICdob29rX2Rpc3Bvc2UnO1xuICBjb3JyZWxhdGlvbklkOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUb2tlbiBvZiB0aGUgaG9vayBiZWluZyBkaXNwb3NlZC4gVXNlZCBieSB0aGUgZW50cnlwb2ludCB0byBvcmRlclxuICAgKiBzYW1lLXRva2VuIGhvb2sgb3BlcmF0aW9ucyBzZXF1ZW50aWFsbHkgaW4gY29kZSBvcmRlci5cbiAgICovXG4gIHRva2VuPzogc3RyaW5nO1xuICBoYXNDcmVhdGVkRXZlbnQ6IGJvb2xlYW47XG59XG5cbmV4cG9ydCB0eXBlIFBlbmRpbmdPcGVyYXRpb24gPVxuICB8IFBlbmRpbmdTdGVwXG4gIHwgUGVuZGluZ1dhaXRcbiAgfCBQZW5kaW5nSG9va1xuICB8IFBlbmRpbmdBdHRyaWJ1dGVcbiAgfCBQZW5kaW5nSG9va0Rpc3Bvc2U7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUXVpY2tKU1J1bnRpbWVSZXN1bHQge1xuICAvKiogVGhlIHdvcmtmbG93IGNvbXBsZXRlZCDigJQgcmVzdWx0IGlzIGZvcm1hdC1wcmVmaXhlZCBkZXZhbHVlIGJ5dGVzICovXG4gIGNvbXBsZXRlZD86IHtcbiAgICByZXN1bHQ6IFVpbnQ4QXJyYXk7XG4gICAgLyoqXG4gICAgICogTGVmdG92ZXIgcGVuZGluZyBvcGVyYXRpb25zIHRoYXQgc3RpbGwgbmVlZCBkdXJhYmxlIHNpZGUgZWZmZWN0cyBhdFxuICAgICAqIGNvbXBsZXRpb246IGFib3J0IHJlY29yZGluZ3MsIHN5c3RlbS1ob29rIGRpc3Bvc2FscywgZmlyZS1hbmQtZm9yZ2V0XG4gICAgICogYXR0cmlidXRlL2hvb2svc3RlcCBldmVudHMuIE1pcnJvcnMgdGhlIG5vZGU6dm0gZW5naW5lJ3NcbiAgICAgKiBkcmFpblBlbmRpbmdRdWV1ZUl0ZW1zLiBUaGUgZW50cnlwb2ludCBkaXNwYXRjaGVzIHRoZXNlIFdJVEhPVVRcbiAgICAgKiBxdWV1ZWluZyBzdGVwcyBvciByZXF1ZXVpbmcgdGhlIHJ1bi5cbiAgICAgKi9cbiAgICBkcmFpbk9wZXJhdGlvbnM/OiBQZW5kaW5nT3BlcmF0aW9uW107XG4gIH07XG4gIC8qKiBUaGUgd29ya2Zsb3cgc3VzcGVuZGVkIHdpdGggcGVuZGluZyBvcGVyYXRpb25zICovXG4gIHN1c3BlbmRlZD86IHtcbiAgICBwZW5kaW5nT3BlcmF0aW9uczogUGVuZGluZ09wZXJhdGlvbltdO1xuICB9O1xuICAvKiogVGhlIHdvcmtmbG93IGZhaWxlZCAqL1xuICBmYWlsZWQ/OiB7XG4gICAgbWVzc2FnZTogc3RyaW5nO1xuICAgIHN0YWNrPzogc3RyaW5nO1xuICAgIG5hbWU/OiBzdHJpbmc7XG4gICAgLyoqIFNlZSBjb21wbGV0ZWQuZHJhaW5PcGVyYXRpb25zIOKAlCBzYW1lIHNlbWFudGljcyBvbiBmYWlsdXJlLiAqL1xuICAgIGRyYWluT3BlcmF0aW9ucz86IFBlbmRpbmdPcGVyYXRpb25bXTtcbiAgICAvKipcbiAgICAgKiBGb3JtYXQtcHJlZml4ZWQgZGV2YWx1ZSBieXRlcyBvZiB0aGUgb3JpZ2luYWwgdGhyb3duIHZhbHVlXG4gICAgICogKEVycm9yIHN1YmNsYXNzIHdpdGggY2F1c2UgY2hhaW4sIHBsYWluIG9iamVjdCwgcHJpbWl0aXZlLCBldGMuKS5cbiAgICAgKiBTZXQgd2hlbiB0aGUgVk0tc2lkZSByZWplY3Rpb24gaGFuZGxlciBzdWNjZXNzZnVsbHkgc2VyaWFsaXplc1xuICAgICAqIHRoZSB0aHJvd24gdmFsdWUuIFRoZSBob3N0IHVzZXMgdGhlc2UgYnl0ZXMgdG8gcmVjb25zdHJ1Y3QgdGhlXG4gICAgICogb3JpZ2luYWwgdmFsdWUgdGhyb3VnaCB0aGUgc3RhbmRhcmQgZXJyb3IgaHlkcmF0aW9uIHBpcGVsaW5lLFxuICAgICAqIHByZXNlcnZpbmcgdHlwZSBpZGVudGl0eSAoVHlwZUVycm9yLCBGYXRhbEVycm9yKSBhbmQgbm9uLUVycm9yXG4gICAgICogdGhyb3dzIHZlcmJhdGltLiBGYWxscyBiYWNrIHRvIHRoZSBtZXNzYWdlL3N0YWNrL25hbWUgZmllbGRzXG4gICAgICogd2hlbiB0aGlzIGlzIHVuZGVmaW5lZCAoZS5nLiBleHRyYWN0RXJyb3IgcHNldWRvLWZhaWx1cmVzKS5cbiAgICAgKi9cbiAgICB2YWx1ZUJ5dGVzPzogVWludDhBcnJheTtcbiAgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBRdWlja0pTUnVudGltZU9wdGlvbnMge1xuICAvKiogVGhlIGNvbXBpbGVkIHdvcmtmbG93IGJ1bmRsZSBjb2RlICh3b3JrZmxvdyBtb2RlIG91dHB1dCBmcm9tIFNXQykgKi9cbiAgd29ya2Zsb3dDb2RlOiBzdHJpbmc7XG4gIC8qKiBUaGUgd29ya2Zsb3cgSUQgKGUuZy4gXCJ3b3JrZmxvdy8vLi93b3JrZmxvd3MvMV9zaW1wbGUvL3NpbXBsZVwiKSAqL1xuICB3b3JrZmxvd0lkOiBzdHJpbmc7XG4gIC8qKiBUaGUgd29ya2Zsb3cgcnVuIGVudGl0eSAqL1xuICB3b3JrZmxvd1J1bjogV29ya2Zsb3dSdW47XG4gIC8qKiBGZWF0dXJlcyBzdXBwb3J0ZWQgYnkgdGhlIFdvcmxkIGV4ZWN1dGluZyB0aGlzIHdvcmtmbG93LiAqL1xuICB3b3JsZENhcGFiaWxpdGllcz86IFdvcmxkQ2FwYWJpbGl0aWVzO1xuICAvKipcbiAgICogVGhlIGZ1bGwgZXZlbnQgbG9nIGZvciB0aGUgcnVuLiBFdmVyeSBpbnZvY2F0aW9uIHJlcGxheXMgdGhlIGNvbXBsZXRlXG4gICAqIGxvZyBmcm9tIHRoZSBzdGFydCAoc2FtZSByZXBsYXkgc2VtYW50aWNzIGFzIHRoZSBgbm9kZTp2bWAgZW5naW5lKS5cbiAgICovXG4gIGV2ZW50czogRXZlbnRbXTtcbiAgLyoqIEVuY3J5cHRpb24ga2V5IGZvciBkZWNyeXB0aW5nIGV2ZW50IHBheWxvYWRzICh1bmRlZmluZWQgaWYgdW5lbmNyeXB0ZWQpICovXG4gIGVuY3J5cHRpb25LZXk/OiBEZWNyeXB0aW9uS2V5O1xuICAvKipcbiAgICogVGhlIGxvY2FsIHBvcnQgdGhlIHdvcmtmbG93IHNlcnZlciBpcyBsaXN0ZW5pbmcgb24sIHVzZWQgdG8gcG9wdWxhdGVcbiAgICogYHdvcmtmbG93TWV0YWRhdGEudXJsYC4gUmVzb2x2ZWQgYXQgY2FsbCB0aW1lIG9uIHRoZSBob3N0IHNpZGUgc28gdGhlXG4gICAqIFZNIGRvZXNuJ3QgaGF2ZSB0byBwcm9iZSB0aGUgZmlsZXN5c3RlbS4gSWdub3JlZCBvbiBWZXJjZWwg4oCUIFZFUkNFTF9VUkxcbiAgICogdGFrZXMgcHJlY2VkZW5jZSB0aGVyZS5cbiAgICovXG4gIHBvcnQ/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBGYWxsYmFjayB3b3JrZmxvdyBpbnB1dCBmcm9tIHRoZSBxdWV1ZSBtZXNzYWdlJ3MgcmVzaWxpZW50LXN0YXJ0XG4gICAqIHBheWxvYWQuIFVzZWQgd2hlbiB0aGUgZmV0Y2hlZCBldmVudCBsb2cgbGFja3MgYSBgcnVuX2NyZWF0ZWRgIGV2ZW50XG4gICAqIChldmVudHVhbGx5LWNvbnNpc3RlbnQgcmVhZCBhZnRlciB0aGUgcGFyZW50J3Mgc3RhcnQoKSB3cm90ZSBpdCkuXG4gICAqL1xuICBydW5JbnB1dD86IFJ1bklucHV0O1xufVxuXG4vLyAtLS0tIFZNIEJvb3RzdHJhcCBDb2RlIC0tLS1cblxuLyoqXG4gKiBKYXZhU2NyaXB0IGNvZGUgdGhhdCBydW5zIGluc2lkZSB0aGUgUXVpY2tKUyBWTSB0byBzZXQgdXAgdGhlIHdvcmtmbG93XG4gKiBwcmltaXRpdmVzLiBUaGlzIHNldHMgdXA6XG4gKiAtIGdsb2JhbFRoaXMuX19wcml2YXRlX3dvcmtmbG93cyAoTWFwKSAtIHdvcmtmbG93IHJlZ2lzdHJ5XG4gKiAtIGdsb2JhbFRoaXMuX19yZXNvbHZlcnMgKE9iamVjdCkgLSBwZW5kaW5nIHByb21pc2UgcmVzb2x2ZS9yZWplY3QgZnVuY3Rpb25zXG4gKiAtIGdsb2JhbFRoaXMuX19wZW5kaW5nIChBcnJheSkgLSBtZXRhZGF0YSBhYm91dCBwZW5kaW5nIG9wZXJhdGlvbnNcbiAqIC0gZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfVVNFX1NURVBcIildIC0gc3RlcCBwcm94eSBmYWN0b3J5XG4gKiAtIGdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIldPUktGTE9XX1NMRUVQXCIpXSAtIHNsZWVwIGZ1bmN0aW9uXG4gKi9cbmNvbnN0IFZNX0JPT1RTVFJBUCA9IGBcbi8vIFN5bWJvbC5kaXNwb3NlIC8gU3ltYm9sLmFzeW5jRGlzcG9zZSBwb2x5ZmlsbHMgZm9yIFF1aWNrSlNcbmlmICh0eXBlb2YgU3ltYm9sLmRpc3Bvc2UgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgU3ltYm9sLmRpc3Bvc2UgPSBTeW1ib2wuZm9yKFwiU3ltYm9sLmRpc3Bvc2VcIik7XG59XG5pZiAodHlwZW9mIFN5bWJvbC5hc3luY0Rpc3Bvc2UgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgU3ltYm9sLmFzeW5jRGlzcG9zZSA9IFN5bWJvbC5mb3IoXCJTeW1ib2wuYXN5bmNEaXNwb3NlXCIpO1xufVxuXG5nbG9iYWxUaGlzLl9fcHJpdmF0ZV93b3JrZmxvd3MgPSBuZXcgTWFwKCk7XG5nbG9iYWxUaGlzLl9fcmVzb2x2ZXJzID0ge307XG5nbG9iYWxUaGlzLl9fcGVuZGluZyA9IFtdO1xuZ2xvYmFsVGhpcy5fX3dvcmtmbG93UmVzdWx0ID0gdW5kZWZpbmVkO1xuZ2xvYmFsVGhpcy5fX3dvcmtmbG93RXJyb3IgPSB1bmRlZmluZWQ7XG4vLyBCdWZmZXIgZm9yIGhvb2tfcmVjZWl2ZWQgcGF5bG9hZHMgdGhhdCBhcnJpdmUgYmVmb3JlIHRoZSBob29rIGlzIGF3YWl0ZWQuXG4vLyBLZXllZCBieSBjb3JyZWxhdGlvbklkIOKGkiBhcnJheSBvZiBwYXlsb2FkcyAocHJlc2VydmVzIGRlbGl2ZXJ5IG9yZGVyKS5cbi8vIFRoaXMgbWlycm9ycyB0aGUgZXZlbnQtcmVwbGF5IHJ1bnRpbWUncyBwYXlsb2Fkc1F1ZXVlIGluIGhvb2sudHMuXG5nbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXIgPSB7fTtcblxuLy8gQnVmZmVyIGZvciBzdGVwL3dhaXQvYXR0ciB0ZXJtaW5hbCBvdXRjb21lcyB0aGF0IGFycml2ZSBiZWZvcmUgdGhpcyBWTVxuLy8gaGFzIGNvbnN0cnVjdGVkIHRoZSBjb3JyZXNwb25kaW5nIGF3YWl0aW5nIHByb21pc2UuIEluIGZyZXNoLVZNIHJlcGxheVxuLy8gdGhlIG11bHRpLXBhc3MgZXZlbnQgc2NhbiBtYWtlcyB0aGlzIHVucmVhY2hhYmxlIChhd2FpdHMgYXJlXG4vLyByZWNvbnN0cnVjdGVkIGJlZm9yZSB0aGVpciB0ZXJtaW5hbHMgYXJlIHJlLXNjYW5uZWQpLCBidXQgdGhlIGxpdmVcbi8vIGNvbnRpbnVhdGlvbiBwYXRoIChjb250aW51ZVdpdGhFdmVudHMpIHNjYW5zIGVhY2ggZGVsdGEgZXhhY3RseSBvbmNlIOKAlFxuLy8gYSBjb25jdXJyZW50IGludm9jYXRpb24ncyB0ZXJtaW5hbCBhcnJpdmluZyBiZWZvcmUgdGhpcyBWTSByZWFjaGVzIHRoZVxuLy8gYXdhaXQgd291bGQgb3RoZXJ3aXNlIGJlIGRyb3BwZWQgb24gdGhlIGZsb29yIGFuZCB0aGUgYXdhaXQgd291bGRcbi8vIG5ldmVyIHNldHRsZSAodGhlIGZlZWQncyBzZWVuLXNldCBtZWFucyBpdCBpcyBuZXZlciByZS1kZWxpdmVyZWQpLlxuLy8gTWlycm9ycyBfX2hvb2tQYXlsb2FkQnVmZmVyLCB3aGljaCBleGlzdHMgZm9yIGV4YWN0bHkgdGhpcyByZWFzb24gb25cbi8vIHRoZSBob29rIHBhdGguIEtleWVkIGJ5IGNvcnJlbGF0aW9uSWQg4oaSIHNpbmdsZSB0ZXJtaW5hbCAoc3RlcHMsIHdhaXRzXG4vLyBhbmQgYXR0cnMgc2V0dGxlIGV4YWN0bHkgb25jZSkuXG5nbG9iYWxUaGlzLl9fdGVybWluYWxCdWZmZXIgPSB7fTtcblxuLy8gUmVnaXN0ZXJzIGEgcmVzb2x2ZXIgZm9yIGFuIGF3YWl0ZWQgcHJpbWl0aXZlLCBmaXJzdCBkcmFpbmluZyBhbnlcbi8vIGJ1ZmZlcmVkIHRlcm1pbmFsIHJlY29yZGVkIGZvciB0aGUgY29ycmVsYXRpb25JZC4gRW50cmllcyBhcmUgcHJlcGFyZWRcbi8vIGhvc3Qtc2lkZSAoYnl0ZXMgYWxyZWFkeSBkZWNyeXB0ZWQ7IHNlZSBwcm9jZXNzRXZlbnRzKS5cbmdsb2JhbFRoaXMuX19yZWdpc3RlclJlc29sdmVyID0gZnVuY3Rpb24oY29ycmVsYXRpb25JZCwgcmVzb2x2ZSwgcmVqZWN0KSB7XG4gIHZhciBidWZmZXJlZCA9IGdsb2JhbFRoaXMuX190ZXJtaW5hbEJ1ZmZlcltjb3JyZWxhdGlvbklkXTtcbiAgaWYgKGJ1ZmZlcmVkKSB7XG4gICAgZGVsZXRlIGdsb2JhbFRoaXMuX190ZXJtaW5hbEJ1ZmZlcltjb3JyZWxhdGlvbklkXTtcbiAgICBpZiAoYnVmZmVyZWQua2luZCA9PT0gXCJyZXNvbHZlX2J5dGVzXCIpIHtcbiAgICAgIHJlc29sdmUoZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwid29ya2Zsb3ctZGVzZXJpYWxpemVcIildKGJ1ZmZlcmVkLmJ5dGVzKSk7XG4gICAgfSBlbHNlIGlmIChidWZmZXJlZC5raW5kID09PSBcInJlc29sdmVfdmFsdWVcIikge1xuICAgICAgcmVzb2x2ZShidWZmZXJlZC52YWx1ZSk7XG4gICAgfSBlbHNlIGlmIChidWZmZXJlZC5raW5kID09PSBcInJlamVjdF9ieXRlc1wiKSB7XG4gICAgICByZWplY3QoZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwid29ya2Zsb3ctZGVzZXJpYWxpemVcIildKGJ1ZmZlcmVkLmJ5dGVzKSk7XG4gICAgfSBlbHNlIGlmIChidWZmZXJlZC5raW5kID09PSBcInJlamVjdF9lcnJvclwiKSB7XG4gICAgICB2YXIgZSA9IG5ldyBFcnJvcihidWZmZXJlZC5tZXNzYWdlKTtcbiAgICAgIGUubmFtZSA9IFwiRmF0YWxFcnJvclwiO1xuICAgICAgZS5mYXRhbCA9IHRydWU7XG4gICAgICBpZiAoYnVmZmVyZWQuc3RhY2spIGUuc3RhY2sgPSBidWZmZXJlZC5zdGFjaztcbiAgICAgIHJlamVjdChlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzb2x2ZSh1bmRlZmluZWQpO1xuICAgIH1cbiAgICByZXR1cm47XG4gIH1cbiAgZ2xvYmFsVGhpcy5fX3Jlc29sdmVyc1tjb3JyZWxhdGlvbklkXSA9IHsgcmVzb2x2ZTogcmVzb2x2ZSwgcmVqZWN0OiByZWplY3QgfTtcbn07XG5cbi8vIFN0dWJzIGZvciBXZWIgQVBJcyB0aGF0IHRoZSB3b3JrZmxvdyBidW5kbGUgbWF5IHJlZmVyZW5jZSBidXQgYXJlIG5vdFxuLy8gYXZhaWxhYmxlIGluIFF1aWNrSlMuIE5hdGl2ZSBDIGV4dGVuc2lvbnMgKGVuY29kaW5nLCBoZWFkZXJzLCB1cmwsXG4vLyBzdHJ1Y3R1cmVkLWNsb25lKSBwcm92aWRlIHRoZSByZWFsIGltcGxlbWVudGF0aW9uczsgdGhlc2UgYXJlIG1pbmltYWxcbi8vIHN0dWJzIGZvciBBUElzIHRoYXQgZG9uJ3QgaGF2ZSBuYXRpdmUgZXh0ZW5zaW9ucyB5ZXQuIChidG9hL2F0b2IgYW5kXG4vLyB0aGUgVWludDhBcnJheSBiYXNlNjQvaGV4IG1ldGhvZHMgYXJlIGJ1aWx0IGludG8gcXVpY2tqcy13YXNpID49IDMuKVxuXG5pZiAodHlwZW9mIFJlYWRhYmxlU3RyZWFtID09PSBcInVuZGVmaW5lZFwiKSB7XG4gIC8vIE1pbmltYWwgUmVhZGFibGVTdHJlYW0gdGhhdCBzdG9yZXMgYm9keSBkYXRhIGZvciBSZXNwb25zZS5qc29uKCkvdGV4dCgpXG4gIGdsb2JhbFRoaXMuUmVhZGFibGVTdHJlYW0gPSBmdW5jdGlvbigpIHt9O1xuICBnbG9iYWxUaGlzLlJlYWRhYmxlU3RyZWFtLnByb3RvdHlwZS5fX2JvZHlEYXRhID0gbnVsbDtcbn1cblxuaWYgKHR5cGVvZiBXcml0YWJsZVN0cmVhbSA9PT0gXCJ1bmRlZmluZWRcIikge1xuICBnbG9iYWxUaGlzLldyaXRhYmxlU3RyZWFtID0gZnVuY3Rpb24oKSB7fTtcbn1cblxuaWYgKHR5cGVvZiBUcmFuc2Zvcm1TdHJlYW0gPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgZ2xvYmFsVGhpcy5UcmFuc2Zvcm1TdHJlYW0gPSBmdW5jdGlvbigpIHt9O1xufVxuXG5pZiAodHlwZW9mIGNvbnNvbGUgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgZ2xvYmFsVGhpcy5jb25zb2xlID0geyBsb2c6IGZ1bmN0aW9uKCl7fSwgZXJyb3I6IGZ1bmN0aW9uKCl7fSwgd2FybjogZnVuY3Rpb24oKXt9LCBpbmZvOiBmdW5jdGlvbigpe30gfTtcbn1cbi8vIFN0dWIgZXhwb3J0cy9tb2R1bGUgZm9yIENKUyBidW5kbGUgZm9ybWF0XG5nbG9iYWxUaGlzLmV4cG9ydHMgPSB7fTtcbmdsb2JhbFRoaXMubW9kdWxlID0geyBleHBvcnRzOiBnbG9iYWxUaGlzLmV4cG9ydHMgfTtcbi8vIE5PVEU6IFRleHRFbmNvZGVyL1RleHREZWNvZGVyIGFyZSBwcm92aWRlZCBieSB0aGUgbmF0aXZlIGVuY29kaW5nIGV4dGVuc2lvbi5cblxuLy8gLS0tLSBEZXRlcm1pbmlzdGljIFxcYGNyeXB0b1xcYCAocGFyaXR5IHdpdGggdGhlIG5vZGU6dm0gZW5naW5lKSAtLS0tXG4vLyBnZXRSYW5kb21WYWx1ZXMgLyByYW5kb21VVUlEIGRyYXcgZnJvbSBNYXRoLnJhbmRvbSwgd2hpY2ggdGhlIGhvc3Rcbi8vIHJlcGxhY2VzIHdpdGggdGhlIHJ1bidzIHNlZWRlZCBQUk5HIGJlZm9yZSBhbnkgdXNlciBjb2RlIHJ1bnMg4oCUIHNvIHRoZVxuLy8gdmFsdWVzIHJlcGxheSBkZXRlcm1pbmlzdGljYWxseSBhbmQgbWF0Y2ggdGhlIG5vZGUgZW5naW5lLCB3aG9zZVxuLy8gaW1wbGVtZW50YXRpb25zIGRyYXcgZnJvbSB0aGUgc2FtZSBzZWVkZWQgc2VxdWVuY2UgKHNlZSB2bS9pbmRleC50cykuXG4vLyBFdmVyeSBjcnlwdG8uc3VidGxlIG1ldGhvZCB0aHJvd3Mgd2l0aCB0aGUgc2FtZSBndWlkYW5jZSBhcyB0aGUgbm9kZVxuLy8gZW5naW5lJ3Mgbm9uLXJlcGxheWFibGUgbWV0aG9kczsgdW5saWtlIG5vZGUsIFxcYGRpZ2VzdFxcYCBpcyBhbHNvXG4vLyB1bmF2YWlsYWJsZSBoZXJlIChubyBuYXRpdmUgaGFzaCBpbiB0aGUgVk0geWV0KS5cbihmdW5jdGlvbigpIHtcbiAgZnVuY3Rpb24gZ2V0UmFuZG9tVmFsdWVzKGFycmF5KSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKykge1xuICAgICAgYXJyYXlbaV0gPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAyNTYpO1xuICAgIH1cbiAgICByZXR1cm4gYXJyYXk7XG4gIH1cbiAgLy8gTWlycm9ycyB2bS91dWlkLnRzIGNyZWF0ZVJhbmRvbVVVSUQ6IGlkZW50aWNhbCBkcmF3IHBhdHRlcm4gZnJvbSB0aGVcbiAgLy8gc2VlZGVkIFBSTkcsIHNvIGJvdGggZW5naW5lcyBwcm9kdWNlIHRoZSBzYW1lIFVVSUQgYXQgdGhlIHNhbWUgcG9pbnRcbiAgLy8gaW4gYSByZXBsYXkuXG4gIGZ1bmN0aW9uIHJhbmRvbVVVSUQoKSB7XG4gICAgdmFyIGNoYXJzID0gXCIwMTIzNDU2Nzg5YWJjZGVmXCI7XG4gICAgdmFyIHV1aWQgPSBcIlwiO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgMzY7IGkrKykge1xuICAgICAgaWYgKGkgPT09IDggfHwgaSA9PT0gMTMgfHwgaSA9PT0gMTggfHwgaSA9PT0gMjMpIHtcbiAgICAgICAgdXVpZCArPSBcIi1cIjtcbiAgICAgIH0gZWxzZSBpZiAoaSA9PT0gMTQpIHtcbiAgICAgICAgdXVpZCArPSBcIjRcIjtcbiAgICAgIH0gZWxzZSBpZiAoaSA9PT0gMTkpIHtcbiAgICAgICAgdXVpZCArPSBjaGFyc1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiA0KSArIDhdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdXVpZCArPSBjaGFyc1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAxNildO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdXVpZDtcbiAgfVxuICBmdW5jdGlvbiBzdWJ0bGVUaHJvdyhuYW1lKSB7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVyciA9IG5ldyBFcnJvcihcIlxcYGNyeXB0by5zdWJ0bGUuXCIgKyBuYW1lICsgXCIoKVxcYCBpcyBub3QgYXZhaWxhYmxlIGluc2lkZSBhIHdvcmtmbG93IGZ1bmN0aW9uLiBNb3ZlIGl0IHRvIGEgc3RlcCBmdW5jdGlvbiB3aGVyZSBmdWxsIE5vZGUuanMgY3J5cHRvIGlzIGF2YWlsYWJsZS5cIik7XG4gICAgICBlcnIubmFtZSA9IFwiV29ya2Zsb3dSdW50aW1lRXJyb3JcIjtcbiAgICAgIHRocm93IGVycjtcbiAgICB9O1xuICB9XG4gIHZhciBzdWJ0bGUgPSB7fTtcbiAgW1wiZW5jcnlwdFwiLFwiZGVjcnlwdFwiLFwic2lnblwiLFwidmVyaWZ5XCIsXCJkaWdlc3RcIixcImdlbmVyYXRlS2V5XCIsXCJkZXJpdmVLZXlcIixcImRlcml2ZUJpdHNcIixcImltcG9ydEtleVwiLFwiZXhwb3J0S2V5XCIsXCJ3cmFwS2V5XCIsXCJ1bndyYXBLZXlcIl0uZm9yRWFjaChmdW5jdGlvbihtKSB7XG4gICAgc3VidGxlW21dID0gc3VidGxlVGhyb3cobSk7XG4gIH0pO1xuICBnbG9iYWxUaGlzLmNyeXB0byA9IHtcbiAgICBnZXRSYW5kb21WYWx1ZXM6IGdldFJhbmRvbVZhbHVlcyxcbiAgICByYW5kb21VVUlEOiByYW5kb21VVUlELFxuICAgIHN1YnRsZTogc3VidGxlLFxuICB9O1xufSkoKTtcblxuLy8gLS0tLSBMb3VkIEludGwgLyBsb2NhbGUgZ3VhcmRzIC0tLS1cbi8vIFF1aWNrSlMgaGFzIG5vIElDVTogXFxgSW50bFxcYCBpcyBhYnNlbnQgYW5kIHRvTG9jYWxlU3RyaW5nLWZhbWlseSBtZXRob2RzXG4vLyBzaWxlbnRseSBpZ25vcmUgdGhlaXIgbG9jYWxlIGFyZ3VtZW50LiBTaWxlbnQgZGl2ZXJnZW5jZSBmcm9tIHRoZSBub2RlXG4vLyBlbmdpbmUgd291bGQgd3JpdGUgZGlmZmVyZW50IHZhbHVlcyBpbnRvIGEgZHVyYWJsZSBldmVudCBsb2cgd2l0aCBub1xuLy8gZXJyb3IgYW55d2hlcmUg4oCUIHNvIG1ha2UgdGhlIGdhcCBsb3VkIGluc3RlYWQ6IEludGwgY29uc3RydWN0b3JzIHRocm93LFxuLy8gYW5kIHRvTG9jYWxlKiBtZXRob2RzIHRocm93IE9OTFkgd2hlbiBjYWxsZWQgd2l0aCBhbiBleHBsaWNpdCBsb2NhbGVcbi8vICh0aGUgbm8tYXJndW1lbnQgZm9ybXMga2VlcCBRdWlja0pTJ3MgZGVmYXVsdCBiZWhhdmlvcikuXG4oZnVuY3Rpb24oKSB7XG4gIGZ1bmN0aW9uIGludGxUaHJvdyhuYW1lKSB7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVyciA9IG5ldyBFcnJvcihcIlxcYEludGwuXCIgKyBuYW1lICsgXCJcXGAgaXMgbm90IGF2YWlsYWJsZSBpbiB0aGUgUXVpY2tKUyB3b3JrZmxvdyBlbmdpbmUgKG5vIElDVSkuIFBlcmZvcm0gbG9jYWxlLXNlbnNpdGl2ZSBmb3JtYXR0aW5nIGluIGEgc3RlcCBmdW5jdGlvbiwgb3IgdXNlIFdPUktGTE9XX1ZNPW5vZGUuXCIpO1xuICAgICAgZXJyLm5hbWUgPSBcIldvcmtmbG93UnVudGltZUVycm9yXCI7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfTtcbiAgfVxuICBpZiAodHlwZW9mIEludGwgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICB2YXIgaW50bCA9IHt9O1xuICAgIFtcIkNvbGxhdG9yXCIsXCJEYXRlVGltZUZvcm1hdFwiLFwiRGlzcGxheU5hbWVzXCIsXCJEdXJhdGlvbkZvcm1hdFwiLFwiTGlzdEZvcm1hdFwiLFwiTG9jYWxlXCIsXCJOdW1iZXJGb3JtYXRcIixcIlBsdXJhbFJ1bGVzXCIsXCJSZWxhdGl2ZVRpbWVGb3JtYXRcIixcIlNlZ21lbnRlclwiXS5mb3JFYWNoKGZ1bmN0aW9uKG4pIHtcbiAgICAgIGludGxbbl0gPSBpbnRsVGhyb3cobik7XG4gICAgfSk7XG4gICAgaW50bC5nZXRDYW5vbmljYWxMb2NhbGVzID0gaW50bFRocm93KFwiZ2V0Q2Fub25pY2FsTG9jYWxlc1wiKTtcbiAgICBnbG9iYWxUaGlzLkludGwgPSBpbnRsO1xuICB9XG4gIGZ1bmN0aW9uIGd1YXJkTG9jYWxlKHByb3RvLCBtZXRob2QpIHtcbiAgICB2YXIgb3JpZ2luYWwgPSBwcm90b1ttZXRob2RdO1xuICAgIGlmICh0eXBlb2Ygb3JpZ2luYWwgIT09IFwiZnVuY3Rpb25cIikgcmV0dXJuO1xuICAgIHByb3RvW21ldGhvZF0gPSBmdW5jdGlvbihsb2NhbGVzKSB7XG4gICAgICBpZiAobG9jYWxlcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHZhciBlcnIgPSBuZXcgRXJyb3IoXCJcXGBcIiArIG1ldGhvZCArIFwiKGxvY2FsZXMsIC4uLilcXGAgd2l0aCBhbiBleHBsaWNpdCBsb2NhbGUgaXMgbm90IHN1cHBvcnRlZCBpbiB0aGUgUXVpY2tKUyB3b3JrZmxvdyBlbmdpbmUgKG5vIElDVSkg4oCUIGl0IHdvdWxkIHNpbGVudGx5IGlnbm9yZSB0aGUgbG9jYWxlLiBGb3JtYXQgaW4gYSBzdGVwIGZ1bmN0aW9uLCBvciBjYWxsIHdpdGhvdXQgYXJndW1lbnRzIGZvciB0aGUgZW5naW5lIGRlZmF1bHQuXCIpO1xuICAgICAgICBlcnIubmFtZSA9IFwiV29ya2Zsb3dSdW50aW1lRXJyb3JcIjtcbiAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG9yaWdpbmFsLmNhbGwodGhpcyk7XG4gICAgfTtcbiAgfVxuICBndWFyZExvY2FsZShOdW1iZXIucHJvdG90eXBlLCBcInRvTG9jYWxlU3RyaW5nXCIpO1xuICBndWFyZExvY2FsZShEYXRlLnByb3RvdHlwZSwgXCJ0b0xvY2FsZVN0cmluZ1wiKTtcbiAgZ3VhcmRMb2NhbGUoRGF0ZS5wcm90b3R5cGUsIFwidG9Mb2NhbGVEYXRlU3RyaW5nXCIpO1xuICBndWFyZExvY2FsZShEYXRlLnByb3RvdHlwZSwgXCJ0b0xvY2FsZVRpbWVTdHJpbmdcIik7XG4gIGd1YXJkTG9jYWxlKFN0cmluZy5wcm90b3R5cGUsIFwidG9Mb2NhbGVMb3dlckNhc2VcIik7XG4gIGd1YXJkTG9jYWxlKFN0cmluZy5wcm90b3R5cGUsIFwidG9Mb2NhbGVVcHBlckNhc2VcIik7XG4gIC8vIGxvY2FsZUNvbXBhcmUncyBsb2NhbGVzIGFyZ3VtZW50IGlzIHRoZSBTRUNPTkQgcGFyYW1ldGVyLlxuICAoZnVuY3Rpb24oKSB7XG4gICAgdmFyIG9yaWdpbmFsID0gU3RyaW5nLnByb3RvdHlwZS5sb2NhbGVDb21wYXJlO1xuICAgIFN0cmluZy5wcm90b3R5cGUubG9jYWxlQ29tcGFyZSA9IGZ1bmN0aW9uKHRoYXQsIGxvY2FsZXMpIHtcbiAgICAgIGlmIChsb2NhbGVzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdmFyIGVyciA9IG5ldyBFcnJvcihcIlxcYGxvY2FsZUNvbXBhcmUodGhhdCwgbG9jYWxlcywgLi4uKVxcYCB3aXRoIGFuIGV4cGxpY2l0IGxvY2FsZSBpcyBub3Qgc3VwcG9ydGVkIGluIHRoZSBRdWlja0pTIHdvcmtmbG93IGVuZ2luZSAobm8gSUNVKS4gQ29tcGFyZSBpbiBhIHN0ZXAgZnVuY3Rpb24sIG9yIGNhbGwgd2l0aG91dCBhIGxvY2FsZS5cIik7XG4gICAgICAgIGVyci5uYW1lID0gXCJXb3JrZmxvd1J1bnRpbWVFcnJvclwiO1xuICAgICAgICB0aHJvdyBlcnI7XG4gICAgICB9XG4gICAgICByZXR1cm4gb3JpZ2luYWwuY2FsbCh0aGlzLCB0aGF0KTtcbiAgICB9O1xuICB9KSgpO1xufSkoKTtcblxuZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfVVNFX1NURVBcIildID0gZnVuY3Rpb24oc3RlcElkLCBjbG9zdXJlVmFyc0ZuKSB7XG4gIHZhciBmbiA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICB2YXIgY29ycmVsYXRpb25JZCA9IFwic3RlcF9cIiArIGdsb2JhbFRoaXMuX19nZW5lcmF0ZVVsaWQoKTtcbiAgICAvLyBDYXB0dXJlICd0aGlzJyBmb3IgbWV0aG9kIGludm9jYXRpb25zIChlLmcuLCBNeUNsYXNzLm1ldGhvZCgpKVxuICAgIHZhciB0aGlzVmFsID0gKHRoaXMgIT09IHVuZGVmaW5lZCAmJiB0aGlzICE9PSBudWxsICYmIHRoaXMgIT09IGdsb2JhbFRoaXMpID8gdGhpcyA6IHVuZGVmaW5lZDtcbiAgICAvLyBTZXJpYWxpemUgc3RlcCBpbnB1dCB1c2luZyB0aGUgaG9zdC1wcm92aWRlZCBkZXZhbHVlIHNlcmlhbGl6ZXIuXG4gICAgLy8gVGhpcyBwcm9kdWNlcyBhIGZvcm1hdC1wcmVmaXhlZCBVaW50OEFycmF5IChcImRldmxcIiArIGRldmFsdWUuc3RyaW5naWZ5KS5cbiAgICB2YXIgaW5wdXQgPSBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJ3b3JrZmxvdy1zZXJpYWxpemVcIildKHtcbiAgICAgIGFyZ3M6IGFyZ3MsXG4gICAgICBjbG9zdXJlVmFyczogY2xvc3VyZVZhcnNGbiA/IGNsb3N1cmVWYXJzRm4oKSA6IHVuZGVmaW5lZCxcbiAgICAgIHRoaXNWYWw6IHRoaXNWYWwsXG4gICAgfSk7XG4gICAgZ2xvYmFsVGhpcy5fX3BlbmRpbmcucHVzaCh7XG4gICAgICB0eXBlOiBcInN0ZXBcIixcbiAgICAgIGNvcnJlbGF0aW9uSWQ6IGNvcnJlbGF0aW9uSWQsXG4gICAgICBzdGVwSWQ6IHN0ZXBJZCxcbiAgICAgIGlucHV0OiBpbnB1dCxcbiAgICAgIGhhc0NyZWF0ZWRFdmVudDogZmFsc2UsXG4gICAgfSk7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgICAgZ2xvYmFsVGhpcy5fX3JlZ2lzdGVyUmVzb2x2ZXIoY29ycmVsYXRpb25JZCwgcmVzb2x2ZSwgcmVqZWN0KTtcbiAgICB9KTtcbiAgfTtcbiAgLy8gU2V0IHN0ZXBJZCBvbiB0aGUgcHJveHkgc28gdGhlIFN0ZXBGdW5jdGlvbiByZWR1Y2VyIGNhbiBkZXRlY3QgYW5kXG4gIC8vIHNlcmlhbGl6ZSBzdGVwIGZ1bmN0aW9uIHJlZmVyZW5jZXMgKGUuZy4gd2hlbiBwYXNzZWQgYXMgYXJndW1lbnRzKS5cbiAgZm4uc3RlcElkID0gc3RlcElkO1xuICBpZiAoY2xvc3VyZVZhcnNGbikgZm4uX19jbG9zdXJlVmFyc0ZuID0gY2xvc3VyZVZhcnNGbjtcbiAgLy8gT3ZlcnJpZGUgLmJpbmQgc28gYSBib3VuZCBzdGVwIHByb3h5IChlLmcuIHRoZSBTV0MgcGx1Z2luJ3NcbiAgLy8gdXNlU3RlcCguLi4pLmJpbmQodGhpcykgZm9yIGxleGljYWwtdGhpcyBhcnJvdyBzdGVwcykga2VlcHMgaXRzXG4gIC8vIHN0ZXBJZCBhbmQgcmVjb3JkcyB0aGUgYm91bmQgcmVjZWl2ZXIgLyBwcmVmaWxsZWQgYXJncyDigJQgdGhlIG5hdGl2ZVxuICAvLyBiaW5kIGRyb3BzIG93biBwcm9wZXJ0aWVzLCB3aGljaCB3b3VsZCBtYWtlIHRoZSBTdGVwRnVuY3Rpb25cbiAgLy8gcmVkdWNlciBmYWlsIHRvIHJlY29nbml6ZSB0aGUgcHJveHkgd2hlbiBpdCBjcm9zc2VzIGEgc2VyaWFsaXphdGlvblxuICAvLyBib3VuZGFyeS4gTWlycm9ycyB0aGUgbm9kZTp2bSBlbmdpbmUncyBvdmVycmlkZSBpbiBzdGVwLnRzLlxuICBmbi5iaW5kID0gZnVuY3Rpb24odGhpc0FyZykge1xuICAgIHZhciBwYXJ0aWFsQXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XG4gICAgdmFyIGJvdW5kID0gRnVuY3Rpb24ucHJvdG90eXBlLmJpbmQuYXBwbHkodGhpcywgW3RoaXNBcmddLmNvbmNhdChwYXJ0aWFsQXJncykpO1xuICAgIGJvdW5kLnN0ZXBJZCA9IHN0ZXBJZDtcbiAgICBpZiAoY2xvc3VyZVZhcnNGbikgYm91bmQuX19jbG9zdXJlVmFyc0ZuID0gY2xvc3VyZVZhcnNGbjtcbiAgICBib3VuZC5fX2JvdW5kVGhpcyA9IHRoaXNBcmc7XG4gICAgaWYgKHBhcnRpYWxBcmdzLmxlbmd0aCA+IDApIGJvdW5kLl9fYm91bmRBcmdzID0gcGFydGlhbEFyZ3M7XG4gICAgcmV0dXJuIGJvdW5kO1xuICB9O1xuICByZXR1cm4gZm47XG59O1xuXG4vLyBQYXJzZXMgYW4gXCJtc1wiIGxpYnJhcnkgc3R5bGUgZHVyYXRpb24gc3RyaW5nIGludG8gbWlsbGlzZWNvbmRzLlxuLy8gU3VwcG9ydHMgdGhlIHNhbWUgdW5pdHMgYXMgdGhlIHJlcGxheSBydW50aW1lICh3aGljaCB1c2VzIHRoZSBcIm1zXCJcbi8vIHBhY2thZ2UpOiBtcyAvIHMgLyBtIC8gaCAvIGQgLyB3IC8geSwgd2l0aCB2ZXJib3NlIGFsaWFzZXNcbi8vIChzZWNvbmRzLCBtaW51dGVzLCAuLi4pLlxuZ2xvYmFsVGhpcy5fX3BhcnNlRHVyYXRpb25NcyA9IGZ1bmN0aW9uKHN0cikge1xuICBzdHIgPSBTdHJpbmcoc3RyKTtcbiAgaWYgKHN0ci5sZW5ndGggPiAxMDApIHJldHVybiB1bmRlZmluZWQ7XG4gIHZhciBtYXRjaCA9IHN0ci5tYXRjaChcbiAgICAvXigtPyg/OlxcXFxkKyk/XFxcXC4/XFxcXGQrKSAqKG1pbGxpc2Vjb25kcz98bXNlY3M/fG1zfHNlY29uZHM/fHNlY3M/fHN8bWludXRlcz98bWlucz98bXxob3Vycz98aHJzP3xofGRheXM/fGR8d2Vla3M/fHd8eWVhcnM/fHlycz98eSk/JC9pXG4gICk7XG4gIGlmICghbWF0Y2gpIHJldHVybiB1bmRlZmluZWQ7XG4gIHZhciBuID0gcGFyc2VGbG9hdChtYXRjaFsxXSk7XG4gIHZhciB0eXBlID0gKG1hdGNoWzJdIHx8IFwibXNcIikudG9Mb3dlckNhc2UoKTtcbiAgdmFyIHMgPSAxMDAwLCBtID0gNjAgKiBzLCBoID0gNjAgKiBtLCBkID0gMjQgKiBoLCB3ID0gNyAqIGQsIHkgPSAzNjUuMjUgKiBkO1xuICBzd2l0Y2ggKHR5cGUpIHtcbiAgICBjYXNlIFwieWVhcnNcIjogY2FzZSBcInllYXJcIjogY2FzZSBcInlyc1wiOiBjYXNlIFwieXJcIjogY2FzZSBcInlcIjogcmV0dXJuIG4gKiB5O1xuICAgIGNhc2UgXCJ3ZWVrc1wiOiBjYXNlIFwid2Vla1wiOiBjYXNlIFwid1wiOiByZXR1cm4gbiAqIHc7XG4gICAgY2FzZSBcImRheXNcIjogY2FzZSBcImRheVwiOiBjYXNlIFwiZFwiOiByZXR1cm4gbiAqIGQ7XG4gICAgY2FzZSBcImhvdXJzXCI6IGNhc2UgXCJob3VyXCI6IGNhc2UgXCJocnNcIjogY2FzZSBcImhyXCI6IGNhc2UgXCJoXCI6IHJldHVybiBuICogaDtcbiAgICBjYXNlIFwibWludXRlc1wiOiBjYXNlIFwibWludXRlXCI6IGNhc2UgXCJtaW5zXCI6IGNhc2UgXCJtaW5cIjogY2FzZSBcIm1cIjogcmV0dXJuIG4gKiBtO1xuICAgIGNhc2UgXCJzZWNvbmRzXCI6IGNhc2UgXCJzZWNvbmRcIjogY2FzZSBcInNlY3NcIjogY2FzZSBcInNlY1wiOiBjYXNlIFwic1wiOiByZXR1cm4gbiAqIHM7XG4gICAgY2FzZSBcIm1pbGxpc2Vjb25kc1wiOiBjYXNlIFwibWlsbGlzZWNvbmRcIjogY2FzZSBcIm1zZWNzXCI6IGNhc2UgXCJtc2VjXCI6IGNhc2UgXCJtc1wiOiByZXR1cm4gbjtcbiAgICBkZWZhdWx0OiByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59O1xuXG5nbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJXT1JLRkxPV19TTEVFUFwiKV0gPSBmdW5jdGlvbihwYXJhbSkge1xuICB2YXIgY29ycmVsYXRpb25JZCA9IFwid2FpdF9cIiArIGdsb2JhbFRoaXMuX19nZW5lcmF0ZVVsaWQoKTtcbiAgdmFyIHJlc3VtZUF0O1xuICBpZiAodHlwZW9mIHBhcmFtID09PSBcIm51bWJlclwiKSB7XG4gICAgcmVzdW1lQXQgPSBuZXcgRGF0ZShEYXRlLm5vdygpICsgcGFyYW0pLnRvSVNPU3RyaW5nKCk7XG4gIH0gZWxzZSBpZiAodHlwZW9mIHBhcmFtID09PSBcInN0cmluZ1wiKSB7XG4gICAgdmFyIG1zID0gZ2xvYmFsVGhpcy5fX3BhcnNlRHVyYXRpb25NcyhwYXJhbSk7XG4gICAgaWYgKHR5cGVvZiBtcyA9PT0gXCJudW1iZXJcIiAmJiBpc0Zpbml0ZShtcykpIHtcbiAgICAgIHJlc3VtZUF0ID0gbmV3IERhdGUoRGF0ZS5ub3coKSArIG1zKS50b0lTT1N0cmluZygpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBOb3QgYSBkdXJhdGlvbiBzdHJpbmcg4oCUIHRyeSBhcyBhbiBhYnNvbHV0ZSBkYXRlIHN0cmluZy5cbiAgICAgIHZhciBkYXRlID0gbmV3IERhdGUocGFyYW0pO1xuICAgICAgaWYgKGlzTmFOKGRhdGUuZ2V0VGltZSgpKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIHNsZWVwIHBhcmFtZXRlcjogXCIgKyBwYXJhbSk7XG4gICAgICB9XG4gICAgICByZXN1bWVBdCA9IGRhdGUudG9JU09TdHJpbmcoKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAocGFyYW0gaW5zdGFuY2VvZiBEYXRlKSB7XG4gICAgaWYgKGlzTmFOKHBhcmFtLmdldFRpbWUoKSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgc2xlZXAgcGFyYW1ldGVyOiBcIiArIHBhcmFtKTtcbiAgICB9XG4gICAgcmVzdW1lQXQgPSBwYXJhbS50b0lTT1N0cmluZygpO1xuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBFcnJvcihcIkludmFsaWQgc2xlZXAgcGFyYW1ldGVyOiBcIiArIHBhcmFtKTtcbiAgfVxuICBnbG9iYWxUaGlzLl9fcGVuZGluZy5wdXNoKHtcbiAgICB0eXBlOiBcIndhaXRcIixcbiAgICBjb3JyZWxhdGlvbklkOiBjb3JyZWxhdGlvbklkLFxuICAgIHJlc3VtZUF0OiByZXN1bWVBdCxcbiAgICBoYXNDcmVhdGVkRXZlbnQ6IGZhbHNlLFxuICB9KTtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgIGdsb2JhbFRoaXMuX19yZWdpc3RlclJlc29sdmVyKGNvcnJlbGF0aW9uSWQsIHJlc29sdmUsIHJlamVjdCk7XG4gIH0pO1xufTtcblxuLy8gUmVzcG9uc2UvUmVxdWVzdCBwb2x5ZmlsbHMg4oCUIC5qc29uKCkvLnRleHQoKS8uYXJyYXlCdWZmZXIoKSBhcmUgdXNlU3RlcFxuLy8gcHJveGllcyB0aGF0IGV4ZWN1dGUgb24gdGhlIGhvc3Qgc2lkZS4gVGhlIHByb3hpZXMgYXJlIGFzc2lnbmVkIGRpcmVjdGx5XG4vLyB0byB0aGUgcHJvdG90eXBlcyBzbyB0aGF0ICd0aGlzJyAodGhlIFJlc3BvbnNlL1JlcXVlc3QgaW5zdGFuY2UpIGlzXG4vLyBzZXJpYWxpemVkIGFzIHRoaXNWYWwgYnkgV09SS0ZMT1dfVVNFX1NURVAsIG1hdGNoaW5nIHRoZSBldmVudC1yZXBsYXlcbi8vIHJ1bnRpbWUncyBhcHByb2FjaCAoY29tbWl0IGRjYjA3NjEpLlxuaWYgKHR5cGVvZiBSZXNwb25zZSA9PT0gXCJ1bmRlZmluZWRcIikge1xuICB2YXIgX19CT0RZX0lOSVQgPSBTeW1ib2wuZm9yKFwiQk9EWV9JTklUXCIpO1xuXG4gIGdsb2JhbFRoaXMuUmVzcG9uc2UgPSBmdW5jdGlvbihib2R5LCBpbml0KSB7XG4gICAgaW5pdCA9IGluaXQgfHwge307XG4gICAgdGhpcy5zdGF0dXMgPSBpbml0LnN0YXR1cyB8fCAyMDA7XG4gICAgdGhpcy5zdGF0dXNUZXh0ID0gaW5pdC5zdGF0dXNUZXh0IHx8IFwiXCI7XG4gICAgdGhpcy5oZWFkZXJzID0gbmV3IGdsb2JhbFRoaXMuSGVhZGVycyhpbml0LmhlYWRlcnMgfHwgW10pO1xuICAgIHRoaXMudHlwZSA9IFwiZGVmYXVsdFwiO1xuICAgIHRoaXMudXJsID0gXCJcIjtcbiAgICB0aGlzLnJlZGlyZWN0ZWQgPSBmYWxzZTtcbiAgICBpZiAoYm9keSAhPT0gbnVsbCAmJiBib2R5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuYm9keSA9IE9iamVjdC5jcmVhdGUoZ2xvYmFsVGhpcy5SZWFkYWJsZVN0cmVhbS5wcm90b3R5cGUpO1xuICAgICAgdGhpcy5ib2R5W19fQk9EWV9JTklUXSA9IGJvZHk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuYm9keSA9IG51bGw7XG4gICAgfVxuICB9O1xuICBPYmplY3QuZGVmaW5lUHJvcGVydHkoZ2xvYmFsVGhpcy5SZXNwb25zZS5wcm90b3R5cGUsIFwib2tcIiwge1xuICAgIGdldDogZnVuY3Rpb24oKSB7IHJldHVybiB0aGlzLnN0YXR1cyA+PSAyMDAgJiYgdGhpcy5zdGF0dXMgPCAzMDA7IH1cbiAgfSk7XG4gIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShnbG9iYWxUaGlzLlJlc3BvbnNlLnByb3RvdHlwZSwgXCJib2R5VXNlZFwiLCB7XG4gICAgZ2V0OiBmdW5jdGlvbigpIHsgcmV0dXJuIGZhbHNlOyB9XG4gIH0pO1xuICAvLyBBc3NpZ24gdXNlU3RlcCBwcm94aWVzIGRpcmVjdGx5IOKAlCAndGhpcycgYmluZGluZyBwcm92aWRlcyB0aGVcbiAgLy8gUmVzcG9uc2UgaW5zdGFuY2UsIHdoaWNoIGdldHMgc2VyaWFsaXplZCBhcyB0aGlzVmFsIGJ5IHRoZSBwcm94eS5cbiAgT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoZ2xvYmFsVGhpcy5SZXNwb25zZS5wcm90b3R5cGUsIHtcbiAgICBhcnJheUJ1ZmZlcjogeyB2YWx1ZTogZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfVVNFX1NURVBcIildKFwiX19idWlsdGluX3Jlc3BvbnNlX2FycmF5X2J1ZmZlclwiKSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9LFxuICAgIGpzb246IHsgdmFsdWU6IGdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIldPUktGTE9XX1VTRV9TVEVQXCIpXShcIl9fYnVpbHRpbl9yZXNwb25zZV9qc29uXCIpLCB3cml0YWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlIH0sXG4gICAgdGV4dDogeyB2YWx1ZTogZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfVVNFX1NURVBcIildKFwiX19idWlsdGluX3Jlc3BvbnNlX3RleHRcIiksIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSxcbiAgfSk7XG4gIGdsb2JhbFRoaXMuUmVzcG9uc2UucHJvdG90eXBlLmJ5dGVzID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuYXJyYXlCdWZmZXIoKS50aGVuKGZ1bmN0aW9uKGJ1ZikgeyByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoYnVmKTsgfSk7XG4gIH07XG4gIGdsb2JhbFRoaXMuUmVzcG9uc2UucHJvdG90eXBlLmNsb25lID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIHIgPSBPYmplY3QuY3JlYXRlKGdsb2JhbFRoaXMuUmVzcG9uc2UucHJvdG90eXBlKTtcbiAgICByLnN0YXR1cyA9IHRoaXMuc3RhdHVzOyByLnN0YXR1c1RleHQgPSB0aGlzLnN0YXR1c1RleHQ7XG4gICAgci5oZWFkZXJzID0gdGhpcy5oZWFkZXJzOyByLnR5cGUgPSB0aGlzLnR5cGU7XG4gICAgci51cmwgPSB0aGlzLnVybDsgci5yZWRpcmVjdGVkID0gdGhpcy5yZWRpcmVjdGVkOyByLmJvZHkgPSB0aGlzLmJvZHk7XG4gICAgcmV0dXJuIHI7XG4gIH07XG4gIGdsb2JhbFRoaXMuUmVzcG9uc2UuanNvbiA9IGZ1bmN0aW9uKGRhdGEsIGluaXQpIHtcbiAgICB2YXIgYm9keSA9IEpTT04uc3RyaW5naWZ5KGRhdGEpO1xuICAgIHZhciBoZWFkZXJzID0gbmV3IGdsb2JhbFRoaXMuSGVhZGVycyhpbml0ID8gaW5pdC5oZWFkZXJzIDogW10pO1xuICAgIGlmICghaGVhZGVycy5oYXMoXCJjb250ZW50LXR5cGVcIikpIHsgaGVhZGVycy5zZXQoXCJjb250ZW50LXR5cGVcIiwgXCJhcHBsaWNhdGlvbi9qc29uXCIpOyB9XG4gICAgcmV0dXJuIG5ldyBnbG9iYWxUaGlzLlJlc3BvbnNlKGJvZHksIHsgc3RhdHVzOiAoaW5pdCAmJiBpbml0LnN0YXR1cykgfHwgMjAwLCBzdGF0dXNUZXh0OiAoaW5pdCAmJiBpbml0LnN0YXR1c1RleHQpIHx8IFwiXCIsIGhlYWRlcnM6IGhlYWRlcnMgfSk7XG4gIH07XG59XG5pZiAodHlwZW9mIFJlcXVlc3QgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgZ2xvYmFsVGhpcy5SZXF1ZXN0ID0gZnVuY3Rpb24oaW5wdXQsIGluaXQpIHtcbiAgICBpbml0ID0gaW5pdCB8fCB7fTtcbiAgICBpZiAodHlwZW9mIGlucHV0ID09PSBcInN0cmluZ1wiKSB7IHRoaXMudXJsID0gaW5wdXQ7IH1cbiAgICBlbHNlIGlmIChpbnB1dCAmJiB0eXBlb2YgaW5wdXQgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIHRoaXMudXJsID0gaW5wdXQudXJsIHx8IFwiXCI7IHRoaXMubWV0aG9kID0gaW5wdXQubWV0aG9kO1xuICAgICAgdGhpcy5oZWFkZXJzID0gaW5wdXQuaGVhZGVyczsgdGhpcy5ib2R5ID0gaW5wdXQuYm9keTtcbiAgICB9XG4gICAgaWYgKGluaXQubWV0aG9kKSB0aGlzLm1ldGhvZCA9IGluaXQubWV0aG9kLnRvVXBwZXJDYXNlKCk7XG4gICAgaWYgKCF0aGlzLm1ldGhvZCkgdGhpcy5tZXRob2QgPSBcIkdFVFwiO1xuICAgIGlmIChpbml0LmhlYWRlcnMpIHRoaXMuaGVhZGVycyA9IG5ldyBnbG9iYWxUaGlzLkhlYWRlcnMoaW5pdC5oZWFkZXJzKTtcbiAgICBpZiAoIXRoaXMuaGVhZGVycykgdGhpcy5oZWFkZXJzID0gbmV3IGdsb2JhbFRoaXMuSGVhZGVycygpO1xuICAgIGlmIChpbml0LmJvZHkgIT09IHVuZGVmaW5lZCkgdGhpcy5ib2R5ID0gaW5pdC5ib2R5O1xuICAgIGlmICghdGhpcy5ib2R5KSB0aGlzLmJvZHkgPSBudWxsO1xuICAgIHRoaXMuZHVwbGV4ID0gaW5pdC5kdXBsZXggfHwgXCJoYWxmXCI7XG4gIH07XG4gIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShnbG9iYWxUaGlzLlJlcXVlc3QucHJvdG90eXBlLCBcImJvZHlVc2VkXCIsIHtcbiAgICBnZXQ6IGZ1bmN0aW9uKCkgeyByZXR1cm4gZmFsc2U7IH1cbiAgfSk7XG4gIE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKGdsb2JhbFRoaXMuUmVxdWVzdC5wcm90b3R5cGUsIHtcbiAgICBhcnJheUJ1ZmZlcjogeyB2YWx1ZTogZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfVVNFX1NURVBcIildKFwiX19idWlsdGluX3Jlc3BvbnNlX2FycmF5X2J1ZmZlclwiKSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9LFxuICAgIGpzb246IHsgdmFsdWU6IGdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIldPUktGTE9XX1VTRV9TVEVQXCIpXShcIl9fYnVpbHRpbl9yZXNwb25zZV9qc29uXCIpLCB3cml0YWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlIH0sXG4gICAgdGV4dDogeyB2YWx1ZTogZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfVVNFX1NURVBcIildKFwiX19idWlsdGluX3Jlc3BvbnNlX3RleHRcIiksIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSxcbiAgfSk7XG59XG5cbi8vIGNyZWF0ZUhvb2sg4oCUIHJldHVybnMgYSBIb29rIG9iamVjdCB0aGF0IGlzIGJvdGggYSBUaGVuYWJsZSBhbmQgQXN5bmNJdGVyYWJsZS5cbi8vIEVhY2ggYXdhaXQveWllbGQgY3JlYXRlcyBhIG5ldyBwcm9taXNlIGtleWVkIGJ5IHRoZSBzYW1lIGNvcnJlbGF0aW9uSWQuXG4vLyBUaGUgcHJvbWlzZSBpcyByZXNvbHZlZCB3aGVuIGEgaG9va19yZWNlaXZlZCBldmVudCBhcnJpdmVzLlxuZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfQ1JFQVRFX0hPT0tcIildID0gZnVuY3Rpb24ob3B0aW9ucykge1xuICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcbiAgaWYgKG9wdGlvbnMuaXNXZWJob29rID09PSB0cnVlICYmIG9wdGlvbnMuZXhwZXJpbWVudGFsX21pblJldGVudGlvbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdXZWJob29rIGhvb2tzIGRvIG5vdCBzdXBwb3J0IFxcYGV4cGVyaW1lbnRhbF9taW5SZXRlbnRpb25cXGAuIFVzZSBhIG5vbi13ZWJob29rIFxcYGNyZWF0ZUhvb2soKVxcYCB3aXRoIFxcYHJlc3VtZUhvb2soKVxcYC4nKTtcbiAgfVxuICBpZiAob3B0aW9ucy5leHBlcmltZW50YWxfbWluUmV0ZW50aW9uICE9PSB1bmRlZmluZWQgJiYgZ2xvYmFsVGhpcy5fX3dvcmxkQ2FwYWJpbGl0aWVzPy5ob29rUmV0ZW50aW9uPy5hY3RpdmUgIT09IHRydWUpIHtcbiAgICB2YXIgdW5zdXBwb3J0ZWRSZXRlbnRpb25FcnJvciA9IG5ldyBFcnJvcignVGhlIGNvbmZpZ3VyZWQgV29ybGQgZG9lcyBub3Qgc3VwcG9ydCBcXGBleHBlcmltZW50YWxfbWluUmV0ZW50aW9uXFxgIGZvciBIb29rcy4nKTtcbiAgICB1bnN1cHBvcnRlZFJldGVudGlvbkVycm9yLm5hbWUgPSBcIkZhdGFsRXJyb3JcIjtcbiAgICB1bnN1cHBvcnRlZFJldGVudGlvbkVycm9yLmZhdGFsID0gdHJ1ZTtcbiAgICB0aHJvdyB1bnN1cHBvcnRlZFJldGVudGlvbkVycm9yO1xuICB9XG4gIHZhciB0b2tlbiA9IG9wdGlvbnMudG9rZW4gfHwgZ2xvYmFsVGhpcy5fX2dlbmVyYXRlTmFub2lkKCk7XG4gIHZhciBjb3JyZWxhdGlvbklkID0gXCJob29rX1wiICsgZ2xvYmFsVGhpcy5fX2dlbmVyYXRlVWxpZCgpO1xuICB2YXIgaXNEaXNwb3NlZCA9IGZhbHNlO1xuICB2YXIgaGFzQ3JlYXRlZEV2ZW50ID0gZmFsc2U7XG4gIHZhciB0b2tlblJldGVudGlvblVudGlsO1xuICBpZiAob3B0aW9ucy5leHBlcmltZW50YWxfbWluUmV0ZW50aW9uICE9PSB1bmRlZmluZWQpIHtcbiAgICB2YXIgbWluUmV0ZW50aW9uID0gb3B0aW9ucy5leHBlcmltZW50YWxfbWluUmV0ZW50aW9uO1xuICAgIGlmICh0eXBlb2YgbWluUmV0ZW50aW9uID09PSBcIm51bWJlclwiKSB7XG4gICAgICBpZiAobWluUmV0ZW50aW9uIDwgMCB8fCAhaXNGaW5pdGUobWluUmV0ZW50aW9uKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIGR1cmF0aW9uOiBcIiArIG1pblJldGVudGlvbiArIFwiLiBFeHBlY3RlZCBhIG5vbi1uZWdhdGl2ZSBmaW5pdGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcy5cIik7XG4gICAgICB9XG4gICAgICB0b2tlblJldGVudGlvblVudGlsID0gRGF0ZS5ub3coKSArIG1pblJldGVudGlvbjtcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBtaW5SZXRlbnRpb24gPT09IFwic3RyaW5nXCIpIHtcbiAgICAgIHZhciByZXRlbnRpb25NcyA9IGdsb2JhbFRoaXMuX19wYXJzZUR1cmF0aW9uTXMobWluUmV0ZW50aW9uKTtcbiAgICAgIGlmICh0eXBlb2YgcmV0ZW50aW9uTXMgIT09IFwibnVtYmVyXCIgfHwgcmV0ZW50aW9uTXMgPCAwIHx8ICFpc0Zpbml0ZShyZXRlbnRpb25NcykpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGR1cmF0aW9uOiBcIicgKyBtaW5SZXRlbnRpb24gKyAnXCIuIEV4cGVjdGVkIGEgdmFsaWQgZHVyYXRpb24gc3RyaW5nIGxpa2UgXCIxc1wiLCBcIjFtXCIsIFwiMWhcIiwgZXRjLicpO1xuICAgICAgfVxuICAgICAgdG9rZW5SZXRlbnRpb25VbnRpbCA9IERhdGUubm93KCkgKyByZXRlbnRpb25NcztcbiAgICB9IGVsc2UgaWYgKG1pblJldGVudGlvbiBpbnN0YW5jZW9mIERhdGUgfHwgKG1pblJldGVudGlvbiAmJiB0eXBlb2YgbWluUmV0ZW50aW9uLmdldFRpbWUgPT09IFwiZnVuY3Rpb25cIikpIHtcbiAgICAgIC8vIEFjY2VwdCBEYXRlLWxpa2Ugb2JqZWN0cyAoYW55dGhpbmcgd2l0aCBnZXRUaW1lKSwgbWF0Y2hpbmdcbiAgICAgIC8vIHBhcnNlRHVyYXRpb25Ub0RhdGU6IHZhbHVlcyB0aGF0IGNyb3NzZWQgdGhlIHNlcmRlIGJvdW5kYXJ5IG1heVxuICAgICAgLy8gbm90IGJlIHJlYWxtLW5hdGl2ZSBEYXRlIGluc3RhbmNlcy5cbiAgICAgIHRva2VuUmV0ZW50aW9uVW50aWwgPSBtaW5SZXRlbnRpb24uZ2V0VGltZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIGR1cmF0aW9uIHBhcmFtZXRlci4gRXhwZWN0ZWQgYSBkdXJhdGlvbiBzdHJpbmcsIG51bWJlciAobWlsbGlzZWNvbmRzKSwgb3IgRGF0ZSBvYmplY3QuXCIpO1xuICAgIH1cbiAgfVxuXG4gIC8vIFJlZ2lzdGVyIGluIHBlbmRpbmcgb3BlcmF0aW9ucy5cbiAgLy8gU2VyaWFsaXplIG1ldGFkYXRhIGluc2lkZSB0aGUgVk0gc28gUmVzcG9uc2UvUmVxdWVzdCBvYmplY3RzIGFyZVxuICAvLyBwcm9wZXJseSBoYW5kbGVkIGJ5IHRoZSBkZXZhbHVlIHJlZHVjZXJzIGJlZm9yZSBjcm9zc2luZyB0aGUgYm91bmRhcnkuXG4gIHZhciBwZW5kaW5nT3AgPSB7XG4gICAgdHlwZTogXCJob29rXCIsXG4gICAgY29ycmVsYXRpb25JZDogY29ycmVsYXRpb25JZCxcbiAgICB0b2tlbjogdG9rZW4sXG4gICAgdG9rZW5SZXRlbnRpb25VbnRpbDogdG9rZW5SZXRlbnRpb25VbnRpbCxcbiAgICBpc1dlYmhvb2s6ICEhb3B0aW9ucy5pc1dlYmhvb2ssXG4gICAgbWV0YWRhdGE6IG9wdGlvbnMubWV0YWRhdGEgPyBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJ3b3JrZmxvdy1zZXJpYWxpemVcIildKG9wdGlvbnMubWV0YWRhdGEpIDogdW5kZWZpbmVkLFxuICAgIGhhc0NyZWF0ZWRFdmVudDogZmFsc2UsXG4gIH07XG4gIGdsb2JhbFRoaXMuX19wZW5kaW5nLnB1c2gocGVuZGluZ09wKTtcblxuICAvLyBQZXItaG9vayBsaWZlY3ljbGUgc3RhdGUgYmFja2luZyBob29rLmdldENvbmZsaWN0KCk6IHJlc29sdmVzIG51bGxcbiAgLy8gb25jZSBjcmVhdGlvbiBpcyBjb25maXJtZWQgKGhvb2tfY3JlYXRlZCksIG9yIHJlc29sdmVzIHdpdGggdGhlXG4gIC8vIGNvbmZsaWN0aW5nIFJ1biBoYW5kbGUgLyByZWplY3RzIHdpdGggSG9va0NvbmZsaWN0RXJyb3Igb25cbiAgLy8gaG9va19jb25mbGljdC4gU3RhdGUgdHJhbnNpdGlvbnMgYXJlIGRyaXZlbiBieSB0aGUgaG9zdCBkdXJpbmcgZXZlbnRcbiAgLy8gcHJvY2Vzc2luZyAoc2VlIHByb2Nlc3NFdmVudHMpLlxuICBnbG9iYWxUaGlzLl9faG9va3MgPSBnbG9iYWxUaGlzLl9faG9va3MgfHwge307XG4gIGdsb2JhbFRoaXMuX19ob29rc1tjb3JyZWxhdGlvbklkXSA9IHtcbiAgICB0b2tlbjogdG9rZW4sXG4gICAgY3JlYXRlZDogZmFsc2UsXG4gICAgY29uZmxpY3Q6IG51bGwsXG4gICAgZ2V0Q29uZmxpY3RSZXNvbHZlcnM6IFtdLFxuICB9O1xuXG4gIC8vIEVhY2ggYXdhaXQgY3JlYXRlcyBhIG5ldyBwcm9taXNlIGZvciB0aGUgbmV4dCBwYXlsb2FkLlxuICAvLyBUaGUgY29ycmVsYXRpb25JZCBzdGF5cyB0aGUgc2FtZSDigJQgdGhlIHJlc29sdmVyIGlzIHJlcGxhY2VkIGVhY2ggdGltZS5cbiAgZnVuY3Rpb24gY3JlYXRlSG9va1Byb21pc2UoKSB7XG4gICAgLy8gQ2hlY2sgdGhlIHBheWxvYWQgYnVmZmVyIGZpcnN0IOKAlCBpZiBhIGhvb2tfcmVjZWl2ZWQgZXZlbnQgYXJyaXZlZFxuICAgIC8vIGJlZm9yZSB0aGlzIGhvb2sgd2FzIGF3YWl0ZWQsIHRoZSBwYXlsb2FkIHdhcyBidWZmZXJlZCBpbiB0aGUgVk1cbiAgICAvLyBoZWFwLiBEcmFpbiBpdCBpbW1lZGlhdGVseSAobWF0Y2hpbmcgZXZlbnQtcmVwbGF5IHBheWxvYWRzUXVldWUpLlxuICAgIHZhciBidWYgPSBnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXJbY29ycmVsYXRpb25JZF07XG4gICAgaWYgKGJ1ZiAmJiBidWYubGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShidWYuc2hpZnQoKSk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QpIHtcbiAgICAgIGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbY29ycmVsYXRpb25JZF0gPSB7IHJlc29sdmU6IHJlc29sdmUsIHJlamVjdDogcmVqZWN0IH07XG4gICAgfSk7XG4gIH1cblxuICBmdW5jdGlvbiBkaXNwb3NlSG9vaygpIHtcbiAgICBpZiAoaXNEaXNwb3NlZCkgcmV0dXJuO1xuICAgIGlzRGlzcG9zZWQgPSB0cnVlO1xuICAgIC8vIEEgY29uZmxpY3RlZCBob29rIHdhcyBuZXZlciBjcmVhdGVkICh0aGUgd29ybGQgcmVqZWN0ZWQgaXRzIGNsYWltXG4gICAgLy8g4oCUIHRoZSB0b2tlbiBiZWxvbmdzIHRvIGFub3RoZXIgcnVuKSwgc28gdGhlcmUgaXMgbm8gZW50aXR5IHRvXG4gICAgLy8gZGlzcG9zZS4gTWlycm9ycyB0aGUgbm9kZTp2bSBlbmdpbmUsIHdoZXJlIGhvb2tfY29uZmxpY3QgcmVtb3Zlc1xuICAgIC8vIHRoZSBpbnZvY2F0aW9uLXF1ZXVlIGl0ZW0gYmVmb3JlIGRpc3Bvc2UgY2FuIG1hcmsgaXQuIEVtaXR0aW5nIGFcbiAgICAvLyBob29rX2Rpc3Bvc2VkIGhlcmUgd291bGQgYmUgcmVqZWN0ZWQgYnkgdGhlIHdvcmxkJ3NcbiAgICAvLyBob29rLWV4aXN0ZW5jZSB2YWxpZGF0aW9uLlxuICAgIHZhciBzdGF0ZSA9IGdsb2JhbFRoaXMuX19ob29rc1tjb3JyZWxhdGlvbklkXTtcbiAgICBpZiAoIXN0YXRlIHx8ICFzdGF0ZS5jb25mbGljdCkge1xuICAgICAgLy8gU2lnbmFsIHRvIHRoZSBlbnRyeXBvaW50IHRvIGNyZWF0ZSBhIGhvb2tfZGlzcG9zZWQgZXZlbnQuIFRoZVxuICAgICAgLy8gdG9rZW4gaXMgY2FycmllZCBzbyB0aGUgZW50cnlwb2ludCBjYW4gb3JkZXIgc2FtZS10b2tlbiBob29rXG4gICAgICAvLyBvcGVyYXRpb25zIHNlcXVlbnRpYWxseSAoYSBkaXNwb3NlIG11c3QgcmVsZWFzZSB0aGUgdG9rZW4gYmVmb3JlXG4gICAgICAvLyBhIGxhdGVyIHNhbWUtdG9rZW4gaG9vaydzIGNyZWF0aW9uIGlzIHZhbGlkYXRlZCkuXG4gICAgICBnbG9iYWxUaGlzLl9fcGVuZGluZy5wdXNoKHtcbiAgICAgICAgdHlwZTogXCJob29rX2Rpc3Bvc2VcIixcbiAgICAgICAgY29ycmVsYXRpb25JZDogY29ycmVsYXRpb25JZCxcbiAgICAgICAgdG9rZW46IHRva2VuLFxuICAgICAgICBoYXNDcmVhdGVkRXZlbnQ6IGZhbHNlLFxuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIElmIHRoZXJlJ3MgYSBwZW5kaW5nIHJlc29sdmVyLCByZXNvbHZlIGl0IHdpdGggdW5kZWZpbmVkIHRvIGJyZWFrIHRoZSBpdGVyYXRvclxuICAgIGlmIChnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzW2NvcnJlbGF0aW9uSWRdKSB7XG4gICAgICBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzW2NvcnJlbGF0aW9uSWRdLnJlc29sdmUodW5kZWZpbmVkKTtcbiAgICAgIGRlbGV0ZSBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzW2NvcnJlbGF0aW9uSWRdO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGdldENvbmZsaWN0KCkge1xuICAgIHZhciBzdGF0ZSA9IGdsb2JhbFRoaXMuX19ob29rc1tjb3JyZWxhdGlvbklkXTtcbiAgICBpZiAoc3RhdGUuY29uZmxpY3QpIHtcbiAgICAgIHJldHVybiBzdGF0ZS5jb25mbGljdC5ydW5cbiAgICAgICAgPyBQcm9taXNlLnJlc29sdmUoc3RhdGUuY29uZmxpY3QucnVuKVxuICAgICAgICA6IFByb21pc2UucmVqZWN0KHN0YXRlLmNvbmZsaWN0LmVycm9yKTtcbiAgICB9XG4gICAgaWYgKHN0YXRlLmNyZWF0ZWQpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbCk7XG4gICAgfVxuICAgIC8vIENyZWF0aW9uIG5vdCB5ZXQgY29uZmlybWVkIGJ5IHRoZSBldmVudCBsb2cg4oCUIHBhcmsgdGhlIGF3YWl0ZXIgYW5kXG4gICAgLy8gZmxhZyB0aGUgcGVuZGluZyBvcCBzbyB0aGUgZW50cnlwb2ludCByZS1pbnZva2VzIHRoZSB3b3JrZmxvdyByaWdodFxuICAgIC8vIGFmdGVyIHdyaXRpbmcgaG9va19jcmVhdGVkIChub3RoaW5nIGV4dGVybmFsIHJlc3VtZXMgYSBnZXRDb25mbGljdFxuICAgIC8vIGF3YWl0ZXI7IGNvbmZpcm1hdGlvbiBvbmx5IGNvbWVzIGZyb20gcmVwbGF5aW5nIHRoZSBuZXcgZXZlbnQpLlxuICAgIHBlbmRpbmdPcC5oYXNHZXRDb25mbGljdEF3YWl0ZXIgPSB0cnVlO1xuICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QpIHtcbiAgICAgIHN0YXRlLmdldENvbmZsaWN0UmVzb2x2ZXJzLnB1c2goeyByZXNvbHZlOiByZXNvbHZlLCByZWplY3Q6IHJlamVjdCB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIHZhciBob29rID0ge1xuICAgIHRva2VuOiB0b2tlbixcbiAgICB0aGVuOiBmdW5jdGlvbihvbkZ1bGZpbGxlZCwgb25SZWplY3RlZCkge1xuICAgICAgcmV0dXJuIGNyZWF0ZUhvb2tQcm9taXNlKCkudGhlbihvbkZ1bGZpbGxlZCwgb25SZWplY3RlZCk7XG4gICAgfSxcbiAgICBnZXRDb25mbGljdDogZ2V0Q29uZmxpY3QsXG4gICAgZGlzcG9zZTogZGlzcG9zZUhvb2ssXG4gIH07XG5cbiAgLy8gU3ltYm9sLmRpc3Bvc2UgZm9yIGV4cGxpY2l0IHJlc291cmNlIG1hbmFnZW1lbnRcbiAgaG9va1tTeW1ib2wuZGlzcG9zZV0gPSBkaXNwb3NlSG9vaztcblxuICAvLyBBc3luY0l0ZXJhYmxlIOKAlCB5aWVsZHMgcGF5bG9hZHMgdW50aWwgZGlzcG9zZWRcbiAgaG9va1tTeW1ib2wuYXN5bmNJdGVyYXRvcl0gPSBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbmV4dDogZnVuY3Rpb24oKSB7XG4gICAgICAgIGlmIChpc0Rpc3Bvc2VkKSB7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh7IGRvbmU6IHRydWUsIHZhbHVlOiB1bmRlZmluZWQgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNyZWF0ZUhvb2tQcm9taXNlKCkudGhlbihmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgICAgIC8vIElmIGRpc3Bvc2VkIHdoaWxlIHdhaXRpbmcsIHNpZ25hbCBkb25lXG4gICAgICAgICAgaWYgKGlzRGlzcG9zZWQpIHJldHVybiB7IGRvbmU6IHRydWUsIHZhbHVlOiB1bmRlZmluZWQgfTtcbiAgICAgICAgICByZXR1cm4geyBkb25lOiBmYWxzZSwgdmFsdWU6IHZhbHVlIH07XG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICAgIHJldHVybjogZnVuY3Rpb24oKSB7XG4gICAgICAgIGRpc3Bvc2VIb29rKCk7XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoeyBkb25lOiB0cnVlLCB2YWx1ZTogdW5kZWZpbmVkIH0pO1xuICAgICAgfSxcbiAgICB9O1xuICB9O1xuXG4gIHJldHVybiBob29rO1xufTtcblxuLy8gc2V0QXR0cmlidXRlcyDigJQgYXR0YWNoZXMgcGxhaW50ZXh0IG1ldGFkYXRhIHRvIHRoZSBjdXJyZW50IHJ1bi5cbi8vIFZhbGlkYXRpb24gaGFwcGVucyBpbiBsaWJyYXJ5IGNvZGUgKG5vcm1hbGl6ZUF0dHJpYnV0ZUNoYW5nZXMpIGJlZm9yZVxuLy8gdGhpcyBkaXNwYXRjaGVyIGlzIGludm9rZWQsIHNvIFwiY2hhbmdlc1wiIGlzIGFscmVhZHkgbm9ybWFsaXplZC4gVGhlXG4vLyByZXR1cm5lZCBwcm9taXNlIHJlc29sdmVzIHdoZW4gdGhlIG1hdGNoaW5nIGF0dHJfc2V0IGV2ZW50IGlzXG4vLyBvYnNlcnZlZCBkdXJpbmcgZXZlbnQgcHJvY2Vzc2luZyDigJQgbWlycm9yaW5nIHRoZSBub2RlOnZtIGVuZ2luZSdzXG4vLyBjcmVhdGVTZXRBdHRyaWJ1dGVzIChhdHRyaWJ1dGUtZGlzcGF0Y2hlci50cykuXG5nbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJXT1JLRkxPV19TRVRfQVRUUklCVVRFU1wiKV0gPSBmdW5jdGlvbihjaGFuZ2VzLCBvcHRpb25zKSB7XG4gIHZhciBjb3JyZWxhdGlvbklkID0gXCJhdHRyX1wiICsgZ2xvYmFsVGhpcy5fX2dlbmVyYXRlVWxpZCgpO1xuICBnbG9iYWxUaGlzLl9fcGVuZGluZy5wdXNoKHtcbiAgICB0eXBlOiBcImF0dHJpYnV0ZVwiLFxuICAgIGNvcnJlbGF0aW9uSWQ6IGNvcnJlbGF0aW9uSWQsXG4gICAgY2hhbmdlczogY2hhbmdlcyxcbiAgICBhbGxvd1Jlc2VydmVkQXR0cmlidXRlczogISEob3B0aW9ucyAmJiBvcHRpb25zLmFsbG93UmVzZXJ2ZWRBdHRyaWJ1dGVzKSxcbiAgICBoYXNDcmVhdGVkRXZlbnQ6IGZhbHNlLFxuICB9KTtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgIGdsb2JhbFRoaXMuX19yZWdpc3RlclJlc29sdmVyKGNvcnJlbGF0aW9uSWQsIHJlc29sdmUsIHJlamVjdCk7XG4gIH0pO1xufTtcblxuLy8gLS0tLSBBYm9ydENvbnRyb2xsZXIgLyBBYm9ydFNpZ25hbCAoaG9vay1iYWNrZWQpIC0tLS1cbi8vIFBvcnQgb2Ygd29ya2Zsb3cvYWJvcnQtY29udHJvbGxlci50cyB0byB0aGUgVk0gcGVuZGluZy1vcCBtb2RlbDpcbi8vIHRoZSBjb250cm9sbGVyIHJlZ2lzdGVycyBhIHN5c3RlbSBob29rOyBhYm9ydCgpIGZsaXBzIHRoZSBzaWduYWxcbi8vIHN5bmNocm9ub3VzbHkgYW5kIG1hcmtzIHRoZSBwZW5kaW5nIG9wIHNvIHRoZSBob3N0IHJlY29yZHMgdGhlIGFib3J0XG4vLyAoaG9va19yZWNlaXZlZCBldmVudCArIHN0cmVhbSBwYWNrZXQpLiBPbiByZXBsYXksIHRoZSByZWNvcmRlZFxuLy8gaG9va19yZWNlaXZlZCBldmVudCBjYWxscyBfc2V0QWJvcnRlZCBkdXJpbmcgZXZlbnQgcHJvY2Vzc2luZyBhbmQgdGhlXG4vLyB3b3JrZmxvdydzIG93biBhYm9ydCgpIGNhbGwgYmVjb21lcyBhIG5vLW9wLlxudmFyIF9fQUJPUlRfU1RSRUFNX05BTUUgPSBTeW1ib2wuZm9yKFwiV09SS0ZMT1dfQUJPUlRfU1RSRUFNX05BTUVcIik7XG52YXIgX19BQk9SVF9IT09LX1RPS0VOID0gU3ltYm9sLmZvcihcIldPUktGTE9XX0FCT1JUX0hPT0tfVE9LRU5cIik7XG5cbmZ1bmN0aW9uIF9fbWFrZUFib3J0RXJyb3IoKSB7XG4gIGlmICh0eXBlb2YgRE9NRXhjZXB0aW9uICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgcmV0dXJuIG5ldyBET01FeGNlcHRpb24oXCJUaGUgb3BlcmF0aW9uIHdhcyBhYm9ydGVkLlwiLCBcIkFib3J0RXJyb3JcIik7XG4gIH1cbiAgdmFyIGUgPSBuZXcgRXJyb3IoXCJUaGUgb3BlcmF0aW9uIHdhcyBhYm9ydGVkLlwiKTtcbiAgZS5uYW1lID0gXCJBYm9ydEVycm9yXCI7XG4gIHJldHVybiBlO1xufVxuXG5mdW5jdGlvbiBXb3JrZmxvd0Fib3J0U2lnbmFsKHN0cmVhbU5hbWUsIGhvb2tUb2tlbikge1xuICB0aGlzLmFib3J0ZWQgPSBmYWxzZTtcbiAgdGhpcy5yZWFzb24gPSB1bmRlZmluZWQ7XG4gIHRoaXNbX19BQk9SVF9TVFJFQU1fTkFNRV0gPSBzdHJlYW1OYW1lO1xuICB0aGlzW19fQUJPUlRfSE9PS19UT0tFTl0gPSBob29rVG9rZW47XG4gIHRoaXMuX19saXN0ZW5lcnMgPSBbXTtcbiAgdGhpcy5fX29uYWJvcnQgPSBudWxsO1xufVxuT2JqZWN0LmRlZmluZVByb3BlcnR5KFdvcmtmbG93QWJvcnRTaWduYWwucHJvdG90eXBlLCBcIm9uYWJvcnRcIiwge1xuICBnZXQ6IGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpcy5fX29uYWJvcnQ7IH0sXG4gIHNldDogZnVuY3Rpb24oaGFuZGxlcikge1xuICAgIHRoaXMuX19vbmFib3J0ID0gaGFuZGxlcjtcbiAgICBpZiAoaGFuZGxlciAmJiB0aGlzLmFib3J0ZWQpIGhhbmRsZXIuY2FsbCh0aGlzKTtcbiAgfSxcbn0pO1xuV29ya2Zsb3dBYm9ydFNpZ25hbC5wcm90b3R5cGUuX3NldEFib3J0ZWQgPSBmdW5jdGlvbihyZWFzb24pIHtcbiAgaWYgKHRoaXMuYWJvcnRlZCkgcmV0dXJuO1xuICB0aGlzLmFib3J0ZWQgPSB0cnVlO1xuICB0aGlzLnJlYXNvbiA9IHJlYXNvbjtcbiAgaWYgKHRoaXMuX19vbmFib3J0KSB0aGlzLl9fb25hYm9ydC5jYWxsKHRoaXMpO1xuICB2YXIgbGlzdGVuZXJzID0gdGhpcy5fX2xpc3RlbmVycztcbiAgdGhpcy5fX2xpc3RlbmVycyA9IFtdO1xuICBmb3IgKHZhciBpID0gMDsgaSA8IGxpc3RlbmVycy5sZW5ndGg7IGkrKykgbGlzdGVuZXJzW2ldKCk7XG59O1xuV29ya2Zsb3dBYm9ydFNpZ25hbC5wcm90b3R5cGUuYWRkRXZlbnRMaXN0ZW5lciA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIGlmICh0eXBlICE9PSBcImFib3J0XCIpIHJldHVybjtcbiAgaWYgKHRoaXMuYWJvcnRlZCkge1xuICAgIC8vIEZpcmUgc3luY2hyb25vdXNseSAobm90IG9uIGEgbWljcm90YXNrKSBmb3IgZGV0ZXJtaW5pc3RpYyByZXBsYXkg4oCUXG4gICAgLy8gbWF0Y2hlcyB0aGUgbm9kZTp2bSBlbmdpbmUncyBXb3JrZmxvd0Fib3J0U2lnbmFsLlxuICAgIGxpc3RlbmVyKCk7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMuX19saXN0ZW5lcnMucHVzaChsaXN0ZW5lcik7XG59O1xuV29ya2Zsb3dBYm9ydFNpZ25hbC5wcm90b3R5cGUucmVtb3ZlRXZlbnRMaXN0ZW5lciA9IGZ1bmN0aW9uKHR5cGUsIGxpc3RlbmVyKSB7XG4gIGlmICh0eXBlICE9PSBcImFib3J0XCIpIHJldHVybjtcbiAgdGhpcy5fX2xpc3RlbmVycyA9IHRoaXMuX19saXN0ZW5lcnMuZmlsdGVyKGZ1bmN0aW9uKGwpIHsgcmV0dXJuIGwgIT09IGxpc3RlbmVyOyB9KTtcbn07XG5Xb3JrZmxvd0Fib3J0U2lnbmFsLnByb3RvdHlwZS50aHJvd0lmQWJvcnRlZCA9IGZ1bmN0aW9uKCkge1xuICBpZiAodGhpcy5hYm9ydGVkKSB7XG4gICAgdGhyb3cgdGhpcy5yZWFzb24gIT09IHVuZGVmaW5lZCAmJiB0aGlzLnJlYXNvbiAhPT0gbnVsbFxuICAgICAgPyB0aGlzLnJlYXNvblxuICAgICAgOiBfX21ha2VBYm9ydEVycm9yKCk7XG4gIH1cbn07XG4vLyBFeHBvc2UgZm9yIHRoZSBzZXJkZSBidW5kbGUncyByZXZpdmVycyAoZXZhbHVhdGVkIGJlZm9yZSB0aGlzIGJvb3RzdHJhcDtcbi8vIHRoZXkgbG9vayB0aGUgY2xhc3MgdXAgbGF6aWx5IGF0IHJldml2ZSB0aW1lKS5cbmdsb2JhbFRoaXMuX19Xb3JrZmxvd0Fib3J0U2lnbmFsID0gV29ya2Zsb3dBYm9ydFNpZ25hbDtcblxuLy8gUmVnaXN0cnkgb2YgbGl2ZSBhYm9ydCBzaWduYWxzIGtleWVkIGJ5IHRoZWlyIGhvb2sgY29ycmVsYXRpb25JZC4gVGhlXG4vLyBob3N0IGRlbGl2ZXJzIGhvb2tfcmVjZWl2ZWQgZXZlbnRzIGZvciB0aGVzZSBpZHMgYXMgX3NldEFib3J0ZWQgY2FsbHMuXG5nbG9iYWxUaGlzLl9fYWJvcnRTaWduYWxzID0ge307XG5cbmdsb2JhbFRoaXMuQWJvcnRDb250cm9sbGVyID0gZnVuY3Rpb24gV29ya2Zsb3dBYm9ydENvbnRyb2xsZXIoKSB7XG4gIHZhciBpZCA9IGdsb2JhbFRoaXMuX19nZW5lcmF0ZVVsaWQoKTtcbiAgdmFyIHN0cmVhbU5hbWUgPSBcInN0cm1fXCIgKyBpZCArIFwiX3N5c3RlbV9hYm9ydFwiO1xuICB2YXIgaG9va1Rva2VuID0gXCJhYnJ0X1wiICsgaWQ7XG4gIHRoaXNbX19BQk9SVF9TVFJFQU1fTkFNRV0gPSBzdHJlYW1OYW1lO1xuICB0aGlzW19fQUJPUlRfSE9PS19UT0tFTl0gPSBob29rVG9rZW47XG4gIHRoaXMuc2lnbmFsID0gbmV3IFdvcmtmbG93QWJvcnRTaWduYWwoc3RyZWFtTmFtZSwgaG9va1Rva2VuKTtcbiAgdmFyIGNvcnJlbGF0aW9uSWQgPSBcImhvb2tfXCIgKyBnbG9iYWxUaGlzLl9fZ2VuZXJhdGVVbGlkKCk7XG4gIC8vIFJlZ2lzdGVyIGFuIGludGVybmFsIHN5c3RlbSBob29rLiBpc1N5c3RlbSBwcmV2ZW50cyB0b2tlbiBuYW1lc3BhY2VcbiAgLy8gY29uZmxpY3RzIHdpdGggdXNlciBob29rcy5cbiAgZ2xvYmFsVGhpcy5fX3BlbmRpbmcucHVzaCh7XG4gICAgdHlwZTogXCJob29rXCIsXG4gICAgY29ycmVsYXRpb25JZDogY29ycmVsYXRpb25JZCxcbiAgICB0b2tlbjogaG9va1Rva2VuLFxuICAgIGlzV2ViaG9vazogZmFsc2UsXG4gICAgaXNTeXN0ZW06IHRydWUsXG4gICAgaGFzQ3JlYXRlZEV2ZW50OiBmYWxzZSxcbiAgfSk7XG4gIGdsb2JhbFRoaXMuX19hYm9ydFNpZ25hbHNbY29ycmVsYXRpb25JZF0gPSB0aGlzLnNpZ25hbDtcbn07XG5nbG9iYWxUaGlzLkFib3J0Q29udHJvbGxlci5wcm90b3R5cGUuYWJvcnQgPSBmdW5jdGlvbihyZWFzb24pIHtcbiAgaWYgKHRoaXMuc2lnbmFsLmFib3J0ZWQpIHJldHVybjsgLy8gYWxyZWFkeSBhYm9ydGVkIChlLmcuIGZyb20gcmVwbGF5KVxuICB0aGlzLnNpZ25hbC5fc2V0QWJvcnRlZChyZWFzb24pO1xuICAvLyBNYXJrIHRoZSBwZW5kaW5nIGhvb2sgb3Agc28gdGhlIGhvc3QgcmVjb3JkcyB0aGUgYWJvcnQuIFRoZSBwYXlsb2FkXG4gIC8vIGlzIHNlcmlhbGl6ZWQgaW4gdGhlIFZNIHNvIHRoZSByZWFzb24gY3Jvc3NlcyB0aGUgYm91bmRhcnkgd2l0aFxuICAvLyB0eXBlIGZpZGVsaXR5IChFcnJvcnMsIERPTUV4Y2VwdGlvbiwgY3VzdG9tIHZhbHVlcykuXG4gIHZhciB0b2tlbiA9IHRoaXNbX19BQk9SVF9IT09LX1RPS0VOXTtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBnbG9iYWxUaGlzLl9fcGVuZGluZy5sZW5ndGg7IGkrKykge1xuICAgIHZhciBpdGVtID0gZ2xvYmFsVGhpcy5fX3BlbmRpbmdbaV07XG4gICAgaWYgKGl0ZW0udHlwZSA9PT0gXCJob29rXCIgJiYgaXRlbS50b2tlbiA9PT0gdG9rZW4pIHtcbiAgICAgIGl0ZW0uYWJvcnRSZXF1ZXN0ZWQgPSB0cnVlO1xuICAgICAgaXRlbS5hYm9ydFBheWxvYWQgPSBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJ3b3JrZmxvdy1zZXJpYWxpemVcIildKHtcbiAgICAgICAgYWJvcnRlZDogdHJ1ZSxcbiAgICAgICAgcmVhc29uOiByZWFzb24sXG4gICAgICB9KTtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxufTtcblxuZ2xvYmFsVGhpcy5BYm9ydFNpZ25hbCA9IHtcbiAgYWJvcnQ6IGZ1bmN0aW9uKHJlYXNvbikge1xuICAgIHZhciBzID0gbmV3IFdvcmtmbG93QWJvcnRTaWduYWwoXCJcIiwgXCJcIik7XG4gICAgcy5fc2V0QWJvcnRlZChyZWFzb24gIT09IHVuZGVmaW5lZCA/IHJlYXNvbiA6IF9fbWFrZUFib3J0RXJyb3IoKSk7XG4gICAgcmV0dXJuIHM7XG4gIH0sXG4gIGFueTogZnVuY3Rpb24oc2lnbmFscykge1xuICAgIHZhciBjb21wb3NpdGUgPSBuZXcgV29ya2Zsb3dBYm9ydFNpZ25hbChcIlwiLCBcIlwiKTtcbiAgICB2YXIgYXJyID0gQXJyYXkuZnJvbShzaWduYWxzKTtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKGFycltpXS5hYm9ydGVkKSB7XG4gICAgICAgIGNvbXBvc2l0ZS5fc2V0QWJvcnRlZChhcnJbaV0ucmVhc29uKTtcbiAgICAgICAgcmV0dXJuIGNvbXBvc2l0ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgdmFyIGxpc3RlbmVycyA9IFtdO1xuICAgIHZhciBjbGVhbnVwID0gZnVuY3Rpb24oKSB7XG4gICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxpc3RlbmVycy5sZW5ndGg7IGorKykge1xuICAgICAgICBpZiAobGlzdGVuZXJzW2pdLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKSB7XG4gICAgICAgICAgbGlzdGVuZXJzW2pdLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgbGlzdGVuZXJzW2pdLmxpc3RlbmVyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgbGlzdGVuZXJzLmxlbmd0aCA9IDA7XG4gICAgfTtcbiAgICBhcnIuZm9yRWFjaChmdW5jdGlvbihzaWduYWwpIHtcbiAgICAgIGlmICghc2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIpIHJldHVybjtcbiAgICAgIHZhciBsaXN0ZW5lciA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoIWNvbXBvc2l0ZS5hYm9ydGVkKSB7XG4gICAgICAgICAgY29tcG9zaXRlLl9zZXRBYm9ydGVkKHNpZ25hbC5yZWFzb24pO1xuICAgICAgICAgIGNsZWFudXAoKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGxpc3RlbmVycy5wdXNoKHsgc2lnbmFsOiBzaWduYWwsIGxpc3RlbmVyOiBsaXN0ZW5lciB9KTtcbiAgICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgbGlzdGVuZXIpO1xuICAgIH0pO1xuICAgIHJldHVybiBjb21wb3NpdGU7XG4gIH0sXG4gIHRpbWVvdXQ6IGZ1bmN0aW9uKCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIFwiQWJvcnRTaWduYWwudGltZW91dCgpIGlzIG5vdCBzdXBwb3J0ZWQgaW4gd29ya2Zsb3cgZnVuY3Rpb25zLiBcIiArXG4gICAgICAgIFwiVXNlIHNsZWVwKCkgd2l0aCBhbiBBYm9ydENvbnRyb2xsZXIgaW5zdGVhZC4gXCIgK1xuICAgICAgICBcIlNlZTogL2RvY3MvZXJyb3JzL2Fib3J0LXNpZ25hbC10aW1lb3V0LWluLXdvcmtmbG93XCJcbiAgICApO1xuICB9LFxufTtcblxuLy8gV09SS0ZMT1dfR0VUX1NUUkVBTV9JRCDigJQgZ2VuZXJhdGVzIGEgc3RyZWFtIElEIGZvciBhIHdvcmtmbG93IHJ1bi5cbi8vIFJlcGxpY2F0ZXMgZ2V0V29ya2Zsb3dSdW5TdHJlYW1JZCgpIGZyb20gdXRpbC50cyBpbnNpZGUgdGhlIFF1aWNrSlMgVk0uXG4vLyBVc2VzIHRoZSBidWlsdC1pbiBidG9hKCkgZm9yIGJhc2U2NHVybCBlbmNvZGluZy5cbmdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIldPUktGTE9XX0dFVF9TVFJFQU1fSURcIildID0gZnVuY3Rpb24obmFtZXNwYWNlKSB7XG4gIHZhciBydW5JZCA9IGdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIldPUktGTE9XX0NPTlRFWFRcIildXG4gICAgPyBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJXT1JLRkxPV19DT05URVhUXCIpXS53b3JrZmxvd1J1bklkXG4gICAgOiBcIlwiO1xuICB2YXIgc3RyZWFtSWQgPSBydW5JZC5yZXBsYWNlKFwid3J1bl9cIiwgXCJzdHJtX1wiKSArIFwiX3VzZXJcIjtcbiAgaWYgKCFuYW1lc3BhY2UpIHJldHVybiBzdHJlYW1JZDtcbiAgLy8gYmFzZTY0dXJsOiBidG9hIHRoZW4gcmVwbGFjZSArIHdpdGggLSwgLyB3aXRoIF8sIHN0cmlwID1cbiAgdmFyIGI2NCA9IGJ0b2EobmFtZXNwYWNlKS5yZXBsYWNlKC9cXFxcKy9nLCBcIi1cIikucmVwbGFjZSgvXFxcXC8vZywgXCJfXCIpLnJlcGxhY2UoLz0rJC8sIFwiXCIpO1xuICByZXR1cm4gc3RyZWFtSWQgKyBcIl9cIiArIGI2NDtcbn07XG5gO1xuXG4vLyAtLS0tIFJ1bnRpbWUgLS0tLVxuXG4vKipcbiAqIFBoYXNlIDEg4oCUIHN0YXRpYyAocnVuLWluZGVwZW5kZW50KSBWTSBpbml0aWFsaXphdGlvbi5cbiAqXG4gKiBDcmVhdGVzIGEgUXVpY2tKUyBWTSBhbmQgbG9hZHMgZXZlcnl0aGluZyB0aGF0IGRvZXMgbm90IGRlcGVuZCBvbiBhXG4gKiBzcGVjaWZpYyB3b3JrZmxvdyBydW46IHRoZSBzZXJkZSBidW5kbGUgKGRldmFsdWUtYmFzZWQgc2VyaWFsaXphdGlvblxuICogdXNlZCBhdCB0aGUgaG9zdC9WTSBib3VuZGFyeSkgYW5kIHRoZSB3b3JrZmxvdy1wcmltaXRpdmUgYm9vdHN0cmFwXG4gKiAodXNlU3RlcCAvIHNsZWVwIC8gY3JlYXRlSG9vayAvIFJlc3BvbnNlLVJlcXVlc3QgcG9seWZpbGxzKS5cbiAqXG4gKiBgZ2V0Tm93TXNgIGJhY2tzIHRoZSBWTSdzIFdBU0kgY2xvY2sgKGBEYXRlLm5vdygpYCAvIGBuZXcgRGF0ZSgpYFxuICogaW5zaWRlIHRoZSBWTSkuIFRoZSBjYWxsYmFjayBpdHNlbGYgaXMgc3RhdGljIOKAlCB0aGUgcGVyLXJ1biBzdGF0ZSBpdFxuICogcmVhZHMgbGl2ZXMgb24gdGhlIGhvc3QgYW5kIGlzIGFkdmFuY2VkIGFzIGV2ZW50cyBhcmUgY29uc3VtZWQsXG4gKiBtYXRjaGluZyB0aGUgbm9kZTp2bSBlbmdpbmUncyBkZXRlcm1pbmlzdGljIHJlcGxheSBjbG9jay5cbiAqXG4gKiBUaGlzIHBoYXNlIGlzIHRoZSBmdXR1cmUgYm91bmRhcnkgZm9yIFZNLW1lbW9yeSBzbmFwc2hvdHRpbmc6IGFcbiAqIGJ1aWxkLXRpbWUgc25hcHNob3QgY2FuIGNhcHR1cmUgdGhlIFZNIHJpZ2h0IGFmdGVyIHRoaXMgZnVuY3Rpb24gYW5kXG4gKiBuZXcgcnVucyBjYW4gcmVzdG9yZSBmcm9tIGl0IGluc3RlYWQgb2YgcGF5aW5nIFZNIGNyZWF0aW9uICsgZXZhbCBjb3N0XG4gKiAoYFF1aWNrSlMucmVzdG9yZWAgYWNjZXB0cyB0aGUgc2FtZSB3YXNpIG92ZXJyaWRlKS5cbiAqL1xuLyoqXG4gKiBMb29zZWx5LXR5cGVkIGFjY2Vzc29yIGZvciB0aGUgYFdlYkFzc2VtYmx5YCBnbG9iYWwuIFRoZSBwYWNrYWdlXG4gKiB0c2NvbmZpZydzIGBsaWI6IFtcImVzMjAyMlwiXWAgZG9lcyBub3QgaW5jbHVkZSB0aGUgRE9NIGxpYiB3aGVyZSB0aGVcbiAqIGBXZWJBc3NlbWJseWAgbmFtZXNwYWNlIHR5cGVzIGxpdmU7IHRoZSBydW50aW1lIGdsb2JhbCBpcyBhdmFpbGFibGUgb25cbiAqIGV2ZXJ5IFdBU00tY2FwYWJsZSBwbGF0Zm9ybSB0aGlzIGVuZ2luZSB0YXJnZXRzLlxuICovXG5jb25zdCBXZWJBc3NlbWJseUdsb2JhbCA9IChnbG9iYWxUaGlzIGFzIGFueSkuV2ViQXNzZW1ibHkgYXMge1xuICBjb21waWxlKGJ5dGVzOiBVaW50OEFycmF5KTogUHJvbWlzZTxvYmplY3Q+O1xufTtcblxudHlwZSBDb21waWxlZEV4dGVuc2lvbiA9IE9taXQ8RXh0ZW5zaW9uRGVzY3JpcHRvciwgJ3dhc20nPiAmIHtcbiAgd2FzbTogRXh0ZW5zaW9uRGVzY3JpcHRvclsnd2FzbSddO1xufTtcblxuLyoqXG4gKiBQcm9jZXNzLXdpZGUgY2FjaGUgb2YgdGhlIGNvbXBpbGVkIGBXZWJBc3NlbWJseS5Nb2R1bGVgcyBmb3IgdGhlIG1haW5cbiAqIFF1aWNrSlMgcnVudGltZSBhbmQgaXRzIG5hdGl2ZSBleHRlbnNpb25zLiBgV2ViQXNzZW1ibHkuY29tcGlsZWAgb2YgdGhlXG4gKiB+NjAwIEtCIHJ1bnRpbWUgYmluYXJ5IGlzIHRoZSBtb3N0IGV4cGVuc2l2ZSBwYXJ0IG9mIFZNIGNyZWF0aW9uIGFuZCBpc1xuICogcHVyZSAobm8gcGVyLVZNIHN0YXRlIOKAlCBpbnN0YW50aWF0aW9uIGJpbmRzIHRoZSBwZXItVk0gbWVtb3J5KSwgc28gaXRcbiAqIG9ubHkgbmVlZHMgdG8gaGFwcGVuIG9uY2UgcGVyIHByb2Nlc3MuIFRoZSBwcm9taXNlIGlzIGNhY2hlZCAobm90IHRoZVxuICogcmVzdWx0KSBzbyBjb25jdXJyZW50IGZpcnN0IGludm9jYXRpb25zIHNoYXJlIGEgc2luZ2xlIGNvbXBpbGF0aW9uLlxuICovXG5sZXQgY29tcGlsZWRBc3NldHNQcm9taXNlOlxuICB8IFByb21pc2U8e1xuICAgICAgd2FzbTogb2JqZWN0O1xuICAgICAgZXh0ZW5zaW9uczogQ29tcGlsZWRFeHRlbnNpb25bXTtcbiAgICB9PlxuICB8IHVuZGVmaW5lZDtcblxuZnVuY3Rpb24gZ2V0Q29tcGlsZWRBc3NldHMoKSB7XG4gIGlmICghY29tcGlsZWRBc3NldHNQcm9taXNlKSB7XG4gICAgY29tcGlsZWRBc3NldHNQcm9taXNlID0gKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IFt3YXNtLCAuLi5leHRlbnNpb25Nb2R1bGVzXSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgV2ViQXNzZW1ibHlHbG9iYWwuY29tcGlsZShxdWlja2pzV2FzbSksXG4gICAgICAgIC4uLnF1aWNranNFeHRlbnNpb25zLm1hcCgoZXh0KSA9PlxuICAgICAgICAgIFdlYkFzc2VtYmx5R2xvYmFsLmNvbXBpbGUoZXh0Lndhc20gYXMgVWludDhBcnJheSlcbiAgICAgICAgKSxcbiAgICAgIF0pO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgd2FzbSxcbiAgICAgICAgZXh0ZW5zaW9uczogcXVpY2tqc0V4dGVuc2lvbnMubWFwKChleHQsIGkpID0+ICh7XG4gICAgICAgICAgLi4uZXh0LFxuICAgICAgICAgIHdhc206IGV4dGVuc2lvbk1vZHVsZXNbaV0gYXMgRXh0ZW5zaW9uRGVzY3JpcHRvclsnd2FzbSddLFxuICAgICAgICB9KSksXG4gICAgICB9O1xuICAgIH0pKCk7XG4gICAgLy8gT24gZmFpbHVyZSwgY2xlYXIgdGhlIGNhY2hlIHNvIGEgbGF0ZXIgaW52b2NhdGlvbiBjYW4gcmV0cnkgcmF0aGVyXG4gICAgLy8gdGhhbiBiZWluZyBzdHVjayB3aXRoIGEgcmVqZWN0ZWQgcHJvbWlzZSBmb3JldmVyLlxuICAgIGNvbXBpbGVkQXNzZXRzUHJvbWlzZS5jYXRjaCgoKSA9PiB7XG4gICAgICBjb21waWxlZEFzc2V0c1Byb21pc2UgPSB1bmRlZmluZWQ7XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIGNvbXBpbGVkQXNzZXRzUHJvbWlzZTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaW5pdFdvcmtmbG93Vk0oXG4gIGdldE5vd01zOiAoKSA9PiBudW1iZXIsXG4gIGludGVycnVwdEJ1ZGdldDogSW50ZXJydXB0QnVkZ2V0XG4pOiBQcm9taXNlPFF1aWNrSlM+IHtcbiAgLy8gRGV0ZXJtaW5pc3RpYyByZXBsYXkgY2xvY2s6IERhdGUubm93KCkgLyBuZXcgRGF0ZSgpIGluc2lkZSB0aGUgVk1cbiAgLy8gcmVhZCB0aGUgaG9zdC1jb250cm9sbGVkIGNsb2NrIGluc3RlYWQgb2Ygd2FsbCB0aW1lLiBSZXBsYXlcbiAgLy8gcmUtZXhlY3V0ZXMgdGhlIHdvcmtmbG93IGZyb20gdGhlIHRvcCBvbiBldmVyeSBpbnZvY2F0aW9uLCBzbyB0aGVcbiAgLy8gY2xvY2sgbXVzdCBiZSBkZXJpdmVkIGZyb20gdGhlIGV2ZW50IGxvZyAobm90IHJlYWwgdGltZSkgZm9yIHRoZVxuICAvLyB3b3JrZmxvdyB0byBvYnNlcnZlIHN0YWJsZSB0aW1lc3RhbXBzIGFjcm9zcyBpbnZvY2F0aW9ucy5cbiAgY29uc3Qgd2FzaTogV2FzaU9wdGlvbnMgPSAobWVtb3J5KSA9PiAoe1xuICAgIGNsb2NrX3RpbWVfZ2V0KF9jbG9ja0lkOiBudW1iZXIsIF9wcmVjaXNpb246IGJpZ2ludCwgcmVzdWx0UHRyOiBudW1iZXIpIHtcbiAgICAgIGNvbnN0IHRpbWVOcyA9IEJpZ0ludChNYXRoLnJvdW5kKGdldE5vd01zKCkpKSAqIDFfMDAwXzAwMG47XG4gICAgICBuZXcgRGF0YVZpZXcobWVtb3J5LmJ1ZmZlcikuc2V0QmlnVWludDY0KHJlc3VsdFB0ciwgdGltZU5zLCB0cnVlKTtcbiAgICAgIHJldHVybiAwO1xuICAgIH0sXG4gIH0pO1xuXG4gIGNvbnN0IGFzc2V0cyA9IGF3YWl0IGdldENvbXBpbGVkQXNzZXRzKCk7XG4gIGNvbnN0IHZtID0gYXdhaXQgUXVpY2tKUy5jcmVhdGUoe1xuICAgIHdhc206IGFzc2V0cy53YXNtIGFzIG5ldmVyLFxuICAgIG1lbW9yeUxpbWl0OiAyNTYgKiAxMDI0ICogMTAyNCxcbiAgICBpbnRlcnJ1cHRIYW5kbGVyOiBjcmVhdGVJbnRlcnJ1cHRIYW5kbGVyKGludGVycnVwdEJ1ZGdldCksXG4gICAgZXh0ZW5zaW9uczogYXNzZXRzLmV4dGVuc2lvbnMsXG4gICAgd2FzaSxcbiAgfSk7XG5cbiAgLy8gRXZhbHVhdGUgdGhlIFZNIHNlcmRlIGJ1bmRsZVxuICB2bS5ldmFsQ29kZShWTV9TRVJERV9CVU5ETEUsICd2bS1zZXJkZS5qcycpLmRpc3Bvc2UoKTtcblxuICAvLyBCb290c3RyYXAgd29ya2Zsb3cgcHJpbWl0aXZlc1xuICB2bS5ldmFsQ29kZShWTV9CT09UU1RSQVAsICdib290c3RyYXAuanMnKS5kaXNwb3NlKCk7XG5cbiAgcmV0dXJuIHZtO1xufVxuXG4vKipcbiAqIEEgbGl2ZSBRdWlja0pTIHdvcmtmbG93IGludm9jYXRpb24uIFdoZW4gdGhlIGluaXRpYWwgYHJlc3VsdGAgaXNcbiAqIGBzdXNwZW5kZWRgLCB0aGUgVk0gaXMga2VwdCBhbGl2ZSBzbyB0aGUgY2FsbGVyIGNhbiBmZWVkIG5ld2x5IHJlY29yZGVkXG4gKiBldmVudHMgKGUuZy4gdGVybWluYWwgZXZlbnRzIG9mIGlubGluZS1leGVjdXRlZCBzdGVwcykgaW50byB0aGUgU0FNRSBWTVxuICogdmlhIGBjb250aW51ZVdpdGhFdmVudHNgIOKAlCByZXN1bWluZyBleGVjdXRpb24gZXhhY3RseSB3aGVyZSBpdCBsZWZ0IG9mZlxuICogd2l0aG91dCBhIGZyZXNoLVZNIHJlLXJlcGxheS4gVGVybWluYWwgcmVzdWx0cyBkaXNwb3NlIHRoZSBWTVxuICogYXV0b21hdGljYWxseTsgYGRpc3Bvc2UoKWAgbXVzdCBiZSBjYWxsZWQgd2hlbiBhYmFuZG9uaW5nIGEgc3VzcGVuZGVkXG4gKiBzZXNzaW9uIChpZGVtcG90ZW50KS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBRdWlja0pTV29ya2Zsb3dTZXNzaW9uIHtcbiAgcmVzdWx0OiBRdWlja0pTUnVudGltZVJlc3VsdDtcbiAgLyoqXG4gICAqIFByb2Nlc3MgbmV3bHkgcmVjb3JkZWQgZXZlbnRzIGluIHRoZSBsaXZlIFZNIGFuZCByZS1ldmFsdWF0ZSB0aGVcbiAgICogd29ya2Zsb3cgc3RhdGUuIE9ubHkgdmFsaWQgd2hpbGUgdGhlIGxhc3QgcmVzdWx0IHdhcyBgc3VzcGVuZGVkYC5cbiAgICogUmVzZXRzIHRoZSBWTSdzIGludGVycnVwdCBidWRnZXQgZm9yIHRoZSBuZXcgZXhlY3V0aW9uIGJ1cnN0LlxuICAgKi9cbiAgY29udGludWVXaXRoRXZlbnRzKG5ld0V2ZW50czogRXZlbnRbXSk6IFByb21pc2U8UXVpY2tKU1J1bnRpbWVSZXN1bHQ+O1xuICAvKiogRGlzcG9zZSB0aGUgVk0gaWYgaXQgaXMgc3RpbGwgYWxpdmUuIFNhZmUgdG8gY2FsbCBtdWx0aXBsZSB0aW1lcy4gKi9cbiAgZGlzcG9zZSgpOiB2b2lkO1xufVxuXG4vKipcbiAqIFJ1biBhIHdvcmtmbG93IGludm9jYXRpb24gdG8gaXRzIGZpcnN0IHNldHRsZWQgc3RhdGUgYW5kIGRpc3Bvc2UgdGhlXG4gKiBWTS4gQ29udmVuaWVuY2Ugd3JhcHBlciBvdmVyIHtAbGluayBzdGFydFF1aWNrSlNXb3JrZmxvd30gZm9yIGNhbGxlcnNcbiAqIChhbmQgdGVzdHMpIHRoYXQgZG9uJ3QgdXNlIGxpdmUtVk0gY29udGludWF0aW9uLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcnVuUXVpY2tKU1dvcmtmbG93KFxuICBvcHRpb25zOiBRdWlja0pTUnVudGltZU9wdGlvbnNcbik6IFByb21pc2U8UXVpY2tKU1J1bnRpbWVSZXN1bHQ+IHtcbiAgY29uc3Qgc2Vzc2lvbiA9IGF3YWl0IHN0YXJ0UXVpY2tKU1dvcmtmbG93KG9wdGlvbnMpO1xuICBzZXNzaW9uLmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHNlc3Npb24ucmVzdWx0O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRRdWlja0pTV29ya2Zsb3coXG4gIG9wdGlvbnM6IFF1aWNrSlNSdW50aW1lT3B0aW9uc1xuKTogUHJvbWlzZTxRdWlja0pTV29ya2Zsb3dTZXNzaW9uPiB7XG4gIGNvbnN0IHsgd29ya2Zsb3dDb2RlLCB3b3JrZmxvd0lkLCB3b3JrZmxvd1J1biwgZXZlbnRzIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHN0YXJ0ZWRBdCA9IHdvcmtmbG93UnVuLnN0YXJ0ZWRBdCA/ICt3b3JrZmxvd1J1bi5zdGFydGVkQXQgOiBEYXRlLm5vdygpO1xuXG4gIC8vIERldGVybWluaXN0aWMgUFJORyBzZWVkIOKAlCBpZGVudGljYWwgZm9yIEVWRVJZIGludm9jYXRpb24gb2YgdGhlIHNhbWVcbiAgLy8gcnVuLiBGdWxsIGV2ZW50IHJlcGxheSByZXF1aXJlcyB0aGlzOiBlYWNoIGludm9jYXRpb24gcmUtZXhlY3V0ZXMgdGhlXG4gIC8vIHdvcmtmbG93IGZyb20gdGhlIHRvcCBhbmQgbXVzdCByZWdlbmVyYXRlIHRoZSBleGFjdCBzYW1lIGNvcnJlbGF0aW9uSWRcbiAgLy8gc2VxdWVuY2Ugc28gdGhhdCBwZW5kaW5nIG9wZXJhdGlvbnMgcmUtY3JlYXRlZCBieSByZXBsYXkgbWF0Y2ggdGhlXG4gIC8vIGV2ZW50cyByZWNvcmRlZCBieSBlYXJsaWVyIGludm9jYXRpb25zLiBTZXF1ZW50aWFsIG9wZXJhdGlvbnMgd2l0aGluXG4gIC8vIG9uZSBleGVjdXRpb24gc3RpbGwgZ2V0IGRpc3RpbmN0IGlkcyBiZWNhdXNlIHRoZSBQUk5HIGFkdmFuY2VzIGFzIHRoZVxuICAvLyB3b3JrZmxvdyBkcmF3cyBmcm9tIGl0LiBJZGVudGljYWwgc2VlZGluZyBhY3Jvc3MgQ09OQ1VSUkVOVCBpbnZvY2F0aW9uc1xuICAvLyBvZiB0aGUgc2FtZSBydW4gaXMgYWxzbyBsb2FkLWJlYXJpbmc6IGJvdGggcHJvZHVjZSB0aGUgc2FtZSBpZHMsIGFuZFxuICAvLyB0aGUgd29ybGQncyBwZXItKHJ1bklkLCBjb3JyZWxhdGlvbklkKSB1bmlxdWVuZXNzIHR1cm5zIHRoZSBkdXBsaWNhdGVcbiAgLy8gYGV2ZW50cy5jcmVhdGVgIGludG8gYW4gRW50aXR5Q29uZmxpY3RFcnJvciB0aGF0IHRoZSBlbnRyeXBvaW50XG4gIC8vIHN3YWxsb3dzLlxuICAvL1xuICAvLyBUaGUgc2VlZCBpbnB1dHMgTVVTVCBiZSBzdGFibGUgYWNyb3NzIGludm9jYXRpb25zLiBOb3RhYmx5XG4gIC8vIGBzdGFydGVkQXRgIGlzIE5PVDogdW5kZXIgdHVyYm8gdGhlIGZpcnN0IGludm9jYXRpb24gcnVucyBhZ2FpbnN0IGFcbiAgLy8gc3ludGhlc2l6ZWQgcnVuIG9iamVjdCB3aG9zZSB0aW1lc3RhbXBzIGRpZmZlciBmcm9tIHRoZSBkdXJhYmx5XG4gIC8vIHN0b3JlZCBvbmVzIHRoYXQgbGF0ZXIgaW52b2NhdGlvbnMgbG9hZC4gTWF0Y2hlcyB0aGUgbm9kZTp2bVxuICAvLyBlbmdpbmUncyBzZWVkICh3b3JrZmxvdy50cykuXG4gIGNvbnN0IHNlZWQgPSBbXG4gICAgd29ya2Zsb3dSdW4ucnVuSWQsXG4gICAgd29ya2Zsb3dSdW4ud29ya2Zsb3dOYW1lLFxuICAgIHdvcmtmbG93UnVuLmRlcGxveW1lbnRJZCxcbiAgXS5qb2luKCc6Jyk7XG4gIGNvbnN0IHJuZyA9IHNlZWRyYW5kb20oc2VlZCk7XG5cbiAgLy8gU2VlZGVkIG5hbm9pZCBnZW5lcmF0b3Ig4oCUIHVzZXMgdGhlIHNhbWUgbmFub2lkIHBhY2thZ2UgYW5kIHNlZWRlZCBQUk5HXG4gIC8vIGFzIHRoZSBub2RlOnZtIGVuZ2luZSBmb3IgY29uc2lzdGVudCB0b2tlbiBnZW5lcmF0aW9uLlxuICBjb25zdCBnZW5lcmF0ZU5hbm9pZCA9IG5hbm9pZC5jdXN0b21SYW5kb20obmFub2lkLnVybEFscGhhYmV0LCAyMSwgKHNpemUpID0+XG4gICAgbmV3IFVpbnQ4QXJyYXkoc2l6ZSkubWFwKCgpID0+IDI1NiAqIHJuZygpKVxuICApO1xuXG4gIC8vIERldGVybWluaXN0aWMgcmVwbGF5IGNsb2NrLCBtaXJyb3JpbmcgdGhlIG5vZGU6dm0gZW5naW5lIChzZWVcbiAgLy8gd29ya2Zsb3cudHMpOiB0aGUgaW5pdGlhbCB2YWx1ZSBpcyB0aGUgcnVuJ3MgY3JlYXRpb24gdGltZSByZWNvdmVyZWRcbiAgLy8gZnJvbSB0aGUgVUxJRCBlbWJlZGRlZCBpbiBgcnVuSWRgIChmYWxsaW5nIGJhY2sgdG8gYGNyZWF0ZWRBdGApLCBhbmRcbiAgLy8gaXQgYWR2YW5jZXMgdG8gZWFjaCBwcm9jZXNzZWQgZXZlbnQncyBgY3JlYXRlZEF0YCBhcyB0aGUgZXZlbnQgbG9nIGlzXG4gIC8vIHJlcGxheWVkLiBNb25vdG9uaWMgKE1hdGgubWF4KSBzbyB0aGUgb3V0ZXIgcHJvY2Vzc0V2ZW50cyByZS1zY2FuXG4gIC8vIGxvb3AgY2FuJ3QgbW92ZSB0aGUgY2xvY2sgYmFja3dhcmRzIG1pZC1leGVjdXRpb24uXG4gIGxldCB2bU5vd01zID1cbiAgICBydW5JZENyZWF0ZWRBdCh3b3JrZmxvd1J1bi5ydW5JZCkgPz8gKCt3b3JrZmxvd1J1bi5jcmVhdGVkQXQgfHwgc3RhcnRlZEF0KTtcbiAgY29uc3QgYWR2YW5jZUNsb2NrID0gKG1zOiBudW1iZXIpID0+IHtcbiAgICBpZiAoTnVtYmVyLmlzRmluaXRlKG1zKSkgdm1Ob3dNcyA9IE1hdGgubWF4KHZtTm93TXMsIG1zKTtcbiAgfTtcblxuICAvLyAtLS0tIFBoYXNlIDE6IHN0YXRpYyBpbml0aWFsaXphdGlvbiAtLS0tXG4gIGNvbnN0IGludGVycnVwdEJ1ZGdldDogSW50ZXJydXB0QnVkZ2V0ID0geyBzdGFydDogRGF0ZS5ub3coKSB9O1xuICBjb25zdCB2bSA9IGF3YWl0IGluaXRXb3JrZmxvd1ZNKCgpID0+IHZtTm93TXMsIGludGVycnVwdEJ1ZGdldCk7XG5cbiAgLy8gQW55IHRocm93IGJldHdlZW4gaGVyZSBhbmQgdGhlIHRlcm1pbmFsIHBhdGhzICh3aGljaCBkaXNwb3NlIHRoZSBWTVxuICAvLyBpbnNpZGUgY2hlY2tXb3JrZmxvd1N0YXRlIC8gZXh0cmFjdEVycm9yIGJlZm9yZSBSRVRVUk5JTkcpIHdvdWxkIGxlYWtcbiAgLy8gYSBsaXZlIFF1aWNrSlMgaW5zdGFuY2UgYW5kIGl0cyBXQVNNIGxpbmVhciBtZW1vcnkgZm9yIHRoZSBsaWZldGltZVxuICAvLyBvZiB0aGUgY29tcHV0ZSBpbnN0YW5jZSDigJQgd2hpY2ggaXMgcmV1c2VkLiBEaXNwb3NlIG9uIHRoZSB3YXkgb3V0IG9mXG4gIC8vIGFuIGV4Y2VwdGlvbmFsIGV4aXQgYW5kIHJldGhyb3cuXG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IHJ1bldvcmtmbG93SW5WTSgpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICB0cnkge1xuICAgICAgdm0uZGlzcG9zZSgpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gQWxyZWFkeSBkaXNwb3NlZCBieSBhIHRlcm1pbmFsIHBhdGgg4oCUIGlnbm9yZS5cbiAgICB9XG4gICAgdGhyb3cgZXJyO1xuICB9XG5cbiAgLy8gLS0tLSBQaGFzZSAyOiBwZXItcnVuIGluaXRpYWxpemF0aW9uIC0tLS1cbiAgYXN5bmMgZnVuY3Rpb24gcnVuV29ya2Zsb3dJblZNKCk6IFByb21pc2U8UXVpY2tKU1dvcmtmbG93U2Vzc2lvbj4ge1xuICAgIHZtLmV2YWxDb2RlKFxuICAgICAgYGdsb2JhbFRoaXMuX193b3JsZENhcGFiaWxpdGllcyA9ICR7SlNPTi5zdHJpbmdpZnkob3B0aW9ucy53b3JsZENhcGFiaWxpdGllcyl9O2BcbiAgICApLmRpc3Bvc2UoKTtcblxuICAgIC8vIFNlZWRlZCBNYXRoLnJhbmRvbVxuICAgIHtcbiAgICAgIHVzaW5nIHJhbmRvbUZuID0gdm0ubmV3RnVuY3Rpb24oJ3JhbmRvbScsICgpID0+IHZtLm5ld051bWJlcihybmcoKSkpO1xuICAgICAgdXNpbmcgbWF0aCA9IHZtLmdsb2JhbC5nZXRQcm9wKCdNYXRoJyk7XG4gICAgICBtYXRoLnNldFByb3AoJ3JhbmRvbScsIHJhbmRvbUZuKTtcbiAgICB9XG5cbiAgICAvLyBTZWVkZWQgbmFub2lkIGdlbmVyYXRvclxuICAgIHtcbiAgICAgIHVzaW5nIG5hbm9pZEZuID0gdm0ubmV3RnVuY3Rpb24oJ19fZ2VuZXJhdGVOYW5vaWQnLCAoKSA9PlxuICAgICAgICB2bS5uZXdTdHJpbmcoZ2VuZXJhdGVOYW5vaWQoKSlcbiAgICAgICk7XG4gICAgICB2bS5zZXRQcm9wKHZtLmdsb2JhbCwgJ19fZ2VuZXJhdGVOYW5vaWQnLCBuYW5vaWRGbik7XG4gICAgfVxuXG4gICAgLy8gSW5qZWN0IGEgZGV0ZXJtaW5pc3RpYyB0aW1lc3RhbXAgZm9yIHRoZSBWTSdzIFVMSUQgZmFjdG9yeS4gVUxJRHNcbiAgICAvLyBwcm9kdWNlZCBpbnNpZGUgdGhlIFZNIHVzZSB0aGlzIGFzIHRoZWlyIHRpbWUgcHJlZml4IGluc3RlYWQgb2ZcbiAgICAvLyBEYXRlLm5vdygpLCBzbyB0d28gY29uY3VycmVudCB3b3JrZmxvdyBpbnZvY2F0aW9ucyBvZiB0aGUgc2FtZSBydW5cbiAgICAvLyBwcm9kdWNlIElERU5USUNBTCBjb3JyZWxhdGlvbklkcyAodGhlIHJhbmRvbSBwb3J0aW9uIGFsc28gbWF0Y2hlc1xuICAgIC8vIGJlY2F1c2UgdGhlIFBSTkcgaXMgc2VlZGVkIHRoZSBzYW1lIHdheSkgYW5kIHRoZSB3b3JsZCdzXG4gICAgLy8gRW50aXR5Q29uZmxpY3RFcnJvciBvbiBgZXZlbnRzLmNyZWF0ZWAgZGVkdXBzIG9uZSBvZiBlYWNoIHBhaXIuXG4gICAgLy8gRGVyaXZlZCBmcm9tIHRoZSBydW5JZCdzIGVtYmVkZGVkIFVMSUQgKHN0YWJsZSBhY3Jvc3MgaW52b2NhdGlvbnMgYnlcbiAgICAvLyBjb25zdHJ1Y3Rpb24g4oCUIHVubGlrZSBgc3RhcnRlZEF0YCwgd2hpY2ggZGlmZmVycyBiZXR3ZWVuIHR1cmJvJ3NcbiAgICAvLyBzeW50aGVzaXplZCBydW4gb2JqZWN0IGFuZCB0aGUgZHVyYWJseSBzdG9yZWQgcnVuKS5cbiAgICB2bS5ldmFsQ29kZShcbiAgICAgIGBnbG9iYWxUaGlzLl9fdWxpZFRpbWVzdGFtcCA9ICR7cnVuSWRDcmVhdGVkQXQod29ya2Zsb3dSdW4ucnVuSWQpID8/ICgrd29ya2Zsb3dSdW4uY3JlYXRlZEF0IHx8IHN0YXJ0ZWRBdCl9O2BcbiAgICApLmRpc3Bvc2UoKTtcblxuICAgIC8vIGBwcm9jZXNzLmVudmAg4oCUIHBhcml0eSB3aXRoIHRoZSBub2RlOnZtIGVuZ2luZSwgd2hpY2ggZXhwb3NlcyBhIGZyb3plblxuICAgIC8vIGNvcHkgb2YgdGhlIGhvc3QgZW52ICh2bS9pbmRleC50cykuIEluamVjdGVkIHBlciBydW4gc28gdGhlIHNuYXBzaG90IG9mXG4gICAgLy8gdGhlIGVudiBpcyB0YWtlbiBhdCBpbnZvY2F0aW9uIHRpbWUsIHNhbWUgYXMgbm9kZS5cbiAgICB7XG4gICAgICBjb25zdCBlbnZIYW5kbGUgPSB2bS5uZXdTdHJpbmcoSlNPTi5zdHJpbmdpZnkocHJvY2Vzcy5lbnYpKTtcbiAgICAgIHZtLnNldFByb3Aodm0uZ2xvYmFsLCAnX193ZGtfZW52JywgZW52SGFuZGxlKTtcbiAgICAgIGVudkhhbmRsZS5kaXNwb3NlKCk7XG4gICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgJ2dsb2JhbFRoaXMucHJvY2VzcyA9IHsgZW52OiBPYmplY3QuZnJlZXplKEpTT04ucGFyc2UoZ2xvYmFsVGhpcy5fX3dka19lbnYpKSB9OycgK1xuICAgICAgICAgICdkZWxldGUgZ2xvYmFsVGhpcy5fX3dka19lbnY7J1xuICAgICAgKS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgLy8gRXhlY3V0ZSB0aGUgd29ya2Zsb3cgYnVuZGxlIOKAlCB1c2UgdGhlIHdvcmtmbG93SWQgYXMgdGhlIGV2YWwgZmlsZW5hbWVcbiAgICAvLyBzbyBRdWlja0pTIHN0YWNrIHRyYWNlcyByZWZlcmVuY2UgdGhlIHdvcmtmbG93IG5hbWUsIGVuYWJsaW5nIHNvdXJjZSBtYXBcbiAgICAvLyByZW1hcHBpbmcgYnkgcmVtYXBFcnJvclN0YWNrICh3aGljaCBtYXRjaGVzIGZyYW1lcyBieSBmaWxlbmFtZSkuXG4gICAgLy8gRXZhbHVhdGVkIGluIHRoZSBwZXItcnVuIHBoYXNlIChhZnRlciBNYXRoLnJhbmRvbSBzZWVkaW5nKSBzbyB0aGF0XG4gICAgLy8gbW9kdWxlLXNjb3BlIHVzZXIgY29kZSBkcmF3cyBmcm9tIHRoZSBzZWVkZWQgUFJORywgbWF0Y2hpbmcgdGhlXG4gICAgLy8gbm9kZTp2bSBlbmdpbmUncyByZXBsYXkgZGV0ZXJtaW5pc20uXG4gICAgdHJ5IHtcbiAgICAgIHZtLmV2YWxDb2RlKHdvcmtmbG93Q29kZSwgd29ya2Zsb3dJZCB8fCAnd29ya2Zsb3cuanMnKS5kaXNwb3NlKCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICByZXR1cm4gbWFrZVNldHRsZWRTZXNzaW9uKFxuICAgICAgICBleHRyYWN0RXJyb3Iodm0sIGVyciwgJ1dvcmtmbG93IGV2YWx1YXRpb24gZmFpbGVkJylcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gRXh0cmFjdCB3b3JrZmxvdyBhcmd1bWVudHMuIFByZWZlciB0aGUgcnVuX2NyZWF0ZWQgZXZlbnQ7IGZhbGwgYmFja1xuICAgIC8vIHRvIHRoZSBxdWV1ZSBtZXNzYWdlJ3MgcnVuSW5wdXQgaWYgdGhlIGV2ZW50IGxvZyBpcyBpbmNvbXBsZXRlXG4gICAgLy8gKGV2ZW50dWFsbHktY29uc2lzdGVudCByZWFkIGFmdGVyIHN0YXJ0KCkpLiBGYWlsaW5nIHRvIGZpbmQgaW5wdXRcbiAgICAvLyBmb3IgYSBmaXJzdCBpbnZvY2F0aW9uIGlzIGZhdGFsIOKAlCBydW5uaW5nIHRoZSB3b3JrZmxvdyBmdW5jdGlvblxuICAgIC8vIHdpdGggbm8gYXJncyB3b3VsZCBzaWxlbnRseSB0dXJuIHR5cGVkIGFyZ3VtZW50cyBpbnRvIGB1bmRlZmluZWRgXG4gICAgLy8gYW5kLCBmb3IgcmVjdXJzaXZlIHdvcmtmbG93cywgcHJvZHVjZSBleHBvbmVudGlhbCBmYW4tb3V0LlxuICAgIGNvbnN0IHJ1bkNyZWF0ZWRFdmVudCA9IGV2ZW50cy5maW5kKChlKSA9PiBlLmV2ZW50VHlwZSA9PT0gJ3J1bl9jcmVhdGVkJyk7XG4gICAgY29uc3QgcnVuQ3JlYXRlZElucHV0ID1cbiAgICAgIHJ1bkNyZWF0ZWRFdmVudCAmJiAnZXZlbnREYXRhJyBpbiBydW5DcmVhdGVkRXZlbnRcbiAgICAgICAgPyAocnVuQ3JlYXRlZEV2ZW50LmV2ZW50RGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik/LmlucHV0XG4gICAgICAgIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IHJ1bklucHV0OiB1bmtub3duID1cbiAgICAgIHJ1bkNyZWF0ZWRJbnB1dCA/PyAob3B0aW9ucy5ydW5JbnB1dD8uaW5wdXQgYXMgdW5rbm93bik7XG5cbiAgICBpZiAocnVuSW5wdXQgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICBjb25zdCBkZWNyeXB0ZWRJbnB1dCA9IGF3YWl0IHByZXBhcmVCeXRlc0ZvclZNKFxuICAgICAgICBydW5JbnB1dCxcbiAgICAgICAgb3B0aW9ucy5lbmNyeXB0aW9uS2V5XG4gICAgICApO1xuICAgICAgcnVudGltZUxvZ2dlci5kZWJ1ZygnUXVpY2tKUyBydW50aW1lOiBydW4gaW5wdXQgZm9ybWF0Jywge1xuICAgICAgICBwcmVmaXg6IG5ldyBUZXh0RGVjb2RlcigpLmRlY29kZShkZWNyeXB0ZWRJbnB1dC5zdWJhcnJheSgwLCA0KSksXG4gICAgICAgIGJ5dGVMZW5ndGg6IGRlY3J5cHRlZElucHV0LmJ5dGVMZW5ndGgsXG4gICAgICAgIHNvdXJjZTogcnVuQ3JlYXRlZElucHV0ID8gJ3J1bl9jcmVhdGVkJyA6ICdxdWV1ZU1lc3NhZ2UucnVuSW5wdXQnLFxuICAgICAgfSk7XG4gICAgICBjb25zdCBpbnB1dEhhbmRsZSA9IHZtLm5ld1VpbnQ4QXJyYXkoZGVjcnlwdGVkSW5wdXQpO1xuICAgICAgdm0uc2V0UHJvcCh2bS5nbG9iYWwsICdfX3dka19pbnB1dCcsIGlucHV0SGFuZGxlKTtcbiAgICAgIGlucHV0SGFuZGxlLmRpc3Bvc2UoKTtcbiAgICB9IGVsc2UgaWYgKHJ1bklucHV0ID09PSB1bmRlZmluZWQgJiYgZXZlbnRzLmxlbmd0aCA+IDApIHtcbiAgICAgIC8vIFRoZSBldmVudCBsb2cgaXMgbm9uLWVtcHR5ICh3ZSBnb3QgcnVuX3N0YXJ0ZWQgb3Igc2ltaWxhcikgYnV0XG4gICAgICAvLyBubyBydW5fY3JlYXRlZCBldmVudCB3YXMgZm91bmQgYW5kIG5vIHF1ZXVlLXByb3ZpZGVkIHJ1bklucHV0IGlzXG4gICAgICAvLyBhdmFpbGFibGUuIFRoaXMgaXMgdGhlIHJhY2UgY29uZGl0aW9uIG9ic2VydmVkIGR1cmluZyB0aGUgZmliXG4gICAgICAvLyBpbmNpZGVudCDigJQgc2lsZW50bHkgZHJvcHBpbmcgYXJndW1lbnRzIHdvdWxkIHR1cm4gYG5gIGludG9cbiAgICAgIC8vIGB1bmRlZmluZWRgIGFuZCwgZm9yIHJlY3Vyc2l2ZSB3b3JrZmxvd3MsIGNhdXNlIGV4cG9uZW50aWFsXG4gICAgICAvLyBmYW4tb3V0LiBGYWlsIGxvdWQ6IHRoZSB0aHJvdyBlc2NhcGVzIHRoZSBlbnRyeXBvaW50IGludG8gdGhlXG4gICAgICAvLyByZXBsYXkgbG9vcCdzIGNhdGNoIGluIHJ1bnRpbWUudHMgKHRoZSBRdWlja0pTIGRpc3BhdGNoIHJ1bnNcbiAgICAgIC8vIGluc2lkZSB0aGF0IGxvb3AncyB0cnkpLCB3aGljaCByZWNvcmRzIHJ1bl9mYWlsZWQuIEEgdmlzaWJsZVxuICAgICAgLy8gdGVybWluYWwgZmFpbHVyZSBpc1xuICAgICAgLy8gcHJlZmVycmVkIG92ZXIgc2lsZW50bHkgZXhlY3V0aW5nIHdpdGggdW5kZWZpbmVkIGFyZ3VtZW50cyDigJQgdGhlXG4gICAgICAvLyBxdWV1ZS1wcm92aWRlZCBydW5JbnB1dCBmYWxsYmFjayBhYm92ZSBtYWtlcyB0aGlzIHBhdGggcmFyZS5cbiAgICAgIC8vIEVtcHR5IGBldmVudHNgIGlzIGFsbG93ZWQgYmVjYXVzZSB0ZXN0cyB0aGF0IGJvb3RzdHJhcCBhIHdvcmtmbG93XG4gICAgICAvLyB3aXRoIG5vIGFyZ3VtZW50cyByZWx5IG9uIHRoZSBvbGQgcGVybWlzc2l2ZSBiZWhhdmlvci5cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYENhbm5vdCBzdGFydCB3b3JrZmxvdyBydW4gXCIke3dvcmtmbG93UnVuLnJ1bklkfVwiOiBubyBydW5fY3JlYXRlZCBldmVudCBmb3VuZCBhbmQgbm8gcnVuSW5wdXQgaW4gdGhlIHF1ZXVlIHBheWxvYWQsIGJ1dCBvdGhlciBldmVudHMgYXJlIHByZXNlbnQgKGxpa2VseSBhIHJlYWQtYWZ0ZXItd3JpdGUgcmFjZSBkdXJpbmcgc3RhcnQoKSkuYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBTZXQgd29ya2Zsb3cgY29udGV4dCBtZXRhZGF0YSAoZm9yIGdldFdvcmtmbG93TWV0YWRhdGEoKSkuXG4gICAgLy8gTXVzdCBtYXRjaCB0aGUgc2hhcGUgdGhhdCB0aGUgbm9kZTp2bSBlbmdpbmUgcHJvZHVjZXMgKHNlZVxuICAgIC8vIHBhY2thZ2VzL2NvcmUvc3JjL3dvcmtmbG93LnRzOiBydW5Xb3JrZmxvdyDihpIgY3R4KSBzbyB1c2VyIGNvZGVcbiAgICAvLyB0aGF0IGNvbXBhcmVzIGBnZXRXb3JrZmxvd01ldGFkYXRhKClgIHZhbHVlcyBiZXR3ZWVuIGEgc3RlcFxuICAgIC8vIChzZXJ2ZXItc2lkZSkgYW5kIHRoZSB3b3JrZmxvdyAoVk0tc2lkZSkgc2VlcyBpZGVudGljYWwgb2JqZWN0cy5cbiAgICB7XG4gICAgICBjb25zdCBtZXRhZGF0YSA9IHtcbiAgICAgICAgd29ya2Zsb3dOYW1lOiB3b3JrZmxvd1J1bi53b3JrZmxvd05hbWUsXG4gICAgICAgIHdvcmtmbG93UnVuSWQ6IHdvcmtmbG93UnVuLnJ1bklkLFxuICAgICAgICB3b3JrZmxvd1N0YXJ0ZWRBdDogd29ya2Zsb3dSdW4uc3RhcnRlZEF0XG4gICAgICAgICAgPyBuZXcgRGF0ZSgrd29ya2Zsb3dSdW4uc3RhcnRlZEF0KVxuICAgICAgICAgIDogbmV3IERhdGUoKSxcbiAgICAgICAgdXJsOiBwcm9jZXNzLmVudi5WRVJDRUxfVVJMXG4gICAgICAgICAgPyBgaHR0cHM6Ly8ke3Byb2Nlc3MuZW52LlZFUkNFTF9VUkx9YFxuICAgICAgICAgIDogYGh0dHA6Ly9sb2NhbGhvc3Q6JHtvcHRpb25zLnBvcnQgPz8gMzAwMH1gLFxuICAgICAgICBmZWF0dXJlczogeyBlbmNyeXB0aW9uOiAhIW9wdGlvbnMuZW5jcnlwdGlvbktleSB9LFxuICAgICAgfTtcbiAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICBgZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwiV09SS0ZMT1dfQ09OVEVYVFwiKV0gPSAke0pTT04uc3RyaW5naWZ5KG1ldGFkYXRhKX07YCArXG4gICAgICAgICAgYGdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIldPUktGTE9XX0NPTlRFWFRcIildLndvcmtmbG93U3RhcnRlZEF0ID0gbmV3IERhdGUoJHtKU09OLnN0cmluZ2lmeShtZXRhZGF0YS53b3JrZmxvd1N0YXJ0ZWRBdC50b0lTT1N0cmluZygpKX0pO2BcbiAgICAgICkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHRoZSB3b3JrZmxvdyBmdW5jdGlvbi4gSWYgdGhlIHdvcmtmbG93IGlzbid0IHJlZ2lzdGVyZWQsXG4gICAgLy8gdGhyb3cgYW4gZXJyb3IgdGFnZ2VkIHdpdGggYG5hbWUgPSBcIldvcmtmbG93Tm90UmVnaXN0ZXJlZEVycm9yXCJgXG4gICAgLy8gc28gdGhlIGhvc3Qtc2lkZSBlbnRyeXBvaW50IGNhbiByZWNvbnN0cnVjdCBhIHJlYWxcbiAgICAvLyBXb3JrZmxvd05vdFJlZ2lzdGVyZWRFcnJvciAoYSBXb3JrZmxvd1J1bnRpbWVFcnJvciBzdWJjbGFzcyB0aGF0XG4gICAgLy8gY2xhc3NpZmllcyBhcyBSVU5USU1FX0VSUk9SKSByYXRoZXIgdGhhbiBhIGdlbmVyaWMgdXNlciBlcnJvci5cbiAgICAvLyBTZWUgcXVpY2tqcy1lbnRyeXBvaW50LnRzJ3MgcnVuX2ZhaWxlZCBicmFuY2guXG4gICAgdHJ5IHtcbiAgICAgIHZtLmV2YWxDb2RlKGBcbiAgICAgIHZhciBfX3dmbiA9IGdsb2JhbFRoaXMuX19wcml2YXRlX3dvcmtmbG93cy5nZXQoJHtKU09OLnN0cmluZ2lmeSh3b3JrZmxvd0lkKX0pO1xuICAgICAgaWYgKCFfX3dmbikge1xuICAgICAgICB2YXIgX193Zm5FcnIgPSBuZXcgRXJyb3IoXCJXb3JrZmxvdyBcXFxcXCJcIiArICR7SlNPTi5zdHJpbmdpZnkod29ya2Zsb3dJZCl9ICsgXCJcXFxcXCIgaXMgbm90IHJlZ2lzdGVyZWQgaW4gdGhlIGN1cnJlbnQgZGVwbG95bWVudC5cIik7XG4gICAgICAgIF9fd2ZuRXJyLm5hbWUgPSBcIldvcmtmbG93Tm90UmVnaXN0ZXJlZEVycm9yXCI7XG4gICAgICAgIHRocm93IF9fd2ZuRXJyO1xuICAgICAgfVxuICAgICAgdmFyIF9fYXJncyA9IGdsb2JhbFRoaXMuX193ZGtfaW5wdXRcbiAgICAgICAgPyBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJ3b3JrZmxvdy1kZXNlcmlhbGl6ZVwiKV0oZ2xvYmFsVGhpcy5fX3dka19pbnB1dClcbiAgICAgICAgOiBbXTtcbiAgICAgIGRlbGV0ZSBnbG9iYWxUaGlzLl9fd2RrX2lucHV0O1xuICAgICAgaWYgKCFBcnJheS5pc0FycmF5KF9fYXJncykpIF9fYXJncyA9IFtfX2FyZ3NdO1xuICAgICAgX193Zm4uYXBwbHkobnVsbCwgX19hcmdzKS50aGVuKFxuICAgICAgICBmdW5jdGlvbihyZXN1bHQpIHsgZ2xvYmFsVGhpcy5fX3dvcmtmbG93UmVzdWx0ID0gZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwid29ya2Zsb3ctc2VyaWFsaXplXCIpXShyZXN1bHQpOyB9LFxuICAgICAgICBmdW5jdGlvbihlcnJvcikge1xuICAgICAgICAgIC8vIFByZXNlcnZlIGRpc3BsYXkgaW5mbyBvbiB0aGUgaG9zdC1zaWRlIGZhaWxlZCBvYmplY3RcbiAgICAgICAgICAvLyAobWF0Y2hlcyB0aGUgbGVnYWN5IGhvc3QtdmlzaWJsZSBzaGFwZSkgQU5EIHNlcmlhbGl6ZSB0aGVcbiAgICAgICAgICAvLyBlbnRpcmUgdGhyb3duIHZhbHVlIHNvIHRoZSBob3N0IGNhbiBkZWh5ZHJhdGUgdGhlIG9yaWdpbmFsXG4gICAgICAgICAgLy8gdHlwZS1pZGVudGl0eSwgY2F1c2UgY2hhaW4sIG9yIG5vbi1FcnJvciB0aHJvd3MgdmVyYmF0aW1cbiAgICAgICAgICAvLyB0aHJvdWdoIHRoZSBzdGFuZGFyZCBlcnJvciBwaXBlbGluZS5cbiAgICAgICAgICBnbG9iYWxUaGlzLl9fd29ya2Zsb3dFcnJvciA9IHtcbiAgICAgICAgICAgIG1lc3NhZ2U6IGVycm9yICYmIGVycm9yLm1lc3NhZ2UgIT0gbnVsbCA/IFN0cmluZyhlcnJvci5tZXNzYWdlKSA6IFN0cmluZyhlcnJvciksXG4gICAgICAgICAgICBzdGFjazogZXJyb3IgJiYgZXJyb3Iuc3RhY2sgPyBlcnJvci5zdGFjayA6IFwiXCIsXG4gICAgICAgICAgICBuYW1lOiBlcnJvciAmJiBlcnJvci5uYW1lID8gZXJyb3IubmFtZSA6IChlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gXCJFcnJvclwiIDogdHlwZW9mIGVycm9yKSxcbiAgICAgICAgICAgIHZhbHVlQnl0ZXM6IGdsb2JhbFRoaXNbU3ltYm9sLmZvcihcIndvcmtmbG93LXNlcmlhbGl6ZVwiKV0oZXJyb3IpLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgICk7XG4gICAgYCkuZGlzcG9zZSgpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcmV0dXJuIG1ha2VTZXR0bGVkU2Vzc2lvbihcbiAgICAgICAgZXh0cmFjdEVycm9yKHZtLCBlcnIsICdGYWlsZWQgdG8gc3RhcnQgd29ya2Zsb3cnKVxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBQcm9jZXNzIGV2ZW50cyBhbmQgZHJhaW4gam9icyBpbiBhIGxvb3AuIEV2ZW50cyBtYXkgcmVzb2x2ZSBwcm9taXNlc1xuICAgIC8vIHRoYXQgdW5ibG9jayB3b3JrZmxvdyBjb2RlLCB3aGljaCB0aGVuIGNyZWF0ZXMgTkVXIHJlc29sdmVycyBmb3JcbiAgICAvLyBzdWJzZXF1ZW50IGV2ZW50cy4gUmUtcHJvY2Vzc2luZyBldmVudHMgbWF0Y2hlcyB0aGVzZSBuZXcgcmVzb2x2ZXJzXG4gICAgLy8gYWdhaW5zdCBldmVudHMgdGhhdCB3ZXJlIGFscmVhZHkgZGVsaXZlcmVkLlxuICAgIHtcbiAgICAgIGxldCBtYXhJdGVyYXRpb25zID0gMTAwO1xuICAgICAgbGV0IG1hZGVQcm9ncmVzczogYm9vbGVhbjtcbiAgICAgIGRvIHtcbiAgICAgICAgbWFkZVByb2dyZXNzID0gYXdhaXQgcHJvY2Vzc0V2ZW50cyhcbiAgICAgICAgICB2bSxcbiAgICAgICAgICBldmVudHMsXG4gICAgICAgICAgYWR2YW5jZUNsb2NrLFxuICAgICAgICAgIG9wdGlvbnMuZW5jcnlwdGlvbktleVxuICAgICAgICApO1xuICAgICAgICBsZXQgYmF0Y2g6IG51bWJlcjtcbiAgICAgICAgZG8ge1xuICAgICAgICAgIGJhdGNoID0gdm0uZXhlY3V0ZVBlbmRpbmdKb2JzKCk7XG4gICAgICAgICAgaWYgKGJhdGNoID4gMCkgbWFkZVByb2dyZXNzID0gdHJ1ZTtcbiAgICAgICAgfSB3aGlsZSAoYmF0Y2ggPiAwKTtcbiAgICAgIH0gd2hpbGUgKG1hZGVQcm9ncmVzcyAmJiAtLW1heEl0ZXJhdGlvbnMgPiAwKTtcbiAgICAgIGlmIChtYWRlUHJvZ3Jlc3MgJiYgbWF4SXRlcmF0aW9ucyA9PT0gMCkge1xuICAgICAgICAvLyBUaGUgZHJhaW4gbG9vcCBoaXQgaXRzIGJvdW5kIHdoaWxlIHN0aWxsIG1ha2luZyBwcm9ncmVzcyDigJRcbiAgICAgICAgLy8gcHJvY2VlZGluZyBhcyBpZiBpdCBjb252ZXJnZWQgd291bGQgcHJlc2VudCBhcyBhIG15c3RlcmlvdXNcbiAgICAgICAgLy8gc3VzcGVuc2lvbiBvciByZXBsYXkgZGl2ZXJnZW5jZS4gTWFrZSB0aGUgZ2l2aW5nLXVwIHZpc2libGUgc29cbiAgICAgICAgLy8gYSB3ZWRnZSBpcyBhdHRyaWJ1dGFibGUgdG8gdGhpcyBib3VuZCByYXRoZXIgdGhhbiBhIG15c3RlcnkuXG4gICAgICAgIHJ1bnRpbWVMb2dnZXIud2FybihcbiAgICAgICAgICAnUXVpY2tKUyBydW50aW1lOiBldmVudCBkcmFpbiBsb29wIGhpdCBpdHMgaXRlcmF0aW9uIGJvdW5kIGJlZm9yZSByZWFjaGluZyBhIGZpeGVkIHBvaW50JyxcbiAgICAgICAgICB7XG4gICAgICAgICAgICB3b3JrZmxvd1J1bklkOiB3b3JrZmxvd1J1bi5ydW5JZCxcbiAgICAgICAgICAgIGV2ZW50Q291bnQ6IGV2ZW50cy5sZW5ndGgsXG4gICAgICAgICAgfVxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIC0tLS0gQ2hlY2sgcmVzdWx0IC0tLS1cbiAgICByZXR1cm4gbWFrZUxpdmVTZXNzaW9uKFxuICAgICAgdm0sXG4gICAgICBpbnRlcnJ1cHRCdWRnZXQsXG4gICAgICBhZHZhbmNlQ2xvY2ssXG4gICAgICBvcHRpb25zLmVuY3J5cHRpb25LZXlcbiAgICApO1xuICB9XG59XG5cbi8qKiBTZXNzaW9uIHdyYXBwZXIgZm9yIGEgcmVzdWx0IHdob3NlIFZNIGlzIGFscmVhZHkgc2V0dGxlZC9kaXNwb3NlZC4gKi9cbmZ1bmN0aW9uIG1ha2VTZXR0bGVkU2Vzc2lvbihcbiAgcmVzdWx0OiBRdWlja0pTUnVudGltZVJlc3VsdFxuKTogUXVpY2tKU1dvcmtmbG93U2Vzc2lvbiB7XG4gIHJldHVybiB7XG4gICAgcmVzdWx0LFxuICAgIGNvbnRpbnVlV2l0aEV2ZW50czogKCkgPT4ge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnUXVpY2tKUyB3b3JrZmxvdyBzZXNzaW9uIGlzIHNldHRsZWQg4oCUIGNvbnRpbnVlV2l0aEV2ZW50cyBpcyBvbmx5IHZhbGlkIHdoaWxlIHN1c3BlbmRlZCdcbiAgICAgICk7XG4gICAgfSxcbiAgICBkaXNwb3NlOiAoKSA9PiB7fSxcbiAgfTtcbn1cblxuLyoqXG4gKiBFdmFsdWF0ZSB0aGUgVk0ncyBzdGF0ZSBhbmQgd3JhcCBpdCBpbiBhIGxpdmUgc2Vzc2lvbi4gV2hpbGUgc3VzcGVuZGVkLFxuICogdGhlIFZNIHN0YXlzIGFsaXZlIHNvIGBjb250aW51ZVdpdGhFdmVudHNgIGNhbiByZXN1bWUgaXQgaW4gcGxhY2U7XG4gKiB0ZXJtaW5hbCBzdGF0ZXMgZGlzcG9zZSB0aGUgVk0gaW1tZWRpYXRlbHkgKGluc2lkZSBjaGVja1dvcmtmbG93U3RhdGUpLlxuICovXG5mdW5jdGlvbiBtYWtlTGl2ZVNlc3Npb24oXG4gIHZtOiBRdWlja0pTLFxuICBpbnRlcnJ1cHRCdWRnZXQ6IEludGVycnVwdEJ1ZGdldCxcbiAgYWR2YW5jZUNsb2NrOiAobXM6IG51bWJlcikgPT4gdm9pZCxcbiAgZW5jcnlwdGlvbktleT86IERlY3J5cHRpb25LZXlcbik6IFF1aWNrSlNXb3JrZmxvd1Nlc3Npb24ge1xuICBjb25zdCByZXN1bHQgPSBjaGVja1dvcmtmbG93U3RhdGUodm0sIHsga2VlcEFsaXZlT25TdXNwZW5kOiB0cnVlIH0pO1xuICBsZXQgYWxpdmUgPSAhIXJlc3VsdC5zdXNwZW5kZWQ7XG5cbiAgY29uc3Qgc2Vzc2lvbjogUXVpY2tKU1dvcmtmbG93U2Vzc2lvbiA9IHtcbiAgICByZXN1bHQsXG4gICAgYXN5bmMgY29udGludWVXaXRoRXZlbnRzKFxuICAgICAgbmV3RXZlbnRzOiBFdmVudFtdXG4gICAgKTogUHJvbWlzZTxRdWlja0pTUnVudGltZVJlc3VsdD4ge1xuICAgICAgaWYgKCFhbGl2ZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ1F1aWNrSlMgd29ya2Zsb3cgc2Vzc2lvbiBpcyBub3QgYWxpdmUg4oCUIGNvbnRpbnVlV2l0aEV2ZW50cyBpcyBvbmx5IHZhbGlkIHdoaWxlIHN1c3BlbmRlZCdcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIC8vIEZyZXNoIGV4ZWN1dGlvbiBidXJzdCDigJQgdGhlIGludGVycnVwdCBidWRnZXQgYm91bmRzIFZNIGNvbXB1dGUsXG4gICAgICAvLyBub3Qgd2FsbCB0aW1lIHNwZW50IHdhaXRpbmcgb24gaW5saW5lIHN0ZXBzIGJldHdlZW4gYnVyc3RzLlxuICAgICAgaW50ZXJydXB0QnVkZ2V0LnN0YXJ0ID0gRGF0ZS5ub3coKTtcblxuICAgICAgbGV0IG1heEl0ZXJhdGlvbnMgPSAxMDA7XG4gICAgICBsZXQgbWFkZVByb2dyZXNzOiBib29sZWFuO1xuICAgICAgZG8ge1xuICAgICAgICBtYWRlUHJvZ3Jlc3MgPSBhd2FpdCBwcm9jZXNzRXZlbnRzKFxuICAgICAgICAgIHZtLFxuICAgICAgICAgIG5ld0V2ZW50cyxcbiAgICAgICAgICBhZHZhbmNlQ2xvY2ssXG4gICAgICAgICAgZW5jcnlwdGlvbktleVxuICAgICAgICApO1xuICAgICAgICBsZXQgYmF0Y2g6IG51bWJlcjtcbiAgICAgICAgZG8ge1xuICAgICAgICAgIGJhdGNoID0gdm0uZXhlY3V0ZVBlbmRpbmdKb2JzKCk7XG4gICAgICAgICAgaWYgKGJhdGNoID4gMCkgbWFkZVByb2dyZXNzID0gdHJ1ZTtcbiAgICAgICAgfSB3aGlsZSAoYmF0Y2ggPiAwKTtcbiAgICAgIH0gd2hpbGUgKG1hZGVQcm9ncmVzcyAmJiAtLW1heEl0ZXJhdGlvbnMgPiAwKTtcblxuICAgICAgY29uc3QgbmV4dCA9IGNoZWNrV29ya2Zsb3dTdGF0ZSh2bSwgeyBrZWVwQWxpdmVPblN1c3BlbmQ6IHRydWUgfSk7XG4gICAgICBpZiAoIW5leHQuc3VzcGVuZGVkKSBhbGl2ZSA9IGZhbHNlO1xuICAgICAgc2Vzc2lvbi5yZXN1bHQgPSBuZXh0O1xuICAgICAgcmV0dXJuIG5leHQ7XG4gICAgfSxcbiAgICBkaXNwb3NlKCk6IHZvaWQge1xuICAgICAgaWYgKGFsaXZlKSB7XG4gICAgICAgIGFsaXZlID0gZmFsc2U7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdm0uZGlzcG9zZSgpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyBBbHJlYWR5IGRpc3Bvc2VkIOKAlCBpZ25vcmUuXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICB9O1xuICByZXR1cm4gc2Vzc2lvbjtcbn1cblxuLy8gLS0tLSBFdmVudCBQcm9jZXNzaW5nIC0tLS1cblxuYXN5bmMgZnVuY3Rpb24gcHJvY2Vzc0V2ZW50cyhcbiAgdm06IFF1aWNrSlMsXG4gIGV2ZW50czogRXZlbnRbXSxcbiAgYWR2YW5jZUNsb2NrOiAobXM6IG51bWJlcikgPT4gdm9pZCxcbiAgZW5jcnlwdGlvbktleT86IERlY3J5cHRpb25LZXlcbik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBsZXQgcmVzb2x2ZWQgPSBmYWxzZTtcbiAgZm9yIChjb25zdCBldmVudCBvZiBldmVudHMpIHtcbiAgICAvLyBBZHZhbmNlIHRoZSBWTSdzIGRldGVybWluaXN0aWMgY2xvY2sgdG8gdGhpcyBldmVudCdzIGNyZWF0aW9uIHRpbWVcbiAgICAvLyBCRUZPUkUgcmVzb2x2aW5nIGFueXRoaW5nLCBzbyB3b3JrZmxvdyBjb2RlIHVuYmxvY2tlZCBieSB0aGlzIGV2ZW50XG4gICAgLy8gb2JzZXJ2ZXMgRGF0ZS5ub3coKSBhdCAob3IgYWZ0ZXIg4oCUIHRoZSBjbG9jayBpcyBtb25vdG9uaWMpIHRoZSB0aW1lXG4gICAgLy8gdGhlIGV2ZW50IHdhcyByZWNvcmRlZC4gTWlycm9ycyB0aGUgbm9kZTp2bSBlbmdpbmUnc1xuICAgIC8vIGBvbkNvbnN1bWVkRXZlbnQg4oaSIHVwZGF0ZVRpbWVzdGFtcCgrZXZlbnQuY3JlYXRlZEF0KWAuXG4gICAgYWR2YW5jZUNsb2NrKCtldmVudC5jcmVhdGVkQXQpO1xuXG4gICAgY29uc3QgY2lkID0gZXZlbnQuY29ycmVsYXRpb25JZDtcbiAgICBpZiAoIWNpZCkgY29udGludWU7XG5cbiAgICAvLyBKU09OLnN0cmluZ2lmeSBoYW5kbGVzIHF1b3RlcywgYmFja3NsYXNoZXMgYW5kIGNvbnRyb2wgY2hhcmFjdGVycztcbiAgICAvLyBjb3JyZWxhdGlvbiBpZHMgYXJlIGhvc3QtZ2VuZXJhdGVkIFVMSURzIHRvZGF5LCBidXQgdGhlIGV2YWwtc3RyaW5nXG4gICAgLy8gc2FmZXR5IHNob3VsZG4ndCBkZXBlbmQgb24gdGhhdCBpbnZhcmlhbnQgYmVpbmcgYXNzZXJ0ZWQgbm93aGVyZS5cbiAgICBjb25zdCBjaWRKcyA9IEpTT04uc3RyaW5naWZ5KGNpZCk7XG4gICAgY29uc3QgZXZlbnREYXRhID1cbiAgICAgICdldmVudERhdGEnIGluIGV2ZW50XG4gICAgICAgID8gKGV2ZW50LmV2ZW50RGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilcbiAgICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICAvLyBMb2cgdGhlIGV2ZW50IGFuZCB3aGV0aGVyIHRoZSByZXNvbHZlciBleGlzdHNcbiAgICBzd2l0Y2ggKGV2ZW50LmV2ZW50VHlwZSkge1xuICAgICAgY2FzZSAnc3RlcF9jb21wbGV0ZWQnOiB7XG4gICAgICAgIGNvbnN0IGhhc1Jlc29sdmVyID0gdm0uZHVtcChcbiAgICAgICAgICB2bS5ldmFsQ29kZShgISFnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XWApXG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IHJhd091dHB1dCA9IGV2ZW50RGF0YT8ucmVzdWx0ID8/IGV2ZW50RGF0YT8ub3V0cHV0O1xuICAgICAgICBpZiAoaGFzUmVzb2x2ZXIpIHtcbiAgICAgICAgICBpZiAocmF3T3V0cHV0IGluc3RhbmNlb2YgVWludDhBcnJheSkge1xuICAgICAgICAgICAgLy8gRGVjcnlwdCBpZiBlbmNyeXB0ZWQg4oCUIHRoZSBWTSBvbmx5IHVuZGVyc3RhbmRzICdkZXZsJyBmb3JtYXRcbiAgICAgICAgICAgIHJ1bnRpbWVMb2dnZXIuZGVidWcoJ1F1aWNrSlMgcnVudGltZTogc3RlcCByZXN1bHQgcmF3Jywge1xuICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBjaWQsXG4gICAgICAgICAgICAgIHJhd1ByZWZpeDogbmV3IFRleHREZWNvZGVyKCkuZGVjb2RlKHJhd091dHB1dC5zdWJhcnJheSgwLCA0KSksXG4gICAgICAgICAgICAgIHJhd0J5dGVMZW5ndGg6IHJhd091dHB1dC5ieXRlTGVuZ3RoLFxuICAgICAgICAgICAgICBpc0J1ZmZlcjogQnVmZmVyLmlzQnVmZmVyKHJhd091dHB1dCksXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnN0IGRlY3J5cHRlZE91dHB1dCA9IGF3YWl0IHByZXBhcmVCeXRlc0ZvclZNKFxuICAgICAgICAgICAgICByYXdPdXRwdXQsXG4gICAgICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBydW50aW1lTG9nZ2VyLmRlYnVnKCdRdWlja0pTIHJ1bnRpbWU6IHN0ZXAgcmVzdWx0IGRlY3J5cHRlZCcsIHtcbiAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogY2lkLFxuICAgICAgICAgICAgICBwcmVmaXg6IG5ldyBUZXh0RGVjb2RlcigpLmRlY29kZShkZWNyeXB0ZWRPdXRwdXQuc3ViYXJyYXkoMCwgNCkpLFxuICAgICAgICAgICAgICBieXRlTGVuZ3RoOiBkZWNyeXB0ZWRPdXRwdXQuYnl0ZUxlbmd0aCxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc3QgYnl0ZXNIYW5kbGUgPSB2bS5uZXdVaW50OEFycmF5KGRlY3J5cHRlZE91dHB1dCk7XG4gICAgICAgICAgICB2bS5zZXRQcm9wKHZtLmdsb2JhbCwgJ19fdG1wX3Jlc3VsdCcsIGJ5dGVzSGFuZGxlKTtcbiAgICAgICAgICAgIGJ5dGVzSGFuZGxlLmRpc3Bvc2UoKTtcbiAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICBgZ2xvYmFsVGhpcy5fX3Jlc29sdmVyc1ske2NpZEpzfV0ucmVzb2x2ZShnbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJ3b3JrZmxvdy1kZXNlcmlhbGl6ZVwiKV0oZ2xvYmFsVGhpcy5fX3RtcF9yZXN1bHQpKTtgICtcbiAgICAgICAgICAgICAgICBgZGVsZXRlIGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dO2AgK1xuICAgICAgICAgICAgICAgIGBkZWxldGUgZ2xvYmFsVGhpcy5fX3RtcF9yZXN1bHQ7YFxuICAgICAgICAgICAgKS5kaXNwb3NlKCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJ1bnRpbWVMb2dnZXIuZGVidWcoJ1F1aWNrSlMgcnVudGltZTogc3RlcCByZXN1bHQgbm9uLWJpbmFyeScsIHtcbiAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogY2lkLFxuICAgICAgICAgICAgICB0eXBlOiB0eXBlb2YgcmF3T3V0cHV0LFxuICAgICAgICAgICAgICBpc051bGw6IHJhd091dHB1dCA9PT0gbnVsbCxcbiAgICAgICAgICAgICAgaXNVbmRlZmluZWQ6IHJhd091dHB1dCA9PT0gdW5kZWZpbmVkLFxuICAgICAgICAgICAgICBjb25zdHJ1Y3RvcjogcmF3T3V0cHV0Py5jb25zdHJ1Y3Rvcj8ubmFtZSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc3Qgc2VyaWFsaXplZCA9XG4gICAgICAgICAgICAgIHJhd091dHB1dCAhPT0gdW5kZWZpbmVkID8gSlNPTi5zdHJpbmdpZnkocmF3T3V0cHV0KSA6ICd1bmRlZmluZWQnO1xuICAgICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICAgIGBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XS5yZXNvbHZlKCR7c2VyaWFsaXplZH0pO2AgK1xuICAgICAgICAgICAgICAgIGBkZWxldGUgZ2xvYmFsVGhpcy5fX3Jlc29sdmVyc1ske2NpZEpzfV07YFxuICAgICAgICAgICAgKS5kaXNwb3NlKCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIERyYWluIEFMTCBtaWNyb3Rhc2tzIGFmdGVyIHJlc29sdmVcbiAgICAgICAgICB7XG4gICAgICAgICAgICByZXNvbHZlZCA9IHRydWU7XG4gICAgICAgICAgICBsZXQgYjogbnVtYmVyO1xuICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICBiID0gdm0uZXhlY3V0ZVBlbmRpbmdKb2JzKCk7XG4gICAgICAgICAgICB9IHdoaWxlIChiID4gMCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE5vIHJlc29sdmVyIHlldCDigJQgYnVmZmVyIHRoZSBwcmVwYXJlZCBvdXRjb21lIHNvIHRoZSBwcm9taXNlXG4gICAgICAgICAgLy8gc2V0dGxlcyB0aGUgbW9tZW50IHRoZSBWTSBjb25zdHJ1Y3RzIGl0IChzZWUgX190ZXJtaW5hbEJ1ZmZlclxuICAgICAgICAgIC8vIGluIHRoZSBib290c3RyYXApLiBXaXRob3V0IHRoaXMsIHRoZSBsaXZlLWNvbnRpbnVhdGlvbiBwYXRoXG4gICAgICAgICAgLy8gKHdoaWNoIHNjYW5zIGVhY2ggZGVsdGEgZXhhY3RseSBvbmNlKSBkcm9wcyB0aGUgdGVybWluYWwgYW5kXG4gICAgICAgICAgLy8gdGhlIGF3YWl0IG5ldmVyIHNldHRsZXMuXG4gICAgICAgICAgaWYgKHJhd091dHB1dCBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpIHtcbiAgICAgICAgICAgIGNvbnN0IGRlY3J5cHRlZE91dHB1dCA9IGF3YWl0IHByZXBhcmVCeXRlc0ZvclZNKFxuICAgICAgICAgICAgICByYXdPdXRwdXQsXG4gICAgICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCBieXRlc0hhbmRsZSA9IHZtLm5ld1VpbnQ4QXJyYXkoZGVjcnlwdGVkT3V0cHV0KTtcbiAgICAgICAgICAgIHZtLnNldFByb3Aodm0uZ2xvYmFsLCAnX190bXBfYnVmJywgYnl0ZXNIYW5kbGUpO1xuICAgICAgICAgICAgYnl0ZXNIYW5kbGUuZGlzcG9zZSgpO1xuICAgICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICAgIGBnbG9iYWxUaGlzLl9fdGVybWluYWxCdWZmZXJbJHtjaWRKc31dID0geyBraW5kOiBcInJlc29sdmVfYnl0ZXNcIiwgYnl0ZXM6IGdsb2JhbFRoaXMuX190bXBfYnVmIH07YCArXG4gICAgICAgICAgICAgICAgYGRlbGV0ZSBnbG9iYWxUaGlzLl9fdG1wX2J1ZjtgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3Qgc2VyaWFsaXplZCA9XG4gICAgICAgICAgICAgIHJhd091dHB1dCAhPT0gdW5kZWZpbmVkID8gSlNPTi5zdHJpbmdpZnkocmF3T3V0cHV0KSA6ICd1bmRlZmluZWQnO1xuICAgICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICAgIGBnbG9iYWxUaGlzLl9fdGVybWluYWxCdWZmZXJbJHtjaWRKc31dID0geyBraW5kOiBcInJlc29sdmVfdmFsdWVcIiwgdmFsdWU6ICR7c2VyaWFsaXplZH0gfTtgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbWFya0NyZWF0ZWQodm0sIGNpZEpzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjYXNlICdzdGVwX2ZhaWxlZCc6IHtcbiAgICAgICAgY29uc3QgaGFzUmVzb2x2ZXIgPSB2bS5kdW1wKFxuICAgICAgICAgIHZtLmV2YWxDb2RlKGAhIWdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dYClcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKGhhc1Jlc29sdmVyKSB7XG4gICAgICAgICAgY29uc3QgZXJyb3JEYXRhID0gZXZlbnREYXRhPy5lcnJvcjtcbiAgICAgICAgICBpZiAoZXJyb3JEYXRhIGluc3RhbmNlb2YgVWludDhBcnJheSkge1xuICAgICAgICAgICAgLy8gTW9kZXJuIHBhdGggKHBvc3QtIzE4NTEpOiB0aGUgc3RlcCBoYW5kbGVyIGRlaHlkcmF0ZWQgdGhlXG4gICAgICAgICAgICAvLyB0aHJvd24gdmFsdWUgdGhyb3VnaCB0aGUgZmlyc3QtY2xhc3MgZXJyb3IgcGlwZWxpbmUuIERlY3J5cHRcbiAgICAgICAgICAgIC8vIChpZiBlbmNyeXB0ZWQpIGFuZCBwYXNzIHRoZSBieXRlcyB0byB0aGUgVk0tc2lkZSBkZXNlcmlhbGl6ZXJcbiAgICAgICAgICAgIC8vIHNvIHRoZSB3b3JrZmxvdyBjYXRjaCBzZWVzIGEgcHJvcGVybHkgdHlwZWQgRXJyb3Igc3ViY2xhc3NcbiAgICAgICAgICAgIC8vIChUeXBlRXJyb3IsIEZhdGFsRXJyb3Igd2l0aCBvcmlnaW5hbCBjYXVzZSBjaGFpbiwgZXRjLikgd2l0aFxuICAgICAgICAgICAgLy8gdGhlIG9yaWdpbmFsIG1lc3NhZ2UgYW5kIHN0YWNrIHByZXNlcnZlZC5cbiAgICAgICAgICAgIGNvbnN0IGRlY3J5cHRlZCA9IGF3YWl0IHByZXBhcmVCeXRlc0ZvclZNKGVycm9yRGF0YSwgZW5jcnlwdGlvbktleSk7XG4gICAgICAgICAgICBjb25zdCBieXRlc0hhbmRsZSA9IHZtLm5ld1VpbnQ4QXJyYXkoZGVjcnlwdGVkKTtcbiAgICAgICAgICAgIHZtLnNldFByb3Aodm0uZ2xvYmFsLCAnX190bXBfZXJyb3InLCBieXRlc0hhbmRsZSk7XG4gICAgICAgICAgICBieXRlc0hhbmRsZS5kaXNwb3NlKCk7XG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYChmdW5jdGlvbigpe2AgK1xuICAgICAgICAgICAgICAgIGB2YXIgZT1nbG9iYWxUaGlzW1N5bWJvbC5mb3IoXCJ3b3JrZmxvdy1kZXNlcmlhbGl6ZVwiKV0oZ2xvYmFsVGhpcy5fX3RtcF9lcnJvcik7YCArXG4gICAgICAgICAgICAgICAgYGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dLnJlamVjdChlKTtgICtcbiAgICAgICAgICAgICAgICBgZGVsZXRlIGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dO2AgK1xuICAgICAgICAgICAgICAgIGBkZWxldGUgZ2xvYmFsVGhpcy5fX3RtcF9lcnJvcjtgICtcbiAgICAgICAgICAgICAgICBgfSkoKWBcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBMZWdhY3kgcGF0aDogcHJlLXBpcGVsaW5lIGV2ZW50cyBzdG9yZWQgZXJyb3IgYXNcbiAgICAgICAgICAgIC8vIGB7IG1lc3NhZ2UsIHN0YWNrLCBjb2RlIH1gLiBSZWNvbnN0cnVjdCBhIEZhdGFsRXJyb3Igc29cbiAgICAgICAgICAgIC8vIHdvcmtmbG93IGNhdGNoIGNhbiBkZXRlY3QgaXQgdmlhIEZhdGFsRXJyb3IuaXMoKSwgbWF0Y2hpbmdcbiAgICAgICAgICAgIC8vIHRoZSBvcmlnaW5hbCBWMSBzdGVwIGhhbmRsZXIgYmVoYXZpb3IuXG4gICAgICAgICAgICBjb25zdCBpc0Vycm9yT2JqZWN0ID1cbiAgICAgICAgICAgICAgdHlwZW9mIGVycm9yRGF0YSA9PT0gJ29iamVjdCcgJiYgZXJyb3JEYXRhICE9PSBudWxsO1xuICAgICAgICAgICAgY29uc3QgbXNnID0gaXNFcnJvck9iamVjdFxuICAgICAgICAgICAgICA/ICgoKGVycm9yRGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikubWVzc2FnZSBhcyBzdHJpbmcpID8/XG4gICAgICAgICAgICAgICAgJ1N0ZXAgZmFpbGVkJylcbiAgICAgICAgICAgICAgOiB0eXBlb2YgZXJyb3JEYXRhID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgICAgID8gZXJyb3JEYXRhXG4gICAgICAgICAgICAgICAgOiAnU3RlcCBmYWlsZWQnO1xuICAgICAgICAgICAgY29uc3QgZXJyb3JTdGFjayA9XG4gICAgICAgICAgICAgIChpc0Vycm9yT2JqZWN0XG4gICAgICAgICAgICAgICAgPyAoZXJyb3JEYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5zdGFja1xuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkKSA/PyAoZXZlbnREYXRhPy5zdGFjayBhcyBzdHJpbmcgfCB1bmRlZmluZWQpO1xuICAgICAgICAgICAgY29uc3Qgc3RhY2tBc3NpZ25tZW50ID0gZXJyb3JTdGFja1xuICAgICAgICAgICAgICA/IGBlLnN0YWNrPSR7SlNPTi5zdHJpbmdpZnkoZXJyb3JTdGFjayl9O2BcbiAgICAgICAgICAgICAgOiAnJztcbiAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICBgKGZ1bmN0aW9uKCl7dmFyIGU9bmV3IEVycm9yKCR7SlNPTi5zdHJpbmdpZnkobXNnKX0pO2UubmFtZT1cIkZhdGFsRXJyb3JcIjtlLmZhdGFsPXRydWU7JHtzdGFja0Fzc2lnbm1lbnR9YCArXG4gICAgICAgICAgICAgICAgYGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dLnJlamVjdChlKTtgICtcbiAgICAgICAgICAgICAgICBgZGVsZXRlIGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dO30pKClgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAge1xuICAgICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgbGV0IGI6IG51bWJlcjtcbiAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgYiA9IHZtLmV4ZWN1dGVQZW5kaW5nSm9icygpO1xuICAgICAgICAgICAgfSB3aGlsZSAoYiA+IDApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBObyByZXNvbHZlciB5ZXQg4oCUIGJ1ZmZlciB0aGUgcHJlcGFyZWQgcmVqZWN0aW9uIChzZWUgdGhlXG4gICAgICAgICAgLy8gc3RlcF9jb21wbGV0ZWQgYnJhbmNoIGFib3ZlIGZvciB0aGUgcmF0aW9uYWxlKS5cbiAgICAgICAgICBjb25zdCBlcnJvckRhdGEgPSBldmVudERhdGE/LmVycm9yO1xuICAgICAgICAgIGlmIChlcnJvckRhdGEgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICAgICAgICBjb25zdCBkZWNyeXB0ZWQgPSBhd2FpdCBwcmVwYXJlQnl0ZXNGb3JWTShlcnJvckRhdGEsIGVuY3J5cHRpb25LZXkpO1xuICAgICAgICAgICAgY29uc3QgYnl0ZXNIYW5kbGUgPSB2bS5uZXdVaW50OEFycmF5KGRlY3J5cHRlZCk7XG4gICAgICAgICAgICB2bS5zZXRQcm9wKHZtLmdsb2JhbCwgJ19fdG1wX2J1ZicsIGJ5dGVzSGFuZGxlKTtcbiAgICAgICAgICAgIGJ5dGVzSGFuZGxlLmRpc3Bvc2UoKTtcbiAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICBgZ2xvYmFsVGhpcy5fX3Rlcm1pbmFsQnVmZmVyWyR7Y2lkSnN9XSA9IHsga2luZDogXCJyZWplY3RfYnl0ZXNcIiwgYnl0ZXM6IGdsb2JhbFRoaXMuX190bXBfYnVmIH07YCArXG4gICAgICAgICAgICAgICAgYGRlbGV0ZSBnbG9iYWxUaGlzLl9fdG1wX2J1ZjtgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgaXNFcnJvck9iamVjdCA9XG4gICAgICAgICAgICAgIHR5cGVvZiBlcnJvckRhdGEgPT09ICdvYmplY3QnICYmIGVycm9yRGF0YSAhPT0gbnVsbDtcbiAgICAgICAgICAgIGNvbnN0IG1zZyA9IGlzRXJyb3JPYmplY3RcbiAgICAgICAgICAgICAgPyAoKChlcnJvckRhdGEgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLm1lc3NhZ2UgYXMgc3RyaW5nKSA/P1xuICAgICAgICAgICAgICAgICdTdGVwIGZhaWxlZCcpXG4gICAgICAgICAgICAgIDogdHlwZW9mIGVycm9yRGF0YSA9PT0gJ3N0cmluZydcbiAgICAgICAgICAgICAgICA/IGVycm9yRGF0YVxuICAgICAgICAgICAgICAgIDogJ1N0ZXAgZmFpbGVkJztcbiAgICAgICAgICAgIGNvbnN0IGVycm9yU3RhY2sgPVxuICAgICAgICAgICAgICAoaXNFcnJvck9iamVjdFxuICAgICAgICAgICAgICAgID8gKGVycm9yRGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikuc3RhY2tcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZCkgPz8gKGV2ZW50RGF0YT8uc3RhY2sgYXMgc3RyaW5nIHwgdW5kZWZpbmVkKTtcbiAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICBgZ2xvYmFsVGhpcy5fX3Rlcm1pbmFsQnVmZmVyWyR7Y2lkSnN9XSA9IHsga2luZDogXCJyZWplY3RfZXJyb3JcIiwgbWVzc2FnZTogJHtKU09OLnN0cmluZ2lmeShtc2cpfSwgc3RhY2s6ICR7ZXJyb3JTdGFjayA/IEpTT04uc3RyaW5naWZ5KGVycm9yU3RhY2spIDogJ3VuZGVmaW5lZCd9IH07YFxuICAgICAgICAgICAgKS5kaXNwb3NlKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIG1hcmtDcmVhdGVkKHZtLCBjaWRKcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgY2FzZSAnd2FpdF9jb21wbGV0ZWQnOiB7XG4gICAgICAgIGNvbnN0IGhhc1Jlc29sdmVyID0gdm0uZHVtcChcbiAgICAgICAgICB2bS5ldmFsQ29kZShgISFnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XWApXG4gICAgICAgICk7XG4gICAgICAgIGlmIChoYXNSZXNvbHZlcikge1xuICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgYGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dLnJlc29sdmUoKTtgICtcbiAgICAgICAgICAgICAgYGRlbGV0ZSBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XTtgXG4gICAgICAgICAgKS5kaXNwb3NlKCk7XG4gICAgICAgICAge1xuICAgICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgbGV0IGI6IG51bWJlcjtcbiAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgYiA9IHZtLmV4ZWN1dGVQZW5kaW5nSm9icygpO1xuICAgICAgICAgICAgfSB3aGlsZSAoYiA+IDApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBObyByZXNvbHZlciB5ZXQg4oCUIGJ1ZmZlciAoc2VlIHN0ZXBfY29tcGxldGVkIGFib3ZlKS5cbiAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgIGBnbG9iYWxUaGlzLl9fdGVybWluYWxCdWZmZXJbJHtjaWRKc31dID0geyBraW5kOiBcInJlc29sdmVfdW5kZWZpbmVkXCIgfTtgXG4gICAgICAgICAgKS5kaXNwb3NlKCk7XG4gICAgICAgIH1cbiAgICAgICAgbWFya0NyZWF0ZWQodm0sIGNpZEpzKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjYXNlICdhdHRyX3NldCc6IHtcbiAgICAgICAgLy8gT25seSB3b3JrZmxvdy13cml0dGVuIGF0dHJpYnV0ZSBldmVudHMgcmVzb2x2ZSBhIHBlbmRpbmdcbiAgICAgICAgLy8gc2V0QXR0cmlidXRlcygpIHByb21pc2U7IHN0ZXAvc3lzdGVtIHdyaXRlcnMgc2hhcmUgbm9cbiAgICAgICAgLy8gY29ycmVsYXRpb25JZHMgd2l0aCBWTSByZXNvbHZlcnMsIHNvIHRoZSBndWFyZCBpcyBkZWZlbnNpdmUuXG4gICAgICAgIGNvbnN0IHdyaXRlciA9IChldmVudERhdGE/LndyaXRlciBhcyB7IHR5cGU/OiBzdHJpbmcgfSB8IHVuZGVmaW5lZClcbiAgICAgICAgICA/LnR5cGU7XG4gICAgICAgIGlmICh3cml0ZXIgIT09ICd3b3JrZmxvdycpIGJyZWFrO1xuICAgICAgICBjb25zdCBoYXNSZXNvbHZlciA9IHZtLmR1bXAoXG4gICAgICAgICAgdm0uZXZhbENvZGUoYCEhZ2xvYmFsVGhpcy5fX3Jlc29sdmVyc1ske2NpZEpzfV1gKVxuICAgICAgICApO1xuICAgICAgICBpZiAoIWhhc1Jlc29sdmVyKSB7XG4gICAgICAgICAgLy8gTm8gcmVzb2x2ZXIgeWV0IOKAlCBidWZmZXIgKHNlZSBzdGVwX2NvbXBsZXRlZCBhYm92ZSkuXG4gICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICBgZ2xvYmFsVGhpcy5fX3Rlcm1pbmFsQnVmZmVyWyR7Y2lkSnN9XSA9IHsga2luZDogXCJyZXNvbHZlX3VuZGVmaW5lZFwiIH07YFxuICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChoYXNSZXNvbHZlcikge1xuICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgYGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dLnJlc29sdmUoKTtgICtcbiAgICAgICAgICAgICAgYGRlbGV0ZSBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XTtgXG4gICAgICAgICAgKS5kaXNwb3NlKCk7XG4gICAgICAgICAge1xuICAgICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgbGV0IGI6IG51bWJlcjtcbiAgICAgICAgICAgIGRvIHtcbiAgICAgICAgICAgICAgYiA9IHZtLmV4ZWN1dGVQZW5kaW5nSm9icygpO1xuICAgICAgICAgICAgfSB3aGlsZSAoYiA+IDApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBtYXJrQ3JlYXRlZCh2bSwgY2lkSnMpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ2hvb2tfcmVjZWl2ZWQnOiB7XG4gICAgICAgIC8vIENoZWNrIGlmIHRoaXMgZXZlbnQgd2FzIGFscmVhZHkgcHJvY2Vzc2VkIChkZWxpdmVyZWQgb3JcbiAgICAgICAgLy8gYnVmZmVyZWQpIHdpdGhpbiB0aGlzIGludm9jYXRpb24uIFByZXZlbnRzIGRvdWJsZS1kZWxpdmVyeSB3aGVuXG4gICAgICAgIC8vIHRoZSBvdXRlciBsb29wIHJlLXNjYW5zIGV2ZW50cy5cbiAgICAgICAgY29uc3QgYWxyZWFkeVByb2Nlc3NlZCA9IGV2ZW50LmV2ZW50SWRcbiAgICAgICAgICA/IHZtLmR1bXAoXG4gICAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICAgIGAhIShnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXIuX19wcm9jZXNzZWRFdmVudElkcyAmJiBnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXIuX19wcm9jZXNzZWRFdmVudElkc1ske0pTT04uc3RyaW5naWZ5KGV2ZW50LmV2ZW50SWQpfV0pYFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApXG4gICAgICAgICAgOiBmYWxzZTtcbiAgICAgICAgaWYgKGFscmVhZHlQcm9jZXNzZWQpIHtcbiAgICAgICAgICBydW50aW1lTG9nZ2VyLmRlYnVnKFxuICAgICAgICAgICAgJ1F1aWNrSlMgcnVudGltZTogaG9va19yZWNlaXZlZCBhbHJlYWR5IHByb2Nlc3NlZCcsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IGNpZCxcbiAgICAgICAgICAgICAgZXZlbnRJZDogZXZlbnQuZXZlbnRJZCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICApO1xuICAgICAgICAgIG1hcmtDcmVhdGVkKHZtLCBjaWRKcyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBSZXNpbGllbnQtcmVzdW1lIGRlZHVwIChwYXJpdHkgd2l0aCB0aGUgbm9kZSBlbmdpbmUnc1xuICAgICAgICAvLyBFdmVudHNDb25zdW1lciBpbiB3b3JrZmxvdy9ob29rLnRzKTogdHdvIGhvb2tfcmVjZWl2ZWQgcm93cyBmb3JcbiAgICAgICAgLy8gT05FIHJlc3VtZSBhdHRlbXB0IHNoYXJlIGEgY2xpZW50LW1pbnRlZCBgcmVzdW1lSWRgIChhIGR1cGxpY2F0ZVxuICAgICAgICAvLyBjYW4gYmUgY29tbWl0dGVkIHdoZW4gdGhlIG1hdGVyaWFsaXphdGlvbiBmYWxsYmFjayByYWNlcyBhXG4gICAgICAgIC8vIGRlbGF5ZWQgZGlyZWN0IHdyaXRlIOKAlCBob29rX3JlY2VpdmVkIGhhcyBubyBzdG9yYWdlIHVuaXF1ZW5lc3NcbiAgICAgICAgLy8gY29uc3RyYWludCkuIERlbGl2ZXIgb25seSB0aGUgZmlyc3QtaW4tbG9nIG9jY3VycmVuY2UuIFRoZSBzZWVuXG4gICAgICAgIC8vIHNldCBsaXZlcyBpbiB0aGUgVk0gaGVhcCBzbyBpdCBpcyBkZXRlcm1pbmlzdGljIHBlciByZXBsYXkgYW5kXG4gICAgICAgIC8vIHN1cnZpdmVzIGV2ZW50IHJlLXNjYW5zIHdpdGhpbiB0aGUgaW52b2NhdGlvbi4gRXZlbnRzIHdpdGhvdXQgYVxuICAgICAgICAvLyByZXN1bWVJZCAob2xkZXIgU0RLcykgYXJlIG5ldmVyIGRlZHVwZWQuXG4gICAgICAgIHtcbiAgICAgICAgICAvLyBUb3AtbGV2ZWwgZXZlbnQucmVzdW1lSWQgaXMgdGhlIGNhbm9uaWNhbCBsb2NhdGlvbiAodGhlIGJhY2tlbmRcbiAgICAgICAgICAvLyBob2lzdHMgaXQgdG8gYSBmaXJzdC1jbGFzcyBjb2x1bW4pOyB0aGUgbmVzdGVkXG4gICAgICAgICAgLy8gZXZlbnREYXRhLnJlc3VtZUlkIGZvcm0gaXMgYSBkZXByZWNhdGVkIGxlZ2FjeSBmYWxsYmFjayDigJRcbiAgICAgICAgICAvLyBtaXJyb3JzIHRoZSBub2RlIGVuZ2luZSdzIGRlZHVwIGluIHdvcmtmbG93L2hvb2sudHMuXG4gICAgICAgICAgY29uc3QgcmVzdW1lSWQgPVxuICAgICAgICAgICAgKGV2ZW50IGFzIHsgcmVzdW1lSWQ/OiB1bmtub3duIH0pLnJlc3VtZUlkID8/XG4gICAgICAgICAgICAoZXZlbnREYXRhIGFzIHsgcmVzdW1lSWQ/OiB1bmtub3duIH0gfCB1bmRlZmluZWQpPy5yZXN1bWVJZDtcbiAgICAgICAgICBpZiAodHlwZW9mIHJlc3VtZUlkID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgY29uc3QgcmVzdW1lSWRKcyA9IEpTT04uc3RyaW5naWZ5KHJlc3VtZUlkKTtcbiAgICAgICAgICAgIGNvbnN0IGR1cGxpY2F0ZSA9IHZtLmR1bXAoXG4gICAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICAgIGAoZ2xvYmFsVGhpcy5fX2hvb2tTZWVuUmVzdW1lSWRzID0gZ2xvYmFsVGhpcy5fX2hvb2tTZWVuUmVzdW1lSWRzIHx8IHt9KVske3Jlc3VtZUlkSnN9XSA9PT0gdHJ1ZWBcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGlmIChkdXBsaWNhdGUpIHtcbiAgICAgICAgICAgICAgcnVudGltZUxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgICAgICAgICAnUXVpY2tKUyBydW50aW1lOiBkdXBsaWNhdGUgaG9va19yZWNlaXZlZCBmb3IgdGhlIHNhbWUgcmVzdW1lIGF0dGVtcHQsIGRyb3BwaW5nJyxcbiAgICAgICAgICAgICAgICB7IGNvcnJlbGF0aW9uSWQ6IGNpZCwgZXZlbnRJZDogZXZlbnQuZXZlbnRJZCwgcmVzdW1lSWQgfVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBpZiAoZXZlbnQuZXZlbnRJZCkge1xuICAgICAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICAgICAgYChnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXIuX19wcm9jZXNzZWRFdmVudElkcyA9IGdsb2JhbFRoaXMuX19ob29rUGF5bG9hZEJ1ZmZlci5fX3Byb2Nlc3NlZEV2ZW50SWRzIHx8IHt9KVske0pTT04uc3RyaW5naWZ5KGV2ZW50LmV2ZW50SWQpfV0gPSB0cnVlO2BcbiAgICAgICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBtYXJrQ3JlYXRlZCh2bSwgY2lkSnMpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgICBgZ2xvYmFsVGhpcy5fX2hvb2tTZWVuUmVzdW1lSWRzWyR7cmVzdW1lSWRKc31dID0gdHJ1ZTtgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBYm9ydCBkZWxpdmVyeTogaG9va19yZWNlaXZlZCBmb3IgYW4gQWJvcnRDb250cm9sbGVyJ3Mgc3lzdGVtXG4gICAgICAgIC8vIGhvb2sgZmxpcHMgdGhlIHJlZ2lzdGVyZWQgc2lnbmFsIGluc3RlYWQgb2YgcmVzb2x2aW5nIGEgcHJvbWlzZS5cbiAgICAgICAgLy8gVGhlIHBheWxvYWQgaXMgdGhlIGRlaHlkcmF0ZWQgYHsgYWJvcnRlZDogdHJ1ZSwgcmVhc29uIH1gIG9iamVjdC5cbiAgICAgICAgY29uc3QgaXNBYm9ydEhvb2sgPSB2bS5kdW1wKFxuICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgYCEhKGdsb2JhbFRoaXMuX19hYm9ydFNpZ25hbHMgJiYgZ2xvYmFsVGhpcy5fX2Fib3J0U2lnbmFsc1ske2NpZEpzfV0pYFxuICAgICAgICAgIClcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKGlzQWJvcnRIb29rKSB7XG4gICAgICAgICAgY29uc3QgcmF3QWJvcnRQYXlsb2FkID0gZXZlbnREYXRhPy5wYXlsb2FkO1xuICAgICAgICAgIGlmIChyYXdBYm9ydFBheWxvYWQgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICAgICAgICBjb25zdCBkZWNyeXB0ZWQgPSBhd2FpdCBwcmVwYXJlQnl0ZXNGb3JWTShcbiAgICAgICAgICAgICAgcmF3QWJvcnRQYXlsb2FkLFxuICAgICAgICAgICAgICBlbmNyeXB0aW9uS2V5XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgY29uc3QgYnl0ZXNIYW5kbGUgPSB2bS5uZXdVaW50OEFycmF5KGRlY3J5cHRlZCk7XG4gICAgICAgICAgICB2bS5zZXRQcm9wKHZtLmdsb2JhbCwgJ19fdG1wX2Fib3J0JywgYnl0ZXNIYW5kbGUpO1xuICAgICAgICAgICAgYnl0ZXNIYW5kbGUuZGlzcG9zZSgpO1xuICAgICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICAgIGAoZnVuY3Rpb24oKXtgICtcbiAgICAgICAgICAgICAgICBgdmFyIHA9Z2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwid29ya2Zsb3ctZGVzZXJpYWxpemVcIildKGdsb2JhbFRoaXMuX190bXBfYWJvcnQpO2AgK1xuICAgICAgICAgICAgICAgIGBkZWxldGUgZ2xvYmFsVGhpcy5fX3RtcF9hYm9ydDtgICtcbiAgICAgICAgICAgICAgICBgZ2xvYmFsVGhpcy5fX2Fib3J0U2lnbmFsc1ske2NpZEpzfV0uX3NldEFib3J0ZWQocCYmdHlwZW9mIHA9PT1cIm9iamVjdFwiP3AucmVhc29uOnVuZGVmaW5lZCk7YCArXG4gICAgICAgICAgICAgICAgYH0pKClgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICAgIGBnbG9iYWxUaGlzLl9fYWJvcnRTaWduYWxzWyR7Y2lkSnN9XS5fc2V0QWJvcnRlZCh1bmRlZmluZWQpO2BcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBUaGUgYWJvcnQgaXMgZHVyYWJseSByZWNvcmRlZCDigJQgY2xlYXIgdGhlIHBlbmRpbmcgb3Anc1xuICAgICAgICAgIC8vIGFib3J0UmVxdWVzdGVkIG1hcmtlciBzbyB0aGUgaG9zdCBkb2Vzbid0IHJlLXJlY29yZCBpdCAodGhlXG4gICAgICAgICAgLy8gd29ya2Zsb3cncyBvd24gYWJvcnQoKSBjYWxsIGNhbiBzZXQgdGhlIGZsYWcgYmVmb3JlIHRoaXNcbiAgICAgICAgICAvLyBldmVudCBpcyBwcm9jZXNzZWQgd2hlbiBpdCBoYXBwZW5zIGxhdGVyIGluIHJlcGxheSBvcmRlcixcbiAgICAgICAgICAvLyBhbmQgaG9va19yZWNlaXZlZCBldmVudHMgYXJlIG5vdCB1bmlxdWUgcGVyIGNvcnJlbGF0aW9uSWQpLlxuICAgICAgICAgIHZtLmV2YWxDb2RlKFxuICAgICAgICAgICAgYChmdW5jdGlvbigpe2AgK1xuICAgICAgICAgICAgICBgdmFyIHA9Z2xvYmFsVGhpcy5fX3BlbmRpbmcuZmluZChmdW5jdGlvbihxKXtyZXR1cm4gcS5jb3JyZWxhdGlvbklkPT09JHtKU09OLnN0cmluZ2lmeShjaWQpfSYmcS50eXBlPT09XCJob29rXCI7fSk7YCArXG4gICAgICAgICAgICAgIGBpZihwKXAuYWJvcnRSZXF1ZXN0ZWQ9ZmFsc2U7YCArXG4gICAgICAgICAgICAgIGB9KSgpYFxuICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIGlmIChldmVudC5ldmVudElkKSB7XG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYChnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXIuX19wcm9jZXNzZWRFdmVudElkcyA9IGdsb2JhbFRoaXMuX19ob29rUGF5bG9hZEJ1ZmZlci5fX3Byb2Nlc3NlZEV2ZW50SWRzIHx8IHt9KVske0pTT04uc3RyaW5naWZ5KGV2ZW50LmV2ZW50SWQpfV0gPSB0cnVlO2BcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB7XG4gICAgICAgICAgICByZXNvbHZlZCA9IHRydWU7XG4gICAgICAgICAgICBsZXQgYjogbnVtYmVyO1xuICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICBiID0gdm0uZXhlY3V0ZVBlbmRpbmdKb2JzKCk7XG4gICAgICAgICAgICB9IHdoaWxlIChiID4gMCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIG1hcmtDcmVhdGVkKHZtLCBjaWRKcyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBoYXNSZXNvbHZlciA9IHZtLmR1bXAoXG4gICAgICAgICAgdm0uZXZhbENvZGUoYCEhZ2xvYmFsVGhpcy5fX3Jlc29sdmVyc1ske2NpZEpzfV1gKVxuICAgICAgICApO1xuICAgICAgICBjb25zdCByYXdQYXlsb2FkID0gZXZlbnREYXRhPy5wYXlsb2FkID8/IGV2ZW50RGF0YT8ucmVzdWx0O1xuICAgICAgICBydW50aW1lTG9nZ2VyLmRlYnVnKCdRdWlja0pTIHJ1bnRpbWU6IHByb2Nlc3NpbmcgaG9va19yZWNlaXZlZCcsIHtcbiAgICAgICAgICBjb3JyZWxhdGlvbklkOiBjaWQsXG4gICAgICAgICAgZXZlbnRJZDogZXZlbnQuZXZlbnRJZCxcbiAgICAgICAgICBoYXNSZXNvbHZlcixcbiAgICAgICAgICBwYXlsb2FkVHlwZTogdHlwZW9mIHJhd1BheWxvYWQsXG4gICAgICAgICAgcGF5bG9hZElzVWludDhBcnJheTogcmF3UGF5bG9hZCBpbnN0YW5jZW9mIFVpbnQ4QXJyYXksXG4gICAgICAgICAgcGF5bG9hZEtleXM6XG4gICAgICAgICAgICByYXdQYXlsb2FkICYmIHR5cGVvZiByYXdQYXlsb2FkID09PSAnb2JqZWN0J1xuICAgICAgICAgICAgICA/IE9iamVjdC5rZXlzKHJhd1BheWxvYWQpXG4gICAgICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgICB9KTtcbiAgICAgICAgaWYgKGhhc1Jlc29sdmVyKSB7XG4gICAgICAgICAgaWYgKHJhd1BheWxvYWQgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICAgICAgICAvLyBEZWNyeXB0IGlmIGVuY3J5cHRlZCDigJQgdGhlIFZNIG9ubHkgdW5kZXJzdGFuZHMgJ2RldmwnIGZvcm1hdFxuICAgICAgICAgICAgY29uc3QgZGVjcnlwdGVkUGF5bG9hZCA9IGF3YWl0IHByZXBhcmVCeXRlc0ZvclZNKFxuICAgICAgICAgICAgICByYXdQYXlsb2FkLFxuICAgICAgICAgICAgICBlbmNyeXB0aW9uS2V5XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgY29uc3QgYnl0ZXNIYW5kbGUgPSB2bS5uZXdVaW50OEFycmF5KGRlY3J5cHRlZFBheWxvYWQpO1xuICAgICAgICAgICAgdm0uc2V0UHJvcCh2bS5nbG9iYWwsICdfX3RtcF9yZXN1bHQnLCBieXRlc0hhbmRsZSk7XG4gICAgICAgICAgICBieXRlc0hhbmRsZS5kaXNwb3NlKCk7XG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dLnJlc29sdmUoZ2xvYmFsVGhpc1tTeW1ib2wuZm9yKFwid29ya2Zsb3ctZGVzZXJpYWxpemVcIildKGdsb2JhbFRoaXMuX190bXBfcmVzdWx0KSk7YCArXG4gICAgICAgICAgICAgICAgYGRlbGV0ZSBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XTtgICtcbiAgICAgICAgICAgICAgICBgZGVsZXRlIGdsb2JhbFRoaXMuX190bXBfcmVzdWx0O2BcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBzZXJpYWxpemVkID1cbiAgICAgICAgICAgICAgcmF3UGF5bG9hZCAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgPyBKU09OLnN0cmluZ2lmeShyYXdQYXlsb2FkKVxuICAgICAgICAgICAgICAgIDogJ3VuZGVmaW5lZCc7XG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbJHtjaWRKc31dLnJlc29sdmUoJHtzZXJpYWxpemVkfSk7YCArXG4gICAgICAgICAgICAgICAgYGRlbGV0ZSBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzWyR7Y2lkSnN9XTtgXG4gICAgICAgICAgICApLmRpc3Bvc2UoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gTWFyayB0aGlzIGV2ZW50IGFzIHByb2Nlc3NlZCBpbiB0aGUgVk0gaGVhcCB0byBwcmV2ZW50XG4gICAgICAgICAgLy8gZG91YmxlLWRlbGl2ZXJ5IHdoZW4gdGhlIG91dGVyIGxvb3AgcmUtc2NhbnMgZXZlbnRzLlxuICAgICAgICAgIGlmIChldmVudC5ldmVudElkKSB7XG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYChnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXIuX19wcm9jZXNzZWRFdmVudElkcyA9IGdsb2JhbFRoaXMuX19ob29rUGF5bG9hZEJ1ZmZlci5fX3Byb2Nlc3NlZEV2ZW50SWRzIHx8IHt9KVske0pTT04uc3RyaW5naWZ5KGV2ZW50LmV2ZW50SWQpfV0gPSB0cnVlO2BcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB7XG4gICAgICAgICAgICByZXNvbHZlZCA9IHRydWU7XG4gICAgICAgICAgICBsZXQgYjogbnVtYmVyO1xuICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICBiID0gdm0uZXhlY3V0ZVBlbmRpbmdKb2JzKCk7XG4gICAgICAgICAgICB9IHdoaWxlIChiID4gMCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE5vIHJlc29sdmVyIHlldCDigJQgYnVmZmVyIHRoZSBwYXlsb2FkIGluIHRoZSBWTSBoZWFwLiBXaGVuXG4gICAgICAgICAgLy8gY3JlYXRlSG9va1Byb21pc2UoKSBpcyBjYWxsZWQgbGF0ZXIsIGl0IHdpbGwgZHJhaW4gdGhpcyBidWZmZXJcbiAgICAgICAgICAvLyBmaXJzdCAobWF0Y2hpbmcgdGhlIG5vZGU6dm0gZW5naW5lJ3MgcGF5bG9hZHNRdWV1ZSBiZWhhdmlvcikuXG4gICAgICAgICAgY29uc3QgZXZlbnRJZEpzID0gZXZlbnQuZXZlbnRJZFxuICAgICAgICAgICAgPyBKU09OLnN0cmluZ2lmeShldmVudC5ldmVudElkKVxuICAgICAgICAgICAgOiAnbnVsbCc7XG4gICAgICAgICAgY29uc3QgYnVmZmVyQW5kVHJhY2sgPVxuICAgICAgICAgICAgYChnbG9iYWxUaGlzLl9faG9va1BheWxvYWRCdWZmZXJbJHtjaWRKc31dID0gZ2xvYmFsVGhpcy5fX2hvb2tQYXlsb2FkQnVmZmVyWyR7Y2lkSnN9XSB8fCBbXSlgICtcbiAgICAgICAgICAgIGAucHVzaCglUEFZTE9BRCUpO2AgK1xuICAgICAgICAgICAgKGV2ZW50LmV2ZW50SWRcbiAgICAgICAgICAgICAgPyBgKGdsb2JhbFRoaXMuX19ob29rUGF5bG9hZEJ1ZmZlci5fX3Byb2Nlc3NlZEV2ZW50SWRzID0gZ2xvYmFsVGhpcy5fX2hvb2tQYXlsb2FkQnVmZmVyLl9fcHJvY2Vzc2VkRXZlbnRJZHMgfHwge30pWyR7ZXZlbnRJZEpzfV0gPSB0cnVlO2BcbiAgICAgICAgICAgICAgOiAnJyk7XG4gICAgICAgICAgaWYgKHJhd1BheWxvYWQgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICAgICAgICAvLyBEZWNyeXB0IGlmIGVuY3J5cHRlZCDigJQgdGhlIFZNIG9ubHkgdW5kZXJzdGFuZHMgJ2RldmwnIGZvcm1hdFxuICAgICAgICAgICAgY29uc3QgZGVjcnlwdGVkUGF5bG9hZCA9IGF3YWl0IHByZXBhcmVCeXRlc0ZvclZNKFxuICAgICAgICAgICAgICByYXdQYXlsb2FkLFxuICAgICAgICAgICAgICBlbmNyeXB0aW9uS2V5XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgY29uc3QgYnl0ZXNIYW5kbGUgPSB2bS5uZXdVaW50OEFycmF5KGRlY3J5cHRlZFBheWxvYWQpO1xuICAgICAgICAgICAgdm0uc2V0UHJvcCh2bS5nbG9iYWwsICdfX3RtcF9yZXN1bHQnLCBieXRlc0hhbmRsZSk7XG4gICAgICAgICAgICBieXRlc0hhbmRsZS5kaXNwb3NlKCk7XG4gICAgICAgICAgICAvLyBOT1RFOiByZXBsYWNlbWVudCBpcyBhIGZ1bmN0aW9uIHNvIGAkYC1zZXF1ZW5jZXMgaW4gdGhlXG4gICAgICAgICAgICAvLyBzdWJzdGl0dXRlZCBKUyBuZXZlciBnZXQgaW50ZXJwcmV0ZWQgYXMgU3RyaW5nLnJlcGxhY2VcbiAgICAgICAgICAgIC8vIHNwZWNpYWwgcmVwbGFjZW1lbnQgcGF0dGVybnMuXG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYnVmZmVyQW5kVHJhY2sucmVwbGFjZShcbiAgICAgICAgICAgICAgICAnJVBBWUxPQUQlJyxcbiAgICAgICAgICAgICAgICAoKSA9PlxuICAgICAgICAgICAgICAgICAgJ2dsb2JhbFRoaXNbU3ltYm9sLmZvcihcIndvcmtmbG93LWRlc2VyaWFsaXplXCIpXShnbG9iYWxUaGlzLl9fdG1wX3Jlc3VsdCknXG4gICAgICAgICAgICAgICkgKyAnZGVsZXRlIGdsb2JhbFRoaXMuX190bXBfcmVzdWx0OydcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBzZXJpYWxpemVkID1cbiAgICAgICAgICAgICAgcmF3UGF5bG9hZCAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgPyBKU09OLnN0cmluZ2lmeShyYXdQYXlsb2FkKVxuICAgICAgICAgICAgICAgIDogJ3VuZGVmaW5lZCc7XG4gICAgICAgICAgICAvLyBGdW5jdGlvbiByZXBsYWNlbWVudDogYSBKU09OLXNlcmlhbGl6ZWQgcGF5bG9hZCBjYW4gY29udGFpblxuICAgICAgICAgICAgLy8gYCQmYCwgYCQnYCwgYCRcXGBgLCAuLi4gd2hpY2ggU3RyaW5nLnJlcGxhY2Ugd291bGQgb3RoZXJ3aXNlXG4gICAgICAgICAgICAvLyBleHBhbmQsIHNpbGVudGx5IGNvcnJ1cHRpbmcgdGhlIGluamVjdGVkIGNvZGUuXG4gICAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgICAgYnVmZmVyQW5kVHJhY2sucmVwbGFjZSgnJVBBWUxPQUQlJywgKCkgPT4gc2VyaWFsaXplZClcbiAgICAgICAgICAgICkuZGlzcG9zZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBtYXJrQ3JlYXRlZCh2bSwgY2lkSnMpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ2hvb2tfY29uZmxpY3QnOiB7XG4gICAgICAgIC8vIEFub3RoZXIgd29ya2Zsb3cgb3ducyB0aGlzIGhvb2sgdG9rZW4uIFBheWxvYWQgYXdhaXRlcnMgcmVqZWN0XG4gICAgICAgIC8vIHdpdGggSG9va0NvbmZsaWN0RXJyb3I7IGdldENvbmZsaWN0KCkgYXdhaXRlcnMgcmVzb2x2ZSB3aXRoIGFcbiAgICAgICAgLy8gUnVuIGhhbmRsZSBmb3IgdGhlIGNvbmZsaWN0aW5nIHJ1biAocmV2aXZlZCB0aHJvdWdoIHRoZSBWTSdzXG4gICAgICAgIC8vIGNsYXNzIHJlZ2lzdHJ5IHNvIGl0cyBtZXRob2RzIGFyZSBkdXJhYmxlIHN0ZXAgcHJveGllcykgb3JcbiAgICAgICAgLy8gcmVqZWN0IHdpdGggdGhlIGVycm9yIHdoZW4gbm8gaGFuZGxlIGNhbiBiZSBjb25zdHJ1Y3RlZCDigJRcbiAgICAgICAgLy8gbWlycm9yaW5nIHRoZSBub2RlOnZtIGVuZ2luZSdzIGhvb2sudHMgaG9va19jb25mbGljdCBoYW5kbGluZy5cbiAgICAgICAgY29uc3QgY29uZmxpY3RUb2tlbiA9IChldmVudERhdGE/LnRva2VuIGFzIHN0cmluZykgPz8gJ3Vua25vd24nO1xuICAgICAgICBjb25zdCBjb25mbGljdGluZ1J1bklkID0gZXZlbnREYXRhPy5jb25mbGljdGluZ1J1bklkIGFzXG4gICAgICAgICAgfCBzdHJpbmdcbiAgICAgICAgICB8IHVuZGVmaW5lZDtcbiAgICAgICAgY29uc3QgZGlkU2V0dGxlID0gdm0uZHVtcChcbiAgICAgICAgICB2bS5ldmFsQ29kZShcbiAgICAgICAgICAgIGAoZnVuY3Rpb24oKXtcbiAgICAgICAgICAgICAgdmFyIGNpZCA9ICR7SlNPTi5zdHJpbmdpZnkoY2lkKX07XG4gICAgICAgICAgICAgIHZhciB0b2tlbiA9ICR7SlNPTi5zdHJpbmdpZnkoY29uZmxpY3RUb2tlbil9O1xuICAgICAgICAgICAgICB2YXIgY29uZmxpY3RpbmdSdW5JZCA9ICR7SlNPTi5zdHJpbmdpZnkoY29uZmxpY3RpbmdSdW5JZCA/PyBudWxsKX07XG4gICAgICAgICAgICAgIHZhciBFcnJDbHMgPSBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoJ0B3b3JrZmxvdy9lcnJvcnMvL0hvb2tDb25mbGljdEVycm9yJyldO1xuICAgICAgICAgICAgICB2YXIgZXJyO1xuICAgICAgICAgICAgICBpZiAodHlwZW9mIEVyckNscyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgIGVyciA9IG5ldyBFcnJDbHModG9rZW4sIGNvbmZsaWN0aW5nUnVuSWQgfHwgdW5kZWZpbmVkKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBlcnIgPSBuZXcgRXJyb3IoJ0hvb2sgdG9rZW4gXCInICsgdG9rZW4gKyAnXCIgaXMgYWxyZWFkeSBpbiB1c2UgYnkgYW5vdGhlciB3b3JrZmxvdycpO1xuICAgICAgICAgICAgICAgIGVyci5uYW1lID0gJ0hvb2tDb25mbGljdEVycm9yJztcbiAgICAgICAgICAgICAgICBlcnIudG9rZW4gPSB0b2tlbjtcbiAgICAgICAgICAgICAgICBpZiAoY29uZmxpY3RpbmdSdW5JZCkgZXJyLmNvbmZsaWN0aW5nUnVuSWQgPSBjb25mbGljdGluZ1J1bklkO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHZhciBydW4gPSBudWxsO1xuICAgICAgICAgICAgICBpZiAoY29uZmxpY3RpbmdSdW5JZCkge1xuICAgICAgICAgICAgICAgIHZhciByZWcgPSBnbG9iYWxUaGlzW1N5bWJvbC5mb3IoJ3dvcmtmbG93LWNsYXNzLXJlZ2lzdHJ5JyldO1xuICAgICAgICAgICAgICAgIHZhciBSdW5DbHMgPSByZWcgJiYgcmVnLmdldCgnY2xhc3MvL3dvcmtmbG93Ly9SdW4nKTtcbiAgICAgICAgICAgICAgICB2YXIgZGVzID0gUnVuQ2xzICYmIFJ1bkNsc1tTeW1ib2wuZm9yKCd3b3JrZmxvdy1kZXNlcmlhbGl6ZScpXTtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGRlcyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgICAgICAgcnVuID0gZGVzLmNhbGwoUnVuQ2xzLCB7IHJ1bklkOiBjb25mbGljdGluZ1J1bklkIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB2YXIgc2V0dGxlZCA9IGZhbHNlO1xuICAgICAgICAgICAgICB2YXIgc3RhdGUgPSBnbG9iYWxUaGlzLl9faG9va3MgJiYgZ2xvYmFsVGhpcy5fX2hvb2tzW2NpZF07XG4gICAgICAgICAgICAgIGlmIChzdGF0ZSAmJiAhc3RhdGUuY29uZmxpY3QpIHtcbiAgICAgICAgICAgICAgICBzdGF0ZS5jb25mbGljdCA9IHsgZXJyb3I6IGVyciwgcnVuOiBydW4gfTtcbiAgICAgICAgICAgICAgICB2YXIgZ2MgPSBzdGF0ZS5nZXRDb25mbGljdFJlc29sdmVycztcbiAgICAgICAgICAgICAgICBzdGF0ZS5nZXRDb25mbGljdFJlc29sdmVycyA9IFtdO1xuICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZ2MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgIGlmIChydW4pIHsgZ2NbaV0ucmVzb2x2ZShydW4pOyB9IGVsc2UgeyBnY1tpXS5yZWplY3QoZXJyKTsgfVxuICAgICAgICAgICAgICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzW2NpZF0pIHtcbiAgICAgICAgICAgICAgICBnbG9iYWxUaGlzLl9fcmVzb2x2ZXJzW2NpZF0ucmVqZWN0KGVycik7XG4gICAgICAgICAgICAgICAgZGVsZXRlIGdsb2JhbFRoaXMuX19yZXNvbHZlcnNbY2lkXTtcbiAgICAgICAgICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICByZXR1cm4gc2V0dGxlZDtcbiAgICAgICAgICAgIH0pKClgXG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgICBpZiAoZGlkU2V0dGxlKSB7XG4gICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgIGxldCBiOiBudW1iZXI7XG4gICAgICAgICAgZG8ge1xuICAgICAgICAgICAgYiA9IHZtLmV4ZWN1dGVQZW5kaW5nSm9icygpO1xuICAgICAgICAgIH0gd2hpbGUgKGIgPiAwKTtcbiAgICAgICAgfVxuICAgICAgICBtYXJrQ3JlYXRlZCh2bSwgY2lkSnMpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ3N0ZXBfY3JlYXRlZCc6XG4gICAgICBjYXNlICdzdGVwX3N0YXJ0ZWQnOlxuICAgICAgY2FzZSAnc3RlcF9yZXRyeWluZyc6XG4gICAgICBjYXNlICd3YWl0X2NyZWF0ZWQnOiB7XG4gICAgICAgIG1hcmtDcmVhdGVkKHZtLCBjaWRKcyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgY2FzZSAnaG9va19jcmVhdGVkJzoge1xuICAgICAgICAvLyBDb25maXJtIGNyZWF0aW9uIGZvciBnZXRDb25mbGljdCgpIGF3YWl0ZXJzOiByZXNvbHZlIHRoZW0gd2l0aFxuICAgICAgICAvLyBudWxsIChubyBjb25mbGljdCkgb25jZSB0aGUgZXZlbnQgbG9nIHByb3ZlcyB0aGUgaG9vayBleGlzdHMuXG4gICAgICAgIGNvbnN0IHNldHRsZWRHZXRDb25mbGljdCA9IHZtLmR1bXAoXG4gICAgICAgICAgdm0uZXZhbENvZGUoXG4gICAgICAgICAgICBgKGZ1bmN0aW9uKCl7XG4gICAgICAgICAgICAgIHZhciBzdGF0ZSA9IGdsb2JhbFRoaXMuX19ob29rcyAmJiBnbG9iYWxUaGlzLl9faG9va3NbJHtKU09OLnN0cmluZ2lmeShjaWQpfV07XG4gICAgICAgICAgICAgIGlmICghc3RhdGUpIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgc3RhdGUuY3JlYXRlZCA9IHRydWU7XG4gICAgICAgICAgICAgIHZhciBnYyA9IHN0YXRlLmdldENvbmZsaWN0UmVzb2x2ZXJzO1xuICAgICAgICAgICAgICBzdGF0ZS5nZXRDb25mbGljdFJlc29sdmVycyA9IFtdO1xuICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGdjLmxlbmd0aDsgaSsrKSBnY1tpXS5yZXNvbHZlKG51bGwpO1xuICAgICAgICAgICAgICByZXR1cm4gZ2MubGVuZ3RoID4gMDtcbiAgICAgICAgICAgIH0pKClgXG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgICBpZiAoc2V0dGxlZEdldENvbmZsaWN0KSB7XG4gICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgIGxldCBiOiBudW1iZXI7XG4gICAgICAgICAgZG8ge1xuICAgICAgICAgICAgYiA9IHZtLmV4ZWN1dGVQZW5kaW5nSm9icygpO1xuICAgICAgICAgIH0gd2hpbGUgKGIgPiAwKTtcbiAgICAgICAgfVxuICAgICAgICBtYXJrQ3JlYXRlZCh2bSwgY2lkSnMpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ2hvb2tfZGlzcG9zZWQnOiB7XG4gICAgICAgIC8vIERpc2FtYmlndWF0ZSBmcm9tIHRoZSBgaG9va2AgcGVuZGluZyBvcCB3aXRoIHRoZSBzYW1lXG4gICAgICAgIC8vIGNvcnJlbGF0aW9uSWQg4oCUIHdlIHdhbnQgdG8gbWFyayB0aGUgYGhvb2tfZGlzcG9zZWAgZW50cnkuXG4gICAgICAgIG1hcmtDcmVhdGVkKHZtLCBjaWRKcywgJ2hvb2tfZGlzcG9zZScpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIHJlc29sdmVkO1xufVxuXG5mdW5jdGlvbiBtYXJrQ3JlYXRlZCh2bTogUXVpY2tKUywgY2lkSnM6IHN0cmluZywgb3BUeXBlPzogc3RyaW5nKTogdm9pZCB7XG4gIC8vIGBjaWRKc2AgaXMgdGhlIEpTT04uc3RyaW5naWZ5LXF1b3RlZCBjb3JyZWxhdGlvbiBpZCAoc2VlIHByb2Nlc3NFdmVudHMpLlxuICAvLyBgaG9va2AgYW5kIGBob29rX2Rpc3Bvc2VgIHBlbmRpbmcgb3BzIHNoYXJlIHRoZSBzYW1lIGNvcnJlbGF0aW9uSWQsXG4gIC8vIHNvIHdoZW4gcHJvY2Vzc2luZyBgaG9va19kaXNwb3NlZGAgZXZlbnRzIHdlIG11c3QgZGlzYW1iaWd1YXRlIGJ5XG4gIC8vIHR5cGUg4oCUIG90aGVyd2lzZSBgLmZpbmQoKWAgcmV0dXJucyB0aGUgb3JpZ2luYWwgYGhvb2tgIG9wIGFuZCB0aGVcbiAgLy8gYGhvb2tfZGlzcG9zZWAgb3AgaXMgbmV2ZXIgbWFya2VkLCBjYXVzaW5nIHRoZSBlbnRyeXBvaW50IHRvIGtlZXBcbiAgLy8gcmV0cnlpbmcgYSBob29rX2Rpc3Bvc2VkIGZvciBhbiBhbHJlYWR5LWRlbGV0ZWQgZW50aXR5LlxuICBjb25zdCBwcmVkaWNhdGUgPSBvcFR5cGVcbiAgICA/IGBmdW5jdGlvbihwKXtyZXR1cm4gcC5jb3JyZWxhdGlvbklkPT09JHtjaWRKc30mJnAudHlwZT09PSR7SlNPTi5zdHJpbmdpZnkob3BUeXBlKX07fWBcbiAgICA6IGBmdW5jdGlvbihwKXtyZXR1cm4gcC5jb3JyZWxhdGlvbklkPT09JHtjaWRKc307fWA7XG4gIHZtLmV2YWxDb2RlKFxuICAgIGB2YXIgX19wPWdsb2JhbFRoaXMuX19wZW5kaW5nLmZpbmQoJHtwcmVkaWNhdGV9KTtgICtcbiAgICAgIGBpZihfX3ApX19wLmhhc0NyZWF0ZWRFdmVudD10cnVlO2BcbiAgKS5kaXNwb3NlKCk7XG59XG5cbi8vIC0tLS0gU3RhdGUgQ2hlY2tpbmcgLS0tLVxuXG4vKipcbiAqIENvbGxlY3QgbGVmdG92ZXIgcGVuZGluZyBvcGVyYXRpb25zIHRoYXQgbmVlZCBkdXJhYmxlIHNpZGUgZWZmZWN0cyB3aGVuXG4gKiB0aGUgd29ya2Zsb3cgcmVhY2hlcyBhIHRlcm1pbmFsIHN0YXRlLiBNaXJyb3JzIHRoZSBub2RlOnZtIGVuZ2luZSdzXG4gKiBkcmFpblBlbmRpbmdRdWV1ZUl0ZW1zICh3b3JrZmxvdy50cyk6IHN0aWxsLWFsaXZlIHN5c3RlbSBob29rc1xuICogKEFib3J0Q29udHJvbGxlcikgd2l0aG91dCBhbiBhYm9ydCBpbiBmbGlnaHQgYXJlIGltcGxpY2l0bHkgZGlzcG9zZWQgc29cbiAqIHRoZXkgZG9uJ3QgbGVhayBob29rIHJvd3M7IG9wcyB3aXRob3V0IGNyZWF0ZWQgZXZlbnRzIChmaXJlLWFuZC1mb3JnZXRcbiAqIGF0dHJpYnV0ZXMvaG9va3Mvc3RlcHMvd2FpdHMpIGFuZCBwZW5kaW5nIGFib3J0IHJlY29yZGluZ3MgYXJlIHN1cmZhY2VkXG4gKiBmb3IgdGhlIGVudHJ5cG9pbnQgdG8gZmx1c2guXG4gKi9cbmZ1bmN0aW9uIGNvbGxlY3REcmFpbk9wZXJhdGlvbnModm06IFF1aWNrSlMpOiBQZW5kaW5nT3BlcmF0aW9uW10ge1xuICB1c2luZyBoID0gdm0uZXZhbENvZGUoYChmdW5jdGlvbigpe1xuICAgIHZhciB0b0Rpc3Bvc2UgPSBbXTtcbiAgICBnbG9iYWxUaGlzLl9fcGVuZGluZy5mb3JFYWNoKGZ1bmN0aW9uKHApe1xuICAgICAgaWYgKHAudHlwZSA9PT0gXCJob29rXCIgJiYgcC5pc1N5c3RlbSAmJiAhcC5hYm9ydFJlcXVlc3RlZCAmJiAhcC5kaXNwb3NlZCkge1xuICAgICAgICBwLmRpc3Bvc2VkID0gdHJ1ZTtcbiAgICAgICAgLy8gT25seSBkaXNwb3NlIGhvb2tzIHRoYXQgd2VyZSBkdXJhYmx5IGNyZWF0ZWQ7IGEgaG9vayB0aGF0IG5ldmVyXG4gICAgICAgIC8vIHJlYWNoZWQgc3RvcmFnZSBoYXMgbm90aGluZyB0byBjbGVhbiB1cC5cbiAgICAgICAgaWYgKHAuaGFzQ3JlYXRlZEV2ZW50KSB7XG4gICAgICAgICAgdG9EaXNwb3NlLnB1c2goe1xuICAgICAgICAgICAgdHlwZTogXCJob29rX2Rpc3Bvc2VcIixcbiAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IHAuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIGhhc0NyZWF0ZWRFdmVudDogZmFsc2UsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgICB0b0Rpc3Bvc2UuZm9yRWFjaChmdW5jdGlvbihkKXsgZ2xvYmFsVGhpcy5fX3BlbmRpbmcucHVzaChkKTsgfSk7XG4gICAgcmV0dXJuIGdsb2JhbFRoaXMuX19wZW5kaW5nLmZpbHRlcihmdW5jdGlvbihwKXtcbiAgICAgIGlmIChwLmFib3J0UmVxdWVzdGVkKSByZXR1cm4gdHJ1ZTtcbiAgICAgIGlmIChwLmhhc0NyZWF0ZWRFdmVudCkgcmV0dXJuIGZhbHNlO1xuICAgICAgLy8gU2tpcCBzeXN0ZW0gaG9va3MgdGhhdCB3ZXJlIGRpc3Bvc2VkIGJlZm9yZSBldmVyIGJlaW5nIGNyZWF0ZWQuXG4gICAgICBpZiAocC50eXBlID09PSBcImhvb2tcIiAmJiBwLmRpc3Bvc2VkKSByZXR1cm4gZmFsc2U7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfSkoKWApO1xuICByZXR1cm4gdm0uZHVtcChoKSBhcyBQZW5kaW5nT3BlcmF0aW9uW107XG59XG5cbmZ1bmN0aW9uIGNoZWNrV29ya2Zsb3dTdGF0ZShcbiAgdm06IFF1aWNrSlMsXG4gIG9wdHM6IHsga2VlcEFsaXZlT25TdXNwZW5kPzogYm9vbGVhbiB9ID0ge31cbik6IFF1aWNrSlNSdW50aW1lUmVzdWx0IHtcbiAgLy8gQ2hlY2sgY29tcGxldGVkIOKAlCBfX3dvcmtmbG93UmVzdWx0IGlzIGEgZm9ybWF0LXByZWZpeGVkIFVpbnQ4QXJyYXlcbiAge1xuICAgIHVzaW5nIGggPSB2bS5ldmFsQ29kZSgnZ2xvYmFsVGhpcy5fX3dvcmtmbG93UmVzdWx0Jyk7XG4gICAgaWYgKCFoLmlzVW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCByZXN1bHRCeXRlcyA9IGgudG9VaW50OEFycmF5KCk7XG4gICAgICBjb25zdCBkcmFpbk9wZXJhdGlvbnMgPSBjb2xsZWN0RHJhaW5PcGVyYXRpb25zKHZtKTtcbiAgICAgIHZtLmRpc3Bvc2UoKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbXBsZXRlZDoge1xuICAgICAgICAgIHJlc3VsdDogcmVzdWx0Qnl0ZXMsXG4gICAgICAgICAgLi4uKGRyYWluT3BlcmF0aW9ucy5sZW5ndGggPiAwID8geyBkcmFpbk9wZXJhdGlvbnMgfSA6IHt9KSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfVxuICB9XG5cbiAgLy8gQ2hlY2sgZmFpbGVkXG4gIHtcbiAgICB1c2luZyBoID0gdm0uZXZhbENvZGUoJ2dsb2JhbFRoaXMuX193b3JrZmxvd0Vycm9yJyk7XG4gICAgaWYgKCFoLmlzVW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCBlcnJvck9iaiA9IHZtLmR1bXAoaCkgYXNcbiAgICAgICAgfCB7XG4gICAgICAgICAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgICAgICAgICBzdGFjaz86IHN0cmluZztcbiAgICAgICAgICAgIG5hbWU/OiBzdHJpbmc7XG4gICAgICAgICAgICB2YWx1ZUJ5dGVzPzogVWludDhBcnJheTtcbiAgICAgICAgICB9XG4gICAgICAgIHwgc3RyaW5nO1xuICAgICAgY29uc3QgZmFpbGVkID1cbiAgICAgICAgdHlwZW9mIGVycm9yT2JqID09PSAnc3RyaW5nJ1xuICAgICAgICAgID8geyBtZXNzYWdlOiBlcnJvck9iaiB9XG4gICAgICAgICAgOiB7XG4gICAgICAgICAgICAgIG1lc3NhZ2U6IGVycm9yT2JqLm1lc3NhZ2UsXG4gICAgICAgICAgICAgIHN0YWNrOiBlcnJvck9iai5zdGFjayB8fCB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIG5hbWU6IGVycm9yT2JqLm5hbWUgfHwgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICB2YWx1ZUJ5dGVzOiBlcnJvck9iai52YWx1ZUJ5dGVzLFxuICAgICAgICAgICAgfTtcbiAgICAgIHJ1bnRpbWVMb2dnZXIuZXJyb3IoJ1F1aWNrSlMgcnVudGltZTogd29ya2Zsb3cgZmFpbGVkIGluIFZNJywge1xuICAgICAgICBlcnJvck1lc3NhZ2U6IGZhaWxlZC5tZXNzYWdlLFxuICAgICAgICBlcnJvck5hbWU6IGZhaWxlZC5uYW1lLFxuICAgICAgICBlcnJvclN0YWNrOiBmYWlsZWQuc3RhY2ssXG4gICAgICB9KTtcbiAgICAgIGNvbnN0IGRyYWluT3BlcmF0aW9ucyA9IGNvbGxlY3REcmFpbk9wZXJhdGlvbnModm0pO1xuICAgICAgdm0uZGlzcG9zZSgpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZmFpbGVkOiB7XG4gICAgICAgICAgLi4uZmFpbGVkLFxuICAgICAgICAgIC4uLihkcmFpbk9wZXJhdGlvbnMubGVuZ3RoID4gMCA/IHsgZHJhaW5PcGVyYXRpb25zIH0gOiB7fSksXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIC8vIENoZWNrIHN1c3BlbmRlZCDigJQgdGhlIHdvcmtmbG93IGlzIHN1c3BlbmRlZCBpZiB0aGVyZSBhcmUgYWN0aXZlIHJlc29sdmVyc1xuICAvLyBPUiBwZW5kaW5nIG9wZXJhdGlvbnMgdGhhdCBoYXZlbid0IGJlZW4gY3JlYXRlZCB5ZXQgKGUuZy4gaG9va3MgY3JlYXRlZFxuICAvLyB1cGZyb250IGJ1dCBub3QgeWV0IGF3YWl0ZWQpXG4gIHtcbiAgICB1c2luZyBoID0gdm0uZXZhbENvZGUoXG4gICAgICAnT2JqZWN0LmtleXMoZ2xvYmFsVGhpcy5fX3Jlc29sdmVycykubGVuZ3RoID4gMCB8fCBnbG9iYWxUaGlzLl9fcGVuZGluZy5zb21lKGZ1bmN0aW9uKHApe3JldHVybiFwLmhhc0NyZWF0ZWRFdmVudDt9KSdcbiAgICApO1xuICAgIGlmICh2bS5kdW1wKGgpKSB7XG4gICAgICB1c2luZyBwZW5kaW5nSCA9IHZtLmV2YWxDb2RlKFxuICAgICAgICAvLyBPcHMgd2l0aCBhbiBhY3RpdmUgcmVzb2x2ZXIgb3Igd2l0aG91dCBhIGNyZWF0ZWQgZXZlbnQgYXJlXG4gICAgICAgIC8vIHBlbmRpbmc7IGFib3J0LXJlcXVlc3RlZCBob29rcyBhcmUgYWxzbyBzdXJmYWNlZCAoZXZlbiB3aGVuXG4gICAgICAgIC8vIGFscmVhZHkgY3JlYXRlZCBhbmQgdW5hd2FpdGVkKSBzbyB0aGUgaG9zdCByZWNvcmRzIHRoZSBhYm9ydC5cbiAgICAgICAgYGdsb2JhbFRoaXMuX19wZW5kaW5nLmZpbHRlcihmdW5jdGlvbihwKXtyZXR1cm4hIWdsb2JhbFRoaXMuX19yZXNvbHZlcnNbcC5jb3JyZWxhdGlvbklkXSB8fCAhcC5oYXNDcmVhdGVkRXZlbnQgfHwgcC5hYm9ydFJlcXVlc3RlZDt9KWBcbiAgICAgICk7XG4gICAgICBjb25zdCBwZW5kaW5nT3BzID0gdm0uZHVtcChwZW5kaW5nSCkgYXMgUGVuZGluZ09wZXJhdGlvbltdO1xuICAgICAgaWYgKCFvcHRzLmtlZXBBbGl2ZU9uU3VzcGVuZCkgdm0uZGlzcG9zZSgpO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdXNwZW5kZWQ6IHtcbiAgICAgICAgICBwZW5kaW5nT3BlcmF0aW9uczogcGVuZGluZ09wcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfVxuICB9XG5cbiAgdm0uZGlzcG9zZSgpO1xuICByZXR1cm4geyBmYWlsZWQ6IHsgbWVzc2FnZTogJ1dvcmtmbG93IGVuZGVkIGluIHVua25vd24gc3RhdGUnIH0gfTtcbn1cblxuLy8gLS0tLSBIZWxwZXJzIC0tLS1cblxuZnVuY3Rpb24gZXh0cmFjdEVycm9yKFxuICB2bTogUXVpY2tKUyxcbiAgZXJyOiB1bmtub3duLFxuICBmYWxsYmFja01lc3NhZ2U6IHN0cmluZ1xuKTogUXVpY2tKU1J1bnRpbWVSZXN1bHQge1xuICBsZXQgbWVzc2FnZSA9IGZhbGxiYWNrTWVzc2FnZTtcbiAgbGV0IHN0YWNrOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGxldCBuYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgaWYgKGVyciBpbnN0YW5jZW9mIEpTRXhjZXB0aW9uKSB7XG4gICAgY29uc3QgZXJyb3IgPSB2bS5kdW1wKGVyci5oYW5kbGUpIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbDtcbiAgICBlcnIuaGFuZGxlLmRpc3Bvc2UoKTtcbiAgICBtZXNzYWdlID0gKGVycm9yPy5tZXNzYWdlIGFzIHN0cmluZykgPz8gZXJyLm1lc3NhZ2UgPz8gZmFsbGJhY2tNZXNzYWdlO1xuICAgIHN0YWNrID0gKGVycm9yPy5zdGFjayBhcyBzdHJpbmcpID8/IGVyci5zdGFjaztcbiAgICBuYW1lID0gKGVycm9yPy5uYW1lIGFzIHN0cmluZykgPz8gZXJyLm5hbWU7XG4gIH0gZWxzZSBpZiAoZXJyIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICBtZXNzYWdlID0gZXJyLm1lc3NhZ2UgPz8gZmFsbGJhY2tNZXNzYWdlO1xuICAgIHN0YWNrID0gZXJyLnN0YWNrO1xuICAgIG5hbWUgPSBlcnIubmFtZTtcbiAgfVxuXG4gIHZtLmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHtcbiAgICBmYWlsZWQ6IHsgbWVzc2FnZSwgc3RhY2ssIG5hbWUgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBNdXRhYmxlIGludGVycnVwdCBidWRnZXQgZm9yIGEgVk0uIFF1aWNrSlMgcG9sbHMgdGhlIGludGVycnVwdCBoYW5kbGVyXG4gKiBkdXJpbmcgSlMgZXhlY3V0aW9uOyB3aGVuIGl0IHJldHVybnMgdHJ1ZSwgZXhlY3V0aW9uIGFib3J0cy4gVGhlIGJ1ZGdldFxuICogYm91bmRzIGEgc2luZ2xlIGhvc3QtPlZNIGV4ZWN1dGlvbiBidXJzdCAoYnVuZGxlIGV2YWwgKyBldmVudFxuICogcHJvY2Vzc2luZyksIG5vdCB0b3RhbCBWTSBsaWZldGltZSDigJQgdGhlIGlubGluZS1zdGVwIGxvb3Aga2VlcHMgYSBWTVxuICogYWxpdmUgYWNyb3NzIHN0ZXAgZXhlY3V0aW9ucyB0aGF0IGNhbiBsZWdpdGltYXRlbHkgdGFrZSBtaW51dGVzLCBzbyB0aGVcbiAqIGhvc3QgcmVzZXRzIHRoZSBidWRnZXQgYmVmb3JlIGVhY2ggcmUtZW50cnkgKHNlZSByZXNldEJ1ZGdldCBjYWxscykuXG4gKlxuICogVGhlIHBlci1idXJzdCBjZWlsaW5nIGlzIHRoZSBzYW1lIGNvbmZpZ3VyYWJsZSBidWRnZXQgYXMgdGhlIG5vZGVcbiAqIGVuZ2luZSdzIFJlcGxheUJ1ZGdldCAoUkVQTEFZX1RJTUVPVVRfTVMsIGRlZmF1bHQgMjQwcyk6IGEgd29ya2Zsb3dcbiAqIHdob3NlIHJlcGxheSB0aGUgbm9kZSBlbmdpbmUgaGFuZGxlcyBmaW5lIG11c3Qgbm90IGJlIGludGVycnVwdGVkIGhlcmVcbiAqIGJ5IGEgbG93ZXIgaGFyZGNvZGVkIGNlaWxpbmcuIFRoZSBpbnRlcnJ1cHQgZXJyb3IgZXNjYXBlc1xuICogcnVuUXVpY2tKU1dvcmtmbG93IGFuZCByZWFjaGVzIHRoZSByZXBsYXkgbG9vcCdzIGNhdGNoIGluIHJ1bnRpbWUudHMsXG4gKiB3aGljaCByZWNvcmRzIHJ1bl9mYWlsZWQuXG4gKi9cbmludGVyZmFjZSBJbnRlcnJ1cHRCdWRnZXQge1xuICBzdGFydDogbnVtYmVyO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVJbnRlcnJ1cHRIYW5kbGVyKGJ1ZGdldDogSW50ZXJydXB0QnVkZ2V0KTogKCkgPT4gYm9vbGVhbiB7XG4gIGNvbnN0IHRpbWVvdXQgPSBnZXRSZXBsYXlUaW1lb3V0TXMoKTtcbiAgcmV0dXJuICgpID0+IERhdGUubm93KCkgLSBidWRnZXQuc3RhcnQgPiB0aW1lb3V0O1xufVxuIl19
|