@eventcatalog/cli 0.3.1 → 0.3.3

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.
package/dist/cli/index.js CHANGED
@@ -25,8 +25,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/cli/index.ts
27
27
  var import_commander = require("commander");
28
- var import_node_fs3 = require("fs");
29
- var import_node_path2 = require("path");
28
+ var import_node_fs4 = require("fs");
29
+ var import_node_path3 = require("path");
30
30
 
31
31
  // src/cli/executor.ts
32
32
  var import_node_fs = require("fs");
@@ -140,6 +140,7 @@ var import_node_path = require("path");
140
140
  var import_open = __toESM(require("open"));
141
141
  var import_sdk3 = __toESM(require("@eventcatalog/sdk"));
142
142
  var RESOURCE_TYPES = ["event", "command", "query", "service", "domain"];
143
+ var SUPPORTED_RESOURCE_TYPES = RESOURCE_TYPES.join(", ");
143
144
  var PLURAL_MAP = {
144
145
  events: "event",
145
146
  commands: "command",
@@ -147,11 +148,40 @@ var PLURAL_MAP = {
147
148
  services: "service",
148
149
  domains: "domain"
149
150
  };
151
+ var KNOWN_UNSUPPORTED_EXPORT_TYPES = /* @__PURE__ */ new Set([
152
+ "channel",
153
+ "channels",
154
+ "team",
155
+ "teams",
156
+ "user",
157
+ "users",
158
+ "container",
159
+ "containers",
160
+ "data-product",
161
+ "data-products",
162
+ "dataproduct",
163
+ "dataproducts",
164
+ "diagram",
165
+ "diagrams",
166
+ "flow",
167
+ "flows"
168
+ ]);
150
169
  function normalizeResourceType(resource) {
151
170
  const lower = resource.toLowerCase();
152
171
  if (PLURAL_MAP[lower]) return PLURAL_MAP[lower];
153
172
  return lower;
154
173
  }
174
+ function assertSupportedExportType(resource, type) {
175
+ const lower = resource.toLowerCase();
176
+ if (KNOWN_UNSUPPORTED_EXPORT_TYPES.has(lower)) {
177
+ throw new Error(
178
+ `Resource type '${resource}' is not yet supported for DSL export. Supported types: ${SUPPORTED_RESOURCE_TYPES}`
179
+ );
180
+ }
181
+ if (!RESOURCE_TYPES.includes(type)) {
182
+ throw new Error(`Invalid resource type '${resource}'. Must be one of: ${SUPPORTED_RESOURCE_TYPES}`);
183
+ }
184
+ }
155
185
  function getResourceFetcher(sdk, type) {
156
186
  switch (type) {
157
187
  case "event":
@@ -242,14 +272,14 @@ ${entries.join("\n")}
242
272
  async function exportCatalog(options) {
243
273
  const { hydrate = false, stdout = false, playground = false, output, dir } = options;
244
274
  const sdk = (0, import_sdk3.default)(dir);
245
- const dslParts = [];
246
- for (const type of RESOURCE_TYPES) {
247
- const fetcher = getCollectionFetcher(sdk, type);
248
- const resources = await fetcher({ latestOnly: true });
249
- if (!resources || resources.length === 0) continue;
250
- const rawDsl = await sdk.toDSL(resources, { type, hydrate });
251
- dslParts.push(rawDsl);
252
- }
275
+ const dslParts = (await Promise.all(
276
+ RESOURCE_TYPES.map(async (type) => {
277
+ const fetcher = getCollectionFetcher(sdk, type);
278
+ const resources = await fetcher({ latestOnly: true });
279
+ if (!resources || resources.length === 0) return "";
280
+ return sdk.toDSL(resources, { type, hydrate });
281
+ })
282
+ )).filter((dsl2) => Boolean(dsl2));
253
283
  if (dslParts.length === 0) {
254
284
  throw new Error(`No resources found in catalog at '${dir}'`);
255
285
  }
@@ -269,9 +299,9 @@ ${vizBlock}` : grouped;
269
299
  const encoded = Buffer.from(dsl).toString("base64");
270
300
  const playgroundUrl = `https://playground.eventcatalog.dev/?code=${encoded}`;
271
301
  await (0, import_open.default)(playgroundUrl);
272
- lines.push("", ` Opening in playground...`);
302
+ lines.push("", ` Opening in EventCatalog Modelling...`);
273
303
  } else {
274
- lines.push("", ` Tip: Use --playground to open in the playground`);
304
+ lines.push("", ` Tip: Use --playground to open in EventCatalog Modelling`);
275
305
  }
276
306
  lines.push("");
277
307
  return lines.join("\n");
@@ -279,9 +309,7 @@ ${vizBlock}` : grouped;
279
309
  async function exportAll(options) {
280
310
  const { resource, hydrate = false, stdout = false, playground = false, output, dir } = options;
281
311
  const type = normalizeResourceType(resource);
282
- if (!RESOURCE_TYPES.includes(type)) {
283
- throw new Error(`Invalid resource type '${resource}'. Must be one of: ${RESOURCE_TYPES.join(", ")}`);
284
- }
312
+ assertSupportedExportType(resource, type);
285
313
  const plural = pluralize(type);
286
314
  const sdk = (0, import_sdk3.default)(dir);
287
315
  const fetcher = getCollectionFetcher(sdk, type);
@@ -305,9 +333,9 @@ ${vizBlock}` : grouped;
305
333
  const encoded = Buffer.from(dsl).toString("base64");
306
334
  const playgroundUrl = `https://playground.eventcatalog.dev/?code=${encoded}`;
307
335
  await (0, import_open.default)(playgroundUrl);
308
- lines.push("", ` Opening in playground...`);
336
+ lines.push("", ` Opening in EventCatalog Modelling...`);
309
337
  } else {
310
- lines.push("", ` Tip: Use --playground to open in the playground`);
338
+ lines.push("", ` Tip: Use --playground to open in EventCatalog Modelling`);
311
339
  }
312
340
  lines.push("");
313
341
  return lines.join("\n");
@@ -318,9 +346,7 @@ async function exportResource(options) {
318
346
  return exportAll(options);
319
347
  }
320
348
  const type = normalizeResourceType(resource);
321
- if (!RESOURCE_TYPES.includes(type)) {
322
- throw new Error(`Invalid resource type '${resource}'. Must be one of: ${RESOURCE_TYPES.join(", ")}`);
323
- }
349
+ assertSupportedExportType(resource, type);
324
350
  const sdk = (0, import_sdk3.default)(dir);
325
351
  const fetcher = getResourceFetcher(sdk, type);
326
352
  const data = await fetcher(id, version2);
@@ -344,19 +370,739 @@ ${vizBlock}` : grouped;
344
370
  const encoded = Buffer.from(dsl).toString("base64");
345
371
  const playgroundUrl = `https://playground.eventcatalog.dev/?code=${encoded}`;
346
372
  await (0, import_open.default)(playgroundUrl);
347
- lines.push("", ` Opening in playground...`);
373
+ lines.push("", ` Opening in EventCatalog Modelling...`);
348
374
  } else {
349
- lines.push("", ` Tip: Use --playground to open in the playground`);
375
+ lines.push("", ` Tip: Use --playground to open in EventCatalog Modelling`);
350
376
  }
351
377
  lines.push("");
352
378
  return lines.join("\n");
353
379
  }
354
380
 
381
+ // src/cli/import.ts
382
+ var import_node_fs3 = require("fs");
383
+ var import_node_path2 = require("path");
384
+ var import_node_crypto = require("crypto");
385
+ var import_node_readline = require("readline");
386
+ var import_gray_matter = __toESM(require("gray-matter"));
387
+ var import_sdk4 = __toESM(require("@eventcatalog/sdk"));
388
+ function normalizeImportedFrontmatter(type, frontmatter) {
389
+ const normalized = { ...frontmatter };
390
+ if (type === "container") {
391
+ if (normalized.container_type === void 0 && normalized.containerType !== void 0) {
392
+ normalized.container_type = normalized.containerType;
393
+ delete normalized.containerType;
394
+ }
395
+ if (normalized.access_mode === void 0 && normalized.accessMode !== void 0) {
396
+ normalized.access_mode = normalized.accessMode;
397
+ delete normalized.accessMode;
398
+ }
399
+ }
400
+ return normalized;
401
+ }
402
+ var RESOURCE_TYPE_FROM_FOLDER = {
403
+ events: "event",
404
+ commands: "command",
405
+ queries: "query",
406
+ services: "service",
407
+ domains: "domain",
408
+ channels: "channel",
409
+ flows: "flow",
410
+ containers: "container",
411
+ "data-products": "dataProduct",
412
+ diagrams: "diagram",
413
+ users: "user",
414
+ teams: "team"
415
+ };
416
+ async function parseDSL(source, options) {
417
+ const { createEcServices, compile } = await import("@eventcatalog/language-server");
418
+ const { EmptyFileSystem, URI } = await import("langium");
419
+ const services = createEcServices(EmptyFileSystem);
420
+ const uri = URI.parse(`file:///import-${Date.now()}.ec`);
421
+ const document = services.shared.workspace.LangiumDocumentFactory.fromString(source, uri);
422
+ services.shared.workspace.LangiumDocuments.addDocument(document);
423
+ await services.shared.workspace.DocumentBuilder.build([document]);
424
+ const parserErrors = document.parseResult.parserErrors;
425
+ if (parserErrors.length > 0) {
426
+ const messages = parserErrors.map((e) => ` Line ${e.token?.startLine ?? "?"}: ${e.message}`);
427
+ throw new Error(`Parse errors:
428
+ ${messages.join("\n")}`);
429
+ }
430
+ const program2 = document.parseResult.value;
431
+ const outputs = compile(program2, { nested: options?.nested });
432
+ try {
433
+ services.shared.workspace.LangiumDocuments.deleteDocument(uri);
434
+ } catch {
435
+ }
436
+ return { outputs, program: program2 };
437
+ }
438
+ function extractResourceTypeFolder(path) {
439
+ const segments = path.split("/");
440
+ let lastTypeFolder = segments[0];
441
+ for (const seg of segments) {
442
+ if (RESOURCE_TYPE_FROM_FOLDER[seg]) {
443
+ lastTypeFolder = seg;
444
+ }
445
+ }
446
+ return lastTypeFolder;
447
+ }
448
+ function parseCompiledOutput(output) {
449
+ const { data: frontmatter, content: markdown } = (0, import_gray_matter.default)(output.content);
450
+ const typeFolder = extractResourceTypeFolder(output.path);
451
+ const type = RESOURCE_TYPE_FROM_FOLDER[typeFolder] || typeFolder;
452
+ return {
453
+ type,
454
+ id: frontmatter.id,
455
+ version: frontmatter.version,
456
+ frontmatter,
457
+ markdown: markdown.trim(),
458
+ path: output.path
459
+ };
460
+ }
461
+ var MESSAGE_TYPE_FOLDER = {
462
+ event: "events",
463
+ command: "commands",
464
+ query: "queries",
465
+ channel: "channels"
466
+ };
467
+ var DEFAULT_STUB_VERSION = "0.0.1";
468
+ var NO_VERSION_KEY = "__no_version__";
469
+ function getResourceNameKey(type, id) {
470
+ return `${type}:${id}`;
471
+ }
472
+ function getResourceVersionKey(type, id, version2) {
473
+ return `${type}:${id}@${version2 || NO_VERSION_KEY}`;
474
+ }
475
+ function hasReferenceStatements(source) {
476
+ return /\b(?:sends|receives|writes-to|reads-from)\b/.test(source);
477
+ }
478
+ async function extractMessageStubs(program2, compiledIds, nested = false) {
479
+ const stubs = [];
480
+ const stubIds = /* @__PURE__ */ new Set();
481
+ function processDefinitions(definitions, parentPath = "") {
482
+ for (const def of definitions) {
483
+ if (def.$type === "VisualizerDef" && def.body) {
484
+ processDefinitions(def.body, parentPath);
485
+ continue;
486
+ }
487
+ if (def.$type === "DomainDef") {
488
+ const domainPath = nested ? `domains/${def.name}` : "";
489
+ const domainBody = def.body || [];
490
+ const domainServices = domainBody.filter((d) => d.$type === "ServiceDef");
491
+ processDefinitions(domainServices, domainPath);
492
+ const subdomains = domainBody.filter((d) => d.$type === "SubdomainDef");
493
+ for (const sub of subdomains) {
494
+ const subPath = nested ? `domains/${def.name}/subdomains/${sub.name}` : "";
495
+ const subServices = (sub.body || []).filter((d) => d.$type === "ServiceDef");
496
+ processDefinitions(subServices, subPath);
497
+ }
498
+ continue;
499
+ }
500
+ if (def.$type !== "ServiceDef") continue;
501
+ const servicePath = nested ? parentPath ? `${parentPath}/services/${def.name}` : `services/${def.name}` : "";
502
+ const body = def.body || [];
503
+ for (const stmt of body) {
504
+ if (stmt.$type === "SendsStmt" || stmt.$type === "ReceivesStmt") {
505
+ const msgType = stmt.messageType;
506
+ const msgName = stmt.messageName;
507
+ const hasBody = stmt.body && stmt.body.length > 0;
508
+ const version2 = stmt.version || DEFAULT_STUB_VERSION;
509
+ if (!hasBody) {
510
+ const folder = MESSAGE_TYPE_FOLDER[msgType];
511
+ if (folder) {
512
+ const key = getResourceVersionKey(msgType, msgName, version2);
513
+ const anyVersionKey = getResourceNameKey(msgType, msgName);
514
+ if (!compiledIds.has(key) && !stubIds.has(key) && !(!stmt.version && compiledIds.has(anyVersionKey))) {
515
+ const stubFolder = nested && servicePath ? `${servicePath}/${folder}` : folder;
516
+ stubIds.add(key);
517
+ stubs.push({
518
+ type: msgType,
519
+ id: msgName,
520
+ version: version2,
521
+ frontmatter: {
522
+ id: msgName,
523
+ name: msgName,
524
+ version: version2
525
+ },
526
+ markdown: "",
527
+ path: `${stubFolder}/${msgName}/versioned/${version2}/index.md`
528
+ });
529
+ }
530
+ }
531
+ }
532
+ if (stmt.channelClause) {
533
+ const channels = stmt.channelClause.channels || [];
534
+ for (const ch of channels) {
535
+ const chName = ch.channelName;
536
+ const chVersion = ch.channelVersion || DEFAULT_STUB_VERSION;
537
+ const chKey = getResourceVersionKey("channel", chName, chVersion);
538
+ const chAnyVersionKey = getResourceNameKey("channel", chName);
539
+ if (compiledIds.has(chKey) || stubIds.has(chKey)) continue;
540
+ if (!ch.channelVersion && compiledIds.has(chAnyVersionKey)) continue;
541
+ const chFolder = nested && parentPath ? `${parentPath}/channels` : "channels";
542
+ stubIds.add(chKey);
543
+ stubs.push({
544
+ type: "channel",
545
+ id: chName,
546
+ version: chVersion,
547
+ frontmatter: {
548
+ id: chName,
549
+ name: chName,
550
+ version: chVersion
551
+ },
552
+ markdown: "",
553
+ path: `${chFolder}/${chName}/versioned/${chVersion}/index.md`
554
+ });
555
+ }
556
+ }
557
+ continue;
558
+ }
559
+ if (stmt.$type === "WritesToStmt" || stmt.$type === "ReadsFromStmt") {
560
+ const containerName = stmt.ref?.name;
561
+ if (!containerName) continue;
562
+ const containerVersion = stmt.ref?.version || DEFAULT_STUB_VERSION;
563
+ const containerKey = getResourceVersionKey("container", containerName, containerVersion);
564
+ const containerAnyVersionKey = getResourceNameKey("container", containerName);
565
+ if (compiledIds.has(containerKey) || stubIds.has(containerKey)) continue;
566
+ if (!stmt.ref?.version && compiledIds.has(containerAnyVersionKey)) continue;
567
+ const containerFolder = nested && parentPath ? `${parentPath}/containers` : "containers";
568
+ stubIds.add(containerKey);
569
+ stubs.push({
570
+ type: "container",
571
+ id: containerName,
572
+ version: containerVersion,
573
+ frontmatter: {
574
+ id: containerName,
575
+ name: containerName,
576
+ version: containerVersion
577
+ },
578
+ markdown: "",
579
+ path: `${containerFolder}/${containerName}/versioned/${containerVersion}/index.md`
580
+ });
581
+ }
582
+ }
583
+ }
584
+ }
585
+ processDefinitions(program2.definitions);
586
+ return stubs;
587
+ }
588
+ function getVersionFromBody(body) {
589
+ if (!body) return void 0;
590
+ const versionStmt = body.find((stmt) => stmt?.$type === "VersionStmt");
591
+ return versionStmt?.value;
592
+ }
593
+ function buildServiceOutputPath(serviceName, body, nested, parentPath) {
594
+ const folder = nested && parentPath ? `${parentPath}/services` : "services";
595
+ const version2 = getVersionFromBody(body);
596
+ if (version2) return `${folder}/${serviceName}/versioned/${version2}/index.md`;
597
+ return `${folder}/${serviceName}/index.md`;
598
+ }
599
+ function extractServiceContainerRefs(program2, nested = false) {
600
+ const refsByPath = /* @__PURE__ */ new Map();
601
+ function processDefinitions(definitions, parentPath = "") {
602
+ for (const def of definitions || []) {
603
+ if (def.$type === "VisualizerDef" && def.body) {
604
+ processDefinitions(def.body, parentPath);
605
+ continue;
606
+ }
607
+ if (def.$type === "DomainDef") {
608
+ const domainPath = nested ? `domains/${def.name}` : "";
609
+ const domainBody = def.body || [];
610
+ const domainServices = domainBody.filter((d) => d.$type === "ServiceDef");
611
+ processDefinitions(domainServices, domainPath);
612
+ const subdomains = domainBody.filter((d) => d.$type === "SubdomainDef");
613
+ for (const sub of subdomains) {
614
+ const subPath = nested ? `domains/${def.name}/subdomains/${sub.name}` : "";
615
+ const subServices = (sub.body || []).filter((d) => d.$type === "ServiceDef");
616
+ processDefinitions(subServices, subPath);
617
+ }
618
+ continue;
619
+ }
620
+ if (def.$type !== "ServiceDef") continue;
621
+ const body = def.body || [];
622
+ const writesTo = body.filter((stmt) => stmt.$type === "WritesToStmt" && stmt.ref?.name).map((stmt) => ({
623
+ id: stmt.ref.name,
624
+ ...stmt.ref.version ? { version: stmt.ref.version } : {}
625
+ }));
626
+ const readsFrom = body.filter((stmt) => stmt.$type === "ReadsFromStmt" && stmt.ref?.name).map((stmt) => ({
627
+ id: stmt.ref.name,
628
+ ...stmt.ref.version ? { version: stmt.ref.version } : {}
629
+ }));
630
+ if (writesTo.length === 0 && readsFrom.length === 0) continue;
631
+ const path = buildServiceOutputPath(def.name, body, nested, parentPath);
632
+ refsByPath.set(path, {
633
+ ...writesTo.length > 0 ? { writesTo } : {},
634
+ ...readsFrom.length > 0 ? { readsFrom } : {}
635
+ });
636
+ }
637
+ }
638
+ processDefinitions(program2.definitions || []);
639
+ return refsByPath;
640
+ }
641
+ var SDK_WRITER_BASE = {
642
+ event: "events",
643
+ command: "commands",
644
+ query: "queries",
645
+ service: "services",
646
+ domain: "domains",
647
+ channel: "channels",
648
+ container: "containers",
649
+ dataProduct: "data-products",
650
+ diagram: "diagrams",
651
+ team: "teams",
652
+ user: "users"
653
+ };
654
+ function extractSdkPath(compiledPath, resourceType) {
655
+ const dirPath = compiledPath.replace(/\/versioned\/[^/]+\/index\.md$/, "").replace(/\/index\.md$/, "").replace(/\.md$/, "");
656
+ const baseFolder = SDK_WRITER_BASE[resourceType];
657
+ if (!baseFolder) return "";
658
+ if (dirPath.startsWith(`${baseFolder}/`)) {
659
+ const relative = dirPath.slice(baseFolder.length + 1);
660
+ if (!relative.includes("/")) return "";
661
+ return relative;
662
+ }
663
+ return `../${dirPath}`;
664
+ }
665
+ var TYPES_WITH_NODE_GRAPH = /* @__PURE__ */ new Set(["event", "command", "query", "service", "domain", "channel", "flow"]);
666
+ function getWriter(sdk, type) {
667
+ switch (type) {
668
+ case "event":
669
+ return sdk.writeEvent;
670
+ case "command":
671
+ return sdk.writeCommand;
672
+ case "query":
673
+ return sdk.writeQuery;
674
+ case "service":
675
+ return sdk.writeService;
676
+ case "domain":
677
+ return sdk.writeDomain;
678
+ case "channel":
679
+ return sdk.writeChannel;
680
+ case "container":
681
+ return sdk.writeDataStore;
682
+ case "dataProduct":
683
+ return sdk.writeDataProduct;
684
+ case "diagram":
685
+ return sdk.writeDiagram;
686
+ case "team":
687
+ return sdk.writeTeam;
688
+ case "user":
689
+ return sdk.writeUser;
690
+ default:
691
+ return null;
692
+ }
693
+ }
694
+ function getReader(sdk, type) {
695
+ switch (type) {
696
+ case "event":
697
+ return sdk.getEvent;
698
+ case "command":
699
+ return sdk.getCommand;
700
+ case "query":
701
+ return sdk.getQuery;
702
+ case "service":
703
+ return sdk.getService;
704
+ case "domain":
705
+ return sdk.getDomain;
706
+ case "channel":
707
+ return sdk.getChannel;
708
+ case "container":
709
+ return sdk.getDataStore;
710
+ case "dataProduct":
711
+ return sdk.getDataProduct;
712
+ case "diagram":
713
+ return sdk.getDiagram;
714
+ case "team":
715
+ return sdk.getTeam;
716
+ case "user":
717
+ return sdk.getUser;
718
+ default:
719
+ return null;
720
+ }
721
+ }
722
+ function promptConfirm(message) {
723
+ return new Promise((resolve4) => {
724
+ const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
725
+ rl.question(`${message} `, (answer) => {
726
+ rl.close();
727
+ const normalized = answer.trim().toLowerCase();
728
+ resolve4(normalized === "" || normalized === "y" || normalized === "yes");
729
+ });
730
+ });
731
+ }
732
+ function promptInput(message, defaultValue) {
733
+ return new Promise((resolve4) => {
734
+ const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
735
+ rl.question(`${message} `, (answer) => {
736
+ rl.close();
737
+ resolve4(answer.trim() || defaultValue);
738
+ });
739
+ });
740
+ }
741
+ function initCatalog(dir, organizationName = "My Organization") {
742
+ const catalogDir = (0, import_node_path2.resolve)(dir);
743
+ (0, import_node_fs3.mkdirSync)(catalogDir, { recursive: true });
744
+ const packageJson = {
745
+ name: "my-catalog",
746
+ version: "0.1.0",
747
+ private: true,
748
+ scripts: {
749
+ dev: "eventcatalog dev",
750
+ build: "eventcatalog build",
751
+ start: "eventcatalog start",
752
+ preview: "eventcatalog preview",
753
+ generate: "eventcatalog generate",
754
+ lint: "eventcatalog-linter",
755
+ test: 'echo "Error: no test specified" && exit 1'
756
+ },
757
+ dependencies: {
758
+ "@eventcatalog/core": "latest",
759
+ "@eventcatalog/linter": "latest"
760
+ }
761
+ };
762
+ (0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(catalogDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
763
+ const cId = (0, import_node_crypto.randomUUID)();
764
+ const config = `/** @type {import('@eventcatalog/core/bin/eventcatalog.config').Config} */
765
+ export default {
766
+ title: 'EventCatalog',
767
+ tagline: 'Discover, Explore and Document your Event Driven Architectures',
768
+ organizationName: '${organizationName}',
769
+ homepageLink: 'https://eventcatalog.dev/',
770
+ output: 'static',
771
+ trailingSlash: false,
772
+ base: '/',
773
+ logo: {
774
+ alt: 'EventCatalog Logo',
775
+ src: '/logo.png',
776
+ text: 'EventCatalog',
777
+ },
778
+ cId: '${cId}',
779
+ };
780
+ `;
781
+ (0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(catalogDir, "eventcatalog.config.js"), config, "utf-8");
782
+ const gitignore = `# Dependencies
783
+ /node_modules
784
+
785
+ # Production
786
+ /build
787
+
788
+ # Generated files
789
+ .astro
790
+ out
791
+ dist
792
+
793
+ # Misc
794
+ .DS_Store
795
+ .env.local
796
+ .env.development.local
797
+ .env.test.local
798
+ .env.production.local
799
+
800
+ npm-debug.log*
801
+ yarn-debug.log*
802
+ yarn-error.log*
803
+
804
+ .eventcatalog-core
805
+
806
+ .env
807
+ .env-*
808
+ `;
809
+ (0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(catalogDir, ".gitignore"), gitignore, "utf-8");
810
+ const envFile = `# EventCatalog Scale License Key, if you want to unlock the scale features
811
+ # You can get a 14 day trial license key from https://eventcatalog.cloud
812
+
813
+ EVENTCATALOG_SCALE_LICENSE_KEY=
814
+
815
+ # Optional key if you are using EventCatalog Chat with OpenAI Models.
816
+ # You need to set \`output\` to \`server\` in your eventcatalog.config.js file.
817
+ # See documentation for more details: https://www.eventcatalog.dev/features/ai-assistant
818
+ OPENAI_API_KEY=
819
+ `;
820
+ (0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(catalogDir, ".env"), envFile, "utf-8");
821
+ (0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(catalogDir, ".npmrc"), "strict-peer-dependencies=false\n", "utf-8");
822
+ (0, import_node_fs3.mkdirSync)((0, import_node_path2.join)(catalogDir, "public"), { recursive: true });
823
+ (0, import_node_fs3.copyFileSync)((0, import_node_path2.join)(__dirname, "logo.png"), (0, import_node_path2.join)(catalogDir, "public", "logo.png"));
824
+ }
825
+ async function importDSL(options) {
826
+ const { files, stdin = false, dryRun = false, flat = false, noInit = false, dir } = options;
827
+ const nested = !flat;
828
+ let source;
829
+ if (stdin) {
830
+ source = await readStdin();
831
+ } else if (files && files.length > 0) {
832
+ const parts = [];
833
+ for (const file of files) {
834
+ const filepath = (0, import_node_path2.resolve)(file);
835
+ if (!(0, import_node_fs3.existsSync)(filepath)) {
836
+ throw new Error(`File not found: ${filepath}`);
837
+ }
838
+ parts.push((0, import_node_fs3.readFileSync)(filepath, "utf-8"));
839
+ }
840
+ source = parts.join("\n\n");
841
+ } else {
842
+ throw new Error("Either provide .ec file paths or use --stdin");
843
+ }
844
+ if (!source.trim()) {
845
+ throw new Error("No DSL content to import");
846
+ }
847
+ const catalogDir = (0, import_node_path2.resolve)(dir);
848
+ let didInit = false;
849
+ if (!noInit && !(0, import_node_fs3.existsSync)((0, import_node_path2.join)(catalogDir, "eventcatalog.config.js")) && process.stdin.isTTY) {
850
+ const confirmed = await promptConfirm(`Initialize a new EventCatalog at ${catalogDir}? (Y/n)`);
851
+ if (confirmed) {
852
+ const organizationName = await promptInput("Organization name (My Organization):", "My Organization");
853
+ initCatalog(dir, organizationName);
854
+ didInit = true;
855
+ }
856
+ }
857
+ const parsed = await parseDSL(source, { nested });
858
+ const outputs = parsed.outputs;
859
+ if (outputs.length === 0) {
860
+ throw new Error("No resources found in DSL content");
861
+ }
862
+ const sdk = (0, import_sdk4.default)(catalogDir);
863
+ const result = { created: [], updated: [], versioned: [], errors: [] };
864
+ const readerCache = /* @__PURE__ */ new Map();
865
+ const readResourceCached = async (reader, type, id, version2) => {
866
+ if (!reader) return void 0;
867
+ const cacheKey = getResourceVersionKey(type, id, version2);
868
+ if (!readerCache.has(cacheKey)) {
869
+ readerCache.set(
870
+ cacheKey,
871
+ reader(id, version2).catch(() => void 0)
872
+ );
873
+ }
874
+ return await readerCache.get(cacheKey);
875
+ };
876
+ const invalidateReaderCache = (type, id, version2) => {
877
+ readerCache.delete(getResourceVersionKey(type, id));
878
+ readerCache.delete(getResourceVersionKey(type, id, version2));
879
+ };
880
+ const resources = outputs.map(parseCompiledOutput);
881
+ const serviceContainerRefsByPath = extractServiceContainerRefs(parsed.program, nested);
882
+ for (const resource of resources) {
883
+ if (resource.type !== "service") continue;
884
+ const refs = serviceContainerRefsByPath.get(resource.path);
885
+ if (!refs) continue;
886
+ resource.frontmatter = {
887
+ ...resource.frontmatter,
888
+ ...refs
889
+ };
890
+ }
891
+ const compiledIds = /* @__PURE__ */ new Set();
892
+ for (const resource of resources) {
893
+ compiledIds.add(getResourceNameKey(resource.type, resource.id));
894
+ compiledIds.add(getResourceVersionKey(resource.type, resource.id, resource.version));
895
+ }
896
+ const stubs = hasReferenceStatements(source) ? await extractMessageStubs(parsed.program, compiledIds, nested) : [];
897
+ resources.push(...stubs);
898
+ for (const resource of resources) {
899
+ const label = resource.version ? `${resource.type} ${resource.id}@${resource.version}` : `${resource.type} ${resource.id}`;
900
+ if (dryRun) {
901
+ const reader = getReader(sdk, resource.type);
902
+ if (reader) {
903
+ const existing = await readResourceCached(reader, resource.type, resource.id, resource.version);
904
+ if (existing) {
905
+ result.updated.push(label);
906
+ } else {
907
+ const latest = await readResourceCached(reader, resource.type, resource.id);
908
+ if (latest && latest.version && latest.version !== resource.version) {
909
+ result.versioned.push(`${resource.type} ${resource.id}@${latest.version}`);
910
+ }
911
+ result.created.push(label);
912
+ }
913
+ } else {
914
+ result.created.push(label);
915
+ }
916
+ continue;
917
+ }
918
+ const writer = getWriter(sdk, resource.type);
919
+ if (!writer) {
920
+ result.errors.push(`${label}: unsupported resource type '${resource.type}'`);
921
+ continue;
922
+ }
923
+ try {
924
+ const reader = getReader(sdk, resource.type);
925
+ const existing = await readResourceCached(reader, resource.type, resource.id, resource.version);
926
+ const latest = !existing && resource.version ? await readResourceCached(reader, resource.type, resource.id) : void 0;
927
+ const versionedFrom = !existing && resource.version && latest?.version && latest.version !== resource.version ? latest.version : void 0;
928
+ const incomingMarkdown = resource.markdown;
929
+ const hasIncomingMarkdown = incomingMarkdown.trim().length > 0;
930
+ let markdown = incomingMarkdown;
931
+ if (!hasIncomingMarkdown) {
932
+ if (existing?.markdown) {
933
+ markdown = existing.markdown;
934
+ } else if (!existing && latest?.markdown) {
935
+ markdown = latest.markdown;
936
+ }
937
+ }
938
+ if (!existing && TYPES_WITH_NODE_GRAPH.has(resource.type)) {
939
+ if (!markdown) {
940
+ markdown = "<NodeGraph />";
941
+ } else if (!markdown.includes("<NodeGraph />")) {
942
+ markdown = `${markdown}
943
+
944
+ <NodeGraph />`;
945
+ }
946
+ }
947
+ const resourceData = {
948
+ ...normalizeImportedFrontmatter(resource.type, resource.frontmatter),
949
+ markdown
950
+ };
951
+ const writeOptions = {
952
+ override: true,
953
+ versionExistingContent: Boolean(versionedFrom)
954
+ };
955
+ if (!existing && nested) {
956
+ const sdkPath = extractSdkPath(resource.path, resource.type);
957
+ if (sdkPath) {
958
+ writeOptions.path = sdkPath;
959
+ }
960
+ }
961
+ await writer(resourceData, writeOptions);
962
+ invalidateReaderCache(resource.type, resource.id, resource.version);
963
+ if (existing) {
964
+ result.updated.push(label);
965
+ } else {
966
+ result.created.push(label);
967
+ if (versionedFrom) {
968
+ result.versioned.push(`${resource.type} ${resource.id}@${versionedFrom}`);
969
+ }
970
+ }
971
+ } catch (error) {
972
+ result.errors.push(`${label}: ${error instanceof Error ? error.message : String(error)}`);
973
+ }
974
+ }
975
+ let output = formatResult(result, dryRun);
976
+ if (didInit) {
977
+ output += ` Tip: Run 'npm install' in ${catalogDir} to install dependencies
978
+ `;
979
+ }
980
+ return output;
981
+ }
982
+ var c = {
983
+ reset: "\x1B[0m",
984
+ bold: "\x1B[1m",
985
+ dim: "\x1B[2m",
986
+ green: "\x1B[32m",
987
+ yellow: "\x1B[33m",
988
+ blue: "\x1B[34m",
989
+ magenta: "\x1B[35m",
990
+ cyan: "\x1B[36m",
991
+ red: "\x1B[31m",
992
+ white: "\x1B[37m",
993
+ gray: "\x1B[90m"
994
+ };
995
+ var TYPE_CONFIG = {
996
+ domain: { color: c.magenta, label: "domain", order: 0 },
997
+ service: { color: c.blue, label: "service", order: 1 },
998
+ event: { color: c.green, label: "event", order: 2 },
999
+ command: { color: c.yellow, label: "command", order: 3 },
1000
+ query: { color: c.cyan, label: "query", order: 4 },
1001
+ channel: { color: c.gray, label: "channel", order: 5 },
1002
+ flow: { color: c.white, label: "flow", order: 6 },
1003
+ container: { color: c.white, label: "container", order: 7 },
1004
+ dataProduct: { color: c.white, label: "data product", order: 8 },
1005
+ diagram: { color: c.white, label: "diagram", order: 9 },
1006
+ user: { color: c.blue, label: "user", order: 10 },
1007
+ team: { color: c.blue, label: "team", order: 11 }
1008
+ };
1009
+ var DEFAULT_TYPE_CONFIG = { color: c.white, label: "resource", order: 99 };
1010
+ function parseLabel(label) {
1011
+ const spaceIdx = label.indexOf(" ");
1012
+ if (spaceIdx === -1) return { type: "", name: label };
1013
+ return { type: label.slice(0, spaceIdx), name: label.slice(spaceIdx + 1) };
1014
+ }
1015
+ function groupByType(labels) {
1016
+ const groups = /* @__PURE__ */ new Map();
1017
+ for (const label of labels) {
1018
+ const { type, name } = parseLabel(label);
1019
+ if (!groups.has(type)) groups.set(type, []);
1020
+ groups.get(type).push(name);
1021
+ }
1022
+ const sorted = new Map(
1023
+ [...groups.entries()].sort((a, b) => {
1024
+ const orderA = (TYPE_CONFIG[a[0]] || DEFAULT_TYPE_CONFIG).order;
1025
+ const orderB = (TYPE_CONFIG[b[0]] || DEFAULT_TYPE_CONFIG).order;
1026
+ return orderA - orderB;
1027
+ })
1028
+ );
1029
+ for (const [, names] of sorted) {
1030
+ names.sort();
1031
+ }
1032
+ return sorted;
1033
+ }
1034
+ function typeBadge(type) {
1035
+ const cfg = TYPE_CONFIG[type] || DEFAULT_TYPE_CONFIG;
1036
+ const padded = ` ${cfg.label} `;
1037
+ return `${cfg.color}\x1B[7m${padded}${c.reset}`;
1038
+ }
1039
+ function formatResourceList(labels) {
1040
+ const lines = [];
1041
+ const groups = groupByType(labels);
1042
+ for (const [type, names] of groups) {
1043
+ const cfg = TYPE_CONFIG[type] || DEFAULT_TYPE_CONFIG;
1044
+ const plural = names.length === 1 ? cfg.label : `${cfg.label}s`;
1045
+ lines.push("");
1046
+ lines.push(` ${typeBadge(type)} ${c.dim}${names.length} ${plural}${c.reset}`);
1047
+ for (const name of names) {
1048
+ lines.push(` ${cfg.color}\u2502${c.reset} ${name}`);
1049
+ }
1050
+ }
1051
+ return lines;
1052
+ }
1053
+ function formatResult(result, dryRun) {
1054
+ const lines = [""];
1055
+ const prefix = dryRun ? `${c.yellow}${c.bold}DRY RUN${c.reset} ` : "";
1056
+ const total = result.created.length + result.updated.length + result.versioned.length;
1057
+ if (total > 0 || result.errors.length > 0) {
1058
+ lines.push(` ${prefix}${c.bold}Import complete${c.reset}`);
1059
+ lines.push(` ${c.dim}${"\u2500".repeat(40)}${c.reset}`);
1060
+ }
1061
+ if (result.created.length > 0) {
1062
+ const verb = dryRun ? "Would create" : "Created";
1063
+ lines.push("");
1064
+ lines.push(` ${c.green}${c.bold}+ ${verb} ${result.created.length} resource(s)${c.reset}`);
1065
+ lines.push(...formatResourceList(result.created));
1066
+ }
1067
+ if (result.updated.length > 0) {
1068
+ const verb = dryRun ? "Would update" : "Updated";
1069
+ lines.push("");
1070
+ lines.push(` ${c.blue}${c.bold}~ ${verb} ${result.updated.length} resource(s)${c.reset}`);
1071
+ lines.push(...formatResourceList(result.updated));
1072
+ }
1073
+ if (result.versioned.length > 0) {
1074
+ const verb = dryRun ? "Would version" : "Versioned";
1075
+ lines.push("");
1076
+ lines.push(` ${c.yellow}${c.bold}\u2191 ${verb} ${result.versioned.length} existing resource(s)${c.reset}`);
1077
+ lines.push(...formatResourceList(result.versioned));
1078
+ }
1079
+ if (result.errors.length > 0) {
1080
+ lines.push("");
1081
+ lines.push(` ${c.red}${c.bold}\u2718 ${result.errors.length} error(s)${c.reset}`);
1082
+ for (const e of result.errors) {
1083
+ lines.push(` ${c.red}\u2502${c.reset} ${c.red}${e}${c.reset}`);
1084
+ }
1085
+ }
1086
+ if (result.created.length === 0 && result.updated.length === 0 && result.errors.length === 0) {
1087
+ lines.push(` ${c.dim}No resources to write.${c.reset}`);
1088
+ }
1089
+ lines.push("");
1090
+ return lines.join("\n");
1091
+ }
1092
+ function readStdin() {
1093
+ return new Promise((resolve4, reject) => {
1094
+ const chunks = [];
1095
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
1096
+ process.stdin.on("end", () => resolve4(Buffer.concat(chunks).toString("utf-8")));
1097
+ process.stdin.on("error", reject);
1098
+ });
1099
+ }
1100
+
355
1101
  // src/cli/index.ts
356
1102
  var version = "1.0.0";
357
1103
  try {
358
- const packageJsonPath = (0, import_node_path2.resolve)(__dirname, "../../package.json");
359
- const packageJson = JSON.parse((0, import_node_fs3.readFileSync)(packageJsonPath, "utf-8"));
1104
+ const packageJsonPath = (0, import_node_path3.resolve)(__dirname, "../../package.json");
1105
+ const packageJson = JSON.parse((0, import_node_fs4.readFileSync)(packageJsonPath, "utf-8"));
360
1106
  version = packageJson.version;
361
1107
  } catch {
362
1108
  }
@@ -371,7 +1117,7 @@ import_commander.program.command("list").description("List all available SDK fun
371
1117
  process.exit(1);
372
1118
  }
373
1119
  });
374
- import_commander.program.command("export").description("Export a catalog resource to EventCatalog DSL (.ec) format").option("-a, --all", "Export the entire catalog (all resource types)", false).option("-r, --resource <type>", "Resource type (event, command, query, service, domain)").option("--id <id>", "Resource ID (omit to export all resources of the given type)").option("-v, --version <version>", "Resource version (defaults to latest)").option("--hydrate", "Include referenced resources (messages, channels, owners)", false).option("--stdout", "Print to stdout instead of writing a file", false).option("--playground", "Open the exported DSL in the playground", false).option("-o, --output <path>", "Output file path (defaults to <id>.ec or catalog.ec)").action(async (opts) => {
1120
+ import_commander.program.command("export").description("Export a catalog resource to EventCatalog DSL (.ec) format").option("-a, --all", "Export the entire catalog (all resource types)", false).option("-r, --resource <type>", "Resource type (event, command, query, service, domain)").option("--id <id>", "Resource ID (omit to export all resources of the given type)").option("-v, --version <version>", "Resource version (defaults to latest)").option("--hydrate", "Include referenced resources (messages, channels, owners)", false).option("--stdout", "Print to stdout instead of writing a file", false).option("--playground", "Open the exported DSL in EventCatalog Modelling", false).option("-o, --output <path>", "Output file path (defaults to <id>.ec or catalog.ec)").action(async (opts) => {
375
1121
  try {
376
1122
  const globalOpts = import_commander.program.opts();
377
1123
  const dir = globalOpts.dir || ".";
@@ -406,6 +1152,24 @@ import_commander.program.command("export").description("Export a catalog resourc
406
1152
  process.exit(1);
407
1153
  }
408
1154
  });
1155
+ import_commander.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) => {
1156
+ try {
1157
+ const globalOpts = import_commander.program.opts();
1158
+ const dir = globalOpts.dir || ".";
1159
+ const result = await importDSL({
1160
+ files: files.length > 0 ? files : void 0,
1161
+ stdin: opts.stdin,
1162
+ dryRun: opts.dryRun,
1163
+ flat: opts.flat,
1164
+ noInit: !opts.init,
1165
+ dir
1166
+ });
1167
+ console.log(result);
1168
+ } catch (error) {
1169
+ console.error(error instanceof Error ? error.message : String(error));
1170
+ process.exit(1);
1171
+ }
1172
+ });
409
1173
  import_commander.program.arguments("<function> [args...]").action(async (functionName, args) => {
410
1174
  try {
411
1175
  const options = import_commander.program.opts();