@eventcatalog/core 3.8.2 → 3.9.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.
Files changed (33) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-OCD75GFW.js → chunk-CZG5RQXY.js} +1 -1
  6. package/dist/{chunk-I7HRERRK.js → chunk-FM44RPBS.js} +1 -1
  7. package/dist/{chunk-MOBOWLEW.js → chunk-HJOMVPCL.js} +1 -1
  8. package/dist/{chunk-275AT7XV.js → chunk-OQYLI4YJ.js} +1 -1
  9. package/dist/{chunk-KBMXUUXX.js → chunk-UYBPI4UO.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +1 -1
  13. package/dist/eventcatalog.js +5 -5
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +50 -0
  19. package/eventcatalog/src/components/MDX/Design/Design.astro +4 -1
  20. package/eventcatalog/src/components/MDX/EntityMap/EntityMap.astro +4 -0
  21. package/eventcatalog/src/components/MDX/Flow/Flow.astro +4 -1
  22. package/eventcatalog/src/components/MDX/NodeGraph/MermaidView.tsx +240 -0
  23. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +4 -0
  24. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +333 -189
  25. package/eventcatalog/src/components/MDX/NodeGraph/VisualizerDropdownContent.tsx +224 -0
  26. package/eventcatalog/src/content.config.ts +1 -1
  27. package/eventcatalog/src/enterprise/ai/chat-api.ts +23 -0
  28. package/eventcatalog/src/enterprise/tools/catalog-tools.ts +96 -0
  29. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version].mermaid.ts +128 -0
  30. package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +4 -0
  31. package/eventcatalog/src/utils/clipboard.ts +22 -0
  32. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +299 -0
  33. package/package.json +1 -1
@@ -0,0 +1,299 @@
1
+ import type { Node, Edge } from '@xyflow/react';
2
+
3
+ /**
4
+ * Mermaid Export Utility
5
+ * Converts React Flow nodes and edges to Mermaid flowchart syntax
6
+ */
7
+
8
+ export interface MermaidExportOptions {
9
+ /** Include class definitions for styling */
10
+ includeStyles?: boolean;
11
+ /** Direction of the flowchart: LR (left-right), TB (top-bottom), etc. */
12
+ direction?: 'LR' | 'TB' | 'RL' | 'BT';
13
+ }
14
+
15
+ /**
16
+ * Mapping of node types to their Mermaid shape syntax
17
+ * Format: [prefix, suffix]
18
+ */
19
+ const NODE_SHAPE_MAP: Record<string, [string, string]> = {
20
+ services: ['[[', ']]'], // stadium shape
21
+ service: ['[[', ']]'],
22
+ events: ['>', ']'], // flag/asymmetric shape (message-like)
23
+ event: ['>', ']'],
24
+ commands: ['>', ']'], // flag/asymmetric shape (message-like)
25
+ command: ['>', ']'],
26
+ queries: ['{{', '}}'], // hexagon
27
+ query: ['{{', '}}'],
28
+ channels: ['[(', ')]'], // cylinder
29
+ channel: ['[(', ')]'],
30
+ domains: ['[', ']'], // rectangle
31
+ domain: ['[', ']'],
32
+ flows: ['([', '])'], // stadium (rounded)
33
+ flow: ['([', '])'],
34
+ step: ['[', ']'], // rectangle
35
+ user: ['((', '))'], // circle
36
+ actor: ['((', '))'], // circle
37
+ externalSystem: ['[[', ']]'], // stadium
38
+ 'external-system': ['[[', ']]'],
39
+ data: ['[(', ')]'], // cylinder (database)
40
+ 'data-product': ['[[', ']]'], // stadium
41
+ 'data-products': ['[[', ']]'],
42
+ entities: ['[', ']'], // rectangle
43
+ entity: ['[', ']'],
44
+ custom: ['[', ']'], // rectangle
45
+ view: ['[', ']'], // rectangle
46
+ note: ['[', ']'], // rectangle
47
+ };
48
+
49
+ /**
50
+ * Mermaid class definitions for styling different node types
51
+ */
52
+ const NODE_STYLE_CLASSES: Record<string, string> = {
53
+ services: 'fill:#ec4899,stroke:#be185d,color:#fff',
54
+ service: 'fill:#ec4899,stroke:#be185d,color:#fff',
55
+ events: 'fill:#f97316,stroke:#c2410c,color:#fff',
56
+ event: 'fill:#f97316,stroke:#c2410c,color:#fff',
57
+ commands: 'fill:#3b82f6,stroke:#1d4ed8,color:#fff',
58
+ command: 'fill:#3b82f6,stroke:#1d4ed8,color:#fff',
59
+ queries: 'fill:#22c55e,stroke:#15803d,color:#fff',
60
+ query: 'fill:#22c55e,stroke:#15803d,color:#fff',
61
+ channels: 'fill:#6b7280,stroke:#374151,color:#fff',
62
+ channel: 'fill:#6b7280,stroke:#374151,color:#fff',
63
+ domains: 'fill:#eab308,stroke:#a16207,color:#000',
64
+ domain: 'fill:#eab308,stroke:#a16207,color:#000',
65
+ flows: 'fill:#14b8a6,stroke:#0f766e,color:#fff',
66
+ flow: 'fill:#14b8a6,stroke:#0f766e,color:#fff',
67
+ step: 'fill:#374151,stroke:#1f2937,color:#fff',
68
+ user: 'fill:#8b5cf6,stroke:#6d28d9,color:#fff',
69
+ actor: 'fill:#eab308,stroke:#a16207,color:#000',
70
+ externalSystem: 'fill:#ec4899,stroke:#be185d,color:#fff',
71
+ 'external-system': 'fill:#ec4899,stroke:#be185d,color:#fff',
72
+ data: 'fill:#3b82f6,stroke:#1d4ed8,color:#fff',
73
+ 'data-product': 'fill:#6366f1,stroke:#4338ca,color:#fff',
74
+ 'data-products': 'fill:#6366f1,stroke:#4338ca,color:#fff',
75
+ entities: 'fill:#6b7280,stroke:#374151,color:#fff',
76
+ entity: 'fill:#6b7280,stroke:#374151,color:#fff',
77
+ custom: 'fill:#9ca3af,stroke:#6b7280,color:#000',
78
+ view: 'fill:#9ca3af,stroke:#6b7280,color:#000',
79
+ note: 'fill:#fef3c7,stroke:#d97706,color:#000',
80
+ };
81
+
82
+ /**
83
+ * Sanitize a string to be a valid Mermaid node ID
84
+ * Mermaid IDs should be alphanumeric with underscores
85
+ */
86
+ export function sanitizeMermaidId(id: string): string {
87
+ // Replace any non-alphanumeric characters (except underscores) with underscores
88
+ // Also handle dots and hyphens which are common in version strings
89
+ return id.replace(/[^a-zA-Z0-9_]/g, '_');
90
+ }
91
+
92
+ /**
93
+ * Escape special characters in Mermaid labels
94
+ */
95
+ function escapeMermaidLabel(label: string): string {
96
+ // Escape quotes and other special characters
97
+ return label.replace(/"/g, '#quot;').replace(/\n/g, '<br/>');
98
+ }
99
+
100
+ /**
101
+ * Get the Mermaid shape syntax for a node type
102
+ */
103
+ export function getMermaidNodeShape(type: string): [string, string] {
104
+ return NODE_SHAPE_MAP[type] || ['[', ']'];
105
+ }
106
+
107
+ /**
108
+ * Helper to format label with version
109
+ */
110
+ function formatLabelWithVersion(name: string, version?: string): string {
111
+ if (version) {
112
+ return `${name} (${version})`;
113
+ }
114
+ return name;
115
+ }
116
+
117
+ /**
118
+ * Extract the display label from a node based on its type and data
119
+ * Includes version information when available
120
+ */
121
+ export function getNodeLabel(node: Node): string {
122
+ const { type, data } = node;
123
+
124
+ if (!data) return node.id;
125
+
126
+ // Handle different node types and their data structures
127
+ if (type === 'services' || type === 'service') {
128
+ const service = (data as any).service;
129
+ const name = service?.name || service?.id || node.id;
130
+ const version = service?.data?.version || service?.version;
131
+ return formatLabelWithVersion(name, version);
132
+ }
133
+
134
+ if (
135
+ type === 'events' ||
136
+ type === 'event' ||
137
+ type === 'commands' ||
138
+ type === 'command' ||
139
+ type === 'queries' ||
140
+ type === 'query'
141
+ ) {
142
+ const message = (data as any).message;
143
+ const name = message?.name || message?.id || node.id;
144
+ const version = message?.data?.version || message?.version;
145
+ return formatLabelWithVersion(name, version);
146
+ }
147
+
148
+ if (type === 'channels' || type === 'channel') {
149
+ const channel = (data as any).channel;
150
+ const name = channel?.name || channel?.id || node.id;
151
+ const version = channel?.data?.version || channel?.version;
152
+ return formatLabelWithVersion(name, version);
153
+ }
154
+
155
+ if (type === 'domains' || type === 'domain') {
156
+ const domain = (data as any).domain;
157
+ // Domain data can be nested in .data
158
+ const domainData = domain?.data || domain;
159
+ const name = domainData?.name || domainData?.id || node.id;
160
+ const version = domainData?.version || domain?.version;
161
+ return formatLabelWithVersion(name, version);
162
+ }
163
+
164
+ if (type === 'flows' || type === 'flow') {
165
+ const flow = (data as any).flow;
166
+ const name = flow?.name || flow?.id || node.id;
167
+ const version = flow?.data?.version || flow?.version;
168
+ return formatLabelWithVersion(name, version);
169
+ }
170
+
171
+ if (type === 'step') {
172
+ const step = (data as any).step;
173
+ return step?.title || step?.name || step?.id || node.id;
174
+ }
175
+
176
+ if (type === 'user' || type === 'actor') {
177
+ return (data as any).name || (data as any).label || (data as any).id || node.id;
178
+ }
179
+
180
+ if (type === 'externalSystem' || type === 'external-system') {
181
+ const system = (data as any).externalSystem || data;
182
+ return system?.name || system?.id || node.id;
183
+ }
184
+
185
+ if (type === 'data') {
186
+ const dataNode = (data as any).data;
187
+ return dataNode?.name || dataNode?.id || node.id;
188
+ }
189
+
190
+ if (type === 'data-product' || type === 'data-products') {
191
+ const dataProduct = (data as any).dataProduct;
192
+ const name = dataProduct?.name || dataProduct?.id || node.id;
193
+ const version = dataProduct?.data?.version || dataProduct?.version;
194
+ return formatLabelWithVersion(name, version);
195
+ }
196
+
197
+ if (type === 'entities' || type === 'entity') {
198
+ const entity = (data as any).entity;
199
+ return entity?.name || entity?.id || node.id;
200
+ }
201
+
202
+ if (type === 'note') {
203
+ return (data as any).text || (data as any).label || 'Note';
204
+ }
205
+
206
+ // Fallback: try common data patterns
207
+ return (data as any).name || (data as any).label || (data as any).title || (data as any).id || node.id;
208
+ }
209
+
210
+ /**
211
+ * Extract label from an edge
212
+ */
213
+ function getEdgeLabel(edge: Edge): string | undefined {
214
+ if (edge.label && typeof edge.label === 'string') {
215
+ return edge.label;
216
+ }
217
+ if (edge.data?.label && typeof edge.data.label === 'string') {
218
+ return edge.data.label;
219
+ }
220
+ return undefined;
221
+ }
222
+
223
+ /**
224
+ * Convert React Flow nodes and edges to Mermaid flowchart syntax
225
+ */
226
+ export function convertToMermaid(nodes: Node[], edges: Edge[], options: MermaidExportOptions = {}): string {
227
+ const { includeStyles = true, direction = 'LR' } = options;
228
+
229
+ const lines: string[] = [];
230
+
231
+ // Add flowchart header
232
+ lines.push(`flowchart ${direction}`);
233
+
234
+ // Collect used node types for class definitions
235
+ const usedTypes = new Set<string>();
236
+
237
+ // Add class definitions if styles are enabled
238
+ if (includeStyles) {
239
+ lines.push('');
240
+ lines.push(' %% Style definitions');
241
+
242
+ // First pass: collect all used node types
243
+ nodes.forEach((node) => {
244
+ if (node.type && NODE_STYLE_CLASSES[node.type]) {
245
+ usedTypes.add(node.type);
246
+ }
247
+ });
248
+
249
+ // Add class definitions for used types
250
+ usedTypes.forEach((type) => {
251
+ const style = NODE_STYLE_CLASSES[type];
252
+ if (style) {
253
+ lines.push(` classDef ${sanitizeMermaidId(type)} ${style}`);
254
+ }
255
+ });
256
+ }
257
+
258
+ // Add node definitions
259
+ lines.push('');
260
+ lines.push(' %% Nodes');
261
+
262
+ nodes.forEach((node) => {
263
+ const sanitizedId = sanitizeMermaidId(node.id);
264
+ const label = escapeMermaidLabel(getNodeLabel(node));
265
+ const [prefix, suffix] = getMermaidNodeShape(node.type || 'custom');
266
+
267
+ let nodeLine = ` ${sanitizedId}${prefix}"${label}"${suffix}`;
268
+
269
+ // Add class reference if styles are enabled
270
+ if (includeStyles && node.type && usedTypes.has(node.type)) {
271
+ nodeLine += `:::${sanitizeMermaidId(node.type)}`;
272
+ }
273
+
274
+ lines.push(nodeLine);
275
+ });
276
+
277
+ // Add edge definitions
278
+ lines.push('');
279
+ lines.push(' %% Edges');
280
+
281
+ edges.forEach((edge) => {
282
+ const sourceId = sanitizeMermaidId(edge.source);
283
+ const targetId = sanitizeMermaidId(edge.target);
284
+ const label = getEdgeLabel(edge);
285
+
286
+ let edgeLine: string;
287
+ if (label) {
288
+ // Clean up multi-line labels
289
+ const cleanLabel = label.replace(/\n/g, ' ').trim();
290
+ edgeLine = ` ${sourceId} -->|"${escapeMermaidLabel(cleanLabel)}"| ${targetId}`;
291
+ } else {
292
+ edgeLine = ` ${sourceId} --> ${targetId}`;
293
+ }
294
+
295
+ lines.push(edgeLine);
296
+ });
297
+
298
+ return lines.join('\n');
299
+ }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/event-catalog/eventcatalog.git"
7
7
  },
8
8
  "type": "module",
9
- "version": "3.8.2",
9
+ "version": "3.9.0",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },