@eventcatalog/core 3.40.1 → 3.41.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 (139) hide show
  1. package/bin/eventcatalog.config.d.ts +1 -0
  2. package/dist/__mocks__/astro-content.d.cts +1 -1
  3. package/dist/__mocks__/astro-content.d.ts +1 -1
  4. package/dist/analytics/analytics.cjs +1 -1
  5. package/dist/analytics/analytics.js +2 -2
  6. package/dist/analytics/count-resources.cjs +1 -0
  7. package/dist/analytics/count-resources.js +1 -1
  8. package/dist/analytics/log-build.cjs +3 -2
  9. package/dist/analytics/log-build.js +4 -4
  10. package/dist/catalog-to-astro-content-directory.cjs +1 -0
  11. package/dist/catalog-to-astro-content-directory.js +2 -2
  12. package/dist/{chunk-4UVFXLPI.js → chunk-3DVHEVHQ.js} +1 -0
  13. package/dist/{chunk-K3ZVEX2Y.js → chunk-3H2RT3CM.js} +1 -1
  14. package/dist/{chunk-BRMLU4PR.js → chunk-3R6TKNHG.js} +1 -1
  15. package/dist/{chunk-OIVICT4V.js → chunk-DKFIEB24.js} +1 -1
  16. package/dist/{chunk-55D645EH.js → chunk-IR4IAKWS.js} +1 -0
  17. package/dist/{chunk-YDXB3BD2.js → chunk-O6KT4DPL.js} +1 -1
  18. package/dist/{chunk-D6IBLY3O.js → chunk-QMORF42U.js} +1 -0
  19. package/dist/{chunk-HNG4KOYQ.js → chunk-QVJGIQYP.js} +1 -1
  20. package/dist/{chunk-4OEF5W6Y.js → chunk-TWFS6THS.js} +1 -1
  21. package/dist/{chunk-7UR72UMK.js → chunk-ZN3JKTWB.js} +5 -5
  22. package/dist/constants.cjs +1 -1
  23. package/dist/constants.js +1 -1
  24. package/dist/eventcatalog.cjs +5 -2
  25. package/dist/eventcatalog.config.d.cts +27 -11
  26. package/dist/eventcatalog.config.d.ts +27 -11
  27. package/dist/eventcatalog.js +14 -14
  28. package/dist/generate.cjs +1 -1
  29. package/dist/generate.js +3 -3
  30. package/dist/map-catalog-to-astro.cjs +1 -0
  31. package/dist/map-catalog-to-astro.js +1 -1
  32. package/dist/search-indexer.cjs +1 -0
  33. package/dist/search-indexer.js +1 -1
  34. package/dist/utils/cli-logger.cjs +1 -1
  35. package/dist/utils/cli-logger.js +2 -2
  36. package/dist/watcher.cjs +1 -0
  37. package/dist/watcher.js +2 -2
  38. package/eventcatalog/public/agents/anthropic-dark.svg +1 -0
  39. package/eventcatalog/public/agents/anthropic-light.svg +1 -0
  40. package/eventcatalog/public/agents/openai-dark.svg +1 -0
  41. package/eventcatalog/public/agents/openai-light.svg +1 -0
  42. package/eventcatalog/public/agents/openai.svg +1 -0
  43. package/eventcatalog/public/icons/agent/anthropic-dark.svg +1 -0
  44. package/eventcatalog/public/icons/agent/anthropic-light.svg +1 -0
  45. package/eventcatalog/public/icons/agent/anthropic.svg +1 -0
  46. package/eventcatalog/public/icons/agent/gemini.svg +1 -0
  47. package/eventcatalog/public/icons/agent/openai-dark.svg +1 -0
  48. package/eventcatalog/public/icons/agent/openai-light.svg +1 -0
  49. package/eventcatalog/public/icons/agent/openai.svg +1 -0
  50. package/eventcatalog/public/icons/agents/anthropic-dark.svg +1 -0
  51. package/eventcatalog/public/icons/agents/anthropic-light.svg +1 -0
  52. package/eventcatalog/public/icons/agents/anthropic.svg +1 -0
  53. package/eventcatalog/public/icons/agents/gemini.svg +1 -0
  54. package/eventcatalog/public/icons/agents/openai-dark.svg +1 -0
  55. package/eventcatalog/public/icons/agents/openai-light.svg +1 -0
  56. package/eventcatalog/public/icons/agents/openai.svg +1 -0
  57. package/eventcatalog/public/icons/protocols/mcp-dark.svg +1 -0
  58. package/eventcatalog/public/icons/protocols/mcp-light.svg +1 -0
  59. package/eventcatalog/public/icons/protocols/mcp.svg +1 -0
  60. package/eventcatalog/public/icons/tools/datadog.svg +1 -0
  61. package/eventcatalog/public/icons/tools/github.svg +1 -0
  62. package/eventcatalog/public/icons/tools/hubspot.svg +1 -0
  63. package/eventcatalog/public/icons/tools/slack.svg +1 -0
  64. package/eventcatalog/public/icons/tools/snowflake.svg +1 -0
  65. package/eventcatalog/public/icons/tools/zendesk.svg +1 -0
  66. package/eventcatalog/src/components/Badge.astro +41 -2
  67. package/eventcatalog/src/components/Grids/MessageGrid.tsx +16 -11
  68. package/eventcatalog/src/components/MDX/AgentTools/AgentTools.astro +132 -0
  69. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +2 -0
  70. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +8 -3
  71. package/eventcatalog/src/components/MDX/components.tsx +2 -0
  72. package/eventcatalog/src/components/Search/SearchModal.tsx +3 -0
  73. package/eventcatalog/src/components/Search/search-utils.ts +2 -0
  74. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -0
  75. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +3 -0
  76. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +165 -0
  77. package/eventcatalog/src/components/Tables/Discover/columns.tsx +132 -7
  78. package/eventcatalog/src/components/Tables/Table.tsx +2 -0
  79. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +17 -0
  80. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +17 -0
  81. package/eventcatalog/src/content.config.ts +83 -25
  82. package/eventcatalog/src/enterprise/collections/resource-docs-utils.ts +7 -4
  83. package/eventcatalog/src/enterprise/mcp/mcp-server.ts +9 -2
  84. package/eventcatalog/src/enterprise/tools/catalog-tools.ts +62 -28
  85. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -1
  86. package/eventcatalog/src/pages/_index.astro +1 -3
  87. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +5 -5
  88. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +5 -2
  89. package/eventcatalog/src/pages/directory/[type]/index.astro +2 -0
  90. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -0
  91. package/eventcatalog/src/pages/discover/[type]/index.astro +69 -23
  92. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId].mdx.ts +1 -0
  93. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -0
  94. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +1 -1
  95. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +47 -2
  96. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
  97. package/eventcatalog/src/pages/docs/[type]/[id]/[version].mdx.ts +2 -0
  98. package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +1 -0
  99. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +3 -0
  100. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
  101. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +24 -1
  102. package/eventcatalog/src/pages/docs/users/[id]/index.astro +24 -1
  103. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +2 -1
  104. package/eventcatalog/src/stores/sidebar-store/builders/agent.ts +141 -0
  105. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +9 -0
  106. package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +19 -0
  107. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +8 -2
  108. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +10 -7
  109. package/eventcatalog/src/stores/sidebar-store/state.ts +121 -2
  110. package/eventcatalog/src/types/index.ts +3 -1
  111. package/eventcatalog/src/utils/collection-colors.ts +5 -0
  112. package/eventcatalog/src/utils/collections/agents.ts +163 -0
  113. package/eventcatalog/src/utils/collections/commands.ts +3 -2
  114. package/eventcatalog/src/utils/collections/domains.ts +94 -15
  115. package/eventcatalog/src/utils/collections/events.ts +3 -2
  116. package/eventcatalog/src/utils/collections/flows.ts +20 -3
  117. package/eventcatalog/src/utils/collections/icons.ts +3 -1
  118. package/eventcatalog/src/utils/collections/messages.ts +35 -5
  119. package/eventcatalog/src/utils/collections/queries.ts +3 -2
  120. package/eventcatalog/src/utils/collections/schemas.ts +4 -4
  121. package/eventcatalog/src/utils/collections/services.ts +1 -1
  122. package/eventcatalog/src/utils/collections/teams.ts +5 -1
  123. package/eventcatalog/src/utils/collections/types.ts +1 -0
  124. package/eventcatalog/src/utils/collections/users.ts +5 -1
  125. package/eventcatalog/src/utils/collections/util.ts +2 -0
  126. package/eventcatalog/src/utils/eventcatalog-config/catalog.ts +1 -0
  127. package/eventcatalog/src/utils/llms.ts +1 -1
  128. package/eventcatalog/src/utils/node-graphs/agents-node-graph.ts +4 -0
  129. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -2
  130. package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +42 -9
  131. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +20 -0
  132. package/eventcatalog/src/utils/node-graphs/export-node-graph.ts +4 -0
  133. package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +22 -1
  134. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +175 -98
  135. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +117 -23
  136. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +30 -0
  137. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  138. package/eventcatalog/src/utils/resource-reference-colors.ts +1 -0
  139. package/package.json +11 -4
@@ -0,0 +1,163 @@
1
+ import { getCollection } from 'astro:content';
2
+ import type { CollectionEntry } from 'astro:content';
3
+ import type { CollectionMessageTypes } from '@types';
4
+ import { createVersionedMap, findInMap, versionMatches } from '@utils/collections/util';
5
+ import { getChannels } from './channels';
6
+
7
+ export type Agent = CollectionEntry<'agents'>;
8
+
9
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
10
+
11
+ interface Props {
12
+ getAllVersions?: boolean;
13
+ returnBody?: boolean;
14
+ }
15
+
16
+ let memoryCache: Record<string, Agent[]> = {};
17
+
18
+ export const getAgents = async ({ getAllVersions = true, returnBody = false }: Props = {}): Promise<Agent[]> => {
19
+ const cacheKey = `${getAllVersions ? 'allVersions' : 'currentVersions'}-${returnBody ? 'withBody' : 'noBody'}`;
20
+
21
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
22
+ return memoryCache[cacheKey];
23
+ }
24
+
25
+ const [allAgents, allEvents, allCommands, allQueries, allContainers, allFlows] = await Promise.all([
26
+ getCollection('agents'),
27
+ getCollection('events'),
28
+ getCollection('commands'),
29
+ getCollection('queries'),
30
+ getCollection('containers'),
31
+ getCollection('flows'),
32
+ ]);
33
+
34
+ const allMessages = [...allEvents, ...allCommands, ...allQueries];
35
+ const agentMap = createVersionedMap(allAgents);
36
+ const messageMap = createVersionedMap(allMessages);
37
+ const containerMap = createVersionedMap(allContainers);
38
+ const flowMap = createVersionedMap(allFlows);
39
+
40
+ const targetAgents = allAgents.filter((agent) => {
41
+ if (agent.data.hidden === true) return false;
42
+ if (!getAllVersions && agent.filePath?.includes('versioned')) return false;
43
+ return true;
44
+ });
45
+
46
+ const processedAgents = await Promise.all(
47
+ targetAgents.map(async (agent) => {
48
+ const agentVersions = agentMap.get(agent.data.id) || [];
49
+ const latestVersion = agentVersions[0]?.data.version || agent.data.version;
50
+ const versions = agentVersions.map((a) => a.data.version);
51
+
52
+ const sends = (agent.data.sends || [])
53
+ .map((m) => findInMap(messageMap, m.id, m.version))
54
+ .filter((e): e is CollectionEntry<CollectionMessageTypes> => !!e);
55
+
56
+ const receives = (agent.data.receives || [])
57
+ .map((m) => findInMap(messageMap, m.id, m.version))
58
+ .filter((e): e is CollectionEntry<CollectionMessageTypes> => !!e);
59
+
60
+ const mappedWritesTo = (agent.data.writesTo || [])
61
+ .map((c) => findInMap(containerMap, c.id, c.version))
62
+ .filter((e): e is CollectionEntry<'containers'> => !!e);
63
+
64
+ const mappedReadsFrom = (agent.data.readsFrom || [])
65
+ .map((c) => findInMap(containerMap, c.id, c.version))
66
+ .filter((e): e is CollectionEntry<'containers'> => !!e);
67
+
68
+ const mappedFlows = (agent.data.flows || [])
69
+ .map((f) => findInMap(flowMap, f.id, f.version))
70
+ .filter((f): f is CollectionEntry<'flows'> => !!f);
71
+
72
+ return {
73
+ ...agent,
74
+ data: {
75
+ ...agent.data,
76
+ writesTo: mappedWritesTo as any,
77
+ readsFrom: mappedReadsFrom as any,
78
+ flows: mappedFlows as any,
79
+ receives: receives as any,
80
+ sends: sends as any,
81
+ versions,
82
+ latestVersion,
83
+ },
84
+ nodes: {
85
+ receives: receives as any,
86
+ sends: sends as any,
87
+ },
88
+ body: returnBody ? agent.body : undefined,
89
+ };
90
+ })
91
+ );
92
+
93
+ processedAgents.sort((a, b) => {
94
+ return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
95
+ });
96
+
97
+ memoryCache[cacheKey] = processedAgents;
98
+
99
+ return processedAgents;
100
+ };
101
+
102
+ export const getProducersOfMessage = (agents: Agent[], message: CollectionEntry<'events' | 'commands' | 'queries'>) => {
103
+ return agents.filter((agent) => {
104
+ return agent.data.sends?.some((send) => {
105
+ const idMatch = send.id === message.data.id;
106
+ if (!send.version) return idMatch;
107
+ if (send.version === 'latest') return idMatch;
108
+ return idMatch && versionMatches(message.data.version, send.version);
109
+ });
110
+ });
111
+ };
112
+
113
+ export const getConsumersOfMessage = (agents: Agent[], message: CollectionEntry<'events' | 'commands' | 'queries'>) => {
114
+ return agents.filter((agent) => {
115
+ return agent.data.receives?.some((receive) => {
116
+ const idMatch = receive.id === message.data.id;
117
+ if (!receive.version) return idMatch;
118
+ if (receive.version === 'latest') return idMatch;
119
+ return idMatch && versionMatches(message.data.version, receive.version);
120
+ });
121
+ });
122
+ };
123
+
124
+ export const getChannelsForAgent = async (agentId: string, version?: string): Promise<CollectionEntry<'channels'>[]> => {
125
+ const allAgents = await getCollection('agents');
126
+ const allChannels = await getChannels({ getAllVersions: true });
127
+
128
+ const agentMap = createVersionedMap(allAgents);
129
+ const agent = findInMap(agentMap, agentId, version);
130
+ if (!agent) return [];
131
+
132
+ const sends = agent.data.sends ?? [];
133
+ const receives = agent.data.receives ?? [];
134
+
135
+ const channelPointers: Array<{ id: string; version?: string }> = [];
136
+
137
+ for (const send of sends) {
138
+ for (const channel of send.to ?? []) {
139
+ channelPointers.push({ id: channel.id, version: channel.version });
140
+ }
141
+ }
142
+
143
+ for (const receive of receives) {
144
+ for (const channel of receive.from ?? []) {
145
+ channelPointers.push({ id: channel.id, version: channel.version });
146
+ }
147
+ }
148
+
149
+ const channelMap = createVersionedMap(allChannels);
150
+ const seen = new Set<string>();
151
+ const channels: CollectionEntry<'channels'>[] = [];
152
+
153
+ for (const pointer of channelPointers) {
154
+ const key = `${pointer.id}-${pointer.version ?? 'latest'}`;
155
+ if (seen.has(key)) continue;
156
+ seen.add(key);
157
+
158
+ const match = findInMap(channelMap, pointer.id, pointer.version);
159
+ if (match) channels.push(match as CollectionEntry<'channels'>);
160
+ }
161
+
162
+ return channels;
163
+ };
@@ -26,8 +26,9 @@ export const getCommands = async ({ getAllVersions = true, hydrateServices = tru
26
26
  }
27
27
 
28
28
  // 1. Fetch collections in parallel
29
- const [allCommands, allServices, allChannels, allDataProducts] = await Promise.all([
29
+ const [allCommands, allAgents, allServices, allChannels, allDataProducts] = await Promise.all([
30
30
  getCollection('commands'),
31
+ getCollection('agents'),
31
32
  getCollection('services'),
32
33
  getCollection('channels'),
33
34
  getCollection('data-products'),
@@ -35,7 +36,7 @@ export const getCommands = async ({ getAllVersions = true, hydrateServices = tru
35
36
 
36
37
  // 2. Build optimized maps
37
38
  const commandMap = createVersionedMap(allCommands);
38
- const pcIndex = buildProducerConsumerIndex(allServices, allDataProducts);
39
+ const pcIndex = buildProducerConsumerIndex(allAgents, allServices, allDataProducts);
39
40
 
40
41
  // Build channel lookup map: channelId → channel entries
41
42
  const channelById = new Map<string, typeof allChannels>();
@@ -2,7 +2,7 @@ import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import path from 'path';
4
4
  import type { CollectionMessageTypes } from '@types';
5
- import type { Service } from './types';
5
+ import type { Agent, Service } from './types';
6
6
  import { createVersionedMap, findInMap, processSpecifications } from '@utils/collections/util';
7
7
 
8
8
  const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
@@ -60,6 +60,45 @@ const hydrateServices = (
60
60
  });
61
61
  };
62
62
 
63
+ const hydrateAgents = (
64
+ agentsList: any[],
65
+ agentMap: Map<string, any[]>,
66
+ messageMap: Map<string, any[]>,
67
+ containerMap: Map<string, any[]>
68
+ ) => {
69
+ return agentsList
70
+ .map((agent: { id: string; version: string | undefined }) => findInMap(agentMap, agent.id, agent.version))
71
+ .filter((a) => !!a)
72
+ .map((agent) => {
73
+ const sends = (agent.data.sends || [])
74
+ .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
75
+ .filter((m: any) => !!m);
76
+
77
+ const receives = (agent.data.receives || [])
78
+ .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
79
+ .filter((m: any) => !!m);
80
+
81
+ const readsFrom = (agent.data.readsFrom || [])
82
+ .map((c: any) => findInMap(containerMap, c.id, c.version))
83
+ .filter((c: any) => !!c);
84
+
85
+ const writesTo = (agent.data.writesTo || [])
86
+ .map((c: any) => findInMap(containerMap, c.id, c.version))
87
+ .filter((c: any) => !!c);
88
+
89
+ return {
90
+ ...agent,
91
+ data: {
92
+ ...agent.data,
93
+ sends: sends as any,
94
+ receives: receives as any,
95
+ readsFrom: readsFrom as any,
96
+ writesTo: writesTo as any,
97
+ },
98
+ };
99
+ });
100
+ };
101
+
63
102
  // --- MAIN FUNCTION ---
64
103
 
65
104
  export const getDomains = async ({
@@ -78,18 +117,29 @@ export const getDomains = async ({
78
117
  }
79
118
 
80
119
  // 1. Fetch collections (always fetch messages to hydrate domain-level sends/receives)
81
- const [allDomains, allServices, allEntities, allFlows, allEvents, allCommands, allQueries, allContainers, allDataProducts] =
82
- await Promise.all([
83
- getCollection('domains'),
84
- getCollection('services'),
85
- getCollection('entities'),
86
- getCollection('flows'),
87
- getCollection('events'),
88
- getCollection('commands'),
89
- getCollection('queries'),
90
- getCollection('containers'),
91
- getCollection('data-products'),
92
- ]);
120
+ const [
121
+ allDomains,
122
+ allAgents,
123
+ allServices,
124
+ allEntities,
125
+ allFlows,
126
+ allEvents,
127
+ allCommands,
128
+ allQueries,
129
+ allContainers,
130
+ allDataProducts,
131
+ ] = await Promise.all([
132
+ getCollection('domains'),
133
+ getCollection('agents'),
134
+ getCollection('services'),
135
+ getCollection('entities'),
136
+ getCollection('flows'),
137
+ getCollection('events'),
138
+ getCollection('commands'),
139
+ getCollection('queries'),
140
+ getCollection('containers'),
141
+ getCollection('data-products'),
142
+ ]);
93
143
 
94
144
  const allMessages = [...allEvents, ...allCommands, ...allQueries];
95
145
  const messageMap = createVersionedMap(allMessages);
@@ -97,6 +147,7 @@ export const getDomains = async ({
97
147
 
98
148
  // 2. Build optimized maps
99
149
  const domainMap = createVersionedMap(allDomains);
150
+ const agentMap = createVersionedMap(allAgents);
100
151
  const serviceMap = createVersionedMap(allServices);
101
152
  const entityMap = createVersionedMap(allEntities);
102
153
  const flowMap = createVersionedMap(allFlows);
@@ -127,13 +178,18 @@ export const getDomains = async ({
127
178
  .map((subDomain: any) => {
128
179
  // Hydrate services for the subdomain
129
180
  let hydratedServices = subDomain.data.services || [];
181
+ let hydratedAgents = subDomain.data.agents || [];
130
182
  if (enrichServices) {
131
183
  hydratedServices = hydrateServices(subDomain.data.services || [], serviceMap, messageMap, containerMap);
184
+ hydratedAgents = hydrateAgents(subDomain.data.agents || [], agentMap, messageMap, containerMap);
132
185
  } else {
133
186
  // Just resolve the service objects without enrichment
134
187
  hydratedServices = (subDomain.data.services || [])
135
188
  .map((service: { id: string; version: string | undefined }) => findInMap(serviceMap, service.id, service.version))
136
189
  .filter((s: any) => !!s);
190
+ hydratedAgents = (subDomain.data.agents || [])
191
+ .map((agent: { id: string; version: string | undefined }) => findInMap(agentMap, agent.id, agent.version))
192
+ .filter((a: any) => !!a);
137
193
  }
138
194
 
139
195
  // Hydrate data products for the subdomain
@@ -146,6 +202,7 @@ export const getDomains = async ({
146
202
  data: {
147
203
  ...subDomain.data,
148
204
  services: hydratedServices as any,
205
+ agents: hydratedAgents as any,
149
206
  'data-products': subdomainDataProducts as any,
150
207
  },
151
208
  };
@@ -173,23 +230,33 @@ export const getDomains = async ({
173
230
 
174
231
  // Resolve Services for Main Domain
175
232
  const servicesInDomain = domain.data.services || [];
233
+ const agentsInDomain = domain.data.agents || [];
176
234
 
177
235
  // Hydrate main domain services
178
236
  let hydratedMainServices = [];
237
+ let hydratedMainAgents = [];
179
238
  if (enrichServices) {
180
239
  hydratedMainServices = hydrateServices(servicesInDomain, serviceMap, messageMap, containerMap);
240
+ hydratedMainAgents = hydrateAgents(agentsInDomain, agentMap, messageMap, containerMap);
181
241
  } else {
182
242
  hydratedMainServices = servicesInDomain
183
243
  .map((service: { id: string; version: string | undefined }) => findInMap(serviceMap, service.id, service.version))
184
244
  .filter((s) => !!s);
245
+ hydratedMainAgents = agentsInDomain
246
+ .map((agent: { id: string; version: string | undefined }) => findInMap(agentMap, agent.id, agent.version))
247
+ .filter((a) => !!a);
185
248
  }
186
249
 
187
250
  // Get already-hydrated subdomain services
188
251
  const hydratedSubdomainServices = subDomains.flatMap((subDomain: any) => subDomain.data.services || []);
252
+ const hydratedSubdomainAgents = subDomains.flatMap((subDomain: any) => subDomain.data.agents || []);
189
253
 
190
254
  const services = includeServicesInSubdomains
191
255
  ? [...(hydratedMainServices as any), ...(hydratedSubdomainServices as any)]
192
256
  : (hydratedMainServices as any);
257
+ const agents = includeServicesInSubdomains
258
+ ? [...(hydratedMainAgents as any), ...(hydratedSubdomainAgents as any)]
259
+ : (hydratedMainAgents as any);
193
260
 
194
261
  // Hydrate domain-level sends and receives
195
262
  const domainSends = (domain.data.sends || [])
@@ -204,6 +271,7 @@ export const getDomains = async ({
204
271
  ...domain,
205
272
  data: {
206
273
  ...domain.data,
274
+ agents: agents as any,
207
275
  services: services as any, // Cast to avoid deep type issues with enriched data
208
276
  domains: subDomains as any,
209
277
  entities: entities as any,
@@ -236,6 +304,7 @@ export const getMessagesForDomain = async (
236
304
  ): Promise<{ sends: CollectionEntry<CollectionMessageTypes>[]; receives: CollectionEntry<CollectionMessageTypes>[] }> => {
237
305
  // We already have the services from the domain
238
306
  const services = domain.data.services as unknown as CollectionEntry<'services'>[];
307
+ const agents = ((domain.data.agents as unknown as CollectionEntry<'agents'>[]) || []) as CollectionEntry<'agents'>[];
239
308
 
240
309
  const [events, commands, queries] = await Promise.all([
241
310
  getCollection('events'),
@@ -249,14 +318,16 @@ export const getMessagesForDomain = async (
249
318
  // Get service-level sends/receives
250
319
  const serviceSends = services.flatMap((service) => service.data.sends || []);
251
320
  const serviceReceives = services.flatMap((service) => service.data.receives || []);
321
+ const agentSends = agents.flatMap((agent) => agent.data.sends || []);
322
+ const agentReceives = agents.flatMap((agent) => agent.data.receives || []);
252
323
 
253
324
  // Get domain-level sends/receives (already hydrated if domain came from getDomains)
254
325
  const domainSends = domain.data.sends || [];
255
326
  const domainReceives = domain.data.receives || [];
256
327
 
257
328
  // Combine and deduplicate - domain-level messages take priority
258
- const allSends = [...domainSends, ...serviceSends];
259
- const allReceives = [...domainReceives, ...serviceReceives];
329
+ const allSends = [...domainSends, ...serviceSends, ...agentSends];
330
+ const allReceives = [...domainReceives, ...serviceReceives, ...agentReceives];
260
331
 
261
332
  const sendsMessages = allSends
262
333
  .map((send: any) => {
@@ -380,6 +451,14 @@ export const getDomainsForService = async (service: Service): Promise<Domain[]>
380
451
  });
381
452
  };
382
453
 
454
+ export const getDomainsForAgent = async (agent: Agent): Promise<Domain[]> => {
455
+ const domains = await getDomains({ getAllVersions: false });
456
+ return domains.filter((d) => {
457
+ const agents = ((d.data.agents as unknown as Agent[]) || []) as Agent[];
458
+ return agents.some((a) => a.data.id === agent.data.id);
459
+ });
460
+ };
461
+
383
462
  export const domainHasEntities = (domain: Domain): boolean => {
384
463
  return (domain.data.entities && domain.data.entities.length > 0) || false;
385
464
  };
@@ -26,8 +26,9 @@ export const getEvents = async ({ getAllVersions = true, hydrateServices = true
26
26
  }
27
27
 
28
28
  // 1. Fetch collections in parallel
29
- const [allEvents, allServices, allChannels, allDataProducts] = await Promise.all([
29
+ const [allEvents, allAgents, allServices, allChannels, allDataProducts] = await Promise.all([
30
30
  getCollection('events'),
31
+ getCollection('agents'),
31
32
  getCollection('services'),
32
33
  getCollection('channels'),
33
34
  getCollection('data-products'),
@@ -35,7 +36,7 @@ export const getEvents = async ({ getAllVersions = true, hydrateServices = true
35
36
 
36
37
  // 2. Build optimized maps
37
38
  const eventMap = createVersionedMap(allEvents);
38
- const pcIndex = buildProducerConsumerIndex(allServices, allDataProducts);
39
+ const pcIndex = buildProducerConsumerIndex(allAgents, allServices, allDataProducts);
39
40
 
40
41
  // Build channel lookup map: channelId → channel entries
41
42
  const channelById = new Map<string, typeof allChannels>();
@@ -3,6 +3,7 @@ import type { CollectionEntry } from 'astro:content';
3
3
  import { createVersionedMap, findInMap } from '@utils/collections/util';
4
4
  import { getDomains } from './domains';
5
5
  import { getServices } from './services';
6
+ import { getAgents } from './agents';
6
7
 
7
8
  const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
8
9
  export type Flow = CollectionEntry<'flows'>;
@@ -24,11 +25,12 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
24
25
  }
25
26
 
26
27
  // 1. Fetch collections in parallel
27
- const [allFlows, allEvents, allCommands, allQueries, allContainers, allDataProducts] = await Promise.all([
28
+ const [allFlows, allEvents, allCommands, allQueries, allAgents, allContainers, allDataProducts] = await Promise.all([
28
29
  getCollection('flows'),
29
30
  getCollection('events'),
30
31
  getCollection('commands'),
31
32
  getCollection('queries'),
33
+ getCollection('agents'),
32
34
  getCollection('containers'),
33
35
  getCollection('data-products'),
34
36
  ]);
@@ -38,6 +40,7 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
38
40
  // 2. Build optimized maps
39
41
  const flowMap = createVersionedMap(allFlows);
40
42
  const messageMap = createVersionedMap(allMessages);
43
+ const agentMap = createVersionedMap(allAgents);
41
44
  const containerMap = createVersionedMap(allContainers);
42
45
  const dataProductMap = createVersionedMap(allDataProducts);
43
46
 
@@ -82,6 +85,18 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
82
85
  };
83
86
  }
84
87
 
88
+ if (step.agent) {
89
+ const pointer = step.agent;
90
+ if (!pointer) return { ...step, type: 'node' };
91
+ const agent = findInMap(agentMap, pointer.id, pointer.version);
92
+
93
+ return {
94
+ ...step,
95
+ type: 'agents',
96
+ agent: agent ? [agent] : [],
97
+ };
98
+ }
99
+
85
100
  if (!step.message) return { ...step, type: 'node' }; // Preserve existing step data for non-messages
86
101
 
87
102
  const message = findInMap(messageMap, step.message.id, step.message.version);
@@ -116,16 +131,18 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
116
131
  };
117
132
 
118
133
  export const getFlowsNotInAnyResource = async (): Promise<Flow[]> => {
119
- const [flows, domains, services] = await Promise.all([
134
+ const [flows, domains, services, agents] = await Promise.all([
120
135
  getFlows({ getAllVersions: false }),
121
136
  getDomains({ getAllVersions: false }),
122
137
  getServices({ getAllVersions: false }),
138
+ getAgents({ getAllVersions: false }),
123
139
  ]);
124
140
 
125
141
  const flowsNotInAnyResource = flows.filter((flow) => {
126
142
  const domainsForFlow = domains.filter((domain) => domain.data.flows?.some((f: any) => f.id === flow.id));
127
143
  const servicesForFlow = services.filter((service) => service.data.flows?.some((f: any) => f.id === flow.id));
128
- return domainsForFlow.length === 0 && servicesForFlow.length === 0;
144
+ const agentsForFlow = agents.filter((agent) => agent.data.flows?.some((f: any) => f.id === flow.id));
145
+ return domainsForFlow.length === 0 && servicesForFlow.length === 0 && agentsForFlow.length === 0;
129
146
  });
130
147
  return flowsNotInAnyResource;
131
148
  };
@@ -12,13 +12,15 @@ import {
12
12
  MapIcon,
13
13
  CubeIcon,
14
14
  } from '@heroicons/react/24/outline';
15
- import { BookText, Box, DatabaseIcon } from 'lucide-react';
15
+ import { BookText, Bot, Box, DatabaseIcon } from 'lucide-react';
16
16
  import { getColorForCollection } from '@utils/collection-colors';
17
17
 
18
18
  export const getIconForCollection = (collection: string) => {
19
19
  switch (collection) {
20
20
  case 'domains':
21
21
  return RectangleGroupIcon;
22
+ case 'agents':
23
+ return Bot;
22
24
  case 'services':
23
25
  return ServerIcon;
24
26
  case 'events':
@@ -20,6 +20,7 @@ interface HydrateProducersAndConsumersProps {
20
20
  latestVersion?: string;
21
21
  };
22
22
  };
23
+ agents?: CollectionEntry<'agents'>[];
23
24
  services: CollectionEntry<'services'>[];
24
25
  dataProducts: CollectionEntry<'data-products'>[];
25
26
  hydrate?: boolean;
@@ -31,6 +32,7 @@ interface HydrateProducersAndConsumersProps {
31
32
  */
32
33
  export const hydrateProducersAndConsumers = ({
33
34
  message,
35
+ agents = [],
34
36
  services = [],
35
37
  dataProducts = [],
36
38
  hydrate = true,
@@ -44,11 +46,23 @@ export const hydrateProducersAndConsumers = ({
44
46
  return satisfies(messageVersion, pointerVersion);
45
47
  };
46
48
 
47
- const toResult = <T extends CollectionEntry<'services'> | CollectionEntry<'data-products'>>(resource: T) => {
49
+ const toResult = <T extends CollectionEntry<'agents'> | CollectionEntry<'services'> | CollectionEntry<'data-products'>>(
50
+ resource: T
51
+ ) => {
48
52
  if (!hydrate) return { id: resource.data.id, version: resource.data.version };
49
53
  return resource;
50
54
  };
51
55
 
56
+ // Agents that send this message (producers)
57
+ const agentProducers = agents
58
+ .filter((a) => a.data.sends?.some((p) => p.id === messageId && matchesVersion(p.version)))
59
+ .map(toResult);
60
+
61
+ // Agents that receive this message (consumers)
62
+ const agentConsumers = agents
63
+ .filter((a) => a.data.receives?.some((p) => p.id === messageId && matchesVersion(p.version)))
64
+ .map(toResult);
65
+
52
66
  // Services that send this message (producers)
53
67
  const serviceProducers = services
54
68
  .filter((s) => s.data.sends?.some((p) => p.id === messageId && matchesVersion(p.version)))
@@ -70,15 +84,15 @@ export const hydrateProducersAndConsumers = ({
70
84
  .map(toResult);
71
85
 
72
86
  return {
73
- producers: [...serviceProducers, ...dataProductProducers],
74
- consumers: [...serviceConsumers, ...dataProductConsumers],
87
+ producers: [...agentProducers, ...serviceProducers, ...dataProductProducers],
88
+ consumers: [...agentConsumers, ...serviceConsumers, ...dataProductConsumers],
75
89
  };
76
90
  };
77
91
 
78
92
  // --- Reverse Index for O(1) producer/consumer lookups ---
79
93
 
80
94
  interface IndexEntry {
81
- resource: CollectionEntry<'services'> | CollectionEntry<'data-products'>;
95
+ resource: CollectionEntry<'agents'> | CollectionEntry<'services'> | CollectionEntry<'data-products'>;
82
96
  pointerVersion?: string;
83
97
  }
84
98
 
@@ -92,6 +106,7 @@ export interface ProducerConsumerIndex {
92
106
  * Call once before the enrichment loop, then use lookupProducersAndConsumers per message.
93
107
  */
94
108
  export const buildProducerConsumerIndex = (
109
+ agents: CollectionEntry<'agents'>[],
95
110
  services: CollectionEntry<'services'>[],
96
111
  dataProducts: CollectionEntry<'data-products'>[]
97
112
  ): ProducerConsumerIndex => {
@@ -107,6 +122,19 @@ export const buildProducerConsumerIndex = (
107
122
  list.push(entry);
108
123
  };
109
124
 
125
+ for (const agent of agents || []) {
126
+ if (agent.data.sends) {
127
+ for (const p of agent.data.sends) {
128
+ addEntry(producers, p.id, { resource: agent, pointerVersion: p.version });
129
+ }
130
+ }
131
+ if (agent.data.receives) {
132
+ for (const p of agent.data.receives) {
133
+ addEntry(consumers, p.id, { resource: agent, pointerVersion: p.version });
134
+ }
135
+ }
136
+ }
137
+
110
138
  for (const service of services || []) {
111
139
  if (service.data.sends) {
112
140
  for (const p of service.data.sends) {
@@ -162,7 +190,9 @@ export const lookupProducersAndConsumers = ({ message, index, hydrate = true }:
162
190
  return satisfies(messageVersion, pointerVersion);
163
191
  };
164
192
 
165
- const toResult = <T extends CollectionEntry<'services'> | CollectionEntry<'data-products'>>(resource: T) => {
193
+ const toResult = <T extends CollectionEntry<'agents'> | CollectionEntry<'services'> | CollectionEntry<'data-products'>>(
194
+ resource: T
195
+ ) => {
166
196
  if (!hydrate) return { id: resource.data.id, version: resource.data.version };
167
197
  return resource;
168
198
  };
@@ -25,8 +25,9 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
25
25
  }
26
26
 
27
27
  // 1. Fetch collections in parallel
28
- const [allQueries, allServices, allChannels, allDataProducts] = await Promise.all([
28
+ const [allQueries, allAgents, allServices, allChannels, allDataProducts] = await Promise.all([
29
29
  getCollection('queries'),
30
+ getCollection('agents'),
30
31
  getCollection('services'),
31
32
  getCollection('channels'),
32
33
  getCollection('data-products'),
@@ -34,7 +35,7 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
34
35
 
35
36
  // 2. Build optimized maps
36
37
  const queryMap = createVersionedMap(allQueries);
37
- const pcIndex = buildProducerConsumerIndex(allServices, allDataProducts);
38
+ const pcIndex = buildProducerConsumerIndex(allAgents, allServices, allDataProducts);
38
39
 
39
40
  // Build channel lookup map: channelId → channel entries
40
41
  const channelById = new Map<string, typeof allChannels>();
@@ -10,13 +10,13 @@ export type Schema = {
10
10
  format: string;
11
11
  };
12
12
 
13
- const getPublicPath = (resource: CollectionEntry<PageTypes>): string | undefined => {
13
+ const getPublicPath = (resource: CollectionEntry<PageTypes> | any): string | undefined => {
14
14
  if (!resource.filePath) return undefined;
15
15
  const folderName = getFolderNameFromFilePath(resource.filePath);
16
16
  return path.join('/generated', resource.collection, folderName);
17
17
  };
18
18
 
19
- export const getSchemaURL = (resource: CollectionEntry<PageTypes>) => {
19
+ export const getSchemaURL = (resource: CollectionEntry<PageTypes> | any) => {
20
20
  const publicPath = getPublicPath(resource);
21
21
  const schemaFilePath = resource?.data?.schemaPath;
22
22
 
@@ -45,7 +45,7 @@ export const getSchemaFormatFromURL = (url: string) => {
45
45
  return format;
46
46
  };
47
47
 
48
- export const getSchemasFromResource = (resource: CollectionEntry<PageTypes>): Schema[] => {
48
+ export const getSchemasFromResource = (resource: CollectionEntry<PageTypes> | any): Schema[] => {
49
49
  const schemaPublicPath = getSchemaURL(resource);
50
50
 
51
51
  if (!schemaPublicPath) {
@@ -53,7 +53,7 @@ export const getSchemasFromResource = (resource: CollectionEntry<PageTypes>): Sc
53
53
  }
54
54
 
55
55
  if (resource.collection === 'services') {
56
- const specifications = resource?.data?.specifications;
56
+ const specifications = (resource?.data as any)?.specifications;
57
57
  const asyncapiPath = Array.isArray(specifications)
58
58
  ? specifications.find((spec) => spec.type === 'asyncapi')?.path
59
59
  : specifications?.asyncapiPath;
@@ -161,7 +161,7 @@ export const getConsumersOfMessage = (services: Service[], message: CollectionEn
161
161
  };
162
162
 
163
163
  export const getSpecificationsForService = (service: CollectionEntry<CollectionTypes>) => {
164
- return processSpecifications(service.data.specifications as any);
164
+ return processSpecifications((service.data as any).specifications as any);
165
165
  };
166
166
  // Get services for channel
167
167
  export const getProducersAndConsumersForChannel = async (channel: CollectionEntry<'channels'>) => {