sliftutils 1.7.9 → 1.7.11
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.
- package/bin/storageserver.js +4 -0
- package/{teststorage → examplestorage}/browser.tsx +18 -22
- package/{teststorage/server.ts → examplestorage/exampleserver.ts} +4 -6
- package/index.d.ts +770 -84
- package/misc/dist/environment.ts.cache +3 -3
- package/misc/dist/getSecret.ts.cache +139 -0
- package/misc/dist/strings.ts.cache +13 -0
- package/misc/dist/zip.ts.cache +9 -0
- package/misc/getSecret.ts +38 -16
- package/misc/https/cloudflareHelpers.d.ts +19 -0
- package/misc/https/cloudflareHelpers.ts +69 -0
- package/misc/https/dist/certs.ts.cache +115 -179
- package/misc/https/dist/cloudflareHelpers.ts.cache +68 -0
- package/misc/https/dist/dns.ts.cache +112 -100
- package/misc/https/dist/hostServer.ts.cache +130 -0
- package/misc/https/dist/httpsCerts.ts.cache +24 -5
- package/misc/https/dist/persistentLocalStorage.ts.cache +3 -2
- package/misc/https/dns.d.ts +41 -8
- package/misc/https/dns.ts +104 -106
- package/misc/https/hostServer.d.ts +0 -3
- package/misc/https/hostServer.ts +12 -8
- package/package.json +3 -2
- package/render-utils/dist/observer.tsx.cache +3 -2
- package/spec.txt +0 -55
- package/storage/ArchivesDisk.d.ts +67 -0
- package/storage/ArchivesDisk.ts +369 -0
- package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +1242 -0
- package/storage/BulkDatabase2/dist/BulkDatabaseFormat.ts.cache +513 -0
- package/storage/BulkDatabase2/dist/BulkDatabaseMerge.ts.cache +352 -0
- package/storage/BulkDatabase2/dist/BulkDatabaseReader.ts.cache +306 -0
- package/storage/BulkDatabase2/dist/LoadedIndex.ts.cache +435 -0
- package/storage/BulkDatabase2/dist/WriteOverlay.ts.cache +58 -0
- package/storage/BulkDatabase2/dist/blockCache.ts.cache +179 -0
- package/storage/BulkDatabase2/dist/mergeLock.ts.cache +149 -0
- package/storage/BulkDatabase2/dist/mergeMarkers.ts.cache +101 -0
- package/storage/BulkDatabase2/dist/streamLog.ts.cache +227 -0
- package/storage/BulkDatabase2/dist/syncClient.ts.cache +104 -0
- package/storage/IArchives.d.ts +127 -1
- package/storage/IArchives.ts +201 -1
- package/storage/backblaze.d.ts +29 -2
- package/storage/backblaze.ts +214 -78
- package/storage/dist/ArchivesDisk.ts.cache +373 -0
- package/storage/dist/FileFolderAPI.tsx.cache +508 -65
- package/storage/dist/IArchives.ts.cache +44 -0
- package/storage/dist/IndexedDBFileFolderAPI.ts.cache +8 -2
- package/storage/dist/JSONStorage.ts.cache +6 -2
- package/storage/dist/PendingManager.tsx.cache +1 -1
- package/storage/dist/TransactionStorage.ts.cache +231 -92
- package/storage/dist/backblaze.ts.cache +797 -0
- package/storage/dist/fileSystemPointer.ts.cache +36 -3
- package/storage/dist/remoteFileStorage.ts.cache +495 -0
- package/storage/remoteStorage/ArchivesRemote.d.ts +34 -18
- package/storage/remoteStorage/ArchivesRemote.ts +85 -65
- package/storage/remoteStorage/ArchivesUrl.d.ts +48 -0
- package/storage/remoteStorage/ArchivesUrl.ts +86 -0
- package/storage/remoteStorage/accessPage.tsx +111 -28
- package/storage/remoteStorage/blobStore.d.ts +128 -25
- package/storage/remoteStorage/blobStore.ts +757 -290
- package/storage/remoteStorage/cliArgs.d.ts +1 -0
- package/storage/remoteStorage/cliArgs.ts +9 -0
- package/storage/remoteStorage/createArchives.d.ts +85 -0
- package/storage/remoteStorage/createArchives.ts +612 -0
- package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +197 -0
- package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +85 -0
- package/storage/remoteStorage/dist/accessPage.tsx.cache +222 -0
- package/storage/remoteStorage/dist/blobStore.ts.cache +837 -0
- package/storage/remoteStorage/dist/cliArgs.ts.cache +16 -0
- package/storage/remoteStorage/dist/createArchives.ts.cache +610 -0
- package/storage/remoteStorage/dist/remoteConfig.ts.cache +206 -0
- package/storage/remoteStorage/dist/sourceWrapper.ts.cache +223 -0
- package/storage/remoteStorage/dist/storageController.ts.cache +500 -0
- package/storage/remoteStorage/dist/storageServer.ts.cache +106 -0
- package/storage/remoteStorage/dist/storageServerCli.ts.cache +39 -0
- package/storage/remoteStorage/dist/storageServerState.ts.cache +518 -0
- package/storage/remoteStorage/grantAccess.js +4 -0
- package/storage/remoteStorage/grantAccessCli.d.ts +1 -0
- package/storage/remoteStorage/grantAccessCli.ts +27 -0
- package/storage/remoteStorage/remoteConfig.d.ts +29 -0
- package/storage/remoteStorage/remoteConfig.ts +194 -0
- package/storage/remoteStorage/sourceWrapper.d.ts +41 -0
- package/storage/remoteStorage/sourceWrapper.ts +223 -0
- package/storage/remoteStorage/spec.md +31 -0
- package/storage/remoteStorage/storageController.d.ts +30 -27
- package/storage/remoteStorage/storageController.ts +297 -159
- package/storage/remoteStorage/storageServer.d.ts +6 -0
- package/storage/remoteStorage/storageServer.ts +88 -94
- package/storage/remoteStorage/storageServerCli.d.ts +1 -0
- package/storage/remoteStorage/storageServerCli.ts +38 -0
- package/storage/remoteStorage/storageServerState.d.ts +43 -0
- package/storage/remoteStorage/storageServerState.ts +540 -0
- package/testsite/server.ts +0 -2
- package/yarn.lock +11 -6
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
+
//exports.BlobStore = exports.DEFAULT_FAST_WRITE_DELAY = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const caching_1 = require("socket-function/src/caching");
|
|
9
|
+
const batching_1 = require("socket-function/src/batching");
|
|
10
|
+
const misc_1 = require("socket-function/src/misc");
|
|
11
|
+
const format_1 = require("socket-function/src/formatting/format");
|
|
12
|
+
const IArchives_1 = require("../IArchives");
|
|
13
|
+
const ArchivesDisk_1 = require("../ArchivesDisk");
|
|
14
|
+
const backblaze_1 = require("../backblaze");
|
|
15
|
+
const remoteConfig_1 = require("./remoteConfig");
|
|
16
|
+
const BulkDatabaseBase_1 = require("../BulkDatabase2/BulkDatabaseBase");
|
|
17
|
+
const FileFolderAPI_1 = require("../FileFolderAPI");
|
|
18
|
+
// The storage engine of the remote storage server. Data lives in synchronization sources (at
|
|
19
|
+
// minimum an ArchivesDisk, the local disk); BlobStore keeps an index of every file (path, last
|
|
20
|
+
// modified time, size, and which source currently holds the data) in a BulkDatabase2, and
|
|
21
|
+
// synchronizes the index from all sources (see ArchivesSource in IArchives.ts).
|
|
22
|
+
// Every startup fully rescans each source's metadata, so the index self-heals; the file with the
|
|
23
|
+
// highest write time wins across all sources, so multiple sources need no stacking order.
|
|
24
|
+
exports.DEFAULT_FAST_WRITE_DELAY = misc_1.timeInMinute * 5;
|
|
25
|
+
const FAST_FLUSH_POLL = 1000 * 15;
|
|
26
|
+
// Index changes are buffered in memory and written to the BulkDatabase2 in batches
|
|
27
|
+
const INDEX_FLUSH_INTERVAL = 1000 * 30;
|
|
28
|
+
// Sources that support getChangesAfter are polled this often
|
|
29
|
+
const CHANGES_POLL_INTERVAL = 1000 * 60;
|
|
30
|
+
// Sources that don't support getChangesAfter get a full metadata rescan this often
|
|
31
|
+
const FULL_RESCAN_INTERVAL = 1000 * 60 * 60;
|
|
32
|
+
// On a request for a file the index doesn't know, changes-after sources are re-polled, at most
|
|
33
|
+
// this often
|
|
34
|
+
const MISS_CHECK_INTERVAL = 1000 * 5;
|
|
35
|
+
// Change polls re-request this much overlap, so clock skew between us and a source can't drop changes
|
|
36
|
+
const CHANGES_POLL_OVERLAP = misc_1.timeInMinute;
|
|
37
|
+
const SCAN_RETRY_DELAY = 1000 * 30;
|
|
38
|
+
// Deletes are tombstones (an empty file IS a missing file): the size-0 index entry is what lets a
|
|
39
|
+
// deletion propagate/reconcile like any other write, and it expires after this long
|
|
40
|
+
const TOMBSTONE_EXPIRY = 1000 * 60 * 60 * 24 * 7;
|
|
41
|
+
const TOMBSTONE_CLEANUP_INTERVAL = 1000 * 60 * 60;
|
|
42
|
+
// While a metadata scan or full sync is running, its progress is logged this often
|
|
43
|
+
const SYNC_PROGRESS_LOG_INTERVAL = 1000 * 60;
|
|
44
|
+
const DISK_LIMIT_CHECK_INTERVAL = 1000 * 60;
|
|
45
|
+
// Full syncs download this many files concurrently (high-latency sources like backblaze would
|
|
46
|
+
// otherwise crawl one round-trip at a time)
|
|
47
|
+
const FULL_SYNC_PARALLEL = 8;
|
|
48
|
+
class BlobStore {
|
|
49
|
+
constructor(folder, sources, config) {
|
|
50
|
+
this.folder = folder;
|
|
51
|
+
this.sources = sources;
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.stopped = { stop: false };
|
|
54
|
+
// The index's BulkDatabase2 files live under <folder>/index
|
|
55
|
+
this.index = new BulkDatabaseBase_1.BulkDatabaseBase("blobIndex", BulkDatabaseBase_1.noopReactiveDeps, async (p) => {
|
|
56
|
+
let base = new FileFolderAPI_1.NodeJSDirectoryHandleWrapper(path_1.default.join(this.folder, "index"));
|
|
57
|
+
for (let part of p.split("/")) {
|
|
58
|
+
if (part)
|
|
59
|
+
base = await base.getDirectoryHandle(part, { create: true });
|
|
60
|
+
}
|
|
61
|
+
return (0, FileFolderAPI_1.wrapHandle)(base);
|
|
62
|
+
});
|
|
63
|
+
// The in-memory copy of the index. All reads are served from it; changes are buffered in
|
|
64
|
+
// dirty and flushed to the BulkDatabase2 in batches (undefined = delete).
|
|
65
|
+
this.mem = new Map();
|
|
66
|
+
// Live totals over mem (tombstones excluded), adjusted on every mutation and recomputed on
|
|
67
|
+
// load - so any drift heals on restart. computeIndexTotals gives the walk-the-index truth.
|
|
68
|
+
this.indexFileCount = 0;
|
|
69
|
+
this.indexByteCount = 0;
|
|
70
|
+
// The same totals per holding source (index 0 = our disk, which readerDiskLimit bounds)
|
|
71
|
+
this.sourceFileCounts = this.sources.map(() => 0);
|
|
72
|
+
this.sourceByteCounts = this.sources.map(() => 0);
|
|
73
|
+
// Background scans / full syncs currently in progress (a Set - one source can have a change
|
|
74
|
+
// poll's full sync and a rescan overlapping)
|
|
75
|
+
this.syncActivities = new Set();
|
|
76
|
+
this.dirty = new Map();
|
|
77
|
+
this.overlay = new Map();
|
|
78
|
+
this.sourceStates = this.sources.map(() => ({
|
|
79
|
+
supportsChangesAfter: false,
|
|
80
|
+
initialScan: (0, misc_1.promiseObj)(),
|
|
81
|
+
scanComplete: false,
|
|
82
|
+
scannedCount: 0,
|
|
83
|
+
changesAfterTime: 0,
|
|
84
|
+
lastMissCheck: 0,
|
|
85
|
+
}));
|
|
86
|
+
this.init = (0, caching_1.lazy)(async () => {
|
|
87
|
+
var _a;
|
|
88
|
+
await this.loadIndex();
|
|
89
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
90
|
+
void this.runSourceSync(i);
|
|
91
|
+
}
|
|
92
|
+
(0, batching_1.runInfinitePoll)(FAST_FLUSH_POLL, () => this.flushOverlay(), this.stopped);
|
|
93
|
+
(0, batching_1.runInfinitePoll)(INDEX_FLUSH_INTERVAL, () => this.flushIndex(), this.stopped);
|
|
94
|
+
(0, batching_1.runInfinitePoll)(TOMBSTONE_CLEANUP_INTERVAL, () => this.cleanupTombstones(), this.stopped);
|
|
95
|
+
if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.readerDiskLimit) {
|
|
96
|
+
(0, batching_1.runInfinitePoll)(DISK_LIMIT_CHECK_INTERVAL, () => this.enforceDiskLimit(), this.stopped);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// readerDiskLimit: the disk is only a bounded read cache, so once it exceeds the limit, the
|
|
100
|
+
// least recently used files are deleted from it - but ONLY when another source verifiably
|
|
101
|
+
// holds a same-or-newer copy (the only copy of a file is never deleted), and the index entry
|
|
102
|
+
// repoints to that source so reads keep working (re-caching on the next read).
|
|
103
|
+
this.evicting = false;
|
|
104
|
+
}
|
|
105
|
+
// Stops all synchronization scans/polls and flushes pending writes. Used when a bucket's
|
|
106
|
+
// routing config changes and the store is rebuilt with new sources.
|
|
107
|
+
async dispose() {
|
|
108
|
+
this.stopped.stop = true;
|
|
109
|
+
await this.flushOverlay(true);
|
|
110
|
+
await this.flushIndex();
|
|
111
|
+
}
|
|
112
|
+
async loadIndex() {
|
|
113
|
+
let [writeTimes, sizes, sources] = await Promise.all([
|
|
114
|
+
this.index.getColumn("writeTime"),
|
|
115
|
+
this.index.getColumn("size"),
|
|
116
|
+
this.index.getColumn("source"),
|
|
117
|
+
]);
|
|
118
|
+
let sizeMap = new Map(sizes.map(x => [x.key, x.value]));
|
|
119
|
+
let sourceMap = new Map(sources.map(x => [x.key, x.value]));
|
|
120
|
+
for (let entry of writeTimes) {
|
|
121
|
+
let size = sizeMap.get(entry.key);
|
|
122
|
+
let source = sourceMap.get(entry.key);
|
|
123
|
+
// Explicit checks, as 0 is a valid size and a valid source number
|
|
124
|
+
if (size === undefined || source === undefined)
|
|
125
|
+
continue;
|
|
126
|
+
let full = { writeTime: entry.value, size, source, changedAt: entry.time, lastAccess: entry.time };
|
|
127
|
+
this.mem.set(entry.key, full);
|
|
128
|
+
this.countEntry(full, 1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
countEntry(entry, direction) {
|
|
132
|
+
if (!entry || entry.size === 0)
|
|
133
|
+
return;
|
|
134
|
+
this.indexFileCount += direction;
|
|
135
|
+
this.indexByteCount += entry.size * direction;
|
|
136
|
+
// A stale entry can reference a source that no longer exists (getIndexEntry cleans those)
|
|
137
|
+
if (this.sources[entry.source]) {
|
|
138
|
+
this.sourceFileCounts[entry.source] += direction;
|
|
139
|
+
this.sourceByteCounts[entry.source] += entry.size * direction;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
setIndexEntry(key, entry) {
|
|
143
|
+
var _a, _b;
|
|
144
|
+
let full = { ...entry, changedAt: Date.now(), lastAccess: Date.now() };
|
|
145
|
+
this.countEntry(this.mem.get(key), -1);
|
|
146
|
+
this.countEntry(full, 1);
|
|
147
|
+
this.mem.set(key, full);
|
|
148
|
+
this.dirty.set(key, full);
|
|
149
|
+
(_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.onIndexChanged) === null || _b === void 0 ? void 0 : _b.call(_a, key);
|
|
150
|
+
}
|
|
151
|
+
deleteIndexEntry(key) {
|
|
152
|
+
let existing = this.mem.get(key);
|
|
153
|
+
if (!existing)
|
|
154
|
+
return;
|
|
155
|
+
this.countEntry(existing, -1);
|
|
156
|
+
this.mem.delete(key);
|
|
157
|
+
this.dirty.set(key, undefined);
|
|
158
|
+
}
|
|
159
|
+
/** The cheap always-current totals plus any in-progress background synchronization. */
|
|
160
|
+
getSyncProgress() {
|
|
161
|
+
var _a;
|
|
162
|
+
return {
|
|
163
|
+
index: { fileCount: this.indexFileCount, byteCount: this.indexByteCount },
|
|
164
|
+
sources: this.sources.map((x, i) => ({
|
|
165
|
+
debugName: x.source.getDebugName(),
|
|
166
|
+
fileCount: this.sourceFileCounts[i],
|
|
167
|
+
byteCount: this.sourceByteCounts[i],
|
|
168
|
+
})),
|
|
169
|
+
readerDiskLimit: (_a = this.config) === null || _a === void 0 ? void 0 : _a.readerDiskLimit,
|
|
170
|
+
syncing: [...this.syncActivities],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** Walks the whole index for exact totals - more expensive than getSyncProgress, but immune to
|
|
174
|
+
* any drift in the maintained counters (and loads the index first, so it's never cold zeros). */
|
|
175
|
+
async computeIndexTotals() {
|
|
176
|
+
await this.init();
|
|
177
|
+
let fileCount = 0;
|
|
178
|
+
let byteCount = 0;
|
|
179
|
+
let sources = this.sources.map(x => ({ debugName: x.source.getDebugName(), fileCount: 0, byteCount: 0 }));
|
|
180
|
+
for (let entry of this.mem.values()) {
|
|
181
|
+
if (entry.size === 0)
|
|
182
|
+
continue;
|
|
183
|
+
fileCount++;
|
|
184
|
+
byteCount += entry.size;
|
|
185
|
+
let source = sources[entry.source];
|
|
186
|
+
if (source) {
|
|
187
|
+
source.fileCount++;
|
|
188
|
+
source.byteCount += entry.size;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return { fileCount, byteCount, sources };
|
|
192
|
+
}
|
|
193
|
+
async flushIndex() {
|
|
194
|
+
if (!this.dirty.size)
|
|
195
|
+
return;
|
|
196
|
+
let dirty = this.dirty;
|
|
197
|
+
this.dirty = new Map();
|
|
198
|
+
let writes = [];
|
|
199
|
+
let deletes = [];
|
|
200
|
+
for (let [key, entry] of dirty) {
|
|
201
|
+
if (entry) {
|
|
202
|
+
writes.push({ key, writeTime: entry.writeTime, size: entry.size, source: entry.source });
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
deletes.push(key);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (writes.length)
|
|
209
|
+
await this.index.writeBatch(writes);
|
|
210
|
+
if (deletes.length)
|
|
211
|
+
await this.index.deleteBatch(deletes);
|
|
212
|
+
}
|
|
213
|
+
// ── synchronization ──
|
|
214
|
+
async runSourceSync(sourceIndex) {
|
|
215
|
+
let { source, noFullSync } = this.sources[sourceIndex];
|
|
216
|
+
let state = this.sourceStates[sourceIndex];
|
|
217
|
+
let listing;
|
|
218
|
+
while (!this.stopped.stop) {
|
|
219
|
+
try {
|
|
220
|
+
let config = await source.getConfig();
|
|
221
|
+
state.supportsChangesAfter = !!(config.supportsChangesAfter && source.getChangesAfter);
|
|
222
|
+
listing = await this.scanSource(sourceIndex);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
console.error(`Initial scan of sync source ${source.getDebugName()} failed, retrying:`, e);
|
|
227
|
+
await (0, batching_1.delay)(SCAN_RETRY_DELAY);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
state.scanComplete = true;
|
|
231
|
+
state.initialScan.resolve(undefined);
|
|
232
|
+
if (this.stopped.stop)
|
|
233
|
+
return;
|
|
234
|
+
if (listing) {
|
|
235
|
+
await this.reconcileSource(sourceIndex, listing);
|
|
236
|
+
}
|
|
237
|
+
if (!noFullSync) {
|
|
238
|
+
try {
|
|
239
|
+
await this.copySourceFiles(sourceIndex);
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
console.error(`Copying files from sync source ${source.getDebugName()} failed:`, e);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (state.supportsChangesAfter) {
|
|
246
|
+
(0, batching_1.runInfinitePoll)(CHANGES_POLL_INTERVAL, async () => {
|
|
247
|
+
await this.pollChanges(sourceIndex);
|
|
248
|
+
if (!noFullSync)
|
|
249
|
+
await this.copySourceFiles(sourceIndex);
|
|
250
|
+
}, this.stopped);
|
|
251
|
+
// Change polls only show what the source HAS, never what it's missing, so pushes run on
|
|
252
|
+
// the full-rescan cadence (findInfo on an index-backed source is cheap)
|
|
253
|
+
(0, batching_1.runInfinitePoll)(FULL_RESCAN_INTERVAL, async () => {
|
|
254
|
+
let files = await source.findInfo("");
|
|
255
|
+
await this.reconcileSource(sourceIndex, new Map(files.map(x => [x.path, x.createTime])));
|
|
256
|
+
}, this.stopped);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
(0, batching_1.runInfinitePoll)(FULL_RESCAN_INTERVAL, async () => {
|
|
260
|
+
let rescan = await this.scanSource(sourceIndex);
|
|
261
|
+
await this.reconcileSource(sourceIndex, rescan);
|
|
262
|
+
if (!noFullSync)
|
|
263
|
+
await this.copySourceFiles(sourceIndex);
|
|
264
|
+
}, this.stopped);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Full metadata scan (size, writeTime, path) of one source, applied to the index. Returns the
|
|
268
|
+
// source's listing (path -> write time), which reconcileSource uses for the push direction.
|
|
269
|
+
async scanSource(sourceIndex) {
|
|
270
|
+
var _a, _b;
|
|
271
|
+
let { source } = this.sources[sourceIndex];
|
|
272
|
+
let state = this.sourceStates[sourceIndex];
|
|
273
|
+
let scanStart = Date.now();
|
|
274
|
+
let scanLogged = false;
|
|
275
|
+
let activity = { type: "metadataScan", sourceDebugName: source.getDebugName(), startTime: scanStart };
|
|
276
|
+
this.syncActivities.add(activity);
|
|
277
|
+
let progressTimer = setInterval(() => {
|
|
278
|
+
scanLogged = true;
|
|
279
|
+
console.log(`Metadata scan of ${source.getDebugName()} still running (${Math.round((Date.now() - scanStart) / 1000)}s, store ${this.folder})`);
|
|
280
|
+
}, SYNC_PROGRESS_LOG_INTERVAL);
|
|
281
|
+
(_b = (_a = progressTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
282
|
+
let files;
|
|
283
|
+
try {
|
|
284
|
+
files = await source.findInfo("");
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
clearInterval(progressTimer);
|
|
288
|
+
this.syncActivities.delete(activity);
|
|
289
|
+
}
|
|
290
|
+
if (scanLogged) {
|
|
291
|
+
console.log(`Metadata scan of ${source.getDebugName()} finished: ${files.length} files in ${Math.round((Date.now() - scanStart) / 1000)}s (store ${this.folder})`);
|
|
292
|
+
}
|
|
293
|
+
let seen = new Map();
|
|
294
|
+
for (let file of files) {
|
|
295
|
+
seen.set(file.path, file.createTime);
|
|
296
|
+
this.applyScanned(sourceIndex, file);
|
|
297
|
+
}
|
|
298
|
+
state.scannedCount = files.length;
|
|
299
|
+
// Index entries this source was the holder of, but that vanished from it (e.g. deleted
|
|
300
|
+
// while we were offline), come out of the index. Entries changed after the scan started
|
|
301
|
+
// are kept — the scan listing may simply predate them. Tombstones have no physical file
|
|
302
|
+
// for a listing to vouch for, so they're exempt (cleanupTombstones expires them instead).
|
|
303
|
+
for (let [key, entry] of this.mem) {
|
|
304
|
+
if (entry.source !== sourceIndex)
|
|
305
|
+
continue;
|
|
306
|
+
if (entry.size === 0)
|
|
307
|
+
continue;
|
|
308
|
+
if (seen.has(key))
|
|
309
|
+
continue;
|
|
310
|
+
if (entry.changedAt >= scanStart)
|
|
311
|
+
continue;
|
|
312
|
+
this.deleteIndexEntry(key);
|
|
313
|
+
}
|
|
314
|
+
state.changesAfterTime = Math.max(state.changesAfterTime, scanStart - CHANGES_POLL_OVERLAP);
|
|
315
|
+
return seen;
|
|
316
|
+
}
|
|
317
|
+
// The push direction of synchronization: everything we know that the source is missing (or
|
|
318
|
+
// holds an older copy of) is written to it — including deletions, as tombstone writes. This is
|
|
319
|
+
// what heals a source whose background writes failed (e.g. it was down): the next scan sees
|
|
320
|
+
// what's missing and re-sends it.
|
|
321
|
+
async reconcileSource(sourceIndex, listing) {
|
|
322
|
+
var _a;
|
|
323
|
+
let { source, validWindow, route } = this.sources[sourceIndex];
|
|
324
|
+
let acceptsWrites = (0, IArchives_1.windowAcceptsWrites)(validWindow);
|
|
325
|
+
try {
|
|
326
|
+
for (let [key, entry] of this.mem) {
|
|
327
|
+
if (this.stopped.stop)
|
|
328
|
+
return;
|
|
329
|
+
if (entry.source === sourceIndex)
|
|
330
|
+
continue;
|
|
331
|
+
// Past-window sources only receive the routing file (see writeToSources), and
|
|
332
|
+
// sharded sources only the keys routing into them
|
|
333
|
+
if (key !== remoteConfig_1.ROUTING_FILE) {
|
|
334
|
+
if (!acceptsWrites)
|
|
335
|
+
continue;
|
|
336
|
+
if (!(0, remoteConfig_1.routeContains)(route, (0, remoteConfig_1.getRoute)(key)))
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
let theirTime = listing.get(key);
|
|
340
|
+
if (theirTime !== undefined && theirTime >= entry.writeTime)
|
|
341
|
+
continue;
|
|
342
|
+
if (entry.size === 0) {
|
|
343
|
+
// A deletion only needs pushing while the source still holds an older copy
|
|
344
|
+
if (theirTime === undefined)
|
|
345
|
+
continue;
|
|
346
|
+
await source.set(key, Buffer.alloc(0), { lastModified: entry.writeTime });
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
let result = await this.sources[entry.source].source.get2(key);
|
|
350
|
+
if (!result)
|
|
351
|
+
continue;
|
|
352
|
+
await source.set(key, result.data, { lastModified: result.writeTime });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
// Abort the pass instead of logging per file; the next scan cycle retries
|
|
357
|
+
console.error(`Reconciling sync source ${source.getDebugName()} failed: ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
applyScanned(sourceIndex, file) {
|
|
361
|
+
let { validWindow, route } = this.sources[sourceIndex];
|
|
362
|
+
let [windowStart, windowEnd] = validWindow;
|
|
363
|
+
if (file.createTime < windowStart || file.createTime > windowEnd)
|
|
364
|
+
return;
|
|
365
|
+
// A partially-overlapping shard's listing includes keys outside our route; ignore them
|
|
366
|
+
if (file.path !== remoteConfig_1.ROUTING_FILE && !(0, remoteConfig_1.routeContains)(route, (0, remoteConfig_1.getRoute)(file.path)))
|
|
367
|
+
return;
|
|
368
|
+
let existing = this.mem.get(file.path);
|
|
369
|
+
// The highest write time wins across all sources (ties keep the existing entry)
|
|
370
|
+
if (existing && file.createTime <= existing.writeTime)
|
|
371
|
+
return;
|
|
372
|
+
this.setIndexEntry(file.path, { writeTime: file.createTime, size: file.size, source: sourceIndex });
|
|
373
|
+
}
|
|
374
|
+
async pollChanges(sourceIndex) {
|
|
375
|
+
let { source } = this.sources[sourceIndex];
|
|
376
|
+
if (!source.getChangesAfter)
|
|
377
|
+
return;
|
|
378
|
+
let state = this.sourceStates[sourceIndex];
|
|
379
|
+
let pollStart = Date.now();
|
|
380
|
+
let changes = await source.getChangesAfter(state.changesAfterTime);
|
|
381
|
+
for (let file of changes) {
|
|
382
|
+
this.applyScanned(sourceIndex, file);
|
|
383
|
+
}
|
|
384
|
+
state.scannedCount += changes.length;
|
|
385
|
+
state.changesAfterTime = pollStart - CHANGES_POLL_OVERLAP;
|
|
386
|
+
}
|
|
387
|
+
// Downloads the files a source currently holds onto our own base source (the local disk),
|
|
388
|
+
// preserving their modified times — so a newer local write always wins. Skipped for noFullSync
|
|
389
|
+
// sources (fronting a large database without copying it); reads still down-cache lazily.
|
|
390
|
+
async copySourceFiles(sourceIndex) {
|
|
391
|
+
var _a, _b;
|
|
392
|
+
if (sourceIndex === 0)
|
|
393
|
+
return;
|
|
394
|
+
let { source } = this.sources[sourceIndex];
|
|
395
|
+
let pending = [];
|
|
396
|
+
let totalBytes = 0;
|
|
397
|
+
for (let [key, entry] of this.mem) {
|
|
398
|
+
if (entry.source !== sourceIndex)
|
|
399
|
+
continue;
|
|
400
|
+
if (entry.size === 0)
|
|
401
|
+
continue;
|
|
402
|
+
pending.push({ key, entry });
|
|
403
|
+
totalBytes += entry.size;
|
|
404
|
+
}
|
|
405
|
+
if (!pending.length)
|
|
406
|
+
return;
|
|
407
|
+
let activity = {
|
|
408
|
+
type: "fullSync",
|
|
409
|
+
sourceDebugName: source.getDebugName(),
|
|
410
|
+
startTime: Date.now(),
|
|
411
|
+
doneFiles: 0,
|
|
412
|
+
totalFiles: pending.length,
|
|
413
|
+
doneBytes: 0,
|
|
414
|
+
totalBytes,
|
|
415
|
+
};
|
|
416
|
+
this.syncActivities.add(activity);
|
|
417
|
+
let progressLogged = false;
|
|
418
|
+
let logProgress = () => {
|
|
419
|
+
progressLogged = true;
|
|
420
|
+
console.log(`Full sync from ${source.getDebugName()} (store ${this.folder}): ${activity.doneFiles}/${pending.length} files (${((activity.doneFiles || 0) / pending.length * 100).toFixed(1)}%), ${(0, format_1.formatNumber)(activity.doneBytes || 0)}B/${(0, format_1.formatNumber)(totalBytes)}B (${(totalBytes && (activity.doneBytes || 0) / totalBytes * 100 || 100).toFixed(1)}%)`);
|
|
421
|
+
};
|
|
422
|
+
let progressTimer = setInterval(logProgress, SYNC_PROGRESS_LOG_INTERVAL);
|
|
423
|
+
(_b = (_a = progressTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
424
|
+
try {
|
|
425
|
+
let nextIndex = 0;
|
|
426
|
+
let failed = false;
|
|
427
|
+
let copyWorker = async () => {
|
|
428
|
+
while (!failed && !this.stopped.stop) {
|
|
429
|
+
let index = nextIndex++;
|
|
430
|
+
if (index >= pending.length)
|
|
431
|
+
return;
|
|
432
|
+
let { key, entry } = pending[index];
|
|
433
|
+
let result = await source.get2(key);
|
|
434
|
+
if (result) {
|
|
435
|
+
await this.sources[0].source.set(key, result.data, { lastModified: result.writeTime });
|
|
436
|
+
// Only move the entry's source if it wasn't changed while we copied
|
|
437
|
+
if (this.mem.get(key) === entry) {
|
|
438
|
+
this.setIndexEntry(key, { writeTime: result.writeTime, size: result.data.length, source: 0 });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
activity.doneFiles = (activity.doneFiles || 0) + 1;
|
|
442
|
+
activity.doneBytes = (activity.doneBytes || 0) + entry.size;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
let workers = [];
|
|
446
|
+
for (let i = 0; i < Math.min(FULL_SYNC_PARALLEL, pending.length); i++) {
|
|
447
|
+
workers.push(copyWorker().catch((e) => {
|
|
448
|
+
// Stop the other workers pulling new files, then surface the error
|
|
449
|
+
failed = true;
|
|
450
|
+
throw e;
|
|
451
|
+
}));
|
|
452
|
+
}
|
|
453
|
+
await Promise.all(workers);
|
|
454
|
+
}
|
|
455
|
+
finally {
|
|
456
|
+
clearInterval(progressTimer);
|
|
457
|
+
this.syncActivities.delete(activity);
|
|
458
|
+
// A sync slow enough to have logged progress also logs its completion
|
|
459
|
+
if (progressLogged) {
|
|
460
|
+
logProgress();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// findInfo and getChangesAfter list from the index, so they must wait for our own base
|
|
465
|
+
// source's initial scan (which might lag minutes) before the listing is trustworthy. The base
|
|
466
|
+
// (local disk) is implicitly required - remote sources are not, they come and go.
|
|
467
|
+
async waitForRequiredScans() {
|
|
468
|
+
await this.sourceStates[0].initialScan.promise;
|
|
469
|
+
}
|
|
470
|
+
// A requested file isn't in the index: our own base source (implicitly required) is checked
|
|
471
|
+
// directly if its initial scan hasn't finished, and changes-after sources are re-polled (at
|
|
472
|
+
// most every 5 seconds)
|
|
473
|
+
async checkMissingKey(key) {
|
|
474
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
475
|
+
let { source } = this.sources[i];
|
|
476
|
+
let state = this.sourceStates[i];
|
|
477
|
+
if (i === 0 && !state.scanComplete) {
|
|
478
|
+
let info = await source.getInfo(key);
|
|
479
|
+
if (info) {
|
|
480
|
+
this.applyScanned(i, { path: key, createTime: info.writeTime, size: info.size });
|
|
481
|
+
}
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (state.supportsChangesAfter && Date.now() - state.lastMissCheck > MISS_CHECK_INTERVAL) {
|
|
485
|
+
state.lastMissCheck = Date.now();
|
|
486
|
+
await this.pollChanges(i);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async getIndexEntry(key) {
|
|
491
|
+
let entry = this.mem.get(key);
|
|
492
|
+
if (entry && this.sources[entry.source])
|
|
493
|
+
return entry;
|
|
494
|
+
if (entry) {
|
|
495
|
+
// The source list changed and this entry's source no longer exists; treat as missing
|
|
496
|
+
this.deleteIndexEntry(key);
|
|
497
|
+
}
|
|
498
|
+
await this.checkMissingKey(key);
|
|
499
|
+
return this.mem.get(key);
|
|
500
|
+
}
|
|
501
|
+
// ── data operations ──
|
|
502
|
+
async get(key, config) {
|
|
503
|
+
let result = await this.get2(key, config);
|
|
504
|
+
return result && result.data || undefined;
|
|
505
|
+
}
|
|
506
|
+
async get2(key, config) {
|
|
507
|
+
await this.init();
|
|
508
|
+
let range = config === null || config === void 0 ? void 0 : config.range;
|
|
509
|
+
let overlayEntry = this.overlay.get(key);
|
|
510
|
+
if (overlayEntry) {
|
|
511
|
+
// An empty file IS a missing file (tombstone)
|
|
512
|
+
if (overlayEntry.data.length === 0)
|
|
513
|
+
return undefined;
|
|
514
|
+
let data = overlayEntry.data;
|
|
515
|
+
let size = data.length;
|
|
516
|
+
if (range) {
|
|
517
|
+
data = data.subarray(Math.min(range.start, data.length), Math.min(range.end, data.length));
|
|
518
|
+
}
|
|
519
|
+
return { data, writeTime: overlayEntry.t, size };
|
|
520
|
+
}
|
|
521
|
+
let entry = await this.getIndexEntry(key);
|
|
522
|
+
if (!entry)
|
|
523
|
+
return undefined;
|
|
524
|
+
if (entry.size === 0)
|
|
525
|
+
return undefined;
|
|
526
|
+
entry.lastAccess = Date.now();
|
|
527
|
+
let holder = entry.source;
|
|
528
|
+
let result;
|
|
529
|
+
let holderError;
|
|
530
|
+
try {
|
|
531
|
+
result = await this.sources[holder].source.get2(key, { range });
|
|
532
|
+
}
|
|
533
|
+
catch (e) {
|
|
534
|
+
holderError = e;
|
|
535
|
+
}
|
|
536
|
+
if (result) {
|
|
537
|
+
// Ranged reads can't populate a cache (they're partial)
|
|
538
|
+
if (holder !== 0 && !range) {
|
|
539
|
+
await this.cacheRead(key, result);
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
// The holder is down or lost the file. ANY other source's copy beats no value - even an
|
|
544
|
+
// OLDER one - and it's copied onto our disk so the next read doesn't depend on luck.
|
|
545
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
546
|
+
if (i === holder)
|
|
547
|
+
continue;
|
|
548
|
+
let fallback;
|
|
549
|
+
try {
|
|
550
|
+
fallback = await this.sources[i].source.get2(key);
|
|
551
|
+
}
|
|
552
|
+
catch (_a) {
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (!fallback)
|
|
556
|
+
continue;
|
|
557
|
+
await this.cacheRead(key, fallback);
|
|
558
|
+
let data = fallback.data;
|
|
559
|
+
if (range) {
|
|
560
|
+
data = data.subarray(Math.min(range.start, data.length), Math.min(range.end, data.length));
|
|
561
|
+
}
|
|
562
|
+
return { data, writeTime: fallback.writeTime, size: fallback.size };
|
|
563
|
+
}
|
|
564
|
+
if (holderError)
|
|
565
|
+
throw holderError;
|
|
566
|
+
// The holder answered "not there" and no other source has it either: the entry was stale
|
|
567
|
+
this.deleteIndexEntry(key);
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
// The read's bytes came from a remote source, so write them onto our own base source (the
|
|
571
|
+
// local disk), which becomes the entry's new holder - reads only pay the remote fetch once
|
|
572
|
+
async cacheRead(key, result) {
|
|
573
|
+
await this.sources[0].source.set(key, result.data, { lastModified: result.writeTime });
|
|
574
|
+
this.setIndexEntry(key, { writeTime: result.writeTime, size: result.data.length, source: 0 });
|
|
575
|
+
}
|
|
576
|
+
async set(key, data, config) {
|
|
577
|
+
await this.init();
|
|
578
|
+
let lastModified = config === null || config === void 0 ? void 0 : config.lastModified;
|
|
579
|
+
if (lastModified) {
|
|
580
|
+
(0, IArchives_1.assertValidLastModified)(lastModified);
|
|
581
|
+
let overlayEntry = this.overlay.get(key);
|
|
582
|
+
let entry = this.mem.get(key);
|
|
583
|
+
let currentTime = overlayEntry && overlayEntry.t || entry && entry.writeTime || 0;
|
|
584
|
+
// An older write never overwrites a newer one (see IArchives.set)
|
|
585
|
+
if (lastModified < currentTime)
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
let writeTime = lastModified || Date.now();
|
|
589
|
+
if (config === null || config === void 0 ? void 0 : config.fast) {
|
|
590
|
+
let writeDelay = config.writeDelay || exports.DEFAULT_FAST_WRITE_DELAY;
|
|
591
|
+
this.overlay.set(key, { data, t: writeTime, flushAt: Date.now() + writeDelay });
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
this.overlay.delete(key);
|
|
595
|
+
await this.writeToSources(key, data, writeTime);
|
|
596
|
+
}
|
|
597
|
+
async del(key, config) {
|
|
598
|
+
// Deletes are tombstone writes (an empty file IS a missing file): the size-0 index entry is
|
|
599
|
+
// ordered by write time like any other write, propagates through synchronization, and
|
|
600
|
+
// expires after TOMBSTONE_EXPIRY
|
|
601
|
+
await this.set(key, Buffer.alloc(0), config);
|
|
602
|
+
}
|
|
603
|
+
getWritableSources(config) {
|
|
604
|
+
let writable = [];
|
|
605
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
606
|
+
if (!(config === null || config === void 0 ? void 0 : config.ignoreWindow) && !(0, IArchives_1.windowAcceptsWrites)(this.sources[i].validWindow))
|
|
607
|
+
continue;
|
|
608
|
+
writable.push(i);
|
|
609
|
+
}
|
|
610
|
+
return writable;
|
|
611
|
+
}
|
|
612
|
+
async writeToSources(key, data, writeTime) {
|
|
613
|
+
// The routing file is exempt from the valid-window and route write filters: the CONFIG
|
|
614
|
+
// must keep flowing to every source, or a client probing one of them first would adopt a
|
|
615
|
+
// stale config. Only data writes stop.
|
|
616
|
+
let isRouting = key === remoteConfig_1.ROUTING_FILE;
|
|
617
|
+
let writable = this.getWritableSources({ ignoreWindow: isRouting });
|
|
618
|
+
let first = writable.shift();
|
|
619
|
+
if (first === undefined) {
|
|
620
|
+
throw new Error(`No source accepts writes (every source's valid window is in the past), so writes cannot be stored (store ${this.folder})`);
|
|
621
|
+
}
|
|
622
|
+
// Only our own (first) source blocks the write. Downstream sources are written in the
|
|
623
|
+
// background: a down downstream source must not fail or stall writes, and reconcileSource
|
|
624
|
+
// re-sends anything they missed once they come back.
|
|
625
|
+
if (data.length === 0) {
|
|
626
|
+
// A tombstone stores nothing on our own source - the index entry alone records it
|
|
627
|
+
await this.sources[first].source.del(key);
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
await this.sources[first].source.set(key, data, { lastModified: writeTime });
|
|
631
|
+
}
|
|
632
|
+
this.setIndexEntry(key, { writeTime, size: data.length, source: first });
|
|
633
|
+
let route = !isRouting && (0, remoteConfig_1.getRoute)(key) || 0;
|
|
634
|
+
for (let i of writable) {
|
|
635
|
+
if (!isRouting && !(0, remoteConfig_1.routeContains)(this.sources[i].route, route))
|
|
636
|
+
continue;
|
|
637
|
+
// Downstream sources receive tombstones as actual empty writes, so their listings show
|
|
638
|
+
// the deletion (size 0) and other stores scan it in as a tombstone
|
|
639
|
+
void this.sources[i].source.set(key, data, { lastModified: writeTime }).catch((e) => {
|
|
640
|
+
var _a;
|
|
641
|
+
console.error(`Background write of ${key} to sync source ${this.sources[i].source.getDebugName()} failed: ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
async getInfo(key) {
|
|
646
|
+
await this.init();
|
|
647
|
+
let overlayEntry = this.overlay.get(key);
|
|
648
|
+
if (overlayEntry) {
|
|
649
|
+
return { writeTime: overlayEntry.t, size: overlayEntry.data.length };
|
|
650
|
+
}
|
|
651
|
+
let entry = await this.getIndexEntry(key);
|
|
652
|
+
if (!entry)
|
|
653
|
+
return undefined;
|
|
654
|
+
return { writeTime: entry.writeTime, size: entry.size };
|
|
655
|
+
}
|
|
656
|
+
async findInfo(prefix, config) {
|
|
657
|
+
await this.init();
|
|
658
|
+
await this.waitForRequiredScans();
|
|
659
|
+
let infos = new Map();
|
|
660
|
+
for (let [key, entry] of this.mem) {
|
|
661
|
+
if (!key.startsWith(prefix))
|
|
662
|
+
continue;
|
|
663
|
+
// Tombstones are missing files, so listings hide them
|
|
664
|
+
if (entry.size === 0)
|
|
665
|
+
continue;
|
|
666
|
+
infos.set(key, { path: key, createTime: entry.writeTime, size: entry.size });
|
|
667
|
+
}
|
|
668
|
+
for (let [key, overlayEntry] of this.overlay) {
|
|
669
|
+
if (!key.startsWith(prefix))
|
|
670
|
+
continue;
|
|
671
|
+
if (overlayEntry.data.length === 0) {
|
|
672
|
+
infos.delete(key);
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
infos.set(key, { path: key, createTime: overlayEntry.t, size: overlayEntry.data.length });
|
|
676
|
+
}
|
|
677
|
+
let files = (0, ArchivesDisk_1.applyFindInfoShape)(Array.from(infos.values()), prefix, config);
|
|
678
|
+
(0, misc_1.sort)(files, x => x.path);
|
|
679
|
+
return files;
|
|
680
|
+
}
|
|
681
|
+
// All files changed after the given time — fast, straight from the in-memory index. Filters on
|
|
682
|
+
// when WE learned of the change (changedAt), so files synchronized late (with old write times)
|
|
683
|
+
// are still reported. Deletions ARE reported, as size-0 tombstone entries — that's how they
|
|
684
|
+
// propagate to stores syncing from us.
|
|
685
|
+
async getChangesAfter(time) {
|
|
686
|
+
await this.init();
|
|
687
|
+
await this.waitForRequiredScans();
|
|
688
|
+
let files = [];
|
|
689
|
+
for (let [key, entry] of this.mem) {
|
|
690
|
+
if (entry.changedAt <= time)
|
|
691
|
+
continue;
|
|
692
|
+
if (this.overlay.has(key))
|
|
693
|
+
continue;
|
|
694
|
+
files.push({ path: key, createTime: entry.writeTime, size: entry.size });
|
|
695
|
+
}
|
|
696
|
+
for (let [key, overlayEntry] of this.overlay) {
|
|
697
|
+
if (overlayEntry.t <= time)
|
|
698
|
+
continue;
|
|
699
|
+
files.push({ path: key, createTime: overlayEntry.t, size: overlayEntry.data.length });
|
|
700
|
+
}
|
|
701
|
+
(0, misc_1.sort)(files, x => x.path);
|
|
702
|
+
return files;
|
|
703
|
+
}
|
|
704
|
+
async getSyncStatus() {
|
|
705
|
+
await this.init();
|
|
706
|
+
return {
|
|
707
|
+
allScansComplete: this.sourceStates.every(x => x.scanComplete),
|
|
708
|
+
indexSize: this.mem.size,
|
|
709
|
+
sources: this.sources.map((x, i) => ({
|
|
710
|
+
debugName: x.source.getDebugName(),
|
|
711
|
+
validWindow: x.validWindow,
|
|
712
|
+
route: x.route,
|
|
713
|
+
noFullSync: x.noFullSync,
|
|
714
|
+
supportsChangesAfter: this.sourceStates[i].supportsChangesAfter,
|
|
715
|
+
initialScanComplete: this.sourceStates[i].scanComplete,
|
|
716
|
+
scannedCount: this.sourceStates[i].scannedCount,
|
|
717
|
+
})),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
// ── large uploads ──
|
|
721
|
+
// Large uploads stream onto the local disk source directly (they may not fit in memory)
|
|
722
|
+
getDiskSource() {
|
|
723
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
724
|
+
let source = this.sources[i].source;
|
|
725
|
+
if (source instanceof ArchivesDisk_1.ArchivesDisk)
|
|
726
|
+
return { disk: source, sourceIndex: i };
|
|
727
|
+
}
|
|
728
|
+
throw new Error(`Large uploads require an ArchivesDisk source, and this store has none (store ${this.folder})`);
|
|
729
|
+
}
|
|
730
|
+
async startLargeUpload() {
|
|
731
|
+
await this.init();
|
|
732
|
+
return await this.getDiskSource().disk.startLargeUpload();
|
|
733
|
+
}
|
|
734
|
+
async appendLargeUpload(id, data) {
|
|
735
|
+
await this.getDiskSource().disk.appendLargeUpload(id, data);
|
|
736
|
+
}
|
|
737
|
+
async finishLargeUpload(id, key) {
|
|
738
|
+
let { disk, sourceIndex } = this.getDiskSource();
|
|
739
|
+
await disk.finishLargeUpload(id, key);
|
|
740
|
+
this.overlay.delete(key);
|
|
741
|
+
let info = await disk.getInfo(key);
|
|
742
|
+
if (info) {
|
|
743
|
+
this.setIndexEntry(key, { writeTime: info.writeTime, size: info.size, source: sourceIndex });
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
async cancelLargeUpload(id) {
|
|
747
|
+
await this.getDiskSource().disk.cancelLargeUpload(id);
|
|
748
|
+
}
|
|
749
|
+
async flushOverlay(force) {
|
|
750
|
+
let now = Date.now();
|
|
751
|
+
for (let [key, entry] of this.overlay) {
|
|
752
|
+
if (!force && entry.flushAt > now)
|
|
753
|
+
continue;
|
|
754
|
+
await this.writeToSources(key, entry.data, entry.t);
|
|
755
|
+
// Only remove if it wasn't overwritten while we were flushing
|
|
756
|
+
if (this.overlay.get(key) === entry) {
|
|
757
|
+
this.overlay.delete(key);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
async enforceDiskLimit() {
|
|
762
|
+
var _a;
|
|
763
|
+
let limit = (_a = this.config) === null || _a === void 0 ? void 0 : _a.readerDiskLimit;
|
|
764
|
+
if (!limit || this.evicting)
|
|
765
|
+
return;
|
|
766
|
+
if (this.sourceByteCounts[0] <= limit)
|
|
767
|
+
return;
|
|
768
|
+
this.evicting = true;
|
|
769
|
+
try {
|
|
770
|
+
let candidates = [];
|
|
771
|
+
for (let [key, entry] of this.mem) {
|
|
772
|
+
if (entry.source !== 0 || entry.size === 0 || key === remoteConfig_1.ROUTING_FILE)
|
|
773
|
+
continue;
|
|
774
|
+
candidates.push({ key, entry });
|
|
775
|
+
}
|
|
776
|
+
(0, misc_1.sort)(candidates, x => x.entry.lastAccess);
|
|
777
|
+
for (let { key, entry } of candidates) {
|
|
778
|
+
if (this.stopped.stop)
|
|
779
|
+
return;
|
|
780
|
+
if (this.sourceByteCounts[0] <= limit)
|
|
781
|
+
break;
|
|
782
|
+
if (this.mem.get(key) !== entry)
|
|
783
|
+
continue;
|
|
784
|
+
let holder;
|
|
785
|
+
for (let i = 1; i < this.sources.length; i++) {
|
|
786
|
+
try {
|
|
787
|
+
let info = await this.sources[i].source.getInfo(key);
|
|
788
|
+
if (info && info.writeTime >= entry.writeTime) {
|
|
789
|
+
holder = i;
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
catch (_b) {
|
|
794
|
+
// A down source just can't vouch for this file right now
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (holder === undefined)
|
|
798
|
+
continue;
|
|
799
|
+
await this.sources[0].source.del(key);
|
|
800
|
+
this.setIndexEntry(key, { writeTime: entry.writeTime, size: entry.size, source: holder });
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
finally {
|
|
804
|
+
this.evicting = false;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// Tombstones only need to exist long enough for every store to learn of the deletion; expired
|
|
808
|
+
// ones come out of the index. The physical empty file is removed only on backblaze sources:
|
|
809
|
+
// remote stores expire their own tombstones (a del there would just mint a fresh one), and our
|
|
810
|
+
// own disk never stored anything for it.
|
|
811
|
+
async cleanupTombstones() {
|
|
812
|
+
let cutoff = Date.now() - TOMBSTONE_EXPIRY;
|
|
813
|
+
for (let [key, entry] of this.mem) {
|
|
814
|
+
if (this.stopped.stop)
|
|
815
|
+
return;
|
|
816
|
+
if (entry.size !== 0)
|
|
817
|
+
continue;
|
|
818
|
+
if (entry.writeTime > cutoff)
|
|
819
|
+
continue;
|
|
820
|
+
this.deleteIndexEntry(key);
|
|
821
|
+
for (let sourceEntry of this.sources) {
|
|
822
|
+
if (!(0, IArchives_1.windowAcceptsWrites)(sourceEntry.validWindow))
|
|
823
|
+
continue;
|
|
824
|
+
let source = sourceEntry.source;
|
|
825
|
+
if (!(source instanceof backblaze_1.ArchivesBackblaze))
|
|
826
|
+
continue;
|
|
827
|
+
void source.del(key).catch((e) => {
|
|
828
|
+
var _a;
|
|
829
|
+
console.error(`Removing expired tombstone ${key} from ${source.getDebugName()} failed: ${(_a = e.stack) !== null && _a !== void 0 ? _a : e}`);
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
exports.BlobStore = BlobStore;
|
|
836
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvYlN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYmxvYlN0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLGdEQUF3QjtBQUN4Qix5REFBbUQ7QUFDbkQsMkRBQXNFO0FBQ3RFLG1EQUEwRTtBQUMxRSxrRUFBcUU7QUFDckUsNENBR3NCO0FBQ3RCLGtEQUFtRTtBQUNuRSw0Q0FBaUQ7QUFDakQsaURBQXVFO0FBQ3ZFLHdFQUF1RjtBQUN2RixvREFBOEY7QUFFOUYsNkZBQTZGO0FBQzdGLCtGQUErRjtBQUMvRiwwRkFBMEY7QUFDMUYsZ0ZBQWdGO0FBQ2hGLGlHQUFpRztBQUNqRywwRkFBMEY7QUFFN0UsUUFBQSx3QkFBd0IsR0FBRyxtQkFBWSxHQUFHLENBQUMsQ0FBQztBQUN6RCxNQUFNLGVBQWUsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO0FBQ2xDLG1GQUFtRjtBQUNuRixNQUFNLG9CQUFvQixHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7QUFDdkMsNkRBQTZEO0FBQzdELE1BQU0scUJBQXFCLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztBQUN4QyxtRkFBbUY7QUFDbkYsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUM1QywrRkFBK0Y7QUFDL0YsYUFBYTtBQUNiLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQztBQUNyQyxzR0FBc0c7QUFDdEcsTUFBTSxvQkFBb0IsR0FBRyxtQkFBWSxDQUFDO0FBQzFDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztBQUNuQyxrR0FBa0c7QUFDbEcsb0ZBQW9GO0FBQ3BGLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUNqRCxNQUFNLDBCQUEwQixHQUFHLElBQUksR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDO0FB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QUUsS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2hDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJO2dCQUFFLE9BQU87WUFDOUIsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLENBQUM7Z0JBQUUsU0FBUztZQUMvQixJQUFJLEtBQUssQ0FBQyxTQUFTLEdBQUcsTUFBTTtnQkFBRSxTQUFTO1lBQ3ZDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMzQixLQUFLLElBQUksV0FBVyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDbkMsSUFBSSxDQUFDLElBQUEsK0JBQW1CLEVBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQztvQkFBRSxTQUFTO2dCQUM1RCxJQUFJLE1BQU0sR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDO2dCQUNoQyxJQUFJLENBQUMsQ0FBQyxNQUFNLFlBQVksNkJBQWlCLENBQUM7b0JBQUUsU0FBUztnQkFDckQsS0FBSyxNQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQVEsRUFBRSxFQUFFOztvQkFDcEMsT0FBTyxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsR0FBRyxTQUFTLE1BQU0sQ0FBQyxZQUFZLEVBQUUsWUFBWSxNQUFBLENBQUMsQ0FBQyxLQUFLLG1DQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7Z0JBQzdHLENBQUMsQ0FBQyxDQUFDO1lBQ1AsQ0FBQztRQUNMLENBQUM7SUFDTCxDQUFDO0NBQ0o7QUFudkJELDhCQW12QkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHsgbGF6eSB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL2NhY2hpbmdcIjtcbmltcG9ydCB7IHJ1bkluZmluaXRlUG9sbCwgZGVsYXkgfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy9iYXRjaGluZ1wiO1xuaW1wb3J0IHsgdGltZUluTWludXRlLCBzb3J0LCBwcm9taXNlT2JqIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvbWlzY1wiO1xuaW1wb3J0IHsgZm9ybWF0TnVtYmVyIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvZm9ybWF0dGluZy9mb3JtYXRcIjtcbmltcG9ydCB7XG4gICAgSUFyY2hpdmVzLCBBcmNoaXZlRmlsZUluZm8sIEFyY2hpdmVzU291cmNlLCBBcmNoaXZlc1N5bmNTdGF0dXMsIGFzc2VydFZhbGlkTGFzdE1vZGlmaWVkLFxuICAgIHdpbmRvd0FjY2VwdHNXcml0ZXMsIFN5bmNBY3Rpdml0eSxcbn0gZnJvbSBcIi4uL0lBcmNoaXZlc1wiO1xuaW1wb3J0IHsgQXJjaGl2ZXNEaXNrLCBhcHBseUZpbmRJbmZvU2hhcGUgfSBmcm9tIFwiLi4vQXJjaGl2ZXNEaXNrXCI7XG5pbXBvcnQgeyBBcmNoaXZlc0JhY2tibGF6ZSB9IGZyb20gXCIuLi9iYWNrYmxhemVcIjtcbmltcG9ydCB7IFJPVVRJTkdfRklMRSwgZ2V0Um91dGUsIHJvdXRlQ29udGFpbnMgfSBmcm9tIFwiLi9yZW1vdGVDb25maWdcIjtcbmltcG9ydCB7IEJ1bGtEYXRhYmFzZUJhc2UsIG5vb3BSZWFjdGl2ZURlcHMgfSBmcm9tIFwiLi4vQnVsa0RhdGFiYXNlMi9CdWxrRGF0YWJhc2VCYXNlXCI7XG5pbXBvcnQgeyB3cmFwSGFuZGxlLCBOb2RlSlNEaXJlY3RvcnlIYW5kbGVXcmFwcGVyLCBEaXJlY3RvcnlXcmFwcGVyIH0gZnJvbSBcIi4uL0ZpbGVGb2xkZXJBUElcIjtcblxuLy8gVGhlIHN0b3JhZ2UgZW5naW5lIG9mIHRoZSByZW1vdGUgc3RvcmFnZSBzZXJ2ZXIuIERhdGEgbGl2ZXMgaW4gc3luY2hyb25pemF0aW9uIHNvdXJjZXMgKGF0XG4vLyBtaW5pbXVtIGFuIEFyY2hpdmVzRGlzaywgdGhlIGxvY2FsIGRpc2spOyBCbG9iU3RvcmUga2VlcHMgYW4gaW5kZXggb2YgZXZlcnkgZmlsZSAocGF0aCwgbGFzdFxuLy8gbW9kaWZpZWQgdGltZSwgc2l6ZSwgYW5kIHdoaWNoIHNvdXJjZSBjdXJyZW50bHkgaG9sZHMgdGhlIGRhdGEpIGluIGEgQnVsa0RhdGFiYXNlMiwgYW5kXG4vLyBzeW5jaHJvbml6ZXMgdGhlIGluZGV4IGZyb20gYWxsIHNvdXJjZXMgKHNlZSBBcmNoaXZlc1NvdXJjZSBpbiBJQXJjaGl2ZXMudHMpLlxuLy8gRXZlcnkgc3RhcnR1cCBmdWxseSByZXNjYW5zIGVhY2ggc291cmNlJ3MgbWV0YWRhdGEsIHNvIHRoZSBpbmRleCBzZWxmLWhlYWxzOyB0aGUgZmlsZSB3aXRoIHRoZVxuLy8gaGlnaGVzdCB3cml0ZSB0aW1lIHdpbnMgYWNyb3NzIGFsbCBzb3VyY2VzLCBzbyBtdWx0aXBsZSBzb3VyY2VzIG5lZWQgbm8gc3RhY2tpbmcgb3JkZXIuXG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX0ZBU1RfV1JJVEVfREVMQVkgPSB0aW1lSW5NaW51dGUgKiA1O1xuY29uc3QgRkFTVF9GTFVTSF9QT0xMID0gMTAwMCAqIDE1O1xuLy8gSW5kZXggY2hhbmdlcyBhcmUgYnVmZmVyZWQgaW4gbWVtb3J5IGFuZCB3cml0dGVuIHRvIHRoZSBCdWxrRGF0YWJhc2UyIGluIGJhdGNoZXNcbmNvbnN0IElOREVYX0ZMVVNIX0lOVEVSVkFMID0gMTAwMCAqIDMwO1xuLy8gU291cmNlcyB0aGF0IHN1cHBvcnQgZ2V0Q2hhbmdlc0FmdGVyIGFyZSBwb2xsZWQgdGhpcyBvZnRlblxuY29uc3QgQ0hBTkdFU19QT0xMX0lOVEVSVkFMID0gMTAwMCAqIDYwO1xuLy8gU291cmNlcyB0aGF0IGRvbid0IHN1cHBvcnQgZ2V0Q2hhbmdlc0FmdGVyIGdldCBhIGZ1bGwgbWV0YWRhdGEgcmVzY2FuIHRoaXMgb2Z0ZW5cbmNvbnN0IEZVTExfUkVTQ0FOX0lOVEVSVkFMID0gMTAwMCAqIDYwICogNjA7XG4vLyBPbiBhIHJlcXVlc3QgZm9yIGEgZmlsZSB0aGUgaW5kZXggZG9lc24ndCBrbm93LCBjaGFuZ2VzLWFmdGVyIHNvdXJjZXMgYXJlIHJlLXBvbGxlZCwgYXQgbW9zdFxuLy8gdGhpcyBvZnRlblxuY29uc3QgTUlTU19DSEVDS19JTlRFUlZBTCA9IDEwMDAgKiA1O1xuLy8gQ2hhbmdlIHBvbGxzIHJlLXJlcXVlc3QgdGhpcyBtdWNoIG92ZXJsYXAsIHNvIGNsb2NrIHNrZXcgYmV0d2VlbiB1cyBhbmQgYSBzb3VyY2UgY2FuJ3QgZHJvcCBjaGFuZ2VzXG5jb25zdCBDSEFOR0VTX1BPTExfT1ZFUkxBUCA9IHRpbWVJbk1pbnV0ZTtcbmNvbnN0IFNDQU5fUkVUUllfREVMQVkgPSAxMDAwICogMzA7XG4vLyBEZWxldGVzIGFyZSB0b21ic3RvbmVzIChhbiBlbXB0eSBmaWxlIElTIGEgbWlzc2luZyBmaWxlKTogdGhlIHNpemUtMCBpbmRleCBlbnRyeSBpcyB3aGF0IGxldHMgYVxuLy8gZGVsZXRpb24gcHJvcGFnYXRlL3JlY29uY2lsZSBsaWtlIGFueSBvdGhlciB3cml0ZSwgYW5kIGl0IGV4cGlyZXMgYWZ0ZXIgdGhpcyBsb25nXG5jb25zdCBUT01CU1RPTkVfRVhQSVJZID0gMTAwMCAqIDYwICogNjAgKiAyNCAqIDc7XG5jb25zdCBUT01CU1RPTkVfQ0xFQU5VUF9JTlRFUlZBTCA9IDEwMDAgKiA2MCAqIDYwO1xuLy8gV2hpbGUgYSBtZXRhZGF0YSBzY2FuIG9yIGZ1bGwgc3luYyBpcyBydW5uaW5nLCBpdHMgcHJvZ3Jlc3MgaXMgbG9nZ2VkIHRoaXMgb2Z0ZW5cbmNvbnN0IFNZTkNfUFJPR1JFU1NfTE9HX0lOVEVSVkFMID0gMTAwMCAqIDYwO1xuY29uc3QgRElTS19MSU1JVF9DSEVDS19JTlRFUlZBTCA9IDEwMDAgKiA2MDtcbi8vIEZ1bGwgc3luY3MgZG93bmxvYWQgdGhpcyBtYW55IGZpbGVzIGNvbmN1cnJlbnRseSAoaGlnaC1sYXRlbmN5IHNvdXJjZXMgbGlrZSBiYWNrYmxhemUgd291bGRcbi8vIG90aGVyd2lzZSBjcmF3bCBvbmUgcm91bmQtdHJpcCBhdCBhIHRpbWUpXG5jb25zdCBGVUxMX1NZTkNfUEFSQUxMRUwgPSA4O1xuXG5leHBvcnQgdHlwZSBXcml0ZUNvbmZpZyA9IHtcbiAgICAvLyBSZXNvbHZlIG9uY2UgdGhlIHdyaXRlIGlzIGluIG1lbW9yeTsgZmx1c2ggdG8gdGhlIHNvdXJjZXMgYWZ0ZXIgd3JpdGVEZWxheSwgY29hbGVzY2luZ1xuICAgIC8vIHdyaXRlcyB0byB0aGUgc2FtZSBrZXkgKG9ubHkgdGhlIGxhdGVzdCBpcyB3cml0dGVuKS4gRGF0YSBpcyBsb3N0IGlmIHRoZSBwcm9jZXNzIGNyYXNoZXMgZmlyc3QuXG4gICAgZmFzdD86IGJvb2xlYW47XG4gICAgd3JpdGVEZWxheT86IG51bWJlcjtcbiAgICAvLyBTdGFtcHMgdGhlIHdyaXRlIHdpdGggdGhpcyBsYXN0LXdyaXRlIHRpbWUgaW5zdGVhZCBvZiBub3cuIE9sZGVyIHRoYW4gdGhlIGN1cnJlbnQgZmlsZSdzXG4gICAgLy8gdGltZSBuby1vcHM7IG1vcmUgdGhhbiAxNSBtaW51dGVzIGluIHRoZSBmdXR1cmUgdGhyb3dzLiBTZWUgSUFyY2hpdmVzLnNldC5cbiAgICBsYXN0TW9kaWZpZWQ/OiBudW1iZXI7XG59O1xuXG4vLyBXaGF0IHRoZSBzdG9yYWdlIHNlcnZlciBuZWVkcyBmcm9tIGEgYnVja2V0J3Mgc3RvcmUuIEJsb2JTdG9yZSBpbXBsZW1lbnRzIGl0IGZ1bGx5OyBBcmNoaXZlc0Rpc2tcbi8vIGFsc28gc2F0aXNmaWVzIGl0ICh1c2VkIGRpcmVjdGx5IGZvciByYXdEaXNrIGJ1Y2tldHMpLCBtaW51cyB0aGUgb3B0aW9uYWwgaW5kZXgtYmFja2VkIG1ldGhvZHMuXG5leHBvcnQgdHlwZSBJQnVja2V0U3RvcmUgPSB7XG4gICAgZ2V0KGZpbGVOYW1lOiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8QnVmZmVyIHwgdW5kZWZpbmVkPjtcbiAgICBnZXQyKGZpbGVOYW1lOiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8eyBkYXRhOiBCdWZmZXI7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZD47XG4gICAgc2V0KGZpbGVOYW1lOiBzdHJpbmcsIGRhdGE6IEJ1ZmZlciwgY29uZmlnPzogV3JpdGVDb25maWcpOiBQcm9taXNlPHZvaWQ+O1xuICAgIGRlbChmaWxlTmFtZTogc3RyaW5nLCBjb25maWc/OiBXcml0ZUNvbmZpZyk6IFByb21pc2U8dm9pZD47XG4gICAgZ2V0SW5mbyhmaWxlTmFtZTogc3RyaW5nKTogUHJvbWlzZTx7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZD47XG4gICAgZmluZEluZm8ocHJlZml4OiBzdHJpbmcsIGNvbmZpZz86IHsgc2hhbGxvdz86IGJvb2xlYW47IHR5cGU/OiBcImZpbGVzXCIgfCBcImZvbGRlcnNcIiB9KTogUHJvbWlzZTxBcmNoaXZlRmlsZUluZm9bXT47XG4gICAgZ2V0Q2hhbmdlc0FmdGVyPyh0aW1lOiBudW1iZXIpOiBQcm9taXNlPEFyY2hpdmVGaWxlSW5mb1tdPjtcbiAgICBnZXRTeW5jU3RhdHVzPygpOiBQcm9taXNlPEFyY2hpdmVzU3luY1N0YXR1cz47XG4gICAgZ2V0U3luY1Byb2dyZXNzPygpOiB7IGluZGV4OiB7IGZpbGVDb3VudDogbnVtYmVyOyBieXRlQ291bnQ6IG51bWJlciB9OyBzeW5jaW5nOiBTeW5jQWN0aXZpdHlbXSB9O1xuICAgIGNvbXB1dGVJbmRleFRvdGFscz8oKTogUHJvbWlzZTx7IGZpbGVDb3VudDogbnVtYmVyOyBieXRlQ291bnQ6IG51bWJlciB9PjtcbiAgICBzdGFydExhcmdlVXBsb2FkKCk6IFByb21pc2U8c3RyaW5nPjtcbiAgICBhcHBlbmRMYXJnZVVwbG9hZChpZDogc3RyaW5nLCBkYXRhOiBCdWZmZXIpOiBQcm9taXNlPHZvaWQ+O1xuICAgIGZpbmlzaExhcmdlVXBsb2FkKGlkOiBzdHJpbmcsIGtleTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPjtcbiAgICBjYW5jZWxMYXJnZVVwbG9hZChpZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPjtcbn07XG5cbnR5cGUgT3ZlcmxheUVudHJ5ID0ge1xuICAgIC8vIEEgemVyby1sZW5ndGggYnVmZmVyIGlzIGEgcGVuZGluZyBkZWxldGUgKHRvbWJzdG9uZSlcbiAgICBkYXRhOiBCdWZmZXI7XG4gICAgdDogbnVtYmVyO1xuICAgIGZsdXNoQXQ6IG51bWJlcjtcbn07XG5cbi8vIE9uZSByb3cgb2YgdGhlIEJ1bGtEYXRhYmFzZTIgaW5kZXggKGtleSBpcyB0aGUgZmlsZSBwYXRoKVxudHlwZSBCbG9iSW5kZXhFbnRyeSA9IHtcbiAgICBrZXk6IHN0cmluZztcbiAgICB3cml0ZVRpbWU6IG51bWJlcjtcbiAgICBzaXplOiBudW1iZXI7XG4gICAgLy8gV2hpY2ggc3luY2hyb25pemF0aW9uIHNvdXJjZSBjdXJyZW50bHkgaG9sZHMgdGhlIGRhdGEgKGFuIGluZGV4IGludG8gdGhlIHNvdXJjZXMgYXJyYXkpXG4gICAgc291cmNlOiBudW1iZXI7XG59O1xudHlwZSBJbmRleEVudHJ5ID0ge1xuICAgIHdyaXRlVGltZTogbnVtYmVyO1xuICAgIHNpemU6IG51bWJlcjtcbiAgICBzb3VyY2U6IG51bWJlcjtcbiAgICAvLyBXaGVuIFdFIGxhc3QgY2hhbmdlZCB0aGlzIGVudHJ5IChub3QgdGhlIGZpbGUncyB3cml0ZSB0aW1lKSDigJQgd2hhdCBnZXRDaGFuZ2VzQWZ0ZXIgZmlsdGVyc1xuICAgIC8vIG9uLCBzbyBsYXRlLWFycml2aW5nIGZpbGVzIHdpdGggb2xkIHdyaXRlIHRpbWVzIGFyZSBzdGlsbCByZXBvcnRlZCBhcyBjaGFuZ2VzXG4gICAgY2hhbmdlZEF0OiBudW1iZXI7XG4gICAgLy8gSW4tbWVtb3J5IG9ubHkgKG5vdCBwZXJzaXN0ZWQpOiB3aGVuIHRoZSBmaWxlIHdhcyBsYXN0IHNlcnZlZCwgZm9yIHJlYWRlckRpc2tMaW1pdCdzIExSVVxuICAgIC8vIGV2aWN0aW9uLiBTdGFydHMgYXMgY2hhbmdlZEF0IG9uIGxvYWQuXG4gICAgbGFzdEFjY2VzczogbnVtYmVyO1xufTtcblxudHlwZSBTb3VyY2VTdGF0ZSA9IHtcbiAgICBzdXBwb3J0c0NoYW5nZXNBZnRlcjogYm9vbGVhbjtcbiAgICBpbml0aWFsU2NhbjogUmV0dXJuVHlwZTx0eXBlb2YgcHJvbWlzZU9iaj47XG4gICAgc2NhbkNvbXBsZXRlOiBib29sZWFuO1xuICAgIC8vIEZpbGVzIHNlZW4gaW4gdGhpcyBzb3VyY2UncyBzY2FucyAvIGNoYW5nZSBwb2xscyBzbyBmYXJcbiAgICBzY2FubmVkQ291bnQ6IG51bWJlcjtcbiAgICAvLyBXYXRlcm1hcmsgZm9yIGdldENoYW5nZXNBZnRlciBwb2xsc1xuICAgIGNoYW5nZXNBZnRlclRpbWU6IG51bWJlcjtcbiAgICBsYXN0TWlzc0NoZWNrOiBudW1iZXI7XG59O1xuXG5leHBvcnQgY2xhc3MgQmxvYlN0b3JlIGltcGxlbWVudHMgSUJ1Y2tldFN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBmb2xkZXI6IHN0cmluZyxcbiAgICAgICAgcHJpdmF0ZSBzb3VyY2VzOiBBcmNoaXZlc1NvdXJjZVtdLFxuICAgICAgICBwcml2YXRlIGNvbmZpZz86IHtcbiAgICAgICAgICAgIC8vIENhbGxlZCB3aGVuZXZlciBhIGtleSdzIGluZGV4IGVudHJ5IGNoYW5nZXMgKG91ciBvd24gd3JpdGVzIEFORCBmaWxlcyBwdWxsZWQgaW4gdmlhXG4gICAgICAgICAgICAvLyBzeW5jaHJvbml6YXRpb24pIOKAlCBob3cgdGhlIHN0b3JhZ2Ugc2VydmVyIG5vdGljZXMgcm91dGluZyBjb25maWcgdXBkYXRlcy5cbiAgICAgICAgICAgIG9uSW5kZXhDaGFuZ2VkPzogKGtleTogc3RyaW5nKSA9PiB2b2lkO1xuICAgICAgICAgICAgLy8gTFJVLWJvdW5kIHRoZSBkaXNrIChiYXNlIHNvdXJjZSkgdG8gdGhpcyBtYW55IGJ5dGVzOyBzZWUgQ29tbW9uQ29uZmlnLnJlYWRlckRpc2tMaW1pdFxuICAgICAgICAgICAgcmVhZGVyRGlza0xpbWl0PzogbnVtYmVyO1xuICAgICAgICB9XG4gICAgKSB7IH1cblxuICAgIHByaXZhdGUgc3RvcHBlZCA9IHsgc3RvcDogZmFsc2UgfTtcblxuICAgIC8vIFRoZSBpbmRleCdzIEJ1bGtEYXRhYmFzZTIgZmlsZXMgbGl2ZSB1bmRlciA8Zm9sZGVyPi9pbmRleFxuICAgIHByaXZhdGUgaW5kZXggPSBuZXcgQnVsa0RhdGFiYXNlQmFzZTxCbG9iSW5kZXhFbnRyeT4oXCJibG9iSW5kZXhcIiwgbm9vcFJlYWN0aXZlRGVwcywgYXN5bmMgKHA6IHN0cmluZykgPT4ge1xuICAgICAgICBsZXQgYmFzZTogRGlyZWN0b3J5V3JhcHBlciA9IG5ldyBOb2RlSlNEaXJlY3RvcnlIYW5kbGVXcmFwcGVyKHBhdGguam9pbih0aGlzLmZvbGRlciwgXCJpbmRleFwiKSk7XG4gICAgICAgIGZvciAobGV0IHBhcnQgb2YgcC5zcGxpdChcIi9cIikpIHtcbiAgICAgICAgICAgIGlmIChwYXJ0KSBiYXNlID0gYXdhaXQgYmFzZS5nZXREaXJlY3RvcnlIYW5kbGUocGFydCwgeyBjcmVhdGU6IHRydWUgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHdyYXBIYW5kbGUoYmFzZSk7XG4gICAgfSk7XG4gICAgLy8gVGhlIGluLW1lbW9yeSBjb3B5IG9mIHRoZSBpbmRleC4gQWxsIHJlYWRzIGFyZSBzZXJ2ZWQgZnJvbSBpdDsgY2hhbmdlcyBhcmUgYnVmZmVyZWQgaW5cbiAgICAvLyBkaXJ0eSBhbmQgZmx1c2hlZCB0byB0aGUgQnVsa0RhdGFiYXNlMiBpbiBiYXRjaGVzICh1bmRlZmluZWQgPSBkZWxldGUpLlxuICAgIHByaXZhdGUgbWVtID0gbmV3IE1hcDxzdHJpbmcsIEluZGV4RW50cnk+KCk7XG4gICAgLy8gTGl2ZSB0b3RhbHMgb3ZlciBtZW0gKHRvbWJzdG9uZXMgZXhjbHVkZWQpLCBhZGp1c3RlZCBvbiBldmVyeSBtdXRhdGlvbiBhbmQgcmVjb21wdXRlZCBvblxuICAgIC8vIGxvYWQgLSBzbyBhbnkgZHJpZnQgaGVhbHMgb24gcmVzdGFydC4gY29tcHV0ZUluZGV4VG90YWxzIGdpdmVzIHRoZSB3YWxrLXRoZS1pbmRleCB0cnV0aC5cbiAgICBwcml2YXRlIGluZGV4RmlsZUNvdW50ID0gMDtcbiAgICBwcml2YXRlIGluZGV4Qnl0ZUNvdW50ID0gMDtcbiAgICAvLyBUaGUgc2FtZSB0b3RhbHMgcGVyIGhvbGRpbmcgc291cmNlIChpbmRleCAwID0gb3VyIGRpc2ssIHdoaWNoIHJlYWRlckRpc2tMaW1pdCBib3VuZHMpXG4gICAgcHJpdmF0ZSBzb3VyY2VGaWxlQ291bnRzID0gdGhpcy5zb3VyY2VzLm1hcCgoKSA9PiAwKTtcbiAgICBwcml2YXRlIHNvdXJjZUJ5dGVDb3VudHMgPSB0aGlzLnNvdXJjZXMubWFwKCgpID0+IDApO1xuICAgIC8vIEJhY2tncm91bmQgc2NhbnMgLyBmdWxsIHN5bmNzIGN1cnJlbnRseSBpbiBwcm9ncmVzcyAoYSBTZXQgLSBvbmUgc291cmNlIGNhbiBoYXZlIGEgY2hhbmdlXG4gICAgLy8gcG9sbCdzIGZ1bGwgc3luYyBhbmQgYSByZXNjYW4gb3ZlcmxhcHBpbmcpXG4gICAgcHJpdmF0ZSBzeW5jQWN0aXZpdGllcyA9IG5ldyBTZXQ8U3luY0FjdGl2aXR5PigpO1xuICAgIHByaXZhdGUgZGlydHkgPSBuZXcgTWFwPHN0cmluZywgSW5kZXhFbnRyeSB8IHVuZGVmaW5lZD4oKTtcbiAgICBwcml2YXRlIG92ZXJsYXkgPSBuZXcgTWFwPHN0cmluZywgT3ZlcmxheUVudHJ5PigpO1xuICAgIHByaXZhdGUgc291cmNlU3RhdGVzID0gdGhpcy5zb3VyY2VzLm1hcCgoKTogU291cmNlU3RhdGUgPT4gKHtcbiAgICAgICAgc3VwcG9ydHNDaGFuZ2VzQWZ0ZXI6IGZhbHNlLFxuICAgICAgICBpbml0aWFsU2NhbjogcHJvbWlzZU9iaigpLFxuICAgICAgICBzY2FuQ29tcGxldGU6IGZhbHNlLFxuICAgICAgICBzY2FubmVkQ291bnQ6IDAsXG4gICAgICAgIGNoYW5nZXNBZnRlclRpbWU6IDAsXG4gICAgICAgIGxhc3RNaXNzQ2hlY2s6IDAsXG4gICAgfSkpO1xuXG4gICAgcHVibGljIGluaXQgPSBsYXp5KGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2FkSW5kZXgoKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLnNvdXJjZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIHZvaWQgdGhpcy5ydW5Tb3VyY2VTeW5jKGkpO1xuICAgICAgICB9XG4gICAgICAgIHJ1bkluZmluaXRlUG9sbChGQVNUX0ZMVVNIX1BPTEwsICgpID0+IHRoaXMuZmx1c2hPdmVybGF5KCksIHRoaXMuc3RvcHBlZCk7XG4gICAgICAgIHJ1bkluZmluaXRlUG9sbChJTkRFWF9GTFVTSF9JTlRFUlZBTCwgKCkgPT4gdGhpcy5mbHVzaEluZGV4KCksIHRoaXMuc3RvcHBlZCk7XG4gICAgICAgIHJ1bkluZmluaXRlUG9sbChUT01CU1RPTkVfQ0xFQU5VUF9JTlRFUlZBTCwgKCkgPT4gdGhpcy5jbGVhbnVwVG9tYnN0b25lcygpLCB0aGlzLnN0b3BwZWQpO1xuICAgICAgICBpZiAodGhpcy5jb25maWc/LnJlYWRlckRpc2tMaW1pdCkge1xuICAgICAgICAgICAgcnVuSW5maW5pdGVQb2xsKERJU0tfTElNSVRfQ0hFQ0tfSU5URVJWQUwsICgpID0+IHRoaXMuZW5mb3JjZURpc2tMaW1pdCgpLCB0aGlzLnN0b3BwZWQpO1xuICAgICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBTdG9wcyBhbGwgc3luY2hyb25pemF0aW9uIHNjYW5zL3BvbGxzIGFuZCBmbHVzaGVzIHBlbmRpbmcgd3JpdGVzLiBVc2VkIHdoZW4gYSBidWNrZXQnc1xuICAgIC8vIHJvdXRpbmcgY29uZmlnIGNoYW5nZXMgYW5kIHRoZSBzdG9yZSBpcyByZWJ1aWx0IHdpdGggbmV3IHNvdXJjZXMuXG4gICAgcHVibGljIGFzeW5jIGRpc3Bvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRoaXMuc3RvcHBlZC5zdG9wID0gdHJ1ZTtcbiAgICAgICAgYXdhaXQgdGhpcy5mbHVzaE92ZXJsYXkodHJ1ZSk7XG4gICAgICAgIGF3YWl0IHRoaXMuZmx1c2hJbmRleCgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgbG9hZEluZGV4KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgW3dyaXRlVGltZXMsIHNpemVzLCBzb3VyY2VzXSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgIHRoaXMuaW5kZXguZ2V0Q29sdW1uKFwid3JpdGVUaW1lXCIpLFxuICAgICAgICAgICAgdGhpcy5pbmRleC5nZXRDb2x1bW4oXCJzaXplXCIpLFxuICAgICAgICAgICAgdGhpcy5pbmRleC5nZXRDb2x1bW4oXCJzb3VyY2VcIiksXG4gICAgICAgIF0pO1xuICAgICAgICBsZXQgc2l6ZU1hcCA9IG5ldyBNYXAoc2l6ZXMubWFwKHggPT4gW3gua2V5LCB4LnZhbHVlXSkpO1xuICAgICAgICBsZXQgc291cmNlTWFwID0gbmV3IE1hcChzb3VyY2VzLm1hcCh4ID0+IFt4LmtleSwgeC52YWx1ZV0pKTtcbiAgICAgICAgZm9yIChsZXQgZW50cnkgb2Ygd3JpdGVUaW1lcykge1xuICAgICAgICAgICAgbGV0IHNpemUgPSBzaXplTWFwLmdldChlbnRyeS5rZXkpO1xuICAgICAgICAgICAgbGV0IHNvdXJjZSA9IHNvdXJjZU1hcC5nZXQoZW50cnkua2V5KTtcbiAgICAgICAgICAgIC8vIEV4cGxpY2l0IGNoZWNrcywgYXMgMCBpcyBhIHZhbGlkIHNpemUgYW5kIGEgdmFsaWQgc291cmNlIG51bWJlclxuICAgICAgICAgICAgaWYgKHNpemUgPT09IHVuZGVmaW5lZCB8fCBzb3VyY2UgPT09IHVuZGVmaW5lZCkgY29udGludWU7XG4gICAgICAgICAgICBsZXQgZnVsbDogSW5kZXhFbnRyeSA9IHsgd3JpdGVUaW1lOiBlbnRyeS52YWx1ZSwgc2l6ZSwgc291cmNlLCBjaGFuZ2VkQXQ6IGVudHJ5LnRpbWUsIGxhc3RBY2Nlc3M6IGVudHJ5LnRpbWUgfTtcbiAgICAgICAgICAgIHRoaXMubWVtLnNldChlbnRyeS5rZXksIGZ1bGwpO1xuICAgICAgICAgICAgdGhpcy5jb3VudEVudHJ5KGZ1bGwsIDEpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjb3VudEVudHJ5KGVudHJ5OiBJbmRleEVudHJ5IHwgdW5kZWZpbmVkLCBkaXJlY3Rpb246IG51bWJlcik6IHZvaWQge1xuICAgICAgICBpZiAoIWVudHJ5IHx8IGVudHJ5LnNpemUgPT09IDApIHJldHVybjtcbiAgICAgICAgdGhpcy5pbmRleEZpbGVDb3VudCArPSBkaXJlY3Rpb247XG4gICAgICAgIHRoaXMuaW5kZXhCeXRlQ291bnQgKz0gZW50cnkuc2l6ZSAqIGRpcmVjdGlvbjtcbiAgICAgICAgLy8gQSBzdGFsZSBlbnRyeSBjYW4gcmVmZXJlbmNlIGEgc291cmNlIHRoYXQgbm8gbG9uZ2VyIGV4aXN0cyAoZ2V0SW5kZXhFbnRyeSBjbGVhbnMgdGhvc2UpXG4gICAgICAgIGlmICh0aGlzLnNvdXJjZXNbZW50cnkuc291cmNlXSkge1xuICAgICAgICAgICAgdGhpcy5zb3VyY2VGaWxlQ291bnRzW2VudHJ5LnNvdXJjZV0gKz0gZGlyZWN0aW9uO1xuICAgICAgICAgICAgdGhpcy5zb3VyY2VCeXRlQ291bnRzW2VudHJ5LnNvdXJjZV0gKz0gZW50cnkuc2l6ZSAqIGRpcmVjdGlvbjtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgc2V0SW5kZXhFbnRyeShrZXk6IHN0cmluZywgZW50cnk6IHsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlcjsgc291cmNlOiBudW1iZXIgfSk6IHZvaWQge1xuICAgICAgICBsZXQgZnVsbDogSW5kZXhFbnRyeSA9IHsgLi4uZW50cnksIGNoYW5nZWRBdDogRGF0ZS5ub3coKSwgbGFzdEFjY2VzczogRGF0ZS5ub3coKSB9O1xuICAgICAgICB0aGlzLmNvdW50RW50cnkodGhpcy5tZW0uZ2V0KGtleSksIC0xKTtcbiAgICAgICAgdGhpcy5jb3VudEVudHJ5KGZ1bGwsIDEpO1xuICAgICAgICB0aGlzLm1lbS5zZXQoa2V5LCBmdWxsKTtcbiAgICAgICAgdGhpcy5kaXJ0eS5zZXQoa2V5LCBmdWxsKTtcbiAgICAgICAgdGhpcy5jb25maWc/Lm9uSW5kZXhDaGFuZ2VkPy4oa2V5KTtcbiAgICB9XG4gICAgcHJpdmF0ZSBkZWxldGVJbmRleEVudHJ5KGtleTogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGxldCBleGlzdGluZyA9IHRoaXMubWVtLmdldChrZXkpO1xuICAgICAgICBpZiAoIWV4aXN0aW5nKSByZXR1cm47XG4gICAgICAgIHRoaXMuY291bnRFbnRyeShleGlzdGluZywgLTEpO1xuICAgICAgICB0aGlzLm1lbS5kZWxldGUoa2V5KTtcbiAgICAgICAgdGhpcy5kaXJ0eS5zZXQoa2V5LCB1bmRlZmluZWQpO1xuICAgIH1cblxuICAgIC8qKiBUaGUgY2hlYXAgYWx3YXlzLWN1cnJlbnQgdG90YWxzIHBsdXMgYW55IGluLXByb2dyZXNzIGJhY2tncm91bmQgc3luY2hyb25pemF0aW9uLiAqL1xuICAgIHB1YmxpYyBnZXRTeW5jUHJvZ3Jlc3MoKToge1xuICAgICAgICBpbmRleDogeyBmaWxlQ291bnQ6IG51bWJlcjsgYnl0ZUNvdW50OiBudW1iZXIgfTtcbiAgICAgICAgc291cmNlczogeyBkZWJ1Z05hbWU6IHN0cmluZzsgZmlsZUNvdW50OiBudW1iZXI7IGJ5dGVDb3VudDogbnVtYmVyIH1bXTtcbiAgICAgICAgcmVhZGVyRGlza0xpbWl0PzogbnVtYmVyO1xuICAgICAgICBzeW5jaW5nOiBTeW5jQWN0aXZpdHlbXTtcbiAgICB9IHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGluZGV4OiB7IGZpbGVDb3VudDogdGhpcy5pbmRleEZpbGVDb3VudCwgYnl0ZUNvdW50OiB0aGlzLmluZGV4Qnl0ZUNvdW50IH0sXG4gICAgICAgICAgICBzb3VyY2VzOiB0aGlzLnNvdXJjZXMubWFwKCh4LCBpKSA9PiAoe1xuICAgICAgICAgICAgICAgIGRlYnVnTmFtZTogeC5zb3VyY2UuZ2V0RGVidWdOYW1lKCksXG4gICAgICAgICAgICAgICAgZmlsZUNvdW50OiB0aGlzLnNvdXJjZUZpbGVDb3VudHNbaV0sXG4gICAgICAgICAgICAgICAgYnl0ZUNvdW50OiB0aGlzLnNvdXJjZUJ5dGVDb3VudHNbaV0sXG4gICAgICAgICAgICB9KSksXG4gICAgICAgICAgICByZWFkZXJEaXNrTGltaXQ6IHRoaXMuY29uZmlnPy5yZWFkZXJEaXNrTGltaXQsXG4gICAgICAgICAgICBzeW5jaW5nOiBbLi4udGhpcy5zeW5jQWN0aXZpdGllc10sXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgLyoqIFdhbGtzIHRoZSB3aG9sZSBpbmRleCBmb3IgZXhhY3QgdG90YWxzIC0gbW9yZSBleHBlbnNpdmUgdGhhbiBnZXRTeW5jUHJvZ3Jlc3MsIGJ1dCBpbW11bmUgdG9cbiAgICAgKiAgYW55IGRyaWZ0IGluIHRoZSBtYWludGFpbmVkIGNvdW50ZXJzIChhbmQgbG9hZHMgdGhlIGluZGV4IGZpcnN0LCBzbyBpdCdzIG5ldmVyIGNvbGQgemVyb3MpLiAqL1xuICAgIHB1YmxpYyBhc3luYyBjb21wdXRlSW5kZXhUb3RhbHMoKTogUHJvbWlzZTx7XG4gICAgICAgIGZpbGVDb3VudDogbnVtYmVyO1xuICAgICAgICBieXRlQ291bnQ6IG51bWJlcjtcbiAgICAgICAgc291cmNlczogeyBkZWJ1Z05hbWU6IHN0cmluZzsgZmlsZUNvdW50OiBudW1iZXI7IGJ5dGVDb3VudDogbnVtYmVyIH1bXTtcbiAgICB9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuICAgICAgICBsZXQgZmlsZUNvdW50ID0gMDtcbiAgICAgICAgbGV0IGJ5dGVDb3VudCA9IDA7XG4gICAgICAgIGxldCBzb3VyY2VzID0gdGhpcy5zb3VyY2VzLm1hcCh4ID0+ICh7IGRlYnVnTmFtZTogeC5zb3VyY2UuZ2V0RGVidWdOYW1lKCksIGZpbGVDb3VudDogMCwgYnl0ZUNvdW50OiAwIH0pKTtcbiAgICAgICAgZm9yIChsZXQgZW50cnkgb2YgdGhpcy5tZW0udmFsdWVzKCkpIHtcbiAgICAgICAgICAgIGlmIChlbnRyeS5zaXplID09PSAwKSBjb250aW51ZTtcbiAgICAgICAgICAgIGZpbGVDb3VudCsrO1xuICAgICAgICAgICAgYnl0ZUNvdW50ICs9IGVudHJ5LnNpemU7XG4gICAgICAgICAgICBsZXQgc291cmNlID0gc291cmNlc1tlbnRyeS5zb3VyY2VdO1xuICAgICAgICAgICAgaWYgKHNvdXJjZSkge1xuICAgICAgICAgICAgICAgIHNvdXJjZS5maWxlQ291bnQrKztcbiAgICAgICAgICAgICAgICBzb3VyY2UuYnl0ZUNvdW50ICs9IGVudHJ5LnNpemU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHsgZmlsZUNvdW50LCBieXRlQ291bnQsIHNvdXJjZXMgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGZsdXNoSW5kZXgoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghdGhpcy5kaXJ0eS5zaXplKSByZXR1cm47XG4gICAgICAgIGxldCBkaXJ0eSA9IHRoaXMuZGlydHk7XG4gICAgICAgIHRoaXMuZGlydHkgPSBuZXcgTWFwKCk7XG4gICAgICAgIGxldCB3cml0ZXM6IEJsb2JJbmRleEVudHJ5W10gPSBbXTtcbiAgICAgICAgbGV0IGRlbGV0ZXM6IHN0cmluZ1tdID0gW107XG4gICAgICAgIGZvciAobGV0IFtrZXksIGVudHJ5XSBvZiBkaXJ0eSkge1xuICAgICAgICAgICAgaWYgKGVudHJ5KSB7XG4gICAgICAgICAgICAgICAgd3JpdGVzLnB1c2goeyBrZXksIHdyaXRlVGltZTogZW50cnkud3JpdGVUaW1lLCBzaXplOiBlbnRyeS5zaXplLCBzb3VyY2U6IGVudHJ5LnNvdXJjZSB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZGVsZXRlcy5wdXNoKGtleSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHdyaXRlcy5sZW5ndGgpIGF3YWl0IHRoaXMuaW5kZXgud3JpdGVCYXRjaCh3cml0ZXMpO1xuICAgICAgICBpZiAoZGVsZXRlcy5sZW5ndGgpIGF3YWl0IHRoaXMuaW5kZXguZGVsZXRlQmF0Y2goZGVsZXRlcyk7XG4gICAgfVxuXG4gICAgLy8g4pSA4pSAIHN5bmNocm9uaXphdGlvbiDilIDilIBcblxuICAgIHByaXZhdGUgYXN5bmMgcnVuU291cmNlU3luYyhzb3VyY2VJbmRleDogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGxldCB7IHNvdXJjZSwgbm9GdWxsU3luYyB9ID0gdGhpcy5zb3VyY2VzW3NvdXJjZUluZGV4XTtcbiAgICAgICAgbGV0IHN0YXRlID0gdGhpcy5zb3VyY2VTdGF0ZXNbc291cmNlSW5kZXhdO1xuICAgICAgICBsZXQgbGlzdGluZzogTWFwPHN0cmluZywgbnVtYmVyPiB8IHVuZGVmaW5lZDtcbiAgICAgICAgd2hpbGUgKCF0aGlzLnN0b3BwZWQuc3RvcCkge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBsZXQgY29uZmlnID0gYXdhaXQgc291cmNlLmdldENvbmZpZygpO1xuICAgICAgICAgICAgICAgIHN0YXRlLnN1cHBvcnRzQ2hhbmdlc0FmdGVyID0gISEoY29uZmlnLnN1cHBvcnRzQ2hhbmdlc0FmdGVyICYmIHNvdXJjZS5nZXRDaGFuZ2VzQWZ0ZXIpO1xuICAgICAgICAgICAgICAgIGxpc3RpbmcgPSBhd2FpdCB0aGlzLnNjYW5Tb3VyY2Uoc291cmNlSW5kZXgpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYEluaXRpYWwgc2NhbiBvZiBzeW5jIHNvdXJjZSAke3NvdXJjZS5nZXREZWJ1Z05hbWUoKX0gZmFpbGVkLCByZXRyeWluZzpgLCBlKTtcbiAgICAgICAgICAgICAgICBhd2FpdCBkZWxheShTQ0FOX1JFVFJZX0RFTEFZKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBzdGF0ZS5zY2FuQ29tcGxldGUgPSB0cnVlO1xuICAgICAgICBzdGF0ZS5pbml0aWFsU2Nhbi5yZXNvbHZlKHVuZGVmaW5lZCk7XG4gICAgICAgIGlmICh0aGlzLnN0b3BwZWQuc3RvcCkgcmV0dXJuO1xuICAgICAgICBpZiAobGlzdGluZykge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5yZWNvbmNpbGVTb3VyY2Uoc291cmNlSW5kZXgsIGxpc3RpbmcpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghbm9GdWxsU3luYykge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLmNvcHlTb3VyY2VGaWxlcyhzb3VyY2VJbmRleCk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihgQ29weWluZyBmaWxlcyBmcm9tIHN5bmMgc291cmNlICR7c291cmNlLmdldERlYnVnTmFtZSgpfSBmYWlsZWQ6YCwgZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN0YXRlLnN1cHBvcnRzQ2hhbmdlc0FmdGVyKSB7XG4gICAgICAgICAgICBydW5JbmZpbml0ZVBvbGwoQ0hBTkdFU19QT0xMX0lOVEVSVkFMLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5wb2xsQ2hhbmdlcyhzb3VyY2VJbmRleCk7XG4gICAgICAgICAgICAgICAgaWYgKCFub0Z1bGxTeW5jKSBhd2FpdCB0aGlzLmNvcHlTb3VyY2VGaWxlcyhzb3VyY2VJbmRleCk7XG4gICAgICAgICAgICB9LCB0aGlzLnN0b3BwZWQpO1xuICAgICAgICAgICAgLy8gQ2hhbmdlIHBvbGxzIG9ubHkgc2hvdyB3aGF0IHRoZSBzb3VyY2UgSEFTLCBuZXZlciB3aGF0IGl0J3MgbWlzc2luZywgc28gcHVzaGVzIHJ1biBvblxuICAgICAgICAgICAgLy8gdGhlIGZ1bGwtcmVzY2FuIGNhZGVuY2UgKGZpbmRJbmZvIG9uIGFuIGluZGV4LWJhY2tlZCBzb3VyY2UgaXMgY2hlYXApXG4gICAgICAgICAgICBydW5JbmZpbml0ZVBvbGwoRlVMTF9SRVNDQU5fSU5URVJWQUwsIGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgZmlsZXMgPSBhd2FpdCBzb3VyY2UuZmluZEluZm8oXCJcIik7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5yZWNvbmNpbGVTb3VyY2Uoc291cmNlSW5kZXgsIG5ldyBNYXAoZmlsZXMubWFwKHggPT4gW3gucGF0aCwgeC5jcmVhdGVUaW1lXSkpKTtcbiAgICAgICAgICAgIH0sIHRoaXMuc3RvcHBlZCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBydW5JbmZpbml0ZVBvbGwoRlVMTF9SRVNDQU5fSU5URVJWQUwsIGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgcmVzY2FuID0gYXdhaXQgdGhpcy5zY2FuU291cmNlKHNvdXJjZUluZGV4KTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnJlY29uY2lsZVNvdXJjZShzb3VyY2VJbmRleCwgcmVzY2FuKTtcbiAgICAgICAgICAgICAgICBpZiAoIW5vRnVsbFN5bmMpIGF3YWl0IHRoaXMuY29weVNvdXJjZUZpbGVzKHNvdXJjZUluZGV4KTtcbiAgICAgICAgICAgIH0sIHRoaXMuc3RvcHBlZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBGdWxsIG1ldGFkYXRhIHNjYW4gKHNpemUsIHdyaXRlVGltZSwgcGF0aCkgb2Ygb25lIHNvdXJjZSwgYXBwbGllZCB0byB0aGUgaW5kZXguIFJldHVybnMgdGhlXG4gICAgLy8gc291cmNlJ3MgbGlzdGluZyAocGF0aCAtPiB3cml0ZSB0aW1lKSwgd2hpY2ggcmVjb25jaWxlU291cmNlIHVzZXMgZm9yIHRoZSBwdXNoIGRpcmVjdGlvbi5cbiAgICBwcml2YXRlIGFzeW5jIHNjYW5Tb3VyY2Uoc291cmNlSW5kZXg6IG51bWJlcik6IFByb21pc2U8TWFwPHN0cmluZywgbnVtYmVyPj4ge1xuICAgICAgICBsZXQgeyBzb3VyY2UgfSA9IHRoaXMuc291cmNlc1tzb3VyY2VJbmRleF07XG4gICAgICAgIGxldCBzdGF0ZSA9IHRoaXMuc291cmNlU3RhdGVzW3NvdXJjZUluZGV4XTtcbiAgICAgICAgbGV0IHNjYW5TdGFydCA9IERhdGUubm93KCk7XG4gICAgICAgIGxldCBzY2FuTG9nZ2VkID0gZmFsc2U7XG4gICAgICAgIGxldCBhY3Rpdml0eTogU3luY0FjdGl2aXR5ID0geyB0eXBlOiBcIm1ldGFkYXRhU2NhblwiLCBzb3VyY2VEZWJ1Z05hbWU6IHNvdXJjZS5nZXREZWJ1Z05hbWUoKSwgc3RhcnRUaW1lOiBzY2FuU3RhcnQgfTtcbiAgICAgICAgdGhpcy5zeW5jQWN0aXZpdGllcy5hZGQoYWN0aXZpdHkpO1xuICAgICAgICBsZXQgcHJvZ3Jlc3NUaW1lciA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgICAgIHNjYW5Mb2dnZWQgPSB0cnVlO1xuICAgICAgICAgICAgY29uc29sZS5sb2coYE1ldGFkYXRhIHNjYW4gb2YgJHtzb3VyY2UuZ2V0RGVidWdOYW1lKCl9IHN0aWxsIHJ1bm5pbmcgKCR7TWF0aC5yb3VuZCgoRGF0ZS5ub3coKSAtIHNjYW5TdGFydCkgLyAxMDAwKX1zLCBzdG9yZSAke3RoaXMuZm9sZGVyfSlgKTtcbiAgICAgICAgfSwgU1lOQ19QUk9HUkVTU19MT0dfSU5URVJWQUwpO1xuICAgICAgICAocHJvZ3Jlc3NUaW1lciBhcyB7IHVucmVmPzogKCkgPT4gdm9pZCB9KS51bnJlZj8uKCk7XG4gICAgICAgIGxldCBmaWxlczogQXJjaGl2ZUZpbGVJbmZvW107XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBmaWxlcyA9IGF3YWl0IHNvdXJjZS5maW5kSW5mbyhcIlwiKTtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwocHJvZ3Jlc3NUaW1lcik7XG4gICAgICAgICAgICB0aGlzLnN5bmNBY3Rpdml0aWVzLmRlbGV0ZShhY3Rpdml0eSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHNjYW5Mb2dnZWQpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBNZXRhZGF0YSBzY2FuIG9mICR7c291cmNlLmdldERlYnVnTmFtZSgpfSBmaW5pc2hlZDogJHtmaWxlcy5sZW5ndGh9IGZpbGVzIGluICR7TWF0aC5yb3VuZCgoRGF0ZS5ub3coKSAtIHNjYW5TdGFydCkgLyAxMDAwKX1zIChzdG9yZSAke3RoaXMuZm9sZGVyfSlgKTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgc2VlbiA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gICAgICAgIGZvciAobGV0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAgICAgICAgIHNlZW4uc2V0KGZpbGUucGF0aCwgZmlsZS5jcmVhdGVUaW1lKTtcbiAgICAgICAgICAgIHRoaXMuYXBwbHlTY2FubmVkKHNvdXJjZUluZGV4LCBmaWxlKTtcbiAgICAgICAgfVxuICAgICAgICBzdGF0ZS5zY2FubmVkQ291bnQgPSBmaWxlcy5sZW5ndGg7XG4gICAgICAgIC8vIEluZGV4IGVudHJpZXMgdGhpcyBzb3VyY2Ugd2FzIHRoZSBob2xkZXIgb2YsIGJ1dCB0aGF0IHZhbmlzaGVkIGZyb20gaXQgKGUuZy4gZGVsZXRlZFxuICAgICAgICAvLyB3aGlsZSB3ZSB3ZXJlIG9mZmxpbmUpLCBjb21lIG91dCBvZiB0aGUgaW5kZXguIEVudHJpZXMgY2hhbmdlZCBhZnRlciB0aGUgc2NhbiBzdGFydGVkXG4gICAgICAgIC8vIGFyZSBrZXB0IOKAlCB0aGUgc2NhbiBsaXN0aW5nIG1heSBzaW1wbHkgcHJlZGF0ZSB0aGVtLiBUb21ic3RvbmVzIGhhdmUgbm8gcGh5c2ljYWwgZmlsZVxuICAgICAgICAvLyBmb3IgYSBsaXN0aW5nIHRvIHZvdWNoIGZvciwgc28gdGhleSdyZSBleGVtcHQgKGNsZWFudXBUb21ic3RvbmVzIGV4cGlyZXMgdGhlbSBpbnN0ZWFkKS5cbiAgICAgICAgZm9yIChsZXQgW2tleSwgZW50cnldIG9mIHRoaXMubWVtKSB7XG4gICAgICAgICAgICBpZiAoZW50cnkuc291cmNlICE9PSBzb3VyY2VJbmRleCkgY29udGludWU7XG4gICAgICAgICAgICBpZiAoZW50cnkuc2l6ZSA9PT0gMCkgY29udGludWU7XG4gICAgICAgICAgICBpZiAoc2Vlbi5oYXMoa2V5KSkgY29udGludWU7XG4gICAgICAgICAgICBpZiAoZW50cnkuY2hhbmdlZEF0ID49IHNjYW5TdGFydCkgY29udGludWU7XG4gICAgICAgICAgICB0aGlzLmRlbGV0ZUluZGV4RW50cnkoa2V5KTtcbiAgICAgICAgfVxuICAgICAgICBzdGF0ZS5jaGFuZ2VzQWZ0ZXJUaW1lID0gTWF0aC5tYXgoc3RhdGUuY2hhbmdlc0FmdGVyVGltZSwgc2NhblN0YXJ0IC0gQ0hBTkdFU19QT0xMX09WRVJMQVApO1xuICAgICAgICByZXR1cm4gc2VlbjtcbiAgICB9XG5cbiAgICAvLyBUaGUgcHVzaCBkaXJlY3Rpb24gb2Ygc3luY2hyb25pemF0aW9uOiBldmVyeXRoaW5nIHdlIGtub3cgdGhhdCB0aGUgc291cmNlIGlzIG1pc3NpbmcgKG9yXG4gICAgLy8gaG9sZHMgYW4gb2xkZXIgY29weSBvZikgaXMgd3JpdHRlbiB0byBpdCDigJQgaW5jbHVkaW5nIGRlbGV0aW9ucywgYXMgdG9tYnN0b25lIHdyaXRlcy4gVGhpcyBpc1xuICAgIC8vIHdoYXQgaGVhbHMgYSBzb3VyY2Ugd2hvc2UgYmFja2dyb3VuZCB3cml0ZXMgZmFpbGVkIChlLmcuIGl0IHdhcyBkb3duKTogdGhlIG5leHQgc2NhbiBzZWVzXG4gICAgLy8gd2hhdCdzIG1pc3NpbmcgYW5kIHJlLXNlbmRzIGl0LlxuICAgIHByaXZhdGUgYXN5bmMgcmVjb25jaWxlU291cmNlKHNvdXJjZUluZGV4OiBudW1iZXIsIGxpc3Rpbmc6IE1hcDxzdHJpbmcsIG51bWJlcj4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgbGV0IHsgc291cmNlLCB2YWxpZFdpbmRvdywgcm91dGUgfSA9IHRoaXMuc291cmNlc1tzb3VyY2VJbmRleF07XG4gICAgICAgIGxldCBhY2NlcHRzV3JpdGVzID0gd2luZG93QWNjZXB0c1dyaXRlcyh2YWxpZFdpbmRvdyk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBmb3IgKGxldCBba2V5LCBlbnRyeV0gb2YgdGhpcy5tZW0pIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zdG9wcGVkLnN0b3ApIHJldHVybjtcbiAgICAgICAgICAgICAgICBpZiAoZW50cnkuc291cmNlID09PSBzb3VyY2VJbmRleCkgY29udGludWU7XG4gICAgICAgICAgICAgICAgLy8gUGFzdC13aW5kb3cgc291cmNlcyBvbmx5IHJlY2VpdmUgdGhlIHJvdXRpbmcgZmlsZSAoc2VlIHdyaXRlVG9Tb3VyY2VzKSwgYW5kXG4gICAgICAgICAgICAgICAgLy8gc2hhcmRlZCBzb3VyY2VzIG9ubHkgdGhlIGtleXMgcm91dGluZyBpbnRvIHRoZW1cbiAgICAgICAgICAgICAgICBpZiAoa2V5ICE9PSBST1VUSU5HX0ZJTEUpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFhY2NlcHRzV3JpdGVzKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFyb3V0ZUNvbnRhaW5zKHJvdXRlLCBnZXRSb3V0ZShrZXkpKSkgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxldCB0aGVpclRpbWUgPSBsaXN0aW5nLmdldChrZXkpO1xuICAgICAgICAgICAgICAgIGlmICh0aGVpclRpbWUgIT09IHVuZGVmaW5lZCAmJiB0aGVpclRpbWUgPj0gZW50cnkud3JpdGVUaW1lKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICBpZiAoZW50cnkuc2l6ZSA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBBIGRlbGV0aW9uIG9ubHkgbmVlZHMgcHVzaGluZyB3aGlsZSB0aGUgc291cmNlIHN0aWxsIGhvbGRzIGFuIG9sZGVyIGNvcHlcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoZWlyVGltZSA9PT0gdW5kZWZpbmVkKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgc291cmNlLnNldChrZXksIEJ1ZmZlci5hbGxvYygwKSwgeyBsYXN0TW9kaWZpZWQ6IGVudHJ5LndyaXRlVGltZSB9KTtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxldCByZXN1bHQgPSBhd2FpdCB0aGlzLnNvdXJjZXNbZW50cnkuc291cmNlXS5zb3VyY2UuZ2V0MihrZXkpO1xuICAgICAgICAgICAgICAgIGlmICghcmVzdWx0KSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICBhd2FpdCBzb3VyY2Uuc2V0KGtleSwgcmVzdWx0LmRhdGEsIHsgbGFzdE1vZGlmaWVkOiByZXN1bHQud3JpdGVUaW1lIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyBBYm9ydCB0aGUgcGFzcyBpbnN0ZWFkIG9mIGxvZ2dpbmcgcGVyIGZpbGU7IHRoZSBuZXh0IHNjYW4gY3ljbGUgcmV0cmllc1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihgUmVjb25jaWxpbmcgc3luYyBzb3VyY2UgJHtzb3VyY2UuZ2V0RGVidWdOYW1lKCl9IGZhaWxlZDogJHsoZSBhcyBFcnJvcikuc3RhY2sgPz8gZX1gKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYXBwbHlTY2FubmVkKHNvdXJjZUluZGV4OiBudW1iZXIsIGZpbGU6IEFyY2hpdmVGaWxlSW5mbyk6IHZvaWQge1xuICAgICAgICBsZXQgeyB2YWxpZFdpbmRvdywgcm91dGUgfSA9IHRoaXMuc291cmNlc1tzb3VyY2VJbmRleF07XG4gICAgICAgIGxldCBbd2luZG93U3RhcnQsIHdpbmRvd0VuZF0gPSB2YWxpZFdpbmRvdztcbiAgICAgICAgaWYgKGZpbGUuY3JlYXRlVGltZSA8IHdpbmRvd1N0YXJ0IHx8IGZpbGUuY3JlYXRlVGltZSA+IHdpbmRvd0VuZCkgcmV0dXJuO1xuICAgICAgICAvLyBBIHBhcnRpYWxseS1vdmVybGFwcGluZyBzaGFyZCdzIGxpc3RpbmcgaW5jbHVkZXMga2V5cyBvdXRzaWRlIG91ciByb3V0ZTsgaWdub3JlIHRoZW1cbiAgICAgICAgaWYgKGZpbGUucGF0aCAhPT0gUk9VVElOR19GSUxFICYmICFyb3V0ZUNvbnRhaW5zKHJvdXRlLCBnZXRSb3V0ZShmaWxlLnBhdGgpKSkgcmV0dXJuO1xuICAgICAgICBsZXQgZXhpc3RpbmcgPSB0aGlzLm1lbS5nZXQoZmlsZS5wYXRoKTtcbiAgICAgICAgLy8gVGhlIGhpZ2hlc3Qgd3JpdGUgdGltZSB3aW5zIGFjcm9zcyBhbGwgc291cmNlcyAodGllcyBrZWVwIHRoZSBleGlzdGluZyBlbnRyeSlcbiAgICAgICAgaWYgKGV4aXN0aW5nICYmIGZpbGUuY3JlYXRlVGltZSA8PSBleGlzdGluZy53cml0ZVRpbWUpIHJldHVybjtcbiAgICAgICAgdGhpcy5zZXRJbmRleEVudHJ5KGZpbGUucGF0aCwgeyB3cml0ZVRpbWU6IGZpbGUuY3JlYXRlVGltZSwgc2l6ZTogZmlsZS5zaXplLCBzb3VyY2U6IHNvdXJjZUluZGV4IH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgcG9sbENoYW5nZXMoc291cmNlSW5kZXg6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgeyBzb3VyY2UgfSA9IHRoaXMuc291cmNlc1tzb3VyY2VJbmRleF07XG4gICAgICAgIGlmICghc291cmNlLmdldENoYW5nZXNBZnRlcikgcmV0dXJuO1xuICAgICAgICBsZXQgc3RhdGUgPSB0aGlzLnNvdXJjZVN0YXRlc1tzb3VyY2VJbmRleF07XG4gICAgICAgIGxldCBwb2xsU3RhcnQgPSBEYXRlLm5vdygpO1xuICAgICAgICBsZXQgY2hhbmdlcyA9IGF3YWl0IHNvdXJjZS5nZXRDaGFuZ2VzQWZ0ZXIoc3RhdGUuY2hhbmdlc0FmdGVyVGltZSk7XG4gICAgICAgIGZvciAobGV0IGZpbGUgb2YgY2hhbmdlcykge1xuICAgICAgICAgICAgdGhpcy5hcHBseVNjYW5uZWQoc291cmNlSW5kZXgsIGZpbGUpO1xuICAgICAgICB9XG4gICAgICAgIHN0YXRlLnNjYW5uZWRDb3VudCArPSBjaGFuZ2VzLmxlbmd0aDtcbiAgICAgICAgc3RhdGUuY2hhbmdlc0FmdGVyVGltZSA9IHBvbGxTdGFydCAtIENIQU5HRVNfUE9MTF9PVkVSTEFQO1xuICAgIH1cblxuICAgIC8vIERvd25sb2FkcyB0aGUgZmlsZXMgYSBzb3VyY2UgY3VycmVudGx5IGhvbGRzIG9udG8gb3VyIG93biBiYXNlIHNvdXJjZSAodGhlIGxvY2FsIGRpc2spLFxuICAgIC8vIHByZXNlcnZpbmcgdGhlaXIgbW9kaWZpZWQgdGltZXMg4oCUIHNvIGEgbmV3ZXIgbG9jYWwgd3JpdGUgYWx3YXlzIHdpbnMuIFNraXBwZWQgZm9yIG5vRnVsbFN5bmNcbiAgICAvLyBzb3VyY2VzIChmcm9udGluZyBhIGxhcmdlIGRhdGFiYXNlIHdpdGhvdXQgY29weWluZyBpdCk7IHJlYWRzIHN0aWxsIGRvd24tY2FjaGUgbGF6aWx5LlxuICAgIHByaXZhdGUgYXN5bmMgY29weVNvdXJjZUZpbGVzKHNvdXJjZUluZGV4OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHNvdXJjZUluZGV4ID09PSAwKSByZXR1cm47XG4gICAgICAgIGxldCB7IHNvdXJjZSB9ID0gdGhpcy5zb3VyY2VzW3NvdXJjZUluZGV4XTtcbiAgICAgICAgbGV0IHBlbmRpbmc6IHsga2V5OiBzdHJpbmc7IGVudHJ5OiBJbmRleEVudHJ5IH1bXSA9IFtdO1xuICAgICAgICBsZXQgdG90YWxCeXRlcyA9IDA7XG4gICAgICAgIGZvciAobGV0IFtrZXksIGVudHJ5XSBvZiB0aGlzLm1lbSkge1xuICAgICAgICAgICAgaWYgKGVudHJ5LnNvdXJjZSAhPT0gc291cmNlSW5kZXgpIGNvbnRpbnVlO1xuICAgICAgICAgICAgaWYgKGVudHJ5LnNpemUgPT09IDApIGNvbnRpbnVlO1xuICAgICAgICAgICAgcGVuZGluZy5wdXNoKHsga2V5LCBlbnRyeSB9KTtcbiAgICAgICAgICAgIHRvdGFsQnl0ZXMgKz0gZW50cnkuc2l6ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXBlbmRpbmcubGVuZ3RoKSByZXR1cm47XG4gICAgICAgIGxldCBhY3Rpdml0eTogU3luY0FjdGl2aXR5ID0ge1xuICAgICAgICAgICAgdHlwZTogXCJmdWxsU3luY1wiLFxuICAgICAgICAgICAgc291cmNlRGVidWdOYW1lOiBzb3VyY2UuZ2V0RGVidWdOYW1lKCksXG4gICAgICAgICAgICBzdGFydFRpbWU6IERhdGUubm93KCksXG4gICAgICAgICAgICBkb25lRmlsZXM6IDAsXG4gICAgICAgICAgICB0b3RhbEZpbGVzOiBwZW5kaW5nLmxlbmd0aCxcbiAgICAgICAgICAgIGRvbmVCeXRlczogMCxcbiAgICAgICAgICAgIHRvdGFsQnl0ZXMsXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc3luY0FjdGl2aXRpZXMuYWRkKGFjdGl2aXR5KTtcbiAgICAgICAgbGV0IHByb2dyZXNzTG9nZ2VkID0gZmFsc2U7XG4gICAgICAgIGxldCBsb2dQcm9ncmVzcyA9ICgpID0+IHtcbiAgICAgICAgICAgIHByb2dyZXNzTG9nZ2VkID0gdHJ1ZTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBGdWxsIHN5bmMgZnJvbSAke3NvdXJjZS5nZXREZWJ1Z05hbWUoKX0gKHN0b3JlICR7dGhpcy5mb2xkZXJ9KTogJHthY3Rpdml0eS5kb25lRmlsZXN9LyR7cGVuZGluZy5sZW5ndGh9IGZpbGVzICgkeygoYWN0aXZpdHkuZG9uZUZpbGVzIHx8IDApIC8gcGVuZGluZy5sZW5ndGggKiAxMDApLnRvRml4ZWQoMSl9JSksICR7Zm9ybWF0TnVtYmVyKGFjdGl2aXR5LmRvbmVCeXRlcyB8fCAwKX1CLyR7Zm9ybWF0TnVtYmVyKHRvdGFsQnl0ZXMpfUIgKCR7KHRvdGFsQnl0ZXMgJiYgKGFjdGl2aXR5LmRvbmVCeXRlcyB8fCAwKSAvIHRvdGFsQnl0ZXMgKiAxMDAgfHwgMTAwKS50b0ZpeGVkKDEpfSUpYCk7XG4gICAgICAgIH07XG4gICAgICAgIGxldCBwcm9ncmVzc1RpbWVyID0gc2V0SW50ZXJ2YWwobG9nUHJvZ3Jlc3MsIFNZTkNfUFJPR1JFU1NfTE9HX0lOVEVSVkFMKTtcbiAgICAgICAgKHByb2dyZXNzVGltZXIgYXMgeyB1bnJlZj86ICgpID0+IHZvaWQgfSkudW5yZWY/LigpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbGV0IG5leHRJbmRleCA9IDA7XG4gICAgICAgICAgICBsZXQgZmFpbGVkID0gZmFsc2U7XG4gICAgICAgICAgICBsZXQgY29weVdvcmtlciA9IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICB3aGlsZSAoIWZhaWxlZCAmJiAhdGhpcy5zdG9wcGVkLnN0b3ApIHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IGluZGV4ID0gbmV4dEluZGV4Kys7XG4gICAgICAgICAgICAgICAgICAgIGlmIChpbmRleCA+PSBwZW5kaW5nLmxlbmd0aCkgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICBsZXQgeyBrZXksIGVudHJ5IH0gPSBwZW5kaW5nW2luZGV4XTtcbiAgICAgICAgICAgICAgICAgICAgbGV0IHJlc3VsdCA9IGF3YWl0IHNvdXJjZS5nZXQyKGtleSk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuc291cmNlc1swXS5zb3VyY2Uuc2V0KGtleSwgcmVzdWx0LmRhdGEsIHsgbGFzdE1vZGlmaWVkOiByZXN1bHQud3JpdGVUaW1lIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gT25seSBtb3ZlIHRoZSBlbnRyeSdzIHNvdXJjZSBpZiBpdCB3YXNuJ3QgY2hhbmdlZCB3aGlsZSB3ZSBjb3BpZWRcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLm1lbS5nZXQoa2V5KSA9PT0gZW50cnkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNldEluZGV4RW50cnkoa2V5LCB7IHdyaXRlVGltZTogcmVzdWx0LndyaXRlVGltZSwgc2l6ZTogcmVzdWx0LmRhdGEubGVuZ3RoLCBzb3VyY2U6IDAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYWN0aXZpdHkuZG9uZUZpbGVzID0gKGFjdGl2aXR5LmRvbmVGaWxlcyB8fCAwKSArIDE7XG4gICAgICAgICAgICAgICAgICAgIGFjdGl2aXR5LmRvbmVCeXRlcyA9IChhY3Rpdml0eS5kb25lQnl0ZXMgfHwgMCkgKyBlbnRyeS5zaXplO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBsZXQgd29ya2VyczogUHJvbWlzZTx2b2lkPltdID0gW107XG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IE1hdGgubWluKEZVTExfU1lOQ19QQVJBTExFTCwgcGVuZGluZy5sZW5ndGgpOyBpKyspIHtcbiAgICAgICAgICAgICAgICB3b3JrZXJzLnB1c2goY29weVdvcmtlcigpLmNhdGNoKChlOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgICAgICAvLyBTdG9wIHRoZSBvdGhlciB3b3JrZXJzIHB1bGxpbmcgbmV3IGZpbGVzLCB0aGVuIHN1cmZhY2UgdGhlIGVycm9yXG4gICAgICAgICAgICAgICAgICAgIGZhaWxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwod29ya2Vycyk7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgICBjbGVhckludGVydmFsKHByb2dyZXNzVGltZXIpO1xuICAgICAgICAgICAgdGhpcy5zeW5jQWN0aXZpdGllcy5kZWxldGUoYWN0aXZpdHkpO1xuICAgICAgICAgICAgLy8gQSBzeW5jIHNsb3cgZW5vdWdoIHRvIGhhdmUgbG9nZ2VkIHByb2dyZXNzIGFsc28gbG9ncyBpdHMgY29tcGxldGlvblxuICAgICAgICAgICAgaWYgKHByb2dyZXNzTG9nZ2VkKSB7XG4gICAgICAgICAgICAgICAgbG9nUHJvZ3Jlc3MoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIGZpbmRJbmZvIGFuZCBnZXRDaGFuZ2VzQWZ0ZXIgbGlzdCBmcm9tIHRoZSBpbmRleCwgc28gdGhleSBtdXN0IHdhaXQgZm9yIG91ciBvd24gYmFzZVxuICAgIC8vIHNvdXJjZSdzIGluaXRpYWwgc2NhbiAod2hpY2ggbWlnaHQgbGFnIG1pbnV0ZXMpIGJlZm9yZSB0aGUgbGlzdGluZyBpcyB0cnVzdHdvcnRoeS4gVGhlIGJhc2VcbiAgICAvLyAobG9jYWwgZGlzaykgaXMgaW1wbGljaXRseSByZXF1aXJlZCAtIHJlbW90ZSBzb3VyY2VzIGFyZSBub3QsIHRoZXkgY29tZSBhbmQgZ28uXG4gICAgcHJpdmF0ZSBhc3luYyB3YWl0Rm9yUmVxdWlyZWRTY2FucygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5zb3VyY2VTdGF0ZXNbMF0uaW5pdGlhbFNjYW4ucHJvbWlzZTtcbiAgICB9XG5cbiAgICAvLyBBIHJlcXVlc3RlZCBmaWxlIGlzbid0IGluIHRoZSBpbmRleDogb3VyIG93biBiYXNlIHNvdXJjZSAoaW1wbGljaXRseSByZXF1aXJlZCkgaXMgY2hlY2tlZFxuICAgIC8vIGRpcmVjdGx5IGlmIGl0cyBpbml0aWFsIHNjYW4gaGFzbid0IGZpbmlzaGVkLCBhbmQgY2hhbmdlcy1hZnRlciBzb3VyY2VzIGFyZSByZS1wb2xsZWQgKGF0XG4gICAgLy8gbW9zdCBldmVyeSA1IHNlY29uZHMpXG4gICAgcHJpdmF0ZSBhc3luYyBjaGVja01pc3NpbmdLZXkoa2V5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLnNvdXJjZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGxldCB7IHNvdXJjZSB9ID0gdGhpcy5zb3VyY2VzW2ldO1xuICAgICAgICAgICAgbGV0IHN0YXRlID0gdGhpcy5zb3VyY2VTdGF0ZXNbaV07XG4gICAgICAgICAgICBpZiAoaSA9PT0gMCAmJiAhc3RhdGUuc2NhbkNvbXBsZXRlKSB7XG4gICAgICAgICAgICAgICAgbGV0IGluZm8gPSBhd2FpdCBzb3VyY2UuZ2V0SW5mbyhrZXkpO1xuICAgICAgICAgICAgICAgIGlmIChpbmZvKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlTY2FubmVkKGksIHsgcGF0aDoga2V5LCBjcmVhdGVUaW1lOiBpbmZvLndyaXRlVGltZSwgc2l6ZTogaW5mby5zaXplIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChzdGF0ZS5zdXBwb3J0c0NoYW5nZXNBZnRlciAmJiBEYXRlLm5vdygpIC0gc3RhdGUubGFzdE1pc3NDaGVjayA+IE1JU1NfQ0hFQ0tfSU5URVJWQUwpIHtcbiAgICAgICAgICAgICAgICBzdGF0ZS5sYXN0TWlzc0NoZWNrID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnBvbGxDaGFuZ2VzKGkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRJbmRleEVudHJ5KGtleTogc3RyaW5nKTogUHJvbWlzZTxJbmRleEVudHJ5IHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGxldCBlbnRyeSA9IHRoaXMubWVtLmdldChrZXkpO1xuICAgICAgICBpZiAoZW50cnkgJiYgdGhpcy5zb3VyY2VzW2VudHJ5LnNvdXJjZV0pIHJldHVybiBlbnRyeTtcbiAgICAgICAgaWYgKGVudHJ5KSB7XG4gICAgICAgICAgICAvLyBUaGUgc291cmNlIGxpc3QgY2hhbmdlZCBhbmQgdGhpcyBlbnRyeSdzIHNvdXJjZSBubyBsb25nZXIgZXhpc3RzOyB0cmVhdCBhcyBtaXNzaW5nXG4gICAgICAgICAgICB0aGlzLmRlbGV0ZUluZGV4RW50cnkoa2V5KTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB0aGlzLmNoZWNrTWlzc2luZ0tleShrZXkpO1xuICAgICAgICByZXR1cm4gdGhpcy5tZW0uZ2V0KGtleSk7XG4gICAgfVxuXG4gICAgLy8g4pSA4pSAIGRhdGEgb3BlcmF0aW9ucyDilIDilIBcblxuICAgIHB1YmxpYyBhc3luYyBnZXQoa2V5OiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8QnVmZmVyIHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGxldCByZXN1bHQgPSBhd2FpdCB0aGlzLmdldDIoa2V5LCBjb25maWcpO1xuICAgICAgICByZXR1cm4gcmVzdWx0ICYmIHJlc3VsdC5kYXRhIHx8IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgZ2V0MihrZXk6IHN0cmluZywgY29uZmlnPzogeyByYW5nZT86IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB9KTogUHJvbWlzZTx7IGRhdGE6IEJ1ZmZlcjsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlciB9IHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuICAgICAgICBsZXQgcmFuZ2UgPSBjb25maWc/LnJhbmdlO1xuICAgICAgICBsZXQgb3ZlcmxheUVudHJ5ID0gdGhpcy5vdmVybGF5LmdldChrZXkpO1xuICAgICAgICBpZiAob3ZlcmxheUVudHJ5KSB7XG4gICAgICAgICAgICAvLyBBbiBlbXB0eSBmaWxlIElTIGEgbWlzc2luZyBmaWxlICh0b21ic3RvbmUpXG4gICAgICAgICAgICBpZiAob3ZlcmxheUVudHJ5LmRhdGEubGVuZ3RoID09PSAwKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICAgICAgbGV0IGRhdGEgPSBvdmVybGF5RW50cnkuZGF0YTtcbiAgICAgICAgICAgIGxldCBzaXplID0gZGF0YS5sZW5ndGg7XG4gICAgICAgICAgICBpZiAocmFuZ2UpIHtcbiAgICAgICAgICAgICAgICBkYXRhID0gZGF0YS5zdWJhcnJheShNYXRoLm1pbihyYW5nZS5zdGFydCwgZGF0YS5sZW5ndGgpLCBNYXRoLm1pbihyYW5nZS5lbmQsIGRhdGEubGVuZ3RoKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4geyBkYXRhLCB3cml0ZVRpbWU6IG92ZXJsYXlFbnRyeS50LCBzaXplIH07XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGVudHJ5ID0gYXdhaXQgdGhpcy5nZXRJbmRleEVudHJ5KGtleSk7XG4gICAgICAgIGlmICghZW50cnkpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChlbnRyeS5zaXplID09PSAwKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICBlbnRyeS5sYXN0QWNjZXNzID0gRGF0ZS5ub3coKTtcbiAgICAgICAgbGV0IGhvbGRlciA9IGVudHJ5LnNvdXJjZTtcbiAgICAgICAgbGV0IHJlc3VsdDogeyBkYXRhOiBCdWZmZXI7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IGhvbGRlckVycm9yOiBFcnJvciB8IHVuZGVmaW5lZDtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJlc3VsdCA9IGF3YWl0IHRoaXMuc291cmNlc1tob2xkZXJdLnNvdXJjZS5nZXQyKGtleSwgeyByYW5nZSB9KTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgaG9sZGVyRXJyb3IgPSBlIGFzIEVycm9yO1xuICAgICAgICB9XG4gICAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgICAgIC8vIFJhbmdlZCByZWFkcyBjYW4ndCBwb3B1bGF0ZSBhIGNhY2hlICh0aGV5J3JlIHBhcnRpYWwpXG4gICAgICAgICAgICBpZiAoaG9sZGVyICE9PSAwICYmICFyYW5nZSkge1xuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuY2FjaGVSZWFkKGtleSwgcmVzdWx0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGhlIGhvbGRlciBpcyBkb3duIG9yIGxvc3QgdGhlIGZpbGUuIEFOWSBvdGhlciBzb3VyY2UncyBjb3B5IGJlYXRzIG5vIHZhbHVlIC0gZXZlbiBhblxuICAgICAgICAvLyBPTERFUiBvbmUgLSBhbmQgaXQncyBjb3BpZWQgb250byBvdXIgZGlzayBzbyB0aGUgbmV4dCByZWFkIGRvZXNuJ3QgZGVwZW5kIG9uIGx1Y2suXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5zb3VyY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoaSA9PT0gaG9sZGVyKSBjb250aW51ZTtcbiAgICAgICAgICAgIGxldCBmYWxsYmFjazogdHlwZW9mIHJlc3VsdDtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgZmFsbGJhY2sgPSBhd2FpdCB0aGlzLnNvdXJjZXNbaV0uc291cmNlLmdldDIoa2V5KTtcbiAgICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFmYWxsYmFjaykgY29udGludWU7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmNhY2hlUmVhZChrZXksIGZhbGxiYWNrKTtcbiAgICAgICAgICAgIGxldCBkYXRhID0gZmFsbGJhY2suZGF0YTtcbiAgICAgICAgICAgIGlmIChyYW5nZSkge1xuICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLnN1YmFycmF5KE1hdGgubWluKHJhbmdlLnN0YXJ0LCBkYXRhLmxlbmd0aCksIE1hdGgubWluKHJhbmdlLmVuZCwgZGF0YS5sZW5ndGgpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB7IGRhdGEsIHdyaXRlVGltZTogZmFsbGJhY2sud3JpdGVUaW1lLCBzaXplOiBmYWxsYmFjay5zaXplIH07XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGhvbGRlckVycm9yKSB0aHJvdyBob2xkZXJFcnJvcjtcbiAgICAgICAgLy8gVGhlIGhvbGRlciBhbnN3ZXJlZCBcIm5vdCB0aGVyZVwiIGFuZCBubyBvdGhlciBzb3VyY2UgaGFzIGl0IGVpdGhlcjogdGhlIGVudHJ5IHdhcyBzdGFsZVxuICAgICAgICB0aGlzLmRlbGV0ZUluZGV4RW50cnkoa2V5KTtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICAvLyBUaGUgcmVhZCdzIGJ5dGVzIGNhbWUgZnJvbSBhIHJlbW90ZSBzb3VyY2UsIHNvIHdyaXRlIHRoZW0gb250byBvdXIgb3duIGJhc2Ugc291cmNlICh0aGVcbiAgICAvLyBsb2NhbCBkaXNrKSwgd2hpY2ggYmVjb21lcyB0aGUgZW50cnkncyBuZXcgaG9sZGVyIC0gcmVhZHMgb25seSBwYXkgdGhlIHJlbW90ZSBmZXRjaCBvbmNlXG4gICAgcHJpdmF0ZSBhc3luYyBjYWNoZVJlYWQoa2V5OiBzdHJpbmcsIHJlc3VsdDogeyBkYXRhOiBCdWZmZXI7IHdyaXRlVGltZTogbnVtYmVyIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5zb3VyY2VzWzBdLnNvdXJjZS5zZXQoa2V5LCByZXN1bHQuZGF0YSwgeyBsYXN0TW9kaWZpZWQ6IHJlc3VsdC53cml0ZVRpbWUgfSk7XG4gICAgICAgIHRoaXMuc2V0SW5kZXhFbnRyeShrZXksIHsgd3JpdGVUaW1lOiByZXN1bHQud3JpdGVUaW1lLCBzaXplOiByZXN1bHQuZGF0YS5sZW5ndGgsIHNvdXJjZTogMCB9KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2V0KGtleTogc3RyaW5nLCBkYXRhOiBCdWZmZXIsIGNvbmZpZz86IFdyaXRlQ29uZmlnKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuICAgICAgICBsZXQgbGFzdE1vZGlmaWVkID0gY29uZmlnPy5sYXN0TW9kaWZpZWQ7XG4gICAgICAgIGlmIChsYXN0TW9kaWZpZWQpIHtcbiAgICAgICAgICAgIGFzc2VydFZhbGlkTGFzdE1vZGlmaWVkKGxhc3RNb2RpZmllZCk7XG4gICAgICAgICAgICBsZXQgb3ZlcmxheUVudHJ5ID0gdGhpcy5vdmVybGF5LmdldChrZXkpO1xuICAgICAgICAgICAgbGV0IGVudHJ5ID0gdGhpcy5tZW0uZ2V0KGtleSk7XG4gICAgICAgICAgICBsZXQgY3VycmVudFRpbWUgPSBvdmVybGF5RW50cnkgJiYgb3ZlcmxheUVudHJ5LnQgfHwgZW50cnkgJiYgZW50cnkud3JpdGVUaW1lIHx8IDA7XG4gICAgICAgICAgICAvLyBBbiBvbGRlciB3cml0ZSBuZXZlciBvdmVyd3JpdGVzIGEgbmV3ZXIgb25lIChzZWUgSUFyY2hpdmVzLnNldClcbiAgICAgICAgICAgIGlmIChsYXN0TW9kaWZpZWQgPCBjdXJyZW50VGltZSkgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGxldCB3cml0ZVRpbWUgPSBsYXN0TW9kaWZpZWQgfHwgRGF0ZS5ub3coKTtcbiAgICAgICAgaWYgKGNvbmZpZz8uZmFzdCkge1xuICAgICAgICAgICAgbGV0IHdyaXRlRGVsYXkgPSBjb25maWcud3JpdGVEZWxheSB8fCBERUZBVUxUX0ZBU1RfV1JJVEVfREVMQVk7XG4gICAgICAgICAgICB0aGlzLm92ZXJsYXkuc2V0KGtleSwgeyBkYXRhLCB0OiB3cml0ZVRpbWUsIGZsdXNoQXQ6IERhdGUubm93KCkgKyB3cml0ZURlbGF5IH0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMub3ZlcmxheS5kZWxldGUoa2V5KTtcbiAgICAgICAgYXdhaXQgdGhpcy53cml0ZVRvU291cmNlcyhrZXksIGRhdGEsIHdyaXRlVGltZSk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGRlbChrZXk6IHN0cmluZywgY29uZmlnPzogV3JpdGVDb25maWcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gRGVsZXRlcyBhcmUgdG9tYnN0b25lIHdyaXRlcyAoYW4gZW1wdHkgZmlsZSBJUyBhIG1pc3NpbmcgZmlsZSk6IHRoZSBzaXplLTAgaW5kZXggZW50cnkgaXNcbiAgICAgICAgLy8gb3JkZXJlZCBieSB3cml0ZSB0aW1lIGxpa2UgYW55IG90aGVyIHdyaXRlLCBwcm9wYWdhdGVzIHRocm91Z2ggc3luY2hyb25pemF0aW9uLCBhbmRcbiAgICAgICAgLy8gZXhwaXJlcyBhZnRlciBUT01CU1RPTkVfRVhQSVJZXG4gICAgICAgIGF3YWl0IHRoaXMuc2V0KGtleSwgQnVmZmVyLmFsbG9jKDApLCBjb25maWcpO1xuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0V3JpdGFibGVTb3VyY2VzKGNvbmZpZz86IHsgaWdub3JlV2luZG93PzogYm9vbGVhbiB9KTogbnVtYmVyW10ge1xuICAgICAgICBsZXQgd3JpdGFibGU6IG51bWJlcltdID0gW107XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5zb3VyY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBpZiAoIWNvbmZpZz8uaWdub3JlV2luZG93ICYmICF3aW5kb3dBY2NlcHRzV3JpdGVzKHRoaXMuc291cmNlc1tpXS52YWxpZFdpbmRvdykpIGNvbnRpbnVlO1xuICAgICAgICAgICAgd3JpdGFibGUucHVzaChpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gd3JpdGFibGU7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyB3cml0ZVRvU291cmNlcyhrZXk6IHN0cmluZywgZGF0YTogQnVmZmVyLCB3cml0ZVRpbWU6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAvLyBUaGUgcm91dGluZyBmaWxlIGlzIGV4ZW1wdCBmcm9tIHRoZSB2YWxpZC13aW5kb3cgYW5kIHJvdXRlIHdyaXRlIGZpbHRlcnM6IHRoZSBDT05GSUdcbiAgICAgICAgLy8gbXVzdCBrZWVwIGZsb3dpbmcgdG8gZXZlcnkgc291cmNlLCBvciBhIGNsaWVudCBwcm9iaW5nIG9uZSBvZiB0aGVtIGZpcnN0IHdvdWxkIGFkb3B0IGFcbiAgICAgICAgLy8gc3RhbGUgY29uZmlnLiBPbmx5IGRhdGEgd3JpdGVzIHN0b3AuXG4gICAgICAgIGxldCBpc1JvdXRpbmcgPSBrZXkgPT09IFJPVVRJTkdfRklMRTtcbiAgICAgICAgbGV0IHdyaXRhYmxlID0gdGhpcy5nZXRXcml0YWJsZVNvdXJjZXMoeyBpZ25vcmVXaW5kb3c6IGlzUm91dGluZyB9KTtcbiAgICAgICAgbGV0IGZpcnN0ID0gd3JpdGFibGUuc2hpZnQoKTtcbiAgICAgICAgaWYgKGZpcnN0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gc291cmNlIGFjY2VwdHMgd3JpdGVzIChldmVyeSBzb3VyY2UncyB2YWxpZCB3aW5kb3cgaXMgaW4gdGhlIHBhc3QpLCBzbyB3cml0ZXMgY2Fubm90IGJlIHN0b3JlZCAoc3RvcmUgJHt0aGlzLmZvbGRlcn0pYCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gT25seSBvdXIgb3duIChmaXJzdCkgc291cmNlIGJsb2NrcyB0aGUgd3JpdGUuIERvd25zdHJlYW0gc291cmNlcyBhcmUgd3JpdHRlbiBpbiB0aGVcbiAgICAgICAgLy8gYmFja2dyb3VuZDogYSBkb3duIGRvd25zdHJlYW0gc291cmNlIG11c3Qgbm90IGZhaWwgb3Igc3RhbGwgd3JpdGVzLCBhbmQgcmVjb25jaWxlU291cmNlXG4gICAgICAgIC8vIHJlLXNlbmRzIGFueXRoaW5nIHRoZXkgbWlzc2VkIG9uY2UgdGhleSBjb21lIGJhY2suXG4gICAgICAgIGlmIChkYXRhLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgLy8gQSB0b21ic3RvbmUgc3RvcmVzIG5vdGhpbmcgb24gb3VyIG93biBzb3VyY2UgLSB0aGUgaW5kZXggZW50cnkgYWxvbmUgcmVjb3JkcyBpdFxuICAgICAgICAgICAgYXdhaXQgdGhpcy5zb3VyY2VzW2ZpcnN0XS5zb3VyY2UuZGVsKGtleSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNvdXJjZXNbZmlyc3RdLnNvdXJjZS5zZXQoa2V5LCBkYXRhLCB7IGxhc3RNb2RpZmllZDogd3JpdGVUaW1lIH0pO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2V0SW5kZXhFbnRyeShrZXksIHsgd3JpdGVUaW1lLCBzaXplOiBkYXRhLmxlbmd0aCwgc291cmNlOiBmaXJzdCB9KTtcbiAgICAgICAgbGV0IHJvdXRlID0gIWlzUm91dGluZyAmJiBnZXRSb3V0ZShrZXkpIHx8IDA7XG4gICAgICAgIGZvciAobGV0IGkgb2Ygd3JpdGFibGUpIHtcbiAgICAgICAgICAgIGlmICghaXNSb3V0aW5nICYmICFyb3V0ZUNvbnRhaW5zKHRoaXMuc291cmNlc1tpXS5yb3V0ZSwgcm91dGUpKSBjb250aW51ZTtcbiAgICAgICAgICAgIC8vIERvd25zdHJlYW0gc291cmNlcyByZWNlaXZlIHRvbWJzdG9uZXMgYXMgYWN0dWFsIGVtcHR5IHdyaXRlcywgc28gdGhlaXIgbGlzdGluZ3Mgc2hvd1xuICAgICAgICAgICAgLy8gdGhlIGRlbGV0aW9uIChzaXplIDApIGFuZCBvdGhlciBzdG9yZXMgc2NhbiBpdCBpbiBhcyBhIHRvbWJzdG9uZVxuICAgICAgICAgICAgdm9pZCB0aGlzLnNvdXJjZXNbaV0uc291cmNlLnNldChrZXksIGRhdGEsIHsgbGFzdE1vZGlmaWVkOiB3cml0ZVRpbWUgfSkuY2F0Y2goKGU6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihgQmFja2dyb3VuZCB3cml0ZSBvZiAke2tleX0gdG8gc3luYyBzb3VyY2UgJHt0aGlzLnNvdXJjZXNbaV0uc291cmNlLmdldERlYnVnTmFtZSgpfSBmYWlsZWQ6ICR7ZS5zdGFjayA/PyBlfWApO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgZ2V0SW5mbyhrZXk6IHN0cmluZyk6IFByb21pc2U8eyB3cml0ZVRpbWU6IG51bWJlcjsgc2l6ZTogbnVtYmVyIH0gfCB1bmRlZmluZWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5pbml0KCk7XG4gICAgICAgIGxldCBvdmVybGF5RW50cnkgPSB0aGlzLm92ZXJsYXkuZ2V0KGtleSk7XG4gICAgICAgIGlmIChvdmVybGF5RW50cnkpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHdyaXRlVGltZTogb3ZlcmxheUVudHJ5LnQsIHNpemU6IG92ZXJsYXlFbnRyeS5kYXRhLmxlbmd0aCB9O1xuICAgICAgICB9XG4gICAgICAgIGxldCBlbnRyeSA9IGF3YWl0IHRoaXMuZ2V0SW5kZXhFbnRyeShrZXkpO1xuICAgICAgICBpZiAoIWVudHJ5KSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICByZXR1cm4geyB3cml0ZVRpbWU6IGVudHJ5LndyaXRlVGltZSwgc2l6ZTogZW50cnkuc2l6ZSB9O1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBmaW5kSW5mbyhwcmVmaXg6IHN0cmluZywgY29uZmlnPzogeyBzaGFsbG93PzogYm9vbGVhbjsgdHlwZT86IFwiZmlsZXNcIiB8IFwiZm9sZGVyc1wiIH0pOiBQcm9taXNlPEFyY2hpdmVGaWxlSW5mb1tdPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuICAgICAgICBhd2FpdCB0aGlzLndhaXRGb3JSZXF1aXJlZFNjYW5zKCk7XG4gICAgICAgIGxldCBpbmZvcyA9IG5ldyBNYXA8c3RyaW5nLCBBcmNoaXZlRmlsZUluZm8+KCk7XG4gICAgICAgIGZvciAobGV0IFtrZXksIGVudHJ5XSBvZiB0aGlzLm1lbSkge1xuICAgICAgICAgICAgaWYgKCFrZXkuc3RhcnRzV2l0aChwcmVmaXgpKSBjb250aW51ZTtcbiAgICAgICAgICAgIC8vIFRvbWJzdG9uZXMgYXJlIG1pc3NpbmcgZmlsZXMsIHNvIGxpc3RpbmdzIGhpZGUgdGhlbVxuICAgICAgICAgICAgaWYgKGVudHJ5LnNpemUgPT09IDApIGNvbnRpbnVlO1xuICAgICAgICAgICAgaW5mb3Muc2V0KGtleSwgeyBwYXRoOiBrZXksIGNyZWF0ZVRpbWU6IGVudHJ5LndyaXRlVGltZSwgc2l6ZTogZW50cnkuc2l6ZSB9KTtcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCBba2V5LCBvdmVybGF5RW50cnldIG9mIHRoaXMub3ZlcmxheSkge1xuICAgICAgICAgICAgaWYgKCFrZXkuc3RhcnRzV2l0aChwcmVmaXgpKSBjb250aW51ZTtcbiAgICAgICAgICAgIGlmIChvdmVybGF5RW50cnkuZGF0YS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICBpbmZvcy5kZWxldGUoa2V5KTtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGluZm9zLnNldChrZXksIHsgcGF0aDoga2V5LCBjcmVhdGVUaW1lOiBvdmVybGF5RW50cnkudCwgc2l6ZTogb3ZlcmxheUVudHJ5LmRhdGEubGVuZ3RoIH0pO1xuICAgICAgICB9XG4gICAgICAgIGxldCBmaWxlcyA9IGFwcGx5RmluZEluZm9TaGFwZShBcnJheS5mcm9tKGluZm9zLnZhbHVlcygpKSwgcHJlZml4LCBjb25maWcpO1xuICAgICAgICBzb3J0KGZpbGVzLCB4ID0+IHgucGF0aCk7XG4gICAgICAgIHJldHVybiBmaWxlcztcbiAgICB9XG5cbiAgICAvLyBBbGwgZmlsZXMgY2hhbmdlZCBhZnRlciB0aGUgZ2l2ZW4gdGltZSDigJQgZmFzdCwgc3RyYWlnaHQgZnJvbSB0aGUgaW4tbWVtb3J5IGluZGV4LiBGaWx0ZXJzIG9uXG4gICAgLy8gd2hlbiBXRSBsZWFybmVkIG9mIHRoZSBjaGFuZ2UgKGNoYW5nZWRBdCksIHNvIGZpbGVzIHN5bmNocm9uaXplZCBsYXRlICh3aXRoIG9sZCB3cml0ZSB0aW1lcylcbiAgICAvLyBhcmUgc3RpbGwgcmVwb3J0ZWQuIERlbGV0aW9ucyBBUkUgcmVwb3J0ZWQsIGFzIHNpemUtMCB0b21ic3RvbmUgZW50cmllcyDigJQgdGhhdCdzIGhvdyB0aGV5XG4gICAgLy8gcHJvcGFnYXRlIHRvIHN0b3JlcyBzeW5jaW5nIGZyb20gdXMuXG4gICAgcHVibGljIGFzeW5jIGdldENoYW5nZXNBZnRlcih0aW1lOiBudW1iZXIpOiBQcm9taXNlPEFyY2hpdmVGaWxlSW5mb1tdPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuICAgICAgICBhd2FpdCB0aGlzLndhaXRGb3JSZXF1aXJlZFNjYW5zKCk7XG4gICAgICAgIGxldCBmaWxlczogQXJjaGl2ZUZpbGVJbmZvW10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQgW2tleSwgZW50cnldIG9mIHRoaXMubWVtKSB7XG4gICAgICAgICAgICBpZiAoZW50cnkuY2hhbmdlZEF0IDw9IHRpbWUpIGNvbnRpbnVlO1xuICAgICAgICAgICAgaWYgKHRoaXMub3ZlcmxheS5oYXMoa2V5KSkgY29udGludWU7XG4gICAgICAgICAgICBmaWxlcy5wdXNoKHsgcGF0aDoga2V5LCBjcmVhdGVUaW1lOiBlbnRyeS53cml0ZVRpbWUsIHNpemU6IGVudHJ5LnNpemUgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgW2tleSwgb3ZlcmxheUVudHJ5XSBvZiB0aGlzLm92ZXJsYXkpIHtcbiAgICAgICAgICAgIGlmIChvdmVybGF5RW50cnkudCA8PSB0aW1lKSBjb250aW51ZTtcbiAgICAgICAgICAgIGZpbGVzLnB1c2goeyBwYXRoOiBrZXksIGNyZWF0ZVRpbWU6IG92ZXJsYXlFbnRyeS50LCBzaXplOiBvdmVybGF5RW50cnkuZGF0YS5sZW5ndGggfSk7XG4gICAgICAgIH1cbiAgICAgICAgc29ydChmaWxlcywgeCA9PiB4LnBhdGgpO1xuICAgICAgICByZXR1cm4gZmlsZXM7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGdldFN5bmNTdGF0dXMoKTogUHJvbWlzZTxBcmNoaXZlc1N5bmNTdGF0dXM+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5pbml0KCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBhbGxTY2Fuc0NvbXBsZXRlOiB0aGlzLnNvdXJjZVN0YXRlcy5ldmVyeSh4ID0+IHguc2NhbkNvbXBsZXRlKSxcbiAgICAgICAgICAgIGluZGV4U2l6ZTogdGhpcy5tZW0uc2l6ZSxcbiAgICAgICAgICAgIHNvdXJjZXM6IHRoaXMuc291cmNlcy5tYXAoKHgsIGkpID0+ICh7XG4gICAgICAgICAgICAgICAgZGVidWdOYW1lOiB4LnNvdXJjZS5nZXREZWJ1Z05hbWUoKSxcbiAgICAgICAgICAgICAgICB2YWxpZFdpbmRvdzogeC52YWxpZFdpbmRvdyxcbiAgICAgICAgICAgICAgICByb3V0ZTogeC5yb3V0ZSxcbiAgICAgICAgICAgICAgICBub0Z1bGxTeW5jOiB4Lm5vRnVsbFN5bmMsXG4gICAgICAgICAgICAgICAgc3VwcG9ydHNDaGFuZ2VzQWZ0ZXI6IHRoaXMuc291cmNlU3RhdGVzW2ldLnN1cHBvcnRzQ2hhbmdlc0FmdGVyLFxuICAgICAgICAgICAgICAgIGluaXRpYWxTY2FuQ29tcGxldGU6IHRoaXMuc291cmNlU3RhdGVzW2ldLnNjYW5Db21wbGV0ZSxcbiAgICAgICAgICAgICAgICBzY2FubmVkQ291bnQ6IHRoaXMuc291cmNlU3RhdGVzW2ldLnNjYW5uZWRDb3VudCxcbiAgICAgICAgICAgIH0pKSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyDilIDilIAgbGFyZ2UgdXBsb2FkcyDilIDilIBcbiAgICAvLyBMYXJnZSB1cGxvYWRzIHN0cmVhbSBvbnRvIHRoZSBsb2NhbCBkaXNrIHNvdXJjZSBkaXJlY3RseSAodGhleSBtYXkgbm90IGZpdCBpbiBtZW1vcnkpXG5cbiAgICBwcml2YXRlIGdldERpc2tTb3VyY2UoKTogeyBkaXNrOiBBcmNoaXZlc0Rpc2s7IHNvdXJjZUluZGV4OiBudW1iZXIgfSB7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5zb3VyY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBsZXQgc291cmNlID0gdGhpcy5zb3VyY2VzW2ldLnNvdXJjZTtcbiAgICAgICAgICAgIGlmIChzb3VyY2UgaW5zdGFuY2VvZiBBcmNoaXZlc0Rpc2spIHJldHVybiB7IGRpc2s6IHNvdXJjZSwgc291cmNlSW5kZXg6IGkgfTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYExhcmdlIHVwbG9hZHMgcmVxdWlyZSBhbiBBcmNoaXZlc0Rpc2sgc291cmNlLCBhbmQgdGhpcyBzdG9yZSBoYXMgbm9uZSAoc3RvcmUgJHt0aGlzLmZvbGRlcn0pYCk7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzdGFydExhcmdlVXBsb2FkKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuaW5pdCgpO1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXREaXNrU291cmNlKCkuZGlzay5zdGFydExhcmdlVXBsb2FkKCk7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBhcHBlbmRMYXJnZVVwbG9hZChpZDogc3RyaW5nLCBkYXRhOiBCdWZmZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5nZXREaXNrU291cmNlKCkuZGlzay5hcHBlbmRMYXJnZVVwbG9hZChpZCwgZGF0YSk7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBmaW5pc2hMYXJnZVVwbG9hZChpZDogc3RyaW5nLCBrZXk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgeyBkaXNrLCBzb3VyY2VJbmRleCB9ID0gdGhpcy5nZXREaXNrU291cmNlKCk7XG4gICAgICAgIGF3YWl0IGRpc2suZmluaXNoTGFyZ2VVcGxvYWQoaWQsIGtleSk7XG4gICAgICAgIHRoaXMub3ZlcmxheS5kZWxldGUoa2V5KTtcbiAgICAgICAgbGV0IGluZm8gPSBhd2FpdCBkaXNrLmdldEluZm8oa2V5KTtcbiAgICAgICAgaWYgKGluZm8pIHtcbiAgICAgICAgICAgIHRoaXMuc2V0SW5kZXhFbnRyeShrZXksIHsgd3JpdGVUaW1lOiBpbmZvLndyaXRlVGltZSwgc2l6ZTogaW5mby5zaXplLCBzb3VyY2U6IHNvdXJjZUluZGV4IH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBjYW5jZWxMYXJnZVVwbG9hZChpZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuZ2V0RGlza1NvdXJjZSgpLmRpc2suY2FuY2VsTGFyZ2VVcGxvYWQoaWQpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZmx1c2hPdmVybGF5KGZvcmNlPzogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgbm93ID0gRGF0ZS5ub3coKTtcbiAgICAgICAgZm9yIChsZXQgW2tleSwgZW50cnldIG9mIHRoaXMub3ZlcmxheSkge1xuICAgICAgICAgICAgaWYgKCFmb3JjZSAmJiBlbnRyeS5mbHVzaEF0ID4gbm93KSBjb250aW51ZTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMud3JpdGVUb1NvdXJjZXMoa2V5LCBlbnRyeS5kYXRhLCBlbnRyeS50KTtcbiAgICAgICAgICAgIC8vIE9ubHkgcmVtb3ZlIGlmIGl0IHdhc24ndCBvdmVyd3JpdHRlbiB3aGlsZSB3ZSB3ZXJlIGZsdXNoaW5nXG4gICAgICAgICAgICBpZiAodGhpcy5vdmVybGF5LmdldChrZXkpID09PSBlbnRyeSkge1xuICAgICAgICAgICAgICAgIHRoaXMub3ZlcmxheS5kZWxldGUoa2V5KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIHJlYWRlckRpc2tMaW1pdDogdGhlIGRpc2sgaXMgb25seSBhIGJvdW5kZWQgcmVhZCBjYWNoZSwgc28gb25jZSBpdCBleGNlZWRzIHRoZSBsaW1pdCwgdGhlXG4gICAgLy8gbGVhc3QgcmVjZW50bHkgdXNlZCBmaWxlcyBhcmUgZGVsZXRlZCBmcm9tIGl0IC0gYnV0IE9OTFkgd2hlbiBhbm90aGVyIHNvdXJjZSB2ZXJpZmlhYmx5XG4gICAgLy8gaG9sZHMgYSBzYW1lLW9yLW5ld2VyIGNvcHkgKHRoZSBvbmx5IGNvcHkgb2YgYSBmaWxlIGlzIG5ldmVyIGRlbGV0ZWQpLCBhbmQgdGhlIGluZGV4IGVudHJ5XG4gICAgLy8gcmVwb2ludHMgdG8gdGhhdCBzb3VyY2Ugc28gcmVhZHMga2VlcCB3b3JraW5nIChyZS1jYWNoaW5nIG9uIHRoZSBuZXh0IHJlYWQpLlxuICAgIHByaXZhdGUgZXZpY3RpbmcgPSBmYWxzZTtcbiAgICBwcml2YXRlIGFzeW5jIGVuZm9yY2VEaXNrTGltaXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGxldCBsaW1pdCA9IHRoaXMuY29uZmlnPy5yZWFkZXJEaXNrTGltaXQ7XG4gICAgICAgIGlmICghbGltaXQgfHwgdGhpcy5ldmljdGluZykgcmV0dXJuO1xuICAgICAgICBpZiAodGhpcy5zb3VyY2VCeXRlQ291bnRzWzBdIDw9IGxpbWl0KSByZXR1cm47XG4gICAgICAgIHRoaXMuZXZpY3RpbmcgPSB0cnVlO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbGV0IGNhbmRpZGF0ZXM6IHsga2V5OiBzdHJpbmc7IGVudHJ5OiBJbmRleEVudHJ5IH1bXSA9IFtdO1xuICAgICAgICAgICAgZm9yIChsZXQgW2tleSwgZW50cnldIG9mIHRoaXMubWVtKSB7XG4gICAgICAgICAgICAgICAgaWYgKGVudHJ5LnNvdXJjZSAhPT0gMCB8fCBlbnRyeS5zaXplID09PSAwIHx8IGtleSA9PT0gUk9VVElOR19GSUxFKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICBjYW5kaWRhdGVzLnB1c2goeyBrZXksIGVudHJ5IH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc29ydChjYW5kaWRhdGVzLCB4ID0+IHguZW50cnkubGFzdEFjY2Vzcyk7XG4gICAgICAgICAgICBmb3IgKGxldCB7IGtleSwgZW50cnkgfSBvZiBjYW5kaWRhdGVzKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc3RvcHBlZC5zdG9wKSByZXR1cm47XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc291cmNlQnl0ZUNvdW50c1swXSA8PSBsaW1pdCkgYnJlYWs7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMubWVtLmdldChrZXkpICE9PSBlbnRyeSkgY29udGludWU7XG4gICAgICAgICAgICAgICAgbGV0IGhvbGRlcjogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAxOyBpIDwgdGhpcy5zb3VyY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgaW5mbyA9IGF3YWl0IHRoaXMuc291cmNlc1tpXS5zb3VyY2UuZ2V0SW5mbyhrZXkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGluZm8gJiYgaW5mby53cml0ZVRpbWUgPj0gZW50cnkud3JpdGVUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaG9sZGVyID0gaTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBBIGRvd24gc291cmNlIGp1c3QgY2FuJ3Qgdm91Y2ggZm9yIHRoaXMgZmlsZSByaWdodCBub3dcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoaG9sZGVyID09PSB1bmRlZmluZWQpIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuc291cmNlc1swXS5zb3VyY2UuZGVsKGtleSk7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXRJbmRleEVudHJ5KGtleSwgeyB3cml0ZVRpbWU6IGVudHJ5LndyaXRlVGltZSwgc2l6ZTogZW50cnkuc2l6ZSwgc291cmNlOiBob2xkZXIgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgICB0aGlzLmV2aWN0aW5nID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUb21ic3RvbmVzIG9ubHkgbmVlZCB0byBleGlzdCBsb25nIGVub3VnaCBmb3IgZXZlcnkgc3RvcmUgdG8gbGVhcm4gb2YgdGhlIGRlbGV0aW9uOyBleHBpcmVkXG4gICAgLy8gb25lcyBjb21lIG91dCBvZiB0aGUgaW5kZXguIFRoZSBwaHlzaWNhbCBlbXB0eSBmaWxlIGlzIHJlbW92ZWQgb25seSBvbiBiYWNrYmxhemUgc291cmNlczpcbiAgICAvLyByZW1vdGUgc3RvcmVzIGV4cGlyZSB0aGVpciBvd24gdG9tYnN0b25lcyAoYSBkZWwgdGhlcmUgd291bGQganVzdCBtaW50IGEgZnJlc2ggb25lKSwgYW5kIG91clxuICAgIC8vIG93biBkaXNrIG5ldmVyIHN0b3JlZCBhbnl0aGluZyBmb3IgaXQuXG4gICAgcHJpdmF0ZSBhc3luYyBjbGVhbnVwVG9tYnN0b25lcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgbGV0IGN1dG9mZiA9IERhdGUubm93KCkgLSBUT01CU1RPTkVfRVhQSVJZO1xuICAgICAgICBmb3IgKGxldCBba2V5LCBlbnRyeV0gb2YgdGhpcy5tZW0pIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnN0b3BwZWQuc3RvcCkgcmV0dXJuO1xuICAgICAgICAgICAgaWYgKGVudHJ5LnNpemUgIT09IDApIGNvbnRpbnVlO1xuICAgICAgICAgICAgaWYgKGVudHJ5LndyaXRlVGltZSA+IGN1dG9mZikgY29udGludWU7XG4gICAgICAgICAgICB0aGlzLmRlbGV0ZUluZGV4RW50cnkoa2V5KTtcbiAgICAgICAgICAgIGZvciAobGV0IHNvdXJjZUVudHJ5IG9mIHRoaXMuc291cmNlcykge1xuICAgICAgICAgICAgICAgIGlmICghd2luZG93QWNjZXB0c1dyaXRlcyhzb3VyY2VFbnRyeS52YWxpZFdpbmRvdykpIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIGxldCBzb3VyY2UgPSBzb3VyY2VFbnRyeS5zb3VyY2U7XG4gICAgICAgICAgICAgICAgaWYgKCEoc291cmNlIGluc3RhbmNlb2YgQXJjaGl2ZXNCYWNrYmxhemUpKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB2b2lkIHNvdXJjZS5kZWwoa2V5KS5jYXRjaCgoZTogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihgUmVtb3ZpbmcgZXhwaXJlZCB0b21ic3RvbmUgJHtrZXl9IGZyb20gJHtzb3VyY2UuZ2V0RGVidWdOYW1lKCl9IGZhaWxlZDogJHtlLnN0YWNrID8/IGV9YCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG59XG4iXX0=
|
|
837
|
+
/* _JS_SOURCE_HASH = "21b10d46919aa6fc475e2f94667760cf3bf3074e2b125584bbf341cdbed22626"; */
|