import { execFile } from 'node:child_process';
import path from 'node:path';
function execFileAsync(cmd, args, opts) {
    return new Promise((resolve, reject) => {
        execFile(cmd, args, opts, (err, stdout) => {
            if (err) {
                reject(Object.assign(err, { stdout }));
            }
            else {
                resolve({ stdout });
            }
        });
    });
}
export async function scanVulnerabilities(opts = {}) {
    const root = opts.root ? path.resolve(opts.root) : process.cwd();
    const args = ['audit', '--json'];
    if (opts.production)
        args.push('--production');
    if (opts.auditLevel)
        args.push(`--audit-level=${opts.auditLevel}`);
    try {
        const { stdout } = await execFileAsync('npm', args, { cwd: root });
        const raw = JSON.parse(stdout);
        return normalizeAuditResult(raw);
    }
    catch (err) {
        if (isNpmAuditError(err)) {
            const parsed = JSON.parse(err.stdout);
            return normalizeAuditResult(parsed);
        }
        return emptyResult();
    }
}
function isNpmAuditError(err) {
    if (typeof err !== 'object' || err === null)
        return false;
    const e = err;
    return typeof e.stdout === 'string' && typeof e.code === 'number';
}
function normalizeAuditResult(raw) {
    const vulnerabilities = raw.vulnerabilities ?? {};
    const metaVulns = raw.metadata?.vulnerabilities ?? {};
    const metaDeps = raw.metadata?.dependencies ?? {};
    const normalized = {};
    let vulnCount = 0;
    for (const [pkgName, vuln] of Object.entries(vulnerabilities)) {
        vulnCount++;
        normalized[pkgName] = {
            name: vuln.name,
            severity: vuln.severity,
            isDirect: vuln.isDirect,
            via: vuln.via.map(v => (typeof v === 'string' ? v : v.title || v.url || v.name)),
            effects: vuln.effects,
            range: vuln.range,
            fixAvailable: vuln.fixAvailable === false ? null : {
                name: vuln.fixAvailable.name,
                version: vuln.fixAvailable.version,
                isSemVerMajor: vuln.fixAvailable.isSemVerMajor,
            },
            nodes: vuln.nodes,
        };
    }
    return {
        generatedAt: new Date().toISOString(),
        totalPackages: vulnCount,
        totalVulnerabilities: vulnCount,
        counts: {
            critical: metaVulns.critical ?? 0,
            high: metaVulns.high ?? 0,
            moderate: metaVulns.moderate ?? 0,
            low: metaVulns.low ?? 0,
            info: metaVulns.info ?? 0,
        },
        vulnerabilities: normalized,
        metadata: {
            vulnerabilities: {
                critical: metaVulns.critical ?? 0,
                high: metaVulns.high ?? 0,
                moderate: metaVulns.moderate ?? 0,
                low: metaVulns.low ?? 0,
                info: metaVulns.info ?? 0,
            },
            dependencies: metaDeps.prod ?? 0,
            devDependencies: metaDeps.dev ?? 0,
            optionalDependencies: metaDeps.optional ?? 0,
            totalDependencies: metaDeps.total ?? 0,
        },
    };
}
function emptyResult() {
    return {
        generatedAt: new Date().toISOString(),
        totalPackages: 0,
        totalVulnerabilities: 0,
        counts: { critical: 0, high: 0, moderate: 0, low: 0, info: 0 },
        vulnerabilities: {},
        metadata: {
            vulnerabilities: { critical: 0, high: 0, moderate: 0, low: 0, info: 0 },
            dependencies: 0,
            devDependencies: 0,
            optionalDependencies: 0,
            totalDependencies: 0,
        },
    };
}
//# sourceMappingURL=scanner.js.map