@dbx-tools/cli-service 0.9.36
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/LICENSE +202 -0
- package/README.md +68 -0
- package/index.ts +14 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +11 -0
- package/lib/src/_config.d.ts +3 -0
- package/lib/src/_config.js +17 -0
- package/lib/src/_control.d.ts +10 -0
- package/lib/src/_control.js +107 -0
- package/lib/src/_host.d.ts +1 -0
- package/lib/src/_host.js +221 -0
- package/lib/src/_package.d.ts +12 -0
- package/lib/src/_package.js +140 -0
- package/lib/src/_paths.d.ts +17 -0
- package/lib/src/_paths.js +55 -0
- package/lib/src/_runtime.d.ts +2 -0
- package/lib/src/_runtime.js +49 -0
- package/lib/src/_startup.d.ts +11 -0
- package/lib/src/_startup.js +81 -0
- package/lib/src/cli.d.ts +19 -0
- package/lib/src/cli.js +61 -0
- package/lib/src/definition.d.ts +82 -0
- package/lib/src/definition.js +91 -0
- package/lib/src/service.d.ts +101 -0
- package/lib/src/service.js +296 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +92 -0
- package/src/_config.ts +22 -0
- package/src/_control.ts +127 -0
- package/src/_host.ts +249 -0
- package/src/_package.ts +152 -0
- package/src/_paths.ts +104 -0
- package/src/_runtime.ts +66 -0
- package/src/_startup.ts +106 -0
- package/src/cli.ts +89 -0
- package/src/definition.ts +119 -0
- package/src/service.ts +472 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install, start, stop, and inspect a system-tray user service.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { readdir, rm } from "node:fs/promises";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
11
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
12
|
+
import * as bin from "@dbx-tools/core/bin";
|
|
13
|
+
import * as exec from "@dbx-tools/core/exec";
|
|
14
|
+
import { readServiceDefinition, writeServiceDefinition } from "./_config.js";
|
|
15
|
+
import { requestServiceControl } from "./_control.js";
|
|
16
|
+
import { resolveServicePackage } from "./_package.js";
|
|
17
|
+
import { defaultRuntimeContext, resolveServicePaths, } from "./_paths.js";
|
|
18
|
+
import { externalRuntimePackages, installServiceRuntime } from "./_runtime.js";
|
|
19
|
+
import { installStartup, removeStartup } from "./_startup.js";
|
|
20
|
+
import { CliServiceDefinitionSchema, } from "./definition.js";
|
|
21
|
+
const DEFAULT_START_TIMEOUT_MILLISECONDS = 15_000;
|
|
22
|
+
const DEFAULT_STOP_TIMEOUT_MILLISECONDS = 10_000;
|
|
23
|
+
const POLL_INTERVAL_MILLISECONDS = 100;
|
|
24
|
+
/** Cross-platform lifecycle manager for one system-tray user service. */
|
|
25
|
+
export class CliService {
|
|
26
|
+
definition;
|
|
27
|
+
runtime;
|
|
28
|
+
globalHomeDirectory;
|
|
29
|
+
bunExecutable;
|
|
30
|
+
compiler;
|
|
31
|
+
runtimeInstaller;
|
|
32
|
+
hostEntrypoint;
|
|
33
|
+
trayExecutable;
|
|
34
|
+
/**
|
|
35
|
+
* Create a lifecycle manager from a serializable definition.
|
|
36
|
+
*
|
|
37
|
+
* @param definition Product-owned service metadata, process, and menu.
|
|
38
|
+
* @param options Host runtime overrides.
|
|
39
|
+
*/
|
|
40
|
+
constructor(definition, options = {}) {
|
|
41
|
+
this.definition = CliServiceDefinitionSchema.parse(definition);
|
|
42
|
+
const defaults = defaultRuntimeContext();
|
|
43
|
+
this.runtime = {
|
|
44
|
+
platform: options.platform ?? defaults.platform,
|
|
45
|
+
environment: options.environment ?? defaults.environment,
|
|
46
|
+
homeDirectory: options.homeDirectory ?? defaults.homeDirectory,
|
|
47
|
+
temporaryDirectory: options.temporaryDirectory ?? defaults.temporaryDirectory,
|
|
48
|
+
};
|
|
49
|
+
this.globalHomeDirectory =
|
|
50
|
+
options.globalHomeDirectory ?? join(this.runtime.homeDirectory, ".dbx-tools");
|
|
51
|
+
this.bunExecutable = options.bunExecutable ?? resolveBunExecutable();
|
|
52
|
+
this.compiler =
|
|
53
|
+
options.compile ??
|
|
54
|
+
((entrypoint, output, workingDirectory, external) => compileWithBun(this.bunExecutable, entrypoint, output, workingDirectory, external));
|
|
55
|
+
this.runtimeInstaller = options.installRuntime ?? installServiceRuntime;
|
|
56
|
+
this.hostEntrypoint = options.hostEntrypoint ?? resolveHostEntrypoint();
|
|
57
|
+
this.trayExecutable = options.trayExecutable ?? resolveTrayExecutable(this.runtime.platform);
|
|
58
|
+
}
|
|
59
|
+
/** Install the login entry and optionally start the service. */
|
|
60
|
+
async install(options = {}) {
|
|
61
|
+
const owner = resolveServicePackage(this.definition.packageName);
|
|
62
|
+
const dependencies = owner.dependencies();
|
|
63
|
+
const external = new Set(externalRuntimePackages(dependencies));
|
|
64
|
+
await this.runtimeInstaller(this.bunExecutable, this.globalHomeDirectory, Object.fromEntries(Object.entries(dependencies).filter(([name]) => external.has(name))));
|
|
65
|
+
const paths = resolveServicePaths(this.definition, this.runtime);
|
|
66
|
+
const installedDefinition = await this.installDefinition(owner);
|
|
67
|
+
const host = await this.ensureCompiledBinary(this.binaryName(installedDefinition, "service"), this.hostEntrypoint, resolve(dirname(this.hostEntrypoint), ".."));
|
|
68
|
+
await this.ensureTrayBinary();
|
|
69
|
+
await this.stop();
|
|
70
|
+
await writeServiceDefinition(paths.configFile, installedDefinition);
|
|
71
|
+
await installStartup(installedDefinition, paths, this.runtime, this.launch(host.path, paths.configFile));
|
|
72
|
+
if (options.start ?? true)
|
|
73
|
+
await this.start();
|
|
74
|
+
}
|
|
75
|
+
/** Start the installed service when it is not already running. */
|
|
76
|
+
async start() {
|
|
77
|
+
const paths = resolveServicePaths(this.definition, this.runtime);
|
|
78
|
+
if (!(await this.status()).installed) {
|
|
79
|
+
throw new Error(`${this.definition.name} is not installed`);
|
|
80
|
+
}
|
|
81
|
+
if ((await requestServiceControl(paths.controlAddress, "status")) !== undefined)
|
|
82
|
+
return;
|
|
83
|
+
const installedDefinition = await readServiceDefinition(paths.configFile);
|
|
84
|
+
const launch = this.launch(this.binaryContext(this.binaryName(installedDefinition, "service")).path, paths.configFile);
|
|
85
|
+
const child = spawn(launch.executable, launch.arguments, {
|
|
86
|
+
detached: true,
|
|
87
|
+
stdio: "ignore",
|
|
88
|
+
});
|
|
89
|
+
child.unref();
|
|
90
|
+
await Promise.race([
|
|
91
|
+
waitForState(async () => (await requestServiceControl(paths.controlAddress, "status")) !== undefined, true, DEFAULT_START_TIMEOUT_MILLISECONDS, `${this.definition.name} did not start`),
|
|
92
|
+
new Promise((_resolve, reject) => {
|
|
93
|
+
child.once("error", reject);
|
|
94
|
+
child.once("exit", (code, signal) => {
|
|
95
|
+
reject(new Error(`${this.definition.name} host exited before startup (${signal ?? String(code)})`));
|
|
96
|
+
});
|
|
97
|
+
}),
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
/** Stop the running tray host and its managed child process. */
|
|
101
|
+
async stop() {
|
|
102
|
+
const paths = resolveServicePaths(this.definition, this.runtime);
|
|
103
|
+
const response = await requestServiceControl(paths.controlAddress, "stop");
|
|
104
|
+
if (!response)
|
|
105
|
+
return;
|
|
106
|
+
await waitForState(async () => (await requestServiceControl(paths.controlAddress, "status")) !== undefined, false, DEFAULT_STOP_TIMEOUT_MILLISECONDS, `${this.definition.name} did not stop`);
|
|
107
|
+
}
|
|
108
|
+
/** Stop and start the installed service. */
|
|
109
|
+
async restart() {
|
|
110
|
+
await this.stop();
|
|
111
|
+
await this.start();
|
|
112
|
+
}
|
|
113
|
+
/** Stop the service and remove its login entry and package-owned state. */
|
|
114
|
+
async uninstall() {
|
|
115
|
+
const paths = resolveServicePaths(this.definition, this.runtime);
|
|
116
|
+
await this.stop();
|
|
117
|
+
await removeStartup(paths);
|
|
118
|
+
await rm(paths.directory, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
/** Read the current installation and process state. */
|
|
121
|
+
async status() {
|
|
122
|
+
const paths = resolveServicePaths(this.definition, this.runtime);
|
|
123
|
+
const installed = existsSync(paths.configFile);
|
|
124
|
+
const response = await requestServiceControl(paths.controlAddress, "status");
|
|
125
|
+
return {
|
|
126
|
+
installed,
|
|
127
|
+
running: response !== undefined,
|
|
128
|
+
...(response
|
|
129
|
+
? {
|
|
130
|
+
pid: response.pid,
|
|
131
|
+
...(response.servicePid ? { servicePid: response.servicePid } : {}),
|
|
132
|
+
}
|
|
133
|
+
: {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
launch(executable, configFile) {
|
|
137
|
+
return {
|
|
138
|
+
executable,
|
|
139
|
+
arguments: ["--service-config", configFile],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async installDefinition(owner) {
|
|
143
|
+
const definition = CliServiceDefinitionSchema.parse({
|
|
144
|
+
...this.definition,
|
|
145
|
+
version: this.definition.version ?? owner.version,
|
|
146
|
+
});
|
|
147
|
+
const command = definition.command
|
|
148
|
+
? await this.installCommand(definition, owner, definition.command, "command")
|
|
149
|
+
: undefined;
|
|
150
|
+
const menu = [];
|
|
151
|
+
for (const [index, item] of (definition.menu ?? []).entries()) {
|
|
152
|
+
menu.push(item.type === "command"
|
|
153
|
+
? {
|
|
154
|
+
...item,
|
|
155
|
+
command: await this.installCommand(definition, owner, item.command, `menu-${index + 1}`),
|
|
156
|
+
}
|
|
157
|
+
: item);
|
|
158
|
+
}
|
|
159
|
+
return CliServiceDefinitionSchema.parse({
|
|
160
|
+
...definition,
|
|
161
|
+
...(command ? { command } : {}),
|
|
162
|
+
...(menu.length > 0 ? { menu } : {}),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async installCommand(definition, owner, command, purpose) {
|
|
166
|
+
if (command.executable)
|
|
167
|
+
return command;
|
|
168
|
+
const entrypoint = command.entrypoint ?? owner.bin(command.binName);
|
|
169
|
+
const installed = await this.ensureCompiledBinary(this.binaryName(definition, purpose), resolve(entrypoint), owner.directory, externalRuntimePackages(owner.dependencies()));
|
|
170
|
+
return {
|
|
171
|
+
executable: installed.path,
|
|
172
|
+
...(command.arguments ? { arguments: command.arguments } : {}),
|
|
173
|
+
...(command.environment ? { environment: command.environment } : {}),
|
|
174
|
+
cwd: command.cwd ?? this.globalHomeDirectory,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async ensureCompiledBinary(name, entrypoint, workingDirectory, external = []) {
|
|
178
|
+
return bin.ensure(name, async ({ tempDir }) => {
|
|
179
|
+
const output = join(tempDir, executableName(name, this.runtime.platform));
|
|
180
|
+
await this.compiler(entrypoint, output, workingDirectory, external);
|
|
181
|
+
return pathToFileURL(output).href;
|
|
182
|
+
}, {
|
|
183
|
+
destination: this.binaryContext(name),
|
|
184
|
+
skipVersionCheck: true,
|
|
185
|
+
force: true,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async ensureTrayBinary() {
|
|
189
|
+
const name = trayExecutableName(this.runtime.platform);
|
|
190
|
+
const binDir = join(this.globalHomeDirectory, "bin", "traybin");
|
|
191
|
+
return bin.ensure(name, pathToFileURL(this.trayExecutable).href, {
|
|
192
|
+
destination: {
|
|
193
|
+
root: this.globalHomeDirectory,
|
|
194
|
+
binDir,
|
|
195
|
+
path: join(binDir, name),
|
|
196
|
+
},
|
|
197
|
+
skipVersionCheck: true,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
binaryContext(name) {
|
|
201
|
+
const binDir = join(this.globalHomeDirectory, "bin");
|
|
202
|
+
return {
|
|
203
|
+
root: this.globalHomeDirectory,
|
|
204
|
+
binDir,
|
|
205
|
+
path: join(binDir, executableName(name, this.runtime.platform)),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
binaryName(definition, purpose) {
|
|
209
|
+
const name = safeToken(definition.name.toLowerCase()).replace(/[._]+/g, "-");
|
|
210
|
+
return purpose === "service" ? name : `${name}-${purpose}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function resolveHostEntrypoint() {
|
|
214
|
+
const current = fileURLToPath(import.meta.url);
|
|
215
|
+
const source = extname(current) === ".ts"
|
|
216
|
+
? join(dirname(current), "_host.ts")
|
|
217
|
+
: resolve(dirname(current), "..", "..", "src", "_host.ts");
|
|
218
|
+
if (existsSync(source))
|
|
219
|
+
return source;
|
|
220
|
+
throw new Error(`could not resolve cli-service host source: ${source}`);
|
|
221
|
+
}
|
|
222
|
+
function resolveBunExecutable() {
|
|
223
|
+
return createRequire(import.meta.url).resolve("bun/bin/bun.exe");
|
|
224
|
+
}
|
|
225
|
+
function trayExecutableName(platform) {
|
|
226
|
+
if (platform === "darwin")
|
|
227
|
+
return "tray_darwin_release";
|
|
228
|
+
if (platform === "linux")
|
|
229
|
+
return "tray_linux_release";
|
|
230
|
+
if (platform === "win32")
|
|
231
|
+
return "tray_windows_release.exe";
|
|
232
|
+
throw new Error(`systray2 does not support ${platform}`);
|
|
233
|
+
}
|
|
234
|
+
function resolveTrayExecutable(platform) {
|
|
235
|
+
const manifest = createRequire(import.meta.url).resolve("systray2/package.json");
|
|
236
|
+
return join(dirname(manifest), "traybin", trayExecutableName(platform));
|
|
237
|
+
}
|
|
238
|
+
function executableName(name, platform) {
|
|
239
|
+
return platform === "win32" ? `${name}.exe` : name;
|
|
240
|
+
}
|
|
241
|
+
function safeToken(value) {
|
|
242
|
+
const token = value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
243
|
+
if (!token)
|
|
244
|
+
throw new TypeError(`invalid binary version: ${value}`);
|
|
245
|
+
return token;
|
|
246
|
+
}
|
|
247
|
+
async function compileWithBun(bunExecutable, entrypoint, output, workingDirectory, external = []) {
|
|
248
|
+
const before = await bunBuildArtifacts(workingDirectory);
|
|
249
|
+
try {
|
|
250
|
+
await exec.spawn(bunExecutable, [
|
|
251
|
+
"build",
|
|
252
|
+
entrypoint,
|
|
253
|
+
"--compile",
|
|
254
|
+
"--compile-autoload-package-json",
|
|
255
|
+
"--outfile",
|
|
256
|
+
output,
|
|
257
|
+
...external.flatMap((dependency) => ["--external", dependency]),
|
|
258
|
+
], {
|
|
259
|
+
check: true,
|
|
260
|
+
cwd: workingDirectory,
|
|
261
|
+
env: {
|
|
262
|
+
...process.env,
|
|
263
|
+
BUN_TMPDIR: dirname(output),
|
|
264
|
+
TMPDIR: dirname(output),
|
|
265
|
+
},
|
|
266
|
+
stdin: "ignore",
|
|
267
|
+
stdout: "capture",
|
|
268
|
+
stderr: "capture",
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
for (const artifact of await bunBuildArtifacts(workingDirectory)) {
|
|
273
|
+
if (!before.has(artifact)) {
|
|
274
|
+
await rm(join(workingDirectory, artifact), {
|
|
275
|
+
recursive: true,
|
|
276
|
+
force: true,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function bunBuildArtifacts(directory) {
|
|
283
|
+
return new Set((await readdir(directory))
|
|
284
|
+
.filter((name) => name.startsWith(".") && name.endsWith(".bun-build"))
|
|
285
|
+
.sort());
|
|
286
|
+
}
|
|
287
|
+
async function waitForState(read, expected, timeoutMilliseconds, message) {
|
|
288
|
+
const deadline = Date.now() + timeoutMilliseconds;
|
|
289
|
+
while (Date.now() < deadline) {
|
|
290
|
+
if ((await read()) === expected)
|
|
291
|
+
return;
|
|
292
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MILLISECONDS));
|
|
293
|
+
}
|
|
294
|
+
throw new Error(message);
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFFSCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDM0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNyQyxPQUFPLEVBQUUsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDNUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUM1RCxPQUFPLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUV4RCxPQUFPLEtBQUssR0FBRyxNQUFNLHFCQUFxQixDQUFDO0FBQzNDLE9BQU8sS0FBSyxJQUFJLE1BQU0sc0JBQXNCLENBQUM7QUFFN0MsT0FBTyxFQUFFLHFCQUFxQixFQUFFLHNCQUFzQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN0RCxPQUFPLEVBQUUscUJBQXFCLEVBQXVCLE1BQU0sZUFBZSxDQUFDO0FBQzNFLE9BQU8sRUFDTCxxQkFBcUIsRUFDckIsbUJBQW1CLEdBRXBCLE1BQU0sYUFBYSxDQUFDO0FBQ3JCLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMvRSxPQUFPLEVBQUUsY0FBYyxFQUFFLGFBQWEsRUFBc0IsTUFBTSxlQUFlLENBQUM7QUFDbEYsT0FBTyxFQUNMLDBCQUEwQixHQUkzQixNQUFNLGlCQUFpQixDQUFDO0FBRXpCLE1BQU0sa0NBQWtDLEdBQUcsTUFBTSxDQUFDO0FBQ2xELE1BQU0saUNBQWlDLEdB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QUFDLE1BQU0sSUFBSSxFQUFFLENBQUMsS0FBSyxRQUFRO1lBQUUsT0FBTztRQUN4QyxNQUFNLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLDBCQUEwQixDQUFDLENBQUMsQ0FBQztJQUNsRixDQUFDO0lBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUMzQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBJbnN0YWxsLCBzdGFydCwgc3RvcCwgYW5kIGluc3BlY3QgYSBzeXN0ZW0tdHJheSB1c2VyIHNlcnZpY2UuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHNwYXduIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHsgZXhpc3RzU3luYyB9IGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgeyByZWFkZGlyLCBybSB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSBcIm5vZGU6bW9kdWxlXCI7XG5pbXBvcnQgeyBkaXJuYW1lLCBleHRuYW1lLCBqb2luLCByZXNvbHZlIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgcGF0aFRvRmlsZVVSTCB9IGZyb20gXCJub2RlOnVybFwiO1xuXG5pbXBvcnQgKiBhcyBiaW4gZnJvbSBcIkBkYngtdG9vbHMvY29yZS9iaW5cIjtcbmltcG9ydCAqIGFzIGV4ZWMgZnJvbSBcIkBkYngtdG9vbHMvY29yZS9leGVjXCI7XG5cbmltcG9ydCB7IHJlYWRTZXJ2aWNlRGVmaW5pdGlvbiwgd3JpdGVTZXJ2aWNlRGVmaW5pdGlvbiB9IGZyb20gXCIuL19jb25maWcudHNcIjtcbmltcG9ydCB7IHJlcXVlc3RTZXJ2aWNlQ29udHJvbCB9IGZyb20gXCIuL19jb250cm9sLnRzXCI7XG5pbXBvcnQgeyByZXNvbHZlU2VydmljZVBhY2thZ2UsIHR5cGUgU2VydmljZVBhY2thZ2UgfSBmcm9tIFwiLi9fcGFja2FnZS50c1wiO1xuaW1wb3J0IHtcbiAgZGVmYXVsdFJ1bnRpbWVDb250ZXh0LFxuICByZXNvbHZlU2VydmljZVBhdGhzLFxuICB0eXBlIFNlcnZpY2VSdW50aW1lQ29udGV4dCxcbn0gZnJvbSBcIi4vX3BhdGhzLnRzXCI7XG5pbXBvcnQgeyBleHRlcm5hbFJ1bnRpbWVQYWNrYWdlcywgaW5zdGFsbFNlcnZpY2VSdW50aW1lIH0gZnJvbSBcIi4vX3J1bnRpbWUudHNcIjtcbmltcG9ydCB7IGluc3RhbGxTdGFydHVwLCByZW1vdmVTdGFydHVwLCB0eXBlIFNlcnZpY2VMYXVuY2ggfSBmcm9tIFwiLi9fc3RhcnR1cC50c1wiO1xuaW1wb3J0IHtcbiAgQ2xpU2VydmljZURlZmluaXRpb25TY2hlbWEsXG4gIHR5cGUgQ2xpU2VydmljZUNvbW1hbmQsXG4gIHR5cGUgQ2xpU2VydmljZURlZmluaXRpb24sXG4gIHR5cGUgQ2xpU2VydmljZU1lbnVJdGVtLFxufSBmcm9tIFwiLi9kZWZpbml0aW9uLnRzXCI7XG5cbmNvbnN0IERFRkFVTFRfU1RBUlRfVElNRU9VVF9NSUxMSVNFQ09ORFMgPSAxNV8wMDA7XG5jb25zdCBERUZBVUxUX1NUT1BfVElNRU9VVF9NSUxMSVNFQ09ORFMgPSAxMF8wMDA7XG5jb25zdCBQT0xMX0lOVEVSVkFMX01JTExJU0VDT05EUyA9IDEwMDtcblxuLyoqIENvbXBpbGUgb25lIFR5cGVTY3JpcHQgb3IgSmF2YVNjcmlwdCBlbnRyeXBvaW50IGludG8gYSBzdGFuZGFsb25lIGV4ZWN1dGFibGUuICovXG5leHBvcnQgdHlwZSBDbGlTZXJ2aWNlQ29tcGlsZXIgPSAoXG4gIGVudHJ5cG9pbnQ6IHN0cmluZyxcbiAgb3V0cHV0OiBzdHJpbmcsXG4gIHdvcmtpbmdEaXJlY3Rvcnk6IHN0cmluZyxcbiAgZXh0ZXJuYWw/OiByZWFkb25seSBzdHJpbmdbXSxcbikgPT4gUHJvbWlzZTx2b2lkPjtcblxuLyoqIEluc3RhbGwgZXhhY3QgZXh0ZXJuYWwgcnVudGltZSBkZXBlbmRlbmNpZXMgaW50byB0aGUgZGJ4LXRvb2xzIGhvbWUuICovXG5leHBvcnQgdHlwZSBDbGlTZXJ2aWNlUnVudGltZUluc3RhbGxlciA9IChcbiAgYnVuRXhlY3V0YWJsZTogc3RyaW5nLFxuICBkaXJlY3Rvcnk6IHN0cmluZyxcbiAgZGVwZW5kZW5jaWVzOiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+PixcbikgPT4gUHJvbWlzZTx2b2lkPjtcblxuLyoqIEhvc3QgcnVudGltZSBvdmVycmlkZXMgZm9yIHRlc3RzLCBlbWJlZGRlZCBkaXN0cmlidXRpb25zLCBhbmQgbm9uc3RhbmRhcmQgaG9tZXMuICovXG5leHBvcnQgaW50ZXJmYWNlIENsaVNlcnZpY2VSdW50aW1lT3B0aW9ucyB7XG4gIC8qKiBPcGVyYXRpbmcgc3lzdGVtIHVzZWQgZm9yIGNvbmZpZ3VyYXRpb24gYW5kIHN0YXJ0dXAgcmVnaXN0cmF0aW9uLiAqL1xuICByZWFkb25seSBwbGF0Zm9ybT86IE5vZGVKUy5QbGF0Zm9ybTtcbiAgLyoqIEVudmlyb25tZW50IHVzZWQgdG8gbG9jYXRlIHBsYXRmb3JtIGNvbmZpZ3VyYXRpb24gZGlyZWN0b3JpZXMuICovXG4gIHJlYWRvbmx5IGVudmlyb25tZW50PzogUmVhZG9ubHk8Tm9kZUpTLlByb2Nlc3NFbnY+O1xuICAvKiogSG9tZSBkaXJlY3RvcnkgdXNlZCB3aGVuIHBsYXRmb3JtIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgYWJzZW50LiAqL1xuICByZWFkb25seSBob21lRGlyZWN0b3J5Pzogc3RyaW5nO1xuICAvKiogVGVtcG9yYXJ5IGRpcmVjdG9yeSB1c2VkIGZvciB0aGUgbG9jYWwgY29udHJvbCBzb2NrZXQuICovXG4gIHJlYWRvbmx5IHRlbXBvcmFyeURpcmVjdG9yeT86IHN0cmluZztcbiAgLyoqIFNoYXJlZCBkYngtdG9vbHMgaG9tZSBjb250YWluaW5nIGluc3RhbGxlZCBzZXJ2aWNlIGJpbmFyaWVzLiAqL1xuICByZWFkb25seSBnbG9iYWxIb21lRGlyZWN0b3J5Pzogc3RyaW5nO1xuICAvKiogUGFja2FnZS1wcm92aWRlZCBCdW4gZXhlY3V0YWJsZSBvdmVycmlkZS4gKi9cbiAgcmVhZG9ubHkgYnVuRXhlY3V0YWJsZT86IHN0cmluZztcbiAgLyoqIFN0YW5kYWxvbmUgY29tcGlsZXIgb3ZlcnJpZGUuICovXG4gIHJlYWRvbmx5IGNvbXBpbGU/OiBDbGlTZXJ2aWNlQ29tcGlsZXI7XG4gIC8qKiBFeHRlcm5hbCBydW50aW1lIGRlcGVuZGVuY3kgaW5zdGFsbGVyIG92ZXJyaWRlLiAqL1xuICByZWFkb25seSBpbnN0YWxsUnVudGltZT86IENsaVNlcnZpY2VSdW50aW1lSW5zdGFsbGVyO1xuICAvKiogU3lzdGVtLXRyYXkgaG9zdCBzb3VyY2UgZW50cnlwb2ludCBvdmVycmlkZS4gKi9cbiAgcmVhZG9ubHkgaG9zdEVudHJ5cG9pbnQ/OiBzdHJpbmc7XG4gIC8qKiBzeXN0cmF5MiBuYXRpdmUgZXhlY3V0YWJsZSBvdmVycmlkZS4gKi9cbiAgcmVhZG9ubHkgdHJheUV4ZWN1dGFibGU/OiBzdHJpbmc7XG59XG5cbi8qKiBPcHRpb25zIGNvbnRyb2xsaW5nIHNlcnZpY2UgaW5zdGFsbGF0aW9uLiAqL1xuZXhwb3J0IGludGVyZmFjZSBDbGlTZXJ2aWNlSW5zdGFsbE9wdGlvbnMge1xuICAvKiogU3RhcnQgdGhlIHNlcnZpY2UgaW1tZWRpYXRlbHkgYWZ0ZXIgd3JpdGluZyBpdHMgY29uZmlndXJhdGlvbiBhbmQgbG9naW4gZW50cnkuICovXG4gIHJlYWRvbmx5IHN0YXJ0PzogYm9vbGVhbjtcbn1cblxuLyoqIEN1cnJlbnQgaW5zdGFsbGF0aW9uIGFuZCBwcm9jZXNzIHN0YXRlIGZvciBhIHN5c3RlbS10cmF5IHNlcnZpY2UuICovXG5leHBvcnQgaW50ZXJmYWNlIENsaVNlcnZpY2VTdGF0dXMge1xuICAvKiogV2hldGhlciB0aGUgc2VyaWFsaXplZCBzZXJ2aWNlIGRlZmluaXRpb24gZXhpc3RzLiAqL1xuICByZWFkb25seSBpbnN0YWxsZWQ6IGJvb2xlYW47XG4gIC8qKiBXaGV0aGVyIHRoZSB0cmF5IGhvc3QgYW5zd2VyZWQgaXRzIGxvY2FsIGNvbnRyb2wgc29ja2V0LiAqL1xuICByZWFkb25seSBydW5uaW5nOiBib29sZWFuO1xuICAvKiogVHJheSBob3N0IHByb2Nlc3MgSUQgd2hlbiBydW5uaW5nLiAqL1xuICByZWFkb25seSBwaWQ/OiBudW1iZXI7XG4gIC8qKiBNYW5hZ2VkIGNoaWxkIHByb2Nlc3MgSUQgd2hlbiB0aGUgZGVmaW5pdGlvbiBpbmNsdWRlcyBhIGNvbW1hbmQuICovXG4gIHJlYWRvbmx5IHNlcnZpY2VQaWQ/OiBudW1iZXI7XG59XG5cbi8qKiBMaWZlY3ljbGUgY29udHJhY3QgY29uc3VtZWQgYnkgQ0xJIGNvbW1hbmQgYnVpbGRlcnMuICovXG5leHBvcnQgaW50ZXJmYWNlIENsaVNlcnZpY2VMaWZlY3ljbGUge1xuICAvKiogSW5zdGFsbCB0aGUgbG9naW4gZW50cnkgYW5kIG9wdGlvbmFsbHkgc3RhcnQgdGhlIHNlcnZpY2UuICovXG4gIGluc3RhbGwob3B0aW9ucz86IENsaVNlcnZpY2VJbnN0YWxsT3B0aW9ucyk6IFByb21pc2U8dm9pZD47XG4gIC8qKiBTdGFydCB0aGUgaW5zdGFsbGVkIHNlcnZpY2Ugd2hlbiBpdCBpcyBub3QgYWxyZWFkeSBydW5uaW5nLiAqL1xuICBzdGFydCgpOiBQcm9taXNlPHZvaWQ+O1xuICAvKiogU3RvcCB0aGUgcnVubmluZyB0cmF5IGhvc3QgYW5kIGl0cyBtYW5hZ2VkIGNoaWxkIHByb2Nlc3MuICovXG4gIHN0b3AoKTogUHJvbWlzZTx2b2lkPjtcbiAgLyoqIFN0b3AgYW5kIHN0YXJ0IHRoZSBpbnN0YWxsZWQgc2VydmljZS4gKi9cbiAgcmVzdGFydCgpOiBQcm9taXNlPHZvaWQ+O1xuICAvKiogU3RvcCB0aGUgc2VydmljZSBhbmQgcmVtb3ZlIGl0cyBsb2dpbiBlbnRyeSBhbmQgcGFja2FnZS1vd25lZCBzdGF0ZS4gKi9cbiAgdW5pbnN0YWxsKCk6IFByb21pc2U8dm9pZD47XG4gIC8qKiBSZWFkIHRoZSBjdXJyZW50IGluc3RhbGxhdGlvbiBhbmQgcHJvY2VzcyBzdGF0ZS4gKi9cbiAgc3RhdHVzKCk6IFByb21pc2U8Q2xpU2VydmljZVN0YXR1cz47XG59XG5cbi8qKiBDcm9zcy1wbGF0Zm9ybSBsaWZlY3ljbGUgbWFuYWdlciBmb3Igb25lIHN5c3RlbS10cmF5IHVzZXIgc2VydmljZS4gKi9cbmV4cG9ydCBjbGFzcyBDbGlTZXJ2aWNlIGltcGxlbWVudHMgQ2xpU2VydmljZUxpZmVjeWNsZSB7XG4gIHJlYWRvbmx5IGRlZmluaXRpb246IENsaVNlcnZpY2VEZWZpbml0aW9uO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgcnVudGltZTogU2VydmljZVJ1bnRpbWVDb250ZXh0O1xuICBwcml2YXRlIHJlYWRvbmx5IGdsb2JhbEhvbWVEaXJlY3Rvcnk6IHN0cmluZztcbiAgcHJpdmF0ZSByZWFkb25seSBidW5FeGVjdXRhYmxlOiBzdHJpbmc7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29tcGlsZXI6IENsaVNlcnZpY2VDb21waWxlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBydW50aW1lSW5zdGFsbGVyOiBDbGlTZXJ2aWNlUnVudGltZUluc3RhbGxlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBob3N0RW50cnlwb2ludDogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IHRyYXlFeGVjdXRhYmxlOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIGxpZmVjeWNsZSBtYW5hZ2VyIGZyb20gYSBzZXJpYWxpemFibGUgZGVmaW5pdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIGRlZmluaXRpb24gUHJvZHVjdC1vd25lZCBzZXJ2aWNlIG1ldGFkYXRhLCBwcm9jZXNzLCBhbmQgbWVudS5cbiAgICogQHBhcmFtIG9wdGlvbnMgSG9zdCBydW50aW1lIG92ZXJyaWRlcy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKGRlZmluaXRpb246IENsaVNlcnZpY2VEZWZpbml0aW9uLCBvcHRpb25zOiBDbGlTZXJ2aWNlUnVudGltZU9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuZGVmaW5pdGlvbiA9IENsaVNlcnZpY2VEZWZpbml0aW9uU2NoZW1hLnBhcnNlKGRlZmluaXRpb24pO1xuICAgIGNvbnN0IGRlZmF1bHRzID0gZGVmYXVsdFJ1bnRpbWVDb250ZXh0KCk7XG4gICAgdGhpcy5ydW50aW1lID0ge1xuICAgICAgcGxhdGZvcm06IG9wdGlvbnMucGxhdGZvcm0gPz8gZGVmYXVsdHMucGxhdGZvcm0sXG4gICAgICBlbnZpcm9ubWVudDogb3B0aW9ucy5lbnZpcm9ubWVudCA/PyBkZWZhdWx0cy5lbnZpcm9ubWVudCxcbiAgICAgIGhvbWVEaXJlY3Rvcnk6IG9wdGlvbnMuaG9tZURpcmVjdG9yeSA/PyBkZWZhdWx0cy5ob21lRGlyZWN0b3J5LFxuICAgICAgdGVtcG9yYXJ5RGlyZWN0b3J5OiBvcHRpb25zLnRlbXBvcmFyeURpcmVjdG9yeSA/PyBkZWZhdWx0cy50ZW1wb3JhcnlEaXJlY3RvcnksXG4gICAgfTtcbiAgICB0aGlzLmdsb2JhbEhvbWVEaXJlY3RvcnkgPVxuICAgICAgb3B0aW9ucy5nbG9iYWxIb21lRGlyZWN0b3J5ID8/IGpvaW4odGhpcy5ydW50aW1lLmhvbWVEaXJlY3RvcnksIFwiLmRieC10b29sc1wiKTtcbiAgICB0aGlzLmJ1bkV4ZWN1dGFibGUgPSBvcHRpb25zLmJ1bkV4ZWN1dGFibGUgPz8gcmVzb2x2ZUJ1bkV4ZWN1dGFibGUoKTtcbiAgICB0aGlzLmNvbXBpbGVyID1cbiAgICAgIG9wdGlvbnMuY29tcGlsZSA/P1xuICAgICAgKChlbnRyeXBvaW50LCBvdXRwdXQsIHdvcmtpbmdEaXJlY3RvcnksIGV4dGVybmFsKSA9PlxuICAgICAgICBjb21waWxlV2l0aEJ1bih0aGlzLmJ1bkV4ZWN1dGFibGUsIGVudHJ5cG9pbnQsIG91dHB1dCwgd29ya2luZ0RpcmVjdG9yeSwgZXh0ZXJuYWwpKTtcbiAgICB0aGlzLnJ1bnRpbWVJbnN0YWxsZXIgPSBvcHRpb25zLmluc3RhbGxSdW50aW1lID8/IGluc3RhbGxTZXJ2aWNlUnVudGltZTtcbiAgICB0aGlzLmhvc3RFbnRyeXBvaW50ID0gb3B0aW9ucy5ob3N0RW50cnlwb2ludCA/PyByZXNvbHZlSG9zdEVudHJ5cG9pbnQoKTtcbiAgICB0aGlzLnRyYXlFeGVjdXRhYmxlID0gb3B0aW9ucy50cmF5RXhlY3V0YWJsZSA/PyByZXNvbHZlVHJheUV4ZWN1dGFibGUodGhpcy5ydW50aW1lLnBsYXRmb3JtKTtcbiAgfVxuXG4gIC8qKiBJbnN0YWxsIHRoZSBsb2dpbiBlbnRyeSBhbmQgb3B0aW9uYWxseSBzdGFydCB0aGUgc2VydmljZS4gKi9cbiAgYXN5bmMgaW5zdGFsbChvcHRpb25zOiBDbGlTZXJ2aWNlSW5zdGFsbE9wdGlvbnMgPSB7fSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG93bmVyID0gcmVzb2x2ZVNlcnZpY2VQYWNrYWdlKHRoaXMuZGVmaW5pdGlvbi5wYWNrYWdlTmFtZSk7XG4gICAgY29uc3QgZGVwZW5kZW5jaWVzID0gb3duZXIuZGVwZW5kZW5jaWVzKCk7XG4gICAgY29uc3QgZXh0ZXJuYWwgPSBuZXcgU2V0KGV4dGVybmFsUnVudGltZVBhY2thZ2VzKGRlcGVuZGVuY2llcykpO1xuICAgIGF3YWl0IHRoaXMucnVudGltZUluc3RhbGxlcihcbiAgICAgIHRoaXMuYnVuRXhlY3V0YWJsZSxcbiAgICAgIHRoaXMuZ2xvYmFsSG9tZURpcmVjdG9yeSxcbiAgICAgIE9iamVjdC5mcm9tRW50cmllcyhPYmplY3QuZW50cmllcyhkZXBlbmRlbmNpZXMpLmZpbHRlcigoW25hbWVdKSA9PiBleHRlcm5hbC5oYXMobmFtZSkpKSxcbiAgICApO1xuICAgIGNvbnN0IHBhdGhzID0gcmVzb2x2ZVNlcnZpY2VQYXRocyh0aGlzLmRlZmluaXRpb24sIHRoaXMucnVudGltZSk7XG4gICAgY29uc3QgaW5zdGFsbGVkRGVmaW5pdGlvbiA9IGF3YWl0IHRoaXMuaW5zdGFsbERlZmluaXRpb24ob3duZXIpO1xuICAgIGNvbnN0IGhvc3QgPSBhd2FpdCB0aGlzLmVuc3VyZUNvbXBpbGVkQmluYXJ5KFxuICAgICAgdGhpcy5iaW5hcnlOYW1lKGluc3RhbGxlZERlZmluaXRpb24sIFwic2VydmljZVwiKSxcbiAgICAgIHRoaXMuaG9zdEVudHJ5cG9pbnQsXG4gICAgICByZXNvbHZlKGRpcm5hbWUodGhpcy5ob3N0RW50cnlwb2ludCksIFwiLi5cIiksXG4gICAgKTtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVRyYXlCaW5hcnkoKTtcbiAgICBhd2FpdCB0aGlzLnN0b3AoKTtcbiAgICBhd2FpdCB3cml0ZVNlcnZpY2VEZWZpbml0aW9uKHBhdGhzLmNvbmZpZ0ZpbGUsIGluc3RhbGxlZERlZmluaXRpb24pO1xuICAgIGF3YWl0IGluc3RhbGxTdGFydHVwKFxuICAgICAgaW5zdGFsbGVkRGVmaW5pdGlvbixcbiAgICAgIHBhdGhzLFxuICAgICAgdGhpcy5ydW50aW1lLFxuICAgICAgdGhpcy5sYXVuY2goaG9zdC5wYXRoLCBwYXRocy5jb25maWdGaWxlKSxcbiAgICApO1xuICAgIGlmIChvcHRpb25zLnN0YXJ0ID8/IHRydWUpIGF3YWl0IHRoaXMuc3RhcnQoKTtcbiAgfVxuXG4gIC8qKiBTdGFydCB0aGUgaW5zdGFsbGVkIHNlcnZpY2Ugd2hlbiBpdCBpcyBub3QgYWxyZWFkeSBydW5uaW5nLiAqL1xuICBhc3luYyBzdGFydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBwYXRocyA9IHJlc29sdmVTZXJ2aWNlUGF0aHModGhpcy5kZWZpbml0aW9uLCB0aGlzLnJ1bnRpbWUpO1xuICAgIGlmICghKGF3YWl0IHRoaXMuc3RhdHVzKCkpLmluc3RhbGxlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke3RoaXMuZGVmaW5pdGlvbi5uYW1lfSBpcyBub3QgaW5zdGFsbGVkYCk7XG4gICAgfVxuICAgIGlmICgoYXdhaXQgcmVxdWVzdFNlcnZpY2VDb250cm9sKHBhdGhzLmNvbnRyb2xBZGRyZXNzLCBcInN0YXR1c1wiKSkgIT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuXG4gICAgY29uc3QgaW5zdGFsbGVkRGVmaW5pdGlvbiA9IGF3YWl0IHJlYWRTZXJ2aWNlRGVmaW5pdGlvbihwYXRocy5jb25maWdGaWxlKTtcbiAgICBjb25zdCBsYXVuY2ggPSB0aGlzLmxhdW5jaChcbiAgICAgIHRoaXMuYmluYXJ5Q29udGV4dCh0aGlzLmJpbmFyeU5hbWUoaW5zdGFsbGVkRGVmaW5pdGlvbiwgXCJzZXJ2aWNlXCIpKS5wYXRoLFxuICAgICAgcGF0aHMuY29uZmlnRmlsZSxcbiAgICApO1xuICAgIGNvbnN0IGNoaWxkID0gc3Bhd24obGF1bmNoLmV4ZWN1dGFibGUsIGxhdW5jaC5hcmd1bWVudHMsIHtcbiAgICAgIGRldGFjaGVkOiB0cnVlLFxuICAgICAgc3RkaW86IFwiaWdub3JlXCIsXG4gICAgfSk7XG4gICAgY2hpbGQudW5yZWYoKTtcblxuICAgIGF3YWl0IFByb21pc2UucmFjZShbXG4gICAgICB3YWl0Rm9yU3RhdGUoXG4gICAgICAgIGFzeW5jICgpID0+IChhd2FpdCByZXF1ZXN0U2VydmljZUNvbnRyb2wocGF0aHMuY29udHJvbEFkZHJlc3MsIFwic3RhdHVzXCIpKSAhPT0gdW5kZWZpbmVkLFxuICAgICAgICB0cnVlLFxuICAgICAgICBERUZBVUxUX1NUQVJUX1RJTUVPVVRfTUlMTElTRUNPTkRTLFxuICAgICAgICBgJHt0aGlzLmRlZmluaXRpb24ubmFtZX0gZGlkIG5vdCBzdGFydGAsXG4gICAgICApLFxuICAgICAgbmV3IFByb21pc2U8bmV2ZXI+KChfcmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGNoaWxkLm9uY2UoXCJlcnJvclwiLCByZWplY3QpO1xuICAgICAgICBjaGlsZC5vbmNlKFwiZXhpdFwiLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgICAgICAgcmVqZWN0KFxuICAgICAgICAgICAgbmV3IEVycm9yKFxuICAgICAgICAgICAgICBgJHt0aGlzLmRlZmluaXRpb24ubmFtZX0gaG9zdCBleGl0ZWQgYmVmb3JlIHN0YXJ0dXAgKCR7c2lnbmFsID8/IFN0cmluZyhjb2RlKX0pYCxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgICAgfSk7XG4gICAgICB9KSxcbiAgICBdKTtcbiAgfVxuXG4gIC8qKiBTdG9wIHRoZSBydW5uaW5nIHRyYXkgaG9zdCBhbmQgaXRzIG1hbmFnZWQgY2hpbGQgcHJvY2Vzcy4gKi9cbiAgYXN5bmMgc3RvcCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBwYXRocyA9IHJlc29sdmVTZXJ2aWNlUGF0aHModGhpcy5kZWZpbml0aW9uLCB0aGlzLnJ1bnRpbWUpO1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgcmVxdWVzdFNlcnZpY2VDb250cm9sKHBhdGhzLmNvbnRyb2xBZGRyZXNzLCBcInN0b3BcIik7XG4gICAgaWYgKCFyZXNwb25zZSkgcmV0dXJuO1xuICAgIGF3YWl0IHdhaXRGb3JTdGF0ZShcbiAgICAgIGFzeW5jICgpID0+IChhd2FpdCByZXF1ZXN0U2VydmljZUNvbnRyb2wocGF0aHMuY29udHJvbEFkZHJlc3MsIFwic3RhdHVzXCIpKSAhPT0gdW5kZWZpbmVkLFxuICAgICAgZmFsc2UsXG4gICAgICBERUZBVUxUX1NUT1BfVElNRU9VVF9NSUxMSVNFQ09ORFMsXG4gICAgICBgJHt0aGlzLmRlZmluaXRpb24ubmFtZX0gZGlkIG5vdCBzdG9wYCxcbiAgICApO1xuICB9XG5cbiAgLyoqIFN0b3AgYW5kIHN0YXJ0IHRoZSBpbnN0YWxsZWQgc2VydmljZS4gKi9cbiAgYXN5bmMgcmVzdGFydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnN0b3AoKTtcbiAgICBhd2FpdCB0aGlzLnN0YXJ0KCk7XG4gIH1cblxuICAvKiogU3RvcCB0aGUgc2VydmljZSBhbmQgcmVtb3ZlIGl0cyBsb2dpbiBlbnRyeSBhbmQgcGFja2FnZS1vd25lZCBzdGF0ZS4gKi9cbiAgYXN5bmMgdW5pbnN0YWxsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHBhdGhzID0gcmVzb2x2ZVNlcnZpY2VQYXRocyh0aGlzLmRlZmluaXRpb24sIHRoaXMucnVudGltZSk7XG4gICAgYXdhaXQgdGhpcy5zdG9wKCk7XG4gICAgYXdhaXQgcmVtb3ZlU3RhcnR1cChwYXRocyk7XG4gICAgYXdhaXQgcm0ocGF0aHMuZGlyZWN0b3J5LCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gIH1cblxuICAvKiogUmVhZCB0aGUgY3VycmVudCBpbnN0YWxsYXRpb24gYW5kIHByb2Nlc3Mgc3RhdGUuICovXG4gIGFzeW5jIHN0YXR1cygpOiBQcm9taXNlPENsaVNlcnZpY2VTdGF0dXM+IHtcbiAgICBjb25zdCBwYXRocyA9IHJlc29sdmVTZXJ2aWNlUGF0aHModGhpcy5kZWZpbml0aW9uLCB0aGlzLnJ1bnRpbWUpO1xuICAgIGNvbnN0IGluc3RhbGxlZCA9IGV4aXN0c1N5bmMocGF0aHMuY29uZmlnRmlsZSk7XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCByZXF1ZXN0U2VydmljZUNvbnRyb2wocGF0aHMuY29udHJvbEFkZHJlc3MsIFwic3RhdHVzXCIpO1xuICAgIHJldHVybiB7XG4gICAgICBpbnN0YWxsZWQsXG4gICAgICBydW5uaW5nOiByZXNwb25zZSAhPT0gdW5kZWZpbmVkLFxuICAgICAgLi4uKHJlc3BvbnNlXG4gICAgICAgID8ge1xuICAgICAgICAgICAgcGlkOiByZXNwb25zZS5waWQsXG4gICAgICAgICAgICAuLi4ocmVzcG9uc2Uuc2VydmljZVBpZCA/IHsgc2VydmljZVBpZDogcmVzcG9uc2Uuc2VydmljZVBpZCB9IDoge30pLFxuICAgICAgICAgIH1cbiAgICAgICAgOiB7fSksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgbGF1bmNoKGV4ZWN1dGFibGU6IHN0cmluZywgY29uZmlnRmlsZTogc3RyaW5nKTogU2VydmljZUxhdW5jaCB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGV4ZWN1dGFibGUsXG4gICAgICBhcmd1bWVudHM6IFtcIi0tc2VydmljZS1jb25maWdcIiwgY29uZmlnRmlsZV0sXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5zdGFsbERlZmluaXRpb24ob3duZXI6IFNlcnZpY2VQYWNrYWdlKTogUHJvbWlzZTxDbGlTZXJ2aWNlRGVmaW5pdGlvbj4ge1xuICAgIGNvbnN0IGRlZmluaXRpb24gPSBDbGlTZXJ2aWNlRGVmaW5pdGlvblNjaGVtYS5wYXJzZSh7XG4gICAgICAuLi50aGlzLmRlZmluaXRpb24sXG4gICAgICB2ZXJzaW9uOiB0aGlzLmRlZmluaXRpb24udmVyc2lvbiA/PyBvd25lci52ZXJzaW9uLFxuICAgIH0pO1xuICAgIGNvbnN0IGNvbW1hbmQgPSBkZWZpbml0aW9uLmNvbW1hbmRcbiAgICAgID8gYXdhaXQgdGhpcy5pbnN0YWxsQ29tbWFuZChkZWZpbml0aW9uLCBvd25lciwgZGVmaW5pdGlvbi5jb21tYW5kLCBcImNvbW1hbmRcIilcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IG1lbnU6IENsaVNlcnZpY2VNZW51SXRlbVtdID0gW107XG4gICAgZm9yIChjb25zdCBbaW5kZXgsIGl0ZW1dIG9mIChkZWZpbml0aW9uLm1lbnUgPz8gW10pLmVudHJpZXMoKSkge1xuICAgICAgbWVudS5wdXNoKFxuICAgICAgICBpdGVtLnR5cGUgPT09IFwiY29tbWFuZFwiXG4gICAgICAgICAgPyB7XG4gICAgICAgICAgICAgIC4uLml0ZW0sXG4gICAgICAgICAgICAgIGNvbW1hbmQ6IGF3YWl0IHRoaXMuaW5zdGFsbENvbW1hbmQoXG4gICAgICAgICAgICAgICAgZGVmaW5pdGlvbixcbiAgICAgICAgICAgICAgICBvd25lcixcbiAgICAgICAgICAgICAgICBpdGVtLmNvbW1hbmQsXG4gICAgICAgICAgICAgICAgYG1lbnUtJHtpbmRleCArIDF9YCxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICA6IGl0ZW0sXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gQ2xpU2VydmljZURlZmluaXRpb25TY2hlbWEucGFyc2Uoe1xuICAgICAgLi4uZGVmaW5pdGlvbixcbiAgICAgIC4uLihjb21tYW5kID8geyBjb21tYW5kIH0gOiB7fSksXG4gICAgICAuLi4obWVudS5sZW5ndGggPiAwID8geyBtZW51IH0gOiB7fSksXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGluc3RhbGxDb21tYW5kKFxuICAgIGRlZmluaXRpb246IENsaVNlcnZpY2VEZWZpbml0aW9uLFxuICAgIG93bmVyOiBTZXJ2aWNlUGFja2FnZSxcbiAgICBjb21tYW5kOiBDbGlTZXJ2aWNlQ29tbWFuZCxcbiAgICBwdXJwb3NlOiBzdHJpbmcsXG4gICk6IFByb21pc2U8Q2xpU2VydmljZUNvbW1hbmQ+IHtcbiAgICBpZiAoY29tbWFuZC5leGVjdXRhYmxlKSByZXR1cm4gY29tbWFuZDtcbiAgICBjb25zdCBlbnRyeXBvaW50ID0gY29tbWFuZC5lbnRyeXBvaW50ID8/IG93bmVyLmJpbihjb21tYW5kLmJpbk5hbWUpO1xuICAgIGNvbnN0IGluc3RhbGxlZCA9IGF3YWl0IHRoaXMuZW5zdXJlQ29tcGlsZWRCaW5hcnkoXG4gICAgICB0aGlzLmJpbmFyeU5hbWUoZGVmaW5pdGlvbiwgcHVycG9zZSksXG4gICAgICByZXNvbHZlKGVudHJ5cG9pbnQpLFxuICAgICAgb3duZXIuZGlyZWN0b3J5LFxuICAgICAgZXh0ZXJuYWxSdW50aW1lUGFja2FnZXMob3duZXIuZGVwZW5kZW5jaWVzKCkpLFxuICAgICk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGV4ZWN1dGFibGU6IGluc3RhbGxlZC5wYXRoLFxuICAgICAgLi4uKGNvbW1hbmQuYXJndW1lbnRzID8geyBhcmd1bWVudHM6IGNvbW1hbmQuYXJndW1lbnRzIH0gOiB7fSksXG4gICAgICAuLi4oY29tbWFuZC5lbnZpcm9ubWVudCA/IHsgZW52aXJvbm1lbnQ6IGNvbW1hbmQuZW52aXJvbm1lbnQgfSA6IHt9KSxcbiAgICAgIGN3ZDogY29tbWFuZC5jd2QgPz8gdGhpcy5nbG9iYWxIb21lRGlyZWN0b3J5LFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGVuc3VyZUNvbXBpbGVkQmluYXJ5KFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICBlbnRyeXBvaW50OiBzdHJpbmcsXG4gICAgd29ya2luZ0RpcmVjdG9yeTogc3RyaW5nLFxuICAgIGV4dGVybmFsOiByZWFkb25seSBzdHJpbmdbXSA9IFtdLFxuICApOiBQcm9taXNlPGJpbi5CaW5Db250ZXh0PiB7XG4gICAgcmV0dXJuIGJpbi5lbnN1cmUoXG4gICAgICBuYW1lLFxuICAgICAgYXN5bmMgKHsgdGVtcERpciB9KSA9PiB7XG4gICAgICAgIGNvbnN0IG91dHB1dCA9IGpvaW4odGVtcERpciwgZXhlY3V0YWJsZU5hbWUobmFtZSwgdGhpcy5ydW50aW1lLnBsYXRmb3JtKSk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tcGlsZXIoZW50cnlwb2ludCwgb3V0cHV0LCB3b3JraW5nRGlyZWN0b3J5LCBleHRlcm5hbCk7XG4gICAgICAgIHJldHVybiBwYXRoVG9GaWxlVVJMKG91dHB1dCkuaHJlZjtcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIGRlc3RpbmF0aW9uOiB0aGlzLmJpbmFyeUNvbnRleHQobmFtZSksXG4gICAgICAgIHNraXBWZXJzaW9uQ2hlY2s6IHRydWUsXG4gICAgICAgIGZvcmNlOiB0cnVlLFxuICAgICAgfSxcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVUcmF5QmluYXJ5KCk6IFByb21pc2U8YmluLkJpbkNvbnRleHQ+IHtcbiAgICBjb25zdCBuYW1lID0gdHJheUV4ZWN1dGFibGVOYW1lKHRoaXMucnVudGltZS5wbGF0Zm9ybSk7XG4gICAgY29uc3QgYmluRGlyID0gam9pbih0aGlzLmdsb2JhbEhvbWVEaXJlY3RvcnksIFwiYmluXCIsIFwidHJheWJpblwiKTtcbiAgICByZXR1cm4gYmluLmVuc3VyZShuYW1lLCBwYXRoVG9GaWxlVVJMKHRoaXMudHJheUV4ZWN1dGFibGUpLmhyZWYsIHtcbiAgICAgIGRlc3RpbmF0aW9uOiB7XG4gICAgICAgIHJvb3Q6IHRoaXMuZ2xvYmFsSG9tZURpcmVjdG9yeSxcbiAgICAgICAgYmluRGlyLFxuICAgICAgICBwYXRoOiBqb2luKGJpbkRpciwgbmFtZSksXG4gICAgICB9LFxuICAgICAgc2tpcFZlcnNpb25DaGVjazogdHJ1ZSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYmluYXJ5Q29udGV4dChuYW1lOiBzdHJpbmcpOiBiaW4uQmluQ29udGV4dCB7XG4gICAgY29uc3QgYmluRGlyID0gam9pbih0aGlzLmdsb2JhbEhvbWVEaXJlY3RvcnksIFwiYmluXCIpO1xuICAgIHJldHVybiB7XG4gICAgICByb290OiB0aGlzLmdsb2JhbEhvbWVEaXJlY3RvcnksXG4gICAgICBiaW5EaXIsXG4gICAgICBwYXRoOiBqb2luKGJpbkRpciwgZXhlY3V0YWJsZU5hbWUobmFtZSwgdGhpcy5ydW50aW1lLnBsYXRmb3JtKSksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgYmluYXJ5TmFtZShkZWZpbml0aW9uOiBDbGlTZXJ2aWNlRGVmaW5pdGlvbiwgcHVycG9zZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBuYW1lID0gc2FmZVRva2VuKGRlZmluaXRpb24ubmFtZS50b0xvd2VyQ2FzZSgpKS5yZXBsYWNlKC9bLl9dKy9nLCBcIi1cIik7XG4gICAgcmV0dXJuIHB1cnBvc2UgPT09IFwic2VydmljZVwiID8gbmFtZSA6IGAke25hbWV9LSR7cHVycG9zZX1gO1xuICB9XG59XG5cbmZ1bmN0aW9uIHJlc29sdmVIb3N0RW50cnlwb2ludCgpOiBzdHJpbmcge1xuICBjb25zdCBjdXJyZW50ID0gZmlsZVVSTFRvUGF0aChpbXBvcnQubWV0YS51cmwpO1xuICBjb25zdCBzb3VyY2UgPVxuICAgIGV4dG5hbWUoY3VycmVudCkgPT09IFwiLnRzXCJcbiAgICAgID8gam9pbihkaXJuYW1lKGN1cnJlbnQpLCBcIl9ob3N0LnRzXCIpXG4gICAgICA6IHJlc29sdmUoZGlybmFtZShjdXJyZW50KSwgXCIuLlwiLCBcIi4uXCIsIFwic3JjXCIsIFwiX2hvc3QudHNcIik7XG4gIGlmIChleGlzdHNTeW5jKHNvdXJjZSkpIHJldHVybiBzb3VyY2U7XG4gIHRocm93IG5ldyBFcnJvcihgY291bGQgbm90IHJlc29sdmUgY2xpLXNlcnZpY2UgaG9zdCBzb3VyY2U6ICR7c291cmNlfWApO1xufVxuXG5mdW5jdGlvbiByZXNvbHZlQnVuRXhlY3V0YWJsZSgpOiBzdHJpbmcge1xuICByZXR1cm4gY3JlYXRlUmVxdWlyZShpbXBvcnQubWV0YS51cmwpLnJlc29sdmUoXCJidW4vYmluL2J1bi5leGVcIik7XG59XG5cbmZ1bmN0aW9uIHRyYXlFeGVjdXRhYmxlTmFtZShwbGF0Zm9ybTogTm9kZUpTLlBsYXRmb3JtKTogc3RyaW5nIHtcbiAgaWYgKHBsYXRmb3JtID09PSBcImRhcndpblwiKSByZXR1cm4gXCJ0cmF5X2Rhcndpbl9yZWxlYXNlXCI7XG4gIGlmIChwbGF0Zm9ybSA9PT0gXCJsaW51eFwiKSByZXR1cm4gXCJ0cmF5X2xpbnV4X3JlbGVhc2VcIjtcbiAgaWYgKHBsYXRmb3JtID09PSBcIndpbjMyXCIpIHJldHVybiBcInRyYXlfd2luZG93c19yZWxlYXNlLmV4ZVwiO1xuICB0aHJvdyBuZXcgRXJyb3IoYHN5c3RyYXkyIGRvZXMgbm90IHN1cHBvcnQgJHtwbGF0Zm9ybX1gKTtcbn1cblxuZnVuY3Rpb24gcmVzb2x2ZVRyYXlFeGVjdXRhYmxlKHBsYXRmb3JtOiBOb2RlSlMuUGxhdGZvcm0pOiBzdHJpbmcge1xuICBjb25zdCBtYW5pZmVzdCA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKS5yZXNvbHZlKFwic3lzdHJheTIvcGFja2FnZS5qc29uXCIpO1xuICByZXR1cm4gam9pbihkaXJuYW1lKG1hbmlmZXN0KSwgXCJ0cmF5YmluXCIsIHRyYXlFeGVjdXRhYmxlTmFtZShwbGF0Zm9ybSkpO1xufVxuXG5mdW5jdGlvbiBleGVjdXRhYmxlTmFtZShuYW1lOiBzdHJpbmcsIHBsYXRmb3JtOiBOb2RlSlMuUGxhdGZvcm0pOiBzdHJpbmcge1xuICByZXR1cm4gcGxhdGZvcm0gPT09IFwid2luMzJcIiA/IGAke25hbWV9LmV4ZWAgOiBuYW1lO1xufVxuXG5mdW5jdGlvbiBzYWZlVG9rZW4odmFsdWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHRva2VuID0gdmFsdWUucmVwbGFjZSgvW15hLXowLTkuXy1dKy9naSwgXCItXCIpLnJlcGxhY2UoL14tK3wtKyQvZywgXCJcIik7XG4gIGlmICghdG9rZW4pIHRocm93IG5ldyBUeXBlRXJyb3IoYGludmFsaWQgYmluYXJ5IHZlcnNpb246ICR7dmFsdWV9YCk7XG4gIHJldHVybiB0b2tlbjtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY29tcGlsZVdpdGhCdW4oXG4gIGJ1bkV4ZWN1dGFibGU6IHN0cmluZyxcbiAgZW50cnlwb2ludDogc3RyaW5nLFxuICBvdXRwdXQ6IHN0cmluZyxcbiAgd29ya2luZ0RpcmVjdG9yeTogc3RyaW5nLFxuICBleHRlcm5hbDogcmVhZG9ubHkgc3RyaW5nW10gPSBbXSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBiZWZvcmUgPSBhd2FpdCBidW5CdWlsZEFydGlmYWN0cyh3b3JraW5nRGlyZWN0b3J5KTtcbiAgdHJ5IHtcbiAgICBhd2FpdCBleGVjLnNwYXduKFxuICAgICAgYnVuRXhlY3V0YWJsZSxcbiAgICAgIFtcbiAgICAgICAgXCJidWlsZFwiLFxuICAgICAgICBlbnRyeXBvaW50LFxuICAgICAgICBcIi0tY29tcGlsZVwiLFxuICAgICAgICBcIi0tY29tcGlsZS1hdXRvbG9hZC1wYWNrYWdlLWpzb25cIixcbiAgICAgICAgXCItLW91dGZpbGVcIixcbiAgICAgICAgb3V0cHV0LFxuICAgICAgICAuLi5leHRlcm5hbC5mbGF0TWFwKChkZXBlbmRlbmN5KSA9PiBbXCItLWV4dGVybmFsXCIsIGRlcGVuZGVuY3ldKSxcbiAgICAgIF0sXG4gICAgICB7XG4gICAgICAgIGNoZWNrOiB0cnVlLFxuICAgICAgICBjd2Q6IHdvcmtpbmdEaXJlY3RvcnksXG4gICAgICAgIGVudjoge1xuICAgICAgICAgIC4uLnByb2Nlc3MuZW52LFxuICAgICAgICAgIEJVTl9UTVBESVI6IGRpcm5hbWUob3V0cHV0KSxcbiAgICAgICAgICBUTVBESVI6IGRpcm5hbWUob3V0cHV0KSxcbiAgICAgICAgfSxcbiAgICAgICAgc3RkaW46IFwiaWdub3JlXCIsXG4gICAgICAgIHN0ZG91dDogXCJjYXB0dXJlXCIsXG4gICAgICAgIHN0ZGVycjogXCJjYXB0dXJlXCIsXG4gICAgICB9LFxuICAgICk7XG4gIH0gZmluYWxseSB7XG4gICAgZm9yIChjb25zdCBhcnRpZmFjdCBvZiBhd2FpdCBidW5CdWlsZEFydGlmYWN0cyh3b3JraW5nRGlyZWN0b3J5KSkge1xuICAgICAgaWYgKCFiZWZvcmUuaGFzKGFydGlmYWN0KSkge1xuICAgICAgICBhd2FpdCBybShqb2luKHdvcmtpbmdEaXJlY3RvcnksIGFydGlmYWN0KSwge1xuICAgICAgICAgIHJlY3Vyc2l2ZTogdHJ1ZSxcbiAgICAgICAgICBmb3JjZTogdHJ1ZSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGJ1bkJ1aWxkQXJ0aWZhY3RzKGRpcmVjdG9yeTogc3RyaW5nKTogUHJvbWlzZTxTZXQ8c3RyaW5nPj4ge1xuICByZXR1cm4gbmV3IFNldChcbiAgICAoYXdhaXQgcmVhZGRpcihkaXJlY3RvcnkpKVxuICAgICAgLmZpbHRlcigobmFtZSkgPT4gbmFtZS5zdGFydHNXaXRoKFwiLlwiKSAmJiBuYW1lLmVuZHNXaXRoKFwiLmJ1bi1idWlsZFwiKSlcbiAgICAgIC5zb3J0KCksXG4gICk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JTdGF0ZShcbiAgcmVhZDogKCkgPT4gUHJvbWlzZTxib29sZWFuPixcbiAgZXhwZWN0ZWQ6IGJvb2xlYW4sXG4gIHRpbWVvdXRNaWxsaXNlY29uZHM6IG51bWJlcixcbiAgbWVzc2FnZTogc3RyaW5nLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHRpbWVvdXRNaWxsaXNlY29uZHM7XG4gIHdoaWxlIChEYXRlLm5vdygpIDwgZGVhZGxpbmUpIHtcbiAgICBpZiAoKGF3YWl0IHJlYWQoKSkgPT09IGV4cGVjdGVkKSByZXR1cm47XG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgUE9MTF9JTlRFUlZBTF9NSUxMSVNFQ09ORFMpKTtcbiAgfVxuICB0aHJvdyBuZXcgRXJyb3IobWVzc2FnZSk7XG59XG4iXX0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/_config.ts","../src/_control.ts","../src/_host.ts","../src/_package.ts","../src/_paths.ts","../src/_runtime.ts","../src/_startup.ts","../src/cli.ts","../src/definition.ts","../src/service.ts","../index.ts"],"version":"5.9.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dbx-tools/cli-service",
|
|
3
|
+
"description": "Cross-platform service lifecycle and system tray menus for CLIs",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/reggie-db/dbx-tools.git",
|
|
7
|
+
"directory": "packages/js/cli/service"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "projen build",
|
|
11
|
+
"compile": "projen compile",
|
|
12
|
+
"default": "projen default",
|
|
13
|
+
"package": "projen package",
|
|
14
|
+
"post-compile": "projen post-compile",
|
|
15
|
+
"pre-compile": "projen pre-compile",
|
|
16
|
+
"prepack": "projen prepack",
|
|
17
|
+
"test": "projen test",
|
|
18
|
+
"watch": "projen watch",
|
|
19
|
+
"projen": "projen"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^24.6.0",
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@dbx-tools/core": "^0.9.36",
|
|
27
|
+
"bun": "1.3.14",
|
|
28
|
+
"commander": "^15.0.0",
|
|
29
|
+
"systray2": "^2.1.4",
|
|
30
|
+
"zod": "4.3.6"
|
|
31
|
+
},
|
|
32
|
+
"main": "./lib/index.js",
|
|
33
|
+
"license": "Apache-2.0",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"main": "./lib/index.js",
|
|
37
|
+
"types": "./lib/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./lib/index.d.ts",
|
|
41
|
+
"default": "./lib/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./cli": {
|
|
44
|
+
"types": "./lib/src/cli.d.ts",
|
|
45
|
+
"default": "./lib/src/cli.js"
|
|
46
|
+
},
|
|
47
|
+
"./definition": {
|
|
48
|
+
"types": "./lib/src/definition.d.ts",
|
|
49
|
+
"default": "./lib/src/definition.js"
|
|
50
|
+
},
|
|
51
|
+
"./service": {
|
|
52
|
+
"types": "./lib/src/service.d.ts",
|
|
53
|
+
"default": "./lib/src/service.js"
|
|
54
|
+
},
|
|
55
|
+
"./package.json": "./package.json"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"version": "0.9.36",
|
|
59
|
+
"types": "./lib/index.d.ts",
|
|
60
|
+
"dbxToolsConfig": {
|
|
61
|
+
"tags": [
|
|
62
|
+
"cli"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"type": "module",
|
|
66
|
+
"exports": {
|
|
67
|
+
".": {
|
|
68
|
+
"types": "./lib/index.d.ts",
|
|
69
|
+
"default": "./lib/index.js"
|
|
70
|
+
},
|
|
71
|
+
"./cli": {
|
|
72
|
+
"types": "./lib/src/cli.d.ts",
|
|
73
|
+
"default": "./lib/src/cli.js"
|
|
74
|
+
},
|
|
75
|
+
"./definition": {
|
|
76
|
+
"types": "./lib/src/definition.d.ts",
|
|
77
|
+
"default": "./lib/src/definition.js"
|
|
78
|
+
},
|
|
79
|
+
"./service": {
|
|
80
|
+
"types": "./lib/src/service.d.ts",
|
|
81
|
+
"default": "./lib/src/service.js"
|
|
82
|
+
},
|
|
83
|
+
"./package.json": "./package.json"
|
|
84
|
+
},
|
|
85
|
+
"files": [
|
|
86
|
+
"index.ts",
|
|
87
|
+
"src",
|
|
88
|
+
"bin",
|
|
89
|
+
"lib"
|
|
90
|
+
],
|
|
91
|
+
"//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"bunx projen\"."
|
|
92
|
+
}
|
package/src/_config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { CliServiceDefinitionSchema, type CliServiceDefinition } from "./definition.ts";
|
|
5
|
+
|
|
6
|
+
export async function readServiceDefinition(path: string): Promise<CliServiceDefinition> {
|
|
7
|
+
return CliServiceDefinitionSchema.parse(JSON.parse(await readFile(path, "utf8")));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function writeServiceDefinition(
|
|
11
|
+
path: string,
|
|
12
|
+
definition: CliServiceDefinition,
|
|
13
|
+
): Promise<void> {
|
|
14
|
+
const parsed = CliServiceDefinitionSchema.parse(definition);
|
|
15
|
+
await mkdir(dirname(path), { recursive: true });
|
|
16
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
17
|
+
await writeFile(temporary, `${JSON.stringify(parsed, null, 2)}\n`, {
|
|
18
|
+
encoding: "utf8",
|
|
19
|
+
mode: 0o600,
|
|
20
|
+
});
|
|
21
|
+
await rename(temporary, path);
|
|
22
|
+
}
|
package/src/_control.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { connect, createServer, type Server, type Socket } from "node:net";
|
|
3
|
+
|
|
4
|
+
export type ServiceControlCommand = "status" | "stop";
|
|
5
|
+
|
|
6
|
+
export interface ServiceControlResponse {
|
|
7
|
+
readonly running: true;
|
|
8
|
+
readonly pid: number;
|
|
9
|
+
readonly servicePid?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function requestServiceControl(
|
|
13
|
+
address: string,
|
|
14
|
+
command: ServiceControlCommand,
|
|
15
|
+
timeoutMilliseconds = 500,
|
|
16
|
+
): Promise<ServiceControlResponse | undefined> {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const socket = connect(address);
|
|
19
|
+
let response = "";
|
|
20
|
+
const timeout = setTimeout(() => {
|
|
21
|
+
socket.destroy();
|
|
22
|
+
resolve(undefined);
|
|
23
|
+
}, timeoutMilliseconds);
|
|
24
|
+
socket.setEncoding("utf8");
|
|
25
|
+
socket.once("connect", () => {
|
|
26
|
+
socket.write(`${JSON.stringify({ command })}\n`);
|
|
27
|
+
});
|
|
28
|
+
socket.on("data", (chunk: string) => {
|
|
29
|
+
response += chunk;
|
|
30
|
+
});
|
|
31
|
+
socket.once("end", () => {
|
|
32
|
+
clearTimeout(timeout);
|
|
33
|
+
if (!response.trim()) {
|
|
34
|
+
resolve(undefined);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const value = JSON.parse(response) as Partial<ServiceControlResponse>;
|
|
39
|
+
if (value.running === true && typeof value.pid === "number") {
|
|
40
|
+
resolve({
|
|
41
|
+
running: true,
|
|
42
|
+
pid: value.pid,
|
|
43
|
+
...(typeof value.servicePid === "number" ? { servicePid: value.servicePid } : {}),
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
resolve(undefined);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reject(error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
socket.once("error", (error: NodeJS.ErrnoException) => {
|
|
53
|
+
clearTimeout(timeout);
|
|
54
|
+
if (["ECONNREFUSED", "ENOENT", "ENOTSOCK", "EPIPE"].includes(error.code ?? "")) {
|
|
55
|
+
resolve(undefined);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
reject(error);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function listenForServiceControl(
|
|
64
|
+
address: string,
|
|
65
|
+
status: () => ServiceControlResponse,
|
|
66
|
+
stop: () => void,
|
|
67
|
+
): Promise<Server> {
|
|
68
|
+
const server = createServer((socket) => handleConnection(socket, status, stop));
|
|
69
|
+
try {
|
|
70
|
+
await listen(server, address);
|
|
71
|
+
return server;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (isWindowsPipe(address) || (error as NodeJS.ErrnoException).code !== "EADDRINUSE") {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
if ((await requestServiceControl(address, "status")) !== undefined) {
|
|
77
|
+
throw new Error(`service control address is already active: ${address}`);
|
|
78
|
+
}
|
|
79
|
+
await rm(address, { force: true });
|
|
80
|
+
await listen(server, address);
|
|
81
|
+
return server;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function closeServiceControl(server: Server, address: string): Promise<void> {
|
|
86
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
87
|
+
if (!isWindowsPipe(address)) await rm(address, { force: true });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function handleConnection(
|
|
91
|
+
socket: Socket,
|
|
92
|
+
status: () => ServiceControlResponse,
|
|
93
|
+
stop: () => void,
|
|
94
|
+
): void {
|
|
95
|
+
let request = "";
|
|
96
|
+
socket.setEncoding("utf8");
|
|
97
|
+
socket.on("data", (chunk: string) => {
|
|
98
|
+
request += chunk;
|
|
99
|
+
if (!request.includes("\n")) return;
|
|
100
|
+
socket.removeAllListeners("data");
|
|
101
|
+
let command: unknown;
|
|
102
|
+
try {
|
|
103
|
+
command = (JSON.parse(request) as { command?: unknown }).command;
|
|
104
|
+
} catch {
|
|
105
|
+
socket.end();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (command === "stop") {
|
|
109
|
+
socket.once("finish", () => setImmediate(stop));
|
|
110
|
+
}
|
|
111
|
+
socket.end(`${JSON.stringify(status())}\n`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function listen(server: Server, address: string): Promise<void> {
|
|
116
|
+
await new Promise<void>((resolve, reject) => {
|
|
117
|
+
server.once("error", reject);
|
|
118
|
+
server.listen(address, () => {
|
|
119
|
+
server.off("error", reject);
|
|
120
|
+
resolve();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isWindowsPipe(address: string): boolean {
|
|
126
|
+
return address.startsWith("\\\\.\\pipe\\");
|
|
127
|
+
}
|