@workflow/core 5.0.0-beta.4 → 5.0.0-beta.41
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 +7 -0
- package/dist/attribute-changes.d.ts.map +1 -0
- package/dist/attribute-changes.js +25 -0
- package/dist/capabilities.d.ts +33 -1
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +71 -4
- package/dist/capture-stack.d.ts +16 -0
- package/dist/capture-stack.d.ts.map +1 -0
- package/dist/capture-stack.js +21 -0
- 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 +26 -3
- package/dist/classify-error.d.ts.map +1 -1
- package/dist/classify-error.js +109 -6
- package/dist/context-errors.d.ts +27 -0
- package/dist/context-errors.d.ts.map +1 -0
- package/dist/context-errors.js +101 -0
- package/dist/context-violation-error.d.ts +97 -0
- package/dist/context-violation-error.d.ts.map +1 -0
- package/dist/context-violation-error.js +149 -0
- package/dist/create-hook.d.ts +62 -4
- package/dist/create-hook.d.ts.map +1 -1
- package/dist/create-hook.js +4 -3
- package/dist/define-hook.d.ts.map +1 -1
- package/dist/define-hook.js +20 -5
- package/dist/describe-error.d.ts +70 -0
- package/dist/describe-error.d.ts.map +1 -0
- package/dist/describe-error.js +189 -0
- package/dist/encryption.d.ts +37 -3
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +97 -31
- package/dist/events-consumer.d.ts +147 -1
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +444 -43
- package/dist/flushable-stream.d.ts +65 -10
- package/dist/flushable-stream.d.ts.map +1 -1
- package/dist/flushable-stream.js +131 -18
- package/dist/global.d.ts +37 -1
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +21 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/log-format.d.ts +25 -0
- package/dist/log-format.d.ts.map +1 -0
- package/dist/log-format.js +250 -0
- package/dist/logger.d.ts +29 -30
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +82 -32
- package/dist/private.d.ts +246 -8
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +345 -9
- package/dist/replay-payload-cache.d.ts +68 -0
- package/dist/replay-payload-cache.d.ts.map +1 -0
- package/dist/replay-payload-cache.js +160 -0
- package/dist/runtime/compute-instance.d.ts +12 -0
- package/dist/runtime/compute-instance.d.ts.map +1 -0
- package/dist/runtime/compute-instance.js +13 -0
- package/dist/runtime/constants.d.ts +251 -0
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +425 -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 +25 -0
- package/dist/runtime/get-port-lazy.d.ts.map +1 -0
- package/dist/runtime/get-port-lazy.js +92 -0
- package/dist/runtime/get-world-lazy.d.ts +23 -0
- package/dist/runtime/get-world-lazy.d.ts.map +1 -0
- package/dist/runtime/get-world-lazy.js +46 -0
- package/dist/runtime/helpers.d.ts +327 -10
- package/dist/runtime/helpers.d.ts.map +1 -1
- package/dist/runtime/helpers.js +586 -40
- package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
- package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
- package/dist/runtime/quickjs-assets.generated.js +30 -0
- package/dist/runtime/quickjs-entrypoint.d.ts +124 -0
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
- package/dist/runtime/quickjs-entrypoint.js +1437 -0
- package/dist/runtime/quickjs-runtime.d.ts +228 -0
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
- package/dist/runtime/quickjs-runtime.js +2372 -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 +2098 -0
- package/dist/runtime/replay-budget.d.ts +89 -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 +27 -4
- package/dist/runtime/resume-hook.d.ts.map +1 -1
- package/dist/runtime/resume-hook.js +452 -76
- 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 +8 -2
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +62 -15
- package/dist/runtime/runs.d.ts +54 -3
- package/dist/runtime/runs.d.ts.map +1 -1
- package/dist/runtime/runs.js +119 -13
- package/dist/runtime/start.d.ts +70 -1
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +313 -60
- package/dist/runtime/step-executor.d.ts +177 -0
- package/dist/runtime/step-executor.d.ts.map +1 -0
- package/dist/runtime/step-executor.js +946 -0
- package/dist/runtime/step-latency.d.ts +197 -0
- package/dist/runtime/step-latency.d.ts.map +1 -0
- package/dist/runtime/step-latency.js +207 -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 +69 -0
- package/dist/runtime/suspension-handler.d.ts +109 -7
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +486 -140
- 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 +84 -0
- package/dist/runtime/wait-continuation.d.ts.map +1 -0
- package/dist/runtime/wait-continuation.js +105 -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 +20 -0
- package/dist/runtime/world-compatibility.d.ts.map +1 -0
- package/dist/runtime/world-compatibility.js +32 -0
- package/dist/runtime/world-init.d.ts +50 -0
- package/dist/runtime/world-init.d.ts.map +1 -0
- package/dist/runtime/world-init.js +50 -0
- package/dist/runtime/world.d.ts +14 -2
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +101 -26
- package/dist/runtime.d.ts +17 -12
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3135 -328
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +1 -1
- 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 +17 -0
- package/dist/serialization/client.d.ts.map +1 -0
- package/dist/serialization/client.js +48 -0
- 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 +14 -0
- package/dist/serialization/codec-devalue.d.ts.map +1 -0
- package/dist/serialization/codec-devalue.js +105 -0
- package/dist/serialization/codec.d.ts +125 -0
- package/dist/serialization/codec.d.ts.map +1 -0
- package/dist/serialization/codec.js +17 -0
- package/dist/serialization/compression.d.ts +104 -0
- package/dist/serialization/compression.d.ts.map +1 -0
- package/dist/serialization/compression.js +260 -0
- package/dist/serialization/encryption.d.ts +134 -0
- package/dist/serialization/encryption.d.ts.map +1 -0
- package/dist/serialization/encryption.js +186 -0
- package/dist/serialization/errors.d.ts +34 -0
- package/dist/serialization/errors.d.ts.map +1 -0
- package/dist/serialization/errors.js +59 -0
- package/dist/serialization/format.d.ts +60 -0
- package/dist/serialization/format.d.ts.map +1 -0
- package/dist/serialization/format.js +97 -0
- package/dist/serialization/hardened.d.ts +155 -0
- package/dist/serialization/hardened.d.ts.map +1 -0
- package/dist/serialization/hardened.js +523 -0
- package/dist/serialization/index.d.ts +20 -0
- package/dist/serialization/index.d.ts.map +1 -0
- package/dist/serialization/index.js +22 -0
- package/dist/serialization/reducers/class-vm.d.ts +20 -0
- package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/class-vm.js +77 -0
- package/dist/serialization/reducers/class.d.ts +11 -0
- package/dist/serialization/reducers/class.d.ts.map +1 -0
- package/dist/serialization/reducers/class.js +73 -0
- package/dist/serialization/reducers/common-vm.d.ts +15 -0
- package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/common-vm.js +579 -0
- package/dist/serialization/reducers/common.d.ts +16 -0
- package/dist/serialization/reducers/common.d.ts.map +1 -0
- package/dist/serialization/reducers/common.js +478 -0
- 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 +35 -0
- package/dist/serialization/reducers/step-function.d.ts.map +1 -0
- package/dist/serialization/reducers/step-function.js +104 -0
- package/dist/serialization/step.d.ts +17 -0
- package/dist/serialization/step.d.ts.map +1 -0
- package/dist/serialization/step.js +48 -0
- package/dist/serialization/types.d.ts +273 -0
- package/dist/serialization/types.d.ts.map +1 -0
- package/dist/serialization/types.js +35 -0
- 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 +71 -0
- package/dist/serialization/workflow.d.ts +29 -0
- package/dist/serialization/workflow.d.ts.map +1 -0
- package/dist/serialization/workflow.js +54 -0
- package/dist/serialization-format.d.ts +60 -4
- package/dist/serialization-format.d.ts.map +1 -1
- package/dist/serialization-format.js +276 -56
- package/dist/serialization.d.ts +374 -221
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +2300 -783
- 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/sleep.d.ts.map +1 -1
- package/dist/sleep.js +3 -2
- package/dist/source-map.d.ts +25 -0
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +148 -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/get-closure-vars.d.ts.map +1 -1
- package/dist/step/get-closure-vars.js +3 -2
- package/dist/step/get-step-metadata.d.ts.map +1 -1
- package/dist/step/get-step-metadata.js +3 -2
- package/dist/step/get-workflow-metadata.d.ts.map +1 -1
- package/dist/step/get-workflow-metadata.js +3 -2
- package/dist/step/writable-stream.d.ts.map +1 -1
- package/dist/step/writable-stream.js +71 -7
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +225 -29
- package/dist/symbols.d.ts +54 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +55 -1
- package/dist/telemetry/semantic-conventions.d.ts +265 -2
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +190 -1
- package/dist/telemetry.d.ts +73 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +153 -16
- 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 +16 -6
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +25 -16
- 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 +85 -14
- package/dist/vm/script-cache.d.ts +28 -0
- package/dist/vm/script-cache.d.ts.map +1 -0
- package/dist/vm/script-cache.js +140 -0
- package/dist/workflow/abort-controller.d.ts +65 -0
- package/dist/workflow/abort-controller.d.ts.map +1 -0
- package/dist/workflow/abort-controller.js +312 -0
- 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 +48 -0
- package/dist/workflow/create-hook.d.ts.map +1 -1
- package/dist/workflow/create-hook.js +25 -3
- package/dist/workflow/define-hook.d.ts +1 -1
- package/dist/workflow/define-hook.d.ts.map +1 -1
- package/dist/workflow/define-hook.js +8 -4
- package/dist/workflow/get-workflow-metadata.d.ts.map +1 -1
- package/dist/workflow/get-workflow-metadata.js +14 -3
- package/dist/workflow/hook.d.ts.map +1 -1
- package/dist/workflow/hook.js +302 -36
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +5 -3
- 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 +57 -8
- package/dist/workflow/world-init-stub.d.ts +15 -0
- package/dist/workflow/world-init-stub.d.ts.map +1 -0
- package/dist/workflow/world-init-stub.js +15 -0
- package/dist/workflow.d.ts +79 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +831 -553
- package/docs/api-reference/create-hook.mdx +79 -0
- package/docs/api-reference/create-webhook.mdx +1 -0
- package/docs/api-reference/define-hook.mdx +26 -24
- package/docs/api-reference/fatal-error.mdx +29 -7
- package/docs/api-reference/fetch.mdx +8 -4
- package/docs/api-reference/index.mdx +3 -0
- package/docs/api-reference/set-attributes.mdx +61 -0
- package/docs/api-reference/sleep.mdx +1 -1
- package/docs/foundations/cancellation.mdx +459 -0
- package/docs/foundations/errors-and-retries.mdx +7 -3
- package/docs/foundations/hooks.mdx +29 -0
- package/docs/foundations/idempotency.mdx +243 -11
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +3 -1
- package/docs/foundations/serialization.mdx +77 -41
- package/docs/foundations/starting-workflows.mdx +79 -2
- package/docs/foundations/streaming.mdx +14 -23
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/how-it-works/cancellation.mdx +287 -0
- package/docs/how-it-works/code-transform.mdx +21 -17
- package/docs/how-it-works/encryption.mdx +5 -5
- package/docs/how-it-works/event-sourcing.mdx +17 -9
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/meta.json +2 -1
- package/package.json +31 -13
- 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 -678
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
import { types } from 'node:util';
|
|
2
|
+
import { EntityConflictError, FatalError, RetryableError, RunExpiredError, ThrottleError, TooEarlyError, WorkflowRuntimeError, } from '@workflow/errors';
|
|
3
|
+
import { createWorkflowBaseUrl, pluralize, stepDisplayName, } from '@workflow/utils';
|
|
4
|
+
import { SPEC_VERSION_CURRENT, SPEC_VERSION_SUPPORTS_COMPRESSION, } from '@workflow/world';
|
|
5
|
+
import { runtimeLogger, stepLogger } from '../logger.js';
|
|
6
|
+
import { getStepFunction } from '../private.js';
|
|
7
|
+
import { cancelAbortReaders, dehydrateStepError, dehydrateStepReturnValue, hydrateStepArguments, hydrateStepError, } from '../serialization.js';
|
|
8
|
+
import { contextStorage } from '../step/context-storage.js';
|
|
9
|
+
import * as Attribute from '../telemetry/semantic-conventions.js';
|
|
10
|
+
import { trace } from '../telemetry.js';
|
|
11
|
+
import { getErrorName, getErrorStack, normalizeUnknownError, promoteAbortErrorToFatal, } from '../types.js';
|
|
12
|
+
import { COMPUTE_INSTANCE_ID } from './compute-instance.js';
|
|
13
|
+
import { isOptimisticInlineStartEnabled, isOptimisticInlineStartExplicitlyDisabled, } from './constants.js';
|
|
14
|
+
import { getPortLazy } from './get-port-lazy.js';
|
|
15
|
+
import { memoizeEncryptionKey, } from './helpers.js';
|
|
16
|
+
import { ReplayRecoveryReporter } from './replay-recovery-reporter.js';
|
|
17
|
+
import { computeStepLatencyEventData, } from './step-latency.js';
|
|
18
|
+
import { safeWaitUntil } from './wait-until.js';
|
|
19
|
+
export const DEFAULT_STEP_MAX_RETRIES = 3;
|
|
20
|
+
/**
|
|
21
|
+
* Extract the inline delta from a step-terminal `events.create` result,
|
|
22
|
+
* if the World populated one. A delta is only meaningful when the caller
|
|
23
|
+
* requested it (`sinceCursor` was passed) and the World returned both
|
|
24
|
+
* `events` and a `cursor`; otherwise we return undefined and the caller
|
|
25
|
+
* falls back to the normal incremental `events.list`. `hasMore` defaults
|
|
26
|
+
* to false (single page) when the World omits it.
|
|
27
|
+
*/
|
|
28
|
+
function extractInlineDelta(result, requested) {
|
|
29
|
+
if (!requested)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (!result.events || result.cursor === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
return {
|
|
34
|
+
events: result.events,
|
|
35
|
+
cursor: result.cursor,
|
|
36
|
+
hasMore: result.hasMore ?? false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Executes a single step: creates step_started event, hydrates input,
|
|
41
|
+
* runs the step function, creates step_completed/step_failed/step_retrying events.
|
|
42
|
+
*
|
|
43
|
+
* Does NOT queue workflow continuation messages — the caller decides what to do next.
|
|
44
|
+
* Used by the combined workflow handler for step execution.
|
|
45
|
+
*/
|
|
46
|
+
export async function executeStep(params) {
|
|
47
|
+
const { world, workflowRunId, workflowName, workflowStartedAt, stepId, stepName, } = params;
|
|
48
|
+
// Truthiness, not presence: `vercel env pull` writes `VERCEL_URL=""` into
|
|
49
|
+
// `.env.local`, and a framework that loads that file locally would otherwise
|
|
50
|
+
// put us on the Vercel branch with nothing to build a host from, making
|
|
51
|
+
// `https://` the base URL of every step.
|
|
52
|
+
const isVercel = Boolean(process.env.VERCEL_URL);
|
|
53
|
+
// Gate payload compression on the run's specVersion.
|
|
54
|
+
const compression = (params.runSpecVersion ?? 0) >= SPEC_VERSION_SUPPORTS_COMPRESSION;
|
|
55
|
+
const replayRecoveryReporter = params.replayRecoveryReporter ?? ReplayRecoveryReporter.inert();
|
|
56
|
+
const createEvent = (data, eventParams) => replayRecoveryReporter.withEventCreate(eventParams, (p) => world.events.create(workflowRunId, data, p));
|
|
57
|
+
const spanName = `step.execute ${stepDisplayName(stepName)}`;
|
|
58
|
+
return trace(spanName, {}, async (span) => {
|
|
59
|
+
span?.setAttributes({
|
|
60
|
+
...Attribute.StepName(stepName),
|
|
61
|
+
...Attribute.WorkflowName(workflowName),
|
|
62
|
+
...Attribute.WorkflowRunId(workflowRunId),
|
|
63
|
+
...Attribute.StepId(stepId),
|
|
64
|
+
});
|
|
65
|
+
// Memoized accessor for the per-run encryption key. The first caller
|
|
66
|
+
// (input hydration on the success path, or one of the early-return
|
|
67
|
+
// dehydrateStepError paths if step_started fails) triggers the actual
|
|
68
|
+
// fetch / HKDF derivation; subsequent callers await the cached promise.
|
|
69
|
+
const getEncryptionKey = memoizeEncryptionKey(world, workflowRunId);
|
|
70
|
+
const stepFn = getStepFunction(stepName);
|
|
71
|
+
if (!stepFn || typeof stepFn !== 'function') {
|
|
72
|
+
// Step function not registered — fail the step immediately (not the run).
|
|
73
|
+
// Create a step_failed event so
|
|
74
|
+
// the workflow can handle it gracefully via try/catch in user code.
|
|
75
|
+
const errorMessage = `Step "${stepName}" is not registered in the current deployment. This usually indicates a build or bundling issue that caused the step to not be included in the deployment.`;
|
|
76
|
+
runtimeLogger.error('Step function not registered, failing step', {
|
|
77
|
+
workflowRunId,
|
|
78
|
+
stepName,
|
|
79
|
+
stepId,
|
|
80
|
+
});
|
|
81
|
+
// On the lazy inline path the suspension handler deferred this step's
|
|
82
|
+
// `step_created`, expecting executeStep to materialize the step via a
|
|
83
|
+
// lazy `step_started` carrying its input. We never get that far for an
|
|
84
|
+
// unregistered step, so the step entity does not exist yet — writing
|
|
85
|
+
// `step_failed` straight away would hit the world's "step must exist"
|
|
86
|
+
// ordering guard and wedge the run. Send the lazy `step_started` first
|
|
87
|
+
// (it creates the step + synthetic `step_created` atomically and keeps
|
|
88
|
+
// replay correct), then fail it below. This also preserves the
|
|
89
|
+
// exactly-one-owner guarantee: a concurrent handler that won the create
|
|
90
|
+
// makes our lazy `step_started` reject with EntityConflictError → we
|
|
91
|
+
// return `skipped` and never write the failure twice.
|
|
92
|
+
if (params.lazyStepInput !== undefined) {
|
|
93
|
+
try {
|
|
94
|
+
// Turbo: this lazy `step_started` must not precede the backgrounded
|
|
95
|
+
// `run_started`. Order it after the run-ready barrier (best-effort —
|
|
96
|
+
// a barrier rejection means the run doesn't exist, and the create
|
|
97
|
+
// below surfaces the real error). No-op outside turbo.
|
|
98
|
+
if (params.runReadyBarrier) {
|
|
99
|
+
await params.runReadyBarrier.catch(() => { });
|
|
100
|
+
}
|
|
101
|
+
await createEvent({
|
|
102
|
+
eventType: 'step_started',
|
|
103
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
104
|
+
correlationId: stepId,
|
|
105
|
+
eventData: {
|
|
106
|
+
stepName,
|
|
107
|
+
workflowName,
|
|
108
|
+
input: params.lazyStepInput,
|
|
109
|
+
// Stamped for consistency even though this step terminal-fails
|
|
110
|
+
// immediately below — the log should never show an unowned
|
|
111
|
+
// lazy start.
|
|
112
|
+
...(params.ownerMessageId !== undefined
|
|
113
|
+
? { ownerMessageId: params.ownerMessageId }
|
|
114
|
+
: {}),
|
|
115
|
+
},
|
|
116
|
+
}, { computeInstanceId: COMPUTE_INSTANCE_ID });
|
|
117
|
+
}
|
|
118
|
+
catch (startErr) {
|
|
119
|
+
if (EntityConflictError.is(startErr)) {
|
|
120
|
+
return { type: 'skipped' };
|
|
121
|
+
}
|
|
122
|
+
if (RunExpiredError.is(startErr)) {
|
|
123
|
+
return { type: 'gone' };
|
|
124
|
+
}
|
|
125
|
+
throw startErr;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
await createEvent({
|
|
130
|
+
eventType: 'step_failed',
|
|
131
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
132
|
+
correlationId: stepId,
|
|
133
|
+
eventData: {
|
|
134
|
+
stepName,
|
|
135
|
+
error: await dehydrateStepError(new FatalError(errorMessage), workflowRunId, await getEncryptionKey(), [], globalThis, compression),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (stepFailErr) {
|
|
140
|
+
if (EntityConflictError.is(stepFailErr)) {
|
|
141
|
+
return { type: 'skipped' };
|
|
142
|
+
}
|
|
143
|
+
throw stepFailErr;
|
|
144
|
+
}
|
|
145
|
+
span?.setAttributes({
|
|
146
|
+
...Attribute.StepStatus('failed'),
|
|
147
|
+
...Attribute.StepFatalError(true),
|
|
148
|
+
});
|
|
149
|
+
return { type: 'failed' };
|
|
150
|
+
}
|
|
151
|
+
const maxRetries = stepFn.maxRetries ?? DEFAULT_STEP_MAX_RETRIES;
|
|
152
|
+
span?.setAttributes({
|
|
153
|
+
...Attribute.StepMaxRetries(maxRetries),
|
|
154
|
+
});
|
|
155
|
+
// maxRetries enforced before starting another attempt. Timeouts can kill
|
|
156
|
+
// a step execution without any way to catch the error, so the post-body/error
|
|
157
|
+
// guards below might miss it. Hence, we use `authoritativeAttempt` count
|
|
158
|
+
// as an additional guard based on step_started count or queue delivery count
|
|
159
|
+
// on backgrounded steps.
|
|
160
|
+
// Thrown-error exhaustion still terminates one attempt earlier via the post-body
|
|
161
|
+
// guard (with the thrown error as cause), and this check does not affect that.
|
|
162
|
+
if (params.authoritativeAttempt !== undefined &&
|
|
163
|
+
params.authoritativeAttempt > maxRetries + 1) {
|
|
164
|
+
const retryCount = maxRetries;
|
|
165
|
+
const errorMessage = `Step "${stepName}" exceeded max retries (${retryCount} ${pluralize('retry', 'retries', retryCount)})`;
|
|
166
|
+
stepLogger.error('Step exceeded max retries', {
|
|
167
|
+
workflowRunId,
|
|
168
|
+
stepName,
|
|
169
|
+
stepId,
|
|
170
|
+
attempt: params.authoritativeAttempt,
|
|
171
|
+
maxRetries,
|
|
172
|
+
});
|
|
173
|
+
try {
|
|
174
|
+
await createEvent({
|
|
175
|
+
eventType: 'step_failed',
|
|
176
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
177
|
+
correlationId: stepId,
|
|
178
|
+
eventData: {
|
|
179
|
+
stepName,
|
|
180
|
+
error: await dehydrateStepError(new FatalError(errorMessage), workflowRunId, await getEncryptionKey(), [], globalThis, compression),
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
if (EntityConflictError.is(err)) {
|
|
186
|
+
// Step already reached a terminal state (a concurrent handler or an
|
|
187
|
+
// earlier delivery failed/completed it) — nothing to do.
|
|
188
|
+
runtimeLogger.info('Tried failing step for exceeded retries, but step has already finished.', {
|
|
189
|
+
workflowRunId,
|
|
190
|
+
stepId,
|
|
191
|
+
stepName,
|
|
192
|
+
message: err instanceof Error ? err.message : String(err),
|
|
193
|
+
});
|
|
194
|
+
return { type: 'skipped' };
|
|
195
|
+
}
|
|
196
|
+
if (RunExpiredError.is(err)) {
|
|
197
|
+
return { type: 'gone' };
|
|
198
|
+
}
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
span?.setAttributes({
|
|
202
|
+
...Attribute.StepStatus('failed'),
|
|
203
|
+
...Attribute.StepRetryExhausted(true),
|
|
204
|
+
});
|
|
205
|
+
return { type: 'failed' };
|
|
206
|
+
}
|
|
207
|
+
// Maps a `step_started` rejection to a terminal StepExecutionResult,
|
|
208
|
+
// shared by the await path (below) and the optimistic-start reconciliation.
|
|
209
|
+
// Returns undefined when the error is not one we translate (caller rethrows).
|
|
210
|
+
const startErrorToResult = (err) => {
|
|
211
|
+
if (ThrottleError.is(err)) {
|
|
212
|
+
const retryAfter = Math.max(1, typeof err.retryAfter === 'number' ? err.retryAfter : 1);
|
|
213
|
+
runtimeLogger.info('Throttled on step_started, deferring', {
|
|
214
|
+
retryAfterSeconds: retryAfter,
|
|
215
|
+
});
|
|
216
|
+
return { type: 'throttled', timeoutSeconds: retryAfter };
|
|
217
|
+
}
|
|
218
|
+
if (RunExpiredError.is(err)) {
|
|
219
|
+
runtimeLogger.info(`Workflow run "${workflowRunId}" has already completed, skipping step "${stepId}": ${err.message}`);
|
|
220
|
+
return { type: 'gone' };
|
|
221
|
+
}
|
|
222
|
+
if (EntityConflictError.is(err)) {
|
|
223
|
+
runtimeLogger.debug('Step in terminal state, skipping', {
|
|
224
|
+
stepName,
|
|
225
|
+
stepId,
|
|
226
|
+
workflowRunId,
|
|
227
|
+
error: err instanceof Error ? err.message : String(err),
|
|
228
|
+
});
|
|
229
|
+
span?.setAttributes({
|
|
230
|
+
...Attribute.StepSkipped(true),
|
|
231
|
+
...Attribute.StepSkipReason('completed'),
|
|
232
|
+
});
|
|
233
|
+
return { type: 'skipped' };
|
|
234
|
+
}
|
|
235
|
+
if (TooEarlyError.is(err)) {
|
|
236
|
+
const timeoutSeconds = Math.max(1, err.retryAfter ?? 1);
|
|
237
|
+
runtimeLogger.debug('Step retryAfter timestamp not yet reached', {
|
|
238
|
+
stepName,
|
|
239
|
+
stepId,
|
|
240
|
+
timeoutSeconds,
|
|
241
|
+
});
|
|
242
|
+
return { type: 'retry', timeoutSeconds };
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
};
|
|
246
|
+
// Optimistic inline start: when we hold the step input locally (lazy inline
|
|
247
|
+
// path) and the optimization is enabled, fire `step_started` WITHOUT
|
|
248
|
+
// awaiting and run the body against locally-synthesized state. A lazy step
|
|
249
|
+
// is always brand-new ⇒ attempt 1, no prior error, started now — so we
|
|
250
|
+
// don't need the server round-trip to begin. We reconcile the in-flight
|
|
251
|
+
// `step_started` before any terminal write (`reconcileOptimisticStart`): if
|
|
252
|
+
// it lost the atomic create-claim (409) or the run is gone/throttled, we
|
|
253
|
+
// discard the body result. Running the body before confirming ownership can
|
|
254
|
+
// execute a step more than once when handlers race — inline step bodies
|
|
255
|
+
// must be idempotent; disable via WORKFLOW_OPTIMISTIC_INLINE_START=0.
|
|
256
|
+
//
|
|
257
|
+
// Turbo mode passes `forceOptimisticStart` to enable this regardless of the
|
|
258
|
+
// env flag (its single-handler guarantee removes the race). But it still
|
|
259
|
+
// defers to an *explicit* `WORKFLOW_OPTIMISTIC_INLINE_START=0`: forced
|
|
260
|
+
// optimistic start runs the body before `step_started`/`run_started` is
|
|
261
|
+
// confirmed, which is exactly the property an operator opts out of with that
|
|
262
|
+
// flag, so an explicit opt-out wins over turbo's force.
|
|
263
|
+
const optimisticStart = params.lazyStepInput !== undefined &&
|
|
264
|
+
// Stale-sensitive guarded batches await the claim so the 412 fence
|
|
265
|
+
// covers the body, not just durable writes — see
|
|
266
|
+
// StepExecutorParams.suppressOptimisticStart.
|
|
267
|
+
params.suppressOptimisticStart !== true &&
|
|
268
|
+
(isOptimisticInlineStartEnabled() ||
|
|
269
|
+
(params.forceOptimisticStart === true &&
|
|
270
|
+
!isOptimisticInlineStartExplicitlyDisabled()));
|
|
271
|
+
let step;
|
|
272
|
+
// Params for the `step_started` create on either path below: the ambient
|
|
273
|
+
// compute-instance stamp plus the optimistic-concurrency claim guard.
|
|
274
|
+
const startEventParams = {
|
|
275
|
+
computeInstanceId: COMPUTE_INSTANCE_ID,
|
|
276
|
+
// Spread as a unit: the three snapshot fields describe one snapshot and
|
|
277
|
+
// must travel together — see StepExecutorParams.preconditionSnapshot.
|
|
278
|
+
...params.preconditionSnapshot,
|
|
279
|
+
};
|
|
280
|
+
// `Date.now()` taken immediately before the `step_started` create is
|
|
281
|
+
// issued (either path below) — anchors RSFS's end point. See
|
|
282
|
+
// StepLatencyEventData.rsfs and the call sites below.
|
|
283
|
+
let stepStartPostSentAtMs;
|
|
284
|
+
// Settled outcome of the in-flight optimistic `step_started`. Handlers are
|
|
285
|
+
// attached synchronously (`.then(ok, err)`) so a fast rejection never
|
|
286
|
+
// surfaces as an unhandledRejection while the body runs.
|
|
287
|
+
let optimisticStartSettled;
|
|
288
|
+
// Await the optimistic `step_started` outcome and translate a lost race /
|
|
289
|
+
// terminal run / throttle into a result that short-circuits the body
|
|
290
|
+
// output. Returns undefined when we own the step and may write its terminal
|
|
291
|
+
// event. A non-translatable rejection is rethrown (so a transient
|
|
292
|
+
// step_started failure propagates to the queue handler for redelivery,
|
|
293
|
+
// exactly as on the await path). Idempotent — safe to call more than once.
|
|
294
|
+
const reconcileOptimisticStart = async () => {
|
|
295
|
+
if (!optimisticStartSettled)
|
|
296
|
+
return undefined;
|
|
297
|
+
const settled = await optimisticStartSettled;
|
|
298
|
+
if (settled.ok)
|
|
299
|
+
return undefined;
|
|
300
|
+
const mapped = startErrorToResult(settled.err);
|
|
301
|
+
if (!mapped)
|
|
302
|
+
throw settled.err;
|
|
303
|
+
return mapped;
|
|
304
|
+
};
|
|
305
|
+
if (optimisticStart) {
|
|
306
|
+
// Chain the lazy `step_started` on the run-ready barrier (turbo mode):
|
|
307
|
+
// the step can't be created before its run exists, but the body below
|
|
308
|
+
// runs immediately against synthesized state, so the `run_started`
|
|
309
|
+
// round-trip overlaps the body rather than blocking it. Outside turbo the
|
|
310
|
+
// barrier is undefined and this is a plain create.
|
|
311
|
+
const startedPromise = (params.runReadyBarrier ?? Promise.resolve()).then(() => {
|
|
312
|
+
// Taken right before the create fires, not before the barrier —
|
|
313
|
+
// RSFS measures the run_started-to-POST stretch, and the barrier
|
|
314
|
+
// wait IS part of that stretch under turbo.
|
|
315
|
+
stepStartPostSentAtMs = Date.now();
|
|
316
|
+
return createEvent({
|
|
317
|
+
eventType: 'step_started',
|
|
318
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
319
|
+
correlationId: stepId,
|
|
320
|
+
eventData: {
|
|
321
|
+
stepName,
|
|
322
|
+
workflowName,
|
|
323
|
+
input: params.lazyStepInput,
|
|
324
|
+
// Inline-ownership stamp — see StepExecutorParams.ownerMessageId.
|
|
325
|
+
...(params.ownerMessageId !== undefined
|
|
326
|
+
? { ownerMessageId: params.ownerMessageId }
|
|
327
|
+
: {}),
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
// Guard the claim — see StepExecutorParams.preconditionSnapshot. A
|
|
331
|
+
// stale (412) rejection surfaces via reconcileOptimisticStart as a
|
|
332
|
+
// non-translatable error: the body result is discarded and the
|
|
333
|
+
// rejection propagates to the caller.
|
|
334
|
+
startEventParams);
|
|
335
|
+
});
|
|
336
|
+
optimisticStartSettled = startedPromise.then(() => ({ ok: true }), (err) => ({ ok: false, err }));
|
|
337
|
+
const now = new Date();
|
|
338
|
+
step = {
|
|
339
|
+
runId: workflowRunId,
|
|
340
|
+
stepId,
|
|
341
|
+
stepName,
|
|
342
|
+
status: 'running',
|
|
343
|
+
input: params.lazyStepInput,
|
|
344
|
+
attempt: 1,
|
|
345
|
+
startedAt: now,
|
|
346
|
+
createdAt: now,
|
|
347
|
+
updatedAt: now,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
// step_started validates state and returns the step entity. On the lazy
|
|
352
|
+
// inline path we also carry the step `input` so the world creates the
|
|
353
|
+
// step on the fly (no separate step_created round-trip). The world's
|
|
354
|
+
// atomic create-claim makes this exactly-one-owner: a concurrent loser
|
|
355
|
+
// gets EntityConflictError, mapped to `{ type: 'skipped' }`, so it never
|
|
356
|
+
// runs the body. When `lazyStepInput` is absent this is the legacy
|
|
357
|
+
// step_started (step already created, no payload).
|
|
358
|
+
try {
|
|
359
|
+
// Inline-ownership stamp: present on the lazy paths AND on the
|
|
360
|
+
// owned-recovery payload-less start (a redelivery of the owning
|
|
361
|
+
// message re-executing its step must re-stamp — ownership derives
|
|
362
|
+
// from the latest start, so an unstamped recovery start would read
|
|
363
|
+
// as "unowned" to a later wake). Only the background-step path
|
|
364
|
+
// passes no ownerMessageId: its start is the bare one, clearing
|
|
365
|
+
// ownership as intended.
|
|
366
|
+
const ownershipStamp = params.ownerMessageId !== undefined
|
|
367
|
+
? { ownerMessageId: params.ownerMessageId }
|
|
368
|
+
: {};
|
|
369
|
+
stepStartPostSentAtMs = Date.now();
|
|
370
|
+
const startResult = await createEvent({
|
|
371
|
+
eventType: 'step_started',
|
|
372
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
373
|
+
correlationId: stepId,
|
|
374
|
+
eventData: params.lazyStepInput !== undefined
|
|
375
|
+
? {
|
|
376
|
+
stepName,
|
|
377
|
+
workflowName,
|
|
378
|
+
input: params.lazyStepInput,
|
|
379
|
+
...ownershipStamp,
|
|
380
|
+
}
|
|
381
|
+
: { stepName, ...ownershipStamp },
|
|
382
|
+
},
|
|
383
|
+
// Guard the claim — see StepExecutorParams.preconditionSnapshot. A
|
|
384
|
+
// stale (412) rejection is intentionally NOT translated by
|
|
385
|
+
// startErrorToResult below, so it propagates to the caller for a
|
|
386
|
+
// fresh replay.
|
|
387
|
+
startEventParams);
|
|
388
|
+
step = startResult.step;
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
const mapped = startErrorToResult(err);
|
|
392
|
+
if (mapped)
|
|
393
|
+
return mapped;
|
|
394
|
+
throw err;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
runtimeLogger.debug('Step execution details', {
|
|
398
|
+
stepName,
|
|
399
|
+
stepId: step.stepId,
|
|
400
|
+
status: step.status,
|
|
401
|
+
attempt: step.attempt,
|
|
402
|
+
});
|
|
403
|
+
span?.setAttributes({
|
|
404
|
+
...Attribute.StepStatus(step.status),
|
|
405
|
+
});
|
|
406
|
+
let result;
|
|
407
|
+
// Check max retries AFTER step_started (attempt was just incremented).
|
|
408
|
+
// Only enforce when the step has a previous error — this distinguishes
|
|
409
|
+
// actual retries (failed → retry) from concurrent inline starts
|
|
410
|
+
// execution loop can cause multiple handlers to step_started the same
|
|
411
|
+
// step simultaneously, inflating the attempt counter without any failure).
|
|
412
|
+
if (step.attempt > maxRetries + 1 && step.error) {
|
|
413
|
+
const retryCount = step.attempt - 1;
|
|
414
|
+
const errorMessage = `Step "${stepName}" exceeded max retries (${retryCount} ${pluralize('retry', 'retries', retryCount)})`;
|
|
415
|
+
stepLogger.error('Step exceeded max retries', {
|
|
416
|
+
workflowRunId,
|
|
417
|
+
stepName,
|
|
418
|
+
retryCount,
|
|
419
|
+
});
|
|
420
|
+
// Preserve the prior attempt's serialized error as the cause so the
|
|
421
|
+
// underlying failure is recoverable from `step.error.cause` after
|
|
422
|
+
// hydration, without forcing consumers to walk the step_retrying
|
|
423
|
+
// event history. Best-effort: if hydration of the prior `step.error`
|
|
424
|
+
// throws, fall back to a FatalError without cause.
|
|
425
|
+
const wrappedError = new FatalError(errorMessage);
|
|
426
|
+
if (step.error != null) {
|
|
427
|
+
try {
|
|
428
|
+
wrappedError.cause = await hydrateStepError(step.error, workflowRunId, await getEncryptionKey());
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
// Ignore — best-effort cause attachment.
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
await createEvent({
|
|
436
|
+
eventType: 'step_failed',
|
|
437
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
438
|
+
correlationId: stepId,
|
|
439
|
+
eventData: {
|
|
440
|
+
stepName,
|
|
441
|
+
error: await dehydrateStepError(wrappedError, workflowRunId, await getEncryptionKey(), [], globalThis, compression),
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
if (EntityConflictError.is(err)) {
|
|
447
|
+
runtimeLogger.info('Tried failing step, but step has already finished.', {
|
|
448
|
+
workflowRunId,
|
|
449
|
+
stepId,
|
|
450
|
+
stepName,
|
|
451
|
+
message: err.message,
|
|
452
|
+
});
|
|
453
|
+
return { type: 'skipped' };
|
|
454
|
+
}
|
|
455
|
+
throw err;
|
|
456
|
+
}
|
|
457
|
+
span?.setAttributes({
|
|
458
|
+
...Attribute.StepStatus('failed'),
|
|
459
|
+
...Attribute.StepRetryExhausted(true),
|
|
460
|
+
});
|
|
461
|
+
return { type: 'failed' };
|
|
462
|
+
}
|
|
463
|
+
// Ops that must be durably committed before step completion (e.g. a
|
|
464
|
+
// step-initiated abort's hook_received event). See StepContext. Declared
|
|
465
|
+
// outside the try so the failure path below can also drain them.
|
|
466
|
+
const preCompletionOps = [];
|
|
467
|
+
const ops = [];
|
|
468
|
+
let opsSettled = true;
|
|
469
|
+
// Latency telemetry to attach to this step's terminal event. Computed
|
|
470
|
+
// right before user code runs; declared here so the failure path (the
|
|
471
|
+
// catch below) can attach it to step_failed too.
|
|
472
|
+
let latencyEventData;
|
|
473
|
+
// Backfill RSFS onto the already-computed telemetry once the optimistic
|
|
474
|
+
// turbo start has settled. On that path the step-start POST fires inside
|
|
475
|
+
// the run-ready barrier's `.then`, so `stepStartPostSentAtMs` — and
|
|
476
|
+
// therefore RSFS — is usually still unset when `latencyEventData` is
|
|
477
|
+
// first computed just before user code (the barrier is still in flight
|
|
478
|
+
// for any non-trivial `run_started` round-trip, which is exactly the
|
|
479
|
+
// slow-run_started case RSFS exists to measure). By the time
|
|
480
|
+
// `reconcileOptimisticStart()` has awaited the barrier the POST timestamp
|
|
481
|
+
// is known, so we patch RSFS in before the terminal event is written.
|
|
482
|
+
// Without this, slow-run_started samples are dropped, biasing RSFS
|
|
483
|
+
// percentiles low (missing-not-at-random). Recomputes only RSFS —
|
|
484
|
+
// TTFS/STSO stay anchored to `executionStartTime` as computed above.
|
|
485
|
+
const backfillOptimisticRsfs = () => {
|
|
486
|
+
if (!latencyEventData || latencyEventData.rsfs !== undefined)
|
|
487
|
+
return;
|
|
488
|
+
const anchorMs = params.latencyTracking?.rsfsAnchorMs;
|
|
489
|
+
if (anchorMs === undefined || stepStartPostSentAtMs === undefined)
|
|
490
|
+
return;
|
|
491
|
+
latencyEventData.rsfs = Math.max(0, stepStartPostSentAtMs - anchorMs);
|
|
492
|
+
if (span) {
|
|
493
|
+
span.setAttributes({
|
|
494
|
+
...Attribute.StepRsfsMs(latencyEventData.rsfs),
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
try {
|
|
499
|
+
const attempt = step.attempt;
|
|
500
|
+
if (!step.startedAt) {
|
|
501
|
+
throw new WorkflowRuntimeError(`Step "${stepId}" has no "startedAt" timestamp`);
|
|
502
|
+
}
|
|
503
|
+
const stepStartedAt = step.startedAt;
|
|
504
|
+
// Use the provided encryption key when available, otherwise resolve
|
|
505
|
+
// through the memoized accessor declared at the top of this trace.
|
|
506
|
+
const encryptionKey = params.encryptionKey ?? (await getEncryptionKey());
|
|
507
|
+
const hydratedInput = await trace('step.hydrate', {}, async (hydrateSpan) => {
|
|
508
|
+
const startTime = Date.now();
|
|
509
|
+
const hydrated = await hydrateStepArguments(step.input, workflowRunId, encryptionKey, ops, globalThis, {}, params.workflowDeploymentId);
|
|
510
|
+
const durationMs = Date.now() - startTime;
|
|
511
|
+
hydrateSpan?.setAttributes({
|
|
512
|
+
...Attribute.StepArgumentsCount(hydrated.args.length),
|
|
513
|
+
...Attribute.QueueDeserializeTimeMs(durationMs),
|
|
514
|
+
});
|
|
515
|
+
return hydrated;
|
|
516
|
+
});
|
|
517
|
+
const args = hydratedInput.args;
|
|
518
|
+
const thisVal = hydratedInput.thisVal ?? null;
|
|
519
|
+
const workflowBaseUrl = createWorkflowBaseUrl(isVercel
|
|
520
|
+
? `https://${process.env.VERCEL_URL}`
|
|
521
|
+
: `http://localhost:${(await getPortLazy()) ?? 3000}`);
|
|
522
|
+
// --- User code execution ---
|
|
523
|
+
// Wrap only stepFn.apply() (user step code) so cleanup below runs on
|
|
524
|
+
// BOTH success and failure. A user-code throw is captured here and
|
|
525
|
+
// re-raised after cancelAbortReaders, so it still flows to the outer
|
|
526
|
+
// catch (step_failed/step_retrying) — but the abort-stream reader is
|
|
527
|
+
// torn down first. Without this, a throwing/retrying signal-bearing
|
|
528
|
+
// step would leak a real-time abort reader per attempt.
|
|
529
|
+
let userCodeError;
|
|
530
|
+
let userCodeFailed = false;
|
|
531
|
+
const executionStartTime = Date.now();
|
|
532
|
+
latencyEventData = computeStepLatencyEventData({
|
|
533
|
+
tracking: params.latencyTracking,
|
|
534
|
+
stepCodeStartedAtMs: executionStartTime,
|
|
535
|
+
attempt,
|
|
536
|
+
lazyStepStart: params.lazyStepInput !== undefined,
|
|
537
|
+
optimisticStart,
|
|
538
|
+
stepStartPostSentAtMs,
|
|
539
|
+
});
|
|
540
|
+
if (latencyEventData) {
|
|
541
|
+
// Mirror the latency telemetry onto the step span so traces show
|
|
542
|
+
// TTFS/STSO/RSFS alongside the flame graph, not just Datadog metrics.
|
|
543
|
+
span?.setAttributes({
|
|
544
|
+
...(latencyEventData.ttfs !== undefined
|
|
545
|
+
? Attribute.StepTtfsMs(latencyEventData.ttfs)
|
|
546
|
+
: {}),
|
|
547
|
+
...(latencyEventData.stso !== undefined
|
|
548
|
+
? Attribute.StepStsoMs(latencyEventData.stso)
|
|
549
|
+
: {}),
|
|
550
|
+
...(latencyEventData.rsfs !== undefined
|
|
551
|
+
? Attribute.StepRsfsMs(latencyEventData.rsfs)
|
|
552
|
+
: {}),
|
|
553
|
+
...(latencyEventData.finalSchedulingReplay !== undefined
|
|
554
|
+
? Attribute.StepFinalSchedulingReplayMs(latencyEventData.finalSchedulingReplay)
|
|
555
|
+
: {}),
|
|
556
|
+
...Attribute.StepLatencyOptimizations(latencyEventData.optimizations ?? []),
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
result = await trace('step.execute', {}, async () => {
|
|
561
|
+
return await contextStorage.run({
|
|
562
|
+
stepMetadata: {
|
|
563
|
+
stepName,
|
|
564
|
+
stepId,
|
|
565
|
+
stepStartedAt: new Date(+stepStartedAt),
|
|
566
|
+
attempt,
|
|
567
|
+
},
|
|
568
|
+
workflowMetadata: {
|
|
569
|
+
workflowName,
|
|
570
|
+
workflowRunId,
|
|
571
|
+
workflowStartedAt: new Date(+workflowStartedAt),
|
|
572
|
+
url: workflowBaseUrl,
|
|
573
|
+
features: { encryption: !!encryptionKey },
|
|
574
|
+
},
|
|
575
|
+
workflowDeploymentId: params.workflowDeploymentId,
|
|
576
|
+
rootRunId: params.rootRunId,
|
|
577
|
+
ops,
|
|
578
|
+
preCompletionOps,
|
|
579
|
+
closureVars: hydratedInput.closureVars,
|
|
580
|
+
encryptionKey,
|
|
581
|
+
// Turbo optimistic start runs this body before `run_started` is
|
|
582
|
+
// durable. Expose the barrier so a direct step-body world write
|
|
583
|
+
// (e.g. `setAttributes`) can order itself after the
|
|
584
|
+
// run exists. Undefined on the await path (run already durable).
|
|
585
|
+
runReadyBarrier: optimisticStart
|
|
586
|
+
? params.runReadyBarrier
|
|
587
|
+
: undefined,
|
|
588
|
+
}, () => stepFn.apply(thisVal, args));
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
catch (err) {
|
|
592
|
+
userCodeError = err;
|
|
593
|
+
userCodeFailed = true;
|
|
594
|
+
}
|
|
595
|
+
const executionTimeMs = Date.now() - executionStartTime;
|
|
596
|
+
// Tear down any abort-stream readers opened while hydrating the step's
|
|
597
|
+
// arguments (a serialized AbortSignal opens a real-time abort reader for
|
|
598
|
+
// the step's duration). Without this the reader's `read()` promise never
|
|
599
|
+
// settles, so the `ops` flush below always loses the 500ms race and the
|
|
600
|
+
// step reports `hasPendingOps` — forcing the inline loop to queue a
|
|
601
|
+
// continuation and paying a full round-trip per signal-bearing step.
|
|
602
|
+
// Runs unconditionally (success or failure) so a throwing step doesn't
|
|
603
|
+
// leak the reader.
|
|
604
|
+
cancelAbortReaders(...args, thisVal, hydratedInput.closureVars);
|
|
605
|
+
// Re-raise a user-code failure now that cleanup has run; the outer
|
|
606
|
+
// catch maps it to step_failed/step_retrying.
|
|
607
|
+
if (userCodeFailed) {
|
|
608
|
+
throw userCodeError;
|
|
609
|
+
}
|
|
610
|
+
span?.setAttributes({
|
|
611
|
+
...Attribute.QueueExecutionTimeMs(executionTimeMs),
|
|
612
|
+
});
|
|
613
|
+
result = await trace('step.dehydrate', {}, async (dehydrateSpan) => {
|
|
614
|
+
const startTime = Date.now();
|
|
615
|
+
const dehydrated = await dehydrateStepReturnValue(result, workflowRunId, encryptionKey, ops, globalThis, false, false, compression,
|
|
616
|
+
// Turbo optimistic start: a returned stream is piped to the server
|
|
617
|
+
// after the body but within this same op flush, so gate its first
|
|
618
|
+
// write on the run-ready barrier. Undefined on the await path.
|
|
619
|
+
optimisticStart ? params.runReadyBarrier : undefined);
|
|
620
|
+
const durationMs = Date.now() - startTime;
|
|
621
|
+
dehydrateSpan?.setAttributes({
|
|
622
|
+
...Attribute.QueueSerializeTimeMs(durationMs),
|
|
623
|
+
...Attribute.StepResultType(typeof dehydrated),
|
|
624
|
+
});
|
|
625
|
+
return dehydrated;
|
|
626
|
+
});
|
|
627
|
+
// Flush pending ops (stream writes, etc.) with a short inline wait.
|
|
628
|
+
// WorkflowServerWritableStream acks writes on buffer entry
|
|
629
|
+
// (group-commit batching); durability is enforced by its drain
|
|
630
|
+
// barrier, which the flushable state's completion awaits after
|
|
631
|
+
// lock release. Most ops settle within ~200ms (lock-release
|
|
632
|
+
// polling + one batched HTTP flush).
|
|
633
|
+
// If ops don't settle in 500ms (e.g., WritableStream kept open
|
|
634
|
+
// across steps), waitUntil handles the rest.
|
|
635
|
+
if (ops.length > 0) {
|
|
636
|
+
const opsPromise = Promise.all(ops);
|
|
637
|
+
// The race below surfaces failures inline when ops settle quickly;
|
|
638
|
+
// if the 500ms timeout wins, the failure is only observed here. The
|
|
639
|
+
// promise handed to waitUntil must never reject (an unconsumed
|
|
640
|
+
// waitUntil rejection crashes the process as unhandledRejection),
|
|
641
|
+
// so unexpected failures are logged instead.
|
|
642
|
+
safeWaitUntil(opsPromise, (err) => {
|
|
643
|
+
runtimeLogger.warn('Background flush of step stream ops failed', {
|
|
644
|
+
workflowRunId,
|
|
645
|
+
stepId,
|
|
646
|
+
error: err instanceof Error ? err.message : String(err),
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
opsSettled = await Promise.race([
|
|
650
|
+
opsPromise.then(() => true, (err) => {
|
|
651
|
+
// Ignore expected client disconnect errors (e.g., browser
|
|
652
|
+
// refresh during streaming)
|
|
653
|
+
const isAbortError = err?.name === 'AbortError' || err?.name === 'ResponseAborted';
|
|
654
|
+
if (isAbortError)
|
|
655
|
+
return true;
|
|
656
|
+
throw err;
|
|
657
|
+
}),
|
|
658
|
+
new Promise((r) => setTimeout(() => r(false), 500)),
|
|
659
|
+
]);
|
|
660
|
+
}
|
|
661
|
+
// Optimistic start: the body ran before `step_started` was confirmed.
|
|
662
|
+
// Reconcile it now — if we lost the create-claim (or the run is
|
|
663
|
+
// gone/throttled) discard this result and don't write step_completed.
|
|
664
|
+
// Reconcile before draining preCompletionOps: a discarded result means
|
|
665
|
+
// the winning handler owns the outcome (and re-fires any abort
|
|
666
|
+
// idempotently), so there's no point paying the abort-commit latency.
|
|
667
|
+
if (optimisticStart) {
|
|
668
|
+
const reconcile = await reconcileOptimisticStart();
|
|
669
|
+
if (reconcile)
|
|
670
|
+
return reconcile;
|
|
671
|
+
// Barrier resolved — the step-start POST timestamp is now known, so
|
|
672
|
+
// RSFS can be attached to the step_completed event below.
|
|
673
|
+
backfillOptimisticRsfs();
|
|
674
|
+
}
|
|
675
|
+
// Commit must-be-durable ops (e.g. a step-initiated abort's
|
|
676
|
+
// hook_received event) before writing step_completed, so any workflow
|
|
677
|
+
// continuation triggered by that event observes the abort rather than
|
|
678
|
+
// racing it. These ops swallow their own errors, so awaiting only
|
|
679
|
+
// enforces ordering (the `.catch()` defends the no-reject contract on
|
|
680
|
+
// StepContext.preCompletionOps).
|
|
681
|
+
//
|
|
682
|
+
// Tradeoff: correctness requires the hook be durable before completion,
|
|
683
|
+
// so — unlike the background `ops` flush above — this cannot be capped
|
|
684
|
+
// with a resolve-on-timeout race. A slow resume therefore adds its
|
|
685
|
+
// latency to a step that aborts a controller, and a true hang holds
|
|
686
|
+
// completion until the platform/queue execution timeout fires; the queue
|
|
687
|
+
// then redelivers and the step retries, re-firing the abort idempotently.
|
|
688
|
+
if (preCompletionOps.length > 0) {
|
|
689
|
+
await Promise.all(preCompletionOps).catch(() => { });
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
catch (err) {
|
|
693
|
+
// Optimistic start: the body threw before `step_started` was confirmed.
|
|
694
|
+
// Reconcile first — if we lost the create-claim (or the run is
|
|
695
|
+
// gone/throttled) the body error is moot; discard it and don't write a
|
|
696
|
+
// terminal event (the winning handler owns the outcome). Reconcile
|
|
697
|
+
// before draining preCompletionOps for the same reason as the success
|
|
698
|
+
// path: a discarded outcome doesn't need the abort committed here.
|
|
699
|
+
if (optimisticStart) {
|
|
700
|
+
const reconcile = await reconcileOptimisticStart();
|
|
701
|
+
if (reconcile)
|
|
702
|
+
return reconcile;
|
|
703
|
+
// Barrier resolved — attach RSFS to the step_failed event(s) below.
|
|
704
|
+
backfillOptimisticRsfs();
|
|
705
|
+
}
|
|
706
|
+
// Order any must-be-durable ops (e.g. a step-initiated abort's
|
|
707
|
+
// hook_received event) ahead of step_failed too — a step that aborts and
|
|
708
|
+
// then throws must still have the abort recorded before the failure
|
|
709
|
+
// continuation observes it. Same latency tradeoff and no-reject contract
|
|
710
|
+
// as the success path above. See StepContext.preCompletionOps.
|
|
711
|
+
if (preCompletionOps.length > 0) {
|
|
712
|
+
await Promise.all(preCompletionOps).catch(() => { });
|
|
713
|
+
}
|
|
714
|
+
const effectiveErr = promoteAbortErrorToFatal(err);
|
|
715
|
+
const normalizedError = await normalizeUnknownError(effectiveErr);
|
|
716
|
+
const normalizedStack = normalizedError.stack || getErrorStack(effectiveErr) || '';
|
|
717
|
+
if (effectiveErr instanceof Error) {
|
|
718
|
+
span?.recordException?.(effectiveErr);
|
|
719
|
+
}
|
|
720
|
+
const isFatal = FatalError.is(effectiveErr);
|
|
721
|
+
span?.setAttributes({
|
|
722
|
+
...Attribute.StepErrorName(getErrorName(effectiveErr)),
|
|
723
|
+
...Attribute.StepErrorMessage(normalizedError.message),
|
|
724
|
+
...Attribute.ErrorType(getErrorName(effectiveErr)),
|
|
725
|
+
...Attribute.ErrorCategory(isFatal
|
|
726
|
+
? 'fatal'
|
|
727
|
+
: RetryableError.is(effectiveErr)
|
|
728
|
+
? 'retryable'
|
|
729
|
+
: 'transient'),
|
|
730
|
+
...Attribute.ErrorRetryable(!isFatal),
|
|
731
|
+
});
|
|
732
|
+
if (RunExpiredError.is(err)) {
|
|
733
|
+
stepLogger.info('Workflow run already completed, skipping step', {
|
|
734
|
+
workflowRunId,
|
|
735
|
+
stepId,
|
|
736
|
+
message: err.message,
|
|
737
|
+
});
|
|
738
|
+
return { type: 'gone' };
|
|
739
|
+
}
|
|
740
|
+
if (isFatal) {
|
|
741
|
+
stepLogger.error('Encountered FatalError while executing step, bubbling up to parent workflow', { workflowRunId, stepName, errorStack: normalizedStack });
|
|
742
|
+
// Apply the normalized stack to the thrown value so the serialized
|
|
743
|
+
// error preserves it for consumers. `types.isNativeError()` works
|
|
744
|
+
// across VM realms (a workflow-thrown error is an instance of the
|
|
745
|
+
// VM's Error class, not the host's).
|
|
746
|
+
if (types.isNativeError(effectiveErr) && normalizedStack) {
|
|
747
|
+
effectiveErr.stack = normalizedStack;
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
await createEvent({
|
|
751
|
+
eventType: 'step_failed',
|
|
752
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
753
|
+
correlationId: stepId,
|
|
754
|
+
eventData: {
|
|
755
|
+
stepName,
|
|
756
|
+
error: await dehydrateStepError(effectiveErr, workflowRunId, await getEncryptionKey(), [], globalThis, compression),
|
|
757
|
+
...latencyEventData,
|
|
758
|
+
},
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
catch (stepFailErr) {
|
|
762
|
+
if (EntityConflictError.is(stepFailErr)) {
|
|
763
|
+
runtimeLogger.info('Tried failing step, but step has already finished.', {
|
|
764
|
+
workflowRunId,
|
|
765
|
+
stepId,
|
|
766
|
+
stepName,
|
|
767
|
+
message: stepFailErr.message,
|
|
768
|
+
});
|
|
769
|
+
return { type: 'skipped' };
|
|
770
|
+
}
|
|
771
|
+
throw stepFailErr;
|
|
772
|
+
}
|
|
773
|
+
span?.setAttributes({
|
|
774
|
+
...Attribute.StepStatus('failed'),
|
|
775
|
+
...Attribute.StepFatalError(true),
|
|
776
|
+
});
|
|
777
|
+
return { type: 'failed' };
|
|
778
|
+
}
|
|
779
|
+
// Non-fatal error: check if retries remaining
|
|
780
|
+
const currentAttempt = step.attempt;
|
|
781
|
+
span?.setAttributes({
|
|
782
|
+
...Attribute.StepAttempt(currentAttempt),
|
|
783
|
+
...Attribute.StepMaxRetries(maxRetries),
|
|
784
|
+
});
|
|
785
|
+
if (currentAttempt >= maxRetries + 1) {
|
|
786
|
+
// Max retries reached
|
|
787
|
+
const retryCount = step.attempt - 1;
|
|
788
|
+
stepLogger.error('Max retries reached, bubbling error to parent workflow', {
|
|
789
|
+
workflowRunId,
|
|
790
|
+
stepName,
|
|
791
|
+
attempt: step.attempt,
|
|
792
|
+
retryCount,
|
|
793
|
+
errorStack: normalizedStack,
|
|
794
|
+
});
|
|
795
|
+
const errorMessage = `Step "${stepName}" failed after ${maxRetries} ${pluralize('retry', 'retries', maxRetries)}: ${normalizedError.message}`;
|
|
796
|
+
// Wrap the original thrown value as `cause` on a fresh FatalError
|
|
797
|
+
// so the wrapping message + retry-count framing is the user-facing
|
|
798
|
+
// error while the original failure remains recoverable from
|
|
799
|
+
// `err.cause` after hydration.
|
|
800
|
+
const wrappedError = new FatalError(errorMessage);
|
|
801
|
+
wrappedError.cause = err;
|
|
802
|
+
if (normalizedStack)
|
|
803
|
+
wrappedError.stack = normalizedStack;
|
|
804
|
+
try {
|
|
805
|
+
await createEvent({
|
|
806
|
+
eventType: 'step_failed',
|
|
807
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
808
|
+
correlationId: stepId,
|
|
809
|
+
eventData: {
|
|
810
|
+
stepName,
|
|
811
|
+
error: await dehydrateStepError(wrappedError, workflowRunId, await getEncryptionKey(), [], globalThis, compression),
|
|
812
|
+
...latencyEventData,
|
|
813
|
+
},
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
catch (stepFailErr) {
|
|
817
|
+
if (EntityConflictError.is(stepFailErr)) {
|
|
818
|
+
runtimeLogger.info('Tried failing step, but step has already finished.', {
|
|
819
|
+
workflowRunId,
|
|
820
|
+
stepId,
|
|
821
|
+
stepName,
|
|
822
|
+
message: stepFailErr.message,
|
|
823
|
+
});
|
|
824
|
+
return { type: 'skipped' };
|
|
825
|
+
}
|
|
826
|
+
throw stepFailErr;
|
|
827
|
+
}
|
|
828
|
+
span?.setAttributes({
|
|
829
|
+
...Attribute.StepStatus('failed'),
|
|
830
|
+
...Attribute.StepRetryExhausted(true),
|
|
831
|
+
});
|
|
832
|
+
return { type: 'failed' };
|
|
833
|
+
}
|
|
834
|
+
// Retries remaining
|
|
835
|
+
if (RetryableError.is(err)) {
|
|
836
|
+
stepLogger.info('Encountered RetryableError, step will be retried', {
|
|
837
|
+
workflowRunId,
|
|
838
|
+
stepName,
|
|
839
|
+
attempt: currentAttempt,
|
|
840
|
+
message: err.message,
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
stepLogger.info('Encountered Error, step will be retried', {
|
|
845
|
+
workflowRunId,
|
|
846
|
+
stepName,
|
|
847
|
+
attempt: currentAttempt,
|
|
848
|
+
errorStack: normalizedStack,
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
// Apply the normalized stack to the thrown value so it survives
|
|
852
|
+
// serialization. See the FatalError site above for why we use
|
|
853
|
+
// `types.isNativeError` instead of `err instanceof Error`.
|
|
854
|
+
if (types.isNativeError(err) && normalizedStack) {
|
|
855
|
+
err.stack = normalizedStack;
|
|
856
|
+
}
|
|
857
|
+
try {
|
|
858
|
+
await createEvent({
|
|
859
|
+
eventType: 'step_retrying',
|
|
860
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
861
|
+
correlationId: stepId,
|
|
862
|
+
eventData: {
|
|
863
|
+
stepName,
|
|
864
|
+
error: await dehydrateStepError(err, workflowRunId, await getEncryptionKey(), [], globalThis, compression),
|
|
865
|
+
...(RetryableError.is(err) && { retryAfter: err.retryAfter }),
|
|
866
|
+
},
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
catch (stepRetryErr) {
|
|
870
|
+
if (EntityConflictError.is(stepRetryErr)) {
|
|
871
|
+
runtimeLogger.info('Tried retrying step, but step has already finished.', {
|
|
872
|
+
workflowRunId,
|
|
873
|
+
stepId,
|
|
874
|
+
stepName,
|
|
875
|
+
message: stepRetryErr.message,
|
|
876
|
+
});
|
|
877
|
+
return { type: 'skipped' };
|
|
878
|
+
}
|
|
879
|
+
throw stepRetryErr;
|
|
880
|
+
}
|
|
881
|
+
const timeoutSeconds = Math.max(1, RetryableError.is(err)
|
|
882
|
+
? Math.ceil((+err.retryAfter.getTime() - Date.now()) / 1000)
|
|
883
|
+
: 1);
|
|
884
|
+
span?.setAttributes({
|
|
885
|
+
...Attribute.StepRetryTimeoutSeconds(timeoutSeconds),
|
|
886
|
+
...Attribute.StepRetryWillRetry(true),
|
|
887
|
+
});
|
|
888
|
+
return { type: 'retry', timeoutSeconds };
|
|
889
|
+
}
|
|
890
|
+
// Create step_completed event outside the step execution failure path:
|
|
891
|
+
// persistence failures are infrastructure errors and should redeliver the
|
|
892
|
+
// queue message, not become user step_retrying/step_failed events.
|
|
893
|
+
let completedResult;
|
|
894
|
+
try {
|
|
895
|
+
completedResult = await createEvent({
|
|
896
|
+
eventType: 'step_completed',
|
|
897
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
898
|
+
correlationId: stepId,
|
|
899
|
+
eventData: {
|
|
900
|
+
stepName,
|
|
901
|
+
workflowName,
|
|
902
|
+
result: result,
|
|
903
|
+
...latencyEventData,
|
|
904
|
+
},
|
|
905
|
+
}, params.inlineDeltaSinceCursor !== undefined
|
|
906
|
+
? { sinceCursor: params.inlineDeltaSinceCursor }
|
|
907
|
+
: undefined);
|
|
908
|
+
}
|
|
909
|
+
catch (err) {
|
|
910
|
+
if (EntityConflictError.is(err)) {
|
|
911
|
+
runtimeLogger.info('Tried completing step, but step has already finished.', {
|
|
912
|
+
workflowRunId,
|
|
913
|
+
stepId,
|
|
914
|
+
stepName,
|
|
915
|
+
message: err.message,
|
|
916
|
+
});
|
|
917
|
+
return { type: 'skipped' };
|
|
918
|
+
}
|
|
919
|
+
if (RunExpiredError.is(err)) {
|
|
920
|
+
stepLogger.info('Workflow run already completed, skipping step', {
|
|
921
|
+
workflowRunId,
|
|
922
|
+
stepId,
|
|
923
|
+
message: err.message,
|
|
924
|
+
});
|
|
925
|
+
return { type: 'gone' };
|
|
926
|
+
}
|
|
927
|
+
throw err;
|
|
928
|
+
}
|
|
929
|
+
const inlineDelta = extractInlineDelta(completedResult, params.inlineDeltaSinceCursor !== undefined);
|
|
930
|
+
span?.setAttributes({
|
|
931
|
+
...Attribute.StepStatus('completed'),
|
|
932
|
+
...Attribute.StepResultType(typeof result),
|
|
933
|
+
});
|
|
934
|
+
if (ops.length > 0) {
|
|
935
|
+
stepLogger.debug('Step has pending ops', {
|
|
936
|
+
workflowRunId,
|
|
937
|
+
stepName,
|
|
938
|
+
opsCount: ops.length,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
// hasPendingOps signals the combined handler to break the loop
|
|
942
|
+
// and queue a continuation so waitUntil can flush them.
|
|
943
|
+
return { type: 'completed', hasPendingOps: !opsSettled, inlineDelta };
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC1leGVjdXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL3N0ZXAtZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLFVBQVUsRUFDVixjQUFjLEVBQ2QsZUFBZSxFQUNmLGFBQWEsRUFDYixhQUFhLEVBQ2Isb0JBQW9CLEdBQ3JCLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUNMLHFCQUFxQixFQUNyQixTQUFTLEVBQ1QsZUFBZSxHQUNoQixNQUFNLGlCQUFpQixDQUFDO0FBVXpCLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsaUNBQWlDLEdBQ2xDLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDekQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUVoRCxPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLGtCQUFrQixFQUNsQix3QkFBd0IsRUFDeEIsb0JBQW9CLEVBQ3BCLGdCQUFnQixHQUNqQixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM1RCxPQUFPLEtBQUssU0FBUyxNQUFNLHNDQUFzQyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN4QyxPQUFPLEVBQ0wsWUFBWSxFQUNaLGFBQWEsRUFDYixxQkFBcUIsRUFDckIsd0JBQXdCLEdBQ3pCLE1BQU0sYUFBYSxDQUFDO0FBQ3JCLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzVELE9BQU8sRUFDTCw4QkFBOEIsRUFDOUIseUNBQXlDLEdBQzFDLE1BQU0sZ0JBQWdCLENBQUM7QUFD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QUMsT0FBTyxNQUFNLENBQUM7U0FDM0MsQ0FBQyxDQUFDO1FBRUgsSUFBSSxHQUFHLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ25CLFVBQVUsQ0FBQyxLQUFLLENBQUMsc0JBQXNCLEVBQUU7Z0JBQ3ZDLGFBQWE7Z0JBQ2IsUUFBUTtnQkFDUixRQUFRLEVBQUUsR0FBRyxDQUFDLE1BQU07YUFDckIsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUNELCtEQUErRDtRQUMvRCx3REFBd0Q7UUFDeEQsT0FBTyxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxDQUFDO0lBQ3hFLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHR5cGVzIH0gZnJvbSAnbm9kZTp1dGlsJztcbmltcG9ydCB7XG4gIEVudGl0eUNvbmZsaWN0RXJyb3IsXG4gIEZhdGFsRXJyb3IsXG4gIFJldHJ5YWJsZUVycm9yLFxuICBSdW5FeHBpcmVkRXJyb3IsXG4gIFRocm90dGxlRXJyb3IsXG4gIFRvb0Vhcmx5RXJyb3IsXG4gIFdvcmtmbG93UnVudGltZUVycm9yLFxufSBmcm9tICdAd29ya2Zsb3cvZXJyb3JzJztcbmltcG9ydCB7XG4gIGNyZWF0ZVdvcmtmbG93QmFzZVVybCxcbiAgcGx1cmFsaXplLFxuICBzdGVwRGlzcGxheU5hbWUsXG59IGZyb20gJ0B3b3JrZmxvdy91dGlscyc7XG5pbXBvcnQgdHlwZSB7XG4gIENyZWF0ZUV2ZW50UGFyYW1zLFxuICBDcmVhdGVFdmVudFJlcXVlc3QsXG4gIEV2ZW50LFxuICBFdmVudFJlc3VsdCxcbiAgU2VyaWFsaXplZERhdGEsXG4gIFN0YXJ0ZWRTdGVwLFxuICBXb3JsZCxcbn0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcbmltcG9ydCB7XG4gIFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICBTUEVDX1ZFUlNJT05fU1VQUE9SVFNfQ09NUFJFU1NJT04sXG59IGZyb20gJ0B3b3JrZmxvdy93b3JsZCc7XG5pbXBvcnQgeyBydW50aW1lTG9nZ2VyLCBzdGVwTG9nZ2VyIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IGdldFN0ZXBGdW5jdGlvbiB9IGZyb20gJy4uL3ByaXZhdGUuanMnO1xuaW1wb3J0IHR5cGUgeyBQYXlsb2FkS2V5IH0gZnJvbSAnLi4vc2VyaWFsaXphdGlvbi9lbmNyeXB0aW9uLmpzJztcbmltcG9ydCB7XG4gIGNhbmNlbEFib3J0UmVhZGVycyxcbiAgZGVoeWRyYXRlU3RlcEVycm9yLFxuICBkZWh5ZHJhdGVTdGVwUmV0dXJuVmFsdWUsXG4gIGh5ZHJhdGVTdGVwQXJndW1lbnRzLFxuICBoeWRyYXRlU3RlcEVycm9yLFxufSBmcm9tICcuLi9zZXJpYWxpemF0aW9uLmpzJztcbmltcG9ydCB7IGNvbnRleHRTdG9yYWdlIH0gZnJvbSAnLi4vc3RlcC9jb250ZXh0LXN0b3JhZ2UuanMnO1xuaW1wb3J0ICogYXMgQXR0cmlidXRlIGZyb20gJy4uL3RlbGVtZXRyeS9zZW1hbnRpYy1jb252ZW50aW9ucy5qcyc7XG5pbXBvcnQgeyB0cmFjZSB9IGZyb20gJy4uL3RlbGVtZXRyeS5qcyc7XG5pbXBvcnQge1xuICBnZXRFcnJvck5hbWUsXG4gIGdldEVycm9yU3RhY2ssXG4gIG5vcm1hbGl6ZVVua25vd25FcnJvcixcbiAgcHJvbW90ZUFib3J0RXJyb3JUb0ZhdGFsLFxufSBmcm9tICcuLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBDT01QVVRFX0lOU1RBTkNFX0lEIH0gZnJvbSAnLi9jb21wdXRlLWluc3RhbmNlLmpzJztcbmltcG9ydCB7XG4gIGlzT3B0aW1pc3RpY0lubGluZVN0YXJ0RW5hYmxlZCxcbiAgaXNPcHRpbWlzdGljSW5saW5lU3RhcnRFeHBsaWNpdGx5RGlzYWJsZWQsXG59IGZyb20gJy4vY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IGdldFBvcnRMYXp5IH0gZnJvbSAnLi9nZXQtcG9ydC1sYXp5LmpzJztcbmltcG9ydCB7XG4gIG1lbW9pemVFbmNyeXB0aW9uS2V5LFxuICB0eXBlIFByZWNvbmRpdGlvblNuYXBzaG90UGFyYW1zLFxufSBmcm9tICcuL2hlbHBlcnMuanMnO1xuaW1wb3J0IHsgUmVwbGF5UmVjb3ZlcnlSZXBvcnRlciB9IGZyb20gJy4vcmVwbGF5LXJlY292ZXJ5LXJlcG9ydGVyLmpzJztcbmltcG9ydCB7XG4gIGNvbXB1dGVTdGVwTGF0ZW5jeUV2ZW50RGF0YSxcbiAgdHlwZSBTdGVwTGF0ZW5jeUV2ZW50RGF0YSxcbiAgdHlwZSBTdGVwTGF0ZW5jeVRyYWNraW5nLFxufSBmcm9tICcuL3N0ZXAtbGF0ZW5jeS5qcyc7XG5pbXBvcnQgeyBzYWZlV2FpdFVudGlsIH0gZnJvbSAnLi93YWl0LXVudGlsLmpzJztcblxuZXhwb3J0IGNvbnN0IERFRkFVTFRfU1RFUF9NQVhfUkVUUklFUyA9IDM7XG5cbi8qKlxuICogRXh0cmFjdCB0aGUgaW5saW5lIGRlbHRhIGZyb20gYSBzdGVwLXRlcm1pbmFsIGBldmVudHMuY3JlYXRlYCByZXN1bHQsXG4gKiBpZiB0aGUgV29ybGQgcG9wdWxhdGVkIG9uZS4gQSBkZWx0YSBpcyBvbmx5IG1lYW5pbmdmdWwgd2hlbiB0aGUgY2FsbGVyXG4gKiByZXF1ZXN0ZWQgaXQgKGBzaW5jZUN1cnNvcmAgd2FzIHBhc3NlZCkgYW5kIHRoZSBXb3JsZCByZXR1cm5lZCBib3RoXG4gKiBgZXZlbnRzYCBhbmQgYSBgY3Vyc29yYDsgb3RoZXJ3aXNlIHdlIHJldHVybiB1bmRlZmluZWQgYW5kIHRoZSBjYWxsZXJcbiAqIGZhbGxzIGJhY2sgdG8gdGhlIG5vcm1hbCBpbmNyZW1lbnRhbCBgZXZlbnRzLmxpc3RgLiBgaGFzTW9yZWAgZGVmYXVsdHNcbiAqIHRvIGZhbHNlIChzaW5nbGUgcGFnZSkgd2hlbiB0aGUgV29ybGQgb21pdHMgaXQuXG4gKi9cbmZ1bmN0aW9uIGV4dHJhY3RJbmxpbmVEZWx0YShcbiAgcmVzdWx0OiB7IGV2ZW50cz86IEV2ZW50W107IGN1cnNvcj86IHN0cmluZyB8IG51bGw7IGhhc01vcmU/OiBib29sZWFuIH0sXG4gIHJlcXVlc3RlZDogYm9vbGVhblxuKTogSW5saW5lRXZlbnREZWx0YSB8IHVuZGVmaW5lZCB7XG4gIGlmICghcmVxdWVzdGVkKSByZXR1cm4gdW5kZWZpbmVkO1xuICBpZiAoIXJlc3VsdC5ldmVudHMgfHwgcmVzdWx0LmN1cnNvciA9PT0gdW5kZWZpbmVkKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4ge1xuICAgIGV2ZW50czogcmVzdWx0LmV2ZW50cyxcbiAgICBjdXJzb3I6IHJlc3VsdC5jdXJzb3IsXG4gICAgaGFzTW9yZTogcmVzdWx0Lmhhc01vcmUgPz8gZmFsc2UsXG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3RlcEV4ZWN1dG9yUGFyYW1zIHtcbiAgd29ybGQ6IFdvcmxkO1xuICB3b3JrZmxvd1J1bklkOiBzdHJpbmc7XG4gIC8qKiBEZXBsb3ltZW50IHRoYXQgb3ducyB0aGUgd29ya2Zsb3cgcnVuLCBmb3IgZm9yd2FyZGVkIHdyaXRhYmxlIHN0cmVhbXMuICovXG4gIHdvcmtmbG93RGVwbG95bWVudElkPzogc3RyaW5nO1xuICB3b3JrZmxvd05hbWU6IHN0cmluZztcbiAgd29ya2Zsb3dTdGFydGVkQXQ6IG51bWJlcjtcbiAgLyoqIFJvb3QgcnVuIGlkIG9mIHRoaXMgcnVuJ3MgbGluZWFnZSwgY2FycmllZCBpbnRvIHRoZSBzdGVwIGNvbnRleHQuICovXG4gIHJvb3RSdW5JZD86IHN0cmluZztcbiAgc3RlcElkOiBzdHJpbmc7XG4gIHN0ZXBOYW1lOiBzdHJpbmc7XG4gIGVuY3J5cHRpb25LZXk/OiBQYXlsb2FkS2V5O1xuICAvKipcbiAgICogVGhlIHdvcmtmbG93IHJ1bidzIHNwZWNWZXJzaW9uLCB1c2VkIHRvIGdhdGUgcGF5bG9hZCBjb21wcmVzc2lvbi5cbiAgICogU3RlcCBvdXRwdXRzL2Vycm9ycyBhcmUgb25seSBjb21wcmVzc2VkIHdoZW4gdGhlIHJ1biBpcyBtYXJrZWQgYXNcbiAgICogY29tcHJlc3Npb24tY2FwYWJsZSAoc3BlY1ZlcnNpb24gPj0gNSkuXG4gICAqL1xuICBydW5TcGVjVmVyc2lvbj86IG51bWJlcjtcbiAgLyoqXG4gICAqIExhenkgc3RlcCBzdGFydDogdGhlIGFscmVhZHktZGVoeWRyYXRlZCBzdGVwIGlucHV0LiBXaGVuIHByb3ZpZGVkLCB0aGVcbiAgICogYHN0ZXBfc3RhcnRlZGAgZXZlbnQgY2FycmllcyB0aGlzIGlucHV0IHNvIHRoZSB3b3JsZCBjcmVhdGVzIHRoZSBzdGVwIG9uXG4gICAqIHRoZSBmbHkgKG5vIHNlcGFyYXRlIGBzdGVwX2NyZWF0ZWRgIHJvdW5kLXRyaXApLiBTZXQgYnkgdGhlIG93bmVkLWlubGluZVxuICAgKiBwYXRoIGZvciB0aGUgc3RlcCB3aG9zZSBgc3RlcF9jcmVhdGVkYCB0aGUgc3VzcGVuc2lvbiBoYW5kbGVyIGRlZmVycmVkLlxuICAgKiBUaGUgd29ybGQncyBhdG9taWMgY3JlYXRlLWNsYWltIGlzIHRoZSBleGFjdGx5LW9uZS1vd25lciBnYXRlOiBsb3NpbmcgaXRcbiAgICogc3VyZmFjZXMgYXMgYEVudGl0eUNvbmZsaWN0RXJyb3JgIOKGkiBgeyB0eXBlOiAnc2tpcHBlZCcgfWAsIHNvIGEgaGFuZGxlclxuICAgKiB0aGF0IGRpZCBub3Qgd2luIHRoZSBjcmVhdGUgbmV2ZXIgcnVucyB0aGUgYm9keS4gT21pdHRlZCBvbiBldmVyeSBvdGhlclxuICAgKiBwYXRoLCB3aGVyZSB0aGUgc3RlcCBhbHJlYWR5IGhhcyBhIGBzdGVwX2NyZWF0ZWRgIGFuZCBgc3RlcF9zdGFydGVkYFxuICAgKiBjYXJyaWVzIG5vIHBheWxvYWQgKHRoZSBsZWdhY3kgY29udHJhY3QpLlxuICAgKi9cbiAgbGF6eVN0ZXBJbnB1dD86IFNlcmlhbGl6ZWREYXRhO1xuICAvKipcbiAgICogSW5saW5lIHN0ZXAgb3duZXJzaGlwOiB0aGUgcXVldWUgbWVzc2FnZSBJRCBvZiB0aGUgaW52b2NhdGlvbiB0aGlzXG4gICAqIGV4ZWN1dGVTdGVwIGNhbGwgcnVucyBpbiAoZnJvbSB0aGUgcXVldWUgaGFuZGxlcidzIG1ldGEpLiBXaGVuIHNldCwgdGhlXG4gICAqIGBzdGVwX3N0YXJ0ZWRgIHRoaXMgY2FsbCBzZW5kcyBpcyBzdGFtcGVkIHdpdGggaXQg4oCUIG9uIHRoZSBsYXp5IHBhdGhzXG4gICAqICh3aGVyZSBgbGF6eVN0ZXBJbnB1dGAgaXMgcHJlc2VudCkgYW5kIG9uIHRoZSBvd25lZC1yZWNvdmVyeVxuICAgKiBwYXlsb2FkLWxlc3Mgc3RhcnQgKHdoZXJlIGl0IGlzIG5vdDsgdGhlIHJlLXN0YW1wIGtlZXBzIGEgcmVjb3ZlcmVkXG4gICAqIHN0ZXAgcmVhZGFibGUgYXMgb3duZWQgYnkgdGhpcyBtZXNzYWdlLCBzaW5jZSBvd25lcnNoaXAgZGVyaXZlcyBmcm9tXG4gICAqIHRoZSBMQVRFU1Qgc3RhcnQg4oCUIHNvIGEgcmVjb3Zlcnkgc3RhcnQgaXMgbmV2ZXIgYmFyZSkuIFdha2VcbiAgICogcmVwbGF5cyB0aGF0IG9ic2VydmUgYW4gYWN0aXZlbHktb3duZWQgc3RlcCBzdXBwcmVzcyB0aGUgaW1tZWRpYXRlXG4gICAqIHJlcXVldWUgYW5kIGVucXVldWUgYSBkZWxheWVkIGJhY2tzdG9wIGluc3RlYWQuIE9taXR0ZWQgb24gdGhlXG4gICAqIGJhY2tncm91bmQtc3RlcCBwYXRoLCB3aG9zZSBiYXJlIHN0YXJ0IGludGVudGlvbmFsbHkgY2xlYXJzIG93bmVyc2hpcFxuICAgKiAodGhlIHN0ZXAgaXMgcXVldWUtb3duZWQgZnJvbSB0aGF0IHBvaW50KS5cbiAgICovXG4gIG93bmVyTWVzc2FnZUlkPzogc3RyaW5nO1xuICAvKipcbiAgICogSW5saW5lLWRlbHRhIG9wdGltaXphdGlvbiAob3B0LWluKS4gV2hlbiBwcm92aWRlZCwgdGhlIGN1cnNvciBvZiB0aGVcbiAgICogZXZlbnQgbG9nIGFzIG9ic2VydmVkIGJ5IHRoZSBjYWxsZXIgKmJlZm9yZSogdGhpcyBzdGVwJ3MgZXZlbnRzIHdlcmVcbiAgICogd3JpdHRlbi4gSXQgaXMgdGhyZWFkZWQgaW50byB0aGUgc3RlcC10ZXJtaW5hbCBgZXZlbnRzLmNyZWF0ZWAgc28gYVxuICAgKiBzdXBwb3J0aW5nIFdvcmxkIGNhbiByZXR1cm4gdGhlIGRlbHRhIG9mIGV2ZW50cyB3cml0dGVuIHNpbmNlIChzZWVcbiAgICoge0BsaW5rIGltcG9ydCgnQHdvcmtmbG93L3dvcmxkJykuQ3JlYXRlRXZlbnRQYXJhbXMuc2luY2VDdXJzb3J9KS5cbiAgICogT25seSBzZXQgdGhpcyB3aGVuIGBjb3JyZWxhdGlvbklkYC1iYXNlZCBvd25lcnNoaXAgZ3VhcmFudGVlcyB0aGlzXG4gICAqIGhhbmRsZXIgaXMgdGhlIHNvbGUgaW5saW5lIHdyaXRlciBmb3IgdGhlIHJ1biBvbiB0aGlzIGl0ZXJhdGlvbi5cbiAgICovXG4gIGlubGluZURlbHRhU2luY2VDdXJzb3I/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBQcmVjb25kaXRpb24tZ3VhcmQgc25hcHNob3Qgb2YgdGhlIGV2ZW50IGxvZyB0aGUgY2FsbGVyJ3MgcmVwbGF5IGxvYWRlZCwgdG9cbiAgICogYXR0YWNoIHRvIHRoaXMgc3RlcCdzIGBzdGVwX3N0YXJ0ZWRgIGNsYWltLiBPbiB0aGUgbGF6eSBpbmxpbmUgcGF0aCB0aGVcbiAgICogY2xhaW0gaXMgdGhlIHN0ZXAncyBGSVJTVCBkdXJhYmxlIHdyaXRlIChpdHMgYHN0ZXBfY3JlYXRlZGAgaXMgZGVmZXJyZWQpLFxuICAgKiBzbyB3aXRob3V0IHRoaXMgdGhlIGNsYWltIHdvdWxkIGJ5cGFzcyB0aGUgb3B0aW1pc3RpYy1jb25jdXJyZW5jeSBndWFyZFxuICAgKiBlbnRpcmVseTogYSByZXBsYXkgd29ya2luZyBmcm9tIGEgc3RhbGUgdmlldyBjb3VsZCBjbGFpbSDigJQgYW5kIHRoZW4gY29tbWl0XG4gICAqIOKAlCBhIHN0ZXAgc2NoZWR1bGVkIHdpdGhvdXQgb2JzZXJ2aW5nIGFuIGV2ZW50IGl0IG5ldmVyIGxvYWRlZC4gQVxuICAgKiBndWFyZC1lbmZvcmNpbmcgV29ybGQgcmVqZWN0cyBhIHN0YWxlIGNsYWltIHdpdGggYFByZWNvbmRpdGlvbkZhaWxlZEVycm9yYFxuICAgKiAoNDEyKTsgZXhlY3V0ZVN0ZXAgZG9lcyBOT1QgdHJhbnNsYXRlIHRoYXQgcmVqZWN0aW9uIChyZS1jbGFpbWluZyBpbiBwbGFjZVxuICAgKiB3b3VsZCBzdGlsbCBjb21taXQgdGhlIHN0YWxlIHNjaGVkdWxlKSwgc28gaXQgcHJvcGFnYXRlcyBmb3IgdGhlIGNhbGxlciB0b1xuICAgKiBhYmFuZG9uIHRoZSBiYXRjaCBhbmQgcmVzdGFydCBpdHMgcmVwbGF5LiBVbmRlZmluZWQgd2hlbiB0aGUgZ3VhcmQgaXNcbiAgICogZGlzYWJsZWQgb3IgdGhlIGNhbGxlciBoYXMgbm8gc25hcHNob3Q7IFdvcmxkcyB0aGF0IGRvbid0IGVuZm9yY2UgdGhlIGd1YXJkXG4gICAqIGlnbm9yZSBpdC5cbiAgICovXG4gIHByZWNvbmRpdGlvblNuYXBzaG90PzogUHJlY29uZGl0aW9uU25hcHNob3RQYXJhbXM7XG4gIC8qKlxuICAgKiBTdXBwcmVzcyBvcHRpbWlzdGljIGlubGluZSBzdGFydCBmb3IgdGhpcyBzdGVwIHJlZ2FyZGxlc3Mgb2ZcbiAgICogYFdPUktGTE9XX09QVElNSVNUSUNfSU5MSU5FX1NUQVJUYCAvIGBmb3JjZU9wdGltaXN0aWNTdGFydGA6IHRha2UgdGhlXG4gICAqIGF3YWl0LXRoZW4tcnVuIHBhdGggc28gdGhlIGJvZHkgb25seSBydW5zIGFmdGVyIHRoZSBgc3RlcF9zdGFydGVkYCBjbGFpbVxuICAgKiBzdWNjZWVkcy4gU2V0IGJ5IHRoZSBydW50aW1lIGZvciBndWFyZC1lbmZvcmNlZCBiYXRjaGVzIHRoYXQgYXJlXG4gICAqIHN0YWxlLXNlbnNpdGl2ZSAoYW4gb3BlbiBob29rIG1lYW5zIGFuIG91dC1vZi1iYW5kIGV2ZW50IGNhbiBtYWtlIHRoZVxuICAgKiBzY2hlZHVsaW5nIHZpZXcgc3RhbGUpOiB0aGUgZ3VhcmQncyA0MTIgZmVuY2UgY2FuIHJlamVjdCBhIHN0YWxlIGNsYWltJ3NcbiAgICogZHVyYWJsZSB3cml0ZXMsIGJ1dCBpdCBjYW5ub3QgdW4tcnVuIGEgYm9keSB0aGF0IG9wdGltaXN0aWMgc3RhcnQgYmVnYW5cbiAgICogYmVmb3JlIHRoZSBjbGFpbSBzZXR0bGVkIOKAlCBhd2FpdGluZyB0aGUgY2xhaW0gZXh0ZW5kcyB0aGUgZmVuY2UgdG8gdXNlclxuICAgKiBjb2RlLiBXaW5zIG92ZXIgYGZvcmNlT3B0aW1pc3RpY1N0YXJ0YCBhbmQgdGhlIGVudiBmbGFnLlxuICAgKi9cbiAgc3VwcHJlc3NPcHRpbWlzdGljU3RhcnQ/OiBib29sZWFuO1xuICAvKipcbiAgICogRm9yY2Ugb3B0aW1pc3RpYyBpbmxpbmUgc3RhcnQgcmVnYXJkbGVzcyBvZlxuICAgKiBgV09SS0ZMT1dfT1BUSU1JU1RJQ19JTkxJTkVfU1RBUlRgLiBTZXQgYnkgdHVyYm8gbW9kZSBvbiB0aGUgZmlyc3QgZGVsaXZlcnlcbiAgICogb2YgdGhlIGZpcnN0IGludm9jYXRpb24sIHdoZXJlIGZvcmNpbmcgaXQgaXMgc2FmZTogdGhlcmUgaXMgbm8gY29uY3VycmVudFxuICAgKiBwZWVyIGhhbmRsZXIgdG8gcmFjZSB0aGUgY3JlYXRlLWNsYWltLCBzbyB0aGUgYm9keSBydW5zIGV4YWN0bHkgb25jZS4gT25seVxuICAgKiBtZWFuaW5nZnVsIHRvZ2V0aGVyIHdpdGggYGxhenlTdGVwSW5wdXRgIChhIGJyYW5kLW5ldyBsYXp5IHN0ZXApLlxuICAgKi9cbiAgZm9yY2VPcHRpbWlzdGljU3RhcnQ/OiBib29sZWFuO1xuICAvKipcbiAgICogVHVyYm8gbW9kZSBvbmx5OiBhIHByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBiYWNrZ3JvdW5kZWRcbiAgICogYHJ1bl9zdGFydGVkYCBoYXMgbGFuZGVkLiBXaGVuIHNldCwgdGhlIGxhenkvb3B0aW1pc3RpYyBgc3RlcF9zdGFydGVkYCBpc1xuICAgKiBjaGFpbmVkIG9uIGl0IHNvIHRoZSBzdGVwIGlzIG5ldmVyIGNyZWF0ZWQgYmVmb3JlIGl0cyBydW4gZXhpc3RzLiBUaGUgYm9keVxuICAgKiBzdGlsbCBydW5zIGltbWVkaWF0ZWx5IGFnYWluc3QgbG9jYWxseS1zeW50aGVzaXplZCBzdGF0ZSDigJQgb25seSB0aGUgbmV0d29ya1xuICAgKiB3cml0ZSB3YWl0cyDigJQgc28gdGhlIGBydW5fc3RhcnRlZGAgcm91bmQtdHJpcCBvdmVybGFwcyB0aGUgYm9keS4gYHVuZGVmaW5lZGBcbiAgICogb3V0c2lkZSB0dXJibywgd2hlcmUgYHJ1bl9zdGFydGVkYCB3YXMgYWxyZWFkeSBhd2FpdGVkIHVwIGZyb250LlxuICAgKi9cbiAgcnVuUmVhZHlCYXJyaWVyPzogUHJvbWlzZTx1bmtub3duPjtcbiAgLyoqXG4gICAqIExhdGVuY3kgdGVsZW1ldHJ5IChUVEZTIC8gU1RTTyk6IGVsaWdpYmlsaXR5IGFuZCBhbmNob3IgdGltZXN0YW1wcyBkZWNpZGVkXG4gICAqIGJ5IHRoZSBvcmNoZXN0cmF0b3IuIFdoZW4gc2V0LCB0aGlzIGV4ZWN1dG9yIGNvbXB1dGVzIHRoZSBmaW5hbCB2YWx1ZXNcbiAgICogYWdhaW5zdCB0aGUgd2FsbCBjbG9jayB0YWtlbiBpbW1lZGlhdGVseSBiZWZvcmUgdXNlciBjb2RlIHJ1bnMgYW5kXG4gICAqIGF0dGFjaGVzIHRoZW0gdG8gdGhlIHN0ZXAncyB0ZXJtaW5hbCBldmVudC4gU2V0IG9ubHkgZm9yIHRoZSBmaXJzdCBzdGVwIG9mXG4gICAqIGFuIGlubGluZSBiYXRjaCwgYW5kIG9ubHkgb24gZmlyc3QtYXR0ZW1wdCBleGVjdXRpb25zIHRoYXQgcXVhbGlmeSDigJQgc2VlXG4gICAqIHJ1bnRpbWUvc3RlcC1sYXRlbmN5LnRzLlxuICAgKi9cbiAgbGF0ZW5jeVRyYWNraW5nPzogU3RlcExhdGVuY3lUcmFja2luZztcbiAgLyoqXG4gICAqIEF1dGhvcml0YXRpdmUgYXR0ZW1wdCBudW1iZXIgZm9yIHRoaXMgZXhlY3V0aW9uLCB1c2VkIHRvIGJvdW5kIHJldHJpZXNcbiAgICogYWdhaW5zdCBgbWF4UmV0cmllc2AgQkVGT1JFIHRoZSBib2R5IHJ1bnMuIEluIG9yZGVyIHRvIGFsc28gY2F0Y2hcbiAgICogc3RlcCB0aW1lb3V0cyAod2hpY2ggd2UgY2FuJ3QgaGF2ZSBjYXRjaCBoYW5kbGVycyBmb3IpLCB3ZSBkZXRlcm1pbmVcbiAgICogdGhlIGF0dGVtcHQgY291bnQgYmFzZWQgYXMgZm9sbG93czpcbiAgICogLSBJbmxpbmUgKGNvbWJpbmVkIGhhbmRsZXIpOiB0aGUgbnVtYmVyIG9mIGBzdGVwX3N0YXJ0ZWRgIGV2ZW50cyBhbHJlYWR5XG4gICAqICAgaW4gdGhlIGV2ZW50IGxvZyBmb3IgdGhpcyBzdGVwLCBwbHVzIG9uZSBmb3IgdGhlIGF0dGVtcHQgYWJvdXQgdG8gcnVuLlxuICAgKiAtIEJhY2tncm91bmQgKHF1ZXVlLWRpc3BhdGNoZWQpOiB0aGUgcXVldWUgZGVsaXZlcnkgY291bnRcbiAgICogICAoYG1ldGFkYXRhLmF0dGVtcHRgKSwgd2hpY2ggaW5jcmVtZW50cyBvbiBldmVyeSByZWRlbGl2ZXJ5LlxuICAgKi9cbiAgYXV0aG9yaXRhdGl2ZUF0dGVtcHQ/OiBudW1iZXI7XG4gIC8qKiBPbmUtc2hvdCByZWNvdmVyeSB0ZWxlbWV0cnkgYWN0aXZhdGVkIGJ5IHRoZSBvcmNoZXN0cmF0b3IgcmVwbGF5LiAqL1xuICByZXBsYXlSZWNvdmVyeVJlcG9ydGVyPzogUmVwbGF5UmVjb3ZlcnlSZXBvcnRlcjtcbn1cblxuLyoqXG4gKiBJbmxpbmUtZGVsdGEgcmV0dXJuZWQgYnkgYSBzdGVwLXRlcm1pbmFsIHdyaXRlIHdoZW4gdGhlIGNhbGxlciBwYXNzZWRcbiAqIHtAbGluayBTdGVwRXhlY3V0b3JQYXJhbXMuaW5saW5lRGVsdGFTaW5jZUN1cnNvcn0gYW5kIHRoZSBXb3JsZCBzdXBwb3J0c1xuICogdGhlIG9wdGltaXphdGlvbi4gYGV2ZW50c2AgYXJlIHRoZSBldmVudHMgd3JpdHRlbiBzdHJpY3RseSBhZnRlciB0aGF0XG4gKiBjdXJzb3IgKHRoaXMgc3RlcCdzIGV2ZW50cyBwbHVzIGFueXRoaW5nIGludGVybGVhdmVkIGluLWJhbmQpLCBgY3Vyc29yYFxuICogaXMgdGhlIHBvc2l0aW9uIHBhc3QgdGhlIGxhc3Qgb25lLCBhbmQgYGhhc01vcmVgIHNpZ25hbHMgYSBmdXJ0aGVyIHBhZ2VcbiAqICh0aGUgY2FsbGVyIHRoZW4gZmFsbHMgYmFjayB0byBhIGZ1bGwgaW5jcmVtZW50YWwgZmV0Y2gpLiBBYnNlbnQgd2hlblxuICogdGhlIFdvcmxkIGRpZCBub3QgcmV0dXJuIGEgZGVsdGEuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5saW5lRXZlbnREZWx0YSB7XG4gIGV2ZW50czogRXZlbnRbXTtcbiAgY3Vyc29yOiBzdHJpbmcgfCBudWxsO1xuICBoYXNNb3JlOiBib29sZWFuO1xufVxuXG4vKipcbiAqIFJlc3VsdCBvZiBhIHN0ZXAgZXhlY3V0aW9uIGF0dGVtcHQuIFRoZSBjYWxsZXIgZGVjaWRlcyB3aGF0IHRvIGRvXG4gKiBiYXNlZCBvbiB0aGUgcmVzdWx0IHR5cGUgKGUuZy4sIHF1ZXVlIHdvcmtmbG93IGNvbnRpbnVhdGlvbiwgcmVwbGF5IGlubGluZSwgZXRjLikuXG4gKlxuICogYGlubGluZURlbHRhYCBpcyBhdHRhY2hlZCB0byBhIGBjb21wbGV0ZWRgIHJlc3VsdCB3aGVuIHRoZSBjYWxsZXJcbiAqIHJlcXVlc3RlZCBpdCB2aWEge0BsaW5rIFN0ZXBFeGVjdXRvclBhcmFtcy5pbmxpbmVEZWx0YVNpbmNlQ3Vyc29yfSBhbmRcbiAqIHRoZSBXb3JsZCByZXR1cm5lZCBvbmUuIFN0ZXAgZmFpbHVyZXMgYXJlIHJhcmUgYW5kIG5vdCB0aGUgaW5saW5lXG4gKiBvcHRpbWl6YXRpb24ncyB0YXJnZXQsIHNvIHRoZSBgZmFpbGVkYCBwYXRoIGxlYXZlcyBpdCB0byB0aGUgbm9ybWFsXG4gKiBpbmNyZW1lbnRhbCBmZXRjaC5cbiAqL1xuZXhwb3J0IHR5cGUgU3RlcEV4ZWN1dGlvblJlc3VsdCA9XG4gIHwge1xuICAgICAgdHlwZTogJ2NvbXBsZXRlZCc7XG4gICAgICBoYXNQZW5kaW5nT3BzPzogYm9vbGVhbjtcbiAgICAgIGlubGluZURlbHRhPzogSW5saW5lRXZlbnREZWx0YTtcbiAgICB9XG4gIHwgeyB0eXBlOiAnZmFpbGVkJyB9XG4gIHwgeyB0eXBlOiAncmV0cnknOyB0aW1lb3V0U2Vjb25kczogbnVtYmVyIH1cbiAgfCB7IHR5cGU6ICdza2lwcGVkJyB9XG4gIHwgeyB0eXBlOiAnZ29uZScgfVxuICB8IHsgdHlwZTogJ3Rocm90dGxlZCc7IHRpbWVvdXRTZWNvbmRzOiBudW1iZXIgfTtcblxuLyoqXG4gKiBFeGVjdXRlcyBhIHNpbmdsZSBzdGVwOiBjcmVhdGVzIHN0ZXBfc3RhcnRlZCBldmVudCwgaHlkcmF0ZXMgaW5wdXQsXG4gKiBydW5zIHRoZSBzdGVwIGZ1bmN0aW9uLCBjcmVhdGVzIHN0ZXBfY29tcGxldGVkL3N0ZXBfZmFpbGVkL3N0ZXBfcmV0cnlpbmcgZXZlbnRzLlxuICpcbiAqIERvZXMgTk9UIHF1ZXVlIHdvcmtmbG93IGNvbnRpbnVhdGlvbiBtZXNzYWdlcyDigJQgdGhlIGNhbGxlciBkZWNpZGVzIHdoYXQgdG8gZG8gbmV4dC5cbiAqIFVzZWQgYnkgdGhlIGNvbWJpbmVkIHdvcmtmbG93IGhhbmRsZXIgZm9yIHN0ZXAgZXhlY3V0aW9uLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZVN0ZXAoXG4gIHBhcmFtczogU3RlcEV4ZWN1dG9yUGFyYW1zXG4pOiBQcm9taXNlPFN0ZXBFeGVjdXRpb25SZXN1bHQ+IHtcbiAgY29uc3Qge1xuICAgIHdvcmxkLFxuICAgIHdvcmtmbG93UnVuSWQsXG4gICAgd29ya2Zsb3dOYW1lLFxuICAgIHdvcmtmbG93U3RhcnRlZEF0LFxuICAgIHN0ZXBJZCxcbiAgICBzdGVwTmFtZSxcbiAgfSA9IHBhcmFtcztcbiAgLy8gVHJ1dGhpbmVzcywgbm90IHByZXNlbmNlOiBgdmVyY2VsIGVudiBwdWxsYCB3cml0ZXMgYFZFUkNFTF9VUkw9XCJcImAgaW50b1xuICAvLyBgLmVudi5sb2NhbGAsIGFuZCBhIGZyYW1ld29yayB0aGF0IGxvYWRzIHRoYXQgZmlsZSBsb2NhbGx5IHdvdWxkIG90aGVyd2lzZVxuICAvLyBwdXQgdXMgb24gdGhlIFZlcmNlbCBicmFuY2ggd2l0aCBub3RoaW5nIHRvIGJ1aWxkIGEgaG9zdCBmcm9tLCBtYWtpbmdcbiAgLy8gYGh0dHBzOi8vYCB0aGUgYmFzZSBVUkwgb2YgZXZlcnkgc3RlcC5cbiAgY29uc3QgaXNWZXJjZWwgPSBCb29sZWFuKHByb2Nlc3MuZW52LlZFUkNFTF9VUkwpO1xuICAvLyBHYXRlIHBheWxvYWQgY29tcHJlc3Npb24gb24gdGhlIHJ1bidzIHNwZWNWZXJzaW9uLlxuICBjb25zdCBjb21wcmVzc2lvbiA9XG4gICAgKHBhcmFtcy5ydW5TcGVjVmVyc2lvbiA/PyAwKSA+PSBTUEVDX1ZFUlNJT05fU1VQUE9SVFNfQ09NUFJFU1NJT047XG4gIGNvbnN0IHJlcGxheVJlY292ZXJ5UmVwb3J0ZXIgPVxuICAgIHBhcmFtcy5yZXBsYXlSZWNvdmVyeVJlcG9ydGVyID8/IFJlcGxheVJlY292ZXJ5UmVwb3J0ZXIuaW5lcnQoKTtcbiAgY29uc3QgY3JlYXRlRXZlbnQgPSA8VCBleHRlbmRzIENyZWF0ZUV2ZW50UmVxdWVzdD4oXG4gICAgZGF0YTogVCxcbiAgICBldmVudFBhcmFtcz86IENyZWF0ZUV2ZW50UGFyYW1zXG4gICkgPT5cbiAgICByZXBsYXlSZWNvdmVyeVJlcG9ydGVyLndpdGhFdmVudENyZWF0ZShldmVudFBhcmFtcywgKHApID0+XG4gICAgICB3b3JsZC5ldmVudHMuY3JlYXRlKHdvcmtmbG93UnVuSWQsIGRhdGEsIHApXG4gICAgKTtcblxuICBjb25zdCBzcGFuTmFtZSA9IGBzdGVwLmV4ZWN1dGUgJHtzdGVwRGlzcGxheU5hbWUoc3RlcE5hbWUpfWA7XG4gIHJldHVybiB0cmFjZShzcGFuTmFtZSwge30sIGFzeW5jIChzcGFuKSA9PiB7XG4gICAgc3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAuLi5BdHRyaWJ1dGUuU3RlcE5hbWUoc3RlcE5hbWUpLFxuICAgICAgLi4uQXR0cmlidXRlLldvcmtmbG93TmFtZSh3b3JrZmxvd05hbWUpLFxuICAgICAgLi4uQXR0cmlidXRlLldvcmtmbG93UnVuSWQod29ya2Zsb3dSdW5JZCksXG4gICAgICAuLi5BdHRyaWJ1dGUuU3RlcElkKHN0ZXBJZCksXG4gICAgfSk7XG5cbiAgICAvLyBNZW1vaXplZCBhY2Nlc3NvciBmb3IgdGhlIHBlci1ydW4gZW5jcnlwdGlvbiBrZXkuIFRoZSBmaXJzdCBjYWxsZXJcbiAgICAvLyAoaW5wdXQgaHlkcmF0aW9uIG9uIHRoZSBzdWNjZXNzIHBhdGgsIG9yIG9uZSBvZiB0aGUgZWFybHktcmV0dXJuXG4gICAgLy8gZGVoeWRyYXRlU3RlcEVycm9yIHBhdGhzIGlmIHN0ZXBfc3RhcnRlZCBmYWlscykgdHJpZ2dlcnMgdGhlIGFjdHVhbFxuICAgIC8vIGZldGNoIC8gSEtERiBkZXJpdmF0aW9uOyBzdWJzZXF1ZW50IGNhbGxlcnMgYXdhaXQgdGhlIGNhY2hlZCBwcm9taXNlLlxuICAgIGNvbnN0IGdldEVuY3J5cHRpb25LZXkgPSBtZW1vaXplRW5jcnlwdGlvbktleSh3b3JsZCwgd29ya2Zsb3dSdW5JZCk7XG5cbiAgICBjb25zdCBzdGVwRm4gPSBnZXRTdGVwRnVuY3Rpb24oc3RlcE5hbWUpO1xuICAgIGlmICghc3RlcEZuIHx8IHR5cGVvZiBzdGVwRm4gIT09ICdmdW5jdGlvbicpIHtcbiAgICAgIC8vIFN0ZXAgZnVuY3Rpb24gbm90IHJlZ2lzdGVyZWQg4oCUIGZhaWwgdGhlIHN0ZXAgaW1tZWRpYXRlbHkgKG5vdCB0aGUgcnVuKS5cbiAgICAgIC8vIENyZWF0ZSBhIHN0ZXBfZmFpbGVkIGV2ZW50IHNvXG4gICAgICAvLyB0aGUgd29ya2Zsb3cgY2FuIGhhbmRsZSBpdCBncmFjZWZ1bGx5IHZpYSB0cnkvY2F0Y2ggaW4gdXNlciBjb2RlLlxuICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gYFN0ZXAgXCIke3N0ZXBOYW1lfVwiIGlzIG5vdCByZWdpc3RlcmVkIGluIHRoZSBjdXJyZW50IGRlcGxveW1lbnQuIFRoaXMgdXN1YWxseSBpbmRpY2F0ZXMgYSBidWlsZCBvciBidW5kbGluZyBpc3N1ZSB0aGF0IGNhdXNlZCB0aGUgc3RlcCB0byBub3QgYmUgaW5jbHVkZWQgaW4gdGhlIGRlcGxveW1lbnQuYDtcbiAgICAgIHJ1bnRpbWVMb2dnZXIuZXJyb3IoJ1N0ZXAgZnVuY3Rpb24gbm90IHJlZ2lzdGVyZWQsIGZhaWxpbmcgc3RlcCcsIHtcbiAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgIHN0ZXBJZCxcbiAgICAgIH0pO1xuICAgICAgLy8gT24gdGhlIGxhenkgaW5saW5lIHBhdGggdGhlIHN1c3BlbnNpb24gaGFuZGxlciBkZWZlcnJlZCB0aGlzIHN0ZXAnc1xuICAgICAgLy8gYHN0ZXBfY3JlYXRlZGAsIGV4cGVjdGluZyBleGVjdXRlU3RlcCB0byBtYXRlcmlhbGl6ZSB0aGUgc3RlcCB2aWEgYVxuICAgICAgLy8gbGF6eSBgc3RlcF9zdGFydGVkYCBjYXJyeWluZyBpdHMgaW5wdXQuIFdlIG5ldmVyIGdldCB0aGF0IGZhciBmb3IgYW5cbiAgICAgIC8vIHVucmVnaXN0ZXJlZCBzdGVwLCBzbyB0aGUgc3RlcCBlbnRpdHkgZG9lcyBub3QgZXhpc3QgeWV0IOKAlCB3cml0aW5nXG4gICAgICAvLyBgc3RlcF9mYWlsZWRgIHN0cmFpZ2h0IGF3YXkgd291bGQgaGl0IHRoZSB3b3JsZCdzIFwic3RlcCBtdXN0IGV4aXN0XCJcbiAgICAgIC8vIG9yZGVyaW5nIGd1YXJkIGFuZCB3ZWRnZSB0aGUgcnVuLiBTZW5kIHRoZSBsYXp5IGBzdGVwX3N0YXJ0ZWRgIGZpcnN0XG4gICAgICAvLyAoaXQgY3JlYXRlcyB0aGUgc3RlcCArIHN5bnRoZXRpYyBgc3RlcF9jcmVhdGVkYCBhdG9taWNhbGx5IGFuZCBrZWVwc1xuICAgICAgLy8gcmVwbGF5IGNvcnJlY3QpLCB0aGVuIGZhaWwgaXQgYmVsb3cuIFRoaXMgYWxzbyBwcmVzZXJ2ZXMgdGhlXG4gICAgICAvLyBleGFjdGx5LW9uZS1vd25lciBndWFyYW50ZWU6IGEgY29uY3VycmVudCBoYW5kbGVyIHRoYXQgd29uIHRoZSBjcmVhdGVcbiAgICAgIC8vIG1ha2VzIG91ciBsYXp5IGBzdGVwX3N0YXJ0ZWRgIHJlamVjdCB3aXRoIEVudGl0eUNvbmZsaWN0RXJyb3Ig4oaSIHdlXG4gICAgICAvLyByZXR1cm4gYHNraXBwZWRgIGFuZCBuZXZlciB3cml0ZSB0aGUgZmFpbHVyZSB0d2ljZS5cbiAgICAgIGlmIChwYXJhbXMubGF6eVN0ZXBJbnB1dCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gVHVyYm86IHRoaXMgbGF6eSBgc3RlcF9zdGFydGVkYCBtdXN0IG5vdCBwcmVjZWRlIHRoZSBiYWNrZ3JvdW5kZWRcbiAgICAgICAgICAvLyBgcnVuX3N0YXJ0ZWRgLiBPcmRlciBpdCBhZnRlciB0aGUgcnVuLXJlYWR5IGJhcnJpZXIgKGJlc3QtZWZmb3J0IOKAlFxuICAgICAgICAgIC8vIGEgYmFycmllciByZWplY3Rpb24gbWVhbnMgdGhlIHJ1biBkb2Vzbid0IGV4aXN0LCBhbmQgdGhlIGNyZWF0ZVxuICAgICAgICAgIC8vIGJlbG93IHN1cmZhY2VzIHRoZSByZWFsIGVycm9yKS4gTm8tb3Agb3V0c2lkZSB0dXJiby5cbiAgICAgICAgICBpZiAocGFyYW1zLnJ1blJlYWR5QmFycmllcikge1xuICAgICAgICAgICAgYXdhaXQgcGFyYW1zLnJ1blJlYWR5QmFycmllci5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGF3YWl0IGNyZWF0ZUV2ZW50KFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBldmVudFR5cGU6ICdzdGVwX3N0YXJ0ZWQnLFxuICAgICAgICAgICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IHN0ZXBJZCxcbiAgICAgICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgIGlucHV0OiBwYXJhbXMubGF6eVN0ZXBJbnB1dCxcbiAgICAgICAgICAgICAgICAvLyBTdGFtcGVkIGZvciBjb25zaXN0ZW5jeSBldmVuIHRob3VnaCB0aGlzIHN0ZXAgdGVybWluYWwtZmFpbHNcbiAgICAgICAgICAgICAgICAvLyBpbW1lZGlhdGVseSBiZWxvdyDigJQgdGhlIGxvZyBzaG91bGQgbmV2ZXIgc2hvdyBhbiB1bm93bmVkXG4gICAgICAgICAgICAgICAgLy8gbGF6eSBzdGFydC5cbiAgICAgICAgICAgICAgICAuLi4ocGFyYW1zLm93bmVyTWVzc2FnZUlkICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgID8geyBvd25lck1lc3NhZ2VJZDogcGFyYW1zLm93bmVyTWVzc2FnZUlkIH1cbiAgICAgICAgICAgICAgICAgIDoge30pLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHsgY29tcHV0ZUluc3RhbmNlSWQ6IENPTVBVVEVfSU5TVEFOQ0VfSUQgfVxuICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKHN0YXJ0RXJyKSB7XG4gICAgICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoc3RhcnRFcnIpKSB7XG4gICAgICAgICAgICByZXR1cm4geyB0eXBlOiAnc2tpcHBlZCcgfTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKFJ1bkV4cGlyZWRFcnJvci5pcyhzdGFydEVycikpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHR5cGU6ICdnb25lJyB9O1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aHJvdyBzdGFydEVycjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY3JlYXRlRXZlbnQoe1xuICAgICAgICAgIGV2ZW50VHlwZTogJ3N0ZXBfZmFpbGVkJyxcbiAgICAgICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcElkLFxuICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICBlcnJvcjogYXdhaXQgZGVoeWRyYXRlU3RlcEVycm9yKFxuICAgICAgICAgICAgICBuZXcgRmF0YWxFcnJvcihlcnJvck1lc3NhZ2UpLFxuICAgICAgICAgICAgICB3b3JrZmxvd1J1bklkLFxuICAgICAgICAgICAgICBhd2FpdCBnZXRFbmNyeXB0aW9uS2V5KCksXG4gICAgICAgICAgICAgIFtdLFxuICAgICAgICAgICAgICBnbG9iYWxUaGlzLFxuICAgICAgICAgICAgICBjb21wcmVzc2lvblxuICAgICAgICAgICAgKSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKHN0ZXBGYWlsRXJyKSB7XG4gICAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKHN0ZXBGYWlsRXJyKSkge1xuICAgICAgICAgIHJldHVybiB7IHR5cGU6ICdza2lwcGVkJyB9O1xuICAgICAgICB9XG4gICAgICAgIHRocm93IHN0ZXBGYWlsRXJyO1xuICAgICAgfVxuICAgICAgc3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwU3RhdHVzKCdmYWlsZWQnKSxcbiAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBGYXRhbEVycm9yKHRydWUpLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4geyB0eXBlOiAnZmFpbGVkJyB9O1xuICAgIH1cblxuICAgIGNvbnN0IG1heFJldHJpZXMgPSBzdGVwRm4ubWF4UmV0cmllcyA/PyBERUZBVUxUX1NURVBfTUFYX1JFVFJJRVM7XG5cbiAgICBzcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwTWF4UmV0cmllcyhtYXhSZXRyaWVzKSxcbiAgICB9KTtcblxuICAgIC8vIG1heFJldHJpZXMgZW5mb3JjZWQgYmVmb3JlIHN0YXJ0aW5nIGFub3RoZXIgYXR0ZW1wdC4gVGltZW91dHMgY2FuIGtpbGxcbiAgICAvLyBhIHN0ZXAgZXhlY3V0aW9uIHdpdGhvdXQgYW55IHdheSB0byBjYXRjaCB0aGUgZXJyb3IsIHNvIHRoZSBwb3N0LWJvZHkvZXJyb3JcbiAgICAvLyBndWFyZHMgYmVsb3cgbWlnaHQgbWlzcyBpdC4gSGVuY2UsIHdlIHVzZSBgYXV0aG9yaXRhdGl2ZUF0dGVtcHRgIGNvdW50XG4gICAgLy8gYXMgYW4gYWRkaXRpb25hbCBndWFyZCBiYXNlZCBvbiBzdGVwX3N0YXJ0ZWQgY291bnQgb3IgcXVldWUgZGVsaXZlcnkgY291bnRcbiAgICAvLyBvbiBiYWNrZ3JvdW5kZWQgc3RlcHMuXG4gICAgLy8gVGhyb3duLWVycm9yIGV4aGF1c3Rpb24gc3RpbGwgdGVybWluYXRlcyBvbmUgYXR0ZW1wdCBlYXJsaWVyIHZpYSB0aGUgcG9zdC1ib2R5XG4gICAgLy8gZ3VhcmQgKHdpdGggdGhlIHRocm93biBlcnJvciBhcyBjYXVzZSksIGFuZCB0aGlzIGNoZWNrIGRvZXMgbm90IGFmZmVjdCB0aGF0LlxuICAgIGlmIChcbiAgICAgIHBhcmFtcy5hdXRob3JpdGF0aXZlQXR0ZW1wdCAhPT0gdW5kZWZpbmVkICYmXG4gICAgICBwYXJhbXMuYXV0aG9yaXRhdGl2ZUF0dGVtcHQgPiBtYXhSZXRyaWVzICsgMVxuICAgICkge1xuICAgICAgY29uc3QgcmV0cnlDb3VudCA9IG1heFJldHJpZXM7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBgU3RlcCBcIiR7c3RlcE5hbWV9XCIgZXhjZWVkZWQgbWF4IHJldHJpZXMgKCR7cmV0cnlDb3VudH0gJHtwbHVyYWxpemUoJ3JldHJ5JywgJ3JldHJpZXMnLCByZXRyeUNvdW50KX0pYDtcbiAgICAgIHN0ZXBMb2dnZXIuZXJyb3IoJ1N0ZXAgZXhjZWVkZWQgbWF4IHJldHJpZXMnLCB7XG4gICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgIHN0ZXBOYW1lLFxuICAgICAgICBzdGVwSWQsXG4gICAgICAgIGF0dGVtcHQ6IHBhcmFtcy5hdXRob3JpdGF0aXZlQXR0ZW1wdCxcbiAgICAgICAgbWF4UmV0cmllcyxcbiAgICAgIH0pO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY3JlYXRlRXZlbnQoe1xuICAgICAgICAgIGV2ZW50VHlwZTogJ3N0ZXBfZmFpbGVkJyxcbiAgICAgICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcElkLFxuICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICBlcnJvcjogYXdhaXQgZGVoeWRyYXRlU3RlcEVycm9yKFxuICAgICAgICAgICAgICBuZXcgRmF0YWxFcnJvcihlcnJvck1lc3NhZ2UpLFxuICAgICAgICAgICAgICB3b3JrZmxvd1J1bklkLFxuICAgICAgICAgICAgICBhd2FpdCBnZXRFbmNyeXB0aW9uS2V5KCksXG4gICAgICAgICAgICAgIFtdLFxuICAgICAgICAgICAgICBnbG9iYWxUaGlzLFxuICAgICAgICAgICAgICBjb21wcmVzc2lvblxuICAgICAgICAgICAgKSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBpZiAoRW50aXR5Q29uZmxpY3RFcnJvci5pcyhlcnIpKSB7XG4gICAgICAgICAgLy8gU3RlcCBhbHJlYWR5IHJlYWNoZWQgYSB0ZXJtaW5hbCBzdGF0ZSAoYSBjb25jdXJyZW50IGhhbmRsZXIgb3IgYW5cbiAgICAgICAgICAvLyBlYXJsaWVyIGRlbGl2ZXJ5IGZhaWxlZC9jb21wbGV0ZWQgaXQpIOKAlCBub3RoaW5nIHRvIGRvLlxuICAgICAgICAgIHJ1bnRpbWVMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgICdUcmllZCBmYWlsaW5nIHN0ZXAgZm9yIGV4Y2VlZGVkIHJldHJpZXMsIGJ1dCBzdGVwIGhhcyBhbHJlYWR5IGZpbmlzaGVkLicsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICAgIHN0ZXBJZCxcbiAgICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICApO1xuICAgICAgICAgIHJldHVybiB7IHR5cGU6ICdza2lwcGVkJyB9O1xuICAgICAgICB9XG4gICAgICAgIGlmIChSdW5FeHBpcmVkRXJyb3IuaXMoZXJyKSkge1xuICAgICAgICAgIHJldHVybiB7IHR5cGU6ICdnb25lJyB9O1xuICAgICAgICB9XG4gICAgICAgIHRocm93IGVycjtcbiAgICAgIH1cbiAgICAgIHNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcFN0YXR1cygnZmFpbGVkJyksXG4gICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwUmV0cnlFeGhhdXN0ZWQodHJ1ZSksXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7IHR5cGU6ICdmYWlsZWQnIH07XG4gICAgfVxuXG4gICAgLy8gTWFwcyBhIGBzdGVwX3N0YXJ0ZWRgIHJlamVjdGlvbiB0byBhIHRlcm1pbmFsIFN0ZXBFeGVjdXRpb25SZXN1bHQsXG4gICAgLy8gc2hhcmVkIGJ5IHRoZSBhd2FpdCBwYXRoIChiZWxvdykgYW5kIHRoZSBvcHRpbWlzdGljLXN0YXJ0IHJlY29uY2lsaWF0aW9uLlxuICAgIC8vIFJldHVybnMgdW5kZWZpbmVkIHdoZW4gdGhlIGVycm9yIGlzIG5vdCBvbmUgd2UgdHJhbnNsYXRlIChjYWxsZXIgcmV0aHJvd3MpLlxuICAgIGNvbnN0IHN0YXJ0RXJyb3JUb1Jlc3VsdCA9IChcbiAgICAgIGVycjogdW5rbm93blxuICAgICk6IFN0ZXBFeGVjdXRpb25SZXN1bHQgfCB1bmRlZmluZWQgPT4ge1xuICAgICAgaWYgKFRocm90dGxlRXJyb3IuaXMoZXJyKSkge1xuICAgICAgICBjb25zdCByZXRyeUFmdGVyID0gTWF0aC5tYXgoXG4gICAgICAgICAgMSxcbiAgICAgICAgICB0eXBlb2YgZXJyLnJldHJ5QWZ0ZXIgPT09ICdudW1iZXInID8gZXJyLnJldHJ5QWZ0ZXIgOiAxXG4gICAgICAgICk7XG4gICAgICAgIHJ1bnRpbWVMb2dnZXIuaW5mbygnVGhyb3R0bGVkIG9uIHN0ZXBfc3RhcnRlZCwgZGVmZXJyaW5nJywge1xuICAgICAgICAgIHJldHJ5QWZ0ZXJTZWNvbmRzOiByZXRyeUFmdGVyLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHsgdHlwZTogJ3Rocm90dGxlZCcsIHRpbWVvdXRTZWNvbmRzOiByZXRyeUFmdGVyIH07XG4gICAgICB9XG4gICAgICBpZiAoUnVuRXhwaXJlZEVycm9yLmlzKGVycikpIHtcbiAgICAgICAgcnVudGltZUxvZ2dlci5pbmZvKFxuICAgICAgICAgIGBXb3JrZmxvdyBydW4gXCIke3dvcmtmbG93UnVuSWR9XCIgaGFzIGFscmVhZHkgY29tcGxldGVkLCBza2lwcGluZyBzdGVwIFwiJHtzdGVwSWR9XCI6ICR7ZXJyLm1lc3NhZ2V9YFxuICAgICAgICApO1xuICAgICAgICByZXR1cm4geyB0eXBlOiAnZ29uZScgfTtcbiAgICAgIH1cbiAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKGVycikpIHtcbiAgICAgICAgcnVudGltZUxvZ2dlci5kZWJ1ZygnU3RlcCBpbiB0ZXJtaW5hbCBzdGF0ZSwgc2tpcHBpbmcnLCB7XG4gICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgc3RlcElkLFxuICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSxcbiAgICAgICAgfSk7XG4gICAgICAgIHNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwU2tpcHBlZCh0cnVlKSxcbiAgICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcFNraXBSZWFzb24oJ2NvbXBsZXRlZCcpLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHsgdHlwZTogJ3NraXBwZWQnIH07XG4gICAgICB9XG4gICAgICBpZiAoVG9vRWFybHlFcnJvci5pcyhlcnIpKSB7XG4gICAgICAgIGNvbnN0IHRpbWVvdXRTZWNvbmRzID0gTWF0aC5tYXgoMSwgZXJyLnJldHJ5QWZ0ZXIgPz8gMSk7XG4gICAgICAgIHJ1bnRpbWVMb2dnZXIuZGVidWcoJ1N0ZXAgcmV0cnlBZnRlciB0aW1lc3RhbXAgbm90IHlldCByZWFjaGVkJywge1xuICAgICAgICAgIHN0ZXBOYW1lLFxuICAgICAgICAgIHN0ZXBJZCxcbiAgICAgICAgICB0aW1lb3V0U2Vjb25kcyxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB7IHR5cGU6ICdyZXRyeScsIHRpbWVvdXRTZWNvbmRzIH07XG4gICAgICB9XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH07XG5cbiAgICAvLyBPcHRpbWlzdGljIGlubGluZSBzdGFydDogd2hlbiB3ZSBob2xkIHRoZSBzdGVwIGlucHV0IGxvY2FsbHkgKGxhenkgaW5saW5lXG4gICAgLy8gcGF0aCkgYW5kIHRoZSBvcHRpbWl6YXRpb24gaXMgZW5hYmxlZCwgZmlyZSBgc3RlcF9zdGFydGVkYCBXSVRIT1VUXG4gICAgLy8gYXdhaXRpbmcgYW5kIHJ1biB0aGUgYm9keSBhZ2FpbnN0IGxvY2FsbHktc3ludGhlc2l6ZWQgc3RhdGUuIEEgbGF6eSBzdGVwXG4gICAgLy8gaXMgYWx3YXlzIGJyYW5kLW5ldyDih5IgYXR0ZW1wdCAxLCBubyBwcmlvciBlcnJvciwgc3RhcnRlZCBub3cg4oCUIHNvIHdlXG4gICAgLy8gZG9uJ3QgbmVlZCB0aGUgc2VydmVyIHJvdW5kLXRyaXAgdG8gYmVnaW4uIFdlIHJlY29uY2lsZSB0aGUgaW4tZmxpZ2h0XG4gICAgLy8gYHN0ZXBfc3RhcnRlZGAgYmVmb3JlIGFueSB0ZXJtaW5hbCB3cml0ZSAoYHJlY29uY2lsZU9wdGltaXN0aWNTdGFydGApOiBpZlxuICAgIC8vIGl0IGxvc3QgdGhlIGF0b21pYyBjcmVhdGUtY2xhaW0gKDQwOSkgb3IgdGhlIHJ1biBpcyBnb25lL3Rocm90dGxlZCwgd2VcbiAgICAvLyBkaXNjYXJkIHRoZSBib2R5IHJlc3VsdC4gUnVubmluZyB0aGUgYm9keSBiZWZvcmUgY29uZmlybWluZyBvd25lcnNoaXAgY2FuXG4gICAgLy8gZXhlY3V0ZSBhIHN0ZXAgbW9yZSB0aGFuIG9uY2Ugd2hlbiBoYW5kbGVycyByYWNlIOKAlCBpbmxpbmUgc3RlcCBib2RpZXNcbiAgICAvLyBtdXN0IGJlIGlkZW1wb3RlbnQ7IGRpc2FibGUgdmlhIFdPUktGTE9XX09QVElNSVNUSUNfSU5MSU5FX1NUQVJUPTAuXG4gICAgLy9cbiAgICAvLyBUdXJibyBtb2RlIHBhc3NlcyBgZm9yY2VPcHRpbWlzdGljU3RhcnRgIHRvIGVuYWJsZSB0aGlzIHJlZ2FyZGxlc3Mgb2YgdGhlXG4gICAgLy8gZW52IGZsYWcgKGl0cyBzaW5nbGUtaGFuZGxlciBndWFyYW50ZWUgcmVtb3ZlcyB0aGUgcmFjZSkuIEJ1dCBpdCBzdGlsbFxuICAgIC8vIGRlZmVycyB0byBhbiAqZXhwbGljaXQqIGBXT1JLRkxPV19PUFRJTUlTVElDX0lOTElORV9TVEFSVD0wYDogZm9yY2VkXG4gICAgLy8gb3B0aW1pc3RpYyBzdGFydCBydW5zIHRoZSBib2R5IGJlZm9yZSBgc3RlcF9zdGFydGVkYC9gcnVuX3N0YXJ0ZWRgIGlzXG4gICAgLy8gY29uZmlybWVkLCB3aGljaCBpcyBleGFjdGx5IHRoZSBwcm9wZXJ0eSBhbiBvcGVyYXRvciBvcHRzIG91dCBvZiB3aXRoIHRoYXRcbiAgICAvLyBmbGFnLCBzbyBhbiBleHBsaWNpdCBvcHQtb3V0IHdpbnMgb3ZlciB0dXJibydzIGZvcmNlLlxuICAgIGNvbnN0IG9wdGltaXN0aWNTdGFydCA9XG4gICAgICBwYXJhbXMubGF6eVN0ZXBJbnB1dCAhPT0gdW5kZWZpbmVkICYmXG4gICAgICAvLyBTdGFsZS1zZW5zaXRpdmUgZ3VhcmRlZCBiYXRjaGVzIGF3YWl0IHRoZSBjbGFpbSBzbyB0aGUgNDEyIGZlbmNlXG4gICAgICAvLyBjb3ZlcnMgdGhlIGJvZHksIG5vdCBqdXN0IGR1cmFibGUgd3JpdGVzIOKAlCBzZWVcbiAgICAgIC8vIFN0ZXBFeGVjdXRvclBhcmFtcy5zdXBwcmVzc09wdGltaXN0aWNTdGFydC5cbiAgICAgIHBhcmFtcy5zdXBwcmVzc09wdGltaXN0aWNTdGFydCAhPT0gdHJ1ZSAmJlxuICAgICAgKGlzT3B0aW1pc3RpY0lubGluZVN0YXJ0RW5hYmxlZCgpIHx8XG4gICAgICAgIChwYXJhbXMuZm9yY2VPcHRpbWlzdGljU3RhcnQgPT09IHRydWUgJiZcbiAgICAgICAgICAhaXNPcHRpbWlzdGljSW5saW5lU3RhcnRFeHBsaWNpdGx5RGlzYWJsZWQoKSkpO1xuXG4gICAgbGV0IHN0ZXA6IFN0YXJ0ZWRTdGVwO1xuICAgIC8vIFBhcmFtcyBmb3IgdGhlIGBzdGVwX3N0YXJ0ZWRgIGNyZWF0ZSBvbiBlaXRoZXIgcGF0aCBiZWxvdzogdGhlIGFtYmllbnRcbiAgICAvLyBjb21wdXRlLWluc3RhbmNlIHN0YW1wIHBsdXMgdGhlIG9wdGltaXN0aWMtY29uY3VycmVuY3kgY2xhaW0gZ3VhcmQuXG4gICAgY29uc3Qgc3RhcnRFdmVudFBhcmFtczogQ3JlYXRlRXZlbnRQYXJhbXMgPSB7XG4gICAgICBjb21wdXRlSW5zdGFuY2VJZDogQ09NUFVURV9JTlNUQU5DRV9JRCxcbiAgICAgIC8vIFNwcmVhZCBhcyBhIHVuaXQ6IHRoZSB0aHJlZSBzbmFwc2hvdCBmaWVsZHMgZGVzY3JpYmUgb25lIHNuYXBzaG90IGFuZFxuICAgICAgLy8gbXVzdCB0cmF2ZWwgdG9nZXRoZXIg4oCUIHNlZSBTdGVwRXhlY3V0b3JQYXJhbXMucHJlY29uZGl0aW9uU25hcHNob3QuXG4gICAgICAuLi5wYXJhbXMucHJlY29uZGl0aW9uU25hcHNob3QsXG4gICAgfTtcbiAgICAvLyBgRGF0ZS5ub3coKWAgdGFrZW4gaW1tZWRpYXRlbHkgYmVmb3JlIHRoZSBgc3RlcF9zdGFydGVkYCBjcmVhdGUgaXNcbiAgICAvLyBpc3N1ZWQgKGVpdGhlciBwYXRoIGJlbG93KSDigJQgYW5jaG9ycyBSU0ZTJ3MgZW5kIHBvaW50LiBTZWVcbiAgICAvLyBTdGVwTGF0ZW5jeUV2ZW50RGF0YS5yc2ZzIGFuZCB0aGUgY2FsbCBzaXRlcyBiZWxvdy5cbiAgICBsZXQgc3RlcFN0YXJ0UG9zdFNlbnRBdE1zOiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gICAgLy8gU2V0dGxlZCBvdXRjb21lIG9mIHRoZSBpbi1mbGlnaHQgb3B0aW1pc3RpYyBgc3RlcF9zdGFydGVkYC4gSGFuZGxlcnMgYXJlXG4gICAgLy8gYXR0YWNoZWQgc3luY2hyb25vdXNseSAoYC50aGVuKG9rLCBlcnIpYCkgc28gYSBmYXN0IHJlamVjdGlvbiBuZXZlclxuICAgIC8vIHN1cmZhY2VzIGFzIGFuIHVuaGFuZGxlZFJlamVjdGlvbiB3aGlsZSB0aGUgYm9keSBydW5zLlxuICAgIGxldCBvcHRpbWlzdGljU3RhcnRTZXR0bGVkOlxuICAgICAgfCBQcm9taXNlPHsgb2s6IHRydWUgfSB8IHsgb2s6IGZhbHNlOyBlcnI6IHVua25vd24gfT5cbiAgICAgIHwgdW5kZWZpbmVkO1xuICAgIC8vIEF3YWl0IHRoZSBvcHRpbWlzdGljIGBzdGVwX3N0YXJ0ZWRgIG91dGNvbWUgYW5kIHRyYW5zbGF0ZSBhIGxvc3QgcmFjZSAvXG4gICAgLy8gdGVybWluYWwgcnVuIC8gdGhyb3R0bGUgaW50byBhIHJlc3VsdCB0aGF0IHNob3J0LWNpcmN1aXRzIHRoZSBib2R5XG4gICAgLy8gb3V0cHV0LiBSZXR1cm5zIHVuZGVmaW5lZCB3aGVuIHdlIG93biB0aGUgc3RlcCBhbmQgbWF5IHdyaXRlIGl0cyB0ZXJtaW5hbFxuICAgIC8vIGV2ZW50LiBBIG5vbi10cmFuc2xhdGFibGUgcmVqZWN0aW9uIGlzIHJldGhyb3duIChzbyBhIHRyYW5zaWVudFxuICAgIC8vIHN0ZXBfc3RhcnRlZCBmYWlsdXJlIHByb3BhZ2F0ZXMgdG8gdGhlIHF1ZXVlIGhhbmRsZXIgZm9yIHJlZGVsaXZlcnksXG4gICAgLy8gZXhhY3RseSBhcyBvbiB0aGUgYXdhaXQgcGF0aCkuIElkZW1wb3RlbnQg4oCUIHNhZmUgdG8gY2FsbCBtb3JlIHRoYW4gb25jZS5cbiAgICBjb25zdCByZWNvbmNpbGVPcHRpbWlzdGljU3RhcnQgPSBhc3luYyAoKTogUHJvbWlzZTxcbiAgICAgIFN0ZXBFeGVjdXRpb25SZXN1bHQgfCB1bmRlZmluZWRcbiAgICA+ID0+IHtcbiAgICAgIGlmICghb3B0aW1pc3RpY1N0YXJ0U2V0dGxlZCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IHNldHRsZWQgPSBhd2FpdCBvcHRpbWlzdGljU3RhcnRTZXR0bGVkO1xuICAgICAgaWYgKHNldHRsZWQub2spIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICBjb25zdCBtYXBwZWQgPSBzdGFydEVycm9yVG9SZXN1bHQoc2V0dGxlZC5lcnIpO1xuICAgICAgaWYgKCFtYXBwZWQpIHRocm93IHNldHRsZWQuZXJyO1xuICAgICAgcmV0dXJuIG1hcHBlZDtcbiAgICB9O1xuXG4gICAgaWYgKG9wdGltaXN0aWNTdGFydCkge1xuICAgICAgLy8gQ2hhaW4gdGhlIGxhenkgYHN0ZXBfc3RhcnRlZGAgb24gdGhlIHJ1bi1yZWFkeSBiYXJyaWVyICh0dXJibyBtb2RlKTpcbiAgICAgIC8vIHRoZSBzdGVwIGNhbid0IGJlIGNyZWF0ZWQgYmVmb3JlIGl0cyBydW4gZXhpc3RzLCBidXQgdGhlIGJvZHkgYmVsb3dcbiAgICAgIC8vIHJ1bnMgaW1tZWRpYXRlbHkgYWdhaW5zdCBzeW50aGVzaXplZCBzdGF0ZSwgc28gdGhlIGBydW5fc3RhcnRlZGBcbiAgICAgIC8vIHJvdW5kLXRyaXAgb3ZlcmxhcHMgdGhlIGJvZHkgcmF0aGVyIHRoYW4gYmxvY2tpbmcgaXQuIE91dHNpZGUgdHVyYm8gdGhlXG4gICAgICAvLyBiYXJyaWVyIGlzIHVuZGVmaW5lZCBhbmQgdGhpcyBpcyBhIHBsYWluIGNyZWF0ZS5cbiAgICAgIGNvbnN0IHN0YXJ0ZWRQcm9taXNlID0gKHBhcmFtcy5ydW5SZWFkeUJhcnJpZXIgPz8gUHJvbWlzZS5yZXNvbHZlKCkpLnRoZW4oXG4gICAgICAgICgpID0+IHtcbiAgICAgICAgICAvLyBUYWtlbiByaWdodCBiZWZvcmUgdGhlIGNyZWF0ZSBmaXJlcywgbm90IGJlZm9yZSB0aGUgYmFycmllciDigJRcbiAgICAgICAgICAvLyBSU0ZTIG1lYXN1cmVzIHRoZSBydW5fc3RhcnRlZC10by1QT1NUIHN0cmV0Y2gsIGFuZCB0aGUgYmFycmllclxuICAgICAgICAgIC8vIHdhaXQgSVMgcGFydCBvZiB0aGF0IHN0cmV0Y2ggdW5kZXIgdHVyYm8uXG4gICAgICAgICAgc3RlcFN0YXJ0UG9zdFNlbnRBdE1zID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICByZXR1cm4gY3JlYXRlRXZlbnQoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGV2ZW50VHlwZTogJ3N0ZXBfc3RhcnRlZCcsXG4gICAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgICAgY29ycmVsYXRpb25JZDogc3RlcElkLFxuICAgICAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgICAgICB3b3JrZmxvd05hbWUsXG4gICAgICAgICAgICAgICAgaW5wdXQ6IHBhcmFtcy5sYXp5U3RlcElucHV0LFxuICAgICAgICAgICAgICAgIC8vIElubGluZS1vd25lcnNoaXAgc3RhbXAg4oCUIHNlZSBTdGVwRXhlY3V0b3JQYXJhbXMub3duZXJNZXNzYWdlSWQuXG4gICAgICAgICAgICAgICAgLi4uKHBhcmFtcy5vd25lck1lc3NhZ2VJZCAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICA/IHsgb3duZXJNZXNzYWdlSWQ6IHBhcmFtcy5vd25lck1lc3NhZ2VJZCB9XG4gICAgICAgICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAvLyBHdWFyZCB0aGUgY2xhaW0g4oCUIHNlZSBTdGVwRXhlY3V0b3JQYXJhbXMucHJlY29uZGl0aW9uU25hcHNob3QuIEFcbiAgICAgICAgICAgIC8vIHN0YWxlICg0MTIpIHJlamVjdGlvbiBzdXJmYWNlcyB2aWEgcmVjb25jaWxlT3B0aW1pc3RpY1N0YXJ0IGFzIGFcbiAgICAgICAgICAgIC8vIG5vbi10cmFuc2xhdGFibGUgZXJyb3I6IHRoZSBib2R5IHJlc3VsdCBpcyBkaXNjYXJkZWQgYW5kIHRoZVxuICAgICAgICAgICAgLy8gcmVqZWN0aW9uIHByb3BhZ2F0ZXMgdG8gdGhlIGNhbGxlci5cbiAgICAgICAgICAgIHN0YXJ0RXZlbnRQYXJhbXNcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICApO1xuICAgICAgb3B0aW1pc3RpY1N0YXJ0U2V0dGxlZCA9IHN0YXJ0ZWRQcm9taXNlLnRoZW4oXG4gICAgICAgICgpID0+ICh7IG9rOiB0cnVlIGFzIGNvbnN0IH0pLFxuICAgICAgICAoZXJyKSA9PiAoeyBvazogZmFsc2UgYXMgY29uc3QsIGVyciB9KVxuICAgICAgKTtcbiAgICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCk7XG4gICAgICBzdGVwID0ge1xuICAgICAgICBydW5JZDogd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgc3RlcElkLFxuICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgc3RhdHVzOiAncnVubmluZycsXG4gICAgICAgIGlucHV0OiBwYXJhbXMubGF6eVN0ZXBJbnB1dCxcbiAgICAgICAgYXR0ZW1wdDogMSxcbiAgICAgICAgc3RhcnRlZEF0OiBub3csXG4gICAgICAgIGNyZWF0ZWRBdDogbm93LFxuICAgICAgICB1cGRhdGVkQXQ6IG5vdyxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIHN0ZXBfc3RhcnRlZCB2YWxpZGF0ZXMgc3RhdGUgYW5kIHJldHVybnMgdGhlIHN0ZXAgZW50aXR5LiBPbiB0aGUgbGF6eVxuICAgICAgLy8gaW5saW5lIHBhdGggd2UgYWxzbyBjYXJyeSB0aGUgc3RlcCBgaW5wdXRgIHNvIHRoZSB3b3JsZCBjcmVhdGVzIHRoZVxuICAgICAgLy8gc3RlcCBvbiB0aGUgZmx5IChubyBzZXBhcmF0ZSBzdGVwX2NyZWF0ZWQgcm91bmQtdHJpcCkuIFRoZSB3b3JsZCdzXG4gICAgICAvLyBhdG9taWMgY3JlYXRlLWNsYWltIG1ha2VzIHRoaXMgZXhhY3RseS1vbmUtb3duZXI6IGEgY29uY3VycmVudCBsb3NlclxuICAgICAgLy8gZ2V0cyBFbnRpdHlDb25mbGljdEVycm9yLCBtYXBwZWQgdG8gYHsgdHlwZTogJ3NraXBwZWQnIH1gLCBzbyBpdCBuZXZlclxuICAgICAgLy8gcnVucyB0aGUgYm9keS4gV2hlbiBgbGF6eVN0ZXBJbnB1dGAgaXMgYWJzZW50IHRoaXMgaXMgdGhlIGxlZ2FjeVxuICAgICAgLy8gc3RlcF9zdGFydGVkIChzdGVwIGFscmVhZHkgY3JlYXRlZCwgbm8gcGF5bG9hZCkuXG4gICAgICB0cnkge1xuICAgICAgICAvLyBJbmxpbmUtb3duZXJzaGlwIHN0YW1wOiBwcmVzZW50IG9uIHRoZSBsYXp5IHBhdGhzIEFORCBvbiB0aGVcbiAgICAgICAgLy8gb3duZWQtcmVjb3ZlcnkgcGF5bG9hZC1sZXNzIHN0YXJ0IChhIHJlZGVsaXZlcnkgb2YgdGhlIG93bmluZ1xuICAgICAgICAvLyBtZXNzYWdlIHJlLWV4ZWN1dGluZyBpdHMgc3RlcCBtdXN0IHJlLXN0YW1wIOKAlCBvd25lcnNoaXAgZGVyaXZlc1xuICAgICAgICAvLyBmcm9tIHRoZSBsYXRlc3Qgc3RhcnQsIHNvIGFuIHVuc3RhbXBlZCByZWNvdmVyeSBzdGFydCB3b3VsZCByZWFkXG4gICAgICAgIC8vIGFzIFwidW5vd25lZFwiIHRvIGEgbGF0ZXIgd2FrZSkuIE9ubHkgdGhlIGJhY2tncm91bmQtc3RlcCBwYXRoXG4gICAgICAgIC8vIHBhc3NlcyBubyBvd25lck1lc3NhZ2VJZDogaXRzIHN0YXJ0IGlzIHRoZSBiYXJlIG9uZSwgY2xlYXJpbmdcbiAgICAgICAgLy8gb3duZXJzaGlwIGFzIGludGVuZGVkLlxuICAgICAgICBjb25zdCBvd25lcnNoaXBTdGFtcCA9XG4gICAgICAgICAgcGFyYW1zLm93bmVyTWVzc2FnZUlkICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgID8geyBvd25lck1lc3NhZ2VJZDogcGFyYW1zLm93bmVyTWVzc2FnZUlkIH1cbiAgICAgICAgICAgIDoge307XG4gICAgICAgIHN0ZXBTdGFydFBvc3RTZW50QXRNcyA9IERhdGUubm93KCk7XG4gICAgICAgIGNvbnN0IHN0YXJ0UmVzdWx0ID0gYXdhaXQgY3JlYXRlRXZlbnQoXG4gICAgICAgICAge1xuICAgICAgICAgICAgZXZlbnRUeXBlOiAnc3RlcF9zdGFydGVkJyxcbiAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IHN0ZXBJZCxcbiAgICAgICAgICAgIGV2ZW50RGF0YTpcbiAgICAgICAgICAgICAgcGFyYW1zLmxhenlTdGVwSW5wdXQgIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgICAgICAgICBpbnB1dDogcGFyYW1zLmxhenlTdGVwSW5wdXQsXG4gICAgICAgICAgICAgICAgICAgIC4uLm93bmVyc2hpcFN0YW1wLFxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIDogeyBzdGVwTmFtZSwgLi4ub3duZXJzaGlwU3RhbXAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vIEd1YXJkIHRoZSBjbGFpbSDigJQgc2VlIFN0ZXBFeGVjdXRvclBhcmFtcy5wcmVjb25kaXRpb25TbmFwc2hvdC4gQVxuICAgICAgICAgIC8vIHN0YWxlICg0MTIpIHJlamVjdGlvbiBpcyBpbnRlbnRpb25hbGx5IE5PVCB0cmFuc2xhdGVkIGJ5XG4gICAgICAgICAgLy8gc3RhcnRFcnJvclRvUmVzdWx0IGJlbG93LCBzbyBpdCBwcm9wYWdhdGVzIHRvIHRoZSBjYWxsZXIgZm9yIGFcbiAgICAgICAgICAvLyBmcmVzaCByZXBsYXkuXG4gICAgICAgICAgc3RhcnRFdmVudFBhcmFtc1xuICAgICAgICApO1xuXG4gICAgICAgIHN0ZXAgPSBzdGFydFJlc3VsdC5zdGVwO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGNvbnN0IG1hcHBlZCA9IHN0YXJ0RXJyb3JUb1Jlc3VsdChlcnIpO1xuICAgICAgICBpZiAobWFwcGVkKSByZXR1cm4gbWFwcGVkO1xuICAgICAgICB0aHJvdyBlcnI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcnVudGltZUxvZ2dlci5kZWJ1ZygnU3RlcCBleGVjdXRpb24gZGV0YWlscycsIHtcbiAgICAgIHN0ZXBOYW1lLFxuICAgICAgc3RlcElkOiBzdGVwLnN0ZXBJZCxcbiAgICAgIHN0YXR1czogc3RlcC5zdGF0dXMsXG4gICAgICBhdHRlbXB0OiBzdGVwLmF0dGVtcHQsXG4gICAgfSk7XG5cbiAgICBzcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwU3RhdHVzKHN0ZXAuc3RhdHVzKSxcbiAgICB9KTtcblxuICAgIGxldCByZXN1bHQ6IHVua25vd247XG5cbiAgICAvLyBDaGVjayBtYXggcmV0cmllcyBBRlRFUiBzdGVwX3N0YXJ0ZWQgKGF0dGVtcHQgd2FzIGp1c3QgaW5jcmVtZW50ZWQpLlxuICAgIC8vIE9ubHkgZW5mb3JjZSB3aGVuIHRoZSBzdGVwIGhhcyBhIHByZXZpb3VzIGVycm9yIOKAlCB0aGlzIGRpc3Rpbmd1aXNoZXNcbiAgICAvLyBhY3R1YWwgcmV0cmllcyAoZmFpbGVkIOKGkiByZXRyeSkgZnJvbSBjb25jdXJyZW50IGlubGluZSBzdGFydHNcbiAgICAvLyBleGVjdXRpb24gbG9vcCBjYW4gY2F1c2UgbXVsdGlwbGUgaGFuZGxlcnMgdG8gc3RlcF9zdGFydGVkIHRoZSBzYW1lXG4gICAgLy8gc3RlcCBzaW11bHRhbmVvdXNseSwgaW5mbGF0aW5nIHRoZSBhdHRlbXB0IGNvdW50ZXIgd2l0aG91dCBhbnkgZmFpbHVyZSkuXG4gICAgaWYgKHN0ZXAuYXR0ZW1wdCA+IG1heFJldHJpZXMgKyAxICYmIHN0ZXAuZXJyb3IpIHtcbiAgICAgIGNvbnN0IHJldHJ5Q291bnQgPSBzdGVwLmF0dGVtcHQgLSAxO1xuICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gYFN0ZXAgXCIke3N0ZXBOYW1lfVwiIGV4Y2VlZGVkIG1heCByZXRyaWVzICgke3JldHJ5Q291bnR9ICR7cGx1cmFsaXplKCdyZXRyeScsICdyZXRyaWVzJywgcmV0cnlDb3VudCl9KWA7XG4gICAgICBzdGVwTG9nZ2VyLmVycm9yKCdTdGVwIGV4Y2VlZGVkIG1heCByZXRyaWVzJywge1xuICAgICAgICB3b3JrZmxvd1J1bklkLFxuICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgcmV0cnlDb3VudCxcbiAgICAgIH0pO1xuICAgICAgLy8gUHJlc2VydmUgdGhlIHByaW9yIGF0dGVtcHQncyBzZXJpYWxpemVkIGVycm9yIGFzIHRoZSBjYXVzZSBzbyB0aGVcbiAgICAgIC8vIHVuZGVybHlpbmcgZmFpbHVyZSBpcyByZWNvdmVyYWJsZSBmcm9tIGBzdGVwLmVycm9yLmNhdXNlYCBhZnRlclxuICAgICAgLy8gaHlkcmF0aW9uLCB3aXRob3V0IGZvcmNpbmcgY29uc3VtZXJzIHRvIHdhbGsgdGhlIHN0ZXBfcmV0cnlpbmdcbiAgICAgIC8vIGV2ZW50IGhpc3RvcnkuIEJlc3QtZWZmb3J0OiBpZiBoeWRyYXRpb24gb2YgdGhlIHByaW9yIGBzdGVwLmVycm9yYFxuICAgICAgLy8gdGhyb3dzLCBmYWxsIGJhY2sgdG8gYSBGYXRhbEVycm9yIHdpdGhvdXQgY2F1c2UuXG4gICAgICBjb25zdCB3cmFwcGVkRXJyb3IgPSBuZXcgRmF0YWxFcnJvcihlcnJvck1lc3NhZ2UpO1xuICAgICAgaWYgKHN0ZXAuZXJyb3IgIT0gbnVsbCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICh3cmFwcGVkRXJyb3IgYXMgRXJyb3IpLmNhdXNlID0gYXdhaXQgaHlkcmF0ZVN0ZXBFcnJvcihcbiAgICAgICAgICAgIHN0ZXAuZXJyb3IsXG4gICAgICAgICAgICB3b3JrZmxvd1J1bklkLFxuICAgICAgICAgICAgYXdhaXQgZ2V0RW5jcnlwdGlvbktleSgpXG4gICAgICAgICAgKTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gSWdub3JlIOKAlCBiZXN0LWVmZm9ydCBjYXVzZSBhdHRhY2htZW50LlxuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBjcmVhdGVFdmVudCh7XG4gICAgICAgICAgZXZlbnRUeXBlOiAnc3RlcF9mYWlsZWQnLFxuICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzdGVwSWQsXG4gICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgIGVycm9yOiBhd2FpdCBkZWh5ZHJhdGVTdGVwRXJyb3IoXG4gICAgICAgICAgICAgIHdyYXBwZWRFcnJvcixcbiAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICAgICAgYXdhaXQgZ2V0RW5jcnlwdGlvbktleSgpLFxuICAgICAgICAgICAgICBbXSxcbiAgICAgICAgICAgICAgZ2xvYmFsVGhpcyxcbiAgICAgICAgICAgICAgY29tcHJlc3Npb25cbiAgICAgICAgICAgICksXG4gICAgICAgICAgfSxcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZXJyKSkge1xuICAgICAgICAgIHJ1bnRpbWVMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgICdUcmllZCBmYWlsaW5nIHN0ZXAsIGJ1dCBzdGVwIGhhcyBhbHJlYWR5IGZpbmlzaGVkLicsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICAgIHN0ZXBJZCxcbiAgICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICAgICAgfVxuICAgICAgICAgICk7XG4gICAgICAgICAgcmV0dXJuIHsgdHlwZTogJ3NraXBwZWQnIH07XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgfVxuICAgICAgc3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwU3RhdHVzKCdmYWlsZWQnKSxcbiAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBSZXRyeUV4aGF1c3RlZCh0cnVlKSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHsgdHlwZTogJ2ZhaWxlZCcgfTtcbiAgICB9XG5cbiAgICAvLyBPcHMgdGhhdCBtdXN0IGJlIGR1cmFibHkgY29tbWl0dGVkIGJlZm9yZSBzdGVwIGNvbXBsZXRpb24gKGUuZy4gYVxuICAgIC8vIHN0ZXAtaW5pdGlhdGVkIGFib3J0J3MgaG9va19yZWNlaXZlZCBldmVudCkuIFNlZSBTdGVwQ29udGV4dC4gRGVjbGFyZWRcbiAgICAvLyBvdXRzaWRlIHRoZSB0cnkgc28gdGhlIGZhaWx1cmUgcGF0aCBiZWxvdyBjYW4gYWxzbyBkcmFpbiB0aGVtLlxuICAgIGNvbnN0IHByZUNvbXBsZXRpb25PcHM6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xuICAgIGNvbnN0IG9wczogUHJvbWlzZTx2b2lkPltdID0gW107XG4gICAgbGV0IG9wc1NldHRsZWQgPSB0cnVlO1xuXG4gICAgLy8gTGF0ZW5jeSB0ZWxlbWV0cnkgdG8gYXR0YWNoIHRvIHRoaXMgc3RlcCdzIHRlcm1pbmFsIGV2ZW50LiBDb21wdXRlZFxuICAgIC8vIHJpZ2h0IGJlZm9yZSB1c2VyIGNvZGUgcnVuczsgZGVjbGFyZWQgaGVyZSBzbyB0aGUgZmFpbHVyZSBwYXRoICh0aGVcbiAgICAvLyBjYXRjaCBiZWxvdykgY2FuIGF0dGFjaCBpdCB0byBzdGVwX2ZhaWxlZCB0b28uXG4gICAgbGV0IGxhdGVuY3lFdmVudERhdGE6IFN0ZXBMYXRlbmN5RXZlbnREYXRhIHwgdW5kZWZpbmVkO1xuXG4gICAgLy8gQmFja2ZpbGwgUlNGUyBvbnRvIHRoZSBhbHJlYWR5LWNvbXB1dGVkIHRlbGVtZXRyeSBvbmNlIHRoZSBvcHRpbWlzdGljXG4gICAgLy8gdHVyYm8gc3RhcnQgaGFzIHNldHRsZWQuIE9uIHRoYXQgcGF0aCB0aGUgc3RlcC1zdGFydCBQT1NUIGZpcmVzIGluc2lkZVxuICAgIC8vIHRoZSBydW4tcmVhZHkgYmFycmllcidzIGAudGhlbmAsIHNvIGBzdGVwU3RhcnRQb3N0U2VudEF0TXNgIOKAlCBhbmRcbiAgICAvLyB0aGVyZWZvcmUgUlNGUyDigJQgaXMgdXN1YWxseSBzdGlsbCB1bnNldCB3aGVuIGBsYXRlbmN5RXZlbnREYXRhYCBpc1xuICAgIC8vIGZpcnN0IGNvbXB1dGVkIGp1c3QgYmVmb3JlIHVzZXIgY29kZSAodGhlIGJhcnJpZXIgaXMgc3RpbGwgaW4gZmxpZ2h0XG4gICAgLy8gZm9yIGFueSBub24tdHJpdmlhbCBgcnVuX3N0YXJ0ZWRgIHJvdW5kLXRyaXAsIHdoaWNoIGlzIGV4YWN0bHkgdGhlXG4gICAgLy8gc2xvdy1ydW5fc3RhcnRlZCBjYXNlIFJTRlMgZXhpc3RzIHRvIG1lYXN1cmUpLiBCeSB0aGUgdGltZVxuICAgIC8vIGByZWNvbmNpbGVPcHRpbWlzdGljU3RhcnQoKWAgaGFzIGF3YWl0ZWQgdGhlIGJhcnJpZXIgdGhlIFBPU1QgdGltZXN0YW1wXG4gICAgLy8gaXMga25vd24sIHNvIHdlIHBhdGNoIFJTRlMgaW4gYmVmb3JlIHRoZSB0ZXJtaW5hbCBldmVudCBpcyB3cml0dGVuLlxuICAgIC8vIFdpdGhvdXQgdGhpcywgc2xvdy1ydW5fc3RhcnRlZCBzYW1wbGVzIGFyZSBkcm9wcGVkLCBiaWFzaW5nIFJTRlNcbiAgICAvLyBwZXJjZW50aWxlcyBsb3cgKG1pc3Npbmctbm90LWF0LXJhbmRvbSkuIFJlY29tcHV0ZXMgb25seSBSU0ZTIOKAlFxuICAgIC8vIFRURlMvU1RTTyBzdGF5IGFuY2hvcmVkIHRvIGBleGVjdXRpb25TdGFydFRpbWVgIGFzIGNvbXB1dGVkIGFib3ZlLlxuICAgIGNvbnN0IGJhY2tmaWxsT3B0aW1pc3RpY1JzZnMgPSAoKTogdm9pZCA9PiB7XG4gICAgICBpZiAoIWxhdGVuY3lFdmVudERhdGEgfHwgbGF0ZW5jeUV2ZW50RGF0YS5yc2ZzICE9PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICAgIGNvbnN0IGFuY2hvck1zID0gcGFyYW1zLmxhdGVuY3lUcmFja2luZz8ucnNmc0FuY2hvck1zO1xuICAgICAgaWYgKGFuY2hvck1zID09PSB1bmRlZmluZWQgfHwgc3RlcFN0YXJ0UG9zdFNlbnRBdE1zID09PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICAgIGxhdGVuY3lFdmVudERhdGEucnNmcyA9IE1hdGgubWF4KDAsIHN0ZXBTdGFydFBvc3RTZW50QXRNcyAtIGFuY2hvck1zKTtcbiAgICAgIGlmIChzcGFuKSB7XG4gICAgICAgIHNwYW4uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBSc2ZzTXMobGF0ZW5jeUV2ZW50RGF0YS5yc2ZzKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBhdHRlbXB0ID0gc3RlcC5hdHRlbXB0O1xuXG4gICAgICBpZiAoIXN0ZXAuc3RhcnRlZEF0KSB7XG4gICAgICAgIHRocm93IG5ldyBXb3JrZmxvd1J1bnRpbWVFcnJvcihcbiAgICAgICAgICBgU3RlcCBcIiR7c3RlcElkfVwiIGhhcyBubyBcInN0YXJ0ZWRBdFwiIHRpbWVzdGFtcGBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0ZXBTdGFydGVkQXQgPSBzdGVwLnN0YXJ0ZWRBdDtcbiAgICAgIC8vIFVzZSB0aGUgcHJvdmlkZWQgZW5jcnlwdGlvbiBrZXkgd2hlbiBhdmFpbGFibGUsIG90aGVyd2lzZSByZXNvbHZlXG4gICAgICAvLyB0aHJvdWdoIHRoZSBtZW1vaXplZCBhY2Nlc3NvciBkZWNsYXJlZCBhdCB0aGUgdG9wIG9mIHRoaXMgdHJhY2UuXG4gICAgICBjb25zdCBlbmNyeXB0aW9uS2V5ID0gcGFyYW1zLmVuY3J5cHRpb25LZXkgPz8gKGF3YWl0IGdldEVuY3J5cHRpb25LZXkoKSk7XG4gICAgICBjb25zdCBoeWRyYXRlZElucHV0ID0gYXdhaXQgdHJhY2UoXG4gICAgICAgICdzdGVwLmh5ZHJhdGUnLFxuICAgICAgICB7fSxcbiAgICAgICAgYXN5bmMgKGh5ZHJhdGVTcGFuKSA9PiB7XG4gICAgICAgICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICBjb25zdCBoeWRyYXRlZCA9IGF3YWl0IGh5ZHJhdGVTdGVwQXJndW1lbnRzKFxuICAgICAgICAgICAgc3RlcC5pbnB1dCxcbiAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICBlbmNyeXB0aW9uS2V5LFxuICAgICAgICAgICAgb3BzLFxuICAgICAgICAgICAgZ2xvYmFsVGhpcyxcbiAgICAgICAgICAgIHt9LFxuICAgICAgICAgICAgcGFyYW1zLndvcmtmbG93RGVwbG95bWVudElkXG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBkdXJhdGlvbk1zID0gRGF0ZS5ub3coKSAtIHN0YXJ0VGltZTtcbiAgICAgICAgICBoeWRyYXRlU3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcEFyZ3VtZW50c0NvdW50KGh5ZHJhdGVkLmFyZ3MubGVuZ3RoKSxcbiAgICAgICAgICAgIC4uLkF0dHJpYnV0ZS5RdWV1ZURlc2VyaWFsaXplVGltZU1zKGR1cmF0aW9uTXMpLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBoeWRyYXRlZDtcbiAgICAgICAgfVxuICAgICAgKTtcblxuICAgICAgY29uc3QgYXJncyA9IGh5ZHJhdGVkSW5wdXQuYXJncztcbiAgICAgIGNvbnN0IHRoaXNWYWwgPSBoeWRyYXRlZElucHV0LnRoaXNWYWwgPz8gbnVsbDtcbiAgICAgIGNvbnN0IHdvcmtmbG93QmFzZVVybCA9IGNyZWF0ZVdvcmtmbG93QmFzZVVybChcbiAgICAgICAgaXNWZXJjZWxcbiAgICAgICAgICA/IGBodHRwczovLyR7cHJvY2Vzcy5lbnYuVkVSQ0VMX1VSTH1gXG4gICAgICAgICAgOiBgaHR0cDovL2xvY2FsaG9zdDokeyhhd2FpdCBnZXRQb3J0TGF6eSgpKSA/PyAzMDAwfWBcbiAgICAgICk7XG5cbiAgICAgIC8vIC0tLSBVc2VyIGNvZGUgZXhlY3V0aW9uIC0tLVxuICAgICAgLy8gV3JhcCBvbmx5IHN0ZXBGbi5hcHBseSgpICh1c2VyIHN0ZXAgY29kZSkgc28gY2xlYW51cCBiZWxvdyBydW5zIG9uXG4gICAgICAvLyBCT1RIIHN1Y2Nlc3MgYW5kIGZhaWx1cmUuIEEgdXNlci1jb2RlIHRocm93IGlzIGNhcHR1cmVkIGhlcmUgYW5kXG4gICAgICAvLyByZS1yYWlzZWQgYWZ0ZXIgY2FuY2VsQWJvcnRSZWFkZXJzLCBzbyBpdCBzdGlsbCBmbG93cyB0byB0aGUgb3V0ZXJcbiAgICAgIC8vIGNhdGNoIChzdGVwX2ZhaWxlZC9zdGVwX3JldHJ5aW5nKSDigJQgYnV0IHRoZSBhYm9ydC1zdHJlYW0gcmVhZGVyIGlzXG4gICAgICAvLyB0b3JuIGRvd24gZmlyc3QuIFdpdGhvdXQgdGhpcywgYSB0aHJvd2luZy9yZXRyeWluZyBzaWduYWwtYmVhcmluZ1xuICAgICAgLy8gc3RlcCB3b3VsZCBsZWFrIGEgcmVhbC10aW1lIGFib3J0IHJlYWRlciBwZXIgYXR0ZW1wdC5cbiAgICAgIGxldCB1c2VyQ29kZUVycm9yOiB1bmtub3duO1xuICAgICAgbGV0IHVzZXJDb2RlRmFpbGVkID0gZmFsc2U7XG5cbiAgICAgIGNvbnN0IGV4ZWN1dGlvblN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgICBsYXRlbmN5RXZlbnREYXRhID0gY29tcHV0ZVN0ZXBMYXRlbmN5RXZlbnREYXRhKHtcbiAgICAgICAgdHJhY2tpbmc6IHBhcmFtcy5sYXRlbmN5VHJhY2tpbmcsXG4gICAgICAgIHN0ZXBDb2RlU3RhcnRlZEF0TXM6IGV4ZWN1dGlvblN0YXJ0VGltZSxcbiAgICAgICAgYXR0ZW1wdCxcbiAgICAgICAgbGF6eVN0ZXBTdGFydDogcGFyYW1zLmxhenlTdGVwSW5wdXQgIT09IHVuZGVmaW5lZCxcbiAgICAgICAgb3B0aW1pc3RpY1N0YXJ0LFxuICAgICAgICBzdGVwU3RhcnRQb3N0U2VudEF0TXMsXG4gICAgICB9KTtcbiAgICAgIGlmIChsYXRlbmN5RXZlbnREYXRhKSB7XG4gICAgICAgIC8vIE1pcnJvciB0aGUgbGF0ZW5jeSB0ZWxlbWV0cnkgb250byB0aGUgc3RlcCBzcGFuIHNvIHRyYWNlcyBzaG93XG4gICAgICAgIC8vIFRURlMvU1RTTy9SU0ZTIGFsb25nc2lkZSB0aGUgZmxhbWUgZ3JhcGgsIG5vdCBqdXN0IERhdGFkb2cgbWV0cmljcy5cbiAgICAgICAgc3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgICAgLi4uKGxhdGVuY3lFdmVudERhdGEudHRmcyAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICA/IEF0dHJpYnV0ZS5TdGVwVHRmc01zKGxhdGVuY3lFdmVudERhdGEudHRmcylcbiAgICAgICAgICAgIDoge30pLFxuICAgICAgICAgIC4uLihsYXRlbmN5RXZlbnREYXRhLnN0c28gIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgPyBBdHRyaWJ1dGUuU3RlcFN0c29NcyhsYXRlbmN5RXZlbnREYXRhLnN0c28pXG4gICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICAuLi4obGF0ZW5jeUV2ZW50RGF0YS5yc2ZzICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgID8gQXR0cmlidXRlLlN0ZXBSc2ZzTXMobGF0ZW5jeUV2ZW50RGF0YS5yc2ZzKVxuICAgICAgICAgICAgOiB7fSksXG4gICAgICAgICAgLi4uKGxhdGVuY3lFdmVudERhdGEuZmluYWxTY2hlZHVsaW5nUmVwbGF5ICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgID8gQXR0cmlidXRlLlN0ZXBGaW5hbFNjaGVkdWxpbmdSZXBsYXlNcyhcbiAgICAgICAgICAgICAgICBsYXRlbmN5RXZlbnREYXRhLmZpbmFsU2NoZWR1bGluZ1JlcGxheVxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcExhdGVuY3lPcHRpbWl6YXRpb25zKFxuICAgICAgICAgICAgbGF0ZW5jeUV2ZW50RGF0YS5vcHRpbWl6YXRpb25zID8/IFtdXG4gICAgICAgICAgKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICByZXN1bHQgPSBhd2FpdCB0cmFjZSgnc3RlcC5leGVjdXRlJywge30sIGFzeW5jICgpID0+IHtcbiAgICAgICAgICByZXR1cm4gYXdhaXQgY29udGV4dFN0b3JhZ2UucnVuKFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBzdGVwTWV0YWRhdGE6IHtcbiAgICAgICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgICAgICBzdGVwSWQsXG4gICAgICAgICAgICAgICAgc3RlcFN0YXJ0ZWRBdDogbmV3IERhdGUoK3N0ZXBTdGFydGVkQXQpLFxuICAgICAgICAgICAgICAgIGF0dGVtcHQsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHdvcmtmbG93TWV0YWRhdGE6IHtcbiAgICAgICAgICAgICAgICB3b3JrZmxvd05hbWUsXG4gICAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICAgICAgICB3b3JrZmxvd1N0YXJ0ZWRBdDogbmV3IERhdGUoK3dvcmtmbG93U3RhcnRlZEF0KSxcbiAgICAgICAgICAgICAgICB1cmw6IHdvcmtmbG93QmFzZVVybCxcbiAgICAgICAgICAgICAgICBmZWF0dXJlczogeyBlbmNyeXB0aW9uOiAhIWVuY3J5cHRpb25LZXkgfSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgd29ya2Zsb3dEZXBsb3ltZW50SWQ6IHBhcmFtcy53b3JrZmxvd0RlcGxveW1lbnRJZCxcbiAgICAgICAgICAgICAgcm9vdFJ1bklkOiBwYXJhbXMucm9vdFJ1bklkLFxuICAgICAgICAgICAgICBvcHMsXG4gICAgICAgICAgICAgIHByZUNvbXBsZXRpb25PcHMsXG4gICAgICAgICAgICAgIGNsb3N1cmVWYXJzOiBoeWRyYXRlZElucHV0LmNsb3N1cmVWYXJzLFxuICAgICAgICAgICAgICBlbmNyeXB0aW9uS2V5LFxuICAgICAgICAgICAgICAvLyBUdXJibyBvcHRpbWlzdGljIHN0YXJ0IHJ1bnMgdGhpcyBib2R5IGJlZm9yZSBgcnVuX3N0YXJ0ZWRgIGlzXG4gICAgICAgICAgICAgIC8vIGR1cmFibGUuIEV4cG9zZSB0aGUgYmFycmllciBzbyBhIGRpcmVjdCBzdGVwLWJvZHkgd29ybGQgd3JpdGVcbiAgICAgICAgICAgICAgLy8gKGUuZy4gYHNldEF0dHJpYnV0ZXNgKSBjYW4gb3JkZXIgaXRzZWxmIGFmdGVyIHRoZVxuICAgICAgICAgICAgICAvLyBydW4gZXhpc3RzLiBVbmRlZmluZWQgb24gdGhlIGF3YWl0IHBhdGggKHJ1biBhbHJlYWR5IGR1cmFibGUpLlxuICAgICAgICAgICAgICBydW5SZWFkeUJhcnJpZXI6IG9wdGltaXN0aWNTdGFydFxuICAgICAgICAgICAgICAgID8gcGFyYW1zLnJ1blJlYWR5QmFycmllclxuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICgpID0+IHN0ZXBGbi5hcHBseSh0aGlzVmFsLCBhcmdzKVxuICAgICAgICAgICk7XG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHVzZXJDb2RlRXJyb3IgPSBlcnI7XG4gICAgICAgIHVzZXJDb2RlRmFpbGVkID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGV4ZWN1dGlvblRpbWVNcyA9IERhdGUubm93KCkgLSBleGVjdXRpb25TdGFydFRpbWU7XG5cbiAgICAgIC8vIFRlYXIgZG93biBhbnkgYWJvcnQtc3RyZWFtIHJlYWRlcnMgb3BlbmVkIHdoaWxlIGh5ZHJhdGluZyB0aGUgc3RlcCdzXG4gICAgICAvLyBhcmd1bWVudHMgKGEgc2VyaWFsaXplZCBBYm9ydFNpZ25hbCBvcGVucyBhIHJlYWwtdGltZSBhYm9ydCByZWFkZXIgZm9yXG4gICAgICAvLyB0aGUgc3RlcCdzIGR1cmF0aW9uKS4gV2l0aG91dCB0aGlzIHRoZSByZWFkZXIncyBgcmVhZCgpYCBwcm9taXNlIG5ldmVyXG4gICAgICAvLyBzZXR0bGVzLCBzbyB0aGUgYG9wc2AgZmx1c2ggYmVsb3cgYWx3YXlzIGxvc2VzIHRoZSA1MDBtcyByYWNlIGFuZCB0aGVcbiAgICAgIC8vIHN0ZXAgcmVwb3J0cyBgaGFzUGVuZGluZ09wc2Ag4oCUIGZvcmNpbmcgdGhlIGlubGluZSBsb29wIHRvIHF1ZXVlIGFcbiAgICAgIC8vIGNvbnRpbnVhdGlvbiBhbmQgcGF5aW5nIGEgZnVsbCByb3VuZC10cmlwIHBlciBzaWduYWwtYmVhcmluZyBzdGVwLlxuICAgICAgLy8gUnVucyB1bmNvbmRpdGlvbmFsbHkgKHN1Y2Nlc3Mgb3IgZmFpbHVyZSkgc28gYSB0aHJvd2luZyBzdGVwIGRvZXNuJ3RcbiAgICAgIC8vIGxlYWsgdGhlIHJlYWRlci5cbiAgICAgIGNhbmNlbEFib3J0UmVhZGVycyguLi5hcmdzLCB0aGlzVmFsLCBoeWRyYXRlZElucHV0LmNsb3N1cmVWYXJzKTtcblxuICAgICAgLy8gUmUtcmFpc2UgYSB1c2VyLWNvZGUgZmFpbHVyZSBub3cgdGhhdCBjbGVhbnVwIGhhcyBydW47IHRoZSBvdXRlclxuICAgICAgLy8gY2F0Y2ggbWFwcyBpdCB0byBzdGVwX2ZhaWxlZC9zdGVwX3JldHJ5aW5nLlxuICAgICAgaWYgKHVzZXJDb2RlRmFpbGVkKSB7XG4gICAgICAgIHRocm93IHVzZXJDb2RlRXJyb3I7XG4gICAgICB9XG5cbiAgICAgIHNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgICAuLi5BdHRyaWJ1dGUuUXVldWVFeGVjdXRpb25UaW1lTXMoZXhlY3V0aW9uVGltZU1zKSxcbiAgICAgIH0pO1xuXG4gICAgICByZXN1bHQgPSBhd2FpdCB0cmFjZSgnc3RlcC5kZWh5ZHJhdGUnLCB7fSwgYXN5bmMgKGRlaHlkcmF0ZVNwYW4pID0+IHtcbiAgICAgICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgY29uc3QgZGVoeWRyYXRlZCA9IGF3YWl0IGRlaHlkcmF0ZVN0ZXBSZXR1cm5WYWx1ZShcbiAgICAgICAgICByZXN1bHQsXG4gICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICBlbmNyeXB0aW9uS2V5LFxuICAgICAgICAgIG9wcyxcbiAgICAgICAgICBnbG9iYWxUaGlzLFxuICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgIGNvbXByZXNzaW9uLFxuICAgICAgICAgIC8vIFR1cmJvIG9wdGltaXN0aWMgc3RhcnQ6IGEgcmV0dXJuZWQgc3RyZWFtIGlzIHBpcGVkIHRvIHRoZSBzZXJ2ZXJcbiAgICAgICAgICAvLyBhZnRlciB0aGUgYm9keSBidXQgd2l0aGluIHRoaXMgc2FtZSBvcCBmbHVzaCwgc28gZ2F0ZSBpdHMgZmlyc3RcbiAgICAgICAgICAvLyB3cml0ZSBvbiB0aGUgcnVuLXJlYWR5IGJhcnJpZXIuIFVuZGVmaW5lZCBvbiB0aGUgYXdhaXQgcGF0aC5cbiAgICAgICAgICBvcHRpbWlzdGljU3RhcnQgPyBwYXJhbXMucnVuUmVhZHlCYXJyaWVyIDogdW5kZWZpbmVkXG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IGR1cmF0aW9uTXMgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuICAgICAgICBkZWh5ZHJhdGVTcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAgICAgICAuLi5BdHRyaWJ1dGUuUXVldWVTZXJpYWxpemVUaW1lTXMoZHVyYXRpb25NcyksXG4gICAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBSZXN1bHRUeXBlKHR5cGVvZiBkZWh5ZHJhdGVkKSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBkZWh5ZHJhdGVkO1xuICAgICAgfSk7XG5cbiAgICAgIC8vIEZsdXNoIHBlbmRpbmcgb3BzIChzdHJlYW0gd3JpdGVzLCBldGMuKSB3aXRoIGEgc2hvcnQgaW5saW5lIHdhaXQuXG4gICAgICAvLyBXb3JrZmxvd1NlcnZlcldyaXRhYmxlU3RyZWFtIGFja3Mgd3JpdGVzIG9uIGJ1ZmZlciBlbnRyeVxuICAgICAgLy8gKGdyb3VwLWNvbW1pdCBiYXRjaGluZyk7IGR1cmFiaWxpdHkgaXMgZW5mb3JjZWQgYnkgaXRzIGRyYWluXG4gICAgICAvLyBiYXJyaWVyLCB3aGljaCB0aGUgZmx1c2hhYmxlIHN0YXRlJ3MgY29tcGxldGlvbiBhd2FpdHMgYWZ0ZXJcbiAgICAgIC8vIGxvY2sgcmVsZWFzZS4gTW9zdCBvcHMgc2V0dGxlIHdpdGhpbiB+MjAwbXMgKGxvY2stcmVsZWFzZVxuICAgICAgLy8gcG9sbGluZyArIG9uZSBiYXRjaGVkIEhUVFAgZmx1c2gpLlxuICAgICAgLy8gSWYgb3BzIGRvbid0IHNldHRsZSBpbiA1MDBtcyAoZS5nLiwgV3JpdGFibGVTdHJlYW0ga2VwdCBvcGVuXG4gICAgICAvLyBhY3Jvc3Mgc3RlcHMpLCB3YWl0VW50aWwgaGFuZGxlcyB0aGUgcmVzdC5cbiAgICAgIGlmIChvcHMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCBvcHNQcm9taXNlID0gUHJvbWlzZS5hbGwob3BzKTtcbiAgICAgICAgLy8gVGhlIHJhY2UgYmVsb3cgc3VyZmFjZXMgZmFpbHVyZXMgaW5saW5lIHdoZW4gb3BzIHNldHRsZSBxdWlja2x5O1xuICAgICAgICAvLyBpZiB0aGUgNTAwbXMgdGltZW91dCB3aW5zLCB0aGUgZmFpbHVyZSBpcyBvbmx5IG9ic2VydmVkIGhlcmUuIFRoZVxuICAgICAgICAvLyBwcm9taXNlIGhhbmRlZCB0byB3YWl0VW50aWwgbXVzdCBuZXZlciByZWplY3QgKGFuIHVuY29uc3VtZWRcbiAgICAgICAgLy8gd2FpdFVudGlsIHJlamVjdGlvbiBjcmFzaGVzIHRoZSBwcm9jZXNzIGFzIHVuaGFuZGxlZFJlamVjdGlvbiksXG4gICAgICAgIC8vIHNvIHVuZXhwZWN0ZWQgZmFpbHVyZXMgYXJlIGxvZ2dlZCBpbnN0ZWFkLlxuICAgICAgICBzYWZlV2FpdFVudGlsKG9wc1Byb21pc2UsIChlcnIpID0+IHtcbiAgICAgICAgICBydW50aW1lTG9nZ2VyLndhcm4oJ0JhY2tncm91bmQgZmx1c2ggb2Ygc3RlcCBzdHJlYW0gb3BzIGZhaWxlZCcsIHtcbiAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICBzdGVwSWQsXG4gICAgICAgICAgICBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgb3BzU2V0dGxlZCA9IGF3YWl0IFByb21pc2UucmFjZShbXG4gICAgICAgICAgb3BzUHJvbWlzZS50aGVuKFxuICAgICAgICAgICAgKCkgPT4gdHJ1ZSBhcyBjb25zdCxcbiAgICAgICAgICAgIChlcnIpID0+IHtcbiAgICAgICAgICAgICAgLy8gSWdub3JlIGV4cGVjdGVkIGNsaWVudCBkaXNjb25uZWN0IGVycm9ycyAoZS5nLiwgYnJvd3NlclxuICAgICAgICAgICAgICAvLyByZWZyZXNoIGR1cmluZyBzdHJlYW1pbmcpXG4gICAgICAgICAgICAgIGNvbnN0IGlzQWJvcnRFcnJvciA9XG4gICAgICAgICAgICAgICAgZXJyPy5uYW1lID09PSAnQWJvcnRFcnJvcicgfHwgZXJyPy5uYW1lID09PSAnUmVzcG9uc2VBYm9ydGVkJztcbiAgICAgICAgICAgICAgaWYgKGlzQWJvcnRFcnJvcikgcmV0dXJuIHRydWUgYXMgY29uc3Q7XG4gICAgICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICApLFxuICAgICAgICAgIG5ldyBQcm9taXNlPGZhbHNlPigocikgPT4gc2V0VGltZW91dCgoKSA9PiByKGZhbHNlKSwgNTAwKSksXG4gICAgICAgIF0pO1xuICAgICAgfVxuXG4gICAgICAvLyBPcHRpbWlzdGljIHN0YXJ0OiB0aGUgYm9keSByYW4gYmVmb3JlIGBzdGVwX3N0YXJ0ZWRgIHdhcyBjb25maXJtZWQuXG4gICAgICAvLyBSZWNvbmNpbGUgaXQgbm93IOKAlCBpZiB3ZSBsb3N0IHRoZSBjcmVhdGUtY2xhaW0gKG9yIHRoZSBydW4gaXNcbiAgICAgIC8vIGdvbmUvdGhyb3R0bGVkKSBkaXNjYXJkIHRoaXMgcmVzdWx0IGFuZCBkb24ndCB3cml0ZSBzdGVwX2NvbXBsZXRlZC5cbiAgICAgIC8vIFJlY29uY2lsZSBiZWZvcmUgZHJhaW5pbmcgcHJlQ29tcGxldGlvbk9wczogYSBkaXNjYXJkZWQgcmVzdWx0IG1lYW5zXG4gICAgICAvLyB0aGUgd2lubmluZyBoYW5kbGVyIG93bnMgdGhlIG91dGNvbWUgKGFuZCByZS1maXJlcyBhbnkgYWJvcnRcbiAgICAgIC8vIGlkZW1wb3RlbnRseSksIHNvIHRoZXJlJ3Mgbm8gcG9pbnQgcGF5aW5nIHRoZSBhYm9ydC1jb21taXQgbGF0ZW5jeS5cbiAgICAgIGlmIChvcHRpbWlzdGljU3RhcnQpIHtcbiAgICAgICAgY29uc3QgcmVjb25jaWxlID0gYXdhaXQgcmVjb25jaWxlT3B0aW1pc3RpY1N0YXJ0KCk7XG4gICAgICAgIGlmIChyZWNvbmNpbGUpIHJldHVybiByZWNvbmNpbGU7XG4gICAgICAgIC8vIEJhcnJpZXIgcmVzb2x2ZWQg4oCUIHRoZSBzdGVwLXN0YXJ0IFBPU1QgdGltZXN0YW1wIGlzIG5vdyBrbm93biwgc29cbiAgICAgICAgLy8gUlNGUyBjYW4gYmUgYXR0YWNoZWQgdG8gdGhlIHN0ZXBfY29tcGxldGVkIGV2ZW50IGJlbG93LlxuICAgICAgICBiYWNrZmlsbE9wdGltaXN0aWNSc2ZzKCk7XG4gICAgICB9XG5cbiAgICAgIC8vIENvbW1pdCBtdXN0LWJlLWR1cmFibGUgb3BzIChlLmcuIGEgc3RlcC1pbml0aWF0ZWQgYWJvcnQnc1xuICAgICAgLy8gaG9va19yZWNlaXZlZCBldmVudCkgYmVmb3JlIHdyaXRpbmcgc3RlcF9jb21wbGV0ZWQsIHNvIGFueSB3b3JrZmxvd1xuICAgICAgLy8gY29udGludWF0aW9uIHRyaWdnZXJlZCBieSB0aGF0IGV2ZW50IG9ic2VydmVzIHRoZSBhYm9ydCByYXRoZXIgdGhhblxuICAgICAgLy8gcmFjaW5nIGl0LiBUaGVzZSBvcHMgc3dhbGxvdyB0aGVpciBvd24gZXJyb3JzLCBzbyBhd2FpdGluZyBvbmx5XG4gICAgICAvLyBlbmZvcmNlcyBvcmRlcmluZyAodGhlIGAuY2F0Y2goKWAgZGVmZW5kcyB0aGUgbm8tcmVqZWN0IGNvbnRyYWN0IG9uXG4gICAgICAvLyBTdGVwQ29udGV4dC5wcmVDb21wbGV0aW9uT3BzKS5cbiAgICAgIC8vXG4gICAgICAvLyBUcmFkZW9mZjogY29ycmVjdG5lc3MgcmVxdWlyZXMgdGhlIGhvb2sgYmUgZHVyYWJsZSBiZWZvcmUgY29tcGxldGlvbixcbiAgICAgIC8vIHNvIOKAlCB1bmxpa2UgdGhlIGJhY2tncm91bmQgYG9wc2AgZmx1c2ggYWJvdmUg4oCUIHRoaXMgY2Fubm90IGJlIGNhcHBlZFxuICAgICAgLy8gd2l0aCBhIHJlc29sdmUtb24tdGltZW91dCByYWNlLiBBIHNsb3cgcmVzdW1lIHRoZXJlZm9yZSBhZGRzIGl0c1xuICAgICAgLy8gbGF0ZW5jeSB0byBhIHN0ZXAgdGhhdCBhYm9ydHMgYSBjb250cm9sbGVyLCBhbmQgYSB0cnVlIGhhbmcgaG9sZHNcbiAgICAgIC8vIGNvbXBsZXRpb24gdW50aWwgdGhlIHBsYXRmb3JtL3F1ZXVlIGV4ZWN1dGlvbiB0aW1lb3V0IGZpcmVzOyB0aGUgcXVldWVcbiAgICAgIC8vIHRoZW4gcmVkZWxpdmVycyBhbmQgdGhlIHN0ZXAgcmV0cmllcywgcmUtZmlyaW5nIHRoZSBhYm9ydCBpZGVtcG90ZW50bHkuXG4gICAgICBpZiAocHJlQ29tcGxldGlvbk9wcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHByZUNvbXBsZXRpb25PcHMpLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnI6IHVua25vd24pIHtcbiAgICAgIC8vIE9wdGltaXN0aWMgc3RhcnQ6IHRoZSBib2R5IHRocmV3IGJlZm9yZSBgc3RlcF9zdGFydGVkYCB3YXMgY29uZmlybWVkLlxuICAgICAgLy8gUmVjb25jaWxlIGZpcnN0IOKAlCBpZiB3ZSBsb3N0IHRoZSBjcmVhdGUtY2xhaW0gKG9yIHRoZSBydW4gaXNcbiAgICAgIC8vIGdvbmUvdGhyb3R0bGVkKSB0aGUgYm9keSBlcnJvciBpcyBtb290OyBkaXNjYXJkIGl0IGFuZCBkb24ndCB3cml0ZSBhXG4gICAgICAvLyB0ZXJtaW5hbCBldmVudCAodGhlIHdpbm5pbmcgaGFuZGxlciBvd25zIHRoZSBvdXRjb21lKS4gUmVjb25jaWxlXG4gICAgICAvLyBiZWZvcmUgZHJhaW5pbmcgcHJlQ29tcGxldGlvbk9wcyBmb3IgdGhlIHNhbWUgcmVhc29uIGFzIHRoZSBzdWNjZXNzXG4gICAgICAvLyBwYXRoOiBhIGRpc2NhcmRlZCBvdXRjb21lIGRvZXNuJ3QgbmVlZCB0aGUgYWJvcnQgY29tbWl0dGVkIGhlcmUuXG4gICAgICBpZiAob3B0aW1pc3RpY1N0YXJ0KSB7XG4gICAgICAgIGNvbnN0IHJlY29uY2lsZSA9IGF3YWl0IHJlY29uY2lsZU9wdGltaXN0aWNTdGFydCgpO1xuICAgICAgICBpZiAocmVjb25jaWxlKSByZXR1cm4gcmVjb25jaWxlO1xuICAgICAgICAvLyBCYXJyaWVyIHJlc29sdmVkIOKAlCBhdHRhY2ggUlNGUyB0byB0aGUgc3RlcF9mYWlsZWQgZXZlbnQocykgYmVsb3cuXG4gICAgICAgIGJhY2tmaWxsT3B0aW1pc3RpY1JzZnMoKTtcbiAgICAgIH1cblxuICAgICAgLy8gT3JkZXIgYW55IG11c3QtYmUtZHVyYWJsZSBvcHMgKGUuZy4gYSBzdGVwLWluaXRpYXRlZCBhYm9ydCdzXG4gICAgICAvLyBob29rX3JlY2VpdmVkIGV2ZW50KSBhaGVhZCBvZiBzdGVwX2ZhaWxlZCB0b28g4oCUIGEgc3RlcCB0aGF0IGFib3J0cyBhbmRcbiAgICAgIC8vIHRoZW4gdGhyb3dzIG11c3Qgc3RpbGwgaGF2ZSB0aGUgYWJvcnQgcmVjb3JkZWQgYmVmb3JlIHRoZSBmYWlsdXJlXG4gICAgICAvLyBjb250aW51YXRpb24gb2JzZXJ2ZXMgaXQuIFNhbWUgbGF0ZW5jeSB0cmFkZW9mZiBhbmQgbm8tcmVqZWN0IGNvbnRyYWN0XG4gICAgICAvLyBhcyB0aGUgc3VjY2VzcyBwYXRoIGFib3ZlLiBTZWUgU3RlcENvbnRleHQucHJlQ29tcGxldGlvbk9wcy5cbiAgICAgIGlmIChwcmVDb21wbGV0aW9uT3BzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwocHJlQ29tcGxldGlvbk9wcykuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBlZmZlY3RpdmVFcnIgPSBwcm9tb3RlQWJvcnRFcnJvclRvRmF0YWwoZXJyKTtcblxuICAgICAgY29uc3Qgbm9ybWFsaXplZEVycm9yID0gYXdhaXQgbm9ybWFsaXplVW5rbm93bkVycm9yKGVmZmVjdGl2ZUVycik7XG4gICAgICBjb25zdCBub3JtYWxpemVkU3RhY2sgPVxuICAgICAgICBub3JtYWxpemVkRXJyb3Iuc3RhY2sgfHwgZ2V0RXJyb3JTdGFjayhlZmZlY3RpdmVFcnIpIHx8ICcnO1xuXG4gICAgICBpZiAoZWZmZWN0aXZlRXJyIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgICAgc3Bhbj8ucmVjb3JkRXhjZXB0aW9uPy4oZWZmZWN0aXZlRXJyKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgaXNGYXRhbCA9IEZhdGFsRXJyb3IuaXMoZWZmZWN0aXZlRXJyKTtcblxuICAgICAgc3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwRXJyb3JOYW1lKGdldEVycm9yTmFtZShlZmZlY3RpdmVFcnIpKSxcbiAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBFcnJvck1lc3NhZ2Uobm9ybWFsaXplZEVycm9yLm1lc3NhZ2UpLFxuICAgICAgICAuLi5BdHRyaWJ1dGUuRXJyb3JUeXBlKGdldEVycm9yTmFtZShlZmZlY3RpdmVFcnIpKSxcbiAgICAgICAgLi4uQXR0cmlidXRlLkVycm9yQ2F0ZWdvcnkoXG4gICAgICAgICAgaXNGYXRhbFxuICAgICAgICAgICAgPyAnZmF0YWwnXG4gICAgICAgICAgICA6IFJldHJ5YWJsZUVycm9yLmlzKGVmZmVjdGl2ZUVycilcbiAgICAgICAgICAgICAgPyAncmV0cnlhYmxlJ1xuICAgICAgICAgICAgICA6ICd0cmFuc2llbnQnXG4gICAgICAgICksXG4gICAgICAgIC4uLkF0dHJpYnV0ZS5FcnJvclJldHJ5YWJsZSghaXNGYXRhbCksXG4gICAgICB9KTtcblxuICAgICAgaWYgKFJ1bkV4cGlyZWRFcnJvci5pcyhlcnIpKSB7XG4gICAgICAgIHN0ZXBMb2dnZXIuaW5mbygnV29ya2Zsb3cgcnVuIGFscmVhZHkgY29tcGxldGVkLCBza2lwcGluZyBzdGVwJywge1xuICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgc3RlcElkLFxuICAgICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHsgdHlwZTogJ2dvbmUnIH07XG4gICAgICB9XG5cbiAgICAgIGlmIChpc0ZhdGFsKSB7XG4gICAgICAgIHN0ZXBMb2dnZXIuZXJyb3IoXG4gICAgICAgICAgJ0VuY291bnRlcmVkIEZhdGFsRXJyb3Igd2hpbGUgZXhlY3V0aW5nIHN0ZXAsIGJ1YmJsaW5nIHVwIHRvIHBhcmVudCB3b3JrZmxvdycsXG4gICAgICAgICAgeyB3b3JrZmxvd1J1bklkLCBzdGVwTmFtZSwgZXJyb3JTdGFjazogbm9ybWFsaXplZFN0YWNrIH1cbiAgICAgICAgKTtcbiAgICAgICAgLy8gQXBwbHkgdGhlIG5vcm1hbGl6ZWQgc3RhY2sgdG8gdGhlIHRocm93biB2YWx1ZSBzbyB0aGUgc2VyaWFsaXplZFxuICAgICAgICAvLyBlcnJvciBwcmVzZXJ2ZXMgaXQgZm9yIGNvbnN1bWVycy4gYHR5cGVzLmlzTmF0aXZlRXJyb3IoKWAgd29ya3NcbiAgICAgICAgLy8gYWNyb3NzIFZNIHJlYWxtcyAoYSB3b3JrZmxvdy10aHJvd24gZXJyb3IgaXMgYW4gaW5zdGFuY2Ugb2YgdGhlXG4gICAgICAgIC8vIFZNJ3MgRXJyb3IgY2xhc3MsIG5vdCB0aGUgaG9zdCdzKS5cbiAgICAgICAgaWYgKHR5cGVzLmlzTmF0aXZlRXJyb3IoZWZmZWN0aXZlRXJyKSAmJiBub3JtYWxpemVkU3RhY2spIHtcbiAgICAgICAgICAoZWZmZWN0aXZlRXJyIGFzIEVycm9yKS5zdGFjayA9IG5vcm1hbGl6ZWRTdGFjaztcbiAgICAgICAgfVxuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGNyZWF0ZUV2ZW50KHtcbiAgICAgICAgICAgIGV2ZW50VHlwZTogJ3N0ZXBfZmFpbGVkJyxcbiAgICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IHN0ZXBJZCxcbiAgICAgICAgICAgIGV2ZW50RGF0YToge1xuICAgICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGF3YWl0IGRlaHlkcmF0ZVN0ZXBFcnJvcihcbiAgICAgICAgICAgICAgICBlZmZlY3RpdmVFcnIsXG4gICAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICAgICAgICBhd2FpdCBnZXRFbmNyeXB0aW9uS2V5KCksXG4gICAgICAgICAgICAgICAgW10sXG4gICAgICAgICAgICAgICAgZ2xvYmFsVGhpcyxcbiAgICAgICAgICAgICAgICBjb21wcmVzc2lvblxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAuLi5sYXRlbmN5RXZlbnREYXRhLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBjYXRjaCAoc3RlcEZhaWxFcnIpIHtcbiAgICAgICAgICBpZiAoRW50aXR5Q29uZmxpY3RFcnJvci5pcyhzdGVwRmFpbEVycikpIHtcbiAgICAgICAgICAgIHJ1bnRpbWVMb2dnZXIuaW5mbyhcbiAgICAgICAgICAgICAgJ1RyaWVkIGZhaWxpbmcgc3RlcCwgYnV0IHN0ZXAgaGFzIGFscmVhZHkgZmluaXNoZWQuJyxcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICAgICAgc3RlcElkLFxuICAgICAgICAgICAgICAgIHN0ZXBOYW1lLFxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IHN0ZXBGYWlsRXJyLm1lc3NhZ2UsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm4geyB0eXBlOiAnc2tpcHBlZCcgfTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhyb3cgc3RlcEZhaWxFcnI7XG4gICAgICAgIH1cbiAgICAgICAgc3Bhbj8uc2V0QXR0cmlidXRlcyh7XG4gICAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBTdGF0dXMoJ2ZhaWxlZCcpLFxuICAgICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwRmF0YWxFcnJvcih0cnVlKSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB7IHR5cGU6ICdmYWlsZWQnIH07XG4gICAgICB9XG5cbiAgICAgIC8vIE5vbi1mYXRhbCBlcnJvcjogY2hlY2sgaWYgcmV0cmllcyByZW1haW5pbmdcbiAgICAgIGNvbnN0IGN1cnJlbnRBdHRlbXB0ID0gc3RlcC5hdHRlbXB0O1xuXG4gICAgICBzcGFuPy5zZXRBdHRyaWJ1dGVzKHtcbiAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBBdHRlbXB0KGN1cnJlbnRBdHRlbXB0KSxcbiAgICAgICAgLi4uQXR0cmlidXRlLlN0ZXBNYXhSZXRyaWVzKG1heFJldHJpZXMpLFxuICAgICAgfSk7XG5cbiAgICAgIGlmIChjdXJyZW50QXR0ZW1wdCA+PSBtYXhSZXRyaWVzICsgMSkge1xuICAgICAgICAvLyBNYXggcmV0cmllcyByZWFjaGVkXG4gICAgICAgIGNvbnN0IHJldHJ5Q291bnQgPSBzdGVwLmF0dGVtcHQgLSAxO1xuICAgICAgICBzdGVwTG9nZ2VyLmVycm9yKFxuICAgICAgICAgICdNYXggcmV0cmllcyByZWFjaGVkLCBidWJibGluZyBlcnJvciB0byBwYXJlbnQgd29ya2Zsb3cnLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgIGF0dGVtcHQ6IHN0ZXAuYXR0ZW1wdCxcbiAgICAgICAgICAgIHJldHJ5Q291bnQsXG4gICAgICAgICAgICBlcnJvclN0YWNrOiBub3JtYWxpemVkU3RhY2ssXG4gICAgICAgICAgfVxuICAgICAgICApO1xuICAgICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBgU3RlcCBcIiR7c3RlcE5hbWV9XCIgZmFpbGVkIGFmdGVyICR7bWF4UmV0cmllc30gJHtwbHVyYWxpemUoJ3JldHJ5JywgJ3JldHJpZXMnLCBtYXhSZXRyaWVzKX06ICR7bm9ybWFsaXplZEVycm9yLm1lc3NhZ2V9YDtcbiAgICAgICAgLy8gV3JhcCB0aGUgb3JpZ2luYWwgdGhyb3duIHZhbHVlIGFzIGBjYXVzZWAgb24gYSBmcmVzaCBGYXRhbEVycm9yXG4gICAgICAgIC8vIHNvIHRoZSB3cmFwcGluZyBtZXNzYWdlICsgcmV0cnktY291bnQgZnJhbWluZyBpcyB0aGUgdXNlci1mYWNpbmdcbiAgICAgICAgLy8gZXJyb3Igd2hpbGUgdGhlIG9yaWdpbmFsIGZhaWx1cmUgcmVtYWlucyByZWNvdmVyYWJsZSBmcm9tXG4gICAgICAgIC8vIGBlcnIuY2F1c2VgIGFmdGVyIGh5ZHJhdGlvbi5cbiAgICAgICAgY29uc3Qgd3JhcHBlZEVycm9yID0gbmV3IEZhdGFsRXJyb3IoZXJyb3JNZXNzYWdlKTtcbiAgICAgICAgKHdyYXBwZWRFcnJvciBhcyBFcnJvcikuY2F1c2UgPSBlcnI7XG4gICAgICAgIGlmIChub3JtYWxpemVkU3RhY2spIHdyYXBwZWRFcnJvci5zdGFjayA9IG5vcm1hbGl6ZWRTdGFjaztcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBjcmVhdGVFdmVudCh7XG4gICAgICAgICAgICBldmVudFR5cGU6ICdzdGVwX2ZhaWxlZCcsXG4gICAgICAgICAgICBzcGVjVmVyc2lvbjogU1BFQ19WRVJTSU9OX0NVUlJFTlQsXG4gICAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzdGVwSWQsXG4gICAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICAgIGVycm9yOiBhd2FpdCBkZWh5ZHJhdGVTdGVwRXJyb3IoXG4gICAgICAgICAgICAgICAgd3JhcHBlZEVycm9yLFxuICAgICAgICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgICAgICAgYXdhaXQgZ2V0RW5jcnlwdGlvbktleSgpLFxuICAgICAgICAgICAgICAgIFtdLFxuICAgICAgICAgICAgICAgIGdsb2JhbFRoaXMsXG4gICAgICAgICAgICAgICAgY29tcHJlc3Npb25cbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgLi4ubGF0ZW5jeUV2ZW50RGF0YSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gY2F0Y2ggKHN0ZXBGYWlsRXJyKSB7XG4gICAgICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoc3RlcEZhaWxFcnIpKSB7XG4gICAgICAgICAgICBydW50aW1lTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICAgICdUcmllZCBmYWlsaW5nIHN0ZXAsIGJ1dCBzdGVwIGhhcyBhbHJlYWR5IGZpbmlzaGVkLicsXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB3b3JrZmxvd1J1bklkLFxuICAgICAgICAgICAgICAgIHN0ZXBJZCxcbiAgICAgICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBzdGVwRmFpbEVyci5tZXNzYWdlLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogJ3NraXBwZWQnIH07XG4gICAgICAgICAgfVxuICAgICAgICAgIHRocm93IHN0ZXBGYWlsRXJyO1xuICAgICAgICB9XG4gICAgICAgIHNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgICAgIC4uLkF0dHJpYnV0ZS5TdGVwU3RhdHVzKCdmYWlsZWQnKSxcbiAgICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcFJldHJ5RXhoYXVzdGVkKHRydWUpLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHsgdHlwZTogJ2ZhaWxlZCcgfTtcbiAgICAgIH1cblxuICAgICAgLy8gUmV0cmllcyByZW1haW5pbmdcbiAgICAgIGlmIChSZXRyeWFibGVFcnJvci5pcyhlcnIpKSB7XG4gICAgICAgIHN0ZXBMb2dnZXIuaW5mbygnRW5jb3VudGVyZWQgUmV0cnlhYmxlRXJyb3IsIHN0ZXAgd2lsbCBiZSByZXRyaWVkJywge1xuICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgYXR0ZW1wdDogY3VycmVudEF0dGVtcHQsXG4gICAgICAgICAgbWVzc2FnZTogZXJyLm1lc3NhZ2UsXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc3RlcExvZ2dlci5pbmZvKCdFbmNvdW50ZXJlZCBFcnJvciwgc3RlcCB3aWxsIGJlIHJldHJpZWQnLCB7XG4gICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICBhdHRlbXB0OiBjdXJyZW50QXR0ZW1wdCxcbiAgICAgICAgICBlcnJvclN0YWNrOiBub3JtYWxpemVkU3RhY2ssXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICAvLyBBcHBseSB0aGUgbm9ybWFsaXplZCBzdGFjayB0byB0aGUgdGhyb3duIHZhbHVlIHNvIGl0IHN1cnZpdmVzXG4gICAgICAvLyBzZXJpYWxpemF0aW9uLiBTZWUgdGhlIEZhdGFsRXJyb3Igc2l0ZSBhYm92ZSBmb3Igd2h5IHdlIHVzZVxuICAgICAgLy8gYHR5cGVzLmlzTmF0aXZlRXJyb3JgIGluc3RlYWQgb2YgYGVyciBpbnN0YW5jZW9mIEVycm9yYC5cbiAgICAgIGlmICh0eXBlcy5pc05hdGl2ZUVycm9yKGVycikgJiYgbm9ybWFsaXplZFN0YWNrKSB7XG4gICAgICAgIChlcnIgYXMgRXJyb3IpLnN0YWNrID0gbm9ybWFsaXplZFN0YWNrO1xuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY3JlYXRlRXZlbnQoe1xuICAgICAgICAgIGV2ZW50VHlwZTogJ3N0ZXBfcmV0cnlpbmcnLFxuICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICBjb3JyZWxhdGlvbklkOiBzdGVwSWQsXG4gICAgICAgICAgZXZlbnREYXRhOiB7XG4gICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgIGVycm9yOiBhd2FpdCBkZWh5ZHJhdGVTdGVwRXJyb3IoXG4gICAgICAgICAgICAgIGVycixcbiAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICAgICAgYXdhaXQgZ2V0RW5jcnlwdGlvbktleSgpLFxuICAgICAgICAgICAgICBbXSxcbiAgICAgICAgICAgICAgZ2xvYmFsVGhpcyxcbiAgICAgICAgICAgICAgY29tcHJlc3Npb25cbiAgICAgICAgICAgICksXG4gICAgICAgICAgICAuLi4oUmV0cnlhYmxlRXJyb3IuaXMoZXJyKSAmJiB7IHJldHJ5QWZ0ZXI6IGVyci5yZXRyeUFmdGVyIH0pLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoc3RlcFJldHJ5RXJyKSB7XG4gICAgICAgIGlmIChFbnRpdHlDb25mbGljdEVycm9yLmlzKHN0ZXBSZXRyeUVycikpIHtcbiAgICAgICAgICBydW50aW1lTG9nZ2VyLmluZm8oXG4gICAgICAgICAgICAnVHJpZWQgcmV0cnlpbmcgc3RlcCwgYnV0IHN0ZXAgaGFzIGFscmVhZHkgZmluaXNoZWQuJyxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgICAgICAgc3RlcElkLFxuICAgICAgICAgICAgICBzdGVwTmFtZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogc3RlcFJldHJ5RXJyLm1lc3NhZ2UsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXR1cm4geyB0eXBlOiAnc2tpcHBlZCcgfTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBzdGVwUmV0cnlFcnI7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHRpbWVvdXRTZWNvbmRzID0gTWF0aC5tYXgoXG4gICAgICAgIDEsXG4gICAgICAgIFJldHJ5YWJsZUVycm9yLmlzKGVycilcbiAgICAgICAgICA/IE1hdGguY2VpbCgoK2Vyci5yZXRyeUFmdGVyLmdldFRpbWUoKSAtIERhdGUubm93KCkpIC8gMTAwMClcbiAgICAgICAgICA6IDFcbiAgICAgICk7XG5cbiAgICAgIHNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcFJldHJ5VGltZW91dFNlY29uZHModGltZW91dFNlY29uZHMpLFxuICAgICAgICAuLi5BdHRyaWJ1dGUuU3RlcFJldHJ5V2lsbFJldHJ5KHRydWUpLFxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiB7IHR5cGU6ICdyZXRyeScsIHRpbWVvdXRTZWNvbmRzIH07XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIHN0ZXBfY29tcGxldGVkIGV2ZW50IG91dHNpZGUgdGhlIHN0ZXAgZXhlY3V0aW9uIGZhaWx1cmUgcGF0aDpcbiAgICAvLyBwZXJzaXN0ZW5jZSBmYWlsdXJlcyBhcmUgaW5mcmFzdHJ1Y3R1cmUgZXJyb3JzIGFuZCBzaG91bGQgcmVkZWxpdmVyIHRoZVxuICAgIC8vIHF1ZXVlIG1lc3NhZ2UsIG5vdCBiZWNvbWUgdXNlciBzdGVwX3JldHJ5aW5nL3N0ZXBfZmFpbGVkIGV2ZW50cy5cbiAgICBsZXQgY29tcGxldGVkUmVzdWx0OiBFdmVudFJlc3VsdDtcbiAgICB0cnkge1xuICAgICAgY29tcGxldGVkUmVzdWx0ID0gYXdhaXQgY3JlYXRlRXZlbnQoXG4gICAgICAgIHtcbiAgICAgICAgICBldmVudFR5cGU6ICdzdGVwX2NvbXBsZXRlZCcsXG4gICAgICAgICAgc3BlY1ZlcnNpb246IFNQRUNfVkVSU0lPTl9DVVJSRU5ULFxuICAgICAgICAgIGNvcnJlbGF0aW9uSWQ6IHN0ZXBJZCxcbiAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgIHN0ZXBOYW1lLFxuICAgICAgICAgICAgd29ya2Zsb3dOYW1lLFxuICAgICAgICAgICAgcmVzdWx0OiByZXN1bHQgYXMgVWludDhBcnJheSxcbiAgICAgICAgICAgIC4uLmxhdGVuY3lFdmVudERhdGEsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgICAgcGFyYW1zLmlubGluZURlbHRhU2luY2VDdXJzb3IgIT09IHVuZGVmaW5lZFxuICAgICAgICAgID8geyBzaW5jZUN1cnNvcjogcGFyYW1zLmlubGluZURlbHRhU2luY2VDdXJzb3IgfVxuICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgaWYgKEVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZXJyKSkge1xuICAgICAgICBydW50aW1lTG9nZ2VyLmluZm8oXG4gICAgICAgICAgJ1RyaWVkIGNvbXBsZXRpbmcgc3RlcCwgYnV0IHN0ZXAgaGFzIGFscmVhZHkgZmluaXNoZWQuJyxcbiAgICAgICAgICB7XG4gICAgICAgICAgICB3b3JrZmxvd1J1bklkLFxuICAgICAgICAgICAgc3RlcElkLFxuICAgICAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgICAgICBtZXNzYWdlOiBlcnIubWVzc2FnZSxcbiAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiB7IHR5cGU6ICdza2lwcGVkJyB9O1xuICAgICAgfVxuICAgICAgaWYgKFJ1bkV4cGlyZWRFcnJvci5pcyhlcnIpKSB7XG4gICAgICAgIHN0ZXBMb2dnZXIuaW5mbygnV29ya2Zsb3cgcnVuIGFscmVhZHkgY29tcGxldGVkLCBza2lwcGluZyBzdGVwJywge1xuICAgICAgICAgIHdvcmtmbG93UnVuSWQsXG4gICAgICAgICAgc3RlcElkLFxuICAgICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHsgdHlwZTogJ2dvbmUnIH07XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuXG4gICAgY29uc3QgaW5saW5lRGVsdGEgPSBleHRyYWN0SW5saW5lRGVsdGEoXG4gICAgICBjb21wbGV0ZWRSZXN1bHQsXG4gICAgICBwYXJhbXMuaW5saW5lRGVsdGFTaW5jZUN1cnNvciAhPT0gdW5kZWZpbmVkXG4gICAgKTtcblxuICAgIHNwYW4/LnNldEF0dHJpYnV0ZXMoe1xuICAgICAgLi4uQXR0cmlidXRlLlN0ZXBTdGF0dXMoJ2NvbXBsZXRlZCcpLFxuICAgICAgLi4uQXR0cmlidXRlLlN0ZXBSZXN1bHRUeXBlKHR5cGVvZiByZXN1bHQpLFxuICAgIH0pO1xuXG4gICAgaWYgKG9wcy5sZW5ndGggPiAwKSB7XG4gICAgICBzdGVwTG9nZ2VyLmRlYnVnKCdTdGVwIGhhcyBwZW5kaW5nIG9wcycsIHtcbiAgICAgICAgd29ya2Zsb3dSdW5JZCxcbiAgICAgICAgc3RlcE5hbWUsXG4gICAgICAgIG9wc0NvdW50OiBvcHMubGVuZ3RoLFxuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIGhhc1BlbmRpbmdPcHMgc2lnbmFscyB0aGUgY29tYmluZWQgaGFuZGxlciB0byBicmVhayB0aGUgbG9vcFxuICAgIC8vIGFuZCBxdWV1ZSBhIGNvbnRpbnVhdGlvbiBzbyB3YWl0VW50aWwgY2FuIGZsdXNoIHRoZW0uXG4gICAgcmV0dXJuIHsgdHlwZTogJ2NvbXBsZXRlZCcsIGhhc1BlbmRpbmdPcHM6ICFvcHNTZXR0bGVkLCBpbmxpbmVEZWx0YSB9O1xuICB9KTtcbn1cbiJdfQ==
|