@workflow/world-local 5.0.0-beta.1 → 5.0.0-beta.11

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 (55) 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 +57 -2
  5. package/dist/fs.d.ts.map +1 -1
  6. package/dist/fs.js +124 -10
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +18 -4
  10. package/dist/init.js +1 -1
  11. package/dist/instrumentObject.js +1 -1
  12. package/dist/queue.d.ts.map +1 -1
  13. package/dist/queue.js +32 -2
  14. package/dist/storage/events-storage.d.ts.map +1 -1
  15. package/dist/storage/events-storage.js +754 -600
  16. package/dist/storage/filters.js +1 -1
  17. package/dist/storage/helpers.js +1 -1
  18. package/dist/storage/hooks-storage.d.ts.map +1 -1
  19. package/dist/storage/hooks-storage.js +3 -2
  20. package/dist/storage/index.d.ts +10 -1
  21. package/dist/storage/index.d.ts.map +1 -1
  22. package/dist/storage/index.js +9 -11
  23. package/dist/storage/legacy.d.ts.map +1 -1
  24. package/dist/storage/legacy.js +10 -5
  25. package/dist/storage/runs-storage.d.ts +29 -3
  26. package/dist/storage/runs-storage.d.ts.map +1 -1
  27. package/dist/storage/runs-storage.js +76 -4
  28. package/dist/storage/steps-storage.d.ts.map +1 -1
  29. package/dist/storage/steps-storage.js +5 -2
  30. package/dist/storage.js +1 -1
  31. package/dist/streamer.d.ts.map +1 -1
  32. package/dist/streamer.js +133 -34
  33. package/dist/telemetry.js +1 -1
  34. package/dist/test-helpers.js +1 -1
  35. package/dist/util.js +1 -1
  36. package/package.json +5 -5
  37. package/dist/config.js.map +0 -1
  38. package/dist/fs.js.map +0 -1
  39. package/dist/index.js.map +0 -1
  40. package/dist/init.js.map +0 -1
  41. package/dist/instrumentObject.js.map +0 -1
  42. package/dist/queue.js.map +0 -1
  43. package/dist/storage/events-storage.js.map +0 -1
  44. package/dist/storage/filters.js.map +0 -1
  45. package/dist/storage/helpers.js.map +0 -1
  46. package/dist/storage/hooks-storage.js.map +0 -1
  47. package/dist/storage/index.js.map +0 -1
  48. package/dist/storage/legacy.js.map +0 -1
  49. package/dist/storage/runs-storage.js.map +0 -1
  50. package/dist/storage/steps-storage.js.map +0 -1
  51. package/dist/storage.js.map +0 -1
  52. package/dist/streamer.js.map +0 -1
  53. package/dist/telemetry.js.map +0 -1
  54. package/dist/test-helpers.js.map +0 -1
  55. package/dist/util.js.map +0 -1
@@ -1,13 +1,63 @@
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';
3
+ import { EntityConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, TooEarlyError, WorkflowRunNotFoundError, WorkflowWorldError, } from '@workflow/errors';
4
4
  import { EventSchema, HookSchema, isLegacySpecVersion, requiresNewerWorld, SPEC_VERSION_CURRENT, StepSchema, 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
9
  import { getObjectCreatedAt, hashToken, 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
+ const stepLocks = new Map();
26
+ const HookTokenClaimSchema = z.object({
27
+ runId: z.string(),
28
+ });
29
+ async function readHookTokenClaim(constraintPath) {
30
+ try {
31
+ return await readJSON(constraintPath, HookTokenClaimSchema);
32
+ }
33
+ catch (error) {
34
+ if (error instanceof SyntaxError || error instanceof z.ZodError) {
35
+ return null;
36
+ }
37
+ throw error;
38
+ }
39
+ }
40
+ function withStepLock(key, fn) {
41
+ const prev = stepLocks.get(key);
42
+ const taskBox = {};
43
+ const task = (async () => {
44
+ if (prev) {
45
+ // Wait for the previous task to settle; don't inherit its errors.
46
+ await prev.catch(() => undefined);
47
+ }
48
+ try {
49
+ return await fn();
50
+ }
51
+ finally {
52
+ if (stepLocks.get(key) === taskBox.task) {
53
+ stepLocks.delete(key);
54
+ }
55
+ }
56
+ })();
57
+ taskBox.task = task;
58
+ stepLocks.set(key, task);
59
+ return task;
60
+ }
11
61
  /**
12
62
  * Helper function to delete all waits associated with a workflow run.
13
63
  * Called when a run reaches a terminal state.
@@ -24,6 +74,33 @@ async function deleteAllWaitsForRun(basedir, runId) {
24
74
  }
25
75
  }
26
76
  }
77
+ /**
78
+ * Persist a lifecycle-driven run update (run_started / run_completed /
79
+ * run_failed / run_cancelled) under the shared per-run file lock,
80
+ * re-reading the on-disk run inside the lock so any attribute writes
81
+ * that landed between the pre-validation `currentRun` read and this
82
+ * write are preserved. Without the re-read, an `experimentalSetAttributes`
83
+ * call sandwiched between the lifecycle read and write would be
84
+ * silently overwritten by the lifecycle write's stale attribute snapshot.
85
+ *
86
+ * `proposed` is the fully-constructed run row the caller wants to
87
+ * write (with the correct discriminated-union status branch). Only the
88
+ * `attributes` field is replaced with the freshest version inside the
89
+ * lock.
90
+ */
91
+ async function writeRunUnderLifecycleLock(basedir, runId, tag, proposed) {
92
+ return withRunFileLock(runId, async () => {
93
+ const fresh = await readJSON(taggedPath(basedir, 'runs', runId, tag), WorkflowRunSchema);
94
+ const next = {
95
+ ...proposed,
96
+ attributes: fresh?.attributes ?? proposed.attributes,
97
+ };
98
+ await writeJSON(taggedPath(basedir, 'runs', runId, tag), next, {
99
+ overwrite: true,
100
+ });
101
+ return next;
102
+ });
103
+ }
27
104
  /**
28
105
  * Creates the events storage implementation using the filesystem.
29
106
  * Implements the Storage['events'] interface with create, list, and listByCorrelationId operations.
@@ -31,657 +108,732 @@ async function deleteAllWaitsForRun(basedir, runId) {
31
108
  export function createEventsStorage(basedir, tag) {
32
109
  return {
33
110
  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()}`;
111
+ // Validate request-supplied IDs before they're concatenated into
112
+ // filesystem paths. This is the primary defense against path traversal
113
+ // attacks where a client supplies runId / correlationId values like
114
+ // "../../../package" to read or write files outside the storage root.
115
+ // Run before taking the per-step mutex so malformed inputs fail fast.
116
+ //
117
+ // Empty `correlationId` values are also rejected here: the event
118
+ // schemas only require `z.string()`, so without this check a
119
+ // step_created / hook_created / wait_created request with
120
+ // `correlationId: ''` would silently be written under a malformed
121
+ // composite key like `${runId}-`.
122
+ if (runId != null && runId !== '') {
123
+ assertSafeEntityId('runId', runId);
40
124
  }
41
- else if (!runId) {
42
- throw new Error('runId is required for non-run_created events');
125
+ if ('correlationId' in data && typeof data.correlationId === 'string') {
126
+ assertSafeEntityId('correlationId', data.correlationId);
43
127
  }
44
- else {
45
- effectiveRunId = runId;
128
+ // Step lifecycle events are serialized per-step via an in-process mutex
129
+ // so that the "check state, then write" sequence in step_started /
130
+ // step_completed / step_failed / step_retrying is atomic. step_created
131
+ // is also serialized so duplicate-create races don't leave extra
132
+ // step_created events in the log.
133
+ const isStepEvent = data.eventType === 'step_created' ||
134
+ data.eventType === 'step_started' ||
135
+ data.eventType === 'step_completed' ||
136
+ data.eventType === 'step_failed' ||
137
+ data.eventType === 'step_retrying';
138
+ if (isStepEvent && runId && data.correlationId) {
139
+ const lockKey = tag
140
+ ? `${runId}-${data.correlationId}.${tag}`
141
+ : `${runId}-${data.correlationId}`;
142
+ return withStepLock(lockKey, () => createImpl());
46
143
  }
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);
144
+ return createImpl();
145
+ async function createImpl() {
146
+ const eventId = `evnt_${monotonicUlid()}`;
147
+ const now = new Date();
148
+ // For run_created events, use client-provided runId or generate one server-side
149
+ let effectiveRunId;
150
+ if (data.eventType === 'run_created' && (!runId || runId === '')) {
151
+ effectiveRunId = `wrun_${monotonicUlid()}`;
52
152
  }
53
- }
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',
153
+ else if (!runId) {
154
+ throw new Error('runId is required for non-run_created events');
155
+ }
156
+ else {
157
+ effectiveRunId = runId;
158
+ }
159
+ // Validate client-provided runId timestamp is within acceptable threshold
160
+ if (data.eventType === 'run_created' && runId && runId !== '') {
161
+ const validationError = validateUlidTimestamp(effectiveRunId, 'wrun_');
162
+ if (validationError) {
163
+ throw new WorkflowWorldError(validationError);
164
+ }
165
+ }
166
+ // specVersion is always sent by the runtime, but we provide a fallback for safety
167
+ const effectiveSpecVersion = data.specVersion ?? SPEC_VERSION_CURRENT;
168
+ // Helper to check if run is in terminal state
169
+ const isRunTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
170
+ // Helper to check if step is in terminal state
171
+ const isStepTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
172
+ // Get current run state for validation (if not creating a new run)
173
+ // Skip run validation for step_completed and step_retrying - they only operate
174
+ // on running steps, and running steps are always allowed to modify regardless
175
+ // of run state. This optimization saves filesystem reads per step event.
176
+ let currentRun = null;
177
+ const skipRunValidationEvents = ['step_completed', 'step_retrying'];
178
+ if (data.eventType !== 'run_created' &&
179
+ !skipRunValidationEvents.includes(data.eventType)) {
180
+ currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
181
+ // Resilient start: run_started on non-existent run with eventData
182
+ // creates the run first, so the queue can bootstrap a run that
183
+ // failed to create during start().
184
+ if (data.eventType === 'run_started' &&
185
+ !currentRun &&
186
+ 'eventData' in data &&
187
+ data.eventData) {
188
+ const runInputData = data.eventData;
189
+ if (runInputData.deploymentId &&
190
+ runInputData.workflowName &&
191
+ runInputData.input !== undefined) {
192
+ // Atomically try to create the run entity. writeExclusive
193
+ // uses O_CREAT|O_EXCL so only the first writer wins,
194
+ // preventing a TOCTOU race where a concurrent run_created
195
+ // from start() could overwrite a run that was already
196
+ // transitioned to 'running'.
197
+ const createdRun = {
107
198
  runId: effectiveRunId,
108
- eventId: runCreatedEventId,
109
- createdAt: now,
199
+ deploymentId: runInputData.deploymentId,
200
+ status: 'pending',
201
+ workflowName: runInputData.workflowName,
110
202
  specVersion: effectiveSpecVersion,
111
- eventData: {
112
- deploymentId: runInputData.deploymentId,
113
- workflowName: runInputData.workflowName,
114
- input: runInputData.input,
115
- executionContext: runInputData.executionContext,
116
- },
203
+ executionContext: runInputData.executionContext,
204
+ input: runInputData.input,
205
+ output: undefined,
206
+ error: undefined,
207
+ startedAt: undefined,
208
+ completedAt: undefined,
209
+ attributes: {},
210
+ createdAt: now,
211
+ updatedAt: now,
117
212
  };
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);
213
+ const runPath = taggedPath(basedir, 'runs', effectiveRunId, tag);
214
+ const created = await writeExclusive(runPath, JSON.stringify(createdRun, jsonReplacer));
215
+ if (created) {
216
+ // We created the run — also write the run_created event.
217
+ const runCreatedEventId = `evnt_${monotonicUlid()}`;
218
+ const runCreatedEvent = {
219
+ eventType: 'run_created',
220
+ runId: effectiveRunId,
221
+ eventId: runCreatedEventId,
222
+ createdAt: now,
223
+ specVersion: effectiveSpecVersion,
224
+ eventData: {
225
+ deploymentId: runInputData.deploymentId,
226
+ workflowName: runInputData.workflowName,
227
+ input: runInputData.input,
228
+ executionContext: runInputData.executionContext,
229
+ },
230
+ };
231
+ const createdCompositeKey = `${effectiveRunId}-${runCreatedEventId}`;
232
+ await writeJSON(taggedPath(basedir, 'events', createdCompositeKey, tag), runCreatedEvent);
233
+ currentRun = createdRun;
234
+ }
235
+ else {
236
+ // Run already exists (concurrent run_created won the
237
+ // race). Re-read it so downstream logic sees the real state.
238
+ currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
239
+ }
126
240
  }
127
241
  }
128
242
  }
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
- };
243
+ // run_failed on a non-existent run is rejected to match the
244
+ // postgres and vercel worlds, which both surface this as a
245
+ // WorkflowRunNotFoundError rather than silently persisting an
246
+ // event for a run that was never created.
247
+ if (data.eventType === 'run_failed' && !currentRun) {
248
+ throw new WorkflowRunNotFoundError(effectiveRunId);
173
249
  }
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}"`);
178
- }
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}"`);
183
- }
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}"`);
189
- }
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}"`);
250
+ // ============================================================
251
+ // VERSION COMPATIBILITY: Check run spec version
252
+ // ============================================================
253
+ // For events that have fetched the run, check version compatibility.
254
+ // Skip for run_created (no existing run) and runtime events (step_completed, step_retrying).
255
+ if (currentRun) {
256
+ // Check if run requires a newer world version
257
+ if (requiresNewerWorld(currentRun.specVersion)) {
258
+ throw new RunNotSupportedError(currentRun.specVersion, SPEC_VERSION_CURRENT);
259
+ }
260
+ // Route to legacy handler for pre-event-sourcing runs
261
+ if (isLegacySpecVersion(currentRun.specVersion)) {
262
+ return handleLegacyEvent(basedir, effectiveRunId, data, currentRun, params);
263
+ }
210
264
  }
211
- // On terminal runs: only allow completing/failing in-progress steps
265
+ // ============================================================
266
+ // VALIDATION: Terminal state and event ordering checks
267
+ // ============================================================
268
+ // Run terminal state validation
212
269
  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}"`);
270
+ const runTerminalEvents = [
271
+ 'run_started',
272
+ 'run_completed',
273
+ 'run_failed',
274
+ ];
275
+ // Idempotent operation: run_cancelled on already cancelled run is allowed
276
+ if (data.eventType === 'run_cancelled' &&
277
+ currentRun.status === 'cancelled') {
278
+ // Return existing state (idempotent)
279
+ const event = {
280
+ ...data,
281
+ runId: effectiveRunId,
282
+ eventId,
283
+ createdAt: now,
284
+ specVersion: effectiveSpecVersion,
285
+ };
286
+ const compositeKey = `${effectiveRunId}-${eventId}`;
287
+ await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
288
+ const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
289
+ return {
290
+ event: stripEventDataRefs(event, resolveData),
291
+ run: currentRun,
292
+ };
293
+ }
294
+ // For run_started on terminal runs, use RunExpiredError so the
295
+ // runtime knows to exit without retrying.
296
+ if (data.eventType === 'run_started') {
297
+ throw new RunExpiredError(`Workflow run "${effectiveRunId}" is already in terminal state "${currentRun.status}"`);
298
+ }
299
+ // Other run state transitions are not allowed on terminal runs
300
+ if (runTerminalEvents.includes(data.eventType) ||
301
+ data.eventType === 'run_cancelled') {
302
+ throw new EntityConflictError(`Cannot transition run from terminal state "${currentRun.status}"`);
303
+ }
304
+ // Creating new entities on terminal runs is not allowed
305
+ if (data.eventType === 'step_created' ||
306
+ data.eventType === 'hook_created' ||
307
+ data.eventType === 'wait_created') {
308
+ throw new EntityConflictError(`Cannot create new entities on run in terminal state "${currentRun.status}"`);
215
309
  }
216
310
  }
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);
311
+ // Step-related event validation (ordering and terminal state)
312
+ // Store existingStep so we can reuse it later (avoid double read)
313
+ let validatedStep = null;
314
+ const stepEvents = [
315
+ 'step_started',
316
+ 'step_completed',
317
+ 'step_failed',
318
+ 'step_retrying',
319
+ ];
320
+ if (stepEvents.includes(data.eventType) && data.correlationId) {
321
+ const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
322
+ validatedStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
323
+ // Event ordering: step must exist before these events
324
+ if (!validatedStep) {
325
+ throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
326
+ }
327
+ // Step terminal state validation
328
+ if (isStepTerminal(validatedStep.status)) {
329
+ throw new EntityConflictError(`Cannot modify step in terminal state "${validatedStep.status}"`);
330
+ }
331
+ // On terminal runs: only allow completing/failing in-progress steps
332
+ if (currentRun && isRunTerminal(currentRun.status)) {
333
+ if (validatedStep.status !== 'running') {
334
+ throw new RunExpiredError(`Cannot modify non-running step on run in terminal state "${currentRun.status}"`);
335
+ }
336
+ }
225
337
  }
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 = {
338
+ // Hook-related event validation (ordering)
339
+ const hookEventsRequiringExistence = ['hook_disposed', 'hook_received'];
340
+ if (hookEventsRequiringExistence.includes(data.eventType) &&
341
+ data.correlationId) {
342
+ const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
343
+ if (!existingHook) {
344
+ throw new HookNotFoundError(data.correlationId);
345
+ }
346
+ }
347
+ const event = {
348
+ ...data,
248
349
  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,
350
+ eventId,
260
351
  createdAt: now,
261
- updatedAt: now,
352
+ specVersion: effectiveSpecVersion,
262
353
  };
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`);
354
+ // Strip eventData from run_started it belongs on run_created only.
355
+ if (data.eventType === 'run_started' && 'eventData' in event) {
356
+ delete event.eventData;
271
357
  }
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
- }
358
+ // Track entity created/updated for EventResult
359
+ let run;
360
+ let step;
361
+ let hook;
362
+ let wait;
363
+ // Create/update entity based on event type (event-sourced architecture)
364
+ // Run lifecycle events
365
+ if (data.eventType === 'run_created' && 'eventData' in data) {
366
+ const runData = data.eventData;
284
367
  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',
368
+ runId: effectiveRunId,
369
+ deploymentId: runData.deploymentId,
370
+ status: 'pending',
371
+ workflowName: runData.workflowName,
372
+ // Propagate specVersion from the event to the run entity
373
+ specVersion: effectiveSpecVersion,
374
+ executionContext: runData.executionContext,
375
+ input: runData.input,
294
376
  output: undefined,
295
377
  error: undefined,
378
+ startedAt: undefined,
296
379
  completedAt: undefined,
297
- startedAt: currentRun.startedAt ?? now,
380
+ attributes: {},
381
+ createdAt: now,
298
382
  updatedAt: now,
299
383
  };
300
- await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
384
+ // Use writeExclusive (O_CREAT|O_EXCL) to atomically create the
385
+ // run entity file. This prevents a TOCTOU race with the resilient
386
+ // start path (run_started on non-existent run) that could result
387
+ // in duplicate run_created events in the event log.
388
+ const runPath = taggedPath(basedir, 'runs', effectiveRunId, tag);
389
+ const created = await writeExclusive(runPath, JSON.stringify(run, jsonReplacer, 2));
390
+ if (!created) {
391
+ throw new EntityConflictError(`Workflow run "${effectiveRunId}" already exists`);
392
+ }
301
393
  }
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
- ]);
394
+ else if (data.eventType === 'run_started') {
395
+ // Reuse currentRun from validation (already read above)
396
+ if (currentRun) {
397
+ // If already running, return the run without inserting a
398
+ // duplicate event. This makes run_started idempotent for
399
+ // concurrent invocations. We omit preloaded events here
400
+ // because this is a rare race-condition path — the runtime
401
+ // falls back to loadWorkflowRunEvents().
402
+ if (currentRun.status === 'running') {
403
+ return { run: currentRun };
404
+ }
405
+ run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
406
+ runId: currentRun.runId,
407
+ deploymentId: currentRun.deploymentId,
408
+ workflowName: currentRun.workflowName,
409
+ specVersion: currentRun.specVersion,
410
+ executionContext: currentRun.executionContext,
411
+ input: currentRun.input,
412
+ createdAt: currentRun.createdAt,
413
+ expiredAt: currentRun.expiredAt,
414
+ status: 'running',
415
+ output: undefined,
416
+ error: undefined,
417
+ completedAt: undefined,
418
+ startedAt: currentRun.startedAt ?? now,
419
+ updatedAt: now,
420
+ attributes: currentRun.attributes,
421
+ });
422
+ }
328
423
  }
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
- ]);
424
+ else if (data.eventType === 'run_completed' && 'eventData' in data) {
425
+ const completedData = data.eventData;
426
+ // Reuse currentRun from validation (already read above)
427
+ if (currentRun) {
428
+ run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
429
+ runId: currentRun.runId,
430
+ deploymentId: currentRun.deploymentId,
431
+ workflowName: currentRun.workflowName,
432
+ specVersion: currentRun.specVersion,
433
+ executionContext: currentRun.executionContext,
434
+ input: currentRun.input,
435
+ createdAt: currentRun.createdAt,
436
+ expiredAt: currentRun.expiredAt,
437
+ startedAt: currentRun.startedAt,
438
+ status: 'completed',
439
+ output: completedData.output,
440
+ error: undefined,
441
+ completedAt: now,
442
+ updatedAt: now,
443
+ attributes: currentRun.attributes,
444
+ });
445
+ await Promise.all([
446
+ deleteAllHooksForRun(basedir, effectiveRunId),
447
+ deleteAllWaitsForRun(basedir, effectiveRunId),
448
+ ]);
449
+ }
361
450
  }
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',
377
- output: undefined,
378
- error: undefined,
379
- completedAt: now,
380
- updatedAt: now,
381
- };
382
- await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
383
- await Promise.all([
384
- deleteAllHooksForRun(basedir, effectiveRunId),
385
- deleteAllWaitsForRun(basedir, effectiveRunId),
386
- ]);
451
+ else if (data.eventType === 'run_failed' && 'eventData' in data) {
452
+ const failedData = data.eventData;
453
+ // Reuse currentRun from validation (already read above)
454
+ if (currentRun) {
455
+ // The error field is SerializedData (Uint8Array) produced by
456
+ // dehydrateRunError. We store it verbatim — consumers hydrate it
457
+ // via hydrateRunError to reconstruct the original thrown value.
458
+ run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
459
+ runId: currentRun.runId,
460
+ deploymentId: currentRun.deploymentId,
461
+ workflowName: currentRun.workflowName,
462
+ specVersion: currentRun.specVersion,
463
+ executionContext: currentRun.executionContext,
464
+ input: currentRun.input,
465
+ createdAt: currentRun.createdAt,
466
+ expiredAt: currentRun.expiredAt,
467
+ startedAt: currentRun.startedAt,
468
+ status: 'failed',
469
+ output: undefined,
470
+ error: failedData.error,
471
+ errorCode: failedData.errorCode,
472
+ completedAt: now,
473
+ updatedAt: now,
474
+ attributes: currentRun.attributes,
475
+ });
476
+ await Promise.all([
477
+ deleteAllHooksForRun(basedir, effectiveRunId),
478
+ deleteAllWaitsForRun(basedir, effectiveRunId),
479
+ ]);
480
+ }
387
481
  }
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),
482
+ else if (data.eventType === 'run_cancelled') {
483
+ // Reuse currentRun from validation (already read above)
484
+ if (currentRun) {
485
+ run = await writeRunUnderLifecycleLock(basedir, effectiveRunId, tag, {
486
+ runId: currentRun.runId,
487
+ deploymentId: currentRun.deploymentId,
488
+ workflowName: currentRun.workflowName,
489
+ specVersion: currentRun.specVersion,
490
+ executionContext: currentRun.executionContext,
491
+ input: currentRun.input,
492
+ createdAt: currentRun.createdAt,
493
+ expiredAt: currentRun.expiredAt,
494
+ startedAt: currentRun.startedAt,
495
+ status: 'cancelled',
496
+ output: undefined,
497
+ error: undefined,
498
+ completedAt: now,
499
+ updatedAt: now,
500
+ attributes: currentRun.attributes,
424
501
  });
502
+ await Promise.all([
503
+ deleteAllHooksForRun(basedir, effectiveRunId),
504
+ deleteAllWaitsForRun(basedir, effectiveRunId),
505
+ ]);
425
506
  }
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}"`);
507
+ }
508
+ else if (
509
+ // Step lifecycle events
510
+ data.eventType === 'step_created' &&
511
+ 'eventData' in data) {
512
+ // step_created: Creates step entity with status 'pending', attempt=0, createdAt set.
513
+ // Two concurrent invocations with identical correlationIds (e.g. the
514
+ // snapshot runtime's deterministic correlationIds across replays)
515
+ // must be deduped otherwise both writes succeed and the event log
516
+ // ends up with duplicate step_created entries. The outer
517
+ // withStepLock mutex serializes within a single process; this
518
+ // O_CREAT|O_EXCL constraint file additionally protects against
519
+ // cross-process races (two pnpm workers, redelivered queue
520
+ // messages, etc.). The loser throws EntityConflictError so the
521
+ // runtime's existing catch path can swallow it and avoid
522
+ // double-queuing the step.
523
+ const stepCreatedLockName = tag
524
+ ? `${effectiveRunId}-${data.correlationId}.created.${tag}`
525
+ : `${effectiveRunId}-${data.correlationId}.created`;
526
+ const stepCreatedLockPath = resolveWithinBase(basedir, '.locks', 'steps', stepCreatedLockName);
527
+ const stepCreatedClaimed = await writeExclusive(stepCreatedLockPath, '');
528
+ if (!stepCreatedClaimed) {
529
+ throw new EntityConflictError(`Step "${data.correlationId}" already created`);
435
530
  }
531
+ const stepData = data.eventData;
436
532
  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,
533
+ runId: effectiveRunId,
534
+ stepId: data.correlationId,
535
+ stepName: stepData.stepName,
536
+ status: 'pending',
537
+ input: stepData.input,
538
+ output: undefined,
539
+ error: undefined,
540
+ attempt: 0,
541
+ startedAt: undefined,
542
+ completedAt: undefined,
543
+ createdAt: now,
445
544
  updatedAt: now,
545
+ // Propagate specVersion from the event to the step entity
546
+ specVersion: effectiveSpecVersion,
446
547
  };
447
- await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
448
- }
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
548
  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, '');
462
- if (!claimed) {
463
- throw new EntityConflictError('Cannot modify step in terminal state');
549
+ await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step);
550
+ }
551
+ else if (data.eventType === 'step_started') {
552
+ // step_started: Increments attempt, sets status to 'running'
553
+ // Sets startedAt only on the first start (not updated on retries)
554
+ // Reuse validatedStep from validation (already read above)
555
+ if (validatedStep) {
556
+ // Check if retryAfter timestamp hasn't been reached yet
557
+ if (validatedStep.retryAfter &&
558
+ validatedStep.retryAfter.getTime() > Date.now()) {
559
+ throw new TooEarlyError(`Cannot start step "${data.correlationId}": retryAfter timestamp has not been reached yet`, {
560
+ retryAfter: Math.ceil((validatedStep.retryAfter.getTime() - Date.now()) / 1000),
561
+ });
562
+ }
563
+ // Best-effort guard: re-read the step entity to check if it
564
+ // reached terminal state between the validation read and now.
565
+ // This narrows the TOCTOU window but does not fully eliminate it
566
+ // (the local world is single-process / dev-only; the postgres
567
+ // world uses SQL-level atomic guards for production).
568
+ const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
569
+ const freshStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
570
+ if (freshStep && isStepTerminal(freshStep.status)) {
571
+ throw new EntityConflictError(`Cannot modify step in terminal state "${freshStep.status}"`);
572
+ }
573
+ step = {
574
+ ...validatedStep,
575
+ status: 'running',
576
+ // Only set startedAt on the first start
577
+ startedAt: validatedStep.startedAt ?? now,
578
+ // Increment attempt counter on every start
579
+ attempt: validatedStep.attempt + 1,
580
+ // Clear retryAfter now that the step has started
581
+ retryAfter: undefined,
582
+ updatedAt: now,
583
+ };
584
+ await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
464
585
  }
465
- step = {
466
- ...validatedStep,
467
- status: 'completed',
468
- output: completedData.result,
469
- completedAt: now,
470
- updatedAt: now,
471
- };
472
- await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
473
586
  }
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, '');
487
- if (!claimed) {
488
- throw new EntityConflictError('Cannot modify step in terminal state');
587
+ else if (data.eventType === 'step_completed' && 'eventData' in data) {
588
+ // step_completed: Terminal state with output
589
+ // Uses writeExclusive on a lock file to atomically prevent concurrent
590
+ // invocations from both completing the same step (TOCTOU race).
591
+ const completedData = data.eventData;
592
+ if (validatedStep) {
593
+ const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
594
+ const lockName = tag
595
+ ? `${stepCompositeKey}.terminal.${tag}`
596
+ : `${stepCompositeKey}.terminal`;
597
+ const terminalLockPath = resolveWithinBase(basedir, '.locks', 'steps', lockName);
598
+ const claimed = await writeExclusive(terminalLockPath, '');
599
+ if (!claimed) {
600
+ throw new EntityConflictError('Cannot modify step in terminal state');
601
+ }
602
+ step = {
603
+ ...validatedStep,
604
+ status: 'completed',
605
+ output: completedData.result,
606
+ completedAt: now,
607
+ updatedAt: now,
608
+ };
609
+ await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
489
610
  }
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,
500
- completedAt: now,
501
- updatedAt: now,
502
- };
503
- await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
504
611
  }
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 });
612
+ else if (data.eventType === 'step_failed' && 'eventData' in data) {
613
+ // step_failed: Terminal state with error
614
+ // Uses writeExclusive on a lock file to atomically prevent concurrent
615
+ // invocations from both failing the same step (TOCTOU race).
616
+ const failedData = data.eventData;
617
+ if (validatedStep) {
618
+ const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
619
+ const lockName = tag
620
+ ? `${stepCompositeKey}.terminal.${tag}`
621
+ : `${stepCompositeKey}.terminal`;
622
+ const terminalLockPath = resolveWithinBase(basedir, '.locks', 'steps', lockName);
623
+ const claimed = await writeExclusive(terminalLockPath, '');
624
+ if (!claimed) {
625
+ throw new EntityConflictError('Cannot modify step in terminal state');
626
+ }
627
+ // The error field is SerializedData (Uint8Array) produced by
628
+ // dehydrateStepError. We store it verbatim — consumers hydrate it
629
+ // via hydrateStepError to reconstruct the original thrown value.
630
+ step = {
631
+ ...validatedStep,
632
+ status: 'failed',
633
+ error: failedData.error,
634
+ completedAt: now,
635
+ updatedAt: now,
636
+ };
637
+ await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
638
+ }
525
639
  }
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
- },
640
+ else if (data.eventType === 'step_retrying' && 'eventData' in data) {
641
+ // step_retrying: Sets status back to 'pending', records error
642
+ // Reuse validatedStep from validation (already read above)
643
+ const retryData = data.eventData;
644
+ if (validatedStep) {
645
+ const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
646
+ step = {
647
+ ...validatedStep,
648
+ status: 'pending',
649
+ error: retryData.error,
650
+ retryAfter: retryData.retryAfter,
651
+ updatedAt: now,
652
+ };
653
+ await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
654
+ }
655
+ }
656
+ else if (
657
+ // Hook lifecycle events
658
+ data.eventType === 'hook_created' &&
659
+ 'eventData' in data) {
660
+ const hookData = data.eventData;
661
+ // Atomically claim the token using an exclusive-create constraint file.
662
+ // This avoids the TOCTOU race of the previous read-all-then-check approach.
663
+ const constraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(hookData.token)}.json`);
664
+ const tokenClaimed = await writeExclusive(constraintPath, JSON.stringify({
665
+ token: hookData.token,
666
+ hookId: data.correlationId,
549
667
  runId: effectiveRunId,
550
- eventId,
668
+ }));
669
+ if (!tokenClaimed) {
670
+ const existingClaim = await readHookTokenClaim(constraintPath);
671
+ // Create hook_conflict event instead of hook_created
672
+ // This allows the workflow to continue and fail gracefully when the hook is awaited
673
+ const conflictEvent = {
674
+ eventType: 'hook_conflict',
675
+ correlationId: data.correlationId,
676
+ eventData: {
677
+ token: hookData.token,
678
+ ...(existingClaim
679
+ ? { conflictingRunId: existingClaim.runId }
680
+ : {}),
681
+ },
682
+ runId: effectiveRunId,
683
+ eventId,
684
+ createdAt: now,
685
+ specVersion: effectiveSpecVersion,
686
+ };
687
+ // Store the conflict event
688
+ const compositeKey = `${effectiveRunId}-${eventId}`;
689
+ await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), conflictEvent);
690
+ const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
691
+ const filteredEvent = stripEventDataRefs(conflictEvent, resolveData);
692
+ // Return EventResult with conflict event (no hook entity created)
693
+ return {
694
+ event: filteredEvent,
695
+ run,
696
+ step,
697
+ hook: undefined,
698
+ };
699
+ }
700
+ hook = {
701
+ runId: effectiveRunId,
702
+ hookId: data.correlationId,
703
+ token: hookData.token,
704
+ metadata: hookData.metadata,
705
+ ownerId: 'local-owner',
706
+ projectId: 'local-project',
707
+ environment: 'local',
551
708
  createdAt: now,
709
+ // Propagate specVersion from the event to the hook entity
552
710
  specVersion: effectiveSpecVersion,
711
+ isWebhook: hookData.isWebhook ?? false,
712
+ isSystem: hookData.isSystem ?? false,
553
713
  };
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
- };
566
- }
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`);
714
+ await writeJSON(taggedPath(basedir, 'hooks', data.correlationId, tag), hook);
593
715
  }
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);
716
+ else if (data.eventType === 'hook_disposed') {
717
+ // hook_disposed: Deletes hook entity, rejects duplicates.
718
+ // Uses writeExclusive on a lock file to atomically prevent concurrent
719
+ // invocations from both disposing the same hook (TOCTOU race).
720
+ const hookLockName = tag
721
+ ? `${data.correlationId}.disposed.${tag}`
722
+ : `${data.correlationId}.disposed`;
723
+ const lockPath = resolveWithinBase(basedir, '.locks', 'hooks', hookLockName);
724
+ const claimed = await writeExclusive(lockPath, '');
725
+ if (!claimed) {
726
+ throw new EntityConflictError(`Hook "${data.correlationId}" already disposed`);
727
+ }
728
+ // Read the hook to get its token before deleting
729
+ const hookPath = taggedPath(basedir, 'hooks', data.correlationId, tag);
730
+ const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
731
+ if (existingHook) {
732
+ // Delete the token constraint file to free up the token for reuse
733
+ const disposedConstraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(existingHook.token)}.json`);
734
+ await deleteJSON(disposedConstraintPath);
735
+ }
736
+ await deleteJSON(hookPath);
601
737
  }
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`);
738
+ else if (data.eventType === 'wait_created' && 'eventData' in data) {
739
+ // wait_created: Creates wait entity with status 'waiting'.
740
+ // Atomic claim on a per-(runId, correlationId) constraint file
741
+ // ensures duplicate wait_created from concurrent invocations
742
+ // surfaces as EntityConflictError (replaces a prior TOCTOU
743
+ // read-then-check that could let both writers through).
744
+ const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
745
+ const waitCreatedLockName = tag
746
+ ? `${waitCompositeKey}.created.${tag}`
747
+ : `${waitCompositeKey}.created`;
748
+ const waitCreatedLockPath = resolveWithinBase(basedir, '.locks', 'waits', waitCreatedLockName);
749
+ const waitCreatedClaimed = await writeExclusive(waitCreatedLockPath, '');
750
+ if (!waitCreatedClaimed) {
751
+ throw new EntityConflictError(`Wait "${data.correlationId}" already exists`);
752
+ }
753
+ const waitData = data.eventData;
754
+ wait = {
755
+ waitId: waitCompositeKey,
756
+ runId: effectiveRunId,
757
+ status: 'waiting',
758
+ resumeAt: waitData.resumeAt,
759
+ completedAt: undefined,
760
+ createdAt: now,
761
+ updatedAt: now,
762
+ specVersion: effectiveSpecVersion,
763
+ };
764
+ await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait);
611
765
  }
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`);
766
+ else if (data.eventType === 'wait_completed') {
767
+ // wait_completed: Transitions wait to 'completed', rejects duplicates.
768
+ // Uses writeExclusive on a lock file to atomically prevent concurrent
769
+ // invocations from both completing the same wait (TOCTOU race).
770
+ const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
771
+ const waitLockName = tag
772
+ ? `${waitCompositeKey}.completed.${tag}`
773
+ : `${waitCompositeKey}.completed`;
774
+ const lockPath = resolveWithinBase(basedir, '.locks', 'waits', waitLockName);
775
+ const claimed = await writeExclusive(lockPath, '');
776
+ if (!claimed) {
777
+ throw new EntityConflictError(`Wait "${data.correlationId}" already completed`);
778
+ }
779
+ const existingWait = await readJSONWithFallback(basedir, 'waits', waitCompositeKey, WaitSchema, tag);
780
+ if (!existingWait) {
781
+ // Clean up the lock file we just claimed — the wait doesn't exist
782
+ await fs.unlink(lockPath).catch(() => { });
783
+ throw new WorkflowWorldError(`Wait "${data.correlationId}" not found`);
784
+ }
785
+ // The lock file (writeExclusive above) already prevents concurrent
786
+ // completions no additional status check needed.
787
+ wait = {
788
+ ...existingWait,
789
+ status: 'completed',
790
+ completedAt: now,
791
+ updatedAt: now,
792
+ };
793
+ await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait, { overwrite: true });
636
794
  }
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`);
795
+ // Note: hook_received events are stored in the event log but don't
796
+ // modify the Hook entity (which doesn't have a payload field)
797
+ // Store event using composite key {runId}-{eventId}
798
+ const compositeKey = `${effectiveRunId}-${eventId}`;
799
+ await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
800
+ const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
801
+ const filteredEvent = stripEventDataRefs(event, resolveData);
802
+ // For run_started: preload one page of events so the runtime can skip
803
+ // the initial events.list call when hasMore is false.
804
+ let events;
805
+ let cursor;
806
+ let hasMore;
807
+ if (data.eventType === 'run_started' && run) {
808
+ const allEvents = await paginatedFileSystemQuery({
809
+ directory: path.join(basedir, 'events'),
810
+ schema: EventSchema,
811
+ filePrefix: `${effectiveRunId}-`,
812
+ sortOrder: 'asc',
813
+ limit: 1000,
814
+ getCreatedAt: getObjectCreatedAt('evnt'),
815
+ getId: (e) => e.eventId,
816
+ });
817
+ events = allEvents.data;
818
+ cursor = allEvents.cursor;
819
+ hasMore = allEvents.hasMore;
642
820
  }
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,
821
+ // Return EventResult with event and any created/updated entity
822
+ return {
823
+ event: filteredEvent,
824
+ run,
825
+ step,
826
+ hook,
827
+ wait,
828
+ events,
829
+ cursor,
830
+ hasMore,
650
831
  };
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
- };
832
+ } // end createImpl
683
833
  },
684
834
  async get(runId, eventId, params) {
835
+ assertSafeEntityId('runId', runId);
836
+ assertSafeEntityId('eventId', eventId);
685
837
  const compositeKey = `${runId}-${eventId}`;
686
838
  const event = await readJSONWithFallback(basedir, 'events', compositeKey, EventSchema, tag);
687
839
  if (!event) {
@@ -692,6 +844,7 @@ export function createEventsStorage(basedir, tag) {
692
844
  },
693
845
  async list(params) {
694
846
  const { runId } = params;
847
+ assertSafeEntityId('runId', runId);
695
848
  const resolveData = params.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
696
849
  const result = await paginatedFileSystemQuery({
697
850
  directory: path.join(basedir, 'events'),
@@ -716,6 +869,7 @@ export function createEventsStorage(basedir, tag) {
716
869
  },
717
870
  async listByCorrelationId(params) {
718
871
  const correlationId = params.correlationId;
872
+ assertSafeEntityId('correlationId', correlationId);
719
873
  const resolveData = params.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
720
874
  const result = await paginatedFileSystemQuery({
721
875
  directory: path.join(basedir, 'events'),
@@ -741,4 +895,4 @@ export function createEventsStorage(basedir, tag) {
741
895
  },
742
896
  };
743
897
  }
744
- //# sourceMappingURL=events-storage.js.map
898
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRzLXN0b3JhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmFnZS9ldmVudHMtc3RvcmFnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsQyxPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixpQkFBaUIsRUFDakIsZUFBZSxFQUNmLG9CQUFvQixFQUNwQixhQUFhLEVBQ2Isd0JBQXdCLEVBQ3hCLGtCQUFrQixHQUNuQixNQUFNLGtCQUFrQixDQUFDO0FBVzFCLE9BQU8sRUFDTCxXQUFXLEVBQ1gsVUFBVSxFQUNWLG1CQUFtQixFQUNuQixrQkFBa0IsRUFDbEIsb0JBQW9CLEVBQ3BCLFVBQVUsRUFDVixxQkFBcUIsRUFDckIsVUFBVSxFQUNWLGlCQUFpQixHQUNsQixNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFDeEIsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzNELE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsVUFBVSxFQUNWLFlBQVksRUFDWixhQUFhLEVBQ2Isd0JBQXdCLEVBQ3hCLFFBQVEsRUFDUixvQkFBb0IsRUFDcEIsaUJBQWlCLEVBQ2pCLFVBQVUsRUFDVixjQUFjLEVBQ2QsU0FBUyxHQUNWLE1BQU0sVUFBVSxDQUFDO0FBQ2xCLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNsRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM1RSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDaEQsT0FBTyxFQUFFLGVBQWUs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Q0FBQyxPQUFPO2FBQ2hDLENBQUMsQ0FBQztZQUVILHlEQUF5RDtZQUN6RCxJQUFJLFdBQVcsS0FBSyxNQUFNLEVBQUUsQ0FBQztnQkFDM0IsT0FBTztvQkFDTCxHQUFHLE1BQU07b0JBQ1QsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FDOUIsa0JBQWtCLENBQUMsS0FBSyxFQUFFLFdBQVcsQ0FBQyxDQUN2QztpQkFDRixDQUFDO1lBQ0osQ0FBQztZQUVELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQge1xuICBFbnRpdHlDb25mbGljdEVycm9yLFxuICBIb29rTm90Rm91bmRFcnJvcixcbiAgUnVuRXhwaXJlZEVycm9yLFxuICBSdW5Ob3RTdXBwb3J0ZWRFcnJvcixcbiAgVG9vRWFybHlFcnJvcixcbiAgV29ya2Zsb3dSdW5Ob3RGb3VuZEVycm9yLFxuICBXb3JrZmxvd1dvcmxkRXJyb3IsXG59IGZyb20gJ0B3b3JrZmxvdy9lcnJvcnMnO1xuaW1wb3J0IHR5cGUge1xuICBFdmVudCxcbiAgRXZlbnRSZXN1bHQsXG4gIEhvb2ssXG4gIFNlcmlhbGl6ZWREYXRhLFxuICBTdGVwLFxuICBTdG9yYWdlLFxuICBXYWl0LFxuICBXb3JrZmxvd1J1bixcbn0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcbmltcG9ydCB7XG4gIEV2ZW50U2NoZW1hLFxuICBIb29rU2NoZW1hLFxuICBpc0xlZ2FjeVNwZWNWZXJzaW9uLFxuICByZXF1aXJlc05ld2VyV29ybGQsXG4gIFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICBTdGVwU2NoZW1hLFxuICB2YWxpZGF0ZVVsaWRUaW1lc3RhbXAsXG4gIFdhaXRTY2hlbWEsXG4gIFdvcmtmbG93UnVuU2NoZW1hLFxufSBmcm9tICdAd29ya2Zsb3cvd29ybGQnO1xuaW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5pbXBvcnQgeyBERUZBVUxUX1JFU09MVkVfREFUQV9PUFRJT04gfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHtcbiAgYXNzZXJ0U2FmZUVudGl0eUlkLFxuICBkZWxldGVKU09OLFxuICBqc29uUmVwbGFjZXIsXG4gIGxpc3RKU09ORmlsZXMsXG4gIHBhZ2luYXRlZEZpbGVTeXN0ZW1RdWVyeSxcbiAgcmVhZEpTT04sXG4gIHJlYWRKU09OV2l0aEZhbGxiYWNrLFxuICByZXNvbHZlV2l0aGluQmFzZSxcbiAgdGFnZ2VkUGF0aCxcbiAgd3JpdGVFeGNsdXNpdmUsXG4gIHdyaXRlSlNPTixcbn0gZnJvbSAnLi4vZnMuanMnO1xuaW1wb3J0IHsgc3RyaXBFdmVudERhdGFSZWZzIH0gZnJvbSAnLi9maWx0ZXJzLmpzJztcbmltcG9ydCB7IGdldE9iamVjdENyZWF0ZWRBdCwgaGFzaFRva2VuLCBtb25vdG9uaWNVbGlkIH0gZnJvbSAnLi9oZWxwZXJzLmpzJztcbmltcG9ydCB7IGRlbGV0ZUFsbEhvb2tzRm9yUnVuIH0gZnJvbSAnLi9ob29rcy1zdG9yYWdlLmpzJztcbmltcG9ydCB7IGhhbmRsZUxlZ2FjeUV2ZW50IH0gZnJvbSAnLi9sZWdhY3kuanMnO1xuaW1wb3J0IHsgd2l0aFJ1bkZpbGVMb2NrIH0gZnJvbSAnLi9ydW5zLXN0b3JhZ2UuanMnO1xuXG4vKipcbiAqIFBlci1zdGVwIGluLXByb2Nlc3MgYXN5bmMgbXV0ZXguIFNlcmlhbGl6ZXMgY29uY3VycmVudCBgZXZlbnRzLmNyZWF0ZWAgY2FsbHNcbiAqIHRoYXQgdGFyZ2V0IHRoZSBzYW1lIHN0ZXAsIHNvIHRoYXQgdGhlIFwiY2hlY2sgdGVybWluYWwgc3RhdGUsIHRoZW4gd3JpdGUgc3RlcFxuICogZW50aXR5ICsgZXZlbnRcIiBzZXF1ZW5jZSBpcyBhdG9taWMuIFdpdGhvdXQgdGhpcywgdHdvIGNvbmN1cnJlbnQgc3RlcF9zdGFydGVkXG4gKiBjYWxscyBjYW4gYm90aCBwYXNzIHRoZSBub3QtdGVybWluYWwgY2hlY2sgYW5kIGJvdGggd3JpdGUgc3RlcF9zdGFydGVkIGV2ZW50c1xuICog4oCUIG9yIGEgc3RlcF9zdGFydGVkIGNhbiBsYW5kIGluIHRoZSBsb2cgYWZ0ZXIgc3RlcF9jb21wbGV0ZWQgaGFzIGFscmVhZHlcbiAqIHdyaXR0ZW4sIHByb2R1Y2luZyB1bmNvbnN1bWVkIGV2ZW50cyBvbiByZXBsYXkuXG4gKlxuICogRHVwbGljYXRlIHN0ZXBfc3RhcnRlZCBldmVudHMgZm9yIGEgbm9uLXRlcm1pbmFsIHN0ZXAgYXJlIHN0aWxsIGFsbG93ZWRcbiAqIChyZXRyaWVzIGxlZ2l0aW1hdGVseSByZS1zdGFydCBhIHN0ZXApLCBvbmx5IHdyaXRlcyB0byBhbiBhbHJlYWR5LXRlcm1pbmFsXG4gKiBzdGVwIGFyZSByZWplY3RlZC5cbiAqL1xuY29uc3Qgc3RlcExvY2tzID0gbmV3IE1hcDxzdHJpbmcsIFByb21pc2U8dW5rbm93bj4+KCk7XG5cbmNvbnN0IEhvb2tUb2tlbkNsYWltU2NoZW1hID0gei5vYmplY3Qoe1xuICBydW5JZDogei5zdHJpbmcoKSxcbn0pO1xuXG5hc3luYyBmdW5jdGlvbiByZWFkSG9va1Rva2VuQ2xhaW0oXG4gIGNvbnN0cmFpbnRQYXRoOiBzdHJpbmdcbik6IFByb21pc2U8ei5pbmZlcjx0eXBlb2YgSG9va1Rva2VuQ2xhaW1TY2hlbWE+IHwgbnVsbD4ge1xuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCByZWFkSlNPTihjb25zdHJhaW50UGF0aCwgSG9va1Rva2VuQ2xhaW1TY2hlbWEpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFN5bnRheEVycm9yIHx8IGVycm9yIGluc3RhbmNlb2Ygei5ab2RFcnJvcikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHRocm93IGVycm9yO1xuICB9XG59XG5cbmZ1bmN0aW9uIHdpdGhTdGVwTG9jazxUPihrZXk6IHN0cmluZywgZm46ICgpID0+IFByb21pc2U8VD4pOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgcHJldiA9IHN0ZXBMb2Nrcy5nZXQoa2V5KTtcbiAgY29uc3QgdGFza0JveDogeyB0YXNrPzogUHJvbWlzZTxUPiB9ID0ge307XG4gIGNvbnN0IHRhc2sgPSAoYXN5bmMgKCkgPT4ge1xuICAgIGlmIChwcmV2KSB7XG4gICAgICAvLyBXYWl0IGZvciB0aGUgcHJldmlvdXMgdGFzayB0byBzZXR0bGU7IGRvbid0IGluaGVyaXQgaXRzIGVycm9ycy5cbiAgICAgIGF3YWl0IHByZXYuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBmbigpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBpZiAoc3RlcExvY2tzLmdldChrZXkpID09PSB0YXNrQm94LnRhc2spIHtcbiAgICAgICAgc3RlcExvY2tzLmRlbGV0ZShrZXkpO1xuICAgICAgfVxuICAgIH1cbiAgfSkoKTtcbiAgdGFza0JveC50YXNrID0gdGFzaztcbiAgc3RlcExvY2tzLnNldChrZXksIHRhc2spO1xuICByZXR1cm4gdGFzaztcbn1cblxuLyoqXG4gKiBIZWxwZXIgZnVuY3Rpb24gdG8gZGVsZXRlIGFsbCB3YWl0cyBhc3NvY2lhdGVkIHdpdGggYSB3b3JrZmxvdyBydW4uXG4gKiBDYWxsZWQgd2hlbiBhIHJ1biByZWFjaGVzIGEgdGVybWluYWwgc3RhdGUuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlbGV0ZUFsbFdhaXRzRm9yUnVuKFxuICBiYXNlZGlyOiBzdHJpbmcsXG4gIHJ1bklkOiBzdHJpbmdcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB3YWl0c0RpciA9IHBhdGguam9pbihiYXNlZGlyLCAnd2FpdHMnKTtcbiAgY29uc3QgZmlsZXMgPSBhd2FpdCBsaXN0SlNPTkZpbGVzKHdhaXRzRGlyKTtcblxuICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAvLyBmaWxlSWRzIG1heSBjb250YWluIHRhZyBzdWZmaXhlcyAoZS5nLiwgXCJ3cnVuX0FCQy1jb3JySWQudml0ZXN0LTBcIilcbiAgICAvLyBidXQgc3RhcnRzV2l0aCBzdGlsbCBtYXRjaGVzIGNvcnJlY3RseSBzaW5jZSB0aGUgdGFnIGlzIGEgc3VmZml4LlxuICAgIGlmIChmaWxlLnN0YXJ0c1dpdGgoYCR7cnVuSWR9LWApKSB7XG4gICAgICBjb25zdCB3YWl0UGF0aCA9IHBhdGguam9pbih3YWl0c0RpciwgYCR7ZmlsZX0uanNvbmApO1xuICAgICAgYXdhaXQgZGVsZXRlSlNPTih3YWl0UGF0aCk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogUGVyc2lzdCBhIGxpZmVjeWNsZS1kcml2ZW4gcnVuIHVwZGF0ZSAocnVuX3N0YXJ0ZWQgLyBydW5fY29tcGxldGVkIC9cbiAqIHJ1bl9mYWlsZWQgLyBydW5fY2FuY2VsbGVkKSB1bmRlciB0aGUgc2hhcmVkIHBlci1ydW4gZmlsZSBsb2NrLFxuICogcmUtcmVhZGluZyB0aGUgb24tZGlzayBydW4gaW5zaWRlIHRoZSBsb2NrIHNvIGFueSBhdHRyaWJ1dGUgd3JpdGVzXG4gKiB0aGF0IGxhbmRlZCBiZXR3ZWVuIHRoZSBwcmUtdmFsaWRhdGlvbiBgY3VycmVudFJ1bmAgcmVhZCBhbmQgdGhpc1xuICogd3JpdGUgYXJlIHByZXNlcnZlZC4gV2l0aG91dCB0aGUgcmUtcmVhZCwgYW4gYGV4cGVyaW1lbnRhbFNldEF0dHJpYnV0ZXNgXG4gKiBjYWxsIHNhbmR3aWNoZWQgYmV0d2VlbiB0aGUgbGlmZWN5Y2xlIHJlYWQgYW5kIHdyaXRlIHdvdWxkIGJlXG4gKiBzaWxlbnRseSBvdmVyd3JpdHRlbiBieSB0aGUgbGlmZWN5Y2xlIHdyaXRlJ3Mgc3RhbGUgYXR0cmlidXRlIHNuYXBzaG90LlxuICpcbiAqIGBwcm9wb3NlZGAgaXMgdGhlIGZ1bGx5LWNvbnN0cnVjdGVkIHJ1biByb3cgdGhlIGNhbGxlciB3YW50cyB0b1xuICogd3JpdGUgKHdpdGggdGhlIGNvcnJlY3QgZGlzY3JpbWluYXRlZC11bmlvbiBzdGF0dXMgYnJhbmNoKS4gT25seSB0aGVcbiAqIGBhdHRyaWJ1dGVzYCBmaWVsZCBpcyByZXBsYWNlZCB3aXRoIHRoZSBmcmVzaGVzdCB2ZXJzaW9uIGluc2lkZSB0aGVcbiAqIGxvY2suXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdyaXRlUnVuVW5kZXJMaWZlY3ljbGVMb2NrPFQgZXh0ZW5kcyBXb3JrZmxvd1J1bj4oXG4gIGJhc2VkaXI6IHN0cmluZyxcbiAgcnVuSWQ6IHN0cmluZyxcbiAgdGFnOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gIHByb3Bvc2VkOiBUXG4pOiBQcm9taXNlPFQ+IHtcbiAgcmV0dXJuIHdpdGhSdW5GaWxlTG9jayhydW5JZCwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGZyZXNoID0gYXdhaXQgcmVhZEpTT04oXG4gICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdydW5zJywgcnVuSWQsIHRhZyksXG4gICAgICBXb3JrZmxvd1J1blNjaGVtYVxuICAgICk7XG4gICAgY29uc3QgbmV4dDogVCA9IHtcbiAgICAgIC4uLnByb3Bvc2VkLFxuICAgICAgYXR0cmlidXRlczogZnJlc2g/LmF0dHJpYnV0ZXMgPz8gcHJvcG9zZWQuYXR0cmlidXRlcyxcbiAgICB9O1xuICAgIGF3YWl0IHdyaXRlSlNPTih0YWdnZWRQYXRoKGJhc2VkaXIsICdydW5zJywgcnVuSWQsIHRhZyksIG5leHQsIHtcbiAgICAgIG92ZXJ3cml0ZTogdHJ1ZSxcbiAgICB9KTtcbiAgICByZXR1cm4gbmV4dDtcbiAgfSk7XG59XG5cbi8qKlxuICogQ3JlYXRlcyB0aGUgZXZlbnRzIHN0b3JhZ2UgaW1wbGVtZW50YXRpb24gdXNpbmcgdGhlIGZpbGVzeXN0ZW0uXG4gKiBJbXBsZW1lbnRzIHRoZSBTdG9yYWdlWydldmVudHMnXSBpbnRlcmZhY2Ugd2l0aCBjcmVhdGUsIGxpc3QsIGFuZCBsaXN0QnlDb3JyZWxhdGlvbklkIG9wZXJhdGlvbnMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFdmVudHNTdG9yYWdlKFxuICBiYXNlZGlyOiBzdHJpbmcsXG4gIHRhZz86IHN0cmluZ1xuKTogU3RvcmFnZVsnZXZlbnRzJ10ge1xuICByZXR1cm4ge1xuICAgIGFzeW5jIGNyZWF0ZShydW5JZCwgZGF0YSwgcGFyYW1zKTogUHJvbWlzZTxFdmVudFJlc3VsdD4ge1xuICAgICAgLy8gVmFsaWRhdGUgcmVxdWVzdC1zdXBwbGllZCBJRHMgYmVmb3JlIHRoZXkncmUgY29uY2F0ZW5hdGVkIGludG9cbiAgICAgIC8vIGZpbGVzeXN0ZW0gcGF0aHMuIFRoaXMgaXMgdGhlIHByaW1hcnkgZGVmZW5zZSBhZ2FpbnN0IHBhdGggdHJhdmVyc2FsXG4gICAgICAvLyBhdHRhY2tzIHdoZXJlIGEgY2xpZW50IHN1cHBsaWVzIHJ1bklkIC8gY29ycmVsYXRpb25JZCB2YWx1ZXMgbGlrZVxuICAgICAgLy8gXCIuLi8uLi8uLi9wYWNrYWdlXCIgdG8gcmVhZCBvciB3cml0ZSBmaWxlcyBvdXRzaWRlIHRoZSBzdG9yYWdlIHJvb3QuXG4gICAgICAvLyBSdW4gYmVmb3JlIHRha2luZyB0aGUgcGVyLXN0ZXAgbXV0ZXggc28gbWFsZm9ybWVkIGlucHV0cyBmYWlsIGZhc3QuXG4gICAgICAvL1xuICAgICAgLy8gRW1wdHkgYGNvcnJlbGF0aW9uSWRgIHZhbHVlcyBhcmUgYWxzbyByZWplY3RlZCBoZXJlOiB0aGUgZXZlbnRcbiAgICAgIC8vIHNjaGVtYXMgb25seSByZXF1aXJlIGB6LnN0cmluZygpYCwgc28gd2l0aG91dCB0aGlzIGNoZWNrIGFcbiAgICAgIC8vIHN0ZXBfY3JlYXRlZCAvIGhvb2tfY3JlYXRlZCAvIHdhaXRfY3JlYXRlZCByZXF1ZXN0IHdpdGhcbiAgICAgIC8vIGBjb3JyZWxhdGlvbklkOiAnJ2Agd291bGQgc2lsZW50bHkgYmUgd3JpdHRlbiB1bmRlciBhIG1hbGZvcm1lZFxuICAgICAgLy8gY29tcG9zaXRlIGtleSBsaWtlIGAke3J1bklkfS1gLlxuICAgICAgaWYgKHJ1bklkICE9IG51bGwgJiYgcnVuSWQgIT09ICcnKSB7XG4gICAgICAgIGFzc2VydFNhZmVFbnRpdHlJZCgncnVuSWQnLCBydW5JZCk7XG4gICAgICB9XG4gICAgICBpZiAoJ2NvcnJlbGF0aW9uSWQnIGluIGRhdGEgJiYgdHlwZW9mIGRhdGEuY29ycmVsYXRpb25JZCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgYXNzZXJ0U2FmZUVudGl0eUlkKCdjb3JyZWxhdGlvbklkJywgZGF0YS5jb3JyZWxhdGlvbklkKTtcbiAgICAgIH1cblxuICAgICAgLy8gU3RlcCBsaWZlY3ljbGUgZXZlbnRzIGFyZSBzZXJpYWxpemVkIHBlci1zdGVwIHZpYSBhbiBpbi1wcm9jZXNzIG11dGV4XG4gICAgICAvLyBzbyB0aGF0IHRoZSBcImNoZWNrIHN0YXRlLCB0aGVuIHdyaXRlXCIgc2VxdWVuY2UgaW4gc3RlcF9zdGFydGVkIC9cbiAgICAgIC8vIHN0ZXBfY29tcGxldGVkIC8gc3RlcF9mYWlsZWQgLyBzdGVwX3JldHJ5aW5nIGlzIGF0b21pYy4gc3RlcF9jcmVhdGVkXG4gICAgICAvLyBpcyBhbHNvIHNlcmlhbGl6ZWQgc28gZHVwbGljYXRlLWNyZWF0ZSByYWNlcyBkb24ndCBsZWF2ZSBleHRyYVxuICAgICAgLy8gc3RlcF9jcmVhdGVkIGV2ZW50cyBpbiB0aGUgbG9nLlxuICAgICAgY29uc3QgaXNTdGVwRXZlbnQgPVxuICAgICAgICBkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfY3JlYXRlZCcgfHxcbiAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX3N0YXJ0ZWQnIHx8XG4gICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9jb21wbGV0ZWQnIHx8XG4gICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9mYWlsZWQnIHx8XG4gICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9yZXRyeWluZyc7XG4gICAgICBpZiAoaXNTdGVwRXZlbnQgJiYgcnVuSWQgJiYgZGF0YS5jb3JyZWxhdGlvbklkKSB7XG4gICAgICAgIGNvbnN0IGxvY2tLZXkgPSB0YWdcbiAgICAgICAgICA/IGAke3J1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH0uJHt0YWd9YFxuICAgICAgICAgIDogYCR7cnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgIHJldHVybiB3aXRoU3RlcExvY2sobG9ja0tleSwgKCkgPT4gY3JlYXRlSW1wbCgpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBjcmVhdGVJbXBsKCk7XG5cbiAgICAgIGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUltcGwoKTogUHJvbWlzZTxFdmVudFJlc3VsdD4ge1xuICAgICAgICBjb25zdCBldmVudElkID0gYGV2bnRfJHttb25vdG9uaWNVbGlkKCl9YDtcbiAgICAgICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcblxuICAgICAgICAvLyBGb3IgcnVuX2NyZWF0ZWQgZXZlbnRzLCB1c2UgY2xpZW50LXByb3ZpZGVkIHJ1bklkIG9yIGdlbmVyYXRlIG9uZSBzZXJ2ZXItc2lkZVxuICAgICAgICBsZXQgZWZmZWN0aXZlUnVuSWQ6IHN0cmluZztcbiAgICAgICAgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAncnVuX2NyZWF0ZWQnICYmICghcnVuSWQgfHwgcnVuSWQgPT09ICcnKSkge1xuICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkID0gYHdydW5fJHttb25vdG9uaWNVbGlkKCl9YDtcbiAgICAgICAgfSBlbHNlIGlmICghcnVuSWQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3J1bklkIGlzIHJlcXVpcmVkIGZvciBub24tcnVuX2NyZWF0ZWQgZXZlbnRzJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZWZmZWN0aXZlUnVuSWQgPSBydW5JZDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFZhbGlkYXRlIGNsaWVudC1wcm92aWRlZCBydW5JZCB0aW1lc3RhbXAgaXMgd2l0aGluIGFjY2VwdGFibGUgdGhyZXNob2xkXG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9jcmVhdGVkJyAmJiBydW5JZCAmJiBydW5JZCAhPT0gJycpIHtcbiAgICAgICAgICBjb25zdCB2YWxpZGF0aW9uRXJyb3IgPSB2YWxpZGF0ZVVsaWRUaW1lc3RhbXAoXG4gICAgICAgICAgICBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICd3cnVuXydcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmICh2YWxpZGF0aW9uRXJyb3IpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBXb3JrZmxvd1dvcmxkRXJyb3IodmFsaWRhdGlvbkVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBzcGVjVmVyc2lvbiBpcyBhbHdheXMgc2VudCBieSB0aGUgcnVudGltZSwgYnV0IHdlIHByb3ZpZGUgYSBmYWxsYmFjayBmb3Igc2FmZXR5XG4gICAgICAgIGNvbnN0IGVmZmVjdGl2ZVNwZWNWZXJzaW9uID0gZGF0YS5zcGVjVmVyc2lvbiA/PyBTUEVDX1ZFUlNJT05fQ1VSUkVOVDtcblxuICAgICAgICAvLyBIZWxwZXIgdG8gY2hlY2sgaWYgcnVuIGlzIGluIHRlcm1pbmFsIHN0YXRlXG4gICAgICAgIGNvbnN0IGlzUnVuVGVybWluYWwgPSAoc3RhdHVzOiBzdHJpbmcpID0+XG4gICAgICAgICAgWydjb21wbGV0ZWQnLCAnZmFpbGVkJywgJ2NhbmNlbGxlZCddLmluY2x1ZGVzKHN0YXR1cyk7XG5cbiAgICAgICAgLy8gSGVscGVyIHRvIGNoZWNrIGlmIHN0ZXAgaXMgaW4gdGVybWluYWwgc3RhdGVcbiAgICAgICAgY29uc3QgaXNTdGVwVGVybWluYWwgPSAoc3RhdHVzOiBzdHJpbmcpID0+XG4gICAgICAgICAgWydjb21wbGV0ZWQnLCAnZmFpbGVkJywgJ2NhbmNlbGxlZCddLmluY2x1ZGVzKHN0YXR1cyk7XG5cbiAgICAgICAgLy8gR2V0IGN1cnJlbnQgcnVuIHN0YXRlIGZvciB2YWxpZGF0aW9uIChpZiBub3QgY3JlYXRpbmcgYSBuZXcgcnVuKVxuICAgICAgICAvLyBTa2lwIHJ1biB2YWxpZGF0aW9uIGZvciBzdGVwX2NvbXBsZXRlZCBhbmQgc3RlcF9yZXRyeWluZyAtIHRoZXkgb25seSBvcGVyYXRlXG4gICAgICAgIC8vIG9uIHJ1bm5pbmcgc3RlcHMsIGFuZCBydW5uaW5nIHN0ZXBzIGFyZSBhbHdheXMgYWxsb3dlZCB0byBtb2RpZnkgcmVnYXJkbGVzc1xuICAgICAgICAvLyBvZiBydW4gc3RhdGUuIFRoaXMgb3B0aW1pemF0aW9uIHNhdmVzIGZpbGVzeXN0ZW0gcmVhZHMgcGVyIHN0ZXAgZXZlbnQuXG4gICAgICAgIGxldCBjdXJyZW50UnVuOiBXb3JrZmxvd1J1biB8IG51bGwgPSBudWxsO1xuICAgICAgICBjb25zdCBza2lwUnVuVmFsaWRhdGlvbkV2ZW50cyA9IFsnc3RlcF9jb21wbGV0ZWQnLCAnc3RlcF9yZXRyeWluZyddO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgZGF0YS5ldmVudFR5cGUgIT09ICdydW5fY3JlYXRlZCcgJiZcbiAgICAgICAgICAhc2tpcFJ1blZhbGlkYXRpb25FdmVudHMuaW5jbHVkZXMoZGF0YS5ldmVudFR5cGUpXG4gICAgICAgICkge1xuICAgICAgICAgIGN1cnJlbnRSdW4gPSBhd2FpdCByZWFkSlNPTldpdGhGYWxsYmFjayhcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAncnVucycsXG4gICAgICAgICAgICBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgIFdvcmtmbG93UnVuU2NoZW1hLFxuICAgICAgICAgICAgdGFnXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIC8vIFJlc2lsaWVudCBzdGFydDogcnVuX3N0YXJ0ZWQgb24gbm9uLWV4aXN0ZW50IHJ1biB3aXRoIGV2ZW50RGF0YVxuICAgICAgICAgIC8vIGNyZWF0ZXMgdGhlIHJ1biBmaXJzdCwgc28gdGhlIHF1ZXVlIGNhbiBib290c3RyYXAgYSBydW4gdGhhdFxuICAgICAgICAgIC8vIGZhaWxlZCB0byBjcmVhdGUgZHVyaW5nIHN0YXJ0KCkuXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdydW5fc3RhcnRlZCcgJiZcbiAgICAgICAgICAgICFjdXJyZW50UnVuICYmXG4gICAgICAgICAgICAnZXZlbnREYXRhJyBpbiBkYXRhICYmXG4gICAgICAgICAgICBkYXRhLmV2ZW50RGF0YVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgY29uc3QgcnVuSW5wdXREYXRhID0gZGF0YS5ldmVudERhdGEgYXMge1xuICAgICAgICAgICAgICBkZXBsb3ltZW50SWQ/OiBzdHJpbmc7XG4gICAgICAgICAgICAgIHdvcmtmbG93TmFtZT86IHN0cmluZztcbiAgICAgICAgICAgICAgaW5wdXQ/OiBhbnk7XG4gICAgICAgICAgICAgIGV4ZWN1dGlvbkNvbnRleHQ/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgcnVuSW5wdXREYXRhLmRlcGxveW1lbnRJZCAmJlxuICAgICAgICAgICAgICBydW5JbnB1dERhdGEud29ya2Zsb3dOYW1lICYmXG4gICAgICAgICAgICAgIHJ1bklucHV0RGF0YS5pbnB1dCAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgLy8gQXRvbWljYWxseSB0cnkgdG8gY3JlYXRlIHRoZSBydW4gZW50aXR5LiB3cml0ZUV4Y2x1c2l2ZVxuICAgICAgICAgICAgICAvLyB1c2VzIE9fQ1JFQVR8T19FWENMIHNvIG9ubHkgdGhlIGZpcnN0IHdyaXRlciB3aW5zLFxuICAgICAgICAgICAgICAvLyBwcmV2ZW50aW5nIGEgVE9DVE9VIHJhY2Ugd2hlcmUgYSBjb25jdXJyZW50IHJ1bl9jcmVhdGVkXG4gICAgICAgICAgICAgIC8vIGZyb20gc3RhcnQoKSBjb3VsZCBvdmVyd3JpdGUgYSBydW4gdGhhdCB3YXMgYWxyZWFkeVxuICAgICAgICAgICAgICAvLyB0cmFuc2l0aW9uZWQgdG8gJ3J1bm5pbmcnLlxuICAgICAgICAgICAgICBjb25zdCBjcmVhdGVkUnVuOiBXb3JrZmxvd1J1biA9IHtcbiAgICAgICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgZGVwbG95bWVudElkOiBydW5JbnB1dERhdGEuZGVwbG95bWVudElkLFxuICAgICAgICAgICAgICAgIHN0YXR1czogJ3BlbmRpbmcnLFxuICAgICAgICAgICAgICAgIHdvcmtmbG93TmFtZTogcnVuSW5wdXREYXRhLndvcmtmbG93TmFtZSxcbiAgICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dDogcnVuSW5wdXREYXRhLmV4ZWN1dGlvbkNvbnRleHQsXG4gICAgICAgICAgICAgICAgaW5wdXQ6IHJ1bklucHV0RGF0YS5pbnB1dCxcbiAgICAgICAgICAgICAgICBvdXRwdXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBlcnJvcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgYXR0cmlidXRlczoge30sXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIGNvbnN0IHJ1blBhdGggPSB0YWdnZWRQYXRoKGJhc2VkaXIsICdydW5zJywgZWZmZWN0aXZlUnVuSWQsIHRhZyk7XG4gICAgICAgICAgICAgIGNvbnN0IGNyZWF0ZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShcbiAgICAgICAgICAgICAgICBydW5QYXRoLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGNyZWF0ZWRSdW4sIGpzb25SZXBsYWNlcilcbiAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICBpZiAoY3JlYXRlZCkge1xuICAgICAgICAgICAgICAgIC8vIFdlIGNyZWF0ZWQgdGhlIHJ1biDigJQgYWxzbyB3cml0ZSB0aGUgcnVuX2NyZWF0ZWQgZXZlbnQuXG4gICAgICAgICAgICAgICAgY29uc3QgcnVuQ3JlYXRlZEV2ZW50SWQgPSBgZXZudF8ke21vbm90b25pY1VsaWQoKX1gO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJ1bkNyZWF0ZWRFdmVudDogRXZlbnQgPSB7XG4gICAgICAgICAgICAgICAgICBldmVudFR5cGU6ICdydW5fY3JlYXRlZCcsXG4gICAgICAgICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgICBldmVudElkOiBydW5DcmVhdGVkRXZlbnRJZCxcbiAgICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgIGRlcGxveW1lbnRJZDogcnVuSW5wdXREYXRhLmRlcGxveW1lbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lOiBydW5JbnB1dERhdGEud29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgICAgICBpbnB1dDogcnVuSW5wdXREYXRhLmlucHV0LFxuICAgICAgICAgICAgICAgICAgICBleGVjdXRpb25Db250ZXh0OiBydW5JbnB1dERhdGEuZXhlY3V0aW9uQ29udGV4dCxcbiAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBjb25zdCBjcmVhdGVkQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7cnVuQ3JlYXRlZEV2ZW50SWR9YDtcbiAgICAgICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdldmVudHMnLCBjcmVhdGVkQ29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgICAgICAgcnVuQ3JlYXRlZEV2ZW50XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBjdXJyZW50UnVuID0gY3JlYXRlZFJ1bjtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBSdW4gYWxyZWFkeSBleGlzdHMgKGNvbmN1cnJlbnQgcnVuX2NyZWF0ZWQgd29uIHRoZVxuICAgICAgICAgICAgICAgIC8vIHJhY2UpLiBSZS1yZWFkIGl0IHNvIGRvd25zdHJlYW0gbG9naWMgc2VlcyB0aGUgcmVhbCBzdGF0ZS5cbiAgICAgICAgICAgICAgICBjdXJyZW50UnVuID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICAgICAgJ3J1bnMnLFxuICAgICAgICAgICAgICAgICAgZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgICAgICBXb3JrZmxvd1J1blNjaGVtYSxcbiAgICAgICAgICAgICAgICAgIHRhZ1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBydW5fZmFpbGVkIG9uIGEgbm9uLWV4aXN0ZW50IHJ1biBpcyByZWplY3RlZCB0byBtYXRjaCB0aGVcbiAgICAgICAgLy8gcG9zdGdyZXMgYW5kIHZlcmNlbCB3b3JsZHMsIHdoaWNoIGJvdGggc3VyZmFjZSB0aGlzIGFzIGFcbiAgICAgICAgLy8gV29ya2Zsb3dSdW5Ob3RGb3VuZEVycm9yIHJhdGhlciB0aGFuIHNpbGVudGx5IHBlcnNpc3RpbmcgYW5cbiAgICAgICAgLy8gZXZlbnQgZm9yIGEgcnVuIHRoYXQgd2FzIG5ldmVyIGNyZWF0ZWQuXG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9mYWlsZWQnICYmICFjdXJyZW50UnVuKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFdvcmtmbG93UnVuTm90Rm91bmRFcnJvcihlZmZlY3RpdmVSdW5JZCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8gVkVSU0lPTiBDT01QQVRJQklMSVRZOiBDaGVjayBydW4gc3BlYyB2ZXJzaW9uXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBGb3IgZXZlbnRzIHRoYXQgaGF2ZSBmZXRjaGVkIHRoZSBydW4sIGNoZWNrIHZlcnNpb24gY29tcGF0aWJpbGl0eS5cbiAgICAgICAgLy8gU2tpcCBmb3IgcnVuX2NyZWF0ZWQgKG5vIGV4aXN0aW5nIHJ1bikgYW5kIHJ1bnRpbWUgZXZlbnRzIChzdGVwX2NvbXBsZXRlZCwgc3RlcF9yZXRyeWluZykuXG4gICAgICAgIGlmIChjdXJyZW50UnVuKSB7XG4gICAgICAgICAgLy8gQ2hlY2sgaWYgcnVuIHJlcXVpcmVzIGEgbmV3ZXIgd29ybGQgdmVyc2lvblxuICAgICAgICAgIGlmIChyZXF1aXJlc05ld2VyV29ybGQoY3VycmVudFJ1bi5zcGVjVmVyc2lvbikpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBSdW5Ob3RTdXBwb3J0ZWRFcnJvcihcbiAgICAgICAgICAgICAgY3VycmVudFJ1bi5zcGVjVmVyc2lvbiEsXG4gICAgICAgICAgICAgIFNQRUNfVkVSU0lPTl9DVVJSRU5UXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFJvdXRlIHRvIGxlZ2FjeSBoYW5kbGVyIGZvciBwcmUtZXZlbnQtc291cmNpbmcgcnVuc1xuICAgICAgICAgIGlmIChpc0xlZ2FjeVNwZWNWZXJzaW9uKGN1cnJlbnRSdW4uc3BlY1ZlcnNpb24pKSB7XG4gICAgICAgICAgICByZXR1cm4gaGFuZGxlTGVnYWN5RXZlbnQoXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICBkYXRhLFxuICAgICAgICAgICAgICBjdXJyZW50UnVuLFxuICAgICAgICAgICAgICBwYXJhbXNcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIFZBTElEQVRJT046IFRlcm1pbmFsIHN0YXRlIGFuZCBldmVudCBvcmRlcmluZyBjaGVja3NcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICAgICAgLy8gUnVuIHRlcm1pbmFsIHN0YXRlIHZhbGlkYXRpb25cbiAgICAgICAgaWYgKGN1cnJlbnRSdW4gJiYgaXNSdW5UZXJtaW5hbChjdXJyZW50UnVuLnN0YXR1cykpIHtcbiAgICAgICAgICBjb25zdCBydW5UZXJtaW5hbEV2ZW50cyA9IFtcbiAgICAgICAgICAgICdydW5fc3RhcnRlZCcsXG4gICAgICAgICAgICAncnVuX2NvbXBsZXRlZCcsXG4gICAgICAgICAgICAncnVuX2ZhaWxlZCcsXG4gICAgICAgICAgXTtcblxuICAgICAgICAgIC8vIElkZW1wb3RlbnQgb3BlcmF0aW9uOiBydW5fY2FuY2VsbGVkIG9uIGFscmVhZHkgY2FuY2VsbGVkIHJ1biBpcyBhbGxvd2VkXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdydW5fY2FuY2VsbGVkJyAmJlxuICAgICAgICAgICAgY3VycmVudFJ1bi5zdGF0dXMgPT09ICdjYW5jZWxsZWQnXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICAvLyBSZXR1cm4gZXhpc3Rpbmcgc3RhdGUgKGlkZW1wb3RlbnQpXG4gICAgICAgICAgICBjb25zdCBldmVudDogRXZlbnQgPSB7XG4gICAgICAgICAgICAgIC4uLmRhdGEsXG4gICAgICAgICAgICAgIHJ1bklkOiBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgICAgY3JlYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBlZmZlY3RpdmVTcGVjVmVyc2lvbixcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBjb25zdCBjb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtldmVudElkfWA7XG4gICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ2V2ZW50cycsIGNvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgZXZlbnRcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCByZXNvbHZlRGF0YSA9XG4gICAgICAgICAgICAgIHBhcmFtcz8ucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgZXZlbnQ6IHN0cmlwRXZlbnREYXRhUmVmcyhldmVudCwgcmVzb2x2ZURhdGEpLFxuICAgICAgICAgICAgICBydW46IGN1cnJlbnRSdW4sXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEZvciBydW5fc3RhcnRlZCBvbiB0ZXJtaW5hbCBydW5zLCB1c2UgUnVuRXhwaXJlZEVycm9yIHNvIHRoZVxuICAgICAgICAgIC8vIHJ1bnRpbWUga25vd3MgdG8gZXhpdCB3aXRob3V0IHJldHJ5aW5nLlxuICAgICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFJ1bkV4cGlyZWRFcnJvcihcbiAgICAgICAgICAgICAgYFdvcmtmbG93IHJ1biBcIiR7ZWZmZWN0aXZlUnVuSWR9XCIgaXMgYWxyZWFkeSBpbiB0ZXJtaW5hbCBzdGF0ZSBcIiR7Y3VycmVudFJ1bi5zdGF0dXN9XCJgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIE90aGVyIHJ1biBzdGF0ZSB0cmFuc2l0aW9ucyBhcmUgbm90IGFsbG93ZWQgb24gdGVybWluYWwgcnVuc1xuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIHJ1blRlcm1pbmFsRXZlbnRzLmluY2x1ZGVzKGRhdGEuZXZlbnRUeXBlKSB8fFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdydW5fY2FuY2VsbGVkJ1xuICAgICAgICAgICkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBDYW5ub3QgdHJhbnNpdGlvbiBydW4gZnJvbSB0ZXJtaW5hbCBzdGF0ZSBcIiR7Y3VycmVudFJ1bi5zdGF0dXN9XCJgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENyZWF0aW5nIG5ldyBlbnRpdGllcyBvbiB0ZXJtaW5hbCBydW5zIGlzIG5vdCBhbGxvd2VkXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2NyZWF0ZWQnIHx8XG4gICAgICAgICAgICBkYXRhLmV2ZW50VHlwZSA9PT0gJ2hvb2tfY3JlYXRlZCcgfHxcbiAgICAgICAgICAgIGRhdGEuZXZlbnRUeXBlID09PSAnd2FpdF9jcmVhdGVkJ1xuICAgICAgICAgICkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBDYW5ub3QgY3JlYXRlIG5ldyBlbnRpdGllcyBvbiBydW4gaW4gdGVybWluYWwgc3RhdGUgXCIke2N1cnJlbnRSdW4uc3RhdHVzfVwiYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTdGVwLXJlbGF0ZWQgZXZlbnQgdmFsaWRhdGlvbiAob3JkZXJpbmcgYW5kIHRlcm1pbmFsIHN0YXRlKVxuICAgICAgICAvLyBTdG9yZSBleGlzdGluZ1N0ZXAgc28gd2UgY2FuIHJldXNlIGl0IGxhdGVyIChhdm9pZCBkb3VibGUgcmVhZClcbiAgICAgICAgbGV0IHZhbGlkYXRlZFN0ZXA6IFN0ZXAgfCBudWxsID0gbnVsbDtcbiAgICAgICAgY29uc3Qgc3RlcEV2ZW50cyA9IFtcbiAgICAgICAgICAnc3RlcF9zdGFydGVkJyxcbiAgICAgICAgICAnc3RlcF9jb21wbGV0ZWQnLFxuICAgICAgICAgICdzdGVwX2ZhaWxlZCcsXG4gICAgICAgICAgJ3N0ZXBfcmV0cnlpbmcnLFxuICAgICAgICBdO1xuICAgICAgICBpZiAoc3RlcEV2ZW50cy5pbmNsdWRlcyhkYXRhLmV2ZW50VHlwZSkgJiYgZGF0YS5jb3JyZWxhdGlvbklkKSB7XG4gICAgICAgICAgY29uc3Qgc3RlcENvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH1gO1xuICAgICAgICAgIHZhbGlkYXRlZFN0ZXAgPSBhd2FpdCByZWFkSlNPTldpdGhGYWxsYmFjayhcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnc3RlcHMnLFxuICAgICAgICAgICAgc3RlcENvbXBvc2l0ZUtleSxcbiAgICAgICAgICAgIFN0ZXBTY2hlbWEsXG4gICAgICAgICAgICB0YWdcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gRXZlbnQgb3JkZXJpbmc6IHN0ZXAgbXVzdCBleGlzdCBiZWZvcmUgdGhlc2UgZXZlbnRzXG4gICAgICAgICAgaWYgKCF2YWxpZGF0ZWRTdGVwKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgV29ya2Zsb3dXb3JsZEVycm9yKFxuICAgICAgICAgICAgICBgU3RlcCBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIG5vdCBmb3VuZGBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gU3RlcCB0ZXJtaW5hbCBzdGF0ZSB2YWxpZGF0aW9uXG4gICAgICAgICAgaWYgKGlzU3RlcFRlcm1pbmFsKHZhbGlkYXRlZFN0ZXAuc3RhdHVzKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBDYW5ub3QgbW9kaWZ5IHN0ZXAgaW4gdGVybWluYWwgc3RhdGUgXCIke3ZhbGlkYXRlZFN0ZXAuc3RhdHVzfVwiYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBPbiB0ZXJtaW5hbCBydW5zOiBvbmx5IGFsbG93IGNvbXBsZXRpbmcvZmFpbGluZyBpbi1wcm9ncmVzcyBzdGVwc1xuICAgICAgICAgIGlmIChjdXJyZW50UnVuICYmIGlzUnVuVGVybWluYWwoY3VycmVudFJ1bi5zdGF0dXMpKSB7XG4gICAgICAgICAgICBpZiAodmFsaWRhdGVkU3RlcC5zdGF0dXMgIT09ICdydW5uaW5nJykge1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgUnVuRXhwaXJlZEVycm9yKFxuICAgICAgICAgICAgICAgIGBDYW5ub3QgbW9kaWZ5IG5vbi1ydW5uaW5nIHN0ZXAgb24gcnVuIGluIHRlcm1pbmFsIHN0YXRlIFwiJHtjdXJyZW50UnVuLnN0YXR1c31cImBcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBIb29rLXJlbGF0ZWQgZXZlbnQgdmFsaWRhdGlvbiAob3JkZXJpbmcpXG4gICAgICAgIGNvbnN0IGhvb2tFdmVudHNSZXF1aXJpbmdFeGlzdGVuY2UgPSBbJ2hvb2tfZGlzcG9zZWQnLCAnaG9va19yZWNlaXZlZCddO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgaG9va0V2ZW50c1JlcXVpcmluZ0V4aXN0ZW5jZS5pbmNsdWRlcyhkYXRhLmV2ZW50VHlwZSkgJiZcbiAgICAgICAgICBkYXRhLmNvcnJlbGF0aW9uSWRcbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgZXhpc3RpbmdIb29rID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJ2hvb2tzJyxcbiAgICAgICAgICAgIGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIEhvb2tTY2hlbWEsXG4gICAgICAgICAgICB0YWdcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgaWYgKCFleGlzdGluZ0hvb2spIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBIb29rTm90Rm91bmRFcnJvcihkYXRhLmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb25zdCBldmVudDogRXZlbnQgPSB7XG4gICAgICAgICAgLi4uZGF0YSxcbiAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgIH07XG4gICAgICAgIC8vIFN0cmlwIGV2ZW50RGF0YSBmcm9tIHJ1bl9zdGFydGVkIOKAlCBpdCBiZWxvbmdzIG9uIHJ1bl9jcmVhdGVkIG9ubHkuXG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJyAmJiAnZXZlbnREYXRhJyBpbiBldmVudCkge1xuICAgICAgICAgIGRlbGV0ZSAoZXZlbnQgYXMgYW55KS5ldmVudERhdGE7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBUcmFjayBlbnRpdHkgY3JlYXRlZC91cGRhdGVkIGZvciBFdmVudFJlc3VsdFxuICAgICAgICBsZXQgcnVuOiBXb3JrZmxvd1J1biB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IHN0ZXA6IFN0ZXAgfCB1bmRlZmluZWQ7XG4gICAgICAgIGxldCBob29rOiBIb29rIHwgdW5kZWZpbmVkO1xuICAgICAgICBsZXQgd2FpdDogV2FpdCB8IHVuZGVmaW5lZDtcblxuICAgICAgICAvLyBDcmVhdGUvdXBkYXRlIGVudGl0eSBiYXNlZCBvbiBldmVudCB0eXBlIChldmVudC1zb3VyY2VkIGFyY2hpdGVjdHVyZSlcbiAgICAgICAgLy8gUnVuIGxpZmVjeWNsZSBldmVudHNcbiAgICAgICAgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAncnVuX2NyZWF0ZWQnICYmICdldmVudERhdGEnIGluIGRhdGEpIHtcbiAgICAgICAgICBjb25zdCBydW5EYXRhID0gZGF0YS5ldmVudERhdGEgYXMge1xuICAgICAgICAgICAgZGVwbG95bWVudElkOiBzdHJpbmc7XG4gICAgICAgICAgICB3b3JrZmxvd05hbWU6IHN0cmluZztcbiAgICAgICAgICAgIGlucHV0OiBTZXJpYWxpemVkRGF0YTtcbiAgICAgICAgICAgIGV4ZWN1dGlvbkNvbnRleHQ/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuICAgICAgICAgIH07XG4gICAgICAgICAgcnVuID0ge1xuICAgICAgICAgICAgcnVuSWQ6IGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgZGVwbG95bWVudElkOiBydW5EYXRhLmRlcGxveW1lbnRJZCxcbiAgICAgICAgICAgIHN0YXR1czogJ3BlbmRpbmcnLFxuICAgICAgICAgICAgd29ya2Zsb3dOYW1lOiBydW5EYXRhLndvcmtmbG93TmFtZSxcbiAgICAgICAgICAgIC8vIFByb3BhZ2F0ZSBzcGVjVmVyc2lvbiBmcm9tIHRoZSBldmVudCB0byB0aGUgcnVuIGVudGl0eVxuICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dDogcnVuRGF0YS5leGVjdXRpb25Db250ZXh0LFxuICAgICAgICAgICAgaW5wdXQ6IHJ1bkRhdGEuaW5wdXQsXG4gICAgICAgICAgICBvdXRwdXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgICBzdGFydGVkQXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICBhdHRyaWJ1dGVzOiB7fSxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgfTtcbiAgICAgICAgICAvLyBVc2Ugd3JpdGVFeGNsdXNpdmUgKE9fQ1JFQVR8T19FWENMKSB0byBhdG9taWNhbGx5IGNyZWF0ZSB0aGVcbiAgICAgICAgICAvLyBydW4gZW50aXR5IGZpbGUuIFRoaXMgcHJldmVudHMgYSBUT0NUT1UgcmFjZSB3aXRoIHRoZSByZXNpbGllbnRcbiAgICAgICAgICAvLyBzdGFydCBwYXRoIChydW5fc3RhcnRlZCBvbiBub24tZXhpc3RlbnQgcnVuKSB0aGF0IGNvdWxkIHJlc3VsdFxuICAgICAgICAgIC8vIGluIGR1cGxpY2F0ZSBydW5fY3JlYXRlZCBldmVudHMgaW4gdGhlIGV2ZW50IGxvZy5cbiAgICAgICAgICBjb25zdCBydW5QYXRoID0gdGFnZ2VkUGF0aChiYXNlZGlyLCAncnVucycsIGVmZmVjdGl2ZVJ1bklkLCB0YWcpO1xuICAgICAgICAgIGNvbnN0IGNyZWF0ZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZShcbiAgICAgICAgICAgIHJ1blBhdGgsXG4gICAgICAgICAgICBKU09OLnN0cmluZ2lmeShydW4sIGpzb25SZXBsYWNlciwgMilcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmICghY3JlYXRlZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBXb3JrZmxvdyBydW4gXCIke2VmZmVjdGl2ZVJ1bklkfVwiIGFscmVhZHkgZXhpc3RzYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdydW5fc3RhcnRlZCcpIHtcbiAgICAgICAgICAvLyBSZXVzZSBjdXJyZW50UnVuIGZyb20gdmFsaWRhdGlvbiAoYWxyZWFkeSByZWFkIGFib3ZlKVxuICAgICAgICAgIGlmIChjdXJyZW50UnVuKSB7XG4gICAgICAgICAgICAvLyBJZiBhbHJlYWR5IHJ1bm5pbmcsIHJldHVybiB0aGUgcnVuIHdpdGhvdXQgaW5zZXJ0aW5nIGFcbiAgICAgICAgICAgIC8vIGR1cGxpY2F0ZSBldmVudC4gIFRoaXMgbWFrZXMgcnVuX3N0YXJ0ZWQgaWRlbXBvdGVudCBmb3JcbiAgICAgICAgICAgIC8vIGNvbmN1cnJlbnQgaW52b2NhdGlvbnMuICBXZSBvbWl0IHByZWxvYWRlZCBldmVudHMgaGVyZVxuICAgICAgICAgICAgLy8gYmVjYXVzZSB0aGlzIGlzIGEgcmFyZSByYWNlLWNvbmRpdGlvbiBwYXRoIOKAlCB0aGUgcnVudGltZVxuICAgICAgICAgICAgLy8gZmFsbHMgYmFjayB0byBsb2FkV29ya2Zsb3dSdW5FdmVudHMoKS5cbiAgICAgICAgICAgIGlmIChjdXJyZW50UnVuLnN0YXR1cyA9PT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgICAgICAgIHJldHVybiB7IHJ1bjogY3VycmVudFJ1biB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBydW4gPSBhd2FpdCB3cml0ZVJ1blVuZGVyTGlmZWN5Y2xlTG9jayhcbiAgICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICAgZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICAgIHRhZyxcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHJ1bklkOiBjdXJyZW50UnVuLnJ1bklkLFxuICAgICAgICAgICAgICAgIGRlcGxveW1lbnRJZDogY3VycmVudFJ1bi5kZXBsb3ltZW50SWQsXG4gICAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lOiBjdXJyZW50UnVuLndvcmtmbG93TmFtZSxcbiAgICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogY3VycmVudFJ1bi5zcGVjVmVyc2lvbixcbiAgICAgICAgICAgICAgICBleGVjdXRpb25Db250ZXh0OiBjdXJyZW50UnVuLmV4ZWN1dGlvbkNvbnRleHQsXG4gICAgICAgICAgICAgICAgaW5wdXQ6IGN1cnJlbnRSdW4uaW5wdXQsXG4gICAgICAgICAgICAgICAgY3JlYXRlZEF0OiBjdXJyZW50UnVuLmNyZWF0ZWRBdCxcbiAgICAgICAgICAgICAgICBleHBpcmVkQXQ6IGN1cnJlbnRSdW4uZXhwaXJlZEF0LFxuICAgICAgICAgICAgICAgIHN0YXR1czogJ3J1bm5pbmcnLFxuICAgICAgICAgICAgICAgIG91dHB1dDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIGVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgY29tcGxldGVkQXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBzdGFydGVkQXQ6IGN1cnJlbnRSdW4uc3RhcnRlZEF0ID8/IG5vdyxcbiAgICAgICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgICBhdHRyaWJ1dGVzOiBjdXJyZW50UnVuLmF0dHJpYnV0ZXMsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAncnVuX2NvbXBsZXRlZCcgJiYgJ2V2ZW50RGF0YScgaW4gZGF0YSkge1xuICAgICAgICAgIGNvbnN0IGNvbXBsZXRlZERhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7IG91dHB1dD86IGFueSB9O1xuICAgICAgICAgIC8vIFJldXNlIGN1cnJlbnRSdW4gZnJvbSB2YWxpZGF0aW9uIChhbHJlYWR5IHJlYWQgYWJvdmUpXG4gICAgICAgICAgaWYgKGN1cnJlbnRSdW4pIHtcbiAgICAgICAgICAgIHJ1biA9IGF3YWl0IHdyaXRlUnVuVW5kZXJMaWZlY3ljbGVMb2NrKFxuICAgICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgICBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgICAgdGFnLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgcnVuSWQ6IGN1cnJlbnRSdW4ucnVuSWQsXG4gICAgICAgICAgICAgICAgZGVwbG95bWVudElkOiBjdXJyZW50UnVuLmRlcGxveW1lbnRJZCxcbiAgICAgICAgICAgICAgICB3b3JrZmxvd05hbWU6IGN1cnJlbnRSdW4ud29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBjdXJyZW50UnVuLnNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgIGV4ZWN1dGlvbkNvbnRleHQ6IGN1cnJlbnRSdW4uZXhlY3V0aW9uQ29udGV4dCxcbiAgICAgICAgICAgICAgICBpbnB1dDogY3VycmVudFJ1bi5pbnB1dCxcbiAgICAgICAgICAgICAgICBjcmVhdGVkQXQ6IGN1cnJlbnRSdW4uY3JlYXRlZEF0LFxuICAgICAgICAgICAgICAgIGV4cGlyZWRBdDogY3VycmVudFJ1bi5leHBpcmVkQXQsXG4gICAgICAgICAgICAgICAgc3RhcnRlZEF0OiBjdXJyZW50UnVuLnN0YXJ0ZWRBdCxcbiAgICAgICAgICAgICAgICBzdGF0dXM6ICdjb21wbGV0ZWQnLFxuICAgICAgICAgICAgICAgIG91dHB1dDogY29tcGxldGVkRGF0YS5vdXRwdXQsXG4gICAgICAgICAgICAgICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgIGF0dHJpYnV0ZXM6IGN1cnJlbnRSdW4uYXR0cmlidXRlcyxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgICAgZGVsZXRlQWxsSG9va3NGb3JSdW4oYmFzZWRpciwgZWZmZWN0aXZlUnVuSWQpLFxuICAgICAgICAgICAgICBkZWxldGVBbGxXYWl0c0ZvclJ1bihiYXNlZGlyLCBlZmZlY3RpdmVSdW5JZCksXG4gICAgICAgICAgICBdKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdydW5fZmFpbGVkJyAmJiAnZXZlbnREYXRhJyBpbiBkYXRhKSB7XG4gICAgICAgICAgY29uc3QgZmFpbGVkRGF0YSA9IGRhdGEuZXZlbnREYXRhIGFzIHtcbiAgICAgICAgICAgIGVycm9yOiB1bmtub3duO1xuICAgICAgICAgICAgZXJyb3JDb2RlPzogc3RyaW5nO1xuICAgICAgICAgIH07XG4gICAgICAgICAgLy8gUmV1c2UgY3VycmVudFJ1biBmcm9tIHZhbGlkYXRpb24gKGFscmVhZHkgcmVhZCBhYm92ZSlcbiAgICAgICAgICBpZiAoY3VycmVudFJ1bikge1xuICAgICAgICAgICAgLy8gVGhlIGVycm9yIGZpZWxkIGlzIFNlcmlhbGl6ZWREYXRhIChVaW50OEFycmF5KSBwcm9kdWNlZCBieVxuICAgICAgICAgICAgLy8gZGVoeWRyYXRlUnVuRXJyb3IuIFdlIHN0b3JlIGl0IHZlcmJhdGltIOKAlCBjb25zdW1lcnMgaHlkcmF0ZSBpdFxuICAgICAgICAgICAgLy8gdmlhIGh5ZHJhdGVSdW5FcnJvciB0byByZWNvbnN0cnVjdCB0aGUgb3JpZ2luYWwgdGhyb3duIHZhbHVlLlxuICAgICAgICAgICAgcnVuID0gYXdhaXQgd3JpdGVSdW5VbmRlckxpZmVjeWNsZUxvY2soXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICB0YWcsXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBydW5JZDogY3VycmVudFJ1bi5ydW5JZCxcbiAgICAgICAgICAgICAgICBkZXBsb3ltZW50SWQ6IGN1cnJlbnRSdW4uZGVwbG95bWVudElkLFxuICAgICAgICAgICAgICAgIHdvcmtmbG93TmFtZTogY3VycmVudFJ1bi53b3JrZmxvd05hbWUsXG4gICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGN1cnJlbnRSdW4uc3BlY1ZlcnNpb24sXG4gICAgICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dDogY3VycmVudFJ1bi5leGVjdXRpb25Db250ZXh0LFxuICAgICAgICAgICAgICAgIGlucHV0OiBjdXJyZW50UnVuLmlucHV0LFxuICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDogY3VycmVudFJ1bi5jcmVhdGVkQXQsXG4gICAgICAgICAgICAgICAgZXhwaXJlZEF0OiBjdXJyZW50UnVuLmV4cGlyZWRBdCxcbiAgICAgICAgICAgICAgICBzdGFydGVkQXQ6IGN1cnJlbnRSdW4uc3RhcnRlZEF0LFxuICAgICAgICAgICAgICAgIHN0YXR1czogJ2ZhaWxlZCcsXG4gICAgICAgICAgICAgICAgb3V0cHV0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgZXJyb3I6IGZhaWxlZERhdGEuZXJyb3IgYXMgVWludDhBcnJheSxcbiAgICAgICAgICAgICAgICBlcnJvckNvZGU6IGZhaWxlZERhdGEuZXJyb3JDb2RlLFxuICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICAgICAgYXR0cmlidXRlczogY3VycmVudFJ1bi5hdHRyaWJ1dGVzLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgICAgICAgICBkZWxldGVBbGxIb29rc0ZvclJ1bihiYXNlZGlyLCBlZmZlY3RpdmVSdW5JZCksXG4gICAgICAgICAgICAgIGRlbGV0ZUFsbFdhaXRzRm9yUnVuKGJhc2VkaXIsIGVmZmVjdGl2ZVJ1bklkKSxcbiAgICAgICAgICAgIF0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9jYW5jZWxsZWQnKSB7XG4gICAgICAgICAgLy8gUmV1c2UgY3VycmVudFJ1biBmcm9tIHZhbGlkYXRpb24gKGFscmVhZHkgcmVhZCBhYm92ZSlcbiAgICAgICAgICBpZiAoY3VycmVudFJ1bikge1xuICAgICAgICAgICAgcnVuID0gYXdhaXQgd3JpdGVSdW5VbmRlckxpZmVjeWNsZUxvY2soXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgIGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICB0YWcsXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBydW5JZDogY3VycmVudFJ1bi5ydW5JZCxcbiAgICAgICAgICAgICAgICBkZXBsb3ltZW50SWQ6IGN1cnJlbnRSdW4uZGVwbG95bWVudElkLFxuICAgICAgICAgICAgICAgIHdvcmtmbG93TmFtZTogY3VycmVudFJ1bi53b3JrZmxvd05hbWUsXG4gICAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGN1cnJlbnRSdW4uc3BlY1ZlcnNpb24sXG4gICAgICAgICAgICAgICAgZXhlY3V0aW9uQ29udGV4dDogY3VycmVudFJ1bi5leGVjdXRpb25Db250ZXh0LFxuICAgICAgICAgICAgICAgIGlucHV0OiBjdXJyZW50UnVuLmlucHV0LFxuICAgICAgICAgICAgICAgIGNyZWF0ZWRBdDogY3VycmVudFJ1bi5jcmVhdGVkQXQsXG4gICAgICAgICAgICAgICAgZXhwaXJlZEF0OiBjdXJyZW50UnVuLmV4cGlyZWRBdCxcbiAgICAgICAgICAgICAgICBzdGFydGVkQXQ6IGN1cnJlbnRSdW4uc3RhcnRlZEF0LFxuICAgICAgICAgICAgICAgIHN0YXR1czogJ2NhbmNlbGxlZCcsXG4gICAgICAgICAgICAgICAgb3V0cHV0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICBjb21wbGV0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgICAgIGF0dHJpYnV0ZXM6IGN1cnJlbnRSdW4uYXR0cmlidXRlcyxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgICAgZGVsZXRlQWxsSG9va3NGb3JSdW4oYmFzZWRpciwgZWZmZWN0aXZlUnVuSWQpLFxuICAgICAgICAgICAgICBkZWxldGVBbGxXYWl0c0ZvclJ1bihiYXNlZGlyLCBlZmZlY3RpdmVSdW5JZCksXG4gICAgICAgICAgICBdKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgLy8gU3RlcCBsaWZlY3ljbGUgZXZlbnRzXG4gICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX2NyZWF0ZWQnICYmXG4gICAgICAgICAgJ2V2ZW50RGF0YScgaW4gZGF0YVxuICAgICAgICApIHtcbiAgICAgICAgICAvLyBzdGVwX2NyZWF0ZWQ6IENyZWF0ZXMgc3RlcCBlbnRpdHkgd2l0aCBzdGF0dXMgJ3BlbmRpbmcnLCBhdHRlbXB0PTAsIGNyZWF0ZWRBdCBzZXQuXG4gICAgICAgICAgLy8gVHdvIGNvbmN1cnJlbnQgaW52b2NhdGlvbnMgd2l0aCBpZGVudGljYWwgY29ycmVsYXRpb25JZHMgKGUuZy4gdGhlXG4gICAgICAgICAgLy8gc25hcHNob3QgcnVudGltZSdzIGRldGVybWluaXN0aWMgY29ycmVsYXRpb25JZHMgYWNyb3NzIHJlcGxheXMpXG4gICAgICAgICAgLy8gbXVzdCBiZSBkZWR1cGVkIOKAlCBvdGhlcndpc2UgYm90aCB3cml0ZXMgc3VjY2VlZCBhbmQgdGhlIGV2ZW50IGxvZ1xuICAgICAgICAgIC8vIGVuZHMgdXAgd2l0aCBkdXBsaWNhdGUgc3RlcF9jcmVhdGVkIGVudHJpZXMuIFRoZSBvdXRlclxuICAgICAgICAgIC8vIHdpdGhTdGVwTG9jayBtdXRleCBzZXJpYWxpemVzIHdpdGhpbiBhIHNpbmdsZSBwcm9jZXNzOyB0aGlzXG4gICAgICAgICAgLy8gT19DUkVBVHxPX0VYQ0wgY29uc3RyYWludCBmaWxlIGFkZGl0aW9uYWxseSBwcm90ZWN0cyBhZ2FpbnN0XG4gICAgICAgICAgLy8gY3Jvc3MtcHJvY2VzcyByYWNlcyAodHdvIHBucG0gd29ya2VycywgcmVkZWxpdmVyZWQgcXVldWVcbiAgICAgICAgICAvLyBtZXNzYWdlcywgZXRjLikuIFRoZSBsb3NlciB0aHJvd3MgRW50aXR5Q29uZmxpY3RFcnJvciBzbyB0aGVcbiAgICAgICAgICAvLyBydW50aW1lJ3MgZXhpc3RpbmcgY2F0Y2ggcGF0aCBjYW4gc3dhbGxvdyBpdCBhbmQgYXZvaWRcbiAgICAgICAgICAvLyBkb3VibGUtcXVldWluZyB0aGUgc3RlcC5cbiAgICAgICAgICBjb25zdCBzdGVwQ3JlYXRlZExvY2tOYW1lID0gdGFnXG4gICAgICAgICAgICA/IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH0uY3JlYXRlZC4ke3RhZ31gXG4gICAgICAgICAgICA6IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH0uY3JlYXRlZGA7XG4gICAgICAgICAgY29uc3Qgc3RlcENyZWF0ZWRMb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICcubG9ja3MnLFxuICAgICAgICAgICAgJ3N0ZXBzJyxcbiAgICAgICAgICAgIHN0ZXBDcmVhdGVkTG9ja05hbWVcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnN0IHN0ZXBDcmVhdGVkQ2xhaW1lZCA9IGF3YWl0IHdyaXRlRXhjbHVzaXZlKFxuICAgICAgICAgICAgc3RlcENyZWF0ZWRMb2NrUGF0aCxcbiAgICAgICAgICAgICcnXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoIXN0ZXBDcmVhdGVkQ2xhaW1lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBTdGVwIFwiJHtkYXRhLmNvcnJlbGF0aW9uSWR9XCIgYWxyZWFkeSBjcmVhdGVkYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3Qgc3RlcERhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7XG4gICAgICAgICAgICBzdGVwTmFtZTogc3RyaW5nO1xuICAgICAgICAgICAgaW5wdXQ6IGFueTtcbiAgICAgICAgICB9O1xuICAgICAgICAgIHN0ZXAgPSB7XG4gICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICBzdGVwSWQ6IGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIHN0ZXBOYW1lOiBzdGVwRGF0YS5zdGVwTmFtZSxcbiAgICAgICAgICAgIHN0YXR1czogJ3BlbmRpbmcnLFxuICAgICAgICAgICAgaW5wdXQ6IHN0ZXBEYXRhLmlucHV0LFxuICAgICAgICAgICAgb3V0cHV0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICBlcnJvcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgYXR0ZW1wdDogMCxcbiAgICAgICAgICAgIHN0YXJ0ZWRBdDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgY29tcGxldGVkQXQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICAvLyBQcm9wYWdhdGUgc3BlY1ZlcnNpb24gZnJvbSB0aGUgZXZlbnQgdG8gdGhlIHN0ZXAgZW50aXR5XG4gICAgICAgICAgICBzcGVjVmVyc2lvbjogZWZmZWN0aXZlU3BlY1ZlcnNpb24sXG4gICAgICAgICAgfTtcbiAgICAgICAgICBjb25zdCBzdGVwQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnc3RlcHMnLCBzdGVwQ29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgc3RlcFxuICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICdzdGVwX3N0YXJ0ZWQnKSB7XG4gICAgICAgICAgLy8gc3RlcF9zdGFydGVkOiBJbmNyZW1lbnRzIGF0dGVtcHQsIHNldHMgc3RhdHVzIHRvICdydW5uaW5nJ1xuICAgICAgICAgIC8vIFNldHMgc3RhcnRlZEF0IG9ubHkgb24gdGhlIGZpcnN0IHN0YXJ0IChub3QgdXBkYXRlZCBvbiByZXRyaWVzKVxuICAgICAgICAgIC8vIFJldXNlIHZhbGlkYXRlZFN0ZXAgZnJvbSB2YWxpZGF0aW9uIChhbHJlYWR5IHJlYWQgYWJvdmUpXG4gICAgICAgICAgaWYgKHZhbGlkYXRlZFN0ZXApIHtcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHJldHJ5QWZ0ZXIgdGltZXN0YW1wIGhhc24ndCBiZWVuIHJlYWNoZWQgeWV0XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIHZhbGlkYXRlZFN0ZXAucmV0cnlBZnRlciAmJlxuICAgICAgICAgICAgICB2YWxpZGF0ZWRTdGVwLnJldHJ5QWZ0ZXIuZ2V0VGltZSgpID4gRGF0ZS5ub3coKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBUb29FYXJseUVycm9yKFxuICAgICAgICAgICAgICAgIGBDYW5ub3Qgc3RhcnQgc3RlcCBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiOiByZXRyeUFmdGVyIHRpbWVzdGFtcCBoYXMgbm90IGJlZW4gcmVhY2hlZCB5ZXRgLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIHJldHJ5QWZ0ZXI6IE1hdGguY2VpbChcbiAgICAgICAgICAgICAgICAgICAgKHZhbGlkYXRlZFN0ZXAucmV0cnlBZnRlci5nZXRUaW1lKCkgLSBEYXRlLm5vdygpKSAvIDEwMDBcbiAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBCZXN0LWVmZm9ydCBndWFyZDogcmUtcmVhZCB0aGUgc3RlcCBlbnRpdHkgdG8gY2hlY2sgaWYgaXRcbiAgICAgICAgICAgIC8vIHJlYWNoZWQgdGVybWluYWwgc3RhdGUgYmV0d2VlbiB0aGUgdmFsaWRhdGlvbiByZWFkIGFuZCBub3cuXG4gICAgICAgICAgICAvLyBUaGlzIG5hcnJvd3MgdGhlIFRPQ1RPVSB3aW5kb3cgYnV0IGRvZXMgbm90IGZ1bGx5IGVsaW1pbmF0ZSBpdFxuICAgICAgICAgICAgLy8gKHRoZSBsb2NhbCB3b3JsZCBpcyBzaW5nbGUtcHJvY2VzcyAvIGRldi1vbmx5OyB0aGUgcG9zdGdyZXNcbiAgICAgICAgICAgIC8vIHdvcmxkIHVzZXMgU1FMLWxldmVsIGF0b21pYyBndWFyZHMgZm9yIHByb2R1Y3Rpb24pLlxuICAgICAgICAgICAgY29uc3Qgc3RlcENvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH1gO1xuICAgICAgICAgICAgY29uc3QgZnJlc2hTdGVwID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICdzdGVwcycsXG4gICAgICAgICAgICAgIHN0ZXBDb21wb3NpdGVLZXksXG4gICAgICAgICAgICAgIFN0ZXBTY2hlbWEsXG4gICAgICAgICAgICAgIHRhZ1xuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGlmIChmcmVzaFN0ZXAgJiYgaXNTdGVwVGVybWluYWwoZnJlc2hTdGVwLnN0YXR1cykpIHtcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgICAgYENhbm5vdCBtb2RpZnkgc3RlcCBpbiB0ZXJtaW5hbCBzdGF0ZSBcIiR7ZnJlc2hTdGVwLnN0YXR1c31cImBcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc3RlcCA9IHtcbiAgICAgICAgICAgICAgLi4udmFsaWRhdGVkU3RlcCxcbiAgICAgICAgICAgICAgc3RhdHVzOiAncnVubmluZycsXG4gICAgICAgICAgICAgIC8vIE9ubHkgc2V0IHN0YXJ0ZWRBdCBvbiB0aGUgZmlyc3Qgc3RhcnRcbiAgICAgICAgICAgICAgc3RhcnRlZEF0OiB2YWxpZGF0ZWRTdGVwLnN0YXJ0ZWRBdCA/PyBub3csXG4gICAgICAgICAgICAgIC8vIEluY3JlbWVudCBhdHRlbXB0IGNvdW50ZXIgb24gZXZlcnkgc3RhcnRcbiAgICAgICAgICAgICAgYXR0ZW1wdDogdmFsaWRhdGVkU3RlcC5hdHRlbXB0ICsgMSxcbiAgICAgICAgICAgICAgLy8gQ2xlYXIgcmV0cnlBZnRlciBub3cgdGhhdCB0aGUgc3RlcCBoYXMgc3RhcnRlZFxuICAgICAgICAgICAgICByZXRyeUFmdGVyOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnc3RlcHMnLCBzdGVwQ29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgICBzdGVwLFxuICAgICAgICAgICAgICB7IG92ZXJ3cml0ZTogdHJ1ZSB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfY29tcGxldGVkJyAmJiAnZXZlbnREYXRhJyBpbiBkYXRhKSB7XG4gICAgICAgICAgLy8gc3RlcF9jb21wbGV0ZWQ6IFRlcm1pbmFsIHN0YXRlIHdpdGggb3V0cHV0XG4gICAgICAgICAgLy8gVXNlcyB3cml0ZUV4Y2x1c2l2ZSBvbiBhIGxvY2sgZmlsZSB0byBhdG9taWNhbGx5IHByZXZlbnQgY29uY3VycmVudFxuICAgICAgICAgIC8vIGludm9jYXRpb25zIGZyb20gYm90aCBjb21wbGV0aW5nIHRoZSBzYW1lIHN0ZXAgKFRPQ1RPVSByYWNlKS5cbiAgICAgICAgICBjb25zdCBjb21wbGV0ZWREYXRhID0gZGF0YS5ldmVudERhdGEgYXMgeyByZXN1bHQ6IGFueSB9O1xuICAgICAgICAgIGlmICh2YWxpZGF0ZWRTdGVwKSB7XG4gICAgICAgICAgICBjb25zdCBzdGVwQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgICBjb25zdCBsb2NrTmFtZSA9IHRhZ1xuICAgICAgICAgICAgICA/IGAke3N0ZXBDb21wb3NpdGVLZXl9LnRlcm1pbmFsLiR7dGFnfWBcbiAgICAgICAgICAgICAgOiBgJHtzdGVwQ29tcG9zaXRlS2V5fS50ZXJtaW5hbGA7XG4gICAgICAgICAgICBjb25zdCB0ZXJtaW5hbExvY2tQYXRoID0gcmVzb2x2ZVdpdGhpbkJhc2UoXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICcubG9ja3MnLFxuICAgICAgICAgICAgICAnc3RlcHMnLFxuICAgICAgICAgICAgICBsb2NrTmFtZVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGNvbnN0IGNsYWltZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZSh0ZXJtaW5hbExvY2tQYXRoLCAnJyk7XG4gICAgICAgICAgICBpZiAoIWNsYWltZWQpIHtcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgICAgJ0Nhbm5vdCBtb2RpZnkgc3RlcCBpbiB0ZXJtaW5hbCBzdGF0ZSdcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHN0ZXAgPSB7XG4gICAgICAgICAgICAgIC4uLnZhbGlkYXRlZFN0ZXAsXG4gICAgICAgICAgICAgIHN0YXR1czogJ2NvbXBsZXRlZCcsXG4gICAgICAgICAgICAgIG91dHB1dDogY29tcGxldGVkRGF0YS5yZXN1bHQsXG4gICAgICAgICAgICAgIGNvbXBsZXRlZEF0OiBub3csXG4gICAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnc3RlcHMnLCBzdGVwQ29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgICBzdGVwLFxuICAgICAgICAgICAgICB7IG92ZXJ3cml0ZTogdHJ1ZSB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3N0ZXBfZmFpbGVkJyAmJiAnZXZlbnREYXRhJyBpbiBkYXRhKSB7XG4gICAgICAgICAgLy8gc3RlcF9mYWlsZWQ6IFRlcm1pbmFsIHN0YXRlIHdpdGggZXJyb3JcbiAgICAgICAgICAvLyBVc2VzIHdyaXRlRXhjbHVzaXZlIG9uIGEgbG9jayBmaWxlIHRvIGF0b21pY2FsbHkgcHJldmVudCBjb25jdXJyZW50XG4gICAgICAgICAgLy8gaW52b2NhdGlvbnMgZnJvbSBib3RoIGZhaWxpbmcgdGhlIHNhbWUgc3RlcCAoVE9DVE9VIHJhY2UpLlxuICAgICAgICAgIGNvbnN0IGZhaWxlZERhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7XG4gICAgICAgICAgICBlcnJvcjogdW5rbm93bjtcbiAgICAgICAgICB9O1xuICAgICAgICAgIGlmICh2YWxpZGF0ZWRTdGVwKSB7XG4gICAgICAgICAgICBjb25zdCBzdGVwQ29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgICBjb25zdCBsb2NrTmFtZSA9IHRhZ1xuICAgICAgICAgICAgICA/IGAke3N0ZXBDb21wb3NpdGVLZXl9LnRlcm1pbmFsLiR7dGFnfWBcbiAgICAgICAgICAgICAgOiBgJHtzdGVwQ29tcG9zaXRlS2V5fS50ZXJtaW5hbGA7XG4gICAgICAgICAgICBjb25zdCB0ZXJtaW5hbExvY2tQYXRoID0gcmVzb2x2ZVdpdGhpbkJhc2UoXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICcubG9ja3MnLFxuICAgICAgICAgICAgICAnc3RlcHMnLFxuICAgICAgICAgICAgICBsb2NrTmFtZVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGNvbnN0IGNsYWltZWQgPSBhd2FpdCB3cml0ZUV4Y2x1c2l2ZSh0ZXJtaW5hbExvY2tQYXRoLCAnJyk7XG4gICAgICAgICAgICBpZiAoIWNsYWltZWQpIHtcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgICAgJ0Nhbm5vdCBtb2RpZnkgc3RlcCBpbiB0ZXJtaW5hbCBzdGF0ZSdcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFRoZSBlcnJvciBmaWVsZCBpcyBTZXJpYWxpemVkRGF0YSAoVWludDhBcnJheSkgcHJvZHVjZWQgYnlcbiAgICAgICAgICAgIC8vIGRlaHlkcmF0ZVN0ZXBFcnJvci4gV2Ugc3RvcmUgaXQgdmVyYmF0aW0g4oCUIGNvbnN1bWVycyBoeWRyYXRlIGl0XG4gICAgICAgICAgICAvLyB2aWEgaHlkcmF0ZVN0ZXBFcnJvciB0byByZWNvbnN0cnVjdCB0aGUgb3JpZ2luYWwgdGhyb3duIHZhbHVlLlxuICAgICAgICAgICAgc3RlcCA9IHtcbiAgICAgICAgICAgICAgLi4udmFsaWRhdGVkU3RlcCxcbiAgICAgICAgICAgICAgc3RhdHVzOiAnZmFpbGVkJyxcbiAgICAgICAgICAgICAgZXJyb3I6IGZhaWxlZERhdGEuZXJyb3IgYXMgVWludDhBcnJheSxcbiAgICAgICAgICAgICAgY29tcGxldGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdzdGVwcycsIHN0ZXBDb21wb3NpdGVLZXksIHRhZyksXG4gICAgICAgICAgICAgIHN0ZXAsXG4gICAgICAgICAgICAgIHsgb3ZlcndyaXRlOiB0cnVlIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnRUeXBlID09PSAnc3RlcF9yZXRyeWluZycgJiYgJ2V2ZW50RGF0YScgaW4gZGF0YSkge1xuICAgICAgICAgIC8vIHN0ZXBfcmV0cnlpbmc6IFNldHMgc3RhdHVzIGJhY2sgdG8gJ3BlbmRpbmcnLCByZWNvcmRzIGVycm9yXG4gICAgICAgICAgLy8gUmV1c2UgdmFsaWRhdGVkU3RlcCBmcm9tIHZhbGlkYXRpb24gKGFscmVhZHkgcmVhZCBhYm92ZSlcbiAgICAgICAgICBjb25zdCByZXRyeURhdGEgPSBkYXRhLmV2ZW50RGF0YSBhcyB7XG4gICAgICAgICAgICBlcnJvcjogdW5rbm93bjtcbiAgICAgICAgICAgIHJldHJ5QWZ0ZXI/OiBEYXRlO1xuICAgICAgICAgIH07XG4gICAgICAgICAgaWYgKHZhbGlkYXRlZFN0ZXApIHtcbiAgICAgICAgICAgIGNvbnN0IHN0ZXBDb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtkYXRhLmNvcnJlbGF0aW9uSWR9YDtcbiAgICAgICAgICAgIHN0ZXAgPSB7XG4gICAgICAgICAgICAgIC4uLnZhbGlkYXRlZFN0ZXAsXG4gICAgICAgICAgICAgIHN0YXR1czogJ3BlbmRpbmcnLFxuICAgICAgICAgICAgICBlcnJvcjogcmV0cnlEYXRhLmVycm9yIGFzIFVpbnQ4QXJyYXksXG4gICAgICAgICAgICAgIHJldHJ5QWZ0ZXI6IHJldHJ5RGF0YS5yZXRyeUFmdGVyLFxuICAgICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3N0ZXBzJywgc3RlcENvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgc3RlcCxcbiAgICAgICAgICAgICAgeyBvdmVyd3JpdGU6IHRydWUgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgLy8gSG9vayBsaWZlY3ljbGUgZXZlbnRzXG4gICAgICAgICAgZGF0YS5ldmVudFR5cGUgPT09ICdob29rX2NyZWF0ZWQnICYmXG4gICAgICAgICAgJ2V2ZW50RGF0YScgaW4gZGF0YVxuICAgICAgICApIHtcbiAgICAgICAgICBjb25zdCBob29rRGF0YSA9IGRhdGEuZXZlbnREYXRhIGFzIHtcbiAgICAgICAgICAgIHRva2VuOiBzdHJpbmc7XG4gICAgICAgICAgICBtZXRhZGF0YT86IGFueTtcbiAgICAgICAgICAgIGlzV2ViaG9vaz86IGJvb2xlYW47XG4gICAgICAgICAgICBpc1N5c3RlbT86IGJvb2xlYW47XG4gICAgICAgICAgfTtcblxuICAgICAgICAgIC8vIEF0b21pY2FsbHkgY2xhaW0gdGhlIHRva2VuIHVzaW5nIGFuIGV4Y2x1c2l2ZS1jcmVhdGUgY29uc3RyYWludCBmaWxlLlxuICAgICAgICAgIC8vIFRoaXMgYXZvaWRzIHRoZSBUT0NUT1UgcmFjZSBvZiB0aGUgcHJldmlvdXMgcmVhZC1hbGwtdGhlbi1jaGVjayBhcHByb2FjaC5cbiAgICAgICAgICBjb25zdCBjb25zdHJhaW50UGF0aCA9IHBhdGguam9pbihcbiAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAnaG9va3MnLFxuICAgICAgICAgICAgJ3Rva2VucycsXG4gICAgICAgICAgICBgJHtoYXNoVG9rZW4oaG9va0RhdGEudG9rZW4pfS5qc29uYFxuICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3QgdG9rZW5DbGFpbWVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUoXG4gICAgICAgICAgICBjb25zdHJhaW50UGF0aCxcbiAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICAgICAgdG9rZW46IGhvb2tEYXRhLnRva2VuLFxuICAgICAgICAgICAgICBob29rSWQ6IGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgcnVuSWQ6IGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgaWYgKCF0b2tlbkNsYWltZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nQ2xhaW0gPSBhd2FpdCByZWFkSG9va1Rva2VuQ2xhaW0oY29uc3RyYWludFBhdGgpO1xuXG4gICAgICAgICAgICAvLyBDcmVhdGUgaG9va19jb25mbGljdCBldmVudCBpbnN0ZWFkIG9mIGhvb2tfY3JlYXRlZFxuICAgICAgICAgICAgLy8gVGhpcyBhbGxvd3MgdGhlIHdvcmtmbG93IHRvIGNvbnRpbnVlIGFuZCBmYWlsIGdyYWNlZnVsbHkgd2hlbiB0aGUgaG9vayBpcyBhd2FpdGVkXG4gICAgICAgICAgICBjb25zdCBjb25mbGljdEV2ZW50OiBFdmVudCA9IHtcbiAgICAgICAgICAgICAgZXZlbnRUeXBlOiAnaG9va19jb25mbGljdCcsXG4gICAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IGRhdGEuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgdG9rZW46IGhvb2tEYXRhLnRva2VuLFxuICAgICAgICAgICAgICAgIC4uLihleGlzdGluZ0NsYWltXG4gICAgICAgICAgICAgICAgICA/IHsgY29uZmxpY3RpbmdSdW5JZDogZXhpc3RpbmdDbGFpbS5ydW5JZCB9XG4gICAgICAgICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgcnVuSWQ6IGVmZmVjdGl2ZVJ1bklkLFxuICAgICAgICAgICAgICBldmVudElkLFxuICAgICAgICAgICAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8gU3RvcmUgdGhlIGNvbmZsaWN0IGV2ZW50XG4gICAgICAgICAgICBjb25zdCBjb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtldmVudElkfWA7XG4gICAgICAgICAgICBhd2FpdCB3cml0ZUpTT04oXG4gICAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ2V2ZW50cycsIGNvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgICAgY29uZmxpY3RFdmVudFxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgY29uc3QgcmVzb2x2ZURhdGEgPVxuICAgICAgICAgICAgICBwYXJhbXM/LnJlc29sdmVEYXRhID8/IERFRkFVTFRfUkVTT0xWRV9EQVRBX09QVElPTjtcbiAgICAgICAgICAgIGNvbnN0IGZpbHRlcmVkRXZlbnQgPSBzdHJpcEV2ZW50RGF0YVJlZnMoXG4gICAgICAgICAgICAgIGNvbmZsaWN0RXZlbnQsXG4gICAgICAgICAgICAgIHJlc29sdmVEYXRhXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAvLyBSZXR1cm4gRXZlbnRSZXN1bHQgd2l0aCBjb25mbGljdCBldmVudCAobm8gaG9vayBlbnRpdHkgY3JlYXRlZClcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIGV2ZW50OiBmaWx0ZXJlZEV2ZW50LFxuICAgICAgICAgICAgICBydW4sXG4gICAgICAgICAgICAgIHN0ZXAsXG4gICAgICAgICAgICAgIGhvb2s6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaG9vayA9IHtcbiAgICAgICAgICAgIHJ1bklkOiBlZmZlY3RpdmVSdW5JZCxcbiAgICAgICAgICAgIGhvb2tJZDogZGF0YS5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgdG9rZW46IGhvb2tEYXRhLnRva2VuLFxuICAgICAgICAgICAgbWV0YWRhdGE6IGhvb2tEYXRhLm1ldGFkYXRhLFxuICAgICAgICAgICAgb3duZXJJZDogJ2xvY2FsLW93bmVyJyxcbiAgICAgICAgICAgIHByb2plY3RJZDogJ2xvY2FsLXByb2plY3QnLFxuICAgICAgICAgICAgZW52aXJvbm1lbnQ6ICdsb2NhbCcsXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIC8vIFByb3BhZ2F0ZSBzcGVjVmVyc2lvbiBmcm9tIHRoZSBldmVudCB0byB0aGUgaG9vayBlbnRpdHlcbiAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBlZmZlY3RpdmVTcGVjVmVyc2lvbixcbiAgICAgICAgICAgIGlzV2ViaG9vazogaG9va0RhdGEuaXNXZWJob29rID8/IGZhbHNlLFxuICAgICAgICAgICAgaXNTeXN0ZW06IGhvb2tEYXRhLmlzU3lzdGVtID8/IGZhbHNlLFxuICAgICAgICAgIH07XG4gICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnaG9va3MnLCBkYXRhLmNvcnJlbGF0aW9uSWQsIHRhZyksXG4gICAgICAgICAgICBob29rXG4gICAgICAgICAgKTtcbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ2hvb2tfZGlzcG9zZWQnKSB7XG4gICAgICAgICAgLy8gaG9va19kaXNwb3NlZDogRGVsZXRlcyBob29rIGVudGl0eSwgcmVqZWN0cyBkdXBsaWNhdGVzLlxuICAgICAgICAgIC8vIFVzZXMgd3JpdGVFeGNsdXNpdmUgb24gYSBsb2NrIGZpbGUgdG8gYXRvbWljYWxseSBwcmV2ZW50IGNvbmN1cnJlbnRcbiAgICAgICAgICAvLyBpbnZvY2F0aW9ucyBmcm9tIGJvdGggZGlzcG9zaW5nIHRoZSBzYW1lIGhvb2sgKFRPQ1RPVSByYWNlKS5cbiAgICAgICAgICBjb25zdCBob29rTG9ja05hbWUgPSB0YWdcbiAgICAgICAgICAgID8gYCR7ZGF0YS5jb3JyZWxhdGlvbklkfS5kaXNwb3NlZC4ke3RhZ31gXG4gICAgICAgICAgICA6IGAke2RhdGEuY29ycmVsYXRpb25JZH0uZGlzcG9zZWRgO1xuICAgICAgICAgIGNvbnN0IGxvY2tQYXRoID0gcmVzb2x2ZVdpdGhpbkJhc2UoXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJy5sb2NrcycsXG4gICAgICAgICAgICAnaG9va3MnLFxuICAgICAgICAgICAgaG9va0xvY2tOYW1lXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBjbGFpbWVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUobG9ja1BhdGgsICcnKTtcbiAgICAgICAgICBpZiAoIWNsYWltZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICBgSG9vayBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIGFscmVhZHkgZGlzcG9zZWRgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBSZWFkIHRoZSBob29rIHRvIGdldCBpdHMgdG9rZW4gYmVmb3JlIGRlbGV0aW5nXG4gICAgICAgICAgY29uc3QgaG9va1BhdGggPSB0YWdnZWRQYXRoKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICdob29rcycsXG4gICAgICAgICAgICBkYXRhLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICB0YWdcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnN0IGV4aXN0aW5nSG9vayA9IGF3YWl0IHJlYWRKU09OV2l0aEZhbGxiYWNrKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICdob29rcycsXG4gICAgICAgICAgICBkYXRhLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICBIb29rU2NoZW1hLFxuICAgICAgICAgICAgdGFnXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoZXhpc3RpbmdIb29rKSB7XG4gICAgICAgICAgICAvLyBEZWxldGUgdGhlIHRva2VuIGNvbnN0cmFpbnQgZmlsZSB0byBmcmVlIHVwIHRoZSB0b2tlbiBmb3IgcmV1c2VcbiAgICAgICAgICAgIGNvbnN0IGRpc3Bvc2VkQ29uc3RyYWludFBhdGggPSBwYXRoLmpvaW4oXG4gICAgICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgICAgICdob29rcycsXG4gICAgICAgICAgICAgICd0b2tlbnMnLFxuICAgICAgICAgICAgICBgJHtoYXNoVG9rZW4oZXhpc3RpbmdIb29rLnRva2VuKX0uanNvbmBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBhd2FpdCBkZWxldGVKU09OKGRpc3Bvc2VkQ29uc3RyYWludFBhdGgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBhd2FpdCBkZWxldGVKU09OKGhvb2tQYXRoKTtcbiAgICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3dhaXRfY3JlYXRlZCcgJiYgJ2V2ZW50RGF0YScgaW4gZGF0YSkge1xuICAgICAgICAgIC8vIHdhaXRfY3JlYXRlZDogQ3JlYXRlcyB3YWl0IGVudGl0eSB3aXRoIHN0YXR1cyAnd2FpdGluZycuXG4gICAgICAgICAgLy8gQXRvbWljIGNsYWltIG9uIGEgcGVyLShydW5JZCwgY29ycmVsYXRpb25JZCkgY29uc3RyYWludCBmaWxlXG4gICAgICAgICAgLy8gZW5zdXJlcyBkdXBsaWNhdGUgd2FpdF9jcmVhdGVkIGZyb20gY29uY3VycmVudCBpbnZvY2F0aW9uc1xuICAgICAgICAgIC8vIHN1cmZhY2VzIGFzIEVudGl0eUNvbmZsaWN0RXJyb3IgKHJlcGxhY2VzIGEgcHJpb3IgVE9DVE9VXG4gICAgICAgICAgLy8gcmVhZC10aGVuLWNoZWNrIHRoYXQgY291bGQgbGV0IGJvdGggd3JpdGVycyB0aHJvdWdoKS5cbiAgICAgICAgICBjb25zdCB3YWl0Q29tcG9zaXRlS2V5ID0gYCR7ZWZmZWN0aXZlUnVuSWR9LSR7ZGF0YS5jb3JyZWxhdGlvbklkfWA7XG4gICAgICAgICAgY29uc3Qgd2FpdENyZWF0ZWRMb2NrTmFtZSA9IHRhZ1xuICAgICAgICAgICAgPyBgJHt3YWl0Q29tcG9zaXRlS2V5fS5jcmVhdGVkLiR7dGFnfWBcbiAgICAgICAgICAgIDogYCR7d2FpdENvbXBvc2l0ZUtleX0uY3JlYXRlZGA7XG4gICAgICAgICAgY29uc3Qgd2FpdENyZWF0ZWRMb2NrUGF0aCA9IHJlc29sdmVXaXRoaW5CYXNlKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICcubG9ja3MnLFxuICAgICAgICAgICAgJ3dhaXRzJyxcbiAgICAgICAgICAgIHdhaXRDcmVhdGVkTG9ja05hbWVcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnN0IHdhaXRDcmVhdGVkQ2xhaW1lZCA9IGF3YWl0IHdyaXRlRXhjbHVzaXZlKFxuICAgICAgICAgICAgd2FpdENyZWF0ZWRMb2NrUGF0aCxcbiAgICAgICAgICAgICcnXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoIXdhaXRDcmVhdGVkQ2xhaW1lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVudGl0eUNvbmZsaWN0RXJyb3IoXG4gICAgICAgICAgICAgIGBXYWl0IFwiJHtkYXRhLmNvcnJlbGF0aW9uSWR9XCIgYWxyZWFkeSBleGlzdHNgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB3YWl0RGF0YSA9IGRhdGEuZXZlbnREYXRhIGFzIHtcbiAgICAgICAgICAgIHJlc3VtZUF0PzogRGF0ZTtcbiAgICAgICAgICB9O1xuICAgICAgICAgIHdhaXQgPSB7XG4gICAgICAgICAgICB3YWl0SWQ6IHdhaXRDb21wb3NpdGVLZXksXG4gICAgICAgICAgICBydW5JZDogZWZmZWN0aXZlUnVuSWQsXG4gICAgICAgICAgICBzdGF0dXM6ICd3YWl0aW5nJyxcbiAgICAgICAgICAgIHJlc3VtZUF0OiB3YWl0RGF0YS5yZXN1bWVBdCxcbiAgICAgICAgICAgIGNvbXBsZXRlZEF0OiB1bmRlZmluZWQsXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICAgICAgICAgIHVwZGF0ZWRBdDogbm93LFxuICAgICAgICAgICAgc3BlY1ZlcnNpb246IGVmZmVjdGl2ZVNwZWNWZXJzaW9uLFxuICAgICAgICAgIH07XG4gICAgICAgICAgYXdhaXQgd3JpdGVKU09OKFxuICAgICAgICAgICAgdGFnZ2VkUGF0aChiYXNlZGlyLCAnd2FpdHMnLCB3YWl0Q29tcG9zaXRlS2V5LCB0YWcpLFxuICAgICAgICAgICAgd2FpdFxuICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudFR5cGUgPT09ICd3YWl0X2NvbXBsZXRlZCcpIHtcbiAgICAgICAgICAvLyB3YWl0X2NvbXBsZXRlZDogVHJhbnNpdGlvbnMgd2FpdCB0byAnY29tcGxldGVkJywgcmVqZWN0cyBkdXBsaWNhdGVzLlxuICAgICAgICAgIC8vIFVzZXMgd3JpdGVFeGNsdXNpdmUgb24gYSBsb2NrIGZpbGUgdG8gYXRvbWljYWxseSBwcmV2ZW50IGNvbmN1cnJlbnRcbiAgICAgICAgICAvLyBpbnZvY2F0aW9ucyBmcm9tIGJvdGggY29tcGxldGluZyB0aGUgc2FtZSB3YWl0IChUT0NUT1UgcmFjZSkuXG4gICAgICAgICAgY29uc3Qgd2FpdENvbXBvc2l0ZUtleSA9IGAke2VmZmVjdGl2ZVJ1bklkfS0ke2RhdGEuY29ycmVsYXRpb25JZH1gO1xuICAgICAgICAgIGNvbnN0IHdhaXRMb2NrTmFtZSA9IHRhZ1xuICAgICAgICAgICAgPyBgJHt3YWl0Q29tcG9zaXRlS2V5fS5jb21wbGV0ZWQuJHt0YWd9YFxuICAgICAgICAgICAgOiBgJHt3YWl0Q29tcG9zaXRlS2V5fS5jb21wbGV0ZWRgO1xuICAgICAgICAgIGNvbnN0IGxvY2tQYXRoID0gcmVzb2x2ZVdpdGhpbkJhc2UoXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJy5sb2NrcycsXG4gICAgICAgICAgICAnd2FpdHMnLFxuICAgICAgICAgICAgd2FpdExvY2tOYW1lXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBjbGFpbWVkID0gYXdhaXQgd3JpdGVFeGNsdXNpdmUobG9ja1BhdGgsICcnKTtcbiAgICAgICAgICBpZiAoIWNsYWltZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICAgICAgICBgV2FpdCBcIiR7ZGF0YS5jb3JyZWxhdGlvbklkfVwiIGFscmVhZHkgY29tcGxldGVkYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgZXhpc3RpbmdXYWl0ID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgICBiYXNlZGlyLFxuICAgICAgICAgICAgJ3dhaXRzJyxcbiAgICAgICAgICAgIHdhaXRDb21wb3NpdGVLZXksXG4gICAgICAgICAgICBXYWl0U2NoZW1hLFxuICAgICAgICAgICAgdGFnXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAoIWV4aXN0aW5nV2FpdCkge1xuICAgICAgICAgICAgLy8gQ2xlYW4gdXAgdGhlIGxvY2sgZmlsZSB3ZSBqdXN0IGNsYWltZWQg4oCUIHRoZSB3YWl0IGRvZXNuJ3QgZXhpc3RcbiAgICAgICAgICAgIGF3YWl0IGZzLnVubGluayhsb2NrUGF0aCkuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgICAgICAgdGhyb3cgbmV3IFdvcmtmbG93V29ybGRFcnJvcihcbiAgICAgICAgICAgICAgYFdhaXQgXCIke2RhdGEuY29ycmVsYXRpb25JZH1cIiBub3QgZm91bmRgXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBUaGUgbG9jayBmaWxlICh3cml0ZUV4Y2x1c2l2ZSBhYm92ZSkgYWxyZWFkeSBwcmV2ZW50cyBjb25jdXJyZW50XG4gICAgICAgICAgLy8gY29tcGxldGlvbnMg4oCUIG5vIGFkZGl0aW9uYWwgc3RhdHVzIGNoZWNrIG5lZWRlZC5cbiAgICAgICAgICB3YWl0ID0ge1xuICAgICAgICAgICAgLi4uZXhpc3RpbmdXYWl0LFxuICAgICAgICAgICAgc3RhdHVzOiAnY29tcGxldGVkJyxcbiAgICAgICAgICAgIGNvbXBsZXRlZEF0OiBub3csXG4gICAgICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgICAgICB9O1xuICAgICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICAgIHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3dhaXRzJywgd2FpdENvbXBvc2l0ZUtleSwgdGFnKSxcbiAgICAgICAgICAgIHdhaXQsXG4gICAgICAgICAgICB7IG92ZXJ3cml0ZTogdHJ1ZSB9XG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBOb3RlOiBob29rX3JlY2VpdmVkIGV2ZW50cyBhcmUgc3RvcmVkIGluIHRoZSBldmVudCBsb2cgYnV0IGRvbid0XG4gICAgICAgIC8vIG1vZGlmeSB0aGUgSG9vayBlbnRpdHkgKHdoaWNoIGRvZXNuJ3QgaGF2ZSBhIHBheWxvYWQgZmllbGQpXG5cbiAgICAgICAgLy8gU3RvcmUgZXZlbnQgdXNpbmcgY29tcG9zaXRlIGtleSB7cnVuSWR9LXtldmVudElkfVxuICAgICAgICBjb25zdCBjb21wb3NpdGVLZXkgPSBgJHtlZmZlY3RpdmVSdW5JZH0tJHtldmVudElkfWA7XG4gICAgICAgIGF3YWl0IHdyaXRlSlNPTihcbiAgICAgICAgICB0YWdnZWRQYXRoKGJhc2VkaXIsICdldmVudHMnLCBjb21wb3NpdGVLZXksIHRhZyksXG4gICAgICAgICAgZXZlbnRcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCByZXNvbHZlRGF0YSA9IHBhcmFtcz8ucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgICBjb25zdCBmaWx0ZXJlZEV2ZW50ID0gc3RyaXBFdmVudERhdGFSZWZzKGV2ZW50LCByZXNvbHZlRGF0YSk7XG5cbiAgICAgICAgLy8gRm9yIHJ1bl9zdGFydGVkOiBwcmVsb2FkIG9uZSBwYWdlIG9mIGV2ZW50cyBzbyB0aGUgcnVudGltZSBjYW4gc2tpcFxuICAgICAgICAvLyB0aGUgaW5pdGlhbCBldmVudHMubGlzdCBjYWxsIHdoZW4gaGFzTW9yZSBpcyBmYWxzZS5cbiAgICAgICAgbGV0IGV2ZW50czogRXZlbnRbXSB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGN1cnNvcjogc3RyaW5nIHwgbnVsbCB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGhhc01vcmU6IGJvb2xlYW4gfCB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChkYXRhLmV2ZW50VHlwZSA9PT0gJ3J1bl9zdGFydGVkJyAmJiBydW4pIHtcbiAgICAgICAgICBjb25zdCBhbGxFdmVudHMgPSBhd2FpdCBwYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnkoe1xuICAgICAgICAgICAgZGlyZWN0b3J5OiBwYXRoLmpvaW4oYmFzZWRpciwgJ2V2ZW50cycpLFxuICAgICAgICAgICAgc2NoZW1hOiBFdmVudFNjaGVtYSxcbiAgICAgICAgICAgIGZpbGVQcmVmaXg6IGAke2VmZmVjdGl2ZVJ1bklkfS1gLFxuICAgICAgICAgICAgc29ydE9yZGVyOiAnYXNjJyxcbiAgICAgICAgICAgIGxpbWl0OiAxMDAwLFxuICAgICAgICAgICAgZ2V0Q3JlYXRlZEF0OiBnZXRPYmplY3RDcmVhdGVkQXQoJ2V2bnQnKSxcbiAgICAgICAgICAgIGdldElkOiAoZSkgPT4gZS5ldmVudElkLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGV2ZW50cyA9IGFsbEV2ZW50cy5kYXRhO1xuICAgICAgICAgIGN1cnNvciA9IGFsbEV2ZW50cy5jdXJzb3I7XG4gICAgICAgICAgaGFzTW9yZSA9IGFsbEV2ZW50cy5oYXNNb3JlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gUmV0dXJuIEV2ZW50UmVzdWx0IHdpdGggZXZlbnQgYW5kIGFueSBjcmVhdGVkL3VwZGF0ZWQgZW50aXR5XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgZXZlbnQ6IGZpbHRlcmVkRXZlbnQsXG4gICAgICAgICAgcnVuLFxuICAgICAgICAgIHN0ZXAsXG4gICAgICAgICAgaG9vayxcbiAgICAgICAgICB3YWl0LFxuICAgICAgICAgIGV2ZW50cyxcbiAgICAgICAgICBjdXJzb3IsXG4gICAgICAgICAgaGFzTW9yZSxcbiAgICAgICAgfTtcbiAgICAgIH0gLy8gZW5kIGNyZWF0ZUltcGxcbiAgICB9LFxuXG4gICAgYXN5bmMgZ2V0KHJ1bklkLCBldmVudElkLCBwYXJhbXMpIHtcbiAgICAgIGFzc2VydFNhZmVFbnRpdHlJZCgncnVuSWQnLCBydW5JZCk7XG4gICAgICBhc3NlcnRTYWZlRW50aXR5SWQoJ2V2ZW50SWQnLCBldmVudElkKTtcbiAgICAgIGNvbnN0IGNvbXBvc2l0ZUtleSA9IGAke3J1bklkfS0ke2V2ZW50SWR9YDtcbiAgICAgIGNvbnN0IGV2ZW50ID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgIGJhc2VkaXIsXG4gICAgICAgICdldmVudHMnLFxuICAgICAgICBjb21wb3NpdGVLZXksXG4gICAgICAgIEV2ZW50U2NoZW1hLFxuICAgICAgICB0YWdcbiAgICAgICk7XG4gICAgICBpZiAoIWV2ZW50KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXZlbnQgJHtldmVudElkfSBpbiBydW4gJHtydW5JZH0gbm90IGZvdW5kYCk7XG4gICAgICB9XG4gICAgICBjb25zdCByZXNvbHZlRGF0YSA9IHBhcmFtcz8ucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgcmV0dXJuIHN0cmlwRXZlbnREYXRhUmVmcyhldmVudCwgcmVzb2x2ZURhdGEpO1xuICAgIH0sXG5cbiAgICBhc3luYyBsaXN0KHBhcmFtcykge1xuICAgICAgY29uc3QgeyBydW5JZCB9ID0gcGFyYW1zO1xuICAgICAgYXNzZXJ0U2FmZUVudGl0eUlkKCdydW5JZCcsIHJ1bklkKTtcbiAgICAgIGNvbnN0IHJlc29sdmVEYXRhID0gcGFyYW1zLnJlc29sdmVEYXRhID8/IERFRkFVTFRfUkVTT0xWRV9EQVRBX09QVElPTjtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBhZ2luYXRlZEZpbGVTeXN0ZW1RdWVyeSh7XG4gICAgICAgIGRpcmVjdG9yeTogcGF0aC5qb2luKGJhc2VkaXIsICdldmVudHMnKSxcbiAgICAgICAgc2NoZW1hOiBFdmVudFNjaGVtYSxcbiAgICAgICAgZmlsZVByZWZpeDogYCR7cnVuSWR9LWAsXG4gICAgICAgIC8vIEV2ZW50cyBpbiBjaHJvbm9sb2dpY2FsIG9yZGVyIChvbGRlc3QgZmlyc3QpIGJ5IGRlZmF1bHQsXG4gICAgICAgIC8vIGRpZmZlcmVudCBmcm9tIHRoZSBkZWZhdWx0IGZvciBvdGhlciBsaXN0IGNhbGxzLlxuICAgICAgICBzb3J0T3JkZXI6IHBhcmFtcy5wYWdpbmF0aW9uPy5zb3J0T3JkZXIgPz8gJ2FzYycsXG4gICAgICAgIGxpbWl0OiBwYXJhbXMucGFnaW5hdGlvbj8ubGltaXQsXG4gICAgICAgIGN1cnNvcjogcGFyYW1zLnBhZ2luYXRpb24/LmN1cnNvcixcbiAgICAgICAgZ2V0Q3JlYXRlZEF0OiBnZXRPYmplY3RDcmVhdGVkQXQoJ2V2bnQnKSxcbiAgICAgICAgZ2V0SWQ6IChldmVudCkgPT4gZXZlbnQuZXZlbnRJZCxcbiAgICAgIH0pO1xuXG4gICAgICAvLyBJZiByZXNvbHZlRGF0YSBpcyBcIm5vbmVcIiwgcmVtb3ZlIGV2ZW50RGF0YSBmcm9tIGV2ZW50c1xuICAgICAgaWYgKHJlc29sdmVEYXRhID09PSAnbm9uZScpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5yZXN1bHQsXG4gICAgICAgICAgZGF0YTogcmVzdWx0LmRhdGEubWFwKChldmVudCkgPT5cbiAgICAgICAgICAgIHN0cmlwRXZlbnREYXRhUmVmcyhldmVudCwgcmVzb2x2ZURhdGEpXG4gICAgICAgICAgKSxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9LFxuXG4gICAgYXN5bmMgbGlzdEJ5Q29ycmVsYXRpb25JZChwYXJhbXMpIHtcbiAgICAgIGNvbnN0IGNvcnJlbGF0aW9uSWQgPSBwYXJhbXMuY29ycmVsYXRpb25JZDtcbiAgICAgIGFzc2VydFNhZmVFbnRpdHlJZCgnY29ycmVsYXRpb25JZCcsIGNvcnJlbGF0aW9uSWQpO1xuICAgICAgY29uc3QgcmVzb2x2ZURhdGEgPSBwYXJhbXMucmVzb2x2ZURhdGEgPz8gREVGQVVMVF9SRVNPTFZFX0RBVEFfT1BUSU9OO1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcGFnaW5hdGVkRmlsZVN5c3RlbVF1ZXJ5KHtcbiAgICAgICAgZGlyZWN0b3J5OiBwYXRoLmpvaW4oYmFzZWRpciwgJ2V2ZW50cycpLFxuICAgICAgICBzY2hlbWE6IEV2ZW50U2NoZW1hLFxuICAgICAgICAvLyBObyBmaWxlUHJlZml4IC0gc2VhcmNoIGFsbCBldmVudHNcbiAgICAgICAgZmlsdGVyOiAoZXZlbnQpID0+IGV2ZW50LmNvcnJlbGF0aW9uSWQgPT09IGNvcnJlbGF0aW9uSWQsXG4gICAgICAgIC8vIEV2ZW50cyBpbiBjaHJvbm9sb2dpY2FsIG9yZGVyIChvbGRlc3QgZmlyc3QpIGJ5IGRlZmF1bHQsXG4gICAgICAgIC8vIGRpZmZlcmVudCBmcm9tIHRoZSBkZWZhdWx0IGZvciBvdGhlciBsaXN0IGNhbGxzLlxuICAgICAgICBzb3J0T3JkZXI6IHBhcmFtcy5wYWdpbmF0aW9uPy5zb3J0T3JkZXIgPz8gJ2FzYycsXG4gICAgICAgIGxpbWl0OiBwYXJhbXMucGFnaW5hdGlvbj8ubGltaXQsXG4gICAgICAgIGN1cnNvcjogcGFyYW1zLnBhZ2luYXRpb24/LmN1cnNvcixcbiAgICAgICAgZ2V0Q3JlYXRlZEF0OiBnZXRPYmplY3RDcmVhdGVkQXQoJ2V2bnQnKSxcbiAgICAgICAgZ2V0SWQ6IChldmVudCkgPT4gZXZlbnQuZXZlbnRJZCxcbiAgICAgIH0pO1xuXG4gICAgICAvLyBJZiByZXNvbHZlRGF0YSBpcyBcIm5vbmVcIiwgcmVtb3ZlIGV2ZW50RGF0YSBmcm9tIGV2ZW50c1xuICAgICAgaWYgKHJlc29sdmVEYXRhID09PSAnbm9uZScpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5yZXN1bHQsXG4gICAgICAgICAgZGF0YTogcmVzdWx0LmRhdGEubWFwKChldmVudCkgPT5cbiAgICAgICAgICAgIHN0cmlwRXZlbnREYXRhUmVmcyhldmVudCwgcmVzb2x2ZURhdGEpXG4gICAgICAgICAgKSxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9LFxuICB9O1xufVxuIl19