import { getBundlerOptions } from './chunk-FXJXKXAV.js';
import { analyzeBundle, getWorkspaceInformation } from './chunk-ZUJ4DO5D.js';
import { createBundler, getInputOptions } from './chunk-YHEVEEDG.js';
import { getPackageRootPath } from './chunk-KAY3R5QX.js';
import { DepsService, FileService } from './chunk-I7JM5EHU.js';
import { slash, isBareModuleSpecifier } from './chunk-4SSB2KSK.js';
import { execSync } from 'child_process';
import { existsSync } from 'fs';
import { writeFile, rm, stat } from 'fs/promises';
import { join, dirname, posix } from 'path';
import { MastraBundler } from '@mastra/core/bundler';
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
import virtual from '@rollup/plugin-virtual';
import * as pkg from 'empathic/package';
import fsExtra, { emptyDir, ensureDir, copy, readJSON } from 'fs-extra/esm';
import { glob } from 'tinyglobby';

var IS_DEFAULT = /* @__PURE__ */ Symbol("IS_DEFAULT");
var Bundler = class extends MastraBundler {
  analyzeOutputDir = ".build";
  outputDir = "output";
  platform = "node";
  constructor(name, component = "BUNDLER") {
    super({ name, component });
  }
  async prepare(outputDirectory) {
    await emptyDir(outputDirectory);
    await ensureDir(join(outputDirectory, this.analyzeOutputDir));
    await ensureDir(join(outputDirectory, this.outputDir));
  }
  async writePackageJson(outputDirectory, dependencies, resolutions) {
    this.logger.debug("Writing project's package.json");
    await ensureDir(outputDirectory);
    const pkgPath = join(outputDirectory, "package.json");
    const dependenciesMap = /* @__PURE__ */ new Map();
    for (const [key, value] of dependencies.entries()) {
      if (key.startsWith("@")) {
        const pkgChunks = key.split("/");
        dependenciesMap.set(`${pkgChunks[0]}/${pkgChunks[1]}`, value);
      } else {
        const pkgName = key.split("/")[0] || key;
        dependenciesMap.set(pkgName, value);
      }
    }
    await writeFile(
      pkgPath,
      JSON.stringify(
        {
          name: "server",
          version: "1.0.0",
          description: "",
          type: "module",
          main: "index.mjs",
          scripts: {
            start: "node ./index.mjs"
          },
          author: "Mastra",
          license: "ISC",
          dependencies: Object.fromEntries(dependenciesMap.entries()),
          ...Object.keys(resolutions ?? {}).length > 0 && { resolutions },
          pnpm: {
            neverBuiltDependencies: []
          }
        },
        null,
        2
      )
    );
  }
  createBundler(inputOptions, outputOptions) {
    return createBundler(inputOptions, outputOptions);
  }
  async getUserBundlerOptions(mastraEntryFile, outputDirectory) {
    const defaultBundlerOptions = {
      externals: [],
      sourcemap: false,
      transpilePackages: [],
      [IS_DEFAULT]: true
    };
    try {
      const bundlerOptions = await getBundlerOptions(mastraEntryFile, outputDirectory);
      return bundlerOptions ?? defaultBundlerOptions;
    } catch (error) {
      this.logger.debug("Failed to get bundler options, sourcemap will be disabled", { error });
    }
    return defaultBundlerOptions;
  }
  async analyze(entry, mastraFile, outputDirectory) {
    return await analyzeBundle(
      [].concat(entry),
      mastraFile,
      {
        outputDir: join(outputDirectory, this.analyzeOutputDir),
        projectRoot: outputDirectory,
        platform: this.platform
      },
      this.logger
    );
  }
  async installDependencies(outputDirectory, rootDir = process.cwd()) {
    const deps = new DepsService(rootDir);
    deps.__setLogger(this.logger);
    await deps.install({ dir: join(outputDirectory, this.outputDir) });
  }
  /**
   * Generate a package-lock.json for the output directory so that deploy targets
   * can use `npm ci` instead of `npm install`, skipping version resolution entirely.
   * This is a lockfile-only operation — no packages are downloaded.
   *
   * Temporarily moves node_modules out of the way because pnpm's symlink-based
   * layout confuses npm's arborist, then restores it afterwards so that
   * `mastra start` (or wrangler) can still resolve dependencies at runtime.
   */
  async generateNpmLockfile(outputDir) {
    const nodeModules = join(outputDir, "node_modules");
    const nodeModulesTmp = join(outputDir, "node_modules.__tmp");
    let movedNodeModules = false;
    try {
      if (await fsExtra.pathExists(nodeModules)) {
        await fsExtra.move(nodeModules, nodeModulesTmp, { overwrite: true });
        movedNodeModules = true;
      }
      execSync("npm install --package-lock-only --force", {
        cwd: outputDir,
        stdio: "pipe",
        timeout: 6e4
      });
    } catch {
      this.logger.warn("Failed to generate package-lock.json \u2014 deploy will fall back to npm install");
    } finally {
      if (movedNodeModules) {
        await rm(nodeModules, { recursive: true, force: true });
        await fsExtra.move(nodeModulesTmp, nodeModules, { overwrite: true });
      }
    }
  }
  async copyPublic(mastraDir, outputDirectory) {
    const publicDir = join(mastraDir, "public");
    try {
      await stat(publicDir);
    } catch {
      return;
    }
    await copy(publicDir, join(outputDirectory, this.outputDir));
  }
  async copyDOTNPMRC({
    rootDir = process.cwd(),
    outputDirectory
  }) {
    const sourceDotNpmRcPath = join(rootDir, ".npmrc");
    const targetDotNpmRcPath = join(outputDirectory, this.outputDir, ".npmrc");
    try {
      await stat(sourceDotNpmRcPath);
      await copy(sourceDotNpmRcPath, targetDotNpmRcPath);
    } catch {
      return;
    }
  }
  async getBundlerOptions(serverFile, mastraEntryFile, analyzedBundleInfo, toolsPaths, { enableSourcemap, enableEsmShim, externals }) {
    const { workspaceRoot } = await getWorkspaceInformation({ mastraEntryFile });
    const closestPkgJson = pkg.up({ cwd: dirname(mastraEntryFile) });
    const projectRoot = closestPkgJson ? dirname(closestPkgJson) : process.cwd();
    const inputOptions = await getInputOptions(
      mastraEntryFile,
      analyzedBundleInfo,
      this.platform,
      {
        "process.env.NODE_ENV": JSON.stringify("production")
      },
      { sourcemap: enableSourcemap, workspaceRoot, projectRoot, enableEsmShim, externalsPreset: externals === true }
    );
    const isVirtual = serverFile.includes("\n") || !existsSync(serverFile);
    const toolsInputOptions = await this.listToolsInputOptions(toolsPaths);
    if (isVirtual) {
      inputOptions.input = { index: "#entry", ...toolsInputOptions };
      if (Array.isArray(inputOptions.plugins)) {
        inputOptions.plugins.unshift(virtual({ "#entry": serverFile }));
      } else {
        inputOptions.plugins = [virtual({ "#entry": serverFile })];
      }
    } else {
      inputOptions.input = { index: serverFile, ...toolsInputOptions };
    }
    return inputOptions;
  }
  getAllToolPaths(mastraDir, toolsPaths = []) {
    const normalizedMastraDir = slash(mastraDir);
    const defaultToolsPath = posix.join(normalizedMastraDir, "tools/**/*.{js,ts}");
    const defaultToolsIgnorePaths = [
      `!${posix.join(normalizedMastraDir, "tools/**/*.{test,spec}.{js,ts}")}`,
      `!${posix.join(normalizedMastraDir, "tools/**/__tests__/**")}`
    ];
    const defaultPaths = [defaultToolsPath, ...defaultToolsIgnorePaths];
    if (toolsPaths.length === 0) {
      return [defaultPaths];
    }
    return [...toolsPaths, defaultPaths];
  }
  async listToolsInputOptions(toolsPaths) {
    const inputs = {};
    for (const toolPath of toolsPaths) {
      const expandedPaths = await glob(toolPath, {
        absolute: true,
        expandDirectories: false
      });
      for (const path of expandedPaths) {
        if (await fsExtra.pathExists(path)) {
          const fileService = new FileService();
          const entryFile = fileService.getFirstExistingFile([
            join(path, "index.ts"),
            join(path, "index.js"),
            path
            // if path itself is a file
          ]);
          if (!entryFile || (await stat(entryFile)).isDirectory()) {
            this.logger.warn("No entry file found, skipping", { path });
            continue;
          }
          const uniqueToolID = crypto.randomUUID();
          const normalizedEntryFile = entryFile.replaceAll("\\", "/");
          inputs[`tools/${uniqueToolID}`] = normalizedEntryFile;
        } else {
          this.logger.warn("Tool path does not exist, skipping", { path });
        }
      }
    }
    return inputs;
  }
  async _bundle(serverFile, mastraEntryFile, {
    projectRoot,
    outputDirectory,
    enableEsmShim = true
  }, toolsPaths = [], bundleLocation = join(outputDirectory, this.outputDir)) {
    const analyzeDir = join(outputDirectory, this.analyzeOutputDir);
    const bundlerOptions = await this.getUserBundlerOptions(mastraEntryFile, outputDirectory);
    const internalBundlerOptions = {
      enableSourcemap: !!bundlerOptions.sourcemap,
      externals: bundlerOptions.externals ?? [],
      enableEsmShim,
      dynamicPackages: bundlerOptions.dynamicPackages
    };
    let analyzedBundleInfo;
    try {
      const resolvedToolsPaths = await this.listToolsInputOptions(toolsPaths);
      analyzedBundleInfo = await analyzeBundle(
        [serverFile, ...Object.values(resolvedToolsPaths)],
        mastraEntryFile,
        {
          outputDir: analyzeDir,
          projectRoot,
          platform: this.platform,
          bundlerOptions: internalBundlerOptions
        },
        this.logger
      );
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      if (error instanceof MastraError) {
        throw error;
      }
      throw new MastraError(
        {
          id: "DEPLOYER_BUNDLER_ANALYZE_FAILED",
          text: `Failed to analyze Mastra application: ${message}`,
          domain: ErrorDomain.DEPLOYER,
          category: ErrorCategory.SYSTEM
        },
        error
      );
    }
    const dependenciesToInstall = /* @__PURE__ */ new Map();
    for (const [dep, depInfo] of analyzedBundleInfo.externalDependencies) {
      if (analyzedBundleInfo.workspaceMap.has(dep) || !isBareModuleSpecifier(dep)) {
        continue;
      }
      let version = depInfo.version;
      let actualPackageName;
      try {
        let rootPath = await getPackageRootPath(dep, projectRoot);
        if (!rootPath) {
          rootPath = await getPackageRootPath(dep, import.meta.dirname);
        }
        if (rootPath) {
          const pkg2 = await readJSON(`${rootPath}/package.json`);
          actualPackageName = pkg2.name;
          if (!version) {
            version = pkg2.version;
          }
        }
      } catch {
      }
      version = version || "latest";
      const isAlias = actualPackageName && dep !== actualPackageName;
      if (isAlias) {
        dependenciesToInstall.set(dep, `npm:${actualPackageName}@${version}`);
      } else {
        dependenciesToInstall.set(dep, version);
      }
    }
    try {
      await this.writePackageJson(join(outputDirectory, this.outputDir), dependenciesToInstall);
      this.logger.info("Bundling Mastra application");
      const inputOptions = await this.getBundlerOptions(
        serverFile,
        mastraEntryFile,
        analyzedBundleInfo,
        toolsPaths,
        internalBundlerOptions
      );
      const bundler = await this.createBundler(
        {
          ...inputOptions,
          logLevel: inputOptions.logLevel === "silent" ? "warn" : inputOptions.logLevel,
          onwarn: (warning) => {
            if (warning.code === "CIRCULAR_DEPENDENCY") {
              if (warning.ids?.[0]?.includes("node_modules")) {
                return;
              }
              this.logger.warn("Circular dependency found", {
                dependency: warning.message.replace("Circular dependency: ", "")
              });
            }
          }
        },
        {
          dir: bundleLocation,
          manualChunks: {
            mastra: ["#mastra"]
          },
          sourcemap: internalBundlerOptions.enableSourcemap
        }
      );
      await bundler.write();
      const toolImports = [];
      const toolsExports = [];
      Array.from(Object.keys(inputOptions.input || {})).filter((key) => key.startsWith("tools/")).forEach((key, index) => {
        const toolExport = `tool${index}`;
        toolImports.push(`import * as ${toolExport} from './${key}.mjs';`);
        toolsExports.push(toolExport);
      });
      await writeFile(
        join(bundleLocation, "tools.mjs"),
        `${toolImports.join("\n")}

export const tools = [${toolsExports.join(", ")}]`
      );
      this.logger.info("Bundling Mastra done");
      this.logger.info("Copying public files");
      await this.copyPublic(dirname(mastraEntryFile), outputDirectory);
      this.logger.info("Done copying public files");
      this.logger.info("Copying .npmrc file");
      await this.copyDOTNPMRC({ outputDirectory, rootDir: projectRoot });
      this.logger.info("Done copying .npmrc file");
      this.logger.info("Installing dependencies");
      await this.installDependencies(outputDirectory, projectRoot);
      this.logger.info("Done installing dependencies");
      this.logger.info("Generating package-lock.json for deploy");
      await this.generateNpmLockfile(join(outputDirectory, this.outputDir));
      this.logger.info("Done generating package-lock.json");
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      throw new MastraError(
        {
          id: "DEPLOYER_BUNDLER_BUNDLE_STAGE_FAILED",
          text: `Failed during bundler bundle stage: ${message}`,
          domain: ErrorDomain.DEPLOYER,
          category: ErrorCategory.SYSTEM
        },
        error
      );
    }
  }
  async lint(_entryFile, _outputDirectory, toolsPaths) {
    const toolsInputOptions = await this.listToolsInputOptions(toolsPaths);
    const toolsLength = Object.keys(toolsInputOptions).length;
    if (toolsLength > 0) {
      this.logger.info("Found tools", { count: toolsLength });
    }
  }
};

export { Bundler, IS_DEFAULT };
//# sourceMappingURL=chunk-ISGGMBYN.js.map
//# sourceMappingURL=chunk-ISGGMBYN.js.map