@decohere/flux 0.0.1-dev.20 → 0.0.1-dev.20261105.110

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