Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/gratis/src/state.ts

Raw
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import path from "node:path";

/**
 * Where routing measurements persist across restarts. Same convention as the
 * web extension: XDG_STATE_HOME wins, else the platform's user state dir.
 */
export function routingStatePath(
	platform: NodeJS.Platform = process.platform,
	env: NodeJS.ProcessEnv = process.env,
	home = homedir(),
): string {
	const paths = platform === "win32" ? path.win32 : path.posix;
	const root = env.XDG_STATE_HOME
		? paths.join(env.XDG_STATE_HOME, "pi")
		: platform === "win32"
			? paths.join(
					env.LOCALAPPDATA ?? paths.join(home, "AppData", "Local"),
					"pi",
				)
			: platform === "darwin"
				? paths.join(home, "Library", "Application Support", "pi")
				: paths.join(home, ".local", "state", "pi");
	return paths.join(root, "gratis", "routing.json");
}

/** Missing, unreadable, or corrupt state is simply absent. */
export async function loadRoutingState(file: string): Promise<unknown> {
	try {
		return JSON.parse(await readFile(file, "utf8"));
	} catch {
		return undefined;
	}
}

/** Owner-only, atomic: write a sibling temp file, then rename over the target. */
export async function saveRoutingState(
	file: string,
	state: unknown,
): Promise<void> {
	await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
	const temporary = `${file}.${process.pid}.tmp`;
	await writeFile(temporary, JSON.stringify(state), { mode: 0o600 });
	await rename(temporary, file);
}