diff --git a/examples/workflow-server/package.json b/examples/workflow-server/package.json index def8b70..1b716ae 100644 --- a/examples/workflow-server/package.json +++ b/examples/workflow-server/package.json @@ -58,6 +58,7 @@ "dependencies": { "@eclipse-glsp/layout-elk": "2.6.0-next", "@eclipse-glsp/server": "2.6.0-next", + "@eclipse-glsp/server-mcp": "2.6.0-next", "inversify": "^6.1.3" }, "devDependencies": { diff --git a/examples/workflow-server/src/common/workflow-diagram-module.ts b/examples/workflow-server/src/common/workflow-diagram-module.ts index 8cfdc82..1b68695 100644 --- a/examples/workflow-server/src/common/workflow-diagram-module.ts +++ b/examples/workflow-server/src/common/workflow-diagram-module.ts @@ -21,7 +21,7 @@ import { ContextMenuItemProvider, DiagramConfiguration, EdgeCreationChecker, - GLSPServer, + GLSPServerInitContribution, GModelDiagramModule, InstanceMultiBinding, LabelEditValidator, @@ -57,13 +57,13 @@ import { TaskEditContextActionProvider } from './taskedit/task-edit-context-prov import { TaskEditValidator } from './taskedit/task-edit-validator'; import { WorkflowDiagramConfiguration } from './workflow-diagram-configuration'; import { WorkflowEdgeCreationChecker } from './workflow-edge-creation-checker'; -import { WorkflowGLSPServer } from './workflow-glsp-server'; +import { CustomArgsInitContribution } from './workflow-glsp-server'; import { WorkflowPopupFactory } from './workflow-popup-factory'; @injectable() export class WorkflowServerModule extends ServerModule { - protected override bindGLSPServer(): BindingTarget { - return WorkflowGLSPServer; + protected override configureGLSPServerInitContributions(binding: MultiBinding): void { + binding.add(CustomArgsInitContribution); } } diff --git a/examples/workflow-server/src/common/workflow-glsp-server.ts b/examples/workflow-server/src/common/workflow-glsp-server.ts index aa7579a..60047ed 100644 --- a/examples/workflow-server/src/common/workflow-glsp-server.ts +++ b/examples/workflow-server/src/common/workflow-glsp-server.ts @@ -13,25 +13,32 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { Args, ArgsUtil, DefaultGLSPServer, InitializeResult, Logger, MaybePromise } from '@eclipse-glsp/server'; +import { + ArgsUtil, + GLSPServer, + GLSPServerInitContribution, + InitializeParameters, + InitializeResult, + Logger, + MaybePromise +} from '@eclipse-glsp/server'; import { inject, injectable } from 'inversify'; @injectable() -export class WorkflowGLSPServer extends DefaultGLSPServer { +export class CustomArgsInitContribution implements GLSPServerInitContribution { MESSAGE_KEY = 'message'; TIMESTAMP_KEY = 'timestamp'; - @inject(Logger) - protected override logger: Logger; + @inject(Logger) protected logger: Logger; - protected override handleInitializeArgs(result: InitializeResult, args: Args | undefined): MaybePromise { - if (!args) { + initializeServer(server: GLSPServer, params: InitializeParameters, result: InitializeResult): MaybePromise { + if (!params.args) { return result; } - const timestamp = ArgsUtil.get(args, this.TIMESTAMP_KEY); - const message = ArgsUtil.get(args, this.MESSAGE_KEY); + const timestamp = ArgsUtil.get(params.args, this.TIMESTAMP_KEY); + const message = ArgsUtil.get(params.args, this.MESSAGE_KEY); - this.logger.debug(`${timestamp}: ${message}`); + this.logger.info(`${timestamp}: ${message}`); return result; } } diff --git a/examples/workflow-server/src/node/app.ts b/examples/workflow-server/src/node/app.ts index 1bc2bfd..3a659bc 100644 --- a/examples/workflow-server/src/node/app.ts +++ b/examples/workflow-server/src/node/app.ts @@ -16,9 +16,10 @@ import 'reflect-metadata'; import { configureELKLayoutModule } from '@eclipse-glsp/layout-elk'; -import { createAppModule, GModelStorage, Logger, SocketServerLauncher, WebSocketServerLauncher } from '@eclipse-glsp/server/node'; +import { GModelStorage, Logger, SocketServerLauncher, WebSocketServerLauncher, createAppModule } from '@eclipse-glsp/server/node'; import { Container } from 'inversify'; +import { configureMcpModule } from '@eclipse-glsp/server-mcp'; import { WorkflowLayoutConfigurator } from '../common/layout/workflow-layout-configurator'; import { WorkflowDiagramModule, WorkflowServerModule } from '../common/workflow-diagram-module'; import { createWorkflowCliParser } from './workflow-cli-parser'; @@ -40,14 +41,14 @@ async function launch(argv?: string[]): Promise { const elkLayoutModule = configureELKLayoutModule({ algorithms: ['layered'], layoutConfigurator: WorkflowLayoutConfigurator }); const serverModule = new WorkflowServerModule().configureDiagramModule(new WorkflowDiagramModule(() => GModelStorage), elkLayoutModule); - + const mcpModule = configureMcpModule(); if (options.webSocket) { const launcher = appContainer.resolve(WebSocketServerLauncher); - launcher.configure(serverModule); + launcher.configure(serverModule, mcpModule); await launcher.start({ port: options.port, host: options.host, path: 'workflow' }); } else { const launcher = appContainer.resolve(SocketServerLauncher); - launcher.configure(serverModule); + launcher.configure(serverModule, mcpModule); await launcher.start({ port: options.port, host: options.host }); } } diff --git a/examples/workflow-server/tsconfig.json b/examples/workflow-server/tsconfig.json index f1c4e83..a82e1ff 100644 --- a/examples/workflow-server/tsconfig.json +++ b/examples/workflow-server/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../../packages/server" }, + { + "path": "../../packages/server-mcp" + }, { "path": "../../packages/layout-elk" } diff --git a/packages/server-mcp/.eslintrc.js b/packages/server-mcp/.eslintrc.js new file mode 100644 index 0000000..7e8a109 --- /dev/null +++ b/packages/server-mcp/.eslintrc.js @@ -0,0 +1,12 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'import/no-unresolved': [ + 'error', + { + ignore: ['^@modelcontextprotocol/sdk/'] + } + ] + } +}; diff --git a/packages/server-mcp/package.json b/packages/server-mcp/package.json new file mode 100644 index 0000000..5e5ca87 --- /dev/null +++ b/packages/server-mcp/package.json @@ -0,0 +1,62 @@ +{ + "name": "@eclipse-glsp/server-mcp", + "version": "2.6.0-next", + "description": "Extension of the GLSP Node Server for the Model Context Protocol", + "keywords": [ + "eclipse", + "graphics", + "diagram", + "modeling", + "visualization", + "glsp", + "diagram editor", + "mcp" + ], + "homepage": "https://www.eclipse.org/glsp/", + "bugs": "https://github.com/eclipse-glsp/glsp/issues", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-glsp/glsp-server-node.git" + }, + "license": "(EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0)", + "author": { + "name": "Eclipse GLSP" + }, + "contributors": [ + { + "name": "Eclipse GLSP Project", + "email": "glsp-dev@eclipse.org", + "url": "https://projects.eclipse.org/projects/ecd.glsp" + } + ], + "main": "lib/index", + "types": "lib/index", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "tsc -b", + "clean": "rimraf lib *.tsbuildinfo coverage .nyc_output", + "generate:index": "glsp generateIndex src -f -s", + "lint": "eslint --ext .ts,.tsx ./src", + "test": "mocha --config ../../.mocharc \"./src/**/*.spec.?(ts|tsx)\"", + "test:ci": "yarn test --reporter mocha-ctrf-json-reporter", + "test:coverage": "nyc yarn test", + "watch": "tsc -w" + }, + "dependencies": { + "@eclipse-glsp/server": "2.6.0-next", + "@modelcontextprotocol/sdk": "^1.25.1", + "express": "^5.2.1" + }, + "devDependencies": { + "@types/express": "^5.0.6" + }, + "peerDependencies": { + "inversify": "^6.1.3" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/server-mcp/src/default-mcp-resource-contribution.ts b/packages/server-mcp/src/default-mcp-resource-contribution.ts new file mode 100644 index 0000000..83c723e --- /dev/null +++ b/packages/server-mcp/src/default-mcp-resource-contribution.ts @@ -0,0 +1,333 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { + ClientSessionManager, + CreateOperationHandler, + DiagramModules, + GModelSerializer, + Logger, + ModelState, + OperationHandlerRegistry +} from '@eclipse-glsp/server'; +import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp'; +import { ReadResourceResult } from '@modelcontextprotocol/sdk/types'; +import { ContainerModule, inject, injectable } from 'inversify'; +import { McpServerContribution } from './mcp-server-contribution'; +import { GLSPMcpServer } from './mcp-server-manager'; +import { extractParam } from './mcp-util'; + +/** + * Default MCP server contribution that provides read-only resources for accessing + * GLSP server state, including sessions, diagram types, element types, + * diagram models, and validation markers. + * + * This contribution can be overridden to customize or extend resource functionality. + */ +@injectable() +export class DefaultMcpResourceContribution implements McpServerContribution { + @inject(Logger) protected logger: Logger; + @inject(DiagramModules) protected diagramModules: Map; + @inject(ClientSessionManager) protected clientSessionManager: ClientSessionManager; + + configure(server: GLSPMcpServer): void { + this.registerSessionsListResource(server); + this.registerSessionInfoResource(server); + this.registerDiagramTypesResource(server); + this.registerElementTypesResource(server); + this.registerDiagramModelResource(server); + } + + protected registerSessionsListResource(server: GLSPMcpServer): void { + server.registerResource( + 'sessions-list', + 'glsp://sessions', + { + title: 'GLSP Sessions List', + description: 'List all active GLSP client sessions across all diagram types', + mimeType: 'application/json' + }, + async () => this.getAllSessions() + ); + } + + protected registerSessionInfoResource(server: GLSPMcpServer): void { + server.registerResource( + 'session-info', + new ResourceTemplate('glsp://sessions/{sessionId}', { + list: async () => { + const sessions = this.clientSessionManager.getSessions(); + return { + resources: sessions.map(session => ({ + uri: `glsp://sessions/${session.id}`, + name: `Session: ${session.id}`, + description: `GLSP session for diagram type: ${session.diagramType}`, + mimeType: 'application/json' + })) + }; + }, + complete: { + sessionId: async () => this.getSessionIds() + } + }), + { + title: 'Session Information', + description: + 'Get detailed metadata for a specific GLSP client session including diagram type, source URI, and edit permissions', + mimeType: 'application/json' + }, + async (_uri, params) => this.getSessionInfo(params) + ); + } + + protected registerDiagramTypesResource(server: GLSPMcpServer): void { + server.registerResource( + 'diagram-types', + 'glsp://types', + { + title: 'Available Diagram Types', + description: 'List all registered GLSP diagram types (e.g., workflow, class-diagram, state-machine)', + mimeType: 'application/json' + }, + async () => this.getDiagramTypesList() + ); + } + + protected registerElementTypesResource(server: GLSPMcpServer): void { + server.registerResource( + 'element-types', + new ResourceTemplate('glsp://types/{diagramType}/elements', { + list: async () => { + const diagramTypes = Array.from(this.diagramModules.keys()); + return { + resources: diagramTypes.map(type => ({ + uri: `glsp://types/${type}/elements`, + name: `Element Types: ${type}`, + description: `Creatable element types for ${type} diagrams`, + mimeType: 'application/json' + })) + }; + }, + complete: { + diagramType: async () => this.getDiagramTypes() + } + }), + { + title: 'Creatable Element Types', + description: + 'List all element types (nodes and edges) that can be created for a specific diagram type. ' + + 'Use this to discover valid elementTypeId values for creation tools.', + mimeType: 'application/json' + }, + async (_uri, params) => this.getElementTypes(params) + ); + } + + protected registerDiagramModelResource(server: GLSPMcpServer): void { + server.registerResource( + 'diagram-model', + new ResourceTemplate('glsp://diagrams/{sessionId}/model', { + list: async () => { + const sessions = this.clientSessionManager.getSessions(); + return { + resources: sessions.map(session => ({ + uri: `glsp://diagrams/${session.id}/model`, + name: `Diagram Model: ${session.id}`, + description: `Complete GLSP model structure for session ${session.id}`, + mimeType: 'application/json' + })) + }; + }, + complete: { + sessionId: async () => this.getSessionIds() + } + }), + { + title: 'Diagram Model Structure', + description: + 'Get the complete GLSP model (GModelRoot) for a session as a JSON structure. ' + + 'Includes all nodes, edges, and their properties.', + mimeType: 'application/json' + }, + async (_uri, params) => this.getDiagramModel(params) + ); + } + + // --- Resource Handlers --- + + protected async getAllSessions(): Promise { + const sessions = this.clientSessionManager.getSessions(); + const sessionsList = sessions.map(session => { + const modelState = session.container.get(ModelState); + return { + sessionId: session.id, + diagramType: session.diagramType, + sourceUri: modelState.sourceUri, + readOnly: modelState.isReadonly + }; + }); + + return { + contents: [ + { + uri: 'glsp://sessions', + mimeType: 'application/json', + text: JSON.stringify({ sessions: sessionsList }, undefined, 2) + } + ] + }; + } + + protected async getSessionInfo(params: Record): Promise { + const sessionId = extractParam(params, 'sessionId'); + if (!sessionId) { + return { contents: [] }; + } + + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return { contents: [] }; + } + + const modelState = session.container.get(ModelState); + const sessionInfo = { + sessionId: session.id, + diagramType: session.diagramType, + sourceUri: modelState.sourceUri, + readOnly: modelState.isReadonly + }; + + return { + contents: [ + { + uri: `glsp://sessions/${sessionId}`, + mimeType: 'application/json', + text: JSON.stringify(sessionInfo, undefined, 2) + } + ] + }; + } + + protected async getDiagramTypesList(): Promise { + const diagramTypes = Array.from(this.diagramModules.keys()); + + return { + contents: [ + { + uri: 'glsp://types', + mimeType: 'application/json', + text: JSON.stringify({ diagramTypes }, undefined, 2) + } + ] + }; + } + + protected async getElementTypes(params: Record): Promise { + const diagramType = extractParam(params, 'diagramType'); + if (!diagramType) { + return { contents: [] }; + } + + // Try to get a session of this diagram type to access the operation handler registry + const sessions = this.clientSessionManager.getSessionsByType(diagramType); + if (sessions.length === 0) { + return { + contents: [ + { + uri: `glsp://types/${diagramType}/elements`, + mimeType: 'application/json', + text: JSON.stringify( + { + diagramType, + nodeTypes: [], + edgeTypes: [], + message: 'No active session found for this diagram type. Create a session first to discover element types.' + }, + undefined, + 2 + ) + } + ] + }; + } + + const session = sessions[0]; + const registry = session.container.get(OperationHandlerRegistry); + + const nodeTypes: Array<{ id: string; label: string }> = []; + const edgeTypes: Array<{ id: string; label: string }> = []; + + for (const key of registry.keys()) { + const handler = registry.get(key); + if (handler && CreateOperationHandler.is(handler)) { + if (key.startsWith('createNode_')) { + const elementTypeId = key.substring('createNode_'.length); + nodeTypes.push({ id: elementTypeId, label: elementTypeId }); + } else if (key.startsWith('createEdge_')) { + const elementTypeId = key.substring('createEdge_'.length); + edgeTypes.push({ id: elementTypeId, label: elementTypeId }); + } + } + } + + return { + contents: [ + { + uri: `glsp://types/${diagramType}/elements`, + mimeType: 'application/json', + text: JSON.stringify({ diagramType, nodeTypes, edgeTypes }, undefined, 2) + } + ] + }; + } + + protected async getDiagramModel(params: Record): Promise { + const sessionId = extractParam(params, 'sessionId'); + if (!sessionId) { + return { contents: [] }; + } + + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return { contents: [] }; + } + + const modelState = session.container.get(ModelState); + const serializer = session.container.get(GModelSerializer); + + const schema = serializer.createSchema(modelState.root); + + return { + contents: [ + { + uri: `glsp://diagrams/${sessionId}/model`, + mimeType: 'application/json', + text: JSON.stringify(schema, undefined, 2) + } + ] + }; + } + + // --- Utility Methods --- + + protected async getSessionIds(): Promise { + return this.clientSessionManager.getSessions().map(s => s.id); + } + + protected async getDiagramTypes(): Promise { + return Array.from(this.diagramModules.keys()); + } +} diff --git a/packages/server-mcp/src/default-mcp-tool-contribution.ts b/packages/server-mcp/src/default-mcp-tool-contribution.ts new file mode 100644 index 0000000..6ae5453 --- /dev/null +++ b/packages/server-mcp/src/default-mcp-tool-contribution.ts @@ -0,0 +1,562 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { DeleteElementOperation, MarkersReason, RedoAction, SaveModelAction, UndoAction } from '@eclipse-glsp/protocol'; +import { + ClientSessionManager, + CommandStack, + CreateEdgeOperation, + CreateNodeOperation, + Logger, + ModelState, + ModelValidator +} from '@eclipse-glsp/server'; +import { CallToolResult } from '@modelcontextprotocol/sdk/types'; +import { inject, injectable } from 'inversify'; +import * as z from 'zod/v4'; +import { McpServerContribution } from './mcp-server-contribution'; +import { GLSPMcpServer } from './mcp-server-manager'; +import { createToolError, createToolSuccess } from './mcp-util'; + +/** + * Default MCP server contribution that provides tools for performing actions on + * GLSP diagrams, including validation and element creation. + * + * This contribution can be overridden to customize or extend tool functionality. + */ +@injectable() +export class DefaultMcpToolContribution implements McpServerContribution { + @inject(Logger) protected logger: Logger; + @inject(ClientSessionManager) protected clientSessionManager: ClientSessionManager; + + configure(server: GLSPMcpServer): void { + this.registerValidateDiagramTool(server); + this.registerCreateNodeTool(server); + this.registerCreateEdgeTool(server); + this.registerDeleteElementTool(server); + this.registerUndoTool(server); + this.registerRedoTool(server); + this.registerSaveTool(server); + } + + protected registerValidateDiagramTool(server: GLSPMcpServer): void { + server.registerTool( + 'validate-diagram', + { + description: + 'Validate diagram elements and return validation markers (errors, warnings, info). ' + + 'Triggers active validation computation. Use elementIds parameter to validate specific elements, ' + + 'or omit to validate the entire model.', + inputSchema: { + sessionId: z.string().describe('Session ID to validate'), + elementIds: z + .array(z.string()) + .optional() + .describe('Array of element IDs to validate. If not provided, validates entire model starting from root.'), + reason: z + .enum([MarkersReason.BATCH, MarkersReason.LIVE]) + .optional() + .default(MarkersReason.LIVE) + .describe('Validation reason: "batch" for thorough validation, "live" for quick incremental checks') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether validation completed successfully'), + markers: z + .array( + z.object({ + label: z.string().describe('Short label for the validation issue'), + description: z.string().describe('Full description of the validation issue'), + elementId: z.string().describe('ID of the element with the issue'), + kind: z.enum(['info', 'warning', 'error']).describe('Severity of the validation issue') + }) + ) + .describe('Array of validation markers found'), + message: z.string().optional().describe('Additional information (e.g., "No validator configured")') + }) + }, + params => this.validateDiagram(params) + ); + } + + protected registerCreateNodeTool(server: GLSPMcpServer): void { + server.registerTool( + 'create-node', + { + description: + 'Create a new node element in the diagram at a specified location. ' + + 'This operation modifies the diagram state and requires user approval. ' + + 'Query glsp://types/{diagramType}/elements resource to discover valid element type IDs.', + inputSchema: { + sessionId: z.string().describe('Session ID where the node should be created'), + elementTypeId: z + .string() + .describe( + 'Element type ID (e.g., "task:manual", "task:automated"). ' + + 'Use element-types resource to discover valid IDs.' + ), + location: z + .object({ + x: z.number().describe('X coordinate in diagram space'), + y: z.number().describe('Y coordinate in diagram space') + }) + .describe('Position where the node should be created (absolute diagram coordinates)'), + containerId: z.string().optional().describe('ID of the container element. If not provided, node is added to the root.'), + args: z + .record(z.string(), z.any()) + .optional() + .describe('Additional type-specific arguments for node creation (varies by element type)') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether node creation succeeded'), + elementId: z.string().optional().describe('ID of the newly created node'), + message: z.string().describe('Success or error message'), + error: z.string().optional().describe('Error message if operation failed'), + details: z.any().optional().describe('Additional error details') + }) + }, + params => this.createNode(params) + ); + } + + protected registerCreateEdgeTool(server: GLSPMcpServer): void { + server.registerTool( + 'create-edge', + { + description: + 'Create a new edge connecting two elements in the diagram. ' + + 'This operation modifies the diagram state and requires user approval. ' + + 'Query glsp://types/{diagramType}/elements resource to discover valid edge type IDs.', + inputSchema: { + sessionId: z.string().describe('Session ID where the edge should be created'), + elementTypeId: z + .string() + .describe('Edge type ID (e.g., "edge", "transition"). Use element-types resource to discover valid IDs.'), + sourceElementId: z.string().describe('ID of the source element (must exist in the diagram)'), + targetElementId: z.string().describe('ID of the target element (must exist in the diagram)'), + args: z + .record(z.string(), z.any()) + .optional() + .describe('Additional type-specific arguments for edge creation (varies by edge type)') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether edge creation succeeded'), + elementId: z.string().optional().describe('ID of the newly created edge'), + message: z.string().describe('Success or error message'), + error: z.string().optional().describe('Error message if operation failed'), + details: z.any().optional().describe('Additional error details') + }) + }, + params => this.createEdge(params) + ); + } + + // --- Tool Handlers --- + + protected async validateDiagram(params: any): Promise { + const { sessionId, elementIds, reason } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const modelState = session.container.get(ModelState); + + // Try to get ModelValidator (it's optional) + let validator: ModelValidator | undefined; + try { + validator = session.container.get(ModelValidator); + } catch (error) { + // No validator bound - this is acceptable + } + + if (!validator) { + return createToolSuccess({ + markers: [], + message: 'No validator configured for this diagram type' + }); + } + + // Determine which elements to validate + const idsToValidate = elementIds && elementIds.length > 0 ? elementIds : [modelState.root.id]; + + // Get elements from index + const elements = modelState.index.getAll(idsToValidate); + + // Run validation + const markers = await validator.validate(elements, reason ?? MarkersReason.BATCH); + + return createToolSuccess({ markers }); + } catch (error) { + this.logger.error('Validation failed', error); + return createToolError('Validation failed', { message: error instanceof Error ? error.message : String(error) }); + } + } + + protected async createNode(params: any): Promise { + const { sessionId, elementTypeId, location, containerId, args } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const modelState = session.container.get(ModelState); + + // Check if model is readonly + if (modelState.isReadonly) { + return createToolError('Model is read-only', { sessionId }); + } + + // Snapshot element IDs before operation using index.allIds() + const beforeIds = new Set(modelState.index.allIds()); + + // Create operation + const operation = CreateNodeOperation.create(elementTypeId, { location, containerId, args }); + + // Dispatch operation + await session.actionDispatcher.dispatch(operation); + + // Snapshot element IDs after operation + const afterIds = modelState.index.allIds(); + + // Find new element ID + const newIds = afterIds.filter(id => !beforeIds.has(id)); + const newElementId = newIds.length > 0 ? newIds[0] : undefined; + + if (!newElementId) { + return createToolError('Node creation succeeded but could not determine element ID'); + } + + return createToolSuccess({ + elementId: newElementId, + message: 'Node created successfully' + }); + } catch (error) { + this.logger.error('Node creation failed', error); + return createToolError('Node creation failed', { message: error instanceof Error ? error.message : String(error) }); + } + } + + protected async createEdge(params: any): Promise { + const { sessionId, elementTypeId, sourceElementId, targetElementId, args } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const modelState = session.container.get(ModelState); + + // Check if model is readonly + if (modelState.isReadonly) { + return createToolError('Model is read-only', { sessionId }); + } + + // Validate source and target exist + const source = modelState.index.find(sourceElementId); + if (!source) { + return createToolError('Source element not found', { sourceElementId }); + } + + const target = modelState.index.find(targetElementId); + if (!target) { + return createToolError('Target element not found', { targetElementId }); + } + + // Snapshot element IDs before operation using index.allIds() + const beforeIds = new Set(modelState.index.allIds()); + + // Create operation + const operation = CreateEdgeOperation.create({ elementTypeId, sourceElementId, targetElementId, args }); + + // Dispatch operation + await session.actionDispatcher.dispatch(operation); + + // Snapshot element IDs after operation + const afterIds = modelState.index.allIds(); + + // Find new element ID + const newIds = afterIds.filter(id => !beforeIds.has(id)); + const newElementId = newIds.length > 0 ? newIds[0] : undefined; + + if (!newElementId) { + return createToolError('Edge creation succeeded but could not determine element ID'); + } + + return createToolSuccess({ + elementId: newElementId, + message: 'Edge created successfully' + }); + } catch (error) { + this.logger.error('Edge creation failed', error); + return createToolError('Edge creation failed', { message: error instanceof Error ? error.message : String(error) }); + } + } + + protected registerDeleteElementTool(server: GLSPMcpServer): void { + server.registerTool( + 'delete-element', + { + description: + 'Delete one or more elements (nodes or edges) from the diagram. ' + + 'This operation modifies the diagram state and requires user approval. ' + + 'Automatically handles dependent elements (e.g., deleting a node also deletes connected edges).', + inputSchema: { + sessionId: z.string().describe('Session ID where the elements should be deleted'), + elementIds: z.array(z.string()).min(1).describe('Array of element IDs to delete. Must include at least one element ID.') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether element deletion succeeded'), + deletedCount: z.number().optional().describe('Number of elements deleted (including dependents)'), + message: z.string().describe('Success or error message'), + error: z.string().optional().describe('Error message if operation failed'), + details: z.any().optional().describe('Additional error details') + }) + }, + params => this.deleteElement(params) + ); + } + + protected async deleteElement(params: any): Promise { + const { sessionId, elementIds } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const modelState = session.container.get(ModelState); + + // Check if model is readonly + if (modelState.isReadonly) { + return createToolError('Model is read-only', { sessionId }); + } + + // Validate elements exist + const missingIds: string[] = []; + for (const elementId of elementIds) { + const element = modelState.index.find(elementId); + if (!element) { + missingIds.push(elementId); + } + } + + if (missingIds.length > 0) { + return createToolError('Some elements not found', { missingIds }); + } + + // Snapshot element count before operation + const beforeCount = modelState.index.allIds().length; + + // Create and dispatch delete operation + const operation = DeleteElementOperation.create(elementIds); + await session.actionDispatcher.dispatch(operation); + + // Calculate how many elements were deleted (including dependents) + const afterCount = modelState.index.allIds().length; + const deletedCount = beforeCount - afterCount; + + return createToolSuccess({ + deletedCount, + message: `Successfully deleted ${deletedCount} element(s) (including dependents)` + }); + } catch (error) { + this.logger.error('Element deletion failed', error); + return createToolError('Element deletion failed', { message: error instanceof Error ? error.message : String(error) }); + } + } + + protected registerUndoTool(server: GLSPMcpServer): void { + server.registerTool( + 'undo', + { + description: 'Undo the last executed command in the diagram. Reverts the most recent change made to the model.', + inputSchema: { + sessionId: z.string().describe('Session ID where undo should be performed') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether undo succeeded'), + canUndo: z.boolean().describe('Whether there are more commands to undo'), + canRedo: z.boolean().describe('Whether there are commands that can be redone'), + message: z.string().describe('Success or error message'), + error: z.string().optional().describe('Error message if operation failed'), + details: z.any().optional().describe('Additional error details') + }) + }, + params => this.undo(params) + ); + } + + protected async undo(params: any): Promise { + const { sessionId } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const modelState = session.container.get(ModelState); + + // Check if model is readonly + if (modelState.isReadonly) { + return createToolError('Model is read-only', { sessionId }); + } + + const commandStack = session.container.get(CommandStack); + + if (!commandStack.canUndo()) { + return createToolError('Nothing to undo', { canUndo: false, canRedo: commandStack.canRedo() }); + } + + // Dispatch undo action + const action = UndoAction.create(); + await session.actionDispatcher.dispatch(action); + + return createToolSuccess({ + canUndo: commandStack.canUndo(), + canRedo: commandStack.canRedo(), + message: 'Undo successful' + }); + } catch (error) { + this.logger.error('Undo failed', error); + return createToolError('Undo failed', { message: error instanceof Error ? error.message : String(error) }); + } + } + + protected registerRedoTool(server: GLSPMcpServer): void { + server.registerTool( + 'redo', + { + description: 'Redo the last undone command in the diagram. Re-applies the most recently undone change.', + inputSchema: { + sessionId: z.string().describe('Session ID where redo should be performed') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether redo succeeded'), + canUndo: z.boolean().describe('Whether there are commands to undo'), + canRedo: z.boolean().describe('Whether there are more commands that can be redone'), + message: z.string().describe('Success or error message'), + error: z.string().optional().describe('Error message if operation failed'), + details: z.any().optional().describe('Additional error details') + }) + }, + params => this.redo(params) + ); + } + + protected async redo(params: any): Promise { + const { sessionId } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const modelState = session.container.get(ModelState); + + // Check if model is readonly + if (modelState.isReadonly) { + return createToolError('Model is read-only', { sessionId }); + } + + const commandStack = session.container.get(CommandStack); + + if (!commandStack.canRedo()) { + return createToolError('Nothing to redo', { canUndo: commandStack.canUndo(), canRedo: false }); + } + + // Dispatch redo action + const action = RedoAction.create(); + await session.actionDispatcher.dispatch(action); + + return createToolSuccess({ + canUndo: commandStack.canUndo(), + canRedo: commandStack.canRedo(), + message: 'Redo successful' + }); + } catch (error) { + this.logger.error('Redo failed', error); + return createToolError('Redo failed', { message: error instanceof Error ? error.message : String(error) }); + } + } + + protected registerSaveTool(server: GLSPMcpServer): void { + server.registerTool( + 'save-model', + { + description: + 'Save the current diagram model to persistent storage. ' + + 'This operation persists all changes back to the source model. ' + + 'Optionally specify a new fileUri to save to a different location.', + inputSchema: { + sessionId: z.string().describe('Session ID where the model should be saved'), + fileUri: z + .string() + .optional() + .describe('Optional destination file URI. If not provided, saves to the original source model location.') + }, + outputSchema: z.object({ + success: z.boolean().describe('Whether save succeeded'), + isDirty: z.boolean().describe('Whether the model is still dirty after save (should be false on success)'), + message: z.string().describe('Success or error message'), + error: z.string().optional().describe('Error message if operation failed'), + details: z.any().optional().describe('Additional error details') + }) + }, + params => this.saveModel(params) + ); + } + + protected async saveModel(params: any): Promise { + const { sessionId, fileUri } = params; + + try { + const session = this.clientSessionManager.getSession(sessionId); + if (!session) { + return createToolError('Session not found', { sessionId }); + } + + const commandStack = session.container.get(CommandStack); + + // Check if there are unsaved changes + if (!commandStack.isDirty) { + return createToolSuccess({ + isDirty: false, + message: 'No changes to save' + }); + } + + // Dispatch save action + const action = SaveModelAction.create({ fileUri }); + await session.actionDispatcher.dispatch(action); + + return createToolSuccess({ + isDirty: commandStack.isDirty, + message: 'Model saved successfully' + }); + } catch (error) { + this.logger.error('Save failed', error); + return createToolError('Save failed', { message: error instanceof Error ? error.message : String(error) }); + } + } +} diff --git a/packages/server-mcp/src/di.config.ts b/packages/server-mcp/src/di.config.ts new file mode 100644 index 0000000..a8a7426 --- /dev/null +++ b/packages/server-mcp/src/di.config.ts @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { GLSPServerInitContribution, GLSPServerListener, bindAsService } from '@eclipse-glsp/server'; +import { ContainerModule } from 'inversify'; +import { DefaultMcpResourceContribution } from './default-mcp-resource-contribution'; +import { DefaultMcpToolContribution } from './default-mcp-tool-contribution'; +import { McpServerContribution } from './mcp-server-contribution'; +import { McpServerManager } from './mcp-server-manager'; + +export function configureMcpModule(): ContainerModule { + return new ContainerModule(bind => { + bind(McpServerManager).toSelf().inSingletonScope(); + bind(GLSPServerInitContribution).toService(McpServerManager); + bind(GLSPServerListener).toService(McpServerManager); + + // Register default MCP contributions for resources and tools + bindAsService(bind, McpServerContribution, DefaultMcpResourceContribution); + bindAsService(bind, McpServerContribution, DefaultMcpToolContribution); + }); +} diff --git a/packages/server-mcp/src/http-server-with-sessions.ts b/packages/server-mcp/src/http-server-with-sessions.ts new file mode 100644 index 0000000..e851b9b --- /dev/null +++ b/packages/server-mcp/src/http-server-with-sessions.ts @@ -0,0 +1,209 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { Deferred, Disposable, Emitter, Logger } from '@eclipse-glsp/server'; +import { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore'; +import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp'; +import { isInitializeRequest } from '@modelcontextprotocol/sdk/types'; +import type { Express } from 'express'; +import * as express from 'express'; +import * as http from 'http'; +import { injectable } from 'inversify'; +import { AddressInfo } from 'net'; +import { randomUUID } from 'node:crypto'; +import { FullMcpServerConfiguration } from './mcp-server-manager'; + +export type WithSessionId = T & { get sessionId(): string }; + +export type McpClientSession = WithSessionId; + +@injectable() +export class McpHttpServerWithSessions implements Disposable { + protected _app?: Express; + protected _server?: http.Server; + protected _addressInfo = new Deferred(); + + protected sessions = new Map(); + protected onSessionCreatedEmitter = new Emitter(); + onSessionCreated = this.onSessionCreatedEmitter.event; + protected onSessionInitializedEmitter = new Emitter(); + onSessionInitialized = this.onSessionInitializedEmitter.event; + + constructor(protected logger: Logger) {} + + get app(): Express | undefined { + return this._app; + } + + get server(): http.Server | undefined { + return this._server; + } + + getAddress(): Promise { + return this._addressInfo.promise; + } + + start({ route, host, port }: FullMcpServerConfiguration): Promise { + this._app = createMcpExpressApp({ host }); + this._app.post(route, this.handlePostRequest.bind(this)); + this._app.get(route, this.handleGetRequest.bind(this)); + this._app.delete(route, this.handleDeleteRequest.bind(this)); + this._server = this._app.listen(port, host); + this._server.on('listening', () => this.listening()); + return this.getAddress(); + } + + protected listening(): void { + const addressInfo = this.server?.address(); + if (!addressInfo) { + this.logger.error('Could not resolve MCP Server address info. Shutting down.'); + this._server?.close(); + return; + } else if (typeof addressInfo === 'string') { + this.logger.error(`MCP Server is unexpectedly listening to pipe or domain socket "${addressInfo}". Shutting down.`); + this._server?.close(); + return; + } + this._addressInfo.resolve(addressInfo); + } + + protected async handlePostRequest(req: express.Request, res: express.Response): Promise { + const client = this.getOrCreateClient(req, res); + if (!client) { + return; + } + + this.logger.info(`Handling POST request for session ${client.sessionId}`); + // Handle the request with existing transport - no need to reconnect + // The existing transport is already connected to the server + try { + await client.handleRequest(req, res, req.body); + } catch (error) { + this.logger.error('Error handling MCP request:', error); + if (!res.headersSent) { + res.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error' }, id: undefined }); + } + } + } + + /** + * Handle GET requests for SSE streams (using built-in support from StreamableHTTP) + */ + protected async handleGetRequest(req: express.Request, res: express.Response): Promise { + const client = this.getClient(req, res); + if (!client) { + return; + } + + // Check for Last-Event-ID header for resumability + const lastEventId = req.headers['last-event-id'] as string | undefined; + if (lastEventId) { + this.logger.info(`Client reconnecting with Last-Event-ID: ${lastEventId}`); + } else { + this.logger.info(`Establishing new SSE stream for session ${client.sessionId}`); + } + await client.handleRequest(req, res); + } + + /** + * Handle DELETE requests for session termination (according to MCP spec). + */ + protected async handleDeleteRequest(req: express.Request, res: express.Response): Promise { + const client = this.getClient(req, res); + if (!client) { + return; + } + + this.logger.info(`Received session termination request for session ${client.sessionId}`); + try { + // this will close the client + await client.handleRequest(req, res); + } catch (error) { + this.logger.error('Error handling session termination:', error); + if (!res.headersSent) { + res.status(500).send('Error processing session termination'); + } + } + } + + protected getOrCreateClient(req: express.Request, res: express.Response): StreamableHTTPServerTransport | undefined { + const client = this.getClient(req); + if (client) { + // existing client + return client; + } + if (!isInitializeRequest(req.body)) { + res.status(400).json({ + jsonrpc: '2.0', + error: { code: -32000, message: 'Bad Request: No valid session ID provided' }, + id: undefined + }); + return undefined; + } + return this.createClient(); + } + + protected getClient(req: express.Request, res?: express.Response): StreamableHTTPServerTransport | undefined { + const sessionId = req.headers['mcp-session-id'] as string | undefined; + if (!sessionId) { + res?.status(400).send('Missing session ID'); + return; + } + const client = this.sessions.get(sessionId); + if (!client) { + res?.status(404).send('Invalid session ID, no client found.'); + return; + } + return client; + } + + protected createClient(): StreamableHTTPServerTransport { + const client = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + eventStore: new InMemoryEventStore(), // Enable resumability + onsessioninitialized: sessionId => { + // Store the transport by session ID when session is initialized + // This avoids race conditions where requests might come in before the session is stored + this.logger.info(`Session initialized with ID: ${sessionId}`); + this.sessions.set(sessionId, client); + this.onSessionInitializedEmitter.fire(client as WithSessionId); + } + }); + client.onclose = () => this.closeClient(client.sessionId); + this.onSessionCreatedEmitter.fire(client); + return client; + } + + protected closeClient(sessionId?: string): void { + if (!sessionId) { + return; + } + const client = this.sessions.get(sessionId); + if (client) { + this.sessions.delete(sessionId); + client.close(); + this.logger.info(`Closed and removed client with session ID ${sessionId}`); + } + } + + dispose(): void { + this.server?.close(); + Array.from(this.sessions.values()).forEach(client => client.close()); + this.sessions.clear(); + this.logger.info('Server shutdown complete'); + } +} diff --git a/packages/server-mcp/src/index.ts b/packages/server-mcp/src/index.ts new file mode 100644 index 0000000..174d0b8 --- /dev/null +++ b/packages/server-mcp/src/index.ts @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './default-mcp-resource-contribution'; +export * from './default-mcp-tool-contribution'; +export * from './di.config'; +export * from './http-server-with-sessions'; +export * from './mcp-server-contribution'; +export * from './mcp-server-manager'; +export * from './mcp-util'; diff --git a/packages/server-mcp/src/mcp-server-contribution.ts b/packages/server-mcp/src/mcp-server-contribution.ts new file mode 100644 index 0000000..159166d --- /dev/null +++ b/packages/server-mcp/src/mcp-server-contribution.ts @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { MaybePromise } from '@eclipse-glsp/server'; +import { CallToolResult, ReadResourceResult } from '@modelcontextprotocol/sdk/types'; +import { GLSPMcpServer } from './mcp-server-manager'; + +export interface McpServerContribution { + configure(server: GLSPMcpServer): MaybePromise; +} +export const McpServerContribution = Symbol('McpServerContribution'); + +export type ToolResultContent = CallToolResult['content'][number]; +export type ResourceResultContent = ReadResourceResult['contents'][number]; diff --git a/packages/server-mcp/src/mcp-server-manager.ts b/packages/server-mcp/src/mcp-server-manager.ts new file mode 100644 index 0000000..8cbeba8 --- /dev/null +++ b/packages/server-mcp/src/mcp-server-manager.ts @@ -0,0 +1,101 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { + Disposable, + DisposableCollection, + GLSPServer, + GLSPServerInitContribution, + GLSPServerListener, + InitializeParameters, + InitializeResult, + Logger, + McpInitializeResult, + McpServerConfiguration, + getMcpServerConfig +} from '@eclipse-glsp/server'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { inject, injectable, multiInject, optional } from 'inversify'; +import { AddressInfo } from 'net'; +import { McpClientSession, McpHttpServerWithSessions } from './http-server-with-sessions'; +import { McpServerContribution } from './mcp-server-contribution'; + +export type FullMcpServerConfiguration = Required; + +export interface GLSPMcpServer extends Pick {} + +@injectable() +export class McpServerManager implements GLSPServerInitContribution, GLSPServerListener, Disposable { + @inject(Logger) protected logger: Logger; + + protected toDispose = new DisposableCollection(); + protected serverUrl: string; + + constructor(@multiInject(McpServerContribution) @optional() protected contributions: McpServerContribution[] = []) {} + + async initializeServer(server: GLSPServer, params: InitializeParameters, result: McpInitializeResult): Promise { + const mcpServerParam = getMcpServerConfig(params); + if (!mcpServerParam) { + return result; + } + + const { port = 0, host = '127.0.0.1', route = '/glsp-mcp', name = 'glspMcpServer' } = mcpServerParam; + const mcpServerConfig: FullMcpServerConfiguration = { port, host, route, name }; + + const httpServer = new McpHttpServerWithSessions(this.logger); + httpServer.onSessionInitialized(client => this.onSessionInitialized(client, mcpServerConfig)); + this.toDispose.push(httpServer); + + const address = await httpServer.start(mcpServerConfig); + this.serverUrl = this.toServerUrl(address, route); + this.logger.info(`MCP server '${mcpServerConfig.name}' is ready to accept new client requests on: ${this.serverUrl}`); + result.mcpServer = { name: mcpServerConfig.name, url: this.serverUrl }; + return result; + } + + protected toServerUrl({ address, family, port }: AddressInfo, route: string, protocol = 'http'): string { + const host = address === '::' || address === '0.0.0.0' ? 'localhost' : family === 'IPv6' ? `[${address}]` : address; + return `${protocol}://${host}:${port}${route}`; + } + + protected onSessionInitialized(client: McpClientSession, config: FullMcpServerConfiguration): void { + this.logger.info(`MCP session initialized with ID: ${client.sessionId}`); + const server = this.createMcpServer(config); + // server assumes control of the connection + server.connect(client); + this.toDispose.push(Disposable.create(() => server.close())); + } + + protected createMcpServer({ name }: FullMcpServerConfiguration): McpServer { + const server = new McpServer({ name, version: '1.0.0' }, { capabilities: { logging: {} } }); + const glspMcpServer: GLSPMcpServer = { + registerPrompt: server.registerPrompt.bind(server), + registerResource: server.registerResource.bind(server), + registerTool: server.registerTool.bind(server) + }; + this.contributions.forEach(contribution => contribution.configure(glspMcpServer)); + return server; + } + + serverShutDown(server: GLSPServer): void { + this.dispose(); + } + + dispose(): void { + this.toDispose.dispose(); + this.toDispose.clear(); + } +} diff --git a/packages/server-mcp/src/mcp-util.ts b/packages/server-mcp/src/mcp-util.ts new file mode 100644 index 0000000..1344da6 --- /dev/null +++ b/packages/server-mcp/src/mcp-util.ts @@ -0,0 +1,72 @@ +/******************************************************************************** + * Copyright (c) 2025 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { CallToolResult } from '@modelcontextprotocol/sdk/types'; + +/** + * Extracts a single parameter value from MCP resource template parameters. + * Parameters can be either a string or an array of strings. + * + * @param params The parameter record from the resource template + * @param key The parameter key to extract + * @returns The first value if it's an array, or the value directly if it's a string + */ +export function extractParam(params: Record, key: string): string | undefined { + const value = params[key]; + return Array.isArray(value) ? value[0] : value; +} + +/** + * Creates a tool result with both text and structured content. + * This generic function handles both success and error cases consistently. + * + * @param data The data to include in the response + * @returns A CallToolResult with the provided data in both text and structured form + */ +export function createToolResult>(data: T): CallToolResult { + return { + content: [ + { + type: 'text', + text: JSON.stringify(data, undefined, 2) + } + ], + structuredContent: data + }; +} + +/** + * Creates a successful tool result with both text and structured content. + * Includes a `success: true` flag and spreads the provided data. + * + * @param data Additional data to include in the success response + * @returns A CallToolResult with success status and the provided data + */ +export function createToolSuccess = Record>(data: T): CallToolResult { + return createToolResult({ success: true, ...data }); +} + +/** + * Creates an error tool result with both text and structured content. + * Includes a `success: false` flag, error message, and optional details. + * + * @param message The error message + * @param details Optional additional error details + * @returns A CallToolResult with error status and details + */ +export function createToolError = Record>(message: string, details?: T): CallToolResult { + return createToolResult({ success: false, message, error: message, ...(details && { details }) }); +} diff --git a/packages/server-mcp/tsconfig.json b/packages/server-mcp/tsconfig.json new file mode 100644 index 0000000..3abf4d6 --- /dev/null +++ b/packages/server-mcp/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@eclipse-glsp/ts-config/mocha", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "composite": true + }, + "exclude": ["**/*.spec.ts"], + "include": ["src"], + "references": [ + { + "path": "../server" + } + ] +} diff --git a/packages/server/src/common/di/server-module.ts b/packages/server/src/common/di/server-module.ts index 209c873..6590f87 100644 --- a/packages/server/src/common/di/server-module.ts +++ b/packages/server/src/common/di/server-module.ts @@ -13,7 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { GLSPServer, GLSPServerListener } from '@eclipse-glsp/protocol'; +import { GLSPServer, GLSPServerInitContribution, GLSPServerListener } from '@eclipse-glsp/protocol'; import { ContainerModule, interfaces } from 'inversify'; import { DefaultGlobalActionProvider, GlobalActionProvider } from '../actions/global-action-provider'; import { DefaultGLSPServer } from '../protocol/glsp-server'; @@ -77,6 +77,9 @@ export class ServerModule extends GLSPModule { this.configureMultiBinding(new MultiBinding(GLSPServerListener), binding => this.configureGLSPServerListeners(binding) ); + this.configureMultiBinding(new MultiBinding(GLSPServerInitContribution), binding => + this.configureGLSPServerInitContributions(binding) + ); applyBindingTarget(context, GlobalActionProvider, this.bindGlobalActionProvider()).inSingletonScope(); @@ -112,4 +115,8 @@ export class ServerModule extends GLSPModule { protected configureGLSPServerListeners(binding: MultiBinding): void { binding.add({ service: ClientSessionManager }); } + + protected configureGLSPServerInitContributions(binding: MultiBinding): void { + // Can be overridden to add contributions + } } diff --git a/packages/server/src/common/protocol/glsp-server.ts b/packages/server/src/common/protocol/glsp-server.ts index 01331f6..a733779 100644 --- a/packages/server/src/common/protocol/glsp-server.ts +++ b/packages/server/src/common/protocol/glsp-server.ts @@ -19,6 +19,7 @@ import { DisposeClientSessionParameters, GLSPClientProxy, GLSPServer, + GLSPServerInitContribution, GLSPServerListener, InitializeClientSessionParameters, InitializeParameters, @@ -60,7 +61,10 @@ export class DefaultGLSPServer implements GLSPServer { protected clientSessions: Map; protected serverListeners: GLSPServerListener[] = []; - constructor(@multiInject(GLSPServerListener) @optional() serverListeners: GLSPServerListener[] = []) { + constructor( + @multiInject(GLSPServerListener) @optional() serverListeners: GLSPServerListener[] = [], + @multiInject(GLSPServerInitContribution) @optional() protected initContributions: GLSPServerInitContribution[] = [] + ) { this.clientSessions = new Map(); serverListeners.forEach(listener => this.addListener(listener)); } @@ -101,12 +105,29 @@ export class DefaultGLSPServer implements GLSPServer { let result = { protocolVersion: DefaultGLSPServer.PROTOCOL_VERSION, serverActions }; + result = await this.initializeServer(params, result); + // keep for backwards compatibility + // eslint-disable-next-line deprecation/deprecation result = await this.handleInitializeArgs(result, params.args); this.getListenersToNotify('serverInitialized').forEach((listener: GLSPServerListener) => listener.serverInitialized!(this)); this.initializeResult = result; return result; } + protected async initializeServer(params: InitializeParameters, result: InitializeResult): Promise { + for (const contribution of this.initContributions) { + try { + result = await contribution.initializeServer(this, params, result); + } catch (error) { + this.logger.error(`Error during server initialization contribution from ${contribution.constructor.name}:`, error); + } + } + return result; + } + + /** + * @deprecated Register a `GLSPServerInitContribution` instead. + */ protected handleInitializeArgs(result: InitializeResult, args: Args | undefined): MaybePromise { return result; } diff --git a/packages/server/src/common/session/client-session-manager.ts b/packages/server/src/common/session/client-session-manager.ts index 0a3714e..129af3c 100644 --- a/packages/server/src/common/session/client-session-manager.ts +++ b/packages/server/src/common/session/client-session-manager.ts @@ -47,6 +47,13 @@ export interface ClientSessionManager { */ getSession(clientSessionId: string): ClientSession | undefined; + /** + * Return all currently active {@link ClientSession}s. + * + * @returns An array of all currently active {@link ClientSession}s. + */ + getSessions(): Array; + /** * Return all currently active {@link ClientSession}s for the given diagram type. * @@ -129,6 +136,10 @@ export class DefaultClientSessionManager implements ClientSessionManager, GLSPSe return this.clientSessions.get(clientSessionId); } + getSessions(): ClientSession[] { + return Array.from(this.clientSessions.values()); + } + getSessionsByType(diagramType: string): ClientSession[] { return Array.from(this.clientSessions.values()).filter(session => session.diagramType === diagramType); } diff --git a/tsconfig.json b/tsconfig.json index 416506b..f166222 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,9 @@ { "path": "./packages/server" }, + { + "path": "./packages/server-mcp" + }, { "path": "./examples/workflow-server" } diff --git a/yarn.lock b/yarn.lock index 2b99491..ca7044f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -368,6 +368,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@hono/node-server@^1.19.7": + version "1.19.7" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.7.tgz#ecb2d3a7af40d1d378e53ce1fc1219f199fbcd6f" + integrity sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw== + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -752,6 +757,28 @@ yargs "17.7.2" yargs-parser "21.1.1" +"@modelcontextprotocol/sdk@^1.25.1": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz#2522d6776ca983a2f6dfc2eb106b89b5be9e072e" + integrity sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ== + dependencies: + "@hono/node-server" "^1.19.7" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.0.1" + express-rate-limit "^7.5.0" + jose "^6.1.1" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.0" + "@napi-rs/wasm-runtime@0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz#d27788176f250d86e498081e3c5ff48a17606918" @@ -1360,11 +1387,26 @@ dependencies: tslib "^2.4.0" +"@types/body-parser@*": + version "1.19.6" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" + integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/chai@^4.3.7": version "4.3.16" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ== +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + "@types/eslint-scope@^3.7.3": version "3.7.7" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" @@ -1386,6 +1428,30 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/express-serve-static-core@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz#74f47555b3d804b54cb7030e6f9aa0c7485cfc5b" + integrity sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.6.tgz#2d724b2c990dcb8c8444063f3580a903f6d500cc" + integrity sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/serve-static" "^2" + +"@types/http-errors@*": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + "@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -1435,11 +1501,36 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== +"@types/qs@*": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" + integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + "@types/semver@^7.5.0": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/send@*": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-1.2.1.tgz#6a784e45543c18c774c049bff6d3dbaf045c9c74" + integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ== + dependencies: + "@types/node" "*" + +"@types/serve-static@^2": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-2.2.0.tgz#d4a447503ead0d1671132d1ab6bd58b805d8de6a" + integrity sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/sinon@^10.0.19": version "10.0.20" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.20.tgz#f1585debf4c0d99f9938f4111e5479fb74865146" @@ -1739,6 +1830,14 @@ abbrev@^3.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-3.0.1.tgz#8ac8b3b5024d31464fe2a5feeea9f4536bf44025" integrity sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg== +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-import-assertions@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" @@ -1777,6 +1876,13 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -1792,6 +1898,16 @@ ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -2040,6 +2156,21 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +body-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.1.tgz#6df606b0eb0a6e3f783dde91dde182c24c82438c" + integrity sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.0" + raw-body "^3.0.1" + type-is "^2.0.1" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2095,6 +2226,11 @@ byte-size@8.1.1: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-8.1.1.tgz#3424608c62d59de5bfda05d31e0313c6174842ae" integrity sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg== +bytes@^3.1.2, bytes@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + cacache@^19.0.1: version "19.0.1" resolved "https://registry.yarnpkg.com/cacache/-/cacache-19.0.1.tgz#3370cc28a758434c85c2585008bd5bdcff17d6cd" @@ -2159,6 +2295,14 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -2502,6 +2646,16 @@ console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.1.tgz#a8b7bbeb2904befdfb6787e5c0c086959f605f9b" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + +content-type@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + conventional-changelog-angular@7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz#5eec8edbff15aa9b1680a8dcfbd53e2d7eb2ba7a" @@ -2585,11 +2739,29 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + cosmiconfig@9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" @@ -2614,7 +2786,7 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cross-spawn@^7.0.6: +cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -2691,7 +2863,7 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.4.1: +debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -2775,6 +2947,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + deprecation@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -2874,6 +3051,11 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + ejs@^3.1.7: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" @@ -2906,6 +3088,11 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== +encodeurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -3090,6 +3277,11 @@ escalade@^3.1.1, escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -3276,6 +3468,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -3286,6 +3483,18 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + execa@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" @@ -3306,6 +3515,45 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== +express-rate-limit@^7.5.0: + version "7.5.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.5.1.tgz#8c3a42f69209a3a1c969890070ece9e20a879dec" + integrity sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw== + +express@^5.0.1, express@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3337,6 +3585,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -3387,6 +3640,18 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-cache-dir@^3.2.0: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -3490,6 +3755,16 @@ form-data@^4.0.4: hasown "^2.0.2" mime-types "^2.1.12" +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fromentries@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" @@ -3586,7 +3861,7 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-intrinsic@^1.2.6: +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -3968,6 +4243,17 @@ http-cache-semantics@^4.1.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-proxy-agent@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" @@ -4003,6 +4289,13 @@ iconv-lite@^0.7.0: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@~0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.1.tgz#d4af1d2092f2bb05aab6296e5e7cd286d2f15432" + integrity sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -4059,7 +4352,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4127,6 +4420,11 @@ ip-address@^10.0.1: resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.0.1.tgz#a8180b783ce7788777d796286d61bce4276818ed" integrity sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA== +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -4281,6 +4579,11 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -4500,6 +4803,11 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +jose@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.1.3.tgz#8453d7be88af7bb7d64a0481d6a35a0145ba3ea5" + integrity sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4550,6 +4858,16 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz#e98ee7b1899ff4a184534d1f167c288c66bbeff4" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -4964,6 +5282,11 @@ md5@^2.3.0: crypt "0.0.2" is-buffer "~1.1.6" +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + meow@^8.1.2: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -4981,6 +5304,11 @@ meow@^8.1.2: type-fest "^0.18.0" yargs-parser "^20.2.3" +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -5004,6 +5332,11 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-types@^2.1.12, mime-types@^2.1.27: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -5011,6 +5344,13 @@ mime-types@^2.1.12, mime-types@^2.1.27: dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -5552,11 +5892,21 @@ nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + object-inspect@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5600,6 +5950,13 @@ object.values@^1.1.7: define-properties "^1.2.1" es-object-atoms "^1.0.0" +on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5878,6 +6235,11 @@ parse-url@^8.1.0: dependencies: parse-path "^7.0.0" +parseurl@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -5924,6 +6286,11 @@ path-to-regexp@^6.2.1: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36" integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw== +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -5976,6 +6343,11 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -6075,6 +6447,14 @@ protocols@^2.0.0, protocols@^2.0.1: resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== +proxy-addr@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -6085,6 +6465,13 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qs@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -6102,6 +6489,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + react-is@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" @@ -6244,6 +6646,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -6319,6 +6726,17 @@ rimraf@^5.0.5: dependencies: glob "^10.3.7" +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-async@^4.0.5: version "4.0.6" resolved "https://registry.yarnpkg.com/run-async/-/run-async-4.0.6.tgz#d53b86acb71f42650fe23de2b3c1b6b6b34b9294" @@ -6418,6 +6836,23 @@ semver@^7.7.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -6432,6 +6867,16 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -6459,6 +6904,11 @@ set-function-name@^2.0.1: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -6483,6 +6933,35 @@ shell-quote@^1.8.1: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" @@ -6493,6 +6972,17 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@3.0.7, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -6709,6 +7199,11 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -6995,6 +7490,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -7130,6 +7630,15 @@ type-fest@^0.8.0, type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" @@ -7245,6 +7754,11 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + upath@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" @@ -7303,6 +7817,11 @@ validate-npm-package-name@6.0.2, validate-npm-package-name@^6.0.0, validate-npm- resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz#4e8d2c4d939975a73dd1b7a65e8f08d44c85df96" integrity sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ== +vary@^1, vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + vscode-jsonrpc@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" @@ -7717,3 +8236,13 @@ yoctocolors-cjs@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== + +zod-to-json-schema@^3.25.0: + version "3.25.0" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz#df504c957c4fb0feff467c74d03e6aab0b013e1c" + integrity sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ== + +"zod@^3.25 || ^4.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.2.1.tgz#07f0388c7edbfd5f5a2466181cb4adf5b5dbd57b" + integrity sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==