@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,10 +1,12 @@
1
- import { ServerIcon } from '@heroicons/react/24/solid';
1
+ import { ServerIcon, DocumentTextIcon, MapIcon } from '@heroicons/react/24/solid';
2
+ import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/24/outline';
2
3
  import { createColumnHelper } from '@tanstack/react-table';
3
4
  import { useMemo, useState } from 'react';
4
5
  import { filterByName, filterCollectionByName } from '../filters/custom-filters';
5
6
  import { buildUrl } from '@utils/url-builder';
6
7
  import { getColorAndIconForCollection } from '@utils/collections/icons';
7
8
  import { createBadgesColumn } from './SharedColumns';
9
+ import FavoriteButton from '@components/FavoriteButton';
8
10
  import type { TData } from '../Table';
9
11
  import type { TableConfiguration } from '@types';
10
12
  const columnHelper = createColumnHelper<TData<'services'>>();
@@ -14,21 +16,18 @@ export const columns = (tableConfiguration: TableConfiguration) => [
14
16
  id: 'name',
15
17
  header: () => <span>{tableConfiguration.columns?.name?.label || 'Service'}</span>,
16
18
  cell: (info) => {
17
- const messageRaw = info.row.original;
19
+ const service = info.row.original;
20
+ const isLatestVersion = service.data.version === service.data.latestVersion;
18
21
  return (
19
22
  <a
20
- href={buildUrl(`/docs/${messageRaw.collection}/${messageRaw.data.id}/${messageRaw.data.version}`)}
21
- className="group inline-flex items-center"
23
+ href={buildUrl(`/docs/${service.collection}/${service.data.id}/${service.data.version}`)}
24
+ className="group inline-flex items-center gap-2 hover:text-[rgb(var(--ec-accent))] transition-colors"
22
25
  >
23
- <span className="inline-flex items-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] hover:border-pink-400 dark:hover:border-pink-500 hover:bg-pink-50 dark:hover:bg-pink-500/10 transition-colors">
24
- <span className="flex items-center justify-center w-6 h-6 bg-pink-500 rounded-l-md">
25
- <ServerIcon className="h-3 w-3 text-white" />
26
- </span>
27
- <span className="px-2 py-1 text-xs text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-page-text))]">
28
- {messageRaw.data.name}
29
- <span className="text-[rgb(var(--ec-icon-color))] ml-1">v{messageRaw.data.version}</span>
30
- </span>
26
+ <ServerIcon className="h-4 w-4 text-pink-500 flex-shrink-0" />
27
+ <span className="text-sm font-medium text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-accent))]">
28
+ {service.data.name}
31
29
  </span>
30
+ {!isLatestVersion && <span className="text-xs text-[rgb(var(--ec-icon-color))]">v{service.data.version}</span>}
32
31
  </a>
33
32
  );
34
33
  },
@@ -45,7 +44,7 @@ export const columns = (tableConfiguration: TableConfiguration) => [
45
44
  const isDraft = info.row.original.data.draft;
46
45
  const displayText = `${summary || ''}${isDraft ? ' (Draft)' : ''}`;
47
46
  return (
48
- <span className="text-sm text-[rgb(var(--ec-page-text-muted))] line-clamp-2" title={displayText}>
47
+ <span className="text-sm text-[rgb(var(--ec-icon-color))] line-clamp-2" title={displayText}>
49
48
  {displayText}
50
49
  </span>
51
50
  );
@@ -58,7 +57,12 @@ export const columns = (tableConfiguration: TableConfiguration) => [
58
57
  }),
59
58
  columnHelper.accessor('data.receives', {
60
59
  id: 'receives',
61
- header: () => <span>Receives</span>,
60
+ header: () => (
61
+ <span className="flex items-center gap-1">
62
+ <ArrowDownIcon className="w-3.5 h-3.5" />
63
+ Receives
64
+ </span>
65
+ ),
62
66
  meta: {
63
67
  filterVariant: 'collection',
64
68
  collectionFilterKey: 'receives',
@@ -78,41 +82,29 @@ export const columns = (tableConfiguration: TableConfiguration) => [
78
82
  [receives]
79
83
  );
80
84
 
81
- if (receives?.length === 0 || !receives)
82
- return (
83
- <span className="inline-flex items-center px-2 py-1 text-xs text-[rgb(var(--ec-icon-color))] bg-[rgb(var(--ec-content-hover))] rounded-md border border-[rgb(var(--ec-page-border))]">
84
- No messages
85
- </span>
86
- );
85
+ if (receives?.length === 0 || !receives) return <span className="text-xs text-[rgb(var(--ec-icon-color))]">-</span>;
87
86
 
88
- const visibleItems = isExpanded ? receiversWithIcons : receiversWithIcons.slice(0, 4);
89
- const hiddenCount = receiversWithIcons.length - 4;
87
+ const visibleItems = isExpanded ? receiversWithIcons : receiversWithIcons.slice(0, 3);
88
+ const hiddenCount = receiversWithIcons.length - 3;
90
89
 
91
90
  return (
92
- <div className="flex flex-col gap-1.5">
91
+ <div className="flex flex-col gap-1">
93
92
  {visibleItems.map((consumer, index: number) => (
94
93
  <a
95
94
  key={`${consumer.data.id}-${index}`}
96
95
  href={buildUrl(`/docs/${consumer.collection}/${consumer.data.id}/${consumer.data.version}`)}
97
- className="group inline-flex items-center"
96
+ className="group inline-flex items-center gap-1.5 text-xs hover:text-[rgb(var(--ec-accent))] transition-colors"
98
97
  >
99
- <span
100
- className={`inline-flex items-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] hover:border-${consumer.color}-400 dark:hover:border-${consumer.color}-500 hover:bg-${consumer.color}-50 dark:hover:bg-${consumer.color}-500/10 transition-colors`}
101
- >
102
- <span className={`flex items-center justify-center w-6 h-6 bg-${consumer.color}-500 rounded-l-md`}>
103
- <consumer.Icon className="h-3 w-3 text-white" />
104
- </span>
105
- <span className="px-2 py-1 text-xs text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-page-text))]">
106
- {consumer.data.name}
107
- <span className="text-[rgb(var(--ec-icon-color))] ml-1">v{consumer.data.version}</span>
108
- </span>
98
+ <consumer.Icon className={`h-3.5 w-3.5 text-${consumer.color}-500 flex-shrink-0`} />
99
+ <span className="truncate max-w-[120px]" title={consumer.data.name}>
100
+ {consumer.data.name}
109
101
  </span>
110
102
  </a>
111
103
  ))}
112
104
  {hiddenCount > 0 && (
113
105
  <button
114
106
  onClick={() => setIsExpanded(!isExpanded)}
115
- className="text-xs text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-page-text))] text-left"
107
+ className="text-xs text-[rgb(var(--ec-accent))] hover:underline text-left"
116
108
  >
117
109
  {isExpanded ? 'Show less' : `+${hiddenCount} more`}
118
110
  </button>
@@ -124,7 +116,12 @@ export const columns = (tableConfiguration: TableConfiguration) => [
124
116
  }),
125
117
  columnHelper.accessor('data.sends', {
126
118
  id: 'sends',
127
- header: () => <span>Sends</span>,
119
+ header: () => (
120
+ <span className="flex items-center gap-1">
121
+ <ArrowUpIcon className="w-3.5 h-3.5" />
122
+ Sends
123
+ </span>
124
+ ),
128
125
  meta: {
129
126
  filterVariant: 'collection',
130
127
  collectionFilterKey: 'sends',
@@ -144,41 +141,29 @@ export const columns = (tableConfiguration: TableConfiguration) => [
144
141
  [sends]
145
142
  );
146
143
 
147
- if (sends?.length === 0 || !sends)
148
- return (
149
- <span className="inline-flex items-center px-2 py-1 text-xs text-[rgb(var(--ec-icon-color))] bg-[rgb(var(--ec-content-hover))] rounded-md border border-[rgb(var(--ec-page-border))]">
150
- No messages
151
- </span>
152
- );
144
+ if (sends?.length === 0 || !sends) return <span className="text-xs text-[rgb(var(--ec-icon-color))]">-</span>;
153
145
 
154
- const visibleItems = isExpanded ? sendersWithIcons : sendersWithIcons.slice(0, 4);
155
- const hiddenCount = sendersWithIcons.length - 4;
146
+ const visibleItems = isExpanded ? sendersWithIcons : sendersWithIcons.slice(0, 3);
147
+ const hiddenCount = sendersWithIcons.length - 3;
156
148
 
157
149
  return (
158
- <div className="flex flex-col gap-1.5">
150
+ <div className="flex flex-col gap-1">
159
151
  {visibleItems.map((sender, index) => (
160
152
  <a
161
153
  key={`${sender.data.id}-${index}`}
162
154
  href={buildUrl(`/docs/${sender.collection}/${sender.data.id}/${sender.data.version}`)}
163
- className="group inline-flex items-center"
155
+ className="group inline-flex items-center gap-1.5 text-xs hover:text-[rgb(var(--ec-accent))] transition-colors"
164
156
  >
165
- <span
166
- className={`inline-flex items-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] hover:border-${sender.color}-400 dark:hover:border-${sender.color}-500 hover:bg-${sender.color}-50 dark:hover:bg-${sender.color}-500/10 transition-colors`}
167
- >
168
- <span className={`flex items-center justify-center w-6 h-6 bg-${sender.color}-500 rounded-l-md`}>
169
- <sender.Icon className="h-3 w-3 text-white" />
170
- </span>
171
- <span className="px-2 py-1 text-xs text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-page-text))]">
172
- {sender.data.name}
173
- <span className="text-[rgb(var(--ec-icon-color))] ml-1">v{sender.data.version}</span>
174
- </span>
157
+ <sender.Icon className={`h-3.5 w-3.5 text-${sender.color}-500 flex-shrink-0`} />
158
+ <span className="truncate max-w-[120px]" title={sender.data.name}>
159
+ {sender.data.name}
175
160
  </span>
176
161
  </a>
177
162
  ))}
178
163
  {hiddenCount > 0 && (
179
164
  <button
180
165
  onClick={() => setIsExpanded(!isExpanded)}
181
- className="text-xs text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-page-text))] text-left"
166
+ className="text-xs text-[rgb(var(--ec-accent))] hover:underline text-left"
182
167
  >
183
168
  {isExpanded ? 'Show less' : `+${hiddenCount} more`}
184
169
  </button>
@@ -191,23 +176,28 @@ export const columns = (tableConfiguration: TableConfiguration) => [
191
176
  createBadgesColumn(columnHelper, tableConfiguration),
192
177
  columnHelper.accessor('data.name', {
193
178
  id: 'actions',
194
- header: () => <span>{tableConfiguration.columns?.actions?.label || 'Actions'}</span>,
179
+ header: () => <span></span>,
195
180
  cell: (info) => {
196
181
  const item = info.row.original;
182
+ const href = buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`);
183
+ const nodeKey = `${item.collection}-${item.data.id}-${item.data.version}`;
197
184
  return (
198
- <div className="flex items-center gap-2">
185
+ <div className="flex items-center gap-0.5">
199
186
  <a
200
- className="inline-flex items-center px-2.5 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))] rounded-md hover:bg-[rgb(var(--ec-content-hover))] hover:text-[rgb(var(--ec-page-text))] transition-colors whitespace-nowrap"
201
- href={buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`)}
187
+ className="p-1.5 text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-accent))] hover:bg-[rgb(var(--ec-accent)/0.1)] rounded-md transition-colors"
188
+ href={href}
189
+ title="View documentation"
202
190
  >
203
- Docs
191
+ <DocumentTextIcon className="w-4 h-4" />
204
192
  </a>
205
193
  <a
206
- className="inline-flex items-center px-2.5 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))] rounded-md hover:bg-[rgb(var(--ec-content-hover))] hover:text-[rgb(var(--ec-page-text))] transition-colors whitespace-nowrap"
194
+ className="p-1.5 text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-accent))] hover:bg-[rgb(var(--ec-accent)/0.1)] rounded-md transition-colors"
207
195
  href={buildUrl(`/visualiser/${item.collection}/${item.data.id}/${item.data.version}`)}
196
+ title="View in visualiser"
208
197
  >
209
- Visualiser
198
+ <MapIcon className="w-4 h-4" />
210
199
  </a>
200
+ <FavoriteButton nodeKey={nodeKey} title={item.data.name} badge="Service" href={href} size="sm" />
211
201
  </div>
212
202
  );
213
203
  },
@@ -1,9 +1,7 @@
1
1
  import { createColumnHelper } from '@tanstack/react-table';
2
- import { Tag } from 'lucide-react';
3
2
  import { useState } from 'react';
4
3
  import { filterByBadge } from '../filters/custom-filters';
5
4
  import type { TCollectionTypes, TData } from '../Table';
6
- import { getIcon } from '@utils/badges';
7
5
  import type { TableConfiguration } from '@types';
8
6
 
9
7
  export const createBadgesColumn = <T extends { data: Pick<TData<U>['data'], 'badges'> }, U extends TCollectionTypes>(
@@ -18,38 +16,25 @@ export const createBadgesColumn = <T extends { data: Pick<TData<U>['data'], 'bad
18
16
  const badges = item.data.badges || [];
19
17
  const [isExpanded, setIsExpanded] = useState(false);
20
18
 
21
- if (badges?.length === 0 || !badges)
22
- return (
23
- <span className="inline-flex items-center px-2 py-1 text-xs text-[rgb(var(--ec-icon-color))] bg-[rgb(var(--ec-content-hover))] rounded-md border border-[rgb(var(--ec-page-border))]">
24
- No badges
25
- </span>
26
- );
19
+ if (badges?.length === 0 || !badges) return <span className="text-xs text-[rgb(var(--ec-icon-color))]">-</span>;
27
20
 
28
- const visibleItems = isExpanded ? badges : badges.slice(0, 4);
29
- const hiddenCount = badges.length - 4;
21
+ const visibleItems = isExpanded ? badges : badges.slice(0, 3);
22
+ const hiddenCount = badges.length - 3;
30
23
 
31
24
  return (
32
- <div className="flex flex-wrap gap-1.5 items-center">
33
- {visibleItems.map((badge, index) => {
34
- const IconComponent = badge.icon ? getIcon(badge.icon) : null;
35
- return (
36
- <span
37
- key={`${badge.id}-${index}`}
38
- className="inline-flex items-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] hover:border-[rgb(var(--ec-accent))] hover:bg-[rgb(var(--ec-accent-subtle))] transition-colors"
39
- >
40
- <span className={`flex items-center justify-center w-6 h-6 bg-${badge.backgroundColor}-500 rounded-l-md`}>
41
- {IconComponent ? <IconComponent className="h-3 w-3 text-white" /> : <Tag className="h-3 w-3 text-white" />}
42
- </span>
43
- <span className="px-2 py-1 text-xs text-[rgb(var(--ec-page-text))]">{badge.content}</span>
44
- </span>
45
- );
46
- })}
47
- {hiddenCount > 0 && (
48
- <button
49
- onClick={() => setIsExpanded(!isExpanded)}
50
- className="text-xs text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-page-text))] px-2 py-1 rounded hover:bg-[rgb(var(--ec-content-hover))] transition-colors"
25
+ <div className="flex flex-wrap gap-1 items-center">
26
+ {visibleItems.map((badge, index) => (
27
+ <span
28
+ key={`${badge.id}-${index}`}
29
+ className="inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-md border border-[rgb(var(--ec-accent)/0.5)] text-[rgb(var(--ec-page-text))] bg-transparent"
30
+ title={badge.content}
51
31
  >
52
- {isExpanded ? 'Show less' : `+${hiddenCount} more`}
32
+ {badge.content}
33
+ </span>
34
+ ))}
35
+ {hiddenCount > 0 && (
36
+ <button onClick={() => setIsExpanded(!isExpanded)} className="text-xs text-[rgb(var(--ec-accent))] hover:underline">
37
+ {isExpanded ? 'less' : `+${hiddenCount}`}
53
38
  </button>
54
39
  )}
55
40
  </div>
@@ -131,7 +131,7 @@ const navigationItems = [
131
131
  id: '/discover',
132
132
  label: 'Explore',
133
133
  icon: TableProperties,
134
- href: buildUrl('/discover/events'),
134
+ href: buildUrl('/discover/domains'),
135
135
  current: currentPath.includes('/discover/'),
136
136
  },
137
137
  {
@@ -0,0 +1,22 @@
1
+ import type { APIRoute } from 'astro';
2
+ import { getNestedSideBarData } from '@stores/sidebar-store/state';
3
+
4
+ /**
5
+ * API route that returns the sidebar navigation data as JSON.
6
+ * This is pre-rendered in static mode to avoid embedding the data in every HTML page.
7
+ * The data is fetched once and cached by the browser.
8
+ */
9
+ export const GET: APIRoute = async () => {
10
+ const sidebarData = await getNestedSideBarData();
11
+
12
+ return new Response(JSON.stringify(sidebarData), {
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ // Cache for 1 hour in browser, allow CDN to cache and revalidate
16
+ 'Cache-Control': 'public, max-age=3600, s-maxage=3600, stale-while-revalidate=86400',
17
+ },
18
+ });
19
+ };
20
+
21
+ // Pre-render this route in static mode so it becomes a static JSON file
22
+ export const prerender = true;
@@ -10,13 +10,15 @@ export class Page extends HybridPage {
10
10
 
11
11
  static async getStaticPaths(): Promise<Array<{ params: any; props: any }>> {
12
12
  const { getFlows } = await import('@utils/collections/flows');
13
+ const { getServices } = await import('@utils/collections/services');
13
14
 
14
15
  const loaders = {
15
16
  ...pageDataLoader,
16
17
  flows: getFlows,
18
+ services: getServices,
17
19
  };
18
20
 
19
- const itemTypes = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'containers'] as const;
21
+ const itemTypes = ['events', 'commands', 'queries', 'domains', 'services', 'flows', 'containers'] as const;
20
22
  const allItems = await Promise.all(itemTypes.map((type) => loaders[type]()));
21
23
 
22
24
  return allItems.flatMap((items, index) => ({
@@ -38,10 +40,12 @@ export class Page extends HybridPage {
38
40
  }
39
41
 
40
42
  const { getFlows } = await import('@utils/collections/flows');
43
+ const { getServices } = await import('@utils/collections/services');
41
44
 
42
45
  const loaders = {
43
46
  ...pageDataLoader,
44
47
  flows: getFlows,
48
+ services: getServices,
45
49
  };
46
50
 
47
51
  // @ts-ignore