@eventcatalog/core 4.10.8 → 4.10.10
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-W6IMDOJJ.js → chunk-K45ABG6N.js} +1 -1
- package/dist/{chunk-LB5EMDAY.js → chunk-PR77FB54.js} +1 -1
- package/dist/{chunk-CDWE6WWB.js → chunk-QXANW76J.js} +1 -1
- package/dist/{chunk-W5C4K7F2.js → chunk-U6R6BFJ5.js} +1 -1
- package/dist/{chunk-DRK5QSQC.js → chunk-Y6VX2CGL.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/components/MDX/NodeGraph/NodeGraph.astro +6 -2
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +58 -12
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +5 -5
- package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/types.ts +10 -1
- package/eventcatalog/src/components/SchemaExplorer/useSchemaDetails.ts +69 -0
- package/eventcatalog/src/components/SchemaExplorer/utils.ts +12 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.spec.ts +52 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.ts +20 -11
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +3 -269
- package/eventcatalog/src/pages/schemas/explorer/content/[key].json.ts +17 -0
- package/eventcatalog/src/stores/sidebar-store/state.ts +3 -28
- package/eventcatalog/src/utils/collections/field-usage.ts +48 -0
- package/eventcatalog/src/utils/node-graphs/channel-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/compact-visualiser-graph.ts +246 -0
- package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/data-products-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/domains-canvas.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/system-context-node-graph.ts +2 -2
- package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +8 -3
- package/eventcatalog/src/utils/node-graphs/utils/utils.ts +30 -2
- package/eventcatalog/src/utils/schema-explorer.ts +276 -0
- package/package.json +3 -3
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
2
|
import {
|
|
4
3
|
createDagreGraph,
|
|
5
4
|
generateIdForNode,
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
buildContextMenuForResource,
|
|
11
10
|
DEFAULT_NODE_WIDTH,
|
|
12
11
|
DEFAULT_NODE_HEIGHT,
|
|
12
|
+
layoutDagreGraph,
|
|
13
13
|
} from '@utils/node-graphs/utils/utils';
|
|
14
14
|
|
|
15
15
|
import { findInMap, createVersionedMap, mergeMaps, collectionToResourceMap } from '@utils/collections/util';
|
|
@@ -220,7 +220,7 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
|
|
|
220
220
|
|
|
221
221
|
if (layout) {
|
|
222
222
|
// Render the diagram in memory getting the X and Y
|
|
223
|
-
|
|
223
|
+
layoutDagreGraph(flow);
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
// Find any duplicated edges, and merge them into one edge
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
2
|
import {
|
|
4
3
|
generateIdForNode,
|
|
5
4
|
createDagreGraph,
|
|
6
5
|
calculatedNodes,
|
|
7
6
|
createEdge,
|
|
8
7
|
getOperationFields,
|
|
8
|
+
layoutDagreGraph,
|
|
9
9
|
} from '@utils/node-graphs/utils/utils';
|
|
10
10
|
import { findInMap, createVersionedMap } from '@utils/collections/util';
|
|
11
11
|
import type { Node, Edge } from '@xyflow/react';
|
|
@@ -247,7 +247,7 @@ export const getDomainsCanvasData = async (): Promise<DomainCanvasData> => {
|
|
|
247
247
|
});
|
|
248
248
|
|
|
249
249
|
// Calculate layout using dagre
|
|
250
|
-
|
|
250
|
+
layoutDagreGraph(dagreGraph);
|
|
251
251
|
|
|
252
252
|
// Apply calculated positions to nodes
|
|
253
253
|
const layoutedDomainNodes = calculatedNodes(dagreGraph, domainNodes);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCollection } from 'astro:content';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
2
|
import {
|
|
4
3
|
createDagreGraph,
|
|
5
4
|
calculatedNodes,
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
getEdgeLabelForServiceAsTarget,
|
|
8
7
|
generatedIdForEdge,
|
|
9
8
|
createEdge,
|
|
9
|
+
layoutDagreGraph,
|
|
10
10
|
} from '@utils/node-graphs/utils/utils';
|
|
11
11
|
import { getNodesAndEdges as getServicesNodeAndEdges } from './services-node-graph';
|
|
12
12
|
import { getNodesAndEdges as getAgentsNodeAndEdges } from './agents-node-graph';
|
|
@@ -176,7 +176,7 @@ export const getNodesAndEdges = async ({
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
if (layout) {
|
|
179
|
-
|
|
179
|
+
layoutDagreGraph(flow);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
return {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
2
|
import {
|
|
4
3
|
createDagreGraph,
|
|
5
4
|
calculatedNodes,
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
DEFAULT_NODE_HEIGHT,
|
|
8
7
|
buildContextMenuForResource,
|
|
9
8
|
buildContextMenuForService,
|
|
9
|
+
layoutDagreGraph,
|
|
10
10
|
} from '@utils/node-graphs/utils/utils';
|
|
11
11
|
import { MarkerType } from '@xyflow/react';
|
|
12
12
|
import type { Node as NodeType } from '@xyflow/react';
|
|
@@ -321,7 +321,7 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
|
|
|
321
321
|
graph.setEdge(edge.source, edge.target);
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
-
|
|
324
|
+
layoutDagreGraph(graph);
|
|
325
325
|
|
|
326
326
|
return {
|
|
327
327
|
nodes: calculatedNodes(graph, nodes),
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// import { getColor } from '@utils/colors';
|
|
2
2
|
import { getEvents } from '@utils/collections/events';
|
|
3
3
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
4
|
-
import dagre from 'dagre';
|
|
5
4
|
import {
|
|
6
5
|
calculatedNodes,
|
|
7
6
|
createDagreGraph,
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
getEdgeLabelForMessageAsSource,
|
|
13
12
|
getEdgeLabelForServiceAsTarget,
|
|
14
13
|
versionMatches,
|
|
14
|
+
layoutDagreGraph,
|
|
15
15
|
} from './utils/utils';
|
|
16
16
|
import { MarkerType, type Node, type Edge } from '@xyflow/react';
|
|
17
17
|
import {
|
|
@@ -691,7 +691,7 @@ const getNodesAndEdges = async ({
|
|
|
691
691
|
});
|
|
692
692
|
|
|
693
693
|
// Render the diagram in memory getting hte X and Y
|
|
694
|
-
|
|
694
|
+
layoutDagreGraph(flow);
|
|
695
695
|
|
|
696
696
|
return {
|
|
697
697
|
nodes: calculatedNodes(flow, nodes),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
|
-
import dagre from 'dagre';
|
|
3
2
|
import {
|
|
4
3
|
createDagreGraph,
|
|
5
4
|
generateIdForNode,
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
DEFAULT_NODE_HEIGHT,
|
|
15
14
|
partitionMessagesByGroup,
|
|
16
15
|
getOperationFields,
|
|
16
|
+
layoutDagreGraph,
|
|
17
17
|
} from '@utils/node-graphs/utils/utils';
|
|
18
18
|
|
|
19
19
|
const sanitizeGroupId = (name: string) => name.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
|
|
@@ -636,7 +636,7 @@ export const getNodesAndEdges = async ({
|
|
|
636
636
|
|
|
637
637
|
if (layout) {
|
|
638
638
|
// Render the diagram in memory getting the X and Y
|
|
639
|
-
|
|
639
|
+
layoutDagreGraph(flow);
|
|
640
640
|
}
|
|
641
641
|
|
|
642
642
|
// Find any duplicated edges, and merge them into one edge
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getCollection } from 'astro:content';
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
|
-
import dagre from 'dagre';
|
|
4
3
|
import {
|
|
5
4
|
createDagreGraph,
|
|
6
5
|
calculatedNodes,
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
generateIdForNode,
|
|
9
8
|
buildContextMenuForSystem,
|
|
10
9
|
DEFAULT_NODE_HEIGHT,
|
|
10
|
+
layoutDagreGraph,
|
|
11
11
|
} from '@utils/node-graphs/utils/utils';
|
|
12
12
|
import { MarkerType } from '@xyflow/react';
|
|
13
13
|
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
@@ -302,7 +302,7 @@ const buildContextGraphFromSeeds = ({
|
|
|
302
302
|
edges.forEach((edge) => flow.setEdge(edge.source, edge.target));
|
|
303
303
|
|
|
304
304
|
if (layout) {
|
|
305
|
-
|
|
305
|
+
layoutDagreGraph(flow);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
return {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { getCollection } from 'astro:content';
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
createDagreGraph,
|
|
4
|
+
calculatedNodes,
|
|
5
|
+
DEFAULT_NODE_WIDTH,
|
|
6
|
+
DEFAULT_NODE_HEIGHT,
|
|
7
|
+
layoutDagreGraph,
|
|
8
|
+
} from '@utils/node-graphs/utils/utils';
|
|
4
9
|
import { getNodesAndEdges as getServicesNodeAndEdges } from './services-node-graph';
|
|
5
10
|
import { getNodesAndEdges as getContainerNodeAndEdges } from './container-node-graph';
|
|
6
11
|
import merge from 'lodash.merge';
|
|
@@ -121,7 +126,7 @@ export const getNodesAndEdges = async ({
|
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
if (layout) {
|
|
124
|
-
|
|
129
|
+
layoutDagreGraph(flow);
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
let laidOutNodes = calculatedNodes(flow, Array.from(nodes.values()));
|
|
@@ -109,6 +109,34 @@ export const createDagreGraph = ({ ranksep = 180, nodesep = 50, ...rest }: any)
|
|
|
109
109
|
return graph;
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* network-simplex produces the nicest layered layout but is O(V·E) and hangs on
|
|
114
|
+
* large architecture maps. tight-tree keeps the same LR layering with far less
|
|
115
|
+
* work. Callers can still force a ranker via createDagreGraph({ ranker }).
|
|
116
|
+
*/
|
|
117
|
+
export const LARGE_GRAPH_NODE_THRESHOLD = 80;
|
|
118
|
+
export const LARGE_GRAPH_EDGE_THRESHOLD = 200;
|
|
119
|
+
export const LARGE_GRAPH_RANKER = 'tight-tree';
|
|
120
|
+
|
|
121
|
+
export const selectDagreRanker = (nodeCount: number, edgeCount: number, explicitRanker?: string) => {
|
|
122
|
+
if (explicitRanker) return explicitRanker;
|
|
123
|
+
if (nodeCount >= LARGE_GRAPH_NODE_THRESHOLD || edgeCount >= LARGE_GRAPH_EDGE_THRESHOLD) {
|
|
124
|
+
return LARGE_GRAPH_RANKER;
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const layoutDagreGraph = (flow: dagre.graphlib.Graph) => {
|
|
130
|
+
const label = (flow.graph() || {}) as { ranker?: string };
|
|
131
|
+
const nodeCount = flow.nodeCount();
|
|
132
|
+
const edgeCount = flow.edgeCount();
|
|
133
|
+
const ranker = selectDagreRanker(nodeCount, edgeCount, label.ranker);
|
|
134
|
+
if (ranker && label.ranker !== ranker) {
|
|
135
|
+
flow.setGraph({ ...label, ranker });
|
|
136
|
+
}
|
|
137
|
+
dagre.layout(flow);
|
|
138
|
+
};
|
|
139
|
+
|
|
112
140
|
export const createEdge = (edgeOptions: Edge) => {
|
|
113
141
|
return {
|
|
114
142
|
label: 'subscribed by',
|
|
@@ -430,8 +458,8 @@ export const getNodesAndEdgesFromDagre = ({
|
|
|
430
458
|
flow.setEdge(edge.source, edge.target);
|
|
431
459
|
});
|
|
432
460
|
|
|
433
|
-
// Render the diagram in memory getting
|
|
434
|
-
|
|
461
|
+
// Render the diagram in memory getting the X and Y
|
|
462
|
+
layoutDagreGraph(flow);
|
|
435
463
|
|
|
436
464
|
return {
|
|
437
465
|
nodes: calculatedNodes(flow, nodes),
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type { SchemaItem, SchemaDetails, Producer, Consumer } from '@components/SchemaExplorer/types';
|
|
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 buildRegistry() {
|
|
32
|
+
// Fetch all messages
|
|
33
|
+
const events = await getEvents({ getAllVersions: true, hydrateServices: false });
|
|
34
|
+
const commands = await getCommands({ getAllVersions: true, hydrateServices: false });
|
|
35
|
+
const queries = await getQueries({ getAllVersions: true, hydrateServices: false });
|
|
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
|
+
// The collection types describe raw content references. With
|
|
56
|
+
// hydrateServices: false, the loaders return compact { id, version } pairs.
|
|
57
|
+
const producers = (message?.data.producers || []) as unknown as Producer[];
|
|
58
|
+
const consumers = (message?.data.consumers || []) as unknown as Consumer[];
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
collection: schema.data.message.collectionName,
|
|
62
|
+
data: {
|
|
63
|
+
id: schema.data.message.id,
|
|
64
|
+
name: schema.data.message.name || message?.data.name || schema.data.name || schema.data.message.id,
|
|
65
|
+
version: schema.data.message.version,
|
|
66
|
+
summary: schema.data.message.summary || message?.data.summary,
|
|
67
|
+
schemaPath,
|
|
68
|
+
owners: await enrichOwners(schema.data.message.owners || []),
|
|
69
|
+
// The list shows only the first producer's label; keep this bounded
|
|
70
|
+
// regardless of how many resources reference the message.
|
|
71
|
+
producerName: producers[0]?.id,
|
|
72
|
+
},
|
|
73
|
+
loadDetails: () => {
|
|
74
|
+
let examples: SchemaDetails['examples'] = [];
|
|
75
|
+
if (message) {
|
|
76
|
+
try {
|
|
77
|
+
examples = getExamplesForResource(message);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(`Error reading examples for ${message.data.id}:`, error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
schemaContent: schema.data.content || '',
|
|
84
|
+
examples,
|
|
85
|
+
data: { producers, consumers },
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
schemaExtension,
|
|
89
|
+
};
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Filter services with specifications and read spec content - only keep essential data
|
|
94
|
+
const servicesWithSpecs = await Promise.all(
|
|
95
|
+
services.map(async (service) => {
|
|
96
|
+
try {
|
|
97
|
+
const specifications = getSpecificationsForService(service);
|
|
98
|
+
|
|
99
|
+
if (specifications.length === 0) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return await Promise.all(
|
|
104
|
+
specifications.map(async (spec) => {
|
|
105
|
+
if (!resourceFileExists(service, spec.path)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const schemaExtension = spec.type;
|
|
110
|
+
const enrichedOwners = await enrichOwners(service.data.owners || []);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
collection: 'services',
|
|
114
|
+
data: {
|
|
115
|
+
id: `${service.data.id}`,
|
|
116
|
+
name: `${service.data.name} - ${spec.name}`,
|
|
117
|
+
version: service.data.version,
|
|
118
|
+
summary: service.data.summary,
|
|
119
|
+
schemaPath: spec.path,
|
|
120
|
+
owners: enrichedOwners,
|
|
121
|
+
},
|
|
122
|
+
loadDetails: () => ({ schemaContent: readResourceFile(service, spec.path) ?? '', examples: [] }),
|
|
123
|
+
schemaExtension,
|
|
124
|
+
specType: spec.type,
|
|
125
|
+
specName: spec.name,
|
|
126
|
+
specFilenameWithoutExtension: spec.filenameWithoutExtension,
|
|
127
|
+
};
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error(`Error reading specifications for service ${service.data.id}:`, error);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Flatten and filter out null values
|
|
138
|
+
const flatServicesWithSpecs = servicesWithSpecs.flat().filter((service) => service !== null);
|
|
139
|
+
|
|
140
|
+
// Fetch all domains
|
|
141
|
+
const domains = await getDomains({ getAllVersions: true });
|
|
142
|
+
|
|
143
|
+
// Filter domains with specifications and read spec content - only keep essential data
|
|
144
|
+
const domainsWithSpecs = await Promise.all(
|
|
145
|
+
domains.map(async (domain) => {
|
|
146
|
+
try {
|
|
147
|
+
const specifications = getSpecificationsForDomain(domain);
|
|
148
|
+
|
|
149
|
+
if (specifications.length === 0) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return await Promise.all(
|
|
154
|
+
specifications.map(async (spec) => {
|
|
155
|
+
if (!resourceFileExists(domain, spec.path)) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const schemaExtension = spec.type;
|
|
160
|
+
const enrichedOwners = await enrichOwners(domain.data.owners || []);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
collection: 'domains',
|
|
164
|
+
data: {
|
|
165
|
+
id: `${domain.data.id}`,
|
|
166
|
+
name: `${domain.data.name} - ${spec.name}`,
|
|
167
|
+
version: domain.data.version,
|
|
168
|
+
summary: domain.data.summary,
|
|
169
|
+
schemaPath: spec.path,
|
|
170
|
+
owners: enrichedOwners,
|
|
171
|
+
},
|
|
172
|
+
loadDetails: () => ({ schemaContent: readResourceFile(domain, spec.path) ?? '', examples: [] }),
|
|
173
|
+
schemaExtension,
|
|
174
|
+
specType: spec.type,
|
|
175
|
+
specName: spec.name,
|
|
176
|
+
specFilenameWithoutExtension: spec.filenameWithoutExtension,
|
|
177
|
+
};
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error(`Error reading specifications for domain ${domain.data.id}:`, error);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Flatten and filter out null values for domains
|
|
188
|
+
const flatDomainsWithSpecs = domainsWithSpecs.flat().filter((domain) => domain !== null);
|
|
189
|
+
|
|
190
|
+
// Fetch all data products and extract contracts from outputs
|
|
191
|
+
const dataProducts = await getDataProducts({ getAllVersions: true });
|
|
192
|
+
|
|
193
|
+
// Filter data products with contracts in outputs and read contract content
|
|
194
|
+
const dataProductsWithContracts = await Promise.all(
|
|
195
|
+
dataProducts.map(async (dataProduct) => {
|
|
196
|
+
try {
|
|
197
|
+
const outputs = dataProduct.data.outputs || [];
|
|
198
|
+
const outputsWithContracts = outputs.filter((output) => output.contract);
|
|
199
|
+
|
|
200
|
+
if (outputsWithContracts.length === 0) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return await Promise.all(
|
|
205
|
+
outputsWithContracts.map(async (output) => {
|
|
206
|
+
const contract = output.contract!;
|
|
207
|
+
if (!resourceFileExists(dataProduct, contract.path)) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const schemaExtension = path.extname(contract.path).slice(1) || 'json';
|
|
212
|
+
const enrichedOwners = await enrichOwners(dataProduct.data.owners || []);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
collection: 'data-products',
|
|
216
|
+
data: {
|
|
217
|
+
id: `${dataProduct.data.id}__${contract.path}`,
|
|
218
|
+
name: contract.name,
|
|
219
|
+
version: dataProduct.data.version,
|
|
220
|
+
summary: `Data contract for ${dataProduct.data.name}`,
|
|
221
|
+
schemaPath: contract.path,
|
|
222
|
+
owners: enrichedOwners,
|
|
223
|
+
},
|
|
224
|
+
loadDetails: () => ({ schemaContent: readResourceFile(dataProduct, contract.path) ?? '', examples: [] }),
|
|
225
|
+
schemaExtension,
|
|
226
|
+
contractType: contract.type,
|
|
227
|
+
dataProductId: dataProduct.data.id,
|
|
228
|
+
dataProductVersion: dataProduct.data.version,
|
|
229
|
+
};
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error(`Error reading contracts for data product ${dataProduct.data.id}:`, error);
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// Flatten and filter out null values for data product contracts
|
|
240
|
+
const flatDataProductContracts = dataProductsWithContracts.flat().filter((contract) => contract !== null);
|
|
241
|
+
|
|
242
|
+
return new Map(
|
|
243
|
+
[...messagesWithSchemas, ...flatServicesWithSpecs, ...flatDomainsWithSpecs, ...flatDataProductContracts].map((entry) => {
|
|
244
|
+
const { loadDetails, ...item } = entry;
|
|
245
|
+
const key = createHash('sha256')
|
|
246
|
+
.update(
|
|
247
|
+
JSON.stringify([
|
|
248
|
+
item.collection,
|
|
249
|
+
item.data.id,
|
|
250
|
+
item.data.version,
|
|
251
|
+
item.data.schemaPath,
|
|
252
|
+
'specType' in item ? item.specType : '',
|
|
253
|
+
])
|
|
254
|
+
)
|
|
255
|
+
.digest('hex');
|
|
256
|
+
return [
|
|
257
|
+
key,
|
|
258
|
+
{ item: { ...item, contentUrl: buildUrl(`/schemas/explorer/content/${key}.json`, true) } as SchemaItem, loadDetails },
|
|
259
|
+
];
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Share metadata during production rendering; dev rebuilds it to reflect content edits.
|
|
265
|
+
let registry: ReturnType<typeof buildRegistry> | undefined;
|
|
266
|
+
export const getSchemaRegistry = () => {
|
|
267
|
+
if (import.meta.env.DEV || process.env.DISABLE_EVENTCATALOG_CACHE === 'true') return buildRegistry();
|
|
268
|
+
return (registry ??= buildRegistry().catch((error) => {
|
|
269
|
+
registry = undefined;
|
|
270
|
+
throw error;
|
|
271
|
+
}));
|
|
272
|
+
};
|
|
273
|
+
export const getSchemaMetadata = async (): Promise<SchemaItem[]> =>
|
|
274
|
+
[...(await getSchemaRegistry()).values()].map(({ item }) => item);
|
|
275
|
+
export const getSchemaDetails = async (key: string): Promise<SchemaDetails | undefined> =>
|
|
276
|
+
(await getSchemaRegistry()).get(key)?.loadDetails();
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "4.10.
|
|
10
|
+
"version": "4.10.10",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -118,9 +118,9 @@
|
|
|
118
118
|
"update-notifier": "^7.3.1",
|
|
119
119
|
"uuid": "^11.1.1",
|
|
120
120
|
"zod": "^4.3.6",
|
|
121
|
-
"@eventcatalog/linter": "1.1.18",
|
|
122
121
|
"@eventcatalog/sdk": "2.29.0",
|
|
123
|
-
"@eventcatalog/visualiser": "^4.1.
|
|
122
|
+
"@eventcatalog/visualiser": "^4.1.4",
|
|
123
|
+
"@eventcatalog/linter": "1.1.18"
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
126
126
|
"@astrojs/check": "^0.9.10",
|