@eventcatalog/core 4.10.9 → 4.10.11

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 (39) 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-3EEFSHHW.js → chunk-3XTFNVGA.js} +1 -1
  6. package/dist/{chunk-ONRJA5DX.js → chunk-N6U5CNT7.js} +1 -1
  7. package/dist/{chunk-BYPWHDX6.js → chunk-RLGMIZSH.js} +1 -1
  8. package/dist/{chunk-5WB6CXZH.js → chunk-TWZKZIRW.js} +1 -1
  9. package/dist/{chunk-EG62NTWS.js → chunk-U52FCZQ6.js} +7 -7
  10. package/dist/{chunk-WD5UW4A4.js → chunk-VFRR3M72.js} +1 -1
  11. package/dist/constants.cjs +1 -1
  12. package/dist/constants.js +1 -1
  13. package/dist/eventcatalog.cjs +1 -1
  14. package/dist/eventcatalog.js +13 -13
  15. package/dist/federation/federate.js +5 -5
  16. package/dist/federation/source-provider.js +2 -2
  17. package/dist/generate.cjs +1 -1
  18. package/dist/generate.js +3 -3
  19. package/dist/utils/cli-logger.cjs +1 -1
  20. package/dist/utils/cli-logger.js +2 -2
  21. package/eventcatalog/src/components/Grids/DomainGrid.tsx +20 -12
  22. package/eventcatalog/src/components/Grids/message-link.spec.ts +59 -0
  23. package/eventcatalog/src/components/Grids/message-link.ts +38 -0
  24. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +58 -12
  25. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +5 -5
  26. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +2 -0
  27. package/eventcatalog/src/components/SchemaExplorer/types.ts +10 -1
  28. package/eventcatalog/src/components/SchemaExplorer/useSchemaDetails.ts +69 -0
  29. package/eventcatalog/src/components/SchemaExplorer/utils.ts +12 -0
  30. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +88 -0
  31. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +19 -8
  32. package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +3 -269
  33. package/eventcatalog/src/pages/schemas/explorer/content/[key].json.ts +17 -0
  34. package/eventcatalog/src/utils/collections/domains.ts +1 -80
  35. package/eventcatalog/src/utils/collections/hydrate-services.ts +51 -0
  36. package/eventcatalog/src/utils/collections/systems.ts +17 -7
  37. package/eventcatalog/src/utils/schema-explorer.ts +276 -0
  38. package/package.json +3 -3
  39. package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
@@ -20,7 +20,7 @@ export interface Owner {
20
20
  }
21
21
 
22
22
  export interface SchemaItem {
23
- collection: CollectionMessageTypes | 'services' | 'data-products';
23
+ collection: CollectionMessageTypes | 'services' | 'domains' | 'data-products';
24
24
  data: {
25
25
  id: string;
26
26
  name: string;
@@ -29,9 +29,12 @@ export interface SchemaItem {
29
29
  schemaPath?: string;
30
30
  producers?: Producer[];
31
31
  consumers?: Consumer[];
32
+ producerName?: string;
32
33
  owners?: Owner[];
33
34
  };
34
35
  schemaContent?: string;
36
+ /** Internal URL for loading this version's content without embedding it in the page. */
37
+ contentUrl?: string;
35
38
  schemaExtension?: string;
36
39
  specType?: string;
37
40
  specName?: string;
@@ -44,6 +47,12 @@ export interface SchemaItem {
44
47
  examples?: MessageExample[];
45
48
  }
46
49
 
50
+ export interface SchemaDetails {
51
+ schemaContent: string;
52
+ examples: MessageExample[];
53
+ data?: Pick<SchemaItem['data'], 'producers' | 'consumers'>;
54
+ }
55
+
47
56
  export interface VersionDiff {
48
57
  fromVersion: string;
49
58
  toVersion: string;
@@ -0,0 +1,69 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import type { SchemaDetails, SchemaItem } from './types';
3
+
4
+ // Scoped to the explorer component's lifetime by the caller. Also deduplicates
5
+ // requests when the selected version is one of the comparison versions.
6
+ export const createSchemaDetailsLoader = () => {
7
+ const requests = new Map<string, Promise<SchemaDetails>>();
8
+ const resolved = new Map<string, SchemaDetails>();
9
+ const load = (url: string): Promise<SchemaDetails> => {
10
+ const existing = requests.get(url);
11
+ if (existing) return existing;
12
+ const request = fetch(url)
13
+ .then(async (response) => {
14
+ if (!response.ok) throw new Error('Unable to load schema content. Please try again.');
15
+ const details = await response.json();
16
+ if (typeof details.schemaContent !== 'string' || !Array.isArray(details.examples)) {
17
+ throw new Error('Unable to load schema content. Please try again.');
18
+ }
19
+ resolved.set(url, details);
20
+ return details as SchemaDetails;
21
+ })
22
+ .catch((error) => {
23
+ requests.delete(url);
24
+ throw error;
25
+ });
26
+ requests.set(url, request);
27
+ return request;
28
+ };
29
+ return Object.assign(load, { peek: (url: string) => resolved.get(url) });
30
+ };
31
+
32
+ export function useSchemaDetails(
33
+ item: SchemaItem | undefined,
34
+ load: ReturnType<typeof createSchemaDetailsLoader>,
35
+ enabled = true
36
+ ) {
37
+ const url = enabled ? item?.contentUrl : undefined;
38
+ const [attempt, setAttempt] = useState(0);
39
+ const [state, setState] = useState<{ url: string; details?: SchemaDetails; error?: string }>();
40
+
41
+ useEffect(() => {
42
+ if (!url || load.peek(url)) return;
43
+ let current = true;
44
+ setState(undefined);
45
+ load(url).then(
46
+ (details) => current && setState({ url, details }),
47
+ () => current && setState({ url, error: 'Unable to load schema content. Please try again.' })
48
+ );
49
+ return () => {
50
+ current = false;
51
+ };
52
+ }, [url, load, attempt]);
53
+
54
+ const matchingState = state?.url === url ? state : undefined;
55
+ const details = url ? matchingState?.details || load.peek(url) : undefined;
56
+ const message = useMemo(
57
+ () => (item && details ? { ...item, ...details, data: { ...item.data, ...details.data } } : item),
58
+ [item, details]
59
+ );
60
+ return {
61
+ message,
62
+ loading: !!url && !details && !matchingState?.error,
63
+ error: details ? undefined : matchingState?.error,
64
+ retry: () => {
65
+ setState(undefined);
66
+ setAttempt((value) => value + 1);
67
+ },
68
+ };
69
+ }
@@ -41,6 +41,18 @@ export function extractServiceName(refId: string): string {
41
41
  return match ? match[1] : refId;
42
42
  }
43
43
 
44
+ /** Resolve both enriched collection entries and compact explorer references. */
45
+ export function getSchemaRelationshipReference(reference: {
46
+ id: string;
47
+ version?: string;
48
+ data?: { id: string; version: string };
49
+ }) {
50
+ return {
51
+ id: reference.data?.id ?? reference.id,
52
+ version: reference.data?.version ?? reference.version,
53
+ };
54
+ }
55
+
44
56
  export const getLanguageForHighlight = (extension?: string): string => {
45
57
  if (!extension) return 'json';
46
58
  const ext = extension.toLowerCase();
@@ -0,0 +1,88 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { loadArchitectureItems, Page } from './_index.data';
3
+
4
+ const collectionMocks = vi.hoisted(() => ({
5
+ getDomains: vi.fn(),
6
+ getServices: vi.fn(),
7
+ getSystems: vi.fn(),
8
+ }));
9
+
10
+ vi.mock('@utils/feature', () => ({
11
+ isSSR: () => false,
12
+ }));
13
+
14
+ vi.mock('@utils/collections/domains', () => ({ getDomains: collectionMocks.getDomains }));
15
+ vi.mock('@utils/collections/systems', () => ({ getSystems: collectionMocks.getSystems }));
16
+ vi.mock('@utils/page-loaders/page-data-loader', () => ({
17
+ pageDataLoader: {
18
+ services: (...args: unknown[]) => collectionMocks.getServices(...args),
19
+ domains: (...args: unknown[]) => collectionMocks.getDomains(...args),
20
+ systems: (...args: unknown[]) => collectionMocks.getSystems(...args),
21
+ },
22
+ }));
23
+
24
+ const domain = {
25
+ collection: 'domains',
26
+ data: { id: 'ordering', name: 'Ordering', version: '1.0.0' },
27
+ };
28
+
29
+ const service = {
30
+ collection: 'services',
31
+ data: { id: 'order-service', name: 'Order Service', version: '1.0.0' },
32
+ };
33
+
34
+ const system = {
35
+ collection: 'systems',
36
+ data: { id: 'order-management-system', name: 'Order Management', version: '1.0.0' },
37
+ };
38
+
39
+ describe('architecture page data', () => {
40
+ beforeEach(() => {
41
+ collectionMocks.getDomains.mockReset().mockResolvedValue([domain]);
42
+ collectionMocks.getServices.mockReset().mockResolvedValue([service]);
43
+ collectionMocks.getSystems.mockReset().mockResolvedValue([system]);
44
+ });
45
+
46
+ it('hydrates domain and system services for static architecture paths', async () => {
47
+ const paths = await Page.getStaticPaths();
48
+
49
+ expect(collectionMocks.getDomains).toHaveBeenCalledWith({ enrichServices: true });
50
+ expect(collectionMocks.getSystems).toHaveBeenCalledWith({ enrichServices: true });
51
+ expect(paths).toEqual(
52
+ expect.arrayContaining([
53
+ expect.objectContaining({
54
+ params: { type: 'domains', id: 'ordering', version: '1.0.0' },
55
+ }),
56
+ expect.objectContaining({
57
+ params: { type: 'systems', id: 'order-management-system', version: '1.0.0' },
58
+ }),
59
+ expect.objectContaining({
60
+ params: { type: 'services', id: 'order-service', version: '1.0.0' },
61
+ }),
62
+ ])
63
+ );
64
+ });
65
+
66
+ it('hydrates domain services when architecture pages are fetched in SSR', async () => {
67
+ await Page.getData({
68
+ props: {},
69
+ params: { type: 'domains', id: 'ordering', version: '1.0.0' },
70
+ } as any);
71
+
72
+ expect(collectionMocks.getDomains).toHaveBeenCalledWith({ enrichServices: true });
73
+ });
74
+
75
+ it('hydrates system services when architecture pages are fetched in SSR', async () => {
76
+ await Page.getData({
77
+ props: {},
78
+ params: { type: 'systems', id: 'order-management-system', version: '1.0.0' },
79
+ } as any);
80
+
81
+ expect(collectionMocks.getSystems).toHaveBeenCalledWith({ enrichServices: true });
82
+ });
83
+
84
+ it('loads domains with enrichServices so DomainGrid command links keep their collection', async () => {
85
+ await loadArchitectureItems('domains');
86
+ expect(collectionMocks.getDomains).toHaveBeenCalledWith({ enrichServices: true });
87
+ });
88
+ });
@@ -3,11 +3,27 @@ import { HybridPage } from '@utils/page-loaders/hybrid-page';
3
3
  import type { PageTypes } from '@types';
4
4
  import { pageDataLoader } from '@utils/page-loaders/page-data-loader';
5
5
  import { getDomains } from '@utils/collections/domains';
6
- import { getServices } from '@utils/collections/services';
7
6
  import { getSystems } from '@utils/collections/systems';
8
7
 
9
8
  const architecturePageTypes: PageTypes[] = ['services', 'domains', 'systems'];
10
9
 
10
+ /**
11
+ * Architecture grids render service sends/receives as docs links, so domains and
12
+ * systems must hydrate those messages (collection + name). `pageDataLoader`
13
+ * uses the cheaper unenriched path used by docs/sidebar.
14
+ */
15
+ export const loadArchitectureItems = (type: PageTypes) => {
16
+ if (type === 'domains') {
17
+ return getDomains({ enrichServices: true });
18
+ }
19
+
20
+ if (type === 'systems') {
21
+ return getSystems({ enrichServices: true });
22
+ }
23
+
24
+ return pageDataLoader[type as PageTypes]();
25
+ };
26
+
11
27
  /**
12
28
  * Documentation page class for all collection types with versioning
13
29
  */
@@ -17,11 +33,7 @@ export class Page extends HybridPage {
17
33
  return [];
18
34
  }
19
35
 
20
- const domains = await getDomains({ enrichServices: true });
21
- const services = await getServices();
22
- const systems = await getSystems();
23
-
24
- const pageData = [services, domains, systems];
36
+ const pageData = await Promise.all(architecturePageTypes.map((type) => loadArchitectureItems(type)));
25
37
 
26
38
  return pageData.flatMap((items, index) =>
27
39
  items.map((item) => ({
@@ -47,8 +59,7 @@ export class Page extends HybridPage {
47
59
  return null;
48
60
  }
49
61
 
50
- // Get all items of the specified type
51
- const items = await pageDataLoader[type as PageTypes]();
62
+ const items = await loadArchitectureItems(type as PageTypes);
52
63
 
53
64
  // Find the specific item by id and version
54
65
  const item = items.find((i) => i.data.id === id && i.data.version === version);
@@ -1,272 +1,6 @@
1
1
  import { isSSR } from '@utils/feature';
2
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 { getDomains, getSpecificationsForDomain } from '@utils/collections/domains';
8
- import { getDataProducts } from '@utils/collections/data-products';
9
- import { getOwner } from '@utils/collections/owners';
10
- import { buildUrl } from '@utils/url-builder';
11
- import { resourceFileExists, readResourceFile } from '@utils/resource-files';
12
- import { getExamplesForResource } from '@utils/collections/examples';
13
- import { getCollection } from 'astro:content';
14
- import path from 'path';
15
-
16
- // Helper function to enrich owners with full details
17
- async function enrichOwners(ownersRaw: any[]) {
18
- if (!ownersRaw || ownersRaw.length === 0) return [];
19
-
20
- const owners = await Promise.all(ownersRaw.map(getOwner));
21
- const filteredOwners = owners.filter((o) => o !== undefined);
22
-
23
- return filteredOwners.map((o) => ({
24
- id: o.data.id,
25
- name: o.data.name,
26
- type: o.collection,
27
- href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
28
- }));
29
- }
30
-
31
- async function fetchAllSchemas() {
32
- // Fetch all messages
33
- const events = await getEvents({ getAllVersions: true });
34
- const commands = await getCommands({ getAllVersions: true });
35
- const queries = await getQueries({ getAllVersions: true });
36
- const schemaEntries = await getCollection('schemas');
37
-
38
- // Fetch all services
39
- const services = await getServices({ getAllVersions: true });
40
-
41
- // Combine all messages
42
- const allMessages = [...events, ...commands, ...queries];
43
- const messagesBySchemaReference = new Map(
44
- allMessages.map((message) => [`${message.collection}:${message.data.id}:${message.data.version}`, message])
45
- );
46
-
47
- // Read message schemas from the generated schemas collection.
48
- const messagesWithSchemas = await Promise.all(
49
- schemaEntries.map(async (schema) => {
50
- const message = messagesBySchemaReference.get(
51
- `${schema.data.message.collectionName}:${schema.data.message.id}:${schema.data.message.version}`
52
- );
53
- const schemaPath = schema.data.file || schema.data.source.path || '';
54
- const schemaExtension = path.extname(schemaPath).slice(1) || schema.data.format;
55
-
56
- if (message) {
57
- try {
58
- const enrichedOwners = await enrichOwners(schema.data.message.owners || []);
59
-
60
- return {
61
- collection: message.collection,
62
- data: {
63
- id: message.data.id,
64
- name: schema.data.message.name || message.data.name,
65
- version: message.data.version,
66
- summary: schema.data.message.summary || message.data.summary,
67
- schemaPath,
68
- producers: message.data.producers || [],
69
- consumers: message.data.consumers || [],
70
- owners: enrichedOwners,
71
- },
72
- schemaContent: schema.data.content || '',
73
- schemaExtension,
74
- examples: getExamplesForResource(message),
75
- };
76
- } catch (error) {
77
- console.error(`Error reading schema metadata for ${message.data.id}:`, error);
78
- const enrichedOwners = await enrichOwners(schema.data.message.owners || []);
79
- return {
80
- collection: message.collection,
81
- data: {
82
- id: message.data.id,
83
- name: schema.data.message.name || schema.data.name || message.data.name,
84
- version: message.data.version,
85
- summary: schema.data.message.summary || message.data.summary,
86
- schemaPath,
87
- producers: message.data.producers || [],
88
- consumers: message.data.consumers || [],
89
- owners: enrichedOwners,
90
- },
91
- schemaContent: schema.data.content || '',
92
- schemaExtension,
93
- };
94
- }
95
- }
96
-
97
- return {
98
- collection: schema.data.message.collectionName,
99
- data: {
100
- id: schema.data.message.id,
101
- name: schema.data.message.name || schema.data.name || schema.data.message.id,
102
- version: schema.data.message.version,
103
- summary: schema.data.message.summary,
104
- schemaPath,
105
- owners: await enrichOwners(schema.data.message.owners || []),
106
- producers: [],
107
- consumers: [],
108
- },
109
- schemaContent: schema.data.content || '',
110
- schemaExtension,
111
- examples: [],
112
- };
113
- })
114
- );
115
-
116
- // Filter services with specifications and read spec content - only keep essential data
117
- const servicesWithSpecs = await Promise.all(
118
- services.map(async (service) => {
119
- try {
120
- const specifications = getSpecificationsForService(service);
121
-
122
- if (specifications.length === 0) {
123
- return null;
124
- }
125
-
126
- return await Promise.all(
127
- specifications.map(async (spec) => {
128
- if (!resourceFileExists(service, spec.path)) {
129
- return null;
130
- }
131
-
132
- const schemaContent = readResourceFile(service, spec.path) ?? '';
133
- const schemaExtension = spec.type;
134
- const enrichedOwners = await enrichOwners(service.data.owners || []);
135
-
136
- return {
137
- collection: 'services',
138
- data: {
139
- id: `${service.data.id}`,
140
- name: `${service.data.name} - ${spec.name}`,
141
- version: service.data.version,
142
- summary: service.data.summary,
143
- schemaPath: spec.path,
144
- owners: enrichedOwners,
145
- },
146
- schemaContent,
147
- schemaExtension,
148
- specType: spec.type,
149
- specName: spec.name,
150
- specFilenameWithoutExtension: spec.filenameWithoutExtension,
151
- };
152
- })
153
- );
154
- } catch (error) {
155
- console.error(`Error reading specifications for service ${service.data.id}:`, error);
156
- return null;
157
- }
158
- })
159
- );
160
-
161
- // Flatten and filter out null values
162
- const flatServicesWithSpecs = servicesWithSpecs.flat().filter((service) => service !== null);
163
-
164
- // Fetch all domains
165
- const domains = await getDomains({ getAllVersions: true });
166
-
167
- // Filter domains with specifications and read spec content - only keep essential data
168
- const domainsWithSpecs = await Promise.all(
169
- domains.map(async (domain) => {
170
- try {
171
- const specifications = getSpecificationsForDomain(domain);
172
-
173
- if (specifications.length === 0) {
174
- return null;
175
- }
176
-
177
- return await Promise.all(
178
- specifications.map(async (spec) => {
179
- if (!resourceFileExists(domain, spec.path)) {
180
- return null;
181
- }
182
-
183
- const schemaContent = readResourceFile(domain, spec.path) ?? '';
184
- const schemaExtension = spec.type;
185
- const enrichedOwners = await enrichOwners(domain.data.owners || []);
186
-
187
- return {
188
- collection: 'domains',
189
- data: {
190
- id: `${domain.data.id}`,
191
- name: `${domain.data.name} - ${spec.name}`,
192
- version: domain.data.version,
193
- summary: domain.data.summary,
194
- schemaPath: spec.path,
195
- owners: enrichedOwners,
196
- },
197
- schemaContent,
198
- schemaExtension,
199
- specType: spec.type,
200
- specName: spec.name,
201
- specFilenameWithoutExtension: spec.filenameWithoutExtension,
202
- };
203
- })
204
- );
205
- } catch (error) {
206
- console.error(`Error reading specifications for domain ${domain.data.id}:`, error);
207
- return null;
208
- }
209
- })
210
- );
211
-
212
- // Flatten and filter out null values for domains
213
- const flatDomainsWithSpecs = domainsWithSpecs.flat().filter((domain) => domain !== null);
214
-
215
- // Fetch all data products and extract contracts from outputs
216
- const dataProducts = await getDataProducts({ getAllVersions: true });
217
-
218
- // Filter data products with contracts in outputs and read contract content
219
- const dataProductsWithContracts = await Promise.all(
220
- dataProducts.map(async (dataProduct) => {
221
- try {
222
- const outputs = dataProduct.data.outputs || [];
223
- const outputsWithContracts = outputs.filter((output) => output.contract);
224
-
225
- if (outputsWithContracts.length === 0) {
226
- return null;
227
- }
228
-
229
- return await Promise.all(
230
- outputsWithContracts.map(async (output) => {
231
- const contract = output.contract!;
232
- if (!resourceFileExists(dataProduct, contract.path)) {
233
- return null;
234
- }
235
-
236
- const schemaContent = readResourceFile(dataProduct, contract.path) ?? '';
237
- const schemaExtension = path.extname(contract.path).slice(1) || 'json';
238
- const enrichedOwners = await enrichOwners(dataProduct.data.owners || []);
239
-
240
- return {
241
- collection: 'data-products',
242
- data: {
243
- id: `${dataProduct.data.id}__${contract.path}`,
244
- name: contract.name,
245
- version: dataProduct.data.version,
246
- summary: `Data contract for ${dataProduct.data.name}`,
247
- schemaPath: contract.path,
248
- owners: enrichedOwners,
249
- },
250
- schemaContent,
251
- schemaExtension,
252
- contractType: contract.type,
253
- dataProductId: dataProduct.data.id,
254
- dataProductVersion: dataProduct.data.version,
255
- };
256
- })
257
- );
258
- } catch (error) {
259
- console.error(`Error reading contracts for data product ${dataProduct.data.id}:`, error);
260
- return null;
261
- }
262
- })
263
- );
264
-
265
- // Flatten and filter out null values for data product contracts
266
- const flatDataProductContracts = dataProductsWithContracts.flat().filter((contract) => contract !== null);
267
-
268
- return [...messagesWithSchemas, ...flatServicesWithSpecs, ...flatDomainsWithSpecs, ...flatDataProductContracts];
269
- }
3
+ import { getSchemaMetadata } from '@utils/schema-explorer';
270
4
 
271
5
  export class Page extends HybridPage {
272
6
  static get prerender(): boolean {
@@ -278,7 +12,7 @@ export class Page extends HybridPage {
278
12
  return [];
279
13
  }
280
14
 
281
- const allSchemas = await fetchAllSchemas();
15
+ const allSchemas = await getSchemaMetadata();
282
16
 
283
17
  return [
284
18
  {
@@ -291,7 +25,7 @@ export class Page extends HybridPage {
291
25
  }
292
26
 
293
27
  protected static async fetchData(_params: any) {
294
- const allSchemas = await fetchAllSchemas();
28
+ const allSchemas = await getSchemaMetadata();
295
29
  return {
296
30
  schemas: allSchemas,
297
31
  };
@@ -0,0 +1,17 @@
1
+ import type { APIRoute } from 'astro';
2
+ import { isSSR } from '@utils/feature';
3
+ import { getSchemaRegistry, getSchemaDetails } from '@utils/schema-explorer';
4
+
5
+ export const prerender = !isSSR();
6
+
7
+ export async function getStaticPaths() {
8
+ return [...(await getSchemaRegistry()).keys()].map((key) => ({ params: { key } }));
9
+ }
10
+
11
+ export const GET: APIRoute = async ({ params }) => {
12
+ const details = params.key ? await getSchemaDetails(params.key) : undefined;
13
+ if (!details) return new Response('Schema not found', { status: 404 });
14
+ return new Response(JSON.stringify(details), {
15
+ headers: { 'Content-Type': 'application/json' },
16
+ });
17
+ };
@@ -4,6 +4,7 @@ import path from 'path';
4
4
  import type { CollectionMessageTypes } from '@types';
5
5
  import type { Agent, Service } from './types';
6
6
  import { createVersionedMap, findInMap, processSpecifications } from '@utils/collections/util';
7
+ import { hydrateAgents, hydrateServices } from '@utils/collections/hydrate-services';
7
8
 
8
9
  const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
9
10
 
@@ -19,86 +20,6 @@ interface Props {
19
20
  // Simple in-memory cache variable
20
21
  let memoryCache: Record<string, Domain[]> = {};
21
22
 
22
- // Helper to hydrate services
23
- const hydrateServices = (
24
- servicesList: any[],
25
- serviceMap: Map<string, any[]>,
26
- messageMap: Map<string, any[]>,
27
- containerMap: Map<string, any[]>
28
- ) => {
29
- return servicesList
30
- .map((service: { id: string; version: string | undefined }) => findInMap(serviceMap, service.id, service.version))
31
- .filter((s) => !!s)
32
- .map((service) => {
33
- // Hydrate service messages and containers
34
- const sends = (service.data.sends || [])
35
- .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
36
- .filter((m: any) => !!m);
37
-
38
- const receives = (service.data.receives || [])
39
- .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
40
- .filter((m: any) => !!m);
41
-
42
- const readsFrom = (service.data.readsFrom || [])
43
- .map((c: any) => findInMap(containerMap, c.id, c.version))
44
- .filter((c: any) => !!c);
45
-
46
- const writesTo = (service.data.writesTo || [])
47
- .map((c: any) => findInMap(containerMap, c.id, c.version))
48
- .filter((c: any) => !!c);
49
-
50
- return {
51
- ...service,
52
- data: {
53
- ...service.data,
54
- sends: sends as any,
55
- receives: receives as any,
56
- readsFrom: readsFrom as any,
57
- writesTo: writesTo as any,
58
- },
59
- };
60
- });
61
- };
62
-
63
- const hydrateAgents = (
64
- agentsList: any[],
65
- agentMap: Map<string, any[]>,
66
- messageMap: Map<string, any[]>,
67
- containerMap: Map<string, any[]>
68
- ) => {
69
- return agentsList
70
- .map((agent: { id: string; version: string | undefined }) => findInMap(agentMap, agent.id, agent.version))
71
- .filter((a) => !!a)
72
- .map((agent) => {
73
- const sends = (agent.data.sends || [])
74
- .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
75
- .filter((m: any) => !!m);
76
-
77
- const receives = (agent.data.receives || [])
78
- .map((msg: any) => findInMap(messageMap, msg.id, msg.version))
79
- .filter((m: any) => !!m);
80
-
81
- const readsFrom = (agent.data.readsFrom || [])
82
- .map((c: any) => findInMap(containerMap, c.id, c.version))
83
- .filter((c: any) => !!c);
84
-
85
- const writesTo = (agent.data.writesTo || [])
86
- .map((c: any) => findInMap(containerMap, c.id, c.version))
87
- .filter((c: any) => !!c);
88
-
89
- return {
90
- ...agent,
91
- data: {
92
- ...agent.data,
93
- sends: sends as any,
94
- receives: receives as any,
95
- readsFrom: readsFrom as any,
96
- writesTo: writesTo as any,
97
- },
98
- };
99
- });
100
- };
101
-
102
23
  // --- MAIN FUNCTION ---
103
24
 
104
25
  export const getDomains = async ({