sliftutils 1.7.12 → 1.7.14

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 (53) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/index.d.ts +95 -39
  3. package/misc/dist/yaml.ts.cache +2 -2
  4. package/misc/dist/yamlBase.ts.cache +2 -2
  5. package/misc/https/hostServer.d.ts +7 -0
  6. package/misc/https/hostServer.ts +89 -20
  7. package/misc/openrouter.ts +17 -14
  8. package/package.json +2 -2
  9. package/storage/ArchivesDisk.d.ts +1 -1
  10. package/storage/ArchivesDisk.ts +2 -1
  11. package/storage/IArchives.d.ts +5 -1
  12. package/storage/IArchives.ts +5 -1
  13. package/storage/backblaze.d.ts +1 -1
  14. package/storage/backblaze.ts +9 -6
  15. package/storage/dist/embeddingFormats.ts.cache +2 -2
  16. package/storage/proxydatabase/dist/Database.ts.cache +2 -2
  17. package/storage/proxydatabase/dist/ivfEmbeddingDatabase.ts.cache +123 -61
  18. package/storage/proxydatabase/dist/transactionSet.ts.cache +16 -4
  19. package/storage/remoteStorage/ArchivesRemote.d.ts +1 -1
  20. package/storage/remoteStorage/ArchivesRemote.ts +2 -1
  21. package/storage/remoteStorage/ArchivesUrl.d.ts +1 -1
  22. package/storage/remoteStorage/ArchivesUrl.ts +13 -9
  23. package/storage/remoteStorage/blobStore.d.ts +6 -2
  24. package/storage/remoteStorage/blobStore.ts +46 -6
  25. package/storage/remoteStorage/createArchives.d.ts +26 -11
  26. package/storage/remoteStorage/createArchives.ts +104 -28
  27. package/storage/remoteStorage/deployTakeover.d.ts +24 -0
  28. package/storage/remoteStorage/deployTakeover.ts +373 -0
  29. package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +16 -11
  30. package/storage/remoteStorage/dist/blobStore.ts.cache +39 -4
  31. package/storage/remoteStorage/dist/createArchives.ts.cache +91 -9
  32. package/storage/remoteStorage/dist/deployTakeover.ts.cache +371 -0
  33. package/storage/remoteStorage/dist/remoteConfig.ts.cache +9 -3
  34. package/storage/remoteStorage/dist/sourceWrapper.ts.cache +3 -3
  35. package/storage/remoteStorage/dist/storageController.ts.cache +11 -12
  36. package/storage/remoteStorage/dist/storageServerState.ts.cache +120 -19
  37. package/storage/remoteStorage/remoteConfig.d.ts +1 -0
  38. package/storage/remoteStorage/remoteConfig.ts +6 -0
  39. package/storage/remoteStorage/storageController.d.ts +2 -0
  40. package/storage/remoteStorage/storageController.ts +9 -9
  41. package/storage/remoteStorage/storageServer.ts +14 -1
  42. package/storage/remoteStorage/storageServerState.d.ts +16 -1
  43. package/storage/remoteStorage/storageServerState.ts +118 -16
  44. package/yarn.lock +4 -4
  45. package/storage/dist/faceFramesData.ts.cache +0 -55
  46. package/storage/embeddingBench.d.ts +0 -1
  47. package/storage/embeddingBench.ts +0 -152
  48. package/storage/faceFramesData.d.ts +0 -1
  49. package/storage/faceFramesData.ts +0 -49
  50. package/storage/faceFramesServer.d.ts +0 -1
  51. package/storage/faceFramesServer.ts +0 -56
  52. package/storage/proxydatabase/ivfDbCheck.d.ts +0 -1
  53. package/storage/proxydatabase/ivfDbCheck.ts +0 -85
@@ -0,0 +1,373 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { exec } from "child_process";
4
+ import { RemoteConfig, RemoteConfigBase, HostedConfig } from "../IArchives";
5
+ import { parseHostedUrl, replaceHostedUrlPort } from "./remoteConfig";
6
+
7
+ // Deploy takeover: the deploy manager (querysub's parametersTimeline) writes <n>-parameters.json
8
+ // files into our instance folder describing when each version runs and when it is shut down. Old
9
+ // and new version overlap for the overlap time (O = oldEnd - newStart). We turn that overlap into
10
+ // an atomic write handoff by REINTERPRETING every routing source that points at us: its valid
11
+ // window is split so that the middle of the overlap ([S + 0.5*O, S + 1.5*O]) points at the new
12
+ // process's temporary alternate port. The existing valid-window machinery then does everything -
13
+ // the old process starts rejecting fresh writes at the first boundary, clients re-resolve, and the
14
+ // new process (which recognizes the alternate port as itself) takes over. The remap is an
15
+ // INTERPRETATION OVERLAY, applied where routing configs are used (see applyDeployRemap callers) -
16
+ // it is never written to disk, never synchronized, and never part of any stored configuration.
17
+ //
18
+ // Everything here is recomputed idempotently from its inputs (timeline files, the port registry,
19
+ // our ancestor pids, the clock) - files can appear or change at any moment, and we can start at
20
+ // any point during a switchover.
21
+
22
+ const PARAMETERS_TIMELINE_FILE_REGEX = /^(\d+)-parameters\.json$/;
23
+ const ALIVE_WINDOW_FOREVER = Number.MAX_SAFE_INTEGER;
24
+ const TIMELINE_POLL_INTERVAL = 60 * 1000;
25
+ // Processes listening on an alternate port register it here (sibling of the storage folder), keyed
26
+ // by their actual pid; content includes their ancestor pids so the timeline entry pid (a shell
27
+ // ancestor of the process) can be matched to a registry entry
28
+ const PORT_REGISTRY_FOLDER_NAME = "storagePortRegistry";
29
+ // Main-port acquisition attempts run this often, tightening near the predecessor's scheduled death
30
+ const ACQUIRE_SLOW_DELAY = 30 * 1000;
31
+ const ACQUIRE_FAST_DELAY = 5 * 1000;
32
+ const ACQUIRE_FAST_WINDOW = 60 * 1000;
33
+ // Disk rescans fire this far before and after the write handoff, catching files the other process
34
+ // wrote (the BulkDatabase2 index is atomic but its entries land at write time, not file time)
35
+ const SWITCH_SCAN_LEAD = 10 * 1000;
36
+ // The dying process must have flushed everything before the handoff: fast-write delays never
37
+ // extend past S + overlap * this fraction, and after that point fast writes flush immediately
38
+ const FLUSH_DEADLINE_FRACTION = 0.25;
39
+ const BOUNDARY_A_FRACTION = 0.5;
40
+ const BOUNDARY_B_FRACTION = 1.5;
41
+ const REMAP_EXPIRE_FRACTION = 2;
42
+
43
+ type TimelineEntry = {
44
+ pid?: number;
45
+ aliveWindow: [number, number];
46
+ parameters: unknown;
47
+ };
48
+
49
+ type TakeoverComputed = {
50
+ // We are scheduled to die with a successor overlapping us (known from the files alone, before
51
+ // the successor's port is discovered - the flush deadline applies immediately)
52
+ dying?: { successorStart: number; overlap: number };
53
+ // The window reinterpretation, once the middle window's port is known (the successor's
54
+ // registered alternate port - or our own, when we ARE the successor)
55
+ remap?: { boundaryA: number; boundaryB: number; expire: number; altPort: number };
56
+ // When our predecessor dies and frees the main port (for acquisition pacing)
57
+ predecessorEnd?: number;
58
+ };
59
+
60
+ export type TakeoverEvent = "remapChanged" | "diskScan";
61
+
62
+ let initialized: { domain: string; mainPort: number; storageFolder: string } | undefined;
63
+ let ancestorPids = new Set<number>();
64
+ let timelineFolder: string | undefined;
65
+ let ourAltPort: number | undefined;
66
+ let current: TakeoverComputed = {};
67
+ let currentKey = JSON.stringify(current);
68
+ let eventListeners: ((event: TakeoverEvent) => void)[] = [];
69
+ let eventTimers: ReturnType<typeof setTimeout>[] = [];
70
+
71
+ function execText(command: string): Promise<string> {
72
+ return new Promise((resolve, reject) => {
73
+ exec(command, (error, stdout) => {
74
+ if (error) {
75
+ reject(error);
76
+ return;
77
+ }
78
+ resolve(stdout);
79
+ });
80
+ });
81
+ }
82
+
83
+ // The timeline entry pid is the screen shell that spawned us - an ancestor - so we walk our whole
84
+ // ancestor chain to find which entry is us
85
+ async function getAncestorPids(): Promise<Set<number>> {
86
+ let pids = new Set<number>([process.pid]);
87
+ let currentPid = process.pid;
88
+ while (true) {
89
+ let ppid: number;
90
+ if (currentPid === process.pid && process.ppid) {
91
+ ppid = process.ppid;
92
+ } else {
93
+ try {
94
+ ppid = parseInt((await execText(`ps -o ppid= -p ${currentPid}`)).trim());
95
+ } catch {
96
+ break;
97
+ }
98
+ }
99
+ if (!ppid || Number.isNaN(ppid) || ppid <= 1 || pids.has(ppid)) break;
100
+ pids.add(ppid);
101
+ currentPid = ppid;
102
+ }
103
+ return pids;
104
+ }
105
+
106
+ async function findTimelineFolder(): Promise<string | undefined> {
107
+ let dir = process.cwd();
108
+ while (true) {
109
+ let files: string[] = [];
110
+ try {
111
+ files = await fs.promises.readdir(dir);
112
+ } catch { }
113
+ if (files.some(file => PARAMETERS_TIMELINE_FILE_REGEX.test(file))) {
114
+ return dir.replaceAll("\\", "/") + "/";
115
+ }
116
+ let parent = path.dirname(dir);
117
+ if (parent === dir) return undefined;
118
+ dir = parent;
119
+ }
120
+ }
121
+
122
+ function pidAlive(pid: number): boolean {
123
+ try {
124
+ process.kill(pid, 0);
125
+ return true;
126
+ } catch (e) {
127
+ return (e as { code?: string }).code === "EPERM";
128
+ }
129
+ }
130
+
131
+ function getRegistryFolder(): string {
132
+ if (!initialized) {
133
+ throw new Error(`Deploy takeover is not initialized`);
134
+ }
135
+ return path.join(path.dirname(path.resolve(initialized.storageFolder)), PORT_REGISTRY_FOLDER_NAME);
136
+ }
137
+
138
+ type RegistryEntry = { pid: number; port: number; ancestorPids: number[] };
139
+
140
+ async function readPortRegistry(): Promise<RegistryEntry[]> {
141
+ let folder = getRegistryFolder();
142
+ let files: string[] = [];
143
+ try {
144
+ files = await fs.promises.readdir(folder);
145
+ } catch {
146
+ return [];
147
+ }
148
+ let entries: RegistryEntry[] = [];
149
+ for (let file of files) {
150
+ let match = /^(\d+)\.json$/.exec(file);
151
+ if (!match) continue;
152
+ let pid = parseInt(match[1]);
153
+ if (!pidAlive(pid)) {
154
+ try {
155
+ await fs.promises.unlink(path.join(folder, file));
156
+ } catch { }
157
+ continue;
158
+ }
159
+ try {
160
+ let entry = JSON.parse(await fs.promises.readFile(path.join(folder, file), "utf8")) as RegistryEntry;
161
+ entries.push({ ...entry, pid });
162
+ } catch { }
163
+ }
164
+ return entries;
165
+ }
166
+
167
+ async function readTimelineEntries(): Promise<TimelineEntry[]> {
168
+ if (!timelineFolder) return [];
169
+ let entries: TimelineEntry[] = [];
170
+ for (let file of await fs.promises.readdir(timelineFolder)) {
171
+ if (!PARAMETERS_TIMELINE_FILE_REGEX.test(file)) continue;
172
+ try {
173
+ entries.push(JSON.parse(await fs.promises.readFile(timelineFolder + file, "utf8")) as TimelineEntry);
174
+ } catch (e) {
175
+ console.warn(`Could not read deploy timeline file ${timelineFolder + file}: ${(e as Error).stack ?? e}`);
176
+ }
177
+ }
178
+ return entries;
179
+ }
180
+
181
+ function makeRemap(successorStart: number, overlap: number, altPort: number): TakeoverComputed["remap"] {
182
+ return {
183
+ boundaryA: successorStart + overlap * BOUNDARY_A_FRACTION,
184
+ boundaryB: successorStart + overlap * BOUNDARY_B_FRACTION,
185
+ expire: successorStart + overlap * REMAP_EXPIRE_FRACTION,
186
+ altPort,
187
+ };
188
+ }
189
+
190
+ function emit(event: TakeoverEvent): void {
191
+ for (let listener of eventListeners) {
192
+ try {
193
+ listener(event);
194
+ } catch (e) {
195
+ console.error(`Deploy takeover listener failed: ${(e as Error).stack ?? e}`);
196
+ }
197
+ }
198
+ }
199
+
200
+ function scheduleEvents(): void {
201
+ for (let timer of eventTimers) {
202
+ clearTimeout(timer);
203
+ }
204
+ eventTimers = [];
205
+ let now = Date.now();
206
+ let schedule = (time: number, fnc: () => void) => {
207
+ if (time <= now) return;
208
+ let timer = setTimeout(fnc, time - now);
209
+ (timer as { unref?: () => void }).unref?.();
210
+ eventTimers.push(timer);
211
+ };
212
+ let remap = current.remap;
213
+ if (remap) {
214
+ schedule(remap.boundaryA - SWITCH_SCAN_LEAD, () => emit("diskScan"));
215
+ schedule(remap.boundaryA + SWITCH_SCAN_LEAD, () => emit("diskScan"));
216
+ schedule(remap.expire, () => void recompute().catch((e: Error) => console.error(`Deploy takeover recompute failed: ${e.stack ?? e}`)));
217
+ }
218
+ }
219
+
220
+ async function recompute(): Promise<void> {
221
+ if (!initialized || !timelineFolder) return;
222
+ let now = Date.now();
223
+ let entries = await readTimelineEntries();
224
+ let registry = await readPortRegistry();
225
+ let us: TimelineEntry | undefined;
226
+ for (let entry of entries) {
227
+ if (entry.pid && ancestorPids.has(entry.pid)) {
228
+ us = entry;
229
+ }
230
+ }
231
+ let computed: TakeoverComputed = {};
232
+ if (us) {
233
+ let usStart = us.aliveWindow[0];
234
+ let next: TimelineEntry | undefined;
235
+ let prev: TimelineEntry | undefined;
236
+ for (let entry of entries) {
237
+ if (entry === us) continue;
238
+ if (entry.aliveWindow[0] > usStart && (!next || entry.aliveWindow[0] < next.aliveWindow[0])) {
239
+ next = entry;
240
+ }
241
+ if (entry.aliveWindow[0] < usStart && (!prev || entry.aliveWindow[0] > prev.aliveWindow[0])) {
242
+ prev = entry;
243
+ }
244
+ }
245
+ if (next && us.aliveWindow[1] !== ALIVE_WINDOW_FOREVER && next.aliveWindow[0] <= us.aliveWindow[1]) {
246
+ let overlap = us.aliveWindow[1] - next.aliveWindow[0];
247
+ let successorStart = next.aliveWindow[0];
248
+ if (overlap > 0 && now < successorStart + overlap * REMAP_EXPIRE_FRACTION) {
249
+ computed.dying = { successorStart, overlap };
250
+ const successorPid = next.pid;
251
+ if (successorPid) {
252
+ let successorEntry = registry.find(x => x.ancestorPids.includes(successorPid));
253
+ if (successorEntry) {
254
+ computed.remap = makeRemap(successorStart, overlap, successorEntry.port);
255
+ }
256
+ }
257
+ }
258
+ }
259
+ if (prev && prev.aliveWindow[1] > usStart) {
260
+ let overlap = prev.aliveWindow[1] - usStart;
261
+ computed.predecessorEnd = prev.aliveWindow[1];
262
+ if (overlap > 0 && ourAltPort && now < usStart + overlap * REMAP_EXPIRE_FRACTION && !computed.remap) {
263
+ computed.remap = makeRemap(usStart, overlap, ourAltPort);
264
+ }
265
+ }
266
+ }
267
+ let key = JSON.stringify(computed);
268
+ if (key === currentKey) return;
269
+ current = computed;
270
+ currentKey = key;
271
+ console.log(`Deploy takeover state changed: ${key}`);
272
+ scheduleEvents();
273
+ emit("remapChanged");
274
+ }
275
+
276
+ /** Starts the takeover machinery. Returns whether a deploy timeline exists - when it doesn't,
277
+ * everything here stays inert (and the caller must NOT fall back to alternate ports, since a busy
278
+ * port then means a real misconfiguration, not a deploy). */
279
+ export async function initDeployTakeover(config: { domain: string; mainPort: number; storageFolder: string }): Promise<boolean> {
280
+ initialized = config;
281
+ ancestorPids = await getAncestorPids();
282
+ timelineFolder = await findTimelineFolder();
283
+ if (!timelineFolder) return false;
284
+ await recompute();
285
+ let poll = setInterval(() => {
286
+ void recompute().catch((e: Error) => console.error(`Deploy takeover recompute failed: ${e.stack ?? e}`));
287
+ }, TIMELINE_POLL_INTERVAL);
288
+ (poll as { unref?: () => void }).unref?.();
289
+ return true;
290
+ }
291
+
292
+ /** Called when we had to listen on an alternate port (the main port was still held by our
293
+ * predecessor): registers it so the predecessor can route the middle overlap window to us. */
294
+ export async function registerAltPort(port: number): Promise<void> {
295
+ ourAltPort = port;
296
+ let folder = getRegistryFolder();
297
+ await fs.promises.mkdir(folder, { recursive: true });
298
+ let entry: RegistryEntry = { pid: process.pid, port, ancestorPids: [...ancestorPids] };
299
+ await fs.promises.writeFile(path.join(folder, `${process.pid}.json`), JSON.stringify(entry));
300
+ await recompute();
301
+ }
302
+
303
+ export function onTakeoverEvent(listener: (event: TakeoverEvent) => void): void {
304
+ eventListeners.push(listener);
305
+ }
306
+
307
+ /** The interpretation overlay: splits every source pointing at our domain+main port so the middle
308
+ * of the deploy overlap points at the alternate port. Pure, in-memory only - the stored routing
309
+ * config is never modified, and this must never be applied to data that gets persisted. */
310
+ export function applyDeployRemap(routing: RemoteConfig): RemoteConfig {
311
+ let remap = current.remap;
312
+ let init = initialized;
313
+ if (!remap || !init) return routing;
314
+ let sources: RemoteConfigBase[] = [];
315
+ let slice = (source: HostedConfig, start: number, end: number, url?: string): HostedConfig => {
316
+ return { ...source, url: url || source.url, validWindow: [start, end] };
317
+ };
318
+ for (let source of routing.sources) {
319
+ if (typeof source === "string" || source.type !== "remote") {
320
+ sources.push(source);
321
+ continue;
322
+ }
323
+ let parsed: { address: string; port: number };
324
+ try {
325
+ parsed = parseHostedUrl(source.url);
326
+ } catch {
327
+ sources.push(source);
328
+ continue;
329
+ }
330
+ if (parsed.address !== init.domain || parsed.port !== init.mainPort) {
331
+ sources.push(source);
332
+ continue;
333
+ }
334
+ let [start, end] = source.validWindow;
335
+ let a = Math.max(start, Math.min(remap.boundaryA, end));
336
+ let b = Math.max(start, Math.min(remap.boundaryB, end));
337
+ let added = false;
338
+ if (a > start) {
339
+ sources.push(slice(source, start, a));
340
+ added = true;
341
+ }
342
+ if (b > a) {
343
+ sources.push(slice(source, a, b, replaceHostedUrlPort(source.url, remap.altPort)));
344
+ added = true;
345
+ }
346
+ if (end > b) {
347
+ sources.push(slice(source, b, end));
348
+ added = true;
349
+ }
350
+ if (!added) {
351
+ sources.push(source);
352
+ }
353
+ }
354
+ return { version: routing.version, sources };
355
+ }
356
+
357
+ /** For the dying process: fast-write flush delays must never extend past this time, and after it
358
+ * fast writes flush immediately - so nothing is left in memory when the write window transfers. */
359
+ export function getFlushDeadline(): number | undefined {
360
+ let dying = current.dying;
361
+ if (!dying) return undefined;
362
+ return dying.successorStart + dying.overlap * FLUSH_DEADLINE_FRACTION;
363
+ }
364
+
365
+ /** How long to wait between main-port acquisition attempts: tight around the predecessor's
366
+ * scheduled death (when the port actually frees), relaxed otherwise. */
367
+ export function getMainPortAcquireDelay(): number {
368
+ let end = current.predecessorEnd;
369
+ if (end !== undefined && Math.abs(Date.now() - end) <= ACQUIRE_FAST_WINDOW) {
370
+ return ACQUIRE_FAST_DELAY;
371
+ }
372
+ return ACQUIRE_SLOW_DELAY;
373
+ }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
3
  //exports.ArchivesUrl = void 0;
4
4
  module.allowclient = true;
5
+ const https_1 = require("socket-function/src/https");
5
6
  const remoteConfig_1 = require("./remoteConfig");
6
7
  // Read-only IArchives over a public bucket's plain-URL form (our storage server's
7
8
  // /file/<account>/<bucketName>/... route, or a backblaze friendly URL). Used when we have no API
@@ -28,26 +29,30 @@ class ArchivesUrl {
28
29
  if (range) {
29
30
  headers["Range"] = `bytes=${range.start}-${range.end - 1}`;
30
31
  }
31
- let response = await fetch(url, { headers });
32
- if (response.status === 404)
33
- return undefined;
34
- if (!response.ok) {
35
- throw new Error(`Read of ${url} failed: ${response.status} ${response.statusText}`);
32
+ let response = { headers: {} };
33
+ let data;
34
+ try {
35
+ data = await (0, https_1.httpsRequest)(url, undefined, "GET", false, { headers, outResponse: response });
36
+ }
37
+ catch (e) {
38
+ // httpsRequest throws on any non-2xx; a missing file is a normal absent result, not an error.
39
+ if (response.statusCode === 404)
40
+ return undefined;
41
+ throw e;
36
42
  }
37
- let data = Buffer.from(await response.arrayBuffer());
38
43
  // A real 206 only returns the requested slice, so the full size lives in Content-Range's total.
39
44
  let size = data.length;
40
- let contentRange = response.headers.get("content-range");
45
+ let contentRange = response.headers["content-range"];
41
46
  let total = contentRange && Number(contentRange.split("/")[1]);
42
47
  if (total && Number.isFinite(total)) {
43
48
  size = total;
44
49
  }
45
50
  // Servers that don't support ranges return the full file with a 200 (ours serves real
46
51
  // 206s, but backblaze friendly URLs and proxies may not)
47
- if (range && response.status === 200) {
52
+ if (range && response.statusCode === 200) {
48
53
  data = data.subarray(Math.min(range.start, data.length), Math.min(range.end, data.length));
49
54
  }
50
- let lastModified = response.headers.get("last-modified");
55
+ let lastModified = response.headers["last-modified"];
51
56
  let writeTime = lastModified && new Date(lastModified).getTime() || 0;
52
57
  return { data, writeTime, size };
53
58
  }
@@ -81,5 +86,5 @@ class ArchivesUrl {
81
86
  }
82
87
  }
83
88
  exports.ArchivesUrl = ArchivesUrl;
84
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQXJjaGl2ZXNVcmwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJBcmNoaXZlc1VybC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxNQUFNLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztBQUcxQixpREFBOEM7QUFFOUMsa0ZBQWtGO0FBQ2xGLGlHQUFpRztBQUNqRyxpR0FBaUc7QUFFakcsTUFBYSxXQUFXO0lBQ3BCLDJGQUEyRjtJQUMzRixZQUFvQixJQUFZO1FBQVosU0FBSSxHQUFKLElBQUksQ0FBUTtJQUFJLENBQUM7SUFFOUIsWUFBWTtRQUNmLE9BQU8sT0FBTyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDOUIsQ0FBQztJQUVPLGFBQWEsQ0FBQyxTQUFpQjtRQUNuQyxPQUFPLElBQUksS0FBSyxDQUFDLEdBQUcsU0FBUyx5R0FBeUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdkosQ0FBQztJQUVNLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBZ0IsRUFBRSxNQUFtRDtRQUNsRixJQUFJLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQy9DLE9BQU8sTUFBTSxJQUFJLE1BQU0sQ0FBQyxJQUFJLElBQUksU0FBUyxDQUFDO0lBQzlDLENBQUM7SUFDTSxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQWdCLEVBQUUsTUFBbUQ7UUFDbkYsSUFBSSxHQUFHLEdBQUcsSUFBQSwyQkFBWSxFQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDNUMsSUFBSSxPQUFPLEdBQTJCLEVBQUUsQ0FBQztRQUN6QyxJQUFJLEtBQUssR0FBRyxNQUFNLGFBQU4sTUFBTSx1QkFBTixNQUFNLENBQUUsS0FBSyxDQUFDO1FBQzFCLElBQUksS0FBSyxFQUFFLENBQUM7WUFDUixPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsU0FBUyxLQUFLLENBQUMsS0FB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TUFBYyxFQUFFLE1BQXlEO1FBQzNGLE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFTSxLQUFLLENBQUMsTUFBTSxDQUFDLElBQVk7UUFDNUIsT0FBTyxJQUFBLDJCQUFZLEVBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQ00sS0FBSyxDQUFDLFNBQVM7UUFDbEIsT0FBTyxFQUFFLENBQUM7SUFDZCxDQUFDO0lBRU0sS0FBSyxDQUFDLGNBQWM7UUFDdkIsT0FBTyxLQUFLLENBQUM7SUFDakIsQ0FBQztDQUNKO0FBNUVELGtDQTRFQyIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5hbGxvd2NsaWVudCA9IHRydWU7XG5cbmltcG9ydCB7IElBcmNoaXZlcywgQXJjaGl2ZUZpbGVJbmZvLCBBcmNoaXZlc0NvbmZpZyB9IGZyb20gXCIuLi9JQXJjaGl2ZXNcIjtcbmltcG9ydCB7IGJ1aWxkRmlsZVVybCB9IGZyb20gXCIuL3JlbW90ZUNvbmZpZ1wiO1xuXG4vLyBSZWFkLW9ubHkgSUFyY2hpdmVzIG92ZXIgYSBwdWJsaWMgYnVja2V0J3MgcGxhaW4tVVJMIGZvcm0gKG91ciBzdG9yYWdlIHNlcnZlcidzXG4vLyAvZmlsZS88YWNjb3VudD4vPGJ1Y2tldE5hbWU+Ly4uLiByb3V0ZSwgb3IgYSBiYWNrYmxhemUgZnJpZW5kbHkgVVJMKS4gVXNlZCB3aGVuIHdlIGhhdmUgbm8gQVBJXG4vLyBhY2Nlc3MgdG8gYSBzb3VyY2UuIE9ubHkgc2luZ2xlLWZpbGUgcmVhZHMgd29yazogdGhlcmUgaXMgbm8gbGlzdGluZywgYW5kIHdyaXRlcyBhbHdheXMgdGhyb3cuXG5cbmV4cG9ydCBjbGFzcyBBcmNoaXZlc1VybCBpbXBsZW1lbnRzIElBcmNoaXZlcyB7XG4gICAgLy8gYmFzZSBpcyB0aGUgYnVja2V0J3MgcHVibGljIGJhc2UgVVJMLCBlLmcuIGh0dHBzOi8vaG9zdDpwb3J0L2ZpbGUvPGFjY291bnQ+LzxidWNrZXROYW1lPlxuICAgIGNvbnN0cnVjdG9yKHByaXZhdGUgYmFzZTogc3RyaW5nKSB7IH1cblxuICAgIHB1YmxpYyBnZXREZWJ1Z05hbWUoKSB7XG4gICAgICAgIHJldHVybiBgdXJsLyR7dGhpcy5iYXNlfWA7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZWFkT25seUVycm9yKG9wZXJhdGlvbjogc3RyaW5nKTogRXJyb3Ige1xuICAgICAgICByZXR1cm4gbmV3IEVycm9yKGAke29wZXJhdGlvbn0gaXMgbm90IHN1cHBvcnRlZCBvdmVyIFVSTC1mb3JtIGFjY2VzcyAobm8gQVBJIGFjY2VzcyB0byB0aGlzIHNvdXJjZSwgb25seSBwdWJsaWMgVVJMIHJlYWRzKS4gU291cmNlOiAke3RoaXMuYmFzZX1gKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgZ2V0KGZpbGVOYW1lOiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8QnVmZmVyIHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGxldCByZXN1bHQgPSBhd2FpdCB0aGlzLmdldDIoZmlsZU5hbWUsIGNvbmZpZyk7XG4gICAgICAgIHJldHVybiByZXN1bHQgJiYgcmVzdWx0LmRhdGEgfHwgdW5kZWZpbmVkO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgZ2V0MihmaWxlTmFtZTogc3RyaW5nLCBjb25maWc/OiB7IHJhbmdlPzogeyBzdGFydDogbnVtYmVyOyBlbmQ6IG51bWJlciB9IH0pOiBQcm9taXNlPHsgZGF0YTogQnVmZmVyOyB3cml0ZVRpbWU6IG51bWJlcjsgc2l6ZTogbnVtYmVyIH0gfCB1bmRlZmluZWQ+IHtcbiAgICAgICAgbGV0IHVybCA9IGJ1aWxkRmlsZVVybCh0aGlzLmJhc2UsIGZpbGVOYW1lKTtcbiAgICAgICAgbGV0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcbiAgICAgICAgbGV0IHJhbmdlID0gY29uZmlnPy5yYW5nZTtcbiAgICAgICAgaWYgKHJhbmdlKSB7XG4gICAgICAgICAgICBoZWFkZXJzW1wiUmFuZ2VcIl0gPSBgYnl0ZXM9JHtyYW5nZS5zdGFydH0tJHtyYW5nZS5lbmQgLSAxfWA7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICAgIGlmIChyZXNwb25zZS5zdGF0dXMgPT09IDQwNCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBSZWFkIG9mICR7dXJsfSBmYWlsZWQ6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGRhdGEgPSBCdWZmZXIuZnJvbShhd2FpdCByZXNwb25zZS5hcnJheUJ1ZmZlcigpKTtcbiAgICAgICAgLy8gQSByZWFsIDIwNiBvbmx5IHJldHVybnMgdGhlIHJlcXVlc3RlZCBzbGljZSwgc28gdGhlIGZ1bGwgc2l6ZSBsaXZlcyBpbiBDb250ZW50LVJhbmdlJ3MgdG90YWwuXG4gICAgICAgIGxldCBzaXplID0gZGF0YS5sZW5ndGg7XG4gICAgICAgIGxldCBjb250ZW50UmFuZ2UgPSByZXNwb25zZS5oZWFkZXJzLmdldChcImNvbnRlbnQtcmFuZ2VcIik7XG4gICAgICAgIGxldCB0b3RhbCA9IGNvbnRlbnRSYW5nZSAmJiBOdW1iZXIoY29udGVudFJhbmdlLnNwbGl0KFwiL1wiKVsxXSk7XG4gICAgICAgIGlmICh0b3RhbCAmJiBOdW1iZXIuaXNGaW5pdGUodG90YWwpKSB7XG4gICAgICAgICAgICBzaXplID0gdG90YWw7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU2VydmVycyB0aGF0IGRvbid0IHN1cHBvcnQgcmFuZ2VzIHJldHVybiB0aGUgZnVsbCBmaWxlIHdpdGggYSAyMDAgKG91cnMgc2VydmVzIHJlYWxcbiAgICAgICAgLy8gMjA2cywgYnV0IGJhY2tibGF6ZSBmcmllbmRseSBVUkxzIGFuZCBwcm94aWVzIG1heSBub3QpXG4gICAgICAgIGlmIChyYW5nZSAmJiByZXNwb25zZS5zdGF0dXMgPT09IDIwMCkge1xuICAgICAgICAgICAgZGF0YSA9IGRhdGEuc3ViYXJyYXkoTWF0aC5taW4ocmFuZ2Uuc3RhcnQsIGRhdGEubGVuZ3RoKSwgTWF0aC5taW4ocmFuZ2UuZW5kLCBkYXRhLmxlbmd0aCkpO1xuICAgICAgICB9XG4gICAgICAgIGxldCBsYXN0TW9kaWZpZWQgPSByZXNwb25zZS5oZWFkZXJzLmdldChcImxhc3QtbW9kaWZpZWRcIik7XG4gICAgICAgIGxldCB3cml0ZVRpbWUgPSBsYXN0TW9kaWZpZWQgJiYgbmV3IERhdGUobGFzdE1vZGlmaWVkKS5nZXRUaW1lKCkgfHwgMDtcbiAgICAgICAgcmV0dXJuIHsgZGF0YSwgd3JpdGVUaW1lLCBzaXplIH07XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBnZXRJbmZvKGZpbGVOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlciB9IHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGxldCByZXN1bHQgPSBhd2FpdCB0aGlzLmdldDIoZmlsZU5hbWUpO1xuICAgICAgICByZXR1cm4gcmVzdWx0ICYmIHsgd3JpdGVUaW1lOiByZXN1bHQud3JpdGVUaW1lLCBzaXplOiByZXN1bHQuc2l6ZSB9IHx8IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2V0KGZpbGVOYW1lOiBzdHJpbmcsIGRhdGE6IEJ1ZmZlciwgY29uZmlnPzogeyBsYXN0TW9kaWZpZWQ/OiBudW1iZXIgfSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aHJvdyB0aGlzLnJlYWRPbmx5RXJyb3IoXCJzZXRcIik7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBkZWwoZmlsZU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aHJvdyB0aGlzLnJlYWRPbmx5RXJyb3IoXCJkZWxcIik7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzZXRMYXJnZUZpbGUoY29uZmlnOiB7IHBhdGg6IHN0cmluZzsgZ2V0TmV4dERhdGEoKTogUHJvbWlzZTxCdWZmZXIgfCB1bmRlZmluZWQ+IH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhyb3cgdGhpcy5yZWFkT25seUVycm9yKFwic2V0TGFyZ2VGaWxlXCIpO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgZmluZChwcmVmaXg6IHN0cmluZywgY29uZmlnPzogeyBzaGFsbG93PzogYm9vbGVhbjsgdHlwZTogXCJmaWxlc1wiIHwgXCJmb2xkZXJzXCIgfSk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICAgICAgdGhyb3cgdGhpcy5yZWFkT25seUVycm9yKFwiZmluZCAobGlzdGluZylcIik7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBmaW5kSW5mbyhwcmVmaXg6IHN0cmluZywgY29uZmlnPzogeyBzaGFsbG93PzogYm9vbGVhbjsgdHlwZTogXCJmaWxlc1wiIHwgXCJmb2xkZXJzXCIgfSk6IFByb21pc2U8QXJjaGl2ZUZpbGVJbmZvW10+IHtcbiAgICAgICAgdGhyb3cgdGhpcy5yZWFkT25seUVycm9yKFwiZmluZEluZm8gKGxpc3RpbmcpXCIpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBnZXRVUkwocGF0aDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgcmV0dXJuIGJ1aWxkRmlsZVVybCh0aGlzLmJhc2UsIHBhdGgpO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgZ2V0Q29uZmlnKCk6IFByb21pc2U8QXJjaGl2ZXNDb25maWc+IHtcbiAgICAgICAgcmV0dXJuIHt9O1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBoYXNXcml0ZUFjY2VzcygpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cbiJdfQ==
85
- /* _JS_SOURCE_HASH = "3b08e87dc92fd46752eef014c02d0b938c7e7ae588bb1d9582446c2f1b980993"; */
89
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQXJjaGl2ZXNVcmwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJBcmNoaXZlc1VybC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxNQUFNLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztBQUUxQixxREFBNEU7QUFFNUUsaURBQThDO0FBRTlDLGtGQUFrRjtBQUNsRixpR0FBaUc7QUFDakcsaUdBQWlHO0FBRWpHLE1BQWEsV0FBVztJQUNwQiwyRkFBMkY7SUFDM0YsWUFBb0IsSUFBWTtRQUFaLFNBQUksR0FBSixJQUFJLENBQVE7SUFBSSxDQUFDO0lBRTlCLFlBQVk7UUFDZixPQUFPLE9BQU8sSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFTyxhQUFhLENBQUMsU0FBaUI7UUFDbkMsT0FBTyxJQUFJLEtBQUssQ0FBQyxHQUFHLFNBQVMseUdBQXlHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZKLENBQUM7SUFFTSxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQWdCLEVBQUUsTUFBbUQ7UUFDbEYsSUFBSSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUMvQyxPQUFPLE1BQU0sSUFBSSxNQUFNLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQztJQUM5QyxDQUFDO0lBQ00sS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFnQixFQUFFLE1BQW1EO1FBQ25GLElBQUksR0FBRyxHQUFHLElBQUEsMkJBQVksRUFBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzVDLElBQUksT0FBTyxHQUEyQixFQUFFLENBQUM7UUFDekMsSUFBSSxLQUFLLEdBQUcsTUFBTSxhQUFOLE1BQU0sdUJBQU4sTUFBTSxDQUFFLEtBQUssQ0FBQztRQUMxQixJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ1IsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLFNBQVMs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QUFFLE1BQXlEO1FBQzNGLE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFTSxLQUFLLENBQUMsTUFBTSxDQUFDLElBQVk7UUFDNUIsT0FBTyxJQUFBLDJCQUFZLEVBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQ00sS0FBSyxDQUFDLFNBQVM7UUFDbEIsT0FBTyxFQUFFLENBQUM7SUFDZCxDQUFDO0lBRU0sS0FBSyxDQUFDLGNBQWM7UUFDdkIsT0FBTyxLQUFLLENBQUM7SUFDakIsQ0FBQztDQUNKO0FBL0VELGtDQStFQyIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5hbGxvd2NsaWVudCA9IHRydWU7XG5cbmltcG9ydCB7IGh0dHBzUmVxdWVzdCwgSHR0cHNSZXNwb25zZUluZm8gfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy9odHRwc1wiO1xuaW1wb3J0IHsgSUFyY2hpdmVzLCBBcmNoaXZlRmlsZUluZm8sIEFyY2hpdmVzQ29uZmlnIH0gZnJvbSBcIi4uL0lBcmNoaXZlc1wiO1xuaW1wb3J0IHsgYnVpbGRGaWxlVXJsIH0gZnJvbSBcIi4vcmVtb3RlQ29uZmlnXCI7XG5cbi8vIFJlYWQtb25seSBJQXJjaGl2ZXMgb3ZlciBhIHB1YmxpYyBidWNrZXQncyBwbGFpbi1VUkwgZm9ybSAob3VyIHN0b3JhZ2Ugc2VydmVyJ3Ncbi8vIC9maWxlLzxhY2NvdW50Pi88YnVja2V0TmFtZT4vLi4uIHJvdXRlLCBvciBhIGJhY2tibGF6ZSBmcmllbmRseSBVUkwpLiBVc2VkIHdoZW4gd2UgaGF2ZSBubyBBUElcbi8vIGFjY2VzcyB0byBhIHNvdXJjZS4gT25seSBzaW5nbGUtZmlsZSByZWFkcyB3b3JrOiB0aGVyZSBpcyBubyBsaXN0aW5nLCBhbmQgd3JpdGVzIGFsd2F5cyB0aHJvdy5cblxuZXhwb3J0IGNsYXNzIEFyY2hpdmVzVXJsIGltcGxlbWVudHMgSUFyY2hpdmVzIHtcbiAgICAvLyBiYXNlIGlzIHRoZSBidWNrZXQncyBwdWJsaWMgYmFzZSBVUkwsIGUuZy4gaHR0cHM6Ly9ob3N0OnBvcnQvZmlsZS88YWNjb3VudD4vPGJ1Y2tldE5hbWU+XG4gICAgY29uc3RydWN0b3IocHJpdmF0ZSBiYXNlOiBzdHJpbmcpIHsgfVxuXG4gICAgcHVibGljIGdldERlYnVnTmFtZSgpIHtcbiAgICAgICAgcmV0dXJuIGB1cmwvJHt0aGlzLmJhc2V9YDtcbiAgICB9XG5cbiAgICBwcml2YXRlIHJlYWRPbmx5RXJyb3Iob3BlcmF0aW9uOiBzdHJpbmcpOiBFcnJvciB7XG4gICAgICAgIHJldHVybiBuZXcgRXJyb3IoYCR7b3BlcmF0aW9ufSBpcyBub3Qgc3VwcG9ydGVkIG92ZXIgVVJMLWZvcm0gYWNjZXNzIChubyBBUEkgYWNjZXNzIHRvIHRoaXMgc291cmNlLCBvbmx5IHB1YmxpYyBVUkwgcmVhZHMpLiBTb3VyY2U6ICR7dGhpcy5iYXNlfWApO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBnZXQoZmlsZU5hbWU6IHN0cmluZywgY29uZmlnPzogeyByYW5nZT86IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB9KTogUHJvbWlzZTxCdWZmZXIgfCB1bmRlZmluZWQ+IHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IGF3YWl0IHRoaXMuZ2V0MihmaWxlTmFtZSwgY29uZmlnKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdCAmJiByZXN1bHQuZGF0YSB8fCB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBnZXQyKGZpbGVOYW1lOiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8eyBkYXRhOiBCdWZmZXI7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZD4ge1xuICAgICAgICBsZXQgdXJsID0gYnVpbGRGaWxlVXJsKHRoaXMuYmFzZSwgZmlsZU5hbWUpO1xuICAgICAgICBsZXQgaGVhZGVyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuICAgICAgICBsZXQgcmFuZ2UgPSBjb25maWc/LnJhbmdlO1xuICAgICAgICBpZiAocmFuZ2UpIHtcbiAgICAgICAgICAgIGhlYWRlcnNbXCJSYW5nZVwiXSA9IGBieXRlcz0ke3JhbmdlLnN0YXJ0fS0ke3JhbmdlLmVuZCAtIDF9YDtcbiAgICAgICAgfVxuICAgICAgICBsZXQgcmVzcG9uc2U6IEh0dHBzUmVzcG9uc2VJbmZvID0geyBoZWFkZXJzOiB7fSB9O1xuICAgICAgICBsZXQgZGF0YTogQnVmZmVyO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgZGF0YSA9IGF3YWl0IGh0dHBzUmVxdWVzdCh1cmwsIHVuZGVmaW5lZCwgXCJHRVRcIiwgZmFsc2UsIHsgaGVhZGVycywgb3V0UmVzcG9uc2U6IHJlc3BvbnNlIH0pO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyBodHRwc1JlcXVlc3QgdGhyb3dzIG9uIGFueSBub24tMnh4OyBhIG1pc3NpbmcgZmlsZSBpcyBhIG5vcm1hbCBhYnNlbnQgcmVzdWx0LCBub3QgYW4gZXJyb3IuXG4gICAgICAgICAgICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gNDA0KSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBIHJlYWwgMjA2IG9ubHkgcmV0dXJucyB0aGUgcmVxdWVzdGVkIHNsaWNlLCBzbyB0aGUgZnVsbCBzaXplIGxpdmVzIGluIENvbnRlbnQtUmFuZ2UncyB0b3RhbC5cbiAgICAgICAgbGV0IHNpemUgPSBkYXRhLmxlbmd0aDtcbiAgICAgICAgbGV0IGNvbnRlbnRSYW5nZSA9IHJlc3BvbnNlLmhlYWRlcnNbXCJjb250ZW50LXJhbmdlXCJdO1xuICAgICAgICBsZXQgdG90YWwgPSBjb250ZW50UmFuZ2UgJiYgTnVtYmVyKGNvbnRlbnRSYW5nZS5zcGxpdChcIi9cIilbMV0pO1xuICAgICAgICBpZiAodG90YWwgJiYgTnVtYmVyLmlzRmluaXRlKHRvdGFsKSkge1xuICAgICAgICAgICAgc2l6ZSA9IHRvdGFsO1xuICAgICAgICB9XG4gICAgICAgIC8vIFNlcnZlcnMgdGhhdCBkb24ndCBzdXBwb3J0IHJhbmdlcyByZXR1cm4gdGhlIGZ1bGwgZmlsZSB3aXRoIGEgMjAwIChvdXJzIHNlcnZlcyByZWFsXG4gICAgICAgIC8vIDIwNnMsIGJ1dCBiYWNrYmxhemUgZnJpZW5kbHkgVVJMcyBhbmQgcHJveGllcyBtYXkgbm90KVxuICAgICAgICBpZiAocmFuZ2UgJiYgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwKSB7XG4gICAgICAgICAgICBkYXRhID0gZGF0YS5zdWJhcnJheShNYXRoLm1pbihyYW5nZS5zdGFydCwgZGF0YS5sZW5ndGgpLCBNYXRoLm1pbihyYW5nZS5lbmQsIGRhdGEubGVuZ3RoKSk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGxhc3RNb2RpZmllZCA9IHJlc3BvbnNlLmhlYWRlcnNbXCJsYXN0LW1vZGlmaWVkXCJdO1xuICAgICAgICBsZXQgd3JpdGVUaW1lID0gbGFzdE1vZGlmaWVkICYmIG5ldyBEYXRlKGxhc3RNb2RpZmllZCkuZ2V0VGltZSgpIHx8IDA7XG4gICAgICAgIHJldHVybiB7IGRhdGEsIHdyaXRlVGltZSwgc2l6ZSB9O1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgZ2V0SW5mbyhmaWxlTmFtZTogc3RyaW5nKTogUHJvbWlzZTx7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZD4ge1xuICAgICAgICBsZXQgcmVzdWx0ID0gYXdhaXQgdGhpcy5nZXQyKGZpbGVOYW1lKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdCAmJiB7IHdyaXRlVGltZTogcmVzdWx0LndyaXRlVGltZSwgc2l6ZTogcmVzdWx0LnNpemUgfSB8fCB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNldChmaWxlTmFtZTogc3RyaW5nLCBkYXRhOiBCdWZmZXIsIGNvbmZpZz86IHsgbGFzdE1vZGlmaWVkPzogbnVtYmVyIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhyb3cgdGhpcy5yZWFkT25seUVycm9yKFwic2V0XCIpO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgZGVsKGZpbGVOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhyb3cgdGhpcy5yZWFkT25seUVycm9yKFwiZGVsXCIpO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgc2V0TGFyZ2VGaWxlKGNvbmZpZzogeyBwYXRoOiBzdHJpbmc7IGdldE5leHREYXRhKCk6IFByb21pc2U8QnVmZmVyIHwgdW5kZWZpbmVkPiB9KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRocm93IHRoaXMucmVhZE9ubHlFcnJvcihcInNldExhcmdlRmlsZVwiKTtcbiAgICB9XG4gICAgcHVibGljIGFzeW5jIGZpbmQocHJlZml4OiBzdHJpbmcsIGNvbmZpZz86IHsgc2hhbGxvdz86IGJvb2xlYW47IHR5cGU6IFwiZmlsZXNcIiB8IFwiZm9sZGVyc1wiIH0pOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gICAgICAgIHRocm93IHRoaXMucmVhZE9ubHlFcnJvcihcImZpbmQgKGxpc3RpbmcpXCIpO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgZmluZEluZm8ocHJlZml4OiBzdHJpbmcsIGNvbmZpZz86IHsgc2hhbGxvdz86IGJvb2xlYW47IHR5cGU6IFwiZmlsZXNcIiB8IFwiZm9sZGVyc1wiIH0pOiBQcm9taXNlPEFyY2hpdmVGaWxlSW5mb1tdPiB7XG4gICAgICAgIHRocm93IHRoaXMucmVhZE9ubHlFcnJvcihcImZpbmRJbmZvIChsaXN0aW5nKVwiKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgZ2V0VVJMKHBhdGg6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgICAgIHJldHVybiBidWlsZEZpbGVVcmwodGhpcy5iYXNlLCBwYXRoKTtcbiAgICB9XG4gICAgcHVibGljIGFzeW5jIGdldENvbmZpZygpOiBQcm9taXNlPEFyY2hpdmVzQ29uZmlnPiB7XG4gICAgICAgIHJldHVybiB7fTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgaGFzV3JpdGVBY2Nlc3MoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG59XG4iXX0=
90
+ /* _JS_SOURCE_HASH = "b0345ca711863e65ce4fc8deaf0a642cdaf6ab861e3630f423ced0350b56666a"; */