@eventcatalog/core 4.1.5 → 4.2.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.
Files changed (34) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-E6IUV4GX.js → chunk-A6SWOPJ4.js} +1 -1
  6. package/dist/{chunk-G74MXXTG.js → chunk-DTQ2MXRB.js} +1 -1
  7. package/dist/{chunk-X77TET65.js → chunk-PUTZR6SV.js} +1 -1
  8. package/dist/{chunk-GIU3S4CI.js → chunk-RQ36O7QA.js} +1 -1
  9. package/dist/{chunk-DIUAURE2.js → chunk-V6UMVNX4.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +36 -23
  13. package/dist/eventcatalog.js +41 -28
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/src/components/Lineage/LineageScenarioSwitcher.tsx +65 -0
  19. package/eventcatalog/src/content.config.ts +9 -0
  20. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +3 -1
  21. package/eventcatalog/src/pages/docs/[type]/[id]/[version].mdx.ts +4 -2
  22. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/_index.data.ts +66 -0
  23. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +201 -0
  24. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +31 -1
  25. package/eventcatalog/src/stores/sidebar-store/state.ts +22 -4
  26. package/eventcatalog/src/utils/collections/commands.ts +1 -1
  27. package/eventcatalog/src/utils/collections/message-triggers.ts +118 -0
  28. package/eventcatalog/src/utils/file-diffs.ts +4 -2
  29. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -1
  30. package/eventcatalog/src/utils/node-graphs/message-lineage.ts +131 -0
  31. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +119 -1
  32. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +15 -1
  33. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +14 -4
  34. package/package.json +4 -4
@@ -1,6 +1,6 @@
1
1
  // import { getColor } from '@utils/colors';
2
2
  import { getEvents } from '@utils/collections/events';
3
- import type { CollectionEntry } from 'astro:content';
3
+ import { getCollection, type CollectionEntry } from 'astro:content';
4
4
  import dagre from 'dagre';
5
5
  import {
6
6
  calculatedNodes,
@@ -42,6 +42,7 @@ import {
42
42
  import { getNodesAndEdgesForChannelChain } from './channel-node-graph';
43
43
  import { getChannelChain, isChannelsConnected } from '@utils/collections/channels';
44
44
  import { getChannels } from '@utils/collections/channels';
45
+ import { getTriggeredByOfMessage, getTriggersOfMessage, type MessageReceiver } from '@utils/collections/message-triggers';
45
46
 
46
47
  type DagreGraph = any;
47
48
  type RoutableResource = CollectionEntry<'agents'> | CollectionEntry<'services'>;
@@ -166,6 +167,52 @@ const getDataProductNodeData = (resource: CollectionEntry<'data-products'>, mode
166
167
  contextMenu: buildContextMenuForResource({ collection: 'data-products', id: resource.data.id, version: resource.data.version }),
167
168
  });
168
169
 
170
+ export const getTriggerReceiverNodeData = (receiver: MessageReceiver, mode: 'simple' | 'full') => {
171
+ if (receiver.collection !== 'domains') return getRoutableNodeData(receiver, mode);
172
+
173
+ return {
174
+ title: receiver.data.id,
175
+ mode,
176
+ domain: {
177
+ ...receiver,
178
+ data: {
179
+ ...receiver.data,
180
+ // Raw domain entries contain service pointers, while the visualiser's
181
+ // domain node expects hydrated services in full mode.
182
+ services: [],
183
+ },
184
+ },
185
+ contextMenu: buildContextMenuForResource({
186
+ collection: 'domains',
187
+ id: receiver.data.id,
188
+ version: receiver.data.version,
189
+ }),
190
+ };
191
+ };
192
+
193
+ export const createTriggerMessageNode = (
194
+ message: CollectionEntry<CollectionMessageTypes>,
195
+ mode: 'simple' | 'full',
196
+ isFocused = false
197
+ ) =>
198
+ createNode({
199
+ id: generateIdForNode(message),
200
+ type: message.collection,
201
+ data: {
202
+ mode,
203
+ ...(isFocused ? { isFocused: true } : {}),
204
+ message: { ...message.data, ...getOperationFields(message.data) },
205
+ contextMenu: buildContextMenuForMessage({
206
+ id: message.data.id,
207
+ version: message.data.version,
208
+ name: message.data.name,
209
+ collection: message.collection,
210
+ schemaPath: (message.data as any).schemaPath,
211
+ }),
212
+ },
213
+ position: { x: 0, y: 0 },
214
+ });
215
+
169
216
  const getNodesAndEdges = async ({
170
217
  id,
171
218
  version,
@@ -194,6 +241,19 @@ const getNodesAndEdges = async ({
194
241
  // Pre-calculate channel map for O(1) lookups
195
242
  const channelMap = createVersionedMap(channels);
196
243
 
244
+ // Trigger metadata lives on raw receive pointers. Hydrated resources replace
245
+ // those pointers with messages, so load the raw collections for this relation.
246
+ const [events, commands, queries, services, agents, domains] = await Promise.all([
247
+ getCollection('events'),
248
+ getCollection('commands'),
249
+ getCollection('queries'),
250
+ getCollection('services'),
251
+ getCollection('agents'),
252
+ getCollection('domains'),
253
+ ]);
254
+ const allMessages = [...events, ...commands, ...queries] as CollectionEntry<CollectionMessageTypes>[];
255
+ const messageReceivers = [...services, ...agents, ...domains] as MessageReceiver[];
256
+
197
257
  // We always render the message itself
198
258
  nodes.push({
199
259
  id: generateIdForNode(message),
@@ -201,6 +261,7 @@ const getNodesAndEdges = async ({
201
261
  targetPosition: 'left',
202
262
  data: {
203
263
  mode,
264
+ isFocused: true,
204
265
  message: {
205
266
  ...message.data,
206
267
  ...getOperationFields(message.data),
@@ -564,6 +625,63 @@ const getNodesAndEdges = async ({
564
625
  }
565
626
  });
566
627
 
628
+ const appendNode = (node: Node) => {
629
+ if (!nodes.some((existingNode: Node) => existingNode.id === node.id)) nodes.push(node);
630
+ };
631
+ const appendEdge = (source: any, target: any, label: string, colorMessage: CollectionEntry<CollectionMessageTypes>) => {
632
+ const id = generatedIdForEdge(source, target);
633
+ if (edges.some((edge: Edge) => edge.id === id)) return;
634
+
635
+ edges.push(
636
+ createEdge({
637
+ id,
638
+ source: generateIdForNode(source),
639
+ target: generateIdForNode(target),
640
+ label,
641
+ data: {
642
+ customColor: getColorFromString(colorMessage.data.id),
643
+ rootSourceAndTarget: {
644
+ source: { id: generateIdForNode(source), collection: source.collection },
645
+ target: { id: generateIdForNode(target), collection: target.collection },
646
+ },
647
+ },
648
+ })
649
+ );
650
+ };
651
+ const appendReceiver = (receiver: MessageReceiver) => {
652
+ const receiverId = generateIdForNode(receiver);
653
+ if (nodes.some((node: Node) => node.id === receiverId)) return;
654
+
655
+ appendNode(
656
+ createNode({
657
+ id: receiverId,
658
+ type: receiver.collection,
659
+ data: getTriggerReceiverNodeData(receiver, mode),
660
+ position: { x: 0, y: 0 },
661
+ })
662
+ );
663
+
664
+ if (receiver.collection !== 'domains') {
665
+ appendAgentToolNodesAndEdges({ agent: receiver, nodes, edges, mode });
666
+ }
667
+ };
668
+
669
+ // Complete both sides of a trigger chain around the focused message:
670
+ // triggering message -> receiver -> triggered message.
671
+ for (const relation of getTriggeredByOfMessage(messageReceivers, message, allMessages)) {
672
+ appendNode(createTriggerMessageNode(relation.message, mode));
673
+ appendReceiver(relation.receiver);
674
+ appendEdge(relation.message, relation.receiver, getEdgeLabelForMessageAsSource(relation.message), relation.message);
675
+ appendEdge(relation.receiver, message, getEdgeLabelForServiceAsTarget(message), message);
676
+ }
677
+
678
+ for (const relation of getTriggersOfMessage(messageReceivers, message, allMessages)) {
679
+ appendReceiver(relation.receiver);
680
+ appendNode(createTriggerMessageNode(relation.message, mode));
681
+ appendEdge(message, relation.receiver, getEdgeLabelForMessageAsSource(message), message);
682
+ appendEdge(relation.receiver, relation.message, getEdgeLabelForServiceAsTarget(relation.message), relation.message);
683
+ }
684
+
567
685
  nodes.forEach((node: any) => {
568
686
  flow.setNode(node.id, { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT });
569
687
  });
@@ -347,7 +347,10 @@ export const getNodesAndEdges = async ({
347
347
  id: generateIdForNode(service),
348
348
  sourcePosition: 'right',
349
349
  targetPosition: 'left',
350
- data: getResourceNodeData(service, mode),
350
+ data: {
351
+ ...getResourceNodeData(service, mode),
352
+ isFocused: service.collection === 'services',
353
+ },
351
354
  type: service.collection,
352
355
  });
353
356
 
@@ -606,6 +609,17 @@ export const getNodesAndEdges = async ({
606
609
  uniqueNodesById.set(node.id, node);
607
610
  }
608
611
  });
612
+
613
+ // Inbound message processing may create the service node before the primary
614
+ // node is added. Mark the surviving copy after deduplication so the service
615
+ // currently being viewed always retains its focus treatment.
616
+ if (service.collection === 'services') {
617
+ const focusedServiceNode = uniqueNodesById.get(generateIdForNode(service));
618
+ if (focusedServiceNode) {
619
+ focusedServiceNode.data = { ...focusedServiceNode.data, isFocused: true };
620
+ }
621
+ }
622
+
609
623
  const uniqueNodes = Array.from(uniqueNodesById.values());
610
624
 
611
625
  uniqueNodes.forEach((node: any) => {
@@ -162,7 +162,10 @@ export const buildContextMenuForMessage = ({
162
162
  collection: string;
163
163
  schemaPath?: string;
164
164
  }): ContextMenuItem[] => {
165
- const items: ContextMenuItem[] = [{ label: 'Read documentation', href: buildUrl(`/docs/${collection}/${id}/${version}`) }];
165
+ const items: ContextMenuItem[] = [
166
+ { label: 'Read documentation', href: buildUrl(`/docs/${collection}/${id}/${version}`) },
167
+ { label: 'Focus node', href: buildUrl(`/visualiser/${collection}/${id}/${version}`) },
168
+ ];
166
169
 
167
170
  if (schemaPath) {
168
171
  items.push({
@@ -250,7 +253,10 @@ export const buildContextMenuForService = ({
250
253
  specifications?: unknown;
251
254
  repository?: { url: string };
252
255
  }): ContextMenuItem[] => {
253
- const items: ContextMenuItem[] = [{ label: 'Read documentation', href: buildUrl(`/docs/services/${id}/${version}`) }];
256
+ const items: ContextMenuItem[] = [
257
+ { label: 'Read documentation', href: buildUrl(`/docs/services/${id}/${version}`) },
258
+ { label: 'Focus node', href: buildUrl(`/visualiser/services/${id}/${version}`) },
259
+ ];
254
260
 
255
261
  const specItems = getSpecMenuItems(specifications, id, version);
256
262
  items.push(...specItems);
@@ -283,7 +289,10 @@ export const buildContextMenuForAgent = ({
283
289
  version: string;
284
290
  repository?: { url: string };
285
291
  }): ContextMenuItem[] => {
286
- const items: ContextMenuItem[] = [{ label: 'Read documentation', href: buildUrl(`/docs/agents/${id}/${version}`) }];
292
+ const items: ContextMenuItem[] = [
293
+ { label: 'Read documentation', href: buildUrl(`/docs/agents/${id}/${version}`) },
294
+ { label: 'Focus node', href: buildUrl(`/visualiser/agents/${id}/${version}`) },
295
+ ];
287
296
 
288
297
  if (repository?.url) {
289
298
  items.push({
@@ -315,6 +324,7 @@ export const buildContextMenuForResource = ({
315
324
  }): ContextMenuItem[] => {
316
325
  return [
317
326
  { label: 'Read documentation', href: buildUrl(`/docs/${collection}/${id}/${version}`) },
327
+ { label: 'Focus node', href: buildUrl(`/visualiser/${collection}/${id}/${version}`) },
318
328
  {
319
329
  label: 'Read changelog',
320
330
  href: buildUrl(`/docs/${collection}/${id}/${version}/changelog`),
@@ -331,7 +341,7 @@ export const buildContextMenuForResource = ({
331
341
  export const buildContextMenuForSystem = ({ id, version }: { id: string; version: string }): ContextMenuItem[] => {
332
342
  return [
333
343
  { label: 'Read documentation', href: buildUrl(`/docs/systems/${id}/${version}`) },
334
- { label: 'View map', href: buildUrl(`/visualiser/systems/${id}/${version}`) },
344
+ { label: 'Focus node', href: buildUrl(`/visualiser/systems/${id}/${version}`) },
335
345
  { label: 'View context', href: buildUrl(`/visualiser/systems/${id}/${version}/context`) },
336
346
  ];
337
347
  };
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "SEE LICENSE IN LICENSE",
9
9
  "type": "module",
10
- "version": "4.1.5",
10
+ "version": "4.2.1",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -116,9 +116,9 @@
116
116
  "update-notifier": "^7.3.1",
117
117
  "uuid": "^10.0.0",
118
118
  "zod": "^4.3.6",
119
- "@eventcatalog/linter": "1.1.4",
120
- "@eventcatalog/sdk": "2.25.0",
121
- "@eventcatalog/visualiser": "^4.0.1"
119
+ "@eventcatalog/sdk": "2.26.0",
120
+ "@eventcatalog/visualiser": "^4.1.0",
121
+ "@eventcatalog/linter": "1.1.5"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@astrojs/check": "^0.9.9",