@decohere/flux 0.0.1-dev.13 → 0.0.1-dev.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +273 -0
- package/commonjs/src/deferred-script-runner.js +56 -0
- package/commonjs/src/flux.command.js +3 -3
- package/commonjs/src/flux.js +56 -0
- package/commonjs/src/flux.module.js +9 -8
- package/commonjs/src/hot-reload-server.js +307 -219
- package/commonjs/src/index.js +34 -2
- package/commonjs/src/rewrite-serve-specifier.js +61 -0
- package/commonjs/src/run.action.js +47 -0
- package/commonjs/src/run.command.js +65 -0
- package/commonjs/src/serve.action.js +78 -7
- package/commonjs/src/serve.command.js +35 -4
- package/commonjs/src/slice-serve.js +130 -0
- package/commonjs/src/source-module-loader.js +388 -0
- package/commonjs/src/static-imports.js +37 -0
- package/commonjs/src/transpile-disk-cache.js +148 -0
- package/es6/src/deferred-script-runner.js +46 -0
- package/es6/src/flux.command.js +3 -3
- package/es6/src/flux.js +41 -0
- package/es6/src/flux.module.js +9 -8
- package/es6/src/hot-reload-server.js +287 -175
- package/es6/src/index.js +5 -3
- package/es6/src/rewrite-serve-specifier.js +51 -0
- package/es6/src/run.action.js +37 -0
- package/es6/src/run.command.js +55 -0
- package/es6/src/serve.action.js +73 -9
- package/es6/src/serve.command.js +35 -4
- package/es6/src/slice-serve.js +105 -0
- package/es6/src/source-module-loader.js +332 -0
- package/es6/src/static-imports.js +27 -0
- package/es6/src/transpile-disk-cache.js +142 -0
- package/package.json +17 -6
- package/types/src/deferred-script-runner.d.ts +18 -0
- package/types/src/flux.command.d.ts +2 -2
- package/types/src/flux.d.ts +43 -0
- package/types/src/flux.module.d.ts +6 -4
- package/types/src/hot-reload-server.d.ts +57 -23
- package/types/src/index.d.ts +8 -1
- package/types/src/rewrite-serve-specifier.d.ts +14 -0
- package/types/src/run.action.d.ts +15 -0
- package/types/src/run.command.d.ts +11 -0
- package/types/src/serve.action.d.ts +13 -3
- package/types/src/serve.command.d.ts +7 -1
- package/types/src/slice-serve.d.ts +52 -0
- package/types/src/source-module-loader.d.ts +104 -0
- package/types/src/static-imports.d.ts +5 -0
- package/types/src/transpile-disk-cache.d.ts +38 -0
|
@@ -1,149 +1,244 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
-
import { registerHooks } from "node:module";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { debounce } from "@decohere/nexus/system";
|
|
3
|
+
import { io } from "@decohere/core/io";
|
|
4
|
+
import { WatchEvents } from "@decohere/devkit/io";
|
|
5
|
+
import { debounce } from "@decohere/core/system";
|
|
8
6
|
import { scribe } from "@decohere/scribe";
|
|
7
|
+
import { SourceModuleLoader } from "./source-module-loader.js";
|
|
9
8
|
const currentFilePath = io.fileUrlToPath(import.meta.url);
|
|
10
9
|
const VERSION_URL_PARAM = "version";
|
|
11
|
-
const
|
|
10
|
+
/** Default quiet window before a file-change reboot. */ export const DEFAULT_REBOOT_DEBOUNCE_MS = 250;
|
|
11
|
+
/** How many triggering paths to print on the info banner before `+N`. */ const MAX_REBOOT_TRIGGER_PATHS = 4;
|
|
12
|
+
export function resolveRebootDebounceMs(value) {
|
|
13
|
+
const delay = value ?? DEFAULT_REBOOT_DEBOUNCE_MS;
|
|
14
|
+
if (!Number.isFinite(delay) || delay < 0) {
|
|
15
|
+
throw new Error(`rebootDebounceMs must be a non-negative number, got ${String(value)}`);
|
|
16
|
+
}
|
|
17
|
+
return delay;
|
|
18
|
+
}
|
|
19
|
+
function formatTriggerList(triggers) {
|
|
20
|
+
if (triggers.length === 0) return undefined;
|
|
21
|
+
if (triggers.length <= MAX_REBOOT_TRIGGER_PATHS) return triggers.join(", ");
|
|
22
|
+
return `${triggers.slice(0, MAX_REBOOT_TRIGGER_PATHS).join(", ")}, +${triggers.length - MAX_REBOOT_TRIGGER_PATHS}`;
|
|
23
|
+
}
|
|
24
|
+
const BANNER_RULE = "═".repeat(56);
|
|
25
|
+
function wrapFluxBanner(title, details) {
|
|
26
|
+
const body = details.map((line)=>` ${line}`).join("\n");
|
|
27
|
+
return `\n${BANNER_RULE}\n ${title}\n${body}\n${BANNER_RULE}\n`;
|
|
28
|
+
}
|
|
29
|
+
/** Info-level HMR start block — keep `flux reboot` in the text so operators can search the buffer. */ export function formatFluxRebootStart(labels, triggers = []) {
|
|
30
|
+
const who = labels.join(", ") || "entry";
|
|
31
|
+
const files = formatTriggerList(triggers);
|
|
32
|
+
const details = files == null ? [
|
|
33
|
+
who
|
|
34
|
+
] : [
|
|
35
|
+
who,
|
|
36
|
+
files
|
|
37
|
+
];
|
|
38
|
+
return wrapFluxBanner("FLUX REBOOT", details);
|
|
39
|
+
}
|
|
40
|
+
/** Info-level HMR finish block. */ export function formatFluxRebootReady(labels, elapsedMs) {
|
|
41
|
+
const who = labels.join(", ") || "entry";
|
|
42
|
+
return wrapFluxBanner("FLUX READY", [
|
|
43
|
+
who,
|
|
44
|
+
`${Math.max(0, Math.round(elapsedMs))}ms`
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
/** Process-wide: Node's ESM cache is process-global, including across HotReloadServer instances. */ let moduleVersionSeq = 0;
|
|
12
48
|
export class HotReloadServer extends EventEmitter {
|
|
13
49
|
// ── State ────────────────────────────────────────────────────────────────
|
|
14
50
|
logger = scribe.logger(HotReloadServer.name);
|
|
51
|
+
loader = new SourceModuleLoader();
|
|
15
52
|
disposed = false;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
workspace;
|
|
53
|
+
/** Absolute entry paths currently served (one or more Flux apps). */ entries = new Map();
|
|
54
|
+
pendingEntryReboots = new Set();
|
|
55
|
+
/** Source paths that scheduled the next reboot (cleared when that reboot starts). */ pendingTriggers = new Set();
|
|
20
56
|
watchSubscription;
|
|
21
|
-
/** Map from absolute .ts source file path to project info */ fileInfoMap = new Map();
|
|
22
57
|
moduleCache = new Map();
|
|
23
58
|
dependencies = new Map();
|
|
24
|
-
|
|
25
|
-
|
|
59
|
+
rebootTail = Promise.resolve();
|
|
60
|
+
rebootDebounceGen = 0;
|
|
61
|
+
rebootInFlight = false;
|
|
62
|
+
rebootFollowUp = false;
|
|
63
|
+
rebootQueued = false;
|
|
64
|
+
/** Modules whose reverse-graph edges are pruned at reboot, not on schedule. */ pendingGraphPrune = new Set();
|
|
65
|
+
scheduleReboot = ()=>{};
|
|
26
66
|
// ── Public API ───────────────────────────────────────────────────────────
|
|
27
67
|
async start(options) {
|
|
28
68
|
this.disposed = false;
|
|
29
69
|
this.dependencies.clear();
|
|
30
70
|
this.moduleCache.clear();
|
|
31
|
-
this.
|
|
71
|
+
this.entries.clear();
|
|
72
|
+
this.pendingEntryReboots.clear();
|
|
73
|
+
this.pendingTriggers.clear();
|
|
74
|
+
this.pendingGraphPrune.clear();
|
|
75
|
+
this.rebootTail = Promise.resolve();
|
|
76
|
+
this.rebootInFlight = false;
|
|
77
|
+
this.rebootFollowUp = false;
|
|
78
|
+
this.rebootQueued = false;
|
|
79
|
+
this.bindScheduleReboot(resolveRebootDebounceMs(options.rebootDebounceMs));
|
|
32
80
|
const { buildConfig } = options;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
81
|
+
await this.loader.configure({
|
|
82
|
+
buildConfig,
|
|
83
|
+
workspace: options.workspace,
|
|
84
|
+
versionFileUrl: (fileUrl)=>this.getCachedOrNew(fileUrl),
|
|
85
|
+
onResolved: this.onPostResolve
|
|
86
|
+
});
|
|
87
|
+
const workspace = this.requireWorkspace();
|
|
88
|
+
const selected = this.resolveProjects(buildConfig, options);
|
|
89
|
+
for (const project of selected){
|
|
90
|
+
const entryPath = path.resolve(project.getSourceEntryPath());
|
|
91
|
+
this.entries.set(entryPath, {
|
|
92
|
+
path: entryPath,
|
|
93
|
+
label: project.name
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
this.loader.registerHooks();
|
|
38
97
|
this.invalidateAllSourceFiles();
|
|
39
|
-
await
|
|
40
|
-
this.watchSubscription =
|
|
41
|
-
this.
|
|
42
|
-
|
|
98
|
+
await workspace.watch(buildConfig);
|
|
99
|
+
this.watchSubscription = workspace.projectFileChanged$.subscribe(this.onFileChange);
|
|
100
|
+
for (const entry of this.entries.values()){
|
|
101
|
+
this.logger.trace(`Serving ${entry.path}`);
|
|
102
|
+
}
|
|
103
|
+
await this.bootEntries([
|
|
104
|
+
...this.entries.keys()
|
|
105
|
+
]);
|
|
43
106
|
this.emit("start");
|
|
44
107
|
}
|
|
45
108
|
async dispose() {
|
|
46
109
|
if (this.disposed) return;
|
|
47
110
|
this.disposed = true;
|
|
111
|
+
this.rebootDebounceGen += 1;
|
|
48
112
|
this.watchSubscription?.unsubscribe();
|
|
49
|
-
this.
|
|
50
|
-
await this.
|
|
113
|
+
await this.rebootTail;
|
|
114
|
+
await this.disposeEntries([
|
|
115
|
+
...this.entries.keys()
|
|
116
|
+
]);
|
|
117
|
+
this.loader.dispose();
|
|
51
118
|
}
|
|
52
119
|
async [Symbol.asyncDispose]() {
|
|
53
120
|
await this.dispose();
|
|
54
121
|
}
|
|
55
122
|
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
56
|
-
async
|
|
123
|
+
async bootEntries(entryPaths) {
|
|
124
|
+
await Promise.all(entryPaths.map((entryPath)=>this.bootEntry(entryPath)));
|
|
125
|
+
}
|
|
126
|
+
async bootEntry(entryPath) {
|
|
127
|
+
const state = this.entries.get(entryPath);
|
|
128
|
+
if (state == null) return;
|
|
57
129
|
try {
|
|
58
|
-
const
|
|
59
|
-
|
|
130
|
+
const mod = await this.loader.importEntry(entryPath);
|
|
131
|
+
state.module = mod?.default;
|
|
132
|
+
state.failed = false;
|
|
60
133
|
} catch (error) {
|
|
134
|
+
state.module = undefined;
|
|
135
|
+
state.failed = true;
|
|
61
136
|
this.logger.error("There was an error booting the entry point. Your application may not function as expected", error);
|
|
62
137
|
}
|
|
63
138
|
}
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
139
|
+
bindScheduleReboot(delayMs) {
|
|
140
|
+
const gen = ++this.rebootDebounceGen;
|
|
141
|
+
this.scheduleReboot = debounce(async ()=>{
|
|
142
|
+
if (this.disposed || gen !== this.rebootDebounceGen) return;
|
|
143
|
+
this.requestReboot();
|
|
144
|
+
}, delayMs);
|
|
69
145
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
146
|
+
requestReboot() {
|
|
147
|
+
if (this.disposed) return;
|
|
148
|
+
if (this.rebootInFlight) {
|
|
149
|
+
this.rebootFollowUp = true;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (this.rebootQueued) return;
|
|
153
|
+
this.rebootQueued = true;
|
|
154
|
+
this.rebootTail = this.rebootTail.then(()=>this.runReboot(), ()=>this.runReboot());
|
|
80
155
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
156
|
+
async runReboot() {
|
|
157
|
+
this.rebootQueued = false;
|
|
158
|
+
if (this.disposed) return;
|
|
159
|
+
this.rebootInFlight = true;
|
|
160
|
+
try {
|
|
161
|
+
do {
|
|
162
|
+
this.rebootFollowUp = false;
|
|
163
|
+
if (this.disposed) return;
|
|
164
|
+
const toReboot = this.pendingEntryReboots.size > 0 ? [
|
|
165
|
+
...this.pendingEntryReboots
|
|
166
|
+
] : [
|
|
167
|
+
...this.entries.keys()
|
|
168
|
+
];
|
|
169
|
+
this.pendingEntryReboots.clear();
|
|
170
|
+
// Drop reverse edges only when rebooting so later saves in the debounce
|
|
171
|
+
// window can still walk the graph and reset the quiet timer.
|
|
172
|
+
this.prunePendingDependencyEdges();
|
|
173
|
+
const startedAt = Date.now();
|
|
174
|
+
const labels = this.labelsFor(toReboot);
|
|
175
|
+
const triggers = this.takeTriggers();
|
|
176
|
+
this.logger.info(formatFluxRebootStart(labels, triggers));
|
|
177
|
+
await this.disposeEntries(toReboot);
|
|
178
|
+
await this.bootEntries(toReboot);
|
|
179
|
+
this.logger.info(formatFluxRebootReady(labels, Date.now() - startedAt));
|
|
180
|
+
this.emit("reboot");
|
|
181
|
+
}while (this.rebootFollowUp && !this.disposed)
|
|
182
|
+
} finally{
|
|
183
|
+
const needsFollowUp = this.rebootFollowUp && !this.disposed;
|
|
184
|
+
this.rebootFollowUp = false;
|
|
185
|
+
this.rebootInFlight = false;
|
|
186
|
+
if (needsFollowUp) this.requestReboot();
|
|
86
187
|
}
|
|
87
|
-
return found;
|
|
88
188
|
}
|
|
89
|
-
async
|
|
90
|
-
|
|
91
|
-
const entries = await Promise.all(projects.map(async (project)=>{
|
|
92
|
-
const files = await project.getAllFiles();
|
|
93
|
-
return files.map((file)=>[
|
|
94
|
-
file.fullPath,
|
|
95
|
-
{
|
|
96
|
-
project
|
|
97
|
-
}
|
|
98
|
-
]);
|
|
99
|
-
}));
|
|
100
|
-
return new Map(entries.flat());
|
|
189
|
+
async disposeEntries(entryPaths) {
|
|
190
|
+
await Promise.all(entryPaths.map((entryPath)=>this.disposeEntry(entryPath)));
|
|
101
191
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (result != null) return {
|
|
118
|
-
format: "module",
|
|
119
|
-
source: result.source,
|
|
120
|
-
shortCircuit: true
|
|
121
|
-
};
|
|
122
|
-
return next(url, context);
|
|
192
|
+
async disposeEntry(entryPath) {
|
|
193
|
+
const paths = entryPath != null ? [
|
|
194
|
+
entryPath
|
|
195
|
+
] : [
|
|
196
|
+
...this.entries.keys()
|
|
197
|
+
];
|
|
198
|
+
for (const p of paths){
|
|
199
|
+
const state = this.entries.get(p);
|
|
200
|
+
if (state == null) continue;
|
|
201
|
+
const entry = state.module;
|
|
202
|
+
state.module = undefined;
|
|
203
|
+
try {
|
|
204
|
+
await entry?.[Symbol.asyncDispose]();
|
|
205
|
+
} catch (error) {
|
|
206
|
+
this.logger.error("Entry asyncDispose failed", error);
|
|
123
207
|
}
|
|
124
|
-
}
|
|
208
|
+
}
|
|
125
209
|
}
|
|
126
|
-
// ──
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
*/ rewriteJsToTs = (specifier, parentUrl)=>{
|
|
132
|
-
if (!specifier.endsWith(".js")) return null;
|
|
133
|
-
const parentClean = parentUrl.split("?")[0];
|
|
134
|
-
if (!parentClean) return null;
|
|
135
|
-
let jsPath;
|
|
136
|
-
try {
|
|
137
|
-
const resolved = new URL(specifier, parentClean);
|
|
138
|
-
jsPath = io.fileUrlToPath(resolved.toString());
|
|
139
|
-
} catch {
|
|
140
|
-
return null;
|
|
210
|
+
// ── Setup ────────────────────────────────────────────────────────────────
|
|
211
|
+
requireWorkspace() {
|
|
212
|
+
const workspace = this.loader.getWorkspace();
|
|
213
|
+
if (workspace == null) {
|
|
214
|
+
throw new Error("SourceModuleLoader has no workspace after configure");
|
|
141
215
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
216
|
+
return workspace;
|
|
217
|
+
}
|
|
218
|
+
resolveProjects(buildConfig, options) {
|
|
219
|
+
const projects = this.loader.getWorkspace()?.getProjects(buildConfig) ?? [];
|
|
220
|
+
const names = options.projects != null && options.projects.length > 0 ? options.projects : options.project != null ? [
|
|
221
|
+
options.project
|
|
222
|
+
] : undefined;
|
|
223
|
+
if (names == null) {
|
|
224
|
+
const first = projects.at(0);
|
|
225
|
+
if (first == null) {
|
|
226
|
+
throw new Error(`No projects found for configuration: ${buildConfig}`);
|
|
227
|
+
}
|
|
228
|
+
return [
|
|
229
|
+
first
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
const selected = [];
|
|
233
|
+
for (const name of names){
|
|
234
|
+
const found = projects.find((p)=>p.name === name);
|
|
235
|
+
if (found == null) {
|
|
236
|
+
throw new Error(`Project "${name}" not found in workspace`);
|
|
237
|
+
}
|
|
238
|
+
selected.push(found);
|
|
239
|
+
}
|
|
240
|
+
return selected;
|
|
241
|
+
}
|
|
147
242
|
/**
|
|
148
243
|
* Post-next dependency tracker: records which source files import which
|
|
149
244
|
* other source files so that reloadIfAffected can traverse the graph.
|
|
@@ -164,68 +259,54 @@ export class HotReloadServer extends EventEmitter {
|
|
|
164
259
|
} catch {
|
|
165
260
|
parentPath = "";
|
|
166
261
|
}
|
|
167
|
-
const isSource = this.
|
|
168
|
-
const parentIsSource = this.
|
|
262
|
+
const isSource = this.loader.hasSourceFile(filePath);
|
|
263
|
+
const parentIsSource = this.loader.hasSourceFile(parentPath);
|
|
169
264
|
if (isSource || parentIsSource) {
|
|
170
265
|
this.trackDependency(filePath, parentPath);
|
|
171
266
|
}
|
|
172
267
|
};
|
|
173
|
-
loadTs = (url)=>{
|
|
174
|
-
if (!url.startsWith("file:")) return null;
|
|
175
|
-
const cleanPath = new URL(url).pathname;
|
|
176
|
-
const fileInfo = this.fileInfoMap.get(cleanPath);
|
|
177
|
-
if (fileInfo == null) return null;
|
|
178
|
-
const cached = this.transpileCache.get(cleanPath);
|
|
179
|
-
if (cached != null) return {
|
|
180
|
-
source: cached
|
|
181
|
-
};
|
|
182
|
-
const { project } = fileInfo;
|
|
183
|
-
const tsConfig = project.getTsConfigSync();
|
|
184
|
-
const userConfig = project.getSwcConfigSync();
|
|
185
|
-
const file = new FileEx({
|
|
186
|
-
fullPath: cleanPath
|
|
187
|
-
});
|
|
188
|
-
const response = this.transpiler.transformFileSync(file, BuildTargets.es6, {
|
|
189
|
-
tsConfig,
|
|
190
|
-
aliasPaths: true,
|
|
191
|
-
userConfig
|
|
192
|
-
});
|
|
193
|
-
if (response.outcome !== "success") {
|
|
194
|
-
this.logger.error(`Failed to transpile ${cleanPath}`, response);
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
this.transpileCache.set(cleanPath, response.code);
|
|
198
|
-
return {
|
|
199
|
-
source: response.code
|
|
200
|
-
};
|
|
201
|
-
};
|
|
202
268
|
// ── File watching ────────────────────────────────────────────────────────
|
|
203
269
|
onFileChange = (evt)=>{
|
|
204
270
|
const { file, eventName, project } = evt;
|
|
205
271
|
const fullPath = file.fullPath;
|
|
206
272
|
this.logger.trace(`File ${eventName}: ${fullPath}`);
|
|
207
|
-
if (eventName ===
|
|
208
|
-
this.
|
|
273
|
+
if (eventName === WatchEvents.unlink) {
|
|
274
|
+
this.loader.invalidateTranspile(fullPath);
|
|
275
|
+
this.moduleCache.set(fullPath, null);
|
|
209
276
|
return;
|
|
210
277
|
}
|
|
211
|
-
if (eventName ===
|
|
278
|
+
if (eventName === WatchEvents.add) {
|
|
212
279
|
if (project.isSourceFile(file)) {
|
|
213
|
-
this.
|
|
214
|
-
project
|
|
215
|
-
});
|
|
280
|
+
this.loader.setSourceFile(fullPath, project);
|
|
216
281
|
this.moduleCache.set(fullPath, null);
|
|
217
282
|
this.reloadIfAffected(fullPath);
|
|
283
|
+
this.retryFailedEntries(fullPath);
|
|
218
284
|
}
|
|
219
285
|
return;
|
|
220
286
|
}
|
|
221
287
|
// change
|
|
222
288
|
if (!project.isSourceFile(file)) return;
|
|
223
|
-
this.
|
|
224
|
-
this.transpiler.invalidateContentCache(new FileEx({
|
|
225
|
-
fullPath
|
|
226
|
-
}));
|
|
289
|
+
this.loader.invalidateTranspile(fullPath);
|
|
227
290
|
this.reloadIfAffected(fullPath);
|
|
291
|
+
this.retryFailedEntries(fullPath);
|
|
228
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* A failed boot (e.g. importing a file that doesn't exist yet) may have recorded no
|
|
295
|
+
* dependency edges, so the graph can't tell whether this save fixes it. Any source
|
|
296
|
+
* add or change retries failed entries.
|
|
297
|
+
*/ retryFailedEntries(fullPath) {
|
|
298
|
+
const failed = [
|
|
299
|
+
...this.entries.values()
|
|
300
|
+
].filter((entry)=>entry.failed);
|
|
301
|
+
if (failed.length === 0) return;
|
|
302
|
+
for (const entry of failed){
|
|
303
|
+
// New URL version: Node caches the failed load and would replay the same error.
|
|
304
|
+
this.invalidateModuleCaches(entry.path);
|
|
305
|
+
this.pendingEntryReboots.add(entry.path);
|
|
306
|
+
}
|
|
307
|
+
this.pendingTriggers.add(fullPath);
|
|
308
|
+
this.scheduleReboot();
|
|
309
|
+
}
|
|
229
310
|
reloadIfAffected(fullPath) {
|
|
230
311
|
const deps = this.getDependencyGraph(fullPath);
|
|
231
312
|
const modules = [
|
|
@@ -234,40 +315,44 @@ export class HotReloadServer extends EventEmitter {
|
|
|
234
315
|
fullPath
|
|
235
316
|
])
|
|
236
317
|
];
|
|
237
|
-
|
|
318
|
+
const affectedEntries = [
|
|
319
|
+
...this.entries.keys()
|
|
320
|
+
].filter((entryPath)=>modules.includes(entryPath));
|
|
321
|
+
if (affectedEntries.length === 0) {
|
|
238
322
|
this.logger.trace(`Change in ${fullPath} does not affect entry — skipping reload`);
|
|
239
323
|
return false;
|
|
240
324
|
}
|
|
241
325
|
this.logger.trace(`Change in ${fullPath} affects ${modules.length} module(s) — scheduling reload`);
|
|
242
|
-
this.
|
|
326
|
+
this.invalidateModuleCaches(...modules);
|
|
327
|
+
for (const module of modules){
|
|
328
|
+
this.pendingGraphPrune.add(module);
|
|
329
|
+
}
|
|
330
|
+
for (const entryPath of affectedEntries){
|
|
331
|
+
this.pendingEntryReboots.add(entryPath);
|
|
332
|
+
}
|
|
333
|
+
this.pendingTriggers.add(fullPath);
|
|
243
334
|
this.scheduleReboot();
|
|
244
335
|
return true;
|
|
245
336
|
}
|
|
246
337
|
// ── Dependency graph ─────────────────────────────────────────────────────
|
|
247
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Every module that imports `moduleId`, directly or transitively. Visits each module
|
|
340
|
+
* once: import graphs share modules (diamonds) and may legally contain cycles.
|
|
341
|
+
*/ getDependencyGraph(moduleId) {
|
|
248
342
|
if (this.isSelf(moduleId)) return [];
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
...parentModules
|
|
260
|
-
]);
|
|
261
|
-
subDeps = [
|
|
262
|
-
...subDeps,
|
|
263
|
-
...subs
|
|
264
|
-
];
|
|
343
|
+
const found = new Set();
|
|
344
|
+
const queue = [
|
|
345
|
+
moduleId
|
|
346
|
+
];
|
|
347
|
+
for(let next = queue.shift(); next != null; next = queue.shift()){
|
|
348
|
+
for (const importer of this.dependencies.get(next) ?? []){
|
|
349
|
+
if (found.has(importer)) continue;
|
|
350
|
+
found.add(importer);
|
|
351
|
+
if (this.loader.hasSourceFile(importer) && !this.isSelf(importer)) queue.push(importer);
|
|
352
|
+
}
|
|
265
353
|
}
|
|
266
354
|
return [
|
|
267
|
-
...
|
|
268
|
-
...moduleDeps,
|
|
269
|
-
...subDeps
|
|
270
|
-
])
|
|
355
|
+
...found
|
|
271
356
|
];
|
|
272
357
|
}
|
|
273
358
|
trackDependency(filePath, parentPath) {
|
|
@@ -291,14 +376,24 @@ export class HotReloadServer extends EventEmitter {
|
|
|
291
376
|
}
|
|
292
377
|
versionModule(moduleUrl) {
|
|
293
378
|
const versionedUrl = new URL(moduleUrl);
|
|
294
|
-
versionedUrl.searchParams.set(VERSION_URL_PARAM,
|
|
379
|
+
versionedUrl.searchParams.set(VERSION_URL_PARAM, String(++moduleVersionSeq));
|
|
295
380
|
return versionedUrl.toString();
|
|
296
381
|
}
|
|
297
|
-
|
|
382
|
+
invalidateModuleCaches(...modules) {
|
|
298
383
|
for (const module of modules){
|
|
299
384
|
this.moduleCache.set(module, null);
|
|
300
|
-
this.
|
|
301
|
-
|
|
385
|
+
this.loader.invalidateTranspile(module);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
prunePendingDependencyEdges() {
|
|
389
|
+
const modules = [
|
|
390
|
+
...this.pendingGraphPrune
|
|
391
|
+
];
|
|
392
|
+
this.pendingGraphPrune.clear();
|
|
393
|
+
this.pruneDependencyParents(...modules);
|
|
394
|
+
}
|
|
395
|
+
/** Remove these modules as parents so boot can rebuild edges without stale importers. */ pruneDependencyParents(...modules) {
|
|
396
|
+
for (const module of modules){
|
|
302
397
|
for (const [key, parents] of this.dependencies){
|
|
303
398
|
const filtered = parents.filter((p)=>p !== module);
|
|
304
399
|
if (filtered.length === 0) {
|
|
@@ -310,14 +405,31 @@ export class HotReloadServer extends EventEmitter {
|
|
|
310
405
|
}
|
|
311
406
|
}
|
|
312
407
|
invalidateAllSourceFiles() {
|
|
313
|
-
for (const filePath of this.
|
|
408
|
+
for (const filePath of this.loader.sourceFilePaths()){
|
|
314
409
|
this.moduleCache.set(filePath, null);
|
|
315
410
|
}
|
|
316
411
|
}
|
|
317
|
-
// ── Utilities ────────────────────────────────────────────────────────────
|
|
318
412
|
isSelf(filePath) {
|
|
319
413
|
return currentFilePath === path.resolve(filePath);
|
|
320
414
|
}
|
|
415
|
+
labelsFor(entryPaths) {
|
|
416
|
+
return entryPaths.map((entryPath)=>this.entries.get(entryPath)?.label ?? path.basename(entryPath));
|
|
417
|
+
}
|
|
418
|
+
takeTriggers() {
|
|
419
|
+
const triggers = [
|
|
420
|
+
...this.pendingTriggers
|
|
421
|
+
].map((fullPath)=>this.formatTrigger(fullPath));
|
|
422
|
+
this.pendingTriggers.clear();
|
|
423
|
+
return triggers;
|
|
424
|
+
}
|
|
425
|
+
formatTrigger(fullPath) {
|
|
426
|
+
const root = this.loader.getWorkspace()?.rootDir;
|
|
427
|
+
if (root != null) {
|
|
428
|
+
const rel = path.relative(path.resolve(root), fullPath);
|
|
429
|
+
if (rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)) {
|
|
430
|
+
return rel.replaceAll("\\", "/");
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return path.basename(fullPath);
|
|
434
|
+
}
|
|
321
435
|
}
|
|
322
|
-
|
|
323
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL2ZsdXgvc3JjL2hvdC1yZWxvYWQtc2VydmVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gXCJub2RlOmV2ZW50c1wiO1xuaW1wb3J0IHR5cGUge1xuXHRMb2FkRm5PdXRwdXQsXG5cdExvYWRIb29rQ29udGV4dCxcblx0UmVzb2x2ZUZuT3V0cHV0LFxuXHRSZXNvbHZlSG9va0NvbnRleHQsXG59IGZyb20gXCJub2RlOm1vZHVsZVwiO1xuaW1wb3J0IHsgcmVnaXN0ZXJIb29rcyB9IGZyb20gXCJub2RlOm1vZHVsZVwiO1xuaW1wb3J0IHBhdGggZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgQnVpbGRUYXJnZXRzLCBTd2NUcmFuc3BpbGVyLCBXb3Jrc3BhY2VEZWZhdWx0cyB9IGZyb20gXCJAZGVjb2hlcmUvZm9yZ2VcIjtcbmltcG9ydCB0eXBlIHsgUHJvamVjdCwgUHJvamVjdEZpbGVFdmVudCwgV29ya3NwYWNlIH0gZnJvbSBcIkBkZWNvaGVyZS9tZXNoXCI7XG5pbXBvcnQgeyBXb3Jrc3BhY2VGYWN0b3J5IH0gZnJvbSBcIkBkZWNvaGVyZS9tZXNoXCI7XG5pbXBvcnQgeyBGaWxlRXgsIGlvIH0gZnJvbSBcIkBkZWNvaGVyZS9uZXh1cy9pb1wiO1xuaW1wb3J0IHsgZGVib3VuY2UgfSBmcm9tIFwiQGRlY29oZXJlL25leHVzL3N5c3RlbVwiO1xuaW1wb3J0IHsgdHlwZSBMb2dnZXIsIHNjcmliZSB9IGZyb20gXCJAZGVjb2hlcmUvc2NyaWJlXCI7XG5pbXBvcnQgdHlwZSB7IFN1YnNjcmlwdGlvbiB9IGZyb20gXCJyeGpzXCI7XG5cbnR5cGUgTG9hZERlbGVnYXRlID0gKHVybDogc3RyaW5nKSA9PiB7IHNvdXJjZTogc3RyaW5nIH0gfCBudWxsO1xuXG5jb25zdCBjdXJyZW50RmlsZVBhdGggPSBpby5maWxlVXJsVG9QYXRoKGltcG9ydC5tZXRhLnVybCk7XG5jb25zdCBWRVJTSU9OX1VSTF9QQVJBTSA9IFwidmVyc2lvblwiO1xuY29uc3QgUkVCT09UX0RFQk9VTkNFX01TID0gMjUwO1xuXG5leHBvcnQgaW50ZXJmYWNlIEhvdFJlbG9hZFNlcnZlckV2ZW50cyB7XG5cdHN0YXJ0OiBbXTtcblx0cmVib290OiBbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBIb3RSZWxvYWRTZXJ2ZXJPcHRpb25zIHtcblx0LyoqIEJ1aWxkIGNvbmZpZ3VyYXRpb24gbmFtZS4gKi9cblx0YnVpbGRDb25maWc6IHN0cmluZztcblx0LyoqIE5hbWUgb2YgdGhlIHByb2plY3QgdG8gc2VydmUuIERlZmF1bHRzIHRvIHRoZSBmaXJzdCBwcm9qZWN0IGluIHRoZSB3b3Jrc3BhY2UuICovXG5cdHByb2plY3Q/OiBzdHJpbmc7XG5cdC8qKlxuXHQgKiBQcmUtYnVpbHQgV29ya3NwYWNlIGluc3RhbmNlIHRvIHVzZSBpbnN0ZWFkIG9mIHJ1bm5pbmcgV29ya3NwYWNlRmFjdG9yeS5cblx0ICogVXNlZnVsIGluIHRlc3RzIHRvIGJ5cGFzcyBmaWxlc3lzdGVtLWJhc2VkIHdvcmtzcGFjZSBkaXNjb3ZlcnkuXG5cdCAqL1xuXHR3b3Jrc3BhY2U/OiBXb3Jrc3BhY2U7XG59XG5cbnR5cGUgRmlsZUluZm8gPSB7IHByb2plY3Q6IFByb2plY3QgfTtcblxuZXhwb3J0IGNsYXNzIEhvdFJlbG9hZFNlcnZlciBleHRlbmRzIEV2ZW50RW1pdHRlcjxIb3RSZWxvYWRTZXJ2ZXJFdmVudHM+IHtcblx0Ly8g4pSA4pSAIFN0YXRlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXHRwcml2YXRlIHJlYWRvbmx5IGxvZ2dlcjogTG9nZ2VyID0gc2NyaWJlLmxvZ2dlcihIb3RSZWxvYWRTZXJ2ZXIubmFtZSk7XG5cdHByaXZhdGUgZGlzcG9zZWQgPSBmYWxzZTtcblx0cHJpdmF0ZSBob29rc1JlZ2lzdGVyZWQgPSBmYWxzZTtcblx0cHJpdmF0ZSBlbnRyeU1vZHVsZVBhdGghOiBzdHJpbmc7XG5cdHByaXZhdGUgZW50cnlNb2R1bGU/OiBBc3luY0Rpc3Bvc2FibGU7XG5cdHByaXZhdGUgd29ya3NwYWNlPzogV29ya3NwYWNlO1xuXHRwcml2YXRlIHdhdGNoU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuXHQvKiogTWFwIGZyb20gYWJzb2x1dGUgLnRzIHNvdXJjZSBmaWxlIHBhdGggdG8gcHJvamVjdCBpbmZvICovXG5cdHByaXZhdGUgZmlsZUluZm9NYXAgPSBuZXcgTWFwPHN0cmluZywgRmlsZUluZm8+KCk7XG5cdHByaXZhdGUgbW9kdWxlQ2FjaGUgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nIHwgbnVsbD4oKTtcblx0cHJpdmF0ZSBkZXBlbmRlbmNpZXMgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nW10+KCk7XG5cdHByaXZhdGUgdHJhbnNwaWxlQ2FjaGUgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuXHRwcml2YXRlIHRyYW5zcGlsZXIgPSBuZXcgU3djVHJhbnNwaWxlcigpO1xuXG5cdC8vIOKUgOKUgCBQdWJsaWMgQVBJIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5cdHB1YmxpYyBhc3luYyBzdGFydChvcHRpb25zOiBIb3RSZWxvYWRTZXJ2ZXJPcHRpb25zKSB7XG5cdFx0dGhpcy5kaXNwb3NlZCA9IGZhbHNlO1xuXHRcdHRoaXMuZGVwZW5kZW5jaWVzLmNsZWFyKCk7XG5cdFx0dGhpcy5tb2R1bGVDYWNoZS5jbGVhcigpO1xuXHRcdHRoaXMudHJhbnNwaWxlQ2FjaGUuY2xlYXIoKTtcblxuXHRcdGNvbnN0IHsgYnVpbGRDb25maWcgfSA9IG9wdGlvbnM7XG5cdFx0dGhpcy53b3Jrc3BhY2UgPSBhd2FpdCB0aGlzLnJlc29sdmVXb3Jrc3BhY2Uob3B0aW9ucyk7XG5cdFx0Y29uc3QgcHJvamVjdCA9IHRoaXMucmVzb2x2ZVByb2plY3QoYnVpbGRDb25maWcsIG9wdGlvbnMucHJvamVjdCk7XG5cblx0XHR0aGlzLmVudHJ5TW9kdWxlUGF0aCA9IHBhdGgucmVzb2x2ZShwcm9qZWN0LmdldFNvdXJjZUVudHJ5UGF0aCgpKTtcblx0XHR0aGlzLmZpbGVJbmZvTWFwID0gYXdhaXQgdGhpcy5idWlsZEZpbGVJbmZvTWFwKGJ1aWxkQ29uZmlnKTtcblxuXHRcdHRoaXMucmVnaXN0ZXJIb29rcygpO1xuXHRcdHRoaXMuaW52YWxpZGF0ZUFsbFNvdXJjZUZpbGVzKCk7XG5cblx0XHRhd2FpdCB0aGlzLndvcmtzcGFjZS53YXRjaChidWlsZENvbmZpZyk7XG5cdFx0dGhpcy53YXRjaFN1YnNjcmlwdGlvbiA9IHRoaXMud29ya3NwYWNlLnByb2plY3RGaWxlQ2hhbmdlZCQuc3Vic2NyaWJlKHRoaXMub25GaWxlQ2hhbmdlKTtcblxuXHRcdHRoaXMubG9nZ2VyLnRyYWNlKGBTZXJ2aW5nICR7dGhpcy5lbnRyeU1vZHVsZVBhdGh9YCk7XG5cdFx0YXdhaXQgdGhpcy5ib290RW50cnkoKTtcblx0XHR0aGlzLmVtaXQoXCJzdGFydFwiKTtcblx0fVxuXG5cdHB1YmxpYyBhc3luYyBkaXNwb3NlKCkge1xuXHRcdGlmICh0aGlzLmRpc3Bvc2VkKSByZXR1cm47XG5cdFx0dGhpcy5kaXNwb3NlZCA9IHRydWU7XG5cdFx0dGhpcy53YXRjaFN1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcblx0XHR0aGlzLndvcmtzcGFjZT8uW1N5bWJvbC5kaXNwb3NlXSgpO1xuXHRcdGF3YWl0IHRoaXMuZGlzcG9zZUVudHJ5KCk7XG5cdH1cblxuXHRwdWJsaWMgYXN5bmMgW1N5bWJvbC5hc3luY0Rpc3Bvc2VdKCkge1xuXHRcdGF3YWl0IHRoaXMuZGlzcG9zZSgpO1xuXHR9XG5cblx0Ly8g4pSA4pSAIExpZmVjeWNsZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuXHRwcml2YXRlIGFzeW5jIGJvb3RFbnRyeSgpIHtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgdXJsID0gdGhpcy52ZXJzaW9uTW9kdWxlKGlvLnBhdGhUb0ZpbGVVcmwodGhpcy5lbnRyeU1vZHVsZVBhdGgpKTtcblx0XHRcdHRoaXMuZW50cnlNb2R1bGUgPSAoYXdhaXQgaW1wb3J0KHVybCkpPy5kZWZhdWx0O1xuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHR0aGlzLmxvZ2dlci5lcnJvcihcblx0XHRcdFx0XCJUaGVyZSB3YXMgYW4gZXJyb3IgYm9vdGluZyB0aGUgZW50cnkgcG9pbnQuIFlvdXIgYXBwbGljYXRpb24gbWF5IG5vdCBmdW5jdGlvbiBhcyBleHBlY3RlZFwiLFxuXHRcdFx0XHRlcnJvcixcblx0XHRcdCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyByZWJvb3QoKSB7XG5cdFx0dGhpcy5sb2dnZXIudHJhY2UoXCJSZWJvb3RpbmcgZW50cnkgbW9kdWxlXCIpO1xuXHRcdGF3YWl0IHRoaXMuZGlzcG9zZUVudHJ5KCk7XG5cdFx0dGhpcy5lbWl0KFwicmVib290XCIpO1xuXHRcdGF3YWl0IHRoaXMuYm9vdEVudHJ5KCk7XG5cdH1cblxuXHRwcml2YXRlIGFzeW5jIGRpc3Bvc2VFbnRyeSgpIHtcblx0XHRhd2FpdCB0aGlzLmVudHJ5TW9kdWxlPy5bU3ltYm9sLmFzeW5jRGlzcG9zZV0oKTtcblx0fVxuXG5cdHByaXZhdGUgc2NoZWR1bGVSZWJvb3QgPSBkZWJvdW5jZSgoKSA9PiB0aGlzLnJlYm9vdCgpLCBSRUJPT1RfREVCT1VOQ0VfTVMpO1xuXG5cdC8vIOKUgOKUgCBTZXR1cCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuXHRwcml2YXRlIGFzeW5jIHJlc29sdmVXb3Jrc3BhY2Uob3B0aW9uczogSG90UmVsb2FkU2VydmVyT3B0aW9ucyk6IFByb21pc2U8V29ya3NwYWNlPiB7XG5cdFx0cmV0dXJuIChcblx0XHRcdG9wdGlvbnMud29ya3NwYWNlID8/XG5cdFx0XHRXb3Jrc3BhY2VGYWN0b3J5LmNyZWF0ZSh7IC4uLldvcmtzcGFjZURlZmF1bHRzLCByb290RGlyOiBwcm9jZXNzLmN3ZCgpIH0pXG5cdFx0KTtcblx0fVxuXG5cdHByaXZhdGUgcmVzb2x2ZVByb2plY3QoYnVpbGRDb25maWc6IHN0cmluZywgbmFtZT86IHN0cmluZyk6IFByb2plY3Qge1xuXHRcdGNvbnN0IHByb2plY3RzID0gdGhpcy53b3Jrc3BhY2U/LmdldFByb2plY3RzKGJ1aWxkQ29uZmlnKSA/PyBbXTtcblx0XHRjb25zdCBmb3VuZCA9IG5hbWUgPT0gbnVsbCA/IHByb2plY3RzLmF0KDApIDogcHJvamVjdHMuZmluZCgocCkgPT4gcC5uYW1lID09PSBuYW1lKTtcblx0XHRpZiAoZm91bmQgPT0gbnVsbCkge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdFx0XHRuYW1lID09IG51bGxcblx0XHRcdFx0XHQ/IGBObyBwcm9qZWN0cyBmb3VuZCBmb3IgY29uZmlndXJhdGlvbjogJHtidWlsZENvbmZpZ31gXG5cdFx0XHRcdFx0OiBgUHJvamVjdCBcIiR7bmFtZX1cIiBub3QgZm91bmQgaW4gd29ya3NwYWNlYCxcblx0XHRcdCk7XG5cdFx0fVxuXHRcdHJldHVybiBmb3VuZDtcblx0fVxuXG5cdHByaXZhdGUgYXN5bmMgYnVpbGRGaWxlSW5mb01hcChidWlsZENvbmZpZzogc3RyaW5nKTogUHJvbWlzZTxNYXA8c3RyaW5nLCBGaWxlSW5mbz4+IHtcblx0XHRjb25zdCBwcm9qZWN0cyA9IHRoaXMud29ya3NwYWNlPy5nZXRQcm9qZWN0cyhidWlsZENvbmZpZykgPz8gW107XG5cdFx0Y29uc3QgZW50cmllcyA9IGF3YWl0IFByb21pc2UuYWxsKFxuXHRcdFx0cHJvamVjdHMubWFwKGFzeW5jIChwcm9qZWN0KSA9PiB7XG5cdFx0XHRcdGNvbnN0IGZpbGVzID0gYXdhaXQgcHJvamVjdC5nZXRBbGxGaWxlcygpO1xuXHRcdFx0XHRyZXR1cm4gZmlsZXMubWFwKChmaWxlKTogW3N0cmluZywgRmlsZUluZm9dID0+IFtmaWxlLmZ1bGxQYXRoLCB7IHByb2plY3QgfV0pO1xuXHRcdFx0fSksXG5cdFx0KTtcblx0XHRyZXR1cm4gbmV3IE1hcChlbnRyaWVzLmZsYXQoKSk7XG5cdH1cblxuXHRwcml2YXRlIHJlZ2lzdGVySG9va3MoKSB7XG5cdFx0aWYgKHRoaXMuaG9va3NSZWdpc3RlcmVkKSByZXR1cm47XG5cdFx0dGhpcy5ob29rc1JlZ2lzdGVyZWQgPSB0cnVlO1xuXHRcdHJlZ2lzdGVySG9va3Moe1xuXHRcdFx0cmVzb2x2ZTogKFxuXHRcdFx0XHRzcGVjaWZpZXI6IHN0cmluZyxcblx0XHRcdFx0Y29udGV4dDogUmVzb2x2ZUhvb2tDb250ZXh0LFxuXHRcdFx0XHRuZXh0OiAoc3BlY2lmaWVyOiBzdHJpbmcsIGNvbnRleHQ/OiBQYXJ0aWFsPFJlc29sdmVIb29rQ29udGV4dD4pID0+IFJlc29sdmVGbk91dHB1dCxcblx0XHRcdCkgPT4ge1xuXHRcdFx0XHRpZiAodGhpcy5kaXNwb3NlZCkgcmV0dXJuIG5leHQoc3BlY2lmaWVyLCBjb250ZXh0KTtcblx0XHRcdFx0Y29uc3QgcGFyZW50VXJsID0gY29udGV4dC5wYXJlbnRVUkwgPz8gXCJcIjtcblx0XHRcdFx0Y29uc3QgcmV3cml0dGVuID0gdGhpcy5yZXdyaXRlSnNUb1RzKHNwZWNpZmllciwgcGFyZW50VXJsKSA/PyBzcGVjaWZpZXI7XG5cdFx0XHRcdGNvbnN0IHJlc3VsdCA9IG5leHQocmV3cml0dGVuLCBjb250ZXh0KSBhcyBSZXNvbHZlRm5PdXRwdXQ7XG5cdFx0XHRcdHRoaXMub25Qb3N0UmVzb2x2ZShyZXN1bHQsIHBhcmVudFVybCk7XG5cdFx0XHRcdHJldHVybiByZXN1bHQ7XG5cdFx0XHR9LFxuXHRcdFx0bG9hZDogKFxuXHRcdFx0XHR1cmw6IHN0cmluZyxcblx0XHRcdFx0Y29udGV4dDogTG9hZEhvb2tDb250ZXh0LFxuXHRcdFx0XHRuZXh0OiAodXJsOiBzdHJpbmcsIGNvbnRleHQ/OiBQYXJ0aWFsPExvYWRIb29rQ29udGV4dD4pID0+IExvYWRGbk91dHB1dCxcblx0XHRcdCk6IExvYWRGbk91dHB1dCA9PiB7XG5cdFx0XHRcdGlmICh0aGlzLmRpc3Bvc2VkKSByZXR1cm4gbmV4dCh1cmwsIGNvbnRleHQpO1xuXHRcdFx0XHRjb25zdCByZXN1bHQgPSB0aGlzLmxvYWRUcyh1cmwpO1xuXHRcdFx0XHRpZiAocmVzdWx0ICE9IG51bGwpXG5cdFx0XHRcdFx0cmV0dXJuIHsgZm9ybWF0OiBcIm1vZHVsZVwiLCBzb3VyY2U6IHJlc3VsdC5zb3VyY2UsIHNob3J0Q2lyY3VpdDogdHJ1ZSB9O1xuXHRcdFx0XHRyZXR1cm4gbmV4dCh1cmwsIGNvbnRleHQpO1xuXHRcdFx0fSxcblx0XHR9KTtcblx0fVxuXG5cdC8vIOKUgOKUgCBNb2R1bGUgaG9va3Mg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cblx0LyoqXG5cdCAqIFByZS1uZXh0IHNwZWNpZmllciByZXdyaXRlcjogaWYgc3BlY2lmaWVyIGlzIGEgLmpzIGltcG9ydCB0aGF0IG1hcHMgdG8gYVxuXHQgKiBrbm93biAudHMgc291cmNlIGZpbGUsIHJldHVybnMgdGhlIHZlcnNpb25lZCAudHMgZmlsZSBVUkwgc28gbmV4dCgpIG5ldmVyXG5cdCAqIHRyaWVzIHRvIGxvYWQgYSBub24tZXhpc3RlbnQgLmpzIGZpbGUgZnJvbSBkaXNrLlxuXHQgKi9cblx0cHJpdmF0ZSByZXdyaXRlSnNUb1RzID0gKHNwZWNpZmllcjogc3RyaW5nLCBwYXJlbnRVcmw6IHN0cmluZyk6IHN0cmluZyB8IG51bGwgPT4ge1xuXHRcdGlmICghc3BlY2lmaWVyLmVuZHNXaXRoKFwiLmpzXCIpKSByZXR1cm4gbnVsbDtcblxuXHRcdGNvbnN0IHBhcmVudENsZWFuID0gcGFyZW50VXJsLnNwbGl0KFwiP1wiKVswXTtcblx0XHRpZiAoIXBhcmVudENsZWFuKSByZXR1cm4gbnVsbDtcblxuXHRcdGxldCBqc1BhdGg6IHN0cmluZztcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcmVzb2x2ZWQgPSBuZXcgVVJMKHNwZWNpZmllciwgcGFyZW50Q2xlYW4pO1xuXHRcdFx0anNQYXRoID0gaW8uZmlsZVVybFRvUGF0aChyZXNvbHZlZC50b1N0cmluZygpKTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdHJldHVybiBudWxsO1xuXHRcdH1cblxuXHRcdGlmICghanNQYXRoLmVuZHNXaXRoKFwiLmpzXCIpKSByZXR1cm4gbnVsbDtcblx0XHRjb25zdCB0c1BhdGggPSBqc1BhdGgucmVwbGFjZSgvXFwuanMkLywgXCIudHNcIik7XG5cdFx0aWYgKCF0aGlzLmZpbGVJbmZvTWFwLmhhcyh0c1BhdGgpKSByZXR1cm4gbnVsbDtcblxuXHRcdHJldHVybiB0aGlzLmdldENhY2hlZE9yTmV3KGlvLnBhdGhUb0ZpbGVVcmwodHNQYXRoKSk7XG5cdH07XG5cblx0LyoqXG5cdCAqIFBvc3QtbmV4dCBkZXBlbmRlbmN5IHRyYWNrZXI6IHJlY29yZHMgd2hpY2ggc291cmNlIGZpbGVzIGltcG9ydCB3aGljaFxuXHQgKiBvdGhlciBzb3VyY2UgZmlsZXMgc28gdGhhdCByZWxvYWRJZkFmZmVjdGVkIGNhbiB0cmF2ZXJzZSB0aGUgZ3JhcGguXG5cdCAqL1xuXHRwcml2YXRlIG9uUG9zdFJlc29sdmUgPSAocmVzdWx0OiBSZXNvbHZlRm5PdXRwdXQsIHBhcmVudFVybDogc3RyaW5nKSA9PiB7XG5cdFx0Y29uc3QgeyB1cmwgfSA9IHJlc3VsdDtcblx0XHRjb25zdCBwYXJlbnRVcmxDbGVhbiA9IHBhcmVudFVybC5zcGxpdChcIj9cIilbMF07XG5cblx0XHRsZXQgZmlsZVBhdGg6IHN0cmluZztcblx0XHRsZXQgcGFyZW50UGF0aDogc3RyaW5nO1xuXG5cdFx0dHJ5IHtcblx0XHRcdGNvbnN0IHVybE9iaiA9IG5ldyBVUkwodXJsKTtcblx0XHRcdHVybE9iai5zZWFyY2ggPSBcIlwiO1xuXHRcdFx0ZmlsZVBhdGggPSBpby5maWxlVXJsVG9QYXRoKHVybE9iai50b1N0cmluZygpKTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0cnkge1xuXHRcdFx0cGFyZW50UGF0aCA9IGlvLmZpbGVVcmxUb1BhdGgocGFyZW50VXJsQ2xlYW4pO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0cGFyZW50UGF0aCA9IFwiXCI7XG5cdFx0fVxuXG5cdFx0Y29uc3QgaXNTb3VyY2UgPSB0aGlzLmZpbGVJbmZvTWFwLmhhcyhmaWxlUGF0aCk7XG5cdFx0Y29uc3QgcGFyZW50SXNTb3VyY2UgPSB0aGlzLmZpbGVJbmZvTWFwLmhhcyhwYXJlbnRQYXRoKTtcblxuXHRcdGlmIChpc1NvdXJjZSB8fCBwYXJlbnRJc1NvdXJjZSkge1xuXHRcdFx0dGhpcy50cmFja0RlcGVuZGVuY3koZmlsZVBhdGgsIHBhcmVudFBhdGgpO1xuXHRcdH1cblx0fTtcblxuXHRwcml2YXRlIGxvYWRUczogTG9hZERlbGVnYXRlID0gKHVybDogc3RyaW5nKSA9PiB7XG5cdFx0aWYgKCF1cmwuc3RhcnRzV2l0aChcImZpbGU6XCIpKSByZXR1cm4gbnVsbDtcblxuXHRcdGNvbnN0IGNsZWFuUGF0aCA9IG5ldyBVUkwodXJsKS5wYXRobmFtZTtcblx0XHRjb25zdCBmaWxlSW5mbyA9IHRoaXMuZmlsZUluZm9NYXAuZ2V0KGNsZWFuUGF0aCk7XG5cdFx0aWYgKGZpbGVJbmZvID09IG51bGwpIHJldHVybiBudWxsO1xuXG5cdFx0Y29uc3QgY2FjaGVkID0gdGhpcy50cmFuc3BpbGVDYWNoZS5nZXQoY2xlYW5QYXRoKTtcblx0XHRpZiAoY2FjaGVkICE9IG51bGwpIHJldHVybiB7IHNvdXJjZTogY2FjaGVkIH07XG5cblx0XHRjb25zdCB7IHByb2plY3QgfSA9IGZpbGVJbmZvO1xuXHRcdGNvbnN0IHRzQ29uZmlnID0gcHJvamVjdC5nZXRUc0NvbmZpZ1N5bmMoKTtcblx0XHRjb25zdCB1c2VyQ29uZmlnID0gcHJvamVjdC5nZXRTd2NDb25maWdTeW5jKCk7XG5cblx0XHRjb25zdCBmaWxlID0gbmV3IEZpbGVFeCh7IGZ1bGxQYXRoOiBjbGVhblBhdGggfSk7XG5cdFx0Y29uc3QgcmVzcG9uc2UgPSB0aGlzLnRyYW5zcGlsZXIudHJhbnNmb3JtRmlsZVN5bmMoZmlsZSwgQnVpbGRUYXJnZXRzLmVzNiwge1xuXHRcdFx0dHNDb25maWcsXG5cdFx0XHRhbGlhc1BhdGhzOiB0cnVlLFxuXHRcdFx0dXNlckNvbmZpZyxcblx0XHR9KTtcblxuXHRcdGlmIChyZXNwb25zZS5vdXRjb21lICE9PSBcInN1Y2Nlc3NcIikge1xuXHRcdFx0dGhpcy5sb2dnZXIuZXJyb3IoYEZhaWxlZCB0byB0cmFuc3BpbGUgJHtjbGVhblBhdGh9YCwgcmVzcG9uc2UpO1xuXHRcdFx0cmV0dXJuIG51bGw7XG5cdFx0fVxuXG5cdFx0dGhpcy50cmFuc3BpbGVDYWNoZS5zZXQoY2xlYW5QYXRoLCByZXNwb25zZS5jb2RlKTtcblx0XHRyZXR1cm4geyBzb3VyY2U6IHJlc3BvbnNlLmNvZGUgfTtcblx0fTtcblxuXHQvLyDilIDilIAgRmlsZSB3YXRjaGluZyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuXHRwcml2YXRlIG9uRmlsZUNoYW5nZSA9IChldnQ6IFByb2plY3RGaWxlRXZlbnQpID0+IHtcblx0XHRjb25zdCB7IGZpbGUsIGV2ZW50TmFtZSwgcHJvamVjdCB9ID0gZXZ0O1xuXHRcdGNvbnN0IGZ1bGxQYXRoID0gZmlsZS5mdWxsUGF0aDtcblxuXHRcdHRoaXMubG9nZ2VyLnRyYWNlKGBGaWxlICR7ZXZlbnROYW1lfTogJHtmdWxsUGF0aH1gKTtcblxuXHRcdGlmIChldmVudE5hbWUgPT09IGlvLldhdGNoRXZlbnRzLnVubGluaykge1xuXHRcdFx0dGhpcy5maWxlSW5mb01hcC5kZWxldGUoZnVsbFBhdGgpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmIChldmVudE5hbWUgPT09IGlvLldhdGNoRXZlbnRzLmFkZCkge1xuXHRcdFx0aWYgKHByb2plY3QuaXNTb3VyY2VGaWxlKGZpbGUpKSB7XG5cdFx0XHRcdHRoaXMuZmlsZUluZm9NYXAuc2V0KGZ1bGxQYXRoLCB7IHByb2plY3QgfSk7XG5cdFx0XHRcdHRoaXMubW9kdWxlQ2FjaGUuc2V0KGZ1bGxQYXRoLCBudWxsKTtcblx0XHRcdFx0dGhpcy5yZWxvYWRJZkFmZmVjdGVkKGZ1bGxQYXRoKTtcblx0XHRcdH1cblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBjaGFuZ2Vcblx0XHRpZiAoIXByb2plY3QuaXNTb3VyY2VGaWxlKGZpbGUpKSByZXR1cm47XG5cdFx0dGhpcy50cmFuc3BpbGVDYWNoZS5kZWxldGUoZnVsbFBhdGgpO1xuXHRcdHRoaXMudHJhbnNwaWxlci5pbnZhbGlkYXRlQ29udGVudENhY2hlKG5ldyBGaWxlRXgoeyBmdWxsUGF0aCB9KSk7XG5cdFx0dGhpcy5yZWxvYWRJZkFmZmVjdGVkKGZ1bGxQYXRoKTtcblx0fTtcblxuXHRwcml2YXRlIHJlbG9hZElmQWZmZWN0ZWQoZnVsbFBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuXHRcdGNvbnN0IGRlcHMgPSB0aGlzLmdldERlcGVuZGVuY3lHcmFwaChmdWxsUGF0aCk7XG5cdFx0Y29uc3QgbW9kdWxlcyA9IFsuLi5uZXcgU2V0KFsuLi5kZXBzLCBmdWxsUGF0aF0pXTtcblxuXHRcdGlmICghbW9kdWxlcy5pbmNsdWRlcyh0aGlzLmVudHJ5TW9kdWxlUGF0aCkpIHtcblx0XHRcdHRoaXMubG9nZ2VyLnRyYWNlKGBDaGFuZ2UgaW4gJHtmdWxsUGF0aH0gZG9lcyBub3QgYWZmZWN0IGVudHJ5IOKAlCBza2lwcGluZyByZWxvYWRgKTtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHR0aGlzLmxvZ2dlci50cmFjZShcblx0XHRcdGBDaGFuZ2UgaW4gJHtmdWxsUGF0aH0gYWZmZWN0cyAke21vZHVsZXMubGVuZ3RofSBtb2R1bGUocykg4oCUIHNjaGVkdWxpbmcgcmVsb2FkYCxcblx0XHQpO1xuXHRcdHRoaXMuaW52YWxpZGF0ZU1vZHVsZSguLi5tb2R1bGVzKTtcblx0XHR0aGlzLnNjaGVkdWxlUmVib290KCk7XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cblxuXHQvLyDilIDilIAgRGVwZW5kZW5jeSBncmFwaCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuXHRwcml2YXRlIGdldERlcGVuZGVuY3lHcmFwaChtb2R1bGVJZDogc3RyaW5nLCBwYXJlbnRNb2R1bGVzOiBzdHJpbmdbXSA9IFtdKTogc3RyaW5nW10ge1xuXHRcdGlmICh0aGlzLmlzU2VsZihtb2R1bGVJZCkpIHJldHVybiBbXTtcblxuXHRcdGNvbnN0IG1vZHVsZURlcHMgPSB0aGlzLmRlcGVuZGVuY2llcy5nZXQobW9kdWxlSWQpID8/IFtdO1xuXHRcdGlmIChwYXJlbnRNb2R1bGVzLmluY2x1ZGVzKG1vZHVsZUlkKSkge1xuXHRcdFx0dGhpcy5sb2dnZXIuZXJyb3IoYENpcmN1bGFyIHJlZmVyZW5jZSBkZXRlY3RlZDogJHttb2R1bGVJZH1gKTtcblx0XHRcdHJldHVybiBbXTtcblx0XHR9XG5cblx0XHRsZXQgc3ViRGVwczogc3RyaW5nW10gPSBbXTtcblx0XHRmb3IgKGNvbnN0IGRlcCBvZiBtb2R1bGVEZXBzKSB7XG5cdFx0XHRpZiAoIXRoaXMuZmlsZUluZm9NYXAuaGFzKGRlcCkpIGNvbnRpbnVlO1xuXHRcdFx0Y29uc3Qgc3VicyA9IHRoaXMuZ2V0RGVwZW5kZW5jeUdyYXBoKGRlcCwgW21vZHVsZUlkLCAuLi5wYXJlbnRNb2R1bGVzXSk7XG5cdFx0XHRzdWJEZXBzID0gWy4uLnN1YkRlcHMsIC4uLnN1YnNdO1xuXHRcdH1cblxuXHRcdHJldHVybiBbLi4ubmV3IFNldChbLi4ubW9kdWxlRGVwcywgLi4uc3ViRGVwc10pXTtcblx0fVxuXG5cdHByaXZhdGUgdHJhY2tEZXBlbmRlbmN5KGZpbGVQYXRoOiBzdHJpbmcsIHBhcmVudFBhdGg6IHN0cmluZykge1xuXHRcdGlmICghcGFyZW50UGF0aCkgcmV0dXJuO1xuXHRcdGNvbnN0IGRlcHMgPSB0aGlzLmRlcGVuZGVuY2llcy5nZXQoZmlsZVBhdGgpID8/IFtdO1xuXHRcdGNvbnN0IHVwZGF0ZWREZXBzID0gbmV3IFNldChbLi4uZGVwcywgcGFyZW50UGF0aF0pO1xuXHRcdHRoaXMuZGVwZW5kZW5jaWVzLnNldChmaWxlUGF0aCwgWy4uLnVwZGF0ZWREZXBzXSk7XG5cdH1cblxuXHQvLyDilIDilIAgTW9kdWxlIGNhY2hlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5cdHByaXZhdGUgZ2V0Q2FjaGVkT3JOZXcobW9kdWxlVXJsOiBzdHJpbmcpIHtcblx0XHRjb25zdCBmaWxlUGF0aCA9IGlvLmZpbGVVcmxUb1BhdGgobW9kdWxlVXJsKTtcblx0XHRjb25zdCBjYWNoZWQgPSB0aGlzLm1vZHVsZUNhY2hlLmdldChmaWxlUGF0aCk7XG5cdFx0Y29uc3QgdmVyc2lvbmVkID0gY2FjaGVkID8/IHRoaXMudmVyc2lvbk1vZHVsZShtb2R1bGVVcmwpO1xuXHRcdHRoaXMubW9kdWxlQ2FjaGUuc2V0KGZpbGVQYXRoLCB2ZXJzaW9uZWQpO1xuXHRcdHJldHVybiB2ZXJzaW9uZWQ7XG5cdH1cblxuXHRwcml2YXRlIHZlcnNpb25Nb2R1bGUobW9kdWxlVXJsOiBzdHJpbmcpIHtcblx0XHRjb25zdCB2ZXJzaW9uZWRVcmwgPSBuZXcgVVJMKG1vZHVsZVVybCk7XG5cdFx0dmVyc2lvbmVkVXJsLnNlYXJjaFBhcmFtcy5zZXQoVkVSU0lPTl9VUkxfUEFSQU0sIERhdGUubm93KCkudG9TdHJpbmcoKSk7XG5cdFx0cmV0dXJuIHZlcnNpb25lZFVybC50b1N0cmluZygpO1xuXHR9XG5cblx0cHJpdmF0ZSBpbnZhbGlkYXRlTW9kdWxlKC4uLm1vZHVsZXM6IHN0cmluZ1tdKSB7XG5cdFx0Zm9yIChjb25zdCBtb2R1bGUgb2YgbW9kdWxlcykge1xuXHRcdFx0dGhpcy5tb2R1bGVDYWNoZS5zZXQobW9kdWxlLCBudWxsKTtcblx0XHRcdHRoaXMudHJhbnNwaWxlQ2FjaGUuZGVsZXRlKG1vZHVsZSk7XG5cdFx0XHQvLyBQcnVuZSBzdGFsZSBvdXRnb2luZyBlZGdlczogcmVtb3ZlIHRoaXMgbW9kdWxlIGFzIGEgcGFyZW50IGZyb20gYWxsIGRlcGVuZGVuY3kgbGlzdHNcblx0XHRcdGZvciAoY29uc3QgW2tleSwgcGFyZW50c10gb2YgdGhpcy5kZXBlbmRlbmNpZXMpIHtcblx0XHRcdFx0Y29uc3QgZmlsdGVyZWQgPSBwYXJlbnRzLmZpbHRlcigocCkgPT4gcCAhPT0gbW9kdWxlKTtcblx0XHRcdFx0aWYgKGZpbHRlcmVkLmxlbmd0aCA9PT0gMCkge1xuXHRcdFx0XHRcdHRoaXMuZGVwZW5kZW5jaWVzLmRlbGV0ZShrZXkpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRoaXMuZGVwZW5kZW5jaWVzLnNldChrZXksIGZpbHRlcmVkKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHByaXZhdGUgaW52YWxpZGF0ZUFsbFNvdXJjZUZpbGVzKCkge1xuXHRcdGZvciAoY29uc3QgZmlsZVBhdGggb2YgdGhpcy5maWxlSW5mb01hcC5rZXlzKCkpIHtcblx0XHRcdHRoaXMubW9kdWxlQ2FjaGUuc2V0KGZpbGVQYXRoLCBudWxsKTtcblx0XHR9XG5cdH1cblxuXHQvLyDilIDilIAgVXRpbGl0aWVzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5cdHByaXZhdGUgaXNTZWxmKGZpbGVQYXRoOiBzdHJpbmcpIHtcblx0XHRyZXR1cm4gY3VycmVudEZpbGVQYXRoID09PSBwYXRoLnJlc29sdmUoZmlsZVBhdGgpO1xuXHR9XG59XG4iXSwibmFtZXMiOlsiRXZlbnRFbWl0dGVyIiwicmVnaXN0ZXJIb29rcyIsInBhdGgiLCJCdWlsZFRhcmdldHMiLCJTd2NUcmFuc3BpbGVyIiwiV29ya3NwYWNlRGVmYXVsdHMiLCJXb3Jrc3BhY2VGYWN0b3J5IiwiRmlsZUV4IiwiaW8iLCJkZWJvdW5jZSIsInNjcmliZSIsImN1cnJlbnRGaWxlUGF0aCIsImZpbGVVcmxUb1BhdGgiLCJ1cmwiLCJWRVJTSU9OX1VSTF9QQVJBTSIsIlJFQk9PVF9ERUJPVU5DRV9NUyIsIkhvdFJlbG9hZFNlcnZlciIsImxvZ2dlciIsIm5hbWUiLCJkaXNwb3NlZCIsImhvb2tzUmVnaXN0ZXJlZCIsImVudHJ5TW9kdWxlUGF0aCIsImVudHJ5TW9kdWxlIiwid29ya3NwYWNlIiwid2F0Y2hTdWJzY3JpcHRpb24iLCJmaWxlSW5mb01hcCIsIk1hcCIsIm1vZHVsZUNhY2hlIiwiZGVwZW5kZW5jaWVzIiwidHJhbnNwaWxlQ2FjaGUiLCJ0cmFuc3BpbGVyIiwic3RhcnQiLCJvcHRpb25zIiwiY2xlYXIiLCJidWlsZENvbmZpZyIsInJlc29sdmVXb3Jrc3BhY2UiLCJwcm9qZWN0IiwicmVzb2x2ZVByb2plY3QiLCJyZXNvbHZlIiwiZ2V0U291cmNlRW50cnlQYXRoIiwiYnVpbGRGaWxlSW5mb01hcCIsImludmFsaWRhdGVBbGxTb3VyY2VGaWxlcyIsIndhdGNoIiwicHJvamVjdEZpbGVDaGFuZ2VkJCIsInN1YnNjcmliZSIsIm9uRmlsZUNoYW5nZSIsInRyYWNlIiwiYm9vdEVudHJ5IiwiZW1pdCIsImRpc3Bvc2UiLCJ1bnN1YnNjcmliZSIsIlN5bWJvbCIsImRpc3Bvc2VFbnRyeSIsImFzeW5jRGlzcG9zZSIsInZlcnNpb25Nb2R1bGUiLCJwYXRoVG9GaWxlVXJsIiwiZGVmYXVsdCIsImVycm9yIiwicmVib290Iiwic2NoZWR1bGVSZWJvb3QiLCJjcmVhdGUiLCJyb290RGlyIiwicHJvY2VzcyIsImN3ZCIsInByb2plY3RzIiwiZ2V0UHJvamVjdHMiLCJmb3VuZCIsImF0IiwiZmluZCIsInAiLCJFcnJvciIsImVudHJpZXMiLCJQcm9taXNlIiwiYWxsIiwibWFwIiwiZmlsZXMiLCJnZXRBbGxGaWxlcyIsImZpbGUiLCJmdWxsUGF0aCIsImZsYXQiLCJzcGVjaWZpZXIiLCJjb250ZXh0IiwibmV4dCIsInBhcmVudFVybCIsInBhcmVudFVSTCIsInJld3JpdHRlbiIsInJld3JpdGVKc1RvVHMiLCJyZXN1bHQiLCJvblBvc3RSZXNvbHZlIiwibG9hZCIsImxvYWRUcyIsImZvcm1hdCIsInNvdXJjZSIsInNob3J0Q2lyY3VpdCIsImVuZHNXaXRoIiwicGFyZW50Q2xlYW4iLCJzcGxpdCIsImpzUGF0aCIsInJlc29sdmVkIiwiVVJMIiwidG9TdHJpbmciLCJ0c1BhdGgiLCJyZXBsYWNlIiwiaGFzIiwiZ2V0Q2FjaGVkT3JOZXciLCJwYXJlbnRVcmxDbGVhbiIsImZpbGVQYXRoIiwicGFyZW50UGF0aCIsInVybE9iaiIsInNlYXJjaCIsImlzU291cmNlIiwicGFyZW50SXNTb3VyY2UiLCJ0cmFja0RlcGVuZGVuY3kiLCJzdGFydHNXaXRoIiwiY2xlYW5QYXRoIiwicGF0aG5hbWUiLCJmaWxlSW5mbyIsImdldCIsImNhY2hlZCIsInRzQ29uZmlnIiwiZ2V0VHNDb25maWdTeW5jIiwidXNlckNvbmZpZyIsImdldFN3Y0NvbmZpZ1N5bmMiLCJyZXNwb25zZSIsInRyYW5zZm9ybUZpbGVTeW5jIiwiZXM2IiwiYWxpYXNQYXRocyIsIm91dGNvbWUiLCJzZXQiLCJjb2RlIiwiZXZ0IiwiZXZlbnROYW1lIiwiV2F0Y2hFdmVudHMiLCJ1bmxpbmsiLCJkZWxldGUiLCJhZGQiLCJpc1NvdXJjZUZpbGUiLCJyZWxvYWRJZkFmZmVjdGVkIiwiaW52YWxpZGF0ZUNvbnRlbnRDYWNoZSIsImRlcHMiLCJnZXREZXBlbmRlbmN5R3JhcGgiLCJtb2R1bGVzIiwiU2V0IiwiaW5jbHVkZXMiLCJsZW5ndGgiLCJpbnZhbGlkYXRlTW9kdWxlIiwibW9kdWxlSWQiLCJwYXJlbnRNb2R1bGVzIiwiaXNTZWxmIiwibW9kdWxlRGVwcyIsInN1YkRlcHMiLCJkZXAiLCJzdWJzIiwidXBkYXRlZERlcHMiLCJtb2R1bGVVcmwiLCJ2ZXJzaW9uZWQiLCJ2ZXJzaW9uZWRVcmwiLCJzZWFyY2hQYXJhbXMiLCJEYXRlIiwibm93IiwibW9kdWxlIiwia2V5IiwicGFyZW50cyIsImZpbHRlcmVkIiwiZmlsdGVyIiwia2V5cyJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsWUFBWSxRQUFRLGNBQWM7QUFPM0MsU0FBU0MsYUFBYSxRQUFRLGNBQWM7QUFDNUMsT0FBT0MsVUFBVSxZQUFZO0FBQzdCLFNBQVNDLFlBQVksRUFBRUMsYUFBYSxFQUFFQyxpQkFBaUIsUUFBUSxrQkFBa0I7QUFFakYsU0FBU0MsZ0JBQWdCLFFBQVEsaUJBQWlCO0FBQ2xELFNBQVNDLE1BQU0sRUFBRUMsRUFBRSxRQUFRLHFCQUFxQjtBQUNoRCxTQUFTQyxRQUFRLFFBQVEseUJBQXlCO0FBQ2xELFNBQXNCQyxNQUFNLFFBQVEsbUJBQW1CO0FBS3ZELE1BQU1DLGtCQUFrQkgsR0FBR0ksYUFBYSxDQUFDLFlBQVlDLEdBQUc7QUFDeEQsTUFBTUMsb0JBQW9CO0FBQzFCLE1BQU1DLHFCQUFxQjtBQXFCM0IsT0FBTyxNQUFNQyx3QkFBd0JoQjtJQUNwQyw0RUFBNEU7SUFDM0RpQixTQUFpQlAsT0FBT08sTUFBTSxDQUFDRCxnQkFBZ0JFLElBQUksRUFBRTtJQUM5REMsV0FBVyxNQUFNO0lBQ2pCQyxrQkFBa0IsTUFBTTtJQUN4QkMsZ0JBQXlCO0lBQ3pCQyxZQUE4QjtJQUM5QkMsVUFBc0I7SUFDdEJDLGtCQUFpQztJQUN6QywyREFBMkQsR0FDM0QsQUFBUUMsY0FBYyxJQUFJQyxNQUF3QjtJQUMxQ0MsY0FBYyxJQUFJRCxNQUE2QjtJQUMvQ0UsZUFBZSxJQUFJRixNQUF3QjtJQUMzQ0cs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
|