import {
  log_build_default
} from "./chunk-ZYWUN3T3.js";
import {
  runMigrations
} from "./chunk-XUAF2H54.js";
import "./chunk-CA4U2JP7.js";
import {
  generate
} from "./chunk-7443A7LZ.js";
import {
  logger
} from "./chunk-ZXAF4JO7.js";
import {
  resolve_catalog_dependencies_default
} from "./chunk-LHR4G2UO.js";
import {
  buildSearchIndex
} from "./chunk-ZONBICNH.js";
import {
  watch
} from "./chunk-O3Y2G6CY.js";
import "./chunk-FHVBLKWN.js";
import "./chunk-DAOXTQVS.js";
import {
  catalogToAstro
} from "./chunk-WQNKHIDM.js";
import "./chunk-VC6VUJIX.js";
import {
  VERSION
} from "./chunk-Z7BGWB5J.js";
import {
  linkCoreNodeModules,
  resolveInstalledCoreNodeModules
} from "./chunk-BWN544LB.js";
import {
  getProjectOutDir,
  isAuthEnabled,
  isIndexedSearchEnabled,
  isOutputServer
} from "./chunk-B7HCX5HM.js";
import {
  getEventCatalogConfigFile,
  verifyRequiredFieldsAreInCatalogConfigFile
} from "./chunk-6QENHZZP.js";

// src/eventcatalog.ts
import { Command } from "commander";
import { execSync, spawn } from "child_process";
import { join } from "path";
import http from "http";
import fs3 from "fs";
import path2 from "path";
import { fileURLToPath } from "url";
import boxen from "boxen";
import updateNotifier from "update-notifier";
import dotenv from "dotenv";

// eventcatalog/src/enterprise/fields/field-indexer.ts
import path from "path";
import fs2 from "fs";

// eventcatalog/src/enterprise/fields/fields-db.ts
import initSqlJs from "sql.js";
import fs from "fs";
var SCHEMA_SQL = `
  CREATE TABLE IF NOT EXISTS fields (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    path TEXT NOT NULL,
    type TEXT NOT NULL,
    description TEXT NOT NULL DEFAULT '',
    required INTEGER NOT NULL DEFAULT 0,
    schema_format TEXT NOT NULL,
    message_id TEXT NOT NULL,
    message_version TEXT NOT NULL,
    message_type TEXT NOT NULL,
    message_name TEXT NOT NULL DEFAULT '',
    message_summary TEXT NOT NULL DEFAULT '',
    message_owners TEXT NOT NULL DEFAULT '[]'
  );

  CREATE TABLE IF NOT EXISTS message_producers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    message_id TEXT NOT NULL,
    message_version TEXT NOT NULL,
    service_id TEXT NOT NULL,
    service_version TEXT NOT NULL,
    service_name TEXT NOT NULL DEFAULT '',
    service_summary TEXT NOT NULL DEFAULT '',
    service_owners TEXT NOT NULL DEFAULT '[]'
  );

  CREATE TABLE IF NOT EXISTS message_consumers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    message_id TEXT NOT NULL,
    message_version TEXT NOT NULL,
    service_id TEXT NOT NULL,
    service_version TEXT NOT NULL,
    service_name TEXT NOT NULL DEFAULT '',
    service_summary TEXT NOT NULL DEFAULT '',
    service_owners TEXT NOT NULL DEFAULT '[]'
  );

  CREATE INDEX IF NOT EXISTS idx_fields_path ON fields(path);
  CREATE INDEX IF NOT EXISTS idx_fields_message ON fields(message_id, message_version);
  CREATE INDEX IF NOT EXISTS idx_producers_message ON message_producers(message_id, message_version);
  CREATE INDEX IF NOT EXISTS idx_consumers_message ON message_consumers(message_id, message_version);
`;
function queryAll(db, sql, params = []) {
  const stmt = db.prepare(sql);
  stmt.bind(params);
  const rows = [];
  while (stmt.step()) {
    rows.push(stmt.getAsObject());
  }
  stmt.free();
  return rows;
}
function queryOne(db, sql, params = []) {
  const stmt = db.prepare(sql);
  stmt.bind(params);
  const result = stmt.step() ? stmt.getAsObject() : null;
  stmt.free();
  return result;
}
var FieldsDatabase = class _FieldsDatabase {
  db;
  dbPath;
  constructor(db, dbPath) {
    this.db = db;
    this.dbPath = dbPath;
  }
  static async create(dbPath, options) {
    if (options?.recreate && fs.existsSync(dbPath)) {
      fs.unlinkSync(dbPath);
    }
    const SQL = await initSqlJs();
    let db;
    if (!options?.recreate && fs.existsSync(dbPath)) {
      const buffer = fs.readFileSync(dbPath);
      db = new SQL.Database(buffer);
    } else {
      db = new SQL.Database();
    }
    db.run(SCHEMA_SQL);
    return new _FieldsDatabase(db, dbPath);
  }
  insertField(field) {
    this.db.run(
      `INSERT INTO fields (path, type, description, required, schema_format, message_id, message_version, message_type, message_name, message_summary, message_owners)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        field.path,
        field.type,
        field.description,
        field.required ? 1 : 0,
        field.schemaFormat,
        field.messageId,
        field.messageVersion,
        field.messageType,
        field.messageName || "",
        field.messageSummary || "",
        JSON.stringify(field.messageOwners || [])
      ]
    );
  }
  insertProducer(messageId, messageVersion, serviceId, serviceVersion, serviceName, serviceSummary, serviceOwners) {
    this.db.run(
      `INSERT INTO message_producers (message_id, message_version, service_id, service_version, service_name, service_summary, service_owners)
       VALUES (?, ?, ?, ?, ?, ?, ?)`,
      [
        messageId,
        messageVersion,
        serviceId,
        serviceVersion,
        serviceName || "",
        serviceSummary || "",
        JSON.stringify(serviceOwners || [])
      ]
    );
  }
  insertConsumer(messageId, messageVersion, serviceId, serviceVersion, serviceName, serviceSummary, serviceOwners) {
    this.db.run(
      `INSERT INTO message_consumers (message_id, message_version, service_id, service_version, service_name, service_summary, service_owners)
       VALUES (?, ?, ?, ?, ?, ?, ?)`,
      [
        messageId,
        messageVersion,
        serviceId,
        serviceVersion,
        serviceName || "",
        serviceSummary || "",
        JSON.stringify(serviceOwners || [])
      ]
    );
  }
  /** Save the in-memory database to disk */
  save() {
    const data = this.db.export();
    const buffer = Buffer.from(data);
    fs.writeFileSync(this.dbPath, buffer);
  }
  queryFields(params) {
    const {
      q,
      shared,
      conflicting,
      format,
      type,
      messageType,
      message,
      producer,
      consumer,
      required,
      path: fieldPath,
      pageSize = 50,
      cursor
    } = params;
    const conditions = [];
    const bindings = [];
    if (fieldPath) {
      conditions.push(`f.path = ?`);
      bindings.push(fieldPath);
    }
    if (q) {
      conditions.push(`(f.path LIKE ? ESCAPE '\\' OR f.description LIKE ? ESCAPE '\\' OR f.type LIKE ? ESCAPE '\\')`);
      const escaped = q.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
      const term = `%${escaped}%`;
      bindings.push(term, term, term);
    }
    if (format) {
      const formats2 = format.split(",").map((f) => f.trim()).filter(Boolean);
      conditions.push(`f.schema_format IN (${formats2.map(() => "?").join(", ")})`);
      bindings.push(...formats2);
    }
    if (type) {
      conditions.push(`f.type = ?`);
      bindings.push(type);
    }
    if (messageType) {
      const types2 = messageType.split(",").map((t) => t.trim()).filter(Boolean);
      conditions.push(`f.message_type IN (${types2.map(() => "?").join(", ")})`);
      bindings.push(...types2);
    }
    if (message) {
      conditions.push(`f.message_id = ?`);
      bindings.push(message);
    }
    if (required) {
      conditions.push(`f.required = 1`);
    }
    if (producer) {
      conditions.push(
        `EXISTS (SELECT 1 FROM message_producers p WHERE p.message_id = f.message_id AND p.message_version = f.message_version AND p.service_id = ?)`
      );
      bindings.push(producer);
    }
    if (consumer) {
      conditions.push(
        `EXISTS (SELECT 1 FROM message_consumers c WHERE c.message_id = f.message_id AND c.message_version = f.message_version AND c.service_id = ?)`
      );
      bindings.push(consumer);
    }
    if (shared) {
      const sharedSubquery = `SELECT path FROM fields GROUP BY path HAVING COUNT(DISTINCT message_id || '/' || message_version) > 1`;
      conditions.push(`f.path IN (${sharedSubquery})`);
    }
    if (conflicting) {
      const conflictSubquery = `SELECT path FROM fields GROUP BY path HAVING COUNT(DISTINCT type) > 1`;
      conditions.push(`f.path IN (${conflictSubquery})`);
    }
    const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
    const cursorConditions = [];
    const cursorBindings = [];
    if (cursor) {
      const lastId = decodeCursor(cursor);
      cursorConditions.push(`f.id > ?`);
      cursorBindings.push(lastId);
    }
    const paginationWhere = cursorConditions.length > 0 ? whereClause ? `${whereClause} AND ${cursorConditions.join(" AND ")}` : `WHERE ${cursorConditions.join(" AND ")}` : whereClause;
    const countSql = `SELECT COUNT(*) as cnt FROM fields f ${whereClause}`;
    const total = queryOne(this.db, countSql, bindings).cnt;
    const mainSql = `SELECT f.* FROM fields f ${paginationWhere} ORDER BY f.id ASC LIMIT ?`;
    const allBindings = [...bindings, ...cursorBindings, pageSize];
    const rows = queryAll(this.db, mainSql, allBindings);
    const parseOwners = (raw) => {
      try {
        return JSON.parse(raw || "[]");
      } catch {
        return [];
      }
    };
    const fields = rows.map((row) => {
      const producers = queryAll(
        this.db,
        `SELECT service_id, service_version, service_name, service_summary, service_owners FROM message_producers WHERE message_id = ? AND message_version = ?`,
        [row.message_id, row.message_version]
      );
      const consumers = queryAll(
        this.db,
        `SELECT service_id, service_version, service_name, service_summary, service_owners FROM message_consumers WHERE message_id = ? AND message_version = ?`,
        [row.message_id, row.message_version]
      );
      const usedInCount = queryOne(this.db, `SELECT COUNT(DISTINCT message_id || '/' || message_version) as cnt FROM fields WHERE path = ?`, [
        row.path
      ]).cnt;
      const typeRows = queryAll(
        this.db,
        `SELECT type, COUNT(DISTINCT message_id || '/' || message_version) as count FROM fields WHERE path = ? GROUP BY type`,
        [row.path]
      );
      const conflicts = typeRows.length > 1 ? typeRows.map((r) => ({ type: r.type, count: r.count })) : void 0;
      return {
        id: row.id,
        path: row.path,
        type: row.type,
        description: row.description,
        required: row.required === 1,
        schemaFormat: row.schema_format,
        messageId: row.message_id,
        messageVersion: row.message_version,
        messageType: row.message_type,
        messageName: row.message_name || row.message_id,
        messageSummary: row.message_summary || "",
        messageOwners: parseOwners(row.message_owners),
        usedInCount,
        conflicts,
        producers: producers.map((p) => ({
          id: p.service_id,
          version: p.service_version,
          name: p.service_name || p.service_id,
          summary: p.service_summary || "",
          owners: parseOwners(p.service_owners)
        })),
        consumers: consumers.map((c) => ({
          id: c.service_id,
          version: c.service_version,
          name: c.service_name || c.service_id,
          summary: c.service_summary || "",
          owners: parseOwners(c.service_owners)
        }))
      };
    });
    const formatsFacetSql = `SELECT f.schema_format as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.schema_format`;
    const formats = queryAll(this.db, formatsFacetSql, bindings);
    const typesFacetSql = `SELECT f.type as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.type`;
    const types = queryAll(this.db, typesFacetSql, bindings);
    const messageTypesFacetSql = `SELECT f.message_type as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.message_type`;
    const messageTypes = queryAll(this.db, messageTypesFacetSql, bindings);
    const lastRow = rows[rows.length - 1];
    const nextCursor = lastRow && rows.length === pageSize ? encodeCursor(lastRow.id) : void 0;
    return {
      fields,
      total,
      cursor: nextCursor,
      facets: { formats, types, messageTypes }
    };
  }
  close() {
    this.db.close();
  }
};
function encodeCursor(id) {
  return Buffer.from(String(id)).toString("base64url");
}
function decodeCursor(cursor) {
  return parseInt(Buffer.from(cursor, "base64url").toString(), 10);
}

// eventcatalog/src/enterprise/fields/field-extractor.ts
function extractSchemaFieldsDeep(content, format) {
  if (!content) return [];
  if (format === "json-schema") {
    return extractJsonSchemaFields(content);
  }
  if (format === "avro") {
    return extractAvroFields(content);
  }
  if (format === "proto") {
    return extractProtoFields(content);
  }
  return [];
}
function extractProtoFields(content) {
  if (!content) return [];
  const fields = [];
  const fieldRegex = /^\s*(repeated\s+|optional\s+|required\s+)?(\w+)\s+(\w+)\s*=\s*\d+\s*;(?:\s*\/\/\s*(.*))?/gm;
  let match;
  while ((match = fieldRegex.exec(content)) !== null) {
    const modifier = (match[1] || "").trim();
    const type = modifier ? `${modifier} ${match[2]}` : match[2];
    fields.push({
      path: match[3],
      type,
      description: match[4]?.trim() || "",
      required: modifier === "required"
    });
  }
  return fields;
}
function extractAvroFields(content) {
  try {
    const schema = JSON.parse(content);
    const fields = [];
    walkAvroRecord(schema, "", fields);
    return fields;
  } catch {
    return [];
  }
}
function getAvroTypeName(type) {
  if (typeof type === "string") return type;
  if (Array.isArray(type)) {
    return type.map((t) => typeof t === "string" ? t : t.type || "complex").join(" | ");
  }
  if (typeof type === "object" && type !== null) {
    if (type.type === "array" && type.items) return `array<${getAvroTypeName(type.items)}>`;
    if (type.type === "record") return type.name || "record";
    return type.type || "complex";
  }
  return "unknown";
}
function walkAvroRecord(schema, prefix, fields) {
  if (!schema.fields || !Array.isArray(schema.fields)) return;
  for (const field of schema.fields) {
    const path3 = prefix ? `${prefix}.${field.name}` : field.name;
    const isOptional = Array.isArray(field.type) && field.type.includes("null");
    const typeName = getAvroTypeName(field.type);
    fields.push({
      path: path3,
      type: typeName,
      description: field.doc || "",
      required: !isOptional
    });
    const innerType = Array.isArray(field.type) ? field.type.find((t) => typeof t === "object" && t.type === "record") : typeof field.type === "object" && field.type.type === "record" ? field.type : null;
    if (innerType) {
      walkAvroRecord(innerType, path3, fields);
    }
    const arrayType = Array.isArray(field.type) ? field.type.find((t) => typeof t === "object" && t.type === "array") : typeof field.type === "object" && field.type.type === "array" ? field.type : null;
    if (arrayType && typeof arrayType.items === "object" && arrayType.items.type === "record") {
      walkAvroRecord(arrayType.items, `${path3}[]`, fields);
    }
  }
}
function extractJsonSchemaFields(content) {
  try {
    const schema = JSON.parse(content);
    const fields = [];
    walkJsonSchema(schema, "", schema.required || [], schema, fields);
    return fields;
  } catch {
    return [];
  }
}
function walkJsonSchema(node, prefix, requiredList, rootSchema, fields) {
  if (node.allOf && Array.isArray(node.allOf)) {
    const merged = { type: "object", properties: {}, required: [] };
    for (let sub of node.allOf) {
      if (sub.$ref) {
        const resolved = resolveLocalRef(sub.$ref, rootSchema);
        if (resolved) sub = resolved;
        else continue;
      }
      Object.assign(merged.properties, sub.properties || {});
      merged.required.push(...sub.required || []);
    }
    walkJsonSchema(merged, prefix, merged.required, rootSchema, fields);
    return;
  }
  if (!node.properties) return;
  for (const [name, prop] of Object.entries(node.properties)) {
    const path3 = prefix ? `${prefix}.${name}` : name;
    const isRequired = requiredList.includes(name);
    if (prop.$ref) {
      const resolved = resolveLocalRef(prop.$ref, rootSchema);
      if (resolved) {
        const rawRefType = resolved.type || "object";
        const type2 = Array.isArray(rawRefType) ? [...rawRefType].sort().join(" | ") : rawRefType;
        fields.push({ path: path3, type: type2, description: resolved.description || "", required: isRequired });
        if (resolved.properties) {
          walkJsonSchema(resolved, path3, resolved.required || [], rootSchema, fields);
        }
      } else {
        fields.push({ path: path3, type: "$ref", description: "", required: isRequired });
      }
      continue;
    }
    const rawType = prop.type || (prop.enum ? "enum" : prop.$ref ? "$ref" : "object");
    const typeList = Array.isArray(rawType) ? rawType : [rawType];
    const type = Array.isArray(rawType) ? [...rawType].sort().join(" | ") : rawType;
    fields.push({ path: path3, type, description: prop.description || "", required: isRequired });
    if (typeList.includes("object") && prop.properties) {
      walkJsonSchema(prop, path3, prop.required || [], rootSchema, fields);
    }
    if (typeList.includes("array") && prop.items) {
      if (prop.items.type === "object" && prop.items.properties) {
        walkJsonSchema(prop.items, `${path3}[]`, prop.items.required || [], rootSchema, fields);
      }
    }
  }
}
function resolveLocalRef(ref, rootSchema) {
  if (!ref.startsWith("#/")) return null;
  const parts = ref.replace("#/", "").split("/");
  let current = rootSchema;
  for (const part of parts) {
    current = current?.[part];
    if (!current) return null;
  }
  return current;
}

// eventcatalog/src/enterprise/fields/field-indexer.ts
function detectFormat(fileName) {
  const ext = path.extname(fileName).toLowerCase();
  if (ext === ".proto") return "proto";
  if (ext === ".avro" || ext === ".avsc") return "avro";
  return "json-schema";
}
async function buildFieldsIndex(catalogDir, outputDir) {
  const sdkModule = await import("@eventcatalog/sdk");
  const sdk = sdkModule.default(catalogDir);
  const dbDir = path.join(outputDir || catalogDir, ".eventcatalog");
  const dbPath = path.join(dbDir, "fields.db");
  if (!fs2.existsSync(dbDir)) {
    fs2.mkdirSync(dbDir, { recursive: true });
  }
  const db = await FieldsDatabase.create(dbPath, { recreate: true });
  const warnings = [];
  try {
    const [events, commands, queries] = await Promise.all([
      sdk.getEvents({ latestOnly: true }),
      sdk.getCommands({ latestOnly: true }),
      sdk.getQueries({ latestOnly: true })
    ]);
    const collections = [
      { entries: events || [], type: "event" },
      { entries: commands || [], type: "command" },
      { entries: queries || [], type: "query" }
    ];
    for (const { entries, type } of collections) {
      for (const entry of entries) {
        const msgId = entry.id;
        const msgVersion = entry.version;
        let schemaData;
        try {
          schemaData = await sdk.getSchemaForMessage(msgId, msgVersion);
        } catch {
          continue;
        }
        if (!schemaData) continue;
        const { schema: content, fileName } = schemaData;
        const format = detectFormat(fileName);
        try {
          const fields = extractSchemaFieldsDeep(content, format);
          for (const field of fields) {
            db.insertField({
              path: field.path,
              type: field.type,
              description: field.description,
              required: field.required,
              schemaFormat: format,
              messageId: msgId,
              messageVersion: msgVersion,
              messageType: type,
              messageName: entry.name || msgId,
              messageSummary: entry.summary || "",
              messageOwners: entry.owners || []
            });
          }
          const { producers, consumers } = await sdk.getProducersAndConsumersForMessage(msgId, msgVersion);
          for (const producer of producers) {
            db.insertProducer(
              msgId,
              msgVersion,
              producer.id,
              producer.version,
              producer.name || producer.id,
              producer.summary || "",
              producer.owners || []
            );
          }
          for (const consumer of consumers) {
            db.insertConsumer(
              msgId,
              msgVersion,
              consumer.id,
              consumer.version,
              consumer.name || consumer.id,
              consumer.summary || "",
              consumer.owners || []
            );
          }
        } catch (err) {
          warnings.push({ messageId: msgId, version: msgVersion, error: err.message });
        }
      }
    }
    db.save();
    db.close();
    return { dbPath, warnings };
  } catch (err) {
    db.close();
    throw err;
  }
}

// src/eventcatalog.ts
import { isEventCatalogStarterEnabled, isEventCatalogScaleEnabled, isFeatureEnabled } from "@eventcatalog/license";
var currentDir = path2.dirname(fileURLToPath(import.meta.url));
var program = new Command().version(VERSION);
var dir = path2.resolve(process.env.PROJECT_DIR || process.cwd());
var core = path2.resolve(process.env.CATALOG_DIR || join(dir, ".eventcatalog-core"));
var eventCatalogDir = path2.resolve(join(currentDir, "../eventcatalog/"));
var getInstalledEventCatalogVersion = () => {
  try {
    const pkg = fs3.readFileSync(join(dir, "package.json"), "utf8");
    const json = JSON.parse(pkg);
    return json.dependencies["@eventcatalog/core"];
  } catch (error) {
    return null;
  }
};
program.name("eventcatalog").description("Documentation tool for event-driven architectures");
var ensureDir = (dir2) => {
  if (!fs3.existsSync(dir2)) {
    fs3.mkdirSync(dir2);
  }
};
var resolveDevPort = async ({ projectDir }) => {
  try {
    const config = await getEventCatalogConfigFile(projectDir);
    const fromConfig = Number(config?.port);
    if (Number.isFinite(fromConfig) && fromConfig > 0) return fromConfig;
  } catch (error) {
  }
  return 3e3;
};
var startDevPrewarm = ({
  port = 3e3,
  paths = ["/ping", "/"],
  retries = 80,
  intervalMs = 250,
  initialDelayMs = 500
}) => {
  let attempt = 0;
  const hit = (requestPath) => new Promise((resolve) => {
    const req = http.get(
      {
        hostname: "127.0.0.1",
        port,
        path: requestPath,
        timeout: 1200
      },
      (res) => {
        res.resume();
        resolve(true);
      }
    );
    req.on("error", () => resolve(false));
    req.on("timeout", () => {
      req.destroy();
      resolve(false);
    });
  });
  const tick = async () => {
    attempt += 1;
    for (const requestPath of paths) {
      const ok = await hit(requestPath);
      if (ok) {
        return;
      }
    }
    if (attempt < retries) {
      setTimeout(tick, intervalMs);
    }
  };
  setTimeout(tick, initialDelayMs);
};
var createAstroLineFilter = () => {
  return (line) => {
    return line.includes("[glob-loader]") || /The collection.*does not exist/.test(line);
  };
};
var createAstroDevLineFilter = () => {
  const shouldFilterAstroLine = createAstroLineFilter();
  return (line) => {
    return shouldFilterAstroLine(line) || line.includes("[router]");
  };
};
var buildDevSearchIndex = async ({ config }) => {
  const result = await buildSearchIndex({
    projectDir: dir,
    outDir: path2.join(core, "public"),
    searchOutputPath: path2.join(core, "public", "pagefind"),
    config,
    isServer: false
  });
  logger.info(`Indexed ${result.records} page(s) into ${path2.relative(core, result.outputPath)}`, "search");
};
var warnIfIndexedSearchUsesAuth = async () => {
  if (!await isAuthEnabled()) {
    return;
  }
  logger.info(
    "Indexed search creates client-readable search files. Make sure your deployment protects /pagefind assets if your catalog is private.",
    "search"
  );
};
var createDevSearchIndexWatcher = ({ config }) => {
  let timeout;
  let isBuilding = false;
  let queued = false;
  const run = async () => {
    if (isBuilding) {
      queued = true;
      return;
    }
    isBuilding = true;
    try {
      await buildDevSearchIndex({ config });
    } catch (err) {
      logger.info(`Failed to rebuild indexed search: ${err.message}`, "search");
    } finally {
      isBuilding = false;
      if (queued) {
        queued = false;
        run();
      }
    }
  };
  return (_err, events) => {
    if (!events.some((event) => event.path.endsWith(".md") || event.path.endsWith(".mdx"))) {
      return;
    }
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(run, 500);
  };
};
var replaceAstroReadyVersionLine = (line) => {
  const matches = line.match(/^(\s*)astro(\s+)v\S+(\s+ready.*)$/i);
  if (!matches) {
    return line;
  }
  return `${matches[1]}eventcatalog${matches[2]}v${VERSION}${matches[3]}`;
};
var runCommandWithFilteredOutput = async ({
  command,
  cwd,
  env,
  shouldFilterLine,
  transformLine = (line) => line
}) => {
  await new Promise((resolve, reject) => {
    const child = spawn(command, {
      cwd,
      env: {
        ...process.env,
        ...env
      },
      shell: true,
      stdio: ["inherit", "pipe", "pipe"]
    });
    let stdoutBuffer = "";
    let stderrBuffer = "";
    const flush = (buffer, writer, isFinal = false) => {
      const lines = buffer.split("\n");
      const remaining = isFinal ? "" : lines.pop() ?? "";
      for (const rawLine of lines) {
        const line = rawLine.replace(/\r/g, "");
        if (line.length === 0) {
          writer.write("\n");
          continue;
        }
        if (!shouldFilterLine(line)) {
          writer.write(`${transformLine(rawLine)}
`);
        }
      }
      return remaining;
    };
    child.stdout.on("data", (chunk) => {
      stdoutBuffer += chunk.toString();
      stdoutBuffer = flush(stdoutBuffer, process.stdout);
    });
    child.stderr.on("data", (chunk) => {
      stderrBuffer += chunk.toString();
      stderrBuffer = flush(stderrBuffer, process.stderr);
    });
    child.on("error", (error) => {
      reject(error);
    });
    child.on("close", (code) => {
      stdoutBuffer = flush(stdoutBuffer, process.stdout, true);
      stderrBuffer = flush(stderrBuffer, process.stderr, true);
      if (code === 0) {
        resolve();
        return;
      }
      reject(new Error(`Command failed with exit code ${code}: ${command}`));
    });
  });
};
var copyCore = () => {
  ensureDir(core);
  if (eventCatalogDir === core) {
    return;
  }
  fs3.cpSync(eventCatalogDir, core, {
    recursive: true,
    filter: (src) => {
      const relativePath = path2.relative(eventCatalogDir, src);
      const pathParts = relativePath.split(path2.sep);
      return !pathParts.some((part) => [".astro", "dist", "node_modules"].includes(part));
    }
  });
  const coreNodeModules = path2.join(core, "node_modules");
  const installedCoreNodeModules = resolveInstalledCoreNodeModules(currentDir);
  linkCoreNodeModules({ coreNodeModules, installedCoreNodeModules });
};
var clearCore = () => {
  if (fs3.existsSync(core)) fs3.rmSync(core, { recursive: true });
};
var checkForUpdate = () => {
  const installedVersion = getInstalledEventCatalogVersion();
  if (!installedVersion) return;
  const majorVersion = parseInt(installedVersion.replace(/[^0-9.]/g, "").split(".")[0], 10);
  if (majorVersion < 3) {
    const v3Message = `\u{1F680} EventCatalog V3 is now available in beta!

You are currently on version ${installedVersion}.
V3 brings exciting new features and improvements.

Upgrade now: npm i @eventcatalog/core@beta`;
    console.log(
      boxen(v3Message, {
        padding: 1,
        margin: 1,
        align: "center",
        borderColor: "magenta",
        borderStyle: "round"
      })
    );
    return;
  }
  const pkg = { name: "@eventcatalog/core", version: installedVersion };
  const notifier = updateNotifier({ pkg, updateCheckInterval: 0 });
  if (notifier.update) {
    const message = `EventCatalog update available ${notifier.update.current} \u2192 ${notifier.update.latest}
Run npm i @eventcatalog/core to update`;
    console.log(
      boxen(message, {
        padding: 1,
        margin: 1,
        align: "center",
        borderColor: "yellow",
        borderStyle: {
          topLeft: " ",
          topRight: " ",
          bottomLeft: " ",
          bottomRight: " ",
          right: " ",
          top: "-",
          bottom: "-",
          left: " "
        }
      })
    );
  }
};
program.command("dev").description("Run development server of EventCatalog").option("-d, --debug", "Output EventCatalog application information into your terminal").option("--force-recreate", "Recreate the eventcatalog-core directory", false).option("--no-prewarm", "Disable automatic dev prewarm request").action(async (options, command) => {
  logger.welcome();
  logger.info("Setting up EventCatalog...", "eventcatalog");
  const isServer = await isOutputServer();
  logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
  if (fs3.existsSync(path2.join(dir, ".env"))) {
    dotenv.config({ path: path2.join(dir, ".env") });
  }
  if (options.debug) {
    logger.info("Debug mode enabled", "debug");
    logger.info(`PROJECT_DIR: ${dir}`, "debug");
    logger.info(`CATALOG_DIR: ${core}`, "debug");
  }
  if (options.forceRecreate) clearCore();
  await verifyRequiredFieldsAreInCatalogConfigFile(dir);
  copyCore();
  await resolve_catalog_dependencies_default(dir, core);
  await runMigrations(dir);
  await catalogToAstro(dir, core);
  const config = await getEventCatalogConfigFile(dir);
  const canEmbedPages = await isFeatureEnabled(
    "@eventcatalog/backstage-plugin-eventcatalog",
    process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
  );
  const isEventCatalogStarter = await isEventCatalogStarterEnabled();
  const isEventCatalogScale = await isEventCatalogScaleEnabled();
  if (isServer) {
    try {
      logger.info("Building fields index...", "fields");
      const { warnings } = await buildFieldsIndex(dir, core);
      if (warnings.length > 0) {
        logger.info(`Fields index built with ${warnings.length} warning(s)`, "fields");
      } else {
        logger.info("Fields index built successfully", "fields");
      }
    } catch (err) {
      logger.info(`Failed to build fields index: ${err.message}`, "fields");
    }
  }
  const shouldBuildIndexedSearch = await isIndexedSearchEnabled();
  if (shouldBuildIndexedSearch) {
    await warnIfIndexedSearchUsesAuth();
    logger.info("Building indexed search for local development...", "search");
    await buildDevSearchIndex({ config });
  }
  checkForUpdate();
  let watchUnsub;
  try {
    watchUnsub = await watch(dir, core, shouldBuildIndexedSearch ? createDevSearchIndexWatcher({ config }) : void 0);
    const args = command.args.join(" ").trim();
    if (options.prewarm) {
      const prewarmPort = await resolveDevPort({
        projectDir: dir
      });
      startDevPrewarm({
        port: prewarmPort,
        paths: ["/ping", "/"]
      });
    }
    await runCommandWithFilteredOutput({
      command: `npx astro dev ${args}`,
      cwd: core,
      env: {
        PROJECT_DIR: dir,
        CATALOG_DIR: core,
        ENABLE_EMBED: String(canEmbedPages || isEventCatalogScale),
        EVENTCATALOG_STARTER: String(isEventCatalogStarter),
        EVENTCATALOG_SCALE: String(isEventCatalogScale),
        EVENTCATALOG_DEV_MODE: "true",
        IGNORE_BUILD_ARTIFACTS: "true",
        NODE_NO_WARNINGS: "1"
      },
      shouldFilterLine: createAstroDevLineFilter(),
      transformLine: replaceAstroReadyVersionLine
    });
  } catch (err) {
    console.error(err);
  } finally {
    await watchUnsub?.();
  }
});
program.command("build").description("Run build of EventCatalog").action(async (options, command) => {
  logger.welcome();
  logger.info("Building EventCatalog...", "build");
  const isServer = await isOutputServer();
  logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
  if (fs3.existsSync(path2.join(dir, ".env"))) {
    dotenv.config({ path: path2.join(dir, ".env") });
  }
  await verifyRequiredFieldsAreInCatalogConfigFile(dir);
  copyCore();
  const isBackstagePluginEnabled = await isFeatureEnabled(
    "@eventcatalog/backstage-plugin-eventcatalog",
    process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
  );
  const isEventCatalogStarter = await isEventCatalogStarterEnabled();
  const isEventCatalogScale = await isEventCatalogScaleEnabled();
  const canEmbedPages = isBackstagePluginEnabled || isEventCatalogScale;
  await log_build_default(dir, {
    isEventCatalogStarterEnabled: isEventCatalogStarter,
    isEventCatalogScaleEnabled: isEventCatalogScale,
    isBackstagePluginEnabled: canEmbedPages || isEventCatalogScale
  });
  await resolve_catalog_dependencies_default(dir, core);
  await runMigrations(dir);
  await catalogToAstro(dir, core);
  if (isServer) {
    try {
      logger.info("Building fields index...", "fields");
      const { warnings } = await buildFieldsIndex(dir, core);
      if (warnings.length > 0) {
        logger.info(`Fields index built with ${warnings.length} warning(s)`, "fields");
      } else {
        logger.info("Fields index built successfully", "fields");
      }
    } catch (err) {
      logger.info(`Failed to build fields index: ${err.message}`, "fields");
    }
  }
  checkForUpdate();
  const args = command.args.join(" ").trim();
  await runCommandWithFilteredOutput({
    command: `npx astro build ${args}`,
    cwd: core,
    env: {
      PROJECT_DIR: dir,
      CATALOG_DIR: core,
      ENABLE_EMBED: String(canEmbedPages),
      EVENTCATALOG_STARTER: String(isEventCatalogStarter),
      EVENTCATALOG_SCALE: String(isEventCatalogScale),
      IGNORE_BUILD_ARTIFACTS: "true"
    },
    shouldFilterLine: createAstroLineFilter()
  });
  if (await isIndexedSearchEnabled()) {
    await warnIfIndexedSearchUsesAuth();
    const config = await getEventCatalogConfigFile(dir);
    const outDir = path2.resolve(dir, await getProjectOutDir());
    logger.info("Building indexed search...", "search");
    const result = await buildSearchIndex({
      projectDir: dir,
      outDir,
      config,
      isServer
    });
    logger.info(`Indexed ${result.records} page(s) into ${path2.relative(dir, result.outputPath)}`, "search");
  }
});
var previewCatalog = ({
  command,
  canEmbedPages = false,
  isEventCatalogStarter = false,
  isEventCatalogScale = false
}) => {
  execSync(
    `cross-env PROJECT_DIR='${dir}' CATALOG_DIR='${core}' ENABLE_EMBED=${canEmbedPages} EVENTCATALOG_STARTER=${isEventCatalogStarter} EVENTCATALOG_SCALE=${isEventCatalogScale} npx astro preview ${command.args.join(" ").trim()}`,
    {
      cwd: core,
      stdio: "inherit"
    }
  );
};
var startServerCatalog = ({
  command,
  canEmbedPages = false,
  isEventCatalogStarter = false,
  isEventCatalogScale = false
}) => {
  const serverEntryPath = path2.join(dir, "dist", "server", "entry.mjs");
  execSync(
    `cross-env PROJECT_DIR='${dir}' CATALOG_DIR='${core}' ENABLE_EMBED=${canEmbedPages} EVENTCATALOG_STARTER=${isEventCatalogStarter} EVENTCATALOG_SCALE=${isEventCatalogScale} node "${serverEntryPath}"`,
    {
      cwd: core,
      stdio: "inherit"
    }
  );
};
program.command("preview").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
  logger.welcome();
  logger.info("Starting preview of your build...", "preview");
  if (fs3.existsSync(path2.join(dir, ".env"))) {
    dotenv.config({ path: path2.join(dir, ".env") });
  }
  const canEmbedPages = await isFeatureEnabled(
    "@eventcatalog/backstage-plugin-eventcatalog",
    process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
  );
  const isEventCatalogStarter = await isEventCatalogStarterEnabled();
  const isEventCatalogScale = await isEventCatalogScaleEnabled();
  previewCatalog({ command, canEmbedPages: canEmbedPages || isEventCatalogScale, isEventCatalogStarter, isEventCatalogScale });
});
program.command("start").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
  logger.welcome();
  logger.info("Starting preview of your build...", "preview");
  if (fs3.existsSync(path2.join(dir, ".env"))) {
    dotenv.config({ path: path2.join(dir, ".env") });
  }
  const canEmbedPages = await isFeatureEnabled(
    "@eventcatalog/backstage-plugin-eventcatalog",
    process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
  );
  const isEventCatalogStarter = await isEventCatalogStarterEnabled();
  const isEventCatalogScale = await isEventCatalogScaleEnabled();
  const isServerOutput = await isOutputServer();
  if (isServerOutput) {
    startServerCatalog({
      command,
      canEmbedPages: canEmbedPages || isEventCatalogScale,
      isEventCatalogStarter,
      isEventCatalogScale
    });
  } else {
    previewCatalog({
      command,
      canEmbedPages: canEmbedPages || isEventCatalogScale,
      isEventCatalogStarter,
      isEventCatalogScale
    });
  }
});
program.command("export").description("Export your EventCatalog using the SDK dumpCatalog function").option("--include-markdown", "Include markdown content in the export", false).action(async (options) => {
  logger.welcome();
  logger.info("Exporting EventCatalog...", "export");
  if (fs3.existsSync(path2.join(dir, ".env"))) {
    dotenv.config({ path: path2.join(dir, ".env") });
  }
  const { default: initSDK } = await import("@eventcatalog/sdk");
  const sdk = initSDK(dir);
  const catalog = await sdk.dumpCatalog({ includeMarkdown: options.includeMarkdown });
  const exportsDir = path2.join(dir, "exports");
  ensureDir(exportsDir);
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
  const exportFile = path2.join(exportsDir, `catalog-${date}.json`);
  fs3.writeFileSync(exportFile, JSON.stringify(catalog, null, 2), "utf-8");
  logger.info(`Catalog exported to ${exportFile}`, "export");
});
program.command("generate [siteDir]").description("Start the generator scripts.").action(async () => {
  if (fs3.existsSync(path2.join(dir, ".env"))) {
    dotenv.config({ path: path2.join(dir, ".env") });
  }
  await generate(dir);
});
program.addHelpText(
  "after",
  `
  Passing Extra Arguments:
    Use the -- delimiter to forward arguments to the underlying process.
    Example: npx eventcatalog dev --debug -- --env=production --port=3000
  `
);
program.parseAsync().then(() => process.exit(0)).catch((err) => {
  console.error(err);
  process.exit(1);
});
