@eventcatalog/core 4.9.0 → 4.10.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 (60) 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-XQHS36GO.js → chunk-5XURY2JV.js} +1 -1
  6. package/dist/{chunk-3CGSSMTK.js → chunk-BDFTJVGN.js} +1 -1
  7. package/dist/{chunk-HKVOSQTD.js → chunk-BQXEWQ5J.js} +1 -1
  8. package/dist/{chunk-MPHX5C7M.js → chunk-HFAQXAG3.js} +1 -1
  9. package/dist/{chunk-LVGQ23LR.js → chunk-IC6XSXJO.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/Badge.astro +3 -1
  19. package/eventcatalog/src/components/FieldsExplorer/FieldNodeGraph.tsx +0 -5
  20. package/eventcatalog/src/components/MDX/Design/Design.astro +2 -0
  21. package/eventcatalog/src/components/MDX/EntityMap/EntityMap.astro +2 -0
  22. package/eventcatalog/src/components/MDX/Flow/Flow.astro +2 -0
  23. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +5 -7
  24. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -0
  25. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +121 -88
  26. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +55 -7
  27. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.spec.ts +59 -15
  28. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +53 -7
  29. package/eventcatalog/src/content.config.ts +70 -0
  30. package/eventcatalog/src/enterprise/collections/resource-docs-utils.ts +19 -0
  31. package/eventcatalog/src/enterprise/fields/pages/fields.astro +2 -0
  32. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +1 -1
  33. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +16 -15
  34. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +21 -17
  35. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +1 -1
  36. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +4 -4
  37. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +16 -8
  38. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +48 -65
  39. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +45 -18
  40. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +1 -1
  41. package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -1
  42. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +5 -1
  43. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +2 -0
  44. package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +2 -0
  45. package/eventcatalog/src/stores/sidebar-store/builders/adr.ts +96 -34
  46. package/eventcatalog/src/stores/sidebar-store/builders/agent.ts +167 -75
  47. package/eventcatalog/src/stores/sidebar-store/builders/container.ts +135 -59
  48. package/eventcatalog/src/stores/sidebar-store/builders/data-product.ts +144 -58
  49. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +306 -188
  50. package/eventcatalog/src/stores/sidebar-store/builders/entity.ts +108 -39
  51. package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +147 -63
  52. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +212 -121
  53. package/eventcatalog/src/stores/sidebar-store/builders/service.ts +223 -127
  54. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +3 -23
  55. package/eventcatalog/src/stores/sidebar-store/builders/system.ts +187 -97
  56. package/eventcatalog/src/stores/sidebar-store/custom-sidebar.ts +678 -0
  57. package/eventcatalog/src/stores/sidebar-store/state.ts +66 -36
  58. package/eventcatalog/src/styles/tailwind.css +82 -0
  59. package/eventcatalog/src/utils/collections/resource-docs.ts +1 -0
  60. package/package.json +3 -3
@@ -25,90 +25,73 @@ const editUrl = resolveUbiquitousLanguageTermEditUrl({
25
25
  configEditUrl: config.editUrl,
26
26
  });
27
27
 
28
- marked.setOptions({
29
- breaks: true,
30
- gfm: true,
31
- });
28
+ // Pass options per call rather than via `marked.setOptions`, which mutates module-global
29
+ // state and leaks between pages (and between HMR reloads) in the same process.
30
+ const renderMarkdown = (markdown: string) => marked.parse(markdown, { gfm: true, breaks: false }) as string;
31
+
32
+ const TermIcon = ubiquitousLanguage.icon
33
+ ? ((LucideIcons as unknown as Record<string, LucideIcons.LucideIcon>)[ubiquitousLanguage.icon] ?? null)
34
+ : null;
32
35
 
36
+ // Badges double as navigation back up the hierarchy: the owning domain and its glossary.
33
37
  const badges = [
34
38
  {
35
39
  content: props.domain.name,
36
40
  icon: RectangleGroupIcon,
41
+ url: buildUrl(`/docs/${props.type}/${props.domainId}/${props.domain.latestVersion}`),
42
+ },
43
+ {
44
+ content: 'Ubiquitous Language',
45
+ icon: LucideIcons.BookOpen,
46
+ url: buildUrl(`/docs/${props.type}/${props.domainId}/language`),
37
47
  },
38
48
  ];
39
49
  ---
40
50
 
41
51
  <VerticalSideBarLayout title={pageTitle} description={ubiquitousLanguage.summary}>
42
52
  <main
43
- class="flex sm:px-8 docs-layout min-h-full max-w-7xl bg-[rgb(var(--ec-page-bg))]"
53
+ class="flex docs-layout min-h-full bg-[rgb(var(--ec-page-bg))]"
44
54
  data-pagefind-body
45
55
  data-pagefind-meta={`title:${pageTitle}`}
46
56
  >
47
57
  <div class="flex docs-layout w-full">
48
- <div class="w-full lg:mr-2 pr-8 py-8 min-h-[50em]">
49
- {/* Breadcrumb */}
50
- <nav class="mb-4 flex items-center space-x-2 text-sm text-[rgb(var(--ec-page-text-muted))]" aria-label="Breadcrumb">
51
- <a
52
- href={buildUrl(`/docs/${props.type}/${props.domainId}/${props.domain.latestVersion}`)}
53
- class="hover:text-[rgb(var(--ec-page-text))] hover:underline flex items-center gap-2"
54
- >
55
- <RectangleGroupIcon className="h-4 w-4" />
56
- {props.domain.name}
57
- </a>
58
- <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
59
- <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"></path>
60
- </svg>
61
- <a
62
- href={buildUrl(`/docs/${props.type}/${props.domainId}/language`)}
63
- class="hover:text-[rgb(var(--ec-page-text))] hover:underline flex items-center gap-2"
64
- >
65
- {
66
- (() => {
67
- const BookOpen = LucideIcons.BookOpen;
68
- //@ts-ignore
69
- return <BookOpen className="h-4 w-4" />;
70
- })()
71
- }
72
- Ubiquitous Language Explorer
73
- </a>
74
- <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
75
- <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"></path>
76
- </svg>
77
- <span class="text-[rgb(var(--ec-page-text))]">{ubiquitousLanguage.name}</span>
78
- </nav>
79
-
80
- <div class="border-b border-[rgb(var(--ec-page-border))] flex justify-between items-start md:pb-2">
81
- <div>
82
- <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">
83
- {ubiquitousLanguage.name}
84
- </h2>
85
- <p class="text-lg pt-2 text-[rgb(var(--ec-page-text-muted))] font-light">{ubiquitousLanguage.summary}</p>
86
- <!-- Add badge -->
87
- {
88
- badges && badges.length > 0 && (
89
- <div class="flex flex-wrap gap-3 py-4">
90
- {badges.map((badge: any) => {
91
- return <Badge badge={badge} />;
92
- })}
93
- </div>
94
- )
95
- }
58
+ <div class="w-full min-w-0 lg:mr-2 pr-24 py-8 min-h-[50em]">
59
+ {/* Header mirrors the resource page (docs/[type]/[id]/[version]/index.astro). */}
60
+ <div class="border-b border-[rgb(var(--ec-page-border))] md:pb-3 pt-4">
61
+ <div class="flex flex-col gap-4">
62
+ <span class="text-xs md:text-sm font-semibold text-[rgb(var(--ec-accent))] capitalize">Term</span>
63
+ <div class="flex items-center gap-3">
64
+ {
65
+ TermIcon && (
66
+ <div class="flex items-center justify-center w-8 h-8 md:w-10 md:h-10 rounded-md bg-[rgb(var(--ec-accent-subtle))] shrink-0">
67
+ <TermIcon className="w-5 h-5 md:w-6 md:h-6 text-[rgb(var(--ec-accent))]" />
68
+ </div>
69
+ )
70
+ }
71
+ <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">
72
+ {ubiquitousLanguage.name}
73
+ </h2>
74
+ </div>
75
+ </div>
76
+ {
77
+ ubiquitousLanguage.summary && (
78
+ <p class="text-base pt-4 text-[rgb(var(--ec-page-text-muted))] font-light">{ubiquitousLanguage.summary}</p>
79
+ )
80
+ }
81
+ <div class="flex flex-wrap gap-1.5 pt-4 pb-1">
82
+ {badges.map((badge: any) => <Badge badge={badge} />)}
96
83
  </div>
97
84
  </div>
98
85
 
99
- {
100
- ubiquitousLanguage.description && (
101
- <div class="prose prose-md py-4 max-w-none" set:html={marked.parse(ubiquitousLanguage.description)} />
102
- )
103
- }
104
-
105
- {
106
- !ubiquitousLanguage.description && (
107
- <div class="prose prose-md py-4 max-w-none">
86
+ <div class="prose prose-md pt-8 pb-4 w-full text-[15px]">
87
+ {
88
+ ubiquitousLanguage.description ? (
89
+ <Fragment set:html={renderMarkdown(ubiquitousLanguage.description)} />
90
+ ) : (
108
91
  <p>No description for {ubiquitousLanguage.name} available.</p>
109
- </div>
110
- )
111
- }
92
+ )
93
+ }
94
+ </div>
112
95
 
113
96
  <Footer />
114
97
  <div class="flex justify-end">
@@ -19,6 +19,11 @@ const pageTitle = `${props.collection} | ${props.data.name}`.replace(/^\w/, (c)
19
19
  const ubiquitousLanguageData = await getUbiquitousLanguageWithSubdomains(props);
20
20
  const ubiquitousLanguage = ubiquitousLanguageData.domain;
21
21
  const { subdomains, duplicateTerms } = ubiquitousLanguageData;
22
+
23
+ const termCount =
24
+ (ubiquitousLanguage?.data.dictionary?.length ?? 0) +
25
+ subdomains.reduce((total, subdomain: any) => total + (subdomain.ubiquitousLanguage?.data.dictionary?.length ?? 0), 0);
26
+ const ownerTypeLabel = props.collection === 'domains' ? 'Domain' : 'System';
22
27
  ---
23
28
 
24
29
  <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
@@ -28,26 +33,31 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
28
33
  data-pagefind-meta={`title:${pageTitle}`}
29
34
  >
30
35
  <div class="flex docs-layout w-full">
31
- <div class="w-full lg:mr-2 pr-24 py-8 min-h-[50em]">
32
- {/* Title Section */}
33
- <div class="relative border-b border-[rgb(var(--ec-page-border))] mb-6 pb-6">
34
- <div class="xl:flex xl:items-start xl:justify-between">
35
- <div class="min-w-0 flex-1">
36
- <h1 class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">Ubiquitous Language</h1>
37
- <p class="pt-2 text-base text-[rgb(var(--ec-page-text-muted))] font-light">
38
- Browse and discover ubiquitous language terms in the {props.data.name} domain{
39
- subdomains.length > 0 ? ' and its subdomains' : ''
40
- }
41
- </p>
42
- </div>
36
+ <div class="w-full min-w-0 lg:mr-2 pr-24 py-8 min-h-[50em]">
37
+ {/* Header mirrors the resource page (docs/[type]/[id]/[version]/index.astro). */}
38
+ <div class="border-b border-[rgb(var(--ec-page-border))] md:pb-3 pt-4">
39
+ <div class="flex flex-col gap-4">
40
+ <span class="text-xs md:text-sm font-semibold text-[rgb(var(--ec-accent))] capitalize">{ownerTypeLabel}</span>
41
+ <h1 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">
42
+ Ubiquitous Language
43
+ </h1>
44
+ </div>
45
+ <p class="text-base pt-4 pb-1 text-[rgb(var(--ec-page-text-muted))] font-light">
46
+ {termCount} {termCount === 1 ? 'term' : 'terms'} used in the {props.data.name} {ownerTypeLabel.toLowerCase()}{
47
+ subdomains.length > 0 ? ' and its subdomains' : ''
48
+ }.
49
+ </p>
50
+ </div>
43
51
 
44
- <div class="mt-4 xl:mt-0 xl:ml-4 xl:flex-shrink-0">
45
- <div class="relative w-full xl:w-80">
52
+ {
53
+ !ubiquitousLanguage && termCount > 0 && (
54
+ <div class="pt-8">
55
+ <div class="relative w-full max-w-md">
46
56
  <input
47
57
  type="text"
48
58
  id="searchInput"
49
59
  placeholder="Search terms..."
50
- class="block w-full rounded-md border-0 py-1.5 pr-4 pl-10 text-sm font-light text-[rgb(var(--ec-header-text))] bg-[rgb(var(--ec-header-bg))] shadow-xs ring-1 ring-inset ring-[rgb(var(--ec-dropdown-border))] placeholder:text-[rgb(var(--ec-icon-color))] focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:outline-none"
60
+ class="block w-full rounded-md border-0 py-2 pr-4 pl-10 text-sm font-light text-[rgb(var(--ec-header-text))] bg-[rgb(var(--ec-header-bg))] shadow-xs ring-1 ring-inset ring-[rgb(var(--ec-dropdown-border))] placeholder:text-[rgb(var(--ec-icon-color))] focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:outline-none"
51
61
  />
52
62
  <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
53
63
  <svg class="h-4 w-4 text-[rgb(var(--ec-icon-color))]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -60,8 +70,8 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
60
70
  </div>
61
71
  </div>
62
72
  </div>
63
- </div>
64
- </div>
73
+ )
74
+ }
65
75
 
66
76
  {
67
77
  !ubiquitousLanguage && subdomains.length === 0 && (
@@ -82,7 +92,7 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
82
92
  )
83
93
  }
84
94
 
85
- <div class="py-4 w-full min-h-[calc(100vh-10em)]">
95
+ <div class="pt-8 pb-4 w-full min-h-[calc(100vh-10em)]">
86
96
  {
87
97
  (ubiquitousLanguage || subdomains.some((s) => s.ubiquitousLanguage)) && (
88
98
  <div>
@@ -101,6 +111,23 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
101
111
  </span>
102
112
  </h3>
103
113
  </div>
114
+ <div class="relative w-64 flex-shrink-0">
115
+ <input
116
+ type="text"
117
+ id="searchInput"
118
+ placeholder="Search terms..."
119
+ class="block w-full rounded-md border-0 py-1.5 pr-4 pl-9 text-sm font-light text-[rgb(var(--ec-header-text))] bg-[rgb(var(--ec-header-bg))] shadow-xs ring-1 ring-inset ring-[rgb(var(--ec-dropdown-border))] placeholder:text-[rgb(var(--ec-icon-color))] focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:outline-none"
120
+ />
121
+ <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
122
+ <svg class="h-4 w-4 text-[rgb(var(--ec-icon-color))]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
123
+ <path
124
+ stroke-linecap="round"
125
+ stroke-linejoin="round"
126
+ stroke-width="2"
127
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
128
+ </svg>
129
+ </div>
130
+ </div>
104
131
  </div>
105
132
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" data-terms-grid="main">
106
133
  {ubiquitousLanguage?.data?.dictionary?.map((term) => (
@@ -103,7 +103,7 @@ const sourceTitle = sourceLabel ? `Synced from ${sourceLabel}` : undefined;
103
103
  data-pagefind-meta={`title:${pageTitle}`}
104
104
  >
105
105
  <div class="flex docs-layout w-full">
106
- <div class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
106
+ <div class="w-full min-w-0 lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
107
107
  <div class="border-b border-[rgb(var(--ec-page-border))] pb-6">
108
108
  <div class="flex justify-start">
109
109
  <div class="flex flex-col justify-between space-y-3">
@@ -100,7 +100,7 @@ const sourceTitle = sourceLabel ? `Synced from ${sourceLabel}` : undefined;
100
100
  data-pagefind-meta={`title:${pageTitle}`}
101
101
  >
102
102
  <div class="flex docs-layout w-full">
103
- <div class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
103
+ <div class="w-full min-w-0 lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
104
104
  <div class="border-b border-[rgb(var(--ec-page-border))] pb-6">
105
105
  <div class="flex justify-start gap-6">
106
106
  {
@@ -130,7 +130,11 @@ const apiAccessEnabled = isEventCatalogScaleEnabled();
130
130
  ---
131
131
 
132
132
  <VerticalSideBarLayout title={pageTitle}>
133
- <div class="h-[calc(100vh-4rem)] w-full overflow-hidden flex flex-col">
133
+ {/* The schema viewer is an app-style surface: pull it out of the content gutter so it sits flush against the sidebar. */}
134
+ <div
135
+ class="h-[calc(100vh-4rem)] overflow-hidden flex flex-col"
136
+ style="margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1); margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1);"
137
+ >
134
138
  {
135
139
  currentMessage ? (
136
140
  <SchemaPageViewer
@@ -3,6 +3,8 @@ import { getCollection, type CollectionEntry } from 'astro:content';
3
3
 
4
4
  import LineageScenarioSwitcher from '@components/Lineage/LineageScenarioSwitcher';
5
5
  import AstroNodeGraph from '@components/MDX/NodeGraph/AstroNodeGraph';
6
+ // Visualiser styles ship in the page head so ClientRouter navigations keep them (see NodeGraph.astro).
7
+ import '@eventcatalog/visualiser/styles-core.css';
6
8
  import ResourceRef from '@components/MDX/ResourceRef/ResourceRef.astro';
7
9
  import Footer from '@layouts/Footer.astro';
8
10
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  import AstroNodeGraph from '@components/MDX/NodeGraph/AstroNodeGraph';
3
+ // Visualiser styles ship in the page head so ClientRouter navigations keep them (see NodeGraph.astro).
4
+ import '@eventcatalog/visualiser/styles-core.css';
3
5
  import { ClientRouter } from 'astro:transitions';
4
6
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
5
7
  import { isEventCatalogChatEnabled } from '@utils/feature';
@@ -8,7 +8,7 @@ import {
8
8
  type AdrResource,
9
9
  } from '@utils/collections/adrs';
10
10
  import { collectionToResourceMap, createVersionedMap, findInMap } from '@utils/collections/util';
11
- import type { ChildRef, NavNode, ResourceGroupContext } from './shared';
11
+ import type { NavNode, ResourceGroupContext } from './shared';
12
12
  import {
13
13
  buildAttachmentsSection,
14
14
  buildOwnersSection,
@@ -17,6 +17,7 @@ import {
17
17
  shouldRenderSideBarSection,
18
18
  } from './shared';
19
19
  import { isChangelogEnabled } from '@utils/feature';
20
+ import { resolveSidebarPages, toCustomSidebarContext, type SidebarSpec } from '../custom-sidebar';
20
21
 
21
22
  const firstClassResourceCollections = [
22
23
  'agents',
@@ -102,13 +103,105 @@ const buildDecisionMakersSection = (decisionMakers: any[]): NavNode | null => {
102
103
  };
103
104
  };
104
105
 
105
- export const buildAdrNode = (adr: Adr, owners: any[], decisionMakers: any[], context: ResourceGroupContext): NavNode => {
106
+ /**
107
+ * Predefined sidebar sections for a decision record, keyed by the token users reference from
108
+ * `sidebar.json` (e.g. `$quick-reference`). Each token is the kebab-cased title of the
109
+ * section as it appears in the default sidebar.
110
+ */
111
+ export type AdrSectionKey =
112
+ | 'quick-reference'
113
+ | 'applies-to'
114
+ | 'supersedes'
115
+ | 'superseded-by'
116
+ | 'amends'
117
+ | 'amended-by'
118
+ | 'related-decisions'
119
+ | 'decision-makers'
120
+ | 'owners'
121
+ | 'code'
122
+ | 'attachments';
123
+
124
+ export type AdrSections = Record<AdrSectionKey, NavNode | NavNode[] | null>;
125
+
126
+ /** Order of sections in the default (generated) sidebar. */
127
+ export const DEFAULT_ADR_SECTION_ORDER: AdrSectionKey[] = [
128
+ 'quick-reference',
129
+ 'applies-to',
130
+ 'supersedes',
131
+ 'superseded-by',
132
+ 'amends',
133
+ 'amended-by',
134
+ 'related-decisions',
135
+ 'decision-makers',
136
+ 'owners',
137
+ 'code',
138
+ 'attachments',
139
+ ];
140
+
141
+ export const buildAdrSections = (adr: Adr, owners: any[], decisionMakers: any[], context: ResourceGroupContext): AdrSections => {
106
142
  const relationships = getAdrRelationships(adr, context.adrs);
107
143
  const appliesToRefs = resolveAppliedResourceRefs(adr, context);
108
144
  const hasAttachments = adr.data.attachments && adr.data.attachments.length > 0;
109
145
  const renderOwners = owners.length > 0 && shouldRenderSideBarSection(adr, 'owners');
110
146
  const renderDecisionMakers = decisionMakers.length > 0 && shouldRenderSideBarSection(adr, 'decisionMakers');
111
147
  const renderRepository = adr.data.repository && shouldRenderSideBarSection(adr, 'repository');
148
+ const renderRelationships = shouldRenderSideBarSection(adr, 'relationships');
149
+
150
+ return {
151
+ 'quick-reference': buildQuickReferenceSection(
152
+ [
153
+ { title: 'Overview', href: buildUrl(`/docs/adrs/${adr.data.id}/${adr.data.version}`) },
154
+ isChangelogEnabled() &&
155
+ shouldRenderSideBarSection(adr, 'changelog') && {
156
+ title: 'Changelog',
157
+ href: buildUrl(`/docs/adrs/${adr.data.id}/${adr.data.version}/changelog`),
158
+ },
159
+ ].filter(Boolean) as { title: string; href: string }[]
160
+ ),
161
+ 'applies-to':
162
+ appliesToRefs.length > 0 && shouldRenderSideBarSection(adr, 'appliesTo')
163
+ ? {
164
+ type: 'group',
165
+ title: 'Applies to',
166
+ icon: 'GitBranch',
167
+ pages: appliesToRefs,
168
+ }
169
+ : null,
170
+ supersedes: renderRelationships ? buildAdrRelationshipSection('Supersedes', 'History', relationships.supersedes) : null,
171
+ 'superseded-by': renderRelationships
172
+ ? buildAdrRelationshipSection('Superseded by', 'History', relationships.supersededBy)
173
+ : null,
174
+ amends: renderRelationships ? buildAdrRelationshipSection('Amends', 'Pencil', relationships.amends) : null,
175
+ 'amended-by': renderRelationships ? buildAdrRelationshipSection('Amended by', 'Pencil', relationships.amendedBy) : null,
176
+ 'related-decisions': renderRelationships
177
+ ? buildAdrRelationshipSection('Related decisions', 'Link', resolveAdrPointers(adr.data.related, context.adrs))
178
+ : null,
179
+ 'decision-makers': renderDecisionMakers ? buildDecisionMakersSection(decisionMakers) : null,
180
+ owners: renderOwners ? buildOwnersSection(owners) : null,
181
+ code: renderRepository ? buildRepositorySection(adr.data.repository as { url: string; language: string }) : null,
182
+ attachments: hasAttachments ? buildAttachmentsSection(adr.data.attachments as any[]) : null,
183
+ };
184
+ };
185
+
186
+ export type BuildAdrNodeOptions = {
187
+ /** A parsed `sidebar.json` for this decision record. When present it replaces the generated sidebar. */
188
+ sidebar?: SidebarSpec;
189
+ };
190
+
191
+ export const buildAdrNode = (
192
+ adr: Adr,
193
+ owners: any[],
194
+ decisionMakers: any[],
195
+ context: ResourceGroupContext,
196
+ options: BuildAdrNodeOptions = {}
197
+ ): NavNode => {
198
+ const sections = buildAdrSections(adr, owners, decisionMakers, context);
199
+
200
+ const pages = resolveSidebarPages(sections, DEFAULT_ADR_SECTION_ORDER, {
201
+ sidebar: options.sidebar,
202
+ resource: { collection: 'adrs', id: adr.data.id, version: adr.data.version, entry: adr },
203
+ context: toCustomSidebarContext(context),
204
+ });
112
205
 
113
206
  return {
114
207
  type: 'item',
@@ -116,37 +209,6 @@ export const buildAdrNode = (adr: Adr, owners: any[], decisionMakers: any[], con
116
209
  badge: 'Decision record',
117
210
  summary: adr.data.summary,
118
211
  icon: 'ClipboardList',
119
- pages: [
120
- buildQuickReferenceSection(
121
- [
122
- { title: 'Overview', href: buildUrl(`/docs/adrs/${adr.data.id}/${adr.data.version}`) },
123
- isChangelogEnabled() &&
124
- shouldRenderSideBarSection(adr, 'changelog') && {
125
- title: 'Changelog',
126
- href: buildUrl(`/docs/adrs/${adr.data.id}/${adr.data.version}/changelog`),
127
- },
128
- ].filter(Boolean) as { title: string; href: string }[]
129
- ),
130
- appliesToRefs.length > 0 &&
131
- shouldRenderSideBarSection(adr, 'appliesTo') && {
132
- type: 'group',
133
- title: 'Applies to',
134
- icon: 'GitBranch',
135
- pages: appliesToRefs,
136
- },
137
- shouldRenderSideBarSection(adr, 'relationships') &&
138
- buildAdrRelationshipSection('Supersedes', 'History', relationships.supersedes),
139
- shouldRenderSideBarSection(adr, 'relationships') &&
140
- buildAdrRelationshipSection('Superseded by', 'History', relationships.supersededBy),
141
- shouldRenderSideBarSection(adr, 'relationships') && buildAdrRelationshipSection('Amends', 'Pencil', relationships.amends),
142
- shouldRenderSideBarSection(adr, 'relationships') &&
143
- buildAdrRelationshipSection('Amended by', 'Pencil', relationships.amendedBy),
144
- shouldRenderSideBarSection(adr, 'relationships') &&
145
- buildAdrRelationshipSection('Related decisions', 'Link', resolveAdrPointers(adr.data.related, context.adrs)),
146
- renderDecisionMakers && buildDecisionMakersSection(decisionMakers),
147
- renderOwners && buildOwnersSection(owners),
148
- renderRepository && buildRepositorySection(adr.data.repository as { url: string; language: string }),
149
- hasAttachments && buildAttachmentsSection(adr.data.attachments as any[]),
150
- ].filter(Boolean) as ChildRef[],
212
+ pages,
151
213
  };
152
214
  };