refactor(src): split oversized modules
This commit is contained in:
112
src/daemon/launchd-plist.ts
Normal file
112
src/daemon/launchd-plist.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
const plistEscape = (value: string): string =>
|
||||
value
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
|
||||
const plistUnescape = (value: string): string =>
|
||||
value
|
||||
.replaceAll("'", "'")
|
||||
.replaceAll(""", '"')
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll("&", "&");
|
||||
|
||||
const renderEnvDict = (
|
||||
env: Record<string, string | undefined> | undefined,
|
||||
): string => {
|
||||
if (!env) return "";
|
||||
const entries = Object.entries(env).filter(
|
||||
([, value]) => typeof value === "string" && value.trim(),
|
||||
);
|
||||
if (entries.length === 0) return "";
|
||||
const items = entries
|
||||
.map(
|
||||
([key, value]) =>
|
||||
`\n <key>${plistEscape(key)}</key>\n <string>${plistEscape(value?.trim() ?? "")}</string>`,
|
||||
)
|
||||
.join("");
|
||||
return `\n <key>EnvironmentVariables</key>\n <dict>${items}\n </dict>`;
|
||||
};
|
||||
|
||||
export async function readLaunchAgentProgramArgumentsFromFile(
|
||||
plistPath: string,
|
||||
): Promise<{
|
||||
programArguments: string[];
|
||||
workingDirectory?: string;
|
||||
environment?: Record<string, string>;
|
||||
sourcePath?: string;
|
||||
} | null> {
|
||||
try {
|
||||
const plist = await fs.readFile(plistPath, "utf8");
|
||||
const programMatch = plist.match(
|
||||
/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/i,
|
||||
);
|
||||
if (!programMatch) return null;
|
||||
const args = Array.from(
|
||||
programMatch[1].matchAll(/<string>([\s\S]*?)<\/string>/gi),
|
||||
).map((match) => plistUnescape(match[1] ?? "").trim());
|
||||
const workingDirMatch = plist.match(
|
||||
/<key>WorkingDirectory<\/key>\s*<string>([\s\S]*?)<\/string>/i,
|
||||
);
|
||||
const workingDirectory = workingDirMatch
|
||||
? plistUnescape(workingDirMatch[1] ?? "").trim()
|
||||
: "";
|
||||
const envMatch = plist.match(
|
||||
/<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/i,
|
||||
);
|
||||
const environment: Record<string, string> = {};
|
||||
if (envMatch) {
|
||||
for (const pair of envMatch[1].matchAll(
|
||||
/<key>([\s\S]*?)<\/key>\s*<string>([\s\S]*?)<\/string>/gi,
|
||||
)) {
|
||||
const key = plistUnescape(pair[1] ?? "").trim();
|
||||
if (!key) continue;
|
||||
const value = plistUnescape(pair[2] ?? "").trim();
|
||||
environment[key] = value;
|
||||
}
|
||||
}
|
||||
return {
|
||||
programArguments: args.filter(Boolean),
|
||||
...(workingDirectory ? { workingDirectory } : {}),
|
||||
...(Object.keys(environment).length > 0 ? { environment } : {}),
|
||||
sourcePath: plistPath,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildLaunchAgentPlist({
|
||||
label,
|
||||
comment,
|
||||
programArguments,
|
||||
workingDirectory,
|
||||
stdoutPath,
|
||||
stderrPath,
|
||||
environment,
|
||||
}: {
|
||||
label: string;
|
||||
comment?: string;
|
||||
programArguments: string[];
|
||||
workingDirectory?: string;
|
||||
stdoutPath: string;
|
||||
stderrPath: string;
|
||||
environment?: Record<string, string | undefined>;
|
||||
}): string {
|
||||
const argsXml = programArguments
|
||||
.map((arg) => `\n <string>${plistEscape(arg)}</string>`)
|
||||
.join("");
|
||||
const workingDirXml = workingDirectory
|
||||
? `\n <key>WorkingDirectory</key>\n <string>${plistEscape(workingDirectory)}</string>`
|
||||
: "";
|
||||
const commentXml = comment?.trim()
|
||||
? `\n <key>Comment</key>\n <string>${plistEscape(comment.trim())}</string>`
|
||||
: "";
|
||||
const envXml = renderEnvDict(environment);
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n <dict>\n <key>Label</key>\n <string>${plistEscape(label)}</string>\n ${commentXml}\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>ProgramArguments</key>\n <array>${argsXml}\n </array>\n ${workingDirXml}\n <key>StandardOutPath</key>\n <string>${plistEscape(stdoutPath)}</string>\n <key>StandardErrorPath</key>\n <string>${plistEscape(stderrPath)}</string>${envXml}\n </dict>\n</plist>\n`;
|
||||
}
|
||||
Reference in New Issue
Block a user