@eventcatalog/core 3.28.3 → 3.29.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.
@@ -37,7 +37,7 @@ var import_axios = __toESM(require("axios"), 1);
37
37
  var import_os = __toESM(require("os"), 1);
38
38
 
39
39
  // package.json
40
- var version = "3.28.3";
40
+ var version = "3.29.0";
41
41
 
42
42
  // src/constants.ts
43
43
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-SBZBCN3T.js";
4
- import "../chunk-L36OEOCB.js";
3
+ } from "../chunk-XN2XNAOY.js";
4
+ import "../chunk-ZY6MUMIK.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -111,7 +111,7 @@ var import_axios = __toESM(require("axios"), 1);
111
111
  var import_os = __toESM(require("os"), 1);
112
112
 
113
113
  // package.json
114
- var version = "3.28.3";
114
+ var version = "3.29.0";
115
115
 
116
116
  // src/constants.ts
117
117
  var VERSION = version;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-JLGWTWR4.js";
4
- import "../chunk-SBZBCN3T.js";
3
+ } from "../chunk-NKN2LYFC.js";
4
+ import "../chunk-XN2XNAOY.js";
5
5
  import "../chunk-4UVFXLPI.js";
6
- import "../chunk-L36OEOCB.js";
6
+ import "../chunk-ZY6MUMIK.js";
7
7
  import "../chunk-5T63CXKU.js";
8
8
  export {
9
9
  log_build_default as default
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-LLC66YM2.js";
3
+ } from "./chunk-B3DM4RWS.js";
4
4
  import {
5
5
  cleanup,
6
6
  getEventCatalogConfigFile
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-L36OEOCB.js";
3
+ } from "./chunk-ZY6MUMIK.js";
4
4
 
5
5
  // src/utils/cli-logger.ts
6
6
  import pc from "picocolors";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "./chunk-SBZBCN3T.js";
3
+ } from "./chunk-XN2XNAOY.js";
4
4
  import {
5
5
  countResources,
6
6
  serializeCounts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-L36OEOCB.js";
3
+ } from "./chunk-ZY6MUMIK.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import axios from "axios";
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "3.28.3";
2
+ var version = "3.29.0";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "3.28.3";
28
+ var version = "3.29.0";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-L36OEOCB.js";
3
+ } from "./chunk-ZY6MUMIK.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -114,7 +114,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
114
114
  var import_picocolors = __toESM(require("picocolors"), 1);
115
115
 
116
116
  // package.json
117
- var version = "3.28.3";
117
+ var version = "3.29.0";
118
118
 
119
119
  // src/constants.ts
120
120
  var VERSION = version;
@@ -6,8 +6,8 @@ import {
6
6
  } from "./chunk-PLNJC7NZ.js";
7
7
  import {
8
8
  log_build_default
9
- } from "./chunk-JLGWTWR4.js";
10
- import "./chunk-SBZBCN3T.js";
9
+ } from "./chunk-NKN2LYFC.js";
10
+ import "./chunk-XN2XNAOY.js";
11
11
  import "./chunk-4UVFXLPI.js";
12
12
  import {
13
13
  runMigrations
@@ -22,13 +22,13 @@ import {
22
22
  } from "./chunk-3KXCGYET.js";
23
23
  import {
24
24
  generate
25
- } from "./chunk-KP324TGM.js";
25
+ } from "./chunk-3YZL6CMP.js";
26
26
  import {
27
27
  logger
28
- } from "./chunk-LLC66YM2.js";
28
+ } from "./chunk-B3DM4RWS.js";
29
29
  import {
30
30
  VERSION
31
- } from "./chunk-L36OEOCB.js";
31
+ } from "./chunk-ZY6MUMIK.js";
32
32
  import {
33
33
  getEventCatalogConfigFile,
34
34
  verifyRequiredFieldsAreInCatalogConfigFile
package/dist/generate.cjs CHANGED
@@ -78,7 +78,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
78
78
  var import_picocolors = __toESM(require("picocolors"), 1);
79
79
 
80
80
  // package.json
81
- var version = "3.28.3";
81
+ var version = "3.29.0";
82
82
 
83
83
  // src/constants.ts
84
84
  var VERSION = version;
package/dist/generate.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generate
3
- } from "./chunk-KP324TGM.js";
4
- import "./chunk-LLC66YM2.js";
5
- import "./chunk-L36OEOCB.js";
3
+ } from "./chunk-3YZL6CMP.js";
4
+ import "./chunk-B3DM4RWS.js";
5
+ import "./chunk-ZY6MUMIK.js";
6
6
  import "./chunk-5T63CXKU.js";
7
7
  export {
8
8
  generate
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(cli_logger_exports);
36
36
  var import_picocolors = __toESM(require("picocolors"), 1);
37
37
 
38
38
  // package.json
39
- var version = "3.28.3";
39
+ var version = "3.29.0";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  logger
3
- } from "../chunk-LLC66YM2.js";
4
- import "../chunk-L36OEOCB.js";
3
+ } from "../chunk-B3DM4RWS.js";
4
+ import "../chunk-ZY6MUMIK.js";
5
5
  export {
6
6
  logger
7
7
  };
@@ -19,6 +19,18 @@ interface SchemaPropertyProps {
19
19
  expand: boolean;
20
20
  }
21
21
 
22
+ // JSON Schema allows `type` to be a string or an array of strings (e.g. ["object", "null"]).
23
+ // These helpers normalise both forms so the rest of the viewer can treat type checks uniformly.
24
+ function hasType(type: any, candidate: string): boolean {
25
+ if (Array.isArray(type)) return type.includes(candidate);
26
+ return type === candidate;
27
+ }
28
+
29
+ function formatType(type: any): string {
30
+ if (Array.isArray(type)) return type.join(' | ');
31
+ return type ?? '';
32
+ }
33
+
22
34
  // Helper function to count properties recursively
23
35
  function countProperties(obj: any): number {
24
36
  if (!obj || typeof obj !== 'object') return 0;
@@ -196,7 +208,7 @@ function processSchema(schema: any, rootSchema?: any): any {
196
208
  }
197
209
 
198
210
  // Process array items
199
- if (schema.type === 'array' && schema.items) {
211
+ if (hasType(schema.type, 'array') && schema.items) {
200
212
  schema = { ...schema, items: processSchema(schema.items, root) };
201
213
  }
202
214
 
@@ -269,11 +281,11 @@ const SchemaProperty = ({ name, details, isRequired, level, isListItem = false,
269
281
  setIsExpanded(expand);
270
282
  }, [expand]);
271
283
 
272
- const hasNestedProperties = details.type === 'object' && details.properties && Object.keys(details.properties).length > 0;
273
- const hasArrayItems = details.type === 'array' && details.items;
284
+ const hasNestedProperties = hasType(details.type, 'object') && details.properties && Object.keys(details.properties).length > 0;
285
+ const hasArrayItems = hasType(details.type, 'array') && details.items;
274
286
  const hasArrayItemProperties =
275
287
  hasArrayItems &&
276
- ((details.items.type === 'object' && details.items.properties) ||
288
+ ((hasType(details.items.type, 'object') && details.items.properties) ||
277
289
  details.items.allOf ||
278
290
  details.items.oneOf ||
279
291
  details.items.anyOf ||
@@ -307,8 +319,8 @@ const SchemaProperty = ({ name, details, isRequired, level, isListItem = false,
307
319
  <div>
308
320
  <span className="font-semibold text-[rgb(var(--ec-page-text))] text-sm">{name}</span>
309
321
  <span className="ml-1.5 text-[rgb(var(--ec-accent))] font-mono text-xs">
310
- {hasVariants ? (details.variantType === 'anyOf' ? 'anyOf' : 'oneOf') : details.type}
311
- {details.type === 'array' && details.items?.type ? `[${details.items.type}]` : ''}
322
+ {hasVariants ? (details.variantType === 'anyOf' ? 'anyOf' : 'oneOf') : formatType(details.type)}
323
+ {hasType(details.type, 'array') && details.items?.type ? `[${formatType(details.items.type)}]` : ''}
312
324
  {details.format ? `<${details.format}>` : ''}
313
325
  {details._refPath && (
314
326
  <span className="text-blue-600 dark:text-blue-400 ml-1">→ {details._refName || details._refPath}</span>
@@ -513,9 +525,9 @@ export default function JSONSchemaViewer({
513
525
  let display = processedSchema;
514
526
  let isArray = false;
515
527
 
516
- if (processedSchema.type === 'array' && processedSchema.items) {
528
+ if (hasType(processedSchema.type, 'array') && processedSchema.items) {
517
529
  isArray = true;
518
- if (processedSchema.items.type === 'object' && processedSchema.items.properties) {
530
+ if (hasType(processedSchema.items.type, 'object') && processedSchema.items.properties) {
519
531
  display = {
520
532
  ...processedSchema.items,
521
533
  description: processedSchema.description || processedSchema.items.description,
@@ -838,7 +850,9 @@ export default function JSONSchemaViewer({
838
850
  <div className="text-[rgb(var(--ec-page-text-muted))] text-sm">
839
851
  <p>
840
852
  This array contains items of type:{' '}
841
- <span className="font-mono text-blue-600 dark:text-blue-400">{processedSchema.items?.type || 'unknown'}</span>
853
+ <span className="font-mono text-blue-600 dark:text-blue-400">
854
+ {processedSchema.items?.type ? formatType(processedSchema.items.type) : 'unknown'}
855
+ </span>
842
856
  </p>
843
857
  {processedSchema.items?.description && (
844
858
  <p className="text-xs mt-2 text-[rgb(var(--ec-page-text-muted))]">{processedSchema.items.description}</p>
@@ -22,6 +22,12 @@ interface Props {
22
22
  renderAllEdges?: boolean;
23
23
  }
24
24
 
25
+ interface Maps {
26
+ messageMap: Map<string, any[]>;
27
+ serviceMap: Map<string, any[]>;
28
+ flowMap: Map<string, any[]>;
29
+ }
30
+
25
31
  const getServiceNode = (step: any, serviceMap: Map<string, any[]>) => {
26
32
  const service = findInMap(serviceMap, step.service.id, step.service.version);
27
33
  return {
@@ -49,53 +55,58 @@ const getMessageNode = (step: any, messageMap: Map<string, any[]>) => {
49
55
  };
50
56
  };
51
57
 
52
- export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simple', renderAllEdges = false }: Props) => {
53
- const graph = defaultFlow || createDagreGraph({ ranksep: 360, nodesep: 200 });
54
- const nodes = [] as any,
55
- edges = [] as any;
56
-
57
- // Fetch all collections in parallel
58
- const [flows, events, commands, queries, services] = await Promise.all([
59
- getCollection('flows'),
60
- getCollection('events'),
61
- getCollection('commands'),
62
- getCollection('queries'),
63
- getCollection('services'),
64
- ]);
65
-
66
- const flow = flows.find((flow) => flow.data.id === id && flow.data.version === version);
67
-
68
- // Nothing found...
69
- if (!flow) {
70
- return {
71
- nodes: [],
72
- edges: [],
73
- };
74
- }
58
+ // Rewrite every id/source/target in a precomputed sub-flow graph with a
59
+ // namespace prefix so inlined copies don't collide with the parent. Nested
60
+ // `data.expandedNodes` / `data.expandedEdges` payloads are rewritten too so
61
+ // the namespace chain stays unique when the same sub-flow is inlined under
62
+ // multiple parents.
63
+ const prefixGraph = (graph: { nodes: any[]; edges: any[] }, prefix: string) => {
64
+ if (!prefix) return graph;
65
+ const nodes = graph.nodes.map((n) => {
66
+ const next: any = { ...n, id: `${prefix}${n.id}` };
67
+ if (n.data?.expandedNodes || n.data?.expandedEdges) {
68
+ const nested = prefixGraph({ nodes: n.data.expandedNodes ?? [], edges: n.data.expandedEdges ?? [] }, prefix);
69
+ next.data = { ...n.data, expandedNodes: nested.nodes, expandedEdges: nested.edges };
70
+ }
71
+ return next;
72
+ });
73
+ const edges = graph.edges.map((e) => ({
74
+ ...e,
75
+ id: `${prefix}${e.id}`,
76
+ source: `${prefix}${e.source}`,
77
+ target: `${prefix}${e.target}`,
78
+ }));
79
+ return { nodes, edges };
80
+ };
75
81
 
76
- // Build maps for O(1) lookups
77
- const messages = [...events, ...commands, ...queries];
78
- const messageMap = createVersionedMap(messages);
79
- const serviceMap = createVersionedMap(services);
80
- const flowMap = createVersionedMap(flows);
82
+ // `subFlowCache` keys each flow's graph by `id@version` so an N-times
83
+ // referenced sub-flow is built once. `visited` short-circuits cycles.
84
+ const buildFlowGraphInternal = (
85
+ flow: any,
86
+ maps: Maps,
87
+ mode: 'simple' | 'full',
88
+ subFlowCache: Map<string, { nodes: any[]; edges: any[] }>,
89
+ visited: Set<string>
90
+ ) => {
91
+ const nodes: any[] = [];
92
+ const edges: any[] = [];
81
93
 
82
- const steps = flow?.data.steps || [];
94
+ const steps = flow?.data?.steps || [];
95
+ const stepNodeId = (stepId: any) => `step-${stepId}`;
83
96
 
84
- // Hydrate the steps with information they may need.
85
97
  const hydratedSteps = steps.map((step: any) => {
86
- if (step.service) return getServiceNode(step, serviceMap);
87
- if (step.flow) return getFlowNode(step, flowMap);
88
- if (step.message) return getMessageNode(step, messageMap);
98
+ if (step.service) return getServiceNode(step, maps.serviceMap);
99
+ if (step.flow) return getFlowNode(step, maps.flowMap);
100
+ if (step.message) return getMessageNode(step, maps.messageMap);
89
101
  if (step.actor) return { ...step, type: 'actor', actor: step.actor };
90
102
  if (step.custom) return { ...step, type: 'custom', custom: step.custom };
91
103
  if (step.externalSystem) return { ...step, type: 'externalSystem', externalSystem: step.externalSystem };
92
104
  return { ...step, type: 'step' };
93
105
  });
94
106
 
95
- // Create nodes
96
107
  hydratedSteps.forEach((step: any, index: number) => {
97
- const node = {
98
- id: `step-${step.id}`,
108
+ const node: NodeType = {
109
+ id: stepNodeId(step.id),
99
110
  sourcePosition: 'right',
100
111
  targetPosition: 'left',
101
112
  data: {
@@ -124,6 +135,21 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
124
135
  id: step.flow.data.id,
125
136
  version: step.flow.data.version,
126
137
  });
138
+
139
+ // Guard cycles; inline the sub-flow's graph so the client can expand on click.
140
+ const subFlowKey = `${step.flow.data.id}@${step.flow.data.version}`;
141
+ if (!visited.has(subFlowKey)) {
142
+ let cached = subFlowCache.get(subFlowKey);
143
+ if (!cached) {
144
+ cached = buildFlowGraphInternal(step.flow, maps, mode, subFlowCache, new Set([...visited, subFlowKey]));
145
+ subFlowCache.set(subFlowKey, cached);
146
+ }
147
+ if (cached.nodes.length > 0) {
148
+ const { nodes: childNodes, edges: childEdges } = prefixGraph(cached, `${node.id}__`);
149
+ node.data.expandedNodes = childNodes;
150
+ node.data.expandedEdges = childEdges;
151
+ }
152
+ }
127
153
  }
128
154
  if (step.message) {
129
155
  node.data.message = { ...step.message, ...step.message.data };
@@ -142,12 +168,10 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
142
168
  nodes.push(node);
143
169
  });
144
170
 
145
- // Create Edges
146
- hydratedSteps.forEach((step: any, index: number) => {
171
+ hydratedSteps.forEach((step: any) => {
147
172
  let paths = step.next_steps || [];
148
173
 
149
174
  if (step.next_step) {
150
- // If its a string or number
151
175
  if (!step.next_step?.id) {
152
176
  paths = [{ id: step.next_step }];
153
177
  } else {
@@ -165,8 +189,8 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
165
189
  paths.forEach((path: any) => {
166
190
  edges.push({
167
191
  id: `step-${step.id}-step-${path.id}`,
168
- source: `step-${step.id}`,
169
- target: `step-${path.id}`,
192
+ source: stepNodeId(step.id),
193
+ target: stepNodeId(path.id),
170
194
  type: 'flow-edge',
171
195
  label: path.label,
172
196
  animated: true,
@@ -184,6 +208,45 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
184
208
  });
185
209
  });
186
210
 
211
+ return { nodes, edges };
212
+ };
213
+
214
+ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simple', renderAllEdges = false }: Props) => {
215
+ const graph = defaultFlow || createDagreGraph({ ranksep: 360, nodesep: 200 });
216
+
217
+ const [flows, events, commands, queries, services] = await Promise.all([
218
+ getCollection('flows'),
219
+ getCollection('events'),
220
+ getCollection('commands'),
221
+ getCollection('queries'),
222
+ getCollection('services'),
223
+ ]);
224
+
225
+ const flow = flows.find((flow) => flow.data.id === id && flow.data.version === version);
226
+
227
+ if (!flow) {
228
+ return {
229
+ nodes: [],
230
+ edges: [],
231
+ };
232
+ }
233
+
234
+ const messages = [...events, ...commands, ...queries];
235
+ const maps: Maps = {
236
+ messageMap: createVersionedMap(messages),
237
+ serviceMap: createVersionedMap(services),
238
+ flowMap: createVersionedMap(flows),
239
+ };
240
+
241
+ const subFlowCache = new Map<string, { nodes: any[]; edges: any[] }>();
242
+ const { nodes, edges } = buildFlowGraphInternal(
243
+ flow,
244
+ maps,
245
+ mode,
246
+ subFlowCache,
247
+ new Set([`${flow.data.id}@${flow.data.version}`])
248
+ );
249
+
187
250
  nodes.forEach((node: any) => {
188
251
  graph.setNode(node.id, { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT });
189
252
  });
@@ -192,7 +255,6 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
192
255
  graph.setEdge(edge.source, edge.target);
193
256
  });
194
257
 
195
- // Render the diagram in memory getting hte X and Y
196
258
  dagre.layout(graph);
197
259
 
198
260
  return {
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "SEE LICENSE IN LICENSE",
9
9
  "type": "module",
10
- "version": "3.28.3",
10
+ "version": "3.29.0",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -105,7 +105,7 @@
105
105
  "zod": "^4.3.6",
106
106
  "@eventcatalog/linter": "1.0.21",
107
107
  "@eventcatalog/sdk": "2.20.0",
108
- "@eventcatalog/visualiser": "^3.18.4"
108
+ "@eventcatalog/visualiser": "^3.19.0"
109
109
  },
110
110
  "devDependencies": {
111
111
  "@astrojs/check": "^0.9.8",