@eventcatalog/core 4.3.2 → 4.3.4
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-BYH34SOZ.js → chunk-5L4FYIAY.js} +1 -1
- package/dist/{chunk-T6UEDS2J.js → chunk-CQQE7NTL.js} +1 -1
- package/dist/{chunk-W4UBACYZ.js → chunk-OPFRJOPV.js} +1 -1
- package/dist/{chunk-25RQCO2H.js → chunk-QFXV3GDK.js} +1 -1
- package/dist/{chunk-YBNWZEFQ.js → chunk-XTC5VFLE.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/api/02-config.md +24 -0
- package/dist/docs/cli/governance.md +1 -1
- package/dist/docs/cli/snapshots.md +1 -1
- package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +56 -7
- package/dist/docs/development/ask-your-architecture/03-mcp-server/introduction.md +8 -3
- package/dist/docs/development/deployment/build-ssr-mode.md +1 -9
- package/dist/docs/development/developer-tools/api-catalog.md +1 -1
- package/dist/docs/development/guides/98-versioning-resources.md +15 -0
- package/dist/docs/development/guides/resources/entities/03-model-entity-relationships.md +26 -6
- package/dist/docs/development/guides/resources/entities/05-entity-maps.md +2 -0
- package/dist/docs/development/guides/resources/entities/06-reference.md +25 -2
- package/dist/docs/development/guides/resources/services/07-versioning-and-lifecycle/01-version-services.md +2 -0
- package/dist/docs/plugins/asyncapi/02-plugin-configuration.md +30 -2
- package/dist/docs/plugins/asyncapi/03-features.md +39 -26
- package/dist/docs/plugins/asyncapi/03a-workflows.md +61 -2
- package/dist/docs/plugins/openapi/02-plugin-configuration.md +1 -2
- package/dist/docs/plugins/openapi/03-features.md +8 -6
- package/dist/docs/plugins/openapi/03a-workflows.md +79 -18
- 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/LatestVersionRedirect.astro +26 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +21 -27
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +16 -7
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.spec.ts +28 -5
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +15 -2
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +8 -24
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +148 -10
- package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +25 -0
- package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +25 -0
- package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +25 -0
- package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +25 -0
- package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +25 -0
- package/eventcatalog/src/pages/docs/services/_latest-version-route.ts +42 -0
- package/eventcatalog/src/stores/sidebar-store/builders/service.ts +9 -12
- package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +4 -2
- package/eventcatalog/src/stores/sidebar-store/state.ts +1 -1
- package/package.json +3 -3
- package/dist/docs/cli/import.md +0 -26
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { SectionCollapsePreferences } from './utils';
|
|
2
|
+
|
|
1
3
|
// ============================================
|
|
2
4
|
// Local Storage Persistence
|
|
3
5
|
// ============================================
|
|
4
6
|
|
|
5
7
|
const STORAGE_KEY = 'eventcatalog-sidebar-nav';
|
|
6
|
-
const
|
|
8
|
+
const SECTION_PREFERENCES_KEY = 'eventcatalog-sidebar-sections:v2';
|
|
7
9
|
const FAVORITES_KEY = 'eventcatalog-sidebar-favorites';
|
|
8
10
|
|
|
9
11
|
// ============================================
|
|
@@ -49,21 +51,28 @@ export const loadState = (): PersistedState | null => {
|
|
|
49
51
|
// Collapsed Sections
|
|
50
52
|
// ============================================
|
|
51
53
|
|
|
52
|
-
export const saveCollapsedSections = (
|
|
54
|
+
export const saveCollapsedSections = (preferences: SectionCollapsePreferences): void => {
|
|
53
55
|
try {
|
|
54
|
-
localStorage.setItem(
|
|
56
|
+
localStorage.setItem(SECTION_PREFERENCES_KEY, JSON.stringify({ expanded: [...preferences.expanded] }));
|
|
55
57
|
} catch (e) {
|
|
56
58
|
console.warn('Failed to save collapsed sections:', e);
|
|
57
59
|
}
|
|
58
60
|
};
|
|
59
61
|
|
|
60
|
-
export const loadCollapsedSections = ():
|
|
62
|
+
export const loadCollapsedSections = (): SectionCollapsePreferences => {
|
|
61
63
|
try {
|
|
62
|
-
const stored = localStorage.getItem(
|
|
63
|
-
|
|
64
|
+
const stored = localStorage.getItem(SECTION_PREFERENCES_KEY);
|
|
65
|
+
if (stored) {
|
|
66
|
+
const preferences = JSON.parse(stored);
|
|
67
|
+
return {
|
|
68
|
+
expanded: new Set(Array.isArray(preferences.expanded) ? preferences.expanded : []),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { expanded: new Set() };
|
|
64
73
|
} catch (e) {
|
|
65
74
|
console.warn('Failed to load collapsed sections:', e);
|
|
66
|
-
return new Set();
|
|
75
|
+
return { expanded: new Set() };
|
|
67
76
|
}
|
|
68
77
|
};
|
|
69
78
|
|
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { isGroupCollapsed } from './utils';
|
|
2
|
+
import { canCollapseGroup, getGroupLabel, isGroupCollapsed } from './utils';
|
|
3
|
+
|
|
4
|
+
const preferences = (expanded: string[] = []) => ({
|
|
5
|
+
expanded: new Set(expanded),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
describe('sidebar group presentation', () => {
|
|
9
|
+
it('includes the visible child count in the group label', () => {
|
|
10
|
+
expect(getGroupLabel('Outbound Messages', 10)).toBe('Outbound Messages (10)');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it.each(['Quick Reference', 'Architecture', 'Resources'])('does not include the child count for %s', (title) => {
|
|
14
|
+
expect(getGroupLabel(title, 10)).toBe(title);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('only allows groups with more than five children to collapse', () => {
|
|
18
|
+
expect(canCollapseGroup(5)).toBe(false);
|
|
19
|
+
expect(canCollapseGroup(6)).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
3
22
|
|
|
4
23
|
describe('isGroupCollapsed', () => {
|
|
5
|
-
it('
|
|
6
|
-
expect(isGroupCollapsed(false, 'group-1',
|
|
24
|
+
it('keeps groups without a caret expanded', () => {
|
|
25
|
+
expect(isGroupCollapsed(false, 'group-1', preferences())).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('collapses expandable groups by default', () => {
|
|
29
|
+
expect(isGroupCollapsed(true, 'outbound-messages', preferences())).toBe(true);
|
|
7
30
|
});
|
|
8
31
|
|
|
9
|
-
it('uses
|
|
10
|
-
expect(isGroupCollapsed(true, '
|
|
32
|
+
it('uses an explicit expanded preference', () => {
|
|
33
|
+
expect(isGroupCollapsed(true, 'outbound-messages', preferences(['outbound-messages']))).toBe(false);
|
|
11
34
|
});
|
|
12
35
|
});
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
// Shared utilities for NestedSideBar components
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
3
|
+
export const SIDEBAR_GROUP_COLLAPSE_THRESHOLD = 5;
|
|
4
|
+
const GROUP_TITLES_WITHOUT_COUNT = new Set(['Quick Reference', 'Architecture', 'Resources']);
|
|
5
|
+
|
|
6
|
+
export type SectionCollapsePreferences = {
|
|
7
|
+
expanded: Set<string>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const canCollapseGroup = (childCount: number): boolean => childCount > SIDEBAR_GROUP_COLLAPSE_THRESHOLD;
|
|
11
|
+
|
|
12
|
+
export const getGroupLabel = (title: string, childCount: number): string =>
|
|
13
|
+
GROUP_TITLES_WITHOUT_COUNT.has(title) ? title : `${title} (${childCount})`;
|
|
14
|
+
|
|
15
|
+
export const isGroupCollapsed = (canCollapse: boolean, groupId: string, preferences: SectionCollapsePreferences): boolean => {
|
|
16
|
+
return canCollapse && !preferences.expanded.has(groupId);
|
|
17
|
+
};
|
|
5
18
|
|
|
6
19
|
/**
|
|
7
20
|
* Returns Tailwind classes for badge styling based on badge type.
|
|
@@ -93,18 +93,15 @@ const editUrl =
|
|
|
93
93
|
(config.editUrl && (props as any)?.filePath ? buildEditUrlForResource(config.editUrl, (props as any).filePath) : '');
|
|
94
94
|
---
|
|
95
95
|
|
|
96
|
-
<VerticalSideBarLayout title={doc.title || 'Documentation'}
|
|
96
|
+
<VerticalSideBarLayout title={doc.title || 'Documentation'}>
|
|
97
|
+
<Fragment slot="sidebar-content">
|
|
98
|
+
<div class="h-full min-h-0">
|
|
99
|
+
<CustomDocsNav />
|
|
100
|
+
</div>
|
|
101
|
+
</Fragment>
|
|
102
|
+
|
|
97
103
|
<div class="custom-docs-shell flex w-full" data-pagefind-body data-pagefind-meta={`title:${doc.title}`}>
|
|
98
|
-
|
|
99
|
-
<aside
|
|
100
|
-
class="sidebar-transition fixed top-0 bottom-0 left-[var(--ec-vertical-nav-width,14rem)] z-10 w-[var(--ec-custom-docs-sidebar-width,20rem)] overflow-hidden border-r border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-rail-bg))]"
|
|
101
|
-
>
|
|
102
|
-
<div class="h-full">
|
|
103
|
-
<CustomDocsNav />
|
|
104
|
-
</div>
|
|
105
|
-
</aside>
|
|
106
|
-
|
|
107
|
-
<div class="sidebar-transition flex w-full min-w-0" style="margin-left: var(--ec-custom-docs-sidebar-width, 20rem);">
|
|
104
|
+
<div class="flex w-full min-w-0">
|
|
108
105
|
<main class="min-w-0 flex-1">
|
|
109
106
|
<div
|
|
110
107
|
class="w-full lg:mr-2 pr-10 py-10 bg-[rgb(var(--ec-page-bg))]"
|
|
@@ -275,19 +272,6 @@ const editUrl =
|
|
|
275
272
|
</VerticalSideBarLayout>
|
|
276
273
|
|
|
277
274
|
<style is:global>
|
|
278
|
-
:root {
|
|
279
|
-
--ec-custom-docs-sidebar-width: 20rem;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
#eventcatalog-header {
|
|
283
|
-
left: calc(var(--ec-vertical-nav-width, 14rem) + var(--ec-custom-docs-sidebar-width, 20rem)) !important;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.custom-docs-shell {
|
|
287
|
-
margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1);
|
|
288
|
-
margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
275
|
.mermaid svg {
|
|
292
276
|
margin: 1em auto 2em;
|
|
293
277
|
}
|
|
@@ -449,9 +449,25 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
449
449
|
}
|
|
450
450
|
})();
|
|
451
451
|
</script>
|
|
452
|
+
<script is:inline>
|
|
453
|
+
(() => {
|
|
454
|
+
const sidebarWidthStorageKey = 'eventcatalog-sidebar-width:v1';
|
|
455
|
+
const minimumSidebarWidth = 224;
|
|
456
|
+
const maximumSidebarWidth = 576;
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
const savedWidth = Number(localStorage.getItem(sidebarWidthStorageKey));
|
|
460
|
+
if (Number.isFinite(savedWidth) && savedWidth > 0) {
|
|
461
|
+
const width = Math.min(maximumSidebarWidth, Math.max(minimumSidebarWidth, savedWidth));
|
|
462
|
+
document.documentElement.style.setProperty('--ec-sidebar-panel-width', `${width}px`);
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {}
|
|
465
|
+
})();
|
|
466
|
+
</script>
|
|
452
467
|
<style is:global>
|
|
453
468
|
:root {
|
|
454
469
|
--ec-vertical-nav-width: 14rem;
|
|
470
|
+
--ec-sidebar-panel-width: 17rem;
|
|
455
471
|
}
|
|
456
472
|
|
|
457
473
|
:root[data-vertical-nav-collapsed='true'] {
|
|
@@ -472,7 +488,6 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
472
488
|
min-height: 100vh;
|
|
473
489
|
min-height: 100dvh;
|
|
474
490
|
background-color: rgb(var(--ec-page-bg));
|
|
475
|
-
--ec-sidebar-panel-width: 17rem;
|
|
476
491
|
display: flex;
|
|
477
492
|
flex-direction: column;
|
|
478
493
|
}
|
|
@@ -580,7 +595,44 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
580
595
|
top: 0;
|
|
581
596
|
left: var(--ec-vertical-nav-width);
|
|
582
597
|
z-index: 20;
|
|
583
|
-
overflow
|
|
598
|
+
overflow: hidden;
|
|
599
|
+
}
|
|
600
|
+
.sidebar-resize-handle {
|
|
601
|
+
position: absolute;
|
|
602
|
+
top: 0;
|
|
603
|
+
right: 0;
|
|
604
|
+
z-index: 30;
|
|
605
|
+
width: 8px;
|
|
606
|
+
height: 100%;
|
|
607
|
+
cursor: col-resize;
|
|
608
|
+
touch-action: none;
|
|
609
|
+
}
|
|
610
|
+
.sidebar-resize-handle::after {
|
|
611
|
+
position: absolute;
|
|
612
|
+
top: 0;
|
|
613
|
+
bottom: 0;
|
|
614
|
+
left: 50%;
|
|
615
|
+
width: 2px;
|
|
616
|
+
content: '';
|
|
617
|
+
background: rgb(var(--ec-accent) / 0.65);
|
|
618
|
+
opacity: 0;
|
|
619
|
+
transform: translateX(-50%);
|
|
620
|
+
transition: opacity 150ms ease;
|
|
621
|
+
}
|
|
622
|
+
.sidebar-resize-handle:hover::after,
|
|
623
|
+
.sidebar-resize-handle:focus-visible::after,
|
|
624
|
+
body.sidebar-is-resizing .sidebar-resize-handle::after {
|
|
625
|
+
opacity: 1;
|
|
626
|
+
}
|
|
627
|
+
.sidebar-resize-handle:focus-visible {
|
|
628
|
+
outline: none;
|
|
629
|
+
}
|
|
630
|
+
body.sidebar-is-resizing {
|
|
631
|
+
cursor: col-resize;
|
|
632
|
+
user-select: none;
|
|
633
|
+
}
|
|
634
|
+
body.sidebar-is-resizing .sidebar-transition {
|
|
635
|
+
transition: none;
|
|
584
636
|
}
|
|
585
637
|
.content-panel {
|
|
586
638
|
min-width: 0;
|
|
@@ -754,14 +806,22 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
754
806
|
</div>
|
|
755
807
|
</aside>
|
|
756
808
|
{
|
|
757
|
-
showNestedSideBar &&
|
|
758
|
-
|
|
759
|
-
<
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
809
|
+
showNestedSideBar && (
|
|
810
|
+
<aside id="sidebar" class="sidebar-panel sidebar-transition">
|
|
811
|
+
{Astro.slots.has('sidebar-content') ? <slot name="sidebar-content" /> : <SideNav class="h-full min-h-0" />}
|
|
812
|
+
<div
|
|
813
|
+
id="sidebar-resize-handle"
|
|
814
|
+
class="sidebar-resize-handle"
|
|
815
|
+
role="separator"
|
|
816
|
+
aria-label="Resize sidebar"
|
|
817
|
+
aria-orientation="vertical"
|
|
818
|
+
aria-valuemin="224"
|
|
819
|
+
aria-valuemax="576"
|
|
820
|
+
aria-valuenow="272"
|
|
821
|
+
tabindex="0"
|
|
822
|
+
/>
|
|
823
|
+
</aside>
|
|
824
|
+
)
|
|
765
825
|
}
|
|
766
826
|
<main class="content-panel sidebar-transition w-full bg-[rgb(var(--ec-page-bg))]" id="content">
|
|
767
827
|
{
|
|
@@ -803,6 +863,9 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
803
863
|
}}
|
|
804
864
|
>
|
|
805
865
|
const VERTICAL_NAV_STORAGE_KEY = 'eventcatalog-vertical-nav-collapsed';
|
|
866
|
+
const SIDEBAR_WIDTH_STORAGE_KEY = 'eventcatalog-sidebar-width:v1';
|
|
867
|
+
const MINIMUM_SIDEBAR_WIDTH = 224;
|
|
868
|
+
const MAXIMUM_SIDEBAR_WIDTH = 576;
|
|
806
869
|
const ACTIVE_NAV_CLASSES = [
|
|
807
870
|
'border-[rgb(var(--ec-accent)/0.2)]',
|
|
808
871
|
'bg-[rgb(var(--ec-page-bg)/0.88)]',
|
|
@@ -926,6 +989,80 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
926
989
|
}
|
|
927
990
|
};
|
|
928
991
|
|
|
992
|
+
const clampSidebarWidth = (width) => Math.min(MAXIMUM_SIDEBAR_WIDTH, Math.max(MINIMUM_SIDEBAR_WIDTH, width));
|
|
993
|
+
|
|
994
|
+
const applySidebarWidth = (width) => {
|
|
995
|
+
const nextWidth = clampSidebarWidth(width);
|
|
996
|
+
document.documentElement.style.setProperty('--ec-sidebar-panel-width', `${nextWidth}px`);
|
|
997
|
+
|
|
998
|
+
const resizeHandle = document.getElementById('sidebar-resize-handle');
|
|
999
|
+
resizeHandle?.setAttribute('aria-valuenow', String(Math.round(nextWidth)));
|
|
1000
|
+
return nextWidth;
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
const persistSidebarWidth = (width) => {
|
|
1004
|
+
try {
|
|
1005
|
+
localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(Math.round(width)));
|
|
1006
|
+
} catch (error) {}
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
const getPersistedSidebarWidth = () => {
|
|
1010
|
+
try {
|
|
1011
|
+
const savedWidth = Number(localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY));
|
|
1012
|
+
return Number.isFinite(savedWidth) && savedWidth > 0 ? clampSidebarWidth(savedWidth) : null;
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
const setupSidebarResize = () => {
|
|
1019
|
+
const sidebar = document.getElementById('sidebar');
|
|
1020
|
+
const resizeHandle = document.getElementById('sidebar-resize-handle');
|
|
1021
|
+
if (!sidebar || !resizeHandle) return;
|
|
1022
|
+
|
|
1023
|
+
const persistedWidth = getPersistedSidebarWidth();
|
|
1024
|
+
const currentWidth = persistedWidth ?? sidebar.getBoundingClientRect().width;
|
|
1025
|
+
applySidebarWidth(currentWidth);
|
|
1026
|
+
|
|
1027
|
+
resizeHandle.onpointerdown = (event) => {
|
|
1028
|
+
if (event.button !== 0) return;
|
|
1029
|
+
|
|
1030
|
+
const startX = event.clientX;
|
|
1031
|
+
const startWidth = sidebar.getBoundingClientRect().width;
|
|
1032
|
+
document.body.classList.add('sidebar-is-resizing');
|
|
1033
|
+
resizeHandle.setPointerCapture(event.pointerId);
|
|
1034
|
+
|
|
1035
|
+
resizeHandle.onpointermove = (moveEvent) => {
|
|
1036
|
+
applySidebarWidth(startWidth + moveEvent.clientX - startX);
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
const finishResize = (upEvent) => {
|
|
1040
|
+
const finalWidth = applySidebarWidth(startWidth + upEvent.clientX - startX);
|
|
1041
|
+
persistSidebarWidth(finalWidth);
|
|
1042
|
+
document.body.classList.remove('sidebar-is-resizing');
|
|
1043
|
+
if (resizeHandle.hasPointerCapture(upEvent.pointerId)) {
|
|
1044
|
+
resizeHandle.releasePointerCapture(upEvent.pointerId);
|
|
1045
|
+
}
|
|
1046
|
+
resizeHandle.onpointermove = null;
|
|
1047
|
+
resizeHandle.onpointerup = null;
|
|
1048
|
+
resizeHandle.onpointercancel = null;
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
resizeHandle.onpointerup = finishResize;
|
|
1052
|
+
resizeHandle.onpointercancel = finishResize;
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
resizeHandle.onkeydown = (event) => {
|
|
1056
|
+
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
|
|
1057
|
+
|
|
1058
|
+
event.preventDefault();
|
|
1059
|
+
const direction = event.key === 'ArrowLeft' ? -1 : 1;
|
|
1060
|
+
const step = event.shiftKey ? 32 : 16;
|
|
1061
|
+
const nextWidth = applySidebarWidth(sidebar.getBoundingClientRect().width + direction * step);
|
|
1062
|
+
persistSidebarWidth(nextWidth);
|
|
1063
|
+
};
|
|
1064
|
+
};
|
|
1065
|
+
|
|
929
1066
|
// Apply immediately when this script executes so transitions don't briefly re-open the rail.
|
|
930
1067
|
applyPersistedVerticalNavState();
|
|
931
1068
|
|
|
@@ -1015,6 +1152,7 @@ const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/do
|
|
|
1015
1152
|
}
|
|
1016
1153
|
|
|
1017
1154
|
setVerticalNavCollapsedState(getPersistedVerticalNavCollapsedState());
|
|
1155
|
+
setupSidebarResize();
|
|
1018
1156
|
|
|
1019
1157
|
syncNavItemStates();
|
|
1020
1158
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
|
|
3
|
+
import { isResourceDocsEnabled, isSSR } from '@utils/feature';
|
|
4
|
+
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { Page as VersionedPage } from '../../../../[type]/[id]/[version]/[docType]/[docId]/_index.data';
|
|
6
|
+
import { getLatestServicePaths, getLatestServiceVersion } from '../../../_latest-version-route';
|
|
7
|
+
|
|
8
|
+
export const prerender = !isSSR();
|
|
9
|
+
export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
|
|
10
|
+
|
|
11
|
+
const { id, docType, docId } = Astro.params;
|
|
12
|
+
const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
|
|
13
|
+
|
|
14
|
+
if (!isResourceDocsEnabled() || !id || !docType || !docId || !version) {
|
|
15
|
+
throw new Response(null, { status: 404, statusText: 'Resource documentation not found' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const redirectUrl = buildUrl(`/docs/services/${id}/${version}/${docType}/${docId}`);
|
|
19
|
+
|
|
20
|
+
if (isSSR()) {
|
|
21
|
+
return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
|
|
22
|
+
}
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<LatestVersionRedirect title="Latest service documentation" redirectUrl={redirectUrl} />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
|
|
3
|
+
import { isSSR } from '@utils/feature';
|
|
4
|
+
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { Page as VersionedPage } from '../../../[type]/[id]/[version]/asyncapi/_[filename].data';
|
|
6
|
+
import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
|
|
7
|
+
|
|
8
|
+
export const prerender = !isSSR();
|
|
9
|
+
export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
|
|
10
|
+
|
|
11
|
+
const { id, filename } = Astro.params;
|
|
12
|
+
const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
|
|
13
|
+
|
|
14
|
+
if (!id || !filename || !version) {
|
|
15
|
+
throw new Response(null, { status: 404, statusText: 'AsyncAPI specification not found' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const redirectUrl = buildUrl(`/docs/services/${id}/${version}/asyncapi/${filename}`);
|
|
19
|
+
|
|
20
|
+
if (isSSR()) {
|
|
21
|
+
return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
|
|
22
|
+
}
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<LatestVersionRedirect title="Latest AsyncAPI specification" redirectUrl={redirectUrl} />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
|
|
3
|
+
import { isChangelogEnabled, isSSR } from '@utils/feature';
|
|
4
|
+
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { Page as VersionedPage } from '../../../[type]/[id]/[version]/changelog/_index.data';
|
|
6
|
+
import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
|
|
7
|
+
|
|
8
|
+
export const prerender = !isSSR();
|
|
9
|
+
export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
|
|
10
|
+
|
|
11
|
+
const { id } = Astro.params;
|
|
12
|
+
const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
|
|
13
|
+
|
|
14
|
+
if (!isChangelogEnabled() || !id || !version) {
|
|
15
|
+
throw new Response(null, { status: 404, statusText: 'Changelog not found' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const redirectUrl = buildUrl(`/docs/services/${id}/${version}/changelog`);
|
|
19
|
+
|
|
20
|
+
if (isSSR()) {
|
|
21
|
+
return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
|
|
22
|
+
}
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<LatestVersionRedirect title="Latest service changelog" redirectUrl={redirectUrl} />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
|
|
3
|
+
import { isSSR } from '@utils/feature';
|
|
4
|
+
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { Page as VersionedPage } from '../../../[type]/[id]/[version]/graphql/_[filename].data';
|
|
6
|
+
import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
|
|
7
|
+
|
|
8
|
+
export const prerender = !isSSR();
|
|
9
|
+
export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
|
|
10
|
+
|
|
11
|
+
const { id, filename } = Astro.params;
|
|
12
|
+
const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
|
|
13
|
+
|
|
14
|
+
if (!id || !filename || !version) {
|
|
15
|
+
throw new Response(null, { status: 404, statusText: 'GraphQL specification not found' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const redirectUrl = buildUrl(`/docs/services/${id}/${version}/graphql/${filename}`);
|
|
19
|
+
|
|
20
|
+
if (isSSR()) {
|
|
21
|
+
return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
|
|
22
|
+
}
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<LatestVersionRedirect title="Latest GraphQL specification" redirectUrl={redirectUrl} />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
|
|
3
|
+
import { isSSR } from '@utils/feature';
|
|
4
|
+
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { Page as VersionedPage } from '../../../[type]/[id]/[version]/spec/_[filename].data';
|
|
6
|
+
import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
|
|
7
|
+
|
|
8
|
+
export const prerender = !isSSR();
|
|
9
|
+
export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
|
|
10
|
+
|
|
11
|
+
const { id, filename } = Astro.params;
|
|
12
|
+
const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
|
|
13
|
+
|
|
14
|
+
if (!id || !filename || !version) {
|
|
15
|
+
throw new Response(null, { status: 404, statusText: 'OpenAPI specification not found' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const redirectUrl = buildUrl(`/docs/services/${id}/${version}/spec/${filename}`);
|
|
19
|
+
|
|
20
|
+
if (isSSR()) {
|
|
21
|
+
return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
|
|
22
|
+
}
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<LatestVersionRedirect title="Latest OpenAPI specification" redirectUrl={redirectUrl} />
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CollectionEntry } from 'astro:content';
|
|
2
|
+
|
|
3
|
+
type StaticPath = {
|
|
4
|
+
params: Record<string, string | undefined>;
|
|
5
|
+
props?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type LatestService = CollectionEntry<'services'>;
|
|
9
|
+
|
|
10
|
+
export const toLatestServicePaths = (paths: StaticPath[], latestServices: LatestService[]): StaticPath[] => {
|
|
11
|
+
const latestVersions = new Map(latestServices.map((service) => [service.data.id, service.data.version]));
|
|
12
|
+
|
|
13
|
+
return paths.flatMap(({ params, props = {} }) => {
|
|
14
|
+
const { type, version, ...aliasParams } = params;
|
|
15
|
+
|
|
16
|
+
if (type !== 'services' || !params.id || latestVersions.get(params.id) !== version) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
params: aliasParams,
|
|
23
|
+
props: {
|
|
24
|
+
...props,
|
|
25
|
+
redirectVersion: version,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getLatestServicePaths = async (paths: StaticPath[]): Promise<StaticPath[]> => {
|
|
33
|
+
const { getServices } = await import('@utils/collections/services');
|
|
34
|
+
const latestServices = await getServices({ getAllVersions: false });
|
|
35
|
+
return toLatestServicePaths(paths, latestServices);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const getLatestServiceVersion = async (id: string): Promise<string | undefined> => {
|
|
39
|
+
const { getServices } = await import('@utils/collections/services');
|
|
40
|
+
const latestServices = await getServices({ getAllVersions: false });
|
|
41
|
+
return latestServices.find((service) => service.data.id === id)?.data.version;
|
|
42
|
+
};
|
|
@@ -58,12 +58,15 @@ export const buildServiceNode = (
|
|
|
58
58
|
const renderEntities = serviceEntities.length > 0 && shouldRenderSideBarSection(service, 'entities');
|
|
59
59
|
const renderOwners = owners.length > 0 && shouldRenderSideBarSection(service, 'owners');
|
|
60
60
|
const renderRepository = service.data.repository && shouldRenderSideBarSection(service, 'repository');
|
|
61
|
+
const isLatestVersion = service.data.version === service.data.latestVersion;
|
|
62
|
+
const docsBasePath = `/docs/services/${service.data.id}${isLatestVersion ? '' : `/${service.data.version}`}`;
|
|
61
63
|
const docsSection = buildResourceDocsSection(
|
|
62
64
|
'services',
|
|
63
65
|
service.data.id,
|
|
64
66
|
service.data.version,
|
|
65
67
|
context.resourceDocs,
|
|
66
|
-
context.resourceDocCategories
|
|
68
|
+
context.resourceDocCategories,
|
|
69
|
+
{ includeVersionInUrl: !isLatestVersion }
|
|
67
70
|
);
|
|
68
71
|
|
|
69
72
|
// Diagrams
|
|
@@ -82,11 +85,11 @@ export const buildServiceNode = (
|
|
|
82
85
|
pages: [
|
|
83
86
|
buildQuickReferenceSection(
|
|
84
87
|
[
|
|
85
|
-
{ title: 'Overview', href: buildUrl(
|
|
88
|
+
{ title: 'Overview', href: buildUrl(docsBasePath) },
|
|
86
89
|
isChangelogEnabled() &&
|
|
87
90
|
shouldRenderSideBarSection(service, 'changelog') && {
|
|
88
91
|
title: 'Changelog',
|
|
89
|
-
href: buildUrl(
|
|
92
|
+
href: buildUrl(`${docsBasePath}/changelog`),
|
|
90
93
|
},
|
|
91
94
|
].filter(Boolean) as { title: string; href: string }[]
|
|
92
95
|
),
|
|
@@ -135,25 +138,19 @@ export const buildServiceNode = (
|
|
|
135
138
|
type: 'item',
|
|
136
139
|
title: `${specification.name}`,
|
|
137
140
|
leftIcon: '/icons/openapi-black.svg',
|
|
138
|
-
href: buildUrl(
|
|
139
|
-
`/docs/services/${service.data.id}/${service.data.version}/spec/${specification.filenameWithoutExtension}`
|
|
140
|
-
),
|
|
141
|
+
href: buildUrl(`${docsBasePath}/spec/${specification.filenameWithoutExtension}`),
|
|
141
142
|
})),
|
|
142
143
|
...asyncAPISpecifications.map((specification) => ({
|
|
143
144
|
type: 'item',
|
|
144
145
|
title: `${specification.name}`,
|
|
145
146
|
leftIcon: '/icons/asyncapi-black.svg',
|
|
146
|
-
href: buildUrl(
|
|
147
|
-
`/docs/services/${service.data.id}/${service.data.version}/asyncapi/${specification.filenameWithoutExtension}`
|
|
148
|
-
),
|
|
147
|
+
href: buildUrl(`${docsBasePath}/asyncapi/${specification.filenameWithoutExtension}`),
|
|
149
148
|
})),
|
|
150
149
|
...graphQLSpecifications.map((specification) => ({
|
|
151
150
|
type: 'item',
|
|
152
151
|
title: `${specification.name}`,
|
|
153
152
|
leftIcon: '/icons/graphql-black.svg',
|
|
154
|
-
href: buildUrl(
|
|
155
|
-
`/docs/services/${service.data.id}/${service.data.version}/graphql/${specification.filenameWithoutExtension}`
|
|
156
|
-
),
|
|
153
|
+
href: buildUrl(`${docsBasePath}/graphql/${specification.filenameWithoutExtension}`),
|
|
157
154
|
})),
|
|
158
155
|
],
|
|
159
156
|
},
|
|
@@ -189,8 +189,10 @@ export const buildResourceDocsSection = (
|
|
|
189
189
|
id: string,
|
|
190
190
|
version: string,
|
|
191
191
|
resourceDocs: ResourceDocEntry[],
|
|
192
|
-
resourceDocCategories: ResourceDocCategoryEntry[]
|
|
192
|
+
resourceDocCategories: ResourceDocCategoryEntry[],
|
|
193
|
+
options: { includeVersionInUrl?: boolean } = {}
|
|
193
194
|
): NavNode | null => {
|
|
195
|
+
const { includeVersionInUrl = true } = options;
|
|
194
196
|
const docsForResource = resourceDocs.filter(
|
|
195
197
|
(doc) => doc.data.resourceCollection === collection && doc.data.resourceId === id && doc.data.resourceVersion === version
|
|
196
198
|
);
|
|
@@ -249,7 +251,7 @@ export const buildResourceDocsSection = (
|
|
|
249
251
|
type: 'item',
|
|
250
252
|
title: doc.data.title || doc.data.id,
|
|
251
253
|
href: buildUrl(
|
|
252
|
-
`/docs/${collection}/${id}
|
|
254
|
+
`/docs/${collection}/${id}${includeVersionInUrl ? `/${version}` : ''}/${encodeURIComponent(doc.data.type)}/${encodeURIComponent(doc.data.id)}`
|
|
253
255
|
),
|
|
254
256
|
})),
|
|
255
257
|
})),
|
|
@@ -68,7 +68,7 @@ const groupAdrsByStatus = (adrs: Adr[]): NavNode[] =>
|
|
|
68
68
|
|
|
69
69
|
groups.push({
|
|
70
70
|
type: 'group',
|
|
71
|
-
title:
|
|
71
|
+
title: formatAdrStatus(status),
|
|
72
72
|
collapseKey: `adrs:status:${status}`,
|
|
73
73
|
subtle: true,
|
|
74
74
|
pages: [...adrsForStatus].sort(byResourceName).map(getAdrNodeKey),
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "4.3.
|
|
10
|
+
"version": "4.3.4",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
"update-notifier": "^7.3.1",
|
|
117
117
|
"uuid": "^10.0.0",
|
|
118
118
|
"zod": "^4.3.6",
|
|
119
|
-
"@eventcatalog/
|
|
119
|
+
"@eventcatalog/sdk": "2.26.3",
|
|
120
120
|
"@eventcatalog/visualiser": "^4.1.1",
|
|
121
|
-
"@eventcatalog/
|
|
121
|
+
"@eventcatalog/linter": "1.1.8"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@astrojs/check": "^0.9.9",
|