@eventcatalog/core 3.44.2 → 3.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-BLJ5FRR3.js → chunk-2QK37BNF.js} +1 -1
- package/dist/{chunk-LIIOK6SZ.js → chunk-6XNMBNIU.js} +1 -1
- package/dist/{chunk-THDZCUFV.js → chunk-E4K4KTSR.js} +1 -1
- package/dist/{chunk-IU3D7JRW.js → chunk-G6KYCMUM.js} +1 -1
- package/dist/{chunk-RUACTEJR.js → chunk-KHL25572.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/content.config.ts +58 -0
- package/eventcatalog/src/enterprise/api/schemas/[collection]/[id]/[version]/index.ts +42 -50
- package/eventcatalog/src/pages/docs/llm/schemas.txt.ts +25 -17
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +33 -16
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +18 -8
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +45 -21
- package/eventcatalog/src/utils/collections/schema-loader.ts +237 -0
- package/package.json +3 -3
|
@@ -140,7 +140,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
140
140
|
var import_os = __toESM(require("os"), 1);
|
|
141
141
|
|
|
142
142
|
// package.json
|
|
143
|
-
var version = "3.
|
|
143
|
+
var version = "3.45.0";
|
|
144
144
|
|
|
145
145
|
// src/constants.ts
|
|
146
146
|
var VERSION = version;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-2QK37BNF.js";
|
|
4
|
+
import "../chunk-6XNMBNIU.js";
|
|
5
5
|
import "../chunk-DAOXTQVS.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-E4K4KTSR.js";
|
|
7
7
|
import "../chunk-6QENHZZP.js";
|
|
8
8
|
export {
|
|
9
9
|
log_build_default as default
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
package/dist/eventcatalog.cjs
CHANGED
|
@@ -144,7 +144,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
144
144
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
145
145
|
|
|
146
146
|
// package.json
|
|
147
|
-
var version = "3.
|
|
147
|
+
var version = "3.45.0";
|
|
148
148
|
|
|
149
149
|
// src/constants.ts
|
|
150
150
|
var VERSION = version;
|
package/dist/eventcatalog.js
CHANGED
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
} from "./chunk-3H2RT3CM.js";
|
|
14
14
|
import {
|
|
15
15
|
log_build_default
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-2QK37BNF.js";
|
|
17
|
+
import "./chunk-6XNMBNIU.js";
|
|
18
18
|
import "./chunk-DAOXTQVS.js";
|
|
19
19
|
import {
|
|
20
20
|
catalogToAstro
|
|
@@ -28,13 +28,13 @@ import {
|
|
|
28
28
|
} from "./chunk-B7HCX5HM.js";
|
|
29
29
|
import {
|
|
30
30
|
generate
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-KHL25572.js";
|
|
32
32
|
import {
|
|
33
33
|
logger
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-G6KYCMUM.js";
|
|
35
35
|
import {
|
|
36
36
|
VERSION
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-E4K4KTSR.js";
|
|
38
38
|
import {
|
|
39
39
|
getEventCatalogConfigFile,
|
|
40
40
|
verifyRequiredFieldsAreInCatalogConfigFile
|
package/dist/generate.cjs
CHANGED
|
@@ -108,7 +108,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
|
|
|
108
108
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
109
109
|
|
|
110
110
|
// package.json
|
|
111
|
-
var version = "3.
|
|
111
|
+
var version = "3.45.0";
|
|
112
112
|
|
|
113
113
|
// src/constants.ts
|
|
114
114
|
var VERSION = version;
|
package/dist/generate.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generate
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-KHL25572.js";
|
|
4
|
+
import "./chunk-G6KYCMUM.js";
|
|
5
|
+
import "./chunk-E4K4KTSR.js";
|
|
6
6
|
import "./chunk-6QENHZZP.js";
|
|
7
7
|
export {
|
|
8
8
|
generate
|
package/dist/utils/cli-logger.js
CHANGED
|
@@ -9,6 +9,7 @@ import fs from 'fs';
|
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { userTeamDirectoryLoader } from './enterprise/directory/user-team-directory';
|
|
11
11
|
import config from '@config';
|
|
12
|
+
import { schemaLoader } from './utils/collections/schema-loader';
|
|
12
13
|
|
|
13
14
|
// Enterprise Collections
|
|
14
15
|
import { customPagesSchema, resourceDocsSchema, resourceDocCategoriesSchema } from './enterprise/collections';
|
|
@@ -68,6 +69,16 @@ const channelPointer = z
|
|
|
68
69
|
})
|
|
69
70
|
.extend(pointer.shape);
|
|
70
71
|
|
|
72
|
+
const schemaPointer = z.object({
|
|
73
|
+
id: z.string().optional(),
|
|
74
|
+
file: z.string().optional(),
|
|
75
|
+
path: z.string().optional(),
|
|
76
|
+
name: z.string().optional(),
|
|
77
|
+
format: z.string().optional(),
|
|
78
|
+
environments: z.array(z.string()).optional(),
|
|
79
|
+
default: z.boolean().optional(),
|
|
80
|
+
});
|
|
81
|
+
|
|
71
82
|
const sendsPointer = z.object({
|
|
72
83
|
id: z.string(),
|
|
73
84
|
version: z.string().optional().default('latest'),
|
|
@@ -375,6 +386,7 @@ const events = defineCollection({
|
|
|
375
386
|
producers: z.array(reference('services')).optional(),
|
|
376
387
|
consumers: z.array(reference('services')).optional(),
|
|
377
388
|
channels: z.array(channelPointer).optional(),
|
|
389
|
+
schemas: z.array(schemaPointer).optional(),
|
|
378
390
|
messageChannels: z.array(reference('channels')).optional(),
|
|
379
391
|
detailsPanel: messageDetailsPanelPropertySchema.optional(),
|
|
380
392
|
})
|
|
@@ -397,6 +409,7 @@ const commands = defineCollection({
|
|
|
397
409
|
producers: z.array(reference('services')).optional(),
|
|
398
410
|
consumers: z.array(reference('services')).optional(),
|
|
399
411
|
channels: z.array(channelPointer).optional(),
|
|
412
|
+
schemas: z.array(schemaPointer).optional(),
|
|
400
413
|
detailsPanel: messageDetailsPanelPropertySchema.optional(),
|
|
401
414
|
messageChannels: z.array(reference('channels')).optional(),
|
|
402
415
|
})
|
|
@@ -419,6 +432,7 @@ const queries = defineCollection({
|
|
|
419
432
|
producers: z.array(reference('services')).optional(),
|
|
420
433
|
consumers: z.array(reference('services')).optional(),
|
|
421
434
|
channels: z.array(channelPointer).optional(),
|
|
435
|
+
schemas: z.array(schemaPointer).optional(),
|
|
422
436
|
detailsPanel: messageDetailsPanelPropertySchema.optional(),
|
|
423
437
|
messageChannels: z.array(reference('channels')).optional(),
|
|
424
438
|
})
|
|
@@ -991,6 +1005,47 @@ const diagrams = defineCollection({
|
|
|
991
1005
|
.extend(baseSchema.shape),
|
|
992
1006
|
});
|
|
993
1007
|
|
|
1008
|
+
const schemas = defineCollection({
|
|
1009
|
+
loader: schemaLoader({
|
|
1010
|
+
messages: {
|
|
1011
|
+
pattern: withIgnoredBuildArtifacts([
|
|
1012
|
+
'**/events/*/index.{md,mdx}',
|
|
1013
|
+
'**/events/*/versioned/*/index.{md,mdx}',
|
|
1014
|
+
'**/commands/*/index.{md,mdx}',
|
|
1015
|
+
'**/commands/*/versioned/*/index.{md,mdx}',
|
|
1016
|
+
'**/queries/*/index.{md,mdx}',
|
|
1017
|
+
'**/queries/*/versioned/*/index.{md,mdx}',
|
|
1018
|
+
]) as string[],
|
|
1019
|
+
base: projectDirBase,
|
|
1020
|
+
},
|
|
1021
|
+
}),
|
|
1022
|
+
schema: z
|
|
1023
|
+
.object({
|
|
1024
|
+
format: z.string(),
|
|
1025
|
+
content: z.string().optional(),
|
|
1026
|
+
file: z.string().optional(),
|
|
1027
|
+
filePath: z.string().optional(),
|
|
1028
|
+
environments: z.array(z.string()).optional(),
|
|
1029
|
+
default: z.boolean().optional(),
|
|
1030
|
+
latest: z.boolean().optional(),
|
|
1031
|
+
message: z.object({
|
|
1032
|
+
collection: z.enum(['events', 'commands', 'queries']),
|
|
1033
|
+
id: z.string(),
|
|
1034
|
+
name: z.string().optional(),
|
|
1035
|
+
version: z.string(),
|
|
1036
|
+
summary: z.string().optional(),
|
|
1037
|
+
owners: z.array(z.string()).optional(),
|
|
1038
|
+
}),
|
|
1039
|
+
source: z.object({
|
|
1040
|
+
provider: z.string(),
|
|
1041
|
+
path: z.string().optional(),
|
|
1042
|
+
url: z.string().optional(),
|
|
1043
|
+
}),
|
|
1044
|
+
readOnly: z.boolean().optional(),
|
|
1045
|
+
})
|
|
1046
|
+
.extend(baseSchema.shape),
|
|
1047
|
+
});
|
|
1048
|
+
|
|
994
1049
|
export const collections = {
|
|
995
1050
|
events,
|
|
996
1051
|
commands,
|
|
@@ -1024,4 +1079,7 @@ export const collections = {
|
|
|
1024
1079
|
|
|
1025
1080
|
// Diagrams Collection
|
|
1026
1081
|
diagrams,
|
|
1082
|
+
|
|
1083
|
+
// Generated from message schema references
|
|
1084
|
+
schemas,
|
|
1027
1085
|
};
|
|
@@ -5,62 +5,55 @@
|
|
|
5
5
|
|
|
6
6
|
import type { APIRoute } from 'astro';
|
|
7
7
|
import { getCollection } from 'astro:content';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import utils from '@eventcatalog/sdk';
|
|
11
8
|
import { isEventCatalogScaleEnabled } from '@utils/feature';
|
|
12
9
|
import { sortVersioned } from '@utils/collections/util';
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
const findSchema = async (collection: string | undefined, id: string, version: string | undefined) => {
|
|
12
|
+
const schemas = await getCollection('schemas');
|
|
13
|
+
const matchingSchemas = schemas.filter(
|
|
14
|
+
(schema) => schema.data.message.collection === collection && schema.data.message.id === id
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (version === 'latest') {
|
|
18
|
+
return (
|
|
19
|
+
matchingSchemas.find((schema) => schema.data.latest) ??
|
|
20
|
+
sortVersioned(matchingSchemas, (schema) => schema.data.message.version)[0]
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return matchingSchemas.find((schema) => schema.data.message.version === version);
|
|
25
|
+
};
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
.filter((message) => fs.existsSync(path.join(path.dirname(message.filePath ?? ''), message.data.schemaPath ?? '')));
|
|
27
|
+
export async function getStaticPaths() {
|
|
28
|
+
const schemas = await getCollection('schemas');
|
|
23
29
|
|
|
24
30
|
// Generate paths for specific versions
|
|
25
|
-
const versionedPaths =
|
|
26
|
-
params: {
|
|
31
|
+
const versionedPaths = schemas.map((schema) => ({
|
|
32
|
+
params: {
|
|
33
|
+
collection: schema.data.message.collection,
|
|
34
|
+
id: schema.data.message.id,
|
|
35
|
+
version: schema.data.message.version,
|
|
36
|
+
},
|
|
27
37
|
props: {
|
|
28
|
-
pathToSchema:
|
|
29
|
-
schema:
|
|
30
|
-
extension: message.data.schemaPath?.split('.').pop(),
|
|
38
|
+
pathToSchema: schema.data.filePath,
|
|
39
|
+
schema: schema.data.content,
|
|
31
40
|
},
|
|
32
41
|
}));
|
|
33
42
|
|
|
34
|
-
// Group messages by collection and id to find latest versions
|
|
35
|
-
const groupedMessages = messagesWithSchemas.reduce(
|
|
36
|
-
(acc, message) => {
|
|
37
|
-
const key = `${message.collection}:${message.data.id}`;
|
|
38
|
-
if (!acc[key]) {
|
|
39
|
-
acc[key] = [];
|
|
40
|
-
}
|
|
41
|
-
acc[key].push(message);
|
|
42
|
-
return acc;
|
|
43
|
-
},
|
|
44
|
-
{} as Record<string, typeof messagesWithSchemas>
|
|
45
|
-
);
|
|
46
|
-
|
|
47
43
|
// Generate "latest" paths for each unique collection/id combination
|
|
48
|
-
const latestPaths =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
const latestPaths = schemas
|
|
45
|
+
.filter((schema) => schema.data.latest)
|
|
46
|
+
.map((latestSchema) => ({
|
|
47
|
+
params: {
|
|
48
|
+
collection: latestSchema.data.message.collection,
|
|
49
|
+
id: latestSchema.data.message.id,
|
|
50
|
+
version: 'latest',
|
|
51
|
+
},
|
|
54
52
|
props: {
|
|
55
|
-
pathToSchema:
|
|
56
|
-
schema:
|
|
57
|
-
path.join(path.dirname(latestMessage.filePath ?? ''), latestMessage.data.schemaPath ?? ''),
|
|
58
|
-
'utf8'
|
|
59
|
-
),
|
|
60
|
-
extension: latestMessage.data.schemaPath?.split('.').pop(),
|
|
53
|
+
pathToSchema: latestSchema.data.filePath,
|
|
54
|
+
schema: latestSchema.data.content,
|
|
61
55
|
},
|
|
62
|
-
};
|
|
63
|
-
});
|
|
56
|
+
}));
|
|
64
57
|
|
|
65
58
|
return [...versionedPaths, ...latestPaths];
|
|
66
59
|
}
|
|
@@ -83,14 +76,14 @@ export const GET: APIRoute = async ({ props, params }) => {
|
|
|
83
76
|
}
|
|
84
77
|
|
|
85
78
|
// In static mode, props are pre-computed by getStaticPaths
|
|
86
|
-
if (props.schema) {
|
|
79
|
+
if (props.schema !== undefined) {
|
|
87
80
|
return new Response(props.schema, {
|
|
88
81
|
headers: { 'Content-Type': 'text/plain' },
|
|
89
82
|
});
|
|
90
83
|
}
|
|
91
84
|
|
|
92
|
-
// In SSR mode, dynamically resolve the schema using the
|
|
93
|
-
const { id, version } = params;
|
|
85
|
+
// In SSR mode, dynamically resolve the schema using the generated schema collection
|
|
86
|
+
const { collection, id, version } = params;
|
|
94
87
|
|
|
95
88
|
if (!id) {
|
|
96
89
|
return new Response(JSON.stringify({ error: 'Missing id parameter' }), {
|
|
@@ -99,17 +92,16 @@ export const GET: APIRoute = async ({ props, params }) => {
|
|
|
99
92
|
});
|
|
100
93
|
}
|
|
101
94
|
|
|
102
|
-
const
|
|
103
|
-
const result = await getSchemaForMessage(id, version === 'latest' ? undefined : version);
|
|
95
|
+
const schema = await findSchema(collection, id, version);
|
|
104
96
|
|
|
105
|
-
if (
|
|
97
|
+
if (schema?.data.content === undefined) {
|
|
106
98
|
return new Response(JSON.stringify({ error: 'Schema not found' }), {
|
|
107
99
|
status: 404,
|
|
108
100
|
headers: { 'Content-Type': 'application/json' },
|
|
109
101
|
});
|
|
110
102
|
}
|
|
111
103
|
|
|
112
|
-
return new Response(
|
|
104
|
+
return new Response(schema.data.content, {
|
|
113
105
|
headers: { 'Content-Type': 'text/plain' },
|
|
114
106
|
});
|
|
115
107
|
};
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { getCollection } from 'astro:content';
|
|
2
2
|
import config from '@config';
|
|
3
3
|
import type { APIRoute } from 'astro';
|
|
4
|
-
import type { CollectionEntry } from 'astro:content';
|
|
5
4
|
import { getSpecificationsForService } from '@utils/collections/services';
|
|
6
5
|
import { isEventCatalogScaleEnabled } from '@utils/feature';
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const queries = await getCollection('queries');
|
|
7
|
+
type MessageCollection = 'events' | 'commands' | 'queries';
|
|
8
|
+
|
|
11
9
|
const services = await getCollection('services');
|
|
10
|
+
const schemas = await getCollection('schemas');
|
|
12
11
|
|
|
13
12
|
type ServiceWithSchema = {
|
|
14
13
|
collection: string;
|
|
@@ -33,8 +32,19 @@ const servicesWithSchemasFlat = servicesWithSchemas.reduce<ServiceWithSchema[]>(
|
|
|
33
32
|
];
|
|
34
33
|
}, []) as ServiceWithSchema[];
|
|
35
34
|
|
|
36
|
-
const
|
|
37
|
-
|
|
35
|
+
const getMessagesWithSchemas = (collection: MessageCollection) => {
|
|
36
|
+
const seenMessages = new Set<string>();
|
|
37
|
+
|
|
38
|
+
return schemas
|
|
39
|
+
.filter((schema) => schema.data.message.collection === collection)
|
|
40
|
+
.map((schema) => {
|
|
41
|
+
const key = `${schema.data.message.collection}:${schema.data.message.id}:${schema.data.message.version}`;
|
|
42
|
+
if (seenMessages.has(key)) return null;
|
|
43
|
+
seenMessages.add(key);
|
|
44
|
+
|
|
45
|
+
return schema;
|
|
46
|
+
})
|
|
47
|
+
.filter((schema): schema is NonNullable<typeof schema> => schema !== null);
|
|
38
48
|
};
|
|
39
49
|
|
|
40
50
|
export const GET: APIRoute = async ({ params, request }) => {
|
|
@@ -51,8 +61,9 @@ export const GET: APIRoute = async ({ params, request }) => {
|
|
|
51
61
|
const url = new URL(request.url);
|
|
52
62
|
const baseUrl = process.env.LLMS_TXT_BASE_URL || `${url.origin}`;
|
|
53
63
|
|
|
54
|
-
const formatVersionedItem = (item:
|
|
55
|
-
|
|
64
|
+
const formatVersionedItem = (item: (typeof schemas)[number]) => {
|
|
65
|
+
const message = item.data.message;
|
|
66
|
+
return `- [${message.name || message.id} - ${message.id} - ${message.version}](${baseUrl}/api/schemas/${message.collection}/${message.id}/${message.version})} ${message.summary ? `- ${message.summary.trim()}` : ''}`;
|
|
56
67
|
};
|
|
57
68
|
|
|
58
69
|
const formatServiceWithSchema = (item: ServiceWithSchema) => {
|
|
@@ -63,19 +74,16 @@ export const GET: APIRoute = async ({ params, request }) => {
|
|
|
63
74
|
`# ${config.organizationName} EventCatalog Schemas`,
|
|
64
75
|
`List of schemas for events, commands, queries, and services in EventCatalog.`,
|
|
65
76
|
'',
|
|
66
|
-
`## Events\n${events
|
|
67
|
-
.
|
|
68
|
-
.map((item) => formatVersionedItem(item, 'events'))
|
|
77
|
+
`## Events\n${getMessagesWithSchemas('events')
|
|
78
|
+
.map((item) => formatVersionedItem(item))
|
|
69
79
|
.join('\n')}`,
|
|
70
80
|
'',
|
|
71
|
-
`## Commands\n${commands
|
|
72
|
-
.
|
|
73
|
-
.map((item) => formatVersionedItem(item, 'commands'))
|
|
81
|
+
`## Commands\n${getMessagesWithSchemas('commands')
|
|
82
|
+
.map((item) => formatVersionedItem(item))
|
|
74
83
|
.join('\n')}`,
|
|
75
84
|
'',
|
|
76
|
-
`## Queries\n${queries
|
|
77
|
-
.
|
|
78
|
-
.map((item) => formatVersionedItem(item, 'queries'))
|
|
85
|
+
`## Queries\n${getMessagesWithSchemas('queries')
|
|
86
|
+
.map((item) => formatVersionedItem(item))
|
|
79
87
|
.join('\n')}`,
|
|
80
88
|
'',
|
|
81
89
|
`## Services\n${servicesWithSchemasFlat.map((item: any) => formatServiceWithSchema(item)).join('\n')}`,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getCollection } from 'astro:content';
|
|
1
2
|
import { isSSR } from '@utils/feature';
|
|
2
3
|
import { HybridPage } from '@utils/page-loaders/hybrid-page';
|
|
3
4
|
import type { PageTypes } from '@types';
|
|
@@ -23,25 +24,41 @@ export class Page extends HybridPage {
|
|
|
23
24
|
// 'entities',
|
|
24
25
|
// 'containers',
|
|
25
26
|
];
|
|
26
|
-
const allItems = await Promise.all(
|
|
27
|
+
const [schemas, ...allItems] = await Promise.all([
|
|
28
|
+
getCollection('schemas'),
|
|
29
|
+
...itemTypes.map((type) => pageDataLoader[type]()),
|
|
30
|
+
]);
|
|
31
|
+
const itemsByKey = new Map(allItems.flat().map((item) => [`${item.collection}:${item.data.id}:${item.data.version}`, item]));
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
const
|
|
33
|
+
const seenMessageSchemas = new Set<string>();
|
|
34
|
+
const messageSchemas = schemas.filter((schema) => {
|
|
35
|
+
const key = `${schema.data.message.collection}:${schema.data.message.id}:${schema.data.message.version}`;
|
|
36
|
+
if (seenMessageSchemas.has(key)) return false;
|
|
37
|
+
seenMessageSchemas.add(key);
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
30
40
|
|
|
31
41
|
// Generate paths for messages with schemas
|
|
32
|
-
const messagePaths =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const messagePaths = messageSchemas
|
|
43
|
+
.map((schema) => {
|
|
44
|
+
const item = itemsByKey.get(`${schema.data.message.collection}:${schema.data.message.id}:${schema.data.message.version}`);
|
|
45
|
+
if (!item) return null;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
params: {
|
|
49
|
+
type: schema.data.message.collection,
|
|
50
|
+
id: schema.data.message.id,
|
|
51
|
+
version: schema.data.message.version,
|
|
52
|
+
},
|
|
53
|
+
props: {
|
|
54
|
+
type: schema.data.message.collection,
|
|
55
|
+
...item,
|
|
56
|
+
// Not everything needs the body of the page itself.
|
|
57
|
+
body: undefined,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
})
|
|
61
|
+
.filter((path): path is NonNullable<typeof path> => path !== null);
|
|
45
62
|
|
|
46
63
|
// Generate paths for data products with contracts
|
|
47
64
|
const dataProducts = await pageDataLoader['data-products']();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { PageTypes } from '@types';
|
|
3
|
+
import { getCollection } from 'astro:content';
|
|
3
4
|
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
4
5
|
import { Page } from './_index.data';
|
|
5
6
|
import SchemaPageViewer from '@components/SchemaExplorer/SchemaPageViewer';
|
|
@@ -76,32 +77,41 @@ if (isDataProduct && contractPath) {
|
|
|
76
77
|
}
|
|
77
78
|
} else {
|
|
78
79
|
// Handle regular messages (events, commands, queries)
|
|
79
|
-
const allItems = await pageDataLoader[type]();
|
|
80
|
+
const [allItems, schemaEntries] = await Promise.all([pageDataLoader[type](), getCollection('schemas')]);
|
|
80
81
|
const versions = allItems.filter((item) => item.data.id === data.id);
|
|
82
|
+
const schemasForMessage = schemaEntries.filter(
|
|
83
|
+
(schema) => schema.data.message.collection === type && schema.data.message.id === data.id
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const getSchemaForVersion = (version: string) => {
|
|
87
|
+
const schemasForVersion = schemasForMessage.filter((schema) => schema.data.message.version === version);
|
|
88
|
+
return schemasForVersion.find((schema) => schema.data.default) ?? schemasForVersion[0];
|
|
89
|
+
};
|
|
81
90
|
|
|
82
91
|
// Transform to SchemaItems
|
|
83
92
|
const availableVersions = versions
|
|
84
|
-
.filter((message) => message.data.schemaPath && resourceFileExists(message, message.data.schemaPath))
|
|
85
93
|
.map((message) => {
|
|
86
94
|
try {
|
|
87
|
-
const
|
|
88
|
-
|
|
95
|
+
const schema = getSchemaForVersion(message.data.version);
|
|
96
|
+
if (!schema) return null;
|
|
97
|
+
|
|
98
|
+
const schemaPath = schema.data.file || schema.data.source.path || '';
|
|
89
99
|
const schemaExtension = path.extname(schemaPath).slice(1);
|
|
90
100
|
|
|
91
101
|
return {
|
|
92
102
|
collection: message.collection,
|
|
93
103
|
data: {
|
|
94
104
|
id: message.data.id,
|
|
95
|
-
name: message.data.name,
|
|
105
|
+
name: schema.data.message.name || message.data.name,
|
|
96
106
|
version: message.data.version,
|
|
97
|
-
summary: message.data.summary,
|
|
98
|
-
schemaPath
|
|
107
|
+
summary: schema.data.message.summary || message.data.summary,
|
|
108
|
+
schemaPath,
|
|
99
109
|
// @ts-ignore
|
|
100
110
|
producers: message.data.producers || [],
|
|
101
111
|
// @ts-ignore
|
|
102
112
|
consumers: message.data.consumers || [],
|
|
103
113
|
},
|
|
104
|
-
schemaContent,
|
|
114
|
+
schemaContent: schema.data.content || '',
|
|
105
115
|
schemaExtension,
|
|
106
116
|
examples: getExamplesForResource(message),
|
|
107
117
|
} as SchemaItem;
|
|
@@ -10,6 +10,7 @@ import { getOwner } from '@utils/collections/owners';
|
|
|
10
10
|
import { buildUrl } from '@utils/url-builder';
|
|
11
11
|
import { resourceFileExists, readResourceFile } from '@utils/resource-files';
|
|
12
12
|
import { getExamplesForResource } from '@utils/collections/examples';
|
|
13
|
+
import { getCollection } from 'astro:content';
|
|
13
14
|
import path from 'path';
|
|
14
15
|
|
|
15
16
|
// Helper function to enrich owners with full details
|
|
@@ -32,61 +33,84 @@ async function fetchAllSchemas() {
|
|
|
32
33
|
const events = await getEvents({ getAllVersions: true });
|
|
33
34
|
const commands = await getCommands({ getAllVersions: true });
|
|
34
35
|
const queries = await getQueries({ getAllVersions: true });
|
|
36
|
+
const schemaEntries = await getCollection('schemas');
|
|
35
37
|
|
|
36
38
|
// Fetch all services
|
|
37
39
|
const services = await getServices({ getAllVersions: true });
|
|
38
40
|
|
|
39
41
|
// Combine all messages
|
|
40
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
|
+
);
|
|
41
46
|
|
|
42
|
-
//
|
|
47
|
+
// Read message schemas from the generated schemas collection.
|
|
43
48
|
const messagesWithSchemas = await Promise.all(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
schemaEntries.map(async (schema) => {
|
|
50
|
+
const message = messagesBySchemaReference.get(
|
|
51
|
+
`${schema.data.message.collection}:${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) {
|
|
48
57
|
try {
|
|
49
|
-
const
|
|
50
|
-
const schemaContent = readResourceFile(message, schemaPath) ?? '';
|
|
51
|
-
const schemaExtension = path.extname(schemaPath).slice(1);
|
|
52
|
-
const enrichedOwners = await enrichOwners(message.data.owners || []);
|
|
58
|
+
const enrichedOwners = await enrichOwners(schema.data.message.owners || []);
|
|
53
59
|
|
|
54
60
|
return {
|
|
55
61
|
collection: message.collection,
|
|
56
62
|
data: {
|
|
57
63
|
id: message.data.id,
|
|
58
|
-
name: message.data.name,
|
|
64
|
+
name: schema.data.message.name || message.data.name,
|
|
59
65
|
version: message.data.version,
|
|
60
|
-
summary: message.data.summary,
|
|
61
|
-
schemaPath
|
|
66
|
+
summary: schema.data.message.summary || message.data.summary,
|
|
67
|
+
schemaPath,
|
|
62
68
|
producers: message.data.producers || [],
|
|
63
69
|
consumers: message.data.consumers || [],
|
|
64
70
|
owners: enrichedOwners,
|
|
65
71
|
},
|
|
66
|
-
schemaContent,
|
|
72
|
+
schemaContent: schema.data.content || '',
|
|
67
73
|
schemaExtension,
|
|
68
74
|
examples: getExamplesForResource(message),
|
|
69
75
|
};
|
|
70
76
|
} catch (error) {
|
|
71
|
-
console.error(`Error reading schema for ${message.data.id}:`, error);
|
|
72
|
-
const enrichedOwners = await enrichOwners(
|
|
77
|
+
console.error(`Error reading schema metadata for ${message.data.id}:`, error);
|
|
78
|
+
const enrichedOwners = await enrichOwners(schema.data.message.owners || []);
|
|
73
79
|
return {
|
|
74
80
|
collection: message.collection,
|
|
75
81
|
data: {
|
|
76
82
|
id: message.data.id,
|
|
77
|
-
name: message.data.name,
|
|
83
|
+
name: schema.data.message.name || schema.data.name || message.data.name,
|
|
78
84
|
version: message.data.version,
|
|
79
|
-
summary: message.data.summary,
|
|
80
|
-
schemaPath
|
|
85
|
+
summary: schema.data.message.summary || message.data.summary,
|
|
86
|
+
schemaPath,
|
|
81
87
|
producers: message.data.producers || [],
|
|
82
88
|
consumers: message.data.consumers || [],
|
|
83
89
|
owners: enrichedOwners,
|
|
84
90
|
},
|
|
85
|
-
schemaContent: '',
|
|
86
|
-
schemaExtension
|
|
91
|
+
schemaContent: schema.data.content || '',
|
|
92
|
+
schemaExtension,
|
|
87
93
|
};
|
|
88
94
|
}
|
|
89
|
-
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
collection: schema.data.message.collection,
|
|
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
|
+
})
|
|
90
114
|
);
|
|
91
115
|
|
|
92
116
|
// Filter services with specifications and read spec content - only keep essential data
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import type { Loader } from 'astro/loaders';
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import matter from 'gray-matter';
|
|
4
|
+
import fs from 'node:fs/promises';
|
|
5
|
+
import fsSync from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { sortVersioned } from './util';
|
|
8
|
+
|
|
9
|
+
type MessageCollection = 'events' | 'commands' | 'queries';
|
|
10
|
+
|
|
11
|
+
type SchemaReference = {
|
|
12
|
+
id?: string;
|
|
13
|
+
file?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
format?: string;
|
|
17
|
+
environments?: string[];
|
|
18
|
+
default?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type MessageFrontmatter = {
|
|
22
|
+
id?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
version?: string;
|
|
25
|
+
summary?: string;
|
|
26
|
+
owners?: string[];
|
|
27
|
+
schemaPath?: string;
|
|
28
|
+
schemas?: SchemaReference[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type MessageSchemaResource = {
|
|
32
|
+
id: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
version?: string;
|
|
35
|
+
format: string;
|
|
36
|
+
content?: string;
|
|
37
|
+
file?: string;
|
|
38
|
+
filePath?: string;
|
|
39
|
+
environments?: string[];
|
|
40
|
+
default?: boolean;
|
|
41
|
+
latest?: boolean;
|
|
42
|
+
message: {
|
|
43
|
+
collection: MessageCollection;
|
|
44
|
+
id: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
version: string;
|
|
47
|
+
summary?: string;
|
|
48
|
+
owners?: string[];
|
|
49
|
+
};
|
|
50
|
+
source: {
|
|
51
|
+
provider: 'file';
|
|
52
|
+
path: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
type SchemaLoaderOptions = {
|
|
57
|
+
messages: {
|
|
58
|
+
pattern: string[];
|
|
59
|
+
base?: string;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type LoaderContext = Parameters<Loader['load']>[0];
|
|
64
|
+
|
|
65
|
+
const MESSAGE_COLLECTIONS: MessageCollection[] = ['events', 'commands', 'queries'];
|
|
66
|
+
|
|
67
|
+
const normalizePath = (value: string) => value.replace(/\\/g, '/');
|
|
68
|
+
|
|
69
|
+
const getMessageCollectionFromPath = (filePath: string): MessageCollection | undefined => {
|
|
70
|
+
const parts = normalizePath(filePath).split('/');
|
|
71
|
+
return MESSAGE_COLLECTIONS.find((collection) => parts.includes(collection));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getSchemaFormat = (schemaPath: string) => {
|
|
75
|
+
const extension = path.extname(schemaPath).replace('.', '').toLowerCase();
|
|
76
|
+
|
|
77
|
+
if (extension === 'avsc' || extension === 'avro') return 'avro';
|
|
78
|
+
if (extension === 'proto') return 'protobuf';
|
|
79
|
+
if (extension === 'json') return 'jsonschema';
|
|
80
|
+
if (extension === 'yaml' || extension === 'yml') return 'yaml';
|
|
81
|
+
|
|
82
|
+
return extension || 'unknown';
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const buildGeneratedSchemaId = (message: { collection: MessageCollection; id: string; version: string }, schemaPath: string) =>
|
|
86
|
+
`schema:${message.collection}:${message.id}:${message.version}:${schemaPath}`;
|
|
87
|
+
|
|
88
|
+
const getSchemaFile = (reference: SchemaReference) => reference.file ?? reference.path;
|
|
89
|
+
|
|
90
|
+
const schemaFileExists = (schema: MessageSchemaResource): schema is MessageSchemaResource & { filePath: string } =>
|
|
91
|
+
Boolean(schema.filePath && fsSync.existsSync(schema.filePath));
|
|
92
|
+
|
|
93
|
+
const addLatestMetadata = (schemas: MessageSchemaResource[]) => {
|
|
94
|
+
const schemasByMessage = schemas.reduce(
|
|
95
|
+
(acc, schema) => {
|
|
96
|
+
const key = `${schema.message.collection}:${schema.message.id}`;
|
|
97
|
+
acc[key] = [...(acc[key] ?? []), schema];
|
|
98
|
+
return acc;
|
|
99
|
+
},
|
|
100
|
+
{} as Record<string, MessageSchemaResource[]>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const latestVersionsByMessage = Object.entries(schemasByMessage).reduce(
|
|
104
|
+
(acc, [key, messageSchemas]) => {
|
|
105
|
+
acc[key] = sortVersioned(messageSchemas, (schema) => schema.message.version)[0]?.message.version;
|
|
106
|
+
return acc;
|
|
107
|
+
},
|
|
108
|
+
{} as Record<string, string | undefined>
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return schemas.map((schema) => {
|
|
112
|
+
const key = `${schema.message.collection}:${schema.message.id}`;
|
|
113
|
+
return {
|
|
114
|
+
...schema,
|
|
115
|
+
latest: schema.message.version === latestVersionsByMessage[key],
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const getMessageSchemasFromFrontmatter = ({
|
|
121
|
+
data,
|
|
122
|
+
collection,
|
|
123
|
+
messageFilePath,
|
|
124
|
+
}: {
|
|
125
|
+
data: MessageFrontmatter;
|
|
126
|
+
collection: MessageCollection;
|
|
127
|
+
messageFilePath: string;
|
|
128
|
+
}): MessageSchemaResource[] => {
|
|
129
|
+
if (!data.id || !data.version) return [];
|
|
130
|
+
|
|
131
|
+
const message = {
|
|
132
|
+
collection,
|
|
133
|
+
id: data.id,
|
|
134
|
+
name: data.name,
|
|
135
|
+
version: data.version,
|
|
136
|
+
summary: data.summary,
|
|
137
|
+
owners: data.owners,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const schemaReferences =
|
|
141
|
+
data.schemas?.filter((schema) => getSchemaFile(schema)) ??
|
|
142
|
+
(data.schemaPath
|
|
143
|
+
? [
|
|
144
|
+
{
|
|
145
|
+
id: buildGeneratedSchemaId(message, data.schemaPath),
|
|
146
|
+
file: data.schemaPath,
|
|
147
|
+
name: 'Schema',
|
|
148
|
+
default: true,
|
|
149
|
+
},
|
|
150
|
+
]
|
|
151
|
+
: []);
|
|
152
|
+
|
|
153
|
+
return schemaReferences.map((reference) => {
|
|
154
|
+
const schemaFile = getSchemaFile(reference) as string;
|
|
155
|
+
const schemaFilePath = path.resolve(path.dirname(messageFilePath), schemaFile);
|
|
156
|
+
const schemaId = reference.id ?? buildGeneratedSchemaId(message, schemaFile);
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
id: schemaId,
|
|
160
|
+
name: reference.name ?? schemaId,
|
|
161
|
+
version: data.version,
|
|
162
|
+
format: reference.format ?? getSchemaFormat(schemaFile),
|
|
163
|
+
file: schemaFile,
|
|
164
|
+
filePath: schemaFilePath,
|
|
165
|
+
environments: reference.environments,
|
|
166
|
+
default: reference.default,
|
|
167
|
+
message,
|
|
168
|
+
source: {
|
|
169
|
+
provider: 'file',
|
|
170
|
+
path: schemaFile,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const loadMessageSchemas = async ({ pattern, base }: SchemaLoaderOptions['messages']) => {
|
|
177
|
+
if (!base) return [];
|
|
178
|
+
|
|
179
|
+
const files = await glob(pattern, {
|
|
180
|
+
cwd: base,
|
|
181
|
+
absolute: true,
|
|
182
|
+
nodir: true,
|
|
183
|
+
ignore: ['dist/**', '**/dist/**'],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const schemas = await Promise.all(
|
|
187
|
+
files.map(async (file) => {
|
|
188
|
+
const collection = getMessageCollectionFromPath(file);
|
|
189
|
+
if (!collection) return [];
|
|
190
|
+
|
|
191
|
+
const { data } = matter.read(file) as { data: MessageFrontmatter };
|
|
192
|
+
return getMessageSchemasFromFrontmatter({ data, collection, messageFilePath: file });
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return addLatestMetadata(schemas.flat().filter(schemaFileExists));
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const getSchemaBody = async (schema: MessageSchemaResource) => {
|
|
200
|
+
if (!schemaFileExists(schema)) return undefined;
|
|
201
|
+
return fs.readFile(schema.filePath, 'utf8');
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const setSchema = async (context: LoaderContext, schema: MessageSchemaResource) => {
|
|
205
|
+
const body = await getSchemaBody(schema);
|
|
206
|
+
if (body === undefined) return;
|
|
207
|
+
|
|
208
|
+
const schemaWithContent = {
|
|
209
|
+
...schema,
|
|
210
|
+
content: body,
|
|
211
|
+
};
|
|
212
|
+
const parsedData = await context.parseData({
|
|
213
|
+
id: schema.id,
|
|
214
|
+
data: schemaWithContent,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
context.store.set({
|
|
218
|
+
id: schema.id,
|
|
219
|
+
data: parsedData,
|
|
220
|
+
body,
|
|
221
|
+
digest: context.generateDigest(schemaWithContent),
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const schemaLoader = ({ messages }: SchemaLoaderOptions): Loader => {
|
|
226
|
+
return {
|
|
227
|
+
name: 'eventcatalog-schema-loader',
|
|
228
|
+
load: async (context) => {
|
|
229
|
+
context.store.clear();
|
|
230
|
+
const schemas = await loadMessageSchemas(messages);
|
|
231
|
+
|
|
232
|
+
for (const schema of schemas) {
|
|
233
|
+
await setSchema(context, schema);
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
};
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.45.0",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -113,8 +113,8 @@
|
|
|
113
113
|
"uuid": "^10.0.0",
|
|
114
114
|
"zod": "^4.3.6",
|
|
115
115
|
"@eventcatalog/sdk": "2.24.1",
|
|
116
|
-
"@eventcatalog/
|
|
117
|
-
"@eventcatalog/
|
|
116
|
+
"@eventcatalog/linter": "1.0.29",
|
|
117
|
+
"@eventcatalog/visualiser": "^3.22.1"
|
|
118
118
|
},
|
|
119
119
|
"devDependencies": {
|
|
120
120
|
"@astrojs/check": "^0.9.9",
|