projen
Version:
CDK for software projects
511 lines • 66.2 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GithubWorkflow = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const node_path_1 = require("node:path");
const case_1 = require("case");
const _resolve_1 = require("../_resolve");
const component_1 = require("../component");
const util_1 = require("../util");
const yaml_1 = require("../yaml");
/**
* Workflow for GitHub.
*
* A workflow is a configurable automated process made up of one or more jobs.
*
* @see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
*/
class GithubWorkflow extends component_1.Component {
/**
* All current jobs of the workflow.
*
* This is a read-only copy, use the respective helper methods to add, update or remove jobs.
*/
get jobs() {
return { ...this._jobs };
}
/**
* @param github The GitHub component of the project this workflow belongs to.
* @param name The name of the workflow, displayed under the repository's "Actions" tab.
* @param options Additional options to configure the workflow.
*/
constructor(github, name, options = {}) {
super(github.project, `${new.target.name}#${name}`);
this._jobs = {};
this.events = {};
const defaultConcurrency = {
cancelInProgress: false,
group: "${{ github.workflow }}",
};
this.name = name;
this.concurrency = options.limitConcurrency
? (0, util_1.deepMerge)([
defaultConcurrency,
options.concurrencyOptions,
])
: undefined;
this.projenCredentials = github.projenCredentials;
this.actions = github.actions;
this.env = options.env;
const workflowsEnabled = github.workflowsEnabled || options.force;
if (workflowsEnabled) {
const fileName = options.fileName ?? `${name.toLocaleLowerCase()}.yml`;
const extension = (0, node_path_1.extname)(fileName).toLowerCase();
if (![".yml", ".yaml"].includes(extension)) {
throw new Error(`GitHub Workflow files must have either a .yml or .yaml file extension, got: ${fileName}`);
}
this.file = new yaml_1.YamlFile(this.project, `.github/workflows/${fileName}`, {
obj: () => this.renderWorkflow(),
// GitHub needs to read the file from the repository in order to work.
committed: true,
});
}
}
/**
* Add events to triggers the workflow.
*
* @param events The event(s) to trigger the workflow.
*/
on(events) {
this.events = {
...this.events,
...events,
};
}
/**
* Adds a single job to the workflow.
* @param id The job name (unique within the workflow)
* @param job The job specification
*/
addJob(id, job) {
this.addJobs({ [id]: job });
}
/**
* Add jobs to the workflow.
*
* @param jobs Jobs to add.
*/
addJobs(jobs) {
verifyJobConstraints(jobs);
Object.assign(this._jobs, { ...jobs });
}
/**
* Get a single job from the workflow.
* @param id The job name (unique within the workflow)
*/
getJob(id) {
return this._jobs[id];
}
/**
* Updates a single job to the workflow.
* @param id The job name (unique within the workflow)
*/
updateJob(id, job) {
this.updateJobs({ [id]: job });
}
/**
* Updates jobs for this workflow
* Does a complete replace, it does not try to merge the jobs
*
* @param jobs Jobs to update.
*/
updateJobs(jobs) {
verifyJobConstraints(jobs);
Object.assign(this._jobs, { ...jobs });
}
/**
* Removes a single job to the workflow.
* @param id The job name (unique within the workflow)
*/
removeJob(id) {
delete this._jobs[id];
}
/**
* Gets a single step from a job by step ID.
*
* The returned object is frozen and read-only. Use `replaceStep` or
* `patchStep` to modify a step.
*
* @param jobId The job name (unique within the workflow)
* @param stepId The step ID to look up
* @returns A read-only copy of the step
*/
getStep(jobId, stepId) {
const steps = this.resolveJobSteps(jobId);
const index = this.findStepIndex(steps, stepId, jobId);
return Object.freeze({ ...steps[index] });
}
/**
* Appends a step to the end of a job's step list.
*
* @param jobId The job name (unique within the workflow)
* @param step The step to add. Must have an `id` set.
*/
appendStep(jobId, step) {
const steps = this.resolveJobSteps(jobId);
this.requireStepId(step);
this.requireUniqueStepId(steps, step.id, jobId);
steps.push(step);
}
/**
* Replaces an existing step in a job, preserving its position.
*
* @param jobId The job name (unique within the workflow)
* @param stepId The ID of the step to replace
* @param replacementStep The replacement step. If `id` is omitted, it inherits the original step's ID.
*/
replaceStep(jobId, stepId, replacementStep) {
const steps = this.resolveJobSteps(jobId);
const index = this.findStepIndex(steps, stepId, jobId);
const newStep = { ...replacementStep, id: replacementStep.id ?? stepId };
// If the id changed, validate uniqueness of the new id
if (newStep.id !== stepId) {
this.requireUniqueStepId(steps, newStep.id, jobId);
}
steps[index] = newStep;
}
/**
* Applies a surgical modification to an existing step.
*
* The provided patch is shallow-merged onto the existing step. Fields not
* present in the patch are preserved unchanged. Use `getStep` to read the
* current step values before constructing the patch.
*
* @param jobId The job name (unique within the workflow)
* @param stepId The ID of the step to patch
* @param patch A partial step object whose fields are shallow-merged onto the existing step
*
* @example
* // Append a flag to an existing test command:
* const step = workflow.getStep("build", "test");
* workflow.patchStep("build", "test", {
* run: `${step.run} --testPathIgnorePatterns "integration/*"`,
* });
*
* @example
* // Add an env var without replacing existing ones:
* const step = workflow.getStep("build", "install");
* workflow.patchStep("build", "install", {
* env: { ...step.env, NODE_AUTH_TOKEN: "${{ steps.jfrog.outputs.oidc-token }}" },
* });
*/
patchStep(jobId, stepId, patch) {
const steps = this.resolveJobSteps(jobId);
const index = this.findStepIndex(steps, stepId, jobId);
const currentStep = steps[index];
steps[index] = { ...currentStep, ...patch, id: stepId };
}
/**
* Removes a step from a job by step ID.
*
* @param jobId The job name (unique within the workflow)
* @param stepId The ID of the step to remove
*/
removeStep(jobId, stepId) {
const steps = this.resolveJobSteps(jobId);
const index = this.findStepIndex(steps, stepId, jobId);
steps.splice(index, 1);
}
/**
* Inserts a step before an existing step, identified by ID.
*
* @param jobId The job name (unique within the workflow)
* @param referenceStepId The ID of the step to insert before
* @param step The step to insert. Must have an `id` set.
*/
insertStepBefore(jobId, referenceStepId, step) {
const steps = this.resolveJobSteps(jobId);
const index = this.findStepIndex(steps, referenceStepId, jobId);
this.requireStepId(step);
this.requireUniqueStepId(steps, step.id, jobId);
steps.splice(index, 0, step);
}
/**
* Inserts a step after an existing step, identified by ID.
*
* @param jobId The job name (unique within the workflow)
* @param referenceStepId The ID of the step to insert after
* @param step The step to insert. Must have an `id` set.
*/
insertStepAfter(jobId, referenceStepId, step) {
const steps = this.resolveJobSteps(jobId);
const index = this.findStepIndex(steps, referenceStepId, jobId);
this.requireStepId(step);
this.requireUniqueStepId(steps, step.id, jobId);
steps.splice(index + 1, 0, step);
}
/**
* Resolves the mutable steps array for a job.
* Throws if the job is not found or is a reusable workflow call.
*/
resolveJobSteps(jobId) {
const job = this._jobs[jobId];
if (!job) {
throw new Error(`Job "${jobId}" not found in workflow "${this.name}". Available jobs: ${Object.keys(this._jobs).join(", ") || "(none)"}`);
}
if ("uses" in job) {
throw new Error(`Job "${jobId}" is a reusable workflow call and does not have steps`);
}
return job.steps;
}
/**
* Finds the index of a step by ID or name within a steps array.
* Matches against `id` first, then falls back to `name`.
* Throws if the step is not found, or if multiple steps match by name.
*/
findStepIndex(steps, stepIdOrName, jobId) {
// Try exact id match first
const byId = steps.findIndex((s) => s.id === stepIdOrName);
if (byId !== -1) {
return byId;
}
// Fall back to name match
const byName = [];
for (let i = 0; i < steps.length; i++) {
if (steps[i].name === stepIdOrName) {
byName.push(i);
}
}
if (byName.length === 1) {
return byName[0];
}
if (byName.length > 1) {
throw new Error(`Multiple steps match name "${stepIdOrName}" in job "${jobId}" (at indices ${byName.join(", ")}). Use a unique step "id" instead.`);
}
const availableIds = steps
.map((s) => s.id)
.filter(Boolean)
.join(", ");
const availableNames = steps
.map((s) => s.name)
.filter(Boolean)
.join(", ");
throw new Error(`Step "${stepIdOrName}" not found in job "${jobId}". Available step IDs: ${availableIds || "(none)"}. Available step names: ${availableNames || "(none)"}`);
}
/**
* Validates that a step has an `id` set.
*/
requireStepId(step) {
if (!step.id) {
throw new Error('Step must have an "id" to be added via the step mutation API');
}
}
/**
* Validates that a step ID is unique within a job's steps.
*/
requireUniqueStepId(steps, stepId, jobId) {
if (steps.some((s) => s.id === stepId)) {
throw new Error(`Step ID "${stepId}" already exists in job "${jobId}". Step IDs must be unique within a job.`);
}
}
renderWorkflow() {
return {
name: this.name,
"run-name": this.runName,
on: snakeCaseKeys(this.events),
concurrency: this.concurrency
? {
group: this.concurrency?.group,
"cancel-in-progress": this.concurrency.cancelInProgress,
}
: undefined,
env: this.env,
jobs: renderJobs(this._jobs, this.actions),
};
}
}
exports.GithubWorkflow = GithubWorkflow;
_a = JSII_RTTI_SYMBOL_1;
GithubWorkflow[_a] = { fqn: "projen.github.GithubWorkflow", version: "0.99.70" };
function snakeCaseKeys(obj) {
if (typeof obj !== "object" || obj == null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(snakeCaseKeys);
}
const result = {};
for (let [k, v] of Object.entries(obj)) {
if (typeof v === "object" && v != null) {
v = snakeCaseKeys(v);
}
result[(0, case_1.snake)(k)] = v;
}
return result;
}
function renderJobs(jobs, actions) {
const result = {};
for (const [name, job] of Object.entries(jobs)) {
result[name] = renderJob(job);
}
return result;
/** @see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions */
function renderJob(job) {
const steps = new Array();
// https://docs.github.com/en/actions/using-workflows/reusing-workflows#supported-keywords-for-jobs-that-call-a-reusable-workflow
if ("uses" in job) {
return {
name: job.name,
needs: arrayOrScalar(job.needs),
if: job.if,
permissions: (0, util_1.kebabCaseKeys)(job.permissions),
concurrency: job.concurrency,
uses: job.uses,
with: job.with,
secrets: job.secrets,
strategy: renderJobStrategy(job.strategy),
};
}
if (job.tools) {
steps.push(...setupTools(job.tools));
}
const userDefinedSteps = (0, util_1.kebabCaseKeys)((0, _resolve_1.resolve)(job.steps), false);
steps.push(...userDefinedSteps);
return {
name: job.name,
needs: arrayOrScalar(job.needs),
"runs-on": arrayOrScalar(job.runsOnGroup) ?? arrayOrScalar(job.runsOn),
permissions: (0, util_1.kebabCaseKeys)(job.permissions),
environment: job.environment,
concurrency: job.concurrency,
outputs: renderJobOutputs(job.outputs),
env: job.env,
defaults: (0, util_1.kebabCaseKeys)(job.defaults),
if: job.if,
steps: steps.map(renderStep),
"timeout-minutes": job.timeoutMinutes,
strategy: renderJobStrategy(job.strategy),
"continue-on-error": job.continueOnError,
container: job.container,
services: job.services,
};
}
function renderJobOutputs(output) {
if (output == null) {
return undefined;
}
const rendered = {};
for (const [name, { stepId, outputName }] of Object.entries(output)) {
rendered[name] = `\${{ steps.${stepId}.outputs.${outputName} }}`;
}
return rendered;
}
function renderJobStrategy(strategy) {
if (strategy == null) {
return undefined;
}
const rendered = {
"max-parallel": strategy.maxParallel,
"fail-fast": strategy.failFast,
};
if (strategy.matrix) {
const matrix = {
include: strategy.matrix.include,
exclude: strategy.matrix.exclude,
};
for (const [key, values] of Object.entries(strategy.matrix.domain ?? {})) {
if (key in matrix) {
// A domain key was set to `include`, or `exclude`:
throw new Error(`Illegal job strategy matrix key: ${key}`);
}
matrix[key] = values;
}
rendered.matrix = matrix;
}
return rendered;
}
function renderStep(step) {
return {
name: step.name,
id: step.id,
if: step.if,
uses: step.uses && actions.get(step.uses),
env: step.env,
run: step.run,
shell: step.shell,
with: step.with,
"continue-on-error": step.continueOnError,
"timeout-minutes": step.timeoutMinutes,
"working-directory": step.workingDirectory,
};
}
}
function arrayOrScalar(arr) {
if (!Array.isArray(arr)) {
return arr;
}
if (arr == null || arr.length === 0) {
return arr;
}
if (arr.length === 1) {
return arr[0];
}
return arr;
}
function setupTools(tools) {
const steps = [];
if (tools.java) {
if (tools.java.cache && !tools.java.packageManager) {
throw new Error("java.packageManager is required when java.cache is true");
}
steps.push({
uses: "actions/setup-java@v5",
with: {
distribution: tools.java.distribution ?? "corretto",
"java-version": tools.java.version,
...(tools.java.cache && { cache: tools.java.packageManager }),
},
});
}
if (tools.node) {
steps.push({
uses: "actions/setup-node@v6",
with: {
"node-version": tools.node.version,
"package-manager-cache": tools.node.cache ?? false,
},
});
}
if (tools.python) {
if (tools.python.cache && !tools.python.packageManager) {
throw new Error("python.packageManager is required when python.cache is true");
}
steps.push({
uses: "actions/setup-python@v6",
with: {
"python-version": tools.python.version,
...(tools.python.cache && { cache: tools.python.packageManager }),
},
});
}
if (tools.go) {
steps.push({
uses: "actions/setup-go@v6",
with: {
"go-version": tools.go.version,
cache: tools.go.cache ?? false,
},
});
}
if (tools.dotnet) {
steps.push({
uses: "actions/setup-dotnet@v5",
with: {
"dotnet-version": tools.dotnet.version,
cache: tools.dotnet.cache ?? false,
},
});
}
return steps;
}
function verifyJobConstraints(jobs) {
// verify that job has a "permissions" statement to ensure workflow can
// operate in repos with default tokens set to readonly
for (const [id, job] of Object.entries(jobs)) {
if (!job.permissions) {
throw new Error(`${id}: all workflow jobs must have a "permissions" clause to ensure workflow can operate in restricted repositories`);
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2Zsb3dzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2dpdGh1Yi93b3JrZmxvd3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSx5Q0FBb0M7QUFDcEMsK0JBQTZCO0FBSzdCLDBDQUFzQztBQUN0Qyw0Q0FBeUM7QUFDekMsa0NBQW1EO0FBQ25ELGtDQUFtQztBQXlFbkM7Ozs7OztHQU1HO0FBQ0gsTUFBYSxjQUFlLFNBQVEscUJBQVM7SUFRM0M7Ozs7T0FJRztJQUNILElBQVcsSUFBSTtRQUliLE9BQU8sRUFBRSxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBMENEOzs7O09BSUc7SUFDSCxZQUNFLE1BQWMsRUFDZCxJQUFZLEVBQ1osVUFBaUMsRUFBRTtRQUVuQyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDLENBQUM7UUFqQnJDLFVBQUssR0FHbEIsRUFBRSxDQUFDO1FBRUMsV0FBTSxHQUF1QixFQUFFLENBQUM7UUFjdEMsTUFBTSxrQkFBa0IsR0FBdUI7WUFDN0MsZ0JBQWdCLEVBQUUsS0FBSztZQUN2QixLQUFLLEVBQUUsd0JBQXdCO1NBQ2hDLENBQUM7UUFFRixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsV0FBVyxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0I7WUFDekMsQ0FBQyxDQUFFLElBQUEsZ0JBQVMsRUFBQztnQkFDVCxrQkFBa0I7Z0JBQ2xCLE9BQU8sQ0FBQyxrQkFBa0I7YUFDM0IsQ0FBd0I7WUFDM0IsQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUNkLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxNQUFNLENBQUMsaUJBQWlCLENBQUM7UUFDbEQsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDO1FBRTlCLElBQUks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LElBQUksRUFBRSx5QkFBeUI7WUFDL0IsSUFBSSxFQUFFO2dCQUNKLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTztnQkFDdEMsS0FBSyxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxJQUFJLEtBQUs7YUFDbkM7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQsU0FBUyxvQkFBb0IsQ0FDM0IsSUFBMEU7SUFFMUUsdUVBQXVFO0lBQ3ZFLHVEQUF1RDtJQUN2RCxLQUFLLE1BQU0sQ0FBQyxFQUFFLEVBQUUsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQzdDLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDckIsTUFBTSxJQUFJLEtBQUssQ0FDYixHQUFHLEVBQUUsZ0hBQWdILENBQ3RILENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBleHRuYW1lIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgc25ha2UgfSBmcm9tIFwiY2FzZVwiO1xuaW1wb3J0IHR5cGUgeyBHaXRIdWJBY3Rpb25zUHJvdmlkZXIgfSBmcm9tIFwiLi9hY3Rpb25zLXByb3ZpZGVyXCI7XG5pbXBvcnQgdHlwZSB7IEdpdEh1YiB9IGZyb20gXCIuL2dpdGh1YlwiO1xuaW1wb3J0IHR5cGUgeyBHaXRodWJDcmVkZW50aWFscyB9IGZyb20gXCIuL2dpdGh1Yi1jcmVkZW50aWFsc1wiO1xuaW1wb3J0IHR5cGUgKiBhcyB3b3JrZmxvd3MgZnJvbSBcIi4vd29ya2Zsb3dzLW1vZGVsXCI7XG5pbXBvcnQgeyByZXNvbHZlIH0gZnJvbSBcIi4uL19yZXNvbHZlXCI7XG5pbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tIFwiLi4vY29tcG9uZW50XCI7XG5pbXBvcnQgeyBkZWVwTWVyZ2UsIGtlYmFiQ2FzZUtleXMgfSBmcm9tIFwiLi4vdXRpbFwiO1xuaW1wb3J0IHsgWWFtbEZpbGUgfSBmcm9tIFwiLi4veWFtbFwiO1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIGBjb25jdXJyZW5jeWAuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ29uY3VycmVuY3lPcHRpb25zIHtcbiAgLyoqXG4gICAqIENvbmN1cnJlbmN5IGdyb3VwIGNvbnRyb2xzIHdoaWNoIHdvcmtmbG93IHJ1bnMgd2lsbCBzaGFyZSB0aGUgc2FtZSBjb25jdXJyZW5jeSBsaW1pdC5cbiAgICogRm9yIGV4YW1wbGUsIGlmIHlvdSBzcGVjaWZ5IGAke3sgZ2l0aHViLndvcmtmbG93IH19LSR7eyBnaXRodWIucmVmIH19YCwgd29ya2Zsb3cgcnVucyB0cmlnZ2VyZWRcbiAgICogb24gdGhlIHNhbWUgYnJhbmNoIGNhbm5vdCBydW4gY29uY3VycmVudGx5LCBidXQgd29ya2Zsb3dzIHJ1bnMgdHJpZ2dlcmVkIG9uIGRpZmZlcmVudCBicmFuY2hlcyBjYW4uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gJHt7IGdpdGh1Yi53b3JrZmxvdyB9fVxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vZG9jcy5naXRodWIuY29tL2VuL2FjdGlvbnMvd3JpdGluZy13b3JrZmxvd3MvY2hvb3Npbmctd2hhdC15b3VyLXdvcmtmbG93LWRvZXMvdXNpbmctY29uY3VycmVuY3kjZXhhbXBsZS1jb25jdXJyZW5jeS1ncm91cHNcbiAgICovXG4gIHJlYWRvbmx5IGdyb3VwPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBXaGVuIGEgd29ya2Zsb3cgaXMgdHJpZ2dlcmVkIHdoaWxlIGFub3RoZXIgb25lIChpbiB0aGUgc2FtZSBncm91cCkgaXMgcnVubmluZywgc2hvdWxkIEdpdEh1YiBjYW5jZWxcbiAgICogdGhlIHJ1bm5pbmcgd29ya2Zsb3c/XG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBjYW5jZWxJblByb2dyZXNzPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBPcHRpb25zIGZvciBgR2l0aHViV29ya2Zsb3dgLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEdpdGh1YldvcmtmbG93T3B0aW9ucyB7XG4gIC8qKlxuICAgKiBGb3JjZSB0aGUgY3JlYXRpb24gb2YgdGhlIHdvcmtmbG93IGV2ZW4gaWYgYHdvcmtmbG93c2AgaXMgZGlzYWJsZWQgaW4gYEdpdEh1YmAuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBmb3JjZT86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEVuYWJsZSBjb25jdXJyZW5jeSBsaW1pdGF0aW9ucy4gVXNlIGBjb25jdXJyZW5jeU9wdGlvbnNgIHRvIGNvbmZpZ3VyZSBzcGVjaWZpYyBub24gZGVmYXVsdCB2YWx1ZXMuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBsaW1pdENvbmN1cnJlbmN5PzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQ29uY3VycmVuY3kgZW5zdXJlcyB0aGF0IG9ubHkgYSBzaW5nbGUgam9iIG9yIHdvcmtmbG93IHVzaW5nIHRoZSBzYW1lIGNvbmN1cnJlbmN5IGdyb3VwIHdpbGwgcnVuIGF0IGEgdGltZS4gQ3VycmVudGx5IGluIGJldGEuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0geyBncm91cDogJHt7IGdpdGh1Yi53b3JrZmxvdyB9fSwgY2FuY2VsSW5Qcm9ncmVzczogZmFsc2UgfVxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vZG9jcy5naXRodWIuY29tL2VuL2FjdGlvbnMvbGVhcm4tZ2l0aHViLWFjdGlvbnMvd29ya2Zsb3ctc3ludGF4LWZvci1naXRodWItYWN0aW9ucyNjb25jdXJyZW5jeVxuICAgKi9cbiAgcmVhZG9ubHkgY29uY3VycmVuY3lPcHRpb25zPzogQ29uY3VycmVuY3lPcHRpb25zO1xuXG4gIC8qKlxuICAgKiBBZGRpdGlvbmFsIGVudmlyb25tZW50IHZhcmlhYmxlcyB0byBzZXQgZm9yIHRoZSB3b3JrZmxvdy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBhZGRpdGlvbmFsIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICAgKi9cbiAgcmVhZG9ubHkgZW52PzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcblxuICAvKipcbiAgICogU2V0IGEgY3VzdG9tIGZpbGUgbmFtZSBmb3IgdGhlIHdvcmtmbG93IGRlZmluaXRpb24gZmlsZS4gTXVzdCBpbmNsdWRlIGVpdGhlciBhIC55bWwgb3IgLnlhbWwgZmlsZSBleHRlbnNpb24uXG4gICAqXG4gICAqIFVzZSB0aGlzIG9wdGlvbiB0byBzZXQgYSBmaWxlIG5hbWUgZm9yIHRoZSB3b3JrZmxvdyBmaWxlLCB0aGF0IGlzIGRpZmZlcmVudCB0aGFuIHRoZSBkaXNwbGF5IG5hbWUuXG4gICAqXG4gICAqIEBleGFtcGxlIFwiYnVpbGQtbmV3LnltbFwiXG4gICAqIEBleGFtcGxlIFwibXktd29ya2Zsb3cueWFtbFwiXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gYSBwYXRoLXNhZmUgdmVyc2lvbiBvZiB0aGUgd29ya2Zsb3cgbmFtZSBwbHVzIHRoZSAueW1sIGZpbGUgZW5kaW5nLCBlLmcuIGJ1aWxkLnltbFxuICAgKi9cbiAgcmVhZG9ubHkgZmlsZU5hbWU/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogV29ya2Zsb3cgZm9yIEdpdEh1Yi5cbiAqXG4gKiBBIHdvcmtmbG93IGlzIGEgY29uZmlndXJhYmxlIGF1dG9tYXRlZCBwcm9jZXNzIG1hZGUgdXAgb2Ygb25lIG9yIG1vcmUgam9icy5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vZG9jcy5naXRodWIuY29tL2VuL2FjdGlvbnMvcmVmZXJlbmNlL3dvcmtmbG93LXN5bnRheC1mb3ItZ2l0aHViLWFjdGlvbnNcbiAqL1xuZXhwb3J0IGNsYXNzIEdpdGh1YldvcmtmbG93IGV4dGVuZHMgQ29tcG9uZW50IHtcbiAgLyoqXG4gICAqIFRoZSBuYW1lIG9mIHRoZSB3b3JrZmxvdy4gR2l0SHViIGRpc3BsYXlzIHRoZSBuYW1lcyBvZiB5b3VyIHdvcmtmbG93cyB1bmRlciB5b3VyIHJlcG9zaXRvcnknc1xuICAgKiBcIkFjdGlvbnNcIiB0YWIuXG4gICAqIEBzZWUgaHR0cHM6Ly9kb2NzLmdpdGh1Yi5jb20vZW4vYWN0aW9ucy93cml0aW5nLXdvcmtmbG93cy93b3JrZmxvdy1zeW50YXgtZm9yLWdpdGh1Yi1hY3Rpb25zI25hbWVcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBuYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEFsbCBjdXJyZW50IGpvYnMgb2YgdGhlIHdvcmtmbG93LlxuICAgKlxuICAgKiBUaGlzIGlzIGEgcmVhZC1vbmx5IGNvcHksIHVzZSB0aGUgcmVzcGVjdGl2ZSBoZWxwZXIgbWV0aG9kcyB0byBhZGQsIHVwZGF0ZSBvciByZW1vdmUgam9icy5cbiAgICovXG4gIHB1YmxpYyBnZXQgam9icygpOiBSZWNvcmQ8XG4gICAgc3RyaW5nLFxuICAgIHdvcmtmbG93cy5Kb2IgfCB3b3JrZmxvd3MuSm9iQ2FsbGluZ1JldXNhYmxlV29ya2Zsb3dcbiAgPiB7XG4gICAgcmV0dXJuIHsgLi4udGhpcy5fam9icyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBjb25jdXJyZW5jeSBjb25maWd1cmF0aW9uIG9mIHRoZSB3b3JrZmxvdy4gdW5kZWZpbmVkIG1lYW5zIG5vIGNvbmN1cnJlbmN5IGxpbWl0YXRpb25zLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNvbmN1cnJlbmN5PzogQ29uY3VycmVuY3lPcHRpb25zO1xuXG4gIC8qKlxuICAgKiBBZGRpdGlvbmFsIGVudmlyb25tZW50IHZhcmlhYmxlcyB0byBzZXQgZm9yIHRoZSB3b3JrZmxvdy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBlbnY/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuXG4gIC8qKlxuICAgKiBUaGUgd29ya2Zsb3cgWUFNTCBmaWxlLiBNYXkgbm90IGV4aXN0IGlmIGB3b3JrZmxvd3NFbmFibGVkYCBpcyBmYWxzZSBvbiBgR2l0SHViYC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBmaWxlOiBZYW1sRmlsZSB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogR2l0SHViIEFQSSBhdXRoZW50aWNhdGlvbiBtZXRob2QgdXNlZCBieSBwcm9qZW4gd29ya2Zsb3dzLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHByb2plbkNyZWRlbnRpYWxzOiBHaXRodWJDcmVkZW50aWFscztcblxuICAvKipcbiAgICogVGhlIG5hbWUgZm9yIHdvcmtmbG93IHJ1bnMgZ2VuZXJhdGVkIGZyb20gdGhlIHdvcmtmbG93LiBHaXRIdWIgZGlzcGxheXMgdGhlXG4gICAqIHdvcmtmbG93IHJ1biBuYW1lIGluIHRoZSBsaXN0IG9mIHdvcmtmbG93IHJ1bnMgb24geW91ciByZXBvc2l0b3J5J3NcbiAgICogXCJBY3Rpb25zXCIgdGFiLiBJZiBgcnVuLW5hbWVgIGlzIG9taXR0ZWQgb3IgaXMgb25seSB3aGl0ZXNwYWNlLCB0aGVuIHRoZSBydW5cbiAgICogbmFtZSBpcyBzZXQgdG8gZXZlbnQtc3BlY2lmaWMgaW5mb3JtYXRpb24gZm9yIHRoZSB3b3JrZmxvdyBydW4uIEZvclxuICAgKiBleGFtcGxlLCBmb3IgYSB3b3JrZmxvdyB0cmlnZ2VyZWQgYnkgYSBgcHVzaGAgb3IgYHB1bGxfcmVxdWVzdGAgZXZlbnQsIGl0XG4gICAqIGlzIHNldCBhcyB0aGUgY29tbWl0IG1lc3NhZ2UuXG4gICAqXG4gICAqIFRoaXMgdmFsdWUgY2FuIGluY2x1ZGUgZXhwcmVzc2lvbnMgYW5kIGNhbiByZWZlcmVuY2UgYGdpdGh1YmAgYW5kIGBpbnB1dHNgXG4gICAqIGNvbnRleHRzLlxuICAgKi9cbiAgcHVibGljIHJ1bk5hbWU/OiBzdHJpbmc7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBfam9iczogUmVjb3JkPFxuICAgIHN0cmluZyxcbiAgICB3b3JrZmxvd3MuSm9iIHwgd29ya2Zsb3dzLkpvYkNhbGxpbmdSZXVzYWJsZVdvcmtmbG93XG4gID4gPSB7fTtcbiAgcHJpdmF0ZSBhY3Rpb25zOiBHaXRIdWJBY3Rpb25zUHJvdmlkZXI7XG4gIHByaXZhdGUgZXZlbnRzOiB3b3JrZmxvd3MuVHJpZ2dlcnMgPSB7fTtcblxuICAvKipcbiAgICogQHBhcmFtIGdpdGh1YiBUaGUgR2l0SHViIGNvbXBvbmVudCBvZiB0aGUgcHJvamVjdCB0aGlzIHdvcmtmbG93IGJlbG9uZ3MgdG8uXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoZSB3b3JrZmxvdywgZGlzcGxheWVkIHVuZGVyIHRoZSByZXBvc2l0b3J5J3MgXCJBY3Rpb25zXCIgdGFiLlxuICAgKiBAcGFyYW0gb3B0aW9ucyBBZGRpdGlvbmFsIG9wdGlvbnMgdG8gY29uZmlndXJlIHRoZSB3b3JrZmxvdy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIGdpdGh1YjogR2l0SHViLFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICBvcHRpb25zOiBHaXRodWJXb3JrZmxvd09wdGlvbnMgPSB7fSxcbiAgKSB7XG4gICAgc3VwZXIoZ2l0aHViLnByb2plY3QsIGAke25ldy50YXJnZXQubmFtZX0jJHtuYW1lfWApO1xuXG4gICAgY29uc3QgZGVmYXVsdENvbmN1cnJlbmN5OiBDb25jdXJyZW5jeU9wdGlvbnMgPSB7XG4gICAgICBjYW5jZWxJblByb2dyZXNzOiBmYWxzZSxcbiAgICAgIGdyb3VwOiBcIiR7eyBnaXRodWIud29ya2Zsb3cgfX1cIixcbiAgICB9O1xuXG4gICAgdGhpcy5uYW1lID0gbmFtZTtcbiAgICB0aGlzLmNvbmN1cnJlbmN5ID0gb3B0aW9ucy5saW1pdENvbmN1cnJlbmN5XG4gICAgICA/IChkZWVwTWVyZ2UoW1xuICAgICAgICAgIGRlZmF1bHRDb25jdXJyZW5jeSxcbiAgICAgICAgICBvcHRpb25zLmNvbmN1cnJlbmN5T3B0aW9ucyxcbiAgICAgICAgXSkgYXMgQ29uY3VycmVuY3lPcHRpb25zKVxuICAgICAgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5wcm9qZW5DcmVkZW50aWFscyA9IGdpdGh1Yi5wcm9qZW5DcmVkZW50aWFscztcbiAgICB0aGlzLmFjdGlvbnMgPSBnaXRodWIuYWN0aW9ucztcblxuICAgIHRoaXMuZW52ID0gb3B0aW9ucy5lbnY7XG5cbiAgICBjb25zdCB3b3JrZmxvd3NFbmFibGVkID0gZ2l0aHViLndvcmtmbG93c0VuYWJsZWQgfHwgb3B0aW9ucy5mb3JjZTtcblxuICAgIGlmICh3b3JrZmxvd3NFbmFibGVkKSB7XG4gICAgICBjb25zdCBmaWxlTmFtZSA9IG9wdGlvbnMuZmlsZU5hbWUgPz8gYCR7bmFtZS50b0xvY2FsZUxvd2VyQ2FzZSgpfS55bWxgO1xuICAgICAgY29uc3QgZXh0ZW5zaW9uID0gZXh0bmFtZShmaWxlTmFtZSkudG9Mb3dlckNhc2UoKTtcblxuICAgICAgaWYgKCFbXCIueW1sXCIsIFwiLnlhbWxcIl0uaW5jbHVkZXMoZXh0ZW5zaW9uKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYEdpdEh1YiBXb3JrZmxvdyBmaWxlcyBtdXN0IGhhdmUgZWl0aGVyIGEgLnltbCBvciAueWFtbCBmaWxlIGV4dGVuc2lvbiwgZ290OiAke2ZpbGVOYW1lfWAsXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuZmlsZSA9IG5ldyBZYW1sRmlsZSh0aGlzLnByb2plY3QsIGAuZ2l0aHViL3dvcmtmbG93cy8ke2ZpbGVOYW1lfWAsIHtcbiAgICAgICAgb2JqOiAoKSA9PiB0aGlzLnJlbmRlcldvcmtmbG93KCksXG4gICAgICAgIC8vIEdpdEh1YiBuZWVkcyB0byByZWFkIHRoZSBmaWxlIGZyb20gdGhlIHJlcG9zaXRvcnkgaW4gb3JkZXIgdG8gd29yay5cbiAgICAgICAgY29tbWl0dGVkOiB0cnVlLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBldmVudHMgdG8gdHJpZ2dlcnMgdGhlIHdvcmtmbG93LlxuICAgKlxuICAgKiBAcGFyYW0gZXZlbnRzIFRoZSBldmVudChzKSB0byB0cmlnZ2VyIHRoZSB3b3JrZmxvdy5cbiAgICovXG4gIHB1YmxpYyBvbihldmVudHM6IHdvcmtmbG93cy5UcmlnZ2Vycykge1xuICAgIHRoaXMuZXZlbnRzID0ge1xuICAgICAgLi4udGhpcy5ldmVudHMsXG4gICAgICAuLi5ldmVudHMsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIGEgc2luZ2xlIGpvYiB0byB0aGUgd29ya2Zsb3cuXG4gICAqIEBwYXJhbSBpZCBUaGUgam9iIG5hbWUgKHVuaXF1ZSB3aXRoaW4gdGhlIHdvcmtmbG93KVxuICAgKiBAcGFyYW0gam9iIFRoZSBqb2Igc3BlY2lmaWNhdGlvblxuICAgKi9cbiAgcHVibGljIGFkZEpvYihcbiAgICBpZDogc3RyaW5nLFxuICAgIGpvYjogd29ya2Zsb3dzLkpvYiB8IHdvcmtmbG93cy5Kb2JDYWxsaW5nUmV1c2FibGVXb3JrZmxvdyxcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5hZGRKb2JzKHsgW2lkXTogam9iIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBqb2JzIHRvIHRoZSB3b3JrZmxvdy5cbiAgICpcbiAgICogQHBhcmFtIGpvYnMgSm9icyB0byBhZGQuXG4gICAqL1xuICBwdWJsaWMgYWRkSm9icyhcbiAgICBqb2JzOiBSZWNvcmQ8c3RyaW5nLCB3b3JrZmxvd3MuSm9iIHwgd29ya2Zsb3dzLkpvYkNhbGxpbmdSZXVzYWJsZVdvcmtmbG93PixcbiAgKSB7XG4gICAgdmVyaWZ5Sm9iQ29uc3RyYWludHMoam9icyk7XG4gICAgT2JqZWN0LmFzc2lnbih0aGlzLl9qb2JzLCB7IC4uLmpvYnMgfSk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgc2luZ2xlIGpvYiBmcm9tIHRoZSB3b3JrZmxvdy5cbiAgICogQHBhcmFtIGlkIFRoZSBqb2IgbmFtZSAodW5pcXVlIHdpdGhpbiB0aGUgd29ya2Zsb3cpXG4gICAqL1xuICBwdWJsaWMgZ2V0Sm9iKFxuICAgIGlkOiBzdHJpbmcsXG4gICk6IHdvcmtmbG93cy5Kb2IgfCB3b3JrZmxvd3MuSm9iQ2FsbGluZ1JldXNhYmxlV29ya2Zsb3cge1xuICAgIHJldHVybiB0aGlzLl9qb2JzW2lkXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIGEgc2luZ2xlIGpvYiB0byB0aGUgd29ya2Zsb3cuXG4gICAqIEBwYXJhbSBpZCBUaGUgam9iIG5hbWUgKHVuaXF1ZSB3aXRoaW4gdGhlIHdvcmtmbG93KVxuICAgKi9cbiAgcHVibGljIHVwZGF0ZUpvYihcbiAgICBpZDogc3RyaW5nLFxuICAgIGpvYjogd29ya2Zsb3dzLkpvYiB8IHdvcmtmbG93cy5Kb2JDYWxsaW5nUmV1c2FibGVXb3JrZmxvdyxcbiAgKSB7XG4gICAgdGhpcy51cGRhdGVKb2JzKHsgW2lkXTogam9iIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgam9icyBmb3IgdGhpcyB3b3JrZmxvd1xuICAgKiBEb2VzIGEgY29tcGxldGUgcmVwbGFjZSwgaXQgZG9lcyBub3QgdHJ5IHRvIG1lcmdlIHRoZSBqb2JzXG4gICAqXG4gICAqIEBwYXJhbSBqb2JzIEpvYnMgdG8gdXBkYXRlLlxuICAgKi9cbiAgcHVibGljIHVwZGF0ZUpvYnMoXG4gICAgam9iczogUmVjb3JkPHN0cmluZywgd29ya2Zsb3dzLkpvYiB8IHdvcmtmbG93cy5Kb2JDYWxsaW5nUmV1c2FibGVXb3JrZmxvdz4sXG4gICkge1xuICAgIHZlcmlmeUpvYkNvbnN0cmFpbnRzKGpvYnMpO1xuICAgIE9iamVjdC5hc3NpZ24odGhpcy5fam9icywgeyAuLi5qb2JzIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSBzaW5nbGUgam9iIHRvIHRoZSB3b3JrZmxvdy5cbiAgICogQHBhcmFtIGlkIFRoZSBqb2IgbmFtZSAodW5pcXVlIHdpdGhpbiB0aGUgd29ya2Zsb3cpXG4gICAqL1xuICBwdWJsaWMgcmVtb3ZlSm9iKGlkOiBzdHJpbmcpIHtcbiAgICBkZWxldGUgdGhpcy5fam9ic1tpZF07XG4gIH1cblxuICAvKipcbiAgICogR2V0cyBhIHNpbmdsZSBzdGVwIGZyb20gYSBqb2IgYnkgc3RlcCBJRC5cbiAgICpcbiAgICogVGhlIHJldHVybmVkIG9iamVjdCBpcyBmcm96ZW4gYW5kIHJlYWQtb25seS4gVXNlIGByZXBsYWNlU3RlcGAgb3JcbiAgICogYHBhdGNoU3RlcGAgdG8gbW9kaWZ5IGEgc3RlcC5cbiAgICpcbiAgICogQHBhcmFtIGpvYklkIFRoZSBqb2IgbmFtZSAodW5pcXVlIHdpdGhpbiB0aGUgd29ya2Zsb3cpXG4gICAqIEBwYXJhbSBzdGVwSWQgVGhlIHN0ZXAgSUQgdG8gbG9vayB1cFxuICAgKiBAcmV0dXJucyBBIHJlYWQtb25seSBjb3B5IG9mIHRoZSBzdGVwXG4gICAqL1xuICBwdWJsaWMgZ2V0U3RlcChqb2JJZDogc3RyaW5nLCBzdGVwSWQ6IHN0cmluZyk6IHdvcmtmbG93cy5Kb2JTdGVwIHtcbiAgICBjb25zdCBzdGVwcyA9IHRoaXMucmVzb2x2ZUpvYlN0ZXBzKGpvYklkKTtcbiAgICBjb25zdCBpbmRleCA9IHRoaXMuZmluZFN0ZXBJbmRleChzdGVwcywgc3RlcElkLCBqb2JJZCk7XG4gICAgcmV0dXJuIE9iamVjdC5mcmVlemUoeyAuLi5zdGVwc1tpbmRleF0gfSk7XG4gIH1cblxuICAvKipcbiAgICogQXBwZW5kcyBhIHN0ZXAgdG8gdGhlIGVuZCBvZiBhIGpvYidzIHN0ZXAgbGlzdC5cbiAgICpcbiAgICogQHBhcmFtIGpvYklkIFRoZSBqb2IgbmFtZSAodW5pcXVlIHdpdGhpbiB0aGUgd29ya2Zsb3cpXG4gICAqIEBwYXJhbSBzdGVwIFRoZSBzdGVwIHRvIGFkZC4gTXVzdCBoYXZlIGFuIGBpZGAgc2V0LlxuICAgKi9cbiAgcHVibGljIGFwcGVuZFN0ZXAoam9iSWQ6IHN0cmluZywgc3RlcDogd29ya2Zsb3dzLkpvYlN0ZXApOiB2b2lkIHtcbiAgICBjb25zdCBzdGVwcyA9IHRoaXMucmVzb2x2ZUpvYlN0ZXBzKGpvYklkKTtcbiAgICB0aGlzLnJlcXVpcmVTdGVwSWQoc3RlcCk7XG4gICAgdGhpcy5yZXF1aXJlVW5pcXVlU3RlcElkKHN0ZXBzLCBzdGVwLmlkISwgam9iSWQpO1xuICAgIHN0ZXBzLnB1c2goc3RlcCk7XG4gIH1cblxuICAvKipcbiAgICogUmVwbGFjZXMgYW4gZXhpc3Rpbmcgc3RlcCBpbiBhIGpvYiwgcHJlc2VydmluZyBpdHMgcG9zaXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBqb2JJZCBUaGUgam9iIG5hbWUgKHVuaXF1ZSB3aXRoaW4gdGhlIHdvcmtmbG93KVxuICAgKiBAcGFyYW0gc3RlcElkIFRoZSBJRCBvZiB0aGUgc3RlcCB0byByZXBsYWNlXG4gICAqIEBwYXJhbSByZXBsYWNlbWVudFN0ZXAgVGhlIHJlcGxhY2VtZW50IHN0ZXAuIElmIGBpZGAgaXMgb21pdHRlZCwgaXQgaW5oZXJpdHMgdGhlIG9yaWdpbmFsIHN0ZXAncyBJRC5cbiAgICovXG4gIHB1YmxpYyByZXBsYWNlU3RlcChcbiAgICBqb2JJZDogc3RyaW5nLFxuICAgIHN0ZXBJZDogc3RyaW5nLFxuICAgIHJlcGxhY2VtZW50U3RlcDogd29ya2Zsb3dzLkpvYlN0ZXAsXG4gICk6IHZvaWQge1xuICAgIGNvbnN0IHN0ZXBzID0gdGhpcy5yZXNvbHZlSm9iU3RlcHMoam9iSWQpO1xuICAgIGNvbnN0IGluZGV4ID0gdGhpcy5maW5kU3RlcEluZGV4KHN0ZXBzLCBzdGVwSWQsIGpvYklkKTtcbiAgICBjb25zdCBuZXdTdGVwID0geyAuLi5yZXBsYWNlbWVudFN0ZXAsIGlkOiByZXBsYWNlbWVudFN0ZXAuaWQgPz8gc3RlcElkIH07XG5cbiAgICAvLyBJZiB0aGUgaWQgY2hhbmdlZCwgdmFsaWRhdGUgdW5pcXVlbmVzcyBvZiB0aGUgbmV3IGlkXG4gICAgaWYgKG5ld1N0ZXAuaWQgIT09IHN0ZXBJZCkge1xuICAgICAgdGhpcy5yZXF1aXJlVW5pcXVlU3RlcElkKHN0ZXBzLCBuZXdTdGVwLmlkISwgam9iSWQpO1xuICAgIH1cblxuICAgIHN0ZXBzW2luZGV4XSA9IG5ld1N0ZXA7XG4gIH1cblxuICAvKipcbiAgICogQXBwbGllcyBhIHN1cmdpY2FsIG1vZGlmaWNhdGlvbiB0byBhbiBleGlzdGluZyBzdGVwLlxuICAgKlxuICAgKiBUaGUgcHJvdmlkZWQgcGF0Y2ggaXMgc2hhbGxvdy1tZXJnZWQgb250byB0aGUgZXhpc3Rpbmcgc3RlcC4gRmllbGRzIG5vdFxuICAgKiBwcmVzZW50IGluIHRoZSBwYXRjaCBhcmUgcHJlc2VydmVkIHVuY2hhbmdlZC4gVXNlIGBnZXRTdGVwYCB0byByZWFkIHRoZVxuICAgKiBjdXJyZW50IHN0ZXAgdmFsdWVzIGJlZm9yZSBjb25zdHJ1Y3RpbmcgdGhlIHBhdGNoLlxuICAgKlxuICAgKiBAcGFyYW0gam9iSWQgVGhlIGpvYiBuYW1lICh1bmlxdWUgd2l0aGluIHRoZSB3b3JrZmxvdylcbiAgICogQHBhcmFtIHN0ZXBJZCBUaGUgSUQgb2YgdGhlIHN0ZXAgdG8gcGF0Y2hcbiAgICogQHBhcmFtIHBhdGNoIEEgcGFydGlhbCBzdGVwIG9iamVjdCB3aG9zZSBmaWVsZHMgYXJlIHNoYWxsb3ctbWVyZ2VkIG9udG8gdGh