@eventcatalog/cli 0.3.3 → 0.4.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## @eventcatalog/cli
2
2
 
3
- Command-line interface for [EventCatalog](https://eventcatalog.dev). Execute catalog operations directly from your terminal to automate your EventCatalog.
3
+ Command-line interface for [EventCatalog](https://eventcatalog.dev). Import and export catalogs using the [EventCatalog DSL](https://www.eventcatalog.dev/docs/development/dsl/introduction), run SDK functions directly from your terminal, and automate your EventCatalog workflows.
4
4
 
5
5
  ### Installation
6
6
 
@@ -11,7 +11,7 @@ npm i @eventcatalog/cli
11
11
  ### Running with npx (no installation required)
12
12
 
13
13
  ```bash
14
- npx @eventcatalog/cli --dir <catalog-path> <function-name> [args...]
14
+ npx @eventcatalog/cli --dir <catalog-path> <command> [args...]
15
15
  ```
16
16
 
17
17
  ### Running after installation
@@ -19,77 +19,180 @@ npx @eventcatalog/cli --dir <catalog-path> <function-name> [args...]
19
19
  If you've installed the package, the `eventcatalog` command is available:
20
20
 
21
21
  ```bash
22
- eventcatalog --dir <catalog-path> <function-name> [args...]
22
+ eventcatalog --dir <catalog-path> <command> [args...]
23
23
  ```
24
24
 
25
- ### Common Operations
25
+ ### Global Options
26
26
 
27
- **List all available functions:**
27
+ | Option | Description | Default |
28
+ | ------------------ | ------------------------------ | ----------------------- |
29
+ | `-d, --dir <path>` | Path to your catalog directory | `.` (current directory) |
30
+
31
+ ---
32
+
33
+ ### Commands
34
+
35
+ #### `import` — Import DSL files into your catalog
36
+
37
+ Parse `.ec` (EventCatalog DSL) files and write them as catalog resources (markdown + frontmatter).
28
38
 
29
39
  ```bash
30
- npx @eventcatalog/cli list
40
+ eventcatalog import [files...] [options]
31
41
  ```
32
42
 
33
- **Get an event:**
43
+ **Options:**
44
+
45
+ | Option | Description |
46
+ | ----------- | ------------------------------------------------------------ |
47
+ | `--stdin` | Read DSL from stdin instead of files |
48
+ | `--dry-run` | Preview changes without writing to disk |
49
+ | `--flat` | Write all resources at the top level (no nested directories) |
50
+ | `--no-init` | Skip the interactive catalog scaffolding prompt |
51
+
52
+ **Examples:**
34
53
 
35
54
  ```bash
36
- npx @eventcatalog/cli --dir ./my-catalog getEvent "OrderCreated"
37
- npx @eventcatalog/cli --dir ./my-catalog getEvent "OrderCreated" "1.0.0"
55
+ # Import a single DSL file
56
+ eventcatalog import architecture.ec
57
+
58
+ # Import multiple files
59
+ eventcatalog import services.ec events.ec domains.ec
60
+
61
+ # Pipe DSL from another tool
62
+ cat architecture.ec | eventcatalog import --stdin
63
+
64
+ # Preview what would change
65
+ eventcatalog import architecture.ec --dry-run
66
+
67
+ # Import without nesting services inside domains
68
+ eventcatalog import architecture.ec --flat
38
69
  ```
39
70
 
40
- **Get all events:**
71
+ **Behaviors:**
72
+
73
+ - If no `eventcatalog.config.js` exists, you'll be prompted to scaffold a new catalog (skip with `--no-init`).
74
+ - Importing a newer version of an existing resource automatically versions the old one.
75
+ - Re-importing the same version overwrites the existing resource.
76
+ - Referenced resources that aren't defined in the DSL (e.g., `sends event OrderCreated` without an inline body) are created as stubs at version `0.0.1`.
77
+ - Existing resource locations are preserved — updates go to where the resource already lives.
78
+
79
+ ---
80
+
81
+ #### `export` — Export catalog resources to DSL
82
+
83
+ Convert catalog resources back into EventCatalog DSL (`.ec`) format.
41
84
 
42
85
  ```bash
43
- npx @eventcatalog/cli --dir ./my-catalog getEvents
44
- npx @eventcatalog/cli --dir ./my-catalog getEvents '{"latestOnly":true}'
86
+ eventcatalog export [options]
45
87
  ```
46
88
 
47
- **Write an event:**
89
+ **Options:**
90
+
91
+ | Option | Description |
92
+ | --------------------- | -------------------------------------------------------------------------------------------- |
93
+ | `--all` | Export the entire catalog |
94
+ | `--resource <type>` | Resource type to export (`event`, `command`, `query`, `service`, `domain`, `channel`) |
95
+ | `--id <id>` | Export a specific resource by ID (requires `--resource`) |
96
+ | `--version <version>` | Export a specific version (requires `--resource` and `--id`) |
97
+ | `--hydrate` | Include referenced resources (e.g., messages referenced by a service) |
98
+ | `--stdout` | Print to stdout instead of writing a file |
99
+ | `--playground` | Open the exported DSL in the [EventCatalog Playground](https://playground.eventcatalog.dev/) |
100
+ | `--output <path>` | Custom output file path |
101
+
102
+ **Examples:**
48
103
 
49
104
  ```bash
50
- npx @eventcatalog/cli --dir ./my-catalog writeEvent '{"id":"OrderCreated","name":"Order Created","version":"1.0.0","markdown":"# Order Created Event"}'
105
+ # Export a single event
106
+ eventcatalog export --resource event --id OrderCreated --stdout
107
+
108
+ # Export all services with their referenced messages
109
+ eventcatalog export --resource service --hydrate --stdout
110
+
111
+ # Export the entire catalog to a file
112
+ eventcatalog export --all --output catalog.ec
113
+
114
+ # Export and open in the playground
115
+ eventcatalog export --all --playground
51
116
  ```
52
117
 
53
- **Get a service:**
118
+ ---
119
+
120
+ #### `list` — List available SDK functions
121
+
122
+ Display all SDK functions organized by category (Events, Commands, Queries, Services, Domains, etc.).
54
123
 
55
124
  ```bash
56
- npx @eventcatalog/cli --dir ./my-catalog getService "InventoryService"
125
+ eventcatalog list
57
126
  ```
58
127
 
59
- **Add an event to a service:**
128
+ ---
129
+
130
+ #### `<function>` — Run any SDK function
131
+
132
+ Any unrecognized command is treated as an SDK function call. Output is JSON.
60
133
 
61
134
  ```bash
62
- npx @eventcatalog/cli --dir ./my-catalog addEventToService "InventoryService" "sends" '{"id":"OrderCreated","version":"1.0.0"}'
135
+ eventcatalog <function-name> [args...]
63
136
  ```
64
137
 
65
- ### Piping to Other Tools
138
+ **Examples:**
139
+
140
+ ```bash
141
+ # Get an event (latest version)
142
+ eventcatalog --dir ./my-catalog getEvent "OrderCreated"
143
+
144
+ # Get a specific version
145
+ eventcatalog --dir ./my-catalog getEvent "OrderCreated" "1.0.0"
146
+
147
+ # Get all events with options
148
+ eventcatalog --dir ./my-catalog getEvents '{"latestOnly":true}'
66
149
 
67
- Output is JSON by default, making it easy to pipe to tools like `jq`:
150
+ # Write an event
151
+ eventcatalog --dir ./my-catalog writeEvent '{"id":"OrderCreated","name":"Order Created","version":"1.0.0","markdown":"# Order Created"}'
152
+
153
+ # Get a service
154
+ eventcatalog --dir ./my-catalog getService "InventoryService"
155
+
156
+ # Add an event to a service
157
+ eventcatalog --dir ./my-catalog addEventToService "InventoryService" "sends" '{"id":"OrderCreated","version":"1.0.0"}'
158
+ ```
159
+
160
+ Run `eventcatalog list` to see all available functions.
161
+
162
+ ---
163
+
164
+ ### Piping and Composing
165
+
166
+ Output from SDK functions is JSON, making it easy to pipe to tools like `jq`:
68
167
 
69
168
  ```bash
70
- # Get all events and filter by version
71
- npx @eventcatalog/cli --dir ./my-catalog getEvents | jq '.[] | select(.version == "1.0.0")'
169
+ # Filter events by version
170
+ eventcatalog --dir ./my-catalog getEvents | jq '.[] | select(.version == "1.0.0")'
72
171
 
73
172
  # Count total events
74
- npx @eventcatalog/cli --dir ./my-catalog getEvents | jq 'length'
173
+ eventcatalog --dir ./my-catalog getEvents | jq 'length'
75
174
 
76
175
  # Extract event IDs
77
- npx @eventcatalog/cli --dir ./my-catalog getEvents | jq '.[].id'
176
+ eventcatalog --dir ./my-catalog getEvents | jq '.[].id'
78
177
  ```
79
178
 
80
179
  ### Arguments Format
81
180
 
82
181
  Arguments are automatically parsed:
83
182
 
84
- - **JSON objects:** `'{"key":"value"}'` - parsed as object
85
- - **JSON arrays:** `'["item1","item2"]'` - parsed as array
86
- - **Booleans:** `true` or `false` - parsed as boolean
87
- - **Numbers:** `42` or `3.14` - parsed as number
88
- - **Strings:** anything else - kept as string
183
+ - **JSON objects:** `'{"key":"value"}'` parsed as object
184
+ - **JSON arrays:** `'["item1","item2"]'` parsed as array
185
+ - **Booleans:** `true` or `false` parsed as boolean
186
+ - **Numbers:** `42` or `3.14` parsed as number
187
+ - **Strings:** anything else kept as string
188
+
189
+ ### Documentation
89
190
 
90
- See the [SDK docs](https://www.eventcatalog.dev/docs/sdk) for more information and examples.
191
+ - [EventCatalog DSL](https://www.eventcatalog.dev/docs/development/dsl/introduction)
192
+ - [SDK reference](https://www.eventcatalog.dev/docs/sdk)
193
+ - [CLI documentation](https://www.eventcatalog.dev/docs/development/cli)
91
194
 
92
- # Enterprise support
195
+ ## Enterprise support
93
196
 
94
197
  Interested in collaborating with us? Our offerings include dedicated support, priority assistance, feature development, custom integrations, and more.
95
198
 
package/dist/cli/index.js CHANGED
@@ -250,19 +250,45 @@ ${items.join("\n\n")}`);
250
250
  }
251
251
  return sections.join("\n\n");
252
252
  }
253
- function buildVisualizerBlock(dsl, name, filterTypes) {
254
- const types = Array.isArray(filterTypes) ? filterTypes : [filterTypes];
255
- const entries = [];
256
- for (const line of dsl.split("\n")) {
257
- const trimmed = line.trimStart();
253
+ function extractResourceDefinitions(dsl, filterTypes) {
254
+ const results = [];
255
+ const lines = dsl.split("\n");
256
+ for (let i = 0; i < lines.length; i++) {
257
+ const trimmed = lines[i].trimStart();
258
258
  const parts = trimmed.split(/\s/);
259
259
  const keyword = parts[0];
260
260
  const id = parts[1];
261
- if (!types.includes(keyword)) continue;
261
+ if (!filterTypes.includes(keyword)) continue;
262
262
  if (!id || !trimmed.endsWith("{")) continue;
263
- entries.push(` ${keyword} ${id}`);
263
+ let version2;
264
+ for (let j = i + 1; j < lines.length; j++) {
265
+ const inner = lines[j].trim();
266
+ if (inner === "}") break;
267
+ const vMatch = inner.match(/^version\s+(.+)$/);
268
+ if (vMatch) {
269
+ version2 = vMatch[1];
270
+ break;
271
+ }
272
+ }
273
+ results.push({ keyword, id, version: version2 });
274
+ }
275
+ return results;
276
+ }
277
+ function buildVisualizerBlock(dsl, name, filterTypes) {
278
+ const types = Array.isArray(filterTypes) ? filterTypes : [filterTypes];
279
+ const definitions = extractResourceDefinitions(dsl, types);
280
+ if (definitions.length === 0) return "";
281
+ const counts = /* @__PURE__ */ new Map();
282
+ for (const def of definitions) {
283
+ const key = `${def.keyword}:${def.id}`;
284
+ counts.set(key, (counts.get(key) || 0) + 1);
264
285
  }
265
- if (entries.length === 0) return "";
286
+ const entries = definitions.map((def) => {
287
+ const key = `${def.keyword}:${def.id}`;
288
+ const needsVersion = (counts.get(key) || 0) > 1 && def.version;
289
+ const ref = needsVersion ? `${def.id}@${def.version}` : def.id;
290
+ return ` ${def.keyword} ${ref}`;
291
+ });
266
292
  return `
267
293
  visualizer main {
268
294
  name "${name}"
@@ -356,7 +382,8 @@ async function exportResource(options) {
356
382
  }
357
383
  const rawDsl = await sdk.toDSL(data, { type, hydrate });
358
384
  const grouped = hydrate ? groupDSLBlocks(rawDsl) : rawDsl;
359
- const vizBlock = buildVisualizerBlock(grouped, `View of ${id}`, type);
385
+ const vizTypes = hydrate ? [...RESOURCE_TYPES] : [type];
386
+ const vizBlock = buildVisualizerBlock(grouped, `View of ${id}`, vizTypes);
360
387
  const dsl = vizBlock ? `${grouped}
361
388
  ${vizBlock}` : grouped;
362
389
  if (stdout) {
@@ -385,6 +412,17 @@ var import_node_crypto = require("crypto");
385
412
  var import_node_readline = require("readline");
386
413
  var import_gray_matter = __toESM(require("gray-matter"));
387
414
  var import_sdk4 = __toESM(require("@eventcatalog/sdk"));
415
+
416
+ // src/cli/dsl-managed-keys.ts
417
+ var MESSAGE_MANAGED_KEYS = /* @__PURE__ */ new Set(["id", "name", "version", "owners", "deprecated", "draft", "summary"]);
418
+ var DSL_MANAGED_KEYS_BY_TYPE = {
419
+ domain: /* @__PURE__ */ new Set(["id", "name", "version", "owners", "deprecated", "draft", "summary", "services", "domains"]),
420
+ event: MESSAGE_MANAGED_KEYS,
421
+ command: MESSAGE_MANAGED_KEYS,
422
+ query: MESSAGE_MANAGED_KEYS
423
+ };
424
+
425
+ // src/cli/import.ts
388
426
  function normalizeImportedFrontmatter(type, frontmatter) {
389
427
  const normalized = { ...frontmatter };
390
428
  if (type === "container") {
@@ -399,6 +437,27 @@ function normalizeImportedFrontmatter(type, frontmatter) {
399
437
  }
400
438
  return normalized;
401
439
  }
440
+ function toFrontmatter(resource) {
441
+ if (!resource || typeof resource !== "object") return {};
442
+ const { markdown: _markdown, ...frontmatter } = resource;
443
+ return frontmatter;
444
+ }
445
+ function mergeImportedFrontmatter(type, incomingFrontmatter, existing, latest) {
446
+ const normalizedIncoming = normalizeImportedFrontmatter(type, incomingFrontmatter);
447
+ const managedKeys = DSL_MANAGED_KEYS_BY_TYPE[type];
448
+ if (!managedKeys) {
449
+ return normalizedIncoming;
450
+ }
451
+ const baseFrontmatter = toFrontmatter(existing ?? latest);
452
+ const preservedFrontmatter = { ...baseFrontmatter };
453
+ for (const key of managedKeys) {
454
+ delete preservedFrontmatter[key];
455
+ }
456
+ return {
457
+ ...preservedFrontmatter,
458
+ ...normalizedIncoming
459
+ };
460
+ }
402
461
  var RESOURCE_TYPE_FROM_FOLDER = {
403
462
  events: "event",
404
463
  commands: "command",
@@ -945,7 +1004,7 @@ async function importDSL(options) {
945
1004
  }
946
1005
  }
947
1006
  const resourceData = {
948
- ...normalizeImportedFrontmatter(resource.type, resource.frontmatter),
1007
+ ...mergeImportedFrontmatter(resource.type, resource.frontmatter, existing, latest),
949
1008
  markdown
950
1009
  };
951
1010
  const writeOptions = {