@decohere/flux 0.0.1-dev.10 → 0.0.1-dev.100

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