@eventcatalog/core 2.58.2 → 2.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) 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-PV4MP6U4.js → chunk-F5WMB6Q5.js} +1 -1
  6. package/dist/{chunk-ZBPULBAC.js → chunk-SCDNNFXH.js} +1 -1
  7. package/dist/{chunk-WK6GQM5P.js → chunk-T3QXQPTB.js} +1 -1
  8. package/dist/constants.cjs +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/eventcatalog.cjs +1 -1
  11. package/dist/eventcatalog.js +3 -3
  12. package/eventcatalog/src/components/Grids/DomainGrid.tsx +67 -2
  13. package/eventcatalog/src/components/Grids/MessageGrid.tsx +157 -41
  14. package/eventcatalog/src/components/Grids/ServiceGrid.tsx +78 -14
  15. package/eventcatalog/src/components/MDX/NodeGraph/Edges/MultilineEdgeLabel.tsx +52 -0
  16. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +13 -0
  17. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +4 -1
  18. package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Data.tsx +55 -16
  19. package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +180 -0
  20. package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +41 -0
  21. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +1 -1
  22. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +250 -59
  23. package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +3 -0
  24. package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +35 -1
  25. package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +2 -2
  26. package/eventcatalog/src/components/Tables/Table.tsx +22 -2
  27. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +152 -0
  28. package/eventcatalog/src/components/Tables/columns/index.tsx +3 -0
  29. package/eventcatalog/src/content.config.ts +57 -1
  30. package/eventcatalog/src/layouts/DiscoverLayout.astro +11 -1
  31. package/eventcatalog/src/pages/architecture/architecture.astro +9 -1
  32. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +1 -1
  33. package/eventcatalog/src/pages/discover/[type]/index.astro +11 -1
  34. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +11 -1
  35. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +50 -1
  36. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
  37. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +4 -1
  38. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +19 -1
  39. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
  40. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +1 -1
  41. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +80 -0
  42. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +52 -0
  43. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +9 -2
  44. package/eventcatalog/src/types/index.ts +20 -2
  45. package/eventcatalog/src/utils/collections/containers.ts +94 -0
  46. package/eventcatalog/src/utils/collections/icons.ts +3 -1
  47. package/eventcatalog/src/utils/collections/services.ts +15 -1
  48. package/eventcatalog/src/utils/collections/util.ts +4 -2
  49. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +155 -0
  50. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +188 -82
  51. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  52. package/package.json +1 -1
@@ -16,6 +16,7 @@ import { getNodesAndEdges as getNodesAndEdgesForFlows } from '@utils/node-graphs
16
16
  import { buildUrl } from '@utils/url-builder';
17
17
  import { getVersionFromCollection } from '@utils/collections/versions';
18
18
  import { pageDataLoader } from '@utils/page-loaders/page-data-loader';
19
+ import { getNodesAndEdges as getNodesAndEdgesForContainer } from '@utils/node-graphs/container-node-graph';
19
20
  import config from '@config';
20
21
 
21
22
  interface Props {
@@ -59,6 +60,7 @@ const getNodesAndEdgesFunctions = {
59
60
  queries: getNodesAndEdgesForQueries,
60
61
  domains: getNodesAndEdgesForDomain,
61
62
  flows: getNodesAndEdgesForFlows,
63
+ containers: getNodesAndEdgesForContainer,
62
64
  };
63
65
 
64
66
  let links: { label: string; url: string }[] = [];
@@ -121,6 +123,17 @@ if (collection === 'domains-entities') {
121
123
  nodes = fetchedNodes;
122
124
  edges = fetchedEdges;
123
125
  }
126
+
127
+ if (collection === 'services-containers') {
128
+ const { nodes: fetchedNodes, edges: fetchedEdges } = await getNodesAndEdgesForService({
129
+ id,
130
+ version,
131
+ renderMessages: false,
132
+ mode: 'full',
133
+ });
134
+ nodes = fetchedNodes;
135
+ edges = fetchedEdges;
136
+ }
124
137
  ---
125
138
 
126
139
  <div>
@@ -32,6 +32,7 @@ import CommandNode from './Nodes/Command';
32
32
  import ExternalSystemNode from './Nodes/ExternalSystem';
33
33
  import DomainNode from './Nodes/Domain';
34
34
  import AnimatedMessageEdge from './Edges/AnimatedMessageEdge';
35
+ import MultilineEdgeLabel from './Edges/MultilineEdgeLabel';
35
36
  import FlowEdge from './Edges/FlowEdge';
36
37
  import CustomNode from './Nodes/Custom';
37
38
  import DataNode from './Nodes/Data';
@@ -124,6 +125,7 @@ const NodeGraphBuilder = ({
124
125
  const edgeTypes = useMemo(
125
126
  () => ({
126
127
  animated: AnimatedMessageEdge,
128
+ multiline: MultilineEdgeLabel,
127
129
  'flow-edge': FlowEdge,
128
130
  }),
129
131
  []
@@ -241,7 +243,7 @@ const NodeGraphBuilder = ({
241
243
  eds.map((edge) => ({
242
244
  ...edge,
243
245
  animated: animateMessages,
244
- type: edge.type === 'flow-edge' ? 'flow-edge' : animateMessages ? 'animated' : 'default',
246
+ type: edge.type === 'flow-edge' || edge.type === 'multiline' ? edge.type : animateMessages ? 'animated' : 'default',
245
247
  data: { ...edge.data, animateMessages, animated: animateMessages },
246
248
  }))
247
249
  );
@@ -423,6 +425,7 @@ const NodeGraphBuilder = ({
423
425
  externalSystem: 'bg-pink-600',
424
426
  actor: 'bg-yellow-500',
425
427
  step: 'bg-gray-700',
428
+ data: 'bg-blue-600',
426
429
  };
427
430
 
428
431
  let legendForDomains: { [key: string]: { count: number; colorClass: string; groupId: string } } = {};
@@ -1,24 +1,63 @@
1
1
  import { Handle, Position } from '@xyflow/react';
2
2
 
3
3
  import { nodeComponents, type DataNode } from '@eventcatalog/visualizer';
4
+ import * as ContextMenu from '@radix-ui/react-context-menu';
5
+ import { buildUrl } from '@utils/url-builder';
4
6
  const DataComponent = nodeComponents.data;
5
7
 
6
- export default function DataNode(props: DataNode) {
8
+ interface Data {
9
+ data: {
10
+ data: {
11
+ id: string;
12
+ version: string;
13
+ name: string;
14
+ };
15
+ };
16
+ }
17
+
18
+ export default function DataNode(props: Data) {
19
+ const { id, version, name } = props.data.data;
20
+
7
21
  return (
8
- <div className="relative">
9
- <Handle
10
- type="target"
11
- position={Position.Left}
12
- style={{ width: 10, height: 10, background: 'blue', zIndex: 10 }}
13
- className="bg-gray-500"
14
- />
15
- <Handle
16
- type="source"
17
- position={Position.Right}
18
- style={{ width: 10, height: 10, background: 'blue', zIndex: 10 }}
19
- className="bg-gray-500"
20
- />
21
- <DataComponent {...props} />
22
- </div>
22
+ <ContextMenu.Root>
23
+ <ContextMenu.Trigger>
24
+ <div className="relative">
25
+ <Handle
26
+ type="target"
27
+ position={Position.Left}
28
+ style={{ width: 10, height: 10, background: 'blue', zIndex: 10 }}
29
+ className="bg-gray-500"
30
+ />
31
+ <Handle
32
+ type="source"
33
+ position={Position.Right}
34
+ style={{ width: 10, height: 10, background: 'blue', zIndex: 10 }}
35
+ className="bg-gray-500"
36
+ />
37
+ <DataComponent {...(props as any)} />
38
+ </div>
39
+ </ContextMenu.Trigger>
40
+ <ContextMenu.Portal>
41
+ <ContextMenu.Content className="min-w-[220px] bg-white rounded-md p-1 shadow-md border border-gray-200">
42
+ <ContextMenu.Item
43
+ asChild
44
+ className="text-sm px-2 py-1.5 outline-none cursor-pointer hover:bg-orange-100 rounded-sm flex items-center"
45
+ >
46
+ <a href={buildUrl(`/docs/containers/${id}/${version}`)}>Read documentation</a>
47
+ </ContextMenu.Item>
48
+ <ContextMenu.Separator className="h-[1px] bg-gray-200 m-1" />
49
+ <ContextMenu.Item asChild>
50
+ <a
51
+ href={buildUrl(`/docs/containers/${id}/${version}/changelog`)}
52
+ className="text-sm px-2 py-1.5 outline-none cursor-pointer hover:bg-orange-100 rounded-sm flex items-center"
53
+ target="_blank"
54
+ rel="noopener noreferrer"
55
+ >
56
+ Read changelog
57
+ </a>
58
+ </ContextMenu.Item>
59
+ </ContextMenu.Content>
60
+ </ContextMenu.Portal>
61
+ </ContextMenu.Root>
23
62
  );
24
63
  }
@@ -0,0 +1,180 @@
1
+ ---
2
+ import type { CollectionEntry } from 'astro:content';
3
+ import PillListFlat from '@components/Lists/PillListFlat';
4
+ import OwnersList from '@components/Lists/OwnersList';
5
+ import VersionList from '@components/Lists/VersionList.astro';
6
+ import { buildUrl } from '@utils/url-builder';
7
+ import { ScrollText, Workflow, RssIcon } from 'lucide-react';
8
+ import RepositoryList from '@components/Lists/RepositoryList.astro';
9
+ import { getOwner } from '@utils/collections/owners';
10
+ import CustomSideBarSectionList from '@components/Lists/CustomSideBarSectionList.astro';
11
+ import { isChangelogEnabled } from '@utils/feature';
12
+ import config from '@config';
13
+
14
+ interface Props {
15
+ container: CollectionEntry<'containers'>;
16
+ }
17
+
18
+ const { container } = Astro.props;
19
+
20
+ const servicesThatWriteToContainer = (container.data.servicesThatWriteToContainer as CollectionEntry<'services'>[]) || [];
21
+ const servicesThatReadFromContainer = (container.data.servicesThatReadFromContainer as CollectionEntry<'services'>[]) || [];
22
+ const ownersRaw = container.data?.owners || [];
23
+
24
+ const owners = await Promise.all<ReturnType<typeof getOwner>>(ownersRaw.map(getOwner));
25
+ const filteredOwners = owners.filter((o) => o !== undefined);
26
+ const resourceGroups = container.data?.resourceGroups || [];
27
+ const attachments = container.data.attachments || [];
28
+
29
+ const attachmentsList = attachments.map((a) => {
30
+ const attachmentIsURL = typeof a === 'string';
31
+ return {
32
+ label: attachmentIsURL ? a : (a.title ?? a.url),
33
+ href: attachmentIsURL ? a : a.url,
34
+ icon: attachmentIsURL ? 'ExternalLinkIcon' : (a.icon ?? 'ExternalLinkIcon'),
35
+ target: '_blank' as const,
36
+ subgroup: attachmentIsURL ? undefined : (a.type ?? ''),
37
+ };
38
+ });
39
+
40
+ const writesToList = servicesThatWriteToContainer?.map((p) => ({
41
+ label: p.data.name,
42
+ tag: `v${p.data.version}`,
43
+ collection: p.collection,
44
+ href: buildUrl(`/docs/services/${p.data.id}/${p.data.version}`),
45
+ }));
46
+
47
+ const readsFromList = servicesThatReadFromContainer?.map((p) => ({
48
+ label: p.data.name,
49
+ tag: `v${p.data.version}`,
50
+ collection: p.collection,
51
+ href: buildUrl(`/docs/services/${p.data.id}/${p.data.version}`),
52
+ }));
53
+
54
+ const ownersList = filteredOwners.map((o) => ({
55
+ label: o.data.name,
56
+ type: o.collection,
57
+ badge: o.collection === 'users' ? o.data.role : 'Team',
58
+ avatarUrl: o.collection === 'users' ? o.data.avatarUrl : '',
59
+ href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
60
+ }));
61
+
62
+ const shouldRenderSideBarSection = (section: string) => {
63
+ if (!container.data.detailsPanel) {
64
+ return true;
65
+ }
66
+ // @ts-ignore
67
+ return container.data.detailsPanel[section]?.visible ?? true;
68
+ };
69
+
70
+ const isRSSEnabled = config.rss?.enabled;
71
+ ---
72
+
73
+ <aside class="sticky top-28 left-0 space-y-8 h-full overflow-y-auto py-4">
74
+ <div class="">
75
+ {
76
+ resourceGroups
77
+ .filter((section) => section.items.length > 0 && section.sidebar)
78
+ .map((section) => <CustomSideBarSectionList section={section} />)
79
+ }
80
+ {
81
+ writesToList.length > 0 && shouldRenderSideBarSection('services') && (
82
+ <PillListFlat
83
+ color="pink"
84
+ title={`Services (writes) (${writesToList.length})`}
85
+ pills={writesToList}
86
+ emptyMessage={'No services are using this resource.'}
87
+ client:load
88
+ />
89
+ )
90
+ }
91
+ {
92
+ readsFromList.length > 0 && shouldRenderSideBarSection('services') && (
93
+ <PillListFlat
94
+ color="pink"
95
+ title={`Services (reads) (${readsFromList.length})`}
96
+ pills={readsFromList}
97
+ emptyMessage={'No services are using this resource.'}
98
+ client:load
99
+ />
100
+ )
101
+ }
102
+
103
+ {
104
+ container.data.versions && shouldRenderSideBarSection('versions') && (
105
+ <VersionList
106
+ title={`Versions (${container.data.versions?.length})`}
107
+ versions={container.data.versions}
108
+ collectionItem={container}
109
+ />
110
+ )
111
+ }
112
+
113
+ {
114
+ container.data.attachments && shouldRenderSideBarSection('attachments') && (
115
+ <PillListFlat
116
+ title={`Attachments (${attachmentsList.length})`}
117
+ pills={attachmentsList}
118
+ emptyMessage={`This resource does not have any attachments.`}
119
+ color="pink"
120
+ client:load
121
+ />
122
+ )
123
+ }
124
+
125
+ {
126
+ ownersList.length > 0 && shouldRenderSideBarSection('owners') && (
127
+ <OwnersList
128
+ title={`Owners (${ownersList.length})`}
129
+ owners={ownersList}
130
+ emptyMessage={`This resource does not have any documented owners.`}
131
+ client:load
132
+ />
133
+ )
134
+ }
135
+
136
+ {
137
+ container.data.repository && shouldRenderSideBarSection('repository') && (
138
+ <RepositoryList repository={container.data.repository?.url} language={container.data.repository?.language} />
139
+ )
140
+ }
141
+
142
+ {
143
+ isRSSEnabled && (
144
+ <div class="mx-auto pb-8 w-full max-w-lg divide-y divide-white/5 rounded-xl bg-white/5">
145
+ <span class="text-sm text-black font-semibold group-data-[hover]:text-black/80 capitalize">Container RSS Feed</span>
146
+ <ul role="list" class="space-y-2 mt-2">
147
+ <li class="has-tooltip rounded-md text-gray-600 group px-1 w-full hover:bg-gradient-to-l hover:from-purple-500 hover:to-purple-700 hover:text-white hover:font-normal ">
148
+ <a class={`flex items-center space-x-2`} target="_blank" href={buildUrl(`/rss/${container.collection}/rss.xml`)}>
149
+ <RssIcon className="h-4 w-4 text-gray-800 group-hover:text-white" strokeWidth={1} />
150
+ <span class="font-light text-sm truncate">RSS Feed</span>
151
+ </a>
152
+ </li>
153
+ </ul>
154
+ </div>
155
+ )
156
+ }
157
+
158
+ <div class="space-y-2">
159
+ <a
160
+ href={buildUrl(`/visualiser/${container.collection}/${container.data.id}/${container.data.version}`)}
161
+ class="flex items-center justify-center space-x-2 text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
162
+ >
163
+ <Workflow strokeWidth={2} size={16} />
164
+ <span class="block">View in visualiser</span>
165
+ </a>
166
+
167
+ {
168
+ isChangelogEnabled() && shouldRenderSideBarSection('changelog') && (
169
+ <a
170
+ href={buildUrl(`/docs/${container.collection}/${container.data.id}/${container.data.latestVersion}/changelog`)}
171
+ class="flex items-center space-x-2 justify-center text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
172
+ >
173
+ <ScrollText strokeWidth={2} size={16} />
174
+ <span class="block">Read changelog</span>
175
+ </a>
176
+ )
177
+ }
178
+ </div>
179
+ </div>
180
+ </aside>
@@ -24,6 +24,11 @@ const sends = (service.data.sends as CollectionEntry<'events'>[]) || [];
24
24
  // @ts-ignore
25
25
  const receives = (service.data.receives as CollectionEntry<'events'>[]) || [];
26
26
 
27
+ // @ts-ignore
28
+ const writesTo = (service.data.writesTo as CollectionEntry<'containers'>[]) || [];
29
+ // @ts-ignore
30
+ const readsFrom = (service.data.readsFrom as CollectionEntry<'containers'>[]) || [];
31
+
27
32
  // @ts-ignore
28
33
  const entities = (service.data.entities as CollectionEntry<'entities'>[]) || [];
29
34
 
@@ -70,6 +75,24 @@ const receivesList = receives
70
75
  href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
71
76
  }));
72
77
 
78
+ const writesToList = writesTo.map((p) => ({
79
+ label: p.data.name,
80
+ badge: p.collection,
81
+ color: p.collection === 'containers' ? 'green' : 'blue',
82
+ collection: p.collection,
83
+ tag: `v${p.data.version}`,
84
+ href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
85
+ }));
86
+
87
+ const readsFromList = readsFrom.map((p) => ({
88
+ label: p.data.name,
89
+ badge: p.collection,
90
+ color: p.collection === 'containers' ? 'green' : 'blue',
91
+ collection: p.collection,
92
+ tag: `v${p.data.version}`,
93
+ href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
94
+ }));
95
+
73
96
  const ownersList = filteredOwners.map((o) => ({
74
97
  label: o.data.name,
75
98
  type: o.collection,
@@ -148,6 +171,24 @@ const shouldRenderSideBarSection = (section: string) => {
148
171
  </>
149
172
  )
150
173
  }
174
+ <>
175
+ <PillListFlat
176
+ title={`Writes to (${writesToList.length})`}
177
+ pills={writesToList}
178
+ emptyMessage={`This service does not receive any messages.`}
179
+ color="orange"
180
+ client:load
181
+ />
182
+ </>
183
+ <>
184
+ <PillListFlat
185
+ title={`Reads from (${readsFromList.length})`}
186
+ pills={readsFromList}
187
+ emptyMessage={`This service does not read from any containers.`}
188
+ color="orange"
189
+ client:load
190
+ />
191
+ </>
151
192
  {
152
193
  service.data.versions && shouldRenderSideBarSection('versions') && (
153
194
  <VersionList versions={service.data.versions} collectionItem={service} />
@@ -64,7 +64,7 @@ const MessageList: React.FC<MessageListProps> = ({ messages, decodedCurrentPath,
64
64
  <span className="text-xs text-gray-400">{message.data.draft ? ' (DRAFT)' : ''}</span>
65
65
  </span>
66
66
  <span
67
- className={`ml-2 text-[10px] flex items-center gap-1 font-medium px-2 uppercase py-0.5 rounded ${getMessageColorByLabelOrCollection(message.collection, message.data?.sidebar?.badge)}`}
67
+ className={`ml-2 text-[10px] flex items-center gap-1 font-medium px-2 uppercase py-0.5 rounded ${getMessageColorByLabelOrCollection(message.collection, message.data?.sidebar?.badge)} ${message.data?.sidebar?.backgroundColor}`}
68
68
  >
69
69
  {message.data?.sidebar?.badge || getMessageCollectionName(message.collection, message)}
70
70
  </span>