@eventcatalog/core 3.5.2 → 3.6.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 (39) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-OKWCSRLE.js → chunk-2DSMO5BZ.js} +1 -1
  6. package/dist/{chunk-YTZSPYJN.js → chunk-O3LNFOFS.js} +1 -1
  7. package/dist/{chunk-YVX5C6L3.js → chunk-O7ZZX4CS.js} +1 -1
  8. package/dist/{chunk-WO3AKJVB.js → chunk-XTN3M6CM.js} +1 -1
  9. package/dist/{chunk-YOFNY2RC.js → chunk-YQ2LO4G6.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +1 -1
  13. package/dist/eventcatalog.js +5 -5
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/src/components/EnvironmentDropdown.tsx +1 -1
  19. package/eventcatalog/src/components/Search/SearchDataLoader.astro +23 -11
  20. package/eventcatalog/src/components/Search/SearchModal.tsx +17 -2
  21. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +12 -6
  22. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +25 -14
  23. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +816 -0
  24. package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +161 -0
  25. package/eventcatalog/src/components/Tables/Discover/columns.tsx +565 -0
  26. package/eventcatalog/src/components/Tables/Discover/index.ts +4 -0
  27. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +1 -1
  28. package/eventcatalog/src/components/Tables/columns/DomainTableColumns.tsx +1 -1
  29. package/eventcatalog/src/components/Tables/columns/FlowTableColumns.tsx +1 -1
  30. package/eventcatalog/src/components/Tables/columns/MessageTableColumns.tsx +1 -1
  31. package/eventcatalog/src/components/Tables/columns/ServiceTableColumns.tsx +54 -64
  32. package/eventcatalog/src/components/Tables/columns/SharedColumns.tsx +15 -30
  33. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +1 -1
  34. package/eventcatalog/src/pages/api/sidebar-data.json.ts +22 -0
  35. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -1
  36. package/eventcatalog/src/pages/discover/[type]/index.astro +360 -41
  37. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +1 -1
  38. package/eventcatalog/src/stores/sidebar-store/state.ts +25 -22
  39. package/package.json +1 -1
@@ -1,7 +1,21 @@
1
1
  ---
2
- import type { CollectionEntry } from 'astro:content';
3
- import type { CollectionTypes } from '@types';
4
- import DiscoverLayout, { type Props as DiscoverLayoutProps } from '@layouts/DiscoverLayout.astro';
2
+ import { QueueListIcon, RectangleGroupIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/outline';
3
+ import ServerIcon from '@heroicons/react/24/outline/ServerIcon';
4
+ import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
5
+ import { DatabaseIcon } from 'lucide-react';
6
+ import { getCommands } from '@utils/collections/commands';
7
+ import { getDomains, getDomainsForService } from '@utils/collections/domains';
8
+ import { getFlows } from '@utils/collections/flows';
9
+ import { getEvents } from '@utils/collections/events';
10
+ import { getServices } from '@utils/collections/services';
11
+ import { getQueries } from '@utils/collections/queries';
12
+ import { getContainers } from '@utils/collections/containers';
13
+ import { getUsers } from '@utils/collections/users';
14
+ import { getTeams } from '@utils/collections/teams';
15
+ import { buildUrl } from '@utils/url-builder';
16
+ import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
17
+ import { DiscoverTable, type DiscoverTableData, type CollectionType } from '@components/Tables/Discover';
18
+ import config from '@config';
5
19
  import { Page } from './_index.data';
6
20
 
7
21
  export const prerender = Page.prerender;
@@ -9,12 +23,165 @@ export const getStaticPaths = Page.getStaticPaths;
9
23
 
10
24
  const { type, data } = await Page.getData(Astro);
11
25
 
12
- let title = `${type} (${data.length})`;
26
+ // Fetch all collections for tabs
27
+ const events = await getEvents();
28
+ const queries = await getQueries();
29
+ const commands = await getCommands();
30
+ const services = await getServices();
31
+ const domains = await getDomains({ getAllVersions: false });
32
+ const flows = await getFlows();
33
+ const containers = await getContainers();
34
+ const users = await getUsers();
35
+ const teams = await getTeams();
13
36
 
14
- if (type === 'containers') {
15
- title = `Data (${data.length})`;
16
- }
37
+ // Create lookup maps for users and teams
38
+ const userMap = new Map(users.map((u) => [u.data.id, u]));
39
+ const teamMap = new Map(teams.map((t) => [t.data.id, t]));
40
+
41
+ // Type configuration for property options
42
+ const typeConfig: Record<
43
+ string,
44
+ {
45
+ label: string;
46
+ propertyOptions: Array<{ id: string; label: string }>;
47
+ }
48
+ > = {
49
+ events: {
50
+ label: 'Events',
51
+ propertyOptions: [
52
+ { id: 'hasProducers', label: 'Has Producers' },
53
+ { id: 'hasConsumers', label: 'Has Consumers' },
54
+ { id: 'hasOwners', label: 'Has Owners' },
55
+ { id: 'isDeprecated', label: 'Is Deprecated' },
56
+ ],
57
+ },
58
+ commands: {
59
+ label: 'Commands',
60
+ propertyOptions: [
61
+ { id: 'hasProducers', label: 'Has Producers' },
62
+ { id: 'hasConsumers', label: 'Has Consumers' },
63
+ { id: 'hasOwners', label: 'Has Owners' },
64
+ { id: 'isDeprecated', label: 'Is Deprecated' },
65
+ ],
66
+ },
67
+ queries: {
68
+ label: 'Queries',
69
+ propertyOptions: [
70
+ { id: 'hasProducers', label: 'Has Producers' },
71
+ { id: 'hasConsumers', label: 'Has Consumers' },
72
+ { id: 'hasOwners', label: 'Has Owners' },
73
+ { id: 'isDeprecated', label: 'Is Deprecated' },
74
+ ],
75
+ },
76
+ domains: {
77
+ label: 'Domains',
78
+ propertyOptions: [
79
+ { id: 'hasServices', label: 'Has Services' },
80
+ { id: 'hasOwners', label: 'Has Owners' },
81
+ { id: 'isSubdomain', label: 'Is Subdomain' },
82
+ { id: 'isDeprecated', label: 'Is Deprecated' },
83
+ ],
84
+ },
85
+ services: {
86
+ label: 'Services',
87
+ propertyOptions: [
88
+ { id: 'hasSpecifications', label: 'Has Specifications' },
89
+ { id: 'hasOwners', label: 'Has Owners' },
90
+ { id: 'hasRepository', label: 'Has Repository' },
91
+ { id: 'hasDataDependencies', label: 'Has Data Dependencies' },
92
+ { id: 'isDeprecated', label: 'Is Deprecated' },
93
+ ],
94
+ },
95
+ flows: {
96
+ label: 'Flows',
97
+ propertyOptions: [
98
+ { id: 'hasOwners', label: 'Has Owners' },
99
+ { id: 'isDeprecated', label: 'Is Deprecated' },
100
+ ],
101
+ },
102
+ containers: {
103
+ label: 'Data',
104
+ propertyOptions: [
105
+ { id: 'hasOwners', label: 'Has Owners' },
106
+ { id: 'hasWriters', label: 'Has Writers' },
107
+ { id: 'hasReaders', label: 'Has Readers' },
108
+ { id: 'isDeprecated', label: 'Is Deprecated' },
109
+ ],
110
+ },
111
+ };
112
+
113
+ const currentTypeConfig = typeConfig[type] || typeConfig.events;
114
+
115
+ // @ts-ignore
116
+ const tableConfiguration = config[type as keyof typeof config]?.tableConfiguration ?? { columns: {} };
17
117
 
118
+ const tabs = [
119
+ {
120
+ label: `Domains (${domains.length})`,
121
+ href: buildUrl('/discover/domains'),
122
+ isActive: type === 'domains',
123
+ icon: RectangleGroupIcon,
124
+ activeColor: 'yellow',
125
+ enabled: domains.length > 0,
126
+ visible: domains.length > 0,
127
+ },
128
+ {
129
+ label: `Services (${services.length})`,
130
+ href: buildUrl('/discover/services'),
131
+ isActive: type === 'services',
132
+ icon: ServerIcon,
133
+ activeColor: 'pink',
134
+ enabled: services.length > 0,
135
+ visible: services.length > 0,
136
+ },
137
+ {
138
+ label: `Data (${containers.length})`,
139
+ href: buildUrl('/discover/containers'),
140
+ isActive: type === 'containers',
141
+ icon: DatabaseIcon,
142
+ activeColor: 'blue',
143
+ enabled: containers.length > 0,
144
+ visible: containers.length > 0,
145
+ },
146
+ {
147
+ label: `Events (${events.length})`,
148
+ href: buildUrl('/discover/events'),
149
+ isActive: type === 'events',
150
+ icon: BoltIcon,
151
+ activeColor: 'orange',
152
+ enabled: events.length > 0,
153
+ visible: events.length > 0,
154
+ },
155
+ {
156
+ label: `Queries (${queries.length})`,
157
+ href: buildUrl('/discover/queries'),
158
+ isActive: type === 'queries',
159
+ icon: MagnifyingGlassIcon,
160
+ activeColor: 'green',
161
+ enabled: queries.length > 0,
162
+ visible: queries.length > 0,
163
+ },
164
+ {
165
+ label: `Commands (${commands.length})`,
166
+ href: buildUrl('/discover/commands'),
167
+ isActive: type === 'commands',
168
+ icon: ChatBubbleLeftIcon,
169
+ activeColor: 'blue',
170
+ enabled: commands.length > 0,
171
+ visible: commands.length > 0,
172
+ },
173
+ {
174
+ label: `Flows (${flows.length})`,
175
+ href: buildUrl('/discover/flows'),
176
+ isActive: type === 'flows',
177
+ icon: QueueListIcon,
178
+ activeColor: 'teal',
179
+ enabled: flows.length > 0,
180
+ visible: flows.length > 0,
181
+ },
182
+ ];
183
+
184
+ // Map data to match the expected structure for the table
18
185
  function mapToItem(i: any) {
19
186
  return {
20
187
  collection: i.collection,
@@ -25,39 +192,191 @@ function mapToItem(i: any) {
25
192
  },
26
193
  };
27
194
  }
195
+
196
+ // Helper to map owner references with type detection
197
+ function mapOwner(o: any) {
198
+ if (!o) return null;
199
+ const id = typeof o === 'string' ? o : o.data?.id || o.id || o;
200
+
201
+ // Look up in users first, then teams
202
+ const user = userMap.get(id);
203
+ if (user) {
204
+ return { id, name: user.data.name || id, type: 'user' as const };
205
+ }
206
+
207
+ const team = teamMap.get(id);
208
+ if (team) {
209
+ return { id, name: team.data.name || id, type: 'team' as const };
210
+ }
211
+
212
+ // Fallback if not found in either
213
+ const name = typeof o === 'string' ? o : o.data?.name || o.name || id;
214
+ return { id, name, type: 'user' as const };
215
+ }
216
+
217
+ // Helper to check if service has specifications
218
+ function hasSpecifications(service: any): boolean {
219
+ const specs = service.data?.specifications;
220
+ if (!specs) return false;
221
+ if (Array.isArray(specs)) return specs.length > 0;
222
+ return !!(specs.openapiPath || specs.asyncapiPath || specs.graphqlPath);
223
+ }
224
+
225
+ // Build a Set of subdomain IDs (domains that are nested within other domains)
226
+ const allSubdomainIds = new Set(
227
+ domains.flatMap((d: any) => (d.data?.domains || []).map((sd: any) => sd.data?.id || sd.id)).filter(Boolean)
228
+ );
229
+
230
+ // For services, enrich with domain information
231
+ const enrichedData =
232
+ type === 'services'
233
+ ? await Promise.all(
234
+ data.map(async (service: any) => {
235
+ const serviceDomains = await getDomainsForService(service);
236
+ return {
237
+ ...service,
238
+ enrichedDomains: serviceDomains.map((d: any) => ({
239
+ id: d.data.id,
240
+ name: d.data.name,
241
+ version: d.data.version,
242
+ })),
243
+ };
244
+ })
245
+ )
246
+ : data;
247
+
248
+ const tableData = enrichedData.map((d: any) => ({
249
+ collection: d.collection,
250
+ owners: (d.data?.owners || []).map(mapOwner).filter(Boolean),
251
+ hasOwners: (d.data?.owners || []).length > 0,
252
+ hasServices: type === 'domains' ? (d.data?.services || []).length > 0 : false,
253
+ isSubdomain: type === 'domains' ? allSubdomainIds.has(d.data.id) : false,
254
+ // Service-specific properties
255
+ domains: type === 'services' ? d.enrichedDomains : undefined,
256
+ hasSpecifications: type === 'services' ? hasSpecifications(d) : false,
257
+ hasRepository: type === 'services' ? !!d.data?.repository?.url : false,
258
+ hasDataDependencies: type === 'services' ? (d.data?.writesTo || []).length > 0 || (d.data?.readsFrom || []).length > 0 : false,
259
+ isDeprecated: d.data?.deprecated === true || (typeof d.data?.deprecated === 'object' && d.data?.deprecated !== null),
260
+ data: {
261
+ id: d.data.id,
262
+ name: d.data.name,
263
+ summary: d.data?.summary,
264
+ version: d.data.version,
265
+ latestVersion: d.data?.latestVersion,
266
+ draft: d.data?.draft,
267
+ badges: d.data?.badges,
268
+ producers: d.data?.producers?.map(mapToItem) ?? [],
269
+ consumers: d.data?.consumers?.map(mapToItem) ?? [],
270
+ receives: d.data?.receives?.map(mapToItem) ?? [],
271
+ sends: d.data?.sends?.map(mapToItem) ?? [],
272
+ services: d.data?.services?.map(mapToItem) ?? [],
273
+ servicesThatWriteToContainer: d.data?.servicesThatWriteToContainer?.map(mapToItem) ?? [],
274
+ servicesThatReadFromContainer: d.data?.servicesThatReadFromContainer?.map(mapToItem) ?? [],
275
+ },
276
+ }));
277
+
278
+ // Get unique owners from all items
279
+ const uniqueOwners = Array.from(new Map(tableData.flatMap((d: any) => d.owners || []).map((o: any) => [o.id, o])).values()).sort(
280
+ (a: any, b: any) => a.name.localeCompare(b.name)
281
+ );
282
+
283
+ // Get unique producers (services) for message types
284
+ // Check for duplicate names and add version if needed
285
+ const servicesByName = new Map<string, typeof services>();
286
+ services.forEach((s) => {
287
+ const name = s.data.name;
288
+ if (!servicesByName.has(name)) servicesByName.set(name, []);
289
+ servicesByName.get(name)!.push(s);
290
+ });
291
+
292
+ const uniqueProducers = services
293
+ .map((s) => {
294
+ const hasDuplicateName = (servicesByName.get(s.data.name)?.length ?? 0) > 1;
295
+ const isLatest = s.data.version === s.data.latestVersion;
296
+ const versionLabel = isLatest ? 'latest' : `v${s.data.version}`;
297
+ return {
298
+ id: s.data.id,
299
+ name: hasDuplicateName ? `${s.data.name} (${versionLabel})` : s.data.name,
300
+ };
301
+ })
302
+ .sort((a, b) => a.name.localeCompare(b.name));
303
+
304
+ // Show producers/consumers filter only for events, commands, queries
305
+ const showProducersFilter = ['events', 'commands', 'queries'].includes(type);
306
+ const showConsumersFilter = ['events', 'commands', 'queries'].includes(type);
307
+
308
+ // Consumers are the same services list
309
+ const uniqueConsumers = uniqueProducers;
310
+
311
+ // Get unique domains for the services filter
312
+ const uniqueDomains = domains.map((d) => ({
313
+ id: d.data.id,
314
+ name: d.data.name,
315
+ version: d.data.version,
316
+ }));
317
+
318
+ // Show domains filter only for services
319
+ const showDomainsFilter = type === 'services';
320
+
321
+ const title = `${currentTypeConfig.label} (${data.length})`;
28
322
  ---
29
323
 
30
- <DiscoverLayout
31
- title={title}
32
- subtitle={`Find, filter and search for any ${type} in your system.`}
33
- data={data.map(
34
- (d: CollectionEntry<CollectionTypes>) =>
35
- ({
36
- collection: d.collection,
37
- data: {
38
- id: d.data.id,
39
- name: d.data.name,
40
- summary: d.data?.summary,
41
- version: d.data.version,
42
- latestVersion: d.data?.latestVersion,
43
- draft: d.data?.draft,
44
- badges: d.data?.badges,
45
- // @ts-ignore
46
- consumers: d.data?.consumers?.map(mapToItem) ?? [],
47
- // @ts-ignore
48
- producers: d.data?.producers?.map(mapToItem) ?? [],
49
- // @ts-ignore
50
- receives: d.data?.receives?.map(mapToItem) ?? [],
51
- // @ts-ignore
52
- sends: d.data?.sends?.map(mapToItem) ?? [],
53
- // @ts-ignore
54
- services: d.data?.services?.map(mapToItem) ?? [],
55
- // @ts-ignore
56
- servicesThatWriteToContainer: d.data?.servicesThatWriteToContainer?.map(mapToItem) ?? [],
57
- // @ts-ignore
58
- servicesThatReadFromContainer: d.data?.servicesThatReadFromContainer?.map(mapToItem) ?? [],
59
- },
60
- }) as DiscoverLayoutProps<typeof type>['data'][0]
61
- )}
62
- type={type}
63
- />
324
+ <VerticalSideBarLayout title={`Explore | ${title}`} showNestedSideBar={false}>
325
+ <main class="ml-0 bg-[rgb(var(--ec-page-bg))] min-h-content">
326
+ <div id="discover-collection-tabs">
327
+ <div class="hidden sm:block">
328
+ <div class="border-b border-[rgb(var(--ec-page-border))]">
329
+ <nav class="flex space-x-8 -mb-0.5 pl-6" aria-label="Tabs">
330
+ {
331
+ tabs
332
+ .filter((tab) => tab.visible)
333
+ .map((tab) => (
334
+ <a
335
+ href={tab.href}
336
+ class:list={[
337
+ 'group inline-flex items-center py-4 px-1 text-sm font-light text-[rgb(var(--ec-page-text))]',
338
+ tab.isActive && 'border-b-[2px] border-[rgb(var(--ec-accent))] text-[rgb(var(--ec-accent))]',
339
+ !tab.isActive && 'opacity-70 hover:opacity-100',
340
+ !tab.enabled && 'disabled',
341
+ ]}
342
+ aria-current="page"
343
+ >
344
+ <tab.icon
345
+ className={`w-6 h-6 -ml-0.5 mr-2 ${tab.isActive ? 'text-[rgb(var(--ec-accent))]' : 'text-[rgb(var(--ec-icon-color))]'}`}
346
+ />
347
+ <span>{tab.label}</span>
348
+ </a>
349
+ ))
350
+ }
351
+ </nav>
352
+ </div>
353
+ </div>
354
+ </div>
355
+
356
+ <div class="py-4 px-6 md:pr-10">
357
+ <DiscoverTable
358
+ data={tableData as DiscoverTableData[]}
359
+ collectionType={type as CollectionType}
360
+ collectionLabel={currentTypeConfig.label}
361
+ domains={uniqueDomains}
362
+ owners={uniqueOwners as Array<{ id: string; name: string; type?: 'user' | 'team' }>}
363
+ producers={uniqueProducers}
364
+ consumers={uniqueConsumers}
365
+ propertyOptions={currentTypeConfig.propertyOptions}
366
+ tableConfiguration={tableConfiguration}
367
+ showDomainsFilter={showDomainsFilter}
368
+ showProducersFilter={showProducersFilter}
369
+ showConsumersFilter={showConsumersFilter}
370
+ client:only="react"
371
+ />
372
+ </div>
373
+ </main>
374
+ </VerticalSideBarLayout>
375
+
376
+ <style>
377
+ a.disabled {
378
+ pointer-events: none;
379
+ cursor: default;
380
+ opacity: 0.25;
381
+ }
382
+ </style>
@@ -31,7 +31,7 @@ export type NavNode = {
31
31
  */
32
32
  export type NavigationData = {
33
33
  roots: ChildRef[]; // What to show at top level
34
- nodes: Record<string, NavNode>; // Flat map of all nodes by key
34
+ nodes: Record<string, NavNode | string>; // Flat map of nodes by key, strings are references to other keys (e.g., unversioned aliases)
35
35
  };
36
36
 
37
37
  export const uniqueBy = <T>(array: T[], key: keyof T): T[] => {
@@ -115,48 +115,55 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
115
115
 
116
116
  const domainNodes = domainsWithOwners.reduce(
117
117
  (acc, { domain, owners }) => {
118
- acc[`domain:${domain.data.id}:${domain.data.version}`] = buildDomainNode(domain, owners, context);
118
+ const versionedKey = `domain:${domain.data.id}:${domain.data.version}`;
119
+ acc[versionedKey] = buildDomainNode(domain, owners, context);
119
120
  if (domain.data.latestVersion === domain.data.version) {
120
- acc[`domain:${domain.data.id}`] = buildDomainNode(domain, owners, context);
121
+ // Store reference to versioned key instead of duplicating the full node
122
+ acc[`domain:${domain.data.id}`] = versionedKey;
121
123
  }
122
124
  return acc;
123
125
  },
124
- {} as Record<string, NavNode>
126
+ {} as Record<string, NavNode | string>
125
127
  );
126
128
 
127
129
  const serviceNodes = servicesWithOwners.reduce(
128
130
  (acc, { service, owners }) => {
129
- acc[`service:${service.data.id}:${service.data.version}`] = buildServiceNode(service, owners, context);
131
+ const versionedKey = `service:${service.data.id}:${service.data.version}`;
132
+ acc[versionedKey] = buildServiceNode(service, owners, context);
130
133
  if (service.data.latestVersion === service.data.version) {
131
- acc[`service:${service.data.id}`] = buildServiceNode(service, owners, context);
134
+ // Store reference to versioned key instead of duplicating the full node
135
+ acc[`service:${service.data.id}`] = versionedKey;
132
136
  }
133
137
  return acc;
134
138
  },
135
- {} as Record<string, NavNode>
139
+ {} as Record<string, NavNode | string>
136
140
  );
137
141
 
138
142
  const messageNodes = messagesWithOwners.reduce(
139
143
  (acc, { message, owners }) => {
140
144
  const type = pluralizeMessageType(message as any);
141
-
142
- acc[`${type}:${message.data.id}:${message.data.version}`] = buildMessageNode(message, owners, context);
145
+ const versionedKey = `${type}:${message.data.id}:${message.data.version}`;
146
+ acc[versionedKey] = buildMessageNode(message, owners, context);
143
147
  if (message.data.latestVersion === message.data.version) {
144
- acc[`${type}:${message.data.id}`] = buildMessageNode(message, owners, context);
148
+ // Store reference to versioned key instead of duplicating the full node
149
+ acc[`${type}:${message.data.id}`] = versionedKey;
145
150
  }
146
151
  return acc;
147
152
  },
148
- {} as Record<string, NavNode>
153
+ {} as Record<string, NavNode | string>
149
154
  );
150
155
 
151
156
  const containerNodes = containerWithOwners.reduce(
152
157
  (acc, { container, owners }) => {
153
- acc[`container:${container.data.id}:${container.data.version}`] = buildContainerNode(container, owners, context);
158
+ const versionedKey = `container:${container.data.id}:${container.data.version}`;
159
+ acc[versionedKey] = buildContainerNode(container, owners, context);
154
160
  if (container.data.latestVersion === container.data.version) {
155
- acc[`container:${container.data.id}`] = buildContainerNode(container, owners, context);
161
+ // Store reference to versioned key instead of duplicating the full node
162
+ acc[`container:${container.data.id}`] = versionedKey;
156
163
  }
157
164
  return acc;
158
165
  },
159
- {} as Record<string, NavNode>
166
+ {} as Record<string, NavNode | string>
160
167
  );
161
168
 
162
169
  const designNodes = designs.reduce(
@@ -186,7 +193,8 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
186
193
 
187
194
  const channelNodes = channels.reduce(
188
195
  (acc, channel) => {
189
- acc[`channel:${channel.data.id}:${channel.data.version}`] = {
196
+ const versionedKey = `channel:${channel.data.id}:${channel.data.version}`;
197
+ acc[versionedKey] = {
190
198
  type: 'item',
191
199
  title: channel.data.name,
192
200
  badge: 'Channel',
@@ -195,17 +203,12 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
195
203
  };
196
204
 
197
205
  if (channel.data.latestVersion === channel.data.version) {
198
- acc[`channel:${channel.data.id}`] = {
199
- type: 'item',
200
- title: channel.data.name,
201
- badge: 'Channel',
202
- summary: channel.data.summary,
203
- href: buildUrl(`/docs/${channel.collection}/${channel.data.id}/${channel.data.version}`),
204
- };
206
+ // Store reference to versioned key instead of duplicating the full node
207
+ acc[`channel:${channel.data.id}`] = versionedKey;
205
208
  }
206
209
  return acc;
207
210
  },
208
- {} as Record<string, NavNode>
211
+ {} as Record<string, NavNode | string>
209
212
  );
210
213
 
211
214
  const teamNodes = teams.reduce(
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/event-catalog/eventcatalog.git"
7
7
  },
8
8
  "type": "module",
9
- "version": "3.5.2",
9
+ "version": "3.6.0",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },