Files

71 lines
2.6 KiB
TypeScript

import { fileURLToPath } from "node:url";
import { hold } from "../packages/world/src/hold.ts";
import { launchHeadlessWeb } from "../packages/world/src/headless-web.ts";
import type { HeadlessWebHandle } from "../packages/world/src/headless-web.ts";
const REPO_ROOT = fileURLToPath(new URL("..", import.meta.url));
const DEV_HEADLESS_NAME = "dev-headless";
export interface DevHeadlessOptions {
name?: string;
replace?: boolean;
keepTokens?: boolean;
rotateTokens?: boolean;
}
function parseArgs(argv: readonly string[]): DevHeadlessOptions {
const supported = new Set(["--replace", "--keep-tokens", "--rotate-tokens"]);
const unsupported = argv.find((argument) => !supported.has(argument));
if (unsupported) throw new Error(`Unsupported dev-headless flag: ${unsupported}`);
return {
replace: argv.includes("--replace")
|| ["1", "true", "yes", "on"].includes((process.env.OPENWORK_DEV_HEADLESS_WEB_REPLACE ?? "").trim().toLowerCase()),
keepTokens: argv.includes("--keep-tokens"),
rotateTokens: argv.includes("--rotate-tokens"),
};
}
function outputs(handle: HeadlessWebHandle): Record<string, string> {
return {
webUrl: handle.manifest.webUrl,
openworkUrl: handle.manifest.openworkUrl,
workspace: handle.manifest.workspace,
runtimeManifest: handle.manifest.runtimeManifestPath,
};
}
/** Isolated source UI + local backend. This is the world behind pnpm dev:headless-web. */
export async function bootDevHeadless(
stack: AsyncDisposableStack,
options: DevHeadlessOptions = {},
): Promise<HeadlessWebHandle> {
const handle = await launchHeadlessWeb({
repoRoot: REPO_ROOT,
name: options.name ?? DEV_HEADLESS_NAME,
state: "isolated",
replace: options.replace,
keepTokens: options.keepTokens,
rotateTokens: options.rotateTokens,
});
return stack.adopt(handle, (headless) => headless.stop());
}
export async function main(argv: readonly string[] = process.argv.slice(2)): Promise<void> {
const options = parseArgs(argv);
await using stack = new AsyncDisposableStack();
const handle = await bootDevHeadless(stack, options);
await hold({ name: DEV_HEADLESS_NAME, outputs: outputs(handle) });
}
export async function detachDevHeadless(argv: readonly string[]): Promise<void> {
const options = parseArgs(argv);
await using stack = new AsyncDisposableStack();
const handle = await bootDevHeadless(stack, options);
await handle.detach();
stack.move();
for (const [key, value] of Object.entries(outputs(handle))) console.log(`${key} ${value}`);
console.log(`World ${JSON.stringify(DEV_HEADLESS_NAME)} is up. Detached; relaunch with --replace to tear it down.`);
}
if (import.meta.main) await main();