// src/actions/publint.ts
import chalk11 from "chalk";

// src/lib/concurrency.ts
import { AsyncLocalStorage } from "node:async_hooks";
var outputStorage = new AsyncLocalStorage();
var captureState = { isInstalled: false };
function installOutputCapture() {
  if (captureState.isInstalled) return;
  captureState.isInstalled = true;
  const originalStdoutWrite = process.stdout.write.bind(process.stdout);
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
  function intercept(original) {
    return function(chunk, ...args) {
      const buffer = outputStorage.getStore();
      if (buffer) {
        const decoder = new TextDecoder();
        buffer.push(typeof chunk === "string" ? chunk : decoder.decode(chunk));
        return true;
      }
      return original(chunk, ...args);
    };
  }
  process.stdout.write = intercept(originalStdoutWrite);
  process.stderr.write = intercept(originalStderrWrite);
}
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/lib/crossSpawn.ts
import crossSpawn from "cross-spawn";
function spawnSync(command, args, options) {
  return crossSpawn.sync(command, args, options);
}

// src/lib/deepMerge.ts
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
function isPlainObject(value) {
  if (typeof value !== "object" || value === null) return false;
  const proto = Object.getPrototypeOf(value);
  return proto === Object.prototype || proto === null;
}
function isMergeable(value) {
  return Array.isArray(value) || isPlainObject(value);
}
function cloneValue(value) {
  if (Array.isArray(value)) {
    return value.map((item) => cloneValue(item));
  }
  if (isPlainObject(value)) {
    const result = {};
    for (const [key, item] of Object.entries(value)) {
      if (UNSAFE_KEYS.has(key)) continue;
      result[key] = cloneValue(item);
    }
    return result;
  }
  return value;
}
function mergeObject(target, source, options) {
  const destination = {};
  if (isPlainObject(target)) {
    for (const [key, item] of Object.entries(target)) {
      if (UNSAFE_KEYS.has(key)) continue;
      destination[key] = cloneValue(item);
    }
  }
  for (const [key, sourceValue] of Object.entries(source)) {
    if (UNSAFE_KEYS.has(key)) continue;
    if (Object.hasOwn(target, key) && isMergeable(sourceValue)) {
      const customMerge = options.customMerge?.(key);
      destination[key] = (customMerge ?? deepMerge)(target[key], sourceValue, options);
    } else {
      destination[key] = cloneValue(sourceValue);
    }
  }
  return destination;
}
function deepMerge(target, source, options = {}) {
  const isSourceIsArray = Array.isArray(source);
  const isTargetIsArray = Array.isArray(target);
  if (isSourceIsArray !== isTargetIsArray) {
    return cloneValue(source);
  }
  if (isSourceIsArray) {
    return [...target, ...source].map((item) => cloneValue(item));
  }
  return mergeObject(target, source, options);
}

// src/lib/packageManagerRuntime.ts
import fs from "node:fs";
import chalk from "chalk";
var DISPLAY_NAMES = {
  bun: "Bun",
  npm: "npm",
  pnpm: "pnpm",
  yarn: "Yarn"
};
var MAC_INSTALL_COMMANDS = {
  bun: "brew install oven-sh/bun/bun",
  npm: "brew install node",
  pnpm: "brew install pnpm",
  yarn: "brew install yarn"
};
var SUPPORTED_PLATFORMS = ["darwin", "linux", "win32"];
function currentPlatform() {
  return SUPPORTED_PLATFORMS.find((platform) => platform === process.platform) ?? "unknown";
}
function shouldAskYesNo(question) {
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
    return false;
  }
  process.stdout.write(question);
  const buffer = Buffer.alloc(1024);
  const bytesRead = fs.readSync(process.stdin.fd, buffer, 0, buffer.length, null);
  const answer = buffer.toString("utf8", 0, bytesRead).trim().toLowerCase();
  return answer === "y" || answer === "yes";
}
function isCommandInstalled(command) {
  const result = spawnSync(command, ["--version"], {
    encoding: "utf8",
    stdio: "ignore"
  });
  return !result.error && result.status === 0;
}
function isPackageManagerInstalled(pm) {
  return isCommandInstalled(pm);
}
function getPackageManagerInstallPlan(pm, platform = currentPlatform()) {
  const displayName = DISPLAY_NAMES[pm];
  if (platform === "darwin") {
    const command = MAC_INSTALL_COMMANDS[pm];
    return {
      command,
      manualInstructions: [
        `Install ${displayName} with: ${command}`
      ],
      packageManager: pm,
      platform,
      supported: true
    };
  }
  if (platform === "linux") {
    return {
      manualInstructions: [
        `Automatic installation for ${displayName} is not implemented on Linux yet.`,
        `Install ${displayName} manually and re-run the command.`
      ],
      packageManager: pm,
      platform,
      supported: false
    };
  }
  if (platform === "win32") {
    return {
      manualInstructions: [
        `Automatic installation for ${displayName} is not implemented on Windows yet.`,
        `Install ${displayName} manually and re-run the command.`
      ],
      packageManager: pm,
      platform,
      supported: false
    };
  }
  return {
    manualInstructions: [
      `Automatic installation for ${displayName} is not implemented on ${platform} yet.`,
      `Install ${displayName} manually and re-run the command.`
    ],
    packageManager: pm,
    platform,
    supported: false
  };
}
function printManualInstructions(plan, context) {
  console.error(chalk.yellow(`${DISPLAY_NAMES[plan.packageManager]} is required for "${context.operation}".`));
  for (const line of plan.manualInstructions) {
    console.error(chalk.gray(`  ${line}`));
  }
}
function shouldInstallWithHomebrew(pm, cwd) {
  const command = MAC_INSTALL_COMMANDS[pm];
  const result = spawnSync("brew", command.replace("brew ", "").split(" "), {
    cwd,
    encoding: "utf8",
    stdio: "inherit"
  });
  return result.status === 0;
}
function didEnsurePackageManagerInstalled(pm, context) {
  if (isPackageManagerInstalled(pm)) return true;
  const plan = getPackageManagerInstallPlan(pm, context.platform ?? currentPlatform());
  const displayName = DISPLAY_NAMES[pm];
  console.error(chalk.yellow(`${context.operation} requires ${displayName}, but it is not installed.`));
  if (!plan.supported) {
    printManualInstructions(plan, context);
    return false;
  }
  if (!isCommandInstalled("brew")) {
    console.error(chalk.yellow("Homebrew is not installed, so automatic installation is unavailable."));
    console.error(chalk.gray("  Install Homebrew from https://brew.sh and then run:"));
    console.error(chalk.gray(`  ${plan.command}`));
    return false;
  }
  const isConfirmed = shouldAskYesNo(chalk.cyan(`Install ${displayName} with Homebrew now? [y/N] `));
  if (!isConfirmed) {
    console.error(chalk.gray(`Skipping install. Run "${plan.command}" manually and then retry.`));
    return false;
  }
  console.error(chalk.cyan(`Installing ${displayName} via Homebrew...
`));
  if (!shouldInstallWithHomebrew(pm, context.cwd)) {
    console.error(chalk.red(`Homebrew failed to install ${displayName}.`));
    console.error(chalk.gray(`Run "${plan.command}" manually and then retry.`));
    return false;
  }
  if (!isPackageManagerInstalled(pm)) {
    console.error(chalk.red(`${displayName} still does not appear to be installed after Homebrew completed.`));
    console.error(chalk.gray(`Run "${plan.command}" manually and then retry.`));
    return false;
  }
  console.error(chalk.green(`${displayName} installed successfully.`));
  return true;
}
function failureResult() {
  return {
    output: ["", ""],
    pid: 0,
    signal: null,
    status: 1,
    stderr: "",
    stdout: ""
  };
}
function spawnPackageManagerSync(pm, args, options) {
  if (!didEnsurePackageManagerInstalled(pm, options.context)) {
    return failureResult();
  }
  return spawnSync(pm, args, { encoding: "utf8", ...options });
}

// src/pm/workspaces.ts
import { existsSync, readFileSync } from "node:fs";
import PATH from "node:path";
import { globSync } from "glob";
function readJsonFile(path8) {
  if (!existsSync(path8)) return void 0;
  try {
    return JSON.parse(readFileSync(path8, "utf8"));
  } catch {
    return void 0;
  }
}
function readRootPackageJson(cwd = process.cwd()) {
  return readJsonFile(PATH.resolve(cwd, "package.json"));
}
function detectPackageManagerFromPackageJson(cwd = process.cwd()) {
  const packageManager = readRootPackageJson(cwd)?.packageManager;
  if (!(packageManager != null && packageManager.length > 0)) return void 0;
  return packageManager.split("@", 1)[0];
}

// src/pm/detectPackageManager.ts
import { existsSync as existsSync2 } from "node:fs";
var knownPackageManagers = /* @__PURE__ */ new Set(["bun", "npm", "pnpm", "yarn"]);
function detectPackageManager() {
  const packageManager = detectPackageManagerFromPackageJson();
  if (packageManager != null && packageManager.length > 0 && knownPackageManagers.has(packageManager)) {
    return packageManager;
  }
  if (existsSync2("pnpm-lock.yaml") || existsSync2("pnpm-workspace.yaml")) return "pnpm";
  if (existsSync2("bun.lock") || existsSync2("bun.lockb")) return "bun";
  if (existsSync2("yarn.lock") || existsSync2(".yarnrc.yml")) return "yarn";
  if (existsSync2("package-lock.json") || existsSync2("npm-shrinkwrap.json")) return "npm";
  return "yarn";
}

// src/pm/registry.ts
var implementations = /* @__PURE__ */ new Map();
function getPackageManager(name) {
  const pmName = name ?? detectPackageManager();
  const pm = implementations.get(pmName);
  if (!pm) {
    throw new Error(
      `No package manager implementation registered for "${pmName}". Ensure registerPackageManager() has been called before getPackageManager().`
    );
  }
  return pm;
}

// src/lib/toolchainVersion.ts
import { readFileSync as readFileSync2 } from "node:fs";
import { createRequire } from "node:module";
var require2 = createRequire(import.meta.url);
function bundledToolchainVersion() {
  const pkgPath = require2.resolve("@xylabs/toolchain/package.json");
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
  if (!(pkg.version != null && pkg.version.length > 0)) throw new Error("Could not read @xylabs/toolchain version");
  return pkg.version;
}

// src/lib/initCwd.ts
function INIT_CWD() {
  return process.env.INIT_CWD ?? process.cwd();
}

// src/lib/loadConfig.ts
import chalk2 from "chalk";
import { cosmiconfig } from "cosmiconfig";
import { TypeScriptLoader } from "cosmiconfig-typescript-loader";
var configState = {
  config: void 0,
  rootConfigPath: void 0
};
var workspaceConfigCache = /* @__PURE__ */ new Map();
var deprecationWarned = /* @__PURE__ */ new Set();
function isRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function createExplorer() {
  return cosmiconfig("xy", { cache: true, loaders: { ".ts": TypeScriptLoader() } });
}
var loadConfig = async (params) => {
  if (configState.config === void 0) {
    const cosmicConfigResult = await createExplorer().search();
    configState.config = cosmicConfigResult?.config ?? {};
    configState.rootConfigPath = cosmicConfigResult?.filepath;
    const configFilePath = cosmicConfigResult?.filepath;
    if (configFilePath !== void 0) {
      console.log(chalk2.green(`Loaded config from ${configFilePath}`));
      if (Boolean(configState.config.verbose)) {
        console.log(chalk2.gray(JSON.stringify(configState.config, null, 2)));
      }
    }
  }
  return deepMerge(configState.config, params ?? {});
};
async function loadWorkspaceConfig(workspaceDir) {
  const cached = workspaceConfigCache.get(workspaceDir);
  if (cached !== void 0) return cached;
  const result = await createExplorer().search(workspaceDir);
  if (!result || result.filepath === configState.rootConfigPath) {
    workspaceConfigCache.set(workspaceDir, {});
    return {};
  }
  const wsConfig = result.config ?? {};
  workspaceConfigCache.set(workspaceDir, wsConfig);
  return wsConfig;
}
var DEPRECATED_COMMAND_FIELDS = /* @__PURE__ */ new Set(["deplint", "publint"]);
function resolveCommandField(cfg, commandName, configPath) {
  const commands = cfg.commands;
  const fromCommands = commands?.[commandName];
  if (fromCommands !== void 0 && typeof fromCommands === "object") {
    return fromCommands;
  }
  const fromTopLevel = cfg[commandName];
  if (fromTopLevel !== void 0 && typeof fromTopLevel === "object" && DEPRECATED_COMMAND_FIELDS.has(commandName)) {
    const key = `${configPath ?? "unknown"}:${commandName}`;
    if (!deprecationWarned.has(key)) {
      deprecationWarned.add(key);
      console.warn(chalk2.yellow(
        `[xy] Deprecated: top-level "${commandName}" in ${configPath ?? "xy.config"} \u2014 move to "commands.${commandName}"`
      ));
    }
    return fromTopLevel;
  }
  return {};
}
function mergeRuleConfigs(target, source) {
  if (isRecord(target) && isRecord(source)) {
    return { ...target, ...source };
  }
  return source;
}
function mergeCommandConfig(rootCmd, workspaceCmd, commandName) {
  return deepMerge(rootCmd, workspaceCmd, {
    customMerge: (key) => (commandName === "deplint" || commandName === "dead") && key === "rules" ? mergeRuleConfigs : void 0
  });
}
async function loadWorkspaceCommandConfig(workspaceDir, commandName) {
  const root = await loadConfig();
  const rootCmd = resolveCommandField(root, commandName, configState.rootConfigPath);
  const wsConfig = await loadWorkspaceConfig(workspaceDir);
  const wsConfigPath = workspaceConfigCache.has(workspaceDir) ? workspaceDir : void 0;
  const wsCmd = resolveCommandField(wsConfig, commandName, wsConfigPath);
  return mergeCommandConfig(rootCmd, wsCmd, commandName);
}

// src/lib/reporting/xyReporter.ts
var reporterState = {
  currentCommand: "unknown",
  isEnvelopeEmitted: false,
  isJsonModeEnabled: false,
  isStrictModeEnabled: false,
  originalStdoutWrite: void 0,
  startTime: 0
};
function isStrictMode() {
  return reporterState.isStrictModeEnabled || process.env.XY_STRICT === "1";
}

// src/lib/shouldRunInstall.ts
import chalk3 from "chalk";
function shouldRunInstall(cwd) {
  const pm = detectPackageManager();
  console.log(chalk3.gray(`Running ${pm} install...`));
  const result = spawnPackageManagerSync(pm, ["install"], {
    context: { cwd, operation: `Run ${pm} install` },
    cwd,
    encoding: "utf8",
    stdio: "inherit"
  });
  if (result.status !== 0) {
    console.warn(chalk3.yellow(`${pm} install failed`));
    return false;
  }
  console.log(chalk3.green("Dependencies installed"));
  return true;
}

// src/actions/incremental.ts
import { createHash } from "node:crypto";
import {
  existsSync as existsSync3,
  lstatSync,
  mkdirSync,
  readdirSync,
  readFileSync as readFileSync3,
  readlinkSync,
  rmSync,
  writeFileSync
} from "node:fs";
import path from "node:path";
import * as yaml from "yaml";
var snapshotVersion = 1;
var dependencyFields = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
var workspaceDependencyFields = ["dependencies", "devDependencies", "peerDependencies"];
var ignoredInputDirs = /* @__PURE__ */ new Set([
  ".cache",
  ".git",
  ".next",
  ".turbo",
  "build",
  "coverage",
  "dist",
  "docs",
  "node_modules"
]);
var ignoredInputFiles = /* @__PURE__ */ new Set([".eslintcache"]);
var outputDirs = ["build", "dist"];
function toPosix(value) {
  return value.split(path.sep).join("/");
}
function normalizeLocation(value) {
  const normalized = toPosix(value).replace(/\/$/u, "");
  return normalized === "" ? "." : normalized;
}
function stableValue(value) {
  if (Array.isArray(value)) return value.map(stableValue);
  if (Boolean(value) && typeof value === "object") {
    const entries = Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, entryValue]) => [key, stableValue(entryValue)]);
    return Object.fromEntries(entries);
  }
  return value;
}
function hashContent(value) {
  return createHash("sha256").update(value).digest("hex");
}
function hashJson(value) {
  return hashContent(JSON.stringify(stableValue(value)));
}
function readJsonFile2(filePath) {
  try {
    return JSON.parse(readFileSync3(filePath, "utf8"));
  } catch {
    return {};
  }
}
function readPackageJson(rootDir, workspace) {
  return readJsonFile2(path.resolve(rootDir, workspace.location, "package.json"));
}
function isStringRecord(value) {
  if (value == null || typeof value !== "object") return false;
  return Object.values(value).every((entry) => typeof entry === "string");
}
function packageDeps(pkg, field) {
  const value = pkg[field];
  return isStringRecord(value) ? value : {};
}
function selectedDeps(pkg, fields) {
  const deps = {};
  for (const field of fields) {
    const fieldEntries = Object.entries(packageDeps(pkg, field));
    for (const [name, range] of fieldEntries) {
      Reflect.set(deps, `${field}:${name}`, range);
    }
  }
  return deps;
}
function workspaceDependencyNames(pkg, workspaceNames) {
  const names = /* @__PURE__ */ new Set();
  for (const field of workspaceDependencyFields) {
    const fieldEntries = Object.entries(packageDeps(pkg, field));
    for (const [name, range] of fieldEntries) {
      if (workspaceNames.has(name) && range.startsWith("workspace:")) {
        names.add(name);
      }
    }
  }
  return [...names].toSorted((a, b) => a.localeCompare(b));
}
function externalDeps(pkg, workspaceNames) {
  const deps = {};
  const depEntries = Object.entries(selectedDeps(pkg, dependencyFields));
  for (const [key, range] of depEntries) {
    const name = key.slice(key.indexOf(":") + 1);
    if (!workspaceNames.has(name) && !range.startsWith("workspace:")) {
      deps[key] = range;
    }
  }
  return deps;
}
function shouldHashRootFile(name) {
  return name === "package.json" || name === "pnpm-workspace.yaml" || name === ".npmrc" || name === ".yarnrc" || name === ".yarnrc.yml" || /^tsconfig(?:\..+)?\.json$/u.test(name) || /^(?:eslint|vite|vitest|xy|tsup)\.config\./u.test(name);
}
function rootConfigFiles(rootDir) {
  return readdirSync(rootDir, { withFileTypes: true }).filter((entry) => entry.isFile() && shouldHashRootFile(entry.name)).map((entry) => entry.name).toSorted((a, b) => a.localeCompare(b));
}
function collectFiles(dir, baseDir = dir) {
  if (!existsSync3(dir)) return [];
  const files = [];
  const entries = readdirSync(dir, { withFileTypes: true });
  for (const entry of entries) {
    if (entry.isDirectory()) {
      if (!ignoredInputDirs.has(entry.name)) {
        files.push(...collectFiles(path.join(dir, entry.name), baseDir));
      }
      continue;
    }
    if (!entry.isFile() && !entry.isSymbolicLink()) continue;
    if (ignoredInputFiles.has(entry.name)) continue;
    const relativePath2 = path.relative(baseDir, path.join(dir, entry.name));
    files.push(toPosix(relativePath2));
  }
  return files.toSorted((a, b) => a.localeCompare(b));
}
function collectOutputFiles(workspaceDir) {
  const files = [];
  for (const outputDir of outputDirs) {
    const abs = path.join(workspaceDir, outputDir);
    if (existsSync3(abs)) {
      files.push(...collectFiles(abs, workspaceDir).map((file) => normalizeLocation(file)));
    }
  }
  return files.toSorted((a, b) => a.localeCompare(b));
}
function hashFiles(baseDir, files) {
  const hash = createHash("sha256");
  hash.update(`count:${files.length}\0`);
  const sortedFiles = files.toSorted((a, b) => a.localeCompare(b));
  for (const file of sortedFiles) {
    const abs = path.resolve(baseDir, file);
    hash.update(file);
    hash.update("\0");
    try {
      const stat = lstatSync(abs);
      if (stat.isSymbolicLink()) {
        hash.update("symlink");
        hash.update(readlinkSync(abs));
      } else if (stat.isFile()) {
        hash.update(readFileSync3(abs));
      }
    } catch {
      hash.update("missing");
    }
    hash.update("\0");
  }
  return hash.digest("hex");
}
function workspaceInputHash(rootDir, workspace) {
  if (workspace.location === ".") {
    return hashFiles(rootDir, rootConfigFiles(rootDir));
  }
  const workspaceDir = path.resolve(rootDir, workspace.location);
  return hashFiles(workspaceDir, collectFiles(workspaceDir));
}
function workspaceOutputHash(rootDir, workspace) {
  const workspaceDir = path.resolve(rootDir, workspace.location);
  return hashFiles(workspaceDir, collectOutputFiles(workspaceDir));
}
function rootHash(rootDir) {
  return hashFiles(rootDir, rootConfigFiles(rootDir));
}
function lockfileHash(rootDir, pm) {
  const lockfilePath = path.resolve(rootDir, pm.lockfile);
  return existsSync3(lockfilePath) ? hashContent(readFileSync3(lockfilePath)) : "";
}
function readPnpmLockfileImporters(rootDir, pm) {
  if (pm.name !== "pnpm") return {};
  const lockfilePath = path.resolve(rootDir, pm.lockfile);
  if (!existsSync3(lockfilePath)) return {};
  try {
    const parsed = yaml.parse(readFileSync3(lockfilePath, "utf8"));
    if (!Boolean(parsed) || typeof parsed !== "object") return {};
    const importers = parsed.importers;
    if (!Boolean(importers) || typeof importers !== "object" || Array.isArray(importers)) return {};
    const hashes = {};
    const sortedImporters = Object.entries(importers).toSorted(([a], [b]) => a.localeCompare(b));
    for (const [name, value] of sortedImporters) {
      hashes[normalizeLocation(name)] = hashJson(value);
    }
    return hashes;
  } catch {
    return {};
  }
}
function workspaceGraphHash(workspaceDeps) {
  return hashJson(workspaceDeps);
}
function safeToolchainVersion() {
  try {
    return bundledToolchainVersion();
  } catch {
    return "unknown";
  }
}
function createCurrentSnapshot({
  action,
  options,
  pm,
  workspaces
}) {
  const rootDir = INIT_CWD();
  const workspaceNames = new Set(workspaces.map((workspace) => workspace.name));
  const workspaceDeps = {};
  const fingerprints = {};
  const sortedWorkspaces = workspaces.toSorted((a, b) => a.name.localeCompare(b.name));
  for (const workspace of sortedWorkspaces) {
    const pkg = readPackageJson(rootDir, workspace);
    const packageJsonPath = path.resolve(rootDir, workspace.location, "package.json");
    const deps = workspaceDependencyNames(pkg, workspaceNames);
    workspaceDeps[workspace.name] = deps;
    fingerprints[workspace.name] = {
      externalDepsHash: hashJson(externalDeps(pkg, workspaceNames)),
      inputHash: workspaceInputHash(rootDir, workspace),
      location: normalizeLocation(workspace.location),
      name: workspace.name,
      outputHash: workspaceOutputHash(rootDir, workspace),
      packageJsonHash: existsSync3(packageJsonPath) ? hashContent(readFileSync3(packageJsonPath)) : "",
      workspaceDepsHash: hashJson(deps)
    };
  }
  return {
    action,
    lockfileHash: lockfileHash(rootDir, pm),
    lockfileImporters: readPnpmLockfileImporters(rootDir, pm),
    optionsHash: options === void 0 ? void 0 : hashJson(options),
    packageManager: pm.name,
    rootHash: rootHash(rootDir),
    toolchainVersion: safeToolchainVersion(),
    version: snapshotVersion,
    workspaceGraphHash: workspaceGraphHash(workspaceDeps),
    workspaces: fingerprints
  };
}
function snapshotPath(rootDir, action) {
  return path.resolve(rootDir, ".xy/cache/incremental", `${action}.json`);
}
function readSnapshot(action) {
  const filePath = snapshotPath(INIT_CWD(), action);
  if (!existsSync3(filePath)) return void 0;
  try {
    const parsed = JSON.parse(readFileSync3(filePath, "utf8"));
    return parsed.version === snapshotVersion && parsed.action === action ? parsed : void 0;
  } catch {
    return void 0;
  }
}
function didWorkspaceNamesChange(previous, current) {
  const previousEntries = Object.values(previous.workspaces).map((workspace) => `${workspace.name}\0${workspace.location}`).toSorted((a, b) => a.localeCompare(b));
  const currentEntries = Object.values(current.workspaces).map((workspace) => `${workspace.name}\0${workspace.location}`).toSorted((a, b) => a.localeCompare(b));
  return previousEntries.join("\n") !== currentEntries.join("\n");
}
function createDependentsMap(workspaces) {
  const rootDir = INIT_CWD();
  const workspaceNames = new Set(workspaces.map((workspace) => workspace.name));
  const dependents = /* @__PURE__ */ new Map();
  for (const workspace of workspaces) {
    dependents.set(workspace.name, /* @__PURE__ */ new Set());
  }
  for (const workspace of workspaces) {
    const deps = workspaceDependencyNames(readPackageJson(rootDir, workspace), workspaceNames);
    for (const dep of deps) {
      dependents.get(dep)?.add(workspace.name);
    }
  }
  return dependents;
}
function expandWithDependents(workspaceNames, workspaces) {
  const dependents = createDependentsMap(workspaces);
  const queue = [...workspaceNames];
  for (const name of queue) {
    const nameDependents = dependents.get(name) ?? [];
    for (const dependent of nameDependents) {
      if (!workspaceNames.has(dependent)) {
        workspaceNames.add(dependent);
        queue.push(dependent);
      }
    }
  }
  return [...workspaceNames].toSorted((a, b) => a.localeCompare(b));
}
function resolvePnpmLockfileImpact(previous, current, workspaces) {
  const byLocation = new Map(workspaces.map((workspace) => [normalizeLocation(workspace.location), workspace.name]));
  const allImporters = /* @__PURE__ */ new Set([
    ...Object.keys(previous.lockfileImporters),
    ...Object.keys(current.lockfileImporters)
  ]);
  const changedImporters = [...allImporters].filter((importer) => previous.lockfileImporters[importer] !== current.lockfileImporters[importer]).toSorted((a, b) => a.localeCompare(b));
  if (changedImporters.length === 0) {
    return { full: "lockfile changed outside workspace importers", workspaceNames: [] };
  }
  const affected = /* @__PURE__ */ new Set();
  for (const importer of changedImporters) {
    if (importer === ".") {
      return { full: "root lockfile importer changed", workspaceNames: [] };
    }
    const workspaceName = byLocation.get(importer);
    if (!(workspaceName != null && workspaceName.length > 0)) {
      return { full: `lockfile importer changed for unknown workspace: ${importer}`, workspaceNames: [] };
    }
    affected.add(workspaceName);
  }
  return { workspaceNames: [...affected].toSorted((a, b) => a.localeCompare(b)) };
}
function resolveLockfileImpact(previous, current, workspaces) {
  if (previous.lockfileHash === current.lockfileHash) return { workspaceNames: [] };
  if (current.packageManager !== "pnpm") {
    return { full: `${current.packageManager} lockfile changed`, workspaceNames: [] };
  }
  return resolvePnpmLockfileImpact(previous, current, workspaces);
}
function fullPlan(action, current, previous, reason) {
  return {
    action,
    affectedWorkspaceNames: Object.keys(current.workspaces).toSorted((a, b) => a.localeCompare(b)),
    current,
    full: true,
    previous,
    reason
  };
}
function compatibilityReason(previous, current) {
  if (previous.version !== snapshotVersion) return "snapshot version changed";
  if (previous.packageManager !== current.packageManager) return "package manager changed";
  if (previous.toolchainVersion !== current.toolchainVersion) return "toolchain version changed";
  if (previous.optionsHash !== current.optionsHash) return `${current.action} options changed`;
  if (didWorkspaceNamesChange(previous, current)) return "workspace set changed";
  if (previous.rootHash !== current.rootHash) return "root configuration changed";
  return void 0;
}
function isTrackingOutputs(action) {
  return action !== "lint";
}
function isExpandingDependents(action) {
  return action !== "lint";
}
function didWorkspaceChange(action, previous, current) {
  return previous.inputHash !== current.inputHash || isTrackingOutputs(action) && previous.outputHash !== current.outputHash || previous.packageJsonHash !== current.packageJsonHash || previous.workspaceDepsHash !== current.workspaceDepsHash || previous.externalDepsHash !== current.externalDepsHash;
}
function collectChangedWorkspaceNames(action, previous, current) {
  const affected = /* @__PURE__ */ new Set();
  for (const [name, workspace] of Object.entries(current.workspaces)) {
    const prior = previous.workspaces[name];
    if (prior === void 0) return { affected, fullReason: `workspace added: ${name}` };
    if (didWorkspaceChange(action, prior, workspace)) affected.add(name);
  }
  if (previous.workspaceGraphHash !== current.workspaceGraphHash) {
    for (const [name, workspace] of Object.entries(current.workspaces)) {
      if (previous.workspaces[name]?.workspaceDepsHash !== workspace.workspaceDepsHash) affected.add(name);
    }
  }
  return { affected };
}
function createIncrementalPlan(params) {
  const current = createCurrentSnapshot(params);
  const previous = readSnapshot(params.action);
  const {
    action,
    isVerbose,
    workspaces
  } = params;
  if (!previous) return fullPlan(action, current, previous, "no previous snapshot");
  const incompatible = compatibilityReason(previous, current);
  if (incompatible != null && incompatible.length > 0) return fullPlan(action, current, previous, incompatible);
  const { affected, fullReason } = collectChangedWorkspaceNames(action, previous, current);
  if (fullReason != null && fullReason.length > 0) return fullPlan(action, current, previous, fullReason);
  const lockImpact = resolveLockfileImpact(previous, current, workspaces);
  if (lockImpact.full != null && lockImpact.full.length > 0) return fullPlan(action, current, previous, lockImpact.full);
  for (const workspaceName of lockImpact.workspaceNames) {
    affected.add(workspaceName);
  }
  const affectedWorkspaceNames = isExpandingDependents(action) ? expandWithDependents(affected, workspaces) : [...affected].toSorted((a, b) => a.localeCompare(b));
  if ((isVerbose ?? false) && affectedWorkspaceNames.length > 0) {
    console.log(`Incremental ${action}: ${affectedWorkspaceNames.join(", ")}`);
  }
  return {
    action,
    affectedWorkspaceNames,
    current,
    full: false,
    previous
  };
}
function writeIncrementalSnapshot(params) {
  const rootDir = INIT_CWD();
  const filePath = snapshotPath(rootDir, params.action);
  mkdirSync(path.dirname(filePath), { recursive: true });
  writeFileSync(filePath, `${JSON.stringify(createCurrentSnapshot(params), null, 2)}
`);
}

// src/actions/package/compile/inputs.ts
import chalk4 from "chalk";
import { glob } from "glob";
var getAllInputs = (srcDir, isVerbose = false) => {
  return glob.sync(`${srcDir}/**/*.{ts,tsx,mts,cts}`, { posix: true }).map((file) => {
    const result = file.slice(Math.max(0, srcDir.length + 1));
    if (isVerbose) {
      console.log(chalk4.gray(`getAllInputs: ${JSON.stringify(result, null, 2)}`));
    }
    return result;
  });
};

// src/actions/package/compile/buildEntries.ts
var bareSpecOrStoryRe = /(?:^|\/)(?:spec|stories)\.(?:ts|tsx|mts|cts)$/;
var isSpecOrStory = (entry) => {
  return entry.includes(".spec.") || entry.includes(".stories.") || entry.startsWith("spec/") || entry.includes("/spec/") || entry.startsWith("stories/") || entry.includes("/stories/") || bareSpecOrStoryRe.test(entry);
};
var isDeclaration = (entry) => /\.d\.(?:ts|mts|cts)$/.test(entry);
var buildEntries = (srcDir, entryMode = "single", options, shouldExcludeSpecAndStories = true, isVerbose = false) => {
  let entries;
  switch (entryMode) {
    case "platform": {
      entries = ["index-node.ts", "index-browser.ts"];
      break;
    }
    case "all": {
      entries = (shouldExcludeSpecAndStories ? getAllInputs(srcDir).filter((entry) => !isSpecOrStory(entry)) : getAllInputs(srcDir)).filter((entry) => !isDeclaration(entry));
      break;
    }
    case "custom": {
      entries = [];
      break;
    }
    default: {
      entries = ["index.ts"];
      break;
    }
  }
  if (typeof options !== "boolean" && Array.isArray(options?.entry)) {
    entries.push(...options.entry);
  }
  if (isVerbose) console.log(`buildEntries [${entryMode}] ${entries.length}`);
  return entries;
};

// src/actions/package/compile/entryOutputName.ts
import path2 from "node:path";
var platformSourceSegments = /* @__PURE__ */ new Set(["browser", "neutral", "node"]);
var toPosixPath = (value) => value.split(path2.sep).join("/");
var normalizeEntryPath = (entry) => toPosixPath(entry).replace(/^\.?\//u, "");
var customEntryOutputName = (entry) => {
  const parsed = path2.posix.parse(normalizeEntryPath(entry));
  const withoutExtension = path2.posix.join(parsed.dir, parsed.name);
  const segments = withoutExtension.split("/");
  return segments.length > 1 && platformSourceSegments.has(segments[0]) ? segments.slice(1).join("/") : withoutExtension;
};
var entryOutputBasename = (entry, entryMode) => {
  if (entryMode === "custom") return customEntryOutputName(entry);
  return path2.posix.parse(normalizeEntryPath(entry)).name;
};

// src/actions/package/compile/resolveCompilePlatforms.ts
import chalk5 from "chalk";
var resolvePlatformRecord = (value) => {
  if (value === void 0 || value === true) return { src: {} };
  if (value === false) return {};
  const { esbuildOptions: _legacyEsbuildOptions, ...entries } = value;
  return entries;
};
var classifyPlatform = (value) => {
  if (value === void 0) {
    return {
      deprecatedEmptyObject: false,
      entries: {},
      state: "unset"
    };
  }
  if (typeof value === "boolean") {
    return {
      deprecatedEmptyObject: false,
      entries: value ? { src: {} } : {},
      state: value ? "build" : "off"
    };
  }
  const isDeprecatedEmptyObject = Object.keys(value).length === 0;
  const entries = resolvePlatformRecord(value);
  return {
    deprecatedEmptyObject: isDeprecatedEmptyObject,
    entries,
    state: Object.keys(entries).length > 0 ? "build" : "off"
  };
};
var resolveCompilePlatforms = (compile) => {
  const node = classifyPlatform(compile?.node);
  const browser = classifyPlatform(compile?.browser);
  const neutral = classifyPlatform(compile?.neutral);
  const isAnyBuild = node.state === "build" || browser.state === "build" || neutral.state === "build";
  const neutralEntries = neutral.state === "build" ? neutral.entries : !isAnyBuild && neutral.state === "unset" ? { src: {} } : {};
  const deprecatedEmptyObjectTargets = [
    ...node.deprecatedEmptyObject ? ["node"] : [],
    ...browser.deprecatedEmptyObject ? ["browser"] : [],
    ...neutral.deprecatedEmptyObject ? ["neutral"] : []
  ];
  return {
    browser: browser.state === "build" ? browser.entries : {},
    deprecatedEmptyObjectTargets,
    neutral: neutralEntries,
    node: node.state === "build" ? node.entries : {}
  };
};

// src/actions/package/compile/XyConfig.ts
var ALL_PUBLINT_CHECKS = [
  "compileTargets",
  "files",
  "importToDefault",
  "main",
  "module",
  "peerDeps",
  "platform",
  "publint",
  "resolutions",
  "rootSource",
  "rootTypes",
  "sideEffects",
  "source",
  "types"
];
var PUBLISH_ONLY_CHECKS = [
  "files",
  "importToDefault",
  "main",
  "module",
  "publint",
  "rootSource",
  "rootTypes",
  "sideEffects",
  "source",
  "types"
];

// src/actions/package/publint.ts
import { promises as fs6 } from "node:fs";
import path7 from "node:path";
import chalk9 from "chalk";
import { glob as glob2 } from "glob";
import sortPackageJson from "sort-package-json";

// src/actions/package/compileTargetsPublint.ts
import fs2 from "node:fs";
import path3 from "node:path";
import chalk6 from "chalk";
import ts from "typescript";
var COMPILE_TARGET_NAMES = /* @__PURE__ */ new Set(["browser", "neutral", "node"]);
var CONFIG_FILE_NAMES = [
  "xy.config.ts",
  "xy.config.mts",
  "xy.config.cts",
  "xy.config.js",
  "xy.config.mjs",
  "xy.config.cjs"
];
function emptyResult() {
  return {
    errors: 0,
    modified: false,
    warnings: 0
  };
}
function findConfigFile(pkgDir) {
  for (const name of CONFIG_FILE_NAMES) {
    const candidate = path3.join(pkgDir, name);
    if (fs2.existsSync(candidate)) return candidate;
  }
  return void 0;
}
function getPropertyName(prop) {
  if (!ts.isPropertyAssignment(prop)) return void 0;
  const { name } = prop;
  if (ts.isIdentifier(name) || ts.isStringLiteral(name)) return name.text;
  return void 0;
}
function collectEmptyTargetProps(sourceFile) {
  const found = [];
  function visit(node) {
    if (ts.isPropertyAssignment(node) && getPropertyName(node) === "compile" && ts.isObjectLiteralExpression(node.initializer)) {
      for (const prop of node.initializer.properties) {
        if (ts.isPropertyAssignment(prop) && COMPILE_TARGET_NAMES.has(getPropertyName(prop) ?? "") && ts.isObjectLiteralExpression(prop.initializer) && prop.initializer.properties.length === 0) {
          found.push(prop);
        }
      }
    }
    ts.forEachChild(node, visit);
  }
  visit(sourceFile);
  return found;
}
function isWhitespace(char) {
  return char !== void 0 && /\s/.test(char);
}
function removeProperties(text, props, sourceFile) {
  const sorted = props.toSorted((a, b) => b.getStart(sourceFile) - a.getStart(sourceFile));
  let result = text;
  for (const prop of sorted) {
    let start = prop.getFullStart();
    let end = prop.getEnd();
    let cursor = end;
    while (isWhitespace(result[cursor])) cursor++;
    if (result[cursor] === ",") {
      end = cursor + 1;
    } else {
      let back = prop.getStart(sourceFile) - 1;
      while (back >= 0 && isWhitespace(result[back])) back--;
      if (result[back] === ",") start = back;
    }
    result = result.slice(0, start) + result.slice(end);
  }
  return result;
}
function checkCompileTargets(pkgDir, shouldFix) {
  const result = emptyResult();
  const configPath = findConfigFile(pkgDir);
  if (configPath === void 0) return result;
  const text = fs2.readFileSync(configPath, "utf8");
  const sourceFile = ts.createSourceFile(path3.basename(configPath), text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
  const emptyProps = collectEmptyTargetProps(sourceFile);
  if (emptyProps.length === 0) return result;
  const configName = path3.basename(configPath);
  if (shouldFix) {
    fs2.writeFileSync(configPath, removeProperties(text, emptyProps, sourceFile));
    for (const prop of emptyProps) {
      console.warn(chalk6.yellow(`Publint [compileTargets]: removed deprecated empty "compile.${getPropertyName(prop)}" target from ${configName}`));
    }
    result.modified = true;
  } else {
    for (const prop of emptyProps) {
      console.warn(chalk6.yellow(
        `Publint [compileTargets]: "compile.${getPropertyName(prop)} = {}" is treated as "do not build" \u2014 set it to \`false\` or remove it (use --shouldFix to remove) in ${configName}`
      ));
    }
  }
  result.warnings += emptyProps.length;
  return result;
}

// src/actions/package/platformPublint.ts
import fs5 from "node:fs";
import { builtinModules } from "node:module";
import path6 from "node:path";
import chalk8 from "chalk";
import { globSync as globSync2 } from "glob";
import ts2 from "typescript";

// src/actions/package/exportMapPublint.ts
import fs4 from "node:fs";
import path5 from "node:path";
import chalk7 from "chalk";

// src/actions/package/expectedCompileOutputs.ts
import fs3 from "node:fs";
import path4 from "node:path";
import { cosmiconfig as cosmiconfig2 } from "cosmiconfig";
import { TypeScriptLoader as TypeScriptLoader2 } from "cosmiconfig-typescript-loader";
var CONFIG_FILE_NAMES2 = [
  "xy.config.ts",
  "xy.config.mts",
  "xy.config.cts",
  "xy.config.js",
  "xy.config.mjs",
  "xy.config.cjs"
];
var PRIMARY_INDEX_BASENAMES = /* @__PURE__ */ new Set([
  "index",
  "index-browser",
  "index-neutral",
  "index-node",
  "index.browser",
  "index.node",
  "index.neutral",
  "browser-index",
  "neutral-index",
  "node-index"
]);
function ensureExportsPath(value) {
  return value.startsWith("./") ? value : `./${value}`;
}
function isPrimaryIndexBasename(basename) {
  return PRIMARY_INDEX_BASENAMES.has(basename);
}
function getRootOutDir(options) {
  return options.outDir ?? options.outdir ?? "dist";
}
function pairedDeclarationFile(runtimeFile) {
  const candidates = runtimeFile.endsWith(".mjs") ? [runtimeFile.replace(/\.mjs$/, ".d.mts"), runtimeFile.replace(/\.mjs$/, ".d.ts")] : runtimeFile.endsWith(".cjs") ? [runtimeFile.replace(/\.cjs$/, ".d.cts"), runtimeFile.replace(/\.cjs$/, ".d.ts")] : runtimeFile.endsWith(".js") ? [runtimeFile.replace(/\.js$/, ".d.ts")] : [];
  return candidates.find((candidate) => fs3.existsSync(candidate) && fs3.statSync(candidate).isFile());
}
function relativeExportsTarget(pkgDir, filePath) {
  return ensureExportsPath(toPosixPath(path4.relative(pkgDir, filePath)));
}
async function loadMergedWorkspaceConfig(pkgDir) {
  const root = await loadConfig();
  const explorer = cosmiconfig2("xy", { cache: true, loaders: { ".ts": TypeScriptLoader2() } });
  const result = await explorer.search(pkgDir);
  if (result?.config === void 0) return root;
  return deepMerge(root, result.config);
}
function hasWorkspaceConfig(pkgDir) {
  return CONFIG_FILE_NAMES2.some((name) => fs3.existsSync(path4.join(pkgDir, name)));
}
function outputForEntry(pkgDir, platform, rootOutDir, entry, entryMode) {
  const basename = entryOutputBasename(entry, entryMode ?? "single");
  const runtimeAbs = path4.join(pkgDir, rootOutDir, platform, `${basename}.mjs`);
  if (!fs3.existsSync(runtimeAbs)) return void 0;
  const runtime = relativeExportsTarget(pkgDir, runtimeAbs);
  const declarationFile = pairedDeclarationFile(runtimeAbs);
  return {
    entry,
    isPrimaryIndex: false,
    platform,
    runtime,
    types: declarationFile === void 0 ? void 0 : relativeExportsTarget(pkgDir, declarationFile)
  };
}
function collectOutputsForPlatform(pkgDir, platform, entries, compile) {
  const outputs = [];
  const entryMode = compile?.entryMode ?? "single";
  for (const [srcDir, options] of Object.entries(entries)) {
    const rootOutDir = getRootOutDir(options);
    const builtEntries = buildEntries(srcDir, entryMode, options, true, false);
    for (const entry of builtEntries) {
      const output = outputForEntry(pkgDir, platform, rootOutDir, entry, entryMode);
      if (output !== void 0) outputs.push(output);
    }
  }
  const byBasename = /* @__PURE__ */ new Map();
  for (const output of outputs) {
    const basename = path4.posix.parse(output.runtime).name;
    const group = byBasename.get(basename) ?? [];
    group.push(output);
    byBasename.set(basename, group);
  }
  for (const output of outputs) {
    const basename = path4.posix.parse(output.runtime).name;
    const group = byBasename.get(basename) ?? [];
    output.isPrimaryIndex = isPrimaryIndexBasename(basename) || group.length === 1;
  }
  return outputs;
}
async function collectExpectedCompileOutputs(pkgDir) {
  if (!hasWorkspaceConfig(pkgDir)) return [];
  const config = await loadMergedWorkspaceConfig(pkgDir);
  const compile = config.compile;
  if (compile === void 0) return [];
  const platforms = resolveCompilePlatforms(compile);
  return [
    ...collectOutputsForPlatform(pkgDir, "node", platforms.node, compile),
    ...collectOutputsForPlatform(pkgDir, "browser", platforms.browser, compile),
    ...collectOutputsForPlatform(pkgDir, "neutral", platforms.neutral, compile)
  ];
}

// src/actions/package/exportMapPublint.ts
var nodeConditionNames = /* @__PURE__ */ new Set(["node", "node-addons"]);
var browserConditionNames = /* @__PURE__ */ new Set(["browser", "web", "webworker", "worker"]);
var neutralConditionNames = /* @__PURE__ */ new Set(["neutral"]);
var platformSegments = /* @__PURE__ */ new Set(["browser", "neutral", "node"]);
function emptyResult2() {
  return {
    errors: 0,
    modified: false,
    warnings: 0
  };
}
function toPosixPath2(value) {
  return value.split(path5.sep).join("/");
}
function isSubpathExportKey(key) {
  return key === "." || key.startsWith("./");
}
function formatExportPath(pathParts) {
  return pathParts.map((part, index) => {
    if (index === 0) return part;
    if (part.startsWith("[")) return part;
    return /^[a-zA-Z_$][\w$]*$/.test(part) ? `.${part}` : `[${JSON.stringify(part)}]`;
  }).join("");
}
function ensureExportsPath2(value) {
  return value.startsWith("./") ? value : `./${value}`;
}
function isRecord2(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function normalizeTarget(target) {
  return ensureExportsPath2(toPosixPath2(target));
}
function conditionPlatform(key) {
  if (nodeConditionNames.has(key)) return "node";
  if (browserConditionNames.has(key)) return "browser";
  if (neutralConditionNames.has(key)) return "neutral";
  return void 0;
}
function getConditionPlatform(conditionPath) {
  for (const condition of conditionPath.toReversed()) {
    const platform = conditionPlatform(condition);
    if (platform) return platform;
  }
  return void 0;
}
function getPathPlatform(target) {
  const segments = target.replace(/^\.\//, "").split(/[\\/]+/);
  const distIndex = segments.indexOf("dist");
  const platform = distIndex === -1 ? void 0 : segments[distIndex + 1];
  return platformSegments.has(platform) ? platform : void 0;
}
function collectExportLeaves(value, subpath = ".", pathParts = ["exports"], conditionPath = []) {
  if (typeof value === "string") {
    if (!value.startsWith("./")) return [];
    const leafKey = pathParts.at(-1);
    const kind = leafKey === "types" ? "types" : "default";
    return [{
      conditionPath,
      conditionPlatform: getConditionPlatform(conditionPath),
      exportPath: formatExportPath(pathParts),
      kind,
      subpath,
      target: value
    }];
  }
  if (Array.isArray(value)) {
    return value.flatMap((entry, index) => collectExportLeaves(entry, subpath, [...pathParts, `[${index}]`], conditionPath));
  }
  if (!isRecord2(value)) return [];
  return Object.entries(value).flatMap(([key, nested]) => {
    if (isSubpathExportKey(key)) {
      return collectExportLeaves(nested, key, [...pathParts, key], conditionPath);
    }
    return collectExportLeaves(nested, subpath, [...pathParts, key], [...conditionPath, key]);
  });
}
function isCompatiblePlatform(expected, leaf) {
  const pathPlatform = getPathPlatform(leaf.target);
  if (pathPlatform !== void 0 && pathPlatform !== expected) return false;
  if (leaf.conditionPlatform === void 0) return true;
  if (expected === "neutral") return leaf.conditionPlatform === "neutral";
  return leaf.conditionPlatform === expected;
}
function findExportedLeaf(leaves, target, kind, platform) {
  const normalized = normalizeTarget(target);
  return leaves.find((leaf) => leaf.kind === kind && normalizeTarget(leaf.target) === normalized && isCompatiblePlatform(platform, leaf));
}
function hasTypesOnlyExport(leaves, expected) {
  if (expected.types === void 0) return false;
  const typesLeaf = findExportedLeaf(leaves, expected.types, "types", expected.platform);
  if (typesLeaf === void 0) return false;
  return findExportedLeaf(leaves, expected.runtime, "default", expected.platform) === void 0;
}
function hasTypesForRuntimeSubpath(leaves, runtimeLeaf, pkgDir) {
  return leaves.some((leaf) => {
    if (leaf.subpath !== runtimeLeaf.subpath || leaf.kind !== "types") return false;
    const absolute = path5.resolve(pkgDir, leaf.target);
    return fs4.existsSync(absolute);
  });
}
function exportMapDiagnostics(pkgDir, pkg, expectedOutputs) {
  const leaves = collectExportLeaves(pkg.exports);
  const diagnostics = [];
  for (const expected of expectedOutputs) {
    const runtimeLeaf = findExportedLeaf(leaves, expected.runtime, "default", expected.platform);
    const isTypesOnly = hasTypesOnlyExport(leaves, expected);
    if (runtimeLeaf === void 0 && !isTypesOnly) {
      diagnostics.push({
        entry: expected.entry,
        kind: "export-map-missing-runtime",
        platform: expected.platform,
        target: expected.runtime
      });
    }
    if (expected.types === void 0) continue;
    const typesLeaf = findExportedLeaf(leaves, expected.types, "types", expected.platform);
    if (typesLeaf !== void 0) continue;
    if (runtimeLeaf !== void 0 && hasTypesForRuntimeSubpath(leaves, runtimeLeaf, pkgDir)) continue;
    if (isTypesOnly) continue;
    diagnostics.push({
      entry: expected.entry,
      kind: "export-map-missing-types",
      platform: expected.platform,
      target: expected.types
    });
  }
  return diagnostics;
}
function renderDiagnostic(diagnostic) {
  switch (diagnostic.kind) {
    case "export-map-missing-runtime": {
      return `Publint [platform]: compiled ${diagnostic.platform} entry "${diagnostic.entry}" (${diagnostic.target}) is not exported`;
    }
    case "export-map-missing-types": {
      return `Publint [platform]: compiled ${diagnostic.platform} entry "${diagnostic.entry}" is missing types export for ${diagnostic.target}`;
    }
    case "export-map-wrong-target": {
      return `Publint [platform]: ${diagnostic.detail}`;
    }
  }
}
function diagnosticKey(diagnostic) {
  return [
    diagnostic.kind,
    diagnostic.platform ?? "",
    diagnostic.target ?? "",
    diagnostic.entry ?? "",
    diagnostic.detail ?? ""
  ].join("\0");
}
function dedupeDiagnostics(diagnostics) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const diagnostic of diagnostics) {
    const key = diagnosticKey(diagnostic);
    if (seen.has(key)) continue;
    seen.add(key);
    result.push(diagnostic);
  }
  return result;
}
function mergeResult(target, source) {
  target.errors += source.errors;
  target.warnings += source.warnings;
  target.modified ||= source.modified;
}
function hasSubpathKeys(exports) {
  return Object.keys(exports).some(isSubpathExportKey);
}
function shouldNormalizeExportsForFix(pkg) {
  if (pkg.exports === void 0) {
    pkg.exports = {};
    return true;
  }
  if (typeof pkg.exports === "string") {
    pkg.exports = { ".": pkg.exports };
    return true;
  }
  if (isRecord2(pkg.exports) && !hasSubpathKeys(pkg.exports)) {
    pkg.exports = { ".": pkg.exports };
    return true;
  }
  return false;
}
function isRecordObject(value) {
  return isRecord2(value);
}
function ensureSubpath(exports, subpath) {
  const existing = exports[subpath];
  if (isRecordObject(existing)) return existing;
  const created = {};
  exports[subpath] = created;
  return created;
}
function setFlatExport(subpathExport, kind, target) {
  if (subpathExport[kind] === void 0) {
    subpathExport[kind] = target;
  }
}
function setConditionExport(subpathExport, platform, kind, target) {
  const conditionKey = platform === "neutral" ? "neutral" : platform;
  const existing = subpathExport[conditionKey];
  const conditionExport = isRecordObject(existing) ? existing : {};
  if (!isRecordObject(existing)) subpathExport[conditionKey] = conditionExport;
  if (conditionExport[kind] === void 0) conditionExport[kind] = target;
}
function preferredSubpath(expected) {
  if (expected.isPrimaryIndex) return ".";
  const basename = path5.posix.parse(expected.runtime).name;
  return `./${basename}`;
}
function activePlatforms(outputs) {
  return [...new Set(outputs.map((output) => output.platform))];
}
function fixMissingExport(pkg, expected, allOutputs) {
  const result = emptyResult2();
  const isNormalized = shouldNormalizeExportsForFix(pkg);
  if (!isRecordObject(pkg.exports)) {
    result.modified = isNormalized;
    return result;
  }
  const subpath = preferredSubpath(expected);
  const subpathExport = ensureSubpath(pkg.exports, subpath);
  const subpathOutputs = allOutputs.filter((output) => preferredSubpath(output) === subpath);
  const platforms = activePlatforms(subpathOutputs);
  const isUseConditions = platforms.length > 1 || subpath === "." && expected.platform !== "neutral";
  if (isUseConditions) {
    setConditionExport(subpathExport, expected.platform, "default", expected.runtime);
    if (expected.types !== void 0) setConditionExport(subpathExport, expected.platform, "types", expected.types);
  } else {
    setFlatExport(subpathExport, "default", expected.runtime);
    if (expected.types !== void 0) setFlatExport(subpathExport, "types", expected.types);
  }
  console.warn(chalk7.yellow(
    `Publint [platform]: added export for ${expected.runtime} at exports[${JSON.stringify(subpath)}]`
  ));
  result.modified = true;
  result.warnings++;
  result.modified ||= isNormalized;
  return result;
}
async function checkExportMappings(pkgDir, pkg, shouldFix) {
  const result = emptyResult2();
  const expectedOutputs = await collectExpectedCompileOutputs(pkgDir);
  if (expectedOutputs.length === 0) return result;
  const diagnostics = dedupeDiagnostics(exportMapDiagnostics(pkgDir, pkg, expectedOutputs));
  if (shouldFix) {
    for (const diagnostic of diagnostics) {
      if (diagnostic.kind !== "export-map-missing-runtime" && diagnostic.kind !== "export-map-missing-types") continue;
      const expected = expectedOutputs.find((output) => output.entry === diagnostic.entry && output.platform === diagnostic.platform);
      if (expected === void 0) continue;
      mergeResult(result, fixMissingExport(pkg, expected, expectedOutputs));
    }
    return result;
  }
  for (const diagnostic of diagnostics) {
    console.error(chalk7.red(renderDiagnostic(diagnostic)));
    result.errors++;
  }
  return result;
}

// src/actions/package/platformPublint.ts
var platformSegments2 = /* @__PURE__ */ new Set(["browser", "neutral", "node"]);
var nodeConditionNames2 = /* @__PURE__ */ new Set(["node", "node-addons"]);
var browserConditionNames2 = /* @__PURE__ */ new Set(["browser", "web", "webworker", "worker"]);
var neutralConditionNames2 = /* @__PURE__ */ new Set(["neutral"]);
var builtinModuleNames = new Set(
  builtinModules.map((mod) => mod.replace(/^node:/, "")).filter((mod) => !mod.startsWith("_"))
);
var codeExtensions = [".mjs", ".cjs", ".js"];
var declarationExtensions = [".d.mts", ".d.cts", ".d.ts"];
function toPosixPath3(value) {
  return value.split(path6.sep).join("/");
}
function relativePath(pkgDir, filePath) {
  return toPosixPath3(path6.relative(pkgDir, filePath));
}
function isSubpathExportKey2(key) {
  return key === "." || key.startsWith("./");
}
function formatExportPath2(pathParts) {
  return pathParts.map((part, index) => {
    if (index === 0) return part;
    if (part.startsWith("[")) return part;
    return /^[a-zA-Z_$][\w$]*$/.test(part) ? `.${part}` : `[${JSON.stringify(part)}]`;
  }).join("");
}
function conditionPlatform2(key) {
  if (nodeConditionNames2.has(key)) return "node";
  if (browserConditionNames2.has(key)) return "browser";
  if (neutralConditionNames2.has(key)) return "neutral";
  return void 0;
}
function getConditionPlatform2(conditionPath) {
  for (const condition of conditionPath.toReversed()) {
    const platform = conditionPlatform2(condition);
    if (platform) return platform;
  }
  return void 0;
}
function getPathPlatform2(target) {
  const segments = target.replace(/^\.\//, "").split(/[\\/]+/);
  const distIndex = segments.indexOf("dist");
  const platform = distIndex === -1 ? void 0 : segments[distIndex + 1];
  return platformSegments2.has(platform) ? platform : void 0;
}
function collectExportTargets(value, pathParts = ["exports"], conditionPath = []) {
  if (typeof value === "string") {
    if (!value.startsWith("./")) return [];
    return [{
      conditionPath,
      conditionPlatform: getConditionPlatform2(conditionPath),
      exportPath: formatExportPath2(pathParts),
      pathPlatform: getPathPlatform2(value),
      target: value
    }];
  }
  if (Array.isArray(value)) {
    return value.flatMap((entry, index) => collectExportTargets(entry, [...pathParts, `[${index}]`], conditionPath));
  }
  if (typeof value !== "object" || value === null) return [];
  return Object.entries(value).flatMap(([key, nested]) => {
    const nextConditionPath = isSubpathExportKey2(key) ? conditionPath : [...conditionPath, key];
    return collectExportTargets(nested, [...pathParts, key], nextConditionPath);
  });
}
function isDeclarationFile(filePath) {
  return declarationExtensions.some((ext) => filePath.endsWith(ext));
}
function isCodeFile(filePath) {
  return codeExtensions.some((ext) => filePath.endsWith(ext));
}
function hasCodeOrDeclarationExtension(filePath) {
  return isCodeFile(filePath) || isDeclarationFile(filePath);
}
function pathCandidates(filePath, extensions) {
  const candidates = [filePath];
  const parsed = path6.parse(filePath);
  const withoutExtension = parsed.ext ? filePath.slice(0, -parsed.ext.length) : filePath;
  for (const ext of extensions) {
    candidates.push(`${withoutExtension}${ext}`, `${filePath}${ext}`, path6.join(filePath, `index${ext}`));
  }
  return [...new Set(candidates)];
}
function declarationCandidates(filePath) {
  const extensionCandidates = filePath.endsWith(".mjs") ? [filePath.replace(/\.mjs$/, ".d.mts"), filePath.replace(/\.mjs$/, ".d.ts")] : filePath.endsWith(".cjs") ? [filePath.replace(/\.cjs$/, ".d.cts"), filePath.replace(/\.cjs$/, ".d.ts")] : filePath.endsWith(".js") ? [filePath.replace(/\.js$/, ".d.ts")] : [];
  return [.../* @__PURE__ */ new Set([filePath, ...extensionCandidates, ...pathCandidates(filePath, declarationExtensions)])];
}
function resolveExistingFile(filePath, hasDeclaration) {
  const candidates = hasDeclaration ? declarationCandidates(filePath) : pathCandidates(filePath, codeExtensions);
  return candidates.find((candidate) => fs5.existsSync(candidate) && fs5.statSync(candidate).isFile());
}
function resolveExportTargetFiles(pkgDir, target) {
  if (!hasCodeOrDeclarationExtension(target) && !target.includes("*")) return [];
  const relativeTarget = target.replace(/^\.\//, "");
  if (relativeTarget.includes("*")) {
    return globSync2(relativeTarget, {
      cwd: pkgDir,
      nodir: true,
      absolute: true
    }).filter(hasCodeOrDeclarationExtension);
  }
  const absoluteTarget = path6.resolve(pkgDir, relativeTarget);
  const exact = fs5.existsSync(absoluteTarget) && fs5.statSync(absoluteTarget).isFile() ? absoluteTarget : resolveExistingFile(absoluteTarget, isDeclarationFile(relativeTarget));
  return exact != null && exact.length > 0 ? [exact] : [];
}
function isNodeBuiltin(specifier) {
  const normalized = specifier.replace(/^node:/, "");
  return specifier === "node" || specifier.startsWith("node:") || builtinModuleNames.has(normalized) || builtinModuleNames.has(normalized.split("/", 1)[0] ?? "");
}
function parseSourceFile(filePath) {
  const sourceCode = fs5.readFileSync(filePath, "utf8");
  const scriptKind = isCodeFile(filePath) ? ts2.ScriptKind.JS : ts2.ScriptKind.TS;
  return ts2.createSourceFile(path6.basename(filePath), sourceCode, ts2.ScriptTarget.Latest, true, scriptKind);
}
function stringLiteralText(node) {
  return node && ts2.isStringLiteral(node) ? node.text : void 0;
}
function collectModuleSpecifiers(sourceFile) {
  const specifiers = [];
  function addSpecifier(node) {
    const text = stringLiteralText(node);
    if (text != null && text.length > 0) specifiers.push(text);
  }
  function visit(node) {
    if (ts2.isImportDeclaration(node) || ts2.isExportDeclaration(node)) {
      addSpecifier(node.moduleSpecifier);
    } else if (ts2.isCallExpression(node) && node.expression.kind === ts2.SyntaxKind.ImportKeyword) {
      addSpecifier(node.arguments[0]);
    } else if (ts2.isCallExpression(node) && ts2.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length > 0) {
      addSpecifier(node.arguments[0]);
    } else if (ts2.isImportTypeNode(node)) {
      if (ts2.isLiteralTypeNode(node.argument)) addSpecifier(node.argument.literal);
    } else if (ts2.isImportEqualsDeclaration(node) && ts2.isExternalModuleReference(node.moduleReference)) {
      addSpecifier(node.moduleReference.expression);
    }
    ts2.forEachChild(node, visit);
  }
  visit(sourceFile);
  for (const ref of sourceFile.typeReferenceDirectives) {
    specifiers.push(ref.fileName);
  }
  return specifiers;
}
function resolveLocalSpecifier(filePath, specifier, hasDeclaration) {
  if (!specifier.startsWith(".")) return void 0;
  return resolveExistingFile(path6.resolve(path6.dirname(filePath), specifier), hasDeclaration);
}
function nodeAmbientGlobalFromName(name) {
  if (name === "NodeJS") return "NodeJS";
  if (name === "Buffer") return "Buffer";
  if (name === "process") return "process";
  return void 0;
}
function getQualifiedRootName(name) {
  return ts2.isIdentifier(name) ? name.text : getQualifiedRootName(name.left);
}
function getPropertyAccessRootName(expression) {
  let current = expression;
  while (ts2.isPropertyAccessExpression(current)) {
    current = current.expression;
  }
  return ts2.isIdentifier(current) ? current.text : "";
}
function getNodeAmbientGlobal(sourceFile) {
  let ambient;
  function visit(node) {
    if (ambient != null && ambient.length > 0) return;
    if (ts2.isTypeReferenceNode(node)) {
      ambient = nodeAmbientGlobalFromName(getQualifiedRootName(node.typeName));
    } else if (ts2.isExpressionWithTypeArguments(node) && ts2.isIdentifier(node.expression)) {
      ambient = nodeAmbientGlobalFromName(node.expression.text);
    } else if (ts2.isExpressionWithTypeArguments(node) && ts2.isPropertyAccessExpression(node.expression)) {
      ambient = nodeAmbientGlobalFromName(getPropertyAccessRootName(node.expression));
    } else if (ts2.isTypeQueryNode(node)) {
      ambient = nodeAmbientGlobalFromName(getQualifiedRootName(node.exprName));
    }
    ts2.forEachChild(node, visit);
  }
  visit(sourceFile);
  return ambient;
}
function scanFileGraph(rootFile, platform, target) {
  const isDeclaration2 = isDeclarationFile(rootFile);
  const queue = [rootFile];
  const seen = /* @__PURE__ */ new Set();
  const diagnostics = [];
  while (queue.length > 0) {
    const file = queue.shift();
    if (!(file != null && file.length > 0) || seen.has(file)) continue;
    seen.add(file);
    const sourceFile = parseSourceFile(file);
    for (const specifier of collectModuleSpecifiers(sourceFile)) {
      if (isNodeBuiltin(specifier)) {
        diagnostics.push({
          detail: specifier,
          exportPath: target.exportPath,
          file,
          kind: "node-builtin-import",
          platform,
          target: target.target
        });
      }
      const resolved = resolveLocalSpecifier(file, specifier, isDeclaration2);
      if (resolved != null && resolved.length > 0 && !seen.has(resolved)) queue.push(resolved);
    }
    if (isDeclaration2) {
      const ambient = getNodeAmbientGlobal(sourceFile);
      if (ambient != null && ambient.length > 0) {
        diagnostics.push({
          detail: ambient,
          exportPath: target.exportPath,
          file,
          kind: "node-ambient-type",
          platform,
          target: target.target
        });
      }
    }
  }
  return diagnostics;
}
function shouldScanForNodeUsage(target) {
  if (target.pathPlatform === "browser" || target.pathPlatform === "neutral") return target.pathPlatform;
  if (target.conditionPlatform === "browser" || target.conditionPlatform === "neutral") return target.conditionPlatform;
  return void 0;
}
function collectConditionTargetDiagnostics(targets) {
  const diagnostics = [];
  for (const target of targets) {
    const { conditionPlatform: condition, pathPlatform: output } = target;
    if (!condition || !output) continue;
    if (condition === "browser" && output === "node" || condition === "node" && output === "browser" || condition === "neutral" && output !== "neutral") {
      diagnostics.push({
        detail: `${condition} condition points to ${output} output`,
        exportPath: target.exportPath,
        kind: "condition-target-mismatch",
        platform: condition,
        target: target.target
      });
    }
  }
  return diagnostics;
}
function isRuntimeTarget(target) {
  return codeExtensions.some((ext) => target.target.endsWith(ext));
}
function isPortableTarget(target) {
  return target.conditionPlatform === "browser" || target.conditionPlatform === "neutral" || target.pathPlatform === "browser" || target.pathPlatform === "neutral" || !target.conditionPlatform && !target.pathPlatform;
}
function isNodeTarget(target) {
  return target.conditionPlatform === "node" || target.pathPlatform === "node";
}
function hasNodeTarget(targets) {
  return targets.some(isNodeTarget);
}
function isNodeOnlyPackage(pkg, targets) {
  const runtimeTargets = targets.filter(isRuntimeTarget);
  const platformTargets = runtimeTargets.length > 0 ? runtimeTargets : targets;
  if (platformTargets.length === 0) return pkg.bin !== void 0;
  return platformTargets.some(isNodeTarget) && !platformTargets.some(isPortableTarget);
}
function readRootTsconfigTypes(pkgDir) {
  const configPath = path6.join(pkgDir, "tsconfig.json");
  if (!fs5.existsSync(configPath)) return [];
  const parsed = ts2.parseConfigFileTextToJson(configPath, fs5.readFileSync(configPath, "utf8"));
  const config = parsed.config;
  const types = config?.compilerOptions?.types;
  return Array.isArray(types) ? types.filter((value) => typeof value === "string") : [];
}
function collectTsconfigDiagnostics(pkgDir, pkg, targets) {
  if (targets.length === 0) return [];
  if (hasNodeTarget(targets) || isNodeOnlyPackage(pkg, targets)) return [];
  const types = readRootTsconfigTypes(pkgDir);
  return types.includes("node") ? [{
    detail: 'compilerOptions.types includes "node"',
    file: path6.join(pkgDir, "tsconfig.json"),
    kind: "tsconfig-node-types"
  }] : [];
}
function diagnosticKey2(pkgDir, diagnostic) {
  return [
    diagnostic.kind,
    diagnostic.platform ?? "",
    diagnostic.target ?? "",
    diagnostic.exportPath ?? "",
    diagnostic.file != null && diagnostic.file.length > 0 ? relativePath(pkgDir, diagnostic.file) : "",
    diagnostic.detail ?? ""
  ].join("\0");
}
function dedupeDiagnostics2(pkgDir, diagnostics) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const diagnostic of diagnostics) {
    const key = diagnosticKey2(pkgDir, diagnostic);
    if (seen.has(key)) continue;
    seen.add(key);
    result.push(diagnostic);
  }
  return result;
}
function renderDiagnostic2(pkgDir, diagnostic) {
  switch (diagnostic.kind) {
    case "condition-target-mismatch": {
      return `Publint [platform]: ${diagnostic.detail} at ${diagnostic.exportPath} (${diagnostic.target})`;
    }
    case "node-builtin-import": {
      const file = relativePath(pkgDir, diagnostic.file ?? "");
      return `Publint [platform]: ${diagnostic.platform} export ${diagnostic.target} imports Node builtin "${diagnostic.detail}" in ${file} (${diagnostic.exportPath})`;
    }
    case "node-ambient-type": {
      const file = relativePath(pkgDir, diagnostic.file ?? "");
      return `Publint [platform]: ${diagnostic.platform} hasDeclaration ${diagnostic.target} exposes Node ambient "${diagnostic.detail}" in ${file} (${diagnostic.exportPath})`;
    }
    case "tsconfig-node-types": {
      return 'Publint [platform]: non-node package tsconfig.json should not include compilerOptions.types "node"';
    }
  }
}
async function checkPlatform(pkgDir, pkg, shouldFix = false) {
  const result = pkg.private === true ? {
    errors: 0,
    modified: false,
    warnings: 0
  } : await checkExportMappings(pkgDir, pkg, shouldFix);
  const targets = collectExportTargets(pkg.exports);
  const diagnostics = [...collectConditionTargetDiagnostics(targets)];
  for (const target of targets) {
    const platform = shouldScanForNodeUsage(target);
    if (!platform) continue;
    for (const file of resolveExportTargetFiles(pkgDir, target.target)) {
      diagnostics.push(...scanFileGraph(file, platform, target));
    }
  }
  diagnostics.push(...collectTsconfigDiagnostics(pkgDir, pkg, targets));
  for (const diagnostic of dedupeDiagnostics2(pkgDir, diagnostics)) {
    console.error(chalk8.red(renderDiagnostic2(pkgDir, diagnostic)));
    result.errors++;
  }
  return result;
}

// src/actions/package/publint.ts
function normalizePublintConfig(value) {
  if (typeof value === "object") return value;
  return {};
}
function mergeWorkspaceExclude(exclude, wsPublintConfig) {
  return /* @__PURE__ */ new Set([
    ...exclude,
    ...wsPublintConfig.exclude ?? []
  ]);
}
var shouldRemoveSourceFromExports = (exports) => {
  let isRemoved = false;
  for (const [key, value] of Object.entries(exports)) {
    if (key === "source") {
      delete exports[key];
      isRemoved = true;
    } else if (typeof value === "object" && value !== null && shouldRemoveSourceFromExports(value)) isRemoved = true;
  }
  return isRemoved;
};
var hasSourceInExports = (exports) => {
  for (const [key, value] of Object.entries(exports)) {
    if (key === "source") return true;
    if (typeof value === "object" && value !== null && hasSourceInExports(value)) return true;
  }
  return false;
};
var hasImportKeyInExports = (exports) => {
  for (const [key, value] of Object.entries(exports)) {
    if (key === "import" && typeof value === "string" && value.endsWith(".mjs")) return true;
    if (typeof value === "object" && value !== null && hasImportKeyInExports(value)) return true;
  }
  return false;
};
var shouldReplaceImportWithDefault = (exports) => {
  let isModified = false;
  for (const [key, value] of Object.entries(exports)) {
    if (key === "import" && typeof value === "string" && value.endsWith(".mjs")) {
      if (exports.default === void 0) {
        exports.default = value;
      }
      delete exports.import;
      if (exports.types === void 0) {
        exports.types = value.replace(/\.mjs$/, ".d.ts");
      }
      isModified = true;
    } else if (typeof value === "object" && value !== null && shouldReplaceImportWithDefault(value)) isModified = true;
  }
  return isModified;
};
var hasTypesInExports = (exports) => {
  for (const [key, value] of Object.entries(exports)) {
    if (key === "types") return true;
    if (typeof value === "object" && value !== null && hasTypesInExports(value)) return true;
  }
  return false;
};
function ensureExportsPath3(value) {
  if (value.startsWith("./") || value.startsWith("../")) return value;
  return `./${value}`;
}
function emptyCustomResult() {
  return {
    errors: 0,
    modified: false,
    warnings: 0
  };
}
function mergeResults(target, source) {
  target.errors += source.errors;
  target.warnings += source.warnings;
  target.modified ||= source.modified;
}
function checkFiles(pkg, shouldFix) {
  const result = emptyCustomResult();
  const files = pkg.files;
  if (files === void 0) {
    console.warn(chalk9.yellow('Publint [custom]: "files" field is missing'));
    result.warnings++;
  }
  if (Array.isArray(files) && !files.includes("README.md")) {
    files.push("README.md");
    console.warn(chalk9.yellow('Publint [custom]: added "README.md" to "files"'));
    result.modified = true;
    result.warnings++;
  }
  if (Array.isArray(files) && files.includes("src")) {
    if (shouldFix) {
      pkg.files = files.filter((f) => f !== "src");
      console.warn(chalk9.yellow('Publint [custom]: removed "src" from "files"'));
      result.modified = true;
    } else {
      console.warn(chalk9.yellow('Publint [custom]: "src" should not be in "files" (use --shouldFix to remove)'));
    }
    result.warnings++;
  }
  return result;
}
function checkExportsSource(pkg, shouldFix) {
  const result = emptyCustomResult();
  const exports = pkg.exports;
  if (exports && typeof exports === "object" && hasSourceInExports(exports)) {
    if (shouldFix) {
      shouldRemoveSourceFromExports(exports);
      console.warn(chalk9.yellow('Publint [custom]: removed "source" entries from "exports"'));
      result.modified = true;
    } else {
      console.warn(chalk9.yellow('Publint [custom]: "source" entries should not be in "exports" (use --shouldFix to remove)'));
    }
    result.warnings++;
  }
  return result;
}
function migrateFieldToExports(pkg, field, exportKey, shouldFix) {
  const result = emptyCustomResult();
  if (pkg[field] === void 0) return result;
  const fieldValue = pkg[field];
  if (!fieldValue.endsWith(".js") && !fieldValue.endsWith(".mjs") && !fieldValue.endsWith(".cjs")) return result;
  if (shouldFix) {
    const exportValue = ensureExportsPath3(fieldValue);
    const exports = pkg.exports;
    if (exports && typeof exports === "object") {
      const dot = exports["."];
      if (dot != null && typeof dot === "object" && !Object.hasOwn(dot, exportKey)) {
        dot[exportKey] = exportValue;
        console.warn(chalk9.yellow(`Publint [custom]: migrated "${field}" to "exports['.'].${exportKey}" (${fieldValue})`));
      }
    } else if (pkg.exports == null) {
      pkg.exports = { ".": { [exportKey]: exportValue } };
      console.warn(chalk9.yellow(`Publint [custom]: migrated "${field}" to "exports" (.\u2192${fieldValue})`));
    }
    delete pkg[field];
    console.warn(chalk9.yellow(`Publint [custom]: removed deprecated "${field}" field`));
    result.modified = true;
  } else {
    console.warn(chalk9.yellow(`Publint [custom]: "${field}" field is deprecated, use "exports" instead (use --shouldFix to remove)`));
  }
  result.warnings++;
  return result;
}
function checkSideEffects(pkg) {
  const result = emptyCustomResult();
  if (pkg.sideEffects !== false) {
    console.warn(chalk9.yellow('Publint [custom]: "sideEffects" field should be set to false'));
    result.warnings++;
  }
  return result;
}
function checkRootSource(pkg, shouldFix) {
  const result = emptyCustomResult();
  for (const field of ["source", "src"]) {
    if (pkg[field] !== void 0) {
      if (shouldFix) {
        delete pkg[field];
        console.warn(chalk9.yellow(`Publint [custom]: removed root-level "${field}" field`));
        result.modified = true;
      } else {
        console.warn(chalk9.yellow(`Publint [custom]: root-level "${field}" field should not be in package.json (use --shouldFix to remove)`));
      }
      result.warnings++;
    }
  }
  return result;
}
function checkResolutions(pkg) {
  const result = emptyCustomResult();
  if (pkg.resolutions !== void 0) {
    console.warn(chalk9.yellow('Publint [custom]: "resolutions" in use'));
    console.warn(chalk9.gray(JSON.stringify(pkg.resolutions, null, 2)));
    result.warnings++;
  }
  return result;
}
function checkImportToDefault(pkg, shouldFix) {
  const result = emptyCustomResult();
  const exports = pkg.exports;
  if (!exports || typeof exports !== "object") return result;
  if (!hasImportKeyInExports(exports)) return result;
  if (shouldFix) {
    shouldReplaceImportWithDefault(exports);
    console.warn(chalk9.yellow('Publint [custom]: renamed "import" to "default" in "exports" and ensured "types" siblings'));
    result.modified = true;
  } else {
    console.warn(chalk9.yellow('Publint [custom]: "import" entries in "exports" should use "default" instead (use --shouldFix to rename)'));
  }
  result.warnings++;
  return result;
}
function checkRootTypes(pkg, shouldFix) {
  const result = emptyCustomResult();
  if (pkg.types === void 0) return result;
  const exports = pkg.exports;
  if (!exports || typeof exports !== "object" || !hasTypesInExports(exports)) return result;
  if (shouldFix) {
    delete pkg.types;
    console.warn(chalk9.yellow('Publint [custom]: removed redundant root "types" field (already defined in "exports")'));
    result.modified = true;
  } else {
    console.warn(chalk9.yellow('Publint [custom]: root "types" field is redundant when "exports" defines types (use --shouldFix to remove)'));
  }
  result.warnings++;
  return result;
}
async function customPubLint(pkgDir, pkg, shouldFix = false, exclude = /* @__PURE__ */ new Set()) {
  const result = emptyCustomResult();
  if (!exclude.has("files")) mergeResults(result, checkFiles(pkg, shouldFix));
  if (!exclude.has("source")) mergeResults(result, checkExportsSource(pkg, shouldFix));
  if (!exclude.has("rootSource")) mergeResults(result, checkRootSource(pkg, shouldFix));
  if (!exclude.has("main")) mergeResults(result, migrateFieldToExports(pkg, "main", "default", shouldFix));
  if (!exclude.has("types")) mergeResults(result, migrateFieldToExports(pkg, "types", "types", shouldFix));
  if (!exclude.has("module")) mergeResults(result, migrateFieldToExports(pkg, "module", "default", shouldFix));
  if (!exclude.has("importToDefault")) mergeResults(result, checkImportToDefault(pkg, shouldFix));
  if (!exclude.has("rootTypes")) mergeResults(result, checkRootTypes(pkg, shouldFix));
  if (!exclude.has("platform")) mergeResults(result, await checkPlatform(pkgDir, pkg, shouldFix));
  if (!exclude.has("sideEffects")) mergeResults(result, checkSideEffects(pkg));
  if (!exclude.has("resolutions")) mergeResults(result, checkResolutions(pkg));
  return [result.errors, result.warnings, result.modified];
}
var ALWAYS_INCLUDED_PATTERNS = [
  "package.json",
  "README",
  "README.*",
  "LICENCE",
  "LICENCE.*",
  "LICENSE",
  "LICENSE.*",
  "CHANGELOG",
  "CHANGELOG.*"
];
async function resolvePackFiles(pkgDir, filesField) {
  const patterns = [...ALWAYS_INCLUDED_PATTERNS];
  if (filesField) {
    for (const pattern of filesField) {
      if (pattern.startsWith("!")) {
        patterns.push(pattern);
      } else {
        patterns.push(pattern, `${pattern}/**`);
      }
    }
  }
  const matched = await glob2(patterns, {
    cwd: pkgDir,
    nodir: true,
    dot: false
  });
  const files = await Promise.all(
    matched.map(async (rel) => {
      const abs = path7.join(pkgDir, rel);
      let data = "";
      try {
        data = await fs6.readFile(abs, "utf8");
      } catch {
      }
      return { name: path7.join(pkgDir, rel), data };
    })
  );
  return files;
}
function logPublintMessage(message, pkg, formatMessage) {
  const formatted = `[${message.code}] ${formatMessage(message, pkg) ?? message.code}`;
  switch (message.type) {
    case "error": {
      console.error(chalk9.red(formatted));
      return;
    }
    case "warning": {
      console.warn(chalk9.yellow(formatted));
      return;
    }
    default: {
      console.log(chalk9.white(formatted));
    }
  }
}
async function runPublintLibrary(pkgDir, pkg, isStrict, shouldPack) {
  const { publint: publint2 } = await import("publint");
  let packOption = false;
  if (shouldPack) {
    const files = await resolvePackFiles(pkgDir, pkg.files);
    packOption = { files };
  }
  const { messages } = await publint2({
    level: "suggestion",
    pack: packOption,
    pkgDir,
    strict: isStrict
  });
  const { formatMessage } = await import("publint/utils");
  for (const message of messages) {
    logPublintMessage(message, pkg, formatMessage);
  }
  return {
    errors: messages.filter((message) => message.type === "error").length,
    warnings: messages.filter((message) => message.type !== "error").length
  };
}
var packagePublintDetailed = async ({
  exclude = /* @__PURE__ */ new Set(),
  shouldFix = false,
  shouldPack: shouldPackParam,
  pkgDir: pkgDirParam,
  isStrict = isStrictMode(),
  verbose: _verbose = false
} = {}) => {
  const pkgDir = path7.resolve(pkgDirParam ?? INIT_CWD());
  const wsPublintConfig = normalizePublintConfig(
    await loadWorkspaceCommandConfig(pkgDir, "publint")
  );
  const workspaceExclude = mergeWorkspaceExclude(exclude, wsPublintConfig);
  const shouldPack = shouldPackParam ?? wsPublintConfig.pack ?? true;
  const sortedPkg = sortPackageJson(await fs6.readFile(`${pkgDir}/package.json`, "utf8"));
  await fs6.writeFile(`${pkgDir}/package.json`, sortedPkg);
  const pkg = JSON.parse(await fs6.readFile(`${pkgDir}/package.json`, "utf8"));
  const effectiveExclude = Boolean(pkg.private) ? /* @__PURE__ */ new Set([...workspaceExclude, ...PUBLISH_ONLY_CHECKS]) : workspaceExclude;
  console.log(chalk9.green(`Publint: ${String(pkg.name)}${Boolean(pkg.private) ? chalk9.gray(" (private)") : ""}`));
  console.log(chalk9.gray(pkgDir));
  let libraryErrors = 0;
  let libraryWarnings = 0;
  if (!effectiveExclude.has("publint")) {
    const library = await runPublintLibrary(pkgDir, pkg, isStrict, shouldPack);
    libraryErrors = library.errors;
    libraryWarnings = library.warnings;
  }
  const [errorCount, warningCount, isModified] = await customPubLint(pkgDir, pkg, shouldFix, effectiveExclude);
  if (isModified) {
    const sorted = sortPackageJson(JSON.stringify(pkg, null, 2));
    await fs6.writeFile(`${pkgDir}/package.json`, sorted);
  }
  const compileTargets = effectiveExclude.has("compileTargets") ? { errors: 0, warnings: 0 } : checkCompileTargets(pkgDir, shouldFix);
  return {
    errors: libraryErrors + errorCount + compileTargets.errors,
    warnings: libraryWarnings + warningCount + compileTargets.warnings
  };
};

// src/actions/package-lint-deps.ts
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
import PATH2 from "node:path";
import chalk10 from "chalk";
import semver from "semver";
function readWorkspacePackageJson(cwd, location) {
  const pkgPath = PATH2.resolve(cwd, location, "package.json");
  try {
    return JSON.parse(readFileSync4(pkgPath, "utf8"));
  } catch {
    return void 0;
  }
}
function writeWorkspacePackageJson(cwd, location, pkg) {
  const pkgPath = PATH2.resolve(cwd, location, "package.json");
  writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
`, "utf8");
}
function buildWorkspaceVersionMap(cwd, workspaces) {
  const map = /* @__PURE__ */ new Map();
  for (const { location, name } of workspaces) {
    if (location === ".") continue;
    const pkg = readWorkspacePackageJson(cwd, location);
    if (!pkg) continue;
    const version = pkg.version;
    if (version != null && version.length > 0) map.set(name, version);
  }
  return map;
}
function internalPeerEntries(peerDeps, workspaceVersions, shouldRewriteWorkspaceProtocol) {
  return Object.entries(peerDeps).flatMap(([dep, version]) => {
    const targetVersion = workspaceVersions.get(dep);
    if (!(targetVersion != null && targetVersion.length > 0)) return [];
    if (shouldRewriteWorkspaceProtocol && version.startsWith("workspace:")) return [];
    return [[dep, version, targetVersion]];
  });
}
function internalPeerVersionIssue(name, location, dep, version, targetVersion, devDepVersion, shouldRewriteWorkspaceProtocol) {
  if (version.startsWith("workspace:")) {
    if (shouldRewriteWorkspaceProtocol) return void 0;
    const expected2 = expectedPeerRange(devDepVersion, targetVersion);
    return `${name} (${location}) peerDependencies.${dep} uses workspace: protocol \u2014 should be "${expected2}"`;
  }
  const expected = expectedPeerRange(devDepVersion, targetVersion);
  if (version !== expected && !semver.satisfies(targetVersion, version)) {
    return `${name} (${location}) peerDependencies.${dep} is "${version}" \u2014 current version ${targetVersion} is not satisfied; expected "${expected}"`;
  }
  return void 0;
}
function expectedPeerRange(devDepVersion, targetVersion) {
  const parsed = semver.parse(targetVersion);
  if (!parsed) return `~${targetVersion}`;
  if (devDepVersion === "workspace:^") {
    return `^${parsed.major}`;
  }
  return `~${parsed.major}.${parsed.minor}`;
}
function checkInternalPeerVersions(cwd, workspaces) {
  const result = {
    errors: [],
    fixable: [],
    warnings: []
  };
  const workspaceVersions = buildWorkspaceVersionMap(cwd, workspaces);
  const pmName = getPackageManager().name;
  const isRewritesWorkspaceProtocol = pmName === "pnpm" || pmName === "bun";
  for (const { location, name } of workspaces) {
    const pkg = readWorkspacePackageJson(cwd, location);
    if (!pkg) continue;
    const peerDeps = pkg.peerDependencies;
    if (!peerDeps) continue;
    const devDeps = pkg.devDependencies;
    for (const [dep, version, targetVersion] of internalPeerEntries(peerDeps, workspaceVersions, isRewritesWorkspaceProtocol)) {
      const issue = internalPeerVersionIssue(name, location, dep, version, targetVersion, devDeps?.[dep], isRewritesWorkspaceProtocol);
      if (issue != null && issue.length > 0) result.fixable.push(issue);
    }
  }
  return result;
}
function fixInternalPeerVersions(cwd, workspaces) {
  const workspaceVersions = buildWorkspaceVersionMap(cwd, workspaces);
  const pmName = getPackageManager().name;
  const isRewritesWorkspaceProtocol = pmName === "pnpm" || pmName === "bun";
  for (const { location, name } of workspaces) {
    const pkg = readWorkspacePackageJson(cwd, location);
    if (!pkg) continue;
    const peerDeps = pkg.peerDependencies;
    if (!peerDeps) continue;
    const devDeps = pkg.devDependencies;
    let isModified = false;
    for (const [dep, version, targetVersion] of internalPeerEntries(peerDeps, workspaceVersions, isRewritesWorkspaceProtocol)) {
      const expected = expectedPeerRange(devDeps?.[dep], targetVersion);
      if (version !== expected) {
        peerDeps[dep] = expected;
        console.log(chalk10.green(`  \u2714 Fixed: set peerDependencies.${dep} to "${expected}" in ${name} (${location})`));
        isModified = true;
      }
    }
    if (isModified) {
      writeWorkspacePackageJson(cwd, location, pkg);
    }
  }
}

// src/actions/result.ts
function actionResult({
  errorCount,
  exitCode = errorCount,
  warningCount
}) {
  if (errorCount === 0 && warningCount === 0 && exitCode === 0) return 0;
  return {
    envelope: {
      errorCount,
      warningCount
    },
    exitCode
  };
}
function resultFromExitCode(exitCode) {
  return actionResult({
    errorCount: exitCode === 0 ? 0 : Math.max(1, exitCode),
    exitCode,
    warningCount: 0
  });
}
function resultExitCode(result) {
  return typeof result === "number" ? result : result.exitCode;
}
function resultErrorCount(result) {
  if (typeof result === "number") return result === 0 ? 0 : Math.max(1, result);
  return result.envelope?.errorCount ?? (result.exitCode === 0 ? 0 : Math.max(1, result.exitCode));
}
function resultWarningCount(result) {
  return typeof result === "number" ? 0 : result.envelope?.warningCount ?? 0;
}
function isCleanResult(result) {
  return resultExitCode(result) === 0 && resultErrorCount(result) === 0 && resultWarningCount(result) === 0;
}

// src/actions/publint.ts
function selectedWorkspaces(workspaces, names) {
  const selected = new Set(names);
  return workspaces.filter((workspace) => selected.has(workspace.name));
}
function shouldUseIncremental(incremental) {
  return incremental !== false;
}
function sortedOptions(value) {
  return [...value ?? []].toSorted((a, b) => a.localeCompare(b));
}
function publintOptionsHash({
  cliExclude,
  cliInclude,
  fix,
  pack
}) {
  return {
    cliExclude: sortedOptions(cliExclude),
    cliInclude: sortedOptions(cliInclude),
    fix: fix ?? false,
    pack,
    strict: isStrictMode()
  };
}
function resolveExclude(publintConfig, cliExclude, cliInclude) {
  const hasExclude = (publintConfig.exclude?.length ?? 0) > 0 || (cliExclude?.length ?? 0) > 0;
  const hasInclude = (publintConfig.include?.length ?? 0) > 0 || (cliInclude?.length ?? 0) > 0;
  if (hasExclude && hasInclude) {
    console.error(chalk11.red("Publint: --include and --exclude cannot be used together"));
    return void 0;
  }
  if (hasInclude) {
    const include = /* @__PURE__ */ new Set([
      ...publintConfig.include ?? [],
      ...cliInclude ?? []
    ]);
    return new Set(ALL_PUBLINT_CHECKS.filter((c) => !include.has(c)));
  }
  return /* @__PURE__ */ new Set([
    ...publintConfig.exclude ?? [],
    ...cliExclude ?? []
  ]);
}
function normalizePublintConfig2(value) {
  if (typeof value === "object") return value;
  return {};
}
async function loadPublintOptions() {
  const config = await loadConfig();
  const publintConfig = normalizePublintConfig2(config.commands?.publint ?? config.publint);
  const exclude = resolveExclude(publintConfig) ?? /* @__PURE__ */ new Set();
  return { exclude, pack: publintConfig.pack ?? true };
}
var publint = async ({
  cliExclude,
  cliInclude,
  fix,
  incremental,
  jobs,
  pack,
  verbose,
  pkg,
  workspaces
}) => {
  if (pkg === void 0 && shouldUseIncremental(incremental)) {
    return await publintIncremental({
      cliExclude,
      cliInclude,
      fix,
      jobs,
      pack,
      verbose,
      workspaces
    });
  }
  return pkg === void 0 ? await publintAll({
    cliExclude,
    cliInclude,
    fix,
    jobs,
    pack,
    verbose,
    workspaces
  }) : await publintSingle({
    cliExclude,
    cliInclude,
    fix,
    pack,
    pkg,
    verbose
  });
};
function logPublintSummary(packages, errors, warnings, ms) {
  const summary = `Checked ${packages} package(s) in ${ms.toFixed(0)}ms with ${errors} error(s) and ${warnings} warning(s) found.`;
  if (errors > 0) {
    console.log(chalk11.red(summary));
  } else {
    console.log(chalk11.blue(summary));
  }
}
var publintSingle = async ({
  cliExclude,
  cliInclude,
  fix,
  pack,
  pkg,
  verbose
}) => {
  const start = performance.now();
  const pm = getPackageManager();
  const workspace = pm.findWorkspace(pkg);
  if (!workspace) {
    console.error(chalk11.red(`Publint: workspace "${pkg}" not found`));
    return resultFromExitCode(1);
  }
  const wsPublintConfig = normalizePublintConfig2(
    await loadWorkspaceCommandConfig(workspace.location, "publint")
  );
  const exclude = resolveExclude(wsPublintConfig, cliExclude, cliInclude);
  if (!exclude) return resultFromExitCode(1);
  const shouldPack = pack ?? wsPublintConfig.pack ?? true;
  const result = await packagePublintDetailed({
    exclude,
    shouldFix: fix,
    shouldPack,
    pkgDir: workspace.location,
    verbose
  });
  logPublintSummary(1, result.errors, result.warnings, performance.now() - start);
  return actionResult({
    errorCount: result.errors,
    exitCode: result.errors + (isStrictMode() ? result.warnings : 0),
    warningCount: result.warnings
  });
};
async function publintIncremental({
  cliExclude,
  cliInclude,
  fix,
  jobs,
  pack,
  verbose,
  workspaces: targetWorkspaces
}) {
  const pm = getPackageManager();
  const allWorkspaces = targetWorkspaces ?? pm.listWorkspaces();
  const options = publintOptionsHash({
    cliExclude,
    cliInclude,
    fix,
    pack
  });
  const plan = createIncrementalPlan({
    action: "publint",
    options,
    pm,
    isVerbose: verbose,
    workspaces: allWorkspaces
  });
  if (plan.full && (plan.reason != null && plan.reason.length > 0)) {
    console.log(chalk11.gray(`  Full publint: ${plan.reason}`));
  }
  if (!plan.full && plan.affectedWorkspaceNames.length === 0) {
    console.log(chalk11.gray("  No changed packages to publint."));
    writeIncrementalSnapshot({
      action: "publint",
      options,
      pm,
      workspaces: allWorkspaces
    });
    return 0;
  }
  const workspaces = plan.full ? allWorkspaces : selectedWorkspaces(allWorkspaces, plan.affectedWorkspaceNames);
  const result = await publintAll({
    cliExclude,
    cliInclude,
    fix,
    jobs,
    pack,
    verbose,
    workspaces
  });
  if (isCleanResult(result)) {
    writeIncrementalSnapshot({
      action: "publint",
      options,
      pm,
      workspaces: allWorkspaces
    });
  }
  return result;
}
var publintAll = async ({
  cliExclude,
  cliInclude,
  fix,
  jobs,
  pack,
  verbose,
  workspaces: targetWorkspaces
}) => {
  const start = performance.now();
  const pm = getPackageManager();
  const workspaces = targetWorkspaces ?? pm.listWorkspaces();
  const concurrency = jobs;
  const results = Array.from({ length: workspaces.length }, () => ({
    errors: 0,
    output: [],
    warnings: 0
  }));
  installOutputCapture();
  await runWithConcurrency(
    workspaces.map((ws, i) => ({ i, ws })),
    concurrency,
    async ({ i, ws }) => {
      const output = [];
      await outputStorage.run(output, async () => {
        try {
          const wsPublintConfig = normalizePublintConfig2(
            await loadWorkspaceCommandConfig(ws.location, "publint")
          );
          const exclude = resolveExclude(wsPublintConfig, cliExclude, cliInclude) ?? /* @__PURE__ */ new Set();
          const shouldPack = pack ?? wsPublintConfig.pack ?? true;
          const result = await packagePublintDetailed({
            exclude,
            shouldFix: fix,
            shouldPack,
            pkgDir: ws.location,
            verbose
          });
          results[i] = {
            errors: result.errors,
            output,
            warnings: result.warnings
          };
        } catch (ex) {
          output.push(chalk11.red(`Publint failed for ${ws.name}: ${ex.message}
`));
          results[i] = {
            errors: 1,
            output,
            warnings: 0
          };
        }
      });
    }
  );
  let totalErrors = 0;
  let totalWarnings = 0;
  for (const {
    errors,
    output,
    warnings
  } of results) {
    for (const line of output) {
      process.stdout.write(line);
    }
    totalErrors += errors;
    totalWarnings += warnings;
  }
  const allExclude = resolveExclude({}, cliExclude, cliInclude) ?? /* @__PURE__ */ new Set();
  if (!allExclude.has("peerDeps")) {
    const cwd = INIT_CWD();
    const peerResult = checkInternalPeerVersions(cwd, workspaces);
    if (peerResult.fixable.length > 0) {
      if (fix ?? false) {
        fixInternalPeerVersions(cwd, workspaces);
        shouldRunInstall();
      } else {
        const isStrict = isStrictMode();
        for (const msg of peerResult.fixable) {
          console.log(isStrict ? chalk11.red(`  \u2717 ${msg} (fixable)`) : chalk11.yellow(`  \u26A0 ${msg} (fixable)`));
        }
        totalWarnings += peerResult.fixable.length;
      }
    }
    totalErrors += peerResult.errors.length;
  }
  logPublintSummary(workspaces.length, totalErrors, totalWarnings, performance.now() - start);
  return actionResult({
    errorCount: totalErrors,
    exitCode: totalErrors + (isStrictMode() ? totalWarnings : 0),
    warningCount: totalWarnings
  });
};
export {
  loadPublintOptions,
  publint,
  publintAll,
  publintSingle
};
//# sourceMappingURL=publint.mjs.map
