import fs10 from 'node:fs';
import { spawnSync, spawn } from 'node:child_process';
import { inspect } from 'node:util';
import path from 'node:path';
import { builtinModules } from 'node:module';
import http from 'node:http';
import net from 'node:net';

// src/features/gen-html/infrastructure/local-server-workflow.ts
function stringifyUnknown(value) {
  if (value === void 0 || value === null) {
    return "";
  }
  if (typeof value === "string") {
    return value;
  }
  if (Buffer.isBuffer(value)) {
    return value.toString("utf8");
  }
  if (value instanceof Error) {
    return value.stack || value.message;
  }
  return inspect(value, { depth: 6, breakLength: 120 });
}
function isNodeError(error) {
  return error instanceof Error && typeof error.code === "string";
}
function getErrorSummary(error) {
  if (error instanceof Error) {
    return `${error.name}: ${error.message}`;
  }
  return stringifyUnknown(error) || "Unknown error";
}

// src/platform/core/timestamp-helpers.ts
function padTimestampPart(value) {
  return String(value).padStart(2, "0");
}
function getTimestampParts(now = /* @__PURE__ */ new Date()) {
  return {
    year: now.getFullYear(),
    month: padTimestampPart(now.getMonth() + 1),
    day: padTimestampPart(now.getDate()),
    hour: padTimestampPart(now.getHours()),
    minute: padTimestampPart(now.getMinutes()),
    second: padTimestampPart(now.getSeconds())
  };
}
function getHumanReadableNow(now = /* @__PURE__ */ new Date()) {
  const { year, month, day, hour, minute, second } = getTimestampParts(now);
  return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
var DEFAULT_LOCAL_SERVER_PORT = 4173;
var LOCAL_SERVER_HOST = "127.0.0.1";
var LOCAL_SERVER_HEALTH_PATH = "/index.json";
var LOCAL_SERVER_PORT_SEARCH_WINDOW = 20;
var LOCAL_SERVER_START_TIMEOUT_MS = 3e4;
var LOCAL_SERVER_STOP_TIMEOUT_MS = 5e3;
var LOCAL_SERVER_LOCK_STALE_MS = 12e4;
var LOCAL_SERVER_STATE_VERSION = 2;
var LOCAL_SERVER_SASS_VERSION = "^1.86.3";
var LOCAL_SERVER_REACT_QUERY_VERSION = "^5.81.2";
var LOCAL_SERVER_TYPES_NODE_VERSION = "^25.0.10";
var LOCAL_SERVER_TYPES_REACT_VERSION = "19.1.0";
var LOCAL_SERVER_TYPES_REACT_DOM_VERSION = "19.1.1";
var LOCAL_SERVER_TYPESCRIPT_VERSION = "5.8.2";
var LOCAL_SERVER_VITE_VERSION = "^6.3.1";
var STORYBOOK_VERSION = "8.6.14";
var LOCAL_SERVER_LEGACY_FILE_NAMES = ["server.mjs"];
var LOCAL_SERVER_STYLE_EXPORT_FILE_PATTERN = /\.(?:css|s[ac]ss)$/i;
var REQUIRED_LOCAL_SERVER_PUBLISHED_PACKAGE_NAMES = [
  "sales-frontend-api",
  "sales-frontend-assets",
  "sales-frontend-bridge",
  "sales-frontend-components",
  "sales-frontend-design-system",
  "sales-frontend-hooks",
  "sales-frontend-solution",
  "sales-frontend-stores",
  "sales-frontend-typescript-config"
];
var IMPORT_SPECIFIER_PATTERN = /\b(?:import|export)\s+(?:type\s+)?(?:[^'"`]*?\s+from\s*)?['"]([^'"`]+)['"]|\bimport\s*\(\s*['"]([^'"`]+)['"]\s*\)/g;
var SOURCE_FILE_EXTENSION_PATTERN = /\.[cm]?[jt]sx?$/i;
var WORKSPACE_PACKAGE_GROUP_NAMES = ["apps", "packages", "configs"];
var WORKSPACE_ROOT_MARKER_FILE_NAMES = ["pnpm-workspace.yaml", "pnpm-workspace.yml"];
var LOCAL_SERVER_RUNTIME_PACKAGE_NAMES = ["react", "react-dom", "@tanstack/react-query"];
var PACKAGE_MANIFEST_DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
var NODE_BUILTIN_MODULE_NAMES = new Set(
  builtinModules.flatMap((moduleName) => [moduleName, moduleName.replace(/^node:/, ""), `node:${moduleName.replace(/^node:/, "")}`])
);
var LOCAL_SERVER_DEFAULT_PACKAGE_JSON = {
  dependencies: {
    "@types/node": LOCAL_SERVER_TYPES_NODE_VERSION,
    "@types/react": LOCAL_SERVER_TYPES_REACT_VERSION,
    "@types/react-dom": LOCAL_SERVER_TYPES_REACT_DOM_VERSION,
    "@tanstack/react-query": LOCAL_SERVER_REACT_QUERY_VERSION,
    "@storybook/addon-essentials": STORYBOOK_VERSION,
    "@storybook/react": STORYBOOK_VERSION,
    "@storybook/react-vite": STORYBOOK_VERSION,
    react: "^19.1.0",
    "react-dom": "^19.1.0",
    sass: LOCAL_SERVER_SASS_VERSION,
    storybook: STORYBOOK_VERSION,
    typescript: LOCAL_SERVER_TYPESCRIPT_VERSION,
    vite: LOCAL_SERVER_VITE_VERSION
  },
  name: "ju-hong-e-local-server",
  private: true,
  scripts: {
    dev: "storybook dev -c ./.storybook",
    start: "storybook dev -c ./.storybook"
  },
  type: "commonjs"
};
function getLocalServerPaths(paths) {
  const directoryPath = path.join(paths.baseDir, "local-server");
  const generatedDirectoryPath = path.join(directoryPath, "generated");
  const multipleDirectoryPath = path.join(directoryPath, "multiple");
  const storybookConfigDirectoryPath = path.join(directoryPath, ".storybook");
  return {
    directoryPath,
    generatedDirectoryPath,
    generatedRegistryPath: path.join(directoryPath, "generated-registry.json"),
    multipleDirectoryPath,
    logPath: path.join(directoryPath, "server.log"),
    packageJsonPath: path.join(directoryPath, "package.json"),
    packageCustomJsonPath: path.join(directoryPath, "package-custom.json"),
    previewTargetPath: path.join(directoryPath, "preview-target.json"),
    refreshLockPath: path.join(directoryPath, "refresh.lock"),
    scssModuleDeclarationPath: path.join(directoryPath, "scss.d.ts"),
    serverLockPath: path.join(directoryPath, "server.lock"),
    statePath: path.join(directoryPath, "server-state.json"),
    storybookConfigDirectoryPath,
    storybookMainConfigPath: path.join(storybookConfigDirectoryPath, "main.ts"),
    storybookManagerConfigPath: path.join(storybookConfigDirectoryPath, "manager.tsx"),
    storybookPreviewConfigPath: path.join(storybookConfigDirectoryPath, "preview.ts"),
    tsconfigPath: path.join(directoryPath, "tsconfig.json")
  };
}
function isRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function readJsonFile(filePath) {
  if (!fs10.existsSync(filePath)) {
    return void 0;
  }
  const raw = fs10.readFileSync(filePath, "utf8").trim();
  if (!raw) {
    return void 0;
  }
  return JSON.parse(raw);
}
function writeJsonFile(filePath, value) {
  fs10.mkdirSync(path.dirname(filePath), { recursive: true });
  fs10.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
`, "utf8");
}
function readLocalServerState(paths) {
  try {
    return readJsonFile(getLocalServerPaths(paths).statePath) || {};
  } catch {
    return {};
  }
}
function writeLocalServerState(paths, state) {
  writeJsonFile(getLocalServerPaths(paths).statePath, {
    ...state,
    version: LOCAL_SERVER_STATE_VERSION
  });
}
function updateLocalServerState(paths, patch) {
  const nextState = {
    ...readLocalServerState(paths),
    ...patch,
    version: LOCAL_SERVER_STATE_VERSION
  };
  writeLocalServerState(paths, nextState);
  return nextState;
}

// src/features/gen-html/infrastructure/local-server/dependencies/manifest.ts
function normalizeDependencies(value) {
  if (!isRecord(value)) {
    return {};
  }
  return Object.entries(value).reduce((accumulator, [key, dependencyValue]) => {
    if (typeof dependencyValue === "string" && dependencyValue.trim()) {
      accumulator[key] = dependencyValue;
    }
    return accumulator;
  }, {});
}
function extractPackageManifestDependencies(value) {
  if (!isRecord(value)) {
    return {};
  }
  return PACKAGE_MANIFEST_DEPENDENCY_FIELDS.reduce((accumulator, fieldName) => {
    Object.assign(accumulator, normalizeDependencies(value[fieldName]));
    return accumulator;
  }, {});
}
function mergeLocalServerPackageJson(value, extraDependencies = {}) {
  const current = isRecord(value) ? value : {};
  const dependencies = normalizeDependencies(current.dependencies);
  const scripts = normalizeDependencies(current.scripts);
  return {
    ...current,
    dependencies: {
      ...LOCAL_SERVER_DEFAULT_PACKAGE_JSON.dependencies,
      ...extraDependencies,
      ...dependencies
    },
    name: typeof current.name === "string" && current.name.trim() ? current.name : LOCAL_SERVER_DEFAULT_PACKAGE_JSON.name,
    private: typeof current.private === "boolean" ? current.private : LOCAL_SERVER_DEFAULT_PACKAGE_JSON.private,
    scripts: {
      ...scripts,
      ...LOCAL_SERVER_DEFAULT_PACKAGE_JSON.scripts
    },
    type: LOCAL_SERVER_DEFAULT_PACKAGE_JSON.type
  };
}
function toSortedDependencies(dependencies) {
  return Object.fromEntries(Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right)));
}
function resolveLocalServerStyleExportTarget(value) {
  if (typeof value === "string") {
    return LOCAL_SERVER_STYLE_EXPORT_FILE_PATTERN.test(value) ? value : "";
  }
  if (Array.isArray(value)) {
    for (const entry of value) {
      const resolvedTarget = resolveLocalServerStyleExportTarget(entry);
      if (resolvedTarget) {
        return resolvedTarget;
      }
    }
    return "";
  }
  if (isRecord(value)) {
    return resolveLocalServerStyleExportTarget(value.default) || resolveLocalServerStyleExportTarget(value.import) || resolveLocalServerStyleExportTarget(value.require);
  }
  return "";
}
function buildLocalServerStyleExportBridgeContent(bridgeFilePath, targetFilePath) {
  const relativeTargetPath = path.relative(path.dirname(bridgeFilePath), targetFilePath).split(path.sep).join("/");
  const normalizedTargetPath = relativeTargetPath.startsWith(".") ? relativeTargetPath : `./${relativeTargetPath}`;
  if (path.extname(targetFilePath).toLowerCase() === ".css") {
    return `@import '${normalizedTargetPath}';
`;
  }
  return `@forward '${normalizedTargetPath}';
`;
}
function synchronizeLocalServerStyleExportBridges(paths, trace) {
  const { directoryPath, packageJsonPath } = getLocalServerPaths(paths);
  const localServerPackageJson = readJsonFile(packageJsonPath);
  const dependencyNames = Object.keys(normalizeDependencies(localServerPackageJson?.dependencies));
  for (const dependencyName of dependencyNames) {
    const dependencyDirectoryPath = path.join(directoryPath, "node_modules", ...dependencyName.split("/"));
    const dependencyPackageJsonPath = path.join(dependencyDirectoryPath, "package.json");
    const dependencyPackageJson = readJsonFile(dependencyPackageJsonPath);
    const dependencyExports = isRecord(dependencyPackageJson?.exports) ? dependencyPackageJson.exports : {};
    for (const [exportKey, exportValue] of Object.entries(dependencyExports)) {
      if (!exportKey.startsWith("./")) {
        continue;
      }
      const exportTargetPath = resolveLocalServerStyleExportTarget(exportValue);
      if (!exportTargetPath) {
        continue;
      }
      const exportSubpath = exportKey.slice(2);
      if (!exportSubpath) {
        continue;
      }
      const targetFilePath = path.resolve(dependencyDirectoryPath, exportTargetPath);
      if (!fs10.existsSync(targetFilePath) || !fs10.statSync(targetFilePath).isFile()) {
        trace("local-server:style-export-bridge:missing-target", `${dependencyName} ${exportKey} -> ${targetFilePath}`);
        continue;
      }
      const bridgeFilePath = path.join(
        dependencyDirectoryPath,
        ...exportSubpath.split("/").filter(Boolean)
      ) + path.extname(targetFilePath);
      const bridgeContent = buildLocalServerStyleExportBridgeContent(bridgeFilePath, targetFilePath);
      const currentBridgeContent = fs10.existsSync(bridgeFilePath) ? fs10.readFileSync(bridgeFilePath, "utf8") : "";
      if (currentBridgeContent === bridgeContent) {
        continue;
      }
      fs10.mkdirSync(path.dirname(bridgeFilePath), { recursive: true });
      fs10.writeFileSync(bridgeFilePath, bridgeContent, "utf8");
      trace("local-server:style-export-bridge:written", `${dependencyName} ${exportKey} -> ${bridgeFilePath}`);
    }
  }
}

// src/features/gen-html/infrastructure/local-server/dependencies/installer.ts
var LOCAL_SERVER_NPM_INSTALL_ARGS = ["install", "--legacy-peer-deps", "--no-audit", "--no-fund"];
function hasAllLocalServerDependencies(directoryPath) {
  const packageJsonPath = path.join(directoryPath, "package.json");
  const packageJson = readJsonFile(packageJsonPath);
  const dependencies = Object.keys(normalizeDependencies(packageJson?.dependencies));
  if (dependencies.length === 0) {
    return true;
  }
  return dependencies.every((dependencyName) => fs10.existsSync(path.join(directoryPath, "node_modules", ...dependencyName.split("/"))));
}
function installLocalServerDependencies(directoryPath, trace) {
  trace("local-server:install:start", `${directoryPath} :: npm ${LOCAL_SERVER_NPM_INSTALL_ARGS.join(" ")}`);
  const result = spawnSync("npm", [...LOCAL_SERVER_NPM_INSTALL_ARGS], {
    cwd: directoryPath,
    encoding: "utf8",
    maxBuffer: 1024 * 1024 * 20,
    stdio: "inherit"
  });
  if (result.error || result.status !== 0) {
    trace("local-server:install:failed", getErrorSummary(result.error || result.stderr || result.status));
    throw new Error("\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84 \uC758\uC874\uC131 \uC124\uCE58\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
  }
  trace("local-server:install:done", directoryPath);
}
function ensureLocalServerDependencies(paths, trace, forceInstall = false) {
  const { directoryPath } = getLocalServerPaths(paths);
  const nodeModulesPath = path.join(directoryPath, "node_modules");
  const hasNodeModules = fs10.existsSync(nodeModulesPath);
  const hasRequiredDependencies = hasNodeModules ? hasAllLocalServerDependencies(directoryPath) : false;
  const shouldInstall = forceInstall || !hasNodeModules || !hasRequiredDependencies;
  if (hasNodeModules && !hasRequiredDependencies) {
    trace("local-server:install:repair", directoryPath);
  }
  if (shouldInstall) {
    installLocalServerDependencies(directoryPath, trace);
  }
  synchronizeLocalServerStyleExportBridges(paths, trace);
}
function removeLegacyLocalServerFiles(localServerPaths, trace) {
  for (const fileName of LOCAL_SERVER_LEGACY_FILE_NAMES) {
    const targetPath = path.join(localServerPaths.directoryPath, fileName);
    if (!fs10.existsSync(targetPath)) {
      continue;
    }
    fs10.unlinkSync(targetPath);
    trace("local-server:cleanup:legacy-file", targetPath);
  }
}
var LATEST_VERSION_TAG = "latest";
var PUBLISHED_INSTALL_BASE_ARGS = ["install", "--legacy-peer-deps", "--no-audit", "--no-fund"];
var PACKAGE_CUSTOM_JSON_TEMPLATE = `// package-custom.json
// local-server \uC758 package.json \uC744 \uC0DD\uC131\uD560 \uB54C \uC6B0\uC120 \uC801\uC6A9\uD560 \uCEE4\uC2A4\uD140 \uC124\uC815 \uD30C\uC77C\uC785\uB2C8\uB2E4.
//
// - dependencies: \uD2B9\uC815 \uD328\uD0A4\uC9C0\uB97C \uC9C0\uC815\uD55C \uBC84\uC804\uC73C\uB85C \uC124\uCE58\uD569\uB2C8\uB2E4.
//   (\uC9C0\uC815\uD558\uC9C0 \uC54A\uC73C\uBA74 sales-frontend-* \uACF5\uC6A9 \uD328\uD0A4\uC9C0\uB294 latest \uB85C \uC124\uCE58\uB429\uB2C8\uB2E4)
// - \uADF8 \uC678 package.json \uC18D\uC131(scripts, type \uB4F1)\uC740 \uC0DD\uC131\uB418\uB294 package.json \uC5D0 \uBCD1\uD569\uB429\uB2C8\uB2E4. (\uCEE4\uC2A4\uD140 \uC6B0\uC120)
//
// \uC0AC\uC6A9 \uC608\uC2DC:
// {
//   "dependencies": {
//     "sales-frontend-design-system": "1.2.3"
//   },
//   "scripts": {
//     "custom": "echo hello"
//   }
// }
{}
`;
function stripJsonComments(input) {
  let result = "";
  let insideString = false;
  let insideLineComment = false;
  let insideBlockComment = false;
  for (let index = 0; index < input.length; index += 1) {
    const character = input[index];
    const nextCharacter = input[index + 1];
    if (insideLineComment) {
      if (character === "\n") {
        insideLineComment = false;
        result += character;
      }
      continue;
    }
    if (insideBlockComment) {
      if (character === "*" && nextCharacter === "/") {
        insideBlockComment = false;
        index += 1;
      }
      continue;
    }
    if (insideString) {
      result += character;
      if (character === "\\") {
        result += nextCharacter ?? "";
        index += 1;
      } else if (character === '"') {
        insideString = false;
      }
      continue;
    }
    if (character === '"') {
      insideString = true;
      result += character;
      continue;
    }
    if (character === "/" && nextCharacter === "/") {
      insideLineComment = true;
      index += 1;
      continue;
    }
    if (character === "/" && nextCharacter === "*") {
      insideBlockComment = true;
      index += 1;
      continue;
    }
    result += character;
  }
  return result;
}
function ensureLocalServerPackageCustomFile(paths, trace) {
  const { packageCustomJsonPath } = getLocalServerPaths(paths);
  if (fs10.existsSync(packageCustomJsonPath)) {
    return false;
  }
  fs10.mkdirSync(path.dirname(packageCustomJsonPath), { recursive: true });
  fs10.writeFileSync(packageCustomJsonPath, PACKAGE_CUSTOM_JSON_TEMPLATE, "utf8");
  trace("local-server:package-custom:created", packageCustomJsonPath);
  return true;
}
function readLocalServerPackageCustom(paths, trace) {
  const { packageCustomJsonPath } = getLocalServerPaths(paths);
  if (!fs10.existsSync(packageCustomJsonPath)) {
    return {};
  }
  const raw = stripJsonComments(fs10.readFileSync(packageCustomJsonPath, "utf8")).trim();
  if (!raw) {
    return {};
  }
  let parsed;
  try {
    parsed = JSON.parse(raw);
  } catch (error) {
    trace("local-server:package-custom:parse-failed", getErrorSummary(error));
    throw new Error(
      `package-custom.json \uC744 \uD574\uC11D\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. JSON \uD615\uC2DD\uC744 \uD655\uC778\uD558\uC138\uC694: ${packageCustomJsonPath} (${getErrorSummary(error)})`
    );
  }
  if (!isRecord(parsed)) {
    trace("local-server:package-custom:invalid-shape", typeof parsed);
    throw new Error(
      `package-custom.json \uC758 \uCD5C\uC0C1\uC704\uB294 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4. \uD615\uC2DD\uC744 \uD655\uC778\uD558\uC138\uC694: ${packageCustomJsonPath}`
    );
  }
  return parsed;
}
function applyLocalServerPackageCustomManifest(packageJson, custom) {
  const manifestOverrides = { ...custom };
  delete manifestOverrides.dependencies;
  if (Object.keys(manifestOverrides).length === 0) {
    return packageJson;
  }
  return {
    ...packageJson,
    ...manifestOverrides
  };
}
function resolveInstalledVersionInDirectory(directoryPath, dependencyName) {
  const manifestPath = path.join(directoryPath, "node_modules", ...dependencyName.split("/"), "package.json");
  if (!fs10.existsSync(manifestPath)) {
    return "";
  }
  try {
    const manifest = JSON.parse(fs10.readFileSync(manifestPath, "utf8"));
    return typeof manifest.version === "string" ? manifest.version.trim() : "";
  } catch {
    return "";
  }
}
function resolveRequiredPublishedInstallTargets(custom) {
  const customDependencies = normalizeDependencies(custom.dependencies);
  const targetNames = /* @__PURE__ */ new Set([...REQUIRED_LOCAL_SERVER_PUBLISHED_PACKAGE_NAMES, ...Object.keys(customDependencies)]);
  const targets = {};
  for (const dependencyName of targetNames) {
    targets[dependencyName] = customDependencies[dependencyName] || LATEST_VERSION_TAG;
  }
  return targets;
}
function resolvePendingInstallSpecs(directoryPath, targets, force) {
  const specs = [];
  for (const [dependencyName, version] of Object.entries(targets)) {
    const installedVersion = resolveInstalledVersionInDirectory(directoryPath, dependencyName);
    if (force) {
      specs.push(`${dependencyName}@${version}`);
      continue;
    }
    if (!installedVersion) {
      specs.push(`${dependencyName}@${version}`);
      continue;
    }
    if (version !== LATEST_VERSION_TAG && version !== installedVersion) {
      specs.push(`${dependencyName}@${version}`);
    }
  }
  return specs;
}
function installRequiredPublishedDependencies(paths, trace, force = false) {
  const { directoryPath } = getLocalServerPaths(paths);
  const custom = readLocalServerPackageCustom(paths, trace);
  const targets = resolveRequiredPublishedInstallTargets(custom);
  const specs = resolvePendingInstallSpecs(directoryPath, targets, force);
  if (specs.length === 0) {
    trace("local-server:published-install:skip", `force=${force}`);
    return [];
  }
  trace("local-server:published-install:start", `npm install ${specs.join(" ")}`);
  const result = spawnSync("npm", [...PUBLISHED_INSTALL_BASE_ARGS, ...specs], {
    cwd: directoryPath,
    encoding: "utf8",
    maxBuffer: 1024 * 1024 * 20,
    stdio: "inherit"
  });
  if (result.error || result.status !== 0) {
    trace("local-server:published-install:failed", getErrorSummary(result.error || result.stderr || result.status));
    throw new Error("\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84 \uACF5\uC6A9 \uD328\uD0A4\uC9C0 \uC124\uCE58\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
  }
  trace("local-server:published-install:done", specs.join(" "));
  return specs;
}
function isBareImportSpecifier(specifier) {
  return Boolean(specifier) && !specifier.startsWith(".") && !specifier.startsWith("/") && !specifier.includes("://") && !specifier.startsWith("\0");
}
function getPackageNameFromImportSpecifier(specifier) {
  if (!isBareImportSpecifier(specifier) || NODE_BUILTIN_MODULE_NAMES.has(specifier)) {
    return "";
  }
  if (specifier.startsWith("@")) {
    const [scope, name] = specifier.split("/");
    return scope && name ? `${scope}/${name}` : "";
  }
  return specifier.split("/")[0] || "";
}
function listSourceFiles(directoryPath) {
  if (!fs10.existsSync(directoryPath) || !fs10.statSync(directoryPath).isDirectory()) {
    return [];
  }
  const collected = [];
  const queue = [directoryPath];
  while (queue.length > 0) {
    const currentDirectoryPath = queue.pop();
    if (!currentDirectoryPath) {
      continue;
    }
    for (const entry of fs10.readdirSync(currentDirectoryPath, { withFileTypes: true })) {
      const entryPath = path.join(currentDirectoryPath, entry.name);
      if (entry.isDirectory()) {
        queue.push(entryPath);
        continue;
      }
      if (entry.isFile() && SOURCE_FILE_EXTENSION_PATTERN.test(entry.name)) {
        collected.push(entryPath);
      }
    }
  }
  return collected.sort((left, right) => left.localeCompare(right));
}
function isBundledLocalServerDependency(packageName) {
  return Object.prototype.hasOwnProperty.call(LOCAL_SERVER_DEFAULT_PACKAGE_JSON.dependencies, packageName);
}
function collectOutputDependencyNames(outputDirectory) {
  const packageNames = /* @__PURE__ */ new Set();
  for (const filePath of listSourceFiles(outputDirectory)) {
    const source = fs10.readFileSync(filePath, "utf8");
    let match = IMPORT_SPECIFIER_PATTERN.exec(source);
    while (match) {
      const packageName = getPackageNameFromImportSpecifier(match[1] || match[2] || "");
      if (packageName && !isBundledLocalServerDependency(packageName)) {
        packageNames.add(packageName);
      }
      match = IMPORT_SPECIFIER_PATTERN.exec(source);
    }
    IMPORT_SPECIFIER_PATTERN.lastIndex = 0;
  }
  return [...packageNames].sort((left, right) => left.localeCompare(right));
}

// src/features/gen-html/infrastructure/local-server/dependencies/workspace.ts
function isWorkspaceRootDirectory(directoryPath) {
  if (!directoryPath) {
    return false;
  }
  if (WORKSPACE_ROOT_MARKER_FILE_NAMES.some((fileName) => fs10.existsSync(path.join(directoryPath, fileName)))) {
    return true;
  }
  const packageJsonPath = path.join(directoryPath, "package.json");
  const manifest = readJsonFile(packageJsonPath);
  if (!isRecord(manifest)) {
    return false;
  }
  return Boolean(manifest.workspaces);
}
function resolveWorkspaceRootDirectory(startDirectory) {
  let currentDirectoryPath = path.resolve(startDirectory);
  while (!isWorkspaceRootDirectory(currentDirectoryPath)) {
    const parentDirectoryPath = path.dirname(currentDirectoryPath);
    if (parentDirectoryPath === currentDirectoryPath) {
      return path.resolve(startDirectory);
    }
    currentDirectoryPath = parentDirectoryPath;
  }
  return currentDirectoryPath;
}
function collectWorkspacePackageJsonPaths(rootDir) {
  const workspaceRootDir = resolveWorkspaceRootDirectory(rootDir);
  const packageJsonPaths = [path.join(workspaceRootDir, "package.json")].filter((filePath) => fs10.existsSync(filePath));
  for (const groupName of WORKSPACE_PACKAGE_GROUP_NAMES) {
    const groupDirectoryPath = path.join(workspaceRootDir, groupName);
    if (!fs10.existsSync(groupDirectoryPath) || !fs10.statSync(groupDirectoryPath).isDirectory()) {
      continue;
    }
    for (const entry of fs10.readdirSync(groupDirectoryPath, { withFileTypes: true })) {
      if (!entry.isDirectory()) {
        continue;
      }
      const packageJsonPath = path.join(groupDirectoryPath, entry.name, "package.json");
      if (fs10.existsSync(packageJsonPath)) {
        packageJsonPaths.push(packageJsonPath);
      }
    }
  }
  return packageJsonPaths.sort((left, right) => left.localeCompare(right));
}
function listAncestorDirectories(startDirectory) {
  const directories = [];
  let currentDirectoryPath = path.resolve(startDirectory);
  let parentDirectoryPath = path.dirname(currentDirectoryPath);
  while (parentDirectoryPath !== currentDirectoryPath) {
    directories.push(currentDirectoryPath);
    currentDirectoryPath = parentDirectoryPath;
    parentDirectoryPath = path.dirname(currentDirectoryPath);
  }
  directories.push(currentDirectoryPath);
  return directories;
}
function resolveInstalledPackageVersion(dependencyName, startDirectories) {
  const candidateDirectories = Array.from(
    new Set(
      startDirectories.flatMap((directoryPath) => {
        if (!directoryPath) {
          return [];
        }
        return listAncestorDirectories(directoryPath);
      })
    )
  );
  for (const directoryPath of candidateDirectories) {
    const packageJsonPath = path.join(directoryPath, "node_modules", ...dependencyName.split("/"), "package.json");
    const manifest = readJsonFile(packageJsonPath);
    const version = typeof manifest?.version === "string" ? manifest.version.trim() : "";
    if (version) {
      return version;
    }
  }
  return "";
}
function resolveLocalServerRuntimeDependencies(_paths, _localServerDirectoryPath, trace) {
  const dependencyNames = [...LOCAL_SERVER_RUNTIME_PACKAGE_NAMES];
  const defaultDependencies = normalizeDependencies(LOCAL_SERVER_DEFAULT_PACKAGE_JSON.dependencies);
  const resolvedDependencies = Object.fromEntries(
    dependencyNames.map((dependencyName) => [dependencyName, defaultDependencies[dependencyName] || ""]).filter(([, version]) => Boolean(version))
  );
  trace(
    "local-server:setup:runtime-dependencies",
    Object.entries(resolvedDependencies).map(([dependencyName, version]) => `${dependencyName}@${version}`).join(", ")
  );
  return resolvedDependencies;
}
function toLocalFileDependencyValue(localServerDirectoryPath, packageDirectoryPath) {
  const relativePath = path.relative(localServerDirectoryPath, packageDirectoryPath).split(path.sep).join("/");
  return `file:${relativePath || "."}`;
}
function buildWorkspaceDependencyCatalog(rootDir, localServerDirectoryPath) {
  const packageJsonPaths = collectWorkspacePackageJsonPaths(rootDir);
  const catalog = /* @__PURE__ */ new Map();
  const workspacePackageDirectories = /* @__PURE__ */ new Map();
  for (const packageJsonPath of packageJsonPaths) {
    const manifest = readJsonFile(packageJsonPath);
    const packageName = typeof manifest?.name === "string" ? manifest.name.trim() : "";
    if (packageName) {
      workspacePackageDirectories.set(packageName, path.dirname(packageJsonPath));
    }
  }
  for (const [packageName, packageDirectoryPath] of workspacePackageDirectories.entries()) {
    if (!catalog.has(packageName)) {
      catalog.set(packageName, toLocalFileDependencyValue(localServerDirectoryPath, packageDirectoryPath));
    }
  }
  for (const packageJsonPath of packageJsonPaths) {
    const manifest = readJsonFile(packageJsonPath);
    const dependencies = extractPackageManifestDependencies(manifest);
    for (const [dependencyName, version] of Object.entries(dependencies)) {
      if (catalog.has(dependencyName)) {
        continue;
      }
      if (version.startsWith("workspace:") && workspacePackageDirectories.has(dependencyName)) {
        catalog.set(
          dependencyName,
          toLocalFileDependencyValue(localServerDirectoryPath, workspacePackageDirectories.get(dependencyName) || "")
        );
        continue;
      }
      if (version.trim()) {
        catalog.set(dependencyName, version);
      }
    }
  }
  return Object.fromEntries([...catalog.entries()].sort(([left], [right]) => left.localeCompare(right)));
}
function resolveGeneratedOutputDependencies(paths, localServerDirectoryPath, outputDirectory, trace) {
  const dependencyNames = collectOutputDependencyNames(outputDirectory);
  if (dependencyNames.length === 0) {
    return {};
  }
  const workspaceCatalog = buildWorkspaceDependencyCatalog(paths.rootDir, localServerDirectoryPath);
  const workspaceRootDir = resolveWorkspaceRootDirectory(paths.rootDir);
  const resolvedDependencies = {};
  const unresolvedDependencyNames = [];
  for (const dependencyName of dependencyNames) {
    const version = workspaceCatalog[dependencyName] || resolveInstalledPackageVersion(dependencyName, [localServerDirectoryPath, paths.rootDir, workspaceRootDir]);
    if (!version) {
      unresolvedDependencyNames.push(dependencyName);
      continue;
    }
    resolvedDependencies[dependencyName] = version;
  }
  if (Object.keys(resolvedDependencies).length > 0) {
    trace(
      "local-server:setup:generated-dependencies",
      Object.entries(resolvedDependencies).map(([dependencyName, version]) => `${dependencyName}@${version}`).join(", ")
    );
  }
  if (unresolvedDependencyNames.length > 0) {
    trace("local-server:setup:unresolved-dependencies", unresolvedDependencyNames.join(", "));
  }
  return resolvedDependencies;
}
function findListeningProcessId(port) {
  if (!Number.isInteger(port) || port <= 0) {
    return void 0;
  }
  if (process.platform === "win32") {
    return void 0;
  }
  const result = spawnSync("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
    encoding: "utf8",
    maxBuffer: 1024 * 1024
  });
  if (result.error || result.status !== 0) {
    return void 0;
  }
  const firstLine = result.stdout.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
  const pid = firstLine ? Number(firstLine) : NaN;
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
}
function getProcessCurrentWorkingDirectory(pid) {
  if (!Number.isInteger(pid) || pid <= 0 || process.platform === "win32") {
    return void 0;
  }
  const result = spawnSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
    encoding: "utf8",
    maxBuffer: 1024 * 1024
  });
  if (result.error || result.status !== 0) {
    return void 0;
  }
  const cwdLine = result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.startsWith("n"));
  return cwdLine ? cwdLine.slice(1) : void 0;
}
function getLocalServerPortCandidates(startPort = DEFAULT_LOCAL_SERVER_PORT) {
  return Array.from({ length: LOCAL_SERVER_PORT_SEARCH_WINDOW }, (_, index) => startPort + index);
}
function findLocalServerListeningProcesses(directoryPath) {
  const resolvedDirectoryPath = path.resolve(directoryPath);
  const processMap = /* @__PURE__ */ new Map();
  for (const port of getLocalServerPortCandidates()) {
    const pid = findListeningProcessId(port);
    if (!pid || processMap.has(pid)) {
      continue;
    }
    const cwd = getProcessCurrentWorkingDirectory(pid);
    if (!cwd || path.resolve(cwd) !== resolvedDirectoryPath) {
      continue;
    }
    processMap.set(pid, { pid, port });
  }
  return Array.from(processMap.values()).sort((left, right) => left.port - right.port);
}
function getHealthUrl(port) {
  return `http://${LOCAL_SERVER_HOST}:${port}${LOCAL_SERVER_HEALTH_PATH}`;
}
function requestJson(url) {
  return new Promise((resolve) => {
    const request = http.get(url, { timeout: 2e3 }, (response) => {
      let body = "";
      response.setEncoding("utf8");
      response.on("data", (chunk) => {
        body += chunk;
      });
      response.on("end", () => {
        if (response.statusCode !== 200) {
          resolve(void 0);
          return;
        }
        try {
          resolve(JSON.parse(body));
        } catch {
          resolve(void 0);
        }
      });
    });
    request.on("timeout", () => {
      request.destroy();
      resolve(void 0);
    });
    request.on("error", () => {
      resolve(void 0);
    });
  });
}
function isPortAvailable(port) {
  return new Promise((resolve) => {
    const server = net.createServer();
    server.once("error", () => {
      resolve(false);
    });
    server.once("listening", () => {
      server.close(() => {
        resolve(true);
      });
    });
    server.listen(port, LOCAL_SERVER_HOST);
  });
}
function wait(delayMs) {
  return new Promise((resolve) => {
    setTimeout(resolve, delayMs);
  });
}
async function findAvailablePort(startPort) {
  for (const nextPort of getLocalServerPortCandidates(startPort)) {
    if (await isPortAvailable(nextPort)) {
      return nextPort;
    }
  }
  throw new Error(`\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB85C\uCEEC \uC11C\uBC84 \uD3EC\uD2B8\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. startPort=${startPort}`);
}
function getLocalServerBaseUrl(port) {
  return `http://${LOCAL_SERVER_HOST}:${port}`;
}
async function waitUntilServerIsReady(options) {
  const startedAt = Date.now();
  while (Date.now() - startedAt < LOCAL_SERVER_START_TIMEOUT_MS) {
    const index = await requestJson(getHealthUrl(options.port));
    if (index && isRecord(index.entries)) {
      return;
    }
    await wait(300);
  }
  throw new Error(`\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84\uAC00 ${LOCAL_SERVER_START_TIMEOUT_MS}ms \uC548\uC5D0 \uC900\uBE44\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. port=${options.port}`);
}
async function waitUntilServerStops(port) {
  const startedAt = Date.now();
  while (Date.now() - startedAt < LOCAL_SERVER_STOP_TIMEOUT_MS) {
    if (await isPortAvailable(port)) {
      return;
    }
    await wait(200);
  }
}
function terminateLocalServerProcess(pid) {
  if (process.platform === "win32") {
    const result = spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
      stdio: "ignore",
      windowsHide: true
    });
    if (result.error || result.status !== 0 && result.status !== 128) {
      throw new Error(`\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84 \uC885\uB8CC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. pid=${pid}`);
    }
    return;
  }
  try {
    process.kill(pid, "SIGTERM");
  } catch (error) {
    if (!isNodeError(error) || error.code !== "ESRCH") {
      throw error;
    }
  }
}
function forceTerminateLocalServerProcess(pid) {
  try {
    process.kill(pid, "SIGKILL");
  } catch (error) {
    if (!isNodeError(error) || error.code !== "ESRCH") {
      throw error;
    }
  }
}
async function cleanupDuplicateLocalServerProcesses(options) {
  const localServerPaths = getLocalServerPaths(options.paths);
  const duplicatedProcesses = findLocalServerListeningProcesses(localServerPaths.directoryPath);
  for (const processInfo of duplicatedProcesses) {
    options.trace("local-server:cleanup:start", `pid=${processInfo.pid} port=${processInfo.port}`);
    terminateLocalServerProcess(processInfo.pid);
    await waitUntilServerStops(processInfo.port);
    const remainingPid = findListeningProcessId(processInfo.port);
    if (remainingPid) {
      options.trace("local-server:cleanup:force", `pid=${remainingPid} port=${processInfo.port}`);
      forceTerminateLocalServerProcess(remainingPid);
      await waitUntilServerStops(processInfo.port);
    }
  }
}
function wait2(delayMs) {
  return new Promise((resolve) => {
    setTimeout(resolve, delayMs);
  });
}
function isStaleLock(lockPath, staleMs) {
  try {
    const stat = fs10.statSync(lockPath);
    return Date.now() - stat.mtimeMs > staleMs;
  } catch {
    return false;
  }
}
function tryAcquireFileLock(options) {
  const record = {
    acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
    owner: options.owner,
    pid: process.pid
  };
  fs10.mkdirSync(path.dirname(options.lockPath), { recursive: true });
  try {
    const fileDescriptor = fs10.openSync(options.lockPath, "wx");
    fs10.writeFileSync(fileDescriptor, `${JSON.stringify(record, null, 2)}
`, "utf8");
    fs10.closeSync(fileDescriptor);
    options.trace("local-server:lock:acquired", `${options.owner} ${options.lockPath}`);
    return true;
  } catch {
    return false;
  }
}
async function withFileLock(options, task) {
  const staleMs = options.staleMs ?? LOCAL_SERVER_LOCK_STALE_MS;
  const timeoutMs = options.timeoutMs ?? staleMs;
  const startedAt = Date.now();
  while (!tryAcquireFileLock(options)) {
    if (isStaleLock(options.lockPath, staleMs)) {
      options.trace("local-server:lock:stale:remove", `${options.owner} ${options.lockPath}`);
      fs10.rmSync(options.lockPath, { force: true });
      continue;
    }
    if (Date.now() - startedAt > timeoutMs) {
      throw new Error(`local-server \uB77D \uD68D\uB4DD \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. owner=${options.owner} lock=${options.lockPath}`);
    }
    options.trace("local-server:lock:wait", `${options.owner} ${options.lockPath}`);
    await wait2(100);
  }
  try {
    return await task();
  } finally {
    fs10.rmSync(options.lockPath, { force: true });
    options.trace("local-server:lock:released", `${options.owner} ${options.lockPath}`);
  }
}
function withLocalServerLock(paths, trace, owner, task) {
  return withFileLock(
    {
      lockPath: getLocalServerPaths(paths).serverLockPath,
      owner,
      trace
    },
    task
  );
}
function withLocalServerRefreshLock(paths, trace, owner, task) {
  return withFileLock(
    {
      lockPath: getLocalServerPaths(paths).refreshLockPath,
      owner,
      trace
    },
    task
  );
}

// src/features/retouch/domain/constants.ts
var RETOUCH_PORT_CANDIDATES = [4196, 4197, 4198, 4199];
var RETOUCH_CHANNEL_PICK_START = "juhonge/retouch/pick-start";
var RETOUCH_CHANNEL_PICK_RESULT = "juhonge/retouch/pick-result";
var RETOUCH_CHANNEL_PICK_CANCEL = "juhonge/retouch/pick-cancel";
var RETOUCH_CHANNEL_CLEAR_TARGET = "juhonge/retouch/clear-target";
var RETOUCH_CHANNEL_FLASH_TARGET = "juhonge/retouch/flash-target";
var RETOUCH_RESULT_MARKER = "__RETOUCH_RESULT__";
var RETOUCH_ERROR_MARKER = "__RETOUCH_ERROR__";
function normalizeOutputRelativePath(outputDirectory, filePath) {
  return path.relative(outputDirectory, filePath).split(path.sep).join("/");
}

// src/features/gen-html/domain/storybook.ts
var STORY_FILE_PATTERN = /\.stories\.[cm]?[jt]sx?$/i;
function isGenHtmlStoryFilePath(filePath) {
  return STORY_FILE_PATTERN.test(filePath);
}

// src/features/gen-html/infrastructure/runtime.ts
function listOutputFiles(outputDirectory) {
  if (!fs10.existsSync(outputDirectory) || !fs10.statSync(outputDirectory).isDirectory()) {
    return [];
  }
  const files = [];
  const visitDirectory = (directoryPath) => {
    const entries = fs10.readdirSync(directoryPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
    for (const entry of entries) {
      const entryPath = path.join(directoryPath, entry.name);
      if (entry.isDirectory()) {
        visitDirectory(entryPath);
        continue;
      }
      if (entry.isFile()) {
        files.push(normalizeOutputRelativePath(outputDirectory, entryPath));
      }
    }
  };
  visitDirectory(outputDirectory);
  return files.sort((left, right) => left.localeCompare(right));
}
function isGenHtmlPreviewDirectoryCandidate(outputDirectory) {
  return listOutputFiles(outputDirectory).some((filePath) => isGenHtmlStoryFilePath(filePath));
}

// src/features/gen-html/infrastructure/local-server/storybook-config.ts
function listGeneratedOutputDirectories(paths) {
  const { generatedDirectoryPath } = getLocalServerPaths(paths);
  if (!fs10.existsSync(generatedDirectoryPath) || !fs10.statSync(generatedDirectoryPath).isDirectory()) {
    return [];
  }
  return fs10.readdirSync(generatedDirectoryPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(generatedDirectoryPath, entry.name)).sort((left, right) => left.localeCompare(right));
}
function resolveValidGeneratedPreviewDirectories(paths) {
  return listGeneratedOutputDirectories(paths).filter((directoryPath) => isGenHtmlPreviewDirectoryCandidate(directoryPath));
}
function setLocalServerPreviewTarget(paths) {
  const localServerPaths = getLocalServerPaths(paths);
  const validDirectories = resolveValidGeneratedPreviewDirectories(paths);
  const nextTarget = validDirectories.length > 0 ? { directories: validDirectories } : {};
  writeJsonFile(localServerPaths.previewTargetPath, nextTarget);
}
function getStorybookExecutablePath(directoryPath) {
  return path.join(directoryPath, "node_modules", ".bin", process.platform === "win32" ? "storybook.cmd" : "storybook");
}
function getScssModuleDeclarationContent() {
  return `declare module '*.module.scss' {
  const classes: Record<string, string>;
  export default classes;
}
`;
}
function getLocalServerTsconfigContent() {
  return `{
  "extends": "sales-frontend-typescript-config/nextjs.json",
  "compilerOptions": {
    "types": ["node"],
    "jsx": "react-jsx",
    "jsxImportSource": "react"
  },
  "include": [".storybook/**/*", "generated/**/*", "scss.d.ts"],
  "exclude": ["node_modules", "dist"]
}
`;
}
function getStorybookMainConfigContent() {
  return `import fs from 'node:fs';
import path from 'node:path';
import type { StorybookConfig } from '@storybook/react-vite';
import type { Alias } from 'vite';

const localServerRoot = path.resolve(__dirname, '..');
const localServerPackageJsonPath = path.resolve(localServerRoot, 'package.json');
const workspaceRoot = path.resolve(__dirname, '..', '..');
const dependencyAliasExcludeNames = new Set(['react', 'react-dom', 'sass', 'storybook']);
const dependencyAliasExcludePrefixes = ['@storybook/'];

function readLocalServerDependencies() {
  try {
    const raw = fs.readFileSync(localServerPackageJsonPath, 'utf8').trim();

    if (!raw) {
      return {};
    }

    const parsed = JSON.parse(raw) as { dependencies?: Record<string, string> };

    return parsed.dependencies && typeof parsed.dependencies === 'object' ? parsed.dependencies : {};
  } catch {
    return {};
  }
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function resolveExportTarget(value: unknown): string {
  if (typeof value === 'string' && value.trim()) {
    return value.trim();
  }

  if (Array.isArray(value)) {
    for (const entry of value) {
      const resolvedTarget = resolveExportTarget(entry);

      if (resolvedTarget) {
        return resolvedTarget;
      }
    }

    return '';
  }

  if (isRecord(value)) {
    return resolveExportTarget(value.import) || resolveExportTarget(value.default) || resolveExportTarget(value.require);
  }

  return '';
}

function escapeRegExp(value: string) {
  return value.replace(/[][\\\\^$.*+?(){}|]/g, '\\\\$&');
}

function buildDependencyAliases() {
  return Object.keys(readLocalServerDependencies()).reduce<Alias[]>((accumulator, dependencyName) => {
    if (dependencyAliasExcludeNames.has(dependencyName) || dependencyAliasExcludePrefixes.some((prefix) => dependencyName.startsWith(prefix))) {
      return accumulator;
    }

    const dependencyPath = path.resolve(localServerRoot, 'node_modules', ...dependencyName.split('/'));
    const dependencyPackageJsonPath = path.resolve(dependencyPath, 'package.json');

    if (fs.existsSync(dependencyPath)) {
      try {
        const dependencyPackageJson = JSON.parse(fs.readFileSync(dependencyPackageJsonPath, 'utf8')) as { exports?: Record<string, unknown> };
        const dependencyExports = isRecord(dependencyPackageJson.exports) ? dependencyPackageJson.exports : {};
        const rootExportTarget = resolveExportTarget(dependencyExports['.']);
        const rootReplacement = rootExportTarget ? path.resolve(dependencyPath, rootExportTarget) : dependencyPath;
        const dependencyNamePattern = escapeRegExp(dependencyName);

        accumulator.push({
          find: new RegExp(\`^\${dependencyNamePattern}$\`),
          replacement: rootReplacement
        });

        for (const [exportKey, exportValue] of Object.entries(dependencyExports)) {
          if (!exportKey.startsWith('./')) {
            continue;
          }

          const exportTarget = resolveExportTarget(exportValue);

          if (!exportTarget) {
            continue;
          }

          accumulator.push({
            find: new RegExp(\`^\${escapeRegExp(\`\${dependencyName}/\${exportKey.slice(2)}\`)}$\`),
            replacement: path.resolve(dependencyPath, exportTarget)
          });
        }
      } catch {
        accumulator.push({
          find: new RegExp(\`^\${escapeRegExp(dependencyName)}$\`),
          replacement: dependencyPath
        });
      }

      accumulator.push({
        find: new RegExp(\`^\${escapeRegExp(dependencyName)}/(.+)$\`),
        replacement: \`\${dependencyPath}/$1\`
      });
    }

    return accumulator;
  }, []);
}

function resolveStories() {
  return ['../generated/**/*.stories.@(js|jsx|ts|tsx)'];
}

const dependencyAliases = buildDependencyAliases();

const config: StorybookConfig = {
  stories: resolveStories(),
  addons: ['@storybook/addon-essentials'],
  framework: {
    name: '@storybook/react-vite',
    options: {}
  },
  docs: {
    autodocs: false
  },
  viteFinal: async (config) => {
    const currentAllow = Array.isArray(config.server?.fs?.allow) ? config.server.fs.allow : [];
    const currentResolveAlias = config.resolve?.alias;
    const currentResolveAliasArray = Array.isArray(currentResolveAlias)
      ? currentResolveAlias
      : Object.entries(currentResolveAlias || {}).map(([find, replacement]) => ({ find, replacement }));
    const currentEsbuild = config.esbuild && typeof config.esbuild === 'object' ? config.esbuild : {};
    const mergedAlias = [...dependencyAliases, ...currentResolveAliasArray];

    config.server = {
      ...(config.server || {}),
      fs: {
        ...(config.server?.fs || {}),
        allow: Array.from(new Set([...currentAllow, workspaceRoot]))
      }
    };
    config.resolve = {
      ...(config.resolve || {}),
      alias: mergedAlias
    };
    config.esbuild = {
      ...currentEsbuild,
      jsx: 'automatic',
      jsxImportSource: 'react'
    };

    return config;
  }
};

export default config;
`;
}
function getStorybookPreviewConfigContent() {
  return `import React from 'react';
import type { Preview } from '@storybook/react';
import { addons as previewAddons } from '@storybook/preview-api';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import 'sales-frontend-design-system/core-styles';

const queryClient = new QueryClient();

const fileNameBannerStyle: React.CSSProperties = {
  position: 'sticky',
  top: 0,
  zIndex: 2147483647,
  padding: '6px 12px',
  background: '#132128',
  color: '#c2f7ea',
  fontFamily: '"SF Mono", "JetBrains Mono", monospace',
  fontSize: '12px',
  lineHeight: 1.5,
  wordBreak: 'break-all',
  borderBottom: '1px solid rgba(194, 247, 234, 0.25)'
};

function LocalServerFileNameBanner({ fileName }: { fileName: string }) {
  if (!fileName) {
    return null;
  }

  return React.createElement('div', { style: fileNameBannerStyle }, '\uD30C\uC77C: ' + fileName);
}

function LocalServerPreviewShell({ children, fileName }: { children: React.ReactNode; fileName: string }) {
  React.useEffect(() => {
    document.documentElement.lang = 'ko';
    document.body.classList.add('stg');

    return () => {
      document.body.classList.remove('stg');
    };
  }, []);

  return React.createElement(
    QueryClientProvider,
    { client: queryClient },
    React.createElement(
      'div',
      { className: 'wrap', id: 'wrap' },
      React.createElement(LocalServerFileNameBanner, { fileName }),
      children,
      React.createElement('div', { id: 'ds-portal' })
    )
  );
}

function resolveStoryFileName(context: { parameters?: Record<string, unknown> }) {
  const fileName = context && context.parameters ? context.parameters.fileName : '';

  return typeof fileName === 'string' ? fileName : '';
}

const preview: Preview = {
  decorators: [
    (Story, context) =>
      React.createElement(
        LocalServerPreviewShell,
        { fileName: resolveStoryFileName(context) },
        React.createElement(Story)
      )
  ],
  parameters: {
    layout: 'fullscreen',
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i
      }
    },
    options: {
      storySort: {
        method: 'alphabetical'
      }
    }
  }
};

// \u2500\u2500 \uB9AC\uD130\uCE58 \uC5B4\uC2DC\uC2A4\uD2B8: DOM \uC120\uD0DD \uBAA8\uB4DC \u2500\u2500
// \uB9E4\uB2C8\uC800 \uD328\uB110\uC758 "DOM \uC120\uD0DD" \uC694\uCCAD\uC744 \uBC1B\uC544 \uCE94\uBC84\uC2A4 \uC694\uC18C\uB97C hover \uD558\uC774\uB77C\uC774\uD2B8 + \uD074\uB9AD\uC73C\uB85C \uC120\uD0DD\uD558\uACE0,
// \uC120\uD0DD \uC694\uC18C\uC758 \uCD94\uC0C1 \uC124\uBA85\uC744 \uB9E4\uB2C8\uC800\uB85C \uD68C\uC2E0\uD55C\uB2E4. (\uBE0C\uB77C\uC6B0\uC800 \uC804\uC6A9 \uCF54\uB4DC)
(function setupRetouchElementPicker() {
  if (typeof document === 'undefined') {
    return;
  }

  var channel = previewAddons.getChannel();
  var picking = false;
  var hoveredElement: HTMLElement | null = null;
  var previousOutline = '';
  var retouchTargetCounter = 0;

  // \uCE69 hover \uC2DC \uAE5C\uBE61\uC784\uC5D0 \uC0AC\uC6A9\uD560 keyframe \uC2A4\uD0C0\uC77C\uC744 \uD55C \uBC88\uB9CC \uC8FC\uC785\uD55C\uB2E4. (\uACE0\uB300\uBE44 \uC0C9 + \uAE00\uB85C\uC6B0\uB85C \uB208\uC5D0 \uC798 \uB744\uAC8C)
  var flashStyle = document.createElement('style');
  flashStyle.textContent = '@keyframes juhonge-retouch-flash { 0% { outline-color: #ff1744; box-shadow: 0 0 0 1px rgba(255,23,68,0.7), 0 0 8px 3px rgba(255,23,68,0.5); } 50% { outline-color: #ffea00; box-shadow: 0 0 0 2px rgba(255,234,0,0.9), 0 0 14px 5px rgba(255,234,0,0.6); } 100% { outline-color: #ff1744; box-shadow: 0 0 0 1px rgba(255,23,68,0.7), 0 0 8px 3px rgba(255,23,68,0.5); } } .juhonge-retouch-flash { outline: 2px solid #ff1744 !important; outline-offset: 2px; border-radius: 2px; animation: juhonge-retouch-flash 0.8s ease-in-out infinite; }';
  document.head.appendChild(flashStyle);

  function clearHighlight() {
    if (hoveredElement) {
      hoveredElement.style.outline = previousOutline;
      hoveredElement = null;
    }
  }

  // CSS module \uD574\uC2DC \uD074\uB798\uC2A4(_name_hash_line)\uB294 \uC77D\uAE30 \uC26C\uC6B4 name \uC73C\uB85C, emotion(css-xxx)\uC740 \uC81C\uAC70\uD55C\uB2E4.
  function toReadableClassName(token: string): string {
    if (!token || token.indexOf('css-') === 0) { return ''; }
    var moduleMatch = token.match(/^_(.+)_[a-z0-9]+_[0-9]+$/);
    return moduleMatch && moduleMatch[1] ? moduleMatch[1] : token;
  }

  function buildReadableClasses(classValue: string): string {
    return classValue
      .split(' ')
      .map(toReadableClassName)
      .filter(function (token) { return token; })
      .slice(0, 5)
      .join(' ');
  }

  // \uC120\uD0DD \uC694\uC18C\uC5D0 data-retouch-target-N \uB9C8\uD0B9 + \uC9C0\uC18D \uD558\uC774\uB77C\uC774\uD2B8\uB97C \uB0A8\uAE30\uACE0, \uD1A0\uD070/\uC124\uBA85\uC744 \uB9CC\uB4E0\uB2E4.
  function markAndDescribe(element: HTMLElement) {
    retouchTargetCounter += 1;
    var index = retouchTargetCounter;
    var tag = element.tagName.toLowerCase();
    var classes = buildReadableClasses(element.getAttribute('class') || '');
    var text = (element.textContent || '').trim();
    text = text.split(String.fromCharCode(10)).join(' ').slice(0, 40);

    element.setAttribute('data-retouch-target-' + index, 'true');
    element.style.outline = '2px solid #d97706';

    var descriptorParts = ['<' + tag + '>'];
    if (classes) { descriptorParts.push('class="' + classes + '"'); }
    if (text) { descriptorParts.push('text="' + text + '"'); }

    return {
      descriptor: descriptorParts.join(' '),
      index: index,
      token: '[' + tag + '-dom-\uD0C0\uAC9F' + index + ']'
    };
  }

  // \uD2B9\uC815 \uC120\uD0DD \uB300\uC0C1(data-retouch-target-N)\uC758 \uB9C8\uD0B9/\uD14C\uB450\uB9AC\uB97C \uC81C\uAC70\uD55C\uB2E4.
  function clearTargetMark(index: number) {
    var marked = document.querySelector('[data-retouch-target-' + index + ']') as HTMLElement | null;
    if (marked) {
      marked.classList.remove('juhonge-retouch-flash');
      marked.style.outline = '';
      marked.removeAttribute('data-retouch-target-' + index);
    }
  }

  // \uCE69 hover \uC2DC \uD574\uB2F9 \uB300\uC0C1\uC758 \uD14C\uB450\uB9AC\uB97C \uAE5C\uBE61\uC774\uACE0 \uD654\uBA74\uC5D0 \uBCF4\uC774\uB3C4\uB85D \uC2A4\uD06C\uB864\uD55C\uB2E4.
  function flashTargetMark(index: number, active: boolean) {
    var marked = document.querySelector('[data-retouch-target-' + index + ']') as HTMLElement | null;
    if (!marked) {
      return;
    }
    if (active) {
      marked.classList.add('juhonge-retouch-flash');
      marked.scrollIntoView({ behavior: 'smooth', block: 'center' });
    } else {
      marked.classList.remove('juhonge-retouch-flash');
    }
  }

  function onMouseMove(event: MouseEvent) {
    if (!picking) { return; }
    var target = event.target as HTMLElement | null;
    if (!target || target === hoveredElement) { return; }
    clearHighlight();
    hoveredElement = target;
    previousOutline = target.style.outline;
    target.style.outline = '2px solid #115e59';
  }

  function stopPicking() {
    picking = false;
    clearHighlight();
    document.removeEventListener('mousemove', onMouseMove, true);
    document.removeEventListener('click', onClick, true);
    document.removeEventListener('keydown', onKeyDown, true);
  }

  function onClick(event: MouseEvent) {
    if (!picking) { return; }
    event.preventDefault();
    event.stopPropagation();
    var target = event.target as HTMLElement | null;
    stopPicking();
    if (target) {
      channel.emit('${RETOUCH_CHANNEL_PICK_RESULT}', markAndDescribe(target));
    }
  }

  function onKeyDown(event: KeyboardEvent) {
    if (picking && event.key === 'Escape') {
      stopPicking();
      channel.emit('${RETOUCH_CHANNEL_PICK_CANCEL}');
    }
  }

  channel.on('${RETOUCH_CHANNEL_PICK_START}', function () {
    if (picking) { return; }
    picking = true;
    document.addEventListener('mousemove', onMouseMove, true);
    document.addEventListener('click', onClick, true);
    document.addEventListener('keydown', onKeyDown, true);
  });

  channel.on('${RETOUCH_CHANNEL_PICK_CANCEL}', stopPicking);
  channel.on('${RETOUCH_CHANNEL_CLEAR_TARGET}', function (index: number) {
    clearTargetMark(index);
  });
  channel.on('${RETOUCH_CHANNEL_FLASH_TARGET}', function (payload: { index: number; active: boolean }) {
    if (payload) {
      flashTargetMark(payload.index, payload.active);
    }
  });
})();

export default preview;
`;
}
function getStorybookManagerConfigContent() {
  return `import React from 'react';
import { addons, types, useChannel, useStorybookApi } from '@storybook/manager-api';
import { AddonPanel } from '@storybook/components';

var PORT_CANDIDATES = [${RETOUCH_PORT_CANDIDATES.join(", ")}];

function RetouchPanel() {
  var api = useStorybookApi();
  var lastImportPathRef = React.useRef('');
  var storyData = api.getCurrentStoryData();
  if (storyData && storyData.importPath) {
    lastImportPathRef.current = storyData.importPath;
  }
  var currentStoryId = storyData && storyData.id ? storyData.id : (storyData && storyData.importPath ? storyData.importPath : '');
  var lastStoryIdRef = React.useRef(currentStoryId);
  var promptRef = React.useRef<HTMLTextAreaElement | null>(null);

  var baseUrlState = React.useState<string | null>(null);
  var baseUrl = baseUrlState[0];
  var setBaseUrl = baseUrlState[1];
  var promptState = React.useState('');
  var prompt = promptState[0];
  var setPrompt = promptState[1];
  var busyState = React.useState(false);
  var busy = busyState[0];
  var setBusy = busyState[1];
  var pickingState = React.useState(false);
  var picking = pickingState[0];
  var setPicking = pickingState[1];
  var messageState = React.useState('');
  var message = messageState[0];
  var setMessage = messageState[1];
  var filesState = React.useState<Array<{ relativePath: string; absolutePath: string; before: string; after: string }>>([]);
  var files = filesState[0];
  var setFiles = filesState[1];
  var targetsState = React.useState<Array<{ token: string; descriptor: string; index: number }>>([]);
  var targets = targetsState[0];
  var setTargets = targetsState[1];
  var logState = React.useState('');
  var log = logState[0];
  var setLog = logState[1];

  var emit = useChannel({
    '${RETOUCH_CHANNEL_PICK_RESULT}': function (payload: { descriptor?: string; token?: string; index?: number } | string) {
      setPicking(false);
      // \uAD6C\uBC84\uC804(\uBB38\uC790\uC5F4) / \uC2E0\uBC84\uC804(\uAC1D\uCCB4) payload\uB97C \uBAA8\uB450 \uD5C8\uC6A9\uD55C\uB2E4.
      var token = typeof payload === 'string' ? payload : (payload && payload.token) || '';
      var descriptor = typeof payload === 'string' ? '' : (payload && payload.descriptor) || '';
      var index = typeof payload === 'string' ? 0 : (payload && typeof payload.index === 'number' ? payload.index : 0);
      if (token) {
        setPrompt(function (current) { return current ? current + ' ' + token : token; });
        setTargets(function (current) { return current.concat([{ token: token, descriptor: descriptor, index: index }]); });
        setMessage('\uC120\uD0DD\uB428: ' + token);
      }
    },
    '${RETOUCH_CHANNEL_PICK_CANCEL}': function () { setPicking(false); }
  });

  // \uCE69 hover \uC2DC \uC88C\uCE21\uC5D0\uC11C \uD574\uB2F9 DOM \uD14C\uB450\uB9AC\uB97C \uAE5C\uBE61\uC778\uB2E4.
  function handleFlashTarget(index, active) {
    emit('${RETOUCH_CHANNEL_FLASH_TARGET}', { index: index, active: active });
  }

  // \uD1A0\uD070\uC744 \uD604\uC7AC \uCEE4\uC11C \uC704\uCE58\uC5D0 \uC0BD\uC785\uD55C\uB2E4. (\uCEE4\uC11C \uC815\uBCF4\uAC00 \uC5C6\uC73C\uBA74 \uB05D\uC5D0 append, \uC911\uBCF5 \uD5C8\uC6A9)
  function handleReinsertTarget(token) {
    var element = promptRef.current;
    if (!element || typeof element.selectionStart !== 'number') {
      setPrompt(function (current) { return current ? current + ' ' + token : token; });
      setMessage('\uD1A0\uD070\uC744 \uD504\uB86C\uD504\uD2B8\uC5D0 \uB123\uC5C8\uC2B5\uB2C8\uB2E4: ' + token);
      return;
    }

    var value = element.value;
    var start = element.selectionStart;
    var end = element.selectionEnd;
    var before = value.slice(0, start);
    var after = value.slice(end);
    var needSpaceBefore = before.length > 0 && before.charAt(before.length - 1) !== ' ';
    var needSpaceAfter = after.length > 0 && after.charAt(0) !== ' ';
    var inserted = (needSpaceBefore ? ' ' : '') + token + (needSpaceAfter ? ' ' : '');
    var nextValue = before + inserted + after;
    var caret = (before + inserted).length;

    setPrompt(nextValue);
    setMessage('\uD1A0\uD070\uC744 \uCEE4\uC11C \uC704\uCE58\uC5D0 \uB123\uC5C8\uC2B5\uB2C8\uB2E4: ' + token);
    window.requestAnimationFrame(function () {
      if (promptRef.current) {
        promptRef.current.focus();
        promptRef.current.setSelectionRange(caret, caret);
      }
    });
  }

  // \uC120\uD0DD \uB300\uC0C1 \uC81C\uAC70: \uC88C\uCE21 \uB9C8\uD0B9/\uD14C\uB450\uB9AC \uC81C\uAC70 + \uD504\uB86C\uD504\uD2B8\uC758 \uD1A0\uD070 \uC81C\uAC70 + \uBAA9\uB85D\uC5D0\uC11C \uC81C\uAC70.
  function handleRemoveTarget(target) {
    emit('${RETOUCH_CHANNEL_CLEAR_TARGET}', target.index);
    setPrompt(function (current) { return current.split(target.token).join(' ').split(' ').filter(Boolean).join(' '); });
    setTargets(function (current) { return current.filter(function (item) { return item.index !== target.index; }); });
    setMessage('\uC120\uD0DD \uD574\uC81C: ' + target.token);
  }

  React.useEffect(function () {
    var cancelled = false;
    (async function () {
      for (var i = 0; i < PORT_CANDIDATES.length; i += 1) {
        var candidate = 'http://127.0.0.1:' + PORT_CANDIDATES[i];
        try {
          var res = await fetch(candidate + '/api/retouch/health', { cache: 'no-store' });
          if (res.ok) {
            if (!cancelled) { setBaseUrl(candidate); }
            return;
          }
        } catch (error) { /* \uB2E4\uC74C \uD6C4\uBCF4 \uC2DC\uB3C4 */ }
      }
      if (!cancelled) { setMessage('\uB9AC\uD130\uCE58 \uC11C\uBC84\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. ju-hong-e\uC5D0\uC11C /retouch-start \uB97C \uC2E4\uD589\uD558\uC138\uC694.'); }
    })();
    return function () { cancelled = true; };
  }, []);

  // \uC2A4\uD1A0\uB9AC\uAC00 \uBC14\uB00C\uBA74 \uC774\uC804 \uC2A4\uD1A0\uB9AC\uC5D0 \uBB36\uC778 \uC120\uD0DD/\uBCC0\uACBD\uC548\uC744 \uCD08\uAE30\uD654\uD55C\uB2E4.
  // (\uC88C\uCE21 \uB9C8\uD0B9\uC740 \uC2A4\uD1A0\uB9AC \uC7AC\uB80C\uB354\uB85C \uC0AC\uB77C\uC9C0\uBBC0\uB85C, \uD328\uB110\uC758 \uCE69/diff/\uB85C\uADF8\uB9CC \uC815\uB9AC\uD558\uBA74 \uC77C\uAD00\uC131\uC774 \uB9DE\uB294\uB2E4)
  React.useEffect(function () {
    if (lastStoryIdRef.current === currentStoryId) {
      return;
    }

    var previousStoryId = lastStoryIdRef.current;

    // \uC120\uD0DD\uD55C DOM(\uCE69) \uB610\uB294 \uC544\uC9C1 \uC801\uC6A9/\uCDE8\uC18C\uD558\uC9C0 \uC54A\uC740 diff\uAC00 \uC788\uC73C\uBA74 \uC774\uB3D9 \uC804\uC5D0 \uD655\uC778\uD55C\uB2E4.
    // diff(files)\uB294 AI\uAC00 \uC774\uBBF8 \uB514\uC2A4\uD06C\uC5D0 \uC4F4 \uBCC0\uACBD\uC758 \uC6D0\uBCF5(before \uC2A4\uB0C5\uC0F7)\uC744 \uD328\uB110 state\uB85C\uB9CC \uB4E4\uACE0 \uC788\uC73C\uBBC0\uB85C,
    // \uD655\uC778 \uC5C6\uC774 \uD3D0\uAE30\uD558\uBA74 [\uCDE8\uC18C(\uB418\uB3CC\uB9AC\uAE30)] \uACBD\uB85C\uAC00 \uC0AC\uB77C\uC9C4\uB2E4. (send \uC131\uACF5 \uC9C1\uD6C4\uC5D4 targets\uAC00 \uBE44\uACE0 files\uB9CC \uB0A8\uB294\uB2E4)
    var hasPendingTargets = targets.length > 0;
    var hasPendingDiff = files.length > 0;
    if (hasPendingTargets || hasPendingDiff) {
      var confirmMessage = hasPendingDiff
        ? '\uC544\uC9C1 \uC801\uC6A9/\uCDE8\uC18C\uD558\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC548(diff)\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uB2E4\uB978 \uC2A4\uD1A0\uB9AC\uB85C \uC774\uB3D9\uD558\uBA74 diff\uAC00 \uC0AC\uB77C\uC838 [\uCDE8\uC18C(\uB418\uB3CC\uB9AC\uAE30)]\uB85C \uC6D0\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774\uB3D9\uD560\uAE4C\uC694?'
        : '\uC120\uD0DD\uD55C DOM\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uB2E4\uB978 \uC2A4\uD1A0\uB9AC\uB85C \uC774\uB3D9\uD558\uBA74 \uC120\uD0DD/\uBCC0\uACBD\uC548\uC774 \uCD08\uAE30\uD654\uB429\uB2C8\uB2E4. \uC774\uB3D9\uD560\uAE4C\uC694?';
      var proceed = window.confirm(confirmMessage);
      if (!proceed) {
        if (previousStoryId) {
          api.selectStory(previousStoryId);
        }
        return;
      }
      // \uCE69 [x] \uC640 \uB3D9\uC77C\uD558\uAC8C: \uC88C\uCE21 \uB9C8\uD0B9 \uC81C\uAC70 + \uD504\uB86C\uD504\uD2B8\uC758 \uD1A0\uD070 \uC81C\uAC70.
      targets.forEach(function (target) { emit('${RETOUCH_CHANNEL_CLEAR_TARGET}', target.index); });
      setPrompt(function (current) {
        var next = current;
        targets.forEach(function (target) { next = next.split(target.token).join(' '); });
        return next.split(' ').filter(Boolean).join(' ');
      });
      setMessage('\uB2E4\uB978 \uC2A4\uD1A0\uB9AC\uB85C \uC774\uB3D9\uD574 \uC120\uD0DD\uD55C DOM \uBC0F \uBCC0\uACBD\uC548\uC744 \uCD08\uAE30\uD654\uD588\uC2B5\uB2C8\uB2E4.');
    }

    lastStoryIdRef.current = currentStoryId;
    setTargets([]);
    setFiles([]);
    setLog('');
  }, [currentStoryId]);

  function handleStartPick() {
    setPicking(true);
    setMessage('\uC88C\uCE21 \uD654\uBA74\uC5D0\uC11C \uC694\uC18C\uB97C \uD074\uB9AD\uD558\uC138\uC694. (Esc \uCDE8\uC18C)');
    emit('${RETOUCH_CHANNEL_PICK_START}');
  }

  async function handleSend() {
    if (!baseUrl) { setMessage('\uB9AC\uD130\uCE58 \uC11C\uBC84 \uC5F0\uACB0\uC744 \uCC3E\uB294 \uC911\uC785\uB2C8\uB2E4.'); return; }
    if (!prompt.trim()) { setMessage('\uD504\uB86C\uD504\uD2B8\uB97C \uC785\uB825\uD558\uC138\uC694.'); return; }
    var importPath = lastImportPathRef.current;
    if (!importPath) { setMessage('\uB300\uC0C1 \uC2A4\uD1A0\uB9AC\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC2A4\uD1A0\uB9AC\uB97C \uBA3C\uC800 \uC120\uD0DD\uD558\uC138\uC694.'); return; }
    setBusy(true);
    setMessage('AI\uAC00 \uD3B8\uC9D1 \uC911\uC785\uB2C8\uB2E4\u2026 (\uC2E4\uC2DC\uAC04 \uB85C\uADF8)');
    setFiles([]);
    setLog('');

    try {
      var res = await fetch(baseUrl + '/api/retouch', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ importPath: importPath, prompt: prompt, selectedElements: targets })
      });

      if (!res.ok) {
        // 400/409 \uB4F1\uC740 JSON \uC73C\uB85C \uB0B4\uB824\uC628\uB2E4.
        var errText = await res.text();
        var errMessage = errText;
        try { var parsed = JSON.parse(errText); errMessage = parsed && parsed.message ? parsed.message : errText; } catch (ignore) { /* \uD3C9\uBB38 */ }
        setMessage('\uC694\uCCAD \uC2E4\uD328: ' + errMessage);
        return;
      }

      if (!res.body) {
        // \uC2A4\uD2B8\uB9BC \uBBF8\uC9C0\uC6D0 \uD658\uACBD \uD3F4\uBC31
        var fallbackText = await res.text();
        handleStreamComplete(fallbackText);
        return;
      }

      var reader = res.body.getReader();
      var decoder = new TextDecoder();
      var buffer = '';

      while (true) {
        var step = await reader.read();
        if (step.done) { break; }
        buffer += decoder.decode(step.value, { stream: true });
        // \uACB0\uACFC \uB9C8\uCEE4 \uC774\uC804\uAE4C\uC9C0\uB294 \uC9C4\uD589 \uB85C\uADF8\uB85C \uD45C\uC2DC\uD55C\uB2E4.
        var markerIndex = buffer.indexOf('${RETOUCH_RESULT_MARKER}');
        var errorIndex = buffer.indexOf('${RETOUCH_ERROR_MARKER}');
        var cut = markerIndex >= 0 ? markerIndex : (errorIndex >= 0 ? errorIndex : buffer.length);
        setLog(buffer.slice(0, cut));
      }

      handleStreamComplete(buffer);
    } catch (error) {
      setMessage(error instanceof Error ? error.message : String(error));
    } finally {
      setBusy(false);
    }
  }

  function handleStreamComplete(buffer: string) {
    var resultIndex = buffer.indexOf('${RETOUCH_RESULT_MARKER}');
    var errorIndex = buffer.indexOf('${RETOUCH_ERROR_MARKER}');

    if (errorIndex >= 0) {
      setLog(buffer.slice(0, errorIndex));
      try {
        var errorPayload = JSON.parse(buffer.slice(errorIndex + '${RETOUCH_ERROR_MARKER}'.length).trim());
        setMessage('\uC2E4\uD328: ' + (errorPayload && errorPayload.message ? errorPayload.message : '\uC54C \uC218 \uC5C6\uB294 \uC624\uB958'));
      } catch (parseError) {
        setMessage('\uC2E4\uD328: \uC751\uB2F5 \uD30C\uC2F1 \uC624\uB958');
      }
      return;
    }

    if (resultIndex >= 0) {
      setLog(buffer.slice(0, resultIndex));
      try {
        var result = JSON.parse(buffer.slice(resultIndex + '${RETOUCH_RESULT_MARKER}'.length).trim());
        setFiles(result.files || []);
        // \uC804\uC1A1\uC774 \uB05D\uB098\uBA74 \uC120\uD0DD \uB9C8\uD0B9\uC744 \uC815\uB9AC\uD55C\uB2E4. (HMR \uC7AC\uB80C\uB354\uB85C\uB3C4 \uC0AC\uB77C\uC9C0\uC9C0\uB9CC \uC989\uC2DC \uC815\uB9AC)
        targets.forEach(function (target) { emit('${RETOUCH_CHANNEL_CLEAR_TARGET}', target.index); });
        setTargets([]);
        if (!result.files || result.files.length === 0) {
          setMessage('\uBCC0\uACBD\uB41C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. (AI\uAC00 \uC218\uC815\uD558\uC9C0 \uC54A\uC558\uAC70\uB098 \uB3D9\uC77C)');
        } else {
          setMessage('AI\uAC00 \uC218\uC815\uC548\uC744 \uC88C\uCE21 \uD654\uBA74\uC5D0 \uBC18\uC601\uD588\uC2B5\uB2C8\uB2E4. \uC544\uB798 diff\uB97C \uD655\uC778\uD558\uACE0 [\uC801\uC6A9](\uC720\uC9C0) \uB610\uB294 [\uCDE8\uC18C](\uB418\uB3CC\uB9AC\uAE30)\uD558\uC138\uC694.');
        }
      } catch (parseError) {
        setMessage('\uACB0\uACFC \uD30C\uC2F1 \uC624\uB958');
      }
      return;
    }

    setMessage('\uC751\uB2F5\uC744 \uBC1B\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.');
  }

  function handleEditAfter(index: number, value: string) {
    setFiles(function (current) {
      return current.map(function (file, i) { return i === index ? Object.assign({}, file, { after: value }) : file; });
    });
  }

  // \uC801\uC6A9/\uB418\uB3CC\uB9AC\uAE30 \uBAA8\uB450 \uAC19\uC740 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uB97C \uC4F0\uB418, \uC801\uC6A9\uC740 after(\uD3B8\uC9D1\uBCF8)\uB97C, \uB418\uB3CC\uB9AC\uAE30\uB294 before(\uC6D0\uBCF8)\uB97C \uB514\uC2A4\uD06C\uC5D0 \uC4F4\uB2E4.
  async function writeFiles(useBefore: boolean, doneMessage: string) {
    if (!baseUrl || files.length === 0) { return; }
    setBusy(true);
    setMessage(useBefore ? '\uC6D0\uBCF8\uC73C\uB85C \uB418\uB3CC\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4\u2026' : '\uC801\uC6A9 \uC911\uC785\uB2C8\uB2E4\u2026');
    try {
      var res = await fetch(baseUrl + '/api/retouch/apply', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          files: files.map(function (file) { return { absolutePath: file.absolutePath, content: useBefore ? file.before : file.after }; })
        })
      });
      var body = await res.json();
      if (!res.ok) { throw new Error(body && body.message ? body.message : '\uC694\uCCAD \uC2E4\uD328'); }
      setMessage(doneMessage);
      setFiles([]);
    } catch (error) {
      setMessage(error instanceof Error ? error.message : String(error));
    } finally {
      setBusy(false);
    }
  }

  function handleApply() {
    writeFiles(false, '\uC801\uC6A9 \uC644\uB8CC \u2014 \uC88C\uCE21 \uD654\uBA74\uC774 \uAC31\uC2E0\uB429\uB2C8\uB2E4.');
  }

  function handleRevert() {
    writeFiles(true, '\uC6D0\uBCF8\uC73C\uB85C \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4 \u2014 \uC88C\uCE21 \uD654\uBA74\uC774 \uC6D0\uC0C1\uBCF5\uAD6C\uB429\uB2C8\uB2E4.');
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', padding: '12px', gap: '10px', boxSizing: 'border-box' }}>
      <div style={{ fontSize: '12px', color: baseUrl ? '#027a48' : '#b42318' }}>
        {baseUrl ? '\uB9AC\uD130\uCE58 \uC11C\uBC84 \uC5F0\uACB0\uB428' : '\uB9AC\uD130\uCE58 \uC11C\uBC84 \uBBF8\uC5F0\uACB0'}
      </div>
      <div style={{ flex: 1, overflow: 'auto', display: 'flex', flexDirection: 'column', gap: '10px' }}>
        {message ? <div style={{ fontSize: '13px', color: '#1e252b', lineHeight: 1.5 }}>{message}</div> : null}
        {log ? (
          <pre style={{ margin: 0, maxHeight: '220px', overflow: 'auto', background: '#0f1720', color: '#c2f7ea', padding: '10px', borderRadius: '8px', whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'monospace', fontSize: '12px', lineHeight: 1.5 }}>{log}</pre>
        ) : null}
        {files.map(function (file, index) {
          return (
            <div key={file.absolutePath} style={{ border: '1px solid #d0d7de', borderRadius: '8px', padding: '8px' }}>
              <div style={{ fontSize: '12px', fontWeight: 700, marginBottom: '6px', wordBreak: 'break-all' }}>{file.relativePath}</div>
              <div style={{ fontSize: '11px', color: '#6e7781', marginBottom: '4px' }}>\uBCC0\uACBD \uD6C4 (\uD3B8\uC9D1 \uAC00\uB2A5)</div>
              <textarea
                value={file.after}
                onChange={function (event) { handleEditAfter(index, event.target.value); }}
                spellCheck={false}
                style={{ width: '100%', minHeight: '160px', fontFamily: 'monospace', fontSize: '12px', boxSizing: 'border-box' }}
              />
              <details style={{ marginTop: '6px' }}>
                <summary style={{ fontSize: '12px', cursor: 'pointer', color: '#6e7781' }}>\uBCC0\uACBD \uC804(\uC6D0\uBCF8) \uBCF4\uAE30</summary>
                <textarea
                  value={file.before}
                  readOnly
                  spellCheck={false}
                  style={{ width: '100%', minHeight: '120px', marginTop: '4px', fontFamily: 'monospace', fontSize: '12px', boxSizing: 'border-box', background: '#f6f8fa' }}
                />
              </details>
            </div>
          );
        })}
        {files.length > 0 ? (
          <div style={{ display: 'flex', gap: '8px' }}>
            <button type="button" disabled={busy} onClick={handleApply} style={{ padding: '6px 12px', cursor: 'pointer', fontWeight: 700 }}>\uC801\uC6A9(\uC720\uC9C0)</button>
            <button type="button" disabled={busy} onClick={handleRevert} style={{ padding: '6px 12px', cursor: 'pointer' }}>\uCDE8\uC18C(\uB418\uB3CC\uB9AC\uAE30)</button>
          </div>
        ) : null}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', borderTop: '1px solid #d0d7de', paddingTop: '10px' }}>
        <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
          <button type="button" onClick={handleStartPick} disabled={picking} style={{ padding: '4px 10px', cursor: 'pointer' }}>
            {picking ? 'DOM \uC120\uD0DD \uC911\u2026' : 'DOM \uC120\uD0DD'}
          </button>
        </div>
        {targets.length > 0 ? (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
            {targets.map(function (target) {
              return (
                <span key={target.index} onMouseEnter={function () { handleFlashTarget(target.index, true); }} onMouseLeave={function () { handleFlashTarget(target.index, false); }} style={{ display: 'inline-flex', alignItems: 'center', gap: '4px', padding: '3px 4px 3px 8px', borderRadius: '14px', background: 'rgba(217, 119, 6, 0.12)', border: '1px solid rgba(217, 119, 6, 0.4)', fontSize: '12px' }}>
                  <span style={{ fontFamily: 'monospace' }}>{target.token}</span>
                  <button type="button" title="\uC774 \uD1A0\uD070\uC744 \uD504\uB86C\uD504\uD2B8\uC5D0 \uB2E4\uC2DC \uC0BD\uC785" onClick={function () { handleReinsertTarget(target.token); }} style={{ padding: '1px 6px', cursor: 'pointer', borderRadius: '10px' }}>\uC7AC\uC0BD\uC785</button>
                  <button type="button" title="\uC120\uD0DD \uD574\uC81C (\uC88C\uCE21 \uD14C\uB450\uB9AC \uC81C\uAC70)" onClick={function () { handleRemoveTarget(target); }} style={{ padding: '1px 7px', cursor: 'pointer', borderRadius: '10px' }}>\u2715</button>
                </span>
              );
            })}
          </div>
        ) : null}
        <textarea
          ref={promptRef}
          value={prompt}
          onChange={function (event) { setPrompt(event.target.value); }}
          placeholder="\uC608) \uC774 \uBC84\uD2BC \uBC30\uACBD\uC744 \uBE68\uAC04\uC0C9\uC73C\uB85C \uBC14\uAFD4\uC918"
          style={{ width: '100%', minHeight: '70px', fontSize: '13px', boxSizing: 'border-box' }}
        />
        <button type="button" onClick={handleSend} disabled={busy} style={{ padding: '8px 12px', cursor: 'pointer', fontWeight: 700 }}>
          {busy ? '\uC2E4\uD589 \uC911\u2026' : '\uC804\uC1A1'}
        </button>
      </div>
    </div>
  );
}

addons.register('juhonge/retouch', function () {
  addons.add('juhonge/retouch/panel', {
    type: types.PANEL,
    title: '\uB9AC\uD130\uCE58',
    match: function (state) { return state.viewMode === 'story'; },
    render: function (renderOptions) {
      return (
        <AddonPanel active={Boolean(renderOptions.active)}>
          <RetouchPanel />
        </AddonPanel>
      );
    }
  });
});
`;
}

// src/features/gen-html/infrastructure/local-server/index.ts
function writeTextFileIfChanged(filePath, content) {
  const previousContent = fs10.existsSync(filePath) ? fs10.readFileSync(filePath, "utf8") : "";
  if (previousContent === content) {
    return false;
  }
  fs10.writeFileSync(filePath, content, "utf8");
  return true;
}
function ensureLocalServerSetup(paths, trace, outputDirectory) {
  const localServerPaths = getLocalServerPaths(paths);
  const outputDirectories = Array.isArray(outputDirectory) ? outputDirectory : outputDirectory ? [outputDirectory] : [];
  const runtimeDependencies = resolveLocalServerRuntimeDependencies(paths, localServerPaths.directoryPath, trace);
  const generatedDependencies = outputDirectories.reduce((accumulator, currentOutputDirectory) => {
    if (!fs10.existsSync(currentOutputDirectory)) {
      return accumulator;
    }
    return {
      ...accumulator,
      ...resolveGeneratedOutputDependencies(paths, localServerPaths.directoryPath, currentOutputDirectory, trace)
    };
  }, {});
  fs10.mkdirSync(localServerPaths.directoryPath, { recursive: true });
  fs10.mkdirSync(localServerPaths.generatedDirectoryPath, { recursive: true });
  fs10.mkdirSync(localServerPaths.multipleDirectoryPath, { recursive: true });
  fs10.mkdirSync(localServerPaths.storybookConfigDirectoryPath, { recursive: true });
  removeLegacyLocalServerFiles(localServerPaths, trace);
  ensureLocalServerPackageCustomFile(paths, trace);
  const packageCustom = readLocalServerPackageCustom(paths, trace);
  trace("local-server:setup:dir", localServerPaths.directoryPath);
  let packageJson = mergeLocalServerPackageJson(void 0, generatedDependencies);
  if (fs10.existsSync(localServerPaths.packageJsonPath)) {
    try {
      packageJson = mergeLocalServerPackageJson(readJsonFile(localServerPaths.packageJsonPath), generatedDependencies);
    } catch (error) {
      throw new Error(`\uB85C\uCEEC \uD504\uB9AC\uBDF0 \uC11C\uBC84 package.json\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. ${getErrorSummary(error)}`);
    }
  }
  packageJson = {
    ...packageJson,
    dependencies: {
      ...normalizeDependencies(packageJson.dependencies),
      ...runtimeDependencies
    }
  };
  packageJson = applyLocalServerPackageCustomManifest(packageJson, packageCustom);
  const normalizedPackageJson = {
    ...packageJson,
    dependencies: toSortedDependencies(normalizeDependencies(packageJson.dependencies))
  };
  const nextPackageJsonContent = `${JSON.stringify(normalizedPackageJson, null, 2)}
`;
  const previousPackageJsonContent = fs10.existsSync(localServerPaths.packageJsonPath) ? fs10.readFileSync(localServerPaths.packageJsonPath, "utf8") : "";
  const packageJsonChanged = previousPackageJsonContent !== nextPackageJsonContent;
  if (packageJsonChanged) {
    fs10.writeFileSync(localServerPaths.packageJsonPath, nextPackageJsonContent, "utf8");
    trace("local-server:setup:package-json-updated", localServerPaths.packageJsonPath);
  }
  const storybookConfigChanged = [
    writeTextFileIfChanged(localServerPaths.storybookMainConfigPath, getStorybookMainConfigContent()),
    writeTextFileIfChanged(localServerPaths.storybookManagerConfigPath, getStorybookManagerConfigContent()),
    writeTextFileIfChanged(localServerPaths.storybookPreviewConfigPath, getStorybookPreviewConfigContent())
  ].some(Boolean);
  const auxiliaryConfigChanged = [
    writeTextFileIfChanged(localServerPaths.scssModuleDeclarationPath, getScssModuleDeclarationContent()),
    writeTextFileIfChanged(localServerPaths.tsconfigPath, getLocalServerTsconfigContent())
  ].some(Boolean);
  setLocalServerPreviewTarget(paths);
  trace(
    "local-server:setup:files",
    `${localServerPaths.storybookConfigDirectoryPath} storybookConfigChanged=${storybookConfigChanged} auxiliaryConfigChanged=${auxiliaryConfigChanged}`
  );
  ensureLocalServerDependencies(paths, trace, packageJsonChanged);
  installRequiredPublishedDependencies(paths, trace);
  const finalPackageJson = readJsonFile(localServerPaths.packageJsonPath);
  const finalDependencies = finalPackageJson ? normalizeDependencies(finalPackageJson.dependencies) : normalizeDependencies(normalizedPackageJson.dependencies);
  return {
    dependencies: toSortedDependencies(finalDependencies),
    packageJsonChanged,
    packageJsonPath: localServerPaths.packageJsonPath,
    storybookConfigChanged
  };
}
async function resolveReusableLocalServerRuntime(paths, trace) {
  const localServerPaths = getLocalServerPaths(paths);
  const state = readLocalServerState(paths);
  const candidatePorts = [
    state.port,
    ...findLocalServerListeningProcesses(localServerPaths.directoryPath).map((processInfo) => processInfo.port)
  ].filter((port) => typeof port === "number" && Number.isInteger(port) && port > 0);
  const uniquePorts = Array.from(new Set(candidatePorts));
  for (const port of uniquePorts) {
    const index = await requestJson(getHealthUrl(port));
    if (!index || !isRecord(index.entries)) {
      continue;
    }
    const listeningProcess = findLocalServerListeningProcesses(localServerPaths.directoryPath).find(
      (processInfo) => processInfo.port === port
    );
    updateLocalServerState(paths, {
      health: "running",
      pid: listeningProcess?.pid ?? state.pid,
      port,
      restartRequired: Boolean(state.restartRequired)
    });
    trace("local-server:reuse", `port=${port} pid=${listeningProcess?.pid ?? state.pid ?? "(unknown)"}`);
    return {
      alreadyRunning: true,
      baseUrl: getLocalServerBaseUrl(port),
      logPath: localServerPaths.logPath,
      port,
      skipped: false
    };
  }
  if (state.port || state.pid || state.health === "running") {
    updateLocalServerState(paths, {
      health: "stale"
    });
    trace("local-server:reuse:stale", `port=${state.port ?? "(none)"} pid=${state.pid ?? "(none)"}`);
  }
  return void 0;
}
async function startLocalServerProcess(options, preferredPort) {
  const localServerPaths = getLocalServerPaths(options.paths);
  const port = await findAvailablePort(preferredPort);
  const executablePath = getStorybookExecutablePath(localServerPaths.directoryPath);
  const logFileDescriptor = fs10.openSync(localServerPaths.logPath, "a");
  const child = spawn(
    executablePath,
    ["dev", "-c", "./.storybook", "--ci", "--host", LOCAL_SERVER_HOST, "--port", String(port)],
    {
      cwd: localServerPaths.directoryPath,
      detached: true,
      env: {
        ...process.env,
        STORYBOOK_DISABLE_TELEMETRY: "1"
      },
      shell: process.platform === "win32",
      stdio: ["ignore", logFileDescriptor, logFileDescriptor]
    }
  );
  child.unref();
  fs10.closeSync(logFileDescriptor);
  options.trace("local-server:start", `pid=${child.pid} port=${port}`);
  try {
    await waitUntilServerIsReady({ port });
  } catch (error) {
    updateLocalServerState(options.paths, {
      health: "stale",
      pid: child.pid,
      port
    });
    throw new Error(`${getErrorSummary(error)} \uB85C\uADF8: ${localServerPaths.logPath}`);
  }
  writeLocalServerState(options.paths, {
    dirty: false,
    health: "running",
    pid: child.pid,
    port,
    restartRequired: false,
    startedAt: getHumanReadableNow()
  });
  return {
    alreadyRunning: false,
    baseUrl: getLocalServerBaseUrl(port),
    logPath: localServerPaths.logPath,
    port,
    skipped: false
  };
}
async function ensureLocalServerRunning(options) {
  const localServerPaths = getLocalServerPaths(options.paths);
  const state = readLocalServerState(options.paths);
  const preferredPort = state.port || DEFAULT_LOCAL_SERVER_PORT;
  if (options.parsedArgs.testMode) {
    options.trace("local-server:skip:test-mode", String(preferredPort));
    return {
      alreadyRunning: false,
      baseUrl: getLocalServerBaseUrl(preferredPort),
      logPath: localServerPaths.logPath,
      port: preferredPort,
      skipped: true
    };
  }
  return withLocalServerLock(options.paths, options.trace, "server-start", async () => {
    ensureLocalServerDependencies(options.paths, options.trace);
    const reusableRuntime = await resolveReusableLocalServerRuntime(options.paths, options.trace);
    if (reusableRuntime) {
      return reusableRuntime;
    }
    await cleanupDuplicateLocalServerProcesses({
      paths: options.paths,
      trace: options.trace
    });
    return startLocalServerProcess(options, preferredPort);
  });
}
function normalizeOutputDirectory(outputDirectory) {
  return path.resolve(outputDirectory);
}
function createDependencyFingerprint(dependencyNames) {
  return dependencyNames.join("|");
}
function listGeneratedOutputDirectories2(paths) {
  const { generatedDirectoryPath } = getLocalServerPaths(paths);
  if (!fs10.existsSync(generatedDirectoryPath) || !fs10.statSync(generatedDirectoryPath).isDirectory()) {
    return [];
  }
  return fs10.readdirSync(generatedDirectoryPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(generatedDirectoryPath, entry.name)).sort((left, right) => left.localeCompare(right));
}
function createRegistryEntry(outputDirectory, componentName = "") {
  const dependencyNames = collectOutputDependencyNames(outputDirectory);
  const normalizedOutputDirectory = normalizeOutputDirectory(outputDirectory);
  return {
    componentName: componentName || path.basename(normalizedOutputDirectory),
    dependencyFingerprint: createDependencyFingerprint(dependencyNames),
    dependencyNames,
    outputDirectory: normalizedOutputDirectory,
    previewEligible: isGenHtmlPreviewDirectoryCandidate(normalizedOutputDirectory),
    updatedAt: (/* @__PURE__ */ new Date()).toISOString()
  };
}
function readGeneratedOutputRegistry(paths) {
  try {
    const registry = readJsonFile(getLocalServerPaths(paths).generatedRegistryPath);
    const entries = Array.isArray(registry?.entries) ? registry.entries.filter((entry) => typeof entry.outputDirectory === "string" && entry.outputDirectory.trim()) : [];
    return {
      entries,
      version: 1
    };
  } catch {
    return {
      entries: [],
      version: 1
    };
  }
}
function writeGeneratedOutputRegistry(paths, registry) {
  writeJsonFile(getLocalServerPaths(paths).generatedRegistryPath, {
    entries: registry.entries.sort((left, right) => left.outputDirectory.localeCompare(right.outputDirectory)),
    version: 1
  });
}
function upsertGeneratedOutputRegistryEntry(options) {
  const registry = readGeneratedOutputRegistry(options.paths);
  const nextEntry = createRegistryEntry(options.outputDirectory, options.componentName);
  const entries = registry.entries.filter(
    (entry) => normalizeOutputDirectory(entry.outputDirectory) !== nextEntry.outputDirectory
  );
  entries.push(nextEntry);
  writeGeneratedOutputRegistry(options.paths, {
    entries});
  options.trace(
    "local-server:registry:upsert",
    `component=${nextEntry.componentName} dependencies=${nextEntry.dependencyNames.length} preview=${nextEntry.previewEligible}`
  );
  return nextEntry;
}
function syncGeneratedOutputRegistryFromDisk(paths, trace) {
  const existingRegistry = readGeneratedOutputRegistry(paths);
  const existingNames = new Map(
    existingRegistry.entries.map((entry) => [normalizeOutputDirectory(entry.outputDirectory), entry.componentName])
  );
  const entries = listGeneratedOutputDirectories2(paths).map(
    (outputDirectory) => createRegistryEntry(outputDirectory, existingNames.get(normalizeOutputDirectory(outputDirectory)) || "")
  );
  const registry = {
    entries,
    version: 1
  };
  writeGeneratedOutputRegistry(paths, registry);
  trace("local-server:registry:sync", `entries=${entries.length}`);
  return registry;
}
function listPreviewEligibleOutputDirectories(registry) {
  return registry.entries.filter((entry) => entry.previewEligible).map((entry) => entry.outputDirectory).sort((left, right) => left.localeCompare(right));
}

// src/features/gen-html/infrastructure/local-server/refresh-coordinator.ts
function refreshLocalServerPreview(options) {
  return withLocalServerRefreshLock(options.paths, options.trace, "refresh-preview", () => {
    options.trace(
      "local-server:refresh:start",
      options.outputDirectory ? `output=${options.outputDirectory}` : "output=all"
    );
    if (options.outputDirectory) {
      upsertGeneratedOutputRegistryEntry({
        componentName: options.componentName,
        outputDirectory: options.outputDirectory,
        paths: options.paths,
        trace: options.trace
      });
    }
    const registry = syncGeneratedOutputRegistryFromDisk(options.paths, options.trace);
    const outputDirectories = listPreviewEligibleOutputDirectories(registry);
    const setup = ensureLocalServerSetup(options.paths, options.trace, outputDirectories);
    const currentState = readLocalServerState(options.paths);
    const shouldRequireRestart = Boolean(
      currentState.health === "running" && (setup.packageJsonChanged || setup.storybookConfigChanged)
    );
    const restartRequired = Boolean(currentState.restartRequired || shouldRequireRestart);
    updateLocalServerState(options.paths, {
      dirty: false,
      health: currentState.health || "unknown",
      lastRefreshAt: getHumanReadableNow(),
      lastRefreshReason: options.reason,
      restartRequired
    });
    options.trace(
      "local-server:refresh:end",
      `outputs=${outputDirectories.length} packageJsonChanged=${Boolean(setup.packageJsonChanged)} storybookConfigChanged=${Boolean(setup.storybookConfigChanged)} restartRequired=${restartRequired}`
    );
    return {
      restartRequired,
      setup
    };
  });
}

// src/features/gen-html/infrastructure/local-server-workflow.ts
function hasExistingLocalServer(paths) {
  return fs10.existsSync(getLocalServerPaths(paths).packageJsonPath);
}
async function prepareBaseLocalServerForGenHtml(paths, trace) {
  const existed = hasExistingLocalServer(paths);
  const localServerPaths = getLocalServerPaths(paths);
  fs10.mkdirSync(localServerPaths.directoryPath, { recursive: true });
  fs10.mkdirSync(localServerPaths.generatedDirectoryPath, { recursive: true });
  fs10.mkdirSync(localServerPaths.multipleDirectoryPath, { recursive: true });
  const setup = ensureLocalServerSetup(paths, trace);
  if (existed) {
    trace("local-server:prepare-base:reuse-existing", getLocalServerPaths(paths).packageJsonPath);
  } else {
    trace("local-server:prepare-base:create-new", getLocalServerPaths(paths).packageJsonPath);
  }
  return {
    existed,
    setup
  };
}
function prepareGeneratedOutputForGenHtml(paths, trace, outputDirectory) {
  return refreshLocalServerPreview({
    outputDirectory,
    paths,
    reason: "gen-html component completed",
    trace
  });
}
async function startLocalServerForGenHtml(parsedArgs, paths, trace) {
  return ensureLocalServerRunning({
    parsedArgs,
    paths,
    trace
  });
}

export { hasExistingLocalServer, prepareBaseLocalServerForGenHtml, prepareGeneratedOutputForGenHtml, startLocalServerForGenHtml };
