semantifold 0.8.0 → 0.10.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.
- package/README.md +11 -5
- package/bin/semantifold.js +6 -0
- package/build/bin/semantifold.d.ts +3 -0
- package/build/bin/semantifold.d.ts.map +1 -0
- package/build/bin/semantifold.js +5 -0
- package/build/index.d.ts +9 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -1
- package/build/src/artifact-path.d.ts +16 -0
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +29 -3
- package/build/src/backends/c-check.d.ts +7 -0
- package/build/src/backends/c-check.d.ts.map +1 -0
- package/build/src/backends/c-check.js +65 -0
- package/build/src/backends/check-plan.d.ts +96 -0
- package/build/src/backends/check-plan.d.ts.map +1 -0
- package/build/src/backends/check-plan.js +135 -0
- package/build/src/backends/cpp-check.d.ts +7 -0
- package/build/src/backends/cpp-check.d.ts.map +1 -0
- package/build/src/backends/cpp-check.js +59 -0
- package/build/src/backends/csharp-check.d.ts +7 -0
- package/build/src/backends/csharp-check.d.ts.map +1 -0
- package/build/src/backends/csharp-check.js +125 -0
- package/build/src/backends/csharp.d.ts +1 -0
- package/build/src/backends/csharp.d.ts.map +1 -1
- package/build/src/backends/csharp.js +5 -4
- package/build/src/backends/dart-check.d.ts +7 -0
- package/build/src/backends/dart-check.d.ts.map +1 -0
- package/build/src/backends/dart-check.js +103 -0
- package/build/src/backends/dart.d.ts +2 -0
- package/build/src/backends/dart.d.ts.map +1 -1
- package/build/src/backends/dart.js +5 -5
- package/build/src/backends/go-check.d.ts +7 -0
- package/build/src/backends/go-check.d.ts.map +1 -0
- package/build/src/backends/go-check.js +77 -0
- package/build/src/backends/go.d.ts +1 -0
- package/build/src/backends/go.d.ts.map +1 -1
- package/build/src/backends/go.js +3 -3
- package/build/src/backends/java-check.d.ts +21 -0
- package/build/src/backends/java-check.d.ts.map +1 -0
- package/build/src/backends/java-check.js +73 -0
- package/build/src/backends/javascript-check.d.ts +7 -0
- package/build/src/backends/javascript-check.d.ts.map +1 -0
- package/build/src/backends/javascript-check.js +29 -0
- package/build/src/backends/kotlin-check.d.ts +7 -0
- package/build/src/backends/kotlin-check.d.ts.map +1 -0
- package/build/src/backends/kotlin-check.js +31 -0
- package/build/src/backends/php-check.d.ts +7 -0
- package/build/src/backends/php-check.d.ts.map +1 -0
- package/build/src/backends/php-check.js +21 -0
- package/build/src/backends/program.d.ts +7 -0
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +16 -1
- package/build/src/backends/python-check.d.ts +7 -0
- package/build/src/backends/python-check.d.ts.map +1 -0
- package/build/src/backends/python-check.js +28 -0
- package/build/src/backends/ruby-check.d.ts +7 -0
- package/build/src/backends/ruby-check.d.ts.map +1 -0
- package/build/src/backends/ruby-check.js +21 -0
- package/build/src/backends/rust-check.d.ts +7 -0
- package/build/src/backends/rust-check.d.ts.map +1 -0
- package/build/src/backends/rust-check.js +65 -0
- package/build/src/backends/swift-check.d.ts +7 -0
- package/build/src/backends/swift-check.d.ts.map +1 -0
- package/build/src/backends/swift-check.js +77 -0
- package/build/src/backends/typescript-check.d.ts +7 -0
- package/build/src/backends/typescript-check.d.ts.map +1 -0
- package/build/src/backends/typescript-check.js +33 -0
- package/build/src/backends/zig-check.d.ts +7 -0
- package/build/src/backends/zig-check.d.ts.map +1 -0
- package/build/src/backends/zig-check.js +70 -0
- package/build/src/diagnostic.d.ts +13 -1
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +14 -2
- package/build/src/frontends/program.d.ts +6 -0
- package/build/src/frontends/program.d.ts.map +1 -1
- package/build/src/frontends/program.js +9 -1
- package/build/src/language-registry.d.ts +54 -0
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +107 -5
- package/build/src/project-build.d.ts +172 -0
- package/build/src/project-build.d.ts.map +1 -0
- package/build/src/project-build.js +320 -0
- package/build/src/project-cli.d.ts +40 -0
- package/build/src/project-cli.d.ts.map +1 -0
- package/build/src/project-cli.js +145 -0
- package/build/src/project-manifest.d.ts +148 -0
- package/build/src/project-manifest.d.ts.map +1 -0
- package/build/src/project-manifest.js +432 -0
- package/build/src/project-reporter.d.ts +81 -0
- package/build/src/project-reporter.d.ts.map +1 -0
- package/build/src/project-reporter.js +293 -0
- package/build/src/project-snapshot.d.ts +129 -0
- package/build/src/project-snapshot.d.ts.map +1 -0
- package/build/src/project-snapshot.js +362 -0
- package/build/src/project-watch-reporter.d.ts +125 -0
- package/build/src/project-watch-reporter.d.ts.map +1 -0
- package/build/src/project-watch-reporter.js +280 -0
- package/build/src/project-watch.d.ts +133 -0
- package/build/src/project-watch.d.ts.map +1 -0
- package/build/src/project-watch.js +839 -0
- package/build/src/publication.d.ts +218 -0
- package/build/src/publication.d.ts.map +1 -0
- package/build/src/publication.js +1832 -0
- package/build/src/semantic/types.d.ts +613 -0
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +163 -1
- package/build/src/subprocess.d.ts +8 -0
- package/build/src/subprocess.d.ts.map +1 -1
- package/build/src/subprocess.js +50 -9
- package/build/src/target-check.d.ts +52 -0
- package/build/src/target-check.d.ts.map +1 -0
- package/build/src/target-check.js +796 -0
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +3 -3
- package/docs/architecture.md +3 -1
- package/docs/c.md +4 -0
- package/docs/cpp.md +4 -0
- package/docs/dart.md +4 -0
- package/docs/generated-artifact-publication.md +98 -0
- package/docs/kotlin.md +2 -2
- package/docs/language-support.md +29 -3
- package/docs/project-build-cli.md +147 -0
- package/docs/project-watch.md +60 -0
- package/docs/rust.md +4 -0
- package/docs/swift.md +2 -0
- package/docs/testing.md +70 -3
- package/docs/watch-build-pipeline.md +42 -26
- package/docs/zig.md +2 -0
- package/examples/jsdoc-java-watch/expected-output.txt +3 -0
- package/examples/jsdoc-java-watch/semantifold.json +23 -0
- package/examples/jsdoc-java-watch/src/main.js +21 -0
- package/index.js +9 -0
- package/package.json +6 -1
- package/src/artifact-path.js +31 -2
- package/src/backends/c-check.js +80 -0
- package/src/backends/check-plan.js +147 -0
- package/src/backends/cpp-check.js +73 -0
- package/src/backends/csharp-check.js +139 -0
- package/src/backends/csharp.js +4 -3
- package/src/backends/dart-check.js +117 -0
- package/src/backends/dart.js +4 -4
- package/src/backends/go-check.js +89 -0
- package/src/backends/go.js +2 -2
- package/src/backends/java-check.js +77 -0
- package/src/backends/javascript-check.js +33 -0
- package/src/backends/kotlin-check.js +41 -0
- package/src/backends/php-check.js +23 -0
- package/src/backends/program.js +17 -0
- package/src/backends/python-check.js +30 -0
- package/src/backends/ruby-check.js +23 -0
- package/src/backends/rust-check.js +78 -0
- package/src/backends/swift-check.js +89 -0
- package/src/backends/typescript-check.js +37 -0
- package/src/backends/zig-check.js +81 -0
- package/src/diagnostic.js +9 -1
- package/src/frontends/program.js +9 -0
- package/src/language-registry.js +113 -4
- package/src/project-build.js +353 -0
- package/src/project-cli.js +155 -0
- package/src/project-manifest.js +479 -0
- package/src/project-reporter.js +320 -0
- package/src/project-snapshot.js +403 -0
- package/src/project-watch-reporter.js +303 -0
- package/src/project-watch.js +870 -0
- package/src/publication.js +2071 -0
- package/src/semantic/types.js +184 -0
- package/src/subprocess.js +49 -8
- package/src/target-check.js +844 -0
- package/src/toolchains.js +3 -2
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,1832 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { findPortableArtifactPathConflict, isSafeArtifactPath } from "./artifact-path.js";
|
|
7
|
+
import { createGeneratedArtifactSet } from "./artifacts.js";
|
|
8
|
+
import { finalizeByteMapping } from "./binary-mapping.js";
|
|
9
|
+
import { SemantifoldDiagnostic } from "./diagnostic.js";
|
|
10
|
+
import { finalizeMapping, toSourceMapV3 } from "./mapping.js";
|
|
11
|
+
const activePointerName = "active-generation.json";
|
|
12
|
+
const generationsName = "generations";
|
|
13
|
+
const journalName = ".semantifold-publication-journal";
|
|
14
|
+
const identityPattern = /^[a-z][a-z0-9-]*$/u;
|
|
15
|
+
const generationPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
16
|
+
const buildRolePattern = /^[a-z][a-z0-9-]*$/u;
|
|
17
|
+
const mediaTypePattern = /^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+(?:;[\u0020-\u007e]+)?$/u;
|
|
18
|
+
const targetRoles = new Set(["application", "binary", "text"]);
|
|
19
|
+
const publicationBoundaries = new Set([
|
|
20
|
+
"after-stage",
|
|
21
|
+
"before-pointer-replace",
|
|
22
|
+
"after-pointer-replace",
|
|
23
|
+
"before-cleanup",
|
|
24
|
+
"before-journal-remove"
|
|
25
|
+
]);
|
|
26
|
+
/** @type {WeakMap<GeneratedArtifactPublisher, {boundary: string, error: Error}>} */
|
|
27
|
+
const injectedPublicationFailures = new WeakMap();
|
|
28
|
+
/** @type {WeakMap<GeneratedArtifactPublisher, (operation: string) => void>} */
|
|
29
|
+
const publicationFilesystemObservers = new WeakMap();
|
|
30
|
+
/** @type {WeakMap<GeneratedArtifactPublisher, (() => Promise<void> | void)[]>} */
|
|
31
|
+
const publicationCommitGuards = new WeakMap();
|
|
32
|
+
/**
|
|
33
|
+
* Queues one internal caller-owned currentness guard for the publisher's next transaction.
|
|
34
|
+
* The guard runs after every validator closes and immediately before the active-pointer commit.
|
|
35
|
+
* This helper is intentionally not part of the package root API.
|
|
36
|
+
* @param {GeneratedArtifactPublisher} publisher - Publisher instance.
|
|
37
|
+
* @param {() => Promise<void> | void} guard - Exact pre-commit guard.
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
export function guardNextPublicationCommit(publisher, guard) {
|
|
41
|
+
if (!(publisher instanceof GeneratedArtifactPublisher) || typeof guard != "function") {
|
|
42
|
+
throw new TypeError("Invalid publication commit guard.");
|
|
43
|
+
}
|
|
44
|
+
const queued = publicationCommitGuards.get(publisher) ?? [];
|
|
45
|
+
queued.push(guard);
|
|
46
|
+
publicationCommitGuards.set(publisher, queued);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Installs one internal, one-shot deterministic failure used by real-filesystem interruption specs.
|
|
50
|
+
* This helper is intentionally not part of the package root API.
|
|
51
|
+
* @param {GeneratedArtifactPublisher} publisher - Publisher instance.
|
|
52
|
+
* @param {"after-stage" | "before-pointer-replace" | "after-pointer-replace" | "before-cleanup" | "before-journal-remove"} boundary - Exact lifecycle boundary.
|
|
53
|
+
* @param {Error} error - Injected failure.
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
export function injectPublicationFailure(publisher, boundary, error) {
|
|
57
|
+
if (!(publisher instanceof GeneratedArtifactPublisher) || !publicationBoundaries.has(boundary) || !(error instanceof Error)) {
|
|
58
|
+
throw new TypeError("Invalid publication failure injection.");
|
|
59
|
+
}
|
|
60
|
+
injectedPublicationFailures.set(publisher, { boundary, error });
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Installs an internal deterministic filesystem-operation observer for durability specs.
|
|
64
|
+
* This helper is intentionally not part of the package root API.
|
|
65
|
+
* @param {GeneratedArtifactPublisher} publisher - Publisher instance.
|
|
66
|
+
* @param {(operation: string) => void} observer - Synchronous operation observer.
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
export function observePublicationFilesystem(publisher, observer) {
|
|
70
|
+
if (!(publisher instanceof GeneratedArtifactPublisher) || typeof observer != "function") {
|
|
71
|
+
throw new TypeError("Invalid publication filesystem observer.");
|
|
72
|
+
}
|
|
73
|
+
publicationFilesystemObservers.set(publisher, observer);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Owns transactional publication in one dedicated directory beneath a canonical project root.
|
|
77
|
+
*/
|
|
78
|
+
export class GeneratedArtifactPublisher {
|
|
79
|
+
/** @type {string} */
|
|
80
|
+
#projectId;
|
|
81
|
+
/** @type {string} */
|
|
82
|
+
#projectRoot;
|
|
83
|
+
/** @type {string} */
|
|
84
|
+
#publicationRoot;
|
|
85
|
+
/** @type {readonly string[]} */
|
|
86
|
+
#sourceRoots;
|
|
87
|
+
/** @type {readonly string[]} */
|
|
88
|
+
#sourceFiles;
|
|
89
|
+
/** @type {Promise<void>} */
|
|
90
|
+
#publicationTail = Promise.resolve();
|
|
91
|
+
/**
|
|
92
|
+
* Creates a project-scoped publisher without touching the filesystem.
|
|
93
|
+
* @param {object} options - Publisher identity and owned paths.
|
|
94
|
+
* @param {string} options.projectId - Stable lowercase project identity.
|
|
95
|
+
* @param {string} options.projectRoot - Canonical absolute project root.
|
|
96
|
+
* @param {string} options.publicationRoot - Canonical absolute publisher-owned directory beneath the project root.
|
|
97
|
+
* @param {readonly string[]} [options.sourceRoots] - Canonical absolute project source directories kept disjoint from publication.
|
|
98
|
+
* @param {readonly string[]} [options.sourceFiles] - Canonical absolute project source files kept disjoint from publication.
|
|
99
|
+
*/
|
|
100
|
+
constructor(options) {
|
|
101
|
+
if (!isPlainObject(options)) {
|
|
102
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", "publication", "Publisher options must be a plain object.");
|
|
103
|
+
}
|
|
104
|
+
const { projectId, projectRoot, publicationRoot, sourceFiles = [], sourceRoots = [] } = options;
|
|
105
|
+
if (!identityPattern.test(projectId ?? "")) {
|
|
106
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", "publication", "Project identity must be a stable lowercase ID.");
|
|
107
|
+
}
|
|
108
|
+
if (!isCanonicalAbsolutePath(projectRoot) || !isCanonicalAbsolutePath(publicationRoot) ||
|
|
109
|
+
!isStrictPathDescendant(projectRoot, publicationRoot)) {
|
|
110
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Project and publication roots must be canonical absolute paths with publication strictly beneath the project root.");
|
|
111
|
+
}
|
|
112
|
+
if (!isDenseArray(sourceRoots) || !isDenseArray(sourceFiles) || sourceRoots.length + sourceFiles.length == 0) {
|
|
113
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Publisher requires canonical absolute source directories or source files.");
|
|
114
|
+
}
|
|
115
|
+
/** @type {string[]} */
|
|
116
|
+
const validatedSourceRoots = [];
|
|
117
|
+
for (let index = 0; index < sourceRoots.length; index += 1) {
|
|
118
|
+
const sourceRoot = sourceRoots[index];
|
|
119
|
+
if (!isCanonicalAbsolutePath(sourceRoot)) {
|
|
120
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Source roots must be a non-empty array of canonical absolute paths.");
|
|
121
|
+
}
|
|
122
|
+
if (!isPathWithin(projectRoot, sourceRoot)) {
|
|
123
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Source roots must remain within the canonical project root.");
|
|
124
|
+
}
|
|
125
|
+
if (pathsOverlap(publicationRoot, sourceRoot)) {
|
|
126
|
+
publicationFailure("PUBLICATION_SOURCE_OVERLAP", projectId, "Publication root and source roots must be disjoint.");
|
|
127
|
+
}
|
|
128
|
+
validatedSourceRoots.push(sourceRoot);
|
|
129
|
+
}
|
|
130
|
+
/** @type {string[]} */
|
|
131
|
+
const validatedSourceFiles = [];
|
|
132
|
+
for (let index = 0; index < sourceFiles.length; index += 1) {
|
|
133
|
+
const sourceFile = sourceFiles[index];
|
|
134
|
+
if (!isCanonicalAbsolutePath(sourceFile) || !isPathWithin(projectRoot, sourceFile)) {
|
|
135
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Source files must be canonical absolute paths within the project root.");
|
|
136
|
+
}
|
|
137
|
+
if (pathsOverlap(publicationRoot, sourceFile)) {
|
|
138
|
+
publicationFailure("PUBLICATION_SOURCE_OVERLAP", projectId, "Publication root and source files must be disjoint.");
|
|
139
|
+
}
|
|
140
|
+
validatedSourceFiles.push(sourceFile);
|
|
141
|
+
}
|
|
142
|
+
this.#projectId = projectId;
|
|
143
|
+
this.#projectRoot = projectRoot;
|
|
144
|
+
this.#publicationRoot = publicationRoot;
|
|
145
|
+
this.#sourceRoots = Object.freeze(validatedSourceRoots);
|
|
146
|
+
this.#sourceFiles = Object.freeze(validatedSourceFiles);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Stages, validates, and publishes one complete immutable project generation.
|
|
150
|
+
* Calls on one publisher instance are serialized in invocation order.
|
|
151
|
+
* @param {import("./semantic/types.js").PublicationRequest} request - Complete publication request.
|
|
152
|
+
* @param {import("./semantic/types.js").PublicationOptions} [options] - Transaction lifecycle controls.
|
|
153
|
+
* @returns {Promise<import("./semantic/types.js").PublishedGeneration>} Committed immutable snapshot.
|
|
154
|
+
*/
|
|
155
|
+
publish(request, options = {}) {
|
|
156
|
+
const signal = validatePublicationOptions(options, this.#projectId);
|
|
157
|
+
const queuedGuards = publicationCommitGuards.get(this);
|
|
158
|
+
const commitGuard = queuedGuards?.shift();
|
|
159
|
+
if (queuedGuards?.length == 0)
|
|
160
|
+
publicationCommitGuards.delete(this);
|
|
161
|
+
/** @type {(value: import("./semantic/types.js").PublishedGeneration) => void} */
|
|
162
|
+
let resolveResult;
|
|
163
|
+
/** @type {(reason?: unknown) => void} */
|
|
164
|
+
let rejectResult;
|
|
165
|
+
const result = new Promise((resolve, reject) => {
|
|
166
|
+
resolveResult = resolve;
|
|
167
|
+
rejectResult = reject;
|
|
168
|
+
});
|
|
169
|
+
const operation = this.#publicationTail.then(async () => {
|
|
170
|
+
try {
|
|
171
|
+
resolveResult(await this.#publish(request, signal, commitGuard));
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
rejectResult(error);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
this.#publicationTail = operation.catch(() => { });
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Resolves the active pointer exactly once and verifies the referenced immutable generation.
|
|
182
|
+
* @returns {Promise<import("./semantic/types.js").PublishedGeneration>} Verified pointer-once snapshot.
|
|
183
|
+
*/
|
|
184
|
+
async resolveActive() {
|
|
185
|
+
await this.#prepareRoot();
|
|
186
|
+
const pointerPath = path.join(this.#publicationRoot, activePointerName);
|
|
187
|
+
const pointer = await readActivePointer(pointerPath, this.#projectId, false);
|
|
188
|
+
if (!pointer)
|
|
189
|
+
publicationFailure("ACTIVE_GENERATION_MISSING", this.#projectId, "No active generation has been published.");
|
|
190
|
+
const generation = await verifyPublishedGeneration(this.#publicationRoot, this.#projectId, pointer, false);
|
|
191
|
+
const cleanupPending = await hasCommittedCleanupJournal(this.#publicationRoot, this.#projectId, pointer.generationId);
|
|
192
|
+
return publishedGeneration(this.#publicationRoot, generation.manifest, cleanupPending);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Performs one serialized publication.
|
|
196
|
+
* @param {import("./semantic/types.js").PublicationRequest} request - Complete request.
|
|
197
|
+
* @param {AbortSignal | undefined} signal - Optional transaction cancellation authority.
|
|
198
|
+
* @param {(() => Promise<void> | void) | undefined} commitGuard - Optional currentness guard.
|
|
199
|
+
* @returns {Promise<import("./semantic/types.js").PublishedGeneration>} Publication result.
|
|
200
|
+
*/
|
|
201
|
+
async #publish(request, signal, commitGuard) {
|
|
202
|
+
const validated = validatePublicationRequest(request, this.#projectId);
|
|
203
|
+
assertPublicationActive(signal, this.#projectId);
|
|
204
|
+
await this.#prepareRoot();
|
|
205
|
+
await reconcilePublication(this.#publicationRoot, this.#projectId, this);
|
|
206
|
+
await verifyAtomicReplacement(this.#publicationRoot, this.#projectId);
|
|
207
|
+
await validateExistingPointer(this.#publicationRoot, this.#projectId);
|
|
208
|
+
const generationPath = path.join(this.#publicationRoot, generationsName, validated.generationId);
|
|
209
|
+
const manifestPath = path.join(generationPath, "manifest.json");
|
|
210
|
+
const tempPointerName = `.active-generation.${validated.generationId}.tmp`;
|
|
211
|
+
const tempPointerPath = path.join(this.#publicationRoot, tempPointerName);
|
|
212
|
+
const journalPath = path.join(this.#publicationRoot, journalName, `${validated.generationId}.json`);
|
|
213
|
+
const retainedGeneration = await optionalStatus(generationPath, this.#projectId, "retained generation") !== null;
|
|
214
|
+
let committed = false;
|
|
215
|
+
let journalCreated = false;
|
|
216
|
+
let generationCreated = false;
|
|
217
|
+
let tempPointerCreated = false;
|
|
218
|
+
let failureCode = "PUBLICATION_STAGE_WRITE_FAILED";
|
|
219
|
+
/** @type {import("./semantic/types.js").GenerationManifest | null} */
|
|
220
|
+
let manifest = null;
|
|
221
|
+
/** @type {Buffer | null} */
|
|
222
|
+
let manifestBytes = null;
|
|
223
|
+
try {
|
|
224
|
+
await assertAbsent(tempPointerPath, "PUBLICATION_UNOWNED_CONFLICT", this.#projectId, "Temporary active pointer path is already owned or ambiguous.");
|
|
225
|
+
await assertAbsent(journalPath, "PUBLICATION_UNOWNED_CONFLICT", this.#projectId, "Publication journal identity is already owned or ambiguous.");
|
|
226
|
+
if (retainedGeneration) {
|
|
227
|
+
const retained = await verifyRetainedGeneration(generationPath, validated, this.#projectId);
|
|
228
|
+
manifest = retained.manifest;
|
|
229
|
+
manifestBytes = retained.manifestBytes;
|
|
230
|
+
}
|
|
231
|
+
const journal = retainedGeneration
|
|
232
|
+
? {
|
|
233
|
+
generationId: validated.generationId,
|
|
234
|
+
operation: "reactivate",
|
|
235
|
+
projectId: this.#projectId,
|
|
236
|
+
schema: "SemantifoldPublicationJournal",
|
|
237
|
+
tempPointer: tempPointerName,
|
|
238
|
+
version: 2
|
|
239
|
+
}
|
|
240
|
+
: {
|
|
241
|
+
generationId: validated.generationId,
|
|
242
|
+
projectId: this.#projectId,
|
|
243
|
+
schema: "SemantifoldPublicationJournal",
|
|
244
|
+
tempPointer: tempPointerName,
|
|
245
|
+
version: 1
|
|
246
|
+
};
|
|
247
|
+
await writeSyncedFile(journalPath, serializeJson(journal), "wx");
|
|
248
|
+
journalCreated = true;
|
|
249
|
+
await syncDirectory(path.dirname(journalPath));
|
|
250
|
+
if (!retainedGeneration) {
|
|
251
|
+
await mkdir(generationPath, { mode: 0o700 });
|
|
252
|
+
generationCreated = true;
|
|
253
|
+
await syncDirectory(path.dirname(generationPath));
|
|
254
|
+
/** @type {import("./semantic/types.js").GenerationTargetManifest[]} */
|
|
255
|
+
const targetManifests = [];
|
|
256
|
+
for (const target of validated.targets) {
|
|
257
|
+
targetManifests.push(await stageTarget(generationPath, target, this.#projectId));
|
|
258
|
+
}
|
|
259
|
+
const manifestCandidate = /** @type {import("./semantic/types.js").GenerationManifest} */ ({
|
|
260
|
+
generationId: validated.generationId,
|
|
261
|
+
projectId: this.#projectId,
|
|
262
|
+
schema: /** @type {const} */ ("SemantifoldGenerationManifest"),
|
|
263
|
+
targets: targetManifests,
|
|
264
|
+
version: /** @type {const} */ (1)
|
|
265
|
+
});
|
|
266
|
+
manifestBytes = serializeJson(manifestCandidate);
|
|
267
|
+
manifest = validateGenerationManifest(JSON.parse(manifestBytes.toString("utf8")), this.#projectId, validated.generationId);
|
|
268
|
+
await writeSyncedFile(manifestPath, manifestBytes, "wx");
|
|
269
|
+
await syncTreeDirectories(generationPath);
|
|
270
|
+
await verifyManifestFiles(generationPath, manifest, this.#projectId);
|
|
271
|
+
}
|
|
272
|
+
if (manifest === null || manifestBytes === null) {
|
|
273
|
+
publicationFailure("PUBLICATION_STAGE_WRITE_FAILED", this.#projectId, "Candidate generation manifest could not be prepared.");
|
|
274
|
+
}
|
|
275
|
+
reachPublicationBoundary(this, "after-stage");
|
|
276
|
+
failureCode = "PUBLICATION_POINTER_FAILED";
|
|
277
|
+
const pointer = {
|
|
278
|
+
generationId: validated.generationId,
|
|
279
|
+
manifestHash: sha256(manifestBytes),
|
|
280
|
+
projectId: this.#projectId,
|
|
281
|
+
schema: "SemantifoldActiveGeneration",
|
|
282
|
+
version: 1
|
|
283
|
+
};
|
|
284
|
+
await writeSyncedFile(tempPointerPath, serializeJson(pointer), "wx", () => {
|
|
285
|
+
tempPointerCreated = true;
|
|
286
|
+
});
|
|
287
|
+
reachPublicationBoundary(this, "before-pointer-replace");
|
|
288
|
+
await commitGuard?.();
|
|
289
|
+
assertPublicationActive(signal, this.#projectId);
|
|
290
|
+
await rename(tempPointerPath, path.join(this.#publicationRoot, activePointerName));
|
|
291
|
+
tempPointerCreated = false;
|
|
292
|
+
committed = true;
|
|
293
|
+
reachPublicationBoundary(this, "after-pointer-replace");
|
|
294
|
+
await syncDirectory(this.#publicationRoot);
|
|
295
|
+
let cleanupPending = false;
|
|
296
|
+
try {
|
|
297
|
+
reachPublicationBoundary(this, "before-cleanup");
|
|
298
|
+
await removeJournalOwnedState({
|
|
299
|
+
journalPath,
|
|
300
|
+
projectId: this.#projectId,
|
|
301
|
+
publicationRoot: this.#publicationRoot,
|
|
302
|
+
publisher: this
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
catch (_error) {
|
|
306
|
+
cleanupPending = true;
|
|
307
|
+
}
|
|
308
|
+
return publishedGeneration(this.#publicationRoot, deepFreeze(manifest), cleanupPending);
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
if (!committed) {
|
|
312
|
+
try {
|
|
313
|
+
await removeJournalOwnedState({
|
|
314
|
+
...(generationCreated ? { candidatePath: generationPath } : {}),
|
|
315
|
+
...(journalCreated ? { journalPath } : {}),
|
|
316
|
+
projectId: this.#projectId,
|
|
317
|
+
publicationRoot: this.#publicationRoot,
|
|
318
|
+
publisher: this,
|
|
319
|
+
...(tempPointerCreated ? { temporaryPointerPath: tempPointerPath } : {})
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
catch (cleanupError) {
|
|
323
|
+
if (cleanupError instanceof SemantifoldDiagnostic)
|
|
324
|
+
throw cleanupError;
|
|
325
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", this.#projectId, "Failed publication left state that requires recovery.", cleanupError);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
329
|
+
throw error;
|
|
330
|
+
publicationFailure(failureCode, this.#projectId, committed
|
|
331
|
+
? "Active generation changed, but durable pointer publication did not complete."
|
|
332
|
+
: "Candidate generation could not be staged.", error);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Creates and validates owned root directories and source-root separation.
|
|
337
|
+
* @returns {Promise<void>} Completion.
|
|
338
|
+
*/
|
|
339
|
+
async #prepareRoot() {
|
|
340
|
+
await ensureDirectoryWithoutSymlinks(this.#projectRoot, false, this.#projectId);
|
|
341
|
+
await ensureDirectoryWithoutSymlinks(this.#publicationRoot, true, this.#projectId);
|
|
342
|
+
let exactProject;
|
|
343
|
+
let exactPublication;
|
|
344
|
+
try {
|
|
345
|
+
exactProject = await realpath(this.#projectRoot);
|
|
346
|
+
exactPublication = await realpath(this.#publicationRoot);
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", this.#projectId, "Publication root could not be resolved safely.", error);
|
|
350
|
+
}
|
|
351
|
+
if (!isStrictPathDescendant(exactProject, exactPublication)) {
|
|
352
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", this.#projectId, "Publication root must resolve strictly beneath the project root.");
|
|
353
|
+
}
|
|
354
|
+
for (const sourceRoot of this.#sourceRoots) {
|
|
355
|
+
await ensureDirectoryWithoutSymlinks(sourceRoot, false, this.#projectId);
|
|
356
|
+
let exactSource;
|
|
357
|
+
try {
|
|
358
|
+
exactSource = await realpath(sourceRoot);
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", this.#projectId, "Source root could not be resolved safely.", error);
|
|
362
|
+
}
|
|
363
|
+
if (!isPathWithin(exactProject, exactSource)) {
|
|
364
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", this.#projectId, "Source roots must resolve within the project root.");
|
|
365
|
+
}
|
|
366
|
+
if (pathsOverlap(exactPublication, exactSource)) {
|
|
367
|
+
publicationFailure("PUBLICATION_SOURCE_OVERLAP", this.#projectId, "Publication root and source roots resolve to overlapping paths.");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
for (const sourceFile of this.#sourceFiles) {
|
|
371
|
+
await ensureDirectoryWithoutSymlinks(path.dirname(sourceFile), false, this.#projectId);
|
|
372
|
+
let status;
|
|
373
|
+
let exactSource;
|
|
374
|
+
try {
|
|
375
|
+
status = await lstat(sourceFile);
|
|
376
|
+
exactSource = await realpath(sourceFile);
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", this.#projectId, "Source file could not be resolved safely.", error);
|
|
380
|
+
}
|
|
381
|
+
if (status.isSymbolicLink()) {
|
|
382
|
+
publicationFailure("PUBLICATION_SYMLINK_TRAVERSAL", this.#projectId, "Source file traversal encountered a symbolic link.");
|
|
383
|
+
}
|
|
384
|
+
if (!status.isFile() || exactSource != sourceFile || !isPathWithin(exactProject, exactSource)) {
|
|
385
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", this.#projectId, "Source files must resolve to real regular files within the project root.");
|
|
386
|
+
}
|
|
387
|
+
if (pathsOverlap(exactPublication, exactSource)) {
|
|
388
|
+
publicationFailure("PUBLICATION_SOURCE_OVERLAP", this.#projectId, "Publication root and source files resolve to overlapping paths.");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
await ensureOwnedDirectory(path.join(this.#publicationRoot, generationsName), this.#projectId);
|
|
392
|
+
await ensureOwnedDirectory(path.join(this.#publicationRoot, journalName), this.#projectId);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Validates and snapshots transaction lifecycle controls before publication is queued.
|
|
397
|
+
* @param {unknown} options - Candidate options.
|
|
398
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
399
|
+
* @returns {AbortSignal | undefined} Optional cancellation signal.
|
|
400
|
+
*/
|
|
401
|
+
function validatePublicationOptions(options, projectId) {
|
|
402
|
+
if (!isPlainObject(options) || Object.keys(options).some(key => key != "signal") ||
|
|
403
|
+
options.signal !== undefined && !(options.signal instanceof AbortSignal)) {
|
|
404
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, "Publication options require only an optional AbortSignal.");
|
|
405
|
+
}
|
|
406
|
+
return /** @type {AbortSignal | undefined} */ (options.signal);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Rejects cancellation before the atomic active-pointer commit begins.
|
|
410
|
+
* @param {AbortSignal | undefined} signal - Optional transaction cancellation authority.
|
|
411
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
412
|
+
* @returns {void}
|
|
413
|
+
*/
|
|
414
|
+
function assertPublicationActive(signal, projectId) {
|
|
415
|
+
if (signal?.aborted) {
|
|
416
|
+
publicationFailure("PUBLICATION_CANCELLED", projectId, "Publication was cancelled before the active generation pointer commit.");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Throws and consumes a configured internal failure at an exact lifecycle boundary.
|
|
421
|
+
* @param {GeneratedArtifactPublisher} publisher - Active publisher.
|
|
422
|
+
* @param {string} boundary - Reached boundary.
|
|
423
|
+
* @returns {void}
|
|
424
|
+
*/
|
|
425
|
+
function reachPublicationBoundary(publisher, boundary) {
|
|
426
|
+
const injection = injectedPublicationFailures.get(publisher);
|
|
427
|
+
if (injection?.boundary == boundary) {
|
|
428
|
+
injectedPublicationFailures.delete(publisher);
|
|
429
|
+
throw injection.error;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Reports one completed filesystem operation to the internal deterministic seam.
|
|
434
|
+
* @param {GeneratedArtifactPublisher} publisher - Active publisher.
|
|
435
|
+
* @param {string} operation - Completed operation.
|
|
436
|
+
* @returns {void}
|
|
437
|
+
*/
|
|
438
|
+
function observePublicationFilesystemOperation(publisher, operation) {
|
|
439
|
+
publicationFilesystemObservers.get(publisher)?.(operation);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Reconciles only candidates and temporary pointers whose ownership is proven by a strict journal.
|
|
443
|
+
* The active pointer remains the sole commit authority.
|
|
444
|
+
* @param {string} publicationRoot - Publication root.
|
|
445
|
+
* @param {string} projectId - Project identity.
|
|
446
|
+
* @param {GeneratedArtifactPublisher} publisher - Active publisher.
|
|
447
|
+
* @returns {Promise<void>} Completion.
|
|
448
|
+
*/
|
|
449
|
+
async function reconcilePublication(publicationRoot, projectId, publisher) {
|
|
450
|
+
const journals = await readPublicationJournals(publicationRoot, projectId);
|
|
451
|
+
if (journals.length == 0)
|
|
452
|
+
return;
|
|
453
|
+
const pointer = await readActivePointer(path.join(publicationRoot, activePointerName), projectId, true);
|
|
454
|
+
if (pointer)
|
|
455
|
+
await verifyPublishedGeneration(publicationRoot, projectId, pointer, true);
|
|
456
|
+
for (const { journal, journalPath } of journals) {
|
|
457
|
+
const candidatePath = ownedPath(path.join(publicationRoot, generationsName), journal.generationId, projectId);
|
|
458
|
+
const temporaryPointerPath = path.join(publicationRoot, journal.tempPointer);
|
|
459
|
+
const committed = pointer?.generationId == journal.generationId;
|
|
460
|
+
const candidateOwned = journal.version == 1;
|
|
461
|
+
if (candidateOwned && !committed) {
|
|
462
|
+
const candidateStatus = await optionalStatus(candidatePath, projectId, "candidate generation");
|
|
463
|
+
if (candidateStatus) {
|
|
464
|
+
if (!candidateStatus.isDirectory() || candidateStatus.isSymbolicLink()) {
|
|
465
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Journal-owned candidate is not a real directory.");
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const temporaryStatus = await optionalStatus(temporaryPointerPath, projectId, "temporary pointer");
|
|
470
|
+
if (temporaryStatus) {
|
|
471
|
+
if (!temporaryStatus.isFile() || temporaryStatus.isSymbolicLink()) {
|
|
472
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Journal-owned temporary pointer is not a regular file.");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
await removeJournalOwnedState({
|
|
476
|
+
...(candidateOwned && !committed ? { candidatePath } : {}),
|
|
477
|
+
journalPath,
|
|
478
|
+
projectId,
|
|
479
|
+
publicationRoot,
|
|
480
|
+
publisher,
|
|
481
|
+
temporaryPointerPath
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Reads and validates every recovery journal without changing publication state.
|
|
487
|
+
* @param {string} publicationRoot - Publication root.
|
|
488
|
+
* @param {string} projectId - Project identity.
|
|
489
|
+
* @returns {Promise<readonly {journal: PublicationJournal, journalPath: string}[]>} Ordered journals.
|
|
490
|
+
*/
|
|
491
|
+
async function readPublicationJournals(publicationRoot, projectId) {
|
|
492
|
+
const journalDirectory = path.join(publicationRoot, journalName);
|
|
493
|
+
let entries;
|
|
494
|
+
try {
|
|
495
|
+
entries = await readdir(journalDirectory);
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Publication journal directory could not be inspected.", error);
|
|
499
|
+
}
|
|
500
|
+
entries.sort(compareStrings);
|
|
501
|
+
/** @type {{journal: PublicationJournal, journalPath: string}[]} */
|
|
502
|
+
const journals = [];
|
|
503
|
+
for (const entry of entries) {
|
|
504
|
+
if (!entry.endsWith(".json") || !generationPattern.test(entry.slice(0, -5))) {
|
|
505
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Publication journal contains an unrecognized entry.");
|
|
506
|
+
}
|
|
507
|
+
const journalPath = path.join(journalDirectory, entry);
|
|
508
|
+
let journal;
|
|
509
|
+
try {
|
|
510
|
+
const status = await lstat(journalPath);
|
|
511
|
+
if (!status.isFile() || status.isSymbolicLink()) {
|
|
512
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Publication journal entry must be a regular file.");
|
|
513
|
+
}
|
|
514
|
+
journal = JSON.parse(await readFile(journalPath, "utf8"));
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
518
|
+
throw error;
|
|
519
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Publication journal entry is not valid JSON.", error);
|
|
520
|
+
}
|
|
521
|
+
if (!isPublicationJournal(journal) || journal.projectId != projectId || `${journal.generationId}.json` != entry) {
|
|
522
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Publication journal entry does not match the versioned project contract.");
|
|
523
|
+
}
|
|
524
|
+
journals.push({ journal, journalPath });
|
|
525
|
+
}
|
|
526
|
+
return Object.freeze(journals);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Reports committed cleanup state while failing closed on every journal entry.
|
|
530
|
+
* @param {string} publicationRoot - Publication root.
|
|
531
|
+
* @param {string} projectId - Project identity.
|
|
532
|
+
* @param {string} generationId - Pointer-selected generation identity.
|
|
533
|
+
* @returns {Promise<boolean>} Whether committed cleanup remains.
|
|
534
|
+
*/
|
|
535
|
+
async function hasCommittedCleanupJournal(publicationRoot, projectId, generationId) {
|
|
536
|
+
const journals = await readPublicationJournals(publicationRoot, projectId);
|
|
537
|
+
return journals.some(({ journal }) => journal.generationId == generationId);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Removes proven journal-owned state and durably synchronizes each parent before discarding its ownership proof.
|
|
541
|
+
* @param {object} options - Exact cleanup state.
|
|
542
|
+
* @param {string} [options.candidatePath] - Proven candidate generation path.
|
|
543
|
+
* @param {string} [options.temporaryPointerPath] - Proven temporary pointer path.
|
|
544
|
+
* @param {string} [options.journalPath] - Ownership journal path.
|
|
545
|
+
* @param {string} options.projectId - Project identity.
|
|
546
|
+
* @param {string} options.publicationRoot - Publication root.
|
|
547
|
+
* @param {GeneratedArtifactPublisher} options.publisher - Active publisher.
|
|
548
|
+
* @returns {Promise<void>} Completion.
|
|
549
|
+
*/
|
|
550
|
+
async function removeJournalOwnedState({ candidatePath, journalPath, projectId, publicationRoot, publisher, temporaryPointerPath }) {
|
|
551
|
+
try {
|
|
552
|
+
if (candidatePath) {
|
|
553
|
+
await rm(candidatePath, { force: true, recursive: true });
|
|
554
|
+
observePublicationFilesystemOperation(publisher, "remove-candidate");
|
|
555
|
+
await syncDirectory(path.join(publicationRoot, generationsName));
|
|
556
|
+
observePublicationFilesystemOperation(publisher, "sync-candidate-parent");
|
|
557
|
+
}
|
|
558
|
+
if (temporaryPointerPath) {
|
|
559
|
+
await rm(temporaryPointerPath, { force: true });
|
|
560
|
+
observePublicationFilesystemOperation(publisher, "remove-temporary-pointer");
|
|
561
|
+
await syncDirectory(publicationRoot);
|
|
562
|
+
observePublicationFilesystemOperation(publisher, "sync-temporary-pointer-parent");
|
|
563
|
+
}
|
|
564
|
+
if (journalPath) {
|
|
565
|
+
reachPublicationBoundary(publisher, "before-journal-remove");
|
|
566
|
+
await rm(journalPath);
|
|
567
|
+
observePublicationFilesystemOperation(publisher, "remove-journal");
|
|
568
|
+
await syncDirectory(path.dirname(journalPath));
|
|
569
|
+
observePublicationFilesystemOperation(publisher, "sync-journal-parent");
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
574
|
+
throw error;
|
|
575
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, "Journal-owned publication state could not be durably reconciled.", error);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Reads optional metadata without following symbolic links.
|
|
580
|
+
* @param {string} filename - Exact owned path.
|
|
581
|
+
* @param {string} projectId - Project identity.
|
|
582
|
+
* @param {string} subject - Stable subject description.
|
|
583
|
+
* @returns {Promise<import("node:fs").Stats | null>} Status or absence.
|
|
584
|
+
*/
|
|
585
|
+
async function optionalStatus(filename, projectId, subject) {
|
|
586
|
+
try {
|
|
587
|
+
return await lstat(filename);
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
if (isErrorCode(error, "ENOENT"))
|
|
591
|
+
return null;
|
|
592
|
+
publicationFailure("PUBLICATION_RECOVERY_FAILED", projectId, `Journal-owned ${subject} could not be inspected.`, error);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Validates and snapshots one publication request before staging.
|
|
597
|
+
* @param {unknown} request - Request candidate.
|
|
598
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
599
|
+
* @returns {ValidatedPublicationRequest} Validated request.
|
|
600
|
+
*/
|
|
601
|
+
function validatePublicationRequest(request, projectId) {
|
|
602
|
+
if (!isPlainObject(request)) {
|
|
603
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, "Publication requires a unique generation ID and a non-empty ordered target array.");
|
|
604
|
+
}
|
|
605
|
+
const generationId = request.generationId;
|
|
606
|
+
const requestedTargets = request.targets;
|
|
607
|
+
if (typeof generationId != "string" || !generationPattern.test(generationId) ||
|
|
608
|
+
!isDenseArray(requestedTargets) || requestedTargets.length == 0) {
|
|
609
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, "Publication requires a unique generation ID and a non-empty ordered target array.");
|
|
610
|
+
}
|
|
611
|
+
/** @type {ValidatedPublicationTarget[]} */
|
|
612
|
+
const targets = [];
|
|
613
|
+
const targetIds = new Set();
|
|
614
|
+
/** @type {string[]} */
|
|
615
|
+
const projections = [];
|
|
616
|
+
/** @type {string[]} */
|
|
617
|
+
const completeArtifactPaths = [];
|
|
618
|
+
for (let index = 0; index < requestedTargets.length; index += 1) {
|
|
619
|
+
const target = requestedTargets[index];
|
|
620
|
+
if (!isPlainObject(target)) {
|
|
621
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, `Target ${index} requires a stable lowercase identity.`);
|
|
622
|
+
}
|
|
623
|
+
const targetId = target.id;
|
|
624
|
+
const role = target.role;
|
|
625
|
+
const sourceProjection = target.sourceProjection;
|
|
626
|
+
const buildProjection = target.buildProjection;
|
|
627
|
+
const artifactSetCandidate = target.artifactSet;
|
|
628
|
+
const validatorsCandidate = target.validators;
|
|
629
|
+
if (typeof targetId != "string" || !identityPattern.test(targetId)) {
|
|
630
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, `Target ${index} requires a stable lowercase identity.`);
|
|
631
|
+
}
|
|
632
|
+
if (typeof role != "string" || !targetRoles.has(role)) {
|
|
633
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, `Target '${targetId}' requires an explicit language-neutral role.`);
|
|
634
|
+
}
|
|
635
|
+
if (targetIds.has(targetId)) {
|
|
636
|
+
publicationFailure("PUBLICATION_PATH_COLLISION", projectId, `Duplicate target identity '${targetId}'.`);
|
|
637
|
+
}
|
|
638
|
+
targetIds.add(targetId);
|
|
639
|
+
if (!isSafeArtifactPath(sourceProjection) || !isSafeArtifactPath(buildProjection)) {
|
|
640
|
+
publicationFailure("INVALID_PUBLICATION_PROJECTION", projectId, `Target '${targetId}' requires canonical relative source and build projections.`);
|
|
641
|
+
}
|
|
642
|
+
projections.push(sourceProjection, buildProjection);
|
|
643
|
+
let artifactSet;
|
|
644
|
+
try {
|
|
645
|
+
artifactSet = createGeneratedArtifactSet(artifactSetCandidate);
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, `Target '${targetId}' does not contain a valid generated artifact set.`, error);
|
|
649
|
+
}
|
|
650
|
+
const validators = validatorsCandidate === undefined ? [] : validatorsCandidate;
|
|
651
|
+
if (!isDenseArray(validators)) {
|
|
652
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, `Target '${targetId}' validators must be a dense function array.`);
|
|
653
|
+
}
|
|
654
|
+
/** @type {import("./semantic/types.js").PublicationValidator[]} */
|
|
655
|
+
const validatedValidators = [];
|
|
656
|
+
for (let validatorIndex = 0; validatorIndex < validators.length; validatorIndex += 1) {
|
|
657
|
+
const validator = validators[validatorIndex];
|
|
658
|
+
if (typeof validator != "function") {
|
|
659
|
+
publicationFailure("INVALID_PUBLICATION_REQUEST", projectId, `Target '${targetId}' validators must be a dense function array.`);
|
|
660
|
+
}
|
|
661
|
+
validatedValidators.push(/** @type {import("./semantic/types.js").PublicationValidator} */ (validator));
|
|
662
|
+
}
|
|
663
|
+
const artifactConflict = findPortableArtifactPathConflict(artifactSet.artifacts.map(({ path: artifactPath }) => artifactPath));
|
|
664
|
+
if (artifactConflict) {
|
|
665
|
+
publicationFailure("PUBLICATION_PATH_COLLISION", projectId, `Target '${targetId}' artifact paths have a portable ${artifactConflict.kind} conflict.`);
|
|
666
|
+
}
|
|
667
|
+
for (const artifact of artifactSet.artifacts) {
|
|
668
|
+
completeArtifactPaths.push(`${sourceProjection}/${artifact.path}`);
|
|
669
|
+
}
|
|
670
|
+
targets.push(Object.freeze({
|
|
671
|
+
artifactSet,
|
|
672
|
+
buildProjection,
|
|
673
|
+
id: targetId,
|
|
674
|
+
role: /** @type {import("./semantic/types.js").PublicationTargetRole} */ (role),
|
|
675
|
+
sourceProjection,
|
|
676
|
+
validators: Object.freeze(validatedValidators)
|
|
677
|
+
}));
|
|
678
|
+
}
|
|
679
|
+
const projectionConflict = findPortableArtifactPathConflict(projections);
|
|
680
|
+
if (projectionConflict) {
|
|
681
|
+
publicationFailure("PUBLICATION_PROJECTION_OVERLAP", projectId, "Target source/build projections must be distinct and non-nested under portable comparison.");
|
|
682
|
+
}
|
|
683
|
+
if (findPortableArtifactPathConflict([...projections, "manifest.json"])) {
|
|
684
|
+
publicationFailure("INVALID_PUBLICATION_PROJECTION", projectId, "Target source/build projections must not overlap the generation manifest path.");
|
|
685
|
+
}
|
|
686
|
+
const artifactConflict = findPortableArtifactPathConflict(completeArtifactPaths);
|
|
687
|
+
if (artifactConflict) {
|
|
688
|
+
publicationFailure("PUBLICATION_PATH_COLLISION", projectId, "Projected artifact paths collide under portable comparison.");
|
|
689
|
+
}
|
|
690
|
+
return { generationId, targets: Object.freeze(targets) };
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Verifies that one retained immutable generation exactly represents a validator-free request.
|
|
694
|
+
* The retained directory is never modified or removed on either success or failure.
|
|
695
|
+
* @param {string} generationPath - Existing generation path.
|
|
696
|
+
* @param {ValidatedPublicationRequest} request - Validated candidate request.
|
|
697
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
698
|
+
* @returns {Promise<{manifest: import("./semantic/types.js").GenerationManifest, manifestBytes: Buffer}>} Exact retained manifest.
|
|
699
|
+
*/
|
|
700
|
+
async function verifyRetainedGeneration(generationPath, request, projectId) {
|
|
701
|
+
if (request.targets.some(target => target.validators.length != 0)) {
|
|
702
|
+
publicationFailure("PUBLICATION_UNOWNED_CONFLICT", projectId, "Retained generations can be reactivated only for exact validator-free candidates.");
|
|
703
|
+
}
|
|
704
|
+
const targetManifests = request.targets.map(target => Object.freeze({
|
|
705
|
+
artifacts: Object.freeze(target.artifactSet.artifacts.map(artifact => describeGeneratedArtifact(artifact).manifest)),
|
|
706
|
+
buildArtifacts: Object.freeze([]),
|
|
707
|
+
id: target.id,
|
|
708
|
+
...(target.artifactSet.metadata === undefined ? {} : { metadata: target.artifactSet.metadata }),
|
|
709
|
+
projections: Object.freeze({ build: target.buildProjection, source: target.sourceProjection }),
|
|
710
|
+
role: target.role,
|
|
711
|
+
target: target.artifactSet.target
|
|
712
|
+
}));
|
|
713
|
+
const candidate = {
|
|
714
|
+
generationId: request.generationId,
|
|
715
|
+
projectId,
|
|
716
|
+
schema: "SemantifoldGenerationManifest",
|
|
717
|
+
targets: targetManifests,
|
|
718
|
+
version: 1
|
|
719
|
+
};
|
|
720
|
+
const expectedBytes = serializeJson(candidate);
|
|
721
|
+
const manifest = validateGenerationManifest(JSON.parse(expectedBytes.toString("utf8")), projectId, request.generationId);
|
|
722
|
+
let manifestBytes;
|
|
723
|
+
try {
|
|
724
|
+
manifestBytes = await readAndSyncRegularFile(path.join(generationPath, "manifest.json"), projectId, "MALFORMED_GENERATION_MANIFEST", "Retained generation manifest must be a regular file.");
|
|
725
|
+
if (!manifestBytes.equals(expectedBytes)) {
|
|
726
|
+
publicationFailure("PUBLICATION_UNOWNED_CONFLICT", projectId, "Retained generation does not exactly match the requested candidate.");
|
|
727
|
+
}
|
|
728
|
+
await verifyManifestFiles(generationPath, manifest, projectId);
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
if (error instanceof SemantifoldDiagnostic && error.code == "PUBLICATION_UNOWNED_CONFLICT")
|
|
732
|
+
throw error;
|
|
733
|
+
publicationFailure("PUBLICATION_UNOWNED_CONFLICT", projectId, "Retained generation could not be verified as the exact requested candidate.", error);
|
|
734
|
+
}
|
|
735
|
+
return { manifest, manifestBytes };
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Stages one validated target and invokes its ordered validators.
|
|
739
|
+
* @param {string} generationPath - Candidate generation path.
|
|
740
|
+
* @param {ValidatedPublicationTarget} target - Validated target.
|
|
741
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
742
|
+
* @returns {Promise<import("./semantic/types.js").GenerationTargetManifest>} Target manifest.
|
|
743
|
+
*/
|
|
744
|
+
async function stageTarget(generationPath, target, projectId) {
|
|
745
|
+
const sourcePath = ownedPath(generationPath, target.sourceProjection, projectId);
|
|
746
|
+
const buildPath = ownedPath(generationPath, target.buildProjection, projectId);
|
|
747
|
+
await mkdir(sourcePath, { mode: 0o700, recursive: true });
|
|
748
|
+
await mkdir(buildPath, { mode: 0o700, recursive: true });
|
|
749
|
+
/** @type {import("./semantic/types.js").GenerationArtifactManifest[]} */
|
|
750
|
+
const artifacts = [];
|
|
751
|
+
for (const artifact of target.artifactSet.artifacts) {
|
|
752
|
+
const filename = ownedPath(sourcePath, artifact.path, projectId);
|
|
753
|
+
const described = describeGeneratedArtifact(artifact);
|
|
754
|
+
await mkdir(path.dirname(filename), { mode: 0o700, recursive: true });
|
|
755
|
+
try {
|
|
756
|
+
await writeSyncedFile(filename, described.bytes, "wx");
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
760
|
+
throw error;
|
|
761
|
+
publicationFailure("PUBLICATION_STAGE_WRITE_FAILED", projectId, `Generated artifact '${artifact.path}' could not be staged.`, error);
|
|
762
|
+
}
|
|
763
|
+
artifacts.push(described.manifest);
|
|
764
|
+
}
|
|
765
|
+
/** @type {import("./semantic/types.js").PublicationBuildArtifactInput[]} */
|
|
766
|
+
const buildDescriptors = [];
|
|
767
|
+
const context = Object.freeze({ buildPath, sourcePath, targetId: target.id });
|
|
768
|
+
for (let index = 0; index < target.validators.length; index += 1) {
|
|
769
|
+
let produced;
|
|
770
|
+
try {
|
|
771
|
+
produced = await target.validators[index](context);
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
publicationFailure("PUBLICATION_VALIDATION_FAILED", projectId, `Validator ${index} failed for target '${target.id}'.`, error);
|
|
775
|
+
}
|
|
776
|
+
if (produced === undefined)
|
|
777
|
+
continue;
|
|
778
|
+
if (!isDenseArray(produced)) {
|
|
779
|
+
publicationFailure("PUBLICATION_VALIDATION_FAILED", projectId, `Validator ${index} returned invalid build-artifact metadata for target '${target.id}'.`);
|
|
780
|
+
}
|
|
781
|
+
for (const descriptor of produced) {
|
|
782
|
+
buildDescriptors.push(validateBuildArtifactDescriptor(descriptor, target.id, projectId));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const descriptorConflict = findPortableArtifactPathConflict(buildDescriptors.map(({ path: artifactPath }) => artifactPath));
|
|
786
|
+
if (descriptorConflict) {
|
|
787
|
+
publicationFailure("PUBLICATION_PATH_COLLISION", projectId, `Target '${target.id}' build artifacts have a portable ${descriptorConflict.kind} conflict.`);
|
|
788
|
+
}
|
|
789
|
+
const stagedBuildFiles = await listRegularFiles(buildPath, projectId);
|
|
790
|
+
const declared = new Set(buildDescriptors.map(({ path: artifactPath }) => artifactPath));
|
|
791
|
+
if (stagedBuildFiles.length != declared.size || stagedBuildFiles.some(filename => !declared.has(filename))) {
|
|
792
|
+
publicationFailure("PUBLICATION_VALIDATION_FAILED", projectId, `Target '${target.id}' validators must declare every staged build artifact exactly once.`);
|
|
793
|
+
}
|
|
794
|
+
/** @type {import("./semantic/types.js").GenerationBuildArtifactManifest[]} */
|
|
795
|
+
const finalizedBuildArtifacts = [];
|
|
796
|
+
for (const descriptor of buildDescriptors) {
|
|
797
|
+
const bytes = await readAndSyncRegularFile(ownedPath(buildPath, descriptor.path, projectId), projectId, "PUBLICATION_STAGE_HASH_MISMATCH", `Build artifact '${descriptor.path}' is not a stable regular file.`);
|
|
798
|
+
finalizedBuildArtifacts.push(Object.freeze({
|
|
799
|
+
byteLength: bytes.byteLength,
|
|
800
|
+
hash: Object.freeze({ algorithm: /** @type {const} */ ("sha256"), value: sha256(bytes) }),
|
|
801
|
+
mediaType: descriptor.mediaType,
|
|
802
|
+
path: descriptor.path,
|
|
803
|
+
role: descriptor.role
|
|
804
|
+
}));
|
|
805
|
+
}
|
|
806
|
+
return Object.freeze({
|
|
807
|
+
artifacts: Object.freeze(artifacts),
|
|
808
|
+
buildArtifacts: Object.freeze(finalizedBuildArtifacts),
|
|
809
|
+
id: target.id,
|
|
810
|
+
...(target.artifactSet.metadata === undefined ? {} : { metadata: target.artifactSet.metadata }),
|
|
811
|
+
projections: Object.freeze({ build: target.buildProjection, source: target.sourceProjection }),
|
|
812
|
+
role: target.role,
|
|
813
|
+
target: target.artifactSet.target
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Derives exact staged bytes and their deterministic generation-manifest record.
|
|
818
|
+
* @param {import("./semantic/types.js").GeneratedSetArtifact} artifact - Validated generated artifact.
|
|
819
|
+
* @returns {{bytes: Buffer, manifest: import("./semantic/types.js").GenerationArtifactManifest}} Bytes and manifest record.
|
|
820
|
+
*/
|
|
821
|
+
function describeGeneratedArtifact(artifact) {
|
|
822
|
+
const bytes = artifact.contentKind == "text"
|
|
823
|
+
? Buffer.from(/** @type {string} */ (artifact.content), "utf8")
|
|
824
|
+
: Buffer.from(/** @type {Uint8Array} */ (artifact.content));
|
|
825
|
+
return {
|
|
826
|
+
bytes,
|
|
827
|
+
manifest: Object.freeze({
|
|
828
|
+
byteLength: bytes.byteLength,
|
|
829
|
+
contentKind: artifact.contentKind,
|
|
830
|
+
hash: Object.freeze({ algorithm: /** @type {const} */ ("sha256"), value: sha256(bytes) }),
|
|
831
|
+
mediaType: artifact.mediaType,
|
|
832
|
+
path: artifact.path,
|
|
833
|
+
provenance: artifact.provenance,
|
|
834
|
+
role: artifact.role
|
|
835
|
+
})
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Validates validator/compiler output metadata.
|
|
840
|
+
* @param {unknown} descriptor - Metadata candidate.
|
|
841
|
+
* @param {string} targetId - Target identity.
|
|
842
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
843
|
+
* @returns {import("./semantic/types.js").PublicationBuildArtifactInput} Validated metadata.
|
|
844
|
+
*/
|
|
845
|
+
function validateBuildArtifactDescriptor(descriptor, targetId, projectId) {
|
|
846
|
+
if (!isPlainObject(descriptor) || !hasExactKeys(descriptor, ["mediaType", "path", "role"])) {
|
|
847
|
+
publicationFailure("PUBLICATION_VALIDATION_FAILED", projectId, `Target '${targetId}' validator returned invalid build-artifact metadata.`);
|
|
848
|
+
}
|
|
849
|
+
const descriptorPath = descriptor.path;
|
|
850
|
+
const mediaType = descriptor.mediaType;
|
|
851
|
+
const role = descriptor.role;
|
|
852
|
+
if (!isSafeArtifactPath(descriptorPath) || typeof mediaType != "string" ||
|
|
853
|
+
!mediaTypePattern.test(mediaType) || /[\r\n]/u.test(mediaType) ||
|
|
854
|
+
typeof role != "string" || !buildRolePattern.test(role)) {
|
|
855
|
+
publicationFailure("PUBLICATION_VALIDATION_FAILED", projectId, `Target '${targetId}' validator returned invalid build-artifact metadata.`);
|
|
856
|
+
}
|
|
857
|
+
return Object.freeze({ mediaType, path: descriptorPath, role });
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Verifies every manifest reference and staged byte before pointer reachability.
|
|
861
|
+
* @param {string} generationPath - Candidate generation path.
|
|
862
|
+
* @param {import("./semantic/types.js").GenerationManifest} manifest - Candidate manifest.
|
|
863
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
864
|
+
* @returns {Promise<void>} Completion.
|
|
865
|
+
*/
|
|
866
|
+
async function verifyManifestFiles(generationPath, manifest, projectId) {
|
|
867
|
+
for (const target of manifest.targets) {
|
|
868
|
+
const sourcePath = ownedPath(generationPath, target.projections.source, projectId);
|
|
869
|
+
const buildPath = ownedPath(generationPath, target.projections.build, projectId);
|
|
870
|
+
const sourceFiles = await listRegularFiles(sourcePath, projectId);
|
|
871
|
+
if (sourceFiles.length != target.artifacts.length ||
|
|
872
|
+
sourceFiles.some(filename => !target.artifacts.some(artifact => artifact.path == filename))) {
|
|
873
|
+
publicationFailure("PUBLICATION_STAGE_HASH_MISMATCH", projectId, `Target '${target.id}' generated-source files do not match its manifest.`);
|
|
874
|
+
}
|
|
875
|
+
for (const artifact of target.artifacts) {
|
|
876
|
+
await verifyManifestFile(sourcePath, artifact, projectId);
|
|
877
|
+
}
|
|
878
|
+
const buildFiles = await listRegularFiles(buildPath, projectId);
|
|
879
|
+
if (buildFiles.length != target.buildArtifacts.length ||
|
|
880
|
+
buildFiles.some(filename => !target.buildArtifacts.some(artifact => artifact.path == filename))) {
|
|
881
|
+
publicationFailure("PUBLICATION_STAGE_HASH_MISMATCH", projectId, `Target '${target.id}' build files do not match its manifest.`);
|
|
882
|
+
}
|
|
883
|
+
for (const artifact of target.buildArtifacts) {
|
|
884
|
+
await verifyManifestFile(buildPath, artifact, projectId);
|
|
885
|
+
}
|
|
886
|
+
try {
|
|
887
|
+
const artifacts = [];
|
|
888
|
+
for (const artifact of target.artifacts) {
|
|
889
|
+
const bytes = await readAndSyncRegularFile(ownedPath(sourcePath, artifact.path, projectId), projectId, "PUBLICATION_STAGE_HASH_MISMATCH", `Manifest artifact '${artifact.path}' is not a stable regular file.`);
|
|
890
|
+
let content;
|
|
891
|
+
if (artifact.contentKind == "text") {
|
|
892
|
+
try {
|
|
893
|
+
content = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
894
|
+
}
|
|
895
|
+
catch (error) {
|
|
896
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, `Text artifact '${artifact.path}' is not valid UTF-8.`, error);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
else
|
|
900
|
+
content = new Uint8Array(bytes);
|
|
901
|
+
artifacts.push({
|
|
902
|
+
content,
|
|
903
|
+
contentKind: artifact.contentKind,
|
|
904
|
+
mediaType: artifact.mediaType,
|
|
905
|
+
ownership: /** @type {const} */ ("generated"),
|
|
906
|
+
path: artifact.path,
|
|
907
|
+
provenance: rehydrateProvenance(artifact.provenance, projectId),
|
|
908
|
+
role: artifact.role
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
createGeneratedArtifactSet({
|
|
912
|
+
artifacts,
|
|
913
|
+
...(target.metadata === undefined ? {} : { metadata: target.metadata }),
|
|
914
|
+
target: target.target
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
catch (error) {
|
|
918
|
+
if (error instanceof SemantifoldDiagnostic && error.code == "MALFORMED_GENERATION_MANIFEST")
|
|
919
|
+
throw error;
|
|
920
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, `Target '${target.id}' artifact provenance does not match its staged bytes.`, error);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
await verifyGenerationInventory(generationPath, manifest, projectId);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Restores JSON-omitted optional mapping fields and verifies persisted mapping projections.
|
|
927
|
+
* @param {import("./semantic/types.js").ArtifactProvenance} provenance - Persisted provenance.
|
|
928
|
+
* @param {string} projectId - Project identity.
|
|
929
|
+
* @returns {import("./semantic/types.js").ArtifactProvenance} Canonical provenance.
|
|
930
|
+
*/
|
|
931
|
+
function rehydrateProvenance(provenance, projectId) {
|
|
932
|
+
if (provenance.kind == "text") {
|
|
933
|
+
const mapping = finalizeMapping(provenance.mapping);
|
|
934
|
+
const sourceMap = toSourceMapV3(mapping);
|
|
935
|
+
const jsonSourceMap = JSON.parse(JSON.stringify(sourceMap));
|
|
936
|
+
if (!sameJsonValue(jsonSourceMap, provenance.sourceMap)) {
|
|
937
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Persisted Source Map projection contradicts its rich mapping.");
|
|
938
|
+
}
|
|
939
|
+
return Object.freeze({
|
|
940
|
+
kind: /** @type {const} */ ("text"),
|
|
941
|
+
mapping,
|
|
942
|
+
sourceMap,
|
|
943
|
+
...(provenance.sourceMapFilename === undefined ? {} : { sourceMapFilename: provenance.sourceMapFilename })
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
if (provenance.kind == "bytes") {
|
|
947
|
+
return Object.freeze({ kind: /** @type {const} */ ("bytes"), mapping: finalizeByteMapping(provenance.mapping) });
|
|
948
|
+
}
|
|
949
|
+
return provenance;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Verifies one referenced file.
|
|
953
|
+
* @param {string} projectionPath - Owning projection path.
|
|
954
|
+
* @param {{byteLength: number, hash: import("./semantic/types.js").GenerationContentHash, path: string}} artifact - Manifest record.
|
|
955
|
+
* @param {string} projectId - Project diagnostic identity.
|
|
956
|
+
* @returns {Promise<Buffer>} Exact verified bytes.
|
|
957
|
+
*/
|
|
958
|
+
async function verifyManifestFile(projectionPath, artifact, projectId) {
|
|
959
|
+
const bytes = await readAndSyncRegularFile(ownedPath(projectionPath, artifact.path, projectId), projectId, "PUBLICATION_STAGE_HASH_MISMATCH", `Manifest artifact '${artifact.path}' is not a stable regular file.`);
|
|
960
|
+
if (bytes.byteLength != artifact.byteLength || sha256(bytes) != artifact.hash.value) {
|
|
961
|
+
publicationFailure("PUBLICATION_STAGE_HASH_MISMATCH", projectId, `Manifest artifact '${artifact.path}' bytes do not match its recorded hash.`);
|
|
962
|
+
}
|
|
963
|
+
return bytes;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Validates an existing active pointer before a new candidate is staged.
|
|
967
|
+
* @param {string} publicationRoot - Publication root.
|
|
968
|
+
* @param {string} projectId - Project identity.
|
|
969
|
+
* @returns {Promise<void>} Completion.
|
|
970
|
+
*/
|
|
971
|
+
async function validateExistingPointer(publicationRoot, projectId) {
|
|
972
|
+
const pointerPath = path.join(publicationRoot, activePointerName);
|
|
973
|
+
const pointer = await readActivePointer(pointerPath, projectId, true);
|
|
974
|
+
if (pointer)
|
|
975
|
+
await verifyPublishedGeneration(publicationRoot, projectId, pointer, false);
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Reads and strictly validates one active pointer.
|
|
979
|
+
* @param {string} pointerPath - Pointer filename.
|
|
980
|
+
* @param {string} projectId - Expected project identity.
|
|
981
|
+
* @param {boolean} optional - Whether absence is allowed.
|
|
982
|
+
* @returns {Promise<ActivePointer | null>} Pointer or absent value.
|
|
983
|
+
*/
|
|
984
|
+
async function readActivePointer(pointerPath, projectId, optional) {
|
|
985
|
+
let pointerBytes;
|
|
986
|
+
try {
|
|
987
|
+
const status = await lstat(pointerPath);
|
|
988
|
+
if (!status.isFile() || status.isSymbolicLink()) {
|
|
989
|
+
publicationFailure("MALFORMED_ACTIVE_GENERATION", projectId, "Active-generation pointer must be a regular file.");
|
|
990
|
+
}
|
|
991
|
+
pointerBytes = await readFile(pointerPath);
|
|
992
|
+
}
|
|
993
|
+
catch (error) {
|
|
994
|
+
if (optional && isErrorCode(error, "ENOENT"))
|
|
995
|
+
return null;
|
|
996
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
997
|
+
throw error;
|
|
998
|
+
publicationFailure(optional ? "MALFORMED_ACTIVE_GENERATION" : "ACTIVE_GENERATION_MISSING", projectId, optional ? "Active-generation pointer could not be read." : "No active generation has been published.", error);
|
|
999
|
+
}
|
|
1000
|
+
let value;
|
|
1001
|
+
try {
|
|
1002
|
+
value = JSON.parse(pointerBytes.toString("utf8"));
|
|
1003
|
+
}
|
|
1004
|
+
catch (error) {
|
|
1005
|
+
publicationFailure("MALFORMED_ACTIVE_GENERATION", projectId, "Active-generation pointer is not valid JSON.", error);
|
|
1006
|
+
}
|
|
1007
|
+
if (!isActivePointer(value) || value.projectId != projectId) {
|
|
1008
|
+
publicationFailure("MALFORMED_ACTIVE_GENERATION", projectId, "Active-generation pointer does not match the versioned project contract.");
|
|
1009
|
+
}
|
|
1010
|
+
return value;
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Verifies a pointer-selected generation without consulting the pointer again.
|
|
1014
|
+
* @param {string} publicationRoot - Publication root.
|
|
1015
|
+
* @param {string} projectId - Project identity.
|
|
1016
|
+
* @param {ActivePointer} pointer - Pointer snapshot.
|
|
1017
|
+
* @param {boolean} cleanupPending - Recovery state.
|
|
1018
|
+
* @returns {Promise<import("./semantic/types.js").PublishedGeneration>} Verified generation.
|
|
1019
|
+
*/
|
|
1020
|
+
async function verifyPublishedGeneration(publicationRoot, projectId, pointer, cleanupPending) {
|
|
1021
|
+
const generationPath = ownedPath(path.join(publicationRoot, generationsName), pointer.generationId, projectId);
|
|
1022
|
+
const manifestPath = path.join(generationPath, "manifest.json");
|
|
1023
|
+
let manifestBytes;
|
|
1024
|
+
try {
|
|
1025
|
+
manifestBytes = await readAndSyncRegularFile(manifestPath, projectId, "MALFORMED_GENERATION_MANIFEST", "Generation manifest must be a regular file.");
|
|
1026
|
+
}
|
|
1027
|
+
catch (error) {
|
|
1028
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1029
|
+
throw error;
|
|
1030
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest could not be read.", error);
|
|
1031
|
+
}
|
|
1032
|
+
if (sha256(manifestBytes) != pointer.manifestHash) {
|
|
1033
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest hash does not match the active pointer.");
|
|
1034
|
+
}
|
|
1035
|
+
let value;
|
|
1036
|
+
try {
|
|
1037
|
+
value = JSON.parse(manifestBytes.toString("utf8"));
|
|
1038
|
+
}
|
|
1039
|
+
catch (error) {
|
|
1040
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest is not valid JSON.", error);
|
|
1041
|
+
}
|
|
1042
|
+
const manifest = validateGenerationManifest(value, projectId, pointer.generationId);
|
|
1043
|
+
await verifyManifestFiles(generationPath, manifest, projectId);
|
|
1044
|
+
return publishedGeneration(publicationRoot, manifest, cleanupPending);
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Strictly validates a persisted generation manifest.
|
|
1048
|
+
* @param {unknown} value - Parsed manifest value.
|
|
1049
|
+
* @param {string} projectId - Expected project identity.
|
|
1050
|
+
* @param {string} generationId - Expected generation identity.
|
|
1051
|
+
* @returns {import("./semantic/types.js").GenerationManifest} Frozen manifest.
|
|
1052
|
+
*/
|
|
1053
|
+
function validateGenerationManifest(value, projectId, generationId) {
|
|
1054
|
+
if (!isPlainObject(value) || !hasExactKeys(value, ["generationId", "projectId", "schema", "targets", "version"]) ||
|
|
1055
|
+
value.schema != "SemantifoldGenerationManifest" || value.version != 1 || value.projectId != projectId ||
|
|
1056
|
+
value.generationId != generationId || !isDenseArray(value.targets) || value.targets.length == 0) {
|
|
1057
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest does not match the versioned project contract.");
|
|
1058
|
+
}
|
|
1059
|
+
/** @type {import("./semantic/types.js").GenerationTargetManifest[]} */
|
|
1060
|
+
const targets = [];
|
|
1061
|
+
/** @type {string[]} */
|
|
1062
|
+
const projections = [];
|
|
1063
|
+
const ids = new Set();
|
|
1064
|
+
for (const target of value.targets) {
|
|
1065
|
+
const targetKeys = target && typeof target == "object" && "metadata" in target
|
|
1066
|
+
? ["artifacts", "buildArtifacts", "id", "metadata", "projections", "role", "target"]
|
|
1067
|
+
: ["artifacts", "buildArtifacts", "id", "projections", "role", "target"];
|
|
1068
|
+
if (!isPlainObject(target) || !hasExactKeys(target, targetKeys) ||
|
|
1069
|
+
!identityPattern.test(typeof target.id == "string" ? target.id : "") || ids.has(target.id) ||
|
|
1070
|
+
typeof target.role != "string" || !targetRoles.has(target.role) ||
|
|
1071
|
+
typeof target.target != "string" || !identityPattern.test(target.target) ||
|
|
1072
|
+
!isPlainObject(target.projections) || !hasExactKeys(target.projections, ["build", "source"]) ||
|
|
1073
|
+
!isSafeArtifactPath(target.projections.source) || !isSafeArtifactPath(target.projections.build) ||
|
|
1074
|
+
!isDenseArray(target.artifacts) || target.artifacts.length == 0 || !isDenseArray(target.buildArtifacts)) {
|
|
1075
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest contains an invalid target record.");
|
|
1076
|
+
}
|
|
1077
|
+
const targetId = /** @type {string} */ (target.id);
|
|
1078
|
+
ids.add(targetId);
|
|
1079
|
+
projections.push(target.projections.source, target.projections.build);
|
|
1080
|
+
const artifacts = target.artifacts.map(artifact => validatePersistedArtifact(artifact, true, projectId));
|
|
1081
|
+
const buildArtifacts = target.buildArtifacts.map(artifact => validatePersistedArtifact(artifact, false, projectId));
|
|
1082
|
+
if (findPortableArtifactPathConflict(artifacts.map(({ path: artifactPath }) => artifactPath)) ||
|
|
1083
|
+
findPortableArtifactPathConflict(buildArtifacts.map(({ path: artifactPath }) => artifactPath))) {
|
|
1084
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest contains colliding artifact paths.");
|
|
1085
|
+
}
|
|
1086
|
+
targets.push(Object.freeze({
|
|
1087
|
+
artifacts: Object.freeze(/** @type {import("./semantic/types.js").GenerationArtifactManifest[]} */ (artifacts)),
|
|
1088
|
+
buildArtifacts: Object.freeze(/** @type {import("./semantic/types.js").GenerationBuildArtifactManifest[]} */ (buildArtifacts)),
|
|
1089
|
+
id: targetId,
|
|
1090
|
+
...(target.metadata === undefined ? {} : { metadata: deepFreeze(/** @type {Record<string, unknown>} */ (target.metadata)) }),
|
|
1091
|
+
projections: Object.freeze({ build: target.projections.build, source: target.projections.source }),
|
|
1092
|
+
role: /** @type {import("./semantic/types.js").PublicationTargetRole} */ (target.role),
|
|
1093
|
+
target: target.target
|
|
1094
|
+
}));
|
|
1095
|
+
}
|
|
1096
|
+
if (findPortableArtifactPathConflict(projections) ||
|
|
1097
|
+
findPortableArtifactPathConflict([...projections, "manifest.json"])) {
|
|
1098
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest contains overlapping projections.");
|
|
1099
|
+
}
|
|
1100
|
+
return Object.freeze({
|
|
1101
|
+
generationId,
|
|
1102
|
+
projectId,
|
|
1103
|
+
schema: /** @type {const} */ ("SemantifoldGenerationManifest"),
|
|
1104
|
+
targets: Object.freeze(targets),
|
|
1105
|
+
version: /** @type {const} */ (1)
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Validates one persisted artifact record.
|
|
1110
|
+
* @param {unknown} value - Record candidate.
|
|
1111
|
+
* @param {boolean} generated - Whether this is a generated-source artifact.
|
|
1112
|
+
* @param {string} projectId - Project identity.
|
|
1113
|
+
* @returns {import("./semantic/types.js").GenerationArtifactManifest | import("./semantic/types.js").GenerationBuildArtifactManifest} Frozen record.
|
|
1114
|
+
*/
|
|
1115
|
+
function validatePersistedArtifact(value, generated, projectId) {
|
|
1116
|
+
const keys = generated
|
|
1117
|
+
? ["byteLength", "contentKind", "hash", "mediaType", "path", "provenance", "role"]
|
|
1118
|
+
: ["byteLength", "hash", "mediaType", "path", "role"];
|
|
1119
|
+
if (!isPlainObject(value) || !hasExactKeys(value, keys) || !isSafeArtifactPath(value.path) ||
|
|
1120
|
+
typeof value.byteLength != "number" || !Number.isSafeInteger(value.byteLength) || value.byteLength < 0 || typeof value.mediaType != "string" ||
|
|
1121
|
+
!mediaTypePattern.test(value.mediaType) || typeof value.role != "string" ||
|
|
1122
|
+
!(generated ? ["entry", "source", "manifest", "support", "mapping", "resource", "loader"].includes(value.role) :
|
|
1123
|
+
buildRolePattern.test(value.role)) ||
|
|
1124
|
+
(generated && value.contentKind != "text" && value.contentKind != "binary") ||
|
|
1125
|
+
(generated && !isPlainObject(value.provenance)) ||
|
|
1126
|
+
!isPlainObject(value.hash) || !hasExactKeys(value.hash, ["algorithm", "value"]) || value.hash.algorithm != "sha256" ||
|
|
1127
|
+
typeof value.hash.value != "string" || !/^[a-f0-9]{64}$/u.test(value.hash.value)) {
|
|
1128
|
+
publicationFailure("MALFORMED_GENERATION_MANIFEST", projectId, "Generation manifest contains an invalid artifact record.");
|
|
1129
|
+
}
|
|
1130
|
+
return Object.freeze(/** @type {import("./semantic/types.js").GenerationArtifactManifest | import("./semantic/types.js").GenerationBuildArtifactManifest} */ ({
|
|
1131
|
+
byteLength: value.byteLength,
|
|
1132
|
+
...(generated ? { contentKind: value.contentKind } : {}),
|
|
1133
|
+
hash: Object.freeze({ algorithm: /** @type {const} */ ("sha256"), value: value.hash.value }),
|
|
1134
|
+
mediaType: value.mediaType,
|
|
1135
|
+
path: value.path,
|
|
1136
|
+
...(generated ? { provenance: deepFreeze(/** @type {import("./semantic/types.js").ArtifactProvenance} */ (value.provenance)) } : {}),
|
|
1137
|
+
role: value.role
|
|
1138
|
+
}));
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Constructs a frozen resolved-generation result.
|
|
1142
|
+
* @param {string} publicationRoot - Publication root.
|
|
1143
|
+
* @param {import("./semantic/types.js").GenerationManifest} manifest - Verified manifest.
|
|
1144
|
+
* @param {boolean} cleanupPending - Whether cleanup remains.
|
|
1145
|
+
* @returns {import("./semantic/types.js").PublishedGeneration} Frozen result.
|
|
1146
|
+
*/
|
|
1147
|
+
function publishedGeneration(publicationRoot, manifest, cleanupPending) {
|
|
1148
|
+
const generationPath = path.join(publicationRoot, generationsName, manifest.generationId);
|
|
1149
|
+
const targets = manifest.targets.map(target => Object.freeze({
|
|
1150
|
+
buildPath: path.join(generationPath, target.projections.build),
|
|
1151
|
+
id: target.id,
|
|
1152
|
+
role: target.role,
|
|
1153
|
+
sourcePath: path.join(generationPath, target.projections.source)
|
|
1154
|
+
}));
|
|
1155
|
+
return Object.freeze({
|
|
1156
|
+
cleanupPending,
|
|
1157
|
+
generationId: manifest.generationId,
|
|
1158
|
+
generationPath,
|
|
1159
|
+
manifest,
|
|
1160
|
+
manifestPath: path.join(generationPath, "manifest.json"),
|
|
1161
|
+
targets: Object.freeze(targets)
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Probes the exact same-directory regular-file replacement primitive used by the active pointer.
|
|
1166
|
+
* @param {string} publicationRoot - Publication root.
|
|
1167
|
+
* @param {string} projectId - Project identity.
|
|
1168
|
+
* @returns {Promise<void>} Completion.
|
|
1169
|
+
*/
|
|
1170
|
+
async function verifyAtomicReplacement(publicationRoot, projectId) {
|
|
1171
|
+
const identity = randomUUID();
|
|
1172
|
+
const source = path.join(publicationRoot, `.semantifold-atomic-${identity}.source`);
|
|
1173
|
+
const destination = path.join(publicationRoot, `.semantifold-atomic-${identity}.destination`);
|
|
1174
|
+
const expected = Buffer.from("candidate\n");
|
|
1175
|
+
let sourceCreated = false;
|
|
1176
|
+
let destinationCreated = false;
|
|
1177
|
+
try {
|
|
1178
|
+
await writeSyncedFile(source, Buffer.from("candidate\n"), "wx");
|
|
1179
|
+
sourceCreated = true;
|
|
1180
|
+
await writeSyncedFile(destination, Buffer.from("previous\n"), "wx");
|
|
1181
|
+
destinationCreated = true;
|
|
1182
|
+
await rename(source, destination);
|
|
1183
|
+
sourceCreated = false;
|
|
1184
|
+
await syncDirectory(publicationRoot);
|
|
1185
|
+
const destinationStatus = await lstat(destination);
|
|
1186
|
+
const replaced = await readFile(destination);
|
|
1187
|
+
if (!destinationStatus.isFile() || destinationStatus.isSymbolicLink() || !replaced.equals(expected)) {
|
|
1188
|
+
publicationFailure("PUBLICATION_ATOMIC_REPLACE_UNSUPPORTED", projectId, "Filesystem did not provide same-directory regular-file replacement.");
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
await lstat(source);
|
|
1192
|
+
publicationFailure("PUBLICATION_ATOMIC_REPLACE_UNSUPPORTED", projectId, "Filesystem did not remove the replaced temporary name atomically.");
|
|
1193
|
+
}
|
|
1194
|
+
catch (error) {
|
|
1195
|
+
if (!isErrorCode(error, "ENOENT"))
|
|
1196
|
+
throw error;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
catch (error) {
|
|
1200
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1201
|
+
throw error;
|
|
1202
|
+
publicationFailure("PUBLICATION_ATOMIC_REPLACE_UNSUPPORTED", projectId, "Filesystem did not provide same-directory regular-file replacement.", error);
|
|
1203
|
+
}
|
|
1204
|
+
finally {
|
|
1205
|
+
await Promise.allSettled([
|
|
1206
|
+
...(sourceCreated ? [rm(source, { force: true })] : []),
|
|
1207
|
+
...(destinationCreated ? [rm(destination, { force: true })] : [])
|
|
1208
|
+
]);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Writes and synchronizes one newly owned file.
|
|
1213
|
+
* @param {string} filename - Exact filename.
|
|
1214
|
+
* @param {Uint8Array} bytes - Exact bytes.
|
|
1215
|
+
* @param {"wx"} flag - Exclusive creation mode.
|
|
1216
|
+
* @param {() => void} [onCreated] - Called immediately after exclusive creation succeeds.
|
|
1217
|
+
* @returns {Promise<void>} Completion.
|
|
1218
|
+
*/
|
|
1219
|
+
async function writeSyncedFile(filename, bytes, flag, onCreated) {
|
|
1220
|
+
const handle = await open(filename, flag, 0o600);
|
|
1221
|
+
onCreated?.();
|
|
1222
|
+
/** @type {unknown} */
|
|
1223
|
+
let failure;
|
|
1224
|
+
try {
|
|
1225
|
+
await handle.writeFile(bytes);
|
|
1226
|
+
await handle.sync();
|
|
1227
|
+
}
|
|
1228
|
+
catch (error) {
|
|
1229
|
+
failure = error;
|
|
1230
|
+
}
|
|
1231
|
+
finally {
|
|
1232
|
+
try {
|
|
1233
|
+
await handle.close();
|
|
1234
|
+
}
|
|
1235
|
+
catch (error) {
|
|
1236
|
+
if (failure === undefined)
|
|
1237
|
+
failure = error;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
if (failure !== undefined) {
|
|
1241
|
+
await rm(filename, { force: true }).catch(() => { });
|
|
1242
|
+
throw failure;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Reads and synchronizes one regular non-symlink file.
|
|
1247
|
+
* @param {string} filename - Exact filename.
|
|
1248
|
+
* @param {string} projectId - Project identity.
|
|
1249
|
+
* @param {string} code - Failure code.
|
|
1250
|
+
* @param {string} message - Stable failure detail.
|
|
1251
|
+
* @returns {Promise<Buffer>} Exact bytes.
|
|
1252
|
+
*/
|
|
1253
|
+
async function readAndSyncRegularFile(filename, projectId, code, message) {
|
|
1254
|
+
try {
|
|
1255
|
+
const status = await lstat(filename);
|
|
1256
|
+
if (!status.isFile() || status.isSymbolicLink())
|
|
1257
|
+
publicationFailure(code, projectId, message);
|
|
1258
|
+
assertRegularFileImmutability(status, new Set(), projectId);
|
|
1259
|
+
const handle = await open(filename, constants.O_RDONLY);
|
|
1260
|
+
try {
|
|
1261
|
+
const bytes = await handle.readFile();
|
|
1262
|
+
await handle.sync();
|
|
1263
|
+
return bytes;
|
|
1264
|
+
}
|
|
1265
|
+
finally {
|
|
1266
|
+
await handle.close();
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
catch (error) {
|
|
1270
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1271
|
+
throw error;
|
|
1272
|
+
publicationFailure(code, projectId, message, error);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Lists every regular file beneath one projection and rejects symlinks or special nodes.
|
|
1277
|
+
* @param {string} directory - Projection root.
|
|
1278
|
+
* @param {string} projectId - Project identity.
|
|
1279
|
+
* @returns {Promise<string[]>} Sorted relative POSIX paths.
|
|
1280
|
+
*/
|
|
1281
|
+
async function listRegularFiles(directory, projectId) {
|
|
1282
|
+
/** @type {string[]} */
|
|
1283
|
+
const files = [];
|
|
1284
|
+
const identities = new Set();
|
|
1285
|
+
try {
|
|
1286
|
+
await visit(directory, "");
|
|
1287
|
+
}
|
|
1288
|
+
catch (error) {
|
|
1289
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1290
|
+
throw error;
|
|
1291
|
+
publicationFailure("PUBLICATION_GENERATION_VERIFICATION_FAILED", projectId, "Immutable generation filesystem inventory could not be verified.", error);
|
|
1292
|
+
}
|
|
1293
|
+
return files.sort(compareStrings);
|
|
1294
|
+
/**
|
|
1295
|
+
* Visits one owned directory.
|
|
1296
|
+
* @param {string} current - Exact directory.
|
|
1297
|
+
* @param {string} relative - Projection-relative path.
|
|
1298
|
+
* @returns {Promise<void>} Completion.
|
|
1299
|
+
*/
|
|
1300
|
+
async function visit(current, relative) {
|
|
1301
|
+
await assertRealInventoryDirectory(current, projectId);
|
|
1302
|
+
const entries = await readdir(current);
|
|
1303
|
+
entries.sort(compareStrings);
|
|
1304
|
+
for (const entry of entries) {
|
|
1305
|
+
const childRelative = relative.length == 0 ? entry : `${relative}/${entry}`;
|
|
1306
|
+
if (!isSafeArtifactPath(childRelative)) {
|
|
1307
|
+
publicationFailure("INVALID_PUBLICATION_PROJECTION", projectId, "Validator output contains a non-canonical relative path.");
|
|
1308
|
+
}
|
|
1309
|
+
const child = path.join(current, entry);
|
|
1310
|
+
const status = await lstat(child);
|
|
1311
|
+
if (status.isSymbolicLink()) {
|
|
1312
|
+
publicationFailure("PUBLICATION_SYMLINK_TRAVERSAL", projectId, "Candidate generation contains a symbolic link.");
|
|
1313
|
+
}
|
|
1314
|
+
if (status.isDirectory())
|
|
1315
|
+
await visit(child, childRelative);
|
|
1316
|
+
else if (status.isFile()) {
|
|
1317
|
+
assertRegularFileImmutability(status, identities, projectId);
|
|
1318
|
+
files.push(childRelative);
|
|
1319
|
+
}
|
|
1320
|
+
else
|
|
1321
|
+
publicationFailure("PUBLICATION_GENERATION_INVENTORY_MISMATCH", projectId, "Immutable generation contains an undeclared filesystem entry.");
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Verifies that the manifest describes every directory and regular file in the immutable generation.
|
|
1327
|
+
* @param {string} generationPath - Exact generation root.
|
|
1328
|
+
* @param {import("./semantic/types.js").GenerationManifest} manifest - Verified manifest.
|
|
1329
|
+
* @param {string} projectId - Project identity.
|
|
1330
|
+
* @returns {Promise<void>} Completion.
|
|
1331
|
+
*/
|
|
1332
|
+
async function verifyGenerationInventory(generationPath, manifest, projectId) {
|
|
1333
|
+
const expectedFiles = new Set(["manifest.json"]);
|
|
1334
|
+
const expectedDirectories = new Set();
|
|
1335
|
+
for (const target of manifest.targets) {
|
|
1336
|
+
addExpectedDirectory(target.projections.source);
|
|
1337
|
+
addExpectedDirectory(target.projections.build);
|
|
1338
|
+
for (const artifact of target.artifacts) {
|
|
1339
|
+
addExpectedFile(`${target.projections.source}/${artifact.path}`);
|
|
1340
|
+
}
|
|
1341
|
+
for (const artifact of target.buildArtifacts) {
|
|
1342
|
+
addExpectedFile(`${target.projections.build}/${artifact.path}`);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
/** @type {Set<string>} */
|
|
1346
|
+
const observedFiles = new Set();
|
|
1347
|
+
/** @type {Set<string>} */
|
|
1348
|
+
const observedDirectories = new Set();
|
|
1349
|
+
const identities = new Set();
|
|
1350
|
+
try {
|
|
1351
|
+
await visit(generationPath, "");
|
|
1352
|
+
}
|
|
1353
|
+
catch (error) {
|
|
1354
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1355
|
+
throw error;
|
|
1356
|
+
publicationFailure("PUBLICATION_GENERATION_VERIFICATION_FAILED", projectId, "Immutable generation filesystem inventory could not be verified.", error);
|
|
1357
|
+
}
|
|
1358
|
+
if (!sameStringSet(expectedFiles, observedFiles) || !sameStringSet(expectedDirectories, observedDirectories)) {
|
|
1359
|
+
publicationFailure("PUBLICATION_GENERATION_INVENTORY_MISMATCH", projectId, "Immutable generation contains an undeclared filesystem entry.");
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Adds one expected directory and each generation-relative parent.
|
|
1363
|
+
* @param {string} relative - POSIX-style generation-relative directory.
|
|
1364
|
+
* @returns {void}
|
|
1365
|
+
*/
|
|
1366
|
+
function addExpectedDirectory(relative) {
|
|
1367
|
+
const parts = relative.split("/");
|
|
1368
|
+
for (let index = 1; index <= parts.length; index += 1) {
|
|
1369
|
+
expectedDirectories.add(parts.slice(0, index).join("/"));
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Adds one expected regular file and its parent directories.
|
|
1374
|
+
* @param {string} relative - POSIX-style generation-relative filename.
|
|
1375
|
+
* @returns {void}
|
|
1376
|
+
*/
|
|
1377
|
+
function addExpectedFile(relative) {
|
|
1378
|
+
expectedFiles.add(relative);
|
|
1379
|
+
const separator = relative.lastIndexOf("/");
|
|
1380
|
+
if (separator >= 0)
|
|
1381
|
+
addExpectedDirectory(relative.slice(0, separator));
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Visits every entry in one generation directory without following links.
|
|
1385
|
+
* @param {string} current - Exact directory.
|
|
1386
|
+
* @param {string} relative - Generation-relative directory.
|
|
1387
|
+
* @returns {Promise<void>} Completion.
|
|
1388
|
+
*/
|
|
1389
|
+
async function visit(current, relative) {
|
|
1390
|
+
await assertRealInventoryDirectory(current, projectId);
|
|
1391
|
+
const entries = await readdir(current);
|
|
1392
|
+
entries.sort(compareStrings);
|
|
1393
|
+
for (const entry of entries) {
|
|
1394
|
+
const childRelative = relative.length == 0 ? entry : `${relative}/${entry}`;
|
|
1395
|
+
if (!isSafeArtifactPath(childRelative)) {
|
|
1396
|
+
publicationFailure("PUBLICATION_GENERATION_INVENTORY_MISMATCH", projectId, "Immutable generation contains an undeclared filesystem entry.");
|
|
1397
|
+
}
|
|
1398
|
+
const child = path.join(current, entry);
|
|
1399
|
+
const status = await lstat(child);
|
|
1400
|
+
if (status.isSymbolicLink()) {
|
|
1401
|
+
publicationFailure("PUBLICATION_SYMLINK_TRAVERSAL", projectId, "Candidate generation contains a symbolic link.");
|
|
1402
|
+
}
|
|
1403
|
+
if (status.isDirectory()) {
|
|
1404
|
+
observedDirectories.add(childRelative);
|
|
1405
|
+
await visit(child, childRelative);
|
|
1406
|
+
}
|
|
1407
|
+
else if (status.isFile()) {
|
|
1408
|
+
assertRegularFileImmutability(status, identities, projectId);
|
|
1409
|
+
observedFiles.add(childRelative);
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
publicationFailure("PUBLICATION_GENERATION_INVENTORY_MISMATCH", projectId, "Immutable generation contains an undeclared filesystem entry.");
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Verifies one inventory traversal root without following symbolic links.
|
|
1419
|
+
* @param {string} directory - Exact directory about to be read.
|
|
1420
|
+
* @param {string} projectId - Project identity.
|
|
1421
|
+
* @returns {Promise<void>} Completion.
|
|
1422
|
+
*/
|
|
1423
|
+
async function assertRealInventoryDirectory(directory, projectId) {
|
|
1424
|
+
const status = await lstat(directory);
|
|
1425
|
+
if (status.isSymbolicLink()) {
|
|
1426
|
+
publicationFailure("PUBLICATION_SYMLINK_TRAVERSAL", projectId, "Candidate generation contains a symbolic link.");
|
|
1427
|
+
}
|
|
1428
|
+
if (!status.isDirectory()) {
|
|
1429
|
+
publicationFailure("PUBLICATION_GENERATION_INVENTORY_MISMATCH", projectId, "Immutable generation contains an undeclared filesystem entry.");
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Rejects external hard links and duplicate stable file identities.
|
|
1434
|
+
* @param {import("node:fs").Stats} status - Regular-file status.
|
|
1435
|
+
* @param {Set<string>} identities - Previously observed device/inode identities.
|
|
1436
|
+
* @param {string} projectId - Project identity.
|
|
1437
|
+
* @returns {void}
|
|
1438
|
+
*/
|
|
1439
|
+
function assertRegularFileImmutability(status, identities, projectId) {
|
|
1440
|
+
if (status.nlink > 1) {
|
|
1441
|
+
publicationFailure("PUBLICATION_HARD_LINK", projectId, "Immutable generation files must not have external hard links.");
|
|
1442
|
+
}
|
|
1443
|
+
const identity = stableFileIdentity(status);
|
|
1444
|
+
if (identity !== null) {
|
|
1445
|
+
if (identities.has(identity)) {
|
|
1446
|
+
publicationFailure("PUBLICATION_HARD_LINK", projectId, "Immutable generation files must have unique filesystem identities.");
|
|
1447
|
+
}
|
|
1448
|
+
identities.add(identity);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Returns a stable device/inode identity only where the host reports one.
|
|
1453
|
+
* @param {import("node:fs").Stats} status - File status.
|
|
1454
|
+
* @returns {string | null} Identity or unavailable.
|
|
1455
|
+
*/
|
|
1456
|
+
function stableFileIdentity(status) {
|
|
1457
|
+
if ((typeof status.dev != "number" && typeof status.dev != "bigint") ||
|
|
1458
|
+
(typeof status.ino != "number" && typeof status.ino != "bigint") || status.ino == 0)
|
|
1459
|
+
return null;
|
|
1460
|
+
return `${String(status.dev)}:${String(status.ino)}`;
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Compares two string sets exactly.
|
|
1464
|
+
* @param {Set<string>} left - First set.
|
|
1465
|
+
* @param {Set<string>} right - Second set.
|
|
1466
|
+
* @returns {boolean} Equality.
|
|
1467
|
+
*/
|
|
1468
|
+
function sameStringSet(left, right) {
|
|
1469
|
+
return left.size == right.size && [...left].every(value => right.has(value));
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Synchronizes every directory in a staged tree from leaves to root.
|
|
1473
|
+
* @param {string} root - Tree root.
|
|
1474
|
+
* @returns {Promise<void>} Completion.
|
|
1475
|
+
*/
|
|
1476
|
+
async function syncTreeDirectories(root) {
|
|
1477
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
1478
|
+
for (const entry of entries) {
|
|
1479
|
+
if (entry.isDirectory() && !entry.isSymbolicLink())
|
|
1480
|
+
await syncTreeDirectories(path.join(root, entry.name));
|
|
1481
|
+
}
|
|
1482
|
+
await syncDirectory(root);
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Synchronizes one directory entry set.
|
|
1486
|
+
* @param {string} directory - Directory path.
|
|
1487
|
+
* @returns {Promise<void>} Completion.
|
|
1488
|
+
*/
|
|
1489
|
+
async function syncDirectory(directory) {
|
|
1490
|
+
const handle = await open(directory, constants.O_RDONLY);
|
|
1491
|
+
try {
|
|
1492
|
+
await handle.sync();
|
|
1493
|
+
}
|
|
1494
|
+
finally {
|
|
1495
|
+
await handle.close();
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Ensures every existing path component is a real directory and optionally creates missing components.
|
|
1500
|
+
* @param {string} directory - Canonical absolute directory.
|
|
1501
|
+
* @param {boolean} create - Whether missing components may be created.
|
|
1502
|
+
* @param {string} projectId - Project identity.
|
|
1503
|
+
* @returns {Promise<void>} Completion.
|
|
1504
|
+
*/
|
|
1505
|
+
async function ensureDirectoryWithoutSymlinks(directory, create, projectId) {
|
|
1506
|
+
const parsed = path.parse(directory);
|
|
1507
|
+
const relative = directory.slice(parsed.root.length);
|
|
1508
|
+
const parts = relative.split(path.sep).filter(Boolean);
|
|
1509
|
+
let current = parsed.root;
|
|
1510
|
+
for (const part of parts) {
|
|
1511
|
+
current = path.join(current, part);
|
|
1512
|
+
try {
|
|
1513
|
+
const status = await lstat(current);
|
|
1514
|
+
if (status.isSymbolicLink()) {
|
|
1515
|
+
publicationFailure("PUBLICATION_SYMLINK_TRAVERSAL", projectId, "Owned path traversal encountered a symbolic link.");
|
|
1516
|
+
}
|
|
1517
|
+
if (!status.isDirectory()) {
|
|
1518
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Owned root path contains a non-directory component.");
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
catch (error) {
|
|
1522
|
+
if (!isErrorCode(error, "ENOENT")) {
|
|
1523
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1524
|
+
throw error;
|
|
1525
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Owned root path could not be validated.", error);
|
|
1526
|
+
}
|
|
1527
|
+
if (!create) {
|
|
1528
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Source root must exist as a real directory.", error);
|
|
1529
|
+
}
|
|
1530
|
+
try {
|
|
1531
|
+
await mkdir(current, { mode: 0o700 });
|
|
1532
|
+
}
|
|
1533
|
+
catch (mkdirError) {
|
|
1534
|
+
publicationFailure("INVALID_PUBLICATION_ROOT", projectId, "Publication root could not be created safely.", mkdirError);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Ensures one reserved publisher directory is neither a symlink nor an unowned file.
|
|
1541
|
+
* @param {string} directory - Reserved directory.
|
|
1542
|
+
* @param {string} projectId - Project identity.
|
|
1543
|
+
* @returns {Promise<void>} Completion.
|
|
1544
|
+
*/
|
|
1545
|
+
async function ensureOwnedDirectory(directory, projectId) {
|
|
1546
|
+
try {
|
|
1547
|
+
await mkdir(directory, { mode: 0o700 });
|
|
1548
|
+
}
|
|
1549
|
+
catch (error) {
|
|
1550
|
+
if (!isErrorCode(error, "EEXIST")) {
|
|
1551
|
+
publicationFailure("PUBLICATION_STAGE_WRITE_FAILED", projectId, "Publisher-owned directory could not be created.", error);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
let status;
|
|
1555
|
+
try {
|
|
1556
|
+
status = await lstat(directory);
|
|
1557
|
+
}
|
|
1558
|
+
catch (error) {
|
|
1559
|
+
publicationFailure("PUBLICATION_STAGE_WRITE_FAILED", projectId, "Reserved publisher path could not be inspected.", error);
|
|
1560
|
+
}
|
|
1561
|
+
if (!status.isDirectory() || status.isSymbolicLink()) {
|
|
1562
|
+
publicationFailure(status.isSymbolicLink() ? "PUBLICATION_SYMLINK_TRAVERSAL" : "PUBLICATION_UNOWNED_CONFLICT", projectId, "Reserved publisher path is not an owned real directory.");
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Ensures an owned path has no pre-existing entry.
|
|
1567
|
+
* @param {string} filename - Exact path.
|
|
1568
|
+
* @param {string} code - Diagnostic code.
|
|
1569
|
+
* @param {string} projectId - Project identity.
|
|
1570
|
+
* @param {string} message - Stable failure detail.
|
|
1571
|
+
* @returns {Promise<void>} Completion.
|
|
1572
|
+
*/
|
|
1573
|
+
async function assertAbsent(filename, code, projectId, message) {
|
|
1574
|
+
try {
|
|
1575
|
+
await lstat(filename);
|
|
1576
|
+
publicationFailure(code, projectId, message);
|
|
1577
|
+
}
|
|
1578
|
+
catch (error) {
|
|
1579
|
+
if (isErrorCode(error, "ENOENT"))
|
|
1580
|
+
return;
|
|
1581
|
+
if (error instanceof SemantifoldDiagnostic)
|
|
1582
|
+
throw error;
|
|
1583
|
+
publicationFailure(code, projectId, message, error);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Resolves a validated relative path under an owned root and rejects escape.
|
|
1588
|
+
* @param {string} root - Owned root.
|
|
1589
|
+
* @param {string} relative - Validated relative POSIX path.
|
|
1590
|
+
* @param {string} projectId - Project identity.
|
|
1591
|
+
* @returns {string} Exact owned path.
|
|
1592
|
+
*/
|
|
1593
|
+
function ownedPath(root, relative, projectId) {
|
|
1594
|
+
const resolved = path.resolve(root, ...relative.split("/"));
|
|
1595
|
+
const child = path.relative(root, resolved);
|
|
1596
|
+
if (child.length == 0 || child == ".." || child.startsWith(`..${path.sep}`) || path.isAbsolute(child)) {
|
|
1597
|
+
publicationFailure("PUBLICATION_PATH_ESCAPE", projectId, "Resolved publication path escaped its owned root.");
|
|
1598
|
+
}
|
|
1599
|
+
return resolved;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Checks a canonical non-root absolute path without filesystem access.
|
|
1603
|
+
* @param {unknown} value - Path candidate.
|
|
1604
|
+
* @returns {value is string} Whether the path is canonical.
|
|
1605
|
+
*/
|
|
1606
|
+
function isCanonicalAbsolutePath(value) {
|
|
1607
|
+
return isCanonicalAbsolutePathForPath(value, path);
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Checks one absolute path using the supplied host path semantics.
|
|
1611
|
+
* This helper is intentionally not part of the package root API.
|
|
1612
|
+
* @param {unknown} value - Path candidate.
|
|
1613
|
+
* @param {typeof path.posix | typeof path.win32} hostPath - Host-native path implementation.
|
|
1614
|
+
* @returns {value is string} Whether the path is canonical for that host.
|
|
1615
|
+
*/
|
|
1616
|
+
export function isCanonicalAbsolutePathForPath(value, hostPath) {
|
|
1617
|
+
const separatorAlias = hostPath.sep == "/" ? "\\" : "/";
|
|
1618
|
+
return typeof value == "string" && value.length > 0 && !value.includes("\0") && !value.includes(separatorAlias) &&
|
|
1619
|
+
hostPath.isAbsolute(value) && value != hostPath.parse(value).root && hostPath.normalize(value) == value &&
|
|
1620
|
+
hostPath.resolve(value) == value;
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Checks conservative portable overlap between absolute paths.
|
|
1624
|
+
* @param {string} left - First path.
|
|
1625
|
+
* @param {string} right - Second path.
|
|
1626
|
+
* @returns {boolean} Whether either contains the other.
|
|
1627
|
+
*/
|
|
1628
|
+
function pathsOverlap(left, right) {
|
|
1629
|
+
const leftKey = left.toLowerCase();
|
|
1630
|
+
const rightKey = right.toLowerCase();
|
|
1631
|
+
return leftKey == rightKey || leftKey.startsWith(`${rightKey}${path.sep}`) || rightKey.startsWith(`${leftKey}${path.sep}`);
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Checks whether a path is equal to or nested beneath one canonical root.
|
|
1635
|
+
* @param {string} root - Canonical root.
|
|
1636
|
+
* @param {string} candidate - Canonical candidate.
|
|
1637
|
+
* @returns {boolean} Whether the candidate is within the root.
|
|
1638
|
+
*/
|
|
1639
|
+
function isPathWithin(root, candidate) {
|
|
1640
|
+
const relative = path.relative(root, candidate);
|
|
1641
|
+
return relative.length == 0 || (relative != ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Checks whether a path is strictly nested beneath one canonical root.
|
|
1645
|
+
* @param {string} root - Canonical root.
|
|
1646
|
+
* @param {string} candidate - Canonical candidate.
|
|
1647
|
+
* @returns {boolean} Whether the candidate is a strict descendant.
|
|
1648
|
+
*/
|
|
1649
|
+
function isStrictPathDescendant(root, candidate) {
|
|
1650
|
+
return isPathWithin(root, candidate) && path.relative(root, candidate).length > 0;
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Serializes stable generated JSON with one trailing LF.
|
|
1654
|
+
* @param {unknown} value - JSON value.
|
|
1655
|
+
* @returns {Buffer} UTF-8 bytes.
|
|
1656
|
+
*/
|
|
1657
|
+
function serializeJson(value) {
|
|
1658
|
+
return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Hashes exact bytes with SHA-256.
|
|
1662
|
+
* @param {Uint8Array} bytes - Exact bytes.
|
|
1663
|
+
* @returns {string} Lowercase hexadecimal hash.
|
|
1664
|
+
*/
|
|
1665
|
+
function sha256(bytes) {
|
|
1666
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Deeply freezes a parsed JSON value.
|
|
1670
|
+
* @template T
|
|
1671
|
+
* @param {T} value - JSON value.
|
|
1672
|
+
* @returns {T} Frozen value.
|
|
1673
|
+
*/
|
|
1674
|
+
function deepFreeze(value) {
|
|
1675
|
+
if (typeof value == "object" && value != null) {
|
|
1676
|
+
for (const child of Object.values(value))
|
|
1677
|
+
deepFreeze(child);
|
|
1678
|
+
Object.freeze(value);
|
|
1679
|
+
}
|
|
1680
|
+
return value;
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Compares detached JSON values without depending on object-key order.
|
|
1684
|
+
* @param {unknown} left - First JSON value.
|
|
1685
|
+
* @param {unknown} right - Second JSON value.
|
|
1686
|
+
* @returns {boolean} Whether the values are structurally equal.
|
|
1687
|
+
*/
|
|
1688
|
+
function sameJsonValue(left, right) {
|
|
1689
|
+
if (left === right)
|
|
1690
|
+
return true;
|
|
1691
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
1692
|
+
return Array.isArray(left) && Array.isArray(right) && left.length == right.length &&
|
|
1693
|
+
left.every((entry, index) => sameJsonValue(entry, right[index]));
|
|
1694
|
+
}
|
|
1695
|
+
if (!isPlainObject(left) || !isPlainObject(right))
|
|
1696
|
+
return false;
|
|
1697
|
+
const leftKeys = Object.keys(left);
|
|
1698
|
+
const rightKeys = Object.keys(right);
|
|
1699
|
+
return leftKeys.length == rightKeys.length &&
|
|
1700
|
+
leftKeys.every(key => Object.hasOwn(right, key) && sameJsonValue(left[key], right[key]));
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Checks an active-pointer value.
|
|
1704
|
+
* @param {unknown} value - Parsed value.
|
|
1705
|
+
* @returns {value is ActivePointer} Whether the value is strict and versioned.
|
|
1706
|
+
*/
|
|
1707
|
+
function isActivePointer(value) {
|
|
1708
|
+
return isPlainObject(value) && hasExactKeys(value, ["generationId", "manifestHash", "projectId", "schema", "version"]) &&
|
|
1709
|
+
value.schema == "SemantifoldActiveGeneration" && value.version == 1 &&
|
|
1710
|
+
typeof value.projectId == "string" && identityPattern.test(value.projectId) &&
|
|
1711
|
+
typeof value.generationId == "string" && generationPattern.test(value.generationId) &&
|
|
1712
|
+
typeof value.manifestHash == "string" && /^[a-f0-9]{64}$/u.test(value.manifestHash);
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Checks a recovery-journal value.
|
|
1716
|
+
* @param {unknown} value - Parsed value.
|
|
1717
|
+
* @returns {value is PublicationJournal} Whether the value is strict and versioned.
|
|
1718
|
+
*/
|
|
1719
|
+
function isPublicationJournal(value) {
|
|
1720
|
+
if (!isPlainObject(value) || value.schema != "SemantifoldPublicationJournal" ||
|
|
1721
|
+
typeof value.projectId != "string" || !identityPattern.test(value.projectId) ||
|
|
1722
|
+
typeof value.generationId != "string" || !generationPattern.test(value.generationId) ||
|
|
1723
|
+
value.tempPointer != `.active-generation.${value.generationId}.tmp`)
|
|
1724
|
+
return false;
|
|
1725
|
+
return value.version == 1 && hasExactKeys(value, ["generationId", "projectId", "schema", "tempPointer", "version"]) ||
|
|
1726
|
+
value.version == 2 && value.operation == "reactivate" &&
|
|
1727
|
+
hasExactKeys(value, ["generationId", "operation", "projectId", "schema", "tempPointer", "version"]);
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Checks an ordinary object with exactly the expected enumerable string keys.
|
|
1731
|
+
* @param {Record<string, unknown>} value - Object candidate.
|
|
1732
|
+
* @param {readonly string[]} keys - Sorted expected keys.
|
|
1733
|
+
* @returns {boolean} Whether keys match exactly.
|
|
1734
|
+
*/
|
|
1735
|
+
function hasExactKeys(value, keys) {
|
|
1736
|
+
const actual = Object.keys(value).sort(compareStrings);
|
|
1737
|
+
return actual.length == keys.length && actual.every((key, index) => key == keys[index]);
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Checks for an ordinary string-keyed object.
|
|
1741
|
+
* @param {unknown} value - Candidate value.
|
|
1742
|
+
* @returns {value is Record<string, unknown>} Whether the value is plain.
|
|
1743
|
+
*/
|
|
1744
|
+
function isPlainObject(value) {
|
|
1745
|
+
return typeof value == "object" && value != null && !Array.isArray(value) && Object.getPrototypeOf(value) == Object.prototype;
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Checks for a dense ordinary array.
|
|
1749
|
+
* @param {unknown} value - Candidate value.
|
|
1750
|
+
* @returns {value is unknown[]} Whether the value is dense.
|
|
1751
|
+
*/
|
|
1752
|
+
function isDenseArray(value) {
|
|
1753
|
+
if (!Array.isArray(value))
|
|
1754
|
+
return false;
|
|
1755
|
+
const indexedKeys = Object.keys(value).filter(key => /^(?:0|[1-9][0-9]*)$/u.test(key));
|
|
1756
|
+
return indexedKeys.length == value.length;
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Compares strings by Unicode code unit.
|
|
1760
|
+
* @param {string} left - First string.
|
|
1761
|
+
* @param {string} right - Second string.
|
|
1762
|
+
* @returns {number} Ordering.
|
|
1763
|
+
*/
|
|
1764
|
+
function compareStrings(left, right) {
|
|
1765
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Checks a narrowed filesystem error code.
|
|
1769
|
+
* @param {unknown} error - Opaque error.
|
|
1770
|
+
* @param {string} code - Expected code.
|
|
1771
|
+
* @returns {boolean} Whether the code matches.
|
|
1772
|
+
*/
|
|
1773
|
+
function isErrorCode(error, code) {
|
|
1774
|
+
return error instanceof Error && "code" in error && error.code == code;
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Throws one stable publication diagnostic without embedding host paths or raw filesystem messages.
|
|
1778
|
+
* @param {string} code - Stable diagnostic code.
|
|
1779
|
+
* @param {string} projectId - Project identity.
|
|
1780
|
+
* @param {string} message - Stable detail.
|
|
1781
|
+
* @param {unknown} [error] - Preserved cause.
|
|
1782
|
+
* @returns {never} Always throws.
|
|
1783
|
+
*/
|
|
1784
|
+
function publicationFailure(code, projectId, message, error) {
|
|
1785
|
+
throw new SemantifoldDiagnostic({
|
|
1786
|
+
cause: error instanceof Error ? error : undefined,
|
|
1787
|
+
code,
|
|
1788
|
+
language: projectId,
|
|
1789
|
+
message
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* @typedef ValidatedPublicationTarget
|
|
1794
|
+
* @property {string} id - Target identity.
|
|
1795
|
+
* @property {import("./semantic/types.js").GeneratedArtifactSet} artifactSet - Detached artifact set.
|
|
1796
|
+
* @property {string} sourceProjection - Source projection.
|
|
1797
|
+
* @property {string} buildProjection - Build projection.
|
|
1798
|
+
* @property {import("./semantic/types.js").PublicationTargetRole} role - Language-neutral target role.
|
|
1799
|
+
* @property {readonly import("./semantic/types.js").PublicationValidator[]} validators - Ordered validators.
|
|
1800
|
+
*/
|
|
1801
|
+
/**
|
|
1802
|
+
* @typedef ValidatedPublicationRequest
|
|
1803
|
+
* @property {string} generationId - Snapshotted generation identity.
|
|
1804
|
+
* @property {readonly ValidatedPublicationTarget[]} targets - Snapshotted ordered targets.
|
|
1805
|
+
*/
|
|
1806
|
+
/**
|
|
1807
|
+
* @typedef ActivePointer
|
|
1808
|
+
* @property {"SemantifoldActiveGeneration"} schema - Schema discriminator.
|
|
1809
|
+
* @property {1} version - Schema version.
|
|
1810
|
+
* @property {string} projectId - Project identity.
|
|
1811
|
+
* @property {string} generationId - Generation identity.
|
|
1812
|
+
* @property {string} manifestHash - SHA-256 generation-manifest hash.
|
|
1813
|
+
*/
|
|
1814
|
+
/**
|
|
1815
|
+
* @typedef CandidatePublicationJournal
|
|
1816
|
+
* @property {"SemantifoldPublicationJournal"} schema - Schema discriminator.
|
|
1817
|
+
* @property {1} version - Schema version.
|
|
1818
|
+
* @property {string} projectId - Project identity.
|
|
1819
|
+
* @property {string} generationId - Candidate generation identity.
|
|
1820
|
+
* @property {string} tempPointer - Owned sibling temporary-pointer filename.
|
|
1821
|
+
*/
|
|
1822
|
+
/**
|
|
1823
|
+
* @typedef ReactivationPublicationJournal
|
|
1824
|
+
* @property {"SemantifoldPublicationJournal"} schema - Schema discriminator.
|
|
1825
|
+
* @property {2} version - Schema version.
|
|
1826
|
+
* @property {"reactivate"} operation - Retained-generation activation without candidate ownership.
|
|
1827
|
+
* @property {string} projectId - Project identity.
|
|
1828
|
+
* @property {string} generationId - Retained generation identity.
|
|
1829
|
+
* @property {string} tempPointer - Owned sibling temporary-pointer filename.
|
|
1830
|
+
*/
|
|
1831
|
+
/** @typedef {CandidatePublicationJournal | ReactivationPublicationJournal} PublicationJournal */
|
|
1832
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHVibGljYXRpb24uanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBQyxVQUFVLEVBQUUsVUFBVSxFQUFDLE1BQU0sYUFBYSxDQUFBO0FBQ2xELE9BQU8sRUFBQyxTQUFTLEVBQUMsTUFBTSxTQUFTLENBQUE7QUFDakMsT0FBTyxFQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQTtBQUM1RixPQUFPLElBQUksTUFBTSxXQUFXLENBQUE7QUFDNUIsT0FBTyxFQUFDLGdDQUFnQyxFQUFFLGtCQUFrQixFQUFDLE1BQU0sb0JBQW9CLENBQUE7QUFDdkYsT0FBTyxFQUFDLDBCQUEwQixFQUFDLE1BQU0sZ0JBQWdCLENBQUE7QUFDekQsT0FBTyxFQUFDLG1CQUFtQixFQUFDLE1BQU0scUJBQXFCLENBQUE7QUFDdkQsT0FBTyxFQUFDLHFCQUFxQixFQUFDLE1BQU0saUJBQWlCLENBQUE7QUFDckQsT0FBTyxFQUFDLGVBQWUsRUFBRSxhQUFhLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFFM0QsTUFBTSxpQkFBaUIsR0FBRyx3QkFBd0IsQ0FBQTtBQUNsRCxNQUFNLGVBQWUsR0FBRyxhQUFhLENBQUE7QUFDckMsTUFBTSxXQUFXLEdBQUcsa0NBQWtDLENBQUE7QUFDdEQsTUFBTSxlQUFlLEdBQUcsb0JBQW9CLENBQUE7QUFDNUMsTUFBTSxpQkFBaUIsR0FBRyxxQ0FBcUMsQ0FBQTtBQUMvRCxNQUFNLGdCQUFnQixHQUFHLG9CQUFvQixDQUFBO0FBQzdDLE1BQU0sZ0JBQWdCLEdBQUcsdUVBQXVFLENBQUE7QUFDaEcsTUFBTSxXQUFXLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxhQUFhLEVBQUUsUUFBUSxFQUFF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cUIsQ0FBQztRQUM5QixLQUFLLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTO1FBQ2pELElBQUk7UUFDSixRQUFRLEVBQUUsU0FBUztRQUNuQixPQUFPO0tBQ1IsQ0FBQyxDQUFBO0FBQ0osQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBRUg7Ozs7R0FJRztBQUVIOzs7Ozs7O0dBT0c7QUFFSDs7Ozs7OztHQU9HO0FBRUg7Ozs7Ozs7O0dBUUc7QUFFSCxpR0FBaUciLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IHtjcmVhdGVIYXNoLCByYW5kb21VVUlEfSBmcm9tIFwibm9kZTpjcnlwdG9cIlxuaW1wb3J0IHtjb25zdGFudHN9IGZyb20gXCJub2RlOmZzXCJcbmltcG9ydCB7bHN0YXQsIG1rZGlyLCBvcGVuLCByZWFkRmlsZSwgcmVhZGRpciwgcmVhbHBhdGgsIHJlbmFtZSwgcm19IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCJcbmltcG9ydCBwYXRoIGZyb20gXCJub2RlOnBhdGhcIlxuaW1wb3J0IHtmaW5kUG9ydGFibGVBcnRpZmFjdFBhdGhDb25mbGljdCwgaXNTYWZlQXJ0aWZhY3RQYXRofSBmcm9tIFwiLi9hcnRpZmFjdC1wYXRoLmpzXCJcbmltcG9ydCB7Y3JlYXRlR2VuZXJhdGVkQXJ0aWZhY3RTZXR9IGZyb20gXCIuL2FydGlmYWN0cy5qc1wiXG5pbXBvcnQge2ZpbmFsaXplQnl0ZU1hcHBpbmd9IGZyb20gXCIuL2JpbmFyeS1tYXBwaW5nLmpzXCJcbmltcG9ydCB7U2VtYW50aWZvbGREaWFnbm9zdGljfSBmcm9tIFwiLi9kaWFnbm9zdGljLmpzXCJcbmltcG9ydCB7ZmluYWxpemVNYXBwaW5nLCB0b1NvdXJjZU1hcFYzfSBmcm9tIFwiLi9tYXBwaW5nLmpzXCJcblxuY29uc3QgYWN0aXZlUG9pbnRlck5hbWUgPSBcImFjdGl2ZS1nZW5lcmF0aW9uLmpzb25cIlxuY29uc3QgZ2VuZXJhdGlvbnNOYW1lID0gXCJnZW5lcmF0aW9uc1wiXG5jb25zdCBqb3VybmFsTmFtZSA9IFwiLnNlbWFudGlmb2xkLXB1YmxpY2F0aW9uLWpvdXJuYWxcIlxuY29uc3QgaWRlbnRpdHlQYXR0ZXJuID0gL15bYS16XVthLXowLTktXSokL3VcbmNvbnN0IGdlbmVyYXRpb25QYXR0ZXJuID0gL15bQS1aYS16MC05XVtBLVphLXowLTkuXy1dezAsMTI3fSQvdVxuY29uc3QgYnVpbGRSb2xlUGF0dGVybiA9IC9eW2Etel1bYS16MC05LV0qJC91XG5jb25zdCBtZWRpYVR5cGVQYXR0ZXJuID0gL15bQS1aYS16MC05ISMkJl5fListXStcXC9bQS1aYS16MC05ISMkJl5fListXSsoPzo7W1xcdTAwMjAtXFx1MDA3ZV0rKT8kL3VcbmNvbnN0IHRhcmdldFJvbGVzID0gbmV3IFNldChbXCJhcHBsaWNhdGlvblwiLCBcImJpbmFyeVwiLCBcInRleHRcIl0pXG5jb25zdCBwdWJsaWNhdGlvbkJvdW5kYXJpZXMgPSBuZXcgU2V0KFtcbiAgXCJhZnRlci1zdGFnZVwiLFxuICBcImJlZm9yZS1wb2ludGVyLXJlcGxhY2VcIixcbiAgXCJhZnRlci1wb2ludGVyLXJlcGxhY2VcIixcbiAgXCJiZWZvcmUtY2xlYW51cFwiLFxuICBcImJlZm9yZS1qb3VybmFsLXJlbW92ZVwiXG5dKVxuLyoqIEB0eXBlIHtXZWFrTWFwPEdlbmVyYXRlZEFydGlmYWN0UHVibGlzaGVyLCB7Ym91bmRhcnk6IHN0cmluZywgZXJyb3I6IEVycm9yfT59ICovXG5jb25zdCBpbmplY3RlZFB1YmxpY2F0aW9uRmFpbHVyZXMgPSBuZXcgV2Vha01hcCgpXG4vKiogQHR5cGUge1dlYWtNYXA8R2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXIsIChvcGVyYXRpb246IHN0cmluZykgPT4gdm9pZD59ICovXG5jb25zdCBwdWJsaWNhdGlvbkZpbGVzeXN0ZW1PYnNlcnZlcnMgPSBuZXcgV2Vha01hcCgpXG4vKiogQHR5cGUge1dlYWtNYXA8R2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXIsICgoKSA9PiBQcm9taXNlPHZvaWQ+IHwgdm9pZClbXT59ICovXG5jb25zdCBwdWJsaWNhdGlvbkNvbW1pdEd1YXJkcyA9IG5ldyBXZWFrTWFwKClcblxuLyoqXG4gKiBRdWV1ZXMgb25lIGludGVybmFsIGNhbGxlci1vd25lZCBjdXJyZW50bmVzcyBndWFyZCBmb3IgdGhlIHB1Ymxpc2hlcidzIG5leHQgdHJhbnNhY3Rpb24uXG4gKiBUaGUgZ3VhcmQgcnVucyBhZnRlciBldmVyeSB2YWxpZGF0b3IgY2xvc2VzIGFuZCBpbW1lZGlhdGVseSBiZWZvcmUgdGhlIGFjdGl2ZS1wb2ludGVyIGNvbW1pdC5cbiAqIFRoaXMgaGVscGVyIGlzIGludGVudGlvbmFsbHkgbm90IHBhcnQgb2YgdGhlIHBhY2thZ2Ugcm9vdCBBUEkuXG4gKiBAcGFyYW0ge0dlbmVyYXRlZEFydGlmYWN0UHVibGlzaGVyfSBwdWJsaXNoZXIgLSBQdWJsaXNoZXIgaW5zdGFuY2UuXG4gKiBAcGFyYW0geygpID0+IFByb21pc2U8dm9pZD4gfCB2b2lkfSBndWFyZCAtIEV4YWN0IHByZS1jb21taXQgZ3VhcmQuXG4gKiBAcmV0dXJucyB7dm9pZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGd1YXJkTmV4dFB1YmxpY2F0aW9uQ29tbWl0KHB1Ymxpc2hlciwgZ3VhcmQpIHtcbiAgaWYgKCEocHVibGlzaGVyIGluc3RhbmNlb2YgR2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXIpIHx8IHR5cGVvZiBndWFyZCAhPSBcImZ1bmN0aW9uXCIpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiSW52YWxpZCBwdWJsaWNhdGlvbiBjb21taXQgZ3VhcmQuXCIpXG4gIH1cbiAgY29uc3QgcXVldWVkID0gcHVibGljYXRpb25Db21taXRHdWFyZHMuZ2V0KHB1Ymxpc2hlcikgPz8gW11cblxuICBxdWV1ZWQucHVzaChndWFyZClcbiAgcHVibGljYXRpb25Db21taXRHdWFyZHMuc2V0KHB1Ymxpc2hlciwgcXVldWVkKVxufVxuXG4vKipcbiAqIEluc3RhbGxzIG9uZSBpbnRlcm5hbCwgb25lLXNob3QgZGV0ZXJtaW5pc3RpYyBmYWlsdXJlIHVzZWQgYnkgcmVhbC1maWxlc3lzdGVtIGludGVycnVwdGlvbiBzcGVjcy5cbiAqIFRoaXMgaGVscGVyIGlzIGludGVudGlvbmFsbHkgbm90IHBhcnQgb2YgdGhlIHBhY2thZ2Ugcm9vdCBBUEkuXG4gKiBAcGFyYW0ge0dlbmVyYXRlZEFydGlmYWN0UHVibGlzaGVyfSBwdWJsaXNoZXIgLSBQdWJsaXNoZXIgaW5zdGFuY2UuXG4gKiBAcGFyYW0ge1wiYWZ0ZXItc3RhZ2VcIiB8IFwiYmVmb3JlLXBvaW50ZXItcmVwbGFjZVwiIHwgXCJhZnRlci1wb2ludGVyLXJlcGxhY2VcIiB8IFwiYmVmb3JlLWNsZWFudXBcIiB8IFwiYmVmb3JlLWpvdXJuYWwtcmVtb3ZlXCJ9IGJvdW5kYXJ5IC0gRXhhY3QgbGlmZWN5Y2xlIGJvdW5kYXJ5LlxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBJbmplY3RlZCBmYWlsdXJlLlxuICogQHJldHVybnMge3ZvaWR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbmplY3RQdWJsaWNhdGlvbkZhaWx1cmUocHVibGlzaGVyLCBib3VuZGFyeSwgZXJyb3IpIHtcbiAgaWYgKCEocHVibGlzaGVyIGluc3RhbmNlb2YgR2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXIpIHx8ICFwdWJsaWNhdGlvbkJvdW5kYXJpZXMuaGFzKGJvdW5kYXJ5KSB8fCAhKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkludmFsaWQgcHVibGljYXRpb24gZmFpbHVyZSBpbmplY3Rpb24uXCIpXG4gIH1cbiAgaW5qZWN0ZWRQdWJsaWNhdGlvbkZhaWx1cmVzLnNldChwdWJsaXNoZXIsIHtib3VuZGFyeSwgZXJyb3J9KVxufVxuXG4vKipcbiAqIEluc3RhbGxzIGFuIGludGVybmFsIGRldGVybWluaXN0aWMgZmlsZXN5c3RlbS1vcGVyYXRpb24gb2JzZXJ2ZXIgZm9yIGR1cmFiaWxpdHkgc3BlY3MuXG4gKiBUaGlzIGhlbHBlciBpcyBpbnRlbnRpb25hbGx5IG5vdCBwYXJ0IG9mIHRoZSBwYWNrYWdlIHJvb3QgQVBJLlxuICogQHBhcmFtIHtHZW5lcmF0ZWRBcnRpZmFjdFB1Ymxpc2hlcn0gcHVibGlzaGVyIC0gUHVibGlzaGVyIGluc3RhbmNlLlxuICogQHBhcmFtIHsob3BlcmF0aW9uOiBzdHJpbmcpID0+IHZvaWR9IG9ic2VydmVyIC0gU3luY2hyb25vdXMgb3BlcmF0aW9uIG9ic2VydmVyLlxuICogQHJldHVybnMge3ZvaWR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBvYnNlcnZlUHVibGljYXRpb25GaWxlc3lzdGVtKHB1Ymxpc2hlciwgb2JzZXJ2ZXIpIHtcbiAgaWYgKCEocHVibGlzaGVyIGluc3RhbmNlb2YgR2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXIpIHx8IHR5cGVvZiBvYnNlcnZlciAhPSBcImZ1bmN0aW9uXCIpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiSW52YWxpZCBwdWJsaWNhdGlvbiBmaWxlc3lzdGVtIG9ic2VydmVyLlwiKVxuICB9XG4gIHB1YmxpY2F0aW9uRmlsZXN5c3RlbU9ic2VydmVycy5zZXQocHVibGlzaGVyLCBvYnNlcnZlcilcbn1cblxuLyoqXG4gKiBPd25zIHRyYW5zYWN0aW9uYWwgcHVibGljYXRpb24gaW4gb25lIGRlZGljYXRlZCBkaXJlY3RvcnkgYmVuZWF0aCBhIGNhbm9uaWNhbCBwcm9qZWN0IHJvb3QuXG4gKi9cbmV4cG9ydCBjbGFzcyBHZW5lcmF0ZWRBcnRpZmFjdFB1Ymxpc2hlciB7XG4gIC8qKiBAdHlwZSB7c3RyaW5nfSAqL1xuICAjcHJvamVjdElkXG4gIC8qKiBAdHlwZSB7c3RyaW5nfSAqL1xuICAjcHJvamVjdFJvb3RcbiAgLyoqIEB0eXBlIHtzdHJpbmd9ICovXG4gICNwdWJsaWNhdGlvblJvb3RcbiAgLyoqIEB0eXBlIHtyZWFkb25seSBzdHJpbmdbXX0gKi9cbiAgI3NvdXJjZVJvb3RzXG4gIC8qKiBAdHlwZSB7cmVhZG9ubHkgc3RyaW5nW119ICovXG4gICNzb3VyY2VGaWxlc1xuICAvKiogQHR5cGUge1Byb21pc2U8dm9pZD59ICovXG4gICNwdWJsaWNhdGlvblRhaWwgPSBQcm9taXNlLnJlc29sdmUoKVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgcHJvamVjdC1zY29wZWQgcHVibGlzaGVyIHdpdGhvdXQgdG91Y2hpbmcgdGhlIGZpbGVzeXN0ZW0uXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gUHVibGlzaGVyIGlkZW50aXR5IGFuZCBvd25lZCBwYXRocy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IG9wdGlvbnMucHJvamVjdElkIC0gU3RhYmxlIGxvd2VyY2FzZSBwcm9qZWN0IGlkZW50aXR5LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5wcm9qZWN0Um9vdCAtIENhbm9uaWNhbCBhYnNvbHV0ZSBwcm9qZWN0IHJvb3QuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLnB1YmxpY2F0aW9uUm9vdCAtIENhbm9uaWNhbCBhYnNvbHV0ZSBwdWJsaXNoZXItb3duZWQgZGlyZWN0b3J5IGJlbmVhdGggdGhlIHByb2plY3Qgcm9vdC5cbiAgICogQHBhcmFtIHtyZWFkb25seSBzdHJpbmdbXX0gW29wdGlvbnMuc291cmNlUm9vdHNdIC0gQ2Fub25pY2FsIGFic29sdXRlIHByb2plY3Qgc291cmNlIGRpcmVjdG9yaWVzIGtlcHQgZGlzam9pbnQgZnJvbSBwdWJsaWNhdGlvbi5cbiAgICogQHBhcmFtIHtyZWFkb25seSBzdHJpbmdbXX0gW29wdGlvbnMuc291cmNlRmlsZXNdIC0gQ2Fub25pY2FsIGFic29sdXRlIHByb2plY3Qgc291cmNlIGZpbGVzIGtlcHQgZGlzam9pbnQgZnJvbSBwdWJsaWNhdGlvbi5cbiAgICovXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHtcbiAgICBpZiAoIWlzUGxhaW5PYmplY3Qob3B0aW9ucykpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUkVRVUVTVFwiLCBcInB1YmxpY2F0aW9uXCIsIFwiUHVibGlzaGVyIG9wdGlvbnMgbXVzdCBiZSBhIHBsYWluIG9iamVjdC5cIilcbiAgICB9XG4gICAgY29uc3Qge3Byb2plY3RJZCwgcHJvamVjdFJvb3QsIHB1YmxpY2F0aW9uUm9vdCwgc291cmNlRmlsZXMgPSBbXSwgc291cmNlUm9vdHMgPSBbXX0gPSBvcHRpb25zXG5cbiAgICBpZiAoIWlkZW50aXR5UGF0dGVybi50ZXN0KHByb2plY3RJZCA/PyBcIlwiKSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9SRVFVRVNUXCIsIFwicHVibGljYXRpb25cIiwgXCJQcm9qZWN0IGlkZW50aXR5IG11c3QgYmUgYSBzdGFibGUgbG93ZXJjYXNlIElELlwiKVxuICAgIH1cbiAgICBpZiAoIWlzQ2Fub25pY2FsQWJzb2x1dGVQYXRoKHByb2plY3RSb290KSB8fCAhaXNDYW5vbmljYWxBYnNvbHV0ZVBhdGgocHVibGljYXRpb25Sb290KSB8fFxuICAgICAgIWlzU3RyaWN0UGF0aERlc2NlbmRhbnQocHJvamVjdFJvb3QsIHB1YmxpY2F0aW9uUm9vdCkpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiUHJvamVjdCBhbmQgcHVibGljYXRpb24gcm9vdHMgbXVzdCBiZSBjYW5vbmljYWwgYWJzb2x1dGUgcGF0aHMgd2l0aCBwdWJsaWNhdGlvbiBzdHJpY3RseSBiZW5lYXRoIHRoZSBwcm9qZWN0IHJvb3QuXCIpXG4gICAgfVxuICAgIGlmICghaXNEZW5zZUFycmF5KHNvdXJjZVJvb3RzKSB8fCAhaXNEZW5zZUFycmF5KHNvdXJjZUZpbGVzKSB8fCBzb3VyY2VSb290cy5sZW5ndGggKyBzb3VyY2VGaWxlcy5sZW5ndGggPT0gMCkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9ST09UXCIsIHByb2plY3RJZCxcbiAgICAgICAgXCJQdWJsaXNoZXIgcmVxdWlyZXMgY2Fub25pY2FsIGFic29sdXRlIHNvdXJjZSBkaXJlY3RvcmllcyBvciBzb3VyY2UgZmlsZXMuXCIpXG4gICAgfVxuICAgIC8qKiBAdHlwZSB7c3RyaW5nW119ICovXG4gICAgY29uc3QgdmFsaWRhdGVkU291cmNlUm9vdHMgPSBbXVxuXG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IHNvdXJjZVJvb3RzLmxlbmd0aDsgaW5kZXggKz0gMSkge1xuICAgICAgY29uc3Qgc291cmNlUm9vdCA9IHNvdXJjZVJvb3RzW2luZGV4XVxuXG4gICAgICBpZiAoIWlzQ2Fub25pY2FsQWJzb2x1dGVQYXRoKHNvdXJjZVJvb3QpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgICAgXCJTb3VyY2Ugcm9vdHMgbXVzdCBiZSBhIG5vbi1lbXB0eSBhcnJheSBvZiBjYW5vbmljYWwgYWJzb2x1dGUgcGF0aHMuXCIpXG4gICAgICB9XG4gICAgICBpZiAoIWlzUGF0aFdpdGhpbihwcm9qZWN0Um9vdCwgc291cmNlUm9vdCkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9ST09UXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIlNvdXJjZSByb290cyBtdXN0IHJlbWFpbiB3aXRoaW4gdGhlIGNhbm9uaWNhbCBwcm9qZWN0IHJvb3QuXCIpXG4gICAgICB9XG4gICAgICBpZiAocGF0aHNPdmVybGFwKHB1YmxpY2F0aW9uUm9vdCwgc291cmNlUm9vdCkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fU09VUkNFX09WRVJMQVBcIiwgcHJvamVjdElkLFxuICAgICAgICAgIFwiUHVibGljYXRpb24gcm9vdCBhbmQgc291cmNlIHJvb3RzIG11c3QgYmUgZGlzam9pbnQuXCIpXG4gICAgICB9XG4gICAgICB2YWxpZGF0ZWRTb3VyY2VSb290cy5wdXNoKHNvdXJjZVJvb3QpXG4gICAgfVxuICAgIC8qKiBAdHlwZSB7c3RyaW5nW119ICovXG4gICAgY29uc3QgdmFsaWRhdGVkU291cmNlRmlsZXMgPSBbXVxuXG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IHNvdXJjZUZpbGVzLmxlbmd0aDsgaW5kZXggKz0gMSkge1xuICAgICAgY29uc3Qgc291cmNlRmlsZSA9IHNvdXJjZUZpbGVzW2luZGV4XVxuXG4gICAgICBpZiAoIWlzQ2Fub25pY2FsQWJzb2x1dGVQYXRoKHNvdXJjZUZpbGUpIHx8ICFpc1BhdGhXaXRoaW4ocHJvamVjdFJvb3QsIHNvdXJjZUZpbGUpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgICAgXCJTb3VyY2UgZmlsZXMgbXVzdCBiZSBjYW5vbmljYWwgYWJzb2x1dGUgcGF0aHMgd2l0aGluIHRoZSBwcm9qZWN0IHJvb3QuXCIpXG4gICAgICB9XG4gICAgICBpZiAocGF0aHNPdmVybGFwKHB1YmxpY2F0aW9uUm9vdCwgc291cmNlRmlsZSkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fU09VUkNFX09WRVJMQVBcIiwgcHJvamVjdElkLFxuICAgICAgICAgIFwiUHVibGljYXRpb24gcm9vdCBhbmQgc291cmNlIGZpbGVzIG11c3QgYmUgZGlzam9pbnQuXCIpXG4gICAgICB9XG4gICAgICB2YWxpZGF0ZWRTb3VyY2VGaWxlcy5wdXNoKHNvdXJjZUZpbGUpXG4gICAgfVxuXG4gICAgdGhpcy4jcHJvamVjdElkID0gcHJvamVjdElkXG4gICAgdGhpcy4jcHJvamVjdFJvb3QgPSBwcm9qZWN0Um9vdFxuICAgIHRoaXMuI3B1YmxpY2F0aW9uUm9vdCA9IHB1YmxpY2F0aW9uUm9vdFxuICAgIHRoaXMuI3NvdXJjZVJvb3RzID0gT2JqZWN0LmZyZWV6ZSh2YWxpZGF0ZWRTb3VyY2VSb290cylcbiAgICB0aGlzLiNzb3VyY2VGaWxlcyA9IE9iamVjdC5mcmVlemUodmFsaWRhdGVkU291cmNlRmlsZXMpXG4gIH1cblxuICAvKipcbiAgICogU3RhZ2VzLCB2YWxpZGF0ZXMsIGFuZCBwdWJsaXNoZXMgb25lIGNvbXBsZXRlIGltbXV0YWJsZSBwcm9qZWN0IGdlbmVyYXRpb24uXG4gICAqIENhbGxzIG9uIG9uZSBwdWJsaXNoZXIgaW5zdGFuY2UgYXJlIHNlcmlhbGl6ZWQgaW4gaW52b2NhdGlvbiBvcmRlci5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLlB1YmxpY2F0aW9uUmVxdWVzdH0gcmVxdWVzdCAtIENvbXBsZXRlIHB1YmxpY2F0aW9uIHJlcXVlc3QuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5QdWJsaWNhdGlvbk9wdGlvbnN9IFtvcHRpb25zXSAtIFRyYW5zYWN0aW9uIGxpZmVjeWNsZSBjb250cm9scy5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5QdWJsaXNoZWRHZW5lcmF0aW9uPn0gQ29tbWl0dGVkIGltbXV0YWJsZSBzbmFwc2hvdC5cbiAgICovXG4gIHB1Ymxpc2gocmVxdWVzdCwgb3B0aW9ucyA9IHt9KSB7XG4gICAgY29uc3Qgc2lnbmFsID0gdmFsaWRhdGVQdWJsaWNhdGlvbk9wdGlvbnMob3B0aW9ucywgdGhpcy4jcHJvamVjdElkKVxuICAgIGNvbnN0IHF1ZXVlZEd1YXJkcyA9IHB1YmxpY2F0aW9uQ29tbWl0R3VhcmRzLmdldCh0aGlzKVxuICAgIGNvbnN0IGNvbW1pdEd1YXJkID0gcXVldWVkR3VhcmRzPy5zaGlmdCgpXG5cbiAgICBpZiAocXVldWVkR3VhcmRzPy5sZW5ndGggPT0gMCkgcHVibGljYXRpb25Db21taXRHdWFyZHMuZGVsZXRlKHRoaXMpXG4gICAgLyoqIEB0eXBlIHsodmFsdWU6IGltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuUHVibGlzaGVkR2VuZXJhdGlvbikgPT4gdm9pZH0gKi9cbiAgICBsZXQgcmVzb2x2ZVJlc3VsdFxuICAgIC8qKiBAdHlwZSB7KHJlYXNvbj86IHVua25vd24pID0+IHZvaWR9ICovXG4gICAgbGV0IHJlamVjdFJlc3VsdFxuICAgIGNvbnN0IHJlc3VsdCA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHJlc29sdmVSZXN1bHQgPSByZXNvbHZlXG4gICAgICByZWplY3RSZXN1bHQgPSByZWplY3RcbiAgICB9KVxuICAgIGNvbnN0IG9wZXJhdGlvbiA9IHRoaXMuI3B1YmxpY2F0aW9uVGFpbC50aGVuKGFzeW5jICgpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlc29sdmVSZXN1bHQoYXdhaXQgdGhpcy4jcHVibGlzaChyZXF1ZXN0LCBzaWduYWwsIGNvbW1pdEd1YXJkKSlcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHJlamVjdFJlc3VsdChlcnJvcilcbiAgICAgIH1cbiAgICB9KVxuXG4gICAgdGhpcy4jcHVibGljYXRpb25UYWlsID0gb3BlcmF0aW9uLmNhdGNoKCgpID0+IHt9KVxuXG4gICAgcmV0dXJuIHJlc3VsdFxuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIHRoZSBhY3RpdmUgcG9pbnRlciBleGFjdGx5IG9uY2UgYW5kIHZlcmlmaWVzIHRoZSByZWZlcmVuY2VkIGltbXV0YWJsZSBnZW5lcmF0aW9uLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLlB1Ymxpc2hlZEdlbmVyYXRpb24+fSBWZXJpZmllZCBwb2ludGVyLW9uY2Ugc25hcHNob3QuXG4gICAqL1xuICBhc3luYyByZXNvbHZlQWN0aXZlKCkge1xuICAgIGF3YWl0IHRoaXMuI3ByZXBhcmVSb290KClcbiAgICBjb25zdCBwb2ludGVyUGF0aCA9IHBhdGguam9pbih0aGlzLiNwdWJsaWNhdGlvblJvb3QsIGFjdGl2ZVBvaW50ZXJOYW1lKVxuICAgIGNvbnN0IHBvaW50ZXIgPSBhd2FpdCByZWFkQWN0aXZlUG9pbnRlcihwb2ludGVyUGF0aCwgdGhpcy4jcHJvamVjdElkLCBmYWxzZSlcblxuICAgIGlmICghcG9pbnRlcikgcHVibGljYXRpb25GYWlsdXJlKFwiQUNUSVZFX0dFTkVSQVRJT05fTUlTU0lOR1wiLCB0aGlzLiNwcm9qZWN0SWQsIFwiTm8gYWN0aXZlIGdlbmVyYXRpb24gaGFzIGJlZW4gcHVibGlzaGVkLlwiKVxuICAgIGNvbnN0IGdlbmVyYXRpb24gPSBhd2FpdCB2ZXJpZnlQdWJsaXNoZWRHZW5lcmF0aW9uKHRoaXMuI3B1YmxpY2F0aW9uUm9vdCwgdGhpcy4jcHJvamVjdElkLCBwb2ludGVyLCBmYWxzZSlcbiAgICBjb25zdCBjbGVhbnVwUGVuZGluZyA9IGF3YWl0IGhhc0NvbW1pdHRlZENsZWFudXBKb3VybmFsKHRoaXMuI3B1YmxpY2F0aW9uUm9vdCwgdGhpcy4jcHJvamVjdElkLCBwb2ludGVyLmdlbmVyYXRpb25JZClcblxuICAgIHJldHVybiBwdWJsaXNoZWRHZW5lcmF0aW9uKHRoaXMuI3B1YmxpY2F0aW9uUm9vdCwgZ2VuZXJhdGlvbi5tYW5pZmVzdCwgY2xlYW51cFBlbmRpbmcpXG4gIH1cblxuICAvKipcbiAgICogUGVyZm9ybXMgb25lIHNlcmlhbGl6ZWQgcHVibGljYXRpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5QdWJsaWNhdGlvblJlcXVlc3R9IHJlcXVlc3QgLSBDb21wbGV0ZSByZXF1ZXN0LlxuICAgKiBAcGFyYW0ge0Fib3J0U2lnbmFsIHwgdW5kZWZpbmVkfSBzaWduYWwgLSBPcHRpb25hbCB0cmFuc2FjdGlvbiBjYW5jZWxsYXRpb24gYXV0aG9yaXR5LlxuICAgKiBAcGFyYW0geygoKSA9PiBQcm9taXNlPHZvaWQ+IHwgdm9pZCkgfCB1bmRlZmluZWR9IGNvbW1pdEd1YXJkIC0gT3B0aW9uYWwgY3VycmVudG5lc3MgZ3VhcmQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuUHVibGlzaGVkR2VuZXJhdGlvbj59IFB1YmxpY2F0aW9uIHJlc3VsdC5cbiAgICovXG4gIGFzeW5jICNwdWJsaXNoKHJlcXVlc3QsIHNpZ25hbCwgY29tbWl0R3VhcmQpIHtcbiAgICBjb25zdCB2YWxpZGF0ZWQgPSB2YWxpZGF0ZVB1YmxpY2F0aW9uUmVxdWVzdChyZXF1ZXN0LCB0aGlzLiNwcm9qZWN0SWQpXG5cbiAgICBhc3NlcnRQdWJsaWNhdGlvbkFjdGl2ZShzaWduYWwsIHRoaXMuI3Byb2plY3RJZClcbiAgICBhd2FpdCB0aGlzLiNwcmVwYXJlUm9vdCgpXG4gICAgYXdhaXQgcmVjb25jaWxlUHVibGljYXRpb24odGhpcy4jcHVibGljYXRpb25Sb290LCB0aGlzLiNwcm9qZWN0SWQsIHRoaXMpXG4gICAgYXdhaXQgdmVyaWZ5QXRvbWljUmVwbGFjZW1lbnQodGhpcy4jcHVibGljYXRpb25Sb290LCB0aGlzLiNwcm9qZWN0SWQpXG4gICAgYXdhaXQgdmFsaWRhdGVFeGlzdGluZ1BvaW50ZXIodGhpcy4jcHVibGljYXRpb25Sb290LCB0aGlzLiNwcm9qZWN0SWQpXG5cbiAgICBjb25zdCBnZW5lcmF0aW9uUGF0aCA9IHBhdGguam9pbih0aGlzLiNwdWJsaWNhdGlvblJvb3QsIGdlbmVyYXRpb25zTmFtZSwgdmFsaWRhdGVkLmdlbmVyYXRpb25JZClcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBwYXRoLmpvaW4oZ2VuZXJhdGlvblBhdGgsIFwibWFuaWZlc3QuanNvblwiKVxuICAgIGNvbnN0IHRlbXBQb2ludGVyTmFtZSA9IGAuYWN0aXZlLWdlbmVyYXRpb24uJHt2YWxpZGF0ZWQuZ2VuZXJhdGlvbklkfS50bXBgXG4gICAgY29uc3QgdGVtcFBvaW50ZXJQYXRoID0gcGF0aC5qb2luKHRoaXMuI3B1YmxpY2F0aW9uUm9vdCwgdGVtcFBvaW50ZXJOYW1lKVxuICAgIGNvbnN0IGpvdXJuYWxQYXRoID0gcGF0aC5qb2luKHRoaXMuI3B1YmxpY2F0aW9uUm9vdCwgam91cm5hbE5hbWUsIGAke3ZhbGlkYXRlZC5nZW5lcmF0aW9uSWR9Lmpzb25gKVxuICAgIGNvbnN0IHJldGFpbmVkR2VuZXJhdGlvbiA9IGF3YWl0IG9wdGlvbmFsU3RhdHVzKGdlbmVyYXRpb25QYXRoLCB0aGlzLiNwcm9qZWN0SWQsIFwicmV0YWluZWQgZ2VuZXJhdGlvblwiKSAhPT0gbnVsbFxuICAgIGxldCBjb21taXR0ZWQgPSBmYWxzZVxuICAgIGxldCBqb3VybmFsQ3JlYXRlZCA9IGZhbHNlXG4gICAgbGV0IGdlbmVyYXRpb25DcmVhdGVkID0gZmFsc2VcbiAgICBsZXQgdGVtcFBvaW50ZXJDcmVhdGVkID0gZmFsc2VcbiAgICBsZXQgZmFpbHVyZUNvZGUgPSBcIlBVQkxJQ0FUSU9OX1NUQUdFX1dSSVRFX0ZBSUxFRFwiXG4gICAgLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRpb25NYW5pZmVzdCB8IG51bGx9ICovXG4gICAgbGV0IG1hbmlmZXN0ID0gbnVsbFxuICAgIC8qKiBAdHlwZSB7QnVmZmVyIHwgbnVsbH0gKi9cbiAgICBsZXQgbWFuaWZlc3RCeXRlcyA9IG51bGxcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBhc3NlcnRBYnNlbnQodGVtcFBvaW50ZXJQYXRoLCBcIlBVQkxJQ0FUSU9OX1VOT1dORURfQ09ORkxJQ1RcIiwgdGhpcy4jcHJvamVjdElkLFxuICAgICAgICBcIlRlbXBvcmFyeSBhY3RpdmUgcG9pbnRlciBwYXRoIGlzIGFscmVhZHkgb3duZWQgb3IgYW1iaWd1b3VzLlwiKVxuICAgICAgYXdhaXQgYXNzZXJ0QWJzZW50KGpvdXJuYWxQYXRoLCBcIlBVQkxJQ0FUSU9OX1VOT1dORURfQ09ORkxJQ1RcIiwgdGhpcy4jcHJvamVjdElkLFxuICAgICAgICBcIlB1YmxpY2F0aW9uIGpvdXJuYWwgaWRlbnRpdHkgaXMgYWxyZWFkeSBvd25lZCBvciBhbWJpZ3VvdXMuXCIpXG4gICAgICBpZiAocmV0YWluZWRHZW5lcmF0aW9uKSB7XG4gICAgICAgIGNvbnN0IHJldGFpbmVkID0gYXdhaXQgdmVyaWZ5UmV0YWluZWRHZW5lcmF0aW9uKGdlbmVyYXRpb25QYXRoLCB2YWxpZGF0ZWQsIHRoaXMuI3Byb2plY3RJZClcblxuICAgICAgICBtYW5pZmVzdCA9IHJldGFpbmVkLm1hbmlmZXN0XG4gICAgICAgIG1hbmlmZXN0Qnl0ZXMgPSByZXRhaW5lZC5tYW5pZmVzdEJ5dGVzXG4gICAgICB9XG4gICAgICBjb25zdCBqb3VybmFsID0gcmV0YWluZWRHZW5lcmF0aW9uXG4gICAgICAgID8ge1xuICAgICAgICAgICAgZ2VuZXJhdGlvbklkOiB2YWxpZGF0ZWQuZ2VuZXJhdGlvbklkLFxuICAgICAgICAgICAgb3BlcmF0aW9uOiBcInJlYWN0aXZhdGVcIixcbiAgICAgICAgICAgIHByb2plY3RJZDogdGhpcy4jcHJvamVjdElkLFxuICAgICAgICAgICAgc2NoZW1hOiBcIlNlbWFudGlmb2xkUHVibGljYXRpb25Kb3VybmFsXCIsXG4gICAgICAgICAgICB0ZW1wUG9pbnRlcjogdGVtcFBvaW50ZXJOYW1lLFxuICAgICAgICAgICAgdmVyc2lvbjogMlxuICAgICAgICAgIH1cbiAgICAgICAgOiB7XG4gICAgICAgICAgICBnZW5lcmF0aW9uSWQ6IHZhbGlkYXRlZC5nZW5lcmF0aW9uSWQsXG4gICAgICAgICAgICBwcm9qZWN0SWQ6IHRoaXMuI3Byb2plY3RJZCxcbiAgICAgICAgICAgIHNjaGVtYTogXCJTZW1hbnRpZm9sZFB1YmxpY2F0aW9uSm91cm5hbFwiLFxuICAgICAgICAgICAgdGVtcFBvaW50ZXI6IHRlbXBQb2ludGVyTmFtZSxcbiAgICAgICAgICAgIHZlcnNpb246IDFcbiAgICAgICAgICB9XG5cbiAgICAgIGF3YWl0IHdyaXRlU3luY2VkRmlsZShqb3VybmFsUGF0aCwgc2VyaWFsaXplSnNvbihqb3VybmFsKSwgXCJ3eFwiKVxuICAgICAgam91cm5hbENyZWF0ZWQgPSB0cnVlXG4gICAgICBhd2FpdCBzeW5jRGlyZWN0b3J5KHBhdGguZGlybmFtZShqb3VybmFsUGF0aCkpXG4gICAgICBpZiAoIXJldGFpbmVkR2VuZXJhdGlvbikge1xuICAgICAgICBhd2FpdCBta2RpcihnZW5lcmF0aW9uUGF0aCwge21vZGU6IDBvNzAwfSlcbiAgICAgICAgZ2VuZXJhdGlvbkNyZWF0ZWQgPSB0cnVlXG4gICAgICAgIGF3YWl0IHN5bmNEaXJlY3RvcnkocGF0aC5kaXJuYW1lKGdlbmVyYXRpb25QYXRoKSlcblxuICAgICAgICAvKiogQHR5cGUge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvblRhcmdldE1hbmlmZXN0W119ICovXG4gICAgICAgIGNvbnN0IHRhcmdldE1hbmlmZXN0cyA9IFtdXG5cbiAgICAgICAgZm9yIChjb25zdCB0YXJnZXQgb2YgdmFsaWRhdGVkLnRhcmdldHMpIHtcbiAgICAgICAgICB0YXJnZXRNYW5pZmVzdHMucHVzaChhd2FpdCBzdGFnZVRhcmdldChnZW5lcmF0aW9uUGF0aCwgdGFyZ2V0LCB0aGlzLiNwcm9qZWN0SWQpKVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG1hbmlmZXN0Q2FuZGlkYXRlID0gLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRpb25NYW5pZmVzdH0gKi8gKHtcbiAgICAgICAgICBnZW5lcmF0aW9uSWQ6IHZhbGlkYXRlZC5nZW5lcmF0aW9uSWQsXG4gICAgICAgICAgcHJvamVjdElkOiB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgc2NoZW1hOiAvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJTZW1hbnRpZm9sZEdlbmVyYXRpb25NYW5pZmVzdFwiKSxcbiAgICAgICAgICB0YXJnZXRzOiB0YXJnZXRNYW5pZmVzdHMsXG4gICAgICAgICAgdmVyc2lvbjogLyoqIEB0eXBlIHtjb25zdH0gKi8gKDEpXG4gICAgICAgIH0pXG5cbiAgICAgICAgbWFuaWZlc3RCeXRlcyA9IHNlcmlhbGl6ZUpzb24obWFuaWZlc3RDYW5kaWRhdGUpXG4gICAgICAgIG1hbmlmZXN0ID0gdmFsaWRhdGVHZW5lcmF0aW9uTWFuaWZlc3QoSlNPTi5wYXJzZShtYW5pZmVzdEJ5dGVzLnRvU3RyaW5nKFwidXRmOFwiKSksIHRoaXMuI3Byb2plY3RJZCxcbiAgICAgICAgICB2YWxpZGF0ZWQuZ2VuZXJhdGlvbklkKVxuXG4gICAgICAgIGF3YWl0IHdyaXRlU3luY2VkRmlsZShtYW5pZmVzdFBhdGgsIG1hbmlmZXN0Qnl0ZXMsIFwid3hcIilcbiAgICAgICAgYXdhaXQgc3luY1RyZWVEaXJlY3RvcmllcyhnZW5lcmF0aW9uUGF0aClcbiAgICAgICAgYXdhaXQgdmVyaWZ5TWFuaWZlc3RGaWxlcyhnZW5lcmF0aW9uUGF0aCwgbWFuaWZlc3QsIHRoaXMuI3Byb2plY3RJZClcbiAgICAgIH1cbiAgICAgIGlmIChtYW5pZmVzdCA9PT0gbnVsbCB8fCBtYW5pZmVzdEJ5dGVzID09PSBudWxsKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1NUQUdFX1dSSVRFX0ZBSUxFRFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgXCJDYW5kaWRhdGUgZ2VuZXJhdGlvbiBtYW5pZmVzdCBjb3VsZCBub3QgYmUgcHJlcGFyZWQuXCIpXG4gICAgICB9XG4gICAgICByZWFjaFB1YmxpY2F0aW9uQm91bmRhcnkodGhpcywgXCJhZnRlci1zdGFnZVwiKVxuICAgICAgZmFpbHVyZUNvZGUgPSBcIlBVQkxJQ0FUSU9OX1BPSU5URVJfRkFJTEVEXCJcblxuICAgICAgY29uc3QgcG9pbnRlciA9IHtcbiAgICAgICAgZ2VuZXJhdGlvbklkOiB2YWxpZGF0ZWQuZ2VuZXJhdGlvbklkLFxuICAgICAgICBtYW5pZmVzdEhhc2g6IHNoYTI1NihtYW5pZmVzdEJ5dGVzKSxcbiAgICAgICAgcHJvamVjdElkOiB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgIHNjaGVtYTogXCJTZW1hbnRpZm9sZEFjdGl2ZUdlbmVyYXRpb25cIixcbiAgICAgICAgdmVyc2lvbjogMVxuICAgICAgfVxuXG4gICAgICBhd2FpdCB3cml0ZVN5bmNlZEZpbGUodGVtcFBvaW50ZXJQYXRoLCBzZXJpYWxpemVKc29uKHBvaW50ZXIpLCBcInd4XCIsICgpID0+IHtcbiAgICAgICAgdGVtcFBvaW50ZXJDcmVhdGVkID0gdHJ1ZVxuICAgICAgfSlcbiAgICAgIHJlYWNoUHVibGljYXRpb25Cb3VuZGFyeSh0aGlzLCBcImJlZm9yZS1wb2ludGVyLXJlcGxhY2VcIilcbiAgICAgIGF3YWl0IGNvbW1pdEd1YXJkPy4oKVxuICAgICAgYXNzZXJ0UHVibGljYXRpb25BY3RpdmUoc2lnbmFsLCB0aGlzLiNwcm9qZWN0SWQpXG4gICAgICBhd2FpdCByZW5hbWUodGVtcFBvaW50ZXJQYXRoLCBwYXRoLmpvaW4odGhpcy4jcHVibGljYXRpb25Sb290LCBhY3RpdmVQb2ludGVyTmFtZSkpXG4gICAgICB0ZW1wUG9pbnRlckNyZWF0ZWQgPSBmYWxzZVxuICAgICAgY29tbWl0dGVkID0gdHJ1ZVxuICAgICAgcmVhY2hQdWJsaWNhdGlvbkJvdW5kYXJ5KHRoaXMsIFwiYWZ0ZXItcG9pbnRlci1yZXBsYWNlXCIpXG4gICAgICBhd2FpdCBzeW5jRGlyZWN0b3J5KHRoaXMuI3B1YmxpY2F0aW9uUm9vdClcblxuICAgICAgbGV0IGNsZWFudXBQZW5kaW5nID0gZmFsc2VcblxuICAgICAgdHJ5IHtcbiAgICAgICAgcmVhY2hQdWJsaWNhdGlvbkJvdW5kYXJ5KHRoaXMsIFwiYmVmb3JlLWNsZWFudXBcIilcbiAgICAgICAgYXdhaXQgcmVtb3ZlSm91cm5hbE93bmVkU3RhdGUoe1xuICAgICAgICAgIGpvdXJuYWxQYXRoLFxuICAgICAgICAgIHByb2plY3RJZDogdGhpcy4jcHJvamVjdElkLFxuICAgICAgICAgIHB1YmxpY2F0aW9uUm9vdDogdGhpcy4jcHVibGljYXRpb25Sb290LFxuICAgICAgICAgIHB1Ymxpc2hlcjogdGhpc1xuICAgICAgICB9KVxuICAgICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICAgIGNsZWFudXBQZW5kaW5nID0gdHJ1ZVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gcHVibGlzaGVkR2VuZXJhdGlvbih0aGlzLiNwdWJsaWNhdGlvblJvb3QsIGRlZXBGcmVlemUobWFuaWZlc3QpLCBjbGVhbnVwUGVuZGluZylcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKCFjb21taXR0ZWQpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCByZW1vdmVKb3VybmFsT3duZWRTdGF0ZSh7XG4gICAgICAgICAgICAuLi4oZ2VuZXJhdGlvbkNyZWF0ZWQgPyB7Y2FuZGlkYXRlUGF0aDogZ2VuZXJhdGlvblBhdGh9IDoge30pLFxuICAgICAgICAgICAgLi4uKGpvdXJuYWxDcmVhdGVkID8ge2pvdXJuYWxQYXRofSA6IHt9KSxcbiAgICAgICAgICAgIHByb2plY3RJZDogdGhpcy4jcHJvamVjdElkLFxuICAgICAgICAgICAgcHVibGljYXRpb25Sb290OiB0aGlzLiNwdWJsaWNhdGlvblJvb3QsXG4gICAgICAgICAgICBwdWJsaXNoZXI6IHRoaXMsXG4gICAgICAgICAgICAuLi4odGVtcFBvaW50ZXJDcmVhdGVkID8ge3RlbXBvcmFyeVBvaW50ZXJQYXRoOiB0ZW1wUG9pbnRlclBhdGh9IDoge30pXG4gICAgICAgICAgfSlcbiAgICAgICAgfSBjYXRjaCAoY2xlYW51cEVycm9yKSB7XG4gICAgICAgICAgaWYgKGNsZWFudXBFcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYykgdGhyb3cgY2xlYW51cEVycm9yXG4gICAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHRoaXMuI3Byb2plY3RJZCxcbiAgICAgICAgICAgIFwiRmFpbGVkIHB1YmxpY2F0aW9uIGxlZnQgc3RhdGUgdGhhdCByZXF1aXJlcyByZWNvdmVyeS5cIiwgY2xlYW51cEVycm9yKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTZW1hbnRpZm9sZERpYWdub3N0aWMpIHRocm93IGVycm9yXG5cbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShmYWlsdXJlQ29kZSwgdGhpcy4jcHJvamVjdElkLFxuICAgICAgICBjb21taXR0ZWRcbiAgICAgICAgICA/IFwiQWN0aXZlIGdlbmVyYXRpb24gY2hhbmdlZCwgYnV0IGR1cmFibGUgcG9pbnRlciBwdWJsaWNhdGlvbiBkaWQgbm90IGNvbXBsZXRlLlwiXG4gICAgICAgICAgOiBcIkNhbmRpZGF0ZSBnZW5lcmF0aW9uIGNvdWxkIG5vdCBiZSBzdGFnZWQuXCIsIGVycm9yKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuZCB2YWxpZGF0ZXMgb3duZWQgcm9vdCBkaXJlY3RvcmllcyBhbmQgc291cmNlLXJvb3Qgc2VwYXJhdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IENvbXBsZXRpb24uXG4gICAqL1xuICBhc3luYyAjcHJlcGFyZVJvb3QoKSB7XG4gICAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5V2l0aG91dFN5bWxpbmtzKHRoaXMuI3Byb2plY3RSb290LCBmYWxzZSwgdGhpcy4jcHJvamVjdElkKVxuICAgIGF3YWl0IGVuc3VyZURpcmVjdG9yeVdpdGhvdXRTeW1saW5rcyh0aGlzLiNwdWJsaWNhdGlvblJvb3QsIHRydWUsIHRoaXMuI3Byb2plY3RJZClcbiAgICBsZXQgZXhhY3RQcm9qZWN0XG4gICAgbGV0IGV4YWN0UHVibGljYXRpb25cblxuICAgIHRyeSB7XG4gICAgICBleGFjdFByb2plY3QgPSBhd2FpdCByZWFscGF0aCh0aGlzLiNwcm9qZWN0Um9vdClcbiAgICAgIGV4YWN0UHVibGljYXRpb24gPSBhd2FpdCByZWFscGF0aCh0aGlzLiNwdWJsaWNhdGlvblJvb3QpXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgIFwiUHVibGljYXRpb24gcm9vdCBjb3VsZCBub3QgYmUgcmVzb2x2ZWQgc2FmZWx5LlwiLCBlcnJvcilcbiAgICB9XG4gICAgaWYgKCFpc1N0cmljdFBhdGhEZXNjZW5kYW50KGV4YWN0UHJvamVjdCwgZXhhY3RQdWJsaWNhdGlvbikpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgIFwiUHVibGljYXRpb24gcm9vdCBtdXN0IHJlc29sdmUgc3RyaWN0bHkgYmVuZWF0aCB0aGUgcHJvamVjdCByb290LlwiKVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IHNvdXJjZVJvb3Qgb2YgdGhpcy4jc291cmNlUm9vdHMpIHtcbiAgICAgIGF3YWl0IGVuc3VyZURpcmVjdG9yeVdpdGhvdXRTeW1saW5rcyhzb3VyY2VSb290LCBmYWxzZSwgdGhpcy4jcHJvamVjdElkKVxuICAgICAgbGV0IGV4YWN0U291cmNlXG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGV4YWN0U291cmNlID0gYXdhaXQgcmVhbHBhdGgoc291cmNlUm9vdClcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgXCJTb3VyY2Ugcm9vdCBjb3VsZCBub3QgYmUgcmVzb2x2ZWQgc2FmZWx5LlwiLCBlcnJvcilcbiAgICAgIH1cblxuICAgICAgaWYgKCFpc1BhdGhXaXRoaW4oZXhhY3RQcm9qZWN0LCBleGFjdFNvdXJjZSkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9ST09UXCIsIHRoaXMuI3Byb2plY3RJZCxcbiAgICAgICAgICBcIlNvdXJjZSByb290cyBtdXN0IHJlc29sdmUgd2l0aGluIHRoZSBwcm9qZWN0IHJvb3QuXCIpXG4gICAgICB9XG4gICAgICBpZiAocGF0aHNPdmVybGFwKGV4YWN0UHVibGljYXRpb24sIGV4YWN0U291cmNlKSkge1xuICAgICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9TT1VSQ0VfT1ZFUkxBUFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgXCJQdWJsaWNhdGlvbiByb290IGFuZCBzb3VyY2Ugcm9vdHMgcmVzb2x2ZSB0byBvdmVybGFwcGluZyBwYXRocy5cIilcbiAgICAgIH1cbiAgICB9XG4gICAgZm9yIChjb25zdCBzb3VyY2VGaWxlIG9mIHRoaXMuI3NvdXJjZUZpbGVzKSB7XG4gICAgICBhd2FpdCBlbnN1cmVEaXJlY3RvcnlXaXRob3V0U3ltbGlua3MocGF0aC5kaXJuYW1lKHNvdXJjZUZpbGUpLCBmYWxzZSwgdGhpcy4jcHJvamVjdElkKVxuICAgICAgbGV0IHN0YXR1c1xuICAgICAgbGV0IGV4YWN0U291cmNlXG5cbiAgICAgIHRyeSB7XG4gICAgICAgIHN0YXR1cyA9IGF3YWl0IGxzdGF0KHNvdXJjZUZpbGUpXG4gICAgICAgIGV4YWN0U291cmNlID0gYXdhaXQgcmVhbHBhdGgoc291cmNlRmlsZSlcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgXCJTb3VyY2UgZmlsZSBjb3VsZCBub3QgYmUgcmVzb2x2ZWQgc2FmZWx5LlwiLCBlcnJvcilcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0dXMuaXNTeW1ib2xpY0xpbmsoKSkge1xuICAgICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9TWU1MSU5LX1RSQVZFUlNBTFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgXCJTb3VyY2UgZmlsZSB0cmF2ZXJzYWwgZW5jb3VudGVyZWQgYSBzeW1ib2xpYyBsaW5rLlwiKVxuICAgICAgfVxuICAgICAgaWYgKCFzdGF0dXMuaXNGaWxlKCkgfHwgZXhhY3RTb3VyY2UgIT0gc291cmNlRmlsZSB8fCAhaXNQYXRoV2l0aGluKGV4YWN0UHJvamVjdCwgZXhhY3RTb3VyY2UpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCB0aGlzLiNwcm9qZWN0SWQsXG4gICAgICAgICAgXCJTb3VyY2UgZmlsZXMgbXVzdCByZXNvbHZlIHRvIHJlYWwgcmVndWxhciBmaWxlcyB3aXRoaW4gdGhlIHByb2plY3Qgcm9vdC5cIilcbiAgICAgIH1cbiAgICAgIGlmIChwYXRoc092ZXJsYXAoZXhhY3RQdWJsaWNhdGlvbiwgZXhhY3RTb3VyY2UpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1NPVVJDRV9PVkVSTEFQXCIsIHRoaXMuI3Byb2plY3RJZCxcbiAgICAgICAgICBcIlB1YmxpY2F0aW9uIHJvb3QgYW5kIHNvdXJjZSBmaWxlcyByZXNvbHZlIHRvIG92ZXJsYXBwaW5nIHBhdGhzLlwiKVxuICAgICAgfVxuICAgIH1cbiAgICBhd2FpdCBlbnN1cmVPd25lZERpcmVjdG9yeShwYXRoLmpvaW4odGhpcy4jcHVibGljYXRpb25Sb290LCBnZW5lcmF0aW9uc05hbWUpLCB0aGlzLiNwcm9qZWN0SWQpXG4gICAgYXdhaXQgZW5zdXJlT3duZWREaXJlY3RvcnkocGF0aC5qb2luKHRoaXMuI3B1YmxpY2F0aW9uUm9vdCwgam91cm5hbE5hbWUpLCB0aGlzLiNwcm9qZWN0SWQpXG4gIH1cbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgYW5kIHNuYXBzaG90cyB0cmFuc2FjdGlvbiBsaWZlY3ljbGUgY29udHJvbHMgYmVmb3JlIHB1YmxpY2F0aW9uIGlzIHF1ZXVlZC5cbiAqIEBwYXJhbSB7dW5rbm93bn0gb3B0aW9ucyAtIENhbmRpZGF0ZSBvcHRpb25zLlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgZGlhZ25vc3RpYyBpZGVudGl0eS5cbiAqIEByZXR1cm5zIHtBYm9ydFNpZ25hbCB8IHVuZGVmaW5lZH0gT3B0aW9uYWwgY2FuY2VsbGF0aW9uIHNpZ25hbC5cbiAqL1xuZnVuY3Rpb24gdmFsaWRhdGVQdWJsaWNhdGlvbk9wdGlvbnMob3B0aW9ucywgcHJvamVjdElkKSB7XG4gIGlmICghaXNQbGFpbk9iamVjdChvcHRpb25zKSB8fCBPYmplY3Qua2V5cyhvcHRpb25zKS5zb21lKGtleSA9PiBrZXkgIT0gXCJzaWduYWxcIikgfHxcbiAgICBvcHRpb25zLnNpZ25hbCAhPT0gdW5kZWZpbmVkICYmICEob3B0aW9ucy5zaWduYWwgaW5zdGFuY2VvZiBBYm9ydFNpZ25hbCkpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJQdWJsaWNhdGlvbiBvcHRpb25zIHJlcXVpcmUgb25seSBhbiBvcHRpb25hbCBBYm9ydFNpZ25hbC5cIilcbiAgfVxuXG4gIHJldHVybiAvKiogQHR5cGUge0Fib3J0U2lnbmFsIHwgdW5kZWZpbmVkfSAqLyAob3B0aW9ucy5zaWduYWwpXG59XG5cbi8qKlxuICogUmVqZWN0cyBjYW5jZWxsYXRpb24gYmVmb3JlIHRoZSBhdG9taWMgYWN0aXZlLXBvaW50ZXIgY29tbWl0IGJlZ2lucy5cbiAqIEBwYXJhbSB7QWJvcnRTaWduYWwgfCB1bmRlZmluZWR9IHNpZ25hbCAtIE9wdGlvbmFsIHRyYW5zYWN0aW9uIGNhbmNlbGxhdGlvbiBhdXRob3JpdHkuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBkaWFnbm9zdGljIGlkZW50aXR5LlxuICogQHJldHVybnMge3ZvaWR9XG4gKi9cbmZ1bmN0aW9uIGFzc2VydFB1YmxpY2F0aW9uQWN0aXZlKHNpZ25hbCwgcHJvamVjdElkKSB7XG4gIGlmIChzaWduYWw/LmFib3J0ZWQpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9DQU5DRUxMRURcIiwgcHJvamVjdElkLFxuICAgICAgXCJQdWJsaWNhdGlvbiB3YXMgY2FuY2VsbGVkIGJlZm9yZSB0aGUgYWN0aXZlIGdlbmVyYXRpb24gcG9pbnRlciBjb21taXQuXCIpXG4gIH1cbn1cblxuLyoqXG4gKiBUaHJvd3MgYW5kIGNvbnN1bWVzIGEgY29uZmlndXJlZCBpbnRlcm5hbCBmYWlsdXJlIGF0IGFuIGV4YWN0IGxpZmVjeWNsZSBib3VuZGFyeS5cbiAqIEBwYXJhbSB7R2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXJ9IHB1Ymxpc2hlciAtIEFjdGl2ZSBwdWJsaXNoZXIuXG4gKiBAcGFyYW0ge3N0cmluZ30gYm91bmRhcnkgLSBSZWFjaGVkIGJvdW5kYXJ5LlxuICogQHJldHVybnMge3ZvaWR9XG4gKi9cbmZ1bmN0aW9uIHJlYWNoUHVibGljYXRpb25Cb3VuZGFyeShwdWJsaXNoZXIsIGJvdW5kYXJ5KSB7XG4gIGNvbnN0IGluamVjdGlvbiA9IGluamVjdGVkUHVibGljYXRpb25GYWlsdXJlcy5nZXQocHVibGlzaGVyKVxuXG4gIGlmIChpbmplY3Rpb24/LmJvdW5kYXJ5ID09IGJvdW5kYXJ5KSB7XG4gICAgaW5qZWN0ZWRQdWJsaWNhdGlvbkZhaWx1cmVzLmRlbGV0ZShwdWJsaXNoZXIpXG4gICAgdGhyb3cgaW5qZWN0aW9uLmVycm9yXG4gIH1cbn1cblxuLyoqXG4gKiBSZXBvcnRzIG9uZSBjb21wbGV0ZWQgZmlsZXN5c3RlbSBvcGVyYXRpb24gdG8gdGhlIGludGVybmFsIGRldGVybWluaXN0aWMgc2VhbS5cbiAqIEBwYXJhbSB7R2VuZXJhdGVkQXJ0aWZhY3RQdWJsaXNoZXJ9IHB1Ymxpc2hlciAtIEFjdGl2ZSBwdWJsaXNoZXIuXG4gKiBAcGFyYW0ge3N0cmluZ30gb3BlcmF0aW9uIC0gQ29tcGxldGVkIG9wZXJhdGlvbi5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICovXG5mdW5jdGlvbiBvYnNlcnZlUHVibGljYXRpb25GaWxlc3lzdGVtT3BlcmF0aW9uKHB1Ymxpc2hlciwgb3BlcmF0aW9uKSB7XG4gIHB1YmxpY2F0aW9uRmlsZXN5c3RlbU9ic2VydmVycy5nZXQocHVibGlzaGVyKT8uKG9wZXJhdGlvbilcbn1cblxuLyoqXG4gKiBSZWNvbmNpbGVzIG9ubHkgY2FuZGlkYXRlcyBhbmQgdGVtcG9yYXJ5IHBvaW50ZXJzIHdob3NlIG93bmVyc2hpcCBpcyBwcm92ZW4gYnkgYSBzdHJpY3Qgam91cm5hbC5cbiAqIFRoZSBhY3RpdmUgcG9pbnRlciByZW1haW5zIHRoZSBzb2xlIGNvbW1pdCBhdXRob3JpdHkuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHVibGljYXRpb25Sb290IC0gUHVibGljYXRpb24gcm9vdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHBhcmFtIHtHZW5lcmF0ZWRBcnRpZmFjdFB1Ymxpc2hlcn0gcHVibGlzaGVyIC0gQWN0aXZlIHB1Ymxpc2hlci5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBDb21wbGV0aW9uLlxuICovXG5hc3luYyBmdW5jdGlvbiByZWNvbmNpbGVQdWJsaWNhdGlvbihwdWJsaWNhdGlvblJvb3QsIHByb2plY3RJZCwgcHVibGlzaGVyKSB7XG4gIGNvbnN0IGpvdXJuYWxzID0gYXdhaXQgcmVhZFB1YmxpY2F0aW9uSm91cm5hbHMocHVibGljYXRpb25Sb290LCBwcm9qZWN0SWQpXG5cbiAgaWYgKGpvdXJuYWxzLmxlbmd0aCA9PSAwKSByZXR1cm5cbiAgY29uc3QgcG9pbnRlciA9IGF3YWl0IHJlYWRBY3RpdmVQb2ludGVyKHBhdGguam9pbihwdWJsaWNhdGlvblJvb3QsIGFjdGl2ZVBvaW50ZXJOYW1lKSwgcHJvamVjdElkLCB0cnVlKVxuXG4gIGlmIChwb2ludGVyKSBhd2FpdCB2ZXJpZnlQdWJsaXNoZWRHZW5lcmF0aW9uKHB1YmxpY2F0aW9uUm9vdCwgcHJvamVjdElkLCBwb2ludGVyLCB0cnVlKVxuICBmb3IgKGNvbnN0IHtqb3VybmFsLCBqb3VybmFsUGF0aH0gb2Ygam91cm5hbHMpIHtcbiAgICBjb25zdCBjYW5kaWRhdGVQYXRoID0gb3duZWRQYXRoKHBhdGguam9pbihwdWJsaWNhdGlvblJvb3QsIGdlbmVyYXRpb25zTmFtZSksIGpvdXJuYWwuZ2VuZXJhdGlvbklkLCBwcm9qZWN0SWQpXG4gICAgY29uc3QgdGVtcG9yYXJ5UG9pbnRlclBhdGggPSBwYXRoLmpvaW4ocHVibGljYXRpb25Sb290LCBqb3VybmFsLnRlbXBQb2ludGVyKVxuICAgIGNvbnN0IGNvbW1pdHRlZCA9IHBvaW50ZXI/LmdlbmVyYXRpb25JZCA9PSBqb3VybmFsLmdlbmVyYXRpb25JZFxuICAgIGNvbnN0IGNhbmRpZGF0ZU93bmVkID0gam91cm5hbC52ZXJzaW9uID09IDFcblxuICAgIGlmIChjYW5kaWRhdGVPd25lZCAmJiAhY29tbWl0dGVkKSB7XG4gICAgICBjb25zdCBjYW5kaWRhdGVTdGF0dXMgPSBhd2FpdCBvcHRpb25hbFN0YXR1cyhjYW5kaWRhdGVQYXRoLCBwcm9qZWN0SWQsIFwiY2FuZGlkYXRlIGdlbmVyYXRpb25cIilcblxuICAgICAgaWYgKGNhbmRpZGF0ZVN0YXR1cykge1xuICAgICAgICBpZiAoIWNhbmRpZGF0ZVN0YXR1cy5pc0RpcmVjdG9yeSgpIHx8IGNhbmRpZGF0ZVN0YXR1cy5pc1N5bWJvbGljTGluaygpKSB7XG4gICAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgICAgIFwiSm91cm5hbC1vd25lZCBjYW5kaWRhdGUgaXMgbm90IGEgcmVhbCBkaXJlY3RvcnkuXCIpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgdGVtcG9yYXJ5U3RhdHVzID0gYXdhaXQgb3B0aW9uYWxTdGF0dXModGVtcG9yYXJ5UG9pbnRlclBhdGgsIHByb2plY3RJZCwgXCJ0ZW1wb3JhcnkgcG9pbnRlclwiKVxuXG4gICAgaWYgKHRlbXBvcmFyeVN0YXR1cykge1xuICAgICAgaWYgKCF0ZW1wb3JhcnlTdGF0dXMuaXNGaWxlKCkgfHwgdGVtcG9yYXJ5U3RhdHVzLmlzU3ltYm9saWNMaW5rKCkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIkpvdXJuYWwtb3duZWQgdGVtcG9yYXJ5IHBvaW50ZXIgaXMgbm90IGEgcmVndWxhciBmaWxlLlwiKVxuICAgICAgfVxuICAgIH1cbiAgICBhd2FpdCByZW1vdmVKb3VybmFsT3duZWRTdGF0ZSh7XG4gICAgICAuLi4oY2FuZGlkYXRlT3duZWQgJiYgIWNvbW1pdHRlZCA/IHtjYW5kaWRhdGVQYXRofSA6IHt9KSxcbiAgICAgIGpvdXJuYWxQYXRoLFxuICAgICAgcHJvamVjdElkLFxuICAgICAgcHVibGljYXRpb25Sb290LFxuICAgICAgcHVibGlzaGVyLFxuICAgICAgdGVtcG9yYXJ5UG9pbnRlclBhdGhcbiAgICB9KVxuICB9XG59XG5cbi8qKlxuICogUmVhZHMgYW5kIHZhbGlkYXRlcyBldmVyeSByZWNvdmVyeSBqb3VybmFsIHdpdGhvdXQgY2hhbmdpbmcgcHVibGljYXRpb24gc3RhdGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHVibGljYXRpb25Sb290IC0gUHVibGljYXRpb24gcm9vdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHJldHVybnMge1Byb21pc2U8cmVhZG9ubHkge2pvdXJuYWw6IFB1YmxpY2F0aW9uSm91cm5hbCwgam91cm5hbFBhdGg6IHN0cmluZ31bXT59IE9yZGVyZWQgam91cm5hbHMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlYWRQdWJsaWNhdGlvbkpvdXJuYWxzKHB1YmxpY2F0aW9uUm9vdCwgcHJvamVjdElkKSB7XG4gIGNvbnN0IGpvdXJuYWxEaXJlY3RvcnkgPSBwYXRoLmpvaW4ocHVibGljYXRpb25Sb290LCBqb3VybmFsTmFtZSlcbiAgbGV0IGVudHJpZXNcblxuICB0cnkge1xuICAgIGVudHJpZXMgPSBhd2FpdCByZWFkZGlyKGpvdXJuYWxEaXJlY3RvcnkpXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgIFwiUHVibGljYXRpb24gam91cm5hbCBkaXJlY3RvcnkgY291bGQgbm90IGJlIGluc3BlY3RlZC5cIiwgZXJyb3IpXG4gIH1cbiAgZW50cmllcy5zb3J0KGNvbXBhcmVTdHJpbmdzKVxuICAvKiogQHR5cGUge3tqb3VybmFsOiBQdWJsaWNhdGlvbkpvdXJuYWwsIGpvdXJuYWxQYXRoOiBzdHJpbmd9W119ICovXG4gIGNvbnN0IGpvdXJuYWxzID0gW11cblxuICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICBpZiAoIWVudHJ5LmVuZHNXaXRoKFwiLmpzb25cIikgfHwgIWdlbmVyYXRpb25QYXR0ZXJuLnRlc3QoZW50cnkuc2xpY2UoMCwgLTUpKSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgXCJQdWJsaWNhdGlvbiBqb3VybmFsIGNvbnRhaW5zIGFuIHVucmVjb2duaXplZCBlbnRyeS5cIilcbiAgICB9XG4gICAgY29uc3Qgam91cm5hbFBhdGggPSBwYXRoLmpvaW4oam91cm5hbERpcmVjdG9yeSwgZW50cnkpXG4gICAgbGV0IGpvdXJuYWxcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdGF0dXMgPSBhd2FpdCBsc3RhdChqb3VybmFsUGF0aClcblxuICAgICAgaWYgKCFzdGF0dXMuaXNGaWxlKCkgfHwgc3RhdHVzLmlzU3ltYm9saWNMaW5rKCkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIlB1YmxpY2F0aW9uIGpvdXJuYWwgZW50cnkgbXVzdCBiZSBhIHJlZ3VsYXIgZmlsZS5cIilcbiAgICAgIH1cbiAgICAgIGpvdXJuYWwgPSBKU09OLnBhcnNlKGF3YWl0IHJlYWRGaWxlKGpvdXJuYWxQYXRoLCBcInV0ZjhcIikpXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYykgdGhyb3cgZXJyb3JcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1JFQ09WRVJZX0ZBSUxFRFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiUHVibGljYXRpb24gam91cm5hbCBlbnRyeSBpcyBub3QgdmFsaWQgSlNPTi5cIiwgZXJyb3IpXG4gICAgfVxuICAgIGlmICghaXNQdWJsaWNhdGlvbkpvdXJuYWwoam91cm5hbCkgfHwgam91cm5hbC5wcm9qZWN0SWQgIT0gcHJvamVjdElkIHx8IGAke2pvdXJuYWwuZ2VuZXJhdGlvbklkfS5qc29uYCAhPSBlbnRyeSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgXCJQdWJsaWNhdGlvbiBqb3VybmFsIGVudHJ5IGRvZXMgbm90IG1hdGNoIHRoZSB2ZXJzaW9uZWQgcHJvamVjdCBjb250cmFjdC5cIilcbiAgICB9XG4gICAgam91cm5hbHMucHVzaCh7am91cm5hbCwgam91cm5hbFBhdGh9KVxuICB9XG5cbiAgcmV0dXJuIE9iamVjdC5mcmVlemUoam91cm5hbHMpXG59XG5cbi8qKlxuICogUmVwb3J0cyBjb21taXR0ZWQgY2xlYW51cCBzdGF0ZSB3aGlsZSBmYWlsaW5nIGNsb3NlZCBvbiBldmVyeSBqb3VybmFsIGVudHJ5LlxuICogQHBhcmFtIHtzdHJpbmd9IHB1YmxpY2F0aW9uUm9vdCAtIFB1YmxpY2F0aW9uIHJvb3QuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBpZGVudGl0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBnZW5lcmF0aW9uSWQgLSBQb2ludGVyLXNlbGVjdGVkIGdlbmVyYXRpb24gaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gV2hldGhlciBjb21taXR0ZWQgY2xlYW51cCByZW1haW5zLlxuICovXG5hc3luYyBmdW5jdGlvbiBoYXNDb21taXR0ZWRDbGVhbnVwSm91cm5hbChwdWJsaWNhdGlvblJvb3QsIHByb2plY3RJZCwgZ2VuZXJhdGlvbklkKSB7XG4gIGNvbnN0IGpvdXJuYWxzID0gYXdhaXQgcmVhZFB1YmxpY2F0aW9uSm91cm5hbHMocHVibGljYXRpb25Sb290LCBwcm9qZWN0SWQpXG5cbiAgcmV0dXJuIGpvdXJuYWxzLnNvbWUoKHtqb3VybmFsfSkgPT4gam91cm5hbC5nZW5lcmF0aW9uSWQgPT0gZ2VuZXJhdGlvbklkKVxufVxuXG4vKipcbiAqIFJlbW92ZXMgcHJvdmVuIGpvdXJuYWwtb3duZWQgc3RhdGUgYW5kIGR1cmFibHkgc3luY2hyb25pemVzIGVhY2ggcGFyZW50IGJlZm9yZSBkaXNjYXJkaW5nIGl0cyBvd25lcnNoaXAgcHJvb2YuXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIEV4YWN0IGNsZWFudXAgc3RhdGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gW29wdGlvbnMuY2FuZGlkYXRlUGF0aF0gLSBQcm92ZW4gY2FuZGlkYXRlIGdlbmVyYXRpb24gcGF0aC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0aW9ucy50ZW1wb3JhcnlQb2ludGVyUGF0aF0gLSBQcm92ZW4gdGVtcG9yYXJ5IHBvaW50ZXIgcGF0aC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0aW9ucy5qb3VybmFsUGF0aF0gLSBPd25lcnNoaXAgam91cm5hbCBwYXRoLlxuICogQHBhcmFtIHtzdHJpbmd9IG9wdGlvbnMucHJvamVjdElkIC0gUHJvamVjdCBpZGVudGl0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLnB1YmxpY2F0aW9uUm9vdCAtIFB1YmxpY2F0aW9uIHJvb3QuXG4gKiBAcGFyYW0ge0dlbmVyYXRlZEFydGlmYWN0UHVibGlzaGVyfSBvcHRpb25zLnB1Ymxpc2hlciAtIEFjdGl2ZSBwdWJsaXNoZXIuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlSm91cm5hbE93bmVkU3RhdGUoe2NhbmRpZGF0ZVBhdGgsIGpvdXJuYWxQYXRoLCBwcm9qZWN0SWQsIHB1YmxpY2F0aW9uUm9vdCwgcHVibGlzaGVyLCB0ZW1wb3JhcnlQb2ludGVyUGF0aH0pIHtcbiAgdHJ5IHtcbiAgICBpZiAoY2FuZGlkYXRlUGF0aCkge1xuICAgICAgYXdhaXQgcm0oY2FuZGlkYXRlUGF0aCwge2ZvcmNlOiB0cnVlLCByZWN1cnNpdmU6IHRydWV9KVxuICAgICAgb2JzZXJ2ZVB1YmxpY2F0aW9uRmlsZXN5c3RlbU9wZXJhdGlvbihwdWJsaXNoZXIsIFwicmVtb3ZlLWNhbmRpZGF0ZVwiKVxuICAgICAgYXdhaXQgc3luY0RpcmVjdG9yeShwYXRoLmpvaW4ocHVibGljYXRpb25Sb290LCBnZW5lcmF0aW9uc05hbWUpKVxuICAgICAgb2JzZXJ2ZVB1YmxpY2F0aW9uRmlsZXN5c3RlbU9wZXJhdGlvbihwdWJsaXNoZXIsIFwic3luYy1jYW5kaWRhdGUtcGFyZW50XCIpXG4gICAgfVxuICAgIGlmICh0ZW1wb3JhcnlQb2ludGVyUGF0aCkge1xuICAgICAgYXdhaXQgcm0odGVtcG9yYXJ5UG9pbnRlclBhdGgsIHtmb3JjZTogdHJ1ZX0pXG4gICAgICBvYnNlcnZlUHVibGljYXRpb25GaWxlc3lzdGVtT3BlcmF0aW9uKHB1Ymxpc2hlciwgXCJyZW1vdmUtdGVtcG9yYXJ5LXBvaW50ZXJcIilcbiAgICAgIGF3YWl0IHN5bmNEaXJlY3RvcnkocHVibGljYXRpb25Sb290KVxuICAgICAgb2JzZXJ2ZVB1YmxpY2F0aW9uRmlsZXN5c3RlbU9wZXJhdGlvbihwdWJsaXNoZXIsIFwic3luYy10ZW1wb3JhcnktcG9pbnRlci1wYXJlbnRcIilcbiAgICB9XG4gICAgaWYgKGpvdXJuYWxQYXRoKSB7XG4gICAgICByZWFjaFB1YmxpY2F0aW9uQm91bmRhcnkocHVibGlzaGVyLCBcImJlZm9yZS1qb3VybmFsLXJlbW92ZVwiKVxuICAgICAgYXdhaXQgcm0oam91cm5hbFBhdGgpXG4gICAgICBvYnNlcnZlUHVibGljYXRpb25GaWxlc3lzdGVtT3BlcmF0aW9uKHB1Ymxpc2hlciwgXCJyZW1vdmUtam91cm5hbFwiKVxuICAgICAgYXdhaXQgc3luY0RpcmVjdG9yeShwYXRoLmRpcm5hbWUoam91cm5hbFBhdGgpKVxuICAgICAgb2JzZXJ2ZVB1YmxpY2F0aW9uRmlsZXN5c3RlbU9wZXJhdGlvbihwdWJsaXNoZXIsIFwic3luYy1qb3VybmFsLXBhcmVudFwiKVxuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTZW1hbnRpZm9sZERpYWdub3N0aWMpIHRocm93IGVycm9yXG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fUkVDT1ZFUllfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgIFwiSm91cm5hbC1vd25lZCBwdWJsaWNhdGlvbiBzdGF0ZSBjb3VsZCBub3QgYmUgZHVyYWJseSByZWNvbmNpbGVkLlwiLCBlcnJvcilcbiAgfVxufVxuXG4vKipcbiAqIFJlYWRzIG9wdGlvbmFsIG1ldGFkYXRhIHdpdGhvdXQgZm9sbG93aW5nIHN5bWJvbGljIGxpbmtzLlxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIC0gRXhhY3Qgb3duZWQgcGF0aC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHBhcmFtIHtzdHJpbmd9IHN1YmplY3QgLSBTdGFibGUgc3ViamVjdCBkZXNjcmlwdGlvbi5cbiAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIm5vZGU6ZnNcIikuU3RhdHMgfCBudWxsPn0gU3RhdHVzIG9yIGFic2VuY2UuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIG9wdGlvbmFsU3RhdHVzKGZpbGVuYW1lLCBwcm9qZWN0SWQsIHN1YmplY3QpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgbHN0YXQoZmlsZW5hbWUpXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKGlzRXJyb3JDb2RlKGVycm9yLCBcIkVOT0VOVFwiKSkgcmV0dXJuIG51bGxcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9SRUNPVkVSWV9GQUlMRURcIiwgcHJvamVjdElkLFxuICAgICAgYEpvdXJuYWwtb3duZWQgJHtzdWJqZWN0fSBjb3VsZCBub3QgYmUgaW5zcGVjdGVkLmAsIGVycm9yKVxuICB9XG59XG5cbi8qKlxuICogVmFsaWRhdGVzIGFuZCBzbmFwc2hvdHMgb25lIHB1YmxpY2F0aW9uIHJlcXVlc3QgYmVmb3JlIHN0YWdpbmcuXG4gKiBAcGFyYW0ge3Vua25vd259IHJlcXVlc3QgLSBSZXF1ZXN0IGNhbmRpZGF0ZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGRpYWdub3N0aWMgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7VmFsaWRhdGVkUHVibGljYXRpb25SZXF1ZXN0fSBWYWxpZGF0ZWQgcmVxdWVzdC5cbiAqL1xuZnVuY3Rpb24gdmFsaWRhdGVQdWJsaWNhdGlvblJlcXVlc3QocmVxdWVzdCwgcHJvamVjdElkKSB7XG4gIGlmICghaXNQbGFpbk9iamVjdChyZXF1ZXN0KSkge1xuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUkVRVUVTVFwiLCBwcm9qZWN0SWQsXG4gICAgICBcIlB1YmxpY2F0aW9uIHJlcXVpcmVzIGEgdW5pcXVlIGdlbmVyYXRpb24gSUQgYW5kIGEgbm9uLWVtcHR5IG9yZGVyZWQgdGFyZ2V0IGFycmF5LlwiKVxuICB9XG4gIGNvbnN0IGdlbmVyYXRpb25JZCA9IHJlcXVlc3QuZ2VuZXJhdGlvbklkXG4gIGNvbnN0IHJlcXVlc3RlZFRhcmdldHMgPSByZXF1ZXN0LnRhcmdldHNcblxuICBpZiAodHlwZW9mIGdlbmVyYXRpb25JZCAhPSBcInN0cmluZ1wiIHx8ICFnZW5lcmF0aW9uUGF0dGVybi50ZXN0KGdlbmVyYXRpb25JZCkgfHxcbiAgICAhaXNEZW5zZUFycmF5KHJlcXVlc3RlZFRhcmdldHMpIHx8IHJlcXVlc3RlZFRhcmdldHMubGVuZ3RoID09IDApIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJQdWJsaWNhdGlvbiByZXF1aXJlcyBhIHVuaXF1ZSBnZW5lcmF0aW9uIElEIGFuZCBhIG5vbi1lbXB0eSBvcmRlcmVkIHRhcmdldCBhcnJheS5cIilcbiAgfVxuICAvKiogQHR5cGUge1ZhbGlkYXRlZFB1YmxpY2F0aW9uVGFyZ2V0W119ICovXG4gIGNvbnN0IHRhcmdldHMgPSBbXVxuICBjb25zdCB0YXJnZXRJZHMgPSBuZXcgU2V0KClcbiAgLyoqIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgY29uc3QgcHJvamVjdGlvbnMgPSBbXVxuICAvKiogQHR5cGUge3N0cmluZ1tdfSAqL1xuICBjb25zdCBjb21wbGV0ZUFydGlmYWN0UGF0aHMgPSBbXVxuXG4gIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCByZXF1ZXN0ZWRUYXJnZXRzLmxlbmd0aDsgaW5kZXggKz0gMSkge1xuICAgIGNvbnN0IHRhcmdldCA9IHJlcXVlc3RlZFRhcmdldHNbaW5kZXhdXG5cbiAgICBpZiAoIWlzUGxhaW5PYmplY3QodGFyZ2V0KSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9SRVFVRVNUXCIsIHByb2plY3RJZCxcbiAgICAgICAgYFRhcmdldCAke2luZGV4fSByZXF1aXJlcyBhIHN0YWJsZSBsb3dlcmNhc2UgaWRlbnRpdHkuYClcbiAgICB9XG4gICAgY29uc3QgdGFyZ2V0SWQgPSB0YXJnZXQuaWRcbiAgICBjb25zdCByb2xlID0gdGFyZ2V0LnJvbGVcbiAgICBjb25zdCBzb3VyY2VQcm9qZWN0aW9uID0gdGFyZ2V0LnNvdXJjZVByb2plY3Rpb25cbiAgICBjb25zdCBidWlsZFByb2plY3Rpb24gPSB0YXJnZXQuYnVpbGRQcm9qZWN0aW9uXG4gICAgY29uc3QgYXJ0aWZhY3RTZXRDYW5kaWRhdGUgPSB0YXJnZXQuYXJ0aWZhY3RTZXRcbiAgICBjb25zdCB2YWxpZGF0b3JzQ2FuZGlkYXRlID0gdGFyZ2V0LnZhbGlkYXRvcnNcblxuICAgIGlmICh0eXBlb2YgdGFyZ2V0SWQgIT0gXCJzdHJpbmdcIiB8fCAhaWRlbnRpdHlQYXR0ZXJuLnRlc3QodGFyZ2V0SWQpKSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICBgVGFyZ2V0ICR7aW5kZXh9IHJlcXVpcmVzIGEgc3RhYmxlIGxvd2VyY2FzZSBpZGVudGl0eS5gKVxuICAgIH1cbiAgICBpZiAodHlwZW9mIHJvbGUgIT0gXCJzdHJpbmdcIiB8fCAhdGFyZ2V0Um9sZXMuaGFzKHJvbGUpKSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICBgVGFyZ2V0ICcke3RhcmdldElkfScgcmVxdWlyZXMgYW4gZXhwbGljaXQgbGFuZ3VhZ2UtbmV1dHJhbCByb2xlLmApXG4gICAgfVxuXG4gICAgaWYgKHRhcmdldElkcy5oYXModGFyZ2V0SWQpKSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9QQVRIX0NPTExJU0lPTlwiLCBwcm9qZWN0SWQsIGBEdXBsaWNhdGUgdGFyZ2V0IGlkZW50aXR5ICcke3RhcmdldElkfScuYClcbiAgICB9XG4gICAgdGFyZ2V0SWRzLmFkZCh0YXJnZXRJZClcbiAgICBpZiAoIWlzU2FmZUFydGlmYWN0UGF0aChzb3VyY2VQcm9qZWN0aW9uKSB8fCAhaXNTYWZlQXJ0aWZhY3RQYXRoKGJ1aWxkUHJvamVjdGlvbikpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUFJPSkVDVElPTlwiLCBwcm9qZWN0SWQsXG4gICAgICAgIGBUYXJnZXQgJyR7dGFyZ2V0SWR9JyByZXF1aXJlcyBjYW5vbmljYWwgcmVsYXRpdmUgc291cmNlIGFuZCBidWlsZCBwcm9qZWN0aW9ucy5gKVxuICAgIH1cbiAgICBwcm9qZWN0aW9ucy5wdXNoKHNvdXJjZVByb2plY3Rpb24sIGJ1aWxkUHJvamVjdGlvbilcbiAgICBsZXQgYXJ0aWZhY3RTZXRcblxuICAgIHRyeSB7XG4gICAgICBhcnRpZmFjdFNldCA9IGNyZWF0ZUdlbmVyYXRlZEFydGlmYWN0U2V0KGFydGlmYWN0U2V0Q2FuZGlkYXRlKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICBgVGFyZ2V0ICcke3RhcmdldElkfScgZG9lcyBub3QgY29udGFpbiBhIHZhbGlkIGdlbmVyYXRlZCBhcnRpZmFjdCBzZXQuYCwgZXJyb3IpXG4gICAgfVxuICAgIGNvbnN0IHZhbGlkYXRvcnMgPSB2YWxpZGF0b3JzQ2FuZGlkYXRlID09PSB1bmRlZmluZWQgPyBbXSA6IHZhbGlkYXRvcnNDYW5kaWRhdGVcblxuICAgIGlmICghaXNEZW5zZUFycmF5KHZhbGlkYXRvcnMpKSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICBgVGFyZ2V0ICcke3RhcmdldElkfScgdmFsaWRhdG9ycyBtdXN0IGJlIGEgZGVuc2UgZnVuY3Rpb24gYXJyYXkuYClcbiAgICB9XG4gICAgLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLlB1YmxpY2F0aW9uVmFsaWRhdG9yW119ICovXG4gICAgY29uc3QgdmFsaWRhdGVkVmFsaWRhdG9ycyA9IFtdXG5cbiAgICBmb3IgKGxldCB2YWxpZGF0b3JJbmRleCA9IDA7IHZhbGlkYXRvckluZGV4IDwgdmFsaWRhdG9ycy5sZW5ndGg7IHZhbGlkYXRvckluZGV4ICs9IDEpIHtcbiAgICAgIGNvbnN0IHZhbGlkYXRvciA9IHZhbGlkYXRvcnNbdmFsaWRhdG9ySW5kZXhdXG5cbiAgICAgIGlmICh0eXBlb2YgdmFsaWRhdG9yICE9IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JFUVVFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICAgIGBUYXJnZXQgJyR7dGFyZ2V0SWR9JyB2YWxpZGF0b3JzIG11c3QgYmUgYSBkZW5zZSBmdW5jdGlvbiBhcnJheS5gKVxuICAgICAgfVxuICAgICAgdmFsaWRhdGVkVmFsaWRhdG9ycy5wdXNoKC8qKiBAdHlwZSB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5QdWJsaWNhdGlvblZhbGlkYXRvcn0gKi8gKHZhbGlkYXRvcikpXG4gICAgfVxuICAgIGNvbnN0IGFydGlmYWN0Q29uZmxpY3QgPSBmaW5kUG9ydGFibGVBcnRpZmFjdFBhdGhDb25mbGljdChhcnRpZmFjdFNldC5hcnRpZmFjdHMubWFwKCh7cGF0aDogYXJ0aWZhY3RQYXRofSkgPT4gYXJ0aWZhY3RQYXRoKSlcblxuICAgIGlmIChhcnRpZmFjdENvbmZsaWN0KSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9QQVRIX0NPTExJU0lPTlwiLCBwcm9qZWN0SWQsXG4gICAgICAgIGBUYXJnZXQgJyR7dGFyZ2V0SWR9JyBhcnRpZmFjdCBwYXRocyBoYXZlIGEgcG9ydGFibGUgJHthcnRpZmFjdENvbmZsaWN0LmtpbmR9IGNvbmZsaWN0LmApXG4gICAgfVxuICAgIGZvciAoY29uc3QgYXJ0aWZhY3Qgb2YgYXJ0aWZhY3RTZXQuYXJ0aWZhY3RzKSB7XG4gICAgICBjb21wbGV0ZUFydGlmYWN0UGF0aHMucHVzaChgJHtzb3VyY2VQcm9qZWN0aW9ufS8ke2FydGlmYWN0LnBhdGh9YClcbiAgICB9XG4gICAgdGFyZ2V0cy5wdXNoKE9iamVjdC5mcmVlemUoe1xuICAgICAgYXJ0aWZhY3RTZXQsXG4gICAgICBidWlsZFByb2plY3Rpb24sXG4gICAgICBpZDogdGFyZ2V0SWQsXG4gICAgICByb2xlOiAvKiogQHR5cGUge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuUHVibGljYXRpb25UYXJnZXRSb2xlfSAqLyAocm9sZSksXG4gICAgICBzb3VyY2VQcm9qZWN0aW9uLFxuICAgICAgdmFsaWRhdG9yczogT2JqZWN0LmZyZWV6ZSh2YWxpZGF0ZWRWYWxpZGF0b3JzKVxuICAgIH0pKVxuICB9XG4gIGNvbnN0IHByb2plY3Rpb25Db25mbGljdCA9IGZpbmRQb3J0YWJsZUFydGlmYWN0UGF0aENvbmZsaWN0KHByb2plY3Rpb25zKVxuXG4gIGlmIChwcm9qZWN0aW9uQ29uZmxpY3QpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9QUk9KRUNUSU9OX09WRVJMQVBcIiwgcHJvamVjdElkLFxuICAgICAgXCJUYXJnZXQgc291cmNlL2J1aWxkIHByb2plY3Rpb25zIG11c3QgYmUgZGlzdGluY3QgYW5kIG5vbi1uZXN0ZWQgdW5kZXIgcG9ydGFibGUgY29tcGFyaXNvbi5cIilcbiAgfVxuICBpZiAoZmluZFBvcnRhYmxlQXJ0aWZhY3RQYXRoQ29uZmxpY3QoWy4uLnByb2plY3Rpb25zLCBcIm1hbmlmZXN0Lmpzb25cIl0pKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9QUk9KRUNUSU9OXCIsIHByb2plY3RJZCxcbiAgICAgIFwiVGFyZ2V0IHNvdXJjZS9idWlsZCBwcm9qZWN0aW9ucyBtdXN0IG5vdCBvdmVybGFwIHRoZSBnZW5lcmF0aW9uIG1hbmlmZXN0IHBhdGguXCIpXG4gIH1cbiAgY29uc3QgYXJ0aWZhY3RDb25mbGljdCA9IGZpbmRQb3J0YWJsZUFydGlmYWN0UGF0aENvbmZsaWN0KGNvbXBsZXRlQXJ0aWZhY3RQYXRocylcblxuICBpZiAoYXJ0aWZhY3RDb25mbGljdCkge1xuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1BBVEhfQ09MTElTSU9OXCIsIHByb2plY3RJZCxcbiAgICAgIFwiUHJvamVjdGVkIGFydGlmYWN0IHBhdGhzIGNvbGxpZGUgdW5kZXIgcG9ydGFibGUgY29tcGFyaXNvbi5cIilcbiAgfVxuXG4gIHJldHVybiB7Z2VuZXJhdGlvbklkLCB0YXJnZXRzOiBPYmplY3QuZnJlZXplKHRhcmdldHMpfVxufVxuXG4vKipcbiAqIFZlcmlmaWVzIHRoYXQgb25lIHJldGFpbmVkIGltbXV0YWJsZSBnZW5lcmF0aW9uIGV4YWN0bHkgcmVwcmVzZW50cyBhIHZhbGlkYXRvci1mcmVlIHJlcXVlc3QuXG4gKiBUaGUgcmV0YWluZWQgZGlyZWN0b3J5IGlzIG5ldmVyIG1vZGlmaWVkIG9yIHJlbW92ZWQgb24gZWl0aGVyIHN1Y2Nlc3Mgb3IgZmFpbHVyZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBnZW5lcmF0aW9uUGF0aCAtIEV4aXN0aW5nIGdlbmVyYXRpb24gcGF0aC5cbiAqIEBwYXJhbSB7VmFsaWRhdGVkUHVibGljYXRpb25SZXF1ZXN0fSByZXF1ZXN0IC0gVmFsaWRhdGVkIGNhbmRpZGF0ZSByZXF1ZXN0LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgZGlhZ25vc3RpYyBpZGVudGl0eS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHttYW5pZmVzdDogaW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0aW9uTWFuaWZlc3QsIG1hbmlmZXN0Qnl0ZXM6IEJ1ZmZlcn0+fSBFeGFjdCByZXRhaW5lZCBtYW5pZmVzdC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5UmV0YWluZWRHZW5lcmF0aW9uKGdlbmVyYXRpb25QYXRoLCByZXF1ZXN0LCBwcm9qZWN0SWQpIHtcbiAgaWYgKHJlcXVlc3QudGFyZ2V0cy5zb21lKHRhcmdldCA9PiB0YXJnZXQudmFsaWRhdG9ycy5sZW5ndGggIT0gMCkpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9VTk9XTkVEX0NPTkZMSUNUXCIsIHByb2plY3RJZCxcbiAgICAgIFwiUmV0YWluZWQgZ2VuZXJhdGlvbnMgY2FuIGJlIHJlYWN0aXZhdGVkIG9ubHkgZm9yIGV4YWN0IHZhbGlkYXRvci1mcmVlIGNhbmRpZGF0ZXMuXCIpXG4gIH1cbiAgY29uc3QgdGFyZ2V0TWFuaWZlc3RzID0gcmVxdWVzdC50YXJnZXRzLm1hcCh0YXJnZXQgPT4gT2JqZWN0LmZyZWV6ZSh7XG4gICAgYXJ0aWZhY3RzOiBPYmplY3QuZnJlZXplKHRhcmdldC5hcnRpZmFjdFNldC5hcnRpZmFjdHMubWFwKGFydGlmYWN0ID0+IGRlc2NyaWJlR2VuZXJhdGVkQXJ0aWZhY3QoYXJ0aWZhY3QpLm1hbmlmZXN0KSksXG4gICAgYnVpbGRBcnRpZmFjdHM6IE9iamVjdC5mcmVlemUoW10pLFxuICAgIGlkOiB0YXJnZXQuaWQsXG4gICAgLi4uKHRhcmdldC5hcnRpZmFjdFNldC5tZXRhZGF0YSA9PT0gdW5kZWZpbmVkID8ge30gOiB7bWV0YWRhdGE6IHRhcmdldC5hcnRpZmFjdFNldC5tZXRhZGF0YX0pLFxuICAgIHByb2plY3Rpb25zOiBPYmplY3QuZnJlZXplKHtidWlsZDogdGFyZ2V0LmJ1aWxkUHJvamVjdGlvbiwgc291cmNlOiB0YXJnZXQuc291cmNlUHJvamVjdGlvbn0pLFxuICAgIHJvbGU6IHRhcmdldC5yb2xlLFxuICAgIHRhcmdldDogdGFyZ2V0LmFydGlmYWN0U2V0LnRhcmdldFxuICB9KSlcbiAgY29uc3QgY2FuZGlkYXRlID0ge1xuICAgIGdlbmVyYXRpb25JZDogcmVxdWVzdC5nZW5lcmF0aW9uSWQsXG4gICAgcHJvamVjdElkLFxuICAgIHNjaGVtYTogXCJTZW1hbnRpZm9sZEdlbmVyYXRpb25NYW5pZmVzdFwiLFxuICAgIHRhcmdldHM6IHRhcmdldE1hbmlmZXN0cyxcbiAgICB2ZXJzaW9uOiAxXG4gIH1cbiAgY29uc3QgZXhwZWN0ZWRCeXRlcyA9IHNlcmlhbGl6ZUpzb24oY2FuZGlkYXRlKVxuICBjb25zdCBtYW5pZmVzdCA9IHZhbGlkYXRlR2VuZXJhdGlvbk1hbmlmZXN0KEpTT04ucGFyc2UoZXhwZWN0ZWRCeXRlcy50b1N0cmluZyhcInV0ZjhcIikpLCBwcm9qZWN0SWQsIHJlcXVlc3QuZ2VuZXJhdGlvbklkKVxuICBsZXQgbWFuaWZlc3RCeXRlc1xuXG4gIHRyeSB7XG4gICAgbWFuaWZlc3RCeXRlcyA9IGF3YWl0IHJlYWRBbmRTeW5jUmVndWxhckZpbGUocGF0aC5qb2luKGdlbmVyYXRpb25QYXRoLCBcIm1hbmlmZXN0Lmpzb25cIiksIHByb2plY3RJZCxcbiAgICAgIFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgXCJSZXRhaW5lZCBnZW5lcmF0aW9uIG1hbmlmZXN0IG11c3QgYmUgYSByZWd1bGFyIGZpbGUuXCIpXG4gICAgaWYgKCFtYW5pZmVzdEJ5dGVzLmVxdWFscyhleHBlY3RlZEJ5dGVzKSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fVU5PV05FRF9DT05GTElDVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiUmV0YWluZWQgZ2VuZXJhdGlvbiBkb2VzIG5vdCBleGFjdGx5IG1hdGNoIHRoZSByZXF1ZXN0ZWQgY2FuZGlkYXRlLlwiKVxuICAgIH1cbiAgICBhd2FpdCB2ZXJpZnlNYW5pZmVzdEZpbGVzKGdlbmVyYXRpb25QYXRoLCBtYW5pZmVzdCwgcHJvamVjdElkKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYyAmJiBlcnJvci5jb2RlID09IFwiUFVCTElDQVRJT05fVU5PV05FRF9DT05GTElDVFwiKSB0aHJvdyBlcnJvclxuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1VOT1dORURfQ09ORkxJQ1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJSZXRhaW5lZCBnZW5lcmF0aW9uIGNvdWxkIG5vdCBiZSB2ZXJpZmllZCBhcyB0aGUgZXhhY3QgcmVxdWVzdGVkIGNhbmRpZGF0ZS5cIiwgZXJyb3IpXG4gIH1cblxuICByZXR1cm4ge21hbmlmZXN0LCBtYW5pZmVzdEJ5dGVzfVxufVxuXG4vKipcbiAqIFN0YWdlcyBvbmUgdmFsaWRhdGVkIHRhcmdldCBhbmQgaW52b2tlcyBpdHMgb3JkZXJlZCB2YWxpZGF0b3JzLlxuICogQHBhcmFtIHtzdHJpbmd9IGdlbmVyYXRpb25QYXRoIC0gQ2FuZGlkYXRlIGdlbmVyYXRpb24gcGF0aC5cbiAqIEBwYXJhbSB7VmFsaWRhdGVkUHVibGljYXRpb25UYXJnZXR9IHRhcmdldCAtIFZhbGlkYXRlZCB0YXJnZXQuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBkaWFnbm9zdGljIGlkZW50aXR5LlxuICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0aW9uVGFyZ2V0TWFuaWZlc3Q+fSBUYXJnZXQgbWFuaWZlc3QuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHN0YWdlVGFyZ2V0KGdlbmVyYXRpb25QYXRoLCB0YXJnZXQsIHByb2plY3RJZCkge1xuICBjb25zdCBzb3VyY2VQYXRoID0gb3duZWRQYXRoKGdlbmVyYXRpb25QYXRoLCB0YXJnZXQuc291cmNlUHJvamVjdGlvbiwgcHJvamVjdElkKVxuICBjb25zdCBidWlsZFBhdGggPSBvd25lZFBhdGgoZ2VuZXJhdGlvblBhdGgsIHRhcmdldC5idWlsZFByb2plY3Rpb24sIHByb2plY3RJZClcblxuICBhd2FpdCBta2Rpcihzb3VyY2VQYXRoLCB7bW9kZTogMG83MDAsIHJlY3Vyc2l2ZTogdHJ1ZX0pXG4gIGF3YWl0IG1rZGlyKGJ1aWxkUGF0aCwge21vZGU6IDBvNzAwLCByZWN1cnNpdmU6IHRydWV9KVxuICAvKiogQHR5cGUge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbkFydGlmYWN0TWFuaWZlc3RbXX0gKi9cbiAgY29uc3QgYXJ0aWZhY3RzID0gW11cblxuICBmb3IgKGNvbnN0IGFydGlmYWN0IG9mIHRhcmdldC5hcnRpZmFjdFNldC5hcnRpZmFjdHMpIHtcbiAgICBjb25zdCBmaWxlbmFtZSA9IG93bmVkUGF0aChzb3VyY2VQYXRoLCBhcnRpZmFjdC5wYXRoLCBwcm9qZWN0SWQpXG4gICAgY29uc3QgZGVzY3JpYmVkID0gZGVzY3JpYmVHZW5lcmF0ZWRBcnRpZmFjdChhcnRpZmFjdClcblxuICAgIGF3YWl0IG1rZGlyKHBhdGguZGlybmFtZShmaWxlbmFtZSksIHttb2RlOiAwbzcwMCwgcmVjdXJzaXZlOiB0cnVlfSlcbiAgICB0cnkge1xuICAgICAgYXdhaXQgd3JpdGVTeW5jZWRGaWxlKGZpbGVuYW1lLCBkZXNjcmliZWQuYnl0ZXMsIFwid3hcIilcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU2VtYW50aWZvbGREaWFnbm9zdGljKSB0aHJvdyBlcnJvclxuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fU1RBR0VfV1JJVEVfRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgYEdlbmVyYXRlZCBhcnRpZmFjdCAnJHthcnRpZmFjdC5wYXRofScgY291bGQgbm90IGJlIHN0YWdlZC5gLCBlcnJvcilcbiAgICB9XG4gICAgYXJ0aWZhY3RzLnB1c2goZGVzY3JpYmVkLm1hbmlmZXN0KVxuICB9XG5cbiAgLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLlB1YmxpY2F0aW9uQnVpbGRBcnRpZmFjdElucHV0W119ICovXG4gIGNvbnN0IGJ1aWxkRGVzY3JpcHRvcnMgPSBbXVxuICBjb25zdCBjb250ZXh0ID0gT2JqZWN0LmZyZWV6ZSh7YnVpbGRQYXRoLCBzb3VyY2VQYXRoLCB0YXJnZXRJZDogdGFyZ2V0LmlkfSlcblxuICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgdGFyZ2V0LnZhbGlkYXRvcnMubGVuZ3RoOyBpbmRleCArPSAxKSB7XG4gICAgbGV0IHByb2R1Y2VkXG5cbiAgICB0cnkge1xuICAgICAgcHJvZHVjZWQgPSBhd2FpdCB0YXJnZXQudmFsaWRhdG9yc1tpbmRleF0oY29udGV4dClcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fVkFMSURBVElPTl9GQUlMRURcIiwgcHJvamVjdElkLFxuICAgICAgICBgVmFsaWRhdG9yICR7aW5kZXh9IGZhaWxlZCBmb3IgdGFyZ2V0ICcke3RhcmdldC5pZH0nLmAsIGVycm9yKVxuICAgIH1cbiAgICBpZiAocHJvZHVjZWQgPT09IHVuZGVmaW5lZCkgY29udGludWVcbiAgICBpZiAoIWlzRGVuc2VBcnJheShwcm9kdWNlZCkpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1ZBTElEQVRJT05fRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgYFZhbGlkYXRvciAke2luZGV4fSByZXR1cm5lZCBpbnZhbGlkIGJ1aWxkLWFydGlmYWN0IG1ldGFkYXRhIGZvciB0YXJnZXQgJyR7dGFyZ2V0LmlkfScuYClcbiAgICB9XG4gICAgZm9yIChjb25zdCBkZXNjcmlwdG9yIG9mIHByb2R1Y2VkKSB7XG4gICAgICBidWlsZERlc2NyaXB0b3JzLnB1c2godmFsaWRhdGVCdWlsZEFydGlmYWN0RGVzY3JpcHRvcihkZXNjcmlwdG9yLCB0YXJnZXQuaWQsIHByb2plY3RJZCkpXG4gICAgfVxuICB9XG5cbiAgY29uc3QgZGVzY3JpcHRvckNvbmZsaWN0ID0gZmluZFBvcnRhYmxlQXJ0aWZhY3RQYXRoQ29uZmxpY3QoYnVpbGREZXNjcmlwdG9ycy5tYXAoKHtwYXRoOiBhcnRpZmFjdFBhdGh9KSA9PiBhcnRpZmFjdFBhdGgpKVxuXG4gIGlmIChkZXNjcmlwdG9yQ29uZmxpY3QpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9QQVRIX0NPTExJU0lPTlwiLCBwcm9qZWN0SWQsXG4gICAgICBgVGFyZ2V0ICcke3RhcmdldC5pZH0nIGJ1aWxkIGFydGlmYWN0cyBoYXZlIGEgcG9ydGFibGUgJHtkZXNjcmlwdG9yQ29uZmxpY3Qua2luZH0gY29uZmxpY3QuYClcbiAgfVxuICBjb25zdCBzdGFnZWRCdWlsZEZpbGVzID0gYXdhaXQgbGlzdFJlZ3VsYXJGaWxlcyhidWlsZFBhdGgsIHByb2plY3RJZClcbiAgY29uc3QgZGVjbGFyZWQgPSBuZXcgU2V0KGJ1aWxkRGVzY3JpcHRvcnMubWFwKCh7cGF0aDogYXJ0aWZhY3RQYXRofSkgPT4gYXJ0aWZhY3RQYXRoKSlcblxuICBpZiAoc3RhZ2VkQnVpbGRGaWxlcy5sZW5ndGggIT0gZGVjbGFyZWQuc2l6ZSB8fCBzdGFnZWRCdWlsZEZpbGVzLnNvbWUoZmlsZW5hbWUgPT4gIWRlY2xhcmVkLmhhcyhmaWxlbmFtZSkpKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fVkFMSURBVElPTl9GQUlMRURcIiwgcHJvamVjdElkLFxuICAgICAgYFRhcmdldCAnJHt0YXJnZXQuaWR9JyB2YWxpZGF0b3JzIG11c3QgZGVjbGFyZSBldmVyeSBzdGFnZWQgYnVpbGQgYXJ0aWZhY3QgZXhhY3RseSBvbmNlLmApXG4gIH1cbiAgLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRpb25CdWlsZEFydGlmYWN0TWFuaWZlc3RbXX0gKi9cbiAgY29uc3QgZmluYWxpemVkQnVpbGRBcnRpZmFjdHMgPSBbXVxuXG4gIGZvciAoY29uc3QgZGVzY3JpcHRvciBvZiBidWlsZERlc2NyaXB0b3JzKSB7XG4gICAgY29uc3QgYnl0ZXMgPSBhd2FpdCByZWFkQW5kU3luY1JlZ3VsYXJGaWxlKG93bmVkUGF0aChidWlsZFBhdGgsIGRlc2NyaXB0b3IucGF0aCwgcHJvamVjdElkKSwgcHJvamVjdElkLFxuICAgICAgXCJQVUJMSUNBVElPTl9TVEFHRV9IQVNIX01JU01BVENIXCIsIGBCdWlsZCBhcnRpZmFjdCAnJHtkZXNjcmlwdG9yLnBhdGh9JyBpcyBub3QgYSBzdGFibGUgcmVndWxhciBmaWxlLmApXG5cbiAgICBmaW5hbGl6ZWRCdWlsZEFydGlmYWN0cy5wdXNoKE9iamVjdC5mcmVlemUoe1xuICAgICAgYnl0ZUxlbmd0aDogYnl0ZXMuYnl0ZUxlbmd0aCxcbiAgICAgIGhhc2g6IE9iamVjdC5mcmVlemUoe2FsZ29yaXRobTogLyoqIEB0eXBlIHtjb25zdH0gKi8gKFwic2hhMjU2XCIpLCB2YWx1ZTogc2hhMjU2KGJ5dGVzKX0pLFxuICAgICAgbWVkaWFUeXBlOiBkZXNjcmlwdG9yLm1lZGlhVHlwZSxcbiAgICAgIHBhdGg6IGRlc2NyaXB0b3IucGF0aCxcbiAgICAgIHJvbGU6IGRlc2NyaXB0b3Iucm9sZVxuICAgIH0pKVxuICB9XG5cbiAgcmV0dXJuIE9iamVjdC5mcmVlemUoe1xuICAgIGFydGlmYWN0czogT2JqZWN0LmZyZWV6ZShhcnRpZmFjdHMpLFxuICAgIGJ1aWxkQXJ0aWZhY3RzOiBPYmplY3QuZnJlZXplKGZpbmFsaXplZEJ1aWxkQXJ0aWZhY3RzKSxcbiAgICBpZDogdGFyZ2V0LmlkLFxuICAgIC4uLih0YXJnZXQuYXJ0aWZhY3RTZXQubWV0YWRhdGEgPT09IHVuZGVmaW5lZCA/IHt9IDoge21ldGFkYXRhOiB0YXJnZXQuYXJ0aWZhY3RTZXQubWV0YWRhdGF9KSxcbiAgICBwcm9qZWN0aW9uczogT2JqZWN0LmZyZWV6ZSh7YnVpbGQ6IHRhcmdldC5idWlsZFByb2plY3Rpb24sIHNvdXJjZTogdGFyZ2V0LnNvdXJjZVByb2plY3Rpb259KSxcbiAgICByb2xlOiB0YXJnZXQucm9sZSxcbiAgICB0YXJnZXQ6IHRhcmdldC5hcnRpZmFjdFNldC50YXJnZXRcbiAgfSlcbn1cblxuLyoqXG4gKiBEZXJpdmVzIGV4YWN0IHN0YWdlZCBieXRlcyBhbmQgdGhlaXIgZGV0ZXJtaW5pc3RpYyBnZW5lcmF0aW9uLW1hbmlmZXN0IHJlY29yZC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0ZWRTZXRBcnRpZmFjdH0gYXJ0aWZhY3QgLSBWYWxpZGF0ZWQgZ2VuZXJhdGVkIGFydGlmYWN0LlxuICogQHJldHVybnMge3tieXRlczogQnVmZmVyLCBtYW5pZmVzdDogaW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0aW9uQXJ0aWZhY3RNYW5pZmVzdH19IEJ5dGVzIGFuZCBtYW5pZmVzdCByZWNvcmQuXG4gKi9cbmZ1bmN0aW9uIGRlc2NyaWJlR2VuZXJhdGVkQXJ0aWZhY3QoYXJ0aWZhY3QpIHtcbiAgY29uc3QgYnl0ZXMgPSBhcnRpZmFjdC5jb250ZW50S2luZCA9PSBcInRleHRcIlxuICAgID8gQnVmZmVyLmZyb20oLyoqIEB0eXBlIHtzdHJpbmd9ICovIChhcnRpZmFjdC5jb250ZW50KSwgXCJ1dGY4XCIpXG4gICAgOiBCdWZmZXIuZnJvbSgvKiogQHR5cGUge1VpbnQ4QXJyYXl9ICovIChhcnRpZmFjdC5jb250ZW50KSlcblxuICByZXR1cm4ge1xuICAgIGJ5dGVzLFxuICAgIG1hbmlmZXN0OiBPYmplY3QuZnJlZXplKHtcbiAgICAgIGJ5dGVMZW5ndGg6IGJ5dGVzLmJ5dGVMZW5ndGgsXG4gICAgICBjb250ZW50S2luZDogYXJ0aWZhY3QuY29udGVudEtpbmQsXG4gICAgICBoYXNoOiBPYmplY3QuZnJlZXplKHthbGdvcml0aG06IC8qKiBAdHlwZSB7Y29uc3R9ICovIChcInNoYTI1NlwiKSwgdmFsdWU6IHNoYTI1NihieXRlcyl9KSxcbiAgICAgIG1lZGlhVHlwZTogYXJ0aWZhY3QubWVkaWFUeXBlLFxuICAgICAgcGF0aDogYXJ0aWZhY3QucGF0aCxcbiAgICAgIHByb3ZlbmFuY2U6IGFydGlmYWN0LnByb3ZlbmFuY2UsXG4gICAgICByb2xlOiBhcnRpZmFjdC5yb2xlXG4gICAgfSlcbiAgfVxufVxuXG4vKipcbiAqIFZhbGlkYXRlcyB2YWxpZGF0b3IvY29tcGlsZXIgb3V0cHV0IG1ldGFkYXRhLlxuICogQHBhcmFtIHt1bmtub3dufSBkZXNjcmlwdG9yIC0gTWV0YWRhdGEgY2FuZGlkYXRlLlxuICogQHBhcmFtIHtzdHJpbmd9IHRhcmdldElkIC0gVGFyZ2V0IGlkZW50aXR5LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgZGlhZ25vc3RpYyBpZGVudGl0eS5cbiAqIEByZXR1cm5zIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLlB1YmxpY2F0aW9uQnVpbGRBcnRpZmFjdElucHV0fSBWYWxpZGF0ZWQgbWV0YWRhdGEuXG4gKi9cbmZ1bmN0aW9uIHZhbGlkYXRlQnVpbGRBcnRpZmFjdERlc2NyaXB0b3IoZGVzY3JpcHRvciwgdGFyZ2V0SWQsIHByb2plY3RJZCkge1xuICBpZiAoIWlzUGxhaW5PYmplY3QoZGVzY3JpcHRvcikgfHwgIWhhc0V4YWN0S2V5cyhkZXNjcmlwdG9yLCBbXCJtZWRpYVR5cGVcIiwgXCJwYXRoXCIsIFwicm9sZVwiXSkpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9WQUxJREFUSU9OX0ZBSUxFRFwiLCBwcm9qZWN0SWQsXG4gICAgICBgVGFyZ2V0ICcke3RhcmdldElkfScgdmFsaWRhdG9yIHJldHVybmVkIGludmFsaWQgYnVpbGQtYXJ0aWZhY3QgbWV0YWRhdGEuYClcbiAgfVxuICBjb25zdCBkZXNjcmlwdG9yUGF0aCA9IGRlc2NyaXB0b3IucGF0aFxuICBjb25zdCBtZWRpYVR5cGUgPSBkZXNjcmlwdG9yLm1lZGlhVHlwZVxuICBjb25zdCByb2xlID0gZGVzY3JpcHRvci5yb2xlXG5cbiAgaWYgKCFpc1NhZmVBcnRpZmFjdFBhdGgoZGVzY3JpcHRvclBhdGgpIHx8IHR5cGVvZiBtZWRpYVR5cGUgIT0gXCJzdHJpbmdcIiB8fFxuICAgICFtZWRpYVR5cGVQYXR0ZXJuLnRlc3QobWVkaWFUeXBlKSB8fCAvW1xcclxcbl0vdS50ZXN0KG1lZGlhVHlwZSkgfHxcbiAgICB0eXBlb2Ygcm9sZSAhPSBcInN0cmluZ1wiIHx8ICFidWlsZFJvbGVQYXR0ZXJuLnRlc3Qocm9sZSkpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9WQUxJREFUSU9OX0ZBSUxFRFwiLCBwcm9qZWN0SWQsXG4gICAgICBgVGFyZ2V0ICcke3RhcmdldElkfScgdmFsaWRhdG9yIHJldHVybmVkIGludmFsaWQgYnVpbGQtYXJ0aWZhY3QgbWV0YWRhdGEuYClcbiAgfVxuXG4gIHJldHVybiBPYmplY3QuZnJlZXplKHttZWRpYVR5cGUsIHBhdGg6IGRlc2NyaXB0b3JQYXRoLCByb2xlfSlcbn1cblxuLyoqXG4gKiBWZXJpZmllcyBldmVyeSBtYW5pZmVzdCByZWZlcmVuY2UgYW5kIHN0YWdlZCBieXRlIGJlZm9yZSBwb2ludGVyIHJlYWNoYWJpbGl0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBnZW5lcmF0aW9uUGF0aCAtIENhbmRpZGF0ZSBnZW5lcmF0aW9uIHBhdGguXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbk1hbmlmZXN0fSBtYW5pZmVzdCAtIENhbmRpZGF0ZSBtYW5pZmVzdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGRpYWdub3N0aWMgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5TWFuaWZlc3RGaWxlcyhnZW5lcmF0aW9uUGF0aCwgbWFuaWZlc3QsIHByb2plY3RJZCkge1xuICBmb3IgKGNvbnN0IHRhcmdldCBvZiBtYW5pZmVzdC50YXJnZXRzKSB7XG4gICAgY29uc3Qgc291cmNlUGF0aCA9IG93bmVkUGF0aChnZW5lcmF0aW9uUGF0aCwgdGFyZ2V0LnByb2plY3Rpb25zLnNvdXJjZSwgcHJvamVjdElkKVxuICAgIGNvbnN0IGJ1aWxkUGF0aCA9IG93bmVkUGF0aChnZW5lcmF0aW9uUGF0aCwgdGFyZ2V0LnByb2plY3Rpb25zLmJ1aWxkLCBwcm9qZWN0SWQpXG4gICAgY29uc3Qgc291cmNlRmlsZXMgPSBhd2FpdCBsaXN0UmVndWxhckZpbGVzKHNvdXJjZVBhdGgsIHByb2plY3RJZClcblxuICAgIGlmIChzb3VyY2VGaWxlcy5sZW5ndGggIT0gdGFyZ2V0LmFydGlmYWN0cy5sZW5ndGggfHxcbiAgICAgIHNvdXJjZUZpbGVzLnNvbWUoZmlsZW5hbWUgPT4gIXRhcmdldC5hcnRpZmFjdHMuc29tZShhcnRpZmFjdCA9PiBhcnRpZmFjdC5wYXRoID09IGZpbGVuYW1lKSkpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1NUQUdFX0hBU0hfTUlTTUFUQ0hcIiwgcHJvamVjdElkLFxuICAgICAgICBgVGFyZ2V0ICcke3RhcmdldC5pZH0nIGdlbmVyYXRlZC1zb3VyY2UgZmlsZXMgZG8gbm90IG1hdGNoIGl0cyBtYW5pZmVzdC5gKVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IGFydGlmYWN0IG9mIHRhcmdldC5hcnRpZmFjdHMpIHtcbiAgICAgIGF3YWl0IHZlcmlmeU1hbmlmZXN0RmlsZShzb3VyY2VQYXRoLCBhcnRpZmFjdCwgcHJvamVjdElkKVxuICAgIH1cbiAgICBjb25zdCBidWlsZEZpbGVzID0gYXdhaXQgbGlzdFJlZ3VsYXJGaWxlcyhidWlsZFBhdGgsIHByb2plY3RJZClcblxuICAgIGlmIChidWlsZEZpbGVzLmxlbmd0aCAhPSB0YXJnZXQuYnVpbGRBcnRpZmFjdHMubGVuZ3RoIHx8XG4gICAgICBidWlsZEZpbGVzLnNvbWUoZmlsZW5hbWUgPT4gIXRhcmdldC5idWlsZEFydGlmYWN0cy5zb21lKGFydGlmYWN0ID0+IGFydGlmYWN0LnBhdGggPT0gZmlsZW5hbWUpKSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fU1RBR0VfSEFTSF9NSVNNQVRDSFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIGBUYXJnZXQgJyR7dGFyZ2V0LmlkfScgYnVpbGQgZmlsZXMgZG8gbm90IG1hdGNoIGl0cyBtYW5pZmVzdC5gKVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IGFydGlmYWN0IG9mIHRhcmdldC5idWlsZEFydGlmYWN0cykge1xuICAgICAgYXdhaXQgdmVyaWZ5TWFuaWZlc3RGaWxlKGJ1aWxkUGF0aCwgYXJ0aWZhY3QsIHByb2plY3RJZClcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGFydGlmYWN0cyA9IFtdXG5cbiAgICAgIGZvciAoY29uc3QgYXJ0aWZhY3Qgb2YgdGFyZ2V0LmFydGlmYWN0cykge1xuICAgICAgICBjb25zdCBieXRlcyA9IGF3YWl0IHJlYWRBbmRTeW5jUmVndWxhckZpbGUob3duZWRQYXRoKHNvdXJjZVBhdGgsIGFydGlmYWN0LnBhdGgsIHByb2plY3RJZCksIHByb2plY3RJZCxcbiAgICAgICAgICBcIlBVQkxJQ0FUSU9OX1NUQUdFX0hBU0hfTUlTTUFUQ0hcIiwgYE1hbmlmZXN0IGFydGlmYWN0ICcke2FydGlmYWN0LnBhdGh9JyBpcyBub3QgYSBzdGFibGUgcmVndWxhciBmaWxlLmApXG4gICAgICAgIGxldCBjb250ZW50XG5cbiAgICAgICAgaWYgKGFydGlmYWN0LmNvbnRlbnRLaW5kID09IFwidGV4dFwiKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnRlbnQgPSBuZXcgVGV4dERlY29kZXIoXCJ1dGYtOFwiLCB7ZmF0YWw6IHRydWV9KS5kZWNvZGUoYnl0ZXMpXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIk1BTEZPUk1FRF9HRU5FUkFUSU9OX01BTklGRVNUXCIsIHByb2plY3RJZCxcbiAgICAgICAgICAgICAgYFRleHQgYXJ0aWZhY3QgJyR7YXJ0aWZhY3QucGF0aH0nIGlzIG5vdCB2YWxpZCBVVEYtOC5gLCBlcnJvcilcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBjb250ZW50ID0gbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpXG4gICAgICAgIGFydGlmYWN0cy5wdXNoKHtcbiAgICAgICAgICBjb250ZW50LFxuICAgICAgICAgIGNvbnRlbnRLaW5kOiBhcnRpZmFjdC5jb250ZW50S2luZCxcbiAgICAgICAgICBtZWRpYVR5cGU6IGFydGlmYWN0Lm1lZGlhVHlwZSxcbiAgICAgICAgICBvd25lcnNoaXA6IC8qKiBAdHlwZSB7Y29uc3R9ICovIChcImdlbmVyYXRlZFwiKSxcbiAgICAgICAgICBwYXRoOiBhcnRpZmFjdC5wYXRoLFxuICAgICAgICAgIHByb3ZlbmFuY2U6IHJlaHlkcmF0ZVByb3ZlbmFuY2UoYXJ0aWZhY3QucHJvdmVuYW5jZSwgcHJvamVjdElkKSxcbiAgICAgICAgICByb2xlOiBhcnRpZmFjdC5yb2xlXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICBjcmVhdGVHZW5lcmF0ZWRBcnRpZmFjdFNldCh7XG4gICAgICAgIGFydGlmYWN0cyxcbiAgICAgICAgLi4uKHRhcmdldC5tZXRhZGF0YSA9PT0gdW5kZWZpbmVkID8ge30gOiB7bWV0YWRhdGE6IHRhcmdldC5tZXRhZGF0YX0pLFxuICAgICAgICB0YXJnZXQ6IHRhcmdldC50YXJnZXRcbiAgICAgIH0pXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYyAmJiBlcnJvci5jb2RlID09IFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIikgdGhyb3cgZXJyb3JcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIk1BTEZPUk1FRF9HRU5FUkFUSU9OX01BTklGRVNUXCIsIHByb2plY3RJZCxcbiAgICAgICAgYFRhcmdldCAnJHt0YXJnZXQuaWR9JyBhcnRpZmFjdCBwcm92ZW5hbmNlIGRvZXMgbm90IG1hdGNoIGl0cyBzdGFnZWQgYnl0ZXMuYCwgZXJyb3IpXG4gICAgfVxuICB9XG4gIGF3YWl0IHZlcmlmeUdlbmVyYXRpb25JbnZlbnRvcnkoZ2VuZXJhdGlvblBhdGgsIG1hbmlmZXN0LCBwcm9qZWN0SWQpXG59XG5cbi8qKlxuICogUmVzdG9yZXMgSlNPTi1vbWl0dGVkIG9wdGlvbmFsIG1hcHBpbmcgZmllbGRzIGFuZCB2ZXJpZmllcyBwZXJzaXN0ZWQgbWFwcGluZyBwcm9qZWN0aW9ucy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5BcnRpZmFjdFByb3ZlbmFuY2V9IHByb3ZlbmFuY2UgLSBQZXJzaXN0ZWQgcHJvdmVuYW5jZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHJldHVybnMge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuQXJ0aWZhY3RQcm92ZW5hbmNlfSBDYW5vbmljYWwgcHJvdmVuYW5jZS5cbiAqL1xuZnVuY3Rpb24gcmVoeWRyYXRlUHJvdmVuYW5jZShwcm92ZW5hbmNlLCBwcm9qZWN0SWQpIHtcbiAgaWYgKHByb3ZlbmFuY2Uua2luZCA9PSBcInRleHRcIikge1xuICAgIGNvbnN0IG1hcHBpbmcgPSBmaW5hbGl6ZU1hcHBpbmcocHJvdmVuYW5jZS5tYXBwaW5nKVxuICAgIGNvbnN0IHNvdXJjZU1hcCA9IHRvU291cmNlTWFwVjMobWFwcGluZylcbiAgICBjb25zdCBqc29uU291cmNlTWFwID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShzb3VyY2VNYXApKVxuXG4gICAgaWYgKCFzYW1lSnNvblZhbHVlKGpzb25Tb3VyY2VNYXAsIHByb3ZlbmFuY2Uuc291cmNlTWFwKSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICBcIlBlcnNpc3RlZCBTb3VyY2UgTWFwIHByb2plY3Rpb24gY29udHJhZGljdHMgaXRzIHJpY2ggbWFwcGluZy5cIilcbiAgICB9XG5cbiAgICByZXR1cm4gT2JqZWN0LmZyZWV6ZSh7XG4gICAgICBraW5kOiAvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJ0ZXh0XCIpLFxuICAgICAgbWFwcGluZyxcbiAgICAgIHNvdXJjZU1hcCxcbiAgICAgIC4uLihwcm92ZW5hbmNlLnNvdXJjZU1hcEZpbGVuYW1lID09PSB1bmRlZmluZWQgPyB7fSA6IHtzb3VyY2VNYXBGaWxlbmFtZTogcHJvdmVuYW5jZS5zb3VyY2VNYXBGaWxlbmFtZX0pXG4gICAgfSlcbiAgfVxuICBpZiAocHJvdmVuYW5jZS5raW5kID09IFwiYnl0ZXNcIikge1xuICAgIHJldHVybiBPYmplY3QuZnJlZXplKHtraW5kOiAvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJieXRlc1wiKSwgbWFwcGluZzogZmluYWxpemVCeXRlTWFwcGluZyhwcm92ZW5hbmNlLm1hcHBpbmcpfSlcbiAgfVxuXG4gIHJldHVybiBwcm92ZW5hbmNlXG59XG5cbi8qKlxuICogVmVyaWZpZXMgb25lIHJlZmVyZW5jZWQgZmlsZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0aW9uUGF0aCAtIE93bmluZyBwcm9qZWN0aW9uIHBhdGguXG4gKiBAcGFyYW0ge3tieXRlTGVuZ3RoOiBudW1iZXIsIGhhc2g6IGltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbkNvbnRlbnRIYXNoLCBwYXRoOiBzdHJpbmd9fSBhcnRpZmFjdCAtIE1hbmlmZXN0IHJlY29yZC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGRpYWdub3N0aWMgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBFeGFjdCB2ZXJpZmllZCBieXRlcy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5TWFuaWZlc3RGaWxlKHByb2plY3Rpb25QYXRoLCBhcnRpZmFjdCwgcHJvamVjdElkKSB7XG4gIGNvbnN0IGJ5dGVzID0gYXdhaXQgcmVhZEFuZFN5bmNSZWd1bGFyRmlsZShvd25lZFBhdGgocHJvamVjdGlvblBhdGgsIGFydGlmYWN0LnBhdGgsIHByb2plY3RJZCksIHByb2plY3RJZCxcbiAgICBcIlBVQkxJQ0FUSU9OX1NUQUdFX0hBU0hfTUlTTUFUQ0hcIiwgYE1hbmlmZXN0IGFydGlmYWN0ICcke2FydGlmYWN0LnBhdGh9JyBpcyBub3QgYSBzdGFibGUgcmVndWxhciBmaWxlLmApXG5cbiAgaWYgKGJ5dGVzLmJ5dGVMZW5ndGggIT0gYXJ0aWZhY3QuYnl0ZUxlbmd0aCB8fCBzaGEyNTYoYnl0ZXMpICE9IGFydGlmYWN0Lmhhc2gudmFsdWUpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9TVEFHRV9IQVNIX01JU01BVENIXCIsIHByb2plY3RJZCxcbiAgICAgIGBNYW5pZmVzdCBhcnRpZmFjdCAnJHthcnRpZmFjdC5wYXRofScgYnl0ZXMgZG8gbm90IG1hdGNoIGl0cyByZWNvcmRlZCBoYXNoLmApXG4gIH1cblxuICByZXR1cm4gYnl0ZXNcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgYW4gZXhpc3RpbmcgYWN0aXZlIHBvaW50ZXIgYmVmb3JlIGEgbmV3IGNhbmRpZGF0ZSBpcyBzdGFnZWQuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHVibGljYXRpb25Sb290IC0gUHVibGljYXRpb24gcm9vdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IENvbXBsZXRpb24uXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHZhbGlkYXRlRXhpc3RpbmdQb2ludGVyKHB1YmxpY2F0aW9uUm9vdCwgcHJvamVjdElkKSB7XG4gIGNvbnN0IHBvaW50ZXJQYXRoID0gcGF0aC5qb2luKHB1YmxpY2F0aW9uUm9vdCwgYWN0aXZlUG9pbnRlck5hbWUpXG4gIGNvbnN0IHBvaW50ZXIgPSBhd2FpdCByZWFkQWN0aXZlUG9pbnRlcihwb2ludGVyUGF0aCwgcHJvamVjdElkLCB0cnVlKVxuXG4gIGlmIChwb2ludGVyKSBhd2FpdCB2ZXJpZnlQdWJsaXNoZWRHZW5lcmF0aW9uKHB1YmxpY2F0aW9uUm9vdCwgcHJvamVjdElkLCBwb2ludGVyLCBmYWxzZSlcbn1cblxuLyoqXG4gKiBSZWFkcyBhbmQgc3RyaWN0bHkgdmFsaWRhdGVzIG9uZSBhY3RpdmUgcG9pbnRlci5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwb2ludGVyUGF0aCAtIFBvaW50ZXIgZmlsZW5hbWUuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gRXhwZWN0ZWQgcHJvamVjdCBpZGVudGl0eS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gb3B0aW9uYWwgLSBXaGV0aGVyIGFic2VuY2UgaXMgYWxsb3dlZC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPEFjdGl2ZVBvaW50ZXIgfCBudWxsPn0gUG9pbnRlciBvciBhYnNlbnQgdmFsdWUuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlYWRBY3RpdmVQb2ludGVyKHBvaW50ZXJQYXRoLCBwcm9qZWN0SWQsIG9wdGlvbmFsKSB7XG4gIGxldCBwb2ludGVyQnl0ZXNcblxuICB0cnkge1xuICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IGxzdGF0KHBvaW50ZXJQYXRoKVxuXG4gICAgaWYgKCFzdGF0dXMuaXNGaWxlKCkgfHwgc3RhdHVzLmlzU3ltYm9saWNMaW5rKCkpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIk1BTEZPUk1FRF9BQ1RJVkVfR0VORVJBVElPTlwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiQWN0aXZlLWdlbmVyYXRpb24gcG9pbnRlciBtdXN0IGJlIGEgcmVndWxhciBmaWxlLlwiKVxuICAgIH1cbiAgICBwb2ludGVyQnl0ZXMgPSBhd2FpdCByZWFkRmlsZShwb2ludGVyUGF0aClcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBpZiAob3B0aW9uYWwgJiYgaXNFcnJvckNvZGUoZXJyb3IsIFwiRU5PRU5UXCIpKSByZXR1cm4gbnVsbFxuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYykgdGhyb3cgZXJyb3JcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUob3B0aW9uYWwgPyBcIk1BTEZPUk1FRF9BQ1RJVkVfR0VORVJBVElPTlwiIDogXCJBQ1RJVkVfR0VORVJBVElPTl9NSVNTSU5HXCIsIHByb2plY3RJZCxcbiAgICAgIG9wdGlvbmFsID8gXCJBY3RpdmUtZ2VuZXJhdGlvbiBwb2ludGVyIGNvdWxkIG5vdCBiZSByZWFkLlwiIDogXCJObyBhY3RpdmUgZ2VuZXJhdGlvbiBoYXMgYmVlbiBwdWJsaXNoZWQuXCIsIGVycm9yKVxuICB9XG4gIGxldCB2YWx1ZVxuXG4gIHRyeSB7XG4gICAgdmFsdWUgPSBKU09OLnBhcnNlKHBvaW50ZXJCeXRlcy50b1N0cmluZyhcInV0ZjhcIikpXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0FDVElWRV9HRU5FUkFUSU9OXCIsIHByb2plY3RJZCxcbiAgICAgIFwiQWN0aXZlLWdlbmVyYXRpb24gcG9pbnRlciBpcyBub3QgdmFsaWQgSlNPTi5cIiwgZXJyb3IpXG4gIH1cbiAgaWYgKCFpc0FjdGl2ZVBvaW50ZXIodmFsdWUpIHx8IHZhbHVlLnByb2plY3RJZCAhPSBwcm9qZWN0SWQpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJNQUxGT1JNRURfQUNUSVZFX0dFTkVSQVRJT05cIiwgcHJvamVjdElkLFxuICAgICAgXCJBY3RpdmUtZ2VuZXJhdGlvbiBwb2ludGVyIGRvZXMgbm90IG1hdGNoIHRoZSB2ZXJzaW9uZWQgcHJvamVjdCBjb250cmFjdC5cIilcbiAgfVxuXG4gIHJldHVybiB2YWx1ZVxufVxuXG4vKipcbiAqIFZlcmlmaWVzIGEgcG9pbnRlci1zZWxlY3RlZCBnZW5lcmF0aW9uIHdpdGhvdXQgY29uc3VsdGluZyB0aGUgcG9pbnRlciBhZ2Fpbi5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwdWJsaWNhdGlvblJvb3QgLSBQdWJsaWNhdGlvbiByb290LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcGFyYW0ge0FjdGl2ZVBvaW50ZXJ9IHBvaW50ZXIgLSBQb2ludGVyIHNuYXBzaG90LlxuICogQHBhcmFtIHtib29sZWFufSBjbGVhbnVwUGVuZGluZyAtIFJlY292ZXJ5IHN0YXRlLlxuICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5QdWJsaXNoZWRHZW5lcmF0aW9uPn0gVmVyaWZpZWQgZ2VuZXJhdGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5UHVibGlzaGVkR2VuZXJhdGlvbihwdWJsaWNhdGlvblJvb3QsIHByb2plY3RJZCwgcG9pbnRlciwgY2xlYW51cFBlbmRpbmcpIHtcbiAgY29uc3QgZ2VuZXJhdGlvblBhdGggPSBvd25lZFBhdGgocGF0aC5qb2luKHB1YmxpY2F0aW9uUm9vdCwgZ2VuZXJhdGlvbnNOYW1lKSwgcG9pbnRlci5nZW5lcmF0aW9uSWQsIHByb2plY3RJZClcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gcGF0aC5qb2luKGdlbmVyYXRpb25QYXRoLCBcIm1hbmlmZXN0Lmpzb25cIilcbiAgbGV0IG1hbmlmZXN0Qnl0ZXNcblxuICB0cnkge1xuICAgIG1hbmlmZXN0Qnl0ZXMgPSBhd2FpdCByZWFkQW5kU3luY1JlZ3VsYXJGaWxlKG1hbmlmZXN0UGF0aCwgcHJvamVjdElkLCBcIk1BTEZPUk1FRF9HRU5FUkFUSU9OX01BTklGRVNUXCIsXG4gICAgICBcIkdlbmVyYXRpb24gbWFuaWZlc3QgbXVzdCBiZSBhIHJlZ3VsYXIgZmlsZS5cIilcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTZW1hbnRpZm9sZERpYWdub3N0aWMpIHRocm93IGVycm9yXG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJHZW5lcmF0aW9uIG1hbmlmZXN0IGNvdWxkIG5vdCBiZSByZWFkLlwiLCBlcnJvcilcbiAgfVxuICBpZiAoc2hhMjU2KG1hbmlmZXN0Qnl0ZXMpICE9IHBvaW50ZXIubWFuaWZlc3RIYXNoKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJHZW5lcmF0aW9uIG1hbmlmZXN0IGhhc2ggZG9lcyBub3QgbWF0Y2ggdGhlIGFjdGl2ZSBwb2ludGVyLlwiKVxuICB9XG4gIGxldCB2YWx1ZVxuXG4gIHRyeSB7XG4gICAgdmFsdWUgPSBKU09OLnBhcnNlKG1hbmlmZXN0Qnl0ZXMudG9TdHJpbmcoXCJ1dGY4XCIpKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIk1BTEZPUk1FRF9HRU5FUkFUSU9OX01BTklGRVNUXCIsIHByb2plY3RJZCxcbiAgICAgIFwiR2VuZXJhdGlvbiBtYW5pZmVzdCBpcyBub3QgdmFsaWQgSlNPTi5cIiwgZXJyb3IpXG4gIH1cbiAgY29uc3QgbWFuaWZlc3QgPSB2YWxpZGF0ZUdlbmVyYXRpb25NYW5pZmVzdCh2YWx1ZSwgcHJvamVjdElkLCBwb2ludGVyLmdlbmVyYXRpb25JZClcblxuICBhd2FpdCB2ZXJpZnlNYW5pZmVzdEZpbGVzKGdlbmVyYXRpb25QYXRoLCBtYW5pZmVzdCwgcHJvamVjdElkKVxuXG4gIHJldHVybiBwdWJsaXNoZWRHZW5lcmF0aW9uKHB1YmxpY2F0aW9uUm9vdCwgbWFuaWZlc3QsIGNsZWFudXBQZW5kaW5nKVxufVxuXG4vKipcbiAqIFN0cmljdGx5IHZhbGlkYXRlcyBhIHBlcnNpc3RlZCBnZW5lcmF0aW9uIG1hbmlmZXN0LlxuICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIFBhcnNlZCBtYW5pZmVzdCB2YWx1ZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBFeHBlY3RlZCBwcm9qZWN0IGlkZW50aXR5LlxuICogQHBhcmFtIHtzdHJpbmd9IGdlbmVyYXRpb25JZCAtIEV4cGVjdGVkIGdlbmVyYXRpb24gaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0aW9uTWFuaWZlc3R9IEZyb3plbiBtYW5pZmVzdC5cbiAqL1xuZnVuY3Rpb24gdmFsaWRhdGVHZW5lcmF0aW9uTWFuaWZlc3QodmFsdWUsIHByb2plY3RJZCwgZ2VuZXJhdGlvbklkKSB7XG4gIGlmICghaXNQbGFpbk9iamVjdCh2YWx1ZSkgfHwgIWhhc0V4YWN0S2V5cyh2YWx1ZSwgW1wiZ2VuZXJhdGlvbklkXCIsIFwicHJvamVjdElkXCIsIFwic2NoZW1hXCIsIFwidGFyZ2V0c1wiLCBcInZlcnNpb25cIl0pIHx8XG4gICAgdmFsdWUuc2NoZW1hICE9IFwiU2VtYW50aWZvbGRHZW5lcmF0aW9uTWFuaWZlc3RcIiB8fCB2YWx1ZS52ZXJzaW9uICE9IDEgfHwgdmFsdWUucHJvamVjdElkICE9IHByb2plY3RJZCB8fFxuICAgIHZhbHVlLmdlbmVyYXRpb25JZCAhPSBnZW5lcmF0aW9uSWQgfHwgIWlzRGVuc2VBcnJheSh2YWx1ZS50YXJnZXRzKSB8fCB2YWx1ZS50YXJnZXRzLmxlbmd0aCA9PSAwKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJHZW5lcmF0aW9uIG1hbmlmZXN0IGRvZXMgbm90IG1hdGNoIHRoZSB2ZXJzaW9uZWQgcHJvamVjdCBjb250cmFjdC5cIilcbiAgfVxuICAvKiogQHR5cGUge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvblRhcmdldE1hbmlmZXN0W119ICovXG4gIGNvbnN0IHRhcmdldHMgPSBbXVxuICAvKiogQHR5cGUge3N0cmluZ1tdfSAqL1xuICBjb25zdCBwcm9qZWN0aW9ucyA9IFtdXG4gIGNvbnN0IGlkcyA9IG5ldyBTZXQoKVxuXG4gIGZvciAoY29uc3QgdGFyZ2V0IG9mIHZhbHVlLnRhcmdldHMpIHtcbiAgICBjb25zdCB0YXJnZXRLZXlzID0gdGFyZ2V0ICYmIHR5cGVvZiB0YXJnZXQgPT0gXCJvYmplY3RcIiAmJiBcIm1ldGFkYXRhXCIgaW4gdGFyZ2V0XG4gICAgICA/IFtcImFydGlmYWN0c1wiLCBcImJ1aWxkQXJ0aWZhY3RzXCIsIFwiaWRcIiwgXCJtZXRhZGF0YVwiLCBcInByb2plY3Rpb25zXCIsIFwicm9sZVwiLCBcInRhcmdldFwiXVxuICAgICAgOiBbXCJhcnRpZmFjdHNcIiwgXCJidWlsZEFydGlmYWN0c1wiLCBcImlkXCIsIFwicHJvamVjdGlvbnNcIiwgXCJyb2xlXCIsIFwidGFyZ2V0XCJdXG5cbiAgICBpZiAoIWlzUGxhaW5PYmplY3QodGFyZ2V0KSB8fCAhaGFzRXhhY3RLZXlzKHRhcmdldCwgdGFyZ2V0S2V5cykgfHxcbiAgICAgICFpZGVudGl0eVBhdHRlcm4udGVzdCh0eXBlb2YgdGFyZ2V0LmlkID09IFwic3RyaW5nXCIgPyB0YXJnZXQuaWQgOiBcIlwiKSB8fCBpZHMuaGFzKHRhcmdldC5pZCkgfHxcbiAgICAgIHR5cGVvZiB0YXJnZXQucm9sZSAhPSBcInN0cmluZ1wiIHx8ICF0YXJnZXRSb2xlcy5oYXModGFyZ2V0LnJvbGUpIHx8XG4gICAgICB0eXBlb2YgdGFyZ2V0LnRhcmdldCAhPSBcInN0cmluZ1wiIHx8ICFpZGVudGl0eVBhdHRlcm4udGVzdCh0YXJnZXQudGFyZ2V0KSB8fFxuICAgICAgIWlzUGxhaW5PYmplY3QodGFyZ2V0LnByb2plY3Rpb25zKSB8fCAhaGFzRXhhY3RLZXlzKHRhcmdldC5wcm9qZWN0aW9ucywgW1wiYnVpbGRcIiwgXCJzb3VyY2VcIl0pIHx8XG4gICAgICAhaXNTYWZlQXJ0aWZhY3RQYXRoKHRhcmdldC5wcm9qZWN0aW9ucy5zb3VyY2UpIHx8ICFpc1NhZmVBcnRpZmFjdFBhdGgodGFyZ2V0LnByb2plY3Rpb25zLmJ1aWxkKSB8fFxuICAgICAgIWlzRGVuc2VBcnJheSh0YXJnZXQuYXJ0aWZhY3RzKSB8fCB0YXJnZXQuYXJ0aWZhY3RzLmxlbmd0aCA9PSAwIHx8ICFpc0RlbnNlQXJyYXkodGFyZ2V0LmJ1aWxkQXJ0aWZhY3RzKSkge1xuICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgcHJvamVjdElkLFxuICAgICAgICBcIkdlbmVyYXRpb24gbWFuaWZlc3QgY29udGFpbnMgYW4gaW52YWxpZCB0YXJnZXQgcmVjb3JkLlwiKVxuICAgIH1cbiAgICBjb25zdCB0YXJnZXRJZCA9IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAodGFyZ2V0LmlkKVxuXG4gICAgaWRzLmFkZCh0YXJnZXRJZClcbiAgICBwcm9qZWN0aW9ucy5wdXNoKHRhcmdldC5wcm9qZWN0aW9ucy5zb3VyY2UsIHRhcmdldC5wcm9qZWN0aW9ucy5idWlsZClcbiAgICBjb25zdCBhcnRpZmFjdHMgPSB0YXJnZXQuYXJ0aWZhY3RzLm1hcChhcnRpZmFjdCA9PiB2YWxpZGF0ZVBlcnNpc3RlZEFydGlmYWN0KGFydGlmYWN0LCB0cnVlLCBwcm9qZWN0SWQpKVxuICAgIGNvbnN0IGJ1aWxkQXJ0aWZhY3RzID0gdGFyZ2V0LmJ1aWxkQXJ0aWZhY3RzLm1hcChhcnRpZmFjdCA9PiB2YWxpZGF0ZVBlcnNpc3RlZEFydGlmYWN0KGFydGlmYWN0LCBmYWxzZSwgcHJvamVjdElkKSlcblxuICAgIGlmIChmaW5kUG9ydGFibGVBcnRpZmFjdFBhdGhDb25mbGljdChhcnRpZmFjdHMubWFwKCh7cGF0aDogYXJ0aWZhY3RQYXRofSkgPT4gYXJ0aWZhY3RQYXRoKSkgfHxcbiAgICAgIGZpbmRQb3J0YWJsZUFydGlmYWN0UGF0aENvbmZsaWN0KGJ1aWxkQXJ0aWZhY3RzLm1hcCgoe3BhdGg6IGFydGlmYWN0UGF0aH0pID0+IGFydGlmYWN0UGF0aCkpKSB7XG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJNQUxGT1JNRURfR0VORVJBVElPTl9NQU5JRkVTVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiR2VuZXJhdGlvbiBtYW5pZmVzdCBjb250YWlucyBjb2xsaWRpbmcgYXJ0aWZhY3QgcGF0aHMuXCIpXG4gICAgfVxuICAgIHRhcmdldHMucHVzaChPYmplY3QuZnJlZXplKHtcbiAgICAgIGFydGlmYWN0czogT2JqZWN0LmZyZWV6ZSgvKiogQHR5cGUge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbkFydGlmYWN0TWFuaWZlc3RbXX0gKi8gKGFydGlmYWN0cykpLFxuICAgICAgYnVpbGRBcnRpZmFjdHM6IE9iamVjdC5mcmVlemUoLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRpb25CdWlsZEFydGlmYWN0TWFuaWZlc3RbXX0gKi8gKGJ1aWxkQXJ0aWZhY3RzKSksXG4gICAgICBpZDogdGFyZ2V0SWQsXG4gICAgICAuLi4odGFyZ2V0Lm1ldGFkYXRhID09PSB1bmRlZmluZWQgPyB7fSA6IHttZXRhZGF0YTogZGVlcEZyZWV6ZSgvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAqLyAodGFyZ2V0Lm1ldGFkYXRhKSl9KSxcbiAgICAgIHByb2plY3Rpb25zOiBPYmplY3QuZnJlZXplKHtidWlsZDogdGFyZ2V0LnByb2plY3Rpb25zLmJ1aWxkLCBzb3VyY2U6IHRhcmdldC5wcm9qZWN0aW9ucy5zb3VyY2V9KSxcbiAgICAgIHJvbGU6IC8qKiBAdHlwZSB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5QdWJsaWNhdGlvblRhcmdldFJvbGV9ICovICh0YXJnZXQucm9sZSksXG4gICAgICB0YXJnZXQ6IHRhcmdldC50YXJnZXRcbiAgICB9KSlcbiAgfVxuICBpZiAoZmluZFBvcnRhYmxlQXJ0aWZhY3RQYXRoQ29uZmxpY3QocHJvamVjdGlvbnMpIHx8XG4gICAgZmluZFBvcnRhYmxlQXJ0aWZhY3RQYXRoQ29uZmxpY3QoWy4uLnByb2plY3Rpb25zLCBcIm1hbmlmZXN0Lmpzb25cIl0pKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiTUFMRk9STUVEX0dFTkVSQVRJT05fTUFOSUZFU1RcIiwgcHJvamVjdElkLFxuICAgICAgXCJHZW5lcmF0aW9uIG1hbmlmZXN0IGNvbnRhaW5zIG92ZXJsYXBwaW5nIHByb2plY3Rpb25zLlwiKVxuICB9XG5cbiAgcmV0dXJuIE9iamVjdC5mcmVlemUoe1xuICAgIGdlbmVyYXRpb25JZCxcbiAgICBwcm9qZWN0SWQsXG4gICAgc2NoZW1hOiAvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJTZW1hbnRpZm9sZEdlbmVyYXRpb25NYW5pZmVzdFwiKSxcbiAgICB0YXJnZXRzOiBPYmplY3QuZnJlZXplKHRhcmdldHMpLFxuICAgIHZlcnNpb246IC8qKiBAdHlwZSB7Y29uc3R9ICovICgxKVxuICB9KVxufVxuXG4vKipcbiAqIFZhbGlkYXRlcyBvbmUgcGVyc2lzdGVkIGFydGlmYWN0IHJlY29yZC5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBSZWNvcmQgY2FuZGlkYXRlLlxuICogQHBhcmFtIHtib29sZWFufSBnZW5lcmF0ZWQgLSBXaGV0aGVyIHRoaXMgaXMgYSBnZW5lcmF0ZWQtc291cmNlIGFydGlmYWN0LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7aW1wb3J0KFwiLi9zZW1hbnRpYy90eXBlcy5qc1wiKS5HZW5lcmF0aW9uQXJ0aWZhY3RNYW5pZmVzdCB8IGltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbkJ1aWxkQXJ0aWZhY3RNYW5pZmVzdH0gRnJvemVuIHJlY29yZC5cbiAqL1xuZnVuY3Rpb24gdmFsaWRhdGVQZXJzaXN0ZWRBcnRpZmFjdCh2YWx1ZSwgZ2VuZXJhdGVkLCBwcm9qZWN0SWQpIHtcbiAgY29uc3Qga2V5cyA9IGdlbmVyYXRlZFxuICAgID8gW1wiYnl0ZUxlbmd0aFwiLCBcImNvbnRlbnRLaW5kXCIsIFwiaGFzaFwiLCBcIm1lZGlhVHlwZVwiLCBcInBhdGhcIiwgXCJwcm92ZW5hbmNlXCIsIFwicm9sZVwiXVxuICAgIDogW1wiYnl0ZUxlbmd0aFwiLCBcImhhc2hcIiwgXCJtZWRpYVR5cGVcIiwgXCJwYXRoXCIsIFwicm9sZVwiXVxuXG4gIGlmICghaXNQbGFpbk9iamVjdCh2YWx1ZSkgfHwgIWhhc0V4YWN0S2V5cyh2YWx1ZSwga2V5cykgfHwgIWlzU2FmZUFydGlmYWN0UGF0aCh2YWx1ZS5wYXRoKSB8fFxuICAgIHR5cGVvZiB2YWx1ZS5ieXRlTGVuZ3RoICE9IFwibnVtYmVyXCIgfHwgIU51bWJlci5pc1NhZmVJbnRlZ2VyKHZhbHVlLmJ5dGVMZW5ndGgpIHx8IHZhbHVlLmJ5dGVMZW5ndGggPCAwIHx8IHR5cGVvZiB2YWx1ZS5tZWRpYVR5cGUgIT0gXCJzdHJpbmdcIiB8fFxuICAgICFtZWRpYVR5cGVQYXR0ZXJuLnRlc3QodmFsdWUubWVkaWFUeXBlKSB8fCB0eXBlb2YgdmFsdWUucm9sZSAhPSBcInN0cmluZ1wiIHx8XG4gICAgIShnZW5lcmF0ZWQgPyBbXCJlbnRyeVwiLCBcInNvdXJjZVwiLCBcIm1hbmlmZXN0XCIsIFwic3VwcG9ydFwiLCBcIm1hcHBpbmdcIiwgXCJyZXNvdXJjZVwiLCBcImxvYWRlclwiXS5pbmNsdWRlcyh2YWx1ZS5yb2xlKSA6XG4gICAgICBidWlsZFJvbGVQYXR0ZXJuLnRlc3QodmFsdWUucm9sZSkpIHx8XG4gICAgKGdlbmVyYXRlZCAmJiB2YWx1ZS5jb250ZW50S2luZCAhPSBcInRleHRcIiAmJiB2YWx1ZS5jb250ZW50S2luZCAhPSBcImJpbmFyeVwiKSB8fFxuICAgIChnZW5lcmF0ZWQgJiYgIWlzUGxhaW5PYmplY3QodmFsdWUucHJvdmVuYW5jZSkpIHx8XG4gICAgIWlzUGxhaW5PYmplY3QodmFsdWUuaGFzaCkgfHwgIWhhc0V4YWN0S2V5cyh2YWx1ZS5oYXNoLCBbXCJhbGdvcml0aG1cIiwgXCJ2YWx1ZVwiXSkgfHwgdmFsdWUuaGFzaC5hbGdvcml0aG0gIT0gXCJzaGEyNTZcIiB8fFxuICAgIHR5cGVvZiB2YWx1ZS5oYXNoLnZhbHVlICE9IFwic3RyaW5nXCIgfHwgIS9eW2EtZjAtOV17NjR9JC91LnRlc3QodmFsdWUuaGFzaC52YWx1ZSkpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJNQUxGT1JNRURfR0VORVJBVElPTl9NQU5JRkVTVFwiLCBwcm9qZWN0SWQsXG4gICAgICBcIkdlbmVyYXRpb24gbWFuaWZlc3QgY29udGFpbnMgYW4gaW52YWxpZCBhcnRpZmFjdCByZWNvcmQuXCIpXG4gIH1cblxuICByZXR1cm4gT2JqZWN0LmZyZWV6ZSgvKiogQHR5cGUge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbkFydGlmYWN0TWFuaWZlc3QgfCBpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLkdlbmVyYXRpb25CdWlsZEFydGlmYWN0TWFuaWZlc3R9ICovICh7XG4gICAgYnl0ZUxlbmd0aDogdmFsdWUuYnl0ZUxlbmd0aCxcbiAgICAuLi4oZ2VuZXJhdGVkID8ge2NvbnRlbnRLaW5kOiB2YWx1ZS5jb250ZW50S2luZH0gOiB7fSksXG4gICAgaGFzaDogT2JqZWN0LmZyZWV6ZSh7YWxnb3JpdGhtOiAvKiogQHR5cGUge2NvbnN0fSAqLyAoXCJzaGEyNTZcIiksIHZhbHVlOiB2YWx1ZS5oYXNoLnZhbHVlfSksXG4gICAgbWVkaWFUeXBlOiB2YWx1ZS5tZWRpYVR5cGUsXG4gICAgcGF0aDogdmFsdWUucGF0aCxcbiAgICAuLi4oZ2VuZXJhdGVkID8ge3Byb3ZlbmFuY2U6IGRlZXBGcmVlemUoLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLkFydGlmYWN0UHJvdmVuYW5jZX0gKi8gKHZhbHVlLnByb3ZlbmFuY2UpKX0gOiB7fSksXG4gICAgcm9sZTogdmFsdWUucm9sZVxuICB9KSlcbn1cblxuLyoqXG4gKiBDb25zdHJ1Y3RzIGEgZnJvemVuIHJlc29sdmVkLWdlbmVyYXRpb24gcmVzdWx0LlxuICogQHBhcmFtIHtzdHJpbmd9IHB1YmxpY2F0aW9uUm9vdCAtIFB1YmxpY2F0aW9uIHJvb3QuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbk1hbmlmZXN0fSBtYW5pZmVzdCAtIFZlcmlmaWVkIG1hbmlmZXN0LlxuICogQHBhcmFtIHtib29sZWFufSBjbGVhbnVwUGVuZGluZyAtIFdoZXRoZXIgY2xlYW51cCByZW1haW5zLlxuICogQHJldHVybnMge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuUHVibGlzaGVkR2VuZXJhdGlvbn0gRnJvemVuIHJlc3VsdC5cbiAqL1xuZnVuY3Rpb24gcHVibGlzaGVkR2VuZXJhdGlvbihwdWJsaWNhdGlvblJvb3QsIG1hbmlmZXN0LCBjbGVhbnVwUGVuZGluZykge1xuICBjb25zdCBnZW5lcmF0aW9uUGF0aCA9IHBhdGguam9pbihwdWJsaWNhdGlvblJvb3QsIGdlbmVyYXRpb25zTmFtZSwgbWFuaWZlc3QuZ2VuZXJhdGlvbklkKVxuICBjb25zdCB0YXJnZXRzID0gbWFuaWZlc3QudGFyZ2V0cy5tYXAodGFyZ2V0ID0+IE9iamVjdC5mcmVlemUoe1xuICAgIGJ1aWxkUGF0aDogcGF0aC5qb2luKGdlbmVyYXRpb25QYXRoLCB0YXJnZXQucHJvamVjdGlvbnMuYnVpbGQpLFxuICAgIGlkOiB0YXJnZXQuaWQsXG4gICAgcm9sZTogdGFyZ2V0LnJvbGUsXG4gICAgc291cmNlUGF0aDogcGF0aC5qb2luKGdlbmVyYXRpb25QYXRoLCB0YXJnZXQucHJvamVjdGlvbnMuc291cmNlKVxuICB9KSlcblxuICByZXR1cm4gT2JqZWN0LmZyZWV6ZSh7XG4gICAgY2xlYW51cFBlbmRpbmcsXG4gICAgZ2VuZXJhdGlvbklkOiBtYW5pZmVzdC5nZW5lcmF0aW9uSWQsXG4gICAgZ2VuZXJhdGlvblBhdGgsXG4gICAgbWFuaWZlc3QsXG4gICAgbWFuaWZlc3RQYXRoOiBwYXRoLmpvaW4oZ2VuZXJhdGlvblBhdGgsIFwibWFuaWZlc3QuanNvblwiKSxcbiAgICB0YXJnZXRzOiBPYmplY3QuZnJlZXplKHRhcmdldHMpXG4gIH0pXG59XG5cbi8qKlxuICogUHJvYmVzIHRoZSBleGFjdCBzYW1lLWRpcmVjdG9yeSByZWd1bGFyLWZpbGUgcmVwbGFjZW1lbnQgcHJpbWl0aXZlIHVzZWQgYnkgdGhlIGFjdGl2ZSBwb2ludGVyLlxuICogQHBhcmFtIHtzdHJpbmd9IHB1YmxpY2F0aW9uUm9vdCAtIFB1YmxpY2F0aW9uIHJvb3QuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBpZGVudGl0eS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBDb21wbGV0aW9uLlxuICovXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlBdG9taWNSZXBsYWNlbWVudChwdWJsaWNhdGlvblJvb3QsIHByb2plY3RJZCkge1xuICBjb25zdCBpZGVudGl0eSA9IHJhbmRvbVVVSUQoKVxuICBjb25zdCBzb3VyY2UgPSBwYXRoLmpvaW4ocHVibGljYXRpb25Sb290LCBgLnNlbWFudGlmb2xkLWF0b21pYy0ke2lkZW50aXR5fS5zb3VyY2VgKVxuICBjb25zdCBkZXN0aW5hdGlvbiA9IHBhdGguam9pbihwdWJsaWNhdGlvblJvb3QsIGAuc2VtYW50aWZvbGQtYXRvbWljLSR7aWRlbnRpdHl9LmRlc3RpbmF0aW9uYClcbiAgY29uc3QgZXhwZWN0ZWQgPSBCdWZmZXIuZnJvbShcImNhbmRpZGF0ZVxcblwiKVxuICBsZXQgc291cmNlQ3JlYXRlZCA9IGZhbHNlXG4gIGxldCBkZXN0aW5hdGlvbkNyZWF0ZWQgPSBmYWxzZVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgd3JpdGVTeW5jZWRGaWxlKHNvdXJjZSwgQnVmZmVyLmZyb20oXCJjYW5kaWRhdGVcXG5cIiksIFwid3hcIilcbiAgICBzb3VyY2VDcmVhdGVkID0gdHJ1ZVxuICAgIGF3YWl0IHdyaXRlU3luY2VkRmlsZShkZXN0aW5hdGlvbiwgQnVmZmVyLmZyb20oXCJwcmV2aW91c1xcblwiKSwgXCJ3eFwiKVxuICAgIGRlc3RpbmF0aW9uQ3JlYXRlZCA9IHRydWVcbiAgICBhd2FpdCByZW5hbWUoc291cmNlLCBkZXN0aW5hdGlvbilcbiAgICBzb3VyY2VDcmVhdGVkID0gZmFsc2VcbiAgICBhd2FpdCBzeW5jRGlyZWN0b3J5KHB1YmxpY2F0aW9uUm9vdClcbiAgICBjb25zdCBkZXN0aW5hdGlvblN0YXR1cyA9IGF3YWl0IGxzdGF0KGRlc3RpbmF0aW9uKVxuICAgIGNvbnN0IHJlcGxhY2VkID0gYXdhaXQgcmVhZEZpbGUoZGVzdGluYXRpb24pXG5cbiAgICBpZiAoIWRlc3RpbmF0aW9uU3RhdHVzLmlzRmlsZSgpIHx8IGRlc3RpbmF0aW9uU3RhdHVzLmlzU3ltYm9saWNMaW5rKCkgfHwgIXJlcGxhY2VkLmVxdWFscyhleHBlY3RlZCkpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX0FUT01JQ19SRVBMQUNFX1VOU1VQUE9SVEVEXCIsIHByb2plY3RJZCxcbiAgICAgICAgXCJGaWxlc3lzdGVtIGRpZCBub3QgcHJvdmlkZSBzYW1lLWRpcmVjdG9yeSByZWd1bGFyLWZpbGUgcmVwbGFjZW1lbnQuXCIpXG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBsc3RhdChzb3VyY2UpXG4gICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9BVE9NSUNfUkVQTEFDRV9VTlNVUFBPUlRFRFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiRmlsZXN5c3RlbSBkaWQgbm90IHJlbW92ZSB0aGUgcmVwbGFjZWQgdGVtcG9yYXJ5IG5hbWUgYXRvbWljYWxseS5cIilcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKCFpc0Vycm9yQ29kZShlcnJvciwgXCJFTk9FTlRcIikpIHRocm93IGVycm9yXG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYykgdGhyb3cgZXJyb3JcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9BVE9NSUNfUkVQTEFDRV9VTlNVUFBPUlRFRFwiLCBwcm9qZWN0SWQsXG4gICAgICBcIkZpbGVzeXN0ZW0gZGlkIG5vdCBwcm92aWRlIHNhbWUtZGlyZWN0b3J5IHJlZ3VsYXItZmlsZSByZXBsYWNlbWVudC5cIiwgZXJyb3IpXG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcbiAgICAgIC4uLihzb3VyY2VDcmVhdGVkID8gW3JtKHNvdXJjZSwge2ZvcmNlOiB0cnVlfSldIDogW10pLFxuICAgICAgLi4uKGRlc3RpbmF0aW9uQ3JlYXRlZCA/IFtybShkZXN0aW5hdGlvbiwge2ZvcmNlOiB0cnVlfSldIDogW10pXG4gICAgXSlcbiAgfVxufVxuXG4vKipcbiAqIFdyaXRlcyBhbmQgc3luY2hyb25pemVzIG9uZSBuZXdseSBvd25lZCBmaWxlLlxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGVuYW1lIC0gRXhhY3QgZmlsZW5hbWUuXG4gKiBAcGFyYW0ge1VpbnQ4QXJyYXl9IGJ5dGVzIC0gRXhhY3QgYnl0ZXMuXG4gKiBAcGFyYW0ge1wid3hcIn0gZmxhZyAtIEV4Y2x1c2l2ZSBjcmVhdGlvbiBtb2RlLlxuICogQHBhcmFtIHsoKSA9PiB2b2lkfSBbb25DcmVhdGVkXSAtIENhbGxlZCBpbW1lZGlhdGVseSBhZnRlciBleGNsdXNpdmUgY3JlYXRpb24gc3VjY2VlZHMuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gd3JpdGVTeW5jZWRGaWxlKGZpbGVuYW1lLCBieXRlcywgZmxhZywgb25DcmVhdGVkKSB7XG4gIGNvbnN0IGhhbmRsZSA9IGF3YWl0IG9wZW4oZmlsZW5hbWUsIGZsYWcsIDBvNjAwKVxuXG4gIG9uQ3JlYXRlZD8uKClcbiAgLyoqIEB0eXBlIHt1bmtub3dufSAqL1xuICBsZXQgZmFpbHVyZVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgaGFuZGxlLndyaXRlRmlsZShieXRlcylcbiAgICBhd2FpdCBoYW5kbGUuc3luYygpXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgZmFpbHVyZSA9IGVycm9yXG4gIH0gZmluYWxseSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGhhbmRsZS5jbG9zZSgpXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChmYWlsdXJlID09PSB1bmRlZmluZWQpIGZhaWx1cmUgPSBlcnJvclxuICAgIH1cbiAgfVxuICBpZiAoZmFpbHVyZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgYXdhaXQgcm0oZmlsZW5hbWUsIHtmb3JjZTogdHJ1ZX0pLmNhdGNoKCgpID0+IHt9KVxuICAgIHRocm93IGZhaWx1cmVcbiAgfVxufVxuXG4vKipcbiAqIFJlYWRzIGFuZCBzeW5jaHJvbml6ZXMgb25lIHJlZ3VsYXIgbm9uLXN5bWxpbmsgZmlsZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZSAtIEV4YWN0IGZpbGVuYW1lLlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcGFyYW0ge3N0cmluZ30gY29kZSAtIEZhaWx1cmUgY29kZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gU3RhYmxlIGZhaWx1cmUgZGV0YWlsLlxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gRXhhY3QgYnl0ZXMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlYWRBbmRTeW5jUmVndWxhckZpbGUoZmlsZW5hbWUsIHByb2plY3RJZCwgY29kZSwgbWVzc2FnZSkge1xuICB0cnkge1xuICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IGxzdGF0KGZpbGVuYW1lKVxuXG4gICAgaWYgKCFzdGF0dXMuaXNGaWxlKCkgfHwgc3RhdHVzLmlzU3ltYm9saWNMaW5rKCkpIHB1YmxpY2F0aW9uRmFpbHVyZShjb2RlLCBwcm9qZWN0SWQsIG1lc3NhZ2UpXG4gICAgYXNzZXJ0UmVndWxhckZpbGVJbW11dGFiaWxpdHkoc3RhdHVzLCBuZXcgU2V0KCksIHByb2plY3RJZClcbiAgICBjb25zdCBoYW5kbGUgPSBhd2FpdCBvcGVuKGZpbGVuYW1lLCBjb25zdGFudHMuT19SRE9OTFkpXG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgYnl0ZXMgPSBhd2FpdCBoYW5kbGUucmVhZEZpbGUoKVxuXG4gICAgICBhd2FpdCBoYW5kbGUuc3luYygpXG5cbiAgICAgIHJldHVybiBieXRlc1xuICAgIH0gZmluYWxseSB7XG4gICAgICBhd2FpdCBoYW5kbGUuY2xvc2UoKVxuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTZW1hbnRpZm9sZERpYWdub3N0aWMpIHRocm93IGVycm9yXG4gICAgcHVibGljYXRpb25GYWlsdXJlKGNvZGUsIHByb2plY3RJZCwgbWVzc2FnZSwgZXJyb3IpXG4gIH1cbn1cblxuLyoqXG4gKiBMaXN0cyBldmVyeSByZWd1bGFyIGZpbGUgYmVuZWF0aCBvbmUgcHJvamVjdGlvbiBhbmQgcmVqZWN0cyBzeW1saW5rcyBvciBzcGVjaWFsIG5vZGVzLlxuICogQHBhcmFtIHtzdHJpbmd9IGRpcmVjdG9yeSAtIFByb2plY3Rpb24gcm9vdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nW10+fSBTb3J0ZWQgcmVsYXRpdmUgUE9TSVggcGF0aHMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGxpc3RSZWd1bGFyRmlsZXMoZGlyZWN0b3J5LCBwcm9qZWN0SWQpIHtcbiAgLyoqIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgY29uc3QgZmlsZXMgPSBbXVxuICBjb25zdCBpZGVudGl0aWVzID0gbmV3IFNldCgpXG5cbiAgdHJ5IHtcbiAgICBhd2FpdCB2aXNpdChkaXJlY3RvcnksIFwiXCIpXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU2VtYW50aWZvbGREaWFnbm9zdGljKSB0aHJvdyBlcnJvclxuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX0dFTkVSQVRJT05fVkVSSUZJQ0FUSU9OX0ZBSUxFRFwiLCBwcm9qZWN0SWQsXG4gICAgICBcIkltbXV0YWJsZSBnZW5lcmF0aW9uIGZpbGVzeXN0ZW0gaW52ZW50b3J5IGNvdWxkIG5vdCBiZSB2ZXJpZmllZC5cIiwgZXJyb3IpXG4gIH1cblxuICByZXR1cm4gZmlsZXMuc29ydChjb21wYXJlU3RyaW5ncylcblxuICAvKipcbiAgICogVmlzaXRzIG9uZSBvd25lZCBkaXJlY3RvcnkuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjdXJyZW50IC0gRXhhY3QgZGlyZWN0b3J5LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcmVsYXRpdmUgLSBQcm9qZWN0aW9uLXJlbGF0aXZlIHBhdGguXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBDb21wbGV0aW9uLlxuICAgKi9cbiAgYXN5bmMgZnVuY3Rpb24gdmlzaXQoY3VycmVudCwgcmVsYXRpdmUpIHtcbiAgICBhd2FpdCBhc3NlcnRSZWFsSW52ZW50b3J5RGlyZWN0b3J5KGN1cnJlbnQsIHByb2plY3RJZClcbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgcmVhZGRpcihjdXJyZW50KVxuXG4gICAgZW50cmllcy5zb3J0KGNvbXBhcmVTdHJpbmdzKVxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgY29uc3QgY2hpbGRSZWxhdGl2ZSA9IHJlbGF0aXZlLmxlbmd0aCA9PSAwID8gZW50cnkgOiBgJHtyZWxhdGl2ZX0vJHtlbnRyeX1gXG5cbiAgICAgIGlmICghaXNTYWZlQXJ0aWZhY3RQYXRoKGNoaWxkUmVsYXRpdmUpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUFJPSkVDVElPTlwiLCBwcm9qZWN0SWQsXG4gICAgICAgICAgXCJWYWxpZGF0b3Igb3V0cHV0IGNvbnRhaW5zIGEgbm9uLWNhbm9uaWNhbCByZWxhdGl2ZSBwYXRoLlwiKVxuICAgICAgfVxuICAgICAgY29uc3QgY2hpbGQgPSBwYXRoLmpvaW4oY3VycmVudCwgZW50cnkpXG4gICAgICBjb25zdCBzdGF0dXMgPSBhd2FpdCBsc3RhdChjaGlsZClcblxuICAgICAgaWYgKHN0YXR1cy5pc1N5bWJvbGljTGluaygpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1NZTUxJTktfVFJBVkVSU0FMXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIkNhbmRpZGF0ZSBnZW5lcmF0aW9uIGNvbnRhaW5zIGEgc3ltYm9saWMgbGluay5cIilcbiAgICAgIH1cbiAgICAgIGlmIChzdGF0dXMuaXNEaXJlY3RvcnkoKSkgYXdhaXQgdmlzaXQoY2hpbGQsIGNoaWxkUmVsYXRpdmUpXG4gICAgICBlbHNlIGlmIChzdGF0dXMuaXNGaWxlKCkpIHtcbiAgICAgICAgYXNzZXJ0UmVndWxhckZpbGVJbW11dGFiaWxpdHkoc3RhdHVzLCBpZGVudGl0aWVzLCBwcm9qZWN0SWQpXG4gICAgICAgIGZpbGVzLnB1c2goY2hpbGRSZWxhdGl2ZSlcbiAgICAgIH0gZWxzZSBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9HRU5FUkFUSU9OX0lOVkVOVE9SWV9NSVNNQVRDSFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiSW1tdXRhYmxlIGdlbmVyYXRpb24gY29udGFpbnMgYW4gdW5kZWNsYXJlZCBmaWxlc3lzdGVtIGVudHJ5LlwiKVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIFZlcmlmaWVzIHRoYXQgdGhlIG1hbmlmZXN0IGRlc2NyaWJlcyBldmVyeSBkaXJlY3RvcnkgYW5kIHJlZ3VsYXIgZmlsZSBpbiB0aGUgaW1tdXRhYmxlIGdlbmVyYXRpb24uXG4gKiBAcGFyYW0ge3N0cmluZ30gZ2VuZXJhdGlvblBhdGggLSBFeGFjdCBnZW5lcmF0aW9uIHJvb3QuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGlvbk1hbmlmZXN0fSBtYW5pZmVzdCAtIFZlcmlmaWVkIG1hbmlmZXN0LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5R2VuZXJhdGlvbkludmVudG9yeShnZW5lcmF0aW9uUGF0aCwgbWFuaWZlc3QsIHByb2plY3RJZCkge1xuICBjb25zdCBleHBlY3RlZEZpbGVzID0gbmV3IFNldChbXCJtYW5pZmVzdC5qc29uXCJdKVxuICBjb25zdCBleHBlY3RlZERpcmVjdG9yaWVzID0gbmV3IFNldCgpXG5cbiAgZm9yIChjb25zdCB0YXJnZXQgb2YgbWFuaWZlc3QudGFyZ2V0cykge1xuICAgIGFkZEV4cGVjdGVkRGlyZWN0b3J5KHRhcmdldC5wcm9qZWN0aW9ucy5zb3VyY2UpXG4gICAgYWRkRXhwZWN0ZWREaXJlY3RvcnkodGFyZ2V0LnByb2plY3Rpb25zLmJ1aWxkKVxuICAgIGZvciAoY29uc3QgYXJ0aWZhY3Qgb2YgdGFyZ2V0LmFydGlmYWN0cykge1xuICAgICAgYWRkRXhwZWN0ZWRGaWxlKGAke3RhcmdldC5wcm9qZWN0aW9ucy5zb3VyY2V9LyR7YXJ0aWZhY3QucGF0aH1gKVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IGFydGlmYWN0IG9mIHRhcmdldC5idWlsZEFydGlmYWN0cykge1xuICAgICAgYWRkRXhwZWN0ZWRGaWxlKGAke3RhcmdldC5wcm9qZWN0aW9ucy5idWlsZH0vJHthcnRpZmFjdC5wYXRofWApXG4gICAgfVxuICB9XG4gIC8qKiBAdHlwZSB7U2V0PHN0cmluZz59ICovXG4gIGNvbnN0IG9ic2VydmVkRmlsZXMgPSBuZXcgU2V0KClcbiAgLyoqIEB0eXBlIHtTZXQ8c3RyaW5nPn0gKi9cbiAgY29uc3Qgb2JzZXJ2ZWREaXJlY3RvcmllcyA9IG5ldyBTZXQoKVxuICBjb25zdCBpZGVudGl0aWVzID0gbmV3IFNldCgpXG5cbiAgdHJ5IHtcbiAgICBhd2FpdCB2aXNpdChnZW5lcmF0aW9uUGF0aCwgXCJcIilcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTZW1hbnRpZm9sZERpYWdub3N0aWMpIHRocm93IGVycm9yXG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fR0VORVJBVElPTl9WRVJJRklDQVRJT05fRkFJTEVEXCIsIHByb2plY3RJZCxcbiAgICAgIFwiSW1tdXRhYmxlIGdlbmVyYXRpb24gZmlsZXN5c3RlbSBpbnZlbnRvcnkgY291bGQgbm90IGJlIHZlcmlmaWVkLlwiLCBlcnJvcilcbiAgfVxuICBpZiAoIXNhbWVTdHJpbmdTZXQoZXhwZWN0ZWRGaWxlcywgb2JzZXJ2ZWRGaWxlcykgfHwgIXNhbWVTdHJpbmdTZXQoZXhwZWN0ZWREaXJlY3Rvcmllcywgb2JzZXJ2ZWREaXJlY3RvcmllcykpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9HRU5FUkFUSU9OX0lOVkVOVE9SWV9NSVNNQVRDSFwiLCBwcm9qZWN0SWQsXG4gICAgICBcIkltbXV0YWJsZSBnZW5lcmF0aW9uIGNvbnRhaW5zIGFuIHVuZGVjbGFyZWQgZmlsZXN5c3RlbSBlbnRyeS5cIilcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIG9uZSBleHBlY3RlZCBkaXJlY3RvcnkgYW5kIGVhY2ggZ2VuZXJhdGlvbi1yZWxhdGl2ZSBwYXJlbnQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSByZWxhdGl2ZSAtIFBPU0lYLXN0eWxlIGdlbmVyYXRpb24tcmVsYXRpdmUgZGlyZWN0b3J5LlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGZ1bmN0aW9uIGFkZEV4cGVjdGVkRGlyZWN0b3J5KHJlbGF0aXZlKSB7XG4gICAgY29uc3QgcGFydHMgPSByZWxhdGl2ZS5zcGxpdChcIi9cIilcblxuICAgIGZvciAobGV0IGluZGV4ID0gMTsgaW5kZXggPD0gcGFydHMubGVuZ3RoOyBpbmRleCArPSAxKSB7XG4gICAgICBleHBlY3RlZERpcmVjdG9yaWVzLmFkZChwYXJ0cy5zbGljZSgwLCBpbmRleCkuam9pbihcIi9cIikpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgb25lIGV4cGVjdGVkIHJlZ3VsYXIgZmlsZSBhbmQgaXRzIHBhcmVudCBkaXJlY3Rvcmllcy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHJlbGF0aXZlIC0gUE9TSVgtc3R5bGUgZ2VuZXJhdGlvbi1yZWxhdGl2ZSBmaWxlbmFtZS5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBmdW5jdGlvbiBhZGRFeHBlY3RlZEZpbGUocmVsYXRpdmUpIHtcbiAgICBleHBlY3RlZEZpbGVzLmFkZChyZWxhdGl2ZSlcbiAgICBjb25zdCBzZXBhcmF0b3IgPSByZWxhdGl2ZS5sYXN0SW5kZXhPZihcIi9cIilcblxuICAgIGlmIChzZXBhcmF0b3IgPj0gMCkgYWRkRXhwZWN0ZWREaXJlY3RvcnkocmVsYXRpdmUuc2xpY2UoMCwgc2VwYXJhdG9yKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBWaXNpdHMgZXZlcnkgZW50cnkgaW4gb25lIGdlbmVyYXRpb24gZGlyZWN0b3J5IHdpdGhvdXQgZm9sbG93aW5nIGxpbmtzLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gY3VycmVudCAtIEV4YWN0IGRpcmVjdG9yeS5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHJlbGF0aXZlIC0gR2VuZXJhdGlvbi1yZWxhdGl2ZSBkaXJlY3RvcnkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBDb21wbGV0aW9uLlxuICAgKi9cbiAgYXN5bmMgZnVuY3Rpb24gdmlzaXQoY3VycmVudCwgcmVsYXRpdmUpIHtcbiAgICBhd2FpdCBhc3NlcnRSZWFsSW52ZW50b3J5RGlyZWN0b3J5KGN1cnJlbnQsIHByb2plY3RJZClcbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgcmVhZGRpcihjdXJyZW50KVxuXG4gICAgZW50cmllcy5zb3J0KGNvbXBhcmVTdHJpbmdzKVxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgY29uc3QgY2hpbGRSZWxhdGl2ZSA9IHJlbGF0aXZlLmxlbmd0aCA9PSAwID8gZW50cnkgOiBgJHtyZWxhdGl2ZX0vJHtlbnRyeX1gXG5cbiAgICAgIGlmICghaXNTYWZlQXJ0aWZhY3RQYXRoKGNoaWxkUmVsYXRpdmUpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX0dFTkVSQVRJT05fSU5WRU5UT1JZX01JU01BVENIXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIkltbXV0YWJsZSBnZW5lcmF0aW9uIGNvbnRhaW5zIGFuIHVuZGVjbGFyZWQgZmlsZXN5c3RlbSBlbnRyeS5cIilcbiAgICAgIH1cbiAgICAgIGNvbnN0IGNoaWxkID0gcGF0aC5qb2luKGN1cnJlbnQsIGVudHJ5KVxuICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgbHN0YXQoY2hpbGQpXG5cbiAgICAgIGlmIChzdGF0dXMuaXNTeW1ib2xpY0xpbmsoKSkge1xuICAgICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9TWU1MSU5LX1RSQVZFUlNBTFwiLCBwcm9qZWN0SWQsXG4gICAgICAgICAgXCJDYW5kaWRhdGUgZ2VuZXJhdGlvbiBjb250YWlucyBhIHN5bWJvbGljIGxpbmsuXCIpXG4gICAgICB9XG4gICAgICBpZiAoc3RhdHVzLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgb2JzZXJ2ZWREaXJlY3Rvcmllcy5hZGQoY2hpbGRSZWxhdGl2ZSlcbiAgICAgICAgYXdhaXQgdmlzaXQoY2hpbGQsIGNoaWxkUmVsYXRpdmUpXG4gICAgICB9IGVsc2UgaWYgKHN0YXR1cy5pc0ZpbGUoKSkge1xuICAgICAgICBhc3NlcnRSZWd1bGFyRmlsZUltbXV0YWJpbGl0eShzdGF0dXMsIGlkZW50aXRpZXMsIHByb2plY3RJZClcbiAgICAgICAgb2JzZXJ2ZWRGaWxlcy5hZGQoY2hpbGRSZWxhdGl2ZSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX0dFTkVSQVRJT05fSU5WRU5UT1JZX01JU01BVENIXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIkltbXV0YWJsZSBnZW5lcmF0aW9uIGNvbnRhaW5zIGFuIHVuZGVjbGFyZWQgZmlsZXN5c3RlbSBlbnRyeS5cIilcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBWZXJpZmllcyBvbmUgaW52ZW50b3J5IHRyYXZlcnNhbCByb290IHdpdGhvdXQgZm9sbG93aW5nIHN5bWJvbGljIGxpbmtzLlxuICogQHBhcmFtIHtzdHJpbmd9IGRpcmVjdG9yeSAtIEV4YWN0IGRpcmVjdG9yeSBhYm91dCB0byBiZSByZWFkLlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gYXNzZXJ0UmVhbEludmVudG9yeURpcmVjdG9yeShkaXJlY3RvcnksIHByb2plY3RJZCkge1xuICBjb25zdCBzdGF0dXMgPSBhd2FpdCBsc3RhdChkaXJlY3RvcnkpXG5cbiAgaWYgKHN0YXR1cy5pc1N5bWJvbGljTGluaygpKSB7XG4gICAgcHVibGljYXRpb25GYWlsdXJlKFwiUFVCTElDQVRJT05fU1lNTElOS19UUkFWRVJTQUxcIiwgcHJvamVjdElkLFxuICAgICAgXCJDYW5kaWRhdGUgZ2VuZXJhdGlvbiBjb250YWlucyBhIHN5bWJvbGljIGxpbmsuXCIpXG4gIH1cbiAgaWYgKCFzdGF0dXMuaXNEaXJlY3RvcnkoKSkge1xuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX0dFTkVSQVRJT05fSU5WRU5UT1JZX01JU01BVENIXCIsIHByb2plY3RJZCxcbiAgICAgIFwiSW1tdXRhYmxlIGdlbmVyYXRpb24gY29udGFpbnMgYW4gdW5kZWNsYXJlZCBmaWxlc3lzdGVtIGVudHJ5LlwiKVxuICB9XG59XG5cbi8qKlxuICogUmVqZWN0cyBleHRlcm5hbCBoYXJkIGxpbmtzIGFuZCBkdXBsaWNhdGUgc3RhYmxlIGZpbGUgaWRlbnRpdGllcy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwibm9kZTpmc1wiKS5TdGF0c30gc3RhdHVzIC0gUmVndWxhci1maWxlIHN0YXR1cy5cbiAqIEBwYXJhbSB7U2V0PHN0cmluZz59IGlkZW50aXRpZXMgLSBQcmV2aW91c2x5IG9ic2VydmVkIGRldmljZS9pbm9kZSBpZGVudGl0aWVzLlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7dm9pZH1cbiAqL1xuZnVuY3Rpb24gYXNzZXJ0UmVndWxhckZpbGVJbW11dGFiaWxpdHkoc3RhdHVzLCBpZGVudGl0aWVzLCBwcm9qZWN0SWQpIHtcbiAgaWYgKHN0YXR1cy5ubGluayA+IDEpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9IQVJEX0xJTktcIiwgcHJvamVjdElkLFxuICAgICAgXCJJbW11dGFibGUgZ2VuZXJhdGlvbiBmaWxlcyBtdXN0IG5vdCBoYXZlIGV4dGVybmFsIGhhcmQgbGlua3MuXCIpXG4gIH1cbiAgY29uc3QgaWRlbnRpdHkgPSBzdGFibGVGaWxlSWRlbnRpdHkoc3RhdHVzKVxuXG4gIGlmIChpZGVudGl0eSAhPT0gbnVsbCkge1xuICAgIGlmIChpZGVudGl0aWVzLmhhcyhpZGVudGl0eSkpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX0hBUkRfTElOS1wiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiSW1tdXRhYmxlIGdlbmVyYXRpb24gZmlsZXMgbXVzdCBoYXZlIHVuaXF1ZSBmaWxlc3lzdGVtIGlkZW50aXRpZXMuXCIpXG4gICAgfVxuICAgIGlkZW50aXRpZXMuYWRkKGlkZW50aXR5KVxuICB9XG59XG5cbi8qKlxuICogUmV0dXJucyBhIHN0YWJsZSBkZXZpY2UvaW5vZGUgaWRlbnRpdHkgb25seSB3aGVyZSB0aGUgaG9zdCByZXBvcnRzIG9uZS5cbiAqIEBwYXJhbSB7aW1wb3J0KFwibm9kZTpmc1wiKS5TdGF0c30gc3RhdHVzIC0gRmlsZSBzdGF0dXMuXG4gKiBAcmV0dXJucyB7c3RyaW5nIHwgbnVsbH0gSWRlbnRpdHkgb3IgdW5hdmFpbGFibGUuXG4gKi9cbmZ1bmN0aW9uIHN0YWJsZUZpbGVJZGVudGl0eShzdGF0dXMpIHtcbiAgaWYgKCh0eXBlb2Ygc3RhdHVzLmRldiAhPSBcIm51bWJlclwiICYmIHR5cGVvZiBzdGF0dXMuZGV2ICE9IFwiYmlnaW50XCIpIHx8XG4gICAgKHR5cGVvZiBzdGF0dXMuaW5vICE9IFwibnVtYmVyXCIgJiYgdHlwZW9mIHN0YXR1cy5pbm8gIT0gXCJiaWdpbnRcIikgfHwgc3RhdHVzLmlubyA9PSAwKSByZXR1cm4gbnVsbFxuXG4gIHJldHVybiBgJHtTdHJpbmcoc3RhdHVzLmRldil9OiR7U3RyaW5nKHN0YXR1cy5pbm8pfWBcbn1cblxuLyoqXG4gKiBDb21wYXJlcyB0d28gc3RyaW5nIHNldHMgZXhhY3RseS5cbiAqIEBwYXJhbSB7U2V0PHN0cmluZz59IGxlZnQgLSBGaXJzdCBzZXQuXG4gKiBAcGFyYW0ge1NldDxzdHJpbmc+fSByaWdodCAtIFNlY29uZCBzZXQuXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gRXF1YWxpdHkuXG4gKi9cbmZ1bmN0aW9uIHNhbWVTdHJpbmdTZXQobGVmdCwgcmlnaHQpIHtcbiAgcmV0dXJuIGxlZnQuc2l6ZSA9PSByaWdodC5zaXplICYmIFsuLi5sZWZ0XS5ldmVyeSh2YWx1ZSA9PiByaWdodC5oYXModmFsdWUpKVxufVxuXG4vKipcbiAqIFN5bmNocm9uaXplcyBldmVyeSBkaXJlY3RvcnkgaW4gYSBzdGFnZWQgdHJlZSBmcm9tIGxlYXZlcyB0byByb290LlxuICogQHBhcmFtIHtzdHJpbmd9IHJvb3QgLSBUcmVlIHJvb3QuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc3luY1RyZWVEaXJlY3Rvcmllcyhyb290KSB7XG4gIGNvbnN0IGVudHJpZXMgPSBhd2FpdCByZWFkZGlyKHJvb3QsIHt3aXRoRmlsZVR5cGVzOiB0cnVlfSlcblxuICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICBpZiAoZW50cnkuaXNEaXJlY3RvcnkoKSAmJiAhZW50cnkuaXNTeW1ib2xpY0xpbmsoKSkgYXdhaXQgc3luY1RyZWVEaXJlY3RvcmllcyhwYXRoLmpvaW4ocm9vdCwgZW50cnkubmFtZSkpXG4gIH1cbiAgYXdhaXQgc3luY0RpcmVjdG9yeShyb290KVxufVxuXG4vKipcbiAqIFN5bmNocm9uaXplcyBvbmUgZGlyZWN0b3J5IGVudHJ5IHNldC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBkaXJlY3RvcnkgLSBEaXJlY3RvcnkgcGF0aC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBDb21wbGV0aW9uLlxuICovXG5hc3luYyBmdW5jdGlvbiBzeW5jRGlyZWN0b3J5KGRpcmVjdG9yeSkge1xuICBjb25zdCBoYW5kbGUgPSBhd2FpdCBvcGVuKGRpcmVjdG9yeSwgY29uc3RhbnRzLk9fUkRPTkxZKVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgaGFuZGxlLnN5bmMoKVxuICB9IGZpbmFsbHkge1xuICAgIGF3YWl0IGhhbmRsZS5jbG9zZSgpXG4gIH1cbn1cblxuLyoqXG4gKiBFbnN1cmVzIGV2ZXJ5IGV4aXN0aW5nIHBhdGggY29tcG9uZW50IGlzIGEgcmVhbCBkaXJlY3RvcnkgYW5kIG9wdGlvbmFsbHkgY3JlYXRlcyBtaXNzaW5nIGNvbXBvbmVudHMuXG4gKiBAcGFyYW0ge3N0cmluZ30gZGlyZWN0b3J5IC0gQ2Fub25pY2FsIGFic29sdXRlIGRpcmVjdG9yeS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gY3JlYXRlIC0gV2hldGhlciBtaXNzaW5nIGNvbXBvbmVudHMgbWF5IGJlIGNyZWF0ZWQuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBpZGVudGl0eS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBDb21wbGV0aW9uLlxuICovXG5hc3luYyBmdW5jdGlvbiBlbnN1cmVEaXJlY3RvcnlXaXRob3V0U3ltbGlua3MoZGlyZWN0b3J5LCBjcmVhdGUsIHByb2plY3RJZCkge1xuICBjb25zdCBwYXJzZWQgPSBwYXRoLnBhcnNlKGRpcmVjdG9yeSlcbiAgY29uc3QgcmVsYXRpdmUgPSBkaXJlY3Rvcnkuc2xpY2UocGFyc2VkLnJvb3QubGVuZ3RoKVxuICBjb25zdCBwYXJ0cyA9IHJlbGF0aXZlLnNwbGl0KHBhdGguc2VwKS5maWx0ZXIoQm9vbGVhbilcbiAgbGV0IGN1cnJlbnQgPSBwYXJzZWQucm9vdFxuXG4gIGZvciAoY29uc3QgcGFydCBvZiBwYXJ0cykge1xuICAgIGN1cnJlbnQgPSBwYXRoLmpvaW4oY3VycmVudCwgcGFydClcbiAgICB0cnkge1xuICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgbHN0YXQoY3VycmVudClcblxuICAgICAgaWYgKHN0YXR1cy5pc1N5bWJvbGljTGluaygpKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1NZTUxJTktfVFJBVkVSU0FMXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIk93bmVkIHBhdGggdHJhdmVyc2FsIGVuY291bnRlcmVkIGEgc3ltYm9saWMgbGluay5cIilcbiAgICAgIH1cbiAgICAgIGlmICghc3RhdHVzLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgcHVibGljYXRpb25GYWlsdXJlKFwiSU5WQUxJRF9QVUJMSUNBVElPTl9ST09UXCIsIHByb2plY3RJZCxcbiAgICAgICAgICBcIk93bmVkIHJvb3QgcGF0aCBjb250YWlucyBhIG5vbi1kaXJlY3RvcnkgY29tcG9uZW50LlwiKVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoIWlzRXJyb3JDb2RlKGVycm9yLCBcIkVOT0VOVFwiKSkge1xuICAgICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTZW1hbnRpZm9sZERpYWdub3N0aWMpIHRocm93IGVycm9yXG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgICAgXCJPd25lZCByb290IHBhdGggY291bGQgbm90IGJlIHZhbGlkYXRlZC5cIiwgZXJyb3IpXG4gICAgICB9XG4gICAgICBpZiAoIWNyZWF0ZSkge1xuICAgICAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJJTlZBTElEX1BVQkxJQ0FUSU9OX1JPT1RcIiwgcHJvamVjdElkLFxuICAgICAgICAgIFwiU291cmNlIHJvb3QgbXVzdCBleGlzdCBhcyBhIHJlYWwgZGlyZWN0b3J5LlwiLCBlcnJvcilcbiAgICAgIH1cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IG1rZGlyKGN1cnJlbnQsIHttb2RlOiAwbzcwMH0pXG4gICAgICB9IGNhdGNoIChta2RpckVycm9yKSB7XG4gICAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIklOVkFMSURfUFVCTElDQVRJT05fUk9PVFwiLCBwcm9qZWN0SWQsXG4gICAgICAgICAgXCJQdWJsaWNhdGlvbiByb290IGNvdWxkIG5vdCBiZSBjcmVhdGVkIHNhZmVseS5cIiwgbWtkaXJFcnJvcilcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBFbnN1cmVzIG9uZSByZXNlcnZlZCBwdWJsaXNoZXIgZGlyZWN0b3J5IGlzIG5laXRoZXIgYSBzeW1saW5rIG5vciBhbiB1bm93bmVkIGZpbGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gZGlyZWN0b3J5IC0gUmVzZXJ2ZWQgZGlyZWN0b3J5LlxuICogQHBhcmFtIHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZW5zdXJlT3duZWREaXJlY3RvcnkoZGlyZWN0b3J5LCBwcm9qZWN0SWQpIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBta2RpcihkaXJlY3RvcnksIHttb2RlOiAwbzcwMH0pXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKCFpc0Vycm9yQ29kZShlcnJvciwgXCJFRVhJU1RcIikpIHtcbiAgICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1NUQUdFX1dSSVRFX0ZBSUxFRFwiLCBwcm9qZWN0SWQsXG4gICAgICAgIFwiUHVibGlzaGVyLW93bmVkIGRpcmVjdG9yeSBjb3VsZCBub3QgYmUgY3JlYXRlZC5cIiwgZXJyb3IpXG4gICAgfVxuICB9XG4gIGxldCBzdGF0dXNcblxuICB0cnkge1xuICAgIHN0YXR1cyA9IGF3YWl0IGxzdGF0KGRpcmVjdG9yeSlcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoXCJQVUJMSUNBVElPTl9TVEFHRV9XUklURV9GQUlMRURcIiwgcHJvamVjdElkLFxuICAgICAgXCJSZXNlcnZlZCBwdWJsaXNoZXIgcGF0aCBjb3VsZCBub3QgYmUgaW5zcGVjdGVkLlwiLCBlcnJvcilcbiAgfVxuXG4gIGlmICghc3RhdHVzLmlzRGlyZWN0b3J5KCkgfHwgc3RhdHVzLmlzU3ltYm9saWNMaW5rKCkpIHtcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoc3RhdHVzLmlzU3ltYm9saWNMaW5rKCkgPyBcIlBVQkxJQ0FUSU9OX1NZTUxJTktfVFJBVkVSU0FMXCIgOiBcIlBVQkxJQ0FUSU9OX1VOT1dORURfQ09ORkxJQ1RcIixcbiAgICAgIHByb2plY3RJZCwgXCJSZXNlcnZlZCBwdWJsaXNoZXIgcGF0aCBpcyBub3QgYW4gb3duZWQgcmVhbCBkaXJlY3RvcnkuXCIpXG4gIH1cbn1cblxuLyoqXG4gKiBFbnN1cmVzIGFuIG93bmVkIHBhdGggaGFzIG5vIHByZS1leGlzdGluZyBlbnRyeS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlbmFtZSAtIEV4YWN0IHBhdGguXG4gKiBAcGFyYW0ge3N0cmluZ30gY29kZSAtIERpYWdub3N0aWMgY29kZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBTdGFibGUgZmFpbHVyZSBkZXRhaWwuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQ29tcGxldGlvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gYXNzZXJ0QWJzZW50KGZpbGVuYW1lLCBjb2RlLCBwcm9qZWN0SWQsIG1lc3NhZ2UpIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBsc3RhdChmaWxlbmFtZSlcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoY29kZSwgcHJvamVjdElkLCBtZXNzYWdlKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChpc0Vycm9yQ29kZShlcnJvciwgXCJFTk9FTlRcIikpIHJldHVyblxuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlbWFudGlmb2xkRGlhZ25vc3RpYykgdGhyb3cgZXJyb3JcbiAgICBwdWJsaWNhdGlvbkZhaWx1cmUoY29kZSwgcHJvamVjdElkLCBtZXNzYWdlLCBlcnJvcilcbiAgfVxufVxuXG4vKipcbiAqIFJlc29sdmVzIGEgdmFsaWRhdGVkIHJlbGF0aXZlIHBhdGggdW5kZXIgYW4gb3duZWQgcm9vdCBhbmQgcmVqZWN0cyBlc2NhcGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gcm9vdCAtIE93bmVkIHJvb3QuXG4gKiBAcGFyYW0ge3N0cmluZ30gcmVsYXRpdmUgLSBWYWxpZGF0ZWQgcmVsYXRpdmUgUE9TSVggcGF0aC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHJldHVybnMge3N0cmluZ30gRXhhY3Qgb3duZWQgcGF0aC5cbiAqL1xuZnVuY3Rpb24gb3duZWRQYXRoKHJvb3QsIHJlbGF0aXZlLCBwcm9qZWN0SWQpIHtcbiAgY29uc3QgcmVzb2x2ZWQgPSBwYXRoLnJlc29sdmUocm9vdCwgLi4ucmVsYXRpdmUuc3BsaXQoXCIvXCIpKVxuICBjb25zdCBjaGlsZCA9IHBhdGgucmVsYXRpdmUocm9vdCwgcmVzb2x2ZWQpXG5cbiAgaWYgKGNoaWxkLmxlbmd0aCA9PSAwIHx8IGNoaWxkID09IFwiLi5cIiB8fCBjaGlsZC5zdGFydHNXaXRoKGAuLiR7cGF0aC5zZXB9YCkgfHwgcGF0aC5pc0Fic29sdXRlKGNoaWxkKSkge1xuICAgIHB1YmxpY2F0aW9uRmFpbHVyZShcIlBVQkxJQ0FUSU9OX1BBVEhfRVNDQVBFXCIsIHByb2plY3RJZCxcbiAgICAgIFwiUmVzb2x2ZWQgcHVibGljYXRpb24gcGF0aCBlc2NhcGVkIGl0cyBvd25lZCByb290LlwiKVxuICB9XG5cbiAgcmV0dXJuIHJlc29sdmVkXG59XG5cbi8qKlxuICogQ2hlY2tzIGEgY2Fub25pY2FsIG5vbi1yb290IGFic29sdXRlIHBhdGggd2l0aG91dCBmaWxlc3lzdGVtIGFjY2Vzcy5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBQYXRoIGNhbmRpZGF0ZS5cbiAqIEByZXR1cm5zIHt2YWx1ZSBpcyBzdHJpbmd9IFdoZXRoZXIgdGhlIHBhdGggaXMgY2Fub25pY2FsLlxuICovXG5mdW5jdGlvbiBpc0Nhbm9uaWNhbEFic29sdXRlUGF0aCh2YWx1ZSkge1xuICByZXR1cm4gaXNDYW5vbmljYWxBYnNvbHV0ZVBhdGhGb3JQYXRoKHZhbHVlLCBwYXRoKVxufVxuXG4vKipcbiAqIENoZWNrcyBvbmUgYWJzb2x1dGUgcGF0aCB1c2luZyB0aGUgc3VwcGxpZWQgaG9zdCBwYXRoIHNlbWFudGljcy5cbiAqIFRoaXMgaGVscGVyIGlzIGludGVudGlvbmFsbHkgbm90IHBhcnQgb2YgdGhlIHBhY2thZ2Ugcm9vdCBBUEkuXG4gKiBAcGFyYW0ge3Vua25vd259IHZhbHVlIC0gUGF0aCBjYW5kaWRhdGUuXG4gKiBAcGFyYW0ge3R5cGVvZiBwYXRoLnBvc2l4IHwgdHlwZW9mIHBhdGgud2luMzJ9IGhvc3RQYXRoIC0gSG9zdC1uYXRpdmUgcGF0aCBpbXBsZW1lbnRhdGlvbi5cbiAqIEByZXR1cm5zIHt2YWx1ZSBpcyBzdHJpbmd9IFdoZXRoZXIgdGhlIHBhdGggaXMgY2Fub25pY2FsIGZvciB0aGF0IGhvc3QuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0Nhbm9uaWNhbEFic29sdXRlUGF0aEZvclBhdGgodmFsdWUsIGhvc3RQYXRoKSB7XG4gIGNvbnN0IHNlcGFyYXRvckFsaWFzID0gaG9zdFBhdGguc2VwID09IFwiL1wiID8gXCJcXFxcXCIgOiBcIi9cIlxuXG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT0gXCJzdHJpbmdcIiAmJiB2YWx1ZS5sZW5ndGggPiAwICYmICF2YWx1ZS5pbmNsdWRlcyhcIlxcMFwiKSAmJiAhdmFsdWUuaW5jbHVkZXMoc2VwYXJhdG9yQWxpYXMpICYmXG4gICAgaG9zdFBhdGguaXNBYnNvbHV0ZSh2YWx1ZSkgJiYgdmFsdWUgIT0gaG9zdFBhdGgucGFyc2UodmFsdWUpLnJvb3QgJiYgaG9zdFBhdGgubm9ybWFsaXplKHZhbHVlKSA9PSB2YWx1ZSAmJlxuICAgIGhvc3RQYXRoLnJlc29sdmUodmFsdWUpID09IHZhbHVlXG59XG5cbi8qKlxuICogQ2hlY2tzIGNvbnNlcnZhdGl2ZSBwb3J0YWJsZSBvdmVybGFwIGJldHdlZW4gYWJzb2x1dGUgcGF0aHMuXG4gKiBAcGFyYW0ge3N0cmluZ30gbGVmdCAtIEZpcnN0IHBhdGguXG4gKiBAcGFyYW0ge3N0cmluZ30gcmlnaHQgLSBTZWNvbmQgcGF0aC5cbiAqIEByZXR1cm5zIHtib29sZWFufSBXaGV0aGVyIGVpdGhlciBjb250YWlucyB0aGUgb3RoZXIuXG4gKi9cbmZ1bmN0aW9uIHBhdGhzT3ZlcmxhcChsZWZ0LCByaWdodCkge1xuICBjb25zdCBsZWZ0S2V5ID0gbGVmdC50b0xvd2VyQ2FzZSgpXG4gIGNvbnN0IHJpZ2h0S2V5ID0gcmlnaHQudG9Mb3dlckNhc2UoKVxuXG4gIHJldHVybiBsZWZ0S2V5ID09IHJpZ2h0S2V5IHx8IGxlZnRLZXkuc3RhcnRzV2l0aChgJHtyaWdodEtleX0ke3BhdGguc2VwfWApIHx8IHJpZ2h0S2V5LnN0YXJ0c1dpdGgoYCR7bGVmdEtleX0ke3BhdGguc2VwfWApXG59XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgYSBwYXRoIGlzIGVxdWFsIHRvIG9yIG5lc3RlZCBiZW5lYXRoIG9uZSBjYW5vbmljYWwgcm9vdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSByb290IC0gQ2Fub25pY2FsIHJvb3QuXG4gKiBAcGFyYW0ge3N0cmluZ30gY2FuZGlkYXRlIC0gQ2Fub25pY2FsIGNhbmRpZGF0ZS5cbiAqIEByZXR1cm5zIHtib29sZWFufSBXaGV0aGVyIHRoZSBjYW5kaWRhdGUgaXMgd2l0aGluIHRoZSByb290LlxuICovXG5mdW5jdGlvbiBpc1BhdGhXaXRoaW4ocm9vdCwgY2FuZGlkYXRlKSB7XG4gIGNvbnN0IHJlbGF0aXZlID0gcGF0aC5yZWxhdGl2ZShyb290LCBjYW5kaWRhdGUpXG5cbiAgcmV0dXJuIHJlbGF0aXZlLmxlbmd0aCA9PSAwIHx8IChyZWxhdGl2ZSAhPSBcIi4uXCIgJiYgIXJlbGF0aXZlLnN0YXJ0c1dpdGgoYC4uJHtwYXRoLnNlcH1gKSAmJiAhcGF0aC5pc0Fic29sdXRlKHJlbGF0aXZlKSlcbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciBhIHBhdGggaXMgc3RyaWN0bHkgbmVzdGVkIGJlbmVhdGggb25lIGNhbm9uaWNhbCByb290LlxuICogQHBhcmFtIHtzdHJpbmd9IHJvb3QgLSBDYW5vbmljYWwgcm9vdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBjYW5kaWRhdGUgLSBDYW5vbmljYWwgY2FuZGlkYXRlLlxuICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIgdGhlIGNhbmRpZGF0ZSBpcyBhIHN0cmljdCBkZXNjZW5kYW50LlxuICovXG5mdW5jdGlvbiBpc1N0cmljdFBhdGhEZXNjZW5kYW50KHJvb3QsIGNhbmRpZGF0ZSkge1xuICByZXR1cm4gaXNQYXRoV2l0aGluKHJvb3QsIGNhbmRpZGF0ZSkgJiYgcGF0aC5yZWxhdGl2ZShyb290LCBjYW5kaWRhdGUpLmxlbmd0aCA+IDBcbn1cblxuLyoqXG4gKiBTZXJpYWxpemVzIHN0YWJsZSBnZW5lcmF0ZWQgSlNPTiB3aXRoIG9uZSB0cmFpbGluZyBMRi5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBKU09OIHZhbHVlLlxuICogQHJldHVybnMge0J1ZmZlcn0gVVRGLTggYnl0ZXMuXG4gKi9cbmZ1bmN0aW9uIHNlcmlhbGl6ZUpzb24odmFsdWUpIHtcbiAgcmV0dXJuIEJ1ZmZlci5mcm9tKGAke0pTT04uc3RyaW5naWZ5KHZhbHVlLCBudWxsLCAyKX1cXG5gLCBcInV0ZjhcIilcbn1cblxuLyoqXG4gKiBIYXNoZXMgZXhhY3QgYnl0ZXMgd2l0aCBTSEEtMjU2LlxuICogQHBhcmFtIHtVaW50OEFycmF5fSBieXRlcyAtIEV4YWN0IGJ5dGVzLlxuICogQHJldHVybnMge3N0cmluZ30gTG93ZXJjYXNlIGhleGFkZWNpbWFsIGhhc2guXG4gKi9cbmZ1bmN0aW9uIHNoYTI1NihieXRlcykge1xuICByZXR1cm4gY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoYnl0ZXMpLmRpZ2VzdChcImhleFwiKVxufVxuXG4vKipcbiAqIERlZXBseSBmcmVlemVzIGEgcGFyc2VkIEpTT04gdmFsdWUuXG4gKiBAdGVtcGxhdGUgVFxuICogQHBhcmFtIHtUfSB2YWx1ZSAtIEpTT04gdmFsdWUuXG4gKiBAcmV0dXJucyB7VH0gRnJvemVuIHZhbHVlLlxuICovXG5mdW5jdGlvbiBkZWVwRnJlZXplKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gXCJvYmplY3RcIiAmJiB2YWx1ZSAhPSBudWxsKSB7XG4gICAgZm9yIChjb25zdCBjaGlsZCBvZiBPYmplY3QudmFsdWVzKHZhbHVlKSkgZGVlcEZyZWV6ZShjaGlsZClcbiAgICBPYmplY3QuZnJlZXplKHZhbHVlKVxuICB9XG5cbiAgcmV0dXJuIHZhbHVlXG59XG5cbi8qKlxuICogQ29tcGFyZXMgZGV0YWNoZWQgSlNPTiB2YWx1ZXMgd2l0aG91dCBkZXBlbmRpbmcgb24gb2JqZWN0LWtleSBvcmRlci5cbiAqIEBwYXJhbSB7dW5rbm93bn0gbGVmdCAtIEZpcnN0IEpTT04gdmFsdWUuXG4gKiBAcGFyYW0ge3Vua25vd259IHJpZ2h0IC0gU2Vjb25kIEpTT04gdmFsdWUuXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gV2hldGhlciB0aGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmFsbHkgZXF1YWwuXG4gKi9cbmZ1bmN0aW9uIHNhbWVKc29uVmFsdWUobGVmdCwgcmlnaHQpIHtcbiAgaWYgKGxlZnQgPT09IHJpZ2h0KSByZXR1cm4gdHJ1ZVxuICBpZiAoQXJyYXkuaXNBcnJheShsZWZ0KSB8fCBBcnJheS5pc0FycmF5KHJpZ2h0KSkge1xuICAgIHJldHVybiBBcnJheS5pc0FycmF5KGxlZnQpICYmIEFycmF5LmlzQXJyYXkocmlnaHQpICYmIGxlZnQubGVuZ3RoID09IHJpZ2h0Lmxlbmd0aCAmJlxuICAgICAgbGVmdC5ldmVyeSgoZW50cnksIGluZGV4KSA9PiBzYW1lSnNvblZhbHVlKGVudHJ5LCByaWdodFtpbmRleF0pKVxuICB9XG4gIGlmICghaXNQbGFpbk9iamVjdChsZWZ0KSB8fCAhaXNQbGFpbk9iamVjdChyaWdodCkpIHJldHVybiBmYWxzZVxuICBjb25zdCBsZWZ0S2V5cyA9IE9iamVjdC5rZXlzKGxlZnQpXG4gIGNvbnN0IHJpZ2h0S2V5cyA9IE9iamVjdC5rZXlzKHJpZ2h0KVxuXG4gIHJldHVybiBsZWZ0S2V5cy5sZW5ndGggPT0gcmlnaHRLZXlzLmxlbmd0aCAmJlxuICAgIGxlZnRLZXlzLmV2ZXJ5KGtleSA9PiBPYmplY3QuaGFzT3duKHJpZ2h0LCBrZXkpICYmIHNhbWVKc29uVmFsdWUobGVmdFtrZXldLCByaWdodFtrZXldKSlcbn1cblxuLyoqXG4gKiBDaGVja3MgYW4gYWN0aXZlLXBvaW50ZXIgdmFsdWUuXG4gKiBAcGFyYW0ge3Vua25vd259IHZhbHVlIC0gUGFyc2VkIHZhbHVlLlxuICogQHJldHVybnMge3ZhbHVlIGlzIEFjdGl2ZVBvaW50ZXJ9IFdoZXRoZXIgdGhlIHZhbHVlIGlzIHN0cmljdCBhbmQgdmVyc2lvbmVkLlxuICovXG5mdW5jdGlvbiBpc0FjdGl2ZVBvaW50ZXIodmFsdWUpIHtcbiAgcmV0dXJuIGlzUGxhaW5PYmplY3QodmFsdWUpICYmIGhhc0V4YWN0S2V5cyh2YWx1ZSwgW1wiZ2VuZXJhdGlvbklkXCIsIFwibWFuaWZlc3RIYXNoXCIsIFwicHJvamVjdElkXCIsIFwic2NoZW1hXCIsIFwidmVyc2lvblwiXSkgJiZcbiAgICB2YWx1ZS5zY2hlbWEgPT0gXCJTZW1hbnRpZm9sZEFjdGl2ZUdlbmVyYXRpb25cIiAmJiB2YWx1ZS52ZXJzaW9uID09IDEgJiZcbiAgICB0eXBlb2YgdmFsdWUucHJvamVjdElkID09IFwic3RyaW5nXCIgJiYgaWRlbnRpdHlQYXR0ZXJuLnRlc3QodmFsdWUucHJvamVjdElkKSAmJlxuICAgIHR5cGVvZiB2YWx1ZS5nZW5lcmF0aW9uSWQgPT0gXCJzdHJpbmdcIiAmJiBnZW5lcmF0aW9uUGF0dGVybi50ZXN0KHZhbHVlLmdlbmVyYXRpb25JZCkgJiZcbiAgICB0eXBlb2YgdmFsdWUubWFuaWZlc3RIYXNoID09IFwic3RyaW5nXCIgJiYgL15bYS1mMC05XXs2NH0kL3UudGVzdCh2YWx1ZS5tYW5pZmVzdEhhc2gpXG59XG5cbi8qKlxuICogQ2hlY2tzIGEgcmVjb3Zlcnktam91cm5hbCB2YWx1ZS5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBQYXJzZWQgdmFsdWUuXG4gKiBAcmV0dXJucyB7dmFsdWUgaXMgUHVibGljYXRpb25Kb3VybmFsfSBXaGV0aGVyIHRoZSB2YWx1ZSBpcyBzdHJpY3QgYW5kIHZlcnNpb25lZC5cbiAqL1xuZnVuY3Rpb24gaXNQdWJsaWNhdGlvbkpvdXJuYWwodmFsdWUpIHtcbiAgaWYgKCFpc1BsYWluT2JqZWN0KHZhbHVlKSB8fCB2YWx1ZS5zY2hlbWEgIT0gXCJTZW1hbnRpZm9sZFB1YmxpY2F0aW9uSm91cm5hbFwiIHx8XG4gICAgdHlwZW9mIHZhbHVlLnByb2plY3RJZCAhPSBcInN0cmluZ1wiIHx8ICFpZGVudGl0eVBhdHRlcm4udGVzdCh2YWx1ZS5wcm9qZWN0SWQpIHx8XG4gICAgdHlwZW9mIHZhbHVlLmdlbmVyYXRpb25JZCAhPSBcInN0cmluZ1wiIHx8ICFnZW5lcmF0aW9uUGF0dGVybi50ZXN0KHZhbHVlLmdlbmVyYXRpb25JZCkgfHxcbiAgICB2YWx1ZS50ZW1wUG9pbnRlciAhPSBgLmFjdGl2ZS1nZW5lcmF0aW9uLiR7dmFsdWUuZ2VuZXJhdGlvbklkfS50bXBgKSByZXR1cm4gZmFsc2VcblxuICByZXR1cm4gdmFsdWUudmVyc2lvbiA9PSAxICYmIGhhc0V4YWN0S2V5cyh2YWx1ZSwgW1wiZ2VuZXJhdGlvbklkXCIsIFwicHJvamVjdElkXCIsIFwic2NoZW1hXCIsIFwidGVtcFBvaW50ZXJcIiwgXCJ2ZXJzaW9uXCJdKSB8fFxuICAgIHZhbHVlLnZlcnNpb24gPT0gMiAmJiB2YWx1ZS5vcGVyYXRpb24gPT0gXCJyZWFjdGl2YXRlXCIgJiZcbiAgICAgIGhhc0V4YWN0S2V5cyh2YWx1ZSwgW1wiZ2VuZXJhdGlvbklkXCIsIFwib3BlcmF0aW9uXCIsIFwicHJvamVjdElkXCIsIFwic2NoZW1hXCIsIFwidGVtcFBvaW50ZXJcIiwgXCJ2ZXJzaW9uXCJdKVxufVxuXG4vKipcbiAqIENoZWNrcyBhbiBvcmRpbmFyeSBvYmplY3Qgd2l0aCBleGFjdGx5IHRoZSBleHBlY3RlZCBlbnVtZXJhYmxlIHN0cmluZyBrZXlzLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gdmFsdWUgLSBPYmplY3QgY2FuZGlkYXRlLlxuICogQHBhcmFtIHtyZWFkb25seSBzdHJpbmdbXX0ga2V5cyAtIFNvcnRlZCBleHBlY3RlZCBrZXlzLlxuICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIga2V5cyBtYXRjaCBleGFjdGx5LlxuICovXG5mdW5jdGlvbiBoYXNFeGFjdEtleXModmFsdWUsIGtleXMpIHtcbiAgY29uc3QgYWN0dWFsID0gT2JqZWN0LmtleXModmFsdWUpLnNvcnQoY29tcGFyZVN0cmluZ3MpXG5cbiAgcmV0dXJuIGFjdHVhbC5sZW5ndGggPT0ga2V5cy5sZW5ndGggJiYgYWN0dWFsLmV2ZXJ5KChrZXksIGluZGV4KSA9PiBrZXkgPT0ga2V5c1tpbmRleF0pXG59XG5cbi8qKlxuICogQ2hlY2tzIGZvciBhbiBvcmRpbmFyeSBzdHJpbmcta2V5ZWQgb2JqZWN0LlxuICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIENhbmRpZGF0ZSB2YWx1ZS5cbiAqIEByZXR1cm5zIHt2YWx1ZSBpcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gV2hldGhlciB0aGUgdmFsdWUgaXMgcGxhaW4uXG4gKi9cbmZ1bmN0aW9uIGlzUGxhaW5PYmplY3QodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSBcIm9iamVjdFwiICYmIHZhbHVlICE9IG51bGwgJiYgIUFycmF5LmlzQXJyYXkodmFsdWUpICYmIE9iamVjdC5nZXRQcm90b3R5cGVPZih2YWx1ZSkgPT0gT2JqZWN0LnByb3RvdHlwZVxufVxuXG4vKipcbiAqIENoZWNrcyBmb3IgYSBkZW5zZSBvcmRpbmFyeSBhcnJheS5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBDYW5kaWRhdGUgdmFsdWUuXG4gKiBAcmV0dXJucyB7dmFsdWUgaXMgdW5rbm93bltdfSBXaGV0aGVyIHRoZSB2YWx1ZSBpcyBkZW5zZS5cbiAqL1xuZnVuY3Rpb24gaXNEZW5zZUFycmF5KHZhbHVlKSB7XG4gIGlmICghQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHJldHVybiBmYWxzZVxuICBjb25zdCBpbmRleGVkS2V5cyA9IE9iamVjdC5rZXlzKHZhbHVlKS5maWx0ZXIoa2V5ID0+IC9eKD86MHxbMS05XVswLTldKikkL3UudGVzdChrZXkpKVxuXG4gIHJldHVybiBpbmRleGVkS2V5cy5sZW5ndGggPT0gdmFsdWUubGVuZ3RoXG59XG5cbi8qKlxuICogQ29tcGFyZXMgc3RyaW5ncyBieSBVbmljb2RlIGNvZGUgdW5pdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBsZWZ0IC0gRmlyc3Qgc3RyaW5nLlxuICogQHBhcmFtIHtzdHJpbmd9IHJpZ2h0IC0gU2Vjb25kIHN0cmluZy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IE9yZGVyaW5nLlxuICovXG5mdW5jdGlvbiBjb21wYXJlU3RyaW5ncyhsZWZ0LCByaWdodCkge1xuICByZXR1cm4gbGVmdCA8IHJpZ2h0ID8gLTEgOiBsZWZ0ID4gcmlnaHQgPyAxIDogMFxufVxuXG4vKipcbiAqIENoZWNrcyBhIG5hcnJvd2VkIGZpbGVzeXN0ZW0gZXJyb3IgY29kZS5cbiAqIEBwYXJhbSB7dW5rbm93bn0gZXJyb3IgLSBPcGFxdWUgZXJyb3IuXG4gKiBAcGFyYW0ge3N0cmluZ30gY29kZSAtIEV4cGVjdGVkIGNvZGUuXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gV2hldGhlciB0aGUgY29kZSBtYXRjaGVzLlxuICovXG5mdW5jdGlvbiBpc0Vycm9yQ29kZShlcnJvciwgY29kZSkge1xuICByZXR1cm4gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciAmJiBcImNvZGVcIiBpbiBlcnJvciAmJiBlcnJvci5jb2RlID09IGNvZGVcbn1cblxuLyoqXG4gKiBUaHJvd3Mgb25lIHN0YWJsZSBwdWJsaWNhdGlvbiBkaWFnbm9zdGljIHdpdGhvdXQgZW1iZWRkaW5nIGhvc3QgcGF0aHMgb3IgcmF3IGZpbGVzeXN0ZW0gbWVzc2FnZXMuXG4gKiBAcGFyYW0ge3N0cmluZ30gY29kZSAtIFN0YWJsZSBkaWFnbm9zdGljIGNvZGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBpZGVudGl0eS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gU3RhYmxlIGRldGFpbC5cbiAqIEBwYXJhbSB7dW5rbm93bn0gW2Vycm9yXSAtIFByZXNlcnZlZCBjYXVzZS5cbiAqIEByZXR1cm5zIHtuZXZlcn0gQWx3YXlzIHRocm93cy5cbiAqL1xuZnVuY3Rpb24gcHVibGljYXRpb25GYWlsdXJlKGNvZGUsIHByb2plY3RJZCwgbWVzc2FnZSwgZXJyb3IpIHtcbiAgdGhyb3cgbmV3IFNlbWFudGlmb2xkRGlhZ25vc3RpYyh7XG4gICAgY2F1c2U6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvciA6IHVuZGVmaW5lZCxcbiAgICBjb2RlLFxuICAgIGxhbmd1YWdlOiBwcm9qZWN0SWQsXG4gICAgbWVzc2FnZVxuICB9KVxufVxuXG4vKipcbiAqIEB0eXBlZGVmIFZhbGlkYXRlZFB1YmxpY2F0aW9uVGFyZ2V0XG4gKiBAcHJvcGVydHkge3N0cmluZ30gaWQgLSBUYXJnZXQgaWRlbnRpdHkuXG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuR2VuZXJhdGVkQXJ0aWZhY3RTZXR9IGFydGlmYWN0U2V0IC0gRGV0YWNoZWQgYXJ0aWZhY3Qgc2V0LlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHNvdXJjZVByb2plY3Rpb24gLSBTb3VyY2UgcHJvamVjdGlvbi5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBidWlsZFByb2plY3Rpb24gLSBCdWlsZCBwcm9qZWN0aW9uLlxuICogQHByb3BlcnR5IHtpbXBvcnQoXCIuL3NlbWFudGljL3R5cGVzLmpzXCIpLlB1YmxpY2F0aW9uVGFyZ2V0Um9sZX0gcm9sZSAtIExhbmd1YWdlLW5ldXRyYWwgdGFyZ2V0IHJvbGUuXG4gKiBAcHJvcGVydHkge3JlYWRvbmx5IGltcG9ydChcIi4vc2VtYW50aWMvdHlwZXMuanNcIikuUHVibGljYXRpb25WYWxpZGF0b3JbXX0gdmFsaWRhdG9ycyAtIE9yZGVyZWQgdmFsaWRhdG9ycy5cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIFZhbGlkYXRlZFB1YmxpY2F0aW9uUmVxdWVzdFxuICogQHByb3BlcnR5IHtzdHJpbmd9IGdlbmVyYXRpb25JZCAtIFNuYXBzaG90dGVkIGdlbmVyYXRpb24gaWRlbnRpdHkuXG4gKiBAcHJvcGVydHkge3JlYWRvbmx5IFZhbGlkYXRlZFB1YmxpY2F0aW9uVGFyZ2V0W119IHRhcmdldHMgLSBTbmFwc2hvdHRlZCBvcmRlcmVkIHRhcmdldHMuXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiBBY3RpdmVQb2ludGVyXG4gKiBAcHJvcGVydHkge1wiU2VtYW50aWZvbGRBY3RpdmVHZW5lcmF0aW9uXCJ9IHNjaGVtYSAtIFNjaGVtYSBkaXNjcmltaW5hdG9yLlxuICogQHByb3BlcnR5IHsxfSB2ZXJzaW9uIC0gU2NoZW1hIHZlcnNpb24uXG4gKiBAcHJvcGVydHkge3N0cmluZ30gcHJvamVjdElkIC0gUHJvamVjdCBpZGVudGl0eS5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBnZW5lcmF0aW9uSWQgLSBHZW5lcmF0aW9uIGlkZW50aXR5LlxuICogQHByb3BlcnR5IHtzdHJpbmd9IG1hbmlmZXN0SGFzaCAtIFNIQS0yNTYgZ2VuZXJhdGlvbi1tYW5pZmVzdCBoYXNoLlxuICovXG5cbi8qKlxuICogQHR5cGVkZWYgQ2FuZGlkYXRlUHVibGljYXRpb25Kb3VybmFsXG4gKiBAcHJvcGVydHkge1wiU2VtYW50aWZvbGRQdWJsaWNhdGlvbkpvdXJuYWxcIn0gc2NoZW1hIC0gU2NoZW1hIGRpc2NyaW1pbmF0b3IuXG4gKiBAcHJvcGVydHkgezF9IHZlcnNpb24gLSBTY2hlbWEgdmVyc2lvbi5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBwcm9qZWN0SWQgLSBQcm9qZWN0IGlkZW50aXR5LlxuICogQHByb3BlcnR5IHtzdHJpbmd9IGdlbmVyYXRpb25JZCAtIENhbmRpZGF0ZSBnZW5lcmF0aW9uIGlkZW50aXR5LlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHRlbXBQb2ludGVyIC0gT3duZWQgc2libGluZyB0ZW1wb3JhcnktcG9pbnRlciBmaWxlbmFtZS5cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIFJlYWN0aXZhdGlvblB1YmxpY2F0aW9uSm91cm5hbFxuICogQHByb3BlcnR5IHtcIlNlbWFudGlmb2xkUHVibGljYXRpb25Kb3VybmFsXCJ9IHNjaGVtYSAtIFNjaGVtYSBkaXNjcmltaW5hdG9yLlxuICogQHByb3BlcnR5IHsyfSB2ZXJzaW9uIC0gU2NoZW1hIHZlcnNpb24uXG4gKiBAcHJvcGVydHkge1wicmVhY3RpdmF0ZVwifSBvcGVyYXRpb24gLSBSZXRhaW5lZC1nZW5lcmF0aW9uIGFjdGl2YXRpb24gd2l0aG91dCBjYW5kaWRhdGUgb3duZXJzaGlwLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IHByb2plY3RJZCAtIFByb2plY3QgaWRlbnRpdHkuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gZ2VuZXJhdGlvbklkIC0gUmV0YWluZWQgZ2VuZXJhdGlvbiBpZGVudGl0eS5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB0ZW1wUG9pbnRlciAtIE93bmVkIHNpYmxpbmcgdGVtcG9yYXJ5LXBvaW50ZXIgZmlsZW5hbWUuXG4gKi9cblxuLyoqIEB0eXBlZGVmIHtDYW5kaWRhdGVQdWJsaWNhdGlvbkpvdXJuYWwgfCBSZWFjdGl2YXRpb25QdWJsaWNhdGlvbkpvdXJuYWx9IFB1YmxpY2F0aW9uSm91cm5hbCAqL1xuIl19
|