function escapeSystemdEnvValue(value) {
    const s = String(value ?? '');
    if (/^[A-Za-z0-9_./:@%+-]+$/.test(s))
        return s.includes('%') ? s.replaceAll('%', '%%') : s;
    const escaped = s
        .replaceAll('\\', '\\\\')
        .replaceAll('%', '%%')
        .replaceAll('\r\n', '\n')
        .replaceAll('\r', '\n')
        .replaceAll('\n', '\\n')
        .replaceAll('"', '\\"');
    return `"${escaped}"`;
}
function assertSingleLineSystemdField(fieldName, value) {
    const s = String(value ?? '');
    if (s.includes('\n') || s.includes('\r')) {
        throw new Error(`${fieldName} must not contain newlines`);
    }
    return s;
}
function escapeSystemdExecArg(value) {
    const s = String(value ?? '');
    if (/^[A-Za-z0-9_./:@%+=-]+$/.test(s))
        return s.includes('%') ? s.replaceAll('%', '%%') : s;
    const escaped = s
        .replaceAll('\\', '\\\\')
        .replaceAll('%', '%%')
        .replaceAll('\r\n', '\n')
        .replaceAll('\r', '\n')
        .replaceAll('\n', '\\n')
        .replaceAll('"', '\\"');
    return `"${escaped}"`;
}
function systemdEnvLines(env) {
    const entries = Object.entries(env ?? {}).filter(([k]) => String(k ?? '').trim());
    const envKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
    const lines = [];
    for (const [kRaw, vRaw] of entries) {
        const k = String(kRaw ?? '').trim();
        if (!k)
            continue;
        if (!envKeyPattern.test(k)) {
            throw new Error(`Invalid systemd environment variable name: ${k}`);
        }
        lines.push(`Environment=${k}=${escapeSystemdEnvValue(String(vRaw ?? ''))}`);
    }
    return lines;
}
export function renderSystemdServiceUnit(params) {
    const desc = assertSingleLineSystemdField('description', String(params.description ?? '').trim() || 'Happier Service');
    const restartPolicy = assertSingleLineSystemdField('restart', String(params.restart ?? '').trim() || 'always');
    const workDir = assertSingleLineSystemdField('workingDirectory', String(params.workingDirectory ?? '').trim());
    const out = assertSingleLineSystemdField('stdoutPath', String(params.stdoutPath ?? '').trim());
    const err = assertSingleLineSystemdField('stderrPath', String(params.stderrPath ?? '').trim());
    const runAsUser = assertSingleLineSystemdField('runAsUser', String(params.runAsUser ?? '').trim());
    const execStart = Array.isArray(params.execStart)
        ? params.execStart.map((a) => escapeSystemdExecArg(String(a ?? ''))).join(' ')
        : assertSingleLineSystemdField('execStart', String(params.execStart ?? '').trim());
    if (!execStart) {
        throw new Error('execStart is required');
    }
    const envLines = systemdEnvLines(params.env);
    const workDirLine = workDir ? `WorkingDirectory=${workDir}\n` : '';
    const userLine = runAsUser ? `User=${runAsUser}\n` : '';
    const outLine = out ? `StandardOutput=append:${out}\n` : '';
    const errLine = err ? `StandardError=append:${err}\n` : '';
    const wantedBy = assertSingleLineSystemdField('wantedBy', String(params.wantedBy ?? '').trim() || 'default.target');
    const envBlock = envLines.length ? `\n${envLines.join('\n')}\n` : '\n';
    return `[Unit]
Description=${desc}
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
${workDirLine}${userLine}${envBlock}ExecStart=${execStart}
Restart=${restartPolicy}
RestartSec=2
${outLine}${errLine}[Install]
WantedBy=${wantedBy}
`;
}
//# sourceMappingURL=systemd.js.map