@eventcatalog/core 2.62.1 → 2.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-4BYDUGYI.js → chunk-6AMZOBWI.js} +1 -1
  6. package/dist/{chunk-GZ2SVHEA.js → chunk-CWGFHLMX.js} +1 -1
  7. package/dist/{chunk-IWFL6VRS.js → chunk-PLMTJHGH.js} +1 -1
  8. package/dist/constants.cjs +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/eventcatalog.cjs +1 -1
  11. package/dist/eventcatalog.config.d.cts +34 -0
  12. package/dist/eventcatalog.config.d.ts +34 -0
  13. package/dist/eventcatalog.js +3 -3
  14. package/eventcatalog/astro.config.mjs +2 -1
  15. package/eventcatalog/public/icons/avro.svg +21 -0
  16. package/eventcatalog/public/icons/json-schema.svg +6 -0
  17. package/eventcatalog/public/icons/proto.svg +10 -0
  18. package/eventcatalog/src/components/Grids/utils.tsx +5 -3
  19. package/eventcatalog/src/components/MDX/RemoteFile.astro +5 -11
  20. package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewerRoot.astro +41 -6
  21. package/eventcatalog/src/components/SchemaExplorer/ApiAccessSection.tsx +139 -0
  22. package/eventcatalog/src/components/SchemaExplorer/AvroSchemaViewer.tsx +423 -0
  23. package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +102 -0
  24. package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx +740 -0
  25. package/eventcatalog/src/components/SchemaExplorer/OwnersSection.tsx +56 -0
  26. package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +33 -0
  27. package/eventcatalog/src/components/SchemaExplorer/ProducersConsumersSection.tsx +91 -0
  28. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +93 -0
  29. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +130 -0
  30. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsHeader.tsx +181 -0
  31. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +232 -0
  32. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +415 -0
  33. package/eventcatalog/src/components/SchemaExplorer/SchemaFilters.tsx +174 -0
  34. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +73 -0
  35. package/eventcatalog/src/components/SchemaExplorer/SchemaViewerModal.tsx +77 -0
  36. package/eventcatalog/src/components/SchemaExplorer/VersionHistoryModal.tsx +72 -0
  37. package/eventcatalog/src/components/SchemaExplorer/types.ts +45 -0
  38. package/eventcatalog/src/components/SchemaExplorer/utils.ts +81 -0
  39. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +33 -2
  40. package/eventcatalog/src/components/Tables/Table.tsx +10 -2
  41. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +10 -8
  42. package/eventcatalog/src/components/Tables/columns/DomainTableColumns.tsx +8 -6
  43. package/eventcatalog/src/components/Tables/columns/FlowTableColumns.tsx +9 -7
  44. package/eventcatalog/src/components/Tables/columns/MessageTableColumns.tsx +11 -9
  45. package/eventcatalog/src/components/Tables/columns/ServiceTableColumns.tsx +9 -7
  46. package/eventcatalog/src/components/Tables/columns/SharedColumns.tsx +4 -2
  47. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +12 -8
  48. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +14 -9
  49. package/eventcatalog/src/components/Tables/columns/index.tsx +9 -8
  50. package/eventcatalog/src/content.config.ts +3 -2
  51. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +1 -0
  52. package/eventcatalog/src/layouts/DirectoryLayout.astro +21 -22
  53. package/eventcatalog/src/layouts/DiscoverLayout.astro +26 -12
  54. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +10 -0
  55. package/eventcatalog/src/pages/api/schemas/[collection]/[id]/[version]/index.ts +45 -0
  56. package/eventcatalog/src/pages/api/schemas/services/[id]/[version]/[specification]/index.ts +51 -0
  57. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
  58. package/eventcatalog/src/pages/docs/llm/schemas.txt.ts +86 -0
  59. package/eventcatalog/src/pages/schemas/index.astro +175 -0
  60. package/eventcatalog/src/types/index.ts +9 -0
  61. package/eventcatalog/src/utils/files.ts +9 -0
  62. package/package.json +1 -1
  63. package/eventcatalog/src/components/MDX/SchemaViewer/SchemaProperty.astro +0 -204
  64. package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewer.astro +0 -705
@@ -14,6 +14,7 @@ import { DatabaseIcon } from 'lucide-react';
14
14
  import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
15
15
  import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
16
16
  import Checkbox from '@components/Checkbox.astro';
17
+ import config from '@config';
17
18
 
18
19
  const events = await getEvents();
19
20
  const queries = await getQueries();
@@ -35,6 +36,9 @@ const currentPath = Astro.url.pathname;
35
36
  const checkboxLatestId = 'latest-only';
36
37
  const checkboxDraftsId = 'show-drafts';
37
38
 
39
+ // @ts-ignore
40
+ const tableConfiguration = config[type as keyof typeof config]?.tableConfiguration ?? { columns: {} };
41
+
38
42
  const tabs = [
39
43
  {
40
44
  label: `Events (${events.length})`,
@@ -43,6 +47,7 @@ const tabs = [
43
47
  icon: BoltIcon,
44
48
  activeColor: 'orange',
45
49
  enabled: events.length > 0,
50
+ visible: events.length > 0,
46
51
  },
47
52
  {
48
53
  label: `Commands (${commands.length})`,
@@ -51,6 +56,7 @@ const tabs = [
51
56
  icon: ChatBubbleLeftIcon,
52
57
  activeColor: 'blue',
53
58
  enabled: commands.length > 0,
59
+ visible: commands.length > 0,
54
60
  },
55
61
  {
56
62
  label: `Queries (${queries.length})`,
@@ -59,6 +65,7 @@ const tabs = [
59
65
  icon: MagnifyingGlassIcon,
60
66
  activeColor: 'green',
61
67
  enabled: queries.length > 0,
68
+ visible: queries.length > 0,
62
69
  },
63
70
  {
64
71
  label: `Services (${services.length})`,
@@ -67,6 +74,7 @@ const tabs = [
67
74
  icon: ServerIcon,
68
75
  activeColor: 'pink',
69
76
  enabled: services.length > 0,
77
+ visible: services.length > 0,
70
78
  },
71
79
  {
72
80
  label: `Domains (${domains.length})`,
@@ -75,6 +83,7 @@ const tabs = [
75
83
  icon: RectangleGroupIcon,
76
84
  activeColor: 'yellow',
77
85
  enabled: domains.length > 0,
86
+ visible: domains.length > 0,
78
87
  },
79
88
  {
80
89
  label: `Data (${containers.length})`,
@@ -83,6 +92,7 @@ const tabs = [
83
92
  icon: DatabaseIcon,
84
93
  activeColor: 'blue',
85
94
  enabled: containers.length > 0,
95
+ visible: containers.length > 0,
86
96
  },
87
97
  {
88
98
  label: `Flows (${flows.length})`,
@@ -91,6 +101,7 @@ const tabs = [
91
101
  icon: QueueListIcon,
92
102
  activeColor: 'teal',
93
103
  enabled: flows.length > 0,
104
+ visible: flows.length > 0,
94
105
  },
95
106
  ];
96
107
  ---
@@ -102,18 +113,20 @@ const tabs = [
102
113
  <div class="border-b border-gray-200">
103
114
  <nav class="flex space-x-8 -mb-0.5 pl-6" aria-label="Tabs">
104
115
  {
105
- tabs.map((tab) => (
106
- <a
107
- href={tab.href}
108
- class={` text-black group inline-flex items-center py-4 px-1 text-sm font-light ${tab.isActive ? `border-${tab.activeColor}-500 border-b-[2px] text-${tab.activeColor}-500` : 'opacity-80'} ${!tab.enabled ? 'disabled' : ''}`}
109
- aria-current="page"
110
- >
111
- <tab.icon
112
- className={`w-6 h-6 -ml-0.5 mr-2 ${tab.isActive ? `text-${tab.activeColor}-500` : 'text-gray-500'}`}
113
- />
114
- <span>{tab.label}</span>
115
- </a>
116
- ))
116
+ tabs
117
+ .filter((tab) => tab.visible)
118
+ .map((tab) => (
119
+ <a
120
+ href={tab.href}
121
+ class={` text-black group inline-flex items-center py-4 px-1 text-sm font-light ${tab.isActive ? `border-${tab.activeColor}-500 border-b-[2px] text-${tab.activeColor}-500` : 'opacity-80'} ${!tab.enabled ? 'disabled' : ''}`}
122
+ aria-current="page"
123
+ >
124
+ <tab.icon
125
+ className={`w-6 h-6 -ml-0.5 mr-2 ${tab.isActive ? `text-${tab.activeColor}-500` : 'text-gray-500'}`}
126
+ />
127
+ <span>{tab.label}</span>
128
+ </a>
129
+ ))
117
130
  }
118
131
  </nav>
119
132
  </div>
@@ -145,6 +158,7 @@ const tabs = [
145
158
  checkboxDraftsId={checkboxDraftsId}
146
159
  data={data}
147
160
  collection={type}
161
+ tableConfiguration={tableConfiguration}
148
162
  client:load
149
163
  />
150
164
  </div>
@@ -17,6 +17,8 @@ import {
17
17
  Rocket,
18
18
  FileText,
19
19
  SquareDashedMousePointerIcon,
20
+ PackageSearch,
21
+ FileJson,
20
22
  } from 'lucide-react';
21
23
  import Header from '../components/Header.astro';
22
24
  import SEO from '../components/Seo.astro';
@@ -121,6 +123,14 @@ const navigationItems = [
121
123
  current: currentPath.includes('/discover/'),
122
124
  sidebar: false,
123
125
  },
126
+ {
127
+ id: '/schemas',
128
+ label: 'Schema Explorer',
129
+ icon: FileJson,
130
+ href: buildUrl('/schemas'),
131
+ current: currentPath.includes('/schemas'),
132
+ sidebar: false,
133
+ },
124
134
  {
125
135
  id: '/directory',
126
136
  label: 'Users & Teams',
@@ -0,0 +1,45 @@
1
+ import type { APIRoute } from 'astro';
2
+ import { getCollection } from 'astro:content';
3
+ import path from 'node:path';
4
+ import fs from 'node:fs';
5
+ import { isEventCatalogScaleEnabled } from '@utils/feature';
6
+
7
+ export async function getStaticPaths() {
8
+ const events = await getCollection('events');
9
+ const commands = await getCollection('commands');
10
+ const queries = await getCollection('queries');
11
+ const messages = [...events, ...commands, ...queries];
12
+ return messages
13
+ .filter((message) => message.data.schemaPath)
14
+ .filter((message) => fs.existsSync(path.join(path.dirname(message.filePath ?? ''), message.data.schemaPath ?? '')))
15
+ .map((message) => ({
16
+ params: { collection: message.collection, id: message.data.id, version: message.data.version },
17
+ props: {
18
+ pathToSchema: path.join(path.dirname(message.filePath ?? ''), message.data.schemaPath ?? ''),
19
+ schema: fs.readFileSync(path.join(path.dirname(message.filePath ?? ''), message.data.schemaPath ?? ''), 'utf8'),
20
+ extension: message.data.schemaPath?.split('.').pop(),
21
+ },
22
+ }));
23
+ }
24
+
25
+ export const GET: APIRoute = async ({ props }) => {
26
+ if (!isEventCatalogScaleEnabled()) {
27
+ return new Response(
28
+ JSON.stringify({
29
+ error: 'feature_not_available_on_server',
30
+ message: 'Schema API is not enabled for this deployment and supported in EventCatalog Scale.',
31
+ }),
32
+ {
33
+ status: 501,
34
+ headers: {
35
+ 'Content-Type': 'application/json',
36
+ 'Cache-Control': 'no-store',
37
+ },
38
+ }
39
+ );
40
+ }
41
+
42
+ return new Response(props.schema, {
43
+ headers: { 'Content-Type': 'text/plain' },
44
+ });
45
+ };
@@ -0,0 +1,51 @@
1
+ import type { APIRoute } from 'astro';
2
+ import { getCollection } from 'astro:content';
3
+ import path from 'node:path';
4
+ import fs from 'node:fs';
5
+ import { getSpecificationsForService } from '@utils/collections/services';
6
+ import { isEventCatalogScaleEnabled } from '@utils/feature';
7
+
8
+ export async function getStaticPaths() {
9
+ const services = await getCollection('services');
10
+ const servicesWithSpecifications = services.filter((service) => getSpecificationsForService(service).length > 0);
11
+ return servicesWithSpecifications.reduce<
12
+ { params: { collection: string; id: string; version: string; specification: string }; props: { schema: string } }[]
13
+ >(
14
+ (acc, service) => {
15
+ const specifications = getSpecificationsForService(service);
16
+ return [
17
+ ...acc,
18
+ ...specifications.map((specification) => ({
19
+ params: {
20
+ collection: service.collection,
21
+ id: service.data.id,
22
+ version: service.data.version,
23
+ specification: specification.type,
24
+ },
25
+ props: {
26
+ schema: fs.readFileSync(path.join(path.dirname(service.filePath ?? ''), specification.path ?? ''), 'utf8'),
27
+ },
28
+ })),
29
+ ];
30
+ },
31
+ [] as { params: { collection: string; id: string; version: string; specification: string }; props: { schema: string } }[]
32
+ );
33
+ }
34
+
35
+ export const GET: APIRoute = async ({ props }) => {
36
+ if (!isEventCatalogScaleEnabled()) {
37
+ return new Response(
38
+ JSON.stringify({
39
+ error: 'feature_not_available_on_server',
40
+ message: 'Schema API is not enabled for this deployment and supported in EventCatalog Scale.',
41
+ }),
42
+ {
43
+ status: 501,
44
+ headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' },
45
+ }
46
+ );
47
+ }
48
+ return new Response(props.schema, {
49
+ headers: { 'Content-Type': 'text/plain' },
50
+ });
51
+ };
@@ -548,6 +548,7 @@ nodeGraphs.push({
548
548
  }
549
549
 
550
550
  if (enableSupportForElkLayout) {
551
+ // @ts-ignore
551
552
  const { default: elkLayouts } = await import('@mermaid-js/layout-elk/dist/mermaid-layout-elk.core.mjs');
552
553
  mermaid.registerLayoutLoaders(elkLayouts);
553
554
  }
@@ -0,0 +1,86 @@
1
+ import { getCollection } from 'astro:content';
2
+ import config from '@config';
3
+ import type { APIRoute } from 'astro';
4
+ import type { CollectionEntry } from 'astro:content';
5
+ import { getSpecificationsForService } from '@utils/collections/services';
6
+ import { isEventCatalogScaleEnabled } from '@utils/feature';
7
+
8
+ const events = await getCollection('events');
9
+ const commands = await getCollection('commands');
10
+ const queries = await getCollection('queries');
11
+ const services = await getCollection('services');
12
+
13
+ type ServiceWithSchema = {
14
+ collection: string;
15
+ id: string;
16
+ version: string;
17
+ specification: string;
18
+ summary: string;
19
+ };
20
+
21
+ const servicesWithSchemas = services.filter((service) => getSpecificationsForService(service).length > 0);
22
+
23
+ const servicesWithSchemasFlat = servicesWithSchemas.reduce<ServiceWithSchema[]>((acc, service) => {
24
+ return [
25
+ ...acc,
26
+ ...getSpecificationsForService(service).map((specification) => ({
27
+ collection: 'services',
28
+ id: service.data.id,
29
+ version: service.data.version,
30
+ specification: specification.type,
31
+ summary: service.data.summary?.trim() || '',
32
+ })),
33
+ ];
34
+ }, []) as ServiceWithSchema[];
35
+
36
+ const messageHasSchema = (message: CollectionEntry<'events' | 'commands' | 'queries'>) => {
37
+ return message.data.schemaPath;
38
+ };
39
+
40
+ export const GET: APIRoute = async ({ params, request }) => {
41
+ if (!isEventCatalogScaleEnabled()) {
42
+ return new Response(
43
+ JSON.stringify({
44
+ error: 'feature_not_available_on_server',
45
+ message: 'Schema API is not enabled for this deployment and supported in EventCatalog Scale.',
46
+ }),
47
+ { status: 501, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' } }
48
+ );
49
+ }
50
+
51
+ const url = new URL(request.url);
52
+ const baseUrl = process.env.LLMS_TXT_BASE_URL || `${url.origin}`;
53
+
54
+ const formatVersionedItem = (item: any, type: string, extraParams?: string | string[]) => {
55
+ return `- [${item.data.name} - ${item.data.id} - ${item.data.version}](${baseUrl}/api/schemas/${type}/${item.data.id}/${item.data.version})} ${item.data.summary ? `- ${item.data.summary.trim()}` : ''}`;
56
+ };
57
+
58
+ const formatServiceWithSchema = (item: ServiceWithSchema) => {
59
+ return `- [${item.id} - ${item.version} - ${item.specification} specification](${baseUrl}/api/schemas/${item.collection}/${item.id}/${item.version}/${item.specification}) ${item.summary ? `- Specification for ${item.summary}` : ''}`;
60
+ };
61
+
62
+ const content = [
63
+ `# ${config.organizationName} EventCatalog Schemas`,
64
+ `List of schemas for events, commands, queries, and services in EventCatalog.`,
65
+ '',
66
+ `## Events\n${events
67
+ .filter(messageHasSchema)
68
+ .map((item) => formatVersionedItem(item, 'events'))
69
+ .join('\n')}`,
70
+ '',
71
+ `## Commands\n${commands
72
+ .filter(messageHasSchema)
73
+ .map((item) => formatVersionedItem(item, 'commands'))
74
+ .join('\n')}`,
75
+ '',
76
+ `## Queries\n${queries
77
+ .filter(messageHasSchema)
78
+ .map((item) => formatVersionedItem(item, 'queries'))
79
+ .join('\n')}`,
80
+ '',
81
+ `## Services\n${servicesWithSchemasFlat.map((item: any) => formatServiceWithSchema(item)).join('\n')}`,
82
+ ].join('\n');
83
+ return new Response(content, {
84
+ headers: { 'Content-Type': 'text/plain; charset=utf-8' },
85
+ });
86
+ };
@@ -0,0 +1,175 @@
1
+ ---
2
+ import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
3
+ import { getEvents } from '@utils/events';
4
+ import { getCommands } from '@utils/commands';
5
+ import { getQueries } from '@utils/queries';
6
+ import { getServices, getSpecificationsForService } from '@utils/collections/services';
7
+ import SchemaExplorer from '@components/SchemaExplorer/SchemaExplorer';
8
+ import { isEventCatalogScaleEnabled } from '@utils/feature';
9
+ import { getOwner } from '@utils/collections/owners';
10
+ import { buildUrl } from '@utils/url-builder';
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+
14
+ // Fetch all messages
15
+ const events = await getEvents({ getAllVersions: true });
16
+ const commands = await getCommands({ getAllVersions: true });
17
+ const queries = await getQueries({ getAllVersions: true });
18
+
19
+ // Fetch all services
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];
161
+
162
+ const apiAccessEnabled = isEventCatalogScaleEnabled();
163
+ ---
164
+
165
+ <VerticalSideBarLayout title="Schema Explorer - EventCatalog">
166
+ <main class="flex sm:px-8 docs-layout h-[calc(100vh-var(--header-height,0px)-64px)]">
167
+ <div class="flex docs-layout w-full h-full">
168
+ <div class="w-full lg:mr-2 pr-8 py-6 flex flex-col h-full">
169
+ <div class="w-full !max-w-none h-full flex flex-col overflow-hidden">
170
+ <SchemaExplorer client:load schemas={allSchemas as any} apiAccessEnabled={apiAccessEnabled} />
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </main>
175
+ </VerticalSideBarLayout>
@@ -20,3 +20,12 @@ export type PageTypes =
20
20
  | 'flows'
21
21
  | 'entities'
22
22
  | 'containers';
23
+
24
+ export type TableConfiguration = {
25
+ columns: {
26
+ [key: string]: {
27
+ label?: string;
28
+ visible?: boolean;
29
+ };
30
+ };
31
+ };
@@ -50,3 +50,12 @@ export const getAbsoluteFilePathForAstroFile = (filePath: string, fileName?: str
50
50
 
51
51
  return resolveProjectPath(filePath, PROJECT_DIR);
52
52
  };
53
+
54
+ /**
55
+ * Checks if a file path is an Avro schema based on its extension
56
+ * @param filePath - The file path to check
57
+ * @returns True if the file is an Avro schema (.avro or .avsc)
58
+ */
59
+ export const isAvroSchema = (filePath: string): boolean => {
60
+ return filePath.endsWith('.avro') || filePath.endsWith('.avsc');
61
+ };
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/event-catalog/eventcatalog.git"
7
7
  },
8
8
  "type": "module",
9
- "version": "2.62.1",
9
+ "version": "2.64.0",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },