@workflow/core 5.0.0-beta.5 → 5.0.0-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attribute-changes.d.ts +5 -0
- package/dist/attribute-changes.d.ts.map +1 -0
- package/dist/attribute-changes.js +25 -0
- package/dist/capabilities.d.ts +35 -2
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +74 -5
- package/dist/capture-stack.d.ts +1 -1
- package/dist/capture-stack.js +2 -2
- package/dist/class-serialization.d.ts +32 -0
- package/dist/class-serialization.d.ts.map +1 -1
- package/dist/class-serialization.js +37 -1
- package/dist/classify-error.d.ts +19 -3
- package/dist/classify-error.d.ts.map +1 -1
- package/dist/classify-error.js +98 -6
- package/dist/context-violation-error.d.ts +5 -5
- package/dist/context-violation-error.d.ts.map +1 -1
- package/dist/context-violation-error.js +8 -6
- package/dist/create-hook.d.ts +72 -7
- package/dist/create-hook.d.ts.map +1 -1
- package/dist/create-hook.js +1 -1
- package/dist/define-hook.js +2 -2
- package/dist/describe-error.d.ts +7 -7
- package/dist/describe-error.d.ts.map +1 -1
- package/dist/describe-error.js +97 -12
- package/dist/encryption.d.ts +38 -4
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +96 -14
- package/dist/events-consumer.d.ts +271 -0
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +646 -42
- package/dist/flushable-stream.d.ts +56 -1
- package/dist/flushable-stream.d.ts.map +1 -1
- package/dist/flushable-stream.js +123 -11
- package/dist/global.d.ts +36 -2
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +21 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/log-format.d.ts +2 -2
- package/dist/log-format.js +7 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +10 -6
- package/dist/private.d.ts +226 -9
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +626 -7
- package/dist/replay-payload-cache.d.ts +64 -0
- package/dist/replay-payload-cache.d.ts.map +1 -0
- package/dist/replay-payload-cache.js +158 -0
- package/dist/runtime/compute-instance.d.ts +13 -0
- package/dist/runtime/compute-instance.d.ts.map +1 -0
- package/dist/runtime/compute-instance.js +14 -0
- package/dist/runtime/constants.d.ts +315 -0
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +502 -16
- package/dist/runtime/count-step-started-events.d.ts +52 -0
- package/dist/runtime/count-step-started-events.d.ts.map +1 -0
- package/dist/runtime/count-step-started-events.js +72 -0
- package/dist/runtime/deployment-guard.d.ts +99 -0
- package/dist/runtime/deployment-guard.d.ts.map +1 -0
- package/dist/runtime/deployment-guard.js +154 -0
- package/dist/runtime/get-port-lazy.d.ts +15 -0
- package/dist/runtime/get-port-lazy.d.ts.map +1 -1
- package/dist/runtime/get-port-lazy.js +75 -6
- package/dist/runtime/get-world-lazy.d.ts +5 -14
- package/dist/runtime/get-world-lazy.d.ts.map +1 -1
- package/dist/runtime/get-world-lazy.js +19 -29
- package/dist/runtime/helpers.d.ts +341 -29
- package/dist/runtime/helpers.d.ts.map +1 -1
- package/dist/runtime/helpers.js +559 -57
- package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
- package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
- package/dist/runtime/quickjs-assets.generated.js +30 -0
- package/dist/runtime/quickjs-entrypoint.d.ts +136 -0
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
- package/dist/runtime/quickjs-entrypoint.js +1686 -0
- package/dist/runtime/quickjs-runtime.d.ts +244 -0
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
- package/dist/runtime/quickjs-runtime.js +2427 -0
- package/dist/runtime/quickjs-serde.d.ts +107 -0
- package/dist/runtime/quickjs-serde.d.ts.map +1 -0
- package/dist/runtime/quickjs-serde.js +2159 -0
- package/dist/runtime/replay-budget.d.ts +97 -0
- package/dist/runtime/replay-budget.d.ts.map +1 -0
- package/dist/runtime/replay-budget.js +139 -0
- package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
- package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
- package/dist/runtime/replay-recovery-reporter.js +64 -0
- package/dist/runtime/resume-hook.d.ts +91 -8
- package/dist/runtime/resume-hook.d.ts.map +1 -1
- package/dist/runtime/resume-hook.js +512 -89
- package/dist/runtime/resume-latency.d.ts +150 -0
- package/dist/runtime/resume-latency.d.ts.map +1 -0
- package/dist/runtime/resume-latency.js +155 -0
- package/dist/runtime/run-id-time.d.ts +19 -0
- package/dist/runtime/run-id-time.d.ts.map +1 -0
- package/dist/runtime/run-id-time.js +42 -0
- package/dist/runtime/run.d.ts +72 -7
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +280 -53
- package/dist/runtime/runs.d.ts +54 -3
- package/dist/runtime/runs.d.ts.map +1 -1
- package/dist/runtime/runs.js +126 -16
- package/dist/runtime/start.d.ts +101 -2
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +352 -61
- package/dist/runtime/step-executor.d.ts +205 -5
- package/dist/runtime/step-executor.d.ts.map +1 -1
- package/dist/runtime/step-executor.js +760 -127
- package/dist/runtime/step-latency.d.ts +220 -0
- package/dist/runtime/step-latency.d.ts.map +1 -0
- package/dist/runtime/step-latency.js +215 -0
- package/dist/runtime/step-ownership.d.ts +72 -0
- package/dist/runtime/step-ownership.d.ts.map +1 -0
- package/dist/runtime/step-ownership.js +114 -0
- package/dist/runtime/step-single-flight.d.ts +12 -0
- package/dist/runtime/step-single-flight.d.ts.map +1 -0
- package/dist/runtime/step-single-flight.js +75 -0
- package/dist/runtime/suspension-handler.d.ts +269 -7
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +1253 -125
- package/dist/runtime/unserializable-step.d.ts +34 -0
- package/dist/runtime/unserializable-step.d.ts.map +1 -0
- package/dist/runtime/unserializable-step.js +52 -0
- package/dist/runtime/vm-mode.d.ts +44 -0
- package/dist/runtime/vm-mode.d.ts.map +1 -0
- package/dist/runtime/vm-mode.js +62 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
- package/dist/runtime/vm-serde-bundle.generated.js +16 -0
- package/dist/runtime/wait-continuation.d.ts +106 -0
- package/dist/runtime/wait-continuation.d.ts.map +1 -0
- package/dist/runtime/wait-continuation.js +136 -0
- package/dist/runtime/wait-until.d.ts +18 -0
- package/dist/runtime/wait-until.d.ts.map +1 -0
- package/dist/runtime/wait-until.js +42 -0
- package/dist/runtime/world-compatibility.d.ts +32 -0
- package/dist/runtime/world-compatibility.d.ts.map +1 -0
- package/dist/runtime/world-compatibility.js +44 -0
- package/dist/runtime/world-init.d.ts +17 -42
- package/dist/runtime/world-init.d.ts.map +1 -1
- package/dist/runtime/world-init.js +18 -43
- package/dist/runtime/world.d.ts +14 -2
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +75 -19
- package/dist/runtime.d.ts +10 -6
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3741 -490
- package/dist/sealed-box.d.ts +167 -0
- package/dist/sealed-box.d.ts.map +1 -0
- package/dist/sealed-box.js +571 -0
- package/dist/serialization/client.d.ts +3 -3
- package/dist/serialization/client.d.ts.map +1 -1
- package/dist/serialization/client.js +8 -4
- package/dist/serialization/codec-devalue-vm.d.ts +16 -0
- package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
- package/dist/serialization/codec-devalue-vm.js +148 -0
- package/dist/serialization/codec-devalue.d.ts +1 -1
- package/dist/serialization/codec-devalue.d.ts.map +1 -1
- package/dist/serialization/codec-devalue.js +24 -6
- package/dist/serialization/codec.d.ts +37 -2
- package/dist/serialization/codec.d.ts.map +1 -1
- package/dist/serialization/codec.js +2 -2
- package/dist/serialization/compression.d.ts +105 -0
- package/dist/serialization/compression.d.ts.map +1 -0
- package/dist/serialization/compression.js +261 -0
- package/dist/serialization/encryption.d.ts +119 -17
- package/dist/serialization/encryption.d.ts.map +1 -1
- package/dist/serialization/encryption.js +152 -17
- package/dist/serialization/errors.d.ts +13 -0
- package/dist/serialization/errors.d.ts.map +1 -1
- package/dist/serialization/errors.js +20 -2
- package/dist/serialization/format.d.ts +6 -6
- package/dist/serialization/format.js +7 -7
- package/dist/serialization/hardened.d.ts +156 -0
- package/dist/serialization/hardened.d.ts.map +1 -0
- package/dist/serialization/hardened.js +538 -0
- package/dist/serialization/index.d.ts +10 -8
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/index.js +10 -8
- package/dist/serialization/reducers/class-vm.d.ts +20 -0
- package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/class-vm.js +77 -0
- package/dist/serialization/reducers/class.d.ts.map +1 -1
- package/dist/serialization/reducers/class.js +11 -6
- package/dist/serialization/reducers/common-vm.d.ts +15 -0
- package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/common-vm.js +616 -0
- package/dist/serialization/reducers/common.d.ts +1 -1
- package/dist/serialization/reducers/common.d.ts.map +1 -1
- package/dist/serialization/reducers/common.js +202 -62
- package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
- package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/step-function-vm.js +97 -0
- package/dist/serialization/reducers/step-function.d.ts.map +1 -1
- package/dist/serialization/reducers/step-function.js +25 -9
- package/dist/serialization/step.d.ts +4 -4
- package/dist/serialization/step.d.ts.map +1 -1
- package/dist/serialization/step.js +9 -5
- package/dist/serialization/types.d.ts +80 -1
- package/dist/serialization/types.d.ts.map +1 -1
- package/dist/serialization/types.js +15 -1
- package/dist/serialization/workflow-vm.d.ts +29 -0
- package/dist/serialization/workflow-vm.d.ts.map +1 -0
- package/dist/serialization/workflow-vm.js +74 -0
- package/dist/serialization-format.d.ts +64 -8
- package/dist/serialization-format.d.ts.map +1 -1
- package/dist/serialization-format.js +225 -73
- package/dist/serialization.d.ts +262 -34
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +1879 -274
- package/dist/set-attributes.d.ts +13 -0
- package/dist/set-attributes.d.ts.map +1 -0
- package/dist/set-attributes.js +60 -0
- package/dist/source-map.d.ts +25 -0
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +154 -10
- package/dist/step/context-storage.d.ts +61 -2
- package/dist/step/context-storage.d.ts.map +1 -1
- package/dist/step/context-storage.js +7 -5
- package/dist/step/writable-stream.d.ts +3 -3
- package/dist/step/writable-stream.d.ts.map +1 -1
- package/dist/step/writable-stream.js +72 -9
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +148 -27
- package/dist/symbols.d.ts +50 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +51 -1
- package/dist/telemetry/semantic-conventions.d.ts +397 -2
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +297 -3
- package/dist/telemetry.d.ts +91 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +244 -17
- package/dist/test-support/orchestrator-context.d.ts +25 -0
- package/dist/test-support/orchestrator-context.d.ts.map +1 -0
- package/dist/test-support/orchestrator-context.js +101 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +23 -1
- package/dist/util.d.ts +0 -5
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -15
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -2
- package/dist/vm/index.d.ts.map +1 -1
- package/dist/vm/index.js +84 -14
- package/dist/vm/script-cache.d.ts +26 -0
- package/dist/vm/script-cache.d.ts.map +1 -0
- package/dist/vm/script-cache.js +131 -0
- package/dist/vm/uint8array-base64.js +1 -1
- package/dist/workflow/abort-controller.d.ts +2 -2
- package/dist/workflow/abort-controller.d.ts.map +1 -1
- package/dist/workflow/abort-controller.js +88 -21
- package/dist/workflow/attribute-dispatcher.d.ts +6 -0
- package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
- package/dist/workflow/attribute-dispatcher.js +45 -0
- package/dist/workflow/create-hook.d.ts.map +1 -1
- package/dist/workflow/create-hook.js +24 -3
- package/dist/workflow/get-workflow-metadata.js +2 -2
- package/dist/workflow/hook.d.ts.map +1 -1
- package/dist/workflow/hook.js +304 -47
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +2 -1
- package/dist/workflow/set-attributes.d.ts +68 -0
- package/dist/workflow/set-attributes.d.ts.map +1 -0
- package/dist/workflow/set-attributes.js +60 -0
- package/dist/workflow/sleep.d.ts.map +1 -1
- package/dist/workflow/sleep.js +58 -12
- package/dist/workflow/world-init-stub.d.ts +3 -3
- package/dist/workflow/world-init-stub.js +4 -4
- package/dist/workflow.d.ts +95 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +895 -561
- package/docs/api-reference/create-hook.mdx +89 -10
- package/docs/api-reference/create-webhook.mdx +16 -15
- package/docs/api-reference/define-hook.mdx +35 -33
- package/docs/api-reference/fatal-error.mdx +30 -8
- package/docs/api-reference/fetch.mdx +14 -10
- package/docs/api-reference/get-step-metadata.mdx +2 -2
- package/docs/api-reference/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/get-writable.mdx +7 -7
- package/docs/api-reference/index.mdx +4 -1
- package/docs/api-reference/retryable-error.mdx +1 -1
- package/docs/api-reference/set-attributes.mdx +61 -0
- package/docs/api-reference/sleep.mdx +4 -4
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +107 -59
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +98 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/package.json +24 -15
- package/dist/runtime/step-handler.d.ts +0 -2
- package/dist/runtime/step-handler.d.ts.map +0 -1
- package/dist/runtime/step-handler.js +0 -776
|
@@ -0,0 +1,1686 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuickJS WebAssembly (WASM) VM integration with the Workflow DevKit.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the entry point for running workflows in the
|
|
5
|
+
* QuickJS VM engine instead of the `node:vm` engine. Both engines
|
|
6
|
+
* implement the same event-replay execution model, where every invocation:
|
|
7
|
+
*
|
|
8
|
+
* 1. Loads the full event log for the run
|
|
9
|
+
* 2. Runs the workflow function from the top in a fresh QuickJS VM,
|
|
10
|
+
* replaying the event log to resolve awaited primitives
|
|
11
|
+
* 3. On suspension: creates events + queues steps for new pending ops
|
|
12
|
+
* 4. On completion: creates run_completed
|
|
13
|
+
* 5. On failure: creates run_failed
|
|
14
|
+
*/
|
|
15
|
+
import { EntityConflictError, HookNotFoundError, MaxEventsExceededError, RunExpiredError, WorkflowNotRegisteredError, } from '@workflow/errors';
|
|
16
|
+
import { parseWorkflowName } from '@workflow/utils/parse-name';
|
|
17
|
+
import { ROOT_RUN_ID_ATTRIBUTE, SPEC_VERSION_CURRENT, SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT, SPEC_VERSION_SUPPORTS_COMPRESSION, } from '@workflow/world';
|
|
18
|
+
import { classifyRunError, isRetryableWorldError } from '../classify-error.js';
|
|
19
|
+
import { runtimeLogger } from '../logger.js';
|
|
20
|
+
import { deriveRunPayloadKeys, encrypt as encryptSerializedData, } from '../serialization/encryption.js';
|
|
21
|
+
import { dehydrateRunError, dehydrateStepArguments, dehydrateStepError, hydrateRunError, maybeEncrypt, } from '../serialization.js';
|
|
22
|
+
import { remapErrorStack, stripInlineSourceMap } from '../source-map.js';
|
|
23
|
+
import * as Attribute from '../telemetry/semantic-conventions.js';
|
|
24
|
+
import { serializeTraceCarrier } from '../telemetry.js';
|
|
25
|
+
import { getInlineOwnershipLeaseSeconds, getMaxInlineSteps, isResilientStepDispatchEnabled, MAX_RESILIENT_STEP_INPUT_BYTES, } from './constants.js';
|
|
26
|
+
import { getPortLazy } from './get-port-lazy.js';
|
|
27
|
+
import { getWorkflowQueueName, queueMessage, stepDispatchIdempotencyKey, } from './helpers.js';
|
|
28
|
+
import { BASELINE_BUNDLE_FILENAME, startQuickJSWorkflow, } from './quickjs-runtime.js';
|
|
29
|
+
import { ReplayBudget } from './replay-budget.js';
|
|
30
|
+
import { executeStep } from './step-executor.js';
|
|
31
|
+
import { runStepSingleFlight } from './step-single-flight.js';
|
|
32
|
+
import { unserializableStepInputPlaceholder } from './unserializable-step.js';
|
|
33
|
+
import { getWaitContinuationDispatch } from './wait-continuation.js';
|
|
34
|
+
import { getWorld } from './world.js';
|
|
35
|
+
/** Tiny ms timer using performance.now(), already monotonic on Node. */
|
|
36
|
+
function tick() {
|
|
37
|
+
return performance.now();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns true when the supplied preloaded events indicate this is the
|
|
41
|
+
* first workflow handler invocation for the run, i.e. the log contains
|
|
42
|
+
* nothing beyond `run_created` / `run_started`. In that case the
|
|
43
|
+
* preloaded events ARE the complete event log and the `events.list`
|
|
44
|
+
* round-trips can be skipped entirely.
|
|
45
|
+
*
|
|
46
|
+
* Crucially, if the world backfilled a missing `run_created` via the
|
|
47
|
+
* resilient start path, `preloadedEvents` contains it even when a fresh
|
|
48
|
+
* `events.list` might not (eventual consistency), so preferring the
|
|
49
|
+
* preloaded events on first invocation is also the more correct choice.
|
|
50
|
+
*
|
|
51
|
+
* Returns false when `preloadedEvents` is missing/empty so the caller
|
|
52
|
+
* falls back to the normal fetch path.
|
|
53
|
+
*
|
|
54
|
+
* Exported for unit testing.
|
|
55
|
+
*/
|
|
56
|
+
export function isFirstInvocation(preloadedEvents) {
|
|
57
|
+
if (!Array.isArray(preloadedEvents) || preloadedEvents.length === 0) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return preloadedEvents.every((e) => e.eventType === 'run_created' || e.eventType === 'run_started');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Queue a step for background execution via the unified workflow queue
|
|
64
|
+
* (V2 architecture). The combined handler in runtime.ts dispatches
|
|
65
|
+
* messages with `stepId` to executeStep, which works for both VM engines.
|
|
66
|
+
* `delaySeconds` supports retry/throttle backoff.
|
|
67
|
+
*/
|
|
68
|
+
async function queueStepMessage(params) {
|
|
69
|
+
const { world, runId, workflowRun, step, delaySeconds, namespace, nextTraceCarrier, purpose, stepInput, wfdiag, } = params;
|
|
70
|
+
const traceCarrier = await nextTraceCarrier();
|
|
71
|
+
await queueMessage(world, getWorkflowQueueName(workflowRun.workflowName, namespace), {
|
|
72
|
+
runId,
|
|
73
|
+
stepId: step.correlationId,
|
|
74
|
+
stepName: step.stepId,
|
|
75
|
+
traceCarrier,
|
|
76
|
+
requestedAt: new Date(),
|
|
77
|
+
...(stepInput !== undefined ? { stepInput: { input: stepInput } } : {}),
|
|
78
|
+
}, {
|
|
79
|
+
// The 'dispatch' key is step-identity-scoped (correlationId + hashed
|
|
80
|
+
// step name), shared with the node engine's dispatch of the same step
|
|
81
|
+
// so the two stay mutually exclusive, without a revoked resilient
|
|
82
|
+
// message absorbing a reassigned correlation id's legitimate dispatch.
|
|
83
|
+
// See stepDispatchIdempotencyKey.
|
|
84
|
+
idempotencyKey: purpose === 'dispatch'
|
|
85
|
+
? stepDispatchIdempotencyKey(step.correlationId, step.stepId)
|
|
86
|
+
: `${step.correlationId}:${purpose}`,
|
|
87
|
+
...(delaySeconds && delaySeconds > 0 ? { delaySeconds } : {}),
|
|
88
|
+
});
|
|
89
|
+
wfdiag('step_queued', {
|
|
90
|
+
stepId: step.stepId,
|
|
91
|
+
correlationId: step.correlationId,
|
|
92
|
+
purpose,
|
|
93
|
+
delaySeconds: delaySeconds ?? 0,
|
|
94
|
+
...(stepInput !== undefined ? { resilient: true } : {}),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Dispatch durable side effects for a set of pending VM operations:
|
|
99
|
+
* step_created (+ optional queueing), hook_created / hook_received (aborts),
|
|
100
|
+
* attr_set, hook_disposed, and wait_created events.
|
|
101
|
+
*
|
|
102
|
+
* Steps are created but (usually) not queued here: queueing (or inline
|
|
103
|
+
* execution) is the caller's decision. Used both for suspension
|
|
104
|
+
* processing (the inline loop) and for the terminal drain (flushing
|
|
105
|
+
* leftover side effects when the workflow completed or failed, mirroring
|
|
106
|
+
* the node:vm engine's drainPendingQueueItems).
|
|
107
|
+
*
|
|
108
|
+
* The one exception is resilient step dispatch: for step cids named in
|
|
109
|
+
* `queueStepCids` (the caller's overflow steps) that pass the eligibility
|
|
110
|
+
* gates, the step_created write is parallelized with the step's queue
|
|
111
|
+
* publish: the message carries the serialized input (`stepInput`) so the
|
|
112
|
+
* consumer can idempotently re-ensure the event if the direct write failed
|
|
113
|
+
* transiently. Steps queued this way are reported in `queuedStepCids`; the
|
|
114
|
+
* caller queues the rest itself.
|
|
115
|
+
*/
|
|
116
|
+
async function dispatchPendingOps(params) {
|
|
117
|
+
const { world, runId, workflowRun, encryptionKey, pendingOperations, namespace, nextTraceCarrier, } = params;
|
|
118
|
+
const skipStepCreation = params.skipStepCreation;
|
|
119
|
+
const queueStepCids = params.queueStepCids;
|
|
120
|
+
const wfdiag = params.wfdiag;
|
|
121
|
+
// Step cids published via resilient dispatch below (create + queue in
|
|
122
|
+
// parallel, message carrying `stepInput`). Reported to the caller so it
|
|
123
|
+
// skips them in its own queueing pass.
|
|
124
|
+
const queuedStepCids = new Set();
|
|
125
|
+
// Step cids finalized as step_created + step_failed because their input
|
|
126
|
+
// refused to serialize. See the `finalizeUnserializableSteps` param.
|
|
127
|
+
const failedSerializationStepCids = new Set();
|
|
128
|
+
// Resilient step dispatch eligibility, shared by every step op below (the
|
|
129
|
+
// per-step input-size check is applied inside the op): feature enabled and
|
|
130
|
+
// a binary-safe (CBOR) queue transport for the run.
|
|
131
|
+
//
|
|
132
|
+
// Unlike the node:vm suspension handler's gate (see
|
|
133
|
+
// SuspensionHandlerParams.stepDispatch), there is NO precondition-guard
|
|
134
|
+
// gate here: this engine's step_created writes are unguarded (no snapshot
|
|
135
|
+
// is attached), so a guard-enforcing World can never 412-reject them:
|
|
136
|
+
// the consumer's re-ensure therefore cannot materialize a step the guard
|
|
137
|
+
// rejected. If this engine ever adopts guarded suspension writes, the
|
|
138
|
+
// capability gate from the node:vm handler must be added here too.
|
|
139
|
+
const resilientDispatchEligible = queueStepCids !== undefined &&
|
|
140
|
+
queueStepCids.size > 0 &&
|
|
141
|
+
isResilientStepDispatchEnabled() &&
|
|
142
|
+
(workflowRun.specVersion ?? 0) >=
|
|
143
|
+
SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT;
|
|
144
|
+
// Set when a hook with a parked getConflict() awaiter had its
|
|
145
|
+
// hook_created written this invocation. The workflow must be re-invoked
|
|
146
|
+
// so replay can confirm creation and resolve the awaiter.
|
|
147
|
+
let createdGetConflictHook = false;
|
|
148
|
+
// Set when a new attr_set event is written this invocation. The
|
|
149
|
+
// workflow must be re-invoked to consume it (resolving the pending
|
|
150
|
+
// setAttributes() promise), so the entrypoint requeues immediately,
|
|
151
|
+
// same pattern as an elapsed wait.
|
|
152
|
+
let createdAttributeEvent = false;
|
|
153
|
+
const opsPromises = [];
|
|
154
|
+
const processHookOp = async (hook) => {
|
|
155
|
+
runtimeLogger.debug('QuickJS runtime: processing hook op', {
|
|
156
|
+
workflowRunId: runId,
|
|
157
|
+
correlationId: hook.correlationId,
|
|
158
|
+
token: hook.token,
|
|
159
|
+
tokenType: typeof hook.token,
|
|
160
|
+
isWebhook: hook.isWebhook,
|
|
161
|
+
isSystem: hook.isSystem,
|
|
162
|
+
hasCreatedEvent: hook.hasCreatedEvent,
|
|
163
|
+
abortRequested: hook.abortRequested,
|
|
164
|
+
});
|
|
165
|
+
if (!hook.hasCreatedEvent) {
|
|
166
|
+
// `hook.metadata` is the format-prefixed devalue bytes
|
|
167
|
+
// produced by `globalThis[Symbol.for('workflow-serialize')]
|
|
168
|
+
// (options.metadata)` inside the VM. Encrypt on the host
|
|
169
|
+
// side before writing, which matches the node:vm engine's
|
|
170
|
+
// `dehydrateStepArguments` flow.
|
|
171
|
+
//
|
|
172
|
+
// No pre-check via hooks.list: with deterministic correlationIds
|
|
173
|
+
// (same VM seed across replays) and per-(runId, correlationId)
|
|
174
|
+
// uniqueness in worlds, the storage layer rejects duplicates as
|
|
175
|
+
// EntityConflictError, which we swallow below. This drops one
|
|
176
|
+
// network round-trip per pending hook.
|
|
177
|
+
try {
|
|
178
|
+
const encryptedMetadata = typeof hook.metadata === 'undefined'
|
|
179
|
+
? undefined
|
|
180
|
+
: await encryptSerializedData(hook.metadata, encryptionKey);
|
|
181
|
+
const result = await world.events.create(runId, {
|
|
182
|
+
eventType: 'hook_created',
|
|
183
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
184
|
+
correlationId: hook.correlationId,
|
|
185
|
+
eventData: {
|
|
186
|
+
token: hook.token,
|
|
187
|
+
tokenRetentionUntil: hook.tokenRetentionUntil === undefined
|
|
188
|
+
? undefined
|
|
189
|
+
: new Date(hook.tokenRetentionUntil),
|
|
190
|
+
metadata: encryptedMetadata,
|
|
191
|
+
// Always include isWebhook explicitly. Worlds default it to
|
|
192
|
+
// `true` when absent, which would break the public webhook
|
|
193
|
+
// endpoint's 404 guard for hooks created via createHook().
|
|
194
|
+
isWebhook: hook.isWebhook,
|
|
195
|
+
// System hooks (AbortController) are exempt from user
|
|
196
|
+
// token namespace conflict checks.
|
|
197
|
+
...(hook.isSystem ? { isSystem: true } : {}),
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
// If storage detected a real token conflict with another
|
|
201
|
+
// workflow's hook, re-queue so the workflow handler can
|
|
202
|
+
// process the conflict event and fail gracefully.
|
|
203
|
+
if (result.event?.eventType === 'hook_conflict') {
|
|
204
|
+
await queueMessage(world, getWorkflowQueueName(workflowRun.workflowName, namespace), {
|
|
205
|
+
runId,
|
|
206
|
+
traceCarrier: await nextTraceCarrier(),
|
|
207
|
+
requestedAt: new Date(),
|
|
208
|
+
}, { idempotencyKey: `hook_conflict_${hook.correlationId}` });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
// Already created by a concurrent invocation, so fall through
|
|
213
|
+
// to abort processing below (if any) instead of bailing.
|
|
214
|
+
if (!EntityConflictError.is(err))
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
if (hook.hasGetConflictAwaiter) {
|
|
218
|
+
createdGetConflictHook = true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (hook.abortRequested) {
|
|
222
|
+
// Record the abort durably: a hook_received event carrying
|
|
223
|
+
// the VM-serialized `{ aborted: true, reason }` payload,
|
|
224
|
+
// plus a best-effort stream packet for real-time step
|
|
225
|
+
// propagation. Mirrors the node:vm engine's suspension
|
|
226
|
+
// handler (hooksNeedingAbort).
|
|
227
|
+
const abortPayload = hook.abortPayload instanceof Uint8Array
|
|
228
|
+
? (await encryptSerializedData(hook.abortPayload, encryptionKey))
|
|
229
|
+
: undefined;
|
|
230
|
+
try {
|
|
231
|
+
await world.events.create(runId, {
|
|
232
|
+
eventType: 'hook_received',
|
|
233
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
234
|
+
correlationId: hook.correlationId,
|
|
235
|
+
eventData: {
|
|
236
|
+
token: hook.token,
|
|
237
|
+
payload: abortPayload,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
if (!EntityConflictError.is(err))
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
245
|
+
// streamName is derived from the abort hook token
|
|
246
|
+
// (`abrt_{id}` → `strm_{id}_system_abort`).
|
|
247
|
+
if (hook.token.startsWith('abrt_') && abortPayload) {
|
|
248
|
+
const streamName = `strm_${hook.token.slice('abrt_'.length)}_system_abort`;
|
|
249
|
+
try {
|
|
250
|
+
await world.streams.write(runId, streamName, abortPayload);
|
|
251
|
+
await world.streams.close(runId, streamName);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Best-effort: the hook event provides the durable
|
|
255
|
+
// fallback.
|
|
256
|
+
runtimeLogger.debug('QuickJS runtime: failed to write abort stream packet', {
|
|
257
|
+
workflowRunId: runId,
|
|
258
|
+
correlationId: hook.correlationId,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
wfdiag('abort_recorded', {
|
|
263
|
+
correlationId: hook.correlationId,
|
|
264
|
+
token: hook.token,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const processHookDisposeOp = async (op) => {
|
|
269
|
+
try {
|
|
270
|
+
await world.events.create(runId, {
|
|
271
|
+
eventType: 'hook_disposed',
|
|
272
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
273
|
+
correlationId: op.correlationId,
|
|
274
|
+
// The hook's token, which the node:vm engine has always sent. A world
|
|
275
|
+
// that keys a hook's token claim separately from the hook itself needs
|
|
276
|
+
// it to release both, and the only alternative is for it to look the
|
|
277
|
+
// token up first. Omitted rather than sent as undefined when the op
|
|
278
|
+
// carries none, so a world that reads it cannot tell the difference
|
|
279
|
+
// between this engine and a client too old to send one.
|
|
280
|
+
...(op.token === undefined ? {} : { eventData: { token: op.token } }),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
if (EntityConflictError.is(err))
|
|
285
|
+
return;
|
|
286
|
+
// Disposing a hook whose entity no longer (or never) exists is an
|
|
287
|
+
// idempotent no-op: the entity may have been torn down by a
|
|
288
|
+
// concurrent run cancellation, or the hook may have lost its
|
|
289
|
+
// token claim to a conflict. There is nothing left to release.
|
|
290
|
+
if (HookNotFoundError.is(err))
|
|
291
|
+
return;
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
// Hook operations are grouped by token and processed SEQUENTIALLY in
|
|
296
|
+
// code order within each group, mirroring the node:vm suspension
|
|
297
|
+
// handler (hookItemsByToken): a dispose() of an earlier hook must
|
|
298
|
+
// release the token before a later same-token hook's creation is
|
|
299
|
+
// validated by the world: parallel dispatch would otherwise record a
|
|
300
|
+
// spurious hook_conflict against the run's own disposed hook (e.g. a
|
|
301
|
+
// dispose→recreate loop reusing one token). Different tokens have no
|
|
302
|
+
// claim interaction, so token groups run in parallel with each other
|
|
303
|
+
// and with the non-hook ops below.
|
|
304
|
+
const hookOpsByToken = new Map();
|
|
305
|
+
for (const op of pendingOperations) {
|
|
306
|
+
let key;
|
|
307
|
+
if (op.type === 'hook' &&
|
|
308
|
+
(!op.hasCreatedEvent || op.abortRequested)) {
|
|
309
|
+
key = op.token;
|
|
310
|
+
}
|
|
311
|
+
else if (op.type === 'hook_dispose' && !op.hasCreatedEvent) {
|
|
312
|
+
// Per-op fallback group when the token is unknown: no ordering
|
|
313
|
+
// guarantees, matching the previous parallel behavior.
|
|
314
|
+
key = op.token ?? `__cid:${op.correlationId}`;
|
|
315
|
+
}
|
|
316
|
+
if (key === undefined)
|
|
317
|
+
continue;
|
|
318
|
+
const group = hookOpsByToken.get(key);
|
|
319
|
+
if (group) {
|
|
320
|
+
group.push(op);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
hookOpsByToken.set(key, [op]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const group of hookOpsByToken.values()) {
|
|
327
|
+
opsPromises.push((async () => {
|
|
328
|
+
for (const op of group) {
|
|
329
|
+
if (op.type === 'hook') {
|
|
330
|
+
await processHookOp(op);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
await processHookDisposeOp(op);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
})());
|
|
337
|
+
}
|
|
338
|
+
for (const op of pendingOperations) {
|
|
339
|
+
if (op.type === 'step' &&
|
|
340
|
+
!op.hasCreatedEvent &&
|
|
341
|
+
!skipStepCreation?.has(op.correlationId)) {
|
|
342
|
+
const step = op;
|
|
343
|
+
opsPromises.push((async () => {
|
|
344
|
+
// The step's input refused to serialize while dumping the VM's
|
|
345
|
+
// pending ops (see PendingStep.serializationError). Finalize it
|
|
346
|
+
// as step_created (placeholder input, since the world requires the
|
|
347
|
+
// step entity before a terminal event) + step_failed carrying
|
|
348
|
+
// the SerializationError, so the live-VM feed rejects the
|
|
349
|
+
// step's promise and workflow code can catch it. Never queue an
|
|
350
|
+
// execution message for it. Mirrors the node:vm engine's
|
|
351
|
+
// finalizeUnserializableStep. In the terminal drain
|
|
352
|
+
// (finalizeUnserializableSteps unset), skip entirely: see the
|
|
353
|
+
// param docs.
|
|
354
|
+
if (step.serializationError) {
|
|
355
|
+
if (!params.finalizeUnserializableSteps) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
runtimeLogger.warn('Step arguments failed to serialize; failing the step so ' +
|
|
359
|
+
'the workflow can observe the error', {
|
|
360
|
+
workflowRunId: runId,
|
|
361
|
+
correlationId: step.correlationId,
|
|
362
|
+
stepName: step.stepId,
|
|
363
|
+
error: step.serializationError.message,
|
|
364
|
+
});
|
|
365
|
+
try {
|
|
366
|
+
await world.events.create(runId, {
|
|
367
|
+
eventType: 'step_created',
|
|
368
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
369
|
+
correlationId: step.correlationId,
|
|
370
|
+
eventData: {
|
|
371
|
+
stepName: step.stepId,
|
|
372
|
+
input: (await dehydrateStepArguments(unserializableStepInputPlaceholder(), runId, encryptionKey, globalThis, false, (workflowRun.specVersion ?? 0) >=
|
|
373
|
+
SPEC_VERSION_SUPPORTS_COMPRESSION)),
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
// Concurrent invocation hit the same deterministic failure
|
|
379
|
+
// and created it first, or the run already finished.
|
|
380
|
+
if (RunExpiredError.is(err))
|
|
381
|
+
return;
|
|
382
|
+
if (!EntityConflictError.is(err))
|
|
383
|
+
throw err;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
await world.events.create(runId, {
|
|
387
|
+
eventType: 'step_failed',
|
|
388
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
389
|
+
correlationId: step.correlationId,
|
|
390
|
+
eventData: {
|
|
391
|
+
stepName: step.stepId,
|
|
392
|
+
error: await dehydrateStepError(step.serializationError, runId, encryptionKey, [], globalThis, (workflowRun.specVersion ?? 0) >=
|
|
393
|
+
SPEC_VERSION_SUPPORTS_COMPRESSION),
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
// Step already terminal or run already finished.
|
|
399
|
+
if (!EntityConflictError.is(err) && !RunExpiredError.is(err)) {
|
|
400
|
+
throw err;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
failedSerializationStepCids.add(step.correlationId);
|
|
404
|
+
wfdiag('step_serialization_failed', {
|
|
405
|
+
stepId: step.stepId,
|
|
406
|
+
correlationId: step.correlationId,
|
|
407
|
+
});
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
// Create step_created event. `step.input` is the
|
|
411
|
+
// format-prefixed devalue bytes ("devl" + devalue) produced
|
|
412
|
+
// by `globalThis[Symbol.for('workflow-serialize')]({args,
|
|
413
|
+
// closureVars, thisVal})` inside the VM. The VM has no
|
|
414
|
+
// access to the CryptoKey, so encryption is applied here
|
|
415
|
+
// on the host side, matching what
|
|
416
|
+
// `dehydrateStepArguments` does in the node:vm engine.
|
|
417
|
+
const encryptedInput = await encryptSerializedData(step.input, encryptionKey);
|
|
418
|
+
// Resilient step dispatch: fire the step_created write and the
|
|
419
|
+
// step's queue publish in parallel: the message carries the
|
|
420
|
+
// same serialized input (`stepInput`) so the consumer can
|
|
421
|
+
// idempotently re-ensure the event if the direct write failed
|
|
422
|
+
// transiently. Mirrors the node:vm suspension handler and the
|
|
423
|
+
// resilient start / resilient hook resume patterns. Only for
|
|
424
|
+
// caller-designated overflow steps with inputs the queue
|
|
425
|
+
// message can safely carry (binary, under the VQS size cap).
|
|
426
|
+
if (resilientDispatchEligible &&
|
|
427
|
+
queueStepCids?.has(step.correlationId) &&
|
|
428
|
+
encryptedInput instanceof Uint8Array &&
|
|
429
|
+
encryptedInput.byteLength <= MAX_RESILIENT_STEP_INPUT_BYTES) {
|
|
430
|
+
const [createResult, queueResult] = await Promise.allSettled([
|
|
431
|
+
world.events.create(runId, {
|
|
432
|
+
eventType: 'step_created',
|
|
433
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
434
|
+
correlationId: step.correlationId,
|
|
435
|
+
eventData: {
|
|
436
|
+
stepName: step.stepId,
|
|
437
|
+
input: encryptedInput,
|
|
438
|
+
},
|
|
439
|
+
}),
|
|
440
|
+
queueStepMessage({
|
|
441
|
+
world,
|
|
442
|
+
runId,
|
|
443
|
+
workflowRun,
|
|
444
|
+
step,
|
|
445
|
+
namespace,
|
|
446
|
+
nextTraceCarrier,
|
|
447
|
+
purpose: 'dispatch',
|
|
448
|
+
stepInput: encryptedInput,
|
|
449
|
+
wfdiag,
|
|
450
|
+
}),
|
|
451
|
+
]);
|
|
452
|
+
// Queue failure is always fatal for this dispatch pass:
|
|
453
|
+
// without the message the step would rely on the create
|
|
454
|
+
// alone, and if the create ALSO failed there would be no
|
|
455
|
+
// durable record at all. Propagating redelivers the
|
|
456
|
+
// orchestrator message, which re-creates the (idempotent)
|
|
457
|
+
// step_created and re-dispatches.
|
|
458
|
+
if (queueResult.status === 'rejected') {
|
|
459
|
+
throw queueResult.reason;
|
|
460
|
+
}
|
|
461
|
+
queuedStepCids.add(step.correlationId);
|
|
462
|
+
if (createResult.status === 'rejected') {
|
|
463
|
+
const err = createResult.reason;
|
|
464
|
+
if (EntityConflictError.is(err)) {
|
|
465
|
+
// Concurrent invocation wrote it first: the message is
|
|
466
|
+
// already out; its duplicate publish dedupes on the
|
|
467
|
+
// shared step-identity-scoped idempotency key.
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (isRetryableWorldError(err)) {
|
|
471
|
+
// Resilient: the write failed transiently (429 / 5xx /
|
|
472
|
+
// transport) but the step message (carrying the same
|
|
473
|
+
// serialized input) was published, so the consumer
|
|
474
|
+
// idempotently re-ensures the step_created before
|
|
475
|
+
// executing.
|
|
476
|
+
runtimeLogger.warn('Step creation event write failed, but the step was ' +
|
|
477
|
+
'dispatched via the queue. The step_created event ' +
|
|
478
|
+
'will be ensured by the queue consumer.', {
|
|
479
|
+
workflowRunId: runId,
|
|
480
|
+
correlationId: step.correlationId,
|
|
481
|
+
stepName: step.stepId,
|
|
482
|
+
error: err instanceof Error ? err.message : String(err),
|
|
483
|
+
});
|
|
484
|
+
wfdiag('step_resilient_dispatch_recovered', {
|
|
485
|
+
stepId: step.stepId,
|
|
486
|
+
correlationId: step.correlationId,
|
|
487
|
+
});
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
throw err;
|
|
491
|
+
}
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
await world.events.create(runId, {
|
|
496
|
+
eventType: 'step_created',
|
|
497
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
498
|
+
correlationId: step.correlationId,
|
|
499
|
+
eventData: {
|
|
500
|
+
stepName: step.stepId,
|
|
501
|
+
input: encryptedInput,
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
if (EntityConflictError.is(err))
|
|
507
|
+
return;
|
|
508
|
+
throw err;
|
|
509
|
+
}
|
|
510
|
+
// NOTE: step queueing is otherwise the caller's decision: the
|
|
511
|
+
// inline loop executes fresh steps in the live VM and only
|
|
512
|
+
// queues the overflow / retry / backstop cases (see
|
|
513
|
+
// queueStepMessage).
|
|
514
|
+
})());
|
|
515
|
+
}
|
|
516
|
+
else if (op.type === 'attribute' && !op.hasCreatedEvent) {
|
|
517
|
+
const attr = op;
|
|
518
|
+
opsPromises.push((async () => {
|
|
519
|
+
try {
|
|
520
|
+
await world.events.create(runId, {
|
|
521
|
+
eventType: 'attr_set',
|
|
522
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
523
|
+
correlationId: attr.correlationId,
|
|
524
|
+
eventData: {
|
|
525
|
+
changes: attr.changes,
|
|
526
|
+
writer: { type: 'workflow' },
|
|
527
|
+
...(attr.allowReservedAttributes
|
|
528
|
+
? { allowReservedAttributes: true }
|
|
529
|
+
: {}),
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
createdAttributeEvent = true;
|
|
533
|
+
}
|
|
534
|
+
catch (err) {
|
|
535
|
+
if (EntityConflictError.is(err)) {
|
|
536
|
+
// Event already exists (concurrent invocation), but the
|
|
537
|
+
// replay still needs to consume it, so requeue.
|
|
538
|
+
createdAttributeEvent = true;
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
throw err;
|
|
542
|
+
}
|
|
543
|
+
})());
|
|
544
|
+
}
|
|
545
|
+
else if (op.type === 'wait' && !op.hasCreatedEvent) {
|
|
546
|
+
const wait = op;
|
|
547
|
+
opsPromises.push((async () => {
|
|
548
|
+
try {
|
|
549
|
+
await world.events.create(runId, {
|
|
550
|
+
eventType: 'wait_created',
|
|
551
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
552
|
+
correlationId: wait.correlationId,
|
|
553
|
+
eventData: {
|
|
554
|
+
resumeAt: new Date(wait.resumeAt),
|
|
555
|
+
},
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
catch (err) {
|
|
559
|
+
if (EntityConflictError.is(err))
|
|
560
|
+
return;
|
|
561
|
+
throw err;
|
|
562
|
+
}
|
|
563
|
+
})());
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Per-op dispatch runs in parallel.
|
|
567
|
+
await Promise.all(opsPromises);
|
|
568
|
+
return {
|
|
569
|
+
createdAttributeEvent,
|
|
570
|
+
createdGetConflictHook,
|
|
571
|
+
queuedStepCids,
|
|
572
|
+
failedSerializationStepCids,
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Run a workflow using the QuickJS WASM VM engine.
|
|
577
|
+
*
|
|
578
|
+
* This replaces the `node:vm` replay path (runWorkflow + EventsConsumer)
|
|
579
|
+
* with a QuickJS VM invocation that performs the same full event replay.
|
|
580
|
+
*
|
|
581
|
+
* KNOWN GAP (slot snapshot): unlike the node:vm path, no event write in
|
|
582
|
+
* this file carries {@link CreateEventParams.eventCount}, so a World never
|
|
583
|
+
* learns which events the writer had not seen and never reports them back.
|
|
584
|
+
* The engine currently relies on per-(runId, correlationId) event
|
|
585
|
+
* uniqueness (EntityConflictError dedup) alone. This is a deliberate
|
|
586
|
+
* simplification while the engine is experimental: wiring the snapshot is
|
|
587
|
+
* tracked follow-up work; anyone adding new write paths here should not
|
|
588
|
+
* assume parity with the node engine on this axis.
|
|
589
|
+
*/
|
|
590
|
+
export async function runWorkflowWithQuickJS(params) {
|
|
591
|
+
const { workflowCode, workflowName, workflowRun, preloadedEvents, preloadedEventsComplete, runInput, parentSpan, maxEventsLimit, deliveryAttempt, ownerMessageId, requestId, namespace, waitContinuation, } = params;
|
|
592
|
+
/**
|
|
593
|
+
* Attempt number for the continuation this invocation is about to arm for
|
|
594
|
+
* `correlationId`. One higher than the incoming continuation's when this
|
|
595
|
+
* invocation IS that continuation and the wait is still pending — the only
|
|
596
|
+
* situation in which the previous key is spent. Every other caller, and
|
|
597
|
+
* every other wait, starts at 0 and keys exactly as it did before attempts
|
|
598
|
+
* existed.
|
|
599
|
+
*/
|
|
600
|
+
const nextWaitContinuationAttempt = (correlationId) => waitContinuation?.correlationId === correlationId
|
|
601
|
+
? waitContinuation.attempt + 1
|
|
602
|
+
: 0;
|
|
603
|
+
// Standalone-caller fallback (tests): without a runtime.ts carrier
|
|
604
|
+
// accessor, fall back to the current invocation context.
|
|
605
|
+
const nextTraceCarrier = params.nextTraceCarrier ?? (() => serializeTraceCarrier());
|
|
606
|
+
const world = await getWorld();
|
|
607
|
+
const runId = workflowRun.runId;
|
|
608
|
+
const invocationStart = tick();
|
|
609
|
+
// Strip the inline source map comment before evaluating the bundle in
|
|
610
|
+
// the QuickJS VM. The map is purely host-side metadata for
|
|
611
|
+
// `remapErrorStack` (called below on workflow failures, against the
|
|
612
|
+
// ORIGINAL `workflowCode`). QuickJS retains source text for
|
|
613
|
+
// stack-trace line lookups, so the few-MB base64 comment would bloat
|
|
614
|
+
// the VM heap for no benefit.
|
|
615
|
+
const workflowCodeForVM = stripInlineSourceMap(workflowCode);
|
|
616
|
+
// Per-invocation diagnostic id so debug logs can be correlated even if
|
|
617
|
+
// the same runId is processed by overlapping invocations on different
|
|
618
|
+
// function instances.
|
|
619
|
+
const invocationId = `inv_${Math.random().toString(36).slice(2, 10)}`;
|
|
620
|
+
// Structured per-checkpoint diagnostic helper, grep-friendly by runId.
|
|
621
|
+
const wfdiag = (checkpoint, fields) => {
|
|
622
|
+
runtimeLogger.debug('QUICKJS_VM_DIAG', {
|
|
623
|
+
checkpoint,
|
|
624
|
+
runId,
|
|
625
|
+
invocationId,
|
|
626
|
+
tElapsedMs: Math.round(tick() - invocationStart),
|
|
627
|
+
...fields,
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
parentSpan?.setAttributes({
|
|
631
|
+
...Attribute.WorkflowVm('quickjs'),
|
|
632
|
+
});
|
|
633
|
+
wfdiag('enter', {
|
|
634
|
+
workflowName,
|
|
635
|
+
deliveryAttempt,
|
|
636
|
+
hasPreloadedEvents: Array.isArray(preloadedEvents) && preloadedEvents.length > 0,
|
|
637
|
+
preloadedEventCount: preloadedEvents?.length ?? 0,
|
|
638
|
+
hasRunInput: !!runInput,
|
|
639
|
+
});
|
|
640
|
+
// The workflowName from the queue topic is already the full workflow ID
|
|
641
|
+
// (e.g. "workflow//./workflows/1_simple//simple")
|
|
642
|
+
const workflowId = workflowName;
|
|
643
|
+
// Resolve the encryption key up front: needed to decrypt event
|
|
644
|
+
// payloads inside the VM and to encrypt event payloads written below.
|
|
645
|
+
// Resolve the FULL capability (symmetric AES key + X25519 keypair), not
|
|
646
|
+
// just `importKey(rawKey)`: a run reading its own event log can encounter
|
|
647
|
+
// sealed (`encp`) hook payloads that a cross-deployment `resumeHook()`
|
|
648
|
+
// wrote to it (sealing is presence-gated on the run's published
|
|
649
|
+
// encryptionPublicKey, which the shared start() path stamps regardless of
|
|
650
|
+
// engine). A bare symmetric key cannot open those and would wedge the run
|
|
651
|
+
// right after hook_received. The node:vm engine resolves the same full
|
|
652
|
+
// capability via memoizeEncryptionKey.
|
|
653
|
+
const rawKey = await world.getEncryptionKeyForRun?.(workflowRun);
|
|
654
|
+
const encryptionKey = rawKey ? await deriveRunPayloadKeys(rawKey) : undefined;
|
|
655
|
+
// Load the FULL event log for the run. On first invocation the
|
|
656
|
+
// preloaded events from the run_started response are the complete log
|
|
657
|
+
// and save the events.list round-trips; a caller-attested complete
|
|
658
|
+
// preload (lazy hook fast path) is trusted the same way.
|
|
659
|
+
let events;
|
|
660
|
+
let eventsFetchedPages = 0;
|
|
661
|
+
const usePreloaded = (preloadedEventsComplete === true &&
|
|
662
|
+
Array.isArray(preloadedEvents) &&
|
|
663
|
+
preloadedEvents.length > 0) ||
|
|
664
|
+
isFirstInvocation(preloadedEvents);
|
|
665
|
+
if (usePreloaded && preloadedEvents) {
|
|
666
|
+
events = preloadedEvents;
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
const allEvents = [];
|
|
670
|
+
let cursor = null;
|
|
671
|
+
let hasMore = true;
|
|
672
|
+
while (hasMore) {
|
|
673
|
+
const response = await world.events.list({
|
|
674
|
+
runId,
|
|
675
|
+
pagination: {
|
|
676
|
+
sortOrder: 'asc',
|
|
677
|
+
cursor: cursor ?? undefined,
|
|
678
|
+
limit: 1000,
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
eventsFetchedPages++;
|
|
682
|
+
allEvents.push(...response.data);
|
|
683
|
+
// Update the cursor to the last successfully fetched page's cursor.
|
|
684
|
+
// Only update when we got results: the final empty-page response
|
|
685
|
+
// returns cursor=null which we must NOT use (it would reset the cursor).
|
|
686
|
+
if (response.cursor) {
|
|
687
|
+
cursor = response.cursor;
|
|
688
|
+
}
|
|
689
|
+
hasMore = response.data.length > 0 && response.cursor != null;
|
|
690
|
+
}
|
|
691
|
+
events = allEvents;
|
|
692
|
+
}
|
|
693
|
+
// Event-limit guard: fail a runaway run once its log reaches the
|
|
694
|
+
// server-supplied ceiling, the same enforcement point as the node:vm
|
|
695
|
+
// engine's replay loop.
|
|
696
|
+
if (maxEventsLimit !== undefined && events.length >= maxEventsLimit) {
|
|
697
|
+
throw new MaxEventsExceededError(events.length, maxEventsLimit);
|
|
698
|
+
}
|
|
699
|
+
parentSpan?.setAttributes({
|
|
700
|
+
...Attribute.QuickJSEventsPreloaded(usePreloaded),
|
|
701
|
+
...Attribute.QuickJSEventsFetchedCount(events.length),
|
|
702
|
+
...Attribute.QuickJSEventsFetchedPages(eventsFetchedPages),
|
|
703
|
+
});
|
|
704
|
+
wfdiag('events_fetched', {
|
|
705
|
+
eventCount: events.length,
|
|
706
|
+
eventsFetchedPages,
|
|
707
|
+
usePreloaded,
|
|
708
|
+
eventTypes: events.reduce((acc, e) => {
|
|
709
|
+
acc[e.eventType] = (acc[e.eventType] ?? 0) + 1;
|
|
710
|
+
return acc;
|
|
711
|
+
}, {}),
|
|
712
|
+
});
|
|
713
|
+
// Check for elapsed waits
|
|
714
|
+
const now = Date.now();
|
|
715
|
+
const completedWaitIds = new Set(events
|
|
716
|
+
.filter((e) => e.eventType === 'wait_completed')
|
|
717
|
+
.map((e) => e.correlationId));
|
|
718
|
+
for (const event of events) {
|
|
719
|
+
if (event.eventType === 'wait_created' &&
|
|
720
|
+
event.correlationId &&
|
|
721
|
+
!completedWaitIds.has(event.correlationId)) {
|
|
722
|
+
const eventData = 'eventData' in event
|
|
723
|
+
? event.eventData
|
|
724
|
+
: undefined;
|
|
725
|
+
const resumeAt = eventData?.resumeAt;
|
|
726
|
+
if (resumeAt && now >= new Date(resumeAt).getTime()) {
|
|
727
|
+
try {
|
|
728
|
+
const result = await world.events.create(runId, {
|
|
729
|
+
eventType: 'wait_completed',
|
|
730
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
731
|
+
correlationId: event.correlationId,
|
|
732
|
+
});
|
|
733
|
+
if (result.event)
|
|
734
|
+
events.push(result.event);
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
if (EntityConflictError.is(err))
|
|
738
|
+
continue;
|
|
739
|
+
throw err;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
// Resolve the workflow server port so `getWorkflowMetadata().url` inside
|
|
745
|
+
// the VM matches what the step-side handler reports. Skipped on Vercel:
|
|
746
|
+
// the VM reads VERCEL_URL directly in that environment.
|
|
747
|
+
const isVercel = process.env.VERCEL_URL !== undefined;
|
|
748
|
+
const port = isVercel ? undefined : await getPortLazy();
|
|
749
|
+
// Run the workflow in the QuickJS VM
|
|
750
|
+
runtimeLogger.debug('QuickJS runtime: invoking VM', {
|
|
751
|
+
workflowRunId: runId,
|
|
752
|
+
workflowId,
|
|
753
|
+
eventCount: events.length,
|
|
754
|
+
});
|
|
755
|
+
const session = await startQuickJSWorkflow({
|
|
756
|
+
// Pass the STRIPPED bundle to the VM so the inline source map
|
|
757
|
+
// doesn't end up in the QuickJS heap. The original (unstripped)
|
|
758
|
+
// `workflowCode` is still kept in this host-side scope and is used
|
|
759
|
+
// by `remapErrorStack` on workflow failures below.
|
|
760
|
+
workflowCode: workflowCodeForVM,
|
|
761
|
+
workflowId,
|
|
762
|
+
workflowRun,
|
|
763
|
+
events,
|
|
764
|
+
worldCapabilities: world.capabilities,
|
|
765
|
+
encryptionKey,
|
|
766
|
+
port,
|
|
767
|
+
runInput,
|
|
768
|
+
});
|
|
769
|
+
let result = session.result;
|
|
770
|
+
runtimeLogger.debug('QuickJS runtime: VM returned', {
|
|
771
|
+
workflowRunId: runId,
|
|
772
|
+
completed: !!result.completed,
|
|
773
|
+
suspended: !!result.suspended,
|
|
774
|
+
failed: !!result.failed,
|
|
775
|
+
pendingOpsCount: result.suspended?.pendingOperations?.length,
|
|
776
|
+
});
|
|
777
|
+
wfdiag('vm_returned', {
|
|
778
|
+
outcome: result.completed
|
|
779
|
+
? 'completed'
|
|
780
|
+
: result.suspended
|
|
781
|
+
? 'suspended'
|
|
782
|
+
: result.failed
|
|
783
|
+
? 'failed'
|
|
784
|
+
: 'unknown',
|
|
785
|
+
pendingOpsCount: result.suspended?.pendingOperations?.length ?? 0,
|
|
786
|
+
pendingOpSummary: result.suspended?.pendingOperations?.map((p) => ({
|
|
787
|
+
type: p.type,
|
|
788
|
+
correlationId: p.correlationId,
|
|
789
|
+
hasCreatedEvent: p.hasCreatedEvent,
|
|
790
|
+
...(p.type === 'step' ? { stepId: p.stepId } : {}),
|
|
791
|
+
})),
|
|
792
|
+
failureMessage: result.failed?.message,
|
|
793
|
+
failureName: result.failed?.name,
|
|
794
|
+
});
|
|
795
|
+
// ---- Inline continuation loop ----
|
|
796
|
+
//
|
|
797
|
+
// While the workflow is suspended, this loop keeps the VM alive and
|
|
798
|
+
// makes as much forward progress as possible within one invocation:
|
|
799
|
+
//
|
|
800
|
+
// 1. Dispatch durable side effects for the suspension's pending ops
|
|
801
|
+
// (step_created / hook_created / attr_set / wait_created /
|
|
802
|
+
// hook_received for aborts) and complete elapsed waits.
|
|
803
|
+
// 2. Feed all newly recorded events (attr_set, hook_created, elapsed
|
|
804
|
+
// wait_completed, terminals written by concurrent invocations, ...)
|
|
805
|
+
// into the LIVE VM via session.continueWithEvents, resuming
|
|
806
|
+
// execution exactly where it left off, no fresh-VM re-replay.
|
|
807
|
+
// Cheap progress is fed BEFORE running step bodies so promise
|
|
808
|
+
// chains that are not gated on steps (hook.getConflict(),
|
|
809
|
+
// setAttributes(), racing sleeps) advance first and can surface
|
|
810
|
+
// additional pending steps for the same inline batch.
|
|
811
|
+
// 3. Once no cheap progress remains, execute up to
|
|
812
|
+
// getMaxInlineSteps() steps created by THIS invocation inline (no
|
|
813
|
+
// queue round-trip), in parallel, with the replay budget paused
|
|
814
|
+
// during step bodies, mirroring the node:vm engine's inline
|
|
815
|
+
// replay loop. Overflow and retry/throttled steps are queued for
|
|
816
|
+
// background execution. A delayed wait-continuation message is
|
|
817
|
+
// enqueued for the soonest pending wait first, so racing timers
|
|
818
|
+
// fire on time (in a separate invocation) while step bodies block
|
|
819
|
+
// this one.
|
|
820
|
+
//
|
|
821
|
+
// The loop exits when the workflow settles, no forward progress is
|
|
822
|
+
// possible in-process, the replay budget is exhausted, or the run is
|
|
823
|
+
// gone.
|
|
824
|
+
const seenEventIds = new Set();
|
|
825
|
+
for (const e of events) {
|
|
826
|
+
if (e.eventId)
|
|
827
|
+
seenEventIds.add(e.eventId);
|
|
828
|
+
}
|
|
829
|
+
// Step cids already executed inline by this invocation.
|
|
830
|
+
const executedStepIds = new Set();
|
|
831
|
+
// Steps for which THIS invocation already sent a queue message.
|
|
832
|
+
const queuedStepIds = new Set();
|
|
833
|
+
// Aborts THIS invocation already recorded (hook_received written).
|
|
834
|
+
// Guards against re-recording when the VM-side flag has not been
|
|
835
|
+
// cleared yet within the same iteration.
|
|
836
|
+
const recordedAbortIds = new Set();
|
|
837
|
+
// Waits for which THIS invocation already completed/scheduled work.
|
|
838
|
+
const completedWaitIds2 = new Set();
|
|
839
|
+
// Inline-ownership state per step correlationId, derived from every
|
|
840
|
+
// event this invocation observes (initial log + every feed): the
|
|
841
|
+
// quickjs analog of the replay-derived ownership on the node engine's
|
|
842
|
+
// StepInvocationQueueItem (see step-ownership.ts). Latest-wins:
|
|
843
|
+
// events arrive in log order, so a later step_started overwrites the
|
|
844
|
+
// stamp; a step_retrying lapses ownership permanently for the id.
|
|
845
|
+
const stepOwnership = new Map();
|
|
846
|
+
const observeEventsForOwnership = (observed) => {
|
|
847
|
+
for (const e of observed) {
|
|
848
|
+
if (e.correlationId === undefined)
|
|
849
|
+
continue;
|
|
850
|
+
if (e.eventType === 'step_started') {
|
|
851
|
+
const owner = 'eventData' in e &&
|
|
852
|
+
e.eventData &&
|
|
853
|
+
'ownerMessageId' in e.eventData &&
|
|
854
|
+
typeof e.eventData.ownerMessageId === 'string'
|
|
855
|
+
? e.eventData.ownerMessageId
|
|
856
|
+
: undefined;
|
|
857
|
+
const prior = stepOwnership.get(e.correlationId);
|
|
858
|
+
stepOwnership.set(e.correlationId, {
|
|
859
|
+
owner,
|
|
860
|
+
startedAtMs: e.createdAt ? +new Date(e.createdAt) : undefined,
|
|
861
|
+
sawRetrying: prior?.sawRetrying ?? false,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
else if (e.eventType === 'step_retrying') {
|
|
865
|
+
const prior = stepOwnership.get(e.correlationId);
|
|
866
|
+
stepOwnership.set(e.correlationId, {
|
|
867
|
+
...(prior ?? {}),
|
|
868
|
+
sawRetrying: true,
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
observeEventsForOwnership(events);
|
|
874
|
+
const scheduledWaitContinuations = new Set();
|
|
875
|
+
const maxInlineSteps = getMaxInlineSteps();
|
|
876
|
+
const budget = new ReplayBudget();
|
|
877
|
+
const workflowStartedAt = workflowRun.startedAt
|
|
878
|
+
? +workflowRun.startedAt
|
|
879
|
+
: Date.now();
|
|
880
|
+
const rootRunId = workflowRun.attributes?.[ROOT_RUN_ID_ATTRIBUTE] ?? runId;
|
|
881
|
+
let inlineStepsExecuted = 0;
|
|
882
|
+
let runGone = false;
|
|
883
|
+
// Set when this invocation wrote an event the workflow must consume to
|
|
884
|
+
// make progress (attr_set, getConflict-awaited hook_created) and the
|
|
885
|
+
// loop has not yet read it back, since eventually-consistent listings can
|
|
886
|
+
// return 0 new events right after a write. If it is still set when the
|
|
887
|
+
// loop exits suspended, the entrypoint requeues immediately instead of
|
|
888
|
+
// exiting awaiting_external with the unblocking event already written
|
|
889
|
+
// and nothing scheduled to read it.
|
|
890
|
+
let pendingRequeueSignal = false;
|
|
891
|
+
/** Fetch all events not yet processed by the live VM (log order). */
|
|
892
|
+
const fetchUnseenEvents = async () => {
|
|
893
|
+
const unseen = [];
|
|
894
|
+
let cursor = null;
|
|
895
|
+
let hasMore = true;
|
|
896
|
+
while (hasMore) {
|
|
897
|
+
const response = await world.events.list({
|
|
898
|
+
runId,
|
|
899
|
+
pagination: {
|
|
900
|
+
sortOrder: 'asc',
|
|
901
|
+
cursor: cursor ?? undefined,
|
|
902
|
+
limit: 1000,
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
for (const e of response.data) {
|
|
906
|
+
if (e.eventId && seenEventIds.has(e.eventId))
|
|
907
|
+
continue;
|
|
908
|
+
if (e.eventId)
|
|
909
|
+
seenEventIds.add(e.eventId);
|
|
910
|
+
unseen.push(e);
|
|
911
|
+
}
|
|
912
|
+
if (response.cursor)
|
|
913
|
+
cursor = response.cursor;
|
|
914
|
+
hasMore = response.data.length > 0 && response.cursor != null;
|
|
915
|
+
}
|
|
916
|
+
observeEventsForOwnership(unseen);
|
|
917
|
+
return unseen;
|
|
918
|
+
};
|
|
919
|
+
try {
|
|
920
|
+
let iteration = 0;
|
|
921
|
+
while (result.suspended && !runGone && !budget.isExhausted()) {
|
|
922
|
+
iteration++;
|
|
923
|
+
// Re-check the event ceiling every turn: the loop appends events on
|
|
924
|
+
// each continueWithEvents, so a single invocation can otherwise grow
|
|
925
|
+
// the log arbitrarily far past the operator's limit (the node engine
|
|
926
|
+
// re-checks per replay for the same reason). `seenEventIds` counts
|
|
927
|
+
// every event this invocation has observed: initial log + all
|
|
928
|
+
// feeds.
|
|
929
|
+
if (maxEventsLimit !== undefined && seenEventIds.size >= maxEventsLimit) {
|
|
930
|
+
throw new MaxEventsExceededError(seenEventIds.size, maxEventsLimit);
|
|
931
|
+
}
|
|
932
|
+
const pendingOperations = result.suspended.pendingOperations;
|
|
933
|
+
// Select this turn's inline candidates BEFORE dispatch: fresh steps
|
|
934
|
+
// (no step_created yet) that this invocation hasn't already handled.
|
|
935
|
+
// Their step_created is deliberately NOT written by dispatch: the
|
|
936
|
+
// inline claim below is a lazy step_started carrying the input,
|
|
937
|
+
// which the world applies as an atomic create-claim. A concurrent
|
|
938
|
+
// invocation racing on the same fresh step loses that claim with
|
|
939
|
+
// EntityConflictError and skips, so step bodies cannot double-run
|
|
940
|
+
// (previously both invocations bare-started the step after one lost
|
|
941
|
+
// the swallowed step_created race).
|
|
942
|
+
const freshSteps = pendingOperations.filter((op) => op.type === 'step' &&
|
|
943
|
+
!op.hasCreatedEvent &&
|
|
944
|
+
!executedStepIds.has(op.correlationId) &&
|
|
945
|
+
!queuedStepIds.has(op.correlationId));
|
|
946
|
+
// Steps whose input refused to serialize (see
|
|
947
|
+
// PendingStep.serializationError) never execute: they must not be
|
|
948
|
+
// inline-claimed (a lazy step_started would need the input that
|
|
949
|
+
// failed) nor queued. Dispatch below finalizes them as step_created
|
|
950
|
+
// + step_failed instead; only healthy steps compete for inline
|
|
951
|
+
// slots and overflow.
|
|
952
|
+
const healthySteps = freshSteps.filter((step) => !step.serializationError);
|
|
953
|
+
const inlineCandidates = maxInlineSteps <= 0 ? [] : healthySteps.slice(0, maxInlineSteps);
|
|
954
|
+
const inlineClaimCids = new Set(inlineCandidates.map((step) => step.correlationId));
|
|
955
|
+
// 1. Durable side effects for this suspension's pending ops.
|
|
956
|
+
const opsToDispatch = pendingOperations.map((op) => op.type === 'hook' &&
|
|
957
|
+
op.abortRequested &&
|
|
958
|
+
recordedAbortIds.has(op.correlationId)
|
|
959
|
+
? { ...op, abortRequested: false }
|
|
960
|
+
: op);
|
|
961
|
+
for (const op of pendingOperations) {
|
|
962
|
+
if (op.type === 'hook' && op.abortRequested) {
|
|
963
|
+
recordedAbortIds.add(op.correlationId);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
// Steps beyond the inline cap are handed to the queue in the same
|
|
967
|
+
// turn their step_created is written. Where eligible, the dispatch
|
|
968
|
+
// below parallelizes each overflow step's step_created write with
|
|
969
|
+
// its queue publish (resilient step dispatch: the message carries
|
|
970
|
+
// `stepInput` so the consumer can re-ensure the event); the rest
|
|
971
|
+
// are queued right after, in parallel. This must all happen BEFORE
|
|
972
|
+
// the event feed below: the feed always observes those
|
|
973
|
+
// step_created writes as unseen events and `continue`s, so a
|
|
974
|
+
// handoff placed after it is unreachable on the only iteration
|
|
975
|
+
// that still classifies these steps as fresh: next turn they carry
|
|
976
|
+
// hasCreatedEvent and would never be queued at all (the wedge behind
|
|
977
|
+
// promiseRaceStressTestWorkflow hanging in the quickjs CI legs). The
|
|
978
|
+
// step-identity-scoped idempotency key makes repeats harmless.
|
|
979
|
+
const overflowSteps = healthySteps.slice(inlineCandidates.length);
|
|
980
|
+
const dispatched = await dispatchPendingOps({
|
|
981
|
+
world,
|
|
982
|
+
runId,
|
|
983
|
+
workflowRun,
|
|
984
|
+
encryptionKey,
|
|
985
|
+
namespace,
|
|
986
|
+
nextTraceCarrier,
|
|
987
|
+
pendingOperations: opsToDispatch,
|
|
988
|
+
skipStepCreation: inlineClaimCids,
|
|
989
|
+
queueStepCids: new Set(overflowSteps.map((s) => s.correlationId)),
|
|
990
|
+
finalizeUnserializableSteps: true,
|
|
991
|
+
wfdiag,
|
|
992
|
+
});
|
|
993
|
+
if (dispatched.createdAttributeEvent ||
|
|
994
|
+
dispatched.createdGetConflictHook) {
|
|
995
|
+
pendingRequeueSignal = true;
|
|
996
|
+
}
|
|
997
|
+
// A finalized unserializable step has terminal events durably
|
|
998
|
+
// written but no execution message anywhere: if the feed below
|
|
999
|
+
// doesn't surface them (eventually-consistent listing) and the loop
|
|
1000
|
+
// exits, nothing would ever re-invoke the run to observe the
|
|
1001
|
+
// failure. Raise the requeue signal (same mechanism as inline
|
|
1002
|
+
// terminals) and mark the steps handled so later turns don't
|
|
1003
|
+
// re-finalize or backstop-queue them.
|
|
1004
|
+
if (dispatched.failedSerializationStepCids.size > 0) {
|
|
1005
|
+
pendingRequeueSignal = true;
|
|
1006
|
+
for (const cid of dispatched.failedSerializationStepCids) {
|
|
1007
|
+
executedStepIds.add(cid);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
for (const cid of dispatched.queuedStepCids) {
|
|
1011
|
+
queuedStepIds.add(cid);
|
|
1012
|
+
}
|
|
1013
|
+
await Promise.all(overflowSteps
|
|
1014
|
+
.filter((step) => !dispatched.queuedStepCids.has(step.correlationId))
|
|
1015
|
+
.map((step) => {
|
|
1016
|
+
queuedStepIds.add(step.correlationId);
|
|
1017
|
+
return queueStepMessage({
|
|
1018
|
+
world,
|
|
1019
|
+
runId,
|
|
1020
|
+
workflowRun,
|
|
1021
|
+
step,
|
|
1022
|
+
namespace,
|
|
1023
|
+
nextTraceCarrier,
|
|
1024
|
+
purpose: 'dispatch',
|
|
1025
|
+
wfdiag,
|
|
1026
|
+
});
|
|
1027
|
+
}));
|
|
1028
|
+
// Complete elapsed waits so their wait_completed events are picked
|
|
1029
|
+
// up by the feed below (instead of a queue re-invocation).
|
|
1030
|
+
const waitCompletePromises = [];
|
|
1031
|
+
for (const op of pendingOperations) {
|
|
1032
|
+
if (op.type !== 'wait')
|
|
1033
|
+
continue;
|
|
1034
|
+
const wait = op;
|
|
1035
|
+
if (completedWaitIds2.has(wait.correlationId))
|
|
1036
|
+
continue;
|
|
1037
|
+
if (new Date(wait.resumeAt).getTime() - Date.now() > 0)
|
|
1038
|
+
continue;
|
|
1039
|
+
completedWaitIds2.add(wait.correlationId);
|
|
1040
|
+
waitCompletePromises.push((async () => {
|
|
1041
|
+
try {
|
|
1042
|
+
await world.events.create(runId, {
|
|
1043
|
+
eventType: 'wait_completed',
|
|
1044
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
1045
|
+
correlationId: wait.correlationId,
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
catch (err) {
|
|
1049
|
+
if (EntityConflictError.is(err))
|
|
1050
|
+
return;
|
|
1051
|
+
throw err;
|
|
1052
|
+
}
|
|
1053
|
+
})());
|
|
1054
|
+
}
|
|
1055
|
+
if (waitCompletePromises.length > 0) {
|
|
1056
|
+
await Promise.all(waitCompletePromises);
|
|
1057
|
+
}
|
|
1058
|
+
// 2. Cheap progress first: feed newly recorded events into the live
|
|
1059
|
+
// VM before blocking on step bodies.
|
|
1060
|
+
{
|
|
1061
|
+
const newEvents = await fetchUnseenEvents();
|
|
1062
|
+
if (newEvents.length > 0) {
|
|
1063
|
+
// The listing caught up with this invocation's writes, so any
|
|
1064
|
+
// attr_set / getConflict hook_created has been (or is being)
|
|
1065
|
+
// consumed by the live VM, so no external requeue is needed.
|
|
1066
|
+
pendingRequeueSignal = false;
|
|
1067
|
+
result = await session.continueWithEvents(newEvents);
|
|
1068
|
+
wfdiag('inline_iteration', {
|
|
1069
|
+
iteration,
|
|
1070
|
+
phase: 'feed',
|
|
1071
|
+
fedEvents: newEvents.length,
|
|
1072
|
+
outcome: result.completed
|
|
1073
|
+
? 'completed'
|
|
1074
|
+
: result.failed
|
|
1075
|
+
? 'failed'
|
|
1076
|
+
: 'suspended',
|
|
1077
|
+
});
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// 3. No cheap progress left, so execute steps inline.
|
|
1082
|
+
const stepOps = pendingOperations.filter((op) => op.type === 'step');
|
|
1083
|
+
// Steps created by an EARLIER invocation (or an earlier turn) that
|
|
1084
|
+
// are still pending, with no work owned by THIS invocation. Mirror
|
|
1085
|
+
// the node engine's ownership decision table (step-ownership.ts),
|
|
1086
|
+
// NOT a deliveryAttempt gate: worlds advance the attempt counter on
|
|
1087
|
+
// routine redeliveries (world-local counts every handled response),
|
|
1088
|
+
// so attempt > 1 is the common case and would fire backstops at
|
|
1089
|
+
// steps actively executing inline in a live invocation.
|
|
1090
|
+
//
|
|
1091
|
+
// - Ownership lease ACTIVE, held by ANOTHER message → the step is
|
|
1092
|
+
// (presumably) executing inline in a live invocation. Arm a
|
|
1093
|
+
// DELAYED backstop for the lease remainder, keyed to the
|
|
1094
|
+
// ownership epoch (a refreshed lease re-arms a fresh backstop
|
|
1095
|
+
// instead of deduping against the in-flight one). If the owner
|
|
1096
|
+
// completes normally, the backstop delivery resolves the step
|
|
1097
|
+
// as 'skipped'.
|
|
1098
|
+
// - Ownership lease ACTIVE, held by THIS message → this delivery
|
|
1099
|
+
// is the owner's redelivery; the claimant crashed
|
|
1100
|
+
// mid-execution. Dispatch immediately for background recovery.
|
|
1101
|
+
// - No stamp / lease EXPIRED / step_retrying observed → the step
|
|
1102
|
+
// is queue-owned or orphaned. Dispatch immediately; the
|
|
1103
|
+
// step-identity-scoped idempotency key dedupes against the
|
|
1104
|
+
// original handoff.
|
|
1105
|
+
const nowMs = Date.now();
|
|
1106
|
+
for (const step of stepOps) {
|
|
1107
|
+
if (!step.hasCreatedEvent)
|
|
1108
|
+
continue;
|
|
1109
|
+
if (executedStepIds.has(step.correlationId))
|
|
1110
|
+
continue;
|
|
1111
|
+
if (queuedStepIds.has(step.correlationId))
|
|
1112
|
+
continue;
|
|
1113
|
+
const ownership = stepOwnership.get(step.correlationId);
|
|
1114
|
+
const ownershipActive = ownership !== undefined &&
|
|
1115
|
+
ownership.owner !== undefined &&
|
|
1116
|
+
!ownership.sawRetrying;
|
|
1117
|
+
let leaseRemainingSeconds = 0;
|
|
1118
|
+
if (ownershipActive && ownership.startedAtMs !== undefined) {
|
|
1119
|
+
const leaseSeconds = getInlineOwnershipLeaseSeconds();
|
|
1120
|
+
leaseRemainingSeconds = Math.min(leaseSeconds, Math.max(0, Math.ceil((ownership.startedAtMs + leaseSeconds * 1000 - nowMs) / 1000)));
|
|
1121
|
+
}
|
|
1122
|
+
if (ownershipActive &&
|
|
1123
|
+
ownership.owner !== ownerMessageId &&
|
|
1124
|
+
leaseRemainingSeconds > 0) {
|
|
1125
|
+
queuedStepIds.add(step.correlationId);
|
|
1126
|
+
await queueStepMessage({
|
|
1127
|
+
world,
|
|
1128
|
+
runId,
|
|
1129
|
+
workflowRun,
|
|
1130
|
+
step,
|
|
1131
|
+
delaySeconds: leaseRemainingSeconds,
|
|
1132
|
+
namespace,
|
|
1133
|
+
nextTraceCarrier,
|
|
1134
|
+
purpose: `backstop:${ownership.startedAtMs}`,
|
|
1135
|
+
wfdiag,
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
queuedStepIds.add(step.correlationId);
|
|
1140
|
+
await queueStepMessage({
|
|
1141
|
+
world,
|
|
1142
|
+
runId,
|
|
1143
|
+
workflowRun,
|
|
1144
|
+
step,
|
|
1145
|
+
namespace,
|
|
1146
|
+
nextTraceCarrier,
|
|
1147
|
+
purpose: 'dispatch',
|
|
1148
|
+
wfdiag,
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (inlineCandidates.length === 0) {
|
|
1153
|
+
// No in-process progress possible: the run awaits an external
|
|
1154
|
+
// stimulus (hook payload, queued step, wait timer).
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
// Racing timers must fire on time while step bodies block this
|
|
1158
|
+
// invocation: enqueue a delayed continuation for the soonest
|
|
1159
|
+
// pending wait (a separate invocation writes its wait_completed at
|
|
1160
|
+
// the right log position, the same mechanism as the node:vm engine's
|
|
1161
|
+
// wait-continuation dispatch).
|
|
1162
|
+
let soonestWait;
|
|
1163
|
+
for (const op of pendingOperations) {
|
|
1164
|
+
if (op.type !== 'wait')
|
|
1165
|
+
continue;
|
|
1166
|
+
const wait = op;
|
|
1167
|
+
if (scheduledWaitContinuations.has(wait.correlationId))
|
|
1168
|
+
continue;
|
|
1169
|
+
// Waits whose wait_completed THIS invocation already wrote (the
|
|
1170
|
+
// elapsed-wait pass above) are done: the event just hasn't fed
|
|
1171
|
+
// back into the VM yet. No continuation needed.
|
|
1172
|
+
if (completedWaitIds2.has(wait.correlationId))
|
|
1173
|
+
continue;
|
|
1174
|
+
const resumeMs = new Date(wait.resumeAt).getTime() - Date.now();
|
|
1175
|
+
// An already-elapsed wait MUST still get a continuation (clamped
|
|
1176
|
+
// to the 1s minimum, exactly like the node engine's
|
|
1177
|
+
// `Math.max(1000, resumeAtMs - now)`), not be skipped: a wait
|
|
1178
|
+
// whose deadline falls between this iteration's elapsed-wait
|
|
1179
|
+
// pass (which saw it as still pending and wrote nothing) and
|
|
1180
|
+
// this sweep would otherwise get NEITHER a wait_completed NOR a
|
|
1181
|
+
// continuation, and the inline batch below then blocks this
|
|
1182
|
+
// invocation for the full step duration with no wake armed
|
|
1183
|
+
// anywhere. For `Promise.race(step, sleep)` that silently hands
|
|
1184
|
+
// the race to the step: the sleep's wait_completed is never
|
|
1185
|
+
// written and the run completes with the wrong winner. The
|
|
1186
|
+
// window between the two checks spans this iteration's dispatch
|
|
1187
|
+
// + feed round-trips, so on network-backed worlds (world-vercel)
|
|
1188
|
+
// a short sleep lands in it routinely, observed as a ~50%
|
|
1189
|
+
// sleepWinsRaceWorkflow failure rate in the Vercel e2e legs,
|
|
1190
|
+
// while world-local's sub-ms round-trips masked it locally. The
|
|
1191
|
+
// continuation invocation's pre-VM elapsed check writes the
|
|
1192
|
+
// wait_completed ~1s later.
|
|
1193
|
+
const seconds = Math.max(1, Math.ceil(resumeMs / 1000));
|
|
1194
|
+
if (!soonestWait || seconds < soonestWait.seconds) {
|
|
1195
|
+
soonestWait = { correlationId: wait.correlationId, seconds };
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
if (soonestWait) {
|
|
1199
|
+
scheduledWaitContinuations.add(soonestWait.correlationId);
|
|
1200
|
+
const attempt = nextWaitContinuationAttempt(soonestWait.correlationId);
|
|
1201
|
+
await queueMessage(world, getWorkflowQueueName(workflowRun.workflowName, namespace), {
|
|
1202
|
+
runId,
|
|
1203
|
+
traceCarrier: await nextTraceCarrier(),
|
|
1204
|
+
requestedAt: new Date(),
|
|
1205
|
+
waitContinuation: {
|
|
1206
|
+
correlationId: soonestWait.correlationId,
|
|
1207
|
+
attempt,
|
|
1208
|
+
},
|
|
1209
|
+
}, getWaitContinuationDispatch(soonestWait.seconds, soonestWait.correlationId, Date.now(), attempt));
|
|
1210
|
+
wfdiag('wait_continuation_scheduled', {
|
|
1211
|
+
correlationId: soonestWait.correlationId,
|
|
1212
|
+
delaySeconds: soonestWait.seconds,
|
|
1213
|
+
attempt,
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
// Execute the inline batch in parallel. The replay budget is
|
|
1217
|
+
// paused while step bodies run: step duration is bounded by the
|
|
1218
|
+
// platform function duration, not the replay timeout. NOTE (by
|
|
1219
|
+
// design): with the budget parked per batch, the only bound on how
|
|
1220
|
+
// many inline steps one invocation can chain is the platform's
|
|
1221
|
+
// function timeout: the SDK deliberately imposes no cap of its
|
|
1222
|
+
// own, matching the node:vm engine, where a long sequential
|
|
1223
|
+
// workflow likewise runs step-by-step until the platform reclaims
|
|
1224
|
+
// the invocation and a redelivery resumes from the log.
|
|
1225
|
+
budget.pause();
|
|
1226
|
+
let outcomes;
|
|
1227
|
+
try {
|
|
1228
|
+
outcomes = await Promise.all(inlineCandidates.map((step) => runStepSingleFlight(runId, step.correlationId, () => (async () => executeStep({
|
|
1229
|
+
world,
|
|
1230
|
+
workflowRunId: runId,
|
|
1231
|
+
workflowDeploymentId: workflowRun.deploymentId,
|
|
1232
|
+
workflowName: workflowRun.workflowName,
|
|
1233
|
+
workflowStartedAt,
|
|
1234
|
+
requestId,
|
|
1235
|
+
rootRunId,
|
|
1236
|
+
stepId: step.correlationId,
|
|
1237
|
+
stepName: step.stepId,
|
|
1238
|
+
encryptionKey,
|
|
1239
|
+
runSpecVersion: workflowRun.specVersion,
|
|
1240
|
+
// Lazy inline claim: step_created is deferred (dispatch
|
|
1241
|
+
// skipped it) and this step_started carries the input,
|
|
1242
|
+
// so the world creates the step atomically:
|
|
1243
|
+
// exactly-one-owner. A concurrent claimant gets
|
|
1244
|
+
// EntityConflictError → { type: 'skipped' } and never
|
|
1245
|
+
// runs the body. Mirrors the node engine's inline path.
|
|
1246
|
+
lazyStepInput: await encryptSerializedData(step.input, encryptionKey),
|
|
1247
|
+
// Ownership stamp: wake replays see the body as in
|
|
1248
|
+
// flight in this invocation and arm a delayed backstop
|
|
1249
|
+
// instead of immediately requeueing the step.
|
|
1250
|
+
ownerMessageId,
|
|
1251
|
+
// A lazy step is brand-new by construction: first
|
|
1252
|
+
// attempt.
|
|
1253
|
+
authoritativeAttempt: 1,
|
|
1254
|
+
}))())));
|
|
1255
|
+
}
|
|
1256
|
+
finally {
|
|
1257
|
+
budget.resume();
|
|
1258
|
+
}
|
|
1259
|
+
inlineStepsExecuted += inlineCandidates.length;
|
|
1260
|
+
for (let i = 0; i < inlineCandidates.length; i++) {
|
|
1261
|
+
const step = inlineCandidates[i];
|
|
1262
|
+
const outcome = outcomes[i];
|
|
1263
|
+
executedStepIds.add(step.correlationId);
|
|
1264
|
+
if (outcome.type === 'retry' || outcome.type === 'throttled') {
|
|
1265
|
+
// Hand the step to the queue with the requested backoff:
|
|
1266
|
+
// background delivery drives the retry from here.
|
|
1267
|
+
queuedStepIds.add(step.correlationId);
|
|
1268
|
+
await queueStepMessage({
|
|
1269
|
+
world,
|
|
1270
|
+
runId,
|
|
1271
|
+
workflowRun,
|
|
1272
|
+
step,
|
|
1273
|
+
delaySeconds: outcome.timeoutSeconds,
|
|
1274
|
+
namespace,
|
|
1275
|
+
nextTraceCarrier,
|
|
1276
|
+
// Suffixed key: this step was inline-claimed, so no dispatch
|
|
1277
|
+
// publish exists under the dispatch key, but suffixing
|
|
1278
|
+
// keeps the retry enqueueable even if a world retired a
|
|
1279
|
+
// historical key for this step (see the purpose docs above).
|
|
1280
|
+
purpose: 'retry:1',
|
|
1281
|
+
wfdiag,
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
else if (outcome.type === 'gone') {
|
|
1285
|
+
runGone = true;
|
|
1286
|
+
}
|
|
1287
|
+
// 'skipped': a concurrent invocation won the lazy create-claim and
|
|
1288
|
+
// owns the body. Marked executed above so this invocation never
|
|
1289
|
+
// re-claims it; the winner's terminal events arrive via the feed
|
|
1290
|
+
// (or drive a separate invocation).
|
|
1291
|
+
}
|
|
1292
|
+
wfdiag('inline_steps_executed', {
|
|
1293
|
+
iteration,
|
|
1294
|
+
count: inlineCandidates.length,
|
|
1295
|
+
outcomes: outcomes.map((o) => o.type),
|
|
1296
|
+
});
|
|
1297
|
+
// Feed the inline batch's terminal events into the live VM. When
|
|
1298
|
+
// the eventually-consistent listing has not surfaced them yet,
|
|
1299
|
+
// exiting must NOT ack silently: the terminals this invocation just
|
|
1300
|
+
// caused are durably written with no queue message left to consume
|
|
1301
|
+
// them (inline steps have none), so an awaiting_external exit would
|
|
1302
|
+
// park the run 'running' with all its steps complete. Raise the
|
|
1303
|
+
// requeue signal so the suspended exit schedules a fresh immediate
|
|
1304
|
+
// invocation whose fresh read picks the terminals up. Outcomes that
|
|
1305
|
+
// wrote no terminal ('skipped': a concurrent claimant owns the
|
|
1306
|
+
// body; 'gone', retry/throttled: a queue message exists) don't
|
|
1307
|
+
// need it, but signaling on them too only costs a no-op invocation
|
|
1308
|
+
// in an already-rare lag window.
|
|
1309
|
+
const newEvents = await fetchUnseenEvents();
|
|
1310
|
+
if (newEvents.length === 0) {
|
|
1311
|
+
pendingRequeueSignal = true;
|
|
1312
|
+
break;
|
|
1313
|
+
}
|
|
1314
|
+
result = await session.continueWithEvents(newEvents);
|
|
1315
|
+
wfdiag('inline_iteration', {
|
|
1316
|
+
iteration,
|
|
1317
|
+
phase: 'steps',
|
|
1318
|
+
fedEvents: newEvents.length,
|
|
1319
|
+
outcome: result.completed
|
|
1320
|
+
? 'completed'
|
|
1321
|
+
: result.failed
|
|
1322
|
+
? 'failed'
|
|
1323
|
+
: 'suspended',
|
|
1324
|
+
budgetExhausted: budget.isExhausted(),
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
finally {
|
|
1329
|
+
session.dispose();
|
|
1330
|
+
}
|
|
1331
|
+
parentSpan?.setAttributes({
|
|
1332
|
+
...Attribute.QuickJSInlineSteps(inlineStepsExecuted),
|
|
1333
|
+
});
|
|
1334
|
+
if (result.completed) {
|
|
1335
|
+
// Workflow completed
|
|
1336
|
+
runtimeLogger.info('QuickJS runtime: workflow completed', {
|
|
1337
|
+
workflowRunId: runId,
|
|
1338
|
+
});
|
|
1339
|
+
parentSpan?.setAttributes({
|
|
1340
|
+
...Attribute.QuickJSOutcome('completed'),
|
|
1341
|
+
});
|
|
1342
|
+
// Flush leftover pending side effects (abort recordings, system-hook
|
|
1343
|
+
// disposals, fire-and-forget attribute/hook events) BEFORE writing
|
|
1344
|
+
// run_completed. Mirrors the node:vm engine's drainPendingQueueItems.
|
|
1345
|
+
// Drain failures are swallowed: the workflow's own outcome is the
|
|
1346
|
+
// source of truth.
|
|
1347
|
+
if (result.completed.drainOperations?.length) {
|
|
1348
|
+
try {
|
|
1349
|
+
await dispatchPendingOps({
|
|
1350
|
+
world,
|
|
1351
|
+
runId,
|
|
1352
|
+
workflowRun,
|
|
1353
|
+
encryptionKey,
|
|
1354
|
+
namespace,
|
|
1355
|
+
nextTraceCarrier,
|
|
1356
|
+
pendingOperations: result.completed.drainOperations,
|
|
1357
|
+
wfdiag,
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
catch (err) {
|
|
1361
|
+
runtimeLogger.warn('QuickJS runtime: terminal drain failed', {
|
|
1362
|
+
workflowRunId: runId,
|
|
1363
|
+
message: err?.message,
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
// Create run_completed event.
|
|
1368
|
+
// The VM serializes the workflow result as format-prefixed devalue bytes
|
|
1369
|
+
// ("devl" + devalue) with no encryption (the VM has no access to the
|
|
1370
|
+
// CryptoKey). Host-side encryption is applied here so that `run_completed`
|
|
1371
|
+
// events have the same `encr`-prefixed payload shape that the node:vm
|
|
1372
|
+
// engine's `dehydrateWorkflowReturnValue` produces.
|
|
1373
|
+
try {
|
|
1374
|
+
await world.events.create(runId, {
|
|
1375
|
+
eventType: 'run_completed',
|
|
1376
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
1377
|
+
eventData: {
|
|
1378
|
+
output: await encryptSerializedData(result.completed.result, encryptionKey),
|
|
1379
|
+
},
|
|
1380
|
+
});
|
|
1381
|
+
wfdiag('exit_completed', { result: 'run_completed_written' });
|
|
1382
|
+
}
|
|
1383
|
+
catch (err) {
|
|
1384
|
+
if (EntityConflictError.is(err) || RunExpiredError.is(err)) {
|
|
1385
|
+
runtimeLogger.warn('Workflow already finished, skipping run_completed', { workflowRunId: runId });
|
|
1386
|
+
wfdiag('exit_completed', { result: 'already_finished' });
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
wfdiag('exit_completed_error', {
|
|
1390
|
+
errorName: err?.name,
|
|
1391
|
+
errorMessage: err?.message,
|
|
1392
|
+
});
|
|
1393
|
+
throw err;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
else if (result.suspended) {
|
|
1397
|
+
// Workflow still suspended after the inline loop. All durable side
|
|
1398
|
+
// effects for the final suspension state were already dispatched by
|
|
1399
|
+
// the loop; what remains is deciding how the run gets re-invoked.
|
|
1400
|
+
const { pendingOperations } = result.suspended;
|
|
1401
|
+
runtimeLogger.info('QuickJS runtime: workflow suspended', {
|
|
1402
|
+
workflowRunId: runId,
|
|
1403
|
+
inlineStepsExecuted,
|
|
1404
|
+
pendingSteps: pendingOperations.filter((p) => p.type === 'step').length,
|
|
1405
|
+
pendingWaits: pendingOperations.filter((p) => p.type === 'wait').length,
|
|
1406
|
+
pendingOps: pendingOperations.map((p) => ({
|
|
1407
|
+
type: p.type,
|
|
1408
|
+
correlationId: p.correlationId,
|
|
1409
|
+
hasCreatedEvent: p.hasCreatedEvent,
|
|
1410
|
+
...(p.type === 'step' ? { stepId: p.stepId } : {}),
|
|
1411
|
+
})),
|
|
1412
|
+
});
|
|
1413
|
+
parentSpan?.setAttributes({
|
|
1414
|
+
...Attribute.QuickJSOutcome('suspended'),
|
|
1415
|
+
...Attribute.QuickJSPendingOpsCount(pendingOperations.length),
|
|
1416
|
+
});
|
|
1417
|
+
if (runGone) {
|
|
1418
|
+
// The run no longer exists (expired / deleted), so nothing to drive.
|
|
1419
|
+
wfdiag('exit_suspended', { action: 'run_gone' });
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
// Exit requeues are FRESH messages, never a `{ timeoutSeconds }`
|
|
1423
|
+
// visibility-redelivery of the current message. Redelivering the
|
|
1424
|
+
// CURRENT message is a trap: a hook-resume delivery carries
|
|
1425
|
+
// `hookInput`, and its redelivery re-runs the lazy-resume re-ensure
|
|
1426
|
+
// in the handler prologue: if the workflow disposed that hook
|
|
1427
|
+
// during this invocation (dispose → sleep), a world that rejects
|
|
1428
|
+
// the re-ensure would ack the message as "nothing left to resume"
|
|
1429
|
+
// and the continuation it carried is silently lost. A fresh message
|
|
1430
|
+
// carries only `runId`, so its delivery always reaches replay (and
|
|
1431
|
+
// under turbo a reschedule would re-engage turbo against a stale
|
|
1432
|
+
// preloaded log: see the reinvoke() docs in runtime.ts).
|
|
1433
|
+
const requeueImmediately = async () => {
|
|
1434
|
+
await queueMessage(world, getWorkflowQueueName(workflowRun.workflowName, namespace), {
|
|
1435
|
+
runId,
|
|
1436
|
+
traceCarrier: await nextTraceCarrier(),
|
|
1437
|
+
requestedAt: new Date(),
|
|
1438
|
+
});
|
|
1439
|
+
};
|
|
1440
|
+
if (budget.isExhausted()) {
|
|
1441
|
+
// The loop stopped on the replay budget with progress still
|
|
1442
|
+
// possible, so continue in a fresh invocation.
|
|
1443
|
+
wfdiag('exit_suspended', { action: 'budget_exhausted_requeue' });
|
|
1444
|
+
await requeueImmediately();
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
// Exit wait sweep. A wait that elapsed in the window since the
|
|
1448
|
+
// loop's last check requeues immediately (its wait_completed is
|
|
1449
|
+
// written by the next invocation's elapsed-wait pass); pending waits
|
|
1450
|
+
// whose continuation the loop already enqueued are skipped.
|
|
1451
|
+
let soonestWait;
|
|
1452
|
+
let hasElapsedWait = false;
|
|
1453
|
+
for (const op of pendingOperations) {
|
|
1454
|
+
if (op.type !== 'wait')
|
|
1455
|
+
continue;
|
|
1456
|
+
const wait = op;
|
|
1457
|
+
const resumeMs = new Date(wait.resumeAt).getTime() - Date.now();
|
|
1458
|
+
if (resumeMs <= 0) {
|
|
1459
|
+
hasElapsedWait = true;
|
|
1460
|
+
}
|
|
1461
|
+
else if (!scheduledWaitContinuations.has(wait.correlationId)) {
|
|
1462
|
+
const timeoutSeconds = Math.max(1, Math.ceil(resumeMs / 1000));
|
|
1463
|
+
if (!soonestWait || timeoutSeconds < soonestWait.seconds) {
|
|
1464
|
+
soonestWait = {
|
|
1465
|
+
seconds: timeoutSeconds,
|
|
1466
|
+
correlationId: wait.correlationId,
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
if (hasElapsedWait) {
|
|
1472
|
+
wfdiag('exit_suspended', { action: 'wait_elapsed_requeue' });
|
|
1473
|
+
await requeueImmediately();
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (pendingRequeueSignal) {
|
|
1477
|
+
// This invocation wrote events the workflow needs to consume
|
|
1478
|
+
// (attr_set / getConflict-awaited hook_created / inline step
|
|
1479
|
+
// terminals) but the eventually-consistent listing never returned
|
|
1480
|
+
// them before the loop exited. Without a requeue the run would
|
|
1481
|
+
// park awaiting_external with its unblocking events already
|
|
1482
|
+
// durably written and no future invocation coming, so requeue
|
|
1483
|
+
// immediately so a fresh read picks them up. In the common case
|
|
1484
|
+
// the loop's own feed observes the writes and clears this flag, so
|
|
1485
|
+
// this only fires when the read actually lagged.
|
|
1486
|
+
wfdiag('exit_suspended', { action: 'unread_self_write_requeue' });
|
|
1487
|
+
await requeueImmediately();
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (soonestWait) {
|
|
1491
|
+
// Delayed continuation for the soonest pending wait the loop has
|
|
1492
|
+
// not already scheduled. The dispatch helper handles delay
|
|
1493
|
+
// clamping (long waits chain across hops) and idempotency-key
|
|
1494
|
+
// dedup of re-observations of the same pending wait. See
|
|
1495
|
+
// runtime/wait-continuation.ts.
|
|
1496
|
+
wfdiag('exit_suspended', {
|
|
1497
|
+
action: 'schedule_wait_timeout',
|
|
1498
|
+
timeoutSeconds: soonestWait.seconds,
|
|
1499
|
+
waitCorrelationId: soonestWait.correlationId,
|
|
1500
|
+
});
|
|
1501
|
+
scheduledWaitContinuations.add(soonestWait.correlationId);
|
|
1502
|
+
const attempt = nextWaitContinuationAttempt(soonestWait.correlationId);
|
|
1503
|
+
await queueMessage(world, getWorkflowQueueName(workflowRun.workflowName, namespace), {
|
|
1504
|
+
runId,
|
|
1505
|
+
traceCarrier: await nextTraceCarrier(),
|
|
1506
|
+
requestedAt: new Date(),
|
|
1507
|
+
waitContinuation: {
|
|
1508
|
+
correlationId: soonestWait.correlationId,
|
|
1509
|
+
attempt,
|
|
1510
|
+
},
|
|
1511
|
+
}, getWaitContinuationDispatch(soonestWait.seconds, soonestWait.correlationId, Date.now(), attempt));
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
wfdiag('exit_suspended', {
|
|
1515
|
+
action: 'awaiting_external',
|
|
1516
|
+
pendingOpsCount: pendingOperations.length,
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
else if (result.failed) {
|
|
1520
|
+
// Workflow failed, so remap stack trace using inline source maps.
|
|
1521
|
+
// Frames carry the run's workflowId as their filename on the fresh
|
|
1522
|
+
// path, but the workflow-independent BASELINE_BUNDLE_FILENAME on the
|
|
1523
|
+
// snapshot path (the name is baked into the shared baseline's
|
|
1524
|
+
// compiled code at hydrate), so remap against both. remapErrorStack
|
|
1525
|
+
// early-exits on a cheap includes() when a filename has no frames.
|
|
1526
|
+
let errorStack = result.failed.stack;
|
|
1527
|
+
if (errorStack) {
|
|
1528
|
+
const parsedName = parseWorkflowName(workflowName);
|
|
1529
|
+
const filename = parsedName?.moduleSpecifier || workflowName;
|
|
1530
|
+
errorStack = remapErrorStack(errorStack, filename, workflowCode);
|
|
1531
|
+
errorStack = remapErrorStack(errorStack, BASELINE_BUNDLE_FILENAME, workflowCode);
|
|
1532
|
+
}
|
|
1533
|
+
// Classify the error so consumers (`run.returnValue`, observability)
|
|
1534
|
+
// get `USER_ERROR` / `RUNTIME_ERROR` on `error.cause.code`, matching
|
|
1535
|
+
// what the node:vm engine already does in runtime.ts.
|
|
1536
|
+
//
|
|
1537
|
+
// The VM serializes errors as `{ name, message, stack }`, so we
|
|
1538
|
+
// reconstruct a host-side Error of the correct class based on the
|
|
1539
|
+
// VM-side `name`: specific WorkflowRuntimeError subclasses need
|
|
1540
|
+
// to be preserved so classifyRunError() tags them as RUNTIME_ERROR.
|
|
1541
|
+
const reconstructed = result.failed.name === 'WorkflowNotRegisteredError'
|
|
1542
|
+
? new WorkflowNotRegisteredError(workflowName)
|
|
1543
|
+
: result.failed.name === 'Error'
|
|
1544
|
+
? new Error(result.failed.message)
|
|
1545
|
+
: Object.assign(new Error(result.failed.message), {
|
|
1546
|
+
name: result.failed.name,
|
|
1547
|
+
});
|
|
1548
|
+
const errorCode = classifyRunError(reconstructed);
|
|
1549
|
+
runtimeLogger.error('QuickJS runtime: workflow failed', {
|
|
1550
|
+
workflowRunId: runId,
|
|
1551
|
+
errorName: result.failed.name,
|
|
1552
|
+
errorMessage: result.failed.message,
|
|
1553
|
+
errorStack,
|
|
1554
|
+
errorCode,
|
|
1555
|
+
});
|
|
1556
|
+
parentSpan?.setAttributes({
|
|
1557
|
+
...Attribute.QuickJSOutcome('failed'),
|
|
1558
|
+
});
|
|
1559
|
+
// Flush leftover pending side effects before writing run_failed,
|
|
1560
|
+
// same drain semantics as the completed branch.
|
|
1561
|
+
if (result.failed.drainOperations?.length) {
|
|
1562
|
+
try {
|
|
1563
|
+
await dispatchPendingOps({
|
|
1564
|
+
world,
|
|
1565
|
+
runId,
|
|
1566
|
+
workflowRun,
|
|
1567
|
+
encryptionKey,
|
|
1568
|
+
namespace,
|
|
1569
|
+
nextTraceCarrier,
|
|
1570
|
+
pendingOperations: result.failed.drainOperations,
|
|
1571
|
+
wfdiag,
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
catch (err) {
|
|
1575
|
+
runtimeLogger.warn('QuickJS runtime: terminal drain failed', {
|
|
1576
|
+
workflowRunId: runId,
|
|
1577
|
+
message: err?.message,
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
// Create run_failed event. Serialize the error through the
|
|
1582
|
+
// first-class dehydration pipeline so consumers (CLI, observability,
|
|
1583
|
+
// run.returnValue) get the same hydrated value shape as the node:vm
|
|
1584
|
+
// engine emits. Two paths:
|
|
1585
|
+
// * Modern (valueBytes present): the VM-side rejection handler
|
|
1586
|
+
// serialized the original thrown value (Error subclass with
|
|
1587
|
+
// cause chain, plain object, primitive, etc.) using the VM's
|
|
1588
|
+
// workflow-serialize. Pass those bytes through directly so
|
|
1589
|
+
// type identity, cause chains, and non-Error throws survive.
|
|
1590
|
+
// Apply encryption if configured (the VM's
|
|
1591
|
+
// serializer doesn't have access to the encryption key).
|
|
1592
|
+
// * Legacy fallback: reconstruct an Error from the host-visible
|
|
1593
|
+
// {name, message, stack} fields and run it through
|
|
1594
|
+
// `dehydrateRunError`. Used when valueBytes is absent (e.g.
|
|
1595
|
+
// extractError pseudo-failures from VM bootstrap).
|
|
1596
|
+
let dehydratedError;
|
|
1597
|
+
if (result.failed.valueBytes) {
|
|
1598
|
+
// Hydrate the VM-side bytes, remap the error stack with the
|
|
1599
|
+
// host-side source map (the VM can't do this: it lacks both the
|
|
1600
|
+
// source map and `remapErrorStack`), and re-dehydrate. This
|
|
1601
|
+
// preserves the original value's type identity / cause chain
|
|
1602
|
+
// while fixing up frames to point at the user's source files.
|
|
1603
|
+
try {
|
|
1604
|
+
const hydrated = await hydrateRunError(result.failed.valueBytes, runId, undefined // VM bytes are unencrypted
|
|
1605
|
+
);
|
|
1606
|
+
if (hydrated &&
|
|
1607
|
+
typeof hydrated === 'object' &&
|
|
1608
|
+
'stack' in hydrated &&
|
|
1609
|
+
typeof hydrated.stack === 'string') {
|
|
1610
|
+
const parsedName = parseWorkflowName(workflowName);
|
|
1611
|
+
const filename = parsedName?.moduleSpecifier || workflowName;
|
|
1612
|
+
// Both filename spaces. See the failed-branch comment above.
|
|
1613
|
+
hydrated.stack = remapErrorStack(remapErrorStack(hydrated.stack, filename, workflowCode), BASELINE_BUNDLE_FILENAME, workflowCode);
|
|
1614
|
+
}
|
|
1615
|
+
// Walk the cause chain and remap nested stacks too.
|
|
1616
|
+
const seen = new WeakSet();
|
|
1617
|
+
let node = hydrated?.cause;
|
|
1618
|
+
while (node && typeof node === 'object' && !seen.has(node)) {
|
|
1619
|
+
seen.add(node);
|
|
1620
|
+
const nodeStack = node.stack;
|
|
1621
|
+
if (typeof nodeStack === 'string') {
|
|
1622
|
+
const parsedName = parseWorkflowName(workflowName);
|
|
1623
|
+
const filename = parsedName?.moduleSpecifier || workflowName;
|
|
1624
|
+
// Both filename spaces. See the failed-branch comment above.
|
|
1625
|
+
node.stack = remapErrorStack(remapErrorStack(nodeStack, filename, workflowCode), BASELINE_BUNDLE_FILENAME, workflowCode);
|
|
1626
|
+
}
|
|
1627
|
+
node = node.cause;
|
|
1628
|
+
}
|
|
1629
|
+
dehydratedError = await dehydrateRunError(hydrated, runId, encryptionKey);
|
|
1630
|
+
}
|
|
1631
|
+
catch (rehydrateErr) {
|
|
1632
|
+
// If hydration / re-dehydration fails for any reason, fall
|
|
1633
|
+
// back to passing through the original VM bytes (applying
|
|
1634
|
+
// encryption if configured). Better to lose source-mapped
|
|
1635
|
+
// frames than to lose the error entirely.
|
|
1636
|
+
runtimeLogger.warn('QuickJS runtime: failed to remap workflow error stack, passing VM bytes through', {
|
|
1637
|
+
workflowRunId: runId,
|
|
1638
|
+
message: rehydrateErr?.message,
|
|
1639
|
+
});
|
|
1640
|
+
dehydratedError = (await maybeEncrypt(result.failed.valueBytes, encryptionKey));
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
else {
|
|
1644
|
+
if (errorStack) {
|
|
1645
|
+
reconstructed.stack = errorStack;
|
|
1646
|
+
}
|
|
1647
|
+
try {
|
|
1648
|
+
dehydratedError = await dehydrateRunError(reconstructed, runId, encryptionKey);
|
|
1649
|
+
}
|
|
1650
|
+
catch (serErr) {
|
|
1651
|
+
// Fall back to a minimal payload so the run still terminates
|
|
1652
|
+
// even when the error itself contains unserializable values.
|
|
1653
|
+
runtimeLogger.warn('QuickJS runtime: failed to dehydrate run error, falling back to bare Error', { workflowRunId: runId, message: serErr?.message });
|
|
1654
|
+
dehydratedError = await dehydrateRunError(Object.assign(new Error(result.failed.message), {
|
|
1655
|
+
name: result.failed.name,
|
|
1656
|
+
}), runId, encryptionKey);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
try {
|
|
1660
|
+
await world.events.create(runId, {
|
|
1661
|
+
eventType: 'run_failed',
|
|
1662
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
1663
|
+
eventData: {
|
|
1664
|
+
error: dehydratedError,
|
|
1665
|
+
errorCode,
|
|
1666
|
+
},
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
catch (err) {
|
|
1670
|
+
if (EntityConflictError.is(err) || RunExpiredError.is(err)) {
|
|
1671
|
+
runtimeLogger.warn('Workflow already finished, skipping run_failed', {
|
|
1672
|
+
workflowRunId: runId,
|
|
1673
|
+
});
|
|
1674
|
+
wfdiag('exit_failed', { result: 'already_finished' });
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
wfdiag('exit_failed_error', {
|
|
1678
|
+
errorName: err?.name,
|
|
1679
|
+
errorMessage: err?.message,
|
|
1680
|
+
});
|
|
1681
|
+
throw err;
|
|
1682
|
+
}
|
|
1683
|
+
wfdiag('exit_failed', { result: 'run_failed_written' });
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVpY2tqcy1lbnRyeXBvaW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3J1bnRpbWUvcXVpY2tqcy1lbnRyeXBvaW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFHSCxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGlCQUFpQixFQUNqQixzQkFBc0IsRUFDdEIsZUFBZSxFQUNmLDBCQUEwQixHQUMzQixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sRUFFTCxxQkFBcUIsRUFFckIsb0JBQW9CLEVBQ3BCLDBDQUEwQyxFQUMxQyxpQ0FBaUMsR0FFbEMsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUMvRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzdDLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsT0FBTyxJQUFJLHFCQUFxQixHQUVqQyxNQUFNLGdDQUFnQyxDQUFDO0FBQ3hDLE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsc0JBQXNCLEVBQ3RCLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsWUFBWSxHQUNiLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLGVBQWUsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3pFLE9BQU8sS0FBSyxTQUFTLE1BQU0sc0NBQXNDLENBQUM7QUFDbEUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDeEQsT0FBTyxFQUNMLDhCQUE4QixFQUM5QixpQkFBaUIsRUFDakIsOEJBQThCLEVBQzlCLDhCQUE4QixHQUMvQixNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxvQkFB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RUFBRTtnQkFDL0IsU0FBUyxFQUFFLFlBQVk7Z0JBQ3ZCLFdBQVcsRUFBRSxvQkFBb0I7Z0JBQ2pDLFNBQVMsRUFBRTtvQkFDVCxLQUFLLEVBQUUsZUFBZTtvQkFDdEIsU0FBUztpQkFDVjthQUNGLENBQUMsQ0FBQztRQUNMLENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsSUFBSSxtQkFBbUIsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksZUFBZSxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMzRCxhQUFhLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxFQUFFO29CQUNuRSxhQUFhLEVBQUUsS0FBSztpQkFDckIsQ0FBQyxDQUFDO2dCQUNILE1BQU0sQ0FBQyxhQUFhLEVBQUUsRUFBRSxNQUFNLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDO2dCQUN0RCxPQUFPO1lBQ1QsQ0FBQztZQUNELE1BQU0sQ0FBQyxtQkFBbUIsRUFBRTtnQkFDMUIsU0FBUyxFQUFHLEdBQWEsRUFBRSxJQUFJO2dCQUMvQixZQUFZLEVBQUcsR0FBYSxFQUFFLE9BQU87YUFDdEMsQ0FBQyxDQUFDO1lBQ0gsTUFBTSxHQUFHLENBQUM7UUFDWixDQUFDO1FBQ0QsTUFBTSxDQUFDLGFBQWEsRUFBRSxFQUFFLE1BQU0sRUFBRSxvQkFBb0IsRUFBRSxDQUFDLENBQUM7SUFDMUQsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFF1aWNrSlMgV2ViQXNzZW1ibHkgKFdBU00pIFZNIGludGVncmF0aW9uIHdpdGggdGhlIFdvcmtmbG93IERldktpdC5cbiAqXG4gKiBUaGlzIG1vZHVsZSBwcm92aWRlcyB0aGUgZW50cnkgcG9pbnQgZm9yIHJ1bm5pbmcgd29ya2Zsb3dzIGluIHRoZVxuICogUXVpY2tKUyBWTSBlbmdpbmUgaW5zdGVhZCBvZiB0aGUgYG5vZGU6dm1gIGVuZ2luZS4gQm90aCBlbmdpbmVzXG4gKiBpbXBsZW1lbnQgdGhlIHNhbWUgZXZlbnQtcmVwbGF5IGV4ZWN1dGlvbiBtb2RlbCwgd2hlcmUgZXZlcnkgaW52b2NhdGlvbjpcbiAqXG4gKiAxLiBMb2FkcyB0aGUgZnVsbCBldmVudCBsb2cgZm9yIHRoZSBydW5cbiAqIDIuIFJ1bnMgdGhlIHdvcmtmbG93IGZ1bmN0aW9uIGZyb20gdGhlIHRvcCBpbiBhIGZyZXNoIFF1aWNrSlMgVk0sXG4gKiAgICByZXBsYXlpbmcgdGhlIGV2ZW50IGxvZyB0byByZXNvbHZlIGF3YWl0ZWQgcHJpbWl0aXZlc1xuICogMy4gT24gc3VzcGVuc2lvbjogY3JlYXRlcyBldmVudHMgKyBxdWV1ZXMgc3RlcHMgZm9yIG5ldyBwZW5kaW5nIG9wc1xuICogNC4gT24gY29tcGxldGlvbjogY3JlYXRlcyBydW5fY29tcGxldGVkXG4gKiA1LiBPbiBmYWlsdXJlOiBjcmVhdGVzIHJ1bl9mYWlsZWRcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFNwYW4gfSBmcm9tICdAb3BlbnRlbGVtZXRyeS9hcGknO1xuaW1wb3J0IHtcbiAgRW50aXR5Q29uZmxpY3RFcnJvcixcbiAgSG9va05vdEZvdW5kRXJyb3IsXG4gIE1heEV2ZW50c0V4Y2VlZGVkRXJyb3IsXG4gIFJ1bkV4cGlyZWRFcnJvcixcbiAgV29ya2Zsb3dOb3RSZWdpc3RlcmVkRXJyb3IsXG59IGZyb20gJ0B3b3JrZmxvdy9lcnJvcnMnO1xuaW1wb3J0IHsgcGFyc2VXb3JrZmxvd05hbWUgfSBmcm9tICdAd29ya2Zsb3cvdXRpbHMvcGFyc2UtbmFtZSc7XG5pbXBvcnQge1xuICB0eXBlIEV2ZW50LFxuICBST09UX1JVTl9JRF9BVFRSSUJVVEUsXG4gIHR5cGUgUnVuSW5wdXQsXG4gIFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICBTUEVDX1ZFUlNJT05fU1VQUE9SVFNfQ0JPUl9RVUVVRV9UUkFOU1BPUlQsXG4gIFNQRUNfVkVSU0lPTl9TVVBQT1JUU19DT01QUkVTU0lPTixcbiAgdHlwZSBXb3JrZmxvd1J1bixcbn0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcbmltcG9ydCB7IGNsYXNzaWZ5UnVuRXJyb3IsIGlzUmV0cnlhYmxlV29ybGRFcnJvciB9IGZyb20gJy4uL2NsYXNzaWZ5LWVycm9yLmpzJztcbmltcG9ydCB7IHJ1bnRpbWVMb2dnZXIgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xuaW1wb3J0IHtcbiAgZGVyaXZlUnVuUGF5bG9hZEtleXMsXG4gIGVuY3J5cHQgYXMgZW5jcnlwdFNlcmlhbGl6ZWREYXRhLFxuICB0eXBlIFJ1blBheWxvYWRLZXlzLFxufSBmcm9tICcuLi9zZXJpYWxpemF0aW9uL2VuY3J5cHRpb24uanMnO1xuaW1wb3J0IHtcbiAgZGVoeWRyYXRlUnVuRXJyb3IsXG4gIGRlaHlkcmF0ZVN0ZXBBcmd1bWVudHMsXG4gIGRlaHlkcmF0ZVN0ZXBFcnJvcixcbiAgaHlkcmF0ZVJ1bkVycm9yLFxuICBtYXliZUVuY3J5cHQsXG59IGZyb20gJy4uL3NlcmlhbGl6YXRpb24uanMnO1xuaW1wb3J0IHsgcmVtYXBFcnJvclN0YWNrLCBzdHJpcElubGluZVNvdXJjZU1hcCB9IGZyb20gJy4uL3NvdXJjZS1tYXAuanMnO1xuaW1wb3J0ICogYXMgQXR0cmlidXRlIGZyb20gJy4uL3RlbGVtZXRyeS9zZW1hbnRpYy1jb252ZW50aW9ucy5qcyc7XG5pbXBvcnQgeyBzZXJpYWxpemVUcmFjZUNhcnJpZXIgfSBmcm9tICcuLi90ZWxlbWV0cnkuanMnO1xuaW1wb3J0IHtcbiAgZ2V0SW5saW5lT3duZXJzaGlwTGVhc2VTZWNvbmRzLFxuICBnZXRNYXhJbmxpbmVTdGVwcyxcbiAgaXNSZXNpbGllbnRTdGVwRGlzcGF0Y2hFbmFibGVkLFxuICBNQVhfUkVTSUxJRU5UX1NURVBfSU5QVVRfQllURVMsXG59IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IGdldFBvcnRMYXp5IH0gZnJvbSAnLi9nZXQtcG9ydC1sYXp5LmpzJztcbmltcG9ydCB7XG4gIGdldFdvcmtmbG93UXVldWVOYW1lLFxuICBxdWV1ZU1lc3NhZ2UsXG4gIHN0ZXBEaXNwYXRjaElkZW1wb3RlbmN5S2V5LFxufSBmcm9tICcuL2hlbHBlcnMuanMnO1xuaW1wb3J0IHtcbiAgQkFTRUxJTkVfQlVORExFX0ZJTEVOQU1FLFxuICB0eXBlIFBlbmRpbmdBdHRyaWJ1dGUsXG4gIHR5cGUgUGVuZGluZ0hvb2ssXG4gIHR5cGUgUGVuZGluZ0hvb2tEaXNwb3NlLFxuICB0eXBlIFBlbmRpbmdPcGVyYXRpb24sXG4gIHR5cGUgUGVuZGluZ1N0ZXAsXG4gIHR5cGUgUGVuZGluZ1dhaXQsXG4gIHN0YXJ0UXVpY2tKU1dvcmtmbG93LFxufSBmcm9tICcuL3F1aWNranMtcnVudGltZS5qcyc7XG5pbXBvcnQgeyBSZXBsYXlCdWRnZXQgfSBmcm9tICcuL3JlcGxheS1idWRnZXQuanMnO1xuaW1wb3J0IHsgZXhlY3V0ZVN0ZXAsIHR5cGUgU3RlcEV4ZWN1dGlvblJlc3VsdCB9IGZyb20gJy4vc3RlcC1leGVjdXRvci5qcyc7XG5pbXBvcnQgeyBydW5TdGVwU2luZ2xlRmxpZ2h0IH0gZnJvbSAnLi9zdGVwLXNpbmdsZS1mbGlnaHQuanMnO1xuaW1wb3J0IHsgdW5zZXJpYWxpemFibGVTdGVwSW5wdXRQbGFjZWhvbGRlciB9IGZyb20gJy4vdW5zZXJpYWxpemFibGUtc3RlcC5qcyc7XG5pbXBvcnQgeyBnZXRXYWl0Q29udGludWF0aW9uRGlzcGF0Y2ggfSBmcm9tICcuL3dhaXQtY29udGludWF0aW9uLmpzJztcbmltcG9ydCB7IGdldFdvcmxkIH0gZnJvbSAnLi93b3JsZC5qcyc7XG5cbi8qKiBUaW55IG1zIHRpbWVyIHVzaW5nIHBlcmZvcm1hbmNlLm5vdygpLCBhbHJlYWR5IG1vbm90b25pYyBvbiBOb2RlLiAqL1xuZnVuY3Rpb24gdGljaygpOiBudW1iZXIge1xuICByZXR1cm4gcGVyZm9ybWFuY2Uubm93KCk7XG59XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIHdoZW4gdGhlIHN1cHBsaWVkIHByZWxvYWRlZCBldmVudHMgaW5kaWNhdGUgdGhpcyBpcyB0aGVcbiAqIGZpcnN0IHdvcmtmbG93IGhhbmRsZXIgaW52b2NhdGlvbiBmb3IgdGhlIHJ1biwgaS5lLiB0aGUgbG9nIGNvbnRhaW5zXG4gKiBub3RoaW5nIGJleW9uZCBgcnVuX2NyZWF0ZWRgIC8gYHJ1bl9zdGFydGVkYC4gSW4gdGhhdCBjYXNlIHRoZVxuICogcHJlbG9hZGVkIGV2ZW50cyBBUkUgdGhlIGNvbXBsZXRlIGV2ZW50IGxvZyBhbmQgdGhlIGBldmVudHMubGlzdGBcbiAqIHJvdW5kLXRyaXBzIGNhbiBiZSBza2lwcGVkIGVudGlyZWx5LlxuICpcbiAqIENydWNpYWxseSwgaWYgdGhlIHdvcmxkIGJhY2tmaWxsZWQgYSBtaXNzaW5nIGBydW5fY3JlYXRlZGAgdmlhIHRoZVxuICogcmVzaWxpZW50IHN0YXJ0IHBhdGgsIGBwcmVsb2FkZWRFdmVudHNgIGNvbnRhaW5zIGl0IGV2ZW4gd2hlbiBhIGZyZXNoXG4gKiBgZXZlbnRzLmxpc3RgIG1pZ2h0IG5vdCAoZXZlbnR1YWwgY29uc2lzdGVuY3kpLCBzbyBwcmVmZXJyaW5nIHRoZVxuICogcHJlbG9hZGVkIGV2ZW50cyBvbiBmaXJzdCBpbnZvY2F0aW9uIGlzIGFsc28gdGhlIG1vcmUgY29ycmVjdCBjaG9pY2UuXG4gKlxuICogUmV0dXJucyBmYWxzZSB3aGVuIGBwcmVsb2FkZWRFdmVudHNgIGlzIG1pc3NpbmcvZW1wdHkgc28gdGhlIGNhbGxlclxuICogZmFsbHMgYmFjayB0byB0aGUgbm9ybWFsIGZldGNoIHBhdGguXG4gKlxuICogRXhwb3J0ZWQgZm9yIHVuaXQgdGVzdGluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzRmlyc3RJbnZvY2F0aW9uKFxuICBwcmVsb2FkZWRFdmVudHM6IHJlYWRvbmx5IEV2ZW50W10gfCB1bmRlZmluZWRcbik6IGJvb2xlYW4ge1xuICBpZiAoIUFycmF5LmlzQXJyYXkocHJlbG9hZGVkRXZlbnRzKSB8fCBwcmVsb2FkZWRFdmVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHJldHVybiBwcmVsb2FkZWRFdmVudHMuZXZlcnkoXG4gICAgKGUpID0+IGUuZXZlbnRUeXBlID09PSAncnVuX2NyZWF0ZWQnIHx8IGUuZXZlbnRUeXBlID09PSAncnVuX3N0YXJ0ZWQnXG4gICk7XG59XG5cbi8qKlxuICogUXVldWUgYSBzdGVwIGZvciBiYWNrZ3JvdW5kIGV4ZWN1dGlvbiB2aWEgdGhlIHVuaWZpZWQgd29ya2Zsb3cgcXVldWVcbiAqIChWMiBhcmNoaXRlY3R1cmUpLiBUaGUgY29tYmluZWQgaGFuZGxlciBpbiBydW50aW1lLnRzIGRpc3BhdGNoZXNcbiAqIG1lc3NhZ2VzIHdpdGggYHN0ZXBJZGAgdG8gZXhlY3V0ZVN0ZXAsIHdoaWNoIHdvcmtzIGZvciBib3RoIFZNIGVuZ2luZXMuXG4gKiBgZGVsYXlTZWNvbmRzYCBzdXBwb3J0cyByZXRyeS90aHJvdHRsZSBiYWNrb2ZmLlxuICovXG5hc3luYyBmdW5jdGlvbiBxdWV1ZVN0ZXBNZXNzYWdlKHBhcmFtczoge1xuICB3b3JsZDogQXdhaXRlZDxSZXR1cm5UeXBlPHR5cGVvZiBnZXRXb3JsZD4+O1xuICBydW5JZDogc3RyaW5nO1xuICB3b3JrZmxvd1J1bjogV29ya2Zsb3dSdW47XG4gIHN0ZXA6IFBlbmRpbmdTdGVwO1xuICBkZWxheVNlY29uZHM/OiBudW1iZXI7XG4gIC8qKiBRdWV1ZSBuYW1lc3BhY2UgZm9yIHRoZSBwdWJsaXNoIChzZWUgcnVudGltZS50cykuICovXG4gIG5hbWVzcGFjZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAvKiogUnVuLW9yaWdpbiB0cmFjZSBjYXJyaWVyIGFjY2Vzc29yIChzZWUgcnVuV29ya2Zsb3dXaXRoUXVpY2tKUykuICovXG4gIG5leHRUcmFjZUNhcnJpZXI6ICgpID0+IFByb21pc2U8UmVjb3JkPHN0cmluZywgc3RyaW5nPj47XG4gIC8qKlxuICAgKiBQdWJsaXNoIHB1cnBvc2UsIHVzZWQgdG8gYnVja2V0IHRoZSBpZGVtcG90ZW5jeSBrZXkuIFdvcmxkcyByZXRpcmVcbiAgICogdXNlZCBrZXlzIChWUVMgcmV0ZW50aW9uIFRUTCwgd29ybGQtcG9zdGdyZXMgY29tcGxldGVkLWtleXMgY2FjaGUpLFxuICAgKiBzbyBhIGtleSBzaGFyZWQgYWNyb3NzIHB1cnBvc2VzIHNpbGVudGx5IHN3YWxsb3dzIHRoZSBzZWNvbmRcbiAgICogcHVibGlzaC4gU2VlIHdhaXQtY29udGludWF0aW9uLnRzIGZvciB0aGUgc2FtZSBoYXphcmQgb24gd2FpdFxuICAgKiBrZXlzLiBgZGlzcGF0Y2hgIGlzIHRoZSBwbGFpbiBiYWNrZ3JvdW5kIGhhbmRvZmYgKG92ZXJmbG93IC8gY3Jhc2hcbiAgICogcmVjb3ZlcnkpIGFuZCB1c2VzIHRoZSBzdGVwLWlkZW50aXR5LXNjb3BlZCBkaXNwYXRjaCBrZXlcbiAgICogKHN0ZXBEaXNwYXRjaElkZW1wb3RlbmN5S2V5KSBzbyBpdCBzdGF5cyBtdXR1YWxseVxuICAgKiBleGNsdXNpdmUgd2l0aCB0aGUgbm9kZSBlbmdpbmUncyBkaXNwYXRjaCBvZiB0aGUgc2FtZSBzdGVwO1xuICAgKiBgYmFja3N0b3A6PGVwb2NoPmAgY292ZXJzIGRlbGF5ZWQgY3Jhc2ggYmFja3N0b3BzLCBzY29wZWQgdG8gdGhlXG4gICAqIG93bmVyc2hpcCBlcG9jaCBzbyBhIHJlZnJlc2hlZCBsZWFzZSByZS1hcm1zIGEgTkVXIGJhY2tzdG9wIGluc3RlYWRcbiAgICogb2YgYmVpbmcgYWJzb3JiZWQgYnkgdGhlIGluLWZsaWdodCBvbmU7IGByZXRyeTo8bj5gIGNvdmVycyBkZWxheWVkXG4gICAqIHJldHJ5L3Rocm90dGxlIHJlLWVucXVldWVzLCBzY29wZWQgdG8gdGhlIGF0dGVtcHQgc28gZWFjaCBiYWNrb2ZmXG4gICAqIGhvcCBpcyBlbnF1ZXVlYWJsZS5cbiAgICovXG4gIHB1cnBvc2U6ICdkaXNwYXRjaCcgfCBgYmFja3N0b3A6JHtzdHJpbmd9YCB8IGByZXRyeToke251bWJlcn1gO1xuICAvKipcbiAgICogUmVzaWxpZW50IHN0ZXAgZGlzcGF0Y2g6IHRoZSBzZXJpYWxpemVkIChwb3NzaWJseSBlbmNyeXB0ZWQpIHN0ZXAgaW5wdXRcbiAgICogdG8gY2Fycnkgb24gdGhlIG1lc3NhZ2UgYXMgYHN0ZXBJbnB1dGAsIHNvIHRoZSBjb25zdW1lciBjYW4gaWRlbXBvdGVudGx5XG4gICAqIHJlLWVuc3VyZSB0aGUgYHN0ZXBfY3JlYXRlZGAgZXZlbnQgaWYgdGhlIHByb2R1Y2VyJ3MgcGFyYWxsZWwgZGlyZWN0XG4gICAqIHdyaXRlIGZhaWxlZCB0cmFuc2llbnRseS4gT25seSBzZXQgb24gYGRpc3BhdGNoYCBwdWJsaXNoZXMgdGhhdFxuICAgKiBkaXNwYXRjaFBlbmRpbmdPcHMgcGFyYWxsZWxpemVzIHdpdGggdGhlIHN0ZXBfY3JlYXRlZCB3cml0ZS5cbiAgICovXG4gIHN0ZXBJbnB1dD86IFVpbnQ4QXJyYXk7XG4gIHdmZGlhZzogKGNoZWNrcG9pbnQ6IHN0cmluZywgZmllbGRzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gdm9pZDtcbn0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qge1xuICAgIHdvcmxkLFxuICAgIHJ1bklkLFxuICAgIHdvcmtmbG93UnVuLFxuICAgIHN0ZXAsXG4gICAgZGVsYXlTZWNvbmRzLFxuICAgIG5hbWVzcGFjZSxcbiAgICBuZXh0VHJhY2VDYXJyaWVyLFxuICAgIHB1cnBvc2UsXG4gICAgc3RlcElucHV0LFxuICAgIHdmZGlhZyxcbiAgfSA9IHBhcmFtcztcbiAgY29uc3QgdHJhY2VDYXJyaWVyID0gYXdhaXQgbmV4dFRyYWNlQ2FycmllcigpO1xuICBhd2FpdCBxdWV1ZU1lc3NhZ2UoXG4gICAgd29ybGQsXG4gICAgZ2V0V29ya2Zsb3dRdWV1ZU5hbWUod29ya2Zsb3dSdW4ud29ya2Zsb3dOYW1lLCBuYW1lc3BhY2UpLFxuICAgIHtcbiAgICAgIHJ1bklkLFxuICAgICAgc3RlcElkOiBzdGVwLmNvcnJlbGF0aW9uSWQsXG4gICAgICBzdGVwTmFtZTogc3RlcC5zdGVwSWQsXG4gICAgICB0cmFjZUNhcnJpZXIsXG4gICAgICByZXF1ZXN0ZWRBdDogbmV3IERhdGUoKSxcbiAgICAgIC4uLihzdGVwSW5wdXQgIT09IHVuZGVmaW5lZCA/IHsgc3RlcElucHV0OiB7IGlucHV0OiBzdGVwSW5wdXQgfSB9IDoge30pLFxuICAgIH0sXG4gICAge1xuICAgICAgLy8gVGhlICdkaXNwYXRjaCcga2V5IGlzIHN0ZXAtaWRlbnRpdHktc2NvcGVkIChjb3JyZWxhdGlvbklkICsgaGFzaGVkXG4gICAgICAvLyBzdGVwIG5hbWUpLCBzaGFyZWQgd2l0aCB0aGUgbm9kZSBlbmdpbmUncyBkaXNwYXRjaCBvZiB0aGUgc2FtZSBzdGVwXG4gICAgICAvLyBzbyB0aGUgdHdvIHN0YXkgbXV0dWFsbHkgZXhjbHVzaXZlLCB3aXRob3V0IGEgcmV2b2tlZCByZXNpbGllbnRcbiAgICAgIC8vIG1lc3NhZ2UgYWJzb3JiaW5nIGEgcmVhc3NpZ25lZCBjb3JyZWxhdGlvbiBpZCdzIGxlZ2l0aW1hdGUgZGlzcGF0Y2guXG4gICAgICAvLyBTZWUgc3RlcERpc3BhdGNoSWRlbXBvdGVuY3lLZXkuXG4gICAgICBpZGVtcG90ZW5jeUtleTpcbiAgICAgICAgcHVycG9zZSA9PT0gJ2Rpc3BhdGNoJ1xuICAgICAgICAgID8gc3RlcERpc3BhdGNoSWRlbXBvdGVuY3lLZXkoc3RlcC5jb3JyZWxhdGlvbklkLCBzdGVwLnN0ZXBJZClcbiAgICAgICAgICA6IGAke3N0ZXAuY29ycmVsYXRpb25JZH06JHtwdXJwb3NlfWAsXG4gICAgICAuLi4oZGVsYXlTZWNvbmRzICYmIGRlbGF5U2Vjb25kcyA+IDAgPyB7IGRlbGF5U2Vjb25kcyB9IDoge30pLFxuICAgIH1cbiAgKTtcbiAgd2ZkaWFnKCdzdGVwX3F1ZXVlZCcsIHtcbiAgICBzdGVwSWQ6IHN0ZXAuc3RlcElkLFxuICAgIGNvcnJlbGF0aW9uSWQ6IHN0ZXAuY29ycmVsYXRpb25JZCxcbiAgICBwdXJwb3NlLFxuICAgIGRlbGF5U2Vjb25kczogZGVsYXlTZWNvbmRzID8/IDAsXG4gICAgLi4uKHN0ZXBJbnB1dCAhPT0gdW5kZWZpbmVkID8geyByZXNpbGllbnQ6IHRydWUgfSA6IHt9KSxcbiAgfSk7XG59XG5cbi8qKlxuICogRGlzcGF0Y2ggZHVyYWJsZSBzaWRlIGVmZmVjdHMgZm9yIGEgc2V0IG9mIHBlbmRpbmcgVk0gb3BlcmF0aW9uczpcbiAqIHN0ZXBfY3JlYXRlZCAoKyBvcHRpb25hbCBxdWV1ZWluZyksIGhvb2tfY3JlYXRlZCAvIGhvb2tfcmVjZWl2ZWQgKGFib3J0cyksXG4gKiBhdHRyX3NldCwgaG9va19kaXNwb3NlZCwgYW5kIHdhaXRfY3JlYXRlZCBldmVudHMuXG4gKlxuICogU3RlcHMgYXJlIGNyZWF0ZWQgYnV0ICh1c3VhbGx5KSBub3QgcXVldWVkIGhlcmU6IHF1ZXVlaW5nIChvciBpbmxpbmVcbiAqIGV4ZWN1dGlvbikgaXMgdGhlIGNhbGxlcidzIGRlY2lzaW9uLiBVc2VkIGJvdGggZm9yIHN1c3BlbnNpb25cbiAqIHByb2Nlc3NpbmcgKHRoZSBpbmxpbmUgbG9vcCkgYW5kIGZvciB0aGUgdGVybWluYWwgZHJhaW4gKGZsdXNoaW5nXG4gKiBsZWZ0b3ZlciBzaWRlIGVmZmVjdHMgd2hlbiB0aGUgd29ya2Zsb3cgY29tcGxldGVkIG9yIGZhaWxlZCwgbWlycm9yaW5nXG4gKiB0aGUgbm9kZTp2bSBlbmdpbmUncyBkcmFpblBlbmRpbmdRdWV1ZUl0ZW1zKS5cbiAqXG4gKiBUaGUgb25lIGV4Y2VwdGlvbiBpcyByZXNpbGllbnQgc3RlcCBkaXNwYXRjaDogZm9yIHN0ZXAgY2lkcyBuYW1lZCBpblxuICogYHF1ZXVlU3RlcENpZHNgICh0aGUgY2FsbGVyJ3Mgb3ZlcmZsb3cgc3RlcHMpIHRoYXQgcGFzcyB0aGUgZWxpZ2liaWxpdHlcbiAqIGdhdGVzLCB0aGUgc3RlcF9jcmVhdGVkIHdyaXRlIGlzIHBhcmFsbGVsaXplZCB3aXRoIHRoZSBzdGVwJ3MgcXVldWVcbiAqIHB1Ymxpc2g6IHRoZSBtZXNzYWdlIGNhcnJpZXMgdGhlIHNlcmlhbGl6ZWQgaW5wdXQgKGBzdGVwSW5wdXRgKSBzbyB0aGVcbiAqIGNvbnN1bWVyIGNhbiBpZGVtcG90ZW50bHkgcmUtZW5zdXJlIHRoZSBldmVudCBpZiB0aGUgZGlyZWN0IHdyaXRlIGZhaWxlZFxuICogdHJhbnNpZW50bHkuIFN0ZXBzIHF1ZXVlZCB0aGlzIHdheSBhcmUgcmVwb3J0ZWQgaW4gYHF1ZXVlZFN0ZXBDaWRzYDsgdGhlXG4gKiBjYWxsZXIgcXVldWVzIHRoZSByZXN0IGl0c2VsZi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZGlzcGF0Y2hQZW5kaW5nT3BzKHBhcmFtczoge1xuICB3b3JsZDogQXdhaXRlZDxSZXR1cm5UeXBlPHR5cGVvZiBnZXRXb3JsZD4+O1xuICBydW5JZDogc3RyaW5nO1xuICB3b3JrZmxvd1J1bjogV29ya2Zsb3dSdW47XG4gIGVuY3J5cHRpb25LZXk6IFJ1blBheWxvYWRLZXlzIHwgdW5kZWZpbmVkO1xuICBwZW5kaW5nT3BlcmF0aW9uczogUGVuZGluZ09wZXJhdGlvbltdO1xuICAvKipcbiAgICogU3RlcCBjaWRzIHdob3NlIGBzdGVwX2NyZWF0ZWRgIG11c3QgTk9UIGJlIHdyaXR0ZW4gaGVyZTogdGhlIGlubGluZVxuICAgKiBsb29wIGNsYWltcyB0aGVzZSBhdG9taWNhbGx5IHZpYSBhIGxhenkgYHN0ZXBfc3RhcnRlZGAgKGNhcnJ5aW5nIHRoZVxuICAgKiBpbnB1dCksIHNvIGEgY29uY3VycmVudCBjbGFpbWFudCBsb3NlcyB3aXRoIEVudGl0eUNvbmZsaWN0RXJyb3JcbiAgICogaW5zdGVhZCBvZiBib3RoIGludm9jYXRpb25zIGJhcmUtc3RhcnRpbmcgdGhlIHNhbWUgc3RlcC5cbiAgICovXG4gIHNraXBTdGVwQ3JlYXRpb24/OiBTZXQ8c3RyaW5nPjtcbiAgLyoqXG4gICAqIFN0ZXAgY2lkcyB0aGUgY2FsbGVyIGludGVuZHMgdG8gaGFuZCB0byB0aGUgcXVldWUgdGhpcyB0dXJuIChvdmVyZmxvd1xuICAgKiBzdGVwcyBiZXlvbmQgdGhlIGlubGluZSBjYXApLiBFbGlnaWJsZSBvbmVzIGFyZSBwdWJsaXNoZWQgaGVyZSwgaW5cbiAgICogcGFyYWxsZWwgd2l0aCB0aGVpciBzdGVwX2NyZWF0ZWQgd3JpdGUgKHJlc2lsaWVudCBzdGVwIGRpc3BhdGNoKSwgYW5kXG4gICAqIHJlcG9ydGVkIGJhY2sgaW4gYHF1ZXVlZFN0ZXBDaWRzYC5cbiAgICovXG4gIHF1ZXVlU3RlcENpZHM/OiBTZXQ8c3RyaW5nPjtcbiAgLyoqIFF1ZXVlIG5hbWVzcGFjZSBmb3IgYWxsIG1lc3NhZ2UgcHVibGlzaGVzIChzZWUgcnVudGltZS50cykuICovXG4gIG5hbWVzcGFjZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAvKipcbiAgICogUnVuLW9yaWdpbiB0cmFjZSBjYXJyaWVyIGFjY2Vzc29yIGZyb20gcnVudGltZS50cy4gSW4gdGhlIGRlZmF1bHRcbiAgICogYGxpbmtlZGAgdHJhY2UgbW9kZSB0aGlzIHJldHVybnMgdGhlIGNhcnJpZXIgb2YgdGhlIHJ1bidzIE9SSUdJTlxuICAgKiBjb250ZXh0ICh3b3JrZmxvdy5zdGFydCksIHNvIGV2ZXJ5IGludm9jYXRpb24gbGlua3MgYmFjayB0byB0aGVcbiAgICogc3RhcnQgaW4gYSBzdGFyLiBDYXB0dXJpbmcgdGhlIGN1cnJlbnQgY29udGV4dCBoZXJlIGluc3RlYWQgd291bGRcbiAgICogY2hhaW4gaW52b2NhdGlvbnMgdG8gZWFjaCBvdGhlciBhbmQgZnJhZ21lbnQgdGhlIHJ1biB2aWV3IG9uIGFzeW5jXG4gICAqIHF1ZXVlcy5cbiAgICovXG4gIG5leHRUcmFjZUNhcnJpZXI6ICgpID0+IFByb21pc2U8UmVjb3JkPHN0cmluZywgc3RyaW5nPj47XG4gIC8qKlxuICAgKiBXaGVuIHRydWUgKHRoZSBpbmxpbmUgbG9vcCksIGEgc3RlcCBjYXJyeWluZyBgc2VyaWFsaXphdGlvbkVycm9yYCBpc1xuICAgKiBmaW5hbGl6ZWQgYXMgc3RlcF9jcmVhdGVkIChwbGFjZWhvbGRlciBpbnB1dCkgKyBzdGVwX2ZhaWxlZCBzbyB0aGVcbiAgICogbGl2ZS1WTSBmZWVkIHJlamVjdHMgdGhlIHN0ZXAncyBwcm9taXNlIGFuZCB3b3JrZmxvdyBjb2RlIGNhbiBjYXRjaFxuICAgKiBpdCwgbWlycm9yaW5nIHRoZSBub2RlOnZtIGVuZ2luZSdzIGZpbmFsaXplVW5zZXJpYWxpemFibGVTdGVwLiBXaGVuXG4gICAqIGZhbHNlICh0aGUgdGVybWluYWwgZHJhaW4pLCBzdWNoIHN0ZXBzIGFyZSBza2lwcGVkIGVudGlyZWx5OiB0aGUgcnVuXG4gICAqIGlzIGFscmVhZHkgY29tcGxldGluZy9mYWlsaW5nLCBubyByZXBsYXkgZm9sbG93cyB0aGUgZHJhaW4gdG8gb2JzZXJ2ZVxuICAgKiB0aGUgZmFpbHVyZSwgYW5kIGEgY29tcGxldGVkIHJ1biBjYXJyeWluZyBhIGZhaWxlZCBzdGVwIHdvdWxkIHJlYWQgYXNcbiAgICogYSBidWcgZnJvbSB0aGUgZGFzaGJvYXJkLCBtYXRjaGluZyB0aGUgbm9kZTp2bSBkcmFpbidzIGJlaGF2aW9yLlxuICAgKi9cbiAgZmluYWxpemVVbnNlcmlhbGl6YWJsZVN0ZXBzPzogYm9vbGVhbjtcbiAgd2ZkaWFnOiAoY2hlY2twb2ludDogc3RyaW5nLCBmaWVsZHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSA9PiB2b2lkO1xufSk6IFByb21pc2U8e1xuICBjcmVhdGVkQXR0cmlidXRlRXZlbnQ6IGJvb2xlYW47XG4gIGNyZWF0ZWRHZXRDb25mbGljdEhvb2s6IGJvb2xlYW47XG4gIC8qKiBTdGVwIGNpZHMgYWxyZWFkeSBwdWJsaXNoZWQgdmlhIHJlc2lsaWVudCBkaXNwYXRjaC4gU2VlIGFib3ZlLiAqL1xuICBxdWV1ZWRTdGVwQ2lkczogU2V0PHN0cmluZz47XG4gIC8qKlxuICAgKiBTdGVwIGNpZHMgZmluYWxpemVkIGFzIGZhaWxlZCBiZWNhdXNlIHRoZWlyIGlucHV0IHJlZnVzZWQgdG9cbiAgICogc2VyaWFsaXplIChzZWUgYGZpbmFsaXplVW5zZXJpYWxpemFibGVTdGVwc2ApLiBObyBleGVjdXRpb24gbWVzc2FnZVxuICAgKiBleGlzdHMgZm9yIHRoZXNlOyB0aGUgY2FsbGVyIG11c3QgZW5zdXJlIHRoZSBydW4gb2JzZXJ2ZXMgdGhlXG4gICAqIHRlcm1pbmFsIGV2ZW50ICh0aGUgaW5saW5lIGxvb3AncyBmZWVkLCBvciB0aGUgcmVxdWV1ZSBzaWduYWwpLlxuICAgKi9cbiAgZmFpbGVkU2VyaWFsaXphdGlvblN0ZXBDaWRzOiBTZXQ8c3RyaW5nPjtcbn0+IHtcbiAgY29uc3Qge1xuICAgIHdvcmxkLFxuICAgIHJ1bklkLFxuICAgIHdvcmtmbG93UnVuLFxuICAgIGVuY3J5cHRpb25LZXksXG4gICAgcGVuZGluZ09wZXJhdGlvbnMsXG4gICAgbmFtZXNwYWNlLFxuICAgIG5leHRUcmFjZUNhcnJpZXIsXG4gIH0gPSBwYXJhbXM7XG4gIGNvbnN0IHNraXBTdGVwQ3JlYXRpb24gPSBwYXJhbXMuc2tpcFN0ZXBDcmVhdGlvbjtcbiAgY29uc3QgcXVldWVTdGVwQ2lkcyA9IHBhcmFtcy5xdWV1ZVN0ZXBDaWRzO1xuICBjb25zdCB3ZmRpYWcgPSBwYXJhbXMud2ZkaWFnO1xuICAvLyBTdGVwIGNpZHMgcHVibGlzaGVkIHZpYSByZXNpbGllbnQgZGlzcGF0Y2ggYmVsb3cgKGNyZWF0ZSArIHF1ZXVlIGluXG4gIC8vIHBhcmFsbGVsLCBtZXNzYWdlIGNhcnJ5aW5nIGBzdGVwSW5wdXRgKS4gUmVwb3J0ZWQgdG8gdGhlIGNhbGxlciBzbyBpdFxuICAvLyBza2lwcyB0aGVtIGluIGl0cyBvd24gcXVldWVpbmcgcGFzcy5cbiAgY29uc3QgcXVldWVkU3RlcENpZHMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgLy8gU3RlcCBjaWRzIGZpbmFsaXplZCBhcyBzdGVwX2NyZWF0ZWQgKyBzdGVwX2ZhaWxlZCBiZWNhdXNlIHRoZWlyIGlucHV0XG4gIC8vIHJlZnVzZWQgdG8gc2VyaWFsaXplLiBTZWUgdGhlIGBmaW5hbGl6ZVVuc2VyaWFsaXphYmxlU3RlcHNgIHBhcmFtLlxuICBjb25zdCBmYWlsZWRTZXJpYWxpemF0aW9uU3RlcENpZHMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgLy8gUmVzaWxpZW50IHN0ZXAgZGlzcGF0Y2ggZWxpZ2liaWxpdHksIHNoYXJlZCBieSBldmVyeSBzdGVwIG9wIGJlbG93ICh0aGVcbiAgLy8gcGVyLXN0ZXAgaW5wdXQtc2l6ZSBjaGVjayBpcyBhcHBsaWVkIGluc2lkZSB0aGUgb3ApOiBmZWF0dXJlIGVuYWJsZWQgYW5kXG4gIC8vIGEgYmluYXJ5LXNhZmUgKENCT1IpIHF1ZXVlIHRyYW5zcG9ydCBmb3IgdGhlIHJ1bi5cbiAgLy9cbiAgLy8gVW5saWtlIHRoZSBub2RlOnZtIHN1c3BlbnNpb24gaGFuZGxlcidzIGdhdGUgKHNlZVxuICAvLyBTdXNwZW5zaW9uSGFuZGxlclBhcmFtcy5zdGVwRGlzcGF0Y2gpLCB0aGVyZSBpcyBOTyBwcmVjb25kaXRpb24tZ3VhcmRcbiAgLy8gZ2F0ZSBoZXJlOiB0aGlzIGVuZ2luZSdzIHN0ZXBfY3JlYXRlZCB3cml0ZXMgYXJlIHVuZ3VhcmRlZCAobm8gc25hcHNob3RcbiAgLy8gaXMgYXR0YWNoZWQpLCBzbyBhIGd1YXJkLWVuZm9yY2luZyBXb3JsZCBjYW4gbmV2ZXIgNDEyLXJlamVjdCB0aGVtOlxuICAvLyB0aGUgY29uc3VtZXIncyByZS1lbnN1cmUgdGhlcmVmb3JlIGNhbm5vdCBtYXRlcmlhbGl6ZSBhIHN0ZXAgdGhlIGd1YXJkXG4gIC8vIHJlamVjdGVkLiBJZiB0aGlzIGVuZ2luZSBldmVyIGFkb3B0cyBndWFyZGVkIHN1c3BlbnNpb24gd3JpdGVzLCB0aGVcbiAgLy8gY2FwYWJpbGl0eSBnYXRlIGZyb20gdGhlIG5vZGU6dm0gaGFuZGxlciBtdXN0IGJlIGFkZGVkIGhlcmUgdG9vLlxuICBjb25zdCByZXNpbGllbnREaXNwYXRjaEVsaWdpYmxlID1cbiAgICBxdWV1ZVN0ZXBDaWRzICE9PSB1bmRlZmluZWQgJiZcbiAgICBxdWV1ZVN0ZXBDaWRzLnNpemUgPiAwICYmXG4gICAgaXNSZXNpbGllbnRTdGVwRGlzcGF0Y2hFbmFibGVkKCkgJiZcbiAgICAod29ya2Zsb3dSdW4uc3BlY1ZlcnNpb24gPz8gMCkgPj1cbiAgICAgIFNQRUNfVkVSU0lPTl9TVVBQT1JUU19DQk9SX1FVRVVFX1RSQU5TUE9SVDtcbiAgLy8gU2V0IHdoZW4gYSBob29rIHdpdGggYSBwYXJrZWQgZ2V0Q29uZmxpY3QoKSBhd2FpdGVyIGhhZCBpdHNcbiAgLy8gaG9va19jcmVhdGVkIHdyaXR0ZW4gdGhpcyBpbnZvY2F0aW9uLiBUaGUgd29ya2Zsb3cgbXVzdCBiZSByZS1pbnZva2VkXG4gIC8vIHNvIHJlcGxheSBjYW4gY29uZmlybSBjcmVhdGlvbiBhbmQgcmVzb2x2ZSB0aGUgYXdhaXRlci5cbiAgbGV0IGNyZWF0ZWRHZXRDb25mbGljdEhvb2sgPSBmYWxzZTtcbiAgLy8gU2V0IHdoZW4gYSBuZXcgYXR0cl9zZXQgZXZlbnQgaXMgd3JpdHRlbiB0aGlzIGludm9jYXRpb24uIFRoZVxuICAvLyB3b3JrZmxvdyBtdXN0IGJlIHJlLWludm9rZWQgdG8gY29uc3VtZSBpdCAocmVzb2x2aW5nIHRoZSBwZW5kaW5nXG4gIC8vIHNldEF0dHJpYnV0ZXMoKSBwcm9taXNlKSwgc28gdGhlIGVudHJ5cG9pbnQgcmVxdWV1ZXMgaW1tZWRpYXRlbHksXG4gIC8vIHNhbWUgcGF0dGVybiBhcyBhbiBlbGFwc2VkIHdhaXQuXG4gIGxldCBjcmVhdGVkQXR0cmlidXRlRXZlbnQgPSBmYWxzZTtcbiAgY29uc3Qgb3BzUHJvbWlzZXM6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xuXG4gIGNvbnN0IHByb2Nlc3NIb29rT3AgPSBhc3luYyAoaG9vazogUGVuZGluZ0hvb2spOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBydW50aW1lTG9nZ2VyLmRlYnVnKCdRdWlja0pTIHJ1bnRpbWU6IHByb2Nlc3NpbmcgaG9vayBvcCcsIHtcbiAgICAgIHdvcmtmbG93UnVuSWQ6IHJ1bklkLFxuICAgICAgY29ycmVsYXRpb25JZDogaG9vay5jb3JyZWxhdGlvbklkLFxuICAgICAgdG9rZW46IGhvb2sudG9rZW4sXG4gICAgICB0b2tlblR5cGU6IHR5cGVvZiBob29rLnRva2VuLFxuICAgICAgaXNXZWJob29rOiBob29rLmlzV2ViaG9vayxcbiAgICAgIGlzU3lzdGVtOiBob29rLmlzU3lzdGVtLFxuICAgICAgaGFzQ3JlYXRlZEV2ZW50OiBob29rLmhhc0NyZWF0ZWRFdmVudCxcbiAgICAgIGFib3J0UmVxdWVzdGVkOiBob29rLmFib3J0UmVxdWVzdGVkLFxuICAgIH0pO1xuXG4gICAgaWYgKCFob29rLmhhc0NyZWF0ZWRFdmVudCkge1xuICAgICAgLy8gYGhvb2subWV0YWRhdGFgIGlzIHRoZSBmb3JtYXQtcHJlZml4ZWQgZGV2YWx1ZSBieXRlc1xuICAgICAgLy8gcHJvZHVjZWQgYnkgYGdsb2JhbFRoaXNbU3ltYm9sLmZvcignd29ya2Zsb3ctc2VyaWFsaXplJyldXG4gICAgICAvLyAob3B0aW9ucy5tZXRhZGF0YSlgIGluc2lkZSB0aGUgVk0uIEVuY3J5cHQgb24gdGhlIGhvc3RcbiAgICAgIC8vIHNpZGUgYmVmb3JlIHdyaXRpbmcsIHdoaWNoIG1hdGNoZXMgdGhlIG5vZGU6dm0gZW5naW5lJ3NcbiAgICAgIC8vIGBkZWh5ZHJhdGVTdGVwQXJndW1lbnRzYCBmbG93LlxuICAgICAgLy9cbiAgICAgIC8vIE5vIHByZS1jaGVjayB2aWEgaG9va3MubGlzdDogd2l0aCBkZXRlcm1pbmlzdGljIGNvcnJlbGF0aW9uSWRzXG4gICAgICAvLyAoc2FtZSBWTSBzZWVkIGFjcm9zcyByZXBsYXlzKSBhbmQgcGVyLShydW5JZCwgY29ycmVsYXRpb25JZClcbiAgICAgIC8vIHVuaXF1ZW5lc3MgaW4gd29ybGRzLCB0aGUgc3RvcmFnZSBsYXllciByZWplY3RzIGR1cGxpY2F0ZXMgYXNcbiAgICAgIC8vIEVudGl0eUNvbmZsaWN0RXJyb3IsIHdoaWNoIHdlIHN3YWxsb3cgYmVsb3cuIFRoaXMgZHJvcHMgb25lXG4gICAgICAvLyBuZXR3b3JrIHJvdW5kLXRyaXAgcGVyIHBlbmRpbmcgaG9vay5cbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGVuY3J5cHRlZE1ldGFkYXRhID1cbiAgICAgICAgICB0eXBlb2YgaG9vay5tZXRhZGF0YSA9PT0gJ3VuZGVmaW5lZCdcbiAgICAgICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgICAgICA6IGF3YWl0IGVuY3J5cHRTZXJpYWxpemVkRGF0YShob29rLm1ldGFkYXRhLCBlbmNyeXB0aW9uS2V5KTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgd29ybGQuZXZlbnRzLmNyZWF0ZShydW5JZCwge1xuICAgICAgICAgIGV2ZW50VHlwZTogJ2hvb2tfY3JlYXRlZCcsXG4gICAgICAgICAgc3BlY1ZlcnNpb246IFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IGhvb2suY29ycmVsYXRpb25JZCxcbiAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgIHRva2VuOiBob29rLnRva2VuLFxuICAgICAgICAgICAgdG9rZW5SZXRlbnRpb25VbnRpbDpcbiAgICAgICAgICAgICAgaG9vay50b2tlblJldGVudGlvblVudGlsID09PSB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgIDogbmV3IERhdGUoaG9vay50b2tlblJldGVudGlvblVudGlsKSxcbiAgICAgICAgICAgIG1ldGFkYXRhOiBlbmNyeXB0ZWRNZXRhZGF0YSxcbiAgICAgICAgICAgIC8vIEFsd2F5cyBpbmNsdWRlIGlzV2ViaG9vayBleHBsaWNpdGx5LiBXb3JsZHMgZGVmYXVsdCBpdCB0b1xuICAgICAgICAgICAgLy8gYHRydWVgIHdoZW4gYWJzZW50LCB3aGljaCB3b3VsZCBicmVhayB0aGUgcHVibGljIHdlYmhvb2tcbiAgICAgICAgICAgIC8vIGVuZHBvaW50J3MgNDA0IGd1YXJkIGZvciBob29rcyBjcmVhdGVkIHZpYSBjcmVhdGVIb29rKCkuXG4gICAgICAgICAgICBpc1dlYmhvb2s6IGhvb2suaXNXZWJob29rLFxuICAgICAgICAgICAgLy8gU3lzdGVtIGhvb2tzIChBYm9ydENvbnRyb2xsZXIpIGFyZSBleGVtcHQgZnJvbSB1c2VyXG4gICAgICAgICAgICAvLyB0b2tlbiBuYW1lc3BhY2UgY29uZmxpY3QgY2hlY2tzLlxuICAgICAgICAgICAgLi4uKGhvb2suaXNTeXN0ZW0gPyB7IGlzU3lzdGVtOiB0cnVlIH0gOiB7fSksXG4gICAgICAgICAgfSBhcyBhbnksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIElmIHN0b3JhZ2UgZGV0ZWN0ZWQgYSByZWFsIHRva2VuIGNvbmZsaWN0IHdpdGggYW5vdGhlclxuICAgICAgICAvLyB3b3JrZmxvdydzIGhvb2ssIHJlLXF1ZXVlIHNvIHRoZSB3b3JrZmxvdyBoYW5kbGVyIGNhblxuICAgICAgICAvLyBwcm9jZXNzIHRoZSBjb25mbGljdCBldmVudCBhbmQgZmFpbCBncmFjZWZ1bGx5LlxuICAgICAgICBpZiAocmVzdWx0LmV2ZW50Py5ldmVudFR5cGUgPT09ICdob29rX2NvbmZsaWN0Jykge1xuICAgICAgICAgIGF3YWl0IHF1ZXVlTWVzc2FnZShcbiAgICAgICAgICAgIHdvcmxkLFxuICAgICAgICAgICAgZ2V0V29ya2Zsb3dRdWV1ZU5hbWUod29ya2Zsb3dSdW4ud29ya2Zsb3dOYW1lLCBuYW1lc3BhY2UpLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBydW5JZCxcbiAgICAgICAgICAgICAgdHJhY2VDYXJyaWVyOiBhd2FpdCBuZXh0VHJhY2VDYXJyaWVyKCksXG4gICAgICAgICAgICAgIHJlcXVlc3RlZEF0OiBuZXcgRGF0ZSgpLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHsgaWRlbXBvdGVuY3lLZXk6IGBob29rX2NvbmZsaWN0XyR7aG9vay5jb3JyZWxhdGlvbklkfWAgfVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAvLyBBbHJlYWR5IGNyZWF0ZWQgYnkgYSBjb25jdXJyZW50IGludm9jYXRpb24sIHNvIGZhbGwgdGhyb3VnaFxuICAgICAgICAvLyB0byBhYm9ydCBwcm9jZXNzaW5nIGJlbG93IChpZiBhbnkpIGluc3RlYWQgb2YgYmFpbGluZy5cbiAgICAgICAgaWYgKCFFbnRpdHlDb25mbGljdEVycm9yLmlzKGVycikpIHRocm93IGVycjtcbiAgICAgIH1cbiAgICAgIGlmIChob29rLmhhc0dldENvbmZsaWN0QXdhaXRlcikge1xuICAgICAgICBjcmVhdGVkR2V0Q29uZmxpY3RIb29rID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaG9vay5hYm9ydFJlcXVlc3RlZCkge1xuICAgICAgLy8gUmVjb3JkIHRoZSBhYm9ydCBkdXJhYmx5OiBhIGhvb2tfcmVjZWl2ZWQgZXZlbnQgY2FycnlpbmdcbiAgICAgIC8vIHRoZSBWTS1zZXJpYWxpemVkIGB7IGFib3J0ZWQ6IHRydWUsIHJlYXNvbiB9YCBwYXlsb2FkLFxuICAgICAgLy8gcGx1cyBhIGJlc3QtZWZmb3J0IHN0cmVhbSBwYWNrZXQgZm9yIHJlYWwtdGltZSBzdGVwXG4gICAgICAvLyBwcm9wYWdhdGlvbi4gTWlycm9ycyB0aGUgbm9kZTp2bSBlbmdpbmUncyBzdXNwZW5zaW9uXG4gICAgICAvLyBoYW5kbGVyIChob29rc05lZWRpbmdBYm9ydCkuXG4gICAgICBjb25zdCBhYm9ydFBheWxvYWQgPVxuICAgICAgICBob29rLmFib3J0UGF5bG9hZCBpbnN0YW5jZW9mIFVpbnQ4QXJyYXlcbiAgICAgICAgICA/ICgoYXdhaXQgZW5jcnlwdFNlcmlhbGl6ZWREYXRhKFxuICAgICAgICAgICAgICBob29rLmFib3J0UGF5bG9hZCxcbiAgICAgICAgICAgICAgZW5jcnlwdGlvbktleVxuICAgICAgICAgICAgKSkgYXMgVWludDhBcnJheSlcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUocnVuSWQsIHtcbiAgICAgICAgICBldmVudFR5cGU6ICdob29rX3JlY2VpdmVkJyxcbiAgICAgICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICAgICAgY29ycmVsYXRpb25JZDogaG9vay5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgdG9rZW46IGhvb2sudG9rZW4sXG4gICAgICAgICAgICBwYXlsb2FkOiBhYm9ydFBheWxvYWQsXG4gICAgICAgICAgfSBhcyBhbnksXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGlmICghRW50aXR5Q29uZmxpY3RFcnJvci5pcyhlcnIpKSB0aHJvdyBlcnI7XG4gICAgICB9XG4gICAgICAvLyBzdHJlYW1OYW1lIGlzIGRlcml2ZWQgZnJvbSB0aGUgYWJvcnQgaG9vayB0b2tlblxuICAgICAgLy8gKGBhYnJ0X3tpZH1gIOKGkiBgc3RybV97aWR9X3N5c3RlbV9hYm9ydGApLlxuICAgICAgaWYgKGhvb2sudG9rZW4uc3RhcnRzV2l0aCgnYWJydF8nKSAmJiBhYm9ydFBheWxvYWQpIHtcbiAgICAgICAgY29uc3Qgc3RyZWFtTmFtZSA9IGBzdHJtXyR7aG9vay50b2tlbi5zbGljZSgnYWJydF8nLmxlbmd0aCl9X3N5c3RlbV9hYm9ydGA7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgd29ybGQuc3RyZWFtcy53cml0ZShydW5JZCwgc3RyZWFtTmFtZSwgYWJvcnRQYXlsb2FkKTtcbiAgICAgICAgICBhd2FpdCB3b3JsZC5zdHJlYW1zLmNsb3NlKHJ1bklkLCBzdHJlYW1OYW1lKTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gQmVzdC1lZmZvcnQ6IHRoZSBob29rIGV2ZW50IHByb3ZpZGVzIHRoZSBkdXJhYmxlXG4gICAgICAgICAgLy8gZmFsbGJhY2suXG4gICAgICAgICAgcnVudGltZUxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgICAgICdRdWlja0pTIHJ1bnRpbWU6IGZhaWxlZCB0byB3cml0ZSBhYm9ydCBzdHJlYW0gcGFja2V0JyxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IGhvb2suY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB3ZmRpYWcoJ2Fib3J0X3JlY29yZGVkJywge1xuICAgICAgICBjb3JyZWxhdGlvbklkOiBob29rLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgIHRva2VuOiBob29rLnRva2VuLFxuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IHByb2Nlc3NIb29rRGlzcG9zZU9wID0gYXN5bmMgKFxuICAgIG9wOiBQZW5kaW5nSG9va0Rpc3Bvc2VcbiAgKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUocnVuSWQsIHtcbiAgICAgICAgZXZlbnRUeXBlOiAnaG9va19kaXNwb3NlZCcsXG4gICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgY29ycmVsYXRpb25JZDogb3AuY29ycmVsYXRpb25JZCxcbiAgICAgICAgLy8gVGhlIGhvb2sncyB0b2tlbiwgd2hpY2ggdGhlIG5vZGU6dm0gZW5naW5lIGhhcyBhbHdheXMgc2VudC4gQSB3b3JsZFxuICAgICAgICAvLyB0aGF0IGtleXMgYSBob29rJ3MgdG9rZW4gY2xhaW0gc2VwYXJhdGVseSBmcm9tIHRoZSBob29rIGl0c2VsZiBuZWVkc1xuICAgICAgICAvLyBpdCB0byByZWxlYXNlIGJvdGgsIGFuZCB0aGUgb25seSBhbHRlcm5hdGl2ZSBpcyBmb3IgaXQgdG8gbG9vayB0aGVcbiAgICAgICAgLy8gdG9rZW4gdXAgZmlyc3QuIE9taXR0ZWQgcmF0aGVyIHRoYW4gc2VudCBhcyB1bmRlZmluZWQgd2hlbiB0aGUgb3BcbiAgICAgICAgLy8gY2FycmllcyBub25lLCBzbyBhIHdvcmxkIHRoYXQgcmVhZHMgaXQgY2Fubm90IHRlbGwgdGhlIGRpZmZlcmVuY2VcbiAgICAgICAgLy8gYmV0d2VlbiB0aGlzIGVuZ2luZSBhbmQgYSBjbGllbnQgdG9vIG9sZCB0byBzZW5kIG9uZS5cbiAgICAgICAgLi4uKG9wLnRva2VuID09PSB1bmRlZmluZWQgPyB7fSA6IHsgZXZlbnREYXRhOiB7IHRva2VuOiBvcC50b2tlbiB9IH0pLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBpZiAoRW50aXR5Q29uZmxpY3RFcnJvci5pcyhlcnIpKSByZXR1cm47XG4gICAgICAvLyBEaXNwb3NpbmcgYSBob29rIHdob3NlIGVudGl0eSBubyBsb25nZXIgKG9yIG5ldmVyKSBleGlzdHMgaXMgYW5cbiAgICAgIC8vIGlkZW1wb3RlbnQgbm8tb3A6IHRoZSBlbnRpdHkgbWF5IGhhdmUgYmVlbiB0b3JuIGRvd24gYnkgYVxuICAgICAgLy8gY29uY3VycmVudCBydW4gY2FuY2VsbGF0aW9uLCBvciB0aGUgaG9vayBtYXkgaGF2ZSBsb3N0IGl0c1xuICAgICAgLy8gdG9rZW4gY2xhaW0gdG8gYSBjb25mbGljdC4gVGhlcmUgaXMgbm90aGluZyBsZWZ0IHRvIHJlbGVhc2UuXG4gICAgICBpZiAoSG9va05vdEZvdW5kRXJyb3IuaXMoZXJyKSkgcmV0dXJuO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgfTtcblxuICAvLyBIb29rIG9wZXJhdGlvbnMgYXJlIGdyb3VwZWQgYnkgdG9rZW4gYW5kIHByb2Nlc3NlZCBTRVFVRU5USUFMTFkgaW5cbiAgLy8gY29kZSBvcmRlciB3aXRoaW4gZWFjaCBncm91cCwgbWlycm9yaW5nIHRoZSBub2RlOnZtIHN1c3BlbnNpb25cbiAgLy8gaGFuZGxlciAoaG9va0l0ZW1zQnlUb2tlbik6IGEgZGlzcG9zZSgpIG9mIGFuIGVhcmxpZXIgaG9vayBtdXN0XG4gIC8vIHJlbGVhc2UgdGhlIHRva2VuIGJlZm9yZSBhIGxhdGVyIHNhbWUtdG9rZW4gaG9vaydzIGNyZWF0aW9uIGlzXG4gIC8vIHZhbGlkYXRlZCBieSB0aGUgd29ybGQ6IHBhcmFsbGVsIGRpc3BhdGNoIHdvdWxkIG90aGVyd2lzZSByZWNvcmQgYVxuICAvLyBzcHVyaW91cyBob29rX2NvbmZsaWN0IGFnYWluc3QgdGhlIHJ1bidzIG93biBkaXNwb3NlZCBob29rIChlLmcuIGFcbiAgLy8gZGlzcG9zZeKGknJlY3JlYXRlIGxvb3AgcmV1c2luZyBvbmUgdG9rZW4pLiBEaWZmZXJlbnQgdG9rZW5zIGhhdmUgbm9cbiAgLy8gY2xhaW0gaW50ZXJhY3Rpb24sIHNvIHRva2VuIGdyb3VwcyBydW4gaW4gcGFyYWxsZWwgd2l0aCBlYWNoIG90aGVyXG4gIC8vIGFuZCB3aXRoIHRoZSBub24taG9vayBvcHMgYmVsb3cuXG4gIGNvbnN0IGhvb2tPcHNCeVRva2VuID0gbmV3IE1hcDxcbiAgICBzdHJpbmcsXG4gICAgKFBlbmRpbmdIb29rIHwgUGVuZGluZ0hvb2tEaXNwb3NlKVtdXG4gID4oKTtcbiAgZm9yIChjb25zdCBvcCBvZiBwZW5kaW5nT3BlcmF0aW9ucykge1xuICAgIGxldCBrZXk6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICBpZiAoXG4gICAgICBvcC50eXBlID09PSAnaG9vaycgJiZcbiAgICAgICghb3AuaGFzQ3JlYXRlZEV2ZW50IHx8IChvcCBhcyBQZW5kaW5nSG9vaykuYWJvcnRSZXF1ZXN0ZWQpXG4gICAgKSB7XG4gICAgICBrZXkgPSAob3AgYXMgUGVuZGluZ0hvb2spLnRva2VuO1xuICAgIH0gZWxzZSBpZiAob3AudHlwZSA9PT0gJ2hvb2tfZGlzcG9zZScgJiYgIW9wLmhhc0NyZWF0ZWRFdmVudCkge1xuICAgICAgLy8gUGVyLW9wIGZhbGxiYWNrIGdyb3VwIHdoZW4gdGhlIHRva2VuIGlzIHVua25vd246IG5vIG9yZGVyaW5nXG4gICAgICAvLyBndWFyYW50ZWVzLCBtYXRjaGluZyB0aGUgcHJldmlvdXMgcGFyYWxsZWwgYmVoYXZpb3IuXG4gICAgICBrZXkgPSAob3AgYXMgUGVuZGluZ0hvb2tEaXNwb3NlKS50b2tlbiA/PyBgX19jaWQ6JHtvcC5jb3JyZWxhdGlvbklkfWA7XG4gICAgfVxuICAgIGlmIChrZXkgPT09IHVuZGVmaW5lZCkgY29udGludWU7XG4gICAgY29uc3QgZ3JvdXAgPSBob29rT3BzQnlUb2tlbi5nZXQoa2V5KTtcbiAgICBpZiAoZ3JvdXApIHtcbiAgICAgIGdyb3VwLnB1c2gob3AgYXMgUGVuZGluZ0hvb2sgfCBQZW5kaW5nSG9va0Rpc3Bvc2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICBob29rT3BzQnlUb2tlbi5zZXQoa2V5LCBbb3AgYXMgUGVuZGluZ0hvb2sgfCBQZW5kaW5nSG9va0Rpc3Bvc2VdKTtcbiAgICB9XG4gIH1cbiAgZm9yIChjb25zdCBncm91cCBvZiBob29rT3BzQnlUb2tlbi52YWx1ZXMoKSkge1xuICAgIG9wc1Byb21pc2VzLnB1c2goXG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBmb3IgKGNvbnN0IG9wIG9mIGdyb3VwKSB7XG4gICAgICAgICAgaWYgKG9wLnR5cGUgPT09ICdob29rJykge1xuICAgICAgICAgICAgYXdhaXQgcHJvY2Vzc0hvb2tPcChvcCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGF3YWl0IHByb2Nlc3NIb29rRGlzcG9zZU9wKG9wKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pKClcbiAgICApO1xuICB9XG5cbiAgZm9yIChjb25zdCBvcCBvZiBwZW5kaW5nT3BlcmF0aW9ucykge1xuICAgIGlmIChcbiAgICAgIG9wLnR5cGUgPT09ICdzdGVwJyAmJlxuICAgICAgIW9wLmhhc0NyZWF0ZWRFdmVudCAmJlxuICAgICAgIXNraXBTdGVwQ3JlYXRpb24/LmhhcyhvcC5jb3JyZWxhdGlvbklkKVxuICAgICkge1xuICAgICAgY29uc3Qgc3RlcCA9IG9wIGFzIFBlbmRpbmdTdGVwO1xuICAgICAgb3BzUHJvbWlzZXMucHVzaChcbiAgICAgICAgKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAvLyBUaGUgc3RlcCdzIGlucHV0IHJlZnVzZWQgdG8gc2VyaWFsaXplIHdoaWxlIGR1bXBpbmcgdGhlIFZNJ3NcbiAgICAgICAgICAvLyBwZW5kaW5nIG9wcyAoc2VlIFBlbmRpbmdTdGVwLnNlcmlhbGl6YXRpb25FcnJvcikuIEZpbmFsaXplIGl0XG4gICAgICAgICAgLy8gYXMgc3RlcF9jcmVhdGVkIChwbGFjZWhvbGRlciBpbnB1dCwgc2luY2UgdGhlIHdvcmxkIHJlcXVpcmVzIHRoZVxuICAgICAgICAgIC8vIHN0ZXAgZW50aXR5IGJlZm9yZSBhIHRlcm1pbmFsIGV2ZW50KSArIHN0ZXBfZmFpbGVkIGNhcnJ5aW5nXG4gICAgICAgICAgLy8gdGhlIFNlcmlhbGl6YXRpb25FcnJvciwgc28gdGhlIGxpdmUtVk0gZmVlZCByZWplY3RzIHRoZVxuICAgICAgICAgIC8vIHN0ZXAncyBwcm9taXNlIGFuZCB3b3JrZmxvdyBjb2RlIGNhbiBjYXRjaCBpdC4gTmV2ZXIgcXVldWUgYW5cbiAgICAgICAgICAvLyBleGVjdXRpb24gbWVzc2FnZSBmb3IgaXQuIE1pcnJvcnMgdGhlIG5vZGU6dm0gZW5naW5lJ3NcbiAgICAgICAgICAvLyBmaW5hbGl6ZVVuc2VyaWFsaXphYmxlU3RlcC4gSW4gdGhlIHRlcm1pbmFsIGRyYWluXG4gICAgICAgICAgLy8gKGZpbmFsaXplVW5zZXJpYWxpemFibGVTdGVwcyB1bnNldCksIHNraXAgZW50aXJlbHk6IHNlZSB0aGVcbiAgICAgICAgICAvLyBwYXJhbSBkb2NzLlxuICAgICAgICAgIGlmIChzdGVwLnNlcmlhbGl6YXRpb25FcnJvcikge1xuICAgICAgICAgICAgaWYgKCFwYXJhbXMuZmluYWxpemVVbnNlcmlhbGl6YWJsZVN0ZXBzKSB7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJ1bnRpbWVMb2dnZXIud2FybihcbiAgICAgICAgICAgICAgJ1N0ZXAgYXJndW1lbnRzIGZhaWxlZCB0byBzZXJpYWxpemU7IGZhaWxpbmcgdGhlIHN0ZXAgc28gJyArXG4gICAgICAgICAgICAgICAgJ3RoZSB3b3JrZmxvdyBjYW4gb2JzZXJ2ZSB0aGUgZXJyb3InLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgICAgIHN0ZXBOYW1lOiBzdGVwLnN0ZXBJZCxcbiAgICAgICAgICAgICAgICBlcnJvcjogc3RlcC5zZXJpYWxpemF0aW9uRXJyb3IubWVzc2FnZSxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUocnVuSWQsIHtcbiAgICAgICAgICAgICAgICBldmVudFR5cGU6ICdzdGVwX2NyZWF0ZWQnLFxuICAgICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzdGVwLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgICBzdGVwTmFtZTogc3RlcC5zdGVwSWQsXG4gICAgICAgICAgICAgICAgICBpbnB1dDogKGF3YWl0IGRlaHlkcmF0ZVN0ZXBBcmd1bWVudHMoXG4gICAgICAgICAgICAgICAgICAgIHVuc2VyaWFsaXphYmxlU3RlcElucHV0UGxhY2Vob2xkZXIoKSxcbiAgICAgICAgICAgICAgICAgICAgcnVuSWQsXG4gICAgICAgICAgICAgICAgICAgIGVuY3J5cHRpb25LZXksXG4gICAgICAgICAgICAgICAgICAgIGdsb2JhbFRoaXMsXG4gICAgICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAod29ya2Zsb3dSdW4uc3BlY1ZlcnNpb24gPz8gMCkgPj1cbiAgICAgICAgICAgICAgICAgICAgICBTUEVDX1ZFUlNJT05fU1VQUE9SVFNfQ09NUFJFU1NJT05cbiAgICAgICAgICAgICAgICAgICkpIGFzIFVpbnQ4QXJyYXksXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgLy8gQ29uY3VycmVudCBpbnZvY2F0aW9uIGhpdCB0aGUgc2FtZSBkZXRlcm1pbmlzdGljIGZhaWx1cmVcbiAgICAgICAgICAgICAgLy8gYW5kIGNyZWF0ZWQgaXQgZmlyc3QsIG9yIHRoZSBydW4gYWxyZWFkeSBmaW5pc2hlZC5cbiAgICAgICAgICAgICAgaWYgKFJ1bkV4cGlyZWRFcnJvci5pcyhlcnIpKSByZXR1cm47XG4gICAgICAgICAgICAgIGlmICghRW50aXR5Q29uZmxpY3RFcnJvci5pcyhlcnIpKSB0aHJvdyBlcnI7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBhd2FpdCB3b3JsZC5ldmVudHMuY3JlYXRlKHJ1bklkLCB7XG4gICAgICAgICAgICAgICAgZXZlbnRUeXBlOiAnc3RlcF9mYWlsZWQnLFxuICAgICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzdGVwLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgICBzdGVwTmFtZTogc3RlcC5zdGVwSWQsXG4gICAgICAgICAgICAgICAgICBlcnJvcjogYXdhaXQgZGVoeWRyYXRlU3RlcEVycm9yKFxuICAgICAgICAgICAgICAgICAgICBzdGVwLnNlcmlhbGl6YXRpb25FcnJvcixcbiAgICAgICAgICAgICAgICAgICAgcnVuSWQsXG4gICAgICAgICAgICAgICAgICAgIGVuY3J5cHRpb25LZXksXG4gICAgICAgICAgICAgICAgICAgIFtdLFxuICAgICAgICAgICAgICAgICAgICBnbG9iYWxUaGlzLFxuICAgICAgICAgICAgICAgICAgICAod29ya2Zsb3dSdW4uc3BlY1ZlcnNpb24gPz8gMCkgPj1cbiAgICAgICAgICAgICAgICAgICAgICBTUEVDX1ZFUlNJT05fU1VQUE9SVFNfQ09NUFJFU1NJT05cbiAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgLy8gU3RlcCBhbHJlYWR5IHRlcm1pbmFsIG9yIHJ1biBhbHJlYWR5IGZpbmlzaGVkLlxuICAgICAgICAgICAgICBpZiAoIUVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZXJyKSAmJiAhUnVuRXhwaXJlZEVycm9yLmlzKGVycikpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZhaWxlZFNlcmlhbGl6YXRpb25TdGVwQ2lkcy5hZGQoc3RlcC5jb3JyZWxhdGlvbklkKTtcbiAgICAgICAgICAgIHdmZGlhZygnc3RlcF9zZXJpYWxpemF0aW9uX2ZhaWxlZCcsIHtcbiAgICAgICAgICAgICAgc3RlcElkOiBzdGVwLnN0ZXBJZCxcbiAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ3JlYXRlIHN0ZXBfY3JlYXRlZCBldmVudC4gYHN0ZXAuaW5wdXRgIGlzIHRoZVxuICAgICAgICAgIC8vIGZvcm1hdC1wcmVmaXhlZCBkZXZhbHVlIGJ5dGVzIChcImRldmxcIiArIGRldmFsdWUpIHByb2R1Y2VkXG4gICAgICAgICAgLy8gYnkgYGdsb2JhbFRoaXNbU3ltYm9sLmZvcignd29ya2Zsb3ctc2VyaWFsaXplJyldKHthcmdzLFxuICAgICAgICAgIC8vIGNsb3N1cmVWYXJzLCB0aGlzVmFsfSlgIGluc2lkZSB0aGUgVk0uIFRoZSBWTSBoYXMgbm9cbiAgICAgICAgICAvLyBhY2Nlc3MgdG8gdGhlIENyeXB0b0tleSwgc28gZW5jcnlwdGlvbiBpcyBhcHBsaWVkIGhlcmVcbiAgICAgICAgICAvLyBvbiB0aGUgaG9zdCBzaWRlLCBtYXRjaGluZyB3aGF0XG4gICAgICAgICAgLy8gYGRlaHlkcmF0ZVN0ZXBBcmd1bWVudHNgIGRvZXMgaW4gdGhlIG5vZGU6dm0gZW5naW5lLlxuICAgICAgICAgIGNvbnN0IGVuY3J5cHRlZElucHV0ID0gYXdhaXQgZW5jcnlwdFNlcmlhbGl6ZWREYXRhKFxuICAgICAgICAgICAgc3RlcC5pbnB1dCxcbiAgICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gUmVzaWxpZW50IHN0ZXAgZGlzcGF0Y2g6IGZpcmUgdGhlIHN0ZXBfY3JlYXRlZCB3cml0ZSBhbmQgdGhlXG4gICAgICAgICAgLy8gc3RlcCdzIHF1ZXVlIHB1Ymxpc2ggaW4gcGFyYWxsZWw6IHRoZSBtZXNzYWdlIGNhcnJpZXMgdGhlXG4gICAgICAgICAgLy8gc2FtZSBzZXJpYWxpemVkIGlucHV0IChgc3RlcElucHV0YCkgc28gdGhlIGNvbnN1bWVyIGNhblxuICAgICAgICAgIC8vIGlkZW1wb3RlbnRseSByZS1lbnN1cmUgdGhlIGV2ZW50IGlmIHRoZSBkaXJlY3Qgd3JpdGUgZmFpbGVkXG4gICAgICAgICAgLy8gdHJhbnNpZW50bHkuIE1pcnJvcnMgdGhlIG5vZGU6dm0gc3VzcGVuc2lvbiBoYW5kbGVyIGFuZCB0aGVcbiAgICAgICAgICAvLyByZXNpbGllbnQgc3RhcnQgLyByZXNpbGllbnQgaG9vayByZXN1bWUgcGF0dGVybnMuIE9ubHkgZm9yXG4gICAgICAgICAgLy8gY2FsbGVyLWRlc2lnbmF0ZWQgb3ZlcmZsb3cgc3RlcHMgd2l0aCBpbnB1dHMgdGhlIHF1ZXVlXG4gICAgICAgICAgLy8gbWVzc2FnZSBjYW4gc2FmZWx5IGNhcnJ5IChiaW5hcnksIHVuZGVyIHRoZSBWUVMgc2l6ZSBjYXApLlxuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIHJlc2lsaWVudERpc3BhdGNoRWxpZ2libGUgJiZcbiAgICAgICAgICAgIHF1ZXVlU3RlcENpZHM/LmhhcyhzdGVwLmNvcnJlbGF0aW9uSWQpICYmXG4gICAgICAgICAgICBlbmNyeXB0ZWRJbnB1dCBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkgJiZcbiAgICAgICAgICAgIGVuY3J5cHRlZElucHV0LmJ5dGVMZW5ndGggPD0gTUFYX1JFU0lMSUVOVF9TVEVQX0lOUFVUX0JZVEVTXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICBjb25zdCBbY3JlYXRlUmVzdWx0LCBxdWV1ZVJlc3VsdF0gPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xuICAgICAgICAgICAgICB3b3JsZC5ldmVudHMuY3JlYXRlKHJ1bklkLCB7XG4gICAgICAgICAgICAgICAgZXZlbnRUeXBlOiAnc3RlcF9jcmVhdGVkJyxcbiAgICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgICAgICAgc3RlcE5hbWU6IHN0ZXAuc3RlcElkLFxuICAgICAgICAgICAgICAgICAgaW5wdXQ6IGVuY3J5cHRlZElucHV0LFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICBxdWV1ZVN0ZXBNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICB3b3JsZCxcbiAgICAgICAgICAgICAgICBydW5JZCxcbiAgICAgICAgICAgICAgICB3b3JrZmxvd1J1bixcbiAgICAgICAgICAgICAgICBzdGVwLFxuICAgICAgICAgICAgICAgIG5hbWVzcGFjZSxcbiAgICAgICAgICAgICAgICBuZXh0VHJhY2VDYXJyaWVyLFxuICAgICAgICAgICAgICAgIHB1cnBvc2U6ICdkaXNwYXRjaCcsXG4gICAgICAgICAgICAgICAgc3RlcElucHV0OiBlbmNyeXB0ZWRJbnB1dCxcbiAgICAgICAgICAgICAgICB3ZmRpYWcsXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgXSk7XG4gICAgICAgICAgICAvLyBRdWV1ZSBmYWlsdXJlIGlzIGFsd2F5cyBmYXRhbCBmb3IgdGhpcyBkaXNwYXRjaCBwYXNzOlxuICAgICAgICAgICAgLy8gd2l0aG91dCB0aGUgbWVzc2FnZSB0aGUgc3RlcCB3b3VsZCByZWx5IG9uIHRoZSBjcmVhdGVcbiAgICAgICAgICAgIC8vIGFsb25lLCBhbmQgaWYgdGhlIGNyZWF0ZSBBTFNPIGZhaWxlZCB0aGVyZSB3b3VsZCBiZSBub1xuICAgICAgICAgICAgLy8gZHVyYWJsZSByZWNvcmQgYXQgYWxsLiBQcm9wYWdhdGluZyByZWRlbGl2ZXJzIHRoZVxuICAgICAgICAgICAgLy8gb3JjaGVzdHJhdG9yIG1lc3NhZ2UsIHdoaWNoIHJlLWNyZWF0ZXMgdGhlIChpZGVtcG90ZW50KVxuICAgICAgICAgICAgLy8gc3RlcF9jcmVhdGVkIGFuZCByZS1kaXNwYXRjaGVzLlxuICAgICAgICAgICAgaWYgKHF1ZXVlUmVzdWx0LnN0YXR1cyA9PT0gJ3JlamVjdGVkJykge1xuICAgICAgICAgICAgICB0aHJvdyBxdWV1ZVJlc3VsdC5yZWFzb247XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBxdWV1ZWRTdGVwQ2lkcy5hZGQoc3RlcC5jb3JyZWxhdGlvbklkKTtcbiAgICAgICAgICAgIGlmIChjcmVhdGVSZXN1bHQuc3RhdHVzID09PSAncmVqZWN0ZWQnKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGVyciA9IGNyZWF0ZVJlc3VsdC5yZWFzb247XG4gICAgICAgICAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKGVycikpIHtcbiAgICAgICAgICAgICAgICAvLyBDb25jdXJyZW50IGludm9jYXRpb24gd3JvdGUgaXQgZmlyc3Q6IHRoZSBtZXNzYWdlIGlzXG4gICAgICAgICAgICAgICAgLy8gYWxyZWFkeSBvdXQ7IGl0cyBkdXBsaWNhdGUgcHVibGlzaCBkZWR1cGVzIG9uIHRoZVxuICAgICAgICAgICAgICAgIC8vIHNoYXJlZCBzdGVwLWlkZW50aXR5LXNjb3BlZCBpZGVtcG90ZW5jeSBrZXkuXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChpc1JldHJ5YWJsZVdvcmxkRXJyb3IoZXJyKSkge1xuICAgICAgICAgICAgICAgIC8vIFJlc2lsaWVudDogdGhlIHdyaXRlIGZhaWxlZCB0cmFuc2llbnRseSAoNDI5IC8gNXh4IC9cbiAgICAgICAgICAgICAgICAvLyB0cmFuc3BvcnQpIGJ1dCB0aGUgc3RlcCBtZXNzYWdlIChjYXJyeWluZyB0aGUgc2FtZVxuICAgICAgICAgICAgICAgIC8vIHNlcmlhbGl6ZWQgaW5wdXQpIHdhcyBwdWJsaXNoZWQsIHNvIHRoZSBjb25zdW1lclxuICAgICAgICAgICAgICAgIC8vIGlkZW1wb3RlbnRseSByZS1lbnN1cmVzIHRoZSBzdGVwX2NyZWF0ZWQgYmVmb3JlXG4gICAgICAgICAgICAgICAgLy8gZXhlY3V0aW5nLlxuICAgICAgICAgICAgICAgIHJ1bnRpbWVMb2dnZXIud2FybihcbiAgICAgICAgICAgICAgICAgICdTdGVwIGNyZWF0aW9uIGV2ZW50IHdyaXRlIGZhaWxlZCwgYnV0IHRoZSBzdGVwIHdhcyAnICtcbiAgICAgICAgICAgICAgICAgICAgJ2Rpc3BhdGNoZWQgdmlhIHRoZSBxdWV1ZS4gVGhlIHN0ZXBfY3JlYXRlZCBldmVudCAnICtcbiAgICAgICAgICAgICAgICAgICAgJ3dpbGwgYmUgZW5zdXJlZCBieSB0aGUgcXVldWUgY29uc3VtZXIuJyxcbiAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICAgICAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IHN0ZXAuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgICAgICAgc3RlcE5hbWU6IHN0ZXAuc3RlcElkLFxuICAgICAgICAgICAgICAgICAgICBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpLFxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgd2ZkaWFnKCdzdGVwX3Jlc2lsaWVudF9kaXNwYXRjaF9yZWNvdmVyZWQnLCB7XG4gICAgICAgICAgICAgICAgICBzdGVwSWQ6IHN0ZXAuc3RlcElkLFxuICAgICAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUocnVuSWQsIHtcbiAgICAgICAgICAgICAgZXZlbnRUeXBlOiAnc3RlcF9jcmVhdGVkJyxcbiAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzdGVwLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgICAgIHN0ZXBOYW1lOiBzdGVwLnN0ZXBJZCxcbiAgICAgICAgICAgICAgICBpbnB1dDogZW5jcnlwdGVkSW5wdXQsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKGVycikpIHJldHVybjtcbiAgICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBOT1RFOiBzdGVwIHF1ZXVlaW5nIGlzIG90aGVyd2lzZSB0aGUgY2FsbGVyJ3MgZGVjaXNpb246IHRoZVxuICAgICAgICAgIC8vIGlubGluZSBsb29wIGV4ZWN1dGVzIGZyZXNoIHN0ZXBzIGluIHRoZSBsaXZlIFZNIGFuZCBvbmx5XG4gICAgICAgICAgLy8gcXVldWVzIHRoZSBvdmVyZmxvdyAvIHJldHJ5IC8gYmFja3N0b3AgY2FzZXMgKHNlZVxuICAgICAgICAgIC8vIHF1ZXVlU3RlcE1lc3NhZ2UpLlxuICAgICAgICB9KSgpXG4gICAgICApO1xuICAgIH0gZWxzZSBpZiAob3AudHlwZSA9PT0gJ2F0dHJpYnV0ZScgJiYgIW9wLmhhc0NyZWF0ZWRFdmVudCkge1xuICAgICAgY29uc3QgYXR0ciA9IG9wIGFzIFBlbmRpbmdBdHRyaWJ1dGU7XG4gICAgICBvcHNQcm9taXNlcy5wdXNoKFxuICAgICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB3b3JsZC5ldmVudHMuY3JlYXRlKHJ1bklkLCB7XG4gICAgICAgICAgICAgIGV2ZW50VHlwZTogJ2F0dHJfc2V0JyxcbiAgICAgICAgICAgICAgc3BlY1ZlcnNpb246IFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBhdHRyLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgICAgIGNoYW5nZXM6IGF0dHIuY2hhbmdlcyxcbiAgICAgICAgICAgICAgICB3cml0ZXI6IHsgdHlwZTogJ3dvcmtmbG93JyB9LFxuICAgICAgICAgICAgICAgIC4uLihhdHRyLmFsbG93UmVzZXJ2ZWRBdHRyaWJ1dGVzXG4gICAgICAgICAgICAgICAgICA/IHsgYWxsb3dSZXNlcnZlZEF0dHJpYnV0ZXM6IHRydWUgfVxuICAgICAgICAgICAgICAgICAgOiB7fSksXG4gICAgICAgICAgICAgIH0gYXMgYW55LFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjcmVhdGVkQXR0cmlidXRlRXZlbnQgPSB0cnVlO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZXJyKSkge1xuICAgICAgICAgICAgICAvLyBFdmVudCBhbHJlYWR5IGV4aXN0cyAoY29uY3VycmVudCBpbnZvY2F0aW9uKSwgYnV0IHRoZVxuICAgICAgICAgICAgICAvLyByZXBsYXkgc3RpbGwgbmVlZHMgdG8gY29uc3VtZSBpdCwgc28gcmVxdWV1ZS5cbiAgICAgICAgICAgICAgY3JlYXRlZEF0dHJpYnV0ZUV2ZW50ID0gdHJ1ZTtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgICAgIH1cbiAgICAgICAgfSkoKVxuICAgICAgKTtcbiAgICB9IGVsc2UgaWYgKG9wLnR5cGUgPT09ICd3YWl0JyAmJiAhb3AuaGFzQ3JlYXRlZEV2ZW50KSB7XG4gICAgICBjb25zdCB3YWl0ID0gb3AgYXMgUGVuZGluZ1dhaXQ7XG4gICAgICBvcHNQcm9taXNlcy5wdXNoKFxuICAgICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB3b3JsZC5ldmVudHMuY3JlYXRlKHJ1bklkLCB7XG4gICAgICAgICAgICAgIGV2ZW50VHlwZTogJ3dhaXRfY3JlYXRlZCcsXG4gICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogd2FpdC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgICAgICByZXN1bWVBdDogbmV3IERhdGUod2FpdC5yZXN1bWVBdCksXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKGVycikpIHJldHVybjtcbiAgICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgICB9XG4gICAgICAgIH0pKClcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gUGVyLW9wIGRpc3BhdGNoIHJ1bnMgaW4gcGFyYWxsZWwuXG4gIGF3YWl0IFByb21pc2UuYWxsKG9wc1Byb21pc2VzKTtcblxuICByZXR1cm4ge1xuICAgIGNyZWF0ZWRBdHRyaWJ1dGVFdmVudCxcbiAgICBjcmVhdGVkR2V0Q29uZmxpY3RIb29rLFxuICAgIHF1ZXVlZFN0ZXBDaWRzLFxuICAgIGZhaWxlZFNlcmlhbGl6YXRpb25TdGVwQ2lkcyxcbiAgfTtcbn1cblxuLyoqXG4gKiBSdW4gYSB3b3JrZmxvdyB1c2luZyB0aGUgUXVpY2tKUyBXQVNNIFZNIGVuZ2luZS5cbiAqXG4gKiBUaGlzIHJlcGxhY2VzIHRoZSBgbm9kZTp2bWAgcmVwbGF5IHBhdGggKHJ1bldvcmtmbG93ICsgRXZlbnRzQ29uc3VtZXIpXG4gKiB3aXRoIGEgUXVpY2tKUyBWTSBpbnZvY2F0aW9uIHRoYXQgcGVyZm9ybXMgdGhlIHNhbWUgZnVsbCBldmVudCByZXBsYXkuXG4gKlxuICogS05PV04gR0FQIChzbG90IHNuYXBzaG90KTogdW5saWtlIHRoZSBub2RlOnZtIHBhdGgsIG5vIGV2ZW50IHdyaXRlIGluXG4gKiB0aGlzIGZpbGUgY2FycmllcyB7QGxpbmsgQ3JlYXRlRXZlbnRQYXJhbXMuZXZlbnRDb3VudH0sIHNvIGEgV29ybGQgbmV2ZXJcbiAqIGxlYXJucyB3aGljaCBldmVudHMgdGhlIHdyaXRlciBoYWQgbm90IHNlZW4gYW5kIG5ldmVyIHJlcG9ydHMgdGhlbSBiYWNrLlxuICogVGhlIGVuZ2luZSBjdXJyZW50bHkgcmVsaWVzIG9uIHBlci0ocnVuSWQsIGNvcnJlbGF0aW9uSWQpIGV2ZW50XG4gKiB1bmlxdWVuZXNzIChFbnRpdHlDb25mbGljdEVycm9yIGRlZHVwKSBhbG9uZS4gVGhpcyBpcyBhIGRlbGliZXJhdGVcbiAqIHNpbXBsaWZpY2F0aW9uIHdoaWxlIHRoZSBlbmdpbmUgaXMgZXhwZXJpbWVudGFsOiB3aXJpbmcgdGhlIHNuYXBzaG90IGlzXG4gKiB0cmFja2VkIGZvbGxvdy11cCB3b3JrOyBhbnlvbmUgYWRkaW5nIG5ldyB3cml0ZSBwYXRocyBoZXJlIHNob3VsZCBub3RcbiAqIGFzc3VtZSBwYXJpdHkgd2l0aCB0aGUgbm9kZSBlbmdpbmUgb24gdGhpcyBheGlzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcnVuV29ya2Zsb3dXaXRoUXVpY2tKUyhwYXJhbXM6IHtcbiAgd29ya2Zsb3dDb2RlOiBzdHJpbmc7XG4gIHdvcmtmbG93TmFtZTogc3RyaW5nO1xuICB3b3JrZmxvd1J1bjogV29ya2Zsb3dSdW47XG4gIC8qKlxuICAgKiBFdmVudHMgcmV0dXJuZWQgaW5saW5lIGJ5IGBldmVudHMuY3JlYXRlKCdydW5fc3RhcnRlZCcsIC4uLilgIG9yIGJ5XG4gICAqIHRoZSBsYXp5IGhvb2sgZmFzdCBwYXRoJ3MgYGhvb2tfcmVjZWl2ZWRgIHByZWxvYWQuIFdoZW4gdGhleSBpbmRpY2F0ZVxuICAgKiBhIGZpcnN0IGludm9jYXRpb24sIG9yIHdoZW4gYHByZWxvYWRlZEV2ZW50c0NvbXBsZXRlYCBhdHRlc3RzIHRoZXlcbiAgICogYXJlIHRoZSBjb21wbGV0ZSBsb2csIHRoZXkgYXJlIHVzZWQgYXMgdGhlIGV2ZW50IGxvZyBpbnN0ZWFkIG9mXG4gICAqIGZldGNoaW5nIHZpYSBgZXZlbnRzLmxpc3RgLCBtYXRjaGluZyB0aGUgbm9kZTp2bSBlbmdpbmUncyBmYXN0IHBhdGguXG4gICAqL1xuICBwcmVsb2FkZWRFdmVudHM/OiBFdmVudFtdO1xuICAvKipcbiAgICogVHJ1ZSB3aGVuIHRoZSBjYWxsZXIgaGFzIHZhbGlkYXRlZCB0aGF0IGBwcmVsb2FkZWRFdmVudHNgIGlzIHRoZSBydW4nc1xuICAgKiBDT01QTEVURSBldmVudCBsb2cgKGUuZy4gdGhlIGxhenkgaG9vayBmYXN0IHBhdGgncyBoYXNNb3JlLWZhbHNlXG4gICAqIHJlcGxheSBwcmVsb2FkKS4gVGhlIGZpcnN0LWludm9jYXRpb24gaGV1cmlzdGljIGJlbG93IG9ubHkgcmVjb2duaXplc1xuICAgKiBydW5fY3JlYXRlZC9ydW5fc3RhcnRlZC1vbmx5IHByZWxvYWRzLCBzbyB3aXRob3V0IHRoaXMgYXR0ZXN0YXRpb24gYVxuICAgKiBob29rLXJlc3VtZSBwcmVsb2FkIHdvdWxkIGJlIGRpc2NhcmRlZCBhbmQgcmVmZXRjaGVkLlxuICAgKi9cbiAgcHJlbG9hZGVkRXZlbnRzQ29tcGxldGU/OiBib29sZWFuO1xuICAvKipcbiAgICogUnVuIGlucHV0IGNhcnJpZWQgdGhyb3VnaCB0aGUgcXVldWUgbWVzc2FnZSBvbiBmaXJzdCBkZWxpdmVyeS4gVXNlZFxuICAgKiBhcyBhIGxhc3QtcmVzb3J0IGZhbGxiYWNrIGZvciBgcnVuX2NyZWF0ZWQuZXZlbnREYXRhLmlucHV0YCB3aGVuXG4gICAqIHRoZSBldmVudCBsb2cgaXMgaW5jb21wbGV0ZS5cbiAgICovXG4gIHJ1bklucHV0PzogUnVuSW5wdXQ7XG4gIC8qKlxuICAgKiBUaGUgcGFyZW50IE9UZWwgc3BhbiAodGhlIG91dGVyIGBXT1JLRkxPVyB7d29ya2Zsb3dOYW1lfWAgc3BhbiBmcm9tXG4gICAqIGBydW50aW1lLnRzYCkuIFdoZW4gc3VwcGxpZWQsIFZNIGxpZmVjeWNsZSBhdHRyaWJ1dGVzIGFyZSBhdHRhY2hlZFxuICAgKiB0byBpdCBmb3IgZW5kLXRvLWVuZCB2aXNpYmlsaXR5LlxuICAgKi9cbiAgcGFyZW50U3Bhbj86IFNwYW47XG4gIC8qKlxuICAgKiBTZXJ2ZXItc3VwcGxpZWQgcGVyLXJ1biBldmVudCBjZWlsaW5nIGZyb20gdGhlIHJ1bl9zdGFydGVkIHJlc3BvbnNlXG4gICAqICh1bmRlZmluZWQg4oeSIG5vIGVuZm9yY2VtZW50KS4gTWlycm9ycyB0aGUgbm9kZTp2bSBlbmdpbmUncyBndWFyZDpcbiAgICogYSBydW5hd2F5IHJ1biBpcyBmYWlsZWQgb25jZSBpdHMgbG9nIHJlYWNoZXMgdGhlIGNlaWxpbmcuIFRoZSB0aHJvd1xuICAgKiBwcm9wYWdhdGVzIHRvIHRoZSByZXBsYXkgbG9vcCdzIGNhdGNoIGluIHJ1bnRpbWUudHMgKHRoZSBRdWlja0pTXG4gICAqIGRpc3BhdGNoIHJ1bnMgaW5zaWRlIHRoYXQgbG9vcCdzIHRyeSksIHdoaWNoIGNsYXNzaWZpZXMgaXQgYW5kXG4gICAqIHJlY29yZHMgcnVuX2ZhaWxlZCB3aXRoIE1BWF9FVkVOVFNfRVhDRUVERUQuXG4gICAqL1xuICBtYXhFdmVudHNMaW1pdD86IG51bWJlcjtcbiAgLyoqXG4gICAqIFF1ZXVlIGRlbGl2ZXJ5IGF0dGVtcHQgb2YgdGhlIG1lc3NhZ2UgZHJpdmluZyB0aGlzIGludm9jYXRpb24gKGZyb21cbiAgICogdGhlIHF1ZXVlIGhhbmRsZXIncyBtZXRhZGF0YTsgMSA9IGZpcnN0IGRlbGl2ZXJ5KS4gU3VyZmFjZWQgaW5cbiAgICogZGlhZ25vc3RpY3M7IGNyYXNoIHJlY292ZXJ5IGl0c2VsZiBpcyBkcml2ZW4gYnkgdGhlIG93bmVyc2hpcC1sZWFzZVxuICAgKiBkZWNpc2lvbiB0YWJsZSBpbiB0aGUgbG9vcCwgbm90IGJ5IHRoZSBhdHRlbXB0IGNvdW50LlxuICAgKi9cbiAgZGVsaXZlcnlBdHRlbXB0PzogbnVtYmVyO1xuICAvKipcbiAgICogUXVldWUgbWVzc2FnZSBJRCBvZiB0aGUgZGVsaXZlcnkgZHJpdmluZyB0aGlzIGludm9jYXRpb24sIHN0YW1wZWQgYXNcbiAgICogYG93bmVyTWVzc2FnZUlkYCBvbiBpbmxpbmUgbGF6eSBzdGVwIGNsYWltcyBzbyB3YWtlIHJlcGxheXMgZGVmZXIgdG9cbiAgICogdGhlIGluLWZsaWdodCBib2R5IGluc3RlYWQgb2YgcmVxdWV1ZWluZyB0aGUgc3RlcC5cbiAgICovXG4gIG93bmVyTWVzc2FnZUlkPzogc3RyaW5nO1xuICAvKiogUmVxdWVzdCBJRCBvZiB0aGUgcXVldWUgaW52b2NhdGlvbiwgd2hlbiB0aGUgcXVldWUgcHJvdmlkZXMgb25lLiAqL1xuICByZXF1ZXN0SWQ/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBRdWV1ZSBuYW1lc3BhY2UgcmVzb2x2ZWQgYXQgcm91dGUgcmVnaXN0cmF0aW9uIChydW50aW1lLnRzKS4gTXVzdCBiZVxuICAgKiB0aHJlYWRlZCBpbnRvIGV2ZXJ5IG1lc3NhZ2UgcHVibGlzaDogdGhlIGJ1aWxkZXJzIGJha2UgdGhlIG5hbWVzcGFjZVxuICAgKiBpbnRvIGdlbmVyYXRlZCByb3V0ZXMsIHNvIGNvbnN1bWVycyBsaXN0ZW4gb24gYF9fPG5zPl93a2Zfd29ya2Zsb3dfKmAuXG4gICAqIEEgcHVibGlzaCB3aXRob3V0IGl0IGxhbmRzIG9uIGBfX3drZl93b3JrZmxvd18qYCBhbmQgaXMgbmV2ZXJcbiAgICogcGlja2VkIHVwLlxuICAgKi9cbiAgbmFtZXNwYWNlPzogc3RyaW5nO1xuICAvKipcbiAgICogUnVuLW9yaWdpbiB0cmFjZSBjYXJyaWVyIGFjY2Vzc29yIGZyb20gcnVudGltZS50c1xuICAgKiAoZ2V0TmV4dFRyYWNlQ2FycmllcikuIEluIHRoZSBkZWZhdWx0IGBsaW5rZWRgIHRyYWNlIG1vZGUgZXZlcnlcbiAgICogaW52b2NhdGlvbiBtdXN0IGxpbmsgYmFjayB0byB0aGUgcnVuJ3Mgb3JpZ2luICh3b3JrZmxvdy5zdGFydCkgaW4gYVxuICAgKiBzdGFyOyBjYXB0dXJpbmcgdGhlIGN1cnJlbnQgaW52b2NhdGlvbiBjb250ZXh0IGluc3RlYWQgd291bGQgY2hhaW5cbiAgICogaW52b2NhdGlvbnMgdG8gZWFjaCBvdGhlciBhbmQgZnJhZ21lbnQgdGhlIHJ1biB2aWV3IG9uIGFzeW5jIHF1ZXVlcy5cbiAgICovXG4gIG5leHRUcmFjZUNhcnJpZXI/OiAoKSA9PiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIHN0cmluZz4+O1xuICAvKipcbiAgICogVGhlIHdhaXQgdGhpcyBpbnZvY2F0aW9uIGlzIHRoZSBkZWxheWVkIGNvbnRpbnVhdGlvbiBmb3IsIGlmIGl0IGlzIG9uZVxuICAgKiAoYFdvcmtmbG93SW52b2tlUGF5bG9hZC53YWl0Q29udGludWF0aW9uYCkuIFJlYWQgb25seSB0byBkZWNpZGUgdGhlIG5leHRcbiAgICogY29udGludWF0aW9uJ3MgaWRlbXBvdGVuY3kga2V5OiBhIGNvbnRpbnVhdGlvbiB0aGF0IGZpbmRzIGl0cyBvd24gd2FpdFxuICAgKiBzdGlsbCBwZW5kaW5nIGhhcyBzcGVudCBpdHMga2V5LCBzbyB0aGUgcmUtYXJtIGhhcyB0byBhZHZhbmNlIHRoZSBhdHRlbXB0XG4gICAqIG9yIHRoZSB3b3JsZCdzIGRlZHVwZSB3aW5kb3cgZHJvcHMgaXQgYW5kIHRoZSB3YWl0IGxvc2VzIGl0cyBvbmx5IHRpbWVyLlxuICAgKi9cbiAgd2FpdENvbnRpbnVhdGlvbj86IHsgY29ycmVsYXRpb25JZDogc3RyaW5nOyBhdHRlbXB0OiBudW1iZXIgfTtcbn0pOiBQcm9taXNlPHsgdGltZW91dFNlY29uZHM/OiBudW1iZXIgfSB8IHZvaWQ+IHtcbiAgY29uc3Qge1xuICAgIHdvcmtmbG93Q29kZSxcbiAgICB3b3JrZmxvd05hbWUsXG4gICAgd29ya2Zsb3dSdW4sXG4gICAgcHJlbG9hZGVkRXZlbnRzLFxuICAgIHByZWxvYWRlZEV2ZW50c0NvbXBsZXRlLFxuICAgIHJ1bklucHV0LFxuICAgIHBhcmVudFNwYW4sXG4gICAgbWF4RXZlbnRzTGltaXQsXG4gICAgZGVsaXZlcnlBdHRlbXB0LFxuICAgIG93bmVyTWVzc2FnZUlkLFxuICAgIHJlcXVlc3RJZCxcbiAgICBuYW1lc3BhY2UsXG4gICAgd2FpdENvbnRpbnVhdGlvbixcbiAgfSA9IHBhcmFtcztcblxuICAvKipcbiAgICogQXR0ZW1wdCBudW1iZXIgZm9yIHRoZSBjb250aW51YXRpb24gdGhpcyBpbnZvY2F0aW9uIGlzIGFib3V0IHRvIGFybSBmb3JcbiAgICogYGNvcnJlbGF0aW9uSWRgLiBPbmUgaGlnaGVyIHRoYW4gdGhlIGluY29taW5nIGNvbnRpbnVhdGlvbidzIHdoZW4gdGhpc1xuICAgKiBpbnZvY2F0aW9uIElTIHRoYXQgY29udGludWF0aW9uIGFuZCB0aGUgd2FpdCBpcyBzdGlsbCBwZW5kaW5nIOKAlCB0aGUgb25seVxuICAgKiBzaXR1YXRpb24gaW4gd2hpY2ggdGhlIHByZXZpb3VzIGtleSBpcyBzcGVudC4gRXZlcnkgb3RoZXIgY2FsbGVyLCBhbmRcbiAgICogZXZlcnkgb3RoZXIgd2FpdCwgc3RhcnRzIGF0IDAgYW5kIGtleXMgZXhhY3RseSBhcyBpdCBkaWQgYmVmb3JlIGF0dGVtcHRzXG4gICAqIGV4aXN0ZWQuXG4gICAqL1xuICBjb25zdCBuZXh0V2FpdENvbnRpbnVhdGlvbkF0dGVtcHQgPSAoY29ycmVsYXRpb25JZDogc3RyaW5nKTogbnVtYmVyID0+XG4gICAgd2FpdENvbnRpbnVhdGlvbj8uY29ycmVsYXRpb25JZCA9PT0gY29ycmVsYXRpb25JZFxuICAgICAgPyB3YWl0Q29udGludWF0aW9uLmF0dGVtcHQgKyAxXG4gICAgICA6IDA7XG4gIC8vIFN0YW5kYWxvbmUtY2FsbGVyIGZhbGxiYWNrICh0ZXN0cyk6IHdpdGhvdXQgYSBydW50aW1lLnRzIGNhcnJpZXJcbiAgLy8gYWNjZXNzb3IsIGZhbGwgYmFjayB0byB0aGUgY3VycmVudCBpbnZvY2F0aW9uIGNvbnRleHQuXG4gIGNvbnN0IG5leHRUcmFjZUNhcnJpZXIgPVxuICAgIHBhcmFtcy5uZXh0VHJhY2VDYXJyaWVyID8/ICgoKSA9PiBzZXJpYWxpemVUcmFjZUNhcnJpZXIoKSk7XG4gIGNvbnN0IHdvcmxkID0gYXdhaXQgZ2V0V29ybGQoKTtcbiAgY29uc3QgcnVuSWQgPSB3b3JrZmxvd1J1bi5ydW5JZDtcbiAgY29uc3QgaW52b2NhdGlvblN0YXJ0ID0gdGljaygpO1xuXG4gIC8vIFN0cmlwIHRoZSBpbmxpbmUgc291cmNlIG1hcCBjb21tZW50IGJlZm9yZSBldmFsdWF0aW5nIHRoZSBidW5kbGUgaW5cbiAgLy8gdGhlIFF1aWNrSlMgVk0uIFRoZSBtYXAgaXMgcHVyZWx5IGhvc3Qtc2lkZSBtZXRhZGF0YSBmb3JcbiAgLy8gYHJlbWFwRXJyb3JTdGFja2AgKGNhbGxlZCBiZWxvdyBvbiB3b3JrZmxvdyBmYWlsdXJlcywgYWdhaW5zdCB0aGVcbiAgLy8gT1JJR0lOQUwgYHdvcmtmbG93Q29kZWApLiBRdWlja0pTIHJldGFpbnMgc291cmNlIHRleHQgZm9yXG4gIC8vIHN0YWNrLXRyYWNlIGxpbmUgbG9va3Vwcywgc28gdGhlIGZldy1NQiBiYXNlNjQgY29tbWVudCB3b3VsZCBibG9hdFxuICAvLyB0aGUgVk0gaGVhcCBmb3Igbm8gYmVuZWZpdC5cbiAgY29uc3Qgd29ya2Zsb3dDb2RlRm9yVk0gPSBzdHJpcElubGluZVNvdXJjZU1hcCh3b3JrZmxvd0NvZGUpO1xuXG4gIC8vIFBlci1pbnZvY2F0aW9uIGRpYWdub3N0aWMgaWQgc28gZGVidWcgbG9ncyBjYW4gYmUgY29ycmVsYXRlZCBldmVuIGlmXG4gIC8vIHRoZSBzYW1lIHJ1bklkIGlzIHByb2Nlc3NlZCBieSBvdmVybGFwcGluZyBpbnZvY2F0aW9ucyBvbiBkaWZmZXJlbnRcbiAgLy8gZnVuY3Rpb24gaW5zdGFuY2VzLlxuICBjb25zdCBpbnZvY2F0aW9uSWQgPSBgaW52XyR7TWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMiwgMTApfWA7XG5cbiAgLy8gU3RydWN0dXJlZCBwZXItY2hlY2twb2ludCBkaWFnbm9zdGljIGhlbHBlciwgZ3JlcC1mcmllbmRseSBieSBydW5JZC5cbiAgY29uc3Qgd2ZkaWFnID0gKGNoZWNrcG9pbnQ6IHN0cmluZywgZmllbGRzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4ge1xuICAgIHJ1bnRpbWVMb2dnZXIuZGVidWcoJ1FVSUNLSlNfVk1fRElBRycsIHtcbiAgICAgIGNoZWNrcG9pbnQsXG4gICAgICBydW5JZCxcbiAgICAgIGludm9jYXRpb25JZCxcbiAgICAgIHRFbGFwc2VkTXM6IE1hdGgucm91bmQodGljaygpIC0gaW52b2NhdGlvblN0YXJ0KSxcbiAgICAgIC4uLmZpZWxkcyxcbiAgICB9KTtcbiAgfTtcblxuICBwYXJlbnRTcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAuLi5BdHRyaWJ1dGUuV29ya2Zsb3dWbSgncXVpY2tqcycpLFxuICB9KTtcblxuICB3ZmRpYWcoJ2VudGVyJywge1xuICAgIHdvcmtmbG93TmFtZSxcbiAgICBkZWxpdmVyeUF0dGVtcHQsXG4gICAgaGFzUHJlbG9hZGVkRXZlbnRzOlxuICAgICAgQXJyYXkuaXNBcnJheShwcmVsb2FkZWRFdmVudHMpICYmIHByZWxvYWRlZEV2ZW50cy5sZW5ndGggPiAwLFxuICAgIHByZWxvYWRlZEV2ZW50Q291bnQ6IHByZWxvYWRlZEV2ZW50cz8ubGVuZ3RoID8/IDAsXG4gICAgaGFzUnVuSW5wdXQ6ICEhcnVuSW5wdXQsXG4gIH0pO1xuXG4gIC8vIFRoZSB3b3JrZmxvd05hbWUgZnJvbSB0aGUgcXVldWUgdG9waWMgaXMgYWxyZWFkeSB0aGUgZnVsbCB3b3JrZmxvdyBJRFxuICAvLyAoZS5nLiBcIndvcmtmbG93Ly8uL3dvcmtmbG93cy8xX3NpbXBsZS8vc2ltcGxlXCIpXG4gIGNvbnN0IHdvcmtmbG93SWQgPSB3b3JrZmxvd05hbWU7XG5cbiAgLy8gUmVzb2x2ZSB0aGUgZW5jcnlwdGlvbiBrZXkgdXAgZnJvbnQ6IG5lZWRlZCB0byBkZWNyeXB0IGV2ZW50XG4gIC8vIHBheWxvYWRzIGluc2lkZSB0aGUgVk0gYW5kIHRvIGVuY3J5cHQgZXZlbnQgcGF5bG9hZHMgd3JpdHRlbiBiZWxvdy5cbiAgLy8gUmVzb2x2ZSB0aGUgRlVMTCBjYXBhYmlsaXR5IChzeW1tZXRyaWMgQUVTIGtleSArIFgyNTUxOSBrZXlwYWlyKSwgbm90XG4gIC8vIGp1c3QgYGltcG9ydEtleShyYXdLZXkpYDogYSBydW4gcmVhZGluZyBpdHMgb3duIGV2ZW50IGxvZyBjYW4gZW5jb3VudGVyXG4gIC8vIHNlYWxlZCAoYGVuY3BgKSBob29rIHBheWxvYWRzIHRoYXQgYSBjcm9zcy1kZXBsb3ltZW50IGByZXN1bWVIb29rKClgXG4gIC8vIHdyb3RlIHRvIGl0IChzZWFsaW5nIGlzIHByZXNlbmNlLWdhdGVkIG9uIHRoZSBydW4ncyBwdWJsaXNoZWRcbiAgLy8gZW5jcnlwdGlvblB1YmxpY0tleSwgd2hpY2ggdGhlIHNoYXJlZCBzdGFydCgpIHBhdGggc3RhbXBzIHJlZ2FyZGxlc3Mgb2ZcbiAgLy8gZW5naW5lKS4gQSBiYXJlIHN5bW1ldHJpYyBrZXkgY2Fubm90IG9wZW4gdGhvc2UgYW5kIHdvdWxkIHdlZGdlIHRoZSBydW5cbiAgLy8gcmlnaHQgYWZ0ZXIgaG9va19yZWNlaXZlZC4gVGhlIG5vZGU6dm0gZW5naW5lIHJlc29sdmVzIHRoZSBzYW1lIGZ1bGxcbiAgLy8gY2FwYWJpbGl0eSB2aWEgbWVtb2l6ZUVuY3J5cHRpb25LZXkuXG4gIGNvbnN0IHJhd0tleSA9IGF3YWl0IHdvcmxkLmdldEVuY3J5cHRpb25LZXlGb3JSdW4/Lih3b3JrZmxvd1J1bik7XG4gIGNvbnN0IGVuY3J5cHRpb25LZXkgPSByYXdLZXkgPyBhd2FpdCBkZXJpdmVSdW5QYXlsb2FkS2V5cyhyYXdLZXkpIDogdW5kZWZpbmVkO1xuXG4gIC8vIExvYWQgdGhlIEZVTEwgZXZlbnQgbG9nIGZvciB0aGUgcnVuLiBPbiBmaXJzdCBpbnZvY2F0aW9uIHRoZVxuICAvLyBwcmVsb2FkZWQgZXZlbnRzIGZyb20gdGhlIHJ1bl9zdGFydGVkIHJlc3BvbnNlIGFyZSB0aGUgY29tcGxldGUgbG9nXG4gIC8vIGFuZCBzYXZlIHRoZSBldmVudHMubGlzdCByb3VuZC10cmlwczsgYSBjYWxsZXItYXR0ZXN0ZWQgY29tcGxldGVcbiAgLy8gcHJlbG9hZCAobGF6eSBob29rIGZhc3QgcGF0aCkgaXMgdHJ1c3RlZCB0aGUgc2FtZSB3YXkuXG4gIGxldCBldmVudHM6IEV2ZW50W107XG4gIGxldCBldmVudHNGZXRjaGVkUGFnZXMgPSAwO1xuICBjb25zdCB1c2VQcmVsb2FkZWQgPVxuICAgIChwcmVsb2FkZWRFdmVudHNDb21wbGV0ZSA9PT0gdHJ1ZSAmJlxuICAgICAgQXJyYXkuaXNBcnJheShwcmVsb2FkZWRFdmVudHMpICYmXG4gICAgICBwcmVsb2FkZWRFdmVudHMubGVuZ3RoID4gMCkgfHxcbiAgICBpc0ZpcnN0SW52b2NhdGlvbihwcmVsb2FkZWRFdmVudHMpO1xuICBpZiAodXNlUHJlbG9hZGVkICYmIHByZWxvYWRlZEV2ZW50cykge1xuICAgIGV2ZW50cyA9IHByZWxvYWRlZEV2ZW50cztcbiAgfSBlbHNlIHtcbiAgICBjb25zdCBhbGxFdmVudHM6IEV2ZW50W10gPSBbXTtcbiAgICBsZXQgY3Vyc29yOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgICBsZXQgaGFzTW9yZSA9IHRydWU7XG5cbiAgICB3aGlsZSAoaGFzTW9yZSkge1xuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCB3b3JsZC5ldmVudHMubGlzdCh7XG4gICAgICAgIHJ1bklkLFxuICAgICAgICBwYWdpbmF0aW9uOiB7XG4gICAgICAgICAgc29ydE9yZGVyOiAnYXNjJyxcbiAgICAgICAgICBjdXJzb3I6IGN1cnNvciA/PyB1bmRlZmluZWQsXG4gICAgICAgICAgbGltaXQ6IDEwMDAsXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICAgIGV2ZW50c0ZldGNoZWRQYWdlcysrO1xuICAgICAgYWxsRXZlbnRzLnB1c2goLi4ucmVzcG9uc2UuZGF0YSk7XG4gICAgICAvLyBVcGRhdGUgdGhlIGN1cnNvciB0byB0aGUgbGFzdCBzdWNjZXNzZnVsbHkgZmV0Y2hlZCBwYWdlJ3MgY3Vyc29yLlxuICAgICAgLy8gT25seSB1cGRhdGUgd2hlbiB3ZSBnb3QgcmVzdWx0czogdGhlIGZpbmFsIGVtcHR5LXBhZ2UgcmVzcG9uc2VcbiAgICAgIC8vIHJldHVybnMgY3Vyc29yPW51bGwgd2hpY2ggd2UgbXVzdCBOT1QgdXNlIChpdCB3b3VsZCByZXNldCB0aGUgY3Vyc29yKS5cbiAgICAgIGlmIChyZXNwb25zZS5jdXJzb3IpIHtcbiAgICAgICAgY3Vyc29yID0gcmVzcG9uc2UuY3Vyc29yO1xuICAgICAgfVxuICAgICAgaGFzTW9yZSA9IHJlc3BvbnNlLmRhdGEubGVuZ3RoID4gMCAmJiByZXNwb25zZS5jdXJzb3IgIT0gbnVsbDtcbiAgICB9XG5cbiAgICBldmVudHMgPSBhbGxFdmVudHM7XG4gIH1cblxuICAvLyBFdmVudC1saW1pdCBndWFyZDogZmFpbCBhIHJ1bmF3YXkgcnVuIG9uY2UgaXRzIGxvZyByZWFjaGVzIHRoZVxuICAvLyBzZXJ2ZXItc3VwcGxpZWQgY2VpbGluZywgdGhlIHNhbWUgZW5mb3JjZW1lbnQgcG9pbnQgYXMgdGhlIG5vZGU6dm1cbiAgLy8gZW5naW5lJ3MgcmVwbGF5IGxvb3AuXG4gIGlmIChtYXhFdmVudHNMaW1pdCAhPT0gdW5kZWZpbmVkICYmIGV2ZW50cy5sZW5ndGggPj0gbWF4RXZlbnRzTGltaXQpIHtcbiAgICB0aHJvdyBuZXcgTWF4RXZlbnRzRXhjZWVkZWRFcnJvcihldmVudHMubGVuZ3RoLCBtYXhFdmVudHNMaW1pdCk7XG4gIH1cblxuICBwYXJlbnRTcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAuLi5BdHRyaWJ1dGUuUXVpY2tKU0V2ZW50c1ByZWxvYWRlZCh1c2VQcmVsb2FkZWQpLFxuICAgIC4uLkF0dHJpYnV0ZS5RdWlja0pTRXZlbnRzRmV0Y2hlZENvdW50KGV2ZW50cy5sZW5ndGgpLFxuICAgIC4uLkF0dHJpYnV0ZS5RdWlja0pTRXZlbnRzRmV0Y2hlZFBhZ2VzKGV2ZW50c0ZldGNoZWRQYWdlcyksXG4gIH0pO1xuXG4gIHdmZGlhZygnZXZlbnRzX2ZldGNoZWQnLCB7XG4gICAgZXZlbnRDb3VudDogZXZlbnRzLmxlbmd0aCxcbiAgICBldmVudHNGZXRjaGVkUGFnZXMsXG4gICAgdXNlUHJlbG9hZGVkLFxuICAgIGV2ZW50VHlwZXM6IGV2ZW50cy5yZWR1Y2U8UmVjb3JkPHN0cmluZywgbnVtYmVyPj4oKGFjYywgZSkgPT4ge1xuICAgICAgYWNjW2UuZXZlbnRUeXBlXSA9IChhY2NbZS5ldmVudFR5cGVdID8/IDApICsgMTtcbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwge30pLFxuICB9KTtcblxuICAvLyBDaGVjayBmb3IgZWxhcHNlZCB3YWl0c1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBjb25zdCBjb21wbGV0ZWRXYWl0SWRzID0gbmV3IFNldChcbiAgICBldmVudHNcbiAgICAgIC5maWx0ZXIoKGUpID0+IGUuZXZlbnRUeXBlID09PSAnd2FpdF9jb21wbGV0ZWQnKVxuICAgICAgLm1hcCgoZSkgPT4gZS5jb3JyZWxhdGlvbklkKVxuICApO1xuICBmb3IgKGNvbnN0IGV2ZW50IG9mIGV2ZW50cykge1xuICAgIGlmIChcbiAgICAgIGV2ZW50LmV2ZW50VHlwZSA9PT0gJ3dhaXRfY3JlYXRlZCcgJiZcbiAgICAgIGV2ZW50LmNvcnJlbGF0aW9uSWQgJiZcbiAgICAgICFjb21wbGV0ZWRXYWl0SWRzLmhhcyhldmVudC5jb3JyZWxhdGlvbklkKVxuICAgICkge1xuICAgICAgY29uc3QgZXZlbnREYXRhID1cbiAgICAgICAgJ2V2ZW50RGF0YScgaW4gZXZlbnRcbiAgICAgICAgICA/IChldmVudC5ldmVudERhdGEgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pXG4gICAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgICBjb25zdCByZXN1bWVBdCA9IGV2ZW50RGF0YT8ucmVzdW1lQXQ7XG4gICAgICBpZiAocmVzdW1lQXQgJiYgbm93ID49IG5ldyBEYXRlKHJlc3VtZUF0IGFzIHN0cmluZykuZ2V0VGltZSgpKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgd29ybGQuZXZlbnRzLmNyZWF0ZShydW5JZCwge1xuICAgICAgICAgICAgZXZlbnRUeXBlOiAnd2FpdF9jb21wbGV0ZWQnLFxuICAgICAgICAgICAgc3BlY1ZlcnNpb246IFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICAgICAgICAgICAgY29ycmVsYXRpb25JZDogZXZlbnQuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAocmVzdWx0LmV2ZW50KSBldmVudHMucHVzaChyZXN1bHQuZXZlbnQpO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBpZiAoRW50aXR5Q29uZmxpY3RFcnJvci5pcyhlcnIpKSBjb250aW51ZTtcbiAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBSZXNvbHZlIHRoZSB3b3JrZmxvdyBzZXJ2ZXIgcG9ydCBzbyBgZ2V0V29ya2Zsb3dNZXRhZGF0YSgpLnVybGAgaW5zaWRlXG4gIC8vIHRoZSBWTSBtYXRjaGVzIHdoYXQgdGhlIHN0ZXAtc2lkZSBoYW5kbGVyIHJlcG9ydHMuIFNraXBwZWQgb24gVmVyY2VsOlxuICAvLyB0aGUgVk0gcmVhZHMgVkVSQ0VMX1VSTCBkaXJlY3RseSBpbiB0aGF0IGVudmlyb25tZW50LlxuICBjb25zdCBpc1ZlcmNlbCA9IHByb2Nlc3MuZW52LlZFUkNFTF9VUkwgIT09IHVuZGVmaW5lZDtcbiAgY29uc3QgcG9ydCA9IGlzVmVyY2VsID8gdW5kZWZpbmVkIDogYXdhaXQgZ2V0UG9ydExhenkoKTtcblxuICAvLyBSdW4gdGhlIHdvcmtmbG93IGluIHRoZSBRdWlja0pTIFZNXG4gIHJ1bnRpbWVMb2dnZXIuZGVidWcoJ1F1aWNrSlMgcnVudGltZTogaW52b2tpbmcgVk0nLCB7XG4gICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgd29ya2Zsb3dJZCxcbiAgICBldmVudENvdW50OiBldmVudHMubGVuZ3RoLFxuICB9KTtcblxuICBjb25zdCBzZXNzaW9uID0gYXdhaXQgc3RhcnRRdWlja0pTV29ya2Zsb3coe1xuICAgIC8vIFBhc3MgdGhlIFNUUklQUEVEIGJ1bmRsZSB0byB0aGUgVk0gc28gdGhlIGlubGluZSBzb3VyY2UgbWFwXG4gICAgLy8gZG9lc24ndCBlbmQgdXAgaW4gdGhlIFF1aWNrSlMgaGVhcC4gVGhlIG9yaWdpbmFsICh1bnN0cmlwcGVkKVxuICAgIC8vIGB3b3JrZmxvd0NvZGVgIGlzIHN0aWxsIGtlcHQgaW4gdGhpcyBob3N0LXNpZGUgc2NvcGUgYW5kIGlzIHVzZWRcbiAgICAvLyBieSBgcmVtYXBFcnJvclN0YWNrYCBvbiB3b3JrZmxvdyBmYWlsdXJlcyBiZWxvdy5cbiAgICB3b3JrZmxvd0NvZGU6IHdvcmtmbG93Q29kZUZvclZNLFxuICAgIHdvcmtmbG93SWQsXG4gICAgd29ya2Zsb3dSdW4sXG4gICAgZXZlbnRzLFxuICAgIHdvcmxkQ2FwYWJpbGl0aWVzOiB3b3JsZC5jYXBhYmlsaXRpZXMsXG4gICAgZW5jcnlwdGlvbktleSxcbiAgICBwb3J0LFxuICAgIHJ1bklucHV0LFxuICB9KTtcbiAgbGV0IHJlc3VsdCA9IHNlc3Npb24ucmVzdWx0O1xuXG4gIHJ1bnRpbWVMb2dnZXIuZGVidWcoJ1F1aWNrSlMgcnVudGltZTogVk0gcmV0dXJuZWQnLCB7XG4gICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgY29tcGxldGVkOiAhIXJlc3VsdC5jb21wbGV0ZWQsXG4gICAgc3VzcGVuZGVkOiAhIXJlc3VsdC5zdXNwZW5kZWQsXG4gICAgZmFpbGVkOiAhIXJlc3VsdC5mYWlsZWQsXG4gICAgcGVuZGluZ09wc0NvdW50OiByZXN1bHQuc3VzcGVuZGVkPy5wZW5kaW5nT3BlcmF0aW9ucz8ubGVuZ3RoLFxuICB9KTtcblxuICB3ZmRpYWcoJ3ZtX3JldHVybmVkJywge1xuICAgIG91dGNvbWU6IHJlc3VsdC5jb21wbGV0ZWRcbiAgICAgID8gJ2NvbXBsZXRlZCdcbiAgICAgIDogcmVzdWx0LnN1c3BlbmRlZFxuICAgICAgICA/ICdzdXNwZW5kZWQnXG4gICAgICAgIDogcmVzdWx0LmZhaWxlZFxuICAgICAgICAgID8gJ2ZhaWxlZCdcbiAgICAgICAgICA6ICd1bmtub3duJyxcbiAgICBwZW5kaW5nT3BzQ291bnQ6IHJlc3VsdC5zdXNwZW5kZWQ/LnBlbmRpbmdPcGVyYXRpb25zPy5sZW5ndGggPz8gMCxcbiAgICBwZW5kaW5nT3BTdW1tYXJ5OiByZXN1bHQuc3VzcGVuZGVkPy5wZW5kaW5nT3BlcmF0aW9ucz8ubWFwKChwKSA9PiAoe1xuICAgICAgdHlwZTogcC50eXBlLFxuICAgICAgY29ycmVsYXRpb25JZDogcC5jb3JyZWxhdGlvbklkLFxuICAgICAgaGFzQ3JlYXRlZEV2ZW50OiBwLmhhc0NyZWF0ZWRFdmVudCxcbiAgICAgIC4uLihwLnR5cGUgPT09ICdzdGVwJyA/IHsgc3RlcElkOiAocCBhcyBQZW5kaW5nU3RlcCkuc3RlcElkIH0gOiB7fSksXG4gICAgfSkpLFxuICAgIGZhaWx1cmVNZXNzYWdlOiByZXN1bHQuZmFpbGVkPy5tZXNzYWdlLFxuICAgIGZhaWx1cmVOYW1lOiByZXN1bHQuZmFpbGVkPy5uYW1lLFxuICB9KTtcblxuICAvLyAtLS0tIElubGluZSBjb250aW51YXRpb24gbG9vcCAtLS0tXG4gIC8vXG4gIC8vIFdoaWxlIHRoZSB3b3JrZmxvdyBpcyBzdXNwZW5kZWQsIHRoaXMgbG9vcCBrZWVwcyB0aGUgVk0gYWxpdmUgYW5kXG4gIC8vIG1ha2VzIGFzIG11Y2ggZm9yd2FyZCBwcm9ncmVzcyBhcyBwb3NzaWJsZSB3aXRoaW4gb25lIGludm9jYXRpb246XG4gIC8vXG4gIC8vICAgMS4gRGlzcGF0Y2ggZHVyYWJsZSBzaWRlIGVmZmVjdHMgZm9yIHRoZSBzdXNwZW5zaW9uJ3MgcGVuZGluZyBvcHNcbiAgLy8gICAgICAoc3RlcF9jcmVhdGVkIC8gaG9va19jcmVhdGVkIC8gYXR0cl9zZXQgLyB3YWl0X2NyZWF0ZWQgL1xuICAvLyAgICAgIGhvb2tfcmVjZWl2ZWQgZm9yIGFib3J0cykgYW5kIGNvbXBsZXRlIGVsYXBzZWQgd2FpdHMuXG4gIC8vICAgMi4gRmVlZCBhbGwgbmV3bHkgcmVjb3JkZWQgZXZlbnRzIChhdHRyX3NldCwgaG9va19jcmVhdGVkLCBlbGFwc2VkXG4gIC8vICAgICAgd2FpdF9jb21wbGV0ZWQsIHRlcm1pbmFscyB3cml0dGVuIGJ5IGNvbmN1cnJlbnQgaW52b2NhdGlvbnMsIC4uLilcbiAgLy8gICAgICBpbnRvIHRoZSBMSVZFIFZNIHZpYSBzZXNzaW9uLmNvbnRpbnVlV2l0aEV2ZW50cywgcmVzdW1pbmdcbiAgLy8gICAgICBleGVjdXRpb24gZXhhY3RseSB3aGVyZSBpdCBsZWZ0IG9mZiwgbm8gZnJlc2gtVk0gcmUtcmVwbGF5LlxuICAvLyAgICAgIENoZWFwIHByb2dyZXNzIGlzIGZlZCBCRUZPUkUgcnVubmluZyBzdGVwIGJvZGllcyBzbyBwcm9taXNlXG4gIC8vICAgICAgY2hhaW5zIHRoYXQgYXJlIG5vdCBnYXRlZCBvbiBzdGVwcyAoaG9vay5nZXRDb25mbGljdCgpLFxuICAvLyAgICAgIHNldEF0dHJpYnV0ZXMoKSwgcmFjaW5nIHNsZWVwcykgYWR2YW5jZSBmaXJzdCBhbmQgY2FuIHN1cmZhY2VcbiAgLy8gICAgICBhZGRpdGlvbmFsIHBlbmRpbmcgc3RlcHMgZm9yIHRoZSBzYW1lIGlubGluZSBiYXRjaC5cbiAgLy8gICAzLiBPbmNlIG5vIGNoZWFwIHByb2dyZXNzIHJlbWFpbnMsIGV4ZWN1dGUgdXAgdG9cbiAgLy8gICAgICBnZXRNYXhJbmxpbmVTdGVwcygpIHN0ZXBzIGNyZWF0ZWQgYnkgVEhJUyBpbnZvY2F0aW9uIGlubGluZSAobm9cbiAgLy8gICAgICBxdWV1ZSByb3VuZC10cmlwKSwgaW4gcGFyYWxsZWwsIHdpdGggdGhlIHJlcGxheSBidWRnZXQgcGF1c2VkXG4gIC8vICAgICAgZHVyaW5nIHN0ZXAgYm9kaWVzLCBtaXJyb3JpbmcgdGhlIG5vZGU6dm0gZW5naW5lJ3MgaW5saW5lXG4gIC8vICAgICAgcmVwbGF5IGxvb3AuIE92ZXJmbG93IGFuZCByZXRyeS90aHJvdHRsZWQgc3RlcHMgYXJlIHF1ZXVlZCBmb3JcbiAgLy8gICAgICBiYWNrZ3JvdW5kIGV4ZWN1dGlvbi4gQSBkZWxheWVkIHdhaXQtY29udGludWF0aW9uIG1lc3NhZ2UgaXNcbiAgLy8gICAgICBlbnF1ZXVlZCBmb3IgdGhlIHNvb25lc3QgcGVuZGluZyB3YWl0IGZpcnN0LCBzbyByYWNpbmcgdGltZXJzXG4gIC8vICAgICAgZmlyZSBvbiB0aW1lIChpbiBhIHNlcGFyYXRlIGludm9jYXRpb24pIHdoaWxlIHN0ZXAgYm9kaWVzIGJsb2NrXG4gIC8vICAgICAgdGhpcyBvbmUuXG4gIC8vXG4gIC8vIFRoZSBsb29wIGV4aXRzIHdoZW4gdGhlIHdvcmtmbG93IHNldHRsZXMsIG5vIGZvcndhcmQgcHJvZ3Jlc3MgaXNcbiAgLy8gcG9zc2libGUgaW4tcHJvY2VzcywgdGhlIHJlcGxheSBidWRnZXQgaXMgZXhoYXVzdGVkLCBvciB0aGUgcnVuIGlzXG4gIC8vIGdvbmUuXG4gIGNvbnN0IHNlZW5FdmVudElkcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBmb3IgKGNvbnN0IGUgb2YgZXZlbnRzKSB7XG4gICAgaWYgKGUuZXZlbnRJZCkgc2VlbkV2ZW50SWRzLmFkZChlLmV2ZW50SWQpO1xuICB9XG4gIC8vIFN0ZXAgY2lkcyBhbHJlYWR5IGV4ZWN1dGVkIGlubGluZSBieSB0aGlzIGludm9jYXRpb24uXG4gIGNvbnN0IGV4ZWN1dGVkU3RlcElkcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAvLyBTdGVwcyBmb3Igd2hpY2ggVEhJUyBpbnZvY2F0aW9uIGFscmVhZHkgc2VudCBhIHF1ZXVlIG1lc3NhZ2UuXG4gIGNvbnN0IHF1ZXVlZFN0ZXBJZHMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgLy8gQWJvcnRzIFRISVMgaW52b2NhdGlvbiBhbHJlYWR5IHJlY29yZGVkIChob29rX3JlY2VpdmVkIHdyaXR0ZW4pLlxuICAvLyBHdWFyZHMgYWdhaW5zdCByZS1yZWNvcmRpbmcgd2hlbiB0aGUgVk0tc2lkZSBmbGFnIGhhcyBub3QgYmVlblxuICAvLyBjbGVhcmVkIHlldCB3aXRoaW4gdGhlIHNhbWUgaXRlcmF0aW9uLlxuICBjb25zdCByZWNvcmRlZEFib3J0SWRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIC8vIFdhaXRzIGZvciB3aGljaCBUSElTIGludm9jYXRpb24gYWxyZWFkeSBjb21wbGV0ZWQvc2NoZWR1bGVkIHdvcmsuXG4gIGNvbnN0IGNvbXBsZXRlZFdhaXRJZHMyID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIC8vIElubGluZS1vd25lcnNoaXAgc3RhdGUgcGVyIHN0ZXAgY29ycmVsYXRpb25JZCwgZGVyaXZlZCBmcm9tIGV2ZXJ5XG4gIC8vIGV2ZW50IHRoaXMgaW52b2NhdGlvbiBvYnNlcnZlcyAoaW5pdGlhbCBsb2cgKyBldmVyeSBmZWVkKTogdGhlXG4gIC8vIHF1aWNranMgYW5hbG9nIG9mIHRoZSByZXBsYXktZGVyaXZlZCBvd25lcnNoaXAgb24gdGhlIG5vZGUgZW5naW5lJ3NcbiAgLy8gU3RlcEludm9jYXRpb25RdWV1ZUl0ZW0gKHNlZSBzdGVwLW93bmVyc2hpcC50cykuIExhdGVzdC13aW5zOlxuICAvLyBldmVudHMgYXJyaXZlIGluIGxvZyBvcmRlciwgc28gYSBsYXRlciBzdGVwX3N0YXJ0ZWQgb3ZlcndyaXRlcyB0aGVcbiAgLy8gc3RhbXA7IGEgc3RlcF9yZXRyeWluZyBsYXBzZXMgb3duZXJzaGlwIHBlcm1hbmVudGx5IGZvciB0aGUgaWQuXG4gIGNvbnN0IHN0ZXBPd25lcnNoaXAgPSBuZXcgTWFwPFxuICAgIHN0cmluZyxcbiAgICB7IG93bmVyPzogc3RyaW5nOyBzdGFydGVkQXRNcz86IG51bWJlcjsgc2F3UmV0cnlpbmc6IGJvb2xlYW4gfVxuICA+KCk7XG4gIGNvbnN0IG9ic2VydmVFdmVudHNGb3JPd25lcnNoaXAgPSAob2JzZXJ2ZWQ6IEV2ZW50W10pOiB2b2lkID0+IHtcbiAgICBmb3IgKGNvbnN0IGUgb2Ygb2JzZXJ2ZWQpIHtcbiAgICAgIGlmIChlLmNvcnJlbGF0aW9uSWQgPT09IHVuZGVmaW5lZCkgY29udGludWU7XG4gICAgICBpZiAoZS5ldmVudFR5cGUgPT09ICdzdGVwX3N0YXJ0ZWQnKSB7XG4gICAgICAgIGNvbnN0IG93bmVyID1cbiAgICAgICAgICAnZXZlbnREYXRhJyBpbiBlICYmXG4gICAgICAgICAgZS5ldmVudERhdGEgJiZcbiAgICAgICAgICAnb3duZXJNZXNzYWdlSWQnIGluIGUuZXZlbnREYXRhICYmXG4gICAgICAgICAgdHlwZW9mIGUuZXZlbnREYXRhLm93bmVyTWVzc2FnZUlkID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgPyBlLmV2ZW50RGF0YS5vd25lck1lc3NhZ2VJZFxuICAgICAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgICAgIGNvbnN0IHByaW9yID0gc3RlcE93bmVyc2hpcC5nZXQoZS5jb3JyZWxhdGlvbklkKTtcbiAgICAgICAgc3RlcE93bmVyc2hpcC5zZXQoZS5jb3JyZWxhdGlvbklkLCB7XG4gICAgICAgICAgb3duZXIsXG4gICAgICAgICAgc3RhcnRlZEF0TXM6IGUuY3JlYXRlZEF0ID8gK25ldyBEYXRlKGUuY3JlYXRlZEF0KSA6IHVuZGVmaW5lZCxcbiAgICAgICAgICBzYXdSZXRyeWluZzogcHJpb3I/LnNhd1JldHJ5aW5nID8/IGZhbHNlLFxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAoZS5ldmVudFR5cGUgPT09ICdzdGVwX3JldHJ5aW5nJykge1xuICAgICAgICBjb25zdCBwcmlvciA9IHN0ZXBPd25lcnNoaXAuZ2V0KGUuY29ycmVsYXRpb25JZCk7XG4gICAgICAgIHN0ZXBPd25lcnNoaXAuc2V0KGUuY29ycmVsYXRpb25JZCwge1xuICAgICAgICAgIC4uLihwcmlvciA/PyB7fSksXG4gICAgICAgICAgc2F3UmV0cnlpbmc6IHRydWUsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbiAgb2JzZXJ2ZUV2ZW50c0Zvck93bmVyc2hpcChldmVudHMpO1xuICBjb25zdCBzY2hlZHVsZWRXYWl0Q29udGludWF0aW9ucyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBjb25zdCBtYXhJbmxpbmVTdGVwcyA9IGdldE1heElubGluZVN0ZXBzKCk7XG4gIGNvbnN0IGJ1ZGdldCA9IG5ldyBSZXBsYXlCdWRnZXQoKTtcbiAgY29uc3Qgd29ya2Zsb3dTdGFydGVkQXQgPSB3b3JrZmxvd1J1bi5zdGFydGVkQXRcbiAgICA/ICt3b3JrZmxvd1J1bi5zdGFydGVkQXRcbiAgICA6IERhdGUubm93KCk7XG4gIGNvbnN0IHJvb3RSdW5JZCA9XG4gICAgKHdvcmtmbG93UnVuLmF0dHJpYnV0ZXMgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPiB8IHVuZGVmaW5lZCk/LltcbiAgICAgIFJPT1RfUlVOX0lEX0FUVFJJQlVURVxuICAgIF0gPz8gcnVuSWQ7XG4gIGxldCBpbmxpbmVTdGVwc0V4ZWN1dGVkID0gMDtcbiAgbGV0IHJ1bkdvbmUgPSBmYWxzZTtcbiAgLy8gU2V0IHdoZW4gdGhpcyBpbnZvY2F0aW9uIHdyb3RlIGFuIGV2ZW50IHRoZSB3b3JrZmxvdyBtdXN0IGNvbnN1bWUgdG9cbiAgLy8gbWFrZSBwcm9ncmVzcyAoYXR0cl9zZXQsIGdldENvbmZsaWN0LWF3YWl0ZWQgaG9va19jcmVhdGVkKSBhbmQgdGhlXG4gIC8vIGxvb3AgaGFzIG5vdCB5ZXQgcmVhZCBpdCBiYWNrLCBzaW5jZSBldmVudHVhbGx5LWNvbnNpc3RlbnQgbGlzdGluZ3MgY2FuXG4gIC8vIHJldHVybiAwIG5ldyBldmVudHMgcmlnaHQgYWZ0ZXIgYSB3cml0ZS4gSWYgaXQgaXMgc3RpbGwgc2V0IHdoZW4gdGhlXG4gIC8vIGxvb3AgZXhpdHMgc3VzcGVuZGVkLCB0aGUgZW50cnlwb2ludCByZXF1ZXVlcyBpbW1lZGlhdGVseSBpbnN0ZWFkIG9mXG4gIC8vIGV4aXRpbmcgYXdhaXRpbmdfZXh0ZXJuYWwgd2l0aCB0aGUgdW5ibG9ja2luZyBldmVudCBhbHJlYWR5IHdyaXR0ZW5cbiAgLy8gYW5kIG5vdGhpbmcgc2NoZWR1bGVkIHRvIHJlYWQgaXQuXG4gIGxldCBwZW5kaW5nUmVxdWV1ZVNpZ25hbCA9IGZhbHNlO1xuXG4gIC8qKiBGZXRjaCBhbGwgZXZlbnRzIG5vdCB5ZXQgcHJvY2Vzc2VkIGJ5IHRoZSBsaXZlIFZNIChsb2cgb3JkZXIpLiAqL1xuICBjb25zdCBmZXRjaFVuc2VlbkV2ZW50cyA9IGFzeW5jICgpOiBQcm9taXNlPEV2ZW50W10+ID0+IHtcbiAgICBjb25zdCB1bnNlZW46IEV2ZW50W10gPSBbXTtcbiAgICBsZXQgY3Vyc29yOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgICBsZXQgaGFzTW9yZSA9IHRydWU7XG4gICAgd2hpbGUgKGhhc01vcmUpIHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgd29ybGQuZXZlbnRzLmxpc3Qoe1xuICAgICAgICBydW5JZCxcbiAgICAgICAgcGFnaW5hdGlvbjoge1xuICAgICAgICAgIHNvcnRPcmRlcjogJ2FzYycsXG4gICAgICAgICAgY3Vyc29yOiBjdXJzb3IgPz8gdW5kZWZpbmVkLFxuICAgICAgICAgIGxpbWl0OiAxMDAwLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICBmb3IgKGNvbnN0IGUgb2YgcmVzcG9uc2UuZGF0YSkge1xuICAgICAgICBpZiAoZS5ldmVudElkICYmIHNlZW5FdmVudElkcy5oYXMoZS5ldmVudElkKSkgY29udGludWU7XG4gICAgICAgIGlmIChlLmV2ZW50SWQpIHNlZW5FdmVudElkcy5hZGQoZS5ldmVudElkKTtcbiAgICAgICAgdW5zZWVuLnB1c2goZSk7XG4gICAgICB9XG4gICAgICBpZiAocmVzcG9uc2UuY3Vyc29yKSBjdXJzb3IgPSByZXNwb25zZS5jdXJzb3I7XG4gICAgICBoYXNNb3JlID0gcmVzcG9uc2UuZGF0YS5sZW5ndGggPiAwICYmIHJlc3BvbnNlLmN1cnNvciAhPSBudWxsO1xuICAgIH1cbiAgICBvYnNlcnZlRXZlbnRzRm9yT3duZXJzaGlwKHVuc2Vlbik7XG4gICAgcmV0dXJuIHVuc2VlbjtcbiAgfTtcblxuICB0cnkge1xuICAgIGxldCBpdGVyYXRpb24gPSAwO1xuICAgIHdoaWxlIChyZXN1bHQuc3VzcGVuZGVkICYmICFydW5Hb25lICYmICFidWRnZXQuaXNFeGhhdXN0ZWQoKSkge1xuICAgICAgaXRlcmF0aW9uKys7XG4gICAgICAvLyBSZS1jaGVjayB0aGUgZXZlbnQgY2VpbGluZyBldmVyeSB0dXJuOiB0aGUgbG9vcCBhcHBlbmRzIGV2ZW50cyBvblxuICAgICAgLy8gZWFjaCBjb250aW51ZVdpdGhFdmVudHMsIHNvIGEgc2luZ2xlIGludm9jYXRpb24gY2FuIG90aGVyd2lzZSBncm93XG4gICAgICAvLyB0aGUgbG9nIGFyYml0cmFyaWx5IGZhciBwYXN0IHRoZSBvcGVyYXRvcidzIGxpbWl0ICh0aGUgbm9kZSBlbmdpbmVcbiAgICAgIC8vIHJlLWNoZWNrcyBwZXIgcmVwbGF5IGZvciB0aGUgc2FtZSByZWFzb24pLiBgc2VlbkV2ZW50SWRzYCBjb3VudHNcbiAgICAgIC8vIGV2ZXJ5IGV2ZW50IHRoaXMgaW52b2NhdGlvbiBoYXMgb2JzZXJ2ZWQ6IGluaXRpYWwgbG9nICsgYWxsXG4gICAgICAvLyBmZWVkcy5cbiAgICAgIGlmIChtYXhFdmVudHNMaW1pdCAhPT0gdW5kZWZpbmVkICYmIHNlZW5FdmVudElkcy5zaXplID49IG1heEV2ZW50c0xpbWl0KSB7XG4gICAgICAgIHRocm93IG5ldyBNYXhFdmVudHNFeGNlZWRlZEVycm9yKHNlZW5FdmVudElkcy5zaXplLCBtYXhFdmVudHNMaW1pdCk7XG4gICAgICB9XG4gICAgICBjb25zdCBwZW5kaW5nT3BlcmF0aW9ucyA9IHJlc3VsdC5zdXNwZW5kZWQucGVuZGluZ09wZXJhdGlvbnM7XG5cbiAgICAgIC8vIFNlbGVjdCB0aGlzIHR1cm4ncyBpbmxpbmUgY2FuZGlkYXRlcyBCRUZPUkUgZGlzcGF0Y2g6IGZyZXNoIHN0ZXBzXG4gICAgICAvLyAobm8gc3RlcF9jcmVhdGVkIHlldCkgdGhhdCB0aGlzIGludm9jYXRpb24gaGFzbid0IGFscmVhZHkgaGFuZGxlZC5cbiAgICAgIC8vIFRoZWlyIHN0ZXBfY3JlYXRlZCBpcyBkZWxpYmVyYXRlbHkgTk9UIHdyaXR0ZW4gYnkgZGlzcGF0Y2g6IHRoZVxuICAgICAgLy8gaW5saW5lIGNsYWltIGJlbG93IGlzIGEgbGF6eSBzdGVwX3N0YXJ0ZWQgY2FycnlpbmcgdGhlIGlucHV0LFxuICAgICAgLy8gd2hpY2ggdGhlIHdvcmxkIGFwcGxpZXMgYXMgYW4gYXRvbWljIGNyZWF0ZS1jbGFpbS4gQSBjb25jdXJyZW50XG4gICAgICAvLyBpbnZvY2F0aW9uIHJhY2luZyBvbiB0aGUgc2FtZSBmcmVzaCBzdGVwIGxvc2VzIHRoYXQgY2xhaW0gd2l0aFxuICAgICAgLy8gRW50aXR5Q29uZmxpY3RFcnJvciBhbmQgc2tpcHMsIHNvIHN0ZXAgYm9kaWVzIGNhbm5vdCBkb3VibGUtcnVuXG4gICAgICAvLyAocHJldmlvdXNseSBib3RoIGludm9jYXRpb25zIGJhcmUtc3RhcnRlZCB0aGUgc3RlcCBhZnRlciBvbmUgbG9zdFxuICAgICAgLy8gdGhlIHN3YWxsb3dlZCBzdGVwX2NyZWF0ZWQgcmFjZSkuXG4gICAgICBjb25zdCBmcmVzaFN0ZXBzID0gcGVuZGluZ09wZXJhdGlvbnMuZmlsdGVyKFxuICAgICAgICAob3ApOiBvcCBpcyBQZW5kaW5nU3RlcCA9PlxuICAgICAgICAgIG9wLnR5cGUgPT09ICdzdGVwJyAmJlxuICAgICAgICAgICFvcC5oYXNDcmVhdGVkRXZlbnQgJiZcbiAgICAgICAgICAhZXhlY3V0ZWRTdGVwSWRzLmhhcyhvcC5jb3JyZWxhdGlvbklkKSAmJlxuICAgICAgICAgICFxdWV1ZWRTdGVwSWRzLmhhcyhvcC5jb3JyZWxhdGlvbklkKVxuICAgICAgKTtcbiAgICAgIC8vIFN0ZXBzIHdob3NlIGlucHV0IHJlZnVzZWQgdG8gc2VyaWFsaXplIChzZWVcbiAgICAgIC8vIFBlbmRpbmdTdGVwLnNlcmlhbGl6YXRpb25FcnJvcikgbmV2ZXIgZXhlY3V0ZTogdGhleSBtdXN0IG5vdCBiZVxuICAgICAgLy8gaW5saW5lLWNsYWltZWQgKGEgbGF6eSBzdGVwX3N0YXJ0ZWQgd291bGQgbmVlZCB0aGUgaW5wdXQgdGhhdFxuICAgICAgLy8gZmFpbGVkKSBub3IgcXVldWVkLiBEaXNwYXRjaCBiZWxvdyBmaW5hbGl6ZXMgdGhlbSBhcyBzdGVwX2NyZWF0ZWRcbiAgICAgIC8vICsgc3RlcF9mYWlsZWQgaW5zdGVhZDsgb25seSBoZWFsdGh5IHN0ZXBzIGNvbXBldGUgZm9yIGlubGluZVxuICAgICAgLy8gc2xvdHMgYW5kIG92ZXJmbG93LlxuICAgICAgY29uc3QgaGVhbHRoeVN0ZXBzID0gZnJlc2hTdGVwcy5maWx0ZXIoXG4gICAgICAgIChzdGVwKSA9PiAhc3RlcC5zZXJpYWxpemF0aW9uRXJyb3JcbiAgICAgICk7XG4gICAgICBjb25zdCBpbmxpbmVDYW5kaWRhdGVzID1cbiAgICAgICAgbWF4SW5saW5lU3RlcHMgPD0gMCA/IFtdIDogaGVhbHRoeVN0ZXBzLnNsaWNlKDAsIG1heElubGluZVN0ZXBzKTtcbiAgICAgIGNvbnN0IGlubGluZUNsYWltQ2lkcyA9IG5ldyBTZXQoXG4gICAgICAgIGlubGluZUNhbmRpZGF0ZXMubWFwKChzdGVwKSA9PiBzdGVwLmNvcnJlbGF0aW9uSWQpXG4gICAgICApO1xuXG4gICAgICAvLyAxLiBEdXJhYmxlIHNpZGUgZWZmZWN0cyBmb3IgdGhpcyBzdXNwZW5zaW9uJ3MgcGVuZGluZyBvcHMuXG4gICAgICBjb25zdCBvcHNUb0Rpc3BhdGNoID0gcGVuZGluZ09wZXJhdGlvbnMubWFwKChvcCkgPT5cbiAgICAgICAgb3AudHlwZSA9PT0gJ2hvb2snICYmXG4gICAgICAgIChvcCBhcyBQZW5kaW5nSG9vaykuYWJvcnRSZXF1ZXN0ZWQgJiZcbiAgICAgICAgcmVjb3JkZWRBYm9ydElkcy5oYXMob3AuY29ycmVsYXRpb25JZClcbiAgICAgICAgICA/ICh7IC4uLm9wLCBhYm9ydFJlcXVlc3RlZDogZmFsc2UgfSBhcyBQZW5kaW5nT3BlcmF0aW9uKVxuICAgICAgICAgIDogb3BcbiAgICAgICk7XG4gICAgICBmb3IgKGNvbnN0IG9wIG9mIHBlbmRpbmdPcGVyYXRpb25zKSB7XG4gICAgICAgIGlmIChvcC50eXBlID09PSAnaG9vaycgJiYgKG9wIGFzIFBlbmRpbmdIb29rKS5hYm9ydFJlcXVlc3RlZCkge1xuICAgICAgICAgIHJlY29yZGVkQWJvcnRJZHMuYWRkKG9wLmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBTdGVwcyBiZXlvbmQgdGhlIGlubGluZSBjYXAgYXJlIGhhbmRlZCB0byB0aGUgcXVldWUgaW4gdGhlIHNhbWVcbiAgICAgIC8vIHR1cm4gdGhlaXIgc3RlcF9jcmVhdGVkIGlzIHdyaXR0ZW4uIFdoZXJlIGVsaWdpYmxlLCB0aGUgZGlzcGF0Y2hcbiAgICAgIC8vIGJlbG93IHBhcmFsbGVsaXplcyBlYWNoIG92ZXJmbG93IHN0ZXAncyBzdGVwX2NyZWF0ZWQgd3JpdGUgd2l0aFxuICAgICAgLy8gaXRzIHF1ZXVlIHB1Ymxpc2ggKHJlc2lsaWVudCBzdGVwIGRpc3BhdGNoOiB0aGUgbWVzc2FnZSBjYXJyaWVzXG4gICAgICAvLyBgc3RlcElucHV0YCBzbyB0aGUgY29uc3VtZXIgY2FuIHJlLWVuc3VyZSB0aGUgZXZlbnQpOyB0aGUgcmVzdFxuICAgICAgLy8gYXJlIHF1ZXVlZCByaWdodCBhZnRlciwgaW4gcGFyYWxsZWwuIFRoaXMgbXVzdCBhbGwgaGFwcGVuIEJFRk9SRVxuICAgICAgLy8gdGhlIGV2ZW50IGZlZWQgYmVsb3c6IHRoZSBmZWVkIGFsd2F5cyBvYnNlcnZlcyB0aG9zZVxuICAgICAgLy8gc3RlcF9jcmVhdGVkIHdyaXRlcyBhcyB1bnNlZW4gZXZlbnRzIGFuZCBgY29udGludWVgcywgc28gYVxuICAgICAgLy8gaGFuZG9mZiBwbGFjZWQgYWZ0ZXIgaXQgaXMgdW5yZWFjaGFibGUgb24gdGhlIG9ubHkgaXRlcmF0aW9uXG4gICAgICAvLyB0aGF0IHN0aWxsIGNsYXNzaWZpZXMgdGhlc2Ugc3RlcHMgYXMgZnJlc2g6IG5leHQgdHVybiB0aGV5IGNhcnJ5XG4gICAgICAvLyBoYXNDcmVhdGVkRXZlbnQgYW5kIHdvdWxkIG5ldmVyIGJlIHF1ZXVlZCBhdCBhbGwgKHRoZSB3ZWRnZSBiZWhpbmRcbiAgICAgIC8vIHByb21pc2VSYWNlU3RyZXNzVGVzdFdvcmtmbG93IGhhbmdpbmcgaW4gdGhlIHF1aWNranMgQ0kgbGVncykuIFRoZVxuICAgICAgLy8gc3RlcC1pZGVudGl0eS1zY29wZWQgaWRlbXBvdGVuY3kga2V5IG1ha2VzIHJlcGVhdHMgaGFybWxlc3MuXG4gICAgICBjb25zdCBvdmVyZmxvd1N0ZXBzID0gaGVhbHRoeVN0ZXBzLnNsaWNlKGlubGluZUNhbmRpZGF0ZXMubGVuZ3RoKTtcbiAgICAgIGNvbnN0IGRpc3BhdGNoZWQgPSBhd2FpdCBkaXNwYXRjaFBlbmRpbmdPcHMoe1xuICAgICAgICB3b3JsZCxcbiAgICAgICAgcnVuSWQsXG4gICAgICAgIHdvcmtmbG93UnVuLFxuICAgICAgICBlbmNyeXB0aW9uS2V5LFxuICAgICAgICBuYW1lc3BhY2UsXG4gICAgICAgIG5leHRUcmFjZUNhcnJpZXIsXG4gICAgICAgIHBlbmRpbmdPcGVyYXRpb25zOiBvcHNUb0Rpc3BhdGNoLFxuICAgICAgICBza2lwU3RlcENyZWF0aW9uOiBpbmxpbmVDbGFpbUNpZHMsXG4gICAgICAgIHF1ZXVlU3RlcENpZHM6IG5ldyBTZXQob3ZlcmZsb3dTdGVwcy5tYXAoKHMpID0+IHMuY29ycmVsYXRpb25JZCkpLFxuICAgICAgICBmaW5hbGl6ZVVuc2VyaWFsaXphYmxlU3RlcHM6IHRydWUsXG4gICAgICAgIHdmZGlhZyxcbiAgICAgIH0pO1xuICAgICAgaWYgKFxuICAgICAgICBkaXNwYXRjaGVkLmNyZWF0ZWRBdHRyaWJ1dGVFdmVudCB8fFxuICAgICAgICBkaXNwYXRjaGVkLmNyZWF0ZWRHZXRDb25mbGljdEhvb2tcbiAgICAgICkge1xuICAgICAgICBwZW5kaW5nUmVxdWV1ZVNpZ25hbCA9IHRydWU7XG4gICAgICB9XG4gICAgICAvLyBBIGZpbmFsaXplZCB1bnNlcmlhbGl6YWJsZSBzdGVwIGhhcyB0ZXJtaW5hbCBldmVudHMgZHVyYWJseVxuICAgICAgLy8gd3JpdHRlbiBidXQgbm8gZXhlY3V0aW9uIG1lc3NhZ2UgYW55d2hlcmU6IGlmIHRoZSBmZWVkIGJlbG93XG4gICAgICAvLyBkb2Vzbid0IHN1cmZhY2UgdGhlbSAoZXZlbnR1YWxseS1jb25zaXN0ZW50IGxpc3RpbmcpIGFuZCB0aGUgbG9vcFxuICAgICAgLy8gZXhpdHMsIG5vdGhpbmcgd291bGQgZXZlciByZS1pbnZva2UgdGhlIHJ1biB0byBvYnNlcnZlIHRoZVxuICAgICAgLy8gZmFpbHVyZS4gUmFpc2UgdGhlIHJlcXVldWUgc2lnbmFsIChzYW1lIG1lY2hhbmlzbSBhcyBpbmxpbmVcbiAgICAgIC8vIHRlcm1pbmFscykgYW5kIG1hcmsgdGhlIHN0ZXBzIGhhbmRsZWQgc28gbGF0ZXIgdHVybnMgZG9uJ3RcbiAgICAgIC8vIHJlLWZpbmFsaXplIG9yIGJhY2tzdG9wLXF1ZXVlIHRoZW0uXG4gICAgICBpZiAoZGlzcGF0Y2hlZC5mYWlsZWRTZXJpYWxpemF0aW9uU3RlcENpZHMuc2l6ZSA+IDApIHtcbiAgICAgICAgcGVuZGluZ1JlcXVldWVTaWduYWwgPSB0cnVlO1xuICAgICAgICBmb3IgKGNvbnN0IGNpZCBvZiBkaXNwYXRjaGVkLmZhaWxlZFNlcmlhbGl6YXRpb25TdGVwQ2lkcykge1xuICAgICAgICAgIGV4ZWN1dGVkU3RlcElkcy5hZGQoY2lkKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmb3IgKGNvbnN0IGNpZCBvZiBkaXNwYXRjaGVkLnF1ZXVlZFN0ZXBDaWRzKSB7XG4gICAgICAgIHF1ZXVlZFN0ZXBJZHMuYWRkKGNpZCk7XG4gICAgICB9XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgb3ZlcmZsb3dTdGVwc1xuICAgICAgICAgIC5maWx0ZXIoKHN0ZXApID0+ICFkaXNwYXRjaGVkLnF1ZXVlZFN0ZXBDaWRzLmhhcyhzdGVwLmNvcnJlbGF0aW9uSWQpKVxuICAgICAgICAgIC5tYXAoKHN0ZXApID0+IHtcbiAgICAgICAgICAgIHF1ZXVlZFN0ZXBJZHMuYWRkKHN0ZXAuY29ycmVsYXRpb25JZCk7XG4gICAgICAgICAgICByZXR1cm4gcXVldWVTdGVwTWVzc2FnZSh7XG4gICAgICAgICAgICAgIHdvcmxkLFxuICAgICAgICAgICAgICBydW5JZCxcbiAgICAgICAgICAgICAgd29ya2Zsb3dSdW4sXG4gICAgICAgICAgICAgIHN0ZXAsXG4gICAgICAgICAgICAgIG5hbWVzcGFjZSxcbiAgICAgICAgICAgICAgbmV4dFRyYWNlQ2FycmllcixcbiAgICAgICAgICAgICAgcHVycG9zZTogJ2Rpc3BhdGNoJyxcbiAgICAgICAgICAgICAgd2ZkaWFnLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSlcbiAgICAgICk7XG5cbiAgICAgIC8vIENvbXBsZXRlIGVsYXBzZWQgd2FpdHMgc28gdGhlaXIgd2FpdF9jb21wbGV0ZWQgZXZlbnRzIGFyZSBwaWNrZWRcbiAgICAgIC8vIHVwIGJ5IHRoZSBmZWVkIGJlbG93IChpbnN0ZWFkIG9mIGEgcXVldWUgcmUtaW52b2NhdGlvbikuXG4gICAgICBjb25zdCB3YWl0Q29tcGxldGVQcm9taXNlczogUHJvbWlzZTx2b2lkPltdID0gW107XG4gICAgICBmb3IgKGNvbnN0IG9wIG9mIHBlbmRpbmdPcGVyYXRpb25zKSB7XG4gICAgICAgIGlmIChvcC50eXBlICE9PSAnd2FpdCcpIGNvbnRpbnVlO1xuICAgICAgICBjb25zdCB3YWl0ID0gb3AgYXMgUGVuZGluZ1dhaXQ7XG4gICAgICAgIGlmIChjb21wbGV0ZWRXYWl0SWRzMi5oYXMod2FpdC5jb3JyZWxhdGlvbklkKSkgY29udGludWU7XG4gICAgICAgIGlmIChuZXcgRGF0ZSh3YWl0LnJlc3VtZUF0KS5nZXRUaW1lKCkgLSBEYXRlLm5vdygpID4gMCkgY29udGludWU7XG4gICAgICAgIGNvbXBsZXRlZFdhaXRJZHMyLmFkZCh3YWl0LmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICB3YWl0Q29tcGxldGVQcm9taXNlcy5wdXNoKFxuICAgICAgICAgIChhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBhd2FpdCB3b3JsZC5ldmVudHMuY3JlYXRlKHJ1bklkLCB7XG4gICAgICAgICAgICAgICAgZXZlbnRUeXBlOiAnd2FpdF9jb21wbGV0ZWQnLFxuICAgICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgICAgICBjb3JyZWxhdGlvbklkOiB3YWl0LmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKGVycikpIHJldHVybjtcbiAgICAgICAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pKClcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGlmICh3YWl0Q29tcGxldGVQcm9taXNlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHdhaXRDb21wbGV0ZVByb21pc2VzKTtcbiAgICAgIH1cblxuICAgICAgLy8gMi4gQ2hlYXAgcHJvZ3Jlc3MgZmlyc3Q6IGZlZWQgbmV3bHkgcmVjb3JkZWQgZXZlbnRzIGludG8gdGhlIGxpdmVcbiAgICAgIC8vIFZNIGJlZm9yZSBibG9ja2luZyBvbiBzdGVwIGJvZGllcy5cbiAgICAgIHtcbiAgICAgICAgY29uc3QgbmV3RXZlbnRzID0gYXdhaXQgZmV0Y2hVbnNlZW5FdmVudHMoKTtcbiAgICAgICAgaWYgKG5ld0V2ZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgLy8gVGhlIGxpc3RpbmcgY2F1Z2h0IHVwIHdpdGggdGhpcyBpbnZvY2F0aW9uJ3Mgd3JpdGVzLCBzbyBhbnlcbiAgICAgICAgICAvLyBhdHRyX3NldCAvIGdldENvbmZsaWN0IGhvb2tfY3JlYXRlZCBoYXMgYmVlbiAob3IgaXMgYmVpbmcpXG4gICAgICAgICAgLy8gY29uc3VtZWQgYnkgdGhlIGxpdmUgVk0sIHNvIG5vIGV4dGVybmFsIHJlcXVldWUgaXMgbmVlZGVkLlxuICAgICAgICAgIHBlbmRpbmdSZXF1ZXVlU2lnbmFsID0gZmFsc2U7XG4gICAgICAgICAgcmVzdWx0ID0gYXdhaXQgc2Vzc2lvbi5jb250aW51ZVdpdGhFdmVudHMobmV3RXZlbnRzKTtcbiAgICAgICAgICB3ZmRpYWcoJ2lubGluZV9pdGVyYXRpb24nLCB7XG4gICAgICAgICAgICBpdGVyYXRpb24sXG4gICAgICAgICAgICBwaGFzZTogJ2ZlZWQnLFxuICAgICAgICAgICAgZmVkRXZlbnRzOiBuZXdFdmVudHMubGVuZ3RoLFxuICAgICAgICAgICAgb3V0Y29tZTogcmVzdWx0LmNvbXBsZXRlZFxuICAgICAgICAgICAgICA/ICdjb21wbGV0ZWQnXG4gICAgICAgICAgICAgIDogcmVzdWx0LmZhaWxlZFxuICAgICAgICAgICAgICAgID8gJ2ZhaWxlZCdcbiAgICAgICAgICAgICAgICA6ICdzdXNwZW5kZWQnLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIDMuIE5vIGNoZWFwIHByb2dyZXNzIGxlZnQsIHNvIGV4ZWN1dGUgc3RlcHMgaW5saW5lLlxuICAgICAgY29uc3Qgc3RlcE9wcyA9IHBlbmRpbmdPcGVyYXRpb25zLmZpbHRlcihcbiAgICAgICAgKG9wKTogb3AgaXMgUGVuZGluZ1N0ZXAgPT4gb3AudHlwZSA9PT0gJ3N0ZXAnXG4gICAgICApO1xuICAgICAgLy8gU3RlcHMgY3JlYXRlZCBieSBhbiBFQVJMSUVSIGludm9jYXRpb24gKG9yIGFuIGVhcmxpZXIgdHVybikgdGhhdFxuICAgICAgLy8gYXJlIHN0aWxsIHBlbmRpbmcsIHdpdGggbm8gd29yayBvd25lZCBieSBUSElTIGludm9jYXRpb24uIE1pcnJvclxuICAgICAgLy8gdGhlIG5vZGUgZW5naW5lJ3Mgb3duZXJzaGlwIGRlY2lzaW9uIHRhYmxlIChzdGVwLW93bmVyc2hpcC50cyksXG4gICAgICAvLyBOT1QgYSBkZWxpdmVyeUF0dGVtcHQgZ2F0ZTogd29ybGRzIGFkdmFuY2UgdGhlIGF0dGVtcHQgY291bnRlciBvblxuICAgICAgLy8gcm91dGluZSByZWRlbGl2ZXJpZXMgKHdvcmxkLWxvY2FsIGNvdW50cyBldmVyeSBoYW5kbGVkIHJlc3BvbnNlKSxcbiAgICAgIC8vIHNvIGF0dGVtcHQgPiAxIGlzIHRoZSBjb21tb24gY2FzZSBhbmQgd291bGQgZmlyZSBiYWNrc3RvcHMgYXRcbiAgICAgIC8vIHN0ZXBzIGFjdGl2ZWx5IGV4ZWN1dGluZyBpbmxpbmUgaW4gYSBsaXZlIGludm9jYXRpb24uXG4gICAgICAvL1xuICAgICAgLy8gICAtIE93bmVyc2hpcCBsZWFzZSBBQ1RJVkUsIGhlbGQgYnkgQU5PVEhFUiBtZXNzYWdlIOKGkiB0aGUgc3RlcCBpc1xuICAgICAgLy8gICAgIChwcmVzdW1hYmx5KSBleGVjdXRpbmcgaW5saW5lIGluIGEgbGl2ZSBpbnZvY2F0aW9uLiBBcm0gYVxuICAgICAgLy8gICAgIERFTEFZRUQgYmFja3N0b3AgZm9yIHRoZSBsZWFzZSByZW1haW5kZXIsIGtleWVkIHRvIHRoZVxuICAgICAgLy8gICAgIG93bmVyc2hpcCBlcG9jaCAoYSByZWZyZXNoZWQgbGVhc2UgcmUtYXJtcyBhIGZyZXNoIGJhY2tzdG9wXG4gICAgICAvLyAgICAgaW5zdGVhZCBvZiBkZWR1cGluZyBhZ2FpbnN0IHRoZSBpbi1mbGlnaHQgb25lKS4gSWYgdGhlIG93bmVyXG4gICAgICAvLyAgICAgY29tcGxldGVzIG5vcm1hbGx5LCB0aGUgYmFja3N0b3AgZGVsaXZlcnkgcmVzb2x2ZXMgdGhlIHN0ZXBcbiAgICAgIC8vICAgICBhcyAnc2tpcHBlZCcuXG4gICAgICAvLyAgIC0gT3duZXJzaGlwIGxlYXNlIEFDVElWRSwgaGVsZCBieSBUSElTIG1lc3NhZ2Ug4oaSIHRoaXMgZGVsaXZlcnlcbiAgICAgIC8vICAgICBpcyB0aGUgb3duZXIncyByZWRlbGl2ZXJ5OyB0aGUgY2xhaW1hbnQgY3Jhc2hlZFxuICAgICAgLy8gICAgIG1pZC1leGVjdXRpb24uIERpc3BhdGNoIGltbWVkaWF0ZWx5IGZvciBiYWNrZ3JvdW5kIHJlY292ZXJ5LlxuICAgICAgLy8gICAtIE5vIHN0YW1wIC8gbGVhc2UgRVhQSVJFRCAvIHN0ZXBfcmV0cnlpbmcgb2JzZXJ2ZWQg4oaSIHRoZSBzdGVwXG4gICAgICAvLyAgICAgaXMgcXVldWUtb3duZWQgb3Igb3JwaGFuZWQuIERpc3BhdGNoIGltbWVkaWF0ZWx5OyB0aGVcbiAgICAgIC8vICAgICBzdGVwLWlkZW50aXR5LXNjb3BlZCBpZGVtcG90ZW5jeSBrZXkgZGVkdXBlcyBhZ2FpbnN0IHRoZVxuICAgICAgLy8gICAgIG9yaWdpbmFsIGhhbmRvZmYuXG4gICAgICBjb25zdCBub3dNcyA9IERhdGUubm93KCk7XG4gICAgICBmb3IgKGNvbnN0IHN0ZXAgb2Ygc3RlcE9wcykge1xuICAgICAgICBpZiAoIXN0ZXAuaGFzQ3JlYXRlZEV2ZW50KSBjb250aW51ZTtcbiAgICAgICAgaWYgKGV4ZWN1dGVkU3RlcElkcy5oYXMoc3RlcC5jb3JyZWxhdGlvbklkKSkgY29udGludWU7XG4gICAgICAgIGlmIChxdWV1ZWRTdGVwSWRzLmhhcyhzdGVwLmNvcnJlbGF0aW9uSWQpKSBjb250aW51ZTtcbiAgICAgICAgY29uc3Qgb3duZXJzaGlwID0gc3RlcE93bmVyc2hpcC5nZXQoc3RlcC5jb3JyZWxhdGlvbklkKTtcbiAgICAgICAgY29uc3Qgb3duZXJzaGlwQWN0aXZlID1cbiAgICAgICAgICBvd25lcnNoaXAgIT09IHVuZGVmaW5lZCAmJlxuICAgICAgICAgIG93bmVyc2hpcC5vd25lciAhPT0gdW5kZWZpbmVkICYmXG4gICAgICAgICAgIW93bmVyc2hpcC5zYXdSZXRyeWluZztcbiAgICAgICAgbGV0IGxlYXNlUmVtYWluaW5nU2Vjb25kcyA9IDA7XG4gICAgICAgIGlmIChvd25lcnNoaXBBY3RpdmUgJiYgb3duZXJzaGlwLnN0YXJ0ZWRBdE1zICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBjb25zdCBsZWFzZVNlY29uZHMgPSBnZXRJbmxpbmVPd25lcnNoaXBMZWFzZVNlY29uZHMoKTtcbiAgICAgICAgICBsZWFzZVJlbWFpbmluZ1NlY29uZHMgPSBNYXRoLm1pbihcbiAgICAgICAgICAgIGxlYXNlU2Vjb25kcyxcbiAgICAgICAgICAgIE1hdGgubWF4KFxuICAgICAgICAgICAgICAwLFxuICAgICAgICAgICAgICBNYXRoLmNlaWwoXG4gICAgICAgICAgICAgICAgKG93bmVyc2hpcC5zdGFydGVkQXRNcyArIGxlYXNlU2Vjb25kcyAqIDEwMDAgLSBub3dNcykgLyAxMDAwXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIClcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGlmIChcbiAgICAgICAgICBvd25lcnNoaXBBY3RpdmUgJiZcbiAgICAgICAgICBvd25lcnNoaXAub3duZXIgIT09IG93bmVyTWVzc2FnZUlkICYmXG4gICAgICAgICAgbGVhc2VSZW1haW5pbmdTZWNvbmRzID4gMFxuICAgICAgICApIHtcbiAgICAgICAgICBxdWV1ZWRTdGVwSWRzLmFkZChzdGVwLmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICAgIGF3YWl0IHF1ZXVlU3RlcE1lc3NhZ2Uoe1xuICAgICAgICAgICAgd29ybGQsXG4gICAgICAgICAgICBydW5JZCxcbiAgICAgICAgICAgIHdvcmtmbG93UnVuLFxuICAgICAgICAgICAgc3RlcCxcbiAgICAgICAgICAgIGRlbGF5U2Vjb25kczogbGVhc2VSZW1haW5pbmdTZWNvbmRzLFxuICAgICAgICAgICAgbmFtZXNwYWNlLFxuICAgICAgICAgICAgbmV4dFRyYWNlQ2FycmllcixcbiAgICAgICAgICAgIHB1cnBvc2U6IGBiYWNrc3RvcDoke293bmVyc2hpcC5zdGFydGVkQXRNc31gLFxuICAgICAgICAgICAgd2ZkaWFnLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHF1ZXVlZFN0ZXBJZHMuYWRkKHN0ZXAuY29ycmVsYXRpb25JZCk7XG4gICAgICAgICAgYXdhaXQgcXVldWVTdGVwTWVzc2FnZSh7XG4gICAgICAgICAgICB3b3JsZCxcbiAgICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgICAgd29ya2Zsb3dSdW4sXG4gICAgICAgICAgICBzdGVwLFxuICAgICAgICAgICAgbmFtZXNwYWNlLFxuICAgICAgICAgICAgbmV4dFRyYWNlQ2FycmllcixcbiAgICAgICAgICAgIHB1cnBvc2U6ICdkaXNwYXRjaCcsXG4gICAgICAgICAgICB3ZmRpYWcsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGlubGluZUNhbmRpZGF0ZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIC8vIE5vIGluLXByb2Nlc3MgcHJvZ3Jlc3MgcG9zc2libGU6IHRoZSBydW4gYXdhaXRzIGFuIGV4dGVybmFsXG4gICAgICAgIC8vIHN0aW11bHVzIChob29rIHBheWxvYWQsIHF1ZXVlZCBzdGVwLCB3YWl0IHRpbWVyKS5cbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIC8vIFJhY2luZyB0aW1lcnMgbXVzdCBmaXJlIG9uIHRpbWUgd2hpbGUgc3RlcCBib2RpZXMgYmxvY2sgdGhpc1xuICAgICAgLy8gaW52b2NhdGlvbjogZW5xdWV1ZSBhIGRlbGF5ZWQgY29udGludWF0aW9uIGZvciB0aGUgc29vbmVzdFxuICAgICAgLy8gcGVuZGluZyB3YWl0IChhIHNlcGFyYXRlIGludm9jYXRpb24gd3JpdGVzIGl0cyB3YWl0X2NvbXBsZXRlZCBhdFxuICAgICAgLy8gdGhlIHJpZ2h0IGxvZyBwb3NpdGlvbiwgdGhlIHNhbWUgbWVjaGFuaXNtIGFzIHRoZSBub2RlOnZtIGVuZ2luZSdzXG4gICAgICAvLyB3YWl0LWNvbnRpbnVhdGlvbiBkaXNwYXRjaCkuXG4gICAgICBsZXQgc29vbmVzdFdhaXQ6IHsgY29ycmVsYXRpb25JZDogc3RyaW5nOyBzZWNvbmRzOiBudW1iZXIgfSB8IHVuZGVmaW5lZDtcbiAgICAgIGZvciAoY29uc3Qgb3Agb2YgcGVuZGluZ09wZXJhdGlvbnMpIHtcbiAgICAgICAgaWYgKG9wLnR5cGUgIT09ICd3YWl0JykgY29udGludWU7XG4gICAgICAgIGNvbnN0IHdhaXQgPSBvcCBhcyBQZW5kaW5nV2FpdDtcbiAgICAgICAgaWYgKHNjaGVkdWxlZFdhaXRDb250aW51YXRpb25zLmhhcyh3YWl0LmNvcnJlbGF0aW9uSWQpKSBjb250aW51ZTtcbiAgICAgICAgLy8gV2FpdHMgd2hvc2Ugd2FpdF9jb21wbGV0ZWQgVEhJUyBpbnZvY2F0aW9uIGFscmVhZHkgd3JvdGUgKHRoZVxuICAgICAgICAvLyBlbGFwc2VkLXdhaXQgcGFzcyBhYm92ZSkgYXJlIGRvbmU6IHRoZSBldmVudCBqdXN0IGhhc24ndCBmZWRcbiAgICAgICAgLy8gYmFjayBpbnRvIHRoZSBWTSB5ZXQuIE5vIGNvbnRpbnVhdGlvbiBuZWVkZWQuXG4gICAgICAgIGlmIChjb21wbGV0ZWRXYWl0SWRzMi5oYXMod2FpdC5jb3JyZWxhdGlvbklkKSkgY29udGludWU7XG4gICAgICAgIGNvbnN0IHJlc3VtZU1zID0gbmV3IERhdGUod2FpdC5yZXN1bWVBdCkuZ2V0VGltZSgpIC0gRGF0ZS5ub3coKTtcbiAgICAgICAgLy8gQW4gYWxyZWFkeS1lbGFwc2VkIHdhaXQgTVVTVCBzdGlsbCBnZXQgYSBjb250aW51YXRpb24gKGNsYW1wZWRcbiAgICAgICAgLy8gdG8gdGhlIDFzIG1pbmltdW0sIGV4YWN0bHkgbGlrZSB0aGUgbm9kZSBlbmdpbmUnc1xuICAgICAgICAvLyBgTWF0aC5tYXgoMTAwMCwgcmVzdW1lQXRNcyAtIG5vdylgKSwgbm90IGJlIHNraXBwZWQ6IGEgd2FpdFxuICAgICAgICAvLyB3aG9zZSBkZWFkbGluZSBmYWxscyBiZXR3ZWVuIHRoaXMgaXRlcmF0aW9uJ3MgZWxhcHNlZC13YWl0XG4gICAgICAgIC8vIHBhc3MgKHdoaWNoIHNhdyBpdCBhcyBzdGlsbCBwZW5kaW5nIGFuZCB3cm90ZSBub3RoaW5nKSBhbmRcbiAgICAgICAgLy8gdGhpcyBzd2VlcCB3b3VsZCBvdGhlcndpc2UgZ2V0IE5FSVRIRVIgYSB3YWl0X2NvbXBsZXRlZCBOT1IgYVxuICAgICAgICAvLyBjb250aW51YXRpb24sIGFuZCB0aGUgaW5saW5lIGJhdGNoIGJlbG93IHRoZW4gYmxvY2tzIHRoaXNcbiAgICAgICAgLy8gaW52b2NhdGlvbiBmb3IgdGhlIGZ1bGwgc3RlcCBkdXJhdGlvbiB3aXRoIG5vIHdha2UgYXJtZWRcbiAgICAgICAgLy8gYW55d2hlcmUuIEZvciBgUHJvbWlzZS5yYWNlKHN0ZXAsIHNsZWVwKWAgdGhhdCBzaWxlbnRseSBoYW5kc1xuICAgICAgICAvLyB0aGUgcmFjZSB0byB0aGUgc3RlcDogdGhlIHNsZWVwJ3Mgd2FpdF9jb21wbGV0ZWQgaXMgbmV2ZXJcbiAgICAgICAgLy8gd3JpdHRlbiBhbmQgdGhlIHJ1biBjb21wbGV0ZXMgd2l0aCB0aGUgd3Jvbmcgd2lubmVyLiBUaGVcbiAgICAgICAgLy8gd2luZG93IGJldHdlZW4gdGhlIHR3byBjaGVja3Mgc3BhbnMgdGhpcyBpdGVyYXRpb24ncyBkaXNwYXRjaFxuICAgICAgICAvLyArIGZlZWQgcm91bmQtdHJpcHMsIHNvIG9uIG5ldHdvcmstYmFja2VkIHdvcmxkcyAod29ybGQtdmVyY2VsKVxuICAgICAgICAvLyBhIHNob3J0IHNsZWVwIGxhbmRzIGluIGl0IHJvdXRpbmVseSwgb2JzZXJ2ZWQgYXMgYSB+NTAlXG4gICAgICAgIC8vIHNsZWVwV2luc1JhY2VXb3JrZmxvdyBmYWlsdXJlIHJhdGUgaW4gdGhlIFZlcmNlbCBlMmUgbGVncyxcbiAgICAgICAgLy8gd2hpbGUgd29ybGQtbG9jYWwncyBzdWItbXMgcm91bmQtdHJpcHMgbWFza2VkIGl0IGxvY2FsbHkuIFRoZVxuICAgICAgICAvLyBjb250aW51YXRpb24gaW52b2NhdGlvbidzIHByZS1WTSBlbGFwc2VkIGNoZWNrIHdyaXRlcyB0aGVcbiAgICAgICAgLy8gd2FpdF9jb21wbGV0ZWQgfjFzIGxhdGVyLlxuICAgICAgICBjb25zdCBzZWNvbmRzID0gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKHJlc3VtZU1zIC8gMTAwMCkpO1xuICAgICAgICBpZiAoIXNvb25lc3RXYWl0IHx8IHNlY29uZHMgPCBzb29uZXN0V2FpdC5zZWNvbmRzKSB7XG4gICAgICAgICAgc29vbmVzdFdhaXQgPSB7IGNvcnJlbGF0aW9uSWQ6IHdhaXQuY29ycmVsYXRpb25JZCwgc2Vjb25kcyB9O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoc29vbmVzdFdhaXQpIHtcbiAgICAgICAgc2NoZWR1bGVkV2FpdENvbnRpbnVhdGlvbnMuYWRkKHNvb25lc3RXYWl0LmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICBjb25zdCBhdHRlbXB0ID0gbmV4dFdhaXRDb250aW51YXRpb25BdHRlbXB0KHNvb25lc3RXYWl0LmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICBhd2FpdCBxdWV1ZU1lc3NhZ2UoXG4gICAgICAgICAgd29ybGQsXG4gICAgICAgICAgZ2V0V29ya2Zsb3dRdWV1ZU5hbWUod29ya2Zsb3dSdW4ud29ya2Zsb3dOYW1lLCBuYW1lc3BhY2UpLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgICAgdHJhY2VDYXJyaWVyOiBhd2FpdCBuZXh0VHJhY2VDYXJyaWVyKCksXG4gICAgICAgICAgICByZXF1ZXN0ZWRBdDogbmV3IERhdGUoKSxcbiAgICAgICAgICAgIHdhaXRDb250aW51YXRpb246IHtcbiAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogc29vbmVzdFdhaXQuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgICAgYXR0ZW1wdCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgICBnZXRXYWl0Q29udGludWF0aW9uRGlzcGF0Y2goXG4gICAgICAgICAgICBzb29uZXN0V2FpdC5zZWNvbmRzLFxuICAgICAgICAgICAgc29vbmVzdFdhaXQuY29ycmVsYXRpb25JZCxcbiAgICAgICAgICAgIERhdGUubm93KCksXG4gICAgICAgICAgICBhdHRlbXB0XG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgICB3ZmRpYWcoJ3dhaXRfY29udGludWF0aW9uX3NjaGVkdWxlZCcsIHtcbiAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzb29uZXN0V2FpdC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgIGRlbGF5U2Vjb25kczogc29vbmVzdFdhaXQuc2Vjb25kcyxcbiAgICAgICAgICBhdHRlbXB0LFxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLy8gRXhlY3V0ZSB0aGUgaW5saW5lIGJhdGNoIGluIHBhcmFsbGVsLiBUaGUgcmVwbGF5IGJ1ZGdldCBpc1xuICAgICAgLy8gcGF1c2VkIHdoaWxlIHN0ZXAgYm9kaWVzIHJ1bjogc3RlcCBkdXJhdGlvbiBpcyBib3VuZGVkIGJ5IHRoZVxuICAgICAgLy8gcGxhdGZvcm0gZnVuY3Rpb24gZHVyYXRpb24sIG5vdCB0aGUgcmVwbGF5IHRpbWVvdXQuIE5PVEUgKGJ5XG4gICAgICAvLyBkZXNpZ24pOiB3aXRoIHRoZSBidWRnZXQgcGFya2VkIHBlciBiYXRjaCwgdGhlIG9ubHkgYm91bmQgb24gaG93XG4gICAgICAvLyBtYW55IGlubGluZSBzdGVwcyBvbmUgaW52b2NhdGlvbiBjYW4gY2hhaW4gaXMgdGhlIHBsYXRmb3JtJ3NcbiAgICAgIC8vIGZ1bmN0aW9uIHRpbWVvdXQ6IHRoZSBTREsgZGVsaWJlcmF0ZWx5IGltcG9zZXMgbm8gY2FwIG9mIGl0c1xuICAgICAgLy8gb3duLCBtYXRjaGluZyB0aGUgbm9kZTp2bSBlbmdpbmUsIHdoZXJlIGEgbG9uZyBzZXF1ZW50aWFsXG4gICAgICAvLyB3b3JrZmxvdyBsaWtld2lzZSBydW5zIHN0ZXAtYnktc3RlcCB1bnRpbCB0aGUgcGxhdGZvcm0gcmVjbGFpbXNcbiAgICAgIC8vIHRoZSBpbnZvY2F0aW9uIGFuZCBhIHJlZGVsaXZlcnkgcmVzdW1lcyBmcm9tIHRoZSBsb2cuXG4gICAgICBidWRnZXQucGF1c2UoKTtcbiAgICAgIGxldCBvdXRjb21lczogU3RlcEV4ZWN1dGlvblJlc3VsdFtdO1xuICAgICAgdHJ5IHtcbiAgICAgICAgb3V0Y29tZXMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgICBpbmxpbmVDYW5kaWRhdGVzLm1hcCgoc3RlcCkgPT5cbiAgICAgICAgICAgIHJ1blN0ZXBTaW5nbGVGbGlnaHQocnVuSWQsIHN0ZXAuY29ycmVsYXRpb25JZCwgKCkgPT5cbiAgICAgICAgICAgICAgKGFzeW5jICgpID0+XG4gICAgICAgICAgICAgICAgZXhlY3V0ZVN0ZXAoe1xuICAgICAgICAgICAgICAgICAgd29ybGQsXG4gICAgICAgICAgICAgICAgICB3b3JrZmxvd1J1bklkOiBydW5JZCxcbiAgICAgICAgICAgICAgICAgIHdvcmtmbG93RGVwbG95bWVudElkOiB3b3JrZmxvd1J1bi5kZXBsb3ltZW50SWQsXG4gICAgICAgICAgICAgICAgICB3b3JrZmxvd05hbWU6IHdvcmtmbG93UnVuLndvcmtmbG93TmFtZSxcbiAgICAgICAgICAgICAgICAgIHdvcmtmbG93U3RhcnRlZEF0LFxuICAgICAgICAgICAgICAgICAgcmVxdWVzdElkLFxuICAgICAgICAgICAgICAgICAgcm9vdFJ1bklkLFxuICAgICAgICAgICAgICAgICAgc3RlcElkOiBzdGVwLmNvcnJlbGF0aW9uSWQsXG4gICAgICAgICAgICAgICAgICBzdGVwTmFtZTogc3RlcC5zdGVwSWQsXG4gICAgICAgICAgICAgICAgICBlbmNyeXB0aW9uS2V5LFxuICAgICAgICAgICAgICAgICAgcnVuU3BlY1ZlcnNpb246IHdvcmtmbG93UnVuLnNwZWNWZXJzaW9uLFxuICAgICAgICAgICAgICAgICAgLy8gTGF6eSBpbmxpbmUgY2xhaW06IHN0ZXBfY3JlYXRlZCBpcyBkZWZlcnJlZCAoZGlzcGF0Y2hcbiAgICAgICAgICAgICAgICAgIC8vIHNraXBwZWQgaXQpIGFuZCB0aGlzIHN0ZXBfc3RhcnRlZCBjYXJyaWVzIHRoZSBpbnB1dCxcbiAgICAgICAgICAgICAgICAgIC8vIHNvIHRoZSB3b3JsZCBjcmVhdGVzIHRoZSBzdGVwIGF0b21pY2FsbHk6XG4gICAgICAgICAgICAgICAgICAvLyBleGFjdGx5LW9uZS1vd25lci4gQSBjb25jdXJyZW50IGNsYWltYW50IGdldHNcbiAgICAgICAgICAgICAgICAgIC8vIEVudGl0eUNvbmZsaWN0RXJyb3Ig4oaSIHsgdHlwZTogJ3NraXBwZWQnIH0gYW5kIG5ldmVyXG4gICAgICAgICAgICAgICAgICAvLyBydW5zIHRoZSBib2R5LiBNaXJyb3JzIHRoZSBub2RlIGVuZ2luZSdzIGlubGluZSBwYXRoLlxuICAgICAgICAgICAgICAgICAgbGF6eVN0ZXBJbnB1dDogYXdhaXQgZW5jcnlwdFNlcmlhbGl6ZWREYXRhKFxuICAgICAgICAgICAgICAgICAgICBzdGVwLmlucHV0LFxuICAgICAgICAgICAgICAgICAgICBlbmNyeXB0aW9uS2V5XG4gICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgLy8gT3duZXJzaGlwIHN0YW1wOiB3YWtlIHJlcGxheXMgc2VlIHRoZSBib2R5IGFzIGluXG4gICAgICAgICAgICAgICAgICAvLyBmbGlnaHQgaW4gdGhpcyBpbnZvY2F0aW9uIGFuZCBhcm0gYSBkZWxheWVkIGJhY2tzdG9wXG4gICAgICAgICAgICAgICAgICAvLyBpbnN0ZWFkIG9mIGltbWVkaWF0ZWx5IHJlcXVldWVpbmcgdGhlIHN0ZXAuXG4gICAgICAgICAgICAgICAgICBvd25lck1lc3NhZ2VJZCxcbiAgICAgICAgICAgICAgICAgIC8vIEEgbGF6eSBzdGVwIGlzIGJyYW5kLW5ldyBieSBjb25zdHJ1Y3Rpb246IGZpcnN0XG4gICAgICAgICAgICAgICAgICAvLyBhdHRlbXB0LlxuICAgICAgICAgICAgICAgICAgYXV0aG9yaXRhdGl2ZUF0dGVtcHQ6IDEsXG4gICAgICAgICAgICAgICAgfSkpKClcbiAgICAgICAgICAgIClcbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBidWRnZXQucmVzdW1lKCk7XG4gICAgICB9XG4gICAgICBpbmxpbmVTdGVwc0V4ZWN1dGVkICs9IGlubGluZUNhbmRpZGF0ZXMubGVuZ3RoO1xuXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlubGluZUNhbmRpZGF0ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3Qgc3RlcCA9IGlubGluZUNhbmRpZGF0ZXNbaV07XG4gICAgICAgIGNvbnN0IG91dGNvbWUgPSBvdXRjb21lc1tpXTtcbiAgICAgICAgZXhlY3V0ZWRTdGVwSWRzLmFkZChzdGVwLmNvcnJlbGF0aW9uSWQpO1xuICAgICAgICBpZiAob3V0Y29tZS50eXBlID09PSAncmV0cnknIHx8IG91dGNvbWUudHlwZSA9PT0gJ3Rocm90dGxlZCcpIHtcbiAgICAgICAgICAvLyBIYW5kIHRoZSBzdGVwIHRvIHRoZSBxdWV1ZSB3aXRoIHRoZSByZXF1ZXN0ZWQgYmFja29mZjpcbiAgICAgICAgICAvLyBiYWNrZ3JvdW5kIGRlbGl2ZXJ5IGRyaXZlcyB0aGUgcmV0cnkgZnJvbSBoZXJlLlxuICAgICAgICAgIHF1ZXVlZFN0ZXBJZHMuYWRkKHN0ZXAuY29ycmVsYXRpb25JZCk7XG4gICAgICAgICAgYXdhaXQgcXVldWVTdGVwTWVzc2FnZSh7XG4gICAgICAgICAgICB3b3JsZCxcbiAgICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgICAgd29ya2Zsb3dSdW4sXG4gICAgICAgICAgICBzdGVwLFxuICAgICAgICAgICAgZGVsYXlTZWNvbmRzOiBvdXRjb21lLnRpbWVvdXRTZWNvbmRzLFxuICAgICAgICAgICAgbmFtZXNwYWNlLFxuICAgICAgICAgICAgbmV4dFRyYWNlQ2FycmllcixcbiAgICAgICAgICAgIC8vIFN1ZmZpeGVkIGtleTogdGhpcyBzdGVwIHdhcyBpbmxpbmUtY2xhaW1lZCwgc28gbm8gZGlzcGF0Y2hcbiAgICAgICAgICAgIC8vIHB1Ymxpc2ggZXhpc3RzIHVuZGVyIHRoZSBkaXNwYXRjaCBrZXksIGJ1dCBzdWZmaXhpbmdcbiAgICAgICAgICAgIC8vIGtlZXBzIHRoZSByZXRyeSBlbnF1ZXVlYWJsZSBldmVuIGlmIGEgd29ybGQgcmV0aXJlZCBhXG4gICAgICAgICAgICAvLyBoaXN0b3JpY2FsIGtleSBmb3IgdGhpcyBzdGVwIChzZWUgdGhlIHB1cnBvc2UgZG9jcyBhYm92ZSkuXG4gICAgICAgICAgICBwdXJwb3NlOiAncmV0cnk6MScsXG4gICAgICAgICAgICB3ZmRpYWcsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAob3V0Y29tZS50eXBlID09PSAnZ29uZScpIHtcbiAgICAgICAgICBydW5Hb25lID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICAvLyAnc2tpcHBlZCc6IGEgY29uY3VycmVudCBpbnZvY2F0aW9uIHdvbiB0aGUgbGF6eSBjcmVhdGUtY2xhaW0gYW5kXG4gICAgICAgIC8vIG93bnMgdGhlIGJvZHkuIE1hcmtlZCBleGVjdXRlZCBhYm92ZSBzbyB0aGlzIGludm9jYXRpb24gbmV2ZXJcbiAgICAgICAgLy8gcmUtY2xhaW1zIGl0OyB0aGUgd2lubmVyJ3MgdGVybWluYWwgZXZlbnRzIGFycml2ZSB2aWEgdGhlIGZlZWRcbiAgICAgICAgLy8gKG9yIGRyaXZlIGEgc2VwYXJhdGUgaW52b2NhdGlvbikuXG4gICAgICB9XG4gICAgICB3ZmRpYWcoJ2lubGluZV9zdGVwc19leGVjdXRlZCcsIHtcbiAgICAgICAgaXRlcmF0aW9uLFxuICAgICAgICBjb3VudDogaW5saW5lQ2FuZGlkYXRlcy5sZW5ndGgsXG4gICAgICAgIG91dGNvbWVzOiBvdXRjb21lcy5tYXAoKG8pID0+IG8udHlwZSksXG4gICAgICB9KTtcblxuICAgICAgLy8gRmVlZCB0aGUgaW5saW5lIGJhdGNoJ3MgdGVybWluYWwgZXZlbnRzIGludG8gdGhlIGxpdmUgVk0uIFdoZW5cbiAgICAgIC8vIHRoZSBldmVudHVhbGx5LWNvbnNpc3RlbnQgbGlzdGluZyBoYXMgbm90IHN1cmZhY2VkIHRoZW0geWV0LFxuICAgICAgLy8gZXhpdGluZyBtdXN0IE5PVCBhY2sgc2lsZW50bHk6IHRoZSB0ZXJtaW5hbHMgdGhpcyBpbnZvY2F0aW9uIGp1c3RcbiAgICAgIC8vIGNhdXNlZCBhcmUgZHVyYWJseSB3cml0dGVuIHdpdGggbm8gcXVldWUgbWVzc2FnZSBsZWZ0IHRvIGNvbnN1bWVcbiAgICAgIC8vIHRoZW0gKGlubGluZSBzdGVwcyBoYXZlIG5vbmUpLCBzbyBhbiBhd2FpdGluZ19leHRlcm5hbCBleGl0IHdvdWxkXG4gICAgICAvLyBwYXJrIHRoZSBydW4gJ3J1bm5pbmcnIHdpdGggYWxsIGl0cyBzdGVwcyBjb21wbGV0ZS4gUmFpc2UgdGhlXG4gICAgICAvLyByZXF1ZXVlIHNpZ25hbCBzbyB0aGUgc3VzcGVuZGVkIGV4aXQgc2NoZWR1bGVzIGEgZnJlc2ggaW1tZWRpYXRlXG4gICAgICAvLyBpbnZvY2F0aW9uIHdob3NlIGZyZXNoIHJlYWQgcGlja3MgdGhlIHRlcm1pbmFscyB1cC4gT3V0Y29tZXMgdGhhdFxuICAgICAgLy8gd3JvdGUgbm8gdGVybWluYWwgKCdza2lwcGVkJzogYSBjb25jdXJyZW50IGNsYWltYW50IG93bnMgdGhlXG4gICAgICAvLyBib2R5OyAnZ29uZScsIHJldHJ5L3Rocm90dGxlZDogYSBxdWV1ZSBtZXNzYWdlIGV4aXN0cykgZG9uJ3RcbiAgICAgIC8vIG5lZWQgaXQsIGJ1dCBzaWduYWxpbmcgb24gdGhlbSB0b28gb25seSBjb3N0cyBhIG5vLW9wIGludm9jYXRpb25cbiAgICAgIC8vIGluIGFuIGFscmVhZHktcmFyZSBsYWcgd2luZG93LlxuICAgICAgY29uc3QgbmV3RXZlbnRzID0gYXdhaXQgZmV0Y2hVbnNlZW5FdmVudHMoKTtcbiAgICAgIGlmIChuZXdFdmVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHBlbmRpbmdSZXF1ZXVlU2lnbmFsID0gdHJ1ZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICByZXN1bHQgPSBhd2FpdCBzZXNzaW9uLmNvbnRpbnVlV2l0aEV2ZW50cyhuZXdFdmVudHMpO1xuXG4gICAgICB3ZmRpYWcoJ2lubGluZV9pdGVyYXRpb24nLCB7XG4gICAgICAgIGl0ZXJhdGlvbixcbiAgICAgICAgcGhhc2U6ICdzdGVwcycsXG4gICAgICAgIGZlZEV2ZW50czogbmV3RXZlbnRzLmxlbmd0aCxcbiAgICAgICAgb3V0Y29tZTogcmVzdWx0LmNvbXBsZXRlZFxuICAgICAgICAgID8gJ2NvbXBsZXRlZCdcbiAgICAgICAgICA6IHJlc3VsdC5mYWlsZWRcbiAgICAgICAgICAgID8gJ2ZhaWxlZCdcbiAgICAgICAgICAgIDogJ3N1c3BlbmRlZCcsXG4gICAgICAgIGJ1ZGdldEV4aGF1c3RlZDogYnVkZ2V0LmlzRXhoYXVzdGVkKCksXG4gICAgICB9KTtcbiAgICB9XG4gIH0gZmluYWxseSB7XG4gICAgc2Vzc2lvbi5kaXNwb3NlKCk7XG4gIH1cblxuICBwYXJlbnRTcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAuLi5BdHRyaWJ1dGUuUXVpY2tKU0lubGluZVN0ZXBzKGlubGluZVN0ZXBzRXhlY3V0ZWQpLFxuICB9KTtcblxuICBpZiAocmVzdWx0LmNvbXBsZXRlZCkge1xuICAgIC8vIFdvcmtmbG93IGNvbXBsZXRlZFxuICAgIHJ1bnRpbWVMb2dnZXIuaW5mbygnUXVpY2tKUyBydW50aW1lOiB3b3JrZmxvdyBjb21wbGV0ZWQnLCB7XG4gICAgICB3b3JrZmxvd1J1bklkOiBydW5JZCxcbiAgICB9KTtcbiAgICBwYXJlbnRTcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAgIC4uLkF0dHJpYnV0ZS5RdWlja0pTT3V0Y29tZSgnY29tcGxldGVkJyksXG4gICAgfSk7XG5cbiAgICAvLyBGbHVzaCBsZWZ0b3ZlciBwZW5kaW5nIHNpZGUgZWZmZWN0cyAoYWJvcnQgcmVjb3JkaW5ncywgc3lzdGVtLWhvb2tcbiAgICAvLyBkaXNwb3NhbHMsIGZpcmUtYW5kLWZvcmdldCBhdHRyaWJ1dGUvaG9vayBldmVudHMpIEJFRk9SRSB3cml0aW5nXG4gICAgLy8gcnVuX2NvbXBsZXRlZC4gTWlycm9ycyB0aGUgbm9kZTp2bSBlbmdpbmUncyBkcmFpblBlbmRpbmdRdWV1ZUl0ZW1zLlxuICAgIC8vIERyYWluIGZhaWx1cmVzIGFyZSBzd2FsbG93ZWQ6IHRoZSB3b3JrZmxvdydzIG93biBvdXRjb21lIGlzIHRoZVxuICAgIC8vIHNvdXJjZSBvZiB0cnV0aC5cbiAgICBpZiAocmVzdWx0LmNvbXBsZXRlZC5kcmFpbk9wZXJhdGlvbnM/Lmxlbmd0aCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZGlzcGF0Y2hQZW5kaW5nT3BzKHtcbiAgICAgICAgICB3b3JsZCxcbiAgICAgICAgICBydW5JZCxcbiAgICAgICAgICB3b3JrZmxvd1J1bixcbiAgICAgICAgICBlbmNyeXB0aW9uS2V5LFxuICAgICAgICAgIG5hbWVzcGFjZSxcbiAgICAgICAgICBuZXh0VHJhY2VDYXJyaWVyLFxuICAgICAgICAgIHBlbmRpbmdPcGVyYXRpb25zOiByZXN1bHQuY29tcGxldGVkLmRyYWluT3BlcmF0aW9ucyxcbiAgICAgICAgICB3ZmRpYWcsXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHJ1bnRpbWVMb2dnZXIud2FybignUXVpY2tKUyBydW50aW1lOiB0ZXJtaW5hbCBkcmFpbiBmYWlsZWQnLCB7XG4gICAgICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICAgICAgbWVzc2FnZTogKGVyciBhcyBFcnJvcik/Lm1lc3NhZ2UsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIENyZWF0ZSBydW5fY29tcGxldGVkIGV2ZW50LlxuICAgIC8vIFRoZSBWTSBzZXJpYWxpemVzIHRoZSB3b3JrZmxvdyByZXN1bHQgYXMgZm9ybWF0LXByZWZpeGVkIGRldmFsdWUgYnl0ZXNcbiAgICAvLyAoXCJkZXZsXCIgKyBkZXZhbHVlKSB3aXRoIG5vIGVuY3J5cHRpb24gKHRoZSBWTSBoYXMgbm8gYWNjZXNzIHRvIHRoZVxuICAgIC8vIENyeXB0b0tleSkuIEhvc3Qtc2lkZSBlbmNyeXB0aW9uIGlzIGFwcGxpZWQgaGVyZSBzbyB0aGF0IGBydW5fY29tcGxldGVkYFxuICAgIC8vIGV2ZW50cyBoYXZlIHRoZSBzYW1lIGBlbmNyYC1wcmVmaXhlZCBwYXlsb2FkIHNoYXBlIHRoYXQgdGhlIG5vZGU6dm1cbiAgICAvLyBlbmdpbmUncyBgZGVoeWRyYXRlV29ya2Zsb3dSZXR1cm5WYWx1ZWAgcHJvZHVjZXMuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUocnVuSWQsIHtcbiAgICAgICAgZXZlbnRUeXBlOiAncnVuX2NvbXBsZXRlZCcsXG4gICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgb3V0cHV0OiBhd2FpdCBlbmNyeXB0U2VyaWFsaXplZERhdGEoXG4gICAgICAgICAgICByZXN1bHQuY29tcGxldGVkLnJlc3VsdCxcbiAgICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgICApLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICB3ZmRpYWcoJ2V4aXRfY29tcGxldGVkJywgeyByZXN1bHQ6ICdydW5fY29tcGxldGVkX3dyaXR0ZW4nIH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZXJyKSB8fCBSdW5FeHBpcmVkRXJyb3IuaXMoZXJyKSkge1xuICAgICAgICBydW50aW1lTG9nZ2VyLndhcm4oXG4gICAgICAgICAgJ1dvcmtmbG93IGFscmVhZHkgZmluaXNoZWQsIHNraXBwaW5nIHJ1bl9jb21wbGV0ZWQnLFxuICAgICAgICAgIHsgd29ya2Zsb3dSdW5JZDogcnVuSWQgfVxuICAgICAgICApO1xuICAgICAgICB3ZmRpYWcoJ2V4aXRfY29tcGxldGVkJywgeyByZXN1bHQ6ICdhbHJlYWR5X2ZpbmlzaGVkJyB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgd2ZkaWFnKCdleGl0X2NvbXBsZXRlZF9lcnJvcicsIHtcbiAgICAgICAgZXJyb3JOYW1lOiAoZXJyIGFzIEVycm9yKT8ubmFtZSxcbiAgICAgICAgZXJyb3JNZXNzYWdlOiAoZXJyIGFzIEVycm9yKT8ubWVzc2FnZSxcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgfSBlbHNlIGlmIChyZXN1bHQuc3VzcGVuZGVkKSB7XG4gICAgLy8gV29ya2Zsb3cgc3RpbGwgc3VzcGVuZGVkIGFmdGVyIHRoZSBpbmxpbmUgbG9vcC4gQWxsIGR1cmFibGUgc2lkZVxuICAgIC8vIGVmZmVjdHMgZm9yIHRoZSBmaW5hbCBzdXNwZW5zaW9uIHN0YXRlIHdlcmUgYWxyZWFkeSBkaXNwYXRjaGVkIGJ5XG4gICAgLy8gdGhlIGxvb3A7IHdoYXQgcmVtYWlucyBpcyBkZWNpZGluZyBob3cgdGhlIHJ1biBnZXRzIHJlLWludm9rZWQuXG4gICAgY29uc3QgeyBwZW5kaW5nT3BlcmF0aW9ucyB9ID0gcmVzdWx0LnN1c3BlbmRlZDtcblxuICAgIHJ1bnRpbWVMb2dnZXIuaW5mbygnUXVpY2tKUyBydW50aW1lOiB3b3JrZmxvdyBzdXNwZW5kZWQnLCB7XG4gICAgICB3b3JrZmxvd1J1bklkOiBydW5JZCxcbiAgICAgIGlubGluZVN0ZXBzRXhlY3V0ZWQsXG4gICAgICBwZW5kaW5nU3RlcHM6IHBlbmRpbmdPcGVyYXRpb25zLmZpbHRlcigocCkgPT4gcC50eXBlID09PSAnc3RlcCcpLmxlbmd0aCxcbiAgICAgIHBlbmRpbmdXYWl0czogcGVuZGluZ09wZXJhdGlvbnMuZmlsdGVyKChwKSA9PiBwLnR5cGUgPT09ICd3YWl0JykubGVuZ3RoLFxuICAgICAgcGVuZGluZ09wczogcGVuZGluZ09wZXJhdGlvbnMubWFwKChwKSA9PiAoe1xuICAgICAgICB0eXBlOiBwLnR5cGUsXG4gICAgICAgIGNvcnJlbGF0aW9uSWQ6IHAuY29ycmVsYXRpb25JZCxcbiAgICAgICAgaGFzQ3JlYXRlZEV2ZW50OiBwLmhhc0NyZWF0ZWRFdmVudCxcbiAgICAgICAgLi4uKHAudHlwZSA9PT0gJ3N0ZXAnID8geyBzdGVwSWQ6IChwIGFzIFBlbmRpbmdTdGVwKS5zdGVwSWQgfSA6IHt9KSxcbiAgICAgIH0pKSxcbiAgICB9KTtcblxuICAgIHBhcmVudFNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgLi4uQXR0cmlidXRlLlF1aWNrSlNPdXRjb21lKCdzdXNwZW5kZWQnKSxcbiAgICAgIC4uLkF0dHJpYnV0ZS5RdWlja0pTUGVuZGluZ09wc0NvdW50KHBlbmRpbmdPcGVyYXRpb25zLmxlbmd0aCksXG4gICAgfSk7XG5cbiAgICBpZiAocnVuR29uZSkge1xuICAgICAgLy8gVGhlIHJ1biBubyBsb25nZXIgZXhpc3RzIChleHBpcmVkIC8gZGVsZXRlZCksIHNvIG5vdGhpbmcgdG8gZHJpdmUuXG4gICAgICB3ZmRpYWcoJ2V4aXRfc3VzcGVuZGVkJywgeyBhY3Rpb246ICdydW5fZ29uZScgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gRXhpdCByZXF1ZXVlcyBhcmUgRlJFU0ggbWVzc2FnZXMsIG5ldmVyIGEgYHsgdGltZW91dFNlY29uZHMgfWBcbiAgICAvLyB2aXNpYmlsaXR5LXJlZGVsaXZlcnkgb2YgdGhlIGN1cnJlbnQgbWVzc2FnZS4gUmVkZWxpdmVyaW5nIHRoZVxuICAgIC8vIENVUlJFTlQgbWVzc2FnZSBpcyBhIHRyYXA6IGEgaG9vay1yZXN1bWUgZGVsaXZlcnkgY2Fycmllc1xuICAgIC8vIGBob29rSW5wdXRgLCBhbmQgaXRzIHJlZGVsaXZlcnkgcmUtcnVucyB0aGUgbGF6eS1yZXN1bWUgcmUtZW5zdXJlXG4gICAgLy8gaW4gdGhlIGhhbmRsZXIgcHJvbG9ndWU6IGlmIHRoZSB3b3JrZmxvdyBkaXNwb3NlZCB0aGF0IGhvb2tcbiAgICAvLyBkdXJpbmcgdGhpcyBpbnZvY2F0aW9uIChkaXNwb3NlIOKGkiBzbGVlcCksIGEgd29ybGQgdGhhdCByZWplY3RzXG4gICAgLy8gdGhlIHJlLWVuc3VyZSB3b3VsZCBhY2sgdGhlIG1lc3NhZ2UgYXMgXCJub3RoaW5nIGxlZnQgdG8gcmVzdW1lXCJcbiAgICAvLyBhbmQgdGhlIGNvbnRpbnVhdGlvbiBpdCBjYXJyaWVkIGlzIHNpbGVudGx5IGxvc3QuIEEgZnJlc2ggbWVzc2FnZVxuICAgIC8vIGNhcnJpZXMgb25seSBgcnVuSWRgLCBzbyBpdHMgZGVsaXZlcnkgYWx3YXlzIHJlYWNoZXMgcmVwbGF5IChhbmRcbiAgICAvLyB1bmRlciB0dXJibyBhIHJlc2NoZWR1bGUgd291bGQgcmUtZW5nYWdlIHR1cmJvIGFnYWluc3QgYSBzdGFsZVxuICAgIC8vIHByZWxvYWRlZCBsb2c6IHNlZSB0aGUgcmVpbnZva2UoKSBkb2NzIGluIHJ1bnRpbWUudHMpLlxuICAgIGNvbnN0IHJlcXVldWVJbW1lZGlhdGVseSA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgIGF3YWl0IHF1ZXVlTWVzc2FnZShcbiAgICAgICAgd29ybGQsXG4gICAgICAgIGdldFdvcmtmbG93UXVldWVOYW1lKHdvcmtmbG93UnVuLndvcmtmbG93TmFtZSwgbmFtZXNwYWNlKSxcbiAgICAgICAge1xuICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgIHRyYWNlQ2FycmllcjogYXdhaXQgbmV4dFRyYWNlQ2FycmllcigpLFxuICAgICAgICAgIHJlcXVlc3RlZEF0OiBuZXcgRGF0ZSgpLFxuICAgICAgICB9XG4gICAgICApO1xuICAgIH07XG5cbiAgICBpZiAoYnVkZ2V0LmlzRXhoYXVzdGVkKCkpIHtcbiAgICAgIC8vIFRoZSBsb29wIHN0b3BwZWQgb24gdGhlIHJlcGxheSBidWRnZXQgd2l0aCBwcm9ncmVzcyBzdGlsbFxuICAgICAgLy8gcG9zc2libGUsIHNvIGNvbnRpbnVlIGluIGEgZnJlc2ggaW52b2NhdGlvbi5cbiAgICAgIHdmZGlhZygnZXhpdF9zdXNwZW5kZWQnLCB7IGFjdGlvbjogJ2J1ZGdldF9leGhhdXN0ZWRfcmVxdWV1ZScgfSk7XG4gICAgICBhd2FpdCByZXF1ZXVlSW1tZWRpYXRlbHkoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBFeGl0IHdhaXQgc3dlZXAuIEEgd2FpdCB0aGF0IGVsYXBzZWQgaW4gdGhlIHdpbmRvdyBzaW5jZSB0aGVcbiAgICAvLyBsb29wJ3MgbGFzdCBjaGVjayByZXF1ZXVlcyBpbW1lZGlhdGVseSAoaXRzIHdhaXRfY29tcGxldGVkIGlzXG4gICAgLy8gd3JpdHRlbiBieSB0aGUgbmV4dCBpbnZvY2F0aW9uJ3MgZWxhcHNlZC13YWl0IHBhc3MpOyBwZW5kaW5nIHdhaXRzXG4gICAgLy8gd2hvc2UgY29udGludWF0aW9uIHRoZSBsb29wIGFscmVhZHkgZW5xdWV1ZWQgYXJlIHNraXBwZWQuXG4gICAgbGV0IHNvb25lc3RXYWl0OiB7IHNlY29uZHM6IG51bWJlcjsgY29ycmVsYXRpb25JZDogc3RyaW5nIH0gfCB1bmRlZmluZWQ7XG4gICAgbGV0IGhhc0VsYXBzZWRXYWl0ID0gZmFsc2U7XG4gICAgZm9yIChjb25zdCBvcCBvZiBwZW5kaW5nT3BlcmF0aW9ucykge1xuICAgICAgaWYgKG9wLnR5cGUgIT09ICd3YWl0JykgY29udGludWU7XG4gICAgICBjb25zdCB3YWl0ID0gb3AgYXMgUGVuZGluZ1dhaXQ7XG4gICAgICBjb25zdCByZXN1bWVNcyA9IG5ldyBEYXRlKHdhaXQucmVzdW1lQXQpLmdldFRpbWUoKSAtIERhdGUubm93KCk7XG4gICAgICBpZiAocmVzdW1lTXMgPD0gMCkge1xuICAgICAgICBoYXNFbGFwc2VkV2FpdCA9IHRydWU7XG4gICAgICB9IGVsc2UgaWYgKCFzY2hlZHVsZWRXYWl0Q29udGludWF0aW9ucy5oYXMod2FpdC5jb3JyZWxhdGlvbklkKSkge1xuICAgICAgICBjb25zdCB0aW1lb3V0U2Vjb25kcyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChyZXN1bWVNcyAvIDEwMDApKTtcbiAgICAgICAgaWYgKCFzb29uZXN0V2FpdCB8fCB0aW1lb3V0U2Vjb25kcyA8IHNvb25lc3RXYWl0LnNlY29uZHMpIHtcbiAgICAgICAgICBzb29uZXN0V2FpdCA9IHtcbiAgICAgICAgICAgIHNlY29uZHM6IHRpbWVvdXRTZWNvbmRzLFxuICAgICAgICAgICAgY29ycmVsYXRpb25JZDogd2FpdC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaGFzRWxhcHNlZFdhaXQpIHtcbiAgICAgIHdmZGlhZygnZXhpdF9zdXNwZW5kZWQnLCB7IGFjdGlvbjogJ3dhaXRfZWxhcHNlZF9yZXF1ZXVlJyB9KTtcbiAgICAgIGF3YWl0IHJlcXVldWVJbW1lZGlhdGVseSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChwZW5kaW5nUmVxdWV1ZVNpZ25hbCkge1xuICAgICAgLy8gVGhpcyBpbnZvY2F0aW9uIHdyb3RlIGV2ZW50cyB0aGUgd29ya2Zsb3cgbmVlZHMgdG8gY29uc3VtZVxuICAgICAgLy8gKGF0dHJfc2V0IC8gZ2V0Q29uZmxpY3QtYXdhaXRlZCBob29rX2NyZWF0ZWQgLyBpbmxpbmUgc3RlcFxuICAgICAgLy8gdGVybWluYWxzKSBidXQgdGhlIGV2ZW50dWFsbHktY29uc2lzdGVudCBsaXN0aW5nIG5ldmVyIHJldHVybmVkXG4gICAgICAvLyB0aGVtIGJlZm9yZSB0aGUgbG9vcCBleGl0ZWQuIFdpdGhvdXQgYSByZXF1ZXVlIHRoZSBydW4gd291bGRcbiAgICAgIC8vIHBhcmsgYXdhaXRpbmdfZXh0ZXJuYWwgd2l0aCBpdHMgdW5ibG9ja2luZyBldmVudHMgYWxyZWFkeVxuICAgICAgLy8gZHVyYWJseSB3cml0dGVuIGFuZCBubyBmdXR1cmUgaW52b2NhdGlvbiBjb21pbmcsIHNvIHJlcXVldWVcbiAgICAgIC8vIGltbWVkaWF0ZWx5IHNvIGEgZnJlc2ggcmVhZCBwaWNrcyB0aGVtIHVwLiBJbiB0aGUgY29tbW9uIGNhc2VcbiAgICAgIC8vIHRoZSBsb29wJ3Mgb3duIGZlZWQgb2JzZXJ2ZXMgdGhlIHdyaXRlcyBhbmQgY2xlYXJzIHRoaXMgZmxhZywgc29cbiAgICAgIC8vIHRoaXMgb25seSBmaXJlcyB3aGVuIHRoZSByZWFkIGFjdHVhbGx5IGxhZ2dlZC5cbiAgICAgIHdmZGlhZygnZXhpdF9zdXNwZW5kZWQnLCB7IGFjdGlvbjogJ3VucmVhZF9zZWxmX3dyaXRlX3JlcXVldWUnIH0pO1xuICAgICAgYXdhaXQgcmVxdWV1ZUltbWVkaWF0ZWx5KCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHNvb25lc3RXYWl0KSB7XG4gICAgICAvLyBEZWxheWVkIGNvbnRpbnVhdGlvbiBmb3IgdGhlIHNvb25lc3QgcGVuZGluZyB3YWl0IHRoZSBsb29wIGhhc1xuICAgICAgLy8gbm90IGFscmVhZHkgc2NoZWR1bGVkLiBUaGUgZGlzcGF0Y2ggaGVscGVyIGhhbmRsZXMgZGVsYXlcbiAgICAgIC8vIGNsYW1waW5nIChsb25nIHdhaXRzIGNoYWluIGFjcm9zcyBob3BzKSBhbmQgaWRlbXBvdGVuY3kta2V5XG4gICAgICAvLyBkZWR1cCBvZiByZS1vYnNlcnZhdGlvbnMgb2YgdGhlIHNhbWUgcGVuZGluZyB3YWl0LiBTZWVcbiAgICAgIC8vIHJ1bnRpbWUvd2FpdC1jb250aW51YXRpb24udHMuXG4gICAgICB3ZmRpYWcoJ2V4aXRfc3VzcGVuZGVkJywge1xuICAgICAgICBhY3Rpb246ICdzY2hlZHVsZV93YWl0X3RpbWVvdXQnLFxuICAgICAgICB0aW1lb3V0U2Vjb25kczogc29vbmVzdFdhaXQuc2Vjb25kcyxcbiAgICAgICAgd2FpdENvcnJlbGF0aW9uSWQ6IHNvb25lc3RXYWl0LmNvcnJlbGF0aW9uSWQsXG4gICAgICB9KTtcbiAgICAgIHNjaGVkdWxlZFdhaXRDb250aW51YXRpb25zLmFkZChzb29uZXN0V2FpdC5jb3JyZWxhdGlvbklkKTtcbiAgICAgIGNvbnN0IGF0dGVtcHQgPSBuZXh0V2FpdENvbnRpbnVhdGlvbkF0dGVtcHQoc29vbmVzdFdhaXQuY29ycmVsYXRpb25JZCk7XG4gICAgICBhd2FpdCBxdWV1ZU1lc3NhZ2UoXG4gICAgICAgIHdvcmxkLFxuICAgICAgICBnZXRXb3JrZmxvd1F1ZXVlTmFtZSh3b3JrZmxvd1J1bi53b3JrZmxvd05hbWUsIG5hbWVzcGFjZSksXG4gICAgICAgIHtcbiAgICAgICAgICBydW5JZCxcbiAgICAgICAgICB0cmFjZUNhcnJpZXI6IGF3YWl0IG5leHRUcmFjZUNhcnJpZXIoKSxcbiAgICAgICAgICByZXF1ZXN0ZWRBdDogbmV3IERhdGUoKSxcbiAgICAgICAgICB3YWl0Q29udGludWF0aW9uOiB7XG4gICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzb29uZXN0V2FpdC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgICAgYXR0ZW1wdCxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICBnZXRXYWl0Q29udGludWF0aW9uRGlzcGF0Y2goXG4gICAgICAgICAgc29vbmVzdFdhaXQuc2Vjb25kcyxcbiAgICAgICAgICBzb29uZXN0V2FpdC5jb3JyZWxhdGlvbklkLFxuICAgICAgICAgIERhdGUubm93KCksXG4gICAgICAgICAgYXR0ZW1wdFxuICAgICAgICApXG4gICAgICApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHdmZGlhZygnZXhpdF9zdXNwZW5kZWQnLCB7XG4gICAgICBhY3Rpb246ICdhd2FpdGluZ19leHRlcm5hbCcsXG4gICAgICBwZW5kaW5nT3BzQ291bnQ6IHBlbmRpbmdPcGVyYXRpb25zLmxlbmd0aCxcbiAgICB9KTtcbiAgfSBlbHNlIGlmIChyZXN1bHQuZmFpbGVkKSB7XG4gICAgLy8gV29ya2Zsb3cgZmFpbGVkLCBzbyByZW1hcCBzdGFjayB0cmFjZSB1c2luZyBpbmxpbmUgc291cmNlIG1hcHMuXG4gICAgLy8gRnJhbWVzIGNhcnJ5IHRoZSBydW4ncyB3b3JrZmxvd0lkIGFzIHRoZWlyIGZpbGVuYW1lIG9uIHRoZSBmcmVzaFxuICAgIC8vIHBhdGgsIGJ1dCB0aGUgd29ya2Zsb3ctaW5kZXBlbmRlbnQgQkFTRUxJTkVfQlVORExFX0ZJTEVOQU1FIG9uIHRoZVxuICAgIC8vIHNuYXBzaG90IHBhdGggKHRoZSBuYW1lIGlzIGJha2VkIGludG8gdGhlIHNoYXJlZCBiYXNlbGluZSdzXG4gICAgLy8gY29tcGlsZWQgY29kZSBhdCBoeWRyYXRlKSwgc28gcmVtYXAgYWdhaW5zdCBib3RoLiByZW1hcEVycm9yU3RhY2tcbiAgICAvLyBlYXJseS1leGl0cyBvbiBhIGNoZWFwIGluY2x1ZGVzKCkgd2hlbiBhIGZpbGVuYW1lIGhhcyBubyBmcmFtZXMuXG4gICAgbGV0IGVycm9yU3RhY2sgPSByZXN1bHQuZmFpbGVkLnN0YWNrO1xuICAgIGlmIChlcnJvclN0YWNrKSB7XG4gICAgICBjb25zdCBwYXJzZWROYW1lID0gcGFyc2VXb3JrZmxvd05hbWUod29ya2Zsb3dOYW1lKTtcbiAgICAgIGNvbnN0IGZpbGVuYW1lID0gcGFyc2VkTmFtZT8ubW9kdWxlU3BlY2lmaWVyIHx8IHdvcmtmbG93TmFtZTtcbiAgICAgIGVycm9yU3RhY2sgPSByZW1hcEVycm9yU3RhY2soZXJyb3JTdGFjaywgZmlsZW5hbWUsIHdvcmtmbG93Q29kZSk7XG4gICAgICBlcnJvclN0YWNrID0gcmVtYXBFcnJvclN0YWNrKFxuICAgICAgICBlcnJvclN0YWNrLFxuICAgICAgICBCQVNFTElORV9CVU5ETEVfRklMRU5BTUUsXG4gICAgICAgIHdvcmtmbG93Q29kZVxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDbGFzc2lmeSB0aGUgZXJyb3Igc28gY29uc3VtZXJzIChgcnVuLnJldHVyblZhbHVlYCwgb2JzZXJ2YWJpbGl0eSlcbiAgICAvLyBnZXQgYFVTRVJfRVJST1JgIC8gYFJVTlRJTUVfRVJST1JgIG9uIGBlcnJvci5jYXVzZS5jb2RlYCwgbWF0Y2hpbmdcbiAgICAvLyB3aGF0IHRoZSBub2RlOnZtIGVuZ2luZSBhbHJlYWR5IGRvZXMgaW4gcnVudGltZS50cy5cbiAgICAvL1xuICAgIC8vIFRoZSBWTSBzZXJpYWxpemVzIGVycm9ycyBhcyBgeyBuYW1lLCBtZXNzYWdlLCBzdGFjayB9YCwgc28gd2VcbiAgICAvLyByZWNvbnN0cnVjdCBhIGhvc3Qtc2lkZSBFcnJvciBvZiB0aGUgY29ycmVjdCBjbGFzcyBiYXNlZCBvbiB0aGVcbiAgICAvLyBWTS1zaWRlIGBuYW1lYDogc3BlY2lmaWMgV29ya2Zsb3dSdW50aW1lRXJyb3Igc3ViY2xhc3NlcyBuZWVkXG4gICAgLy8gdG8gYmUgcHJlc2VydmVkIHNvIGNsYXNzaWZ5UnVuRXJyb3IoKSB0YWdzIHRoZW0gYXMgUlVOVElNRV9FUlJPUi5cbiAgICBjb25zdCByZWNvbnN0cnVjdGVkOiBFcnJvciA9XG4gICAgICByZXN1bHQuZmFpbGVkLm5hbWUgPT09ICdXb3JrZmxvd05vdFJlZ2lzdGVyZWRFcnJvcidcbiAgICAgICAgPyBuZXcgV29ya2Zsb3dOb3RSZWdpc3RlcmVkRXJyb3Iod29ya2Zsb3dOYW1lKVxuICAgICAgICA6IHJlc3VsdC5mYWlsZWQubmFtZSA9PT0gJ0Vycm9yJ1xuICAgICAgICAgID8gbmV3IEVycm9yKHJlc3VsdC5mYWlsZWQubWVzc2FnZSlcbiAgICAgICAgICA6IE9iamVjdC5hc3NpZ24obmV3IEVycm9yKHJlc3VsdC5mYWlsZWQubWVzc2FnZSksIHtcbiAgICAgICAgICAgICAgbmFtZTogcmVzdWx0LmZhaWxlZC5uYW1lLFxuICAgICAgICAgICAgfSk7XG4gICAgY29uc3QgZXJyb3JDb2RlID0gY2xhc3NpZnlSdW5FcnJvcihyZWNvbnN0cnVjdGVkKTtcblxuICAgIHJ1bnRpbWVMb2dnZXIuZXJyb3IoJ1F1aWNrSlMgcnVudGltZTogd29ya2Zsb3cgZmFpbGVkJywge1xuICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICBlcnJvck5hbWU6IHJlc3VsdC5mYWlsZWQubmFtZSxcbiAgICAgIGVycm9yTWVzc2FnZTogcmVzdWx0LmZhaWxlZC5tZXNzYWdlLFxuICAgICAgZXJyb3JTdGFjayxcbiAgICAgIGVycm9yQ29kZSxcbiAgICB9KTtcbiAgICBwYXJlbnRTcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAgIC4uLkF0dHJpYnV0ZS5RdWlja0pTT3V0Y29tZSgnZmFpbGVkJyksXG4gICAgfSk7XG5cbiAgICAvLyBGbHVzaCBsZWZ0b3ZlciBwZW5kaW5nIHNpZGUgZWZmZWN0cyBiZWZvcmUgd3JpdGluZyBydW5fZmFpbGVkLFxuICAgIC8vIHNhbWUgZHJhaW4gc2VtYW50aWNzIGFzIHRoZSBjb21wbGV0ZWQgYnJhbmNoLlxuICAgIGlmIChyZXN1bHQuZmFpbGVkLmRyYWluT3BlcmF0aW9ucz8ubGVuZ3RoKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBkaXNwYXRjaFBlbmRpbmdPcHMoe1xuICAgICAgICAgIHdvcmxkLFxuICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgIHdvcmtmbG93UnVuLFxuICAgICAgICAgIGVuY3J5cHRpb25LZXksXG4gICAgICAgICAgbmFtZXNwYWNlLFxuICAgICAgICAgIG5leHRUcmFjZUNhcnJpZXIsXG4gICAgICAgICAgcGVuZGluZ09wZXJhdGlvbnM6IHJlc3VsdC5mYWlsZWQuZHJhaW5PcGVyYXRpb25zLFxuICAgICAgICAgIHdmZGlhZyxcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgcnVudGltZUxvZ2dlci53YXJuKCdRdWlja0pTIHJ1bnRpbWU6IHRlcm1pbmFsIGRyYWluIGZhaWxlZCcsIHtcbiAgICAgICAgICB3b3JrZmxvd1J1bklkOiBydW5JZCxcbiAgICAgICAgICBtZXNzYWdlOiAoZXJyIGFzIEVycm9yKT8ubWVzc2FnZSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIHJ1bl9mYWlsZWQgZXZlbnQuIFNlcmlhbGl6ZSB0aGUgZXJyb3IgdGhyb3VnaCB0aGVcbiAgICAvLyBmaXJzdC1jbGFzcyBkZWh5ZHJhdGlvbiBwaXBlbGluZSBzbyBjb25zdW1lcnMgKENMSSwgb2JzZXJ2YWJpbGl0eSxcbiAgICAvLyBydW4ucmV0dXJuVmFsdWUpIGdldCB0aGUgc2FtZSBoeWRyYXRlZCB2YWx1ZSBzaGFwZSBhcyB0aGUgbm9kZTp2bVxuICAgIC8vIGVuZ2luZSBlbWl0cy4gVHdvIHBhdGhzOlxuICAgIC8vICAgKiBNb2Rlcm4gKHZhbHVlQnl0ZXMgcHJlc2VudCk6IHRoZSBWTS1zaWRlIHJlamVjdGlvbiBoYW5kbGVyXG4gICAgLy8gICAgIHNlcmlhbGl6ZWQgdGhlIG9yaWdpbmFsIHRocm93biB2YWx1ZSAoRXJyb3Igc3ViY2xhc3Mgd2l0aFxuICAgIC8vICAgICBjYXVzZSBjaGFpbiwgcGxhaW4gb2JqZWN0LCBwcmltaXRpdmUsIGV0Yy4pIHVzaW5nIHRoZSBWTSdzXG4gICAgLy8gICAgIHdvcmtmbG93LXNlcmlhbGl6ZS4gUGFzcyB0aG9zZSBieXRlcyB0aHJvdWdoIGRpcmVjdGx5IHNvXG4gICAgLy8gICAgIHR5cGUgaWRlbnRpdHksIGNhdXNlIGNoYWlucywgYW5kIG5vbi1FcnJvciB0aHJvd3Mgc3Vydml2ZS5cbiAgICAvLyAgICAgQXBwbHkgZW5jcnlwdGlvbiBpZiBjb25maWd1cmVkICh0aGUgVk0nc1xuICAgIC8vICAgICBzZXJpYWxpemVyIGRvZXNuJ3QgaGF2ZSBhY2Nlc3MgdG8gdGhlIGVuY3J5cHRpb24ga2V5KS5cbiAgICAvLyAgICogTGVnYWN5IGZhbGxiYWNrOiByZWNvbnN0cnVjdCBhbiBFcnJvciBmcm9tIHRoZSBob3N0LXZpc2libGVcbiAgICAvLyAgICAge25hbWUsIG1lc3NhZ2UsIHN0YWNrfSBmaWVsZHMgYW5kIHJ1biBpdCB0aHJvdWdoXG4gICAgLy8gICAgIGBkZWh5ZHJhdGVSdW5FcnJvcmAuIFVzZWQgd2hlbiB2YWx1ZUJ5dGVzIGlzIGFic2VudCAoZS5nLlxuICAgIC8vICAgICBleHRyYWN0RXJyb3IgcHNldWRvLWZhaWx1cmVzIGZyb20gVk0gYm9vdHN0cmFwKS5cbiAgICBsZXQgZGVoeWRyYXRlZEVycm9yOiBVaW50OEFycmF5O1xuICAgIGlmIChyZXN1bHQuZmFpbGVkLnZhbHVlQnl0ZXMpIHtcbiAgICAgIC8vIEh5ZHJhdGUgdGhlIFZNLXNpZGUgYnl0ZXMsIHJlbWFwIHRoZSBlcnJvciBzdGFjayB3aXRoIHRoZVxuICAgICAgLy8gaG9zdC1zaWRlIHNvdXJjZSBtYXAgKHRoZSBWTSBjYW4ndCBkbyB0aGlzOiBpdCBsYWNrcyBib3RoIHRoZVxuICAgICAgLy8gc291cmNlIG1hcCBhbmQgYHJlbWFwRXJyb3JTdGFja2ApLCBhbmQgcmUtZGVoeWRyYXRlLiBUaGlzXG4gICAgICAvLyBwcmVzZXJ2ZXMgdGhlIG9yaWdpbmFsIHZhbHVlJ3MgdHlwZSBpZGVudGl0eSAvIGNhdXNlIGNoYWluXG4gICAgICAvLyB3aGlsZSBmaXhpbmcgdXAgZnJhbWVzIHRvIHBvaW50IGF0IHRoZSB1c2VyJ3Mgc291cmNlIGZpbGVzLlxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgaHlkcmF0ZWQgPSBhd2FpdCBoeWRyYXRlUnVuRXJyb3IoXG4gICAgICAgICAgcmVzdWx0LmZhaWxlZC52YWx1ZUJ5dGVzLFxuICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgIHVuZGVmaW5lZCAvLyBWTSBieXRlcyBhcmUgdW5lbmNyeXB0ZWRcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGh5ZHJhdGVkICYmXG4gICAgICAgICAgdHlwZW9mIGh5ZHJhdGVkID09PSAnb2JqZWN0JyAmJlxuICAgICAgICAgICdzdGFjaycgaW4gKGh5ZHJhdGVkIGFzIG9iamVjdCkgJiZcbiAgICAgICAgICB0eXBlb2YgKGh5ZHJhdGVkIGFzIHsgc3RhY2s/OiB1bmtub3duIH0pLnN0YWNrID09PSAnc3RyaW5nJ1xuICAgICAgICApIHtcbiAgICAgICAgICBjb25zdCBwYXJzZWROYW1lID0gcGFyc2VXb3JrZmxvd05hbWUod29ya2Zsb3dOYW1lKTtcbiAgICAgICAgICBjb25zdCBmaWxlbmFtZSA9IHBhcnNlZE5hbWU/Lm1vZHVsZVNwZWNpZmllciB8fCB3b3JrZmxvd05hbWU7XG4gICAgICAgICAgLy8gQm90aCBmaWxlbmFtZSBzcGFjZXMuIFNlZSB0aGUgZmFpbGVkLWJyYW5jaCBjb21tZW50IGFib3ZlLlxuICAgICAgICAgIChoeWRyYXRlZCBhcyB7IHN0YWNrPzogc3RyaW5nIH0pLnN0YWNrID0gcmVtYXBFcnJvclN0YWNrKFxuICAgICAgICAgICAgcmVtYXBFcnJvclN0YWNrKFxuICAgICAgICAgICAgICAoaHlkcmF0ZWQgYXMgeyBzdGFjazogc3RyaW5nIH0pLnN0YWNrLFxuICAgICAgICAgICAgICBmaWxlbmFtZSxcbiAgICAgICAgICAgICAgd29ya2Zsb3dDb2RlXG4gICAgICAgICAgICApLFxuICAgICAgICAgICAgQkFTRUxJTkVfQlVORExFX0ZJTEVOQU1FLFxuICAgICAgICAgICAgd29ya2Zsb3dDb2RlXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBXYWxrIHRoZSBjYXVzZSBjaGFpbiBhbmQgcmVtYXAgbmVzdGVkIHN0YWNrcyB0b28uXG4gICAgICAgIGNvbnN0IHNlZW4gPSBuZXcgV2Vha1NldDxvYmplY3Q+KCk7XG4gICAgICAgIGxldCBub2RlID0gKGh5ZHJhdGVkIGFzIHsgY2F1c2U/OiB1bmtub3duIH0pPy5jYXVzZTtcbiAgICAgICAgd2hpbGUgKG5vZGUgJiYgdHlwZW9mIG5vZGUgPT09ICdvYmplY3QnICYmICFzZWVuLmhhcyhub2RlIGFzIG9iamVjdCkpIHtcbiAgICAgICAgICBzZWVuLmFkZChub2RlIGFzIG9iamVjdCk7XG4gICAgICAgICAgY29uc3Qgbm9kZVN0YWNrID0gKG5vZGUgYXMgeyBzdGFjaz86IHVua25vd24gfSkuc3RhY2s7XG4gICAgICAgICAgaWYgKHR5cGVvZiBub2RlU3RhY2sgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBjb25zdCBwYXJzZWROYW1lID0gcGFyc2VXb3JrZmxvd05hbWUod29ya2Zsb3dOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGZpbGVuYW1lID0gcGFyc2VkTmFtZT8ubW9kdWxlU3BlY2lmaWVyIHx8IHdvcmtmbG93TmFtZTtcbiAgICAgICAgICAgIC8vIEJvdGggZmlsZW5hbWUgc3BhY2VzLiBTZWUgdGhlIGZhaWxlZC1icmFuY2ggY29tbWVudCBhYm92ZS5cbiAgICAgICAgICAgIChub2RlIGFzIHsgc3RhY2s/OiBzdHJpbmcgfSkuc3RhY2sgPSByZW1hcEVycm9yU3RhY2soXG4gICAgICAgICAgICAgIHJlbWFwRXJyb3JTdGFjayhub2RlU3RhY2ssIGZpbGVuYW1lLCB3b3JrZmxvd0NvZGUpLFxuICAgICAgICAgICAgICBCQVNFTElORV9CVU5ETEVfRklMRU5BTUUsXG4gICAgICAgICAgICAgIHdvcmtmbG93Q29kZVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbm9kZSA9IChub2RlIGFzIHsgY2F1c2U/OiB1bmtub3duIH0pLmNhdXNlO1xuICAgICAgICB9XG4gICAgICAgIGRlaHlkcmF0ZWRFcnJvciA9IGF3YWl0IGRlaHlkcmF0ZVJ1bkVycm9yKFxuICAgICAgICAgIGh5ZHJhdGVkLFxuICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKHJlaHlkcmF0ZUVycikge1xuICAgICAgICAvLyBJZiBoeWRyYXRpb24gLyByZS1kZWh5ZHJhdGlvbiBmYWlscyBmb3IgYW55IHJlYXNvbiwgZmFsbFxuICAgICAgICAvLyBiYWNrIHRvIHBhc3NpbmcgdGhyb3VnaCB0aGUgb3JpZ2luYWwgVk0gYnl0ZXMgKGFwcGx5aW5nXG4gICAgICAgIC8vIGVuY3J5cHRpb24gaWYgY29uZmlndXJlZCkuIEJldHRlciB0byBsb3NlIHNvdXJjZS1tYXBwZWRcbiAgICAgICAgLy8gZnJhbWVzIHRoYW4gdG8gbG9zZSB0aGUgZXJyb3IgZW50aXJlbHkuXG4gICAgICAgIHJ1bnRpbWVMb2dnZXIud2FybihcbiAgICAgICAgICAnUXVpY2tKUyBydW50aW1lOiBmYWlsZWQgdG8gcmVtYXAgd29ya2Zsb3cgZXJyb3Igc3RhY2ssIHBhc3NpbmcgVk0gYnl0ZXMgdGhyb3VnaCcsXG4gICAgICAgICAge1xuICAgICAgICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICAgICAgICBtZXNzYWdlOiAocmVoeWRyYXRlRXJyIGFzIEVycm9yKT8ubWVzc2FnZSxcbiAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgICAgIGRlaHlkcmF0ZWRFcnJvciA9IChhd2FpdCBtYXliZUVuY3J5cHQoXG4gICAgICAgICAgcmVzdWx0LmZhaWxlZC52YWx1ZUJ5dGVzLFxuICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgKSkgYXMgVWludDhBcnJheTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGVycm9yU3RhY2spIHtcbiAgICAgICAgcmVjb25zdHJ1Y3RlZC5zdGFjayA9IGVycm9yU3RhY2s7XG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICBkZWh5ZHJhdGVkRXJyb3IgPSBhd2FpdCBkZWh5ZHJhdGVSdW5FcnJvcihcbiAgICAgICAgICByZWNvbnN0cnVjdGVkLFxuICAgICAgICAgIHJ1bklkLFxuICAgICAgICAgIGVuY3J5cHRpb25LZXlcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKHNlckVycikge1xuICAgICAgICAvLyBGYWxsIGJhY2sgdG8gYSBtaW5pbWFsIHBheWxvYWQgc28gdGhlIHJ1biBzdGlsbCB0ZXJtaW5hdGVzXG4gICAgICAgIC8vIGV2ZW4gd2hlbiB0aGUgZXJyb3IgaXRzZWxmIGNvbnRhaW5zIHVuc2VyaWFsaXphYmxlIHZhbHVlcy5cbiAgICAgICAgcnVudGltZUxvZ2dlci53YXJuKFxuICAgICAgICAgICdRdWlja0pTIHJ1bnRpbWU6IGZhaWxlZCB0byBkZWh5ZHJhdGUgcnVuIGVycm9yLCBmYWxsaW5nIGJhY2sgdG8gYmFyZSBFcnJvcicsXG4gICAgICAgICAgeyB3b3JrZmxvd1J1bklkOiBydW5JZCwgbWVzc2FnZTogKHNlckVyciBhcyBFcnJvcik/Lm1lc3NhZ2UgfVxuICAgICAgICApO1xuICAgICAgICBkZWh5ZHJhdGVkRXJyb3IgPSBhd2FpdCBkZWh5ZHJhdGVSdW5FcnJvcihcbiAgICAgICAgICBPYmplY3QuYXNzaWduKG5ldyBFcnJvcihyZXN1bHQuZmFpbGVkLm1lc3NhZ2UpLCB7XG4gICAgICAgICAgICBuYW1lOiByZXN1bHQuZmFpbGVkLm5hbWUsXG4gICAgICAgICAgfSksXG4gICAgICAgICAgcnVuSWQsXG4gICAgICAgICAgZW5jcnlwdGlvbktleVxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cbiAgICB0cnkge1xuICAgICAgYXdhaXQgd29ybGQuZXZlbnRzLmNyZWF0ZShydW5JZCwge1xuICAgICAgICBldmVudFR5cGU6ICdydW5fZmFpbGVkJyxcbiAgICAgICAgc3BlY1ZlcnNpb246IFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICBlcnJvcjogZGVoeWRyYXRlZEVycm9yLFxuICAgICAgICAgIGVycm9yQ29kZSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZXJyKSB8fCBSdW5FeHBpcmVkRXJyb3IuaXMoZXJyKSkge1xuICAgICAgICBydW50aW1lTG9nZ2VyLndhcm4oJ1dvcmtmbG93IGFscmVhZHkgZmluaXNoZWQsIHNraXBwaW5nIHJ1bl9mYWlsZWQnLCB7XG4gICAgICAgICAgd29ya2Zsb3dSdW5JZDogcnVuSWQsXG4gICAgICAgIH0pO1xuICAgICAgICB3ZmRpYWcoJ2V4aXRfZmFpbGVkJywgeyByZXN1bHQ6ICdhbHJlYWR5X2ZpbmlzaGVkJyB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgd2ZkaWFnKCdleGl0X2ZhaWxlZF9lcnJvcicsIHtcbiAgICAgICAgZXJyb3JOYW1lOiAoZXJyIGFzIEVycm9yKT8ubmFtZSxcbiAgICAgICAgZXJyb3JNZXNzYWdlOiAoZXJyIGFzIEVycm9yKT8ubWVzc2FnZSxcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgICB3ZmRpYWcoJ2V4aXRfZmFpbGVkJywgeyByZXN1bHQ6ICdydW5fZmFpbGVkX3dyaXR0ZW4nIH0pO1xuICB9XG59XG4iXX0=
|