repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/klaus/src/pi-ext-browser-open.ts
Raw// Generated by scripts/codegen.mjs. DO NOT EDIT.
// ============================================================================
// THIS FILE IS GENERATED. DO NOT EDIT IT DIRECTLY.
// Instead, edit scripts/codegen/templates/pi-ext-browser-open.ts.
// Then run: mise run //:codegen
// ============================================================================
import { spawn } from "node:child_process";
export type BrowserUrlOpenerOptions = {
platform?: NodeJS.Platform;
env?: NodeJS.ProcessEnv;
timeoutMs?: number;
candidates?: BrowserOpenerCandidate[];
};
export type BrowserOpenerCandidate = {
command: string;
args: string[];
windowsHide?: boolean;
};
/** Open a URL through platform-native handlers. Returns selected command. */
export async function openUrl(
url: string,
options: BrowserUrlOpenerOptions,
): Promise<string | undefined> {
for (const candidate of options.candidates ??
openerCandidates(url, options)) {
if (await trySpawn(candidate, options)) return candidate.command;
}
return undefined;
}
export function openerCandidates(
url: string,
options: Pick<BrowserUrlOpenerOptions, "platform" | "env"> = {},
): BrowserOpenerCandidate[] {
const platform = options.platform ?? process.platform;
const env = options.env ?? process.env;
if (env.TERMUX_VERSION) return [{ command: "termux-open-url", args: [url] }];
if (platform === "darwin") return [{ command: "open", args: [url] }];
if (platform === "win32")
return [
{
command: "rundll32.exe",
args: ["url.dll,FileProtocolHandler", url],
windowsHide: true,
},
];
return [
{ command: "xdg-open", args: [url] },
{ command: "wslview", args: [url] },
{ command: "gio", args: ["open", url] },
];
}
function trySpawn(
candidate: BrowserOpenerCandidate,
options: BrowserUrlOpenerOptions,
): Promise<boolean> {
return new Promise((resolve) => {
const child = spawn(candidate.command, candidate.args, {
detached: true,
stdio: "ignore",
windowsHide: candidate.windowsHide ?? true,
env: options.env,
});
let settled = false;
const done = (ok: boolean) => {
if (settled) return;
settled = true;
resolve(ok);
};
child.once("error", () => done(false));
child.once("spawn", () => {
child.unref();
done(true);
});
if (options.timeoutMs !== undefined)
setTimeout(() => done(false), options.timeoutMs).unref();
});
}