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