@eventcatalog/core 3.42.0 → 3.43.1

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 (52) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/count-resources.cjs +1 -0
  4. package/dist/analytics/count-resources.js +1 -1
  5. package/dist/analytics/log-build.cjs +3 -1
  6. package/dist/analytics/log-build.js +4 -4
  7. package/dist/{chunk-KE6YTTLB.js → chunk-2GQO7I7E.js} +1 -1
  8. package/dist/{chunk-6FAGUEM4.js → chunk-C6S5P57F.js} +1 -1
  9. package/dist/{chunk-3DVHEVHQ.js → chunk-DAOXTQVS.js} +1 -0
  10. package/dist/{chunk-VPZ77Y6E.js → chunk-KV5FCOV4.js} +1 -1
  11. package/dist/{chunk-L66TCSM7.js → chunk-OOX6HAE4.js} +3 -2
  12. package/dist/{chunk-UQIDXF2V.js → chunk-Z5QHV4ZY.js} +1 -1
  13. package/dist/{chunk-QMORF42U.js → chunk-ZONBICNH.js} +8 -0
  14. package/dist/constants.cjs +1 -1
  15. package/dist/constants.js +1 -1
  16. package/dist/eventcatalog.cjs +11 -1
  17. package/dist/eventcatalog.js +7 -7
  18. package/dist/generate.cjs +1 -1
  19. package/dist/generate.js +3 -3
  20. package/dist/search-indexer.cjs +8 -0
  21. package/dist/search-indexer.js +1 -1
  22. package/dist/utils/cli-logger.cjs +1 -1
  23. package/dist/utils/cli-logger.js +2 -2
  24. package/eventcatalog/src/components/MDX/Attachments.astro +3 -3
  25. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +11 -2
  26. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +100 -2
  27. package/eventcatalog/src/components/Tables/Discover/columns.tsx +53 -1
  28. package/eventcatalog/src/content.config.ts +61 -0
  29. package/eventcatalog/src/enterprise/collections/resource-docs-utils.ts +19 -0
  30. package/eventcatalog/src/layouts/DiscoverLayout.astro +12 -1
  31. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +98 -46
  32. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -0
  33. package/eventcatalog/src/pages/discover/[type]/index.astro +17 -0
  34. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -0
  35. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +68 -2
  36. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +1 -0
  37. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +26 -1
  38. package/eventcatalog/src/pages/docs/users/[id]/index.astro +26 -1
  39. package/eventcatalog/src/stores/sidebar-store/builders/adr.ts +150 -0
  40. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +2 -0
  41. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +50 -0
  42. package/eventcatalog/src/stores/sidebar-store/state.ts +209 -68
  43. package/eventcatalog/src/types/index.ts +2 -0
  44. package/eventcatalog/src/utils/collection-colors.ts +2 -0
  45. package/eventcatalog/src/utils/collections/adr-constants.ts +53 -0
  46. package/eventcatalog/src/utils/collections/adrs.ts +146 -0
  47. package/eventcatalog/src/utils/collections/icons.ts +2 -0
  48. package/eventcatalog/src/utils/collections/teams.ts +6 -1
  49. package/eventcatalog/src/utils/collections/users.ts +17 -10
  50. package/eventcatalog/src/utils/collections/util.ts +2 -0
  51. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  52. package/package.json +4 -4
@@ -17,6 +17,7 @@ import { useStore } from '@nanostores/react';
17
17
  import { favoritesStore, toggleFavorite, type FavoriteItem } from '../../../stores/favorites-store';
18
18
  import type { DiscoverTableData, CollectionType } from './DiscoverTable';
19
19
  import type { TableConfiguration } from '@types';
20
+ import { formatAdrDate, isAdrCollection } from '@utils/collections/adr-constants';
20
21
 
21
22
  const columnHelper = createColumnHelper<DiscoverTableData>();
22
23
 
@@ -107,6 +108,7 @@ const createBadgesColumn = (tableConfiguration: TableConfiguration) =>
107
108
  columnHelper.accessor((row) => row.data.badges, {
108
109
  id: 'badges',
109
110
  header: () => <span>{tableConfiguration?.columns?.badges?.label || 'Badges'}</span>,
111
+ enableSorting: false,
110
112
  cell: (info) => <BadgesCell badges={info.getValue() || []} />,
111
113
  meta: {
112
114
  showFilter: false,
@@ -143,7 +145,7 @@ const RowActionsMenu = ({ item, collectionType }: { item: DiscoverTableData; col
143
145
  const favorites = useStore(favoritesStore);
144
146
  const href = buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`);
145
147
  const visualiserHref = buildUrl(`/visualiser/${item.collection}/${item.data.id}/${item.data.version}`);
146
- const hasVisualiser = true;
148
+ const hasVisualiser = !isAdrCollection(item.collection);
147
149
  const nodeKey = `${item.collection}-${item.data.id}-${item.data.version}`;
148
150
  const badgeLabel =
149
151
  collectionType === 'external-systems'
@@ -234,6 +236,7 @@ const createActionsColumn = (collectionType: CollectionType, tableConfiguration:
234
236
  columnHelper.accessor('data.name', {
235
237
  id: 'actions',
236
238
  header: () => <span></span>,
239
+ enableSorting: false,
237
240
  cell: (info) => {
238
241
  const item = info.row.original;
239
242
  return <RowActionsMenu item={item} collectionType={collectionType} />;
@@ -392,6 +395,53 @@ export const getAgentColumns = (tableConfiguration: TableConfiguration) => [
392
395
  createActionsColumn('agents', tableConfiguration),
393
396
  ];
394
397
 
398
+ // ============================================================================
399
+ // ADR COLUMNS
400
+ // ============================================================================
401
+ export const getAdrColumns = (tableConfiguration: TableConfiguration) => [
402
+ columnHelper.accessor('data.name', {
403
+ id: 'name',
404
+ header: () => <span>{tableConfiguration?.columns?.name?.label || 'Decision record'}</span>,
405
+ cell: (info) => <ResourceNameCell item={info.row.original} />,
406
+ meta: {
407
+ filterVariant: 'name',
408
+ },
409
+ }),
410
+ createSummaryColumn(tableConfiguration),
411
+ columnHelper.accessor('data.statusBadge', {
412
+ id: 'status',
413
+ header: () => <span>{tableConfiguration?.columns?.status?.label || 'Status'}</span>,
414
+ enableSorting: false,
415
+ cell: (info) => {
416
+ const badge = info.getValue();
417
+ if (!badge) return <span className="text-xs text-[rgb(var(--ec-icon-color))]">-</span>;
418
+ return <BadgesCell badges={[badge]} />;
419
+ },
420
+ meta: {
421
+ showFilter: false,
422
+ },
423
+ }),
424
+ columnHelper.accessor('data.date', {
425
+ id: 'date',
426
+ header: () => <span>{tableConfiguration?.columns?.date?.label || 'Date'}</span>,
427
+ sortingFn: (rowA, rowB) => {
428
+ const left = rowA.original.data.date ? new Date(rowA.original.data.date).getTime() : 0;
429
+ const right = rowB.original.data.date ? new Date(rowB.original.data.date).getTime() : 0;
430
+ return left - right;
431
+ },
432
+ cell: (info) => {
433
+ const date = info.getValue();
434
+ if (!date) return <span className="text-xs text-[rgb(var(--ec-icon-color))]">-</span>;
435
+ return <span className="text-[0.8rem] text-[rgb(var(--ec-page-text))]">{formatAdrDate(new Date(date))}</span>;
436
+ },
437
+ meta: {
438
+ showFilter: false,
439
+ },
440
+ }),
441
+ createBadgesColumn(tableConfiguration),
442
+ createActionsColumn('adrs', tableConfiguration),
443
+ ];
444
+
395
445
  // ============================================================================
396
446
  // EVENT COLUMNS
397
447
  // ============================================================================
@@ -677,6 +727,8 @@ export const getDiscoverColumns = (collectionType: CollectionType, tableConfigur
677
727
  switch (collectionType) {
678
728
  case 'agents':
679
729
  return getAgentColumns(tableConfiguration);
730
+ case 'adrs':
731
+ return getAdrColumns(tableConfiguration);
680
732
  case 'events':
681
733
  return getEventColumns(tableConfiguration);
682
734
  case 'commands':
@@ -4,6 +4,7 @@ import { glob } from 'astro/loaders';
4
4
  import { glob as globPackage } from 'glob';
5
5
  import { v4 as uuidv4 } from 'uuid';
6
6
  import { badge, ownerReference } from './content.config-shared-collections';
7
+ import { ADR_STATUS_VALUES } from './utils/collections/adr-constants';
7
8
  import fs from 'fs';
8
9
  import path from 'path';
9
10
 
@@ -550,6 +551,65 @@ const agents = defineCollection({
550
551
  .extend(baseSchema.shape),
551
552
  });
552
553
 
554
+ const adrStatus = z.enum(ADR_STATUS_VALUES);
555
+
556
+ const adrPointer = z.object({
557
+ id: z.string(),
558
+ version: z.string().optional().default('latest'),
559
+ });
560
+
561
+ const adrResourcePointer = adrPointer.extend({
562
+ type: z.enum([
563
+ 'agent',
564
+ 'service',
565
+ 'event',
566
+ 'command',
567
+ 'query',
568
+ 'flow',
569
+ 'channel',
570
+ 'domain',
571
+ 'user',
572
+ 'team',
573
+ 'container',
574
+ 'entity',
575
+ 'diagram',
576
+ 'data-product',
577
+ ]),
578
+ });
579
+
580
+ const adrs = defineCollection({
581
+ loader: glob({
582
+ pattern: withIgnoredBuildArtifacts(['**/adrs/*/index.(md|mdx)', '**/adrs/*/versioned/*/index.(md|mdx)']),
583
+ base: projectDirBase,
584
+ generateId: ({ data }) => `${data.id}-${data.version}`,
585
+ }),
586
+ schema: z
587
+ .object({
588
+ status: adrStatus,
589
+ date: z.coerce.date(),
590
+ decisionMakers: z.array(ownerReference).optional(),
591
+ appliesTo: z.array(adrResourcePointer).optional(),
592
+ supersedes: z.array(adrPointer).optional(),
593
+ supersededBy: z.array(adrPointer).optional(),
594
+ amends: z.array(adrPointer).optional(),
595
+ amendedBy: z.array(adrPointer).optional(),
596
+ related: z.array(adrPointer).optional(),
597
+ detailsPanel: z
598
+ .object({
599
+ status: detailPanelPropertySchema.optional(),
600
+ date: detailPanelPropertySchema.optional(),
601
+ decisionMakers: detailPanelPropertySchema.optional(),
602
+ appliesTo: detailPanelPropertySchema.optional(),
603
+ relationships: detailPanelPropertySchema.optional(),
604
+ owners: detailPanelPropertySchema.optional(),
605
+ repository: detailPanelPropertySchema.optional(),
606
+ changelog: detailPanelPropertySchema.optional(),
607
+ })
608
+ .optional(),
609
+ })
610
+ .extend(baseSchema.shape),
611
+ });
612
+
553
613
  // 1) Put this near your other enums/utilities
554
614
  const containerTypeEnum = z.enum([
555
615
  // Core
@@ -915,6 +975,7 @@ export const collections = {
915
975
  queries,
916
976
  services,
917
977
  agents,
978
+ adrs,
918
979
  channels,
919
980
  users,
920
981
  teams,
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { getCollection, type CollectionEntry } from 'astro:content';
7
+ import fs from 'node:fs';
7
8
  import path from 'node:path';
8
9
  import { coerce, rcompare } from 'semver';
9
10
  import { sortVersioned } from '../../utils/collections/util';
@@ -77,6 +78,16 @@ let memoryResourceLookupPromise: Promise<Record<ResourceCollection, ResourceLook
77
78
  const normalizePath = (value: string) => value.replace(/\\/g, '/').replace(/^\.\//, '');
78
79
  const normalizeTypeName = (value: string) => value.trim().toLowerCase();
79
80
 
81
+ const isMissingGeneratedContentFile = (filePath: string) => {
82
+ const normalizedPath = normalizePath(filePath);
83
+
84
+ if (!path.isAbsolute(filePath) && !normalizedPath.startsWith('../')) {
85
+ return false;
86
+ }
87
+
88
+ return !fs.existsSync(filePath);
89
+ };
90
+
80
91
  const inferOrderFromFilePath = (filePath: string): number | undefined => {
81
92
  const normalizedPath = normalizePath(filePath);
82
93
  const fileName = normalizedPath.split('/').pop();
@@ -373,6 +384,10 @@ export const getResourceDocs = async (): Promise<ResourceDocEntry[]> => {
373
384
  return null;
374
385
  }
375
386
 
387
+ if (isMissingGeneratedContentFile(doc.filePath)) {
388
+ return null;
389
+ }
390
+
376
391
  const resolvedResource = resolveResourceFromPath(doc.filePath, lookups);
377
392
  if (!resolvedResource) {
378
393
  return null;
@@ -483,6 +498,10 @@ export const getResourceDocCategories = async (): Promise<ResourceDocCategoryEnt
483
498
  continue;
484
499
  }
485
500
 
501
+ if (isMissingGeneratedContentFile(category.filePath)) {
502
+ continue;
503
+ }
504
+
486
505
  const resolvedResource = resolveResourceFromPath(category.filePath, lookups);
487
506
  if (!resolvedResource) {
488
507
  continue;
@@ -10,7 +10,8 @@ import { getServices } from '@utils/collections/services';
10
10
  import { buildUrl } from '@utils/url-builder';
11
11
  import { getQueries } from '@utils/collections/queries';
12
12
  import { getContainers } from '@utils/collections/containers';
13
- import { DatabaseIcon } from 'lucide-react';
13
+ import { BookText, DatabaseIcon } from 'lucide-react';
14
+ import { getAdrs } from '@utils/collections/adrs';
14
15
  import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
15
16
  import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
16
17
  import Checkbox from '@components/Checkbox.astro';
@@ -23,6 +24,7 @@ const services = await getServices();
23
24
  const domains = await getDomains();
24
25
  const flows = await getFlows();
25
26
  const containers = await getContainers();
27
+ const adrs = await getAdrs();
26
28
  export interface Props<T extends TCollectionTypes> {
27
29
  title: string;
28
30
  subtitle: string;
@@ -85,6 +87,15 @@ const tabs = [
85
87
  enabled: domains.length > 0,
86
88
  visible: domains.length > 0,
87
89
  },
90
+ {
91
+ label: `Decision Records (${adrs.length})`,
92
+ href: buildUrl('/discover/adrs'),
93
+ isActive: currentPath === '/discover/adrs',
94
+ icon: BookText,
95
+ activeColor: 'purple',
96
+ enabled: adrs.length > 0,
97
+ visible: adrs.length > 0,
98
+ },
88
99
  {
89
100
  label: `Data (${containers.length})`,
90
101
  href: buildUrl('/discover/containers'),
@@ -11,6 +11,7 @@ import {
11
11
  TableProperties,
12
12
  BotMessageSquare,
13
13
  BookOpen,
14
+ BookText,
14
15
  FileText,
15
16
  SquareDashedMousePointerIcon,
16
17
  FileCode,
@@ -61,15 +62,16 @@ import { getEvents } from '@utils/collections/events';
61
62
  import { getServices } from '@utils/collections/services';
62
63
  import { getAgents } from '@utils/collections/agents';
63
64
  import { getFlows } from '@utils/collections/flows';
65
+ import { getAdrs } from '@utils/collections/adrs';
66
+ import { getContainers } from '@utils/collections/containers';
67
+ import { getDataProducts } from '@utils/collections/data-products';
64
68
  import { isCollectionVisibleInCatalog } from '@eventcatalog';
65
69
  import { buildUrl } from '@utils/url-builder';
66
70
  import { getQueries } from '@utils/collections/queries';
67
- import { hasLandingPageForDocs } from '@utils/pages';
68
71
  import { filterSidebarItems } from '@utils/sidebar-visibility';
69
72
 
70
73
  import { isEmbedEnabled, isCustomStylesEnabled, isEventCatalogScaleEnabled, isCustomDocsEnabled, isSSR } from '@utils/feature';
71
74
 
72
- const catalogHasDefaultLandingPageForDocs = await hasLandingPageForDocs();
73
75
  const customDocs = await getCollection('customPages');
74
76
 
75
77
  let events: any[] = [];
@@ -77,20 +79,24 @@ let commands: any[] = [];
77
79
  let queries: any[] = [];
78
80
  let services: any[] = [];
79
81
  let agents: any[] = [];
82
+ let adrs: any[] = [];
80
83
  let domains: any[] = [];
81
84
  let flows: any[] = [];
82
-
83
- if (!catalogHasDefaultLandingPageForDocs) {
84
- [events, commands, queries, services, agents, domains, flows] = await Promise.all([
85
- getEvents({ getAllVersions: false, hydrateServices: false }),
86
- getCommands({ getAllVersions: false, hydrateServices: false }),
87
- getQueries({ getAllVersions: false, hydrateServices: false }),
88
- getServices({ getAllVersions: false }),
89
- getAgents({ getAllVersions: false }),
90
- getDomains({ getAllVersions: false }),
91
- getFlows({ getAllVersions: false }),
92
- ]);
93
- }
85
+ let containers: any[] = [];
86
+ let dataProducts: any[] = [];
87
+
88
+ [events, commands, queries, services, agents, adrs, domains, flows, containers, dataProducts] = await Promise.all([
89
+ getEvents({ getAllVersions: false, hydrateServices: false }),
90
+ getCommands({ getAllVersions: false, hydrateServices: false }),
91
+ getQueries({ getAllVersions: false, hydrateServices: false }),
92
+ getServices({ getAllVersions: false }),
93
+ getAgents({ getAllVersions: false }),
94
+ getAdrs({ getAllVersions: false }),
95
+ getDomains({ getAllVersions: false }),
96
+ getFlows({ getAllVersions: false }),
97
+ getContainers({ getAllVersions: false }),
98
+ getDataProducts({ getAllVersions: false }),
99
+ ]);
94
100
 
95
101
  // Try and load any custom styles if they exist
96
102
  if (isCustomStylesEnabled()) {
@@ -203,8 +209,47 @@ const premiumFeatures: Array<{
203
209
  isPremium?: boolean;
204
210
  }> = [];
205
211
 
212
+ const internalServices = services.filter((service) => !service.data.externalSystem);
213
+ const externalSystems = services.filter((service) => service.data.externalSystem);
214
+
206
215
  const browseItems = filterSidebarItems(
207
216
  [
217
+ {
218
+ id: '/discover/agents',
219
+ aliases: ['/discover'],
220
+ label: 'Agents',
221
+ icon: BotMessageSquare,
222
+ href: buildUrl('/discover/agents'),
223
+ current: currentPath === buildUrl('/discover/agents'),
224
+ visible: agents.length > 0,
225
+ },
226
+ {
227
+ id: '/discover/adrs',
228
+ aliases: ['/discover'],
229
+ label: 'Decision Records',
230
+ icon: BookText,
231
+ href: buildUrl('/discover/adrs'),
232
+ current: currentPath === buildUrl('/discover/adrs'),
233
+ visible: adrs.length > 0,
234
+ },
235
+ {
236
+ id: '/discover/data-products',
237
+ aliases: ['/discover'],
238
+ label: 'Data Products',
239
+ icon: CubeIcon,
240
+ href: buildUrl('/discover/data-products'),
241
+ current: currentPath === buildUrl('/discover/data-products'),
242
+ visible: dataProducts.length > 0,
243
+ },
244
+ {
245
+ id: '/discover/containers',
246
+ aliases: ['/discover'],
247
+ label: 'Data Stores',
248
+ icon: Database,
249
+ href: buildUrl('/discover/containers'),
250
+ current: currentPath === buildUrl('/discover/containers'),
251
+ visible: containers.length > 0,
252
+ },
208
253
  {
209
254
  id: '/discover/domains',
210
255
  aliases: ['/discover'],
@@ -212,6 +257,7 @@ const browseItems = filterSidebarItems(
212
257
  icon: RectangleGroupIcon,
213
258
  href: buildUrl('/discover/domains'),
214
259
  current: currentPath === buildUrl('/discover/domains'),
260
+ visible: domains.length > 0,
215
261
  },
216
262
  {
217
263
  id: '/discover/services',
@@ -220,6 +266,7 @@ const browseItems = filterSidebarItems(
220
266
  icon: ServerIcon,
221
267
  href: buildUrl('/discover/services'),
222
268
  current: currentPath === buildUrl('/discover/services'),
269
+ visible: internalServices.length > 0,
223
270
  },
224
271
  {
225
272
  id: '/discover/external-systems',
@@ -228,6 +275,7 @@ const browseItems = filterSidebarItems(
228
275
  icon: GlobeAltIcon,
229
276
  href: buildUrl('/discover/external-systems'),
230
277
  current: currentPath === buildUrl('/discover/external-systems'),
278
+ visible: externalSystems.length > 0,
231
279
  },
232
280
  {
233
281
  id: '/discover/events',
@@ -236,6 +284,7 @@ const browseItems = filterSidebarItems(
236
284
  icon: BoltIcon,
237
285
  href: buildUrl('/discover/events'),
238
286
  current: currentPath === buildUrl('/discover/events'),
287
+ visible: events.length > 0,
239
288
  },
240
289
  {
241
290
  id: '/discover/commands',
@@ -244,6 +293,7 @@ const browseItems = filterSidebarItems(
244
293
  icon: ChatBubbleLeftIcon,
245
294
  href: buildUrl('/discover/commands'),
246
295
  current: currentPath === buildUrl('/discover/commands'),
296
+ visible: commands.length > 0,
247
297
  },
248
298
  {
249
299
  id: '/discover/queries',
@@ -252,6 +302,7 @@ const browseItems = filterSidebarItems(
252
302
  icon: MagnifyingGlassIcon,
253
303
  href: buildUrl('/discover/queries'),
254
304
  current: currentPath === buildUrl('/discover/queries'),
305
+ visible: queries.length > 0,
255
306
  },
256
307
  {
257
308
  id: '/discover/flows',
@@ -260,34 +311,13 @@ const browseItems = filterSidebarItems(
260
311
  icon: QueueListIcon,
261
312
  href: buildUrl('/discover/flows'),
262
313
  current: currentPath === buildUrl('/discover/flows'),
263
- },
264
- {
265
- id: '/discover/containers',
266
- aliases: ['/discover'],
267
- label: 'Data Stores',
268
- icon: Database,
269
- href: buildUrl('/discover/containers'),
270
- current: currentPath === buildUrl('/discover/containers'),
271
- },
272
- {
273
- id: '/discover/data-products',
274
- aliases: ['/discover'],
275
- label: 'Data Products',
276
- icon: CubeIcon,
277
- href: buildUrl('/discover/data-products'),
278
- current: currentPath === buildUrl('/discover/data-products'),
279
- },
280
- {
281
- id: '/discover/agents',
282
- aliases: ['/discover'],
283
- label: 'Agents',
284
- icon: BotMessageSquare,
285
- href: buildUrl('/discover/agents'),
286
- current: currentPath === buildUrl('/discover/agents'),
314
+ visible: flows.length > 0,
287
315
  },
288
316
  ],
289
317
  userSideBarConfiguration
290
- );
318
+ )
319
+ .filter((item) => item.visible !== false)
320
+ .sort((a, b) => a.label.localeCompare(b.label));
291
321
 
292
322
  const organizationItems = filterSidebarItems(
293
323
  [
@@ -329,15 +359,25 @@ const currentNavigationItem = [...navigationItems, ...studioNavigationItem, ...p
329
359
  const { title, description, showNestedSideBar = true, showHeader = true } = Astro.props;
330
360
 
331
361
  const canPageBeEmbedded = isEmbedEnabled();
362
+ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/docs', true)];
332
363
  ---
333
364
 
334
365
  <BaseLayout title={`EventCatalog | ${title}`} description={description} ogTitle={title}>
335
366
  <Fragment slot="head">
336
- <script is:inline>
367
+ <script is:inline define:vars={{ verticalNavAutoCollapsePaths }}>
337
368
  (() => {
369
+ const verticalNavStorageKey = 'eventcatalog-vertical-nav-collapsed';
370
+ const routeShouldAutoCollapseVerticalNav = (pathname) =>
371
+ verticalNavAutoCollapsePaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));
372
+
338
373
  try {
339
- const savedState = localStorage.getItem('eventcatalog-vertical-nav-collapsed');
340
- const isCollapsed = savedState === null ? true : savedState === 'true';
374
+ const shouldAutoCollapse = routeShouldAutoCollapseVerticalNav(window.location.pathname);
375
+ if (shouldAutoCollapse) {
376
+ localStorage.setItem(verticalNavStorageKey, 'true');
377
+ }
378
+
379
+ const savedState = localStorage.getItem(verticalNavStorageKey);
380
+ const isCollapsed = shouldAutoCollapse ? true : savedState === null ? true : savedState === 'true';
341
381
  document.documentElement.setAttribute('data-vertical-nav-collapsed', isCollapsed ? 'true' : 'false');
342
382
  } catch (error) {
343
383
  document.documentElement.setAttribute('data-vertical-nav-collapsed', 'true');
@@ -737,6 +777,7 @@ const canPageBeEmbedded = isEmbedEnabled();
737
777
  navigationItems,
738
778
  currentNavigationItem,
739
779
  canPageBeEmbedded,
780
+ verticalNavAutoCollapsePaths,
740
781
  }}
741
782
  >
742
783
  const VERTICAL_NAV_STORAGE_KEY = 'eventcatalog-vertical-nav-collapsed';
@@ -817,7 +858,21 @@ const canPageBeEmbedded = isEmbedEnabled();
817
858
  });
818
859
  };
819
860
 
861
+ const routeShouldAutoCollapseVerticalNav = (pathname) =>
862
+ verticalNavAutoCollapsePaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));
863
+
864
+ const persistVerticalNavCollapsedState = (collapsed) => {
865
+ try {
866
+ localStorage.setItem(VERTICAL_NAV_STORAGE_KEY, String(collapsed));
867
+ } catch (error) {}
868
+ };
869
+
820
870
  const getPersistedVerticalNavCollapsedState = () => {
871
+ if (routeShouldAutoCollapseVerticalNav(window.location.pathname)) {
872
+ persistVerticalNavCollapsedState(true);
873
+ return true;
874
+ }
875
+
821
876
  try {
822
877
  const savedState = localStorage.getItem(VERTICAL_NAV_STORAGE_KEY);
823
878
  return savedState === null ? true : savedState === 'true';
@@ -948,10 +1003,7 @@ const canPageBeEmbedded = isEmbedEnabled();
948
1003
  const nextState = !isCollapsed;
949
1004
 
950
1005
  setVerticalNavCollapsedState(nextState);
951
-
952
- try {
953
- localStorage.setItem(VERTICAL_NAV_STORAGE_KEY, String(nextState));
954
- } catch (error) {}
1006
+ persistVerticalNavCollapsedState(nextState);
955
1007
  };
956
1008
  }
957
1009
  });
@@ -11,6 +11,7 @@ export class Page extends HybridPage {
11
11
  static async getStaticPaths(): Promise<Array<{ params: any; props: any }>> {
12
12
  const { getFlows } = await import('@utils/collections/flows');
13
13
  const { getAgents } = await import('@utils/collections/agents');
14
+ const { getAdrs } = await import('@utils/collections/adrs');
14
15
  const { getServices } = await import('@utils/collections/services');
15
16
  const { getDataProducts } = await import('@utils/collections/data-products');
16
17
 
@@ -20,6 +21,7 @@ export class Page extends HybridPage {
20
21
  const loaders = {
21
22
  ...pageDataLoader,
22
23
  agents: getAgents,
24
+ adrs: getAdrs,
23
25
  flows: getFlows,
24
26
  services: getInternalServices,
25
27
  'external-systems': getExternalServices,
@@ -29,6 +31,7 @@ export class Page extends HybridPage {
29
31
  const itemTypes = [
30
32
  'events',
31
33
  'agents',
34
+ 'adrs',
32
35
  'commands',
33
36
  'queries',
34
37
  'domains',
@@ -60,6 +63,7 @@ export class Page extends HybridPage {
60
63
 
61
64
  const { getFlows } = await import('@utils/collections/flows');
62
65
  const { getAgents } = await import('@utils/collections/agents');
66
+ const { getAdrs } = await import('@utils/collections/adrs');
63
67
  const { getServices } = await import('@utils/collections/services');
64
68
  const { getDataProducts } = await import('@utils/collections/data-products');
65
69
 
@@ -69,6 +73,7 @@ export class Page extends HybridPage {
69
73
  const loaders = {
70
74
  ...pageDataLoader,
71
75
  agents: getAgents,
76
+ adrs: getAdrs,
72
77
  flows: getFlows,
73
78
  services: getInternalServices,
74
79
  'external-systems': getExternalServices,
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { getCommands } from '@utils/collections/commands';
3
3
  import { getAgents } from '@utils/collections/agents';
4
+ import { createAdrStatusBadge, getAdrs } from '@utils/collections/adrs';
4
5
  import { getDomains, getDomainsForAgent, getDomainsForService } from '@utils/collections/domains';
5
6
  import { getEvents } from '@utils/collections/events';
6
7
  import { getServices } from '@utils/collections/services';
@@ -24,6 +25,7 @@ const events = await getEvents();
24
25
  const queries = await getQueries();
25
26
  const commands = await getCommands();
26
27
  const agents = await getAgents();
28
+ const adrs = await getAdrs();
27
29
  const services = await getServices();
28
30
  const domains = await getDomains({ getAllVersions: false });
29
31
  const containers = await getContainers();
@@ -108,6 +110,15 @@ const typeConfig: Record<
108
110
  { id: 'isDeprecated', label: 'Is Deprecated' },
109
111
  ],
110
112
  },
113
+ adrs: {
114
+ label: 'Decision Records',
115
+ description: 'Browse the decision records across your catalog and inspect their status and scope.',
116
+ propertyOptions: [
117
+ { id: 'hasOwners', label: 'Has Owners' },
118
+ { id: 'hasAppliesTo', label: 'Applies To Resources' },
119
+ { id: 'hasDecisionMakers', label: 'Has Decision Makers' },
120
+ ],
121
+ },
111
122
  'external-systems': {
112
123
  label: 'External Systems',
113
124
  description: 'Explore the external systems connected to your architecture and the contracts around them.',
@@ -200,6 +211,7 @@ function hasSpecifications(service: any): boolean {
200
211
  // Build lookup maps for all collections (for resolving data product inputs/outputs)
201
212
  const allCollections = [
202
213
  ...agents.map((a) => ({ ...a, collection: 'agents' })),
214
+ ...adrs.map((adr) => ({ ...adr, collection: 'adrs' })),
203
215
  ...services.map((s) => ({ ...s, collection: 'services' })),
204
216
  ...containers.map((c) => ({ ...c, collection: 'containers' })),
205
217
  ...channels.map((c) => ({ ...c, collection: 'channels' })),
@@ -266,6 +278,8 @@ const tableData = enrichedData.map((d: any) => ({
266
278
  hasDataDependencies: isServiceOrAgentLike ? (d.data?.writesTo || []).length > 0 || (d.data?.readsFrom || []).length > 0 : false,
267
279
  hasModel: type === 'agents' ? !!d.data?.model : false,
268
280
  hasTools: type === 'agents' ? (d.data?.tools || []).length > 0 : false,
281
+ hasAppliesTo: type === 'adrs' ? (d.data?.appliesTo || []).length > 0 : false,
282
+ hasDecisionMakers: type === 'adrs' ? (d.data?.decisionMakers || []).length > 0 : false,
269
283
  // Data-product-specific properties
270
284
  hasInputs: type === 'data-products' ? (d.data?.inputs || []).length > 0 : false,
271
285
  hasOutputs: type === 'data-products' ? (d.data?.outputs || []).length > 0 : false,
@@ -279,6 +293,9 @@ const tableData = enrichedData.map((d: any) => ({
279
293
  latestVersion: d.data?.latestVersion,
280
294
  draft: d.data?.draft,
281
295
  badges: d.data?.badges,
296
+ status: d.data?.status,
297
+ date: d.data?.date,
298
+ statusBadge: d.data?.status ? createAdrStatusBadge(d.data.status) : undefined,
282
299
  producers: d.data?.producers?.map(mapToItem) ?? [],
283
300
  consumers: d.data?.consumers?.map(mapToItem) ?? [],
284
301
  receives: d.data?.receives?.map(mapToItem) ?? [],
@@ -14,6 +14,7 @@ export class Page extends HybridPage {
14
14
 
15
15
  const itemTypes: PageTypes[] = [
16
16
  'agents',
17
+ 'adrs',
17
18
  'events',
18
19
  'commands',
19
20
  'queries',