"use strict";
/**
 * Registry Clients for Package Metadata
 * Fetches package information from npm and PyPI registries
 * Used by the Hallucination Firewall (Story C) to assess dependency risk
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchNPMMetadata = fetchNPMMetadata;
exports.fetchPyPIMetadata = fetchPyPIMetadata;
exports.extractNpmDependencies = extractNpmDependencies;
exports.extractPythonRequirements = extractPythonRequirements;
exports.extractPyprojectDependencies = extractPyprojectDependencies;
exports.getPackageFileType = getPackageFileType;
exports.calculatePackageAgeDays = calculatePackageAgeDays;
exports.rateLimitDelay = rateLimitDelay;
exports.clearRegistryCaches = clearRegistryCaches;
// Cache for package metadata to avoid repeated requests
const npmMetadataCache = new Map();
const pypiMetadataCache = new Map();
// Rate limiting configuration
const RATE_LIMIT_DELAY_MS = 100;
/**
 * Fetch package metadata from npm registry
 * Returns null if package doesn't exist
 */
async function fetchNPMMetadata(packageName) {
    // Check cache first
    if (npmMetadataCache.has(packageName)) {
        return npmMetadataCache.get(packageName) || null;
    }
    try {
        // Fetch package info from npm registry
        const registryResponse = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
            headers: {
                'Accept': 'application/json',
            },
        });
        if (!registryResponse.ok) {
            if (registryResponse.status === 404) {
                npmMetadataCache.set(packageName, null);
                return null;
            }
            // Non-404 error - don't cache, might be transient
            console.warn(`[Registry] npm registry error for ${packageName}: ${registryResponse.status}`);
            return null;
        }
        const data = await registryResponse.json();
        // Fetch download counts separately (different API)
        let weeklyDownloads = 0;
        try {
            const downloadsResponse = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`);
            if (downloadsResponse.ok) {
                const downloadsData = await downloadsResponse.json();
                weeklyDownloads = downloadsData.downloads || 0;
            }
        }
        catch {
            // Download count is optional, don't fail
        }
        const metadata = {
            name: data.name,
            version: (data['dist-tags']?.latest) || '',
            description: data.description,
            maintainers: data.maintainers || [],
            time: data.time || { created: '', modified: '' },
            repository: data.repository,
            homepage: data.homepage,
            license: data.license,
            downloads: { weekly: weeklyDownloads },
        };
        npmMetadataCache.set(packageName, metadata);
        return metadata;
    }
    catch (error) {
        console.warn(`[Registry] Failed to fetch npm metadata for ${packageName}:`, error);
        // Don't cache network errors
        return null;
    }
}
/**
 * Fetch package metadata from PyPI registry
 * Returns null if package doesn't exist
 */
async function fetchPyPIMetadata(packageName) {
    // Check cache first
    if (pypiMetadataCache.has(packageName)) {
        return pypiMetadataCache.get(packageName) || null;
    }
    try {
        const response = await fetch(`https://pypi.org/pypi/${encodeURIComponent(packageName)}/json`, {
            headers: {
                'Accept': 'application/json',
            },
        });
        if (!response.ok) {
            if (response.status === 404) {
                pypiMetadataCache.set(packageName, null);
                return null;
            }
            console.warn(`[Registry] PyPI registry error for ${packageName}: ${response.status}`);
            return null;
        }
        const data = await response.json();
        const info = (data.info || {});
        // Get release date from the latest version
        let releaseDate;
        const releases = data.releases?.[info.version];
        if (releases && releases.length > 0) {
            releaseDate = releases[0].upload_time;
        }
        const metadata = {
            name: info.name,
            version: info.version,
            summary: info.summary,
            author: info.author,
            authorEmail: info.author_email,
            license: info.license,
            projectUrls: info.project_urls,
            releaseDate,
            requiresPython: info.requires_python,
        };
        pypiMetadataCache.set(packageName, metadata);
        return metadata;
    }
    catch (error) {
        console.warn(`[Registry] Failed to fetch PyPI metadata for ${packageName}:`, error);
        return null;
    }
}
/**
 * Extract dependencies from package.json content
 */
function extractNpmDependencies(content) {
    try {
        const pkg = JSON.parse(content);
        const deps = [];
        const lines = content.split('\n');
        const depSections = [
            { key: 'dependencies', source: 'dependencies' },
            { key: 'devDependencies', source: 'devDependencies' },
            { key: 'peerDependencies', source: 'peerDependencies' },
            { key: 'optionalDependencies', source: 'optionalDependencies' },
        ];
        for (const { key, source } of depSections) {
            const depsObj = pkg[key];
            if (!depsObj || typeof depsObj !== 'object')
                continue;
            for (const [name, version] of Object.entries(depsObj)) {
                // Find the line number for this dependency
                const lineIndex = lines.findIndex(l => l.includes(`"${name}"`));
                deps.push({
                    name,
                    version: version,
                    source,
                    line: lineIndex >= 0 ? lineIndex + 1 : 1,
                });
            }
        }
        return deps;
    }
    catch {
        return [];
    }
}
/**
 * Extract dependencies from requirements.txt content
 */
function extractPythonRequirements(content) {
    const deps = [];
    const lines = content.split('\n');
    lines.forEach((line, index) => {
        // Skip comments and empty lines
        const trimmed = line.trim();
        if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('-')) {
            return;
        }
        // Parse package name and optional version
        // Formats: package, package==1.0.0, package>=1.0.0, package[extra], etc.
        const match = trimmed.match(/^([a-zA-Z0-9_-]+)(?:\[.*?\])?(?:[=<>~!]+(.+))?/);
        if (match) {
            deps.push({
                name: match[1],
                version: match[2],
                source: 'requirements',
                line: index + 1,
            });
        }
    });
    return deps;
}
/**
 * Extract dependencies from pyproject.toml content
 */
function extractPyprojectDependencies(content) {
    const deps = [];
    const lines = content.split('\n');
    let inDependencies = false;
    lines.forEach((line, index) => {
        const trimmed = line.trim();
        // Check for dependencies section
        if (trimmed === '[project.dependencies]' || trimmed === 'dependencies = [') {
            inDependencies = true;
            return;
        }
        // Exit dependencies section
        if (inDependencies && (trimmed.startsWith('[') || trimmed === ']')) {
            inDependencies = false;
            return;
        }
        if (inDependencies) {
            // Parse dependency line: "package>=1.0.0", or package = ">=1.0.0"
            const match = trimmed.match(/^["']?([a-zA-Z0-9_-]+)(?:\[.*?\])?(?:[=<>~!]+)?/);
            if (match && match[1]) {
                deps.push({
                    name: match[1],
                    version: undefined,
                    source: 'requirements',
                    line: index + 1,
                });
            }
        }
    });
    return deps;
}
/**
 * Determine the package file type from path
 */
function getPackageFileType(filePath) {
    const fileName = filePath.split('/').pop()?.toLowerCase() || '';
    if (fileName === 'package.json' ||
        fileName === 'package-lock.json' ||
        fileName === 'yarn.lock' ||
        fileName === 'pnpm-lock.yaml') {
        return 'npm';
    }
    if (fileName === 'requirements.txt' ||
        fileName === 'pyproject.toml' ||
        fileName === 'pipfile' ||
        fileName === 'pipfile.lock') {
        return 'python';
    }
    return null;
}
/**
 * Calculate package age in days from creation date
 */
function calculatePackageAgeDays(createdDate) {
    if (!createdDate)
        return Infinity; // Unknown age, treat as old (safe)
    try {
        const created = new Date(createdDate);
        const now = new Date();
        const diffMs = now.getTime() - created.getTime();
        return Math.floor(diffMs / (1000 * 60 * 60 * 24));
    }
    catch {
        return Infinity;
    }
}
/**
 * Rate limiter helper - adds delay between registry requests
 */
async function rateLimitDelay() {
    return new Promise(resolve => setTimeout(resolve, RATE_LIMIT_DELAY_MS));
}
/**
 * Clear all caches (useful for testing)
 */
function clearRegistryCaches() {
    npmMetadataCache.clear();
    pypiMetadataCache.clear();
}
//# sourceMappingURL=registry-clients.js.map