@eventcatalog/core 3.41.4 → 3.43.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 (55) 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-VQLDZRHC.js → chunk-2EI3M7OO.js} +1 -1
  8. package/dist/{chunk-COPXPOV2.js → chunk-7M5IQL3J.js} +1 -1
  9. package/dist/{chunk-3DVHEVHQ.js → chunk-DAOXTQVS.js} +1 -0
  10. package/dist/{chunk-OH2U6UEJ.js → chunk-KY74BE42.js} +1 -1
  11. package/dist/{chunk-LYRAK5LI.js → chunk-QV2PKXZM.js} +3 -2
  12. package/dist/{chunk-QMORF42U.js → chunk-ZONBICNH.js} +8 -0
  13. package/dist/{chunk-YWG7CCN7.js → chunk-ZQHBDPIY.js} +1 -1
  14. package/dist/constants.cjs +1 -1
  15. package/dist/constants.js +1 -1
  16. package/dist/docs/development/developer-tools/api-catalog.md +114 -0
  17. package/dist/eventcatalog.cjs +11 -1
  18. package/dist/eventcatalog.js +7 -7
  19. package/dist/generate.cjs +1 -1
  20. package/dist/generate.js +3 -3
  21. package/dist/search-indexer.cjs +8 -0
  22. package/dist/search-indexer.js +1 -1
  23. package/dist/utils/cli-logger.cjs +1 -1
  24. package/dist/utils/cli-logger.js +2 -2
  25. package/eventcatalog/src/components/MDX/Attachments.astro +3 -3
  26. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +11 -2
  27. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +100 -2
  28. package/eventcatalog/src/components/Tables/Discover/columns.tsx +53 -1
  29. package/eventcatalog/src/content.config.ts +61 -0
  30. package/eventcatalog/src/enterprise/collections/resource-docs-utils.ts +19 -0
  31. package/eventcatalog/src/layouts/DiscoverLayout.astro +12 -1
  32. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +98 -46
  33. package/eventcatalog/src/pages/.well-known/api-catalog.ts +191 -0
  34. package/eventcatalog/src/pages/api-catalog/specifications/[collection]/[id]/[version]/[specification].ts +109 -0
  35. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -0
  36. package/eventcatalog/src/pages/discover/[type]/index.astro +17 -0
  37. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -0
  38. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +68 -2
  39. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +1 -0
  40. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +26 -1
  41. package/eventcatalog/src/pages/docs/users/[id]/index.astro +26 -1
  42. package/eventcatalog/src/stores/sidebar-store/builders/adr.ts +150 -0
  43. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +2 -0
  44. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +50 -0
  45. package/eventcatalog/src/stores/sidebar-store/state.ts +209 -68
  46. package/eventcatalog/src/types/index.ts +2 -0
  47. package/eventcatalog/src/utils/collection-colors.ts +2 -0
  48. package/eventcatalog/src/utils/collections/adr-constants.ts +53 -0
  49. package/eventcatalog/src/utils/collections/adrs.ts +146 -0
  50. package/eventcatalog/src/utils/collections/icons.ts +2 -0
  51. package/eventcatalog/src/utils/collections/teams.ts +6 -1
  52. package/eventcatalog/src/utils/collections/users.ts +17 -10
  53. package/eventcatalog/src/utils/collections/util.ts +2 -0
  54. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  55. package/package.json +1 -1
@@ -6,10 +6,12 @@ import {
6
6
  getFacetedUniqueValues,
7
7
  getFilteredRowModel,
8
8
  getPaginationRowModel,
9
+ getSortedRowModel,
9
10
  useReactTable,
10
11
  type ColumnFiltersState,
12
+ type SortingState,
11
13
  } from '@tanstack/react-table';
12
- import { ChevronLeft, ChevronRight, SearchX, X, Search, Users } from 'lucide-react';
14
+ import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight, SearchX, X, Search, Users } from 'lucide-react';
13
15
  import { UserIcon } from '@heroicons/react/24/outline';
14
16
  import { useEffect, useMemo, useState } from 'react';
15
17
  import type { TableConfiguration } from '@types';
@@ -17,12 +19,14 @@ import { isSameVersion } from '@utils/collections/version-compare';
17
19
  import { resolveIconUrl } from '@utils/icon';
18
20
  import { FilterDropdown, CheckboxItem } from './FilterComponents';
19
21
  import { getDiscoverColumns } from './columns';
22
+ import { formatAdrStatus, type AdrStatus } from '@utils/collections/adr-constants';
20
23
 
21
24
  export type CollectionType =
22
25
  | 'agents'
23
26
  | 'events'
24
27
  | 'commands'
25
28
  | 'queries'
29
+ | 'adrs'
26
30
  | 'services'
27
31
  | 'external-systems'
28
32
  | 'domains'
@@ -41,6 +45,8 @@ export interface DiscoverTableData {
41
45
  hasDataDependencies?: boolean;
42
46
  hasTools?: boolean;
43
47
  hasModel?: boolean;
48
+ hasAppliesTo?: boolean;
49
+ hasDecisionMakers?: boolean;
44
50
  hasInputs?: boolean;
45
51
  hasOutputs?: boolean;
46
52
  data: {
@@ -58,6 +64,13 @@ export interface DiscoverTableData {
58
64
  textColor?: string;
59
65
  url?: string;
60
66
  }>;
67
+ status?: string;
68
+ date?: string | Date;
69
+ statusBadge?: {
70
+ content: string;
71
+ backgroundColor?: string;
72
+ textColor?: string;
73
+ };
61
74
  producers?: Array<any>;
62
75
  consumers?: Array<any>;
63
76
  receives?: Array<any>;
@@ -159,6 +172,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
159
172
  [collectionType, tableConfiguration]
160
173
  );
161
174
  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
175
+ const [sorting, setSorting] = useState<SortingState>([]);
162
176
  const [tableFilter, setTableFilter] = useState('');
163
177
  const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
164
178
  const PAGE_SIZE_STORAGE_KEY = 'eventcatalog-discover-page-size';
@@ -182,6 +196,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
182
196
  const [selectedAgentModels, setSelectedAgentModels] = useState<string[]>([]);
183
197
  const [selectedBadges, setSelectedBadges] = useState<string[]>([]);
184
198
  const [selectedProperties, setSelectedProperties] = useState<string[]>([]);
199
+ const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
185
200
 
186
201
  // Collect unique badges from all items
187
202
  const allBadges = useMemo(() => {
@@ -286,6 +301,14 @@ export function DiscoverTable<T extends DiscoverTableData>({
286
301
  }
287
302
  }
288
303
 
304
+ // ADR status filter
305
+ if (selectedStatuses.length > 0) {
306
+ const status = row.data.status;
307
+ if (!status || !selectedStatuses.includes(status)) {
308
+ return false;
309
+ }
310
+ }
311
+
289
312
  // Property filters
290
313
  if (selectedProperties.length > 0) {
291
314
  for (const prop of selectedProperties) {
@@ -364,6 +387,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
364
387
  selectedAgentProviders,
365
388
  selectedAgentModels,
366
389
  selectedBadges,
390
+ selectedStatuses,
367
391
  selectedProperties,
368
392
  tableFilter,
369
393
  ]);
@@ -378,14 +402,17 @@ export function DiscoverTable<T extends DiscoverTableData>({
378
402
  data: filteredData,
379
403
  columns,
380
404
  onColumnFiltersChange: setColumnFilters,
405
+ onSortingChange: setSorting,
381
406
  getCoreRowModel: getCoreRowModel(),
382
407
  getFilteredRowModel: getFilteredRowModel(),
408
+ getSortedRowModel: getSortedRowModel(),
383
409
  getFacetedRowModel: getFacetedRowModel(),
384
410
  getFacetedUniqueValues: getFacetedUniqueValues(),
385
411
  getFacetedMinMaxValues: getFacetedMinMaxValues(),
386
412
  getPaginationRowModel: getPaginationRowModel(),
387
413
  state: {
388
414
  columnFilters,
415
+ sorting,
389
416
  columnVisibility,
390
417
  },
391
418
  initialState: {
@@ -468,6 +495,27 @@ export function DiscoverTable<T extends DiscoverTableData>({
468
495
  return counts;
469
496
  }, [initialData]);
470
497
 
498
+ const statusCounts = useMemo(() => {
499
+ const counts: Record<string, number> = {};
500
+ initialData.forEach((item) => {
501
+ const status = item.data.status;
502
+ if (status) counts[status] = (counts[status] || 0) + 1;
503
+ });
504
+ return counts;
505
+ }, [initialData]);
506
+
507
+ const statusOptions = useMemo(
508
+ () =>
509
+ Object.keys(statusCounts)
510
+ .map((status) => ({
511
+ id: status,
512
+ name: formatAdrStatus(status as AdrStatus),
513
+ count: statusCounts[status],
514
+ }))
515
+ .sort((a, b) => a.name.localeCompare(b.name)),
516
+ [statusCounts]
517
+ );
518
+
471
519
  const toggleDomain = (domainId: string) => {
472
520
  setSelectedDomains((prev) => (prev.includes(domainId) ? prev.filter((id) => id !== domainId) : [...prev, domainId]));
473
521
  };
@@ -498,6 +546,10 @@ export function DiscoverTable<T extends DiscoverTableData>({
498
546
  setSelectedBadges((prev) => (prev.includes(badgeContent) ? prev.filter((b) => b !== badgeContent) : [...prev, badgeContent]));
499
547
  };
500
548
 
549
+ const toggleStatus = (status: string) => {
550
+ setSelectedStatuses((prev) => (prev.includes(status) ? prev.filter((item) => item !== status) : [...prev, status]));
551
+ };
552
+
501
553
  const toggleProperty = (propertyId: string) => {
502
554
  setSelectedProperties((prev) => (prev.includes(propertyId) ? prev.filter((p) => p !== propertyId) : [...prev, propertyId]));
503
555
  };
@@ -510,6 +562,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
510
562
  setSelectedAgentProviders([]);
511
563
  setSelectedAgentModels([]);
512
564
  setSelectedBadges([]);
565
+ setSelectedStatuses([]);
513
566
  setSelectedProperties([]);
514
567
  setShowOnlyLatest(true);
515
568
  setOnlyShowDrafts(false);
@@ -525,6 +578,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
525
578
  selectedAgentProviders.length +
526
579
  selectedAgentModels.length +
527
580
  selectedBadges.length +
581
+ selectedStatuses.length +
528
582
  selectedProperties.length +
529
583
  (!showOnlyLatest ? 1 : 0) +
530
584
  (onlyShowDrafts ? 1 : 0);
@@ -545,6 +599,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
545
599
  (id) => agentProviders.find((provider) => provider.id === id)?.name || id
546
600
  );
547
601
  const selectedAgentModelNames = selectedAgentModels.map((id) => agentModels.find((model) => model.id === id)?.name || id);
602
+ const selectedStatusNames = selectedStatuses.map((id) => statusOptions.find((status) => status.id === id)?.name || id);
548
603
 
549
604
  // Filter producers/consumers to only show those with count > 0
550
605
  const filteredProducers = producers.filter((p) => (producerCounts[p.id] || 0) > 0);
@@ -767,6 +822,32 @@ export function DiscoverTable<T extends DiscoverTableData>({
767
822
  </div>
768
823
  )}
769
824
 
825
+ {/* ADR Status Filter */}
826
+ {collectionType === 'adrs' && statusOptions.length > 0 && (
827
+ <div>
828
+ <label className="block text-xs font-medium text-[rgb(var(--ec-page-text)/0.8)] mb-1.5">Status</label>
829
+ <FilterDropdown
830
+ label="Select statuses..."
831
+ selectedItems={selectedStatusNames}
832
+ onClear={() => setSelectedStatuses([])}
833
+ onRemoveItem={(name) => {
834
+ const status = statusOptions.find((item) => item.name === name);
835
+ if (status) toggleStatus(status.id);
836
+ }}
837
+ >
838
+ {statusOptions.map((status) => (
839
+ <CheckboxItem
840
+ key={status.id}
841
+ label={status.name}
842
+ checked={selectedStatuses.includes(status.id)}
843
+ onChange={() => toggleStatus(status.id)}
844
+ count={status.count}
845
+ />
846
+ ))}
847
+ </FilterDropdown>
848
+ </div>
849
+ )}
850
+
770
851
  {/* Badges Filter */}
771
852
  {allBadges.length > 0 && (
772
853
  <div>
@@ -899,7 +980,24 @@ export function DiscoverTable<T extends DiscoverTableData>({
899
980
  key={`${header.id}`}
900
981
  className="px-4 py-2.5 text-left text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] uppercase tracking-wider"
901
982
  >
902
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
983
+ {header.isPlaceholder ? null : header.column.id === 'date' && header.column.getCanSort() ? (
984
+ <button
985
+ type="button"
986
+ onClick={header.column.getToggleSortingHandler()}
987
+ className="flex items-center gap-1.5 rounded-sm text-left uppercase tracking-wider hover:text-[rgb(var(--ec-page-text))] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent))]"
988
+ >
989
+ {flexRender(header.column.columnDef.header, header.getContext())}
990
+ {header.column.getIsSorted() === 'asc' ? (
991
+ <ArrowUp className="h-3.5 w-3.5" />
992
+ ) : header.column.getIsSorted() === 'desc' ? (
993
+ <ArrowDown className="h-3.5 w-3.5" />
994
+ ) : (
995
+ <ArrowUpDown className="h-3.5 w-3.5 opacity-40" />
996
+ )}
997
+ </button>
998
+ ) : (
999
+ flexRender(header.column.columnDef.header, header.getContext())
1000
+ )}
903
1001
  </th>
904
1002
  ))}
905
1003
  </tr>
@@ -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'),