sliftutils 1.7.10 → 1.7.12
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/.claude/settings.local.json +2 -1
- package/examplestorage/exampleserver.ts +0 -2
- package/index.d.ts +301 -54
- 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 -12
- package/misc/https/dns.ts +104 -101
- package/misc/https/hostServer.d.ts +0 -6
- package/misc/https/hostServer.ts +9 -5
- package/package.json +2 -2
- package/render-utils/dist/observer.tsx.cache +3 -2
- package/spec.txt +0 -90
- package/storage/ArchivesDisk.d.ts +2 -0
- package/storage/ArchivesDisk.ts +7 -3
- 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 +47 -15
- package/storage/IArchives.ts +86 -31
- package/storage/backblaze.d.ts +15 -0
- package/storage/backblaze.ts +199 -79
- 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 +5 -1
- package/storage/remoteStorage/ArchivesRemote.ts +28 -8
- package/storage/remoteStorage/ArchivesUrl.d.ts +2 -0
- package/storage/remoteStorage/ArchivesUrl.ts +16 -4
- package/storage/remoteStorage/blobStore.d.ts +63 -3
- package/storage/remoteStorage/blobStore.ts +419 -101
- package/storage/remoteStorage/createArchives.d.ts +31 -10
- package/storage/remoteStorage/createArchives.ts +470 -253
- 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/remoteConfig.d.ts +8 -0
- package/storage/remoteStorage/remoteConfig.ts +105 -5
- 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 +11 -0
- package/storage/remoteStorage/storageController.ts +109 -40
- package/storage/remoteStorage/storageServer.d.ts +0 -5
- package/storage/remoteStorage/storageServer.ts +17 -10
- package/storage/remoteStorage/storageServerCli.ts +0 -3
- package/storage/remoteStorage/storageServerState.d.ts +7 -1
- package/storage/remoteStorage/storageServerState.ts +230 -48
- package/testsite/server.ts +0 -2
- package/yarn.lock +11 -6
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
3
|
+
//exports.RemoteStorageController = exports.STORAGE_ACCESS_DENIED = exports.STORAGE_NOT_AUTHENTICATED = exports.STORAGE_AUTH_PURPOSE = exports.REMOTE_STORAGE_CLASS_GUID = void 0;
|
|
4
|
+
module.allowclient = true;
|
|
5
|
+
const SocketFunction_1 = require("socket-function/SocketFunction");
|
|
6
|
+
const nodeCache_1 = require("socket-function/src/nodeCache");
|
|
7
|
+
const callHTTPHandler_1 = require("socket-function/src/callHTTPHandler");
|
|
8
|
+
const callManager_1 = require("socket-function/src/callManager");
|
|
9
|
+
const RequireController_1 = require("socket-function/require/RequireController");
|
|
10
|
+
const misc_1 = require("socket-function/src/misc");
|
|
11
|
+
const certs_1 = require("../../misc/https/certs");
|
|
12
|
+
const IArchives_1 = require("../IArchives");
|
|
13
|
+
const remoteConfig_1 = require("./remoteConfig");
|
|
14
|
+
const storageServerState_1 = require("./storageServerState");
|
|
15
|
+
// The remote storage server's API. Authentication uses certs.ts machine identities: a client
|
|
16
|
+
// proves it owns its machine key by signing a timestamped token (bound to this server, so tokens
|
|
17
|
+
// can't be replayed elsewhere), and the server then trusts that connection as that machineId.
|
|
18
|
+
// Access to an account is granted to specific machineIds, via a command line command run on the
|
|
19
|
+
// storage machine (see storageServer.ts).
|
|
20
|
+
//
|
|
21
|
+
// There is no bucket-creation API: a bucket exists iff its routing config (ROUTING_FILE) exists,
|
|
22
|
+
// and writing that file creates or reconfigures the bucket (see storageServerState.ts). Reads of
|
|
23
|
+
// nonexistent buckets return undefined / empty, same as reads of nonexistent files.
|
|
24
|
+
exports.REMOTE_STORAGE_CLASS_GUID = "RemoteStorageController-b7e42a91";
|
|
25
|
+
exports.STORAGE_AUTH_PURPOSE = "remoteStorage-auth-1";
|
|
26
|
+
// Error markers, so clients can identify these failures inside error messages
|
|
27
|
+
exports.STORAGE_NOT_AUTHENTICATED = "REMOTE_STORAGE_NOT_AUTHENTICATED_cf2f7b1e";
|
|
28
|
+
exports.STORAGE_ACCESS_DENIED = "REMOTE_STORAGE_ACCESS_DENIED_9d81a4c0";
|
|
29
|
+
const AUTH_TIME_WINDOW = misc_1.timeInMinute * 10;
|
|
30
|
+
const MAX_SESSIONS = 100 * 1000;
|
|
31
|
+
const MAX_REQUESTS_PER_IP = 50;
|
|
32
|
+
// callerNodeId -> authenticated machineId. Connections are long-lived websockets, so a session
|
|
33
|
+
// lasts until the connection drops (clients re-authenticate on reconnect).
|
|
34
|
+
const sessions = new Map();
|
|
35
|
+
const CONTENT_TYPES = {
|
|
36
|
+
html: "text/html", js: "text/javascript", css: "text/css", json: "application/json",
|
|
37
|
+
txt: "text/plain", png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif",
|
|
38
|
+
svg: "image/svg+xml", webp: "image/webp", mp4: "video/mp4", webm: "video/webm",
|
|
39
|
+
mp3: "audio/mpeg", wav: "audio/wav", pdf: "application/pdf",
|
|
40
|
+
};
|
|
41
|
+
function assertValidName(value, kind) {
|
|
42
|
+
if (!/^[\w-]{1,64}$/.test(value)) {
|
|
43
|
+
throw new Error(`Invalid ${kind} ${JSON.stringify(value)}, expected 1-64 characters of letters/numbers/underscore/dash`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function assertValidPath(path) {
|
|
47
|
+
if (Buffer.from(path, "utf8").length > 1000) {
|
|
48
|
+
throw new Error(`Path too long: ${path.length} characters > 1000. Path: ${path.slice(0, 200)}`);
|
|
49
|
+
}
|
|
50
|
+
if (!path || path.startsWith("/") || path.endsWith("/") || path.includes("//") || path.includes("\\") || path.includes("\x00")) {
|
|
51
|
+
throw new Error(`Invalid path ${JSON.stringify(path.slice(0, 200))}, paths cannot be empty, start or end with /, or contain //, backslashes, or null characters`);
|
|
52
|
+
}
|
|
53
|
+
// Paths are one-to-one with files on disk, so . and .. segments would escape the store folder
|
|
54
|
+
if (path.split("/").some(part => part === "." || part === "..")) {
|
|
55
|
+
throw new Error(`Invalid path ${JSON.stringify(path.slice(0, 200))}, paths cannot contain . or .. segments`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getCallerMachineId() {
|
|
59
|
+
let caller = SocketFunction_1.SocketFunction.getCaller();
|
|
60
|
+
let machineId = sessions.get(caller.nodeId);
|
|
61
|
+
if (!machineId) {
|
|
62
|
+
throw new Error(`${exports.STORAGE_NOT_AUTHENTICATED} Call authenticate first (connection ${caller.nodeId})`);
|
|
63
|
+
}
|
|
64
|
+
return machineId;
|
|
65
|
+
}
|
|
66
|
+
function getCallerIP() {
|
|
67
|
+
return (0, nodeCache_1.getNodeIdIP)(SocketFunction_1.SocketFunction.getCaller().nodeId);
|
|
68
|
+
}
|
|
69
|
+
function isAdmin(machineId) {
|
|
70
|
+
return machineId === (0, certs_1.getOwnMachineId)((0, storageServerState_1.getStorageServerConfig)().rootDomain);
|
|
71
|
+
}
|
|
72
|
+
function requireAdmin() {
|
|
73
|
+
let machineId = getCallerMachineId();
|
|
74
|
+
if (!isAdmin(machineId)) {
|
|
75
|
+
throw new Error(`${exports.STORAGE_ACCESS_DENIED} Admin commands must be run from the storage machine itself (caller machine ${machineId})`);
|
|
76
|
+
}
|
|
77
|
+
return machineId;
|
|
78
|
+
}
|
|
79
|
+
async function requireAccess(account) {
|
|
80
|
+
assertValidName(account, "account");
|
|
81
|
+
let machineId = getCallerMachineId();
|
|
82
|
+
if (isAdmin(machineId))
|
|
83
|
+
return machineId;
|
|
84
|
+
let trust = await (0, storageServerState_1.getTrust)();
|
|
85
|
+
let trusted = await trust.get(`${account}|${machineId}`);
|
|
86
|
+
if (!trusted) {
|
|
87
|
+
let { domain, port } = (0, storageServerState_1.getStorageServerConfig)();
|
|
88
|
+
throw new Error(`${exports.STORAGE_ACCESS_DENIED} Machine ${machineId} has no access to account ${JSON.stringify(account)}. Visit https://${domain}:${port}/${account} for access instructions.`);
|
|
89
|
+
}
|
|
90
|
+
return machineId;
|
|
91
|
+
}
|
|
92
|
+
// A single command, runnable from anywhere, that sshes into the storage machine and runs the
|
|
93
|
+
// grantAccess CLI there
|
|
94
|
+
function getGrantAccessCommand(requestId) {
|
|
95
|
+
let { sshTarget, serverCommand } = (0, storageServerState_1.getStorageServerConfig)();
|
|
96
|
+
return `ssh ${sshTarget} '${serverCommand} --requestId ${requestId}'`;
|
|
97
|
+
}
|
|
98
|
+
async function getBucket(account, bucketName) {
|
|
99
|
+
assertValidName(account, "account");
|
|
100
|
+
assertValidName(bucketName, "bucket name");
|
|
101
|
+
return await (0, storageServerState_1.getLoadedBucket)(account, bucketName);
|
|
102
|
+
}
|
|
103
|
+
class RemoteStorageControllerBase {
|
|
104
|
+
// Latency measurement (see SourceWrapper's pinging); no auth, it measures the transport
|
|
105
|
+
async ping() { }
|
|
106
|
+
// Proves the caller owns the machine key for the machineId in its certificate. The signature
|
|
107
|
+
// must be fresh and bound to this server, so it cannot be replayed to (or from) other servers.
|
|
108
|
+
async authenticate(token) {
|
|
109
|
+
let { domain, port } = (0, storageServerState_1.getStorageServerConfig)();
|
|
110
|
+
let caller = SocketFunction_1.SocketFunction.getCaller();
|
|
111
|
+
if (Math.abs(Date.now() - token.time) > AUTH_TIME_WINDOW) {
|
|
112
|
+
throw new Error(`Auth token time is too far from the server time (token ${token.time}, server ${Date.now()}, allowed drift ${AUTH_TIME_WINDOW}ms)`);
|
|
113
|
+
}
|
|
114
|
+
(0, certs_1.verify)(token.certPem, token.signature, {
|
|
115
|
+
purpose: exports.STORAGE_AUTH_PURPOSE,
|
|
116
|
+
time: token.time,
|
|
117
|
+
server: `${domain}:${port}`,
|
|
118
|
+
});
|
|
119
|
+
let machineId = (0, certs_1.getCommonName)(token.certPem).split(".")[0];
|
|
120
|
+
if (!(0, certs_1.verifyMachineIdForPublicKey)({ machineId, publicKey: (0, certs_1.getPublicIdentifier)(token.certPem) })) {
|
|
121
|
+
throw new Error(`Certificate common name ${JSON.stringify((0, certs_1.getCommonName)(token.certPem))} does not match its public key`);
|
|
122
|
+
}
|
|
123
|
+
sessions.set(caller.nodeId, machineId);
|
|
124
|
+
while (sessions.size > MAX_SESSIONS) {
|
|
125
|
+
let oldest = sessions.keys().next().value;
|
|
126
|
+
if (oldest === undefined)
|
|
127
|
+
break;
|
|
128
|
+
sessions.delete(oldest);
|
|
129
|
+
}
|
|
130
|
+
return { machineId, ip: getCallerIP() };
|
|
131
|
+
}
|
|
132
|
+
// Records that the calling machine wants access to an account. Requests are kept per requesting
|
|
133
|
+
// IP, so the storage machine's admin can list them with --listAccess <ip> and grant one.
|
|
134
|
+
async requestAccess(account) {
|
|
135
|
+
assertValidName(account, "account");
|
|
136
|
+
let machineId = getCallerMachineId();
|
|
137
|
+
let ip = getCallerIP();
|
|
138
|
+
let requestsStorage = await (0, storageServerState_1.getRequests)();
|
|
139
|
+
let requests = await requestsStorage.get(ip) || [];
|
|
140
|
+
let existing = requests.find(x => x.account === account && x.machineId === machineId);
|
|
141
|
+
if (existing) {
|
|
142
|
+
existing.time = Date.now();
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
existing = {
|
|
146
|
+
requestId: Math.random().toString(36).slice(2, 10),
|
|
147
|
+
account,
|
|
148
|
+
machineId,
|
|
149
|
+
ip,
|
|
150
|
+
time: Date.now(),
|
|
151
|
+
};
|
|
152
|
+
requests.push(existing);
|
|
153
|
+
}
|
|
154
|
+
while (requests.length > MAX_REQUESTS_PER_IP)
|
|
155
|
+
requests.shift();
|
|
156
|
+
await requestsStorage.set(ip, requests);
|
|
157
|
+
return { machineId, ip, requestId: existing.requestId, grantAccessCommand: getGrantAccessCommand(existing.requestId) };
|
|
158
|
+
}
|
|
159
|
+
async getAccessState(account) {
|
|
160
|
+
assertValidName(account, "account");
|
|
161
|
+
let machineId = getCallerMachineId();
|
|
162
|
+
let ip = getCallerIP();
|
|
163
|
+
let trust = await (0, storageServerState_1.getTrust)();
|
|
164
|
+
let hasAccess = isAdmin(machineId) || !!await trust.get(`${account}|${machineId}`);
|
|
165
|
+
let result = { machineId, ip, hasAccess };
|
|
166
|
+
if (!hasAccess) {
|
|
167
|
+
let requests = await (0, storageServerState_1.getRequests)();
|
|
168
|
+
let ownRequest = (await requests.get(ip) || []).find(x => x.account === account && x.machineId === machineId);
|
|
169
|
+
if (ownRequest) {
|
|
170
|
+
result.grantAccessCommand = getGrantAccessCommand(ownRequest.requestId);
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
let trustedMachines = [];
|
|
175
|
+
for (let key of await trust.getKeys()) {
|
|
176
|
+
if (!key.startsWith(`${account}|`))
|
|
177
|
+
continue;
|
|
178
|
+
let record = await trust.get(key);
|
|
179
|
+
if (record)
|
|
180
|
+
trustedMachines.push(record);
|
|
181
|
+
}
|
|
182
|
+
result.trustedMachines = trustedMachines;
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
// Callable by any machine that has access to `account`. Returns pending access requests for the
|
|
186
|
+
// account that come from EXACTLY `ip`. Callers must type in an IP explicitly — the server never
|
|
187
|
+
// volunteers a list of requesting IPs, so a trusted user can't accidentally approve a random
|
|
188
|
+
// request from a machine they didn't mean to trust.
|
|
189
|
+
async listRequestsForIP(account, ip) {
|
|
190
|
+
let requests = await (0, storageServerState_1.getRequests)();
|
|
191
|
+
return (await requests.get(ip) || []).filter(x => x.account === account);
|
|
192
|
+
}
|
|
193
|
+
// Callable by any machine that has access to the request's account (or by the storage-machine
|
|
194
|
+
// admin). Grants the requested access; the caller must supply the specific requestId, which they
|
|
195
|
+
// only get by explicitly looking up requests for a specific IP.
|
|
196
|
+
async grantAccess(requestId) {
|
|
197
|
+
// Must capture in the synchronous phase — SocketFunction.getCaller() only works before any await.
|
|
198
|
+
let callerMachineId = getCallerMachineId();
|
|
199
|
+
let trust = await (0, storageServerState_1.getTrust)();
|
|
200
|
+
let requests = await (0, storageServerState_1.getRequests)();
|
|
201
|
+
for (let ip of await requests.getKeys()) {
|
|
202
|
+
for (let request of await requests.get(ip) || []) {
|
|
203
|
+
if (request.requestId !== requestId)
|
|
204
|
+
continue;
|
|
205
|
+
if (!isAdmin(callerMachineId) && !await trust.get(`${request.account}|${callerMachineId}`)) {
|
|
206
|
+
throw new Error(`${exports.STORAGE_ACCESS_DENIED} Machine ${callerMachineId} has no access to account ${JSON.stringify(request.account)}`);
|
|
207
|
+
}
|
|
208
|
+
let record = {
|
|
209
|
+
account: request.account,
|
|
210
|
+
machineId: request.machineId,
|
|
211
|
+
ip: request.ip,
|
|
212
|
+
time: Date.now(),
|
|
213
|
+
};
|
|
214
|
+
await trust.set(`${request.account}|${request.machineId}`, record);
|
|
215
|
+
return record;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
throw new Error(`No access request found with id ${JSON.stringify(requestId)}. It may have already been granted or expired.`);
|
|
219
|
+
}
|
|
220
|
+
// Admin (must be run from the storage machine itself, which shares the server's machineId).
|
|
221
|
+
// Only returns requests for the given IP, so you cannot accidentally grant a request from an
|
|
222
|
+
// IP you didn't explicitly type in.
|
|
223
|
+
async adminListRequests(ip) {
|
|
224
|
+
let requests = await (0, storageServerState_1.getRequests)();
|
|
225
|
+
return await requests.get(ip) || [];
|
|
226
|
+
}
|
|
227
|
+
async adminGrantAccess(requestId) {
|
|
228
|
+
let trust = await (0, storageServerState_1.getTrust)();
|
|
229
|
+
let requests = await (0, storageServerState_1.getRequests)();
|
|
230
|
+
for (let ip of await requests.getKeys()) {
|
|
231
|
+
for (let request of await requests.get(ip) || []) {
|
|
232
|
+
if (request.requestId !== requestId)
|
|
233
|
+
continue;
|
|
234
|
+
let record = {
|
|
235
|
+
account: request.account,
|
|
236
|
+
machineId: request.machineId,
|
|
237
|
+
ip: request.ip,
|
|
238
|
+
time: Date.now(),
|
|
239
|
+
};
|
|
240
|
+
await trust.set(`${request.account}|${request.machineId}`, record);
|
|
241
|
+
return record;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
throw new Error(`No access request found with id ${JSON.stringify(requestId)}. It may have already been granted or expired.`);
|
|
245
|
+
}
|
|
246
|
+
async get(account, bucketName, path, range) {
|
|
247
|
+
let result = await this.get2(account, bucketName, path, range);
|
|
248
|
+
return result && result.data || undefined;
|
|
249
|
+
}
|
|
250
|
+
async get2(account, bucketName, path, range) {
|
|
251
|
+
assertValidPath(path);
|
|
252
|
+
let bucket = await getBucket(account, bucketName);
|
|
253
|
+
if (!bucket)
|
|
254
|
+
return undefined;
|
|
255
|
+
return await bucket.store.get2(path, { range });
|
|
256
|
+
}
|
|
257
|
+
async set(account, bucketName, path, data, lastModified) {
|
|
258
|
+
assertValidName(bucketName, "bucket name");
|
|
259
|
+
assertValidPath(path);
|
|
260
|
+
// Handles bucket creation (writes of ROUTING_FILE), reconfiguration, fast mode, and
|
|
261
|
+
// immutability — see storageServerState.ts
|
|
262
|
+
await (0, storageServerState_1.writeBucketFile)(account, bucketName, path, Buffer.from(data), { lastModified });
|
|
263
|
+
}
|
|
264
|
+
async del(account, bucketName, path) {
|
|
265
|
+
assertValidName(bucketName, "bucket name");
|
|
266
|
+
assertValidPath(path);
|
|
267
|
+
await (0, storageServerState_1.deleteBucketFile)(account, bucketName, path);
|
|
268
|
+
}
|
|
269
|
+
async getInfo(account, bucketName, path) {
|
|
270
|
+
assertValidPath(path);
|
|
271
|
+
let bucket = await getBucket(account, bucketName);
|
|
272
|
+
if (!bucket)
|
|
273
|
+
return undefined;
|
|
274
|
+
return await bucket.store.getInfo(path);
|
|
275
|
+
}
|
|
276
|
+
async findInfo(account, bucketName, prefix, config) {
|
|
277
|
+
let bucket = await getBucket(account, bucketName);
|
|
278
|
+
if (!bucket)
|
|
279
|
+
return [];
|
|
280
|
+
return await bucket.store.findInfo(prefix, config);
|
|
281
|
+
}
|
|
282
|
+
// Fast (served from the store's BulkDatabase2 index, not a scan) — see IArchives.getChangesAfter
|
|
283
|
+
async getChangesAfter(account, bucketName, time) {
|
|
284
|
+
let bucket = await getBucket(account, bucketName);
|
|
285
|
+
if (!bucket)
|
|
286
|
+
return [];
|
|
287
|
+
if (!bucket.store.getChangesAfter) {
|
|
288
|
+
throw new Error(`Bucket ${account}/${bucketName} does not support getChangesAfter (rawDisk buckets have no index)`);
|
|
289
|
+
}
|
|
290
|
+
return await bucket.store.getChangesAfter(time);
|
|
291
|
+
}
|
|
292
|
+
async getArchivesConfig(account, bucketName) {
|
|
293
|
+
var _a, _b;
|
|
294
|
+
let bucket = await getBucket(account, bucketName);
|
|
295
|
+
// Missing buckets say true, matching what they become once created (the default store type)
|
|
296
|
+
let progress = (_b = bucket === null || bucket === void 0 ? void 0 : (_a = bucket.store).getSyncProgress) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
297
|
+
return {
|
|
298
|
+
supportsChangesAfter: !bucket || !!bucket.store.getChangesAfter,
|
|
299
|
+
remoteConfig: bucket === null || bucket === void 0 ? void 0 : bucket.routing,
|
|
300
|
+
index: progress === null || progress === void 0 ? void 0 : progress.index,
|
|
301
|
+
indexSources: progress === null || progress === void 0 ? void 0 : progress.sources,
|
|
302
|
+
readerDiskLimit: progress === null || progress === void 0 ? void 0 : progress.readerDiskLimit,
|
|
303
|
+
syncing: progress === null || progress === void 0 ? void 0 : progress.syncing,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
/** Walks the whole index for exact totals (overall and per holding source) - more expensive
|
|
307
|
+
* than the maintained counters that getArchivesConfig returns, but immune to counter drift. */
|
|
308
|
+
async getIndexInfo(account, bucketName) {
|
|
309
|
+
let bucket = await getBucket(account, bucketName);
|
|
310
|
+
if (!bucket || !bucket.store.computeIndexTotals)
|
|
311
|
+
return undefined;
|
|
312
|
+
return await bucket.store.computeIndexTotals();
|
|
313
|
+
}
|
|
314
|
+
async getSyncStatus(account, bucketName) {
|
|
315
|
+
let bucket = await getBucket(account, bucketName);
|
|
316
|
+
if (!bucket)
|
|
317
|
+
return { allScansComplete: true, indexSize: 0, sources: [] };
|
|
318
|
+
if (!bucket.store.getSyncStatus) {
|
|
319
|
+
throw new Error(`Bucket ${account}/${bucketName} does not support getSyncStatus (rawDisk buckets have no synchronization)`);
|
|
320
|
+
}
|
|
321
|
+
return await bucket.store.getSyncStatus();
|
|
322
|
+
}
|
|
323
|
+
async startLargeFile(account, bucketName, path) {
|
|
324
|
+
(0, storageServerState_1.assertWritesAllowed)();
|
|
325
|
+
// Validates now, so the upload doesn't fail at the end
|
|
326
|
+
assertValidPath(path);
|
|
327
|
+
let bucket = await getBucket(account, bucketName);
|
|
328
|
+
if (!bucket) {
|
|
329
|
+
throw new Error(`Bucket ${account}/${bucketName} does not exist. Write its routing config to ${JSON.stringify(remoteConfig_1.ROUTING_FILE)} to create it.`);
|
|
330
|
+
}
|
|
331
|
+
await (0, storageServerState_1.assertMutable)(bucket, path, Date.now());
|
|
332
|
+
let id = await bucket.store.startLargeUpload();
|
|
333
|
+
largeUploadInfo.set(id, { account, bucketName, path });
|
|
334
|
+
return id;
|
|
335
|
+
}
|
|
336
|
+
async uploadPart(uploadId, data) {
|
|
337
|
+
(0, storageServerState_1.assertWritesAllowed)();
|
|
338
|
+
let info = largeUploadInfo.get(uploadId);
|
|
339
|
+
if (!info)
|
|
340
|
+
throw new Error(`Unknown large upload ${uploadId}`);
|
|
341
|
+
let bucket = await getBucket(info.account, info.bucketName);
|
|
342
|
+
if (!bucket)
|
|
343
|
+
throw new Error(`Bucket ${info.account}/${info.bucketName} no longer exists`);
|
|
344
|
+
await bucket.store.appendLargeUpload(uploadId, Buffer.from(data));
|
|
345
|
+
}
|
|
346
|
+
async finishLargeFile(uploadId) {
|
|
347
|
+
(0, storageServerState_1.assertWritesAllowed)();
|
|
348
|
+
let info = largeUploadInfo.get(uploadId);
|
|
349
|
+
if (!info)
|
|
350
|
+
throw new Error(`Unknown large upload ${uploadId}`);
|
|
351
|
+
largeUploadInfo.delete(uploadId);
|
|
352
|
+
let bucket = await getBucket(info.account, info.bucketName);
|
|
353
|
+
if (!bucket)
|
|
354
|
+
throw new Error(`Bucket ${info.account}/${info.bucketName} no longer exists`);
|
|
355
|
+
await bucket.store.finishLargeUpload(uploadId, info.path);
|
|
356
|
+
}
|
|
357
|
+
async cancelLargeFile(uploadId) {
|
|
358
|
+
let info = largeUploadInfo.get(uploadId);
|
|
359
|
+
if (!info)
|
|
360
|
+
return;
|
|
361
|
+
largeUploadInfo.delete(uploadId);
|
|
362
|
+
let bucket = await getBucket(info.account, info.bucketName);
|
|
363
|
+
if (!bucket)
|
|
364
|
+
return;
|
|
365
|
+
await bucket.store.cancelLargeUpload(uploadId);
|
|
366
|
+
}
|
|
367
|
+
// The server's single default HTTP route. /file/<account>/<bucketName>/<path> serves files from
|
|
368
|
+
// public buckets over plain GETs (see IArchives.getURL); every other path serves the access
|
|
369
|
+
// page (via RequireController.requireHTML — the path is the account name, see accessPage.tsx).
|
|
370
|
+
async httpEntry(config) {
|
|
371
|
+
var _a, _b;
|
|
372
|
+
// Both are keyed by the current call and must be captured synchronously, before any await
|
|
373
|
+
let caller = SocketFunction_1.SocketFunction.getCaller();
|
|
374
|
+
let request = (0, callHTTPHandler_1.getCurrentHTTPRequest)();
|
|
375
|
+
let pathname = new URL((request === null || request === void 0 ? void 0 : request.url) || "/", "https://localhost").pathname;
|
|
376
|
+
if (!pathname.startsWith("/file/")) {
|
|
377
|
+
return await (0, callManager_1.performLocalCall)({
|
|
378
|
+
caller,
|
|
379
|
+
call: { nodeId: caller.nodeId, classGuid: RequireController_1.RequireController._classGuid, functionName: "requireHTML", args: [config] },
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
let parts = pathname.split("/").filter(x => x).map(decodeURIComponent);
|
|
383
|
+
let account = parts[1];
|
|
384
|
+
let bucketName = parts[2];
|
|
385
|
+
let filePath = parts.slice(3).join("/");
|
|
386
|
+
if (!account || !bucketName || !filePath) {
|
|
387
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(Buffer.from(""), { status: "404" });
|
|
388
|
+
}
|
|
389
|
+
assertValidName(account, "account");
|
|
390
|
+
assertValidName(bucketName, "bucket name");
|
|
391
|
+
assertValidPath(filePath);
|
|
392
|
+
let bucket = await (0, storageServerState_1.getLoadedBucket)(account, bucketName);
|
|
393
|
+
if (!bucket) {
|
|
394
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(Buffer.from(""), { status: "404" });
|
|
395
|
+
}
|
|
396
|
+
if (!((_a = bucket.self) === null || _a === void 0 ? void 0 : _a.public)) {
|
|
397
|
+
throw new Error(`Bucket ${account}/${bucketName} is not public, so its files cannot be read over plain URLs`);
|
|
398
|
+
}
|
|
399
|
+
// The index answers existence + write time + size without touching any data, so
|
|
400
|
+
// If-Modified-Since and range validation cost nothing
|
|
401
|
+
let info = await bucket.store.getInfo(filePath);
|
|
402
|
+
if (!info || !info.size) {
|
|
403
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(Buffer.from(""), { status: "404" });
|
|
404
|
+
}
|
|
405
|
+
let ext = filePath.split(".").pop() || "";
|
|
406
|
+
let headers = {
|
|
407
|
+
"Content-Type": CONTENT_TYPES[ext.toLowerCase()] || "application/octet-stream",
|
|
408
|
+
"Last-Modified": new Date(info.writeTime).toUTCString(),
|
|
409
|
+
"Accept-Ranges": "bytes",
|
|
410
|
+
};
|
|
411
|
+
if ((_b = bucket.self) === null || _b === void 0 ? void 0 : _b.immutable) {
|
|
412
|
+
headers["Cache-Control"] = `max-age=${IArchives_1.IMMUTABLE_CACHE_TIME / 1000}`;
|
|
413
|
+
}
|
|
414
|
+
let ifModifiedSince = request === null || request === void 0 ? void 0 : request.headers["if-modified-since"];
|
|
415
|
+
if (typeof ifModifiedSince === "string") {
|
|
416
|
+
let since = new Date(ifModifiedSince).getTime();
|
|
417
|
+
// Last-Modified is served at 1 second resolution, so compare at that resolution
|
|
418
|
+
if (since && Math.floor(info.writeTime / 1000) * 1000 <= since) {
|
|
419
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(Buffer.from(""), { ...headers, status: "304" });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
let range;
|
|
423
|
+
let rangeHeader = request === null || request === void 0 ? void 0 : request.headers["range"];
|
|
424
|
+
if (typeof rangeHeader === "string") {
|
|
425
|
+
// Single-range form only (bytes=start-end / start- / -suffix); anything else serves the full file
|
|
426
|
+
let match = /^bytes=(\d*)-(\d*)$/.exec(rangeHeader.trim());
|
|
427
|
+
if (match && (match[1] || match[2])) {
|
|
428
|
+
let start;
|
|
429
|
+
let endInclusive = info.size - 1;
|
|
430
|
+
if (!match[1]) {
|
|
431
|
+
start = Math.max(0, info.size - +match[2]);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
start = +match[1];
|
|
435
|
+
if (match[2]) {
|
|
436
|
+
endInclusive = Math.min(+match[2], info.size - 1);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (start >= info.size || start > endInclusive) {
|
|
440
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(Buffer.from(""), { ...headers, "Content-Range": `bytes */${info.size}`, status: "416" });
|
|
441
|
+
}
|
|
442
|
+
range = { start, end: endInclusive + 1 };
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
let result = await bucket.store.get2(filePath, { range });
|
|
446
|
+
if (!result) {
|
|
447
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(Buffer.from(""), { status: "404" });
|
|
448
|
+
}
|
|
449
|
+
if (range) {
|
|
450
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(result.data, { ...headers, "Content-Range": `bytes ${range.start}-${range.end - 1}/${info.size}`, status: "206" });
|
|
451
|
+
}
|
|
452
|
+
return (0, callHTTPHandler_1.setHTTPResultHeaders)(result.data, headers);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const largeUploadInfo = new Map();
|
|
456
|
+
// Access checks run as hooks on the register shape below, keyed off the call's arguments, so the
|
|
457
|
+
// method bodies don't each repeat them
|
|
458
|
+
const accountAccess = async (context) => {
|
|
459
|
+
await requireAccess(String(context.call.args[0]));
|
|
460
|
+
};
|
|
461
|
+
const uploadAccess = async (context) => {
|
|
462
|
+
let info = largeUploadInfo.get(String(context.call.args[0]));
|
|
463
|
+
// Unknown upload ids are handled by the methods themselves (throw / no-op)
|
|
464
|
+
if (!info)
|
|
465
|
+
return;
|
|
466
|
+
await requireAccess(info.account);
|
|
467
|
+
};
|
|
468
|
+
const adminAccess = async () => {
|
|
469
|
+
requireAdmin();
|
|
470
|
+
};
|
|
471
|
+
exports.RemoteStorageController = SocketFunction_1.SocketFunction.register(exports.REMOTE_STORAGE_CLASS_GUID, new RemoteStorageControllerBase(), () => ({
|
|
472
|
+
ping: {},
|
|
473
|
+
authenticate: {},
|
|
474
|
+
requestAccess: {},
|
|
475
|
+
getAccessState: {},
|
|
476
|
+
listRequestsForIP: { hooks: [accountAccess] },
|
|
477
|
+
grantAccess: {},
|
|
478
|
+
adminListRequests: { hooks: [adminAccess] },
|
|
479
|
+
adminGrantAccess: { hooks: [adminAccess] },
|
|
480
|
+
get: { hooks: [accountAccess] },
|
|
481
|
+
get2: { hooks: [accountAccess] },
|
|
482
|
+
set: { hooks: [accountAccess] },
|
|
483
|
+
del: { hooks: [accountAccess] },
|
|
484
|
+
getInfo: { hooks: [accountAccess] },
|
|
485
|
+
findInfo: { hooks: [accountAccess] },
|
|
486
|
+
getChangesAfter: { hooks: [accountAccess] },
|
|
487
|
+
getArchivesConfig: { hooks: [accountAccess] },
|
|
488
|
+
getIndexInfo: { hooks: [accountAccess] },
|
|
489
|
+
getSyncStatus: { hooks: [accountAccess] },
|
|
490
|
+
startLargeFile: { hooks: [accountAccess] },
|
|
491
|
+
uploadPart: { hooks: [uploadAccess] },
|
|
492
|
+
finishLargeFile: { hooks: [uploadAccess] },
|
|
493
|
+
cancelLargeFile: { hooks: [uploadAccess] },
|
|
494
|
+
httpEntry: {},
|
|
495
|
+
}), () => ({
|
|
496
|
+
noClientHooks: true,
|
|
497
|
+
noDefaultHooks: true,
|
|
498
|
+
}));
|
|
499
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZUNvbnRyb2xsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdG9yYWdlQ29udHJvbGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxNQUFNLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztBQUUxQixtRUFBZ0U7QUFFaEUsNkRBQTREO0FBQzVELHlFQUFrRztBQUNsRyxpRUFBbUU7QUFDbkUsaUZBQThFO0FBQzlFLG1EQUF3RDtBQUN4RCxrREFBa0k7QUFDbEksNENBQXlHO0FBQ3pHLGlEQUE4QztBQUM5Qyw2REFHOEI7QUFFOUIsNkZBQTZGO0FBQzdGLGlHQUFpRztBQUNqRyw4RkFBOEY7QUFDOUYsZ0dBQWdHO0FBQ2hHLDBDQUEwQztBQUMxQyxFQUFFO0FBQ0YsaUdBQWlHO0FBQ2pHLGlHQUFpRztBQUNqRyxvRkFBb0Y7QUFFdkUsUUFBQSx5QkFBeUIsR0FBRyxrQ0FBa0MsQ0FBQztBQUMvRCxRQUFBLG9CQUFvQixHQUFHLHNCQUFzQixDQUFDO0FBQzNELDhFQUE4RTtBQUNqRSxRQUFBLHlCQUF5QixHQUFHLDJDQUEyQyxDQUFDO0FBQ3hFLFFBQUEscUJBQXFCLEdBQUcsdUNBQXVDLENBQUM7QUFFN0UsTUFBTSxnQkFBZ0IsR0FBRyxtQkFBWSxHQUFHLEVBQUUsQ0FBQztBQUMzQyxNQUFNLFlBQVksR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDO0FBQ2hDLE1BQU0sbUJBQW1CLEdBQUcsRUFBRSxDQUFDO0FBa0MvQiwrRkFBK0Y7QUFDL0YsMkVBQTJFO0FBQzNFLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0FBRTNDLE1BQU0sYUFBYSxHQUE4QjtJQUM3QyxJQUFJLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRSxpQkFBaUIsRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxrQkFBa0I7SUFDbkYsR0FBRyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsV0FBVyxFQUFFLEdBQUcsRUFB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Q0FBQyxFQUFFO0lBQzNDLGlCQUFpQixFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsYUFBYSxDQUFDLEVBQUU7SUFDN0MsWUFBWSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsYUFBYSxDQUFDLEVBQUU7SUFDeEMsYUFBYSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsYUFBYSxDQUFDLEVBQUU7SUFDekMsY0FBYyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsYUFBYSxDQUFDLEVBQUU7SUFDMUMsVUFBVSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsWUFBWSxDQUFDLEVBQUU7SUFDckMsZUFBZSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsWUFBWSxDQUFDLEVBQUU7SUFDMUMsZUFBZSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsWUFBWSxDQUFDLEVBQUU7SUFDMUMsU0FBUyxFQUFFLEVBQUU7Q0FDaEIsQ0FBQyxFQUNGLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDSCxhQUFhLEVBQUUsSUFBSTtJQUNuQixjQUFjLEVBQUUsSUFBSTtDQUN2QixDQUFDLENBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5hbGxvd2NsaWVudCA9IHRydWU7XG5cbmltcG9ydCB7IFNvY2tldEZ1bmN0aW9uIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9Tb2NrZXRGdW5jdGlvblwiO1xuaW1wb3J0IHsgU29ja2V0RnVuY3Rpb25Ib29rIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9Tb2NrZXRGdW5jdGlvblR5cGVzXCI7XG5pbXBvcnQgeyBnZXROb2RlSWRJUCB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL25vZGVDYWNoZVwiO1xuaW1wb3J0IHsgc2V0SFRUUFJlc3VsdEhlYWRlcnMsIGdldEN1cnJlbnRIVFRQUmVxdWVzdCB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL2NhbGxIVFRQSGFuZGxlclwiO1xuaW1wb3J0IHsgcGVyZm9ybUxvY2FsQ2FsbCB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL2NhbGxNYW5hZ2VyXCI7XG5pbXBvcnQgeyBSZXF1aXJlQ29udHJvbGxlciB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vcmVxdWlyZS9SZXF1aXJlQ29udHJvbGxlclwiO1xuaW1wb3J0IHsgdGltZUluTWludXRlIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvbWlzY1wiO1xuaW1wb3J0IHsgZ2V0Q29tbW9uTmFtZSwgZ2V0UHVibGljSWRlbnRpZmllciwgZ2V0T3duTWFjaGluZUlkLCB2ZXJpZnksIHZlcmlmeU1hY2hpbmVJZEZvclB1YmxpY0tleSB9IGZyb20gXCIuLi8uLi9taXNjL2h0dHBzL2NlcnRzXCI7XG5pbXBvcnQgeyBBcmNoaXZlRmlsZUluZm8sIEFyY2hpdmVzQ29uZmlnLCBBcmNoaXZlc1N5bmNTdGF0dXMsIElNTVVUQUJMRV9DQUNIRV9USU1FIH0gZnJvbSBcIi4uL0lBcmNoaXZlc1wiO1xuaW1wb3J0IHsgUk9VVElOR19GSUxFIH0gZnJvbSBcIi4vcmVtb3RlQ29uZmlnXCI7XG5pbXBvcnQge1xuICAgIGdldFN0b3JhZ2VTZXJ2ZXJDb25maWcsIGdldFRydXN0LCBnZXRSZXF1ZXN0cywgZ2V0TG9hZGVkQnVja2V0LCB3cml0ZUJ1Y2tldEZpbGUsXG4gICAgZGVsZXRlQnVja2V0RmlsZSwgYXNzZXJ0V3JpdGVzQWxsb3dlZCwgYXNzZXJ0TXV0YWJsZSwgTG9hZGVkQnVja2V0LFxufSBmcm9tIFwiLi9zdG9yYWdlU2VydmVyU3RhdGVcIjtcblxuLy8gVGhlIHJlbW90ZSBzdG9yYWdlIHNlcnZlcidzIEFQSS4gQXV0aGVudGljYXRpb24gdXNlcyBjZXJ0cy50cyBtYWNoaW5lIGlkZW50aXRpZXM6IGEgY2xpZW50XG4vLyBwcm92ZXMgaXQgb3ducyBpdHMgbWFjaGluZSBrZXkgYnkgc2lnbmluZyBhIHRpbWVzdGFtcGVkIHRva2VuIChib3VuZCB0byB0aGlzIHNlcnZlciwgc28gdG9rZW5zXG4vLyBjYW4ndCBiZSByZXBsYXllZCBlbHNld2hlcmUpLCBhbmQgdGhlIHNlcnZlciB0aGVuIHRydXN0cyB0aGF0IGNvbm5lY3Rpb24gYXMgdGhhdCBtYWNoaW5lSWQuXG4vLyBBY2Nlc3MgdG8gYW4gYWNjb3VudCBpcyBncmFudGVkIHRvIHNwZWNpZmljIG1hY2hpbmVJZHMsIHZpYSBhIGNvbW1hbmQgbGluZSBjb21tYW5kIHJ1biBvbiB0aGVcbi8vIHN0b3JhZ2UgbWFjaGluZSAoc2VlIHN0b3JhZ2VTZXJ2ZXIudHMpLlxuLy9cbi8vIFRoZXJlIGlzIG5vIGJ1Y2tldC1jcmVhdGlvbiBBUEk6IGEgYnVja2V0IGV4aXN0cyBpZmYgaXRzIHJvdXRpbmcgY29uZmlnIChST1VUSU5HX0ZJTEUpIGV4aXN0cyxcbi8vIGFuZCB3cml0aW5nIHRoYXQgZmlsZSBjcmVhdGVzIG9yIHJlY29uZmlndXJlcyB0aGUgYnVja2V0IChzZWUgc3RvcmFnZVNlcnZlclN0YXRlLnRzKS4gUmVhZHMgb2Zcbi8vIG5vbmV4aXN0ZW50IGJ1Y2tldHMgcmV0dXJuIHVuZGVmaW5lZCAvIGVtcHR5LCBzYW1lIGFzIHJlYWRzIG9mIG5vbmV4aXN0ZW50IGZpbGVzLlxuXG5leHBvcnQgY29uc3QgUkVNT1RFX1NUT1JBR0VfQ0xBU1NfR1VJRCA9IFwiUmVtb3RlU3RvcmFnZUNvbnRyb2xsZXItYjdlNDJhOTFcIjtcbmV4cG9ydCBjb25zdCBTVE9SQUdFX0FVVEhfUFVSUE9TRSA9IFwicmVtb3RlU3RvcmFnZS1hdXRoLTFcIjtcbi8vIEVycm9yIG1hcmtlcnMsIHNvIGNsaWVudHMgY2FuIGlkZW50aWZ5IHRoZXNlIGZhaWx1cmVzIGluc2lkZSBlcnJvciBtZXNzYWdlc1xuZXhwb3J0IGNvbnN0IFNUT1JBR0VfTk9UX0FVVEhFTlRJQ0FURUQgPSBcIlJFTU9URV9TVE9SQUdFX05PVF9BVVRIRU5USUNBVEVEX2NmMmY3YjFlXCI7XG5leHBvcnQgY29uc3QgU1RPUkFHRV9BQ0NFU1NfREVOSUVEID0gXCJSRU1PVEVfU1RPUkFHRV9BQ0NFU1NfREVOSUVEXzlkODFhNGMwXCI7XG5cbmNvbnN0IEFVVEhfVElNRV9XSU5ET1cgPSB0aW1lSW5NaW51dGUgKiAxMDtcbmNvbnN0IE1BWF9TRVNTSU9OUyA9IDEwMCAqIDEwMDA7XG5jb25zdCBNQVhfUkVRVUVTVFNfUEVSX0lQID0gNTA7XG5cbmV4cG9ydCB0eXBlIEF1dGhUb2tlbiA9IHtcbiAgICBjZXJ0UGVtOiBzdHJpbmc7XG4gICAgdGltZTogbnVtYmVyO1xuICAgIHNpZ25hdHVyZTogc3RyaW5nO1xufTtcbmV4cG9ydCB0eXBlIEFjY2Vzc1JlcXVlc3QgPSB7XG4gICAgcmVxdWVzdElkOiBzdHJpbmc7XG4gICAgYWNjb3VudDogc3RyaW5nO1xuICAgIG1hY2hpbmVJZDogc3RyaW5nO1xuICAgIGlwOiBzdHJpbmc7XG4gICAgdGltZTogbnVtYmVyO1xufTtcbmV4cG9ydCB0eXBlIFRydXN0UmVjb3JkID0ge1xuICAgIGFjY291bnQ6IHN0cmluZztcbiAgICBtYWNoaW5lSWQ6IHN0cmluZztcbiAgICBpcDogc3RyaW5nO1xuICAgIHRpbWU6IG51bWJlcjtcbn07XG5leHBvcnQgdHlwZSBBY2Nlc3NTdGF0ZSA9IHtcbiAgICBtYWNoaW5lSWQ6IHN0cmluZztcbiAgICBpcDogc3RyaW5nO1xuICAgIGhhc0FjY2VzczogYm9vbGVhbjtcbiAgICAvLyBBIHNpbmdsZSBzc2ggY29tbWFuZCwgcnVubmFibGUgZnJvbSBhbnl3aGVyZSwgdGhhdCBydW5zIHRoZSBncmFudEFjY2VzcyBDTEkgb24gdGhlIHN0b3JhZ2VcbiAgICAvLyBtYWNoaW5lIHRvIGdyYW50IHRoZSBjYWxsZXIncyBvd24gcGVuZGluZyByZXF1ZXN0LiBPbmx5IHNldCB3aGVuIHRoZSBjYWxsZXIgaGFzIGEgcGVuZGluZ1xuICAgIC8vIHJlcXVlc3QgKHNvIGFuIGFscmVhZHktdHJ1c3RlZCBjYWxsZXIgaGFzIG5vIG5lZWQgZm9yIGl0KS5cbiAgICBncmFudEFjY2Vzc0NvbW1hbmQ/OiBzdHJpbmc7XG4gICAgLy8gT25seSB0aGUgbWFjaGluZXMgdGhhdCBBTFJFQURZIGhhdmUgYWNjZXNzLiBQZW5kaW5nIHJlcXVlc3RzIGFyZSBORVZFUiBsaXN0ZWQgaGVyZSDigJQgc2hvd2luZ1xuICAgIC8vIHRoZW0gd291bGQgbGV0IGEgdHJ1c3RlZCB1c2VyIGFjY2lkZW50YWxseSBhcHByb3ZlIGEgcmFuZG9tIHJlcXVlc3QuIENhbGxlcnMgc2VlIHBlbmRpbmdcbiAgICAvLyByZXF1ZXN0cyBvbmx5IGJ5IGV4cGxpY2l0bHkgdHlwaW5nIGFuIElQIGludG8gbGlzdFJlcXVlc3RzRm9ySVAuXG4gICAgdHJ1c3RlZE1hY2hpbmVzPzogVHJ1c3RSZWNvcmRbXTtcbn07XG5cbi8vIGNhbGxlck5vZGVJZCAtPiBhdXRoZW50aWNhdGVkIG1hY2hpbmVJZC4gQ29ubmVjdGlvbnMgYXJlIGxvbmctbGl2ZWQgd2Vic29ja2V0cywgc28gYSBzZXNzaW9uXG4vLyBsYXN0cyB1bnRpbCB0aGUgY29ubmVjdGlvbiBkcm9wcyAoY2xpZW50cyByZS1hdXRoZW50aWNhdGUgb24gcmVjb25uZWN0KS5cbmNvbnN0IHNlc3Npb25zID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcblxuY29uc3QgQ09OVEVOVF9UWVBFUzogeyBbZXh0OiBzdHJpbmddOiBzdHJpbmcgfSA9IHtcbiAgICBodG1sOiBcInRleHQvaHRtbFwiLCBqczogXCJ0ZXh0L2phdmFzY3JpcHRcIiwgY3NzOiBcInRleHQvY3NzXCIsIGpzb246IFwiYXBwbGljYXRpb24vanNvblwiLFxuICAgIHR4dDogXCJ0ZXh0L3BsYWluXCIsIHBuZzogXCJpbWFnZS9wbmdcIiwganBnOiBcImltYWdlL2pwZWdcIiwganBlZzogXCJpbWFnZS9qcGVnXCIsIGdpZjogXCJpbWFnZS9naWZcIixcbiAgICBzdmc6IFwiaW1hZ2Uvc3ZnK3htbFwiLCB3ZWJwOiBcImltYWdlL3dlYnBcIiwgbXA0OiBcInZpZGVvL21wNFwiLCB3ZWJtOiBcInZpZGVvL3dlYm1cIixcbiAgICBtcDM6IFwiYXVkaW8vbXBlZ1wiLCB3YXY6IFwiYXVkaW8vd2F2XCIsIHBkZjogXCJhcHBsaWNhdGlvbi9wZGZcIixcbn07XG5cbmZ1bmN0aW9uIGFzc2VydFZhbGlkTmFtZSh2YWx1ZTogc3RyaW5nLCBraW5kOiBzdHJpbmcpIHtcbiAgICBpZiAoIS9eW1xcdy1dezEsNjR9JC8udGVzdCh2YWx1ZSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkICR7a2luZH0gJHtKU09OLnN0cmluZ2lmeSh2YWx1ZSl9LCBleHBlY3RlZCAxLTY0IGNoYXJhY3RlcnMgb2YgbGV0dGVycy9udW1iZXJzL3VuZGVyc2NvcmUvZGFzaGApO1xuICAgIH1cbn1cbmZ1bmN0aW9uIGFzc2VydFZhbGlkUGF0aChwYXRoOiBzdHJpbmcpIHtcbiAgICBpZiAoQnVmZmVyLmZyb20ocGF0aCwgXCJ1dGY4XCIpLmxlbmd0aCA+IDEwMDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBQYXRoIHRvbyBsb25nOiAke3BhdGgubGVuZ3RofSBjaGFyYWN0ZXJzID4gMTAwMC4gUGF0aDogJHtwYXRoLnNsaWNlKDAsIDIwMCl9YCk7XG4gICAgfVxuICAgIGlmICghcGF0aCB8fCBwYXRoLnN0YXJ0c1dpdGgoXCIvXCIpIHx8IHBhdGguZW5kc1dpdGgoXCIvXCIpIHx8IHBhdGguaW5jbHVkZXMoXCIvL1wiKSB8fCBwYXRoLmluY2x1ZGVzKFwiXFxcXFwiKSB8fCBwYXRoLmluY2x1ZGVzKFwiXFx4MDBcIikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHBhdGggJHtKU09OLnN0cmluZ2lmeShwYXRoLnNsaWNlKDAsIDIwMCkpfSwgcGF0aHMgY2Fubm90IGJlIGVtcHR5LCBzdGFydCBvciBlbmQgd2l0aCAvLCBvciBjb250YWluIC8vLCBiYWNrc2xhc2hlcywgb3IgbnVsbCBjaGFyYWN0ZXJzYCk7XG4gICAgfVxuICAgIC8vIFBhdGhzIGFyZSBvbmUtdG8tb25lIHdpdGggZmlsZXMgb24gZGlzaywgc28gLiBhbmQgLi4gc2VnbWVudHMgd291bGQgZXNjYXBlIHRoZSBzdG9yZSBmb2xkZXJcbiAgICBpZiAocGF0aC5zcGxpdChcIi9cIikuc29tZShwYXJ0ID0+IHBhcnQgPT09IFwiLlwiIHx8IHBhcnQgPT09IFwiLi5cIikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHBhdGggJHtKU09OLnN0cmluZ2lmeShwYXRoLnNsaWNlKDAsIDIwMCkpfSwgcGF0aHMgY2Fubm90IGNvbnRhaW4gLiBvciAuLiBzZWdtZW50c2ApO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gZ2V0Q2FsbGVyTWFjaGluZUlkKCk6IHN0cmluZyB7XG4gICAgbGV0IGNhbGxlciA9IFNvY2tldEZ1bmN0aW9uLmdldENhbGxlcigpO1xuICAgIGxldCBtYWNoaW5lSWQgPSBzZXNzaW9ucy5nZXQoY2FsbGVyLm5vZGVJZCk7XG4gICAgaWYgKCFtYWNoaW5lSWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGAke1NUT1JBR0VfTk9UX0FVVEhFTlRJQ0FURUR9IENhbGwgYXV0aGVudGljYXRlIGZpcnN0IChjb25uZWN0aW9uICR7Y2FsbGVyLm5vZGVJZH0pYCk7XG4gICAgfVxuICAgIHJldHVybiBtYWNoaW5lSWQ7XG59XG5mdW5jdGlvbiBnZXRDYWxsZXJJUCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBnZXROb2RlSWRJUChTb2NrZXRGdW5jdGlvbi5nZXRDYWxsZXIoKS5ub2RlSWQpO1xufVxuZnVuY3Rpb24gaXNBZG1pbihtYWNoaW5lSWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBtYWNoaW5lSWQgPT09IGdldE93bk1hY2hpbmVJZChnZXRTdG9yYWdlU2VydmVyQ29uZmlnKCkucm9vdERvbWFpbik7XG59XG5mdW5jdGlvbiByZXF1aXJlQWRtaW4oKTogc3RyaW5nIHtcbiAgICBsZXQgbWFjaGluZUlkID0gZ2V0Q2FsbGVyTWFjaGluZUlkKCk7XG4gICAgaWYgKCFpc0FkbWluKG1hY2hpbmVJZCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGAke1NUT1JBR0VfQUNDRVNTX0RFTklFRH0gQWRtaW4gY29tbWFuZHMgbXVzdCBiZSBydW4gZnJvbSB0aGUgc3RvcmFnZSBtYWNoaW5lIGl0c2VsZiAoY2FsbGVyIG1hY2hpbmUgJHttYWNoaW5lSWR9KWApO1xuICAgIH1cbiAgICByZXR1cm4gbWFjaGluZUlkO1xufVxuYXN5bmMgZnVuY3Rpb24gcmVxdWlyZUFjY2VzcyhhY2NvdW50OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGFzc2VydFZhbGlkTmFtZShhY2NvdW50LCBcImFjY291bnRcIik7XG4gICAgbGV0IG1hY2hpbmVJZCA9IGdldENhbGxlck1hY2hpbmVJZCgpO1xuICAgIGlmIChpc0FkbWluKG1hY2hpbmVJZCkpIHJldHVybiBtYWNoaW5lSWQ7XG4gICAgbGV0IHRydXN0ID0gYXdhaXQgZ2V0VHJ1c3QoKTtcbiAgICBsZXQgdHJ1c3RlZCA9IGF3YWl0IHRydXN0LmdldChgJHthY2NvdW50fXwke21hY2hpbmVJZH1gKTtcbiAgICBpZiAoIXRydXN0ZWQpIHtcbiAgICAgICAgbGV0IHsgZG9tYWluLCBwb3J0IH0gPSBnZXRTdG9yYWdlU2VydmVyQ29uZmlnKCk7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgJHtTVE9SQUdFX0FDQ0VTU19ERU5JRUR9IE1hY2hpbmUgJHttYWNoaW5lSWR9IGhhcyBubyBhY2Nlc3MgdG8gYWNjb3VudCAke0pTT04uc3RyaW5naWZ5KGFjY291bnQpfS4gVmlzaXQgaHR0cHM6Ly8ke2RvbWFpbn06JHtwb3J0fS8ke2FjY291bnR9IGZvciBhY2Nlc3MgaW5zdHJ1Y3Rpb25zLmApO1xuICAgIH1cbiAgICByZXR1cm4gbWFjaGluZUlkO1xufVxuXG4vLyBBIHNpbmdsZSBjb21tYW5kLCBydW5uYWJsZSBmcm9tIGFueXdoZXJlLCB0aGF0IHNzaGVzIGludG8gdGhlIHN0b3JhZ2UgbWFjaGluZSBhbmQgcnVucyB0aGVcbi8vIGdyYW50QWNjZXNzIENMSSB0aGVyZVxuZnVuY3Rpb24gZ2V0R3JhbnRBY2Nlc3NDb21tYW5kKHJlcXVlc3RJZDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBsZXQgeyBzc2hUYXJnZXQsIHNlcnZlckNvbW1hbmQgfSA9IGdldFN0b3JhZ2VTZXJ2ZXJDb25maWcoKTtcbiAgICByZXR1cm4gYHNzaCAke3NzaFRhcmdldH0gJyR7c2VydmVyQ29tbWFuZH0gLS1yZXF1ZXN0SWQgJHtyZXF1ZXN0SWR9J2A7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldEJ1Y2tldChhY2NvdW50OiBzdHJpbmcsIGJ1Y2tldE5hbWU6IHN0cmluZyk6IFByb21pc2U8TG9hZGVkQnVja2V0IHwgdW5kZWZpbmVkPiB7XG4gICAgYXNzZXJ0VmFsaWROYW1lKGFjY291bnQsIFwiYWNjb3VudFwiKTtcbiAgICBhc3NlcnRWYWxpZE5hbWUoYnVja2V0TmFtZSwgXCJidWNrZXQgbmFtZVwiKTtcbiAgICByZXR1cm4gYXdhaXQgZ2V0TG9hZGVkQnVja2V0KGFjY291bnQsIGJ1Y2tldE5hbWUpO1xufVxuXG5jbGFzcyBSZW1vdGVTdG9yYWdlQ29udHJvbGxlckJhc2Uge1xuICAgIC8vIExhdGVuY3kgbWVhc3VyZW1lbnQgKHNlZSBTb3VyY2VXcmFwcGVyJ3MgcGluZ2luZyk7IG5vIGF1dGgsIGl0IG1lYXN1cmVzIHRoZSB0cmFuc3BvcnRcbiAgICBhc3luYyBwaW5nKCk6IFByb21pc2U8dm9pZD4geyB9XG5cbiAgICAvLyBQcm92ZXMgdGhlIGNhbGxlciBvd25zIHRoZSBtYWNoaW5lIGtleSBmb3IgdGhlIG1hY2hpbmVJZCBpbiBpdHMgY2VydGlmaWNhdGUuIFRoZSBzaWduYXR1cmVcbiAgICAvLyBtdXN0IGJlIGZyZXNoIGFuZCBib3VuZCB0byB0aGlzIHNlcnZlciwgc28gaXQgY2Fubm90IGJlIHJlcGxheWVkIHRvIChvciBmcm9tKSBvdGhlciBzZXJ2ZXJzLlxuICAgIGFzeW5jIGF1dGhlbnRpY2F0ZSh0b2tlbjogQXV0aFRva2VuKTogUHJvbWlzZTx7IG1hY2hpbmVJZDogc3RyaW5nOyBpcDogc3RyaW5nIH0+IHtcbiAgICAgICAgbGV0IHsgZG9tYWluLCBwb3J0IH0gPSBnZXRTdG9yYWdlU2VydmVyQ29uZmlnKCk7XG4gICAgICAgIGxldCBjYWxsZXIgPSBTb2NrZXRGdW5jdGlvbi5nZXRDYWxsZXIoKTtcbiAgICAgICAgaWYgKE1hdGguYWJzKERhdGUubm93KCkgLSB0b2tlbi50aW1lKSA+IEFVVEhfVElNRV9XSU5ET1cpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQXV0aCB0b2tlbiB0aW1lIGlzIHRvbyBmYXIgZnJvbSB0aGUgc2VydmVyIHRpbWUgKHRva2VuICR7dG9rZW4udGltZX0sIHNlcnZlciAke0RhdGUubm93KCl9LCBhbGxvd2VkIGRyaWZ0ICR7QVVUSF9USU1FX1dJTkRPV31tcylgKTtcbiAgICAgICAgfVxuICAgICAgICB2ZXJpZnkodG9rZW4uY2VydFBlbSwgdG9rZW4uc2lnbmF0dXJlLCB7XG4gICAgICAgICAgICBwdXJwb3NlOiBTVE9SQUdFX0FVVEhfUFVSUE9TRSxcbiAgICAgICAgICAgIHRpbWU6IHRva2VuLnRpbWUsXG4gICAgICAgICAgICBzZXJ2ZXI6IGAke2RvbWFpbn06JHtwb3J0fWAsXG4gICAgICAgIH0pO1xuICAgICAgICBsZXQgbWFjaGluZUlkID0gZ2V0Q29tbW9uTmFtZSh0b2tlbi5jZXJ0UGVtKS5zcGxpdChcIi5cIilbMF07XG4gICAgICAgIGlmICghdmVyaWZ5TWFjaGluZUlkRm9yUHVibGljS2V5KHsgbWFjaGluZUlkLCBwdWJsaWNLZXk6IGdldFB1YmxpY0lkZW50aWZpZXIodG9rZW4uY2VydFBlbSkgfSkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2VydGlmaWNhdGUgY29tbW9uIG5hbWUgJHtKU09OLnN0cmluZ2lmeShnZXRDb21tb25OYW1lKHRva2VuLmNlcnRQZW0pKX0gZG9lcyBub3QgbWF0Y2ggaXRzIHB1YmxpYyBrZXlgKTtcbiAgICAgICAgfVxuICAgICAgICBzZXNzaW9ucy5zZXQoY2FsbGVyLm5vZGVJZCwgbWFjaGluZUlkKTtcbiAgICAgICAgd2hpbGUgKHNlc3Npb25zLnNpemUgPiBNQVhfU0VTU0lPTlMpIHtcbiAgICAgICAgICAgIGxldCBvbGRlc3QgPSBzZXNzaW9ucy5rZXlzKCkubmV4dCgpLnZhbHVlO1xuICAgICAgICAgICAgaWYgKG9sZGVzdCA9PT0gdW5kZWZpbmVkKSBicmVhaztcbiAgICAgICAgICAgIHNlc3Npb25zLmRlbGV0ZShvbGRlc3QpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IG1hY2hpbmVJZCwgaXA6IGdldENhbGxlcklQKCkgfTtcbiAgICB9XG5cbiAgICAvLyBSZWNvcmRzIHRoYXQgdGhlIGNhbGxpbmcgbWFjaGluZSB3YW50cyBhY2Nlc3MgdG8gYW4gYWNjb3VudC4gUmVxdWVzdHMgYXJlIGtlcHQgcGVyIHJlcXVlc3RpbmdcbiAgICAvLyBJUCwgc28gdGhlIHN0b3JhZ2UgbWFjaGluZSdzIGFkbWluIGNhbiBsaXN0IHRoZW0gd2l0aCAtLWxpc3RBY2Nlc3MgPGlwPiBhbmQgZ3JhbnQgb25lLlxuICAgIGFzeW5jIHJlcXVlc3RBY2Nlc3MoYWNjb3VudDogc3RyaW5nKTogUHJvbWlzZTx7IG1hY2hpbmVJZDogc3RyaW5nOyBpcDogc3RyaW5nOyByZXF1ZXN0SWQ6IHN0cmluZzsgZ3JhbnRBY2Nlc3NDb21tYW5kOiBzdHJpbmcgfT4ge1xuICAgICAgICBhc3NlcnRWYWxpZE5hbWUoYWNjb3VudCwgXCJhY2NvdW50XCIpO1xuICAgICAgICBsZXQgbWFjaGluZUlkID0gZ2V0Q2FsbGVyTWFjaGluZUlkKCk7XG4gICAgICAgIGxldCBpcCA9IGdldENhbGxlcklQKCk7XG4gICAgICAgIGxldCByZXF1ZXN0c1N0b3JhZ2UgPSBhd2FpdCBnZXRSZXF1ZXN0cygpO1xuICAgICAgICBsZXQgcmVxdWVzdHMgPSBhd2FpdCByZXF1ZXN0c1N0b3JhZ2UuZ2V0KGlwKSB8fCBbXTtcbiAgICAgICAgbGV0IGV4aXN0aW5nID0gcmVxdWVzdHMuZmluZCh4ID0+IHguYWNjb3VudCA9PT0gYWNjb3VudCAmJiB4Lm1hY2hpbmVJZCA9PT0gbWFjaGluZUlkKTtcbiAgICAgICAgaWYgKGV4aXN0aW5nKSB7XG4gICAgICAgICAgICBleGlzdGluZy50aW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGV4aXN0aW5nID0ge1xuICAgICAgICAgICAgICAgIHJlcXVlc3RJZDogTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMiwgMTApLFxuICAgICAgICAgICAgICAgIGFjY291bnQsXG4gICAgICAgICAgICAgICAgbWFjaGluZUlkLFxuICAgICAgICAgICAgICAgIGlwLFxuICAgICAgICAgICAgICAgIHRpbWU6IERhdGUubm93KCksXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdHMucHVzaChleGlzdGluZyk7XG4gICAgICAgIH1cbiAgICAgICAgd2hpbGUgKHJlcXVlc3RzLmxlbmd0aCA+IE1BWF9SRVFVRVNUU19QRVJfSVApIHJlcXVlc3RzLnNoaWZ0KCk7XG4gICAgICAgIGF3YWl0IHJlcXVlc3RzU3RvcmFnZS5zZXQoaXAsIHJlcXVlc3RzKTtcbiAgICAgICAgcmV0dXJuIHsgbWFjaGluZUlkLCBpcCwgcmVxdWVzdElkOiBleGlzdGluZy5yZXF1ZXN0SWQsIGdyYW50QWNjZXNzQ29tbWFuZDogZ2V0R3JhbnRBY2Nlc3NDb21tYW5kKGV4aXN0aW5nLnJlcXVlc3RJZCkgfTtcbiAgICB9XG5cbiAgICBhc3luYyBnZXRBY2Nlc3NTdGF0ZShhY2NvdW50OiBzdHJpbmcpOiBQcm9taXNlPEFjY2Vzc1N0YXRlPiB7XG4gICAgICAgIGFzc2VydFZhbGlkTmFtZShhY2NvdW50LCBcImFjY291bnRcIik7XG4gICAgICAgIGxldCBtYWNoaW5lSWQgPSBnZXRDYWxsZXJNYWNoaW5lSWQoKTtcbiAgICAgICAgbGV0IGlwID0gZ2V0Q2FsbGVySVAoKTtcbiAgICAgICAgbGV0IHRydXN0ID0gYXdhaXQgZ2V0VHJ1c3QoKTtcbiAgICAgICAgbGV0IGhhc0FjY2VzcyA9IGlzQWRtaW4obWFjaGluZUlkKSB8fCAhIWF3YWl0IHRydXN0LmdldChgJHthY2NvdW50fXwke21hY2hpbmVJZH1gKTtcbiAgICAgICAgbGV0IHJlc3VsdDogQWNjZXNzU3RhdGUgPSB7IG1hY2hpbmVJZCwgaXAsIGhhc0FjY2VzcyB9O1xuICAgICAgICBpZiAoIWhhc0FjY2Vzcykge1xuICAgICAgICAgICAgbGV0IHJlcXVlc3RzID0gYXdhaXQgZ2V0UmVxdWVzdHMoKTtcbiAgICAgICAgICAgIGxldCBvd25SZXF1ZXN0ID0gKGF3YWl0IHJlcXVlc3RzLmdldChpcCkgfHwgW10pLmZpbmQoeCA9PiB4LmFjY291bnQgPT09IGFjY291bnQgJiYgeC5tYWNoaW5lSWQgPT09IG1hY2hpbmVJZCk7XG4gICAgICAgICAgICBpZiAob3duUmVxdWVzdCkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5ncmFudEFjY2Vzc0NvbW1hbmQgPSBnZXRHcmFudEFjY2Vzc0NvbW1hbmQob3duUmVxdWVzdC5yZXF1ZXN0SWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCB0cnVzdGVkTWFjaGluZXM6IFRydXN0UmVjb3JkW10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIGF3YWl0IHRydXN0LmdldEtleXMoKSkge1xuICAgICAgICAgICAgaWYgKCFrZXkuc3RhcnRzV2l0aChgJHthY2NvdW50fXxgKSkgY29udGludWU7XG4gICAgICAgICAgICBsZXQgcmVjb3JkID0gYXdhaXQgdHJ1c3QuZ2V0KGtleSk7XG4gICAgICAgICAgICBpZiAocmVjb3JkKSB0cnVzdGVkTWFjaGluZXMucHVzaChyZWNvcmQpO1xuICAgICAgICB9XG4gICAgICAgIHJlc3VsdC50cnVzdGVkTWFjaGluZXMgPSB0cnVzdGVkTWFjaGluZXM7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgLy8gQ2FsbGFibGUgYnkgYW55IG1hY2hpbmUgdGhhdCBoYXMgYWNjZXNzIHRvIGBhY2NvdW50YC4gUmV0dXJucyBwZW5kaW5nIGFjY2VzcyByZXF1ZXN0cyBmb3IgdGhlXG4gICAgLy8gYWNjb3VudCB0aGF0IGNvbWUgZnJvbSBFWEFDVExZIGBpcGAuIENhbGxlcnMgbXVzdCB0eXBlIGluIGFuIElQIGV4cGxpY2l0bHkg4oCUIHRoZSBzZXJ2ZXIgbmV2ZXJcbiAgICAvLyB2b2x1bnRlZXJzIGEgbGlzdCBvZiByZXF1ZXN0aW5nIElQcywgc28gYSB0cnVzdGVkIHVzZXIgY2FuJ3QgYWNjaWRlbnRhbGx5IGFwcHJvdmUgYSByYW5kb21cbiAgICAvLyByZXF1ZXN0IGZyb20gYSBtYWNoaW5lIHRoZXkgZGlkbid0IG1lYW4gdG8gdHJ1c3QuXG4gICAgYXN5bmMgbGlzdFJlcXVlc3RzRm9ySVAoYWNjb3VudDogc3RyaW5nLCBpcDogc3RyaW5nKTogUHJvbWlzZTxBY2Nlc3NSZXF1ZXN0W10+IHtcbiAgICAgICAgbGV0IHJlcXVlc3RzID0gYXdhaXQgZ2V0UmVxdWVzdHMoKTtcbiAgICAgICAgcmV0dXJuIChhd2FpdCByZXF1ZXN0cy5nZXQoaXApIHx8IFtdKS5maWx0ZXIoeCA9PiB4LmFjY291bnQgPT09IGFjY291bnQpO1xuICAgIH1cblxuICAgIC8vIENhbGxhYmxlIGJ5IGFueSBtYWNoaW5lIHRoYXQgaGFzIGFjY2VzcyB0byB0aGUgcmVxdWVzdCdzIGFjY291bnQgKG9yIGJ5IHRoZSBzdG9yYWdlLW1hY2hpbmVcbiAgICAvLyBhZG1pbikuIEdyYW50cyB0aGUgcmVxdWVzdGVkIGFjY2VzczsgdGhlIGNhbGxlciBtdXN0IHN1cHBseSB0aGUgc3BlY2lmaWMgcmVxdWVzdElkLCB3aGljaCB0aGV5XG4gICAgLy8gb25seSBnZXQgYnkgZXhwbGljaXRseSBsb29raW5nIHVwIHJlcXVlc3RzIGZvciBhIHNwZWNpZmljIElQLlxuICAgIGFzeW5jIGdyYW50QWNjZXNzKHJlcXVlc3RJZDogc3RyaW5nKTogUHJvbWlzZTxUcnVzdFJlY29yZD4ge1xuICAgICAgICAvLyBNdXN0IGNhcHR1cmUgaW4gdGhlIHN5bmNocm9ub3VzIHBoYXNlIOKAlCBTb2NrZXRGdW5jdGlvbi5nZXRDYWxsZXIoKSBvbmx5IHdvcmtzIGJlZm9yZSBhbnkgYXdhaXQuXG4gICAgICAgIGxldCBjYWxsZXJNYWNoaW5lSWQgPSBnZXRDYWxsZXJNYWNoaW5lSWQoKTtcbiAgICAgICAgbGV0IHRydXN0ID0gYXdhaXQgZ2V0VHJ1c3QoKTtcbiAgICAgICAgbGV0IHJlcXVlc3RzID0gYXdhaXQgZ2V0UmVxdWVzdHMoKTtcbiAgICAgICAgZm9yIChsZXQgaXAgb2YgYXdhaXQgcmVxdWVzdHMuZ2V0S2V5cygpKSB7XG4gICAgICAgICAgICBmb3IgKGxldCByZXF1ZXN0IG9mIGF3YWl0IHJlcXVlc3RzLmdldChpcCkgfHwgW10pIHtcbiAgICAgICAgICAgICAgICBpZiAocmVxdWVzdC5yZXF1ZXN0SWQgIT09IHJlcXVlc3RJZCkgY29udGludWU7XG4gICAgICAgICAgICAgICAgaWYgKCFpc0FkbWluKGNhbGxlck1hY2hpbmVJZCkgJiYgIWF3YWl0IHRydXN0LmdldChgJHtyZXF1ZXN0LmFjY291bnR9fCR7Y2FsbGVyTWFjaGluZUlkfWApKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgJHtTVE9SQUdFX0FDQ0VTU19ERU5JRUR9IE1hY2hpbmUgJHtjYWxsZXJNYWNoaW5lSWR9IGhhcyBubyBhY2Nlc3MgdG8gYWNjb3VudCAke0pTT04uc3RyaW5naWZ5KHJlcXVlc3QuYWNjb3VudCl9YCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGxldCByZWNvcmQ6IFRydXN0UmVjb3JkID0ge1xuICAgICAgICAgICAgICAgICAgICBhY2NvdW50OiByZXF1ZXN0LmFjY291bnQsXG4gICAgICAgICAgICAgICAgICAgIG1hY2hpbmVJZDogcmVxdWVzdC5tYWNoaW5lSWQsXG4gICAgICAgICAgICAgICAgICAgIGlwOiByZXF1ZXN0LmlwLFxuICAgICAgICAgICAgICAgICAgICB0aW1lOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgYXdhaXQgdHJ1c3Quc2V0KGAke3JlcXVlc3QuYWNjb3VudH18JHtyZXF1ZXN0Lm1hY2hpbmVJZH1gLCByZWNvcmQpO1xuICAgICAgICAgICAgICAgIHJldHVybiByZWNvcmQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBhY2Nlc3MgcmVxdWVzdCBmb3VuZCB3aXRoIGlkICR7SlNPTi5zdHJpbmdpZnkocmVxdWVzdElkKX0uIEl0IG1heSBoYXZlIGFscmVhZHkgYmVlbiBncmFudGVkIG9yIGV4cGlyZWQuYCk7XG4gICAgfVxuXG4gICAgLy8gQWRtaW4gKG11c3QgYmUgcnVuIGZyb20gdGhlIHN0b3JhZ2UgbWFjaGluZSBpdHNlbGYsIHdoaWNoIHNoYXJlcyB0aGUgc2VydmVyJ3MgbWFjaGluZUlkKS5cbiAgICAvLyBPbmx5IHJldHVybnMgcmVxdWVzdHMgZm9yIHRoZSBnaXZlbiBJUCwgc28geW91IGNhbm5vdCBhY2NpZGVudGFsbHkgZ3JhbnQgYSByZXF1ZXN0IGZyb20gYW5cbiAgICAvLyBJUCB5b3UgZGlkbid0IGV4cGxpY2l0bHkgdHlwZSBpbi5cbiAgICBhc3luYyBhZG1pbkxpc3RSZXF1ZXN0cyhpcDogc3RyaW5nKTogUHJvbWlzZTxBY2Nlc3NSZXF1ZXN0W10+IHtcbiAgICAgICAgbGV0IHJlcXVlc3RzID0gYXdhaXQgZ2V0UmVxdWVzdHMoKTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHJlcXVlc3RzLmdldChpcCkgfHwgW107XG4gICAgfVxuICAgIGFzeW5jIGFkbWluR3JhbnRBY2Nlc3MocmVxdWVzdElkOiBzdHJpbmcpOiBQcm9taXNlPFRydXN0UmVjb3JkPiB7XG4gICAgICAgIGxldCB0cnVzdCA9IGF3YWl0IGdldFRydXN0KCk7XG4gICAgICAgIGxldCByZXF1ZXN0cyA9IGF3YWl0IGdldFJlcXVlc3RzKCk7XG4gICAgICAgIGZvciAobGV0IGlwIG9mIGF3YWl0IHJlcXVlc3RzLmdldEtleXMoKSkge1xuICAgICAgICAgICAgZm9yIChsZXQgcmVxdWVzdCBvZiBhd2FpdCByZXF1ZXN0cy5nZXQoaXApIHx8IFtdKSB7XG4gICAgICAgICAgICAgICAgaWYgKHJlcXVlc3QucmVxdWVzdElkICE9PSByZXF1ZXN0SWQpIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIGxldCByZWNvcmQ6IFRydXN0UmVjb3JkID0ge1xuICAgICAgICAgICAgICAgICAgICBhY2NvdW50OiByZXF1ZXN0LmFjY291bnQsXG4gICAgICAgICAgICAgICAgICAgIG1hY2hpbmVJZDogcmVxdWVzdC5tYWNoaW5lSWQsXG4gICAgICAgICAgICAgICAgICAgIGlwOiByZXF1ZXN0LmlwLFxuICAgICAgICAgICAgICAgICAgICB0aW1lOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgYXdhaXQgdHJ1c3Quc2V0KGAke3JlcXVlc3QuYWNjb3VudH18JHtyZXF1ZXN0Lm1hY2hpbmVJZH1gLCByZWNvcmQpO1xuICAgICAgICAgICAgICAgIHJldHVybiByZWNvcmQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBhY2Nlc3MgcmVxdWVzdCBmb3VuZCB3aXRoIGlkICR7SlNPTi5zdHJpbmdpZnkocmVxdWVzdElkKX0uIEl0IG1heSBoYXZlIGFscmVhZHkgYmVlbiBncmFudGVkIG9yIGV4cGlyZWQuYCk7XG4gICAgfVxuXG4gICAgYXN5bmMgZ2V0KGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nLCBwYXRoOiBzdHJpbmcsIHJhbmdlPzogeyBzdGFydDogbnVtYmVyOyBlbmQ6IG51bWJlciB9KTogUHJvbWlzZTxCdWZmZXIgfCB1bmRlZmluZWQ+IHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IGF3YWl0IHRoaXMuZ2V0MihhY2NvdW50LCBidWNrZXROYW1lLCBwYXRoLCByYW5nZSk7XG4gICAgICAgIHJldHVybiByZXN1bHQgJiYgcmVzdWx0LmRhdGEgfHwgdW5kZWZpbmVkO1xuICAgIH1cbiAgICBhc3luYyBnZXQyKGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nLCBwYXRoOiBzdHJpbmcsIHJhbmdlPzogeyBzdGFydDogbnVtYmVyOyBlbmQ6IG51bWJlciB9KTogUHJvbWlzZTx7IGRhdGE6IEJ1ZmZlcjsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlciB9IHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGFzc2VydFZhbGlkUGF0aChwYXRoKTtcbiAgICAgICAgbGV0IGJ1Y2tldCA9IGF3YWl0IGdldEJ1Y2tldChhY2NvdW50LCBidWNrZXROYW1lKTtcbiAgICAgICAgaWYgKCFidWNrZXQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgIHJldHVybiBhd2FpdCBidWNrZXQuc3RvcmUuZ2V0MihwYXRoLCB7IHJhbmdlIH0pO1xuICAgIH1cbiAgICBhc3luYyBzZXQoYWNjb3VudDogc3RyaW5nLCBidWNrZXROYW1lOiBzdHJpbmcsIHBhdGg6IHN0cmluZywgZGF0YTogQnVmZmVyLCBsYXN0TW9kaWZpZWQ/OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXNzZXJ0VmFsaWROYW1lKGJ1Y2tldE5hbWUsIFwiYnVja2V0IG5hbWVcIik7XG4gICAgICAgIGFzc2VydFZhbGlkUGF0aChwYXRoKTtcbiAgICAgICAgLy8gSGFuZGxlcyBidWNrZXQgY3JlYXRpb24gKHdyaXRlcyBvZiBST1VUSU5HX0ZJTEUpLCByZWNvbmZpZ3VyYXRpb24sIGZhc3QgbW9kZSwgYW5kXG4gICAgICAgIC8vIGltbXV0YWJpbGl0eSDigJQgc2VlIHN0b3JhZ2VTZXJ2ZXJTdGF0ZS50c1xuICAgICAgICBhd2FpdCB3cml0ZUJ1Y2tldEZpbGUoYWNjb3VudCwgYnVja2V0TmFtZSwgcGF0aCwgQnVmZmVyLmZyb20oZGF0YSksIHsgbGFzdE1vZGlmaWVkIH0pO1xuICAgIH1cbiAgICBhc3luYyBkZWwoYWNjb3VudDogc3RyaW5nLCBidWNrZXROYW1lOiBzdHJpbmcsIHBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhc3NlcnRWYWxpZE5hbWUoYnVja2V0TmFtZSwgXCJidWNrZXQgbmFtZVwiKTtcbiAgICAgICAgYXNzZXJ0VmFsaWRQYXRoKHBhdGgpO1xuICAgICAgICBhd2FpdCBkZWxldGVCdWNrZXRGaWxlKGFjY291bnQsIGJ1Y2tldE5hbWUsIHBhdGgpO1xuICAgIH1cbiAgICBhc3luYyBnZXRJbmZvKGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nLCBwYXRoOiBzdHJpbmcpOiBQcm9taXNlPHsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlciB9IHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGFzc2VydFZhbGlkUGF0aChwYXRoKTtcbiAgICAgICAgbGV0IGJ1Y2tldCA9IGF3YWl0IGdldEJ1Y2tldChhY2NvdW50LCBidWNrZXROYW1lKTtcbiAgICAgICAgaWYgKCFidWNrZXQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgIHJldHVybiBhd2FpdCBidWNrZXQuc3RvcmUuZ2V0SW5mbyhwYXRoKTtcbiAgICB9XG4gICAgYXN5bmMgZmluZEluZm8oYWNjb3VudDogc3RyaW5nLCBidWNrZXROYW1lOiBzdHJpbmcsIHByZWZpeDogc3RyaW5nLCBjb25maWc/OiB7IHNoYWxsb3c/OiBib29sZWFuOyB0eXBlPzogXCJmaWxlc1wiIHwgXCJmb2xkZXJzXCIgfSk6IFByb21pc2U8QXJjaGl2ZUZpbGVJbmZvW10+IHtcbiAgICAgICAgbGV0IGJ1Y2tldCA9IGF3YWl0IGdldEJ1Y2tldChhY2NvdW50LCBidWNrZXROYW1lKTtcbiAgICAgICAgaWYgKCFidWNrZXQpIHJldHVybiBbXTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IGJ1Y2tldC5zdG9yZS5maW5kSW5mbyhwcmVmaXgsIGNvbmZpZyk7XG4gICAgfVxuICAgIC8vIEZhc3QgKHNlcnZlZCBmcm9tIHRoZSBzdG9yZSdzIEJ1bGtEYXRhYmFzZTIgaW5kZXgsIG5vdCBhIHNjYW4pIOKAlCBzZWUgSUFyY2hpdmVzLmdldENoYW5nZXNBZnRlclxuICAgIGFzeW5jIGdldENoYW5nZXNBZnRlcihhY2NvdW50OiBzdHJpbmcsIGJ1Y2tldE5hbWU6IHN0cmluZywgdGltZTogbnVtYmVyKTogUHJvbWlzZTxBcmNoaXZlRmlsZUluZm9bXT4ge1xuICAgICAgICBsZXQgYnVja2V0ID0gYXdhaXQgZ2V0QnVja2V0KGFjY291bnQsIGJ1Y2tldE5hbWUpO1xuICAgICAgICBpZiAoIWJ1Y2tldCkgcmV0dXJuIFtdO1xuICAgICAgICBpZiAoIWJ1Y2tldC5zdG9yZS5nZXRDaGFuZ2VzQWZ0ZXIpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQnVja2V0ICR7YWNjb3VudH0vJHtidWNrZXROYW1lfSBkb2VzIG5vdCBzdXBwb3J0IGdldENoYW5nZXNBZnRlciAocmF3RGlzayBidWNrZXRzIGhhdmUgbm8gaW5kZXgpYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGF3YWl0IGJ1Y2tldC5zdG9yZS5nZXRDaGFuZ2VzQWZ0ZXIodGltZSk7XG4gICAgfVxuICAgIGFzeW5jIGdldEFyY2hpdmVzQ29uZmlnKGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nKTogUHJvbWlzZTxBcmNoaXZlc0NvbmZpZz4ge1xuICAgICAgICBsZXQgYnVja2V0ID0gYXdhaXQgZ2V0QnVja2V0KGFjY291bnQsIGJ1Y2tldE5hbWUpO1xuICAgICAgICAvLyBNaXNzaW5nIGJ1Y2tldHMgc2F5IHRydWUsIG1hdGNoaW5nIHdoYXQgdGhleSBiZWNvbWUgb25jZSBjcmVhdGVkICh0aGUgZGVmYXVsdCBzdG9yZSB0eXBlKVxuICAgICAgICBsZXQgcHJvZ3Jlc3MgPSBidWNrZXQ/LnN0b3JlLmdldFN5bmNQcm9ncmVzcz8uKCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdXBwb3J0c0NoYW5nZXNBZnRlcjogIWJ1Y2tldCB8fCAhIWJ1Y2tldC5zdG9yZS5nZXRDaGFuZ2VzQWZ0ZXIsXG4gICAgICAgICAgICByZW1vdGVDb25maWc6IGJ1Y2tldD8ucm91dGluZyxcbiAgICAgICAgICAgIGluZGV4OiBwcm9ncmVzcz8uaW5kZXgsXG4gICAgICAgICAgICBpbmRleFNvdXJjZXM6IHByb2dyZXNzPy5zb3VyY2VzLFxuICAgICAgICAgICAgcmVhZGVyRGlza0xpbWl0OiBwcm9ncmVzcz8ucmVhZGVyRGlza0xpbWl0LFxuICAgICAgICAgICAgc3luY2luZzogcHJvZ3Jlc3M/LnN5bmNpbmcsXG4gICAgICAgIH07XG4gICAgfVxuICAgIC8qKiBXYWxrcyB0aGUgd2hvbGUgaW5kZXggZm9yIGV4YWN0IHRvdGFscyAob3ZlcmFsbCBhbmQgcGVyIGhvbGRpbmcgc291cmNlKSAtIG1vcmUgZXhwZW5zaXZlXG4gICAgICogIHRoYW4gdGhlIG1haW50YWluZWQgY291bnRlcnMgdGhhdCBnZXRBcmNoaXZlc0NvbmZpZyByZXR1cm5zLCBidXQgaW1tdW5lIHRvIGNvdW50ZXIgZHJpZnQuICovXG4gICAgYXN5bmMgZ2V0SW5kZXhJbmZvKGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nKTogUHJvbWlzZTx7IGZpbGVDb3VudDogbnVtYmVyOyBieXRlQ291bnQ6IG51bWJlcjsgc291cmNlczogeyBkZWJ1Z05hbWU6IHN0cmluZzsgZmlsZUNvdW50OiBudW1iZXI7IGJ5dGVDb3VudDogbnVtYmVyIH1bXSB9IHwgdW5kZWZpbmVkPiB7XG4gICAgICAgIGxldCBidWNrZXQgPSBhd2FpdCBnZXRCdWNrZXQoYWNjb3VudCwgYnVja2V0TmFtZSk7XG4gICAgICAgIGlmICghYnVja2V0IHx8ICFidWNrZXQuc3RvcmUuY29tcHV0ZUluZGV4VG90YWxzKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgICByZXR1cm4gYXdhaXQgYnVja2V0LnN0b3JlLmNvbXB1dGVJbmRleFRvdGFscygpO1xuICAgIH1cbiAgICBhc3luYyBnZXRTeW5jU3RhdHVzKGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nKTogUHJvbWlzZTxBcmNoaXZlc1N5bmNTdGF0dXM+IHtcbiAgICAgICAgbGV0IGJ1Y2tldCA9IGF3YWl0IGdldEJ1Y2tldChhY2NvdW50LCBidWNrZXROYW1lKTtcbiAgICAgICAgaWYgKCFidWNrZXQpIHJldHVybiB7IGFsbFNjYW5zQ29tcGxldGU6IHRydWUsIGluZGV4U2l6ZTogMCwgc291cmNlczogW10gfTtcbiAgICAgICAgaWYgKCFidWNrZXQuc3RvcmUuZ2V0U3luY1N0YXR1cykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBCdWNrZXQgJHthY2NvdW50fS8ke2J1Y2tldE5hbWV9IGRvZXMgbm90IHN1cHBvcnQgZ2V0U3luY1N0YXR1cyAocmF3RGlzayBidWNrZXRzIGhhdmUgbm8gc3luY2hyb25pemF0aW9uKWApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhd2FpdCBidWNrZXQuc3RvcmUuZ2V0U3luY1N0YXR1cygpO1xuICAgIH1cblxuICAgIGFzeW5jIHN0YXJ0TGFyZ2VGaWxlKGFjY291bnQ6IHN0cmluZywgYnVja2V0TmFtZTogc3RyaW5nLCBwYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgICAgICBhc3NlcnRXcml0ZXNBbGxvd2VkKCk7XG4gICAgICAgIC8vIFZhbGlkYXRlcyBub3csIHNvIHRoZSB1cGxvYWQgZG9lc24ndCBmYWlsIGF0IHRoZSBlbmRcbiAgICAgICAgYXNzZXJ0VmFsaWRQYXRoKHBhdGgpO1xuICAgICAgICBsZXQgYnVja2V0ID0gYXdhaXQgZ2V0QnVja2V0KGFjY291bnQsIGJ1Y2tldE5hbWUpO1xuICAgICAgICBpZiAoIWJ1Y2tldCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBCdWNrZXQgJHthY2NvdW50fS8ke2J1Y2tldE5hbWV9IGRvZXMgbm90IGV4aXN0LiBXcml0ZSBpdHMgcm91dGluZyBjb25maWcgdG8gJHtKU09OLnN0cmluZ2lmeShST1VUSU5HX0ZJTEUpfSB0byBjcmVhdGUgaXQuYCk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgYXNzZXJ0TXV0YWJsZShidWNrZXQsIHBhdGgsIERhdGUubm93KCkpO1xuICAgICAgICBsZXQgaWQgPSBhd2FpdCBidWNrZXQuc3RvcmUuc3RhcnRMYXJnZVVwbG9hZCgpO1xuICAgICAgICBsYXJnZVVwbG9hZEluZm8uc2V0KGlkLCB7IGFjY291bnQsIGJ1Y2tldE5hbWUsIHBhdGggfSk7XG4gICAgICAgIHJldHVybiBpZDtcbiAgICB9XG4gICAgYXN5bmMgdXBsb2FkUGFydCh1cGxvYWRJZDogc3RyaW5nLCBkYXRhOiBCdWZmZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXNzZXJ0V3JpdGVzQWxsb3dlZCgpO1xuICAgICAgICBsZXQgaW5mbyA9IGxhcmdlVXBsb2FkSW5mby5nZXQodXBsb2FkSWQpO1xuICAgICAgICBpZiAoIWluZm8pIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBsYXJnZSB1cGxvYWQgJHt1cGxvYWRJZH1gKTtcbiAgICAgICAgbGV0IGJ1Y2tldCA9IGF3YWl0IGdldEJ1Y2tldChpbmZvLmFjY291bnQsIGluZm8uYnVja2V0TmFtZSk7XG4gICAgICAgIGlmICghYnVja2V0KSB0aHJvdyBuZXcgRXJyb3IoYEJ1Y2tldCAke2luZm8uYWNjb3VudH0vJHtpbmZvLmJ1Y2tldE5hbWV9IG5vIGxvbmdlciBleGlzdHNgKTtcbiAgICAgICAgYXdhaXQgYnVja2V0LnN0b3JlLmFwcGVuZExhcmdlVXBsb2FkKHVwbG9hZElkLCBCdWZmZXIuZnJvbShkYXRhKSk7XG4gICAgfVxuICAgIGFzeW5jIGZpbmlzaExhcmdlRmlsZSh1cGxvYWRJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGFzc2VydFdyaXRlc0FsbG93ZWQoKTtcbiAgICAgICAgbGV0IGluZm8gPSBsYXJnZVVwbG9hZEluZm8uZ2V0KHVwbG9hZElkKTtcbiAgICAgICAgaWYgKCFpbmZvKSB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gbGFyZ2UgdXBsb2FkICR7dXBsb2FkSWR9YCk7XG4gICAgICAgIGxhcmdlVXBsb2FkSW5mby5kZWxldGUodXBsb2FkSWQpO1xuICAgICAgICBsZXQgYnVja2V0ID0gYXdhaXQgZ2V0QnVja2V0KGluZm8uYWNjb3VudCwgaW5mby5idWNrZXROYW1lKTtcbiAgICAgICAgaWYgKCFidWNrZXQpIHRocm93IG5ldyBFcnJvcihgQnVja2V0ICR7aW5mby5hY2NvdW50fS8ke2luZm8uYnVja2V0TmFtZX0gbm8gbG9uZ2VyIGV4aXN0c2ApO1xuICAgICAgICBhd2FpdCBidWNrZXQuc3RvcmUuZmluaXNoTGFyZ2VVcGxvYWQodXBsb2FkSWQsIGluZm8ucGF0aCk7XG4gICAgfVxuICAgIGFzeW5jIGNhbmNlbExhcmdlRmlsZSh1cGxvYWRJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGxldCBpbmZvID0gbGFyZ2VVcGxvYWRJbmZvLmdldCh1cGxvYWRJZCk7XG4gICAgICAgIGlmICghaW5mbykgcmV0dXJuO1xuICAgICAgICBsYXJnZVVwbG9hZEluZm8uZGVsZXRlKHVwbG9hZElkKTtcbiAgICAgICAgbGV0IGJ1Y2tldCA9IGF3YWl0IGdldEJ1Y2tldChpbmZvLmFjY291bnQsIGluZm8uYnVja2V0TmFtZSk7XG4gICAgICAgIGlmICghYnVja2V0KSByZXR1cm47XG4gICAgICAgIGF3YWl0IGJ1Y2tldC5zdG9yZS5jYW5jZWxMYXJnZVVwbG9hZCh1cGxvYWRJZCk7XG4gICAgfVxuXG4gICAgLy8gVGhlIHNlcnZlcidzIHNpbmdsZSBkZWZhdWx0IEhUVFAgcm91dGUuIC9maWxlLzxhY2NvdW50Pi88YnVja2V0TmFtZT4vPHBhdGg+IHNlcnZlcyBmaWxlcyBmcm9tXG4gICAgLy8gcHVibGljIGJ1Y2tldHMgb3ZlciBwbGFpbiBHRVRzIChzZWUgSUFyY2hpdmVzLmdldFVSTCk7IGV2ZXJ5IG90aGVyIHBhdGggc2VydmVzIHRoZSBhY2Nlc3NcbiAgICAvLyBwYWdlICh2aWEgUmVxdWlyZUNvbnRyb2xsZXIucmVxdWlyZUhUTUwg4oCUIHRoZSBwYXRoIGlzIHRoZSBhY2NvdW50IG5hbWUsIHNlZSBhY2Nlc3NQYWdlLnRzeCkuXG4gICAgYXN5bmMgaHR0cEVudHJ5KGNvbmZpZz86IHsgcmVxdWlyZUNhbGxzPzogc3RyaW5nW107IGNhY2hlVGltZT86IG51bWJlciB9KTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgICAgICAgLy8gQm90aCBhcmUga2V5ZWQgYnkgdGhlIGN1cnJlbnQgY2FsbCBhbmQgbXVzdCBiZSBjYXB0dXJlZCBzeW5jaHJvbm91c2x5LCBiZWZvcmUgYW55IGF3YWl0XG4gICAgICAgIGxldCBjYWxsZXIgPSBTb2NrZXRGdW5jdGlvbi5nZXRDYWxsZXIoKTtcbiAgICAgICAgbGV0IHJlcXVlc3QgPSBnZXRDdXJyZW50SFRUUFJlcXVlc3QoKTtcbiAgICAgICAgbGV0IHBhdGhuYW1lID0gbmV3IFVSTChyZXF1ZXN0Py51cmwgfHwgXCIvXCIsIFwiaHR0cHM6Ly9sb2NhbGhvc3RcIikucGF0aG5hbWU7XG4gICAgICAgIGlmICghcGF0aG5hbWUuc3RhcnRzV2l0aChcIi9maWxlL1wiKSkge1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHBlcmZvcm1Mb2NhbENhbGwoe1xuICAgICAgICAgICAgICAgIGNhbGxlcixcbiAgICAgICAgICAgICAgICBjYWxsOiB7IG5vZGVJZDogY2FsbGVyLm5vZGVJZCwgY2xhc3NHdWlkOiBSZXF1aXJlQ29udHJvbGxlci5fY2xhc3NHdWlkLCBmdW5jdGlvbk5hbWU6IFwicmVxdWlyZUhUTUxcIiwgYXJnczogW2NvbmZpZ10gfSxcbiAgICAgICAgICAgIH0pIGFzIEJ1ZmZlcjtcbiAgICAgICAgfVxuICAgICAgICBsZXQgcGFydHMgPSBwYXRobmFtZS5zcGxpdChcIi9cIikuZmlsdGVyKHggPT4geCkubWFwKGRlY29kZVVSSUNvbXBvbmVudCk7XG4gICAgICAgIGxldCBhY2NvdW50ID0gcGFydHNbMV07XG4gICAgICAgIGxldCBidWNrZXROYW1lID0gcGFydHNbMl07XG4gICAgICAgIGxldCBmaWxlUGF0aCA9IHBhcnRzLnNsaWNlKDMpLmpvaW4oXCIvXCIpO1xuICAgICAgICBpZiAoIWFjY291bnQgfHwgIWJ1Y2tldE5hbWUgfHwgIWZpbGVQYXRoKSB7XG4gICAgICAgICAgICByZXR1cm4gc2V0SFRUUFJlc3VsdEhlYWRlcnMoQnVmZmVyLmZyb20oXCJcIiksIHsgc3RhdHVzOiBcIjQwNFwiIH0pO1xuICAgICAgICB9XG4gICAgICAgIGFzc2VydFZhbGlkTmFtZShhY2NvdW50LCBcImFjY291bnRcIik7XG4gICAgICAgIGFzc2VydFZhbGlkTmFtZShidWNrZXROYW1lLCBcImJ1Y2tldCBuYW1lXCIpO1xuICAgICAgICBhc3NlcnRWYWxpZFBhdGgoZmlsZVBhdGgpO1xuICAgICAgICBsZXQgYnVja2V0ID0gYXdhaXQgZ2V0TG9hZGVkQnVja2V0KGFjY291bnQsIGJ1Y2tldE5hbWUpO1xuICAgICAgICBpZiAoIWJ1Y2tldCkge1xuICAgICAgICAgICAgcmV0dXJuIHNldEhUVFBSZXN1bHRIZWFkZXJzKEJ1ZmZlci5mcm9tKFwiXCIpLCB7IHN0YXR1czogXCI0MDRcIiB9KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWJ1Y2tldC5zZWxmPy5wdWJsaWMpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQnVja2V0ICR7YWNjb3VudH0vJHtidWNrZXROYW1lfSBpcyBub3QgcHVibGljLCBzbyBpdHMgZmlsZXMgY2Fubm90IGJlIHJlYWQgb3ZlciBwbGFpbiBVUkxzYCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGhlIGluZGV4IGFuc3dlcnMgZXhpc3RlbmNlICsgd3JpdGUgdGltZSArIHNpemUgd2l0aG91dCB0b3VjaGluZyBhbnkgZGF0YSwgc29cbiAgICAgICAgLy8gSWYtTW9kaWZpZWQtU2luY2UgYW5kIHJhbmdlIHZhbGlkYXRpb24gY29zdCBub3RoaW5nXG4gICAgICAgIGxldCBpbmZvID0gYXdhaXQgYnVja2V0LnN0b3JlLmdldEluZm8oZmlsZVBhdGgpO1xuICAgICAgICBpZiAoIWluZm8gfHwgIWluZm8uc2l6ZSkge1xuICAgICAgICAgICAgcmV0dXJuIHNldEhUVFBSZXN1bHRIZWFkZXJzKEJ1ZmZlci5mcm9tKFwiXCIpLCB7IHN0YXR1czogXCI0MDRcIiB9KTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgZXh0ID0gZmlsZVBhdGguc3BsaXQoXCIuXCIpLnBvcCgpIHx8IFwiXCI7XG4gICAgICAgIGxldCBoZWFkZXJzOiB7IFtoZWFkZXI6IHN0cmluZ106IHN0cmluZyB9ID0ge1xuICAgICAgICAgICAgXCJDb250ZW50LVR5cGVcIjogQ09OVEVOVF9UWVBFU1tleHQudG9Mb3dlckNhc2UoKV0gfHwgXCJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW1cIixcbiAgICAgICAgICAgIFwiTGFzdC1Nb2RpZmllZFwiOiBuZXcgRGF0ZShpbmZvLndyaXRlVGltZSkudG9VVENTdHJpbmcoKSxcbiAgICAgICAgICAgIFwiQWNjZXB0LVJhbmdlc1wiOiBcImJ5dGVzXCIsXG4gICAgICAgIH07XG4gICAgICAgIGlmIChidWNrZXQuc2VsZj8uaW1tdXRhYmxlKSB7XG4gICAgICAgICAgICBoZWFkZXJzW1wiQ2FjaGUtQ29udHJvbFwiXSA9IGBtYXgtYWdlPSR7SU1NVVRBQkxFX0NBQ0hFX1RJTUUgLyAxMDAwfWA7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGlmTW9kaWZpZWRTaW5jZSA9IHJlcXVlc3Q/LmhlYWRlcnNbXCJpZi1tb2RpZmllZC1zaW5jZVwiXTtcbiAgICAgICAgaWYgKHR5cGVvZiBpZk1vZGlmaWVkU2luY2UgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgIGxldCBzaW5jZSA9IG5ldyBEYXRlKGlmTW9kaWZpZWRTaW5jZSkuZ2V0VGltZSgpO1xuICAgICAgICAgICAgLy8gTGFzdC1Nb2RpZmllZCBpcyBzZXJ2ZWQgYXQgMSBzZWNvbmQgcmVzb2x1dGlvbiwgc28gY29tcGFyZSBhdCB0aGF0IHJlc29sdXRpb25cbiAgICAgICAgICAgIGlmIChzaW5jZSAmJiBNYXRoLmZsb29yKGluZm8ud3JpdGVUaW1lIC8gMTAwMCkgKiAxMDAwIDw9IHNpbmNlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHNldEhUVFBSZXN1bHRIZWFkZXJzKEJ1ZmZlci5mcm9tKFwiXCIpLCB7IC4uLmhlYWRlcnMsIHN0YXR1czogXCIzMDRcIiB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXQgcmFuZ2U6IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB8IHVuZGVmaW5lZDtcbiAgICAgICAgbGV0IHJhbmdlSGVhZGVyID0gcmVxdWVzdD8uaGVhZGVyc1tcInJhbmdlXCJdO1xuICAgICAgICBpZiAodHlwZW9mIHJhbmdlSGVhZGVyID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgICAvLyBTaW5nbGUtcmFuZ2UgZm9ybSBvbmx5IChieXRlcz1zdGFydC1lbmQgLyBzdGFydC0gLyAtc3VmZml4KTsgYW55dGhpbmcgZWxzZSBzZXJ2ZXMgdGhlIGZ1bGwgZmlsZVxuICAgICAgICAgICAgbGV0IG1hdGNoID0gL15ieXRlcz0oXFxkKiktKFxcZCopJC8uZXhlYyhyYW5nZUhlYWRlci50cmltKCkpO1xuICAgICAgICAgICAgaWYgKG1hdGNoICYmIChtYXRjaFsxXSB8fCBtYXRjaFsyXSkpIHtcbiAgICAgICAgICAgICAgICBsZXQgc3RhcnQ6IG51bWJlcjtcbiAgICAgICAgICAgICAgICBsZXQgZW5kSW5jbHVzaXZlID0gaW5mby5zaXplIC0gMTtcbiAgICAgICAgICAgICAgICBpZiAoIW1hdGNoWzFdKSB7XG4gICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gTWF0aC5tYXgoMCwgaW5mby5zaXplIC0gK21hdGNoWzJdKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBzdGFydCA9ICttYXRjaFsxXTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKG1hdGNoWzJdKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBlbmRJbmNsdXNpdmUgPSBNYXRoLm1pbigrbWF0Y2hbMl0sIGluZm8uc2l6ZSAtIDEpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChzdGFydCA+PSBpbmZvLnNpemUgfHwgc3RhcnQgPiBlbmRJbmNsdXNpdmUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHNldEhUVFBSZXN1bHRIZWFkZXJzKEJ1ZmZlci5mcm9tKFwiXCIpLCB7IC4uLmhlYWRlcnMsIFwiQ29udGVudC1SYW5nZVwiOiBgYnl0ZXMgKi8ke2luZm8uc2l6ZX1gLCBzdGF0dXM6IFwiNDE2XCIgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJhbmdlID0geyBzdGFydCwgZW5kOiBlbmRJbmNsdXNpdmUgKyAxIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHJlc3VsdCA9IGF3YWl0IGJ1Y2tldC5zdG9yZS5nZXQyKGZpbGVQYXRoLCB7IHJhbmdlIH0pO1xuICAgICAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgICAgICAgcmV0dXJuIHNldEhUVFBSZXN1bHRIZWFkZXJzKEJ1ZmZlci5mcm9tKFwiXCIpLCB7IHN0YXR1czogXCI0MDRcIiB9KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAocmFuZ2UpIHtcbiAgICAgICAgICAgIHJldHVybiBzZXRIVFRQUmVzdWx0SGVhZGVycyhyZXN1bHQuZGF0YSwgeyAuLi5oZWFkZXJzLCBcIkNvbnRlbnQtUmFuZ2VcIjogYGJ5dGVzICR7cmFuZ2Uuc3RhcnR9LSR7cmFuZ2UuZW5kIC0gMX0vJHtpbmZvLnNpemV9YCwgc3RhdHVzOiBcIjIwNlwiIH0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzZXRIVFRQUmVzdWx0SGVhZGVycyhyZXN1bHQuZGF0YSwgaGVhZGVycyk7XG4gICAgfVxufVxuXG5jb25zdCBsYXJnZVVwbG9hZEluZm8gPSBuZXcgTWFwPHN0cmluZywgeyBhY2NvdW50OiBzdHJpbmc7IGJ1Y2tldE5hbWU6IHN0cmluZzsgcGF0aDogc3RyaW5nIH0+KCk7XG5cbi8vIEFjY2VzcyBjaGVja3MgcnVuIGFzIGhvb2tzIG9uIHRoZSByZWdpc3RlciBzaGFwZSBiZWxvdywga2V5ZWQgb2ZmIHRoZSBjYWxsJ3MgYXJndW1lbnRzLCBzbyB0aGVcbi8vIG1ldGhvZCBib2RpZXMgZG9uJ3QgZWFjaCByZXBlYXQgdGhlbVxuY29uc3QgYWNjb3VudEFjY2VzczogU29ja2V0RnVuY3Rpb25Ib29rID0gYXN5bmMgKGNvbnRleHQpID0+IHtcbiAgICBhd2FpdCByZXF1aXJlQWNjZXNzKFN0cmluZyhjb250ZXh0LmNhbGwuYXJnc1swXSkpO1xufTtcbmNvbnN0IHVwbG9hZEFjY2VzczogU29ja2V0RnVuY3Rpb25Ib29rID0gYXN5bmMgKGNvbnRleHQpID0+IHtcbiAgICBsZXQgaW5mbyA9IGxhcmdlVXBsb2FkSW5mby5nZXQoU3RyaW5nKGNvbnRleHQuY2FsbC5hcmdzWzBdKSk7XG4gICAgLy8gVW5rbm93biB1cGxvYWQgaWRzIGFyZSBoYW5kbGVkIGJ5IHRoZSBtZXRob2RzIHRoZW1zZWx2ZXMgKHRocm93IC8gbm8tb3ApXG4gICAgaWYgKCFpbmZvKSByZXR1cm47XG4gICAgYXdhaXQgcmVxdWlyZUFjY2VzcyhpbmZvLmFjY291bnQpO1xufTtcbmNvbnN0IGFkbWluQWNjZXNzOiBTb2NrZXRGdW5jdGlvbkhvb2sgPSBhc3luYyAoKSA9PiB7XG4gICAgcmVxdWlyZUFkbWluKCk7XG59O1xuXG5leHBvcnQgY29uc3QgUmVtb3RlU3RvcmFnZUNvbnRyb2xsZXIgPSBTb2NrZXRGdW5jdGlvbi5yZWdpc3RlcihcbiAgICBSRU1PVEVfU1RPUkFHRV9DTEFTU19HVUlELFxuICAgIG5ldyBSZW1vdGVTdG9yYWdlQ29udHJvbGxlckJhc2UoKSxcbiAgICAoKSA9PiAoe1xuICAgICAgICBwaW5nOiB7fSxcbiAgICAgICAgYXV0aGVudGljYXRlOiB7fSxcbiAgICAgICAgcmVxdWVzdEFjY2Vzczoge30sXG4gICAgICAgIGdldEFjY2Vzc1N0YXRlOiB7fSxcbiAgICAgICAgbGlzdFJlcXVlc3RzRm9ySVA6IHsgaG9va3M6IFthY2NvdW50QWNjZXNzXSB9LFxuICAgICAgICBncmFudEFjY2Vzczoge30sXG4gICAgICAgIGFkbWluTGlzdFJlcXVlc3RzOiB7IGhvb2tzOiBbYWRtaW5BY2Nlc3NdIH0sXG4gICAgICAgIGFkbWluR3JhbnRBY2Nlc3M6IHsgaG9va3M6IFthZG1pbkFjY2Vzc10gfSxcbiAgICAgICAgZ2V0OiB7IGhvb2tzOiBbYWNjb3VudEFjY2Vzc10gfSxcbiAgICAgICAgZ2V0MjogeyBob29rczogW2FjY291bnRBY2Nlc3NdIH0sXG4gICAgICAgIHNldDogeyBob29rczogW2FjY291bnRBY2Nlc3NdIH0sXG4gICAgICAgIGRlbDogeyBob29rczogW2FjY291bnRBY2Nlc3NdIH0sXG4gICAgICAgIGdldEluZm86IHsgaG9va3M6IFthY2NvdW50QWNjZXNzXSB9LFxuICAgICAgICBmaW5kSW5mbzogeyBob29rczogW2FjY291bnRBY2Nlc3NdIH0sXG4gICAgICAgIGdldENoYW5nZXNBZnRlcjogeyBob29rczogW2FjY291bnRBY2Nlc3NdIH0sXG4gICAgICAgIGdldEFyY2hpdmVzQ29uZmlnOiB7IGhvb2tzOiBbYWNjb3VudEFjY2Vzc10gfSxcbiAgICAgICAgZ2V0SW5kZXhJbmZvOiB7IGhvb2tzOiBbYWNjb3VudEFjY2Vzc10gfSxcbiAgICAgICAgZ2V0U3luY1N0YXR1czogeyBob29rczogW2FjY291bnRBY2Nlc3NdIH0sXG4gICAgICAgIHN0YXJ0TGFyZ2VGaWxlOiB7IGhvb2tzOiBbYWNjb3VudEFjY2Vzc10gfSxcbiAgICAgICAgdXBsb2FkUGFydDogeyBob29rczogW3VwbG9hZEFjY2Vzc10gfSxcbiAgICAgICAgZmluaXNoTGFyZ2VGaWxlOiB7IGhvb2tzOiBbdXBsb2FkQWNjZXNzXSB9LFxuICAgICAgICBjYW5jZWxMYXJnZUZpbGU6IHsgaG9va3M6IFt1cGxvYWRBY2Nlc3NdIH0sXG4gICAgICAgIGh0dHBFbnRyeToge30sXG4gICAgfSksXG4gICAgKCkgPT4gKHtcbiAgICAgICAgbm9DbGllbnRIb29rczogdHJ1ZSxcbiAgICAgICAgbm9EZWZhdWx0SG9va3M6IHRydWUsXG4gICAgfSlcbik7XG4iXX0=
|
|
500
|
+
/* _JS_SOURCE_HASH = "1f9c71eed3bbc12aa14eb6d02745131cca48eb4ea142276c2937d85ae7bf92db"; */
|