@eventcatalog/core 3.14.6 → 3.15.0-beta.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.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-6NRRWRLT.js → chunk-4JTLFCQ7.js} +1 -1
- package/dist/{chunk-TERDXO46.js → chunk-7RPTQBIY.js} +1 -1
- package/dist/{chunk-LNIPDPVB.js → chunk-CVH4LGYA.js} +1 -1
- package/dist/{chunk-NYVQSLA5.js → chunk-FRILQLHV.js} +1 -1
- package/dist/{chunk-KWCAGR52.js → chunk-VUARMJ2E.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +56 -10
- package/eventcatalog/src/content.config.ts +53 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +85 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +195 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +86 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +195 -0
- package/eventcatalog/src/stores/sidebar-store/builders/container.ts +9 -0
- package/eventcatalog/src/stores/sidebar-store/builders/data-product.ts +15 -9
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +9 -0
- package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +13 -4
- package/eventcatalog/src/stores/sidebar-store/builders/message.ts +9 -0
- package/eventcatalog/src/stores/sidebar-store/builders/service.ts +9 -0
- package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +82 -0
- package/eventcatalog/src/stores/sidebar-store/state.ts +27 -2
- package/eventcatalog/src/utils/collections/data-products.ts +6 -2
- package/eventcatalog/src/utils/collections/resource-docs.ts +601 -0
- package/eventcatalog/src/utils/feature.ts +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isSSR, isResourceDocsEnabled } from '@utils/feature';
|
|
2
|
+
import { HybridPage } from '@utils/page-loaders/hybrid-page';
|
|
3
|
+
import { getResourceDocs, getResourceDocsForResource, type ResourceCollection } from '@utils/collections/resource-docs';
|
|
4
|
+
|
|
5
|
+
const supportedResourceCollections = new Set<ResourceCollection>([
|
|
6
|
+
'domains',
|
|
7
|
+
'services',
|
|
8
|
+
'events',
|
|
9
|
+
'commands',
|
|
10
|
+
'queries',
|
|
11
|
+
'flows',
|
|
12
|
+
'containers',
|
|
13
|
+
'channels',
|
|
14
|
+
'entities',
|
|
15
|
+
'data-products',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export class Page extends HybridPage {
|
|
19
|
+
static async getStaticPaths() {
|
|
20
|
+
if (isSSR() || !isResourceDocsEnabled()) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const docs = await getResourceDocs();
|
|
25
|
+
const latestDocs = docs.filter((doc) => doc.data.version === doc.data.latestVersion);
|
|
26
|
+
|
|
27
|
+
return latestDocs.map((doc) => ({
|
|
28
|
+
params: {
|
|
29
|
+
type: doc.data.resourceCollection,
|
|
30
|
+
id: doc.data.resourceId,
|
|
31
|
+
version: doc.data.resourceVersion,
|
|
32
|
+
docType: doc.data.type,
|
|
33
|
+
docId: doc.data.id,
|
|
34
|
+
},
|
|
35
|
+
props: {},
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected static async fetchData(params: any) {
|
|
40
|
+
if (!isResourceDocsEnabled()) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const decodeParam = (value: string) => {
|
|
45
|
+
try {
|
|
46
|
+
return decodeURIComponent(value);
|
|
47
|
+
} catch {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const type = decodeParam(params.type);
|
|
53
|
+
const id = decodeParam(params.id);
|
|
54
|
+
const version = decodeParam(params.version);
|
|
55
|
+
const docType = decodeParam(params.docType);
|
|
56
|
+
const docId = decodeParam(params.docId);
|
|
57
|
+
if (!type || !id || !version || !docType || !docId) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!supportedResourceCollections.has(type as ResourceCollection)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const docsForResource = await getResourceDocsForResource(type as ResourceCollection, id, version);
|
|
66
|
+
const doc = docsForResource.find(
|
|
67
|
+
(resourceDoc) =>
|
|
68
|
+
resourceDoc.data.type === docType &&
|
|
69
|
+
resourceDoc.data.id === docId &&
|
|
70
|
+
resourceDoc.data.version === resourceDoc.data.latestVersion
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (!doc) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return doc;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected static createNotFoundResponse(): Response {
|
|
81
|
+
return new Response(null, {
|
|
82
|
+
status: 404,
|
|
83
|
+
statusText: 'Resource documentation not found',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { render } from 'astro:content';
|
|
3
|
+
|
|
4
|
+
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
5
|
+
import components from '@components/MDX/components';
|
|
6
|
+
import { buildUrl } from '@utils/url-builder';
|
|
7
|
+
import { AlignLeftIcon, HistoryIcon } from 'lucide-react';
|
|
8
|
+
import { isResourceDocsEnabled } from '@utils/feature';
|
|
9
|
+
import { getIcon } from '@utils/badges';
|
|
10
|
+
import { collectionToResourceMap } from '@utils/collections/util';
|
|
11
|
+
|
|
12
|
+
import { Page } from './_index.data';
|
|
13
|
+
|
|
14
|
+
export const prerender = Page.prerender;
|
|
15
|
+
export const getStaticPaths = Page.getStaticPaths;
|
|
16
|
+
|
|
17
|
+
if (!isResourceDocsEnabled()) {
|
|
18
|
+
return Astro.redirect(buildUrl('/docs/custom/feature'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = await Page.getData(Astro);
|
|
22
|
+
const { Content, headings } = await render(props);
|
|
23
|
+
|
|
24
|
+
const title = props.data.title || props.data.id;
|
|
25
|
+
const pageTitle = `${title} | ${props.data.resourceId}`;
|
|
26
|
+
const versions = props.data.versions || [props.data.version];
|
|
27
|
+
const badges = (props.data.badges || []).map((badge: any) => ({
|
|
28
|
+
...badge,
|
|
29
|
+
iconComponent: badge.icon ? getIcon(badge.icon) : null,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const docsBasePath = `/docs/${props.data.resourceCollection}/${props.data.resourceId}/${props.data.resourceVersion}/${encodeURIComponent(props.data.type)}/${encodeURIComponent(props.data.id)}`;
|
|
33
|
+
const singularResourceName =
|
|
34
|
+
collectionToResourceMap[props.data.resourceCollection as keyof typeof collectionToResourceMap] ??
|
|
35
|
+
props.data.resourceCollection.slice(0, props.data.resourceCollection.length - 1);
|
|
36
|
+
|
|
37
|
+
const pagefindAttributes =
|
|
38
|
+
props.data.version === props.data.latestVersion
|
|
39
|
+
? {
|
|
40
|
+
'data-pagefind-body': '',
|
|
41
|
+
'data-pagefind-meta': `title:${pageTitle}`,
|
|
42
|
+
}
|
|
43
|
+
: {};
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
|
|
47
|
+
<main class="flex docs-layout h-full bg-[rgb(var(--ec-page-bg))]" {...pagefindAttributes}>
|
|
48
|
+
<div class="flex docs-layout w-full pl-16">
|
|
49
|
+
<div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 bg-[rgb(var(--ec-page-bg))]">
|
|
50
|
+
<div class="border-b border-[rgb(var(--ec-page-border))] pb-4">
|
|
51
|
+
<p class="text-xs uppercase tracking-wide text-[rgb(var(--ec-page-text-muted))]">
|
|
52
|
+
<a
|
|
53
|
+
href={buildUrl(`/docs/${props.data.resourceCollection}/${props.data.resourceId}/${props.data.resourceVersion}`)}
|
|
54
|
+
class="hover:underline"
|
|
55
|
+
>
|
|
56
|
+
{singularResourceName}: {props.data.resourceId}
|
|
57
|
+
</a>
|
|
58
|
+
</p>
|
|
59
|
+
<div class="flex items-center gap-2 pt-1">
|
|
60
|
+
<h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">{title}</h2>
|
|
61
|
+
<span
|
|
62
|
+
class="text-xs rounded-md px-2 py-1 bg-[rgb(var(--ec-content-hover))] text-[rgb(var(--ec-page-text-muted))] border border-[rgb(var(--ec-page-border))]"
|
|
63
|
+
>
|
|
64
|
+
v{props.data.version}
|
|
65
|
+
{props.data.version === props.data.latestVersion && ' (latest)'}
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
{
|
|
69
|
+
props.data.summary && (
|
|
70
|
+
<p class="text-lg pt-2 text-[rgb(var(--ec-page-text-muted))] font-light">{props.data.summary}</p>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
{
|
|
74
|
+
badges.length > 0 && (
|
|
75
|
+
<div class="flex flex-wrap gap-3 pt-4">
|
|
76
|
+
{badges.map((badge: any) => (
|
|
77
|
+
<span
|
|
78
|
+
class={`
|
|
79
|
+
inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium
|
|
80
|
+
bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))]
|
|
81
|
+
text-[rgb(var(--ec-page-text))]
|
|
82
|
+
`}
|
|
83
|
+
>
|
|
84
|
+
{badge.iconComponent && (
|
|
85
|
+
<badge.iconComponent className="w-4 h-4 flex-shrink-0 text-[rgb(var(--ec-icon-color))]" />
|
|
86
|
+
)}
|
|
87
|
+
<span>{badge.content}</span>
|
|
88
|
+
</span>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
</div>
|
|
94
|
+
<div data-pagefind-ignore>
|
|
95
|
+
{
|
|
96
|
+
props.data.version !== props.data.latestVersion && (
|
|
97
|
+
<div class="rounded-md bg-[rgb(var(--ec-accent-subtle))] p-4 not-prose my-4">
|
|
98
|
+
<div class="flex">
|
|
99
|
+
<div class="flex-shrink-0">
|
|
100
|
+
<svg class="h-5 w-5 text-[rgb(var(--ec-accent))]" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
101
|
+
<path
|
|
102
|
+
fill-rule="evenodd"
|
|
103
|
+
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
|
|
104
|
+
clip-rule="evenodd"
|
|
105
|
+
/>
|
|
106
|
+
</svg>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="ml-3">
|
|
109
|
+
<h3 class="text-sm font-medium text-[rgb(var(--ec-accent-text))]">New version found</h3>
|
|
110
|
+
<div class="mt-2 text-sm text-[rgb(var(--ec-accent-text))]">
|
|
111
|
+
<p>
|
|
112
|
+
You are looking at a previous version of the {singularResourceName} doc <strong>{title}</strong>.{' '}
|
|
113
|
+
<a
|
|
114
|
+
class="underline hover:text-primary block pt-2"
|
|
115
|
+
href={buildUrl(`${docsBasePath}/${props.data.latestVersion}`)}
|
|
116
|
+
>
|
|
117
|
+
The latest version of this doc is <span>v{props.data.latestVersion}</span> →
|
|
118
|
+
</a>
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
</div>
|
|
127
|
+
<div class="prose py-8 max-w-none">
|
|
128
|
+
<Content components={components(props)} />
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<aside class="hidden lg:block sticky top-0 pb-10 w-80 overflow-y-auto border-l border-[rgb(var(--ec-page-border))] py-2">
|
|
132
|
+
<div class="sticky top-28 left-0 h-full overflow-y-auto px-6 py-4">
|
|
133
|
+
<h3 class="text-sm text-[rgb(var(--ec-page-text))] font-semibold capitalize flex items-center gap-2">
|
|
134
|
+
<AlignLeftIcon className="w-4 h-4" />
|
|
135
|
+
On this page
|
|
136
|
+
</h3>
|
|
137
|
+
<nav class="space-y-1 text-sm py-4">
|
|
138
|
+
{
|
|
139
|
+
headings.map((heading) => {
|
|
140
|
+
if (heading.depth > 3) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return (
|
|
144
|
+
<a
|
|
145
|
+
href={`#${heading.slug}`}
|
|
146
|
+
class={`block text-[12px] py-0.5 ${heading.depth === 1 ? 'font-light' : ''} text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]`}
|
|
147
|
+
style={`padding-left: ${(heading.depth - 1) * 8}px`}
|
|
148
|
+
>
|
|
149
|
+
{heading.text}
|
|
150
|
+
</a>
|
|
151
|
+
);
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
</nav>
|
|
155
|
+
<div class="space-y-2 pb-4">
|
|
156
|
+
<span class="text-xs text-[rgb(var(--ec-page-text))] font-semibold capitalize">Versions ({versions.length})</span>
|
|
157
|
+
<ul role="list" class="space-y-2">
|
|
158
|
+
{
|
|
159
|
+
versions.map((version: string) => (
|
|
160
|
+
<li class="version-item rounded-md px-1 group w-full">
|
|
161
|
+
<a class="flex items-center space-x-2" href={buildUrl(`${docsBasePath}/${encodeURIComponent(version)}`)}>
|
|
162
|
+
<HistoryIcon
|
|
163
|
+
className="h-4 w-4 text-[rgb(var(--ec-page-text-muted))] group-hover:text-[rgb(var(--ec-accent-text))]"
|
|
164
|
+
strokeWidth={1}
|
|
165
|
+
/>
|
|
166
|
+
<span
|
|
167
|
+
class={`font-light text-xs text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-accent-text))] ${
|
|
168
|
+
version === props.data.version ? 'underline' : ''
|
|
169
|
+
}`}
|
|
170
|
+
>
|
|
171
|
+
{version === props.data.latestVersion ? `v${version} (latest)` : `v${version}`}
|
|
172
|
+
</span>
|
|
173
|
+
</a>
|
|
174
|
+
</li>
|
|
175
|
+
))
|
|
176
|
+
}
|
|
177
|
+
</ul>
|
|
178
|
+
<div class="border-b border-[rgb(var(--ec-page-border))] pt-2"></div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</aside>
|
|
182
|
+
</div>
|
|
183
|
+
</main>
|
|
184
|
+
</VerticalSideBarLayout>
|
|
185
|
+
|
|
186
|
+
<style is:global>
|
|
187
|
+
.docs-layout .prose {
|
|
188
|
+
max-width: none;
|
|
189
|
+
overflow: auto;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.version-item:hover {
|
|
193
|
+
background: linear-gradient(to left, rgb(var(--ec-accent-gradient-from)), rgb(var(--ec-accent-gradient-to)));
|
|
194
|
+
}
|
|
195
|
+
</style>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
buildRepositorySection,
|
|
9
9
|
buildAttachmentsSection,
|
|
10
10
|
buildDiagramNavItems,
|
|
11
|
+
buildResourceDocsSection,
|
|
11
12
|
} from './shared';
|
|
12
13
|
import { isVisualiserEnabled } from '@utils/feature';
|
|
13
14
|
|
|
@@ -42,6 +43,13 @@ export const buildContainerNode = (
|
|
|
42
43
|
const hasAttachments = container.data.attachments && container.data.attachments.length > 0;
|
|
43
44
|
|
|
44
45
|
const renderRepository = container.data.repository && shouldRenderSideBarSection(container, 'repository');
|
|
46
|
+
const docsSection = buildResourceDocsSection(
|
|
47
|
+
'containers',
|
|
48
|
+
container.data.id,
|
|
49
|
+
container.data.version,
|
|
50
|
+
context.resourceDocs,
|
|
51
|
+
context.resourceDocCategories
|
|
52
|
+
);
|
|
45
53
|
|
|
46
54
|
// Diagrams
|
|
47
55
|
const containerDiagrams = container.data.diagrams || [];
|
|
@@ -60,6 +68,7 @@ export const buildContainerNode = (
|
|
|
60
68
|
href: buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`),
|
|
61
69
|
},
|
|
62
70
|
]),
|
|
71
|
+
docsSection,
|
|
63
72
|
renderVisualiser && {
|
|
64
73
|
type: 'group',
|
|
65
74
|
title: 'Architecture',
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type { CollectionEntry } from 'astro:content';
|
|
2
2
|
import { buildUrl } from '@utils/url-builder';
|
|
3
|
-
import type { NavNode, ChildRef } from './shared';
|
|
4
|
-
import { buildQuickReferenceSection, buildOwnersSection, shouldRenderSideBarSection } from './shared';
|
|
3
|
+
import type { NavNode, ChildRef, ResourceGroupContext } from './shared';
|
|
4
|
+
import { buildQuickReferenceSection, buildOwnersSection, shouldRenderSideBarSection, buildResourceDocsSection } from './shared';
|
|
5
5
|
import { isVisualiserEnabled } from '@utils/feature';
|
|
6
6
|
import { getItemsFromCollectionByIdAndSemverOrLatest, sortVersioned } from '@utils/collections/util';
|
|
7
7
|
import { getSchemaFormatFromURL } from '@utils/collections/schemas';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
services: CollectionEntry<'services'>[];
|
|
14
|
-
containers: CollectionEntry<'containers'>[];
|
|
9
|
+
type DataProductContext = Pick<
|
|
10
|
+
ResourceGroupContext,
|
|
11
|
+
'events' | 'commands' | 'queries' | 'services' | 'containers' | 'resourceDocs' | 'resourceDocCategories'
|
|
12
|
+
> & {
|
|
15
13
|
channels: CollectionEntry<'channels'>[];
|
|
16
|
-
}
|
|
14
|
+
};
|
|
17
15
|
|
|
18
16
|
// Get highest version from matched items (semver ranges may return multiple matches)
|
|
19
17
|
const getHighestVersion = <T extends { data: { version: string } }>(items: T[]): T | undefined => {
|
|
@@ -68,6 +66,13 @@ export const buildDataProductNode = (
|
|
|
68
66
|
|
|
69
67
|
const renderVisualiser = isVisualiserEnabled();
|
|
70
68
|
const renderOwners = owners.length > 0 && shouldRenderSideBarSection(dataProduct, 'owners');
|
|
69
|
+
const docsSection = buildResourceDocsSection(
|
|
70
|
+
'data-products',
|
|
71
|
+
dataProduct.data.id,
|
|
72
|
+
dataProduct.data.version,
|
|
73
|
+
context.resourceDocs,
|
|
74
|
+
context.resourceDocCategories
|
|
75
|
+
);
|
|
71
76
|
|
|
72
77
|
// Resolve inputs and outputs to their proper sidebar references
|
|
73
78
|
const resolvedInputs = inputs.map((input) => resolvePointerToRef(input, context)).filter(Boolean) as string[];
|
|
@@ -94,6 +99,7 @@ export const buildDataProductNode = (
|
|
|
94
99
|
buildQuickReferenceSection([
|
|
95
100
|
{ title: 'Overview', href: buildUrl(`/docs/data-products/${dataProduct.data.id}/${dataProduct.data.version}`) },
|
|
96
101
|
]),
|
|
102
|
+
docsSection,
|
|
97
103
|
renderVisualiser && {
|
|
98
104
|
type: 'group',
|
|
99
105
|
title: 'Architecture',
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
buildRepositorySection,
|
|
10
10
|
buildAttachmentsSection,
|
|
11
11
|
buildDiagramNavItems,
|
|
12
|
+
buildResourceDocsSection,
|
|
12
13
|
} from './shared';
|
|
13
14
|
import { isVisualiserEnabled } from '@utils/feature';
|
|
14
15
|
import { pluralizeMessageType } from '@utils/collections/messages';
|
|
@@ -41,6 +42,13 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
41
42
|
const hasAttachments = domain.data.attachments && domain.data.attachments.length > 0;
|
|
42
43
|
|
|
43
44
|
const renderRepository = domain.data.repository && shouldRenderSideBarSection(domain, 'repository');
|
|
45
|
+
const docsSection = buildResourceDocsSection(
|
|
46
|
+
'domains',
|
|
47
|
+
domain.data.id,
|
|
48
|
+
domain.data.version,
|
|
49
|
+
context.resourceDocs,
|
|
50
|
+
context.resourceDocCategories
|
|
51
|
+
);
|
|
44
52
|
|
|
45
53
|
// Domain-level messages (sends/receives)
|
|
46
54
|
const sendsMessages = domain.data.sends || [];
|
|
@@ -70,6 +78,7 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
70
78
|
{ title: 'Overview', href: buildUrl(`/docs/domains/${domain.data.id}/${domain.data.version}`) },
|
|
71
79
|
renderUbiquitousLanguage && { title: 'Ubiquitous Language', href: buildUrl(`/docs/domains/${domain.data.id}/language`) },
|
|
72
80
|
]),
|
|
81
|
+
docsSection,
|
|
73
82
|
{
|
|
74
83
|
type: 'group',
|
|
75
84
|
title: 'Architecture',
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { CollectionEntry } from 'astro:content';
|
|
2
2
|
import { buildUrl } from '@utils/url-builder';
|
|
3
|
-
import type { NavNode, ChildRef } from './shared';
|
|
4
|
-
import { buildQuickReferenceSection } from './shared';
|
|
3
|
+
import type { NavNode, ChildRef, ResourceGroupContext } from './shared';
|
|
4
|
+
import { buildQuickReferenceSection, buildResourceDocsSection } from './shared';
|
|
5
|
+
|
|
6
|
+
export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceGroupContext): NavNode => {
|
|
7
|
+
const docsSection = buildResourceDocsSection(
|
|
8
|
+
'flows',
|
|
9
|
+
flow.data.id,
|
|
10
|
+
flow.data.version,
|
|
11
|
+
context.resourceDocs,
|
|
12
|
+
context.resourceDocCategories
|
|
13
|
+
);
|
|
5
14
|
|
|
6
|
-
export const buildFlowNode = (flow: CollectionEntry<'flows'>): NavNode => {
|
|
7
15
|
return {
|
|
8
16
|
type: 'item',
|
|
9
17
|
title: flow.data.name,
|
|
@@ -12,6 +20,7 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>): NavNode => {
|
|
|
12
20
|
summary: flow.data.summary,
|
|
13
21
|
pages: [
|
|
14
22
|
buildQuickReferenceSection([{ title: 'Overview', href: buildUrl(`/docs/flows/${flow.data.id}/${flow.data.version}`) }]),
|
|
23
|
+
docsSection,
|
|
15
24
|
{
|
|
16
25
|
type: 'group',
|
|
17
26
|
title: 'Architecture',
|
|
@@ -24,6 +33,6 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>): NavNode => {
|
|
|
24
33
|
},
|
|
25
34
|
].filter(Boolean) as ChildRef[],
|
|
26
35
|
},
|
|
27
|
-
],
|
|
36
|
+
].filter(Boolean) as ChildRef[],
|
|
28
37
|
};
|
|
29
38
|
};
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
buildRepositorySection,
|
|
10
10
|
buildAttachmentsSection,
|
|
11
11
|
buildDiagramNavItems,
|
|
12
|
+
buildResourceDocsSection,
|
|
12
13
|
} from './shared';
|
|
13
14
|
import { isVisualiserEnabled } from '@utils/feature';
|
|
14
15
|
|
|
@@ -35,6 +36,13 @@ export const buildMessageNode = (
|
|
|
35
36
|
|
|
36
37
|
const hasSchema = message.data.schemaPath !== undefined;
|
|
37
38
|
const renderVisualiser = isVisualiserEnabled();
|
|
39
|
+
const docsSection = buildResourceDocsSection(
|
|
40
|
+
collection as 'events' | 'commands' | 'queries',
|
|
41
|
+
message.data.id,
|
|
42
|
+
message.data.version,
|
|
43
|
+
context.resourceDocs,
|
|
44
|
+
context.resourceDocCategories
|
|
45
|
+
);
|
|
38
46
|
|
|
39
47
|
const hasAttachments = message.data.attachments && message.data.attachments.length > 0;
|
|
40
48
|
|
|
@@ -57,6 +65,7 @@ export const buildMessageNode = (
|
|
|
57
65
|
href: buildUrl(`/docs/${collection}/${message.data.id}/${message.data.version}`),
|
|
58
66
|
},
|
|
59
67
|
]),
|
|
68
|
+
docsSection,
|
|
60
69
|
renderVisualiser && {
|
|
61
70
|
type: 'group',
|
|
62
71
|
title: 'Architecture',
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildRepositorySection,
|
|
12
12
|
buildAttachmentsSection,
|
|
13
13
|
buildDiagramNavItems,
|
|
14
|
+
buildResourceDocsSection,
|
|
14
15
|
} from './shared';
|
|
15
16
|
import { isVisualiserEnabled } from '@utils/feature';
|
|
16
17
|
import { pluralizeMessageType } from '@utils/collections/messages';
|
|
@@ -44,6 +45,13 @@ export const buildServiceNode = (service: CollectionEntry<'services'>, owners: a
|
|
|
44
45
|
const renderEntities = serviceEntities.length > 0 && shouldRenderSideBarSection(service, 'entities');
|
|
45
46
|
const renderOwners = owners.length > 0 && shouldRenderSideBarSection(service, 'owners');
|
|
46
47
|
const renderRepository = service.data.repository && shouldRenderSideBarSection(service, 'repository');
|
|
48
|
+
const docsSection = buildResourceDocsSection(
|
|
49
|
+
'services',
|
|
50
|
+
service.data.id,
|
|
51
|
+
service.data.version,
|
|
52
|
+
context.resourceDocs,
|
|
53
|
+
context.resourceDocCategories
|
|
54
|
+
);
|
|
47
55
|
|
|
48
56
|
// Diagrams
|
|
49
57
|
const serviceDiagrams = service.data.diagrams || [];
|
|
@@ -59,6 +67,7 @@ export const buildServiceNode = (service: CollectionEntry<'services'>, owners: a
|
|
|
59
67
|
buildQuickReferenceSection([
|
|
60
68
|
{ title: 'Overview', href: buildUrl(`/docs/services/${service.data.id}/${service.data.version}`) },
|
|
61
69
|
]),
|
|
70
|
+
docsSection,
|
|
62
71
|
{
|
|
63
72
|
type: 'group',
|
|
64
73
|
title: 'Architecture',
|
|
@@ -2,6 +2,12 @@ import type { ResourceGroup } from '@eventcatalog/sdk';
|
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
3
|
import { getLatestVersionInCollectionById } from '@utils/collections/util';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import {
|
|
6
|
+
getGroupedResourceDocsByType,
|
|
7
|
+
type ResourceCollection,
|
|
8
|
+
type ResourceDocEntry,
|
|
9
|
+
type ResourceDocCategoryEntry,
|
|
10
|
+
} from '@utils/collections/resource-docs';
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* A child reference can be:
|
|
@@ -17,6 +23,7 @@ export type NavNode = {
|
|
|
17
23
|
type: 'group' | 'item';
|
|
18
24
|
title: string;
|
|
19
25
|
icon?: string; // Lucide icon name
|
|
26
|
+
subtle?: boolean; // Render lightweight styling for nested subgroup headers
|
|
20
27
|
leftIcon?: string; // Path to SVG icon shown on the left of the label
|
|
21
28
|
href?: string; // URL (for leaf items)
|
|
22
29
|
external?: boolean; // If true, the item will open in a new tab
|
|
@@ -55,6 +62,8 @@ export type ResourceGroupContext = {
|
|
|
55
62
|
flows: CollectionEntry<'flows'>[];
|
|
56
63
|
containers: CollectionEntry<'containers'>[];
|
|
57
64
|
diagrams: CollectionEntry<'diagrams'>[];
|
|
65
|
+
resourceDocs: ResourceDocEntry[];
|
|
66
|
+
resourceDocCategories: ResourceDocCategoryEntry[];
|
|
58
67
|
};
|
|
59
68
|
|
|
60
69
|
export const buildQuickReferenceSection = (items: { title: string; href: string }[]): NavNode => ({
|
|
@@ -112,6 +121,79 @@ export const buildAttachmentsSection = (attachments: any[]): NavNode | null => {
|
|
|
112
121
|
})),
|
|
113
122
|
};
|
|
114
123
|
};
|
|
124
|
+
|
|
125
|
+
export const buildResourceDocsSection = (
|
|
126
|
+
collection: ResourceCollection,
|
|
127
|
+
id: string,
|
|
128
|
+
version: string,
|
|
129
|
+
resourceDocs: ResourceDocEntry[],
|
|
130
|
+
resourceDocCategories: ResourceDocCategoryEntry[]
|
|
131
|
+
): NavNode | null => {
|
|
132
|
+
const docsForResource = resourceDocs.filter(
|
|
133
|
+
(doc) => doc.data.resourceCollection === collection && doc.data.resourceId === id && doc.data.resourceVersion === version
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
if (docsForResource.length === 0) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const categoriesForResource = resourceDocCategories.filter(
|
|
141
|
+
(category) =>
|
|
142
|
+
category.data.resourceCollection === collection &&
|
|
143
|
+
category.data.resourceId === id &&
|
|
144
|
+
category.data.resourceVersion === version
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const groupedDocs = getGroupedResourceDocsByType(docsForResource, {
|
|
148
|
+
latestOnly: true,
|
|
149
|
+
categories: categoriesForResource,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (groupedDocs.length === 0) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const typeLabelMap: Record<string, string> = {
|
|
157
|
+
adrs: 'ADR',
|
|
158
|
+
runbooks: 'Runbook',
|
|
159
|
+
contracts: 'Contract',
|
|
160
|
+
troubleshooting: 'Troubleshooting',
|
|
161
|
+
guides: 'Guide',
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const toTypeLabel = (value: string) => {
|
|
165
|
+
if (typeLabelMap[value]) {
|
|
166
|
+
return typeLabelMap[value];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const normalized = value
|
|
170
|
+
.split(/[-_\s]+/)
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
173
|
+
.join(' ');
|
|
174
|
+
|
|
175
|
+
return normalized || 'Doc';
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
type: 'group',
|
|
180
|
+
title: 'Documentation',
|
|
181
|
+
icon: 'BookText',
|
|
182
|
+
pages: groupedDocs.map((group) => ({
|
|
183
|
+
type: 'group',
|
|
184
|
+
title: group.label || toTypeLabel(group.type),
|
|
185
|
+
subtle: true,
|
|
186
|
+
pages: group.docs.map((doc) => ({
|
|
187
|
+
type: 'item',
|
|
188
|
+
title: doc.data.title || doc.data.id,
|
|
189
|
+
href: buildUrl(
|
|
190
|
+
`/docs/${collection}/${id}/${version}/${encodeURIComponent(doc.data.type)}/${encodeURIComponent(doc.data.id)}`
|
|
191
|
+
),
|
|
192
|
+
})),
|
|
193
|
+
})),
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
115
197
|
export const buildResourceGroupSections = (resourceGroups: ResourceGroup[], context: ResourceGroupContext) => {
|
|
116
198
|
return resourceGroups.map((resourceGroup) => buildResourceGroupSection(resourceGroup, context));
|
|
117
199
|
};
|
|
@@ -8,6 +8,7 @@ import { getUsers } from '@utils/collections/users';
|
|
|
8
8
|
import { getTeams } from '@utils/collections/teams';
|
|
9
9
|
import { getDiagrams } from '@utils/collections/diagrams';
|
|
10
10
|
import { getDataProducts } from '@utils/collections/data-products';
|
|
11
|
+
import { getResourceDocCategories, getResourceDocs } from '@utils/collections/resource-docs';
|
|
11
12
|
import { buildUrl } from '@utils/url-builder';
|
|
12
13
|
import type { NavigationData, NavNode, ChildRef } from './builders/shared';
|
|
13
14
|
import { buildDomainNode } from './builders/domain';
|
|
@@ -19,6 +20,7 @@ import { buildDataProductNode } from './builders/data-product';
|
|
|
19
20
|
import config from '@config';
|
|
20
21
|
import { getDesigns } from '@utils/collections/designs';
|
|
21
22
|
import { getChannels } from '@utils/collections/channels';
|
|
23
|
+
import { buildQuickReferenceSection, buildResourceDocsSection } from './builders/shared';
|
|
22
24
|
|
|
23
25
|
export type { NavigationData, NavNode, ChildRef };
|
|
24
26
|
|
|
@@ -45,6 +47,8 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
45
47
|
channels,
|
|
46
48
|
diagrams,
|
|
47
49
|
dataProducts,
|
|
50
|
+
resourceDocs,
|
|
51
|
+
resourceDocCategories,
|
|
48
52
|
] = await Promise.all([
|
|
49
53
|
getDomains({ getAllVersions: false, includeServicesInSubdomains: false }),
|
|
50
54
|
getServices({ getAllVersions: false }),
|
|
@@ -57,6 +61,8 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
57
61
|
getChannels({ getAllVersions: false }),
|
|
58
62
|
getDiagrams({ getAllVersions: false }),
|
|
59
63
|
getDataProducts({ getAllVersions: false }),
|
|
64
|
+
getResourceDocs(),
|
|
65
|
+
getResourceDocCategories(),
|
|
60
66
|
]);
|
|
61
67
|
|
|
62
68
|
// Calculate derived lists to avoid extra fetches
|
|
@@ -75,6 +81,8 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
75
81
|
containers,
|
|
76
82
|
diagrams,
|
|
77
83
|
dataProducts,
|
|
84
|
+
resourceDocs,
|
|
85
|
+
resourceDocCategories,
|
|
78
86
|
};
|
|
79
87
|
|
|
80
88
|
// Process all domains with their owners first (async)
|
|
@@ -122,7 +130,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
122
130
|
|
|
123
131
|
const flowNodes = flows.reduce(
|
|
124
132
|
(acc, flow) => {
|
|
125
|
-
acc[`flow:${flow.data.id}:${flow.data.version}`] = buildFlowNode(flow);
|
|
133
|
+
acc[`flow:${flow.data.id}:${flow.data.version}`] = buildFlowNode(flow, context);
|
|
126
134
|
return acc;
|
|
127
135
|
},
|
|
128
136
|
{} as Record<string, NavNode>
|
|
@@ -196,6 +204,8 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
196
204
|
services,
|
|
197
205
|
containers,
|
|
198
206
|
channels,
|
|
207
|
+
resourceDocs,
|
|
208
|
+
resourceDocCategories,
|
|
199
209
|
};
|
|
200
210
|
|
|
201
211
|
const dataProductNodes = dataProductWithOwners.reduce(
|
|
@@ -238,12 +248,27 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
238
248
|
const channelNodes = channels.reduce(
|
|
239
249
|
(acc, channel) => {
|
|
240
250
|
const versionedKey = `channel:${channel.data.id}:${channel.data.version}`;
|
|
251
|
+
const docsSection = buildResourceDocsSection(
|
|
252
|
+
'channels',
|
|
253
|
+
channel.data.id,
|
|
254
|
+
channel.data.version,
|
|
255
|
+
resourceDocs,
|
|
256
|
+
resourceDocCategories
|
|
257
|
+
);
|
|
241
258
|
acc[versionedKey] = {
|
|
242
259
|
type: 'item',
|
|
243
260
|
title: channel.data.name,
|
|
244
261
|
badge: 'Channel',
|
|
245
262
|
summary: channel.data.summary,
|
|
246
|
-
|
|
263
|
+
pages: [
|
|
264
|
+
buildQuickReferenceSection([
|
|
265
|
+
{
|
|
266
|
+
title: 'Overview',
|
|
267
|
+
href: buildUrl(`/docs/${channel.collection}/${channel.data.id}/${channel.data.version}`),
|
|
268
|
+
},
|
|
269
|
+
]),
|
|
270
|
+
docsSection,
|
|
271
|
+
].filter(Boolean) as ChildRef[],
|
|
247
272
|
};
|
|
248
273
|
|
|
249
274
|
if (channel.data.latestVersion === channel.data.version) {
|