@eventcatalog/core 2.65.0 → 3.0.0-beta.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 (130) hide show
  1. package/README.md +1 -26
  2. package/dist/analytics/analytics.cjs +1 -1
  3. package/dist/analytics/analytics.js +2 -2
  4. package/dist/analytics/log-build.cjs +1 -1
  5. package/dist/analytics/log-build.js +3 -3
  6. package/dist/{chunk-NK6OYMRD.js → chunk-JB4YT5JY.js} +1 -1
  7. package/dist/{chunk-BMDTX5IN.js → chunk-TQ4HZREX.js} +1 -1
  8. package/dist/{chunk-IJRFYF4B.js → chunk-X4W4YC3U.js} +1 -1
  9. package/dist/constants.cjs +1 -1
  10. package/dist/constants.js +1 -1
  11. package/dist/eventcatalog.cjs +1 -21
  12. package/dist/eventcatalog.config.d.cts +10 -0
  13. package/dist/eventcatalog.config.d.ts +10 -0
  14. package/dist/eventcatalog.js +3 -20
  15. package/eventcatalog/src/components/CopyAsMarkdown.tsx +19 -1
  16. package/eventcatalog/src/components/FavoriteButton.tsx +54 -0
  17. package/eventcatalog/src/components/Grids/DomainGrid.tsx +386 -362
  18. package/eventcatalog/src/components/Grids/MessageGrid.tsx +166 -518
  19. package/eventcatalog/src/components/Header.astro +48 -23
  20. package/eventcatalog/src/components/Lists/VersionList.astro +2 -2
  21. package/eventcatalog/src/components/MDX/Design/Design.astro +4 -1
  22. package/eventcatalog/src/components/MDX/Flow/Flow.astro +2 -1
  23. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +3 -3
  24. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +8 -2
  25. package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +37 -0
  26. package/eventcatalog/src/components/Search/Search.astro +48 -28
  27. package/eventcatalog/src/components/Search/SearchModal.tsx +393 -702
  28. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +298 -0
  29. package/eventcatalog/src/components/SideNav/NestedSideBar/builders/container.ts +66 -0
  30. package/eventcatalog/src/components/SideNav/NestedSideBar/builders/domain.ts +101 -0
  31. package/eventcatalog/src/components/SideNav/NestedSideBar/builders/flow.ts +29 -0
  32. package/eventcatalog/src/components/SideNav/NestedSideBar/builders/message.ts +84 -0
  33. package/eventcatalog/src/components/SideNav/NestedSideBar/builders/service.ts +147 -0
  34. package/eventcatalog/src/components/SideNav/NestedSideBar/builders/shared.ts +146 -0
  35. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +1073 -0
  36. package/eventcatalog/src/components/SideNav/NestedSideBar/sidebar-builder.ts +365 -0
  37. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +90 -0
  38. package/eventcatalog/src/components/SideNav/SideNav.astro +18 -28
  39. package/eventcatalog/src/content.config.ts +2 -0
  40. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +10 -4
  41. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +3 -3
  42. package/eventcatalog/src/layouts/DirectoryLayout.astro +2 -2
  43. package/eventcatalog/src/layouts/DiscoverLayout.astro +3 -3
  44. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +85 -63
  45. package/eventcatalog/src/layouts/VisualiserLayout.astro +3 -3
  46. package/eventcatalog/src/pages/_index.astro +530 -110
  47. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +64 -0
  48. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +29 -0
  49. package/eventcatalog/src/pages/directory/[type]/_index.data.ts +4 -4
  50. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -4
  51. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +3 -3
  52. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -5
  53. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +362 -190
  54. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +1 -1
  55. package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +4 -4
  56. package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +1 -4
  57. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +3 -27
  58. package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +2 -2
  59. package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +2 -2
  60. package/eventcatalog/src/pages/index.astro +14 -5
  61. package/eventcatalog/src/pages/nav-index.json.ts +30 -0
  62. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +77 -0
  63. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +90 -0
  64. package/eventcatalog/src/pages/schemas/{index.astro → explorer/index.astro} +3 -3
  65. package/eventcatalog/src/pages/studio.astro +3 -3
  66. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +4 -3
  67. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +2 -2
  68. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +4 -3
  69. package/eventcatalog/src/stores/favorites-store.ts +83 -0
  70. package/eventcatalog/src/stores/sidebar-store.ts +8 -0
  71. package/eventcatalog/src/utils/collections/changelogs.ts +7 -4
  72. package/eventcatalog/src/utils/{channels.ts → collections/channels.ts} +81 -31
  73. package/eventcatalog/src/utils/collections/commands.ts +134 -0
  74. package/eventcatalog/src/utils/collections/containers.ts +44 -33
  75. package/eventcatalog/src/utils/collections/domains.ts +204 -62
  76. package/eventcatalog/src/utils/{entities.ts → collections/entities.ts} +44 -24
  77. package/eventcatalog/src/utils/collections/events.ts +136 -0
  78. package/eventcatalog/src/utils/collections/flows.ts +59 -25
  79. package/eventcatalog/src/utils/{messages.ts → collections/messages.ts} +13 -4
  80. package/eventcatalog/src/utils/{queries.ts → collections/queries.ts} +49 -28
  81. package/eventcatalog/src/utils/collections/services.ts +100 -68
  82. package/eventcatalog/src/utils/collections/teams.ts +94 -0
  83. package/eventcatalog/src/utils/collections/users.ts +122 -0
  84. package/eventcatalog/src/utils/collections/util.ts +57 -1
  85. package/eventcatalog/src/utils/feature.ts +3 -1
  86. package/eventcatalog/src/utils/{collections/file-diffs.ts → file-diffs.ts} +1 -1
  87. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -0
  88. package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +16 -6
  89. package/eventcatalog/src/utils/node-graphs/domains-canvas.ts +14 -10
  90. package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +36 -64
  91. package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +23 -19
  92. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +36 -49
  93. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +22 -18
  94. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +4 -4
  95. package/eventcatalog/tailwind.config.mjs +14 -0
  96. package/eventcatalog/tsconfig.json +2 -1
  97. package/package.json +7 -4
  98. package/eventcatalog/public/logo_old.png +0 -0
  99. package/eventcatalog/src/components/DiscoverInsight.astro +0 -61
  100. package/eventcatalog/src/components/Grids/ServiceGrid.tsx +0 -534
  101. package/eventcatalog/src/components/Lists/CustomSideBarSectionList.astro +0 -55
  102. package/eventcatalog/src/components/Lists/ProtocolList.tsx +0 -74
  103. package/eventcatalog/src/components/Lists/RepositoryList.astro +0 -37
  104. package/eventcatalog/src/components/Lists/SpecificationsList.astro +0 -67
  105. package/eventcatalog/src/components/SideBars/ChannelSideBar.astro +0 -204
  106. package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +0 -180
  107. package/eventcatalog/src/components/SideBars/DomainSideBar.astro +0 -273
  108. package/eventcatalog/src/components/SideBars/EntitySideBar.astro +0 -139
  109. package/eventcatalog/src/components/SideBars/FlowSideBar.astro +0 -128
  110. package/eventcatalog/src/components/SideBars/MessageSideBar.astro +0 -248
  111. package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +0 -294
  112. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/CollapsibleGroup.tsx +0 -46
  113. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +0 -78
  114. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/SpecificationList.tsx +0 -83
  115. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +0 -1250
  116. package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +0 -91
  117. package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +0 -201
  118. package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +0 -190
  119. package/eventcatalog/src/components/SideNav/TreeView/index.tsx +0 -94
  120. package/eventcatalog/src/components/TreeView/index.tsx +0 -328
  121. package/eventcatalog/src/components/TreeView/styles.module.css +0 -264
  122. package/eventcatalog/src/components/TreeView/useSlots.ts +0 -95
  123. package/eventcatalog/src/pages/architecture/[type]/index.astro +0 -14
  124. package/eventcatalog/src/pages/architecture/architecture.astro +0 -101
  125. package/eventcatalog/src/pages/architecture/docs/[type]/index.astro +0 -14
  126. package/eventcatalog/src/utils/commands.ts +0 -112
  127. package/eventcatalog/src/utils/events.ts +0 -108
  128. package/eventcatalog/src/utils/generators/index.ts +0 -10
  129. package/eventcatalog/src/utils/teams.ts +0 -72
  130. package/eventcatalog/src/utils/users.ts +0 -72
@@ -1,79 +1,202 @@
1
- import { getItemsFromCollectionByIdAndSemverOrLatest, getVersionForCollectionItem } from '@utils/collections/util';
2
1
  import { getCollection } from 'astro:content';
3
2
  import type { CollectionEntry } from 'astro:content';
4
3
  import path from 'path';
5
4
  import type { CollectionMessageTypes } from '@types';
6
5
  import type { Service } from './services';
7
6
  import utils from '@eventcatalog/sdk';
7
+ import { createVersionedMap, findInMap } from '@utils/collections/util';
8
8
 
9
9
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
10
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
10
11
 
11
12
  export type Domain = CollectionEntry<'domains'>;
12
13
  export type UbiquitousLanguage = CollectionEntry<'ubiquitousLanguages'>;
14
+
13
15
  interface Props {
14
16
  getAllVersions?: boolean;
17
+ includeServicesInSubdomains?: boolean;
18
+ enrichServices?: boolean;
15
19
  }
16
20
 
17
- // Update cache to store both versions
18
- let cachedDomains: Record<string, Domain[]> = {
19
- allVersions: [],
20
- currentVersions: [],
21
+ // Simple in-memory cache variable
22
+ let memoryCache: Record<string, Domain[]> = {};
23
+
24
+ // Helper to hydrate services
25
+ const hydrateServices = (
26
+ servicesList: any[],
27
+ serviceMap: Map<string, any[]>,
28
+ messageMap: Map<string, any[]>,
29
+ containerMap: Map<string, any[]>
30
+ ) => {
31
+ return servicesList
32
+ .map((service: { id: string; version: string | undefined }) => findInMap(serviceMap, service.id, service.version))
33
+ .filter((s) => !!s)
34
+ .map((service) => {
35
+ // Hydrate service messages and containers
36
+ const sends = (service.data.sends || [])
37
+ .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
38
+ .filter((m: any) => !!m);
39
+
40
+ const receives = (service.data.receives || [])
41
+ .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
42
+ .filter((m: any) => !!m);
43
+
44
+ const readsFrom = (service.data.readsFrom || [])
45
+ .map((c: any) => findInMap(containerMap, c.id, c.version))
46
+ .filter((c: any) => !!c);
47
+
48
+ const writesTo = (service.data.writesTo || [])
49
+ .map((c: any) => findInMap(containerMap, c.id, c.version))
50
+ .filter((c: any) => !!c);
51
+
52
+ return {
53
+ ...service,
54
+ data: {
55
+ ...service.data,
56
+ sends: sends as any,
57
+ receives: receives as any,
58
+ readsFrom: readsFrom as any,
59
+ writesTo: writesTo as any,
60
+ },
61
+ };
62
+ });
21
63
  };
22
64
 
23
- export const getDomains = async ({ getAllVersions = true }: Props = {}): Promise<Domain[]> => {
24
- const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
65
+ // --- MAIN FUNCTION ---
66
+
67
+ export const getDomains = async ({
68
+ getAllVersions = true,
69
+ includeServicesInSubdomains = true,
70
+ enrichServices = false,
71
+ }: Props = {}): Promise<Domain[]> => {
72
+ // console.time('✅ New getDomains');
25
73
 
26
- // Check if we have cached domains for this specific getAllVersions value
27
- if (cachedDomains[cacheKey].length > 0) {
28
- return cachedDomains[cacheKey];
74
+ const cacheKey = `${getAllVersions ? 'allVersions' : 'currentVersions'}-${includeServicesInSubdomains ? 'true' : 'false'}-${enrichServices ? 'enriched' : 'simple'}`;
75
+
76
+ // Check cache
77
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
78
+ // console.timeEnd('✅ New getDomains');
79
+ return memoryCache[cacheKey];
80
+ }
81
+
82
+ // 1. Fetch collections
83
+ const collectionsToFetch: any[] = [
84
+ getCollection('domains'),
85
+ getCollection('services'),
86
+ getCollection('entities'),
87
+ getCollection('flows'),
88
+ ];
89
+
90
+ if (enrichServices) {
91
+ collectionsToFetch.push(
92
+ getCollection('events'),
93
+ getCollection('commands'),
94
+ getCollection('queries'),
95
+ getCollection('containers')
96
+ );
97
+ }
98
+
99
+ const results = await Promise.all(collectionsToFetch);
100
+ const [allDomains, allServices, allEntities, allFlows] = results;
101
+
102
+ let messageMap = new Map();
103
+ let containerMap = new Map();
104
+
105
+ if (enrichServices) {
106
+ const [, , , , allEvents, allCommands, allQueries, allContainers] = results;
107
+ const allMessages = [...allEvents, ...allCommands, ...allQueries];
108
+ messageMap = createVersionedMap(allMessages);
109
+ containerMap = createVersionedMap(allContainers);
29
110
  }
30
111
 
31
- // Get all the domains that are not versioned
32
- const domains = await getCollection('domains', (domain) => {
33
- return (getAllVersions || !domain.filePath?.includes('versioned')) && domain.data.hidden !== true;
112
+ // 2. Build optimized maps
113
+ const domainMap = createVersionedMap(allDomains);
114
+ const serviceMap = createVersionedMap(allServices);
115
+ const entityMap = createVersionedMap(allEntities);
116
+ const flowMap = createVersionedMap(allFlows);
117
+
118
+ // 3. Filter the domains we actually want to process/return
119
+ const targetDomains = allDomains.filter((domain: Domain) => {
120
+ // Filter out hidden
121
+ if (domain.data.hidden === true) return false;
122
+ // Handle version filtering
123
+ if (!getAllVersions && domain.filePath?.includes('versioned')) return false;
124
+ return true;
34
125
  });
35
126
 
36
- // Get all the services that are not versioned
37
- const servicesCollection = await getCollection('services');
38
- const entitiesCollection = await getCollection('entities');
127
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
39
128
 
40
- // @ts-ignore // TODO: Fix this type
41
- cachedDomains[cacheKey] = await Promise.all(
42
- domains.map(async (domain) => {
43
- const { latestVersion, versions } = getVersionForCollectionItem(domain, domains);
129
+ // 4. Process domains using Map lookups (O(1))
130
+ const processedDomains = await Promise.all(
131
+ targetDomains.map(async (domain: Domain) => {
132
+ // Get version info from the map
133
+ const domainVersions = domainMap.get(domain.data.id) || [];
134
+ const latestVersion = domainVersions[0]?.data.version || domain.data.version;
135
+ const versions = domainVersions.map((d) => d.data.version);
44
136
 
45
- // const receives = service.data.receives || [];
46
- const servicesInDomain = domain.data.services || [];
137
+ // Resolve Subdomains
47
138
  const subDomainsInDomain = domain.data.domains || [];
48
- const entitiesInDomain = domain.data.entities || [];
49
139
  const subDomains = subDomainsInDomain
50
- .map((_subDomain: { id: string; version: string | undefined }) =>
51
- getItemsFromCollectionByIdAndSemverOrLatest(domains, _subDomain.id, _subDomain.version)
52
- )
53
- .flat()
54
- // Stop circular references
55
- .filter((subDomain) => subDomain.data.id !== domain.data.id);
56
-
57
- // Services in the sub domains
58
- const subdomainServices = subDomains.flatMap((subDomain) => subDomain.data.services || []);
59
-
60
- const services = [...servicesInDomain, ...subdomainServices]
61
- .map((_service: { id: string; version: string | undefined }) =>
62
- getItemsFromCollectionByIdAndSemverOrLatest(servicesCollection, _service.id, _service.version)
63
- )
64
- .flat();
65
-
66
- const entities = [...entitiesInDomain]
67
- .map((_entity: { id: string; version: string | undefined }) =>
68
- getItemsFromCollectionByIdAndSemverOrLatest(entitiesCollection, _entity.id, _entity.version)
69
- )
70
- .flat();
71
-
72
- const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
140
+ .map((sd: { id: string; version: string | undefined }) => findInMap(domainMap, sd.id, sd.version))
141
+ .filter((sd): sd is Domain => !!sd && sd.data.id !== domain.data.id) // Filter nulls and self-refs
142
+ .map((subDomain: any) => {
143
+ // Hydrate services for the subdomain
144
+ let hydratedServices = subDomain.data.services || [];
145
+ if (enrichServices) {
146
+ hydratedServices = hydrateServices(subDomain.data.services || [], serviceMap, messageMap, containerMap);
147
+ } else {
148
+ // Just resolve the service objects without enrichment
149
+ hydratedServices = (subDomain.data.services || [])
150
+ .map((service: { id: string; version: string | undefined }) => findInMap(serviceMap, service.id, service.version))
151
+ .filter((s: any) => !!s);
152
+ }
153
+
154
+ return {
155
+ ...subDomain,
156
+ data: {
157
+ ...subDomain.data,
158
+ services: hydratedServices as any,
159
+ },
160
+ };
161
+ });
162
+
163
+ // Resolve Entities
164
+ const entitiesInDomain = domain.data.entities || [];
165
+ const entities = entitiesInDomain
166
+ .map((entity: { id: string; version: string | undefined }) => findInMap(entityMap, entity.id, entity.version))
167
+ .filter((e): e is CollectionEntry<'entities'> => !!e);
168
+
169
+ // Resolve Flows
170
+ const flowsInDomain = domain.data.flows || [];
171
+ const flows = flowsInDomain
172
+ .map((flow: { id: string; version: string | undefined }) => findInMap(flowMap, flow.id, flow.version))
173
+ .filter((f): f is CollectionEntry<'flows'> => !!f);
174
+
175
+ // Resolve Services for Main Domain
176
+ const servicesInDomain = domain.data.services || [];
177
+
178
+ // Hydrate main domain services
179
+ let hydratedMainServices = [];
180
+ if (enrichServices) {
181
+ hydratedMainServices = hydrateServices(servicesInDomain, serviceMap, messageMap, containerMap);
182
+ } else {
183
+ hydratedMainServices = servicesInDomain
184
+ .map((service: { id: string; version: string | undefined }) => findInMap(serviceMap, service.id, service.version))
185
+ .filter((s) => !!s);
186
+ }
187
+
188
+ // Get already-hydrated subdomain services
189
+ const hydratedSubdomainServices = subDomains.flatMap((subDomain: any) => subDomain.data.services || []);
190
+
191
+ const services = includeServicesInSubdomains
192
+ ? [...(hydratedMainServices as any), ...(hydratedSubdomainServices as any)]
193
+ : (hydratedMainServices as any);
194
+
195
+ // Calculate folder paths
73
196
  const folderName = await getResourceFolderName(
74
197
  process.env.PROJECT_DIR ?? '',
75
198
  domain.data.id,
76
- domain.data.version.toString()
199
+ domain.data.version?.toString()
77
200
  );
78
201
  const domainFolderName = folderName ?? domain.id.replace(`-${domain.data.version}`, '');
79
202
 
@@ -81,9 +204,10 @@ export const getDomains = async ({ getAllVersions = true }: Props = {}): Promise
81
204
  ...domain,
82
205
  data: {
83
206
  ...domain.data,
84
- services: services,
85
- domains: subDomains,
86
- entities: entities,
207
+ services: services as any, // Cast to avoid deep type issues with enriched data
208
+ domains: subDomains as any,
209
+ entities: entities as any,
210
+ flows: flows as any,
87
211
  latestVersion,
88
212
  versions,
89
213
  },
@@ -99,12 +223,17 @@ export const getDomains = async ({ getAllVersions = true }: Props = {}): Promise
99
223
  })
100
224
  );
101
225
 
102
- // order them by the name of the domain
103
- cachedDomains[cacheKey].sort((a, b) => {
226
+ // Sort by name
227
+ processedDomains.sort((a, b) => {
104
228
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
105
229
  });
106
230
 
107
- return cachedDomains[cacheKey];
231
+ // Cache result
232
+ memoryCache[cacheKey] = processedDomains;
233
+
234
+ // console.timeEnd('✅ New getDomains');
235
+
236
+ return processedDomains;
108
237
  };
109
238
 
110
239
  export const getMessagesForDomain = async (
@@ -113,23 +242,29 @@ export const getMessagesForDomain = async (
113
242
  // We already have the services from the domain
114
243
  const services = domain.data.services as unknown as CollectionEntry<'services'>[];
115
244
 
116
- const events = await getCollection('events');
117
- const commands = await getCollection('commands');
118
- const queries = await getCollection('queries');
245
+ const [events, commands, queries] = await Promise.all([
246
+ getCollection('events'),
247
+ getCollection('commands'),
248
+ getCollection('queries'),
249
+ ]);
119
250
 
120
251
  const allMessages = [...events, ...commands, ...queries];
252
+ const messageMap = createVersionedMap(allMessages);
121
253
 
122
254
  const sends = services.flatMap((service) => service.data.sends || []);
123
255
  const receives = services.flatMap((service) => service.data.receives || []);
124
256
 
125
- const sendsMessages = sends.map((send) => getItemsFromCollectionByIdAndSemverOrLatest(allMessages, send.id, send.version));
126
- const receivesMessages = receives.map((receive) =>
127
- getItemsFromCollectionByIdAndSemverOrLatest(allMessages, receive.id, receive.version)
128
- );
257
+ const sendsMessages = sends
258
+ .map((send) => findInMap(messageMap, send.id, send.version))
259
+ .filter((msg): msg is CollectionEntry<CollectionMessageTypes> => !!msg);
260
+
261
+ const receivesMessages = receives
262
+ .map((receive) => findInMap(messageMap, receive.id, receive.version))
263
+ .filter((msg): msg is CollectionEntry<CollectionMessageTypes> => !!msg);
129
264
 
130
265
  return {
131
- sends: sendsMessages.flat(),
132
- receives: receivesMessages.flat(),
266
+ sends: sendsMessages,
267
+ receives: receivesMessages,
133
268
  };
134
269
  };
135
270
 
@@ -212,6 +347,13 @@ export const getParentDomains = async (domain: Domain): Promise<Domain[]> => {
212
347
  });
213
348
  };
214
349
 
350
+ // Only return domains that are not found any any subdomain configuration
351
+ export const getRootDomains = async (): Promise<Domain[]> => {
352
+ const domains = await getDomains({ getAllVersions: false });
353
+ const allSubDomains = domains.flatMap((d) => d.data.domains as unknown as Domain[]);
354
+ return domains.filter((d) => !allSubDomains.some((sd) => sd.data.id === d.data.id));
355
+ };
356
+
215
357
  export const getDomainsForService = async (service: Service): Promise<Domain[]> => {
216
358
  const domains = await getDomains({ getAllVersions: false });
217
359
  return domains.filter((d) => {
@@ -1,8 +1,8 @@
1
1
  import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import path from 'path';
4
- import { getVersionForCollectionItem, satisfies } from './collections/util';
5
4
  import utils from '@eventcatalog/sdk';
5
+ import { createVersionedMap, satisfies } from './util';
6
6
 
7
7
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
8
 
@@ -11,6 +11,7 @@ export type Entity = CollectionEntry<'entities'> & {
11
11
  path: string;
12
12
  filePath: string;
13
13
  type: string;
14
+ publicPath: string;
14
15
  };
15
16
  };
16
17
 
@@ -19,46 +20,62 @@ interface Props {
19
20
  }
20
21
 
21
22
  // cache for build time
22
- let cachedEntities: Record<string, Entity[]> = {
23
- allVersions: [],
24
- currentVersions: [],
25
- };
23
+ let memoryCache: Record<string, Entity[]> = {};
26
24
 
27
25
  export const getEntities = async ({ getAllVersions = true }: Props = {}): Promise<Entity[]> => {
26
+ // console.time('✅ New getEntities');
28
27
  const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
29
28
 
30
- if (cachedEntities[cacheKey].length > 0) {
31
- return cachedEntities[cacheKey];
29
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0) {
30
+ // console.timeEnd('✅ New getEntities');
31
+ return memoryCache[cacheKey];
32
32
  }
33
33
 
34
- const entities = await getCollection('entities', (entity) => {
35
- return (getAllVersions || !entity.filePath?.includes('versioned')) && entity.data.hidden !== true;
34
+ // 1. Fetch collections in parallel
35
+ const [allEntities, allServices, allDomains] = await Promise.all([
36
+ getCollection('entities'),
37
+ getCollection('services'),
38
+ getCollection('domains'),
39
+ ]);
40
+
41
+ // 2. Build optimized maps
42
+ const entityMap = createVersionedMap(allEntities);
43
+
44
+ // 3. Filter entities
45
+ const targetEntities = allEntities.filter((entity) => {
46
+ if (entity.data.hidden === true) return false;
47
+ if (!getAllVersions && entity.filePath?.includes('versioned')) return false;
48
+ return true;
36
49
  });
37
50
 
38
- const services = await getCollection('services');
39
- const domains = await getCollection('domains');
51
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
40
52
 
41
- cachedEntities[cacheKey] = await Promise.all(
42
- entities.map(async (entity) => {
43
- const { latestVersion, versions } = getVersionForCollectionItem(entity, entities);
53
+ // 4. Enrich entities
54
+ const processedEntities = await Promise.all(
55
+ targetEntities.map(async (entity) => {
56
+ // Version info
57
+ const entityVersions = entityMap.get(entity.data.id) || [];
58
+ const latestVersion = entityVersions[0]?.data.version || entity.data.version;
59
+ const versions = entityVersions.map((e) => e.data.version);
44
60
 
45
- const servicesThatReferenceEntity = services.filter((service) =>
61
+ // Find Services that reference this entity
62
+ const servicesThatReferenceEntity = allServices.filter((service) =>
46
63
  service.data.entities?.some((item) => {
47
- if (item.id != entity.data.id) return false;
48
- if (item.version == 'latest' || item.version == undefined) return entity.data.version == latestVersion;
64
+ if (item.id !== entity.data.id) return false;
65
+ if (item.version === 'latest' || item.version === undefined) return entity.data.version === latestVersion;
49
66
  return satisfies(entity.data.version, item.version);
50
67
  })
51
68
  );
52
69
 
53
- const domainsThatReferenceEntity = domains.filter((domain) =>
70
+ // Find Domains that reference this entity
71
+ const domainsThatReferenceEntity = allDomains.filter((domain) =>
54
72
  domain.data.entities?.some((item) => {
55
- if (item.id != entity.data.id) return false;
56
- if (item.version == 'latest' || item.version == undefined) return entity.data.version == latestVersion;
73
+ if (item.id !== entity.data.id) return false;
74
+ if (item.version === 'latest' || item.version === undefined) return entity.data.version === latestVersion;
57
75
  return satisfies(entity.data.version, item.version);
58
76
  })
59
77
  );
60
78
 
61
- const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
62
79
  const folderName = await getResourceFolderName(
63
80
  process.env.PROJECT_DIR ?? '',
64
81
  entity.data.id,
@@ -87,10 +104,13 @@ export const getEntities = async ({ getAllVersions = true }: Props = {}): Promis
87
104
  })
88
105
  );
89
106
 
90
- // order them by the name of the event
91
- cachedEntities[cacheKey].sort((a, b) => {
107
+ // order them by the name of the entity
108
+ processedEntities.sort((a, b) => {
92
109
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
93
110
  });
94
111
 
95
- return cachedEntities[cacheKey];
112
+ memoryCache[cacheKey] = processedEntities;
113
+ // console.timeEnd('✅ New getEntities');
114
+
115
+ return processedEntities;
96
116
  };
@@ -0,0 +1,136 @@
1
+ import { getCollection } from 'astro:content';
2
+ import type { CollectionEntry } from 'astro:content';
3
+ import path from 'path';
4
+ import { createVersionedMap, findInMap, satisfies } from './util';
5
+ import utils from '@eventcatalog/sdk';
6
+
7
+ const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
9
+
10
+ type Event = CollectionEntry<'events'> & {
11
+ catalog: {
12
+ path: string;
13
+ filePath: string;
14
+ type: string;
15
+ publicPath: string;
16
+ };
17
+ };
18
+
19
+ interface Props {
20
+ getAllVersions?: boolean;
21
+ hydrateServices?: boolean;
22
+ }
23
+
24
+ // Simple in-memory cache
25
+ let memoryCache: Record<string, Event[]> = {};
26
+
27
+ export const getEvents = async ({ getAllVersions = true, hydrateServices = true }: Props = {}): Promise<Event[]> => {
28
+ // console.time('✅ New getEvents');
29
+ const cacheKey = `${getAllVersions ? 'allVersions' : 'currentVersions'}-${hydrateServices ? 'hydrated' : 'minimal'}`;
30
+
31
+ // Check cache
32
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
33
+ // console.timeEnd('✅ New getEvents');
34
+ return memoryCache[cacheKey];
35
+ }
36
+
37
+ // 1. Fetch collections in parallel
38
+ const [allEvents, allServices, allChannels] = await Promise.all([
39
+ getCollection('events'),
40
+ getCollection('services'),
41
+ getCollection('channels'),
42
+ ]);
43
+
44
+ // 2. Build optimized maps
45
+ const eventMap = createVersionedMap(allEvents);
46
+ // We don't map services/channels by ID because we need to iterate them to find relationships (reverse lookup)
47
+ // or use them for hydration.
48
+ // Actually, for hydration we CAN use a map if we know the IDs, but here we scan services to find producers/consumers.
49
+
50
+ // 3. Filter events
51
+ const targetEvents = allEvents.filter((event) => {
52
+ if (event.data.hidden === true) return false;
53
+ if (!getAllVersions && event.filePath?.includes('versioned')) return false;
54
+ return true;
55
+ });
56
+
57
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
58
+
59
+ // 4. Enrich events
60
+ const processedEvents = await Promise.all(
61
+ targetEvents.map(async (event) => {
62
+ // Version info
63
+ const eventVersions = eventMap.get(event.data.id) || [];
64
+ const latestVersion = eventVersions[0]?.data.version || event.data.version;
65
+ const versions = eventVersions.map((e) => e.data.version);
66
+
67
+ // Find Producers (Services that send this event)
68
+ const producers = allServices
69
+ .filter((service) =>
70
+ service.data.sends?.some((item) => {
71
+ if (item.id !== event.data.id) return false;
72
+ if (item.version === 'latest' || item.version === undefined) return event.data.version === latestVersion;
73
+ return satisfies(event.data.version, item.version);
74
+ })
75
+ )
76
+ .map((service) => {
77
+ if (!hydrateServices) return { id: service.data.id, version: service.data.version };
78
+ return service;
79
+ });
80
+
81
+ // Find Consumers (Services that receive this event)
82
+ const consumers = allServices
83
+ .filter((service) =>
84
+ service.data.receives?.some((item) => {
85
+ if (item.id !== event.data.id) return false;
86
+ if (item.version === 'latest' || item.version === undefined) return event.data.version === latestVersion;
87
+ return satisfies(event.data.version, item.version);
88
+ })
89
+ )
90
+ .map((service) => {
91
+ if (!hydrateServices) return { id: service.data.id, version: service.data.version };
92
+ return service;
93
+ });
94
+
95
+ // Find Channels
96
+ const messageChannels = event.data.channels || [];
97
+ // This is O(N*M) where N is event channels and M is all channels.
98
+ // Typically M is small, but we could optimize if needed.
99
+ // Given the logic is simply ID match, we can use a Set or Map if needed, but array filter is likely fine for now unless M is huge.
100
+ const channelsForEvent = allChannels.filter((c) => messageChannels.some((channel) => c.data.id === channel.id));
101
+
102
+ const folderName = await getResourceFolderName(process.env.PROJECT_DIR ?? '', event.data.id, event.data.version.toString());
103
+ const eventFolderName = folderName ?? event.id.replace(`-${event.data.version}`, '');
104
+
105
+ return {
106
+ ...event,
107
+ data: {
108
+ ...event.data,
109
+ messageChannels: channelsForEvent,
110
+ producers: producers as any, // Cast for hydration flexibility
111
+ consumers: consumers as any,
112
+ versions,
113
+ latestVersion,
114
+ },
115
+ catalog: {
116
+ path: path.join(event.collection, event.id.replace('/index.mdx', '')),
117
+ absoluteFilePath: path.join(PROJECT_DIR, event.collection, event.id.replace('/index.mdx', '/index.md')),
118
+ astroContentFilePath: path.join(process.cwd(), 'src', 'content', event.collection, event.id),
119
+ filePath: path.join(process.cwd(), 'src', 'catalog-files', event.collection, event.id.replace('/index.mdx', '')),
120
+ publicPath: path.join('/generated', event.collection, eventFolderName),
121
+ type: 'event',
122
+ },
123
+ };
124
+ })
125
+ );
126
+
127
+ // order them by the name of the event
128
+ processedEvents.sort((a, b) => {
129
+ return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
130
+ });
131
+
132
+ memoryCache[cacheKey] = processedEvents;
133
+ // console.timeEnd('✅ New getEvents');
134
+
135
+ return processedEvents;
136
+ };