@workflow/world-local 5.0.0-beta.2 → 5.0.0-beta.20
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/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/fs.d.ts +64 -5
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +148 -19
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -9
- package/dist/init.js +1 -1
- package/dist/instrumentObject.d.ts.map +1 -1
- package/dist/instrumentObject.js +69 -3
- package/dist/queue.d.ts +2 -2
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +64 -12
- package/dist/storage/events-storage.d.ts.map +1 -1
- package/dist/storage/events-storage.js +1393 -601
- package/dist/storage/filters.js +1 -1
- package/dist/storage/helpers.d.ts +13 -0
- package/dist/storage/helpers.d.ts.map +1 -1
- package/dist/storage/helpers.js +23 -1
- package/dist/storage/hooks-storage.d.ts.map +1 -1
- package/dist/storage/hooks-storage.js +11 -4
- package/dist/storage/index.d.ts +10 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +9 -11
- package/dist/storage/legacy.d.ts.map +1 -1
- package/dist/storage/legacy.js +10 -5
- package/dist/storage/runs-storage.d.ts +29 -3
- package/dist/storage/runs-storage.d.ts.map +1 -1
- package/dist/storage/runs-storage.js +76 -4
- package/dist/storage/steps-storage.d.ts.map +1 -1
- package/dist/storage/steps-storage.js +5 -2
- package/dist/storage.js +1 -1
- package/dist/streamer.d.ts.map +1 -1
- package/dist/streamer.js +45 -32
- package/dist/telemetry.d.ts +8 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +5 -1
- package/dist/test-helpers.d.ts +1 -0
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +1 -1
- package/dist/util.js +1 -1
- package/package.json +6 -6
- package/dist/config.js.map +0 -1
- package/dist/fs.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/init.js.map +0 -1
- package/dist/instrumentObject.js.map +0 -1
- package/dist/queue.js.map +0 -1
- package/dist/storage/events-storage.js.map +0 -1
- package/dist/storage/filters.js.map +0 -1
- package/dist/storage/helpers.js.map +0 -1
- package/dist/storage/hooks-storage.js.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/legacy.js.map +0 -1
- package/dist/storage/runs-storage.js.map +0 -1
- package/dist/storage/steps-storage.js.map +0 -1
- package/dist/storage.js.map +0 -1
- package/dist/streamer.js.map +0 -1
- package/dist/telemetry.js.map +0 -1
- package/dist/test-helpers.js.map +0 -1
- package/dist/util.js.map +0 -1
|
@@ -1,13 +1,238 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { EntityConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, TooEarlyError, WorkflowWorldError, } from '@workflow/errors';
|
|
4
|
-
import { EventSchema, HookSchema, isLegacySpecVersion, requiresNewerWorld, SPEC_VERSION_CURRENT, StepSchema, validateUlidTimestamp, WaitSchema, WorkflowRunSchema, } from '@workflow/world';
|
|
3
|
+
import { EntityConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, TooEarlyError, WorkflowRunNotFoundError, WorkflowWorldError, } from '@workflow/errors';
|
|
4
|
+
import { applyAttributeChanges, EventSchema, HookSchema, isLegacySpecVersion, requiresNewerWorld, SPEC_VERSION_CURRENT, StepSchema, ulidToDate, validateAttributeChanges, validateUlidTimestamp, WaitSchema, WorkflowRunSchema, } from '@workflow/world';
|
|
5
|
+
import { z } from 'zod';
|
|
5
6
|
import { DEFAULT_RESOLVE_DATA_OPTION } from '../config.js';
|
|
6
|
-
import { deleteJSON, jsonReplacer, listJSONFiles, paginatedFileSystemQuery, readJSONWithFallback, taggedPath, writeExclusive, writeJSON, } from '../fs.js';
|
|
7
|
+
import { assertSafeEntityId, deleteJSON, jsonReplacer, listJSONFiles, paginatedFileSystemQuery, readJSON, readJSONWithFallback, resolveWithinBase, taggedPath, writeExclusive, writeJSON, } from '../fs.js';
|
|
7
8
|
import { stripEventDataRefs } from './filters.js';
|
|
8
|
-
import { getObjectCreatedAt, hashToken, monotonicUlid } from './helpers.js';
|
|
9
|
+
import { getObjectCreatedAt, hashToken, hookRecoveryMarkerPath, monotonicUlid, } from './helpers.js';
|
|
9
10
|
import { deleteAllHooksForRun } from './hooks-storage.js';
|
|
10
11
|
import { handleLegacyEvent } from './legacy.js';
|
|
12
|
+
import { withRunFileLock } from './runs-storage.js';
|
|
13
|
+
/**
|
|
14
|
+
* Per-step in-process async mutex. Serializes concurrent `events.create` calls
|
|
15
|
+
* that target the same step, so that the "check terminal state, then write step
|
|
16
|
+
* entity + event" sequence is atomic. Without this, two concurrent step_started
|
|
17
|
+
* calls can both pass the not-terminal check and both write step_started events
|
|
18
|
+
* — or a step_started can land in the log after step_completed has already
|
|
19
|
+
* written, producing unconsumed events on replay.
|
|
20
|
+
*
|
|
21
|
+
* Duplicate step_started events for a non-terminal step are still allowed
|
|
22
|
+
* (retries legitimately re-start a step), only writes to an already-terminal
|
|
23
|
+
* step are rejected.
|
|
24
|
+
*/
|
|
25
|
+
// `stepLocks` and `hookLocks` are now instantiated per
|
|
26
|
+
// `createEventsStorage` call (see inside the function) rather than
|
|
27
|
+
// being module-level. The on-disk constraint / claim files remain
|
|
28
|
+
// the durable source of truth across processes; the in-process
|
|
29
|
+
// mutex is a per-instance optimization that closes a short race
|
|
30
|
+
// window in the dedup-recovery path. Per-instance scoping lets
|
|
31
|
+
// tests simulate cross-process behavior with two storage instances
|
|
32
|
+
// sharing one data directory (each instance has independent locks
|
|
33
|
+
// but a shared filesystem), exactly matching the cross-process
|
|
34
|
+
// semantics without spawning subprocesses.
|
|
35
|
+
const HookTokenClaimSchema = z.object({
|
|
36
|
+
// The token-claim writer below has always persisted `hookId`, but
|
|
37
|
+
// this read schema previously omitted it, which is the bug fixed
|
|
38
|
+
// by https://github.com/vercel/workflow/issues/2283. `optional()`
|
|
39
|
+
// is defensive: any claim file that somehow lacks the field still
|
|
40
|
+
// parses (yielding `undefined`) and falls through to the cross-
|
|
41
|
+
// hook conflict branch, matching pre-fix behavior.
|
|
42
|
+
hookId: z.string().optional(),
|
|
43
|
+
runId: z.string(),
|
|
44
|
+
// `eventId` is the canonical hook_created event ID the claiming
|
|
45
|
+
// worker committed to publishing. Persisting it here turns the
|
|
46
|
+
// claim file into a durable convergence key for cross-worker /
|
|
47
|
+
// cross-process retries (see comment on the hook_created branch).
|
|
48
|
+
// `optional()` for backward compatibility: a legacy claim file
|
|
49
|
+
// written before this field existed falls through to the recovery-
|
|
50
|
+
// marker upgrade path, which atomically pins a canonical eventId
|
|
51
|
+
// via a sidecar marker (also a `writeExclusive`).
|
|
52
|
+
eventId: z.string().optional(),
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Sidecar recovery marker that pins a canonical `hook_created`
|
|
56
|
+
* eventId for a legacy token claim — one written by a version of
|
|
57
|
+
* this storage that did not yet persist `eventId` inline in the
|
|
58
|
+
* claim file. Without this marker, two cross-process retries
|
|
59
|
+
* reading a legacy claim each generate their own eventId, land
|
|
60
|
+
* their `writeExclusive(eventPath)` calls at different paths, and
|
|
61
|
+
* append two `hook_created` events for the same `(runId, hookId)`.
|
|
62
|
+
*
|
|
63
|
+
* The marker is written via `writeExclusive` — the first retry to
|
|
64
|
+
* land it pins its candidate eventId as canonical, and every
|
|
65
|
+
* subsequent retry reads and adopts that eventId before the common
|
|
66
|
+
* event publish. Schema is just `{ eventId }` because identity is
|
|
67
|
+
* already encoded in the marker's filename hash, so different token
|
|
68
|
+
* lifetimes can never share one marker (see
|
|
69
|
+
* `hookRecoveryMarkerPath`).
|
|
70
|
+
*/
|
|
71
|
+
const HookRecoveryMarkerSchema = z.object({
|
|
72
|
+
eventId: z.string(),
|
|
73
|
+
});
|
|
74
|
+
async function readHookTokenClaim(constraintPath) {
|
|
75
|
+
try {
|
|
76
|
+
return await readJSON(constraintPath, HookTokenClaimSchema);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error instanceof SyntaxError || error instanceof z.ZodError) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function readHookRecoveryMarker(markerPath) {
|
|
86
|
+
try {
|
|
87
|
+
return await readJSON(markerPath, HookRecoveryMarkerSchema);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error instanceof SyntaxError || error instanceof z.ZodError) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Probe the run's event log for an existing `hook_created` event
|
|
98
|
+
* with the given correlationId. Used by the legacy-claim recovery
|
|
99
|
+
* path to detect "already published by a pre-upgrade write" before
|
|
100
|
+
* pinning a canonical eventId — without this check, a post-upgrade
|
|
101
|
+
* retry encountering a legacy claim whose `hook_created` was
|
|
102
|
+
* already written (with the pre-upgrade writer's own eventId) would
|
|
103
|
+
* pin a *different* eventId via the marker and publish a duplicate
|
|
104
|
+
* event at the marker's path.
|
|
105
|
+
*
|
|
106
|
+
* The inline-`eventId` fast path does NOT need this probe: the
|
|
107
|
+
* canonical eventId is durable in the claim file, so the existing
|
|
108
|
+
* publish (`writeExclusive(eventPath)`) will fail iff the event
|
|
109
|
+
* already exists at that exact path — which is the correct
|
|
110
|
+
* "already-published" semantic.
|
|
111
|
+
*/
|
|
112
|
+
async function findExistingHookCreatedEventId(basedir, runId, correlationId) {
|
|
113
|
+
const result = await paginatedFileSystemQuery({
|
|
114
|
+
directory: path.join(basedir, 'events'),
|
|
115
|
+
schema: EventSchema,
|
|
116
|
+
filePrefix: `${runId}-`,
|
|
117
|
+
filter: (event) => event.eventType === 'hook_created' &&
|
|
118
|
+
event.correlationId === correlationId,
|
|
119
|
+
limit: 1,
|
|
120
|
+
getCreatedAt: getObjectCreatedAt('evnt'),
|
|
121
|
+
getId: (event) => event.eventId,
|
|
122
|
+
});
|
|
123
|
+
return result.data[0]?.eventId ?? null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Repair an "event-first orphan": the hook entity write is deferred
|
|
127
|
+
* until after the `hook_created` event publish commits (so a failed
|
|
128
|
+
* publish cannot mutate already-committed state — see the comment on
|
|
129
|
+
* the deferred write), which opens the inverse crash window — a
|
|
130
|
+
* crash AFTER the event publish but BEFORE the deferred entity write
|
|
131
|
+
* leaves the event in the log with the hook entity missing. A retry
|
|
132
|
+
* then collides at the event publish and throws
|
|
133
|
+
* `EntityConflictError` (correct — the event IS committed), but
|
|
134
|
+
* without this repair the entity would stay missing forever and the
|
|
135
|
+
* hook would be unresolvable.
|
|
136
|
+
*
|
|
137
|
+
* The entity MUST be reconstructed from the persisted canonical
|
|
138
|
+
* event's payload — NOT the retry's `eventData` — otherwise a retry
|
|
139
|
+
* carrying different `metadata` / `isWebhook` would silently change
|
|
140
|
+
* committed state. The write uses `writeExclusive` (create-if-absent)
|
|
141
|
+
* so a concurrent writer racing this repair cannot be overwritten;
|
|
142
|
+
* whichever write lands first, the content is identical because both
|
|
143
|
+
* derive from the same persisted event.
|
|
144
|
+
*/
|
|
145
|
+
async function repairHookEntityFromPersistedEvent(basedir, runId, hookId, persistedEventId, tag) {
|
|
146
|
+
const compositeKey = `${runId}-${persistedEventId}`;
|
|
147
|
+
const persistedEvent = await readJSONWithFallback(basedir, 'events', compositeKey, EventSchema, tag);
|
|
148
|
+
if (!persistedEvent ||
|
|
149
|
+
persistedEvent.eventType !== 'hook_created' ||
|
|
150
|
+
persistedEvent.correlationId !== hookId) {
|
|
151
|
+
// Nothing trustworthy to repair from.
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const existingHook = await readJSONWithFallback(basedir, 'hooks', hookId, HookSchema, tag);
|
|
155
|
+
if (existingHook) {
|
|
156
|
+
// Entity already present — not an orphan, leave it untouched.
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const eventData = (persistedEvent.eventData ?? {});
|
|
160
|
+
if (typeof eventData.token !== 'string') {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const hook = {
|
|
164
|
+
runId,
|
|
165
|
+
hookId,
|
|
166
|
+
token: eventData.token,
|
|
167
|
+
metadata: eventData.metadata,
|
|
168
|
+
ownerId: 'local-owner',
|
|
169
|
+
projectId: 'local-project',
|
|
170
|
+
environment: 'local',
|
|
171
|
+
createdAt: persistedEvent.createdAt,
|
|
172
|
+
specVersion: persistedEvent.specVersion,
|
|
173
|
+
isWebhook: eventData.isWebhook ?? false,
|
|
174
|
+
isSystem: eventData.isSystem ?? false,
|
|
175
|
+
};
|
|
176
|
+
await writeExclusive(taggedPath(basedir, 'hooks', hookId, tag), JSON.stringify(hook, jsonReplacer, 2));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Atomically pin a canonical `hook_created` eventId for a legacy
|
|
180
|
+
* claim (one without an inline `eventId`). The first retry to
|
|
181
|
+
* `writeExclusive` the recovery marker wins; its `candidateEventId`
|
|
182
|
+
* becomes canonical. Subsequent retries read the marker and adopt
|
|
183
|
+
* its `eventId`. Together with the `writeExclusive(eventPath)` in
|
|
184
|
+
* the outer event publish, this gives the legacy-fallback path the
|
|
185
|
+
* same single-event convergence guarantee as the inline-`eventId`
|
|
186
|
+
* fast path.
|
|
187
|
+
*
|
|
188
|
+
* Returns the canonical eventId for the caller to adopt, or `null`
|
|
189
|
+
* if we lost the marker race AND the resulting marker file is
|
|
190
|
+
* unreadable (extremely rare; corrupted disk). Callers treat `null`
|
|
191
|
+
* as "give up, throw `EntityConflictError`" so the runtime's
|
|
192
|
+
* concurrent-replay catch path swallows this attempt and lets
|
|
193
|
+
* another one through.
|
|
194
|
+
*/
|
|
195
|
+
async function pinCanonicalEventIdForLegacyClaim(basedir, token, runId, hookId, candidateEventId) {
|
|
196
|
+
const markerPath = hookRecoveryMarkerPath(basedir, token, runId, hookId);
|
|
197
|
+
const markerContent = JSON.stringify({ eventId: candidateEventId });
|
|
198
|
+
const won = await writeExclusive(markerPath, markerContent);
|
|
199
|
+
if (won) {
|
|
200
|
+
return candidateEventId;
|
|
201
|
+
}
|
|
202
|
+
const existing = await readHookRecoveryMarker(markerPath);
|
|
203
|
+
return existing?.eventId ?? null;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* In-process per-key async mutex backed by a caller-supplied `Map`.
|
|
207
|
+
* Used by `createEventsStorage` to serialize same-key event writes
|
|
208
|
+
* (`step_*` for the same step, `hook_created` for the same hook).
|
|
209
|
+
* The map is instantiated per-storage-instance — different
|
|
210
|
+
* instances do NOT share locks, so two instances sharing one data
|
|
211
|
+
* directory behave exactly like two separate OS processes from the
|
|
212
|
+
* locking standpoint. Cross-instance / cross-process arbitration
|
|
213
|
+
* relies on the on-disk constraint / claim files instead.
|
|
214
|
+
*/
|
|
215
|
+
function withInProcessLock(locks, key, fn) {
|
|
216
|
+
const prev = locks.get(key);
|
|
217
|
+
const taskBox = {};
|
|
218
|
+
const task = (async () => {
|
|
219
|
+
if (prev) {
|
|
220
|
+
// Wait for the previous task to settle; don't inherit its errors.
|
|
221
|
+
await prev.catch(() => undefined);
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
return await fn();
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
if (locks.get(key) === taskBox.task) {
|
|
228
|
+
locks.delete(key);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})();
|
|
232
|
+
taskBox.task = task;
|
|
233
|
+
locks.set(key, task);
|
|
234
|
+
return task;
|
|
235
|
+
}
|
|
11
236
|
/**
|
|
12
237
|
* Helper function to delete all waits associated with a workflow run.
|
|
13
238
|
* Called when a run reaches a terminal state.
|
|
@@ -24,664 +249,1229 @@ async function deleteAllWaitsForRun(basedir, runId) {
|
|
|
24
249
|
}
|
|
25
250
|
}
|
|
26
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Persist a lifecycle-driven run update (run_started / run_completed /
|
|
254
|
+
* run_failed / run_cancelled) under the shared per-run file lock,
|
|
255
|
+
* re-reading the on-disk run inside the lock so any attribute writes
|
|
256
|
+
* that landed between the pre-validation `currentRun` read and this
|
|
257
|
+
* write are preserved. Without the re-read, an `experimentalSetAttributes`
|
|
258
|
+
* call sandwiched between the lifecycle read and write would be
|
|
259
|
+
* silently overwritten by the lifecycle write's stale attribute snapshot.
|
|
260
|
+
*
|
|
261
|
+
* `proposed` is the fully-constructed run row the caller wants to
|
|
262
|
+
* write (with the correct discriminated-union status branch). Only the
|
|
263
|
+
* `attributes` field is replaced with the freshest version inside the
|
|
264
|
+
* lock.
|
|
265
|
+
*/
|
|
266
|
+
async function writeRunUnderLifecycleLock(basedir, runId, tag, proposed) {
|
|
267
|
+
return withRunFileLock(runId, async () => {
|
|
268
|
+
const fresh = await readJSON(taggedPath(basedir, 'runs', runId, tag), WorkflowRunSchema);
|
|
269
|
+
const next = {
|
|
270
|
+
...proposed,
|
|
271
|
+
attributes: fresh?.attributes ?? proposed.attributes,
|
|
272
|
+
};
|
|
273
|
+
await writeJSON(taggedPath(basedir, 'runs', runId, tag), next, {
|
|
274
|
+
overwrite: true,
|
|
275
|
+
});
|
|
276
|
+
return next;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
27
279
|
/**
|
|
28
280
|
* Creates the events storage implementation using the filesystem.
|
|
29
281
|
* Implements the Storage['events'] interface with create, list, and listByCorrelationId operations.
|
|
30
282
|
*/
|
|
31
283
|
export function createEventsStorage(basedir, tag) {
|
|
284
|
+
// Per-instance in-process mutexes. Two storage instances sharing
|
|
285
|
+
// one data directory get independent lock maps, which makes them
|
|
286
|
+
// behave like two separate OS processes from the locking
|
|
287
|
+
// standpoint — cross-instance arbitration relies on the on-disk
|
|
288
|
+
// `writeExclusive` constraint / claim files instead. Tests use
|
|
289
|
+
// this to exercise cross-process convergence without spawning
|
|
290
|
+
// subprocesses.
|
|
291
|
+
//
|
|
292
|
+
// `stepLocks` serializes step lifecycle events for the same
|
|
293
|
+
// (runId, correlationId): see comment further down in the
|
|
294
|
+
// `isStepEvent` branch.
|
|
295
|
+
//
|
|
296
|
+
// `hookLocks` serializes `hook_created` calls for the same
|
|
297
|
+
// (runId, correlationId) so the "claim token, then write hook
|
|
298
|
+
// entity + event" sequence runs to completion before another
|
|
299
|
+
// in-process invocation enters the dedup branch.
|
|
300
|
+
const stepLocks = new Map();
|
|
301
|
+
const hookLocks = new Map();
|
|
32
302
|
return {
|
|
33
303
|
async create(runId, data, params) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
304
|
+
// Validate request-supplied IDs before they're concatenated into
|
|
305
|
+
// filesystem paths. This is the primary defense against path traversal
|
|
306
|
+
// attacks where a client supplies runId / correlationId values like
|
|
307
|
+
// "../../../package" to read or write files outside the storage root.
|
|
308
|
+
// Run before taking the per-step mutex so malformed inputs fail fast.
|
|
309
|
+
//
|
|
310
|
+
// Empty `correlationId` values are also rejected here: the event
|
|
311
|
+
// schemas only require `z.string()`, so without this check a
|
|
312
|
+
// step_created / hook_created / wait_created request with
|
|
313
|
+
// `correlationId: ''` would silently be written under a malformed
|
|
314
|
+
// composite key like `${runId}-`.
|
|
315
|
+
if (runId != null && runId !== '') {
|
|
316
|
+
assertSafeEntityId('runId', runId);
|
|
40
317
|
}
|
|
41
|
-
|
|
42
|
-
|
|
318
|
+
if ('correlationId' in data && typeof data.correlationId === 'string') {
|
|
319
|
+
assertSafeEntityId('correlationId', data.correlationId);
|
|
43
320
|
}
|
|
44
|
-
|
|
45
|
-
|
|
321
|
+
// Step lifecycle events are serialized per-step via an in-process mutex
|
|
322
|
+
// so that the "check state, then write" sequence in step_started /
|
|
323
|
+
// step_completed / step_failed / step_retrying is atomic. step_created
|
|
324
|
+
// is also serialized so duplicate-create races don't leave extra
|
|
325
|
+
// step_created events in the log.
|
|
326
|
+
const isStepEvent = data.eventType === 'step_created' ||
|
|
327
|
+
data.eventType === 'step_started' ||
|
|
328
|
+
data.eventType === 'step_completed' ||
|
|
329
|
+
data.eventType === 'step_failed' ||
|
|
330
|
+
data.eventType === 'step_retrying';
|
|
331
|
+
if (isStepEvent && runId && data.correlationId) {
|
|
332
|
+
const lockKey = tag
|
|
333
|
+
? `${runId}-${data.correlationId}.${tag}`
|
|
334
|
+
: `${runId}-${data.correlationId}`;
|
|
335
|
+
return withInProcessLock(stepLocks, lockKey, () => createImpl());
|
|
46
336
|
}
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
337
|
+
// `hook_created` is serialized per-(runId, hookId) so the
|
|
338
|
+
// "claim token, write hook entity, write event" sequence runs to
|
|
339
|
+
// completion before another in-process invocation enters the
|
|
340
|
+
// same-hook dedup branch. Without this, two same-tick concurrent
|
|
341
|
+
// callers can race between the winner's `writeExclusive(claim)`
|
|
342
|
+
// and `writeJSON(hook)`, making the second caller momentarily
|
|
343
|
+
// observe a claim with no matching hook entity — which the
|
|
344
|
+
// crash-recovery path below would misinterpret as a prior crash
|
|
345
|
+
// and incorrectly fall through to a second hook entity write.
|
|
346
|
+
if (data.eventType === 'hook_created' && runId && data.correlationId) {
|
|
347
|
+
const lockKey = tag
|
|
348
|
+
? `${runId}-${data.correlationId}.hook.${tag}`
|
|
349
|
+
: `${runId}-${data.correlationId}.hook`;
|
|
350
|
+
return withInProcessLock(hookLocks, lockKey, () => createImpl());
|
|
53
351
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
352
|
+
return createImpl();
|
|
353
|
+
async function createImpl() {
|
|
354
|
+
// Most paths use the freshly-generated candidate eventId. The
|
|
355
|
+
// hook_created dedup-recovery path below may reassign it to
|
|
356
|
+
// the canonical eventId persisted in the durable token claim
|
|
357
|
+
// so concurrent / cross-process workers converge on a single
|
|
358
|
+
// event in the log.
|
|
359
|
+
let eventId = `evnt_${monotonicUlid()}`;
|
|
360
|
+
const now = new Date();
|
|
361
|
+
// For run_created events, use client-provided runId or generate one server-side
|
|
362
|
+
let effectiveRunId;
|
|
363
|
+
if (data.eventType === 'run_created' && (!runId || runId === '')) {
|
|
364
|
+
effectiveRunId = `wrun_${monotonicUlid()}`;
|
|
365
|
+
}
|
|
366
|
+
else if (!runId) {
|
|
367
|
+
throw new Error('runId is required for non-run_created events');
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
effectiveRunId = runId;
|
|
371
|
+
}
|
|
372
|
+
// Validate client-provided runId timestamp is within acceptable threshold
|
|
373
|
+
if (data.eventType === 'run_created' && runId && runId !== '') {
|
|
374
|
+
const validationError = validateUlidTimestamp(effectiveRunId, 'wrun_');
|
|
375
|
+
if (validationError) {
|
|
376
|
+
throw new WorkflowWorldError(validationError);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// specVersion is always sent by the runtime, but we provide a fallback for safety
|
|
380
|
+
const effectiveSpecVersion = data.specVersion ?? SPEC_VERSION_CURRENT;
|
|
381
|
+
// Helper to check if run is in terminal state
|
|
382
|
+
const isRunTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
|
|
383
|
+
// Helper to check if step is in terminal state
|
|
384
|
+
const isStepTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
|
|
385
|
+
// Get current run state for validation (if not creating a new run)
|
|
386
|
+
// Skip run validation for step_completed and step_retrying - they only operate
|
|
387
|
+
// on running steps, and running steps are always allowed to modify regardless
|
|
388
|
+
// of run state. This optimization saves filesystem reads per step event.
|
|
389
|
+
let currentRun = null;
|
|
390
|
+
const skipRunValidationEvents = ['step_completed', 'step_retrying'];
|
|
391
|
+
if (data.eventType !== 'run_created' &&
|
|
392
|
+
!skipRunValidationEvents.includes(data.eventType)) {
|
|
393
|
+
currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
|
|
394
|
+
// Resilient start: run_started on non-existent run with eventData
|
|
395
|
+
// creates the run first, so the queue can bootstrap a run that
|
|
396
|
+
// failed to create during start().
|
|
397
|
+
if (data.eventType === 'run_started' &&
|
|
398
|
+
!currentRun &&
|
|
399
|
+
'eventData' in data &&
|
|
400
|
+
data.eventData) {
|
|
401
|
+
const runInputData = data.eventData;
|
|
402
|
+
if (runInputData.deploymentId &&
|
|
403
|
+
runInputData.workflowName &&
|
|
404
|
+
runInputData.input !== undefined) {
|
|
405
|
+
validateAttributeChanges(Object.entries(runInputData.attributes ?? {}).map(([key, value]) => ({ key, value })), {
|
|
406
|
+
allowReservedAttributes: runInputData.allowReservedAttributes === true,
|
|
407
|
+
});
|
|
408
|
+
// Atomically try to publish the run entity so only the first
|
|
409
|
+
// writer wins, preventing a TOCTOU race where a concurrent
|
|
410
|
+
// run_created from start() could overwrite a run that was
|
|
411
|
+
// already transitioned to 'running'.
|
|
412
|
+
const createdRun = {
|
|
107
413
|
runId: effectiveRunId,
|
|
108
|
-
|
|
109
|
-
|
|
414
|
+
deploymentId: runInputData.deploymentId,
|
|
415
|
+
status: 'pending',
|
|
416
|
+
workflowName: runInputData.workflowName,
|
|
110
417
|
specVersion: effectiveSpecVersion,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
418
|
+
executionContext: runInputData.executionContext,
|
|
419
|
+
input: runInputData.input,
|
|
420
|
+
output: undefined,
|
|
421
|
+
error: undefined,
|
|
422
|
+
startedAt: undefined,
|
|
423
|
+
completedAt: undefined,
|
|
424
|
+
attributes: runInputData.attributes ?? {},
|
|
425
|
+
createdAt: now,
|
|
426
|
+
updatedAt: now,
|
|
117
427
|
};
|
|
118
|
-
const
|
|
119
|
-
await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
428
|
+
const runPath = taggedPath(basedir, 'runs', effectiveRunId, tag);
|
|
429
|
+
const created = await writeExclusive(runPath, JSON.stringify(createdRun, jsonReplacer));
|
|
430
|
+
if (created) {
|
|
431
|
+
// We created the run — also write the run_created event.
|
|
432
|
+
const runCreatedEventId = `evnt_${monotonicUlid()}`;
|
|
433
|
+
const runCreatedEvent = {
|
|
434
|
+
eventType: 'run_created',
|
|
435
|
+
runId: effectiveRunId,
|
|
436
|
+
eventId: runCreatedEventId,
|
|
437
|
+
createdAt: now,
|
|
438
|
+
specVersion: effectiveSpecVersion,
|
|
439
|
+
eventData: {
|
|
440
|
+
deploymentId: runInputData.deploymentId,
|
|
441
|
+
workflowName: runInputData.workflowName,
|
|
442
|
+
input: runInputData.input,
|
|
443
|
+
executionContext: runInputData.executionContext,
|
|
444
|
+
attributes: runInputData.attributes,
|
|
445
|
+
allowReservedAttributes: runInputData.allowReservedAttributes,
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
const createdCompositeKey = `${effectiveRunId}-${runCreatedEventId}`;
|
|
449
|
+
await writeJSON(taggedPath(basedir, 'events', createdCompositeKey, tag), runCreatedEvent);
|
|
450
|
+
currentRun = createdRun;
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
// Run already exists (concurrent run_created won the
|
|
454
|
+
// race). Re-read it so downstream logic sees the real state.
|
|
455
|
+
currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
|
|
456
|
+
}
|
|
126
457
|
}
|
|
127
458
|
}
|
|
128
459
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (currentRun) {
|
|
136
|
-
// Check if run requires a newer world version
|
|
137
|
-
if (requiresNewerWorld(currentRun.specVersion)) {
|
|
138
|
-
throw new RunNotSupportedError(currentRun.specVersion, SPEC_VERSION_CURRENT);
|
|
139
|
-
}
|
|
140
|
-
// Route to legacy handler for pre-event-sourcing runs
|
|
141
|
-
if (isLegacySpecVersion(currentRun.specVersion)) {
|
|
142
|
-
return handleLegacyEvent(basedir, effectiveRunId, data, currentRun, params);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// ============================================================
|
|
146
|
-
// VALIDATION: Terminal state and event ordering checks
|
|
147
|
-
// ============================================================
|
|
148
|
-
// Run terminal state validation
|
|
149
|
-
if (currentRun && isRunTerminal(currentRun.status)) {
|
|
150
|
-
const runTerminalEvents = [
|
|
151
|
-
'run_started',
|
|
152
|
-
'run_completed',
|
|
153
|
-
'run_failed',
|
|
154
|
-
];
|
|
155
|
-
// Idempotent operation: run_cancelled on already cancelled run is allowed
|
|
156
|
-
if (data.eventType === 'run_cancelled' &&
|
|
157
|
-
currentRun.status === 'cancelled') {
|
|
158
|
-
// Return existing state (idempotent)
|
|
159
|
-
const event = {
|
|
160
|
-
...data,
|
|
161
|
-
runId: effectiveRunId,
|
|
162
|
-
eventId,
|
|
163
|
-
createdAt: now,
|
|
164
|
-
specVersion: effectiveSpecVersion,
|
|
165
|
-
};
|
|
166
|
-
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
167
|
-
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
|
|
168
|
-
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
169
|
-
return {
|
|
170
|
-
event: stripEventDataRefs(event, resolveData),
|
|
171
|
-
run: currentRun,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
// For run_started on terminal runs, use RunExpiredError so the
|
|
175
|
-
// runtime knows to exit without retrying.
|
|
176
|
-
if (data.eventType === 'run_started') {
|
|
177
|
-
throw new RunExpiredError(`Workflow run "${effectiveRunId}" is already in terminal state "${currentRun.status}"`);
|
|
460
|
+
// run_failed on a non-existent run is rejected to match the
|
|
461
|
+
// postgres and vercel worlds, which both surface this as a
|
|
462
|
+
// WorkflowRunNotFoundError rather than silently persisting an
|
|
463
|
+
// event for a run that was never created.
|
|
464
|
+
if (data.eventType === 'run_failed' && !currentRun) {
|
|
465
|
+
throw new WorkflowRunNotFoundError(effectiveRunId);
|
|
178
466
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
data.eventType === 'run_cancelled') {
|
|
182
|
-
throw new EntityConflictError(`Cannot transition run from terminal state "${currentRun.status}"`);
|
|
467
|
+
if (data.eventType === 'attr_set' && !currentRun) {
|
|
468
|
+
throw new WorkflowRunNotFoundError(effectiveRunId);
|
|
183
469
|
}
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
470
|
+
// ============================================================
|
|
471
|
+
// VERSION COMPATIBILITY: Check run spec version
|
|
472
|
+
// ============================================================
|
|
473
|
+
// For events that have fetched the run, check version compatibility.
|
|
474
|
+
// Skip for run_created (no existing run) and runtime events (step_completed, step_retrying).
|
|
475
|
+
if (currentRun) {
|
|
476
|
+
// Check if run requires a newer world version
|
|
477
|
+
if (requiresNewerWorld(currentRun.specVersion)) {
|
|
478
|
+
throw new RunNotSupportedError(currentRun.specVersion, SPEC_VERSION_CURRENT);
|
|
479
|
+
}
|
|
480
|
+
// Route to legacy handler for pre-event-sourcing runs
|
|
481
|
+
if (isLegacySpecVersion(currentRun.specVersion)) {
|
|
482
|
+
return handleLegacyEvent(basedir, effectiveRunId, data, currentRun, params);
|
|
483
|
+
}
|
|
189
484
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
'
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
// Step terminal state validation
|
|
208
|
-
if (isStepTerminal(validatedStep.status)) {
|
|
209
|
-
throw new EntityConflictError(`Cannot modify step in terminal state "${validatedStep.status}"`);
|
|
210
|
-
}
|
|
211
|
-
// On terminal runs: only allow completing/failing in-progress steps
|
|
485
|
+
// ============================================================
|
|
486
|
+
// VALIDATION: Terminal state and event ordering checks
|
|
487
|
+
// ============================================================
|
|
488
|
+
// Lazy step start: a step_started carrying step-creation data
|
|
489
|
+
// (stepName + input) is allowed to arrive with no prior step_created
|
|
490
|
+
// — it creates the step on the fly (see the materialization block
|
|
491
|
+
// below). This mirrors the resilient run_started path. Detect it here
|
|
492
|
+
// so the entity-creation terminal-run guard treats it like a creation
|
|
493
|
+
// and the "step must exist" ordering guard doesn't reject it.
|
|
494
|
+
const lazyStepStart = data.eventType === 'step_started' &&
|
|
495
|
+
'eventData' in data &&
|
|
496
|
+
!!data.eventData &&
|
|
497
|
+
typeof data.eventData.stepName ===
|
|
498
|
+
'string' &&
|
|
499
|
+
data.eventData.input !== undefined;
|
|
500
|
+
// Run terminal state validation
|
|
212
501
|
if (currentRun && isRunTerminal(currentRun.status)) {
|
|
213
|
-
|
|
214
|
-
|
|
502
|
+
const runTerminalEvents = [
|
|
503
|
+
'run_started',
|
|
504
|
+
'run_completed',
|
|
505
|
+
'run_failed',
|
|
506
|
+
];
|
|
507
|
+
// Idempotent operation: run_cancelled on already cancelled run is allowed
|
|
508
|
+
if (data.eventType === 'run_cancelled' &&
|
|
509
|
+
currentRun.status === 'cancelled') {
|
|
510
|
+
// Return existing state (idempotent)
|
|
511
|
+
const event = {
|
|
512
|
+
...data,
|
|
513
|
+
runId: effectiveRunId,
|
|
514
|
+
eventId,
|
|
515
|
+
createdAt: now,
|
|
516
|
+
specVersion: effectiveSpecVersion,
|
|
517
|
+
};
|
|
518
|
+
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
519
|
+
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
|
|
520
|
+
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
521
|
+
return {
|
|
522
|
+
event: stripEventDataRefs(event, resolveData),
|
|
523
|
+
run: currentRun,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
// For run_started on terminal runs, use RunExpiredError so the
|
|
527
|
+
// runtime knows to exit without retrying.
|
|
528
|
+
if (data.eventType === 'run_started') {
|
|
529
|
+
throw new RunExpiredError(`Workflow run "${effectiveRunId}" is already in terminal state "${currentRun.status}"`);
|
|
530
|
+
}
|
|
531
|
+
// Other run state transitions are not allowed on terminal runs
|
|
532
|
+
if (runTerminalEvents.includes(data.eventType) ||
|
|
533
|
+
data.eventType === 'run_cancelled') {
|
|
534
|
+
throw new EntityConflictError(`Cannot transition run from terminal state "${currentRun.status}"`);
|
|
535
|
+
}
|
|
536
|
+
// Creating new entities on terminal runs is not allowed. A lazy
|
|
537
|
+
// step_started creates a step, so it is rejected here too — a bare
|
|
538
|
+
// (non-lazy) step_started falls through to the step-validation
|
|
539
|
+
// block below, which uses RunExpiredError for terminal runs.
|
|
540
|
+
if (data.eventType === 'step_created' ||
|
|
541
|
+
data.eventType === 'hook_created' ||
|
|
542
|
+
data.eventType === 'wait_created' ||
|
|
543
|
+
lazyStepStart) {
|
|
544
|
+
throw new EntityConflictError(`Cannot create new entities on run in terminal state "${currentRun.status}"`);
|
|
545
|
+
}
|
|
546
|
+
if (data.eventType === 'attr_set') {
|
|
547
|
+
throw new EntityConflictError(`Cannot set attributes on run in terminal state "${currentRun.status}"`);
|
|
215
548
|
}
|
|
216
549
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
550
|
+
// Step-related event validation (ordering and terminal state)
|
|
551
|
+
// Store existingStep so we can reuse it later (avoid double read)
|
|
552
|
+
let validatedStep = null;
|
|
553
|
+
const stepEvents = [
|
|
554
|
+
'step_started',
|
|
555
|
+
'step_completed',
|
|
556
|
+
'step_failed',
|
|
557
|
+
'step_retrying',
|
|
558
|
+
];
|
|
559
|
+
if (stepEvents.includes(data.eventType) && data.correlationId) {
|
|
560
|
+
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
561
|
+
validatedStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
|
|
562
|
+
// Event ordering: step must exist before these events — except on
|
|
563
|
+
// the lazy-start path, where step_started creates the step itself.
|
|
564
|
+
if (!validatedStep && !lazyStepStart) {
|
|
565
|
+
throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
|
|
566
|
+
}
|
|
567
|
+
// Lazy start exactly-once gate: a lazy step_started always CREATES
|
|
568
|
+
// the step (the owned-inline path only sends one for a step whose
|
|
569
|
+
// step_created it deferred). If the step already exists, a concurrent
|
|
570
|
+
// handler won the create — this caller is a loser and must not start
|
|
571
|
+
// or run the step. Throw EntityConflictError so the runtime's
|
|
572
|
+
// executeStep maps it to `skipped`. This is critical: the plain start
|
|
573
|
+
// transition below permits re-starting a non-terminal step (retries
|
|
574
|
+
// rely on that), so without this gate a loser would re-start a
|
|
575
|
+
// running step and run the body a second time.
|
|
576
|
+
if (lazyStepStart && validatedStep) {
|
|
577
|
+
throw new EntityConflictError(`Step "${data.correlationId}" already created`);
|
|
578
|
+
}
|
|
579
|
+
// Step terminal state validation. validatedStep can be null only on
|
|
580
|
+
// the lazy-start path (no step yet) — there is nothing terminal to
|
|
581
|
+
// guard against in that case, so these checks are skipped.
|
|
582
|
+
if (validatedStep) {
|
|
583
|
+
if (isStepTerminal(validatedStep.status)) {
|
|
584
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${validatedStep.status}"`);
|
|
585
|
+
}
|
|
586
|
+
// On terminal runs: only allow completing/failing in-progress steps
|
|
587
|
+
if (currentRun && isRunTerminal(currentRun.status)) {
|
|
588
|
+
if (validatedStep.status !== 'running') {
|
|
589
|
+
throw new RunExpiredError(`Cannot modify non-running step on run in terminal state "${currentRun.status}"`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
225
593
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
let wait;
|
|
243
|
-
// Create/update entity based on event type (event-sourced architecture)
|
|
244
|
-
// Run lifecycle events
|
|
245
|
-
if (data.eventType === 'run_created' && 'eventData' in data) {
|
|
246
|
-
const runData = data.eventData;
|
|
247
|
-
run = {
|
|
594
|
+
// Hook-related event validation (ordering)
|
|
595
|
+
const hookEventsRequiringExistence = ['hook_disposed', 'hook_received'];
|
|
596
|
+
if (hookEventsRequiringExistence.includes(data.eventType) &&
|
|
597
|
+
data.correlationId) {
|
|
598
|
+
const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
|
|
599
|
+
if (!existingHook) {
|
|
600
|
+
throw new HookNotFoundError(data.correlationId);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// `event` may be reassigned later in the `hook_created`
|
|
604
|
+
// dedup-recovery branch to swap in a canonical eventId /
|
|
605
|
+
// createdAt persisted in the durable token claim so
|
|
606
|
+
// concurrent / cross-process workers converge on a single
|
|
607
|
+
// event in the log.
|
|
608
|
+
let event = {
|
|
609
|
+
...data,
|
|
248
610
|
runId: effectiveRunId,
|
|
249
|
-
|
|
250
|
-
status: 'pending',
|
|
251
|
-
workflowName: runData.workflowName,
|
|
252
|
-
// Propagate specVersion from the event to the run entity
|
|
253
|
-
specVersion: effectiveSpecVersion,
|
|
254
|
-
executionContext: runData.executionContext,
|
|
255
|
-
input: runData.input,
|
|
256
|
-
output: undefined,
|
|
257
|
-
error: undefined,
|
|
258
|
-
startedAt: undefined,
|
|
259
|
-
completedAt: undefined,
|
|
611
|
+
eventId,
|
|
260
612
|
createdAt: now,
|
|
261
|
-
|
|
613
|
+
specVersion: effectiveSpecVersion,
|
|
262
614
|
};
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// in duplicate run_created events in the event log.
|
|
267
|
-
const runPath = taggedPath(basedir, 'runs', effectiveRunId, tag);
|
|
268
|
-
const created = await writeExclusive(runPath, JSON.stringify(run, jsonReplacer, 2));
|
|
269
|
-
if (!created) {
|
|
270
|
-
throw new EntityConflictError(`Workflow run "${effectiveRunId}" already exists`);
|
|
615
|
+
// Strip eventData from run_started — it belongs on run_created only.
|
|
616
|
+
if (data.eventType === 'run_started' && 'eventData' in event) {
|
|
617
|
+
delete event.eventData;
|
|
271
618
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
619
|
+
// Strip only the step `input` from the lazy step_started event row —
|
|
620
|
+
// it belongs on the synthetic step_created written above. stepName is
|
|
621
|
+
// preserved for the client replay consumer's step-name divergence
|
|
622
|
+
// check (packages/core/src/step.ts).
|
|
623
|
+
if (lazyStepStart &&
|
|
624
|
+
'eventData' in event &&
|
|
625
|
+
event.eventData) {
|
|
626
|
+
const { input: _strippedInput, ...rest } = event.eventData;
|
|
627
|
+
event.eventData = rest;
|
|
628
|
+
}
|
|
629
|
+
// Track entity created/updated for EventResult
|
|
630
|
+
let run;
|
|
631
|
+
let step;
|
|
632
|
+
let hook;
|
|
633
|
+
let wait;
|
|
634
|
+
// Lazy step start: set true when this step_started atomically created
|
|
635
|
+
// the step (the caller won the create-claim). Surfaced on EventResult
|
|
636
|
+
// as the runtime's exactly-once ownership signal.
|
|
637
|
+
let stepCreatedLazily = false;
|
|
638
|
+
// For `hook_created`, the hook entity write is deferred until
|
|
639
|
+
// AFTER the outer event publish succeeds, so a retry that
|
|
640
|
+
// collides with an already-published `hook_created` does not
|
|
641
|
+
// mutate the durable hook entity with the retry's payload.
|
|
642
|
+
// `hookEntityWriteOptions` carries the `{ overwrite }` mode
|
|
643
|
+
// chosen by the dedup-recovery branch above (undefined for
|
|
644
|
+
// first writers, `{ overwrite: true }` for retries that may
|
|
645
|
+
// be repairing an orphaned partial write).
|
|
646
|
+
let hookEntityWriteOptions;
|
|
647
|
+
// Create/update entity based on event type (event-sourced architecture)
|
|
648
|
+
// Run lifecycle events
|
|
649
|
+
if (data.eventType === 'run_created' && 'eventData' in data) {
|
|
650
|
+
const runData = data.eventData;
|
|
651
|
+
validateAttributeChanges(Object.entries(runData.attributes ?? {}).map(([key, value]) => ({
|
|
652
|
+
key,
|
|
653
|
+
value,
|
|
654
|
+
})), {
|
|
655
|
+
allowReservedAttributes: runData.allowReservedAttributes === true,
|
|
656
|
+
});
|
|
284
657
|
run = {
|
|
285
|
-
runId:
|
|
286
|
-
deploymentId:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
status: 'running',
|
|
658
|
+
runId: effectiveRunId,
|
|
659
|
+
deploymentId: runData.deploymentId,
|
|
660
|
+
status: 'pending',
|
|
661
|
+
workflowName: runData.workflowName,
|
|
662
|
+
// Propagate specVersion from the event to the run entity
|
|
663
|
+
specVersion: effectiveSpecVersion,
|
|
664
|
+
executionContext: runData.executionContext,
|
|
665
|
+
input: runData.input,
|
|
294
666
|
output: undefined,
|
|
295
667
|
error: undefined,
|
|
668
|
+
startedAt: undefined,
|
|
296
669
|
completedAt: undefined,
|
|
297
|
-
|
|
670
|
+
attributes: runData.attributes ?? {},
|
|
671
|
+
createdAt: now,
|
|
298
672
|
updatedAt: now,
|
|
299
673
|
};
|
|
300
|
-
|
|
674
|
+
// Atomically publish the run entity file without overwriting an
|
|
675
|
+
// existing winner. This prevents a TOCTOU race with the resilient
|
|
676
|
+
// start path (run_started on non-existent run) that could result in
|
|
677
|
+
// duplicate run_created events in the event log.
|
|
678
|
+
const runPath = taggedPath(basedir, 'runs', effectiveRunId, tag);
|
|
679
|
+
const created = await writeExclusive(runPath, JSON.stringify(run, jsonReplacer, 2));
|
|
680
|
+
if (!created) {
|
|
681
|
+
throw new EntityConflictError(`Workflow run "${effectiveRunId}" already exists`);
|
|
682
|
+
}
|
|
301
683
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
684
|
+
else if (data.eventType === 'run_started') {
|
|
685
|
+
// Reuse currentRun from validation (already read above)
|
|
686
|
+
if (currentRun) {
|
|
687
|
+
// If already running, return the run without inserting a
|
|
688
|
+
// duplicate event. This makes run_started idempotent for
|
|
689
|
+
// concurrent invocations. We omit preloaded events here
|
|
690
|
+
// because this is a rare race-condition path — the runtime
|
|
691
|
+
// falls back to loadWorkflowRunEvents().
|
|
692
|
+
if (currentRun.status === 'running') {
|
|
693
|
+
return { run: currentRun };
|
|
694
|
+
}
|
|
695
|
+
run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
|
|
696
|
+
runId: currentRun.runId,
|
|
697
|
+
deploymentId: currentRun.deploymentId,
|
|
698
|
+
workflowName: currentRun.workflowName,
|
|
699
|
+
specVersion: currentRun.specVersion,
|
|
700
|
+
executionContext: currentRun.executionContext,
|
|
701
|
+
input: currentRun.input,
|
|
702
|
+
createdAt: currentRun.createdAt,
|
|
703
|
+
expiredAt: currentRun.expiredAt,
|
|
704
|
+
status: 'running',
|
|
705
|
+
output: undefined,
|
|
706
|
+
error: undefined,
|
|
707
|
+
completedAt: undefined,
|
|
708
|
+
startedAt: currentRun.startedAt ?? now,
|
|
709
|
+
updatedAt: now,
|
|
710
|
+
attributes: currentRun.attributes,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
328
713
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
};
|
|
356
|
-
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
|
|
357
|
-
await Promise.all([
|
|
358
|
-
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
359
|
-
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
360
|
-
]);
|
|
714
|
+
else if (data.eventType === 'run_completed' && 'eventData' in data) {
|
|
715
|
+
const completedData = data.eventData;
|
|
716
|
+
// Reuse currentRun from validation (already read above)
|
|
717
|
+
if (currentRun) {
|
|
718
|
+
run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
|
|
719
|
+
runId: currentRun.runId,
|
|
720
|
+
deploymentId: currentRun.deploymentId,
|
|
721
|
+
workflowName: currentRun.workflowName,
|
|
722
|
+
specVersion: currentRun.specVersion,
|
|
723
|
+
executionContext: currentRun.executionContext,
|
|
724
|
+
input: currentRun.input,
|
|
725
|
+
createdAt: currentRun.createdAt,
|
|
726
|
+
expiredAt: currentRun.expiredAt,
|
|
727
|
+
startedAt: currentRun.startedAt,
|
|
728
|
+
status: 'completed',
|
|
729
|
+
output: completedData.output,
|
|
730
|
+
error: undefined,
|
|
731
|
+
completedAt: now,
|
|
732
|
+
updatedAt: now,
|
|
733
|
+
attributes: currentRun.attributes,
|
|
734
|
+
});
|
|
735
|
+
await Promise.all([
|
|
736
|
+
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
737
|
+
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
738
|
+
]);
|
|
739
|
+
}
|
|
361
740
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
741
|
+
else if (data.eventType === 'run_failed' && 'eventData' in data) {
|
|
742
|
+
const failedData = data.eventData;
|
|
743
|
+
// Reuse currentRun from validation (already read above)
|
|
744
|
+
if (currentRun) {
|
|
745
|
+
// The error field is SerializedData (Uint8Array) produced by
|
|
746
|
+
// dehydrateRunError. We store it verbatim — consumers hydrate it
|
|
747
|
+
// via hydrateRunError to reconstruct the original thrown value.
|
|
748
|
+
run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
|
|
749
|
+
runId: currentRun.runId,
|
|
750
|
+
deploymentId: currentRun.deploymentId,
|
|
751
|
+
workflowName: currentRun.workflowName,
|
|
752
|
+
specVersion: currentRun.specVersion,
|
|
753
|
+
executionContext: currentRun.executionContext,
|
|
754
|
+
input: currentRun.input,
|
|
755
|
+
createdAt: currentRun.createdAt,
|
|
756
|
+
expiredAt: currentRun.expiredAt,
|
|
757
|
+
startedAt: currentRun.startedAt,
|
|
758
|
+
status: 'failed',
|
|
759
|
+
output: undefined,
|
|
760
|
+
error: failedData.error,
|
|
761
|
+
errorCode: failedData.errorCode,
|
|
762
|
+
completedAt: now,
|
|
763
|
+
updatedAt: now,
|
|
764
|
+
attributes: currentRun.attributes,
|
|
765
|
+
});
|
|
766
|
+
await Promise.all([
|
|
767
|
+
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
768
|
+
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
769
|
+
]);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
else if (data.eventType === 'run_cancelled') {
|
|
773
|
+
// Reuse currentRun from validation (already read above)
|
|
774
|
+
if (currentRun) {
|
|
775
|
+
run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
|
|
776
|
+
runId: currentRun.runId,
|
|
777
|
+
deploymentId: currentRun.deploymentId,
|
|
778
|
+
workflowName: currentRun.workflowName,
|
|
779
|
+
specVersion: currentRun.specVersion,
|
|
780
|
+
executionContext: currentRun.executionContext,
|
|
781
|
+
input: currentRun.input,
|
|
782
|
+
createdAt: currentRun.createdAt,
|
|
783
|
+
expiredAt: currentRun.expiredAt,
|
|
784
|
+
startedAt: currentRun.startedAt,
|
|
785
|
+
status: 'cancelled',
|
|
786
|
+
output: undefined,
|
|
787
|
+
error: undefined,
|
|
788
|
+
completedAt: now,
|
|
789
|
+
updatedAt: now,
|
|
790
|
+
attributes: currentRun.attributes,
|
|
791
|
+
});
|
|
792
|
+
await Promise.all([
|
|
793
|
+
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
794
|
+
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
795
|
+
]);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else if (data.eventType === 'attr_set' && currentRun) {
|
|
799
|
+
run = await withRunFileLock(effectiveRunId, async () => {
|
|
800
|
+
const fresh = await readJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), WorkflowRunSchema);
|
|
801
|
+
if (!fresh) {
|
|
802
|
+
throw new WorkflowRunNotFoundError(effectiveRunId);
|
|
803
|
+
}
|
|
804
|
+
validateAttributeChanges(data.eventData.changes, {
|
|
805
|
+
existingKeys: Object.keys(fresh.attributes),
|
|
806
|
+
allowReservedAttributes: data.eventData.allowReservedAttributes === true,
|
|
807
|
+
});
|
|
808
|
+
// Claim the correlation dedup lock only after validation: a
|
|
809
|
+
// validation failure must leave the correlationId unclaimed so
|
|
810
|
+
// the runtime's retry of the same event is not misreported as
|
|
811
|
+
// "already exists" while the event was never written (the
|
|
812
|
+
// dispatcher would then wait forever for an event that is not
|
|
813
|
+
// in the log).
|
|
814
|
+
if (data.correlationId &&
|
|
815
|
+
data.eventData.writer.type === 'workflow') {
|
|
816
|
+
const attrLockName = tag
|
|
817
|
+
? `${effectiveRunId}-${data.correlationId}.created.${tag}`
|
|
818
|
+
: `${effectiveRunId}-${data.correlationId}.created`;
|
|
819
|
+
const attrLockPath = resolveWithinBase(basedir, '.locks', 'attributes', attrLockName);
|
|
820
|
+
const attrClaimed = await writeExclusive(attrLockPath, '');
|
|
821
|
+
if (!attrClaimed) {
|
|
822
|
+
throw new EntityConflictError(`Attribute event "${data.correlationId}" already exists`);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const next = {
|
|
826
|
+
...fresh,
|
|
827
|
+
attributes: applyAttributeChanges(fresh.attributes, data.eventData.changes),
|
|
828
|
+
updatedAt: now,
|
|
829
|
+
};
|
|
830
|
+
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), next, { overwrite: true });
|
|
831
|
+
return next;
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
else if (
|
|
835
|
+
// Step lifecycle events
|
|
836
|
+
data.eventType === 'step_created' &&
|
|
837
|
+
'eventData' in data) {
|
|
838
|
+
// step_created: Creates step entity with status 'pending', attempt=0, createdAt set.
|
|
839
|
+
// Two concurrent invocations with identical correlationIds (e.g. the
|
|
840
|
+
// snapshot runtime's deterministic correlationIds across replays)
|
|
841
|
+
// must be deduped — otherwise both writes succeed and the event log
|
|
842
|
+
// ends up with duplicate step_created entries. The outer
|
|
843
|
+
// withStepLock mutex serializes within a single process; this
|
|
844
|
+
// The exclusive constraint file additionally protects against
|
|
845
|
+
// cross-process races (two pnpm workers, redelivered queue messages,
|
|
846
|
+
// etc.). The loser throws EntityConflictError so the runtime's
|
|
847
|
+
// existing catch path can swallow it and avoid double-queuing the
|
|
848
|
+
// step.
|
|
849
|
+
const stepCreatedLockName = tag
|
|
850
|
+
? `${effectiveRunId}-${data.correlationId}.created.${tag}`
|
|
851
|
+
: `${effectiveRunId}-${data.correlationId}.created`;
|
|
852
|
+
const stepCreatedLockPath = resolveWithinBase(basedir, '.locks', 'steps', stepCreatedLockName);
|
|
853
|
+
const stepCreatedClaimed = await writeExclusive(stepCreatedLockPath, '');
|
|
854
|
+
if (!stepCreatedClaimed) {
|
|
855
|
+
throw new EntityConflictError(`Step "${data.correlationId}" already created`);
|
|
856
|
+
}
|
|
857
|
+
const stepData = data.eventData;
|
|
858
|
+
step = {
|
|
859
|
+
runId: effectiveRunId,
|
|
860
|
+
stepId: data.correlationId,
|
|
861
|
+
stepName: stepData.stepName,
|
|
862
|
+
status: 'pending',
|
|
863
|
+
input: stepData.input,
|
|
377
864
|
output: undefined,
|
|
378
865
|
error: undefined,
|
|
379
|
-
|
|
866
|
+
attempt: 0,
|
|
867
|
+
startedAt: undefined,
|
|
868
|
+
completedAt: undefined,
|
|
869
|
+
createdAt: now,
|
|
380
870
|
updatedAt: now,
|
|
871
|
+
// Propagate specVersion from the event to the step entity
|
|
872
|
+
specVersion: effectiveSpecVersion,
|
|
381
873
|
};
|
|
382
|
-
|
|
383
|
-
await
|
|
384
|
-
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
385
|
-
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
386
|
-
]);
|
|
874
|
+
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
875
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step);
|
|
387
876
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
877
|
+
else if (data.eventType === 'step_started') {
|
|
878
|
+
// step_started: Increments attempt, sets status to 'running'
|
|
879
|
+
// Sets startedAt only on the first start (not updated on retries)
|
|
880
|
+
// Reuse validatedStep from validation (already read above)
|
|
881
|
+
// Lazy step start: no prior step_created — create the step entity
|
|
882
|
+
// and a synthetic step_created event now, then fall through to the
|
|
883
|
+
// start transition below. Mirrors the resilient run_started path:
|
|
884
|
+
// the step entity is claimed atomically (first writer wins) and the
|
|
885
|
+
// synthetic step_created event keeps replay correct (the client step
|
|
886
|
+
// consumer marks hasCreatedEvent only when it observes that event).
|
|
887
|
+
if (!validatedStep && lazyStepStart) {
|
|
888
|
+
const lazyData = data.eventData;
|
|
889
|
+
const stepCreatedLockName = tag
|
|
890
|
+
? `${effectiveRunId}-${data.correlationId}.created.${tag}`
|
|
891
|
+
: `${effectiveRunId}-${data.correlationId}.created`;
|
|
892
|
+
const stepCreatedLockPath = resolveWithinBase(basedir, '.locks', 'steps', stepCreatedLockName);
|
|
893
|
+
const stepCreatedClaimed = await writeExclusive(stepCreatedLockPath, '');
|
|
894
|
+
if (!stepCreatedClaimed) {
|
|
895
|
+
// A concurrent handler already claimed the create for this
|
|
896
|
+
// step. The atomic claim is the exactly-once ownership gate:
|
|
897
|
+
// only the winner runs the step body inline. Throw
|
|
898
|
+
// EntityConflictError — the runtime's executeStep maps this to
|
|
899
|
+
// `skipped`, so the loser does not start or run the step. This
|
|
900
|
+
// preserves the same "exactly one handler owns each step"
|
|
901
|
+
// guarantee the separate step_created claim provides today.
|
|
902
|
+
throw new EntityConflictError(`Step "${data.correlationId}" already created`);
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
const createdStep = {
|
|
906
|
+
runId: effectiveRunId,
|
|
907
|
+
stepId: data.correlationId,
|
|
908
|
+
stepName: lazyData.stepName,
|
|
909
|
+
status: 'pending',
|
|
910
|
+
input: lazyData.input,
|
|
911
|
+
output: undefined,
|
|
912
|
+
error: undefined,
|
|
913
|
+
attempt: 0,
|
|
914
|
+
startedAt: undefined,
|
|
915
|
+
completedAt: undefined,
|
|
916
|
+
createdAt: now,
|
|
917
|
+
updatedAt: now,
|
|
918
|
+
specVersion: effectiveSpecVersion,
|
|
919
|
+
};
|
|
920
|
+
await writeJSON(taggedPath(basedir, 'steps', `${effectiveRunId}-${data.correlationId}`, tag), createdStep);
|
|
921
|
+
// Write the synthetic step_created event so replay observes it
|
|
922
|
+
// (the client step consumer sets hasCreatedEvent only on a
|
|
923
|
+
// step_created event). Its eventId is a fresh monotonic ULID.
|
|
924
|
+
// Ordering vs. the step_started event row does not affect
|
|
925
|
+
// correctness: the step_started consumer is a no-op and only
|
|
926
|
+
// step_created flips hasCreatedEvent, so the end state is the
|
|
927
|
+
// same whichever sorts first — this matches the resilient
|
|
928
|
+
// run_started → run_created precedent in this file.
|
|
929
|
+
const stepCreatedEventId = `evnt_${monotonicUlid()}`;
|
|
930
|
+
const stepCreatedEvent = {
|
|
931
|
+
eventType: 'step_created',
|
|
932
|
+
runId: effectiveRunId,
|
|
933
|
+
eventId: stepCreatedEventId,
|
|
934
|
+
createdAt: now,
|
|
935
|
+
specVersion: effectiveSpecVersion,
|
|
936
|
+
correlationId: data.correlationId,
|
|
937
|
+
eventData: {
|
|
938
|
+
stepName: lazyData.stepName,
|
|
939
|
+
input: lazyData.input,
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
await writeJSON(taggedPath(basedir, 'events', `${effectiveRunId}-${stepCreatedEventId}`, tag), stepCreatedEvent);
|
|
943
|
+
validatedStep = createdStep;
|
|
944
|
+
stepCreatedLazily = true;
|
|
945
|
+
}
|
|
425
946
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
947
|
+
if (validatedStep) {
|
|
948
|
+
// Check if retryAfter timestamp hasn't been reached yet
|
|
949
|
+
if (validatedStep.retryAfter &&
|
|
950
|
+
validatedStep.retryAfter.getTime() > Date.now()) {
|
|
951
|
+
throw new TooEarlyError(`Cannot start step "${data.correlationId}": retryAfter timestamp has not been reached yet`, {
|
|
952
|
+
retryAfter: Math.ceil((validatedStep.retryAfter.getTime() - Date.now()) / 1000),
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
// Best-effort guard: re-read the step entity to check if it
|
|
956
|
+
// reached terminal state between the validation read and now.
|
|
957
|
+
// This narrows the TOCTOU window but does not fully eliminate it
|
|
958
|
+
// (the local world is single-process / dev-only; the postgres
|
|
959
|
+
// world uses SQL-level atomic guards for production).
|
|
960
|
+
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
961
|
+
const freshStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
|
|
962
|
+
if (freshStep && isStepTerminal(freshStep.status)) {
|
|
963
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${freshStep.status}"`);
|
|
964
|
+
}
|
|
965
|
+
step = {
|
|
966
|
+
...validatedStep,
|
|
967
|
+
status: 'running',
|
|
968
|
+
// Only set startedAt on the first start
|
|
969
|
+
startedAt: validatedStep.startedAt ?? now,
|
|
970
|
+
// Increment attempt counter on every start
|
|
971
|
+
attempt: validatedStep.attempt + 1,
|
|
972
|
+
// Clear retryAfter now that the step has started
|
|
973
|
+
retryAfter: undefined,
|
|
974
|
+
updatedAt: now,
|
|
975
|
+
};
|
|
976
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
435
977
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
978
|
+
}
|
|
979
|
+
else if (data.eventType === 'step_completed' && 'eventData' in data) {
|
|
980
|
+
// step_completed: Terminal state with output
|
|
981
|
+
// Uses writeExclusive on a lock file to atomically prevent concurrent
|
|
982
|
+
// invocations from both completing the same step (TOCTOU race).
|
|
983
|
+
const completedData = data.eventData;
|
|
984
|
+
if (validatedStep) {
|
|
985
|
+
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
986
|
+
const lockName = tag
|
|
987
|
+
? `${stepCompositeKey}.terminal.${tag}`
|
|
988
|
+
: `${stepCompositeKey}.terminal`;
|
|
989
|
+
const terminalLockPath = resolveWithinBase(basedir, '.locks', 'steps', lockName);
|
|
990
|
+
const claimed = await writeExclusive(terminalLockPath, '');
|
|
991
|
+
if (!claimed) {
|
|
992
|
+
throw new EntityConflictError('Cannot modify step in terminal state');
|
|
993
|
+
}
|
|
994
|
+
step = {
|
|
995
|
+
...validatedStep,
|
|
996
|
+
status: 'completed',
|
|
997
|
+
output: completedData.result,
|
|
998
|
+
completedAt: now,
|
|
999
|
+
updatedAt: now,
|
|
1000
|
+
};
|
|
1001
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
else if (data.eventType === 'step_failed' && 'eventData' in data) {
|
|
1005
|
+
// step_failed: Terminal state with error
|
|
1006
|
+
// Uses writeExclusive on a lock file to atomically prevent concurrent
|
|
1007
|
+
// invocations from both failing the same step (TOCTOU race).
|
|
1008
|
+
const failedData = data.eventData;
|
|
1009
|
+
if (validatedStep) {
|
|
1010
|
+
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
1011
|
+
const lockName = tag
|
|
1012
|
+
? `${stepCompositeKey}.terminal.${tag}`
|
|
1013
|
+
: `${stepCompositeKey}.terminal`;
|
|
1014
|
+
const terminalLockPath = resolveWithinBase(basedir, '.locks', 'steps', lockName);
|
|
1015
|
+
const claimed = await writeExclusive(terminalLockPath, '');
|
|
1016
|
+
if (!claimed) {
|
|
1017
|
+
throw new EntityConflictError('Cannot modify step in terminal state');
|
|
1018
|
+
}
|
|
1019
|
+
// The error field is SerializedData (Uint8Array) produced by
|
|
1020
|
+
// dehydrateStepError. We store it verbatim — consumers hydrate it
|
|
1021
|
+
// via hydrateStepError to reconstruct the original thrown value.
|
|
1022
|
+
step = {
|
|
1023
|
+
...validatedStep,
|
|
1024
|
+
status: 'failed',
|
|
1025
|
+
error: failedData.error,
|
|
1026
|
+
completedAt: now,
|
|
1027
|
+
updatedAt: now,
|
|
1028
|
+
};
|
|
1029
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
else if (data.eventType === 'step_retrying' && 'eventData' in data) {
|
|
1033
|
+
// step_retrying: Sets status back to 'pending', records error
|
|
1034
|
+
// Reuse validatedStep from validation (already read above)
|
|
1035
|
+
const retryData = data.eventData;
|
|
1036
|
+
if (validatedStep) {
|
|
1037
|
+
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
1038
|
+
step = {
|
|
1039
|
+
...validatedStep,
|
|
1040
|
+
status: 'pending',
|
|
1041
|
+
error: retryData.error,
|
|
1042
|
+
retryAfter: retryData.retryAfter,
|
|
1043
|
+
updatedAt: now,
|
|
1044
|
+
};
|
|
1045
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
else if (
|
|
1049
|
+
// Hook lifecycle events
|
|
1050
|
+
data.eventType === 'hook_created' &&
|
|
1051
|
+
'eventData' in data) {
|
|
1052
|
+
const hookData = data.eventData;
|
|
1053
|
+
// Atomically claim the token using an exclusive-create constraint file.
|
|
1054
|
+
// This avoids the TOCTOU race of the previous read-all-then-check approach.
|
|
1055
|
+
const constraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(hookData.token)}.json`);
|
|
1056
|
+
// Persist `eventId` in the claim so concurrent / cross-
|
|
1057
|
+
// process retries can converge on a single canonical
|
|
1058
|
+
// `hook_created` event path. See the recovery comment
|
|
1059
|
+
// below.
|
|
1060
|
+
const tokenClaimed = await writeExclusive(constraintPath, JSON.stringify({
|
|
1061
|
+
token: hookData.token,
|
|
1062
|
+
hookId: data.correlationId,
|
|
1063
|
+
runId: effectiveRunId,
|
|
1064
|
+
eventId,
|
|
1065
|
+
}));
|
|
1066
|
+
// Recovery shape: the durable record of a successful hook
|
|
1067
|
+
// creation is the `hook_created` event in the event log. The
|
|
1068
|
+
// claim file and hook entity are written before the event,
|
|
1069
|
+
// and the three writes are NOT atomic, so a crash at any
|
|
1070
|
+
// point can leave one or two of them on disk without the
|
|
1071
|
+
// event. Treating those as "completed" would have the
|
|
1072
|
+
// suspension handler swallow the retry and permanently lose
|
|
1073
|
+
// the `hook_created` event from the log.
|
|
1074
|
+
//
|
|
1075
|
+
// When the dedup branch fires for the same `(runId, hookId)`,
|
|
1076
|
+
// we converge on the canonical `eventId` persisted in the
|
|
1077
|
+
// claim file by the original (winning) `writeExclusive`. By
|
|
1078
|
+
// adopting that eventId for this retry's event write — and
|
|
1079
|
+
// letting the outer no-overwrite `writeJSON` for the event
|
|
1080
|
+
// throw `EntityConflictError` on collision — concurrent /
|
|
1081
|
+
// cross-process workers either:
|
|
1082
|
+
// - publish the same event at the same path exactly once
|
|
1083
|
+
// (the loser's `writeJSON` throws EntityConflictError,
|
|
1084
|
+
// which the runtime's existing concurrent-replay catch
|
|
1085
|
+
// path at suspension-handler.ts:142 swallows), or
|
|
1086
|
+
// - converge on a single recovery write when the prior
|
|
1087
|
+
// claim was orphaned by a crash before the event landed.
|
|
1088
|
+
//
|
|
1089
|
+
// The legacy fallback (`existingClaim.eventId` undefined)
|
|
1090
|
+
// is for claim files written before this field was added —
|
|
1091
|
+
// those probe the event log directly and fall through to a
|
|
1092
|
+
// fresh-eventId recovery write. The legacy path does not
|
|
1093
|
+
// converge across workers but cannot regress behavior for
|
|
1094
|
+
// freshly-written claims.
|
|
1095
|
+
//
|
|
1096
|
+
// The `withHookLock` in-process mutex above keeps two same-
|
|
1097
|
+
// tick in-process callers from racing into this branch with
|
|
1098
|
+
// the winner mid-write, but is not sufficient across
|
|
1099
|
+
// processes — the durable convergence key (`claim.eventId`)
|
|
1100
|
+
// is what closes the cross-process race.
|
|
1101
|
+
let writeHookEntityWithOverwrite = false;
|
|
1102
|
+
if (!tokenClaimed) {
|
|
1103
|
+
const existingClaim = await readHookTokenClaim(constraintPath);
|
|
1104
|
+
if (existingClaim?.runId === effectiveRunId &&
|
|
1105
|
+
existingClaim.hookId === data.correlationId) {
|
|
1106
|
+
// Adopt a canonical eventId for the recovery write. The
|
|
1107
|
+
// outer event publish (`writeExclusive(eventPath)`)
|
|
1108
|
+
// either succeeds (we publish the canonical event,
|
|
1109
|
+
// repairing a partial write left by the original
|
|
1110
|
+
// claimant) or returns `false` and we throw
|
|
1111
|
+
// `EntityConflictError` (the event was already
|
|
1112
|
+
// published — a real duplicate). Either way the log
|
|
1113
|
+
// ends with exactly one `hook_created` event for this
|
|
1114
|
+
// `(runId, hookId)`.
|
|
1115
|
+
//
|
|
1116
|
+
// The canonical eventId comes from one of two places:
|
|
1117
|
+
//
|
|
1118
|
+
// - `existingClaim.eventId` for claims written by
|
|
1119
|
+
// this version (the writer above persists the
|
|
1120
|
+
// candidate eventId atomically with the claim).
|
|
1121
|
+
// The eventId is durable, so the outer
|
|
1122
|
+
// `writeExclusive(eventPath)` alone is enough to
|
|
1123
|
+
// arbitrate publication: it fails iff the event
|
|
1124
|
+
// was already published at that exact path.
|
|
1125
|
+
//
|
|
1126
|
+
// - The recovery-marker sidecar for legacy claims
|
|
1127
|
+
// written before `eventId` was persisted inline
|
|
1128
|
+
// in the claim. The marker is itself a
|
|
1129
|
+
// `writeExclusive`, so the first retry pins its
|
|
1130
|
+
// candidate eventId as canonical and subsequent
|
|
1131
|
+
// retries adopt it. Without this, two processes
|
|
1132
|
+
// both reading the same legacy claim would each
|
|
1133
|
+
// generate their own eventId, land their
|
|
1134
|
+
// `writeExclusive(eventPath)` calls at different
|
|
1135
|
+
// paths, and append two events.
|
|
1136
|
+
//
|
|
1137
|
+
// For legacy claims we also must probe the event
|
|
1138
|
+
// log for an existing `hook_created` event BEFORE
|
|
1139
|
+
// pinning a canonical eventId: the pre-upgrade
|
|
1140
|
+
// writer may have already published the event
|
|
1141
|
+
// with its own eventId, and the marker has no way
|
|
1142
|
+
// of knowing that eventId after the fact. Without
|
|
1143
|
+
// this probe, a post-upgrade retry would pin a
|
|
1144
|
+
// different eventId, write a hook entity, and
|
|
1145
|
+
// publish a duplicate event at the marker's path.
|
|
1146
|
+
let canonicalEventId;
|
|
1147
|
+
if (existingClaim.eventId) {
|
|
1148
|
+
canonicalEventId = existingClaim.eventId;
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
const alreadyPublishedEventId = await findExistingHookCreatedEventId(basedir, effectiveRunId, data.correlationId);
|
|
1152
|
+
if (alreadyPublishedEventId !== null) {
|
|
1153
|
+
// The pre-upgrade writer may have crashed between
|
|
1154
|
+
// its event publish and its hook entity write —
|
|
1155
|
+
// repair the entity from the persisted event's
|
|
1156
|
+
// payload before surfacing the benign duplicate.
|
|
1157
|
+
await repairHookEntityFromPersistedEvent(basedir, effectiveRunId, data.correlationId, alreadyPublishedEventId, tag);
|
|
1158
|
+
throw new EntityConflictError(`Hook "${data.correlationId}" already created`);
|
|
1159
|
+
}
|
|
1160
|
+
const pinned = await pinCanonicalEventIdForLegacyClaim(basedir, hookData.token, effectiveRunId, data.correlationId, eventId);
|
|
1161
|
+
if (pinned === null) {
|
|
1162
|
+
// Lost the marker race and the marker file is
|
|
1163
|
+
// unreadable (extremely rare; corrupted disk).
|
|
1164
|
+
// Treat as a real duplicate so the runtime's
|
|
1165
|
+
// concurrent-replay catch path swallows this
|
|
1166
|
+
// attempt instead of risking divergent
|
|
1167
|
+
// publication.
|
|
1168
|
+
throw new EntityConflictError(`Hook "${data.correlationId}" already created`);
|
|
1169
|
+
}
|
|
1170
|
+
canonicalEventId = pinned;
|
|
1171
|
+
}
|
|
1172
|
+
// Rebuild `event` with the canonical eventId and a
|
|
1173
|
+
// deterministic `createdAt` derived from the eventId
|
|
1174
|
+
// (a ULID) so two workers writing the same event
|
|
1175
|
+
// produce byte-identical content.
|
|
1176
|
+
eventId = canonicalEventId;
|
|
1177
|
+
const canonicalCreatedAt = ulidToDate(eventId.replace(/^evnt_/, '')) ?? now;
|
|
1178
|
+
event = {
|
|
1179
|
+
...data,
|
|
1180
|
+
runId: effectiveRunId,
|
|
1181
|
+
eventId,
|
|
1182
|
+
createdAt: canonicalCreatedAt,
|
|
1183
|
+
specVersion: effectiveSpecVersion,
|
|
1184
|
+
};
|
|
1185
|
+
writeHookEntityWithOverwrite = true;
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
// Cross-hook / cross-run conflict: a different
|
|
1189
|
+
// (runId, hookId) holds this token. Create a
|
|
1190
|
+
// hook_conflict event so the workflow can fail
|
|
1191
|
+
// gracefully when the hook is awaited.
|
|
1192
|
+
const conflictEvent = {
|
|
1193
|
+
eventType: 'hook_conflict',
|
|
1194
|
+
correlationId: data.correlationId,
|
|
1195
|
+
eventData: {
|
|
1196
|
+
token: hookData.token,
|
|
1197
|
+
...(existingClaim
|
|
1198
|
+
? { conflictingRunId: existingClaim.runId }
|
|
1199
|
+
: {}),
|
|
1200
|
+
},
|
|
1201
|
+
runId: effectiveRunId,
|
|
1202
|
+
eventId,
|
|
1203
|
+
createdAt: now,
|
|
1204
|
+
specVersion: effectiveSpecVersion,
|
|
1205
|
+
};
|
|
1206
|
+
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
1207
|
+
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), conflictEvent);
|
|
1208
|
+
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
1209
|
+
const filteredEvent = stripEventDataRefs(conflictEvent, resolveData);
|
|
1210
|
+
// Return EventResult with conflict event (no hook entity created)
|
|
1211
|
+
return {
|
|
1212
|
+
event: filteredEvent,
|
|
1213
|
+
run,
|
|
1214
|
+
step,
|
|
1215
|
+
hook: undefined,
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
// Compute the hook entity now, but defer its write until
|
|
1220
|
+
// AFTER the outer event publish at the bottom of this
|
|
1221
|
+
// function commits. The retry path can reach this branch
|
|
1222
|
+
// for a hook whose `hook_created` event was already
|
|
1223
|
+
// published successfully (an "already-committed
|
|
1224
|
+
// duplicate"); in that case the outer `writeExclusive`
|
|
1225
|
+
// for the event will return false and we will throw
|
|
1226
|
+
// `EntityConflictError`. Writing the hook entity here
|
|
1227
|
+
// first would mutate already-committed durable state with
|
|
1228
|
+
// the retry's payload (e.g. different `metadata` or
|
|
1229
|
+
// `isWebhook`) before the event publish proved which
|
|
1230
|
+
// outcome we are in — leaving the entity and event log
|
|
1231
|
+
// inconsistent. By deferring, the entity is only written
|
|
1232
|
+
// when the publish actually succeeds (first writer or
|
|
1233
|
+
// orphan recovery). See pranaygp's review on PR #2295 for
|
|
1234
|
+
// the karthikscale3 repro.
|
|
1235
|
+
hook = {
|
|
1236
|
+
runId: effectiveRunId,
|
|
1237
|
+
hookId: data.correlationId,
|
|
1238
|
+
token: hookData.token,
|
|
1239
|
+
metadata: hookData.metadata,
|
|
1240
|
+
ownerId: 'local-owner',
|
|
1241
|
+
projectId: 'local-project',
|
|
1242
|
+
environment: 'local',
|
|
1243
|
+
// Use the (possibly canonical) event's createdAt so two
|
|
1244
|
+
// workers writing the same hook entity produce byte-
|
|
1245
|
+
// identical content during convergence.
|
|
1246
|
+
createdAt: event.createdAt,
|
|
1247
|
+
// Propagate specVersion from the event to the hook entity
|
|
1248
|
+
specVersion: effectiveSpecVersion,
|
|
1249
|
+
isWebhook: hookData.isWebhook ?? false,
|
|
1250
|
+
isSystem: hookData.isSystem ?? false,
|
|
446
1251
|
};
|
|
447
|
-
|
|
1252
|
+
hookEntityWriteOptions = writeHookEntityWithOverwrite
|
|
1253
|
+
? { overwrite: true }
|
|
1254
|
+
: undefined;
|
|
448
1255
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const
|
|
457
|
-
const
|
|
458
|
-
? `${stepCompositeKey}.terminal.${tag}`
|
|
459
|
-
: `${stepCompositeKey}.terminal`;
|
|
460
|
-
const terminalLockPath = path.join(basedir, '.locks', 'steps', lockName);
|
|
461
|
-
const claimed = await writeExclusive(terminalLockPath, '');
|
|
1256
|
+
else if (data.eventType === 'hook_disposed') {
|
|
1257
|
+
// hook_disposed: Deletes hook entity, rejects duplicates.
|
|
1258
|
+
// Uses writeExclusive on a lock file to atomically prevent concurrent
|
|
1259
|
+
// invocations from both disposing the same hook (TOCTOU race).
|
|
1260
|
+
const hookLockName = tag
|
|
1261
|
+
? `${data.correlationId}.disposed.${tag}`
|
|
1262
|
+
: `${data.correlationId}.disposed`;
|
|
1263
|
+
const lockPath = resolveWithinBase(basedir, '.locks', 'hooks', hookLockName);
|
|
1264
|
+
const claimed = await writeExclusive(lockPath, '');
|
|
462
1265
|
if (!claimed) {
|
|
463
|
-
throw new EntityConflictError(
|
|
1266
|
+
throw new EntityConflictError(`Hook "${data.correlationId}" already disposed`);
|
|
464
1267
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
1268
|
+
// Read the hook to get its token before deleting
|
|
1269
|
+
const hookPath = taggedPath(basedir, 'hooks', data.correlationId, tag);
|
|
1270
|
+
const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
|
|
1271
|
+
if (existingHook) {
|
|
1272
|
+
// Delete the token constraint file to free up the token
|
|
1273
|
+
// for reuse, and delete this hook's recovery marker (if
|
|
1274
|
+
// any) for disk hygiene. The marker's filename hash
|
|
1275
|
+
// includes `(token, runId, hookId)` so different
|
|
1276
|
+
// lifetimes never collide, but cleaning up reduces disk
|
|
1277
|
+
// leak for hooks that go through the recovery path.
|
|
1278
|
+
const disposedConstraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(existingHook.token)}.json`);
|
|
1279
|
+
await deleteJSON(disposedConstraintPath);
|
|
1280
|
+
await deleteJSON(hookRecoveryMarkerPath(basedir, existingHook.token, existingHook.runId, existingHook.hookId));
|
|
1281
|
+
}
|
|
1282
|
+
await deleteJSON(hookPath);
|
|
1283
|
+
}
|
|
1284
|
+
else if (data.eventType === 'wait_created' && 'eventData' in data) {
|
|
1285
|
+
// wait_created: Creates wait entity with status 'waiting'.
|
|
1286
|
+
// Atomic claim on a per-(runId, correlationId) constraint file
|
|
1287
|
+
// ensures duplicate wait_created from concurrent invocations
|
|
1288
|
+
// surfaces as EntityConflictError (replaces a prior TOCTOU
|
|
1289
|
+
// read-then-check that could let both writers through).
|
|
1290
|
+
const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
1291
|
+
const waitCreatedLockName = tag
|
|
1292
|
+
? `${waitCompositeKey}.created.${tag}`
|
|
1293
|
+
: `${waitCompositeKey}.created`;
|
|
1294
|
+
const waitCreatedLockPath = resolveWithinBase(basedir, '.locks', 'waits', waitCreatedLockName);
|
|
1295
|
+
const waitCreatedClaimed = await writeExclusive(waitCreatedLockPath, '');
|
|
1296
|
+
if (!waitCreatedClaimed) {
|
|
1297
|
+
throw new EntityConflictError(`Wait "${data.correlationId}" already exists`);
|
|
1298
|
+
}
|
|
1299
|
+
const waitData = data.eventData;
|
|
1300
|
+
wait = {
|
|
1301
|
+
waitId: waitCompositeKey,
|
|
1302
|
+
runId: effectiveRunId,
|
|
1303
|
+
status: 'waiting',
|
|
1304
|
+
resumeAt: waitData.resumeAt,
|
|
1305
|
+
completedAt: undefined,
|
|
1306
|
+
createdAt: now,
|
|
470
1307
|
updatedAt: now,
|
|
1308
|
+
specVersion: effectiveSpecVersion,
|
|
471
1309
|
};
|
|
472
|
-
await writeJSON(taggedPath(basedir, '
|
|
1310
|
+
await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait);
|
|
473
1311
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
: `${stepCompositeKey}.terminal`;
|
|
485
|
-
const terminalLockPath = path.join(basedir, '.locks', 'steps', lockName);
|
|
486
|
-
const claimed = await writeExclusive(terminalLockPath, '');
|
|
1312
|
+
else if (data.eventType === 'wait_completed') {
|
|
1313
|
+
// wait_completed: Transitions wait to 'completed', rejects duplicates.
|
|
1314
|
+
// Uses writeExclusive on a lock file to atomically prevent concurrent
|
|
1315
|
+
// invocations from both completing the same wait (TOCTOU race).
|
|
1316
|
+
const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
1317
|
+
const waitLockName = tag
|
|
1318
|
+
? `${waitCompositeKey}.completed.${tag}`
|
|
1319
|
+
: `${waitCompositeKey}.completed`;
|
|
1320
|
+
const lockPath = resolveWithinBase(basedir, '.locks', 'waits', waitLockName);
|
|
1321
|
+
const claimed = await writeExclusive(lockPath, '');
|
|
487
1322
|
if (!claimed) {
|
|
488
|
-
throw new EntityConflictError(
|
|
1323
|
+
throw new EntityConflictError(`Wait "${data.correlationId}" already completed`);
|
|
489
1324
|
}
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
1325
|
+
const existingWait = await readJSONWithFallback(basedir, 'waits', waitCompositeKey, WaitSchema, tag);
|
|
1326
|
+
if (!existingWait) {
|
|
1327
|
+
// Clean up the lock file we just claimed — the wait doesn't exist
|
|
1328
|
+
await fs.unlink(lockPath).catch(() => { });
|
|
1329
|
+
throw new WorkflowWorldError(`Wait "${data.correlationId}" not found`);
|
|
1330
|
+
}
|
|
1331
|
+
// The lock file (writeExclusive above) already prevents concurrent
|
|
1332
|
+
// completions — no additional status check needed.
|
|
1333
|
+
wait = {
|
|
1334
|
+
...existingWait,
|
|
1335
|
+
status: 'completed',
|
|
500
1336
|
completedAt: now,
|
|
501
1337
|
updatedAt: now,
|
|
502
1338
|
};
|
|
503
|
-
await writeJSON(taggedPath(basedir, '
|
|
1339
|
+
await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait, { overwrite: true });
|
|
504
1340
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
//
|
|
508
|
-
//
|
|
509
|
-
|
|
510
|
-
if
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
1341
|
+
// Note: hook_received events are stored in the event log but don't
|
|
1342
|
+
// modify the Hook entity (which doesn't have a payload field)
|
|
1343
|
+
// Store event using composite key {runId}-{eventId}.
|
|
1344
|
+
//
|
|
1345
|
+
// `writeExclusive` (O_CREAT|O_EXCL via temp-file + hard-link)
|
|
1346
|
+
// is the cross-process atomic publish primitive: if the file
|
|
1347
|
+
// already exists, returns false instead of overwriting. This
|
|
1348
|
+
// is critical for the hook_created dedup-recovery convergence
|
|
1349
|
+
// (above) — two workers that adopt the same canonical eventId
|
|
1350
|
+
// race here; whoever links the file first wins, the loser
|
|
1351
|
+
// throws EntityConflictError, and the runtime's existing
|
|
1352
|
+
// concurrent-replay catch path at suspension-handler.ts:142
|
|
1353
|
+
// swallows it. For all other event types, eventIds are
|
|
1354
|
+
// monotonic ULIDs (globally unique by construction) so a
|
|
1355
|
+
// collision indicates a real bug and EntityConflictError is
|
|
1356
|
+
// also the right surface — same shape as step_created's
|
|
1357
|
+
// claim-file behavior.
|
|
1358
|
+
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
1359
|
+
const eventPublished = await writeExclusive(taggedPath(basedir, 'events', compositeKey, tag), JSON.stringify(event, jsonReplacer, 2));
|
|
1360
|
+
if (!eventPublished) {
|
|
1361
|
+
// For `hook_created`, losing the event publish means the
|
|
1362
|
+
// event was already committed at this exact (canonical)
|
|
1363
|
+
// path. The original publisher may have crashed between
|
|
1364
|
+
// its event publish and its deferred hook-entity write
|
|
1365
|
+
// (the inverse of the crash window the deferral closes),
|
|
1366
|
+
// leaving an event-first orphan: the event is in the log
|
|
1367
|
+
// but the entity is missing and the hook is unresolvable.
|
|
1368
|
+
// Repair the entity from the PERSISTED event's payload
|
|
1369
|
+
// (never the retry's — different retry metadata must not
|
|
1370
|
+
// change committed state) before surfacing the benign
|
|
1371
|
+
// duplicate to the runtime's concurrent-replay catch path.
|
|
1372
|
+
if (data.eventType === 'hook_created' && data.correlationId) {
|
|
1373
|
+
await repairHookEntityFromPersistedEvent(basedir, effectiveRunId, data.correlationId, eventId, tag);
|
|
1374
|
+
}
|
|
1375
|
+
throw new EntityConflictError(`Event "${eventId}" already exists for run "${effectiveRunId}"`);
|
|
525
1376
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
token: hookData.token,
|
|
537
|
-
hookId: data.correlationId,
|
|
538
|
-
runId: effectiveRunId,
|
|
539
|
-
}));
|
|
540
|
-
if (!tokenClaimed) {
|
|
541
|
-
// Create hook_conflict event instead of hook_created
|
|
542
|
-
// This allows the workflow to continue and fail gracefully when the hook is awaited
|
|
543
|
-
const conflictEvent = {
|
|
544
|
-
eventType: 'hook_conflict',
|
|
545
|
-
correlationId: data.correlationId,
|
|
546
|
-
eventData: {
|
|
547
|
-
token: hookData.token,
|
|
548
|
-
},
|
|
549
|
-
runId: effectiveRunId,
|
|
550
|
-
eventId,
|
|
551
|
-
createdAt: now,
|
|
552
|
-
specVersion: effectiveSpecVersion,
|
|
553
|
-
};
|
|
554
|
-
// Store the conflict event
|
|
555
|
-
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
556
|
-
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), conflictEvent);
|
|
557
|
-
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
558
|
-
const filteredEvent = stripEventDataRefs(conflictEvent, resolveData);
|
|
559
|
-
// Return EventResult with conflict event (no hook entity created)
|
|
560
|
-
return {
|
|
561
|
-
event: filteredEvent,
|
|
562
|
-
run,
|
|
563
|
-
step,
|
|
564
|
-
hook: undefined,
|
|
565
|
-
};
|
|
1377
|
+
// Write the hook entity ONLY now that the event publish has
|
|
1378
|
+
// committed. Doing this earlier (in the `hook_created`
|
|
1379
|
+
// branch above) would mutate an already-committed hook
|
|
1380
|
+
// entity with the retry's payload before the event publish
|
|
1381
|
+
// proved whether this attempt was repairing a missing event
|
|
1382
|
+
// or just colliding with an already-published `hook_created`.
|
|
1383
|
+
// The branch sets `hookEntityWriteOptions` iff this event
|
|
1384
|
+
// type writes an entity.
|
|
1385
|
+
if (hook && data.eventType === 'hook_created') {
|
|
1386
|
+
await writeJSON(taggedPath(basedir, 'hooks', hook.hookId, tag), hook, hookEntityWriteOptions);
|
|
566
1387
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
//
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
completedAt: now,
|
|
649
|
-
updatedAt: now,
|
|
1388
|
+
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
1389
|
+
const filteredEvent = stripEventDataRefs(event, resolveData);
|
|
1390
|
+
// For run_started: preload one page of events so the runtime can skip
|
|
1391
|
+
// the initial events.list call when hasMore is false.
|
|
1392
|
+
let events;
|
|
1393
|
+
let cursor;
|
|
1394
|
+
let hasMore;
|
|
1395
|
+
if (data.eventType === 'run_started' && run) {
|
|
1396
|
+
const allEvents = await paginatedFileSystemQuery({
|
|
1397
|
+
directory: path.join(basedir, 'events'),
|
|
1398
|
+
schema: EventSchema,
|
|
1399
|
+
filePrefix: `${effectiveRunId}-`,
|
|
1400
|
+
sortOrder: 'asc',
|
|
1401
|
+
limit: 1000,
|
|
1402
|
+
getCreatedAt: getObjectCreatedAt('evnt'),
|
|
1403
|
+
getId: (e) => e.eventId,
|
|
1404
|
+
});
|
|
1405
|
+
events = allEvents.data;
|
|
1406
|
+
cursor = allEvents.cursor;
|
|
1407
|
+
hasMore = allEvents.hasMore;
|
|
1408
|
+
}
|
|
1409
|
+
// Inline-delta optimization: on a step-terminal write the inline
|
|
1410
|
+
// runtime loop can pass `sinceCursor` (the cursor from before it
|
|
1411
|
+
// began writing this step's events). We return the delta of
|
|
1412
|
+
// events written strictly after that cursor — exactly what an
|
|
1413
|
+
// `events.list({ cursor: sinceCursor, sortOrder: 'asc' })` would
|
|
1414
|
+
// return right now — so the loop can skip a redundant round-trip.
|
|
1415
|
+
//
|
|
1416
|
+
// This is computed against the same on-disk log the list path
|
|
1417
|
+
// reads, so it captures everything the fetch would: this step's
|
|
1418
|
+
// step_created/step_started/step_completed, any attr_set the step
|
|
1419
|
+
// body wrote, and any in-band events (e.g. hook_received,
|
|
1420
|
+
// wait_completed) another writer appended since the cursor. That
|
|
1421
|
+
// equivalence is what makes skipping the fetch safe — a missed
|
|
1422
|
+
// in-band event cannot diverge replay because the delta is the
|
|
1423
|
+
// fetch.
|
|
1424
|
+
//
|
|
1425
|
+
// Only step-terminal events qualify: step_created/step_started are
|
|
1426
|
+
// not loop boundaries (the loop fetches after step_completed /
|
|
1427
|
+
// step_failed), and run-terminal events end the loop. `resolveData`
|
|
1428
|
+
// matches the list path so eventData refs are handled identically.
|
|
1429
|
+
if ((data.eventType === 'step_completed' ||
|
|
1430
|
+
data.eventType === 'step_failed') &&
|
|
1431
|
+
typeof params?.sinceCursor === 'string') {
|
|
1432
|
+
// Intentionally no `limit`: this returns a single default-size page,
|
|
1433
|
+
// unlike the `events.list` path which loops `while (hasMore)` to
|
|
1434
|
+
// exhaustion. That is safe — and must NOT be "fixed" by paginating
|
|
1435
|
+
// here — because the contract is single-page-or-fallback, not
|
|
1436
|
+
// complete-delta. When the delta overflows one page,
|
|
1437
|
+
// paginatedFileSystemQuery sets `hasMore: true` and slices `data` to
|
|
1438
|
+
// the page (see fs.ts), which we forward verbatim below. The SDK
|
|
1439
|
+
// consume side (runtime.ts) only stashes the delta when `!hasMore`
|
|
1440
|
+
// and otherwise falls back to the exhaustive `events.list` loop, so a
|
|
1441
|
+
// truncated page is never consumed as if it were the full delta.
|
|
1442
|
+
const delta = await paginatedFileSystemQuery({
|
|
1443
|
+
directory: path.join(basedir, 'events'),
|
|
1444
|
+
schema: EventSchema,
|
|
1445
|
+
filePrefix: `${effectiveRunId}-`,
|
|
1446
|
+
sortOrder: 'asc',
|
|
1447
|
+
cursor: params.sinceCursor,
|
|
1448
|
+
getCreatedAt: getObjectCreatedAt('evnt'),
|
|
1449
|
+
getId: (e) => e.eventId,
|
|
1450
|
+
});
|
|
1451
|
+
events =
|
|
1452
|
+
resolveData === 'none'
|
|
1453
|
+
? delta.data.map((e) => stripEventDataRefs(e, resolveData))
|
|
1454
|
+
: delta.data;
|
|
1455
|
+
cursor = delta.cursor;
|
|
1456
|
+
hasMore = delta.hasMore;
|
|
1457
|
+
}
|
|
1458
|
+
// Return EventResult with event and any created/updated entity
|
|
1459
|
+
return {
|
|
1460
|
+
event: filteredEvent,
|
|
1461
|
+
run,
|
|
1462
|
+
step,
|
|
1463
|
+
hook,
|
|
1464
|
+
wait,
|
|
1465
|
+
events,
|
|
1466
|
+
cursor,
|
|
1467
|
+
hasMore,
|
|
1468
|
+
...(stepCreatedLazily ? { stepCreated: true } : {}),
|
|
650
1469
|
};
|
|
651
|
-
|
|
652
|
-
}
|
|
653
|
-
// Note: hook_received events are stored in the event log but don't
|
|
654
|
-
// modify the Hook entity (which doesn't have a payload field)
|
|
655
|
-
// Store event using composite key {runId}-{eventId}
|
|
656
|
-
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
657
|
-
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
|
|
658
|
-
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
659
|
-
const filteredEvent = stripEventDataRefs(event, resolveData);
|
|
660
|
-
// For run_started: include all events so the runtime can skip
|
|
661
|
-
// the initial events.list call and reduce TTFB.
|
|
662
|
-
let events;
|
|
663
|
-
if (data.eventType === 'run_started' && run) {
|
|
664
|
-
const allEvents = await paginatedFileSystemQuery({
|
|
665
|
-
directory: path.join(basedir, 'events'),
|
|
666
|
-
schema: EventSchema,
|
|
667
|
-
filePrefix: `${effectiveRunId}-`,
|
|
668
|
-
sortOrder: 'asc',
|
|
669
|
-
getCreatedAt: getObjectCreatedAt('evnt'),
|
|
670
|
-
getId: (e) => e.eventId,
|
|
671
|
-
});
|
|
672
|
-
events = allEvents.data;
|
|
673
|
-
}
|
|
674
|
-
// Return EventResult with event and any created/updated entity
|
|
675
|
-
return {
|
|
676
|
-
event: filteredEvent,
|
|
677
|
-
run,
|
|
678
|
-
step,
|
|
679
|
-
hook,
|
|
680
|
-
wait,
|
|
681
|
-
events,
|
|
682
|
-
};
|
|
1470
|
+
} // end createImpl
|
|
683
1471
|
},
|
|
684
1472
|
async get(runId, eventId, params) {
|
|
1473
|
+
assertSafeEntityId('runId', runId);
|
|
1474
|
+
assertSafeEntityId('eventId', eventId);
|
|
685
1475
|
const compositeKey = `${runId}-${eventId}`;
|
|
686
1476
|
const event = await readJSONWithFallback(basedir, 'events', compositeKey, EventSchema, tag);
|
|
687
1477
|
if (!event) {
|
|
@@ -692,6 +1482,7 @@ export function createEventsStorage(basedir, tag) {
|
|
|
692
1482
|
},
|
|
693
1483
|
async list(params) {
|
|
694
1484
|
const { runId } = params;
|
|
1485
|
+
assertSafeEntityId('runId', runId);
|
|
695
1486
|
const resolveData = params.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
696
1487
|
const result = await paginatedFileSystemQuery({
|
|
697
1488
|
directory: path.join(basedir, 'events'),
|
|
@@ -716,6 +1507,7 @@ export function createEventsStorage(basedir, tag) {
|
|
|
716
1507
|
},
|
|
717
1508
|
async listByCorrelationId(params) {
|
|
718
1509
|
const correlationId = params.correlationId;
|
|
1510
|
+
assertSafeEntityId('correlationId', correlationId);
|
|
719
1511
|
const resolveData = params.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
720
1512
|
const result = await paginatedFileSystemQuery({
|
|
721
1513
|
directory: path.join(basedir, 'events'),
|
|
@@ -741,4 +1533,4 @@ export function createEventsStorage(basedir, tag) {
|
|
|
741
1533
|
},
|
|
742
1534
|
};
|
|
743
1535
|
}
|
|
744
|
-
//# sourceMappingURL=events-storage.js.map
|
|
1536
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRzLXN0b3JhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmFnZS9ldmVudHMtc3RvcmFnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsQyxPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixpQkFBaUIsRUFDakIsZUFBZSxFQUNmLG9CQUFvQixFQUNwQixhQUFhLEVBQ2Isd0JBQXdCLEVBQ3hCLGtCQUFrQixHQUNuQixNQUFNLGtCQUFrQixDQUFDO0FBVzFCLE9BQU8sRUFDTCxxQkFBcUIsRUFDckIsV0FBVyxFQUNYLFVBQVUsRUFDVixtQkFBbUIsRUFDbkIsa0JBQWtCLEVBQ2xCLG9CQUFvQixFQUNwQixVQUFVLEVBQ1YsVUFBVSxFQUNWLHdCQUF3QixFQUN4QixxQkFBcUIsRUFDckIsVUFBVSxFQUNWLGlCQUFpQixHQUNsQixNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFDeEIsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzNELE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsVUFBVSxFQUNWLFlBQVksRUFDWixhQUFhLEVBQ2Isd0JBQXdCLEVBQ3hCLFFBQVEsRUFDUixvQkFBb0IsRUFDcEIsaUJBQWlCLEVBQ2pCLFVBQVUsRUFDVixjQUFjLEVBQ2QsU0FBUyxHQUNWLE1BQU0sVUFBVSxDQUFDO0FBQ2xCLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNsRCxPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLFNBQVMsRUFDVCxzQkFBc0IsRUFDdEIsYUFBYSxHQUNkLE1BQU0sY0FBYyxDQUFDO0FBQ3RCLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzFELE9BQU8sRUFB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cGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHtcbiAgRW50aXR5Q29uZmxpY3RFcnJvcixcbiAgSG9va05vdEZvdW5kRXJyb3IsXG4gIFJ1bkV4cGlyZWRFcnJvcixcbiAgUnVuTm90U3VwcG9ydGVkRXJyb3IsXG4gIFRvb0Vhcmx5RXJyb3IsXG4gIFdvcmtmbG93UnVuTm90Rm91bmRFcnJvcixcbiAgV29ya2Zsb3dXb3JsZEVycm9yLFxufSBmcm9tICdAd29ya2Zsb3cvZXJyb3JzJztcbmltcG9ydCB0eXBlIHtcbiAgRXZlbnQsXG4gIEV2ZW50UmVzdWx0LFxuICBIb29rLFxuICBTZXJpYWxpemVkRGF0YSxcbiAgU3RlcCxcbiAgU3RvcmFnZSxcbiAgV2FpdCxcbiAgV29ya2Zsb3dSdW4sXG59IGZyb20gJ0B3b3JrZmxvdy93b3JsZCc7XG5pbXBvcnQge1xuICBhcHBseUF0dHJpYnV0ZUNoYW5nZXMsXG4gIEV2ZW50U2NoZW1hLFxuICBIb29rU2NoZW1hLFxuICBpc0xlZ2FjeVNwZWNWZXJzaW9uLFxuICByZXF1aXJlc05ld2VyV29ybGQsXG4gIFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICBTdGVwU2NoZW1hLFxuICB1bGlkVG9EYXRlLFxuICB2YWxpZGF0ZUF0dHJpYnV0ZUNoYW5nZXMsXG4gIHZhbGlkYXRlVWxpZFRpbWVzdGFtcCxcbiAgV2FpdFNjaGVtYSxcbiAgV29ya2Zsb3dSdW5TY2hlbWEsXG59IGZyb20gJ0B3b3JrZmxvdy93b3JsZCc7XG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcbmltcG9ydCB7IERFRkFVTFRfUkVTT0xWRV9EQVRBX09QVElPTiB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XG5pbXBvcnQge1xuICBhc3NlcnRTYWZlRW50aXR5SWQsXG4gIGRlbGV0ZUpTT04sXG4gIGpzb25SZXBsYWNlcixcbiAgbGlzdEpTT05GaWxlcyxcbiAgcGFnaW5hdGVkRmlsZVN5c3RlbVF1ZXJ5LFxuICByZWFkSlNPTixcbiAgcmVhZEpTT05XaXRoRmFsbGJhY2ssXG4gIHJlc29sdmVXaXRoaW5CYXNlLFxuICB0YWdnZWRQYXRoLFxuICB3cml0ZUV4Y2x1c2l2ZSxcbiAgd3JpdGVKU09OLFxufSBmcm9tICcuLi9mcy5qcyc7XG5pbXBvcnQgeyBzdHJpcEV2ZW50RGF0YVJlZnMgfSBmcm9tICcuL2ZpbHRlcnMuanMnO1xuaW1wb3J0IHtcbiAgZ2V0T2JqZWN0Q3JlYXRlZEF0LFxuICBoYXNoVG9rZW4sXG4gIGhvb2tSZWNvdmVyeU1hcmtlclBhdGgsXG4gIG1vbm90b25pY1VsaWQsXG59IGZyb20gJy4vaGVscGVycy5qcyc7XG5pbXBvcnQgeyBkZWxldGVBbGxIb29rc0ZvclJ1biB9IGZyb20gJy4vaG9va3Mtc3RvcmFnZS5qcyc7XG5pbXBvcnQgeyBoYW5kbGVMZWdhY3lFdmVudCB9IGZyb20gJy4vbGVnYWN5LmpzJztcbmltcG9ydCB7IHdpdGhSdW5GaWxlTG9jayB9IGZyb20gJy4vcnVucy1zdG9yYWdlLmpzJztcblxuLyoqXG4gKiBQZXItc3RlcCBpbi1wcm9jZXNzIGFzeW5jIG11dGV4LiBTZXJpYWxpemVzIGNvbmN1cnJlbnQgYGV2ZW50cy5jcmVhdGVgIGNhbGxzXG4gKiB0aGF0IHRhcmdldCB0aGUgc2FtZSBzdGVwLCBzbyB0aGF0IHRoZSBcImNoZWNrIHRlcm1pbmFsIHN0YXRlLCB0aGVuIHdyaXRlIHN0ZXBcbiAqIGVudGl0eSArIGV2ZW50XCIgc2VxdWVuY2UgaXMgYXRvbWljLiBXaXRob3V0IHRoaXMsIHR3byBjb25jdXJyZW50IHN0ZXBfc3RhcnRlZFxuICogY2FsbHMgY2FuIGJvdGggcGFzcyB0aGUgbm90LXRlcm1pbmFsIGNoZWNrIGFuZCBib3RoIHdyaXRlIHN0ZXBfc3RhcnRlZCBldmVudHNcbiAqIOKAlCBvciBhIHN0ZXBfc3RhcnRlZCBjYW4gbGFuZCBpbiB0aGUgbG9nIGFmdGVyIHN0ZXBfY29tcGxldGVkIGhhcyBhbHJlYWR5XG4gKiB3cml0dGVuLCBwcm9kdWNpbmcgdW5jb25zdW1lZCBldmVudHMgb24gcmVwbGF5LlxuICpcbiAqIER1cGxpY2F0ZSBzdGVwX3N0YXJ0ZWQgZXZlbnRzIGZvciBhIG5vbi10ZXJtaW5hbCBzdGVwIGFyZSBzdGlsbCBhbGxvd2VkXG4gKiAocmV0cmllcyBsZWdpdGltYXRlbHkgcmUtc3RhcnQgYSBzdGVwKSwgb25seSB3cml0ZXMgdG8gYW4gYWxyZWFkeS10ZXJtaW5hbFxuICogc3RlcCBhcmUgcmVqZWN0ZWQuXG4gKi9cbi8vIGBzdGVwTG9ja3NgIGFuZCBgaG9va0xvY2tzYCBhcmUgbm93IGluc3RhbnRpYXRlZCBwZXJcbi8vIGBjcmVhdGVFdmVudHNTdG9yYWdlYCBjYWxsIChzZWUgaW5zaWRlIHRoZSBmdW5jdGlvbikgcmF0aGVyIHRoYW5cbi8vIGJlaW5nIG1vZHVsZS1sZXZlbC4gVGhlIG9uLWRpc2sgY29uc3RyYWludCAvIGNsYWltIGZpbGVzIHJlbWFpblxuLy8gdGhlIGR1cmFibGUgc291cmNlIG9mIHRydXRoIGFjcm9zcyBwcm9jZXNzZXM7IHRoZSBpbi1wcm9jZXNzXG4vLyBtdXRleCBpcyBhIHBlci1pbnN0YW5jZSBvcHRpbWl6YXRpb24gdGhhdCBjbG9zZXMgYSBzaG9ydCByYWNlXG4vLyB3aW5kb3cgaW4gdGhlIGRlZHVwLXJlY292ZXJ5IHBhdGguIFBlci1pbnN0YW5jZSBzY29waW5nIGxldHNcbi8vIHRlc3RzIHNpbXVsYXRlIGNyb3NzLXByb2Nlc3MgYmVoYXZpb3Igd2l0aCB0d28gc3RvcmFnZSBpbnN0YW5jZXNcbi8vIHNoYXJpbmcgb25lIGRhdGEgZGlyZWN0b3J5IChlYWNoIGluc3RhbmNlIGhhcyBpbmRlcGVuZGVudCBsb2Nrc1xuLy8gYnV0IGEgc2hhcmVkIGZpbGVzeXN0ZW0pLCBleGFjdGx5IG1hdGNoaW5nIHRoZSBjcm9zcy1wcm9jZXNzXG4vLyBzZW1hbnRpY3Mgd2l0aG91dCBzcGF3bmluZyBzdWJwcm9jZXNzZXMuXG5cbmNvbnN0IEhvb2tUb2tlbkNsYWltU2NoZW1hID0gei5vYmplY3Qoe1xuICAvLyBUaGUgdG9rZW4tY2xhaW0gd3JpdGVyIGJlbG93IGhhcyBhbHdheXMgcGVyc2lzdGVkIGBob29rSWRgLCBidXRcbiAgLy8gdGhpcyByZWFkIHNjaGVtYSBwcmV2aW91c2x5IG9taXR0ZWQgaXQsIHdoaWNoIGlzIHRoZSBidWcgZml4ZWRcbiAgLy8gYnkgaHR0cHM6Ly9naXRodWIuY29tL3ZlcmNlbC93b3JrZmxvdy9pc3N1ZXMvMjI4My4gYG9wdGlvbmFsKClgXG4gIC8vIGlzIGRlZmVuc2l2ZTogYW55IGNsYWltIGZpbGUgdGhhdCBzb21laG93IGxhY2tzIHRoZSBmaWVsZCBzdGlsbFxuICAvLyBwYXJzZXMgKHlpZWxkaW5nIGB1bmRlZmluZWRgKSBhbmQgZmFsbHMgdGhyb3VnaCB0byB0aGUgY3Jvc3MtXG4gIC8vIGhvb2sgY29uZmxpY3QgYnJhbmNoLCBtYXRjaGluZyBwcmUtZml4IGJlaGF2aW9yLlxuICBob29rSWQ6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgcnVuSWQ6IHouc3RyaW5nKCksXG4gIC8vIGBldmVudElkYCBpcyB0aGUgY2Fub25pY2FsIGhvb2tfY3JlYXRlZCBldmVudCBJRCB0aGUgY2xhaW1pbmdcbiAgLy8gd29ya2VyIGNvbW1pdHRlZCB0byBwdWJsaXNoaW5nLiBQZXJzaXN0aW5nIGl0IGhlcmUgdHVybnMgdGhlXG4gIC8vIGNsYWltIGZpbGUgaW50byBhIGR1cmFibGUgY29udmVyZ2VuY2Uga2V5IGZvciBjcm9zcy13b3JrZXIgL1xuICAvLyBjcm9zcy1wcm9jZXNzIHJldHJpZXMgKHNlZSBjb21tZW50IG9uIHRoZSBob29rX2NyZWF0ZWQgYnJhbmNoKS5cbiAgLy8gYG9wdGlvbmFsKClgIGZvciBiYWNrd2FyZCBjb21wYXRpYmlsaXR5OiBhIGxlZ2FjeSBjbGFpbSBmaWxlXG4gIC8vIHdyaXR0ZW4gYmVmb3JlIHRoaXMgZmllbGQgZXhpc3RlZCBmYWxscyB0aHJvdWdoIHRvIHRoZSByZWNvdmVyeS1cbiAgLy8gbWFya2VyIHVwZ3JhZGUgcGF0aCwgd2hpY2ggYXRvbWljYWxseSBwaW5zIGEgY2Fub25pY2FsIGV2ZW50SWRcbiAgLy8gdmlhIGEgc2lkZWNhciBtYXJrZXIgKGFsc28gYSBgd3JpdGVFeGNsdXNpdmVgKS5cbiAgZXZlbnRJZDogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxufSk7XG5cbi8qKlxuICogU2lkZWNhciByZWNvdmVyeSBtYXJrZXIgdGhhdCBwaW5zIGEgY2Fub25pY2FsIGBob29rX2NyZWF0ZWRgXG4gKiBldmVudElkIGZvciBhIGxlZ2FjeSB0b2tlbiBjbGFpbSDigJQgb25lIHdyaXR0ZW4gYnkgYSB2ZXJzaW9uIG9mXG4gKiB0aGlzIHN0b3JhZ2UgdGhhdCBkaWQgbm90IHlldCBwZXJzaXN0IGBldmVudElkYCBpbmxpbmUgaW4gdGhlXG4gKiBjbGFpbSBmaWxlLiBXaXRob3V0IHRoaXMgbWFya2VyLCB0d28gY3Jvc3MtcHJvY2VzcyByZXRyaWVzXG4gKiByZWFkaW5nIGEgbGVnYWN5IGNsYWltIGVhY2ggZ2VuZXJhdGUgdGhlaXIgb3duIGV2ZW50SWQsIGxhbmRcbiAqIHRoZWlyIGB3cml0ZUV4Y2x1c2l2ZShldmVudFBhdGgpYCBjYWxscyBhdCBkaWZmZXJlbnQgcGF0aHMsIGFuZFxuICogYXBwZW5kIHR3byBgaG9va19jcmVhdGVkYCBldmVudHMgZm9yIHRoZSBzYW1lIGAocnVuSWQsIGhvb2tJZClgLlxuICpcbiAqIFRoZSBtYXJrZXIgaXMgd3JpdHRlbiB2aWEgYHdyaXRlRXhjbHVzaXZlYCDigJQgdGhlIGZpcnN0IHJldHJ5IHRvXG4gKiBsYW5kIGl0IHBpbnMgaXRzIGNhbmRpZGF0ZSBldmVudElkIGFzIGNhbm9uaWNhbCwgYW5kIGV2ZXJ5XG4gKiBzdWJzZXF1ZW50IHJldHJ5IHJlYWRzIGFuZCBhZG9wdHMgdGhhdCBldmVudElkIGJlZm9yZSB0aGUgY29tbW9uXG4gKiBldmVudCBwdWJsaXNoLiBTY2hlbWEgaXMganVzdCBgeyBldmVudElkIH1gIGJlY2F1c2UgaWRlbnRpdHkgaXNcbiAqIGFscmVhZHkgZW5jb2RlZCBpbiB0aGUgbWFya2VyJ3MgZmlsZW5hbWUgaGFzaCwgc28gZGlmZmVyZW50IHRva2VuXG4gKiBsaWZldGltZXMgY2FuIG5ldmVyIHNoYXJlIG9uZSBtYXJrZXIgKHNlZVxuICogYGhvb2tSZWNvdmVyeU1hcmtlclBhdGhgKS5cbiAqL1xuY29uc3QgSG9va1JlY292ZXJ5TWFya2VyU2NoZW1hID0gei5vYmplY3Qoe1xuICBldmVudElkOiB6LnN0cmluZygpLFxufSk7XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRIb29rVG9rZW5DbGFpbShcbiAgY29uc3RyYWludFBhdGg6IHN0cmluZ1xuKTogUHJvbWlzZTx6LmluZmVyPHR5cGVvZiBIb29rVG9rZW5DbGFpbVNjaGVtYT4gfCBudWxsPiB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IHJlYWRKU09OKGNvbnN0cmFpbnRQYXRoLCBIb29rVG9rZW5DbGFpbVNjaGVtYSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU3ludGF4RXJyb3IgfHwgZXJyb3IgaW5zdGFuY2VvZiB6LlpvZEVycm9yKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVhZEhvb2tSZWNvdmVyeU1hcmtlcihcbiAgbWFya2VyUGF0aDogc3RyaW5nXG4pOiBQcm9taXNlPHouaW5mZXI8dHlwZW9mIEhvb2tSZWNvdmVyeU1hcmtlclNjaGVtYT4gfCBudWxsPiB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IHJlYWRKU09OKG1hcmtlclBhdGgsIEhvb2tSZWNvdmVyeU1hcmtlclNjaGVtYSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU3ludGF4RXJyb3IgfHwgZXJyb3IgaW5zdGFuY2VvZiB6LlpvZEVycm9yKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuLyoqXG4gKiBQcm9iZSB0aGUgcnVuJ3MgZXZlbnQgbG9nIGZvciBhbiBleGlzdGluZyBgaG9va19jcmVhdGVkYCBldmVudFxuICogd2l0aCB0aGUgZ2l2ZW4gY29ycmVsYXRpb25JZC4gVXNlZCBieSB0aGUgbGVnYWN5LWNsYWltIHJlY292ZXJ5XG4gKiBwYXRoIHRvIGRldGVjdCBcImFscmVhZHkgcHVibGlzaGVkIGJ5IGEgcHJlLXVwZ3JhZGUgd3JpdGVcIiBiZWZvcmVcbiAqIHBpbm5pbmcgYSBjYW5vbmljYWwgZXZlbnRJZCDigJQgd2l0aG91dCB0aGlzIGNoZWNrLCBhIHBvc3QtdXBncmFkZVxuICogcmV0cnkgZW5jb3VudGVyaW5nIGEgbGVnYWN5IGNsYWltIHdob3NlIGBob29rX2NyZWF0ZWRgIHdhc1xuICogYWxyZWFkeSB3cml0dGVuICh3aXRoIHRoZSBwcmUtdXBncmFkZSB3cml0ZXIncyBvd24gZXZlbnRJZCkgd291bGRcbiAqIHBpbiBhICpkaWZmZXJlbnQqIGV2ZW50SWQgdmlhIHRoZSBtYXJrZXIgYW5kIHB1Ymxpc2ggYSBkdXBsaWNhdGVcbiAqIGV2ZW50IGF0IHRoZSBtYXJrZXIncyBwYXRoLlxuICpcbiAqIFRoZSBpbmxpbmUtYGV2ZW50SWRgIGZhc3QgcGF0aCBkb2VzIE5PVCBuZWVkIHRoaXMgcHJvYmU6IHRoZVxuICogY2Fub25pY2FsIGV2ZW50SWQgaXMgZHVyYWJsZSBpbiB0aGUgY2xhaW0gZmlsZSwgc28gdGhlIGV4aXN0aW5nXG4gKiBwdWJsaXNoIChgd3JpdGVFeGNsdXNpdmUoZXZlbnRQYXRoKWApIHdpbGwgZmFpbCBpZmYgdGhlIGV2ZW50XG4gKiBhbHJlYWR5IGV4aXN0cyBhdCB0aGF0IGV4YWN0IHBhdGgg4oCUIHdoaWNoIGlzIHRoZSBjb3JyZWN0XG4gKiBcImFscmVhZHktcHVibGlzaGVkXCIgc2VtYW50aWMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGZpbmRFeGlzdGluZ0hvb2tDcmVhdGVkRXZlbnRJZChcbiAgYmFzZWRpcjogc3RyaW5nLFxuICBydW5JZDogc3RyaW5nLFxuICBjb3JyZWxhdGlvbklkOiBzdHJpbmdcbik6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBwYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnkoe1xuICAgIGRpcmVjdG9yeTogcGF0aC5qb2luKGJhc2VkaXIsICdldmVudHMnKSxcbiAgICBzY2hlbWE6IEV2ZW50U2NoZW1hLFxuICAgIGZpbGVQcmVmaXg6IGAke3J1bklkfS1gLFxuICAgIGZpbHRlcjogKGV2ZW50KSA9PlxuICAgICAgZXZlbnQuZXZlbnRUeXBlID09PSAnaG9va19jcmVhdGVkJyAmJlxuICAgICAgZXZlbnQuY29ycmVsYXRpb25JZCA9PT0gY29ycmVsYXRpb25JZCxcbiAgICBsaW1pdDogMSxcbiAgICBnZXRDcmVhdGVkQXQ6IGdldE9iamVjdENyZWF0ZWRBdCgnZXZudCcpLFxuICAgIGdldElkOiAoZXZlbnQpID0+IGV2ZW50LmV2ZW50SWQsXG4gIH0pO1xuICByZXR1cm4gcmVzdWx0LmRhdGFbMF0/LmV2ZW50SWQgPz8gbnVsbDtcbn1cblxuLyoqXG4gKiBSZXBhaXIgYW4gXCJldmVudC1maXJzdCBvcnBoYW5cIjogdGhlIGhvb2sgZW50aXR5IHdyaXRlIGlzIGRlZmVycmVkXG4gKiB1bnRpbCBhZnRlciB0aGUgYGhvb2tfY3JlYXRlZGAgZXZlbnQgcHVibGlzaCBjb21taXRzIChzbyBhIGZhaWxlZFxuICogcHVibGlzaCBjYW5ub3QgbXV0YXRlIGFscmVhZHktY29tbWl0dGVkIHN0YXRlIOKAlCBzZWUgdGhlIGNvbW1lbnQgb25cbiAqIHRoZSBkZWZlcnJlZCB3cml0ZSksIHdoaWNoIG9wZW5zIHRoZSBpbnZlcnNlIGNyYXNoIHdpbmRvdyDigJQgYVxuICogY3Jhc2ggQUZURVIgdGhlIGV2ZW50IHB1Ymxpc2ggYnV0IEJFRk9SRSB0aGUgZGVmZXJyZWQgZW50aXR5IHdyaXRlXG4gKiBsZWF2ZXMgdGhlIGV2ZW50IGluIHRoZSBsb2cgd2l0aCB0aGUgaG9vayBlbnRpdHkgbWlzc2luZy4gQSByZXRyeVxuICogdGhlbiBjb2xsaWRlcyBhdCB0aGUgZXZlbnQgcHVibGlzaCBhbmQgdGhyb3dzXG4gKiBgRW50aXR5Q29uZmxpY3RFcnJvcmAgKGNvcnJlY3Qg4oCUIHRoZSBldmVudCBJUyBjb21taXR0ZWQpLCBidXRcbiAqIHdpdGhvdXQgdGhpcyByZXBhaXIgdGhlIGVudGl0eSB3b3VsZCBzdGF5IG1pc3NpbmcgZm9yZXZlciBhbmQgdGhlXG4gKiBob29rIHdvdWxkIGJlIHVucmVzb2x2YWJsZS5cbiAqXG4gKiBUaGUgZW50aXR5IE1VU1QgYmUgcmVjb25zdHJ1Y3RlZCBmcm9tIHRoZSBwZXJzaXN0ZWQgY2Fub25pY2FsXG4gKiBldmVudCdzIHBheWxvYWQg4oCUIE5PVCB0aGUgcmV0cnkncyBgZXZlbnREYXRhYCDigJQgb3RoZXJ3aXNlIGEgcmV0cnlcbiAqIGNhcnJ5aW5nIGRpZmZlcmVudCBgbWV0YWRhdGFgIC8gYGlzV2ViaG9va2Agd291bGQgc2lsZW50bHkgY2hhbmdlXG4gKiBjb21taXR0ZWQgc3RhdGUuIFRoZSB3cml0ZSB1c2VzIGB3cml0ZUV4Y2x1c2l2ZWAgKGNyZWF0ZS1pZi1hYnNlbnQpXG4gKiBzbyBhIGNvbmN1cnJlbnQgd3JpdGVyIHJhY2luZyB0aGlzIHJlcGFpciBjYW5ub3QgYmUgb3ZlcndyaXR0ZW47XG4gKiB3aGljaGV2ZXIgd3JpdGUgbGFuZHMgZmlyc3QsIHRoZSBjb250ZW50IGlzIGlkZW50aWNhbCBiZWNhdXNlIGJvdGhcbiAqIGRlcml2ZSBmcm9tIHRoZSBzYW1lIHBlcnNpc3RlZCBldmVudC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVwYWlySG9va0VudGl0eUZyb21QZXJzaXN0ZWRFdmVudChcbiAgYmFzZWRpcjogc3RyaW5nLFxuICBydW5JZDogc3RyaW5nLFxuICBob29rSWQ6IHN0cmluZyxcbiAgcGVyc2lzdGVkRXZlbnRJZDogc3RyaW5nLFxuICB0YWc6IHN0cmluZyB8IHVuZGVmaW5lZFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGNvbXBvc2l0ZUtleSA9IGAke3J1bklkfS0ke3BlcnNpc3RlZEV2ZW50SWR9YDtcbiAgY29uc3QgcGVyc2lzdGVkRXZlbnQgPSBhd2FpdCByZWFkSlNPTldpdGhGYWxsYmFjayhcbiAgICBiYXNlZGlyLFxuICAgICdldmVudHMnLFxuICAgIGNvbXBvc2l0ZUtleSxcbiAgICBFdmVudFNjaGVtYSxcbiAgICB0YWdcbiAgKTtcbiAgaWYgKFxuICAgICFwZXJzaXN0ZWRFdmVudCB8fFxuICAgIHBlcnNpc3RlZEV2ZW50LmV2ZW50VHlwZSAhPT0gJ2hvb2tfY3JlYXRlZCcgfHxcbiAgICBwZXJzaXN0ZWRFdmVudC5jb3JyZWxhdGlvbklkICE9PSBob29rSWRcbiAgKSB7XG4gICAgLy8gTm90aGluZyB0cnVzdHdvcnRoeSB0byByZXBhaXIgZnJvbS5cbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgZXhpc3RpbmdIb29rID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgYmFzZWRpcixcbiAgICAnaG9va3MnLFxuICAgIGhvb2tJZCxcbiAgICBIb29rU2NoZW1hLFxuICAgIHRhZ1xuICApO1xuICBpZiAoZXhpc3RpbmdIb29rKSB7XG4gICAgLy8gRW50aXR5IGFscmVhZHkgcHJlc2VudCDigJQgbm90IGFuIG9ycGhhbiwgbGVhdmUgaXQgdW50b3VjaGVkLlxuICAgIHJldHVybjtcbiAgfVxuICBjb25zdCBldmVudERhdGEgPSAocGVyc2lzdGVkRXZlbnQuZXZlbnREYXRhID8/IHt9KSBhcyB7XG4gICAgdG9rZW4/OiBzdHJpbmc7XG4gICAgbWV0YWRhdGE/OiBTZXJpYWxpemVkRGF0YTtcbiAgICBpc1dlYmhvb2s/OiBib29sZWFuO1xuICAgIGlzU3lzdGVtPzogYm9vbGVhbjtcbiAgfTtcbiAgaWYgKHR5cGVvZiBldmVudERhdGEudG9rZW4gIT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IGhvb2s6IEhvb2sgPSB7XG4gICAgcnVuSWQsXG4gICAgaG9va0lkLFxuICAgIHRva2VuOiBldmVudERhdGEudG9rZW4sXG4gICAgbWV0YWRhdGE6IGV2ZW50RGF0YS5tZXRhZGF0YSxcbiAgICBvd25lcklkOiAnbG9jYWwtb3duZXInLFxuICAgIHByb2plY3RJZDogJ2xvY2FsLXByb2plY3QnLFxuICAgIGVudmlyb25tZW50OiAnbG9jYWwnLFxuICAgIGNyZWF0ZWRBdDogcGVyc2lzdGVkRXZlbnQuY3JlYXRlZEF0LFxuICAgIHNwZWNWZXJzaW9uOiBwZXJzaXN0ZWRFdmVudC5zcGVjVmVyc2lvbixcbiAgICBpc1dlYmhvb2s6IGV2ZW50RGF0YS5pc1dlYmhvb2sgPz8gZmFsc2UsXG4gICAgaXNTeXN0ZW06IGV2ZW50RGF0YS5pc1N5c3RlbSA/PyBmYWxzZSxcbiAgfTtcbiAgYXdhaXQgd3JpdGVFeGNsdXNpdmUoXG4gICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnaG9va3MnLCBob29rSWQsIHRhZyksXG4gICAgSlNPTi5zdHJpbmdpZnkoaG9vaywganNvblJlcGxhY2VyLCAyKVxuICApO1xufVxuXG4vKipcbiAqIEF0b21pY2FsbHkgcGluIGEgY2Fub25pY2FsIGBob29rX2NyZWF0ZWRgIGV2ZW50SWQgZm9yIGEgbGVnYWN5XG4gKiBjbGFpbSAob25lIHdpdGhvdXQgYW4gaW5saW5lIGBldmVudElkYCkuIFRoZSBmaXJzdCByZXRyeSB0b1xuICogYHdyaXRlRXhjbHVzaXZlYCB0aGUgcmVjb3ZlcnkgbWFya2VyIHdpbnM7IGl0cyBgY2FuZGlkYXRlRXZlbnRJZGBcbiAqIGJlY29tZXMgY2Fub25pY2FsLiBTdWJzZXF1ZW50IHJldHJpZXMgcmVhZCB0aGUgbWFya2VyIGFuZCBhZG9wdFxuICogaXRzIGBldmVudElkYC4gVG9nZXRoZXIgd2l0aCB0aGUgYHdyaXRlRXhjbHVzaXZlKGV2ZW50UGF0aClgIGluXG4gKiB0aGUgb3V0ZXIgZXZlbnQgcHVibGlzaCwgdGhpcyBnaXZlcyB0aGUgbGVnYWN5LWZhbGxiYWNrIHBhdGggdGhlXG4gKiBzYW1lIHNpbmdsZS1ldmVudCBjb252ZXJnZW5jZSBndWFyYW50ZWUgYXMgdGhlIGlubGluZS1gZXZlbnRJZGBcbiAqIGZhc3QgcGF0aC5cbiAqXG4gKiBSZXR1cm5zIHRoZSBjYW5vbmljYWwgZXZlbnRJZCBmb3IgdGhlIGNhbGxlciB0byBhZG9wdCwgb3IgYG51bGxgXG4gKiBpZiB3ZSBsb3N0IHRoZSBtYXJrZXIgcmFjZSBBTkQgdGhlIHJlc3VsdGluZyBtYXJrZXIgZmlsZSBpc1xuICogdW5yZWFkYWJsZSAoZXh0cmVtZWx5IHJhcmU7IGNvcnJ1cHRlZCBkaXNrKS4gQ2FsbGVycyB0cmVhdCBgbnVsbGBcbiAqIGFzIFwiZ2l2ZSB1cCwgdGhyb3cgYEVudGl0eUNvbmZsaWN0RXJyb3JgXCIgc28gdGhlIHJ1bnRpbWUnc1xuICogY29uY3VycmVudC1yZXBsYXkgY2F0Y2ggcGF0aCBzd2FsbG93cyB0aGlzIGF0dGVtcHQgYW5kIGxldHNcbiAqIGFub3RoZXIgb25lIHRocm91Z2guXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHBpbkNhbm9uaWNhbEV2ZW50SWRGb3JMZWdhY3lDbGFpbShcbiAgYmFzZWRpcjogc3RyaW5nLFxuICB0b2tlbjogc3RyaW5nLFxuICBydW5JZDogc3RyaW5nLFxuICBob29rSWQ6IHN0cmluZyxcbiAgY2FuZGlkYXRlRXZlbnRJZDogc3RyaW5nXG4pOiBQcm9taXNlPHN0cmluZyB8IG51bGw+IHtcbiAgY29uc3QgbWFya2VyUGF0aCA9IGhvb2tSZWNvdmVyeU1hcmtlclBhdGgoYmFzZWRpciwgdG9rZW4sIHJ1bklkLCBob29rSWQpO1xuICBjb25zdCBtYXJrZXJDb250ZW50ID0gSlNPTi5zdHJpbmdpZnkoeyBldmVudElkOiBjYW5kaWRhdGVFdmVudElkIH0pO1xuICBjb25zdCB3b24gPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShtYXJrZXJQYXRoLCBtYXJrZXJDb250ZW50KTtcbiAgaWYgKHdvbikge1xuICAgIHJldHVybiBjYW5kaWRhdGVFdmVudElkO1xuICB9XG4gIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgcmVhZEhvb2tSZWNvdmVyeU1hcmtlcihtYXJrZXJQYXRoKTtcbiAgcmV0dXJuIGV4aXN0aW5nPy5ldmVudElkID8/IG51bGw7XG59XG5cbi8qKlxuICogSW4tcHJvY2VzcyBwZXIta2V5IGFzeW5jIG11dGV4IGJhY2tlZCBieSBhIGNhbGxlci1zdXBwbGllZCBgTWFwYC5cbiAqIFVzZWQgYnkgYGNyZWF0ZUV2ZW50c1N0b3JhZ2VgIHRvIHNlcmlhbGl6ZSBzYW1lLWtleSBldmVudCB3cml0ZXNcbiAqIChgc3RlcF8qYCBmb3IgdGhlIHNhbWUgc3RlcCwgYGhvb2tfY3JlYXRlZGAgZm9yIHRoZSBzYW1lIGhvb2spLlxuICogVGhlIG1hcCBpcyBpbnN0YW50aWF0ZWQgcGVyLXN0b3JhZ2UtaW5zdGFuY2Ug4oCUIGRpZmZlcmVudFxuICogaW5zdGFuY2VzIGRvIE5PVCBzaGFyZSBsb2Nrcywgc28gdHdvIGluc3RhbmNlcyBzaGFyaW5nIG9uZSBkYXRhXG4gKiBkaXJlY3RvcnkgYmVoYXZlIGV4YWN0bHkgbGlrZSB0d28gc2VwYXJhdGUgT1MgcHJvY2Vzc2VzIGZyb20gdGhlXG4gKiBsb2NraW5nIHN0YW5kcG9pbnQuIENyb3NzLWluc3RhbmNlIC8gY3Jvc3MtcHJvY2VzcyBhcmJpdHJhdGlvblxuICogcmVsaWVzIG9uIHRoZSBvbi1kaXNrIGNvbnN0cmFpbnQgLyBjbGFpbSBmaWxlcyBpbnN0ZWFkLlxuICovXG5mdW5jdGlvbiB3aXRoSW5Qcm9jZXNzTG9jazxUPihcbiAgbG9ja3M6IE1hcDxzdHJpbmcsIFByb21pc2U8dW5rbm93bj4+LFxuICBrZXk6IHN0cmluZyxcbiAgZm46ICgpID0+IFByb21pc2U8VD5cbik6IFByb21pc2U8VD4ge1xuICBjb25zdCBwcmV2ID0gbG9ja3MuZ2V0KGtleSk7XG4gIGNvbnN0IHRhc2tCb3g6IHsgdGFzaz86IFByb21pc2U8VD4gfSA9IHt9O1xuICBjb25zdCB0YXNrID0gKGFzeW5jICgpID0+IHtcbiAgICBpZiAocHJldikge1xuICAgICAgLy8gV2FpdCBmb3IgdGhlIHByZXZpb3VzIHRhc2sgdG8gc2V0dGxlOyBkb24ndCBpbmhlcml0IGl0cyBlcnJvcnMuXG4gICAgICBhd2FpdCBwcmV2LmNhdGNoKCgpID0+IHVuZGVmaW5lZCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgZm4oKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKGxvY2tzLmdldChrZXkpID09PSB0YXNrQm94LnRhc2spIHtcbiAgICAgICAgbG9ja3MuZGVsZXRlKGtleSk7XG4gICAgICB9XG4gICAgfVxuICB9KSgpO1xuICB0YXNrQm94LnRhc2sgPSB0YXNrO1xuICBsb2Nrcy5zZXQoa2V5LCB0YXNrKTtcbiAgcmV0dXJuIHRhc2s7XG59XG5cbi8qKlxuICogSGVscGVyIGZ1bmN0aW9uIHRvIGRlbGV0ZSBhbGwgd2FpdHMgYXNzb2NpYXRlZCB3aXRoIGEgd29ya2Zsb3cgcnVuLlxuICogQ2FsbGVkIHdoZW4gYSBydW4gcmVhY2hlcyBhIHRlcm1pbmFsIHN0YXRlLlxuICovXG5hc3luYyBmdW5jdGlvbiBkZWxldGVBbGxXYWl0c0ZvclJ1bihcbiAgYmFzZWRpcjogc3RyaW5nLFxuICBydW5JZDogc3RyaW5nXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qgd2FpdHNEaXIgPSBwYXRoLmpvaW4oYmFzZWRpciwgJ3dhaXRzJyk7XG4gIGNvbnN0IGZpbGVzID0gYXdhaXQgbGlzdEpTT05GaWxlcyh3YWl0c0Rpcik7XG5cbiAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgLy8gZmlsZUlkcyBtYXkgY29udGFpbiB0YWcgc3VmZml4ZXMgKGUuZy4sIFwid3J1bl9BQkMtY29ycklkLnZpdGVzdC0wXCIpXG4gICAgLy8gYnV0IHN0YXJ0c1dpdGggc3RpbGwgbWF0Y2hlcyBjb3JyZWN0bHkgc2luY2UgdGhlIHRhZyBpcyBhIHN1ZmZpeC5cbiAgICBpZiAoZmlsZS5zdGFydHNXaXRoKGAke3J1bklkfS1gKSkge1xuICAgICAgY29uc3Qgd2FpdFBhdGggPSBwYXRoLmpvaW4od2FpdHNEaXIsIGAke2ZpbGV9Lmpzb25gKTtcbiAgICAgIGF3YWl0IGRlbGV0ZUpTT04od2FpdFBhdGgpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIFBlcnNpc3QgYSBsaWZlY3ljbGUtZHJpdmVuIHJ1biB1cGRhdGUgKHJ1bl9zdGFydGVkIC8gcnVuX2NvbXBsZXRlZCAvXG4gKiBydW5fZmFpbGVkIC8gcnVuX2NhbmNlbGxlZCkgdW5kZXIgdGhlIHNoYXJlZCBwZXItcnVuIGZpbGUgbG9jayxcbiAqIHJlLXJlYWRpbmcgdGhlIG9uLWRpc2sgcnVuIGluc2lkZSB0aGUgbG9jayBzbyBhbnkgYXR0cmlidXRlIHdyaXRlc1xuICogdGhhdCBsYW5kZWQgYmV0d2VlbiB0aGUgcHJlLXZhbGlkYXRpb24gYGN1cnJlbnRSdW5gIHJlYWQgYW5kIHRoaXNcbiAqIHdyaXRlIGFyZSBwcmVzZXJ2ZWQuIFdpdGhvdXQgdGhlIHJlLXJlYWQsIGFuIGBleHBlcmltZW50YWxTZXRBdHRyaWJ1dGVzYFxuICogY2FsbCBzYW5kd2ljaGVkIGJldHdlZW4gdGhlIGxpZmVjeWNsZSByZWFkIGFuZCB3cml0ZSB3b3VsZCBiZVxuICogc2lsZW50bHkgb3ZlcndyaXR0ZW4gYnkgdGhlIGxpZmVjeWNsZSB3cml0ZSdzIHN0YWxlIGF0dHJpYnV0ZSBzbmFwc2hvdC5cbiAqXG4gKiBgcHJvcG9zZWRgIGlzIHRoZSBmdWxseS1jb25zdHJ1Y3RlZCBydW4gcm93IHRoZSBjYWxsZXIgd2FudHMgdG9cbiAqIHdyaXRlICh3aXRoIHRoZSBjb3JyZWN0IGRpc2NyaW1pbmF0ZWQtdW5pb24gc3RhdHVzIGJyYW5jaCkuIE9ubHkgdGhlXG4gKiBgYXR0cmlidXRlc2AgZmllbGQgaXMgcmVwbGFjZWQgd2l0aCB0aGUgZnJlc2hlc3QgdmVyc2lvbiBpbnNpZGUgdGhlXG4gKiBsb2NrLlxuICovXG5hc3luYyBmdW5jdGlvbiB3cml0ZVJ1blVuZGVyTGlmZWN5Y2xlTG9jazxUIGV4dGVuZHMgV29ya2Zsb3dSdW4+KFxuICBiYXNlZGlyOiBzdHJpbmcsXG4gIHJ1bklkOiBzdHJpbmcsXG4gIHRhZzogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICBwcm9wb3NlZDogVFxuKTogUHJvbWlzZTxUPiB7XG4gIHJldHVybiB3aXRoUnVuRmlsZUxvY2socnVuSWQsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBmcmVzaCA9IGF3YWl0IHJlYWRKU09OKFxuICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAncnVucycsIHJ1bklkLCB0YWcpLFxuICAgICAgV29ya2Zsb3dSdW5TY2hlbWFcbiAgICApO1xuICAgIGNvbnN0IG5leHQ6IFQgPSB7XG4gICAgICAuLi5wcm9wb3NlZCxcbiAgICAgIGF0dHJpYnV0ZXM6IGZyZXNoPy5hdHRyaWJ1dGVzID8/IHByb3Bvc2VkLmF0dHJpYnV0ZXMsXG4gICAgfTtcbiAgICBhd2FpdCB3cml0ZUpTT04odGFnZ2VkUGF0aChiYXNlZGlyLCAncnVucycsIHJ1bklkLCB0YWcpLCBuZXh0LCB7XG4gICAgICBvdmVyd3JpdGU6IHRydWUsXG4gICAgfSk7XG4gICAgcmV0dXJuIG5leHQ7XG4gIH0pO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGV2ZW50cyBzdG9yYWdlIGltcGxlbWVudGF0aW9uIHVzaW5nIHRoZSBmaWxlc3lzdGVtLlxuICogSW1wbGVtZW50cyB0aGUgU3RvcmFnZVsnZXZlbnRzJ10gaW50ZXJmYWNlIHdpdGggY3JlYXRlLCBsaXN0LCBhbmQgbGlzdEJ5Q29ycmVsYXRpb25JZCBvcGVyYXRpb25zLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRXZlbnRzU3RvcmFnZShcbiAgYmFzZWRpcjogc3RyaW5nLFxuICB0YWc/OiBzdHJpbmdcbik6IFN0b3JhZ2VbJ2V2ZW50cyddIHtcbiAgLy8gUGVyLWluc3RhbmNlIGluLXByb2Nlc3MgbXV0ZXhlcy4gVHdvIHN0b3JhZ2UgaW5zdGFuY2VzIHNoYXJpbmdcbiAgLy8gb25lIGRhdGEgZGlyZWN0b3J5IGdldCBpbmRlcGVuZGVudCBsb2NrIG1hcHMsIHdoaWNoIG1ha2VzIHRoZW1cbiAgLy8gYmVoYXZlIGxpa2UgdHdvIHNlcGFyYXRlIE9TIHByb2Nlc3NlcyBmcm9tIHRoZSBsb2NraW5nXG4gIC8vIHN0YW5kcG9pbnQg4oCUIGNyb3NzLWluc3RhbmNlIGFyYml0cmF0aW9uIHJlbGllcyBvbiB0aGUgb24tZGlza1xuICAvLyBgd3JpdGVFeGNsdXNpdmVgIGNvbnN0cmFpbnQgLyBjbGFpbSBmaWxlcyBpbnN0ZWFkLiBUZXN0cyB1c2VcbiAgLy8gdGhpcyB0byBleGVyY2lzZSBjcm9zcy1wcm9jZXNzIGNvbnZlcmdlbmNlIHdpdGhvdXQgc3Bhd25pbmdcbiAgLy8gc3VicHJvY2Vzc2VzLlxuICAvL1xuICAvLyBgc3RlcExvY2tzYCBzZXJpYWxpemVzIHN0ZXAgbGlmZWN5Y2xlIGV2ZW50cyBmb3IgdGhlIHNhbWVcbiAgLy8gKHJ1bklkLCBjb3JyZWxhdGlvbklkKTogc2VlIGNvbW1lbnQgZnVydGhlciBkb3duIGluIHRoZVxuICAvLyBgaXNTdGVwRXZlbnRgIGJyYW5jaC5cbiAgLy9cbiAgLy8gYGhvb2tMb2Nrc2Agc2VyaWFsaXplcyBgaG9va19jcmVhdGVkYCBjYWxscyBmb3IgdGhlIHNhbWVcbiAgLy8gKHJ1bklkLCBjb3JyZWxhdGlvbklkKSBzbyB0aGUgXCJjbGFpbSB0b2tlbiwgdGhlbiB3cml0ZSBob29rXG4gIC8vIGVudGl0eSArIGV2ZW50XCIgc2VxdWVuY2UgcnVucyB0byBjb21wbGV0aW9uIGJlZm9yZSBhbm90aGVyXG4gIC8vIGluLXByb2Nlc3MgaW52b2NhdGlvbiBlbnRlcnMgdGhlIGRlZHVwIGJyYW5jaC5cbiAgY29uc3Qgc3RlcExvY2tzID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2U8dW5rbm93bj4+KCk7XG4gIGNvbnN0IGhvb2tMb2NrcyA9IG5ldyBNYXA8c3RyaW5nLCBQcm9taXNlPHVua25vd24+PigpO1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgY3JlYXRlKHJ1bklkLCBkYXRhLCBwYXJhbXMpOiBQcm9taXNlPEV2ZW50UmVzdWx0PiB7XG4gICAgICAvLyBWYWxpZGF0ZSByZXF1ZXN0LXN1cHBsaWVkIElEcyBiZWZvcmUgdGhleSdyZSBjb25jYXRlbmF0ZWQgaW50b1xuICAgICAgLy8gZmlsZXN5c3RlbSBwYXRocy4gVGhpcyBpcyB0aGUgcHJpbWFyeSBkZWZlbnNlIGFnYWluc3QgcGF0aCB0cmF2ZXJzYWxcbiAgICAgIC8vIGF0dGFja3Mgd2hlcmUgYSBjbGllbnQgc3VwcGxpZXMgcnVuSWQgLyBjb3JyZWxhdGlvbklkIHZhbHVlcyBsaWtlXG4gICAgICAvLyBcIi4uLy4uLy4uL3BhY2thZ2VcIiB0byByZWFkIG9yIHdyaXRlIGZpbGVzIG91dHNpZGUgdGhlIHN0b3JhZ2Ugcm9vdC5cbiAgICAgIC8vIFJ1biBiZWZvcmUgdGFraW5nIHRoZSBwZXItc3RlcCBtdXRleCBzbyBtYWxmb3JtZWQgaW5wdXRzIGZhaWwgZmFzdC5cbiAgICAgIC8vXG4gICAgICAvLyBFbXB0eSBgY29ycmVsYXRpb25JZGAgdmFsdWVzIGFyZSBhbHNvIHJlamVjdGVkIGhlcmU6IHRoZSBldmVudFxuICAgICAgLy8gc2NoZW1hcyBvbmx5IHJlcXVpcmUgYHouc3RyaW5nKClgLCBzbyB3aXRob3V0IHRoaXMgY2hlY2sgYVxuICAgICAgLy8gc3RlcF9jcmVhdGVkIC8gaG9va19jcmVhdGVkIC8gd2FpdF9jcmVhdGVkIHJlcXVlc3Qgd2l0aFxuICAgICAgLy8gYGNvcnJlbGF0aW9uSWQ6ICcnYCB3b3VsZCBzaWxlbnRseSBiZSB3cml0dGVuIHVuZGVyIGEgbWFsZm9ybWVkXG4gICAgICAvLyBjb21wb3NpdGUga2V5IGxpa2UgYCR7cnVuSWR9LWAuXG4gICAgICBpZiAocnVuSWQgIT0gbnVsbCAmJiBydW5JZCAhPT0gJycpIHtcbiAgICAgICAgYXNzZXJ0U2FmZUVudGl0eUlkKCdydW5JZCcsIHJ1bklkKTtcbiAgICAgIH1cbiAgICAgIGlmICgnY29ycmVsYXRpb25JZCcgaW4gZGF0YSAmJiB0eXBlb2YgZGF0YS5jb3JyZWxhdGlvbklkID09PSAnc3RyaW5nJykge1xuICAgICAgICBhc3NlcnRTYWZlRW50aXR5SWQoJ2NvcnJlbGF0aW9uSWQnLCBkYXRhLmNvcnJlbGF0aW9uSWQpO1xuICAgICAgfVxuXG4gICAgICAvLyBTdGVwIGxpZmVjeWNsZSBldmVudHMgYXJlIHNlcmlhbGl6ZWQgcGVyLXN0ZXAgdmlhIGFuIGluLXByb2Nlc3MgbXV0ZXhcbiAgICAgIC8vIHNvIHRoYXQgdGhlIFwiY2hlY2sgc3RhdGUsIHRoZW4gd3JpdGVcIiBzZXF1ZW5jZSBpbiBzdGVwX3N0YXJ0ZWQgL1xuICAgICAgLy8gc3RlcF9jb21wbGV0ZWQgLyBzdGVwX2ZhaWxlZCAvIHN0ZXBfcmV0cnlpbmcgaXMgYXRvbWljLiBzdGVwX2NyZWF0ZWRcbiAgICAgIC8vIGlzIGFsc28gc2VyaWFsaXplZCBzbyBkdXBsaWNhdGUtY3JlYXRlIHJhY2VzIGRvbid0IGxlYXZlIGV4dHJhXG4gICAgICAvLyBzdGVwX2NyZWF0ZWQgZXZlbnRzIGluIHRoZSBsb2cuXG4gICAgICBjb25zdCBpc1N0ZXBFdmVudCA9XG4gICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9jcmVhdGVkJyB8fFxuICAgICAgICBkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfc3RhcnRlZCcgfHxcbiAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2NvbXBsZXRlZCcgfHxcbiAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2ZhaWxlZCcgfHxcbiAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX3JldHJ5aW5nJztcbiAgICAgIGlmIChpc1N0ZXBFdmVudCAmJiBydW5JZCAmJiBkYXRhLmNvcnJlbGF0aW9uSWQpIHtcbiAgICAgICAgY29uc3QgbG9ja0tleSA9IHRhZ1xuICAgICAgICAgID8gYCR7cnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfS4ke3RhZ31gXG4gICAgICAgICAgOiBgJHtydW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9YDtcbiAgICAgICAgcmV0dXJuIHdpdGhJblByb2Nlc3NMb2NrKHN0ZXBMb2NrcywgbG9ja0tleSwgKCkgPT4gY3JlYXRlSW1wbCgpKTtcbiAgICAgIH1cbiAgICAgIC8vIGBob29rX2NyZWF0ZWRgIGlzIHNlcmlhbGl6ZWQgcGVyLShydW5JZCwgaG9va0lkKSBzbyB0aGVcbiAgICAgIC8vIFwiY2xhaW0gdG9rZW4sIHdyaXRlIGhvb2sgZW50aXR5LCB3cml0ZSBldmVudFwiIHNlcXVlbmNlIHJ1bnMgdG9cbiAgICAgIC8vIGNvbXBsZXRpb24gYmVmb3JlIGFub3RoZXIgaW4tcHJvY2VzcyBpbnZvY2F0aW9uIGVudGVycyB0aGVcbiAgICAgIC8vIHNhbWUtaG9vayBkZWR1cCBicmFuY2guIFdpdGhvdXQgdGhpcywgdHdvIHNhbWUtdGljayBjb25jdXJyZW50XG4gICAgICAvLyBjYWxsZXJzIGNhbiByYWNlIGJldHdlZW4gdGhlIHdpbm5lcidzIGB3cml0ZUV4Y2x1c2l2ZShjbGFpbSlgXG4gICAgICAvLyBhbmQgYHdyaXRlSlNPTihob29rKWAsIG1ha2luZyB0aGUgc2Vjb25kIGNhbGxlciBtb21lbnRhcmlseVxuICAgICAgLy8gb2JzZXJ2ZSBhIGNsYWltIHdpdGggbm8gbWF0Y2hpbmcgaG9vayBlbnRpdHkg4oCUIHdoaWNoIHRoZVxuICAgICAgLy8gY3Jhc2gtcmVjb3ZlcnkgcGF0aCBiZWxvdyB3b3VsZCBtaXNpbnRlcnByZXQgYXMgYSBwcmlvciBjcmFzaFxuICAgICAgLy8gYW5kIGluY29ycmVjdGx5IGZhbGwgdGhyb3VnaCB0byBhIHNlY29uZCBob29rIGVudGl0eSB3cml0ZS5cbiAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ2hvb2tfY3JlYXRlZCcgJiYgcnVuSWQgJiYgZGF0YS5jb3JyZWxhdGlvbklkKSB7XG4gICAgICAgIGNvbnN0IGxvY2tLZXkgPSB0YWdcbiAgICAgICAgICA/IGAke3J1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH0uaG9vay4ke3RhZ31gXG4gICAgICAgICAgOiBgJHtydW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9Lmhvb2tgO1xuICAgICAgICByZXR1cm4gd2l0aEluUHJvY2Vzc0xvY2soaG9va0xvY2tzLCBsb2NrS2V5LCAoKSA9PiBjcmVhdGVJbXBsKCkpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGNyZWF0ZUltcGwoKTtcblxuICAgICAgYXN5bmMgZnVuY3Rpb24gY3JlYXRlSW1wbCgpOiBQcm9taXNlPEV2ZW50UmVzdWx0PiB7XG4gICAgICAgIC8vIE1vc3QgcGF0aHMgdXNlIHRoZSBmcmVzaGx5LWdlbmVyYXRlZCBjYW5kaWRhdGUgZXZlbnRJZC4gVGhlXG4gICAgICAgIC8vIGhvb2tfY3JlYXRlZCBkZWR1cC1yZWNvdmVyeSBwYXRoIGJlbG93IG1heSByZWFzc2lnbiBpdCB0b1xuICAgICAgICAvLyB0aGUgY2Fub25pY2FsIGV2ZW50SWQgcGVyc2lzdGVkIGluIHRoZSBkdXJhYmxlIHRva2VuIGNsYWltXG4gICAgICAgIC8vIHNvIGNvbmN1cnJlbnQgLyBjcm9zcy1wcm9jZXNzIHdvcmtlcnMgY29udmVyZ2Ugb24gYSBzaW5nbGVcbiAgICAgICAgLy8gZXZlbnQgaW4gdGhlIGxvZy5cbiAgICAgICAgbGV0IGV2ZW50SWQgPSBgZXZudF8ke21vbm90b25pY1VsaWQoKX1gO1xuICAgICAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuXG4gICAgICAgIC8vIEZvciBydW5fY3JlYXRlZCBldmVudHMsIHVzZSBjbGllbnQtcHJvdmlkZWQgcnVuSWQgb3IgZ2VuZXJhdGUgb25lIHNlcnZlci1zaWRlXG4gICAgICAgIGxldCBlZmZlY3RpdmVSdW5JZDogc3RyaW5nO1xuICAgICAgICBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdydW5fY3JlYXRlZCcgJiYgKCFydW5JZCB8fCBydW5JZCA9PT0gJycpKSB7XG4gICAgICAgICAgZWZmZWN0aXZlUnVuSWQgPSBgd3J1bl8ke21vbm90b25pY1VsaWQoKX1gO1xuICAgICAgICB9IGVsc2UgaWYgKCFydW5JZCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigncnVuSWQgaXMgcmVxdWlyZWQgZm9yIG5vbi1ydW5fY3JlYXRlZCBldmVudHMnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBlZmZlY3RpdmVSdW5JZCA9IHJ1bklkO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVmFsaWRhdGUgY2xpZW50LXByb3ZpZGVkIHJ1bklkIHRpbWVzdGFtcCBpcyB3aXRoaW4gYWNjZXB0YWJsZSB0aHJlc2hvbGRcbiAgICAgICAgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAncnVuX2NyZWF0ZWQnICYmIHJ1bklkICYmIHJ1bklkICE9PSAnJykge1xuICAgICAgICAgIGNvbnN0IHZhbGlkYXRpb25FcnJvciA9IHZhbGlkYXRlVWxpZFRpbWVzdGFtcChcbiAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgJ3dydW5fJ1xuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKHZhbGlkYXRpb25FcnJvcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFdvcmtmbG93V29ybGRFcnJvcih2YWxpZGF0aW9uRXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHNwZWNWZXJzaW9uIGlzIGFsd2F5cyBzZW50IGJ5IHRoZSBydW50aW1lLCBidXQgd2UgcHJvdmlkZSBhIGZhbGxiYWNrIGZvciBzYWZldHlcbiAgICAgICAgY29uc3QgZWZmZWN0aXZlU3BlY1ZlcnNpb24gPSBkYXRhLnNwZWNWZXJzaW9uID8/IFNQRUNfVkVSU0lPTl9DVVJSRU5UO1xuXG4gICAgICAgIC8vIEhlbHBlciB0byBjaGVjayBpZiBydW4gaXMgaW4gdGVybWluYWwgc3RhdGVcbiAgICAgICAgY29uc3QgaXNSdW5UZXJtaW5hbCA9IChzdGF0dXM6IHN0cmluZykgPT5cbiAgICAgICAgICBbJ2NvbXBsZXRlZCcsICdmYWlsZWQnLCAnY2FuY2VsbGVkJ10uaW5jbHVkZXMoc3RhdHVzKTtcblxuICAgICAgICAvLyBIZWxwZXIgdG8gY2hlY2sgaWYgc3RlcCBpcyBpbiB0ZXJtaW5hbCBzdGF0ZVxuICAgICAgICBjb25zdCBpc1N0ZXBUZXJtaW5hbCA9IChzdGF0dXM6IHN0cmluZykgPT5cbiAgICAgICAgICBbJ2NvbXBsZXRlZCcsICdmYWlsZWQnLCAnY2FuY2VsbGVkJ10uaW5jbHVkZXMoc3RhdHVzKTtcblxuICAgICAgICAvLyBHZXQgY3VycmVudCBydW4gc3RhdGUgZm9yIHZhbGlkYXRpb24gKGlmIG5vdCBjcmVhdGluZyBhIG5ldyBydW4pXG4gICAgICAgIC8vIFNraXAgcnVuIHZhbGlkYXRpb24gZm9yIHN0ZXBfY29tcGxldGVkIGFuZCBzdGVwX3JldHJ5aW5nIC0gdGhleSBvbmx5IG9wZXJhdGVcbiAgICAgICAgLy8gb24gcnVubmluZyBzdGVwcywgYW5kIHJ1bm5pbmcgc3RlcHMgYXJlIGFsd2F5cyBhbGxvd2VkIHRvIG1vZGlmeSByZWdhcmRsZXNzXG4gICAgICAgIC8vIG9mIHJ1biBzdGF0ZS4gVGhpcyBvcHRpbWl6YXRpb24gc2F2ZXMgZmlsZXN5c3RlbSByZWFkcyBwZXIgc3RlcCBldmVudC5cbiAgICAgICAgbGV0IGN1cnJlbnRSdW46IFdvcmtmbG93UnVuIHwgbnVsbCA9IG51bGw7XG4gICAgICAgIGNvbnN0IHNraXBSdW5WYWxpZGF0aW9uRXZlbnRzID0gWydzdGVwX2NvbXBsZXRlZCcsICdzdGVwX3JldHJ5aW5nJ107XG4gICAgICAgIGlmIChcbiAgICAgICAgICBkYXRhLmV2ZW50VHlwZSAhPT0gJ3J1bl9jcmVhdGVkJyAmJlxuICAgICAgICAgICFza2lwUnVuVmFsaWRhdGlvbkV2ZW50cy5pbmNsdWRlcyhkYXRhLmV2ZW50VHlwZSlcbiAgICAgICAgKSB7XG4gICAgICAgICAgY3VycmVudFJ1biA9IGF3YWl0IHJlYWRKU09OV2l0aEZhbGxiYWNrKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICdydW5zJyxcbiAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgV29ya2Zsb3dSdW5TY2hlbWEsXG4gICAgICAgICAgICB0YWdcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gUmVzaWxpZW50IHN0YXJ0OiBydW5fc3RhcnRlZCBvbiBub24tZXhpc3RlbnQgcnVuIHdpdGggZXZlbnREYXRhXG4gICAgICAgICAgLy8gY3JlYXRlcyB0aGUgcnVuIGZpcnN0LCBzbyB0aGUgcXVldWUgY2FuIGJvb3RzdHJhcCBhIHJ1biB0aGF0XG4gICAgICAgICAgLy8gZmFpbGVkIHRvIGNyZWF0ZSBkdXJpbmcgc3RhcnQoKS5cbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICBkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJyAmJlxuICAgICAgICAgICAgIWN1cnJlbnRSdW4gJiZcbiAgICAgICAgICAgICdldmVudERhdGEnIGluIGRhdGEgJiZcbiAgICAgICAgICAgIGRhdGEuZXZlbnREYXRhXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICBjb25zdCBydW5JbnB1dERhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7XG4gICAgICAgICAgICAgIGRlcGxveW1lbnRJZD86IHN0cmluZztcbiAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lPzogc3RyaW5nO1xuICAgICAgICAgICAgICBpbnB1dD86IGFueTtcbiAgICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dD86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gICAgICAgICAgICAgIGF0dHJpYnV0ZXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICAgICAgICAgICAgICBhbGxvd1Jlc2VydmVkQXR0cmlidXRlcz86IHRydWU7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICBydW5JbnB1dERhdGEuZGVwbG95bWVudElkICYmXG4gICAgICAgICAgICAgIHJ1bklucHV0RGF0YS53b3JrZmxvd05hbWUgJiZcbiAgICAgICAgICAgICAgcnVuSW5wdXREYXRhLmlucHV0ICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICB2YWxpZGF0ZUF0dHJpYnV0ZUNoYW5nZXMoXG4gICAgICAgICAgICAgICAgT2JqZWN0LmVudHJpZXMocnVuSW5wdXREYXRhLmF0dHJpYnV0ZXMgPz8ge30pLm1hcChcbiAgICAgICAgICAgICAgICAgIChba2V5LCB2YWx1ZV0pID0+ICh7IGtleSwgdmFsdWUgfSlcbiAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGFsbG93UmVzZXJ2ZWRBdHRyaWJ1dGVzOlxuICAgICAgICAgICAgICAgICAgICBydW5JbnB1dERhdGEuYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXMgPT09IHRydWUsXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAvLyBBdG9taWNhbGx5IHRyeSB0byBwdWJsaXNoIHRoZSBydW4gZW50aXR5IHNvIG9ubHkgdGhlIGZpcnN0XG4gICAgICAgICAgICAgIC8vIHdyaXRlciB3aW5zLCBwcmV2ZW50aW5nIGEgVE9DVE9VIHJhY2Ugd2hlcmUgYSBjb25jdXJyZW50XG4gICAgICAgICAgICAgIC8vIHJ1bl9jcmVhdGVkIGZyb20gc3RhcnQoKSBjb3VsZCBvdmVyd3JpdGUgYSBydW4gdGhhdCB3YXNcbiAgICAgICAgICAgICAgLy8gYWxyZWFkeSB0cmFuc2l0aW9uZWQgdG8gJ3J1bm5pbmcnLlxuICAgICAgICAgICAgICBjb25zdCBjcmVhdGVkUnVuOiBXb3JrZmxvd1J1biA9IHtcbiAgICAgICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgZGVwbG95bWVudElkOiBydW5JbnB1dERhdGEuZGVwbG95bWVudElkLFxuICAgICAgICAgICAgICAgIHN0YXR1czogJ3BlbmRpbmcnLFxuICAgICAgICAgICAgICAgIHdvcmtmbG93TmFtZTogcnVuSW5wdXREYXRhLndvcmtmbG93TmFtZSxcbiAgICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dDogcnVuSW5wdXREYXRhLmV4ZWN1dGlvbkNvbnRleHQsXG4gICAgICAgICAgICAgICAgaW5wdXQ6IHJ1bklucHV0RGF0YS5pbnB1dCxcbiAgICAgICAgICAgICAgICBvdXRwdXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBlcnJvcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgYXR0cmlidXRlczogcnVuSW5wdXREYXRhLmF0dHJpYnV0ZXMgPz8ge30sXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIGNvbnN0IHJ1blBhdGggPSB0YWdnZWRQYXRoKGJhc2VkaXIsICdydW5zJywgZWZmZWN0aXZlUnVuSWQsIHRhZyk7XG4gICAgICAgICAgICAgIGNvbnN0IGNyZWF0ZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShcbiAgICAgICAgICAgICAgICBydW5QYXRoLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGNyZWF0ZWRSdW4sIGpzb25SZXBsYWNlcilcbiAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICBpZiAoY3JlYXRlZCkge1xuICAgICAgICAgICAgICAgIC8vIFdlIGNyZWF0ZWQgdGhlIHJ1biDigJQgYWxzbyB3cml0ZSB0aGUgcnVuX2NyZWF0ZWQgZXZlbnQuXG4gICAgICAgICAgICAgICAgY29uc3QgcnVuQ3JlYXRlZEV2ZW50SWQgPSBgZXZudF8ke21vbm90b25pY1VsaWQoKX1gO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJ1bkNyZWF0ZWRFdmVudDogRXZlbnQgPSB7XG4gICAgICAgICAgICAgICAgICBldmVudFR5cGU6ICdydW5fY3JlYXRlZCcsXG4gICAgICAgICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgICBldmVudElkOiBydW5DcmVhdGVkRXZlbnRJZCxcbiAgICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgIGRlcGxveW1lbnRJZDogcnVuSW5wdXREYXRhLmRlcGxveW1lbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lOiBydW5JbnB1dERhdGEud29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgICAgICBpbnB1dDogcnVuSW5wdXREYXRhLmlucHV0LFxuICAgICAgICAgICAgICAgICAgICBleGVjdXRpb25Db250ZXh0OiBydW5JbnB1dERhdGEuZXhlY3V0aW9uQ29udGV4dCxcbiAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRlczogcnVuSW5wdXREYXRhLmF0dHJpYnV0ZXMsXG4gICAgICAgICAgICAgICAgICAgIGFsbG93UmVzZXJ2ZWRBdHRyaWJ1dGVzOlxuICAgICAgICAgICAgICAgICAgICAgIHJ1bklucHV0RGF0YS5hbGxvd1Jlc2VydmVkQXR0cmlidXRlcyxcbiAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBjb25zdCBjcmVhdGVkQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7cnVuQ3JlYXRlZEV2ZW50SWR9YDtcbiAgICAgICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdldmVudHMnLCBjcmVhdGVkQ29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgICAgICAgcnVuQ3JlYXRlZEV2ZW50XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBjdXJyZW50UnVuID0gY3JlYXRlZFJ1bjtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBSdW4gYWxyZWFkeSBleGlzdHMgKGNvbmN1cnJlbnQgcnVuX2NyZWF0ZWQgd29uIHRoZVxuICAgICAgICAgICAgICAgIC8vIHJhY2UpLiBSZS1yZWFkIGl0IHNvIGRvd25zdHJlYW0gbG9naWMgc2VlcyB0aGUgcmVhbCBzdGF0ZS5cbiAgICAgICAgICAgICAgICBjdXJyZW50UnVuID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAgICAgJ3J1bnMnLFxuICAgICAgICAgICAgICAgICAgZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgICBXb3JrZmxvd1J1blNjaGVtYSxcbiAgICAgICAgICAgICAgICAgIHRhZ1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBydW5fZmFpbGVkIG9uIGEgbm9uLWV4aXN0ZW50IHJ1biBpcyByZWplY3RlZCB0byBtYXRjaCB0aGVcbiAgICAgICAgLy8gcG9zdGdyZXMgYW5kIHZlcmNlbCB3b3JsZHMsIHdoaWNoIGJvdGggc3VyZmFjZSB0aGlzIGFzIGFcbiAgICAgICAgLy8gV29ya2Zsb3dSdW5Ob3RGb3VuZEVycm9yIHJhdGhlciB0aGFuIHNpbGVudGx5IHBlcnNpc3RpbmcgYW5cbiAgICAgICAgLy8gZXZlbnQgZm9yIGEgcnVuIHRoYXQgd2FzIG5ldmVyIGNyZWF0ZWQuXG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9mYWlsZWQnICYmICFjdXJyZW50UnVuKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFdvcmtmbG93UnVuTm90Rm91bmRFcnJvcihlZmZlY3RpdmVSdW5JZCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAnYXR0cl9zZXQnICYmICFjdXJyZW50UnVuKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFdvcmtmbG93UnVuTm90Rm91bmRFcnJvcihlZmZlY3RpdmVSdW5JZCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8gVkVSU0lPTiBDT01QQVRJQklMSVRZOiBDaGVjayBydW4gc3BlYyB2ZXJzaW9uXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBGb3IgZXZlbnRzIHRoYXQgaGF2ZSBmZXRjaGVkIHRoZSBydW4sIGNoZWNrIHZlcnNpb24gY29tcGF0aWJpbGl0eS5cbiAgICAgICAgLy8gU2tpcCBmb3IgcnVuX2NyZWF0ZWQgKG5vIGV4aXN0aW5nIHJ1bikgYW5kIHJ1bnRpbWUgZXZlbnRzIChzdGVwX2NvbXBsZXRlZCwgc3RlcF9yZXRyeWluZykuXG4gICAgICAgIGlmIChjdXJyZW50UnVuKSB7XG4gICAgICAgICAgLy8gQ2hlY2sgaWYgcnVuIHJlcXVpcmVzIGEgbmV3ZXIgd29ybGQgdmVyc2lvblxuICAgICAgICAgIGlmIChyZXF1aXJlc05ld2VyV29ybGQoY3VycmVudFJ1bi5zcGVjVmVyc2lvbikpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBSdW5Ob3RTdXBwb3J0ZWRFcnJvcihcbiAgICAgICAgICAgICAgY3VycmVudFJ1bi5zcGVjVmVyc2lvbiEsXG4gICAgICAgICAgICAgIFNQRUNfVkVSU0lPTl9DVVJSRU5UXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFJvdXRlIHRvIGxlZ2FjeSBoYW5kbGVyIGZvciBwcmUtZXZlbnQtc291cmNpbmcgcnVuc1xuICAgICAgICAgIGlmIChpc0xlZ2FjeVNwZWNWZXJzaW9uKGN1cnJlbnRSdW4uc3BlY1ZlcnNpb24pKSB7XG4gICAgICAgICAgICByZXR1cm4gaGFuZGxlTGVnYWN5RXZlbnQoXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICBkYXRhLFxuICAgICAgICAgICAgICBjdXJyZW50UnVuLFxuICAgICAgICAgICAgICBwYXJhbXNcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIFZBTElEQVRJT046IFRlcm1pbmFsIHN0YXRlIGFuZCBldmVudCBvcmRlcmluZyBjaGVja3NcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICAgICAgLy8gTGF6eSBzdGVwIHN0YXJ0OiBhIHN0ZXBfc3RhcnRlZCBjYXJyeWluZyBzdGVwLWNyZWF0aW9uIGRhdGFcbiAgICAgICAgLy8gKHN0ZXBOYW1lICsgaW5wdXQpIGlzIGFsbG93ZWQgdG8gYXJyaXZlIHdpdGggbm8gcHJpb3Igc3RlcF9jcmVhdGVkXG4gICAgICAgIC8vIOKAlCBpdCBjcmVhdGVzIHRoZSBzdGVwIG9uIHRoZSBmbHkgKHNlZSB0aGUgbWF0ZXJpYWxpemF0aW9uIGJsb2NrXG4gICAgICAgIC8vIGJlbG93KS4gVGhpcyBtaXJyb3JzIHRoZSByZXNpbGllbnQgcnVuX3N0YXJ0ZWQgcGF0aC4gRGV0ZWN0IGl0IGhlcmVcbiAgICAgICAgLy8gc28gdGhlIGVudGl0eS1jcmVhdGlvbiB0ZXJtaW5hbC1ydW4gZ3VhcmQgdHJlYXRzIGl0IGxpa2UgYSBjcmVhdGlvblxuICAgICAgICAvLyBhbmQgdGhlIFwic3RlcCBtdXN0IGV4aXN0XCIgb3JkZXJpbmcgZ3VhcmQgZG9lc24ndCByZWplY3QgaXQuXG4gICAgICAgIGNvbnN0IGxhenlTdGVwU3RhcnQgPVxuICAgICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9zdGFydGVkJyAmJlxuICAgICAgICAgICdldmVudERhdGEnIGluIGRhdGEgJiZcbiAgICAgICAgICAhIWRhdGEuZXZlbnREYXRhICYmXG4gICAgICAgICAgdHlwZW9mIChkYXRhLmV2ZW50RGF0YSBhcyB7IHN0ZXBOYW1lPzogdW5rbm93biB9KS5zdGVwTmFtZSA9PT1cbiAgICAgICAgICAgICdzdHJpbmcnICYmXG4gICAgICAgICAgKGRhdGEuZXZlbnREYXRhIGFzIHsgaW5wdXQ/OiB1bmtub3duIH0pLmlucHV0ICE9PSB1bmRlZmluZWQ7XG5cbiAgICAgICAgLy8gUnVuIHRlcm1pbmFsIHN0YXRlIHZhbGlkYXRpb25cbiAgICAgICAgaWYgKGN1cnJlbnRSdW4gJiYgaXNSdW5UZXJtaW5hbChjdXJyZW50UnVuLnN0YXR1cykpIHtcbiAgICAgICAgICBjb25zdCBydW5UZXJtaW5hbEV2ZW50cyA9IFtcbiAgICAgICAgICAgICdydW5fc3RhcnRlZCcsXG4gICAgICAgICAgICAncnVuX2NvbXBsZXRlZCcsXG4gICAgICAgICAgICAncnVuX2ZhaWxlZCcsXG4gICAgICAgICAgXTtcblxuICAgICAgICAgIC8vIElkZW1wb3RlbnQgb3BlcmF0aW9uOiBydW5fY2FuY2VsbGVkIG9uIGFscmVhZHkgY2FuY2VsbGVkIHJ1biBpcyBhbGxvd2VkXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdydW5fY2FuY2VsbGVkJyAmJlxuICAgICAgICAgICAgY3VycmVudFJ1bi5zdGF0dXMgPT09ICdjYW5jZWxsZWQnXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICAvLyBSZXR1cm4gZXhpc3Rpbmcgc3RhdGUgKGlkZW1wb3RlbnQpXG4gICAgICAgICAgICBjb25zdCBldmVudDogRXZlbnQgPSB7XG4gICAgICAgICAgICAgIC4uLmRhdGEsXG4gICAgICAgICAgICAgIHJ1bklkOiBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBlZmZlY3RpdmVTcGVjVmVyc2lvbixcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjb25zdCBjb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtldmVudElkfWA7XG4gICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ2V2ZW50cycsIGNvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgZXZlbnRcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCByZXNvbHZlRGF0YSA9XG4gICAgICAgICAgICAgIHBhcmFtcz8ucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgZXZlbnQ6IHN0cmlwRXZlbnREYXRhUmVmcyhldmVudCwgcmVzb2x2ZURhdGEpLFxuICAgICAgICAgICAgICBydW46IGN1cnJlbnRSdW4sXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEZvciBydW5fc3RhcnRlZCBvbiB0ZXJtaW5hbCBydW5zLCB1c2UgUnVuRXhwaXJlZEVycm9yIHNvIHRoZVxuICAgICAgICAgIC8vIHJ1bnRpbWUga25vd3MgdG8gZXhpdCB3aXRob3V0IHJldHJ5aW5nLlxuICAgICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFJ1bkV4cGlyZWRFcnJvcihcbiAgICAgICAgICAgICAgYFdvcmtmbG93IHJ1biBcIiR7ZWZmZWN0aXZlUnVuSWR9XCIgaXMgYWxyZWFkeSBpbiB0ZXJtaW5hbCBzdGF0ZSBcIiR7Y3VycmVudFJ1bi5zdGF0dXN9XCJgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIE90aGVyIHJ1biBzdGF0ZSB0cmFuc2l0aW9ucyBhcmUgbm90IGFsbG93ZWQgb24gdGVybWluYWwgcnVuc1xuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIHJ1blRlcm1pbmFsRXZlbnRzLmluY2x1ZGVzKGRhdGEuZXZlbnRUeXBlKSB8fFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdydW5fY2FuY2VsbGVkJ1xuICAgICAgICAgICkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBDYW5ub3QgdHJhbnNpdGlvbiBydW4gZnJvbSB0ZXJtaW5hbCBzdGF0ZSBcIiR7Y3VycmVudFJ1bi5zdGF0dXN9XCJgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENyZWF0aW5nIG5ldyBlbnRpdGllcyBvbiB0ZXJtaW5hbCBydW5zIGlzIG5vdCBhbGxvd2VkLiBBIGxhenlcbiAgICAgICAgICAvLyBzdGVwX3N0YXJ0ZWQgY3JlYXRlcyBhIHN0ZXAsIHNvIGl0IGlzIHJlamVjdGVkIGhlcmUgdG9vIOKAlCBhIGJhcmVcbiAgICAgICAgICAvLyAobm9uLWxhenkpIHN0ZXBfc3RhcnRlZCBmYWxscyB0aHJvdWdoIHRvIHRoZSBzdGVwLXZhbGlkYXRpb25cbiAgICAgICAgICAvLyBibG9jayBiZWxvdywgd2hpY2ggdXNlcyBSdW5FeHBpcmVkRXJyb3IgZm9yIHRlcm1pbmFsIHJ1bnMuXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2NyZWF0ZWQnIHx8XG4gICAgICAgICAgICBkYXRhLmV2ZW50VHlwZSA9PT0gJ2hvb2tfY3JlYXRlZCcgfHxcbiAgICAgICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnd2FpdF9jcmVhdGVkJyB8fFxuICAgICAgICAgICAgbGF6eVN0ZXBTdGFydFxuICAgICAgICAgICkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBDYW5ub3QgY3JlYXRlIG5ldyBlbnRpdGllcyBvbiBydW4gaW4gdGVybWluYWwgc3RhdGUgXCIke2N1cnJlbnRSdW4uc3RhdHVzfVwiYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdhdHRyX3NldCcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICBgQ2Fubm90IHNldCBhdHRyaWJ1dGVzIG9uIHJ1biBpbiB0ZXJtaW5hbCBzdGF0ZSBcIiR7Y3VycmVudFJ1bi5zdGF0dXN9XCJgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFN0ZXAtcmVsYXRlZCBldmVudCB2YWxpZGF0aW9uIChvcmRlcmluZyBhbmQgdGVybWluYWwgc3RhdGUpXG4gICAgICAgIC8vIFN0b3JlIGV4aXN0aW5nU3RlcCBzbyB3ZSBjYW4gcmV1c2UgaXQgbGF0ZXIgKGF2b2lkIGRvdWJsZSByZWFkKVxuICAgICAgICBsZXQgdmFsaWRhdGVkU3RlcDogU3RlcCB8IG51bGwgPSBudWxsO1xuICAgICAgICBjb25zdCBzdGVwRXZlbnRzID0gW1xuICAgICAgICAgICdzdGVwX3N0YXJ0ZWQnLFxuICAgICAgICAgICdzdGVwX2NvbXBsZXRlZCcsXG4gICAgICAgICAgJ3N0ZXBfZmFpbGVkJyxcbiAgICAgICAgICAnc3RlcF9yZXRyeWluZycsXG4gICAgICAgIF07XG4gICAgICAgIGlmIChzdGVwRXZlbnRzLmluY2x1ZGVzKGRhdGEuZXZlbnRUeXBlKSAmJiBkYXRhLmNvcnJlbGF0aW9uSWQpIHtcbiAgICAgICAgICBjb25zdCBzdGVwQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgdmFsaWRhdGVkU3RlcCA9IGF3YWl0IHJlYWRKU09OV2l0aEZhbGxiYWNrKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICdzdGVwcycsXG4gICAgICAgICAgICBzdGVwQ29tcG9zaXRlS2V5LFxuICAgICAgICAgICAgU3RlcFNjaGVtYSxcbiAgICAgICAgICAgIHRhZ1xuICAgICAgICAgICk7XG5cbiAgICAgICAgICAvLyBFdmVudCBvcmRlcmluZzogc3RlcCBtdXN0IGV4aXN0IGJlZm9yZSB0aGVzZSBldmVudHMg4oCUIGV4Y2VwdCBvblxuICAgICAgICAgIC8vIHRoZSBsYXp5LXN0YXJ0IHBhdGgsIHdoZXJlIHN0ZXBfc3RhcnRlZCBjcmVhdGVzIHRoZSBzdGVwIGl0c2VsZi5cbiAgICAgICAgICBpZiAoIXZhbGlkYXRlZFN0ZXAgJiYgIWxhenlTdGVwU3RhcnQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBXb3JrZmxvd1dvcmxkRXJyb3IoXG4gICAgICAgICAgICAgIGBTdGVwIFwiJHtkYXRhLmNvcnJlbGF0aW9uSWR9XCIgbm90IGZvdW5kYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBMYXp5IHN0YXJ0IGV4YWN0bHktb25jZSBnYXRlOiBhIGxhenkgc3RlcF9zdGFydGVkIGFsd2F5cyBDUkVBVEVTXG4gICAgICAgICAgLy8gdGhlIHN0ZXAgKHRoZSBvd25lZC1pbmxpbmUgcGF0aCBvbmx5IHNlbmRzIG9uZSBmb3IgYSBzdGVwIHdob3NlXG4gICAgICAgICAgLy8gc3RlcF9jcmVhdGVkIGl0IGRlZmVycmVkKS4gSWYgdGhlIHN0ZXAgYWxyZWFkeSBleGlzdHMsIGEgY29uY3VycmVudFxuICAgICAgICAgIC8vIGhhbmRsZXIgd29uIHRoZSBjcmVhdGUg4oCUIHRoaXMgY2FsbGVyIGlzIGEgbG9zZXIgYW5kIG11c3Qgbm90IHN0YXJ0XG4gICAgICAgICAgLy8gb3IgcnVuIHRoZSBzdGVwLiBUaHJvdyBFbnRpdHlDb25mbGljdEVycm9yIHNvIHRoZSBydW50aW1lJ3NcbiAgICAgICAgICAvLyBleGVjdXRlU3RlcCBtYXBzIGl0IHRvIGBza2lwcGVkYC4gVGhpcyBpcyBjcml0aWNhbDogdGhlIHBsYWluIHN0YXJ0XG4gICAgICAgICAgLy8gdHJhbnNpdGlvbiBiZWxvdyBwZXJtaXRzIHJlLXN0YXJ0aW5nIGEgbm9uLXRlcm1pbmFsIHN0ZXAgKHJldHJpZXNcbiAgICAgICAgICAvLyByZWx5IG9uIHRoYXQpLCBzbyB3aXRob3V0IHRoaXMgZ2F0ZSBhIGxvc2VyIHdvdWxkIHJlLXN0YXJ0IGFcbiAgICAgICAgICAvLyBydW5uaW5nIHN0ZXAgYW5kIHJ1biB0aGUgYm9keSBhIHNlY29uZCB0aW1lLlxuICAgICAgICAgIGlmIChsYXp5U3RlcFN0YXJ0ICYmIHZhbGlkYXRlZFN0ZXApIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICBgU3RlcCBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIGFscmVhZHkgY3JlYXRlZGBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gU3RlcCB0ZXJtaW5hbCBzdGF0ZSB2YWxpZGF0aW9uLiB2YWxpZGF0ZWRTdGVwIGNhbiBiZSBudWxsIG9ubHkgb25cbiAgICAgICAgICAvLyB0aGUgbGF6eS1zdGFydCBwYXRoIChubyBzdGVwIHlldCkg4oCUIHRoZXJlIGlzIG5vdGhpbmcgdGVybWluYWwgdG9cbiAgICAgICAgICAvLyBndWFyZCBhZ2FpbnN0IGluIHRoYXQgY2FzZSwgc28gdGhlc2UgY2hlY2tzIGFyZSBza2lwcGVkLlxuICAgICAgICAgIGlmICh2YWxpZGF0ZWRTdGVwKSB7XG4gICAgICAgICAgICBpZiAoaXNTdGVwVGVybWluYWwodmFsaWRhdGVkU3RlcC5zdGF0dXMpKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICAgIGBDYW5ub3QgbW9kaWZ5IHN0ZXAgaW4gdGVybWluYWwgc3RhdGUgXCIke3ZhbGlkYXRlZFN0ZXAuc3RhdHVzfVwiYFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBPbiB0ZXJtaW5hbCBydW5zOiBvbmx5IGFsbG93IGNvbXBsZXRpbmcvZmFpbGluZyBpbi1wcm9ncmVzcyBzdGVwc1xuICAgICAgICAgICAgaWYgKGN1cnJlbnRSdW4gJiYgaXNSdW5UZXJtaW5hbChjdXJyZW50UnVuLnN0YXR1cykpIHtcbiAgICAgICAgICAgICAgaWYgKHZhbGlkYXRlZFN0ZXAuc3RhdHVzICE9PSAncnVubmluZycpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgUnVuRXhwaXJlZEVycm9yKFxuICAgICAgICAgICAgICAgICAgYENhbm5vdCBtb2RpZnkgbm9uLXJ1bm5pbmcgc3RlcCBvbiBydW4gaW4gdGVybWluYWwgc3RhdGUgXCIke2N1cnJlbnRSdW4uc3RhdHVzfVwiYFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBIb29rLXJlbGF0ZWQgZXZlbnQgdmFsaWRhdGlvbiAob3JkZXJpbmcpXG4gICAgICAgIGNvbnN0IGhvb2tFdmVudHNSZXF1aXJpbmdFeGlzdGVuY2UgPSBbJ2hvb2tfZGlzcG9zZWQnLCAnaG9va19yZWNlaXZlZCddO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgaG9va0V2ZW50c1JlcXVpcmluZ0V4aXN0ZW5jZS5pbmNsdWRlcyhkYXRhLmV2ZW50VHlwZSkgJiZcbiAgICAgICAgICBkYXRhLmNvcnJlbGF0aW9uSWRcbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgZXhpc3RpbmdIb29rID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJ2hvb2tzJyxcbiAgICAgICAgICAgIGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIEhvb2tTY2hlbWEsXG4gICAgICAgICAgICB0YWdcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgaWYgKCFleGlzdGluZ0hvb2spIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBIb29rTm90Rm91bmRFcnJvcihkYXRhLmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBgZXZlbnRgIG1heSBiZSByZWFzc2lnbmVkIGxhdGVyIGluIHRoZSBgaG9va19jcmVhdGVkYFxuICAgICAgICAvLyBkZWR1cC1yZWNvdmVyeSBicmFuY2ggdG8gc3dhcCBpbiBhIGNhbm9uaWNhbCBldmVudElkIC9cbiAgICAgICAgLy8gY3JlYXRlZEF0IHBlcnNpc3RlZCBpbiB0aGUgZHVyYWJsZSB0b2tlbiBjbGFpbSBzb1xuICAgICAgICAvLyBjb25jdXJyZW50IC8gY3Jvc3MtcHJvY2VzcyB3b3JrZXJzIGNvbnZlcmdlIG9uIGEgc2luZ2xlXG4gICAgICAgIC8vIGV2ZW50IGluIHRoZSBsb2cuXG4gICAgICAgIGxldCBldmVudDogRXZlbnQgPSB7XG4gICAgICAgICAgLi4uZGF0YSxcbiAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgIH07XG4gICAgICAgIC8vIFN0cmlwIGV2ZW50RGF0YSBmcm9tIHJ1bl9zdGFydGVkIOKAlCBpdCBiZWxvbmdzIG9uIHJ1bl9jcmVhdGVkIG9ubHkuXG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJyAmJiAnZXZlbnREYXRhJyBpbiBldmVudCkge1xuICAgICAgICAgIGRlbGV0ZSAoZXZlbnQgYXMgYW55KS5ldmVudERhdGE7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU3RyaXAgb25seSB0aGUgc3RlcCBgaW5wdXRgIGZyb20gdGhlIGxhenkgc3RlcF9zdGFydGVkIGV2ZW50IHJvdyDigJRcbiAgICAgICAgLy8gaXQgYmVsb25ncyBvbiB0aGUgc3ludGhldGljIHN0ZXBfY3JlYXRlZCB3cml0dGVuIGFib3ZlLiBzdGVwTmFtZSBpc1xuICAgICAgICAvLyBwcmVzZXJ2ZWQgZm9yIHRoZSBjbGllbnQgcmVwbGF5IGNvbnN1bWVyJ3Mgc3RlcC1uYW1lIGRpdmVyZ2VuY2VcbiAgICAgICAgLy8gY2hlY2sgKHBhY2thZ2VzL2NvcmUvc3JjL3N0ZXAudHMpLlxuICAgICAgICBpZiAoXG4gICAgICAgICAgbGF6eVN0ZXBTdGFydCAmJlxuICAgICAgICAgICdldmVudERhdGEnIGluIGV2ZW50ICYmXG4gICAgICAgICAgKGV2ZW50IGFzIHsgZXZlbnREYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfSkuZXZlbnREYXRhXG4gICAgICAgICkge1xuICAgICAgICAgIGNvbnN0IHsgaW5wdXQ6IF9zdHJpcHBlZElucHV0LCAuLi5yZXN0IH0gPSAoXG4gICAgICAgICAgICBldmVudCBhcyB7IGV2ZW50RGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfVxuICAgICAgICAgICkuZXZlbnREYXRhO1xuICAgICAgICAgIChldmVudCBhcyB7IGV2ZW50RGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfSkuZXZlbnREYXRhID0gcmVzdDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRyYWNrIGVudGl0eSBjcmVhdGVkL3VwZGF0ZWQgZm9yIEV2ZW50UmVzdWx0XG4gICAgICAgIGxldCBydW46IFdvcmtmbG93UnVuIHwgdW5kZWZpbmVkO1xuICAgICAgICBsZXQgc3RlcDogU3RlcCB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGhvb2s6IEhvb2sgfCB1bmRlZmluZWQ7XG4gICAgICAgIGxldCB3YWl0OiBXYWl0IHwgdW5kZWZpbmVkO1xuICAgICAgICAvLyBMYXp5IHN0ZXAgc3RhcnQ6IHNldCB0cnVlIHdoZW4gdGhpcyBzdGVwX3N0YXJ0ZWQgYXRvbWljYWxseSBjcmVhdGVkXG4gICAgICAgIC8vIHRoZSBzdGVwICh0aGUgY2FsbGVyIHdvbiB0aGUgY3JlYXRlLWNsYWltKS4gU3VyZmFjZWQgb24gRXZlbnRSZXN1bHRcbiAgICAgICAgLy8gYXMgdGhlIHJ1bnRpbWUncyBleGFjdGx5LW9uY2Ugb3duZXJzaGlwIHNpZ25hbC5cbiAgICAgICAgbGV0IHN0ZXBDcmVhdGVkTGF6aWx5ID0gZmFsc2U7XG4gICAgICAgIC8vIEZvciBgaG9va19jcmVhdGVkYCwgdGhlIGhvb2sgZW50aXR5IHdyaXRlIGlzIGRlZmVycmVkIHVudGlsXG4gICAgICAgIC8vIEFGVEVSIHRoZSBvdXRlciBldmVudCBwdWJsaXNoIHN1Y2NlZWRzLCBzbyBhIHJldHJ5IHRoYXRcbiAgICAgICAgLy8gY29sbGlkZXMgd2l0aCBhbiBhbHJlYWR5LXB1Ymxpc2hlZCBgaG9va19jcmVhdGVkYCBkb2VzIG5vdFxuICAgICAgICAvLyBtdXRhdGUgdGhlIGR1cmFibGUgaG9vayBlbnRpdHkgd2l0aCB0aGUgcmV0cnkncyBwYXlsb2FkLlxuICAgICAgICAvLyBgaG9va0VudGl0eVdyaXRlT3B0aW9uc2AgY2FycmllcyB0aGUgYHsgb3ZlcndyaXRlIH1gIG1vZGVcbiAgICAgICAgLy8gY2hvc2VuIGJ5IHRoZSBkZWR1cC1yZWNvdmVyeSBicmFuY2ggYWJvdmUgKHVuZGVmaW5lZCBmb3JcbiAgICAgICAgLy8gZmlyc3Qgd3JpdGVycywgYHsgb3ZlcndyaXRlOiB0cnVlIH1gIGZvciByZXRyaWVzIHRoYXQgbWF5XG4gICAgICAgIC8vIGJlIHJlcGFpcmluZyBhbiBvcnBoYW5lZCBwYXJ0aWFsIHdyaXRlKS5cbiAgICAgICAgbGV0IGhvb2tFbnRpdHlXcml0ZU9wdGlvbnM6IHsgb3ZlcndyaXRlOiBib29sZWFuIH0gfCB1bmRlZmluZWQ7XG5cbiAgICAgICAgLy8gQ3JlYXRlL3VwZGF0ZSBlbnRpdHkgYmFzZWQgb24gZXZlbnQgdHlwZSAoZXZlbnQtc291cmNlZCBhcmNoaXRlY3R1cmUpXG4gICAgICAgIC8vIFJ1biBsaWZlY3ljbGUgZXZlbnRzXG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9jcmVhdGVkJyAmJiAnZXZlbnREYXRhJyBpbiBkYXRhKSB7XG4gICAgICAgICAgY29uc3QgcnVuRGF0YSA9IGRhdGEuZXZlbnREYXRhIGFzIHtcbiAgICAgICAgICAgIGRlcGxveW1lbnRJZDogc3RyaW5nO1xuICAgICAgICAgICAgd29ya2Zsb3dOYW1lOiBzdHJpbmc7XG4gICAgICAgICAgICBpbnB1dDogU2VyaWFsaXplZERhdGE7XG4gICAgICAgICAgICBleGVjdXRpb25Db250ZXh0PzogUmVjb3JkPHN0cmluZywgYW55PjtcbiAgICAgICAgICAgIGF0dHJpYnV0ZXM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICAgICAgICAgICAgYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXM/OiB0cnVlO1xuICAgICAgICAgIH07XG4gICAgICAgICAgdmFsaWRhdGVBdHRyaWJ1dGVDaGFuZ2VzKFxuICAgICAgICAgICAgT2JqZWN0LmVudHJpZXMocnVuRGF0YS5hdHRyaWJ1dGVzID8/IHt9KS5tYXAoKFtrZXksIHZhbHVlXSkgPT4gKHtcbiAgICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgICB2YWx1ZSxcbiAgICAgICAgICAgIH0pKSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXM6IHJ1bkRhdGEuYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXMgPT09IHRydWUsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgKTtcbiAgICAgICAgICBydW4gPSB7XG4gICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICBkZXBsb3ltZW50SWQ6IHJ1bkRhdGEuZGVwbG95bWVudElkLFxuICAgICAgICAgICAgc3RhdHVzOiAncGVuZGluZycsXG4gICAgICAgICAgICB3b3JrZmxvd05hbWU6IHJ1bkRhdGEud29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgLy8gUHJvcGFnYXRlIHNwZWNWZXJzaW9uIGZyb20gdGhlIGV2ZW50IHRvIHRoZSBydW4gZW50aXR5XG4gICAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgICAgICBleGVjdXRpb25Db250ZXh0OiBydW5EYXRhLmV4ZWN1dGlvbkNvbnRleHQsXG4gICAgICAgICAgICBpbnB1dDogcnVuRGF0YS5pbnB1dCxcbiAgICAgICAgICAgIG91dHB1dDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIHN0YXJ0ZWRBdDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgY29tcGxldGVkQXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGF0dHJpYnV0ZXM6IHJ1bkRhdGEuYXR0cmlidXRlcyA/PyB7fSxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgfTtcbiAgICAgICAgICAvLyBBdG9taWNhbGx5IHB1Ymxpc2ggdGhlIHJ1biBlbnRpdHkgZmlsZSB3aXRob3V0IG92ZXJ3cml0aW5nIGFuXG4gICAgICAgICAgLy8gZXhpc3Rpbmcgd2lubmVyLiBUaGlzIHByZXZlbnRzIGEgVE9DVE9VIHJhY2Ugd2l0aCB0aGUgcmVzaWxpZW50XG4gICAgICAgICAgLy8gc3RhcnQgcGF0aCAocnVuX3N0YXJ0ZWQgb24gbm9uLWV4aXN0ZW50IHJ1bikgdGhhdCBjb3VsZCByZXN1bHQgaW5cbiAgICAgICAgICAvLyBkdXBsaWNhdGUgcnVuX2NyZWF0ZWQgZXZlbnRzIGluIHRoZSBldmVudCBsb2cuXG4gICAgICAgICAgY29uc3QgcnVuUGF0aCA9IHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3J1bnMnLCBlZmZlY3RpdmVSdW5JZCwgdGFnKTtcbiAgICAgICAgICBjb25zdCBjcmVhdGVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUoXG4gICAgICAgICAgICBydW5QYXRoLFxuICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkocnVuLCBqc29uUmVwbGFjZXIsIDIpXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoIWNyZWF0ZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICBgV29ya2Zsb3cgcnVuIFwiJHtlZmZlY3RpdmVSdW5JZH1cIiBhbHJlYWR5IGV4aXN0c2BcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAncnVuX3N0YXJ0ZWQnKSB7XG4gICAgICAgICAgLy8gUmV1c2UgY3VycmVudFJ1biBmcm9tIHZhbGlkYXRpb24gKGFscmVhZHkgcmVhZCBhYm92ZSlcbiAgICAgICAgICBpZiAoY3VycmVudFJ1bikge1xuICAgICAgICAgICAgLy8gSWYgYWxyZWFkeSBydW5uaW5nLCByZXR1cm4gdGhlIHJ1biB3aXRob3V0IGluc2VydGluZyBhXG4gICAgICAgICAgICAvLyBkdXBsaWNhdGUgZXZlbnQuICBUaGlzIG1ha2VzIHJ1bl9zdGFydGVkIGlkZW1wb3RlbnQgZm9yXG4gICAgICAgICAgICAvLyBjb25jdXJyZW50IGludm9jYXRpb25zLiAgV2Ugb21pdCBwcmVsb2FkZWQgZXZlbnRzIGhlcmVcbiAgICAgICAgICAgIC8vIGJlY2F1c2UgdGhpcyBpcyBhIHJhcmUgcmFjZS1jb25kaXRpb24gcGF0aCDigJQgdGhlIHJ1bnRpbWVcbiAgICAgICAgICAgIC8vIGZhbGxzIGJhY2sgdG8gbG9hZFdvcmtmbG93UnVuRXZlbnRzKCkuXG4gICAgICAgICAgICBpZiAoY3VycmVudFJ1bi5zdGF0dXMgPT09ICdydW5uaW5nJykge1xuICAgICAgICAgICAgICByZXR1cm4geyBydW46IGN1cnJlbnRSdW4gfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcnVuID0gYXdhaXQgd3JpdGVSdW5VbmRlckxpZmVjeWNsZUxvY2soXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICB0YWcsXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBydW5JZDogY3VycmVudFJ1bi5ydW5JZCxcbiAgICAgICAgICAgICAgICBkZXBsb3ltZW50SWQ6IGN1cnJlbnRSdW4uZGVwbG95bWVudElkLFxuICAgICAgICAgICAgICAgIHdvcmtmbG93TmFtZTogY3VycmVudFJ1bi53b3JrZmxvd05hbWUsXG4gICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGN1cnJlbnRSdW4uc3BlY1ZlcnNpb24sXG4gICAgICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dDogY3VycmVudFJ1bi5leGVjdXRpb25Db250ZXh0LFxuICAgICAgICAgICAgICAgIGlucHV0OiBjdXJyZW50UnVuLmlucHV0LFxuICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDogY3VycmVudFJ1bi5jcmVhdGVkQXQsXG4gICAgICAgICAgICAgICAgZXhwaXJlZEF0OiBjdXJyZW50UnVuLmV4cGlyZWRBdCxcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdydW5uaW5nJyxcbiAgICAgICAgICAgICAgICBvdXRwdXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBlcnJvcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgc3RhcnRlZEF0OiBjdXJyZW50UnVuLnN0YXJ0ZWRBdCA/PyBub3csXG4gICAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgYXR0cmlidXRlczogY3VycmVudFJ1bi5hdHRyaWJ1dGVzLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9jb21wbGV0ZWQnICYmICdldmVudERhdGEnIGluIGRhdGEpIHtcbiAgICAgICAgICBjb25zdCBjb21wbGV0ZWREYXRhID0gZGF0YS5ldmVudERhdGEgYXMgeyBvdXRwdXQ/OiBhbnkgfTtcbiAgICAgICAgICAvLyBSZXVzZSBjdXJyZW50UnVuIGZyb20gdmFsaWRhdGlvbiAoYWxyZWFkeSByZWFkIGFib3ZlKVxuICAgICAgICAgIGlmIChjdXJyZW50UnVuKSB7XG4gICAgICAgICAgICBydW4gPSBhd2FpdCB3cml0ZVJ1blVuZGVyTGlmZWN5Y2xlTG9jayhcbiAgICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICAgZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgIHRhZyxcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHJ1bklkOiBjdXJyZW50UnVuLnJ1bklkLFxuICAgICAgICAgICAgICAgIGRlcGxveW1lbnRJZDogY3VycmVudFJ1bi5kZXBsb3ltZW50SWQsXG4gICAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lOiBjdXJyZW50UnVuLndvcmtmbG93TmFtZSxcbiAgICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogY3VycmVudFJ1bi5zcGVjVmVyc2lvbixcbiAgICAgICAgICAgICAgICBleGVjdXRpb25Db250ZXh0OiBjdXJyZW50UnVuLmV4ZWN1dGlvbkNvbnRleHQsXG4gICAgICAgICAgICAgICAgaW5wdXQ6IGN1cnJlbnRSdW4uaW5wdXQsXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBjdXJyZW50UnVuLmNyZWF0ZWRBdCxcbiAgICAgICAgICAgICAgICBleHBpcmVkQXQ6IGN1cnJlbnRSdW4uZXhwaXJlZEF0LFxuICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdDogY3VycmVudFJ1bi5zdGFydGVkQXQsXG4gICAgICAgICAgICAgICAgc3RhdHVzOiAnY29tcGxldGVkJyxcbiAgICAgICAgICAgICAgICBvdXRwdXQ6IGNvbXBsZXRlZERhdGEub3V0cHV0LFxuICAgICAgICAgICAgICAgIGVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgY29tcGxldGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgICBhdHRyaWJ1dGVzOiBjdXJyZW50UnVuLmF0dHJpYnV0ZXMsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAgICAgICAgIGRlbGV0ZUFsbEhvb2tzRm9yUnVuKGJhc2VkaXIsIGVmZmVjdGl2ZVJ1bklkKSxcbiAgICAgICAgICAgICAgZGVsZXRlQWxsV2FpdHNGb3JSdW4oYmFzZWRpciwgZWZmZWN0aXZlUnVuSWQpLFxuICAgICAgICAgICAgXSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAncnVuX2ZhaWxlZCcgJiYgJ2V2ZW50RGF0YScgaW4gZGF0YSkge1xuICAgICAgICAgIGNvbnN0IGZhaWxlZERhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7XG4gICAgICAgICAgICBlcnJvcjogdW5rbm93bjtcbiAgICAgICAgICAgIGVycm9yQ29kZT86IHN0cmluZztcbiAgICAgICAgICB9O1xuICAgICAgICAgIC8vIFJldXNlIGN1cnJlbnRSdW4gZnJvbSB2YWxpZGF0aW9uIChhbHJlYWR5IHJlYWQgYWJvdmUpXG4gICAgICAgICAgaWYgKGN1cnJlbnRSdW4pIHtcbiAgICAgICAgICAgIC8vIFRoZSBlcnJvciBmaWVsZCBpcyBTZXJpYWxpemVkRGF0YSAoVWludDhBcnJheSkgcHJvZHVjZWQgYnlcbiAgICAgICAgICAgIC8vIGRlaHlkcmF0ZVJ1bkVycm9yLiBXZSBzdG9yZSBpdCB2ZXJiYXRpbSDigJQgY29uc3VtZXJzIGh5ZHJhdGUgaXRcbiAgICAgICAgICAgIC8vIHZpYSBoeWRyYXRlUnVuRXJyb3IgdG8gcmVjb25zdHJ1Y3QgdGhlIG9yaWdpbmFsIHRocm93biB2YWx1ZS5cbiAgICAgICAgICAgIHJ1biA9IGF3YWl0IHdyaXRlUnVuVW5kZXJMaWZlY3ljbGVMb2NrKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgdGFnLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcnVuSWQ6IGN1cnJlbnRSdW4ucnVuSWQsXG4gICAgICAgICAgICAgICAgZGVwbG95bWVudElkOiBjdXJyZW50UnVuLmRlcGxveW1lbnRJZCxcbiAgICAgICAgICAgICAgICB3b3JrZmxvd05hbWU6IGN1cnJlbnRSdW4ud29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBjdXJyZW50UnVuLnNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgIGV4ZWN1dGlvbkNvbnRleHQ6IGN1cnJlbnRSdW4uZXhlY3V0aW9uQ29udGV4dCxcbiAgICAgICAgICAgICAgICBpbnB1dDogY3VycmVudFJ1bi5pbnB1dCxcbiAgICAgICAgICAgICAgICBjcmVhdGVkQXQ6IGN1cnJlbnRSdW4uY3JlYXRlZEF0LFxuICAgICAgICAgICAgICAgIGV4cGlyZWRBdDogY3VycmVudFJ1bi5leHBpcmVkQXQsXG4gICAgICAgICAgICAgICAgc3RhcnRlZEF0OiBjdXJyZW50UnVuLnN0YXJ0ZWRBdCxcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdmYWlsZWQnLFxuICAgICAgICAgICAgICAgIG91dHB1dDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGVycm9yOiBmYWlsZWREYXRhLmVycm9yIGFzIFVpbnQ4QXJyYXksXG4gICAgICAgICAgICAgICAgZXJyb3JDb2RlOiBmYWlsZWREYXRhLmVycm9yQ29kZSxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgIGF0dHJpYnV0ZXM6IGN1cnJlbnRSdW4uYXR0cmlidXRlcyxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgICAgZGVsZXRlQWxsSG9va3NGb3JSdW4oYmFzZWRpciwgZWZmZWN0aXZlUnVuSWQpLFxuICAgICAgICAgICAgICBkZWxldGVBbGxXYWl0c0ZvclJ1bihiYXNlZGlyLCBlZmZlY3RpdmVSdW5JZCksXG4gICAgICAgICAgICBdKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdydW5fY2FuY2VsbGVkJykge1xuICAgICAgICAgIC8vIFJldXNlIGN1cnJlbnRSdW4gZnJvbSB2YWxpZGF0aW9uIChhbHJlYWR5IHJlYWQgYWJvdmUpXG4gICAgICAgICAgaWYgKGN1cnJlbnRSdW4pIHtcbiAgICAgICAgICAgIHJ1biA9IGF3YWl0IHdyaXRlUnVuVW5kZXJMaWZlY3ljbGVMb2NrKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgdGFnLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcnVuSWQ6IGN1cnJlbnRSdW4ucnVuSWQsXG4gICAgICAgICAgICAgICAgZGVwbG95bWVudElkOiBjdXJyZW50UnVuLmRlcGxveW1lbnRJZCxcbiAgICAgICAgICAgICAgICB3b3JrZmxvd05hbWU6IGN1cnJlbnRSdW4ud29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBjdXJyZW50UnVuLnNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgIGV4ZWN1dGlvbkNvbnRleHQ6IGN1cnJlbnRSdW4uZXhlY3V0aW9uQ29udGV4dCxcbiAgICAgICAgICAgICAgICBpbnB1dDogY3VycmVudFJ1bi5pbnB1dCxcbiAgICAgICAgICAgICAgICBjcmVhdGVkQXQ6IGN1cnJlbnRSdW4uY3JlYXRlZEF0LFxuICAgICAgICAgICAgICAgIGV4cGlyZWRBdDogY3VycmVudFJ1bi5leHBpcmVkQXQsXG4gICAgICAgICAgICAgICAgc3RhcnRlZEF0OiBjdXJyZW50UnVuLnN0YXJ0ZWRBdCxcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdjYW5jZWxsZWQnLFxuICAgICAgICAgICAgICAgIG91dHB1dDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgY29tcGxldGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgICBhdHRyaWJ1dGVzOiBjdXJyZW50UnVuLmF0dHJpYnV0ZXMsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAgICAgICAgIGRlbGV0ZUFsbEhvb2tzRm9yUnVuKGJhc2VkaXIsIGVmZmVjdGl2ZVJ1bklkKSxcbiAgICAgICAgICAgICAgZGVsZXRlQWxsV2FpdHNGb3JSdW4oYmFzZWRpciwgZWZmZWN0aXZlUnVuSWQpLFxuICAgICAgICAgICAgXSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAnYXR0cl9zZXQnICYmIGN1cnJlbnRSdW4pIHtcbiAgICAgICAgICBydW4gPSBhd2FpdCB3aXRoUnVuRmlsZUxvY2soZWZmZWN0aXZlUnVuSWQsIGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGZyZXNoID0gYXdhaXQgcmVhZEpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3J1bnMnLCBlZmZlY3RpdmVSdW5JZCwgdGFnKSxcbiAgICAgICAgICAgICAgV29ya2Zsb3dSdW5TY2hlbWFcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBpZiAoIWZyZXNoKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBXb3JrZmxvd1J1bk5vdEZvdW5kRXJyb3IoZWZmZWN0aXZlUnVuSWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdmFsaWRhdGVBdHRyaWJ1dGVDaGFuZ2VzKGRhdGEuZXZlbnREYXRhLmNoYW5nZXMsIHtcbiAgICAgICAgICAgICAgZXhpc3RpbmdLZXlzOiBPYmplY3Qua2V5cyhmcmVzaC5hdHRyaWJ1dGVzKSxcbiAgICAgICAgICAgICAgYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXM6XG4gICAgICAgICAgICAgICAgZGF0YS5ldmVudERhdGEuYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXMgPT09IHRydWUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIENsYWltIHRoZSBjb3JyZWxhdGlvbiBkZWR1cCBsb2NrIG9ubHkgYWZ0ZXIgdmFsaWRhdGlvbjogYVxuICAgICAgICAgICAgLy8gdmFsaWRhdGlvbiBmYWlsdXJlIG11c3QgbGVhdmUgdGhlIGNvcnJlbGF0aW9uSWQgdW5jbGFpbWVkIHNvXG4gICAgICAgICAgICAvLyB0aGUgcnVudGltZSdzIHJldHJ5IG9mIHRoZSBzYW1lIGV2ZW50IGlzIG5vdCBtaXNyZXBvcnRlZCBhc1xuICAgICAgICAgICAgLy8gXCJhbHJlYWR5IGV4aXN0c1wiIHdoaWxlIHRoZSBldmVudCB3YXMgbmV2ZXIgd3JpdHRlbiAodGhlXG4gICAgICAgICAgICAvLyBkaXNwYXRjaGVyIHdvdWxkIHRoZW4gd2FpdCBmb3JldmVyIGZvciBhbiBldmVudCB0aGF0IGlzIG5vdFxuICAgICAgICAgICAgLy8gaW4gdGhlIGxvZykuXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIGRhdGEuY29ycmVsYXRpb25JZCAmJlxuICAgICAgICAgICAgICBkYXRhLmV2ZW50RGF0YS53cml0ZXIudHlwZSA9PT0gJ3dvcmtmbG93J1xuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGF0dHJMb2NrTmFtZSA9IHRhZ1xuICAgICAgICAgICAgICAgID8gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfS5jcmVhdGVkLiR7dGFnfWBcbiAgICAgICAgICAgICAgICA6IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH0uY3JlYXRlZGA7XG4gICAgICAgICAgICAgIGNvbnN0IGF0dHJMb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICAgJy5sb2NrcycsXG4gICAgICAgICAgICAgICAgJ2F0dHJpYnV0ZXMnLFxuICAgICAgICAgICAgICAgIGF0dHJMb2NrTmFtZVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBjb25zdCBhdHRyQ2xhaW1lZCA9IGF3YWl0IHdyaXRlRXhjbHVzaXZlKGF0dHJMb2NrUGF0aCwgJycpO1xuICAgICAgICAgICAgICBpZiAoIWF0dHJDbGFpbWVkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgICAgICBgQXR0cmlidXRlIGV2ZW50IFwiJHtkYXRhLmNvcnJlbGF0aW9uSWR9XCIgYWxyZWFkeSBleGlzdHNgXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgbmV4dCA9IHtcbiAgICAgICAgICAgICAgLi4uZnJlc2gsXG4gICAgICAgICAgICAgIGF0dHJpYnV0ZXM6IGFwcGx5QXR0cmlidXRlQ2hhbmdlcyhcbiAgICAgICAgICAgICAgICBmcmVzaC5hdHRyaWJ1dGVzLFxuICAgICAgICAgICAgICAgIGRhdGEuZXZlbnREYXRhLmNoYW5nZXNcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICB9IGFzIFdvcmtmbG93UnVuO1xuICAgICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdydW5zJywgZWZmZWN0aXZlUnVuSWQsIHRhZyksXG4gICAgICAgICAgICAgIG5leHQsXG4gICAgICAgICAgICAgIHsgb3ZlcndyaXRlOiB0cnVlIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm4gbmV4dDtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgICAvLyBTdGVwIGxpZmVjeWNsZSBldmVudHNcbiAgICAgICAgICBkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfY3JlYXRlZCcgJiZcbiAgICAgICAgICAnZXZlbnREYXRhJyBpbiBkYXRhXG4gICAgICAgICkge1xuICAgICAgICAgIC8vIHN0ZXBfY3JlYXRlZDogQ3JlYXRlcyBzdGVwIGVudGl0eSB3aXRoIHN0YXR1cyAncGVuZGluZycsIGF0dGVtcHQ9MCwgY3JlYXRlZEF0IHNldC5cbiAgICAgICAgICAvLyBUd28gY29uY3VycmVudCBpbnZvY2F0aW9ucyB3aXRoIGlkZW50aWNhbCBjb3JyZWxhdGlvbklkcyAoZS5nLiB0aGVcbiAgICAgICAgICAvLyBzbmFwc2hvdCBydW50aW1lJ3MgZGV0ZXJtaW5pc3RpYyBjb3JyZWxhdGlvbklkcyBhY3Jvc3MgcmVwbGF5cylcbiAgICAgICAgICAvLyBtdXN0IGJlIGRlZHVwZWQg4oCUIG90aGVyd2lzZSBib3RoIHdyaXRlcyBzdWNjZWVkIGFuZCB0aGUgZXZlbnQgbG9nXG4gICAgICAgICAgLy8gZW5kcyB1cCB3aXRoIGR1cGxpY2F0ZSBzdGVwX2NyZWF0ZWQgZW50cmllcy4gVGhlIG91dGVyXG4gICAgICAgICAgLy8gd2l0aFN0ZXBMb2NrIG11dGV4IHNlcmlhbGl6ZXMgd2l0aGluIGEgc2luZ2xlIHByb2Nlc3M7IHRoaXNcbiAgICAgICAgICAvLyBUaGUgZXhjbHVzaXZlIGNvbnN0cmFpbnQgZmlsZSBhZGRpdGlvbmFsbHkgcHJvdGVjdHMgYWdhaW5zdFxuICAgICAgICAgIC8vIGNyb3NzLXByb2Nlc3MgcmFjZXMgKHR3byBwbnBtIHdvcmtlcnMsIHJlZGVsaXZlcmVkIHF1ZXVlIG1lc3NhZ2VzLFxuICAgICAgICAgIC8vIGV0Yy4pLiBUaGUgbG9zZXIgdGhyb3dzIEVudGl0eUNvbmZsaWN0RXJyb3Igc28gdGhlIHJ1bnRpbWUnc1xuICAgICAgICAgIC8vIGV4aXN0aW5nIGNhdGNoIHBhdGggY2FuIHN3YWxsb3cgaXQgYW5kIGF2b2lkIGRvdWJsZS1xdWV1aW5nIHRoZVxuICAgICAgICAgIC8vIHN0ZXAuXG4gICAgICAgICAgY29uc3Qgc3RlcENyZWF0ZWRMb2NrTmFtZSA9IHRhZ1xuICAgICAgICAgICAgPyBgJHtlZmZlY3RpdmVSdW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9LmNyZWF0ZWQuJHt0YWd9YFxuICAgICAgICAgICAgOiBgJHtlZmZlY3RpdmVSdW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9LmNyZWF0ZWRgO1xuICAgICAgICAgIGNvbnN0IHN0ZXBDcmVhdGVkTG9ja1BhdGggPSByZXNvbHZlV2l0aGluQmFzZShcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnLmxvY2tzJyxcbiAgICAgICAgICAgICdzdGVwcycsXG4gICAgICAgICAgICBzdGVwQ3JlYXRlZExvY2tOYW1lXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBzdGVwQ3JlYXRlZENsYWltZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShcbiAgICAgICAgICAgIHN0ZXBDcmVhdGVkTG9ja1BhdGgsXG4gICAgICAgICAgICAnJ1xuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKCFzdGVwQ3JlYXRlZENsYWltZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICBgU3RlcCBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIGFscmVhZHkgY3JlYXRlZGBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IHN0ZXBEYXRhID0gZGF0YS5ldmVudERhdGEgYXMge1xuICAgICAgICAgICAgc3RlcE5hbWU6IHN0cmluZztcbiAgICAgICAgICAgIGlucHV0OiBhbnk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBzdGVwID0ge1xuICAgICAgICAgICAgcnVuSWQ6IGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgc3RlcElkOiBkYXRhLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICBzdGVwTmFtZTogc3RlcERhdGEuc3RlcE5hbWUsXG4gICAgICAgICAgICBzdGF0dXM6ICdwZW5kaW5nJyxcbiAgICAgICAgICAgIGlucHV0OiBzdGVwRGF0YS5pbnB1dCxcbiAgICAgICAgICAgIG91dHB1dDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGF0dGVtcHQ6IDAsXG4gICAgICAgICAgICBzdGFydGVkQXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgLy8gUHJvcGFnYXRlIHNwZWNWZXJzaW9uIGZyb20gdGhlIGV2ZW50IHRvIHRoZSBzdGVwIGVudGl0eVxuICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgIH07XG4gICAgICAgICAgY29uc3Qgc3RlcENvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH1gO1xuICAgICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3N0ZXBzJywgc3RlcENvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgIHN0ZXBcbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9zdGFydGVkJykge1xuICAgICAgICAgIC8vIHN0ZXBfc3RhcnRlZDogSW5jcmVtZW50cyBhdHRlbXB0LCBzZXRzIHN0YXR1cyB0byAncnVubmluZydcbiAgICAgICAgICAvLyBTZXRzIHN0YXJ0ZWRBdCBvbmx5IG9uIHRoZSBmaXJzdCBzdGFydCAobm90IHVwZGF0ZWQgb24gcmV0cmllcylcbiAgICAgICAgICAvLyBSZXVzZSB2YWxpZGF0ZWRTdGVwIGZyb20gdmFsaWRhdGlvbiAoYWxyZWFkeSByZWFkIGFib3ZlKVxuXG4gICAgICAgICAgLy8gTGF6eSBzdGVwIHN0YXJ0OiBubyBwcmlvciBzdGVwX2NyZWF0ZWQg4oCUIGNyZWF0ZSB0aGUgc3RlcCBlbnRpdHlcbiAgICAgICAgICAvLyBhbmQgYSBzeW50aGV0aWMgc3RlcF9jcmVhdGVkIGV2ZW50IG5vdywgdGhlbiBmYWxsIHRocm91Z2ggdG8gdGhlXG4gICAgICAgICAgLy8gc3RhcnQgdHJhbnNpdGlvbiBiZWxvdy4gTWlycm9ycyB0aGUgcmVzaWxpZW50IHJ1bl9zdGFydGVkIHBhdGg6XG4gICAgICAgICAgLy8gdGhlIHN0ZXAgZW50aXR5IGlzIGNsYWltZWQgYXRvbWljYWxseSAoZmlyc3Qgd3JpdGVyIHdpbnMpIGFuZCB0aGVcbiAgICAgICAgICAvLyBzeW50aGV0aWMgc3RlcF9jcmVhdGVkIGV2ZW50IGtlZXBzIHJlcGxheSBjb3JyZWN0ICh0aGUgY2xpZW50IHN0ZXBcbiAgICAgICAgICAvLyBjb25zdW1lciBtYXJrcyBoYXNDcmVhdGVkRXZlbnQgb25seSB3aGVuIGl0IG9ic2VydmVzIHRoYXQgZXZlbnQpLlxuICAgICAgICAgIGlmICghdmFsaWRhdGVkU3RlcCAmJiBsYXp5U3RlcFN0YXJ0KSB7XG4gICAgICAgICAgICBjb25zdCBsYXp5RGF0YSA9IGRhdGEuZXZlbnREYXRhIGFzIHtcbiAgICAgICAgICAgICAgc3RlcE5hbWU6IHN0cmluZztcbiAgICAgICAgICAgICAgaW5wdXQ6IGFueTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjb25zdCBzdGVwQ3JlYXRlZExvY2tOYW1lID0gdGFnXG4gICAgICAgICAgICAgID8gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfS5jcmVhdGVkLiR7dGFnfWBcbiAgICAgICAgICAgICAgOiBgJHtlZmZlY3RpdmVSdW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9LmNyZWF0ZWRgO1xuICAgICAgICAgICAgY29uc3Qgc3RlcENyZWF0ZWRMb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAnLmxvY2tzJyxcbiAgICAgICAgICAgICAgJ3N0ZXBzJyxcbiAgICAgICAgICAgICAgc3RlcENyZWF0ZWRMb2NrTmFtZVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGNvbnN0IHN0ZXBDcmVhdGVkQ2xhaW1lZCA9IGF3YWl0IHdyaXRlRXhjbHVzaXZlKFxuICAgICAgICAgICAgICBzdGVwQ3JlYXRlZExvY2tQYXRoLFxuICAgICAgICAgICAgICAnJ1xuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGlmICghc3RlcENyZWF0ZWRDbGFpbWVkKSB7XG4gICAgICAgICAgICAgIC8vIEEgY29uY3VycmVudCBoYW5kbGVyIGFscmVhZHkgY2xhaW1lZCB0aGUgY3JlYXRlIGZvciB0aGlzXG4gICAgICAgICAgICAgIC8vIHN0ZXAuIFRoZSBhdG9taWMgY2xhaW0gaXMgdGhlIGV4YWN0bHktb25jZSBvd25lcnNoaXAgZ2F0ZTpcbiAgICAgICAgICAgICAgLy8gb25seSB0aGUgd2lubmVyIHJ1bnMgdGhlIHN0ZXAgYm9keSBpbmxpbmUuIFRocm93XG4gICAgICAgICAgICAgIC8vIEVudGl0eUNvbmZsaWN0RXJyb3Ig4oCUIHRoZSBydW50aW1lJ3MgZXhlY3V0ZVN0ZXAgbWFwcyB0aGlzIHRvXG4gICAgICAgICAgICAgIC8vIGBza2lwcGVkYCwgc28gdGhlIGxvc2VyIGRvZXMgbm90IHN0YXJ0IG9yIHJ1biB0aGUgc3RlcC4gVGhpc1xuICAgICAgICAgICAgICAvLyBwcmVzZXJ2ZXMgdGhlIHNhbWUgXCJleGFjdGx5IG9uZSBoYW5kbGVyIG93bnMgZWFjaCBzdGVwXCJcbiAgICAgICAgICAgICAgLy8gZ3VhcmFudGVlIHRoZSBzZXBhcmF0ZSBzdGVwX2NyZWF0ZWQgY2xhaW0gcHJvdmlkZXMgdG9kYXkuXG4gICAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICAgIGBTdGVwIFwiJHtkYXRhLmNvcnJlbGF0aW9uSWR9XCIgYWxyZWFkeSBjcmVhdGVkYFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgY29uc3QgY3JlYXRlZFN0ZXA6IFN0ZXAgPSB7XG4gICAgICAgICAgICAgICAgcnVuSWQ6IGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICAgIHN0ZXBJZDogZGF0YS5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgICAgIHN0ZXBOYW1lOiBsYXp5RGF0YS5zdGVwTmFtZSxcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdwZW5kaW5nJyxcbiAgICAgICAgICAgICAgICBpbnB1dDogbGF6eURhdGEuaW5wdXQsXG4gICAgICAgICAgICAgICAgb3V0cHV0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBhdHRlbXB0OiAwLFxuICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgICAgdGFnZ2VkUGF0aChcbiAgICAgICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICAgICAnc3RlcHMnLFxuICAgICAgICAgICAgICAgICAgYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWAsXG4gICAgICAgICAgICAgICAgICB0YWdcbiAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgIGNyZWF0ZWRTdGVwXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIC8vIFdyaXRlIHRoZSBzeW50aGV0aWMgc3RlcF9jcmVhdGVkIGV2ZW50IHNvIHJlcGxheSBvYnNlcnZlcyBpdFxuICAgICAgICAgICAgICAvLyAodGhlIGNsaWVudCBzdGVwIGNvbnN1bWVyIHNldHMgaGFzQ3JlYXRlZEV2ZW50IG9ubHkgb24gYVxuICAgICAgICAgICAgICAvLyBzdGVwX2NyZWF0ZWQgZXZlbnQpLiBJdHMgZXZlbnRJZCBpcyBhIGZyZXNoIG1vbm90b25pYyBVTElELlxuICAgICAgICAgICAgICAvLyBPcmRlcmluZyB2cy4gdGhlIHN0ZXBfc3RhcnRlZCBldmVudCByb3cgZG9lcyBub3QgYWZmZWN0XG4gICAgICAgICAgICAgIC8vIGNvcnJlY3RuZXNzOiB0aGUgc3RlcF9zdGFydGVkIGNvbnN1bWVyIGlzIGEgbm8tb3AgYW5kIG9ubHlcbiAgICAgICAgICAgICAgLy8gc3RlcF9jcmVhdGVkIGZsaXBzIGhhc0NyZWF0ZWRFdmVudCwgc28gdGhlIGVuZCBzdGF0ZSBpcyB0aGVcbiAgICAgICAgICAgICAgLy8gc2FtZSB3aGljaGV2ZXIgc29ydHMgZmlyc3Qg4oCUIHRoaXMgbWF0Y2hlcyB0aGUgcmVzaWxpZW50XG4gICAgICAgICAgICAgIC8vIHJ1bl9zdGFydGVkIOKGkiBydW5fY3JlYXRlZCBwcmVjZWRlbnQgaW4gdGhpcyBmaWxlLlxuICAgICAgICAgICAgICBjb25zdCBzdGVwQ3JlYXRlZEV2ZW50SWQgPSBgZXZudF8ke21vbm90b25pY1VsaWQoKX1gO1xuICAgICAgICAgICAgICBjb25zdCBzdGVwQ3JlYXRlZEV2ZW50OiBFdmVudCA9IHtcbiAgICAgICAgICAgICAgICBldmVudFR5cGU6ICdzdGVwX2NyZWF0ZWQnLFxuICAgICAgICAgICAgICAgIHJ1bklkOiBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgICBldmVudElkOiBzdGVwQ3JlYXRlZEV2ZW50SWQsXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgICAgICAgIHN0ZXBOYW1lOiBsYXp5RGF0YS5zdGVwTmFtZSxcbiAgICAgICAgICAgICAgICAgIGlucHV0OiBsYXp5RGF0YS5pbnB1dCxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgICAgdGFnZ2VkUGF0aChcbiAgICAgICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICAgICAnZXZlbnRzJyxcbiAgICAgICAgICAgICAgICAgIGAke2VmZmVjdGl2ZVJ1bklkfS0ke3N0ZXBDcmVhdGVkRXZlbnRJZH1gLFxuICAgICAgICAgICAgICAgICAgdGFnXG4gICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgICBzdGVwQ3JlYXRlZEV2ZW50XG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIHZhbGlkYXRlZFN0ZXAgPSBjcmVhdGVkU3RlcDtcbiAgICAgICAgICAgICAgc3RlcENyZWF0ZWRMYXppbHkgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICh2YWxpZGF0ZWRTdGVwKSB7XG4gICAgICAgICAgICAvLyBDaGVjayBpZiByZXRyeUFmdGVyIHRpbWVzdGFtcCBoYXNuJ3QgYmVlbiByZWFjaGVkIHlldFxuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICB2YWxpZGF0ZWRTdGVwLnJldHJ5QWZ0ZXIgJiZcbiAgICAgICAgICAgICAgdmFsaWRhdGVkU3RlcC5yZXRyeUFmdGVyLmdldFRpbWUoKSA+IERhdGUubm93KClcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgVG9vRWFybHlFcnJvcihcbiAgICAgICAgICAgICAgICBgQ2Fubm90IHN0YXJ0IHN0ZXAgXCIke2RhdGEuY29ycmVsYXRpb25JZH1cIjogcmV0cnlBZnRlciB0aW1lc3RhbXAgaGFzIG5vdCBiZWVuIHJlYWNoZWQgeWV0YCxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICByZXRyeUFmdGVyOiBNYXRoLmNlaWwoXG4gICAgICAgICAgICAgICAgICAgICh2YWxpZGF0ZWRTdGVwLnJldHJ5QWZ0ZXIuZ2V0VGltZSgpIC0gRGF0ZS5ub3coKSkgLyAxMDAwXG4gICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQmVzdC1lZmZvcnQgZ3VhcmQ6IHJlLXJlYWQgdGhlIHN0ZXAgZW50aXR5IHRvIGNoZWNrIGlmIGl0XG4gICAgICAgICAgICAvLyByZWFjaGVkIHRlcm1pbmFsIHN0YXRlIGJldHdlZW4gdGhlIHZhbGlkYXRpb24gcmVhZCBhbmQgbm93LlxuICAgICAgICAgICAgLy8gVGhpcyBuYXJyb3dzIHRoZSBUT0NUT1Ugd2luZG93IGJ1dCBkb2VzIG5vdCBmdWxseSBlbGltaW5hdGUgaXRcbiAgICAgICAgICAgIC8vICh0aGUgbG9jYWwgd29ybGQgaXMgc2luZ2xlLXByb2Nlc3MgLyBkZXYtb25seTsgdGhlIHBvc3RncmVzXG4gICAgICAgICAgICAvLyB3b3JsZCB1c2VzIFNRTC1sZXZlbCBhdG9taWMgZ3VhcmRzIGZvciBwcm9kdWN0aW9uKS5cbiAgICAgICAgICAgIGNvbnN0IHN0ZXBDb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9YDtcbiAgICAgICAgICAgIGNvbnN0IGZyZXNoU3RlcCA9IGF3YWl0IHJlYWRKU09OV2l0aEZhbGxiYWNrKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAnc3RlcHMnLFxuICAgICAgICAgICAgICBzdGVwQ29tcG9zaXRlS2V5LFxuICAgICAgICAgICAgICBTdGVwU2NoZW1hLFxuICAgICAgICAgICAgICB0YWdcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBpZiAoZnJlc2hTdGVwICYmIGlzU3RlcFRlcm1pbmFsKGZyZXNoU3RlcC5zdGF0dXMpKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICAgIGBDYW5ub3QgbW9kaWZ5IHN0ZXAgaW4gdGVybWluYWwgc3RhdGUgXCIke2ZyZXNoU3RlcC5zdGF0dXN9XCJgXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHN0ZXAgPSB7XG4gICAgICAgICAgICAgIC4uLnZhbGlkYXRlZFN0ZXAsXG4gICAgICAgICAgICAgIHN0YXR1czogJ3J1bm5pbmcnLFxuICAgICAgICAgICAgICAvLyBPbmx5IHNldCBzdGFydGVkQXQgb24gdGhlIGZpcnN0IHN0YXJ0XG4gICAgICAgICAgICAgIHN0YXJ0ZWRBdDogdmFsaWRhdGVkU3RlcC5zdGFydGVkQXQgPz8gbm93LFxuICAgICAgICAgICAgICAvLyBJbmNyZW1lbnQgYXR0ZW1wdCBjb3VudGVyIG9uIGV2ZXJ5IHN0YXJ0XG4gICAgICAgICAgICAgIGF0dGVtcHQ6IHZhbGlkYXRlZFN0ZXAuYXR0ZW1wdCArIDEsXG4gICAgICAgICAgICAgIC8vIENsZWFyIHJldHJ5QWZ0ZXIgbm93IHRoYXQgdGhlIHN0ZXAgaGFzIHN0YXJ0ZWRcbiAgICAgICAgICAgICAgcmV0cnlBZnRlcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3N0ZXBzJywgc3RlcENvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgc3RlcCxcbiAgICAgICAgICAgICAgeyBvdmVyd3JpdGU6IHRydWUgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2NvbXBsZXRlZCcgJiYgJ2V2ZW50RGF0YScgaW4gZGF0YSkge1xuICAgICAgICAgIC8vIHN0ZXBfY29tcGxldGVkOiBUZXJtaW5hbCBzdGF0ZSB3aXRoIG91dHB1dFxuICAgICAgICAgIC8vIFVzZXMgd3JpdGVFeGNsdXNpdmUgb24gYSBsb2NrIGZpbGUgdG8gYXRvbWljYWxseSBwcmV2ZW50IGNvbmN1cnJlbnRcbiAgICAgICAgICAvLyBpbnZvY2F0aW9ucyBmcm9tIGJvdGggY29tcGxldGluZyB0aGUgc2FtZSBzdGVwIChUT0NUT1UgcmFjZSkuXG4gICAgICAgICAgY29uc3QgY29tcGxldGVkRGF0YSA9IGRhdGEuZXZlbnREYXRhIGFzIHsgcmVzdWx0OiBhbnkgfTtcbiAgICAgICAgICBpZiAodmFsaWRhdGVkU3RlcCkge1xuICAgICAgICAgICAgY29uc3Qgc3RlcENvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH1gO1xuICAgICAgICAgICAgY29uc3QgbG9ja05hbWUgPSB0YWdcbiAgICAgICAgICAgICAgPyBgJHtzdGVwQ29tcG9zaXRlS2V5fS50ZXJtaW5hbC4ke3RhZ31gXG4gICAgICAgICAgICAgIDogYCR7c3RlcENvbXBvc2l0ZUtleX0udGVybWluYWxgO1xuICAgICAgICAgICAgY29uc3QgdGVybWluYWxMb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAnLmxvY2tzJyxcbiAgICAgICAgICAgICAgJ3N0ZXBzJyxcbiAgICAgICAgICAgICAgbG9ja05hbWVcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCBjbGFpbWVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUodGVybWluYWxMb2NrUGF0aCwgJycpO1xuICAgICAgICAgICAgaWYgKCFjbGFpbWVkKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICAgICdDYW5ub3QgbW9kaWZ5IHN0ZXAgaW4gdGVybWluYWwgc3RhdGUnXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzdGVwID0ge1xuICAgICAgICAgICAgICAuLi52YWxpZGF0ZWRTdGVwLFxuICAgICAgICAgICAgICBzdGF0dXM6ICdjb21wbGV0ZWQnLFxuICAgICAgICAgICAgICBvdXRwdXQ6IGNvbXBsZXRlZERhdGEucmVzdWx0LFxuICAgICAgICAgICAgICBjb21wbGV0ZWRBdDogbm93LFxuICAgICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3N0ZXBzJywgc3RlcENvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgc3RlcCxcbiAgICAgICAgICAgICAgeyBvdmVyd3JpdGU6IHRydWUgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2ZhaWxlZCcgJiYgJ2V2ZW50RGF0YScgaW4gZGF0YSkge1xuICAgICAgICAgIC8vIHN0ZXBfZmFpbGVkOiBUZXJtaW5hbCBzdGF0ZSB3aXRoIGVycm9yXG4gICAgICAgICAgLy8gVXNlcyB3cml0ZUV4Y2x1c2l2ZSBvbiBhIGxvY2sgZmlsZSB0byBhdG9taWNhbGx5IHByZXZlbnQgY29uY3VycmVudFxuICAgICAgICAgIC8vIGludm9jYXRpb25zIGZyb20gYm90aCBmYWlsaW5nIHRoZSBzYW1lIHN0ZXAgKFRPQ1RPVSByYWNlKS5cbiAgICAgICAgICBjb25zdCBmYWlsZWREYXRhID0gZGF0YS5ldmVudERhdGEgYXMge1xuICAgICAgICAgICAgZXJyb3I6IHVua25vd247XG4gICAgICAgICAgfTtcbiAgICAgICAgICBpZiAodmFsaWRhdGVkU3RlcCkge1xuICAgICAgICAgICAgY29uc3Qgc3RlcENvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH1gO1xuICAgICAgICAgICAgY29uc3QgbG9ja05hbWUgPSB0YWdcbiAgICAgICAgICAgICAgPyBgJHtzdGVwQ29tcG9zaXRlS2V5fS50ZXJtaW5hbC4ke3RhZ31gXG4gICAgICAgICAgICAgIDogYCR7c3RlcENvbXBvc2l0ZUtleX0udGVybWluYWxgO1xuICAgICAgICAgICAgY29uc3QgdGVybWluYWxMb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAnLmxvY2tzJyxcbiAgICAgICAgICAgICAgJ3N0ZXBzJyxcbiAgICAgICAgICAgICAgbG9ja05hbWVcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCBjbGFpbWVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUodGVybWluYWxMb2NrUGF0aCwgJycpO1xuICAgICAgICAgICAgaWYgKCFjbGFpbWVkKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICAgICdDYW5ub3QgbW9kaWZ5IHN0ZXAgaW4gdGVybWluYWwgc3RhdGUnXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBUaGUgZXJyb3IgZmllbGQgaXMgU2VyaWFsaXplZERhdGEgKFVpbnQ4QXJyYXkpIHByb2R1Y2VkIGJ5XG4gICAgICAgICAgICAvLyBkZWh5ZHJhdGVTdGVwRXJyb3IuIFdlIHN0b3JlIGl0IHZlcmJhdGltIOKAlCBjb25zdW1lcnMgaHlkcmF0ZSBpdFxuICAgICAgICAgICAgLy8gdmlhIGh5ZHJhdGVTdGVwRXJyb3IgdG8gcmVjb25zdHJ1Y3QgdGhlIG9yaWdpbmFsIHRocm93biB2YWx1ZS5cbiAgICAgICAgICAgIHN0ZXAgPSB7XG4gICAgICAgICAgICAgIC4uLnZhbGlkYXRlZFN0ZXAsXG4gICAgICAgICAgICAgIHN0YXR1czogJ2ZhaWxlZCcsXG4gICAgICAgICAgICAgIGVycm9yOiBmYWlsZWREYXRhLmVycm9yIGFzIFVpbnQ4QXJyYXksXG4gICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiBub3csXG4gICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnc3RlcHMnLCBzdGVwQ29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgICBzdGVwLFxuICAgICAgICAgICAgICB7IG92ZXJ3cml0ZTogdHJ1ZSB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfcmV0cnlpbmcnICYmICdldmVudERhdGEnIGluIGRhdGEpIHtcbiAgICAgICAgICAvLyBzdGVwX3JldHJ5aW5nOiBTZXRzIHN0YXR1cyBiYWNrIHRvICdwZW5kaW5nJywgcmVjb3JkcyBlcnJvclxuICAgICAgICAgIC8vIFJldXNlIHZhbGlkYXRlZFN0ZXAgZnJvbSB2YWxpZGF0aW9uIChhbHJlYWR5IHJlYWQgYWJvdmUpXG4gICAgICAgICAgY29uc3QgcmV0cnlEYXRhID0gZGF0YS5ldmVudERhdGEgYXMge1xuICAgICAgICAgICAgZXJyb3I6IHVua25vd247XG4gICAgICAgICAgICByZXRyeUFmdGVyPzogRGF0ZTtcbiAgICAgICAgICB9O1xuICAgICAgICAgIGlmICh2YWxpZGF0ZWRTdGVwKSB7XG4gICAgICAgICAgICBjb25zdCBzdGVwQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgICBzdGVwID0ge1xuICAgICAgICAgICAgICAuLi52YWxpZGF0ZWRTdGVwLFxuICAgICAgICAgICAgICBzdGF0dXM6ICdwZW5kaW5nJyxcbiAgICAgICAgICAgICAgZXJyb3I6IHJldHJ5RGF0YS5lcnJvciBhcyBVaW50OEFycmF5LFxuICAgICAgICAgICAgICByZXRyeUFmdGVyOiByZXRyeURhdGEucmV0cnlBZnRlcixcbiAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdzdGVwcycsIHN0ZXBDb21wb3NpdGVLZXksIHRhZyksXG4gICAgICAgICAgICAgIHN0ZXAsXG4gICAgICAgICAgICAgIHsgb3ZlcndyaXRlOiB0cnVlIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKFxuICAgICAgICAgIC8vIEhvb2sgbGlmZWN5Y2xlIGV2ZW50c1xuICAgICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnaG9va19jcmVhdGVkJyAmJlxuICAgICAgICAgICdldmVudERhdGEnIGluIGRhdGFcbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgaG9va0RhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7XG4gICAgICAgICAgICB0b2tlbjogc3RyaW5nO1xuICAgICAgICAgICAgbWV0YWRhdGE/OiBhbnk7XG4gICAgICAgICAgICBpc1dlYmhvb2s/OiBib29sZWFuO1xuICAgICAgICAgICAgaXNTeXN0ZW0/OiBib29sZWFuO1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICAvLyBBdG9taWNhbGx5IGNsYWltIHRoZSB0b2tlbiB1c2luZyBhbiBleGNsdXNpdmUtY3JlYXRlIGNvbnN0cmFpbnQgZmlsZS5cbiAgICAgICAgICAvLyBUaGlzIGF2b2lkcyB0aGUgVE9DVE9VIHJhY2Ugb2YgdGhlIHByZXZpb3VzIHJlYWQtYWxsLXRoZW4tY2hlY2sgYXBwcm9hY2guXG4gICAgICAgICAgY29uc3QgY29uc3RyYWludFBhdGggPSBwYXRoLmpvaW4oXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJ2hvb2tzJyxcbiAgICAgICAgICAgICd0b2tlbnMnLFxuICAgICAgICAgICAgYCR7aGFzaFRva2VuKGhvb2tEYXRhLnRva2VuKX0uanNvbmBcbiAgICAgICAgICApO1xuICAgICAgICAgIC8vIFBlcnNpc3QgYGV2ZW50SWRgIGluIHRoZSBjbGFpbSBzbyBjb25jdXJyZW50IC8gY3Jvc3MtXG4gICAgICAgICAgLy8gcHJvY2VzcyByZXRyaWVzIGNhbiBjb252ZXJnZSBvbiBhIHNpbmdsZSBjYW5vbmljYWxcbiAgICAgICAgICAvLyBgaG9va19jcmVhdGVkYCBldmVudCBwYXRoLiBTZWUgdGhlIHJlY292ZXJ5IGNvbW1lbnRcbiAgICAgICAgICAvLyBiZWxvdy5cbiAgICAgICAgICBjb25zdCB0b2tlbkNsYWltZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShcbiAgICAgICAgICAgIGNvbnN0cmFpbnRQYXRoLFxuICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgICB0b2tlbjogaG9va0RhdGEudG9rZW4sXG4gICAgICAgICAgICAgIGhvb2tJZDogZGF0YS5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgIGV2ZW50SWQsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICk7XG5cbiAgICAgICAgICAvLyBSZWNvdmVyeSBzaGFwZTogdGhlIGR1cmFibGUgcmVjb3JkIG9mIGEgc3VjY2Vzc2Z1bCBob29rXG4gICAgICAgICAgLy8gY3JlYXRpb24gaXMgdGhlIGBob29rX2NyZWF0ZWRgIGV2ZW50IGluIHRoZSBldmVudCBsb2cuIFRoZVxuICAgICAgICAgIC8vIGNsYWltIGZpbGUgYW5kIGhvb2sgZW50aXR5IGFyZSB3cml0dGVuIGJlZm9yZSB0aGUgZXZlbnQsXG4gICAgICAgICAgLy8gYW5kIHRoZSB0aHJlZSB3cml0ZXMgYXJlIE5PVCBhdG9taWMsIHNvIGEgY3Jhc2ggYXQgYW55XG4gICAgICAgICAgLy8gcG9pbnQgY2FuIGxlYXZlIG9uZSBvciB0d28gb2YgdGhlbSBvbiBkaXNrIHdpdGhvdXQgdGhlXG4gICAgICAgICAgLy8gZXZlbnQuIFRyZWF0aW5nIHRob3NlIGFzIFwiY29tcGxldGVkXCIgd291bGQgaGF2ZSB0aGVcbiAgICAgICAgICAvLyBzdXNwZW5zaW9uIGhhbmRsZXIgc3dhbGxvdyB0aGUgcmV0cnkgYW5kIHBlcm1hbmVudGx5IGxvc2VcbiAgICAgICAgICAvLyB0aGUgYGhvb2tfY3JlYXRlZGAgZXZlbnQgZnJvbSB0aGUgbG9nLlxuICAgICAgICAgIC8vXG4gICAgICAgICAgLy8gV2hlbiB0aGUgZGVkdXAgYnJhbmNoIGZpcmVzIGZvciB0aGUgc2FtZSBgKHJ1bklkLCBob29rSWQpYCxcbiAgICAgICAgICAvLyB3ZSBjb252ZXJnZSBvbiB0aGUgY2Fub25pY2FsIGBldmVudElkYCBwZXJzaXN0ZWQgaW4gdGhlXG4gICAgICAgICAgLy8gY2xhaW0gZmlsZSBieSB0aGUgb3JpZ2luYWwgKHdpbm5pbmcpIGB3cml0ZUV4Y2x1c2l2ZWAuIEJ5XG4gICAgICAgICAgLy8gYWRvcHRpbmcgdGhhdCBldmVudElkIGZvciB0aGlzIHJldHJ5J3MgZXZlbnQgd3JpdGUg4oCUIGFuZFxuICAgICAgICAgIC8vIGxldHRpbmcgdGhlIG91dGVyIG5vLW92ZXJ3cml0ZSBgd3JpdGVKU09OYCBmb3IgdGhlIGV2ZW50XG4gICAgICAgICAgLy8gdGhyb3cgYEVudGl0eUNvbmZsaWN0RXJyb3JgIG9uIGNvbGxpc2lvbiDigJQgY29uY3VycmVudCAvXG4gICAgICAgICAgLy8gY3Jvc3MtcHJvY2VzcyB3b3JrZXJzIGVpdGhlcjpcbiAgICAgICAgICAvLyAgIC0gcHVibGlzaCB0aGUgc2FtZSBldmVudCBhdCB0aGUgc2FtZSBwYXRoIGV4YWN0bHkgb25jZVxuICAgICAgICAgIC8vICAgICAodGhlIGxvc2VyJ3MgYHdyaXRlSlNPTmAgdGhyb3dzIEVudGl0eUNvbmZsaWN0RXJyb3IsXG4gICAgICAgICAgLy8gICAgIHdoaWNoIHRoZSBydW50aW1lJ3MgZXhpc3RpbmcgY29uY3VycmVudC1yZXBsYXkgY2F0Y2hcbiAgICAgICAgICAvLyAgICAgcGF0aCBhdCBzdXNwZW5zaW9uLWhhbmRsZXIudHM6MTQyIHN3YWxsb3dzKSwgb3JcbiAgICAgICAgICAvLyAgIC0gY29udmVyZ2Ugb24gYSBzaW5nbGUgcmVjb3Zlcnkgd3JpdGUgd2hlbiB0aGUgcHJpb3JcbiAgICAgICAgICAvLyAgICAgY2xhaW0gd2FzIG9ycGhhbmVkIGJ5IGEgY3Jhc2ggYmVmb3JlIHRoZSBldmVudCBsYW5kZWQuXG4gICAgICAgICAgLy9cbiAgICAgICAgICAvLyBUaGUgbGVnYWN5IGZhbGxiYWNrIChgZXhpc3RpbmdDbGFpbS5ldmVudElkYCB1bmRlZmluZWQpXG4gICAgICAgICAgLy8gaXMgZm9yIGNsYWltIGZpbGVzIHdyaXR0ZW4gYmVmb3JlIHRoaXMgZmllbGQgd2FzIGFkZGVkIOKAlFxuICAgICAgICAgIC8vIHRob3NlIHByb2JlIHRoZSBldmVudCBsb2cgZGlyZWN0bHkgYW5kIGZhbGwgdGhyb3VnaCB0byBhXG4gICAgICAgICAgLy8gZnJlc2gtZXZlbnRJZCByZWNvdmVyeSB3cml0ZS4gVGhlIGxlZ2FjeSBwYXRoIGRvZXMgbm90XG4gICAgICAgICAgLy8gY29udmVyZ2UgYWNyb3NzIHdvcmtlcnMgYnV0IGNhbm5vdCByZWdyZXNzIGJlaGF2aW9yIGZvclxuICAgICAgICAgIC8vIGZyZXNobHktd3JpdHRlbiBjbGFpbXMuXG4gICAgICAgICAgLy9cbiAgICAgICAgICAvLyBUaGUgYHdpdGhIb29rTG9ja2AgaW4tcHJvY2VzcyBtdXRleCBhYm92ZSBrZWVwcyB0d28gc2FtZS1cbiAgICAgICAgICAvLyB0aWNrIGluLXByb2Nlc3MgY2FsbGVycyBmcm9tIHJhY2luZyBpbnRvIHRoaXMgYnJhbmNoIHdpdGhcbiAgICAgICAgICAvLyB0aGUgd2lubmVyIG1pZC13cml0ZSwgYnV0IGlzIG5vdCBzdWZmaWNpZW50IGFjcm9zc1xuICAgICAgICAgIC8vIHByb2Nlc3NlcyDigJQgdGhlIGR1cmFibGUgY29udmVyZ2VuY2Uga2V5IChgY2xhaW0uZXZlbnRJZGApXG4gICAgICAgICAgLy8gaXMgd2hhdCBjbG9zZXMgdGhlIGNyb3NzLXByb2Nlc3MgcmFjZS5cbiAgICAgICAgICBsZXQgd3JpdGVIb29rRW50aXR5V2l0aE92ZXJ3cml0ZSA9IGZhbHNlO1xuXG4gICAgICAgICAgaWYgKCF0b2tlbkNsYWltZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nQ2xhaW0gPSBhd2FpdCByZWFkSG9va1Rva2VuQ2xhaW0oY29uc3RyYWludFBhdGgpO1xuXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIGV4aXN0aW5nQ2xhaW0/LnJ1bklkID09PSBlZmZlY3RpdmVSdW5JZCAmJlxuICAgICAgICAgICAgICBleGlzdGluZ0NsYWltLmhvb2tJZCA9PT0gZGF0YS5jb3JyZWxhdGlvbklkXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgLy8gQWRvcHQgYSBjYW5vbmljYWwgZXZlbnRJZCBmb3IgdGhlIHJlY292ZXJ5IHdyaXRlLiBUaGVcbiAgICAgICAgICAgICAgLy8gb3V0ZXIgZXZlbnQgcHVibGlzaCAoYHdyaXRlRXhjbHVzaXZlKGV2ZW50UGF0aClgKVxuICAgICAgICAgICAgICAvLyBlaXRoZXIgc3VjY2VlZHMgKHdlIHB1Ymxpc2ggdGhlIGNhbm9uaWNhbCBldmVudCxcbiAgICAgICAgICAgICAgLy8gcmVwYWlyaW5nIGEgcGFydGlhbCB3cml0ZSBsZWZ0IGJ5IHRoZSBvcmlnaW5hbFxuICAgICAgICAgICAgICAvLyBjbGFpbWFudCkgb3IgcmV0dXJucyBgZmFsc2VgIGFuZCB3ZSB0aHJvd1xuICAgICAgICAgICAgICAvLyBgRW50aXR5Q29uZmxpY3RFcnJvcmAgKHRoZSBldmVudCB3YXMgYWxyZWFkeVxuICAgICAgICAgICAgICAvLyBwdWJsaXNoZWQg4oCUIGEgcmVhbCBkdXBsaWNhdGUpLiBFaXRoZXIgd2F5IHRoZSBsb2dcbiAgICAgICAgICAgICAgLy8gZW5kcyB3aXRoIGV4YWN0bHkgb25lIGBob29rX2NyZWF0ZWRgIGV2ZW50IGZvciB0aGlzXG4gICAgICAgICAgICAgIC8vIGAocnVuSWQsIGhvb2tJZClgLlxuICAgICAgICAgICAgICAvL1xuICAgICAgICAgICAgICAvLyBUaGUgY2Fub25pY2FsIGV2ZW50SWQgY29tZXMgZnJvbSBvbmUgb2YgdHdvIHBsYWNlczpcbiAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgLy8gICAtIGBleGlzdGluZ0NsYWltLmV2ZW50SWRgIGZvciBjbGFpbXMgd3JpdHRlbiBieVxuICAgICAgICAgICAgICAvLyAgICAgdGhpcyB2ZXJzaW9uICh0aGUgd3JpdGVyIGFib3ZlIHBlcnNpc3RzIHRoZVxuICAgICAgICAgICAgICAvLyAgICAgY2FuZGlkYXRlIGV2ZW50SWQgYXRvbWljYWxseSB3aXRoIHRoZSBjbGFpbSkuXG4gICAgICAgICAgICAgIC8vICAgICBUaGUgZXZlbnRJZCBpcyBkdXJhYmxlLCBzbyB0aGUgb3V0ZXJcbiAgICAgICAgICAgICAgLy8gICAgIGB3cml0ZUV4Y2x1c2l2ZShldmVudFBhdGgpYCBhbG9uZSBpcyBlbm91Z2ggdG9cbiAgICAgICAgICAgICAgLy8gICAgIGFyYml0cmF0ZSBwdWJsaWNhdGlvbjogaXQgZmFpbHMgaWZmIHRoZSBldmVudFxuICAgICAgICAgICAgICAvLyAgICAgd2FzIGFscmVhZHkgcHVibGlzaGVkIGF0IHRoYXQgZXhhY3QgcGF0aC5cbiAgICAgICAgICAgICAgLy9cbiAgICAgICAgICAgICAgLy8gICAtIFRoZSByZWNvdmVyeS1tYXJrZXIgc2lkZWNhciBmb3IgbGVnYWN5IGNsYWltc1xuICAgICAgICAgICAgICAvLyAgICAgd3JpdHRlbiBiZWZvcmUgYGV2ZW50SWRgIHdhcyBwZXJzaXN0ZWQgaW5saW5lXG4gICAgICAgICAgICAgIC8vICAgICBpbiB0aGUgY2xhaW0uIFRoZSBtYXJrZXIgaXMgaXRzZWxmIGFcbiAgICAgICAgICAgICAgLy8gICAgIGB3cml0ZUV4Y2x1c2l2ZWAsIHNvIHRoZSBmaXJzdCByZXRyeSBwaW5zIGl0c1xuICAgICAgICAgICAgICAvLyAgICAgY2FuZGlkYXRlIGV2ZW50SWQgYXMgY2Fub25pY2FsIGFuZCBzdWJzZXF1ZW50XG4gICAgICAgICAgICAgIC8vICAgICByZXRyaWVzIGFkb3B0IGl0LiBXaXRob3V0IHRoaXMsIHR3byBwcm9jZXNzZXNcbiAgICAgICAgICAgICAgLy8gICAgIGJvdGggcmVhZGluZyB0aGUgc2FtZSBsZWdhY3kgY2xhaW0gd291bGQgZWFjaFxuICAgICAgICAgICAgICAvLyAgICAgZ2VuZXJhdGUgdGhlaXIgb3duIGV2ZW50SWQsIGxhbmQgdGhlaXJcbiAgICAgICAgICAgICAgLy8gICAgIGB3cml0ZUV4Y2x1c2l2ZShldmVudFBhdGgpYCBjYWxscyBhdCBkaWZmZXJlbnRcbiAgICAgICAgICAgICAgLy8gICAgIHBhdGhzLCBhbmQgYXBwZW5kIHR3byBldmVudHMuXG4gICAgICAgICAgICAgIC8vXG4gICAgICAgICAgICAgIC8vICAgICBGb3IgbGVnYWN5IGNsYWltcyB3ZSBhbHNvIG11c3QgcHJvYmUgdGhlIGV2ZW50XG4gICAgICAgICAgICAgIC8vICAgICBsb2cgZm9yIGFuIGV4aXN0aW5nIGBob29rX2NyZWF0ZWRgIGV2ZW50IEJFRk9SRVxuICAgICAgICAgICAgICAvLyAgICAgcGlubmluZyBhIGNhbm9uaWNhbCBldmVudElkOiB0aGUgcHJlLXVwZ3JhZGVcbiAgICAgICAgICAgICAgLy8gICAgIHdyaXRlciBtYXkgaGF2ZSBhbHJlYWR5IHB1Ymxpc2hlZCB0aGUgZXZlbnRcbiAgICAgICAgICAgICAgLy8gICAgIHdpdGggaXRzIG93biBldmVudElkLCBhbmQgdGhlIG1hcmtlciBoYXMgbm8gd2F5XG4gICAgICAgICAgICAgIC8vICAgICBvZiBrbm93aW5nIHRoYXQgZXZlbnRJZCBhZnRlciB0aGUgZmFjdC4gV2l0aG91dFxuICAgICAgICAgICAgICAvLyAgICAgdGhpcyBwcm9iZSwgYSBwb3N0LXVwZ3JhZGUgcmV0cnkgd291bGQgcGluIGFcbiAgICAgICAgICAgICAgLy8gICAgIGRpZmZlcmVudCBldmVudElkLCB3cml0ZSBhIGhvb2sgZW50aXR5LCBhbmRcbiAgICAgICAgICAgICAgLy8gICAgIHB1Ymxpc2ggYSBkdXBsaWNhdGUgZXZlbnQgYXQgdGhlIG1hcmtlcidzIHBhdGguXG4gICAgICAgICAgICAgIGxldCBjYW5vbmljYWxFdmVudElkOiBzdHJpbmc7XG4gICAgICAgICAgICAgIGlmIChleGlzdGluZ0NsYWltLmV2ZW50SWQpIHtcbiAgICAgICAgICAgICAgICBjYW5vbmljYWxFdmVudElkID0gZXhpc3RpbmdDbGFpbS5ldmVudElkO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnN0IGFscmVhZHlQdWJsaXNoZWRFdmVudElkID1cbiAgICAgICAgICAgICAgICAgIGF3YWl0IGZpbmRFeGlzdGluZ0hvb2tDcmVhdGVkRXZlbnRJZChcbiAgICAgICAgICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICAgICAgICAgZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgICAgIGRhdGEuY29ycmVsYXRpb25JZFxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBpZiAoYWxyZWFkeVB1Ymxpc2hlZEV2ZW50SWQgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIC8vIFRoZSBwcmUtdXBncmFkZSB3cml0ZXIgbWF5IGhhdmUgY3Jhc2hlZCBiZXR3ZWVuXG4gICAgICAgICAgICAgICAgICAvLyBpdHMgZXZlbnQgcHVibGlzaCBhbmQgaXRzIGhvb2sgZW50aXR5IHdyaXRlIOKAlFxuICAgICAgICAgICAgICAgICAgLy8gcmVwYWlyIHRoZSBlbnRpdHkgZnJvbSB0aGUgcGVyc2lzdGVkIGV2ZW50J3NcbiAgICAgICAgICAgICAgICAgIC8vIHBheWxvYWQgYmVmb3JlIHN1cmZhY2luZyB0aGUgYmVuaWduIGR1cGxpY2F0ZS5cbiAgICAgICAgICAgICAgICAgIGF3YWl0IHJlcGFpckhvb2tFbnRpdHlGcm9tUGVyc2lzdGVkRXZlbnQoXG4gICAgICAgICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICAgICAgICBkYXRhLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgICAgICAgIGFscmVhZHlQdWJsaXNoZWRFdmVudElkLFxuICAgICAgICAgICAgICAgICAgICB0YWdcbiAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRW50aXR5Q29uZmxpY3RFcnJvcihcbiAgICAgICAgICAgICAgICAgICAgYEhvb2sgXCIke2RhdGEuY29ycmVsYXRpb25JZH1cIiBhbHJlYWR5IGNyZWF0ZWRgXG4gICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb25zdCBwaW5uZWQgPSBhd2FpdCBwaW5DYW5vbmljYWxFdmVudElkRm9yTGVnYWN5Q2xhaW0oXG4gICAgICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAgICAgaG9va0RhdGEudG9rZW4sXG4gICAgICAgICAgICAgICAgICBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgICAgIGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgICAgIGV2ZW50SWRcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIGlmIChwaW5uZWQgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgIC8vIExvc3QgdGhlIG1hcmtlciByYWNlIGFuZCB0aGUgbWFya2VyIGZpbGUgaXNcbiAgICAgICAgICAgICAgICAgIC8vIHVucmVhZGFibGUgKGV4dHJlbWVseSByYXJlOyBjb3JydXB0ZWQgZGlzaykuXG4gICAgICAgICAgICAgICAgICAvLyBUcmVhdCBhcyBhIHJlYWwgZHVwbGljYXRlIHNvIHRoZSBydW50aW1lJ3NcbiAgICAgICAgICAgICAgICAgIC8vIGNvbmN1cnJlbnQtcmVwbGF5IGNhdGNoIHBhdGggc3dhbGxvd3MgdGhpc1xuICAgICAgICAgICAgICAgICAgLy8gYXR0ZW1wdCBpbnN0ZWFkIG9mIHJpc2tpbmcgZGl2ZXJnZW50XG4gICAgICAgICAgICAgICAgICAvLyBwdWJsaWNhdGlvbi5cbiAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICAgICAgICBgSG9vayBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIGFscmVhZHkgY3JlYXRlZGBcbiAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNhbm9uaWNhbEV2ZW50SWQgPSBwaW5uZWQ7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAvLyBSZWJ1aWxkIGBldmVudGAgd2l0aCB0aGUgY2Fub25pY2FsIGV2ZW50SWQgYW5kIGFcbiAgICAgICAgICAgICAgLy8gZGV0ZXJtaW5pc3RpYyBgY3JlYXRlZEF0YCBkZXJpdmVkIGZyb20gdGhlIGV2ZW50SWRcbiAgICAgICAgICAgICAgLy8gKGEgVUxJRCkgc28gdHdvIHdvcmtlcnMgd3JpdGluZyB0aGUgc2FtZSBldmVudFxuICAgICAgICAgICAgICAvLyBwcm9kdWNlIGJ5dGUtaWRlbnRpY2FsIGNvbnRlbnQuXG4gICAgICAgICAgICAgIGV2ZW50SWQgPSBjYW5vbmljYWxFdmVudElkO1xuICAgICAgICAgICAgICBjb25zdCBjYW5vbmljYWxDcmVhdGVkQXQgPVxuICAgICAgICAgICAgICAgIHVsaWRUb0RhdGUoZXZlbnRJZC5yZXBsYWNlKC9eZXZudF8vLCAnJykpID8/IG5vdztcbiAgICAgICAgICAgICAgZXZlbnQgPSB7XG4gICAgICAgICAgICAgICAgLi4uZGF0YSxcbiAgICAgICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgICAgICBjcmVhdGVkQXQ6IGNhbm9uaWNhbENyZWF0ZWRBdCxcbiAgICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIHdyaXRlSG9va0VudGl0eVdpdGhPdmVyd3JpdGUgPSB0cnVlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgLy8gQ3Jvc3MtaG9vayAvIGNyb3NzLXJ1biBjb25mbGljdDogYSBkaWZmZXJlbnRcbiAgICAgICAgICAgICAgLy8gKHJ1bklkLCBob29rSWQpIGhvbGRzIHRoaXMgdG9rZW4uIENyZWF0ZSBhXG4gICAgICAgICAgICAgIC8vIGhvb2tfY29uZmxpY3QgZXZlbnQgc28gdGhlIHdvcmtmbG93IGNhbiBmYWlsXG4gICAgICAgICAgICAgIC8vIGdyYWNlZnVsbHkgd2hlbiB0aGUgaG9vayBpcyBhd2FpdGVkLlxuICAgICAgICAgICAgICBjb25zdCBjb25mbGljdEV2ZW50OiBFdmVudCA9IHtcbiAgICAgICAgICAgICAgICBldmVudFR5cGU6ICdob29rX2NvbmZsaWN0JyxcbiAgICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBkYXRhLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgICB0b2tlbjogaG9va0RhdGEudG9rZW4sXG4gICAgICAgICAgICAgICAgICAuLi4oZXhpc3RpbmdDbGFpbVxuICAgICAgICAgICAgICAgICAgICA/IHsgY29uZmxpY3RpbmdSdW5JZDogZXhpc3RpbmdDbGFpbS5ydW5JZCB9XG4gICAgICAgICAgICAgICAgICAgIDoge30pLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgcnVuSWQ6IGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICAgIGV2ZW50SWQsXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgIGNvbnN0IGNvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2V2ZW50SWR9YDtcbiAgICAgICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ2V2ZW50cycsIGNvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgICBjb25mbGljdEV2ZW50XG4gICAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgICAgY29uc3QgcmVzb2x2ZURhdGEgPVxuICAgICAgICAgICAgICAgIHBhcmFtcz8ucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgICAgICAgICBjb25zdCBmaWx0ZXJlZEV2ZW50ID0gc3RyaXBFdmVudERhdGFSZWZzKFxuICAgICAgICAgICAgICAgIGNvbmZsaWN0RXZlbnQsXG4gICAgICAgICAgICAgICAgcmVzb2x2ZURhdGFcbiAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAvLyBSZXR1cm4gRXZlbnRSZXN1bHQgd2l0aCBjb25mbGljdCBldmVudCAobm8gaG9vayBlbnRpdHkgY3JlYXRlZClcbiAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBldmVudDogZmlsdGVyZWRFdmVudCxcbiAgICAgICAgICAgICAgICBydW4sXG4gICAgICAgICAgICAgICAgc3RlcCxcbiAgICAgICAgICAgICAgICBob29rOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ29tcHV0ZSB0aGUgaG9vayBlbnRpdHkgbm93LCBidXQgZGVmZXIgaXRzIHdyaXRlIHVudGlsXG4gICAgICAgICAgLy8gQUZURVIgdGhlIG91dGVyIGV2ZW50IHB1Ymxpc2ggYXQgdGhlIGJvdHRvbSBvZiB0aGlzXG4gICAgICAgICAgLy8gZnVuY3Rpb24gY29tbWl0cy4gVGhlIHJldHJ5IHBhdGggY2FuIHJlYWNoIHRoaXMgYnJhbmNoXG4gICAgICAgICAgLy8gZm9yIGEgaG9vayB3aG9zZSBgaG9va19jcmVhdGVkYCBldmVudCB3YXMgYWxyZWFkeVxuICAgICAgICAgIC8vIHB1Ymxpc2hlZCBzdWNjZXNzZnVsbHkgKGFuIFwiYWxyZWFkeS1jb21taXR0ZWRcbiAgICAgICAgICAvLyBkdXBsaWNhdGVcIik7IGluIHRoYXQgY2FzZSB0aGUgb3V0ZXIgYHdyaXRlRXhjbHVzaXZlYFxuICAgICAgICAgIC8vIGZvciB0aGUgZXZlbnQgd2lsbCByZXR1cm4gZmFsc2UgYW5kIHdlIHdpbGwgdGhyb3dcbiAgICAgICAgICAvLyBgRW50aXR5Q29uZmxpY3RFcnJvcmAuIFdyaXRpbmcgdGhlIGhvb2sgZW50aXR5IGhlcmVcbiAgICAgICAgICAvLyBmaXJzdCB3b3VsZCBtdXRhdGUgYWxyZWFkeS1jb21taXR0ZWQgZHVyYWJsZSBzdGF0ZSB3aXRoXG4gICAgICAgICAgLy8gdGhlIHJldHJ5J3MgcGF5bG9hZCAoZS5nLiBkaWZmZXJlbnQgYG1ldGFkYXRhYCBvclxuICAgICAgICAgIC8vIGBpc1dlYmhvb2tgKSBiZWZvcmUgdGhlIGV2ZW50IHB1Ymxpc2ggcHJvdmVkIHdoaWNoXG4gICAgICAgICAgLy8gb3V0Y29tZSB3ZSBhcmUgaW4g4oCUIGxlYXZpbmcgdGhlIGVudGl0eSBhbmQgZXZlbnQgbG9nXG4gICAgICAgICAgLy8gaW5jb25zaXN0ZW50LiBCeSBkZWZlcnJpbmcsIHRoZSBlbnRpdHkgaXMgb25seSB3cml0dGVuXG4gICAgICAgICAgLy8gd2hlbiB0aGUgcHVibGlzaCBhY3R1YWxseSBzdWNjZWVkcyAoZmlyc3Qgd3JpdGVyIG9yXG4gICAgICAgICAgLy8gb3JwaGFuIHJlY292ZXJ5KS4gU2VlIHByYW5heWdwJ3MgcmV2aWV3IG9uIFBSICMyMjk1IGZvclxuICAgICAgICAgIC8vIHRoZSBrYXJ0aGlrc2NhbGUzIHJlcHJvLlxuICAgICAgICAgIGhvb2sgPSB7XG4gICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICBob29rSWQ6IGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIHRva2VuOiBob29rRGF0YS50b2tlbixcbiAgICAgICAgICAgIG1ldGFkYXRhOiBob29rRGF0YS5tZXRhZGF0YSxcbiAgICAgICAgICAgIG93bmVySWQ6ICdsb2NhbC1vd25lcicsXG4gICAgICAgICAgICBwcm9qZWN0SWQ6ICdsb2NhbC1wcm9qZWN0JyxcbiAgICAgICAgICAgIGVudmlyb25tZW50OiAnbG9jYWwnLFxuICAgICAgICAgICAgLy8gVXNlIHRoZSAocG9zc2libHkgY2Fub25pY2FsKSBldmVudCdzIGNyZWF0ZWRBdCBzbyB0d29cbiAgICAgICAgICAgIC8vIHdvcmtlcnMgd3JpdGluZyB0aGUgc2FtZSBob29rIGVudGl0eSBwcm9kdWNlIGJ5dGUtXG4gICAgICAgICAgICAvLyBpZGVudGljYWwgY29udGVudCBkdXJpbmcgY29udmVyZ2VuY2UuXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IGV2ZW50LmNyZWF0ZWRBdCxcbiAgICAgICAgICAgIC8vIFByb3BhZ2F0ZSBzcGVjVmVyc2lvbiBmcm9tIHRoZSBldmVudCB0byB0aGUgaG9vayBlbnRpdHlcbiAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBlZmZlY3RpdmVTcGVjVmVyc2lvbixcbiAgICAgICAgICAgIGlzV2ViaG9vazogaG9va0RhdGEuaXNXZWJob29rID8/IGZhbHNlLFxuICAgICAgICAgICAgaXNTeXN0ZW06IGhvb2tEYXRhLmlzU3lzdGVtID8/IGZhbHNlLFxuICAgICAgICAgIH07XG4gICAgICAgICAgaG9va0VudGl0eVdyaXRlT3B0aW9ucyA9IHdyaXRlSG9va0VudGl0eVdpdGhPdmVyd3JpdGVcbiAgICAgICAgICAgID8geyBvdmVyd3JpdGU6IHRydWUgfVxuICAgICAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdob29rX2Rpc3Bvc2VkJykge1xuICAgICAgICAgIC8vIGhvb2tfZGlzcG9zZWQ6IERlbGV0ZXMgaG9vayBlbnRpdHksIHJlamVjdHMgZHVwbGljYXRlcy5cbiAgICAgICAgICAvLyBVc2VzIHdyaXRlRXhjbHVzaXZlIG9uIGEgbG9jayBmaWxlIHRvIGF0b21pY2FsbHkgcHJldmVudCBjb25jdXJyZW50XG4gICAgICAgICAgLy8gaW52b2NhdGlvbnMgZnJvbSBib3RoIGRpc3Bvc2luZyB0aGUgc2FtZSBob29rIChUT0NUT1UgcmFjZSkuXG4gICAgICAgICAgY29uc3QgaG9va0xvY2tOYW1lID0gdGFnXG4gICAgICAgICAgICA/IGAke2RhdGEuY29ycmVsYXRpb25JZH0uZGlzcG9zZWQuJHt0YWd9YFxuICAgICAgICAgICAgOiBgJHtkYXRhLmNvcnJlbGF0aW9uSWR9LmRpc3Bvc2VkYDtcbiAgICAgICAgICBjb25zdCBsb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICcubG9ja3MnLFxuICAgICAgICAgICAgJ2hvb2tzJyxcbiAgICAgICAgICAgIGhvb2tMb2NrTmFtZVxuICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3QgY2xhaW1lZCA9IGF3YWl0IHdyaXRlRXhjbHVzaXZlKGxvY2tQYXRoLCAnJyk7XG4gICAgICAgICAgaWYgKCFjbGFpbWVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRW50aXR5Q29uZmxpY3RFcnJvcihcbiAgICAgICAgICAgICAgYEhvb2sgXCIke2RhdGEuY29ycmVsYXRpb25JZH1cIiBhbHJlYWR5IGRpc3Bvc2VkYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gUmVhZCB0aGUgaG9vayB0byBnZXQgaXRzIHRva2VuIGJlZm9yZSBkZWxldGluZ1xuICAgICAgICAgIGNvbnN0IGhvb2tQYXRoID0gdGFnZ2VkUGF0aChcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnaG9va3MnLFxuICAgICAgICAgICAgZGF0YS5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgdGFnXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBleGlzdGluZ0hvb2sgPSBhd2FpdCByZWFkSlNPTldpdGhGYWxsYmFjayhcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnaG9va3MnLFxuICAgICAgICAgICAgZGF0YS5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgSG9va1NjaGVtYSxcbiAgICAgICAgICAgIHRhZ1xuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKGV4aXN0aW5nSG9vaykge1xuICAgICAgICAgICAgLy8gRGVsZXRlIHRoZSB0b2tlbiBjb25zdHJhaW50IGZpbGUgdG8gZnJlZSB1cCB0aGUgdG9rZW5cbiAgICAgICAgICAgIC8vIGZvciByZXVzZSwgYW5kIGRlbGV0ZSB0aGlzIGhvb2sncyByZWNvdmVyeSBtYXJrZXIgKGlmXG4gICAgICAgICAgICAvLyBhbnkpIGZvciBkaXNrIGh5Z2llbmUuIFRoZSBtYXJrZXIncyBmaWxlbmFtZSBoYXNoXG4gICAgICAgICAgICAvLyBpbmNsdWRlcyBgKHRva2VuLCBydW5JZCwgaG9va0lkKWAgc28gZGlmZmVyZW50XG4gICAgICAgICAgICAvLyBsaWZldGltZXMgbmV2ZXIgY29sbGlkZSwgYnV0IGNsZWFuaW5nIHVwIHJlZHVjZXMgZGlza1xuICAgICAgICAgICAgLy8gbGVhayBmb3IgaG9va3MgdGhhdCBnbyB0aHJvdWdoIHRoZSByZWNvdmVyeSBwYXRoLlxuICAgICAgICAgICAgY29uc3QgZGlzcG9zZWRDb25zdHJhaW50UGF0aCA9IHBhdGguam9pbihcbiAgICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICAgJ2hvb2tzJyxcbiAgICAgICAgICAgICAgJ3Rva2VucycsXG4gICAgICAgICAgICAgIGAke2hhc2hUb2tlbihleGlzdGluZ0hvb2sudG9rZW4pfS5qc29uYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGF3YWl0IGRlbGV0ZUpTT04oZGlzcG9zZWRDb25zdHJhaW50UGF0aCk7XG4gICAgICAgICAgICBhd2FpdCBkZWxldGVKU09OKFxuICAgICAgICAgICAgICBob29rUmVjb3ZlcnlNYXJrZXJQYXRoKFxuICAgICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICAgZXhpc3RpbmdIb29rLnRva2VuLFxuICAgICAgICAgICAgICAgIGV4aXN0aW5nSG9vay5ydW5JZCxcbiAgICAgICAgICAgICAgICBleGlzdGluZ0hvb2suaG9va0lkXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGF3YWl0IGRlbGV0ZUpTT04oaG9va1BhdGgpO1xuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAnd2FpdF9jcmVhdGVkJyAmJiAnZXZlbnREYXRhJyBpbiBkYXRhKSB7XG4gICAgICAgICAgLy8gd2FpdF9jcmVhdGVkOiBDcmVhdGVzIHdhaXQgZW50aXR5IHdpdGggc3RhdHVzICd3YWl0aW5nJy5cbiAgICAgICAgICAvLyBBdG9taWMgY2xhaW0gb24gYSBwZXItKHJ1bklkLCBjb3JyZWxhdGlvbklkKSBjb25zdHJhaW50IGZpbGVcbiAgICAgICAgICAvLyBlbnN1cmVzIGR1cGxpY2F0ZSB3YWl0X2NyZWF0ZWQgZnJvbSBjb25jdXJyZW50IGludm9jYXRpb25zXG4gICAgICAgICAgLy8gc3VyZmFjZXMgYXMgRW50aXR5Q29uZmxpY3RFcnJvciAocmVwbGFjZXMgYSBwcmlvciBUT0NUT1VcbiAgICAgICAgICAvLyByZWFkLXRoZW4tY2hlY2sgdGhhdCBjb3VsZCBsZXQgYm90aCB3cml0ZXJzIHRocm91Z2gpLlxuICAgICAgICAgIGNvbnN0IHdhaXRDb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9YDtcbiAgICAgICAgICBjb25zdCB3YWl0Q3JlYXRlZExvY2tOYW1lID0gdGFnXG4gICAgICAgICAgICA/IGAke3dhaXRDb21wb3NpdGVLZXl9LmNyZWF0ZWQuJHt0YWd9YFxuICAgICAgICAgICAgOiBgJHt3YWl0Q29tcG9zaXRlS2V5fS5jcmVhdGVkYDtcbiAgICAgICAgICBjb25zdCB3YWl0Q3JlYXRlZExvY2tQYXRoID0gcmVzb2x2ZVdpdGhpbkJhc2UoXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJy5sb2NrcycsXG4gICAgICAgICAgICAnd2FpdHMnLFxuICAgICAgICAgICAgd2FpdENyZWF0ZWRMb2NrTmFtZVxuICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3Qgd2FpdENyZWF0ZWRDbGFpbWVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUoXG4gICAgICAgICAgICB3YWl0Q3JlYXRlZExvY2tQYXRoLFxuICAgICAgICAgICAgJydcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmICghd2FpdENyZWF0ZWRDbGFpbWVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRW50aXR5Q29uZmxpY3RFcnJvcihcbiAgICAgICAgICAgICAgYFdhaXQgXCIke2RhdGEuY29ycmVsYXRpb25JZH1cIiBhbHJlYWR5IGV4aXN0c2BcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IHdhaXREYXRhID0gZGF0YS5ldmVudERhdGEgYXMge1xuICAgICAgICAgICAgcmVzdW1lQXQ/OiBEYXRlO1xuICAgICAgICAgIH07XG4gICAgICAgICAgd2FpdCA9IHtcbiAgICAgICAgICAgIHdhaXRJZDogd2FpdENvbXBvc2l0ZUtleSxcbiAgICAgICAgICAgIHJ1bklkOiBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgIHN0YXR1czogJ3dhaXRpbmcnLFxuICAgICAgICAgICAgcmVzdW1lQXQ6IHdhaXREYXRhLnJlc3VtZUF0LFxuICAgICAgICAgICAgY29tcGxldGVkQXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgICAgfTtcbiAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICd3YWl0cycsIHdhaXRDb21wb3NpdGVLZXksIHRhZyksXG4gICAgICAgICAgICB3YWl0XG4gICAgICAgICAgKTtcbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3dhaXRfY29tcGxldGVkJykge1xuICAgICAgICAgIC8vIHdhaXRfY29tcGxldGVkOiBUcmFuc2l0aW9ucyB3YWl0IHRvICdjb21wbGV0ZWQnLCByZWplY3RzIGR1cGxpY2F0ZXMuXG4gICAgICAgICAgLy8gVXNlcyB3cml0ZUV4Y2x1c2l2ZSBvbiBhIGxvY2sgZmlsZSB0byBhdG9taWNhbGx5IHByZXZlbnQgY29uY3VycmVudFxuICAgICAgICAgIC8vIGludm9jYXRpb25zIGZyb20gYm90aCBjb21wbGV0aW5nIHRoZSBzYW1lIHdhaXQgKFRPQ1RPVSByYWNlKS5cbiAgICAgICAgICBjb25zdCB3YWl0Q29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgY29uc3Qgd2FpdExvY2tOYW1lID0gdGFnXG4gICAgICAgICAgICA/IGAke3dhaXRDb21wb3NpdGVLZXl9LmNvbXBsZXRlZC4ke3RhZ31gXG4gICAgICAgICAgICA6IGAke3dhaXRDb21wb3NpdGVLZXl9LmNvbXBsZXRlZGA7XG4gICAgICAgICAgY29uc3QgbG9ja1BhdGggPSByZXNvbHZlV2l0aGluQmFzZShcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnLmxvY2tzJyxcbiAgICAgICAgICAgICd3YWl0cycsXG4gICAgICAgICAgICB3YWl0TG9ja05hbWVcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnN0IGNsYWltZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShsb2NrUGF0aCwgJycpO1xuICAgICAgICAgIGlmICghY2xhaW1lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBXYWl0IFwiJHtkYXRhLmNvcnJlbGF0aW9uSWR9XCIgYWxyZWFkeSBjb21wbGV0ZWRgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBleGlzdGluZ1dhaXQgPSBhd2FpdCByZWFkSlNPTldpdGhGYWxsYmFjayhcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnd2FpdHMnLFxuICAgICAgICAgICAgd2FpdENvbXBvc2l0ZUtleSxcbiAgICAgICAgICAgIFdhaXRTY2hlbWEsXG4gICAgICAgICAgICB0YWdcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmICghZXhpc3RpbmdXYWl0KSB7XG4gICAgICAgICAgICAvLyBDbGVhbiB1cCB0aGUgbG9jayBmaWxlIHdlIGp1c3QgY2xhaW1lZCDigJQgdGhlIHdhaXQgZG9lc24ndCBleGlzdFxuICAgICAgICAgICAgYXdhaXQgZnMudW5saW5rKGxvY2tQYXRoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICAgICAgICB0aHJvdyBuZXcgV29ya2Zsb3dXb3JsZEVycm9yKFxuICAgICAgICAgICAgICBgV2FpdCBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIG5vdCBmb3VuZGBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIFRoZSBsb2NrIGZpbGUgKHdyaXRlRXhjbHVzaXZlIGFib3ZlKSBhbHJlYWR5IHByZXZlbnRzIGNvbmN1cnJlbnRcbiAgICAgICAgICAvLyBjb21wbGV0aW9ucyDigJQgbm8gYWRkaXRpb25hbCBzdGF0dXMgY2hlY2sgbmVlZGVkLlxuICAgICAgICAgIHdhaXQgPSB7XG4gICAgICAgICAgICAuLi5leGlzdGluZ1dhaXQsXG4gICAgICAgICAgICBzdGF0dXM6ICdjb21wbGV0ZWQnLFxuICAgICAgICAgICAgY29tcGxldGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgIH07XG4gICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnd2FpdHMnLCB3YWl0Q29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgd2FpdCxcbiAgICAgICAgICAgIHsgb3ZlcndyaXRlOiB0cnVlIH1cbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIC8vIE5vdGU6IGhvb2tfcmVjZWl2ZWQgZXZlbnRzIGFyZSBzdG9yZWQgaW4gdGhlIGV2ZW50IGxvZyBidXQgZG9uJ3RcbiAgICAgICAgLy8gbW9kaWZ5IHRoZSBIb29rIGVudGl0eSAod2hpY2ggZG9lc24ndCBoYXZlIGEgcGF5bG9hZCBmaWVsZClcblxuICAgICAgICAvLyBTdG9yZSBldmVudCB1c2luZyBjb21wb3NpdGUga2V5IHtydW5JZH0te2V2ZW50SWR9LlxuICAgICAgICAvL1xuICAgICAgICAvLyBgd3JpdGVFeGNsdXNpdmVgIChPX0NSRUFUfE9fRVhDTCB2aWEgdGVtcC1maWxlICsgaGFyZC1saW5rKVxuICAgICAgICAvLyBpcyB0aGUgY3Jvc3MtcHJvY2VzcyBhdG9taWMgcHVibGlzaCBwcmltaXRpdmU6IGlmIHRoZSBmaWxlXG4gICAgICAgIC8vIGFscmVhZHkgZXhpc3RzLCByZXR1cm5zIGZhbHNlIGluc3RlYWQgb2Ygb3ZlcndyaXRpbmcuIFRoaXNcbiAgICAgICAgLy8gaXMgY3JpdGljYWwgZm9yIHRoZSBob29rX2NyZWF0ZWQgZGVkdXAtcmVjb3ZlcnkgY29udmVyZ2VuY2VcbiAgICAgICAgLy8gKGFib3ZlKSDigJQgdHdvIHdvcmtlcnMgdGhhdCBhZG9wdCB0aGUgc2FtZSBjYW5vbmljYWwgZXZlbnRJZFxuICAgICAgICAvLyByYWNlIGhlcmU7IHdob2V2ZXIgbGlua3MgdGhlIGZpbGUgZmlyc3Qgd2lucywgdGhlIGxvc2VyXG4gICAgICAgIC8vIHRocm93cyBFbnRpdHlDb25mbGljdEVycm9yLCBhbmQgdGhlIHJ1bnRpbWUncyBleGlzdGluZ1xuICAgICAgICAvLyBjb25jdXJyZW50LXJlcGxheSBjYXRjaCBwYXRoIGF0IHN1c3BlbnNpb24taGFuZGxlci50czoxNDJcbiAgICAgICAgLy8gc3dhbGxvd3MgaXQuIEZvciBhbGwgb3RoZXIgZXZlbnQgdHlwZXMsIGV2ZW50SWRzIGFyZVxuICAgICAgICAvLyBtb25vdG9uaWMgVUxJRHMgKGdsb2JhbGx5IHVuaXF1ZSBieSBjb25zdHJ1Y3Rpb24pIHNvIGFcbiAgICAgICAgLy8gY29sbGlzaW9uIGluZGljYXRlcyBhIHJlYWwgYnVnIGFuZCBFbnRpdHlDb25mbGljdEVycm9yIGlzXG4gICAgICAgIC8vIGFsc28gdGhlIHJpZ2h0IHN1cmZhY2Ug4oCUIHNhbWUgc2hhcGUgYXMgc3RlcF9jcmVhdGVkJ3NcbiAgICAgICAgLy8gY2xhaW0tZmlsZSBiZWhhdmlvci5cbiAgICAgICAgY29uc3QgY29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZXZlbnRJZH1gO1xuICAgICAgICBjb25zdCBldmVudFB1Ymxpc2hlZCA9IGF3YWl0IHdyaXRlRXhjbHVzaXZlKFxuICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ2V2ZW50cycsIGNvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICBKU09OLnN0cmluZ2lmeShldmVudCwganNvblJlcGxhY2VyLCAyKVxuICAgICAgICApO1xuICAgICAgICBpZiAoIWV2ZW50UHVibGlzaGVkKSB7XG4gICAgICAgICAgLy8gRm9yIGBob29rX2NyZWF0ZWRgLCBsb3NpbmcgdGhlIGV2ZW50IHB1Ymxpc2ggbWVhbnMgdGhlXG4gICAgICAgICAgLy8gZXZlbnQgd2FzIGFscmVhZHkgY29tbWl0dGVkIGF0IHRoaXMgZXhhY3QgKGNhbm9uaWNhbClcbiAgICAgICAgICAvLyBwYXRoLiBUaGUgb3JpZ2luYWwgcHVibGlzaGVyIG1heSBoYXZlIGNyYXNoZWQgYmV0d2VlblxuICAgICAgICAgIC8vIGl0cyBldmVudCBwdWJsaXNoIGFuZCBpdHMgZGVmZXJyZWQgaG9vay1lbnRpdHkgd3JpdGVcbiAgICAgICAgICAvLyAodGhlIGludmVyc2Ugb2YgdGhlIGNyYXNoIHdpbmRvdyB0aGUgZGVmZXJyYWwgY2xvc2VzKSxcbiAgICAgICAgICAvLyBsZWF2aW5nIGFuIGV2ZW50LWZpcnN0IG9ycGhhbjogdGhlIGV2ZW50IGlzIGluIHRoZSBsb2dcbiAgICAgICAgICAvLyBidXQgdGhlIGVudGl0eSBpcyBtaXNzaW5nIGFuZCB0aGUgaG9vayBpcyB1bnJlc29sdmFibGUuXG4gICAgICAgICAgLy8gUmVwYWlyIHRoZSBlbnRpdHkgZnJvbSB0aGUgUEVSU0lTVEVEIGV2ZW50J3MgcGF5bG9hZFxuICAgICAgICAgIC8vIChuZXZlciB0aGUgcmV0cnkncyDigJQgZGlmZmVyZW50IHJldHJ5IG1ldGFkYXRhIG11c3Qgbm90XG4gICAgICAgICAgLy8gY2hhbmdlIGNvbW1pdHRlZCBzdGF0ZSkgYmVmb3JlIHN1cmZhY2luZyB0aGUgYmVuaWduXG4gICAgICAgICAgLy8gZHVwbGljYXRlIHRvIHRoZSBydW50aW1lJ3MgY29uY3VycmVudC1yZXBsYXkgY2F0Y2ggcGF0aC5cbiAgICAgICAgICBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdob29rX2NyZWF0ZWQnICYmIGRhdGEuY29ycmVsYXRpb25JZCkge1xuICAgICAgICAgICAgYXdhaXQgcmVwYWlySG9va0VudGl0eUZyb21QZXJzaXN0ZWRFdmVudChcbiAgICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICAgZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgIGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgICAgdGFnXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aHJvdyBuZXcgRW50aXR5Q29uZmxpY3RFcnJvcihcbiAgICAgICAgICAgIGBFdmVudCBcIiR7ZXZlbnRJZH1cIiBhbHJlYWR5IGV4aXN0cyBmb3IgcnVuIFwiJHtlZmZlY3RpdmVSdW5JZH1cImBcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gV3JpdGUgdGhlIGhvb2sgZW50aXR5IE9OTFkgbm93IHRoYXQgdGhlIGV2ZW50IHB1Ymxpc2ggaGFzXG4gICAgICAgIC8vIGNvbW1pdHRlZC4gRG9pbmcgdGhpcyBlYXJsaWVyIChpbiB0aGUgYGhvb2tfY3JlYXRlZGBcbiAgICAgICAgLy8gYnJhbmNoIGFib3ZlKSB3b3VsZCBtdXRhdGUgYW4gYWxyZWFkeS1jb21taXR0ZWQgaG9va1xuICAgICAgICAvLyBlbnRpdHkgd2l0aCB0aGUgcmV0cnkncyBwYXlsb2FkIGJlZm9yZSB0aGUgZXZlbnQgcHVibGlzaFxuICAgICAgICAvLyBwcm92ZWQgd2hldGhlciB0aGlzIGF0dGVtcHQgd2FzIHJlcGFpcmluZyBhIG1pc3NpbmcgZXZlbnRcbiAgICAgICAgLy8gb3IganVzdCBjb2xsaWRpbmcgd2l0aCBhbiBhbHJlYWR5LXB1Ymxpc2hlZCBgaG9va19jcmVhdGVkYC5cbiAgICAgICAgLy8gVGhlIGJyYW5jaCBzZXRzIGBob29rRW50aXR5V3JpdGVPcHRpb25zYCBpZmYgdGhpcyBldmVudFxuICAgICAgICAvLyB0eXBlIHdyaXRlcyBhbiBlbnRpdHkuXG4gICAgICAgIGlmIChob29rICYmIGRhdGEuZXZlbnRUeXBlID09PSAnaG9va19jcmVhdGVkJykge1xuICAgICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ2hvb2tzJywgaG9vay5ob29rSWQsIHRhZyksXG4gICAgICAgICAgICBob29rLFxuICAgICAgICAgICAgaG9va0VudGl0eVdyaXRlT3B0aW9uc1xuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCByZXNvbHZlRGF0YSA9IHBhcmFtcz8ucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgICBjb25zdCBmaWx0ZXJlZEV2ZW50ID0gc3RyaXBFdmVudERhdGFSZWZzKGV2ZW50LCByZXNvbHZlRGF0YSk7XG5cbiAgICAgICAgLy8gRm9yIHJ1bl9zdGFydGVkOiBwcmVsb2FkIG9uZSBwYWdlIG9mIGV2ZW50cyBzbyB0aGUgcnVudGltZSBjYW4gc2tpcFxuICAgICAgICAvLyB0aGUgaW5pdGlhbCBldmVudHMubGlzdCBjYWxsIHdoZW4gaGFzTW9yZSBpcyBmYWxzZS5cbiAgICAgICAgbGV0IGV2ZW50czogRXZlbnRbXSB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGN1cnNvcjogc3RyaW5nIHwgbnVsbCB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGhhc01vcmU6IGJvb2xlYW4gfCB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJyAmJiBydW4pIHtcbiAgICAgICAgICBjb25zdCBhbGxFdmVudHMgPSBhd2FpdCBwYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnkoe1xuICAgICAgICAgICAgZGlyZWN0b3J5OiBwYXRoLmpvaW4oYmFzZWRpciwgJ2V2ZW50cycpLFxuICAgICAgICAgICAgc2NoZW1hOiBFdmVudFNjaGVtYSxcbiAgICAgICAgICAgIGZpbGVQcmVmaXg6IGAke2VmZmVjdGl2ZVJ1bklkfS1gLFxuICAgICAgICAgICAgc29ydE9yZGVyOiAnYXNjJyxcbiAgICAgICAgICAgIGxpbWl0OiAxMDAwLFxuICAgICAgICAgICAgZ2V0Q3JlYXRlZEF0OiBnZXRPYmplY3RDcmVhdGVkQXQoJ2V2bnQnKSxcbiAgICAgICAgICAgIGdldElkOiAoZSkgPT4gZS5ldmVudElkLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGV2ZW50cyA9IGFsbEV2ZW50cy5kYXRhO1xuICAgICAgICAgIGN1cnNvciA9IGFsbEV2ZW50cy5jdXJzb3I7XG4gICAgICAgICAgaGFzTW9yZSA9IGFsbEV2ZW50cy5oYXNNb3JlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSW5saW5lLWRlbHRhIG9wdGltaXphdGlvbjogb24gYSBzdGVwLXRlcm1pbmFsIHdyaXRlIHRoZSBpbmxpbmVcbiAgICAgICAgLy8gcnVudGltZSBsb29wIGNhbiBwYXNzIGBzaW5jZUN1cnNvcmAgKHRoZSBjdXJzb3IgZnJvbSBiZWZvcmUgaXRcbiAgICAgICAgLy8gYmVnYW4gd3JpdGluZyB0aGlzIHN0ZXAncyBldmVudHMpLiBXZSByZXR1cm4gdGhlIGRlbHRhIG9mXG4gICAgICAgIC8vIGV2ZW50cyB3cml0dGVuIHN0cmljdGx5IGFmdGVyIHRoYXQgY3Vyc29yIOKAlCBleGFjdGx5IHdoYXQgYW5cbiAgICAgICAgLy8gYGV2ZW50cy5saXN0KHsgY3Vyc29yOiBzaW5jZUN1cnNvciwgc29ydE9yZGVyOiAnYXNjJyB9KWAgd291bGRcbiAgICAgICAgLy8gcmV0dXJuIHJpZ2h0IG5vdyDigJQgc28gdGhlIGxvb3AgY2FuIHNraXAgYSByZWR1bmRhbnQgcm91bmQtdHJpcC5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gVGhpcyBpcyBjb21wdXRlZCBhZ2FpbnN0IHRoZSBzYW1lIG9uLWRpc2sgbG9nIHRoZSBsaXN0IHBhdGhcbiAgICAgICAgLy8gcmVhZHMsIHNvIGl0IGNhcHR1cmVzIGV2ZXJ5dGhpbmcgdGhlIGZldGNoIHdvdWxkOiB0aGlzIHN0ZXAnc1xuICAgICAgICAvLyBzdGVwX2NyZWF0ZWQvc3RlcF9zdGFydGVkL3N0ZXBfY29tcGxldGVkLCBhbnkgYXR0cl9zZXQgdGhlIHN0ZXBcbiAgICAgICAgLy8gYm9keSB3cm90ZSwgYW5kIGFueSBpbi1iYW5kIGV2ZW50cyAoZS5nLiBob29rX3JlY2VpdmVkLFxuICAgICAgICAvLyB3YWl0X2NvbXBsZXRlZCkgYW5vdGhlciB3cml0ZXIgYXBwZW5kZWQgc2luY2UgdGhlIGN1cnNvci4gVGhhdFxuICAgICAgICAvLyBlcXVpdmFsZW5jZSBpcyB3aGF0IG1ha2VzIHNraXBwaW5nIHRoZSBmZXRjaCBzYWZlIOKAlCBhIG1pc3NlZFxuICAgICAgICAvLyBpbi1iYW5kIGV2ZW50IGNhbm5vdCBkaXZlcmdlIHJlcGxheSBiZWNhdXNlIHRoZSBkZWx0YSBpcyB0aGVcbiAgICAgICAgLy8gZmV0Y2guXG4gICAgICAgIC8vXG4gICAgICAgIC8vIE9ubHkgc3RlcC10ZXJtaW5hbCBldmVudHMgcXVhbGlmeTogc3RlcF9jcmVhdGVkL3N0ZXBfc3RhcnRlZCBhcmVcbiAgICAgICAgLy8gbm90IGxvb3AgYm91bmRhcmllcyAodGhlIGxvb3AgZmV0Y2hlcyBhZnRlciBzdGVwX2NvbXBsZXRlZCAvXG4gICAgICAgIC8vIHN0ZXBfZmFpbGVkKSwgYW5kIHJ1bi10ZXJtaW5hbCBldmVudHMgZW5kIHRoZSBsb29wLiBgcmVzb2x2ZURhdGFgXG4gICAgICAgIC8vIG1hdGNoZXMgdGhlIGxpc3QgcGF0aCBzbyBldmVudERhdGEgcmVmcyBhcmUgaGFuZGxlZCBpZGVudGljYWxseS5cbiAgICAgICAgaWYgKFxuICAgICAgICAgIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfY29tcGxldGVkJyB8fFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2ZhaWxlZCcpICYmXG4gICAgICAgICAgdHlwZW9mIHBhcmFtcz8uc2luY2VDdXJzb3IgPT09ICdzdHJpbmcnXG4gICAgICAgICkge1xuICAgICAgICAgIC8vIEludGVudGlvbmFsbHkgbm8gYGxpbWl0YDogdGhpcyByZXR1cm5zIGEgc2luZ2xlIGRlZmF1bHQtc2l6ZSBwYWdlLFxuICAgICAgICAgIC8vIHVubGlrZSB0aGUgYGV2ZW50cy5saXN0YCBwYXRoIHdoaWNoIGxvb3BzIGB3aGlsZSAoaGFzTW9yZSlgIHRvXG4gICAgICAgICAgLy8gZXhoYXVzdGlvbi4gVGhhdCBpcyBzYWZlIOKAlCBhbmQgbXVzdCBOT1QgYmUgXCJmaXhlZFwiIGJ5IHBhZ2luYXRpbmdcbiAgICAgICAgICAvLyBoZXJlIOKAlCBiZWNhdXNlIHRoZSBjb250cmFjdCBpcyBzaW5nbGUtcGFnZS1vci1mYWxsYmFjaywgbm90XG4gICAgICAgICAgLy8gY29tcGxldGUtZGVsdGEuIFdoZW4gdGhlIGRlbHRhIG92ZXJmbG93cyBvbmUgcGFnZSxcbiAgICAgICAgICAvLyBwYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnkgc2V0cyBgaGFzTW9yZTogdHJ1ZWAgYW5kIHNsaWNlcyBgZGF0YWAgdG9cbiAgICAgICAgICAvLyB0aGUgcGFnZSAoc2VlIGZzLnRzKSwgd2hpY2ggd2UgZm9yd2FyZCB2ZXJiYXRpbSBiZWxvdy4gVGhlIFNES1xuICAgICAgICAgIC8vIGNvbnN1bWUgc2lkZSAocnVudGltZS50cykgb25seSBzdGFzaGVzIHRoZSBkZWx0YSB3aGVuIGAhaGFzTW9yZWBcbiAgICAgICAgICAvLyBhbmQgb3RoZXJ3aXNlIGZhbGxzIGJhY2sgdG8gdGhlIGV4aGF1c3RpdmUgYGV2ZW50cy5saXN0YCBsb29wLCBzbyBhXG4gICAgICAgICAgLy8gdHJ1bmNhdGVkIHBhZ2UgaXMgbmV2ZXIgY29uc3VtZWQgYXMgaWYgaXQgd2VyZSB0aGUgZnVsbCBkZWx0YS5cbiAgICAgICAgICBjb25zdCBkZWx0YSA9IGF3YWl0IHBhZ2luYXRlZEZpbGVTeXN0ZW1RdWVyeSh7XG4gICAgICAgICAgICBkaXJlY3Rvcnk6IHBhdGguam9pbihiYXNlZGlyLCAnZXZlbnRzJyksXG4gICAgICAgICAgICBzY2hlbWE6IEV2ZW50U2NoZW1hLFxuICAgICAgICAgICAgZmlsZVByZWZpeDogYCR7ZWZmZWN0aXZlUnVuSWR9LWAsXG4gICAgICAgICAgICBzb3J0T3JkZXI6ICdhc2MnLFxuICAgICAgICAgICAgY3Vyc29yOiBwYXJhbXMuc2luY2VDdXJzb3IsXG4gICAgICAgICAgICBnZXRDcmVhdGVkQXQ6IGdldE9iamVjdENyZWF0ZWRBdCgnZXZudCcpLFxuICAgICAgICAgICAgZ2V0SWQ6IChlKSA9PiBlLmV2ZW50SWQsXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgZXZlbnRzID1cbiAgICAgICAgICAgIHJlc29sdmVEYXRhID09PSAnbm9uZSdcbiAgICAgICAgICAgICAgPyBkZWx0YS5kYXRhLm1hcCgoZSkgPT4gc3RyaXBFdmVudERhdGFSZWZzKGUsIHJlc29sdmVEYXRhKSlcbiAgICAgICAgICAgICAgOiBkZWx0YS5kYXRhO1xuICAgICAgICAgIGN1cnNvciA9IGRlbHRhLmN1cnNvcjtcbiAgICAgICAgICBoYXNNb3JlID0gZGVsdGEuaGFzTW9yZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFJldHVybiBFdmVudFJlc3VsdCB3aXRoIGV2ZW50IGFuZCBhbnkgY3JlYXRlZC91cGRhdGVkIGVudGl0eVxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGV2ZW50OiBmaWx0ZXJlZEV2ZW50LFxuICAgICAgICAgIHJ1bixcbiAgICAgICAgICBzdGVwLFxuICAgICAgICAgIGhvb2ssXG4gICAgICAgICAgd2FpdCxcbiAgICAgICAgICBldmVudHMsXG4gICAgICAgICAgY3Vyc29yLFxuICAgICAgICAgIGhhc01vcmUsXG4gICAgICAgICAgLi4uKHN0ZXBDcmVhdGVkTGF6aWx5ID8geyBzdGVwQ3JlYXRlZDogdHJ1ZSB9IDoge30pLFxuICAgICAgICB9O1xuICAgICAgfSAvLyBlbmQgY3JlYXRlSW1wbFxuICAgIH0sXG5cbiAgICBhc3luYyBnZXQocnVuSWQsIGV2ZW50SWQsIHBhcmFtcykge1xuICAgICAgYXNzZXJ0U2FmZUVudGl0eUlkKCdydW5JZCcsIHJ1bklkKTtcbiAgICAgIGFzc2VydFNhZmVFbnRpdHlJZCgnZXZlbnRJZCcsIGV2ZW50SWQpO1xuICAgICAgY29uc3QgY29tcG9zaXRlS2V5ID0gYCR7cnVuSWR9LSR7ZXZlbnRJZH1gO1xuICAgICAgY29uc3QgZXZlbnQgPSBhd2FpdCByZWFkSlNPTldpdGhGYWxsYmFjayhcbiAgICAgICAgYmFzZWRpcixcbiAgICAgICAgJ2V2ZW50cycsXG4gICAgICAgIGNvbXBvc2l0ZUtleSxcbiAgICAgICAgRXZlbnRTY2hlbWEsXG4gICAgICAgIHRhZ1xuICAgICAgKTtcbiAgICAgIGlmICghZXZlbnQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFdmVudCAke2V2ZW50SWR9IGluIHJ1biAke3J1bklkfSBub3QgZm91bmRgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJlc29sdmVEYXRhID0gcGFyYW1zPy5yZXNvbHZlRGF0YSA/PyBERUZBVUxUX1JFU09MVkVfREFUQV9PUFRJT047XG4gICAgICByZXR1cm4gc3RyaXBFdmVudERhdGFSZWZzKGV2ZW50LCByZXNvbHZlRGF0YSk7XG4gICAgfSxcblxuICAgIGFzeW5jIGxpc3QocGFyYW1zKSB7XG4gICAgICBjb25zdCB7IHJ1bklkIH0gPSBwYXJhbXM7XG4gICAgICBhc3NlcnRTYWZlRW50aXR5SWQoJ3J1bklkJywgcnVuSWQpO1xuICAgICAgY29uc3QgcmVzb2x2ZURhdGEgPSBwYXJhbXMucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcGFnaW5hdGVkRmlsZVN5c3RlbVF1ZXJ5KHtcbiAgICAgICAgZGlyZWN0b3J5OiBwYXRoLmpvaW4oYmFzZWRpciwgJ2V2ZW50cycpLFxuICAgICAgICBzY2hlbWE6IEV2ZW50U2NoZW1hLFxuICAgICAgICBmaWxlUHJlZml4OiBgJHtydW5JZH0tYCxcbiAgICAgICAgLy8gRXZlbnRzIGluIGNocm9ub2xvZ2ljYWwgb3JkZXIgKG9sZGVzdCBmaXJzdCkgYnkgZGVmYXVsdCxcbiAgICAgICAgLy8gZGlmZmVyZW50IGZyb20gdGhlIGRlZmF1bHQgZm9yIG90aGVyIGxpc3QgY2FsbHMuXG4gICAgICAgIHNvcnRPcmRlcjogcGFyYW1zLnBhZ2luYXRpb24/LnNvcnRPcmRlciA/PyAnYXNjJyxcbiAgICAgICAgbGltaXQ6IHBhcmFtcy5wYWdpbmF0aW9uPy5saW1pdCxcbiAgICAgICAgY3Vyc29yOiBwYXJhbXMucGFnaW5hdGlvbj8uY3Vyc29yLFxuICAgICAgICBnZXRDcmVhdGVkQXQ6IGdldE9iamVjdENyZWF0ZWRBdCgnZXZudCcpLFxuICAgICAgICBnZXRJZDogKGV2ZW50KSA9PiBldmVudC5ldmVudElkLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIElmIHJlc29sdmVEYXRhIGlzIFwibm9uZVwiLCByZW1vdmUgZXZlbnREYXRhIGZyb20gZXZlbnRzXG4gICAgICBpZiAocmVzb2x2ZURhdGEgPT09ICdub25lJykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLnJlc3VsdCxcbiAgICAgICAgICBkYXRhOiByZXN1bHQuZGF0YS5tYXAoKGV2ZW50KSA9PlxuICAgICAgICAgICAgc3RyaXBFdmVudERhdGFSZWZzKGV2ZW50LCByZXNvbHZlRGF0YSlcbiAgICAgICAgICApLFxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0sXG5cbiAgICBhc3luYyBsaXN0QnlDb3JyZWxhdGlvbklkKHBhcmFtcykge1xuICAgICAgY29uc3QgY29ycmVsYXRpb25JZCA9IHBhcmFtcy5jb3JyZWxhdGlvbklkO1xuICAgICAgYXNzZXJ0U2FmZUVudGl0eUlkKCdjb3JyZWxhdGlvbklkJywgY29ycmVsYXRpb25JZCk7XG4gICAgICBjb25zdCByZXNvbHZlRGF0YSA9IHBhcmFtcy5yZXNvbHZlRGF0YSA/PyBERUZBVUxUX1JFU09MVkVfREFUQV9PUFRJT047XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnkoe1xuICAgICAgICBkaXJlY3Rvcnk6IHBhdGguam9pbihiYXNlZGlyLCAnZXZlbnRzJyksXG4gICAgICAgIHNjaGVtYTogRXZlbnRTY2hlbWEsXG4gICAgICAgIC8vIE5vIGZpbGVQcmVmaXggLSBzZWFyY2ggYWxsIGV2ZW50c1xuICAgICAgICBmaWx0ZXI6IChldmVudCkgPT4gZXZlbnQuY29ycmVsYXRpb25JZCA9PT0gY29ycmVsYXRpb25JZCxcbiAgICAgICAgLy8gRXZlbnRzIGluIGNocm9ub2xvZ2ljYWwgb3JkZXIgKG9sZGVzdCBmaXJzdCkgYnkgZGVmYXVsdCxcbiAgICAgICAgLy8gZGlmZmVyZW50IGZyb20gdGhlIGRlZmF1bHQgZm9yIG90aGVyIGxpc3QgY2FsbHMuXG4gICAgICAgIHNvcnRPcmRlcjogcGFyYW1zLnBhZ2luYXRpb24/LnNvcnRPcmRlciA/PyAnYXNjJyxcbiAgICAgICAgbGltaXQ6IHBhcmFtcy5wYWdpbmF0aW9uPy5saW1pdCxcbiAgICAgICAgY3Vyc29yOiBwYXJhbXMucGFnaW5hdGlvbj8uY3Vyc29yLFxuICAgICAgICBnZXRDcmVhdGVkQXQ6IGdldE9iamVjdENyZWF0ZWRBdCgnZXZudCcpLFxuICAgICAgICBnZXRJZDogKGV2ZW50KSA9PiBldmVudC5ldmVudElkLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIElmIHJlc29sdmVEYXRhIGlzIFwibm9uZVwiLCByZW1vdmUgZXZlbnREYXRhIGZyb20gZXZlbnRzXG4gICAgICBpZiAocmVzb2x2ZURhdGEgPT09ICdub25lJykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLnJlc3VsdCxcbiAgICAgICAgICBkYXRhOiByZXN1bHQuZGF0YS5tYXAoKGV2ZW50KSA9PlxuICAgICAgICAgICAgc3RyaXBFdmVudERhdGFSZWZzKGV2ZW50LCByZXNvbHZlRGF0YSlcbiAgICAgICAgICApLFxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0sXG4gIH07XG59XG4iXX0=
|