// src/actions/deplint/resolvePeerBumpFixes.ts
import chalk from "chalk";
import semver from "semver";

// src/lib/concurrency.ts
import { AsyncLocalStorage } from "node:async_hooks";
var outputStorage = new AsyncLocalStorage();
async function runWithConcurrency(items, concurrency, fn) {
  let next = 0;
  async function worker() {
    while (next < items.length) {
      const i = next++;
      await fn(items[i]);
    }
  }
  await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
}

// src/actions/deplint/fetchPackageManifest.ts
var REGISTRY_BASE = "https://registry.npmjs.org";
var cache = /* @__PURE__ */ new Map();
async function fetchUncached(packageName) {
  try {
    const url = `${REGISTRY_BASE}/${encodeURIComponent(packageName)}`;
    const res = await fetch(url, { headers: { Accept: "application/vnd.npm.install-v1+json" } });
    if (!res.ok) return void 0;
    const data = await res.json();
    const versionsMap = data.versions ?? {};
    const versions = Object.entries(versionsMap).map(
      ([version, manifest]) => ({ peerDependencies: manifest.peerDependencies ?? {}, version })
    );
    return { distTags: data["dist-tags"] ?? {}, versions };
  } catch {
    return void 0;
  }
}
function fetchPackageManifest(packageName) {
  const existing = cache.get(packageName);
  if (existing) return existing;
  const pending = fetchUncached(packageName);
  cache.set(packageName, pending);
  return pending;
}
async function fetchPackageManifests(packageNames, concurrency = 16) {
  const unique = [...new Set(packageNames)];
  const results = /* @__PURE__ */ new Map();
  await runWithConcurrency(unique, concurrency, async (name) => {
    results.set(name, await fetchPackageManifest(name));
  });
  return results;
}

// src/actions/deplint/resolvePeerBumpFixes.ts
function keyOf(k) {
  return `${k.dependency}|${k.peer}|${k.installedPeerVersion}|${k.currentRange}`;
}
function collectPeerBumpFixes(diagnostics) {
  const fixes = [];
  const packageNames = /* @__PURE__ */ new Set();
  for (const diagnostic of diagnostics) {
    for (const fix of diagnostic.fixes ?? []) {
      if (fix.action !== "resolve-peer-bump") continue;
      fixes.push(fix);
      packageNames.add(fix.dependency);
    }
  }
  return { fixes, packageNames: [...packageNames] };
}
function acceptsPeer(version, peer, installedPeerVersion) {
  const range = version.peerDependencies[peer];
  if (!range || range === "*") return true;
  if (range.startsWith("workspace:")) return true;
  try {
    return semver.satisfies(installedPeerVersion, range, { includePrerelease: true });
  } catch {
    return false;
  }
}
function pickHighest(versions) {
  if (versions.length === 0) return void 0;
  const stable = versions.filter((v) => semver.prerelease(v) === null);
  const pool = stable.length > 0 ? stable : versions;
  return pool.toSorted(semver.rcompare)[0];
}
function pickUpgradeTarget(manifest, peer, installedPeerVersion, currentRange) {
  const valid = manifest.versions.filter((v) => semver.valid(v.version) !== null);
  const currentBest = pickHighest(
    valid.filter((v) => semver.satisfies(v.version, currentRange, { includePrerelease: true })).filter((v) => acceptsPeer(v, peer, installedPeerVersion)).map((v) => v.version)
  );
  const absoluteBest = pickHighest(
    valid.filter((v) => acceptsPeer(v, peer, installedPeerVersion)).map((v) => v.version)
  );
  if (!absoluteBest) return void 0;
  if (currentBest && semver.gte(currentBest, absoluteBest)) return void 0;
  let currentMin;
  try {
    currentMin = semver.minVersion(currentRange)?.version;
  } catch {
  }
  if (currentMin && semver.lt(absoluteBest, currentMin)) return void 0;
  return absoluteBest;
}
function buildBumpedRange(resolvedVersion, currentRange) {
  if (currentRange.startsWith("^")) return `^${resolvedVersion}`;
  if (currentRange.startsWith("~")) return `~${resolvedVersion}`;
  return resolvedVersion;
}
function resolveOneFix(fix, manifest, resolutionCache2, loggedKeys2) {
  const cacheKey = keyOf(fix);
  const firstSeen = !resolutionCache2.has(cacheKey);
  let resolvedVersion;
  if (firstSeen) {
    resolvedVersion = manifest ? pickUpgradeTarget(manifest, fix.peer, fix.installedPeerVersion, fix.currentRange) : void 0;
    resolutionCache2.set(cacheKey, resolvedVersion);
  } else {
    resolvedVersion = resolutionCache2.get(cacheKey);
  }
  if (!resolvedVersion) {
    if (!loggedKeys2.has(cacheKey)) {
      loggedKeys2.add(cacheKey);
      console.log(
        `  ${chalk.dim(
          `No newer version of ${fix.dependency} accepts peer ${fix.peer}@${fix.installedPeerVersion} beyond current range ${fix.currentRange} \u2014 skipping bump (fix peer manually)`
        )}`
      );
    }
    return void 0;
  }
  const bumped = buildBumpedRange(resolvedVersion, fix.currentRange);
  if (bumped === fix.currentRange) return void 0;
  return {
    action: "set-version",
    dependency: fix.dependency,
    detail: `bumped ${fix.section} range to ${bumped} so ${fix.dependency}@${resolvedVersion} is installed (accepts peer ${fix.peer}@${fix.installedPeerVersion})`,
    section: fix.section,
    version: bumped
  };
}
var resolutionCache = /* @__PURE__ */ new Map();
var loggedKeys = /* @__PURE__ */ new Set();
function clearPeerBumpCache() {
  resolutionCache.clear();
  loggedKeys.clear();
}
async function resolvePeerBumpFixes(diagnostics) {
  const { fixes, packageNames } = collectPeerBumpFixes(diagnostics);
  if (fixes.length === 0) return;
  const manifests = await fetchPackageManifests(packageNames);
  for (const diagnostic of diagnostics) {
    if (!diagnostic.fixes) continue;
    const replacement = [];
    for (const fix of diagnostic.fixes) {
      if (fix.action !== "resolve-peer-bump") {
        replacement.push(fix);
        continue;
      }
      const resolved = resolveOneFix(fix, manifests.get(fix.dependency), resolutionCache, loggedKeys);
      if (resolved) replacement.push(resolved);
    }
    diagnostic.fixes = replacement;
  }
}
export {
  clearPeerBumpCache,
  resolvePeerBumpFixes
};
//# sourceMappingURL=resolvePeerBumpFixes.mjs.map
