@eventcatalog/core 3.0.0-beta.2 → 3.0.0-beta.21
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/README.md +10 -0
- package/dist/__mocks__/astro-content.cjs +32 -0
- package/dist/__mocks__/astro-content.d.cts +13 -0
- package/dist/__mocks__/astro-content.d.ts +13 -0
- package/dist/__mocks__/astro-content.js +7 -0
- 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-JSONCD7V.js → chunk-2FUEBPD3.js} +1 -1
- package/dist/{chunk-3W6JYTHP.js → chunk-HABY2LVH.js} +6 -2
- package/dist/{chunk-H4QHE5YZ.js → chunk-KQAMO3R4.js} +1 -1
- package/dist/chunk-Q6KRYWPV.js +44 -0
- package/dist/{chunk-PQL6O5YA.js → chunk-RRP2B7BL.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +84 -65
- package/dist/eventcatalog.config.d.cts +4 -0
- package/dist/eventcatalog.config.d.ts +4 -0
- package/dist/eventcatalog.js +45 -57
- package/dist/generate.cjs +48 -2
- package/dist/generate.js +3 -1
- package/dist/utils/cli-logger.cjs +82 -0
- package/dist/utils/cli-logger.d.cts +10 -0
- package/dist/utils/cli-logger.d.ts +10 -0
- package/dist/utils/cli-logger.js +7 -0
- package/eventcatalog/astro.config.mjs +4 -1
- package/eventcatalog/integrations/ecstudio-watcher.mjs +1 -1
- package/eventcatalog/integrations/eventcatalog-features.ts +69 -0
- package/eventcatalog/public/icons/asyncapi-black.svg +2 -0
- package/eventcatalog/public/icons/graphql-black.svg +1 -0
- package/eventcatalog/public/icons/openapi-black.svg +1 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +821 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanelButton.tsx +24 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +1 -3
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +8 -8
- package/eventcatalog/src/components/Header.astro +25 -5
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +14 -3
- package/eventcatalog/src/components/Search/Search.astro +2 -2
- package/eventcatalog/src/components/Search/SearchModal.tsx +16 -7
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +9 -2
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/domain.ts +7 -6
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/service.ts +6 -3
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/shared.ts +1 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +23 -8
- package/eventcatalog/src/components/SideNav/NestedSideBar/sidebar-builder.ts +57 -11
- package/eventcatalog/src/content.config.ts +1 -10
- package/eventcatalog/src/enterprise/ai/chat-api.ts +262 -0
- package/eventcatalog/src/enterprise/auth/[...auth].ts +3 -0
- package/eventcatalog/src/enterprise/auth/login.astro +420 -0
- package/eventcatalog/src/enterprise/collections/index.ts +0 -1
- package/eventcatalog/src/layouts/Footer.astro +8 -5
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +30 -19
- package/eventcatalog/src/pages/_index.astro +8 -9
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +19 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +7 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +5 -5
- package/eventcatalog/src/pages/docs/teams/[id].mdx.ts +36 -0
- package/eventcatalog/src/pages/docs/users/[id].mdx.ts +36 -0
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +178 -0
- package/eventcatalog/src/pages/schemas/explorer/index.astro +5 -155
- package/eventcatalog/src/remark-plugins/directives.ts +30 -9
- package/eventcatalog/src/utils/collections/schemas.ts +31 -7
- package/eventcatalog/src/utils/feature.ts +8 -4
- package/eventcatalog/src/utils/resource-files.ts +86 -0
- package/package.json +12 -15
- package/default-files-for-collections/changelogs.md +0 -5
- package/default-files-for-collections/channels.md +0 -8
- package/default-files-for-collections/commands.md +0 -8
- package/default-files-for-collections/domains.md +0 -8
- package/default-files-for-collections/events.md +0 -8
- package/default-files-for-collections/flows.md +0 -11
- package/default-files-for-collections/queries.md +0 -8
- package/default-files-for-collections/services.md +0 -8
- package/default-files-for-collections/ubiquitousLanguages.md +0 -7
- package/eventcatalog/src/enterprise/collections/chat-prompts.ts +0 -32
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/Chat.tsx +0 -60
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatMessage.tsx +0 -414
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatSidebar.tsx +0 -169
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/InputModal.tsx +0 -244
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/MentionInput.tsx +0 -211
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/WelcomePromptArea.tsx +0 -176
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/default-prompts.ts +0 -93
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/hooks/ChatProvider.tsx +0 -143
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/windows/ChatWindow.server.tsx +0 -387
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/chat.ts +0 -59
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +0 -104
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +0 -140
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/anthropic.ts +0 -28
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/google.ts +0 -41
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/index.ts +0 -26
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/openai.ts +0 -61
- package/eventcatalog/src/enterprise/eventcatalog-chat/utils/chat-prompts.ts +0 -50
- package/eventcatalog/src/pages/auth/login.astro +0 -280
- package/eventcatalog/src/pages/chat/feature.astro +0 -179
- package/eventcatalog/src/pages/chat/index.astro +0 -10
- package/eventcatalog/src/pages/nav-index.json.ts +0 -30
- /package/eventcatalog/src/{pages → enterprise}/auth/error.astro +0 -0
- /package/eventcatalog/src/{middleware-auth.ts → enterprise/auth/middleware/middleware-auth.ts} +0 -0
- /package/eventcatalog/src/{middleware.ts → enterprise/auth/middleware/middleware.ts} +0 -0
- /package/eventcatalog/src/{pages/unauthorized/index.astro → enterprise/auth/unauthorized.astro} +0 -0
- /package/eventcatalog/src/{pages → enterprise}/plans/index.astro +0 -0
|
@@ -69,9 +69,21 @@ const pagefindAttributes =
|
|
|
69
69
|
</VerticalSideBarLayout>
|
|
70
70
|
|
|
71
71
|
<script is:inline src={js}></script>
|
|
72
|
-
<script define:vars={{ schema: stringified, config }}>
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
<script is:inline define:vars={{ schema: stringified, config }}>
|
|
73
|
+
function initAsyncApi() {
|
|
74
|
+
const root = document.getElementById('asyncapi');
|
|
75
|
+
if (!root || root.dataset.initialized) return;
|
|
76
|
+
|
|
77
|
+
if (typeof AsyncApiStandalone !== 'undefined') {
|
|
78
|
+
AsyncApiStandalone.render({ schema, config }, root);
|
|
79
|
+
root.dataset.initialized = 'true';
|
|
80
|
+
} else {
|
|
81
|
+
setTimeout(initAsyncApi, 50);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
document.addEventListener('astro:page-load', initAsyncApi);
|
|
86
|
+
initAsyncApi();
|
|
75
87
|
</script>
|
|
76
88
|
|
|
77
89
|
<style>
|
|
@@ -82,6 +94,10 @@ const pagefindAttributes =
|
|
|
82
94
|
}
|
|
83
95
|
}
|
|
84
96
|
|
|
97
|
+
.burger-menu {
|
|
98
|
+
display: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
85
101
|
.aui-root .z-10 {
|
|
86
102
|
z-index: 8;
|
|
87
103
|
}
|
|
@@ -73,16 +73,16 @@ const logList = await Promise.all(logListPromise);
|
|
|
73
73
|
|
|
74
74
|
const getBadge = () => {
|
|
75
75
|
if (props.collection === 'services') {
|
|
76
|
-
return { backgroundColor: 'pink', textColor: 'pink', content: 'Service', icon: ServerIcon, class: 'text-pink-
|
|
76
|
+
return { backgroundColor: 'pink', textColor: 'pink', content: 'Service', icon: ServerIcon, class: 'text-pink-600' };
|
|
77
77
|
}
|
|
78
78
|
if (props.collection === 'events') {
|
|
79
|
-
return { backgroundColor: 'orange', textColor: 'orange', content: 'Event', icon: BoltIcon, class: 'text-orange-
|
|
79
|
+
return { backgroundColor: 'orange', textColor: 'orange', content: 'Event', icon: BoltIcon, class: 'text-orange-600' };
|
|
80
80
|
}
|
|
81
81
|
if (props.collection === 'commands') {
|
|
82
|
-
return { backgroundColor: 'blue', textColor: 'blue', content: 'Command', icon: ChatBubbleLeftIcon, class: 'text-blue-
|
|
82
|
+
return { backgroundColor: 'blue', textColor: 'blue', content: 'Command', icon: ChatBubbleLeftIcon, class: 'text-blue-600' };
|
|
83
83
|
}
|
|
84
84
|
if (props.collection === 'queries') {
|
|
85
|
-
return { backgroundColor: 'green', textColor: 'green', content: 'Query', icon: MagnifyingGlassIcon, class: 'text-
|
|
85
|
+
return { backgroundColor: 'green', textColor: 'green', content: 'Query', icon: MagnifyingGlassIcon, class: 'text-green-600' };
|
|
86
86
|
}
|
|
87
87
|
if (props.collection === 'domains') {
|
|
88
88
|
return {
|
|
@@ -90,14 +90,14 @@ const getBadge = () => {
|
|
|
90
90
|
textColor: 'yellow',
|
|
91
91
|
content: 'Domain',
|
|
92
92
|
icon: RectangleGroupIcon,
|
|
93
|
-
class: 'text-yellow-
|
|
93
|
+
class: 'text-yellow-600',
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
if (props.collection === 'containers') {
|
|
97
|
-
return { backgroundColor: 'blue', textColor: 'blue', content: 'Container', icon: DatabaseIcon, class: 'text-blue-
|
|
97
|
+
return { backgroundColor: 'blue', textColor: 'blue', content: 'Container', icon: DatabaseIcon, class: 'text-blue-600' };
|
|
98
98
|
}
|
|
99
99
|
if (props.collection === 'flows') {
|
|
100
|
-
return { backgroundColor: 'teal', textColor: 'teal', content: 'Flow', icon: QueueListIcon, class: 'text-teal-
|
|
100
|
+
return { backgroundColor: 'teal', textColor: 'teal', content: 'Flow', icon: QueueListIcon, class: 'text-teal-600' };
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
|
|
@@ -36,7 +36,7 @@ if (isRemote) {
|
|
|
36
36
|
const pageTitle = `${collection} | ${data.name} | GraphQL Schema`.replace(/^\w/, (c) => c.toUpperCase());
|
|
37
37
|
|
|
38
38
|
const getServiceBadge = () => {
|
|
39
|
-
return [{ backgroundColor: 'pink', textColor: 'pink', content: 'Service', icon: ServerIcon, class: 'text-pink-
|
|
39
|
+
return [{ backgroundColor: 'pink', textColor: 'pink', content: 'Service', icon: ServerIcon, class: 'text-pink-600' }];
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
const getGraphQLBadge = () => {
|
|
@@ -71,17 +71,17 @@ const getContentBadges = () =>
|
|
|
71
71
|
|
|
72
72
|
const getBadge = () => {
|
|
73
73
|
if (props.collection === 'services') {
|
|
74
|
-
return [{ backgroundColor: 'pink', textColor: 'pink', content: 'Service', icon: ServerIcon, class: 'text-pink-
|
|
74
|
+
return [{ backgroundColor: 'pink', textColor: 'pink', content: 'Service', icon: ServerIcon, class: 'text-pink-600' }];
|
|
75
75
|
}
|
|
76
76
|
if (props.collection === 'events') {
|
|
77
|
-
return [{ backgroundColor: 'orange', textColor: 'orange', content: 'Event', icon: BoltIcon, class: 'text-orange-
|
|
77
|
+
return [{ backgroundColor: 'orange', textColor: 'orange', content: 'Event', icon: BoltIcon, class: 'text-orange-600' }];
|
|
78
78
|
}
|
|
79
79
|
if (props.collection === 'commands') {
|
|
80
|
-
return [{ backgroundColor: 'blue', textColor: 'blue', content: 'Command', icon: ChatBubbleLeftIcon, class: 'text-blue-
|
|
80
|
+
return [{ backgroundColor: 'blue', textColor: 'blue', content: 'Command', icon: ChatBubbleLeftIcon, class: 'text-blue-600' }];
|
|
81
81
|
}
|
|
82
82
|
if (props.collection === 'queries') {
|
|
83
83
|
return [
|
|
84
|
-
{ backgroundColor: 'green', textColor: 'green', content: 'Query', icon: MagnifyingGlassIcon, class: 'text-green-
|
|
84
|
+
{ backgroundColor: 'green', textColor: 'green', content: 'Query', icon: MagnifyingGlassIcon, class: 'text-green-600' },
|
|
85
85
|
];
|
|
86
86
|
}
|
|
87
87
|
if (props.collection === 'domains') {
|
|
@@ -91,7 +91,7 @@ const getBadge = () => {
|
|
|
91
91
|
textColor: 'yellow',
|
|
92
92
|
content: 'Domain',
|
|
93
93
|
icon: RectangleGroupIcon,
|
|
94
|
-
class: 'text-yellow-
|
|
94
|
+
class: 'text-yellow-600',
|
|
95
95
|
},
|
|
96
96
|
];
|
|
97
97
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// This file exposes the markdown for EventCatalog in the Url
|
|
2
|
+
// For example http://localhost:3000/docs/teams/full-stack loads the Page and http://localhost:3000/docs/teams/full-stack.mdx loads the markdown
|
|
3
|
+
// This is used for the LLMs to load the markdown for the given item (llms.txt);
|
|
4
|
+
|
|
5
|
+
import type { APIRoute } from 'astro';
|
|
6
|
+
import { getCollection } from 'astro:content';
|
|
7
|
+
import config from '@config';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
|
|
10
|
+
const teams = await getCollection('teams');
|
|
11
|
+
|
|
12
|
+
export async function getStaticPaths() {
|
|
13
|
+
// Just return empty array if LLMs are not enabled
|
|
14
|
+
if (!config.llmsTxt?.enabled) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return teams.map((team) => ({
|
|
19
|
+
params: { type: 'teams', id: team.data.id },
|
|
20
|
+
props: { content: team },
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const GET: APIRoute = async ({ params, props }) => {
|
|
25
|
+
// Just return empty array if LLMs are not enabled
|
|
26
|
+
if (!config.llmsTxt?.enabled) {
|
|
27
|
+
return new Response('llms.txt is not enabled for this Catalog.', { status: 404 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (props?.content?.filePath) {
|
|
31
|
+
const file = fs.readFileSync(props.content.filePath, 'utf8');
|
|
32
|
+
return new Response(file, { status: 200 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return new Response('Not found', { status: 404 });
|
|
36
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// This file exposes the markdown for EventCatalog in the Url
|
|
2
|
+
// For example http://localhost:3000/docs/users/dboyne loads the Page and http://localhost:3000/docs/users/dboyne.mdx loads the markdown
|
|
3
|
+
// This is used for the LLMs to load the markdown for the given item (llms.txt);
|
|
4
|
+
|
|
5
|
+
import type { APIRoute } from 'astro';
|
|
6
|
+
import { getCollection } from 'astro:content';
|
|
7
|
+
import config from '@config';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
|
|
10
|
+
const users = await getCollection('users');
|
|
11
|
+
|
|
12
|
+
export async function getStaticPaths() {
|
|
13
|
+
// Just return empty array if LLMs are not enabled
|
|
14
|
+
if (!config.llmsTxt?.enabled) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return users.map((user) => ({
|
|
19
|
+
params: { type: 'users', id: user.data.id },
|
|
20
|
+
props: { content: user },
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const GET: APIRoute = async ({ params, props }) => {
|
|
25
|
+
// Just return empty array if LLMs are not enabled
|
|
26
|
+
if (!config.llmsTxt?.enabled) {
|
|
27
|
+
return new Response('llms.txt is not enabled for this Catalog.', { status: 404 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (props?.content?.filePath) {
|
|
31
|
+
const file = fs.readFileSync(props.content?.filePath, 'utf8');
|
|
32
|
+
return new Response(file, { status: 200 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return new Response('Not found', { status: 404 });
|
|
36
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { isSSR } from '@utils/feature';
|
|
2
|
+
import { HybridPage } from '@utils/page-loaders/hybrid-page';
|
|
3
|
+
import { getEvents } from '@utils/collections/events';
|
|
4
|
+
import { getCommands } from '@utils/collections/commands';
|
|
5
|
+
import { getQueries } from '@utils/collections/queries';
|
|
6
|
+
import { getServices, getSpecificationsForService } from '@utils/collections/services';
|
|
7
|
+
import { getOwner } from '@utils/collections/owners';
|
|
8
|
+
import { buildUrl } from '@utils/url-builder';
|
|
9
|
+
import { resourceFileExists, readResourceFile } from '@utils/resource-files';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
|
|
12
|
+
// Helper function to enrich owners with full details
|
|
13
|
+
async function enrichOwners(ownersRaw: any[]) {
|
|
14
|
+
if (!ownersRaw || ownersRaw.length === 0) return [];
|
|
15
|
+
|
|
16
|
+
const owners = await Promise.all(ownersRaw.map(getOwner));
|
|
17
|
+
const filteredOwners = owners.filter((o) => o !== undefined);
|
|
18
|
+
|
|
19
|
+
return filteredOwners.map((o) => ({
|
|
20
|
+
id: o.data.id,
|
|
21
|
+
name: o.data.name,
|
|
22
|
+
type: o.collection,
|
|
23
|
+
href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function fetchAllSchemas() {
|
|
28
|
+
// Fetch all messages
|
|
29
|
+
const events = await getEvents({ getAllVersions: true });
|
|
30
|
+
const commands = await getCommands({ getAllVersions: true });
|
|
31
|
+
const queries = await getQueries({ getAllVersions: true });
|
|
32
|
+
|
|
33
|
+
// Fetch all services
|
|
34
|
+
const services = await getServices({ getAllVersions: true });
|
|
35
|
+
|
|
36
|
+
// Combine all messages
|
|
37
|
+
const allMessages = [...events, ...commands, ...queries];
|
|
38
|
+
|
|
39
|
+
// Filter messages with schemas and read schema content - only keep essential data
|
|
40
|
+
const messagesWithSchemas = await Promise.all(
|
|
41
|
+
allMessages
|
|
42
|
+
.filter((message) => message.data.schemaPath)
|
|
43
|
+
.filter((message) => resourceFileExists(message, message.data.schemaPath ?? ''))
|
|
44
|
+
.map(async (message) => {
|
|
45
|
+
try {
|
|
46
|
+
const schemaPath = message.data.schemaPath ?? '';
|
|
47
|
+
const schemaContent = readResourceFile(message, schemaPath) ?? '';
|
|
48
|
+
const schemaExtension = path.extname(schemaPath).slice(1);
|
|
49
|
+
const enrichedOwners = await enrichOwners(message.data.owners || []);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
collection: message.collection,
|
|
53
|
+
data: {
|
|
54
|
+
id: message.data.id,
|
|
55
|
+
name: message.data.name,
|
|
56
|
+
version: message.data.version,
|
|
57
|
+
summary: message.data.summary,
|
|
58
|
+
schemaPath: message.data.schemaPath,
|
|
59
|
+
producers: message.data.producers || [],
|
|
60
|
+
consumers: message.data.consumers || [],
|
|
61
|
+
owners: enrichedOwners,
|
|
62
|
+
},
|
|
63
|
+
schemaContent,
|
|
64
|
+
schemaExtension,
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(`Error reading schema for ${message.data.id}:`, error);
|
|
68
|
+
const enrichedOwners = await enrichOwners(message.data.owners || []);
|
|
69
|
+
return {
|
|
70
|
+
collection: message.collection,
|
|
71
|
+
data: {
|
|
72
|
+
id: message.data.id,
|
|
73
|
+
name: message.data.name,
|
|
74
|
+
version: message.data.version,
|
|
75
|
+
summary: message.data.summary,
|
|
76
|
+
schemaPath: message.data.schemaPath,
|
|
77
|
+
producers: message.data.producers || [],
|
|
78
|
+
consumers: message.data.consumers || [],
|
|
79
|
+
owners: enrichedOwners,
|
|
80
|
+
},
|
|
81
|
+
schemaContent: '',
|
|
82
|
+
schemaExtension: 'json',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Filter services with specifications and read spec content - only keep essential data
|
|
89
|
+
const servicesWithSpecs = await Promise.all(
|
|
90
|
+
services.map(async (service) => {
|
|
91
|
+
try {
|
|
92
|
+
const specifications = getSpecificationsForService(service);
|
|
93
|
+
|
|
94
|
+
if (specifications.length === 0) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return await Promise.all(
|
|
99
|
+
specifications.map(async (spec) => {
|
|
100
|
+
if (!resourceFileExists(service, spec.path)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const schemaContent = readResourceFile(service, spec.path) ?? '';
|
|
105
|
+
const schemaExtension = spec.type;
|
|
106
|
+
const enrichedOwners = await enrichOwners(service.data.owners || []);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
collection: 'services',
|
|
110
|
+
data: {
|
|
111
|
+
id: `${service.data.id}`,
|
|
112
|
+
name: `${service.data.name} - ${spec.name}`,
|
|
113
|
+
version: service.data.version,
|
|
114
|
+
summary: service.data.summary,
|
|
115
|
+
schemaPath: spec.path,
|
|
116
|
+
owners: enrichedOwners,
|
|
117
|
+
},
|
|
118
|
+
schemaContent,
|
|
119
|
+
schemaExtension,
|
|
120
|
+
specType: spec.type,
|
|
121
|
+
specName: spec.name,
|
|
122
|
+
specFilenameWithoutExtension: spec.filenameWithoutExtension,
|
|
123
|
+
};
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(`Error reading specifications for service ${service.data.id}:`, error);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Flatten and filter out null values
|
|
134
|
+
const flatServicesWithSpecs = servicesWithSpecs.flat().filter((service) => service !== null);
|
|
135
|
+
|
|
136
|
+
return [...messagesWithSchemas, ...flatServicesWithSpecs];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export class Page extends HybridPage {
|
|
140
|
+
static get prerender(): boolean {
|
|
141
|
+
return !isSSR();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static async getStaticPaths(): Promise<Array<{ params: any; props: any }>> {
|
|
145
|
+
if (isSSR()) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const allSchemas = await fetchAllSchemas();
|
|
150
|
+
|
|
151
|
+
return [
|
|
152
|
+
{
|
|
153
|
+
params: {},
|
|
154
|
+
props: {
|
|
155
|
+
schemas: allSchemas,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
protected static async fetchData(_params: any) {
|
|
162
|
+
const allSchemas = await fetchAllSchemas();
|
|
163
|
+
return {
|
|
164
|
+
schemas: allSchemas,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected static hasValidProps(props: any): boolean {
|
|
169
|
+
return props && props.schemas !== undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected static createNotFoundResponse(): Response {
|
|
173
|
+
return new Response(null, {
|
|
174
|
+
status: 404,
|
|
175
|
+
statusText: 'Schema explorer not found',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -1,163 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
3
|
-
import { getEvents } from '@utils/collections/events';
|
|
4
|
-
import { getCommands } from '@utils/collections/commands';
|
|
5
|
-
import { getQueries } from '@utils/collections/queries';
|
|
6
|
-
import { getServices, getSpecificationsForService } from '@utils/collections/services';
|
|
7
3
|
import SchemaExplorer from '@components/SchemaExplorer/SchemaExplorer';
|
|
8
4
|
import { isEventCatalogScaleEnabled } from '@utils/feature';
|
|
9
|
-
import {
|
|
10
|
-
import { buildUrl } from '@utils/url-builder';
|
|
11
|
-
import fs from 'fs';
|
|
12
|
-
import path from 'path';
|
|
5
|
+
import { Page } from './_index.data';
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const commands = await getCommands({ getAllVersions: true });
|
|
17
|
-
const queries = await getQueries({ getAllVersions: true });
|
|
7
|
+
export const prerender = Page.prerender;
|
|
8
|
+
export const getStaticPaths = Page.getStaticPaths;
|
|
18
9
|
|
|
19
|
-
|
|
20
|
-
const services = await getServices({ getAllVersions: true });
|
|
21
|
-
|
|
22
|
-
// Combine all messages
|
|
23
|
-
const allMessages = [...events, ...commands, ...queries];
|
|
24
|
-
|
|
25
|
-
// Helper function to enrich owners with full details
|
|
26
|
-
async function enrichOwners(ownersRaw: any[]) {
|
|
27
|
-
if (!ownersRaw || ownersRaw.length === 0) return [];
|
|
28
|
-
|
|
29
|
-
const owners = await Promise.all(ownersRaw.map(getOwner));
|
|
30
|
-
const filteredOwners = owners.filter((o) => o !== undefined);
|
|
31
|
-
|
|
32
|
-
return filteredOwners.map((o) => ({
|
|
33
|
-
id: o.data.id,
|
|
34
|
-
name: o.data.name,
|
|
35
|
-
type: o.collection,
|
|
36
|
-
href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
|
|
37
|
-
}));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Filter messages with schemas and read schema content - only keep essential data
|
|
41
|
-
const messagesWithSchemas = await Promise.all(
|
|
42
|
-
allMessages
|
|
43
|
-
.filter((message) => message.data.schemaPath)
|
|
44
|
-
// Make sure the file exists
|
|
45
|
-
.filter((message) => fs.existsSync(path.join(path.dirname(message.filePath ?? ''), message.data.schemaPath ?? '')))
|
|
46
|
-
.map(async (message) => {
|
|
47
|
-
try {
|
|
48
|
-
// Get the schema file path
|
|
49
|
-
const schemaPath = message.data.schemaPath;
|
|
50
|
-
const fullSchemaPath = path.join(path.dirname(message.filePath ?? ''), schemaPath ?? '');
|
|
51
|
-
|
|
52
|
-
// Read the schema content
|
|
53
|
-
let schemaContent = '';
|
|
54
|
-
if (fs.existsSync(fullSchemaPath)) {
|
|
55
|
-
schemaContent = fs.readFileSync(fullSchemaPath, 'utf-8');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Get schema file extension
|
|
59
|
-
const schemaExtension = path.extname(schemaPath ?? '').slice(1);
|
|
60
|
-
|
|
61
|
-
// Enrich owners with full details
|
|
62
|
-
const enrichedOwners = await enrichOwners(message.data.owners || []);
|
|
63
|
-
|
|
64
|
-
// Only return essential data - strip out markdown, full data objects, etc.
|
|
65
|
-
return {
|
|
66
|
-
collection: message.collection,
|
|
67
|
-
data: {
|
|
68
|
-
id: message.data.id,
|
|
69
|
-
name: message.data.name,
|
|
70
|
-
version: message.data.version,
|
|
71
|
-
summary: message.data.summary,
|
|
72
|
-
schemaPath: message.data.schemaPath,
|
|
73
|
-
producers: message.data.producers || [],
|
|
74
|
-
consumers: message.data.consumers || [],
|
|
75
|
-
owners: enrichedOwners,
|
|
76
|
-
},
|
|
77
|
-
schemaContent,
|
|
78
|
-
schemaExtension,
|
|
79
|
-
};
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error(`Error reading schema for ${message.data.id}:`, error);
|
|
82
|
-
const enrichedOwners = await enrichOwners(message.data.owners || []);
|
|
83
|
-
return {
|
|
84
|
-
collection: message.collection,
|
|
85
|
-
data: {
|
|
86
|
-
id: message.data.id,
|
|
87
|
-
name: message.data.name,
|
|
88
|
-
version: message.data.version,
|
|
89
|
-
summary: message.data.summary,
|
|
90
|
-
schemaPath: message.data.schemaPath,
|
|
91
|
-
producers: message.data.producers || [],
|
|
92
|
-
consumers: message.data.consumers || [],
|
|
93
|
-
owners: enrichedOwners,
|
|
94
|
-
},
|
|
95
|
-
schemaContent: '',
|
|
96
|
-
schemaExtension: 'json',
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// Filter services with specifications and read spec content - only keep essential data
|
|
103
|
-
const servicesWithSpecs = await Promise.all(
|
|
104
|
-
services.map(async (service) => {
|
|
105
|
-
try {
|
|
106
|
-
const specifications = getSpecificationsForService(service);
|
|
107
|
-
|
|
108
|
-
// Only include services that have specifications
|
|
109
|
-
if (specifications.length === 0) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Process each specification file for this service
|
|
114
|
-
return await Promise.all(
|
|
115
|
-
specifications.map(async (spec) => {
|
|
116
|
-
const specPath = path.join(path.dirname(service.filePath ?? ''), spec.path);
|
|
117
|
-
|
|
118
|
-
// Only include if the spec file exists
|
|
119
|
-
if (!fs.existsSync(specPath)) {
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const schemaContent = fs.readFileSync(specPath, 'utf-8');
|
|
124
|
-
// Use spec type (openapi, asyncapi) as the extension for proper labeling
|
|
125
|
-
const schemaExtension = spec.type;
|
|
126
|
-
|
|
127
|
-
// Enrich owners with full details
|
|
128
|
-
const enrichedOwners = await enrichOwners(service.data.owners || []);
|
|
129
|
-
|
|
130
|
-
// Only return essential data - strip out markdown, sends/receives, entities, etc.
|
|
131
|
-
return {
|
|
132
|
-
collection: 'services',
|
|
133
|
-
data: {
|
|
134
|
-
id: `${service.data.id}`,
|
|
135
|
-
name: `${service.data.name} - ${spec.name}`,
|
|
136
|
-
version: service.data.version,
|
|
137
|
-
summary: service.data.summary,
|
|
138
|
-
schemaPath: spec.path,
|
|
139
|
-
owners: enrichedOwners,
|
|
140
|
-
},
|
|
141
|
-
schemaContent,
|
|
142
|
-
schemaExtension,
|
|
143
|
-
specType: spec.type,
|
|
144
|
-
specName: spec.name,
|
|
145
|
-
specFilenameWithoutExtension: spec.filenameWithoutExtension,
|
|
146
|
-
};
|
|
147
|
-
})
|
|
148
|
-
);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
console.error(`Error reading specifications for service ${service.data.id}:`, error);
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
// Flatten and filter out null values
|
|
157
|
-
const flatServicesWithSpecs = servicesWithSpecs.flat().filter((service) => service !== null);
|
|
158
|
-
|
|
159
|
-
// Combine messages and services
|
|
160
|
-
const allSchemas = [...messagesWithSchemas, ...flatServicesWithSpecs];
|
|
10
|
+
const { schemas } = await Page.getData(Astro);
|
|
161
11
|
|
|
162
12
|
const apiAccessEnabled = isEventCatalogScaleEnabled();
|
|
163
13
|
---
|
|
@@ -167,7 +17,7 @@ const apiAccessEnabled = isEventCatalogScaleEnabled();
|
|
|
167
17
|
<div class="flex docs-layout w-full h-full">
|
|
168
18
|
<div class="w-full lg:mr-2 pr-8 py-6 flex flex-col h-full">
|
|
169
19
|
<div class="w-full !max-w-none h-full flex flex-col overflow-hidden">
|
|
170
|
-
<SchemaExplorer client:load schemas={
|
|
20
|
+
<SchemaExplorer client:load schemas={schemas as any} apiAccessEnabled={apiAccessEnabled} />
|
|
171
21
|
</div>
|
|
172
22
|
</div>
|
|
173
23
|
</div>
|
|
@@ -30,7 +30,33 @@ export function remarkDirectives() {
|
|
|
30
30
|
class: `rounded-lg p-4 my-4 ${blockTypes[node.name as keyof typeof blockTypes] || ''}`,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Check if there's a custom title (label) provided via :::note[Custom Title]
|
|
34
|
+
// In remark-directive, the label is stored in node.children as a paragraph node
|
|
35
|
+
// with data.directiveLabel = true
|
|
36
|
+
let titleChildren;
|
|
37
|
+
let contentChildren;
|
|
38
|
+
|
|
39
|
+
const firstChild = node.children && node.children.length > 0 ? node.children[0] : null;
|
|
40
|
+
const hasCustomTitle = firstChild && firstChild.data?.directiveLabel === true;
|
|
41
|
+
|
|
42
|
+
if (hasCustomTitle && firstChild) {
|
|
43
|
+
// Custom title was provided in the label - it contains markdown parsed as inline content
|
|
44
|
+
titleChildren = firstChild.children || [
|
|
45
|
+
{ type: 'text', value: node.name.charAt(0).toUpperCase() + node.name.slice(1) },
|
|
46
|
+
];
|
|
47
|
+
contentChildren = node.children.slice(1);
|
|
48
|
+
} else {
|
|
49
|
+
// No custom title, use default based on directive name
|
|
50
|
+
titleChildren = [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
value: node.name.charAt(0).toUpperCase() + node.name.slice(1),
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
contentChildren = node.children;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Create header div that will contain icon and title
|
|
34
60
|
const headerNode = {
|
|
35
61
|
type: 'element',
|
|
36
62
|
data: {
|
|
@@ -70,7 +96,7 @@ export function remarkDirectives() {
|
|
|
70
96
|
},
|
|
71
97
|
],
|
|
72
98
|
},
|
|
73
|
-
//
|
|
99
|
+
// Title (with support for markdown)
|
|
74
100
|
{
|
|
75
101
|
type: 'element',
|
|
76
102
|
data: {
|
|
@@ -79,12 +105,7 @@ export function remarkDirectives() {
|
|
|
79
105
|
class: '',
|
|
80
106
|
},
|
|
81
107
|
},
|
|
82
|
-
children:
|
|
83
|
-
{
|
|
84
|
-
type: 'text',
|
|
85
|
-
value: node.name.charAt(0).toUpperCase() + node.name.slice(1),
|
|
86
|
-
},
|
|
87
|
-
],
|
|
108
|
+
children: titleChildren,
|
|
88
109
|
},
|
|
89
110
|
],
|
|
90
111
|
};
|
|
@@ -98,7 +119,7 @@ export function remarkDirectives() {
|
|
|
98
119
|
class: 'prose prose-md w-full !max-w-none ',
|
|
99
120
|
},
|
|
100
121
|
},
|
|
101
|
-
children:
|
|
122
|
+
children: contentChildren,
|
|
102
123
|
};
|
|
103
124
|
|
|
104
125
|
// Replace node's children with header and content
|
|
@@ -2,6 +2,7 @@ import type { CollectionEntry } from 'astro:content';
|
|
|
2
2
|
import type { PageTypes } from '@types';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import { getAbsoluteFilePathForAstroFile } from '@utils/files';
|
|
5
6
|
|
|
6
7
|
export type Schema = {
|
|
7
8
|
url: string;
|
|
@@ -13,12 +14,23 @@ export const getSchemaURL = (resource: CollectionEntry<PageTypes>) => {
|
|
|
13
14
|
const publicPath = resource?.catalog?.publicPath;
|
|
14
15
|
const schemaFilePath = resource?.data?.schemaPath;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
// No schema file path, return an empty string
|
|
18
|
+
if (!schemaFilePath) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!publicPath) {
|
|
23
|
+
// Then we try and get the absolute file path from the resource
|
|
24
|
+
const absoluteFilePath = getAbsoluteFilePathForAstroFile(resource.filePath ?? '', schemaFilePath ?? '');
|
|
25
|
+
if (absoluteFilePath) {
|
|
26
|
+
return absoluteFilePath;
|
|
27
|
+
}
|
|
28
|
+
// Can't find the schema file, return an empty string
|
|
17
29
|
return;
|
|
18
30
|
}
|
|
19
31
|
|
|
20
32
|
// new URL
|
|
21
|
-
return path.join(publicPath, schemaFilePath);
|
|
33
|
+
return path.join(publicPath, schemaFilePath ?? '');
|
|
22
34
|
};
|
|
23
35
|
|
|
24
36
|
export const getSchemaFormatFromURL = (url: string) => {
|
|
@@ -43,17 +55,29 @@ export const getSchemasFromResource = (resource: CollectionEntry<PageTypes>): Sc
|
|
|
43
55
|
? specifications.find((spec) => spec.type === 'openapi')?.path
|
|
44
56
|
: specifications?.openapiPath;
|
|
45
57
|
// @ts-ignore
|
|
46
|
-
|
|
58
|
+
let publicPath = resource?.catalog?.publicPath;
|
|
47
59
|
const schemas = [];
|
|
48
60
|
|
|
49
61
|
if (asyncapiPath) {
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
if (!publicPath) {
|
|
63
|
+
// We try and get the absoulate file path from the resource
|
|
64
|
+
const absoluteFilePath = getAbsoluteFilePathForAstroFile(resource.filePath ?? '', asyncapiPath ?? '');
|
|
65
|
+
schemas.push({ url: buildUrl(absoluteFilePath), format: 'asyncapi' });
|
|
66
|
+
} else {
|
|
67
|
+
// The resource has the public path, so we can use it to build the URL
|
|
68
|
+
schemas.push({ url: buildUrl(path.join(publicPath, asyncapiPath)), format: 'asyncapi' });
|
|
69
|
+
}
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
if (openapiPath) {
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
if (!publicPath) {
|
|
74
|
+
// We try and get the absoulate file path from the resource
|
|
75
|
+
const absoluteFilePath = getAbsoluteFilePathForAstroFile(resource.filePath ?? '', openapiPath ?? '');
|
|
76
|
+
schemas.push({ url: buildUrl(absoluteFilePath), format: 'openapi' });
|
|
77
|
+
} else {
|
|
78
|
+
// The resource has the public path, so we can use it to build the URL
|
|
79
|
+
schemas.push({ url: buildUrl(path.join(publicPath, openapiPath)), format: 'openapi' });
|
|
80
|
+
}
|
|
57
81
|
}
|
|
58
82
|
|
|
59
83
|
return schemas;
|