chore: initialize from frontend template 4dc033c

This commit is contained in:
DongHyeonka
2026-08-13 18:23:26 +09:00
commit 40107eec84
897 changed files with 234824 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
import { spawn, type ChildProcess } from "node:child_process";
export type ProviderProcessInput = Readonly<{
executable: string;
arguments: readonly string[];
environment: NodeJS.ProcessEnv;
timeoutMs: number;
}>;
type ProviderChild = Pick<ChildProcess, "kill" | "once" | "pid">;
export async function runProviderProcess(
input: ProviderProcessInput,
dependencies: Readonly<{
spawnChild?: (input: ProviderProcessInput) => ProviderChild;
setTimer?: (callback: () => void, milliseconds: number) => ReturnType<typeof setTimeout>;
clearTimer?: (timer: ReturnType<typeof setTimeout>) => void;
killProcessGroup?: (child: ProviderChild) => void;
}> = {},
): Promise<void> {
if (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs <= 0) {
throw new TypeError("provider process timeout must be a positive integer");
}
const child = (dependencies.spawnChild ?? defaultSpawn)(input);
const setTimer = dependencies.setTimer ?? setTimeout;
const clearTimer = dependencies.clearTimer ?? clearTimeout;
const killProcessGroup = dependencies.killProcessGroup ?? defaultKillProcessGroup;
await new Promise<void>((resolve, reject) => {
let settled = false;
let timedOut = false;
const killFailures: unknown[] = [];
const settle = (error?: Error): void => {
if (settled) return;
settled = true;
clearTimer(timer);
error ? reject(error) : resolve();
};
const timer = setTimer(() => {
timedOut = true;
try {
killProcessGroup(child);
} catch (groupError) {
killFailures.push(groupError);
try {
child.kill("SIGKILL");
} catch (fallbackError) {
killFailures.push(fallbackError);
}
}
}, input.timeoutMs);
child.once("error", (error: Error) => {
if (!timedOut) settle(error);
});
child.once("close", (code: number | null, signal: NodeJS.Signals | null) => {
if (timedOut) {
const timeoutError = new Error(
"sandboxed external provider command timed out after process close",
);
settle(
killFailures.length === 0
? timeoutError
: new AggregateError(
[timeoutError, ...killFailures],
"sandboxed external provider timed out and process-group kill failed before close",
{ cause: killFailures.at(-1) },
),
);
} else if (code === 0 && signal === null) {
settle();
} else {
settle(
new Error(
`sandboxed external provider failed: exit=${code ?? "none"}, signal=${signal ?? "none"}`,
),
);
}
});
});
}
function defaultSpawn(input: ProviderProcessInput): ProviderChild {
return spawn(input.executable, [...input.arguments], {
env: input.environment,
stdio: "inherit",
detached: true,
});
}
function defaultKillProcessGroup(child: ProviderChild): void {
if (child.pid && child.pid > 0) {
try {
process.kill(-child.pid, "SIGKILL");
return;
} catch (error) {
if (!hasErrorCode(error, "ESRCH")) throw error;
}
}
child.kill("SIGKILL");
}
function hasErrorCode(error: unknown, code: string): boolean {
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
}