@eventcatalog/cli 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,8 +9,8 @@ var __dirname = /* @__PURE__ */ getDirname();
9
9
 
10
10
  // src/cli/index.ts
11
11
  import { program } from "commander";
12
- import { readFileSync } from "fs";
13
- import { resolve as resolve2 } from "path";
12
+ import { readFileSync as readFileSync2 } from "fs";
13
+ import { resolve as resolve3 } from "path";
14
14
 
15
15
  // src/cli/executor.ts
16
16
  import { existsSync } from "fs";
@@ -251,7 +251,7 @@ ${vizBlock}` : grouped;
251
251
  const lines = ["", ` Exported full catalog to ${filepath}`];
252
252
  if (playground) {
253
253
  const encoded = Buffer.from(dsl).toString("base64");
254
- const playgroundUrl = `http://localhost:5173/?code=${encoded}`;
254
+ const playgroundUrl = `https://playground.eventcatalog.dev/?code=${encoded}`;
255
255
  await open(playgroundUrl);
256
256
  lines.push("", ` Opening in playground...`);
257
257
  } else {
@@ -269,8 +269,8 @@ async function exportAll(options) {
269
269
  const plural = pluralize(type);
270
270
  const sdk = createSDK3(dir);
271
271
  const fetcher = getCollectionFetcher(sdk, type);
272
- const allResources = await fetcher({ latestOnly: true });
273
- if (!allResources || allResources.length === 0) {
272
+ const allResources = await fetcher({ latestOnly: true }) || [];
273
+ if (allResources.length === 0) {
274
274
  throw new Error(`No ${plural} found in catalog at '${dir}'`);
275
275
  }
276
276
  const rawDsl = await sdk.toDSL(allResources, { type, hydrate });
@@ -287,7 +287,7 @@ ${vizBlock}` : grouped;
287
287
  const lines = ["", ` Exported ${allResources.length} ${plural} to ${filepath}`];
288
288
  if (playground) {
289
289
  const encoded = Buffer.from(dsl).toString("base64");
290
- const playgroundUrl = `http://localhost:5173/?code=${encoded}`;
290
+ const playgroundUrl = `https://playground.eventcatalog.dev/?code=${encoded}`;
291
291
  await open(playgroundUrl);
292
292
  lines.push("", ` Opening in playground...`);
293
293
  } else {
@@ -326,7 +326,7 @@ ${vizBlock}` : grouped;
326
326
  const lines = ["", ` Exported ${type} '${id}' to ${filepath}`];
327
327
  if (playground) {
328
328
  const encoded = Buffer.from(dsl).toString("base64");
329
- const playgroundUrl = `http://localhost:5173/?code=${encoded}`;
329
+ const playgroundUrl = `https://playground.eventcatalog.dev/?code=${encoded}`;
330
330
  await open(playgroundUrl);
331
331
  lines.push("", ` Opening in playground...`);
332
332
  } else {
@@ -336,11 +336,568 @@ ${vizBlock}` : grouped;
336
336
  return lines.join("\n");
337
337
  }
338
338
 
339
+ // src/cli/import.ts
340
+ import { readFileSync, existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2, copyFileSync } from "fs";
341
+ import { resolve as resolve2, join } from "path";
342
+ import { randomUUID } from "crypto";
343
+ import { createInterface } from "readline";
344
+ import matter from "gray-matter";
345
+ import createSDK4 from "@eventcatalog/sdk";
346
+ var RESOURCE_TYPE_FROM_FOLDER = {
347
+ events: "event",
348
+ commands: "command",
349
+ queries: "query",
350
+ services: "service",
351
+ domains: "domain",
352
+ channels: "channel",
353
+ flows: "flow",
354
+ containers: "container",
355
+ "data-products": "dataProduct",
356
+ diagrams: "diagram",
357
+ users: "user",
358
+ teams: "team"
359
+ };
360
+ async function parseDSL(source, options) {
361
+ const { createEcServices, compile } = await import("@eventcatalog/language-server");
362
+ const { EmptyFileSystem, URI } = await import("langium");
363
+ const services = createEcServices(EmptyFileSystem);
364
+ const uri = URI.parse(`file:///import-${Date.now()}.ec`);
365
+ const document = services.shared.workspace.LangiumDocumentFactory.fromString(source, uri);
366
+ services.shared.workspace.LangiumDocuments.addDocument(document);
367
+ await services.shared.workspace.DocumentBuilder.build([document]);
368
+ const parserErrors = document.parseResult.parserErrors;
369
+ if (parserErrors.length > 0) {
370
+ const messages = parserErrors.map((e) => ` Line ${e.token?.startLine ?? "?"}: ${e.message}`);
371
+ throw new Error(`Parse errors:
372
+ ${messages.join("\n")}`);
373
+ }
374
+ const program2 = document.parseResult.value;
375
+ const outputs = compile(program2, { nested: options?.nested });
376
+ try {
377
+ services.shared.workspace.LangiumDocuments.deleteDocument(uri);
378
+ } catch {
379
+ }
380
+ return outputs;
381
+ }
382
+ function extractResourceTypeFolder(path2) {
383
+ const segments = path2.split("/");
384
+ let lastTypeFolder = segments[0];
385
+ for (const seg of segments) {
386
+ if (RESOURCE_TYPE_FROM_FOLDER[seg]) {
387
+ lastTypeFolder = seg;
388
+ }
389
+ }
390
+ return lastTypeFolder;
391
+ }
392
+ function parseCompiledOutput(output) {
393
+ const { data: frontmatter, content: markdown } = matter(output.content);
394
+ const typeFolder = extractResourceTypeFolder(output.path);
395
+ const type = RESOURCE_TYPE_FROM_FOLDER[typeFolder] || typeFolder;
396
+ return {
397
+ type,
398
+ id: frontmatter.id,
399
+ version: frontmatter.version,
400
+ frontmatter,
401
+ markdown: markdown.trim(),
402
+ path: output.path
403
+ };
404
+ }
405
+ var MESSAGE_TYPE_FOLDER = {
406
+ event: "events",
407
+ command: "commands",
408
+ query: "queries",
409
+ channel: "channels"
410
+ };
411
+ var DEFAULT_STUB_VERSION = "0.0.1";
412
+ async function extractMessageStubs(source, compiledIds, nested = false) {
413
+ const { createEcServices } = await import("@eventcatalog/language-server");
414
+ const { EmptyFileSystem, URI } = await import("langium");
415
+ const services = createEcServices(EmptyFileSystem);
416
+ const uri = URI.parse(`file:///stub-extract-${Date.now()}.ec`);
417
+ const document = services.shared.workspace.LangiumDocumentFactory.fromString(source, uri);
418
+ services.shared.workspace.LangiumDocuments.addDocument(document);
419
+ await services.shared.workspace.DocumentBuilder.build([document]);
420
+ const program2 = document.parseResult.value;
421
+ const stubs = [];
422
+ const stubIds = /* @__PURE__ */ new Set();
423
+ function processDefinitions(definitions, parentPath = "") {
424
+ for (const def of definitions) {
425
+ if (def.$type === "VisualizerDef" && def.body) {
426
+ processDefinitions(def.body, parentPath);
427
+ continue;
428
+ }
429
+ if (def.$type === "DomainDef") {
430
+ const domainPath = nested ? `domains/${def.name}` : "";
431
+ const domainBody = def.body || [];
432
+ const domainServices = domainBody.filter((d) => d.$type === "ServiceDef");
433
+ processDefinitions(domainServices, domainPath);
434
+ const subdomains = domainBody.filter((d) => d.$type === "SubdomainDef");
435
+ for (const sub of subdomains) {
436
+ const subPath = nested ? `domains/${def.name}/subdomains/${sub.name}` : "";
437
+ const subServices = (sub.body || []).filter((d) => d.$type === "ServiceDef");
438
+ processDefinitions(subServices, subPath);
439
+ }
440
+ continue;
441
+ }
442
+ if (def.$type !== "ServiceDef") continue;
443
+ const servicePath = nested ? parentPath ? `${parentPath}/services/${def.name}` : `services/${def.name}` : "";
444
+ const body = def.body || [];
445
+ for (const stmt of body) {
446
+ if (stmt.$type !== "SendsStmt" && stmt.$type !== "ReceivesStmt") continue;
447
+ const msgType = stmt.messageType;
448
+ const msgName = stmt.messageName;
449
+ const hasBody = stmt.body && stmt.body.length > 0;
450
+ if (hasBody) continue;
451
+ const key = `${msgType}:${msgName}`;
452
+ if (compiledIds.has(key) || stubIds.has(key)) continue;
453
+ const folder = MESSAGE_TYPE_FOLDER[msgType];
454
+ if (!folder) continue;
455
+ const version2 = stmt.version || DEFAULT_STUB_VERSION;
456
+ const stubFolder = nested && servicePath ? `${servicePath}/${folder}` : folder;
457
+ stubIds.add(key);
458
+ stubs.push({
459
+ type: msgType,
460
+ id: msgName,
461
+ version: version2,
462
+ frontmatter: {
463
+ id: msgName,
464
+ name: msgName,
465
+ version: version2
466
+ },
467
+ markdown: "",
468
+ path: `${stubFolder}/${msgName}/versioned/${version2}/index.md`
469
+ });
470
+ if (stmt.channelClause) {
471
+ const channels = stmt.channelClause.channels || [];
472
+ for (const ch of channels) {
473
+ const chName = ch.channelName;
474
+ const chKey = `channel:${chName}`;
475
+ if (compiledIds.has(chKey) || stubIds.has(chKey)) continue;
476
+ const chVersion = ch.channelVersion || DEFAULT_STUB_VERSION;
477
+ const chFolder = nested && parentPath ? `${parentPath}/channels` : "channels";
478
+ stubIds.add(chKey);
479
+ stubs.push({
480
+ type: "channel",
481
+ id: chName,
482
+ version: chVersion,
483
+ frontmatter: {
484
+ id: chName,
485
+ name: chName,
486
+ version: chVersion
487
+ },
488
+ markdown: "",
489
+ path: `${chFolder}/${chName}/versioned/${chVersion}/index.md`
490
+ });
491
+ }
492
+ }
493
+ }
494
+ }
495
+ }
496
+ processDefinitions(program2.definitions);
497
+ try {
498
+ services.shared.workspace.LangiumDocuments.deleteDocument(uri);
499
+ } catch {
500
+ }
501
+ return stubs;
502
+ }
503
+ var SDK_WRITER_BASE = {
504
+ event: "events",
505
+ command: "commands",
506
+ query: "queries",
507
+ service: "services",
508
+ domain: "domains",
509
+ channel: "channels",
510
+ container: "containers",
511
+ team: "teams",
512
+ user: "users"
513
+ };
514
+ function extractSdkPath(compiledPath, resourceType) {
515
+ const dirPath = compiledPath.replace(/\/versioned\/[^/]+\/index\.md$/, "").replace(/\/index\.md$/, "").replace(/\.md$/, "");
516
+ const baseFolder = SDK_WRITER_BASE[resourceType];
517
+ if (!baseFolder) return "";
518
+ if (dirPath.startsWith(`${baseFolder}/`)) {
519
+ const relative = dirPath.slice(baseFolder.length + 1);
520
+ if (!relative.includes("/")) return "";
521
+ return relative;
522
+ }
523
+ return `../${dirPath}`;
524
+ }
525
+ var TYPES_WITH_NODE_GRAPH = /* @__PURE__ */ new Set(["event", "command", "query", "service", "domain", "channel", "flow"]);
526
+ function getWriter(sdk, type) {
527
+ switch (type) {
528
+ case "event":
529
+ return sdk.writeEvent;
530
+ case "command":
531
+ return sdk.writeCommand;
532
+ case "query":
533
+ return sdk.writeQuery;
534
+ case "service":
535
+ return sdk.writeService;
536
+ case "domain":
537
+ return sdk.writeDomain;
538
+ case "channel":
539
+ return sdk.writeChannel;
540
+ case "team":
541
+ return sdk.writeTeam;
542
+ case "user":
543
+ return sdk.writeUser;
544
+ default:
545
+ return null;
546
+ }
547
+ }
548
+ function getReader(sdk, type) {
549
+ switch (type) {
550
+ case "event":
551
+ return sdk.getEvent;
552
+ case "command":
553
+ return sdk.getCommand;
554
+ case "query":
555
+ return sdk.getQuery;
556
+ case "service":
557
+ return sdk.getService;
558
+ case "domain":
559
+ return sdk.getDomain;
560
+ case "channel":
561
+ return sdk.getChannel;
562
+ case "team":
563
+ return sdk.getTeam;
564
+ case "user":
565
+ return sdk.getUser;
566
+ default:
567
+ return null;
568
+ }
569
+ }
570
+ function promptConfirm(message) {
571
+ return new Promise((resolve4) => {
572
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
573
+ rl.question(`${message} `, (answer) => {
574
+ rl.close();
575
+ const normalized = answer.trim().toLowerCase();
576
+ resolve4(normalized === "" || normalized === "y" || normalized === "yes");
577
+ });
578
+ });
579
+ }
580
+ function promptInput(message, defaultValue) {
581
+ return new Promise((resolve4) => {
582
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
583
+ rl.question(`${message} `, (answer) => {
584
+ rl.close();
585
+ resolve4(answer.trim() || defaultValue);
586
+ });
587
+ });
588
+ }
589
+ function initCatalog(dir, organizationName = "My Organization") {
590
+ const catalogDir = resolve2(dir);
591
+ mkdirSync(catalogDir, { recursive: true });
592
+ const packageJson = {
593
+ name: "my-catalog",
594
+ version: "0.1.0",
595
+ private: true,
596
+ scripts: {
597
+ dev: "eventcatalog dev",
598
+ build: "eventcatalog build",
599
+ start: "eventcatalog start",
600
+ preview: "eventcatalog preview",
601
+ generate: "eventcatalog generate",
602
+ lint: "eventcatalog-linter",
603
+ test: 'echo "Error: no test specified" && exit 1'
604
+ },
605
+ dependencies: {
606
+ "@eventcatalog/core": "latest",
607
+ "@eventcatalog/linter": "latest"
608
+ }
609
+ };
610
+ writeFileSync2(join(catalogDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
611
+ const cId = randomUUID();
612
+ const config = `/** @type {import('@eventcatalog/core/bin/eventcatalog.config').Config} */
613
+ export default {
614
+ title: 'EventCatalog',
615
+ tagline: 'Discover, Explore and Document your Event Driven Architectures',
616
+ organizationName: '${organizationName}',
617
+ homepageLink: 'https://eventcatalog.dev/',
618
+ output: 'static',
619
+ trailingSlash: false,
620
+ base: '/',
621
+ logo: {
622
+ alt: 'EventCatalog Logo',
623
+ src: '/logo.png',
624
+ text: 'EventCatalog',
625
+ },
626
+ cId: '${cId}',
627
+ };
628
+ `;
629
+ writeFileSync2(join(catalogDir, "eventcatalog.config.js"), config, "utf-8");
630
+ const gitignore = `# Dependencies
631
+ /node_modules
632
+
633
+ # Production
634
+ /build
635
+
636
+ # Generated files
637
+ .astro
638
+ out
639
+ dist
640
+
641
+ # Misc
642
+ .DS_Store
643
+ .env.local
644
+ .env.development.local
645
+ .env.test.local
646
+ .env.production.local
647
+
648
+ npm-debug.log*
649
+ yarn-debug.log*
650
+ yarn-error.log*
651
+
652
+ .eventcatalog-core
653
+
654
+ .env
655
+ .env-*
656
+ `;
657
+ writeFileSync2(join(catalogDir, ".gitignore"), gitignore, "utf-8");
658
+ const envFile = `# EventCatalog Scale License Key, if you want to unlock the scale features
659
+ # You can get a 14 day trial license key from https://eventcatalog.cloud
660
+
661
+ EVENTCATALOG_SCALE_LICENSE_KEY=
662
+
663
+ # Optional key if you are using EventCatalog Chat with OpenAI Models.
664
+ # You need to set \`output\` to \`server\` in your eventcatalog.config.js file.
665
+ # See documentation for more details: https://www.eventcatalog.dev/features/ai-assistant
666
+ OPENAI_API_KEY=
667
+ `;
668
+ writeFileSync2(join(catalogDir, ".env"), envFile, "utf-8");
669
+ writeFileSync2(join(catalogDir, ".npmrc"), "strict-peer-dependencies=false\n", "utf-8");
670
+ mkdirSync(join(catalogDir, "public"), { recursive: true });
671
+ copyFileSync(join(__dirname, "logo.png"), join(catalogDir, "public", "logo.png"));
672
+ }
673
+ async function importDSL(options) {
674
+ const { files, stdin = false, dryRun = false, flat = false, noInit = false, dir } = options;
675
+ const nested = !flat;
676
+ let source;
677
+ if (stdin) {
678
+ source = await readStdin();
679
+ } else if (files && files.length > 0) {
680
+ const parts = [];
681
+ for (const file of files) {
682
+ const filepath = resolve2(file);
683
+ if (!existsSync2(filepath)) {
684
+ throw new Error(`File not found: ${filepath}`);
685
+ }
686
+ parts.push(readFileSync(filepath, "utf-8"));
687
+ }
688
+ source = parts.join("\n\n");
689
+ } else {
690
+ throw new Error("Either provide .ec file paths or use --stdin");
691
+ }
692
+ if (!source.trim()) {
693
+ throw new Error("No DSL content to import");
694
+ }
695
+ const catalogDir = resolve2(dir);
696
+ let didInit = false;
697
+ if (!noInit && !existsSync2(join(catalogDir, "eventcatalog.config.js")) && process.stdin.isTTY) {
698
+ const confirmed = await promptConfirm(`Initialize a new EventCatalog at ${catalogDir}? (Y/n)`);
699
+ if (confirmed) {
700
+ const organizationName = await promptInput("Organization name (My Organization):", "My Organization");
701
+ initCatalog(dir, organizationName);
702
+ didInit = true;
703
+ }
704
+ }
705
+ const outputs = await parseDSL(source, { nested });
706
+ if (outputs.length === 0) {
707
+ throw new Error("No resources found in DSL content");
708
+ }
709
+ const sdk = createSDK4(catalogDir);
710
+ const result = { created: [], updated: [], versioned: [], errors: [] };
711
+ const resources = outputs.map(parseCompiledOutput);
712
+ const compiledIds = new Set(resources.map((r) => `${r.type}:${r.id}`));
713
+ const stubs = await extractMessageStubs(source, compiledIds, nested);
714
+ resources.push(...stubs);
715
+ for (const resource of resources) {
716
+ const label = resource.version ? `${resource.type} ${resource.id}@${resource.version}` : `${resource.type} ${resource.id}`;
717
+ if (dryRun) {
718
+ const reader = getReader(sdk, resource.type);
719
+ if (reader) {
720
+ const existing = await reader(resource.id, resource.version).catch(() => void 0);
721
+ if (existing) {
722
+ result.updated.push(label);
723
+ } else {
724
+ const latest = await reader(resource.id).catch(() => void 0);
725
+ if (latest && latest.version && latest.version !== resource.version) {
726
+ result.versioned.push(`${resource.type} ${resource.id}@${latest.version}`);
727
+ }
728
+ result.created.push(label);
729
+ }
730
+ } else {
731
+ result.created.push(label);
732
+ }
733
+ continue;
734
+ }
735
+ const writer = getWriter(sdk, resource.type);
736
+ if (!writer) {
737
+ result.errors.push(`${label}: unsupported resource type '${resource.type}'`);
738
+ continue;
739
+ }
740
+ try {
741
+ const reader = getReader(sdk, resource.type);
742
+ const existing = reader ? await reader(resource.id, resource.version).catch(() => void 0) : void 0;
743
+ let markdown = resource.markdown;
744
+ if (!existing && TYPES_WITH_NODE_GRAPH.has(resource.type)) {
745
+ markdown = markdown ? `${markdown}
746
+
747
+ <NodeGraph />` : "<NodeGraph />";
748
+ }
749
+ const resourceData = {
750
+ ...resource.frontmatter,
751
+ markdown
752
+ };
753
+ const writeOptions = {
754
+ override: true,
755
+ versionExistingContent: true
756
+ };
757
+ if (!existing && nested) {
758
+ const sdkPath = extractSdkPath(resource.path, resource.type);
759
+ if (sdkPath) {
760
+ writeOptions.path = sdkPath;
761
+ }
762
+ }
763
+ await writer(resourceData, writeOptions);
764
+ if (existing) {
765
+ result.updated.push(label);
766
+ } else {
767
+ result.created.push(label);
768
+ }
769
+ } catch (error) {
770
+ result.errors.push(`${label}: ${error instanceof Error ? error.message : String(error)}`);
771
+ }
772
+ }
773
+ let output = formatResult(result, dryRun);
774
+ if (didInit) {
775
+ output += ` Tip: Run 'npm install' in ${catalogDir} to install dependencies
776
+ `;
777
+ }
778
+ return output;
779
+ }
780
+ var c = {
781
+ reset: "\x1B[0m",
782
+ bold: "\x1B[1m",
783
+ dim: "\x1B[2m",
784
+ green: "\x1B[32m",
785
+ yellow: "\x1B[33m",
786
+ blue: "\x1B[34m",
787
+ magenta: "\x1B[35m",
788
+ cyan: "\x1B[36m",
789
+ red: "\x1B[31m",
790
+ white: "\x1B[37m",
791
+ gray: "\x1B[90m"
792
+ };
793
+ var TYPE_CONFIG = {
794
+ domain: { color: c.magenta, label: "domain", order: 0 },
795
+ service: { color: c.blue, label: "service", order: 1 },
796
+ event: { color: c.green, label: "event", order: 2 },
797
+ command: { color: c.yellow, label: "command", order: 3 },
798
+ query: { color: c.cyan, label: "query", order: 4 },
799
+ channel: { color: c.gray, label: "channel", order: 5 },
800
+ flow: { color: c.white, label: "flow", order: 6 },
801
+ user: { color: c.blue, label: "user", order: 7 },
802
+ team: { color: c.blue, label: "team", order: 8 }
803
+ };
804
+ var DEFAULT_TYPE_CONFIG = { color: c.white, label: "resource", order: 99 };
805
+ function parseLabel(label) {
806
+ const spaceIdx = label.indexOf(" ");
807
+ if (spaceIdx === -1) return { type: "", name: label };
808
+ return { type: label.slice(0, spaceIdx), name: label.slice(spaceIdx + 1) };
809
+ }
810
+ function groupByType(labels) {
811
+ const groups = /* @__PURE__ */ new Map();
812
+ for (const label of labels) {
813
+ const { type, name } = parseLabel(label);
814
+ if (!groups.has(type)) groups.set(type, []);
815
+ groups.get(type).push(name);
816
+ }
817
+ const sorted = new Map(
818
+ [...groups.entries()].sort((a, b) => {
819
+ const orderA = (TYPE_CONFIG[a[0]] || DEFAULT_TYPE_CONFIG).order;
820
+ const orderB = (TYPE_CONFIG[b[0]] || DEFAULT_TYPE_CONFIG).order;
821
+ return orderA - orderB;
822
+ })
823
+ );
824
+ for (const [, names] of sorted) {
825
+ names.sort();
826
+ }
827
+ return sorted;
828
+ }
829
+ function typeBadge(type) {
830
+ const cfg = TYPE_CONFIG[type] || DEFAULT_TYPE_CONFIG;
831
+ const padded = ` ${cfg.label} `;
832
+ return `${cfg.color}\x1B[7m${padded}${c.reset}`;
833
+ }
834
+ function formatResourceList(labels) {
835
+ const lines = [];
836
+ const groups = groupByType(labels);
837
+ for (const [type, names] of groups) {
838
+ const cfg = TYPE_CONFIG[type] || DEFAULT_TYPE_CONFIG;
839
+ const plural = names.length === 1 ? cfg.label : `${cfg.label}s`;
840
+ lines.push("");
841
+ lines.push(` ${typeBadge(type)} ${c.dim}${names.length} ${plural}${c.reset}`);
842
+ for (const name of names) {
843
+ lines.push(` ${cfg.color}\u2502${c.reset} ${name}`);
844
+ }
845
+ }
846
+ return lines;
847
+ }
848
+ function formatResult(result, dryRun) {
849
+ const lines = [""];
850
+ const prefix = dryRun ? `${c.yellow}${c.bold}DRY RUN${c.reset} ` : "";
851
+ const total = result.created.length + result.updated.length + result.versioned.length;
852
+ if (total > 0 || result.errors.length > 0) {
853
+ lines.push(` ${prefix}${c.bold}Import complete${c.reset}`);
854
+ lines.push(` ${c.dim}${"\u2500".repeat(40)}${c.reset}`);
855
+ }
856
+ if (result.created.length > 0) {
857
+ const verb = dryRun ? "Would create" : "Created";
858
+ lines.push("");
859
+ lines.push(` ${c.green}${c.bold}+ ${verb} ${result.created.length} resource(s)${c.reset}`);
860
+ lines.push(...formatResourceList(result.created));
861
+ }
862
+ if (result.updated.length > 0) {
863
+ const verb = dryRun ? "Would update" : "Updated";
864
+ lines.push("");
865
+ lines.push(` ${c.blue}${c.bold}~ ${verb} ${result.updated.length} resource(s)${c.reset}`);
866
+ lines.push(...formatResourceList(result.updated));
867
+ }
868
+ if (result.versioned.length > 0) {
869
+ const verb = dryRun ? "Would version" : "Versioned";
870
+ lines.push("");
871
+ lines.push(` ${c.yellow}${c.bold}\u2191 ${verb} ${result.versioned.length} existing resource(s)${c.reset}`);
872
+ lines.push(...formatResourceList(result.versioned));
873
+ }
874
+ if (result.errors.length > 0) {
875
+ lines.push("");
876
+ lines.push(` ${c.red}${c.bold}\u2718 ${result.errors.length} error(s)${c.reset}`);
877
+ for (const e of result.errors) {
878
+ lines.push(` ${c.red}\u2502${c.reset} ${c.red}${e}${c.reset}`);
879
+ }
880
+ }
881
+ if (result.created.length === 0 && result.updated.length === 0 && result.errors.length === 0) {
882
+ lines.push(` ${c.dim}No resources to write.${c.reset}`);
883
+ }
884
+ lines.push("");
885
+ return lines.join("\n");
886
+ }
887
+ function readStdin() {
888
+ return new Promise((resolve4, reject) => {
889
+ const chunks = [];
890
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
891
+ process.stdin.on("end", () => resolve4(Buffer.concat(chunks).toString("utf-8")));
892
+ process.stdin.on("error", reject);
893
+ });
894
+ }
895
+
339
896
  // src/cli/index.ts
340
897
  var version = "1.0.0";
341
898
  try {
342
- const packageJsonPath = resolve2(__dirname, "../../package.json");
343
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
899
+ const packageJsonPath = resolve3(__dirname, "../../package.json");
900
+ const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
344
901
  version = packageJson.version;
345
902
  } catch {
346
903
  }
@@ -390,6 +947,24 @@ program.command("export").description("Export a catalog resource to EventCatalog
390
947
  process.exit(1);
391
948
  }
392
949
  });
950
+ program.command("import [files...]").description("Import EventCatalog DSL (.ec) files into catalog markdown").option("--stdin", "Read DSL from stdin", false).option("--dry-run", "Preview resources without writing", false).option("--flat", "Write resources in flat structure (no nesting under domains/services)", false).option("--no-init", "Skip catalog initialization prompt").action(async (files, opts) => {
951
+ try {
952
+ const globalOpts = program.opts();
953
+ const dir = globalOpts.dir || ".";
954
+ const result = await importDSL({
955
+ files: files.length > 0 ? files : void 0,
956
+ stdin: opts.stdin,
957
+ dryRun: opts.dryRun,
958
+ flat: opts.flat,
959
+ noInit: !opts.init,
960
+ dir
961
+ });
962
+ console.log(result);
963
+ } catch (error) {
964
+ console.error(error instanceof Error ? error.message : String(error));
965
+ process.exit(1);
966
+ }
967
+ });
393
968
  program.arguments("<function> [args...]").action(async (functionName, args) => {
394
969
  try {
395
970
  const options = program.opts();