@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.
Files changed (63) hide show
  1. package/dist/config.d.ts +6 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +1 -1
  4. package/dist/fs.d.ts +64 -5
  5. package/dist/fs.d.ts.map +1 -1
  6. package/dist/fs.js +148 -19
  7. package/dist/index.d.ts +3 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +26 -9
  10. package/dist/init.js +1 -1
  11. package/dist/instrumentObject.d.ts.map +1 -1
  12. package/dist/instrumentObject.js +69 -3
  13. package/dist/queue.d.ts +2 -2
  14. package/dist/queue.d.ts.map +1 -1
  15. package/dist/queue.js +64 -12
  16. package/dist/storage/events-storage.d.ts.map +1 -1
  17. package/dist/storage/events-storage.js +1393 -601
  18. package/dist/storage/filters.js +1 -1
  19. package/dist/storage/helpers.d.ts +13 -0
  20. package/dist/storage/helpers.d.ts.map +1 -1
  21. package/dist/storage/helpers.js +23 -1
  22. package/dist/storage/hooks-storage.d.ts.map +1 -1
  23. package/dist/storage/hooks-storage.js +11 -4
  24. package/dist/storage/index.d.ts +10 -1
  25. package/dist/storage/index.d.ts.map +1 -1
  26. package/dist/storage/index.js +9 -11
  27. package/dist/storage/legacy.d.ts.map +1 -1
  28. package/dist/storage/legacy.js +10 -5
  29. package/dist/storage/runs-storage.d.ts +29 -3
  30. package/dist/storage/runs-storage.d.ts.map +1 -1
  31. package/dist/storage/runs-storage.js +76 -4
  32. package/dist/storage/steps-storage.d.ts.map +1 -1
  33. package/dist/storage/steps-storage.js +5 -2
  34. package/dist/storage.js +1 -1
  35. package/dist/streamer.d.ts.map +1 -1
  36. package/dist/streamer.js +45 -32
  37. package/dist/telemetry.d.ts +8 -0
  38. package/dist/telemetry.d.ts.map +1 -1
  39. package/dist/telemetry.js +5 -1
  40. package/dist/test-helpers.d.ts +1 -0
  41. package/dist/test-helpers.d.ts.map +1 -1
  42. package/dist/test-helpers.js +1 -1
  43. package/dist/util.js +1 -1
  44. package/package.json +6 -6
  45. package/dist/config.js.map +0 -1
  46. package/dist/fs.js.map +0 -1
  47. package/dist/index.js.map +0 -1
  48. package/dist/init.js.map +0 -1
  49. package/dist/instrumentObject.js.map +0 -1
  50. package/dist/queue.js.map +0 -1
  51. package/dist/storage/events-storage.js.map +0 -1
  52. package/dist/storage/filters.js.map +0 -1
  53. package/dist/storage/helpers.js.map +0 -1
  54. package/dist/storage/hooks-storage.js.map +0 -1
  55. package/dist/storage/index.js.map +0 -1
  56. package/dist/storage/legacy.js.map +0 -1
  57. package/dist/storage/runs-storage.js.map +0 -1
  58. package/dist/storage/steps-storage.js.map +0 -1
  59. package/dist/storage.js.map +0 -1
  60. package/dist/streamer.js.map +0 -1
  61. package/dist/telemetry.js.map +0 -1
  62. package/dist/test-helpers.js.map +0 -1
  63. 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
- const eventId = `evnt_${monotonicUlid()}`;
35
- const now = new Date();
36
- // For run_created events, use client-provided runId or generate one server-side
37
- let effectiveRunId;
38
- if (data.eventType === 'run_created' && (!runId || runId === '')) {
39
- effectiveRunId = `wrun_${monotonicUlid()}`;
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
- else if (!runId) {
42
- throw new Error('runId is required for non-run_created events');
318
+ if ('correlationId' in data && typeof data.correlationId === 'string') {
319
+ assertSafeEntityId('correlationId', data.correlationId);
43
320
  }
44
- else {
45
- effectiveRunId = runId;
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
- // Validate client-provided runId timestamp is within acceptable threshold
48
- if (data.eventType === 'run_created' && runId && runId !== '') {
49
- const validationError = validateUlidTimestamp(effectiveRunId, 'wrun_');
50
- if (validationError) {
51
- throw new WorkflowWorldError(validationError);
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
- // specVersion is always sent by the runtime, but we provide a fallback for safety
55
- const effectiveSpecVersion = data.specVersion ?? SPEC_VERSION_CURRENT;
56
- // Helper to check if run is in terminal state
57
- const isRunTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
58
- // Helper to check if step is in terminal state
59
- const isStepTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
60
- // Get current run state for validation (if not creating a new run)
61
- // Skip run validation for step_completed and step_retrying - they only operate
62
- // on running steps, and running steps are always allowed to modify regardless
63
- // of run state. This optimization saves filesystem reads per step event.
64
- let currentRun = null;
65
- const skipRunValidationEvents = ['step_completed', 'step_retrying'];
66
- if (data.eventType !== 'run_created' &&
67
- !skipRunValidationEvents.includes(data.eventType)) {
68
- currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
69
- // Resilient start: run_started on non-existent run with eventData
70
- // creates the run first, so the queue can bootstrap a run that
71
- // failed to create during start().
72
- if (data.eventType === 'run_started' &&
73
- !currentRun &&
74
- 'eventData' in data &&
75
- data.eventData) {
76
- const runInputData = data.eventData;
77
- if (runInputData.deploymentId &&
78
- runInputData.workflowName &&
79
- runInputData.input !== undefined) {
80
- // Atomically try to create the run entity. writeExclusive
81
- // uses O_CREAT|O_EXCL so only the first writer wins,
82
- // preventing a TOCTOU race where a concurrent run_created
83
- // from start() could overwrite a run that was already
84
- // transitioned to 'running'.
85
- const createdRun = {
86
- runId: effectiveRunId,
87
- deploymentId: runInputData.deploymentId,
88
- status: 'pending',
89
- workflowName: runInputData.workflowName,
90
- specVersion: effectiveSpecVersion,
91
- executionContext: runInputData.executionContext,
92
- input: runInputData.input,
93
- output: undefined,
94
- error: undefined,
95
- startedAt: undefined,
96
- completedAt: undefined,
97
- createdAt: now,
98
- updatedAt: now,
99
- };
100
- const runPath = taggedPath(basedir, 'runs', effectiveRunId, tag);
101
- const created = await writeExclusive(runPath, JSON.stringify(createdRun, jsonReplacer));
102
- if (created) {
103
- // We created the run — also write the run_created event.
104
- const runCreatedEventId = `evnt_${monotonicUlid()}`;
105
- const runCreatedEvent = {
106
- eventType: 'run_created',
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
- eventId: runCreatedEventId,
109
- createdAt: now,
414
+ deploymentId: runInputData.deploymentId,
415
+ status: 'pending',
416
+ workflowName: runInputData.workflowName,
110
417
  specVersion: effectiveSpecVersion,
111
- eventData: {
112
- deploymentId: runInputData.deploymentId,
113
- workflowName: runInputData.workflowName,
114
- input: runInputData.input,
115
- executionContext: runInputData.executionContext,
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 createdCompositeKey = `${effectiveRunId}-${runCreatedEventId}`;
119
- await writeJSON(taggedPath(basedir, 'events', createdCompositeKey, tag), runCreatedEvent);
120
- currentRun = createdRun;
121
- }
122
- else {
123
- // Run already exists (concurrent run_created won the
124
- // race). Re-read it so downstream logic sees the real state.
125
- currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
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
- // VERSION COMPATIBILITY: Check run spec version
132
- // ============================================================
133
- // For events that have fetched the run, check version compatibility.
134
- // Skip for run_created (no existing run) and runtime events (step_completed, step_retrying).
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
- // Other run state transitions are not allowed on terminal runs
180
- if (runTerminalEvents.includes(data.eventType) ||
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
- // Creating new entities on terminal runs is not allowed
185
- if (data.eventType === 'step_created' ||
186
- data.eventType === 'hook_created' ||
187
- data.eventType === 'wait_created') {
188
- throw new EntityConflictError(`Cannot create new entities on run in terminal state "${currentRun.status}"`);
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
- // Step-related event validation (ordering and terminal state)
192
- // Store existingStep so we can reuse it later (avoid double read)
193
- let validatedStep = null;
194
- const stepEvents = [
195
- 'step_started',
196
- 'step_completed',
197
- 'step_failed',
198
- 'step_retrying',
199
- ];
200
- if (stepEvents.includes(data.eventType) && data.correlationId) {
201
- const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
202
- validatedStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
203
- // Event ordering: step must exist before these events
204
- if (!validatedStep) {
205
- throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
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
- if (validatedStep.status !== 'running') {
214
- throw new RunExpiredError(`Cannot modify non-running step on run in terminal state "${currentRun.status}"`);
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
- // Hook-related event validation (ordering)
219
- const hookEventsRequiringExistence = ['hook_disposed', 'hook_received'];
220
- if (hookEventsRequiringExistence.includes(data.eventType) &&
221
- data.correlationId) {
222
- const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
223
- if (!existingHook) {
224
- throw new HookNotFoundError(data.correlationId);
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
- const event = {
228
- ...data,
229
- runId: effectiveRunId,
230
- eventId,
231
- createdAt: now,
232
- specVersion: effectiveSpecVersion,
233
- };
234
- // Strip eventData from run_started — it belongs on run_created only.
235
- if (data.eventType === 'run_started' && 'eventData' in event) {
236
- delete event.eventData;
237
- }
238
- // Track entity created/updated for EventResult
239
- let run;
240
- let step;
241
- let hook;
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
- deploymentId: runData.deploymentId,
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
- updatedAt: now,
613
+ specVersion: effectiveSpecVersion,
262
614
  };
263
- // Use writeExclusive (O_CREAT|O_EXCL) to atomically create the
264
- // run entity file. This prevents a TOCTOU race with the resilient
265
- // start path (run_started on non-existent run) that could result
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
- else if (data.eventType === 'run_started') {
274
- // Reuse currentRun from validation (already read above)
275
- if (currentRun) {
276
- // If already running, return the run without inserting a
277
- // duplicate event. This makes run_started idempotent for
278
- // concurrent invocations. We omit preloaded events here
279
- // because this is a rare race-condition path — the runtime
280
- // falls back to getAllWorkflowRunEvents().
281
- if (currentRun.status === 'running') {
282
- return { run: currentRun };
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: currentRun.runId,
286
- deploymentId: currentRun.deploymentId,
287
- workflowName: currentRun.workflowName,
288
- specVersion: currentRun.specVersion,
289
- executionContext: currentRun.executionContext,
290
- input: currentRun.input,
291
- createdAt: currentRun.createdAt,
292
- expiredAt: currentRun.expiredAt,
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
- startedAt: currentRun.startedAt ?? now,
670
+ attributes: runData.attributes ?? {},
671
+ createdAt: now,
298
672
  updatedAt: now,
299
673
  };
300
- await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
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
- else if (data.eventType === 'run_completed' && 'eventData' in data) {
304
- const completedData = data.eventData;
305
- // Reuse currentRun from validation (already read above)
306
- if (currentRun) {
307
- run = {
308
- runId: currentRun.runId,
309
- deploymentId: currentRun.deploymentId,
310
- workflowName: currentRun.workflowName,
311
- specVersion: currentRun.specVersion,
312
- executionContext: currentRun.executionContext,
313
- input: currentRun.input,
314
- createdAt: currentRun.createdAt,
315
- expiredAt: currentRun.expiredAt,
316
- startedAt: currentRun.startedAt,
317
- status: 'completed',
318
- output: completedData.output,
319
- error: undefined,
320
- completedAt: now,
321
- updatedAt: now,
322
- };
323
- await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
324
- await Promise.all([
325
- deleteAllHooksForRun(basedir, effectiveRunId),
326
- deleteAllWaitsForRun(basedir, effectiveRunId),
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
- else if (data.eventType === 'run_failed' && 'eventData' in data) {
331
- const failedData = data.eventData;
332
- // Reuse currentRun from validation (already read above)
333
- if (currentRun) {
334
- run = {
335
- runId: currentRun.runId,
336
- deploymentId: currentRun.deploymentId,
337
- workflowName: currentRun.workflowName,
338
- specVersion: currentRun.specVersion,
339
- executionContext: currentRun.executionContext,
340
- input: currentRun.input,
341
- createdAt: currentRun.createdAt,
342
- expiredAt: currentRun.expiredAt,
343
- startedAt: currentRun.startedAt,
344
- status: 'failed',
345
- output: undefined,
346
- error: {
347
- message: typeof failedData.error === 'string'
348
- ? failedData.error
349
- : (failedData.error?.message ?? 'Unknown error'),
350
- stack: failedData.error?.stack,
351
- code: failedData.errorCode,
352
- },
353
- completedAt: now,
354
- updatedAt: now,
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
- else if (data.eventType === 'run_cancelled') {
364
- // Reuse currentRun from validation (already read above)
365
- if (currentRun) {
366
- run = {
367
- runId: currentRun.runId,
368
- deploymentId: currentRun.deploymentId,
369
- workflowName: currentRun.workflowName,
370
- specVersion: currentRun.specVersion,
371
- executionContext: currentRun.executionContext,
372
- input: currentRun.input,
373
- createdAt: currentRun.createdAt,
374
- expiredAt: currentRun.expiredAt,
375
- startedAt: currentRun.startedAt,
376
- status: 'cancelled',
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
- completedAt: now,
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
- await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
383
- await Promise.all([
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
- else if (
390
- // Step lifecycle events
391
- data.eventType === 'step_created' &&
392
- 'eventData' in data) {
393
- // step_created: Creates step entity with status 'pending', attempt=0, createdAt set
394
- const stepData = data.eventData;
395
- step = {
396
- runId: effectiveRunId,
397
- stepId: data.correlationId,
398
- stepName: stepData.stepName,
399
- status: 'pending',
400
- input: stepData.input,
401
- output: undefined,
402
- error: undefined,
403
- attempt: 0,
404
- startedAt: undefined,
405
- completedAt: undefined,
406
- createdAt: now,
407
- updatedAt: now,
408
- // Propagate specVersion from the event to the step entity
409
- specVersion: effectiveSpecVersion,
410
- };
411
- const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
412
- await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step);
413
- }
414
- else if (data.eventType === 'step_started') {
415
- // step_started: Increments attempt, sets status to 'running'
416
- // Sets startedAt only on the first start (not updated on retries)
417
- // Reuse validatedStep from validation (already read above)
418
- if (validatedStep) {
419
- // Check if retryAfter timestamp hasn't been reached yet
420
- if (validatedStep.retryAfter &&
421
- validatedStep.retryAfter.getTime() > Date.now()) {
422
- throw new TooEarlyError(`Cannot start step "${data.correlationId}": retryAfter timestamp has not been reached yet`, {
423
- retryAfter: Math.ceil((validatedStep.retryAfter.getTime() - Date.now()) / 1000),
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
- // Best-effort guard: re-read the step entity to check if it
427
- // reached terminal state between the validation read and now.
428
- // This narrows the TOCTOU window but does not fully eliminate it
429
- // (the local world is single-process / dev-only; the postgres
430
- // world uses SQL-level atomic guards for production).
431
- const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
432
- const freshStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
433
- if (freshStep && isStepTerminal(freshStep.status)) {
434
- throw new EntityConflictError(`Cannot modify step in terminal state "${freshStep.status}"`);
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
- step = {
437
- ...validatedStep,
438
- status: 'running',
439
- // Only set startedAt on the first start
440
- startedAt: validatedStep.startedAt ?? now,
441
- // Increment attempt counter on every start
442
- attempt: validatedStep.attempt + 1,
443
- // Clear retryAfter now that the step has started
444
- retryAfter: undefined,
445
- updatedAt: now,
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
- await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
1252
+ hookEntityWriteOptions = writeHookEntityWithOverwrite
1253
+ ? { overwrite: true }
1254
+ : undefined;
448
1255
  }
449
- }
450
- else if (data.eventType === 'step_completed' && 'eventData' in data) {
451
- // step_completed: Terminal state with output
452
- // Uses writeExclusive on a lock file to atomically prevent concurrent
453
- // invocations from both completing the same step (TOCTOU race).
454
- const completedData = data.eventData;
455
- if (validatedStep) {
456
- const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
457
- const lockName = tag
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('Cannot modify step in terminal state');
1266
+ throw new EntityConflictError(`Hook "${data.correlationId}" already disposed`);
464
1267
  }
465
- step = {
466
- ...validatedStep,
467
- status: 'completed',
468
- output: completedData.result,
469
- completedAt: now,
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, 'steps', stepCompositeKey, tag), step, { overwrite: true });
1310
+ await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait);
473
1311
  }
474
- }
475
- else if (data.eventType === 'step_failed' && 'eventData' in data) {
476
- // step_failed: Terminal state with error
477
- // Uses writeExclusive on a lock file to atomically prevent concurrent
478
- // invocations from both failing the same step (TOCTOU race).
479
- const failedData = data.eventData;
480
- if (validatedStep) {
481
- const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
482
- const lockName = tag
483
- ? `${stepCompositeKey}.terminal.${tag}`
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('Cannot modify step in terminal state');
1323
+ throw new EntityConflictError(`Wait "${data.correlationId}" already completed`);
489
1324
  }
490
- const error = {
491
- message: typeof failedData.error === 'string'
492
- ? failedData.error
493
- : (failedData.error?.message ?? 'Unknown error'),
494
- stack: failedData.stack,
495
- };
496
- step = {
497
- ...validatedStep,
498
- status: 'failed',
499
- error,
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, 'steps', stepCompositeKey, tag), step, { overwrite: true });
1339
+ await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait, { overwrite: true });
504
1340
  }
505
- }
506
- else if (data.eventType === 'step_retrying' && 'eventData' in data) {
507
- // step_retrying: Sets status back to 'pending', records error
508
- // Reuse validatedStep from validation (already read above)
509
- const retryData = data.eventData;
510
- if (validatedStep) {
511
- const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
512
- step = {
513
- ...validatedStep,
514
- status: 'pending',
515
- error: {
516
- message: typeof retryData.error === 'string'
517
- ? retryData.error
518
- : (retryData.error?.message ?? 'Unknown error'),
519
- stack: retryData.stack,
520
- },
521
- retryAfter: retryData.retryAfter,
522
- updatedAt: now,
523
- };
524
- await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
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
- else if (
528
- // Hook lifecycle events
529
- data.eventType === 'hook_created' &&
530
- 'eventData' in data) {
531
- const hookData = data.eventData;
532
- // Atomically claim the token using an exclusive-create constraint file.
533
- // This avoids the TOCTOU race of the previous read-all-then-check approach.
534
- const constraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(hookData.token)}.json`);
535
- const tokenClaimed = await writeExclusive(constraintPath, JSON.stringify({
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
- hook = {
568
- runId: effectiveRunId,
569
- hookId: data.correlationId,
570
- token: hookData.token,
571
- metadata: hookData.metadata,
572
- ownerId: 'local-owner',
573
- projectId: 'local-project',
574
- environment: 'local',
575
- createdAt: now,
576
- // Propagate specVersion from the event to the hook entity
577
- specVersion: effectiveSpecVersion,
578
- isWebhook: hookData.isWebhook ?? false,
579
- };
580
- await writeJSON(taggedPath(basedir, 'hooks', data.correlationId, tag), hook);
581
- }
582
- else if (data.eventType === 'hook_disposed') {
583
- // hook_disposed: Deletes hook entity, rejects duplicates.
584
- // Uses writeExclusive on a lock file to atomically prevent concurrent
585
- // invocations from both disposing the same hook (TOCTOU race).
586
- const hookLockName = tag
587
- ? `${data.correlationId}.disposed.${tag}`
588
- : `${data.correlationId}.disposed`;
589
- const lockPath = path.join(basedir, '.locks', 'hooks', hookLockName);
590
- const claimed = await writeExclusive(lockPath, '');
591
- if (!claimed) {
592
- throw new EntityConflictError(`Hook "${data.correlationId}" already disposed`);
593
- }
594
- // Read the hook to get its token before deleting
595
- const hookPath = taggedPath(basedir, 'hooks', data.correlationId, tag);
596
- const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
597
- if (existingHook) {
598
- // Delete the token constraint file to free up the token for reuse
599
- const disposedConstraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(existingHook.token)}.json`);
600
- await deleteJSON(disposedConstraintPath);
601
- }
602
- await deleteJSON(hookPath);
603
- }
604
- else if (data.eventType === 'wait_created' && 'eventData' in data) {
605
- // wait_created: Creates wait entity with status 'waiting'
606
- const waitData = data.eventData;
607
- const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
608
- const existingWait = await readJSONWithFallback(basedir, 'waits', waitCompositeKey, WaitSchema, tag);
609
- if (existingWait) {
610
- throw new EntityConflictError(`Wait "${data.correlationId}" already exists`);
611
- }
612
- wait = {
613
- waitId: waitCompositeKey,
614
- runId: effectiveRunId,
615
- status: 'waiting',
616
- resumeAt: waitData.resumeAt,
617
- completedAt: undefined,
618
- createdAt: now,
619
- updatedAt: now,
620
- specVersion: effectiveSpecVersion,
621
- };
622
- await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait);
623
- }
624
- else if (data.eventType === 'wait_completed') {
625
- // wait_completed: Transitions wait to 'completed', rejects duplicates.
626
- // Uses writeExclusive on a lock file to atomically prevent concurrent
627
- // invocations from both completing the same wait (TOCTOU race).
628
- const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
629
- const waitLockName = tag
630
- ? `${waitCompositeKey}.completed.${tag}`
631
- : `${waitCompositeKey}.completed`;
632
- const lockPath = path.join(basedir, '.locks', 'waits', waitLockName);
633
- const claimed = await writeExclusive(lockPath, '');
634
- if (!claimed) {
635
- throw new EntityConflictError(`Wait "${data.correlationId}" already completed`);
636
- }
637
- const existingWait = await readJSONWithFallback(basedir, 'waits', waitCompositeKey, WaitSchema, tag);
638
- if (!existingWait) {
639
- // Clean up the lock file we just claimed — the wait doesn't exist
640
- await fs.unlink(lockPath).catch(() => { });
641
- throw new WorkflowWorldError(`Wait "${data.correlationId}" not found`);
642
- }
643
- // The lock file (writeExclusive above) already prevents concurrent
644
- // completions — no additional status check needed.
645
- wait = {
646
- ...existingWait,
647
- status: 'completed',
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
- await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait, { overwrite: true });
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=