@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.
- package/dist/cli/index.js +584 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +584 -9
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/logo.png +0 -0
- package/dist/cli-docs.js +31 -0
- package/dist/cli-docs.js.map +1 -1
- package/dist/cli-docs.mjs +31 -0
- package/dist/cli-docs.mjs.map +1 -1
- package/package.json +5 -2
package/dist/cli/index.mjs
CHANGED
|
@@ -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
|
|
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 = `
|
|
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 (
|
|
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 = `
|
|
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 = `
|
|
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 =
|
|
343
|
-
const packageJson = JSON.parse(
|
|
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();
|