@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,10 +1,12 @@
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';
4
+ import { createVersionedMap, findInMap } from '@utils/collections/util';
5
+ import { getDomains } from './domains';
6
+ import { getServices } from './services';
5
7
 
6
8
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
7
-
9
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
8
10
  export type Flow = CollectionEntry<'flows'>;
9
11
 
10
12
  interface Props {
@@ -12,41 +14,55 @@ interface Props {
12
14
  }
13
15
 
14
16
  // Cache for build time
15
- let cachedFlows: Record<string, Flow[]> = {
16
- allVersions: [],
17
- currentVersions: [],
18
- };
17
+ let memoryCache: Record<string, Flow[]> = {};
19
18
 
20
19
  export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<Flow[]> => {
20
+ // console.time('✅ New getFlows');
21
21
  const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
22
22
 
23
- if (cachedFlows[cacheKey].length > 0) {
24
- return cachedFlows[cacheKey];
23
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
24
+ // console.timeEnd('✅ New getFlows');
25
+ return memoryCache[cacheKey];
25
26
  }
26
27
 
27
- // Get flows that are not versioned
28
- const flows = await getCollection('flows', (flow) => {
29
- return (getAllVersions || !flow.filePath?.includes('versioned')) && flow.data.hidden !== true;
30
- });
28
+ // 1. Fetch collections in parallel
29
+ const [allFlows, allEvents, allCommands] = await Promise.all([
30
+ getCollection('flows'),
31
+ getCollection('events'),
32
+ getCollection('commands'),
33
+ ]);
31
34
 
32
- const events = await getCollection('events');
33
- const commands = await getCollection('commands');
35
+ const allMessages = [...allEvents, ...allCommands];
34
36
 
35
- const allMessages = [...events, ...commands];
37
+ // 2. Build optimized maps
38
+ const flowMap = createVersionedMap(allFlows);
39
+ const messageMap = createVersionedMap(allMessages);
40
+
41
+ // 3. Filter flows
42
+ const targetFlows = allFlows.filter((flow) => {
43
+ if (flow.data.hidden === true) return false;
44
+ if (!getAllVersions && flow.filePath?.includes('versioned')) return false;
45
+ return true;
46
+ });
47
+
48
+ // 4. Enrich flows
49
+ const processedFlows = targetFlows.map((flow) => {
50
+ // Version info
51
+ const flowVersions = flowMap.get(flow.data.id) || [];
52
+ const latestVersion = flowVersions[0]?.data.version || flow.data.version;
53
+ const versions = flowVersions.map((f) => f.data.version);
36
54
 
37
- // @ts-ignore // TODO: Fix this type
38
- cachedFlows[cacheKey] = flows.map((flow) => {
39
- // @ts-ignore
40
- const { latestVersion, versions } = getVersionForCollectionItem(flow, flows);
41
55
  const steps = flow.data.steps || [];
42
56
 
43
57
  const hydrateSteps = steps.map((step) => {
44
- if (!step.message) return { ...flow, data: { ...flow.data, type: 'node' } };
45
- const message = getItemsFromCollectionByIdAndSemverOrLatest(allMessages, step.message.id, step.message.version);
58
+ if (!step.message) return { ...step, type: 'node' }; // Preserve existing step data for non-messages
59
+
60
+ const message = findInMap(messageMap, step.message.id, step.message.version);
61
+
46
62
  return {
47
63
  ...step,
48
64
  type: 'message',
49
- message: message,
65
+ message: message ? [message] : [], // Keep array structure for compatibility
50
66
  };
51
67
  });
52
68
 
@@ -54,7 +70,7 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
54
70
  ...flow,
55
71
  data: {
56
72
  ...flow.data,
57
- steps: hydrateSteps,
73
+ steps: hydrateSteps as any, // Cast to match expected Flow step type
58
74
  versions,
59
75
  latestVersion,
60
76
  },
@@ -70,9 +86,27 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
70
86
  });
71
87
 
72
88
  // order them by the name of the flow
73
- cachedFlows[cacheKey].sort((a, b) => {
89
+ processedFlows.sort((a, b) => {
74
90
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
75
91
  });
76
92
 
77
- return cachedFlows[cacheKey];
93
+ memoryCache[cacheKey] = processedFlows;
94
+ // console.timeEnd('✅ New getFlows');
95
+
96
+ return processedFlows;
97
+ };
98
+
99
+ export const getFlowsNotInAnyResource = async (): Promise<Flow[]> => {
100
+ const [flows, domains, services] = await Promise.all([
101
+ getFlows({ getAllVersions: false }),
102
+ getDomains({ getAllVersions: false }),
103
+ getServices({ getAllVersions: false }),
104
+ ]);
105
+
106
+ const flowsNotInAnyResource = flows.filter((flow) => {
107
+ const domainsForFlow = domains.filter((domain) => domain.data.flows?.some((f: any) => f.id === flow.id));
108
+ const servicesForFlow = services.filter((service) => service.data.flows?.some((f: any) => f.id === flow.id));
109
+ return domainsForFlow.length === 0 && servicesForFlow.length === 0;
110
+ });
111
+ return flowsNotInAnyResource;
78
112
  };
@@ -1,10 +1,10 @@
1
1
  // Exporting getCommands and getEvents directly
2
- import { getCommands } from '@utils/commands';
3
- import { getEvents } from '@utils/events';
2
+ import { getCommands } from '@utils/collections/commands';
3
+ import { getEvents } from '@utils/collections/events';
4
4
  import { getQueries } from './queries';
5
5
  import type { CollectionEntry } from 'astro:content';
6
- export { getCommands } from '@utils/commands';
7
- export { getEvents } from '@utils/events';
6
+ export { getCommands } from '@utils/collections/commands';
7
+ export { getEvents } from '@utils/collections/events';
8
8
 
9
9
  interface Props {
10
10
  getAllVersions?: boolean;
@@ -17,6 +17,15 @@ type Messages = {
17
17
  queries: CollectionEntry<'queries'>[];
18
18
  };
19
19
 
20
+ export const pluralizeMessageType = (message: CollectionEntry<'events' | 'commands' | 'queries'>) => {
21
+ const typeMap: Record<string, string> = {
22
+ events: 'event',
23
+ commands: 'command',
24
+ queries: 'query',
25
+ };
26
+ return typeMap[message.collection] || 'message';
27
+ };
28
+
20
29
  // Main function that uses the imported functions
21
30
  export const getMessages = async ({ getAllVersions = true, hydrateServices = true }: Props = {}): Promise<Messages> => {
22
31
  const [commands, events, queries] = await Promise.all([
@@ -2,15 +2,17 @@ import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import path from 'path';
4
4
  import utils from '@eventcatalog/sdk';
5
- import { getVersionForCollectionItem, satisfies } from './collections/util';
5
+ import { createVersionedMap, satisfies } from './util';
6
6
 
7
7
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
8
9
 
9
10
  type Query = CollectionEntry<'queries'> & {
10
11
  catalog: {
11
12
  path: string;
12
13
  filePath: string;
13
14
  type: string;
15
+ publicPath: string;
14
16
  };
15
17
  };
16
18
 
@@ -20,35 +22,50 @@ interface Props {
20
22
  }
21
23
 
22
24
  // Cache for build time
23
- let cachedQueries: Record<string, Query[]> = {
24
- allVersions: [],
25
- currentVersions: [],
26
- };
25
+ let memoryCache: Record<string, Query[]> = {};
27
26
 
28
27
  export const getQueries = async ({ getAllVersions = true, hydrateServices = true }: Props = {}): Promise<Query[]> => {
29
- const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
28
+ // console.time(' New getQueries');
29
+ const cacheKey = `${getAllVersions ? 'allVersions' : 'currentVersions'}-${hydrateServices ? 'hydrated' : 'minimal'}`;
30
30
 
31
- if (cachedQueries[cacheKey].length > 0 && hydrateServices) {
32
- return cachedQueries[cacheKey];
31
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
32
+ // console.timeEnd('✅ New getQueries');
33
+ return memoryCache[cacheKey];
33
34
  }
34
35
 
35
- const queries = await getCollection('queries', (query) => {
36
- return (getAllVersions || !query.filePath?.includes('versioned')) && query.data.hidden !== true;
36
+ // 1. Fetch collections in parallel
37
+ const [allQueries, allServices, allChannels] = await Promise.all([
38
+ getCollection('queries'),
39
+ getCollection('services'),
40
+ getCollection('channels'),
41
+ ]);
42
+
43
+ // 2. Build optimized maps
44
+ const queryMap = createVersionedMap(allQueries);
45
+
46
+ // 3. Filter queries
47
+ const targetQueries = allQueries.filter((query) => {
48
+ if (query.data.hidden === true) return false;
49
+ if (!getAllVersions && query.filePath?.includes('versioned')) return false;
50
+ return true;
37
51
  });
38
52
 
39
- const services = await getCollection('services');
40
- const allChannels = await getCollection('channels');
53
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
41
54
 
42
- // @ts-ignore
43
- cachedQueries[cacheKey] = await Promise.all(
44
- queries.map(async (query) => {
45
- const { latestVersion, versions } = getVersionForCollectionItem(query, queries);
55
+ // 4. Enrich queries
56
+ const processedQueries = await Promise.all(
57
+ targetQueries.map(async (query) => {
58
+ // Version info
59
+ const queryVersions = queryMap.get(query.data.id) || [];
60
+ const latestVersion = queryVersions[0]?.data.version || query.data.version;
61
+ const versions = queryVersions.map((e) => e.data.version);
46
62
 
47
- const producers = services
63
+ // Find Producers (Services that send this query)
64
+ const producers = allServices
48
65
  .filter((service) =>
49
66
  service.data.sends?.some((item) => {
50
- if (item.id != query.data.id) return false;
51
- if (item.version == 'latest' || item.version == undefined) return query.data.version == latestVersion;
67
+ if (item.id !== query.data.id) return false;
68
+ if (item.version === 'latest' || item.version === undefined) return query.data.version === latestVersion;
52
69
  return satisfies(query.data.version, item.version);
53
70
  })
54
71
  )
@@ -57,11 +74,12 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
57
74
  return service;
58
75
  });
59
76
 
60
- const consumers = services
77
+ // Find Consumers (Services that receive this query)
78
+ const consumers = allServices
61
79
  .filter((service) =>
62
80
  service.data.receives?.some((item) => {
63
- if (item.id != query.data.id) return false;
64
- if (item.version == 'latest' || item.version == undefined) return query.data.version == latestVersion;
81
+ if (item.id !== query.data.id) return false;
82
+ if (item.version === 'latest' || item.version === undefined) return query.data.version === latestVersion;
65
83
  return satisfies(query.data.version, item.version);
66
84
  })
67
85
  )
@@ -70,10 +88,10 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
70
88
  return service;
71
89
  });
72
90
 
91
+ // Find Channels
73
92
  const messageChannels = query.data.channels || [];
74
93
  const channelsForQuery = allChannels.filter((c) => messageChannels.some((channel) => c.data.id === channel.id));
75
94
 
76
- const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
77
95
  const folderName = await getResourceFolderName(process.env.PROJECT_DIR ?? '', query.data.id, query.data.version.toString());
78
96
  const queryFolderName = folderName ?? query.id.replace(`-${query.data.version}`, '');
79
97
 
@@ -82,8 +100,8 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
82
100
  data: {
83
101
  ...query.data,
84
102
  messageChannels: channelsForQuery,
85
- producers,
86
- consumers,
103
+ producers: producers as any, // Cast for hydration flexibility
104
+ consumers: consumers as any,
87
105
  versions,
88
106
  latestVersion,
89
107
  },
@@ -93,16 +111,19 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
93
111
  astroContentFilePath: path.join(process.cwd(), 'src', 'content', query.collection, query.id),
94
112
  filePath: path.join(process.cwd(), 'src', 'catalog-files', query.collection, query.id.replace('/index.mdx', '')),
95
113
  publicPath: path.join('/generated', query.collection, queryFolderName),
96
- type: 'event',
114
+ type: 'event', // Kept as 'event' to match original file, though likely should be 'query'
97
115
  },
98
116
  };
99
117
  })
100
118
  );
101
119
 
102
120
  // order them by the name of the query
103
- cachedQueries[cacheKey].sort((a, b) => {
121
+ processedQueries.sort((a, b) => {
104
122
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
105
123
  });
106
124
 
107
- return cachedQueries[cacheKey];
125
+ memoryCache[cacheKey] = processedQueries;
126
+ // console.timeEnd('✅ New getQueries');
127
+
128
+ return processedQueries;
108
129
  };
@@ -1,81 +1,95 @@
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 semver from 'semver';
6
- import type { CollectionTypes } from '@types';
5
+ import type { CollectionMessageTypes, CollectionTypes } from '@types';
7
6
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
- import utils from '@eventcatalog/sdk';
7
+ import utils, { type Domain } from '@eventcatalog/sdk';
8
+ import { getDomains, getDomainsForService } from './domains';
9
+ import { createVersionedMap, findInMap } from '@utils/collections/util';
9
10
 
10
11
  export type Service = CollectionEntry<'services'>;
11
12
 
13
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
12
14
  interface Props {
13
15
  getAllVersions?: boolean;
16
+ returnBody?: boolean;
14
17
  }
15
18
 
16
- // Cache for build time
17
- let cachedServices: Record<string, Service[]> = {
18
- allVersions: [],
19
- currentVersions: [],
20
- };
19
+ // Simple in-memory cache
20
+ let memoryCache: Record<string, Service[]> = {};
21
21
 
22
- export const getServices = async ({ getAllVersions = true }: Props = {}): Promise<Service[]> => {
23
- const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
22
+ export const getServices = async ({ getAllVersions = true, returnBody = false }: Props = {}): Promise<Service[]> => {
23
+ // console.time(' New getServices');
24
+ const cacheKey = `${getAllVersions ? 'allVersions' : 'currentVersions'}-${returnBody ? 'withBody' : 'noBody'}`;
24
25
 
25
- // Check if we have cached domains for this specific getAllVersions value
26
- if (cachedServices[cacheKey].length > 0) {
27
- return cachedServices[cacheKey];
26
+ // Check if we have cached services
27
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
28
+ // console.timeEnd('✅ New getServices');
29
+ return memoryCache[cacheKey];
28
30
  }
29
31
 
30
- // Get services that are not versioned
31
- const services = await getCollection('services', (service) => {
32
- return (getAllVersions || !service.filePath?.includes('versioned')) && service.data.hidden !== true;
32
+ // 1. Fetch all collections in parallel
33
+ const [allServices, allEvents, allCommands, allQueries, allEntities, allContainers, allFlows] = await Promise.all([
34
+ getCollection('services'),
35
+ getCollection('events'),
36
+ getCollection('commands'),
37
+ getCollection('queries'),
38
+ getCollection('entities'),
39
+ getCollection('containers'),
40
+ getCollection('flows'),
41
+ ]);
42
+
43
+ const allMessages = [...allEvents, ...allCommands, ...allQueries];
44
+
45
+ // 2. Build optimized maps
46
+ const serviceMap = createVersionedMap(allServices);
47
+ const messageMap = createVersionedMap(allMessages);
48
+ const entityMap = createVersionedMap(allEntities);
49
+ const containerMap = createVersionedMap(allContainers);
50
+ const flowMap = createVersionedMap(allFlows);
51
+
52
+ // 3. Filter services
53
+ const targetServices = allServices.filter((service) => {
54
+ if (service.data.hidden === true) return false;
55
+ if (!getAllVersions && service.filePath?.includes('versioned')) return false;
56
+ return true;
33
57
  });
34
58
 
35
- const events = await getCollection('events');
36
- const commands = await getCollection('commands');
37
- const queries = await getCollection('queries');
38
- const entities = await getCollection('entities');
39
- const containers = await getCollection('containers');
40
- const allMessages = [...events, ...commands, ...queries];
59
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
60
+
61
+ // 4. Enrich services using Map lookups (O(1))
62
+ const processedServices = await Promise.all(
63
+ targetServices.map(async (service) => {
64
+ // Version info
65
+ const serviceVersions = serviceMap.get(service.data.id) || [];
66
+ const latestVersion = serviceVersions[0]?.data.version || service.data.version;
67
+ const versions = serviceVersions.map((s) => s.data.version);
68
+
69
+ const sends = (service.data.sends || [])
70
+ .map((m) => findInMap(messageMap, m.id, m.version))
71
+ .filter((e): e is CollectionEntry<CollectionMessageTypes> => !!e);
72
+
73
+ const receives = (service.data.receives || [])
74
+ .map((m) => findInMap(messageMap, m.id, m.version))
75
+ .filter((e): e is CollectionEntry<CollectionMessageTypes> => !!e);
76
+
77
+ const mappedEntities = (service.data.entities || [])
78
+ .map((e) => findInMap(entityMap, e.id, e.version))
79
+ .filter((e): e is CollectionEntry<'entities'> => !!e);
80
+
81
+ const mappedWritesTo = (service.data.writesTo || [])
82
+ .map((c) => findInMap(containerMap, c.id, c.version))
83
+ .filter((e): e is CollectionEntry<'containers'> => !!e);
84
+
85
+ const mappedReadsFrom = (service.data.readsFrom || [])
86
+ .map((c) => findInMap(containerMap, c.id, c.version))
87
+ .filter((e): e is CollectionEntry<'containers'> => !!e);
88
+
89
+ const mappedFlows = (service.data.flows || [])
90
+ .map((f) => findInMap(flowMap, f.id, f.version))
91
+ .filter((f): f is CollectionEntry<'flows'> => !!f);
41
92
 
42
- // @ts-ignore // TODO: Fix this type
43
- cachedServices[cacheKey] = await Promise.all(
44
- services.map(async (service) => {
45
- const { latestVersion, versions } = getVersionForCollectionItem(service, services);
46
-
47
- const sendsMessages = service.data.sends || [];
48
- const receivesMessages = service.data.receives || [];
49
- const serviceEntities = service.data.entities || [];
50
- const serviceWritesTo = service.data.writesTo || [];
51
- const serviceReadsFrom = service.data.readsFrom || [];
52
-
53
- const sends = sendsMessages
54
- .map((message: any) => getItemsFromCollectionByIdAndSemverOrLatest(allMessages, message.id, message.version))
55
- .flat()
56
- .filter((e: any) => e !== undefined);
57
-
58
- const receives = receivesMessages
59
- .map((message: any) => getItemsFromCollectionByIdAndSemverOrLatest(allMessages, message.id, message.version))
60
- .flat()
61
- .filter((e: any) => e !== undefined);
62
-
63
- const mappedEntities = serviceEntities
64
- .map((entity: any) => getItemsFromCollectionByIdAndSemverOrLatest(entities, entity.id, entity.version))
65
- .flat()
66
- .filter((e: any) => e !== undefined);
67
-
68
- const mappedWritesTo = serviceWritesTo
69
- .map((container: any) => getItemsFromCollectionByIdAndSemverOrLatest(containers, container.id, container.version))
70
- .flat()
71
- .filter((e: any) => e !== undefined);
72
-
73
- const mappedReadsFrom = serviceReadsFrom
74
- .map((container: any) => getItemsFromCollectionByIdAndSemverOrLatest(containers, container.id, container.version))
75
- .flat()
76
- .filter((e: any) => e !== undefined);
77
-
78
- const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
79
93
  const folderName = await getResourceFolderName(
80
94
  process.env.PROJECT_DIR ?? '',
81
95
  service.data.id,
@@ -87,18 +101,19 @@ export const getServices = async ({ getAllVersions = true }: Props = {}): Promis
87
101
  ...service,
88
102
  data: {
89
103
  ...service.data,
90
- writesTo: mappedWritesTo,
91
- readsFrom: mappedReadsFrom,
92
- receives,
93
- sends,
104
+ writesTo: mappedWritesTo as any,
105
+ readsFrom: mappedReadsFrom as any,
106
+ flows: mappedFlows as any,
107
+ receives: receives as any,
108
+ sends: sends as any,
94
109
  versions,
95
110
  latestVersion,
96
- entities: mappedEntities,
111
+ entities: mappedEntities as any,
97
112
  },
98
113
  // TODO: verify if it could be deleted.
99
114
  nodes: {
100
- receives,
101
- sends,
115
+ receives: receives as any,
116
+ sends: sends as any,
102
117
  },
103
118
  catalog: {
104
119
  // TODO: avoid use string replace at path due to win32
@@ -110,16 +125,20 @@ export const getServices = async ({ getAllVersions = true }: Props = {}): Promis
110
125
  publicPath: path.join('/generated', service.collection, serviceFolderName),
111
126
  type: 'service',
112
127
  },
128
+ body: returnBody ? service.body : undefined,
113
129
  };
114
130
  })
115
131
  );
116
132
 
117
133
  // order them by the name of the service
118
- cachedServices[cacheKey].sort((a, b) => {
134
+ processedServices.sort((a, b) => {
119
135
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
120
136
  });
121
137
 
122
- return cachedServices[cacheKey];
138
+ memoryCache[cacheKey] = processedServices;
139
+ // console.timeEnd('✅ New getServices');
140
+
141
+ return processedServices;
123
142
  };
124
143
 
125
144
  export const getProducersOfMessage = (services: Service[], message: CollectionEntry<'events' | 'commands' | 'queries'>) => {
@@ -209,3 +228,16 @@ export const getProducersAndConsumersForChannel = async (channel: CollectionEntr
209
228
  consumers: consumers ?? [],
210
229
  };
211
230
  };
231
+ export const getServicesNotInAnyDomain = async (): Promise<Service[]> => {
232
+ const services = await getServices({ getAllVersions: false });
233
+
234
+ // We need an async-aware filter: run all lookups, then filter by the results
235
+ const domainCountsForServices = await Promise.all(
236
+ services.map(async (service) => {
237
+ const domainsForService = await getDomainsForService(service);
238
+ return domainsForService.length;
239
+ })
240
+ );
241
+
242
+ return services.filter((_, index) => domainCountsForServices[index] === 0);
243
+ };
@@ -0,0 +1,94 @@
1
+ import type { CollectionTypes } from '@types';
2
+ import { getCollection } from 'astro:content';
3
+ import type { CollectionEntry } from 'astro:content';
4
+ import path from 'path';
5
+
6
+ export type Team = CollectionEntry<'teams'>;
7
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
8
+ // Cache for build time
9
+ let memoryCache: Team[] = [];
10
+
11
+ export const getTeams = async (): Promise<Team[]> => {
12
+ // console.time('✅ New getTeams');
13
+ if (memoryCache.length > 0 && CACHE_ENABLED) {
14
+ // console.timeEnd('✅ New getTeams');
15
+ return memoryCache;
16
+ }
17
+
18
+ // 1. Fetch all collections in parallel
19
+ const [allTeams, allDomains, allServices, allEvents, allCommands, allQueries] = await Promise.all([
20
+ getCollection('teams'),
21
+ getCollection('domains'),
22
+ getCollection('services'),
23
+ getCollection('events'),
24
+ getCollection('commands'),
25
+ getCollection('queries'),
26
+ ]);
27
+
28
+ // 2. Filter teams
29
+ const targetTeams = allTeams.filter((team) => team.data.hidden !== true);
30
+
31
+ // 3. Build Owner Index: Map<OwnerID, Item[]>
32
+ // This index groups all items (domains, services, etc.) by their owner IDs.
33
+ // This allows O(1) lookup to find all items owned by a specific team.
34
+ const ownershipMap = new Map<string, CollectionEntry<CollectionTypes>[]>();
35
+
36
+ const addToIndex = (items: CollectionEntry<CollectionTypes>[]) => {
37
+ for (const item of items) {
38
+ if (item.data.owners) {
39
+ for (const owner of item.data.owners) {
40
+ if (!ownershipMap.has(owner.id)) {
41
+ ownershipMap.set(owner.id, []);
42
+ }
43
+ ownershipMap.get(owner.id)!.push(item);
44
+ }
45
+ }
46
+ }
47
+ };
48
+
49
+ addToIndex(allDomains);
50
+ addToIndex(allServices);
51
+ addToIndex(allEvents);
52
+ addToIndex(allCommands);
53
+ addToIndex(allQueries);
54
+
55
+ // 4. Enrich teams using the ownership index
56
+ const processedTeams = targetTeams.map((team) => {
57
+ const teamId = team.data.id;
58
+ const ownedItems = ownershipMap.get(teamId) || [];
59
+
60
+ // Categorize items
61
+ const ownedDomains = ownedItems.filter((i) => i.collection === 'domains') as CollectionEntry<'domains'>[];
62
+ const ownedServices = ownedItems.filter((i) => i.collection === 'services') as CollectionEntry<'services'>[];
63
+ const ownedEvents = ownedItems.filter((i) => i.collection === 'events') as CollectionEntry<'events'>[];
64
+ const ownedCommands = ownedItems.filter((i) => i.collection === 'commands') as CollectionEntry<'commands'>[];
65
+ const ownedQueries = ownedItems.filter((i) => i.collection === 'queries') as CollectionEntry<'queries'>[];
66
+
67
+ return {
68
+ ...team,
69
+ data: {
70
+ ...team.data,
71
+ ownedDomains,
72
+ ownedServices,
73
+ ownedCommands,
74
+ ownedQueries,
75
+ ownedEvents,
76
+ },
77
+ catalog: {
78
+ path: path.join(team.collection, team.id.replace('/index.mdx', '')),
79
+ filePath: path.join(process.cwd(), 'src', 'catalog-files', team.collection, team.id.replace('/index.mdx', '')),
80
+ type: 'team',
81
+ },
82
+ };
83
+ });
84
+
85
+ // order them by the name of the team
86
+ processedTeams.sort((a, b) => {
87
+ return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
88
+ });
89
+
90
+ memoryCache = processedTeams;
91
+ // console.timeEnd('✅ New getTeams');
92
+
93
+ return processedTeams;
94
+ };