@cloudsnorkel/cdk-github-runners 0.14.24 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.jsii +5400 -255
  2. package/API.md +1048 -24
  3. package/README.md +52 -0
  4. package/assets/delete-failed-runner.lambda/index.js +105 -9
  5. package/assets/idle-runner-repear.lambda/index.js +136 -14
  6. package/assets/image-builders/aws-image-builder/delete-resources.lambda/index.js +1 -1
  7. package/assets/image-builders/build-image.lambda/index.js +1 -1
  8. package/assets/providers/ami-root-device.lambda/index.js +1 -1
  9. package/assets/setup.lambda/index.html +7 -7
  10. package/assets/setup.lambda/index.js +101 -8
  11. package/assets/status.lambda/index.js +104 -8
  12. package/assets/token-retriever.lambda/index.js +104 -8
  13. package/assets/warm-runner-manager.lambda/index.js +5892 -0
  14. package/assets/webhook-handler.lambda/index.js +109 -11
  15. package/assets/webhook-redelivery.lambda/index.js +122 -24
  16. package/lib/access.js +1 -1
  17. package/lib/delete-failed-runner.lambda.js +2 -2
  18. package/lib/idle-runner-repear.lambda.js +33 -7
  19. package/lib/image-builders/api.js +1 -1
  20. package/lib/image-builders/aws-image-builder/base-image.d.ts +13 -0
  21. package/lib/image-builders/aws-image-builder/base-image.js +36 -3
  22. package/lib/image-builders/aws-image-builder/builder.js +4 -4
  23. package/lib/image-builders/aws-image-builder/delete-resources.lambda.js +2 -2
  24. package/lib/image-builders/aws-image-builder/deprecated/ami.js +1 -1
  25. package/lib/image-builders/aws-image-builder/deprecated/container.js +1 -1
  26. package/lib/image-builders/aws-image-builder/deprecated/linux-components.js +1 -1
  27. package/lib/image-builders/aws-image-builder/deprecated/windows-components.js +1 -1
  28. package/lib/image-builders/build-image.lambda.js +2 -2
  29. package/lib/image-builders/codebuild-deprecated.js +1 -1
  30. package/lib/image-builders/components.js +3 -3
  31. package/lib/image-builders/static.js +1 -1
  32. package/lib/index.d.ts +1 -0
  33. package/lib/index.js +2 -1
  34. package/lib/lambda-github.d.ts +1 -1
  35. package/lib/lambda-github.js +3 -2
  36. package/lib/lambda-helpers.js +4 -4
  37. package/lib/providers/ami-root-device.lambda.js +2 -2
  38. package/lib/providers/codebuild.d.ts +16 -0
  39. package/lib/providers/codebuild.js +15 -4
  40. package/lib/providers/common.js +3 -3
  41. package/lib/providers/composite.js +1 -1
  42. package/lib/providers/ec2.d.ts +5 -0
  43. package/lib/providers/ec2.js +31 -17
  44. package/lib/providers/ecs.d.ts +17 -0
  45. package/lib/providers/ecs.js +43 -38
  46. package/lib/providers/fargate.js +9 -31
  47. package/lib/providers/lambda.js +2 -2
  48. package/lib/runner.d.ts +25 -2
  49. package/lib/runner.js +119 -17
  50. package/lib/secrets.js +1 -1
  51. package/lib/setup.lambda.js +2 -2
  52. package/lib/utils.d.ts +10 -1
  53. package/lib/utils.js +15 -1
  54. package/lib/warm-runner-manager-function.d.ts +18 -0
  55. package/lib/warm-runner-manager-function.js +24 -0
  56. package/lib/warm-runner-manager.lambda.d.ts +41 -0
  57. package/lib/warm-runner-manager.lambda.js +487 -0
  58. package/lib/warm-runner.d.ts +147 -0
  59. package/lib/warm-runner.js +210 -0
  60. package/lib/webhook-handler.lambda.js +5 -3
  61. package/lib/webhook-redelivery.lambda.js +17 -16
  62. package/lib/webhook.d.ts +4 -0
  63. package/lib/webhook.js +2 -1
  64. package/node_modules/cron-parser/LICENSE +21 -0
  65. package/node_modules/cron-parser/README.md +408 -0
  66. package/node_modules/cron-parser/dist/CronDate.js +518 -0
  67. package/node_modules/cron-parser/dist/CronExpression.js +520 -0
  68. package/node_modules/cron-parser/dist/CronExpressionParser.js +382 -0
  69. package/node_modules/cron-parser/dist/CronFieldCollection.js +371 -0
  70. package/node_modules/cron-parser/dist/CronFileParser.js +109 -0
  71. package/node_modules/cron-parser/dist/fields/CronDayOfMonth.js +44 -0
  72. package/node_modules/cron-parser/dist/fields/CronDayOfWeek.js +51 -0
  73. package/node_modules/cron-parser/dist/fields/CronField.js +214 -0
  74. package/node_modules/cron-parser/dist/fields/CronHour.js +40 -0
  75. package/node_modules/cron-parser/dist/fields/CronMinute.js +40 -0
  76. package/node_modules/cron-parser/dist/fields/CronMonth.js +44 -0
  77. package/node_modules/cron-parser/dist/fields/CronSecond.js +40 -0
  78. package/node_modules/cron-parser/dist/fields/index.js +24 -0
  79. package/node_modules/cron-parser/dist/fields/types.js +2 -0
  80. package/node_modules/cron-parser/dist/index.js +31 -0
  81. package/node_modules/cron-parser/dist/types/CronDate.d.ts +288 -0
  82. package/node_modules/cron-parser/dist/types/CronExpression.d.ts +118 -0
  83. package/node_modules/cron-parser/dist/types/CronExpressionParser.d.ts +70 -0
  84. package/node_modules/cron-parser/dist/types/CronFieldCollection.d.ts +153 -0
  85. package/node_modules/cron-parser/dist/types/CronFileParser.d.ts +30 -0
  86. package/node_modules/cron-parser/dist/types/fields/CronDayOfMonth.d.ts +25 -0
  87. package/node_modules/cron-parser/dist/types/fields/CronDayOfWeek.d.ts +30 -0
  88. package/node_modules/cron-parser/dist/types/fields/CronField.d.ts +130 -0
  89. package/node_modules/cron-parser/dist/types/fields/CronHour.d.ts +23 -0
  90. package/node_modules/cron-parser/dist/types/fields/CronMinute.d.ts +23 -0
  91. package/node_modules/cron-parser/dist/types/fields/CronMonth.d.ts +24 -0
  92. package/node_modules/cron-parser/dist/types/fields/CronSecond.d.ts +23 -0
  93. package/node_modules/cron-parser/dist/types/fields/index.d.ts +8 -0
  94. package/node_modules/cron-parser/dist/types/fields/types.d.ts +18 -0
  95. package/node_modules/cron-parser/dist/types/index.d.ts +8 -0
  96. package/node_modules/cron-parser/dist/types/utils/random.d.ts +10 -0
  97. package/node_modules/cron-parser/dist/utils/random.js +38 -0
  98. package/node_modules/cron-parser/package.json +117 -0
  99. package/node_modules/luxon/LICENSE.md +7 -0
  100. package/node_modules/luxon/README.md +55 -0
  101. package/node_modules/luxon/build/amd/luxon.js +8741 -0
  102. package/node_modules/luxon/build/amd/luxon.js.map +1 -0
  103. package/node_modules/luxon/build/cjs-browser/luxon.js +8739 -0
  104. package/node_modules/luxon/build/cjs-browser/luxon.js.map +1 -0
  105. package/node_modules/luxon/build/es6/luxon.mjs +8133 -0
  106. package/node_modules/luxon/build/es6/luxon.mjs.map +1 -0
  107. package/node_modules/luxon/build/global/luxon.js +8744 -0
  108. package/node_modules/luxon/build/global/luxon.js.map +1 -0
  109. package/node_modules/luxon/build/global/luxon.min.js +1 -0
  110. package/node_modules/luxon/build/global/luxon.min.js.map +1 -0
  111. package/node_modules/luxon/build/node/luxon.js +7792 -0
  112. package/node_modules/luxon/build/node/luxon.js.map +1 -0
  113. package/node_modules/luxon/package.json +87 -0
  114. package/node_modules/luxon/src/datetime.js +2603 -0
  115. package/node_modules/luxon/src/duration.js +1009 -0
  116. package/node_modules/luxon/src/errors.js +61 -0
  117. package/node_modules/luxon/src/impl/conversions.js +206 -0
  118. package/node_modules/luxon/src/impl/diff.js +95 -0
  119. package/node_modules/luxon/src/impl/digits.js +94 -0
  120. package/node_modules/luxon/src/impl/english.js +233 -0
  121. package/node_modules/luxon/src/impl/formats.js +176 -0
  122. package/node_modules/luxon/src/impl/formatter.js +434 -0
  123. package/node_modules/luxon/src/impl/invalid.js +14 -0
  124. package/node_modules/luxon/src/impl/locale.js +569 -0
  125. package/node_modules/luxon/src/impl/regexParser.js +335 -0
  126. package/node_modules/luxon/src/impl/tokenParser.js +505 -0
  127. package/node_modules/luxon/src/impl/util.js +330 -0
  128. package/node_modules/luxon/src/impl/zoneUtil.js +34 -0
  129. package/node_modules/luxon/src/info.js +205 -0
  130. package/node_modules/luxon/src/interval.js +669 -0
  131. package/node_modules/luxon/src/luxon.js +26 -0
  132. package/node_modules/luxon/src/package.json +4 -0
  133. package/node_modules/luxon/src/settings.js +180 -0
  134. package/node_modules/luxon/src/zone.js +97 -0
  135. package/node_modules/luxon/src/zones/IANAZone.js +235 -0
  136. package/node_modules/luxon/src/zones/fixedOffsetZone.js +150 -0
  137. package/node_modules/luxon/src/zones/invalidZone.js +53 -0
  138. package/node_modules/luxon/src/zones/systemZone.js +61 -0
  139. package/package.json +33 -24
@@ -0,0 +1,487 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handler = handler;
4
+ /**
5
+ * Warm Runner Manager Lambda
6
+ *
7
+ * Maintains a pool of pre-provisioned ("warm") GitHub self-hosted runners so jobs
8
+ * start with near-zero latency instead of waiting for a fresh runner to provision.
9
+ *
10
+ * ## Lifecycle
11
+ *
12
+ * 1. **Fill** — An EventBridge cron rule (midnight UTC for always-on, or window
13
+ * start for scheduled) sends a fill payload to the shared SQS queue. For
14
+ * AlwaysOnWarmRunner only, a CloudFormation custom resource (on deploy)
15
+ * invokes this Lambda directly to fill immediately; the deadline is set to
16
+ * the next midnight UTC (not full 24h) so runners last until the next cron
17
+ * fill. ScheduledWarmRunner has no deployment-fill — first fill is at the
18
+ * next schedule occurrence.
19
+ *
20
+ * 2. **Keeper** — Each keeper message tracks one runner. The SQS queue delivers the
21
+ * message, this Lambda inspects the runner, and one of the following happens:
22
+ * - Runner is past its deadline → the keeper stops the Step Function and deletes
23
+ * the runner. No replacement is created.
24
+ * - Runner is idle and within deadline → message is returned to the queue
25
+ * (via batch item failure) to be checked again after the visibility timeout.
26
+ * - Runner is busy or its Step Function finished → a replacement runner is
27
+ * started with the same deadline, and a new keeper message is enqueued.
28
+ * - Config hash mismatch (config was changed/removed since this runner was
29
+ * created) → the runner is stopped and deleted. No replacement is created.
30
+ * This is how old runners are cleaned up quickly on config changes.
31
+ *
32
+ * 3. **Shutdown mechanisms** — The keeper is the primary mechanism for enforcing the
33
+ * absolute deadline: when a runner is past its deadline, the keeper stops the
34
+ * Step Function and deletes the runner. Fallbacks:
35
+ * - **Idle reaper**: Measures idle time from runner *registration* (cdkghr:started
36
+ * label), not step function start. So it fires at deadline + provisioning delay.
37
+ * It's a fallback if the keeper misses a message; it does not enforce the
38
+ * absolute deadline precisely.
39
+ * - **Step Function idle timeout**: Each runner is started with `maxIdleSeconds`
40
+ * matching the deadline. If both keeper and idle reaper miss it, the Step
41
+ * Function will self-terminate when its idle timeout fires.
42
+ *
43
+ * ## Config hash
44
+ *
45
+ * Each warm runner config (provider, count, labels, owner, repo, duration) is
46
+ * hashed at CDK synth time. All current hashes are stored in the WARM_CONFIG_HASHES
47
+ * environment variable. Fill payloads and keeper messages carry the hash. When the
48
+ * keeper processes a message whose hash is not in the current set, it knows the
49
+ * config was changed or removed and stops the runner immediately. This helps quickly
50
+ * get rid of stale runners while keeping over-provisioning to a minimum.
51
+ *
52
+ * ## Gotchas
53
+ *
54
+ * - Keeper messages rely on SQS redelivery (batch item failure) for periodic
55
+ * checking. The visibility timeout (1 min) determines how often runners are
56
+ * polled. Failed messages are retried until they succeed or the runner
57
+ * self-terminates at its idle timeout.
58
+ * - Each fill unconditionally starts `count` runners — it does not check how many
59
+ * are already running. On cron fire, this creates a brief overlap with the
60
+ * previous cycle's runners (which are near their deadline).
61
+ * - Removing all warm runners configurations may result in warm runners staying
62
+ * around until they expire. To remove all warm runners quickly, set count to 0
63
+ * and deploy. Only once all the warm runners are stopped, you can remove all
64
+ * configurations and deploy again.
65
+ * - **Gaps in coverage**: The Step Function that provisions each warm runner uses
66
+ * increasing timeouts between retries for provider failures (CodeBuild timeout,
67
+ * Lambda timeout, capacity errors, etc.). While a warm runner slot is retrying,
68
+ * that slot has no runner. This may create gaps in coverage. An idle warm runner
69
+ * that fails to provision (or whose replacement fails) will be unavailable until
70
+ * the retry succeeds. Current retry mechanism has built-in back-off rate and can
71
+ * be tweaked using `retryOptions`. This will be improved in the future.
72
+ */
73
+ const crypto = require("crypto");
74
+ const client_sfn_1 = require("@aws-sdk/client-sfn");
75
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
76
+ const lambda_github_1 = require("./lambda-github");
77
+ const lambda_helpers_1 = require("./lambda-helpers");
78
+ const sfn = new client_sfn_1.SFNClient();
79
+ const sqs = new client_sqs_1.SQSClient();
80
+ const SFN_EXECUTION_NAME_MAX_LENGTH = 80;
81
+ function isSqsEvent(event) {
82
+ return Array.isArray(event.Records);
83
+ }
84
+ function isFillInput(event) {
85
+ return typeof event === 'object' && event !== null && event.action === 'fill';
86
+ }
87
+ function isCustomResourceEvent(event) {
88
+ const e = event;
89
+ return typeof e?.RequestType === 'string' && typeof e?.ResponseURL === 'string';
90
+ }
91
+ function requireEnv(name) {
92
+ const value = process.env[name];
93
+ if (!value) {
94
+ throw new Error(`Missing environment variable ${name}`);
95
+ }
96
+ return value;
97
+ }
98
+ /**
99
+ * Deterministic execution name for idempotent fills. Same seed → same name, so retries
100
+ * (custom resource, SQS redelivery) don't create duplicate runners.
101
+ */
102
+ function deterministicExecutionName(providerPath, seed) {
103
+ const pathWithoutStack = providerPath.split('/').slice(1).join('/') || providerPath;
104
+ const sanitized = `warm-${pathWithoutStack.replace(/[^a-zA-Z0-9-]/g, '-')}`;
105
+ const hash = crypto.createHash('sha256').update(seed).digest('hex').slice(0, 16);
106
+ const maxPrefixLen = SFN_EXECUTION_NAME_MAX_LENGTH - hash.length - 1;
107
+ return `${sanitized.slice(0, maxPrefixLen)}-${hash}`;
108
+ }
109
+ /** Returns Unix ms of the next midnight UTC. Used for AlwaysOn deployment-fill deadline. */
110
+ function getNextMidnightUtcMs() {
111
+ const now = new Date();
112
+ const nextMidnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0, 0));
113
+ return nextMidnight.getTime();
114
+ }
115
+ /** Find installation id for our app. Normal code path gets this from the webhook payload, but we schedule these ourselves. */
116
+ async function resolveInstallationId(owner, repo) {
117
+ const appOctokit = await (0, lambda_github_1.getAppOctokit)();
118
+ if (!appOctokit) {
119
+ return undefined; // PAT authentication
120
+ }
121
+ if (repo) {
122
+ const { data } = await appOctokit.rest.apps.getRepoInstallation({ owner, repo });
123
+ return data.id;
124
+ }
125
+ else {
126
+ const { data } = await appOctokit.rest.apps.getOrgInstallation({ org: owner });
127
+ return data.id;
128
+ }
129
+ }
130
+ /** Start a warm runner and enqueue a keeper message using SQS. */
131
+ async function startWarmRunnerAndEnqueueKeeper(input) {
132
+ const stepFunctionArn = requireEnv('STEP_FUNCTION_ARN');
133
+ const queueUrl = requireEnv('WARM_RUNNER_QUEUE_URL');
134
+ const remainingSeconds = Math.floor((input.absoluteDeadline - Date.now()) / 1000);
135
+ if (remainingSeconds <= 0) {
136
+ console.log({
137
+ notice: 'Absolute deadline already passed; not starting replacement',
138
+ configHash: input.configHash,
139
+ runnerName: input.executionName,
140
+ input,
141
+ });
142
+ return;
143
+ }
144
+ let executionArn;
145
+ try {
146
+ const result = await sfn.send(new client_sfn_1.StartExecutionCommand({
147
+ stateMachineArn: stepFunctionArn,
148
+ name: input.executionName,
149
+ input: JSON.stringify({
150
+ owner: input.owner,
151
+ repo: input.repo || '',
152
+ jobId: -1,
153
+ jobUrl: '',
154
+ installationId: input.installationId ?? -1,
155
+ jobLabels: input.providerLabels.join(','),
156
+ provider: input.providerPath,
157
+ labels: [...input.providerLabels, 'cdkghr:warm'].join(','),
158
+ maxIdleSeconds: remainingSeconds,
159
+ }),
160
+ }));
161
+ executionArn = result.executionArn;
162
+ }
163
+ catch (e) {
164
+ if (e instanceof client_sfn_1.ExecutionAlreadyExists) {
165
+ // Idempotent retry: execution was already started (e.g. Lambda timed out mid-fill).
166
+ // Don't enqueue. The first attempt already did, so duplicate keeper messages would cause duplicate replacements.
167
+ console.log({
168
+ notice: 'ExecutionAlreadyExists — idempotent retry, skipping enqueue',
169
+ configHash: input.configHash,
170
+ slot: input.slot,
171
+ runnerName: input.executionName,
172
+ });
173
+ return;
174
+ }
175
+ else {
176
+ throw e;
177
+ }
178
+ }
179
+ const message = {
180
+ executionArn,
181
+ runnerName: input.executionName,
182
+ owner: input.owner,
183
+ repo: input.repo,
184
+ installationId: input.installationId,
185
+ providerPath: input.providerPath,
186
+ providerLabels: input.providerLabels,
187
+ absoluteDeadline: input.absoluteDeadline,
188
+ configHash: input.configHash,
189
+ };
190
+ await sqs.send(new client_sqs_1.SendMessageCommand({
191
+ QueueUrl: queueUrl,
192
+ MessageBody: JSON.stringify(message),
193
+ }));
194
+ console.log({
195
+ notice: 'Started warm runner and enqueued keeper message',
196
+ configHash: input.configHash,
197
+ slot: input.slot,
198
+ runnerName: input.executionName,
199
+ executionArn,
200
+ remainingSeconds,
201
+ });
202
+ }
203
+ /**
204
+ * Unconditionally starts `count` warm runners for the given config and enqueues keeper messages.
205
+ *
206
+ * @param input the fill payload
207
+ * @param getNameForSlot a function to generate a unique and stable execution name for each slot
208
+ * @param source the source of the fill for debugging purposes
209
+ * @param absoluteDeadlineOverride if provided, use this instead of now + duration (for AlwaysOn deployment-fill)
210
+ */
211
+ async function runFiller(input, getNameForSlot, source, absoluteDeadlineOverride) {
212
+ const installationId = await resolveInstallationId(input.owner, input.repo);
213
+ const absoluteDeadline = absoluteDeadlineOverride ?? Date.now() + input.duration * 1000;
214
+ for (let i = 0; i < input.count; i++) {
215
+ await startWarmRunnerAndEnqueueKeeper({
216
+ providerPath: input.providerPath,
217
+ providerLabels: input.providerLabels,
218
+ owner: input.owner,
219
+ repo: input.repo,
220
+ installationId,
221
+ absoluteDeadline,
222
+ configHash: input.configHash,
223
+ executionName: getNameForSlot(i),
224
+ slot: i,
225
+ });
226
+ }
227
+ console.log({
228
+ notice: 'Fill complete - started warm runners',
229
+ source,
230
+ configHash: input.configHash,
231
+ providerPath: input.providerPath,
232
+ started: input.count,
233
+ });
234
+ }
235
+ /** Stop the step function and delete the runner from GitHub. */
236
+ async function stopAndDeleteRunner(input, octokit, secrets, reason) {
237
+ try {
238
+ await sfn.send(new client_sfn_1.StopExecutionCommand({
239
+ executionArn: input.executionArn,
240
+ error: reason,
241
+ cause: 'Warm runner stopped by keeper',
242
+ }));
243
+ }
244
+ catch (e) {
245
+ console.error({
246
+ notice: 'Failed to stop step function',
247
+ configHash: input.configHash,
248
+ runnerName: input.runnerName,
249
+ executionArn: input.executionArn,
250
+ error: e,
251
+ input,
252
+ });
253
+ }
254
+ const runner = await (0, lambda_github_1.getRunner)(octokit, secrets.runnerLevel, input.owner, input.repo, input.runnerName);
255
+ if (runner) {
256
+ try {
257
+ await (0, lambda_github_1.deleteRunner)(octokit, secrets.runnerLevel, input.owner, input.repo, runner.id);
258
+ }
259
+ catch (e) {
260
+ console.error({
261
+ notice: 'Failed to delete runner',
262
+ configHash: input.configHash,
263
+ runnerName: input.runnerName,
264
+ runnerId: runner.id,
265
+ error: e,
266
+ input,
267
+ });
268
+ }
269
+ }
270
+ }
271
+ /**
272
+ * Warm runner manager Lambda - handles three invocation modes:
273
+ *
274
+ * 1. CloudFormation Custom Resource - triggered on stack deploy (Create/Update) for AlwaysOnWarmRunner only. Runs
275
+ * runFiller with deadline = next midnight UTC so runners last until the next cron fill. Delete is a no-op.
276
+ * 2. SQS messages - fill or keeper
277
+ * - Fill - from EventBridge cron. Uses messageId for deterministic execution names (idempotent on redelivery).
278
+ * - Keeper - tracks one runner. Uses SQS message cycling for periodic checks.
279
+ * Each message tracks one warm runner. The keeper checks:
280
+ * - Past deadline - stop the Step Function and delete the runner.
281
+ * - Config hash - if the message's `configHash` doesn't match the current `WARM_CONFIG_HASHES` env var, the runner is from a stale config - stop it and discard the message without replacement.
282
+ * - Busy/finished - if the Step Function ended or the GitHub runner is busy (took a job), start a replacement runner (inheriting the same deadline and config hash).
283
+ * - Not found yet (runner/infrastructure still starting) - retry later (message goes back to queue).
284
+ * - Still idle - retry later to check again.
285
+ */
286
+ async function handler(event) {
287
+ if (isCustomResourceEvent(event)) {
288
+ const physicalId = ('PhysicalResourceId' in event ? event.PhysicalResourceId : undefined) ?? event.LogicalResourceId;
289
+ try {
290
+ const props = event.ResourceProperties;
291
+ console.log({
292
+ notice: 'Custom resource fill',
293
+ requestType: event.RequestType,
294
+ logicalResourceId: event.LogicalResourceId,
295
+ configHash: props.configHash,
296
+ providerPath: props.providerPath,
297
+ count: props.count,
298
+ });
299
+ if (event.RequestType === 'Create' || event.RequestType === 'Update') {
300
+ const getNameForSlot = (slot) => deterministicExecutionName(props.providerPath, `${event.LogicalResourceId}:${event.RequestType}:${props.configHash}:${slot}`);
301
+ const deadline = getNextMidnightUtcMs();
302
+ await runFiller(props, getNameForSlot, 'customResource', deadline);
303
+ }
304
+ await (0, lambda_helpers_1.customResourceRespond)(event, 'SUCCESS', 'OK', physicalId, {});
305
+ }
306
+ catch (e) {
307
+ console.error({ notice: 'Custom resource handler failed', error: e });
308
+ await (0, lambda_helpers_1.customResourceRespond)(event, 'FAILED', e.message || 'Internal Error', physicalId, {});
309
+ }
310
+ return;
311
+ }
312
+ if (!isSqsEvent(event)) {
313
+ console.error({ notice: 'Unknown event type; ignoring', event });
314
+ return;
315
+ }
316
+ const validHashes = new Set((process.env.WARM_CONFIG_HASHES ?? '').split(',').filter(Boolean));
317
+ const result = { batchItemFailures: [] };
318
+ const octokitCache = new Map();
319
+ for (const record of event.Records) {
320
+ let body;
321
+ try {
322
+ body = JSON.parse(record.body);
323
+ }
324
+ catch (e) {
325
+ console.error({
326
+ notice: 'Failed to parse message body',
327
+ requestId: record.messageId,
328
+ error: e,
329
+ });
330
+ continue;
331
+ }
332
+ const retryLater = () => result.batchItemFailures.push({ itemIdentifier: record.messageId });
333
+ const isFill = isFillInput(body);
334
+ const configHash = body.configHash;
335
+ const runnerName = isFill ? undefined : body.runnerName;
336
+ console.log({
337
+ notice: 'Processing SQS message',
338
+ messageId: record.messageId,
339
+ configHash,
340
+ runnerName,
341
+ });
342
+ // scheduled fill from EventBridge via SQS
343
+ if (isFill) {
344
+ const fillPayload = body;
345
+ try {
346
+ console.log({
347
+ notice: 'Scheduled fill',
348
+ configHash,
349
+ providerPath: fillPayload.providerPath,
350
+ count: fillPayload.count,
351
+ });
352
+ const getNameForSlot = (slot) => deterministicExecutionName(fillPayload.providerPath, `${record.messageId}:${slot}`);
353
+ await runFiller(fillPayload, getNameForSlot, 'scheduled', undefined);
354
+ }
355
+ catch (e) {
356
+ console.error({
357
+ notice: 'Fill failed',
358
+ messageId: record.messageId,
359
+ configHash: fillPayload.configHash,
360
+ error: e,
361
+ });
362
+ retryLater();
363
+ }
364
+ continue;
365
+ }
366
+ // keeper message
367
+ const input = body;
368
+ console.log({
369
+ notice: 'Checking warm runner',
370
+ configHash: input.configHash,
371
+ runnerName: input.runnerName,
372
+ });
373
+ // get github access (cached per installationId to avoid re-reading the secrets manager and Github API every time)
374
+ let octokit;
375
+ let secrets;
376
+ const cached = octokitCache.get(input.installationId);
377
+ if (cached) {
378
+ octokit = cached.octokit;
379
+ secrets = cached.secrets;
380
+ }
381
+ else {
382
+ const got = await (0, lambda_github_1.getOctokit)(input.installationId);
383
+ octokit = got.octokit;
384
+ secrets = got.githubSecrets;
385
+ octokitCache.set(input.installationId, { octokit, secrets });
386
+ }
387
+ // stale config - best-effort stop, then discard message (runner will self-terminate at its idle timeout)
388
+ if (!validHashes.has(input.configHash)) {
389
+ console.log({
390
+ notice: 'Config hash mismatch (new CDK deployment, old runner) - stopping stale warm runner',
391
+ configHash: input.configHash,
392
+ runnerName: input.runnerName,
393
+ validHashes: validHashes,
394
+ });
395
+ try {
396
+ await stopAndDeleteRunner(input, octokit, secrets, 'StaleWarmRunner');
397
+ }
398
+ catch (e) {
399
+ console.error({
400
+ notice: 'Best-effort cleanup of stale warm runner failed; it will self-terminate at idle timeout',
401
+ configHash: input.configHash,
402
+ runnerName: input.runnerName,
403
+ error: e,
404
+ });
405
+ }
406
+ continue;
407
+ }
408
+ // past deadline - keeper must stop and delete the runner
409
+ if (Date.now() >= input.absoluteDeadline) {
410
+ console.log({
411
+ notice: 'Warm runner past deadline, stopping and deleting',
412
+ configHash: input.configHash,
413
+ runnerName: input.runnerName,
414
+ });
415
+ try {
416
+ await stopAndDeleteRunner(input, octokit, secrets, 'WarmRunnerExpired');
417
+ }
418
+ catch (e) {
419
+ console.error({
420
+ notice: 'Failed to stop expired warm runner',
421
+ configHash: input.configHash,
422
+ runnerName: input.runnerName,
423
+ error: e,
424
+ });
425
+ // don't retry to not accidentally create a new runner
426
+ // idle reaper will take care of it soon enough
427
+ }
428
+ continue;
429
+ }
430
+ // check if step function is still running
431
+ const execution = await sfn.send(new client_sfn_1.DescribeExecutionCommand({ executionArn: input.executionArn }));
432
+ const stillRunning = execution.status === 'RUNNING';
433
+ // find runner
434
+ const runner = await (0, lambda_github_1.getRunner)(octokit, secrets.runnerLevel, input.owner, input.repo, input.runnerName);
435
+ // need replacement: step function finished (not running) or runner took a job (busy)
436
+ if (!stillRunning || runner?.busy) {
437
+ console.log({
438
+ notice: 'Warm runner finished or busy; starting replacement',
439
+ configHash: input.configHash,
440
+ runnerName: input.runnerName,
441
+ stillRunning,
442
+ runnerBusy: runner?.busy ?? false,
443
+ });
444
+ try {
445
+ await startWarmRunnerAndEnqueueKeeper({
446
+ providerPath: input.providerPath,
447
+ providerLabels: input.providerLabels,
448
+ owner: input.owner,
449
+ repo: input.repo,
450
+ installationId: input.installationId,
451
+ absoluteDeadline: input.absoluteDeadline,
452
+ configHash: input.configHash,
453
+ executionName: deterministicExecutionName(input.providerPath, record.messageId),
454
+ });
455
+ }
456
+ catch (e) {
457
+ console.error({
458
+ notice: 'Failed to start replacement warm runner',
459
+ configHash: input.configHash,
460
+ runnerName: input.runnerName,
461
+ error: e,
462
+ });
463
+ retryLater();
464
+ }
465
+ continue;
466
+ }
467
+ // step function still running but runner not found yet
468
+ if (!runner) {
469
+ console.log({
470
+ notice: 'Runner not running yet',
471
+ configHash: input.configHash,
472
+ runnerName: input.runnerName,
473
+ });
474
+ retryLater();
475
+ continue;
476
+ }
477
+ // still idle - check again later
478
+ console.log({
479
+ notice: 'Runner still idle - will check again later',
480
+ configHash: input.configHash,
481
+ runnerName: input.runnerName,
482
+ });
483
+ retryLater();
484
+ }
485
+ return result;
486
+ }
487
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2FybS1ydW5uZXItbWFuYWdlci5sYW1iZGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2FybS1ydW5uZXItbWFuYWdlci5sYW1iZGEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUF5VkEsMEJBb05DO0FBN2lCRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvRUc7QUFDSCxpQ0FBaUM7QUFDakMsb0RBTTZCO0FBQzdCLG9EQUFvRTtBQUdwRSxtREFBb0c7QUFDcEcscURBQXlEO0FBRXpELE1BQU0sR0FBRyxHQUFHLElBQUksc0JBQVMsRUFBRSxDQUFDO0FBQzVCLE1BQU0sR0FBRyxHQUFHLElBQUksc0JBQVMsRUFBRSxDQUFDO0FBRTVCLE1BQU0sNkJBQTZCLEdBQUcsRUFBRSxDQUFDO0FBNEJ6QyxTQUFTLFVBQVUsQ0FBQyxLQUFjO0lBQ2hDLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBRSxLQUE0QixDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQzlELENBQUM7QUFFRCxTQUFTLFdBQVcsQ0FBQyxLQUFjO0lBQ2pDLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUssS0FBK0IsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDO0FBQzNHLENBQUM7QUFFRCxTQUFTLHFCQUFxQixDQUFDLEtBQWM7SUFDM0MsTUFBTSxDQUFDLEdBQUcsS0FBb0QsQ0FBQztJQUMvRCxPQUFPLE9BQU8sQ0FBQyxFQUFFLFdBQVcsS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLEVBQUUsV0FBVyxLQUFLLFFBQVEsQ0FBQztBQUNsRixDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsSUFBWTtJQUM5QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztR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QUFVO29CQUM1QixhQUFhLEVBQUUsMEJBQTBCLENBQUMsS0FBSyxDQUFDLFlBQVksRUFBRSxNQUFNLENBQUMsU0FBUyxDQUFDO2lCQUNoRixDQUFDLENBQUM7WUFDTCxDQUFDO1lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDWCxPQUFPLENBQUMsS0FBSyxDQUFDO29CQUNaLE1BQU0sRUFBRSx5Q0FBeUM7b0JBQ2pELFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVTtvQkFDNUIsVUFBVSxFQUFFLEtBQUssQ0FBQyxVQUFVO29CQUM1QixLQUFLLEVBQUUsQ0FBQztpQkFDVCxDQUFDLENBQUM7Z0JBQ0gsVUFBVSxFQUFFLENBQUM7WUFDZixDQUFDO1lBQ0QsU0FBUztRQUNYLENBQUM7UUFFRCx1REFBdUQ7UUFDdkQsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ1osT0FBTyxDQUFDLEdBQUcsQ0FBQztnQkFDVixNQUFNLEVBQUUsd0JBQXdCO2dCQUNoQyxVQUFVLEVBQUUsS0FBSyxDQUFDLFVBQVU7Z0JBQzVCLFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVTthQUM3QixDQUFDLENBQUM7WUFDSCxVQUFVLEVBQUUsQ0FBQztZQUNiLFNBQVM7UUFDWCxDQUFDO1FBRUQsaUNBQWlDO1FBQ2pDLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDVixNQUFNLEVBQUUsNENBQTRDO1lBQ3BELFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVTtZQUM1QixVQUFVLEVBQUUsS0FBSyxDQUFDLFVBQVU7U0FDN0IsQ0FBQyxDQUFDO1FBQ0gsVUFBVSxFQUFFLENBQUM7SUFDZixDQUFDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogV2FybSBSdW5uZXIgTWFuYWdlciBMYW1iZGFcbiAqXG4gKiBNYWludGFpbnMgYSBwb29sIG9mIHByZS1wcm92aXNpb25lZCAoXCJ3YXJtXCIpIEdpdEh1YiBzZWxmLWhvc3RlZCBydW5uZXJzIHNvIGpvYnNcbiAqIHN0YXJ0IHdpdGggbmVhci16ZXJvIGxhdGVuY3kgaW5zdGVhZCBvZiB3YWl0aW5nIGZvciBhIGZyZXNoIHJ1bm5lciB0byBwcm92aXNpb24uXG4gKlxuICogIyMgTGlmZWN5Y2xlXG4gKlxuICogMS4gKipGaWxsKiog4oCUIEFuIEV2ZW50QnJpZGdlIGNyb24gcnVsZSAobWlkbmlnaHQgVVRDIGZvciBhbHdheXMtb24sIG9yIHdpbmRvd1xuICogICAgc3RhcnQgZm9yIHNjaGVkdWxlZCkgc2VuZHMgYSBmaWxsIHBheWxvYWQgdG8gdGhlIHNoYXJlZCBTUVMgcXVldWUuIEZvclxuICogICAgQWx3YXlzT25XYXJtUnVubmVyIG9ubHksIGEgQ2xvdWRGb3JtYXRpb24gY3VzdG9tIHJlc291cmNlIChvbiBkZXBsb3kpXG4gKiAgICBpbnZva2VzIHRoaXMgTGFtYmRhIGRpcmVjdGx5IHRvIGZpbGwgaW1tZWRpYXRlbHk7IHRoZSBkZWFkbGluZSBpcyBzZXQgdG9cbiAqICAgIHRoZSBuZXh0IG1pZG5pZ2h0IFVUQyAobm90IGZ1bGwgMjRoKSBzbyBydW5uZXJzIGxhc3QgdW50aWwgdGhlIG5leHQgY3JvblxuICogICAgZmlsbC4gU2NoZWR1bGVkV2FybVJ1bm5lciBoYXMgbm8gZGVwbG95bWVudC1maWxsIOKAlCBmaXJzdCBmaWxsIGlzIGF0IHRoZVxuICogICAgbmV4dCBzY2hlZHVsZSBvY2N1cnJlbmNlLlxuICpcbiAqIDIuICoqS2VlcGVyKiog4oCUIEVhY2gga2VlcGVyIG1lc3NhZ2UgdHJhY2tzIG9uZSBydW5uZXIuIFRoZSBTUVMgcXVldWUgZGVsaXZlcnMgdGhlXG4gKiAgICBtZXNzYWdlLCB0aGlzIExhbWJkYSBpbnNwZWN0cyB0aGUgcnVubmVyLCBhbmQgb25lIG9mIHRoZSBmb2xsb3dpbmcgaGFwcGVuczpcbiAqICAgIC0gUnVubmVyIGlzIHBhc3QgaXRzIGRlYWRsaW5lIOKGkiB0aGUga2VlcGVyIHN0b3BzIHRoZSBTdGVwIEZ1bmN0aW9uIGFuZCBkZWxldGVzXG4gKiAgICAgIHRoZSBydW5uZXIuIE5vIHJlcGxhY2VtZW50IGlzIGNyZWF0ZWQuXG4gKiAgICAtIFJ1bm5lciBpcyBpZGxlIGFuZCB3aXRoaW4gZGVhZGxpbmUg4oaSIG1lc3NhZ2UgaXMgcmV0dXJuZWQgdG8gdGhlIHF1ZXVlXG4gKiAgICAgICh2aWEgYmF0Y2ggaXRlbSBmYWlsdXJlKSB0byBiZSBjaGVja2VkIGFnYWluIGFmdGVyIHRoZSB2aXNpYmlsaXR5IHRpbWVvdXQuXG4gKiAgICAtIFJ1bm5lciBpcyBidXN5IG9yIGl0cyBTdGVwIEZ1bmN0aW9uIGZpbmlzaGVkIOKGkiBhIHJlcGxhY2VtZW50IHJ1bm5lciBpc1xuICogICAgICBzdGFydGVkIHdpdGggdGhlIHNhbWUgZGVhZGxpbmUsIGFuZCBhIG5ldyBrZWVwZXIgbWVzc2FnZSBpcyBlbnF1ZXVlZC5cbiAqICAgIC0gQ29uZmlnIGhhc2ggbWlzbWF0Y2ggKGNvbmZpZyB3YXMgY2hhbmdlZC9yZW1vdmVkIHNpbmNlIHRoaXMgcnVubmVyIHdhc1xuICogICAgICBjcmVhdGVkKSDihpIgdGhlIHJ1bm5lciBpcyBzdG9wcGVkIGFuZCBkZWxldGVkLiBObyByZXBsYWNlbWVudCBpcyBjcmVhdGVkLlxuICogICAgICBUaGlzIGlzIGhvdyBvbGQgcnVubmVycyBhcmUgY2xlYW5lZCB1cCBxdWlja2x5IG9uIGNvbmZpZyBjaGFuZ2VzLlxuICpcbiAqIDMuICoqU2h1dGRvd24gbWVjaGFuaXNtcyoqIOKAlCBUaGUga2VlcGVyIGlzIHRoZSBwcmltYXJ5IG1lY2hhbmlzbSBmb3IgZW5mb3JjaW5nIHRoZVxuICogICAgYWJzb2x1dGUgZGVhZGxpbmU6IHdoZW4gYSBydW5uZXIgaXMgcGFzdCBpdHMgZGVhZGxpbmUsIHRoZSBrZWVwZXIgc3RvcHMgdGhlXG4gKiAgICBTdGVwIEZ1bmN0aW9uIGFuZCBkZWxldGVzIHRoZSBydW5uZXIuIEZhbGxiYWNrczpcbiAqICAgIC0gKipJZGxlIHJlYXBlcioqOiBNZWFzdXJlcyBpZGxlIHRpbWUgZnJvbSBydW5uZXIgKnJlZ2lzdHJhdGlvbiogKGNka2docjpzdGFydGVkXG4gKiAgICAgIGxhYmVsKSwgbm90IHN0ZXAgZnVuY3Rpb24gc3RhcnQuIFNvIGl0IGZpcmVzIGF0IGRlYWRsaW5lICsgcHJvdmlzaW9uaW5nIGRlbGF5LlxuICogICAgICBJdCdzIGEgZmFsbGJhY2sgaWYgdGhlIGtlZXBlciBtaXNzZXMgYSBtZXNzYWdlOyBpdCBkb2VzIG5vdCBlbmZvcmNlIHRoZVxuICogICAgICBhYnNvbHV0ZSBkZWFkbGluZSBwcmVjaXNlbHkuXG4gKiAgICAtICoqU3RlcCBGdW5jdGlvbiBpZGxlIHRpbWVvdXQqKjogRWFjaCBydW5uZXIgaXMgc3RhcnRlZCB3aXRoIGBtYXhJZGxlU2Vjb25kc2BcbiAqICAgICAgbWF0Y2hpbmcgdGhlIGRlYWRsaW5lLiBJZiBib3RoIGtlZXBlciBhbmQgaWRsZSByZWFwZXIgbWlzcyBpdCwgdGhlIFN0ZXBcbiAqICAgICAgRnVuY3Rpb24gd2lsbCBzZWxmLXRlcm1pbmF0ZSB3aGVuIGl0cyBpZGxlIHRpbWVvdXQgZmlyZXMuXG4gKlxuICogIyMgQ29uZmlnIGhhc2hcbiAqXG4gKiBFYWNoIHdhcm0gcnVubmVyIGNvbmZpZyAocHJvdmlkZXIsIGNvdW50LCBsYWJlbHMsIG93bmVyLCByZXBvLCBkdXJhdGlvbikgaXNcbiAqIGhhc2hlZCBhdCBDREsgc3ludGggdGltZS4gQWxsIGN1cnJlbnQgaGFzaGVzIGFyZSBzdG9yZWQgaW4gdGhlIFdBUk1fQ09ORklHX0hBU0hFU1xuICogZW52aXJvbm1lbnQgdmFyaWFibGUuIEZpbGwgcGF5bG9hZHMgYW5kIGtlZXBlciBtZXNzYWdlcyBjYXJyeSB0aGUgaGFzaC4gV2hlbiB0aGVcbiAqIGtlZXBlciBwcm9jZXNzZXMgYSBtZXNzYWdlIHdob3NlIGhhc2ggaXMgbm90IGluIHRoZSBjdXJyZW50IHNldCwgaXQga25vd3MgdGhlXG4gKiBjb25maWcgd2FzIGNoYW5nZWQgb3IgcmVtb3ZlZCBhbmQgc3RvcHMgdGhlIHJ1bm5lciBpbW1lZGlhdGVseS4gVGhpcyBoZWxwcyBxdWlja2x5XG4gKiBnZXQgcmlkIG9mIHN0YWxlIHJ1bm5lcnMgd2hpbGUga2VlcGluZyBvdmVyLXByb3Zpc2lvbmluZyB0byBhIG1pbmltdW0uXG4gKlxuICogIyMgR290Y2hhc1xuICpcbiAqIC0gS2VlcGVyIG1lc3NhZ2VzIHJlbHkgb24gU1FTIHJlZGVsaXZlcnkgKGJhdGNoIGl0ZW0gZmFpbHVyZSkgZm9yIHBlcmlvZGljXG4gKiAgIGNoZWNraW5nLiBUaGUgdmlzaWJpbGl0eSB0aW1lb3V0ICgxIG1pbikgZGV0ZXJtaW5lcyBob3cgb2Z0ZW4gcnVubmVycyBhcmVcbiAqICAgcG9sbGVkLiBGYWlsZWQgbWVzc2FnZXMgYXJlIHJldHJpZWQgdW50aWwgdGhleSBzdWNjZWVkIG9yIHRoZSBydW5uZXJcbiAqICAgc2VsZi10ZXJtaW5hdGVzIGF0IGl0cyBpZGxlIHRpbWVvdXQuXG4gKiAtIEVhY2ggZmlsbCB1bmNvbmRpdGlvbmFsbHkgc3RhcnRzIGBjb3VudGAgcnVubmVycyDigJQgaXQgZG9lcyBub3QgY2hlY2sgaG93IG1hbnlcbiAqICAgYXJlIGFscmVhZHkgcnVubmluZy4gT24gY3JvbiBmaXJlLCB0aGlzIGNyZWF0ZXMgYSBicmllZiBvdmVybGFwIHdpdGggdGhlXG4gKiAgIHByZXZpb3VzIGN5Y2xlJ3MgcnVubmVycyAod2hpY2ggYXJlIG5lYXIgdGhlaXIgZGVhZGxpbmUpLlxuICogLSBSZW1vdmluZyBhbGwgd2FybSBydW5uZXJzIGNvbmZpZ3VyYXRpb25zIG1heSByZXN1bHQgaW4gd2FybSBydW5uZXJzIHN0YXlpbmdcbiAqICAgYXJvdW5kIHVudGlsIHRoZXkgZXhwaXJlLiBUbyByZW1vdmUgYWxsIHdhcm0gcnVubmVycyBxdWlja2x5LCBzZXQgY291bnQgdG8gMFxuICogICBhbmQgZGVwbG95LiBPbmx5IG9uY2UgYWxsIHRoZSB3YXJtIHJ1bm5lcnMgYXJlIHN0b3BwZWQsIHlvdSBjYW4gcmVtb3ZlIGFsbFxuICogICBjb25maWd1cmF0aW9ucyBhbmQgZGVwbG95IGFnYWluLlxuICogLSAqKkdhcHMgaW4gY292ZXJhZ2UqKjogVGhlIFN0ZXAgRnVuY3Rpb24gdGhhdCBwcm92aXNpb25zIGVhY2ggd2FybSBydW5uZXIgdXNlc1xuICogICBpbmNyZWFzaW5nIHRpbWVvdXRzIGJldHdlZW4gcmV0cmllcyBmb3IgcHJvdmlkZXIgZmFpbHVyZXMgKENvZGVCdWlsZCB0aW1lb3V0LFxuICogICBMYW1iZGEgdGltZW91dCwgY2FwYWNpdHkgZXJyb3JzLCBldGMuKS4gV2hpbGUgYSB3YXJtIHJ1bm5lciBzbG90IGlzIHJldHJ5aW5nLFxuICogICB0aGF0IHNsb3QgaGFzIG5vIHJ1bm5lci4gVGhpcyBtYXkgY3JlYXRlIGdhcHMgaW4gY292ZXJhZ2UuIEFuIGlkbGUgd2FybSBydW5uZXJcbiAqICAgdGhhdCBmYWlscyB0byBwcm92aXNpb24gKG9yIHdob3NlIHJlcGxhY2VtZW50IGZhaWxzKSB3aWxsIGJlIHVuYXZhaWxhYmxlIHVudGlsXG4gKiAgIHRoZSByZXRyeSBzdWNjZWVkcy4gQ3VycmVudCByZXRyeSBtZWNoYW5pc20gaGFzIGJ1aWx0LWluIGJhY2stb2ZmIHJhdGUgYW5kIGNhblxuICogICBiZSB0d2Vha2VkIHVzaW5nIGByZXRyeU9wdGlvbnNgLiBUaGlzIHdpbGwgYmUgaW1wcm92ZWQgaW4gdGhlIGZ1dHVyZS5cbiAqL1xuaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQge1xuICBEZXNjcmliZUV4ZWN1dGlvbkNvbW1hbmQsXG4gIFNGTkNsaWVudCxcbiAgU3RhcnRFeGVjdXRpb25Db21tYW5kLFxuICBTdG9wRXhlY3V0aW9uQ29tbWFuZCxcbiAgRXhlY3V0aW9uQWxyZWFkeUV4aXN0cyxcbn0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXNmbic7XG5pbXBvcnQgeyBTUVNDbGllbnQsIFNlbmRNZXNzYWdlQ29tbWFuZCB9IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1zcXMnO1xuaW1wb3J0IHR5cGUgeyBPY3Rva2l0IH0gZnJvbSAnQG9jdG9raXQvcmVzdCc7XG5pbXBvcnQgKiBhcyBBV1NMYW1iZGEgZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBkZWxldGVSdW5uZXIsIGdldEFwcE9jdG9raXQsIGdldE9jdG9raXQsIGdldFJ1bm5lciwgR2l0SHViU2VjcmV0cyB9IGZyb20gJy4vbGFtYmRhLWdpdGh1Yic7XG5pbXBvcnQgeyBjdXN0b21SZXNvdXJjZVJlc3BvbmQgfSBmcm9tICcuL2xhbWJkYS1oZWxwZXJzJztcblxuY29uc3Qgc2ZuID0gbmV3IFNGTkNsaWVudCgpO1xuY29uc3Qgc3FzID0gbmV3IFNRU0NsaWVudCgpO1xuXG5jb25zdCBTRk5fRVhFQ1VUSU9OX05BTUVfTUFYX0xFTkdUSCA9IDgwO1xuXG5leHBvcnQgaW50ZXJmYWNlIFdhcm1SdW5uZXJLZWVwZXJNZXNzYWdlIHtcbiAgcmVhZG9ubHkgZXhlY3V0aW9uQXJuOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHJ1bm5lck5hbWU6IHN0cmluZztcbiAgcmVhZG9ubHkgb3duZXI6IHN0cmluZztcbiAgcmVhZG9ubHkgcmVwbzogc3RyaW5nO1xuICByZWFkb25seSBpbnN0YWxsYXRpb25JZD86IG51bWJlcjtcbiAgcmVhZG9ubHkgcHJvdmlkZXJQYXRoOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHByb3ZpZGVyTGFiZWxzOiBzdHJpbmdbXTtcbiAgcmVhZG9ubHkgYWJzb2x1dGVEZWFkbGluZTogbnVtYmVyOyAvLyBVbml4IG1zIOKAlCBpbmhlcml0ZWQgYnkgcmVwbGFjZW1lbnRzXG4gIHJlYWRvbmx5IGNvbmZpZ0hhc2g6IHN0cmluZztcbn1cblxuLyoqXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXYXJtUnVubmVyRmlsbFBheWxvYWQge1xuICByZWFkb25seSBhY3Rpb246ICdmaWxsJztcbiAgcmVhZG9ubHkgcHJvdmlkZXJQYXRoOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHByb3ZpZGVyTGFiZWxzOiBzdHJpbmdbXTtcbiAgcmVhZG9ubHkgY291bnQ6IG51bWJlcjtcbiAgcmVhZG9ubHkgZHVyYXRpb246IG51bWJlcjtcbiAgcmVhZG9ubHkgb3duZXI6IHN0cmluZztcbiAgcmVhZG9ubHkgcmVwbzogc3RyaW5nO1xuICByZWFkb25seSBjb25maWdIYXNoOiBzdHJpbmc7XG59XG5cbmZ1bmN0aW9uIGlzU3FzRXZlbnQoZXZlbnQ6IHVua25vd24pOiBldmVudCBpcyBBV1NMYW1iZGEuU1FTRXZlbnQge1xuICByZXR1cm4gQXJyYXkuaXNBcnJheSgoZXZlbnQgYXMgQVdTTGFtYmRhLlNRU0V2ZW50KS5SZWNvcmRzKTtcbn1cblxuZnVuY3Rpb24gaXNGaWxsSW5wdXQoZXZlbnQ6IHVua25vd24pOiBldmVudCBpcyBXYXJtUnVubmVyRmlsbFBheWxvYWQge1xuICByZXR1cm4gdHlwZW9mIGV2ZW50ID09PSAnb2JqZWN0JyAmJiBldmVudCAhPT0gbnVsbCAmJiAoZXZlbnQgYXMgV2FybVJ1bm5lckZpbGxQYXlsb2FkKS5hY3Rpb24gPT09ICdmaWxsJztcbn1cblxuZnVuY3Rpb24gaXNDdXN0b21SZXNvdXJjZUV2ZW50KGV2ZW50OiB1bmtub3duKTogZXZlbnQgaXMgQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCB7XG4gIGNvbnN0IGUgPSBldmVudCBhcyBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50O1xuICByZXR1cm4gdHlwZW9mIGU/LlJlcXVlc3RUeXBlID09PSAnc3RyaW5nJyAmJiB0eXBlb2YgZT8uUmVzcG9uc2VVUkwgPT09ICdzdHJpbmcnO1xufVxuXG5mdW5jdGlvbiByZXF1aXJlRW52KG5hbWU6IHN0cmluZykge1xuICBjb25zdCB2YWx1ZSA9IHByb2Nlc3MuZW52W25hbWVdO1xuICBpZiAoIXZhbHVlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBNaXNzaW5nIGVudmlyb25tZW50IHZhcmlhYmxlICR7bmFtZX1gKTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59XG5cbmludGVyZmFjZSBTdGFydFdhcm1SdW5uZXJJbnB1dCB7XG4gIHJlYWRvbmx5IHByb3ZpZGVyUGF0aDogc3RyaW5nO1xuICByZWFkb25seSBwcm92aWRlckxhYmVsczogc3RyaW5nW107XG4gIHJlYWRvbmx5IG93bmVyOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHJlcG86IHN0cmluZztcbiAgcmVhZG9ubHkgaW5zdGFsbGF0aW9uSWQ/OiBudW1iZXI7XG4gIHJlYWRvbmx5IGFic29sdXRlRGVhZGxpbmU6IG51bWJlcjsgLy8gVW5peCBtc1xuICByZWFkb25seSBjb25maWdIYXNoOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGV4ZWN1dGlvbk5hbWU6IHN0cmluZztcbiAgcmVhZG9ubHkgc2xvdD86IG51bWJlcjsgLy8gMC1iYXNlZCBpbmRleCB3aGVuIGZpbGxpbmcgbXVsdGlwbGUgc2xvdHM7IGhlbHBzIGNvcnJlbGF0ZSBsb2dzXG59XG5cbi8qKlxuICogRGV0ZXJtaW5pc3RpYyBleGVjdXRpb24gbmFtZSBmb3IgaWRlbXBvdGVudCBmaWxscy4gU2FtZSBzZWVkIOKGkiBzYW1lIG5hbWUsIHNvIHJldHJpZXNcbiAqIChjdXN0b20gcmVzb3VyY2UsIFNRUyByZWRlbGl2ZXJ5KSBkb24ndCBjcmVhdGUgZHVwbGljYXRlIHJ1bm5lcnMuXG4gKi9cbmZ1bmN0aW9uIGRldGVybWluaXN0aWNFeGVjdXRpb25OYW1lKHByb3ZpZGVyUGF0aDogc3RyaW5nLCBzZWVkOiBzdHJpbmcpIHtcbiAgY29uc3QgcGF0aFdpdGhvdXRTdGFjayA9IHByb3ZpZGVyUGF0aC5zcGxpdCgnLycpLnNsaWNlKDEpLmpvaW4oJy8nKSB8fCBwcm92aWRlclBhdGg7XG4gIGNvbnN0IHNhbml0aXplZCA9IGB3YXJtLSR7cGF0aFdpdGhvdXRTdGFjay5yZXBsYWNlKC9bXmEtekEtWjAtOS1dL2csICctJyl9YDtcbiAgY29uc3QgaGFzaCA9IGNyeXB0by5jcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoc2VlZCkuZGlnZXN0KCdoZXgnKS5zbGljZSgwLCAxNik7XG4gIGNvbnN0IG1heFByZWZpeExlbiA9IFNGTl9FWEVDVVRJT05fTkFNRV9NQVhfTEVOR1RIIC0gaGFzaC5sZW5ndGggLSAxO1xuICByZXR1cm4gYCR7c2FuaXRpemVkLnNsaWNlKDAsIG1heFByZWZpeExlbil9LSR7aGFzaH1gO1xufVxuXG4vKiogUmV0dXJucyBVbml4IG1zIG9mIHRoZSBuZXh0IG1pZG5pZ2h0IFVUQy4gVXNlZCBmb3IgQWx3YXlzT24gZGVwbG95bWVudC1maWxsIGRlYWRsaW5lLiAqL1xuZnVuY3Rpb24gZ2V0TmV4dE1pZG5pZ2h0VXRjTXMoKTogbnVtYmVyIHtcbiAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcbiAgY29uc3QgbmV4dE1pZG5pZ2h0ID0gbmV3IERhdGUoRGF0ZS5VVEMobm93LmdldFVUQ0Z1bGxZZWFyKCksIG5vdy5nZXRVVENNb250aCgpLCBub3cuZ2V0VVRDRGF0ZSgpICsgMSwgMCwgMCwgMCwgMCkpO1xuICByZXR1cm4gbmV4dE1pZG5pZ2h0LmdldFRpbWUoKTtcbn1cblxuLyoqIEZpbmQgaW5zdGFsbGF0aW9uIGlkIGZvciBvdXIgYXBwLiBOb3JtYWwgY29kZSBwYXRoIGdldHMgdGhpcyBmcm9tIHRoZSB3ZWJob29rIHBheWxvYWQsIGJ1dCB3ZSBzY2hlZHVsZSB0aGVzZSBvdXJzZWx2ZXMuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlSW5zdGFsbGF0aW9uSWQob3duZXI6IHN0cmluZywgcmVwbzogc3RyaW5nKSB7XG4gIGNvbnN0IGFwcE9jdG9raXQgPSBhd2FpdCBnZXRBcHBPY3Rva2l0KCk7XG4gIGlmICghYXBwT2N0b2tpdCkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7IC8vIFBBVCBhdXRoZW50aWNhdGlvblxuICB9XG5cbiAgaWYgKHJlcG8pIHtcbiAgICBjb25zdCB7IGRhdGEgfSA9IGF3YWl0IGFwcE9jdG9raXQucmVzdC5hcHBzLmdldFJlcG9JbnN0YWxsYXRpb24oeyBvd25lciwgcmVwbyB9KTtcbiAgICByZXR1cm4gZGF0YS5pZDtcbiAgfSBlbHNlIHtcbiAgICBjb25zdCB7IGRhdGEgfSA9IGF3YWl0IGFwcE9jdG9raXQucmVzdC5hcHBzLmdldE9yZ0luc3RhbGxhdGlvbih7IG9yZzogb3duZXIgfSk7XG4gICAgcmV0dXJuIGRhdGEuaWQ7XG4gIH1cbn1cblxuLyoqIFN0YXJ0IGEgd2FybSBydW5uZXIgYW5kIGVucXVldWUgYSBrZWVwZXIgbWVzc2FnZSB1c2luZyBTUVMuICovXG5hc3luYyBmdW5jdGlvbiBzdGFydFdhcm1SdW5uZXJBbmRFbnF1ZXVlS2VlcGVyKGlucHV0OiBTdGFydFdhcm1SdW5uZXJJbnB1dCkge1xuICBjb25zdCBzdGVwRnVuY3Rpb25Bcm4gPSByZXF1aXJlRW52KCdTVEVQX0ZVTkNUSU9OX0FSTicpO1xuICBjb25zdCBxdWV1ZVVybCA9IHJlcXVpcmVFbnYoJ1dBUk1fUlVOTkVSX1FVRVVFX1VSTCcpO1xuXG4gIGNvbnN0IHJlbWFpbmluZ1NlY29uZHMgPSBNYXRoLmZsb29yKChpbnB1dC5hYnNvbHV0ZURlYWRsaW5lIC0gRGF0ZS5ub3coKSkgLyAxMDAwKTtcbiAgaWYgKHJlbWFpbmluZ1NlY29uZHMgPD0gMCkge1xuICAgIGNvbnNvbGUubG9nKHtcbiAgICAgIG5vdGljZTogJ0Fic29sdXRlIGRlYWRsaW5lIGFscmVhZHkgcGFzc2VkOyBub3Qgc3RhcnRpbmcgcmVwbGFjZW1lbnQnLFxuICAgICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICAgIHJ1bm5lck5hbWU6IGlucHV0LmV4ZWN1dGlvbk5hbWUsXG4gICAgICBpbnB1dCxcbiAgICB9KTtcbiAgICByZXR1cm47XG4gIH1cblxuICBsZXQgZXhlY3V0aW9uQXJuOiBzdHJpbmc7XG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgc2ZuLnNlbmQobmV3IFN0YXJ0RXhlY3V0aW9uQ29tbWFuZCh7XG4gICAgICBzdGF0ZU1hY2hpbmVBcm46IHN0ZXBGdW5jdGlvbkFybixcbiAgICAgIG5hbWU6IGlucHV0LmV4ZWN1dGlvbk5hbWUsXG4gICAgICBpbnB1dDogSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICBvd25lcjogaW5wdXQub3duZXIsXG4gICAgICAgIHJlcG86IGlucHV0LnJlcG8gfHwgJycsXG4gICAgICAgIGpvYklkOiAtMSxcbiAgICAgICAgam9iVXJsOiAnJyxcbiAgICAgICAgaW5zdGFsbGF0aW9uSWQ6IGlucHV0Lmluc3RhbGxhdGlvbklkID8/IC0xLFxuICAgICAgICBqb2JMYWJlbHM6IGlucHV0LnByb3ZpZGVyTGFiZWxzLmpvaW4oJywnKSxcbiAgICAgICAgcHJvdmlkZXI6IGlucHV0LnByb3ZpZGVyUGF0aCxcbiAgICAgICAgbGFiZWxzOiBbLi4uaW5wdXQucHJvdmlkZXJMYWJlbHMsICdjZGtnaHI6d2FybSddLmpvaW4oJywnKSxcbiAgICAgICAgbWF4SWRsZVNlY29uZHM6IHJlbWFpbmluZ1NlY29uZHMsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gICAgZXhlY3V0aW9uQXJuID0gcmVzdWx0LmV4ZWN1dGlvbkFybiE7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBpZiAoZSBpbnN0YW5jZW9mIEV4ZWN1dGlvbkFscmVhZHlFeGlzdHMpIHtcbiAgICAgIC8vIElkZW1wb3RlbnQgcmV0cnk6IGV4ZWN1dGlvbiB3YXMgYWxyZWFkeSBzdGFydGVkIChlLmcuIExhbWJkYSB0aW1lZCBvdXQgbWlkLWZpbGwpLlxuICAgICAgLy8gRG9uJ3QgZW5xdWV1ZS4gVGhlIGZpcnN0IGF0dGVtcHQgYWxyZWFkeSBkaWQsIHNvIGR1cGxpY2F0ZSBrZWVwZXIgbWVzc2FnZXMgd291bGQgY2F1c2UgZHVwbGljYXRlIHJlcGxhY2VtZW50cy5cbiAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgbm90aWNlOiAnRXhlY3V0aW9uQWxyZWFkeUV4aXN0cyDigJQgaWRlbXBvdGVudCByZXRyeSwgc2tpcHBpbmcgZW5xdWV1ZScsXG4gICAgICAgIGNvbmZpZ0hhc2g6IGlucHV0LmNvbmZpZ0hhc2gsXG4gICAgICAgIHNsb3Q6IGlucHV0LnNsb3QsXG4gICAgICAgIHJ1bm5lck5hbWU6IGlucHV0LmV4ZWN1dGlvbk5hbWUsXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgZTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBtZXNzYWdlOiBXYXJtUnVubmVyS2VlcGVyTWVzc2FnZSA9IHtcbiAgICBleGVjdXRpb25Bcm4sXG4gICAgcnVubmVyTmFtZTogaW5wdXQuZXhlY3V0aW9uTmFtZSxcbiAgICBvd25lcjogaW5wdXQub3duZXIsXG4gICAgcmVwbzogaW5wdXQucmVwbyxcbiAgICBpbnN0YWxsYXRpb25JZDogaW5wdXQuaW5zdGFsbGF0aW9uSWQsXG4gICAgcHJvdmlkZXJQYXRoOiBpbnB1dC5wcm92aWRlclBhdGgsXG4gICAgcHJvdmlkZXJMYWJlbHM6IGlucHV0LnByb3ZpZGVyTGFiZWxzLFxuICAgIGFic29sdXRlRGVhZGxpbmU6IGlucHV0LmFic29sdXRlRGVhZGxpbmUsXG4gICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgfTtcblxuICBhd2FpdCBzcXMuc2VuZChuZXcgU2VuZE1lc3NhZ2VDb21tYW5kKHtcbiAgICBRdWV1ZVVybDogcXVldWVVcmwsXG4gICAgTWVzc2FnZUJvZHk6IEpTT04uc3RyaW5naWZ5KG1lc3NhZ2UpLFxuICB9KSk7XG5cbiAgY29uc29sZS5sb2coe1xuICAgIG5vdGljZTogJ1N0YXJ0ZWQgd2FybSBydW5uZXIgYW5kIGVucXVldWVkIGtlZXBlciBtZXNzYWdlJyxcbiAgICBjb25maWdIYXNoOiBpbnB1dC5jb25maWdIYXNoLFxuICAgIHNsb3Q6IGlucHV0LnNsb3QsXG4gICAgcnVubmVyTmFtZTogaW5wdXQuZXhlY3V0aW9uTmFtZSxcbiAgICBleGVjdXRpb25Bcm4sXG4gICAgcmVtYWluaW5nU2Vjb25kcyxcbiAgfSk7XG59XG5cbi8qKlxuICogVW5jb25kaXRpb25hbGx5IHN0YXJ0cyBgY291bnRgIHdhcm0gcnVubmVycyBmb3IgdGhlIGdpdmVuIGNvbmZpZyBhbmQgZW5xdWV1ZXMga2VlcGVyIG1lc3NhZ2VzLlxuICpcbiAqIEBwYXJhbSBpbnB1dCB0aGUgZmlsbCBwYXlsb2FkXG4gKiBAcGFyYW0gZ2V0TmFtZUZvclNsb3QgYSBmdW5jdGlvbiB0byBnZW5lcmF0ZSBhIHVuaXF1ZSBhbmQgc3RhYmxlIGV4ZWN1dGlvbiBuYW1lIGZvciBlYWNoIHNsb3RcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvZiB0aGUgZmlsbCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzXG4gKiBAcGFyYW0gYWJzb2x1dGVEZWFkbGluZU92ZXJyaWRlIGlmIHByb3ZpZGVkLCB1c2UgdGhpcyBpbnN0ZWFkIG9mIG5vdyArIGR1cmF0aW9uIChmb3IgQWx3YXlzT24gZGVwbG95bWVudC1maWxsKVxuICovXG5hc3luYyBmdW5jdGlvbiBydW5GaWxsZXIoaW5wdXQ6IFdhcm1SdW5uZXJGaWxsUGF5bG9hZCwgZ2V0TmFtZUZvclNsb3Q6IChzbG90OiBudW1iZXIpID0+IHN0cmluZywgc291cmNlOiBzdHJpbmcsIGFic29sdXRlRGVhZGxpbmVPdmVycmlkZT86IG51bWJlcikge1xuICBjb25zdCBpbnN0YWxsYXRpb25JZCA9IGF3YWl0IHJlc29sdmVJbnN0YWxsYXRpb25JZChpbnB1dC5vd25lciwgaW5wdXQucmVwbyk7XG4gIGNvbnN0IGFic29sdXRlRGVhZGxpbmUgPSBhYnNvbHV0ZURlYWRsaW5lT3ZlcnJpZGUgPz8gRGF0ZS5ub3coKSArIGlucHV0LmR1cmF0aW9uICogMTAwMDtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IGlucHV0LmNvdW50OyBpKyspIHtcbiAgICBhd2FpdCBzdGFydFdhcm1SdW5uZXJBbmRFbnF1ZXVlS2VlcGVyKHtcbiAgICAgIHByb3ZpZGVyUGF0aDogaW5wdXQucHJvdmlkZXJQYXRoLFxuICAgICAgcHJvdmlkZXJMYWJlbHM6IGlucHV0LnByb3ZpZGVyTGFiZWxzLFxuICAgICAgb3duZXI6IGlucHV0Lm93bmVyLFxuICAgICAgcmVwbzogaW5wdXQucmVwbyxcbiAgICAgIGluc3RhbGxhdGlvbklkLFxuICAgICAgYWJzb2x1dGVEZWFkbGluZSxcbiAgICAgIGNvbmZpZ0hhc2g6IGlucHV0LmNvbmZpZ0hhc2gsXG4gICAgICBleGVjdXRpb25OYW1lOiBnZXROYW1lRm9yU2xvdChpKSxcbiAgICAgIHNsb3Q6IGksXG4gICAgfSk7XG4gIH1cblxuICBjb25zb2xlLmxvZyh7XG4gICAgbm90aWNlOiAnRmlsbCBjb21wbGV0ZSAtIHN0YXJ0ZWQgd2FybSBydW5uZXJzJyxcbiAgICBzb3VyY2UsXG4gICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICBwcm92aWRlclBhdGg6IGlucHV0LnByb3ZpZGVyUGF0aCxcbiAgICBzdGFydGVkOiBpbnB1dC5jb3VudCxcbiAgfSk7XG59XG5cbi8qKiBTdG9wIHRoZSBzdGVwIGZ1bmN0aW9uIGFuZCBkZWxldGUgdGhlIHJ1bm5lciBmcm9tIEdpdEh1Yi4gKi9cbmFzeW5jIGZ1bmN0aW9uIHN0b3BBbmREZWxldGVSdW5uZXIoaW5wdXQ6IFdhcm1SdW5uZXJLZWVwZXJNZXNzYWdlLCBvY3Rva2l0OiBPY3Rva2l0LCBzZWNyZXRzOiBHaXRIdWJTZWNyZXRzLCByZWFzb246IHN0cmluZykge1xuICB0cnkge1xuICAgIGF3YWl0IHNmbi5zZW5kKG5ldyBTdG9wRXhlY3V0aW9uQ29tbWFuZCh7XG4gICAgICBleGVjdXRpb25Bcm46IGlucHV0LmV4ZWN1dGlvbkFybixcbiAgICAgIGVycm9yOiByZWFzb24sXG4gICAgICBjYXVzZTogJ1dhcm0gcnVubmVyIHN0b3BwZWQgYnkga2VlcGVyJyxcbiAgICB9KSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmVycm9yKHtcbiAgICAgIG5vdGljZTogJ0ZhaWxlZCB0byBzdG9wIHN0ZXAgZnVuY3Rpb24nLFxuICAgICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICAgIHJ1bm5lck5hbWU6IGlucHV0LnJ1bm5lck5hbWUsXG4gICAgICBleGVjdXRpb25Bcm46IGlucHV0LmV4ZWN1dGlvbkFybixcbiAgICAgIGVycm9yOiBlLFxuICAgICAgaW5wdXQsXG4gICAgfSk7XG4gIH1cblxuICBjb25zdCBydW5uZXIgPSBhd2FpdCBnZXRSdW5uZXIob2N0b2tpdCwgc2VjcmV0cy5ydW5uZXJMZXZlbCwgaW5wdXQub3duZXIsIGlucHV0LnJlcG8sIGlucHV0LnJ1bm5lck5hbWUpO1xuICBpZiAocnVubmVyKSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGRlbGV0ZVJ1bm5lcihvY3Rva2l0LCBzZWNyZXRzLnJ1bm5lckxldmVsLCBpbnB1dC5vd25lciwgaW5wdXQucmVwbywgcnVubmVyLmlkKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLmVycm9yKHtcbiAgICAgICAgbm90aWNlOiAnRmFpbGVkIHRvIGRlbGV0ZSBydW5uZXInLFxuICAgICAgICBjb25maWdIYXNoOiBpbnB1dC5jb25maWdIYXNoLFxuICAgICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgICAgICBydW5uZXJJZDogcnVubmVyLmlkLFxuICAgICAgICBlcnJvcjogZSxcbiAgICAgICAgaW5wdXQsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBXYXJtIHJ1bm5lciBtYW5hZ2VyIExhbWJkYSAtIGhhbmRsZXMgdGhyZWUgaW52b2NhdGlvbiBtb2RlczpcbiAqXG4gKiAxLiBDbG91ZEZvcm1hdGlvbiBDdXN0b20gUmVzb3VyY2UgLSB0cmlnZ2VyZWQgb24gc3RhY2sgZGVwbG95IChDcmVhdGUvVXBkYXRlKSBmb3IgQWx3YXlzT25XYXJtUnVubmVyIG9ubHkuIFJ1bnNcbiAqICAgIHJ1bkZpbGxlciB3aXRoIGRlYWRsaW5lID0gbmV4dCBtaWRuaWdodCBVVEMgc28gcnVubmVycyBsYXN0IHVudGlsIHRoZSBuZXh0IGNyb24gZmlsbC4gRGVsZXRlIGlzIGEgbm8tb3AuXG4gKiAyLiBTUVMgbWVzc2FnZXMgLSBmaWxsIG9yIGtlZXBlclxuICogICAgLSBGaWxsIC0gZnJvbSBFdmVudEJyaWRnZSBjcm9uLiBVc2VzIG1lc3NhZ2VJZCBmb3IgZGV0ZXJtaW5pc3RpYyBleGVjdXRpb24gbmFtZXMgKGlkZW1wb3RlbnQgb24gcmVkZWxpdmVyeSkuXG4gKiAgICAtIEtlZXBlciAtIHRyYWNrcyBvbmUgcnVubmVyLiBVc2VzIFNRUyBtZXNzYWdlIGN5Y2xpbmcgZm9yIHBlcmlvZGljIGNoZWNrcy5cbiAqICAgICAgRWFjaCBtZXNzYWdlIHRyYWNrcyBvbmUgd2FybSBydW5uZXIuIFRoZSBrZWVwZXIgY2hlY2tzOlxuICogICAgICAtIFBhc3QgZGVhZGxpbmUgLSBzdG9wIHRoZSBTdGVwIEZ1bmN0aW9uIGFuZCBkZWxldGUgdGhlIHJ1bm5lci5cbiAqICAgICAgLSBDb25maWcgaGFzaCAtIGlmIHRoZSBtZXNzYWdlJ3MgYGNvbmZpZ0hhc2hgIGRvZXNuJ3QgbWF0Y2ggdGhlIGN1cnJlbnQgYFdBUk1fQ09ORklHX0hBU0hFU2AgZW52IHZhciwgdGhlIHJ1bm5lciBpcyBmcm9tIGEgc3RhbGUgY29uZmlnIC0gc3RvcCBpdCBhbmQgZGlzY2FyZCB0aGUgbWVzc2FnZSB3aXRob3V0IHJlcGxhY2VtZW50LlxuICogICAgICAtIEJ1c3kvZmluaXNoZWQgLSBpZiB0aGUgU3RlcCBGdW5jdGlvbiBlbmRlZCBvciB0aGUgR2l0SHViIHJ1bm5lciBpcyBidXN5ICh0b29rIGEgam9iKSwgc3RhcnQgYSByZXBsYWNlbWVudCBydW5uZXIgKGluaGVyaXRpbmcgdGhlIHNhbWUgZGVhZGxpbmUgYW5kIGNvbmZpZyBoYXNoKS5cbiAqICAgICAgLSBOb3QgZm91bmQgeWV0IChydW5uZXIvaW5mcmFzdHJ1Y3R1cmUgc3RpbGwgc3RhcnRpbmcpIC0gcmV0cnkgbGF0ZXIgKG1lc3NhZ2UgZ29lcyBiYWNrIHRvIHF1ZXVlKS5cbiAqICAgICAgLSBTdGlsbCBpZGxlIC0gcmV0cnkgbGF0ZXIgdG8gY2hlY2sgYWdhaW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuU1FTRXZlbnQgfCBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGlmIChpc0N1c3RvbVJlc291cmNlRXZlbnQoZXZlbnQpKSB7XG4gICAgY29uc3QgcGh5c2ljYWxJZCA9ICgnUGh5c2ljYWxSZXNvdXJjZUlkJyBpbiBldmVudCA/IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA6IHVuZGVmaW5lZCkgPz8gZXZlbnQuTG9naWNhbFJlc291cmNlSWQ7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHByb3BzID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzIGFzIHVua25vd24gYXMgV2FybVJ1bm5lckZpbGxQYXlsb2FkO1xuICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICBub3RpY2U6ICdDdXN0b20gcmVzb3VyY2UgZmlsbCcsXG4gICAgICAgIHJlcXVlc3RUeXBlOiBldmVudC5SZXF1ZXN0VHlwZSxcbiAgICAgICAgbG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgICAgICBjb25maWdIYXNoOiBwcm9wcy5jb25maWdIYXNoLFxuICAgICAgICBwcm92aWRlclBhdGg6IHByb3BzLnByb3ZpZGVyUGF0aCxcbiAgICAgICAgY291bnQ6IHByb3BzLmNvdW50LFxuICAgICAgfSk7XG4gICAgICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdDcmVhdGUnIHx8IGV2ZW50LlJlcXVlc3RUeXBlID09PSAnVXBkYXRlJykge1xuICAgICAgICBjb25zdCBnZXROYW1lRm9yU2xvdCA9IChzbG90OiBudW1iZXIpID0+XG4gICAgICAgICAgZGV0ZXJtaW5pc3RpY0V4ZWN1dGlvbk5hbWUocHJvcHMucHJvdmlkZXJQYXRoLCBgJHtldmVudC5Mb2dpY2FsUmVzb3VyY2VJZH06JHtldmVudC5SZXF1ZXN0VHlwZX06JHtwcm9wcy5jb25maWdIYXNofToke3Nsb3R9YCk7XG4gICAgICAgIGNvbnN0IGRlYWRsaW5lID0gZ2V0TmV4dE1pZG5pZ2h0VXRjTXMoKTtcbiAgICAgICAgYXdhaXQgcnVuRmlsbGVyKHByb3BzLCBnZXROYW1lRm9yU2xvdCwgJ2N1c3RvbVJlc291cmNlJywgZGVhZGxpbmUpO1xuICAgICAgfVxuICAgICAgYXdhaXQgY3VzdG9tUmVzb3VyY2VSZXNwb25kKGV2ZW50LCAnU1VDQ0VTUycsICdPSycsIHBoeXNpY2FsSWQsIHt9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLmVycm9yKHsgbm90aWNlOiAnQ3VzdG9tIHJlc291cmNlIGhhbmRsZXIgZmFpbGVkJywgZXJyb3I6IGUgfSk7XG4gICAgICBhd2FpdCBjdXN0b21SZXNvdXJjZVJlc3BvbmQoZXZlbnQsICdGQUlMRUQnLCAoZSBhcyBFcnJvcikubWVzc2FnZSB8fCAnSW50ZXJuYWwgRXJyb3InLCBwaHlzaWNhbElkLCB7fSk7XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuXG4gIGlmICghaXNTcXNFdmVudChldmVudCkpIHtcbiAgICBjb25zb2xlLmVycm9yKHsgbm90aWNlOiAnVW5rbm93biBldmVudCB0eXBlOyBpZ25vcmluZycsIGV2ZW50IH0pO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHZhbGlkSGFzaGVzID0gbmV3IFNldCgocHJvY2Vzcy5lbnYuV0FSTV9DT05GSUdfSEFTSEVTID8/ICcnKS5zcGxpdCgnLCcpLmZpbHRlcihCb29sZWFuKSk7XG4gIGNvbnN0IHJlc3VsdDogQVdTTGFtYmRhLlNRU0JhdGNoUmVzcG9uc2UgPSB7IGJhdGNoSXRlbUZhaWx1cmVzOiBbXSB9O1xuICBjb25zdCBvY3Rva2l0Q2FjaGUgPSBuZXcgTWFwPG51bWJlciB8IHVuZGVmaW5lZCwgeyBvY3Rva2l0OiBPY3Rva2l0OyBzZWNyZXRzOiBHaXRIdWJTZWNyZXRzIH0+KCk7XG5cbiAgZm9yIChjb25zdCByZWNvcmQgb2YgZXZlbnQuUmVjb3Jkcykge1xuICAgIGxldCBib2R5OiBXYXJtUnVubmVyS2VlcGVyTWVzc2FnZSB8IFdhcm1SdW5uZXJGaWxsUGF5bG9hZDtcbiAgICB0cnkge1xuICAgICAgYm9keSA9IEpTT04ucGFyc2UocmVjb3JkLmJvZHkpIGFzIFdhcm1SdW5uZXJLZWVwZXJNZXNzYWdlIHwgV2FybVJ1bm5lckZpbGxQYXlsb2FkO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3Ioe1xuICAgICAgICBub3RpY2U6ICdGYWlsZWQgdG8gcGFyc2UgbWVzc2FnZSBib2R5JyxcbiAgICAgICAgcmVxdWVzdElkOiByZWNvcmQubWVzc2FnZUlkLFxuICAgICAgICBlcnJvcjogZSxcbiAgICAgIH0pO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmV0cnlMYXRlciA9ICgpID0+IHJlc3VsdC5iYXRjaEl0ZW1GYWlsdXJlcy5wdXNoKHsgaXRlbUlkZW50aWZpZXI6IHJlY29yZC5tZXNzYWdlSWQgfSk7XG5cbiAgICBjb25zdCBpc0ZpbGwgPSBpc0ZpbGxJbnB1dChib2R5KTtcbiAgICBjb25zdCBjb25maWdIYXNoID0gKGJvZHkgYXMgV2FybVJ1bm5lckZpbGxQYXlsb2FkICYgV2FybVJ1bm5lcktlZXBlck1lc3NhZ2UpLmNvbmZpZ0hhc2g7XG4gICAgY29uc3QgcnVubmVyTmFtZSA9IGlzRmlsbCA/IHVuZGVmaW5lZCA6IChib2R5IGFzIFdhcm1SdW5uZXJLZWVwZXJNZXNzYWdlKS5ydW5uZXJOYW1lO1xuICAgIGNvbnNvbGUubG9nKHtcbiAgICAgIG5vdGljZTogJ1Byb2Nlc3NpbmcgU1FTIG1lc3NhZ2UnLFxuICAgICAgbWVzc2FnZUlkOiByZWNvcmQubWVzc2FnZUlkLFxuICAgICAgY29uZmlnSGFzaCxcbiAgICAgIHJ1bm5lck5hbWUsXG4gICAgfSk7XG5cbiAgICAvLyBzY2hlZHVsZWQgZmlsbCBmcm9tIEV2ZW50QnJpZGdlIHZpYSBTUVNcbiAgICBpZiAoaXNGaWxsKSB7XG4gICAgICBjb25zdCBmaWxsUGF5bG9hZCA9IGJvZHkgYXMgV2FybVJ1bm5lckZpbGxQYXlsb2FkO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICAgIG5vdGljZTogJ1NjaGVkdWxlZCBmaWxsJyxcbiAgICAgICAgICBjb25maWdIYXNoLFxuICAgICAgICAgIHByb3ZpZGVyUGF0aDogZmlsbFBheWxvYWQucHJvdmlkZXJQYXRoLFxuICAgICAgICAgIGNvdW50OiBmaWxsUGF5bG9hZC5jb3VudCxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IGdldE5hbWVGb3JTbG90ID0gKHNsb3Q6IG51bWJlcikgPT5cbiAgICAgICAgICBkZXRlcm1pbmlzdGljRXhlY3V0aW9uTmFtZShmaWxsUGF5bG9hZC5wcm92aWRlclBhdGgsIGAke3JlY29yZC5tZXNzYWdlSWR9OiR7c2xvdH1gKTtcbiAgICAgICAgYXdhaXQgcnVuRmlsbGVyKGZpbGxQYXlsb2FkLCBnZXROYW1lRm9yU2xvdCwgJ3NjaGVkdWxlZCcsIHVuZGVmaW5lZCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3Ioe1xuICAgICAgICAgIG5vdGljZTogJ0ZpbGwgZmFpbGVkJyxcbiAgICAgICAgICBtZXNzYWdlSWQ6IHJlY29yZC5tZXNzYWdlSWQsXG4gICAgICAgICAgY29uZmlnSGFzaDogZmlsbFBheWxvYWQuY29uZmlnSGFzaCxcbiAgICAgICAgICBlcnJvcjogZSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHJ5TGF0ZXIoKTtcbiAgICAgIH1cbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIGtlZXBlciBtZXNzYWdlXG4gICAgY29uc3QgaW5wdXQgPSBib2R5IGFzIFdhcm1SdW5uZXJLZWVwZXJNZXNzYWdlO1xuICAgIGNvbnNvbGUubG9nKHtcbiAgICAgIG5vdGljZTogJ0NoZWNraW5nIHdhcm0gcnVubmVyJyxcbiAgICAgIGNvbmZpZ0hhc2g6IGlucHV0LmNvbmZpZ0hhc2gsXG4gICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgIH0pO1xuXG4gICAgLy8gZ2V0IGdpdGh1YiBhY2Nlc3MgKGNhY2hlZCBwZXIgaW5zdGFsbGF0aW9uSWQgdG8gYXZvaWQgcmUtcmVhZGluZyB0aGUgc2VjcmV0cyBtYW5hZ2VyIGFuZCBHaXRodWIgQVBJIGV2ZXJ5IHRpbWUpXG4gICAgbGV0IG9jdG9raXQ6IE9jdG9raXQ7XG4gICAgbGV0IHNlY3JldHM6IEdpdEh1YlNlY3JldHM7XG4gICAgY29uc3QgY2FjaGVkID0gb2N0b2tpdENhY2hlLmdldChpbnB1dC5pbnN0YWxsYXRpb25JZCk7XG4gICAgaWYgKGNhY2hlZCkge1xuICAgICAgb2N0b2tpdCA9IGNhY2hlZC5vY3Rva2l0O1xuICAgICAgc2VjcmV0cyA9IGNhY2hlZC5zZWNyZXRzO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBnb3QgPSBhd2FpdCBnZXRPY3Rva2l0KGlucHV0Lmluc3RhbGxhdGlvbklkKTtcbiAgICAgIG9jdG9raXQgPSBnb3Qub2N0b2tpdDtcbiAgICAgIHNlY3JldHMgPSBnb3QuZ2l0aHViU2VjcmV0cztcbiAgICAgIG9jdG9raXRDYWNoZS5zZXQoaW5wdXQuaW5zdGFsbGF0aW9uSWQsIHsgb2N0b2tpdCwgc2VjcmV0cyB9KTtcbiAgICB9XG5cbiAgICAvLyBzdGFsZSBjb25maWcgLSBiZXN0LWVmZm9ydCBzdG9wLCB0aGVuIGRpc2NhcmQgbWVzc2FnZSAocnVubmVyIHdpbGwgc2VsZi10ZXJtaW5hdGUgYXQgaXRzIGlkbGUgdGltZW91dClcbiAgICBpZiAoIXZhbGlkSGFzaGVzLmhhcyhpbnB1dC5jb25maWdIYXNoKSkge1xuICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICBub3RpY2U6ICdDb25maWcgaGFzaCBtaXNtYXRjaCAobmV3IENESyBkZXBsb3ltZW50LCBvbGQgcnVubmVyKSAtIHN0b3BwaW5nIHN0YWxlIHdhcm0gcnVubmVyJyxcbiAgICAgICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICAgICAgcnVubmVyTmFtZTogaW5wdXQucnVubmVyTmFtZSxcbiAgICAgICAgdmFsaWRIYXNoZXM6IHZhbGlkSGFzaGVzLFxuICAgICAgfSk7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHN0b3BBbmREZWxldGVSdW5uZXIoaW5wdXQsIG9jdG9raXQsIHNlY3JldHMsICdTdGFsZVdhcm1SdW5uZXInKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcih7XG4gICAgICAgICAgbm90aWNlOiAnQmVzdC1lZmZvcnQgY2xlYW51cCBvZiBzdGFsZSB3YXJtIHJ1bm5lciBmYWlsZWQ7IGl0IHdpbGwgc2VsZi10ZXJtaW5hdGUgYXQgaWRsZSB0aW1lb3V0JyxcbiAgICAgICAgICBjb25maWdIYXNoOiBpbnB1dC5jb25maWdIYXNoLFxuICAgICAgICAgIHJ1bm5lck5hbWU6IGlucHV0LnJ1bm5lck5hbWUsXG4gICAgICAgICAgZXJyb3I6IGUsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gcGFzdCBkZWFkbGluZSAtIGtlZXBlciBtdXN0IHN0b3AgYW5kIGRlbGV0ZSB0aGUgcnVubmVyXG4gICAgaWYgKERhdGUubm93KCkgPj0gaW5wdXQuYWJzb2x1dGVEZWFkbGluZSkge1xuICAgICAgY29uc29sZS5sb2coe1xuICAgICAgICBub3RpY2U6ICdXYXJtIHJ1bm5lciBwYXN0IGRlYWRsaW5lLCBzdG9wcGluZyBhbmQgZGVsZXRpbmcnLFxuICAgICAgICBjb25maWdIYXNoOiBpbnB1dC5jb25maWdIYXNoLFxuICAgICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgICAgfSk7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBzdG9wQW5kRGVsZXRlUnVubmVyKGlucHV0LCBvY3Rva2l0LCBzZWNyZXRzLCAnV2FybVJ1bm5lckV4cGlyZWQnKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcih7XG4gICAgICAgICAgbm90aWNlOiAnRmFpbGVkIHRvIHN0b3AgZXhwaXJlZCB3YXJtIHJ1bm5lcicsXG4gICAgICAgICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICAgICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgICAgICAgIGVycm9yOiBlLFxuICAgICAgICB9KTtcbiAgICAgICAgLy8gZG9uJ3QgcmV0cnkgdG8gbm90IGFjY2lkZW50YWxseSBjcmVhdGUgYSBuZXcgcnVubmVyXG4gICAgICAgIC8vIGlkbGUgcmVhcGVyIHdpbGwgdGFrZSBjYXJlIG9mIGl0IHNvb24gZW5vdWdoXG4gICAgICB9XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBjaGVjayBpZiBzdGVwIGZ1bmN0aW9uIGlzIHN0aWxsIHJ1bm5pbmdcbiAgICBjb25zdCBleGVjdXRpb24gPSBhd2FpdCBzZm4uc2VuZChuZXcgRGVzY3JpYmVFeGVjdXRpb25Db21tYW5kKHsgZXhlY3V0aW9uQXJuOiBpbnB1dC5leGVjdXRpb25Bcm4gfSkpO1xuICAgIGNvbnN0IHN0aWxsUnVubmluZyA9IGV4ZWN1dGlvbi5zdGF0dXMgPT09ICdSVU5OSU5HJztcblxuICAgIC8vIGZpbmQgcnVubmVyXG4gICAgY29uc3QgcnVubmVyID0gYXdhaXQgZ2V0UnVubmVyKG9jdG9raXQsIHNlY3JldHMucnVubmVyTGV2ZWwsIGlucHV0Lm93bmVyLCBpbnB1dC5yZXBvLCBpbnB1dC5ydW5uZXJOYW1lKTtcblxuICAgIC8vIG5lZWQgcmVwbGFjZW1lbnQ6IHN0ZXAgZnVuY3Rpb24gZmluaXNoZWQgKG5vdCBydW5uaW5nKSBvciBydW5uZXIgdG9vayBhIGpvYiAoYnVzeSlcbiAgICBpZiAoIXN0aWxsUnVubmluZyB8fCBydW5uZXI/LmJ1c3kpIHtcbiAgICAgIGNvbnNvbGUubG9nKHtcbiAgICAgICAgbm90aWNlOiAnV2FybSBydW5uZXIgZmluaXNoZWQgb3IgYnVzeTsgc3RhcnRpbmcgcmVwbGFjZW1lbnQnLFxuICAgICAgICBjb25maWdIYXNoOiBpbnB1dC5jb25maWdIYXNoLFxuICAgICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgICAgICBzdGlsbFJ1bm5pbmcsXG4gICAgICAgIHJ1bm5lckJ1c3k6IHJ1bm5lcj8uYnVzeSA/PyBmYWxzZSxcbiAgICAgIH0pO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc3RhcnRXYXJtUnVubmVyQW5kRW5xdWV1ZUtlZXBlcih7XG4gICAgICAgICAgcHJvdmlkZXJQYXRoOiBpbnB1dC5wcm92aWRlclBhdGgsXG4gICAgICAgICAgcHJvdmlkZXJMYWJlbHM6IGlucHV0LnByb3ZpZGVyTGFiZWxzLFxuICAgICAgICAgIG93bmVyOiBpbnB1dC5vd25lcixcbiAgICAgICAgICByZXBvOiBpbnB1dC5yZXBvLFxuICAgICAgICAgIGluc3RhbGxhdGlvbklkOiBpbnB1dC5pbnN0YWxsYXRpb25JZCxcbiAgICAgICAgICBhYnNvbHV0ZURlYWRsaW5lOiBpbnB1dC5hYnNvbHV0ZURlYWRsaW5lLFxuICAgICAgICAgIGNvbmZpZ0hhc2g6IGlucHV0LmNvbmZpZ0hhc2gsXG4gICAgICAgICAgZXhlY3V0aW9uTmFtZTogZGV0ZXJtaW5pc3RpY0V4ZWN1dGlvbk5hbWUoaW5wdXQucHJvdmlkZXJQYXRoLCByZWNvcmQubWVzc2FnZUlkKSxcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3Ioe1xuICAgICAgICAgIG5vdGljZTogJ0ZhaWxlZCB0byBzdGFydCByZXBsYWNlbWVudCB3YXJtIHJ1bm5lcicsXG4gICAgICAgICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICAgICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgICAgICAgIGVycm9yOiBlLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0cnlMYXRlcigpO1xuICAgICAgfVxuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gc3RlcCBmdW5jdGlvbiBzdGlsbCBydW5uaW5nIGJ1dCBydW5uZXIgbm90IGZvdW5kIHlldFxuICAgIGlmICghcnVubmVyKSB7XG4gICAgICBjb25zb2xlLmxvZyh7XG4gICAgICAgIG5vdGljZTogJ1J1bm5lciBub3QgcnVubmluZyB5ZXQnLFxuICAgICAgICBjb25maWdIYXNoOiBpbnB1dC5jb25maWdIYXNoLFxuICAgICAgICBydW5uZXJOYW1lOiBpbnB1dC5ydW5uZXJOYW1lLFxuICAgICAgfSk7XG4gICAgICByZXRyeUxhdGVyKCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBzdGlsbCBpZGxlIC0gY2hlY2sgYWdhaW4gbGF0ZXJcbiAgICBjb25zb2xlLmxvZyh7XG4gICAgICBub3RpY2U6ICdSdW5uZXIgc3RpbGwgaWRsZSAtIHdpbGwgY2hlY2sgYWdhaW4gbGF0ZXInLFxuICAgICAgY29uZmlnSGFzaDogaW5wdXQuY29uZmlnSGFzaCxcbiAgICAgIHJ1bm5lck5hbWU6IGlucHV0LnJ1bm5lck5hbWUsXG4gICAgfSk7XG4gICAgcmV0cnlMYXRlcigpO1xuICB9XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiJdfQ==