#!/usr/bin/env node
import {
  FRAMEWORK_PNPM_OVERRIDES
} from "./chunk-ZEELHSY3.mjs";
import {
  DEFAULT_WEB_BASE_URL
} from "./chunk-H76MT5UR.mjs";

// src/services/project/workspace-dependencies.ts
import crypto from "crypto";
import { spawn } from "child_process";
import "events";
import { existsSync } from "fs";
import { mkdir, lstat, readFile, rm, writeFile } from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";

// src/services/project/dependency-tooling-messages.ts
var DEPENDENCY_SETUP_DOCS_PATH = "/docs/reference/dependency-setup";
var DEPENDENCY_SETUP_DOCS_URL = `${DEFAULT_WEB_BASE_URL}${DEPENDENCY_SETUP_DOCS_PATH}`;
function buildMissingDependencyToolingMessage() {
  return [
    "Dreamboard needs dependency tooling to finish `dreamboard sync`.",
    "Use Node 24+ with Corepack enabled, then run `dreamboard sync` again.",
    "If Corepack is unavailable on this machine, install pnpm globally with `npm install -g pnpm`.",
    `Help: ${DEPENDENCY_SETUP_DOCS_URL}`
  ].join("\n");
}
function buildPackageLockConflictMessage() {
  return [
    "Dreamboard manages workspace dependencies during `dreamboard sync`.",
    "This workspace has an npm lockfile that conflicts with Dreamboard-managed dependencies.",
    "Remove `package-lock.json` and run `dreamboard sync` again.",
    `Help: ${DEPENDENCY_SETUP_DOCS_URL}`
  ].join("\n");
}
function buildMissingGeneratedLockfileMessage() {
  return [
    "Dreamboard could not finish preparing workspace dependencies during `dreamboard sync`.",
    "Diagnostic: `pnpm-lock.yaml` was not created.",
    `Help: ${DEPENDENCY_SETUP_DOCS_URL}`
  ].join("\n");
}
function buildDependencyPreparationFailureMessage(options) {
  const details = options.output?.trim();
  return [
    `Dreamboard could not finish preparing workspace dependencies during \`dreamboard sync\`${options.exitCode != null ? ` (exit code ${options.exitCode})` : ""}.`,
    details ? `Diagnostic output:
${details}` : null,
    `Help: ${DEPENDENCY_SETUP_DOCS_URL}`
  ].filter(Boolean).join("\n");
}

// src/services/project/workspace-dependencies.ts
var MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
var CLI_ROOT = path.resolve(MODULE_DIR, "../../..");
var REPO_ROOT = path.resolve(CLI_ROOT, "../..");
var REPO_PACKAGE_JSON_PATH = path.join(REPO_ROOT, "package.json");
var DEFAULT_PACKAGE_MANAGER = "pnpm@10.4.1";
var SDK_PACKAGE_NAME = "@dreamboard-games/sdk";
var SDK_REQUIRED_DIST_FILES = [
  "dist/index.d.ts",
  "dist/index.js",
  "dist/types.d.ts",
  "dist/types.js",
  "dist/ui.d.ts",
  "dist/ui.js",
  "dist/runtime.d.ts",
  "dist/runtime.js"
];
var DEPENDENCY_INSTALL_METADATA_PATH_SEGMENTS = [
  ".dreamboard",
  "dependency-install.json"
];
var LockfileGenerationError = class extends Error {
  constructor(binary, details) {
    super(buildLockfileGenerationErrorMessage(binary, details), {
      cause: details.cause
    });
    this.binary = binary;
    this.details = details;
    this.name = "LockfileGenerationError";
  }
  binary;
  details;
};
async function generatePnpmLockfile(projectRoot, _options = {}) {
  await ensurePackageManagerNormalized(projectRoot);
  await assertNoNpmLockfileConflict(projectRoot);
  await runPackageManagerCommand(projectRoot, {
    args: [
      "install",
      "--ignore-workspace",
      "--lockfile-only",
      "--config.shared-workspace-lockfile=false"
    ]
  });
  await assertPnpmLockfilePresent(projectRoot);
  return true;
}
async function installWorkspaceDependencies(projectRoot, _options = {}) {
  const result = await reconcileWorkspaceDependencies(projectRoot);
  return result.required;
}
async function reconcileWorkspaceDependencies(projectRoot) {
  const packageJsonPath = path.join(projectRoot, "package.json");
  if (!await pathExists(packageJsonPath)) {
    return {
      required: false,
      installed: false,
      lockfileGenerated: false,
      packageManagerNormalized: false,
      fingerprint: null
    };
  }
  await assertNoNpmLockfileConflict(projectRoot);
  const packageManagerNormalized = await ensurePackageManagerNormalized(projectRoot);
  const pnpmLockfilePath = path.join(projectRoot, "pnpm-lock.yaml");
  const nodeModulesPath = path.join(projectRoot, "node_modules");
  const metadataPath = path.join(
    projectRoot,
    ...DEPENDENCY_INSTALL_METADATA_PATH_SEGMENTS
  );
  let lockfileGenerated = false;
  let installed = false;
  const metadata = await readJsonFile(metadataPath);
  const lockfileExists = await pathExists(pnpmLockfilePath);
  if (!lockfileExists) {
    await runPackageManagerCommand(projectRoot, {
      args: [
        "install",
        "--ignore-workspace",
        "--config.shared-workspace-lockfile=false"
      ]
    });
    lockfileGenerated = true;
    installed = true;
  }
  await assertPnpmLockfilePresent(projectRoot);
  let fingerprint = await fingerprintInstallManifest({
    packageJsonPath,
    lockfilePath: pnpmLockfilePath
  });
  const hasValidInstalledDeps = await pathExists(nodeModulesPath) && await hasValidInstalledDependencies({
    packageJsonPath,
    nodeModulesPath
  });
  const shouldInstall = !installed && (metadata?.dependencyFingerprint !== fingerprint || !hasValidInstalledDeps);
  if (shouldInstall) {
    await rm(nodeModulesPath, { recursive: true, force: true });
    await runPackageManagerCommand(projectRoot, {
      args: [
        "install",
        "--ignore-workspace",
        "--config.shared-workspace-lockfile=false"
      ]
    });
    installed = true;
    fingerprint = await fingerprintInstallManifest({
      packageJsonPath,
      lockfilePath: pnpmLockfilePath
    });
  }
  if (installed || packageManagerNormalized || !metadata) {
    await mkdir(path.dirname(metadataPath), { recursive: true });
    await writeJsonFile(metadataPath, {
      dependencyFingerprint: fingerprint,
      installedAt: (/* @__PURE__ */ new Date()).toISOString(),
      packageManager: await readRepoPackageManager()
    });
  }
  return {
    required: true,
    installed,
    lockfileGenerated,
    packageManagerNormalized,
    fingerprint
  };
}
async function assertNoNpmLockfileConflict(projectRoot) {
  const packageLockPath = path.join(projectRoot, "package-lock.json");
  const pnpmLockPath = path.join(projectRoot, "pnpm-lock.yaml");
  if (await pathExists(packageLockPath) && !await pathExists(pnpmLockPath)) {
    throw new Error(buildPackageLockConflictMessage());
  }
}
async function assertPnpmLockfilePresent(projectRoot) {
  const pnpmLockfilePath = path.join(projectRoot, "pnpm-lock.yaml");
  if (!await pathExists(pnpmLockfilePath)) {
    throw new Error(buildMissingGeneratedLockfileMessage());
  }
}
async function ensurePackageManagerNormalized(projectRoot) {
  const packageJsonPath = path.join(projectRoot, "package.json");
  const packageJsonContent = await readFile(packageJsonPath, "utf8");
  const packageJson = JSON.parse(packageJsonContent);
  const packageManager = await readRepoPackageManager();
  const nextPackageJson = {
    ...packageJson,
    packageManager,
    pnpm: mergePnpmConfig(packageJson.pnpm)
  };
  const normalizedPackageJson = `${JSON.stringify(nextPackageJson, null, 2)}
`;
  if (normalizedPackageJson === packageJsonContent) {
    return false;
  }
  await writeFile(packageJsonPath, normalizedPackageJson, "utf8");
  return true;
}
function mergePnpmConfig(existingPnpm) {
  const existingOverrides = existingPnpm?.overrides && typeof existingPnpm.overrides === "object" && !Array.isArray(existingPnpm.overrides) ? existingPnpm.overrides : {};
  return {
    ...existingPnpm ?? {},
    overrides: {
      ...existingOverrides,
      ...FRAMEWORK_PNPM_OVERRIDES
    }
  };
}
async function fingerprintInstallManifest(options) {
  const packageJson = await readFile(options.packageJsonPath, "utf8");
  const lockfile = await readFile(options.lockfilePath, "utf8");
  const packageManager = await readRepoPackageManager();
  return fingerprintContent([
    packageJson,
    lockfile,
    `packageManager:${packageManager}`
  ]);
}
async function hasValidInstalledDependencies(options) {
  const packageJson = await readJsonFile(
    options.packageJsonPath
  );
  if (!packageJson) {
    return false;
  }
  const directDependencyNames = /* @__PURE__ */ new Set();
  for (const field of [
    "dependencies",
    "devDependencies",
    "optionalDependencies"
  ]) {
    for (const packageName of Object.keys(packageJson[field] ?? {})) {
      directDependencyNames.add(packageName);
    }
  }
  if (directDependencyNames.size === 0) {
    return true;
  }
  for (const packageName of directDependencyNames) {
    const packageRoot = path.join(
      options.nodeModulesPath,
      ...packageName.split("/")
    );
    if (!await pathExists(path.join(packageRoot, "package.json"))) {
      return false;
    }
    if (packageName === SDK_PACKAGE_NAME && !await hasInstalledSdkDistFiles(packageRoot)) {
      return false;
    }
  }
  return true;
}
async function hasInstalledSdkDistFiles(packageRoot) {
  for (const relativePath of SDK_REQUIRED_DIST_FILES) {
    if (!await pathExists(path.join(packageRoot, relativePath))) {
      return false;
    }
  }
  return true;
}
function hasExactPnpmVersion(value) {
  return /^pnpm@\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(value ?? "");
}
async function readRepoPackageManager() {
  const packageJson = await readJsonFile(
    REPO_PACKAGE_JSON_PATH
  );
  const packageManager = packageJson?.packageManager?.trim();
  return hasExactPnpmVersion(packageManager) ? packageManager : DEFAULT_PACKAGE_MANAGER;
}
function fingerprintContent(parts) {
  return crypto.createHash("sha256").update(parts.join("\n---\n")).digest("hex");
}
function resolvePnpmInstallInvocation(installArgs) {
  const corepackPath = path.join(path.dirname(process.execPath), "corepack");
  if (existsSync(corepackPath)) {
    return { command: corepackPath, args: ["pnpm", ...installArgs] };
  }
  return { command: "pnpm", args: [...installArgs] };
}
async function runPackageManagerCommand(projectRoot, command) {
  const invocation = resolvePnpmInstallInvocation(command.args);
  await runLockfileCommand(projectRoot, {
    binary: invocation.command,
    args: invocation.args
  });
}
async function runLockfileCommand(projectRoot, command) {
  await new Promise((resolve, reject) => {
    const child = spawn(command.binary, command.args, {
      cwd: projectRoot,
      env: process.env,
      stdio: ["ignore", "pipe", "pipe"]
    });
    let stdout = "";
    let stderr = "";
    bindStream(child.stdout, (chunk) => {
      stdout += chunk.toString();
    });
    bindStream(child.stderr, (chunk) => {
      stderr += chunk.toString();
    });
    child.on("error", (cause) => {
      reject(
        new LockfileGenerationError(command.binary, {
          stdout,
          stderr,
          cause
        })
      );
    });
    child.on("close", (code) => {
      if (code === 0) {
        resolve();
        return;
      }
      reject(
        new LockfileGenerationError(command.binary, {
          code,
          stdout,
          stderr
        })
      );
    });
  });
}
function buildLockfileGenerationErrorMessage(binary, details) {
  if (details.cause) {
    const errnoError = details.cause;
    const binaryName = path.basename(binary).toLowerCase();
    if (errnoError.code === "ENOENT" && (binaryName === "pnpm" || binaryName === "pnpm.cmd" || binaryName === "corepack" || binaryName === "corepack.exe")) {
      return buildMissingDependencyToolingMessage();
    }
    return `Failed to start ${binary} for Dreamboard dependency reconciliation. ${details.cause.message}`;
  }
  const output = [details.stdout.trim(), details.stderr.trim()].filter((chunk) => chunk.length > 0).join("\n");
  return buildDependencyPreparationFailureMessage({
    exitCode: details.code,
    output
  });
}
async function pathExists(targetPath) {
  try {
    await lstat(targetPath);
    return true;
  } catch {
    return false;
  }
}
async function readJsonFile(filePath) {
  try {
    return JSON.parse(await readFile(filePath, "utf8"));
  } catch {
    return null;
  }
}
async function writeJsonFile(filePath, value) {
  await writeFile(filePath, `${JSON.stringify(value, null, 2)}
`, "utf8");
}
function bindStream(stream, onData) {
  stream?.on("data", onData);
}

export {
  generatePnpmLockfile,
  installWorkspaceDependencies,
  reconcileWorkspaceDependencies
};
//# sourceMappingURL=chunk-XQXDOBYB.mjs.map