@decohere/flux 0.0.1-dev.125 → 0.0.1-dev.13

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