// SPEC-597-EXEMPT: canonical mapping (npm-package↔framework, keyword catalogue)
// — must enumerate framework names by design; no detection logic to migrate.
// Planu — Package Registry Clients
// Queries npm, PyPI, crates.io, pkg.go.dev, packagist for latest versions.
// Uses globalThis.fetch (Node.js 22+) — no external HTTP dependencies.
// === Registry URL templates ===
const REGISTRY_URLS = {
    npm: 'https://registry.npmjs.org/{name}',
    pypi: 'https://pypi.org/pypi/{name}/json',
    crates: 'https://crates.io/api/v1/crates/{name}',
    go: 'https://proxy.golang.org/{name}/@latest',
    packagist: 'https://repo.packagist.org/p2/{name}.json',
    pub: 'https://pub.dev/api/packages/{name}',
    nuget: 'https://api.nuget.org/v3-flatcontainer/{name}/index.json',
    rubygems: 'https://rubygems.org/api/v1/gems/{name}.json',
};
const TIMEOUT_MS = 10_000;
// === Public API ===
export async function fetchLatestVersion(packageName, registry) {
    const urlTemplate = REGISTRY_URLS[registry];
    const url = urlTemplate.replace('{name}', encodeURIComponent(packageName));
    try {
        const response = await globalThis.fetch(url, {
            headers: buildHeaders(registry),
            signal: AbortSignal.timeout(TIMEOUT_MS),
        });
        if (!response.ok) {
            return null;
        }
        const data = await response.json();
        return parseRegistryResponse(data, registry);
    }
    catch {
        return null;
    }
}
export function classifyUpdateType(current, latest) {
    const cur = parseSemver(current);
    const lat = parseSemver(latest);
    if (!cur || !lat) {
        return 'up-to-date';
    }
    if (cur.major === lat.major && cur.minor === lat.minor && cur.patch === lat.patch) {
        return 'up-to-date';
    }
    if (lat.major > cur.major) {
        return 'major';
    }
    if (lat.minor > cur.minor) {
        return 'minor';
    }
    if (lat.patch > cur.patch) {
        return 'patch';
    }
    return 'up-to-date';
}
export function detectRegistry(packageName, language) {
    const lang = language?.toLowerCase() ?? '';
    if (lang === 'python' || lang === 'python3') {
        return 'pypi';
    }
    if (lang === 'rust') {
        return 'crates';
    }
    if (lang === 'go' || lang === 'golang') {
        return 'go';
    }
    if (lang === 'php') {
        return 'packagist';
    }
    if (lang === 'dart' || lang === 'flutter') {
        return 'pub';
    }
    if (lang === 'csharp' || lang === 'c#' || lang === 'dotnet') {
        return 'nuget';
    }
    if (lang === 'ruby') {
        return 'rubygems';
    }
    // Heuristic: scoped packages (@scope/name) are npm
    if (packageName.startsWith('@')) {
        return 'npm';
    }
    // Default to npm for JS/TS ecosystem
    return 'npm';
}
export async function checkPackageVersions(dependencies, language) {
    const results = [];
    const entries = Object.entries(dependencies);
    // Process in batches to respect rate limits
    const batchSize = 5;
    for (let i = 0; i < entries.length; i += batchSize) {
        const batch = entries.slice(i, i + batchSize);
        const batchResults = await Promise.allSettled(batch.map(async ([name, version]) => {
            const cleanVersion = version.replace(/[\^~>=<]/g, '');
            const registry = detectRegistry(name, language);
            const latest = await fetchLatestVersion(name, registry);
            if (latest) {
                return {
                    name,
                    currentVersion: cleanVersion,
                    latestVersion: latest.version,
                    updateType: classifyUpdateType(cleanVersion, latest.version),
                    deprecated: latest.deprecated,
                    replacement: latest.replacement,
                    registry,
                    checkedAt: new Date().toISOString(),
                };
            }
            // Fallback: registry unreachable
            return {
                name,
                currentVersion: cleanVersion,
                latestVersion: cleanVersion,
                updateType: 'up-to-date',
                deprecated: false,
                registry,
                checkedAt: new Date().toISOString(),
            };
        }));
        for (const result of batchResults) {
            if (result.status === 'fulfilled') {
                results.push(result.value);
            }
        }
    }
    return results;
}
// === Internal helpers ===
function buildHeaders(registry) {
    const headers = {
        'User-Agent': 'Planu/1.0',
        Accept: 'application/json',
    };
    // crates.io requires a user agent
    if (registry === 'crates') {
        headers['User-Agent'] = 'Planu/1.0 (https://github.com/planudev)';
    }
    return headers;
}
function parseSemver(version) {
    const clean = version.replace(/^[v=]/, '').split('-')[0] ?? '';
    const parts = clean.split('.');
    const major = Number(parts[0]);
    const minor = Number(parts[1] ?? '0');
    const patch = Number(parts[2] ?? '0');
    if (isNaN(major)) {
        return null;
    }
    return { major, minor, patch };
}
function parseRegistryResponse(data, registry) {
    if (!data || typeof data !== 'object') {
        return null;
    }
    switch (registry) {
        case 'npm':
            return parseNpmResponse(data);
        case 'pypi':
            return parsePyPIResponse(data);
        case 'crates':
            return parseCratesResponse(data);
        case 'go':
            return parseGoResponse(data);
        case 'packagist':
            return parsePackagistResponse(data);
        case 'pub':
            return parsePubResponse(data);
        case 'nuget':
            return parseNugetResponse(data);
        case 'rubygems':
            return parseRubygemsResponse(data);
    }
}
function parseNpmResponse(data) {
    const distTags = data['dist-tags'];
    const version = distTags?.latest;
    if (!version) {
        return null;
    }
    // Check deprecation in latest version info
    const versions = data.versions;
    const latestInfo = versions?.[version];
    const deprecated = typeof latestInfo?.deprecated === 'string';
    return { version, deprecated };
}
function parsePyPIResponse(data) {
    const info = data.info;
    const version = info?.version;
    if (typeof version !== 'string') {
        return null;
    }
    const classifiers = (info?.classifiers ?? []);
    const deprecated = classifiers.some((c) => c.includes('Inactive') || c.includes('Deprecated'));
    return { version, deprecated };
}
function parseCratesResponse(data) {
    const crate = data.crate;
    const version = crate?.max_stable_version ?? crate?.max_version;
    if (typeof version !== 'string') {
        return null;
    }
    return { version, deprecated: false };
}
function parseGoResponse(data) {
    const version = data.Version;
    if (typeof version !== 'string') {
        return null;
    }
    return { version: version.replace(/^v/, ''), deprecated: false };
}
function parsePackagistResponse(data) {
    const packages = data.packages;
    if (!packages) {
        return null;
    }
    const entries = Object.values(packages)[0];
    const latest = entries?.[0];
    if (!latest) {
        return null;
    }
    const version = latest.version;
    if (typeof version !== 'string') {
        return null;
    }
    const abandoned = latest.abandoned;
    return {
        version: version.replace(/^v/, ''),
        deprecated: typeof abandoned === 'string' || abandoned === true,
        replacement: typeof abandoned === 'string' ? abandoned : undefined,
    };
}
function parsePubResponse(data) {
    const latest = data.latest;
    const version = latest?.version;
    if (typeof version !== 'string') {
        return null;
    }
    return { version, deprecated: false };
}
function parseNugetResponse(data) {
    const versions = data.versions;
    if (!versions || versions.length === 0) {
        return null;
    }
    // Last version in the list is the latest
    const version = versions[versions.length - 1];
    if (!version) {
        return null;
    }
    return { version, deprecated: false };
}
function parseRubygemsResponse(data) {
    const version = data.version;
    if (typeof version !== 'string') {
        return null;
    }
    return { version, deprecated: false };
}
//# sourceMappingURL=package-registry.js.map