@eventcatalog/core 5.4.0 → 5.4.2
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/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-TERJXCLK.js → chunk-CQ4L4SMB.js} +1 -1
- package/dist/{chunk-PAS36QHK.js → chunk-EXRIIYEP.js} +1 -1
- package/dist/{chunk-OT2U5OES.js → chunk-HV4ONKTM.js} +1 -1
- package/dist/{chunk-OX6TSVHY.js → chunk-QJ7KKN2D.js} +1 -1
- package/dist/{chunk-EPDVXHC7.js → chunk-VSBMYXLL.js} +3 -3
- package/dist/{chunk-O2K4VPDO.js → chunk-XB6FJMI3.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +9 -9
- package/dist/federation/federate.js +3 -3
- package/dist/federation/source-provider.js +2 -2
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +3 -1
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +70 -70
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +68 -68
- package/eventcatalog/src/features/studio/api/canvases-api.ts +3 -1
- package/eventcatalog/src/features/studio/canvas-actions.ts +8 -2
- package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +4 -3
- package/eventcatalog/src/utils/node-graphs/system-levels-node-graph.ts +195 -9
- package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +48 -5
- package/package.json +4 -4
- package/dist/{chunk-6K7XZAYI.js → chunk-A2RZR3U4.js} +3 -3
|
@@ -20,7 +20,9 @@ import type { SignedInUser } from '../server/sign-in';
|
|
|
20
20
|
* "edges"?: [...] }, starting with the nodes and edges given, if any (as the
|
|
21
21
|
* add_to_canvas MCP tool takes them: catalog resources or new components, x/y
|
|
22
22
|
* their centres, and only the connections in edges are made, each drawn
|
|
23
|
-
* along its "route" if given: { "points": [{x, y}, ...], "label"?: {x, y}
|
|
23
|
+
* along its "route" if given: { "points": [{x, y}, ...], "label"?: {x, y},
|
|
24
|
+
* "sourceHeight"?: number, "targetHeight"?: number }, the heights its nodes
|
|
25
|
+
* were laid out at),
|
|
24
26
|
* and "levels" (L1 and L2 laid out, e.g. as a diagram shows them, by the
|
|
25
27
|
* nodes' refs), shown as given until what's on the canvas changes
|
|
26
28
|
* GET /api/studio/canvases/:id One canvas
|
|
@@ -96,9 +96,15 @@ const pointSchema = z.object({ x: z.number(), y: z.number() });
|
|
|
96
96
|
/**
|
|
97
97
|
* Edges for a new canvas (through the Studio API): as add_to_canvas takes them, and how each is drawn, if known
|
|
98
98
|
* (e.g. copied from a diagram): the points it goes through (canvas coordinates, at right angles) and where its
|
|
99
|
-
* label goes
|
|
99
|
+
* label goes, and how tall its nodes were laid out (for its ends to land on them as they render). It follows its
|
|
100
|
+
* nodes when they're moved.
|
|
100
101
|
*/
|
|
101
|
-
const routeSchema = z.object({
|
|
102
|
+
const routeSchema = z.object({
|
|
103
|
+
points: z.array(pointSchema).min(2).max(200),
|
|
104
|
+
label: pointSchema.optional(),
|
|
105
|
+
sourceHeight: z.number().positive().optional(),
|
|
106
|
+
targetHeight: z.number().positive().optional(),
|
|
107
|
+
});
|
|
102
108
|
export const newCanvasEdgesSchema = z.array(edgeSpecsSchema.element.extend({ route: routeSchema.optional() }));
|
|
103
109
|
export type NewCanvasEdges = z.output<typeof newCanvasEdgesSchema>;
|
|
104
110
|
|
|
@@ -46,7 +46,7 @@ type ModelContextTool = {
|
|
|
46
46
|
title?: string;
|
|
47
47
|
description: string;
|
|
48
48
|
inputSchema?: object;
|
|
49
|
-
annotations?: { readOnlyHint?: boolean; consequentialHint?: boolean };
|
|
49
|
+
annotations?: { readOnlyHint?: boolean; consequentialHint?: boolean; untrustedContentHint?: boolean };
|
|
50
50
|
execute: (input: unknown, options?: { signal?: AbortSignal }) => Promise<ToolResult>;
|
|
51
51
|
};
|
|
52
52
|
type ModelContext = { registerTool: (tool: ModelContextTool, options?: { signal?: AbortSignal }) => Promise<void> | void };
|
|
@@ -204,7 +204,8 @@ const createCanvasTools = (get: () => CanvasWebMcpOptions, getStage: () => Agent
|
|
|
204
204
|
description:
|
|
205
205
|
'Reads the collaborative canvas open on this page: its nodes (ids, names, positions, the catalog resource they stand for), connections, comment threads, and who is on it. Call it first, and again to see what people changed.',
|
|
206
206
|
input: z.object({}),
|
|
207
|
-
|
|
207
|
+
// Names, notes and comments come from people
|
|
208
|
+
annotations: { readOnlyHint: true, untrustedContentHint: true },
|
|
208
209
|
execute: () =>
|
|
209
210
|
withDoc((doc) => ({
|
|
210
211
|
...describeCanvas(doc),
|
|
@@ -342,7 +343,7 @@ const createCanvasTools = (get: () => CanvasWebMcpOptions, getStage: () => Agent
|
|
|
342
343
|
description:
|
|
343
344
|
'Selects nodes and moves the view to them, to point the user at something. Leave out nodeIds to show the whole canvas.',
|
|
344
345
|
input: z.object({ nodeIds: z.array(z.string()).optional() }),
|
|
345
|
-
|
|
346
|
+
// Not read only: it changes the user's selection and view
|
|
346
347
|
execute: ({ nodeIds }) => {
|
|
347
348
|
if (nodeIds?.length) get().select(nodeIds);
|
|
348
349
|
get().fitView(nodeIds);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { getCollection } from 'astro:content';
|
|
1
2
|
import { getNodesAndEdges as getSystemContextNodesAndEdges } from './system-context-node-graph';
|
|
2
|
-
import { getNodesAndEdges as getSystemNodesAndEdges } from './systems-node-graph';
|
|
3
|
+
import { getNodesAndEdges as getSystemNodesAndEdges, memberServiceNodeIds, SERVICE_NODE_TYPES } from './systems-node-graph';
|
|
3
4
|
import { layoutLevels } from '@utils/node-graphs/layout-node-graph';
|
|
5
|
+
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
4
6
|
|
|
5
7
|
interface NodesAndEdgesProps {
|
|
6
8
|
id: string;
|
|
@@ -22,6 +24,145 @@ export const getNodesAndEdges = (props: NodesAndEdgesProps) => {
|
|
|
22
24
|
return levelsCache.get(key)!;
|
|
23
25
|
};
|
|
24
26
|
|
|
27
|
+
const nodeIdOf = (resource: { data: { id: string; version?: string } }) => `${resource.data.id}-${resource.data.version}`;
|
|
28
|
+
|
|
29
|
+
const isSameResource = (a: { data: { id: string; version?: string } }, b: { data: { id: string; version?: string } }) =>
|
|
30
|
+
a.data.id === b.data.id && a.data.version === b.data.version;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Every system that lists a service, keyed by the service's resolved node id
|
|
34
|
+
* (`id-version`). Pointers use the same version matching as the rest of the
|
|
35
|
+
* catalog (`latest`, exact versions, semver ranges), so the key is the
|
|
36
|
+
* concrete service the pointer selects, not the pointer text.
|
|
37
|
+
*/
|
|
38
|
+
export const collectServiceOwners = (systems: any[], serviceMap: Map<string, any[]>) => {
|
|
39
|
+
const owners = new Map<string, any[]>();
|
|
40
|
+
for (const system of systems) {
|
|
41
|
+
for (const ref of system.data.services || []) {
|
|
42
|
+
const service = findInMap(serviceMap, ref.id, ref.version);
|
|
43
|
+
if (!service) continue;
|
|
44
|
+
const id = nodeIdOf(service);
|
|
45
|
+
const list = owners.get(id) ?? [];
|
|
46
|
+
if (!list.some((owner) => isSameResource(owner, system))) list.push(system);
|
|
47
|
+
owners.set(id, list);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return owners;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Which of a service's systems stands for it on this diagram.
|
|
55
|
+
* A direct relationship wins, in the order the focused system declares them
|
|
56
|
+
* (their version pointers resolved the same way as the context diagram).
|
|
57
|
+
* Otherwise the lowest system id, then version, so catalog order doesn't matter.
|
|
58
|
+
* Systems that aren't already on the diagram are not candidates: the service
|
|
59
|
+
* is drawn on its own instead of inventing a system box.
|
|
60
|
+
*/
|
|
61
|
+
const chooseOwningSystem = (owners: any[], contextSystemIds: Set<string>, focused: any, systemMap: Map<string, any[]>) => {
|
|
62
|
+
const inGraph = owners.filter((system) => !isSameResource(system, focused) && contextSystemIds.has(nodeIdOf(system)));
|
|
63
|
+
if (inGraph.length === 0) return undefined;
|
|
64
|
+
|
|
65
|
+
const inGraphIds = new Set(inGraph.map(nodeIdOf));
|
|
66
|
+
for (const relationship of focused.data.relationships || []) {
|
|
67
|
+
const related = findInMap(systemMap, relationship.id, relationship.version);
|
|
68
|
+
if (related && inGraphIds.has(nodeIdOf(related))) return related;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return [...inGraph].sort((a, b) => {
|
|
72
|
+
const byId = a.data.id.localeCompare(b.data.id);
|
|
73
|
+
return byId !== 0 ? byId : String(a.data.version).localeCompare(String(b.data.version));
|
|
74
|
+
})[0];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const detachFromGroup = (node: any) => {
|
|
78
|
+
const { parentId, extent, ...rest } = node;
|
|
79
|
+
return rest;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const edgePair = (source: string, target: string) => `${source}->${target}`;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Drops edges that repeat a relationship (either direction) and edges that
|
|
86
|
+
* repeat the same source, target and label. Relationship edges themselves stay.
|
|
87
|
+
*/
|
|
88
|
+
export const dedupeEdges = (edges: any[], relationshipPairs: Set<string>) => {
|
|
89
|
+
const seen = new Set<string>();
|
|
90
|
+
return edges.filter((edge) => {
|
|
91
|
+
const isRelationship = String(edge.id).startsWith('context-');
|
|
92
|
+
const pair = edgePair(edge.source, edge.target);
|
|
93
|
+
if (!isRelationship && relationshipPairs.has(pair)) return false;
|
|
94
|
+
if (isRelationship) return true;
|
|
95
|
+
const key = `${pair}->${edge.label ?? ''}`;
|
|
96
|
+
if (seen.has(key)) return false;
|
|
97
|
+
seen.add(key);
|
|
98
|
+
return true;
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Services that belong to another system already on the diagram are removed
|
|
104
|
+
* and their edges point at that system. Services in no system, or whose system
|
|
105
|
+
* isn't on the diagram, are kept but not inside this system's group. A service
|
|
106
|
+
* this system lists stays in the group even when another system lists it too.
|
|
107
|
+
*/
|
|
108
|
+
export const rehomeServicesInOtherSystems = ({
|
|
109
|
+
nodes,
|
|
110
|
+
edges,
|
|
111
|
+
groupId,
|
|
112
|
+
focusedSystem,
|
|
113
|
+
allSystems,
|
|
114
|
+
allServices,
|
|
115
|
+
contextSystemIds,
|
|
116
|
+
relationshipPairs,
|
|
117
|
+
}: {
|
|
118
|
+
nodes: any[];
|
|
119
|
+
edges: any[];
|
|
120
|
+
groupId: string;
|
|
121
|
+
focusedSystem: any;
|
|
122
|
+
allSystems: any[];
|
|
123
|
+
allServices: any[];
|
|
124
|
+
contextSystemIds: Set<string>;
|
|
125
|
+
relationshipPairs: Set<string>;
|
|
126
|
+
}) => {
|
|
127
|
+
const serviceMap = createVersionedMap(allServices);
|
|
128
|
+
const systemMap = createVersionedMap(allSystems);
|
|
129
|
+
const owners = collectServiceOwners(allSystems, serviceMap);
|
|
130
|
+
const members = memberServiceNodeIds(focusedSystem, serviceMap);
|
|
131
|
+
const replacedBy = new Map<string, string>();
|
|
132
|
+
|
|
133
|
+
const nextNodes = nodes
|
|
134
|
+
.map((node) => {
|
|
135
|
+
if (node.id === groupId || !SERVICE_NODE_TYPES.has(node.type)) return node;
|
|
136
|
+
const nodeOwners = owners.get(node.id) ?? [];
|
|
137
|
+
// Listed here, or resolved as a member: it belongs inside this group
|
|
138
|
+
if (members.has(node.id) || nodeOwners.some((system) => isSameResource(system, focusedSystem))) {
|
|
139
|
+
return node.parentId === groupId ? node : { ...node, parentId: groupId, extent: 'parent' };
|
|
140
|
+
}
|
|
141
|
+
const owner = chooseOwningSystem(nodeOwners, contextSystemIds, focusedSystem, systemMap);
|
|
142
|
+
if (owner) {
|
|
143
|
+
replacedBy.set(node.id, nodeIdOf(owner));
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return detachFromGroup(node);
|
|
147
|
+
})
|
|
148
|
+
.filter((node): node is any => !!node);
|
|
149
|
+
|
|
150
|
+
const seen = new Set<string>();
|
|
151
|
+
const nextEdges = edges.flatMap((edge) => {
|
|
152
|
+
const source = replacedBy.get(edge.source) ?? edge.source;
|
|
153
|
+
const target = replacedBy.get(edge.target) ?? edge.target;
|
|
154
|
+
if (source === target) return [];
|
|
155
|
+
const pair = edgePair(source, target);
|
|
156
|
+
if (relationshipPairs.has(pair)) return [];
|
|
157
|
+
const key = `${pair}->${edge.label ?? ''}`;
|
|
158
|
+
if (seen.has(key)) return [];
|
|
159
|
+
seen.add(key);
|
|
160
|
+
return [{ ...edge, source, target }];
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return { nodes: nextNodes, edges: nextEdges, replacedBy };
|
|
164
|
+
};
|
|
165
|
+
|
|
25
166
|
/**
|
|
26
167
|
* A system's resource diagram shown inside its context diagram: the system's node
|
|
27
168
|
* is replaced by the expanded system group (services, data stores, messages...),
|
|
@@ -30,17 +171,27 @@ export const getNodesAndEdges = (props: NodesAndEdgesProps) => {
|
|
|
30
171
|
* its messages and channels (`hiddenMessages`).
|
|
31
172
|
*/
|
|
32
173
|
const buildLevels = async ({ id, version, mode = 'simple', channelRenderMode }: NodesAndEdgesProps) => {
|
|
33
|
-
const [context, system] = await Promise.all([
|
|
174
|
+
const [context, system, allSystems, allServices] = await Promise.all([
|
|
34
175
|
getSystemContextNodesAndEdges({ id, version, mode, layout: false }),
|
|
35
|
-
getSystemNodesAndEdges({
|
|
176
|
+
getSystemNodesAndEdges({
|
|
177
|
+
id,
|
|
178
|
+
version,
|
|
179
|
+
mode,
|
|
180
|
+
channelRenderMode,
|
|
181
|
+
wrapInSystemGroup: true,
|
|
182
|
+
confineToMembers: true,
|
|
183
|
+
layout: false,
|
|
184
|
+
}),
|
|
185
|
+
getCollection('systems'),
|
|
186
|
+
getCollection('services'),
|
|
36
187
|
]);
|
|
37
188
|
|
|
38
189
|
const group = system.nodes.find((node: any) => node.type === 'system-group');
|
|
39
190
|
const rootSystemNode = context.nodes.find((node: any) => node.data?.isFocused);
|
|
40
191
|
if (!group || !rootSystemNode) return layoutLevels(system, context);
|
|
41
192
|
|
|
42
|
-
const
|
|
43
|
-
|
|
193
|
+
const focusedSystem = allSystems.find((systemEntry) => systemEntry.data.id === id && systemEntry.data.version === version);
|
|
194
|
+
|
|
44
195
|
const toGroup = (nodeId: string) => (nodeId === rootSystemNode.id ? group.id : nodeId);
|
|
45
196
|
const contextEdges = context.edges.map((edge: any) => ({
|
|
46
197
|
...edge,
|
|
@@ -48,12 +199,47 @@ const buildLevels = async ({ id, version, mode = 'simple', channelRenderMode }:
|
|
|
48
199
|
source: toGroup(edge.source),
|
|
49
200
|
target: toGroup(edge.target),
|
|
50
201
|
}));
|
|
202
|
+
const relationshipPairs = new Set<string>();
|
|
203
|
+
for (const edge of contextEdges) {
|
|
204
|
+
relationshipPairs.add(edgePair(edge.source, edge.target));
|
|
205
|
+
relationshipPairs.add(edgePair(edge.target, edge.source));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const contextSystemIds = new Set(
|
|
209
|
+
context.nodes.filter((node: any) => node.type === 'systems' && node.id !== rootSystemNode.id).map((node: any) => node.id)
|
|
210
|
+
);
|
|
51
211
|
|
|
52
|
-
|
|
212
|
+
const rehomed = focusedSystem
|
|
213
|
+
? rehomeServicesInOtherSystems({
|
|
214
|
+
nodes: system.nodes,
|
|
215
|
+
edges: system.edges,
|
|
216
|
+
groupId: group.id,
|
|
217
|
+
focusedSystem,
|
|
218
|
+
allSystems,
|
|
219
|
+
allServices,
|
|
220
|
+
contextSystemIds,
|
|
221
|
+
relationshipPairs,
|
|
222
|
+
})
|
|
223
|
+
: { nodes: system.nodes, edges: system.edges };
|
|
224
|
+
|
|
225
|
+
const systemNodeIds = new Set(rehomed.nodes.map((node: any) => node.id));
|
|
226
|
+
const contextNodes = context.nodes.filter((node: any) => node.id !== rootSystemNode.id && !systemNodeIds.has(node.id));
|
|
227
|
+
|
|
228
|
+
// Lay the context and the expanded system out together. Redirects happen
|
|
229
|
+
// before this, so level 2 (messages hidden) bridges onto the system boxes.
|
|
53
230
|
const detailed = {
|
|
54
231
|
// Parent must come before its children in the array for React Flow
|
|
55
|
-
nodes: [group, ...contextNodes, ...
|
|
56
|
-
edges: [...
|
|
232
|
+
nodes: [group, ...contextNodes, ...rehomed.nodes.filter((node: any) => node.id !== group.id)],
|
|
233
|
+
edges: [...rehomed.edges, ...contextEdges],
|
|
234
|
+
};
|
|
235
|
+
const laidOut = await layoutLevels(detailed, context);
|
|
236
|
+
// Hiding messages can bridge two systems that a relationship already joins
|
|
237
|
+
return {
|
|
238
|
+
...laidOut,
|
|
239
|
+
edges: dedupeEdges(laidOut.edges, relationshipPairs),
|
|
240
|
+
hiddenMessages: {
|
|
241
|
+
...laidOut.hiddenMessages,
|
|
242
|
+
edges: dedupeEdges(laidOut.hiddenMessages.edges, relationshipPairs),
|
|
243
|
+
},
|
|
57
244
|
};
|
|
58
|
-
return layoutLevels(detailed, context);
|
|
59
245
|
};
|
|
@@ -15,6 +15,11 @@ interface NodesAndEdgesProps {
|
|
|
15
15
|
wrapInSystemGroup?: boolean;
|
|
16
16
|
channelRenderMode?: 'single' | 'flat';
|
|
17
17
|
layout?: boolean;
|
|
18
|
+
// Only parent this system's own services. Services pulled in because they
|
|
19
|
+
// send or receive the same messages stay in the graph, but outside the group.
|
|
20
|
+
// The domain diagram leaves this off: it uses the parent to tell which
|
|
21
|
+
// system's graph a service came from.
|
|
22
|
+
confineToMembers?: boolean;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export const getNodesAndEdges = async ({
|
|
@@ -25,6 +30,7 @@ export const getNodesAndEdges = async ({
|
|
|
25
30
|
wrapInSystemGroup = false,
|
|
26
31
|
channelRenderMode = 'flat',
|
|
27
32
|
layout = true,
|
|
33
|
+
confineToMembers = false,
|
|
28
34
|
}: NodesAndEdgesProps) => {
|
|
29
35
|
let nodes = new Map(),
|
|
30
36
|
edges = new Map();
|
|
@@ -118,21 +124,52 @@ export const getNodesAndEdges = async ({
|
|
|
118
124
|
});
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
// Wrap
|
|
122
|
-
//
|
|
127
|
+
// Wrap the graph in one parent "system group" boundary node. Messages,
|
|
128
|
+
// channels and data stores stay in it. With confineToMembers, only this
|
|
129
|
+
// system's own services are children; other services are left outside.
|
|
123
130
|
const graph = {
|
|
124
|
-
nodes:
|
|
131
|
+
nodes:
|
|
132
|
+
wrapInSystemGroup && nodes.size > 0
|
|
133
|
+
? wrapNodesInSystemGroup(
|
|
134
|
+
[...nodes.values()],
|
|
135
|
+
system,
|
|
136
|
+
confineToMembers ? memberServiceNodeIds(system, serviceMap) : undefined
|
|
137
|
+
)
|
|
138
|
+
: [...nodes.values()],
|
|
125
139
|
edges: [...edges.values()],
|
|
126
140
|
};
|
|
127
141
|
|
|
128
142
|
return layout ? layoutNodeGraph(graph) : graph;
|
|
129
143
|
};
|
|
130
144
|
|
|
145
|
+
/** Service and agent nodes. Anything else in the graph (messages, channels, data stores) stays with the system. */
|
|
146
|
+
export const SERVICE_NODE_TYPES = new Set(['services', 'agents']);
|
|
147
|
+
|
|
148
|
+
/** Node ids of the services a system lists, with `latest` and semver ranges resolved to a real version. */
|
|
149
|
+
export const memberServiceNodeIds = (
|
|
150
|
+
system: { data: { services?: { id: string; version?: string }[] } },
|
|
151
|
+
serviceMap: Map<string, { data: { id: string; version?: string } }[]>
|
|
152
|
+
) => {
|
|
153
|
+
const ids = new Set<string>();
|
|
154
|
+
for (const ref of system.data.services || []) {
|
|
155
|
+
const service = findInMap(serviceMap, ref.id, ref.version);
|
|
156
|
+
if (service) ids.add(`${service.data.id}-${service.data.version}`);
|
|
157
|
+
}
|
|
158
|
+
return ids;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const withoutGroupParent = (node: any) => {
|
|
162
|
+
const { parentId, extent, ...rest } = node;
|
|
163
|
+
return rest;
|
|
164
|
+
};
|
|
165
|
+
|
|
131
166
|
/**
|
|
132
167
|
* Wraps a system's nodes in one parent `system-group` node (sized to fit them
|
|
133
168
|
* when the graph is laid out), so they visibly belong to the system.
|
|
169
|
+
* When `memberIds` is set, service and agent nodes that aren't members are
|
|
170
|
+
* left outside the group.
|
|
134
171
|
*/
|
|
135
|
-
const wrapNodesInSystemGroup = (systemNodes: any[], system: any) => {
|
|
172
|
+
const wrapNodesInSystemGroup = (systemNodes: any[], system: any, memberIds?: Set<string>) => {
|
|
136
173
|
const groupId = `system-group-${system.data.id}-${system.data.version}`;
|
|
137
174
|
|
|
138
175
|
const groupNode = {
|
|
@@ -149,5 +186,11 @@ const wrapNodesInSystemGroup = (systemNodes: any[], system: any) => {
|
|
|
149
186
|
};
|
|
150
187
|
|
|
151
188
|
// Parent must come before its children in the array for React Flow
|
|
152
|
-
return [
|
|
189
|
+
return [
|
|
190
|
+
groupNode,
|
|
191
|
+
...systemNodes.map((node) => {
|
|
192
|
+
if (memberIds && SERVICE_NODE_TYPES.has(node.type) && !memberIds.has(node.id)) return withoutGroupParent(node);
|
|
193
|
+
return { ...node, parentId: groupId, extent: 'parent' };
|
|
194
|
+
}),
|
|
195
|
+
];
|
|
153
196
|
};
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "BUSL-1.1",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "5.4.
|
|
10
|
+
"version": "5.4.2",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -141,11 +141,11 @@
|
|
|
141
141
|
"y-protocols": "^1.0.7",
|
|
142
142
|
"yjs": "^13.6.33",
|
|
143
143
|
"zod": "^4.6.6",
|
|
144
|
+
"@eventcatalog/exporters": "0.1.2",
|
|
144
145
|
"@eventcatalog/fitness": "0.1.0",
|
|
145
|
-
"@eventcatalog/sdk": "2.31.2",
|
|
146
146
|
"@eventcatalog/linter": "1.2.4",
|
|
147
|
-
"@eventcatalog/
|
|
148
|
-
"@eventcatalog/visualiser": "^5.0.
|
|
147
|
+
"@eventcatalog/sdk": "2.31.2",
|
|
148
|
+
"@eventcatalog/visualiser": "^5.0.1"
|
|
149
149
|
},
|
|
150
150
|
"devDependencies": {
|
|
151
151
|
"@astrojs/check": "^0.9.10",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createGitHubSourceProvider
|
|
3
|
-
} from "./chunk-352FGP6W.js";
|
|
4
1
|
import {
|
|
5
2
|
createFileSystemSourceProvider
|
|
6
3
|
} from "./chunk-SDZQJTJW.js";
|
|
4
|
+
import {
|
|
5
|
+
createGitHubSourceProvider
|
|
6
|
+
} from "./chunk-352FGP6W.js";
|
|
7
7
|
|
|
8
8
|
// src/federation/source-provider.ts
|
|
9
9
|
var createFederationSourceProvider = (projectDirectory, providers = {}) => {
|