@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,18 +1,19 @@
1
1
  import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import path from 'path';
4
- import { getItemsFromCollectionByIdAndSemverOrLatest, getVersionForCollectionItem, satisfies } from './collections/util';
5
- import { getMessages } from './messages';
4
+ import { getItemsFromCollectionByIdAndSemverOrLatest, createVersionedMap, satisfies } from './util';
6
5
  import type { CollectionMessageTypes } from '@types';
7
6
  import utils from '@eventcatalog/sdk';
8
7
 
9
8
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
9
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
10
10
 
11
11
  type Channel = CollectionEntry<'channels'> & {
12
12
  catalog: {
13
13
  path: string;
14
14
  filePath: string;
15
15
  type: string;
16
+ publicPath: string;
16
17
  };
17
18
  };
18
19
 
@@ -21,43 +22,89 @@ interface Props {
21
22
  }
22
23
 
23
24
  // cache for build time
24
- let cachedChannels: Record<string, Channel[]> = {
25
- allVersions: [],
26
- currentVersions: [],
27
- };
25
+ let memoryCache: Record<string, Channel[]> = {};
28
26
 
29
27
  export const getChannels = async ({ getAllVersions = true }: Props = {}): Promise<Channel[]> => {
28
+ // console.time('✅ New getChannels');
30
29
  const cacheKey = getAllVersions ? 'allVersions' : 'currentVersions';
31
30
 
32
- if (cachedChannels[cacheKey].length > 0) {
33
- return cachedChannels[cacheKey];
31
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0 && CACHE_ENABLED) {
32
+ // console.timeEnd('✅ New getChannels');
33
+ return memoryCache[cacheKey];
34
34
  }
35
35
 
36
- const channels = await getCollection('channels', (query) => {
37
- return (getAllVersions || !query.filePath?.includes('versioned')) && query.data.hidden !== true;
38
- });
39
-
40
- const { commands, events, queries } = await getMessages();
41
- const allMessages = [...commands, ...events, ...queries];
42
-
43
- cachedChannels[cacheKey] = await Promise.all(
44
- channels.map(async (channel) => {
45
- const { latestVersion, versions } = getVersionForCollectionItem(channel, channels);
46
-
47
- const messagesForChannel = allMessages.filter((message) => {
48
- return message.data.channels?.some((messageChannel) => {
49
- if (messageChannel.id != channel.data.id) return false;
50
- if (messageChannel.version == 'latest' || messageChannel.version == undefined)
51
- return channel.data.version == latestVersion;
52
- return satisfies(channel.data.version, messageChannel.version);
36
+ // 1. Fetch collections in parallel
37
+ const [allChannels, allEvents, allCommands, allQueries] = await Promise.all([
38
+ getCollection('channels'),
39
+ getCollection('events'),
40
+ getCollection('commands'),
41
+ getCollection('queries'),
42
+ ]);
43
+
44
+ const allMessages = [...allEvents, ...allCommands, ...allQueries];
45
+
46
+ // 2. Build optimized maps
47
+ const channelMap = createVersionedMap(allChannels);
48
+
49
+ // 3. Build Message Index by Channel ID (Reverse Index)
50
+ // Map<ChannelID, Array<{ message: Message, requiredVersion: string | undefined }>>
51
+ const messagesByChannelId = new Map<
52
+ string,
53
+ Array<{ message: CollectionEntry<CollectionMessageTypes>; requiredVersion?: string }>
54
+ >();
55
+
56
+ for (const message of allMessages) {
57
+ if (message.data.channels) {
58
+ for (const channelRef of message.data.channels) {
59
+ if (!messagesByChannelId.has(channelRef.id)) {
60
+ messagesByChannelId.set(channelRef.id, []);
61
+ }
62
+ messagesByChannelId.get(channelRef.id)!.push({
63
+ message,
64
+ requiredVersion: channelRef.version,
53
65
  });
54
- });
66
+ }
67
+ }
68
+ }
55
69
 
56
- const messages = messagesForChannel.map((message: CollectionEntry<CollectionMessageTypes>) => {
57
- return { id: message.data.id, name: message.data.name, version: message.data.version, collection: message.collection };
70
+ // 4. Filter channels
71
+ const targetChannels = allChannels.filter((channel) => {
72
+ if (channel.data.hidden === true) return false;
73
+ if (!getAllVersions && channel.filePath?.includes('versioned')) return false;
74
+ return true;
75
+ });
76
+
77
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
78
+
79
+ // 5. Enrich channels
80
+ const processedChannels = await Promise.all(
81
+ targetChannels.map(async (channel) => {
82
+ // Version info
83
+ const channelVersions = channelMap.get(channel.data.id) || [];
84
+ const latestVersion = channelVersions[0]?.data.version || channel.data.version;
85
+ const versions = channelVersions.map((c) => c.data.version);
86
+
87
+ // Find messages for this channel version
88
+ const candidateMessages = messagesByChannelId.get(channel.data.id) || [];
89
+
90
+ const messagesForChannel = candidateMessages
91
+ .filter(({ requiredVersion }) => {
92
+ if (requiredVersion === 'latest' || requiredVersion === undefined) {
93
+ return channel.data.version === latestVersion;
94
+ }
95
+ return satisfies(channel.data.version, requiredVersion);
96
+ })
97
+ .map(({ message }) => message);
98
+
99
+ const messages = messagesForChannel.map((message) => {
100
+ return {
101
+ id: message.data.id,
102
+ name: message.data.name,
103
+ version: message.data.version,
104
+ collection: message.collection,
105
+ };
58
106
  });
59
107
 
60
- const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
61
108
  const folderName = await getResourceFolderName(
62
109
  process.env.PROJECT_DIR ?? '',
63
110
  channel.data.id,
@@ -86,11 +133,14 @@ export const getChannels = async ({ getAllVersions = true }: Props = {}): Promis
86
133
  );
87
134
 
88
135
  // order them by the name of the channel
89
- cachedChannels[cacheKey].sort((a, b) => {
136
+ processedChannels.sort((a, b) => {
90
137
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
91
138
  });
92
139
 
93
- return cachedChannels[cacheKey];
140
+ memoryCache[cacheKey] = processedChannels;
141
+ // console.timeEnd('✅ New getChannels');
142
+
143
+ return processedChannels;
94
144
  };
95
145
 
96
146
  // Could be recursive, we need to keep going until we find a loop or until we reach the target channel
@@ -0,0 +1,134 @@
1
+ import { getCollection } from 'astro:content';
2
+ import type { CollectionEntry } from 'astro:content';
3
+ import path from 'path';
4
+ import { createVersionedMap, 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 Command = CollectionEntry<'commands'> & {
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, Command[]> = {};
26
+
27
+ export const getCommands = async ({ getAllVersions = true, hydrateServices = true }: Props = {}): Promise<Command[]> => {
28
+ // console.time('✅ New getCommands');
29
+ const cacheKey = `${getAllVersions ? 'allVersions' : 'currentVersions'}-${hydrateServices ? 'hydrated' : 'minimal'}`;
30
+
31
+ // Check cache
32
+ if (memoryCache[cacheKey] && memoryCache[cacheKey].length > 0) {
33
+ // console.timeEnd('✅ New getCommands');
34
+ return memoryCache[cacheKey];
35
+ }
36
+
37
+ // 1. Fetch collections in parallel
38
+ const [allCommands, allServices, allChannels] = await Promise.all([
39
+ getCollection('commands'),
40
+ getCollection('services'),
41
+ getCollection('channels'),
42
+ ]);
43
+
44
+ // 2. Build optimized maps
45
+ const commandMap = createVersionedMap(allCommands);
46
+
47
+ // 3. Filter commands
48
+ const targetCommands = allCommands.filter((command) => {
49
+ if (command.data.hidden === true) return false;
50
+ if (!getAllVersions && command.filePath?.includes('versioned')) return false;
51
+ return true;
52
+ });
53
+
54
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
55
+
56
+ // 4. Enrich commands
57
+ const processedCommands = await Promise.all(
58
+ targetCommands.map(async (command) => {
59
+ // Version info
60
+ const commandVersions = commandMap.get(command.data.id) || [];
61
+ const latestVersion = commandVersions[0]?.data.version || command.data.version;
62
+ const versions = commandVersions.map((e) => e.data.version);
63
+
64
+ // Find Producers (Services that send this command)
65
+ const producers = allServices
66
+ .filter((service) =>
67
+ service.data.sends?.some((item) => {
68
+ if (item.id !== command.data.id) return false;
69
+ if (item.version === 'latest' || item.version === undefined) return command.data.version === latestVersion;
70
+ return satisfies(command.data.version, item.version);
71
+ })
72
+ )
73
+ .map((service) => {
74
+ if (!hydrateServices) return { id: service.data.id, version: service.data.version };
75
+ return service;
76
+ });
77
+
78
+ // Find Consumers (Services that receive this command)
79
+ const consumers = allServices
80
+ .filter((service) =>
81
+ service.data.receives?.some((item) => {
82
+ if (item.id !== command.data.id) return false;
83
+ if (item.version === 'latest' || item.version === undefined) return command.data.version === latestVersion;
84
+ return satisfies(command.data.version, item.version);
85
+ })
86
+ )
87
+ .map((service) => {
88
+ if (!hydrateServices) return { id: service.data.id, version: service.data.version };
89
+ return service;
90
+ });
91
+
92
+ // Find Channels
93
+ const messageChannels = command.data.channels || [];
94
+ const channelsForCommand = allChannels.filter((c) => messageChannels.some((channel) => c.data.id === channel.id));
95
+
96
+ const folderName = await getResourceFolderName(
97
+ process.env.PROJECT_DIR ?? '',
98
+ command.data.id,
99
+ command.data.version.toString()
100
+ );
101
+ const commandFolderName = folderName ?? command.id.replace(`-${command.data.version}`, '');
102
+
103
+ return {
104
+ ...command,
105
+ data: {
106
+ ...command.data,
107
+ messageChannels: channelsForCommand,
108
+ producers: producers as any, // Cast for hydration flexibility
109
+ consumers: consumers as any,
110
+ versions,
111
+ latestVersion,
112
+ },
113
+ catalog: {
114
+ path: path.join(command.collection, command.id.replace('/index.mdx', '')),
115
+ absoluteFilePath: path.join(PROJECT_DIR, command.collection, command.id.replace('/index.mdx', '/index.md')),
116
+ astroContentFilePath: path.join(process.cwd(), 'src', 'content', command.collection, command.id),
117
+ filePath: path.join(process.cwd(), 'src', 'catalog-files', command.collection, command.id.replace('/index.mdx', '')),
118
+ publicPath: path.join('/generated', command.collection, commandFolderName),
119
+ type: 'command',
120
+ },
121
+ };
122
+ })
123
+ );
124
+
125
+ // order them by the name of the command
126
+ processedCommands.sort((a, b) => {
127
+ return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
128
+ });
129
+
130
+ memoryCache[cacheKey] = processedCommands;
131
+ // console.timeEnd('✅ New getCommands');
132
+
133
+ return processedCommands;
134
+ };
@@ -1,16 +1,17 @@
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 './util';
4
+ import { createVersionedMap, satisfies } from './util';
5
5
  import utils from '@eventcatalog/sdk';
6
6
 
7
7
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
-
8
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
9
9
  export type Entity = CollectionEntry<'containers'> & {
10
10
  catalog: {
11
11
  path: string;
12
12
  filePath: string;
13
13
  type: string;
14
+ publicPath: string;
14
15
  };
15
16
  };
16
17
 
@@ -19,54 +20,61 @@ 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 getContainers = async ({ getAllVersions = true }: Props = {}): Promise<Entity[]> => {
26
+ // console.time('✅ New getContainers');
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 && CACHE_ENABLED) {
30
+ // console.timeEnd('✅ New getContainers');
31
+ return memoryCache[cacheKey];
32
32
  }
33
33
 
34
- const containers = await getCollection('containers', (container) => {
35
- return (getAllVersions || !container.filePath?.includes('versioned')) && container.data.hidden !== true;
36
- });
34
+ // 1. Fetch collections in parallel
35
+ const [allContainers, allServices] = await Promise.all([getCollection('containers'), getCollection('services')]);
37
36
 
38
- const services = await getCollection('services');
37
+ // 2. Build optimized maps
38
+ const containerMap = createVersionedMap(allContainers);
39
39
 
40
- cachedEntities[cacheKey] = await Promise.all(
41
- containers.map(async (container) => {
42
- const { latestVersion, versions } = getVersionForCollectionItem(container, containers);
40
+ // 3. Filter containers
41
+ const targetContainers = allContainers.filter((container) => {
42
+ if (container.data.hidden === true) return false;
43
+ if (!getAllVersions && container.filePath?.includes('versioned')) return false;
44
+ return true;
45
+ });
43
46
 
44
- const servicesThatReferenceContainer = services.filter((service) => {
45
- const references = [...(service.data.writesTo || []), ...(service.data.readsFrom || [])];
46
- return references.some((item) => {
47
- if (item.id != container.data.id) return false;
48
- if (item.version == 'latest' || item.version == undefined) return container.data.version == latestVersion;
49
- return satisfies(container.data.version, item.version);
50
- });
51
- });
47
+ const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
48
+
49
+ // 4. Enrich containers
50
+ const processedContainers = await Promise.all(
51
+ targetContainers.map(async (container) => {
52
+ // Version info
53
+ const containerVersions = containerMap.get(container.data.id) || [];
54
+ const latestVersion = containerVersions[0]?.data.version || container.data.version;
55
+ const versions = containerVersions.map((c) => c.data.version);
52
56
 
53
- const servicesThatWriteToContainer = services.filter((service) => {
57
+ // Find Services that write to this container
58
+ const servicesThatWriteToContainer = allServices.filter((service) => {
54
59
  return service.data?.writesTo?.some((item) => {
55
- if (item.id != container.data.id) return false;
56
- if (item.version == 'latest' || item.version == undefined) return container.data.version == latestVersion;
60
+ if (item.id !== container.data.id) return false;
61
+ if (item.version === 'latest' || item.version === undefined) return container.data.version === latestVersion;
57
62
  return satisfies(container.data.version, item.version);
58
63
  });
59
64
  });
60
65
 
61
- const servicesThatReadFromContainer = services.filter((service) => {
66
+ // Find Services that read from this container
67
+ const servicesThatReadFromContainer = allServices.filter((service) => {
62
68
  return service.data?.readsFrom?.some((item) => {
63
- if (item.id != container.data.id) return false;
64
- if (item.version == 'latest' || item.version == undefined) return container.data.version == latestVersion;
69
+ if (item.id !== container.data.id) return false;
70
+ if (item.version === 'latest' || item.version === undefined) return container.data.version === latestVersion;
65
71
  return satisfies(container.data.version, item.version);
66
72
  });
67
73
  });
68
74
 
69
- const { getResourceFolderName } = utils(process.env.PROJECT_DIR ?? '');
75
+ // Combine references
76
+ const servicesThatReferenceContainer = [...new Set([...servicesThatWriteToContainer, ...servicesThatReadFromContainer])];
77
+
70
78
  const folderName = await getResourceFolderName(
71
79
  process.env.PROJECT_DIR ?? '',
72
80
  container.data.id,
@@ -102,10 +110,13 @@ export const getContainers = async ({ getAllVersions = true }: Props = {}): Prom
102
110
  })
103
111
  );
104
112
 
105
- // order them by the name of the event
106
- cachedEntities[cacheKey].sort((a, b) => {
113
+ // order them by the name of the container
114
+ processedContainers.sort((a, b) => {
107
115
  return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
108
116
  });
109
117
 
110
- return cachedEntities[cacheKey];
118
+ memoryCache[cacheKey] = processedContainers;
119
+ // console.timeEnd('✅ New getContainers');
120
+
121
+ return processedContainers;
111
122
  };