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