sliftutils 1.7.109 → 1.7.111

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.
@@ -1,10 +1,11 @@
1
- "use strict";
1
+ "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
3
  //exports.moveArchiveFile = exports.copyArchiveFile = void 0;
4
+ const storageLogs_1 = require("./remoteStorage/storageLogs");
4
5
  // Cross-archive file operations built ON TOP of IArchives (copy, move) - helpers over the interface, not part of it, so they live beside it rather than in it.
5
6
  const LARGE_COPY_THRESHOLD = 64 * 1024 * 1024;
6
7
  const LARGE_COPY_CHUNK = 32 * 1024 * 1024;
7
- /** Copies one file between two archives. The source's CURRENT size and write time always come from getInfo right here - callers never supply them, because a stale size turns into ranged reads of a file that has changed (failing forever), and a stale write time re-orders history. Small files go as a single get2+set; past LARGE_COPY_THRESHOLD the copy streams through setLargeFile in LARGE_COPY_CHUNK ranged reads, so the whole file is never in memory. Returns the copied file's info, or undefined when the source doesn't have the file. */
8
+ /** Copies one file between two archives. The source's CURRENT size and write time always come from getInfo right here - callers never supply them, because a stale size turns into ranged reads of a file that has changed (failing forever), and a stale write time re-orders history. Small files go as a single get2+set; past LARGE_COPY_THRESHOLD the copy streams through setLargeFile in LARGE_COPY_CHUNK ranged reads, so the whole file is never in memory. Returns the copied file's info, and undefined for every way the copy did NOT land: the source doesn't have the file, the destination already had a NEWER file (refused up front rather than roll it back), or the destination silently dropped the write (its own only-take-latest won a race we lost - caught by confirming with getInfo afterward). The refused/dropped cases are logged as errors; a caller that treats undefined as "missing at the source" must getInfo the destination to learn the actual latest value. */
8
9
  async function copyArchiveFile(config) {
9
10
  let { from, to, path } = config;
10
11
  let toPath = config.toPath || path;
@@ -14,39 +15,55 @@ async function copyArchiveFile(config) {
14
15
  let size = info.size;
15
16
  // internal (synchronization between replicas of the same key) preserves the source's write time, so ordering survives propagation. A plain copy is a NEW write and is stamped now: the source's old stamp would make it LOSE to any newer write or tombstone at the destination, silently - move a file back to a folder it was deleted from and the copy is dropped, then the caller deletes the source, and the file is gone entirely.
16
17
  let writeTime = config.internal && info.writeTime || Date.now();
18
+ // A destination that already holds a NEWER file must not be overwritten with our older one - and the destination's own only-take-latest would drop the write SILENTLY, leaving the caller believing the copy happened. Refusing here, loudly, is what turns "the remote has something we missed" from a masked bug into a log line the caller can act on.
19
+ let destInfo = await to.getInfo(toPath, { noFallbacks: config.noFallbacks });
20
+ if (destInfo && destInfo.writeTime > writeTime) {
21
+ (0, storageLogs_1.logStorageError)(`Refusing to copy ${JSON.stringify(path)} from ${from.getDebugName()} to ${to.getDebugName()}${toPath !== path && ` (as ${JSON.stringify(toPath)})` || ""}: the destination already has a NEWER file (ours: ${size} bytes at ${new Date(writeTime).toISOString()}, theirs: ${destInfo.size} bytes at ${new Date(destInfo.writeTime).toISOString()}) - copying would roll it back`);
22
+ return undefined;
23
+ }
24
+ let copiedSize;
17
25
  if (size <= LARGE_COPY_THRESHOLD) {
18
26
  let result = await from.get2(path, { internal: config.internal, noFallbacks: config.noFallbacks });
19
27
  // Empty counts as absent, never as content to copy: an empty file IS a deletion, set refuses empty buffers, and deletions travel through their own path (del / scan tombstones)
20
28
  if (!result || !result.data || !result.data.length)
21
29
  return undefined;
22
30
  await to.set(toPath, result.data, { lastModified: writeTime, forceSetImmutable: config.forceSetImmutable, noChecks: config.noChecks, internal: config.internal });
23
- return { writeTime, size: result.data.length };
31
+ copiedSize = result.data.length;
24
32
  }
25
- // Const so the closure keeps the narrowed type
26
- const totalSize = size;
27
- let offset = 0;
28
- await to.setLargeFile({
29
- path: toPath,
30
- lastModified: writeTime,
31
- forceSetImmutable: config.forceSetImmutable,
32
- noChecks: config.noChecks,
33
- internal: config.internal,
34
- restartStream: () => {
35
- offset = 0;
36
- },
37
- getNextData: async () => {
38
- if (offset >= totalSize)
39
- return undefined;
40
- let end = Math.min(offset + LARGE_COPY_CHUNK, totalSize);
41
- let data = await from.get(path, { range: { start: offset, end }, internal: config.internal, noFallbacks: config.noFallbacks });
42
- if (!data || !data.length) {
43
- throw new Error(`Ranged read of ${JSON.stringify(path)} from ${from.getDebugName()} returned ${data && data.length || "nothing"} at ${offset}-${end} (expected ${end - offset} bytes of a ${totalSize} byte file - it changed or vanished mid-copy)`);
44
- }
45
- offset += data.length;
46
- return data;
47
- },
48
- });
49
- return { writeTime, size: totalSize };
33
+ else {
34
+ // Const so the closure keeps the narrowed type
35
+ const totalSize = size;
36
+ let offset = 0;
37
+ await to.setLargeFile({
38
+ path: toPath,
39
+ lastModified: writeTime,
40
+ forceSetImmutable: config.forceSetImmutable,
41
+ noChecks: config.noChecks,
42
+ internal: config.internal,
43
+ restartStream: () => {
44
+ offset = 0;
45
+ },
46
+ getNextData: async () => {
47
+ if (offset >= totalSize)
48
+ return undefined;
49
+ let end = Math.min(offset + LARGE_COPY_CHUNK, totalSize);
50
+ let data = await from.get(path, { range: { start: offset, end }, internal: config.internal, noFallbacks: config.noFallbacks });
51
+ if (!data || !data.length) {
52
+ throw new Error(`Ranged read of ${JSON.stringify(path)} from ${from.getDebugName()} returned ${data && data.length || "nothing"} at ${offset}-${end} (expected ${end - offset} bytes of a ${totalSize} byte file - it changed or vanished mid-copy)`);
53
+ }
54
+ offset += data.length;
55
+ return data;
56
+ },
57
+ });
58
+ copiedSize = totalSize;
59
+ }
60
+ // Every backend drops a superseded write SILENTLY (its only-take-latest is the last line of defense against races the up-front check can't see), so a returned set is not proof the copy landed - only the destination reporting the file at OUR time or newer is. Newer also counts as landed: the destination is at least as new as what we pushed (and b2 always stamps its own, later, upload time).
61
+ let confirmed = await to.getInfo(toPath, { noFallbacks: config.noFallbacks });
62
+ if (!confirmed || confirmed.writeTime < writeTime) {
63
+ (0, storageLogs_1.logStorageError)(`Copy of ${JSON.stringify(path)} from ${from.getDebugName()} to ${to.getDebugName()}${toPath !== path && ` (as ${JSON.stringify(toPath)})` || ""} did not land: we wrote ${copiedSize} bytes at ${new Date(writeTime).toISOString()}, but the destination reports ${confirmed && `${confirmed.size} bytes at ${new Date(confirmed.writeTime).toISOString()}` || "nothing"} - it dropped the write (a newer write or deletion won the race)`);
64
+ return undefined;
65
+ }
66
+ return { writeTime, size: copiedSize };
50
67
  }
51
68
  exports.copyArchiveFile = copyArchiveFile;
52
69
  /**
@@ -71,6 +88,11 @@ async function moveArchiveFile(config) {
71
88
  }
72
89
  let copied = await copyArchiveFile({ from, to, path, toPath, noFallbacks: config.noFallbacks });
73
90
  if (!copied) {
91
+ // Undefined is two cases (see copyArchiveFile) - asking the source which one keeps the error honest
92
+ let sourceInfo = await from.getInfo(path, { noFallbacks: config.noFallbacks });
93
+ if (sourceInfo) {
94
+ throw new Error(`Cannot move ${JSON.stringify(path)} (${sourceInfo.size} bytes at ${new Date(sourceInfo.writeTime).toISOString()}) from ${from.getDebugName()}: ${to.getDebugName()} already has a newer file at ${JSON.stringify(toPath)}, and the copy was refused rather than roll it back - the source is left untouched`);
95
+ }
74
96
  throw new Error(`Cannot move ${JSON.stringify(path)}: ${from.getDebugName()} does not have it`);
75
97
  }
76
98
  let confirmed = await to.getInfo(toPath, { noFallbacks: config.noFallbacks });
@@ -80,5 +102,5 @@ async function moveArchiveFile(config) {
80
102
  await from.del(path);
81
103
  }
82
104
  exports.moveArchiveFile = moveArchiveFile;
83
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXJjaGl2ZUhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhcmNoaXZlSGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFHQSwrSkFBK0o7QUFFL0osTUFBTSxvQkFBb0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQztBQUM5QyxNQUFNLGdCQUFnQixHQUFHLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBRTFDLDRoQkFBNGhCO0FBQ3JoQixLQUFLLFVBQVUsZUFBZSxDQUFDLE1BVXJDO0lBQ0csSUFBSSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEdBQUcsTUFBTSxDQUFDO0lBQ2hDLElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDO0lBQ25DLElBQUksSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxXQUFXLEVBQUUsTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7SUFDekUsSUFBSSxDQUFDLElBQUk7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1QixJQUFJLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBQ3JCLHlhQUF5YTtJQUN6YSxJQUFJLFNBQVMsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ2hFLElBQUksSUFBSSxJQUFJLG9CQUFvQixFQUFFLENBQUM7UUFDL0IsSUFBSSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztRQUNuRyxnTEFBZ0w7UUFDaEwsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJ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QUFJLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQ2hHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNWLE1BQU0sSUFBSSxLQUFLLENBQUMsZUFBZSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxZQUFZLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNwRyxDQUFDO0lBQ0QsSUFBSSxTQUFTLEdBQUcsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUM5RSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDYixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxTQUFTLElBQUksQ0FBQyxZQUFZLEVBQUUsc0JBQXNCLEVBQUUsQ0FBQyxZQUFZLEVBQUUsb0JBQW9CLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLHdGQUF3RixDQUFDLENBQUM7SUFDL1AsQ0FBQztJQUNELE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUN6QixDQUFDO0FBMUJELDBDQTBCQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIFR5cGUtb25seTogSUFyY2hpdmVzLnRzIHJlLWV4cG9ydHMgb3VyIGZ1bmN0aW9ucywgc28gYSB2YWx1ZSBpbXBvcnQgYmFjayBhdCBpdCB3b3VsZCBiZSBhIHJlcXVpcmUgY3ljbGVcbmltcG9ydCB0eXBlIHsgSUFyY2hpdmVzIH0gZnJvbSBcIi4vSUFyY2hpdmVzXCI7XG5cbi8vIENyb3NzLWFyY2hpdmUgZmlsZSBvcGVyYXRpb25zIGJ1aWx0IE9OIFRPUCBvZiBJQXJjaGl2ZXMgKGNvcHksIG1vdmUpIC0gaGVscGVycyBvdmVyIHRoZSBpbnRlcmZhY2UsIG5vdCBwYXJ0IG9mIGl0LCBzbyB0aGV5IGxpdmUgYmVzaWRlIGl0IHJhdGhlciB0aGFuIGluIGl0LlxuXG5jb25zdCBMQVJHRV9DT1BZX1RIUkVTSE9MRCA9IDY0ICogMTAyNCAqIDEwMjQ7XG5jb25zdCBMQVJHRV9DT1BZX0NIVU5LID0gMzIgKiAxMDI0ICogMTAyNDtcblxuLyoqIENvcGllcyBvbmUgZmlsZSBiZXR3ZWVuIHR3byBhcmNoaXZlcy4gVGhlIHNvdXJjZSdzIENVUlJFTlQgc2l6ZSBhbmQgd3JpdGUgdGltZSBhbHdheXMgY29tZSBmcm9tIGdldEluZm8gcmlnaHQgaGVyZSAtIGNhbGxlcnMgbmV2ZXIgc3VwcGx5IHRoZW0sIGJlY2F1c2UgYSBzdGFsZSBzaXplIHR1cm5zIGludG8gcmFuZ2VkIHJlYWRzIG9mIGEgZmlsZSB0aGF0IGhhcyBjaGFuZ2VkIChmYWlsaW5nIGZvcmV2ZXIpLCBhbmQgYSBzdGFsZSB3cml0ZSB0aW1lIHJlLW9yZGVycyBoaXN0b3J5LiBTbWFsbCBmaWxlcyBnbyBhcyBhIHNpbmdsZSBnZXQyK3NldDsgcGFzdCBMQVJHRV9DT1BZX1RIUkVTSE9MRCB0aGUgY29weSBzdHJlYW1zIHRocm91Z2ggc2V0TGFyZ2VGaWxlIGluIExBUkdFX0NPUFlfQ0hVTksgcmFuZ2VkIHJlYWRzLCBzbyB0aGUgd2hvbGUgZmlsZSBpcyBuZXZlciBpbiBtZW1vcnkuIFJldHVybnMgdGhlIGNvcGllZCBmaWxlJ3MgaW5mbywgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIHNvdXJjZSBkb2Vzbid0IGhhdmUgdGhlIGZpbGUuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY29weUFyY2hpdmVGaWxlKGNvbmZpZzoge1xuICAgIGZyb206IElBcmNoaXZlcztcbiAgICB0bzogSUFyY2hpdmVzO1xuICAgIHBhdGg6IHN0cmluZztcbiAgICAvKiogVGhlIHBhdGggYXQgdGhlIGRlc3RpbmF0aW9uIC0gZGVmYXVsdHMgdG8gcGF0aCAodGhlIGNvbW1vbiBjYXNlOiB0aGUgc2FtZSBrZXkgbW92aW5nIGJldHdlZW4gdHdvIGFyY2hpdmVzKS4gKi9cbiAgICB0b1BhdGg/OiBzdHJpbmc7XG4gICAgZm9yY2VTZXRJbW11dGFibGU/OiBib29sZWFuO1xuICAgIG5vQ2hlY2tzPzogYm9vbGVhbjtcbiAgICBpbnRlcm5hbD86IGJvb2xlYW47XG4gICAgbm9GYWxsYmFja3M/OiBib29sZWFuO1xufSk6IFByb21pc2U8eyB3cml0ZVRpbWU6IG51bWJlcjsgc2l6ZTogbnVtYmVyIH0gfCB1bmRlZmluZWQ+IHtcbiAgICBsZXQgeyBmcm9tLCB0bywgcGF0aCB9ID0gY29uZmlnO1xuICAgIGxldCB0b1BhdGggPSBjb25maWcudG9QYXRoIHx8IHBhdGg7XG4gICAgbGV0IGluZm8gPSBhd2FpdCBmcm9tLmdldEluZm8ocGF0aCwgeyBub0ZhbGxiYWNrczogY29uZmlnLm5vRmFsbGJhY2tzIH0pO1xuICAgIGlmICghaW5mbykgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBsZXQgc2l6ZSA9IGluZm8uc2l6ZTtcbiAgICAvLyBpbnRlcm5hbCAoc3luY2hyb25pemF0aW9uIGJldHdlZW4gcmVwbGljYXMgb2YgdGhlIHNhbWUga2V5KSBwcmVzZXJ2ZXMgdGhlIHNvdXJjZSdzIHdyaXRlIHRpbWUsIHNvIG9yZGVyaW5nIHN1cnZpdmVzIHByb3BhZ2F0aW9uLiBBIHBsYWluIGNvcHkgaXMgYSBORVcgd3JpdGUgYW5kIGlzIHN0YW1wZWQgbm93OiB0aGUgc291cmNlJ3Mgb2xkIHN0YW1wIHdvdWxkIG1ha2UgaXQgTE9TRSB0byBhbnkgbmV3ZXIgd3JpdGUgb3IgdG9tYnN0b25lIGF0IHRoZSBkZXN0aW5hdGlvbiwgc2lsZW50bHkgLSBtb3ZlIGEgZmlsZSBiYWNrIHRvIGEgZm9sZGVyIGl0IHdhcyBkZWxldGVkIGZyb20gYW5kIHRoZSBjb3B5IGlzIGRyb3BwZWQsIHRoZW4gdGhlIGNhbGxlciBkZWxldGVzIHRoZSBzb3VyY2UsIGFuZCB0aGUgZmlsZSBpcyBnb25lIGVudGlyZWx5LlxuICAgIGxldCB3cml0ZVRpbWUgPSBjb25maWcuaW50ZXJuYWwgJiYgaW5mby53cml0ZVRpbWUgfHwgRGF0ZS5ub3coKTtcbiAgICBpZiAoc2l6ZSA8PSBMQVJHRV9DT1BZX1RIUkVTSE9MRCkge1xuICAgICAgICBsZXQgcmVzdWx0ID0gYXdhaXQgZnJvbS5nZXQyKHBhdGgsIHsgaW50ZXJuYWw6IGNvbmZpZy5pbnRlcm5hbCwgbm9GYWxsYmFja3M6IGNvbmZpZy5ub0ZhbGxiYWNrcyB9KTtcbiAgICAgICAgLy8gRW1wdHkgY291bnRzIGFzIGFic2VudCwgbmV2ZXIgYXMgY29udGVudCB0byBjb3B5OiBhbiBlbXB0eSBmaWxlIElTIGEgZGVsZXRpb24sIHNldCByZWZ1c2VzIGVtcHR5IGJ1ZmZlcnMsIGFuZCBkZWxldGlvbnMgdHJhdmVsIHRocm91Z2ggdGhlaXIgb3duIHBhdGggKGRlbCAvIHNjYW4gdG9tYnN0b25lcylcbiAgICAgICAgaWYgKCFyZXN1bHQgfHwgIXJlc3VsdC5kYXRhIHx8ICFyZXN1bHQuZGF0YS5sZW5ndGgpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgIGF3YWl0IHRvLnNldCh0b1BhdGgsIHJlc3VsdC5kYXRhLCB7IGxhc3RNb2RpZmllZDogd3JpdGVUaW1lLCBmb3JjZVNldEltbXV0YWJsZTogY29uZmlnLmZvcmNlU2V0SW1tdXRhYmxlLCBub0NoZWNrczogY29uZmlnLm5vQ2hlY2tzLCBpbnRlcm5hbDogY29uZmlnLmludGVybmFsIH0pO1xuICAgICAgICByZXR1cm4geyB3cml0ZVRpbWUsIHNpemU6IHJlc3VsdC5kYXRhLmxlbmd0aCB9O1xuICAgIH1cbiAgICAvLyBDb25zdCBzbyB0aGUgY2xvc3VyZSBrZWVwcyB0aGUgbmFycm93ZWQgdHlwZVxuICAgIGNvbnN0IHRvdGFsU2l6ZSA9IHNpemU7XG4gICAgbGV0IG9mZnNldCA9IDA7XG4gICAgYXdhaXQgdG8uc2V0TGFyZ2VGaWxlKHtcbiAgICAgICAgcGF0aDogdG9QYXRoLFxuICAgICAgICBsYXN0TW9kaWZpZWQ6IHdyaXRlVGltZSxcbiAgICAgICAgZm9yY2VTZXRJbW11dGFibGU6IGNvbmZpZy5mb3JjZVNldEltbXV0YWJsZSxcbiAgICAgICAgbm9DaGVja3M6IGNvbmZpZy5ub0NoZWNrcyxcbiAgICAgICAgaW50ZXJuYWw6IGNvbmZpZy5pbnRlcm5hbCxcbiAgICAgICAgcmVzdGFydFN0cmVhbTogKCkgPT4ge1xuICAgICAgICAgICAgb2Zmc2V0ID0gMDtcbiAgICAgICAgfSxcbiAgICAgICAgZ2V0TmV4dERhdGE6IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGlmIChvZmZzZXQgPj0gdG90YWxTaXplKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICAgICAgbGV0IGVuZCA9IE1hdGgubWluKG9mZnNldCArIExBUkdFX0NPUFlfQ0hVTkssIHRvdGFsU2l6ZSk7XG4gICAgICAgICAgICBsZXQgZGF0YSA9IGF3YWl0IGZyb20uZ2V0KHBhdGgsIHsgcmFuZ2U6IHsgc3RhcnQ6IG9mZnNldCwgZW5kIH0sIGludGVybmFsOiBjb25maWcuaW50ZXJuYWwsIG5vRmFsbGJhY2tzOiBjb25maWcubm9GYWxsYmFja3MgfSk7XG4gICAgICAgICAgICBpZiAoIWRhdGEgfHwgIWRhdGEubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBSYW5nZWQgcmVhZCBvZiAke0pTT04uc3RyaW5naWZ5KHBhdGgpfSBmcm9tICR7ZnJvbS5nZXREZWJ1Z05hbWUoKX0gcmV0dXJuZWQgJHtkYXRhICYmIGRhdGEubGVuZ3RoIHx8IFwibm90aGluZ1wifSBhdCAke29mZnNldH0tJHtlbmR9IChleHBlY3RlZCAke2VuZCAtIG9mZnNldH0gYnl0ZXMgb2YgYSAke3RvdGFsU2l6ZX0gYnl0ZSBmaWxlIC0gaXQgY2hhbmdlZCBvciB2YW5pc2hlZCBtaWQtY29weSlgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9mZnNldCArPSBkYXRhLmxlbmd0aDtcbiAgICAgICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgICB9LFxuICAgIH0pO1xuICAgIHJldHVybiB7IHdyaXRlVGltZSwgc2l6ZTogdG90YWxTaXplIH07XG59XG5cbi8qKlxuICogTW92ZXMgb25lIGZpbGUgLSBiZXR3ZWVuIHR3byBhcmNoaXZlcywgb3IgYmV0d2VlbiB0d28gcGF0aHMgb2Ygb25lLiBXaGVuIGZyb20gYW5kIHRvIGFyZSB0aGUgU0FNRVxuICogYXJjaGl2ZXMgaW5zdGFuY2UgYW5kIGl0IGltcGxlbWVudHMgbW92ZSwgdGhlIGJhY2tlbmQgbW92ZXMgdGhlIGZpbGUgaXRzZWxmIChiYWNrYmxhemUgY29waWVzXG4gKiBzZXJ2ZXItc2lkZSwgZGlzayByZW5hbWVzLCB0aGUgc3RvcmFnZSBzZXJ2ZXIgcmVsb2NhdGVzIGl0IG5vZGUtc2lkZSkgYW5kIHRoZSBieXRlcyBuZXZlciB0cmF2ZWxcbiAqIHRocm91Z2ggdXMuIEV2ZXJ5dGhpbmcgZWxzZSBpcyB0aGUgc2FmZSBmYWxsYmFjazogY29weSwgdGhlbiBDT05GSVJNIHRoZSBkZXN0aW5hdGlvbiBhY3R1YWxseVxuICogcmVwb3J0cyB0aGUgZmlsZSAoZ2V0SW5mbyksIGFuZCBvbmx5IHRoZW4gZGVsZXRlIHRoZSBzb3VyY2UgLSB0aGUgb25lIG9yZGVyIGluIHdoaWNoIG5vIGZhaWx1cmVcbiAqIGNhbiBsb3NlIHRoZSBmaWxlLCBvbmx5IGF0IHdvcnN0IGxlYXZlIGl0IGluIGJvdGggcGxhY2VzLiBUaHJvd3Mgd2hlbiB0aGUgc291cmNlIGRvZXNuJ3QgaGF2ZSB0aGVcbiAqIGZpbGUsIG9yIHdoZW4gdGhlIGNvcHkgY2Fubm90IGJlIGNvbmZpcm1lZCAodGhlIHNvdXJjZSBpcyB0aGVuIGxlZnQgdW50b3VjaGVkKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1vdmVBcmNoaXZlRmlsZShjb25maWc6IHtcbiAgICBmcm9tOiBJQXJjaGl2ZXM7XG4gICAgdG86IElBcmNoaXZlcztcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgLyoqIFRoZSBwYXRoIGF0IHRoZSBkZXN0aW5hdGlvbiAtIGRlZmF1bHRzIHRvIHBhdGggKG1vdmluZyBiZXR3ZWVuIHR3byBhcmNoaXZlcykuIFJlcXVpcmVkIGluIHByYWN0aWNlIHdoZW4gZnJvbSBhbmQgdG8gYXJlIHRoZSBzYW1lIGFyY2hpdmVzLCB3aGVyZSB0aGUgc2FtZSBwYXRoIHdvdWxkIGJlIGEgbm8tb3AuICovXG4gICAgdG9QYXRoPzogc3RyaW5nO1xuICAgIG5vRmFsbGJhY2tzPzogYm9vbGVhbjtcbn0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBsZXQgeyBmcm9tLCB0bywgcGF0aCB9ID0gY29uZmlnO1xuICAgIGxldCB0b1BhdGggPSBjb25maWcudG9QYXRoIHx8IHBhdGg7XG4gICAgaWYgKGZyb20gPT09IHRvKSB7XG4gICAgICAgIGlmIChwYXRoID09PSB0b1BhdGgpIHJldHVybjtcbiAgICAgICAgaWYgKGZyb20ubW92ZSkge1xuICAgICAgICAgICAgYXdhaXQgZnJvbS5tb3ZlKHsgZnJvbVBhdGg6IHBhdGgsIHRvUGF0aCB9KTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBsZXQgY29waWVkID0gYXdhaXQgY29weUFyY2hpdmVGaWxlKHsgZnJvbSwgdG8sIHBhdGgsIHRvUGF0aCwgbm9GYWxsYmFja3M6IGNvbmZpZy5ub0ZhbGxiYWNrcyB9KTtcbiAgICBpZiAoIWNvcGllZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBtb3ZlICR7SlNPTi5zdHJpbmdpZnkocGF0aCl9OiAke2Zyb20uZ2V0RGVidWdOYW1lKCl9IGRvZXMgbm90IGhhdmUgaXRgKTtcbiAgICB9XG4gICAgbGV0IGNvbmZpcm1lZCA9IGF3YWl0IHRvLmdldEluZm8odG9QYXRoLCB7IG5vRmFsbGJhY2tzOiBjb25maWcubm9GYWxsYmFja3MgfSk7XG4gICAgaWYgKCFjb25maXJtZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBOb3QgZGVsZXRpbmcgJHtKU09OLnN0cmluZ2lmeShwYXRoKX0gZnJvbSAke2Zyb20uZ2V0RGVidWdOYW1lKCl9IGFmdGVyIGNvcHlpbmcgaXQ6ICR7dG8uZ2V0RGVidWdOYW1lKCl9IGRvZXMgbm90IHJlcG9ydCAke0pTT04uc3RyaW5naWZ5KHRvUGF0aCl9ICh0aGUgY29weSBjbGFpbWVkIHRvIHN1Y2NlZWQsIHNvIHNvbWV0aGluZyBpcyB3cm9uZyAtIHRoZSBmaWxlIGlzIGxlZnQgYXQgdGhlIHNvdXJjZSlgKTtcbiAgICB9XG4gICAgYXdhaXQgZnJvbS5kZWwocGF0aCk7XG59XG4iXX0=
84
- /* _JS_SOURCE_HASH = "3f4425d396aad4af5038c7dadec3359e6266b310d603c8f8b6b9e99375041c3f"; */
105
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXJjaGl2ZUhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhcmNoaXZlSGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSw2REFBOEQ7QUFFOUQsK0pBQStKO0FBRS9KLE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDOUMsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQztBQUUxQyx1OEJBQXU4QjtBQUNoOEIsS0FBSyxVQUFVLGVBQWUsQ0FBQyxNQVVyQztJQUNHLElBQUksRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQztJQUNoQyxJQUFJLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQztJQUNuQyxJQUFJLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQ3pFLElBQUksQ0FBQyxJQUFJO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDNUIsSUFBSSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQztJQUNyQix5YUFBeWE7SUFDemEsSUFBSSxTQUFTLEdBQUcsTUFBTSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNoRSwwVkFBMFY7SUFDMVYsSUFBSSxRQUFRLEdBQUcsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUM3RSxJQUFJLFFBQVEsSUFBSSxRQUFRLENBQUMsU0FBUyxHQUFHLFNBQVMsRUFBRSxDQUFDO1FBQzdDLElBQUEsNkJBQWUsRUFBQyxvQkFBb0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsWUFBWSxFQUFFLE9BQU8sRUFBRSxDQUFDLFlBQVksRUFBRSxHQUFHLE1BQU0sS0FBSyxJQUFJLElBQUksUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxHQUFHLElBQUksRUFBRSxxREFBcUQsSUFBSSxhQUFhLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxhQUFhLFFBQVEsQ0FBQyxJQUFJLGFBQWEsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxnQ0FBZ0MsQ0FBQyxDQUFDO1FBQ25ZLE9BQU8sU0FBUyxDQUFDO0lBQ3JCLENBQUM7SUFDRCxJQUFJLFVBQWtCLENBQUM7SUFDdkIsSUFBSSxJQUFJLElBQUksb0JBQW9CLEVBQUUsQ0FBQztRQUMvQixJQUFJLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQUUsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1FBQ25HLGdMQUFnTDtRQUNoTCxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLE9BQU8sU0FBUyxDQUFDO1FBQ3JFLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLElBQUksRUFBRSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxDQUFDLGlCQUFpQixFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNsSyxVQUFVLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7SUFDcEMsQ0FBQztTQUFNLENBQUM7UUFDSiwrQ0FBK0M7UUFDL0MsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBQ3ZCLElBQUksTUFBTSxHQUFHLENBQUMsQ0FBQztRQUNmLE1BQU0sRUFBRSxDQUFDLFlBQVksQ0FBQztZQUNsQixJQUFJLEVBQUUsTUFBTTtZQUNaLFlBQVksRUFBRSxTQUFTO1lBQ3ZCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQyxpQkFBaUI7WUFDM0MsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRO1lBQ3pCLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtZQUN6QixhQUFhLEVBQUUsR0FBRyxFQUFFO2dCQUNoQixNQUFNLEdBQUcsQ0FBQyxDQUFDO1lBQ2YsQ0FBQztZQUNELFdBQVcsRUFBRSxLQUFLLElBQUksRUFBRTtnQkFDcEIsSUFBSSxNQUFNLElBQUksU0FBUztvQkFBRSxPQUFPLFNBQVMsQ0FBQztnQkFDMUMsSUFBSSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEdBQUcsZ0JBQWdCLEVBQUUsU0FBUyxDQUFDLENBQUM7Z0JBQ3pELElBQUksSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztnQkFDL0gsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztvQkFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsWUFBWSxFQUFFLGFBQWEsSUFBSSxJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksU0FBUyxPQUFPLE1BQU0sSUFBSSxHQUFHLGNBQWMsR0FBRyxHQUFHLE1BQU0sZUFBZSxTQUFTLCtDQUErQyxDQUFDLENBQUM7Z0JBQzFQLENBQUM7Z0JBQ0QsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUM7Z0JBQ3RCLE9BQU8sSUFBSSxDQUFDO1lBQ2hCLENBQUM7U0FDSixDQUFDLENBQUM7UUFDSCxVQUFVLEdBQUcsU0FBUyxDQUFDO0lBQzNCLENBQUM7SUFDRCx5WUFBeVk7SUFDelksSUFBSSxTQUFTLEdBQUcsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUM5RSxJQUFJLENBQUMsU0FBUyxJQUFJLFNBQVMsQ0FBQyxTQUFTLEdBQUcsU0FBUyxFQUFFLENBQUM7UUFDaEQsSUFBQSw2QkFBZSxFQUFDLFdBQVcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsWUFBWSxFQUFFLE9BQU8sRUFBRSxDQUFDLFlBQVksRUFBRSxHQUFHLE1BQU0sS0FBSyxJQUFJLElBQUksUUFBUSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxHQUFHLElBQUksRUFBRSwyQkFBMkIsVUFBVSxhQUFhLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxpQ0FBaUMsU0FBUyxJQUFJLEdBQUcsU0FBUyxDQUFDLElBQUksYUFBYSxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsV0FBVyxFQUFFLEVBQUUsSUFBSSxTQUFTLGtFQUFrRSxDQUFDLENBQUM7UUFDN2IsT0FBTyxTQUFTLENBQUM7SUFDckIsQ0FBQztJQUNELE9BQU8sRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFDO0FBQzNDLENBQUM7QUFoRUQsMENBZ0VDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSSxLQUFLLFVBQVUsZUFBZSxDQUFDLE1BT3JDO0lBQ0csSUFBSSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEdBQUcsTUFBTSxDQUFDO0lBQ2hDLElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDO0lBQ25DLElBQUksSUFBSSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ2QsSUFBSSxJQUFJLEtBQUssTUFBTTtZQUFFLE9BQU87UUFDNUIsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDWixNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDNUMsT0FBTztRQUNYLENBQUM7SUFDTCxDQUFDO0lBQ0QsSUFBSSxNQUFNLEdBQUcsTUFBTSxlQUFlLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQ2hHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNWLG9HQUFvRztRQUNwRyxJQUFJLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1FBQy9FLElBQUksVUFBVSxFQUFFLENBQUM7WUFDYixNQUFNLElBQUksS0FBSyxDQUFDLGVBQWUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsS0FBSyxVQUFVLENBQUMsSUFBSSxhQUFhLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxXQUFXLEVBQUUsVUFBVSxJQUFJLENBQUMsWUFBWSxFQUFFLEtBQUssRUFBRSxDQUFDLFlBQVksRUFBRSxnQ0FBZ0MsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsb0ZBQW9GLENBQUMsQ0FBQztRQUNuVSxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxlQUFlLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssSUFBSSxDQUFDLFlBQVksRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO0lBQ3BHLENBQUM7SUFDRCxJQUFJLFNBQVMsR0FBRyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQzlFLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNiLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLFlBQVksRUFBRSxzQkFBc0IsRUFBRSxDQUFDLFlBQVksRUFBRSxvQkFBb0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsd0ZBQXdGLENBQUMsQ0FBQztJQUMvUCxDQUFDO0lBQ0QsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3pCLENBQUM7QUEvQkQsMENBK0JDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gVHlwZS1vbmx5OiBJQXJjaGl2ZXMudHMgcmUtZXhwb3J0cyBvdXIgZnVuY3Rpb25zLCBzbyBhIHZhbHVlIGltcG9ydCBiYWNrIGF0IGl0IHdvdWxkIGJlIGEgcmVxdWlyZSBjeWNsZVxuaW1wb3J0IHR5cGUgeyBJQXJjaGl2ZXMgfSBmcm9tIFwiLi9JQXJjaGl2ZXNcIjtcbmltcG9ydCB7IGxvZ1N0b3JhZ2VFcnJvciB9IGZyb20gXCIuL3JlbW90ZVN0b3JhZ2Uvc3RvcmFnZUxvZ3NcIjtcblxuLy8gQ3Jvc3MtYXJjaGl2ZSBmaWxlIG9wZXJhdGlvbnMgYnVpbHQgT04gVE9QIG9mIElBcmNoaXZlcyAoY29weSwgbW92ZSkgLSBoZWxwZXJzIG92ZXIgdGhlIGludGVyZmFjZSwgbm90IHBhcnQgb2YgaXQsIHNvIHRoZXkgbGl2ZSBiZXNpZGUgaXQgcmF0aGVyIHRoYW4gaW4gaXQuXG5cbmNvbnN0IExBUkdFX0NPUFlfVEhSRVNIT0xEID0gNjQgKiAxMDI0ICogMTAyNDtcbmNvbnN0IExBUkdFX0NPUFlfQ0hVTksgPSAzMiAqIDEwMjQgKiAxMDI0O1xuXG4vKiogQ29waWVzIG9uZSBmaWxlIGJldHdlZW4gdHdvIGFyY2hpdmVzLiBUaGUgc291cmNlJ3MgQ1VSUkVOVCBzaXplIGFuZCB3cml0ZSB0aW1lIGFsd2F5cyBjb21lIGZyb20gZ2V0SW5mbyByaWdodCBoZXJlIC0gY2FsbGVycyBuZXZlciBzdXBwbHkgdGhlbSwgYmVjYXVzZSBhIHN0YWxlIHNpemUgdHVybnMgaW50byByYW5nZWQgcmVhZHMgb2YgYSBmaWxlIHRoYXQgaGFzIGNoYW5nZWQgKGZhaWxpbmcgZm9yZXZlciksIGFuZCBhIHN0YWxlIHdyaXRlIHRpbWUgcmUtb3JkZXJzIGhpc3RvcnkuIFNtYWxsIGZpbGVzIGdvIGFzIGEgc2luZ2xlIGdldDIrc2V0OyBwYXN0IExBUkdFX0NPUFlfVEhSRVNIT0xEIHRoZSBjb3B5IHN0cmVhbXMgdGhyb3VnaCBzZXRMYXJnZUZpbGUgaW4gTEFSR0VfQ09QWV9DSFVOSyByYW5nZWQgcmVhZHMsIHNvIHRoZSB3aG9sZSBmaWxlIGlzIG5ldmVyIGluIG1lbW9yeS4gUmV0dXJucyB0aGUgY29waWVkIGZpbGUncyBpbmZvLCBhbmQgdW5kZWZpbmVkIGZvciBldmVyeSB3YXkgdGhlIGNvcHkgZGlkIE5PVCBsYW5kOiB0aGUgc291cmNlIGRvZXNuJ3QgaGF2ZSB0aGUgZmlsZSwgdGhlIGRlc3RpbmF0aW9uIGFscmVhZHkgaGFkIGEgTkVXRVIgZmlsZSAocmVmdXNlZCB1cCBmcm9udCByYXRoZXIgdGhhbiByb2xsIGl0IGJhY2spLCBvciB0aGUgZGVzdGluYXRpb24gc2lsZW50bHkgZHJvcHBlZCB0aGUgd3JpdGUgKGl0cyBvd24gb25seS10YWtlLWxhdGVzdCB3b24gYSByYWNlIHdlIGxvc3QgLSBjYXVnaHQgYnkgY29uZmlybWluZyB3aXRoIGdldEluZm8gYWZ0ZXJ3YXJkKS4gVGhlIHJlZnVzZWQvZHJvcHBlZCBjYXNlcyBhcmUgbG9nZ2VkIGFzIGVycm9yczsgYSBjYWxsZXIgdGhhdCB0cmVhdHMgdW5kZWZpbmVkIGFzIFwibWlzc2luZyBhdCB0aGUgc291cmNlXCIgbXVzdCBnZXRJbmZvIHRoZSBkZXN0aW5hdGlvbiB0byBsZWFybiB0aGUgYWN0dWFsIGxhdGVzdCB2YWx1ZS4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjb3B5QXJjaGl2ZUZpbGUoY29uZmlnOiB7XG4gICAgZnJvbTogSUFyY2hpdmVzO1xuICAgIHRvOiBJQXJjaGl2ZXM7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIC8qKiBUaGUgcGF0aCBhdCB0aGUgZGVzdGluYXRpb24gLSBkZWZhdWx0cyB0byBwYXRoICh0aGUgY29tbW9uIGNhc2U6IHRoZSBzYW1lIGtleSBtb3ZpbmcgYmV0d2VlbiB0d28gYXJjaGl2ZXMpLiAqL1xuICAgIHRvUGF0aD86IHN0cmluZztcbiAgICBmb3JjZVNldEltbXV0YWJsZT86IGJvb2xlYW47XG4gICAgbm9DaGVja3M/OiBib29sZWFuO1xuICAgIGludGVybmFsPzogYm9vbGVhbjtcbiAgICBub0ZhbGxiYWNrcz86IGJvb2xlYW47XG59KTogUHJvbWlzZTx7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZD4ge1xuICAgIGxldCB7IGZyb20sIHRvLCBwYXRoIH0gPSBjb25maWc7XG4gICAgbGV0IHRvUGF0aCA9IGNvbmZpZy50b1BhdGggfHwgcGF0aDtcbiAgICBsZXQgaW5mbyA9IGF3YWl0IGZyb20uZ2V0SW5mbyhwYXRoLCB7IG5vRmFsbGJhY2tzOiBjb25maWcubm9GYWxsYmFja3MgfSk7XG4gICAgaWYgKCFpbmZvKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIGxldCBzaXplID0gaW5mby5zaXplO1xuICAgIC8vIGludGVybmFsIChzeW5jaHJvbml6YXRpb24gYmV0d2VlbiByZXBsaWNhcyBvZiB0aGUgc2FtZSBrZXkpIHByZXNlcnZlcyB0aGUgc291cmNlJ3Mgd3JpdGUgdGltZSwgc28gb3JkZXJpbmcgc3Vydml2ZXMgcHJvcGFnYXRpb24uIEEgcGxhaW4gY29weSBpcyBhIE5FVyB3cml0ZSBhbmQgaXMgc3RhbXBlZCBub3c6IHRoZSBzb3VyY2UncyBvbGQgc3RhbXAgd291bGQgbWFrZSBpdCBMT1NFIHRvIGFueSBuZXdlciB3cml0ZSBvciB0b21ic3RvbmUgYXQgdGhlIGRlc3RpbmF0aW9uLCBzaWxlbnRseSAtIG1vdmUgYSBmaWxlIGJhY2sgdG8gYSBmb2xkZXIgaXQgd2FzIGRlbGV0ZWQgZnJvbSBhbmQgdGhlIGNvcHkgaXMgZHJvcHBlZCwgdGhlbiB0aGUgY2FsbGVyIGRlbGV0ZXMgdGhlIHNvdXJjZSwgYW5kIHRoZSBmaWxlIGlzIGdvbmUgZW50aXJlbHkuXG4gICAgbGV0IHdyaXRlVGltZSA9IGNvbmZpZy5pbnRlcm5hbCAmJiBpbmZvLndyaXRlVGltZSB8fCBEYXRlLm5vdygpO1xuICAgIC8vIEEgZGVzdGluYXRpb24gdGhhdCBhbHJlYWR5IGhvbGRzIGEgTkVXRVIgZmlsZSBtdXN0IG5vdCBiZSBvdmVyd3JpdHRlbiB3aXRoIG91ciBvbGRlciBvbmUgLSBhbmQgdGhlIGRlc3RpbmF0aW9uJ3Mgb3duIG9ubHktdGFrZS1sYXRlc3Qgd291bGQgZHJvcCB0aGUgd3JpdGUgU0lMRU5UTFksIGxlYXZpbmcgdGhlIGNhbGxlciBiZWxpZXZpbmcgdGhlIGNvcHkgaGFwcGVuZWQuIFJlZnVzaW5nIGhlcmUsIGxvdWRseSwgaXMgd2hhdCB0dXJucyBcInRoZSByZW1vdGUgaGFzIHNvbWV0aGluZyB3ZSBtaXNzZWRcIiBmcm9tIGEgbWFza2VkIGJ1ZyBpbnRvIGEgbG9nIGxpbmUgdGhlIGNhbGxlciBjYW4gYWN0IG9uLlxuICAgIGxldCBkZXN0SW5mbyA9IGF3YWl0IHRvLmdldEluZm8odG9QYXRoLCB7IG5vRmFsbGJhY2tzOiBjb25maWcubm9GYWxsYmFja3MgfSk7XG4gICAgaWYgKGRlc3RJbmZvICYmIGRlc3RJbmZvLndyaXRlVGltZSA+IHdyaXRlVGltZSkge1xuICAgICAgICBsb2dTdG9yYWdlRXJyb3IoYFJlZnVzaW5nIHRvIGNvcHkgJHtKU09OLnN0cmluZ2lmeShwYXRoKX0gZnJvbSAke2Zyb20uZ2V0RGVidWdOYW1lKCl9IHRvICR7dG8uZ2V0RGVidWdOYW1lKCl9JHt0b1BhdGggIT09IHBhdGggJiYgYCAoYXMgJHtKU09OLnN0cmluZ2lmeSh0b1BhdGgpfSlgIHx8IFwiXCJ9OiB0aGUgZGVzdGluYXRpb24gYWxyZWFkeSBoYXMgYSBORVdFUiBmaWxlIChvdXJzOiAke3NpemV9IGJ5dGVzIGF0ICR7bmV3IERhdGUod3JpdGVUaW1lKS50b0lTT1N0cmluZygpfSwgdGhlaXJzOiAke2Rlc3RJbmZvLnNpemV9IGJ5dGVzIGF0ICR7bmV3IERhdGUoZGVzdEluZm8ud3JpdGVUaW1lKS50b0lTT1N0cmluZygpfSkgLSBjb3B5aW5nIHdvdWxkIHJvbGwgaXQgYmFja2ApO1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBsZXQgY29waWVkU2l6ZTogbnVtYmVyO1xuICAgIGlmIChzaXplIDw9IExBUkdFX0NPUFlfVEhSRVNIT0xEKSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBhd2FpdCBmcm9tLmdldDIocGF0aCwgeyBpbnRlcm5hbDogY29uZmlnLmludGVybmFsLCBub0ZhbGxiYWNrczogY29uZmlnLm5vRmFsbGJhY2tzIH0pO1xuICAgICAgICAvLyBFbXB0eSBjb3VudHMgYXMgYWJzZW50LCBuZXZlciBhcyBjb250ZW50IHRvIGNvcHk6IGFuIGVtcHR5IGZpbGUgSVMgYSBkZWxldGlvbiwgc2V0IHJlZnVzZXMgZW1wdHkgYnVmZmVycywgYW5kIGRlbGV0aW9ucyB0cmF2ZWwgdGhyb3VnaCB0aGVpciBvd24gcGF0aCAoZGVsIC8gc2NhbiB0b21ic3RvbmVzKVxuICAgICAgICBpZiAoIXJlc3VsdCB8fCAhcmVzdWx0LmRhdGEgfHwgIXJlc3VsdC5kYXRhLmxlbmd0aCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgYXdhaXQgdG8uc2V0KHRvUGF0aCwgcmVzdWx0LmRhdGEsIHsgbGFzdE1vZGlmaWVkOiB3cml0ZVRpbWUsIGZvcmNlU2V0SW1tdXRhYmxlOiBjb25maWcuZm9yY2VTZXRJbW11dGFibGUsIG5vQ2hlY2tzOiBjb25maWcubm9DaGVja3MsIGludGVybmFsOiBjb25maWcuaW50ZXJuYWwgfSk7XG4gICAgICAgIGNvcGllZFNpemUgPSByZXN1bHQuZGF0YS5sZW5ndGg7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQ29uc3Qgc28gdGhlIGNsb3N1cmUga2VlcHMgdGhlIG5hcnJvd2VkIHR5cGVcbiAgICAgICAgY29uc3QgdG90YWxTaXplID0gc2l6ZTtcbiAgICAgICAgbGV0IG9mZnNldCA9IDA7XG4gICAgICAgIGF3YWl0IHRvLnNldExhcmdlRmlsZSh7XG4gICAgICAgICAgICBwYXRoOiB0b1BhdGgsXG4gICAgICAgICAgICBsYXN0TW9kaWZpZWQ6IHdyaXRlVGltZSxcbiAgICAgICAgICAgIGZvcmNlU2V0SW1tdXRhYmxlOiBjb25maWcuZm9yY2VTZXRJbW11dGFibGUsXG4gICAgICAgICAgICBub0NoZWNrczogY29uZmlnLm5vQ2hlY2tzLFxuICAgICAgICAgICAgaW50ZXJuYWw6IGNvbmZpZy5pbnRlcm5hbCxcbiAgICAgICAgICAgIHJlc3RhcnRTdHJlYW06ICgpID0+IHtcbiAgICAgICAgICAgICAgICBvZmZzZXQgPSAwO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGdldE5leHREYXRhOiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKG9mZnNldCA+PSB0b3RhbFNpemUpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgbGV0IGVuZCA9IE1hdGgubWluKG9mZnNldCArIExBUkdFX0NPUFlfQ0hVTkssIHRvdGFsU2l6ZSk7XG4gICAgICAgICAgICAgICAgbGV0IGRhdGEgPSBhd2FpdCBmcm9tLmdldChwYXRoLCB7IHJhbmdlOiB7IHN0YXJ0OiBvZmZzZXQsIGVuZCB9LCBpbnRlcm5hbDogY29uZmlnLmludGVybmFsLCBub0ZhbGxiYWNrczogY29uZmlnLm5vRmFsbGJhY2tzIH0pO1xuICAgICAgICAgICAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBSYW5nZWQgcmVhZCBvZiAke0pTT04uc3RyaW5naWZ5KHBhdGgpfSBmcm9tICR7ZnJvbS5nZXREZWJ1Z05hbWUoKX0gcmV0dXJuZWQgJHtkYXRhICYmIGRhdGEubGVuZ3RoIHx8IFwibm90aGluZ1wifSBhdCAke29mZnNldH0tJHtlbmR9IChleHBlY3RlZCAke2VuZCAtIG9mZnNldH0gYnl0ZXMgb2YgYSAke3RvdGFsU2l6ZX0gYnl0ZSBmaWxlIC0gaXQgY2hhbmdlZCBvciB2YW5pc2hlZCBtaWQtY29weSlgKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgb2Zmc2V0ICs9IGRhdGEubGVuZ3RoO1xuICAgICAgICAgICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvcGllZFNpemUgPSB0b3RhbFNpemU7XG4gICAgfVxuICAgIC8vIEV2ZXJ5IGJhY2tlbmQgZHJvcHMgYSBzdXBlcnNlZGVkIHdyaXRlIFNJTEVOVExZIChpdHMgb25seS10YWtlLWxhdGVzdCBpcyB0aGUgbGFzdCBsaW5lIG9mIGRlZmVuc2UgYWdhaW5zdCByYWNlcyB0aGUgdXAtZnJvbnQgY2hlY2sgY2FuJ3Qgc2VlKSwgc28gYSByZXR1cm5lZCBzZXQgaXMgbm90IHByb29mIHRoZSBjb3B5IGxhbmRlZCAtIG9ubHkgdGhlIGRlc3RpbmF0aW9uIHJlcG9ydGluZyB0aGUgZmlsZSBhdCBPVVIgdGltZSBvciBuZXdlciBpcy4gTmV3ZXIgYWxzbyBjb3VudHMgYXMgbGFuZGVkOiB0aGUgZGVzdGluYXRpb24gaXMgYXQgbGVhc3QgYXMgbmV3IGFzIHdoYXQgd2UgcHVzaGVkIChhbmQgYjIgYWx3YXlzIHN0YW1wcyBpdHMgb3duLCBsYXRlciwgdXBsb2FkIHRpbWUpLlxuICAgIGxldCBjb25maXJtZWQgPSBhd2FpdCB0by5nZXRJbmZvKHRvUGF0aCwgeyBub0ZhbGxiYWNrczogY29uZmlnLm5vRmFsbGJhY2tzIH0pO1xuICAgIGlmICghY29uZmlybWVkIHx8IGNvbmZpcm1lZC53cml0ZVRpbWUgPCB3cml0ZVRpbWUpIHtcbiAgICAgICAgbG9nU3RvcmFnZUVycm9yKGBDb3B5IG9mICR7SlNPTi5zdHJpbmdpZnkocGF0aCl9IGZyb20gJHtmcm9tLmdldERlYnVnTmFtZSgpfSB0byAke3RvLmdldERlYnVnTmFtZSgpfSR7dG9QYXRoICE9PSBwYXRoICYmIGAgKGFzICR7SlNPTi5zdHJpbmdpZnkodG9QYXRoKX0pYCB8fCBcIlwifSBkaWQgbm90IGxhbmQ6IHdlIHdyb3RlICR7Y29waWVkU2l6ZX0gYnl0ZXMgYXQgJHtuZXcgRGF0ZSh3cml0ZVRpbWUpLnRvSVNPU3RyaW5nKCl9LCBidXQgdGhlIGRlc3RpbmF0aW9uIHJlcG9ydHMgJHtjb25maXJtZWQgJiYgYCR7Y29uZmlybWVkLnNpemV9IGJ5dGVzIGF0ICR7bmV3IERhdGUoY29uZmlybWVkLndyaXRlVGltZSkudG9JU09TdHJpbmcoKX1gIHx8IFwibm90aGluZ1wifSAtIGl0IGRyb3BwZWQgdGhlIHdyaXRlIChhIG5ld2VyIHdyaXRlIG9yIGRlbGV0aW9uIHdvbiB0aGUgcmFjZSlgKTtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgcmV0dXJuIHsgd3JpdGVUaW1lLCBzaXplOiBjb3BpZWRTaXplIH07XG59XG5cbi8qKlxuICogTW92ZXMgb25lIGZpbGUgLSBiZXR3ZWVuIHR3byBhcmNoaXZlcywgb3IgYmV0d2VlbiB0d28gcGF0aHMgb2Ygb25lLiBXaGVuIGZyb20gYW5kIHRvIGFyZSB0aGUgU0FNRVxuICogYXJjaGl2ZXMgaW5zdGFuY2UgYW5kIGl0IGltcGxlbWVudHMgbW92ZSwgdGhlIGJhY2tlbmQgbW92ZXMgdGhlIGZpbGUgaXRzZWxmIChiYWNrYmxhemUgY29waWVzXG4gKiBzZXJ2ZXItc2lkZSwgZGlzayByZW5hbWVzLCB0aGUgc3RvcmFnZSBzZXJ2ZXIgcmVsb2NhdGVzIGl0IG5vZGUtc2lkZSkgYW5kIHRoZSBieXRlcyBuZXZlciB0cmF2ZWxcbiAqIHRocm91Z2ggdXMuIEV2ZXJ5dGhpbmcgZWxzZSBpcyB0aGUgc2FmZSBmYWxsYmFjazogY29weSwgdGhlbiBDT05GSVJNIHRoZSBkZXN0aW5hdGlvbiBhY3R1YWxseVxuICogcmVwb3J0cyB0aGUgZmlsZSAoZ2V0SW5mbyksIGFuZCBvbmx5IHRoZW4gZGVsZXRlIHRoZSBzb3VyY2UgLSB0aGUgb25lIG9yZGVyIGluIHdoaWNoIG5vIGZhaWx1cmVcbiAqIGNhbiBsb3NlIHRoZSBmaWxlLCBvbmx5IGF0IHdvcnN0IGxlYXZlIGl0IGluIGJvdGggcGxhY2VzLiBUaHJvd3Mgd2hlbiB0aGUgc291cmNlIGRvZXNuJ3QgaGF2ZSB0aGVcbiAqIGZpbGUsIG9yIHdoZW4gdGhlIGNvcHkgY2Fubm90IGJlIGNvbmZpcm1lZCAodGhlIHNvdXJjZSBpcyB0aGVuIGxlZnQgdW50b3VjaGVkKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1vdmVBcmNoaXZlRmlsZShjb25maWc6IHtcbiAgICBmcm9tOiBJQXJjaGl2ZXM7XG4gICAgdG86IElBcmNoaXZlcztcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgLyoqIFRoZSBwYXRoIGF0IHRoZSBkZXN0aW5hdGlvbiAtIGRlZmF1bHRzIHRvIHBhdGggKG1vdmluZyBiZXR3ZWVuIHR3byBhcmNoaXZlcykuIFJlcXVpcmVkIGluIHByYWN0aWNlIHdoZW4gZnJvbSBhbmQgdG8gYXJlIHRoZSBzYW1lIGFyY2hpdmVzLCB3aGVyZSB0aGUgc2FtZSBwYXRoIHdvdWxkIGJlIGEgbm8tb3AuICovXG4gICAgdG9QYXRoPzogc3RyaW5nO1xuICAgIG5vRmFsbGJhY2tzPzogYm9vbGVhbjtcbn0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBsZXQgeyBmcm9tLCB0bywgcGF0aCB9ID0gY29uZmlnO1xuICAgIGxldCB0b1BhdGggPSBjb25maWcudG9QYXRoIHx8IHBhdGg7XG4gICAgaWYgKGZyb20gPT09IHRvKSB7XG4gICAgICAgIGlmIChwYXRoID09PSB0b1BhdGgpIHJldHVybjtcbiAgICAgICAgaWYgKGZyb20ubW92ZSkge1xuICAgICAgICAgICAgYXdhaXQgZnJvbS5tb3ZlKHsgZnJvbVBhdGg6IHBhdGgsIHRvUGF0aCB9KTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBsZXQgY29waWVkID0gYXdhaXQgY29weUFyY2hpdmVGaWxlKHsgZnJvbSwgdG8sIHBhdGgsIHRvUGF0aCwgbm9GYWxsYmFja3M6IGNvbmZpZy5ub0ZhbGxiYWNrcyB9KTtcbiAgICBpZiAoIWNvcGllZCkge1xuICAgICAgICAvLyBVbmRlZmluZWQgaXMgdHdvIGNhc2VzIChzZWUgY29weUFyY2hpdmVGaWxlKSAtIGFza2luZyB0aGUgc291cmNlIHdoaWNoIG9uZSBrZWVwcyB0aGUgZXJyb3IgaG9uZXN0XG4gICAgICAgIGxldCBzb3VyY2VJbmZvID0gYXdhaXQgZnJvbS5nZXRJbmZvKHBhdGgsIHsgbm9GYWxsYmFja3M6IGNvbmZpZy5ub0ZhbGxiYWNrcyB9KTtcbiAgICAgICAgaWYgKHNvdXJjZUluZm8pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2Fubm90IG1vdmUgJHtKU09OLnN0cmluZ2lmeShwYXRoKX0gKCR7c291cmNlSW5mby5zaXplfSBieXRlcyBhdCAke25ldyBEYXRlKHNvdXJjZUluZm8ud3JpdGVUaW1lKS50b0lTT1N0cmluZygpfSkgZnJvbSAke2Zyb20uZ2V0RGVidWdOYW1lKCl9OiAke3RvLmdldERlYnVnTmFtZSgpfSBhbHJlYWR5IGhhcyBhIG5ld2VyIGZpbGUgYXQgJHtKU09OLnN0cmluZ2lmeSh0b1BhdGgpfSwgYW5kIHRoZSBjb3B5IHdhcyByZWZ1c2VkIHJhdGhlciB0aGFuIHJvbGwgaXQgYmFjayAtIHRoZSBzb3VyY2UgaXMgbGVmdCB1bnRvdWNoZWRgKTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBtb3ZlICR7SlNPTi5zdHJpbmdpZnkocGF0aCl9OiAke2Zyb20uZ2V0RGVidWdOYW1lKCl9IGRvZXMgbm90IGhhdmUgaXRgKTtcbiAgICB9XG4gICAgbGV0IGNvbmZpcm1lZCA9IGF3YWl0IHRvLmdldEluZm8odG9QYXRoLCB7IG5vRmFsbGJhY2tzOiBjb25maWcubm9GYWxsYmFja3MgfSk7XG4gICAgaWYgKCFjb25maXJtZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBOb3QgZGVsZXRpbmcgJHtKU09OLnN0cmluZ2lmeShwYXRoKX0gZnJvbSAke2Zyb20uZ2V0RGVidWdOYW1lKCl9IGFmdGVyIGNvcHlpbmcgaXQ6ICR7dG8uZ2V0RGVidWdOYW1lKCl9IGRvZXMgbm90IHJlcG9ydCAke0pTT04uc3RyaW5naWZ5KHRvUGF0aCl9ICh0aGUgY29weSBjbGFpbWVkIHRvIHN1Y2NlZWQsIHNvIHNvbWV0aGluZyBpcyB3cm9uZyAtIHRoZSBmaWxlIGlzIGxlZnQgYXQgdGhlIHNvdXJjZSlgKTtcbiAgICB9XG4gICAgYXdhaXQgZnJvbS5kZWwocGF0aCk7XG59XG4iXX0=
106
+ /* _JS_SOURCE_HASH = "e5ae243c31baf5c206ce3c1a0c7a06fc18386f9b58a521ee64e2b8329403d2eb"; */
@@ -14,6 +14,7 @@ import { SourcesList } from "./sourcesList";
14
14
  import { StoreSync } from "./storeSync";
15
15
  import { StoreConfig } from "./storeConfig";
16
16
  import { asDelayed, unwrapDelayed } from "./ArchivesDelayed";
17
+ import { logMutation, logStorageError, logStorageWarn } from "./storageLogs";
17
18
 
18
19
  // The storage engine of the remote storage server. Data lives in synchronization sources (at minimum an ArchivesDisk, the local disk); BlobStore keeps an index of every file (path, last modified time, size, and which source currently holds the data) in a BulkDatabase2, and answers every read from it (see ArchivesSource in IArchives.ts). Keeping that index in agreement with the sources is StoreSync's job, not this file's; what lives here is the index itself, reads, writes, and the validation a write has to pass.
19
20
 
@@ -71,7 +72,7 @@ let lastWrongTargetLog = 0;
71
72
  function logWrongTargetRejection(message: string): void {
72
73
  if (Date.now() - lastWrongTargetLog < WRONG_TARGET_LOG_THROTTLE) return;
73
74
  lastWrongTargetLog = Date.now();
74
- console.log(message);
75
+ logStorageError(message);
75
76
  }
76
77
 
77
78
  // The bucket store, and the only one: every bucket a server holds is served by these, one per store name in its routing config. Its sources are plain IArchives - the first is always the store's own disk folder, the rest are the configured peers (see createStoreSource) - so "no synchronization" is simply a store with no peers, not a different kind of store.
@@ -154,10 +155,10 @@ export class BlobStore {
154
155
  await this.ownDisk.set(ROUTING_FILE, Buffer.from(serializeRemoteConfig(provided)), { lastModified: Date.now() });
155
156
  await this.applyRoutingConfig();
156
157
  } else {
157
- console.warn(`Store ${JSON.stringify(this.storeName)} (folder ${this.folder}) initialized with no configuration, and the requesting client had no routing config naming it either - running unconfigured`);
158
+ logStorageWarn(`Store ${JSON.stringify(this.storeName)} (folder ${this.folder}) initialized with no configuration, and the requesting client had no routing config naming it either - running unconfigured`);
158
159
  }
159
160
  } catch (e) {
160
- console.error(`Asking the requesting client for the routing config of store ${JSON.stringify(this.storeName)} (folder ${this.folder}) failed - running unconfigured: ${(e as Error).stack ?? e}`);
161
+ logStorageError(`Asking the requesting client for the routing config of store ${JSON.stringify(this.storeName)} (folder ${this.folder}) failed - running unconfigured: ${(e as Error).stack ?? e}`);
161
162
  }
162
163
  }
163
164
  for (let i = 0; i < this.sources.length; i++) {
@@ -191,7 +192,7 @@ export class BlobStore {
191
192
  peers.push(source);
192
193
  }
193
194
  if (routing && !entries.length) {
194
- console.warn(`The routing config in store ${this.folder} (version ${getConfigVersion(routing)}) does not name this store (${JSON.stringify(this.storeName)}) as one of ours, so it stays a disk-only store`);
195
+ logStorageWarn(`The routing config in store ${this.folder} (version ${getConfigVersion(routing)}) does not name this store (${JSON.stringify(this.storeName)}) as one of ours, so it stays a disk-only store`);
195
196
  }
196
197
  let previousVersion = this.appliedRoutingVersion;
197
198
  let newVersion = routing && getConfigVersion(routing) || -1;
@@ -214,7 +215,7 @@ export class BlobStore {
214
215
  try {
215
216
  return parseRoutingData(data);
216
217
  } catch (e) {
217
- console.error(`Ignoring the routing config in store ${this.folder}: it could not be parsed (${(e as Error).message})`);
218
+ logStorageError(`Ignoring the routing config in store ${this.folder}: it could not be parsed: ${(e as Error).stack ?? e}`);
218
219
  return undefined;
219
220
  }
220
221
  }
@@ -229,7 +230,7 @@ export class BlobStore {
229
230
  public reapplyRoutingConfig(): void {
230
231
  let next = this.routingApplies.then(() => this.applyRoutingConfig());
231
232
  this.routingApplies = next.catch(() => { });
232
- void next.catch((e: Error) => console.error(`Applying the routing config in store ${this.folder} failed: ${e.stack ?? e}`));
233
+ void next.catch((e: Error) => logStorageError(`Applying the routing config in store ${this.folder} failed: ${e.stack ?? e}`));
233
234
  }
234
235
 
235
236
  // Our own disk first (always), then every peer whose route overlaps ours - the sources this store synchronizes with, as its own config describes them
@@ -451,6 +452,7 @@ export class BlobStore {
451
452
  }
452
453
  await this.set({ path: config.toPath, data: result.data });
453
454
  await this.del({ path: config.fromPath });
455
+ logMutation({ op: "move", folder: this.folder, store: this.storeName, path: config.fromPath, toPath: config.toPath, size: result.data.length });
454
456
  }
455
457
 
456
458
  public async getInfo(config: { path: string; includeTombstones?: boolean }): Promise<{ writeTime: number; size: number } | undefined> {
@@ -625,7 +627,7 @@ export class BlobStore {
625
627
  for (let i = 1; i < originalLength; i++) {
626
628
  if (!this.isLive(i) || matched.has(i)) continue;
627
629
  // The slot is dead the moment this returns; dropping the index entries it held is the part that reads the index, so it finishes in the background
628
- void this.sync.removeSource(i).catch((e: Error) => console.error(`Removing sync source ${this.sources[i].source.getDebugName()} (store ${this.folder}) failed: ${e.stack ?? e}`));
630
+ void this.sync.removeSource(i).catch((e: Error) => logStorageError(`Removing sync source ${this.sources[i].source.getDebugName()} (store ${this.folder}) failed: ${e.stack ?? e}`));
629
631
  }
630
632
  }
631
633
 
@@ -695,6 +697,7 @@ export class BlobStore {
695
697
  let info = await disk.getInfo(config.path);
696
698
  if (info) {
697
699
  this.setIndexEntry(config.path, { writeTime: info.writeTime, size: info.size, sourcesListIndex: this.sourcesListIndexOfSlot(sourceIndex) });
700
+ logMutation({ op: "setLarge", folder: this.folder, store: this.storeName, path: config.path, size: info.size, writeTime: info.writeTime, internal: config.internal });
698
701
  }
699
702
  }
700
703
  public async cancelLargeUpload(config: { id: string }): Promise<void> {
@@ -848,6 +851,7 @@ export class BlobStore {
848
851
  throw new Error(`Cannot undelete ${JSON.stringify(key)} (store ${this.folder}): it has no marked deletion to restore - it was never deleted here${this.getDeletedEntry(key) && ", or its deletion history was already dropped (a plain tombstone remains, but the bytes are gone)" || ""}`);
849
852
  }
850
853
  console.log(`Undeleted ${JSON.stringify(key)} (store ${this.folder}): the index entry (${marked?.size} bytes, deleted ${marked && new Date(marked.deleteTime).toISOString()}) is live again as of ${new Date(writeTime).toISOString()} - the bytes never left the disk`);
854
+ logMutation({ op: "undelete", folder: this.folder, store: this.storeName, path: key, size: marked?.size, writeTime, internal });
851
855
  this.config?.onIndexChanged?.(key);
852
856
  if (internal) return;
853
857
  // Peers marked their own copies when the deletion propagated, so the restore propagates the same way. Straight past any write delay - a buffered undelete could be read back as its placeholder byte.
@@ -859,7 +863,7 @@ export class BlobStore {
859
863
  if (this.sources[i].sourceConfig?.type !== "remote") continue;
860
864
  let push = unwrapDelayed(this.sources[i].source).set(key, UNDELETE_PLACEHOLDER, { lastModified: writeTime, undelete: true, noChecks: true, internal: true });
861
865
  void push.catch((e: Error) => {
862
- console.error(`Background undelete of ${key} on sync source ${this.sources[i].source.getDebugName()} failed (its scan of us re-finds the file anyway): ${e.stack ?? e}`);
866
+ logStorageError(`Background undelete of ${key} on sync source ${this.sources[i].source.getDebugName()} (store ${this.folder}) failed (its scan of us re-finds the file anyway): ${e.stack ?? e}`);
863
867
  });
864
868
  }
865
869
  }
@@ -910,6 +914,7 @@ export class BlobStore {
910
914
 
911
915
  /** Counts a synchronization transfer in the server's access statistics (see getStore's wiring): "sync get" for bytes pulled off a source, "sync set" for bytes pushed to one. */
912
916
  public noteSyncTransfer(operation: "sync get" | "sync set", path: string, bytes: number): void {
917
+ logMutation({ op: operation, folder: this.folder, store: this.storeName, path, size: bytes });
913
918
  this.config?.onSyncTransfer?.(operation, path, bytes);
914
919
  }
915
920
 
@@ -1026,10 +1031,12 @@ export class BlobStore {
1026
1031
  if (data.length === 0) {
1027
1032
  // The bytes stay on our disk as deletion history (see writeToSources) - the marked index entry is the whole of the deletion
1028
1033
  this.setIndexDeleted(key, config.lastModified);
1034
+ logMutation({ op: "del", folder: this.folder, store: this.storeName, path: key, writeTime: config.lastModified, internal: true });
1029
1035
  return;
1030
1036
  }
1031
1037
  await this.sources[0].source.set(key, data, { lastModified: config.lastModified, forceSetImmutable: true, noChecks: true });
1032
1038
  this.setIndexEntry(key, { writeTime: config.lastModified, size: data.length, sourcesListIndex: this.sourcesListIndexOfSlot(0) });
1039
+ logMutation({ op: "set", folder: this.folder, store: this.storeName, path: key, size: data.length, writeTime: config.lastModified, internal: true });
1033
1040
  }
1034
1041
 
1035
1042
  // The read's bytes came from a remote source, so write them onto our own base source (the local disk), which becomes the entry's new holder - reads only pay the remote fetch once
@@ -1094,6 +1101,7 @@ export class BlobStore {
1094
1101
  await this.sources[first].source.set(key, data, { lastModified: writeTime, noChecks: true });
1095
1102
  this.setIndexEntry(key, { writeTime, size: data.length, sourcesListIndex: this.sourcesListIndexOfSlot(first) });
1096
1103
  }
1104
+ logMutation({ op: data.length === 0 && "del" || "set", folder: this.folder, store: this.storeName, path: key, size: data.length, writeTime });
1097
1105
  if (isRouting) return;
1098
1106
  let route = getRoute(key);
1099
1107
  // Deletions did not consume the first slot (nothing is written anywhere locally), so it receives the propagated deletion like the rest - except slot 0, our own disk, whose bytes ARE the history being kept
@@ -1110,7 +1118,7 @@ export class BlobStore {
1110
1118
  push = this.sources[i].source.set(key, data, { lastModified: writeTime, forceSetImmutable: true, noChecks: true, internal: true });
1111
1119
  }
1112
1120
  void push.catch((e: Error) => {
1113
- console.error(`Background write of ${key} to sync source ${this.sources[i].source.getDebugName()} failed: ${e.stack ?? e}`);
1121
+ logStorageError(`Background write of ${key} to sync source ${this.sources[i].source.getDebugName()} (store ${this.folder}) failed: ${e.stack ?? e}`);
1114
1122
  });
1115
1123
  }
1116
1124
  }
@@ -627,6 +627,11 @@ export class ArchivesChain implements IArchives {
627
627
  // Inline rather than moveArchiveFile, which - given one archives that implements move - would just call back into this method
628
628
  let copied = await copyArchiveFile({ from: this, to: this, path: config.fromPath, toPath: config.toPath });
629
629
  if (!copied) {
630
+ // Undefined is two cases (see copyArchiveFile) - asking the source which one keeps the error honest
631
+ let sourceInfo = await this.getInfo(config.fromPath);
632
+ if (sourceInfo) {
633
+ throw new Error(`Cannot move ${JSON.stringify(config.fromPath)} (${sourceInfo.size} bytes at ${new Date(sourceInfo.writeTime).toISOString()}) to ${JSON.stringify(config.toPath)}: the destination already has a newer file, and the copy was refused rather than roll it back (${this.getDebugName()}) - the source is left untouched`);
634
+ }
630
635
  throw new Error(`Cannot move ${JSON.stringify(config.fromPath)} to ${JSON.stringify(config.toPath)}: the source file does not exist on ${this.getDebugName()}`);
631
636
  }
632
637
  let confirmed = await this.getInfo(config.toPath);