@chandresh726/switchy 1.0.11 → 1.0.13

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/cli.js CHANGED
@@ -7,7 +7,7 @@ import { spawn as spawn3 } from "node:child_process";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@chandresh726/switchy",
10
- version: "1.0.11",
10
+ version: "1.0.13",
11
11
  description: "Install, run, update, and stop the local-first Switchy application.",
12
12
  license: "MIT",
13
13
  type: "module",
package/dist/cli.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/commands.ts", "../package.json", "../src/config.ts", "../src/files.ts", "../src/process-manager.ts", "../src/paths.ts", "../src/release.ts", "../src/platform.ts", "../src/runtime.ts", "../src/cli.ts"],
4
- "sourcesContent": ["import { readFile } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\n\nimport {\n CLI_VERSION,\n DEFAULT_PORT,\n resolveApplicationVersion,\n} from \"./config.js\";\nimport {\n readJsonFile,\n withDirectoryLock,\n} from \"./files.js\";\nimport {\n fetchLiveness,\n getRunningProcess,\n startSwitchyProcess,\n stopSwitchy,\n} from \"./process-manager.js\";\nimport {\n ensureSwitchyDirectories,\n getSwitchyPaths,\n} from \"./paths.js\";\nimport {\n installRuntime,\n resolveLatestStableVersion,\n setCurrentVersion,\n} from \"./release.js\";\nimport {\n createUpdateSnapshot,\n ensurePlaywrightBrowser,\n runDatabaseMigration,\n} from \"./runtime.js\";\nimport type { CurrentVersionRecord } from \"./types.js\";\n\nasync function installedVersion(): Promise<string | null> {\n const paths = getSwitchyPaths();\n const current = await readJsonFile<CurrentVersionRecord>(paths.currentVersion);\n return current?.schemaVersion === 1 ? current.version : null;\n}\n\nexport async function startCommand({\n version,\n port = DEFAULT_PORT,\n foreground = false,\n}: {\n version?: string;\n port?: number;\n foreground?: boolean;\n} = {}): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n await withDirectoryLock(paths.startLock, () =>\n startCommandUnlocked({ paths, version, port, foreground })\n );\n}\n\nasync function startCommandUnlocked({\n paths,\n version,\n port,\n foreground,\n}: {\n paths: ReturnType<typeof getSwitchyPaths>;\n version?: string;\n port: number;\n foreground: boolean;\n}): Promise<void> {\n const existing = await getRunningProcess(paths);\n if (existing) {\n console.log(\n `Switchy ${existing.record.version} is already running at `\n + `http://${existing.record.hostname}:${existing.record.port}`\n );\n return;\n }\n\n const selectedVersion = resolveApplicationVersion(version);\n const runtimeDirectory = await installRuntime(selectedVersion, paths);\n await ensurePlaywrightBrowser(runtimeDirectory, paths);\n await runDatabaseMigration(runtimeDirectory, paths);\n await setCurrentVersion(paths, selectedVersion);\n const record = await startSwitchyProcess({\n paths,\n runtimeDirectory,\n version: selectedVersion,\n port,\n foreground,\n });\n if (!foreground) {\n console.log(\n `Switchy ${selectedVersion} is running at `\n + `http://${record.hostname}:${record.port}`\n );\n }\n}\n\nexport async function stopCommand(force = false): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n await withDirectoryLock(paths.startLock, async () => {\n if (await stopSwitchy(paths, force)) console.log(\"Switchy stopped.\");\n else console.log(\"Switchy is not running.\");\n });\n}\n\nexport async function statusCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n const running = await getRunningProcess(paths);\n if (!running) {\n console.log(\"Switchy is stopped.\");\n return;\n }\n const live = running.live ?? await fetchLiveness(running.record);\n console.log(`Switchy ${running.record.version} is running.`);\n console.log(`URL: http://${running.record.hostname}:${running.record.port}`);\n console.log(`PID: ${running.record.pid}`);\n console.log(`Health: ${live ? \"Live\" : \"Starting or unavailable\"}`);\n}\n\nexport async function updateCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n await withDirectoryLock(paths.startLock, async () => {\n const latestVersion = await resolveLatestStableVersion();\n const currentVersion = await installedVersion();\n if (currentVersion === latestVersion) {\n console.log(`Switchy ${latestVersion} is already current.`);\n return;\n }\n\n const wasRunning = await getRunningProcess(paths);\n await installRuntime(latestVersion, paths);\n if (wasRunning) await stopSwitchy(paths);\n const snapshot = currentVersion\n ? await createUpdateSnapshot(paths, currentVersion, latestVersion)\n : null;\n try {\n await startCommandUnlocked({\n paths,\n version: latestVersion,\n port: wasRunning?.record.port ?? DEFAULT_PORT,\n foreground: false,\n });\n } catch (error) {\n if (snapshot) {\n console.error(`Update snapshot retained at ${snapshot}`);\n }\n throw error;\n }\n });\n}\n\nexport async function logsCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n const contents = await readFile(paths.logFile, \"utf8\").catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"\";\n throw error;\n });\n const lines = contents.trimEnd().split(\"\\n\");\n console.log(lines.slice(-200).join(\"\\n\"));\n}\n\nexport async function openCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n const running = await getRunningProcess(paths);\n if (!running) throw new Error(\"Switchy is not running\");\n const url = `http://${running.record.hostname}:${running.record.port}`;\n let executable: string;\n let args: string[];\n if (process.platform === \"darwin\") {\n executable = \"open\";\n args = [url];\n } else if (process.platform === \"win32\") {\n executable = \"cmd\";\n args = [\"/c\", \"start\", \"\", url];\n } else {\n executable = \"xdg-open\";\n args = [url];\n }\n const child = spawn(executable, args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n console.log(url);\n}\n\nexport function versionCommand(): void {\n console.log(CLI_VERSION);\n}\n", "{\n \"name\": \"@chandresh726/switchy\",\n \"version\": \"1.0.11\",\n \"description\": \"Install, run, update, and stop the local-first Switchy application.\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"bin\": {\n \"switchy\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"engines\": {\n \"node\": \">=24 <25\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Chandresh726/Switchy.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://github.com/Chandresh726/Switchy#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/Chandresh726/Switchy/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"prepack\": \"pnpm build\"\n },\n \"dependencies\": {\n \"tar\": \"^7.5.22\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.13.3\",\n \"esbuild\": \"^0.28.1\",\n \"typescript\": \"^5.9.3\"\n }\n}\n", "import packageJson from \"../package.json\" with { type: \"json\" };\n\nexport const CLI_VERSION = packageJson.version;\nexport const DEFAULT_PORT = 6767;\nexport const DEFAULT_HOSTNAME = \"127.0.0.1\";\nexport const GITHUB_REPOSITORY = \"Chandresh726/Switchy\";\nexport const RELEASE_MANIFEST_FILE = \"switchy-manifest.json\";\nexport const STARTUP_TIMEOUT_MS = 60_000;\nexport const SHUTDOWN_TIMEOUT_MS = 15_000;\n\nexport function resolveApplicationVersion(requestedVersion?: string): string {\n return requestedVersion ?? CLI_VERSION;\n}\n\nexport function assertSupportedNodeVersion(\n nodeVersion = process.versions.node\n): void {\n if (!/^24\\.\\d+\\.\\d+$/u.test(nodeVersion)) {\n throw new Error(\n `Switchy requires Node.js 24; current version is ${nodeVersion}`\n );\n }\n}\n", "import { randomUUID } from \"node:crypto\";\nimport {\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst LOCK_TIMEOUT_MS = 60_000;\nconst INVALID_LOCK_GRACE_MS = 30_000;\nconst LOCK_OWNER_FILE = \"owner.json\";\n\ninterface LockOwner {\n pid: number;\n token: string;\n}\n\nfunction processIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function readLockOwner(lockPath: string): Promise<LockOwner | null> {\n try {\n const value = JSON.parse(\n await readFile(path.join(lockPath, LOCK_OWNER_FILE), \"utf8\")\n ) as Partial<LockOwner>;\n if (\n !Number.isInteger(value.pid)\n || (value.pid ?? 0) <= 0\n || typeof value.token !== \"string\"\n || value.token.length === 0\n ) {\n return null;\n }\n return value as LockOwner;\n } catch {\n return null;\n }\n}\n\nasync function removeOwnedLock(\n lockPath: string,\n token: string\n): Promise<void> {\n const owner = await readLockOwner(lockPath);\n if (owner?.token === token) {\n await rm(lockPath, { recursive: true, force: true });\n }\n}\n\nexport async function readJsonFile<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf8\")) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nexport async function writeJsonFileAtomic(\n filePath: string,\n value: unknown\n): Promise<void> {\n const temporaryPath = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${randomUUID()}.tmp`\n );\n await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}\\n`, {\n mode: 0o600,\n });\n await rename(temporaryPath, filePath);\n}\n\nexport async function removeFile(filePath: string): Promise<void> {\n await rm(filePath, { force: true });\n}\n\nexport async function withDirectoryLock<T>(\n lockPath: string,\n operation: () => Promise<T>\n): Promise<T> {\n await mkdir(path.dirname(lockPath), { recursive: true, mode: 0o700 });\n const deadline = Date.now() + LOCK_TIMEOUT_MS;\n const token = randomUUID();\n while (true) {\n try {\n await mkdir(lockPath, { mode: 0o700 });\n try {\n await writeFile(\n path.join(lockPath, LOCK_OWNER_FILE),\n `${JSON.stringify({ pid: process.pid, token })}\\n`,\n { mode: 0o600 }\n );\n } catch (error) {\n await rm(lockPath, { recursive: true, force: true });\n throw error;\n }\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const [owner, lockStats] = await Promise.all([\n readLockOwner(lockPath),\n stat(lockPath).catch(() => null),\n ]);\n if (\n (owner && !processIsAlive(owner.pid))\n || (\n !owner\n && lockStats\n && Date.now() - lockStats.mtimeMs > INVALID_LOCK_GRACE_MS\n )\n ) {\n await rm(lockPath, { recursive: true, force: true });\n continue;\n }\n if (Date.now() >= deadline) {\n throw new Error(`Timed out waiting for ${path.basename(lockPath)}`);\n }\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n }\n\n try {\n return await operation();\n } finally {\n await removeOwnedLock(lockPath, token);\n }\n}\n", "import { open } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\n\nimport {\n DEFAULT_HOSTNAME,\n SHUTDOWN_TIMEOUT_MS,\n STARTUP_TIMEOUT_MS,\n} from \"./config.js\";\nimport {\n readJsonFile,\n removeFile,\n writeJsonFileAtomic,\n} from \"./files.js\";\nimport type { ProcessRecord, SwitchyPaths } from \"./types.js\";\n\nfunction processIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function readProcessRecord(\n paths: SwitchyPaths\n): Promise<ProcessRecord | null> {\n const record = await readJsonFile<ProcessRecord>(paths.processRecord)\n .catch(() => null);\n if (\n !record\n || record.schemaVersion !== 1\n || !Number.isInteger(record.pid)\n || record.pid <= 0\n || typeof record.instanceId !== \"string\"\n || record.instanceId.length === 0\n || !/^\\d+\\.\\d+\\.\\d+$/u.test(record.version)\n || record.hostname !== DEFAULT_HOSTNAME\n || !Number.isInteger(record.port)\n || record.port < 1\n || record.port > 65_535\n || !Number.isFinite(Date.parse(record.startedAt))\n ) {\n return null;\n }\n return record;\n}\n\nexport async function fetchLiveness(record: ProcessRecord): Promise<{\n status: \"live\";\n version: string;\n instanceId: string | null;\n} | null> {\n try {\n const response = await fetch(\n `http://${record.hostname}:${record.port}/api/health/live`,\n { signal: AbortSignal.timeout(2_000) }\n );\n if (!response.ok) return null;\n const value = await response.json() as {\n status?: unknown;\n version?: unknown;\n instanceId?: unknown;\n };\n if (value.status !== \"live\" || typeof value.version !== \"string\") return null;\n return {\n status: \"live\",\n version: value.version,\n instanceId: typeof value.instanceId === \"string\" ? value.instanceId : null,\n };\n } catch {\n return null;\n }\n}\n\nexport async function getRunningProcess(\n paths: SwitchyPaths\n): Promise<{ record: ProcessRecord; live: Awaited<ReturnType<typeof fetchLiveness>> } | null> {\n const record = await readProcessRecord(paths);\n if (!record) {\n await removeFile(paths.processRecord);\n return null;\n }\n if (!processIsAlive(record.pid)) {\n await removeFile(paths.processRecord);\n return null;\n }\n return { record, live: await fetchLiveness(record) };\n}\n\nasync function waitForExit(pid: number, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!processIsAlive(pid)) return true;\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n return !processIsAlive(pid);\n}\n\nasync function signalProcessTree(\n pid: number,\n signal: \"SIGTERM\" | \"SIGKILL\"\n): Promise<void> {\n if (process.platform !== \"win32\") {\n try {\n process.kill(-pid, signal);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") throw error;\n try {\n process.kill(pid, signal);\n } catch (fallbackError) {\n if ((fallbackError as NodeJS.ErrnoException).code !== \"ESRCH\") {\n throw fallbackError;\n }\n }\n return;\n }\n }\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\n \"taskkill\",\n [\n \"/PID\",\n String(pid),\n \"/T\",\n \"/F\",\n ],\n { stdio: \"ignore\", windowsHide: true }\n );\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0 || !processIsAlive(pid)) resolve();\n else reject(new Error(`taskkill exited with code ${code}`));\n });\n });\n}\n\nexport async function stopSwitchy(\n paths: SwitchyPaths,\n force = false\n): Promise<boolean> {\n const running = await getRunningProcess(paths);\n if (!running) return false;\n if (\n !force\n && (\n !running.live\n || running.live.instanceId !== running.record.instanceId\n )\n ) {\n throw new Error(\n \"The recorded process could not be verified as Switchy. Re-run with stop --force.\"\n );\n }\n\n await signalProcessTree(\n running.record.pid,\n force ? \"SIGKILL\" : \"SIGTERM\"\n );\n if (!(await waitForExit(running.record.pid, SHUTDOWN_TIMEOUT_MS))) {\n throw new Error(\"Switchy did not stop in time. Re-run with stop --force.\");\n }\n await removeFile(paths.processRecord);\n return true;\n}\n\nasync function waitUntilReady(record: ProcessRecord): Promise<void> {\n const deadline = Date.now() + STARTUP_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (!processIsAlive(record.pid)) {\n throw new Error(\"Switchy exited before becoming ready\");\n }\n try {\n const response = await fetch(\n `http://${record.hostname}:${record.port}/api/health/ready`,\n { signal: AbortSignal.timeout(2_000) }\n );\n if (response.ok) return;\n } catch {\n // The server may still be starting.\n }\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n throw new Error(\"Switchy did not become ready within 60 seconds\");\n}\n\nexport async function startSwitchyProcess({\n paths,\n runtimeDirectory,\n version,\n port,\n foreground,\n}: {\n paths: SwitchyPaths;\n runtimeDirectory: string;\n version: string;\n port: number;\n foreground: boolean;\n}): Promise<ProcessRecord> {\n const instanceId = randomUUID();\n const environment: NodeJS.ProcessEnv = {\n ...process.env,\n NODE_ENV: \"production\",\n HOSTNAME: DEFAULT_HOSTNAME,\n PORT: String(port),\n SWITCHY_HOME: paths.root,\n SWITCHY_INSTANCE_ID: instanceId,\n PLAYWRIGHT_BROWSERS_PATH: paths.playwright,\n };\n const serverPath = path.join(runtimeDirectory, \"server.js\");\n const logHandle = foreground\n ? null\n : await open(paths.logFile, \"a\", 0o600);\n const child = spawn(process.execPath, [serverPath], {\n cwd: runtimeDirectory,\n detached: !foreground,\n env: environment,\n stdio: foreground\n ? \"inherit\"\n : [\"ignore\", logHandle!.fd, logHandle!.fd],\n });\n await logHandle?.close();\n if (!child.pid) throw new Error(\"Switchy did not return a process ID\");\n\n const record: ProcessRecord = {\n schemaVersion: 1,\n pid: child.pid,\n instanceId,\n version,\n hostname: DEFAULT_HOSTNAME,\n port,\n startedAt: new Date().toISOString(),\n };\n await writeJsonFileAtomic(paths.processRecord, record);\n\n if (foreground) {\n await new Promise<void>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0 || code === null) resolve();\n else reject(new Error(`Switchy exited with code ${code}`));\n });\n }).finally(() => removeFile(paths.processRecord));\n return record;\n }\n\n child.unref();\n try {\n await waitUntilReady(record);\n return record;\n } catch (error) {\n if (processIsAlive(record.pid)) {\n await signalProcessTree(record.pid, \"SIGTERM\");\n if (!(await waitForExit(record.pid, SHUTDOWN_TIMEOUT_MS))) {\n await signalProcessTree(record.pid, \"SIGKILL\");\n if (!(await waitForExit(record.pid, SHUTDOWN_TIMEOUT_MS))) {\n throw new Error(\n `Switchy failed to start and process ${record.pid} could not be stopped`,\n { cause: error }\n );\n }\n }\n }\n await removeFile(paths.processRecord);\n throw error;\n }\n}\n", "import { mkdir } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport type { SwitchyPaths } from \"./types.js\";\n\nexport function getSwitchyPaths(\n configuredRoot = process.env.SWITCHY_HOME\n): SwitchyPaths {\n const override = configuredRoot?.trim();\n if (override && !path.isAbsolute(override)) {\n throw new Error(\"SWITCHY_HOME must be an absolute path\");\n }\n const root = override\n ? path.normalize(override)\n : path.join(os.homedir(), \".switchy\");\n const data = path.join(root, \"data\");\n const app = path.join(root, \"app\");\n const runtime = path.join(root, \"runtime\");\n const logs = path.join(root, \"logs\");\n const cache = path.join(root, \"cache\");\n return {\n root,\n data,\n productionData: path.join(data, \"production\"),\n app,\n versions: path.join(app, \"versions\"),\n currentVersion: path.join(app, \"current.json\"),\n runtime,\n processRecord: path.join(runtime, \"process.json\"),\n installLock: path.join(runtime, \"install.lock\"),\n startLock: path.join(runtime, \"start.lock\"),\n logs,\n logFile: path.join(logs, \"switchy.log\"),\n cache,\n downloads: path.join(cache, \"downloads\"),\n playwright: path.join(cache, \"playwright\"),\n updateSnapshots: path.join(root, \"update-snapshots\"),\n };\n}\n\nexport async function ensureSwitchyDirectories(paths: SwitchyPaths): Promise<void> {\n for (const directory of [\n paths.root,\n paths.data,\n paths.app,\n paths.versions,\n paths.runtime,\n paths.logs,\n paths.cache,\n paths.downloads,\n paths.playwright,\n paths.updateSnapshots,\n ]) {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n }\n}\n", "import { createHash, randomUUID } from \"node:crypto\";\nimport { createReadStream, createWriteStream } from \"node:fs\";\nimport {\n cp,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n} from \"node:fs/promises\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport path from \"node:path\";\n\nimport { extract } from \"tar\";\n\nimport {\n CLI_VERSION,\n GITHUB_REPOSITORY,\n RELEASE_MANIFEST_FILE,\n} from \"./config.js\";\nimport {\n withDirectoryLock,\n writeJsonFileAtomic,\n} from \"./files.js\";\nimport {\n currentTarget,\n isSupportedTarget,\n} from \"./platform.js\";\nimport type {\n CurrentVersionRecord,\n ReleaseArtifact,\n ReleaseManifest,\n SwitchyPaths,\n} from \"./types.js\";\n\nconst STABLE_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+$/u;\n\nexport function assertStableVersion(version: string): string {\n if (!STABLE_VERSION_PATTERN.test(version)) {\n throw new Error(`Invalid stable Switchy version: ${version}`);\n }\n return version;\n}\n\nfunction releaseManifestUrl(version: string): string {\n return process.env.SWITCHY_MANIFEST_URL\n ?? `https://github.com/${GITHUB_REPOSITORY}/releases/download/v${version}/${RELEASE_MANIFEST_FILE}`;\n}\n\nfunction assertReleaseArtifact(value: unknown): ReleaseArtifact {\n if (\n typeof value !== \"object\"\n || value === null\n || typeof (value as ReleaseArtifact).file !== \"string\"\n || path.basename((value as ReleaseArtifact).file)\n !== (value as ReleaseArtifact).file\n || !/^switchy-\\d+\\.\\d+\\.\\d+-(?:darwin|linux|win32)-(?:arm64|x64)\\.tar\\.gz$/u\n .test((value as ReleaseArtifact).file)\n || typeof (value as ReleaseArtifact).sha256 !== \"string\"\n || !/^[a-f0-9]{64}$/u.test((value as ReleaseArtifact).sha256)\n || typeof (value as ReleaseArtifact).size !== \"number\"\n || !Number.isSafeInteger((value as ReleaseArtifact).size)\n || (value as ReleaseArtifact).size <= 0\n || (value as ReleaseArtifact).size > 1_000_000_000\n ) {\n throw new Error(\"Release manifest contains an invalid artifact\");\n }\n return value as ReleaseArtifact;\n}\n\nexport function parseReleaseManifest(value: unknown): ReleaseManifest {\n if (\n typeof value !== \"object\"\n || value === null\n || (value as ReleaseManifest).schemaVersion !== 1\n || typeof (value as ReleaseManifest).version !== \"string\"\n || !STABLE_VERSION_PATTERN.test((value as ReleaseManifest).version)\n || typeof (value as ReleaseManifest).publishedAt !== \"string\"\n || typeof (value as ReleaseManifest).nodeVersion !== \"string\"\n || typeof (value as ReleaseManifest).artifacts !== \"object\"\n || (value as ReleaseManifest).artifacts === null\n ) {\n throw new Error(\"Release manifest is invalid\");\n }\n const manifest = value as ReleaseManifest;\n if (\n !Number.isFinite(Date.parse(manifest.publishedAt))\n || !/^\\d+\\.\\d+\\.\\d+$/u.test(manifest.nodeVersion)\n || Array.isArray(manifest.artifacts)\n ) {\n throw new Error(\"Release manifest is invalid\");\n }\n for (const [target, valueArtifact] of Object.entries(manifest.artifacts)) {\n if (!isSupportedTarget(target)) {\n throw new Error(`Release manifest contains unsupported target ${target}`);\n }\n const artifact = assertReleaseArtifact(valueArtifact);\n if (artifact.file !== `switchy-${manifest.version}-${target}.tar.gz`) {\n throw new Error(\"Release manifest contains inconsistent artifact metadata\");\n }\n }\n return manifest;\n}\n\nasync function fetchJson(url: string): Promise<unknown> {\n const response = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n \"User-Agent\": `switchy-cli/${CLI_VERSION}`,\n },\n signal: AbortSignal.timeout(30_000),\n });\n if (!response.ok) {\n throw new Error(`Request failed with ${response.status}: ${url}`);\n }\n return response.json() as Promise<unknown>;\n}\n\nasync function fetchReleaseManifest(version: string): Promise<{\n manifest: ReleaseManifest;\n url: string;\n}> {\n const url = releaseManifestUrl(version);\n const manifest = parseReleaseManifest(await fetchJson(url));\n if (manifest.version !== version) {\n throw new Error(\n `Release manifest version ${manifest.version} does not match ${version}`\n );\n }\n return { manifest, url };\n}\n\nexport async function resolveLatestStableVersion(): Promise<string> {\n const override = process.env.SWITCHY_LATEST_VERSION?.trim();\n if (override) return assertStableVersion(override);\n const value = await fetchJson(\n `https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest`\n );\n const tagName = typeof value === \"object\" && value !== null\n ? (value as { tag_name?: unknown }).tag_name\n : null;\n if (typeof tagName !== \"string\" || !/^v\\d+\\.\\d+\\.\\d+$/u.test(tagName)) {\n throw new Error(\"Latest GitHub release does not contain a stable version tag\");\n }\n return tagName.slice(1);\n}\n\nasync function sha256(filePath: string): Promise<string> {\n const hash = createHash(\"sha256\");\n for await (const chunk of createReadStream(filePath)) {\n hash.update(chunk);\n }\n return hash.digest(\"hex\");\n}\n\nasync function downloadFile(\n url: string,\n destination: string,\n expectedSize: number\n): Promise<void> {\n const response = await fetch(url, {\n headers: { \"User-Agent\": `switchy-cli/${CLI_VERSION}` },\n redirect: \"follow\",\n signal: AbortSignal.timeout(300_000),\n });\n if (!response.ok || !response.body) {\n throw new Error(`Runtime download failed with ${response.status}`);\n }\n const contentLengthHeader = response.headers.get(\"content-length\");\n const contentLength = Number(contentLengthHeader);\n if (\n contentLengthHeader !== null\n && Number.isFinite(contentLength)\n && contentLength !== expectedSize\n ) {\n throw new Error(\"Runtime download size does not match the release manifest\");\n }\n let downloadedSize = 0;\n const sizeLimit = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n downloadedSize += chunk.length;\n if (downloadedSize > expectedSize) {\n callback(new Error(\"Runtime download exceeded its declared size\"));\n return;\n }\n callback(null, chunk);\n },\n });\n await pipeline(\n Readable.fromWeb(response.body as import(\"node:stream/web\").ReadableStream),\n sizeLimit,\n createWriteStream(destination, { mode: 0o600 })\n );\n if (downloadedSize !== expectedSize) {\n throw new Error(\"Runtime download size does not match the release manifest\");\n }\n}\n\nasync function validateRuntimeDirectory(\n runtimeDirectory: string,\n version: string,\n target: string\n): Promise<void> {\n for (const relativePath of [\n \"server.js\",\n \"bin/migrate.cjs\",\n \"drizzle/meta/_journal.json\",\n \"switchy-runtime.json\",\n ]) {\n const fileStats = await stat(path.join(runtimeDirectory, relativePath))\n .catch(() => null);\n if (!fileStats?.isFile()) {\n throw new Error(`Runtime is missing ${relativePath}`);\n }\n }\n let metadata: {\n schemaVersion?: unknown;\n version?: unknown;\n target?: unknown;\n nodeVersion?: unknown;\n builtAt?: unknown;\n };\n try {\n metadata = JSON.parse(\n await readFile(\n path.join(runtimeDirectory, \"switchy-runtime.json\"),\n \"utf8\"\n )\n ) as typeof metadata;\n } catch {\n throw new Error(\"Runtime metadata is invalid\");\n }\n if (\n metadata.schemaVersion !== 1\n || metadata.version !== version\n || metadata.target !== target\n || typeof metadata.nodeVersion !== \"string\"\n || !/^\\d+\\.\\d+\\.\\d+$/u.test(metadata.nodeVersion)\n || metadata.nodeVersion.split(\".\", 1)[0]\n !== process.versions.node.split(\".\", 1)[0]\n || typeof metadata.builtAt !== \"string\"\n || !Number.isFinite(Date.parse(metadata.builtAt))\n ) {\n throw new Error(\"Runtime metadata does not match this installation\");\n }\n}\n\nasync function installRuntimeUnlocked(\n version: string,\n paths: SwitchyPaths\n): Promise<string> {\n assertStableVersion(version);\n const target = currentTarget();\n const versionDirectory = path.join(paths.versions, version);\n if ((await stat(versionDirectory).catch(() => null))?.isDirectory()) {\n try {\n await validateRuntimeDirectory(versionDirectory, version, target);\n return versionDirectory;\n } catch {\n await rm(versionDirectory, { recursive: true, force: true });\n }\n }\n\n const localRuntime = process.env.SWITCHY_RUNTIME_SOURCE?.trim();\n const temporaryDirectory = path.join(\n paths.app,\n `.install-${version}-${randomUUID()}`\n );\n await rm(temporaryDirectory, { recursive: true, force: true });\n await mkdir(temporaryDirectory, { recursive: true, mode: 0o700 });\n\n try {\n if (localRuntime) {\n await cp(path.resolve(localRuntime), temporaryDirectory, {\n recursive: true,\n verbatimSymlinks: true,\n });\n } else {\n const { manifest, url: manifestUrl } = await fetchReleaseManifest(version);\n const artifact = manifest.artifacts[target];\n if (!artifact) {\n throw new Error(`Release ${version} does not support ${target}`);\n }\n const archivePath = path.join(paths.downloads, artifact.file);\n const artifactUrl = new URL(artifact.file, manifestUrl).toString();\n const existing = await stat(archivePath).catch(() => null);\n const cachedArchiveIsValid = existing?.size === artifact.size\n && await sha256(archivePath) === artifact.sha256;\n if (!cachedArchiveIsValid) {\n await rm(archivePath, { force: true });\n try {\n await downloadFile(artifactUrl, archivePath, artifact.size);\n } catch (error) {\n await rm(archivePath, { force: true });\n throw error;\n }\n }\n const downloadedArchive = await stat(archivePath).catch(() => null);\n if (\n downloadedArchive?.size !== artifact.size\n || await sha256(archivePath) !== artifact.sha256\n ) {\n await rm(archivePath, { force: true });\n throw new Error(\"Runtime checksum verification failed\");\n }\n await extract({\n cwd: temporaryDirectory,\n file: archivePath,\n preservePaths: false,\n strict: true,\n });\n }\n await validateRuntimeDirectory(temporaryDirectory, version, target);\n await rename(temporaryDirectory, versionDirectory);\n return versionDirectory;\n } catch (error) {\n await rm(temporaryDirectory, { recursive: true, force: true });\n throw error;\n }\n}\n\nexport async function installRuntime(\n version: string,\n paths: SwitchyPaths\n): Promise<string> {\n return withDirectoryLock(paths.installLock, async () => {\n return installRuntimeUnlocked(version, paths);\n });\n}\n\nexport async function setCurrentVersion(\n paths: SwitchyPaths,\n version: string\n): Promise<void> {\n const value: CurrentVersionRecord = {\n schemaVersion: 1,\n version,\n updatedAt: new Date().toISOString(),\n };\n await writeJsonFileAtomic(paths.currentVersion, value);\n}\n", "const SUPPORTED_TARGETS = [\n \"darwin-arm64\",\n \"darwin-x64\",\n \"linux-arm64\",\n \"linux-x64\",\n \"win32-x64\",\n] as const;\n\nconst supportedTargets = new Set<string>(SUPPORTED_TARGETS);\n\nexport function isSupportedTarget(target: string): boolean {\n return supportedTargets.has(target);\n}\n\nexport function currentTarget(\n platform = process.platform,\n architecture = process.arch\n): string {\n const target = `${platform}-${architecture}`;\n if (!isSupportedTarget(target)) {\n throw new Error(`Switchy does not provide a runtime for ${target}`);\n }\n return target;\n}\n", "import { spawn } from \"node:child_process\";\nimport { cp, mkdir, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { SwitchyPaths } from \"./types.js\";\n\nasync function run(\n executable: string,\n args: string[],\n options: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n quiet?: boolean;\n }\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(executable, args, {\n cwd: options.cwd,\n env: options.env,\n stdio: options.quiet ? \"ignore\" : \"inherit\",\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`${path.basename(executable)} exited with code ${code}`));\n });\n });\n}\n\nexport async function ensurePlaywrightBrowser(\n runtimeDirectory: string,\n paths: SwitchyPaths\n): Promise<void> {\n if (process.env.SWITCHY_SKIP_BROWSER_INSTALL === \"1\") return;\n const cliPath = path.join(\n runtimeDirectory,\n \"node_modules\",\n \"playwright\",\n \"cli.js\"\n );\n if (!(await stat(cliPath).catch(() => null))?.isFile()) {\n throw new Error(\"The packaged runtime is missing the Playwright installer\");\n }\n await run(process.execPath, [cliPath, \"install\", \"chromium\"], {\n cwd: runtimeDirectory,\n env: {\n ...process.env,\n PLAYWRIGHT_BROWSERS_PATH: paths.playwright,\n },\n quiet: true,\n });\n}\n\nexport async function runDatabaseMigration(\n runtimeDirectory: string,\n paths: SwitchyPaths\n): Promise<void> {\n await run(\n process.execPath,\n [path.join(runtimeDirectory, \"bin\", \"migrate.cjs\")],\n {\n cwd: runtimeDirectory,\n env: {\n ...process.env,\n NODE_ENV: \"production\",\n SWITCHY_HOME: paths.root,\n },\n }\n );\n}\n\nexport async function createUpdateSnapshot(\n paths: SwitchyPaths,\n fromVersion: string,\n toVersion: string\n): Promise<string | null> {\n if (!(await stat(paths.productionData).catch(() => null))?.isDirectory()) {\n return null;\n }\n const timestamp = new Date().toISOString().replaceAll(/[:.]/gu, \"-\");\n const snapshotDirectory = path.join(\n paths.updateSnapshots,\n `update-${fromVersion}-to-${toVersion}-${timestamp}`\n );\n await mkdir(snapshotDirectory, { recursive: true, mode: 0o700 });\n await cp(paths.productionData, path.join(snapshotDirectory, \"production\"), {\n recursive: true,\n });\n await writeFile(\n path.join(snapshotDirectory, \"update.json\"),\n `${JSON.stringify({\n fromVersion,\n toVersion,\n createdAt: new Date().toISOString(),\n }, null, 2)}\\n`,\n { mode: 0o600 }\n );\n return snapshotDirectory;\n}\n", "import {\n logsCommand,\n openCommand,\n startCommand,\n statusCommand,\n stopCommand,\n updateCommand,\n versionCommand,\n} from \"./commands.js\";\nimport { DEFAULT_PORT, assertSupportedNodeVersion } from \"./config.js\";\n\nfunction optionValue(args: string[], name: string): string | undefined {\n const index = args.indexOf(name);\n if (index < 0) return undefined;\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new Error(`${name} requires a value`);\n }\n return value;\n}\n\nfunction parsePort(args: string[]): number | undefined {\n const value = optionValue(args, \"--port\");\n if (!value) return undefined;\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new Error(\"--port must be an integer between 1 and 65535\");\n }\n return port;\n}\n\nfunction printHelp(): void {\n console.log(`Switchy CLI\n\nUsage:\n switchy start [--port ${DEFAULT_PORT}] [--foreground] [--app-version <version>]\n switchy stop [--force]\n switchy status\n switchy update\n switchy logs\n switchy open\n switchy version`);\n}\n\nasync function main(): Promise<void> {\n assertSupportedNodeVersion();\n const args = process.argv.slice(2);\n const command = args[0] ?? \"help\";\n switch (command) {\n case \"start\":\n await startCommand({\n version: optionValue(args, \"--app-version\"),\n port: parsePort(args),\n foreground: args.includes(\"--foreground\"),\n });\n break;\n case \"stop\":\n await stopCommand(args.includes(\"--force\"));\n break;\n case \"status\":\n await statusCommand();\n break;\n case \"update\":\n await updateCommand();\n break;\n case \"logs\":\n await logsCommand();\n break;\n case \"open\":\n await openCommand();\n break;\n case \"version\":\n case \"--version\":\n case \"-v\":\n versionCommand();\n break;\n case \"help\":\n case \"--help\":\n case \"-h\":\n printHelp();\n break;\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],
4
+ "sourcesContent": ["import { readFile } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\n\nimport {\n CLI_VERSION,\n DEFAULT_PORT,\n resolveApplicationVersion,\n} from \"./config.js\";\nimport {\n readJsonFile,\n withDirectoryLock,\n} from \"./files.js\";\nimport {\n fetchLiveness,\n getRunningProcess,\n startSwitchyProcess,\n stopSwitchy,\n} from \"./process-manager.js\";\nimport {\n ensureSwitchyDirectories,\n getSwitchyPaths,\n} from \"./paths.js\";\nimport {\n installRuntime,\n resolveLatestStableVersion,\n setCurrentVersion,\n} from \"./release.js\";\nimport {\n createUpdateSnapshot,\n ensurePlaywrightBrowser,\n runDatabaseMigration,\n} from \"./runtime.js\";\nimport type { CurrentVersionRecord } from \"./types.js\";\n\nasync function installedVersion(): Promise<string | null> {\n const paths = getSwitchyPaths();\n const current = await readJsonFile<CurrentVersionRecord>(paths.currentVersion);\n return current?.schemaVersion === 1 ? current.version : null;\n}\n\nexport async function startCommand({\n version,\n port = DEFAULT_PORT,\n foreground = false,\n}: {\n version?: string;\n port?: number;\n foreground?: boolean;\n} = {}): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n await withDirectoryLock(paths.startLock, () =>\n startCommandUnlocked({ paths, version, port, foreground })\n );\n}\n\nasync function startCommandUnlocked({\n paths,\n version,\n port,\n foreground,\n}: {\n paths: ReturnType<typeof getSwitchyPaths>;\n version?: string;\n port: number;\n foreground: boolean;\n}): Promise<void> {\n const existing = await getRunningProcess(paths);\n if (existing) {\n console.log(\n `Switchy ${existing.record.version} is already running at `\n + `http://${existing.record.hostname}:${existing.record.port}`\n );\n return;\n }\n\n const selectedVersion = resolveApplicationVersion(version);\n const runtimeDirectory = await installRuntime(selectedVersion, paths);\n await ensurePlaywrightBrowser(runtimeDirectory, paths);\n await runDatabaseMigration(runtimeDirectory, paths);\n await setCurrentVersion(paths, selectedVersion);\n const record = await startSwitchyProcess({\n paths,\n runtimeDirectory,\n version: selectedVersion,\n port,\n foreground,\n });\n if (!foreground) {\n console.log(\n `Switchy ${selectedVersion} is running at `\n + `http://${record.hostname}:${record.port}`\n );\n }\n}\n\nexport async function stopCommand(force = false): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n await withDirectoryLock(paths.startLock, async () => {\n if (await stopSwitchy(paths, force)) console.log(\"Switchy stopped.\");\n else console.log(\"Switchy is not running.\");\n });\n}\n\nexport async function statusCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n const running = await getRunningProcess(paths);\n if (!running) {\n console.log(\"Switchy is stopped.\");\n return;\n }\n const live = running.live ?? await fetchLiveness(running.record);\n console.log(`Switchy ${running.record.version} is running.`);\n console.log(`URL: http://${running.record.hostname}:${running.record.port}`);\n console.log(`PID: ${running.record.pid}`);\n console.log(`Health: ${live ? \"Live\" : \"Starting or unavailable\"}`);\n}\n\nexport async function updateCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n await ensureSwitchyDirectories(paths);\n await withDirectoryLock(paths.startLock, async () => {\n const latestVersion = await resolveLatestStableVersion();\n const currentVersion = await installedVersion();\n if (currentVersion === latestVersion) {\n console.log(`Switchy ${latestVersion} is already current.`);\n return;\n }\n\n const wasRunning = await getRunningProcess(paths);\n await installRuntime(latestVersion, paths);\n if (wasRunning) await stopSwitchy(paths);\n const snapshot = currentVersion\n ? await createUpdateSnapshot(paths, currentVersion, latestVersion)\n : null;\n try {\n await startCommandUnlocked({\n paths,\n version: latestVersion,\n port: wasRunning?.record.port ?? DEFAULT_PORT,\n foreground: false,\n });\n } catch (error) {\n if (snapshot) {\n console.error(`Update snapshot retained at ${snapshot}`);\n }\n throw error;\n }\n });\n}\n\nexport async function logsCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n const contents = await readFile(paths.logFile, \"utf8\").catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"\";\n throw error;\n });\n const lines = contents.trimEnd().split(\"\\n\");\n console.log(lines.slice(-200).join(\"\\n\"));\n}\n\nexport async function openCommand(): Promise<void> {\n const paths = getSwitchyPaths();\n const running = await getRunningProcess(paths);\n if (!running) throw new Error(\"Switchy is not running\");\n const url = `http://${running.record.hostname}:${running.record.port}`;\n let executable: string;\n let args: string[];\n if (process.platform === \"darwin\") {\n executable = \"open\";\n args = [url];\n } else if (process.platform === \"win32\") {\n executable = \"cmd\";\n args = [\"/c\", \"start\", \"\", url];\n } else {\n executable = \"xdg-open\";\n args = [url];\n }\n const child = spawn(executable, args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n console.log(url);\n}\n\nexport function versionCommand(): void {\n console.log(CLI_VERSION);\n}\n", "{\n \"name\": \"@chandresh726/switchy\",\n \"version\": \"1.0.13\",\n \"description\": \"Install, run, update, and stop the local-first Switchy application.\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"bin\": {\n \"switchy\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"engines\": {\n \"node\": \">=24 <25\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Chandresh726/Switchy.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://github.com/Chandresh726/Switchy#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/Chandresh726/Switchy/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"prepack\": \"pnpm build\"\n },\n \"dependencies\": {\n \"tar\": \"^7.5.22\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.13.3\",\n \"esbuild\": \"^0.28.1\",\n \"typescript\": \"^5.9.3\"\n }\n}\n", "import packageJson from \"../package.json\" with { type: \"json\" };\n\nexport const CLI_VERSION = packageJson.version;\nexport const DEFAULT_PORT = 6767;\nexport const DEFAULT_HOSTNAME = \"127.0.0.1\";\nexport const GITHUB_REPOSITORY = \"Chandresh726/Switchy\";\nexport const RELEASE_MANIFEST_FILE = \"switchy-manifest.json\";\nexport const STARTUP_TIMEOUT_MS = 60_000;\nexport const SHUTDOWN_TIMEOUT_MS = 15_000;\n\nexport function resolveApplicationVersion(requestedVersion?: string): string {\n return requestedVersion ?? CLI_VERSION;\n}\n\nexport function assertSupportedNodeVersion(\n nodeVersion = process.versions.node\n): void {\n if (!/^24\\.\\d+\\.\\d+$/u.test(nodeVersion)) {\n throw new Error(\n `Switchy requires Node.js 24; current version is ${nodeVersion}`\n );\n }\n}\n", "import { randomUUID } from \"node:crypto\";\nimport {\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst LOCK_TIMEOUT_MS = 60_000;\nconst INVALID_LOCK_GRACE_MS = 30_000;\nconst LOCK_OWNER_FILE = \"owner.json\";\n\ninterface LockOwner {\n pid: number;\n token: string;\n}\n\nfunction processIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function readLockOwner(lockPath: string): Promise<LockOwner | null> {\n try {\n const value = JSON.parse(\n await readFile(path.join(lockPath, LOCK_OWNER_FILE), \"utf8\")\n ) as Partial<LockOwner>;\n if (\n !Number.isInteger(value.pid)\n || (value.pid ?? 0) <= 0\n || typeof value.token !== \"string\"\n || value.token.length === 0\n ) {\n return null;\n }\n return value as LockOwner;\n } catch {\n return null;\n }\n}\n\nasync function removeOwnedLock(\n lockPath: string,\n token: string\n): Promise<void> {\n const owner = await readLockOwner(lockPath);\n if (owner?.token === token) {\n await rm(lockPath, { recursive: true, force: true });\n }\n}\n\nexport async function readJsonFile<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf8\")) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nexport async function writeJsonFileAtomic(\n filePath: string,\n value: unknown\n): Promise<void> {\n const temporaryPath = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${randomUUID()}.tmp`\n );\n await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}\\n`, {\n mode: 0o600,\n });\n await rename(temporaryPath, filePath);\n}\n\nexport async function removeFile(filePath: string): Promise<void> {\n await rm(filePath, { force: true });\n}\n\nexport async function withDirectoryLock<T>(\n lockPath: string,\n operation: () => Promise<T>\n): Promise<T> {\n await mkdir(path.dirname(lockPath), { recursive: true, mode: 0o700 });\n const deadline = Date.now() + LOCK_TIMEOUT_MS;\n const token = randomUUID();\n while (true) {\n try {\n await mkdir(lockPath, { mode: 0o700 });\n try {\n await writeFile(\n path.join(lockPath, LOCK_OWNER_FILE),\n `${JSON.stringify({ pid: process.pid, token })}\\n`,\n { mode: 0o600 }\n );\n } catch (error) {\n await rm(lockPath, { recursive: true, force: true });\n throw error;\n }\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const [owner, lockStats] = await Promise.all([\n readLockOwner(lockPath),\n stat(lockPath).catch(() => null),\n ]);\n if (\n (owner && !processIsAlive(owner.pid))\n || (\n !owner\n && lockStats\n && Date.now() - lockStats.mtimeMs > INVALID_LOCK_GRACE_MS\n )\n ) {\n await rm(lockPath, { recursive: true, force: true });\n continue;\n }\n if (Date.now() >= deadline) {\n throw new Error(`Timed out waiting for ${path.basename(lockPath)}`);\n }\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n }\n\n try {\n return await operation();\n } finally {\n await removeOwnedLock(lockPath, token);\n }\n}\n", "import { open } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\n\nimport {\n DEFAULT_HOSTNAME,\n SHUTDOWN_TIMEOUT_MS,\n STARTUP_TIMEOUT_MS,\n} from \"./config.js\";\nimport {\n readJsonFile,\n removeFile,\n writeJsonFileAtomic,\n} from \"./files.js\";\nimport type { ProcessRecord, SwitchyPaths } from \"./types.js\";\n\nfunction processIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function readProcessRecord(\n paths: SwitchyPaths\n): Promise<ProcessRecord | null> {\n const record = await readJsonFile<ProcessRecord>(paths.processRecord)\n .catch(() => null);\n if (\n !record\n || record.schemaVersion !== 1\n || !Number.isInteger(record.pid)\n || record.pid <= 0\n || typeof record.instanceId !== \"string\"\n || record.instanceId.length === 0\n || !/^\\d+\\.\\d+\\.\\d+$/u.test(record.version)\n || record.hostname !== DEFAULT_HOSTNAME\n || !Number.isInteger(record.port)\n || record.port < 1\n || record.port > 65_535\n || !Number.isFinite(Date.parse(record.startedAt))\n ) {\n return null;\n }\n return record;\n}\n\nexport async function fetchLiveness(record: ProcessRecord): Promise<{\n status: \"live\";\n version: string;\n instanceId: string | null;\n} | null> {\n try {\n const response = await fetch(\n `http://${record.hostname}:${record.port}/api/health/live`,\n { signal: AbortSignal.timeout(2_000) }\n );\n if (!response.ok) return null;\n const value = await response.json() as {\n status?: unknown;\n version?: unknown;\n instanceId?: unknown;\n };\n if (value.status !== \"live\" || typeof value.version !== \"string\") return null;\n return {\n status: \"live\",\n version: value.version,\n instanceId: typeof value.instanceId === \"string\" ? value.instanceId : null,\n };\n } catch {\n return null;\n }\n}\n\nexport async function getRunningProcess(\n paths: SwitchyPaths\n): Promise<{ record: ProcessRecord; live: Awaited<ReturnType<typeof fetchLiveness>> } | null> {\n const record = await readProcessRecord(paths);\n if (!record) {\n await removeFile(paths.processRecord);\n return null;\n }\n if (!processIsAlive(record.pid)) {\n await removeFile(paths.processRecord);\n return null;\n }\n return { record, live: await fetchLiveness(record) };\n}\n\nasync function waitForExit(pid: number, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!processIsAlive(pid)) return true;\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n return !processIsAlive(pid);\n}\n\nasync function signalProcessTree(\n pid: number,\n signal: \"SIGTERM\" | \"SIGKILL\"\n): Promise<void> {\n if (process.platform !== \"win32\") {\n try {\n process.kill(-pid, signal);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") throw error;\n try {\n process.kill(pid, signal);\n } catch (fallbackError) {\n if ((fallbackError as NodeJS.ErrnoException).code !== \"ESRCH\") {\n throw fallbackError;\n }\n }\n return;\n }\n }\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\n \"taskkill\",\n [\n \"/PID\",\n String(pid),\n \"/T\",\n \"/F\",\n ],\n { stdio: \"ignore\", windowsHide: true }\n );\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0 || !processIsAlive(pid)) resolve();\n else reject(new Error(`taskkill exited with code ${code}`));\n });\n });\n}\n\nexport async function stopSwitchy(\n paths: SwitchyPaths,\n force = false\n): Promise<boolean> {\n const running = await getRunningProcess(paths);\n if (!running) return false;\n if (\n !force\n && (\n !running.live\n || running.live.instanceId !== running.record.instanceId\n )\n ) {\n throw new Error(\n \"The recorded process could not be verified as Switchy. Re-run with stop --force.\"\n );\n }\n\n await signalProcessTree(\n running.record.pid,\n force ? \"SIGKILL\" : \"SIGTERM\"\n );\n if (!(await waitForExit(running.record.pid, SHUTDOWN_TIMEOUT_MS))) {\n throw new Error(\"Switchy did not stop in time. Re-run with stop --force.\");\n }\n await removeFile(paths.processRecord);\n return true;\n}\n\nasync function waitUntilReady(record: ProcessRecord): Promise<void> {\n const deadline = Date.now() + STARTUP_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (!processIsAlive(record.pid)) {\n throw new Error(\"Switchy exited before becoming ready\");\n }\n try {\n const response = await fetch(\n `http://${record.hostname}:${record.port}/api/health/ready`,\n { signal: AbortSignal.timeout(2_000) }\n );\n if (response.ok) return;\n } catch {\n // The server may still be starting.\n }\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n throw new Error(\"Switchy did not become ready within 60 seconds\");\n}\n\nexport async function startSwitchyProcess({\n paths,\n runtimeDirectory,\n version,\n port,\n foreground,\n}: {\n paths: SwitchyPaths;\n runtimeDirectory: string;\n version: string;\n port: number;\n foreground: boolean;\n}): Promise<ProcessRecord> {\n const instanceId = randomUUID();\n const environment: NodeJS.ProcessEnv = {\n ...process.env,\n NODE_ENV: \"production\",\n HOSTNAME: DEFAULT_HOSTNAME,\n PORT: String(port),\n SWITCHY_HOME: paths.root,\n SWITCHY_INSTANCE_ID: instanceId,\n PLAYWRIGHT_BROWSERS_PATH: paths.playwright,\n };\n const serverPath = path.join(runtimeDirectory, \"server.js\");\n const logHandle = foreground\n ? null\n : await open(paths.logFile, \"a\", 0o600);\n const child = spawn(process.execPath, [serverPath], {\n cwd: runtimeDirectory,\n detached: !foreground,\n env: environment,\n stdio: foreground\n ? \"inherit\"\n : [\"ignore\", logHandle!.fd, logHandle!.fd],\n });\n await logHandle?.close();\n if (!child.pid) throw new Error(\"Switchy did not return a process ID\");\n\n const record: ProcessRecord = {\n schemaVersion: 1,\n pid: child.pid,\n instanceId,\n version,\n hostname: DEFAULT_HOSTNAME,\n port,\n startedAt: new Date().toISOString(),\n };\n await writeJsonFileAtomic(paths.processRecord, record);\n\n if (foreground) {\n await new Promise<void>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0 || code === null) resolve();\n else reject(new Error(`Switchy exited with code ${code}`));\n });\n }).finally(() => removeFile(paths.processRecord));\n return record;\n }\n\n child.unref();\n try {\n await waitUntilReady(record);\n return record;\n } catch (error) {\n if (processIsAlive(record.pid)) {\n await signalProcessTree(record.pid, \"SIGTERM\");\n if (!(await waitForExit(record.pid, SHUTDOWN_TIMEOUT_MS))) {\n await signalProcessTree(record.pid, \"SIGKILL\");\n if (!(await waitForExit(record.pid, SHUTDOWN_TIMEOUT_MS))) {\n throw new Error(\n `Switchy failed to start and process ${record.pid} could not be stopped`,\n { cause: error }\n );\n }\n }\n }\n await removeFile(paths.processRecord);\n throw error;\n }\n}\n", "import { mkdir } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport type { SwitchyPaths } from \"./types.js\";\n\nexport function getSwitchyPaths(\n configuredRoot = process.env.SWITCHY_HOME\n): SwitchyPaths {\n const override = configuredRoot?.trim();\n if (override && !path.isAbsolute(override)) {\n throw new Error(\"SWITCHY_HOME must be an absolute path\");\n }\n const root = override\n ? path.normalize(override)\n : path.join(os.homedir(), \".switchy\");\n const data = path.join(root, \"data\");\n const app = path.join(root, \"app\");\n const runtime = path.join(root, \"runtime\");\n const logs = path.join(root, \"logs\");\n const cache = path.join(root, \"cache\");\n return {\n root,\n data,\n productionData: path.join(data, \"production\"),\n app,\n versions: path.join(app, \"versions\"),\n currentVersion: path.join(app, \"current.json\"),\n runtime,\n processRecord: path.join(runtime, \"process.json\"),\n installLock: path.join(runtime, \"install.lock\"),\n startLock: path.join(runtime, \"start.lock\"),\n logs,\n logFile: path.join(logs, \"switchy.log\"),\n cache,\n downloads: path.join(cache, \"downloads\"),\n playwright: path.join(cache, \"playwright\"),\n updateSnapshots: path.join(root, \"update-snapshots\"),\n };\n}\n\nexport async function ensureSwitchyDirectories(paths: SwitchyPaths): Promise<void> {\n for (const directory of [\n paths.root,\n paths.data,\n paths.app,\n paths.versions,\n paths.runtime,\n paths.logs,\n paths.cache,\n paths.downloads,\n paths.playwright,\n paths.updateSnapshots,\n ]) {\n await mkdir(directory, { recursive: true, mode: 0o700 });\n }\n}\n", "import { createHash, randomUUID } from \"node:crypto\";\nimport { createReadStream, createWriteStream } from \"node:fs\";\nimport {\n cp,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n} from \"node:fs/promises\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport path from \"node:path\";\n\nimport { extract } from \"tar\";\n\nimport {\n CLI_VERSION,\n GITHUB_REPOSITORY,\n RELEASE_MANIFEST_FILE,\n} from \"./config.js\";\nimport {\n withDirectoryLock,\n writeJsonFileAtomic,\n} from \"./files.js\";\nimport {\n currentTarget,\n isSupportedTarget,\n} from \"./platform.js\";\nimport type {\n CurrentVersionRecord,\n ReleaseArtifact,\n ReleaseManifest,\n SwitchyPaths,\n} from \"./types.js\";\n\nconst STABLE_VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+$/u;\n\nexport function assertStableVersion(version: string): string {\n if (!STABLE_VERSION_PATTERN.test(version)) {\n throw new Error(`Invalid stable Switchy version: ${version}`);\n }\n return version;\n}\n\nfunction releaseManifestUrl(version: string): string {\n return process.env.SWITCHY_MANIFEST_URL\n ?? `https://github.com/${GITHUB_REPOSITORY}/releases/download/v${version}/${RELEASE_MANIFEST_FILE}`;\n}\n\nfunction assertReleaseArtifact(value: unknown): ReleaseArtifact {\n if (\n typeof value !== \"object\"\n || value === null\n || typeof (value as ReleaseArtifact).file !== \"string\"\n || path.basename((value as ReleaseArtifact).file)\n !== (value as ReleaseArtifact).file\n || !/^switchy-\\d+\\.\\d+\\.\\d+-(?:darwin|linux|win32)-(?:arm64|x64)\\.tar\\.gz$/u\n .test((value as ReleaseArtifact).file)\n || typeof (value as ReleaseArtifact).sha256 !== \"string\"\n || !/^[a-f0-9]{64}$/u.test((value as ReleaseArtifact).sha256)\n || typeof (value as ReleaseArtifact).size !== \"number\"\n || !Number.isSafeInteger((value as ReleaseArtifact).size)\n || (value as ReleaseArtifact).size <= 0\n || (value as ReleaseArtifact).size > 1_000_000_000\n ) {\n throw new Error(\"Release manifest contains an invalid artifact\");\n }\n return value as ReleaseArtifact;\n}\n\nexport function parseReleaseManifest(value: unknown): ReleaseManifest {\n if (\n typeof value !== \"object\"\n || value === null\n || (value as ReleaseManifest).schemaVersion !== 1\n || typeof (value as ReleaseManifest).version !== \"string\"\n || !STABLE_VERSION_PATTERN.test((value as ReleaseManifest).version)\n || typeof (value as ReleaseManifest).publishedAt !== \"string\"\n || typeof (value as ReleaseManifest).nodeVersion !== \"string\"\n || typeof (value as ReleaseManifest).artifacts !== \"object\"\n || (value as ReleaseManifest).artifacts === null\n ) {\n throw new Error(\"Release manifest is invalid\");\n }\n const manifest = value as ReleaseManifest;\n if (\n !Number.isFinite(Date.parse(manifest.publishedAt))\n || !/^\\d+\\.\\d+\\.\\d+$/u.test(manifest.nodeVersion)\n || Array.isArray(manifest.artifacts)\n ) {\n throw new Error(\"Release manifest is invalid\");\n }\n for (const [target, valueArtifact] of Object.entries(manifest.artifacts)) {\n if (!isSupportedTarget(target)) {\n throw new Error(`Release manifest contains unsupported target ${target}`);\n }\n const artifact = assertReleaseArtifact(valueArtifact);\n if (artifact.file !== `switchy-${manifest.version}-${target}.tar.gz`) {\n throw new Error(\"Release manifest contains inconsistent artifact metadata\");\n }\n }\n return manifest;\n}\n\nasync function fetchJson(url: string): Promise<unknown> {\n const response = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n \"User-Agent\": `switchy-cli/${CLI_VERSION}`,\n },\n signal: AbortSignal.timeout(30_000),\n });\n if (!response.ok) {\n throw new Error(`Request failed with ${response.status}: ${url}`);\n }\n return response.json() as Promise<unknown>;\n}\n\nasync function fetchReleaseManifest(version: string): Promise<{\n manifest: ReleaseManifest;\n url: string;\n}> {\n const url = releaseManifestUrl(version);\n const manifest = parseReleaseManifest(await fetchJson(url));\n if (manifest.version !== version) {\n throw new Error(\n `Release manifest version ${manifest.version} does not match ${version}`\n );\n }\n return { manifest, url };\n}\n\nexport async function resolveLatestStableVersion(): Promise<string> {\n const override = process.env.SWITCHY_LATEST_VERSION?.trim();\n if (override) return assertStableVersion(override);\n const value = await fetchJson(\n `https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest`\n );\n const tagName = typeof value === \"object\" && value !== null\n ? (value as { tag_name?: unknown }).tag_name\n : null;\n if (typeof tagName !== \"string\" || !/^v\\d+\\.\\d+\\.\\d+$/u.test(tagName)) {\n throw new Error(\"Latest GitHub release does not contain a stable version tag\");\n }\n return tagName.slice(1);\n}\n\nasync function sha256(filePath: string): Promise<string> {\n const hash = createHash(\"sha256\");\n for await (const chunk of createReadStream(filePath)) {\n hash.update(chunk);\n }\n return hash.digest(\"hex\");\n}\n\nasync function downloadFile(\n url: string,\n destination: string,\n expectedSize: number\n): Promise<void> {\n const response = await fetch(url, {\n headers: { \"User-Agent\": `switchy-cli/${CLI_VERSION}` },\n redirect: \"follow\",\n signal: AbortSignal.timeout(300_000),\n });\n if (!response.ok || !response.body) {\n throw new Error(`Runtime download failed with ${response.status}`);\n }\n const contentLengthHeader = response.headers.get(\"content-length\");\n const contentLength = Number(contentLengthHeader);\n if (\n contentLengthHeader !== null\n && Number.isFinite(contentLength)\n && contentLength !== expectedSize\n ) {\n throw new Error(\"Runtime download size does not match the release manifest\");\n }\n let downloadedSize = 0;\n const sizeLimit = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n downloadedSize += chunk.length;\n if (downloadedSize > expectedSize) {\n callback(new Error(\"Runtime download exceeded its declared size\"));\n return;\n }\n callback(null, chunk);\n },\n });\n await pipeline(\n Readable.fromWeb(response.body as import(\"node:stream/web\").ReadableStream),\n sizeLimit,\n createWriteStream(destination, { mode: 0o600 })\n );\n if (downloadedSize !== expectedSize) {\n throw new Error(\"Runtime download size does not match the release manifest\");\n }\n}\n\nasync function validateRuntimeDirectory(\n runtimeDirectory: string,\n version: string,\n target: string\n): Promise<void> {\n for (const relativePath of [\n \"server.js\",\n \"bin/migrate.cjs\",\n \"drizzle/meta/_journal.json\",\n \"switchy-runtime.json\",\n ]) {\n const fileStats = await stat(path.join(runtimeDirectory, relativePath))\n .catch(() => null);\n if (!fileStats?.isFile()) {\n throw new Error(`Runtime is missing ${relativePath}`);\n }\n }\n let metadata: {\n schemaVersion?: unknown;\n version?: unknown;\n target?: unknown;\n nodeVersion?: unknown;\n builtAt?: unknown;\n };\n try {\n metadata = JSON.parse(\n await readFile(\n path.join(runtimeDirectory, \"switchy-runtime.json\"),\n \"utf8\"\n )\n ) as typeof metadata;\n } catch {\n throw new Error(\"Runtime metadata is invalid\");\n }\n if (\n metadata.schemaVersion !== 1\n || metadata.version !== version\n || metadata.target !== target\n || typeof metadata.nodeVersion !== \"string\"\n || !/^\\d+\\.\\d+\\.\\d+$/u.test(metadata.nodeVersion)\n || metadata.nodeVersion.split(\".\", 1)[0]\n !== process.versions.node.split(\".\", 1)[0]\n || typeof metadata.builtAt !== \"string\"\n || !Number.isFinite(Date.parse(metadata.builtAt))\n ) {\n throw new Error(\"Runtime metadata does not match this installation\");\n }\n}\n\nasync function installRuntimeUnlocked(\n version: string,\n paths: SwitchyPaths\n): Promise<string> {\n assertStableVersion(version);\n const target = currentTarget();\n const versionDirectory = path.join(paths.versions, version);\n if ((await stat(versionDirectory).catch(() => null))?.isDirectory()) {\n try {\n await validateRuntimeDirectory(versionDirectory, version, target);\n return versionDirectory;\n } catch {\n await rm(versionDirectory, { recursive: true, force: true });\n }\n }\n\n const localRuntime = process.env.SWITCHY_RUNTIME_SOURCE?.trim();\n const temporaryDirectory = path.join(\n paths.app,\n `.install-${version}-${randomUUID()}`\n );\n await rm(temporaryDirectory, { recursive: true, force: true });\n await mkdir(temporaryDirectory, { recursive: true, mode: 0o700 });\n\n try {\n if (localRuntime) {\n await cp(path.resolve(localRuntime), temporaryDirectory, {\n recursive: true,\n verbatimSymlinks: true,\n });\n } else {\n const { manifest, url: manifestUrl } = await fetchReleaseManifest(version);\n const artifact = manifest.artifacts[target];\n if (!artifact) {\n throw new Error(`Release ${version} does not support ${target}`);\n }\n const archivePath = path.join(paths.downloads, artifact.file);\n const artifactUrl = new URL(artifact.file, manifestUrl).toString();\n const existing = await stat(archivePath).catch(() => null);\n const cachedArchiveIsValid = existing?.size === artifact.size\n && await sha256(archivePath) === artifact.sha256;\n if (!cachedArchiveIsValid) {\n await rm(archivePath, { force: true });\n try {\n await downloadFile(artifactUrl, archivePath, artifact.size);\n } catch (error) {\n await rm(archivePath, { force: true });\n throw error;\n }\n }\n const downloadedArchive = await stat(archivePath).catch(() => null);\n if (\n downloadedArchive?.size !== artifact.size\n || await sha256(archivePath) !== artifact.sha256\n ) {\n await rm(archivePath, { force: true });\n throw new Error(\"Runtime checksum verification failed\");\n }\n await extract({\n cwd: temporaryDirectory,\n file: archivePath,\n preservePaths: false,\n strict: true,\n });\n }\n await validateRuntimeDirectory(temporaryDirectory, version, target);\n await rename(temporaryDirectory, versionDirectory);\n return versionDirectory;\n } catch (error) {\n await rm(temporaryDirectory, { recursive: true, force: true });\n throw error;\n }\n}\n\nexport async function installRuntime(\n version: string,\n paths: SwitchyPaths\n): Promise<string> {\n return withDirectoryLock(paths.installLock, async () => {\n return installRuntimeUnlocked(version, paths);\n });\n}\n\nexport async function setCurrentVersion(\n paths: SwitchyPaths,\n version: string\n): Promise<void> {\n const value: CurrentVersionRecord = {\n schemaVersion: 1,\n version,\n updatedAt: new Date().toISOString(),\n };\n await writeJsonFileAtomic(paths.currentVersion, value);\n}\n", "const SUPPORTED_TARGETS = [\n \"darwin-arm64\",\n \"darwin-x64\",\n \"linux-arm64\",\n \"linux-x64\",\n \"win32-x64\",\n] as const;\n\nconst supportedTargets = new Set<string>(SUPPORTED_TARGETS);\n\nexport function isSupportedTarget(target: string): boolean {\n return supportedTargets.has(target);\n}\n\nexport function currentTarget(\n platform = process.platform,\n architecture = process.arch\n): string {\n const target = `${platform}-${architecture}`;\n if (!isSupportedTarget(target)) {\n throw new Error(`Switchy does not provide a runtime for ${target}`);\n }\n return target;\n}\n", "import { spawn } from \"node:child_process\";\nimport { cp, mkdir, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { SwitchyPaths } from \"./types.js\";\n\nasync function run(\n executable: string,\n args: string[],\n options: {\n cwd: string;\n env: NodeJS.ProcessEnv;\n quiet?: boolean;\n }\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(executable, args, {\n cwd: options.cwd,\n env: options.env,\n stdio: options.quiet ? \"ignore\" : \"inherit\",\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`${path.basename(executable)} exited with code ${code}`));\n });\n });\n}\n\nexport async function ensurePlaywrightBrowser(\n runtimeDirectory: string,\n paths: SwitchyPaths\n): Promise<void> {\n if (process.env.SWITCHY_SKIP_BROWSER_INSTALL === \"1\") return;\n const cliPath = path.join(\n runtimeDirectory,\n \"node_modules\",\n \"playwright\",\n \"cli.js\"\n );\n if (!(await stat(cliPath).catch(() => null))?.isFile()) {\n throw new Error(\"The packaged runtime is missing the Playwright installer\");\n }\n await run(process.execPath, [cliPath, \"install\", \"chromium\"], {\n cwd: runtimeDirectory,\n env: {\n ...process.env,\n PLAYWRIGHT_BROWSERS_PATH: paths.playwright,\n },\n quiet: true,\n });\n}\n\nexport async function runDatabaseMigration(\n runtimeDirectory: string,\n paths: SwitchyPaths\n): Promise<void> {\n await run(\n process.execPath,\n [path.join(runtimeDirectory, \"bin\", \"migrate.cjs\")],\n {\n cwd: runtimeDirectory,\n env: {\n ...process.env,\n NODE_ENV: \"production\",\n SWITCHY_HOME: paths.root,\n },\n }\n );\n}\n\nexport async function createUpdateSnapshot(\n paths: SwitchyPaths,\n fromVersion: string,\n toVersion: string\n): Promise<string | null> {\n if (!(await stat(paths.productionData).catch(() => null))?.isDirectory()) {\n return null;\n }\n const timestamp = new Date().toISOString().replaceAll(/[:.]/gu, \"-\");\n const snapshotDirectory = path.join(\n paths.updateSnapshots,\n `update-${fromVersion}-to-${toVersion}-${timestamp}`\n );\n await mkdir(snapshotDirectory, { recursive: true, mode: 0o700 });\n await cp(paths.productionData, path.join(snapshotDirectory, \"production\"), {\n recursive: true,\n });\n await writeFile(\n path.join(snapshotDirectory, \"update.json\"),\n `${JSON.stringify({\n fromVersion,\n toVersion,\n createdAt: new Date().toISOString(),\n }, null, 2)}\\n`,\n { mode: 0o600 }\n );\n return snapshotDirectory;\n}\n", "import {\n logsCommand,\n openCommand,\n startCommand,\n statusCommand,\n stopCommand,\n updateCommand,\n versionCommand,\n} from \"./commands.js\";\nimport { DEFAULT_PORT, assertSupportedNodeVersion } from \"./config.js\";\n\nfunction optionValue(args: string[], name: string): string | undefined {\n const index = args.indexOf(name);\n if (index < 0) return undefined;\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) {\n throw new Error(`${name} requires a value`);\n }\n return value;\n}\n\nfunction parsePort(args: string[]): number | undefined {\n const value = optionValue(args, \"--port\");\n if (!value) return undefined;\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new Error(\"--port must be an integer between 1 and 65535\");\n }\n return port;\n}\n\nfunction printHelp(): void {\n console.log(`Switchy CLI\n\nUsage:\n switchy start [--port ${DEFAULT_PORT}] [--foreground] [--app-version <version>]\n switchy stop [--force]\n switchy status\n switchy update\n switchy logs\n switchy open\n switchy version`);\n}\n\nasync function main(): Promise<void> {\n assertSupportedNodeVersion();\n const args = process.argv.slice(2);\n const command = args[0] ?? \"help\";\n switch (command) {\n case \"start\":\n await startCommand({\n version: optionValue(args, \"--app-version\"),\n port: parsePort(args),\n foreground: args.includes(\"--foreground\"),\n });\n break;\n case \"stop\":\n await stopCommand(args.includes(\"--force\"));\n break;\n case \"status\":\n await statusCommand();\n break;\n case \"update\":\n await updateCommand();\n break;\n case \"logs\":\n await logsCommand();\n break;\n case \"open\":\n await openCommand();\n break;\n case \"version\":\n case \"--version\":\n case \"-v\":\n versionCommand();\n break;\n case \"help\":\n case \"--help\":\n case \"-h\":\n printHelp();\n break;\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],
5
5
  "mappings": "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",
6
6
  "names": ["readFile", "spawn", "path", "randomUUID", "processIsAlive", "randomUUID", "path", "mkdir", "path", "randomUUID", "mkdir", "readFile", "rename", "rm", "stat", "path", "path", "stat", "readFile", "rm", "randomUUID", "mkdir", "rename", "spawn", "cp", "mkdir", "stat", "writeFile", "path", "readFile", "spawn"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chandresh726/switchy",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Install, run, update, and stop the local-first Switchy application.",
5
5
  "license": "MIT",
6
6
  "type": "module",