@eventcatalog/core 2.64.3 → 2.65.0-beta.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 (35) hide show
  1. package/README.md +2 -1
  2. package/dist/analytics/analytics.cjs +1 -1
  3. package/dist/analytics/analytics.js +2 -2
  4. package/dist/analytics/log-build.cjs +1 -1
  5. package/dist/analytics/log-build.js +3 -3
  6. package/dist/{chunk-CXZKUSOU.js → chunk-2ZXSFW7J.js} +1 -1
  7. package/dist/chunk-622JYJWG.js +109 -0
  8. package/dist/{chunk-C7L3FLQL.js → chunk-6MJGAOPK.js} +1 -1
  9. package/dist/chunk-BH3JMNAV.js +12 -0
  10. package/dist/{chunk-WAWMXWSY.js → chunk-GGFP7ZBX.js} +1 -1
  11. package/dist/constants.cjs +1 -1
  12. package/dist/constants.js +1 -1
  13. package/dist/eventcatalog.cjs +139 -24
  14. package/dist/eventcatalog.js +9 -3
  15. package/dist/migrations/index.cjs +150 -0
  16. package/dist/migrations/index.d.cts +3 -0
  17. package/dist/migrations/index.d.ts +3 -0
  18. package/dist/migrations/index.js +7 -0
  19. package/dist/migrations/message-channels-to-service-channels.cjs +139 -0
  20. package/dist/migrations/message-channels-to-service-channels.d.cts +6 -0
  21. package/dist/migrations/message-channels-to-service-channels.d.ts +6 -0
  22. package/dist/migrations/message-channels-to-service-channels.js +6 -0
  23. package/eventcatalog/src/components/MDX/NodeGraph/Edges/AnimatedMessageEdge.tsx +42 -28
  24. package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewerRoot.astro +1 -0
  25. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +41 -35
  26. package/eventcatalog/src/content.config.ts +31 -3
  27. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +0 -4
  28. package/eventcatalog/src/hooks/eventcatalog-visualizer.ts +35 -15
  29. package/eventcatalog/src/utils/channels.ts +73 -1
  30. package/eventcatalog/src/utils/collections/util.ts +7 -0
  31. package/eventcatalog/src/utils/node-graphs/channel-node-graph.ts +75 -0
  32. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +856 -61
  33. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +46 -70
  34. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +26 -80
  35. package/package.json +2 -2
@@ -27,8 +27,8 @@ export const useEventCatalogVisualiser = ({
27
27
  skipProcessing = false,
28
28
  }: EventCatalogVisualizerProps) => {
29
29
  const [hideChannels, setHideChannels] = useState(false);
30
- const [initialNodes] = useState(nodes);
31
- const [initialEdges] = useState(edges);
30
+ const [initialNodes, setInitialNodes] = useState(nodes);
31
+ const [initialEdges, setInitialEdges] = useState(edges);
32
32
 
33
33
  // Initialize hideChannels from localStorage
34
34
  useEffect(() => {
@@ -38,6 +38,14 @@ export const useEventCatalogVisualiser = ({
38
38
  }
39
39
  }, []);
40
40
 
41
+ useEffect(() => {
42
+ const hasChannels = nodes.some((node) => node.type === 'channels');
43
+ if (!hideChannels || hasChannels) {
44
+ setInitialNodes(nodes);
45
+ setInitialEdges(edges);
46
+ }
47
+ }, [nodes, edges, hideChannels]);
48
+
41
49
  const toggleChannelsVisibility = useCallback(() => {
42
50
  setHideChannels((prev) => {
43
51
  const newValue = !prev;
@@ -50,7 +58,7 @@ export const useEventCatalogVisualiser = ({
50
58
  const updatedNodes = useMemo(() => nodes.filter((node) => node.type !== 'channels'), [nodes]);
51
59
 
52
60
  const updatedEdges = useMemo(() => {
53
- return edges.reduce<Edge[]>((acc, edge) => {
61
+ const newEdges = edges.reduce<Edge[]>((acc, edge) => {
54
62
  const { source, target, data } = edge;
55
63
  const targetIsChannel = channels.some((channel) => channel.id === target);
56
64
  const sourceIsChannel = channels.some((channel) => channel.id === source);
@@ -59,30 +67,42 @@ export const useEventCatalogVisualiser = ({
59
67
  return [...acc, edge];
60
68
  }
61
69
 
62
- const dataTarget = data?.target as CollectionEntry<CollectionTypes>;
63
- const dataSource = data?.source as CollectionEntry<CollectionTypes>;
70
+ if (sourceIsChannel || targetIsChannel) {
71
+ const rootSourceAndTarget = data?.rootSourceAndTarget as {
72
+ source: { id: string; collection: string };
73
+ target: { id: string; collection: string };
74
+ };
75
+
76
+ if (!rootSourceAndTarget) {
77
+ return [...acc, edge];
78
+ }
64
79
 
65
- if (sourceIsChannel) {
66
- const edgeLabel =
67
- dataTarget?.collection === 'services'
68
- ? getEdgeLabelForMessageAsSource(dataSource as CollectionEntry<CollectionMessageTypes>)
69
- : getEdgeLabelForServiceAsTarget(dataTarget as CollectionEntry<CollectionMessageTypes>);
80
+ // is target services?
81
+ const targetIsService = rootSourceAndTarget?.target?.collection === 'services';
82
+ const edgeLabel = targetIsService
83
+ ? getEdgeLabelForMessageAsSource(rootSourceAndTarget.source as any)
84
+ : getEdgeLabelForServiceAsTarget(rootSourceAndTarget.target as any);
85
+
86
+ const newEdgeId = `${rootSourceAndTarget.source.id}-${rootSourceAndTarget.target.id}`;
70
87
 
71
88
  return [
72
89
  ...acc,
73
90
  createEdge({
74
- id: generatedIdForEdge(dataSource, dataTarget),
75
- source: generateIdForNode(dataSource),
76
- target: generateIdForNode(dataTarget),
91
+ id: newEdgeId,
92
+ source: rootSourceAndTarget.source.id,
93
+ target: rootSourceAndTarget.target.id,
77
94
  label: edgeLabel,
78
95
  }),
79
96
  ];
80
97
  }
81
-
82
- return [...acc, edge];
98
+ return acc;
83
99
  }, []);
100
+
101
+ return newEdges.filter((edge, index, self) => index === self.findIndex((t) => t.id === edge.id));
84
102
  }, [edges, channels]);
85
103
 
104
+ // console.log('UPDATED EDGES', JSON.stringify(updatedEdges, null, 2));
105
+
86
106
  useEffect(() => {
87
107
  // Skip processing if there are no channels to manage
88
108
  if (skipProcessing) {
@@ -1,7 +1,7 @@
1
1
  import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import path from 'path';
4
- import { getVersionForCollectionItem, satisfies } from './collections/util';
4
+ import { getItemsFromCollectionByIdAndSemverOrLatest, getVersionForCollectionItem, satisfies } from './collections/util';
5
5
  import { getMessages } from './messages';
6
6
  import type { CollectionMessageTypes } from '@types';
7
7
  import utils from '@eventcatalog/sdk';
@@ -92,3 +92,75 @@ export const getChannels = async ({ getAllVersions = true }: Props = {}): Promis
92
92
 
93
93
  return cachedChannels[cacheKey];
94
94
  };
95
+
96
+ // Could be recursive, we need to keep going until we find a loop or until we reach the target channel
97
+ export const isChannelsConnected = (
98
+ sourceChannel: CollectionEntry<'channels'>,
99
+ targetChannel: CollectionEntry<'channels'>,
100
+ channels: CollectionEntry<'channels'>[],
101
+ visited: Set<string> = new Set()
102
+ ) => {
103
+ // Create a unique key for this channel (id + version to handle multiple versions)
104
+ const channelKey = `${sourceChannel.data.id}:${sourceChannel.data.version}`;
105
+
106
+ // Base case: we've reached the target channel
107
+ if (sourceChannel.data.id === targetChannel.data.id) {
108
+ return true;
109
+ }
110
+
111
+ // Prevent infinite loops by tracking visited channels
112
+ if (visited.has(channelKey)) {
113
+ return false;
114
+ }
115
+
116
+ // Mark this channel as visited
117
+ visited.add(channelKey);
118
+
119
+ const routes = sourceChannel.data.routes ?? [];
120
+ for (const route of routes) {
121
+ const routeChannel = getItemsFromCollectionByIdAndSemverOrLatest(
122
+ channels,
123
+ route.id,
124
+ route.version
125
+ )[0] as CollectionEntry<'channels'>;
126
+
127
+ if (routeChannel) {
128
+ // Pass the visited set to the recursive call
129
+ if (isChannelsConnected(routeChannel, targetChannel, channels, visited)) {
130
+ return true;
131
+ }
132
+ }
133
+ }
134
+ return false;
135
+ };
136
+
137
+ // Go from the source to the target channel and return the channel chain
138
+ export const getChannelChain = (
139
+ sourceChannel: CollectionEntry<'channels'>,
140
+ targetChannel: CollectionEntry<'channels'>,
141
+ channels: CollectionEntry<'channels'>[]
142
+ ): CollectionEntry<'channels'>[] => {
143
+ // Base case: we've reached the target channel
144
+ if (sourceChannel.data.id === targetChannel.data.id && sourceChannel.data.version === targetChannel.data.version) {
145
+ return [sourceChannel];
146
+ }
147
+
148
+ const routes = sourceChannel.data.routes ?? [];
149
+
150
+ if (routes.length > 0 && isChannelsConnected(sourceChannel, targetChannel, channels)) {
151
+ // Need to check every route and see if any of them are connected to the target channel
152
+ for (const route of routes) {
153
+ const routeChannel = getItemsFromCollectionByIdAndSemverOrLatest(
154
+ channels,
155
+ route.id,
156
+ route.version
157
+ )[0] as CollectionEntry<'channels'>;
158
+ if (routeChannel) {
159
+ if (isChannelsConnected(routeChannel, targetChannel, channels)) {
160
+ return [sourceChannel, ...getChannelChain(routeChannel, targetChannel, channels)];
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return [];
166
+ };
@@ -42,6 +42,13 @@ export function sortVersioned<T>(versioned: T[], versionExtractor: (e: T) => str
42
42
  }
43
43
  }
44
44
 
45
+ // Takes a collection and a id of a resource, and checks if the version is the latest version in the collection
46
+ export const getLatestVersionInCollectionById = (collection: CollectionEntry<CollectionTypes>[], id: string) => {
47
+ const items = collection.filter((i) => i.data.id === id);
48
+ const sortedVersions = sortVersioned(items, (v) => v.data.version);
49
+ return sortedVersions[0]?.data.version ?? id;
50
+ };
51
+
45
52
  export const getVersionForCollectionItem = (
46
53
  item: CollectionEntry<CollectionTypes>,
47
54
  collection: CollectionEntry<CollectionTypes>[]
@@ -0,0 +1,75 @@
1
+ import type { CollectionEntry } from 'astro:content';
2
+ import { createEdge, createNode, generatedIdForEdge, generateIdForNode, getColorFromString } from './utils/utils';
3
+ import { type Node, type Edge } from '@xyflow/react';
4
+
5
+ interface CollectionItem {
6
+ collection: string;
7
+ data: any;
8
+ }
9
+
10
+ export const getNodesAndEdgesForChannelChain = ({
11
+ source,
12
+ target,
13
+ channelChain = [],
14
+ mode = 'simple',
15
+ }: {
16
+ source: CollectionItem;
17
+ target: CollectionItem;
18
+ channelChain: CollectionEntry<'channels'>[];
19
+ mode?: 'simple' | 'full';
20
+ }) => {
21
+ const nodes: Node[] = [];
22
+ const edges: Edge[] = [];
23
+
24
+ // We found a channel chain, we need to render all the channels in the chain
25
+ for (const channel of channelChain) {
26
+ nodes.push(
27
+ createNode({
28
+ id: generateIdForNode(channel),
29
+ type: channel.collection,
30
+ data: { mode, channel: { ...channel.data, ...channel } },
31
+ position: { x: 0, y: 0 },
32
+ })
33
+ );
34
+ }
35
+
36
+ // Connect the source to the first channel in the chain
37
+ edges.push(
38
+ createEdge({
39
+ id: generatedIdForEdge(source, channelChain[0]),
40
+ source: generateIdForNode(source),
41
+ target: generateIdForNode(channelChain[0]),
42
+ label: 'routes to',
43
+ data: { customColor: getColorFromString(source.data.id) },
44
+ })
45
+ );
46
+
47
+ // Make sure all the channels in the chain are connected together
48
+ for (const channel of channelChain) {
49
+ const index = channelChain.findIndex((c) => c.id === channel.id);
50
+ if (channelChain[index + 1]) {
51
+ edges.push(
52
+ createEdge({
53
+ id: generatedIdForEdge(channel, channelChain[index + 1]),
54
+ source: generateIdForNode(channel),
55
+ target: generateIdForNode(channelChain[index + 1]),
56
+ label: `routes to`,
57
+ data: { customColor: getColorFromString(source.data.id) },
58
+ })
59
+ );
60
+ }
61
+ }
62
+
63
+ // Connect the last channel to the target
64
+ edges.push(
65
+ createEdge({
66
+ id: generatedIdForEdge(channelChain[channelChain.length - 1], target),
67
+ source: generateIdForNode(channelChain[channelChain.length - 1]),
68
+ target: generateIdForNode(target),
69
+ label: 'consumes',
70
+ data: { customColor: getColorFromString(source.data.id) },
71
+ })
72
+ );
73
+
74
+ return { nodes, edges };
75
+ };