@eventcatalog/visualiser 5.0.1 → 5.1.1
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/{chunk-R3QHQ7LF.mjs → chunk-JB2NMDKX.mjs} +2 -2
- package/dist/chunk-JB2NMDKX.mjs.map +1 -0
- package/dist/chunk-P2DH6CFV.mjs +1196 -0
- package/dist/chunk-P2DH6CFV.mjs.map +1 -0
- package/dist/chunk-STJQWGKW.mjs +1 -0
- package/dist/{chunk-KVTGV7VK.mjs → chunk-ZHXUMAPK.mjs} +1706 -1484
- package/dist/chunk-ZHXUMAPK.mjs.map +1 -0
- package/dist/export-mermaid-CbER9roE.d.mts +23 -0
- package/dist/{elk-layout-CpbZ6hB9.d.ts → export-mermaid-xL98eL1l.d.ts} +21 -1
- package/dist/index.d.mts +57 -41
- package/dist/index.d.ts +58 -42
- package/dist/index.js +2159 -1246
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -5
- package/dist/layout.d.mts +1 -0
- package/dist/layout.d.ts +1 -1
- package/dist/layout.js +453 -0
- package/dist/layout.js.map +1 -1
- package/dist/layout.mjs +4 -2
- package/dist/model.d.mts +2 -2
- package/dist/model.mjs +1 -1
- package/dist/{schema-DKA0EQ_H.d.mts → schema-CKdQ-Ak2.d.mts} +7 -7
- package/dist/studio.d.mts +13 -3
- package/dist/studio.mjs +98 -90
- package/dist/studio.mjs.map +1 -1
- package/dist/styles-core.css +122 -120
- package/dist/styles.css +122 -120
- package/package.json +1 -2
- package/dist/chunk-KVTGV7VK.mjs.map +0 -1
- package/dist/chunk-NOHQEQD7.mjs +0 -468
- package/dist/chunk-NOHQEQD7.mjs.map +0 -1
- package/dist/chunk-Q3OM5AL5.mjs +0 -1
- package/dist/chunk-R3QHQ7LF.mjs.map +0 -1
- /package/dist/{chunk-Q3OM5AL5.mjs.map → chunk-STJQWGKW.mjs.map} +0 -0
package/dist/chunk-NOHQEQD7.mjs
DELETED
|
@@ -1,468 +0,0 @@
|
|
|
1
|
-
// src/nodes/node-types.ts
|
|
2
|
-
var SERVICE = ["service", "services"];
|
|
3
|
-
var AGENT = ["agent", "agents"];
|
|
4
|
-
var AGENT_TOOL = ["agentTool", "agent-tool"];
|
|
5
|
-
var EVENT = ["event", "events"];
|
|
6
|
-
var QUERY = ["queries", "query", "querie"];
|
|
7
|
-
var COMMAND = ["command", "commands"];
|
|
8
|
-
var CHANNEL = ["channel", "channels"];
|
|
9
|
-
var ACTOR = ["actor", "actors"];
|
|
10
|
-
var DATA = ["data"];
|
|
11
|
-
var VIEW = ["view"];
|
|
12
|
-
var MESSAGE = [...EVENT, ...COMMAND, ...QUERY];
|
|
13
|
-
|
|
14
|
-
// src/utils/utils/utils.ts
|
|
15
|
-
import { MarkerType, Position } from "@xyflow/react";
|
|
16
|
-
var generateIdForNode = (node) => {
|
|
17
|
-
return `${node.data.id}-${node.data.version}`;
|
|
18
|
-
};
|
|
19
|
-
var generateIdForNodes = (nodes) => {
|
|
20
|
-
return nodes.map((node) => `${node.data.id}-${node.data.version}`).join("-");
|
|
21
|
-
};
|
|
22
|
-
var generatedIdForEdge = (source, target) => {
|
|
23
|
-
return `${source.data.id}-${source.data.version}-${target.data.id}-${target.data.version}`;
|
|
24
|
-
};
|
|
25
|
-
var getColorFromString = (id) => {
|
|
26
|
-
let hash = 0;
|
|
27
|
-
for (let i = 0; i < id.length; i++) {
|
|
28
|
-
hash = id.charCodeAt(i) + ((hash << 5) - hash);
|
|
29
|
-
}
|
|
30
|
-
let color = "#";
|
|
31
|
-
for (let i = 0; i < 3; i++) {
|
|
32
|
-
const value = hash >> i * 8 & 255;
|
|
33
|
-
color += value.toString(16).padStart(2, "0");
|
|
34
|
-
}
|
|
35
|
-
return color;
|
|
36
|
-
};
|
|
37
|
-
var getEdgeLabelForServiceAsTarget = (data) => {
|
|
38
|
-
const type = data.collection;
|
|
39
|
-
switch (type) {
|
|
40
|
-
case "commands":
|
|
41
|
-
return "invokes";
|
|
42
|
-
case "events":
|
|
43
|
-
return "publishes \nevent";
|
|
44
|
-
case "queries":
|
|
45
|
-
return "requests";
|
|
46
|
-
default:
|
|
47
|
-
return "sends to";
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var getEdgeLabelForMessageAsSource = (data, throughChannel = false) => {
|
|
51
|
-
const type = data.collection;
|
|
52
|
-
switch (type) {
|
|
53
|
-
case "commands":
|
|
54
|
-
return "accepts";
|
|
55
|
-
case "events":
|
|
56
|
-
return throughChannel ? "subscribed to" : "subscribed by";
|
|
57
|
-
case "queries":
|
|
58
|
-
return "accepts";
|
|
59
|
-
default:
|
|
60
|
-
return "sends to";
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
var getEdgeLabelSize = (label) => {
|
|
64
|
-
if (typeof label !== "string" || !label.trim())
|
|
65
|
-
return { width: 0, height: 0 };
|
|
66
|
-
const lines = label.split("\n");
|
|
67
|
-
const longestLine = Math.max(...lines.map((line) => line.trim().length));
|
|
68
|
-
return { width: longestLine * 6 + 20, height: lines.length * 14 + 12 };
|
|
69
|
-
};
|
|
70
|
-
var createEdge = (edgeOptions) => {
|
|
71
|
-
return {
|
|
72
|
-
label: "subscribed by",
|
|
73
|
-
animated: false,
|
|
74
|
-
// markerStart: {
|
|
75
|
-
// type: MarkerType.Arrow,
|
|
76
|
-
// width: 40,
|
|
77
|
-
// height: 40,
|
|
78
|
-
// },
|
|
79
|
-
markerEnd: {
|
|
80
|
-
type: MarkerType.ArrowClosed,
|
|
81
|
-
width: 40,
|
|
82
|
-
height: 40,
|
|
83
|
-
color: "rgb(var(--ec-page-text-muted))"
|
|
84
|
-
},
|
|
85
|
-
style: {
|
|
86
|
-
strokeWidth: 1.5,
|
|
87
|
-
stroke: "rgb(var(--ec-page-text-muted))",
|
|
88
|
-
strokeDasharray: "5 5"
|
|
89
|
-
},
|
|
90
|
-
...edgeOptions
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
var createNode = (values) => {
|
|
94
|
-
return {
|
|
95
|
-
sourcePosition: Position.Right,
|
|
96
|
-
targetPosition: Position.Left,
|
|
97
|
-
...values
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// src/utils/elk-layout.ts
|
|
102
|
-
var DEFAULT_SIZE = { width: 240, height: 112 };
|
|
103
|
-
var NODE_SIZES = {
|
|
104
|
-
events: { width: 240, height: 104 },
|
|
105
|
-
commands: { width: 240, height: 126 },
|
|
106
|
-
queries: { width: 240, height: 126 },
|
|
107
|
-
services: { width: 240, height: 112 },
|
|
108
|
-
data: { width: 240, height: 108 },
|
|
109
|
-
systems: { width: 240, height: 120 },
|
|
110
|
-
system: { width: 240, height: 120 },
|
|
111
|
-
"context-domain": { width: 240, height: 112 },
|
|
112
|
-
"context-actor": { width: 180, height: 80 },
|
|
113
|
-
actor: { width: 180, height: 80 },
|
|
114
|
-
note: { width: 200, height: 150 },
|
|
115
|
-
step: { width: 240, height: 80 },
|
|
116
|
-
// Groups with nothing in them (groups with children are sized to fit them)
|
|
117
|
-
"domain-group": { width: 320, height: 160 },
|
|
118
|
-
"system-group": { width: 320, height: 160 }
|
|
119
|
-
};
|
|
120
|
-
var GROUP_TYPES = /* @__PURE__ */ new Set(["domain-group", "system-group", "group"]);
|
|
121
|
-
var getNodeSize = (node) => {
|
|
122
|
-
if (node.measured?.width && node.measured?.height) {
|
|
123
|
-
return { width: node.measured.width, height: node.measured.height };
|
|
124
|
-
}
|
|
125
|
-
if (typeof node.width === "number" && typeof node.height === "number") {
|
|
126
|
-
return { width: node.width, height: node.height };
|
|
127
|
-
}
|
|
128
|
-
if (node.type === "domains" || node.type === "domain") {
|
|
129
|
-
const services = node.data?.domain?.data?.services || [];
|
|
130
|
-
return { width: 260, height: 60 + services.length * 40 };
|
|
131
|
-
}
|
|
132
|
-
return NODE_SIZES[node.type || ""] ?? DEFAULT_SIZE;
|
|
133
|
-
};
|
|
134
|
-
var GROUP_PADDING = "[top=100,left=40,bottom=40,right=40]";
|
|
135
|
-
var SPACING = {
|
|
136
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "60",
|
|
137
|
-
"elk.spacing.nodeNode": "40",
|
|
138
|
-
"elk.spacing.edgeNode": "25",
|
|
139
|
-
"elk.spacing.edgeEdge": "15"
|
|
140
|
-
};
|
|
141
|
-
var LARGE_GRAPH_NODES = 80;
|
|
142
|
-
var LARGE_GRAPH_EDGES = 200;
|
|
143
|
-
var LARGE_GRAPH_OPTIONS = {
|
|
144
|
-
"elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
|
|
145
|
-
"elk.layered.thoroughness": "1",
|
|
146
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "OFF"
|
|
147
|
-
};
|
|
148
|
-
var elk;
|
|
149
|
-
var getElk = async () => {
|
|
150
|
-
if (!elk) {
|
|
151
|
-
const { default: ELK } = await import("elkjs/lib/elk.bundled.js");
|
|
152
|
-
elk = new ELK();
|
|
153
|
-
}
|
|
154
|
-
return elk;
|
|
155
|
-
};
|
|
156
|
-
var layoutWithElk = async ({ nodes, edges }, { sizeOf = getNodeSize } = {}) => {
|
|
157
|
-
const nodesById = new Map(nodes.map((node) => [node.id, node]));
|
|
158
|
-
const groupIds = new Set(
|
|
159
|
-
nodes.map((node) => node.parentId).filter((id) => !!id && nodesById.has(id))
|
|
160
|
-
);
|
|
161
|
-
const parentOf = (node) => node.parentId && groupIds.has(node.parentId) ? node.parentId : void 0;
|
|
162
|
-
const layoutEdges = edges.filter(
|
|
163
|
-
(edge) => nodesById.has(edge.source) && nodesById.has(edge.target)
|
|
164
|
-
);
|
|
165
|
-
const ports = /* @__PURE__ */ new Map();
|
|
166
|
-
const addPort = (nodeId, id, side) => {
|
|
167
|
-
const nodePorts = ports.get(nodeId) || [];
|
|
168
|
-
if (!nodePorts.some((port) => port.id === id)) {
|
|
169
|
-
ports.set(nodeId, [
|
|
170
|
-
...nodePorts,
|
|
171
|
-
{ id, layoutOptions: { "elk.port.side": side } }
|
|
172
|
-
]);
|
|
173
|
-
}
|
|
174
|
-
return id;
|
|
175
|
-
};
|
|
176
|
-
const edgePorts = layoutEdges.map((edge, index) => ({
|
|
177
|
-
source: addPort(
|
|
178
|
-
edge.source,
|
|
179
|
-
edge.sourceHandle ? `${edge.source}__${edge.sourceHandle}` : `e${index}__out`,
|
|
180
|
-
"EAST"
|
|
181
|
-
),
|
|
182
|
-
target: addPort(
|
|
183
|
-
edge.target,
|
|
184
|
-
edge.targetHandle ? `${edge.target}__${edge.targetHandle}` : `e${index}__in`,
|
|
185
|
-
"WEST"
|
|
186
|
-
)
|
|
187
|
-
}));
|
|
188
|
-
const toElkNode = (node) => ({
|
|
189
|
-
id: node.id,
|
|
190
|
-
...groupIds.has(node.id) ? {
|
|
191
|
-
children: nodes.filter((child) => parentOf(child) === node.id).map(toElkNode)
|
|
192
|
-
} : sizeOf(node),
|
|
193
|
-
layoutOptions: {
|
|
194
|
-
"elk.portConstraints": "FIXED_SIDE",
|
|
195
|
-
...groupIds.has(node.id) ? { "elk.padding": GROUP_PADDING, ...SPACING } : {},
|
|
196
|
-
// A node's own options (e.g. a group's padding) win
|
|
197
|
-
...node.data?.layoutOptions ?? {}
|
|
198
|
-
},
|
|
199
|
-
ports: ports.get(node.id) || []
|
|
200
|
-
});
|
|
201
|
-
const layout = await (await getElk()).layout({
|
|
202
|
-
id: "root",
|
|
203
|
-
layoutOptions: {
|
|
204
|
-
"elk.algorithm": "layered",
|
|
205
|
-
"elk.direction": "RIGHT",
|
|
206
|
-
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
207
|
-
"elk.edgeRouting": "ORTHOGONAL",
|
|
208
|
-
// Edge routes in absolute coordinates (node positions stay relative to their parent)
|
|
209
|
-
"elk.json.edgeCoords": "ROOT",
|
|
210
|
-
"elk.edgeLabels.placement": "CENTER",
|
|
211
|
-
...SPACING,
|
|
212
|
-
...nodes.length >= LARGE_GRAPH_NODES || edges.length >= LARGE_GRAPH_EDGES ? LARGE_GRAPH_OPTIONS : { "elk.layered.nodePlacement.strategy": "NETWORK_SIMPLEX" }
|
|
213
|
-
},
|
|
214
|
-
children: nodes.filter((node) => !parentOf(node)).map(toElkNode),
|
|
215
|
-
edges: layoutEdges.map((edge, index) => ({
|
|
216
|
-
id: `e${index}`,
|
|
217
|
-
sources: [edgePorts[index].source],
|
|
218
|
-
targets: [edgePorts[index].target],
|
|
219
|
-
// Labels sit on their edge, rather than beside it (only works set per label)
|
|
220
|
-
labels: edge.label ? [
|
|
221
|
-
{
|
|
222
|
-
text: String(edge.label),
|
|
223
|
-
...getEdgeLabelSize(edge.label),
|
|
224
|
-
layoutOptions: { "elk.edgeLabels.inline": "true" }
|
|
225
|
-
}
|
|
226
|
-
] : []
|
|
227
|
-
}))
|
|
228
|
-
});
|
|
229
|
-
const laidOut = /* @__PURE__ */ new Map();
|
|
230
|
-
const collect = (elkNode) => (elkNode.children || []).forEach((child) => {
|
|
231
|
-
laidOut.set(child.id, child);
|
|
232
|
-
collect(child);
|
|
233
|
-
});
|
|
234
|
-
collect(layout);
|
|
235
|
-
const absolute = (nodeId) => {
|
|
236
|
-
const node = nodesById.get(nodeId);
|
|
237
|
-
const parent = node && parentOf(node);
|
|
238
|
-
const offset = parent ? absolute(parent) : { x: 0, y: 0 };
|
|
239
|
-
const elkNode = laidOut.get(nodeId);
|
|
240
|
-
return { x: offset.x + (elkNode?.x ?? 0), y: offset.y + (elkNode?.y ?? 0) };
|
|
241
|
-
};
|
|
242
|
-
const laidOutAt = (nodeId) => ({
|
|
243
|
-
...absolute(nodeId),
|
|
244
|
-
height: laidOut.get(nodeId)?.height
|
|
245
|
-
});
|
|
246
|
-
const routes = /* @__PURE__ */ new Map();
|
|
247
|
-
(layout.edges || []).forEach((elkEdge) => {
|
|
248
|
-
const section = elkEdge.sections?.[0];
|
|
249
|
-
if (!section) return;
|
|
250
|
-
const label = elkEdge.labels?.[0];
|
|
251
|
-
const edge = layoutEdges[Number(elkEdge.id.slice(1))];
|
|
252
|
-
routes.set(edge, {
|
|
253
|
-
points: [
|
|
254
|
-
section.startPoint,
|
|
255
|
-
...section.bendPoints || [],
|
|
256
|
-
section.endPoint
|
|
257
|
-
],
|
|
258
|
-
label: label ? {
|
|
259
|
-
x: (label.x ?? 0) + (label.width ?? 0) / 2,
|
|
260
|
-
y: (label.y ?? 0) + (label.height ?? 0) / 2
|
|
261
|
-
} : void 0,
|
|
262
|
-
source: laidOutAt(edge.source),
|
|
263
|
-
target: laidOutAt(edge.target)
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
const depth = (node) => {
|
|
267
|
-
const parent = node && parentOf(node);
|
|
268
|
-
return parent ? depth(nodesById.get(parent)) + 1 : 0;
|
|
269
|
-
};
|
|
270
|
-
return {
|
|
271
|
-
// Parents must come before their children in the array for React Flow
|
|
272
|
-
nodes: [...nodes].sort((a, b) => depth(a) - depth(b)).map((node) => {
|
|
273
|
-
const elkNode = laidOut.get(node.id);
|
|
274
|
-
if (!elkNode) return node;
|
|
275
|
-
return {
|
|
276
|
-
...node,
|
|
277
|
-
origin: [0, 0],
|
|
278
|
-
position: { x: elkNode.x ?? 0, y: elkNode.y ?? 0 },
|
|
279
|
-
...groupIds.has(node.id) || GROUP_TYPES.has(node.type || "") && typeof node.style?.width !== "number" ? {
|
|
280
|
-
style: {
|
|
281
|
-
...node.style,
|
|
282
|
-
width: elkNode.width,
|
|
283
|
-
height: elkNode.height
|
|
284
|
-
},
|
|
285
|
-
// Re-measured at its new size
|
|
286
|
-
measured: void 0
|
|
287
|
-
} : {}
|
|
288
|
-
};
|
|
289
|
-
}),
|
|
290
|
-
edges: edges.map((edge) => {
|
|
291
|
-
const route = routes.get(edge);
|
|
292
|
-
return route ? { ...edge, data: { ...edge.data, route } } : edge;
|
|
293
|
-
})
|
|
294
|
-
};
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
// src/utils/hide-messages.ts
|
|
298
|
-
import { MarkerType as MarkerType2 } from "@xyflow/react";
|
|
299
|
-
var MESSAGE_NODE_TYPES = [...MESSAGE, "messageGroup", "messageGroupExpanded"];
|
|
300
|
-
var isMessageNode = (node) => MESSAGE_NODE_TYPES.includes(node.type || "");
|
|
301
|
-
var getCollection = (type = "") => {
|
|
302
|
-
if (EVENT.includes(type)) return "events";
|
|
303
|
-
if (COMMAND.includes(type)) return "commands";
|
|
304
|
-
if (QUERY.includes(type)) return "queries";
|
|
305
|
-
return "messages";
|
|
306
|
-
};
|
|
307
|
-
var getMessages = (node) => {
|
|
308
|
-
const data = node.data;
|
|
309
|
-
if (node.type === "messageGroup") {
|
|
310
|
-
return (data?.messages || []).map(({ message: message2 }) => [
|
|
311
|
-
message2?.data?.name || message2?.data?.id,
|
|
312
|
-
getCollection(message2?.collection)
|
|
313
|
-
]);
|
|
314
|
-
}
|
|
315
|
-
const message = data?.message;
|
|
316
|
-
return message ? [[message.name || message.id, getCollection(node.type)]] : [];
|
|
317
|
-
};
|
|
318
|
-
var VERBS = {
|
|
319
|
-
events: "publishes",
|
|
320
|
-
commands: "invokes",
|
|
321
|
-
queries: "requests"
|
|
322
|
-
};
|
|
323
|
-
var getMessagesLabel = (messages) => {
|
|
324
|
-
if (messages.size === 0) return void 0;
|
|
325
|
-
const collections = new Set(messages.values());
|
|
326
|
-
const collection = collections.size === 1 ? Array.from(collections)[0] : "messages";
|
|
327
|
-
const verb = VERBS[collection] || "sends";
|
|
328
|
-
const sent = messages.size === 1 ? Array.from(messages.keys())[0] : `${messages.size} ${collection}`;
|
|
329
|
-
return `${verb}
|
|
330
|
-
${sent}`;
|
|
331
|
-
};
|
|
332
|
-
var isCarrier = (node) => isMessageNode(node) || node.type === "channels";
|
|
333
|
-
var isCrossDomain = (edge) => !!edge.data?.crossDomain;
|
|
334
|
-
var getRoute = (edge) => edge.data?.route;
|
|
335
|
-
var joinRoutes = (routes) => {
|
|
336
|
-
if (!routes.every((route) => route && route.points.length > 0)) return;
|
|
337
|
-
const laidOut = routes;
|
|
338
|
-
const arrives = laidOut[0].points[laidOut[0].points.length - 1];
|
|
339
|
-
const leaves = laidOut[1].points[0];
|
|
340
|
-
return {
|
|
341
|
-
points: laidOut.flatMap((route) => route.points),
|
|
342
|
-
label: { x: (arrives.x + leaves.x) / 2, y: (arrives.y + leaves.y) / 2 },
|
|
343
|
-
source: laidOut[0].source,
|
|
344
|
-
target: laidOut[laidOut.length - 1].target
|
|
345
|
-
};
|
|
346
|
-
};
|
|
347
|
-
var hideNodes = (nodes, edges, isHidden) => {
|
|
348
|
-
const hiddenNodes = new Map(
|
|
349
|
-
nodes.filter(isHidden).map((node) => [node.id, node])
|
|
350
|
-
);
|
|
351
|
-
if (hiddenNodes.size === 0) return { nodes, edges };
|
|
352
|
-
const outgoingEdges = /* @__PURE__ */ new Map();
|
|
353
|
-
for (const edge of edges) {
|
|
354
|
-
const outgoing = outgoingEdges.get(edge.source) || [];
|
|
355
|
-
outgoing.push(edge);
|
|
356
|
-
outgoingEdges.set(edge.source, outgoing);
|
|
357
|
-
}
|
|
358
|
-
const keptEdges = [];
|
|
359
|
-
const bridgedEdges = /* @__PURE__ */ new Map();
|
|
360
|
-
for (const edge of edges) {
|
|
361
|
-
if (hiddenNodes.has(edge.source)) continue;
|
|
362
|
-
const target = hiddenNodes.get(edge.target);
|
|
363
|
-
if (!target) {
|
|
364
|
-
keptEdges.push(edge);
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
if (!isCarrier(target)) continue;
|
|
368
|
-
const stack = [
|
|
369
|
-
{
|
|
370
|
-
id: edge.target,
|
|
371
|
-
messages: [],
|
|
372
|
-
routes: [getRoute(edge)],
|
|
373
|
-
crossDomain: isCrossDomain(edge)
|
|
374
|
-
}
|
|
375
|
-
];
|
|
376
|
-
const visited = /* @__PURE__ */ new Set();
|
|
377
|
-
while (stack.length > 0) {
|
|
378
|
-
const current = stack.pop();
|
|
379
|
-
if (visited.has(current.id)) continue;
|
|
380
|
-
visited.add(current.id);
|
|
381
|
-
const messages = [
|
|
382
|
-
...current.messages,
|
|
383
|
-
...getMessages(hiddenNodes.get(current.id))
|
|
384
|
-
];
|
|
385
|
-
for (const next of outgoingEdges.get(current.id) || []) {
|
|
386
|
-
const routes = [...current.routes, getRoute(next)];
|
|
387
|
-
const crossDomain = current.crossDomain || isCrossDomain(next);
|
|
388
|
-
const nextNode = hiddenNodes.get(next.target);
|
|
389
|
-
if (nextNode) {
|
|
390
|
-
if (isCarrier(nextNode))
|
|
391
|
-
stack.push({ id: next.target, messages, routes, crossDomain });
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
if (next.target === edge.source) continue;
|
|
395
|
-
const id = `bridged-${edge.source}-${next.target}`;
|
|
396
|
-
const bridged = bridgedEdges.get(id) || {
|
|
397
|
-
source: edge.source,
|
|
398
|
-
target: next.target,
|
|
399
|
-
messages: /* @__PURE__ */ new Map(),
|
|
400
|
-
route: joinRoutes(routes),
|
|
401
|
-
crossDomain: false
|
|
402
|
-
};
|
|
403
|
-
bridged.crossDomain || (bridged.crossDomain = crossDomain);
|
|
404
|
-
messages.forEach(
|
|
405
|
-
([name, collection]) => bridged.messages.set(name, collection)
|
|
406
|
-
);
|
|
407
|
-
bridgedEdges.set(id, bridged);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
const newEdges = Array.from(
|
|
412
|
-
bridgedEdges,
|
|
413
|
-
([id, bridged]) => createEdge({
|
|
414
|
-
id,
|
|
415
|
-
source: bridged.source,
|
|
416
|
-
target: bridged.target,
|
|
417
|
-
label: getMessagesLabel(bridged.messages),
|
|
418
|
-
// The same size arrow as the rest of the graph's edges
|
|
419
|
-
markerEnd: {
|
|
420
|
-
type: MarkerType2.ArrowClosed,
|
|
421
|
-
width: 20,
|
|
422
|
-
height: 20,
|
|
423
|
-
color: "rgb(var(--ec-page-text-muted))"
|
|
424
|
-
},
|
|
425
|
-
// Along the same route, and crossing domains if any edge it replaces did
|
|
426
|
-
data: {
|
|
427
|
-
...bridged.route ? { route: bridged.route } : {},
|
|
428
|
-
...bridged.crossDomain ? { crossDomain: true } : {}
|
|
429
|
-
}
|
|
430
|
-
})
|
|
431
|
-
);
|
|
432
|
-
return {
|
|
433
|
-
nodes: nodes.filter((node) => !hiddenNodes.has(node.id)),
|
|
434
|
-
edges: [...keptEdges, ...newEdges]
|
|
435
|
-
};
|
|
436
|
-
};
|
|
437
|
-
var hideMessageNodes = (nodes, edges) => hideNodes(nodes, edges, isCarrier);
|
|
438
|
-
|
|
439
|
-
export {
|
|
440
|
-
SERVICE,
|
|
441
|
-
AGENT,
|
|
442
|
-
AGENT_TOOL,
|
|
443
|
-
EVENT,
|
|
444
|
-
QUERY,
|
|
445
|
-
COMMAND,
|
|
446
|
-
CHANNEL,
|
|
447
|
-
ACTOR,
|
|
448
|
-
DATA,
|
|
449
|
-
VIEW,
|
|
450
|
-
MESSAGE,
|
|
451
|
-
generateIdForNode,
|
|
452
|
-
generateIdForNodes,
|
|
453
|
-
generatedIdForEdge,
|
|
454
|
-
getColorFromString,
|
|
455
|
-
getEdgeLabelForServiceAsTarget,
|
|
456
|
-
getEdgeLabelForMessageAsSource,
|
|
457
|
-
getEdgeLabelSize,
|
|
458
|
-
createEdge,
|
|
459
|
-
createNode,
|
|
460
|
-
getNodeSize,
|
|
461
|
-
layoutWithElk,
|
|
462
|
-
isMessageNode,
|
|
463
|
-
getMessagesLabel,
|
|
464
|
-
isCarrier,
|
|
465
|
-
hideNodes,
|
|
466
|
-
hideMessageNodes
|
|
467
|
-
};
|
|
468
|
-
//# sourceMappingURL=chunk-NOHQEQD7.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nodes/node-types.ts","../src/utils/utils/utils.ts","../src/utils/elk-layout.ts","../src/utils/hide-messages.ts"],"sourcesContent":["export const SERVICE = [\"service\", \"services\"];\nexport const AGENT = [\"agent\", \"agents\"];\nexport const AGENT_TOOL = [\"agentTool\", \"agent-tool\"];\nexport const EVENT = [\"event\", \"events\"];\nexport const QUERY = [\"queries\", \"query\", \"querie\"];\nexport const COMMAND = [\"command\", \"commands\"];\nexport const CHANNEL = [\"channel\", \"channels\"];\nexport const ACTOR = [\"actor\", \"actors\"];\nexport const DATA = [\"data\"];\nexport const VIEW = [\"view\"];\n\nexport const MESSAGE = [...EVENT, ...COMMAND, ...QUERY];\n","// Can't use the CollectionEntry type from astro:content because a client component is using this util\n\nimport { MarkerType, Position, type Edge, type Node } from \"@xyflow/react\";\n\ninterface BaseCollectionData {\n id: string;\n version: string;\n}\n\ninterface CollectionItem {\n collection: string;\n data: BaseCollectionData;\n}\n\ninterface MessageCollectionItem extends CollectionItem {\n collection: \"commands\" | \"events\" | \"queries\";\n}\n\nexport const generateIdForNode = (node: CollectionItem) => {\n return `${node.data.id}-${node.data.version}`;\n};\nexport const generateIdForNodes = (nodes: any) => {\n return nodes\n .map((node: any) => `${node.data.id}-${node.data.version}`)\n .join(\"-\");\n};\nexport const generatedIdForEdge = (\n source: CollectionItem,\n target: CollectionItem,\n) => {\n return `${source.data.id}-${source.data.version}-${target.data.id}-${target.data.version}`;\n};\n\nexport const getColorFromString = (id: string) => {\n // Takes the given id (string) and returns a custom hex color based on the id\n // Create a hash from the string\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n hash = id.charCodeAt(i) + ((hash << 5) - hash);\n }\n\n // Convert the hash into a hex color\n let color = \"#\";\n for (let i = 0; i < 3; i++) {\n const value = (hash >> (i * 8)) & 0xff;\n color += value.toString(16).padStart(2, \"0\");\n }\n\n return color;\n};\n\nexport const getEdgeLabelForServiceAsTarget = (data: MessageCollectionItem) => {\n const type = data.collection;\n switch (type) {\n case \"commands\":\n return \"invokes\";\n case \"events\":\n return \"publishes \\nevent\";\n case \"queries\":\n return \"requests\";\n default:\n return \"sends to\";\n }\n};\nexport const getEdgeLabelForMessageAsSource = (\n data: MessageCollectionItem,\n throughChannel = false,\n) => {\n const type = data.collection;\n switch (type) {\n case \"commands\":\n return \"accepts\";\n case \"events\":\n return throughChannel ? \"subscribed to\" : \"subscribed by\";\n case \"queries\":\n return \"accepts\";\n default:\n return \"sends to\";\n }\n};\n\n/**\n * Rough size of an edge label, so the layout can leave room for it on its edge\n */\nexport const getEdgeLabelSize = (label: unknown) => {\n if (typeof label !== \"string\" || !label.trim())\n return { width: 0, height: 0 };\n const lines = label.split(\"\\n\");\n const longestLine = Math.max(...lines.map((line) => line.trim().length));\n return { width: longestLine * 6 + 20, height: lines.length * 14 + 12 };\n};\n\nexport const createEdge = (edgeOptions: Edge): Edge => {\n return {\n label: \"subscribed by\",\n animated: false,\n // markerStart: {\n // type: MarkerType.Arrow,\n // width: 40,\n // height: 40,\n // },\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 40,\n height: 40,\n color: \"rgb(var(--ec-page-text-muted))\",\n },\n style: {\n strokeWidth: 1.5,\n stroke: \"rgb(var(--ec-page-text-muted))\",\n strokeDasharray: \"5 5\",\n },\n ...edgeOptions,\n };\n};\n\nexport const createNode = (values: Node): Node => {\n return {\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n ...values,\n };\n};\n","import type { Edge, Node } from \"@xyflow/react\";\nimport type {\n ELK,\n ElkExtendedEdge,\n ElkNode,\n ElkPort,\n LayoutOptions,\n} from \"elkjs/lib/elk-api\";\nimport { getEdgeLabelSize } from \"./utils/utils\";\n\nexport type LayoutGraph = { nodes: Node[]; edges: Edge[] };\nexport type LayoutSize = { width: number; height: number };\nexport type EdgeRoute = {\n points: { x: number; y: number }[];\n label?: { x: number; y: number };\n /**\n * Where the edge's source and target were laid out (top-left, in the flow),\n * and how tall: nodes often render shorter than the layout estimated\n */\n source?: { x: number; y: number; height?: number };\n target?: { x: number; y: number; height?: number };\n};\n\n/**\n * A route copied to another canvas (e.g. a diagram opened in Studio), which\n * places the nodes itself: where it goes, and how tall its nodes were laid out\n */\nexport type CopiedEdgeRoute = Pick<EdgeRoute, \"points\" | \"label\"> & {\n sourceHeight?: number;\n targetHeight?: number;\n};\n\n// The size each node type renders at (at most), for nodes that haven't been\n// rendered (and measured) yet, e.g. when laying a graph out on the server\nconst DEFAULT_SIZE: LayoutSize = { width: 240, height: 112 };\nconst NODE_SIZES: Record<string, LayoutSize> = {\n events: { width: 240, height: 104 },\n commands: { width: 240, height: 126 },\n queries: { width: 240, height: 126 },\n services: { width: 240, height: 112 },\n data: { width: 240, height: 108 },\n systems: { width: 240, height: 120 },\n system: { width: 240, height: 120 },\n \"context-domain\": { width: 240, height: 112 },\n \"context-actor\": { width: 180, height: 80 },\n actor: { width: 180, height: 80 },\n note: { width: 200, height: 150 },\n step: { width: 240, height: 80 },\n // Groups with nothing in them (groups with children are sized to fit them)\n \"domain-group\": { width: 320, height: 160 },\n \"system-group\": { width: 320, height: 160 },\n};\n\n// Nodes sized by their style, like groups: an empty one needs a size too\nconst GROUP_TYPES = new Set([\"domain-group\", \"system-group\", \"group\"]);\n\n/** The size a node is laid out at: as rendered, or an estimate for its type */\nexport const getNodeSize = (node: Node): LayoutSize => {\n if (node.measured?.width && node.measured?.height) {\n return { width: node.measured.width, height: node.measured.height };\n }\n if (typeof node.width === \"number\" && typeof node.height === \"number\") {\n return { width: node.width, height: node.height };\n }\n // Domains list their services, so grow with them\n if (node.type === \"domains\" || node.type === \"domain\") {\n const services = (node.data as any)?.domain?.data?.services || [];\n return { width: 260, height: 60 + services.length * 40 };\n }\n return NODE_SIZES[node.type || \"\"] ?? DEFAULT_SIZE;\n};\n\n// Room for a group's header above its children\nconst GROUP_PADDING = \"[top=100,left=40,bottom=40,right=40]\";\n\n// Space between nodes and edges. Set on every group as well as the graph, as\n// groups don't take it from the graph (they'd use ELK's much tighter defaults)\nconst SPACING = {\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"60\",\n \"elk.spacing.nodeNode\": \"40\",\n \"elk.spacing.edgeNode\": \"25\",\n \"elk.spacing.edgeEdge\": \"15\",\n};\n\n// Large graphs (e.g. a big domain) use cheaper settings: the nicest node\n// placement takes minutes on a few thousand edges, these take a second or two\nconst LARGE_GRAPH_NODES = 80;\nconst LARGE_GRAPH_EDGES = 200;\nconst LARGE_GRAPH_OPTIONS: LayoutOptions = {\n \"elk.layered.nodePlacement.strategy\": \"BRANDES_KOEPF\",\n \"elk.layered.thoroughness\": \"1\",\n \"elk.layered.crossingMinimization.greedySwitch.type\": \"OFF\",\n};\n\nlet elk: ELK | undefined;\n// Loaded when first needed, so pages that never lay out in the browser don't download it\nconst getElk = async () => {\n if (!elk) {\n const { default: ELK } = await import(\"elkjs/lib/elk.bundled.js\");\n elk = new ELK();\n }\n return elk;\n};\n\n/**\n * Lays a graph out with ELK (left to right). Nested groups (nodes other nodes\n * have as their `parentId`) are laid out in one pass with their children.\n * Edges are routed at right angles around nodes, each edge with its own\n * connection point so edges don't run on top of each other, and labels are\n * placed on their edge. Each edge keeps its type and gets its route (and label\n * position) in `data.route`, which the visualiser's edges are drawn along.\n *\n * Node positions are top-left corners (nodes get `origin: [0, 0]`), children\n * relative to their group. Groups are sized to fit their children.\n */\nexport const layoutWithElk = async (\n { nodes, edges }: LayoutGraph,\n { sizeOf = getNodeSize }: { sizeOf?: (node: Node) => LayoutSize } = {},\n): Promise<LayoutGraph> => {\n const nodesById = new Map(nodes.map((node) => [node.id, node]));\n const groupIds = new Set(\n nodes\n .map((node) => node.parentId)\n .filter((id): id is string => !!id && nodesById.has(id)),\n );\n const parentOf = (node: Node) =>\n node.parentId && groupIds.has(node.parentId) ? node.parentId : undefined;\n\n // Each edge gets its own port, leaving from the right of its source and\n // arriving at the left of its target, so ELK spreads edges out along a\n // node's side rather than routing them all through one point. Edges to a\n // specific handle (e.g. a service listed in a domain) share that handle's port.\n const layoutEdges = edges.filter(\n (edge) => nodesById.has(edge.source) && nodesById.has(edge.target),\n );\n const ports = new Map<string, ElkPort[]>();\n const addPort = (\n nodeId: string,\n id: string,\n side: \"EAST\" | \"WEST\",\n ): string => {\n const nodePorts = ports.get(nodeId) || [];\n if (!nodePorts.some((port) => port.id === id)) {\n ports.set(nodeId, [\n ...nodePorts,\n { id, layoutOptions: { \"elk.port.side\": side } },\n ]);\n }\n return id;\n };\n const edgePorts = layoutEdges.map((edge, index) => ({\n source: addPort(\n edge.source,\n edge.sourceHandle\n ? `${edge.source}__${edge.sourceHandle}`\n : `e${index}__out`,\n \"EAST\",\n ),\n target: addPort(\n edge.target,\n edge.targetHandle\n ? `${edge.target}__${edge.targetHandle}`\n : `e${index}__in`,\n \"WEST\",\n ),\n }));\n\n const toElkNode = (node: Node): ElkNode => ({\n id: node.id,\n ...(groupIds.has(node.id)\n ? {\n children: nodes\n .filter((child) => parentOf(child) === node.id)\n .map(toElkNode),\n }\n : sizeOf(node)),\n layoutOptions: {\n \"elk.portConstraints\": \"FIXED_SIDE\",\n ...(groupIds.has(node.id)\n ? { \"elk.padding\": GROUP_PADDING, ...SPACING }\n : {}),\n // A node's own options (e.g. a group's padding) win\n ...((node.data as { layoutOptions?: LayoutOptions } | undefined)\n ?.layoutOptions ?? {}),\n },\n ports: ports.get(node.id) || [],\n });\n\n const layout = await (\n await getElk()\n ).layout({\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": \"RIGHT\",\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.edgeRouting\": \"ORTHOGONAL\",\n // Edge routes in absolute coordinates (node positions stay relative to their parent)\n \"elk.json.edgeCoords\": \"ROOT\",\n \"elk.edgeLabels.placement\": \"CENTER\",\n ...SPACING,\n ...(nodes.length >= LARGE_GRAPH_NODES || edges.length >= LARGE_GRAPH_EDGES\n ? LARGE_GRAPH_OPTIONS\n : { \"elk.layered.nodePlacement.strategy\": \"NETWORK_SIMPLEX\" }),\n },\n children: nodes.filter((node) => !parentOf(node)).map(toElkNode),\n edges: layoutEdges.map((edge, index) => ({\n id: `e${index}`,\n sources: [edgePorts[index].source],\n targets: [edgePorts[index].target],\n // Labels sit on their edge, rather than beside it (only works set per label)\n labels: edge.label\n ? [\n {\n text: String(edge.label),\n ...getEdgeLabelSize(edge.label),\n layoutOptions: { \"elk.edgeLabels.inline\": \"true\" },\n },\n ]\n : [],\n })),\n });\n\n const laidOut = new Map<string, ElkNode>();\n const collect = (elkNode: ElkNode) =>\n (elkNode.children || []).forEach((child) => {\n laidOut.set(child.id, child);\n collect(child);\n });\n collect(layout);\n\n // Where a node was laid out in the flow (ELK positions are relative to the parent)\n const absolute = (nodeId: string): { x: number; y: number } => {\n const node = nodesById.get(nodeId);\n const parent = node && parentOf(node);\n const offset = parent ? absolute(parent) : { x: 0, y: 0 };\n const elkNode = laidOut.get(nodeId);\n return { x: offset.x + (elkNode?.x ?? 0), y: offset.y + (elkNode?.y ?? 0) };\n };\n\n const laidOutAt = (nodeId: string) => ({\n ...absolute(nodeId),\n height: laidOut.get(nodeId)?.height,\n });\n\n const routes = new Map<Edge, EdgeRoute>();\n (layout.edges || []).forEach((elkEdge: ElkExtendedEdge) => {\n const section = elkEdge.sections?.[0];\n if (!section) return;\n const label = elkEdge.labels?.[0];\n const edge = layoutEdges[Number(elkEdge.id.slice(1))];\n routes.set(edge, {\n points: [\n section.startPoint,\n ...(section.bendPoints || []),\n section.endPoint,\n ],\n label: label\n ? {\n x: (label.x ?? 0) + (label.width ?? 0) / 2,\n y: (label.y ?? 0) + (label.height ?? 0) / 2,\n }\n : undefined,\n source: laidOutAt(edge.source),\n target: laidOutAt(edge.target),\n });\n });\n\n const depth = (node?: Node): number => {\n const parent = node && parentOf(node);\n return parent ? depth(nodesById.get(parent)) + 1 : 0;\n };\n\n return {\n // Parents must come before their children in the array for React Flow\n nodes: [...nodes]\n .sort((a, b) => depth(a) - depth(b))\n .map((node) => {\n const elkNode = laidOut.get(node.id);\n if (!elkNode) return node;\n return {\n ...node,\n origin: [0, 0] as [number, number],\n position: { x: elkNode.x ?? 0, y: elkNode.y ?? 0 },\n ...(groupIds.has(node.id) ||\n (GROUP_TYPES.has(node.type || \"\") &&\n typeof node.style?.width !== \"number\")\n ? {\n style: {\n ...node.style,\n width: elkNode.width,\n height: elkNode.height,\n },\n // Re-measured at its new size\n measured: undefined,\n }\n : {}),\n };\n }),\n edges: edges.map((edge) => {\n const route = routes.get(edge);\n return route ? { ...edge, data: { ...edge.data, route } } : edge;\n }),\n };\n};\n","import { MarkerType, type Edge, type Node } from \"@xyflow/react\";\nimport { COMMAND, EVENT, MESSAGE, QUERY } from \"../nodes/node-types\";\nimport { createEdge } from \"./utils/utils\";\nimport type { EdgeRoute } from \"./elk-layout\";\n\nconst MESSAGE_NODE_TYPES = [...MESSAGE, \"messageGroup\", \"messageGroupExpanded\"];\n\nexport const isMessageNode = (node: Node) =>\n MESSAGE_NODE_TYPES.includes(node.type || \"\");\n\nconst getCollection = (type = \"\") => {\n if (EVENT.includes(type)) return \"events\";\n if (COMMAND.includes(type)) return \"commands\";\n if (QUERY.includes(type)) return \"queries\";\n return \"messages\";\n};\n\n// Returns the messages a node represents, as [name, collection] pairs\nconst getMessages = (node: Node): [string, string][] => {\n const data = node.data as any;\n if (node.type === \"messageGroup\") {\n return (data?.messages || []).map(({ message }: any) => [\n message?.data?.name || message?.data?.id,\n getCollection(message?.collection),\n ]);\n }\n const message = data?.message;\n return message\n ? [[message.name || message.id, getCollection(node.type)]]\n : [];\n};\n\nconst VERBS: Record<string, string> = {\n events: \"publishes\",\n commands: \"invokes\",\n queries: \"requests\",\n};\n\n/**\n * Label for an edge carrying messages (name -> collection), e.g.\n * \"publishes\\nProduct Created\", \"publishes\\n3 events\", \"sends\\n3 messages\"\n */\nexport const getMessagesLabel = (messages: Map<string, string>) => {\n if (messages.size === 0) return undefined;\n const collections = new Set(messages.values());\n const collection =\n collections.size === 1 ? Array.from(collections)[0] : \"messages\";\n const verb = VERBS[collection] || \"sends\";\n const sent =\n messages.size === 1\n ? Array.from(messages.keys())[0]\n : `${messages.size} ${collection}`;\n return `${verb}\\n${sent}`;\n};\n\n// Messages and channels carry what's sent between the nodes either side of them\nexport const isCarrier = (node: Node) =>\n isMessageNode(node) || node.type === \"channels\";\n\nconst isCrossDomain = (edge: Edge) =>\n !!(edge.data as { crossDomain?: boolean } | undefined)?.crossDomain;\n\nconst getRoute = (edge: Edge) =>\n (edge.data as { route?: EdgeRoute } | undefined)?.route;\n\n/**\n * The route of an edge bridging hidden nodes: along the routes of the edges it\n * replaces (through where the hidden nodes were), labelled where the first\n * hidden node was. None unless they're all laid out.\n */\nconst joinRoutes = (routes: (EdgeRoute | undefined)[]) => {\n if (!routes.every((route) => route && route.points.length > 0)) return;\n const laidOut = routes as EdgeRoute[];\n const arrives = laidOut[0].points[laidOut[0].points.length - 1];\n const leaves = laidOut[1].points[0];\n return {\n points: laidOut.flatMap((route) => route.points),\n label: { x: (arrives.x + leaves.x) / 2, y: (arrives.y + leaves.y) / 2 },\n source: laidOut[0].source,\n target: laidOut[laidOut.length - 1].target,\n };\n};\n\n/**\n * Removes the nodes `isHidden` picks. Messages and channels removed are\n * bridged: the nodes either side of them are connected directly, labelled with\n * the messages between them. Other edges to removed nodes go with them.\n * e.g. hiding events, ServiceA -> OrderPlaced -> ServiceB becomes ServiceA -> ServiceB\n */\nexport const hideNodes = (\n nodes: Node[],\n edges: Edge[],\n isHidden: (node: Node) => boolean,\n) => {\n const hiddenNodes = new Map(\n nodes.filter(isHidden).map((node) => [node.id, node]),\n );\n if (hiddenNodes.size === 0) return { nodes, edges };\n\n const outgoingEdges = new Map<string, Edge[]>();\n for (const edge of edges) {\n const outgoing = outgoingEdges.get(edge.source) || [];\n outgoing.push(edge);\n outgoingEdges.set(edge.source, outgoing);\n }\n\n const keptEdges: Edge[] = [];\n const bridgedEdges = new Map<\n string,\n {\n source: string;\n target: string;\n messages: Map<string, string>;\n route?: EdgeRoute;\n crossDomain: boolean;\n }\n >();\n\n for (const edge of edges) {\n if (hiddenNodes.has(edge.source)) continue;\n const target = hiddenNodes.get(edge.target);\n if (!target) {\n keptEdges.push(edge);\n continue;\n }\n if (!isCarrier(target)) continue;\n\n // Follow the edge through hidden messages and channels until we reach\n // visible nodes, collecting the names of the messages along the way\n const stack = [\n {\n id: edge.target,\n messages: [] as [string, string][],\n routes: [getRoute(edge)],\n crossDomain: isCrossDomain(edge),\n },\n ];\n const visited = new Set<string>();\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (visited.has(current.id)) continue;\n visited.add(current.id);\n const messages = [\n ...current.messages,\n ...getMessages(hiddenNodes.get(current.id)!),\n ];\n\n for (const next of outgoingEdges.get(current.id) || []) {\n const routes = [...current.routes, getRoute(next)];\n const crossDomain = current.crossDomain || isCrossDomain(next);\n const nextNode = hiddenNodes.get(next.target);\n if (nextNode) {\n if (isCarrier(nextNode))\n stack.push({ id: next.target, messages, routes, crossDomain });\n continue;\n }\n if (next.target === edge.source) continue;\n\n // Prefixed, so it can't clash with an edge directly between the two\n const id = `bridged-${edge.source}-${next.target}`;\n const bridged = bridgedEdges.get(id) || {\n source: edge.source,\n target: next.target,\n messages: new Map<string, string>(),\n route: joinRoutes(routes),\n crossDomain: false,\n };\n bridged.crossDomain ||= crossDomain;\n messages.forEach(([name, collection]) =>\n bridged.messages.set(name, collection),\n );\n bridgedEdges.set(id, bridged);\n }\n }\n }\n\n const newEdges = Array.from(bridgedEdges, ([id, bridged]) =>\n createEdge({\n id,\n source: bridged.source,\n target: bridged.target,\n label: getMessagesLabel(bridged.messages),\n // The same size arrow as the rest of the graph's edges\n markerEnd: {\n type: MarkerType.ArrowClosed,\n width: 20,\n height: 20,\n color: \"rgb(var(--ec-page-text-muted))\",\n },\n // Along the same route, and crossing domains if any edge it replaces did\n data: {\n ...(bridged.route ? { route: bridged.route } : {}),\n ...(bridged.crossDomain ? { crossDomain: true } : {}),\n },\n }),\n );\n\n return {\n nodes: nodes.filter((node) => !hiddenNodes.has(node.id)),\n edges: [...keptEdges, ...newEdges],\n };\n};\n\n/**\n * Removes message nodes (events, commands, queries) and the channels they\n * travel through, connecting the nodes on either side directly.\n * e.g. ServiceA -> OrderPlaced -> orders-topic -> ServiceB becomes ServiceA -> ServiceB\n */\nexport const hideMessageNodes = (nodes: Node[], edges: Edge[]) =>\n hideNodes(nodes, edges, isCarrier);\n"],"mappings":"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","names":["MarkerType","message","MarkerType"]}
|
package/dist/chunk-Q3OM5AL5.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-Q3OM5AL5.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/model/schema.ts","../src/model/validate.ts"],"sourcesContent":["/**\n * The architecture model agents send us: EventCatalog's vocabulary (domains, systems, services, messages,\n * channels, containers) and how they relate. Shared by the MCP server and the view, so no server-only imports.\n */\nimport { z } from \"zod\";\n\nexport const RESOURCE_TYPES = [\n \"domain\",\n \"system\",\n \"service\",\n \"event\",\n \"command\",\n \"query\",\n \"channel\",\n \"container\",\n // Around the architecture, as Studio draws them: people, third parties, AI agents and user interfaces\n \"actor\",\n \"external-system\",\n \"agent\",\n \"view\",\n // The domain model (an entity, e.g. Order, with its properties) and who owns what (a team)\n \"entity\",\n \"team\",\n] as const;\nexport const ResourceType = z.enum(RESOURCE_TYPES);\nexport type ResourceType = z.infer<typeof ResourceType>;\n\nexport const MESSAGE_TYPES = new Set<ResourceType>([\n \"event\",\n \"command\",\n \"query\",\n]);\n\nexport const SCHEMA_FORMATS = [\n \"json-schema\",\n \"avro\",\n \"protobuf\",\n \"json\",\n \"yaml\",\n \"xml\",\n \"graphql\",\n \"other\",\n] as const;\nconst SCHEMA_FORMAT = z.enum(SCHEMA_FORMATS);\n\n/** A field of an entity (as EventCatalog's entities have them), e.g. { name: \"orderId\", type: \"string\" } */\nexport const EntityProperty = z.object({\n name: z.string().min(1).max(100),\n type: z\n .string()\n .min(1)\n .max(100)\n .describe(\n 'e.g. \"string\", \"number\", \"Customer\" (another entity), \"OrderLine[]\"',\n ),\n required: z.boolean().optional(),\n description: z.string().max(300).optional(),\n});\nexport type EntityProperty = z.infer<typeof EntityProperty>;\n\n/** The kinds of data store (EventCatalog's container_type), e.g. a Redis cache is \"cache\" */\nexport const CONTAINER_TYPES = [\n \"database\",\n \"cache\",\n \"objectStore\",\n \"searchIndex\",\n \"dataWarehouse\",\n \"dataLake\",\n \"externalSaaS\",\n \"other\",\n] as const;\nexport type ContainerType = (typeof CONTAINER_TYPES)[number];\n\nexport const Resource = z.object({\n id: z\n .string()\n .min(1)\n .max(120)\n .describe('Stable, unique id, e.g. \"OrderPlaced\" or \"orders-service\"'),\n type: ResourceType,\n name: z.string().min(1).max(200),\n summary: z\n .string()\n .max(1000)\n .optional()\n .describe(\n \"One or two sentences on what it is or does. Give every resource one when you can: it is shown on its node and its page\",\n ),\n parent: z\n .string()\n .optional()\n .describe(\n \"Id of what contains it: a service, agent, view or entity sits in a system or domain, a system in a domain, a domain in a domain (subdomain). Messages, channels, containers, actors and external systems usually have no parent\",\n ),\n version: z.string().optional().describe(\"Defaults to 0.0.1\"),\n owners: z\n .array(z.string())\n .max(20)\n .optional()\n .describe(\"Teams or people that own it\"),\n technology: z\n .string()\n .max(100)\n .optional()\n .describe('For containers and channels, e.g. \"PostgreSQL\", \"Kafka\", \"SQS\"'),\n containerType: z\n .enum(CONTAINER_TYPES)\n .optional()\n .describe(\n \"For containers: what kind of data store it is (EventCatalog's container_type). Defaults to database\",\n ),\n evidence: z\n .string()\n .max(500)\n .optional()\n .describe(\n 'Where this was found, e.g. \"src/orders/publisher.ts:42\" or \"specs/orders.asyncapi.yaml\"',\n ),\n properties: z\n .array(EntityProperty)\n .max(50)\n .optional()\n .describe(\"For entities: their fields, e.g. id, status, total\"),\n card: z\n .boolean()\n .optional()\n .describe(\n \"For domains and systems: drawn as a card connected to what it holds, rather than a box around it (set in the view)\",\n ),\n // Only web links: it's opened when \"View the code\" is pressed, so a javascript: or data: link would run there\n url: z\n .url({ protocol: /^https?$/ })\n .max(1000)\n .optional()\n .describe(\n 'Link to the code or spec it was found in, e.g. a GitHub permalink (shown as \"View the code\")',\n ),\n schema: z\n .object({\n format: SCHEMA_FORMAT,\n content: z\n .string()\n .min(1)\n .max(20000)\n .describe(\"The schema, copied as it is from the spec or code\"),\n path: z\n .string()\n .max(300)\n .optional()\n .describe('Where the schema lives, e.g. \"schemas/OrderPlaced.avsc\"'),\n })\n .optional()\n .describe(\n \"For events, commands and queries: the message's schema (JSON Schema, Avro, Protobuf...), shown on its page\",\n ),\n});\nexport type Resource = z.infer<typeof Resource>;\n\nexport const RELATION_KINDS = [\n \"sends\",\n \"receives\",\n \"routes-through\",\n \"reads\",\n \"writes\",\n \"connects\",\n] as const;\nexport const RelationKind = z.enum(RELATION_KINDS);\nexport type RelationKind = z.infer<typeof RelationKind>;\n\nexport const Relation = z.object({\n from: z.string().describe(\"Id of the resource the relation starts at\"),\n to: z.string().describe(\"Id of the resource it points to\"),\n kind: RelationKind.describe(\n \"sends / receives: service (or agent, view, actor, external system) -> message. routes-through: message -> channel. reads / writes: service -> container. connects: anything else (e.g. an actor using a view, a service calling an external system), described by its label\",\n ),\n label: z\n .string()\n .max(80)\n .optional()\n .describe(\n 'What it says on the diagram. For connects, what it means (e.g. \"uses\", \"calls\", \"notifies\"); for the others, leave it out for EventCatalog\\'s wording (e.g. \"publishes event\") or set it to say more (e.g. \"every 5 minutes\")',\n ),\n fromField: z\n .string()\n .max(100)\n .optional()\n .describe(\n 'For a connection from an entity: the property it starts at (e.g. \"customerId\")',\n ),\n toField: z\n .string()\n .max(100)\n .optional()\n .describe(\n 'For a connection to an entity: the property it points to (e.g. \"id\")',\n ),\n inferred: z\n .boolean()\n .optional()\n .describe(\n \"true when guessed rather than seen directly in code or specs (drawn dashed)\",\n ),\n evidence: z.string().max(500).optional(),\n});\nexport type Relation = z.infer<typeof Relation>;\n\nexport const NOTE_COLOR_NAMES = [\n \"yellow\",\n \"blue\",\n \"green\",\n \"pink\",\n \"purple\",\n \"gray\",\n] as const;\n\nexport const LEVELS = [\"overview\", \"services\", \"messages\"] as const;\nexport type Level = (typeof LEVELS)[number];\n\nexport const Note = z.object({\n text: z.string().max(1000),\n resource: z\n .string()\n .optional()\n .describe(\n \"Id of the resource the note is about; none for a note on the whole diagram\",\n ),\n author: z.string().max(100).optional(),\n // Where it sits on the canvas (set by the view when people move it), its colour and width\n x: z.number().optional(),\n y: z.number().optional(),\n color: z.enum(NOTE_COLOR_NAMES).optional(),\n width: z.number().min(120).max(800).optional(),\n // Written as a sticky note, or as plain text on the canvas, and when (shown on the note)\n kind: z.enum([\"sticky\", \"text\"]).optional(),\n fontSize: z\n .number()\n .min(8)\n .max(200)\n .optional()\n .describe(\"For text: its size, as it was scaled on the canvas\"),\n angle: z\n .number()\n .min(-360)\n .max(360)\n .optional()\n .describe(\"For text: how far it is turned, in degrees (clockwise)\"),\n createdAt: z\n .number()\n .optional()\n .describe(\"When it was written (milliseconds since 1970), shown on it\"),\n // As notes in EventCatalog are: on the level of detail it was written at (every level when not set)\n level: z\n .enum(LEVELS)\n .optional()\n .describe(\n \"The level it shows at (overview, services or messages); every level when not set\",\n ),\n});\nexport type Note = z.infer<typeof Note>;\n\n/** Where a resource is on a level: its top-left corner (relative to its container on the canvas) and a container's size */\nconst Place = z.object({\n x: z.number(),\n y: z.number(),\n width: z.number().optional(),\n height: z.number().optional(),\n});\nexport const Layout = z.object({\n overview: z.record(z.string(), Place).optional(),\n services: z.record(z.string(), Place).optional(),\n messages: z.record(z.string(), Place).optional(),\n});\nexport type Layout = z.infer<typeof Layout>;\n\n/** Who wrote a comment: a person (their name and colour on the canvas) or an agent */\nexport const CommentAuthor = z.object({\n name: z.string().min(1).max(80),\n color: z.string().max(20).default(\"#6b7280\"),\n agent: z.boolean().optional(),\n});\nconst XY = z.object({ x: z.number(), y: z.number() });\n/** For something at a place (not on a resource): the level it was put on, 1 (overview) or 2 (services). None: messages */\nconst PlaceLevel = z.union([z.literal(1), z.literal(2)]).optional();\n\n/**\n * A comment thread on the canvas, as EventCatalog Studio's: pinned at a place, or on a resource (nodeId, the resource\n * id, with an offset from its top left, so it moves with it), with its messages (the first one starts it, the rest\n * reply), and resolved once it's dealt with\n */\nexport const CommentThread = z.object({\n id: z.string().min(1).max(60),\n position: XY.describe(\"Where the pin is on the canvas (messages level)\"),\n nodeId: z\n .string()\n .max(200)\n .optional()\n .describe(\"The resource it is pinned to, if it is\"),\n offset: XY.optional().describe(\"Where on that resource, from its top left\"),\n level: PlaceLevel.describe(\n \"For one at a place: the level it is on, 1 (overview) or 2 (services); none: messages\",\n ),\n author: CommentAuthor,\n createdAt: z.number(),\n resolved: z.boolean().default(false),\n messages: z\n .array(\n z.object({\n id: z.string().min(1).max(60),\n text: z.string().min(1).max(4000),\n author: CommentAuthor,\n createdAt: z.number(),\n }),\n )\n .min(1)\n .max(200),\n});\nexport type CommentThread = z.infer<typeof CommentThread>;\n\n/** How much a hotspot matters: critical (red), warning (amber) or info (blue) */\nexport const HOTSPOT_SEVERITIES = [\"critical\", \"warning\", \"info\"] as const;\nexport type HotspotSeverity = (typeof HOTSPOT_SEVERITIES)[number];\n\n/**\n * A hotspot: something on the diagram people should know about, as an architect would flag it: a risk, a single\n * point of failure, a known problem, or the key part to understand. On a resource (shown on it), or at a place\n */\nexport const Hotspot = z.object({\n id: z\n .string()\n .min(1)\n .max(60)\n .default(() => crypto.randomUUID().slice(0, 8)),\n severity: z\n .enum(HOTSPOT_SEVERITIES)\n .default(\"warning\")\n .describe(\"critical, warning (the default) or info\"),\n title: z\n .string()\n .min(1)\n .max(120)\n .describe(\n 'What it is, short, e.g. \"Single point of failure\" or \"No retries on payment\"',\n ),\n description: z\n .string()\n .max(1000)\n .optional()\n .describe(\"Why it matters, and what to do about it\"),\n resource: z\n .string()\n .max(200)\n .optional()\n .describe(\n \"Id of the resource it is about (it shows on it). Leave out for one at a place\",\n ),\n position: XY.optional().describe(\n \"Where it is on the canvas, for one not on a resource: set by the view\",\n ),\n level: PlaceLevel.describe(\n \"For one at a place: the level it is on, 1 (overview) or 2 (services); none: messages. Set by the view\",\n ),\n author: CommentAuthor.optional().describe(\n \"Who flagged it (set it to you, with agent: true)\",\n ),\n createdAt: z.number().optional(),\n});\nexport type Hotspot = z.infer<typeof Hotspot>;\n\nexport const Model = z.object({\n title: z\n .string()\n .min(1)\n .max(200)\n .describe('What the diagram shows, e.g. \"Orders domain\"'),\n resources: z.array(Resource).min(1).max(300),\n relations: z.array(Relation).max(1000).default([]),\n notes: z\n .array(Note)\n .max(100)\n .optional()\n .describe(\n \"Notes on the diagram or its resources (users add their own in the view)\",\n ),\n comments: z\n .array(CommentThread)\n .max(200)\n .optional()\n .describe(\n \"Comment threads people left on the canvas (and their replies): keep them as they are when you change the diagram\",\n ),\n hotspots: z\n .array(Hotspot)\n .max(100)\n .optional()\n .describe(\n \"Hotspots: what people should know about the architecture (risks, problems, the key parts), each on a resource. Keep the ones there are when you change the diagram\",\n ),\n layout: Layout.optional().describe(\n \"Where things are on the canvas (set by the view when people arrange it): leave it as it is\",\n ),\n});\nexport type Model = z.infer<typeof Model>;\n\n/**\n * A scene of a tour: the agent showing the user something on the diagram, as a person at the canvas would. It\n * switches level, zooms to resources, highlights them (fading the rest), writes notes next to them and pins code.\n */\nexport const Scene = z.object({\n caption: z\n .string()\n .max(400)\n .optional()\n .describe(\"What you say in this scene, shown under the diagram\"),\n level: z\n .enum(LEVELS)\n .optional()\n .describe(\n \"Level of detail: overview (domains, systems), services, messages (everything)\",\n ),\n focus: z\n .array(z.string())\n .max(20)\n .optional()\n .describe(\n \"Ids of resources to zoom to: one zooms close to it, several fit them all. Leave out to keep the view (or show everything in the first scene)\",\n ),\n highlight: z\n .array(z.string())\n .max(50)\n .optional()\n .describe(\"Ids to highlight, fading everything else. Defaults to focus\"),\n notes: z\n .array(z.object({ resource: z.string(), text: z.string().min(1).max(600) }))\n .max(6)\n .optional()\n .describe(\n 'Notes you write on the canvas, next to resources, e.g. \"This is where the order is saved\"',\n ),\n code: z\n .array(\n z.object({\n resource: z.string(),\n title: z\n .string()\n .max(120)\n .optional()\n .describe('e.g. \"OrderPlaced.avsc\" or \"src/orders/publisher.ts:42\"'),\n language: z.string().max(30).optional(),\n code: z\n .string()\n .min(1)\n .max(4000)\n .describe(\n \"Copied from the code or specs you read, e.g. the event's schema\",\n ),\n }),\n )\n .max(3)\n .optional()\n .describe(\n \"Code pinned next to a resource, e.g. an event's schema or the handler that consumes it\",\n ),\n});\nexport type Scene = z.infer<typeof Scene>;\nexport const Tour = z.array(Scene).min(1).max(20);\n\n/** What the view shows: the model, where to start, and a tour to play */\nexport type ArchitecturePayload = {\n model: Model;\n focus?: string;\n level?: Level;\n viewUrl?: string;\n tour?: Scene[];\n};\n","/**\n * Checks beyond the schema: references point at something, relations go the right way, parents make sense.\n * Returns every problem at once, written so an agent can fix the model and try again.\n */\nimport {\n MESSAGE_TYPES,\n type Model,\n type Resource,\n type ResourceType,\n type Scene,\n} from \"./schema\";\n\nexport type ValidationResult = { errors: string[]; warnings: string[] };\n\n// What each relation kind connects\nconst RELATION_RULES: Record<\n string,\n { from: ResourceType[]; to: ResourceType[] }\n> = {\n sends: {\n from: [\"service\", \"agent\", \"view\", \"actor\", \"external-system\"],\n to: [\"event\", \"command\", \"query\"],\n },\n receives: {\n from: [\"service\", \"agent\", \"view\", \"actor\", \"external-system\"],\n to: [\"event\", \"command\", \"query\"],\n },\n \"routes-through\": { from: [\"event\", \"command\", \"query\"], to: [\"channel\"] },\n reads: { from: [\"service\", \"agent\"], to: [\"container\"] },\n writes: { from: [\"service\", \"agent\"], to: [\"container\"] },\n};\n// \"connects\" joins anything to anything (described by its label)\n\n// What each type of resource may sit in. Only domains and systems hold things (EventCatalog: domains contain systems\n// and subdomains, systems their services); on the canvas people can also put messages, channels and the like in them\nconst IN_CONTAINERS: ResourceType[] = [\"system\", \"domain\"];\nconst PARENT_RULES: Partial<Record<ResourceType, ResourceType[]>> = {\n service: IN_CONTAINERS,\n agent: IN_CONTAINERS,\n view: IN_CONTAINERS,\n event: IN_CONTAINERS,\n command: IN_CONTAINERS,\n query: IN_CONTAINERS,\n channel: IN_CONTAINERS,\n container: IN_CONTAINERS,\n actor: IN_CONTAINERS,\n \"external-system\": IN_CONTAINERS,\n entity: IN_CONTAINERS,\n team: IN_CONTAINERS,\n system: IN_CONTAINERS,\n domain: [\"domain\"],\n};\n\n/** A tour's scenes must point at resources in the model */\nexport function validateTour(model: Model, tour: Scene[]): string[] {\n const ids = new Set(model.resources.map((r) => r.id));\n const errors: string[] = [];\n tour.forEach((scene, index) => {\n const used = [\n ...(scene.focus ?? []),\n ...(scene.highlight ?? []),\n ...(scene.notes ?? []).map((n) => n.resource),\n ...(scene.code ?? []).map((c) => c.resource),\n ];\n for (const id of new Set(used))\n if (!ids.has(id))\n errors.push(\n `Scene ${index + 1}: \"${id}\" isn't a resource in the diagram. Use the ids from the model.`,\n );\n });\n return errors;\n}\n\nexport function validateModel(model: Model): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const byId = new Map<string, Resource>();\n\n for (const resource of model.resources) {\n if (byId.has(resource.id))\n errors.push(\n `Duplicate id \"${resource.id}\": every resource needs a unique id.`,\n );\n byId.set(resource.id, resource);\n }\n\n for (const resource of model.resources) {\n if (!resource.parent) continue;\n const parent = byId.get(resource.parent);\n const allowed = PARENT_RULES[resource.type];\n if (!allowed) {\n errors.push(\n `${resource.type} \"${resource.id}\" has parent \"${resource.parent}\", but a ${resource.type} can't have a parent. Remove it.`,\n );\n } else if (!parent) {\n errors.push(\n `${resource.type} \"${resource.id}\" has parent \"${resource.parent}\", which isn't a resource. Add it or remove the parent.`,\n );\n } else if (!allowed.includes(parent.type)) {\n errors.push(\n `${resource.type} \"${resource.id}\" is in ${parent.type} \"${parent.id}\", but a ${resource.type} can only sit in a ${allowed.join(\" or \")}.` +\n (resource.type === \"domain\" && parent.type === \"system\"\n ? \" In EventCatalog domains contain systems: set the system's parent to the domain instead.\"\n : \"\"),\n );\n }\n }\n\n // Parent cycles\n for (const resource of model.resources) {\n const seen = new Set<string>([resource.id]);\n for (\n let current = resource.parent && byId.get(resource.parent);\n current;\n current = current.parent ? byId.get(current.parent) : undefined\n ) {\n if (seen.has(current.id)) {\n errors.push(\n `\"${resource.id}\" is inside itself (parents loop through \"${current.id}\"). Break the loop.`,\n );\n break;\n }\n seen.add(current.id);\n }\n }\n\n model.relations.forEach((relation, index) => {\n const label = `Relation ${index} (${relation.from} ${relation.kind} ${relation.to})`;\n const from = byId.get(relation.from);\n const to = byId.get(relation.to);\n if (!from)\n errors.push(\n `${label}: \"${relation.from}\" isn't a resource. Add it or fix the id.`,\n );\n if (!to)\n errors.push(\n `${label}: \"${relation.to}\" isn't a resource. Add it or fix the id.`,\n );\n if (!from || !to) return;\n const rule = RELATION_RULES[relation.kind];\n if (\n rule &&\n (!rule.from.includes(from.type) || !rule.to.includes(to.type))\n ) {\n errors.push(\n `${label}: \"${relation.kind}\" goes from a ${rule.from.join(\"/\")} to a ${rule.to.join(\"/\")}, not from a ${from.type} to a ${to.type}.`,\n );\n }\n });\n\n (model.hotspots ?? []).forEach((hotspot, index) => {\n if (hotspot.resource && !byId.has(hotspot.resource))\n errors.push(\n `Hotspot ${index + 1} (\"${hotspot.title.slice(0, 40)}\"): \"${hotspot.resource}\" isn't a resource. Use the id of a resource in the model.`,\n );\n if (!hotspot.resource && !hotspot.position)\n errors.push(\n `Hotspot ${index + 1} (\"${hotspot.title.slice(0, 40)}\"): set resource, the id of the resource it's about.`,\n );\n });\n (model.notes ?? []).forEach((note, index) => {\n if (note.resource && !byId.has(note.resource))\n errors.push(\n `Note ${index + 1}: \"${note.resource}\" isn't a resource. Use the id of a resource in the model, or leave it out.`,\n );\n });\n\n // Warnings: likely gaps, but still worth drawing\n const connected = new Set(model.relations.flatMap((r) => [r.from, r.to]));\n const sent = new Set(\n model.relations.filter((r) => r.kind === \"sends\").map((r) => r.to),\n );\n for (const resource of model.resources) {\n if (resource.type === \"service\" && !connected.has(resource.id))\n warnings.push(\n `Service \"${resource.id}\" has no relations: what does it send, receive, read or write?`,\n );\n if (MESSAGE_TYPES.has(resource.type) && !sent.has(resource.id))\n warnings.push(\n `${resource.type} \"${resource.id}\" has no sender in the model.`,\n );\n }\n const inferred = model.relations.filter((r) => r.inferred).length;\n if (model.relations.length >= 4 && inferred / model.relations.length > 0.5)\n warnings.push(\n `${inferred} of ${model.relations.length} relations are inferred: look for evidence in code or specs to confirm them.`,\n );\n\n return { errors, warnings };\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|