@mnaoumov/obsidian-lib 10.2.0 → 10.4.0
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/command-handlers/undo-bulk-operation-command-handler/package.json +6 -0
- package/components/bulk-operation-commit-component/package.json +6 -0
- package/dist/lib/cjs/__merged.cjs +42 -1
- package/dist/lib/cjs/__merged.d.cts +5 -0
- package/dist/lib/cjs/bulk-operation-progress.cjs +120 -0
- package/dist/lib/cjs/bulk-operation-progress.d.cts +33 -0
- package/dist/lib/cjs/bulk-operation.cjs +220 -0
- package/dist/lib/cjs/bulk-operation.d.cts +237 -0
- package/dist/lib/cjs/command-handlers/index.cjs +4 -1
- package/dist/lib/cjs/command-handlers/index.d.cts +1 -0
- package/dist/lib/cjs/command-handlers/undo-bulk-operation-command-handler.cjs +164 -0
- package/dist/lib/cjs/command-handlers/undo-bulk-operation-command-handler.d.cts +51 -0
- package/dist/lib/cjs/components/bulk-operation-commit-component.cjs +337 -0
- package/dist/lib/cjs/components/bulk-operation-commit-component.d.cts +152 -0
- package/dist/lib/cjs/components/index.cjs +4 -1
- package/dist/lib/cjs/components/index.d.cts +1 -0
- package/dist/lib/cjs/desktop-bulk-operation-git.cjs +534 -0
- package/dist/lib/cjs/desktop-bulk-operation-git.d.cts +196 -0
- package/dist/lib/cjs/generated-during-build.cjs +1 -1
- package/dist/lib/cjs/i18n/locales/en.cjs +10 -1
- package/dist/lib/cjs/i18n/locales/en.d.cts +9 -0
- package/dist/lib/cjs/i18n/locales/translations-map.d.cts +9 -0
- package/dist/lib/cjs/index.cjs +10 -1
- package/dist/lib/cjs/index.d.cts +3 -0
- package/dist/lib/cjs/link.cjs +1 -1
- package/dist/lib/cjs/link.d.cts +24 -6
- package/dist/lib/cjs/loop.cjs +3 -1
- package/dist/lib/esm/__merged.d.mts +5 -0
- package/dist/lib/esm/__merged.mjs +43 -1
- package/dist/lib/esm/bulk-operation-progress.d.mts +33 -0
- package/dist/lib/esm/bulk-operation-progress.mjs +31 -0
- package/dist/lib/esm/bulk-operation.d.mts +237 -0
- package/dist/lib/esm/bulk-operation.mjs +125 -0
- package/dist/lib/esm/command-handlers/index.d.mts +1 -0
- package/dist/lib/esm/command-handlers/index.mjs +3 -1
- package/dist/lib/esm/command-handlers/undo-bulk-operation-command-handler.d.mts +51 -0
- package/dist/lib/esm/command-handlers/undo-bulk-operation-command-handler.mjs +76 -0
- package/dist/lib/esm/components/bulk-operation-commit-component.d.mts +152 -0
- package/dist/lib/esm/components/bulk-operation-commit-component.mjs +245 -0
- package/dist/lib/esm/components/index.d.mts +1 -0
- package/dist/lib/esm/components/index.mjs +3 -1
- package/dist/lib/esm/desktop-bulk-operation-git.d.mts +196 -0
- package/dist/lib/esm/desktop-bulk-operation-git.mjs +463 -0
- package/dist/lib/esm/generated-during-build.mjs +1 -1
- package/dist/lib/esm/i18n/locales/en.d.mts +9 -0
- package/dist/lib/esm/i18n/locales/en.mjs +10 -1
- package/dist/lib/esm/i18n/locales/translations-map.d.mts +9 -0
- package/dist/lib/esm/index.d.mts +3 -0
- package/dist/lib/esm/index.mjs +7 -1
- package/dist/lib/esm/link.d.mts +24 -6
- package/dist/lib/esm/link.mjs +1 -1
- package/dist/lib/esm/loop.mjs +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/*
|
|
2
|
+
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
3
|
+
if you want to view the source, please visit the github repository of this plugin
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
function ensureBrowserProcess() {
|
|
7
|
+
const browserProcess = {
|
|
8
|
+
browser: true,
|
|
9
|
+
cwd() {
|
|
10
|
+
return "/";
|
|
11
|
+
},
|
|
12
|
+
env: {},
|
|
13
|
+
platform: "android"
|
|
14
|
+
};
|
|
15
|
+
const existingProcess = globalThis.process;
|
|
16
|
+
if (!existingProcess) {
|
|
17
|
+
globalThis.process = browserProcess;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (existingProcess.versions?.node) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const existingProcessRecord = existingProcess;
|
|
24
|
+
for (const [key, value] of Object.entries(browserProcess)) {
|
|
25
|
+
existingProcessRecord[key] ??= value;
|
|
26
|
+
}
|
|
27
|
+
existingProcessRecord["browser"] = true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
ensureBrowserProcess();
|
|
31
|
+
})();
|
|
32
|
+
|
|
33
|
+
import { setTimeoutAsync } from "@mnaoumov/typescript-lib/async";
|
|
34
|
+
import { noopAsync } from "@mnaoumov/typescript-lib/function";
|
|
35
|
+
import { escapeRegExp } from "@mnaoumov/typescript-lib/reg-exp";
|
|
36
|
+
import { ensureNonNullable } from "@mnaoumov/typescript-lib/type-guards";
|
|
37
|
+
import { execFile } from "node:child_process";
|
|
38
|
+
import {
|
|
39
|
+
readFile,
|
|
40
|
+
rename,
|
|
41
|
+
stat,
|
|
42
|
+
unlink,
|
|
43
|
+
writeFile
|
|
44
|
+
} from "node:fs/promises";
|
|
45
|
+
import {
|
|
46
|
+
hostname,
|
|
47
|
+
uptime
|
|
48
|
+
} from "node:os";
|
|
49
|
+
import { join } from "node:path";
|
|
50
|
+
import { registerBulkOperationProgressListener } from "./bulk-operation-progress.mjs";
|
|
51
|
+
import {
|
|
52
|
+
buildBeforeOperationCommitSubject,
|
|
53
|
+
buildOperationCommitMessage,
|
|
54
|
+
BULK_OPERATION_LOCK_FILE_NAME,
|
|
55
|
+
getNextOperationId,
|
|
56
|
+
isBulkOperationLock,
|
|
57
|
+
isBulkOperationLockStale,
|
|
58
|
+
parseBulkOperationCommit,
|
|
59
|
+
PLUGIN_TRAILER_KEY
|
|
60
|
+
} from "./bulk-operation.mjs";
|
|
61
|
+
const DEFAULT_LOCK_WAIT_TIMEOUT_IN_MILLISECONDS = 6e5;
|
|
62
|
+
const HEARTBEAT_THROTTLE_IN_MILLISECONDS = 5e3;
|
|
63
|
+
const INDEX_LOCK_RETRY_COUNT = 20;
|
|
64
|
+
const INDEX_LOCK_RETRY_DELAY_IN_MILLISECONDS = 500;
|
|
65
|
+
const LOCK_POLL_INTERVAL_IN_MILLISECONDS = 1e3;
|
|
66
|
+
const MAX_BUFFER_IN_BYTES = 268435456;
|
|
67
|
+
const MILLISECONDS_IN_SECOND = 1e3;
|
|
68
|
+
const GIT_FATAL_EXIT_CODE = 128;
|
|
69
|
+
const REV_PARSE_OUTPUT_LINE_COUNT = 2;
|
|
70
|
+
const UNIT_SEPARATOR = "";
|
|
71
|
+
class BulkOperationGitRepo {
|
|
72
|
+
/**
|
|
73
|
+
* The absolute path of the repo's git directory.
|
|
74
|
+
*/
|
|
75
|
+
gitDirectory;
|
|
76
|
+
/**
|
|
77
|
+
* The absolute path of the folder whose changes are committed.
|
|
78
|
+
*/
|
|
79
|
+
workingDirectory;
|
|
80
|
+
lockFilePath;
|
|
81
|
+
lockWaitTimeoutInMilliseconds;
|
|
82
|
+
ownLock = null;
|
|
83
|
+
/**
|
|
84
|
+
* Creates a new instance. Use {@link BulkOperationGitRepo.open} to find the repo of a folder.
|
|
85
|
+
*
|
|
86
|
+
* @param params - The constructor parameters.
|
|
87
|
+
*/
|
|
88
|
+
constructor(params) {
|
|
89
|
+
this.gitDirectory = params.gitDirectory;
|
|
90
|
+
this.workingDirectory = params.workingDirectory;
|
|
91
|
+
this.lockWaitTimeoutInMilliseconds = params.lockWaitTimeoutInMilliseconds ?? DEFAULT_LOCK_WAIT_TIMEOUT_IN_MILLISECONDS;
|
|
92
|
+
this.lockFilePath = join(this.gitDirectory, BULK_OPERATION_LOCK_FILE_NAME);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Finds the git repo a folder belongs to.
|
|
96
|
+
*
|
|
97
|
+
* @param workingDirectory - The folder, normally the vault root.
|
|
98
|
+
* @returns The repo, or `null` when the folder is not inside a git work tree or `git` cannot be run.
|
|
99
|
+
*/
|
|
100
|
+
static async open(workingDirectory) {
|
|
101
|
+
let result;
|
|
102
|
+
try {
|
|
103
|
+
result = await execGit({ cwd: workingDirectory, gitArguments: ["rev-parse", "--is-inside-work-tree", "--absolute-git-dir"] });
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const [isInsideWorkTree, gitDirectory] = result.stdout.split(/\r?\n/, REV_PARSE_OUTPUT_LINE_COUNT);
|
|
108
|
+
return result.exitCode !== 0 || isInsideWorkTree !== "true" || !gitDirectory ? null : new BulkOperationGitRepo({ gitDirectory, workingDirectory });
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Refuses to go on when the repo routes files through git-annex but `git-annex` cannot be run, since a
|
|
112
|
+
* plain `git add` would then put the large files into git itself.
|
|
113
|
+
*/
|
|
114
|
+
async assertCanCommitLargeFiles() {
|
|
115
|
+
const annexFilters = await this.runGit({ allowedExitCodes: [0, 1], gitArguments: ["config", "--get-regexp", String.raw`^filter\.annex\.`] });
|
|
116
|
+
if (!annexFilters.stdout.trim()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const annexVersion = await this.runGit({ gitArguments: ["annex", "version", "--raw"] }).catch(() => null);
|
|
120
|
+
if (annexVersion === null) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
"The vault repository routes files through git-annex, but git-annex is not on PATH. Bulk operation commits are refused, because they would add large files to git itself."
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Lists the bulk-operation commits a plugin made on the current branch, newest first.
|
|
128
|
+
*
|
|
129
|
+
* @param pluginId - The plugin id.
|
|
130
|
+
* @returns The commits.
|
|
131
|
+
*/
|
|
132
|
+
async listBulkOperationCommits(pluginId) {
|
|
133
|
+
const result = await this.runGit({
|
|
134
|
+
allowedExitCodes: [0, GIT_FATAL_EXIT_CODE],
|
|
135
|
+
gitArguments: [
|
|
136
|
+
"log",
|
|
137
|
+
`--format=%H${UNIT_SEPARATOR}%aI${UNIT_SEPARATOR}%s`,
|
|
138
|
+
"--extended-regexp",
|
|
139
|
+
`--grep=^${PLUGIN_TRAILER_KEY}: ${escapeRegExp(pluginId)}$`
|
|
140
|
+
]
|
|
141
|
+
});
|
|
142
|
+
if (result.exitCode !== 0) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
const commits = [];
|
|
146
|
+
for (const line of result.stdout.split(/\r?\n/)) {
|
|
147
|
+
const [sha = "", date = "", ...subjectParts] = line.split(UNIT_SEPARATOR);
|
|
148
|
+
const commit = parseBulkOperationCommit({ date, sha, subject: subjectParts.join(UNIT_SEPARATOR) });
|
|
149
|
+
if (commit) {
|
|
150
|
+
commits.push(commit);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return commits;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Commits whatever a stale lock's operation left behind as `OP<n> interrupted` and removes the lock.
|
|
157
|
+
*
|
|
158
|
+
* @returns The id of the recovered operation, or `null` when there was no stale lock.
|
|
159
|
+
*/
|
|
160
|
+
async recoverStaleLock() {
|
|
161
|
+
const lock = await this.readLock();
|
|
162
|
+
return !lock || !this.isStale(lock) ? null : await this.recover(lock);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Reverts a bulk-operation commit with `git revert --no-edit`.
|
|
166
|
+
*
|
|
167
|
+
* Whatever is pending is committed first, so the revert starts from a clean tree. A conflict is NOT aborted: the
|
|
168
|
+
* repo is left mid-revert for the user to resolve.
|
|
169
|
+
*
|
|
170
|
+
* @param commit - The commit to revert.
|
|
171
|
+
* @returns Whether the revert stopped on a conflict, and what git printed.
|
|
172
|
+
*/
|
|
173
|
+
async revertBulkOperationCommit(commit) {
|
|
174
|
+
await this.assertCanCommitLargeFiles();
|
|
175
|
+
await this.acquireLock();
|
|
176
|
+
try {
|
|
177
|
+
await this.commitPending({ body: "", subject: `chore: before revert of ${commit.operationId}` });
|
|
178
|
+
const result = await this.runGit({ allowedExitCodes: [0, 1], gitArguments: ["revert", "--no-edit", commit.sha] });
|
|
179
|
+
return { isConflict: result.exitCode !== 0, output: `${result.stdout}${result.stderr}`.trim() };
|
|
180
|
+
} finally {
|
|
181
|
+
await this.releaseLock();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Runs a bulk operation between two commits.
|
|
186
|
+
*
|
|
187
|
+
* Takes the lease lock, commits whatever is pending as `chore: before OP<n>`, runs the operation, waits for its
|
|
188
|
+
* follow-ups to settle, commits the result as `OP<n>: <summary>` and releases the lock. An operation that throws
|
|
189
|
+
* still gets its partial changes committed, as `OP<n> interrupted`, before the error is rethrown.
|
|
190
|
+
*
|
|
191
|
+
* @typeParam T - The type of the operation's result.
|
|
192
|
+
* @param params - The parameters.
|
|
193
|
+
* @returns The operation's result, its id and the hash of its commit.
|
|
194
|
+
*/
|
|
195
|
+
async runBulkOperation(params) {
|
|
196
|
+
await this.assertCanCommitLargeFiles();
|
|
197
|
+
await this.acquireLock();
|
|
198
|
+
let unregisterProgressListener = null;
|
|
199
|
+
try {
|
|
200
|
+
const operationId = await this.allocateOperationId(params.pluginId);
|
|
201
|
+
await this.commitPending({ body: "", subject: buildBeforeOperationCommitSubject(operationId) });
|
|
202
|
+
unregisterProgressListener = registerBulkOperationProgressListener(this.createThrottledHeartbeat());
|
|
203
|
+
let result;
|
|
204
|
+
try {
|
|
205
|
+
result = await params.operation();
|
|
206
|
+
await params.settle();
|
|
207
|
+
} catch (error) {
|
|
208
|
+
await this.heartbeat();
|
|
209
|
+
await this.commitOperation({ isInterrupted: true, operationId, pluginId: params.pluginId });
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
await this.heartbeat();
|
|
213
|
+
const commitSha = await this.commitOperation({ operationId, pluginId: params.pluginId, summary: params.summary });
|
|
214
|
+
return { commitSha, operationId, result };
|
|
215
|
+
} finally {
|
|
216
|
+
unregisterProgressListener?.();
|
|
217
|
+
await this.releaseLock();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async acquireLock() {
|
|
221
|
+
const deadline = Date.now() + this.lockWaitTimeoutInMilliseconds;
|
|
222
|
+
for (; ; ) {
|
|
223
|
+
if (await this.tryCreateLock()) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const lock = await this.readLock();
|
|
227
|
+
if (!lock) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (this.isStale(lock)) {
|
|
231
|
+
await this.recover(lock);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (Date.now() >= deadline) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Timed out waiting for ${lock.operationId || "an operation"} of ${lock.pluginId} on ${lock.host} to release the bulk operation lock ${this.lockFilePath}.`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
await setTimeoutAsync(LOCK_POLL_INTERVAL_IN_MILLISECONDS);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async allocateOperationId(pluginId) {
|
|
243
|
+
const operationId = await this.findNextOperationId();
|
|
244
|
+
const ownLock = ensureNonNullable(this.ownLock);
|
|
245
|
+
this.ownLock = { ...ownLock, heartbeatAt: Date.now(), operationId, pluginId };
|
|
246
|
+
await this.writeLock(this.ownLock);
|
|
247
|
+
return operationId;
|
|
248
|
+
}
|
|
249
|
+
async commitOperation(params) {
|
|
250
|
+
const changedFileCount = await this.stageAll();
|
|
251
|
+
if (changedFileCount === 0) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
const message = buildOperationCommitMessage({ ...params, changedFileCount, timestamp: /* @__PURE__ */ new Date() });
|
|
255
|
+
await this.commitStaged(message);
|
|
256
|
+
const headResult = await this.runGit({ gitArguments: ["rev-parse", "HEAD"] });
|
|
257
|
+
return headResult.stdout.trim();
|
|
258
|
+
}
|
|
259
|
+
async commitPending(message) {
|
|
260
|
+
if (await this.stageAll() === 0) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
await this.commitStaged(message);
|
|
264
|
+
}
|
|
265
|
+
async commitStaged(message) {
|
|
266
|
+
const gitArguments = ["commit", "--quiet", "-m", message.subject];
|
|
267
|
+
if (message.body) {
|
|
268
|
+
gitArguments.push("-m", message.body);
|
|
269
|
+
}
|
|
270
|
+
gitArguments.push("--", ".");
|
|
271
|
+
await this.runGit({ gitArguments });
|
|
272
|
+
}
|
|
273
|
+
createThrottledHeartbeat() {
|
|
274
|
+
let lastHeartbeatAt = Date.now();
|
|
275
|
+
let pendingHeartbeat = noopAsync();
|
|
276
|
+
return () => {
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
if (now - lastHeartbeatAt < HEARTBEAT_THROTTLE_IN_MILLISECONDS) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
lastHeartbeatAt = now;
|
|
282
|
+
pendingHeartbeat = pendingHeartbeat.then(() => this.heartbeat()).catch((error) => {
|
|
283
|
+
console.error("Failed to refresh the bulk operation lock heartbeat", error);
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
async findNextOperationId() {
|
|
288
|
+
const result = await this.runGit({
|
|
289
|
+
gitArguments: ["log", "--all", "--format=%s", "--extended-regexp", "--grep=^(chore: before )?OP[0-9]+"]
|
|
290
|
+
});
|
|
291
|
+
return getNextOperationId(result.stdout.split(/\r?\n/));
|
|
292
|
+
}
|
|
293
|
+
async heartbeat() {
|
|
294
|
+
const lock = await this.readLock();
|
|
295
|
+
if (!lock || !this.isOwnLock(lock)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
await this.writeLock({ ...lock, heartbeatAt: Date.now() });
|
|
299
|
+
}
|
|
300
|
+
isOwnLock(lock) {
|
|
301
|
+
return this.ownLock !== null && lock.host === this.ownLock.host && lock.processId === this.ownLock.processId && lock.startedAt === this.ownLock.startedAt;
|
|
302
|
+
}
|
|
303
|
+
isStale(lock) {
|
|
304
|
+
return isBulkOperationLockStale(lock, {
|
|
305
|
+
bootedAt: Date.now() - uptime() * MILLISECONDS_IN_SECOND,
|
|
306
|
+
host: hostname(),
|
|
307
|
+
isProcessAlive,
|
|
308
|
+
now: Date.now()
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async readLock() {
|
|
312
|
+
let text;
|
|
313
|
+
try {
|
|
314
|
+
text = await readFile(this.lockFilePath, "utf-8");
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (isErrorWithCode(error, "ENOENT")) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const value = JSON.parse(text);
|
|
323
|
+
if (isBulkOperationLock(value)) {
|
|
324
|
+
return value;
|
|
325
|
+
}
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
const fileStats = await stat(this.lockFilePath);
|
|
329
|
+
return {
|
|
330
|
+
heartbeatAt: fileStats.mtimeMs,
|
|
331
|
+
host: "",
|
|
332
|
+
operationId: "",
|
|
333
|
+
pluginId: "",
|
|
334
|
+
processId: 0,
|
|
335
|
+
processStartedAt: 0,
|
|
336
|
+
startedAt: 0
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async recover(lock) {
|
|
340
|
+
const operationId = lock.operationId || await this.findNextOperationId();
|
|
341
|
+
await this.commitOperation({ isInterrupted: true, operationId, pluginId: lock.pluginId || "unknown" });
|
|
342
|
+
await unlink(this.lockFilePath).catch((error) => {
|
|
343
|
+
if (!isErrorWithCode(error, "ENOENT")) {
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
return operationId;
|
|
348
|
+
}
|
|
349
|
+
async releaseLock() {
|
|
350
|
+
const lock = await this.readLock();
|
|
351
|
+
const isOwnLock = lock !== null && this.isOwnLock(lock);
|
|
352
|
+
this.ownLock = null;
|
|
353
|
+
if (!isOwnLock) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
await unlink(this.lockFilePath).catch((error) => {
|
|
357
|
+
if (!isErrorWithCode(error, "ENOENT")) {
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async runGit(params) {
|
|
363
|
+
const allowedExitCodes = params.allowedExitCodes ?? [0];
|
|
364
|
+
for (let attempt = 1; ; attempt++) {
|
|
365
|
+
const result = await execGit({ cwd: this.workingDirectory, gitArguments: params.gitArguments });
|
|
366
|
+
if (allowedExitCodes.includes(result.exitCode)) {
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
if (attempt < INDEX_LOCK_RETRY_COUNT && result.stderr.includes("index.lock")) {
|
|
370
|
+
await setTimeoutAsync(INDEX_LOCK_RETRY_DELAY_IN_MILLISECONDS);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
throw new GitCommandError(params.gitArguments, result.exitCode, result.stderr);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async stageAll() {
|
|
377
|
+
await this.runGit({ gitArguments: ["add", "--all", "--", "."] });
|
|
378
|
+
const result = await this.runGit({ gitArguments: ["diff", "--cached", "--name-only", "-z", "--", "."] });
|
|
379
|
+
return result.stdout.split("\0").filter(Boolean).length;
|
|
380
|
+
}
|
|
381
|
+
async tryCreateLock() {
|
|
382
|
+
const lock = createLockRecord();
|
|
383
|
+
try {
|
|
384
|
+
await writeFile(this.lockFilePath, JSON.stringify(lock), { flag: "wx" });
|
|
385
|
+
this.ownLock = lock;
|
|
386
|
+
return true;
|
|
387
|
+
} catch (error) {
|
|
388
|
+
if (isErrorWithCode(error, "EEXIST")) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async writeLock(lock) {
|
|
395
|
+
const temporaryPath = `${this.lockFilePath}.${String(lock.processId)}.tmp`;
|
|
396
|
+
await writeFile(temporaryPath, JSON.stringify(lock));
|
|
397
|
+
await rename(temporaryPath, this.lockFilePath);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
class GitCommandError extends Error {
|
|
401
|
+
/**
|
|
402
|
+
* Creates a new instance.
|
|
403
|
+
*
|
|
404
|
+
* @param gitArguments - The arguments `git` was run with.
|
|
405
|
+
* @param exitCode - The exit code.
|
|
406
|
+
* @param stderr - What `git` printed to stderr.
|
|
407
|
+
*/
|
|
408
|
+
constructor(gitArguments, exitCode, stderr) {
|
|
409
|
+
super(`git ${gitArguments.join(" ")} failed with exit code ${String(exitCode)}: ${stderr.trim()}`);
|
|
410
|
+
this.gitArguments = gitArguments;
|
|
411
|
+
this.exitCode = exitCode;
|
|
412
|
+
this.stderr = stderr;
|
|
413
|
+
this.name = "GitCommandError";
|
|
414
|
+
}
|
|
415
|
+
gitArguments;
|
|
416
|
+
exitCode;
|
|
417
|
+
stderr;
|
|
418
|
+
}
|
|
419
|
+
function createLockRecord() {
|
|
420
|
+
const now = Date.now();
|
|
421
|
+
return {
|
|
422
|
+
heartbeatAt: now,
|
|
423
|
+
host: hostname(),
|
|
424
|
+
operationId: "",
|
|
425
|
+
pluginId: "",
|
|
426
|
+
processId: process.pid,
|
|
427
|
+
processStartedAt: getProcessStartedAt(),
|
|
428
|
+
startedAt: now
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
async function execGit(params) {
|
|
432
|
+
return await new Promise((resolve, reject) => {
|
|
433
|
+
execFile("git", [...params.gitArguments], { cwd: params.cwd, maxBuffer: MAX_BUFFER_IN_BYTES, windowsHide: true }, (error, stdout, stderr) => {
|
|
434
|
+
if (error && typeof error.code !== "number") {
|
|
435
|
+
reject(new Error(`Failed to run git ${params.gitArguments.join(" ")}`, { cause: error }));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
resolve({ exitCode: typeof error?.code === "number" ? error.code : 0, stderr, stdout });
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
function getProcessStartedAt() {
|
|
443
|
+
return Math.round(performance.timeOrigin);
|
|
444
|
+
}
|
|
445
|
+
function isErrorWithCode(error, code) {
|
|
446
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
447
|
+
}
|
|
448
|
+
function isProcessAlive(processId) {
|
|
449
|
+
if (processId <= 0) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
process.kill(processId, 0);
|
|
454
|
+
return true;
|
|
455
|
+
} catch (error) {
|
|
456
|
+
return !isErrorWithCode(error, "ESRCH") && (isErrorWithCode(error, "EPERM") || null);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
export {
|
|
460
|
+
BulkOperationGitRepo,
|
|
461
|
+
GitCommandError
|
|
462
|
+
};
|
|
463
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9kZXNrdG9wLWJ1bGstb3BlcmF0aW9uLWdpdC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBmaWxlXG4gKlxuICogVGhlIGRlc2t0b3AgaGFsZiBvZiBidWxrLW9wZXJhdGlvbiBnaXQgY29tbWl0czogZHJpdmVzIHRoZSBzeXN0ZW0gYGdpdGAgQ0xJIHRvIHdyYXAgYSBidWxrIG9wZXJhdGlvbiBpbiBjb21taXRzLFxuICogZ3VhcmRlZCBieSBhIGxlYXNlIGxvY2sgaW4gdGhlIHJlcG8ncyBnaXQgZGlyZWN0b3J5LlxuICpcbiAqIFRoZSBzeXN0ZW0gQ0xJIGlzIHVzZWQgcmF0aGVyIHRoYW4gYSBidW5kbGVkIGltcGxlbWVudGF0aW9uIGJlY2F1c2UgaXQgaG9ub3JzIHRoZSByZXBvJ3Mgb3duIGNvbmZpZywgaG9va3NcbiAqIGFuZCBjbGVhbi9zbXVkZ2UgZmlsdGVycywgZ2l0LWFubmV4J3MgYW1vbmcgdGhlbSwgYW5kIGJlY2F1c2UgYGdpdCByZXZlcnRgIGlzIHRoZSB3aG9sZSB1bmRvIHN0b3J5LlxuICpcbiAqIEV2ZXJ5IGNvbW1hbmQgcnVucyB3aXRoIHRoZSB2YXVsdCBmb2xkZXIgYXMgaXRzIHdvcmtpbmcgZGlyZWN0b3J5IGFuZCBgLmAgYXMgaXRzIHBhdGhzcGVjLCBzbyB3aGVuIHRoZSB2YXVsdCBpcyBhXG4gKiBzdWJmb2xkZXIgb2YgYSBsYXJnZXIgcmVwbyBvbmx5IHRoZSB2YXVsdCdzIGNoYW5nZXMgYXJlIGNvbW1pdHRlZC5cbiAqXG4gKiBEZXNrdG9wLW9ubHkgKGBub2RlOmNoaWxkX3Byb2Nlc3NgLCBgbm9kZTpmc2AsIGBub2RlOm9zYCksIGJ1dCBpbmVydCB0byBpbXBvcnQgb24gbW9iaWxlOiBub3RoaW5nIGhlcmUgdG91Y2hlcyBhXG4gKiBOb2RlIGJ1aWx0aW4gdW50aWwgYSBmdW5jdGlvbiBpcyBjYWxsZWQuXG4gKi9cblxuaW1wb3J0IHsgc2V0VGltZW91dEFzeW5jIH0gZnJvbSAnQG1uYW91bW92L3R5cGVzY3JpcHQtbGliL2FzeW5jJztcbmltcG9ydCB7IG5vb3BBc3luYyB9IGZyb20gJ0BtbmFvdW1vdi90eXBlc2NyaXB0LWxpYi9mdW5jdGlvbic7XG5pbXBvcnQgeyBlc2NhcGVSZWdFeHAgfSBmcm9tICdAbW5hb3Vtb3YvdHlwZXNjcmlwdC1saWIvcmVnLWV4cCc7XG5pbXBvcnQgeyBlbnN1cmVOb25OdWxsYWJsZSB9IGZyb20gJ0BtbmFvdW1vdi90eXBlc2NyaXB0LWxpYi90eXBlLWd1YXJkcyc7XG5pbXBvcnQgeyBleGVjRmlsZSB9IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQge1xuICByZWFkRmlsZSxcbiAgcmVuYW1lLFxuICBzdGF0LFxuICB1bmxpbmssXG4gIHdyaXRlRmlsZVxufSBmcm9tICdub2RlOmZzL3Byb21pc2VzJztcbmltcG9ydCB7XG4gIGhvc3RuYW1lLFxuICB1cHRpbWVcbn0gZnJvbSAnbm9kZTpvcyc7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSAnbm9kZTpwYXRoJztcblxuaW1wb3J0IHR5cGUge1xuICBCdWxrT3BlcmF0aW9uQ29tbWl0LFxuICBCdWxrT3BlcmF0aW9uTG9ja1xufSBmcm9tICcuL2J1bGstb3BlcmF0aW9uLnRzJztcblxuaW1wb3J0IHsgcmVnaXN0ZXJCdWxrT3BlcmF0aW9uUHJvZ3Jlc3NMaXN0ZW5lciB9IGZyb20gJy4vYnVsay1vcGVyYXRpb24tcHJvZ3Jlc3MudHMnO1xuaW1wb3J0IHtcbiAgYnVpbGRCZWZvcmVPcGVyYXRpb25Db21taXRTdWJqZWN0LFxuICBidWlsZE9wZXJhdGlvbkNvbW1pdE1lc3NhZ2UsXG4gIEJVTEtfT1BFUkFUSU9OX0xPQ0tfRklMRV9OQU1FLFxuICBnZXROZXh0T3BlcmF0aW9uSWQsXG4gIGlzQnVsa09wZXJhdGlvbkxvY2ssXG4gIGlzQnVsa09wZXJhdGlvbkxvY2tTdGFsZSxcbiAgcGFyc2VCdWxrT3BlcmF0aW9uQ29tbWl0LFxuICBQTFVHSU5fVFJBSUxFUl9LRVlcbn0gZnJvbSAnLi9idWxrLW9wZXJhdGlvbi50cyc7XG5cbi8qKlxuICogQ29uc3RydWN0b3IgcGFyYW1ldGVycyBmb3Ige0BsaW5rIEJ1bGtPcGVyYXRpb25HaXRSZXBvfS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWxrT3BlcmF0aW9uR2l0UmVwb0NvbnN0cnVjdG9yUGFyYW1zIHtcbiAgLyoqXG4gICAqIFRoZSBhYnNvbHV0ZSBwYXRoIG9mIHRoZSByZXBvJ3MgZ2l0IGRpcmVjdG9yeSwgd2hlcmUgdGhlIGxlYXNlIGxvY2sgbGl2ZXMuXG4gICAqL1xuICByZWFkb25seSBnaXREaXJlY3Rvcnk6IHN0cmluZztcblxuICAvKipcbiAgICogSG93IGxvbmcgdG8gd2FpdCBmb3IgYW5vdGhlciBvcGVyYXRpb24ncyBsb2NrIGJlZm9yZSBnaXZpbmcgdXAuXG4gICAqXG4gICAqIEBkZWZhdWx0IGA2MDBfMDAwYFxuICAgKi9cbiAgcmVhZG9ubHkgbG9ja1dhaXRUaW1lb3V0SW5NaWxsaXNlY29uZHM/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFRoZSBhYnNvbHV0ZSBwYXRoIG9mIHRoZSBmb2xkZXIgd2hvc2UgY2hhbmdlcyBhcmUgY29tbWl0dGVkOiB0aGUgdmF1bHQgcm9vdC5cbiAgICovXG4gIHJlYWRvbmx5IHdvcmtpbmdEaXJlY3Rvcnk6IHN0cmluZztcbn1cblxuLyoqXG4gKiBQYXJhbWV0ZXJzIGZvciB7QGxpbmsgQnVsa09wZXJhdGlvbkdpdFJlcG8ucnVuQnVsa09wZXJhdGlvbn0uXG4gKlxuICogQHR5cGVQYXJhbSBUIC0gVGhlIHR5cGUgb2YgdGhlIG9wZXJhdGlvbidzIHJlc3VsdC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCdWxrT3BlcmF0aW9uR2l0UmVwb1J1bkJ1bGtPcGVyYXRpb25QYXJhbXM8VD4ge1xuICAvKipcbiAgICogUnVucyB0aGUgYnVsayBvcGVyYXRpb24uXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBvcGVyYXRpb24ncyByZXN1bHQuXG4gICAqL1xuICByZWFkb25seSBvcGVyYXRpb246ICgpID0+IFByb21pc2U8VD47XG5cbiAgLyoqXG4gICAqIFRoZSBpZCBvZiB0aGUgcGx1Z2luIHJ1bm5pbmcgdGhlIG9wZXJhdGlvbi5cbiAgICovXG4gIHJlYWRvbmx5IHBsdWdpbklkOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFdhaXRzIGZvciB0aGUgZm9sbG93LXVwcyB0aGUgb3BlcmF0aW9uIHNldCBvZmYgKHJlbmFtZSBoYW5kbGVycywgbGluayB1cGRhdGVzLCB0aGUgbWV0YWRhdGEgY2FjaGUpIHRvIGZpbmlzaCwgc29cbiAgICogdGhlaXIgY2hhbmdlcyBsYW5kIGluIHRoZSBvcGVyYXRpb24ncyBjb21taXQuXG4gICAqL1xuICByZWFkb25seSBzZXR0bGU6ICgpID0+IFByb21pc2U8dm9pZD47XG5cbiAgLyoqXG4gICAqIEEgb25lLWxpbmUgZGVzY3JpcHRpb24gb2YgdGhlIG9wZXJhdGlvbiwgdXNlZCBpbiB0aGUgY29tbWl0IHN1YmplY3QuXG4gICAqL1xuICByZWFkb25seSBzdW1tYXJ5OiBzdHJpbmc7XG59XG5cbi8qKlxuICogVGhlIHJlc3VsdCBvZiB7QGxpbmsgQnVsa09wZXJhdGlvbkdpdFJlcG8ucnVuQnVsa09wZXJhdGlvbn0uXG4gKlxuICogQHR5cGVQYXJhbSBUIC0gVGhlIHR5cGUgb2YgdGhlIG9wZXJhdGlvbidzIHJlc3VsdC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHaXRCdWxrT3BlcmF0aW9uUmVzdWx0PFQ+IHtcbiAgLyoqXG4gICAqIFRoZSBoYXNoIG9mIHRoZSBjb21taXQgcmVjb3JkaW5nIHRoZSBvcGVyYXRpb24sIG9yIGBudWxsYCB3aGVuIHRoZSBvcGVyYXRpb24gY2hhbmdlZCBub3RoaW5nLlxuICAgKi9cbiAgcmVhZG9ubHkgY29tbWl0U2hhOiBudWxsIHwgc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgb3BlcmF0aW9uIGlkLCBzdWNoIGFzIGBPUDEyYC5cbiAgICovXG4gIHJlYWRvbmx5IG9wZXJhdGlvbklkOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBvcGVyYXRpb24ncyByZXN1bHQuXG4gICAqL1xuICByZWFkb25seSByZXN1bHQ6IFQ7XG59XG5cbi8qKlxuICogVGhlIHJlc3VsdCBvZiB7QGxpbmsgQnVsa09wZXJhdGlvbkdpdFJlcG8ucmV2ZXJ0QnVsa09wZXJhdGlvbkNvbW1pdH0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgR2l0UmV2ZXJ0UmVzdWx0IHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIHJldmVydCBzdG9wcGVkIG9uIGEgY29uZmxpY3QsIGxlYXZpbmcgdGhlIHJlcG8gbWlkLXJldmVydCBmb3IgdGhlIHVzZXIgdG8gcmVzb2x2ZS5cbiAgICovXG4gIHJlYWRvbmx5IGlzQ29uZmxpY3Q6IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFdoYXQgZ2l0IHByaW50ZWQuXG4gICAqL1xuICByZWFkb25seSBvdXRwdXQ6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIEJ1bGtPcGVyYXRpb25HaXRSZXBvUnVuR2l0UGFyYW1zIHtcbiAgcmVhZG9ubHkgYWxsb3dlZEV4aXRDb2Rlcz86IHJlYWRvbmx5IG51bWJlcltdO1xuICByZWFkb25seSBnaXRBcmd1bWVudHM6IHJlYWRvbmx5IHN0cmluZ1tdO1xufVxuXG5pbnRlcmZhY2UgR2l0Q29tbWFuZFJlc3VsdCB7XG4gIHJlYWRvbmx5IGV4aXRDb2RlOiBudW1iZXI7XG4gIHJlYWRvbmx5IHN0ZGVycjogc3RyaW5nO1xuICByZWFkb25seSBzdGRvdXQ6IHN0cmluZztcbn1cblxuY29uc3QgREVGQVVMVF9MT0NLX1dBSVRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSA2MDBfMDAwO1xuY29uc3QgSEVBUlRCRUFUX1RIUk9UVExFX0lOX01JTExJU0VDT05EUyA9IDUwMDA7XG5jb25zdCBJTkRFWF9MT0NLX1JFVFJZX0NPVU5UID0gMjA7XG5jb25zdCBJTkRFWF9MT0NLX1JFVFJZX0RFTEFZX0lOX01JTExJU0VDT05EUyA9IDUwMDtcbmNvbnN0IExPQ0tfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMgPSAxMDAwO1xuY29uc3QgTUFYX0JVRkZFUl9JTl9CWVRFUyA9IDI2OF80MzVfNDU2O1xuY29uc3QgTUlMTElTRUNPTkRTX0lOX1NFQ09ORCA9IDEwMDA7XG5jb25zdCBHSVRfRkFUQUxfRVhJVF9DT0RFID0gMTI4O1xuY29uc3QgUkVWX1BBUlNFX09VVFBVVF9MSU5FX0NPVU5UID0gMjtcbmNvbnN0IFVOSVRfU0VQQVJBVE9SID0gJ1xcdXsxRn0nO1xuXG5pbnRlcmZhY2UgQnVsa09wZXJhdGlvbkdpdFJlcG9Db21taXRPcGVyYXRpb25QYXJhbXMge1xuICByZWFkb25seSBpc0ludGVycnVwdGVkPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgb3BlcmF0aW9uSWQ6IHN0cmluZztcbiAgcmVhZG9ubHkgcGx1Z2luSWQ6IHN0cmluZztcbiAgcmVhZG9ubHkgc3VtbWFyeT86IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIENvbW1pdE1lc3NhZ2Uge1xuICByZWFkb25seSBib2R5OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHN1YmplY3Q6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIEV4ZWNHaXRQYXJhbXMge1xuICByZWFkb25seSBjd2Q6IHN0cmluZztcbiAgcmVhZG9ubHkgZ2l0QXJndW1lbnRzOiByZWFkb25seSBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBBIGdpdCByZXBvIGhvbGRpbmcgYSB2YXVsdCwgZHJpdmVuIHRvIHdyYXAgYnVsayBvcGVyYXRpb25zIGluIGNvbW1pdHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBCdWxrT3BlcmF0aW9uR2l0UmVwbyB7XG4gIC8qKlxuICAgKiBUaGUgYWJzb2x1dGUgcGF0aCBvZiB0aGUgcmVwbydzIGdpdCBkaXJlY3RvcnkuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZ2l0RGlyZWN0b3J5OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBhYnNvbHV0ZSBwYXRoIG9mIHRoZSBmb2xkZXIgd2hvc2UgY2hhbmdlcyBhcmUgY29tbWl0dGVkLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHdvcmtpbmdEaXJlY3Rvcnk6IHN0cmluZztcblxuICBwcml2YXRlIHJlYWRvbmx5IGxvY2tGaWxlUGF0aDogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IGxvY2tXYWl0VGltZW91dEluTWlsbGlzZWNvbmRzOiBudW1iZXI7XG4gIHByaXZhdGUgb3duTG9jazogQnVsa09wZXJhdGlvbkxvY2sgfCBudWxsID0gbnVsbDtcblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZS4gVXNlIHtAbGluayBCdWxrT3BlcmF0aW9uR2l0UmVwby5vcGVufSB0byBmaW5kIHRoZSByZXBvIG9mIGEgZm9sZGVyLlxuICAgKlxuICAgKiBAcGFyYW0gcGFyYW1zIC0gVGhlIGNvbnN0cnVjdG9yIHBhcmFtZXRlcnMuXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3IocGFyYW1zOiBCdWxrT3BlcmF0aW9uR2l0UmVwb0NvbnN0cnVjdG9yUGFyYW1zKSB7XG4gICAgdGhpcy5naXREaXJlY3RvcnkgPSBwYXJhbXMuZ2l0RGlyZWN0b3J5O1xuICAgIHRoaXMud29ya2luZ0RpcmVjdG9yeSA9IHBhcmFtcy53b3JraW5nRGlyZWN0b3J5O1xuICAgIHRoaXMubG9ja1dhaXRUaW1lb3V0SW5NaWxsaXNlY29uZHMgPSBwYXJhbXMubG9ja1dhaXRUaW1lb3V0SW5NaWxsaXNlY29uZHMgPz8gREVGQVVMVF9MT0NLX1dBSVRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFM7XG4gICAgdGhpcy5sb2NrRmlsZVBhdGggPSBqb2luKHRoaXMuZ2l0RGlyZWN0b3J5LCBCVUxLX09QRVJBVElPTl9MT0NLX0ZJTEVfTkFNRSk7XG4gIH1cblxuICAvKipcbiAgICogRmluZHMgdGhlIGdpdCByZXBvIGEgZm9sZGVyIGJlbG9uZ3MgdG8uXG4gICAqXG4gICAqIEBwYXJhbSB3b3JraW5nRGlyZWN0b3J5IC0gVGhlIGZvbGRlciwgbm9ybWFsbHkgdGhlIHZhdWx0IHJvb3QuXG4gICAqIEByZXR1cm5zIFRoZSByZXBvLCBvciBgbnVsbGAgd2hlbiB0aGUgZm9sZGVyIGlzIG5vdCBpbnNpZGUgYSBnaXQgd29yayB0cmVlIG9yIGBnaXRgIGNhbm5vdCBiZSBydW4uXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIG9wZW4od29ya2luZ0RpcmVjdG9yeTogc3RyaW5nKTogUHJvbWlzZTxCdWxrT3BlcmF0aW9uR2l0UmVwbyB8IG51bGw+IHtcbiAgICBsZXQgcmVzdWx0OiBHaXRDb21tYW5kUmVzdWx0O1xuICAgIHRyeSB7XG4gICAgICByZXN1bHQgPSBhd2FpdCBleGVjR2l0KHsgY3dkOiB3b3JraW5nRGlyZWN0b3J5LCBnaXRBcmd1bWVudHM6IFsncmV2LXBhcnNlJywgJy0taXMtaW5zaWRlLXdvcmstdHJlZScsICctLWFic29sdXRlLWdpdC1kaXInXSB9KTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBjb25zdCBbaXNJbnNpZGVXb3JrVHJlZSwgZ2l0RGlyZWN0b3J5XSA9IHJlc3VsdC5zdGRvdXQuc3BsaXQoL1xccj9cXG4vLCBSRVZfUEFSU0VfT1VUUFVUX0xJTkVfQ09VTlQpO1xuICAgIHJldHVybiByZXN1bHQuZXhpdENvZGUgIT09IDAgfHwgaXNJbnNpZGVXb3JrVHJlZSAhPT0gJ3RydWUnIHx8ICFnaXREaXJlY3RvcnkgPyBudWxsIDogbmV3IEJ1bGtPcGVyYXRpb25HaXRSZXBvKHsgZ2l0RGlyZWN0b3J5LCB3b3JraW5nRGlyZWN0b3J5IH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZnVzZXMgdG8gZ28gb24gd2hlbiB0aGUgcmVwbyByb3V0ZXMgZmlsZXMgdGhyb3VnaCBnaXQtYW5uZXggYnV0IGBnaXQtYW5uZXhgIGNhbm5vdCBiZSBydW4sIHNpbmNlIGFcbiAgICogcGxhaW4gYGdpdCBhZGRgIHdvdWxkIHRoZW4gcHV0IHRoZSBsYXJnZSBmaWxlcyBpbnRvIGdpdCBpdHNlbGYuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgYXNzZXJ0Q2FuQ29tbWl0TGFyZ2VGaWxlcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBhbm5leEZpbHRlcnMgPSBhd2FpdCB0aGlzLnJ1bkdpdCh7IGFsbG93ZWRFeGl0Q29kZXM6IFswLCAxXSwgZ2l0QXJndW1lbnRzOiBbJ2NvbmZpZycsICctLWdldC1yZWdleHAnLCBTdHJpbmcucmF3YF5maWx0ZXJcXC5hbm5leFxcLmBdIH0pO1xuICAgIGlmICghYW5uZXhGaWx0ZXJzLnN0ZG91dC50cmltKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgYW5uZXhWZXJzaW9uID0gYXdhaXQgdGhpcy5ydW5HaXQoeyBnaXRBcmd1bWVudHM6IFsnYW5uZXgnLCAndmVyc2lvbicsICctLXJhdyddIH0pLmNhdGNoKCgpID0+IG51bGwpO1xuICAgIGlmIChhbm5leFZlcnNpb24gPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ1RoZSB2YXVsdCByZXBvc2l0b3J5IHJvdXRlcyBmaWxlcyB0aHJvdWdoIGdpdC1hbm5leCwgYnV0IGdpdC1hbm5leCBpcyBub3Qgb24gUEFUSC4gQnVsayBvcGVyYXRpb24gY29tbWl0cyBhcmUgcmVmdXNlZCwgYmVjYXVzZSB0aGV5IHdvdWxkIGFkZCBsYXJnZSBmaWxlcyB0byBnaXQgaXRzZWxmLidcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIExpc3RzIHRoZSBidWxrLW9wZXJhdGlvbiBjb21taXRzIGEgcGx1Z2luIG1hZGUgb24gdGhlIGN1cnJlbnQgYnJhbmNoLCBuZXdlc3QgZmlyc3QuXG4gICAqXG4gICAqIEBwYXJhbSBwbHVnaW5JZCAtIFRoZSBwbHVnaW4gaWQuXG4gICAqIEByZXR1cm5zIFRoZSBjb21taXRzLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGxpc3RCdWxrT3BlcmF0aW9uQ29tbWl0cyhwbHVnaW5JZDogc3RyaW5nKTogUHJvbWlzZTxCdWxrT3BlcmF0aW9uQ29tbWl0W10+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLnJ1bkdpdCh7XG4gICAgICBhbGxvd2VkRXhpdENvZGVzOiBbMCwgR0lUX0ZBVEFMX0VYSVRfQ09ERV0sXG4gICAgICBnaXRBcmd1bWVudHM6IFtcbiAgICAgICAgJ2xvZycsXG4gICAgICAgIGAtLWZvcm1hdD0lSCR7VU5JVF9TRVBBUkFUT1J9JWFJJHtVTklUX1NFUEFSQVRPUn0lc2AsXG4gICAgICAgICctLWV4dGVuZGVkLXJlZ2V4cCcsXG4gICAgICAgIGAtLWdyZXA9XiR7UExVR0lOX1RSQUlMRVJfS0VZfTogJHtlc2NhcGVSZWdFeHAocGx1Z2luSWQpfSRgXG4gICAgICBdXG4gICAgfSk7XG4gICAgaWYgKHJlc3VsdC5leGl0Q29kZSAhPT0gMCkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICBjb25zdCBjb21taXRzOiBCdWxrT3BlcmF0aW9uQ29tbWl0W10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGxpbmUgb2YgcmVzdWx0LnN0ZG91dC5zcGxpdCgvXFxyP1xcbi8pKSB7XG4gICAgICBjb25zdCBbc2hhID0gJycsIGRhdGUgPSAnJywgLi4uc3ViamVjdFBhcnRzXSA9IGxpbmUuc3BsaXQoVU5JVF9TRVBBUkFUT1IpO1xuICAgICAgY29uc3QgY29tbWl0ID0gcGFyc2VCdWxrT3BlcmF0aW9uQ29tbWl0KHsgZGF0ZSwgc2hhLCBzdWJqZWN0OiBzdWJqZWN0UGFydHMuam9pbihVTklUX1NFUEFSQVRPUikgfSk7XG4gICAgICBpZiAoY29tbWl0KSB7XG4gICAgICAgIGNvbW1pdHMucHVzaChjb21taXQpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gY29tbWl0cztcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21taXRzIHdoYXRldmVyIGEgc3RhbGUgbG9jaydzIG9wZXJhdGlvbiBsZWZ0IGJlaGluZCBhcyBgT1A8bj4gaW50ZXJydXB0ZWRgIGFuZCByZW1vdmVzIHRoZSBsb2NrLlxuICAgKlxuICAgKiBAcmV0dXJucyBUaGUgaWQgb2YgdGhlIHJlY292ZXJlZCBvcGVyYXRpb24sIG9yIGBudWxsYCB3aGVuIHRoZXJlIHdhcyBubyBzdGFsZSBsb2NrLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHJlY292ZXJTdGFsZUxvY2soKTogUHJvbWlzZTxudWxsIHwgc3RyaW5nPiB7XG4gICAgY29uc3QgbG9jayA9IGF3YWl0IHRoaXMucmVhZExvY2soKTtcbiAgICByZXR1cm4gIWxvY2sgfHwgIXRoaXMuaXNTdGFsZShsb2NrKSA/IG51bGwgOiAoYXdhaXQgdGhpcy5yZWNvdmVyKGxvY2spKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXZlcnRzIGEgYnVsay1vcGVyYXRpb24gY29tbWl0IHdpdGggYGdpdCByZXZlcnQgLS1uby1lZGl0YC5cbiAgICpcbiAgICogV2hhdGV2ZXIgaXMgcGVuZGluZyBpcyBjb21taXR0ZWQgZmlyc3QsIHNvIHRoZSByZXZlcnQgc3RhcnRzIGZyb20gYSBjbGVhbiB0cmVlLiBBIGNvbmZsaWN0IGlzIE5PVCBhYm9ydGVkOiB0aGVcbiAgICogcmVwbyBpcyBsZWZ0IG1pZC1yZXZlcnQgZm9yIHRoZSB1c2VyIHRvIHJlc29sdmUuXG4gICAqXG4gICAqIEBwYXJhbSBjb21taXQgLSBUaGUgY29tbWl0IHRvIHJldmVydC5cbiAgICogQHJldHVybnMgV2hldGhlciB0aGUgcmV2ZXJ0IHN0b3BwZWQgb24gYSBjb25mbGljdCwgYW5kIHdoYXQgZ2l0IHByaW50ZWQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmV2ZXJ0QnVsa09wZXJhdGlvbkNvbW1pdChjb21taXQ6IEJ1bGtPcGVyYXRpb25Db21taXQpOiBQcm9taXNlPEdpdFJldmVydFJlc3VsdD4ge1xuICAgIGF3YWl0IHRoaXMuYXNzZXJ0Q2FuQ29tbWl0TGFyZ2VGaWxlcygpO1xuICAgIGF3YWl0IHRoaXMuYWNxdWlyZUxvY2soKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5jb21taXRQZW5kaW5nKHsgYm9keTogJycsIHN1YmplY3Q6IGBjaG9yZTogYmVmb3JlIHJldmVydCBvZiAke2NvbW1pdC5vcGVyYXRpb25JZH1gIH0pO1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5ydW5HaXQoeyBhbGxvd2VkRXhpdENvZGVzOiBbMCwgMV0sIGdpdEFyZ3VtZW50czogWydyZXZlcnQnLCAnLS1uby1lZGl0JywgY29tbWl0LnNoYV0gfSk7XG4gICAgICByZXR1cm4geyBpc0NvbmZsaWN0OiByZXN1bHQuZXhpdENvZGUgIT09IDAsIG91dHB1dDogYCR7cmVzdWx0LnN0ZG91dH0ke3Jlc3VsdC5zdGRlcnJ9YC50cmltKCkgfTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5yZWxlYXNlTG9jaygpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGEgYnVsayBvcGVyYXRpb24gYmV0d2VlbiB0d28gY29tbWl0cy5cbiAgICpcbiAgICogVGFrZXMgdGhlIGxlYXNlIGxvY2ssIGNvbW1pdHMgd2hhdGV2ZXIgaXMgcGVuZGluZyBhcyBgY2hvcmU6IGJlZm9yZSBPUDxuPmAsIHJ1bnMgdGhlIG9wZXJhdGlvbiwgd2FpdHMgZm9yIGl0c1xuICAgKiBmb2xsb3ctdXBzIHRvIHNldHRsZSwgY29tbWl0cyB0aGUgcmVzdWx0IGFzIGBPUDxuPjogPHN1bW1hcnk+YCBhbmQgcmVsZWFzZXMgdGhlIGxvY2suIEFuIG9wZXJhdGlvbiB0aGF0IHRocm93c1xuICAgKiBzdGlsbCBnZXRzIGl0cyBwYXJ0aWFsIGNoYW5nZXMgY29tbWl0dGVkLCBhcyBgT1A8bj4gaW50ZXJydXB0ZWRgLCBiZWZvcmUgdGhlIGVycm9yIGlzIHJldGhyb3duLlxuICAgKlxuICAgKiBAdHlwZVBhcmFtIFQgLSBUaGUgdHlwZSBvZiB0aGUgb3BlcmF0aW9uJ3MgcmVzdWx0LlxuICAgKiBAcGFyYW0gcGFyYW1zIC0gVGhlIHBhcmFtZXRlcnMuXG4gICAqIEByZXR1cm5zIFRoZSBvcGVyYXRpb24ncyByZXN1bHQsIGl0cyBpZCBhbmQgdGhlIGhhc2ggb2YgaXRzIGNvbW1pdC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBydW5CdWxrT3BlcmF0aW9uPFQ+KHBhcmFtczogQnVsa09wZXJhdGlvbkdpdFJlcG9SdW5CdWxrT3BlcmF0aW9uUGFyYW1zPFQ+KTogUHJvbWlzZTxHaXRCdWxrT3BlcmF0aW9uUmVzdWx0PFQ+PiB7XG4gICAgYXdhaXQgdGhpcy5hc3NlcnRDYW5Db21taXRMYXJnZUZpbGVzKCk7XG4gICAgYXdhaXQgdGhpcy5hY3F1aXJlTG9jaygpO1xuICAgIGxldCB1bnJlZ2lzdGVyUHJvZ3Jlc3NMaXN0ZW5lcjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IG9wZXJhdGlvbklkID0gYXdhaXQgdGhpcy5hbGxvY2F0ZU9wZXJhdGlvbklkKHBhcmFtcy5wbHVnaW5JZCk7XG4gICAgICBhd2FpdCB0aGlzLmNvbW1pdFBlbmRpbmcoeyBib2R5OiAnJywgc3ViamVjdDogYnVpbGRCZWZvcmVPcGVyYXRpb25Db21taXRTdWJqZWN0KG9wZXJhdGlvbklkKSB9KTtcblxuICAgICAgdW5yZWdpc3RlclByb2dyZXNzTGlzdGVuZXIgPSByZWdpc3RlckJ1bGtPcGVyYXRpb25Qcm9ncmVzc0xpc3RlbmVyKHRoaXMuY3JlYXRlVGhyb3R0bGVkSGVhcnRiZWF0KCkpO1xuXG4gICAgICBsZXQgcmVzdWx0OiBUO1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgcGFyYW1zLm9wZXJhdGlvbigpO1xuICAgICAgICBhd2FpdCBwYXJhbXMuc2V0dGxlKCk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBhd2FpdCB0aGlzLmhlYXJ0YmVhdCgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbW1pdE9wZXJhdGlvbih7IGlzSW50ZXJydXB0ZWQ6IHRydWUsIG9wZXJhdGlvbklkLCBwbHVnaW5JZDogcGFyYW1zLnBsdWdpbklkIH0pO1xuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cblxuICAgICAgYXdhaXQgdGhpcy5oZWFydGJlYXQoKTtcbiAgICAgIGNvbnN0IGNvbW1pdFNoYSA9IGF3YWl0IHRoaXMuY29tbWl0T3BlcmF0aW9uKHsgb3BlcmF0aW9uSWQsIHBsdWdpbklkOiBwYXJhbXMucGx1Z2luSWQsIHN1bW1hcnk6IHBhcmFtcy5zdW1tYXJ5IH0pO1xuICAgICAgcmV0dXJuIHsgY29tbWl0U2hhLCBvcGVyYXRpb25JZCwgcmVzdWx0IH07XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHVucmVnaXN0ZXJQcm9ncmVzc0xpc3RlbmVyPy4oKTtcbiAgICAgIGF3YWl0IHRoaXMucmVsZWFzZUxvY2soKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGFjcXVpcmVMb2NrKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHRoaXMubG9ja1dhaXRUaW1lb3V0SW5NaWxsaXNlY29uZHM7XG4gICAgZm9yICg7Oykge1xuICAgICAgaWYgKGF3YWl0IHRoaXMudHJ5Q3JlYXRlTG9jaygpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbG9jayA9IGF3YWl0IHRoaXMucmVhZExvY2soKTtcbiAgICAgIGlmICghbG9jaykge1xuICAgICAgICAvLyBSZWxlYXNlZCBiZXR3ZWVuIHRoZSB0d28gY2FsbHM6IHRyeSB0byB0YWtlIGl0IGFnYWluIGF0IG9uY2UuXG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5pc1N0YWxlKGxvY2spKSB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVjb3Zlcihsb2NrKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChEYXRlLm5vdygpID49IGRlYWRsaW5lKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgVGltZWQgb3V0IHdhaXRpbmcgZm9yICR7bG9jay5vcGVyYXRpb25JZCB8fCAnYW4gb3BlcmF0aW9uJ30gb2YgJHtsb2NrLnBsdWdpbklkfSBvbiAke2xvY2suaG9zdH0gdG8gcmVsZWFzZSB0aGUgYnVsayBvcGVyYXRpb24gbG9jayAke3RoaXMubG9ja0ZpbGVQYXRofS5gXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IHNldFRpbWVvdXRBc3luYyhMT0NLX1BPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGFsbG9jYXRlT3BlcmF0aW9uSWQocGx1Z2luSWQ6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3Qgb3BlcmF0aW9uSWQgPSBhd2FpdCB0aGlzLmZpbmROZXh0T3BlcmF0aW9uSWQoKTtcbiAgICBjb25zdCBvd25Mb2NrID0gZW5zdXJlTm9uTnVsbGFibGUodGhpcy5vd25Mb2NrKTtcbiAgICB0aGlzLm93bkxvY2sgPSB7IC4uLm93bkxvY2ssIGhlYXJ0YmVhdEF0OiBEYXRlLm5vdygpLCBvcGVyYXRpb25JZCwgcGx1Z2luSWQgfTtcbiAgICBhd2FpdCB0aGlzLndyaXRlTG9jayh0aGlzLm93bkxvY2spO1xuICAgIHJldHVybiBvcGVyYXRpb25JZDtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY29tbWl0T3BlcmF0aW9uKHBhcmFtczogQnVsa09wZXJhdGlvbkdpdFJlcG9Db21taXRPcGVyYXRpb25QYXJhbXMpOiBQcm9taXNlPG51bGwgfCBzdHJpbmc+IHtcbiAgICBjb25zdCBjaGFuZ2VkRmlsZUNvdW50ID0gYXdhaXQgdGhpcy5zdGFnZUFsbCgpO1xuICAgIGlmIChjaGFuZ2VkRmlsZUNvdW50ID09PSAwKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3QgbWVzc2FnZSA9IGJ1aWxkT3BlcmF0aW9uQ29tbWl0TWVzc2FnZSh7IC4uLnBhcmFtcywgY2hhbmdlZEZpbGVDb3VudCwgdGltZXN0YW1wOiBuZXcgRGF0ZSgpIH0pO1xuICAgIGF3YWl0IHRoaXMuY29tbWl0U3RhZ2VkKG1lc3NhZ2UpO1xuICAgIGNvbnN0IGhlYWRSZXN1bHQgPSBhd2FpdCB0aGlzLnJ1bkdpdCh7IGdpdEFyZ3VtZW50czogWydyZXYtcGFyc2UnLCAnSEVBRCddIH0pO1xuICAgIHJldHVybiBoZWFkUmVzdWx0LnN0ZG91dC50cmltKCk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNvbW1pdFBlbmRpbmcobWVzc2FnZTogQ29tbWl0TWVzc2FnZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChhd2FpdCB0aGlzLnN0YWdlQWxsKCkgPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgYXdhaXQgdGhpcy5jb21taXRTdGFnZWQobWVzc2FnZSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNvbW1pdFN0YWdlZChtZXNzYWdlOiBDb21taXRNZXNzYWdlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgZ2l0QXJndW1lbnRzID0gWydjb21taXQnLCAnLS1xdWlldCcsICctbScsIG1lc3NhZ2Uuc3ViamVjdF07XG4gICAgaWYgKG1lc3NhZ2UuYm9keSkge1xuICAgICAgZ2l0QXJndW1lbnRzLnB1c2goJy1tJywgbWVzc2FnZS5ib2R5KTtcbiAgICB9XG4gICAgZ2l0QXJndW1lbnRzLnB1c2goJy0tJywgJy4nKTtcbiAgICBhd2FpdCB0aGlzLnJ1bkdpdCh7IGdpdEFyZ3VtZW50cyB9KTtcbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlVGhyb3R0bGVkSGVhcnRiZWF0KCk6ICgpID0+IHZvaWQge1xuICAgIGxldCBsYXN0SGVhcnRiZWF0QXQgPSBEYXRlLm5vdygpO1xuICAgIGxldCBwZW5kaW5nSGVhcnRiZWF0OiBQcm9taXNlPHZvaWQ+ID0gbm9vcEFzeW5jKCk7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgICBpZiAobm93IC0gbGFzdEhlYXJ0YmVhdEF0IDwgSEVBUlRCRUFUX1RIUk9UVExFX0lOX01JTExJU0VDT05EUykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBsYXN0SGVhcnRiZWF0QXQgPSBub3c7XG4gICAgICBwZW5kaW5nSGVhcnRiZWF0ID0gcGVuZGluZ0hlYXJ0YmVhdC50aGVuKCgpID0+IHRoaXMuaGVhcnRiZWF0KCkpLmNhdGNoKChlcnJvcjogdW5rbm93bikgPT4ge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gcmVmcmVzaCB0aGUgYnVsayBvcGVyYXRpb24gbG9jayBoZWFydGJlYXQnLCBlcnJvcik7XG4gICAgICB9KTtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBmaW5kTmV4dE9wZXJhdGlvbklkKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgLy8gYC0tYWxsYCBhbHNvIHJlYWRzIGEgcmVwb3NpdG9yeSB3aXRoIG5vIGNvbW1pdCB5ZXQsIHdoZXJlIGEgcGxhaW4gYGdpdCBsb2dgIGZhaWxzLlxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucnVuR2l0KHtcbiAgICAgIGdpdEFyZ3VtZW50czogWydsb2cnLCAnLS1hbGwnLCAnLS1mb3JtYXQ9JXMnLCAnLS1leHRlbmRlZC1yZWdleHAnLCAnLS1ncmVwPV4oY2hvcmU6IGJlZm9yZSApP09QWzAtOV0rJ11cbiAgICB9KTtcbiAgICByZXR1cm4gZ2V0TmV4dE9wZXJhdGlvbklkKHJlc3VsdC5zdGRvdXQuc3BsaXQoL1xccj9cXG4vKSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGhlYXJ0YmVhdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBsb2NrID0gYXdhaXQgdGhpcy5yZWFkTG9jaygpO1xuICAgIGlmICghbG9jayB8fCAhdGhpcy5pc093bkxvY2sobG9jaykpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgYXdhaXQgdGhpcy53cml0ZUxvY2soeyAuLi5sb2NrLCBoZWFydGJlYXRBdDogRGF0ZS5ub3coKSB9KTtcbiAgfVxuXG4gIHByaXZhdGUgaXNPd25Mb2NrKGxvY2s6IEJ1bGtPcGVyYXRpb25Mb2NrKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMub3duTG9jayAhPT0gbnVsbFxuICAgICAgJiYgbG9jay5ob3N0ID09PSB0aGlzLm93bkxvY2suaG9zdFxuICAgICAgJiYgbG9jay5wcm9jZXNzSWQgPT09IHRoaXMub3duTG9jay5wcm9jZXNzSWRcbiAgICAgICYmIGxvY2suc3RhcnRlZEF0ID09PSB0aGlzLm93bkxvY2suc3RhcnRlZEF0O1xuICB9XG5cbiAgcHJpdmF0ZSBpc1N0YWxlKGxvY2s6IEJ1bGtPcGVyYXRpb25Mb2NrKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzQnVsa09wZXJhdGlvbkxvY2tTdGFsZShsb2NrLCB7XG4gICAgICBib290ZWRBdDogRGF0ZS5ub3coKSAtIHVwdGltZSgpICogTUlMTElTRUNPTkRTX0lOX1NFQ09ORCxcbiAgICAgIGhvc3Q6IGhvc3RuYW1lKCksXG4gICAgICBpc1Byb2Nlc3NBbGl2ZSxcbiAgICAgIG5vdzogRGF0ZS5ub3coKVxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWFkTG9jaygpOiBQcm9taXNlPEJ1bGtPcGVyYXRpb25Mb2NrIHwgbnVsbD4ge1xuICAgIGxldCB0ZXh0OiBzdHJpbmc7XG4gICAgdHJ5IHtcbiAgICAgIHRleHQgPSBhd2FpdCByZWFkRmlsZSh0aGlzLmxvY2tGaWxlUGF0aCwgJ3V0Zi04Jyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChpc0Vycm9yV2l0aENvZGUoZXJyb3IsICdFTk9FTlQnKSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBjb25zdCB2YWx1ZTogdW5rbm93biA9IEpTT04ucGFyc2UodGV4dCk7XG4gICAgICBpZiAoaXNCdWxrT3BlcmF0aW9uTG9jayh2YWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgfVxuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gVXBkYXRlcyBhcmUgYXRvbWljLCBidXQgYSBsb2NrIGJlaW5nIGNyZWF0ZWQgY2FuIGJlIHJlYWQgYmVmb3JlIGl0cyBjb250ZW50IGlzIHdyaXR0ZW4uIFRoZSBmaWxlJ3MgYWdlXG4gICAgICAvLyBzdGFuZHMgaW4gZm9yIHRoZSBoZWFydGJlYXQsIHNvIHN1Y2ggYSBsb2NrIHJlYWRzIGFzIGZyZXNoIGFuZCBhIGNvcnJ1cHQgb25lIGdvZXMgc3RhbGUgbGlrZSBhbnkgb3RoZXIuXG4gICAgfVxuICAgIGNvbnN0IGZpbGVTdGF0cyA9IGF3YWl0IHN0YXQodGhpcy5sb2NrRmlsZVBhdGgpO1xuICAgIHJldHVybiB7XG4gICAgICBoZWFydGJlYXRBdDogZmlsZVN0YXRzLm10aW1lTXMsXG4gICAgICBob3N0OiAnJyxcbiAgICAgIG9wZXJhdGlvbklkOiAnJyxcbiAgICAgIHBsdWdpbklkOiAnJyxcbiAgICAgIHByb2Nlc3NJZDogMCxcbiAgICAgIHByb2Nlc3NTdGFydGVkQXQ6IDAsXG4gICAgICBzdGFydGVkQXQ6IDBcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWNvdmVyKGxvY2s6IEJ1bGtPcGVyYXRpb25Mb2NrKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCBvcGVyYXRpb25JZCA9IGxvY2sub3BlcmF0aW9uSWQgfHwgYXdhaXQgdGhpcy5maW5kTmV4dE9wZXJhdGlvbklkKCk7XG4gICAgYXdhaXQgdGhpcy5jb21taXRPcGVyYXRpb24oeyBpc0ludGVycnVwdGVkOiB0cnVlLCBvcGVyYXRpb25JZCwgcGx1Z2luSWQ6IGxvY2sucGx1Z2luSWQgfHwgJ3Vua25vd24nIH0pO1xuICAgIGF3YWl0IHVubGluayh0aGlzLmxvY2tGaWxlUGF0aCkuY2F0Y2goKGVycm9yOiB1bmtub3duKSA9PiB7XG4gICAgICBpZiAoIWlzRXJyb3JXaXRoQ29kZShlcnJvciwgJ0VOT0VOVCcpKSB7XG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBvcGVyYXRpb25JZDtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVsZWFzZUxvY2soKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbG9jayA9IGF3YWl0IHRoaXMucmVhZExvY2soKTtcbiAgICBjb25zdCBpc093bkxvY2sgPSBsb2NrICE9PSBudWxsICYmIHRoaXMuaXNPd25Mb2NrKGxvY2spO1xuICAgIHRoaXMub3duTG9jayA9IG51bGw7XG4gICAgaWYgKCFpc093bkxvY2spIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgYXdhaXQgdW5saW5rKHRoaXMubG9ja0ZpbGVQYXRoKS5jYXRjaCgoZXJyb3I6IHVua25vd24pID0+IHtcbiAgICAgIGlmICghaXNFcnJvcldpdGhDb2RlKGVycm9yLCAnRU5PRU5UJykpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJ1bkdpdChwYXJhbXM6IEJ1bGtPcGVyYXRpb25HaXRSZXBvUnVuR2l0UGFyYW1zKTogUHJvbWlzZTxHaXRDb21tYW5kUmVzdWx0PiB7XG4gICAgY29uc3QgYWxsb3dlZEV4aXRDb2RlcyA9IHBhcmFtcy5hbGxvd2VkRXhpdENvZGVzID8/IFswXTtcbiAgICBmb3IgKGxldCBhdHRlbXB0ID0gMTs7IGF0dGVtcHQrKykge1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZXhlY0dpdCh7IGN3ZDogdGhpcy53b3JraW5nRGlyZWN0b3J5LCBnaXRBcmd1bWVudHM6IHBhcmFtcy5naXRBcmd1bWVudHMgfSk7XG4gICAgICBpZiAoYWxsb3dlZEV4aXRDb2Rlcy5pbmNsdWRlcyhyZXN1bHQuZXhpdENvZGUpKSB7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9XG4gICAgICBpZiAoYXR0ZW1wdCA8IElOREVYX0xPQ0tfUkVUUllfQ09VTlQgJiYgcmVzdWx0LnN0ZGVyci5pbmNsdWRlcygnaW5kZXgubG9jaycpKSB7XG4gICAgICAgIGF3YWl0IHNldFRpbWVvdXRBc3luYyhJTkRFWF9MT0NLX1JFVFJZX0RFTEFZX0lOX01JTExJU0VDT05EUyk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgdGhyb3cgbmV3IEdpdENvbW1hbmRFcnJvcihwYXJhbXMuZ2l0QXJndW1lbnRzLCByZXN1bHQuZXhpdENvZGUsIHJlc3VsdC5zdGRlcnIpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc3RhZ2VBbGwoKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBhd2FpdCB0aGlzLnJ1bkdpdCh7IGdpdEFyZ3VtZW50czogWydhZGQnLCAnLS1hbGwnLCAnLS0nLCAnLiddIH0pO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMucnVuR2l0KHsgZ2l0QXJndW1lbnRzOiBbJ2RpZmYnLCAnLS1jYWNoZWQnLCAnLS1uYW1lLW9ubHknLCAnLXonLCAnLS0nLCAnLiddIH0pO1xuICAgIHJldHVybiByZXN1bHQuc3Rkb3V0LnNwbGl0KCdcXDAnKS5maWx0ZXIoQm9vbGVhbikubGVuZ3RoO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB0cnlDcmVhdGVMb2NrKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGxvY2sgPSBjcmVhdGVMb2NrUmVjb3JkKCk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHdyaXRlRmlsZSh0aGlzLmxvY2tGaWxlUGF0aCwgSlNPTi5zdHJpbmdpZnkobG9jayksIHsgZmxhZzogJ3d4JyB9KTtcbiAgICAgIHRoaXMub3duTG9jayA9IGxvY2s7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGlzRXJyb3JXaXRoQ29kZShlcnJvciwgJ0VFWElTVCcpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgd3JpdGVMb2NrKGxvY2s6IEJ1bGtPcGVyYXRpb25Mb2NrKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gV3JpdHRlbiBiZXNpZGUgdGhlIGxvY2sgYW5kIHJlbmFtZWQgb3ZlciBpdCwgc28gYSByZWFkZXIgbmV2ZXIgc2VlcyBhIGhhbGYtd3JpdHRlbiBoZWFydGJlYXQuXG4gICAgY29uc3QgdGVtcG9yYXJ5UGF0aCA9IGAke3RoaXMubG9ja0ZpbGVQYXRofS4ke1N0cmluZyhsb2NrLnByb2Nlc3NJZCl9LnRtcGA7XG4gICAgYXdhaXQgd3JpdGVGaWxlKHRlbXBvcmFyeVBhdGgsIEpTT04uc3RyaW5naWZ5KGxvY2spKTtcbiAgICBhd2FpdCByZW5hbWUodGVtcG9yYXJ5UGF0aCwgdGhpcy5sb2NrRmlsZVBhdGgpO1xuICB9XG59XG5cbi8qKlxuICogQW4gZXJyb3IgdGhyb3duIHdoZW4gYSBgZ2l0YCBjb21tYW5kIGZhaWxzLlxuICovXG5leHBvcnQgY2xhc3MgR2l0Q29tbWFuZEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICAvKipcbiAgICogQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZS5cbiAgICpcbiAgICogQHBhcmFtIGdpdEFyZ3VtZW50cyAtIFRoZSBhcmd1bWVudHMgYGdpdGAgd2FzIHJ1biB3aXRoLlxuICAgKiBAcGFyYW0gZXhpdENvZGUgLSBUaGUgZXhpdCBjb2RlLlxuICAgKiBAcGFyYW0gc3RkZXJyIC0gV2hhdCBgZ2l0YCBwcmludGVkIHRvIHN0ZGVyci5cbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihwdWJsaWMgcmVhZG9ubHkgZ2l0QXJndW1lbnRzOiByZWFkb25seSBzdHJpbmdbXSwgcHVibGljIHJlYWRvbmx5IGV4aXRDb2RlOiBudW1iZXIsIHB1YmxpYyByZWFkb25seSBzdGRlcnI6IHN0cmluZykge1xuICAgIHN1cGVyKGBnaXQgJHtnaXRBcmd1bWVudHMuam9pbignICcpfSBmYWlsZWQgd2l0aCBleGl0IGNvZGUgJHtTdHJpbmcoZXhpdENvZGUpfTogJHtzdGRlcnIudHJpbSgpfWApO1xuICAgIHRoaXMubmFtZSA9ICdHaXRDb21tYW5kRXJyb3InO1xuICB9XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUxvY2tSZWNvcmQoKTogQnVsa09wZXJhdGlvbkxvY2sge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICByZXR1cm4ge1xuICAgIGhlYXJ0YmVhdEF0OiBub3csXG4gICAgaG9zdDogaG9zdG5hbWUoKSxcbiAgICBvcGVyYXRpb25JZDogJycsXG4gICAgcGx1Z2luSWQ6ICcnLFxuICAgIHByb2Nlc3NJZDogcHJvY2Vzcy5waWQsXG4gICAgcHJvY2Vzc1N0YXJ0ZWRBdDogZ2V0UHJvY2Vzc1N0YXJ0ZWRBdCgpLFxuICAgIHN0YXJ0ZWRBdDogbm93XG4gIH07XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGV4ZWNHaXQocGFyYW1zOiBFeGVjR2l0UGFyYW1zKTogUHJvbWlzZTxHaXRDb21tYW5kUmVzdWx0PiB7XG4gIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgZXhlY0ZpbGUoJ2dpdCcsIFsuLi5wYXJhbXMuZ2l0QXJndW1lbnRzXSwgeyBjd2Q6IHBhcmFtcy5jd2QsIG1heEJ1ZmZlcjogTUFYX0JVRkZFUl9JTl9CWVRFUywgd2luZG93c0hpZGU6IHRydWUgfSwgKGVycm9yLCBzdGRvdXQsIHN0ZGVycikgPT4ge1xuICAgICAgaWYgKGVycm9yICYmIHR5cGVvZiBlcnJvci5jb2RlICE9PSAnbnVtYmVyJykge1xuICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gcnVuIGdpdCAke3BhcmFtcy5naXRBcmd1bWVudHMuam9pbignICcpfWAsIHsgY2F1c2U6IGVycm9yIH0pKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgcmVzb2x2ZSh7IGV4aXRDb2RlOiB0eXBlb2YgZXJyb3I/LmNvZGUgPT09ICdudW1iZXInID8gZXJyb3IuY29kZSA6IDAsIHN0ZGVyciwgc3Rkb3V0IH0pO1xuICAgIH0pO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gZ2V0UHJvY2Vzc1N0YXJ0ZWRBdCgpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5yb3VuZChwZXJmb3JtYW5jZS50aW1lT3JpZ2luKTtcbn1cblxuZnVuY3Rpb24gaXNFcnJvcldpdGhDb2RlKGVycm9yOiB1bmtub3duLCBjb2RlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIHR5cGVvZiBlcnJvciA9PT0gJ29iamVjdCcgJiYgZXJyb3IgIT09IG51bGwgJiYgJ2NvZGUnIGluIGVycm9yICYmIGVycm9yLmNvZGUgPT09IGNvZGU7XG59XG5cbmZ1bmN0aW9uIGlzUHJvY2Vzc0FsaXZlKHByb2Nlc3NJZDogbnVtYmVyKTogYm9vbGVhbiB8IG51bGwge1xuICBpZiAocHJvY2Vzc0lkIDw9IDApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgdHJ5IHtcbiAgICBwcm9jZXNzLmtpbGwocHJvY2Vzc0lkLCAwKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICByZXR1cm4gIWlzRXJyb3JXaXRoQ29kZShlcnJvciwgJ0VTUkNIJykgJiYgKGlzRXJyb3JXaXRoQ29kZShlcnJvciwgJ0VQRVJNJykgfHwgbnVsbCk7XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFnQkEsU0FBUyx1QkFBdUI7QUFDaEMsU0FBUyxpQkFBaUI7QUFDMUIsU0FBUyxvQkFBb0I7QUFDN0IsU0FBUyx5QkFBeUI7QUFDbEMsU0FBUyxnQkFBZ0I7QUFDekI7QUFBQSxFQUNFO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLE9BQ0s7QUFDUDtBQUFBLEVBQ0U7QUFBQSxFQUNBO0FBQUEsT0FDSztBQUNQLFNBQVMsWUFBWTtBQU9yQixTQUFTLDZDQUE2QztBQUN0RDtBQUFBLEVBQ0U7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsT0FDSztBQXNHUCxN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
|