flightdeck 0.2.96 → 0.3.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/dist/{flightdeck.lib-DMlmcPLR.js → flightdeck.lib-DneT-dY9.js} +54 -2
- package/dist/flightdeck.lib-DneT-dY9.js.map +1 -0
- package/dist/flightdeck.x.js +26 -7
- package/dist/flightdeck.x.js.map +1 -1
- package/dist/klaxon.x.js.map +1 -1
- package/dist/lib.d.ts +18 -15
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -1
- package/package.json +5 -5
- package/src/flightdeck.lib.ts +93 -17
- package/src/flightdeck.x.ts +29 -6
- package/src/klaxon.x.ts +1 -1
- package/src/lib.ts +2 -2
- package/dist/flightdeck.lib-DMlmcPLR.js.map +0 -1
|
@@ -28,7 +28,52 @@ const FLIGHTDECK_UPDATE_PHASES = [`notified`, `confirmed`];
|
|
|
28
28
|
function isVersionNumber(version) {
|
|
29
29
|
return /^\d+\.\d+\.\d+$/.test(version) || !Number.isNaN(Number.parseFloat(version));
|
|
30
30
|
}
|
|
31
|
+
async function waitForPidExit(pid, timeoutMs = 5e3, intervalMs = 100) {
|
|
32
|
+
return new Promise((pass, fail) => {
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
const check = () => {
|
|
35
|
+
process.stdout.write(`.`);
|
|
36
|
+
try {
|
|
37
|
+
process.kill(pid, 0);
|
|
38
|
+
if (Date.now() - start > timeoutMs) {
|
|
39
|
+
fail(/* @__PURE__ */ new Error(`Timeout waiting for PID ${pid}`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setTimeout(check, intervalMs);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (err instanceof Error && `code` in err) {
|
|
45
|
+
if (err.code === `ESRCH`) {
|
|
46
|
+
pass();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (err.code === `EPERM`) {
|
|
50
|
+
if (Date.now() - start > timeoutMs) {
|
|
51
|
+
fail(/* @__PURE__ */ new Error(`Timeout waiting for PID ${pid} (EPERM; still alive)`));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
return setTimeout(check, intervalMs);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
fail(err);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
check();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
31
64
|
var FlightDeck = class {
|
|
65
|
+
static async kill(flightdeckRootDir, packageName) {
|
|
66
|
+
console.info(`Killing FlightDeck instance of "${packageName}"`);
|
|
67
|
+
const currentPid = new FilesystemStorage({ path: resolve(flightdeckRootDir, `storage`, packageName) }).getItem(`currentPid`);
|
|
68
|
+
if (currentPid === null) throw new Error(`No pid for "${packageName}" found in storage`);
|
|
69
|
+
const pid = Number(currentPid);
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
process.kill(pid, `SIGTERM`);
|
|
72
|
+
await waitForPidExit(pid, 5e3, 5);
|
|
73
|
+
const elapsed = Date.now() - now;
|
|
74
|
+
process.stdout.write(`🌜 "${packageName}" (running as process ${pid}) exited in ${elapsed}ms\n`);
|
|
75
|
+
return pid;
|
|
76
|
+
}
|
|
32
77
|
options;
|
|
33
78
|
safety = 0;
|
|
34
79
|
storage;
|
|
@@ -64,6 +109,7 @@ var FlightDeck = class {
|
|
|
64
109
|
this.live.use(Promise.all(this.servicesLive));
|
|
65
110
|
this.dead.use(Promise.all(this.servicesDead));
|
|
66
111
|
this.storage = new FilesystemStorage({ path: resolve(flightdeckRootDir, `storage`, options.packageName) });
|
|
112
|
+
this.storage.setItem(`currentPid`, `${process.pid}`);
|
|
67
113
|
if (FLIGHTDECK_SECRET === void 0) this.logger.warn(`No FLIGHTDECK_SECRET environment variable found. FlightDeck will not run an update server.`);
|
|
68
114
|
else createServer((req, res) => {
|
|
69
115
|
let data = [];
|
|
@@ -117,6 +163,12 @@ var FlightDeck = class {
|
|
|
117
163
|
}).catch((thrown) => {
|
|
118
164
|
if (thrown instanceof Error) this.logger.error(`Failed to start all services:`, thrown.message);
|
|
119
165
|
});
|
|
166
|
+
process.on(`SIGTERM`, async () => {
|
|
167
|
+
console.info(`Killed by SIGTERM`);
|
|
168
|
+
await this.stopAllServices();
|
|
169
|
+
this.storage.removeItem(`currentPid`);
|
|
170
|
+
process.exit(0);
|
|
171
|
+
});
|
|
120
172
|
}
|
|
121
173
|
async seekUpdate(version) {
|
|
122
174
|
this.logger.info(`Checking for updates...`);
|
|
@@ -212,7 +264,7 @@ var FlightDeck = class {
|
|
|
212
264
|
this.logger.info(`Auto-respawn saw "${serviceName}" exit with code ${exitCode}`);
|
|
213
265
|
this.services[serviceName] = null;
|
|
214
266
|
if (!this.autoRespawnDeadServices) {
|
|
215
|
-
this.logger.info(
|
|
267
|
+
this.logger.info(`😴 Auto-respawn is off; "${serviceName}" rests.`);
|
|
216
268
|
return;
|
|
217
269
|
}
|
|
218
270
|
const updatePhase = this.storage.getItem(`updatePhase`);
|
|
@@ -390,4 +442,4 @@ var FlightDeckLogger = class {
|
|
|
390
442
|
|
|
391
443
|
//#endregion
|
|
392
444
|
export { FLIGHTDECK_UPDATE_PHASES as a, FlightDeckLogger as c, FLIGHTDECK_SETUP_PHASES as i, isVersionNumber as l, FLIGHTDECK_INFO as n, FLIGHTDECK_WARN as o, FLIGHTDECK_LNAV_FORMAT as r, FlightDeck as s, FLIGHTDECK_ERROR as t };
|
|
393
|
-
//# sourceMappingURL=flightdeck.lib-
|
|
445
|
+
//# sourceMappingURL=flightdeck.lib-DneT-dY9.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flightdeck.lib-DneT-dY9.js","names":["data: Uint8Array[]","log: FlightDeckLog"],"sources":["../src/flightdeck.env.ts","../src/flightdeck.lib.ts"],"sourcesContent":["import { createEnv } from \"@t3-oss/env-core\"\nimport { type } from \"arktype\"\n\nexport const env = createEnv({\n\tserver: { FLIGHTDECK_SECRET: type(`string`, `|`, `undefined`) },\n\tclientPrefix: `NEVER`,\n\tclient: {},\n\truntimeEnv: import.meta.env as Record<string, string>,\n\temptyStringAsUndefined: true,\n})\n","/* eslint-disable @typescript-eslint/only-throw-error */\nimport type { ChildProcessWithoutNullStreams } from \"node:child_process\"\nimport { execSync, spawn } from \"node:child_process\"\nimport { createServer } from \"node:http\"\nimport { homedir } from \"node:os\"\nimport { resolve } from \"node:path\"\nimport { inspect } from \"node:util\"\n\nimport { Future } from \"atom.io/internal\"\nimport { discoverType } from \"atom.io/introspection\"\nimport { fromEntries, toEntries } from \"atom.io/json\"\nimport { ChildSocket } from \"atom.io/realtime-server\"\nimport { CronJob } from \"cron\"\nimport { FilesystemStorage } from \"safedeposit\"\n\nimport type { LnavFormat } from \"../gen/lnav-format-schema.gen.ts\"\nimport { env } from \"./flightdeck.env.ts\"\n\nexport const FLIGHTDECK_SETUP_PHASES = [`downloaded`, `installed`] as const\n\nexport type FlightDeckSetupPhase = (typeof FLIGHTDECK_SETUP_PHASES)[number]\n\nexport const FLIGHTDECK_UPDATE_PHASES = [`notified`, `confirmed`] as const\n\nexport type FlightDeckUpdatePhase = (typeof FLIGHTDECK_UPDATE_PHASES)[number]\n\nexport function isVersionNumber(version: string): boolean {\n\treturn (\n\t\t/^\\d+\\.\\d+\\.\\d+$/.test(version) || !Number.isNaN(Number.parseFloat(version))\n\t)\n}\n\nexport type FlightDeckSaveData = {\n\tcurrentPid: `${number}`\n\tsetupPhase: FlightDeckSetupPhase\n\tupdatePhase: FlightDeckUpdatePhase\n\tupdateAwaitedVersion: string\n}\n\nexport type FlightDeckOptions<S extends string = string> = {\n\treadonly packageName: string\n\treadonly services: { [service in S]: { run: string; waitFor: boolean } }\n\treadonly scripts: {\n\t\treadonly download: string\n\t\treadonly install: string\n\t\treadonly checkAvailability?: string\n\t}\n\treadonly port?: number | undefined\n\treadonly flightdeckRootDir?: string | undefined\n\treadonly jsonLogging?: boolean | undefined\n}\n\nasync function waitForPidExit(pid: number, timeoutMs = 5000, intervalMs = 100) {\n\treturn new Promise<void>((pass, fail) => {\n\t\tconst start = Date.now()\n\n\t\tconst check = () => {\n\t\t\tprocess.stdout.write(`.`)\n\t\t\ttry {\n\t\t\t\t// check existence\n\t\t\t\tprocess.kill(pid, 0)\n\t\t\t\t// process still alive\n\t\t\t\tif (Date.now() - start > timeoutMs) {\n\t\t\t\t\tfail(new Error(`Timeout waiting for PID ${pid}`))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tsetTimeout(check, intervalMs)\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && `code` in err) {\n\t\t\t\t\tif (err.code === `ESRCH`) {\n\t\t\t\t\t\tpass()\n\t\t\t\t\t\treturn // exited\n\t\t\t\t\t}\n\t\t\t\t\tif (err.code === `EPERM`) {\n\t\t\t\t\t\t// process is alive but protected\n\t\t\t\t\t\tif (Date.now() - start > timeoutMs) {\n\t\t\t\t\t\t\tfail(\n\t\t\t\t\t\t\t\tnew Error(`Timeout waiting for PID ${pid} (EPERM; still alive)`),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn setTimeout(check, intervalMs)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfail(err)\n\t\t\t\treturn // unexpected error\n\t\t\t}\n\t\t}\n\n\t\tcheck()\n\t})\n}\n\nexport class FlightDeck<S extends string = string> {\n\tpublic static async kill(\n\t\tflightdeckRootDir: string,\n\t\tpackageName: string,\n\t): Promise<number> {\n\t\tconsole.info(`Killing FlightDeck instance of \"${packageName}\"`)\n\t\tconst storage = new FilesystemStorage<FlightDeckSaveData>({\n\t\t\tpath: resolve(flightdeckRootDir, `storage`, packageName),\n\t\t})\n\t\tconst currentPid = storage.getItem(`currentPid`)\n\t\tif (currentPid === null) {\n\t\t\tthrow new Error(`No pid for \"${packageName}\" found in storage`)\n\t\t}\n\t\tconst pid = Number(currentPid)\n\t\tconst now = Date.now()\n\t\tprocess.kill(pid, `SIGTERM`)\n\t\tawait waitForPidExit(pid, 5000, 5)\n\t\tconst elapsed = Date.now() - now\n\t\tprocess.stdout.write(\n\t\t\t`🌜 \"${packageName}\" (running as process ${pid}) exited in ${elapsed}ms\\n`,\n\t\t)\n\t\treturn pid\n\t}\n\n\tpublic readonly options: FlightDeckOptions<S>\n\tprotected safety = 0\n\n\tprotected storage: FilesystemStorage<FlightDeckSaveData>\n\tprotected services: {\n\t\t[service in S]: ChildSocket<\n\t\t\t{ timeToStop: []; updatesReady: [] },\n\t\t\t{ readyToUpdate: []; alive: [] },\n\t\t\tChildProcessWithoutNullStreams\n\t\t> | null\n\t}\n\tprotected serviceIdx: { readonly [service in S]: number }\n\tpublic defaultServicesReadyToUpdate: { readonly [service in S]: boolean }\n\tpublic servicesReadyToUpdate: { [service in S]: boolean }\n\tpublic autoRespawnDeadServices: boolean\n\n\tprotected logger: Pick<Console, `error` | `info` | `warn`>\n\tprotected serviceLoggers: {\n\t\treadonly [service in S]: FlightDeckLogger\n\t}\n\n\tprotected updateAvailabilityChecker: CronJob | null = null\n\n\tpublic servicesLive: Future<void>[]\n\tpublic servicesDead: Future<void>[]\n\tpublic live = new Future(() => {})\n\tpublic dead = new Future(() => {})\n\n\tprotected restartTimes: number[] = []\n\n\tpublic constructor(options: FlightDeckOptions<S>) {\n\t\tthis.options = options\n\t\tconst { FLIGHTDECK_SECRET } = env\n\t\tconst { flightdeckRootDir = resolve(homedir(), `.flightdeck`) } = options\n\t\tconst port = options.port ?? 8080\n\t\tconst origin = `http://localhost:${port}`\n\n\t\tconst servicesEntries = toEntries(options.services)\n\t\tthis.services = fromEntries(\n\t\t\tservicesEntries.map(([serviceName]) => [serviceName, null]),\n\t\t)\n\t\tthis.serviceIdx = fromEntries(\n\t\t\tservicesEntries.map(([serviceName], idx) => [serviceName, idx]),\n\t\t)\n\t\tthis.defaultServicesReadyToUpdate = fromEntries(\n\t\t\tservicesEntries.map(([serviceName, { waitFor }]) => [\n\t\t\t\tserviceName,\n\t\t\t\t!waitFor,\n\t\t\t]),\n\t\t)\n\t\tthis.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate }\n\t\tthis.autoRespawnDeadServices = true\n\n\t\tthis.logger = new FlightDeckLogger(\n\t\t\tthis.options.packageName,\n\t\t\tprocess.pid,\n\t\t\tundefined,\n\t\t\t{ jsonLogging: this.options.jsonLogging ?? false },\n\t\t)\n\t\tthis.serviceLoggers = fromEntries(\n\t\t\tservicesEntries.map(([serviceName]) => [\n\t\t\t\tserviceName,\n\t\t\t\tnew FlightDeckLogger(\n\t\t\t\t\tthis.options.packageName,\n\t\t\t\t\tprocess.pid,\n\t\t\t\t\tserviceName,\n\t\t\t\t\t{ jsonLogging: this.options.jsonLogging ?? false },\n\t\t\t\t),\n\t\t\t]),\n\t\t)\n\n\t\tthis.servicesLive = servicesEntries.map(() => new Future(() => {}))\n\t\tthis.servicesDead = servicesEntries.map(() => new Future(() => {}))\n\t\tthis.live.use(Promise.all(this.servicesLive))\n\t\tthis.dead.use(Promise.all(this.servicesDead))\n\n\t\tthis.storage = new FilesystemStorage({\n\t\t\tpath: resolve(flightdeckRootDir, `storage`, options.packageName),\n\t\t})\n\n\t\tthis.storage.setItem(`currentPid`, `${process.pid}`)\n\n\t\tif (FLIGHTDECK_SECRET === undefined) {\n\t\t\tthis.logger.warn(\n\t\t\t\t`No FLIGHTDECK_SECRET environment variable found. FlightDeck will not run an update server.`,\n\t\t\t)\n\t\t} else {\n\t\t\tcreateServer((req, res) => {\n\t\t\t\tlet data: Uint8Array[] = []\n\t\t\t\treq\n\t\t\t\t\t.on(`data`, (chunk) => {\n\t\t\t\t\t\tdata.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk))\n\t\t\t\t\t})\n\t\t\t\t\t.on(`end`, async () => {\n\t\t\t\t\t\tconst authHeader = req.headers.authorization\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (typeof req.url === `undefined`) throw 400\n\t\t\t\t\t\t\tconst expectedAuthHeader = `Bearer ${FLIGHTDECK_SECRET}`\n\t\t\t\t\t\t\tif (authHeader !== `Bearer ${FLIGHTDECK_SECRET}`) {\n\t\t\t\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t\t\t\t`Unauthorized: needed \\`${expectedAuthHeader}\\`, got \\`${authHeader}\\``,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tthrow 401\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst url = new URL(req.url, origin)\n\t\t\t\t\t\t\tthis.logger.info(req.method, url.pathname)\n\n\t\t\t\t\t\t\tconst versionForeignInput = Buffer.concat(data).toString()\n\t\t\t\t\t\t\tif (!isVersionNumber(versionForeignInput)) {\n\t\t\t\t\t\t\t\tthrow 400\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tres.writeHead(200)\n\t\t\t\t\t\t\tres.end()\n\n\t\t\t\t\t\t\tthis.storage.setItem(`updatePhase`, `notified`)\n\t\t\t\t\t\t\tthis.storage.setItem(`updateAwaitedVersion`, versionForeignInput)\n\t\t\t\t\t\t\tconst { checkAvailability } = options.scripts\n\t\t\t\t\t\t\tif (checkAvailability) {\n\t\t\t\t\t\t\t\tawait this.updateAvailabilityChecker?.stop()\n\t\t\t\t\t\t\t\tawait this.seekUpdate(versionForeignInput)\n\t\t\t\t\t\t\t\tconst updatePhase = this.storage.getItem(`updatePhase`)\n\t\t\t\t\t\t\t\tthis.logger.info(`> storage(\"updatePhase\") >`, updatePhase)\n\t\t\t\t\t\t\t\tif (updatePhase === `notified`) {\n\t\t\t\t\t\t\t\t\tthis.updateAvailabilityChecker = new CronJob(\n\t\t\t\t\t\t\t\t\t\t`*/30 * * * * *`,\n\t\t\t\t\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\t\t\t\t\tawait this.seekUpdate(versionForeignInput)\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tthis.updateAvailabilityChecker.start()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.downloadPackage()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (thrown) {\n\t\t\t\t\t\t\tthis.logger.error(thrown, req.url)\n\t\t\t\t\t\t\tif (typeof thrown === `number`) {\n\t\t\t\t\t\t\t\tres.writeHead(thrown)\n\t\t\t\t\t\t\t\tres.end()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tdata = []\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t}).listen(port, () => {\n\t\t\t\tthis.logger.info(`Server started on port ${port}`)\n\t\t\t})\n\t\t}\n\n\t\tthis.startAllServices()\n\t\t\t.then(() => {\n\t\t\t\tthis.logger.info(`All services started.`)\n\t\t\t})\n\t\t\t.catch((thrown) => {\n\t\t\t\tif (thrown instanceof Error) {\n\t\t\t\t\tthis.logger.error(`Failed to start all services:`, thrown.message)\n\t\t\t\t}\n\t\t\t})\n\n\t\tprocess.on(`SIGTERM`, async () => {\n\t\t\tconsole.info(`Killed by SIGTERM`)\n\t\t\tawait this.stopAllServices()\n\t\t\tthis.storage.removeItem(`currentPid`)\n\t\t\tprocess.exit(0)\n\t\t})\n\t}\n\n\tprotected async seekUpdate(version: string): Promise<void> {\n\t\tthis.logger.info(`Checking for updates...`)\n\t\tconst { checkAvailability } = this.options.scripts\n\t\tif (!checkAvailability) {\n\t\t\tthis.logger.info(`No checkAvailability script found.`)\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tconst out = execSync(`${checkAvailability} ${version}`)\n\t\t\tthis.logger.info(`Check stdout:`, out.toString())\n\t\t\tawait this.updateAvailabilityChecker?.stop()\n\t\t\tthis.storage.setItem(`updatePhase`, `confirmed`)\n\t\t\tthis.downloadPackage()\n\t\t\tthis.announceUpdate()\n\t\t} catch (thrown) {\n\t\t\tif (thrown instanceof Error) {\n\t\t\t\tthis.logger.error(`Check failed:`, thrown.message)\n\t\t\t} else {\n\t\t\t\tconst thrownType = discoverType(thrown)\n\t\t\t\tthis.logger.error(`Check threw`, thrownType, thrown)\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected announceUpdate(): void {\n\t\tfor (const entry of toEntries(this.services)) {\n\t\t\tconst [serviceName, service] = entry\n\t\t\tif (service) {\n\t\t\t\tif (this.options.services[serviceName].waitFor) {\n\t\t\t\t\tservice.emit(`updatesReady`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.startService(serviceName)\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected tryUpdate(): void {\n\t\tif (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {\n\t\t\tthis.logger.info(`All services are ready to update.`)\n\t\t\tthis.stopAllServices()\n\t\t\t\t.then(() => {\n\t\t\t\t\tthis.logger.info(`All services stopped; starting up fresh...`)\n\t\t\t\t\tthis.startAllServices()\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tthis.logger.info(`All services started; we're back online.`)\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((thrown) => {\n\t\t\t\t\t\t\tif (thrown instanceof Error) {\n\t\t\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\t\t`Failed to start all services:`,\n\t\t\t\t\t\t\t\t\tthrown.message,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\t.catch((thrown) => {\n\t\t\t\t\tif (thrown instanceof Error) {\n\t\t\t\t\t\tthis.logger.error(`Failed to stop all services:`, thrown.message)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}\n\t}\n\n\tprotected startAllServices(): Future<unknown> {\n\t\tthis.logger.info(`Starting all services...`)\n\t\tthis.autoRespawnDeadServices = true\n\t\tconst setupPhase = this.storage.getItem(`setupPhase`)\n\t\tthis.logger.info(`> storage(\"setupPhase\") >`, setupPhase)\n\t\tswitch (setupPhase) {\n\t\t\tcase null:\n\t\t\t\tthis.logger.info(`Starting from scratch.`)\n\t\t\t\tthis.downloadPackage()\n\t\t\t\tthis.installPackage()\n\t\t\t\treturn this.startAllServices()\n\t\t\tcase `downloaded`:\n\t\t\t\tthis.logger.info(`Found package downloaded but not installed.`)\n\t\t\t\tthis.installPackage()\n\t\t\t\treturn this.startAllServices()\n\t\t\tcase `installed`: {\n\t\t\t\tfor (const [serviceName] of toEntries(this.services)) {\n\t\t\t\t\tthis.startService(serviceName)\n\t\t\t\t}\n\t\t\t\treturn this.live\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected startService(serviceName: S): void {\n\t\tthis.logger.info(\n\t\t\t`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`,\n\t\t)\n\t\tif (this.safety >= 2) {\n\t\t\tthrow new Error(`Out of tries...`)\n\t\t}\n\t\tthis.safety++\n\n\t\tconst [exe, ...args] = this.options.services[serviceName].run.split(` `)\n\t\tconst serviceProcess = spawn(exe, args, {\n\t\t\tcwd: this.options.flightdeckRootDir,\n\t\t\tenv: import.meta.env as Record<string, string>,\n\t\t})\n\t\tconst serviceLogger = this.serviceLoggers[serviceName]\n\t\tconst service = (this.services[serviceName] = new ChildSocket(\n\t\t\tserviceProcess,\n\t\t\t`${this.options.packageName}::${serviceName}`,\n\t\t\tserviceLogger,\n\t\t))\n\t\tserviceLogger.processCode = service.proc.pid ?? -1\n\t\tthis.services[serviceName].onAny((...messages) => {\n\t\t\tserviceLogger.info(`💬`, ...messages)\n\t\t})\n\t\tthis.services[serviceName].on(`readyToUpdate`, () => {\n\t\t\tthis.logger.info(`Service \"${serviceName}\" is ready to update.`)\n\t\t\tthis.servicesReadyToUpdate[serviceName] = true\n\t\t\tthis.tryUpdate()\n\t\t})\n\t\tthis.services[serviceName].on(`alive`, () => {\n\t\t\tthis.servicesLive[this.serviceIdx[serviceName]].use(Promise.resolve())\n\t\t\tthis.servicesDead[this.serviceIdx[serviceName]] = new Future(() => {})\n\t\t\tif (this.dead.done) {\n\t\t\t\tthis.dead = new Future(() => {})\n\t\t\t}\n\t\t\tthis.dead.use(Promise.all(this.servicesDead))\n\t\t})\n\t\tthis.services[serviceName].proc.once(`close`, (exitCode) => {\n\t\t\tthis.logger.info(\n\t\t\t\t`Auto-respawn saw \"${serviceName}\" exit with code ${exitCode}`,\n\t\t\t)\n\t\t\tthis.services[serviceName] = null\n\t\t\tif (!this.autoRespawnDeadServices) {\n\t\t\t\tthis.logger.info(`😴 Auto-respawn is off; \"${serviceName}\" rests.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst updatePhase = this.storage.getItem(`updatePhase`)\n\t\t\tthis.logger.info(`> storage(\"updatePhase\") >`, updatePhase)\n\t\t\tconst updatesAreReady = updatePhase === `confirmed`\n\t\t\tif (updatesAreReady) {\n\t\t\t\tthis.serviceLoggers[serviceName].info(`Updating before startup...`)\n\t\t\t\tthis.restartTimes = []\n\t\t\t\tthis.installPackage()\n\t\t\t\tthis.startService(serviceName)\n\t\t\t} else {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tconst fiveMinutesAgo = now - 5 * 60 * 1000\n\t\t\t\tthis.restartTimes = this.restartTimes.filter(\n\t\t\t\t\t(time) => time > fiveMinutesAgo,\n\t\t\t\t)\n\t\t\t\tthis.restartTimes.push(now)\n\n\t\t\t\tif (this.restartTimes.length < 5) {\n\t\t\t\t\tthis.serviceLoggers[serviceName].info(`Crashed. Restarting...`)\n\t\t\t\t\tthis.startService(serviceName)\n\t\t\t\t} else {\n\t\t\t\t\tthis.serviceLoggers[serviceName].info(\n\t\t\t\t\t\t`Crashed 5 times in 5 minutes. Not restarting.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tthis.safety = 0\n\t}\n\n\tprotected downloadPackage(): void {\n\t\tthis.logger.info(`Downloading...`)\n\t\ttry {\n\t\t\tconst out = execSync(this.options.scripts.download)\n\t\t\tthis.logger.info(`Download stdout:`, out.toString())\n\t\t\tthis.storage.setItem(`setupPhase`, `downloaded`)\n\t\t\tthis.logger.info(`Downloaded!`)\n\t\t} catch (thrown) {\n\t\t\tif (thrown instanceof Error) {\n\t\t\t\tthis.logger.error(`Failed to get the latest release: ${thrown.message}`)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tprotected installPackage(): void {\n\t\tthis.logger.info(`Installing...`)\n\n\t\ttry {\n\t\t\tconst out = execSync(this.options.scripts.install)\n\t\t\tthis.logger.info(`Install stdout:`, out.toString())\n\t\t\tthis.storage.setItem(`setupPhase`, `installed`)\n\t\t\tthis.logger.info(`Installed!`)\n\t\t} catch (thrown) {\n\t\t\tif (thrown instanceof Error) {\n\t\t\t\tthis.logger.error(`Failed to get the latest release: ${thrown.message}`)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tpublic stopAllServices(): Future<unknown> {\n\t\tthis.logger.info(`Stopping all services... auto-respawn disabled.`)\n\t\tthis.autoRespawnDeadServices = false\n\t\tfor (const [serviceName] of toEntries(this.services)) {\n\t\t\tthis.stopService(serviceName)\n\t\t}\n\t\treturn this.dead\n\t}\n\n\tpublic stopService(serviceName: S): void {\n\t\tconst service = this.services[serviceName]\n\t\tif (service) {\n\t\t\tthis.logger.info(`Stopping service \"${serviceName}\"...`)\n\t\t\tthis.servicesDead[this.serviceIdx[serviceName]].use(\n\t\t\t\tnew Promise((pass) => {\n\t\t\t\t\tservice.emit(`timeToStop`)\n\t\t\t\t\tservice.proc.once(`close`, (exitCode) => {\n\t\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t\t`Stopped service \"${serviceName}\"; exited with code ${exitCode}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tthis.services[serviceName] = null\n\t\t\t\t\t\tpass()\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t)\n\t\t\tthis.dead.use(Promise.all(this.servicesDead))\n\t\t\tthis.servicesLive[this.serviceIdx[serviceName]] = new Future(() => {})\n\t\t\tif (this.live.done) {\n\t\t\t\tthis.live = new Future(() => {})\n\t\t\t}\n\t\t\tthis.live.use(Promise.all(this.servicesLive))\n\t\t} else {\n\t\t\tthis.serviceLoggers[serviceName].error(\n\t\t\t\t`Tried to stop service, but it wasn't running.`,\n\t\t\t)\n\t\t}\n\t}\n}\n\nexport const FLIGHTDECK_INFO = `info`\nexport const FLIGHTDECK_WARN = `warn`\nexport const FLIGHTDECK_ERROR = `ERR!`\n\nexport type FlightDeckLog = {\n\tlevel:\n\t\t| typeof FLIGHTDECK_ERROR\n\t\t| typeof FLIGHTDECK_INFO\n\t\t| typeof FLIGHTDECK_WARN\n\ttimestamp: number\n\tpackage: string\n\tservice?: string\n\tprocess: number\n\tbody: string\n}\n\nconst LINE_FORMAT = `line-format` satisfies keyof LnavFormat\nconst VALUE = `value` satisfies keyof LnavFormat\n\nexport type LnavFormatVisualComponent = Exclude<\n\tExclude<LnavFormat[`line-format`], undefined>[number],\n\tstring\n>\n\nexport type LnavFormatBreakdown = Exclude<LnavFormat[`value`], undefined>\nexport type MemberOf<T> = T[keyof T]\nexport type LnavFormatValueDefinition = MemberOf<LnavFormatBreakdown>\n\nexport type FlightDeckFormat = {\n\t[LINE_FORMAT]: (\n\t\t| string\n\t\t| (LnavFormatVisualComponent & {\n\t\t\t\tfield: keyof FlightDeckLog | `__level__` | `__timestamp__`\n\t\t })\n\t)[]\n\t[VALUE]: {\n\t\t[K in keyof FlightDeckLog]: LnavFormatValueDefinition & {\n\t\t\tkind: FlightDeckLog[K] extends number | undefined\n\t\t\t\t? `integer`\n\t\t\t\t: FlightDeckLog[K] extends string | undefined\n\t\t\t\t\t? `string`\n\t\t\t\t\t: never\n\t\t}\n\t}\n}\n\nexport const FLIGHTDECK_LNAV_FORMAT = {\n\ttitle: `FlightDeck Log`,\n\tdescription: `Format for events logged by the FlightDeck process manager.`,\n\t\"file-type\": `json`,\n\t\"timestamp-field\": `timestamp`,\n\t\"timestamp-divisor\": 1000,\n\t\"module-field\": `package`,\n\t\"opid-field\": `service`,\n\t\"level-field\": `level`,\n\tlevel: {\n\t\tinfo: FLIGHTDECK_INFO,\n\t\twarning: FLIGHTDECK_WARN,\n\t\terror: FLIGHTDECK_ERROR,\n\t},\n\n\t[LINE_FORMAT]: [\n\t\t{\n\t\t\tfield: `level`,\n\t\t},\n\t\t{\n\t\t\tprefix: ` `,\n\t\t\tfield: `__timestamp__`,\n\t\t\t\"timestamp-format\": `%Y-%m-%dT%H:%M:%S.%L%Z`,\n\t\t},\n\t\t{\n\t\t\tprefix: ` `,\n\t\t\tfield: `process`,\n\t\t\t\"min-width\": 5,\n\t\t},\n\t\t{\n\t\t\tprefix: `:`,\n\t\t\tfield: `package`,\n\t\t},\n\t\t{\n\t\t\tprefix: `:`,\n\t\t\tfield: `service`,\n\t\t\t\"default-value\": ``,\n\t\t},\n\t\t{\n\t\t\tprefix: `: `,\n\t\t\tfield: `body`,\n\t\t},\n\t],\n\n\t[VALUE]: {\n\t\ttimestamp: {\n\t\t\tkind: `integer`,\n\t\t},\n\t\tlevel: {\n\t\t\tkind: `string`,\n\t\t},\n\t\tpackage: {\n\t\t\tkind: `string`,\n\t\t},\n\t\tservice: {\n\t\t\tkind: `string`,\n\t\t},\n\t\tprocess: {\n\t\t\tkind: `integer`,\n\t\t},\n\t\tbody: {\n\t\t\tkind: `string`,\n\t\t},\n\t},\n} as const satisfies FlightDeckFormat & LnavFormat\n\nexport class FlightDeckLogger\n\timplements Pick<Console, `error` | `info` | `warn`>\n{\n\tpublic readonly packageName: string\n\tpublic readonly serviceName?: string\n\tpublic readonly jsonLogging: boolean\n\tpublic processCode: number\n\tpublic constructor(\n\t\tpackageName: string,\n\t\tprocessCode: number,\n\t\tserviceName?: string,\n\t\toptions?: { jsonLogging: boolean },\n\t) {\n\t\tthis.packageName = packageName\n\t\tif (serviceName) {\n\t\t\tthis.serviceName = serviceName\n\t\t}\n\t\tthis.processCode = processCode\n\t\tthis.jsonLogging = options?.jsonLogging ?? false\n\t}\n\tprotected log(\n\t\tlevel:\n\t\t\t| typeof FLIGHTDECK_ERROR\n\t\t\t| typeof FLIGHTDECK_INFO\n\t\t\t| typeof FLIGHTDECK_WARN,\n\t\t...messages: unknown[]\n\t): void {\n\t\tif (this.jsonLogging) {\n\t\t\tlet body = messages\n\t\t\t\t.map((message) =>\n\t\t\t\t\ttypeof message === `string`\n\t\t\t\t\t\t? message\n\t\t\t\t\t\t: inspect(message, false, null, true),\n\t\t\t\t)\n\t\t\t\t.join(` `)\n\t\t\tif (body.includes(`\\n`)) {\n\t\t\t\tbody = `\\n ${body.split(`\\n`).join(`\\n `)}`\n\t\t\t}\n\t\t\tconst log: FlightDeckLog = {\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tlevel,\n\t\t\t\tprocess: this.processCode,\n\t\t\t\tpackage: this.packageName,\n\t\t\t\tbody,\n\t\t\t}\n\t\t\tif (this.serviceName) {\n\t\t\t\tlog.service = this.serviceName\n\t\t\t}\n\t\t\tprocess.stdout.write(JSON.stringify(log) + `\\n`)\n\t\t} else {\n\t\t\tconst source = this.serviceName\n\t\t\t\t? `${this.packageName}:${this.serviceName}`\n\t\t\t\t: this.packageName\n\t\t\tswitch (level) {\n\t\t\t\tcase FLIGHTDECK_INFO:\n\t\t\t\t\tconsole.log(`${source}:`, ...messages)\n\t\t\t\t\tbreak\n\t\t\t\tcase FLIGHTDECK_WARN:\n\t\t\t\t\tconsole.warn(`${source}:`, ...messages)\n\t\t\t\t\tbreak\n\t\t\t\tcase FLIGHTDECK_ERROR:\n\t\t\t\t\tconsole.error(`${source}:`, ...messages)\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tpublic info(...messages: unknown[]): void {\n\t\tthis.log(FLIGHTDECK_INFO, ...messages)\n\t}\n\n\tpublic warn(...messages: unknown[]): void {\n\t\tthis.log(FLIGHTDECK_WARN, ...messages)\n\t}\n\n\tpublic error(...messages: unknown[]): void {\n\t\tthis.log(FLIGHTDECK_ERROR, ...messages)\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAGA,MAAa,MAAM,UAAU;CAC5B,QAAQ,EAAE,mBAAmB,KAAK,UAAU,KAAK,YAAY,EAAE;CAC/D,cAAc;CACd,QAAQ,EAAE;CACV,YAAY,OAAO,KAAK;CACxB,wBAAwB;CACxB,CAAC;;;;ACSF,MAAa,0BAA0B,CAAC,cAAc,YAAY;AAIlE,MAAa,2BAA2B,CAAC,YAAY,YAAY;AAIjE,SAAgB,gBAAgB,SAA0B;AACzD,QACC,kBAAkB,KAAK,QAAQ,IAAI,CAAC,OAAO,MAAM,OAAO,WAAW,QAAQ,CAAC;;AAwB9E,eAAe,eAAe,KAAa,YAAY,KAAM,aAAa,KAAK;AAC9E,QAAO,IAAI,SAAe,MAAM,SAAS;EACxC,MAAM,QAAQ,KAAK,KAAK;EAExB,MAAM,cAAc;AACnB,WAAQ,OAAO,MAAM,IAAI;AACzB,OAAI;AAEH,YAAQ,KAAK,KAAK,EAAE;AAEpB,QAAI,KAAK,KAAK,GAAG,QAAQ,WAAW;AACnC,0BAAK,IAAI,MAAM,2BAA2B,MAAM,CAAC;AACjD;;AAED,eAAW,OAAO,WAAW;YACrB,KAAK;AACb,QAAI,eAAe,SAAS,UAAU,KAAK;AAC1C,SAAI,IAAI,SAAS,SAAS;AACzB,YAAM;AACN;;AAED,SAAI,IAAI,SAAS,SAAS;AAEzB,UAAI,KAAK,KAAK,GAAG,QAAQ,WAAW;AACnC,4BACC,IAAI,MAAM,2BAA2B,IAAI,uBAAuB,CAChE;AACD;;AAED,aAAO,WAAW,OAAO,WAAW;;;AAGtC,SAAK,IAAI;AACT;;;AAIF,SAAO;GACN;;AAGH,IAAa,aAAb,MAAmD;CAClD,aAAoB,KACnB,mBACA,aACkB;AAClB,UAAQ,KAAK,mCAAmC,YAAY,GAAG;EAI/D,MAAM,aAHU,IAAI,kBAAsC,EACzD,MAAM,QAAQ,mBAAmB,WAAW,YAAY,EACxD,CAAC,CACyB,QAAQ,aAAa;AAChD,MAAI,eAAe,KAClB,OAAM,IAAI,MAAM,eAAe,YAAY,oBAAoB;EAEhE,MAAM,MAAM,OAAO,WAAW;EAC9B,MAAM,MAAM,KAAK,KAAK;AACtB,UAAQ,KAAK,KAAK,UAAU;AAC5B,QAAM,eAAe,KAAK,KAAM,EAAE;EAClC,MAAM,UAAU,KAAK,KAAK,GAAG;AAC7B,UAAQ,OAAO,MACd,OAAO,YAAY,wBAAwB,IAAI,cAAc,QAAQ,MACrE;AACD,SAAO;;CAGR,AAAgB;CAChB,AAAU,SAAS;CAEnB,AAAU;CACV,AAAU;CAOV,AAAU;CACV,AAAO;CACP,AAAO;CACP,AAAO;CAEP,AAAU;CACV,AAAU;CAIV,AAAU,4BAA4C;CAEtD,AAAO;CACP,AAAO;CACP,AAAO,OAAO,IAAI,aAAa,GAAG;CAClC,AAAO,OAAO,IAAI,aAAa,GAAG;CAElC,AAAU,eAAyB,EAAE;CAErC,AAAO,YAAY,SAA+B;AACjD,OAAK,UAAU;EACf,MAAM,EAAE,sBAAsB;EAC9B,MAAM,EAAE,oBAAoB,QAAQ,SAAS,EAAE,cAAc,KAAK;EAClE,MAAM,OAAO,QAAQ,QAAQ;EAC7B,MAAM,SAAS,oBAAoB;EAEnC,MAAM,kBAAkB,UAAU,QAAQ,SAAS;AACnD,OAAK,WAAW,YACf,gBAAgB,KAAK,CAAC,iBAAiB,CAAC,aAAa,KAAK,CAAC,CAC3D;AACD,OAAK,aAAa,YACjB,gBAAgB,KAAK,CAAC,cAAc,QAAQ,CAAC,aAAa,IAAI,CAAC,CAC/D;AACD,OAAK,+BAA+B,YACnC,gBAAgB,KAAK,CAAC,aAAa,EAAE,eAAe,CACnD,aACA,CAAC,QACD,CAAC,CACF;AACD,OAAK,wBAAwB,EAAE,GAAG,KAAK,8BAA8B;AACrE,OAAK,0BAA0B;AAE/B,OAAK,SAAS,IAAI,iBACjB,KAAK,QAAQ,aACb,QAAQ,KACR,QACA,EAAE,aAAa,KAAK,QAAQ,eAAe,OAAO,CAClD;AACD,OAAK,iBAAiB,YACrB,gBAAgB,KAAK,CAAC,iBAAiB,CACtC,aACA,IAAI,iBACH,KAAK,QAAQ,aACb,QAAQ,KACR,aACA,EAAE,aAAa,KAAK,QAAQ,eAAe,OAAO,CAClD,CACD,CAAC,CACF;AAED,OAAK,eAAe,gBAAgB,UAAU,IAAI,aAAa,GAAG,CAAC;AACnE,OAAK,eAAe,gBAAgB,UAAU,IAAI,aAAa,GAAG,CAAC;AACnE,OAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;AAC7C,OAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;AAE7C,OAAK,UAAU,IAAI,kBAAkB,EACpC,MAAM,QAAQ,mBAAmB,WAAW,QAAQ,YAAY,EAChE,CAAC;AAEF,OAAK,QAAQ,QAAQ,cAAc,GAAG,QAAQ,MAAM;AAEpD,MAAI,sBAAsB,OACzB,MAAK,OAAO,KACX,6FACA;MAED,eAAc,KAAK,QAAQ;GAC1B,IAAIA,OAAqB,EAAE;AAC3B,OACE,GAAG,SAAS,UAAU;AACtB,SAAK,KAAK,iBAAiB,SAAS,QAAQ,OAAO,KAAK,MAAM,CAAC;KAC9D,CACD,GAAG,OAAO,YAAY;IACtB,MAAM,aAAa,IAAI,QAAQ;AAC/B,QAAI;AACH,SAAI,OAAO,IAAI,QAAQ,YAAa,OAAM;KAC1C,MAAM,qBAAqB,UAAU;AACrC,SAAI,eAAe,UAAU,qBAAqB;AACjD,WAAK,OAAO,KACX,0BAA0B,mBAAmB,YAAY,WAAW,IACpE;AACD,YAAM;;KAEP,MAAM,MAAM,IAAI,IAAI,IAAI,KAAK,OAAO;AACpC,UAAK,OAAO,KAAK,IAAI,QAAQ,IAAI,SAAS;KAE1C,MAAM,sBAAsB,OAAO,OAAO,KAAK,CAAC,UAAU;AAC1D,SAAI,CAAC,gBAAgB,oBAAoB,CACxC,OAAM;AAGP,SAAI,UAAU,IAAI;AAClB,SAAI,KAAK;AAET,UAAK,QAAQ,QAAQ,eAAe,WAAW;AAC/C,UAAK,QAAQ,QAAQ,wBAAwB,oBAAoB;KACjE,MAAM,EAAE,sBAAsB,QAAQ;AACtC,SAAI,mBAAmB;AACtB,YAAM,KAAK,2BAA2B,MAAM;AAC5C,YAAM,KAAK,WAAW,oBAAoB;MAC1C,MAAM,cAAc,KAAK,QAAQ,QAAQ,cAAc;AACvD,WAAK,OAAO,KAAK,8BAA8B,YAAY;AAC3D,UAAI,gBAAgB,YAAY;AAC/B,YAAK,4BAA4B,IAAI,QACpC,kBACA,YAAY;AACX,cAAM,KAAK,WAAW,oBAAoB;SAE3C;AACD,YAAK,0BAA0B,OAAO;;WAGvC,MAAK,iBAAiB;aAEf,QAAQ;AAChB,UAAK,OAAO,MAAM,QAAQ,IAAI,IAAI;AAClC,SAAI,OAAO,WAAW,UAAU;AAC/B,UAAI,UAAU,OAAO;AACrB,UAAI,KAAK;;cAED;AACT,YAAO,EAAE;;KAET;IACF,CAAC,OAAO,YAAY;AACrB,QAAK,OAAO,KAAK,0BAA0B,OAAO;IACjD;AAGH,OAAK,kBAAkB,CACrB,WAAW;AACX,QAAK,OAAO,KAAK,wBAAwB;IACxC,CACD,OAAO,WAAW;AAClB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,iCAAiC,OAAO,QAAQ;IAElE;AAEH,UAAQ,GAAG,WAAW,YAAY;AACjC,WAAQ,KAAK,oBAAoB;AACjC,SAAM,KAAK,iBAAiB;AAC5B,QAAK,QAAQ,WAAW,aAAa;AACrC,WAAQ,KAAK,EAAE;IACd;;CAGH,MAAgB,WAAW,SAAgC;AAC1D,OAAK,OAAO,KAAK,0BAA0B;EAC3C,MAAM,EAAE,sBAAsB,KAAK,QAAQ;AAC3C,MAAI,CAAC,mBAAmB;AACvB,QAAK,OAAO,KAAK,qCAAqC;AACtD;;AAED,MAAI;GACH,MAAM,MAAM,SAAS,GAAG,kBAAkB,GAAG,UAAU;AACvD,QAAK,OAAO,KAAK,iBAAiB,IAAI,UAAU,CAAC;AACjD,SAAM,KAAK,2BAA2B,MAAM;AAC5C,QAAK,QAAQ,QAAQ,eAAe,YAAY;AAChD,QAAK,iBAAiB;AACtB,QAAK,gBAAgB;WACb,QAAQ;AAChB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,iBAAiB,OAAO,QAAQ;QAC5C;IACN,MAAM,aAAa,aAAa,OAAO;AACvC,SAAK,OAAO,MAAM,eAAe,YAAY,OAAO;;;;CAKvD,AAAU,iBAAuB;AAChC,OAAK,MAAM,SAAS,UAAU,KAAK,SAAS,EAAE;GAC7C,MAAM,CAAC,aAAa,WAAW;AAC/B,OAAI,SACH;QAAI,KAAK,QAAQ,SAAS,aAAa,QACtC,SAAQ,KAAK,eAAe;SAG7B,MAAK,aAAa,YAAY;;;CAKjC,AAAU,YAAkB;AAC3B,MAAI,UAAU,KAAK,sBAAsB,CAAC,OAAO,GAAG,aAAa,QAAQ,EAAE;AAC1E,QAAK,OAAO,KAAK,oCAAoC;AACrD,QAAK,iBAAiB,CACpB,WAAW;AACX,SAAK,OAAO,KAAK,6CAA6C;AAC9D,SAAK,kBAAkB,CACrB,WAAW;AACX,UAAK,OAAO,KAAK,2CAA2C;MAC3D,CACD,OAAO,WAAW;AAClB,SAAI,kBAAkB,MACrB,MAAK,OAAO,MACX,iCACA,OAAO,QACP;MAED;KACF,CACD,OAAO,WAAW;AAClB,QAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,gCAAgC,OAAO,QAAQ;KAEjE;;;CAIL,AAAU,mBAAoC;AAC7C,OAAK,OAAO,KAAK,2BAA2B;AAC5C,OAAK,0BAA0B;EAC/B,MAAM,aAAa,KAAK,QAAQ,QAAQ,aAAa;AACrD,OAAK,OAAO,KAAK,6BAA6B,WAAW;AACzD,UAAQ,YAAR;GACC,KAAK;AACJ,SAAK,OAAO,KAAK,yBAAyB;AAC1C,SAAK,iBAAiB;AACtB,SAAK,gBAAgB;AACrB,WAAO,KAAK,kBAAkB;GAC/B,KAAK;AACJ,SAAK,OAAO,KAAK,8CAA8C;AAC/D,SAAK,gBAAgB;AACrB,WAAO,KAAK,kBAAkB;GAC/B,KAAK;AACJ,SAAK,MAAM,CAAC,gBAAgB,UAAU,KAAK,SAAS,CACnD,MAAK,aAAa,YAAY;AAE/B,WAAO,KAAK;;;CAKf,AAAU,aAAa,aAAsB;AAC5C,OAAK,OAAO,KACX,oBAAoB,KAAK,QAAQ,YAAY,IAAI,YAAY,QAAQ,KAAK,OAAO,OACjF;AACD,MAAI,KAAK,UAAU,EAClB,OAAM,IAAI,MAAM,kBAAkB;AAEnC,OAAK;EAEL,MAAM,CAAC,KAAK,GAAG,QAAQ,KAAK,QAAQ,SAAS,aAAa,IAAI,MAAM,IAAI;EACxE,MAAM,iBAAiB,MAAM,KAAK,MAAM;GACvC,KAAK,KAAK,QAAQ;GAClB,KAAK,OAAO,KAAK;GACjB,CAAC;EACF,MAAM,gBAAgB,KAAK,eAAe;AAM1C,gBAAc,eALG,KAAK,SAAS,eAAe,IAAI,YACjD,gBACA,GAAG,KAAK,QAAQ,YAAY,IAAI,eAChC,cACA,EACmC,KAAK,OAAO;AAChD,OAAK,SAAS,aAAa,OAAO,GAAG,aAAa;AACjD,iBAAc,KAAK,MAAM,GAAG,SAAS;IACpC;AACF,OAAK,SAAS,aAAa,GAAG,uBAAuB;AACpD,QAAK,OAAO,KAAK,YAAY,YAAY,uBAAuB;AAChE,QAAK,sBAAsB,eAAe;AAC1C,QAAK,WAAW;IACf;AACF,OAAK,SAAS,aAAa,GAAG,eAAe;AAC5C,QAAK,aAAa,KAAK,WAAW,cAAc,IAAI,QAAQ,SAAS,CAAC;AACtE,QAAK,aAAa,KAAK,WAAW,gBAAgB,IAAI,aAAa,GAAG;AACtE,OAAI,KAAK,KAAK,KACb,MAAK,OAAO,IAAI,aAAa,GAAG;AAEjC,QAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;IAC5C;AACF,OAAK,SAAS,aAAa,KAAK,KAAK,UAAU,aAAa;AAC3D,QAAK,OAAO,KACX,qBAAqB,YAAY,mBAAmB,WACpD;AACD,QAAK,SAAS,eAAe;AAC7B,OAAI,CAAC,KAAK,yBAAyB;AAClC,SAAK,OAAO,KAAK,4BAA4B,YAAY,UAAU;AACnE;;GAED,MAAM,cAAc,KAAK,QAAQ,QAAQ,cAAc;AACvD,QAAK,OAAO,KAAK,8BAA8B,YAAY;AAE3D,OADwB,gBAAgB,aACnB;AACpB,SAAK,eAAe,aAAa,KAAK,6BAA6B;AACnE,SAAK,eAAe,EAAE;AACtB,SAAK,gBAAgB;AACrB,SAAK,aAAa,YAAY;UACxB;IACN,MAAM,MAAM,KAAK,KAAK;IACtB,MAAM,iBAAiB,MAAM,MAAS;AACtC,SAAK,eAAe,KAAK,aAAa,QACpC,SAAS,OAAO,eACjB;AACD,SAAK,aAAa,KAAK,IAAI;AAE3B,QAAI,KAAK,aAAa,SAAS,GAAG;AACjC,UAAK,eAAe,aAAa,KAAK,yBAAyB;AAC/D,UAAK,aAAa,YAAY;UAE9B,MAAK,eAAe,aAAa,KAChC,gDACA;;IAGF;AACF,OAAK,SAAS;;CAGf,AAAU,kBAAwB;AACjC,OAAK,OAAO,KAAK,iBAAiB;AAClC,MAAI;GACH,MAAM,MAAM,SAAS,KAAK,QAAQ,QAAQ,SAAS;AACnD,QAAK,OAAO,KAAK,oBAAoB,IAAI,UAAU,CAAC;AACpD,QAAK,QAAQ,QAAQ,cAAc,aAAa;AAChD,QAAK,OAAO,KAAK,cAAc;WACvB,QAAQ;AAChB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,qCAAqC,OAAO,UAAU;AAEzE;;;CAIF,AAAU,iBAAuB;AAChC,OAAK,OAAO,KAAK,gBAAgB;AAEjC,MAAI;GACH,MAAM,MAAM,SAAS,KAAK,QAAQ,QAAQ,QAAQ;AAClD,QAAK,OAAO,KAAK,mBAAmB,IAAI,UAAU,CAAC;AACnD,QAAK,QAAQ,QAAQ,cAAc,YAAY;AAC/C,QAAK,OAAO,KAAK,aAAa;WACtB,QAAQ;AAChB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,qCAAqC,OAAO,UAAU;AAEzE;;;CAIF,AAAO,kBAAmC;AACzC,OAAK,OAAO,KAAK,kDAAkD;AACnE,OAAK,0BAA0B;AAC/B,OAAK,MAAM,CAAC,gBAAgB,UAAU,KAAK,SAAS,CACnD,MAAK,YAAY,YAAY;AAE9B,SAAO,KAAK;;CAGb,AAAO,YAAY,aAAsB;EACxC,MAAM,UAAU,KAAK,SAAS;AAC9B,MAAI,SAAS;AACZ,QAAK,OAAO,KAAK,qBAAqB,YAAY,MAAM;AACxD,QAAK,aAAa,KAAK,WAAW,cAAc,IAC/C,IAAI,SAAS,SAAS;AACrB,YAAQ,KAAK,aAAa;AAC1B,YAAQ,KAAK,KAAK,UAAU,aAAa;AACxC,UAAK,OAAO,KACX,oBAAoB,YAAY,sBAAsB,WACtD;AACD,UAAK,SAAS,eAAe;AAC7B,WAAM;MACL;KACD,CACF;AACD,QAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;AAC7C,QAAK,aAAa,KAAK,WAAW,gBAAgB,IAAI,aAAa,GAAG;AACtE,OAAI,KAAK,KAAK,KACb,MAAK,OAAO,IAAI,aAAa,GAAG;AAEjC,QAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;QAE7C,MAAK,eAAe,aAAa,MAChC,gDACA;;;AAKJ,MAAa,kBAAkB;AAC/B,MAAa,kBAAkB;AAC/B,MAAa,mBAAmB;AAchC,MAAM,cAAc;AACpB,MAAM,QAAQ;AA6Bd,MAAa,yBAAyB;CACrC,OAAO;CACP,aAAa;CACb,aAAa;CACb,mBAAmB;CACnB,qBAAqB;CACrB,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,OAAO;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACP;EAEA,cAAc;EACd,EACC,OAAO,SACP;EACD;GACC,QAAQ;GACR,OAAO;GACP,oBAAoB;GACpB;EACD;GACC,QAAQ;GACR,OAAO;GACP,aAAa;GACb;EACD;GACC,QAAQ;GACR,OAAO;GACP;EACD;GACC,QAAQ;GACR,OAAO;GACP,iBAAiB;GACjB;EACD;GACC,QAAQ;GACR,OAAO;GACP;EACD;EAEA,QAAQ;EACR,WAAW,EACV,MAAM,WACN;EACD,OAAO,EACN,MAAM,UACN;EACD,SAAS,EACR,MAAM,UACN;EACD,SAAS,EACR,MAAM,UACN;EACD,SAAS,EACR,MAAM,WACN;EACD,MAAM,EACL,MAAM,UACN;EACD;CACD;AAED,IAAa,mBAAb,MAEA;CACC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAO;CACP,AAAO,YACN,aACA,aACA,aACA,SACC;AACD,OAAK,cAAc;AACnB,MAAI,YACH,MAAK,cAAc;AAEpB,OAAK,cAAc;AACnB,OAAK,cAAc,SAAS,eAAe;;CAE5C,AAAU,IACT,OAIA,GAAG,UACI;AACP,MAAI,KAAK,aAAa;GACrB,IAAI,OAAO,SACT,KAAK,YACL,OAAO,YAAY,WAChB,UACA,QAAQ,SAAS,OAAO,MAAM,KAAK,CACtC,CACA,KAAK,IAAI;AACX,OAAI,KAAK,SAAS,KAAK,CACtB,QAAO,OAAO,KAAK,MAAM,KAAK,CAAC,KAAK,OAAO;GAE5C,MAAMC,MAAqB;IAC1B,WAAW,KAAK,KAAK;IACrB;IACA,SAAS,KAAK;IACd,SAAS,KAAK;IACd;IACA;AACD,OAAI,KAAK,YACR,KAAI,UAAU,KAAK;AAEpB,WAAQ,OAAO,MAAM,KAAK,UAAU,IAAI,GAAG,KAAK;SAC1C;GACN,MAAM,SAAS,KAAK,cACjB,GAAG,KAAK,YAAY,GAAG,KAAK,gBAC5B,KAAK;AACR,WAAQ,OAAR;IACC,KAAK;AACJ,aAAQ,IAAI,GAAG,OAAO,IAAI,GAAG,SAAS;AACtC;IACD,KAAK;AACJ,aAAQ,KAAK,GAAG,OAAO,IAAI,GAAG,SAAS;AACvC;IACD,KAAK;AACJ,aAAQ,MAAM,GAAG,OAAO,IAAI,GAAG,SAAS;AACxC;;;;CAIJ,AAAO,KAAK,GAAG,UAA2B;AACzC,OAAK,IAAI,iBAAiB,GAAG,SAAS;;CAGvC,AAAO,KAAK,GAAG,UAA2B;AACzC,OAAK,IAAI,iBAAiB,GAAG,SAAS;;CAGvC,AAAO,MAAM,GAAG,UAA2B;AAC1C,OAAK,IAAI,kBAAkB,GAAG,SAAS"}
|
package/dist/flightdeck.x.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as FlightDeckLogger, s as FlightDeck } from "./flightdeck.lib-
|
|
2
|
+
import { c as FlightDeckLogger, s as FlightDeck } from "./flightdeck.lib-DneT-dY9.js";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { type } from "arktype";
|
|
5
5
|
import { cli, optional, options, parseBooleanOption, parseNumberOption } from "comline";
|
|
@@ -68,6 +68,23 @@ const FLIGHTDECK_MANUAL = options(`Run the FlightDeck process manager.`, type({
|
|
|
68
68
|
parse: parseBooleanOption
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
|
+
const KILL_MANUAL = options(`Kill the FlightDeck process.`, type({
|
|
72
|
+
flightdeckRootDir: `string`,
|
|
73
|
+
packageName: `string`
|
|
74
|
+
}), {
|
|
75
|
+
flightdeckRootDir: {
|
|
76
|
+
flag: `d`,
|
|
77
|
+
required: true,
|
|
78
|
+
description: `Directory where the service is stored.`,
|
|
79
|
+
example: `--flightdeckRootDir="./services/sample/repo/my-app/current"`
|
|
80
|
+
},
|
|
81
|
+
packageName: {
|
|
82
|
+
flag: `n`,
|
|
83
|
+
required: true,
|
|
84
|
+
description: `Name of the package.`,
|
|
85
|
+
example: `--packageName="my-app"`
|
|
86
|
+
}
|
|
87
|
+
});
|
|
71
88
|
const SCHEMA_MANUAL = options(`Write a json schema for the FlightDeck options.`, type({ "outdir?": `string` }), { outdir: {
|
|
72
89
|
flag: `o`,
|
|
73
90
|
required: false,
|
|
@@ -83,6 +100,7 @@ const { inputs, writeJsonSchema } = cli({
|
|
|
83
100
|
routeOptions: {
|
|
84
101
|
"": FLIGHTDECK_MANUAL,
|
|
85
102
|
$configPath: FLIGHTDECK_MANUAL,
|
|
103
|
+
kill: KILL_MANUAL,
|
|
86
104
|
schema: SCHEMA_MANUAL
|
|
87
105
|
},
|
|
88
106
|
debugOutput: true,
|
|
@@ -98,13 +116,14 @@ switch (inputs.case) {
|
|
|
98
116
|
writeJsonSchema(outdir ?? `.`);
|
|
99
117
|
}
|
|
100
118
|
break;
|
|
119
|
+
case `kill`:
|
|
120
|
+
{
|
|
121
|
+
const { flightdeckRootDir, packageName } = inputs.opts;
|
|
122
|
+
await FlightDeck.kill(flightdeckRootDir, packageName);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
101
125
|
case ``:
|
|
102
|
-
case `$configPath`:
|
|
103
|
-
const flightDeck = new FlightDeck(inputs.opts);
|
|
104
|
-
process.on(`close`, async () => {
|
|
105
|
-
await flightDeck.stopAllServices();
|
|
106
|
-
});
|
|
107
|
-
}
|
|
126
|
+
case `$configPath`: new FlightDeck(inputs.opts);
|
|
108
127
|
}
|
|
109
128
|
|
|
110
129
|
//#endregion
|
package/dist/flightdeck.x.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flightdeck.x.js","names":[],"sources":["../src/flightdeck.x.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport * as path from \"node:path\"\n\nimport { type } from \"arktype\"\nimport type { OptionsGroup } from \"comline\"\nimport {\n\tcli,\n\toptional,\n\toptions,\n\tparseBooleanOption,\n\tparseNumberOption,\n} from \"comline\"\n\nimport type { FlightDeckOptions } from \"./flightdeck.lib\"\nimport { FlightDeck, FlightDeckLogger } from \"./flightdeck.lib\"\n\nconst CLI_LOGGER = new FlightDeckLogger(`comline`, process.pid, undefined, {\n\tjsonLogging: true,\n})\nObject.assign(console, {\n\tlog: CLI_LOGGER.info.bind(CLI_LOGGER),\n\tinfo: CLI_LOGGER.info.bind(CLI_LOGGER),\n\twarn: CLI_LOGGER.warn.bind(CLI_LOGGER),\n\terror: CLI_LOGGER.error.bind(CLI_LOGGER),\n})\n\nconst FLIGHTDECK_MANUAL = options(\n\t`Run the FlightDeck process manager.`,\n\ttype({\n\t\t\"port?\": `number`,\n\t\tpackageName: `string`,\n\t\tservices: { \"[string]\": { run: `string`, waitFor: `boolean` } },\n\t\tflightdeckRootDir: `string`,\n\t\tscripts: {\n\t\t\tdownload: `string`,\n\t\t\tinstall: `string`,\n\t\t\tcheckAvailability: `string`,\n\t\t},\n\t\t\"jsonLogging?\": `boolean`,\n\t}),\n\t{\n\t\tport: {\n\t\t\tflag: `p`,\n\t\t\trequired: false,\n\t\t\tdescription: `Port to run the flightdeck server on.`,\n\t\t\texample: `--port=8080`,\n\t\t\tparse: parseNumberOption,\n\t\t},\n\t\tpackageName: {\n\t\t\tflag: `n`,\n\t\t\trequired: true,\n\t\t\tdescription: `Name of the package.`,\n\t\t\texample: `--packageName=\"my-app\"`,\n\t\t},\n\t\tservices: {\n\t\t\tflag: `s`,\n\t\t\trequired: true,\n\t\t\tdescription: `Map of service names to executables.`,\n\t\t\texample: `--services=\"{\\\\\"frontend\\\\\":{\\\\\"run\\\\\":\\\\\"./frontend\\\\\",\\\\\"waitFor\\\\\":false},\\\\\"backend\\\\\":{\\\\\"run\\\\\":\\\\\"./backend\\\\\",\\\\\"waitFor\\\\\":true}}\"`,\n\t\t\tparse: JSON.parse,\n\t\t},\n\t\tflightdeckRootDir: {\n\t\t\tflag: `d`,\n\t\t\trequired: true,\n\t\t\tdescription: `Directory where the service is stored.`,\n\t\t\texample: `--flightdeckRootDir=\"./services/sample/repo/my-app/current\"`,\n\t\t},\n\t\tscripts: {\n\t\t\tflag: `r`,\n\t\t\trequired: true,\n\t\t\tdescription: `Map of scripts to run.`,\n\t\t\texample: `--scripts=\"{\\\\\"download\\\\\":\\\\\"npm i\",\\\\\"install\\\\\":\\\\\"npm run build\\\\\"}\"`,\n\t\t\tparse: JSON.parse,\n\t\t},\n\t\tjsonLogging: {\n\t\t\tflag: `j`,\n\t\t\trequired: false,\n\t\t\tdescription: `Enable json logging.`,\n\t\t\texample: `--jsonLogging`,\n\t\t\tparse: parseBooleanOption,\n\t\t},\n\t},\n) satisfies OptionsGroup<FlightDeckOptions>\n\nconst SCHEMA_MANUAL = options(\n\t`Write a json schema for the FlightDeck options.`,\n\ttype({ \"outdir?\": `string` }),\n\t{\n\t\toutdir: {\n\t\t\tflag: `o`,\n\t\t\trequired: false,\n\t\t\tdescription: `Directory to write the schema to.`,\n\t\t\texample: `--outdir=./dist`,\n\t\t},\n\t},\n) satisfies OptionsGroup<{ outdir?: string | undefined }>\n\nconst parse = cli(\n\t{\n\t\tcliName: `flightdeck`,\n\t\troutes: optional({ schema: null, $configPath: null }),\n\t\trouteOptions: {\n\t\t\t\"\": FLIGHTDECK_MANUAL,\n\t\t\t$configPath: FLIGHTDECK_MANUAL,\n\t\t\tschema: SCHEMA_MANUAL,\n\t\t},\n\t\tdebugOutput: true,\n\t\tdiscoverConfigPath: (args) => {\n\t\t\tif (args[0] === `schema`) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst configPath =\n\t\t\t\targs[0] ?? path.join(process.cwd(), `flightdeck.config.json`)\n\t\t\treturn configPath\n\t\t},\n\t},\n\tconsole,\n)\n\nconst { inputs, writeJsonSchema } = parse(process.argv)\n\nswitch (inputs.case) {\n\tcase `schema`:\n\t\t{\n\t\t\tconst { outdir } = inputs.opts\n\t\t\twriteJsonSchema(outdir ?? `.`)\n\t\t}\n\t\tbreak\n\tcase
|
|
1
|
+
{"version":3,"file":"flightdeck.x.js","names":[],"sources":["../src/flightdeck.x.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport * as path from \"node:path\"\n\nimport { type } from \"arktype\"\nimport type { OptionsGroup } from \"comline\"\nimport {\n\tcli,\n\toptional,\n\toptions,\n\tparseBooleanOption,\n\tparseNumberOption,\n} from \"comline\"\n\nimport type { FlightDeckOptions } from \"./flightdeck.lib.ts\"\nimport { FlightDeck, FlightDeckLogger } from \"./flightdeck.lib.ts\"\n\nconst CLI_LOGGER = new FlightDeckLogger(`comline`, process.pid, undefined, {\n\tjsonLogging: true,\n})\nObject.assign(console, {\n\tlog: CLI_LOGGER.info.bind(CLI_LOGGER),\n\tinfo: CLI_LOGGER.info.bind(CLI_LOGGER),\n\twarn: CLI_LOGGER.warn.bind(CLI_LOGGER),\n\terror: CLI_LOGGER.error.bind(CLI_LOGGER),\n})\n\nconst FLIGHTDECK_MANUAL = options(\n\t`Run the FlightDeck process manager.`,\n\ttype({\n\t\t\"port?\": `number`,\n\t\tpackageName: `string`,\n\t\tservices: { \"[string]\": { run: `string`, waitFor: `boolean` } },\n\t\tflightdeckRootDir: `string`,\n\t\tscripts: {\n\t\t\tdownload: `string`,\n\t\t\tinstall: `string`,\n\t\t\tcheckAvailability: `string`,\n\t\t},\n\t\t\"jsonLogging?\": `boolean`,\n\t}),\n\t{\n\t\tport: {\n\t\t\tflag: `p`,\n\t\t\trequired: false,\n\t\t\tdescription: `Port to run the flightdeck server on.`,\n\t\t\texample: `--port=8080`,\n\t\t\tparse: parseNumberOption,\n\t\t},\n\t\tpackageName: {\n\t\t\tflag: `n`,\n\t\t\trequired: true,\n\t\t\tdescription: `Name of the package.`,\n\t\t\texample: `--packageName=\"my-app\"`,\n\t\t},\n\t\tservices: {\n\t\t\tflag: `s`,\n\t\t\trequired: true,\n\t\t\tdescription: `Map of service names to executables.`,\n\t\t\texample: `--services=\"{\\\\\"frontend\\\\\":{\\\\\"run\\\\\":\\\\\"./frontend\\\\\",\\\\\"waitFor\\\\\":false},\\\\\"backend\\\\\":{\\\\\"run\\\\\":\\\\\"./backend\\\\\",\\\\\"waitFor\\\\\":true}}\"`,\n\t\t\tparse: JSON.parse,\n\t\t},\n\t\tflightdeckRootDir: {\n\t\t\tflag: `d`,\n\t\t\trequired: true,\n\t\t\tdescription: `Directory where the service is stored.`,\n\t\t\texample: `--flightdeckRootDir=\"./services/sample/repo/my-app/current\"`,\n\t\t},\n\t\tscripts: {\n\t\t\tflag: `r`,\n\t\t\trequired: true,\n\t\t\tdescription: `Map of scripts to run.`,\n\t\t\texample: `--scripts=\"{\\\\\"download\\\\\":\\\\\"npm i\",\\\\\"install\\\\\":\\\\\"npm run build\\\\\"}\"`,\n\t\t\tparse: JSON.parse,\n\t\t},\n\t\tjsonLogging: {\n\t\t\tflag: `j`,\n\t\t\trequired: false,\n\t\t\tdescription: `Enable json logging.`,\n\t\t\texample: `--jsonLogging`,\n\t\t\tparse: parseBooleanOption,\n\t\t},\n\t},\n) satisfies OptionsGroup<FlightDeckOptions>\n\nconst KILL_MANUAL = options(\n\t`Kill the FlightDeck process.`,\n\ttype({ flightdeckRootDir: `string`, packageName: `string` }),\n\t{\n\t\tflightdeckRootDir: {\n\t\t\tflag: `d`,\n\t\t\trequired: true,\n\t\t\tdescription: `Directory where the service is stored.`,\n\t\t\texample: `--flightdeckRootDir=\"./services/sample/repo/my-app/current\"`,\n\t\t},\n\t\tpackageName: {\n\t\t\tflag: `n`,\n\t\t\trequired: true,\n\t\t\tdescription: `Name of the package.`,\n\t\t\texample: `--packageName=\"my-app\"`,\n\t\t},\n\t},\n)\n\nconst SCHEMA_MANUAL = options(\n\t`Write a json schema for the FlightDeck options.`,\n\ttype({ \"outdir?\": `string` }),\n\t{\n\t\toutdir: {\n\t\t\tflag: `o`,\n\t\t\trequired: false,\n\t\t\tdescription: `Directory to write the schema to.`,\n\t\t\texample: `--outdir=./dist`,\n\t\t},\n\t},\n) satisfies OptionsGroup<{ outdir?: string | undefined }>\n\nconst parse = cli(\n\t{\n\t\tcliName: `flightdeck`,\n\t\troutes: optional({ schema: null, $configPath: null }),\n\t\trouteOptions: {\n\t\t\t\"\": FLIGHTDECK_MANUAL,\n\t\t\t$configPath: FLIGHTDECK_MANUAL,\n\t\t\tkill: KILL_MANUAL,\n\t\t\tschema: SCHEMA_MANUAL,\n\t\t},\n\t\tdebugOutput: true,\n\t\tdiscoverConfigPath: (args) => {\n\t\t\tif (args[0] === `schema`) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst configPath =\n\t\t\t\targs[0] ?? path.join(process.cwd(), `flightdeck.config.json`)\n\t\t\treturn configPath\n\t\t},\n\t},\n\tconsole,\n)\n\nconst { inputs, writeJsonSchema } = parse(process.argv)\n\nswitch (inputs.case) {\n\tcase `schema`:\n\t\t{\n\t\t\tconst { outdir } = inputs.opts\n\t\t\twriteJsonSchema(outdir ?? `.`)\n\t\t}\n\t\tbreak\n\tcase `kill`:\n\t\t{\n\t\t\tconst { flightdeckRootDir, packageName } = inputs.opts\n\t\t\tawait FlightDeck.kill(flightdeckRootDir, packageName)\n\t\t}\n\t\tbreak\n\tcase ``:\n\tcase `$configPath`: {\n\t\tnew FlightDeck(inputs.opts)\n\t}\n}\n"],"mappings":";;;;;;;AAiBA,MAAM,aAAa,IAAI,iBAAiB,WAAW,QAAQ,KAAK,QAAW,EAC1E,aAAa,MACb,CAAC;AACF,OAAO,OAAO,SAAS;CACtB,KAAK,WAAW,KAAK,KAAK,WAAW;CACrC,MAAM,WAAW,KAAK,KAAK,WAAW;CACtC,MAAM,WAAW,KAAK,KAAK,WAAW;CACtC,OAAO,WAAW,MAAM,KAAK,WAAW;CACxC,CAAC;AAEF,MAAM,oBAAoB,QACzB,uCACA,KAAK;CACJ,SAAS;CACT,aAAa;CACb,UAAU,EAAE,YAAY;EAAE,KAAK;EAAU,SAAS;EAAW,EAAE;CAC/D,mBAAmB;CACnB,SAAS;EACR,UAAU;EACV,SAAS;EACT,mBAAmB;EACnB;CACD,gBAAgB;CAChB,CAAC,EACF;CACC,MAAM;EACL,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT,OAAO;EACP;CACD,aAAa;EACZ,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT;CACD,UAAU;EACT,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT,OAAO,KAAK;EACZ;CACD,mBAAmB;EAClB,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT;CACD,SAAS;EACR,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT,OAAO,KAAK;EACZ;CACD,aAAa;EACZ,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT,OAAO;EACP;CACD,CACD;AAED,MAAM,cAAc,QACnB,gCACA,KAAK;CAAE,mBAAmB;CAAU,aAAa;CAAU,CAAC,EAC5D;CACC,mBAAmB;EAClB,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT;CACD,aAAa;EACZ,MAAM;EACN,UAAU;EACV,aAAa;EACb,SAAS;EACT;CACD,CACD;AAED,MAAM,gBAAgB,QACrB,mDACA,KAAK,EAAE,WAAW,UAAU,CAAC,EAC7B,EACC,QAAQ;CACP,MAAM;CACN,UAAU;CACV,aAAa;CACb,SAAS;CACT,EACD,CACD;AAyBD,MAAM,EAAE,QAAQ,oBAvBF,IACb;CACC,SAAS;CACT,QAAQ,SAAS;EAAE,QAAQ;EAAM,aAAa;EAAM,CAAC;CACrD,cAAc;EACb,IAAI;EACJ,aAAa;EACb,MAAM;EACN,QAAQ;EACR;CACD,aAAa;CACb,qBAAqB,SAAS;AAC7B,MAAI,KAAK,OAAO,SACf;AAID,SADC,KAAK,MAAM,KAAK,KAAK,QAAQ,KAAK,EAAE,yBAAyB;;CAG/D,EACD,QACA,CAEyC,QAAQ,KAAK;AAEvD,QAAQ,OAAO,MAAf;CACC,KAAK;EACJ;GACC,MAAM,EAAE,WAAW,OAAO;AAC1B,mBAAgB,UAAU,IAAI;;AAE/B;CACD,KAAK;EACJ;GACC,MAAM,EAAE,mBAAmB,gBAAgB,OAAO;AAClD,SAAM,WAAW,KAAK,mBAAmB,YAAY;;AAEtD;CACD,KAAK;CACL,KAAK,cACJ,KAAI,WAAW,OAAO,KAAK"}
|
package/dist/klaxon.x.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"klaxon.x.js","names":["changesetsPublishedPackagesSchema: Type<Klaxon.ScrambleOptions>","Klaxon.scramble"],"sources":["../src/klaxon.x.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { type Type, type } from \"arktype\"\nimport { cli, options, required } from \"comline\"\n\nimport * as Klaxon from \"./klaxon.lib\"\n\nconst changesetsPublishedPackagesSchema: Type<Klaxon.ScrambleOptions> = type({\n\tpackageConfig: { \"[string]\": { endpoint: `string` } },\n\tsecretsConfig: { \"[string]\": `string` },\n\tpublishedPackages: [{ name: `string`, version: `string` }, `[]`],\n})\n\nconst klaxon = cli({\n\tcliName: `klaxon`,\n\troutes: required({\n\t\tscramble: null,\n\t}),\n\trouteOptions: {\n\t\tscramble: options(``, changesetsPublishedPackagesSchema, {\n\t\t\tpackageConfig: {\n\t\t\t\tdescription: `Maps the names of your packages to the endpoints that klaxon will POST to.`,\n\t\t\t\texample: `--packageConfig=\"{\\\\\"my-app\\\\\":{\\\\\"endpoint\\\\\":\\\\\"https://my-app.com\\\\\"}}\"`,\n\t\t\t\tflag: `c`,\n\t\t\t\tparse: JSON.parse,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tsecretsConfig: {\n\t\t\t\tdescription: `Maps the names of your packages to the secrets that klaxon will use to authenticate with their respective endpoints.`,\n\t\t\t\texample: `--secretsConfig=\"{\\\\\"my-app\\\\\":\\\\\"XXXX-XXXX-XXXX\\\\\"}\"`,\n\t\t\t\tflag: `s`,\n\t\t\t\tparse: JSON.parse,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tpublishedPackages: {\n\t\t\t\tdescription: `The output of the \"Publish\" step in Changesets.`,\n\t\t\t\texample: `--publishedPackages=\"[{\\\\\"name\\\\\":\\\\\"my-app\\\\\",\\\\\"version\\\\\":\\\\\"0.0.0\\\\\"}]\"`,\n\t\t\t\tflag: `p`,\n\t\t\t\tparse: JSON.parse,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t}),\n\t},\n})\n\nconst { inputs } = klaxon(process.argv)\nawait Klaxon.scramble(inputs.opts).then((scrambleResult) => {\n\tconsole.log(scrambleResult)\n})\n"],"mappings":";;;;;;AAOA,MAAMA,oCAAkE,KAAK;CAC5E,eAAe,EAAE,YAAY,EAAE,UAAU,UAAU,EAAE;CACrD,eAAe,EAAE,YAAY,UAAU;CACvC,mBAAmB,CAAC;EAAE,MAAM;EAAU,SAAS;EAAU,EAAE,KAAK;CAChE,CAAC;AAkCF,MAAM,EAAE,WAhCO,IAAI;CAClB,SAAS;CACT,QAAQ,SAAS,EAChB,UAAU,MACV,CAAC;CACF,cAAc,EACb,UAAU,QAAQ,IAAI,mCAAmC;EACxD,eAAe;GACd,aAAa;GACb,SAAS;GACT,MAAM;GACN,OAAO,KAAK;GACZ,UAAU;GACV;EACD,eAAe;GACd,aAAa;GACb,SAAS;GACT,MAAM;GACN,OAAO,KAAK;GACZ,UAAU;GACV;EACD,mBAAmB;GAClB,aAAa;GACb,SAAS;GACT,MAAM;GACN,OAAO,KAAK;GACZ,UAAU;GACV;EACD,CAAC,EACF;CACD,CAAC,CAEwB,QAAQ,KAAK;AACvC,MAAMC,SAAgB,OAAO,KAAK,CAAC,MAAM,mBAAmB;AAC3D,SAAQ,IAAI,eAAe;EAC1B"}
|
|
1
|
+
{"version":3,"file":"klaxon.x.js","names":["changesetsPublishedPackagesSchema: Type<Klaxon.ScrambleOptions>","Klaxon.scramble"],"sources":["../src/klaxon.x.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { type Type, type } from \"arktype\"\nimport { cli, options, required } from \"comline\"\n\nimport * as Klaxon from \"./klaxon.lib.ts\"\n\nconst changesetsPublishedPackagesSchema: Type<Klaxon.ScrambleOptions> = type({\n\tpackageConfig: { \"[string]\": { endpoint: `string` } },\n\tsecretsConfig: { \"[string]\": `string` },\n\tpublishedPackages: [{ name: `string`, version: `string` }, `[]`],\n})\n\nconst klaxon = cli({\n\tcliName: `klaxon`,\n\troutes: required({\n\t\tscramble: null,\n\t}),\n\trouteOptions: {\n\t\tscramble: options(``, changesetsPublishedPackagesSchema, {\n\t\t\tpackageConfig: {\n\t\t\t\tdescription: `Maps the names of your packages to the endpoints that klaxon will POST to.`,\n\t\t\t\texample: `--packageConfig=\"{\\\\\"my-app\\\\\":{\\\\\"endpoint\\\\\":\\\\\"https://my-app.com\\\\\"}}\"`,\n\t\t\t\tflag: `c`,\n\t\t\t\tparse: JSON.parse,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tsecretsConfig: {\n\t\t\t\tdescription: `Maps the names of your packages to the secrets that klaxon will use to authenticate with their respective endpoints.`,\n\t\t\t\texample: `--secretsConfig=\"{\\\\\"my-app\\\\\":\\\\\"XXXX-XXXX-XXXX\\\\\"}\"`,\n\t\t\t\tflag: `s`,\n\t\t\t\tparse: JSON.parse,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\tpublishedPackages: {\n\t\t\t\tdescription: `The output of the \"Publish\" step in Changesets.`,\n\t\t\t\texample: `--publishedPackages=\"[{\\\\\"name\\\\\":\\\\\"my-app\\\\\",\\\\\"version\\\\\":\\\\\"0.0.0\\\\\"}]\"`,\n\t\t\t\tflag: `p`,\n\t\t\t\tparse: JSON.parse,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t}),\n\t},\n})\n\nconst { inputs } = klaxon(process.argv)\nawait Klaxon.scramble(inputs.opts).then((scrambleResult) => {\n\tconsole.log(scrambleResult)\n})\n"],"mappings":";;;;;;AAOA,MAAMA,oCAAkE,KAAK;CAC5E,eAAe,EAAE,YAAY,EAAE,UAAU,UAAU,EAAE;CACrD,eAAe,EAAE,YAAY,UAAU;CACvC,mBAAmB,CAAC;EAAE,MAAM;EAAU,SAAS;EAAU,EAAE,KAAK;CAChE,CAAC;AAkCF,MAAM,EAAE,WAhCO,IAAI;CAClB,SAAS;CACT,QAAQ,SAAS,EAChB,UAAU,MACV,CAAC;CACF,cAAc,EACb,UAAU,QAAQ,IAAI,mCAAmC;EACxD,eAAe;GACd,aAAa;GACb,SAAS;GACT,MAAM;GACN,OAAO,KAAK;GACZ,UAAU;GACV;EACD,eAAe;GACd,aAAa;GACb,SAAS;GACT,MAAM;GACN,OAAO,KAAK;GACZ,UAAU;GACV;EACD,mBAAmB;GAClB,aAAa;GACb,SAAS;GACT,MAAM;GACN,OAAO,KAAK;GACZ,UAAU;GACV;EACD,CAAC,EACF;CACD,CAAC,CAEwB,QAAQ,KAAK;AACvC,MAAMC,SAAgB,OAAO,KAAK,CAAC,MAAM,mBAAmB;AAC3D,SAAQ,IAAI,eAAe;EAC1B"}
|
package/dist/lib.d.ts
CHANGED
|
@@ -790,29 +790,32 @@ type FlightDeckSetupPhase = (typeof FLIGHTDECK_SETUP_PHASES)[number];
|
|
|
790
790
|
declare const FLIGHTDECK_UPDATE_PHASES: readonly ["notified", "confirmed"];
|
|
791
791
|
type FlightDeckUpdatePhase = (typeof FLIGHTDECK_UPDATE_PHASES)[number];
|
|
792
792
|
declare function isVersionNumber(version: string): boolean;
|
|
793
|
+
type FlightDeckSaveData = {
|
|
794
|
+
currentPid: `${number}`;
|
|
795
|
+
setupPhase: FlightDeckSetupPhase;
|
|
796
|
+
updatePhase: FlightDeckUpdatePhase;
|
|
797
|
+
updateAwaitedVersion: string;
|
|
798
|
+
};
|
|
793
799
|
type FlightDeckOptions<S extends string = string> = {
|
|
794
|
-
packageName: string;
|
|
795
|
-
services: { [service in S]: {
|
|
800
|
+
readonly packageName: string;
|
|
801
|
+
readonly services: { [service in S]: {
|
|
796
802
|
run: string;
|
|
797
803
|
waitFor: boolean;
|
|
798
804
|
} };
|
|
799
|
-
scripts: {
|
|
800
|
-
download: string;
|
|
801
|
-
install: string;
|
|
802
|
-
checkAvailability?: string;
|
|
805
|
+
readonly scripts: {
|
|
806
|
+
readonly download: string;
|
|
807
|
+
readonly install: string;
|
|
808
|
+
readonly checkAvailability?: string;
|
|
803
809
|
};
|
|
804
|
-
port?: number | undefined;
|
|
805
|
-
flightdeckRootDir?: string | undefined;
|
|
806
|
-
jsonLogging?: boolean | undefined;
|
|
810
|
+
readonly port?: number | undefined;
|
|
811
|
+
readonly flightdeckRootDir?: string | undefined;
|
|
812
|
+
readonly jsonLogging?: boolean | undefined;
|
|
807
813
|
};
|
|
808
814
|
declare class FlightDeck<S extends string = string> {
|
|
815
|
+
static kill(flightdeckRootDir: string, packageName: string): Promise<number>;
|
|
809
816
|
readonly options: FlightDeckOptions<S>;
|
|
810
817
|
protected safety: number;
|
|
811
|
-
protected storage: FilesystemStorage<
|
|
812
|
-
setupPhase: FlightDeckSetupPhase;
|
|
813
|
-
updatePhase: FlightDeckUpdatePhase;
|
|
814
|
-
updateAwaitedVersion: string;
|
|
815
|
-
}>;
|
|
818
|
+
protected storage: FilesystemStorage<FlightDeckSaveData>;
|
|
816
819
|
protected services: { [service in S]: ChildSocket<{
|
|
817
820
|
timeToStop: [];
|
|
818
821
|
updatesReady: [];
|
|
@@ -982,5 +985,5 @@ declare function scramble<K$1 extends string = string>({
|
|
|
982
985
|
publishedPackages
|
|
983
986
|
}: ScrambleOptions<K$1>): Promise<ScrambleResult<K$1>>;
|
|
984
987
|
//#endregion
|
|
985
|
-
export { FLIGHTDECK_ERROR, FLIGHTDECK_INFO, FLIGHTDECK_LNAV_FORMAT, FLIGHTDECK_SETUP_PHASES, FLIGHTDECK_UPDATE_PHASES, FLIGHTDECK_WARN, FlightDeck, FlightDeckFormat, FlightDeckLog, FlightDeckLogger, FlightDeckOptions, FlightDeckSetupPhase, FlightDeckUpdatePhase, klaxon_lib_d_exports as Klaxon, LnavFormatBreakdown, LnavFormatValueDefinition, LnavFormatVisualComponent, MemberOf, isVersionNumber };
|
|
988
|
+
export { FLIGHTDECK_ERROR, FLIGHTDECK_INFO, FLIGHTDECK_LNAV_FORMAT, FLIGHTDECK_SETUP_PHASES, FLIGHTDECK_UPDATE_PHASES, FLIGHTDECK_WARN, FlightDeck, FlightDeckFormat, FlightDeckLog, FlightDeckLogger, FlightDeckOptions, FlightDeckSaveData, FlightDeckSetupPhase, FlightDeckUpdatePhase, klaxon_lib_d_exports as Klaxon, LnavFormatBreakdown, LnavFormatValueDefinition, LnavFormatVisualComponent, MemberOf, isVersionNumber };
|
|
986
989
|
//# sourceMappingURL=lib.d.ts.map
|
package/dist/lib.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","names":[],"sources":["../gen/lnav-format-schema.gen.ts","../src/flightdeck.lib.ts","../src/klaxon.lib.ts"],"sourcesContent":[],"mappings":";;;;;;;;cAEa,kBAAgB,CAAA,CAAA;;;;;;;;IAAhB,OAAA,CAAA,EAAA,MAihDqC,GAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAjhDrB,WAAA,CAAA,EAAA,MAAA,GAAA,SAAA;IAAA,KAAA,CAAA,EAAA,MAAA,GAAA,OAAA,GAAA,QAAA,GAAA,QAAA,GAAA,QAAA,GAAA,QAAA,GAAA,OAAA,GAAA,OAAA,GAAA,QAAA,GAAA,SAAA,GAAA,OAAA,GAAA,UAAA,GAAA,OAAA,GAAA,SAAA;IAmhDjB,KAAA,CAAA,EAAA;;;;ICngDC,OAAA,CAAA,EAAA,MAAA,GAAA,SAA8D;IAE/D,WAAA,CAAA,EAAA,MAAoB,GAAA,SAAW;IAE9B,KAAA,CAAA,EAAA,MAAA,GAAA,OAA6D,GAAA,QAAA,GAAA,QAAA,GAAA,QAAA,GAAA,QAAA,GAAA,OAAA,GAAA,OAAA,GAAA,QAAA,GAAA,SAAA,GAAA,OAAA,GAAA,UAAA,GAAA,OAAA,GAAA,SAAA;IAE9D,KAAA,CAAA,EAAA;MAEI,IAAA,CAAA,EAAA,MAAe,GAAA,SAAA;IAMnB,CAAA,EAAA,GAAA,SAAA;
|
|
1
|
+
{"version":3,"file":"lib.d.ts","names":[],"sources":["../gen/lnav-format-schema.gen.ts","../src/flightdeck.lib.ts","../src/klaxon.lib.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/lib.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FLIGHTDECK_UPDATE_PHASES, c as FlightDeckLogger, i as FLIGHTDECK_SETUP_PHASES, l as isVersionNumber, n as FLIGHTDECK_INFO, o as FLIGHTDECK_WARN, r as FLIGHTDECK_LNAV_FORMAT, s as FlightDeck, t as FLIGHTDECK_ERROR } from "./flightdeck.lib-
|
|
1
|
+
import { a as FLIGHTDECK_UPDATE_PHASES, c as FlightDeckLogger, i as FLIGHTDECK_SETUP_PHASES, l as isVersionNumber, n as FLIGHTDECK_INFO, o as FLIGHTDECK_WARN, r as FLIGHTDECK_LNAV_FORMAT, s as FlightDeck, t as FLIGHTDECK_ERROR } from "./flightdeck.lib-DneT-dY9.js";
|
|
2
2
|
import { t as klaxon_lib_exports } from "./klaxon.lib-C5FWJaIY.js";
|
|
3
3
|
|
|
4
4
|
export { FLIGHTDECK_ERROR, FLIGHTDECK_INFO, FLIGHTDECK_LNAV_FORMAT, FLIGHTDECK_SETUP_PHASES, FLIGHTDECK_UPDATE_PHASES, FLIGHTDECK_WARN, FlightDeck, FlightDeckLogger, klaxon_lib_exports as Klaxon, isVersionNumber };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flightdeck",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jeremy Banka",
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@biomejs/js-api": "4.0.0",
|
|
39
39
|
"@biomejs/wasm-nodejs": "2.3.4",
|
|
40
|
-
"@types/bun": "npm:bun-types@1.3.
|
|
40
|
+
"@types/bun": "npm:bun-types@1.3.2",
|
|
41
41
|
"@types/node": "24.10.0",
|
|
42
42
|
"@types/tmp": "0.2.6",
|
|
43
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
43
|
+
"@typescript/native-preview": "7.0.0-dev.20251108.1",
|
|
44
44
|
"concurrently": "9.2.1",
|
|
45
45
|
"eslint": "9.39.1",
|
|
46
46
|
"json-schema-to-zod": "2.6.1",
|
|
47
47
|
"rimraf": "6.1.0",
|
|
48
48
|
"tmp": "0.2.5",
|
|
49
|
-
"tsdown": "0.16.
|
|
50
|
-
"vitest": "4.0.
|
|
49
|
+
"tsdown": "0.16.1",
|
|
50
|
+
"vitest": "4.0.8",
|
|
51
51
|
"zod": "4.1.12",
|
|
52
52
|
"varmint": "0.5.11"
|
|
53
53
|
},
|
package/src/flightdeck.lib.ts
CHANGED
|
@@ -13,8 +13,8 @@ import { ChildSocket } from "atom.io/realtime-server"
|
|
|
13
13
|
import { CronJob } from "cron"
|
|
14
14
|
import { FilesystemStorage } from "safedeposit"
|
|
15
15
|
|
|
16
|
-
import type { LnavFormat } from "../gen/lnav-format-schema.gen"
|
|
17
|
-
import { env } from "./flightdeck.env"
|
|
16
|
+
import type { LnavFormat } from "../gen/lnav-format-schema.gen.ts"
|
|
17
|
+
import { env } from "./flightdeck.env.ts"
|
|
18
18
|
|
|
19
19
|
export const FLIGHTDECK_SETUP_PHASES = [`downloaded`, `installed`] as const
|
|
20
20
|
|
|
@@ -30,28 +30,95 @@ export function isVersionNumber(version: string): boolean {
|
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export type FlightDeckSaveData = {
|
|
34
|
+
currentPid: `${number}`
|
|
35
|
+
setupPhase: FlightDeckSetupPhase
|
|
36
|
+
updatePhase: FlightDeckUpdatePhase
|
|
37
|
+
updateAwaitedVersion: string
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
export type FlightDeckOptions<S extends string = string> = {
|
|
34
|
-
packageName: string
|
|
35
|
-
services: { [service in S]: { run: string; waitFor: boolean } }
|
|
36
|
-
scripts: {
|
|
37
|
-
download: string
|
|
38
|
-
install: string
|
|
39
|
-
checkAvailability?: string
|
|
41
|
+
readonly packageName: string
|
|
42
|
+
readonly services: { [service in S]: { run: string; waitFor: boolean } }
|
|
43
|
+
readonly scripts: {
|
|
44
|
+
readonly download: string
|
|
45
|
+
readonly install: string
|
|
46
|
+
readonly checkAvailability?: string
|
|
40
47
|
}
|
|
41
|
-
port?: number | undefined
|
|
42
|
-
flightdeckRootDir?: string | undefined
|
|
43
|
-
jsonLogging?: boolean | undefined
|
|
48
|
+
readonly port?: number | undefined
|
|
49
|
+
readonly flightdeckRootDir?: string | undefined
|
|
50
|
+
readonly jsonLogging?: boolean | undefined
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function waitForPidExit(pid: number, timeoutMs = 5000, intervalMs = 100) {
|
|
54
|
+
return new Promise<void>((pass, fail) => {
|
|
55
|
+
const start = Date.now()
|
|
56
|
+
|
|
57
|
+
const check = () => {
|
|
58
|
+
process.stdout.write(`.`)
|
|
59
|
+
try {
|
|
60
|
+
// check existence
|
|
61
|
+
process.kill(pid, 0)
|
|
62
|
+
// process still alive
|
|
63
|
+
if (Date.now() - start > timeoutMs) {
|
|
64
|
+
fail(new Error(`Timeout waiting for PID ${pid}`))
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
setTimeout(check, intervalMs)
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (err instanceof Error && `code` in err) {
|
|
70
|
+
if (err.code === `ESRCH`) {
|
|
71
|
+
pass()
|
|
72
|
+
return // exited
|
|
73
|
+
}
|
|
74
|
+
if (err.code === `EPERM`) {
|
|
75
|
+
// process is alive but protected
|
|
76
|
+
if (Date.now() - start > timeoutMs) {
|
|
77
|
+
fail(
|
|
78
|
+
new Error(`Timeout waiting for PID ${pid} (EPERM; still alive)`),
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
return setTimeout(check, intervalMs)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
fail(err)
|
|
86
|
+
return // unexpected error
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
check()
|
|
91
|
+
})
|
|
44
92
|
}
|
|
45
93
|
|
|
46
94
|
export class FlightDeck<S extends string = string> {
|
|
95
|
+
public static async kill(
|
|
96
|
+
flightdeckRootDir: string,
|
|
97
|
+
packageName: string,
|
|
98
|
+
): Promise<number> {
|
|
99
|
+
console.info(`Killing FlightDeck instance of "${packageName}"`)
|
|
100
|
+
const storage = new FilesystemStorage<FlightDeckSaveData>({
|
|
101
|
+
path: resolve(flightdeckRootDir, `storage`, packageName),
|
|
102
|
+
})
|
|
103
|
+
const currentPid = storage.getItem(`currentPid`)
|
|
104
|
+
if (currentPid === null) {
|
|
105
|
+
throw new Error(`No pid for "${packageName}" found in storage`)
|
|
106
|
+
}
|
|
107
|
+
const pid = Number(currentPid)
|
|
108
|
+
const now = Date.now()
|
|
109
|
+
process.kill(pid, `SIGTERM`)
|
|
110
|
+
await waitForPidExit(pid, 5000, 5)
|
|
111
|
+
const elapsed = Date.now() - now
|
|
112
|
+
process.stdout.write(
|
|
113
|
+
`🌜 "${packageName}" (running as process ${pid}) exited in ${elapsed}ms\n`,
|
|
114
|
+
)
|
|
115
|
+
return pid
|
|
116
|
+
}
|
|
117
|
+
|
|
47
118
|
public readonly options: FlightDeckOptions<S>
|
|
48
119
|
protected safety = 0
|
|
49
120
|
|
|
50
|
-
protected storage: FilesystemStorage<
|
|
51
|
-
setupPhase: FlightDeckSetupPhase
|
|
52
|
-
updatePhase: FlightDeckUpdatePhase
|
|
53
|
-
updateAwaitedVersion: string
|
|
54
|
-
}>
|
|
121
|
+
protected storage: FilesystemStorage<FlightDeckSaveData>
|
|
55
122
|
protected services: {
|
|
56
123
|
[service in S]: ChildSocket<
|
|
57
124
|
{ timeToStop: []; updatesReady: [] },
|
|
@@ -128,6 +195,8 @@ export class FlightDeck<S extends string = string> {
|
|
|
128
195
|
path: resolve(flightdeckRootDir, `storage`, options.packageName),
|
|
129
196
|
})
|
|
130
197
|
|
|
198
|
+
this.storage.setItem(`currentPid`, `${process.pid}`)
|
|
199
|
+
|
|
131
200
|
if (FLIGHTDECK_SECRET === undefined) {
|
|
132
201
|
this.logger.warn(
|
|
133
202
|
`No FLIGHTDECK_SECRET environment variable found. FlightDeck will not run an update server.`,
|
|
@@ -205,6 +274,13 @@ export class FlightDeck<S extends string = string> {
|
|
|
205
274
|
this.logger.error(`Failed to start all services:`, thrown.message)
|
|
206
275
|
}
|
|
207
276
|
})
|
|
277
|
+
|
|
278
|
+
process.on(`SIGTERM`, async () => {
|
|
279
|
+
console.info(`Killed by SIGTERM`)
|
|
280
|
+
await this.stopAllServices()
|
|
281
|
+
this.storage.removeItem(`currentPid`)
|
|
282
|
+
process.exit(0)
|
|
283
|
+
})
|
|
208
284
|
}
|
|
209
285
|
|
|
210
286
|
protected async seekUpdate(version: string): Promise<void> {
|
|
@@ -338,7 +414,7 @@ export class FlightDeck<S extends string = string> {
|
|
|
338
414
|
)
|
|
339
415
|
this.services[serviceName] = null
|
|
340
416
|
if (!this.autoRespawnDeadServices) {
|
|
341
|
-
this.logger.info(
|
|
417
|
+
this.logger.info(`😴 Auto-respawn is off; "${serviceName}" rests.`)
|
|
342
418
|
return
|
|
343
419
|
}
|
|
344
420
|
const updatePhase = this.storage.getItem(`updatePhase`)
|
package/src/flightdeck.x.ts
CHANGED
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
parseNumberOption,
|
|
13
13
|
} from "comline"
|
|
14
14
|
|
|
15
|
-
import type { FlightDeckOptions } from "./flightdeck.lib"
|
|
16
|
-
import { FlightDeck, FlightDeckLogger } from "./flightdeck.lib"
|
|
15
|
+
import type { FlightDeckOptions } from "./flightdeck.lib.ts"
|
|
16
|
+
import { FlightDeck, FlightDeckLogger } from "./flightdeck.lib.ts"
|
|
17
17
|
|
|
18
18
|
const CLI_LOGGER = new FlightDeckLogger(`comline`, process.pid, undefined, {
|
|
19
19
|
jsonLogging: true,
|
|
@@ -83,6 +83,25 @@ const FLIGHTDECK_MANUAL = options(
|
|
|
83
83
|
},
|
|
84
84
|
) satisfies OptionsGroup<FlightDeckOptions>
|
|
85
85
|
|
|
86
|
+
const KILL_MANUAL = options(
|
|
87
|
+
`Kill the FlightDeck process.`,
|
|
88
|
+
type({ flightdeckRootDir: `string`, packageName: `string` }),
|
|
89
|
+
{
|
|
90
|
+
flightdeckRootDir: {
|
|
91
|
+
flag: `d`,
|
|
92
|
+
required: true,
|
|
93
|
+
description: `Directory where the service is stored.`,
|
|
94
|
+
example: `--flightdeckRootDir="./services/sample/repo/my-app/current"`,
|
|
95
|
+
},
|
|
96
|
+
packageName: {
|
|
97
|
+
flag: `n`,
|
|
98
|
+
required: true,
|
|
99
|
+
description: `Name of the package.`,
|
|
100
|
+
example: `--packageName="my-app"`,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
|
|
86
105
|
const SCHEMA_MANUAL = options(
|
|
87
106
|
`Write a json schema for the FlightDeck options.`,
|
|
88
107
|
type({ "outdir?": `string` }),
|
|
@@ -103,6 +122,7 @@ const parse = cli(
|
|
|
103
122
|
routeOptions: {
|
|
104
123
|
"": FLIGHTDECK_MANUAL,
|
|
105
124
|
$configPath: FLIGHTDECK_MANUAL,
|
|
125
|
+
kill: KILL_MANUAL,
|
|
106
126
|
schema: SCHEMA_MANUAL,
|
|
107
127
|
},
|
|
108
128
|
debugOutput: true,
|
|
@@ -127,11 +147,14 @@ switch (inputs.case) {
|
|
|
127
147
|
writeJsonSchema(outdir ?? `.`)
|
|
128
148
|
}
|
|
129
149
|
break
|
|
150
|
+
case `kill`:
|
|
151
|
+
{
|
|
152
|
+
const { flightdeckRootDir, packageName } = inputs.opts
|
|
153
|
+
await FlightDeck.kill(flightdeckRootDir, packageName)
|
|
154
|
+
}
|
|
155
|
+
break
|
|
130
156
|
case ``:
|
|
131
157
|
case `$configPath`: {
|
|
132
|
-
|
|
133
|
-
process.on(`close`, async () => {
|
|
134
|
-
await flightDeck.stopAllServices()
|
|
135
|
-
})
|
|
158
|
+
new FlightDeck(inputs.opts)
|
|
136
159
|
}
|
|
137
160
|
}
|
package/src/klaxon.x.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { type Type, type } from "arktype"
|
|
4
4
|
import { cli, options, required } from "comline"
|
|
5
5
|
|
|
6
|
-
import * as Klaxon from "./klaxon.lib"
|
|
6
|
+
import * as Klaxon from "./klaxon.lib.ts"
|
|
7
7
|
|
|
8
8
|
const changesetsPublishedPackagesSchema: Type<Klaxon.ScrambleOptions> = type({
|
|
9
9
|
packageConfig: { "[string]": { endpoint: `string` } },
|
package/src/lib.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"flightdeck.lib-DMlmcPLR.js","names":["data: Uint8Array[]","log: FlightDeckLog"],"sources":["../src/flightdeck.env.ts","../src/flightdeck.lib.ts"],"sourcesContent":["import { createEnv } from \"@t3-oss/env-core\"\nimport { type } from \"arktype\"\n\nexport const env = createEnv({\n\tserver: { FLIGHTDECK_SECRET: type(`string`, `|`, `undefined`) },\n\tclientPrefix: `NEVER`,\n\tclient: {},\n\truntimeEnv: import.meta.env as Record<string, string>,\n\temptyStringAsUndefined: true,\n})\n","/* eslint-disable @typescript-eslint/only-throw-error */\nimport type { ChildProcessWithoutNullStreams } from \"node:child_process\"\nimport { execSync, spawn } from \"node:child_process\"\nimport { createServer } from \"node:http\"\nimport { homedir } from \"node:os\"\nimport { resolve } from \"node:path\"\nimport { inspect } from \"node:util\"\n\nimport { Future } from \"atom.io/internal\"\nimport { discoverType } from \"atom.io/introspection\"\nimport { fromEntries, toEntries } from \"atom.io/json\"\nimport { ChildSocket } from \"atom.io/realtime-server\"\nimport { CronJob } from \"cron\"\nimport { FilesystemStorage } from \"safedeposit\"\n\nimport type { LnavFormat } from \"../gen/lnav-format-schema.gen\"\nimport { env } from \"./flightdeck.env\"\n\nexport const FLIGHTDECK_SETUP_PHASES = [`downloaded`, `installed`] as const\n\nexport type FlightDeckSetupPhase = (typeof FLIGHTDECK_SETUP_PHASES)[number]\n\nexport const FLIGHTDECK_UPDATE_PHASES = [`notified`, `confirmed`] as const\n\nexport type FlightDeckUpdatePhase = (typeof FLIGHTDECK_UPDATE_PHASES)[number]\n\nexport function isVersionNumber(version: string): boolean {\n\treturn (\n\t\t/^\\d+\\.\\d+\\.\\d+$/.test(version) || !Number.isNaN(Number.parseFloat(version))\n\t)\n}\n\nexport type FlightDeckOptions<S extends string = string> = {\n\tpackageName: string\n\tservices: { [service in S]: { run: string; waitFor: boolean } }\n\tscripts: {\n\t\tdownload: string\n\t\tinstall: string\n\t\tcheckAvailability?: string\n\t}\n\tport?: number | undefined\n\tflightdeckRootDir?: string | undefined\n\tjsonLogging?: boolean | undefined\n}\n\nexport class FlightDeck<S extends string = string> {\n\tpublic readonly options: FlightDeckOptions<S>\n\tprotected safety = 0\n\n\tprotected storage: FilesystemStorage<{\n\t\tsetupPhase: FlightDeckSetupPhase\n\t\tupdatePhase: FlightDeckUpdatePhase\n\t\tupdateAwaitedVersion: string\n\t}>\n\tprotected services: {\n\t\t[service in S]: ChildSocket<\n\t\t\t{ timeToStop: []; updatesReady: [] },\n\t\t\t{ readyToUpdate: []; alive: [] },\n\t\t\tChildProcessWithoutNullStreams\n\t\t> | null\n\t}\n\tprotected serviceIdx: { readonly [service in S]: number }\n\tpublic defaultServicesReadyToUpdate: { readonly [service in S]: boolean }\n\tpublic servicesReadyToUpdate: { [service in S]: boolean }\n\tpublic autoRespawnDeadServices: boolean\n\n\tprotected logger: Pick<Console, `error` | `info` | `warn`>\n\tprotected serviceLoggers: {\n\t\treadonly [service in S]: FlightDeckLogger\n\t}\n\n\tprotected updateAvailabilityChecker: CronJob | null = null\n\n\tpublic servicesLive: Future<void>[]\n\tpublic servicesDead: Future<void>[]\n\tpublic live = new Future(() => {})\n\tpublic dead = new Future(() => {})\n\n\tprotected restartTimes: number[] = []\n\n\tpublic constructor(options: FlightDeckOptions<S>) {\n\t\tthis.options = options\n\t\tconst { FLIGHTDECK_SECRET } = env\n\t\tconst { flightdeckRootDir = resolve(homedir(), `.flightdeck`) } = options\n\t\tconst port = options.port ?? 8080\n\t\tconst origin = `http://localhost:${port}`\n\n\t\tconst servicesEntries = toEntries(options.services)\n\t\tthis.services = fromEntries(\n\t\t\tservicesEntries.map(([serviceName]) => [serviceName, null]),\n\t\t)\n\t\tthis.serviceIdx = fromEntries(\n\t\t\tservicesEntries.map(([serviceName], idx) => [serviceName, idx]),\n\t\t)\n\t\tthis.defaultServicesReadyToUpdate = fromEntries(\n\t\t\tservicesEntries.map(([serviceName, { waitFor }]) => [\n\t\t\t\tserviceName,\n\t\t\t\t!waitFor,\n\t\t\t]),\n\t\t)\n\t\tthis.servicesReadyToUpdate = { ...this.defaultServicesReadyToUpdate }\n\t\tthis.autoRespawnDeadServices = true\n\n\t\tthis.logger = new FlightDeckLogger(\n\t\t\tthis.options.packageName,\n\t\t\tprocess.pid,\n\t\t\tundefined,\n\t\t\t{ jsonLogging: this.options.jsonLogging ?? false },\n\t\t)\n\t\tthis.serviceLoggers = fromEntries(\n\t\t\tservicesEntries.map(([serviceName]) => [\n\t\t\t\tserviceName,\n\t\t\t\tnew FlightDeckLogger(\n\t\t\t\t\tthis.options.packageName,\n\t\t\t\t\tprocess.pid,\n\t\t\t\t\tserviceName,\n\t\t\t\t\t{ jsonLogging: this.options.jsonLogging ?? false },\n\t\t\t\t),\n\t\t\t]),\n\t\t)\n\n\t\tthis.servicesLive = servicesEntries.map(() => new Future(() => {}))\n\t\tthis.servicesDead = servicesEntries.map(() => new Future(() => {}))\n\t\tthis.live.use(Promise.all(this.servicesLive))\n\t\tthis.dead.use(Promise.all(this.servicesDead))\n\n\t\tthis.storage = new FilesystemStorage({\n\t\t\tpath: resolve(flightdeckRootDir, `storage`, options.packageName),\n\t\t})\n\n\t\tif (FLIGHTDECK_SECRET === undefined) {\n\t\t\tthis.logger.warn(\n\t\t\t\t`No FLIGHTDECK_SECRET environment variable found. FlightDeck will not run an update server.`,\n\t\t\t)\n\t\t} else {\n\t\t\tcreateServer((req, res) => {\n\t\t\t\tlet data: Uint8Array[] = []\n\t\t\t\treq\n\t\t\t\t\t.on(`data`, (chunk) => {\n\t\t\t\t\t\tdata.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk))\n\t\t\t\t\t})\n\t\t\t\t\t.on(`end`, async () => {\n\t\t\t\t\t\tconst authHeader = req.headers.authorization\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (typeof req.url === `undefined`) throw 400\n\t\t\t\t\t\t\tconst expectedAuthHeader = `Bearer ${FLIGHTDECK_SECRET}`\n\t\t\t\t\t\t\tif (authHeader !== `Bearer ${FLIGHTDECK_SECRET}`) {\n\t\t\t\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t\t\t\t`Unauthorized: needed \\`${expectedAuthHeader}\\`, got \\`${authHeader}\\``,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tthrow 401\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst url = new URL(req.url, origin)\n\t\t\t\t\t\t\tthis.logger.info(req.method, url.pathname)\n\n\t\t\t\t\t\t\tconst versionForeignInput = Buffer.concat(data).toString()\n\t\t\t\t\t\t\tif (!isVersionNumber(versionForeignInput)) {\n\t\t\t\t\t\t\t\tthrow 400\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tres.writeHead(200)\n\t\t\t\t\t\t\tres.end()\n\n\t\t\t\t\t\t\tthis.storage.setItem(`updatePhase`, `notified`)\n\t\t\t\t\t\t\tthis.storage.setItem(`updateAwaitedVersion`, versionForeignInput)\n\t\t\t\t\t\t\tconst { checkAvailability } = options.scripts\n\t\t\t\t\t\t\tif (checkAvailability) {\n\t\t\t\t\t\t\t\tawait this.updateAvailabilityChecker?.stop()\n\t\t\t\t\t\t\t\tawait this.seekUpdate(versionForeignInput)\n\t\t\t\t\t\t\t\tconst updatePhase = this.storage.getItem(`updatePhase`)\n\t\t\t\t\t\t\t\tthis.logger.info(`> storage(\"updatePhase\") >`, updatePhase)\n\t\t\t\t\t\t\t\tif (updatePhase === `notified`) {\n\t\t\t\t\t\t\t\t\tthis.updateAvailabilityChecker = new CronJob(\n\t\t\t\t\t\t\t\t\t\t`*/30 * * * * *`,\n\t\t\t\t\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\t\t\t\t\tawait this.seekUpdate(versionForeignInput)\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tthis.updateAvailabilityChecker.start()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.downloadPackage()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (thrown) {\n\t\t\t\t\t\t\tthis.logger.error(thrown, req.url)\n\t\t\t\t\t\t\tif (typeof thrown === `number`) {\n\t\t\t\t\t\t\t\tres.writeHead(thrown)\n\t\t\t\t\t\t\t\tres.end()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tdata = []\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t}).listen(port, () => {\n\t\t\t\tthis.logger.info(`Server started on port ${port}`)\n\t\t\t})\n\t\t}\n\n\t\tthis.startAllServices()\n\t\t\t.then(() => {\n\t\t\t\tthis.logger.info(`All services started.`)\n\t\t\t})\n\t\t\t.catch((thrown) => {\n\t\t\t\tif (thrown instanceof Error) {\n\t\t\t\t\tthis.logger.error(`Failed to start all services:`, thrown.message)\n\t\t\t\t}\n\t\t\t})\n\t}\n\n\tprotected async seekUpdate(version: string): Promise<void> {\n\t\tthis.logger.info(`Checking for updates...`)\n\t\tconst { checkAvailability } = this.options.scripts\n\t\tif (!checkAvailability) {\n\t\t\tthis.logger.info(`No checkAvailability script found.`)\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tconst out = execSync(`${checkAvailability} ${version}`)\n\t\t\tthis.logger.info(`Check stdout:`, out.toString())\n\t\t\tawait this.updateAvailabilityChecker?.stop()\n\t\t\tthis.storage.setItem(`updatePhase`, `confirmed`)\n\t\t\tthis.downloadPackage()\n\t\t\tthis.announceUpdate()\n\t\t} catch (thrown) {\n\t\t\tif (thrown instanceof Error) {\n\t\t\t\tthis.logger.error(`Check failed:`, thrown.message)\n\t\t\t} else {\n\t\t\t\tconst thrownType = discoverType(thrown)\n\t\t\t\tthis.logger.error(`Check threw`, thrownType, thrown)\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected announceUpdate(): void {\n\t\tfor (const entry of toEntries(this.services)) {\n\t\t\tconst [serviceName, service] = entry\n\t\t\tif (service) {\n\t\t\t\tif (this.options.services[serviceName].waitFor) {\n\t\t\t\t\tservice.emit(`updatesReady`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.startService(serviceName)\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected tryUpdate(): void {\n\t\tif (toEntries(this.servicesReadyToUpdate).every(([, isReady]) => isReady)) {\n\t\t\tthis.logger.info(`All services are ready to update.`)\n\t\t\tthis.stopAllServices()\n\t\t\t\t.then(() => {\n\t\t\t\t\tthis.logger.info(`All services stopped; starting up fresh...`)\n\t\t\t\t\tthis.startAllServices()\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tthis.logger.info(`All services started; we're back online.`)\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((thrown) => {\n\t\t\t\t\t\t\tif (thrown instanceof Error) {\n\t\t\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\t\t`Failed to start all services:`,\n\t\t\t\t\t\t\t\t\tthrown.message,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\t.catch((thrown) => {\n\t\t\t\t\tif (thrown instanceof Error) {\n\t\t\t\t\t\tthis.logger.error(`Failed to stop all services:`, thrown.message)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t}\n\t}\n\n\tprotected startAllServices(): Future<unknown> {\n\t\tthis.logger.info(`Starting all services...`)\n\t\tthis.autoRespawnDeadServices = true\n\t\tconst setupPhase = this.storage.getItem(`setupPhase`)\n\t\tthis.logger.info(`> storage(\"setupPhase\") >`, setupPhase)\n\t\tswitch (setupPhase) {\n\t\t\tcase null:\n\t\t\t\tthis.logger.info(`Starting from scratch.`)\n\t\t\t\tthis.downloadPackage()\n\t\t\t\tthis.installPackage()\n\t\t\t\treturn this.startAllServices()\n\t\t\tcase `downloaded`:\n\t\t\t\tthis.logger.info(`Found package downloaded but not installed.`)\n\t\t\t\tthis.installPackage()\n\t\t\t\treturn this.startAllServices()\n\t\t\tcase `installed`: {\n\t\t\t\tfor (const [serviceName] of toEntries(this.services)) {\n\t\t\t\t\tthis.startService(serviceName)\n\t\t\t\t}\n\t\t\t\treturn this.live\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected startService(serviceName: S): void {\n\t\tthis.logger.info(\n\t\t\t`Starting service ${this.options.packageName}::${serviceName}, try ${this.safety}/2...`,\n\t\t)\n\t\tif (this.safety >= 2) {\n\t\t\tthrow new Error(`Out of tries...`)\n\t\t}\n\t\tthis.safety++\n\n\t\tconst [exe, ...args] = this.options.services[serviceName].run.split(` `)\n\t\tconst serviceProcess = spawn(exe, args, {\n\t\t\tcwd: this.options.flightdeckRootDir,\n\t\t\tenv: import.meta.env as Record<string, string>,\n\t\t})\n\t\tconst serviceLogger = this.serviceLoggers[serviceName]\n\t\tconst service = (this.services[serviceName] = new ChildSocket(\n\t\t\tserviceProcess,\n\t\t\t`${this.options.packageName}::${serviceName}`,\n\t\t\tserviceLogger,\n\t\t))\n\t\tserviceLogger.processCode = service.proc.pid ?? -1\n\t\tthis.services[serviceName].onAny((...messages) => {\n\t\t\tserviceLogger.info(`💬`, ...messages)\n\t\t})\n\t\tthis.services[serviceName].on(`readyToUpdate`, () => {\n\t\t\tthis.logger.info(`Service \"${serviceName}\" is ready to update.`)\n\t\t\tthis.servicesReadyToUpdate[serviceName] = true\n\t\t\tthis.tryUpdate()\n\t\t})\n\t\tthis.services[serviceName].on(`alive`, () => {\n\t\t\tthis.servicesLive[this.serviceIdx[serviceName]].use(Promise.resolve())\n\t\t\tthis.servicesDead[this.serviceIdx[serviceName]] = new Future(() => {})\n\t\t\tif (this.dead.done) {\n\t\t\t\tthis.dead = new Future(() => {})\n\t\t\t}\n\t\t\tthis.dead.use(Promise.all(this.servicesDead))\n\t\t})\n\t\tthis.services[serviceName].proc.once(`close`, (exitCode) => {\n\t\t\tthis.logger.info(\n\t\t\t\t`Auto-respawn saw \"${serviceName}\" exit with code ${exitCode}`,\n\t\t\t)\n\t\t\tthis.services[serviceName] = null\n\t\t\tif (!this.autoRespawnDeadServices) {\n\t\t\t\tthis.logger.info(`Auto-respawn is off; \"${serviceName}\" rests.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst updatePhase = this.storage.getItem(`updatePhase`)\n\t\t\tthis.logger.info(`> storage(\"updatePhase\") >`, updatePhase)\n\t\t\tconst updatesAreReady = updatePhase === `confirmed`\n\t\t\tif (updatesAreReady) {\n\t\t\t\tthis.serviceLoggers[serviceName].info(`Updating before startup...`)\n\t\t\t\tthis.restartTimes = []\n\t\t\t\tthis.installPackage()\n\t\t\t\tthis.startService(serviceName)\n\t\t\t} else {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tconst fiveMinutesAgo = now - 5 * 60 * 1000\n\t\t\t\tthis.restartTimes = this.restartTimes.filter(\n\t\t\t\t\t(time) => time > fiveMinutesAgo,\n\t\t\t\t)\n\t\t\t\tthis.restartTimes.push(now)\n\n\t\t\t\tif (this.restartTimes.length < 5) {\n\t\t\t\t\tthis.serviceLoggers[serviceName].info(`Crashed. Restarting...`)\n\t\t\t\t\tthis.startService(serviceName)\n\t\t\t\t} else {\n\t\t\t\t\tthis.serviceLoggers[serviceName].info(\n\t\t\t\t\t\t`Crashed 5 times in 5 minutes. Not restarting.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tthis.safety = 0\n\t}\n\n\tprotected downloadPackage(): void {\n\t\tthis.logger.info(`Downloading...`)\n\t\ttry {\n\t\t\tconst out = execSync(this.options.scripts.download)\n\t\t\tthis.logger.info(`Download stdout:`, out.toString())\n\t\t\tthis.storage.setItem(`setupPhase`, `downloaded`)\n\t\t\tthis.logger.info(`Downloaded!`)\n\t\t} catch (thrown) {\n\t\t\tif (thrown instanceof Error) {\n\t\t\t\tthis.logger.error(`Failed to get the latest release: ${thrown.message}`)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tprotected installPackage(): void {\n\t\tthis.logger.info(`Installing...`)\n\n\t\ttry {\n\t\t\tconst out = execSync(this.options.scripts.install)\n\t\t\tthis.logger.info(`Install stdout:`, out.toString())\n\t\t\tthis.storage.setItem(`setupPhase`, `installed`)\n\t\t\tthis.logger.info(`Installed!`)\n\t\t} catch (thrown) {\n\t\t\tif (thrown instanceof Error) {\n\t\t\t\tthis.logger.error(`Failed to get the latest release: ${thrown.message}`)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tpublic stopAllServices(): Future<unknown> {\n\t\tthis.logger.info(`Stopping all services... auto-respawn disabled.`)\n\t\tthis.autoRespawnDeadServices = false\n\t\tfor (const [serviceName] of toEntries(this.services)) {\n\t\t\tthis.stopService(serviceName)\n\t\t}\n\t\treturn this.dead\n\t}\n\n\tpublic stopService(serviceName: S): void {\n\t\tconst service = this.services[serviceName]\n\t\tif (service) {\n\t\t\tthis.logger.info(`Stopping service \"${serviceName}\"...`)\n\t\t\tthis.servicesDead[this.serviceIdx[serviceName]].use(\n\t\t\t\tnew Promise((pass) => {\n\t\t\t\t\tservice.emit(`timeToStop`)\n\t\t\t\t\tservice.proc.once(`close`, (exitCode) => {\n\t\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t\t`Stopped service \"${serviceName}\"; exited with code ${exitCode}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tthis.services[serviceName] = null\n\t\t\t\t\t\tpass()\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t)\n\t\t\tthis.dead.use(Promise.all(this.servicesDead))\n\t\t\tthis.servicesLive[this.serviceIdx[serviceName]] = new Future(() => {})\n\t\t\tif (this.live.done) {\n\t\t\t\tthis.live = new Future(() => {})\n\t\t\t}\n\t\t\tthis.live.use(Promise.all(this.servicesLive))\n\t\t} else {\n\t\t\tthis.serviceLoggers[serviceName].error(\n\t\t\t\t`Tried to stop service, but it wasn't running.`,\n\t\t\t)\n\t\t}\n\t}\n}\n\nexport const FLIGHTDECK_INFO = `info`\nexport const FLIGHTDECK_WARN = `warn`\nexport const FLIGHTDECK_ERROR = `ERR!`\n\nexport type FlightDeckLog = {\n\tlevel:\n\t\t| typeof FLIGHTDECK_ERROR\n\t\t| typeof FLIGHTDECK_INFO\n\t\t| typeof FLIGHTDECK_WARN\n\ttimestamp: number\n\tpackage: string\n\tservice?: string\n\tprocess: number\n\tbody: string\n}\n\nconst LINE_FORMAT = `line-format` satisfies keyof LnavFormat\nconst VALUE = `value` satisfies keyof LnavFormat\n\nexport type LnavFormatVisualComponent = Exclude<\n\tExclude<LnavFormat[`line-format`], undefined>[number],\n\tstring\n>\n\nexport type LnavFormatBreakdown = Exclude<LnavFormat[`value`], undefined>\nexport type MemberOf<T> = T[keyof T]\nexport type LnavFormatValueDefinition = MemberOf<LnavFormatBreakdown>\n\nexport type FlightDeckFormat = {\n\t[LINE_FORMAT]: (\n\t\t| string\n\t\t| (LnavFormatVisualComponent & {\n\t\t\t\tfield: keyof FlightDeckLog | `__level__` | `__timestamp__`\n\t\t })\n\t)[]\n\t[VALUE]: {\n\t\t[K in keyof FlightDeckLog]: LnavFormatValueDefinition & {\n\t\t\tkind: FlightDeckLog[K] extends number | undefined\n\t\t\t\t? `integer`\n\t\t\t\t: FlightDeckLog[K] extends string | undefined\n\t\t\t\t\t? `string`\n\t\t\t\t\t: never\n\t\t}\n\t}\n}\n\nexport const FLIGHTDECK_LNAV_FORMAT = {\n\ttitle: `FlightDeck Log`,\n\tdescription: `Format for events logged by the FlightDeck process manager.`,\n\t\"file-type\": `json`,\n\t\"timestamp-field\": `timestamp`,\n\t\"timestamp-divisor\": 1000,\n\t\"module-field\": `package`,\n\t\"opid-field\": `service`,\n\t\"level-field\": `level`,\n\tlevel: {\n\t\tinfo: FLIGHTDECK_INFO,\n\t\twarning: FLIGHTDECK_WARN,\n\t\terror: FLIGHTDECK_ERROR,\n\t},\n\n\t[LINE_FORMAT]: [\n\t\t{\n\t\t\tfield: `level`,\n\t\t},\n\t\t{\n\t\t\tprefix: ` `,\n\t\t\tfield: `__timestamp__`,\n\t\t\t\"timestamp-format\": `%Y-%m-%dT%H:%M:%S.%L%Z`,\n\t\t},\n\t\t{\n\t\t\tprefix: ` `,\n\t\t\tfield: `process`,\n\t\t\t\"min-width\": 5,\n\t\t},\n\t\t{\n\t\t\tprefix: `:`,\n\t\t\tfield: `package`,\n\t\t},\n\t\t{\n\t\t\tprefix: `:`,\n\t\t\tfield: `service`,\n\t\t\t\"default-value\": ``,\n\t\t},\n\t\t{\n\t\t\tprefix: `: `,\n\t\t\tfield: `body`,\n\t\t},\n\t],\n\n\t[VALUE]: {\n\t\ttimestamp: {\n\t\t\tkind: `integer`,\n\t\t},\n\t\tlevel: {\n\t\t\tkind: `string`,\n\t\t},\n\t\tpackage: {\n\t\t\tkind: `string`,\n\t\t},\n\t\tservice: {\n\t\t\tkind: `string`,\n\t\t},\n\t\tprocess: {\n\t\t\tkind: `integer`,\n\t\t},\n\t\tbody: {\n\t\t\tkind: `string`,\n\t\t},\n\t},\n} as const satisfies FlightDeckFormat & LnavFormat\n\nexport class FlightDeckLogger\n\timplements Pick<Console, `error` | `info` | `warn`>\n{\n\tpublic readonly packageName: string\n\tpublic readonly serviceName?: string\n\tpublic readonly jsonLogging: boolean\n\tpublic processCode: number\n\tpublic constructor(\n\t\tpackageName: string,\n\t\tprocessCode: number,\n\t\tserviceName?: string,\n\t\toptions?: { jsonLogging: boolean },\n\t) {\n\t\tthis.packageName = packageName\n\t\tif (serviceName) {\n\t\t\tthis.serviceName = serviceName\n\t\t}\n\t\tthis.processCode = processCode\n\t\tthis.jsonLogging = options?.jsonLogging ?? false\n\t}\n\tprotected log(\n\t\tlevel:\n\t\t\t| typeof FLIGHTDECK_ERROR\n\t\t\t| typeof FLIGHTDECK_INFO\n\t\t\t| typeof FLIGHTDECK_WARN,\n\t\t...messages: unknown[]\n\t): void {\n\t\tif (this.jsonLogging) {\n\t\t\tlet body = messages\n\t\t\t\t.map((message) =>\n\t\t\t\t\ttypeof message === `string`\n\t\t\t\t\t\t? message\n\t\t\t\t\t\t: inspect(message, false, null, true),\n\t\t\t\t)\n\t\t\t\t.join(` `)\n\t\t\tif (body.includes(`\\n`)) {\n\t\t\t\tbody = `\\n ${body.split(`\\n`).join(`\\n `)}`\n\t\t\t}\n\t\t\tconst log: FlightDeckLog = {\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\tlevel,\n\t\t\t\tprocess: this.processCode,\n\t\t\t\tpackage: this.packageName,\n\t\t\t\tbody,\n\t\t\t}\n\t\t\tif (this.serviceName) {\n\t\t\t\tlog.service = this.serviceName\n\t\t\t}\n\t\t\tprocess.stdout.write(JSON.stringify(log) + `\\n`)\n\t\t} else {\n\t\t\tconst source = this.serviceName\n\t\t\t\t? `${this.packageName}:${this.serviceName}`\n\t\t\t\t: this.packageName\n\t\t\tswitch (level) {\n\t\t\t\tcase FLIGHTDECK_INFO:\n\t\t\t\t\tconsole.log(`${source}:`, ...messages)\n\t\t\t\t\tbreak\n\t\t\t\tcase FLIGHTDECK_WARN:\n\t\t\t\t\tconsole.warn(`${source}:`, ...messages)\n\t\t\t\t\tbreak\n\t\t\t\tcase FLIGHTDECK_ERROR:\n\t\t\t\t\tconsole.error(`${source}:`, ...messages)\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tpublic info(...messages: unknown[]): void {\n\t\tthis.log(FLIGHTDECK_INFO, ...messages)\n\t}\n\n\tpublic warn(...messages: unknown[]): void {\n\t\tthis.log(FLIGHTDECK_WARN, ...messages)\n\t}\n\n\tpublic error(...messages: unknown[]): void {\n\t\tthis.log(FLIGHTDECK_ERROR, ...messages)\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAGA,MAAa,MAAM,UAAU;CAC5B,QAAQ,EAAE,mBAAmB,KAAK,UAAU,KAAK,YAAY,EAAE;CAC/D,cAAc;CACd,QAAQ,EAAE;CACV,YAAY,OAAO,KAAK;CACxB,wBAAwB;CACxB,CAAC;;;;ACSF,MAAa,0BAA0B,CAAC,cAAc,YAAY;AAIlE,MAAa,2BAA2B,CAAC,YAAY,YAAY;AAIjE,SAAgB,gBAAgB,SAA0B;AACzD,QACC,kBAAkB,KAAK,QAAQ,IAAI,CAAC,OAAO,MAAM,OAAO,WAAW,QAAQ,CAAC;;AAiB9E,IAAa,aAAb,MAAmD;CAClD,AAAgB;CAChB,AAAU,SAAS;CAEnB,AAAU;CAKV,AAAU;CAOV,AAAU;CACV,AAAO;CACP,AAAO;CACP,AAAO;CAEP,AAAU;CACV,AAAU;CAIV,AAAU,4BAA4C;CAEtD,AAAO;CACP,AAAO;CACP,AAAO,OAAO,IAAI,aAAa,GAAG;CAClC,AAAO,OAAO,IAAI,aAAa,GAAG;CAElC,AAAU,eAAyB,EAAE;CAErC,AAAO,YAAY,SAA+B;AACjD,OAAK,UAAU;EACf,MAAM,EAAE,sBAAsB;EAC9B,MAAM,EAAE,oBAAoB,QAAQ,SAAS,EAAE,cAAc,KAAK;EAClE,MAAM,OAAO,QAAQ,QAAQ;EAC7B,MAAM,SAAS,oBAAoB;EAEnC,MAAM,kBAAkB,UAAU,QAAQ,SAAS;AACnD,OAAK,WAAW,YACf,gBAAgB,KAAK,CAAC,iBAAiB,CAAC,aAAa,KAAK,CAAC,CAC3D;AACD,OAAK,aAAa,YACjB,gBAAgB,KAAK,CAAC,cAAc,QAAQ,CAAC,aAAa,IAAI,CAAC,CAC/D;AACD,OAAK,+BAA+B,YACnC,gBAAgB,KAAK,CAAC,aAAa,EAAE,eAAe,CACnD,aACA,CAAC,QACD,CAAC,CACF;AACD,OAAK,wBAAwB,EAAE,GAAG,KAAK,8BAA8B;AACrE,OAAK,0BAA0B;AAE/B,OAAK,SAAS,IAAI,iBACjB,KAAK,QAAQ,aACb,QAAQ,KACR,QACA,EAAE,aAAa,KAAK,QAAQ,eAAe,OAAO,CAClD;AACD,OAAK,iBAAiB,YACrB,gBAAgB,KAAK,CAAC,iBAAiB,CACtC,aACA,IAAI,iBACH,KAAK,QAAQ,aACb,QAAQ,KACR,aACA,EAAE,aAAa,KAAK,QAAQ,eAAe,OAAO,CAClD,CACD,CAAC,CACF;AAED,OAAK,eAAe,gBAAgB,UAAU,IAAI,aAAa,GAAG,CAAC;AACnE,OAAK,eAAe,gBAAgB,UAAU,IAAI,aAAa,GAAG,CAAC;AACnE,OAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;AAC7C,OAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;AAE7C,OAAK,UAAU,IAAI,kBAAkB,EACpC,MAAM,QAAQ,mBAAmB,WAAW,QAAQ,YAAY,EAChE,CAAC;AAEF,MAAI,sBAAsB,OACzB,MAAK,OAAO,KACX,6FACA;MAED,eAAc,KAAK,QAAQ;GAC1B,IAAIA,OAAqB,EAAE;AAC3B,OACE,GAAG,SAAS,UAAU;AACtB,SAAK,KAAK,iBAAiB,SAAS,QAAQ,OAAO,KAAK,MAAM,CAAC;KAC9D,CACD,GAAG,OAAO,YAAY;IACtB,MAAM,aAAa,IAAI,QAAQ;AAC/B,QAAI;AACH,SAAI,OAAO,IAAI,QAAQ,YAAa,OAAM;KAC1C,MAAM,qBAAqB,UAAU;AACrC,SAAI,eAAe,UAAU,qBAAqB;AACjD,WAAK,OAAO,KACX,0BAA0B,mBAAmB,YAAY,WAAW,IACpE;AACD,YAAM;;KAEP,MAAM,MAAM,IAAI,IAAI,IAAI,KAAK,OAAO;AACpC,UAAK,OAAO,KAAK,IAAI,QAAQ,IAAI,SAAS;KAE1C,MAAM,sBAAsB,OAAO,OAAO,KAAK,CAAC,UAAU;AAC1D,SAAI,CAAC,gBAAgB,oBAAoB,CACxC,OAAM;AAGP,SAAI,UAAU,IAAI;AAClB,SAAI,KAAK;AAET,UAAK,QAAQ,QAAQ,eAAe,WAAW;AAC/C,UAAK,QAAQ,QAAQ,wBAAwB,oBAAoB;KACjE,MAAM,EAAE,sBAAsB,QAAQ;AACtC,SAAI,mBAAmB;AACtB,YAAM,KAAK,2BAA2B,MAAM;AAC5C,YAAM,KAAK,WAAW,oBAAoB;MAC1C,MAAM,cAAc,KAAK,QAAQ,QAAQ,cAAc;AACvD,WAAK,OAAO,KAAK,8BAA8B,YAAY;AAC3D,UAAI,gBAAgB,YAAY;AAC/B,YAAK,4BAA4B,IAAI,QACpC,kBACA,YAAY;AACX,cAAM,KAAK,WAAW,oBAAoB;SAE3C;AACD,YAAK,0BAA0B,OAAO;;WAGvC,MAAK,iBAAiB;aAEf,QAAQ;AAChB,UAAK,OAAO,MAAM,QAAQ,IAAI,IAAI;AAClC,SAAI,OAAO,WAAW,UAAU;AAC/B,UAAI,UAAU,OAAO;AACrB,UAAI,KAAK;;cAED;AACT,YAAO,EAAE;;KAET;IACF,CAAC,OAAO,YAAY;AACrB,QAAK,OAAO,KAAK,0BAA0B,OAAO;IACjD;AAGH,OAAK,kBAAkB,CACrB,WAAW;AACX,QAAK,OAAO,KAAK,wBAAwB;IACxC,CACD,OAAO,WAAW;AAClB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,iCAAiC,OAAO,QAAQ;IAElE;;CAGJ,MAAgB,WAAW,SAAgC;AAC1D,OAAK,OAAO,KAAK,0BAA0B;EAC3C,MAAM,EAAE,sBAAsB,KAAK,QAAQ;AAC3C,MAAI,CAAC,mBAAmB;AACvB,QAAK,OAAO,KAAK,qCAAqC;AACtD;;AAED,MAAI;GACH,MAAM,MAAM,SAAS,GAAG,kBAAkB,GAAG,UAAU;AACvD,QAAK,OAAO,KAAK,iBAAiB,IAAI,UAAU,CAAC;AACjD,SAAM,KAAK,2BAA2B,MAAM;AAC5C,QAAK,QAAQ,QAAQ,eAAe,YAAY;AAChD,QAAK,iBAAiB;AACtB,QAAK,gBAAgB;WACb,QAAQ;AAChB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,iBAAiB,OAAO,QAAQ;QAC5C;IACN,MAAM,aAAa,aAAa,OAAO;AACvC,SAAK,OAAO,MAAM,eAAe,YAAY,OAAO;;;;CAKvD,AAAU,iBAAuB;AAChC,OAAK,MAAM,SAAS,UAAU,KAAK,SAAS,EAAE;GAC7C,MAAM,CAAC,aAAa,WAAW;AAC/B,OAAI,SACH;QAAI,KAAK,QAAQ,SAAS,aAAa,QACtC,SAAQ,KAAK,eAAe;SAG7B,MAAK,aAAa,YAAY;;;CAKjC,AAAU,YAAkB;AAC3B,MAAI,UAAU,KAAK,sBAAsB,CAAC,OAAO,GAAG,aAAa,QAAQ,EAAE;AAC1E,QAAK,OAAO,KAAK,oCAAoC;AACrD,QAAK,iBAAiB,CACpB,WAAW;AACX,SAAK,OAAO,KAAK,6CAA6C;AAC9D,SAAK,kBAAkB,CACrB,WAAW;AACX,UAAK,OAAO,KAAK,2CAA2C;MAC3D,CACD,OAAO,WAAW;AAClB,SAAI,kBAAkB,MACrB,MAAK,OAAO,MACX,iCACA,OAAO,QACP;MAED;KACF,CACD,OAAO,WAAW;AAClB,QAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,gCAAgC,OAAO,QAAQ;KAEjE;;;CAIL,AAAU,mBAAoC;AAC7C,OAAK,OAAO,KAAK,2BAA2B;AAC5C,OAAK,0BAA0B;EAC/B,MAAM,aAAa,KAAK,QAAQ,QAAQ,aAAa;AACrD,OAAK,OAAO,KAAK,6BAA6B,WAAW;AACzD,UAAQ,YAAR;GACC,KAAK;AACJ,SAAK,OAAO,KAAK,yBAAyB;AAC1C,SAAK,iBAAiB;AACtB,SAAK,gBAAgB;AACrB,WAAO,KAAK,kBAAkB;GAC/B,KAAK;AACJ,SAAK,OAAO,KAAK,8CAA8C;AAC/D,SAAK,gBAAgB;AACrB,WAAO,KAAK,kBAAkB;GAC/B,KAAK;AACJ,SAAK,MAAM,CAAC,gBAAgB,UAAU,KAAK,SAAS,CACnD,MAAK,aAAa,YAAY;AAE/B,WAAO,KAAK;;;CAKf,AAAU,aAAa,aAAsB;AAC5C,OAAK,OAAO,KACX,oBAAoB,KAAK,QAAQ,YAAY,IAAI,YAAY,QAAQ,KAAK,OAAO,OACjF;AACD,MAAI,KAAK,UAAU,EAClB,OAAM,IAAI,MAAM,kBAAkB;AAEnC,OAAK;EAEL,MAAM,CAAC,KAAK,GAAG,QAAQ,KAAK,QAAQ,SAAS,aAAa,IAAI,MAAM,IAAI;EACxE,MAAM,iBAAiB,MAAM,KAAK,MAAM;GACvC,KAAK,KAAK,QAAQ;GAClB,KAAK,OAAO,KAAK;GACjB,CAAC;EACF,MAAM,gBAAgB,KAAK,eAAe;AAM1C,gBAAc,eALG,KAAK,SAAS,eAAe,IAAI,YACjD,gBACA,GAAG,KAAK,QAAQ,YAAY,IAAI,eAChC,cACA,EACmC,KAAK,OAAO;AAChD,OAAK,SAAS,aAAa,OAAO,GAAG,aAAa;AACjD,iBAAc,KAAK,MAAM,GAAG,SAAS;IACpC;AACF,OAAK,SAAS,aAAa,GAAG,uBAAuB;AACpD,QAAK,OAAO,KAAK,YAAY,YAAY,uBAAuB;AAChE,QAAK,sBAAsB,eAAe;AAC1C,QAAK,WAAW;IACf;AACF,OAAK,SAAS,aAAa,GAAG,eAAe;AAC5C,QAAK,aAAa,KAAK,WAAW,cAAc,IAAI,QAAQ,SAAS,CAAC;AACtE,QAAK,aAAa,KAAK,WAAW,gBAAgB,IAAI,aAAa,GAAG;AACtE,OAAI,KAAK,KAAK,KACb,MAAK,OAAO,IAAI,aAAa,GAAG;AAEjC,QAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;IAC5C;AACF,OAAK,SAAS,aAAa,KAAK,KAAK,UAAU,aAAa;AAC3D,QAAK,OAAO,KACX,qBAAqB,YAAY,mBAAmB,WACpD;AACD,QAAK,SAAS,eAAe;AAC7B,OAAI,CAAC,KAAK,yBAAyB;AAClC,SAAK,OAAO,KAAK,yBAAyB,YAAY,UAAU;AAChE;;GAED,MAAM,cAAc,KAAK,QAAQ,QAAQ,cAAc;AACvD,QAAK,OAAO,KAAK,8BAA8B,YAAY;AAE3D,OADwB,gBAAgB,aACnB;AACpB,SAAK,eAAe,aAAa,KAAK,6BAA6B;AACnE,SAAK,eAAe,EAAE;AACtB,SAAK,gBAAgB;AACrB,SAAK,aAAa,YAAY;UACxB;IACN,MAAM,MAAM,KAAK,KAAK;IACtB,MAAM,iBAAiB,MAAM,MAAS;AACtC,SAAK,eAAe,KAAK,aAAa,QACpC,SAAS,OAAO,eACjB;AACD,SAAK,aAAa,KAAK,IAAI;AAE3B,QAAI,KAAK,aAAa,SAAS,GAAG;AACjC,UAAK,eAAe,aAAa,KAAK,yBAAyB;AAC/D,UAAK,aAAa,YAAY;UAE9B,MAAK,eAAe,aAAa,KAChC,gDACA;;IAGF;AACF,OAAK,SAAS;;CAGf,AAAU,kBAAwB;AACjC,OAAK,OAAO,KAAK,iBAAiB;AAClC,MAAI;GACH,MAAM,MAAM,SAAS,KAAK,QAAQ,QAAQ,SAAS;AACnD,QAAK,OAAO,KAAK,oBAAoB,IAAI,UAAU,CAAC;AACpD,QAAK,QAAQ,QAAQ,cAAc,aAAa;AAChD,QAAK,OAAO,KAAK,cAAc;WACvB,QAAQ;AAChB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,qCAAqC,OAAO,UAAU;AAEzE;;;CAIF,AAAU,iBAAuB;AAChC,OAAK,OAAO,KAAK,gBAAgB;AAEjC,MAAI;GACH,MAAM,MAAM,SAAS,KAAK,QAAQ,QAAQ,QAAQ;AAClD,QAAK,OAAO,KAAK,mBAAmB,IAAI,UAAU,CAAC;AACnD,QAAK,QAAQ,QAAQ,cAAc,YAAY;AAC/C,QAAK,OAAO,KAAK,aAAa;WACtB,QAAQ;AAChB,OAAI,kBAAkB,MACrB,MAAK,OAAO,MAAM,qCAAqC,OAAO,UAAU;AAEzE;;;CAIF,AAAO,kBAAmC;AACzC,OAAK,OAAO,KAAK,kDAAkD;AACnE,OAAK,0BAA0B;AAC/B,OAAK,MAAM,CAAC,gBAAgB,UAAU,KAAK,SAAS,CACnD,MAAK,YAAY,YAAY;AAE9B,SAAO,KAAK;;CAGb,AAAO,YAAY,aAAsB;EACxC,MAAM,UAAU,KAAK,SAAS;AAC9B,MAAI,SAAS;AACZ,QAAK,OAAO,KAAK,qBAAqB,YAAY,MAAM;AACxD,QAAK,aAAa,KAAK,WAAW,cAAc,IAC/C,IAAI,SAAS,SAAS;AACrB,YAAQ,KAAK,aAAa;AAC1B,YAAQ,KAAK,KAAK,UAAU,aAAa;AACxC,UAAK,OAAO,KACX,oBAAoB,YAAY,sBAAsB,WACtD;AACD,UAAK,SAAS,eAAe;AAC7B,WAAM;MACL;KACD,CACF;AACD,QAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;AAC7C,QAAK,aAAa,KAAK,WAAW,gBAAgB,IAAI,aAAa,GAAG;AACtE,OAAI,KAAK,KAAK,KACb,MAAK,OAAO,IAAI,aAAa,GAAG;AAEjC,QAAK,KAAK,IAAI,QAAQ,IAAI,KAAK,aAAa,CAAC;QAE7C,MAAK,eAAe,aAAa,MAChC,gDACA;;;AAKJ,MAAa,kBAAkB;AAC/B,MAAa,kBAAkB;AAC/B,MAAa,mBAAmB;AAchC,MAAM,cAAc;AACpB,MAAM,QAAQ;AA6Bd,MAAa,yBAAyB;CACrC,OAAO;CACP,aAAa;CACb,aAAa;CACb,mBAAmB;CACnB,qBAAqB;CACrB,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,OAAO;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACP;EAEA,cAAc;EACd,EACC,OAAO,SACP;EACD;GACC,QAAQ;GACR,OAAO;GACP,oBAAoB;GACpB;EACD;GACC,QAAQ;GACR,OAAO;GACP,aAAa;GACb;EACD;GACC,QAAQ;GACR,OAAO;GACP;EACD;GACC,QAAQ;GACR,OAAO;GACP,iBAAiB;GACjB;EACD;GACC,QAAQ;GACR,OAAO;GACP;EACD;EAEA,QAAQ;EACR,WAAW,EACV,MAAM,WACN;EACD,OAAO,EACN,MAAM,UACN;EACD,SAAS,EACR,MAAM,UACN;EACD,SAAS,EACR,MAAM,UACN;EACD,SAAS,EACR,MAAM,WACN;EACD,MAAM,EACL,MAAM,UACN;EACD;CACD;AAED,IAAa,mBAAb,MAEA;CACC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAO;CACP,AAAO,YACN,aACA,aACA,aACA,SACC;AACD,OAAK,cAAc;AACnB,MAAI,YACH,MAAK,cAAc;AAEpB,OAAK,cAAc;AACnB,OAAK,cAAc,SAAS,eAAe;;CAE5C,AAAU,IACT,OAIA,GAAG,UACI;AACP,MAAI,KAAK,aAAa;GACrB,IAAI,OAAO,SACT,KAAK,YACL,OAAO,YAAY,WAChB,UACA,QAAQ,SAAS,OAAO,MAAM,KAAK,CACtC,CACA,KAAK,IAAI;AACX,OAAI,KAAK,SAAS,KAAK,CACtB,QAAO,OAAO,KAAK,MAAM,KAAK,CAAC,KAAK,OAAO;GAE5C,MAAMC,MAAqB;IAC1B,WAAW,KAAK,KAAK;IACrB;IACA,SAAS,KAAK;IACd,SAAS,KAAK;IACd;IACA;AACD,OAAI,KAAK,YACR,KAAI,UAAU,KAAK;AAEpB,WAAQ,OAAO,MAAM,KAAK,UAAU,IAAI,GAAG,KAAK;SAC1C;GACN,MAAM,SAAS,KAAK,cACjB,GAAG,KAAK,YAAY,GAAG,KAAK,gBAC5B,KAAK;AACR,WAAQ,OAAR;IACC,KAAK;AACJ,aAAQ,IAAI,GAAG,OAAO,IAAI,GAAG,SAAS;AACtC;IACD,KAAK;AACJ,aAAQ,KAAK,GAAG,OAAO,IAAI,GAAG,SAAS;AACvC;IACD,KAAK;AACJ,aAAQ,MAAM,GAAG,OAAO,IAAI,GAAG,SAAS;AACxC;;;;CAIJ,AAAO,KAAK,GAAG,UAA2B;AACzC,OAAK,IAAI,iBAAiB,GAAG,SAAS;;CAGvC,AAAO,KAAK,GAAG,UAA2B;AACzC,OAAK,IAAI,iBAAiB,GAAG,SAAS;;CAGvC,AAAO,MAAM,GAAG,UAA2B;AAC1C,OAAK,IAAI,kBAAkB,GAAG,SAAS"}
|