@pipeline-builder/pipeline-core 0.0.1
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/LICENSE +202 -0
- package/README.md +69 -0
- package/lib/cdk.d.ts +38 -0
- package/lib/cdk.js +44 -0
- package/lib/config/app-config.d.ts +109 -0
- package/lib/config/app-config.js +197 -0
- package/lib/config/aws-config-cdk.d.ts +49 -0
- package/lib/config/aws-config-cdk.js +65 -0
- package/lib/config/billing-config.d.ts +24 -0
- package/lib/config/billing-config.js +497 -0
- package/lib/config/config-types.d.ts +346 -0
- package/lib/config/config-types.js +4 -0
- package/lib/config/entitlements.d.ts +23 -0
- package/lib/config/entitlements.js +46 -0
- package/lib/config/handler-constants.d.ts +10 -0
- package/lib/config/handler-constants.js +39 -0
- package/lib/config/infrastructure-config.d.ts +66 -0
- package/lib/config/infrastructure-config.js +197 -0
- package/lib/config/server-config.d.ts +53 -0
- package/lib/config/server-config.js +175 -0
- package/lib/config/service-client.d.ts +13 -0
- package/lib/config/service-client.js +20 -0
- package/lib/core/artifact-manager.d.ts +62 -0
- package/lib/core/artifact-manager.js +83 -0
- package/lib/core/id-generator.d.ts +50 -0
- package/lib/core/id-generator.js +50 -0
- package/lib/core/metadata-builder.d.ts +48 -0
- package/lib/core/metadata-builder.js +278 -0
- package/lib/core/metadata-helpers.d.ts +31 -0
- package/lib/core/metadata-helpers.js +55 -0
- package/lib/core/network-types.d.ts +200 -0
- package/lib/core/network-types.js +4 -0
- package/lib/core/network.d.ts +33 -0
- package/lib/core/network.js +112 -0
- package/lib/core/pipeline-helpers.d.ts +61 -0
- package/lib/core/pipeline-helpers.js +563 -0
- package/lib/core/pipeline-types.d.ts +118 -0
- package/lib/core/pipeline-types.js +131 -0
- package/lib/core/role-types.d.ts +254 -0
- package/lib/core/role-types.js +4 -0
- package/lib/core/role.d.ts +14 -0
- package/lib/core/role.js +117 -0
- package/lib/core/security-group-types.d.ts +84 -0
- package/lib/core/security-group-types.js +4 -0
- package/lib/core/security-group.d.ts +14 -0
- package/lib/core/security-group.js +33 -0
- package/lib/handlers/plugin-lookup-handler.d.ts +32 -0
- package/lib/handlers/plugin-lookup-handler.js +292 -0
- package/lib/handlers/pnpm-lock.yaml +12 -0
- package/lib/index.d.ts +45 -0
- package/lib/index.js +53 -0
- package/lib/pipeline/pipeline-builder.d.ts +123 -0
- package/lib/pipeline/pipeline-builder.js +476 -0
- package/lib/pipeline/pipeline-configuration.d.ts +81 -0
- package/lib/pipeline/pipeline-configuration.js +214 -0
- package/lib/pipeline/plugin-lookup.d.ts +131 -0
- package/lib/pipeline/plugin-lookup.js +316 -0
- package/lib/pipeline/plugin-spec.d.ts +138 -0
- package/lib/pipeline/plugin-spec.js +4 -0
- package/lib/pipeline/source-builder.d.ts +62 -0
- package/lib/pipeline/source-builder.js +164 -0
- package/lib/pipeline/source-types.d.ts +221 -0
- package/lib/pipeline/source-types.js +4 -0
- package/lib/pipeline/stage-builder.d.ts +78 -0
- package/lib/pipeline/stage-builder.js +129 -0
- package/lib/pipeline/step-types.d.ts +222 -0
- package/lib/pipeline/step-types.js +4 -0
- package/lib/template/evaluator.d.ts +42 -0
- package/lib/template/evaluator.js +164 -0
- package/lib/template/index.d.ts +34 -0
- package/lib/template/index.js +118 -0
- package/lib/template/metrics.d.ts +10 -0
- package/lib/template/metrics.js +43 -0
- package/lib/template/plugin-resolver.d.ts +8 -0
- package/lib/template/plugin-resolver.js +48 -0
- package/lib/template/tokenizer.d.ts +49 -0
- package/lib/template/tokenizer.js +233 -0
- package/lib/template/topo-sort.d.ts +17 -0
- package/lib/template/topo-sort.js +88 -0
- package/lib/template/validate.d.ts +45 -0
- package/lib/template/validate.js +96 -0
- package/lib/template/walker.d.ts +34 -0
- package/lib/template/walker.js +83 -0
- package/package.json +184 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { createLogger, SYSTEM_ORG_ID } from '@pipeline-builder/api-core';
|
|
4
|
+
import { Duration, SecretValue, Stack } from 'aws-cdk-lib';
|
|
5
|
+
import { BuildEnvironmentVariableType, BuildSpec, ComputeType as CDKComputeType, LinuxBuildImage } from 'aws-cdk-lib/aws-codebuild';
|
|
6
|
+
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
|
|
7
|
+
import { CodeBuildStep, ManualApprovalStep, ShellStep } from 'aws-cdk-lib/pipelines';
|
|
8
|
+
import { metadataForShellStep, metadataForCodeBuildStep, metadataForBuildEnvironment, networkConfigFromMetadata } from './metadata-builder.js';
|
|
9
|
+
import { extractMetadataEnv, merge } from './metadata-helpers.js';
|
|
10
|
+
import { resolveNetwork } from './network.js';
|
|
11
|
+
import { PluginType, ComputeType } from './pipeline-types.js';
|
|
12
|
+
import { Config, CoreConstants } from '../config/app-config.js';
|
|
13
|
+
import { resolvePluginTemplates } from '../template/plugin-resolver.js';
|
|
14
|
+
const log = createLogger('pipeline-helpers');
|
|
15
|
+
/**
|
|
16
|
+
* Build environment variables from plugin config, merged metadata, and custom env.
|
|
17
|
+
*
|
|
18
|
+
* Merge order (last wins):
|
|
19
|
+
* 1. plugin.env — plugin default env vars (lowest priority)
|
|
20
|
+
* 2. non-namespaced metadata keys — e.g. PYTHON_VERSION, WORKDIR
|
|
21
|
+
* 3. customEnv — per-step custom env vars (highest priority)
|
|
22
|
+
*/
|
|
23
|
+
const BOOTSTRAP_CMD = 'export WORKDIR=${WORKDIR:-./}; cd ${WORKDIR}';
|
|
24
|
+
function buildEnv(plugin, metadata, customEnv) {
|
|
25
|
+
return {
|
|
26
|
+
...(plugin.env ?? {}),
|
|
27
|
+
...extractMetadataEnv(metadata),
|
|
28
|
+
...(customEnv ?? {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wrap build commands based on failure behavior.
|
|
33
|
+
* - 'fail' (default): No wrapping — commands fail the pipeline naturally.
|
|
34
|
+
* - 'warn': Run commands with `set +e`, capture failures, log warnings, continue.
|
|
35
|
+
* - 'ignore': Append `|| true` to each command — failures are silently swallowed.
|
|
36
|
+
*
|
|
37
|
+
* Only applied to build commands, not install commands (install failures should always stop the build).
|
|
38
|
+
*/
|
|
39
|
+
function wrapCommandsForFailureBehavior(commands, behavior) {
|
|
40
|
+
if (!behavior || behavior === 'fail')
|
|
41
|
+
return commands;
|
|
42
|
+
if (behavior === 'ignore') {
|
|
43
|
+
return commands.map(cmd => `${cmd} || true`);
|
|
44
|
+
}
|
|
45
|
+
// 'warn': run all commands, capture failures, but don't stop
|
|
46
|
+
return [
|
|
47
|
+
'set +e',
|
|
48
|
+
'_STEP_EXIT=0',
|
|
49
|
+
...commands.map(cmd => `${cmd} || { echo "WARNING: Command failed with exit code $?"; _STEP_EXIT=1; }`),
|
|
50
|
+
'set -e',
|
|
51
|
+
'if [ "$_STEP_EXIT" -ne 0 ]; then echo "WARNING: One or more commands in this step failed"; fi',
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build bootstrap-prefixed install and build commands from plugin config.
|
|
56
|
+
* Each command list is prepended with a WORKDIR bootstrap that defaults to './'.
|
|
57
|
+
* When custom commands are provided, they are injected before/after the plugin's commands.
|
|
58
|
+
* Build commands are optionally wrapped by failureBehavior logic.
|
|
59
|
+
*/
|
|
60
|
+
function buildCommands(plugin, custom, failureBehavior) {
|
|
61
|
+
const userCommands = [
|
|
62
|
+
...(custom?.preCommands ?? []),
|
|
63
|
+
...(plugin.commands?.length ? plugin.commands : []),
|
|
64
|
+
...(custom?.postCommands ?? []),
|
|
65
|
+
];
|
|
66
|
+
return {
|
|
67
|
+
installCommands: [
|
|
68
|
+
BOOTSTRAP_CMD,
|
|
69
|
+
...(custom?.preInstallCommands ?? []),
|
|
70
|
+
...(plugin.installCommands ?? []),
|
|
71
|
+
...(custom?.postInstallCommands ?? []),
|
|
72
|
+
],
|
|
73
|
+
commands: [BOOTSTRAP_CMD, ...wrapCommandsForFailureBehavior(userCommands, failureBehavior)],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Convert a plain env record to CodeBuild's environmentVariables format (PLAINTEXT).
|
|
78
|
+
*/
|
|
79
|
+
function toCodeBuildEnvVars(env) {
|
|
80
|
+
return Object.fromEntries(Object.entries(env).map(([name, value]) => [name, { value }]));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build SECRETS_MANAGER-type environment variables from plugin secret declarations.
|
|
84
|
+
* Uses naming convention: pipeline-builder/{orgId}/{secretName}
|
|
85
|
+
* Each org manages these secrets in their own AWS Secrets Manager.
|
|
86
|
+
*/
|
|
87
|
+
const VALID_SECRET_NAME = /^[a-zA-Z0-9/_+=.@-]+$/;
|
|
88
|
+
function toSecretEnvVars(secrets, orgId) {
|
|
89
|
+
return Object.fromEntries(secrets.map(({ name }) => {
|
|
90
|
+
const secretPath = CoreConstants.secretPath(orgId, name);
|
|
91
|
+
if (!VALID_SECRET_NAME.test(secretPath)) {
|
|
92
|
+
throw new Error(`Secret path "${secretPath}" contains invalid characters for AWS Secrets Manager`);
|
|
93
|
+
}
|
|
94
|
+
return [
|
|
95
|
+
name,
|
|
96
|
+
{
|
|
97
|
+
value: secretPath,
|
|
98
|
+
type: BuildEnvironmentVariableType.SECRETS_MANAGER,
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* In-cluster / local registry hostnames that AWS CodeBuild (out-of-cluster)
|
|
105
|
+
* cannot resolve. Baking one of these into a CodeBuild image URI guarantees a
|
|
106
|
+
* `BUILD_CONTAINER_UNABLE_TO_PULL_IMAGE` failure at build time, so we fail the
|
|
107
|
+
* synth fast instead — with a message that says how to fix it.
|
|
108
|
+
*/
|
|
109
|
+
const UNREACHABLE_PULL_HOSTS = new Set(['registry', 'localhost', '127.0.0.1', '::1', '0.0.0.0']);
|
|
110
|
+
/** Kubernetes / mDNS suffixes that only resolve inside the cluster/LAN. */
|
|
111
|
+
const UNREACHABLE_HOST_SUFFIXES = ['.svc', '.svc.cluster.local', '.local'];
|
|
112
|
+
/**
|
|
113
|
+
* Reduce a registry host value to its bare hostname for the reachability check:
|
|
114
|
+
* strip IPv6 brackets and any `:port` suffix (an operator may put host+port in
|
|
115
|
+
* one field), so `registry:5000` / `[::1]:5000` are still recognized.
|
|
116
|
+
*/
|
|
117
|
+
function bareHostname(rawHost) {
|
|
118
|
+
let h = rawHost.trim().toLowerCase();
|
|
119
|
+
if (h.startsWith('[')) {
|
|
120
|
+
const end = h.indexOf(']');
|
|
121
|
+
if (end !== -1)
|
|
122
|
+
return h.slice(1, end); // [::1]:5000 -> ::1
|
|
123
|
+
}
|
|
124
|
+
// Strip a trailing :port only for non-IPv6 values (IPv6 contains '::' or >1 ':').
|
|
125
|
+
if (h.includes(':') && (h.match(/:/g)?.length ?? 0) === 1)
|
|
126
|
+
h = h.split(':')[0];
|
|
127
|
+
return h;
|
|
128
|
+
}
|
|
129
|
+
/** True when CodeBuild (out-of-cluster) could not resolve this registry host. */
|
|
130
|
+
function isUnreachablePullHost(rawHost) {
|
|
131
|
+
const h = bareHostname(rawHost);
|
|
132
|
+
if (h === '')
|
|
133
|
+
return true; // no host → malformed URI
|
|
134
|
+
if (UNREACHABLE_PULL_HOSTS.has(h))
|
|
135
|
+
return true;
|
|
136
|
+
return UNREACHABLE_HOST_SUFFIXES.some((s) => h.endsWith(s));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the external (out-of-cluster) registry pull host + `:port` suffix for
|
|
140
|
+
* a CodeBuild image URI, and fail fast if it resolved to an in-cluster/local
|
|
141
|
+
* default that CodeBuild can't reach.
|
|
142
|
+
*
|
|
143
|
+
* Prefers `pullHost`/`pullPort` (the public endpoint CodeBuild can resolve) and
|
|
144
|
+
* falls back to `host`/`port` for single-host deploys. pipeline-manager bakes
|
|
145
|
+
* the right pull host into the synth automatically from the platform URL it
|
|
146
|
+
* deploys against (`props.registry`); this guard catches the case where neither
|
|
147
|
+
* that nor `IMAGE_REGISTRY_PULL_HOST`/`PLATFORM_BASE_URL` was supplied.
|
|
148
|
+
*
|
|
149
|
+
* Escape hatch: `ALLOW_INCLUSTER_PULL_HOST=true` (e.g. CodeBuild in a VPC whose
|
|
150
|
+
* private DNS genuinely resolves the in-cluster name).
|
|
151
|
+
*/
|
|
152
|
+
function resolveExternalPullTarget(registry) {
|
|
153
|
+
const host = registry.pullHost || registry.host || '';
|
|
154
|
+
const port = registry.pullPort ?? registry.port;
|
|
155
|
+
if (process.env.ALLOW_INCLUSTER_PULL_HOST !== 'true' && isUnreachablePullHost(host)) {
|
|
156
|
+
throw new Error(`Registry pull host resolved to '${host}', which AWS CodeBuild cannot reach — it is the ` +
|
|
157
|
+
'in-cluster/local default. The synthesized pipeline would fail at build time with ' +
|
|
158
|
+
'BUILD_CONTAINER_UNABLE_TO_PULL_IMAGE. Set the platform\'s public host via ' +
|
|
159
|
+
'IMAGE_REGISTRY_PULL_HOST or PLATFORM_BASE_URL before deploying — pipeline-manager bakes ' +
|
|
160
|
+
'this in automatically from the platform URL it deploys against. ' +
|
|
161
|
+
'Set ALLOW_INCLUSTER_PULL_HOST=true to bypass (in-VPC private DNS).');
|
|
162
|
+
}
|
|
163
|
+
const portPart = port && port !== 80 && port !== 443 ? `:${port}` : '';
|
|
164
|
+
return { host, portPart };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the CodeBuild image to use for a plugin.
|
|
168
|
+
*
|
|
169
|
+
* Strategy:
|
|
170
|
+
* 1. If the plugin sets `aws:cdk:codebuild:buildenvironment:buildImage`
|
|
171
|
+
* explicitly in metadata, the metadata-builder passthrough handles it
|
|
172
|
+
* (via `metadataForBuildEnvironment`). This function returns
|
|
173
|
+
* `undefined` and the metadata wins.
|
|
174
|
+
* 2. Otherwise, if the plugin has a `name`+`version` AND the registry
|
|
175
|
+
* config is populated, build a `LinuxBuildImage.fromDockerRegistry()`
|
|
176
|
+
* image pointing at `<registry-host>:<port>/<ns>/<name>:<version>`
|
|
177
|
+
* where `<ns>` is `system` or `org-<orgId>`. CodeBuild authenticates by
|
|
178
|
+
* sending the per-org platform Secret as Basic auth to
|
|
179
|
+
* `pipeline-image-registry`'s `/token` endpoint; the JWT in `password`
|
|
180
|
+
* resolves to a registry token scoped to the org.
|
|
181
|
+
* 3. If `metadata_only`, return `undefined` so CodeBuild uses its default
|
|
182
|
+
* (`standard:7.0`).
|
|
183
|
+
*
|
|
184
|
+
* `scope` and `orgId` are required: the per-org platform Secret is named
|
|
185
|
+
* `pipeline-builder/<orgId>/platform`, and `Secret.fromSecretNameV2()`
|
|
186
|
+
* needs a Construct to anchor the imported secret to.
|
|
187
|
+
*/
|
|
188
|
+
export function resolvePluginImage(scope, plugin, orgId) {
|
|
189
|
+
// `metadata_only` plugins legitimately have no image — their work runs
|
|
190
|
+
// in the default CodeBuild image. Quiet skip.
|
|
191
|
+
if (plugin.buildType === 'metadata_only')
|
|
192
|
+
return undefined;
|
|
193
|
+
// build_image / prebuilt plugins must have name + version (DB constraint
|
|
194
|
+
// makes both NOT NULL). Defensive check in case a malformed row slips in.
|
|
195
|
+
if (!plugin.name || !plugin.version) {
|
|
196
|
+
log.warn(`Plugin "${plugin.name}" has buildType=${plugin.buildType} but missing name/version — ` +
|
|
197
|
+
'CodeBuild will run on aws/codebuild/standard:8.0 and won\'t have the plugin\'s baked tools.');
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
let registry;
|
|
201
|
+
try {
|
|
202
|
+
registry = Config.get('registry');
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Config namespace not loaded (e.g., unit tests without full config).
|
|
206
|
+
log.warn(`Plugin "${plugin.name}:${plugin.version}" needs the registry config but it's not loaded — ` +
|
|
207
|
+
'CodeBuild will fall back to aws/codebuild/standard:8.0. ' +
|
|
208
|
+
'Set IMAGE_REGISTRY_HOST + IMAGE_REGISTRY_PORT in pipeline-manager\'s environment.');
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
if (!registry?.host) {
|
|
212
|
+
log.warn(`Plugin "${plugin.name}:${plugin.version}" needs IMAGE_REGISTRY_HOST but it's empty — ` +
|
|
213
|
+
'CodeBuild will fall back to aws/codebuild/standard:8.0. ' +
|
|
214
|
+
'Set IMAGE_REGISTRY_HOST in pipeline-manager\'s environment to use the plugin image.');
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
if (!scope) {
|
|
218
|
+
log.warn(`Plugin "${plugin.name}" image resolution skipped: no construct scope provided`);
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
// Per-org auth requires orgId — the Secret is named per org.
|
|
222
|
+
if (!orgId) {
|
|
223
|
+
log.warn(`Plugin "${plugin.name}" image resolution skipped: orgId is required to ` +
|
|
224
|
+
'resolve the per-org platform Secret used for CodeBuild Basic auth.');
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
// Compose the image URI. Namespace by ownership:
|
|
228
|
+
// - Plugins owned by the system org → `system/<name>:<version>`.
|
|
229
|
+
// pipeline-image-registry's token service grants pull on `system/*`
|
|
230
|
+
// to any authenticated org user (read-only catalog of shared plugins).
|
|
231
|
+
// - Plugins owned by a tenant org → `org-<orgId>/<name>:<version>`.
|
|
232
|
+
// The token service grants pull,push only to members of that org.
|
|
233
|
+
// The plugin's own `orgId` (not the caller's) decides the namespace —
|
|
234
|
+
// tenant pipelines pulling shared system plugins still get the `system/`
|
|
235
|
+
// path because that's where the image actually lives.
|
|
236
|
+
// Use the external pull host/port: this image URI is consumed by AWS
|
|
237
|
+
// CodeBuild (out-of-cluster), which can't resolve the in-cluster
|
|
238
|
+
// `registry:5000` ClusterIP. Fall back to `host`/`port` for single-host /
|
|
239
|
+
// in-cluster-only deploys where no separate pull host is configured.
|
|
240
|
+
const { host: pullHost, portPart } = resolveExternalPullTarget(registry);
|
|
241
|
+
// Must match the PUSH side (api/plugin pluginUri) exactly, or a system
|
|
242
|
+
// plugin's image is pushed to `system/…` but this synth pulls from
|
|
243
|
+
// `org-<objectid>/…` → build-time pull failure. `SYSTEM_ORG_ID` is the
|
|
244
|
+
// well-known ObjectId (NOT the string 'system').
|
|
245
|
+
const namespace = plugin.orgId === SYSTEM_ORG_ID ? 'system' : `org-${plugin.orgId}`;
|
|
246
|
+
const imageUri = `${pullHost}${portPart}/${namespace}/${plugin.name}:${plugin.version}`;
|
|
247
|
+
// CodeBuild reads `pipeline-builder/<orgId>/platform` and sends its
|
|
248
|
+
// `username`/`password` fields as HTTP Basic to the registry. The
|
|
249
|
+
// registry challenges with a Bearer realm pointing at
|
|
250
|
+
// pipeline-image-registry's `/token` endpoint; the Docker client forwards
|
|
251
|
+
// those creds, the password is verified as a platform JWT, and a registry
|
|
252
|
+
// token scoped to the org is issued.
|
|
253
|
+
//
|
|
254
|
+
// Same Secret is read by the plugin-lookup Lambda (via the `password`
|
|
255
|
+
// field) — one Secret serves both flows.
|
|
256
|
+
const stack = Stack.of(scope);
|
|
257
|
+
const secretName = CoreConstants.secretPath(orgId, 'platform');
|
|
258
|
+
// CDK construct IDs allow only [A-Za-z0-9_-]; sanitize the secret name.
|
|
259
|
+
const secretConstructId = `PlatformCreds_${secretName.replace(/[^A-Za-z0-9_-]/g, '_')}`;
|
|
260
|
+
const credentialsSecret = stack.node.tryFindChild(secretConstructId)
|
|
261
|
+
?? Secret.fromSecretNameV2(stack, secretConstructId, secretName);
|
|
262
|
+
return LinuxBuildImage.fromDockerRegistry(imageUri, {
|
|
263
|
+
secretsManagerCredentials: credentialsSecret,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Resolve the fallback CodeBuild image for steps without a plugin-baked one.
|
|
268
|
+
*
|
|
269
|
+
* Reads `aws.codeBuild.defaultImage` (env: `CODEBUILD_DEFAULT_IMAGE`).
|
|
270
|
+
* Default: `pipeline-bootstrap:1.0` — the local tag built by
|
|
271
|
+
* `deploy/codebuild/bootstrap/Dockerfile`.
|
|
272
|
+
*
|
|
273
|
+
* - AWS curated image (`aws/codebuild/*`): use the matching `LinuxBuildImage`
|
|
274
|
+
* constant so CDK emits `imagePullCredentialsType: CODEBUILD`. Using
|
|
275
|
+
* `fromDockerRegistry()` here would set SERVICE_ROLE credentials, which
|
|
276
|
+
* CFN rejects with "cannot use a CodeBuild curated image with
|
|
277
|
+
* imagePullCredentialsType SERVICE_ROLE".
|
|
278
|
+
* - Bare tag (no `/`): auto-prefixed to
|
|
279
|
+
* `<registry-host>:<port>/library/<tag>` using the registry config,
|
|
280
|
+
* with the per-org platform Secret as Basic auth — same path
|
|
281
|
+
* `resolvePluginImage()` uses for plugin images. Needs `scope` + `orgId`.
|
|
282
|
+
* - Fully-qualified registry URI (contains `/`): used as-is, no auth wired.
|
|
283
|
+
* - Missing registry config or scope/orgId → falls back to
|
|
284
|
+
* `aws/codebuild/standard:8.0` (as a CURATED image, see above), with a
|
|
285
|
+
* warning so synth degrades gracefully on partially configured envs.
|
|
286
|
+
*/
|
|
287
|
+
const STANDARD_8_0 = 'aws/codebuild/standard:8.0';
|
|
288
|
+
/**
|
|
289
|
+
* aws-cdk-lib 2.258 has no `LinuxBuildImage.STANDARD_8_0` static yet, so the
|
|
290
|
+
* curated 8.0 fallback resolves via `fromCodeBuildImageId` (still CODEBUILD creds).
|
|
291
|
+
*/
|
|
292
|
+
const fallbackBuildImage = () => LinuxBuildImage.fromCodeBuildImageId(STANDARD_8_0);
|
|
293
|
+
/**
|
|
294
|
+
* Map AWS curated `aws/codebuild/*` image strings to the matching
|
|
295
|
+
* `LinuxBuildImage` static. Extend when newer standard images ship.
|
|
296
|
+
* Returns `undefined` for non-curated strings — caller should use
|
|
297
|
+
* `fromDockerRegistry()` for those.
|
|
298
|
+
*/
|
|
299
|
+
function curatedLinuxBuildImage(imageId) {
|
|
300
|
+
if (!imageId.startsWith('aws/codebuild/'))
|
|
301
|
+
return undefined;
|
|
302
|
+
switch (imageId) {
|
|
303
|
+
case 'aws/codebuild/standard:6.0': return LinuxBuildImage.STANDARD_6_0;
|
|
304
|
+
case 'aws/codebuild/standard:5.0': return LinuxBuildImage.STANDARD_5_0;
|
|
305
|
+
// Newer codenamed images route through fromCodeBuildImageId, which
|
|
306
|
+
// CDK also marks as CODEBUILD credentials (curated).
|
|
307
|
+
default: return LinuxBuildImage.fromCodeBuildImageId(imageId);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
export function resolveDefaultBuildImage(scope, orgId) {
|
|
311
|
+
let configured;
|
|
312
|
+
try {
|
|
313
|
+
configured = Config.get('aws').codeBuild.defaultImage;
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
configured = STANDARD_8_0;
|
|
317
|
+
}
|
|
318
|
+
// AWS curated image → CODEBUILD credentials. MUST go through the
|
|
319
|
+
// LinuxBuildImage constants, not fromDockerRegistry().
|
|
320
|
+
const curated = curatedLinuxBuildImage(configured);
|
|
321
|
+
if (curated)
|
|
322
|
+
return curated;
|
|
323
|
+
// Fully-qualified URI — operator owns it. No registry/Secret wiring.
|
|
324
|
+
if (configured.includes('/')) {
|
|
325
|
+
return LinuxBuildImage.fromDockerRegistry(configured);
|
|
326
|
+
}
|
|
327
|
+
// Bare tag → auto-prefix with the platform registry, mirroring
|
|
328
|
+
// resolvePluginImage(). Bail to the curated standard:8.0 fallback
|
|
329
|
+
// (with a warning) at each missing prerequisite so synth degrades
|
|
330
|
+
// gracefully on partially configured environments instead of failing.
|
|
331
|
+
let registry;
|
|
332
|
+
try {
|
|
333
|
+
registry = Config.get('registry');
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
log.warn(`CODEBUILD_DEFAULT_IMAGE='${configured}' is a bare tag but registry config not loaded — ` +
|
|
337
|
+
`falling back to ${STANDARD_8_0}. Set IMAGE_REGISTRY_HOST + IMAGE_REGISTRY_PORT.`);
|
|
338
|
+
return fallbackBuildImage();
|
|
339
|
+
}
|
|
340
|
+
if (!registry?.host) {
|
|
341
|
+
log.warn(`CODEBUILD_DEFAULT_IMAGE='${configured}' is a bare tag but IMAGE_REGISTRY_HOST is empty — ` +
|
|
342
|
+
`falling back to ${STANDARD_8_0}.`);
|
|
343
|
+
return fallbackBuildImage();
|
|
344
|
+
}
|
|
345
|
+
if (!scope || !orgId) {
|
|
346
|
+
log.warn(`CODEBUILD_DEFAULT_IMAGE='${configured}' needs scope+orgId for Secret auth — ` +
|
|
347
|
+
`falling back to ${STANDARD_8_0}. (Caller did not supply them.)`);
|
|
348
|
+
return fallbackBuildImage();
|
|
349
|
+
}
|
|
350
|
+
// Use the external pull host/port (not the in-cluster host) — this URI is
|
|
351
|
+
// consumed by AWS CodeBuild, which can't resolve the `registry:5000` ClusterIP.
|
|
352
|
+
const { host: pullHost, portPart } = resolveExternalPullTarget(registry);
|
|
353
|
+
const imageUri = `${pullHost}${portPart}/library/${configured}`;
|
|
354
|
+
// Same Secret as resolvePluginImage — per-org platform Secret, CodeBuild
|
|
355
|
+
// sends it as Basic auth, image-registry verifies the JWT in `password`.
|
|
356
|
+
const stack = Stack.of(scope);
|
|
357
|
+
const secretName = CoreConstants.secretPath(orgId, 'platform');
|
|
358
|
+
const secretConstructId = `PlatformCreds_${secretName.replace(/[^A-Za-z0-9_-]/g, '_')}`;
|
|
359
|
+
const credentialsSecret = stack.node.tryFindChild(secretConstructId)
|
|
360
|
+
?? Secret.fromSecretNameV2(stack, secretConstructId, secretName);
|
|
361
|
+
return LinuxBuildImage.fromDockerRegistry(imageUri, {
|
|
362
|
+
secretsManagerCredentials: credentialsSecret,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Create a CodeBuild step or Shell step based on plugin configuration.
|
|
367
|
+
*
|
|
368
|
+
* Metadata merge order (last wins):
|
|
369
|
+
* 1. Plugin metadata (from plugin.metadata in database) — the plugin
|
|
370
|
+
* author's baked-in defaults
|
|
371
|
+
* 2. Step-level metadata (from options.metadata) — the pipeline author's
|
|
372
|
+
* per-step overrides, which are more specific and must win
|
|
373
|
+
*
|
|
374
|
+
* Environment merge order (last wins):
|
|
375
|
+
* 1. Plugin env vars (from plugin.env)
|
|
376
|
+
* 2. Custom env vars (from options.env)
|
|
377
|
+
* 3. WORKDIR from merged metadata
|
|
378
|
+
*
|
|
379
|
+
* CDK prop spread order (last wins):
|
|
380
|
+
* programmatic defaults (input, commands, env, network) → metadata overrides
|
|
381
|
+
*
|
|
382
|
+
* This means metadata keys like `aws:cdk:pipelines:codebuildstep:commands`
|
|
383
|
+
* will override the plugin-derived commands when explicitly set.
|
|
384
|
+
*/
|
|
385
|
+
export function createCodeBuildStep(options) {
|
|
386
|
+
const { id, input, metadata, network, scope, preInstallCommands, postInstallCommands, preCommands, postCommands, env: customEnv, additionalInputs, timeout, failureBehavior, artifactManager, stageName, stageAlias, pluginAlias, orgId, pipelineScope, } = options;
|
|
387
|
+
// Resolve {{ ... }} templates in plugin spec fields against the pipeline scope.
|
|
388
|
+
// Every call goes through the resolver — plugins without template tokens are
|
|
389
|
+
// a no-op fast path inside resolvePluginTemplates().
|
|
390
|
+
const plugin = resolvePluginTemplates(options.plugin, pipelineScope);
|
|
391
|
+
// Per-step metadata (the pipeline author's overrides, e.g. JAVA_VERSION=25)
|
|
392
|
+
// must win over the plugin's catalog `metadata` defaults, so it goes LAST.
|
|
393
|
+
// Reversing these silently dropped per-step overrides for any plugin that
|
|
394
|
+
// ships non-empty catalog metadata (plugins that carry defaults under `env`
|
|
395
|
+
// instead — like java-corretto — were unaffected, which masked this).
|
|
396
|
+
const merged = merge(plugin.metadata ?? {}, metadata ?? {});
|
|
397
|
+
// ManualApprovalStep: no commands, env, compute, or network — just id + optional comment
|
|
398
|
+
if (plugin.pluginType === PluginType.MANUAL_APPROVAL_STEP) {
|
|
399
|
+
return new ManualApprovalStep(id, {
|
|
400
|
+
comment: typeof merged.APPROVAL_COMMENT === 'string' ? merged.APPROVAL_COMMENT : undefined,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
log.debug('[CreateCodeBuildStep] Building step with merged metadata');
|
|
404
|
+
// Warn about required secrets without orgId (can't resolve)
|
|
405
|
+
const requiredSecrets = plugin.secrets?.filter(s => s.required) ?? [];
|
|
406
|
+
if (requiredSecrets.length > 0 && !orgId) {
|
|
407
|
+
log.warn(`Plugin "${plugin.name}" declares ${requiredSecrets.length} required secret(s) but no orgId is available. ` +
|
|
408
|
+
`Secrets will not be injected: ${requiredSecrets.map(s => s.name).join(', ')}`);
|
|
409
|
+
}
|
|
410
|
+
// Resolve plugin secrets as SECRETS_MANAGER env vars — only REQUIRED ones.
|
|
411
|
+
// CodeBuild treats a SECRETS_MANAGER env var as MANDATORY: it fails the build
|
|
412
|
+
// at DOWNLOAD_SOURCE ("ResourceNotFoundException: can't find the specified
|
|
413
|
+
// secret") if the secret is absent. Auto-wiring an OPTIONAL secret
|
|
414
|
+
// (`required: false`, e.g. NVD_API_KEY / SEMGREP_APP_TOKEN) the operator hasn't
|
|
415
|
+
// created would therefore break every build — the exact opposite of "optional".
|
|
416
|
+
// Optional secrets are left for the plugin to read only where the operator has
|
|
417
|
+
// provisioned them (out-of-band env/secret), not force-injected here.
|
|
418
|
+
const secretEnvVars = (requiredSecrets.length && orgId)
|
|
419
|
+
? toSecretEnvVars(requiredSecrets, orgId)
|
|
420
|
+
: {};
|
|
421
|
+
const env = buildEnv(plugin, merged, customEnv);
|
|
422
|
+
// CodePipeline resolved variables — must be in CodeBuildStep.env (action-level),
|
|
423
|
+
// not buildEnvironment.environmentVariables (project-level)
|
|
424
|
+
const actionEnv = {
|
|
425
|
+
PIPELINE_EXECUTION_ID: '#{codepipeline.PipelineExecutionId}',
|
|
426
|
+
};
|
|
427
|
+
const outputDir = plugin.primaryOutputDirectory;
|
|
428
|
+
const ensureOutputDir = (outputDir && !outputDir.includes('*'))
|
|
429
|
+
? [`mkdir -p "${outputDir}" && touch "${outputDir}/.gitkeep"`]
|
|
430
|
+
: [];
|
|
431
|
+
const { installCommands, commands } = buildCommands(plugin, {
|
|
432
|
+
preInstallCommands: [...ensureOutputDir, ...(preInstallCommands ?? [])],
|
|
433
|
+
postInstallCommands,
|
|
434
|
+
preCommands,
|
|
435
|
+
postCommands,
|
|
436
|
+
}, failureBehavior);
|
|
437
|
+
const programmatic = { input, installCommands, commands };
|
|
438
|
+
// Resolve the plugin's runtime image. CodeBuild defaults to
|
|
439
|
+
// `aws/codebuild/standard:7.0` when no `buildImage` is set — that's the
|
|
440
|
+
// AWS-managed Ubuntu image and DOES NOT have any plugin-baked tools
|
|
441
|
+
// (pipeline-manager, snyk, terraform, etc.). Wiring the plugin's image
|
|
442
|
+
// here means CodeBuild pulls from our private registry and the tools
|
|
443
|
+
// installed in the plugin's Dockerfile are actually available.
|
|
444
|
+
const pluginBuildImage = resolvePluginImage(scope, plugin, orgId);
|
|
445
|
+
// ShellStep branch.
|
|
446
|
+
// ShellStep itself doesn't accept `buildEnvironment`/`buildImage`. CDK
|
|
447
|
+
// pipelines wraps ShellSteps in a default CodeBuild action that runs on
|
|
448
|
+
// `aws/codebuild/standard:7.0` — which won't have the plugin's tools.
|
|
449
|
+
//
|
|
450
|
+
// So when a SHELL_STEP plugin DOES have an image (because its author
|
|
451
|
+
// baked tools into a Dockerfile), we PROMOTE it to a CodeBuildStep with
|
|
452
|
+
// the resolved image. The plugin author's intent ("use my baked tools")
|
|
453
|
+
// is preserved without forcing them to change pluginType.
|
|
454
|
+
//
|
|
455
|
+
// When the plugin has no image (or registry isn't configured), we keep
|
|
456
|
+
// the original ShellStep — it's lighter weight and the default
|
|
457
|
+
// CodeBuild image is fine.
|
|
458
|
+
if (plugin.pluginType === PluginType.SHELL_STEP) {
|
|
459
|
+
if (!pluginBuildImage) {
|
|
460
|
+
return new ShellStep(id, {
|
|
461
|
+
...programmatic,
|
|
462
|
+
env: { ...env, ...actionEnv },
|
|
463
|
+
...metadataForShellStep(merged),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
log.debug(`[CreateCodeBuildStep] SHELL_STEP plugin "${plugin.name}" has a registry image — promoting to CodeBuildStep so the plugin image is actually used`);
|
|
467
|
+
// Fall through to the CodeBuildStep path below.
|
|
468
|
+
}
|
|
469
|
+
const computeType = getComputeType(plugin.computeType ?? options.defaultComputeType ?? 'SMALL');
|
|
470
|
+
// Step network: explicit step config wins, else this step's `ec2:network` metadata.
|
|
471
|
+
const resolvedNetwork = network ?? networkConfigFromMetadata(merged);
|
|
472
|
+
const networkProps = resolvedNetwork
|
|
473
|
+
? resolveNetwork(scope, options.uniqueId, resolvedNetwork)
|
|
474
|
+
: {};
|
|
475
|
+
// Metadata spread last so it can override programmatic defaults.
|
|
476
|
+
// NOTE: Caching is supported via two metadata paths:
|
|
477
|
+
// 1. MetadataKeys.CACHE ('aws:cdk:pipelines:codebuildstep:cache') — passed directly
|
|
478
|
+
// as the CodeBuildStep `cache` prop (expects a codebuild.Cache object).
|
|
479
|
+
// 2. MetadataKeys.PARTIAL_BUILD_SPEC ('aws:cdk:pipelines:codebuildstep:partialbuildspec')
|
|
480
|
+
// — passed as `partialBuildSpec`, which can include a `cache:` section for
|
|
481
|
+
// S3 or local caching (e.g., BuildSpec.fromObject({ cache: { paths: [...] } })).
|
|
482
|
+
// Per-step buildEnvironment overrides from metadata (aws:cdk:codebuild:buildenvironment:*).
|
|
483
|
+
// A metadata-provided computeType is a raw passthrough, so normalize it through
|
|
484
|
+
// getComputeType — otherwise a friendly value like "LARGE" reaches CodeBuild verbatim
|
|
485
|
+
// and fails with "Invalid compute type provided" (it needs BUILD_GENERAL1_LARGE).
|
|
486
|
+
const rawMetaBuildEnv = metadataForBuildEnvironment(merged);
|
|
487
|
+
const metaBuildEnv = typeof rawMetaBuildEnv.computeType === 'string'
|
|
488
|
+
? { ...rawMetaBuildEnv, computeType: getComputeType(rawMetaBuildEnv.computeType) }
|
|
489
|
+
: rawMetaBuildEnv;
|
|
490
|
+
const step = new CodeBuildStep(id, {
|
|
491
|
+
...programmatic,
|
|
492
|
+
...networkProps,
|
|
493
|
+
...(additionalInputs && { additionalInputs }),
|
|
494
|
+
...(timeout && { timeout: Duration.minutes(timeout) }),
|
|
495
|
+
env: actionEnv,
|
|
496
|
+
primaryOutputDirectory: plugin.primaryOutputDirectory ?? undefined,
|
|
497
|
+
// Pin the buildspec shell to POSIX /bin/sh so execution is identical to
|
|
498
|
+
// CodeBuild's default and portable across minimal base images (no bash
|
|
499
|
+
// dependency). Plugin specs are authored POSIX-clean to match — no bash-only
|
|
500
|
+
// `[[ ]]`, arrays, `read -ra <<<`, or `source`/`nvm use`/`sdk use`. A
|
|
501
|
+
// plugin-supplied partialBuildSpec (none today) would override this via the
|
|
502
|
+
// metadata spread below and must then set `env.shell` itself.
|
|
503
|
+
partialBuildSpec: BuildSpec.fromObject({ env: { shell: '/bin/sh' } }),
|
|
504
|
+
buildEnvironment: {
|
|
505
|
+
computeType,
|
|
506
|
+
buildImage: pluginBuildImage ?? resolveDefaultBuildImage(scope, orgId),
|
|
507
|
+
environmentVariables: {
|
|
508
|
+
...toCodeBuildEnvVars(env),
|
|
509
|
+
...secretEnvVars,
|
|
510
|
+
},
|
|
511
|
+
...metaBuildEnv,
|
|
512
|
+
},
|
|
513
|
+
...metadataForCodeBuildStep(merged),
|
|
514
|
+
});
|
|
515
|
+
// Register with artifact manager if primaryOutputDirectory is set
|
|
516
|
+
if (plugin.primaryOutputDirectory && artifactManager && stageName) {
|
|
517
|
+
const artifactKey = {
|
|
518
|
+
stageName,
|
|
519
|
+
stageAlias: stageAlias ?? `${stageName}-alias`,
|
|
520
|
+
pluginName: plugin.name,
|
|
521
|
+
pluginAlias: pluginAlias ?? `${plugin.name}-alias`,
|
|
522
|
+
outputDirectory: plugin.primaryOutputDirectory,
|
|
523
|
+
};
|
|
524
|
+
artifactManager.add(artifactKey, step);
|
|
525
|
+
}
|
|
526
|
+
return step;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Convert string or ComputeType enum to CDK ComputeType
|
|
530
|
+
*/
|
|
531
|
+
export function getComputeType(input = 'SMALL') {
|
|
532
|
+
// If already a CDK ComputeType, return as-is
|
|
533
|
+
if (typeof input !== 'string') {
|
|
534
|
+
return input;
|
|
535
|
+
}
|
|
536
|
+
// Already a native CodeBuild ComputeType (e.g. "BUILD_GENERAL1_LARGE")? Accept as-is
|
|
537
|
+
// so both friendly (LARGE) and native values round-trip without falling back to SMALL.
|
|
538
|
+
if (Object.values(CDKComputeType).includes(input)) {
|
|
539
|
+
return input;
|
|
540
|
+
}
|
|
541
|
+
const normalized = input.toUpperCase();
|
|
542
|
+
const mapping = {
|
|
543
|
+
[ComputeType.SMALL]: CDKComputeType.SMALL,
|
|
544
|
+
[ComputeType.MEDIUM]: CDKComputeType.MEDIUM,
|
|
545
|
+
[ComputeType.LARGE]: CDKComputeType.LARGE,
|
|
546
|
+
[ComputeType.X2_LARGE]: CDKComputeType.X2_LARGE,
|
|
547
|
+
};
|
|
548
|
+
const result = mapping[normalized];
|
|
549
|
+
if (!result) {
|
|
550
|
+
log.warn(`Unknown compute type "${input}", falling back to SMALL`);
|
|
551
|
+
return CDKComputeType.SMALL;
|
|
552
|
+
}
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Unwrap a SecretValue | string into a plain string.
|
|
557
|
+
* When a SecretValue is provided (e.g. from Secrets Manager), calls unsafeUnwrap()
|
|
558
|
+
* to extract the underlying value.
|
|
559
|
+
*/
|
|
560
|
+
export function unwrapSecret(value) {
|
|
561
|
+
return typeof value === 'string' ? value : value.unsafeUnwrap();
|
|
562
|
+
}
|
|
563
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGlwZWxpbmUtaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb3JlL3BpcGVsaW5lLWhlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXpFLE9BQU8sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzRCxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsU0FBUyxFQUFFLFdBQVcsSUFBSSxjQUFjLEVBQUUsZUFBZSxFQUFvQixNQUFNLDJCQUEyQixDQUFDO0FBQ3RKLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN4RCxPQUFPLEVBQUUsYUFBYSxFQUFFLGtCQUFrQixFQUFFLFNBQVMsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBR3JGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSx3QkFBd0IsRUFBRSwyQkFBMkIsRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9JLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxLQUFLLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzlDLE9BQU8sRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFxQixNQUFNLHFCQUFxQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFaEUsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFeEUsTUFBTSxHQUFHLEdBQUcsWUFBWSxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUFFN0M7Ozs7Ozs7R0FPRztBQUNILE1BQU0sYUFBYSxHQUFHLDhDQUE4QyxDQUFDO0FBRXJFLFNBQVMs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QyxLQUFLLENBQUMsRUFBRSxjQUFjLENBQUMsS0FBSztRQUN6QyxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxjQUFjLENBQUMsUUFBUTtLQUNoRCxDQUFDO0lBRUYsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ25DLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLEdBQUcsQ0FBQyxJQUFJLENBQUMseUJBQXlCLEtBQUssMEJBQTBCLENBQUMsQ0FBQztRQUNuRSxPQUFPLGNBQWMsQ0FBQyxLQUFLLENBQUM7SUFDOUIsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FBQyxLQUEyQjtJQUN0RCxPQUFPLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUM7QUFDbEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIsIFNZU1RFTV9PUkdfSUQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLWRhdGEnO1xuaW1wb3J0IHsgRHVyYXRpb24sIFNlY3JldFZhbHVlLCBTdGFjayB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IEJ1aWxkRW52aXJvbm1lbnRWYXJpYWJsZVR5cGUsIEJ1aWxkU3BlYywgQ29tcHV0ZVR5cGUgYXMgQ0RLQ29tcHV0ZVR5cGUsIExpbnV4QnVpbGRJbWFnZSwgdHlwZSBJQnVpbGRJbWFnZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jb2RlYnVpbGQnO1xuaW1wb3J0IHsgU2VjcmV0IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXNlY3JldHNtYW5hZ2VyJztcbmltcG9ydCB7IENvZGVCdWlsZFN0ZXAsIE1hbnVhbEFwcHJvdmFsU3RlcCwgU2hlbGxTdGVwIH0gZnJvbSAnYXdzLWNkay1saWIvcGlwZWxpbmVzJztcbmltcG9ydCB0eXBlIHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgdHlwZSB7IEFydGlmYWN0S2V5IH0gZnJvbSAnLi9hcnRpZmFjdC1tYW5hZ2VyLmpzJztcbmltcG9ydCB7IG1ldGFkYXRhRm9yU2hlbGxTdGVwLCBtZXRhZGF0YUZvckNvZGVCdWlsZFN0ZXAsIG1ldGFkYXRhRm9yQnVpbGRFbnZpcm9ubWVudCwgbmV0d29ya0NvbmZpZ0Zyb21NZXRhZGF0YSB9IGZyb20gJy4vbWV0YWRhdGEtYnVpbGRlci5qcyc7XG5pbXBvcnQgeyBleHRyYWN0TWV0YWRhdGFFbnYsIG1lcmdlIH0gZnJvbSAnLi9tZXRhZGF0YS1oZWxwZXJzLmpzJztcbmltcG9ydCB7IHJlc29sdmVOZXR3b3JrIH0gZnJvbSAnLi9uZXR3b3JrLmpzJztcbmltcG9ydCB7IFBsdWdpblR5cGUsIENvbXB1dGVUeXBlLCB0eXBlIE1ldGFEYXRhVHlwZSB9IGZyb20gJy4vcGlwZWxpbmUtdHlwZXMuanMnO1xuaW1wb3J0IHsgQ29uZmlnLCBDb3JlQ29uc3RhbnRzIH0gZnJvbSAnLi4vY29uZmlnL2FwcC1jb25maWcuanMnO1xuaW1wb3J0IHR5cGUgeyBDb2RlQnVpbGRTdGVwT3B0aW9ucywgU3RlcEN1c3RvbWl6YXRpb24gfSBmcm9tICcuLi9waXBlbGluZS9zdGVwLXR5cGVzLmpzJztcbmltcG9ydCB7IHJlc29sdmVQbHVnaW5UZW1wbGF0ZXMgfSBmcm9tICcuLi90ZW1wbGF0ZS9wbHVnaW4tcmVzb2x2ZXIuanMnO1xuXG5jb25zdCBsb2cgPSBjcmVhdGVMb2dnZXIoJ3BpcGVsaW5lLWhlbHBlcnMnKTtcblxuLyoqXG4gKiBCdWlsZCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZnJvbSBwbHVnaW4gY29uZmlnLCBtZXJnZWQgbWV0YWRhdGEsIGFuZCBjdXN0b20gZW52LlxuICpcbiAqIE1lcmdlIG9yZGVyIChsYXN0IHdpbnMpOlxuICogICAxLiBwbHVnaW4uZW52IOKAlCBwbHVnaW4gZGVmYXVsdCBlbnYgdmFycyAobG93ZXN0IHByaW9yaXR5KVxuICogICAyLiBub24tbmFtZXNwYWNlZCBtZXRhZGF0YSBrZXlzIOKAlCBlLmcuIFBZVEhPTl9WRVJTSU9OLCBXT1JLRElSXG4gKiAgIDMuIGN1c3RvbUVudiDigJQgcGVyLXN0ZXAgY3VzdG9tIGVudiB2YXJzIChoaWdoZXN0IHByaW9yaXR5KVxuICovXG5jb25zdCBCT09UU1RSQVBfQ01EID0gJ2V4cG9ydCBXT1JLRElSPSR7V09SS0RJUjotLi99OyBjZCAke1dPUktESVJ9JztcblxuZnVuY3Rpb24gYnVpbGRFbnYocGx1Z2luOiBQbHVnaW4sIG1ldGFkYXRhOiBNZXRhRGF0YVR5cGUsIGN1c3RvbUVudj86IFJlY29yZDxzdHJpbmcsIHN0cmluZz4pOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcbiAgcmV0dXJuIHtcbiAgICAuLi4ocGx1Z2luLmVudiA/PyB7fSksXG4gICAgLi4uZXh0cmFjdE1ldGFkYXRhRW52KG1ldGFkYXRhKSxcbiAgICAuLi4oY3VzdG9tRW52ID8/IHt9KSxcbiAgfTtcbn1cblxuLyoqXG4gKiBXcmFwIGJ1aWxkIGNvbW1hbmRzIGJhc2VkIG9uIGZhaWx1cmUgYmVoYXZpb3IuXG4gKiAtICdmYWlsJyAoZGVmYXVsdCk6IE5vIHdyYXBwaW5nIOKAlCBjb21tYW5kcyBmYWlsIHRoZSBwaXBlbGluZSBuYXR1cmFsbHkuXG4gKiAtICd3YXJuJzogUnVuIGNvbW1hbmRzIHdpdGggYHNldCArZWAsIGNhcHR1cmUgZmFpbHVyZXMsIGxvZyB3YXJuaW5ncywgY29udGludWUuXG4gKiAtICdpZ25vcmUnOiBBcHBlbmQgYHx8IHRydWVgIHRvIGVhY2ggY29tbWFuZCDigJQgZmFpbHVyZXMgYXJlIHNpbGVudGx5IHN3YWxsb3dlZC5cbiAqXG4gKiBPbmx5IGFwcGxpZWQgdG8gYnVpbGQgY29tbWFuZHMsIG5vdCBpbnN0YWxsIGNvbW1hbmRzIChpbnN0YWxsIGZhaWx1cmVzIHNob3VsZCBhbHdheXMgc3RvcCB0aGUgYnVpbGQpLlxuICovXG5mdW5jdGlvbiB3cmFwQ29tbWFuZHNGb3JGYWlsdXJlQmVoYXZpb3IoY29tbWFuZHM6IHN0cmluZ1tdLCBiZWhhdmlvcj86ICdmYWlsJyB8ICd3YXJuJyB8ICdpZ25vcmUnKTogc3RyaW5nW10ge1xuICBpZiAoIWJlaGF2aW9yIHx8IGJlaGF2aW9yID09PSAnZmFpbCcpIHJldHVybiBjb21tYW5kcztcblxuICBpZiAoYmVoYXZpb3IgPT09ICdpZ25vcmUnKSB7XG4gICAgcmV0dXJuIGNvbW1hbmRzLm1hcChjbWQgPT4gYCR7Y21kfSB8fCB0cnVlYCk7XG4gIH1cblxuICAvLyAnd2Fybic6IHJ1biBhbGwgY29tbWFuZHMsIGNhcHR1cmUgZmFpbHVyZXMsIGJ1dCBkb24ndCBzdG9wXG4gIHJldHVybiBbXG4gICAgJ3NldCArZScsXG4gICAgJ19TVEVQX0VYSVQ9MCcsXG4gICAgLi4uY29tbWFuZHMubWFwKGNtZCA9PiBgJHtjbWR9IHx8IHsgZWNobyBcIldBUk5JTkc6IENvbW1hbmQgZmFpbGVkIHdpdGggZXhpdCBjb2RlICQ/XCI7IF9TVEVQX0VYSVQ9MTsgfWApLFxuICAgICdzZXQgLWUnLFxuICAgICdpZiBbIFwiJF9TVEVQX0VYSVRcIiAtbmUgMCBdOyB0aGVuIGVjaG8gXCJXQVJOSU5HOiBPbmUgb3IgbW9yZSBjb21tYW5kcyBpbiB0aGlzIHN0ZXAgZmFpbGVkXCI7IGZpJyxcbiAgXTtcbn1cblxuLyoqXG4gKiBCdWlsZCBib290c3RyYXAtcHJlZml4ZWQgaW5zdGFsbCBhbmQgYnVpbGQgY29tbWFuZHMgZnJvbSBwbHVnaW4gY29uZmlnLlxuICogRWFjaCBjb21tYW5kIGxpc3QgaXMgcHJlcGVuZGVkIHdpdGggYSBXT1JLRElSIGJvb3RzdHJhcCB0aGF0IGRlZmF1bHRzIHRvICcuLycuXG4gKiBXaGVuIGN1c3RvbSBjb21tYW5kcyBhcmUgcHJvdmlkZWQsIHRoZXkgYXJlIGluamVjdGVkIGJlZm9yZS9hZnRlciB0aGUgcGx1Z2luJ3MgY29tbWFuZHMuXG4gKiBCdWlsZCBjb21tYW5kcyBhcmUgb3B0aW9uYWxseSB3cmFwcGVkIGJ5IGZhaWx1cmVCZWhhdmlvciBsb2dpYy5cbiAqL1xuZnVuY3Rpb24gYnVpbGRDb21tYW5kcyhwbHVnaW46IFBsdWdpbiwgY3VzdG9tPzogU3RlcEN1c3RvbWl6YXRpb24sIGZhaWx1cmVCZWhhdmlvcj86ICdmYWlsJyB8ICd3YXJuJyB8ICdpZ25vcmUnKTogeyBpbnN0YWxsQ29tbWFuZHM6IHN0cmluZ1tdOyBjb21tYW5kczogc3RyaW5nW10gfSB7XG4gIGNvbnN0IHVzZXJDb21tYW5kcyA9IFtcbiAgICAuLi4oY3VzdG9tPy5wcmVDb21tYW5kcyA/PyBbXSksXG4gICAgLi4uKHBsdWdpbi5jb21tYW5kcz8ubGVuZ3RoID8gcGx1Z2luLmNvbW1hbmRzIDogW10pLFxuICAgIC4uLihjdXN0b20/LnBvc3RDb21tYW5kcyA/PyBbXSksXG4gIF07XG5cbiAgcmV0dXJuIHtcbiAgICBpbnN0YWxsQ29tbWFuZHM6IFtcbiAgICAgIEJPT1RTVFJBUF9DTUQsXG4gICAgICAuLi4oY3VzdG9tPy5wcmVJbnN0YWxsQ29tbWFuZHMgPz8gW10pLFxuICAgICAgLi4uKHBsdWdpbi5pbnN0YWxsQ29tbWFuZHMgPz8gW10pLFxuICAgICAgLi4uKGN1c3RvbT8ucG9zdEluc3RhbGxDb21tYW5kcyA/PyBbXSksXG4gICAgXSxcbiAgICBjb21tYW5kczogW0JPT1RTVFJBUF9DTUQsIC4uLndyYXBDb21tYW5kc0ZvckZhaWx1cmVCZWhhdmlvcih1c2VyQ29tbWFuZHMsIGZhaWx1cmVCZWhhdmlvcildLFxuICB9O1xufVxuXG4vKipcbiAqIENvbnZlcnQgYSBwbGFpbiBlbnYgcmVjb3JkIHRvIENvZGVCdWlsZCdzIGVudmlyb25tZW50VmFyaWFibGVzIGZvcm1hdCAoUExBSU5URVhUKS5cbiAqL1xuZnVuY3Rpb24gdG9Db2RlQnVpbGRFbnZWYXJzKGVudjogUmVjb3JkPHN0cmluZywgc3RyaW5nPik6IFJlY29yZDxzdHJpbmcsIHsgdmFsdWU6IHN0cmluZyB9PiB7XG4gIHJldHVybiBPYmplY3QuZnJvbUVudHJpZXMoXG4gICAgT2JqZWN0LmVudHJpZXMoZW52KS5tYXAoKFtuYW1lLCB2YWx1ZV0pID0+IFtuYW1lLCB7IHZhbHVlIH1dKSxcbiAgKTtcbn1cblxuLyoqXG4gKiBCdWlsZCBTRUNSRVRTX01BTkFHRVItdHlwZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZnJvbSBwbHVnaW4gc2VjcmV0IGRlY2xhcmF0aW9ucy5cbiAqIFVzZXMgbmFtaW5nIGNvbnZlbnRpb246IHBpcGVsaW5lLWJ1aWxkZXIve29yZ0lkfS97c2VjcmV0TmFtZX1cbiAqIEVhY2ggb3JnIG1hbmFnZXMgdGhlc2Ugc2VjcmV0cyBpbiB0aGVpciBvd24gQVdTIFNlY3JldHMgTWFuYWdlci5cbiAqL1xuY29uc3QgVkFMSURfU0VDUkVUX05BTUUgPSAvXlthLXpBLVowLTkvXys9LkAtXSskLztcblxuZnVuY3Rpb24gdG9TZWNyZXRFbnZWYXJzKFxuICBzZWNyZXRzOiBBcnJheTx7IG5hbWU6IHN0cmluZzsgcmVxdWlyZWQ6IGJvb2xlYW4gfT4sXG4gIG9yZ0lkOiBzdHJpbmcsXG4pOiBSZWNvcmQ8c3RyaW5nLCB7IHZhbHVlOiBzdHJpbmc7IHR5cGU6IEJ1aWxkRW52aXJvbm1lbnRWYXJpYWJsZVR5cGUgfT4ge1xuICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgIHNlY3JldHMubWFwKCh7IG5hbWUgfSkgPT4ge1xuICAgICAgY29uc3Qgc2VjcmV0UGF0aCA9IENvcmVDb25zdGFudHMuc2VjcmV0UGF0aChvcmdJZCwgbmFtZSk7XG4gICAgICBpZiAoIVZBTElEX1NFQ1JFVF9OQU1FLnRlc3Qoc2VjcmV0UGF0aCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBTZWNyZXQgcGF0aCBcIiR7c2VjcmV0UGF0aH1cIiBjb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnMgZm9yIEFXUyBTZWNyZXRzIE1hbmFnZXJgKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBbXG4gICAgICAgIG5hbWUsXG4gICAgICAgIHtcbiAgICAgICAgICB2YWx1ZTogc2VjcmV0UGF0aCxcbiAgICAgICAgICB0eXBlOiBCdWlsZEVudmlyb25tZW50VmFyaWFibGVUeXBlLlNFQ1JFVFNfTUFOQUdFUixcbiAgICAgICAgfSxcbiAgICAgIF07XG4gICAgfSksXG4gICk7XG59XG5cbi8qKlxuICogSW4tY2x1c3RlciAvIGxvY2FsIHJlZ2lzdHJ5IGhvc3RuYW1lcyB0aGF0IEFXUyBDb2RlQnVpbGQgKG91dC1vZi1jbHVzdGVyKVxuICogY2Fubm90IHJlc29sdmUuIEJha2luZyBvbmUgb2YgdGhlc2UgaW50byBhIENvZGVCdWlsZCBpbWFnZSBVUkkgZ3VhcmFudGVlcyBhXG4gKiBgQlVJTERfQ09OVEFJTkVSX1VOQUJMRV9UT19QVUxMX0lNQUdFYCBmYWlsdXJlIGF0IGJ1aWxkIHRpbWUsIHNvIHdlIGZhaWwgdGhlXG4gKiBzeW50aCBmYXN0IGluc3RlYWQg4oCUIHdpdGggYSBtZXNzYWdlIHRoYXQgc2F5cyBob3cgdG8gZml4IGl0LlxuICovXG5jb25zdCBVTlJFQUNIQUJMRV9QVUxMX0hPU1RTID0gbmV3IFNldChbJ3JlZ2lzdHJ5JywgJ2xvY2FsaG9zdCcsICcxMjcuMC4wLjEnLCAnOjoxJywgJzAuMC4wLjAnXSk7XG5cbi8qKiBLdWJlcm5ldGVzIC8gbUROUyBzdWZmaXhlcyB0aGF0IG9ubHkgcmVzb2x2ZSBpbnNpZGUgdGhlIGNsdXN0ZXIvTEFOLiAqL1xuY29uc3QgVU5SRUFDSEFCTEVfSE9TVF9TVUZGSVhFUyA9IFsnLnN2YycsICcuc3ZjLmNsdXN0ZXIubG9jYWwnLCAnLmxvY2FsJ107XG5cbi8qKlxuICogUmVkdWNlIGEgcmVnaXN0cnkgaG9zdCB2YWx1ZSB0byBpdHMgYmFyZSBob3N0bmFtZSBmb3IgdGhlIHJlYWNoYWJpbGl0eSBjaGVjazpcbiAqIHN0cmlwIElQdjYgYnJhY2tldHMgYW5kIGFueSBgOnBvcnRgIHN1ZmZpeCAoYW4gb3BlcmF0b3IgbWF5IHB1dCBob3N0K3BvcnQgaW5cbiAqIG9uZSBmaWVsZCksIHNvIGByZWdpc3RyeTo1MDAwYCAvIGBbOjoxXTo1MDAwYCBhcmUgc3RpbGwgcmVjb2duaXplZC5cbiAqL1xuZnVuY3Rpb24gYmFyZUhvc3RuYW1lKHJhd0hvc3Q6IHN0cmluZyk6IHN0cmluZyB7XG4gIGxldCBoID0gcmF3SG9zdC50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgaWYgKGguc3RhcnRzV2l0aCgnWycpKSB7XG4gICAgY29uc3QgZW5kID0gaC5pbmRleE9mKCddJyk7XG4gICAgaWYgKGVuZCAhPT0gLTEpIHJldHVybiBoLnNsaWNlKDEsIGVuZCk7IC8vIFs6OjFdOjUwMDAgLT4gOjoxXG4gIH1cbiAgLy8gU3RyaXAgYSB0cmFpbGluZyA6cG9ydCBvbmx5IGZvciBub24tSVB2NiB2YWx1ZXMgKElQdjYgY29udGFpbnMgJzo6JyBvciA+MSAnOicpLlxuICBpZiAoaC5pbmNsdWRlcygnOicpICYmIChoLm1hdGNoKC86L2cpPy5sZW5ndGggPz8gMCkgPT09IDEpIGggPSBoLnNwbGl0KCc6JylbMF07XG4gIHJldHVybiBoO1xufVxuXG4vKiogVHJ1ZSB3aGVuIENvZGVCdWlsZCAob3V0LW9mLWNsdXN0ZXIpIGNvdWxkIG5vdCByZXNvbHZlIHRoaXMgcmVnaXN0cnkgaG9zdC4gKi9cbmZ1bmN0aW9uIGlzVW5yZWFjaGFibGVQdWxsSG9zdChyYXdIb3N0OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3QgaCA9IGJhcmVIb3N0bmFtZShyYXdIb3N0KTtcbiAgaWYgKGggPT09ICcnKSByZXR1cm4gdHJ1ZTsgLy8gbm8gaG9zdCDihpIgbWFsZm9ybWVkIFVSSVxuICBpZiAoVU5SRUFDSEFCTEVfUFVMTF9IT1NUUy5oYXMoaCkpIHJldHVybiB0cnVlO1xuICByZXR1cm4gVU5SRUFDSEFCTEVfSE9TVF9TVUZGSVhFUy5zb21lKChzKSA9PiBoLmVuZHNXaXRoKHMpKTtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBleHRlcm5hbCAob3V0LW9mLWNsdXN0ZXIpIHJlZ2lzdHJ5IHB1bGwgaG9zdCArIGA6cG9ydGAgc3VmZml4IGZvclxuICogYSBDb2RlQnVpbGQgaW1hZ2UgVVJJLCBhbmQgZmFpbCBmYXN0IGlmIGl0IHJlc29sdmVkIHRvIGFuIGluLWNsdXN0ZXIvbG9jYWxcbiAqIGRlZmF1bHQgdGhhdCBDb2RlQnVpbGQgY2FuJ3QgcmVhY2guXG4gKlxuICogUHJlZmVycyBgcHVsbEhvc3RgL2BwdWxsUG9ydGAgKHRoZSBwdWJsaWMgZW5kcG9pbnQgQ29kZUJ1aWxkIGNhbiByZXNvbHZlKSBhbmRcbiAqIGZhbGxzIGJhY2sgdG8gYGhvc3RgL2Bwb3J0YCBmb3Igc2luZ2xlLWhvc3QgZGVwbG95cy4gcGlwZWxpbmUtbWFuYWdlciBiYWtlc1xuICogdGhlIHJpZ2h0IHB1bGwgaG9zdCBpbnRvIHRoZSBzeW50aCBhdXRvbWF0aWNhbGx5IGZyb20gdGhlIHBsYXRmb3JtIFVSTCBpdFxuICogZGVwbG95cyBhZ2FpbnN0IChgcHJvcHMucmVnaXN0cnlgKTsgdGhpcyBndWFyZCBjYXRjaGVzIHRoZSBjYXNlIHdoZXJlIG5laXRoZXJcbiAqIHRoYXQgbm9yIGBJTUFHRV9SRUdJU1RSWV9QVUxMX0hPU1RgL2BQTEFURk9STV9CQVNFX1VSTGAgd2FzIHN1cHBsaWVkLlxuICpcbiAqIEVzY2FwZSBoYXRjaDogYEFMTE9XX0lOQ0xVU1RFUl9QVUxMX0hPU1Q9dHJ1ZWAgKGUuZy4gQ29kZUJ1aWxkIGluIGEgVlBDIHdob3NlXG4gKiBwcml2YXRlIEROUyBnZW51aW5lbHkgcmVzb2x2ZXMgdGhlIGluLWNsdXN0ZXIgbmFtZSkuXG4gKi9cbmZ1bmN0aW9uIHJlc29sdmVFeHRlcm5hbFB1bGxUYXJnZXQoXG4gIHJlZ2lzdHJ5OiB7IGhvc3Q/OiBzdHJpbmc7IHBvcnQ/OiBudW1iZXI7IHB1bGxIb3N0Pzogc3RyaW5nOyBwdWxsUG9ydD86IG51bWJlciB9LFxuKTogeyBob3N0OiBzdHJpbmc7IHBvcnRQYXJ0OiBzdHJpbmcgfSB7XG4gIGNvbnN0IGhvc3QgPSByZWdpc3RyeS5wdWxsSG9zdCB8fCByZWdpc3RyeS5ob3N0IHx8ICcnO1xuICBjb25zdCBwb3J0ID0gcmVnaXN0cnkucHVsbFBvcnQgPz8gcmVnaXN0cnkucG9ydDtcbiAgaWYgKHByb2Nlc3MuZW52LkFMTE9XX0lOQ0xVU1RFUl9QVUxMX0hPU1QgIT09ICd0cnVlJyAmJiBpc1VucmVhY2hhYmxlUHVsbEhvc3QoaG9zdCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgUmVnaXN0cnkgcHVsbCBob3N0IHJlc29sdmVkIHRvICcke2hvc3R9Jywgd2hpY2ggQVdTIENvZGVCdWlsZCBjYW5ub3QgcmVhY2gg4oCUIGl0IGlzIHRoZSBgICtcbiAgICAgICdpbi1jbHVzdGVyL2xvY2FsIGRlZmF1bHQuIFRoZSBzeW50aGVzaXplZCBwaXBlbGluZSB3b3VsZCBmYWlsIGF0IGJ1aWxkIHRpbWUgd2l0aCAnICtcbiAgICAgICdCVUlMRF9DT05UQUlORVJfVU5BQkxFX1RPX1BVTExfSU1BR0UuIFNldCB0aGUgcGxhdGZvcm1cXCdzIHB1YmxpYyBob3N0IHZpYSAnICtcbiAgICAgICdJTUFHRV9SRUdJU1RSWV9QVUxMX0hPU1Qgb3IgUExBVEZPUk1fQkFTRV9VUkwgYmVmb3JlIGRlcGxveWluZyDigJQgcGlwZWxpbmUtbWFuYWdlciBiYWtlcyAnICtcbiAgICAgICd0aGlzIGluIGF1dG9tYXRpY2FsbHkgZnJvbSB0aGUgcGxhdGZvcm0gVVJMIGl0IGRlcGxveXMgYWdhaW5zdC4gJyArXG4gICAgICAnU2V0IEFMTE9XX0lOQ0xVU1RFUl9QVUxMX0hPU1Q9dHJ1ZSB0byBieXBhc3MgKGluLVZQQyBwcml2YXRlIEROUykuJyxcbiAgICApO1xuICB9XG4gIGNvbnN0IHBvcnRQYXJ0ID0gcG9ydCAmJiBwb3J0ICE9PSA4MCAmJiBwb3J0ICE9PSA0NDMgPyBgOiR7cG9ydH1gIDogJyc7XG4gIHJldHVybiB7IGhvc3QsIHBvcnRQYXJ0IH07XG59XG5cbi8qKlxuICogUmVzb2x2ZSB0aGUgQ29kZUJ1aWxkIGltYWdlIHRvIHVzZSBmb3IgYSBwbHVnaW4uXG4gKlxuICogU3RyYXRlZ3k6XG4gKiAgIDEuIElmIHRoZSBwbHVnaW4gc2V0cyBgYXdzOmNkazpjb2RlYnVpbGQ6YnVpbGRlbnZpcm9ubWVudDpidWlsZEltYWdlYFxuICogICAgICBleHBsaWNpdGx5IGluIG1ldGFkYXRhLCB0aGUgbWV0YWRhdGEtYnVpbGRlciBwYXNzdGhyb3VnaCBoYW5kbGVzIGl0XG4gKiAgICAgICh2aWEgYG1ldGFkYXRhRm9yQnVpbGRFbnZpcm9ubWVudGApLiBUaGlzIGZ1bmN0aW9uIHJldHVybnNcbiAqICAgICAgYHVuZGVmaW5lZGAgYW5kIHRoZSBtZXRhZGF0YSB3aW5zLlxuICogICAyLiBPdGhlcndpc2UsIGlmIHRoZSBwbHVnaW4gaGFzIGEgYG5hbWVgK2B2ZXJzaW9uYCBBTkQgdGhlIHJlZ2lzdHJ5XG4gKiAgICAgIGNvbmZpZyBpcyBwb3B1bGF0ZWQsIGJ1aWxkIGEgYExpbnV4QnVpbGRJbWFnZS5mcm9tRG9ja2VyUmVnaXN0cnkoKWBcbiAqICAgICAgaW1hZ2UgcG9pbnRpbmcgYXQgYDxyZWdpc3RyeS1ob3N0Pjo8cG9ydD4vPG5zPi88bmFtZT46PHZlcnNpb24+YFxuICogICAgICB3aGVyZSBgPG5zPmAgaXMgYHN5c3RlbWAgb3IgYG9yZy08b3JnSWQ+YC4gQ29kZUJ1aWxkIGF1dGhlbnRpY2F0ZXMgYnlcbiAqICAgICAgc2VuZGluZyB0aGUgcGVyLW9yZyBwbGF0Zm9ybSBTZWNyZXQgYXMgQmFzaWMgYXV0aCB0b1xuICogICAgICBgcGlwZWxpbmUtaW1hZ2UtcmVnaXN0cnlgJ3MgYC90b2tlbmAgZW5kcG9pbnQ7IHRoZSBKV1QgaW4gYHBhc3N3b3JkYFxuICogICAgICByZXNvbHZlcyB0byBhIHJlZ2lzdHJ5IHRva2VuIHNjb3BlZCB0byB0aGUgb3JnLlxuICogICAzLiBJZiBgbWV0YWRhdGFfb25seWAsIHJldHVybiBgdW5kZWZpbmVkYCBzbyBDb2RlQnVpbGQgdXNlcyBpdHMgZGVmYXVsdFxuICogICAgICAoYHN0YW5kYXJkOjcuMGApLlxuICpcbiAqIGBzY29wZWAgYW5kIGBvcmdJZGAgYXJlIHJlcXVpcmVkOiB0aGUgcGVyLW9yZyBwbGF0Zm9ybSBTZWNyZXQgaXMgbmFtZWRcbiAqIGBwaXBlbGluZS1idWlsZGVyLzxvcmdJZD4vcGxhdGZvcm1gLCBhbmQgYFNlY3JldC5mcm9tU2VjcmV0TmFtZVYyKClgXG4gKiBuZWVkcyBhIENvbnN0cnVjdCB0byBhbmNob3IgdGhlIGltcG9ydGVkIHNlY3JldCB0by5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVQbHVnaW5JbWFnZShzY29wZTogQ29uc3RydWN0IHwgdW5kZWZpbmVkLCBwbHVnaW46IFBsdWdpbiwgb3JnSWQ/OiBzdHJpbmcpOiBJQnVpbGRJbWFnZSB8IHVuZGVmaW5lZCB7XG4gIC8vIGBtZXRhZGF0YV9vbmx5YCBwbHVnaW5zIGxlZ2l0aW1hdGVseSBoYXZlIG5vIGltYWdlIOKAlCB0aGVpciB3b3JrIHJ1bnNcbiAgLy8gaW4gdGhlIGRlZmF1bHQgQ29kZUJ1aWxkIGltYWdlLiBRdWlldCBza2lwLlxuICBpZiAocGx1Z2luLmJ1aWxkVHlwZSA9PT0gJ21ldGFkYXRhX29ubHknKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gIC8vIGJ1aWxkX2ltYWdlIC8gcHJlYnVpbHQgcGx1Z2lucyBtdXN0IGhhdmUgbmFtZSArIHZlcnNpb24gKERCIGNvbnN0cmFpbnRcbiAgLy8gbWFrZXMgYm90aCBOT1QgTlVMTCkuIERlZmVuc2l2ZSBjaGVjayBpbiBjYXNlIGEgbWFsZm9ybWVkIHJvdyBzbGlwcyBpbi5cbiAgaWYgKCFwbHVnaW4ubmFtZSB8fCAhcGx1Z2luLnZlcnNpb24pIHtcbiAgICBsb2cud2FybihcbiAgICAgIGBQbHVnaW4gXCIke3BsdWdpbi5uYW1lfVwiIGhhcyBidWlsZFR5cGU9JHtwbHVnaW4uYnVpbGRUeXBlfSBidXQgbWlzc2luZyBuYW1lL3ZlcnNpb24g4oCUIGAgK1xuICAgICAgJ0NvZGVCdWlsZCB3aWxsIHJ1biBvbiBhd3MvY29kZWJ1aWxkL3N0YW5kYXJkOjguMCBhbmQgd29uXFwndCBoYXZlIHRoZSBwbHVnaW5cXCdzIGJha2VkIHRvb2xzLicsXG4gICAgKTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgbGV0IHJlZ2lzdHJ5O1xuICB0cnkge1xuICAgIHJlZ2lzdHJ5ID0gQ29uZmlnLmdldCgncmVnaXN0cnknKTtcbiAgfSBjYXRjaCB7XG4gICAgLy8gQ29uZmlnIG5hbWVzcGFjZSBub3QgbG9hZGVkIChlLmcuLCB1bml0IHRlc3RzIHdpdGhvdXQgZnVsbCBjb25maWcpLlxuICAgIGxvZy53YXJuKFxuICAgICAgYFBsdWdpbiBcIiR7cGx1Z2luLm5hbWV9OiR7cGx1Z2luLnZlcnNpb259XCIgbmVlZHMgdGhlIHJlZ2lzdHJ5IGNvbmZpZyBidXQgaXQncyBub3QgbG9hZGVkIOKAlCBgICtcbiAgICAgICdDb2RlQnVpbGQgd2lsbCBmYWxsIGJhY2sgdG8gYXdzL2NvZGVidWlsZC9zdGFuZGFyZDo4LjAuICcgK1xuICAgICAgJ1NldCBJTUFHRV9SRUdJU1RSWV9IT1NUICsgSU1BR0VfUkVHSVNUUllfUE9SVCBpbiBwaXBlbGluZS1tYW5hZ2VyXFwncyBlbnZpcm9ubWVudC4nLFxuICAgICk7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIGlmICghcmVnaXN0cnk/Lmhvc3QpIHtcbiAgICBsb2cud2FybihcbiAgICAgIGBQbHVnaW4gXCIke3BsdWdpbi5uYW1lfToke3BsdWdpbi52ZXJzaW9ufVwiIG5lZWRzIElNQUdFX1JFR0lTVFJZX0hPU1QgYnV0IGl0J3MgZW1wdHkg4oCUIGAgK1xuICAgICAgJ0NvZGVCdWlsZCB3aWxsIGZhbGwgYmFjayB0byBhd3MvY29kZWJ1aWxkL3N0YW5kYXJkOjguMC4gJyArXG4gICAgICAnU2V0IElNQUdFX1JFR0lTVFJZX0hPU1QgaW4gcGlwZWxpbmUtbWFuYWdlclxcJ3MgZW52aXJvbm1lbnQgdG8gdXNlIHRoZSBwbHVnaW4gaW1hZ2UuJyxcbiAgICApO1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgaWYgKCFzY29wZSkge1xuICAgIGxvZy53YXJuKGBQbHVnaW4gXCIke3BsdWdpbi5uYW1lfVwiIGltYWdlIHJlc29sdXRpb24gc2tpcHBlZDogbm8gY29uc3RydWN0IHNjb3BlIHByb3ZpZGVkYCk7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8vIFBlci1vcmcgYXV0aCByZXF1aXJlcyBvcmdJZCDigJQgdGhlIFNlY3JldCBpcyBuYW1lZCBwZXIgb3JnLlxuICBpZiAoIW9yZ0lkKSB7XG4gICAgbG9nLndhcm4oXG4gICAgICBgUGx1Z2luIFwiJHtwbHVnaW4ubmFtZX1cIiBpbWFnZSByZXNvbHV0aW9uIHNraXBwZWQ6IG9yZ0lkIGlzIHJlcXVpcmVkIHRvIGAgK1xuICAgICAgJ3Jlc29sdmUgdGhlIHBlci1vcmcgcGxhdGZvcm0gU2VjcmV0IHVzZWQgZm9yIENvZGVCdWlsZCBCYXNpYyBhdXRoLicsXG4gICAgKTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgLy8gQ29tcG9zZSB0aGUgaW1hZ2UgVVJJLiBOYW1lc3BhY2UgYnkgb3duZXJzaGlwOlxuICAvLyAgIC0gUGx1Z2lucyBvd25lZCBieSB0aGUgc3lzdGVtIG9yZyDihpIgYHN5c3RlbS88bmFtZT46PHZlcnNpb24+YC5cbiAgLy8gICAgIHBpcGVsaW5lLWltYWdlLXJlZ2lzdHJ5J3MgdG9rZW4gc2VydmljZSBncmFudHMgcHVsbCBvbiBgc3lzdGVtLypgXG4gIC8vICAgICB0byBhbnkgYXV0aGVudGljYXRlZCBvcmcgdXNlciAocmVhZC1vbmx5IGNhdGFsb2cgb2Ygc2hhcmVkIHBsdWdpbnMpLlxuICAvLyAgIC0gUGx1Z2lucyBvd25lZCBieSBhIHRlbmFudCBvcmcg4oaSIGBvcmctPG9yZ0lkPi88bmFtZT46PHZlcnNpb24+YC5cbiAgLy8gICAgIFRoZSB0b2tlbiBzZXJ2aWNlIGdyYW50cyBwdWxsLHB1c2ggb25seSB0byBtZW1iZXJzIG9mIHRoYXQgb3JnLlxuICAvLyBUaGUgcGx1Z2luJ3Mgb3duIGBvcmdJZGAgKG5vdCB0aGUgY2FsbGVyJ3MpIGRlY2lkZXMgdGhlIG5hbWVzcGFjZSDigJRcbiAgLy8gdGVuYW50IHBpcGVsaW5lcyBwdWxsaW5nIHNoYXJlZCBzeXN0ZW0gcGx1Z2lucyBzdGlsbCBnZXQgdGhlIGBzeXN0ZW0vYFxuICAvLyBwYXRoIGJlY2F1c2UgdGhhdCdzIHdoZXJlIHRoZSBpbWFnZSBhY3R1YWxseSBsaXZlcy5cbiAgLy8gVXNlIHRoZSBleHRlcm5hbCBwdWxsIGhvc3QvcG9ydDogdGhpcyBpbWFnZSBVUkkgaXMgY29uc3VtZWQgYnkgQVdTXG4gIC8vIENvZGVCdWlsZCAob3V0LW9mLWNsdXN0ZXIpLCB3aGljaCBjYW4ndCByZXNvbHZlIHRoZSBpbi1jbHVzdGVyXG4gIC8vIGByZWdpc3RyeTo1MDAwYCBDbHVzdGVySVAuIEZhbGwgYmFjayB0byBgaG9zdGAvYHBvcnRgIGZvciBzaW5nbGUtaG9zdCAvXG4gIC8vIGluLWNsdXN0ZXItb25seSBkZXBsb3lzIHdoZXJlIG5vIHNlcGFyYXRlIHB1bGwgaG9zdCBpcyBjb25maWd1cmVkLlxuICBjb25zdCB7IGhvc3Q6IHB1bGxIb3N0LCBwb3J0UGFydCB9ID0gcmVzb2x2ZUV4dGVybmFsUHVsbFRhcmdldChyZWdpc3RyeSk7XG4gIC8vIE11c3QgbWF0Y2ggdGhlIFBVU0ggc2lkZSAoYXBpL3BsdWdpbiBwbHVnaW5VcmkpIGV4YWN0bHksIG9yIGEgc3lzdGVtXG4gIC8vIHBsdWdpbidzIGltYWdlIGlzIHB1c2hlZCB0byBgc3lzdGVtL+KApmAgYnV0IHRoaXMgc3ludGggcHVsbHMgZnJvbVxuICAvLyBgb3JnLTxvYmplY3RpZD4v4oCmYCDihpIgYnVpbGQtdGltZSBwdWxsIGZhaWx1cmUuIGBTWVNURU1fT1JHX0lEYCBpcyB0aGVcbiAgLy8gd2VsbC1rbm93biBPYmplY3RJZCAoTk9UIHRoZSBzdHJpbmcgJ3N5c3RlbScpLlxuICBjb25zdCBuYW1lc3BhY2UgPSBwbHVnaW4ub3JnSWQgPT09IFNZU1RFTV9PUkdfSUQgPyAnc3lzdGVtJyA6IGBvcmctJHtwbHVnaW4ub3JnSWR9YDtcbiAgY29uc3QgaW1hZ2VVcmkgPSBgJHtwdWxsSG9zdH0ke3BvcnRQYXJ0fS8ke25hbWVzcGFjZX0vJHtwbHVnaW4ubmFtZX06JHtwbHVnaW4udmVyc2lvbn1gO1xuXG4gIC8vIENvZGVCdWlsZCByZWFkcyBgcGlwZWxpbmUtYnVpbGRlci88b3JnSWQ+L3BsYXRmb3JtYCBhbmQgc2VuZHMgaXRzXG4gIC8vIGB1c2VybmFtZWAvYHBhc3N3b3JkYCBmaWVsZHMgYXMgSFRUUCBCYXNpYyB0byB0aGUgcmVnaXN0cnkuIFRoZVxuICAvLyByZWdpc3RyeSBjaGFsbGVuZ2VzIHdpdGggYSBCZWFyZXIgcmVhbG0gcG9pbnRpbmcgYXRcbiAgLy8gcGlwZWxpbmUtaW1hZ2UtcmVnaXN0cnkncyBgL3Rva2VuYCBlbmRwb2ludDsgdGhlIERvY2tlciBjbGllbnQgZm9yd2FyZHNcbiAgLy8gdGhvc2UgY3JlZHMsIHRoZSBwYXNzd29yZCBpcyB2ZXJpZmllZCBhcyBhIHBsYXRmb3JtIEpXVCwgYW5kIGEgcmVnaXN0cnlcbiAgLy8gdG9rZW4gc2NvcGVkIHRvIHRoZSBvcmcgaXMgaXNzdWVkLlxuICAvL1xuICAvLyBTYW1lIFNlY3JldCBpcyByZWFkIGJ5IHRoZSBwbHVnaW4tbG9va3VwIExhbWJkYSAodmlhIHRoZSBgcGFzc3dvcmRgXG4gIC8vIGZpZWxkKSDigJQgb25lIFNlY3JldCBzZXJ2ZXMgYm90aCBmbG93cy5cbiAgY29uc3Qgc3RhY2sgPSBTdGFjay5vZihzY29wZSk7XG4gIGNvbnN0IHNlY3JldE5hbWUgPSBDb3JlQ29uc3RhbnRzLnNlY3JldFBhdGgob3JnSWQsICdwbGF0Zm9ybScpO1xuICAvLyBDREsgY29uc3RydWN0IElEcyBhbGxvdyBvbmx5IFtBLVphLXowLTlfLV07IHNhbml0aXplIHRoZSBzZWNyZXQgbmFtZS5cbiAgY29uc3Qgc2VjcmV0Q29uc3RydWN0SWQgPSBgUGxhdGZvcm1DcmVkc18ke3NlY3JldE5hbWUucmVwbGFjZSgvW15BLVphLXowLTlfLV0vZywgJ18nKX1gO1xuICBjb25zdCBjcmVkZW50aWFsc1NlY3JldCA9IChzdGFjay5ub2RlLnRyeUZpbmRDaGlsZChzZWNyZXRDb25zdHJ1Y3RJZCkgYXMgU2VjcmV0IHwgdW5kZWZpbmVkKVxuICAgID8/IFNlY3JldC5mcm9tU2VjcmV0TmFtZVYyKHN0YWNrLCBzZWNyZXRDb25zdHJ1Y3RJZCwgc2VjcmV0TmFtZSk7XG5cbiAgcmV0dXJuIExpbnV4QnVpbGRJbWFnZS5mcm9tRG9ja2VyUmVnaXN0cnkoaW1hZ2VVcmksIHtcbiAgICBzZWNyZXRzTWFuYWdlckNyZWRlbnRpYWxzOiBjcmVkZW50aWFsc1NlY3JldCxcbiAgfSk7XG59XG5cbi8qKlxuICogUmVzb2x2ZSB0aGUgZmFsbGJhY2sgQ29kZUJ1aWxkIGltYWdlIGZvciBzdGVwcyB3aXRob3V0IGEgcGx1Z2luLWJha2VkIG9uZS5cbiAqXG4gKiBSZWFkcyBgYXdzLmNvZGVCdWlsZC5kZWZhdWx0SW1hZ2VgIChlbnY6IGBDT0RFQlVJTERfREVGQVVMVF9JTUFHRWApLlxuICogRGVmYXVsdDogYHBpcGVsaW5lLWJvb3RzdHJhcDoxLjBgIOKAlCB0aGUgbG9jYWwgdGFnIGJ1aWx0IGJ5XG4gKiBgZGVwbG95L2NvZGVidWlsZC9ib290c3RyYXAvRG9ja2VyZmlsZWAuXG4gKlxuICogLSBBV1MgY3VyYXRlZCBpbWFnZSAoYGF3cy9jb2RlYnVpbGQvKmApOiB1c2UgdGhlIG1hdGNoaW5nIGBMaW51eEJ1aWxkSW1hZ2VgXG4gKiAgIGNvbnN0YW50IHNvIENESyBlbWl0cyBgaW1hZ2VQdWxsQ3JlZGVudGlhbHNUeXBlOiBDT0RFQlVJTERgLiBVc2luZ1xuICogICBgZnJvbURvY2tlclJlZ2lzdHJ5KClgIGhlcmUgd291bGQgc2V0IFNFUlZJQ0VfUk9MRSBjcmVkZW50aWFscywgd2hpY2hcbiAqICAgQ0ZOIHJlamVjdHMgd2l0aCBcImNhbm5vdCB1c2UgYSBDb2RlQnVpbGQgY3VyYXRlZCBpbWFnZSB3aXRoXG4gKiAgIGltYWdlUHVsbENyZWRlbnRpYWxzVHlwZSBTRVJWSUNFX1JPTEVcIi5cbiAqIC0gQmFyZSB0YWcgKG5vIGAvYCk6IGF1dG8tcHJlZml4ZWQgdG9cbiAqICAgYDxyZWdpc3RyeS1ob3N0Pjo8cG9ydD4vbGlicmFyeS88dGFnPmAgdXNpbmcgdGhlIHJlZ2lzdHJ5IGNvbmZpZyxcbiAqICAgd2l0aCB0aGUgcGVyLW9yZyBwbGF0Zm9ybSBTZWNyZXQgYXMgQmFzaWMgYXV0aCDigJQgc2FtZSBwYXRoXG4gKiAgIGByZXNvbHZlUGx1Z2luSW1hZ2UoKWAgdXNlcyBmb3IgcGx1Z2luIGltYWdlcy4gTmVlZHMgYHNjb3BlYCArIGBvcmdJZGAuXG4gKiAtIEZ1bGx5LXF1YWxpZmllZCByZWdpc3RyeSBVUkkgKGNvbnRhaW5zIGAvYCk6IHVzZWQgYXMtaXMsIG5vIGF1dGggd2lyZWQuXG4gKiAtIE1pc3NpbmcgcmVnaXN0cnkgY29uZmlnIG9yIHNjb3BlL29yZ0lkIOKGkiBmYWxscyBiYWNrIHRvXG4gKiAgIGBhd3MvY29kZWJ1aWxkL3N0YW5kYXJkOjguMGAgKGFzIGEgQ1VSQVRFRCBpbWFnZSwgc2VlIGFib3ZlKSwgd2l0aCBhXG4gKiAgIHdhcm5pbmcgc28gc3ludGggZGVncmFkZXMgZ3JhY2VmdWxseSBvbiBwYXJ0aWFsbHkgY29uZmlndXJlZCBlbnZzLlxuICovXG5jb25zdCBTVEFOREFSRF84XzAgPSAnYXdzL2NvZGVidWlsZC9zdGFuZGFyZDo4LjAnO1xuXG4vKipcbiAqIGF3cy1jZGstbGliIDIuMjU4IGhhcyBubyBgTGludXhCdWlsZEltYWdlLlNUQU5EQVJEXzhfMGAgc3RhdGljIHlldCwgc28gdGhlXG4gKiBjdXJhdGVkIDguMCBmYWxsYmFjayByZXNvbHZlcyB2aWEgYGZyb21Db2RlQnVpbGRJbWFnZUlkYCAoc3RpbGwgQ09ERUJVSUxEIGNyZWRzKS5cbiAqL1xuY29uc3QgZmFsbGJhY2tCdWlsZEltYWdlID0gKCk6IElCdWlsZEltYWdlID0+IExpbnV4QnVpbGRJbWFnZS5mcm9tQ29kZUJ1aWxkSW1hZ2VJZChTVEFOREFSRF84XzApO1xuXG4vKipcbiAqIE1hcCBBV1MgY3VyYXRlZCBgYXdzL2NvZGVidWlsZC8qYCBpbWFnZSBzdHJpbmdzIHRvIHRoZSBtYXRjaGluZ1xuICogYExpbnV4QnVpbGRJbWFnZWAgc3RhdGljLiBFeHRlbmQgd2hlbiBuZXdlciBzdGFuZGFyZCBpbWFnZXMgc2hpcC5cbiAqIFJldHVybnMgYHVuZGVmaW5lZGAgZm9yIG5vbi1jdXJhdGVkIHN0cmluZ3Mg4oCUIGNhbGxlciBzaG91bGQgdXNlXG4gKiBgZnJvbURvY2tlclJlZ2lzdHJ5KClgIGZvciB0aG9zZS5cbiAqL1xuZnVuY3Rpb24gY3VyYXRlZExpbnV4QnVpbGRJbWFnZShpbWFnZUlkOiBzdHJpbmcpOiBJQnVpbGRJbWFnZSB8IHVuZGVmaW5lZCB7XG4gIGlmICghaW1hZ2VJZC5zdGFydHNXaXRoKCdhd3MvY29kZWJ1aWxkLycpKSByZXR1cm4gdW5kZWZpbmVkO1xuICBzd2l0Y2ggKGltYWdlSWQpIHtcbiAgICBjYXNlICdhd3MvY29kZWJ1aWxkL3N0YW5kYXJkOjYuMCc6IHJldHVybiBMaW51eEJ1aWxkSW1hZ2UuU1RBTkRBUkRfNl8wO1xuICAgIGNhc2UgJ2F3cy9jb2RlYnVpbGQvc3RhbmRhcmQ6NS4wJzogcmV0dXJuIExpbnV4QnVpbGRJbWFnZS5TVEFOREFSRF81XzA7XG4gICAgLy8gTmV3ZXIgY29kZW5hbWVkIGltYWdlcyByb3V0ZSB0aHJvdWdoIGZyb21Db2RlQnVpbGRJbWFnZUlkLCB3aGljaFxuICAgIC8vIENESyBhbHNvIG1hcmtzIGFzIENPREVCVUlMRCBjcmVkZW50aWFscyAoY3VyYXRlZCkuXG4gICAgZGVmYXVsdDogcmV0dXJuIExpbnV4QnVpbGRJbWFnZS5mcm9tQ29kZUJ1aWxkSW1hZ2VJZChpbWFnZUlkKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZURlZmF1bHRCdWlsZEltYWdlKHNjb3BlPzogQ29uc3RydWN0LCBvcmdJZD86IHN0cmluZyk6IElCdWlsZEltYWdlIHtcbiAgbGV0IGNvbmZpZ3VyZWQ6IHN0cmluZztcbiAgdHJ5IHtcbiAgICBjb25maWd1cmVkID0gQ29uZmlnLmdldCgnYXdzJykuY29kZUJ1aWxkLmRlZmF1bHRJbWFnZTtcbiAgfSBjYXRjaCB7XG4gICAgY29uZmlndXJlZCA9IFNUQU5EQVJEXzhfMDtcbiAgfVxuXG4gIC8vIEFXUyBjdXJhdGVkIGltYWdlIOKGkiBDT0RFQlVJTEQgY3JlZGVudGlhbHMuIE1VU1QgZ28gdGhyb3VnaCB0aGVcbiAgLy8gTGludXhCdWlsZEltYWdlIGNvbnN0YW50cywgbm90IGZyb21Eb2NrZXJSZWdpc3RyeSgpLlxuICBjb25zdCBjdXJhdGVkID0gY3VyYXRlZExpbnV4QnVpbGRJbWFnZShjb25maWd1cmVkKTtcbiAgaWYgKGN1cmF0ZWQpIHJldHVybiBjdXJhdGVkO1xuXG4gIC8vIEZ1bGx5LXF1YWxpZmllZCBVUkkg4oCUIG9wZXJhdG9yIG93bnMgaXQuIE5vIHJlZ2lzdHJ5L1NlY3JldCB3aXJpbmcuXG4gIGlmIChjb25maWd1cmVkLmluY2x1ZGVzKCcvJykpIHtcbiAgICByZXR1cm4gTGludXhCdWlsZEltYWdlLmZyb21Eb2NrZXJSZWdpc3RyeShjb25maWd1cmVkKTtcbiAgfVxuXG4gIC8vIEJhcmUgdGFnIOKGkiBhdXRvLXByZWZpeCB3aXRoIHRoZSBwbGF0Zm9ybSByZWdpc3RyeSwgbWlycm9yaW5nXG4gIC8vIHJlc29sdmVQbHVnaW5JbWFnZSgpLiBCYWlsIHRvIHRoZSBjdXJhdGVkIHN0YW5kYXJkOjguMCBmYWxsYmFja1xuICAvLyAod2l0aCBhIHdhcm5pbmcpIGF0IGVhY2ggbWlzc2luZyBwcmVyZXF1aXNpdGUgc28gc3ludGggZGVncmFkZXNcbiAgLy8gZ3JhY2VmdWxseSBvbiBwYXJ0aWFsbHkgY29uZmlndXJlZCBlbnZpcm9ubWVudHMgaW5zdGVhZCBvZiBmYWlsaW5nLlxuICBsZXQgcmVnaXN0cnk7XG4gIHRyeSB7XG4gICAgcmVnaXN0cnkgPSBDb25maWcuZ2V0KCdyZWdpc3RyeScpO1xuICB9IGNhdGNoIHtcbiAgICBsb2cud2FybihcbiAgICAgIGBDT0RFQlVJTERfREVGQVVMVF9JTUFHRT0nJHtjb25maWd1cmVkfScgaXMgYSBiYXJlIHRhZyBidXQgcmVnaXN0cnkgY29uZmlnIG5vdCBsb2FkZWQg4oCUIGAgK1xuICAgICAgYGZhbGxpbmcgYmFjayB0byAke1NUQU5EQVJEXzhfMH0uIFNldCBJTUFHRV9SRUdJU1RSWV9IT1NUICsgSU1BR0VfUkVHSVNUUllfUE9SVC5gLFxuICAgICk7XG4gICAgcmV0dXJuIGZhbGxiYWNrQnVpbGRJbWFnZSgpO1xuICB9XG4gIGlmICghcmVnaXN0cnk/Lmhvc3QpIHtcbiAgICBsb2cud2FybihcbiAgICAgIGBDT0RFQlVJTERfREVGQVVMVF9JTUFHRT0nJHtjb25maWd1cmVkfScgaXMgYSBiYXJlIHRhZyBidXQgSU1BR0VfUkVHSVNUUllfSE9TVCBpcyBlbXB0eSDigJQgYCArXG4gICAgICBgZmFsbGluZyBiYWNrIHRvICR7U1RBTkRBUkRfOF8wfS5gLFxuICAgICk7XG4gICAgcmV0dXJuIGZhbGxiYWNrQnVpbGRJbWFnZSgpO1xuICB9XG4gIGlmICghc2NvcGUgfHwgIW9yZ0lkKSB7XG4gICAgbG9nLndhcm4oXG4gICAgICBgQ09ERUJVSUxEX0RFRkFVTFRfSU1BR0U9JyR7Y29uZmlndXJlZH0nIG5lZWRzIHNjb3BlK29yZ0lkIGZvciBTZWNyZXQgYXV0aCDigJQgYCArXG4gICAgICBgZmFsbGluZyBiYWNrIHRvICR7U1RBTkRBUkRfOF8wfS4gKENhbGxlciBkaWQgbm90IHN1cHBseSB0aGVtLilgLFxuICAgICk7XG4gICAgcmV0dXJuIGZhbGxiYWNrQnVpbGRJbWFnZSgpO1xuICB9XG5cbiAgLy8gVXNlIHRoZSBleHRlcm5hbCBwdWxsIGhvc3QvcG9ydCAobm90IHRoZSBpbi1jbHVzdGVyIGhvc3QpIOKAlCB0aGlzIFVSSSBpc1xuICAvLyBjb25zdW1lZCBieSBBV1MgQ29kZUJ1aWxkLCB3aGljaCBjYW4ndCByZXNvbHZlIHRoZSBgcmVnaXN0cnk6NTAwMGAgQ2x1c3RlcklQLlxuICBjb25zdCB7IGhvc3Q6IHB1bGxIb3N0LCBwb3J0UGFydCB9ID0gcmVzb2x2ZUV4dGVybmFsUHVsbFRhcmdldChyZWdpc3RyeSk7XG4gIGNvbnN0IGltYWdlVXJpID0gYCR7cHVsbEhvc3R9JHtwb3J0UGFydH0vbGlicmFyeS8ke2NvbmZpZ3VyZWR9YDtcblxuICAvLyBTYW1lIFNlY3JldCBhcyByZXNvbHZlUGx1Z2luSW1hZ2Ug4oCUIHBlci1vcmcgcGxhdGZvcm0gU2VjcmV0LCBDb2RlQnVpbGRcbiAgLy8gc2VuZHMgaXQgYXMgQmFzaWMgYXV0aCwgaW1hZ2UtcmVnaXN0cnkgdmVyaWZpZXMgdGhlIEpXVCBpbiBgcGFzc3dvcmRgLlxuICBjb25zdCBzdGFjayA9IFN0YWNrLm9mKHNjb3BlKTtcbiAgY29uc3Qgc2VjcmV0TmFtZSA9IENvcmVDb25zdGFudHMuc2VjcmV0UGF0aChvcmdJZCwgJ3BsYXRmb3JtJyk7XG4gIGNvbnN0IHNlY3JldENvbnN0cnVjdElkID0gYFBsYXRmb3JtQ3JlZHNfJHtzZWNyZXROYW1lLnJlcGxhY2UoL1teQS1aYS16MC05Xy1dL2csICdfJyl9YDtcbiAgY29uc3QgY3JlZGVudGlhbHNTZWNyZXQgPSAoc3RhY2subm9kZS50cnlGaW5kQ2hpbGQoc2VjcmV0Q29uc3RydWN0SWQpIGFzIFNlY3JldCB8IHVuZGVmaW5lZClcbiAgICA/PyBTZWNyZXQuZnJvbVNlY3JldE5hbWVWMihzdGFjaywgc2VjcmV0Q29uc3RydWN0SWQsIHNlY3JldE5hbWUpO1xuXG4gIHJldHVybiBMaW51eEJ1aWxkSW1hZ2UuZnJvbURvY2tlclJlZ2lzdHJ5KGltYWdlVXJpLCB7XG4gICAgc2VjcmV0c01hbmFnZXJDcmVkZW50aWFsczogY3JlZGVudGlhbHNTZWNyZXQsXG4gIH0pO1xufVxuXG4vKipcbiAqIENyZWF0ZSBhIENvZGVCdWlsZCBzdGVwIG9yIFNoZWxsIHN0ZXAgYmFzZWQgb24gcGx1Z2luIGNvbmZpZ3VyYXRpb24uXG4gKlxuICogTWV0YWRhdGEgbWVyZ2Ugb3JkZXIgKGxhc3Qgd2lucyk6XG4gKiAgIDEuIFBsdWdpbiBtZXRhZGF0YSAoZnJvbSBwbHVnaW4ubWV0YWRhdGEgaW4gZGF0YWJhc2UpIOKAlCB0aGUgcGx1Z2luXG4gKiAgICAgIGF1dGhvcidzIGJha2VkLWluIGRlZmF1bHRzXG4gKiAgIDIuIFN0ZXAtbGV2ZWwgbWV0YWRhdGEgKGZyb20gb3B0aW9ucy5tZXRhZGF0YSkg4oCUIHRoZSBwaXBlbGluZSBhdXRob3Inc1xuICogICAgICBwZXItc3RlcCBvdmVycmlkZXMsIHdoaWNoIGFyZSBtb3JlIHNwZWNpZmljIGFuZCBtdXN0IHdpblxuICpcbiAqIEVudmlyb25tZW50IG1lcmdlIG9yZGVyIChsYXN0IHdpbnMpOlxuICogICAxLiBQbHVnaW4gZW52IHZhcnMgKGZyb20gcGx1Z2luLmVudilcbiAqICAgMi4gQ3VzdG9tIGVudiB2YXJzIChmcm9tIG9wdGlvbnMuZW52KVxuICogICAzLiBXT1JLRElSIGZyb20gbWVyZ2VkIG1ldGFkYXRhXG4gKlxuICogQ0RLIHByb3Agc3ByZWFkIG9yZGVyIChsYXN0IHdpbnMpOlxuICogICBwcm9ncmFtbWF0aWMgZGVmYXVsdHMgKGlucHV0LCBjb21tYW5kcywgZW52LCBuZXR3b3JrKSDihpIgbWV0YWRhdGEgb3ZlcnJpZGVzXG4gKlxuICogVGhpcyBtZWFucyBtZXRhZGF0YSBrZXlzIGxpa2UgYGF3czpjZGs6cGlwZWxpbmVzOmNvZGVidWlsZHN0ZXA6Y29tbWFuZHNgXG4gKiB3aWxsIG92ZXJyaWRlIHRoZSBwbHVnaW4tZGVyaXZlZCBjb21tYW5kcyB3aGVuIGV4cGxpY2l0bHkgc2V0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlQ29kZUJ1aWxkU3RlcChvcHRpb25zOiBDb2RlQnVpbGRTdGVwT3B0aW9ucyk6IFNoZWxsU3RlcCB8IENvZGVCdWlsZFN0ZXAgfCBNYW51YWxBcHByb3ZhbFN0ZXAge1xuICBjb25zdCB7XG4gICAgaWQsIGlucHV0LCBtZXRhZGF0YSwgbmV0d29yaywgc2NvcGUsXG4gICAgcHJlSW5zdGFsbENvbW1hbmRzLCBwb3N0SW5zdGFsbENvbW1hbmRzLCBwcmVDb21tYW5kcywgcG9zdENvbW1hbmRzLFxuICAgIGVudjogY3VzdG9tRW52LCBhZGRpdGlvbmFsSW5wdXRzLCB0aW1lb3V0LCBmYWlsdXJlQmVoYXZpb3IsXG4gICAgYXJ0aWZhY3RNYW5hZ2VyLCBzdGFnZU5hbWUsIHN0YWdlQWxpYXMsIHBsdWdpbkFsaWFzLCBvcmdJZCwgcGlwZWxpbmVTY29wZSxcbiAgfSA9IG9wdGlvbnM7XG5cbiAgLy8gUmVzb2x2ZSB7eyAuLi4gfX0gdGVtcGxhdGVzIGluIHBsdWdpbiBzcGVjIGZpZWxkcyBhZ2FpbnN0IHRoZSBwaXBlbGluZSBzY29wZS5cbiAgLy8gRXZlcnkgY2FsbCBnb2VzIHRocm91Z2ggdGhlIHJlc29sdmVyIOKAlCBwbHVnaW5zIHdpdGhvdXQgdGVtcGxhdGUgdG9rZW5zIGFyZVxuICAvLyBhIG5vLW9wIGZhc3QgcGF0aCBpbnNpZGUgcmVzb2x2ZVBsdWdpblRlbXBsYXRlcygpLlxuICBjb25zdCBwbHVnaW4gPSByZXNvbHZlUGx1Z2luVGVtcGxhdGVzKG9wdGlvbnMucGx1Z2luLCBwaXBlbGluZVNjb3BlKTtcblxuICAvLyBQZXItc3RlcCBtZXRhZGF0YSAodGhlIHBpcGVsaW5lIGF1dGhvcidzIG92ZXJyaWRlcywgZS5nLiBKQVZBX1ZFUlNJT049MjUpXG4gIC8vIG11c3Qgd2luIG92ZXIgdGhlIHBsdWdpbidzIGNhdGFsb2cgYG1ldGFkYXRhYCBkZWZhdWx0cywgc28gaXQgZ29lcyBMQVNULlxuICAvLyBSZXZlcnNpbmcgdGhlc2Ugc2lsZW50bHkgZHJvcHBlZCBwZXItc3RlcCBvdmVycmlkZXMgZm9yIGFueSBwbHVnaW4gdGhhdFxuICAvLyBzaGlwcyBub24tZW1wdHkgY2F0YWxvZyBtZXRhZGF0YSAocGx1Z2lucyB0aGF0IGNhcnJ5IGRlZmF1bHRzIHVuZGVyIGBlbnZgXG4gIC8vIGluc3RlYWQg4oCUIGxpa2UgamF2YS1jb3JyZXR0byDigJQgd2VyZSB1bmFmZmVjdGVkLCB3aGljaCBtYXNrZWQgdGhpcykuXG4gIGNvbnN0IG1lcmdlZCA9IG1lcmdlKHBsdWdpbi5tZXRhZGF0YSA/PyB7fSwgbWV0YWRhdGEgPz8ge30pO1xuXG4gIC8vIE1hbnVhbEFwcHJvdmFsU3RlcDogbm8gY29tbWFuZHMsIGVudiwgY29tcHV0ZSwgb3IgbmV0d29yayDigJQganVzdCBpZCArIG9wdGlvbmFsIGNvbW1lbnRcbiAgaWYgKHBsdWdpbi5wbHVnaW5UeXBlID09PSBQbHVnaW5UeXBlLk1BTlVBTF9BUFBST1ZBTF9TVEVQKSB7XG4gICAgcmV0dXJuIG5ldyBNYW51YWxBcHByb3ZhbFN0ZXAoaWQsIHtcbiAgICAgIGNvbW1lbnQ6IHR5cGVvZiBtZXJnZWQuQVBQUk9WQUxfQ09NTUVOVCA9PT0gJ3N0cmluZycgPyBtZXJnZWQuQVBQUk9WQUxfQ09NTUVOVCA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgfVxuXG4gIGxvZy5kZWJ1ZygnW0NyZWF0ZUNvZGVCdWlsZFN0ZXBdIEJ1aWxkaW5nIHN0ZXAgd2l0aCBtZXJnZWQgbWV0YWRhdGEnKTtcblxuICAvLyBXYXJuIGFib3V0IHJlcXVpcmVkIHNlY3JldHMgd2l0aG91dCBvcmdJZCAoY2FuJ3QgcmVzb2x2ZSlcbiAgY29uc3QgcmVxdWlyZWRTZWNyZXRzID0gcGx1Z2luLnNlY3JldHM/LmZpbHRlcihzID0+IHMucmVxdWlyZWQpID8/IFtdO1xuICBpZiAocmVxdWlyZWRTZWNyZXRzLmxlbmd0aCA+IDAgJiYgIW9yZ0lkKSB7XG4gICAgbG9nLndhcm4oXG4gICAgICBgUGx1Z2luIFwiJHtwbHVnaW4ubmFtZX1cIiBkZWNsYXJlcyAke3JlcXVpcmVkU2VjcmV0cy5sZW5ndGh9IHJlcXVpcmVkIHNlY3JldChzKSBidXQgbm8gb3JnSWQgaXMgYXZhaWxhYmxlLiBgICtcbiAgICAgIGBTZWNyZXRzIHdpbGwgbm90IGJlIGluamVjdGVkOiAke3JlcXVpcmVkU2VjcmV0cy5tYXAocyA9PiBzLm5hbWUpLmpvaW4oJywgJyl9YCxcbiAgICApO1xuICB9XG5cbiAgLy8gUmVzb2x2ZSBwbHVnaW4gc2VjcmV0cyBhcyBTRUNSRVRTX01BTkFHRVIgZW52IHZhcnMg4oCUIG9ubHkgUkVRVUlSRUQgb25lcy5cbiAgLy8gQ29kZUJ1aWxkIHRyZWF0cyBhIFNFQ1JFVFNfTUFOQUdFUiBlbnYgdmFyIGFzIE1BTkRBVE9SWTogaXQgZmFpbHMgdGhlIGJ1aWxkXG4gIC8vIGF0IERPV05MT0FEX1NPVVJDRSAoXCJSZXNvdXJjZU5vdEZvdW5kRXhjZXB0aW9uOiBjYW4ndCBmaW5kIHRoZSBzcGVjaWZpZWRcbiAgLy8gc2VjcmV0XCIpIGlmIHRoZSBzZWNyZXQgaXMgYWJzZW50LiBBdXRvLXdpcmluZyBhbiBPUFRJT05BTCBzZWNyZXRcbiAgLy8gKGByZXF1aXJlZDogZmFsc2VgLCBlLmcuIE5WRF9BUElfS0VZIC8gU0VNR1JFUF9BUFBfVE9LRU4pIHRoZSBvcGVyYXRvciBoYXNuJ3RcbiAgLy8gY3JlYXRlZCB3b3VsZCB0aGVyZWZvcmUgYnJlYWsgZXZlcnkgYnVpbGQg4oCUIHRoZSBleGFjdCBvcHBvc2l0ZSBvZiBcIm9wdGlvbmFsXCIuXG4gIC8vIE9wdGlvbmFsIHNlY3JldHMgYXJlIGxlZnQgZm9yIHRoZSBwbHVnaW4gdG8gcmVhZCBvbmx5IHdoZXJlIHRoZSBvcGVyYXRvciBoYXNcbiAgLy8gcHJvdmlzaW9uZWQgdGhlbSAob3V0LW9mLWJhbmQgZW52L3NlY3JldCksIG5vdCBmb3JjZS1pbmplY3RlZCBoZXJlLlxuICBjb25zdCBzZWNyZXRFbnZWYXJzID0gKHJlcXVpcmVkU2VjcmV0cy5sZW5ndGggJiYgb3JnSWQpXG4gICAgPyB0b1NlY3JldEVudlZhcnMocmVxdWlyZWRTZWNyZXRzLCBvcmdJZClcbiAgICA6IHt9O1xuXG4gIGNvbnN0IGVudiA9IGJ1aWxkRW52KHBsdWdpbiwgbWVyZ2VkLCBjdXN0b21FbnYpO1xuXG4gIC8vIENvZGVQaXBlbGluZSByZXNvbHZlZCB2YXJpYWJsZXMg4oCUIG11c3QgYmUgaW4gQ29kZUJ1aWxkU3RlcC5lbnYgKGFjdGlvbi1sZXZlbCksXG4gIC8vIG5vdCBidWlsZEVudmlyb25tZW50LmVudmlyb25tZW50VmFyaWFibGVzIChwcm9qZWN0LWxldmVsKVxuICBjb25zdCBhY3Rpb25FbnY6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgUElQRUxJTkVfRVhFQ1VUSU9OX0lEOiAnI3tjb2RlcGlwZWxpbmUuUGlwZWxpbmVFeGVjdXRpb25JZH0nLFxuICB9O1xuXG4gIGNvbnN0IG91dHB1dERpciA9IHBsdWdpbi5wcmltYXJ5T3V0cHV0RGlyZWN0b3J5O1xuICBjb25zdCBlbnN1cmVPdXRwdXREaXIgPSAob3V0cHV0RGlyICYmICFvdXRwdXREaXIuaW5jbHVkZXMoJyonKSlcbiAgICA/IFtgbWtkaXIgLXAgXCIke291dHB1dERpcn1cIiAmJiB0b3VjaCBcIiR7b3V0cHV0RGlyfS8uZ2l0a2VlcFwiYF1cbiAgICA6IFtdO1xuXG4gIGNvbnN0IHsgaW5zdGFsbENvbW1hbmRzLCBjb21tYW5kcyB9ID0gYnVpbGRDb21tYW5kcyhwbHVnaW4sIHtcbiAgICBwcmVJbnN0YWxsQ29tbWFuZHM6IFsuLi5lbnN1cmVPdXRwdXREaXIsIC4uLihwcmVJbnN0YWxsQ29tbWFuZHMgPz8gW10pXSxcbiAgICBwb3N0SW5zdGFsbENvbW1hbmRzLFxuICAgIHByZUNvbW1hbmRzLFxuICAgIHBvc3RDb21tYW5kcyxcbiAgfSwgZmFpbHVyZUJlaGF2aW9yKTtcblxuICBjb25zdCBwcm9ncmFtbWF0aWMgPSB7IGlucHV0LCBpbnN0YWxsQ29tbWFuZHMsIGNvbW1hbmRzIH07XG5cbiAgLy8gUmVzb2x2ZSB0aGUgcGx1Z2luJ3MgcnVudGltZSBpbWFnZS4gQ29kZUJ1aWxkIGRlZmF1bHRzIHRvXG4gIC8vIGBhd3MvY29kZWJ1aWxkL3N0YW5kYXJkOjcuMGAgd2hlbiBubyBgYnVpbGRJbWFnZWAgaXMgc2V0IOKAlCB0aGF0J3MgdGhlXG4gIC8vIEFXUy1tYW5hZ2VkIFVidW50dSBpbWFnZSBhbmQgRE9FUyBOT1QgaGF2ZSBhbnkgcGx1Z2luLWJha2VkIHRvb2xzXG4gIC8vIChwaXBlbGluZS1tYW5hZ2VyLCBzbnlrLCB0ZXJyYWZvcm0sIGV0Yy4pLiBXaXJpbmcgdGhlIHBsdWdpbidzIGltYWdlXG4gIC8vIGhlcmUgbWVhbnMgQ29kZUJ1aWxkIHB1bGxzIGZyb20gb3VyIHByaXZhdGUgcmVnaXN0cnkgYW5kIHRoZSB0b29sc1xuICAvLyBpbnN0YWxsZWQgaW4gdGhlIHBsdWdpbidzIERvY2tlcmZpbGUgYXJlIGFjdHVhbGx5IGF2YWlsYWJsZS5cbiAgY29uc3QgcGx1Z2luQnVpbGRJbWFnZSA9IHJlc29sdmVQbHVnaW5JbWFnZShzY29wZSwgcGx1Z2luLCBvcmdJZCk7XG5cbiAgLy8gU2hlbGxTdGVwIGJyYW5jaC5cbiAgLy8gU2hlbGxTdGVwIGl0c2VsZiBkb2Vzbid0IGFjY2VwdCBgYnVpbGRFbnZpcm9ubWVudGAvYGJ1aWxkSW1hZ2VgLiBDREtcbiAgLy8gcGlwZWxpbmVzIHdyYXBzIFNoZWxsU3RlcHMgaW4gYSBkZWZhdWx0IENvZGVCdWlsZCBhY3Rpb24gdGhhdCBydW5zIG9uXG4gIC8vIGBhd3MvY29kZWJ1aWxkL3N0YW5kYXJkOjcuMGAg4oCUIHdoaWNoIHdvbid0IGhhdmUgdGhlIHBsdWdpbidzIHRvb2xzLlxuICAvL1xuICAvLyBTbyB3aGVuIGEgU0hFTExfU1RFUCBwbHVnaW4gRE9FUyBoYXZlIGFuIGltYWdlIChiZWNhdXNlIGl0cyBhdXRob3JcbiAgLy8gYmFrZWQgdG9vbHMgaW50byBhIERvY2tlcmZpbGUpLCB3ZSBQUk9NT1RFIGl0IHRvIGEgQ29kZUJ1aWxkU3RlcCB3aXRoXG4gIC8vIHRoZSByZXNvbHZlZCBpbWFnZS4gVGhlIHBsdWdpbiBhdXRob3IncyBpbnRlbnQgKFwidXNlIG15IGJha2VkIHRvb2xzXCIpXG4gIC8vIGlzIHByZXNlcnZlZCB3aXRob3V0IGZvcmNpbmcgdGhlbSB0byBjaGFuZ2UgcGx1Z2luVHlwZS5cbiAgLy9cbiAgLy8gV2hlbiB0aGUgcGx1Z2luIGhhcyBubyBpbWFnZSAob3IgcmVnaXN0cnkgaXNuJ3QgY29uZmlndXJlZCksIHdlIGtlZXBcbiAgLy8gdGhlIG9yaWdpbmFsIFNoZWxsU3RlcCDigJQgaXQncyBsaWdodGVyIHdlaWdodCBhbmQgdGhlIGRlZmF1bHRcbiAgLy8gQ29kZUJ1aWxkIGltYWdlIGlzIGZpbmUuXG4gIGlmIChwbHVnaW4ucGx1Z2luVHlwZSA9PT0gUGx1Z2luVHlwZS5TSEVMTF9TVEVQKSB7XG4gICAgaWYgKCFwbHVnaW5CdWlsZEltYWdlKSB7XG4gICAgICByZXR1cm4gbmV3IFNoZWxsU3RlcChpZCwge1xuICAgICAgICAuLi5wcm9ncmFtbWF0aWMsXG4gICAgICAgIGVudjogeyAuLi5lbnYsIC4uLmFjdGlvbkVudiB9LFxuICAgICAgICAuLi5tZXRhZGF0YUZvclNoZWxsU3RlcChtZXJnZWQpLFxuICAgICAgfSk7XG4gICAgfVxuICAgIGxvZy5kZWJ1ZyhgW0NyZWF0ZUNvZGVCdWlsZFN0ZXBdIFNIRUxMX1NURVAgcGx1Z2luIFwiJHtwbHVnaW4ubmFtZX1cIiBoYXMgYSByZWdpc3RyeSBpbWFnZSDigJQgcHJvbW90aW5nIHRvIENvZGVCdWlsZFN0ZXAgc28gdGhlIHBsdWdpbiBpbWFnZSBpcyBhY3R1YWxseSB1c2VkYCk7XG4gICAgLy8gRmFsbCB0aHJvdWdoIHRvIHRoZSBDb2RlQnVpbGRTdGVwIHBhdGggYmVsb3cuXG4gIH1cblxuICBjb25zdCBjb21wdXRlVHlwZSA9IGdldENvbXB1dGVUeXBlKFxuICAgIHBsdWdpbi5jb21wdXRlVHlwZSA/PyBvcHRpb25zLmRlZmF1bHRDb21wdXRlVHlwZSA/PyAnU01BTEwnLFxuICApO1xuXG4gIC8vIFN0ZXAgbmV0d29yazogZXhwbGljaXQgc3RlcCBjb25maWcgd2lucywgZWxzZSB0aGlzIHN0ZXAncyBgZWMyOm5ldHdvcmtgIG1ldGFkYXRhLlxuICBjb25zdCByZXNvbHZlZE5ldHdvcmsgPSBuZXR3b3JrID8/IG5ldHdvcmtDb25maWdGcm9tTWV0YWRhdGEobWVyZ2VkKTtcbiAgY29uc3QgbmV0d29ya1Byb3BzID0gcmVzb2x2ZWROZXR3b3JrXG4gICAgPyByZXNvbHZlTmV0d29yayhzY29wZSwgb3B0aW9ucy51bmlxdWVJZCwgcmVzb2x2ZWROZXR3b3JrKVxuICAgIDoge307XG5cbiAgLy8gTWV0YWRhdGEgc3ByZWFkIGxhc3Qgc28gaXQgY2FuIG92ZXJyaWRlIHByb2dyYW1tYXRpYyBkZWZhdWx0cy5cbiAgLy8gTk9URTogQ2FjaGluZyBpcyBzdXBwb3J0ZWQgdmlhIHR3byBtZXRhZGF0YSBwYXRoczpcbiAgLy8gICAxLiBNZXRhZGF0YUtleXMuQ0FDSEUgKCdhd3M6Y2RrOnBpcGVsaW5lczpjb2RlYnVpbGRzdGVwOmNhY2hlJykg4oCUIHBhc3NlZCBkaXJlY3RseVxuICAvLyAgICAgIGFzIHRoZSBDb2RlQnVpbGRTdGVwIGBjYWNoZWAgcHJvcCAoZXhwZWN0cyBhIGNvZGVidWlsZC5DYWNoZSBvYmplY3QpLlxuICAvLyAgIDIuIE1ldGFkYXRhS2V5cy5QQVJUSUFMX0JVSUxEX1NQRUMgKCdhd3M6Y2RrOnBpcGVsaW5lczpjb2RlYnVpbGRzdGVwOnBhcnRpYWxidWlsZHNwZWMnKVxuICAvLyAgICAgIOKAlCBwYXNzZWQgYXMgYHBhcnRpYWxCdWlsZFNwZWNgLCB3aGljaCBjYW4gaW5jbHVkZSBhIGBjYWNoZTpgIHNlY3Rpb24gZm9yXG4gIC8vICAgICAgUzMgb3IgbG9jYWwgY2FjaGluZyAoZS5nLiwgQnVpbGRTcGVjLmZyb21PYmplY3QoeyBjYWNoZTogeyBwYXRoczogWy4uLl0gfSB9KSkuXG4gIC8vIFBlci1zdGVwIGJ1aWxkRW52aXJvbm1lbnQgb3ZlcnJpZGVzIGZyb20gbWV0YWRhdGEgKGF3czpjZGs6Y29kZWJ1aWxkOmJ1aWxkZW52aXJvbm1lbnQ6KikuXG4gIC8vIEEgbWV0YWRhdGEtcHJvdmlkZWQgY29tcHV0ZVR5cGUgaXMgYSByYXcgcGFzc3Rocm91Z2gsIHNvIG5vcm1hbGl6ZSBpdCB0aHJvdWdoXG4gIC8vIGdldENvbXB1dGVUeXBlIOKAlCBvdGhlcndpc2UgYSBmcmllbmRseSB2YWx1ZSBsaWtlIFwiTEFSR0VcIiByZWFjaGVzIENvZGVCdWlsZCB2ZXJiYXRpbVxuICAvLyBhbmQgZmFpbHMgd2l0aCBcIkludmFsaWQgY29tcHV0ZSB0eXBlIHByb3ZpZGVkXCIgKGl0IG5lZWRzIEJVSUxEX0dFTkVSQUwxX0xBUkdFKS5cbiAgY29uc3QgcmF3TWV0YUJ1aWxkRW52ID0gbWV0YWRhdGFGb3JCdWlsZEVudmlyb25tZW50KG1lcmdlZCk7XG4gIGNvbnN0IG1ldGFCdWlsZEVudiA9IHR5cGVvZiByYXdNZXRhQnVpbGRFbnYuY29tcHV0ZVR5cGUgPT09ICdzdHJpbmcnXG4gICAgPyB7IC4uLnJhd01ldGFCdWlsZEVudiwgY29tcHV0ZVR5cGU6IGdldENvbXB1dGVUeXBlKHJhd01ldGFCdWlsZEVudi5jb21wdXRlVHlwZSkgfVxuICAgIDogcmF3TWV0YUJ1aWxkRW52O1xuXG4gIGNvbnN0IHN0ZXAgPSBuZXcgQ29kZUJ1aWxkU3RlcChpZCwge1xuICAgIC4uLnByb2dyYW1tYXRpYyxcbiAgICAuLi5uZXR3b3JrUHJvcHMsXG4gICAgLi4uKGFkZGl0aW9uYWxJbnB1dHMgJiYgeyBhZGRpdGlvbmFsSW5wdXRzIH0pLFxuICAgIC4uLih0aW1lb3V0ICYmIHsgdGltZW91dDogRHVyYXRpb24ubWludXRlcyh0aW1lb3V0KSB9KSxcbiAgICBlbnY6IGFjdGlvbkVudixcbiAgICBwcmltYXJ5T3V0cHV0RGlyZWN0b3J5OiBwbHVnaW4ucHJpbWFyeU91dHB1dERpcmVjdG9yeSA/PyB1bmRlZmluZWQsXG4gICAgLy8gUGluIHRoZSBidWlsZHNwZWMgc2hlbGwgdG8gUE9TSVggL2Jpbi9zaCBzbyBleGVjdXRpb24gaXMgaWRlbnRpY2FsIHRvXG4gICAgLy8gQ29kZUJ1aWxkJ3MgZGVmYXVsdCBhbmQgcG9ydGFibGUgYWNyb3NzIG1pbmltYWwgYmFzZSBpbWFnZXMgKG5vIGJhc2hcbiAgICAvLyBkZXBlbmRlbmN5KS4gUGx1Z2luIHNwZWNzIGFyZSBhdXRob3JlZCBQT1NJWC1jbGVhbiB0byBtYXRjaCDigJQgbm8gYmFzaC1vbmx5XG4gICAgLy8gYFtbIF1dYCwgYXJyYXlzLCBgcmVhZCAtcmEgPDw8YCwgb3IgYHNvdXJjZWAvYG52bSB1c2VgL2BzZGsgdXNlYC4gQVxuICAgIC8vIHBsdWdpbi1zdXBwbGllZCBwYXJ0aWFsQnVpbGRTcGVjIChub25lIHRvZGF5KSB3b3VsZCBvdmVycmlkZSB0aGlzIHZpYSB0aGVcbiAgICAvLyBtZXRhZGF0YSBzcHJlYWQgYmVsb3cgYW5kIG11c3QgdGhlbiBzZXQgYGVudi5zaGVsbGAgaXRzZWxmLlxuICAgIHBhcnRpYWxCdWlsZFNwZWM6IEJ1aWxkU3BlYy5mcm9tT2JqZWN0KHsgZW52OiB7IHNoZWxsOiAnL2Jpbi9zaCcgfSB9KSxcbiAgICBidWlsZEVudmlyb25tZW50OiB7XG4gICAgICBjb21wdXRlVHlwZSxcbiAgICAgIGJ1aWxkSW1hZ2U6IHBsdWdpbkJ1aWxkSW1hZ2UgPz8gcmVzb2x2ZURlZmF1bHRCdWlsZEltYWdlKHNjb3BlLCBvcmdJZCksXG4gICAgICBlbnZpcm9ubWVudFZhcmlhYmxlczoge1xuICAgICAgICAuLi50b0NvZGVCdWlsZEVudlZhcnMoZW52KSxcbiAgICAgICAgLi4uc2VjcmV0RW52VmFycyxcbiAgICAgIH0sXG4gICAgICAuLi5tZXRhQnVpbGRFbnYsXG4gICAgfSxcbiAgICAuLi5tZXRhZGF0YUZvckNvZGVCdWlsZFN0ZXAobWVyZ2VkKSxcbiAgfSk7XG5cbiAgLy8gUmVnaXN0ZXIgd2l0aCBhcnRpZmFjdCBtYW5hZ2VyIGlmIHByaW1hcnlPdXRwdXREaXJlY3RvcnkgaXMgc2V0XG4gIGlmIChwbHVnaW4ucHJpbWFyeU91dHB1dERpcmVjdG9yeSAmJiBhcnRpZmFjdE1hbmFnZXIgJiYgc3RhZ2VOYW1lKSB7XG4gICAgY29uc3QgYXJ0aWZhY3RLZXk6IEFydGlmYWN0S2V5ID0ge1xuICAgICAgc3RhZ2VOYW1lLFxuICAgICAgc3RhZ2VBbGlhczogc3RhZ2VBbGlhcyA/PyBgJHtzdGFnZU5hbWV9LWFsaWFzYCxcbiAgICAgIHBsdWdpbk5hbWU6IHBsdWdpbi5uYW1lLFxuICAgICAgcGx1Z2luQWxpYXM6IHBsdWdpbkFsaWFzID8/IGAke3BsdWdpbi5uYW1lfS1hbGlhc2AsXG4gICAgICBvdXRwdXREaXJlY3Rvcnk6IHBsdWdpbi5wcmltYXJ5T3V0cHV0RGlyZWN0b3J5LFxuICAgIH07XG4gICAgYXJ0aWZhY3RNYW5hZ2VyLmFkZChhcnRpZmFjdEtleSwgc3RlcCk7XG4gIH1cblxuICByZXR1cm4gc3RlcDtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IHN0cmluZyBvciBDb21wdXRlVHlwZSBlbnVtIHRvIENESyBDb21wdXRlVHlwZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29tcHV0ZVR5cGUoaW5wdXQ6IHN0cmluZyB8IENES0NvbXB1dGVUeXBlID0gJ1NNQUxMJyk6IENES0NvbXB1dGVUeXBlIHtcbiAgLy8gSWYgYWxyZWFkeSBhIENESyBDb21wdXRlVHlwZSwgcmV0dXJuIGFzLWlzXG4gIGlmICh0eXBlb2YgaW5wdXQgIT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIGlucHV0O1xuICB9XG5cbiAgLy8gQWxyZWFkeSBhIG5hdGl2ZSBDb2RlQnVpbGQgQ29tcHV0ZVR5cGUgKGUuZy4gXCJCVUlMRF9HRU5FUkFMMV9MQVJHRVwiKT8gQWNjZXB0IGFzLWlzXG4gIC8vIHNvIGJvdGggZnJpZW5kbHkgKExBUkdFKSBhbmQgbmF0aXZlIHZhbHVlcyByb3VuZC10cmlwIHdpdGhvdXQgZmFsbGluZyBiYWNrIHRvIFNNQUxMLlxuICBpZiAoKE9iamVjdC52YWx1ZXMoQ0RLQ29tcHV0ZVR5cGUpIGFzIHN0cmluZ1tdKS5pbmNsdWRlcyhpbnB1dCkpIHtcbiAgICByZXR1cm4gaW5wdXQgYXMgQ0RLQ29tcHV0ZVR5cGU7XG4gIH1cblxuICBjb25zdCBub3JtYWxpemVkID0gaW5wdXQudG9VcHBlckNhc2UoKSBhcyBDb21wdXRlVHlwZTtcblxuICBjb25zdCBtYXBwaW5nOiBSZWNvcmQ8Q29tcHV0ZVR5cGUsIENES0NvbXB1dGVUeXBlPiA9IHtcbiAgICBbQ29tcHV0ZVR5cGUuU01BTExdOiBDREtDb21wdXRlVHlwZS5TTUFMTCxcbiAgICBbQ29tcHV0ZVR5cGUuTUVESVVNXTogQ0RLQ29tcHV0ZVR5cGUuTUVESVVNLFxuICAgIFtDb21wdXRlVHlwZS5MQVJHRV06IENES0NvbXB1dGVUeXBlLkxBUkdFLFxuICAgIFtDb21wdXRlVHlwZS5YMl9MQVJHRV06IENES0NvbXB1dGVUeXBlLlgyX0xBUkdFLFxuICB9O1xuXG4gIGNvbnN0IHJlc3VsdCA9IG1hcHBpbmdbbm9ybWFsaXplZF07XG4gIGlmICghcmVzdWx0KSB7XG4gICAgbG9nLndhcm4oYFVua25vd24gY29tcHV0ZSB0eXBlIFwiJHtpbnB1dH1cIiwgZmFsbGluZyBiYWNrIHRvIFNNQUxMYCk7XG4gICAgcmV0dXJuIENES0NvbXB1dGVUeXBlLlNNQUxMO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogVW53cmFwIGEgU2VjcmV0VmFsdWUgfCBzdHJpbmcgaW50byBhIHBsYWluIHN0cmluZy5cbiAqIFdoZW4gYSBTZWNyZXRWYWx1ZSBpcyBwcm92aWRlZCAoZS5nLiBmcm9tIFNlY3JldHMgTWFuYWdlciksIGNhbGxzIHVuc2FmZVVud3JhcCgpXG4gKiB0byBleHRyYWN0IHRoZSB1bmRlcmx5aW5nIHZhbHVlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdW53cmFwU2VjcmV0KHZhbHVlOiBTZWNyZXRWYWx1ZSB8IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gdmFsdWUgOiB2YWx1ZS51bnNhZmVVbndyYXAoKTtcbn1cbiJdfQ==
|