sliftutils 1.7.102 → 1.7.104
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/index.d.ts +223 -7
- package/package.json +2 -2
- package/storage/IArchives.d.ts +18 -0
- package/storage/IArchives.ts +14 -0
- package/storage/StreamingLogs.d.ts +77 -0
- package/storage/StreamingLogs.ts +412 -0
- package/storage/TransactionFile.d.ts +12 -6
- package/storage/TransactionFile.ts +44 -12
- package/storage/dist/IArchives.ts.cache +2 -2
- package/storage/dist/StreamingLogs.ts.cache +397 -0
- package/storage/dist/TransactionFile.ts.cache +41 -9
- package/storage/remoteStorage/ArchivesRemote.ts +3 -3
- package/storage/remoteStorage/blobStore.d.ts +28 -0
- package/storage/remoteStorage/blobStore.ts +117 -11
- package/storage/remoteStorage/createArchives.d.ts +40 -1
- package/storage/remoteStorage/createArchives.ts +84 -0
- package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +5 -5
- package/storage/remoteStorage/dist/blobStore.ts.cache +122 -12
- package/storage/remoteStorage/dist/createArchives.ts.cache +87 -3
- package/storage/remoteStorage/dist/storageController.ts.cache +40 -3
- package/storage/remoteStorage/dist/storageLogs.ts.cache +109 -0
- package/storage/remoteStorage/dist/storageServerState.ts.cache +18 -4
- package/storage/remoteStorage/dist/storeSync.ts.cache +43 -2
- package/storage/remoteStorage/spec.md +4 -0
- package/storage/remoteStorage/storageController.d.ts +10 -0
- package/storage/remoteStorage/storageController.ts +37 -3
- package/storage/remoteStorage/storageLogs.d.ts +29 -0
- package/storage/remoteStorage/storageLogs.ts +96 -0
- package/storage/remoteStorage/storageServerState.ts +16 -2
- package/storage/remoteStorage/storeSync.d.ts +1 -0
- package/storage/remoteStorage/storeSync.ts +38 -0
- package/yarn.lock +4 -4
|
@@ -57,6 +57,9 @@ class TransactionFile {
|
|
|
57
57
|
this.values.delete(record.k);
|
|
58
58
|
this.deleted.delete(record.k);
|
|
59
59
|
}
|
|
60
|
+
else if (record.m) {
|
|
61
|
+
this.applyDelete(record.k, record.t, record.t, record.v, record.w);
|
|
62
|
+
}
|
|
60
63
|
else if (record.d) {
|
|
61
64
|
this.applyDelete(record.k, record.t, record.t);
|
|
62
65
|
}
|
|
@@ -77,7 +80,7 @@ class TransactionFile {
|
|
|
77
80
|
get(key) {
|
|
78
81
|
return this.values.get(key);
|
|
79
82
|
}
|
|
80
|
-
/** When the key was deleted, if it was. Absent both here and in get means we have never heard of it. */
|
|
83
|
+
/** When the key was deleted, if it was (value included when the deletion is still marked - see LogTombstone). Absent both here and in get means we have never heard of it. */
|
|
81
84
|
getDeleted(key) {
|
|
82
85
|
return this.deleted.get(key);
|
|
83
86
|
}
|
|
@@ -111,13 +114,37 @@ class TransactionFile {
|
|
|
111
114
|
this.append({ k: key, t: time, v: value });
|
|
112
115
|
return true;
|
|
113
116
|
}
|
|
114
|
-
/** Deletes as of `time`, keeping the tombstone. Returns false when something at least as new is already here. */
|
|
117
|
+
/** Deletes as of `time`, keeping the tombstone. A key that had a live value keeps it in the tombstone as MARKED for deletion (readable and restorable until dropValue). Returns false when something at least as new is already here. */
|
|
115
118
|
delete(key, time) {
|
|
116
|
-
|
|
119
|
+
let live = this.values.get(key);
|
|
120
|
+
if (!this.applyDelete(key, time, Date.now(), live === null || live === void 0 ? void 0 : live.value, live === null || live === void 0 ? void 0 : live.time))
|
|
121
|
+
return false;
|
|
122
|
+
if (live) {
|
|
123
|
+
this.append({ k: key, t: time, m: 1, v: live.value, w: live.time });
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.append({ k: key, t: time, d: 1 });
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
/** Undoes a marked deletion: the kept value becomes live again, as of `time` (a fresh time, so the restore outranks the deletion everywhere it propagated). Returns false when there is no marked value to restore, or something at least as new is already here. */
|
|
131
|
+
unmark(key, time) {
|
|
132
|
+
let tombstone = this.deleted.get(key);
|
|
133
|
+
if (!tombstone || tombstone.value === undefined)
|
|
134
|
+
return false;
|
|
135
|
+
if (!this.applySet(key, tombstone.value, time, Date.now()))
|
|
117
136
|
return false;
|
|
118
|
-
this.append({ k: key, t: time,
|
|
137
|
+
this.append({ k: key, t: time, v: tombstone.value });
|
|
119
138
|
return true;
|
|
120
139
|
}
|
|
140
|
+
/** Drops a marked deletion's kept value (its history has been physically removed), leaving a plain tombstone with the same delete time. */
|
|
141
|
+
dropValue(key) {
|
|
142
|
+
let tombstone = this.deleted.get(key);
|
|
143
|
+
if (!tombstone || tombstone.value === undefined)
|
|
144
|
+
return;
|
|
145
|
+
this.deleted.set(key, { time: tombstone.time, changedAt: Date.now() });
|
|
146
|
+
this.append({ k: key, t: tombstone.time, d: 1 });
|
|
147
|
+
}
|
|
121
148
|
/** Forgets the key entirely, tombstone included - for a tombstone old enough that nobody needs to hear about the deletion any more, and for an entry that turned out never to have existed. Not a deletion: it leaves nothing behind to propagate. */
|
|
122
149
|
purge(key) {
|
|
123
150
|
let had = this.values.delete(key);
|
|
@@ -133,11 +160,11 @@ class TransactionFile {
|
|
|
133
160
|
this.values.set(key, { value, time, changedAt });
|
|
134
161
|
return true;
|
|
135
162
|
}
|
|
136
|
-
applyDelete(key, time, changedAt) {
|
|
163
|
+
applyDelete(key, time, changedAt, value, valueTime) {
|
|
137
164
|
if (time < this.timeOf(key))
|
|
138
165
|
return false;
|
|
139
166
|
this.values.delete(key);
|
|
140
|
-
this.deleted.set(key, { time, changedAt });
|
|
167
|
+
this.deleted.set(key, { time, changedAt, value, valueTime });
|
|
141
168
|
return true;
|
|
142
169
|
}
|
|
143
170
|
append(record) {
|
|
@@ -192,7 +219,12 @@ class TransactionFile {
|
|
|
192
219
|
records.push(JSON.stringify({ k: key, t: entry.time, v: entry.value }));
|
|
193
220
|
}
|
|
194
221
|
for (let [key, tombstone] of this.deleted) {
|
|
195
|
-
|
|
222
|
+
if (tombstone.value !== undefined) {
|
|
223
|
+
records.push(JSON.stringify({ k: key, t: tombstone.time, m: 1, v: tombstone.value, w: tombstone.valueTime }));
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
records.push(JSON.stringify({ k: key, t: tombstone.time, d: 1 }));
|
|
227
|
+
}
|
|
196
228
|
}
|
|
197
229
|
// Cleared with the snapshot: every pending record is already reflected in the maps, so the snapshot contains it. Anything appended from here on is NOT in the snapshot, and lands in the new log after the rename.
|
|
198
230
|
this.pending = [];
|
|
@@ -208,5 +240,5 @@ class TransactionFile {
|
|
|
208
240
|
}
|
|
209
241
|
}
|
|
210
242
|
exports.TransactionFile = TransactionFile;
|
|
211
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJhbnNhY3Rpb25GaWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiVHJhbnNhY3Rpb25GaWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDRDQUFvQjtBQUNwQixnREFBd0I7QUFDeEIseURBQW1EO0FBRW5ELG9iQUFvYjtBQUNwYixFQUFFO0FBQ0Ysb0tBQW9LO0FBRXBLLCtJQUErSTtBQUMvSSxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUM7QUFDeEIsOEZBQThGO0FBQzlGLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0FBQ2pDLHNMQUFzTDtBQUN0TCxNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUM7QUFxQnhCLE1BQWEsZUFBZTtJQUN4QixZQUFvQixRQUFnQjtRQUFoQixhQUFRLEdBQVIsUUFBUSxDQUFRO1FBRTVCLFdBQU0sR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztRQUN4QyxZQUFPLEdBQUcsSUFBSSxHQUFHLEVBQXdCLENBQUM7UUFDbEQsNEdBQTRHO1FBQ3BHLGVBQVUsR0FBRyxDQUFDLENBQUM7UUFDZixZQUFPLEdBQWEsRUFBRSxDQUFDO1FBRS9CLGdHQUFnRztRQUN4RixlQUFVLEdBQWtCLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUV0RCw4RkFBOEY7UUFDdkYsU0FBSSxHQUFHLElBQUEsY0FBSSxFQUFDLEtBQUssSUFBSSxFQUFFO1lBQzFCLElBQUksSUFBWSxDQUFDO1lBQ2pCLElBQUksQ0FBQztnQkFDRCxJQUFJLEdBQUcsTUFBTSxZQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzdELENBQUM7WUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNULElBQUssQ0FBdUIsQ0FBQyxJQUFJLEtBQUssUUFBUTtvQkFBRSxPQUFPO2dCQUN2RCxNQUFNLENBQUMsQ0FBQztZQUNaLENBQUM7WUFDRCxJQUFJLE9BQU8sR0FBRyxD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c291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZzIGZyb20gXCJmc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IGxhenkgfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy9jYWNoaW5nXCI7XG5cbi8vIEEgcGVyc2lzdGVudCBNYXAgdGhhdCBrbm93cyBhYm91dCB0aW1lIGFuZCBhYm91dCBkZWxldGlvbnMuIEV2ZXJ5IHZhbHVlIGNhcnJpZXMgdGhlIHRpbWUgaXQgd2FzIHdyaXR0ZW4sIHNvIGEgd3JpdGUgdGhhdCBhcnJpdmVzIG91dCBvZiBvcmRlciBpcyBzaW1wbHkgbm90IGFwcGxpZWQgLSB0aGUgbWFwIGNhbm5vdCBiZSBtYWRlIHRvIGdvIGJhY2t3YXJkcy4gQSBkZWxldGVkIGtleSBrZWVwcyBpdHMgdGltZSB0b28sIGFzIGEgdG9tYnN0b25lIGluIGEgc2Vjb25kIG1hcCwgc28gXCJ3YXMgdGhpcyBkZWxldGVkLCBhbmQgd2hlblwiIGlzIGFzIGNoZWFwIHRvIGFzayBhcyBcImRvZXMgdGhpcyBleGlzdFwiLCBhbmQgZXhwaXJpbmcgb2xkIHRvbWJzdG9uZXMgaXMgYSB3YWxrIG9mIHRoZSB0b21ic3RvbmVzIGFsb25lIHJhdGhlciB0aGFuIG9mIGV2ZXJ5dGhpbmcuXG4vL1xuLy8gT24gZGlzayBpdCBpcyBvbmUgYXBwZW5kLW9ubHkgbG9nIG9mIHRoZSBzZXRzLCBkZWxldGVzIGFuZCBwdXJnZXMgdGhhdCBwcm9kdWNlZCBib3RoIG1hcHMsIHJld3JpdHRlbiBmcm9tIHRoZW0gd2hlbmV2ZXIgaXQgaGFzIGdyb3duIHRvIHNldmVyYWwgdGltZXMgdGhlaXIgc2l6ZS5cblxuLy8gVGhlIGxvZyBpcyByZXdyaXR0ZW4gb25jZSBpdCBob2xkcyB0aGlzIG1hbnkgdGltZXMgbW9yZSByZWNvcmRzIHRoYW4gdGhlcmUgYXJlIGtleXMgKGEgbG9nIG9mIDMgcmVjb3JkcyBwZXIgc3Vydml2aW5nIGtleSBpcyBtb3N0bHkgaGlzdG9yeSlcbmNvbnN0IENPTVBBQ1RfUkFUSU8gPSAzO1xuLy8gLi4uYnV0IG5ldmVyIGZvciBhIHNtYWxsIGxvZywgd2hlcmUgdGhlIHJld3JpdGUgd291bGQgY29zdCBtb3JlIHRoYW4gdGhlIGhpc3RvcnkgaXQgcmVtb3Zlc1xuY29uc3QgTUlOX0NPTVBBQ1RfUkVDT1JEUyA9IDEwMDA7XG4vLyBBcHBlbmRzIGFyZSBjb2FsZXNjZWQgZm9yIHRoaXMgbG9uZy4gQSBjcmFzaCBsb3NlcyBhdCBtb3N0IHRoaXMgbXVjaCAtIHRoZSBjYWxsZXJzIG9mIHRoaXMgY2xhc3MgdHJlYXQgdGhlaXIgZGF0YSBhcyByZWNvbnN0cnVjdGlibGUgLSBhbmQgdGhlIGFsdGVybmF0aXZlIGlzIGEgZmlsZSB3cml0ZSBwZXIgc2V0LlxuY29uc3QgRkxVU0hfREVMQVkgPSA1MDA7XG5cbi8qKiBBIGxpdmUgdmFsdWU6IHdoYXQgd2FzIHN0b3JlZCwgd2hlbiBpdCB3YXMgd3JpdHRlbiAodGhlIGNhbGxlcidzIG9yZGVyaW5nKSwgYW5kIHdoZW4gd2UgbGFzdCBjaGFuZ2VkIGl0IChvdXJzKS4gKi9cbmV4cG9ydCB0eXBlIExvZ0VudHJ5PFQ+ID0geyB2YWx1ZTogVDsgdGltZTogbnVtYmVyOyBjaGFuZ2VkQXQ6IG51bWJlciB9O1xuLyoqIEEgZGVsZXRlZCBrZXk6IHdoZW4gaXQgd2FzIGRlbGV0ZWQsIGFuZCB3aGVuIHdlIGxlYXJuZWQuIFRoZSB2YWx1ZSBpcyBnb25lOyB0aGUgdGltZSBpcyB0aGUgcG9pbnQgb2YgYSB0b21ic3RvbmUuICovXG5leHBvcnQgdHlwZSBMb2dUb21ic3RvbmUgPSB7IHRpbWU6IG51bWJlcjsgY2hhbmdlZEF0OiBudW1iZXIgfTtcblxuLy8gT25lIGxpbmUgb2YgdGhlIGxvZywgd2l0aCBzaW5nbGUtbGV0dGVyIGZpZWxkIG5hbWVzIGJlY2F1c2UgdGhlcmUgaXMgb25lIHJlY29yZCBwZXIgc2V0L2RlbGV0ZS9wdXJnZSBldmVyIG1hZGUgYW5kIHRoZSBmaWVsZCBuYW1lcyBhcmUgbW9zdCBvZiBhIHJlY29yZCdzIG92ZXJoZWFkLiBKU09OLnN0cmluZ2lmeSBlc2NhcGVzIG5ld2xpbmVzIGluc2lkZSBrZXlzIGFuZCB2YWx1ZXMsIHNvIGEgcmVjb3JkIGNhbiBuZXZlciBjb250YWluIHRoZSBzZXBhcmF0b3IuXG50eXBlIExvZ1JlY29yZDxUPiA9IHtcbiAgICAvLyBrZXlcbiAgICBrOiBzdHJpbmc7XG4gICAgLy8gdGltZSwgbGFzdE1vZGlmaWVkIHRpbWUgb2YgdGhlIGZpbGUsIG9yIHRoZSBkZWxldGUgdGltZSwgb3IgdGhlIHB1cmdlIHRpbWVcbiAgICB0OiBudW1iZXI7XG4gICAgLy8gdmFsdWVcbiAgICB2PzogVDtcbiAgICAvLyBkZWxldGVkLCB0aGUgZmlsZSBvbiBkaXNrIGlzIGRlbGV0ZWQsIHByZXNlcnZlZCB3aGVuIHdlIGNvbXBhY3RcbiAgICBkPzogMTtcbiAgICAvLyBwdXJnZWQsIHRoZSBmaWxlIG9uIGRpc2sgaXMgZGVsZXRlZCwgbm90IHByZXNlcnZlZCB3aGVuIHdlIGNvbXBhY3RcbiAgICBwPzogMTtcbn07XG5cbmV4cG9ydCBjbGFzcyBUcmFuc2FjdGlvbkZpbGU8VD4ge1xuICAgIGNvbnN0cnVjdG9yKHByaXZhdGUgZmlsZVBhdGg6IHN0cmluZykgeyB9XG5cbiAgICBwcml2YXRlIHZhbHVlcyA9IG5ldyBNYXA8c3RyaW5nLCBMb2dFbnRyeTxUPj4oKTtcbiAgICBwcml2YXRlIGRlbGV0ZWQgPSBuZXcgTWFwPHN0cmluZywgTG9nVG9tYnN0b25lPigpO1xuICAgIC8vIFJlY29yZHMgdGhlIGxvZyBob2xkcyAod3JpdHRlbiBwbHVzIHBlbmRpbmcpIC0gd2hhdCBjb21wYWN0aW9uIGlzIGRlY2lkZWQgYWdhaW5zdCwgTk9UIHRoZSBudW1iZXIgb2Yga2V5c1xuICAgIHByaXZhdGUgbG9nUmVjb3JkcyA9IDA7XG4gICAgcHJpdmF0ZSBwZW5kaW5nOiBzdHJpbmdbXSA9IFtdO1xuICAgIHByaXZhdGUgZmx1c2hUaW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCB1bmRlZmluZWQ7XG4gICAgLy8gU2VyaWFsaXplcyBldmVyeXRoaW5nIHRoYXQgdG91Y2hlcyB0aGUgZmlsZSwgc28gYW4gYXBwZW5kIGNhbiBuZXZlciBpbnRlcmxlYXZlIHdpdGggYSByZXdyaXRlXG4gICAgcHJpdmF0ZSB3cml0ZUNoYWluOiBQcm9taXNlPHZvaWQ+ID0gUHJvbWlzZS5yZXNvbHZlKCk7XG5cbiAgICAvKiogUmVhZHMgdGhlIGxvZyBhbmQgcmVwbGF5cyBpdCBpbnRvIG1lbW9yeS4gRXZlcnkgb3RoZXIgbWV0aG9kIGFzc3VtZXMgdGhpcyBoYXMgZmluaXNoZWQuICovXG4gICAgcHVibGljIGxvYWQgPSBsYXp5KGFzeW5jICgpID0+IHtcbiAgICAgICAgbGV0IGRhdGE6IHN0cmluZztcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGRhdGEgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkRmlsZSh0aGlzLmZpbGVQYXRoLCBcInV0ZjhcIik7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGlmICgoZSBhcyB7IGNvZGU/OiBzdHJpbmcgfSkuY29kZSA9PT0gXCJFTk9FTlRcIikgcmV0dXJuO1xuICAgICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgYXBwbGllZCA9IDA7XG4gICAgICAgIGxldCBkYW1hZ2VkID0gMDtcbiAgICAgICAgZm9yIChsZXQgbGluZSBvZiBkYXRhLnNwbGl0KFwiXFxuXCIpKSB7XG4gICAgICAgICAgICBpZiAoIWxpbmUpIGNvbnRpbnVlO1xuICAgICAgICAgICAgbGV0IHJlY29yZDogTG9nUmVjb3JkPFQ+IHwgdW5kZWZpbmVkO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICByZWNvcmQgPSBKU09OLnBhcnNlKGxpbmUpIGFzIExvZ1JlY29yZDxUPjtcbiAgICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgICAgIC8vIEEgY3Jhc2ggbWlkLWFwcGVuZCBsZWF2ZXMgYSB0b3JuIGZpbmFsIGxpbmU7IGFueXRoaW5nIGVhcmxpZXIgbWVhbnMgcmVhbCBkYW1hZ2UuIEVpdGhlciB3YXkgdGhlIHJlc3Qgb2YgdGhlIGxvZyBpcyBzdGlsbCB3b3J0aCBoYXZpbmcgLSB0aGlzIGlzIGFuIGluZGV4LCBhbmQgc2Nhbm5pbmcgcmVidWlsZHMgd2hhdGV2ZXIgd2UgbG9zZS5cbiAgICAgICAgICAgICAgICBkYW1hZ2VkKys7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBhcHBsaWVkKys7XG4gICAgICAgICAgICAvLyBSZXBsYXllZCB0aHJvdWdoIHRoZSBzYW1lIG9yZGVyaW5nIHJ1bGVzIHRoYXQgcHJvZHVjZWQgaXQsIHNvIGEgbG9nIHdyaXR0ZW4gb3V0IG9mIG9yZGVyIHN0aWxsIGxhbmRzIG9uIHRoZSBzYW1lIHN0YXRlXG4gICAgICAgICAgICBpZiAocmVjb3JkLnApIHtcbiAgICAgICAgICAgICAgICB0aGlzLnZhbHVlcy5kZWxldGUocmVjb3JkLmspO1xuICAgICAgICAgICAgICAgIHRoaXMuZGVsZXRlZC5kZWxldGUocmVjb3JkLmspO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChyZWNvcmQuZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlEZWxldGUocmVjb3JkLmssIHJlY29yZC50LCByZWNvcmQudCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHJlY29yZC52ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmFwcGx5U2V0KHJlY29yZC5rLCByZWNvcmQudiwgcmVjb3JkLnQsIHJlY29yZC50KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxvZ1JlY29yZHMgPSBhcHBsaWVkO1xuICAgICAgICBpZiAoZGFtYWdlZCkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihgJHtkYW1hZ2VkfSBkYW1hZ2VkIHJlY29yZChzKSBpbiAke3RoaXMuZmlsZVBhdGh9IHdlcmUgc2tpcHBlZDsgJHthcHBsaWVkfSBhcHBsaWVkLCBsZWF2aW5nICR7dGhpcy52YWx1ZXMuc2l6ZX0gdmFsdWVzIGFuZCAke3RoaXMuZGVsZXRlZC5zaXplfSB0b21ic3RvbmVzYCk7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8qKiBUaGUgbGl2ZSB2YWx1ZSwgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIGtleSBkb2VzIG5vdCBleGlzdCBoZXJlIChkZWxldGVkIGluY2x1ZGVkIC0gYSBkZWxldGlvbiBpcyBhbiBhYnNlbmNlLCBzZWUgZ2V0RGVsZXRlZCBmb3IgaXRzIHRpbWUpLiAqL1xuICAgIHB1YmxpYyBnZXQoa2V5OiBzdHJpbmcpOiBMb2dFbnRyeTxUPiB8IHVuZGVmaW5lZCB7XG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5nZXQoa2V5KTtcbiAgICB9XG4gICAgLyoqIFdoZW4gdGhlIGtleSB3YXMgZGVsZXRlZCwgaWYgaXQgd2FzLiBBYnNlbnQgYm90aCBoZXJlIGFuZCBpbiBnZXQgbWVhbnMgd2UgaGF2ZSBuZXZlciBoZWFyZCBvZiBpdC4gKi9cbiAgICBwdWJsaWMgZ2V0RGVsZXRlZChrZXk6IHN0cmluZyk6IExvZ1RvbWJzdG9uZSB8IHVuZGVmaW5lZCB7XG4gICAgICAgIHJldHVybiB0aGlzLmRlbGV0ZWQuZ2V0KGtleSk7XG4gICAgfVxuICAgIC8qKiBUaGUgdGltZSB0aGUga2V5IGxhc3QgY2hhbmdlZCBlaXRoZXIgd2F5LCBvciAwIGlmIHdlIGhhdmUgbmV2ZXIgaGVhcmQgb2YgaXQgLSB3aGF0IGEgbmV3IHdyaXRlIGhhcyB0byBiZWF0LiAqL1xuICAgIHB1YmxpYyB0aW1lT2Yoa2V5OiBzdHJpbmcpOiBudW1iZXIge1xuICAgICAgICBsZXQgbGl2ZSA9IHRoaXMudmFsdWVzLmdldChrZXkpO1xuICAgICAgICBpZiAobGl2ZSkgcmV0dXJuIGxpdmUudGltZTtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGVsZXRlZC5nZXQoa2V5KT8udGltZSB8fCAwO1xuICAgIH1cblxuICAgIC8qKiBPKDEpLCBhbmQgY291bnRzIG9ubHkgd2hhdCBleGlzdHMuICovXG4gICAgcHVibGljIGdldCBzaXplKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5zaXplO1xuICAgIH1cbiAgICBwdWJsaWMgZ2V0IGRlbGV0ZWRTaXplKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmRlbGV0ZWQuc2l6ZTtcbiAgICB9XG4gICAgLyoqIExpdmUgdmFsdWVzIG9ubHkuIExpdmUsIGluIGluc2VydGlvbiBvcmRlciAtIGRlbGV0aW5nIGR1cmluZyBpdGVyYXRpb24gaXMgc2FmZSAoSlMgc2tpcHMgZW50cmllcyByZW1vdmVkIGJlZm9yZSB0aGV5IGFyZSByZWFjaGVkKSwgd2hpY2ggaXMgd2hhdCB0aGUgcGFzc2VzIHRoYXQgd2FsayBldmVyeXRoaW5nIGFuZCBwcnVuZSBhcyB0aGV5IGdvIHJlbHkgb24uICovXG4gICAgcHVibGljIGVudHJpZXMoKTogSXRlcmFibGVJdGVyYXRvcjxbc3RyaW5nLCBMb2dFbnRyeTxUPl0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMudmFsdWVzLmVudHJpZXMoKTtcbiAgICB9XG4gICAgLyoqIFRoZSB0b21ic3RvbmVzLCB3aGljaCBpcyBhIG11Y2ggc21hbGxlciB3YWxrIHRoYW4gdGhlIHZhbHVlcyAtIHNvIGV4cGlyaW5nIHRoZW0sIG9yIGxpc3Rpbmcgd2hhdCB3YXMgZGVsZXRlZCBzaW5jZSBzb21lIHRpbWUsIGNvc3RzIHdoYXQgaXQgc2hvdWxkLiAqL1xuICAgIHB1YmxpYyBkZWxldGVkRW50cmllcygpOiBJdGVyYWJsZUl0ZXJhdG9yPFtzdHJpbmcsIExvZ1RvbWJzdG9uZV0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGVsZXRlZC5lbnRyaWVzKCk7XG4gICAgfVxuXG4gICAgLyoqIFN0b3JlcyBhIHZhbHVlIGFzIG9mIGB0aW1lYC4gUmV0dXJucyBmYWxzZSB3aGVuIHNvbWV0aGluZyBhdCBsZWFzdCBhcyBuZXcgaXMgYWxyZWFkeSBoZXJlLCBpbiB3aGljaCBjYXNlIG5vdGhpbmcgY2hhbmdlZCAtIGFuIG91dC1vZi1vcmRlciB3cml0ZSBpcyBub3QgYW4gZXJyb3IsIGl0IGlzIGp1c3QgbGF0ZS4gKi9cbiAgICBwdWJsaWMgc2V0KGtleTogc3RyaW5nLCB2YWx1ZTogVCwgdGltZTogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgICAgIGlmICghdGhpcy5hcHBseVNldChrZXksIHZhbHVlLCB0aW1lLCBEYXRlLm5vdygpKSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICB0aGlzLmFwcGVuZCh7IGs6IGtleSwgdDogdGltZSwgdjogdmFsdWUgfSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8qKiBEZWxldGVzIGFzIG9mIGB0aW1lYCwga2VlcGluZyB0aGUgdG9tYnN0b25lLiBSZXR1cm5zIGZhbHNlIHdoZW4gc29tZXRoaW5nIGF0IGxlYXN0IGFzIG5ldyBpcyBhbHJlYWR5IGhlcmUuICovXG4gICAgcHVibGljIGRlbGV0ZShrZXk6IHN0cmluZywgdGltZTogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgICAgIGlmICghdGhpcy5hcHBseURlbGV0ZShrZXksIHRpbWUsIERhdGUubm93KCkpKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIHRoaXMuYXBwZW5kKHsgazoga2V5LCB0OiB0aW1lLCBkOiAxIH0pO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAvKiogRm9yZ2V0cyB0aGUga2V5IGVudGlyZWx5LCB0b21ic3RvbmUgaW5jbHVkZWQgLSBmb3IgYSB0b21ic3RvbmUgb2xkIGVub3VnaCB0aGF0IG5vYm9keSBuZWVkcyB0byBoZWFyIGFib3V0IHRoZSBkZWxldGlvbiBhbnkgbW9yZSwgYW5kIGZvciBhbiBlbnRyeSB0aGF0IHR1cm5lZCBvdXQgbmV2ZXIgdG8gaGF2ZSBleGlzdGVkLiBOb3QgYSBkZWxldGlvbjogaXQgbGVhdmVzIG5vdGhpbmcgYmVoaW5kIHRvIHByb3BhZ2F0ZS4gKi9cbiAgICBwdWJsaWMgcHVyZ2Uoa2V5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICAgICAgbGV0IGhhZCA9IHRoaXMudmFsdWVzLmRlbGV0ZShrZXkpO1xuICAgICAgICBoYWQgPSB0aGlzLmRlbGV0ZWQuZGVsZXRlKGtleSkgfHwgaGFkO1xuICAgICAgICBpZiAoIWhhZCkgcmV0dXJuO1xuICAgICAgICB0aGlzLmFwcGVuZCh7IGs6IGtleSwgdDogRGF0ZS5ub3coKSwgcDogMSB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFwcGx5U2V0KGtleTogc3RyaW5nLCB2YWx1ZTogVCwgdGltZTogbnVtYmVyLCBjaGFuZ2VkQXQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICBpZiAodGltZSA8IHRoaXMudGltZU9mKGtleSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgdGhpcy5kZWxldGVkLmRlbGV0ZShrZXkpO1xuICAgICAgICB0aGlzLnZhbHVlcy5zZXQoa2V5LCB7IHZhbHVlLCB0aW1lLCBjaGFuZ2VkQXQgfSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXBwbHlEZWxldGUoa2V5OiBzdHJpbmcsIHRpbWU6IG51bWJlciwgY2hhbmdlZEF0OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgaWYgKHRpbWUgPCB0aGlzLnRpbWVPZihrZXkpKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIHRoaXMudmFsdWVzLmRlbGV0ZShrZXkpO1xuICAgICAgICB0aGlzLmRlbGV0ZWQuc2V0KGtleSwgeyB0aW1lLCBjaGFuZ2VkQXQgfSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXBwZW5kKHJlY29yZDogTG9nUmVjb3JkPFQ+KTogdm9pZCB7XG4gICAgICAgIHRoaXMucGVuZGluZy5wdXNoKEpTT04uc3RyaW5naWZ5KHJlY29yZCkpO1xuICAgICAgICB0aGlzLmxvZ1JlY29yZHMrKztcbiAgICAgICAgdGhpcy5zY2hlZHVsZUZsdXNoKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzY2hlZHVsZUZsdXNoKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5mbHVzaFRpbWVyICE9PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICAgICAgdGhpcy5mbHVzaFRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmZsdXNoVGltZXIgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICB2b2lkIHRoaXMuZmx1c2goKS5jYXRjaCgoZTogRXJyb3IpID0+IGNvbnNvbGUuZXJyb3IoYEZsdXNoaW5nICR7dGhpcy5maWxlUGF0aH0gZmFpbGVkOiAke2Uuc3RhY2sgPz8gZX1gKSk7XG4gICAgICAgIH0sIEZMVVNIX0RFTEFZKTtcbiAgICAgICAgKHRoaXMuZmx1c2hUaW1lciBhcyB7IHVucmVmPzogKCkgPT4gdm9pZCB9KS51bnJlZj8uKCk7XG4gICAgfVxuXG4gICAgLyoqIFdyaXRlcyBldmVyeXRoaW5nIHBlbmRpbmcgKHJld3JpdGluZyB0aGUgbG9nIGZpcnN0IGlmIGl0IGhhcyBncm93biB0b28gZmFyIHBhc3Qgd2hhdCBpdCBkZXNjcmliZXMpLiAqL1xuICAgIHB1YmxpYyBhc3luYyBmbHVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IHRoaXMud3JpdGVDaGFpbi50aGVuKCgpID0+IHRoaXMud3JpdGUoKSk7XG4gICAgICAgIC8vIFRoZSBjaGFpbiBvbmx5IGV4aXN0cyB0byBzZXJpYWxpemUgd3JpdGVzLCBzbyBpdCBtdXN0IG5vdCBzdGF5IHJlamVjdGVkOiBhIGZhaWx1cmUgYmVsb25ncyB0byB0aGUgY2FsbGVyIHRoYXQgYXNrZWQgZm9yIHRoaXMgZmx1c2gsIG5vdCB0byBldmVyeSBmbHVzaCBhZnRlciBpdFxuICAgICAgICB0aGlzLndyaXRlQ2hhaW4gPSByZXN1bHQuY2F0Y2goKCkgPT4geyB9KTtcbiAgICAgICAgYXdhaXQgcmVzdWx0O1xuICAgIH1cblxuICAgIHByaXZhdGUgZGlyZWN0b3J5ID0gbGF6eShhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IGZzLnByb21pc2VzLm1rZGlyKHBhdGguZGlybmFtZSh0aGlzLmZpbGVQYXRoKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfSk7XG5cbiAgICBwcml2YXRlIGFzeW5jIHdyaXRlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5sb2dSZWNvcmRzID4gTWF0aC5tYXgoTUlOX0NPTVBBQ1RfUkVDT1JEUywgKHRoaXMudmFsdWVzLnNpemUgKyB0aGlzLmRlbGV0ZWQuc2l6ZSkgKiBDT01QQUNUX1JBVElPKSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb21wYWN0KCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgdGhpcy5hcHBlbmRQZW5kaW5nKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBhcHBlbmRQZW5kaW5nKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIXRoaXMucGVuZGluZy5sZW5ndGgpIHJldHVybjtcbiAgICAgICAgbGV0IHJlY29yZHMgPSB0aGlzLnBlbmRpbmc7XG4gICAgICAgIHRoaXMucGVuZGluZyA9IFtdO1xuICAgICAgICBhd2FpdCB0aGlzLmRpcmVjdG9yeSgpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMuYXBwZW5kRmlsZSh0aGlzLmZpbGVQYXRoLCByZWNvcmRzLmpvaW4oXCJcXG5cIikgKyBcIlxcblwiKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgLy8gUHV0IHRoZW0gYmFjayByYXRoZXIgdGhhbiBsb3NlIHRoZW06IHRoZSB2YWx1ZXMgYXJlIHN0aWxsIGluIG1lbW9yeSwgc28gYWxsIHRoYXQgaXMgYXQgcmlzayBpcyBoYXZpbmcgdG8gcmVidWlsZCB0aGVtIGJ5IHNjYW5uaW5nXG4gICAgICAgICAgICB0aGlzLnBlbmRpbmcgPSByZWNvcmRzLmNvbmNhdCh0aGlzLnBlbmRpbmcpO1xuICAgICAgICAgICAgdGhpcy5zY2hlZHVsZUZsdXNoKCk7XG4gICAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gUmV3cml0ZXMgdGhlIGxvZyBhcyB0aGUgY3VycmVudCBjb250ZW50cyBvZiBib3RoIG1hcHMuIFRoZXkgYXJlIHRoZSB0cnV0aCAtIHRoZSBsb2cgb25seSBleGlzdHMgdG8gcmVidWlsZCB0aGVtIC0gc28gdGhlIHNuYXBzaG90IElTIHRoZSBuZXcgbG9nLlxuICAgIHByaXZhdGUgYXN5bmMgY29tcGFjdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgbGV0IHJlY29yZHM6IHN0cmluZ1tdID0gW107XG4gICAgICAgIGZvciAobGV0IFtrZXksIGVudHJ5XSBvZiB0aGlzLnZhbHVlcykge1xuICAgICAgICAgICAgcmVjb3Jkcy5wdXNoKEpTT04uc3RyaW5naWZ5KHsgazoga2V5LCB0OiBlbnRyeS50aW1lLCB2OiBlbnRyeS52YWx1ZSB9IHNhdGlzZmllcyBMb2dSZWNvcmQ8VD4pKTtcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGxldCBba2V5LCB0b21ic3RvbmVdIG9mIHRoaXMuZGVsZXRlZCkge1xuICAgICAgICAgICAgcmVjb3Jkcy5wdXNoKEpTT04uc3RyaW5naWZ5KHsgazoga2V5LCB0OiB0b21ic3RvbmUudGltZSwgZDogMSB9IHNhdGlzZmllcyBMb2dSZWNvcmQ8VD4pKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBDbGVhcmVkIHdpdGggdGhlIHNuYXBzaG90OiBldmVyeSBwZW5kaW5nIHJlY29yZCBpcyBhbHJlYWR5IHJlZmxlY3RlZCBpbiB0aGUgbWFwcywgc28gdGhlIHNuYXBzaG90IGNvbnRhaW5zIGl0LiBBbnl0aGluZyBhcHBlbmRlZCBmcm9tIGhlcmUgb24gaXMgTk9UIGluIHRoZSBzbmFwc2hvdCwgYW5kIGxhbmRzIGluIHRoZSBuZXcgbG9nIGFmdGVyIHRoZSByZW5hbWUuXG4gICAgICAgIHRoaXMucGVuZGluZyA9IFtdO1xuICAgICAgICBsZXQgc3RhcnRlZFdpdGggPSB0aGlzLmxvZ1JlY29yZHM7XG4gICAgICAgIGxldCB0ZW1wUGF0aCA9IGAke3RoaXMuZmlsZVBhdGh9LmNvbXBhY3RpbmdgO1xuICAgICAgICBhd2FpdCB0aGlzLmRpcmVjdG9yeSgpO1xuICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy53cml0ZUZpbGUodGVtcFBhdGgsIHJlY29yZHMubGVuZ3RoICYmIGAke3JlY29yZHMuam9pbihcIlxcblwiKX1cXG5gIHx8IFwiXCIpO1xuICAgICAgICAvLyBSZXBsYWNlcyB0aGUgb2xkIGxvZyBpbiBvbmUgc3RlcCwgc28gYSBjcmFzaCBsZWF2ZXMgZWl0aGVyIHRoZSB3aG9sZSBvbGQgbG9nIG9yIHRoZSB3aG9sZSBuZXcgb25lXG4gICAgICAgIGF3YWl0IGZzLnByb21pc2VzLnJlbmFtZSh0ZW1wUGF0aCwgdGhpcy5maWxlUGF0aCk7XG4gICAgICAgIHRoaXMubG9nUmVjb3JkcyA9IHJlY29yZHMubGVuZ3RoICsgdGhpcy5wZW5kaW5nLmxlbmd0aDtcbiAgICAgICAgY29uc29sZS5sb2coYFJld3JvdGUgJHt0aGlzLmZpbGVQYXRofTogJHtzdGFydGVkV2l0aH0gbG9nIHJlY29yZHMgZm9yICR7dGhpcy52YWx1ZXMuc2l6ZX0gdmFsdWVzIGFuZCAke3RoaXMuZGVsZXRlZC5zaXplfSB0b21ic3RvbmVzYCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYXBwZW5kUGVuZGluZygpO1xuICAgIH1cbn1cbiJdfQ==
|
|
212
|
-
/* _JS_SOURCE_HASH = "
|
|
243
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJhbnNhY3Rpb25GaWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiVHJhbnNhY3Rpb25GaWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDRDQUFvQjtBQUNwQixnREFBd0I7QUFDeEIseURBQW1EO0FBRW5ELG9iQUFvYjtBQUNwYixFQUFFO0FBQ0Ysb0tBQW9LO0FBRXBLLCtJQUErSTtBQUMvSSxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUM7QUFDeEIsOEZBQThGO0FBQzlGLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0FBQ2pDLHNMQUFzTDtBQUN0TCxNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUM7QUF5QnhCLE1BQWEsZUFBZTtJQUN4QixZQUFvQixRQUFnQjtRQUFoQixhQUFRLEdBQVIsUUFBUSxDQUFRO1FBRTVCLFdBQU0sR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztRQUN4QyxZQUFPLEdBQUcsSUFBSSxHQUFHLEVBQTJCLENBQUM7UUFDckQsNEdBQTRHO1FBQ3BHLGVBQVUsR0FBRyxDQUFDLENBQUM7UUFDZixZQUFPLEdBQWEsRUFBRSxDQUFDO1FBRS9CLGdHQUFnRztRQUN4RixlQUFVLEdBQWtCLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUV0RCw4RkFBOEY7UUFDdkYsU0FBSSxHQUFHLElBQUEsY0FBSSxFQUFDLEtBQUssSUFBSSxFQUFFO1lBQzFCLElBQUksSUFBWSxDQUFDO1lBQ2pCLElBQUksQ0FBQztnQkFDRCxJQUFJLEdBQUcsTUFBTSxZQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQzdELENBQUM7WUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNULElBQUssQ0FBdUIsQ0FBQyxJQUFJLEtBQUssUUFBUTtvQkFBRSxPQUFPO2dCQUN2RCxNQUFNLENBQUMsQ0FBQztZQUNaLENBQUM7WUFDRCxJQUFJLE9BQU8sR0FBRyxD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QUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBeUIsQ0FBQyxDQUFDLENBQUM7WUFDN0YsQ0FBQztRQUNMLENBQUM7UUFDRCxtTkFBbU47UUFDbk4sSUFBSSxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDbEIsSUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUNsQyxJQUFJLFFBQVEsR0FBRyxHQUFHLElBQUksQ0FBQyxRQUFRLGFBQWEsQ0FBQztRQUM3QyxNQUFNLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUN2QixNQUFNLFlBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3pGLG9HQUFvRztRQUNwRyxNQUFNLFlBQUUsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBQ3ZELE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxJQUFJLENBQUMsUUFBUSxLQUFLLFdBQVcsb0JBQW9CLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxlQUFlLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxhQUFhLENBQUMsQ0FBQztRQUN2SSxNQUFNLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUMvQixDQUFDO0NBQ0o7QUF4TkQsMENBd05DIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZzIGZyb20gXCJmc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IGxhenkgfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy9jYWNoaW5nXCI7XG5cbi8vIEEgcGVyc2lzdGVudCBNYXAgdGhhdCBrbm93cyBhYm91dCB0aW1lIGFuZCBhYm91dCBkZWxldGlvbnMuIEV2ZXJ5IHZhbHVlIGNhcnJpZXMgdGhlIHRpbWUgaXQgd2FzIHdyaXR0ZW4sIHNvIGEgd3JpdGUgdGhhdCBhcnJpdmVzIG91dCBvZiBvcmRlciBpcyBzaW1wbHkgbm90IGFwcGxpZWQgLSB0aGUgbWFwIGNhbm5vdCBiZSBtYWRlIHRvIGdvIGJhY2t3YXJkcy4gQSBkZWxldGVkIGtleSBrZWVwcyBpdHMgdGltZSB0b28sIGFzIGEgdG9tYnN0b25lIGluIGEgc2Vjb25kIG1hcCwgc28gXCJ3YXMgdGhpcyBkZWxldGVkLCBhbmQgd2hlblwiIGlzIGFzIGNoZWFwIHRvIGFzayBhcyBcImRvZXMgdGhpcyBleGlzdFwiLCBhbmQgZXhwaXJpbmcgb2xkIHRvbWJzdG9uZXMgaXMgYSB3YWxrIG9mIHRoZSB0b21ic3RvbmVzIGFsb25lIHJhdGhlciB0aGFuIG9mIGV2ZXJ5dGhpbmcuXG4vL1xuLy8gT24gZGlzayBpdCBpcyBvbmUgYXBwZW5kLW9ubHkgbG9nIG9mIHRoZSBzZXRzLCBkZWxldGVzIGFuZCBwdXJnZXMgdGhhdCBwcm9kdWNlZCBib3RoIG1hcHMsIHJld3JpdHRlbiBmcm9tIHRoZW0gd2hlbmV2ZXIgaXQgaGFzIGdyb3duIHRvIHNldmVyYWwgdGltZXMgdGhlaXIgc2l6ZS5cblxuLy8gVGhlIGxvZyBpcyByZXdyaXR0ZW4gb25jZSBpdCBob2xkcyB0aGlzIG1hbnkgdGltZXMgbW9yZSByZWNvcmRzIHRoYW4gdGhlcmUgYXJlIGtleXMgKGEgbG9nIG9mIDMgcmVjb3JkcyBwZXIgc3Vydml2aW5nIGtleSBpcyBtb3N0bHkgaGlzdG9yeSlcbmNvbnN0IENPTVBBQ1RfUkFUSU8gPSAzO1xuLy8gLi4uYnV0IG5ldmVyIGZvciBhIHNtYWxsIGxvZywgd2hlcmUgdGhlIHJld3JpdGUgd291bGQgY29zdCBtb3JlIHRoYW4gdGhlIGhpc3RvcnkgaXQgcmVtb3Zlc1xuY29uc3QgTUlOX0NPTVBBQ1RfUkVDT1JEUyA9IDEwMDA7XG4vLyBBcHBlbmRzIGFyZSBjb2FsZXNjZWQgZm9yIHRoaXMgbG9uZy4gQSBjcmFzaCBsb3NlcyBhdCBtb3N0IHRoaXMgbXVjaCAtIHRoZSBjYWxsZXJzIG9mIHRoaXMgY2xhc3MgdHJlYXQgdGhlaXIgZGF0YSBhcyByZWNvbnN0cnVjdGlibGUgLSBhbmQgdGhlIGFsdGVybmF0aXZlIGlzIGEgZmlsZSB3cml0ZSBwZXIgc2V0LlxuY29uc3QgRkxVU0hfREVMQVkgPSA1MDA7XG5cbi8qKiBBIGxpdmUgdmFsdWU6IHdoYXQgd2FzIHN0b3JlZCwgd2hlbiBpdCB3YXMgd3JpdHRlbiAodGhlIGNhbGxlcidzIG9yZGVyaW5nKSwgYW5kIHdoZW4gd2UgbGFzdCBjaGFuZ2VkIGl0IChvdXJzKS4gKi9cbmV4cG9ydCB0eXBlIExvZ0VudHJ5PFQ+ID0geyB2YWx1ZTogVDsgdGltZTogbnVtYmVyOyBjaGFuZ2VkQXQ6IG51bWJlciB9O1xuLyoqIEEgZGVsZXRlZCBrZXk6IHdoZW4gaXQgd2FzIGRlbGV0ZWQsIGFuZCB3aGVuIHdlIGxlYXJuZWQuIFdoZW4gdGhlIGtleSBoYWQgYSBsaXZlIHZhbHVlIGF0IGRlbGV0aW9uIHRpbWUgaXQgaXMgTUFSS0VEIHJhdGhlciB0aGFuIGdvbmUgLSB0aGUgdmFsdWUgKGFuZCBpdHMgb3JpZ2luYWwgd3JpdGUgdGltZSkgcmlkZXMgYWxvbmcsIHNvIHRoZSB1bmRlcmx5aW5nIGRhdGEgY2FuIHN0aWxsIGJlIHJlYWQgYW5kIHRoZSBkZWxldGlvbiBjYW4gYmUgdW5kb25lIChzZWUgdW5tYXJrKSB1bnRpbCB0aGUgaGlzdG9yeSBpcyBkcm9wcGVkIChzZWUgZHJvcFZhbHVlKS4gKi9cbmV4cG9ydCB0eXBlIExvZ1RvbWJzdG9uZTxUPiA9IHsgdGltZTogbnVtYmVyOyBjaGFuZ2VkQXQ6IG51bWJlcjsgdmFsdWU/OiBUOyB2YWx1ZVRpbWU/OiBudW1iZXIgfTtcblxuLy8gT25lIGxpbmUgb2YgdGhlIGxvZywgd2l0aCBzaW5nbGUtbGV0dGVyIGZpZWxkIG5hbWVzIGJlY2F1c2UgdGhlcmUgaXMgb25lIHJlY29yZCBwZXIgc2V0L2RlbGV0ZS9wdXJnZSBldmVyIG1hZGUgYW5kIHRoZSBmaWVsZCBuYW1lcyBhcmUgbW9zdCBvZiBhIHJlY29yZCdzIG92ZXJoZWFkLiBKU09OLnN0cmluZ2lmeSBlc2NhcGVzIG5ld2xpbmVzIGluc2lkZSBrZXlzIGFuZCB2YWx1ZXMsIHNvIGEgcmVjb3JkIGNhbiBuZXZlciBjb250YWluIHRoZSBzZXBhcmF0b3IuXG50eXBlIExvZ1JlY29yZDxUPiA9IHtcbiAgICAvLyBrZXlcbiAgICBrOiBzdHJpbmc7XG4gICAgLy8gdGltZSwgbGFzdE1vZGlmaWVkIHRpbWUgb2YgdGhlIGZpbGUsIG9yIHRoZSBkZWxldGUgdGltZSwgb3IgdGhlIHB1cmdlIHRpbWVcbiAgICB0OiBudW1iZXI7XG4gICAgLy8gdmFsdWVcbiAgICB2PzogVDtcbiAgICAvLyBkZWxldGVkLCB0aGUgZmlsZSBvbiBkaXNrIGlzIGRlbGV0ZWQsIHByZXNlcnZlZCB3aGVuIHdlIGNvbXBhY3RcbiAgICBkPzogMTtcbiAgICAvLyBtYXJrZWQgZm9yIGRlbGV0aW9uOiBkZWxldGVkLCBidXQgdGhlIHZhbHVlICh2KSBhbmQgaXRzIG9yaWdpbmFsIHdyaXRlIHRpbWUgKHcpIGFyZSBrZXB0LCBzbyB0aGUgZGF0YSBpcyBzdGlsbCByZWFkYWJsZSBhbmQgcmVzdG9yYWJsZS4gUHJlc2VydmVkIHdoZW4gd2UgY29tcGFjdC5cbiAgICBtPzogMTtcbiAgICAvLyB3cml0ZVRpbWUsIHRoZSBvcmlnaW5hbCB3cml0ZSB0aW1lIG9mIGEgbWFya2VkLWZvci1kZWxldGlvbiB2YWx1ZSAodCBpcyB0aGUgREVMRVRFIHRpbWUgb24gdGhvc2UgcmVjb3JkcylcbiAgICB3PzogbnVtYmVyO1xuICAgIC8vIHB1cmdlZCwgdGhlIGZpbGUgb24gZGlzayBpcyBkZWxldGVkLCBub3QgcHJlc2VydmVkIHdoZW4gd2UgY29tcGFjdFxuICAgIHA/OiAxO1xufTtcblxuZXhwb3J0IGNsYXNzIFRyYW5zYWN0aW9uRmlsZTxUPiB7XG4gICAgY29uc3RydWN0b3IocHJpdmF0ZSBmaWxlUGF0aDogc3RyaW5nKSB7IH1cblxuICAgIHByaXZhdGUgdmFsdWVzID0gbmV3IE1hcDxzdHJpbmcsIExvZ0VudHJ5PFQ+PigpO1xuICAgIHByaXZhdGUgZGVsZXRlZCA9IG5ldyBNYXA8c3RyaW5nLCBMb2dUb21ic3RvbmU8VD4+KCk7XG4gICAgLy8gUmVjb3JkcyB0aGUgbG9nIGhvbGRzICh3cml0dGVuIHBsdXMgcGVuZGluZykgLSB3aGF0IGNvbXBhY3Rpb24gaXMgZGVjaWRlZCBhZ2FpbnN0LCBOT1QgdGhlIG51bWJlciBvZiBrZXlzXG4gICAgcHJpdmF0ZSBsb2dSZWNvcmRzID0gMDtcbiAgICBwcml2YXRlIHBlbmRpbmc6IHN0cmluZ1tdID0gW107XG4gICAgcHJpdmF0ZSBmbHVzaFRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IHVuZGVmaW5lZDtcbiAgICAvLyBTZXJpYWxpemVzIGV2ZXJ5dGhpbmcgdGhhdCB0b3VjaGVzIHRoZSBmaWxlLCBzbyBhbiBhcHBlbmQgY2FuIG5ldmVyIGludGVybGVhdmUgd2l0aCBhIHJld3JpdGVcbiAgICBwcml2YXRlIHdyaXRlQ2hhaW46IFByb21pc2U8dm9pZD4gPSBQcm9taXNlLnJlc29sdmUoKTtcblxuICAgIC8qKiBSZWFkcyB0aGUgbG9nIGFuZCByZXBsYXlzIGl0IGludG8gbWVtb3J5LiBFdmVyeSBvdGhlciBtZXRob2QgYXNzdW1lcyB0aGlzIGhhcyBmaW5pc2hlZC4gKi9cbiAgICBwdWJsaWMgbG9hZCA9IGxhenkoYXN5bmMgKCkgPT4ge1xuICAgICAgICBsZXQgZGF0YTogc3RyaW5nO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgZGF0YSA9IGF3YWl0IGZzLnByb21pc2VzLnJlYWRGaWxlKHRoaXMuZmlsZVBhdGgsIFwidXRmOFwiKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgaWYgKChlIGFzIHsgY29kZT86IHN0cmluZyB9KS5jb2RlID09PSBcIkVOT0VOVFwiKSByZXR1cm47XG4gICAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGxldCBhcHBsaWVkID0gMDtcbiAgICAgICAgbGV0IGRhbWFnZWQgPSAwO1xuICAgICAgICBmb3IgKGxldCBsaW5lIG9mIGRhdGEuc3BsaXQoXCJcXG5cIikpIHtcbiAgICAgICAgICAgIGlmICghbGluZSkgY29udGludWU7XG4gICAgICAgICAgICBsZXQgcmVjb3JkOiBMb2dSZWNvcmQ8VD4gfCB1bmRlZmluZWQ7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHJlY29yZCA9IEpTT04ucGFyc2UobGluZSkgYXMgTG9nUmVjb3JkPFQ+O1xuICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgICAgLy8gQSBjcmFzaCBtaWQtYXBwZW5kIGxlYXZlcyBhIHRvcm4gZmluYWwgbGluZTsgYW55dGhpbmcgZWFybGllciBtZWFucyByZWFsIGRhbWFnZS4gRWl0aGVyIHdheSB0aGUgcmVzdCBvZiB0aGUgbG9nIGlzIHN0aWxsIHdvcnRoIGhhdmluZyAtIHRoaXMgaXMgYW4gaW5kZXgsIGFuZCBzY2FubmluZyByZWJ1aWxkcyB3aGF0ZXZlciB3ZSBsb3NlLlxuICAgICAgICAgICAgICAgIGRhbWFnZWQrKztcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGFwcGxpZWQrKztcbiAgICAgICAgICAgIC8vIFJlcGxheWVkIHRocm91Z2ggdGhlIHNhbWUgb3JkZXJpbmcgcnVsZXMgdGhhdCBwcm9kdWNlZCBpdCwgc28gYSBsb2cgd3JpdHRlbiBvdXQgb2Ygb3JkZXIgc3RpbGwgbGFuZHMgb24gdGhlIHNhbWUgc3RhdGVcbiAgICAgICAgICAgIGlmIChyZWNvcmQucCkge1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWVzLmRlbGV0ZShyZWNvcmQuayk7XG4gICAgICAgICAgICAgICAgdGhpcy5kZWxldGVkLmRlbGV0ZShyZWNvcmQuayk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHJlY29yZC5tKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5hcHBseURlbGV0ZShyZWNvcmQuaywgcmVjb3JkLnQsIHJlY29yZC50LCByZWNvcmQudiwgcmVjb3JkLncpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChyZWNvcmQuZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlEZWxldGUocmVjb3JkLmssIHJlY29yZC50LCByZWNvcmQudCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHJlY29yZC52ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmFwcGx5U2V0KHJlY29yZC5rLCByZWNvcmQudiwgcmVjb3JkLnQsIHJlY29yZC50KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxvZ1JlY29yZHMgPSBhcHBsaWVkO1xuICAgICAgICBpZiAoZGFtYWdlZCkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihgJHtkYW1hZ2VkfSBkYW1hZ2VkIHJlY29yZChzKSBpbiAke3RoaXMuZmlsZVBhdGh9IHdlcmUgc2tpcHBlZDsgJHthcHBsaWVkfSBhcHBsaWVkLCBsZWF2aW5nICR7dGhpcy52YWx1ZXMuc2l6ZX0gdmFsdWVzIGFuZCAke3RoaXMuZGVsZXRlZC5zaXplfSB0b21ic3RvbmVzYCk7XG4gICAgICAgIH1cbiAgICB9KTtcblxuICAgIC8qKiBUaGUgbGl2ZSB2YWx1ZSwgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIGtleSBkb2VzIG5vdCBleGlzdCBoZXJlIChkZWxldGVkIGluY2x1ZGVkIC0gYSBkZWxldGlvbiBpcyBhbiBhYnNlbmNlLCBzZWUgZ2V0RGVsZXRlZCBmb3IgaXRzIHRpbWUpLiAqL1xuICAgIHB1YmxpYyBnZXQoa2V5OiBzdHJpbmcpOiBMb2dFbnRyeTxUPiB8IHVuZGVmaW5lZCB7XG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5nZXQoa2V5KTtcbiAgICB9XG4gICAgLyoqIFdoZW4gdGhlIGtleSB3YXMgZGVsZXRlZCwgaWYgaXQgd2FzICh2YWx1ZSBpbmNsdWRlZCB3aGVuIHRoZSBkZWxldGlvbiBpcyBzdGlsbCBtYXJrZWQgLSBzZWUgTG9nVG9tYnN0b25lKS4gQWJzZW50IGJvdGggaGVyZSBhbmQgaW4gZ2V0IG1lYW5zIHdlIGhhdmUgbmV2ZXIgaGVhcmQgb2YgaXQuICovXG4gICAgcHVibGljIGdldERlbGV0ZWQoa2V5OiBzdHJpbmcpOiBMb2dUb21ic3RvbmU8VD4gfCB1bmRlZmluZWQge1xuICAgICAgICByZXR1cm4gdGhpcy5kZWxldGVkLmdldChrZXkpO1xuICAgIH1cbiAgICAvKiogVGhlIHRpbWUgdGhlIGtleSBsYXN0IGNoYW5nZWQgZWl0aGVyIHdheSwgb3IgMCBpZiB3ZSBoYXZlIG5ldmVyIGhlYXJkIG9mIGl0IC0gd2hhdCBhIG5ldyB3cml0ZSBoYXMgdG8gYmVhdC4gKi9cbiAgICBwdWJsaWMgdGltZU9mKGtleTogc3RyaW5nKTogbnVtYmVyIHtcbiAgICAgICAgbGV0IGxpdmUgPSB0aGlzLnZhbHVlcy5nZXQoa2V5KTtcbiAgICAgICAgaWYgKGxpdmUpIHJldHVybiBsaXZlLnRpbWU7XG4gICAgICAgIHJldHVybiB0aGlzLmRlbGV0ZWQuZ2V0KGtleSk/LnRpbWUgfHwgMDtcbiAgICB9XG5cbiAgICAvKiogTygxKSwgYW5kIGNvdW50cyBvbmx5IHdoYXQgZXhpc3RzLiAqL1xuICAgIHB1YmxpYyBnZXQgc2l6ZSgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy52YWx1ZXMuc2l6ZTtcbiAgICB9XG4gICAgcHVibGljIGdldCBkZWxldGVkU2l6ZSgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5kZWxldGVkLnNpemU7XG4gICAgfVxuICAgIC8qKiBMaXZlIHZhbHVlcyBvbmx5LiBMaXZlLCBpbiBpbnNlcnRpb24gb3JkZXIgLSBkZWxldGluZyBkdXJpbmcgaXRlcmF0aW9uIGlzIHNhZmUgKEpTIHNraXBzIGVudHJpZXMgcmVtb3ZlZCBiZWZvcmUgdGhleSBhcmUgcmVhY2hlZCksIHdoaWNoIGlzIHdoYXQgdGhlIHBhc3NlcyB0aGF0IHdhbGsgZXZlcnl0aGluZyBhbmQgcHJ1bmUgYXMgdGhleSBnbyByZWx5IG9uLiAqL1xuICAgIHB1YmxpYyBlbnRyaWVzKCk6IEl0ZXJhYmxlSXRlcmF0b3I8W3N0cmluZywgTG9nRW50cnk8VD5dPiB7XG4gICAgICAgIHJldHVybiB0aGlzLnZhbHVlcy5lbnRyaWVzKCk7XG4gICAgfVxuICAgIC8qKiBUaGUgdG9tYnN0b25lcywgd2hpY2ggaXMgYSBtdWNoIHNtYWxsZXIgd2FsayB0aGFuIHRoZSB2YWx1ZXMgLSBzbyBleHBpcmluZyB0aGVtLCBvciBsaXN0aW5nIHdoYXQgd2FzIGRlbGV0ZWQgc2luY2Ugc29tZSB0aW1lLCBjb3N0cyB3aGF0IGl0IHNob3VsZC4gKi9cbiAgICBwdWJsaWMgZGVsZXRlZEVudHJpZXMoKTogSXRlcmFibGVJdGVyYXRvcjxbc3RyaW5nLCBMb2dUb21ic3RvbmU8VD5dPiB7XG4gICAgICAgIHJldHVybiB0aGlzLmRlbGV0ZWQuZW50cmllcygpO1xuICAgIH1cblxuICAgIC8qKiBTdG9yZXMgYSB2YWx1ZSBhcyBvZiBgdGltZWAuIFJldHVybnMgZmFsc2Ugd2hlbiBzb21ldGhpbmcgYXQgbGVhc3QgYXMgbmV3IGlzIGFscmVhZHkgaGVyZSwgaW4gd2hpY2ggY2FzZSBub3RoaW5nIGNoYW5nZWQgLSBhbiBvdXQtb2Ytb3JkZXIgd3JpdGUgaXMgbm90IGFuIGVycm9yLCBpdCBpcyBqdXN0IGxhdGUuICovXG4gICAgcHVibGljIHNldChrZXk6IHN0cmluZywgdmFsdWU6IFQsIHRpbWU6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICBpZiAoIXRoaXMuYXBwbHlTZXQoa2V5LCB2YWx1ZSwgdGltZSwgRGF0ZS5ub3coKSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgdGhpcy5hcHBlbmQoeyBrOiBrZXksIHQ6IHRpbWUsIHY6IHZhbHVlIH0pO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAvKiogRGVsZXRlcyBhcyBvZiBgdGltZWAsIGtlZXBpbmcgdGhlIHRvbWJzdG9uZS4gQSBrZXkgdGhhdCBoYWQgYSBsaXZlIHZhbHVlIGtlZXBzIGl0IGluIHRoZSB0b21ic3RvbmUgYXMgTUFSS0VEIGZvciBkZWxldGlvbiAocmVhZGFibGUgYW5kIHJlc3RvcmFibGUgdW50aWwgZHJvcFZhbHVlKS4gUmV0dXJucyBmYWxzZSB3aGVuIHNvbWV0aGluZyBhdCBsZWFzdCBhcyBuZXcgaXMgYWxyZWFkeSBoZXJlLiAqL1xuICAgIHB1YmxpYyBkZWxldGUoa2V5OiBzdHJpbmcsIHRpbWU6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICBsZXQgbGl2ZSA9IHRoaXMudmFsdWVzLmdldChrZXkpO1xuICAgICAgICBpZiAoIXRoaXMuYXBwbHlEZWxldGUoa2V5LCB0aW1lLCBEYXRlLm5vdygpLCBsaXZlPy52YWx1ZSwgbGl2ZT8udGltZSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgaWYgKGxpdmUpIHtcbiAgICAgICAgICAgIHRoaXMuYXBwZW5kKHsgazoga2V5LCB0OiB0aW1lLCBtOiAxLCB2OiBsaXZlLnZhbHVlLCB3OiBsaXZlLnRpbWUgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmFwcGVuZCh7IGs6IGtleSwgdDogdGltZSwgZDogMSB9KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAvKiogVW5kb2VzIGEgbWFya2VkIGRlbGV0aW9uOiB0aGUga2VwdCB2YWx1ZSBiZWNvbWVzIGxpdmUgYWdhaW4sIGFzIG9mIGB0aW1lYCAoYSBmcmVzaCB0aW1lLCBzbyB0aGUgcmVzdG9yZSBvdXRyYW5rcyB0aGUgZGVsZXRpb24gZXZlcnl3aGVyZSBpdCBwcm9wYWdhdGVkKS4gUmV0dXJucyBmYWxzZSB3aGVuIHRoZXJlIGlzIG5vIG1hcmtlZCB2YWx1ZSB0byByZXN0b3JlLCBvciBzb21ldGhpbmcgYXQgbGVhc3QgYXMgbmV3IGlzIGFscmVhZHkgaGVyZS4gKi9cbiAgICBwdWJsaWMgdW5tYXJrKGtleTogc3RyaW5nLCB0aW1lOiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgbGV0IHRvbWJzdG9uZSA9IHRoaXMuZGVsZXRlZC5nZXQoa2V5KTtcbiAgICAgICAgaWYgKCF0b21ic3RvbmUgfHwgdG9tYnN0b25lLnZhbHVlID09PSB1bmRlZmluZWQpIHJldHVybiBmYWxzZTtcbiAgICAgICAgaWYgKCF0aGlzLmFwcGx5U2V0KGtleSwgdG9tYnN0b25lLnZhbHVlLCB0aW1lLCBEYXRlLm5vdygpKSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICB0aGlzLmFwcGVuZCh7IGs6IGtleSwgdDogdGltZSwgdjogdG9tYnN0b25lLnZhbHVlIH0pO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAvKiogRHJvcHMgYSBtYXJrZWQgZGVsZXRpb24ncyBrZXB0IHZhbHVlIChpdHMgaGlzdG9yeSBoYXMgYmVlbiBwaHlzaWNhbGx5IHJlbW92ZWQpLCBsZWF2aW5nIGEgcGxhaW4gdG9tYnN0b25lIHdpdGggdGhlIHNhbWUgZGVsZXRlIHRpbWUuICovXG4gICAgcHVibGljIGRyb3BWYWx1ZShrZXk6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICBsZXQgdG9tYnN0b25lID0gdGhpcy5kZWxldGVkLmdldChrZXkpO1xuICAgICAgICBpZiAoIXRvbWJzdG9uZSB8fCB0b21ic3RvbmUudmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgICAgICB0aGlzLmRlbGV0ZWQuc2V0KGtleSwgeyB0aW1lOiB0b21ic3RvbmUudGltZSwgY2hhbmdlZEF0OiBEYXRlLm5vdygpIH0pO1xuICAgICAgICB0aGlzLmFwcGVuZCh7IGs6IGtleSwgdDogdG9tYnN0b25lLnRpbWUsIGQ6IDEgfSk7XG4gICAgfVxuXG4gICAgLyoqIEZvcmdldHMgdGhlIGtleSBlbnRpcmVseSwgdG9tYnN0b25lIGluY2x1ZGVkIC0gZm9yIGEgdG9tYnN0b25lIG9sZCBlbm91Z2ggdGhhdCBub2JvZHkgbmVlZHMgdG8gaGVhciBhYm91dCB0aGUgZGVsZXRpb24gYW55IG1vcmUsIGFuZCBmb3IgYW4gZW50cnkgdGhhdCB0dXJuZWQgb3V0IG5ldmVyIHRvIGhhdmUgZXhpc3RlZC4gTm90IGEgZGVsZXRpb246IGl0IGxlYXZlcyBub3RoaW5nIGJlaGluZCB0byBwcm9wYWdhdGUuICovXG4gICAgcHVibGljIHB1cmdlKGtleTogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGxldCBoYWQgPSB0aGlzLnZhbHVlcy5kZWxldGUoa2V5KTtcbiAgICAgICAgaGFkID0gdGhpcy5kZWxldGVkLmRlbGV0ZShrZXkpIHx8IGhhZDtcbiAgICAgICAgaWYgKCFoYWQpIHJldHVybjtcbiAgICAgICAgdGhpcy5hcHBlbmQoeyBrOiBrZXksIHQ6IERhdGUubm93KCksIHA6IDEgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhcHBseVNldChrZXk6IHN0cmluZywgdmFsdWU6IFQsIHRpbWU6IG51bWJlciwgY2hhbmdlZEF0OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgaWYgKHRpbWUgPCB0aGlzLnRpbWVPZihrZXkpKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIHRoaXMuZGVsZXRlZC5kZWxldGUoa2V5KTtcbiAgICAgICAgdGhpcy52YWx1ZXMuc2V0KGtleSwgeyB2YWx1ZSwgdGltZSwgY2hhbmdlZEF0IH0pO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFwcGx5RGVsZXRlKGtleTogc3RyaW5nLCB0aW1lOiBudW1iZXIsIGNoYW5nZWRBdDogbnVtYmVyLCB2YWx1ZT86IFQsIHZhbHVlVGltZT86IG51bWJlcik6IGJvb2xlYW4ge1xuICAgICAgICBpZiAodGltZSA8IHRoaXMudGltZU9mKGtleSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgdGhpcy52YWx1ZXMuZGVsZXRlKGtleSk7XG4gICAgICAgIHRoaXMuZGVsZXRlZC5zZXQoa2V5LCB7IHRpbWUsIGNoYW5nZWRBdCwgdmFsdWUsIHZhbHVlVGltZSB9KTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhcHBlbmQocmVjb3JkOiBMb2dSZWNvcmQ8VD4pOiB2b2lkIHtcbiAgICAgICAgdGhpcy5wZW5kaW5nLnB1c2goSlNPTi5zdHJpbmdpZnkocmVjb3JkKSk7XG4gICAgICAgIHRoaXMubG9nUmVjb3JkcysrO1xuICAgICAgICB0aGlzLnNjaGVkdWxlRmx1c2goKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNjaGVkdWxlRmx1c2goKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmZsdXNoVGltZXIgIT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgICAgICB0aGlzLmZsdXNoVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuZmx1c2hUaW1lciA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIHZvaWQgdGhpcy5mbHVzaCgpLmNhdGNoKChlOiBFcnJvcikgPT4gY29uc29sZS5lcnJvcihgRmx1c2hpbmcgJHt0aGlzLmZpbGVQYXRofSBmYWlsZWQ6ICR7ZS5zdGFjayA/PyBlfWApKTtcbiAgICAgICAgfSwgRkxVU0hfREVMQVkpO1xuICAgICAgICAodGhpcy5mbHVzaFRpbWVyIGFzIHsgdW5yZWY/OiAoKSA9PiB2b2lkIH0pLnVucmVmPy4oKTtcbiAgICB9XG5cbiAgICAvKiogV3JpdGVzIGV2ZXJ5dGhpbmcgcGVuZGluZyAocmV3cml0aW5nIHRoZSBsb2cgZmlyc3QgaWYgaXQgaGFzIGdyb3duIHRvbyBmYXIgcGFzdCB3aGF0IGl0IGRlc2NyaWJlcykuICovXG4gICAgcHVibGljIGFzeW5jIGZsdXNoKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgcmVzdWx0ID0gdGhpcy53cml0ZUNoYWluLnRoZW4oKCkgPT4gdGhpcy53cml0ZSgpKTtcbiAgICAgICAgLy8gVGhlIGNoYWluIG9ubHkgZXhpc3RzIHRvIHNlcmlhbGl6ZSB3cml0ZXMsIHNvIGl0IG11c3Qgbm90IHN0YXkgcmVqZWN0ZWQ6IGEgZmFpbHVyZSBiZWxvbmdzIHRvIHRoZSBjYWxsZXIgdGhhdCBhc2tlZCBmb3IgdGhpcyBmbHVzaCwgbm90IHRvIGV2ZXJ5IGZsdXNoIGFmdGVyIGl0XG4gICAgICAgIHRoaXMud3JpdGVDaGFpbiA9IHJlc3VsdC5jYXRjaCgoKSA9PiB7IH0pO1xuICAgICAgICBhd2FpdCByZXN1bHQ7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBkaXJlY3RvcnkgPSBsYXp5KGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMubWtkaXIocGF0aC5kaXJuYW1lKHRoaXMuZmlsZVBhdGgpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICB9KTtcblxuICAgIHByaXZhdGUgYXN5bmMgd3JpdGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICh0aGlzLmxvZ1JlY29yZHMgPiBNYXRoLm1heChNSU5fQ09NUEFDVF9SRUNPUkRTLCAodGhpcy52YWx1ZXMuc2l6ZSArIHRoaXMuZGVsZXRlZC5zaXplKSAqIENPTVBBQ1RfUkFUSU8pKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmNvbXBhY3QoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB0aGlzLmFwcGVuZFBlbmRpbmcoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGFwcGVuZFBlbmRpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghdGhpcy5wZW5kaW5nLmxlbmd0aCkgcmV0dXJuO1xuICAgICAgICBsZXQgcmVjb3JkcyA9IHRoaXMucGVuZGluZztcbiAgICAgICAgdGhpcy5wZW5kaW5nID0gW107XG4gICAgICAgIGF3YWl0IHRoaXMuZGlyZWN0b3J5KCk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy5hcHBlbmRGaWxlKHRoaXMuZmlsZVBhdGgsIHJlY29yZHMuam9pbihcIlxcblwiKSArIFwiXFxuXCIpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyBQdXQgdGhlbSBiYWNrIHJhdGhlciB0aGFuIGxvc2UgdGhlbTogdGhlIHZhbHVlcyBhcmUgc3RpbGwgaW4gbWVtb3J5LCBzbyBhbGwgdGhhdCBpcyBhdCByaXNrIGlzIGhhdmluZyB0byByZWJ1aWxkIHRoZW0gYnkgc2Nhbm5pbmdcbiAgICAgICAgICAgIHRoaXMucGVuZGluZyA9IHJlY29yZHMuY29uY2F0KHRoaXMucGVuZGluZyk7XG4gICAgICAgICAgICB0aGlzLnNjaGVkdWxlRmx1c2goKTtcbiAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZXdyaXRlcyB0aGUgbG9nIGFzIHRoZSBjdXJyZW50IGNvbnRlbnRzIG9mIGJvdGggbWFwcy4gVGhleSBhcmUgdGhlIHRydXRoIC0gdGhlIGxvZyBvbmx5IGV4aXN0cyB0byByZWJ1aWxkIHRoZW0gLSBzbyB0aGUgc25hcHNob3QgSVMgdGhlIG5ldyBsb2cuXG4gICAgcHJpdmF0ZSBhc3luYyBjb21wYWN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgcmVjb3Jkczogc3RyaW5nW10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQgW2tleSwgZW50cnldIG9mIHRoaXMudmFsdWVzKSB7XG4gICAgICAgICAgICByZWNvcmRzLnB1c2goSlNPTi5zdHJpbmdpZnkoeyBrOiBrZXksIHQ6IGVudHJ5LnRpbWUsIHY6IGVudHJ5LnZhbHVlIH0gc2F0aXNmaWVzIExvZ1JlY29yZDxUPikpO1xuICAgICAgICB9XG4gICAgICAgIGZvciAobGV0IFtrZXksIHRvbWJzdG9uZV0gb2YgdGhpcy5kZWxldGVkKSB7XG4gICAgICAgICAgICBpZiAodG9tYnN0b25lLnZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICByZWNvcmRzLnB1c2goSlNPTi5zdHJpbmdpZnkoeyBrOiBrZXksIHQ6IHRvbWJzdG9uZS50aW1lLCBtOiAxLCB2OiB0b21ic3RvbmUudmFsdWUsIHc6IHRvbWJzdG9uZS52YWx1ZVRpbWUgfSBzYXRpc2ZpZXMgTG9nUmVjb3JkPFQ+KSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJlY29yZHMucHVzaChKU09OLnN0cmluZ2lmeSh7IGs6IGtleSwgdDogdG9tYnN0b25lLnRpbWUsIGQ6IDEgfSBzYXRpc2ZpZXMgTG9nUmVjb3JkPFQ+KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2xlYXJlZCB3aXRoIHRoZSBzbmFwc2hvdDogZXZlcnkgcGVuZGluZyByZWNvcmQgaXMgYWxyZWFkeSByZWZsZWN0ZWQgaW4gdGhlIG1hcHMsIHNvIHRoZSBzbmFwc2hvdCBjb250YWlucyBpdC4gQW55dGhpbmcgYXBwZW5kZWQgZnJvbSBoZXJlIG9uIGlzIE5PVCBpbiB0aGUgc25hcHNob3QsIGFuZCBsYW5kcyBpbiB0aGUgbmV3IGxvZyBhZnRlciB0aGUgcmVuYW1lLlxuICAgICAgICB0aGlzLnBlbmRpbmcgPSBbXTtcbiAgICAgICAgbGV0IHN0YXJ0ZWRXaXRoID0gdGhpcy5sb2dSZWNvcmRzO1xuICAgICAgICBsZXQgdGVtcFBhdGggPSBgJHt0aGlzLmZpbGVQYXRofS5jb21wYWN0aW5nYDtcbiAgICAgICAgYXdhaXQgdGhpcy5kaXJlY3RvcnkoKTtcbiAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMud3JpdGVGaWxlKHRlbXBQYXRoLCByZWNvcmRzLmxlbmd0aCAmJiBgJHtyZWNvcmRzLmpvaW4oXCJcXG5cIil9XFxuYCB8fCBcIlwiKTtcbiAgICAgICAgLy8gUmVwbGFjZXMgdGhlIG9sZCBsb2cgaW4gb25lIHN0ZXAsIHNvIGEgY3Jhc2ggbGVhdmVzIGVpdGhlciB0aGUgd2hvbGUgb2xkIGxvZyBvciB0aGUgd2hvbGUgbmV3IG9uZVxuICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy5yZW5hbWUodGVtcFBhdGgsIHRoaXMuZmlsZVBhdGgpO1xuICAgICAgICB0aGlzLmxvZ1JlY29yZHMgPSByZWNvcmRzLmxlbmd0aCArIHRoaXMucGVuZGluZy5sZW5ndGg7XG4gICAgICAgIGNvbnNvbGUubG9nKGBSZXdyb3RlICR7dGhpcy5maWxlUGF0aH06ICR7c3RhcnRlZFdpdGh9IGxvZyByZWNvcmRzIGZvciAke3RoaXMudmFsdWVzLnNpemV9IHZhbHVlcyBhbmQgJHt0aGlzLmRlbGV0ZWQuc2l6ZX0gdG9tYnN0b25lc2ApO1xuICAgICAgICBhd2FpdCB0aGlzLmFwcGVuZFBlbmRpbmcoKTtcbiAgICB9XG59XG4iXX0=
|
|
244
|
+
/* _JS_SOURCE_HASH = "ffe3b7e837a49b8f71ec49e14665b7b069ec8e831ce90fe9526b5a2f27182868"; */
|
|
@@ -155,7 +155,7 @@ export class ArchivesRemote implements IArchives {
|
|
|
155
155
|
return result && result.data || undefined;
|
|
156
156
|
}
|
|
157
157
|
public async get2(fileName: string, config?: GetConfig): Promise<{ data: Buffer; writeTime: number; size: number } | undefined> {
|
|
158
|
-
let result = await this.call(() => this.controller.get2({ account: this.account, bucketName: this.bucketName, path: fileName, sourceConfig: this.config.sourceConfig, range: config?.range, internal: config?.internal, includeTombstones: config?.includeTombstones }));
|
|
158
|
+
let result = await this.call(() => this.controller.get2({ account: this.account, bucketName: this.bucketName, path: fileName, sourceConfig: this.config.sourceConfig, range: config?.range, internal: config?.internal, includeTombstones: config?.includeTombstones, includeMarked: config?.includeMarked }));
|
|
159
159
|
return result && { data: Buffer.from(result.data), writeTime: result.writeTime, size: result.size } || undefined;
|
|
160
160
|
}
|
|
161
161
|
public async set(fileName: string, data: Buffer, config?: SetConfig): Promise<string> {
|
|
@@ -167,7 +167,7 @@ export class ArchivesRemote implements IArchives {
|
|
|
167
167
|
await this.setLargeFile({ path: fileName, ...config, ...bufferChunkStream(data) });
|
|
168
168
|
return fileName;
|
|
169
169
|
}
|
|
170
|
-
await this.call(() => this.controller.set({ account: this.account, bucketName: this.bucketName, path: fileName, data, sourceConfig: this.config.sourceConfig, lastModified: config?.lastModified, forceSetImmutable: config?.forceSetImmutable, internal: config?.internal }));
|
|
170
|
+
await this.call(() => this.controller.set({ account: this.account, bucketName: this.bucketName, path: fileName, data, sourceConfig: this.config.sourceConfig, lastModified: config?.lastModified, forceSetImmutable: config?.forceSetImmutable, internal: config?.internal, undelete: config?.undelete }));
|
|
171
171
|
return fileName;
|
|
172
172
|
}
|
|
173
173
|
public async del(fileName: string, config?: DelConfig): Promise<void> {
|
|
@@ -180,7 +180,7 @@ export class ArchivesRemote implements IArchives {
|
|
|
180
180
|
return await this.call(() => this.controller.getInfo({ account: this.account, bucketName: this.bucketName, path: fileName, sourceConfig: this.config.sourceConfig, includeTombstones: config?.includeTombstones }));
|
|
181
181
|
}
|
|
182
182
|
public async findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]> {
|
|
183
|
-
return await this.call(() => this.controller.findInfo({ account: this.account, bucketName: this.bucketName, prefix, sourceConfig: this.config.sourceConfig, shallow: config?.shallow, type: config?.type }));
|
|
183
|
+
return await this.call(() => this.controller.findInfo({ account: this.account, bucketName: this.bucketName, prefix, sourceConfig: this.config.sourceConfig, shallow: config?.shallow, type: config?.type, includeMarked: config?.includeMarked }));
|
|
184
184
|
}
|
|
185
185
|
public async find(prefix: string, config?: FindConfig): Promise<string[]> {
|
|
186
186
|
return (await this.findInfo(prefix, config)).map(x => x.path);
|
|
@@ -4,6 +4,9 @@ import { IArchives, ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus, Changes
|
|
|
4
4
|
import { StoreSync } from "./storeSync";
|
|
5
5
|
import { StoreConfig } from "./storeConfig";
|
|
6
6
|
export declare const WINDOW_END_FLUSH_MARGIN: number;
|
|
7
|
+
export declare const HISTORY_MIN_BYTES: number;
|
|
8
|
+
/** The multiple of a store's live bytes its deletion history may grow to. Async so it can later become dynamic and user-configurable; for now it is a constant. */
|
|
9
|
+
export declare function getHistoryFactor(): Promise<number>;
|
|
7
10
|
/** What we store about a file. Its times are not in here: the index keeps those for every key, deleted ones included (see TransactionFile). */
|
|
8
11
|
type IndexValue = {
|
|
9
12
|
size: number;
|
|
@@ -102,6 +105,7 @@ export declare class BlobStore {
|
|
|
102
105
|
};
|
|
103
106
|
internal?: boolean;
|
|
104
107
|
includeTombstones?: boolean;
|
|
108
|
+
includeMarked?: boolean;
|
|
105
109
|
}): Promise<{
|
|
106
110
|
data: Buffer;
|
|
107
111
|
writeTime: number;
|
|
@@ -113,6 +117,7 @@ export declare class BlobStore {
|
|
|
113
117
|
lastModified?: number;
|
|
114
118
|
forceSetImmutable?: boolean;
|
|
115
119
|
internal?: boolean;
|
|
120
|
+
undelete?: boolean;
|
|
116
121
|
}): Promise<void>;
|
|
117
122
|
del(config: {
|
|
118
123
|
path: string;
|
|
@@ -142,6 +147,11 @@ export declare class BlobStore {
|
|
|
142
147
|
fileCount: number;
|
|
143
148
|
byteCount: number;
|
|
144
149
|
};
|
|
150
|
+
marked: {
|
|
151
|
+
fileCount: number;
|
|
152
|
+
byteCount: number;
|
|
153
|
+
oldestDeleteTime?: number;
|
|
154
|
+
};
|
|
145
155
|
sources: {
|
|
146
156
|
debugName: string;
|
|
147
157
|
fileCount: number;
|
|
@@ -229,6 +239,24 @@ export declare class BlobStore {
|
|
|
229
239
|
writeTime: number;
|
|
230
240
|
changedAt: number;
|
|
231
241
|
}]>;
|
|
242
|
+
/** A file MARKED for deletion: its kept index value plus when it was deleted. Undefined when the key is live, never existed, or its history was already dropped. */
|
|
243
|
+
getMarkedEntry(key: string): (IndexEntry & {
|
|
244
|
+
deleteTime: number;
|
|
245
|
+
}) | undefined;
|
|
246
|
+
/** Every file marked for deletion - the deletion history, walked by retention and by includeMarked listings. */
|
|
247
|
+
markedEntries(): IterableIterator<[string, IndexEntry & {
|
|
248
|
+
deleteTime: number;
|
|
249
|
+
}]>;
|
|
250
|
+
/** The deletion history's totals: how many marked files, their bytes, and the delete time of the OLDEST one - which is how far back the history reaches. */
|
|
251
|
+
markedTotals(): {
|
|
252
|
+
fileCount: number;
|
|
253
|
+
byteCount: number;
|
|
254
|
+
oldestDeleteTime?: number;
|
|
255
|
+
};
|
|
256
|
+
/** Physically removes a marked file's bytes from our disk and drops its kept value, leaving a plain tombstone that ages out normally - retention calling time on the oldest history. */
|
|
257
|
+
dropMarkedHistory(key: string): Promise<void>;
|
|
258
|
+
/** See SetConfig.undelete: flips a marked deletion back to live (fresh write time, so the restore outranks the deletion everywhere it propagated) - the bytes never left the disk, so reads just work again. Internal restores are a peer's propagation and tolerate having nothing to restore (this node may never have held the file); a caller's restore throws instead. */
|
|
259
|
+
private undeleteKey;
|
|
232
260
|
/** How many files we hold, deletions excluded. */
|
|
233
261
|
indexSize(): number;
|
|
234
262
|
/** Totals over the files we hold, broken down by the slot holding each (entries can name a source that is no longer configured, which counts towards the total but no slot). */
|
|
@@ -22,6 +22,16 @@ export const WINDOW_END_FLUSH_MARGIN = timeInMinute * 5;
|
|
|
22
22
|
const WRONG_TARGET_LOG_THROTTLE = 60 * 1000;
|
|
23
23
|
// Marks an upload id that never reached the disk, so it can never collide with one ArchivesDisk handed out
|
|
24
24
|
const DISCARDED_UPLOAD_PREFIX = "discarded_";
|
|
25
|
+
// set refuses empty buffers, and an undelete carries no data - this single ignored byte satisfies the wire
|
|
26
|
+
const UNDELETE_PLACEHOLDER = Buffer.from([1]);
|
|
27
|
+
|
|
28
|
+
// Deletion history: deleted files keep their bytes on disk (marked in the index) until the history outgrows max(HISTORY_MIN_BYTES, live bytes * history factor) - see StoreSync.enforceHistoryLimit
|
|
29
|
+
export const HISTORY_MIN_BYTES = 10 * 1024 * 1024 * 1024;
|
|
30
|
+
const HISTORY_FACTOR = 1;
|
|
31
|
+
/** The multiple of a store's live bytes its deletion history may grow to. Async so it can later become dynamic and user-configurable; for now it is a constant. */
|
|
32
|
+
export async function getHistoryFactor(): Promise<number> {
|
|
33
|
+
return HISTORY_FACTOR;
|
|
34
|
+
}
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
/** What we store about a file. Its times are not in here: the index keeps those for every key, deleted ones included (see TransactionFile). */
|
|
@@ -274,7 +284,7 @@ export class BlobStore {
|
|
|
274
284
|
await this.index.flush();
|
|
275
285
|
}
|
|
276
286
|
|
|
277
|
-
public async get2(config: { path: string; range?: { start: number; end: number }; internal?: boolean; includeTombstones?: boolean }): Promise<{ data: Buffer; writeTime: number; size: number } | undefined> {
|
|
287
|
+
public async get2(config: { path: string; range?: { start: number; end: number }; internal?: boolean; includeTombstones?: boolean; includeMarked?: boolean }): Promise<{ data: Buffer; writeTime: number; size: number } | undefined> {
|
|
278
288
|
if (config.internal) {
|
|
279
289
|
return await this.getInternal2(config);
|
|
280
290
|
}
|
|
@@ -283,6 +293,12 @@ export class BlobStore {
|
|
|
283
293
|
let range = config.range;
|
|
284
294
|
// Not in the index means it does not exist here: the index IS the answer, and scanning heals it on its own schedule. An entry whose holder is no longer in the source list is still valid - getEntryHolder resolves the persisted URL directly, and get2's fallback loop covers a holder that is gone entirely.
|
|
285
295
|
let entry = this.getIndexEntry(key);
|
|
296
|
+
// A marked deletion still has its bytes, and the kept value says where - so an includeMarked read is a normal read through the marked entry (it just must not RE-INDEX anything: see the markedRead guards below)
|
|
297
|
+
let markedRead = false;
|
|
298
|
+
if (!entry && config.includeMarked) {
|
|
299
|
+
entry = this.getMarkedEntry(key);
|
|
300
|
+
markedRead = !!entry;
|
|
301
|
+
}
|
|
286
302
|
if (!entry) {
|
|
287
303
|
if (!config.includeTombstones) return undefined;
|
|
288
304
|
// A deletion has no bytes - the tombstone IS the answer, so a flag-caller gets its time with empty data
|
|
@@ -310,8 +326,8 @@ export class BlobStore {
|
|
|
310
326
|
}
|
|
311
327
|
}
|
|
312
328
|
if (result) {
|
|
313
|
-
// Ranged reads can't populate a cache (they're partial)
|
|
314
|
-
if (this.slotForSourcesListIndex(entry.sourcesListIndex) !== 0 && !range) {
|
|
329
|
+
// Ranged reads can't populate a cache (they're partial), and a marked read must not re-index anything - caching would resurrect the deleted key as live
|
|
330
|
+
if (!markedRead && this.slotForSourcesListIndex(entry.sourcesListIndex) !== 0 && !range) {
|
|
315
331
|
await this.cacheRead(key, result);
|
|
316
332
|
}
|
|
317
333
|
return result;
|
|
@@ -336,7 +352,10 @@ export class BlobStore {
|
|
|
336
352
|
continue;
|
|
337
353
|
}
|
|
338
354
|
if (!fallback) continue;
|
|
339
|
-
|
|
355
|
+
// A marked read must not re-index anything - caching would resurrect the deleted key as live
|
|
356
|
+
if (!markedRead) {
|
|
357
|
+
await this.cacheRead(key, fallback);
|
|
358
|
+
}
|
|
340
359
|
let data = fallback.data;
|
|
341
360
|
if (range) {
|
|
342
361
|
data = data.subarray(Math.min(range.start, data.length), Math.min(range.end, data.length));
|
|
@@ -344,12 +363,14 @@ export class BlobStore {
|
|
|
344
363
|
return { data, writeTime: fallback.writeTime, size: fallback.size };
|
|
345
364
|
}
|
|
346
365
|
if (holderError) throw holderError;
|
|
347
|
-
// The holder answered "not there" and no other source has it either: the entry was stale. Forgotten rather than deleted - nothing happened to this file, we were simply wrong about holding it, and saying otherwise would push a deletion out to everyone else.
|
|
348
|
-
|
|
366
|
+
// The holder answered "not there" and no other source has it either: the entry was stale. Forgotten rather than deleted - nothing happened to this file, we were simply wrong about holding it, and saying otherwise would push a deletion out to everyone else. (Never for a marked read: purging would erase the deletion's tombstone.)
|
|
367
|
+
if (!markedRead) {
|
|
368
|
+
this.purgeIndexEntry(key);
|
|
369
|
+
}
|
|
349
370
|
return undefined;
|
|
350
371
|
}
|
|
351
372
|
|
|
352
|
-
public async set(config: { path: string; data: Buffer; lastModified?: number; forceSetImmutable?: boolean; internal?: boolean }): Promise<void> {
|
|
373
|
+
public async set(config: { path: string; data: Buffer; lastModified?: number; forceSetImmutable?: boolean; internal?: boolean; undelete?: boolean }): Promise<void> {
|
|
353
374
|
let { path: key, data } = config;
|
|
354
375
|
if (!data.length) {
|
|
355
376
|
throw new Error(`set was called with an empty buffer for ${JSON.stringify(key)} (store ${this.folder}): an empty file IS a deletion in this system and would read back as missing - call del instead`);
|
|
@@ -363,6 +384,13 @@ export class BlobStore {
|
|
|
363
384
|
} else {
|
|
364
385
|
this.assertWriteTarget(key, route, config.lastModified);
|
|
365
386
|
}
|
|
387
|
+
if (config.undelete) {
|
|
388
|
+
if (key === ROUTING_FILE) {
|
|
389
|
+
throw new Error(`The routing config ${JSON.stringify(ROUTING_FILE)} cannot be undeleted (it cannot be deleted in the first place)`);
|
|
390
|
+
}
|
|
391
|
+
await this.undeleteKey(key, writeTime, config.internal);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
366
394
|
if (key !== ROUTING_FILE && this.storeConfig.all().length) {
|
|
367
395
|
if (config.forceSetImmutable) {
|
|
368
396
|
if (!config.lastModified) {
|
|
@@ -447,6 +475,13 @@ export class BlobStore {
|
|
|
447
475
|
if (!key.startsWith(prefix)) continue;
|
|
448
476
|
infos.set(key, { path: key, createTime: entry.writeTime, size: entry.size });
|
|
449
477
|
}
|
|
478
|
+
if (config.includeMarked) {
|
|
479
|
+
// A key is live OR marked, never both, so this cannot collide with the loop above
|
|
480
|
+
for (let [key, entry] of this.markedEntries()) {
|
|
481
|
+
if (!key.startsWith(prefix)) continue;
|
|
482
|
+
infos.set(key, { path: key, createTime: entry.writeTime, size: entry.size });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
450
485
|
let files = applyFindInfoShape(Array.from(infos.values()), prefix, { shallow: config.shallow, type: config.type });
|
|
451
486
|
sort(files, x => x.path);
|
|
452
487
|
return files;
|
|
@@ -480,6 +515,7 @@ export class BlobStore {
|
|
|
480
515
|
/** The index's totals plus any in-progress background synchronization. */
|
|
481
516
|
public getSyncProgress(): {
|
|
482
517
|
index: { fileCount: number; byteCount: number };
|
|
518
|
+
marked: { fileCount: number; byteCount: number; oldestDeleteTime?: number };
|
|
483
519
|
sources: { debugName: string; fileCount: number; byteCount: number }[];
|
|
484
520
|
readerDiskLimit?: number;
|
|
485
521
|
syncing: SyncActivity[];
|
|
@@ -487,6 +523,7 @@ export class BlobStore {
|
|
|
487
523
|
let totals = this.namedIndexTotals();
|
|
488
524
|
return {
|
|
489
525
|
index: { fileCount: totals.fileCount, byteCount: totals.byteCount },
|
|
526
|
+
marked: this.markedTotals(),
|
|
490
527
|
sources: totals.sources,
|
|
491
528
|
readerDiskLimit: this.readerDiskLimit,
|
|
492
529
|
syncing: this.sync.getActivities(),
|
|
@@ -762,6 +799,71 @@ export class BlobStore {
|
|
|
762
799
|
}
|
|
763
800
|
}
|
|
764
801
|
|
|
802
|
+
/** A file MARKED for deletion: its kept index value plus when it was deleted. Undefined when the key is live, never existed, or its history was already dropped. */
|
|
803
|
+
public getMarkedEntry(key: string): (IndexEntry & { deleteTime: number }) | undefined {
|
|
804
|
+
let tombstone = this.index.getDeleted(key);
|
|
805
|
+
if (!tombstone || tombstone.value === undefined) return undefined;
|
|
806
|
+
return { ...tombstone.value, writeTime: tombstone.valueTime || tombstone.time, changedAt: tombstone.changedAt, deleteTime: tombstone.time };
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/** Every file marked for deletion - the deletion history, walked by retention and by includeMarked listings. */
|
|
810
|
+
public *markedEntries(): IterableIterator<[string, IndexEntry & { deleteTime: number }]> {
|
|
811
|
+
for (let [key, tombstone] of this.index.deletedEntries()) {
|
|
812
|
+
if (tombstone.value === undefined) continue;
|
|
813
|
+
yield [key, { ...tombstone.value, writeTime: tombstone.valueTime || tombstone.time, changedAt: tombstone.changedAt, deleteTime: tombstone.time }];
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/** The deletion history's totals: how many marked files, their bytes, and the delete time of the OLDEST one - which is how far back the history reaches. */
|
|
818
|
+
public markedTotals(): { fileCount: number; byteCount: number; oldestDeleteTime?: number } {
|
|
819
|
+
let fileCount = 0;
|
|
820
|
+
let byteCount = 0;
|
|
821
|
+
let oldestDeleteTime: number | undefined;
|
|
822
|
+
for (let [, entry] of this.markedEntries()) {
|
|
823
|
+
fileCount++;
|
|
824
|
+
byteCount += entry.size;
|
|
825
|
+
if (oldestDeleteTime === undefined || entry.deleteTime < oldestDeleteTime) {
|
|
826
|
+
oldestDeleteTime = entry.deleteTime;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return { fileCount, byteCount, oldestDeleteTime };
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/** Physically removes a marked file's bytes from our disk and drops its kept value, leaving a plain tombstone that ages out normally - retention calling time on the oldest history. */
|
|
833
|
+
public async dropMarkedHistory(key: string): Promise<void> {
|
|
834
|
+
await this.ownDisk.del(key);
|
|
835
|
+
this.index.dropValue(key);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/** See SetConfig.undelete: flips a marked deletion back to live (fresh write time, so the restore outranks the deletion everywhere it propagated) - the bytes never left the disk, so reads just work again. Internal restores are a peer's propagation and tolerate having nothing to restore (this node may never have held the file); a caller's restore throws instead. */
|
|
839
|
+
private async undeleteKey(key: string, writeTime: number, internal: boolean | undefined): Promise<void> {
|
|
840
|
+
let marked = this.getMarkedEntry(key);
|
|
841
|
+
if (!this.index.unmark(key, writeTime)) {
|
|
842
|
+
// Already live: undelete is idempotent (retries and peer propagation both re-send it)
|
|
843
|
+
if (this.getIndexEntry(key)) return;
|
|
844
|
+
if (internal) {
|
|
845
|
+
console.log(`Undelete of ${JSON.stringify(key)} (store ${this.folder}) has nothing to restore here - this node may never have held the file`);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
throw new Error(`Cannot undelete ${JSON.stringify(key)} (store ${this.folder}): it has no marked deletion to restore - it was never deleted here${this.getDeletedEntry(key) && ", or its deletion history was already dropped (a plain tombstone remains, but the bytes are gone)" || ""}`);
|
|
849
|
+
}
|
|
850
|
+
console.log(`Undeleted ${JSON.stringify(key)} (store ${this.folder}): the index entry (${marked?.size} bytes, deleted ${marked && new Date(marked.deleteTime).toISOString()}) is live again as of ${new Date(writeTime).toISOString()} - the bytes never left the disk`);
|
|
851
|
+
this.config?.onIndexChanged?.(key);
|
|
852
|
+
if (internal) return;
|
|
853
|
+
// Peers marked their own copies when the deletion propagated, so the restore propagates the same way. Straight past any write delay - a buffered undelete could be read back as its placeholder byte.
|
|
854
|
+
let route = getRoute(key);
|
|
855
|
+
for (let i of this.getWritableSources()) {
|
|
856
|
+
if (i === 0) continue;
|
|
857
|
+
if (!routeContains(this.sources[i].route, route)) continue;
|
|
858
|
+
// Only our own servers understand the flag - a raw source (backblaze) would store the placeholder byte as the file. Their older copy is re-pushed by reconciliation instead.
|
|
859
|
+
if (this.sources[i].sourceConfig?.type !== "remote") continue;
|
|
860
|
+
let push = unwrapDelayed(this.sources[i].source).set(key, UNDELETE_PLACEHOLDER, { lastModified: writeTime, undelete: true, noChecks: true, internal: true });
|
|
861
|
+
void push.catch((e: Error) => {
|
|
862
|
+
console.error(`Background undelete of ${key} on sync source ${this.sources[i].source.getDebugName()} failed (its scan of us re-finds the file anyway): ${e.stack ?? e}`);
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
765
867
|
/** How many files we hold, deletions excluded. */
|
|
766
868
|
public indexSize(): number {
|
|
767
869
|
return this.index.size;
|
|
@@ -922,8 +1024,7 @@ export class BlobStore {
|
|
|
922
1024
|
assertValidLastModified(config.lastModified);
|
|
923
1025
|
if (config.lastModified < await this.currentWriteTime(key)) return;
|
|
924
1026
|
if (data.length === 0) {
|
|
925
|
-
//
|
|
926
|
-
await this.sources[0].source.del(key);
|
|
1027
|
+
// The bytes stay on our disk as deletion history (see writeToSources) - the marked index entry is the whole of the deletion
|
|
927
1028
|
this.setIndexDeleted(key, config.lastModified);
|
|
928
1029
|
return;
|
|
929
1030
|
}
|
|
@@ -935,6 +1036,8 @@ export class BlobStore {
|
|
|
935
1036
|
private async cacheRead(key: string, result: { data: Buffer; writeTime: number }): Promise<void> {
|
|
936
1037
|
await this.sources[0].source.set(key, result.data, { lastModified: result.writeTime, forceSetImmutable: true, noChecks: true });
|
|
937
1038
|
this.setIndexEntry(key, { writeTime: result.writeTime, size: result.data.length, sourcesListIndex: this.sourcesListIndexOfSlot(0) });
|
|
1039
|
+
// A down-cache pulls the bytes off a source exactly like synchronization does, so it counts (and logs) the same way
|
|
1040
|
+
this.noteSyncTransfer("sync get", key, result.data.length);
|
|
938
1041
|
}
|
|
939
1042
|
|
|
940
1043
|
// The shared engine of set and del: an empty buffer is exactly a deletion here, which is why the empty-buffer rejection lives in set (the public API), not in this machinery
|
|
@@ -985,8 +1088,7 @@ export class BlobStore {
|
|
|
985
1088
|
}
|
|
986
1089
|
// Only our own (first) source blocks the write. Downstream sources are written in the background: a down downstream source must not fail or stall writes, and reconciliation re-sends anything they missed once they come back.
|
|
987
1090
|
if (data.length === 0) {
|
|
988
|
-
//
|
|
989
|
-
await this.sources[first].source.del(key);
|
|
1091
|
+
// The bytes STAY on our disk, as deletion history: the index marks the key deleted (keeping its value - see TransactionFile.delete), reads stop finding it, and the retention pass physically removes the oldest history once it outgrows its budget (see StoreSync.enforceHistoryLimit). The marked index entry is the whole of the local deletion.
|
|
990
1092
|
this.setIndexDeleted(key, writeTime);
|
|
991
1093
|
} else {
|
|
992
1094
|
await this.sources[first].source.set(key, data, { lastModified: writeTime, noChecks: true });
|
|
@@ -994,6 +1096,10 @@ export class BlobStore {
|
|
|
994
1096
|
}
|
|
995
1097
|
if (isRouting) return;
|
|
996
1098
|
let route = getRoute(key);
|
|
1099
|
+
// Deletions did not consume the first slot (nothing is written anywhere locally), so it receives the propagated deletion like the rest - except slot 0, our own disk, whose bytes ARE the history being kept
|
|
1100
|
+
if (data.length === 0 && first !== 0) {
|
|
1101
|
+
writable.unshift(first);
|
|
1102
|
+
}
|
|
997
1103
|
for (let i of writable) {
|
|
998
1104
|
if (!routeContains(this.sources[i].route, route)) continue;
|
|
999
1105
|
// Deletions travel as del carrying the original write time (never as empty sets - set rejects empty buffers). Backblaze materializes such dels as real empty files, so its listings still show the deletion for other stores to scan in as a tombstone.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
|
-
import { IArchives, RemoteConfig, RemoteConfigBase, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, MoveFileConfig, SetConfig, SetLargeFileConfig } from "../IArchives";
|
|
3
|
+
import { IArchives, RemoteConfig, RemoteConfigBase, SourceConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, MoveFileConfig, SetConfig, SetLargeFileConfig } from "../IArchives";
|
|
4
4
|
import { ServerBucketInfo, ActiveBucketInfo } from "./storageServerState";
|
|
5
|
+
import { LogFileInfo } from "../StreamingLogs";
|
|
5
6
|
/** The address, port, account, and bucket name a bucket routing URL addresses. Throws when the URL isn't a hosted bucket routing URL (https://host:port/file/<account>/<bucketName>/storage/storagerouting.json). */
|
|
6
7
|
export { parseHostedUrl, parseBackblazeUrl, getBucketBaseUrl } from "./remoteConfig";
|
|
7
8
|
/** A client for ONE source - see storeSources.ts. Re-exported here because a chain is built out of them. */
|
|
@@ -26,6 +27,9 @@ export declare class ArchivesChain implements IArchives {
|
|
|
26
27
|
machineId: string;
|
|
27
28
|
ip: string;
|
|
28
29
|
} | undefined>;
|
|
30
|
+
/** The sources that can serve a file right now, in dispatch order - the first is the write node, the one a plain read asks first. Each entry's url is what GetConfig.sourceUrl / GetInfoConfig.sourceUrl accept, so listing these and then reading with sourceUrl compares the copies the sources actually hold. */
|
|
31
|
+
getFileSources(fileName: string): Promise<SourceConfig[]>;
|
|
32
|
+
private runOnSource;
|
|
29
33
|
get(fileName: string, config?: GetConfig): Promise<Buffer | undefined>;
|
|
30
34
|
/** get2, but trying sources in latency order (fastest first) instead of config order. While this is much faster, it might miss immediate writes: the write node is no longer tried first, so a lagging replica may answer with a slightly older value. Exclusive with noFallbacks (which only considers one source - the write node - so there is no order to speed up); passing both throws. */
|
|
31
35
|
getFast(fileName: string, config?: GetConfig): Promise<{
|
|
@@ -67,6 +71,8 @@ export declare class ArchivesChain implements IArchives {
|
|
|
67
71
|
set(fileName: string, data: Buffer, config?: SetConfig): Promise<string>;
|
|
68
72
|
private setRoutingConfig;
|
|
69
73
|
del(fileName: string, config?: DelConfig): Promise<void>;
|
|
74
|
+
/** See IArchives.undelete: restores a file marked for deletion, dispatched to the write node as SetConfig.undelete (the write node propagates the restore to its peers itself). */
|
|
75
|
+
undelete(fileName: string): Promise<void>;
|
|
70
76
|
/** See IArchives.move. When one node is the write target for BOTH paths, that node moves the file itself - the bytes never come through us - with the same wrong-window/route re-resolution as any write. When the paths route to different shards no single node holds both, so the move degrades to a copy through us plus a delete, CONFIRMED at the destination before the source is touched. No smart timeout on the node-side move: it can be a big file's worth of node-side work, which the upload-sized deadlines would misjudge. */
|
|
71
77
|
move(config: MoveFileConfig): Promise<void>;
|
|
72
78
|
private getVariableShardTargets;
|
|
@@ -117,6 +123,39 @@ export declare function clearServerWriteStats(config: {
|
|
|
117
123
|
}): Promise<{
|
|
118
124
|
clearedBuckets: number;
|
|
119
125
|
}>;
|
|
126
|
+
/** The operation-log files ONE storage server holds (every server logs only its own operations - see listAllServerLogFiles for the whole fleet). The names carry pid/thread/time-range/entry-count metadata; see LogFileInfo. */
|
|
127
|
+
export declare function listServerLogFiles(config: {
|
|
128
|
+
url: string;
|
|
129
|
+
account: string;
|
|
130
|
+
}): Promise<LogFileInfo[]>;
|
|
131
|
+
/** Every server's log files at once, one entry per url - a server that cannot answer reports its error instead of failing the rest. */
|
|
132
|
+
export declare function listAllServerLogFiles(config: {
|
|
133
|
+
urls: string[];
|
|
134
|
+
account: string;
|
|
135
|
+
}): Promise<{
|
|
136
|
+
url: string;
|
|
137
|
+
files?: LogFileInfo[];
|
|
138
|
+
error?: string;
|
|
139
|
+
}[]>;
|
|
140
|
+
/** Downloads the named log files off one server and decodes them into the logged objects (the wire always carries them LZ4-compressed - live files are compressed in memory server-side). */
|
|
141
|
+
export declare function getServerLogs(config: {
|
|
142
|
+
url: string;
|
|
143
|
+
account: string;
|
|
144
|
+
names: string[];
|
|
145
|
+
}): Promise<{
|
|
146
|
+
name: string;
|
|
147
|
+
entries: unknown[];
|
|
148
|
+
}[]>;
|
|
149
|
+
/** getServerLogs, but SEARCHED instead of fully decoded: the raw JSON text is substring-matched (every search string must appear in a statement - see createLogSearcher), and only the matching statements are decoded into objects. Far cheaper than decoding whole files to look for one path or caller. */
|
|
150
|
+
export declare function searchServerLogs(config: {
|
|
151
|
+
url: string;
|
|
152
|
+
account: string;
|
|
153
|
+
names: string[];
|
|
154
|
+
searches: string[];
|
|
155
|
+
}): Promise<{
|
|
156
|
+
name: string;
|
|
157
|
+
entries: unknown[];
|
|
158
|
+
}[]>;
|
|
120
159
|
export declare function getBucketInfo(config: {
|
|
121
160
|
url: string;
|
|
122
161
|
}): Promise<ArchivesConfig>;
|