@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.
- package/README.md +2 -1
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-CXZKUSOU.js → chunk-2ZXSFW7J.js} +1 -1
- package/dist/chunk-622JYJWG.js +109 -0
- package/dist/{chunk-C7L3FLQL.js → chunk-6MJGAOPK.js} +1 -1
- package/dist/chunk-BH3JMNAV.js +12 -0
- package/dist/{chunk-WAWMXWSY.js → chunk-GGFP7ZBX.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +139 -24
- package/dist/eventcatalog.js +9 -3
- package/dist/migrations/index.cjs +150 -0
- package/dist/migrations/index.d.cts +3 -0
- package/dist/migrations/index.d.ts +3 -0
- package/dist/migrations/index.js +7 -0
- package/dist/migrations/message-channels-to-service-channels.cjs +139 -0
- package/dist/migrations/message-channels-to-service-channels.d.cts +6 -0
- package/dist/migrations/message-channels-to-service-channels.d.ts +6 -0
- package/dist/migrations/message-channels-to-service-channels.js +6 -0
- package/eventcatalog/src/components/MDX/NodeGraph/Edges/AnimatedMessageEdge.tsx +42 -28
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewerRoot.astro +1 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +41 -35
- package/eventcatalog/src/content.config.ts +31 -3
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +0 -4
- package/eventcatalog/src/hooks/eventcatalog-visualizer.ts +35 -15
- package/eventcatalog/src/utils/channels.ts +73 -1
- package/eventcatalog/src/utils/collections/util.ts +7 -0
- package/eventcatalog/src/utils/node-graphs/channel-node-graph.ts +75 -0
- package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +856 -61
- package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +46 -70
- package/eventcatalog/src/utils/node-graphs/utils/utils.ts +26 -80
- 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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
75
|
-
source:
|
|
76
|
-
target:
|
|
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
|
+
};
|