@eventcatalog/core 5.2.0 → 5.4.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/count-resources.cjs +10 -0
- package/dist/analytics/count-resources.js +1 -1
- package/dist/analytics/log-build.cjs +11 -1
- package/dist/analytics/log-build.js +4 -4
- package/dist/{chunk-DJCOJ77J.js → chunk-7RJN4FNG.js} +1 -1
- package/dist/{chunk-RNYFUWJI.js → chunk-EPDVXHC7.js} +4 -4
- package/dist/{chunk-XI5HQWM2.js → chunk-O2K4VPDO.js} +1 -1
- package/dist/{chunk-N2LLD5TR.js → chunk-OT2U5OES.js} +1 -1
- package/dist/{chunk-QJ7KKN2D.js → chunk-OX6TSVHY.js} +1 -1
- package/dist/{chunk-66OL4B7U.js → chunk-PAS36QHK.js} +1 -1
- package/dist/{chunk-NBYKF4Z4.js → chunk-TERJXCLK.js} +1 -1
- package/dist/{chunk-ZAZHAVKB.js → chunk-YRZK7HKX.js} +10 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +1 -1
- package/dist/docs/development/developer-tools/api-catalog.md +0 -2
- package/dist/docs/development/export/01-introduction.md +1 -1
- package/dist/docs/development/export/04-reference.md +1 -1
- package/dist/docs/development/guides/fitness-functions/01-what-are-fitness-functions.md +116 -0
- package/dist/docs/development/guides/fitness-functions/02-create-fitness-function.md +222 -0
- package/dist/docs/development/guides/fitness-functions/03-global-and-resource-functions.md +198 -0
- package/dist/docs/development/guides/fitness-functions/04-run-in-ci.md +223 -0
- package/dist/docs/development/guides/fitness-functions/05-api-reference.md +271 -0
- package/dist/docs/development/guides/fitness-functions/06-examples.md +354 -0
- package/dist/docs/development/guides/fitness-functions/_category_.json +11 -0
- package/dist/docs/development/guides/owners/05-teams-reference.md +0 -12
- package/dist/docs/development/guides/owners/06-users-reference.md +0 -12
- package/dist/docs/development/upgrading/v5.md +1 -1
- package/dist/eventcatalog.cjs +26 -2
- package/dist/eventcatalog.js +28 -14
- package/dist/federation/federate.js +3 -3
- package/dist/federation/source-provider.js +2 -2
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/runtime-dependencies/@eventcatalog__fitness.mjs +3 -0
- package/dist/runtime-dependencies/@modelcontextprotocol__client.mjs +3 -0
- package/dist/runtime-dependencies/@modelcontextprotocol__server.mjs +3 -0
- package/dist/runtime-dependencies/manifest.json +3 -4
- package/dist/search-indexer.cjs +1 -1
- package/dist/search-indexer.js +1 -1
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/content.config.ts +0 -3
- package/eventcatalog/src/features/collections/resource-docs.ts +0 -1
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +73 -74
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +71 -73
- package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +1 -1
- package/eventcatalog/src/features/mcp/custom-tool-input.ts +38 -0
- package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -4
- package/eventcatalog/src/features/mcp/mcp-scope.ts +2 -3
- package/eventcatalog/src/features/mcp/mcp-server.ts +24 -17
- package/eventcatalog/src/features/studio/server/canvas-mcp.ts +2 -4
- package/eventcatalog/src/features/tools/catalog-tools.ts +6 -8
- package/eventcatalog/src/pages/api-catalog/specifications/[collection]/[id]/[version]/[specification].ts +2 -4
- package/eventcatalog/src/utils/collections/adrs.ts +0 -1
- package/eventcatalog/src/utils/collections/agents.ts +0 -1
- package/eventcatalog/src/utils/collections/channels.ts +0 -1
- package/eventcatalog/src/utils/collections/commands.ts +0 -1
- package/eventcatalog/src/utils/collections/containers.ts +0 -1
- package/eventcatalog/src/utils/collections/domains.ts +0 -2
- package/eventcatalog/src/utils/collections/entities.ts +0 -1
- package/eventcatalog/src/utils/collections/events.ts +0 -1
- package/eventcatalog/src/utils/collections/flows.ts +0 -2
- package/eventcatalog/src/utils/collections/owners.ts +1 -4
- package/eventcatalog/src/utils/collections/queries.ts +0 -1
- package/eventcatalog/src/utils/collections/resource-docs.ts +1 -5
- package/eventcatalog/src/utils/collections/resource-owners.ts +1 -2
- package/eventcatalog/src/utils/collections/services.ts +0 -1
- package/eventcatalog/src/utils/collections/systems.ts +0 -1
- package/eventcatalog/src/utils/collections/teams.ts +3 -6
- package/eventcatalog/src/utils/collections/users.ts +4 -8
- package/eventcatalog/src/utils/homepage-dashboard.ts +2 -3
- package/eventcatalog/src/utils/node-graphs/flow-lanes.ts +2 -6
- package/package.json +9 -6
- package/dist/runtime-dependencies/@modelcontextprotocol__sdk.mjs +0 -3
- package/dist/runtime-dependencies/@modelcontextprotocol__sdk__server__mcp.js.mjs +0 -3
- package/dist/runtime-dependencies/@modelcontextprotocol__sdk__server__webStandardStreamableHttp.js.mjs +0 -3
- package/dist/runtime-dependencies/@modelcontextprotocol__sdk__types.js.mjs +0 -3
- package/eventcatalog/src/features/studio/server/tool-icons.ts +0 -30
- package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
|
@@ -7,7 +7,7 @@ import { config as configureZod } from 'zod';
|
|
|
7
7
|
import { createRoot } from 'react-dom/client';
|
|
8
8
|
import type { App, McpUiHostContext } from '@modelcontextprotocol/ext-apps';
|
|
9
9
|
import { useApp } from '@modelcontextprotocol/ext-apps/react';
|
|
10
|
-
import type { CallToolResult } from '@modelcontextprotocol/
|
|
10
|
+
import type { CallToolResult } from '@modelcontextprotocol/client';
|
|
11
11
|
|
|
12
12
|
// MCP hosts block eval in views, so stop zod (used by the MCP Apps SDK) from probing for it
|
|
13
13
|
configureZod({ jitless: true });
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { fromJsonSchema, type JsonSchemaType, type StandardSchemaWithJSON } from '@modelcontextprotocol/server';
|
|
2
|
+
import { asSchema, type FlexibleSchema } from 'ai';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
/** Zod 4.2+ (and other Standard JSON Schema) schemas, which the MCP SDK lists and validates itself */
|
|
6
|
+
const hasJsonSchema = (schema: unknown): schema is StandardSchemaWithJSON =>
|
|
7
|
+
typeof schema === 'object' &&
|
|
8
|
+
schema !== null &&
|
|
9
|
+
typeof (schema as { '~standard'?: { jsonSchema?: { input?: unknown } } })['~standard']?.jsonSchema?.input === 'function';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A custom tool's input schema (eventcatalog.chat.js, in the AI SDK's format) for the MCP SDK. Zod 4.2+ schemas are
|
|
13
|
+
* used as they are. The others the AI SDK takes (zod 3, jsonSchema()) are listed from their JSON Schema and validated
|
|
14
|
+
* by the schema itself, so coercion, defaults and transforms work as before: checking the JSON Schema first would turn
|
|
15
|
+
* away input the schema accepts, such as "5" for a coerced number. MCP SDK 2 can't list zod 3 schemas, and one failed
|
|
16
|
+
* tools/list for every tool.
|
|
17
|
+
*/
|
|
18
|
+
export const toCustomToolInput = async (schema: unknown): Promise<StandardSchemaWithJSON> => {
|
|
19
|
+
if (!schema) return z.object({});
|
|
20
|
+
if (hasJsonSchema(schema)) return schema;
|
|
21
|
+
|
|
22
|
+
const { jsonSchema, validate } = asSchema(schema as FlexibleSchema<unknown>);
|
|
23
|
+
const json = (await jsonSchema) as JsonSchemaType;
|
|
24
|
+
// jsonSchema() can leave out a validator, and then the arguments are checked against the JSON Schema
|
|
25
|
+
if (!validate) return fromJsonSchema(json);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
'~standard': {
|
|
29
|
+
version: 1,
|
|
30
|
+
vendor: 'eventcatalog',
|
|
31
|
+
jsonSchema: { input: () => json, output: () => json },
|
|
32
|
+
validate: async (value) => {
|
|
33
|
+
const result = await validate(value);
|
|
34
|
+
return result.success ? { value: result.value } : { issues: [{ message: result.error.message }] };
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
2
|
-
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
1
|
+
import type { Icon, McpServer, ToolAnnotations } from '@modelcontextprotocol/server';
|
|
3
2
|
import type { McpUiDisplayMode } from '@modelcontextprotocol/ext-apps';
|
|
4
3
|
import { registerAppResource, RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from '@modelcontextprotocol/ext-apps/server';
|
|
5
4
|
import { z } from 'zod';
|
|
@@ -40,6 +39,8 @@ type ToolDefinition<Shape extends z.ZodRawShape> = {
|
|
|
40
39
|
input: Shape;
|
|
41
40
|
/** More `_meta` for the tool, e.g. host extensions such as ChatGPT's `openai/ui` entrypoints */
|
|
42
41
|
meta?: Record<string, unknown>;
|
|
42
|
+
/** Icons listed with the tool (MCP 2025-11-25 tool icons, which ChatGPT shows on entrypoints) */
|
|
43
|
+
icons?: Icon[];
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
export type McpAppDefinition<Shape extends z.ZodRawShape, ViewShape extends z.ZodRawShape, Payload> = {
|
|
@@ -106,13 +107,14 @@ export function registerMcpApp<Shape extends z.ZodRawShape, ViewShape extends z.
|
|
|
106
107
|
};
|
|
107
108
|
|
|
108
109
|
const toolInput = z.object(app.tool.input);
|
|
109
|
-
server.registerTool
|
|
110
|
+
server.registerTool(
|
|
110
111
|
app.tool.name,
|
|
111
112
|
{
|
|
112
113
|
title: app.tool.title,
|
|
113
114
|
description: app.tool.description,
|
|
114
115
|
inputSchema: toolInput,
|
|
115
116
|
annotations: app.annotations,
|
|
117
|
+
...(app.tool.icons && { icons: app.tool.icons }),
|
|
116
118
|
_meta: loadView ? { ...appViewMeta(app.resource.uri), ...app.tool.meta } : undefined,
|
|
117
119
|
},
|
|
118
120
|
showInView
|
|
@@ -130,7 +132,7 @@ export function registerMcpApp<Shape extends z.ZodRawShape, ViewShape extends z.
|
|
|
130
132
|
};
|
|
131
133
|
|
|
132
134
|
const viewToolInput = z.object(app.viewTool.input);
|
|
133
|
-
server.registerTool
|
|
135
|
+
server.registerTool(
|
|
134
136
|
app.viewTool.name,
|
|
135
137
|
{
|
|
136
138
|
title: app.viewTool.title,
|
|
@@ -35,7 +35,6 @@ type CatalogEntry = {
|
|
|
35
35
|
version: string;
|
|
36
36
|
name?: string;
|
|
37
37
|
summary?: string;
|
|
38
|
-
hidden?: boolean;
|
|
39
38
|
[key: string]: any;
|
|
40
39
|
};
|
|
41
40
|
[key: string]: any;
|
|
@@ -78,7 +77,7 @@ export class McpScope {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
add(entry: CatalogEntry | undefined) {
|
|
81
|
-
if (!entry
|
|
80
|
+
if (!entry) return false;
|
|
82
81
|
|
|
83
82
|
const collectionEntries = this.entries.get(entry.collection) ?? new Map<string, CatalogEntry>();
|
|
84
83
|
const key = entryKey(entry.data.id, entry.data.version);
|
|
@@ -147,7 +146,7 @@ export async function resolveMcpScope(
|
|
|
147
146
|
|
|
148
147
|
const rootCollection = graphTypeToCollection[graph.root.type];
|
|
149
148
|
const root = resolve(rootCollection, graph.root.id, graph.root.version);
|
|
150
|
-
if (!root
|
|
149
|
+
if (!root) throw scopeNotFound(ref);
|
|
151
150
|
|
|
152
151
|
const scope = new McpScope(ref, root);
|
|
153
152
|
for (const resource of graph.resources) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { APIRoute } from 'astro';
|
|
2
2
|
import { Hono, type Context } from 'hono';
|
|
3
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
4
|
-
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
3
|
+
import { McpServer, WebStandardStreamableHTTPServerTransport, type StandardSchemaWithJSON } from '@modelcontextprotocol/server';
|
|
5
4
|
import { z } from 'zod';
|
|
6
5
|
import { join } from 'node:path';
|
|
7
6
|
import * as catalogTools from '@features/tools/catalog-tools';
|
|
@@ -21,6 +20,7 @@ import type { SchemaViewerPayload } from './apps/schema-viewer/shared';
|
|
|
21
20
|
import { RESOURCE_VIEWS, VIEWER_META_KEY, VIEWER_RESOURCE_URI, VIEWER_VIEW_TOOL, type ViewerPayload } from './apps/viewer/shared';
|
|
22
21
|
import { toSchemaViewerSchemas } from './schema-viewer';
|
|
23
22
|
import { getMcpAppViewLoader, mcpAppNote, registerMcpApp } from './mcp-apps';
|
|
23
|
+
import { toCustomToolInput } from './custom-tool-input';
|
|
24
24
|
import { buildUrl } from '@utils/url-builder';
|
|
25
25
|
import { isCanvasEnabled } from '@utils/feature';
|
|
26
26
|
import { CANVAS_TOOL_NAMES, registerCanvasTools } from '@features/studio/server/canvas-mcp';
|
|
@@ -74,19 +74,34 @@ function createToolHandler<T>(fn: (params: T) => Promise<any>, errorMessage: str
|
|
|
74
74
|
|
|
75
75
|
// Load extended tools from user configuration
|
|
76
76
|
let extendedTools: Record<string, any> = {};
|
|
77
|
-
let extendedToolNames: string[] = [];
|
|
78
77
|
|
|
79
78
|
try {
|
|
80
79
|
const providerConfiguration = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.chat.js'));
|
|
81
80
|
|
|
82
81
|
if (providerConfiguration.tools) {
|
|
83
82
|
extendedTools = providerConfiguration.tools;
|
|
84
|
-
extendedToolNames = Object.keys(extendedTools);
|
|
85
83
|
}
|
|
86
84
|
} catch (error) {
|
|
87
85
|
// No chat configuration or tools defined - this is fine
|
|
88
86
|
}
|
|
89
87
|
|
|
88
|
+
// The extended tools the MCP server offers, each with its input for the MCP SDK. Worked out once (converting a
|
|
89
|
+
// schema can be async), so the tools registered and the tools listed by GET /docs/mcp are the same ones.
|
|
90
|
+
const extendedToolInputs = new Map<string, StandardSchemaWithJSON>();
|
|
91
|
+
for (const [toolName, toolConfig] of Object.entries(extendedTools)) {
|
|
92
|
+
if (!toolConfig || typeof toolConfig !== 'object') continue;
|
|
93
|
+
if (!toolConfig.description || !toolConfig.execute) {
|
|
94
|
+
console.warn(`[MCP] Skipping invalid extended tool: ${toolName}`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
extendedToolInputs.set(toolName, await toCustomToolInput(toolConfig.inputSchema || toolConfig.parameters));
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.warn(`[MCP] Skipping extended tool ${toolName}: its input schema could not be read (${error})`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const extendedToolNames = [...extendedToolInputs.keys()];
|
|
104
|
+
|
|
90
105
|
const MCP_SERVER_VERSION = '1.3.0';
|
|
91
106
|
|
|
92
107
|
// Every built-in tool only reads the catalog, so clients can safely auto-approve them
|
|
@@ -633,24 +648,16 @@ function createMcpServer(
|
|
|
633
648
|
}
|
|
634
649
|
|
|
635
650
|
// Register extended tools from user configuration
|
|
636
|
-
for (const [toolName,
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
// Extract tool properties (Vercel AI SDK format)
|
|
640
|
-
// The AI SDK tool() helper uses "inputSchema" for Zod schemas
|
|
641
|
-
const { title, description, parameters, inputSchema, annotations, execute } = toolConfig;
|
|
642
|
-
|
|
643
|
-
if (!description || !execute) {
|
|
644
|
-
console.warn(`[MCP] Skipping invalid extended tool: ${toolName}`);
|
|
645
|
-
continue;
|
|
646
|
-
}
|
|
651
|
+
for (const [toolName, input] of scope ? [] : extendedToolInputs) {
|
|
652
|
+
// Tool properties in the Vercel AI SDK format
|
|
653
|
+
const { title, description, annotations, execute } = extendedTools[toolName];
|
|
647
654
|
|
|
648
655
|
server.registerTool(
|
|
649
656
|
toolName,
|
|
650
657
|
{
|
|
651
658
|
...(title && { title }),
|
|
652
|
-
description
|
|
653
|
-
inputSchema:
|
|
659
|
+
description,
|
|
660
|
+
inputSchema: input,
|
|
654
661
|
...(annotations && { annotations }),
|
|
655
662
|
},
|
|
656
663
|
async (params: any) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import type * as Y from 'yjs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
} from '../agent-choreography';
|
|
38
38
|
import { STUDIO_SOCKET_PATH, findStudioRuntime } from './runtime';
|
|
39
39
|
import { getAgent } from '../agents';
|
|
40
|
-
import { addToolIcons } from './tool-icons';
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
42
|
* MCP tools for the collaborative canvas: agents create canvases, join the ones people are on, and
|
|
@@ -139,6 +138,7 @@ export function registerCanvasTools(server: McpServer, { catalogUrl, userAgent }
|
|
|
139
138
|
// ChatGPT entrypoints (OpenAI MCP extensions): people can open canvases from the sidebar or as a tab in a
|
|
140
139
|
// thread, which calls the tool with {} (the list of canvases)
|
|
141
140
|
meta: { 'openai/ui': { entrypoints: [{ type: 'global' }, { type: 'thread' }] } },
|
|
141
|
+
icons: [CANVAS_ICON],
|
|
142
142
|
input: {
|
|
143
143
|
canvasId: canvasId.optional(),
|
|
144
144
|
title: z.string().optional().describe('For a new canvas: what it is for'),
|
|
@@ -490,6 +490,4 @@ export function registerCanvasTools(server: McpServer, { catalogUrl, userAgent }
|
|
|
490
490
|
return found ? text({ threadId, resolved }) : text({ error: `No comment thread "${threadId}"` }, true);
|
|
491
491
|
}
|
|
492
492
|
);
|
|
493
|
-
|
|
494
|
-
addToolIcons(server, { openCanvas: [CANVAS_ICON] });
|
|
495
493
|
}
|
|
@@ -220,14 +220,12 @@ const describeVersion = (version?: string) => version ?? 'latest';
|
|
|
220
220
|
*/
|
|
221
221
|
export async function getResources(params: { collection: string; cursor?: string; search?: string }) {
|
|
222
222
|
const resources = await getCollection(params.collection as any);
|
|
223
|
-
let allResults = resources
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
summary: resource.data.summary,
|
|
230
|
-
}));
|
|
223
|
+
let allResults = resources.map((resource: any) => ({
|
|
224
|
+
id: resource.data.id,
|
|
225
|
+
version: resource.data.version,
|
|
226
|
+
name: resource.data.name,
|
|
227
|
+
summary: resource.data.summary,
|
|
228
|
+
}));
|
|
231
229
|
|
|
232
230
|
// Apply search filter if provided
|
|
233
231
|
if (params.search) {
|
|
@@ -39,9 +39,7 @@ const getSpecificationsForResource = (
|
|
|
39
39
|
|
|
40
40
|
export async function getStaticPaths() {
|
|
41
41
|
const [services, domains] = await Promise.all([getCollection('services'), getCollection('domains')]);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return resources.flatMap((resource) =>
|
|
42
|
+
return [...services, ...domains].flatMap((resource) =>
|
|
45
43
|
getSpecificationsForResource(resource).map((specification) => ({
|
|
46
44
|
params: {
|
|
47
45
|
collection: resource.collection,
|
|
@@ -74,7 +72,7 @@ export const GET: APIRoute = async ({ params, props }) => {
|
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
const resources = await getCollection(collection);
|
|
77
|
-
const resource = resources.find((item) => item.data.id === id && item.data.version === version
|
|
75
|
+
const resource = resources.find((item) => item.data.id === id && item.data.version === version);
|
|
78
76
|
|
|
79
77
|
if (!resource) {
|
|
80
78
|
return new Response(JSON.stringify({ error: 'Resource not found' }), {
|
|
@@ -39,7 +39,6 @@ export const getAdrs = async ({ getAllVersions = true, returnBody = false }: Pro
|
|
|
39
39
|
const adrMap = createVersionedMap(allAdrs);
|
|
40
40
|
|
|
41
41
|
const targetAdrs = allAdrs.filter((adr) => {
|
|
42
|
-
if (adr.data.hidden === true) return false;
|
|
43
42
|
if (!getAllVersions && adr.filePath?.includes('versioned')) return false;
|
|
44
43
|
return true;
|
|
45
44
|
});
|
|
@@ -38,7 +38,6 @@ export const getAgents = async ({ getAllVersions = true, returnBody = false }: P
|
|
|
38
38
|
const flowMap = createVersionedMap(allFlows);
|
|
39
39
|
|
|
40
40
|
const targetAgents = allAgents.filter((agent) => {
|
|
41
|
-
if (agent.data.hidden === true) return false;
|
|
42
41
|
if (!getAllVersions && agent.filePath?.includes('versioned')) return false;
|
|
43
42
|
return true;
|
|
44
43
|
});
|
|
@@ -66,7 +66,6 @@ export const getChannels = async ({ getAllVersions = true }: Props = {}): Promis
|
|
|
66
66
|
|
|
67
67
|
// 4. Filter channels
|
|
68
68
|
const targetChannels = allChannels.filter((channel) => {
|
|
69
|
-
if (channel.data.hidden === true) return false;
|
|
70
69
|
if (!getAllVersions && channel.filePath?.includes('versioned')) return false;
|
|
71
70
|
return true;
|
|
72
71
|
});
|
|
@@ -51,7 +51,6 @@ export const getCommands = async ({ getAllVersions = true, hydrateServices = tru
|
|
|
51
51
|
|
|
52
52
|
// 3. Filter commands
|
|
53
53
|
const targetCommands = allCommands.filter((command) => {
|
|
54
|
-
if (command.data.hidden === true) return false;
|
|
55
54
|
if (!getAllVersions && command.filePath?.includes('versioned')) return false;
|
|
56
55
|
return true;
|
|
57
56
|
});
|
|
@@ -33,7 +33,6 @@ export const getContainers = async ({ getAllVersions = true }: Props = {}): Prom
|
|
|
33
33
|
|
|
34
34
|
// 3. Filter containers
|
|
35
35
|
const targetContainers = allContainers.filter((container) => {
|
|
36
|
-
if (container.data.hidden === true) return false;
|
|
37
36
|
if (!getAllVersions && container.filePath?.includes('versioned')) return false;
|
|
38
37
|
return true;
|
|
39
38
|
});
|
|
@@ -79,8 +79,6 @@ export const getDomains = async ({
|
|
|
79
79
|
|
|
80
80
|
// 3. Filter the domains we actually want to process/return
|
|
81
81
|
const targetDomains = allDomains.filter((domain: Domain) => {
|
|
82
|
-
// Filter out hidden
|
|
83
|
-
if (domain.data.hidden === true) return false;
|
|
84
82
|
// Handle version filtering
|
|
85
83
|
if (!getAllVersions && domain.filePath?.includes('versioned')) return false;
|
|
86
84
|
return true;
|
|
@@ -34,7 +34,6 @@ export const getEntities = async ({ getAllVersions = true }: Props = {}): Promis
|
|
|
34
34
|
|
|
35
35
|
// 3. Filter entities
|
|
36
36
|
const targetEntities = allEntities.filter((entity) => {
|
|
37
|
-
if (entity.data.hidden === true) return false;
|
|
38
37
|
if (!getAllVersions && entity.filePath?.includes('versioned')) return false;
|
|
39
38
|
return true;
|
|
40
39
|
});
|
|
@@ -51,7 +51,6 @@ export const getEvents = async ({ getAllVersions = true, hydrateServices = true
|
|
|
51
51
|
|
|
52
52
|
// 3. Filter events
|
|
53
53
|
const targetEvents = allEvents.filter((event) => {
|
|
54
|
-
if (event.data.hidden === true) return false;
|
|
55
54
|
if (!getAllVersions && event.filePath?.includes('versioned')) return false;
|
|
56
55
|
return true;
|
|
57
56
|
});
|
|
@@ -46,7 +46,6 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
|
|
|
46
46
|
|
|
47
47
|
// 3. Filter flows
|
|
48
48
|
const targetFlows = allFlows.filter((flow) => {
|
|
49
|
-
if (flow.data.hidden === true) return false;
|
|
50
49
|
if (!getAllVersions && flow.filePath?.includes('versioned')) return false;
|
|
51
50
|
return true;
|
|
52
51
|
});
|
|
@@ -230,7 +229,6 @@ export const getFlowsForMessages = async (
|
|
|
230
229
|
const flowsByMessage = new Map<string, FlowReference[]>();
|
|
231
230
|
|
|
232
231
|
for (const flow of flows) {
|
|
233
|
-
if (flow.data.hidden === true) continue;
|
|
234
232
|
const reference: FlowReference = {
|
|
235
233
|
id: flow.data.id,
|
|
236
234
|
version: flow.data.version,
|
|
@@ -11,10 +11,7 @@ const getOwners = (function () {
|
|
|
11
11
|
async function init() {
|
|
12
12
|
const ownersMap = new Map<string, CollectionEntry<'users' | 'teams'>>();
|
|
13
13
|
|
|
14
|
-
const owners = await Promise.all([
|
|
15
|
-
getCollection('users', (entry) => entry.data.hidden !== true),
|
|
16
|
-
getCollection('teams', (entry) => entry.data.hidden !== true),
|
|
17
|
-
]);
|
|
14
|
+
const owners = await Promise.all([getCollection('users'), getCollection('teams')]);
|
|
18
15
|
|
|
19
16
|
for (const owner of owners.flat()) {
|
|
20
17
|
ownersMap.set(owner.data.id, owner);
|
|
@@ -50,7 +50,6 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
|
|
|
50
50
|
|
|
51
51
|
// 3. Filter queries
|
|
52
52
|
const targetQueries = allQueries.filter((query) => {
|
|
53
|
-
if (query.data.hidden === true) return false;
|
|
54
53
|
if (!getAllVersions && query.filePath?.includes('versioned')) return false;
|
|
55
54
|
return true;
|
|
56
55
|
});
|
|
@@ -250,7 +250,7 @@ const getResourceFolderFromFilePath = (filePath: string): string | undefined =>
|
|
|
250
250
|
|
|
251
251
|
const buildLookup = (
|
|
252
252
|
resources: Array<{
|
|
253
|
-
data: { id: string; version: string
|
|
253
|
+
data: { id: string; version: string };
|
|
254
254
|
filePath?: string;
|
|
255
255
|
}>
|
|
256
256
|
): ResourceLookup => {
|
|
@@ -258,9 +258,6 @@ const buildLookup = (
|
|
|
258
258
|
const folderToId = new Map<string, string>();
|
|
259
259
|
|
|
260
260
|
for (const resource of resources) {
|
|
261
|
-
if (resource.data.hidden === true) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
261
|
const list = groupedById.get(resource.data.id) || [];
|
|
265
262
|
list.push(resource.data.version);
|
|
266
263
|
groupedById.set(resource.data.id, list);
|
|
@@ -390,7 +387,6 @@ export const getResourceDocs = async (): Promise<ResourceDocEntry[]> => {
|
|
|
390
387
|
const [docs, lookups] = await Promise.all([getCollection('resourceDocs'), getResourceLookups()]);
|
|
391
388
|
|
|
392
389
|
const docsWithResources = docs
|
|
393
|
-
.filter((doc) => doc.data.hidden !== true)
|
|
394
390
|
.map((doc) => {
|
|
395
391
|
if (!doc.filePath) {
|
|
396
392
|
return null;
|
|
@@ -53,8 +53,7 @@ const once = <T>(load: () => Promise<T>) => {
|
|
|
53
53
|
return () => (loaded ??= load());
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
const isCurrentVersion = (entry: {
|
|
57
|
-
entry.data.hidden !== true && !entry.filePath?.includes('versioned');
|
|
56
|
+
const isCurrentVersion = (entry: { filePath?: string }) => !entry.filePath?.includes('versioned');
|
|
58
57
|
|
|
59
58
|
// Owners come from their own collection's loader, so an owner of a type is that collection's entry.
|
|
60
59
|
const asEntry = <C extends CollectionKey>(owner: ResourceOwner) => owner as unknown as CollectionEntry<C>;
|
|
@@ -54,7 +54,6 @@ export const getServices = async ({ getAllVersions = true, returnBody = false }:
|
|
|
54
54
|
|
|
55
55
|
// 3. Filter services
|
|
56
56
|
const targetServices = allServices.filter((service) => {
|
|
57
|
-
if (service.data.hidden === true) return false;
|
|
58
57
|
if (!getAllVersions && service.filePath?.includes('versioned')) return false;
|
|
59
58
|
return true;
|
|
60
59
|
});
|
|
@@ -42,7 +42,6 @@ export const getSystems = async ({ getAllVersions = true, enrichServices = false
|
|
|
42
42
|
|
|
43
43
|
// Filter systems
|
|
44
44
|
const targetSystems = allSystems.filter((system) => {
|
|
45
|
-
if (system.data.hidden === true) return false;
|
|
46
45
|
if (!getAllVersions && system.filePath?.includes('versioned')) return false;
|
|
47
46
|
return true;
|
|
48
47
|
});
|
|
@@ -29,10 +29,7 @@ export const getTeams = async (): Promise<Team[]> => {
|
|
|
29
29
|
getAdrs({ getAllVersions: false }),
|
|
30
30
|
]);
|
|
31
31
|
|
|
32
|
-
// 2.
|
|
33
|
-
const targetTeams = allTeams.filter((team) => team.data.hidden !== true);
|
|
34
|
-
|
|
35
|
-
// 3. Build Owner Index: Map<OwnerID, Item[]>
|
|
32
|
+
// 2. Build Owner Index: Map<OwnerID, Item[]>
|
|
36
33
|
// This index groups all items (domains, services, etc.) by their owner IDs.
|
|
37
34
|
// This allows O(1) lookup to find all items owned by a specific team.
|
|
38
35
|
const ownershipMap = new Map<string, CollectionEntry<CollectionTypes>[]>();
|
|
@@ -59,8 +56,8 @@ export const getTeams = async (): Promise<Team[]> => {
|
|
|
59
56
|
addToIndex(allQueries);
|
|
60
57
|
addToIndex(allAdrs);
|
|
61
58
|
|
|
62
|
-
//
|
|
63
|
-
const processedTeams =
|
|
59
|
+
// 3. Enrich teams using the ownership index
|
|
60
|
+
const processedTeams = allTeams.map((team) => {
|
|
64
61
|
const teamId = team.data.id;
|
|
65
62
|
const ownedItems = ownershipMap.get(teamId) || [];
|
|
66
63
|
|
|
@@ -31,11 +31,7 @@ export const getUsers = async (): Promise<User[]> => {
|
|
|
31
31
|
getAdrs({ getAllVersions: false }),
|
|
32
32
|
]);
|
|
33
33
|
|
|
34
|
-
// 2.
|
|
35
|
-
const targetUsers = allUsers.filter((user) => user.data.hidden !== true);
|
|
36
|
-
const visibleTeams = allTeams.filter((team) => team.data.hidden !== true);
|
|
37
|
-
|
|
38
|
-
// 3. Process users (Optimization: Iterate once over relationships if possible,
|
|
34
|
+
// 2. Process users (Optimization: Iterate once over relationships if possible,
|
|
39
35
|
// but since we need to check ownership for EACH user against ALL items,
|
|
40
36
|
// we can't easily invert the map without building an "owner" index first.
|
|
41
37
|
// Given users/teams count is usually lower than events/services, iterating users and filtering items is acceptable,
|
|
@@ -67,8 +63,8 @@ export const getUsers = async (): Promise<User[]> => {
|
|
|
67
63
|
addToIndex(allAdrs);
|
|
68
64
|
|
|
69
65
|
// Team Membership Index: Map<UserID, Team[]>
|
|
70
|
-
const teamMembershipMap = new Map<string, typeof
|
|
71
|
-
for (const team of
|
|
66
|
+
const teamMembershipMap = new Map<string, typeof allTeams>();
|
|
67
|
+
for (const team of allTeams) {
|
|
72
68
|
if (team.data.members) {
|
|
73
69
|
for (const member of team.data.members) {
|
|
74
70
|
if (!teamMembershipMap.has(member.id)) {
|
|
@@ -79,7 +75,7 @@ export const getUsers = async (): Promise<User[]> => {
|
|
|
79
75
|
}
|
|
80
76
|
}
|
|
81
77
|
|
|
82
|
-
const mappedUsers =
|
|
78
|
+
const mappedUsers = allUsers.map((user) => {
|
|
83
79
|
const userId = user.data.id;
|
|
84
80
|
const associatedTeams = teamMembershipMap.get(userId) || [];
|
|
85
81
|
const associatedTeamIds = associatedTeams.map((t) => t.data.id);
|
|
@@ -17,7 +17,6 @@ export type DashboardEntry = {
|
|
|
17
17
|
name?: string;
|
|
18
18
|
version: string;
|
|
19
19
|
summary?: string;
|
|
20
|
-
hidden?: boolean;
|
|
21
20
|
owners?: Owner[];
|
|
22
21
|
schemaPath?: string;
|
|
23
22
|
};
|
|
@@ -41,8 +40,8 @@ export type DashboardData = {
|
|
|
41
40
|
|
|
42
41
|
const MESSAGE_COLLECTIONS = ['events', 'commands', 'queries'] as const;
|
|
43
42
|
|
|
44
|
-
// The latest version of a resource
|
|
45
|
-
const isCurrent = (entry: DashboardEntry) =>
|
|
43
|
+
// The latest version of a resource (older versions live in a `versioned` folder)
|
|
44
|
+
const isCurrent = (entry: DashboardEntry) => !entry.filePath?.includes('versioned');
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
47
|
* Everything the homepage dashboard shows, from the catalog's collections: counts, and what changed recently.
|
|
@@ -39,8 +39,7 @@ const pointerIds = (pointers?: { id: string }[]) => (pointers || []).map((pointe
|
|
|
39
39
|
* A resource listed by a domain directly belongs to that domain, otherwise to
|
|
40
40
|
* the domain of the system it's in. Built once per flow from the collections,
|
|
41
41
|
* rather than asking `getDomainsForService`, `getOwner` and so on for each step,
|
|
42
|
-
* so it follows their rules: latest versions only
|
|
43
|
-
* are left out.
|
|
42
|
+
* so it follows their rules: latest versions only.
|
|
44
43
|
*/
|
|
45
44
|
export const createLaneIndex = ({ domains, systems, teams, users }: LaneCollections) => {
|
|
46
45
|
const systemOf = new Map<string, Lane>();
|
|
@@ -75,14 +74,12 @@ export const createLaneIndex = ({ domains, systems, teams, users }: LaneCollecti
|
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
const owners = new Map<string, { lane: Lane; isTeam: boolean }>();
|
|
78
|
-
const hiddenOwners = new Set<string>();
|
|
79
77
|
for (const [entries, isTeam] of [
|
|
80
78
|
[users, false],
|
|
81
79
|
[teams, true],
|
|
82
80
|
] as const) {
|
|
83
81
|
for (const owner of entries) {
|
|
84
|
-
|
|
85
|
-
else owners.set(owner.data.id, { lane: toLane(owner), isTeam });
|
|
82
|
+
owners.set(owner.data.id, { lane: toLane(owner), isTeam });
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
85
|
|
|
@@ -90,7 +87,6 @@ export const createLaneIndex = ({ domains, systems, teams, users }: LaneCollecti
|
|
|
90
87
|
const teamOf = (resource: Resource): Lane | undefined => {
|
|
91
88
|
const resolved = (resource.data.owners || [])
|
|
92
89
|
.map((owner) => (typeof owner === 'string' ? owner : owner.id))
|
|
93
|
-
.filter((id) => !hiddenOwners.has(id))
|
|
94
90
|
.map((id) => owners.get(id) ?? { lane: { id, name: id }, isTeam: false })
|
|
95
91
|
.sort((a, b) => Number(b.isTeam) - Number(a.isTeam));
|
|
96
92
|
return resolved[0]?.lane;
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "BUSL-1.1",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "5.
|
|
10
|
+
"version": "5.4.0",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -65,8 +65,9 @@
|
|
|
65
65
|
"@iconify-json/logos": "^1.2.4",
|
|
66
66
|
"@mcp-b/webmcp-polyfill": "5.1.0",
|
|
67
67
|
"@mermaid-js/layout-elk": "^0.2.0",
|
|
68
|
-
"@modelcontextprotocol/
|
|
69
|
-
"@modelcontextprotocol/
|
|
68
|
+
"@modelcontextprotocol/client": "2.3.1",
|
|
69
|
+
"@modelcontextprotocol/ext-apps": "2.0.3",
|
|
70
|
+
"@modelcontextprotocol/server": "2.3.1",
|
|
70
71
|
"@nanostores/react": "^1.0.0",
|
|
71
72
|
"@parcel/watcher": "^2.4.1",
|
|
72
73
|
"@radix-ui/react-context-menu": "^2.2.6",
|
|
@@ -140,9 +141,10 @@
|
|
|
140
141
|
"y-protocols": "^1.0.7",
|
|
141
142
|
"yjs": "^13.6.33",
|
|
142
143
|
"zod": "^4.6.6",
|
|
143
|
-
"@eventcatalog/
|
|
144
|
-
"@eventcatalog/
|
|
145
|
-
"@eventcatalog/
|
|
144
|
+
"@eventcatalog/fitness": "0.1.0",
|
|
145
|
+
"@eventcatalog/sdk": "2.31.2",
|
|
146
|
+
"@eventcatalog/linter": "1.2.4",
|
|
147
|
+
"@eventcatalog/exporters": "0.1.2",
|
|
146
148
|
"@eventcatalog/visualiser": "^5.0.0"
|
|
147
149
|
},
|
|
148
150
|
"devDependencies": {
|
|
@@ -188,6 +190,7 @@
|
|
|
188
190
|
"start:catalog:server": "node scripts/start-server-locally.js",
|
|
189
191
|
"preview:catalog": "node scripts/preview-catalog-locally.js",
|
|
190
192
|
"generate:catalog": "node scripts/generate-catalog-locally.js",
|
|
193
|
+
"fitness:catalog": "node scripts/fitness-catalog-locally.js",
|
|
191
194
|
"verify-build:catalog": "rimraf dist && pnpm run build:cd",
|
|
192
195
|
"generate": "pnpm run build:bin && npx . generate",
|
|
193
196
|
"format": "prettier --config .prettierrc --write \"**/*.{js,jsx,ts,tsx,json,astro}\"",
|