UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands

43 lines (40 loc) 1.49 kB
import { spawn } from 'node:child_process'; import { platform } from 'node:os'; import { LunoraError } from '@lunora/errors'; const platformCommand = () => { const os = platform(); if (os === "darwin") { return { args: [], command: "open" }; } if (os === "win32") { return { args: ["/c", "start", ""], command: "cmd" }; } return { args: [], command: "xdg-open" }; }; const escapeForCmd = (url) => url.replaceAll("%", "%25").replaceAll("&", "%26").replaceAll("|", "%7C").replaceAll("^", "%5E").replaceAll("<", "%3C").replaceAll(">", "%3E").replaceAll("(", "%28").replaceAll(")", "%29").replaceAll('"', "%22").replaceAll("!", "%21"); const platformOpener = (url) => new Promise((resolve, reject) => { const { args, command } = platformCommand(); const safeUrl = platform() === "win32" ? escapeForCmd(url) : url; const child = spawn(command, [...args, safeUrl], { detached: true, stdio: "ignore" }); child.once("error", (error) => { reject(error); }); child.once("spawn", () => { child.unref(); resolve(); }); }); const openUrl = async (url, options = {}) => { let parsed; try { parsed = new URL(url); } catch { throw new LunoraError("INTERNAL", `Invalid URL: ${url}`); } if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { throw new LunoraError("INTERNAL", `Refusing to open non-http(s) URL: ${url}`); } const opener = options.opener ?? platformOpener; await opener(url); }; export { openUrl as o };